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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-12-20 17:22:11 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-12-20 17:22:11 +0300
commit0c872e02b2c822e3397515ec324051ff540f0cd5 (patch)
treece2fb6ce7030e4dad0f4118d21ab6453e5938cdd
parentf7e05a6853b12f02911494c4b3fe53d9540d74fc (diff)
Add latest changes from gitlab-org/gitlab@15-7-stable-eev15.7.0-rc42
-rw-r--r--.dockerignore1
-rw-r--r--.eslintrc.yml6
-rw-r--r--.gitignore1
-rw-r--r--.gitlab-ci.yml14
-rw-r--r--.gitlab/CODEOWNERS88
-rw-r--r--.gitlab/ci/build-images.gitlab-ci.yml13
-rw-r--r--.gitlab/ci/docs.gitlab-ci.yml4
-rw-r--r--.gitlab/ci/frontend.gitlab-ci.yml31
-rw-r--r--.gitlab/ci/global.gitlab-ci.yml25
-rw-r--r--.gitlab/ci/notify.gitlab-ci.yml4
-rw-r--r--.gitlab/ci/package-and-test/main.gitlab-ci.yml72
-rw-r--r--.gitlab/ci/qa.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/rails.gitlab-ci.yml21
-rw-r--r--.gitlab/ci/rails/shared.gitlab-ci.yml5
-rw-r--r--.gitlab/ci/reports.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/review-apps/main.gitlab-ci.yml35
-rw-r--r--.gitlab/ci/review-apps/qa.gitlab-ci.yml1
-rw-r--r--.gitlab/ci/review.gitlab-ci.yml45
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml138
-rw-r--r--.gitlab/ci/static-analysis.gitlab-ci.yml8
-rw-r--r--.gitlab/issue_templates/Broken Master - Flaky.md5
-rw-r--r--.gitlab/issue_templates/Broken Master - Non-flaky.md2
-rw-r--r--.gitlab/issue_templates/Doc_cleanup.md80
-rw-r--r--.gitlab/issue_templates/Feature Flag Roll Out.md18
-rw-r--r--.gitlab/issue_templates/Geo Replicate a new Git repository type.md45
-rw-r--r--.gitlab/issue_templates/Geo Replicate a new blob type.md36
-rw-r--r--.gitlab/issue_templates/Global Search - bug.md30
-rw-r--r--.gitlab/issue_templates/Global Search - feature.md13
-rw-r--r--.gitlab/issue_templates/Global Search - maintenance.md11
-rw-r--r--.gitlab/issue_templates/Navigation - Left Sidebar Proposals.md14
-rw-r--r--.gitlab/issue_templates/Navigation Proposals.md15
-rw-r--r--.gitlab/issue_templates/Security developer workflow.md4
-rw-r--r--.gitlab/issue_templates/UX Theme.md51
-rw-r--r--.gitlab/merge_request_templates/Default.md2
-rw-r--r--.gitlab/merge_request_templates/Deprecations.md4
-rw-r--r--.gitlab/merge_request_templates/New Version of gitlab-styles.md45
-rw-r--r--.gitlab/merge_request_templates/Removals.md4
-rw-r--r--.gitlab/merge_request_templates/Revert To Resolve Incident.md14
-rw-r--r--.gitlab/merge_request_templates/Security Release.md2
-rwxr-xr-x.lefthook/pre-push/merge_conflicts23
-rw-r--r--.rubocop.yml67
-rw-r--r--.rubocop_todo/cop/user_admin.yml114
-rw-r--r--.rubocop_todo/database/multiple_databases.yml10
-rw-r--r--.rubocop_todo/database/rescue_query_canceled.yml1
-rw-r--r--.rubocop_todo/fips/md5.yml3
-rw-r--r--.rubocop_todo/fips/sha1.yml3
-rw-r--r--.rubocop_todo/gitlab/delegate_predicate_methods.yml2
-rw-r--r--.rubocop_todo/gitlab/deprecate_track_redis_hll_event.yml15
-rw-r--r--.rubocop_todo/gitlab/feature_available_usage.yml262
-rw-r--r--.rubocop_todo/gitlab/json.yml465
-rw-r--r--.rubocop_todo/gitlab/namespaced_class.yml27
-rw-r--r--.rubocop_todo/gitlab/service_response.yml1
-rw-r--r--.rubocop_todo/gitlab/strong_memoize_attr.yml759
-rw-r--r--.rubocop_todo/graphql/argument_uniqueness.yml2
-rw-r--r--.rubocop_todo/graphql/descriptions.yml98
-rw-r--r--.rubocop_todo/graphql/field_definitions.yml3
-rw-r--r--.rubocop_todo/graphql/field_hash_key.yml6
-rw-r--r--.rubocop_todo/graphql/field_method.yml3
-rw-r--r--.rubocop_todo/graphql/ordered_fields.yml12
-rw-r--r--.rubocop_todo/graphql/resolver_method_length.yml6
-rw-r--r--.rubocop_todo/graphql/unused_argument.yml5
-rw-r--r--.rubocop_todo/layout/argument_alignment.yml678
-rw-r--r--.rubocop_todo/layout/closing_parenthesis_indentation.yml2
-rw-r--r--.rubocop_todo/layout/empty_line_between_defs.yml8
-rw-r--r--.rubocop_todo/layout/first_array_element_indentation.yml25
-rw-r--r--.rubocop_todo/layout/first_hash_element_indentation.yml67
-rw-r--r--.rubocop_todo/layout/hash_alignment.yml2
-rw-r--r--.rubocop_todo/layout/leading_comment_space.yml2
-rw-r--r--.rubocop_todo/layout/line_length.yml137
-rw-r--r--.rubocop_todo/layout/multiline_operation_indentation.yml52
-rw-r--r--.rubocop_todo/layout/space_around_operators.yml2
-rw-r--r--.rubocop_todo/layout/space_before_block_braces.yml2
-rw-r--r--.rubocop_todo/layout/space_in_lambda_literal.yml90
-rw-r--r--.rubocop_todo/layout/space_inside_block_braces.yml2
-rw-r--r--.rubocop_todo/layout/space_inside_parens.yml84
-rw-r--r--.rubocop_todo/layout/trailing_whitespace.yml3
-rw-r--r--.rubocop_todo/lint/constant_definition_in_block.yml13
-rw-r--r--.rubocop_todo/lint/missing_cop_enable_directive.yml2
-rw-r--r--.rubocop_todo/lint/mixed_regexp_capture_types.yml2
-rw-r--r--.rubocop_todo/lint/redundant_cop_disable_directive.yml36
-rw-r--r--.rubocop_todo/metrics/abc_size.yml6
-rw-r--r--.rubocop_todo/metrics/cyclomatic_complexity.yml2
-rw-r--r--.rubocop_todo/metrics/perceived_complexity.yml3
-rw-r--r--.rubocop_todo/migration/background_migration_base_class.yml22
-rw-r--r--.rubocop_todo/migration/background_migration_record.yml98
-rw-r--r--.rubocop_todo/migration/background_migrations.yml69
-rw-r--r--.rubocop_todo/naming/heredoc_delimiter_case.yml1
-rw-r--r--.rubocop_todo/naming/heredoc_delimiter_naming.yml4
-rw-r--r--.rubocop_todo/performance/active_record_subtransaction_methods.yml1
-rw-r--r--.rubocop_todo/performance/bind_call.yml10
-rw-r--r--.rubocop_todo/performance/method_object_as_block.yml5
-rw-r--r--.rubocop_todo/performance/redundant_block_call.yml12
-rw-r--r--.rubocop_todo/performance/string_include.yml3
-rw-r--r--.rubocop_todo/rails/active_record_callbacks_order.yml24
-rw-r--r--.rubocop_todo/rails/content_tag.yml9
-rw-r--r--.rubocop_todo/rails/file_path.yml4
-rw-r--r--.rubocop_todo/rails/helper_instance_variable.yml2
-rw-r--r--.rubocop_todo/rails/http_status.yml9
-rw-r--r--.rubocop_todo/rails/index_with.yml53
-rw-r--r--.rubocop_todo/rails/inverse_of.yml1
-rw-r--r--.rubocop_todo/rails/negate_include.yml2
-rw-r--r--.rubocop_todo/rails/pluck.yml220
-rw-r--r--.rubocop_todo/rails/redundant_foreign_key.yml2
-rw-r--r--.rubocop_todo/rails/time_zone.yml230
-rw-r--r--.rubocop_todo/rake/require.yml1
-rw-r--r--.rubocop_todo/rspec/any_instance_of.yml858
-rw-r--r--.rubocop_todo/rspec/context_wording.yml123
-rw-r--r--.rubocop_todo/rspec/described_class.yml29
-rw-r--r--.rubocop_todo/rspec/empty_example_group.yml5
-rw-r--r--.rubocop_todo/rspec/empty_line_after_example_group.yml39
-rw-r--r--.rubocop_todo/rspec/empty_line_after_hook.yml8
-rw-r--r--.rubocop_todo/rspec/expect_change.yml1183
-rw-r--r--.rubocop_todo/rspec/expect_in_hook.yml33
-rw-r--r--.rubocop_todo/rspec/factory_bot/avoid_create.yml379
-rw-r--r--.rubocop_todo/rspec/file_path.yml3
-rw-r--r--.rubocop_todo/rspec/hooks_before_examples.yml32
-rw-r--r--.rubocop_todo/rspec/instance_variable.yml364
-rw-r--r--.rubocop_todo/rspec/leaky_constant_declaration.yml17
-rw-r--r--.rubocop_todo/rspec/let_before_examples.yml17
-rw-r--r--.rubocop_todo/rspec/overwriting_setup.yml1
-rw-r--r--.rubocop_todo/rspec/predicate_matcher.yml516
-rw-r--r--.rubocop_todo/rspec/repeated_example_group_body.yml5
-rw-r--r--.rubocop_todo/rspec/repeated_example_group_description.yml12
-rw-r--r--.rubocop_todo/rspec/return_from_stub.yml34
-rw-r--r--.rubocop_todo/rspec/scattered_let.yml30
-rw-r--r--.rubocop_todo/rspec/shared_examples.yml24
-rw-r--r--.rubocop_todo/rspec/timecop_freeze.yml24
-rw-r--r--.rubocop_todo/rspec/timecop_travel.yml4
-rw-r--r--.rubocop_todo/rspec/variable_definition.yml3
-rw-r--r--.rubocop_todo/rspec/verified_doubles.yml2213
-rw-r--r--.rubocop_todo/security/io_methods.yml6
-rw-r--r--.rubocop_todo/style/accessor_grouping.yml3
-rw-r--r--.rubocop_todo/style/bare_percent_literals.yml3
-rw-r--r--.rubocop_todo/style/class_and_module_children.yml20
-rw-r--r--.rubocop_todo/style/conditional_assignment.yml2
-rw-r--r--.rubocop_todo/style/each_for_simple_loop.yml2
-rw-r--r--.rubocop_todo/style/empty_else.yml4
-rw-r--r--.rubocop_todo/style/empty_method.yml31
-rw-r--r--.rubocop_todo/style/explicit_block_argument.yml13
-rw-r--r--.rubocop_todo/style/float_division.yml2
-rw-r--r--.rubocop_todo/style/format_string.yml32
-rw-r--r--.rubocop_todo/style/guard_clause.yml41
-rw-r--r--.rubocop_todo/style/hash_as_last_array_item.yml6
-rw-r--r--.rubocop_todo/style/hash_each_methods.yml7
-rw-r--r--.rubocop_todo/style/if_inside_else.yml48
-rw-r--r--.rubocop_todo/style/if_unless_modifier.yml55
-rw-r--r--.rubocop_todo/style/keyword_parameters_order.yml52
-rw-r--r--.rubocop_todo/style/lambda.yml2
-rw-r--r--.rubocop_todo/style/next.yml2
-rw-r--r--.rubocop_todo/style/numeric_literal_prefix.yml2
-rw-r--r--.rubocop_todo/style/percent_literal_delimiters.yml54
-rw-r--r--.rubocop_todo/style/redundant_begin.yml52
-rw-r--r--.rubocop_todo/style/redundant_condition.yml2
-rw-r--r--.rubocop_todo/style/redundant_interpolation.yml59
-rw-r--r--.rubocop_todo/style/redundant_parentheses.yml6
-rw-r--r--.rubocop_todo/style/redundant_regexp_escape.yml38
-rw-r--r--.rubocop_todo/style/redundant_self.yml7
-rw-r--r--.rubocop_todo/style/single_argument_dig.yml3
-rw-r--r--.rubocop_todo/style/sole_nested_conditional.yml4
-rw-r--r--.rubocop_todo/style/string_concatenation.yml26
-rw-r--r--.rubocop_todo/style/string_literals_in_interpolation.yml5
-rw-r--r--.rubocop_todo/style/symbol_proc.yml7
-rw-r--r--.ruby-version2
-rw-r--r--.secretsignore66
-rw-r--r--.yamllint36
-rw-r--r--CHANGELOG.md36
-rw-r--r--Dockerfile.assets2
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_KAS_VERSION2
-rw-r--r--GITLAB_PAGES_VERSION2
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile45
-rw-r--r--Gemfile.checksum110
-rw-r--r--Gemfile.lock167
-rw-r--r--app/assets/images/web-ide-promo-popover.svg101
-rw-r--r--app/assets/javascripts/admin/background_migrations/components/database_listbox.vue6
-rw-r--r--app/assets/javascripts/admin/broadcast_messages/components/base.vue5
-rw-r--r--app/assets/javascripts/admin/broadcast_messages/components/datetime_picker.vue47
-rw-r--r--app/assets/javascripts/admin/broadcast_messages/components/message_form.vue225
-rw-r--r--app/assets/javascripts/admin/broadcast_messages/components/message_form_group.vue34
-rw-r--r--app/assets/javascripts/admin/broadcast_messages/components/messages_table.vue19
-rw-r--r--app/assets/javascripts/admin/broadcast_messages/constants.js35
-rw-r--r--app/assets/javascripts/admin/broadcast_messages/edit.js43
-rw-r--r--app/assets/javascripts/admin/broadcast_messages/index.js5
-rw-r--r--app/assets/javascripts/alert_management/components/alert_management_table.vue7
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_form.vue2
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue6
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue1
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/components/base.vue192
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/components/filter_bar.vue153
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/components/formatted_stage_count.vue (renamed from app/assets/javascripts/cycle_analytics/components/formatted_stage_count.vue)0
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/components/metric_tile.vue (renamed from app/assets/javascripts/cycle_analytics/components/metric_tile.vue)0
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/components/path_navigation.vue114
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/components/stage_table.vue305
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/components/total_time.vue (renamed from app/assets/javascripts/cycle_analytics/components/total_time.vue)0
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/components/value_stream_filters.vue (renamed from app/assets/javascripts/cycle_analytics/components/value_stream_filters.vue)0
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/constants.js (renamed from app/assets/javascripts/cycle_analytics/constants.js)0
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/index.js50
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/store/actions.js (renamed from app/assets/javascripts/cycle_analytics/store/actions.js)0
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/store/getters.js (renamed from app/assets/javascripts/cycle_analytics/store/getters.js)0
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/store/index.js (renamed from app/assets/javascripts/cycle_analytics/store/index.js)0
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/store/mutation_types.js (renamed from app/assets/javascripts/cycle_analytics/store/mutation_types.js)0
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/store/mutations.js (renamed from app/assets/javascripts/cycle_analytics/store/mutations.js)0
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/store/state.js31
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/utils.js (renamed from app/assets/javascripts/cycle_analytics/utils.js)0
-rw-r--r--app/assets/javascripts/api/analytics_api.js5
-rw-r--r--app/assets/javascripts/artifacts/graphql/queries/get_job_artifacts.query.graphql1
-rw-r--r--app/assets/javascripts/awards_handler.js8
-rw-r--r--app/assets/javascripts/badges/components/badge_form.vue12
-rw-r--r--app/assets/javascripts/badges/constants.js8
-rw-r--r--app/assets/javascripts/batch_comments/components/draft_note.vue50
-rw-r--r--app/assets/javascripts/behaviors/copy_code.js5
-rw-r--r--app/assets/javascripts/behaviors/index.js1
-rw-r--r--app/assets/javascripts/behaviors/markdown/init_gfm.js13
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_gfm.js77
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_math.js6
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_observability.js33
-rw-r--r--app/assets/javascripts/behaviors/preview_markdown.js1
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js2
-rw-r--r--app/assets/javascripts/blob/components/blob_header.vue2
-rw-r--r--app/assets/javascripts/blob/openapi/index.js2
-rw-r--r--app/assets/javascripts/blob/viewer/index.js2
-rw-r--r--app/assets/javascripts/blob_edit/blob_bundle.js2
-rw-r--r--app/assets/javascripts/blob_edit/edit_blob.js1
-rw-r--r--app/assets/javascripts/boards/components/board_content.vue10
-rw-r--r--app/assets/javascripts/boards/components/board_content_sidebar.vue25
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue4
-rw-r--r--app/assets/javascripts/boards/components/boards_selector.vue2
-rw-r--r--app/assets/javascripts/boards/components/issue_board_filtered_search.vue30
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_sidebar_time_tracker.vue3
-rw-r--r--app/assets/javascripts/boards/issue_board_filters.js6
-rw-r--r--app/assets/javascripts/boards/stores/actions.js1
-rw-r--r--app/assets/javascripts/branches/components/sort_dropdown.vue30
-rw-r--r--app/assets/javascripts/branches/init_new_branch_ref_selector.js25
-rw-r--r--app/assets/javascripts/ci/ci_lint/components/ci_lint.vue131
-rw-r--r--app/assets/javascripts/ci/ci_lint/index.js31
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/code_snippet_alert/code_snippet_alert.vue (renamed from app/assets/javascripts/pipeline_editor/components/code_snippet_alert/code_snippet_alert.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/code_snippet_alert/constants.js (renamed from app/assets/javascripts/pipeline_editor/components/code_snippet_alert/constants.js)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/commit/commit_form.vue (renamed from app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/commit/commit_section.vue (renamed from app/assets/javascripts/pipeline_editor/components/commit/commit_section.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/drawer/cards/first_pipeline_card.vue (renamed from app/assets/javascripts/pipeline_editor/components/drawer/cards/first_pipeline_card.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/drawer/cards/getting_started_card.vue (renamed from app/assets/javascripts/pipeline_editor/components/drawer/cards/getting_started_card.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/drawer/cards/pipeline_config_reference_card.vue (renamed from app/assets/javascripts/pipeline_editor/components/drawer/cards/pipeline_config_reference_card.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/drawer/cards/visualize_and_lint_card.vue (renamed from app/assets/javascripts/pipeline_editor/components/drawer/cards/visualize_and_lint_card.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/drawer/pipeline_editor_drawer.vue (renamed from app/assets/javascripts/pipeline_editor/components/drawer/pipeline_editor_drawer.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/drawer/ui/demo_job_pill.vue (renamed from app/assets/javascripts/pipeline_editor/components/drawer/ui/demo_job_pill.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/editor/ci_config_merged_preview.vue (renamed from app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/editor/ci_editor_header.vue68
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/editor/text_editor.vue (renamed from app/assets/javascripts/pipeline_editor/components/editor/text_editor.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/file_nav/branch_switcher.vue254
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue72
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/file_tree/container.vue (renamed from app/assets/javascripts/pipeline_editor/components/file_tree/container.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/file_tree/file_item.vue (renamed from app/assets/javascripts/pipeline_editor/components/file_tree/file_item.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/header/pipeline_editor_header.vue (renamed from app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_header.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/header/pipeline_editor_mini_graph.vue (renamed from app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/header/pipeline_status.vue188
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/header/validation_segment.vue126
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/lint/ci_lint_results.vue (renamed from app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/lint/ci_lint_results_param.vue (renamed from app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_param.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/lint/ci_lint_results_value.vue (renamed from app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_value.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/lint/ci_lint_warnings.vue (renamed from app/assets/javascripts/pipeline_editor/components/lint/ci_lint_warnings.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/pipeline_editor_tabs.vue (renamed from app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/popovers/file_tree_popover.vue (renamed from app/assets/javascripts/pipeline_editor/components/popovers/file_tree_popover.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/popovers/validate_pipeline_popover.vue (renamed from app/assets/javascripts/pipeline_editor/components/popovers/validate_pipeline_popover.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/popovers/walkthrough_popover.vue (renamed from app/assets/javascripts/pipeline_editor/components/popovers/walkthrough_popover.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/ui/confirm_unsaved_changes_dialog.vue (renamed from app/assets/javascripts/pipeline_editor/components/ui/confirm_unsaved_changes_dialog.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/ui/editor_tab.vue156
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/ui/pipeline_editor_empty_state.vue72
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/ui/pipeline_editor_messages.vue (renamed from app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_messages.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/validate/ci_validate.vue (renamed from app/assets/javascripts/pipeline_editor/components/validate/ci_validate.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/constants.js (renamed from app/assets/javascripts/pipeline_editor/constants.js)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/mutations/client/lint_ci.mutation.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/mutations/client/lint_ci.mutation.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/mutations/client/update_app_status.mutation.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/mutations/client/update_app_status.mutation.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/mutations/client/update_current_branch.mutation.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/mutations/client/update_current_branch.mutation.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/mutations/client/update_last_commit_branch.mutation.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/mutations/client/update_last_commit_branch.mutation.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/mutations/client/update_pipeline_etag.mutation.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/mutations/client/update_pipeline_etag.mutation.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/queries/available_branches.query.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/queries/available_branches.query.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/queries/blob_content.query.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/queries/blob_content.query.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/queries/ci_config.query.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/queries/ci_config.query.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/queries/client/app_status.query.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/queries/client/app_status.query.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/queries/client/current_branch.query.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/queries/client/current_branch.query.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/queries/client/last_commit_branch.query.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/queries/client/last_commit_branch.query.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/queries/client/pipeline_etag.query.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline_etag.query.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/queries/get_starter_template.query.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/queries/get_starter_template.query.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/queries/latest_commit_sha.query.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/queries/latest_commit_sha.query.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/queries/pipeline.query.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/queries/pipeline.query.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/resolvers.js (renamed from app/assets/javascripts/pipeline_editor/graphql/resolvers.js)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/graphql/typedefs.graphql (renamed from app/assets/javascripts/pipeline_editor/graphql/typedefs.graphql)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/index.js (renamed from app/assets/javascripts/pipeline_editor/index.js)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/pipeline_editor_app.vue (renamed from app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/pipeline_editor_home.vue (renamed from app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue)0
-rw-r--r--app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue311
-rw-r--r--app/assets/javascripts/ci/pipeline_schedules/constants.js2
-rw-r--r--app/assets/javascripts/ci/pipeline_schedules/mount_pipeline_schedules_form_app.js24
-rw-r--r--app/assets/javascripts/ci/reports/codequality_report/components/codequality_issue_body.vue76
-rw-r--r--app/assets/javascripts/ci/reports/codequality_report/constants.js53
-rw-r--r--app/assets/javascripts/ci/reports/codequality_report/store/actions.js (renamed from app/assets/javascripts/reports/codequality_report/store/actions.js)0
-rw-r--r--app/assets/javascripts/ci/reports/codequality_report/store/getters.js (renamed from app/assets/javascripts/reports/codequality_report/store/getters.js)0
-rw-r--r--app/assets/javascripts/ci/reports/codequality_report/store/index.js (renamed from app/assets/javascripts/reports/codequality_report/store/index.js)0
-rw-r--r--app/assets/javascripts/ci/reports/codequality_report/store/mutation_types.js (renamed from app/assets/javascripts/reports/codequality_report/store/mutation_types.js)0
-rw-r--r--app/assets/javascripts/ci/reports/codequality_report/store/mutations.js (renamed from app/assets/javascripts/reports/codequality_report/store/mutations.js)0
-rw-r--r--app/assets/javascripts/ci/reports/codequality_report/store/state.js (renamed from app/assets/javascripts/reports/codequality_report/store/state.js)0
-rw-r--r--app/assets/javascripts/ci/reports/codequality_report/store/utils/codequality_parser.js (renamed from app/assets/javascripts/reports/codequality_report/store/utils/codequality_parser.js)0
-rw-r--r--app/assets/javascripts/ci/reports/components/grouped_issues_list.vue106
-rw-r--r--app/assets/javascripts/ci/reports/components/issue_body.js17
-rw-r--r--app/assets/javascripts/ci/reports/components/issue_status_icon.vue (renamed from app/assets/javascripts/reports/components/issue_status_icon.vue)0
-rw-r--r--app/assets/javascripts/ci/reports/components/issues_list.vue119
-rw-r--r--app/assets/javascripts/ci/reports/components/report_item.vue67
-rw-r--r--app/assets/javascripts/ci/reports/components/report_link.vue (renamed from app/assets/javascripts/reports/components/report_link.vue)0
-rw-r--r--app/assets/javascripts/ci/reports/components/report_section.vue (renamed from app/assets/javascripts/reports/components/report_section.vue)0
-rw-r--r--app/assets/javascripts/ci/reports/components/summary_row.vue (renamed from app/assets/javascripts/reports/components/summary_row.vue)0
-rw-r--r--app/assets/javascripts/ci/reports/constants.js (renamed from app/assets/javascripts/reports/constants.js)0
-rw-r--r--app/assets/javascripts/ci/runner/admin_runner_show/admin_runner_show_app.vue53
-rw-r--r--app/assets/javascripts/ci/runner/admin_runner_show/index.js2
-rw-r--r--app/assets/javascripts/ci/runner/admin_runners/admin_runners_app.vue17
-rw-r--r--app/assets/javascripts/ci/runner/components/cells/runner_stacked_summary_cell.vue112
-rw-r--r--app/assets/javascripts/ci/runner/components/cells/runner_status_cell.vue6
-rw-r--r--app/assets/javascripts/ci/runner/components/cells/runner_summary_cell.vue125
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_detail.vue2
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_groups.vue2
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_job_status_badge.vue55
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_list.vue11
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_projects.vue2
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_type_tabs.vue9
-rw-r--r--app/assets/javascripts/ci/runner/components/search_tokens/paused_token_config.js4
-rw-r--r--app/assets/javascripts/ci/runner/components/search_tokens/status_token_config.js4
-rw-r--r--app/assets/javascripts/ci/runner/components/search_tokens/tag_token_config.js4
-rw-r--r--app/assets/javascripts/ci/runner/components/stat/runner_count.vue4
-rw-r--r--app/assets/javascripts/ci/runner/components/stat/runner_stats.vue41
-rw-r--r--app/assets/javascripts/ci/runner/constants.js11
-rw-r--r--app/assets/javascripts/ci/runner/graphql/list/list_item_shared.fragment.graphql1
-rw-r--r--app/assets/javascripts/ci/runner/group_runners/group_runners_app.vue3
-rw-r--r--app/assets/javascripts/ci/runner/runner_search_utils.js1
-rw-r--r--app/assets/javascripts/ci_lint/components/ci_lint.vue131
-rw-r--r--app/assets/javascripts/ci_lint/index.js31
-rw-r--r--app/assets/javascripts/ci_secure_files/components/secure_files_list.vue4
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_group_variables.vue1
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_project_variables.vue1
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue43
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue13
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_shared.vue10
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue71
-rw-r--r--app/assets/javascripts/ci_variable_list/constants.js18
-rw-r--r--app/assets/javascripts/ci_variable_list/graphql/mutations/group_add_variable.mutation.graphql1
-rw-r--r--app/assets/javascripts/ci_variable_list/graphql/mutations/group_delete_variable.mutation.graphql1
-rw-r--r--app/assets/javascripts/ci_variable_list/graphql/mutations/group_update_variable.mutation.graphql1
-rw-r--r--app/assets/javascripts/ci_variable_list/graphql/mutations/project_add_variable.mutation.graphql1
-rw-r--r--app/assets/javascripts/ci_variable_list/graphql/mutations/project_delete_variable.mutation.graphql1
-rw-r--r--app/assets/javascripts/ci_variable_list/graphql/mutations/project_update_variable.mutation.graphql1
-rw-r--r--app/assets/javascripts/ci_variable_list/graphql/queries/group_variables.query.graphql2
-rw-r--r--app/assets/javascripts/ci_variable_list/graphql/queries/project_variables.query.graphql2
-rw-r--r--app/assets/javascripts/ci_variable_list/graphql/queries/variables.query.graphql1
-rw-r--r--app/assets/javascripts/clusters_list/clusters_util.js8
-rw-r--r--app/assets/javascripts/clusters_list/components/agent_token.vue11
-rw-r--r--app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue103
-rw-r--r--app/assets/javascripts/clusters_list/constants.js6
-rw-r--r--app/assets/javascripts/constants.js3
-rw-r--r--app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue4
-rw-r--r--app/assets/javascripts/content_editor/components/content_editor.vue14
-rw-r--r--app/assets/javascripts/content_editor/components/formatting_toolbar.vue110
-rw-r--r--app/assets/javascripts/content_editor/components/suggestions_dropdown.vue4
-rw-r--r--app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue3
-rw-r--r--app/assets/javascripts/content_editor/components/top_toolbar.vue112
-rw-r--r--app/assets/javascripts/content_editor/extensions/code_block_highlight.js4
-rw-r--r--app/assets/javascripts/content_editor/extensions/comment.js49
-rw-r--r--app/assets/javascripts/content_editor/extensions/image.js18
-rw-r--r--app/assets/javascripts/content_editor/extensions/reference_label.js2
-rw-r--r--app/assets/javascripts/content_editor/services/create_content_editor.js2
-rw-r--r--app/assets/javascripts/content_editor/services/gl_api_markdown_deserializer.js5
-rw-r--r--app/assets/javascripts/content_editor/services/markdown_serializer.js3
-rw-r--r--app/assets/javascripts/content_editor/services/serialization_helpers.js27
-rw-r--r--app/assets/javascripts/crm/components/crm_form.vue (renamed from app/assets/javascripts/crm/components/form.vue)0
-rw-r--r--app/assets/javascripts/crm/contacts/components/contact_form_wrapper.vue6
-rw-r--r--app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue6
-rw-r--r--app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue5
-rw-r--r--app/assets/javascripts/cycle_analytics/components/base.vue192
-rw-r--r--app/assets/javascripts/cycle_analytics/components/filter_bar.vue144
-rw-r--r--app/assets/javascripts/cycle_analytics/components/path_navigation.vue113
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_table.vue305
-rw-r--r--app/assets/javascripts/cycle_analytics/index.js50
-rw-r--r--app/assets/javascripts/cycle_analytics/store/state.js31
-rw-r--r--app/assets/javascripts/deploy_tokens/components/new_deploy_token.vue208
-rw-r--r--app/assets/javascripts/deploy_tokens/deploy_token_translations.js41
-rw-r--r--app/assets/javascripts/deprecated_jquery_dropdown/render.js2
-rw-r--r--app/assets/javascripts/deprecated_notes.js28
-rw-r--r--app/assets/javascripts/design_management/components/design_notes/design_discussion.vue2
-rw-r--r--app/assets/javascripts/design_management/components/design_notes/design_note.vue12
-rw-r--r--app/assets/javascripts/design_management/components/design_todo_button.vue2
-rw-r--r--app/assets/javascripts/design_management/components/upload/design_version_dropdown.vue92
-rw-r--r--app/assets/javascripts/design_management/pages/design/index.vue2
-rw-r--r--app/assets/javascripts/diffs/components/commit_item.vue5
-rw-r--r--app/assets/javascripts/diffs/components/diff_code_quality.vue39
-rw-r--r--app/assets/javascripts/diffs/components/diff_discussion_reply.vue17
-rw-r--r--app/assets/javascripts/diffs/components/diff_expansion_cell.vue5
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue9
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue4
-rw-r--r--app/assets/javascripts/diffs/components/diff_view.vue16
-rw-r--r--app/assets/javascripts/diffs/i18n.js3
-rw-r--r--app/assets/javascripts/diffs/index.js3
-rw-r--r--app/assets/javascripts/diffs/store/actions.js35
-rw-r--r--app/assets/javascripts/diffs/utils/merge_request.js18
-rw-r--r--app/assets/javascripts/editor/components/source_editor_toolbar.vue13
-rw-r--r--app/assets/javascripts/editor/components/source_editor_toolbar_button.vue17
-rw-r--r--app/assets/javascripts/editor/constants.js104
-rw-r--r--app/assets/javascripts/editor/extensions/source_editor_markdown_ext.js48
-rw-r--r--app/assets/javascripts/editor/extensions/source_editor_markdown_livepreview_ext.js3
-rw-r--r--app/assets/javascripts/editor/schema/ci.json213
-rw-r--r--app/assets/javascripts/environments/components/deploy_board.vue2
-rw-r--r--app/assets/javascripts/environments/environment_details/constants.js47
-rw-r--r--app/assets/javascripts/environments/environment_details/index.vue118
-rw-r--r--app/assets/javascripts/environments/graphql/queries/environment_details.query.graphql48
-rw-r--r--app/assets/javascripts/environments/helpers/deployment_data_transformation_helper.js62
-rw-r--r--app/assets/javascripts/environments/mount_show.js30
-rw-r--r--app/assets/javascripts/error_tracking/components/error_tracking_list.vue4
-rw-r--r--app/assets/javascripts/error_tracking/components/stacktrace_entry.vue3
-rw-r--r--app/assets/javascripts/feature_flags/components/feature_flags_table.vue14
-rw-r--r--app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue2
-rw-r--r--app/assets/javascripts/feature_flags/components/strategies/percent_rollout.vue2
-rw-r--r--app/assets/javascripts/feature_flags/components/strategy_label.vue29
-rw-r--r--app/assets/javascripts/feature_flags/utils.js16
-rw-r--r--app/assets/javascripts/feature_highlight/feature_highlight_popover.vue9
-rw-r--r--app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js31
-rw-r--r--app/assets/javascripts/filtered_search/available_dropdown_mappings.js34
-rw-r--r--app/assets/javascripts/filtered_search/constants.js15
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_utils.js3
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js12
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js7
-rw-r--r--app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js51
-rw-r--r--app/assets/javascripts/filtered_search/visual_token_value.js3
-rw-r--r--app/assets/javascripts/flash.js8
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue5
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js19
-rw-r--r--app/assets/javascripts/gitlab_pages/components/pages_pipeline_wizard.vue2
-rw-r--r--app/assets/javascripts/gitlab_version_check/components/security_patch_upgrade_alert.vue76
-rw-r--r--app/assets/javascripts/gitlab_version_check/components/security_patch_upgrade_alert_modal.vue160
-rw-r--r--app/assets/javascripts/gitlab_version_check/constants.js22
-rw-r--r--app/assets/javascripts/gitlab_version_check/index.js116
-rw-r--r--app/assets/javascripts/gitlab_version_check/utils.js18
-rw-r--r--app/assets/javascripts/graphql_shared/fragments/alert.fragment.graphql1
-rw-r--r--app/assets/javascripts/graphql_shared/mutations/alert_status_update.mutation.graphql1
-rw-r--r--app/assets/javascripts/graphql_shared/possible_types.json7
-rw-r--r--app/assets/javascripts/graphql_shared/queries/alert_details.query.graphql1
-rw-r--r--app/assets/javascripts/groups/components/app.vue75
-rw-r--r--app/assets/javascripts/groups/components/empty_state.vue91
-rw-r--r--app/assets/javascripts/groups/components/empty_states/archived_projects_empty_state.vue21
-rw-r--r--app/assets/javascripts/groups/components/empty_states/shared_projects_empty_state.vue21
-rw-r--r--app/assets/javascripts/groups/components/empty_states/subgroups_and_projects_empty_state.vue90
-rw-r--r--app/assets/javascripts/groups/components/group_item.vue12
-rw-r--r--app/assets/javascripts/groups/components/group_name_and_path.vue2
-rw-r--r--app/assets/javascripts/groups/components/groups.vue23
-rw-r--r--app/assets/javascripts/groups/components/overview_tabs.vue35
-rw-r--r--app/assets/javascripts/groups/components/transfer_group_form.vue1
-rw-r--r--app/assets/javascripts/header.js21
-rw-r--r--app/assets/javascripts/header_search/components/app.vue3
-rw-r--r--app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue2
-rw-r--r--app/assets/javascripts/header_search/constants.js3
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/form.vue5
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/success_message.vue2
-rw-r--r--app/assets/javascripts/ide/components/error_message.vue5
-rw-r--r--app/assets/javascripts/ide/components/jobs/detail.vue5
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/index.vue2
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/upload.vue4
-rw-r--r--app/assets/javascripts/ide/components/panes/right.vue17
-rw-r--r--app/assets/javascripts/ide/components/pipelines/list.vue26
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue42
-rw-r--r--app/assets/javascripts/ide/components/switch_editors/switch_editors_view.vue103
-rw-r--r--app/assets/javascripts/ide/components/terminal/empty_state.vue5
-rw-r--r--app/assets/javascripts/ide/constants.js3
-rw-r--r--app/assets/javascripts/ide/index.js5
-rw-r--r--app/assets/javascripts/ide/init_gitlab_web_ide.js98
-rw-r--r--app/assets/javascripts/ide/lib/diff/controller.js2
-rw-r--r--app/assets/javascripts/ide/lib/editor_options.js11
-rw-r--r--app/assets/javascripts/ide/lib/gitlab_web_ide/get_base_config.js12
-rw-r--r--app/assets/javascripts/ide/lib/gitlab_web_ide/index.js2
-rw-r--r--app/assets/javascripts/ide/lib/gitlab_web_ide/setup_root_element.js14
-rw-r--r--app/assets/javascripts/ide/remote/index.js40
-rw-r--r--app/assets/javascripts/ide/services/index.js2
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal/actions/session_controls.js4
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal/messages.js4
-rw-r--r--app/assets/javascripts/ide/stores/state.js1
-rw-r--r--app/assets/javascripts/import_entities/components/group_dropdown.vue44
-rw-r--r--app/assets/javascripts/import_entities/components/import_status.vue2
-rw-r--r--app/assets/javascripts/import_entities/constants.js6
-rw-r--r--app/assets/javascripts/import_entities/import_groups/components/import_table.vue40
-rw-r--r--app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue5
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js10
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/queries/available_namespaces.query.graphql6
-rw-r--r--app/assets/javascripts/import_entities/import_groups/index.js2
-rw-r--r--app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue18
-rw-r--r--app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue52
-rw-r--r--app/assets/javascripts/import_entities/import_projects/index.js16
-rw-r--r--app/assets/javascripts/import_entities/import_projects/store/actions.js86
-rw-r--r--app/assets/javascripts/import_entities/import_projects/store/getters.js2
-rw-r--r--app/assets/javascripts/import_entities/import_projects/store/mutation_types.js8
-rw-r--r--app/assets/javascripts/import_entities/import_projects/store/mutations.js30
-rw-r--r--app/assets/javascripts/import_entities/import_projects/store/state.js5
-rw-r--r--app/assets/javascripts/import_entities/import_projects/utils.js5
-rw-r--r--app/assets/javascripts/incidents/components/incidents_list.vue2
-rw-r--r--app/assets/javascripts/incidents_settings/incidents_settings_service.js4
-rw-r--r--app/assets/javascripts/integrations/edit/components/dynamic_field.vue28
-rw-r--r--app/assets/javascripts/integrations/edit/components/integration_form.vue149
-rw-r--r--app/assets/javascripts/integrations/edit/components/integration_form_actions.vue143
-rw-r--r--app/assets/javascripts/integrations/edit/index.js1
-rw-r--r--app/assets/javascripts/invite_members/components/import_project_members_modal.vue23
-rw-r--r--app/assets/javascripts/invite_members/components/invite_group_notification.vue37
-rw-r--r--app/assets/javascripts/invite_members/components/invite_groups_modal.vue37
-rw-r--r--app/assets/javascripts/invite_members/components/invite_members_modal.vue161
-rw-r--r--app/assets/javascripts/invite_members/components/invite_modal_base.vue107
-rw-r--r--app/assets/javascripts/invite_members/components/members_token_select.vue17
-rw-r--r--app/assets/javascripts/invite_members/constants.js11
-rw-r--r--app/assets/javascripts/invite_members/init_import_project_members_modal.js4
-rw-r--r--app/assets/javascripts/invite_members/init_invite_groups_modal.js5
-rw-r--r--app/assets/javascripts/invite_members/init_invite_members_modal.js1
-rw-r--r--app/assets/javascripts/invite_members/utils/trigger_successful_invite_alert.js23
-rw-r--r--app/assets/javascripts/issuable/bulk_update_sidebar/components/move_issues_button.vue171
-rw-r--r--app/assets/javascripts/issuable/bulk_update_sidebar/components/status_dropdown.vue57
-rw-r--r--app/assets/javascripts/issuable/bulk_update_sidebar/components/subscriptions_dropdown.vue51
-rw-r--r--app/assets/javascripts/issuable/bulk_update_sidebar/constants.js23
-rw-r--r--app/assets/javascripts/issuable/bulk_update_sidebar/index.js75
-rw-r--r--app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_actions.js126
-rw-r--r--app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_sidebar.js159
-rw-r--r--app/assets/javascripts/issuable/components/related_issuable_item.vue10
-rw-r--r--app/assets/javascripts/issuable/index.js15
-rw-r--r--app/assets/javascripts/issuable/issuable_bulk_update_actions.js126
-rw-r--r--app/assets/javascripts/issuable/issuable_bulk_update_sidebar.js167
-rw-r--r--app/assets/javascripts/issuable/issuable_label_selector.js56
-rw-r--r--app/assets/javascripts/issues/create_merge_request_dropdown.js8
-rw-r--r--app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue271
-rw-r--r--app/assets/javascripts/issues/dashboard/index.js26
-rw-r--r--app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql36
-rw-r--r--app/assets/javascripts/issues/index.js2
-rw-r--r--app/assets/javascripts/issues/issue.js6
-rw-r--r--app/assets/javascripts/issues/list/components/empty_state_with_any_issues.vue53
-rw-r--r--app/assets/javascripts/issues/list/components/empty_state_without_any_issues.vue110
-rw-r--r--app/assets/javascripts/issues/list/components/issue_card_statistics.vue56
-rw-r--r--app/assets/javascripts/issues/list/components/issues_list_app.vue360
-rw-r--r--app/assets/javascripts/issues/list/components/new_issue_dropdown.vue4
-rw-r--r--app/assets/javascripts/issues/list/constants.js155
-rw-r--r--app/assets/javascripts/issues/list/index.js25
-rw-r--r--app/assets/javascripts/issues/list/queries/get_issues.query.graphql3
-rw-r--r--app/assets/javascripts/issues/list/utils.js64
-rw-r--r--app/assets/javascripts/issues/manual_ordering.js4
-rw-r--r--app/assets/javascripts/issues/related_merge_requests/store/actions.js4
-rw-r--r--app/assets/javascripts/issues/show/components/app.vue8
-rw-r--r--app/assets/javascripts/issues/show/components/description.vue14
-rw-r--r--app/assets/javascripts/issues/show/components/fields/description.vue1
-rw-r--r--app/assets/javascripts/issues/show/components/form.vue2
-rw-r--r--app/assets/javascripts/issues/show/components/header_actions.vue15
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/constants.js1
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/edit_timeline_event.vue2
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql6
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_alert.graphql1
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql6
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue73
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue73
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue59
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue5
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue7
-rw-r--r--app/assets/javascripts/issues/show/components/locked_warning.vue27
-rw-r--r--app/assets/javascripts/issues/show/components/title.vue3
-rw-r--r--app/assets/javascripts/jira_connect/branches/components/source_branch_dropdown.vue55
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/constants.js2
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/index.vue42
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions.vue35
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/version_select_form.vue1
-rw-r--r--app/assets/javascripts/jobs/components/filtered_search/jobs_filtered_search.vue18
-rw-r--r--app/assets/javascripts/jobs/components/job/empty_state.vue31
-rw-r--r--app/assets/javascripts/jobs/components/job/graphql/mutations/job_retry_with_variables.mutation.graphql16
-rw-r--r--app/assets/javascripts/jobs/components/job/graphql/queries/get_job.query.graphql17
-rw-r--r--app/assets/javascripts/jobs/components/job/job_app.vue25
-rw-r--r--app/assets/javascripts/jobs/components/job/legacy_manual_variables_form.vue192
-rw-r--r--app/assets/javascripts/jobs/components/job/manual_variables_form.vue163
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/job_sidebar_retry_button.vue29
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/legacy_sidebar_header.vue104
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/sidebar.vue21
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/sidebar_header.vue87
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/sidebar_job_details_container.vue7
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/trigger_block.vue6
-rw-r--r--app/assets/javascripts/jobs/constants.js15
-rw-r--r--app/assets/javascripts/jobs/index.js9
-rw-r--r--app/assets/javascripts/labels/labels_select.js2
-rw-r--r--app/assets/javascripts/language_switcher/components/app.vue49
-rw-r--r--app/assets/javascripts/language_switcher/constants.js1
-rw-r--r--app/assets/javascripts/language_switcher/index.js23
-rw-r--r--app/assets/javascripts/lib/dompurify.js2
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js27
-rw-r--r--app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue5
-rw-r--r--app/assets/javascripts/lib/utils/constants.js1
-rw-r--r--app/assets/javascripts/lib/utils/create_and_submit_form.js26
-rw-r--r--app/assets/javascripts/lib/utils/dom_utils.js21
-rw-r--r--app/assets/javascripts/lib/utils/http_status.js56
-rw-r--r--app/assets/javascripts/lib/utils/poll.js4
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js2
-rw-r--r--app/assets/javascripts/listbox/index.js4
-rw-r--r--app/assets/javascripts/listbox/redirect_behavior.js2
-rw-r--r--app/assets/javascripts/main.js17
-rw-r--r--app/assets/javascripts/members/components/avatars/user_avatar.vue8
-rw-r--r--app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue6
-rw-r--r--app/assets/javascripts/members/constants.js8
-rw-r--r--app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.vue2
-rw-r--r--app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.vue2
-rw-r--r--app/assets/javascripts/merge_request.js8
-rw-r--r--app/assets/javascripts/merge_request_tabs.js63
-rw-r--r--app/assets/javascripts/merge_requests/components/sticky_header.vue11
-rw-r--r--app/assets/javascripts/merge_requests/components/target_project_dropdown.vue87
-rw-r--r--app/assets/javascripts/ml/experiment_tracking/components/experiment.vue36
-rw-r--r--app/assets/javascripts/ml/experiment_tracking/components/incubation_alert.vue6
-rw-r--r--app/assets/javascripts/ml/experiment_tracking/components/ml_candidate.vue94
-rw-r--r--app/assets/javascripts/ml/experiment_tracking/components/ml_experiment.vue59
-rw-r--r--app/assets/javascripts/monitoring/components/charts/empty_chart.vue4
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue6
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard_header.vue3
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard_panel.vue8
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard_panel_builder.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue8
-rw-r--r--app/assets/javascripts/monitoring/components/group_empty_state.vue3
-rw-r--r--app/assets/javascripts/monitoring/components/refresh_button.vue16
-rw-r--r--app/assets/javascripts/monitoring/components/variables_section.vue7
-rw-r--r--app/assets/javascripts/monitoring/csv_export.js2
-rw-r--r--app/assets/javascripts/monitoring/requests/index.js9
-rw-r--r--app/assets/javascripts/monitoring/utils.js1
-rw-r--r--app/assets/javascripts/mr_notes/discussion_counter.js28
-rw-r--r--app/assets/javascripts/mr_notes/index.js36
-rw-r--r--app/assets/javascripts/mr_notes/init.js52
-rw-r--r--app/assets/javascripts/mr_notes/init_count.js13
-rw-r--r--app/assets/javascripts/mr_notes/init_notes.js33
-rw-r--r--app/assets/javascripts/nav/components/new_nav_toggle.vue71
-rw-r--r--app/assets/javascripts/new_branch_form.js8
-rw-r--r--app/assets/javascripts/notebook/cells/markdown.vue2
-rw-r--r--app/assets/javascripts/notebook/cells/output/html.vue10
-rw-r--r--app/assets/javascripts/notebook/cells/output/latex.vue2
-rw-r--r--app/assets/javascripts/notebook/cells/output/markdown.vue4
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue6
-rw-r--r--app/assets/javascripts/notes/components/diff_discussion_header.vue3
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue3
-rw-r--r--app/assets/javascripts/notes/components/note_actions.vue3
-rw-r--r--app/assets/javascripts/notes/components/note_body.vue10
-rw-r--r--app/assets/javascripts/notes/components/note_header.vue10
-rw-r--r--app/assets/javascripts/notes/components/note_signed_out_widget.vue2
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue6
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue13
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue89
-rw-r--r--app/assets/javascripts/notes/index.js53
-rw-r--r--app/assets/javascripts/notes/stores/actions.js67
-rw-r--r--app/assets/javascripts/notes/stores/getters.js20
-rw-r--r--app/assets/javascripts/notes/stores/modules/index.js1
-rw-r--r--app/assets/javascripts/notes/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js3
-rw-r--r--app/assets/javascripts/observability/components/observability_app.vue71
-rw-r--r--app/assets/javascripts/observability/components/skeleton/dashboards.vue29
-rw-r--r--app/assets/javascripts/observability/components/skeleton/explore.vue27
-rw-r--r--app/assets/javascripts/observability/components/skeleton/index.vue89
-rw-r--r--app/assets/javascripts/observability/components/skeleton/manage.vue25
-rw-r--r--app/assets/javascripts/observability/constants.js16
-rw-r--r--app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/delete_alert.vue14
-rw-r--r--app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list.vue2
-rw-r--r--app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/details.js6
-rw-r--r--app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/details.vue13
-rw-r--r--app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue2
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/pages/details.vue4
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/utils.js2
-rw-r--r--app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list_app.vue6
-rw-r--r--app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/actions.js3
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue5
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/list/package_search.vue20
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/graphql/fragments/package_data.fragment.graphql3
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql1
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/pages/details.vue15
-rw-r--r--app/assets/javascripts/packages_and_registries/shared/components/package_path.vue2
-rw-r--r--app/assets/javascripts/packages_and_registries/shared/constants/package_registry.js2
-rw-r--r--app/assets/javascripts/packages_and_registries/shared/utils.js2
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue10
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/signup_restrictions.js1
-rw-r--r--app/assets/javascripts/pages/admin/broadcast_messages/edit/index.js8
-rw-r--r--app/assets/javascripts/pages/admin/broadcast_messages/index.js10
-rw-r--r--app/assets/javascripts/pages/admin/broadcast_messages/index/index.js10
-rw-r--r--app/assets/javascripts/pages/admin/dashboard/index.js3
-rw-r--r--app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue3
-rw-r--r--app/assets/javascripts/pages/dashboard/todos/index/todos.js50
-rw-r--r--app/assets/javascripts/pages/groups/merge_requests/index.js8
-rw-r--r--app/assets/javascripts/pages/help/index/index.js2
-rw-r--r--app/assets/javascripts/pages/import/fogbugz/new_user_map/components/user_select.vue8
-rw-r--r--app/assets/javascripts/pages/import/gitlab_projects/new/index.js2
-rw-r--r--app/assets/javascripts/pages/import/manifest/new/index.js3
-rw-r--r--app/assets/javascripts/pages/import/phabricator/new/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/branches/new/index.js7
-rw-r--r--app/assets/javascripts/pages/projects/ci/lints/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/ci/pipeline_editor/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/commits/show/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/cycle_analytics/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/environments/show/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue87
-rw-r--r--app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue32
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/creations/new/compare.js5
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js27
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/diffs/index.js5
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/index/index.js9
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js2
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/page.js45
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/show/index.js48
-rw-r--r--app/assets/javascripts/pages/projects/ml/candidates/show/index.js27
-rw-r--r--app/assets/javascripts/pages/projects/ml/experiments/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/new/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue13
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js82
-rw-r--r--app/assets/javascripts/pages/projects/project.js27
-rw-r--r--app/assets/javascripts/pages/projects/settings/merge_requests/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue206
-rw-r--r--app/assets/javascripts/pages/projects/shared/web_ide_link/index.js11
-rw-r--r--app/assets/javascripts/pages/projects/tags/new/index.js4
-rw-r--r--app/assets/javascripts/pages/registrations/new/index.js3
-rw-r--r--app/assets/javascripts/pages/sessions/new/index.js2
-rw-r--r--app/assets/javascripts/pages/shared/wikis/components/wiki_content.vue9
-rw-r--r--app/assets/javascripts/pages/shared/wikis/render_gfm_facade.js5
-rw-r--r--app/assets/javascripts/pages/web_ide/remote_ide/index.js3
-rw-r--r--app/assets/javascripts/performance_bar/components/detailed_metric.vue23
-rw-r--r--app/assets/javascripts/performance_bar/components/performance_bar_app.vue4
-rw-r--r--app/assets/javascripts/performance_bar/components/request_warning.vue5
-rw-r--r--app/assets/javascripts/performance_bar/constants.js10
-rw-r--r--app/assets/javascripts/pipeline_editor/components/editor/ci_editor_header.vue70
-rw-r--r--app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue254
-rw-r--r--app/assets/javascripts/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue72
-rw-r--r--app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue188
-rw-r--r--app/assets/javascripts/pipeline_editor/components/header/validation_segment.vue126
-rw-r--r--app/assets/javascripts/pipeline_editor/components/ui/editor_tab.vue154
-rw-r--r--app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_empty_state.vue72
-rw-r--r--app/assets/javascripts/pipeline_new/components/legacy_pipeline_new_form.vue490
-rw-r--r--app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue4
-rw-r--r--app/assets/javascripts/pipeline_new/index.js50
-rw-r--r--app/assets/javascripts/pipeline_wizard/components/step_nav.vue3
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue7
-rw-r--r--app/assets/javascripts/pipelines/components/graph/job_item.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue5
-rw-r--r--app/assets/javascripts/pipelines/components/jobs_shared/action_component.vue1
-rw-r--r--app/assets/javascripts/pipelines/components/jobs_shared/job_name_component.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_mini_graph/job_item.vue8
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue14
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stage.vue12
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stages.vue14
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines_filtered_search.vue12
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/test_reports/test_reports.vue14
-rw-r--r--app/assets/javascripts/pipelines/mixins/pipelines_mixin.js2
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_bundle.js65
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_dag.js42
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_failed_jobs.js36
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_graph.js35
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_jobs.js34
-rw-r--r--app/assets/javascripts/pipelines/pipeline_test_details.js40
-rw-r--r--app/assets/javascripts/popovers/components/popovers.vue5
-rw-r--r--app/assets/javascripts/profile/account/components/update_username.vue3
-rw-r--r--app/assets/javascripts/projects/commit/components/branches_dropdown.vue7
-rw-r--r--app/assets/javascripts/projects/commit/components/form_modal.vue12
-rw-r--r--app/assets/javascripts/projects/commit/init_revert_commit_modal.js1
-rw-r--r--app/assets/javascripts/projects/commits/index.js31
-rw-r--r--app/assets/javascripts/projects/compare/components/repo_dropdown.vue2
-rw-r--r--app/assets/javascripts/projects/compare/components/revision_card.vue2
-rw-r--r--app/assets/javascripts/projects/default_project_templates.js8
-rw-r--r--app/assets/javascripts/projects/new/components/app.vue2
-rw-r--r--app/assets/javascripts/projects/new/components/new_project_url_select.vue23
-rw-r--r--app/assets/javascripts/projects/new/constants.js2
-rw-r--r--app/assets/javascripts/projects/new/index.js2
-rw-r--r--app/assets/javascripts/projects/project_name_rules.js28
-rw-r--r--app/assets/javascripts/projects/project_new.js14
-rw-r--r--app/assets/javascripts/projects/settings/access_dropdown.js2
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js1
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue40
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js9
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/queries/branch_rules_details.query.graphql1
-rw-r--r--app/assets/javascripts/projects/settings/repository/branch_rules/app.vue11
-rw-r--r--app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue57
-rw-r--r--app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql24
-rw-r--r--app/assets/javascripts/projects/settings/utils.js17
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue5
-rw-r--r--app/assets/javascripts/protected_tags/protected_tag_create.js2
-rw-r--r--app/assets/javascripts/releases/components/app_index.vue1
-rw-r--r--app/assets/javascripts/releases/components/release_block.vue15
-rw-r--r--app/assets/javascripts/releases/components/release_block_footer.vue34
-rw-r--r--app/assets/javascripts/releases/graphql/fragments/release_for_editing.fragment.graphql1
-rw-r--r--app/assets/javascripts/releases/util.js3
-rw-r--r--app/assets/javascripts/reports/codequality_report/components/codequality_issue_body.vue76
-rw-r--r--app/assets/javascripts/reports/codequality_report/constants.js31
-rw-r--r--app/assets/javascripts/reports/components/grouped_issues_list.vue106
-rw-r--r--app/assets/javascripts/reports/components/issue_body.js17
-rw-r--r--app/assets/javascripts/reports/components/issues_list.vue119
-rw-r--r--app/assets/javascripts/reports/components/report_item.vue67
-rw-r--r--app/assets/javascripts/repository/components/last_commit.vue12
-rw-r--r--app/assets/javascripts/repository/components/preview/index.vue8
-rw-r--r--app/assets/javascripts/repository/components/table/index.vue10
-rw-r--r--app/assets/javascripts/repository/components/table/row.vue34
-rw-r--r--app/assets/javascripts/repository/components/tree_content.vue2
-rw-r--r--app/assets/javascripts/repository/constants.js1
-rw-r--r--app/assets/javascripts/repository/index.js31
-rw-r--r--app/assets/javascripts/repository/queries/commit.query.graphql7
-rw-r--r--app/assets/javascripts/repository/utils/ref_switcher_utils.js30
-rw-r--r--app/assets/javascripts/search/sidebar/components/confidentiality_filter.vue12
-rw-r--r--app/assets/javascripts/search/sidebar/components/results_filters.vue27
-rw-r--r--app/assets/javascripts/search/sidebar/components/scope_navigation.vue47
-rw-r--r--app/assets/javascripts/search/sidebar/components/status_filter.vue12
-rw-r--r--app/assets/javascripts/search/sidebar/constants/index.js2
-rw-r--r--app/assets/javascripts/search/topbar/components/app.vue71
-rw-r--r--app/assets/javascripts/search/topbar/components/searchable_dropdown_item.vue3
-rw-r--r--app/assets/javascripts/search/topbar/constants.js2
-rw-r--r--app/assets/javascripts/search/topbar/index.js20
-rw-r--r--app/assets/javascripts/security_configuration/components/training_provider_list.vue4
-rw-r--r--app/assets/javascripts/self_monitor/components/self_monitor_form.vue13
-rw-r--r--app/assets/javascripts/self_monitor/store/actions.js8
-rw-r--r--app/assets/javascripts/sentry/constants.js1
-rw-r--r--app/assets/javascripts/sentry/index.js16
-rw-r--r--app/assets/javascripts/sentry/legacy_index.js34
-rw-r--r--app/assets/javascripts/sentry/legacy_sentry_config.js64
-rw-r--r--app/assets/javascripts/sentry/sentry_browser_wrapper.js27
-rw-r--r--app/assets/javascripts/sentry/sentry_config.js39
-rw-r--r--app/assets/javascripts/set_status_modal/set_status_form.vue4
-rw-r--r--app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignees_realtime.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue8
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/copy/copy_email_to_clipboard.vue24
-rw-r--r--app/assets/javascripts/sidebar/components/copy/copyable_field.vue (renamed from app/assets/javascripts/vue_shared/components/sidebar/copyable_field.vue)0
-rw-r--r--app/assets/javascripts/sidebar/components/copy/sidebar_reference_widget.vue59
-rw-r--r--app/assets/javascripts/sidebar/components/copy_email_to_clipboard.vue24
-rw-r--r--app/assets/javascripts/sidebar/components/crm_contacts/crm_contacts.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue10
-rw-r--r--app/assets/javascripts/sidebar/components/incidents/constants.js25
-rw-r--r--app/assets/javascripts/sidebar/components/incidents/escalation_status.vue9
-rw-r--r--app/assets/javascripts/sidebar/components/incidents/sidebar_escalation_status.vue9
-rw-r--r--app/assets/javascripts/sidebar/components/incidents/utils.js5
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_vue/constants.js (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_button.vue45
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_contents.vue48
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_contents_create_view.vue122
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view.vue230
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_title.vue46
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_value.vue74
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_value_collapsed.vue53
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_vue/label_item.vue109
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_vue/labels_select_root.vue345
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/actions.js (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/getters.js (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/getters.js)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/index.js (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/index.js)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/mutation_types.js (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutation_types.js)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/mutations.js (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/state.js (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/state.js)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/constants.js (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/constants.js)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents.vue244
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view.vue200
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents_labels_view.vue177
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_footer.vue (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_footer.vue)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_header.vue (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_value.vue (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/embedded_labels_list.vue73
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/create_label.mutation.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/create_label.mutation.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/epic_labels.query.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_labels.query.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/epic_update_labels.mutation.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_update_labels.mutation.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/group_labels.query.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/issue_labels.query.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/merge_request_labels.query.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/merge_request_labels.query.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/project_labels.query.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/label_item.vue (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue)0
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/labels_select_root.vue441
-rw-r--r--app/assets/javascripts/sidebar/components/labels/labels_select_widget/utils.js (renamed from app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/utils.js)0
-rw-r--r--app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/move/issuable_move_dropdown.vue (renamed from app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue)0
-rw-r--r--app/assets/javascripts/sidebar/components/move/move_issues_button.vue171
-rw-r--r--app/assets/javascripts/sidebar/components/participants/sidebar_participants_widget.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/reference/sidebar_reference_widget.vue59
-rw-r--r--app/assets/javascripts/sidebar/components/reviewers/reviewers.vue20
-rw-r--r--app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue16
-rw-r--r--app/assets/javascripts/sidebar/components/severity/constants.js41
-rw-r--r--app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/status/status_dropdown.vue57
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue51
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/constants.js1
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/create_timelog_form.vue227
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/help_state.vue5
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/report.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue40
-rw-r--r--app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/todo_toggle/todo_button.vue44
-rw-r--r--app/assets/javascripts/sidebar/components/toggle/toggle_sidebar.vue (renamed from app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue)0
-rw-r--r--app/assets/javascripts/sidebar/constants.js187
-rw-r--r--app/assets/javascripts/sidebar/mount_milestone_sidebar.js1
-rw-r--r--app/assets/javascripts/sidebar/mount_sidebar.js100
-rw-r--r--app/assets/javascripts/sidebar/queries/create_timelog.mutation.graphql17
-rw-r--r--app/assets/javascripts/sidebar/queries/delete_timelog.mutation.graphql (renamed from app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/get_alert_assignees.query.graphql22
-rw-r--r--app/assets/javascripts/sidebar/queries/get_issue_assignees.query.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/get_issue_crm_contacts.query.graphql (renamed from app/assets/javascripts/sidebar/components/crm_contacts/queries/get_issue_crm_contacts.query.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/get_issue_participants.query.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/get_issue_timelogs.query.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/get_merge_request_reviewers.query.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/queries/get_merge_request_reviewers.query.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/get_mr_assignees.query.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/get_mr_participants.query.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_participants.query.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/get_mr_timelogs.query.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_timelogs.query.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/issue_crm_contacts.fragment.graphql (renamed from app/assets/javascripts/sidebar/components/crm_contacts/queries/issue_crm_contacts.fragment.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/issue_crm_contacts.subscription.graphql (renamed from app/assets/javascripts/sidebar/components/crm_contacts/queries/issue_crm_contacts.subscription.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/merge_request_reviewers.subscription.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/queries/merge_request_reviewers.subscription.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/move_issue.mutation.graphql (renamed from app/assets/javascripts/issuable/bulk_update_sidebar/components/graphql/mutations/move_issue.mutation.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/update_issuable_severity.mutation.graphql (renamed from app/assets/javascripts/sidebar/components/severity/graphql/mutations/update_issuable_severity.mutation.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/update_issue_assignees.mutation.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/update_issue_lock.mutation.graphql (renamed from app/assets/javascripts/sidebar/components/lock/mutations/update_issue_lock.mutation.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/update_merge_request_lock.mutation.graphql (renamed from app/assets/javascripts/sidebar/components/lock/mutations/update_merge_request_lock.mutation.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/queries/update_mr_assignees.mutation.graphql (renamed from app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql)0
-rw-r--r--app/assets/javascripts/sidebar/sidebar_mediator.js15
-rw-r--r--app/assets/javascripts/sidebar/utils.js24
-rw-r--r--app/assets/javascripts/snippets/components/snippet_description_view.vue2
-rw-r--r--app/assets/javascripts/surveys/merge_request_experience/app.vue5
-rw-r--r--app/assets/javascripts/tags/init_new_tag_ref_selector.js23
-rw-r--r--app/assets/javascripts/terms/components/app.vue12
-rw-r--r--app/assets/javascripts/terraform/components/init_command_modal.vue8
-rw-r--r--app/assets/javascripts/tooltips/components/tooltips.vue3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue7
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment/memory_usage.vue6
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue21
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/child_content.vue12
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue18
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue9
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/state_container.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/merge_checks_failed.vue15
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue64
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue5
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue52
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/unresolved_discussions.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue6
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/action_buttons.vue134
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/dynamic_content.vue11
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/status_icon.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue44
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_row.vue35
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/extensions/code_quality/constants.js31
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/extensions/code_quality/index.js88
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/i18n.js7
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue38
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/queries/get_state.subscription.graphql7
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.fragment.graphql39
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.subscription.graphql9
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge_merge_request.fragment.graphql39
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js8
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue12
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/alert_status.vue2
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue2
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_header.vue2
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/system_notes/system_note.vue5
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/graphql/mutations/alert_set_assignees.mutation.graphql1
-rw-r--r--app/assets/javascripts/vue_shared/components/actions_button.vue112
-rw-r--r--app/assets/javascripts/vue_shared/components/awards_list.vue10
-rw-r--r--app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/code_block_highlighted.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue10
-rw-r--r--app/assets/javascripts/vue_shared/components/confirm_modal.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/dismissible_alert.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js57
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue14
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue119
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/user_token.vue119
-rw-r--r--app/assets/javascripts/vue_shared/components/group_select/group_select.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/header_ci_component.vue12
-rw-r--r--app/assets/javascripts/vue_shared/components/help_popover.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.stories.js26
-rw-r--r--app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.vue110
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/apply_suggestion.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue11
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field_view.vue10
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_row.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/suggestions.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.stories.js (renamed from app/assets/javascripts/vue_shared/components/markdown_drawer/makrdown_drawer.stories.js)0
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.vue21
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown_drawer/utils/fetch.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/metric_images/metric_images_tab.vue7
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/system_note.vue17
-rw-r--r--app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue45
-rw-r--r--app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/registry/registry_search.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_button.vue45
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents.vue48
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue122
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue230
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue46
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value.vue74
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed.vue53
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue109
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue345
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue241
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue200
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue177
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue406
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql21
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js21
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue44
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js21
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue13
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/plugins/link_dependencies.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/plugins/utils/go_sum_linker.js34
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/web_ide_link.vue161
-rw-r--r--app/assets/javascripts/vue_shared/constants.js4
-rw-r--r--app/assets/javascripts/vue_shared/issuable/create/components/issuable_form.vue4
-rw-r--r--app/assets/javascripts/vue_shared/issuable/create/components/issuable_label_selector.vue92
-rw-r--r--app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue4
-rw-r--r--app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue1
-rw-r--r--app/assets/javascripts/vue_shared/issuable/show/components/issuable_description.vue12
-rw-r--r--app/assets/javascripts/vue_shared/issuable/show/components/issuable_title.vue10
-rw-r--r--app/assets/javascripts/vue_shared/new_namespace/components/legacy_container.vue3
-rw-r--r--app/assets/javascripts/vue_shared/new_namespace/components/welcome.vue2
-rw-r--r--app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue3
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue4
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/store/getters.js2
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/store/utils.js8
-rw-r--r--app/assets/javascripts/webhooks/components/push_events.vue2
-rw-r--r--app/assets/javascripts/webhooks/constants.js4
-rw-r--r--app/assets/javascripts/whats_new/components/feature.vue5
-rw-r--r--app/assets/javascripts/work_items/components/notes/system_note.vue229
-rw-r--r--app/assets/javascripts/work_items/components/work_item_assignees.vue1
-rw-r--r--app/assets/javascripts/work_items/components/work_item_description.vue89
-rw-r--r--app/assets/javascripts/work_items/components/work_item_description_rendered.vue16
-rw-r--r--app/assets/javascripts/work_items/components/work_item_detail.vue241
-rw-r--r--app/assets/javascripts/work_items/components/work_item_detail_modal.vue7
-rw-r--r--app/assets/javascripts/work_items/components/work_item_information.vue53
-rw-r--r--app/assets/javascripts/work_items/components/work_item_labels.vue6
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/index.js2
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/okr_actions_split_button.vue66
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue248
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/work_item_link_child_metadata.vue123
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue111
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue86
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue244
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/work_item_tree_children.vue68
-rw-r--r--app/assets/javascripts/work_items/components/work_item_milestone.vue10
-rw-r--r--app/assets/javascripts/work_items/components/work_item_notes.vue109
-rw-r--r--app/assets/javascripts/work_items/components/work_item_type_icon.vue5
-rw-r--r--app/assets/javascripts/work_items/constants.js51
-rw-r--r--app/assets/javascripts/work_items/graphql/discussion.fragment.graphql12
-rw-r--r--app/assets/javascripts/work_items/graphql/milestone.fragment.graphql3
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item_links.query.graphql3
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item_metadata_widgets.fragment.graphql29
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item_notes.query.graphql27
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item_notes_by_iid.query.graphql32
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item_tree.query.graphql53
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql28
-rw-r--r--app/assets/javascripts/work_items/index.js12
-rw-r--r--app/assets/javascripts/work_items/pages/work_item_root.vue6
-rw-r--r--app/assets/javascripts/work_items/utils.js6
-rw-r--r--app/assets/stylesheets/_page_specific_files.scss4
-rw-r--r--app/assets/stylesheets/components/content_editor.scss12
-rw-r--r--app/assets/stylesheets/components/ref_selector.scss2
-rw-r--r--app/assets/stylesheets/fonts.scss32
-rw-r--r--app/assets/stylesheets/framework/common.scss4
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss14
-rw-r--r--app/assets/stylesheets/framework/emojis.scss4
-rw-r--r--app/assets/stylesheets/framework/filters.scss5
-rw-r--r--app/assets/stylesheets/framework/forms.scss38
-rw-r--r--app/assets/stylesheets/framework/header.scss57
-rw-r--r--app/assets/stylesheets/framework/kbd.scss4
-rw-r--r--app/assets/stylesheets/framework/mixins.scss2
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss644
-rw-r--r--app/assets/stylesheets/framework/typography.scss15
-rw-r--r--app/assets/stylesheets/framework/variables.scss71
-rw-r--r--app/assets/stylesheets/page_bundles/_pipeline_mixins.scss23
-rw-r--r--app/assets/stylesheets/page_bundles/alert_management_details.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/boards.scss21
-rw-r--r--app/assets/stylesheets/page_bundles/clusters.scss8
-rw-r--r--app/assets/stylesheets/page_bundles/ide.scss14
-rw-r--r--app/assets/stylesheets/page_bundles/incidents.scss9
-rw-r--r--app/assets/stylesheets/page_bundles/issuable.scss183
-rw-r--r--app/assets/stylesheets/page_bundles/issuable_list.scss96
-rw-r--r--app/assets/stylesheets/page_bundles/merge_requests.scss69
-rw-r--r--app/assets/stylesheets/page_bundles/milestone.scss10
-rw-r--r--app/assets/stylesheets/page_bundles/oncall_schedules.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/pipelines.scss39
-rw-r--r--app/assets/stylesheets/page_bundles/search.scss336
-rw-r--r--app/assets/stylesheets/page_bundles/settings.scss209
-rw-r--r--app/assets/stylesheets/page_bundles/todos.scss81
-rw-r--r--app/assets/stylesheets/page_bundles/tree.scss15
-rw-r--r--app/assets/stylesheets/page_bundles/users.scss70
-rw-r--r--app/assets/stylesheets/page_bundles/work_items.scss4
-rw-r--r--app/assets/stylesheets/pages/colors.scss8
-rw-r--r--app/assets/stylesheets/pages/commits.scss5
-rw-r--r--app/assets/stylesheets/pages/events.scss2
-rw-r--r--app/assets/stylesheets/pages/issuable.scss912
-rw-r--r--app/assets/stylesheets/pages/login.scss2
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss6
-rw-r--r--app/assets/stylesheets/pages/ml_experiment_tracking.scss6
-rw-r--r--app/assets/stylesheets/pages/monitor.scss5
-rw-r--r--app/assets/stylesheets/pages/note_form.scss3
-rw-r--r--app/assets/stylesheets/pages/notes.scss38
-rw-r--r--app/assets/stylesheets/pages/projects.scss87
-rw-r--r--app/assets/stylesheets/pages/search.scss405
-rw-r--r--app/assets/stylesheets/pages/settings.scss228
-rw-r--r--app/assets/stylesheets/pages/users.scss68
-rw-r--r--app/assets/stylesheets/startup/startup-dark.scss394
-rw-r--r--app/assets/stylesheets/startup/startup-general.scss141
-rw-r--r--app/assets/stylesheets/startup/startup-signin.scss23
-rw-r--r--app/assets/stylesheets/themes/_dark.scss182
-rw-r--r--app/assets/stylesheets/themes/dark_mode_overrides.scss143
-rw-r--r--app/assets/stylesheets/utilities.scss12
-rw-r--r--app/channels/graphql_channel.rb4
-rw-r--r--app/components/diffs/stats_component.rb16
-rw-r--r--app/components/pajamas/button_component.rb2
-rw-r--r--app/controllers/abuse_reports_controller.rb2
-rw-r--r--app/controllers/admin/abuse_reports_controller.rb2
-rw-r--r--app/controllers/admin/application_settings/appearances_controller.rb1
-rw-r--r--app/controllers/admin/application_settings_controller.rb16
-rw-r--r--app/controllers/admin/background_jobs_controller.rb6
-rw-r--r--app/controllers/admin/background_migrations_controller.rb98
-rw-r--r--app/controllers/admin/batched_jobs_controller.rb34
-rw-r--r--app/controllers/admin/broadcast_messages_controller.rb154
-rw-r--r--app/controllers/admin/ci/variables_controller.rb80
-rw-r--r--app/controllers/admin/groups_controller.rb5
-rw-r--r--app/controllers/admin/plan_limits_controller.rb1
-rw-r--r--app/controllers/admin/projects_controller.rb8
-rw-r--r--app/controllers/admin/spam_logs_controller.rb2
-rw-r--r--app/controllers/admin/system_info_controller.rb10
-rw-r--r--app/controllers/admin/users_controller.rb14
-rw-r--r--app/controllers/application_controller.rb20
-rw-r--r--app/controllers/concerns/authenticates_with_two_factor.rb2
-rw-r--r--app/controllers/concerns/controller_with_cross_project_access_check.rb4
-rw-r--r--app/controllers/concerns/creates_commit.rb2
-rw-r--r--app/controllers/concerns/cycle_analytics_params.rb5
-rw-r--r--app/controllers/concerns/enforces_two_factor_authentication.rb18
-rw-r--r--app/controllers/concerns/impersonation.rb4
-rw-r--r--app/controllers/concerns/import/github_oauth.rb1
-rw-r--r--app/controllers/concerns/integrations/params.rb4
-rw-r--r--app/controllers/concerns/invisible_captcha_on_signup.rb2
-rw-r--r--app/controllers/concerns/issuable_actions.rb18
-rw-r--r--app/controllers/concerns/issuable_collections.rb4
-rw-r--r--app/controllers/concerns/issues_calendar.rb4
-rw-r--r--app/controllers/concerns/labels_as_hash.rb4
-rw-r--r--app/controllers/concerns/lfs_request.rb22
-rw-r--r--app/controllers/concerns/membership_actions.rb16
-rw-r--r--app/controllers/concerns/metrics/dashboard/prometheus_api_proxy.rb4
-rw-r--r--app/controllers/concerns/metrics_dashboard.rb4
-rw-r--r--app/controllers/concerns/milestone_actions.rb24
-rw-r--r--app/controllers/concerns/notes_actions.rb38
-rw-r--r--app/controllers/concerns/oauth_applications.rb6
-rw-r--r--app/controllers/concerns/observability/content_security_policy.rb25
-rw-r--r--app/controllers/concerns/page_limiter.rb9
-rw-r--r--app/controllers/concerns/paginated_collection.rb4
-rw-r--r--app/controllers/concerns/preferred_language_switcher.rb2
-rw-r--r--app/controllers/concerns/preview_markdown.rb8
-rw-r--r--app/controllers/concerns/product_analytics_tracking.rb68
-rw-r--r--app/controllers/concerns/record_user_last_activity.rb5
-rw-r--r--app/controllers/concerns/render_service_results.rb24
-rw-r--r--app/controllers/concerns/renders_ldap_servers.rb12
-rw-r--r--app/controllers/concerns/routable_actions.rb10
-rw-r--r--app/controllers/concerns/snippets/blobs_actions.rb17
-rw-r--r--app/controllers/concerns/sorting_preference.rb4
-rw-r--r--app/controllers/concerns/sourcegraph_decorator.rb4
-rw-r--r--app/controllers/concerns/uploads_actions.rb24
-rw-r--r--app/controllers/concerns/verifies_with_email.rb10
-rw-r--r--app/controllers/concerns/vscode_cdn_csp.rb17
-rw-r--r--app/controllers/concerns/web_hooks/hook_actions.rb19
-rw-r--r--app/controllers/dashboard/snippets_controller.rb2
-rw-r--r--app/controllers/dashboard/todos_controller.rb39
-rw-r--r--app/controllers/explore/snippets_controller.rb2
-rw-r--r--app/controllers/google_api/authorizations_controller.rb11
-rw-r--r--app/controllers/graphql_controller.rb6
-rw-r--r--app/controllers/groups/application_controller.rb22
-rw-r--r--app/controllers/groups/boards_controller.rb10
-rw-r--r--app/controllers/groups/dependency_proxy_for_containers_controller.rb2
-rw-r--r--app/controllers/groups/observability_controller.rb23
-rw-r--r--app/controllers/groups/settings/ci_cd_controller.rb2
-rw-r--r--app/controllers/groups/usage_quotas_controller.rb28
-rw-r--r--app/controllers/groups/variables_controller.rb2
-rw-r--r--app/controllers/groups_controller.rb2
-rw-r--r--app/controllers/ide_controller.rb1
-rw-r--r--app/controllers/import/bitbucket_controller.rb23
-rw-r--r--app/controllers/import/bulk_imports_controller.rb2
-rw-r--r--app/controllers/import/fogbugz_controller.rb4
-rw-r--r--app/controllers/import/gitea_controller.rb26
-rw-r--r--app/controllers/import/github_controller.rb48
-rw-r--r--app/controllers/jira_connect/app_descriptor_controller.rb6
-rw-r--r--app/controllers/jira_connect/application_controller.rb26
-rw-r--r--app/controllers/jira_connect/cors_preflight_checks_controller.rb16
-rw-r--r--app/controllers/jira_connect/events_controller.rb7
-rw-r--r--app/controllers/jira_connect/installations_controller.rb12
-rw-r--r--app/controllers/jira_connect/oauth_application_ids_controller.rb3
-rw-r--r--app/controllers/jira_connect/public_keys_controller.rb4
-rw-r--r--app/controllers/jira_connect/subscriptions_controller.rb10
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb2
-rw-r--r--app/controllers/passwords_controller.rb4
-rw-r--r--app/controllers/profiles/keys_controller.rb2
-rw-r--r--app/controllers/profiles/preferences_controller.rb3
-rw-r--r--app/controllers/profiles/two_factor_auths_controller.rb16
-rw-r--r--app/controllers/projects/autocomplete_sources_controller.rb2
-rw-r--r--app/controllers/projects/badges_controller.rb30
-rw-r--r--app/controllers/projects/blame_controller.rb2
-rw-r--r--app/controllers/projects/blob_controller.rb3
-rw-r--r--app/controllers/projects/branches_controller.rb2
-rw-r--r--app/controllers/projects/ci/daily_build_group_report_results_controller.rb2
-rw-r--r--app/controllers/projects/clusters_controller.rb1
-rw-r--r--app/controllers/projects/commit_controller.rb2
-rw-r--r--app/controllers/projects/commits_controller.rb14
-rw-r--r--app/controllers/projects/compare_controller.rb4
-rw-r--r--app/controllers/projects/environments_controller.rb6
-rw-r--r--app/controllers/projects/find_file_controller.rb2
-rw-r--r--app/controllers/projects/forks_controller.rb4
-rw-r--r--app/controllers/projects/graphs_controller.rb38
-rw-r--r--app/controllers/projects/incidents_controller.rb1
-rw-r--r--app/controllers/projects/issues_controller.rb15
-rw-r--r--app/controllers/projects/jobs_controller.rb3
-rw-r--r--app/controllers/projects/merge_requests/creations_controller.rb13
-rw-r--r--app/controllers/projects/merge_requests/diffs_controller.rb8
-rw-r--r--app/controllers/projects/merge_requests_controller.rb153
-rw-r--r--app/controllers/projects/metrics_dashboard_controller.rb4
-rw-r--r--app/controllers/projects/ml/candidates_controller.rb23
-rw-r--r--app/controllers/projects/ml/experiments_controller.rb1
-rw-r--r--app/controllers/projects/network_controller.rb10
-rw-r--r--app/controllers/projects/performance_monitoring/dashboards_controller.rb4
-rw-r--r--app/controllers/projects/pipelines_controller.rb5
-rw-r--r--app/controllers/projects/protected_branches_controller.rb6
-rw-r--r--app/controllers/projects/raw_controller.rb4
-rw-r--r--app/controllers/projects/refs_controller.rb20
-rw-r--r--app/controllers/projects/registry/repositories_controller.rb4
-rw-r--r--app/controllers/projects/runner_projects_controller.rb2
-rw-r--r--app/controllers/projects/service_desk_controller.rb2
-rw-r--r--app/controllers/projects/service_ping_controller.rb6
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb13
-rw-r--r--app/controllers/projects/settings/integrations_controller.rb4
-rw-r--r--app/controllers/projects/settings/repository_controller.rb8
-rw-r--r--app/controllers/projects/snippets/application_controller.rb2
-rw-r--r--app/controllers/projects/tags_controller.rb2
-rw-r--r--app/controllers/projects/tree_controller.rb3
-rw-r--r--app/controllers/projects/variables_controller.rb2
-rw-r--r--app/controllers/projects/work_items_controller.rb3
-rw-r--r--app/controllers/projects_controller.rb38
-rw-r--r--app/controllers/registrations/welcome_controller.rb8
-rw-r--r--app/controllers/registrations_controller.rb39
-rw-r--r--app/controllers/repositories/lfs_locks_api_controller.rb8
-rw-r--r--app/controllers/repositories/lfs_storage_controller.rb2
-rw-r--r--app/controllers/search_controller.rb25
-rw-r--r--app/controllers/snippets/application_controller.rb2
-rw-r--r--app/controllers/snippets/notes_controller.rb2
-rw-r--r--app/controllers/users_controller.rb3
-rw-r--r--app/controllers/web_ide/remote_ide_controller.rb53
-rw-r--r--app/events/gitlab_subscriptions/renewed_event.rb17
-rw-r--r--app/experiments/concerns/project_commit_count.rb6
-rw-r--r--app/finders/autocomplete/routes_finder.rb6
-rw-r--r--app/finders/ci/freeze_periods_finder.rb16
-rw-r--r--app/finders/ci/jobs_finder.rb11
-rw-r--r--app/finders/ci/pipelines_finder.rb10
-rw-r--r--app/finders/ci/runners_finder.rb17
-rw-r--r--app/finders/clusters/agent_tokens_finder.rb22
-rw-r--r--app/finders/deployments_finder.rb1
-rw-r--r--app/finders/environments/environments_finder.rb10
-rw-r--r--app/finders/freeze_periods_finder.rb14
-rw-r--r--app/finders/git_refs_finder.rb53
-rw-r--r--app/finders/group_descendants_finder.rb2
-rw-r--r--app/finders/group_members_finder.rb4
-rw-r--r--app/finders/members_finder.rb4
-rw-r--r--app/finders/merge_request_target_project_finder.rb2
-rw-r--r--app/finders/notes_finder.rb6
-rw-r--r--app/finders/personal_access_tokens_finder.rb2
-rw-r--r--app/finders/projects_finder.rb17
-rw-r--r--app/finders/releases/group_releases_finder.rb4
-rw-r--r--app/finders/repositories/tree_finder.rb26
-rw-r--r--app/finders/todos_finder.rb2
-rw-r--r--app/finders/users_finder.rb6
-rw-r--r--app/graphql/graphql_triggers.rb8
-rw-r--r--app/graphql/mutations/alert_management/alerts/set_assignees.rb2
-rw-r--r--app/graphql/mutations/alert_management/alerts/todo/create.rb2
-rw-r--r--app/graphql/mutations/alert_management/base.rb18
-rw-r--r--app/graphql/mutations/alert_management/create_alert_issue.rb2
-rw-r--r--app/graphql/mutations/alert_management/update_alert_status.rb2
-rw-r--r--app/graphql/mutations/ci/pipeline_schedule/create.rb72
-rw-r--r--app/graphql/mutations/ci/pipeline_schedule/play.rb32
-rw-r--r--app/graphql/mutations/ci/pipeline_schedule/variable_input_type.rb19
-rw-r--r--app/graphql/mutations/ci/runner/update.rb2
-rw-r--r--app/graphql/mutations/clusters/agent_tokens/create.rb6
-rw-r--r--app/graphql/mutations/container_repositories/destroy.rb4
-rw-r--r--app/graphql/mutations/incident_management/timeline_event/update.rb4
-rw-r--r--app/graphql/mutations/issues/link_alerts.rb26
-rw-r--r--app/graphql/mutations/issues/unlink_alert.rb33
-rw-r--r--app/graphql/mutations/notes/create/diff_note.rb8
-rw-r--r--app/graphql/mutations/notes/create/image_diff_note.rb6
-rw-r--r--app/graphql/mutations/notes/create/note.rb6
-rw-r--r--app/graphql/mutations/timelogs/create.rb8
-rw-r--r--app/graphql/mutations/todos/restore_many.rb6
-rw-r--r--app/graphql/mutations/work_items/create.rb2
-rw-r--r--app/graphql/resolvers/base_resolver.rb9
-rw-r--r--app/graphql/resolvers/ci/project_runners_resolver.rb15
-rw-r--r--app/graphql/resolvers/ci/runner_groups_resolver.rb45
-rw-r--r--app/graphql/resolvers/ci/runner_jobs_resolver.rb12
-rw-r--r--app/graphql/resolvers/ci/runner_owner_project_resolver.rb26
-rw-r--r--app/graphql/resolvers/ci/runner_projects_resolver.rb14
-rw-r--r--app/graphql/resolvers/clusters/agent_tokens_resolver.rb13
-rw-r--r--app/graphql/resolvers/concerns/looks_ahead.rb16
-rw-r--r--app/graphql/resolvers/concerns/resolves_groups.rb1
-rw-r--r--app/graphql/resolvers/environments/nested_environments_resolver.rb18
-rw-r--r--app/graphql/resolvers/environments_resolver.rb4
-rw-r--r--app/graphql/resolvers/group_packages_resolver.rb6
-rw-r--r--app/graphql/resolvers/issues_resolver.rb5
-rw-r--r--app/graphql/resolvers/package_details_resolver.rb8
-rw-r--r--app/graphql/resolvers/package_pipelines_resolver.rb2
-rw-r--r--app/graphql/resolvers/paginated_tree_resolver.rb5
-rw-r--r--app/graphql/resolvers/project_jobs_resolver.rb14
-rw-r--r--app/graphql/resolvers/projects/fork_details_resolver.rb21
-rw-r--r--app/graphql/resolvers/work_items/work_item_discussions_resolver.rb68
-rw-r--r--app/graphql/resolvers/work_items_resolver.rb1
-rw-r--r--app/graphql/types/alert_management/alert_type.rb10
-rw-r--r--app/graphql/types/base_field.rb7
-rw-r--r--app/graphql/types/ci/config_variable_type.rb1
-rw-r--r--app/graphql/types/ci/freeze_period_status_enum.rb13
-rw-r--r--app/graphql/types/ci/freeze_period_type.rb41
-rw-r--r--app/graphql/types/ci/pipeline_schedule_type.rb46
-rw-r--r--app/graphql/types/ci/pipeline_schedule_variable_type.rb13
-rw-r--r--app/graphql/types/ci/pipeline_type.rb2
-rw-r--r--app/graphql/types/ci/runner_job_execution_status_enum.rb19
-rw-r--r--app/graphql/types/ci/runner_type.rb93
-rw-r--r--app/graphql/types/commit_signature_interface.rb5
-rw-r--r--app/graphql/types/commit_signatures/gpg_signature_type.rb1
-rw-r--r--app/graphql/types/commit_signatures/ssh_signature_type.rb23
-rw-r--r--app/graphql/types/commit_signatures/x509_signature_type.rb1
-rw-r--r--app/graphql/types/container_repository_type.rb2
-rw-r--r--app/graphql/types/dependency_proxy/manifest_type.rb2
-rw-r--r--app/graphql/types/deployment_details_type.rb17
-rw-r--r--app/graphql/types/deployment_type.rb18
-rw-r--r--app/graphql/types/environment_type.rb11
-rw-r--r--app/graphql/types/global_id_type.rb4
-rw-r--r--app/graphql/types/group_connection.rb22
-rw-r--r--app/graphql/types/issue_type_enum.rb4
-rw-r--r--app/graphql/types/key_type.rb17
-rw-r--r--app/graphql/types/merge_request_type.rb4
-rw-r--r--app/graphql/types/mutation_type.rb4
-rw-r--r--app/graphql/types/nested_environment_type.rb28
-rw-r--r--app/graphql/types/notes/note_type.rb8
-rw-r--r--app/graphql/types/packages/package_links_type.rb2
-rw-r--r--app/graphql/types/permission_types/base_permission_type.rb8
-rw-r--r--app/graphql/types/permission_types/deployment.rb14
-rw-r--r--app/graphql/types/permission_types/environment.rb11
-rw-r--r--app/graphql/types/permission_types/project.rb3
-rw-r--r--app/graphql/types/project_statistics_type.rb8
-rw-r--r--app/graphql/types/project_type.rb31
-rw-r--r--app/graphql/types/projects/fork_details_type.rb20
-rw-r--r--app/graphql/types/query_type.rb2
-rw-r--r--app/graphql/types/release_type.rb10
-rw-r--r--app/graphql/types/root_storage_statistics_type.rb2
-rw-r--r--app/graphql/types/subscription_type.rb5
-rw-r--r--app/graphql/types/todo_action_enum.rb3
-rw-r--r--app/graphql/types/todo_type.rb6
-rw-r--r--app/graphql/types/user_interface.rb5
-rw-r--r--app/graphql/types/work_items/notes_filter_type_enum.rb20
-rw-r--r--app/graphql/types/work_items/widget_interface.rb5
-rw-r--r--app/graphql/types/work_items/widgets/hierarchy_type.rb23
-rw-r--r--app/graphql/types/work_items/widgets/notes_type.rb26
-rw-r--r--app/helpers/application_helper.rb36
-rw-r--r--app/helpers/application_settings_helper.rb7
-rw-r--r--app/helpers/auth_helper.rb8
-rw-r--r--app/helpers/avatars_helper.rb2
-rw-r--r--app/helpers/blob_helper.rb4
-rw-r--r--app/helpers/ci/jobs_helper.rb2
-rw-r--r--app/helpers/ci/runners_helper.rb4
-rw-r--r--app/helpers/ci/secure_files_helper.rb2
-rw-r--r--app/helpers/commits_helper.rb2
-rw-r--r--app/helpers/diff_helper.rb12
-rw-r--r--app/helpers/dropdowns_helper.rb8
-rw-r--r--app/helpers/emails_helper.rb4
-rw-r--r--app/helpers/environment_helper.rb1
-rw-r--r--app/helpers/environments_helper.rb6
-rw-r--r--app/helpers/events_helper.rb2
-rw-r--r--app/helpers/groups/observability_helper.rb8
-rw-r--r--app/helpers/groups/settings_helper.rb2
-rw-r--r--app/helpers/hooks_helper.rb2
-rw-r--r--app/helpers/icons_helper.rb2
-rw-r--r--app/helpers/ide_helper.rb12
-rw-r--r--app/helpers/integrations_helper.rb23
-rw-r--r--app/helpers/invite_members_helper.rb7
-rw-r--r--app/helpers/issuables_helper.rb34
-rw-r--r--app/helpers/issues_helper.rb12
-rw-r--r--app/helpers/labels_helper.rb6
-rw-r--r--app/helpers/listbox_helper.rb4
-rw-r--r--app/helpers/markup_helper.rb21
-rw-r--r--app/helpers/members_helper.rb22
-rw-r--r--app/helpers/nav/top_nav_helper.rb13
-rw-r--r--app/helpers/nav_helper.rb6
-rw-r--r--app/helpers/numbers_helper.rb2
-rw-r--r--app/helpers/page_layout_helper.rb4
-rw-r--r--app/helpers/preferences_helper.rb14
-rw-r--r--app/helpers/preferred_language_switcher_helper.rb21
-rw-r--r--app/helpers/profiles_helper.rb8
-rw-r--r--app/helpers/programming_languages_helper.rb20
-rw-r--r--app/helpers/projects/ml/experiments_helper.rb40
-rw-r--r--app/helpers/projects/pipeline_helper.rb1
-rw-r--r--app/helpers/projects_helper.rb48
-rw-r--r--app/helpers/routing/pseudonymization_helper.rb1
-rw-r--r--app/helpers/search_helper.rb65
-rw-r--r--app/helpers/sidebars_helper.rb8
-rw-r--r--app/helpers/sorting_helper.rb56
-rw-r--r--app/helpers/ssh_keys_helper.rb4
-rw-r--r--app/helpers/submodule_helper.rb4
-rw-r--r--app/helpers/timeboxes_helper.rb2
-rw-r--r--app/helpers/todos_helper.rb82
-rw-r--r--app/helpers/tooling/visual_review_helper.rb8
-rw-r--r--app/helpers/version_check_helper.rb15
-rw-r--r--app/helpers/web_hooks/web_hooks_helper.rb2
-rw-r--r--app/helpers/wiki_helper.rb2
-rw-r--r--app/helpers/x509_helper.rb4
-rw-r--r--app/mailers/emails/profile.rb3
-rw-r--r--app/models/abuse_report.rb2
-rw-r--r--app/models/achievements/achievement.rb18
-rw-r--r--app/models/alert_management/alert.rb20
-rw-r--r--app/models/alert_management/http_integration.rb2
-rw-r--r--app/models/analytics/cycle_analytics/aggregation.rb11
-rw-r--r--app/models/analytics/usage_trends/measurement.rb6
-rw-r--r--app/models/appearance.rb2
-rw-r--r--app/models/application_setting.rb25
-rw-r--r--app/models/application_setting_implementation.rb6
-rw-r--r--app/models/audit_event.rb10
-rw-r--r--app/models/award_emoji.rb6
-rw-r--r--app/models/badge.rb2
-rw-r--r--app/models/blob_viewer/metrics_dashboard_yml.rb15
-rw-r--r--app/models/board_group_recent_visit.rb2
-rw-r--r--app/models/board_project_recent_visit.rb2
-rw-r--r--app/models/bulk_import.rb2
-rw-r--r--app/models/bulk_imports/entity.rb2
-rw-r--r--app/models/bulk_imports/export_upload.rb1
-rw-r--r--app/models/bulk_imports/tracker.rb2
-rw-r--r--app/models/ci/bridge.rb19
-rw-r--r--app/models/ci/build.rb54
-rw-r--r--app/models/ci/build_metadata.rb15
-rw-r--r--app/models/ci/build_need.rb5
-rw-r--r--app/models/ci/build_pending_state.rb4
-rw-r--r--app/models/ci/build_report_result.rb4
-rw-r--r--app/models/ci/build_runner_session.rb4
-rw-r--r--app/models/ci/build_trace_chunk.rb7
-rw-r--r--app/models/ci/build_trace_metadata.rb12
-rw-r--r--app/models/ci/freeze_period.rb59
-rw-r--r--app/models/ci/freeze_period_status.rb31
-rw-r--r--app/models/ci/job_artifact.rb9
-rw-r--r--app/models/ci/job_token/allowlist.rb42
-rw-r--r--app/models/ci/job_token/project_scope_link.rb4
-rw-r--r--app/models/ci/job_token/scope.rb59
-rw-r--r--app/models/ci/job_variable.rb3
-rw-r--r--app/models/ci/pending_build.rb3
-rw-r--r--app/models/ci/pipeline.rb13
-rw-r--r--app/models/ci/pipeline_schedule.rb4
-rw-r--r--app/models/ci/pipeline_schedule_variable.rb2
-rw-r--r--app/models/ci/processable.rb4
-rw-r--r--app/models/ci/resource_group.rb11
-rw-r--r--app/models/ci/runner.rb3
-rw-r--r--app/models/ci/runner_namespace.rb2
-rw-r--r--app/models/ci/running_build.rb11
-rw-r--r--app/models/ci/secure_file.rb11
-rw-r--r--app/models/ci/sources/pipeline.rb15
-rw-r--r--app/models/ci/unit_test_failure.rb4
-rw-r--r--app/models/clusters/agent_token.rb4
-rw-r--r--app/models/commit.rb10
-rw-r--r--app/models/commit_range.rb2
-rw-r--r--app/models/commit_signatures/gpg_signature.rb9
-rw-r--r--app/models/commit_signatures/ssh_signature.rb9
-rw-r--r--app/models/commit_signatures/x509_commit_signature.rb9
-rw-r--r--app/models/concerns/avatarable.rb1
-rw-r--r--app/models/concerns/cache_markdown_field.rb2
-rw-r--r--app/models/concerns/cached_commit.rb4
-rw-r--r--app/models/concerns/ci/partitionable.rb44
-rw-r--r--app/models/concerns/ci/partitionable/partitioned_filter.rb41
-rw-r--r--app/models/concerns/commit_signature.rb4
-rw-r--r--app/models/concerns/counter_attribute.rb201
-rw-r--r--app/models/concerns/has_user_type.rb6
-rw-r--r--app/models/concerns/issuable.rb2
-rw-r--r--app/models/concerns/milestoneable.rb23
-rw-r--r--app/models/concerns/sensitive_serializable_hash.rb2
-rw-r--r--app/models/concerns/signature_type.rb13
-rw-r--r--app/models/concerns/sortable.rb2
-rw-r--r--app/models/concerns/taskable.rb15
-rw-r--r--app/models/concerns/time_trackable.rb10
-rw-r--r--app/models/container_repository.rb19
-rw-r--r--app/models/customer_relations/organization.rb4
-rw-r--r--app/models/dependency_proxy/group_setting.rb2
-rw-r--r--app/models/deploy_token.rb1
-rw-r--r--app/models/deployment.rb9
-rw-r--r--app/models/environment.rb51
-rw-r--r--app/models/event.rb14
-rw-r--r--app/models/generic_commit_status.rb8
-rw-r--r--app/models/gpg_key.rb2
-rw-r--r--app/models/group.rb28
-rw-r--r--app/models/group_deploy_key.rb5
-rw-r--r--app/models/hooks/active_hook_filter.rb4
-rw-r--r--app/models/hooks/service_hook.rb5
-rw-r--r--app/models/hooks/web_hook.rb27
-rw-r--r--app/models/import_export_upload.rb1
-rw-r--r--app/models/integration.rb4
-rw-r--r--app/models/integrations/asana.rb6
-rw-r--r--app/models/integrations/bamboo.rb2
-rw-r--r--app/models/integrations/base_chat_notification.rb33
-rw-r--r--app/models/integrations/base_slack_notification.rb9
-rw-r--r--app/models/integrations/base_slash_commands.rb2
-rw-r--r--app/models/integrations/confluence.rb2
-rw-r--r--app/models/integrations/datadog.rb10
-rw-r--r--app/models/integrations/flowdock.rb43
-rw-r--r--app/models/integrations/jira.rb9
-rw-r--r--app/models/integrations/mattermost.rb2
-rw-r--r--app/models/integrations/packagist.rb8
-rw-r--r--app/models/integrations/pushover.rb2
-rw-r--r--app/models/integrations/slack.rb7
-rw-r--r--app/models/issue.rb20
-rw-r--r--app/models/issue_collection.rb44
-rw-r--r--app/models/issue_email_participant.rb2
-rw-r--r--app/models/iteration.rb3
-rw-r--r--app/models/jira_connect_installation.rb12
-rw-r--r--app/models/key.rb12
-rw-r--r--app/models/lfs_object.rb1
-rw-r--r--app/models/member.rb17
-rw-r--r--app/models/members/group_member.rb6
-rw-r--r--app/models/members/member_role.rb14
-rw-r--r--app/models/members/project_member.rb4
-rw-r--r--app/models/merge_request.rb20
-rw-r--r--app/models/merge_request/predictions.rb7
-rw-r--r--app/models/merge_request_context_commit.rb2
-rw-r--r--app/models/merge_request_diff.rb26
-rw-r--r--app/models/merge_request_diff_commit.rb2
-rw-r--r--app/models/ml/candidate.rb17
-rw-r--r--app/models/ml/candidate_metadata.rb14
-rw-r--r--app/models/ml/experiment.rb1
-rw-r--r--app/models/ml/experiment_metadata.rb14
-rw-r--r--app/models/namespace.rb38
-rw-r--r--app/models/namespace_setting.rb10
-rw-r--r--app/models/namespace_statistics.rb2
-rw-r--r--app/models/note.rb2
-rw-r--r--app/models/operations/feature_flags_client.rb6
-rw-r--r--app/models/packages/package.rb1
-rw-r--r--app/models/packages/rpm/repository_file.rb10
-rw-r--r--app/models/pages/lookup_path.rb4
-rw-r--r--app/models/pages/virtual_domain.rb1
-rw-r--r--app/models/pages_domain.rb3
-rw-r--r--app/models/performance_monitoring/prometheus_dashboard.rb7
-rw-r--r--app/models/personal_access_token.rb3
-rw-r--r--app/models/postgresql/detached_partition.rb4
-rw-r--r--app/models/programming_language.rb18
-rw-r--r--app/models/project.rb156
-rw-r--r--app/models/project_export_job.rb29
-rw-r--r--app/models/project_statistics.rb52
-rw-r--r--app/models/projects/forks/divergence_counts.rb72
-rw-r--r--app/models/projects/import_export/relation_export_upload.rb1
-rw-r--r--app/models/prometheus_alert.rb2
-rw-r--r--app/models/protected_branch.rb6
-rw-r--r--app/models/remote_mirror.rb7
-rw-r--r--app/models/resource_label_event.rb2
-rw-r--r--app/models/service_desk_setting.rb2
-rw-r--r--app/models/snippet_statistics.rb2
-rw-r--r--app/models/synthetic_note.rb1
-rw-r--r--app/models/todo.rb20
-rw-r--r--app/models/upload.rb9
-rw-r--r--app/models/user.rb67
-rw-r--r--app/models/user_detail.rb2
-rw-r--r--app/models/user_preference.rb69
-rw-r--r--app/models/users/callout.rb4
-rw-r--r--app/models/users/group_callout.rb3
-rw-r--r--app/models/users/phone_number_validation.rb6
-rw-r--r--app/models/work_item.rb19
-rw-r--r--app/models/work_items/hierarchy_restriction.rb14
-rw-r--r--app/models/work_items/parent_link.rb62
-rw-r--r--app/models/work_items/type.rb88
-rw-r--r--app/models/work_items/widgets/notes.rb14
-rw-r--r--app/policies/base_policy.rb11
-rw-r--r--app/policies/ci/freeze_period_policy.rb2
-rw-r--r--app/policies/ci/pipeline_schedule_variable_policy.rb7
-rw-r--r--app/policies/commit_signatures/ssh_signature_policy.rb7
-rw-r--r--app/policies/concerns/archived_abilities.rb53
-rw-r--r--app/policies/concerns/readonly_abilities.rb53
-rw-r--r--app/policies/group_member_policy.rb2
-rw-r--r--app/policies/group_policy.rb5
-rw-r--r--app/policies/issue_policy.rb19
-rw-r--r--app/policies/merge_request_policy.rb14
-rw-r--r--app/policies/namespaces/user_namespace_policy.rb3
-rw-r--r--app/policies/note_policy.rb8
-rw-r--r--app/policies/project_member_policy.rb2
-rw-r--r--app/policies/project_policy.rb35
-rw-r--r--app/presenters/blob_presenter.rb2
-rw-r--r--app/presenters/ci/freeze_period_presenter.rb13
-rw-r--r--app/presenters/group_member_presenter.rb4
-rw-r--r--app/presenters/member_presenter.rb4
-rw-r--r--app/presenters/packages/pypi/simple_package_versions_presenter.rb5
-rw-r--r--app/presenters/project_member_presenter.rb6
-rw-r--r--app/presenters/project_presenter.rb16
-rw-r--r--app/presenters/search_service_presenter.rb8
-rw-r--r--app/serializers/analytics/cycle_analytics/configuration_entity.rb6
-rw-r--r--app/serializers/build_details_entity.rb2
-rw-r--r--app/serializers/ci/basic_variable_entity.rb1
-rw-r--r--app/serializers/issuable_sidebar_basic_entity.rb4
-rw-r--r--app/serializers/issue_entity.rb13
-rw-r--r--app/serializers/member_entity.rb2
-rw-r--r--app/serializers/merge_request_metrics_entity.rb8
-rw-r--r--app/serializers/merge_request_poll_widget_entity.rb2
-rw-r--r--app/serializers/project_entity.rb4
-rw-r--r--app/services/admin/set_feature_flag_service.rb145
-rw-r--r--app/services/bulk_imports/create_service.rb33
-rw-r--r--app/services/bulk_imports/file_download_service.rb18
-rw-r--r--app/services/chat_names/find_user_service.rb13
-rw-r--r--app/services/ci/after_requeue_job_service.rb68
-rw-r--r--app/services/ci/append_build_trace_service.rb13
-rw-r--r--app/services/ci/create_downstream_pipeline_service.rb28
-rw-r--r--app/services/ci/create_pipeline_service.rb19
-rw-r--r--app/services/ci/enqueue_job_service.rb25
-rw-r--r--app/services/ci/generate_kubeconfig_service.rb11
-rw-r--r--app/services/ci/job_artifacts/create_service.rb4
-rw-r--r--app/services/ci/pipeline_schedule_service.rb2
-rw-r--r--app/services/ci/pipeline_schedules/calculate_next_run_service.rb6
-rw-r--r--app/services/ci/play_bridge_service.rb7
-rw-r--r--app/services/ci/play_build_service.rb12
-rw-r--r--app/services/ci/process_build_service.rb2
-rw-r--r--app/services/ci/register_job_service.rb16
-rw-r--r--app/services/ci/reset_skipped_jobs_service.rb70
-rw-r--r--app/services/ci/retry_job_service.rb13
-rw-r--r--app/services/ci/test_failure_history_service.rb3
-rw-r--r--app/services/ci/track_failed_build_service.rb5
-rw-r--r--app/services/ci/unlock_artifacts_service.rb50
-rw-r--r--app/services/clusters/agents/filter_authorizations_service.rb50
-rw-r--r--app/services/clusters/agents/refresh_authorization_service.rb6
-rw-r--r--app/services/clusters/applications/base_service.rb96
-rw-r--r--app/services/clusters/applications/check_progress_service.rb50
-rw-r--r--app/services/clusters/applications/install_service.rb32
-rw-r--r--app/services/clusters/applications/prometheus_config_service.rb155
-rw-r--r--app/services/clusters/applications/upgrade_service.rb34
-rw-r--r--app/services/clusters/kubernetes/create_or_update_service_account_service.rb2
-rw-r--r--app/services/concerns/incident_management/usage_data.rb18
-rw-r--r--app/services/concerns/rate_limited_service.rb4
-rw-r--r--app/services/deployments/create_for_build_service.rb2
-rw-r--r--app/services/design_management/generate_image_versions_service.rb20
-rw-r--r--app/services/environments/create_for_build_service.rb8
-rw-r--r--app/services/environments/schedule_to_delete_review_apps_service.rb2
-rw-r--r--app/services/error_tracking/list_projects_service.rb21
-rw-r--r--app/services/event_create_service.rb86
-rw-r--r--app/services/git/branch_hooks_service.rb21
-rw-r--r--app/services/groups/group_links/create_service.rb2
-rw-r--r--app/services/groups/group_links/destroy_service.rb2
-rw-r--r--app/services/groups/group_links/update_service.rb2
-rw-r--r--app/services/groups/import_export/import_service.rb18
-rw-r--r--app/services/import/base_service.rb25
-rw-r--r--app/services/import/bitbucket_server_service.rb2
-rw-r--r--app/services/import/github/gists_import_service.rb34
-rw-r--r--app/services/import/github_service.rb1
-rw-r--r--app/services/import/gitlab_projects/file_acquisition_strategies/file_upload.rb2
-rw-r--r--app/services/import/gitlab_projects/file_acquisition_strategies/remote_file.rb2
-rw-r--r--app/services/import/gitlab_projects/file_acquisition_strategies/remote_file_s3.rb2
-rw-r--r--app/services/incident_management/incidents/create_service.rb2
-rw-r--r--app/services/incident_management/link_alerts/base_service.rb27
-rw-r--r--app/services/incident_management/link_alerts/create_service.rb42
-rw-r--r--app/services/incident_management/link_alerts/destroy_service.rb30
-rw-r--r--app/services/incident_management/pager_duty/process_webhook_service.rb22
-rw-r--r--app/services/incident_management/timeline_events/base_service.rb29
-rw-r--r--app/services/incident_management/timeline_events/create_service.rb13
-rw-r--r--app/services/incident_management/timeline_events/destroy_service.rb2
-rw-r--r--app/services/incident_management/timeline_events/update_service.rb43
-rw-r--r--app/services/issuable/discussions_list_service.rb16
-rw-r--r--app/services/issue_links/create_service.rb4
-rw-r--r--app/services/issues/base_service.rb5
-rw-r--r--app/services/issues/close_service.rb13
-rw-r--r--app/services/issues/create_service.rb2
-rw-r--r--app/services/issues/move_service.rb13
-rw-r--r--app/services/issues/update_service.rb2
-rw-r--r--app/services/jira_connect/create_asymmetric_jwt_service.rb11
-rw-r--r--app/services/jira_connect_installations/proxy_lifecycle_event_service.rb91
-rw-r--r--app/services/jira_connect_installations/update_service.rb61
-rw-r--r--app/services/jira_import/start_import_service.rb2
-rw-r--r--app/services/markup/rendering_service.rb36
-rw-r--r--app/services/members/destroy_service.rb4
-rw-r--r--app/services/merge_requests/after_create_service.rb2
-rw-r--r--app/services/merge_requests/approval_service.rb1
-rw-r--r--app/services/merge_requests/assign_issues_service.rb16
-rw-r--r--app/services/merge_requests/base_service.rb15
-rw-r--r--app/services/merge_requests/build_service.rb10
-rw-r--r--app/services/merge_requests/create_service.rb2
-rw-r--r--app/services/merge_requests/push_options_handler_service.rb2
-rw-r--r--app/services/merge_requests/remove_approval_service.rb1
-rw-r--r--app/services/metrics/dashboard/grafana_metric_embed_service.rb3
-rw-r--r--app/services/ml/experiment_tracking/candidate_repository.rb59
-rw-r--r--app/services/ml/experiment_tracking/experiment_repository.rb41
-rw-r--r--app/services/notification_service.rb18
-rw-r--r--app/services/packages/debian/process_package_file_service.rb101
-rw-r--r--app/services/packages/rpm/parse_package_service.rb4
-rw-r--r--app/services/pages_domains/obtain_lets_encrypt_certificate_service.rb2
-rw-r--r--app/services/pages_domains/retry_acme_order_service.rb21
-rw-r--r--app/services/personal_access_tokens/revoke_service.rb24
-rw-r--r--app/services/projects/batch_forks_count_service.rb4
-rw-r--r--app/services/projects/batch_open_issues_count_service.rb4
-rw-r--r--app/services/projects/container_repository/cleanup_tags_base_service.rb6
-rw-r--r--app/services/projects/container_repository/destroy_service.rb40
-rw-r--r--app/services/projects/container_repository/gitlab/cleanup_tags_service.rb6
-rw-r--r--app/services/projects/create_service.rb3
-rw-r--r--app/services/projects/import_export/export_service.rb2
-rw-r--r--app/services/projects/import_export/parallel_export_service.rb98
-rw-r--r--app/services/projects/import_service.rb3
-rw-r--r--app/services/projects/lfs_pointers/lfs_download_link_list_service.rb25
-rw-r--r--app/services/projects/lfs_pointers/lfs_download_service.rb6
-rw-r--r--app/services/projects/lfs_pointers/lfs_import_service.rb4
-rw-r--r--app/services/projects/lfs_pointers/lfs_object_download_list_service.rb43
-rw-r--r--app/services/projects/refresh_build_artifacts_size_statistics_service.rb2
-rw-r--r--app/services/projects/update_pages_service.rb7
-rw-r--r--app/services/projects/update_remote_mirror_service.rb2
-rw-r--r--app/services/projects/update_service.rb38
-rw-r--r--app/services/protected_branches/api_service.rb6
-rw-r--r--app/services/protected_branches/base_service.rb8
-rw-r--r--app/services/protected_branches/cache_service.rb11
-rw-r--r--app/services/protected_branches/create_service.rb4
-rw-r--r--app/services/protected_branches/destroy_service.rb2
-rw-r--r--app/services/protected_branches/legacy_api_create_service.rb2
-rw-r--r--app/services/protected_branches/legacy_api_update_service.rb2
-rw-r--r--app/services/protected_branches/update_service.rb2
-rw-r--r--app/services/quick_actions/interpret_service.rb6
-rw-r--r--app/services/repositories/housekeeping_service.rb6
-rw-r--r--app/services/search_service.rb6
-rw-r--r--app/services/snippets/create_service.rb2
-rw-r--r--app/services/system_notes/commit_service.rb8
-rw-r--r--app/services/task_list_toggle_service.rb4
-rw-r--r--app/services/timelogs/base_service.rb4
-rw-r--r--app/services/timelogs/create_service.rb3
-rw-r--r--app/services/todo_service.rb82
-rw-r--r--app/services/users/approve_service.rb2
-rw-r--r--app/services/users/assigned_issues_count_service.rb63
-rw-r--r--app/services/users/banned_user_base_service.rb2
-rw-r--r--app/services/users/build_service.rb2
-rw-r--r--app/services/users/keys_count_service.rb2
-rw-r--r--app/services/users/migrate_records_to_ghost_user_service.rb5
-rw-r--r--app/services/users/reject_service.rb2
-rw-r--r--app/services/users/update_highest_member_role_service.rb4
-rw-r--r--app/services/web_hooks/log_execution_service.rb16
-rw-r--r--app/services/wiki_pages/update_service.rb2
-rw-r--r--app/services/work_items/create_and_link_service.rb2
-rw-r--r--app/services/work_items/create_from_task_service.rb2
-rw-r--r--app/services/work_items/create_service.rb2
-rw-r--r--app/services/work_items/delete_task_service.rb2
-rw-r--r--app/uploaders/ci/secure_file_uploader.rb4
-rw-r--r--app/uploaders/file_mover.rb1
-rw-r--r--app/uploaders/file_uploader.rb4
-rw-r--r--app/uploaders/gitlab_uploader.rb13
-rw-r--r--app/uploaders/object_storage.rb63
-rw-r--r--app/uploaders/packages/composer/cache_uploader.rb2
-rw-r--r--app/uploaders/packages/debian/component_file_uploader.rb2
-rw-r--r--app/uploaders/packages/debian/distribution_release_file_uploader.rb2
-rw-r--r--app/uploaders/packages/package_file_uploader.rb2
-rw-r--r--app/uploaders/packages/rpm/repository_file_uploader.rb2
-rw-r--r--app/uploaders/pages/deployment_uploader.rb7
-rw-r--r--app/uploaders/terraform/state_uploader.rb4
-rw-r--r--app/validators/iso8601_date_validator.rb9
-rw-r--r--app/validators/json_schemas/build_metadata_id_tokens.json29
-rw-r--r--app/validators/json_schemas/build_report_result_data.json11
-rw-r--r--app/validators/json_schemas/build_report_result_data_tests.json26
-rw-r--r--app/validators/json_schemas/ci_secure_file_metadata.json4
-rw-r--r--app/validators/json_schemas/daily_build_group_report_result_data.json7
-rw-r--r--app/validators/json_schemas/merge_request_predictions_suggested_reviewers.json10
-rw-r--r--app/validators/json_schemas/web_hooks_url_variables.json2
-rw-r--r--app/views/abuse_reports/new.html.haml6
-rw-r--r--app/views/admin/abuse_reports/_abuse_report.html.haml12
-rw-r--r--app/views/admin/application_settings/_account_and_limit.html.haml2
-rw-r--r--app/views/admin/application_settings/_ci_cd.html.haml8
-rw-r--r--app/views/admin/application_settings/_default_branch.html.haml2
-rw-r--r--app/views/admin/application_settings/_error_tracking.html.haml2
-rw-r--r--app/views/admin/application_settings/_git_lfs_limits.html.haml2
-rw-r--r--app/views/admin/application_settings/_grafana.html.haml2
-rw-r--r--app/views/admin/application_settings/_kroki.html.haml2
-rw-r--r--app/views/admin/application_settings/_localization.html.haml7
-rw-r--r--app/views/admin/application_settings/_mailgun.html.haml2
-rw-r--r--app/views/admin/application_settings/_outbound.html.haml2
-rw-r--r--app/views/admin/application_settings/_performance_bar.html.haml2
-rw-r--r--app/views/admin/application_settings/_plantuml.html.haml2
-rw-r--r--app/views/admin/application_settings/_repository_check.html.haml41
-rw-r--r--app/views/admin/application_settings/_repository_static_objects.html.haml4
-rw-r--r--app/views/admin/application_settings/_repository_storage.html.haml2
-rw-r--r--app/views/admin/application_settings/_runner_registrars_form.html.haml4
-rw-r--r--app/views/admin/application_settings/_search_limits.html.haml4
-rw-r--r--app/views/admin/application_settings/_spam.html.haml5
-rw-r--r--app/views/admin/application_settings/_terminal.html.haml4
-rw-r--r--app/views/admin/application_settings/_terraform_limits.html.haml11
-rw-r--r--app/views/admin/application_settings/_visibility_and_access.html.haml8
-rw-r--r--app/views/admin/application_settings/appearances/_system_header_footer_form.html.haml2
-rw-r--r--app/views/admin/application_settings/appearances/show.html.haml1
-rw-r--r--app/views/admin/application_settings/ci/_header.html.haml3
-rw-r--r--app/views/admin/application_settings/ci_cd.html.haml1
-rw-r--r--app/views/admin/application_settings/general.html.haml3
-rw-r--r--app/views/admin/application_settings/integrations.html.haml1
-rw-r--r--app/views/admin/application_settings/metrics_and_profiling.html.haml5
-rw-r--r--app/views/admin/application_settings/network.html.haml1
-rw-r--r--app/views/admin/application_settings/preferences.html.haml17
-rw-r--r--app/views/admin/application_settings/reporting.html.haml1
-rw-r--r--app/views/admin/application_settings/repository.html.haml7
-rw-r--r--app/views/admin/application_settings/service_usage_data.html.haml5
-rw-r--r--app/views/admin/applications/index.html.haml6
-rw-r--r--app/views/admin/broadcast_messages/_form.html.haml8
-rw-r--r--app/views/admin/broadcast_messages/edit.html.haml17
-rw-r--r--app/views/admin/broadcast_messages/index.html.haml1
-rw-r--r--app/views/admin/dashboard/_security_newsletter_callout.html.haml2
-rw-r--r--app/views/admin/dashboard/index.html.haml11
-rw-r--r--app/views/admin/deploy_keys/edit.html.haml3
-rw-r--r--app/views/admin/groups/_form.html.haml6
-rw-r--r--app/views/admin/groups/_group.html.haml6
-rw-r--r--app/views/admin/groups/index.html.haml3
-rw-r--r--app/views/admin/hook_logs/show.html.haml7
-rw-r--r--app/views/admin/identities/_form.html.haml4
-rw-r--r--app/views/admin/identities/_identity.html.haml4
-rw-r--r--app/views/admin/labels/index.html.haml5
-rw-r--r--app/views/admin/projects/_projects.html.haml4
-rw-r--r--app/views/admin/projects/index.html.haml4
-rw-r--r--app/views/admin/projects/show.html.haml5
-rw-r--r--app/views/admin/topics/_form.html.haml11
-rw-r--r--app/views/admin/topics/index.html.haml2
-rw-r--r--app/views/admin/users/_form.html.haml6
-rw-r--r--app/views/admin/users/_head.html.haml3
-rw-r--r--app/views/admin/users/_users.html.haml2
-rw-r--r--app/views/ci/runner/_how_to_setup_runner.html.haml5
-rw-r--r--app/views/ci/variables/_content.html.haml10
-rw-r--r--app/views/ci/variables/_header.html.haml2
-rw-r--r--app/views/ci/variables/_index.html.haml2
-rw-r--r--app/views/ci/variables/_variable_row.html.haml3
-rw-r--r--app/views/clusters/clusters/_details.html.haml2
-rw-r--r--app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml2
-rw-r--r--app/views/dashboard/_activities.html.haml3
-rw-r--r--app/views/dashboard/_groups_head.html.haml4
-rw-r--r--app/views/dashboard/_projects_head.html.haml17
-rw-r--r--app/views/dashboard/_projects_nav.html.haml8
-rw-r--r--app/views/dashboard/_snippets_head.html.haml3
-rw-r--r--app/views/dashboard/issues.html.haml6
-rw-r--r--app/views/dashboard/merge_requests.html.haml1
-rw-r--r--app/views/dashboard/projects/_nav.html.haml23
-rw-r--r--app/views/dashboard/projects/index.html.haml2
-rw-r--r--app/views/dashboard/todos/_todo.html.haml98
-rw-r--r--app/views/dashboard/todos/index.html.haml1
-rw-r--r--app/views/devise/shared/_footer.html.haml3
-rw-r--r--app/views/devise/shared/_language_switcher.html.haml3
-rw-r--r--app/views/devise/shared/_signup_box.html.haml1
-rw-r--r--app/views/devise/unlocks/new.html.haml4
-rw-r--r--app/views/explore/projects/_filter.html.haml4
-rw-r--r--app/views/explore/projects/_nav.html.haml1
-rw-r--r--app/views/explore/projects/index.html.haml2
-rw-r--r--app/views/explore/projects/page_out_of_bounds.html.haml5
-rw-r--r--app/views/explore/projects/starred.html.haml2
-rw-r--r--app/views/explore/projects/topic.html.haml1
-rw-r--r--app/views/explore/projects/trending.html.haml2
-rw-r--r--app/views/groups/_activities.html.haml3
-rw-r--r--app/views/groups/_group_admin_settings.html.haml10
-rw-r--r--app/views/groups/_home_panel.html.haml50
-rw-r--r--app/views/groups/_import_group_from_another_instance_panel.html.haml40
-rw-r--r--app/views/groups/_invite_groups_modal.html.haml2
-rw-r--r--app/views/groups/_invite_members_modal.html.haml1
-rw-r--r--app/views/groups/_new_group_fields.html.haml6
-rw-r--r--app/views/groups/edit.html.haml2
-rw-r--r--app/views/groups/group_members/index.html.haml4
-rw-r--r--app/views/groups/issues.html.haml3
-rw-r--r--app/views/groups/labels/index.html.haml2
-rw-r--r--app/views/groups/merge_requests.html.haml1
-rw-r--r--app/views/groups/milestones/_form.html.haml6
-rw-r--r--app/views/groups/milestones/index.html.haml10
-rw-r--r--app/views/groups/projects.html.haml16
-rw-r--r--app/views/groups/registry/repositories/index.html.haml3
-rw-r--r--app/views/groups/runners/_settings.html.haml7
-rw-r--r--app/views/groups/runners/index.html.haml2
-rw-r--r--app/views/groups/settings/_export.html.haml12
-rw-r--r--app/views/groups/settings/_general.html.haml2
-rw-r--r--app/views/groups/settings/_git_access_protocols.html.haml2
-rw-r--r--app/views/groups/settings/_permissions.html.haml2
-rw-r--r--app/views/groups/settings/_remove_button.html.haml4
-rw-r--r--app/views/groups/settings/_transfer.html.haml4
-rw-r--r--app/views/groups/settings/applications/index.html.haml1
-rw-r--r--app/views/groups/settings/ci_cd/_auto_devops_form.html.haml2
-rw-r--r--app/views/groups/settings/ci_cd/_form.html.haml2
-rw-r--r--app/views/groups/settings/repository/_default_branch.html.haml2
-rw-r--r--app/views/groups/usage_quotas/index.html.haml7
-rw-r--r--app/views/help/index.html.haml2
-rw-r--r--app/views/ide/_show.html.haml15
-rw-r--r--app/views/import/_githubish_status.html.haml3
-rw-r--r--app/views/import/bulk_imports/status.html.haml1
-rw-r--r--app/views/import/github/status.html.haml1
-rw-r--r--app/views/import/gitlab_projects/new.html.haml7
-rw-r--r--app/views/import/manifest/_form.html.haml20
-rw-r--r--app/views/import/shared/_new_project_form.html.haml29
-rw-r--r--app/views/invites/show.html.haml6
-rw-r--r--app/views/jira_connect/users/show.html.haml5
-rw-r--r--app/views/layouts/_google_tag_manager_head.html.haml11
-rw-r--r--app/views/layouts/_head.html.haml18
-rw-r--r--app/views/layouts/_loading_hints.html.haml6
-rw-r--r--app/views/layouts/_page.html.haml1
-rw-r--r--app/views/layouts/_search.html.haml2
-rw-r--r--app/views/layouts/group_settings.html.haml1
-rw-r--r--app/views/layouts/header/_current_user_dropdown.html.haml4
-rw-r--r--app/views/layouts/header/_default.html.haml8
-rw-r--r--app/views/layouts/header/_gitlab_version.html.haml2
-rw-r--r--app/views/layouts/header/_marketing_links.html.haml16
-rw-r--r--app/views/layouts/header/_registration_enabled_callout.html.haml6
-rw-r--r--app/views/layouts/header/_sign_in_register_button.html.haml3
-rw-r--r--app/views/layouts/jira_connect.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_admin.html.haml24
-rw-r--r--app/views/layouts/nav/sidebar/_profile.html.haml26
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml2
-rw-r--r--app/views/layouts/project_settings.html.haml1
-rw-r--r--app/views/layouts/search.html.haml1
-rw-r--r--app/views/notify/_reassigned_issuable_email.html.haml2
-rw-r--r--app/views/notify/access_token_revoked_email.html.haml2
-rw-r--r--app/views/notify/access_token_revoked_email.text.erb4
-rw-r--r--app/views/notify/autodevops_disabled_email.html.haml2
-rw-r--r--app/views/notify/issue_moved_email.html.haml2
-rw-r--r--app/views/notify/repository_push_email.html.haml2
-rw-r--r--app/views/profiles/accounts/show.html.haml6
-rw-r--r--app/views/profiles/keys/_form.html.haml6
-rw-r--r--app/views/profiles/keys/_key.html.haml5
-rw-r--r--app/views/profiles/keys/_key_details.html.haml5
-rw-r--r--app/views/profiles/preferences/show.html.haml23
-rw-r--r--app/views/projects/_files.html.haml11
-rw-r--r--app/views/projects/_flash_messages.html.haml4
-rw-r--r--app/views/projects/_fork_info.html.haml14
-rw-r--r--app/views/projects/_home_panel.html.haml37
-rw-r--r--app/views/projects/_invite_groups_modal.html.haml2
-rw-r--r--app/views/projects/_invite_members_modal.html.haml1
-rw-r--r--app/views/projects/_merge_request_merge_checks_settings.html.haml13
-rw-r--r--app/views/projects/_merge_request_pipelines_and_threads_options.html.haml13
-rw-r--r--app/views/projects/_new_project_fields.html.haml3
-rw-r--r--app/views/projects/blob/_editor.html.haml3
-rw-r--r--app/views/projects/blob/_template_selectors.html.haml10
-rw-r--r--app/views/projects/branches/new.html.haml12
-rw-r--r--app/views/projects/buttons/_clone.html.haml12
-rw-r--r--app/views/projects/buttons/_download.html.haml2
-rw-r--r--app/views/projects/buttons/_fork.html.haml8
-rw-r--r--app/views/projects/buttons/_star.html.haml12
-rw-r--r--app/views/projects/commit/_commit_box.html.haml4
-rw-r--r--app/views/projects/commit/_signature.html.haml2
-rw-r--r--app/views/projects/commit/_signature_badge.html.haml15
-rw-r--r--app/views/projects/commit/_signature_badge_user.html.haml22
-rw-r--r--app/views/projects/commit/x509/_signature_badge_user.html.haml2
-rw-r--r--app/views/projects/commits/_commits.html.haml4
-rw-r--r--app/views/projects/commits/show.html.haml5
-rw-r--r--app/views/projects/diffs/_diffs.html.haml2
-rw-r--r--app/views/projects/environments/show.html.haml49
-rw-r--r--app/views/projects/graphs/show.html.haml10
-rw-r--r--app/views/projects/issuable/_show.html.haml1
-rw-r--r--app/views/projects/issues/index.html.haml5
-rw-r--r--app/views/projects/issues/service_desk.html.haml2
-rw-r--r--app/views/projects/jobs/_table.html.haml18
-rw-r--r--app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml42
-rw-r--r--app/views/projects/merge_requests/_code_dropdown.html.haml34
-rw-r--r--app/views/projects/merge_requests/_page.html.haml114
-rw-r--r--app/views/projects/merge_requests/creations/_new_compare.html.haml27
-rw-r--r--app/views/projects/merge_requests/diffs.html.haml1
-rw-r--r--app/views/projects/merge_requests/index.html.haml1
-rw-r--r--app/views/projects/merge_requests/show.html.haml114
-rw-r--r--app/views/projects/ml/candidates/show.html.haml7
-rw-r--r--app/views/projects/network/show.html.haml3
-rw-r--r--app/views/projects/notes/_more_actions_dropdown.html.haml2
-rw-r--r--app/views/projects/pages/_list.html.haml2
-rw-r--r--app/views/projects/pages_domains/new.html.haml9
-rw-r--r--app/views/projects/pages_domains/show.html.haml6
-rw-r--r--app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml61
-rw-r--r--app/views/projects/pipeline_schedules/_table.html.haml20
-rw-r--r--app/views/projects/pipeline_schedules/index.html.haml5
-rw-r--r--app/views/projects/pipeline_schedules/new.html.haml2
-rw-r--r--app/views/projects/pipelines/_info.html.haml7
-rw-r--r--app/views/projects/pipelines/_with_tabs.html.haml48
-rw-r--r--app/views/projects/pipelines/show.html.haml12
-rw-r--r--app/views/projects/project_members/index.html.haml6
-rw-r--r--app/views/projects/protected_branches/_branches_list.html.haml4
-rw-r--r--app/views/projects/protected_branches/_create_protected_branch.html.haml14
-rw-r--r--app/views/projects/protected_branches/_index.html.haml7
-rw-r--r--app/views/projects/protected_branches/_protected_branch.html.haml2
-rw-r--r--app/views/projects/protected_branches/_update_protected_branch.html.haml1
-rw-r--r--app/views/projects/protected_branches/shared/_branches_list.html.haml38
-rw-r--r--app/views/projects/protected_branches/shared/_create_protected_branch.html.haml35
-rw-r--r--app/views/projects/protected_branches/shared/_protected_branch.html.haml23
-rw-r--r--app/views/projects/protected_branches/show.html.haml25
-rw-r--r--app/views/projects/protected_tags/shared/_create_protected_tag.html.haml4
-rw-r--r--app/views/projects/registry/repositories/index.html.haml3
-rw-r--r--app/views/projects/runners/_group_runners.html.haml10
-rw-r--r--app/views/projects/runners/_runner.html.haml2
-rw-r--r--app/views/projects/runners/_shared_runners.html.haml9
-rw-r--r--app/views/projects/runners/_specific_runners.html.haml2
-rw-r--r--app/views/projects/settings/_general.html.haml4
-rw-r--r--app/views/projects/settings/branch_rules/index.html.haml2
-rw-r--r--app/views/projects/settings/ci_cd/_autodevops_form.html.haml4
-rw-r--r--app/views/projects/settings/operations/_alert_management.html.haml2
-rw-r--r--app/views/projects/settings/repository/_protected_branches.html.haml2
-rw-r--r--app/views/projects/show.html.haml5
-rw-r--r--app/views/projects/starrers/index.html.haml1
-rw-r--r--app/views/projects/tags/new.html.haml14
-rw-r--r--app/views/projects/tree/_tree_header.html.haml2
-rw-r--r--app/views/projects/triggers/_form.html.haml4
-rw-r--r--app/views/protected_branches/_branches_list.html.haml4
-rw-r--r--app/views/protected_branches/_create_protected_branch.html.haml14
-rw-r--r--app/views/protected_branches/_index.html.haml7
-rw-r--r--app/views/protected_branches/_protected_branch.html.haml2
-rw-r--r--app/views/protected_branches/_update_protected_branch.html.haml1
-rw-r--r--app/views/protected_branches/shared/_branches_list.html.haml38
-rw-r--r--app/views/protected_branches/shared/_create_protected_branch.html.haml35
-rw-r--r--app/views/protected_branches/shared/_dropdown.html.haml (renamed from app/views/projects/protected_branches/shared/_dropdown.html.haml)0
-rw-r--r--app/views/protected_branches/shared/_index.html.haml (renamed from app/views/projects/protected_branches/shared/_index.html.haml)0
-rw-r--r--app/views/protected_branches/shared/_matching_branch.html.haml (renamed from app/views/projects/protected_branches/shared/_matching_branch.html.haml)0
-rw-r--r--app/views/protected_branches/shared/_protected_branch.html.haml23
-rw-r--r--app/views/protected_branches/shared/_update_protected_branch.html.haml40
-rw-r--r--app/views/protected_branches/show.html.haml25
-rw-r--r--app/views/pwa/manifest.json.erb6
-rw-r--r--app/views/registrations/welcome/show.html.haml36
-rw-r--r--app/views/search/_category.html.haml2
-rw-r--r--app/views/search/results/_issuable.html.haml2
-rw-r--r--app/views/search/show.html.haml4
-rw-r--r--app/views/shared/_auto_devops_callout.html.haml25
-rw-r--r--app/views/shared/_file_highlight.html.haml28
-rw-r--r--app/views/shared/_ide_root.html.haml11
-rw-r--r--app/views/shared/_issuable_meta_data.html.haml10
-rw-r--r--app/views/shared/_label.html.haml12
-rw-r--r--app/views/shared/_milestones_filter.html.haml2
-rw-r--r--app/views/shared/_new_project_item_select.html.haml2
-rw-r--r--app/views/shared/_ref_switcher.html.haml2
-rw-r--r--app/views/shared/_web_ide_button.html.haml2
-rw-r--r--app/views/shared/builds/_tabs.html.haml2
-rw-r--r--app/views/shared/empty_states/_milestones.html.haml2
-rw-r--r--app/views/shared/empty_states/_milestones_tab.html.haml2
-rw-r--r--app/views/shared/file_hooks/_index.html.haml21
-rw-r--r--app/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml4
-rw-r--r--app/views/shared/integrations/prometheus/_custom_metrics.html.haml4
-rw-r--r--app/views/shared/integrations/prometheus/_metrics.html.haml4
-rw-r--r--app/views/shared/issuable/_form.html.haml4
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml4
-rw-r--r--app/views/shared/issuable/form/_title.html.haml4
-rw-r--r--app/views/shared/issue_type/_details_content.html.haml2
-rw-r--r--app/views/shared/nav/_sidebar_submenu.html.haml2
-rw-r--r--app/views/shared/projects/_dropdown.html.haml2
-rw-r--r--app/views/shared/projects/_project.html.haml2
-rw-r--r--app/views/shared/projects/_search_bar.html.haml26
-rw-r--r--app/views/shared/projects/_search_form.html.haml21
-rw-r--r--app/views/shared/projects/_sort_dropdown.html.haml39
-rw-r--r--app/views/shared/projects/protected_branches/_update_protected_branch.html.haml40
-rw-r--r--app/views/shared/runners/_form.html.haml2
-rw-r--r--app/views/shared/ssh_keys/_key_delete.html.haml14
-rw-r--r--app/views/shared/topics/_search_form.html.haml2
-rw-r--r--app/views/shared/web_hooks/_form.html.haml44
-rw-r--r--app/views/shared/web_hooks/_hook.html.haml2
-rw-r--r--app/views/shared/web_hooks/_test_button.html.haml6
-rw-r--r--app/views/users/show.html.haml2
-rw-r--r--app/views/web_ide/remote_ide/index.html.haml5
-rw-r--r--app/workers/all_queues.yml112
-rw-r--r--app/workers/bulk_imports/entity_worker.rb2
-rw-r--r--app/workers/bulk_imports/export_request_worker.rb62
-rw-r--r--app/workers/bulk_imports/pipeline_worker.rb127
-rw-r--r--app/workers/ci/create_downstream_pipeline_worker.rb8
-rw-r--r--app/workers/concerns/gitlab/github_import/object_importer.rb9
-rw-r--r--app/workers/concerns/waitable_worker.rb25
-rw-r--r--app/workers/container_registry/cleanup_worker.rb2
-rw-r--r--app/workers/container_registry/delete_container_repository_worker.rb1
-rw-r--r--app/workers/container_registry/migration/enqueuer_worker.rb4
-rw-r--r--app/workers/database/batched_background_migration/ci_database_worker.rb4
-rw-r--r--app/workers/database/batched_background_migration/ci_execution_worker.rb9
-rw-r--r--app/workers/database/batched_background_migration/execution_worker.rb37
-rw-r--r--app/workers/database/batched_background_migration/main_execution_worker.rb9
-rw-r--r--app/workers/database/batched_background_migration/single_database_worker.rb41
-rw-r--r--app/workers/database/batched_background_migration_worker.rb4
-rw-r--r--app/workers/delete_container_repository_worker.rb61
-rw-r--r--app/workers/flush_counter_increments_worker.rb2
-rw-r--r--app/workers/gitlab/export/prune_project_export_jobs_worker.rb23
-rw-r--r--app/workers/gitlab/github_gists_import/finish_import_worker.rb46
-rw-r--r--app/workers/gitlab/github_gists_import/import_gist_worker.rb75
-rw-r--r--app/workers/gitlab/github_gists_import/start_import_worker.rb64
-rw-r--r--app/workers/gitlab_shell_worker.rb2
-rw-r--r--app/workers/issuable_export_csv_worker.rb4
-rw-r--r--app/workers/jira_connect/send_uninstalled_hook_worker.rb22
-rw-r--r--app/workers/mail_scheduler/notification_service_worker.rb4
-rw-r--r--app/workers/merge_requests/delete_branch_worker.rb27
-rw-r--r--app/workers/merge_requests/delete_source_branch_worker.rb9
-rw-r--r--app/workers/namespaces/root_statistics_worker.rb2
-rw-r--r--app/workers/object_storage/background_move_worker.rb35
-rw-r--r--app/workers/packages/debian/process_package_file_worker.rb52
-rw-r--r--app/workers/post_receive.rb8
-rw-r--r--app/workers/projects/delete_branch_worker.rb30
-rw-r--r--app/workers/projects/import_export/parallel_project_export_worker.rb61
-rw-r--r--app/workers/projects/inactive_projects_deletion_cron_worker.rb10
-rw-r--r--app/workers/run_pipeline_schedule_worker.rb2
-rw-r--r--app/workers/update_highest_role_worker.rb2
-rwxr-xr-xbin/audit-event-type24
-rwxr-xr-xbin/spring8
-rw-r--r--config/application.rb26
-rw-r--r--config/audit_events/types/policy_project_updated.yml8
-rw-r--r--config/audit_events/types/type_schema.json6
-rw-r--r--config/dependency_decisions.yml18
-rw-r--r--config/environments/development.rb4
-rw-r--r--config/events/1651053267_event_create_service_action_active_users_project_repo.yml23
-rw-r--r--config/events/1651053267_event_create_service_project_action.yml23
-rw-r--r--config/events/1655726589_ide_edit_g_edit_by_web_ide.yml22
-rw-r--r--config/events/1655726622_ide_edit_g_edit_by_live_preview.yml22
-rw-r--r--config/events/1655726650_ide_edit_g_edit_by_sfe.yml22
-rw-r--r--config/events/1655726683_ide_edit_g_edit_by_snippet_ide.yml22
-rw-r--r--config/events/1656510012_merge_requests_i_code_review_user_approve_mr.yml26
-rw-r--r--config/events/1656690716_post_receive_source_code_pushes.yml26
-rw-r--r--config/events/1669277827_API__Commits_commit.yml26
-rw-r--r--config/events/1669597397_Gitlab__UsageDataCounters__EditorUniqueCounter_ide_edit.yml21
-rw-r--r--config/events/1669605315_PostReceive_push.yml21
-rw-r--r--config/events/1669605645_Gitlab__UsageDataCounters__MergeRequestActivityUniqueCounter_approve.yml22
-rw-r--r--config/events/1669814629_StatusPage__PublishService_incident_management_incident_published.yml24
-rw-r--r--config/events/1669815074_Mutations__AlertManagement__Alerts__Todo__Create_incident_management_alert_todo.yml26
-rw-r--r--config/events/1669817378_Mutations__AlertManagement__Alerts__SetAssignees_incident_management_alert_assigned.yml26
-rw-r--r--config/events/1669817630_Mutations__AlertManagement__CreateAlertIssue_incident_management_incident_created.yml26
-rw-r--r--config/events/1669817815_Mutations__AlertManagement__UpdateAlertStatus_incident_management_alert_status_change.yml26
-rw-r--r--config/events/1669818009_IncidentManagement__TimelineEvents__CreateService_incident_management_timeline_event_.yml27
-rw-r--r--config/events/1669902189_IncidentManagement__TimelineEvents__DestroyService_incident_management_timeline_event.yml26
-rw-r--r--config/events/1669902383_IncidentManagement__TimelineEvents__UpdateService_incident_management_timeline_event_.yml26
-rw-r--r--config/events/1669902538_IssueLinks__CreateService_incident_management_incident_relate.yml26
-rw-r--r--config/events/1669902705_IssueLinks__DestroyService_incident_management_incident_unrelate.yml26
-rw-r--r--config/events/1669902889_Issues__CloseService_incident_management_incident_closed.yml26
-rw-r--r--config/events/1669903092_Issues__ReopenService_incident_management_incident_reopened.yml26
-rw-r--r--config/events/1669903273_Issues__UpdateService_incident_management_incident_change_confidential.yml26
-rw-r--r--config/events/1669903414_Issues__ZoomLinkService_incident_management_incident_zoom_meeting.yml26
-rw-r--r--config/events/1669903530_Notes__CreateService_incident_management_incident_comment.yml26
-rw-r--r--config/events/1669903650_TodoService_incident_management_incident_todo.yml26
-rw-r--r--config/events/1670570965_Issues__UpdateService_incident_management_incident_assigned.yml26
-rw-r--r--config/events/1671198983_Gitlab__UsageDataCounters__MergeRequestActivityUniqueCounter_create.yml27
-rw-r--r--config/events/schema.json70
-rw-r--r--config/feature_categories.yml8
-rw-r--r--config/feature_flags/development/actors_aware_gitaly_calls.yml8
-rw-r--r--config/feature_flags/development/add_refresh_pull_mirror_worker.yml8
-rw-r--r--config/feature_flags/development/allow_audit_event_type_filtering.yml8
-rw-r--r--config/feature_flags/development/allow_dots_on_tf_state_names.yml8
-rw-r--r--config/feature_flags/development/always_async_project_authorizations_refresh.yml8
-rw-r--r--config/feature_flags/development/approval_rules_pagination.yml8
-rw-r--r--config/feature_flags/development/automated_email_provision.yml8
-rw-r--r--config/feature_flags/development/ban_user_feature_flag.yml2
-rw-r--r--config/feature_flags/development/batched_migrations_parallel_execution.yml8
-rw-r--r--config/feature_flags/development/block_weak_passwords.yml8
-rw-r--r--config/feature_flags/development/cache_project_integrations.yml8
-rw-r--r--config/feature_flags/development/check_etags_diffs_batch_before_write_cache.yml8
-rw-r--r--config/feature_flags/development/check_ip_address_for_email_verification.yml8
-rw-r--r--config/feature_flags/development/ci_assign_job_token_on_scheduling.yml8
-rw-r--r--config/feature_flags/development/ci_bridge_remove_sourced_pipelines.yml8
-rw-r--r--config/feature_flags/development/ci_build_partition_id_token_prefix.yml8
-rw-r--r--config/feature_flags/development/ci_enforce_rate_limits_jobs_api.yml8
-rw-r--r--config/feature_flags/development/ci_hooks_pre_get_sources_script.yml8
-rw-r--r--config/feature_flags/development/ci_job_token_scope.yml2
-rw-r--r--config/feature_flags/development/ci_partitioning_use_ci_builds_metadata_routing_table.yml8
-rw-r--r--config/feature_flags/development/ci_raw_variables_in_yaml_config.yml2
-rw-r--r--config/feature_flags/development/ci_refactoring_external_mapper.yml8
-rw-r--r--config/feature_flags/development/ci_register_job_temporary_lock.yml2
-rw-r--r--config/feature_flags/development/ci_retry_job_fix.yml8
-rw-r--r--config/feature_flags/development/ci_reuse_build_in_seed_context.yml8
-rw-r--r--config/feature_flags/development/ci_secure_files.yml8
-rw-r--r--config/feature_flags/development/ci_skip_auto_cancelation_on_child_pipelines.yml8
-rw-r--r--config/feature_flags/development/ci_update_unlocked_job_artifacts.yml8
-rw-r--r--config/feature_flags/development/collect_package_events.yml2
-rw-r--r--config/feature_flags/development/container_registry_delete_repository_with_cron_worker.yml8
-rw-r--r--config/feature_flags/development/container_registry_migration_limit_gitlab_org.yml2
-rw-r--r--config/feature_flags/development/container_registry_migration_phase2_all_plans.yml2
-rw-r--r--config/feature_flags/development/container_registry_migration_phase2_capacity_1.yml2
-rw-r--r--config/feature_flags/development/container_registry_migration_phase2_capacity_10.yml2
-rw-r--r--config/feature_flags/development/container_registry_migration_phase2_capacity_2.yml2
-rw-r--r--config/feature_flags/development/container_registry_migration_phase2_capacity_25.yml2
-rw-r--r--config/feature_flags/development/container_registry_migration_phase2_capacity_40.yml2
-rw-r--r--config/feature_flags/development/container_registry_migration_phase2_capacity_5.yml2
-rw-r--r--config/feature_flags/development/container_registry_migration_phase2_delete_container_repository_worker_support.yml2
-rw-r--r--config/feature_flags/development/container_registry_migration_phase2_enabled.yml2
-rw-r--r--config/feature_flags/development/container_registry_migration_phase2_enqueue_speed_fast.yml2
-rw-r--r--config/feature_flags/development/container_registry_migration_phase2_enqueue_speed_slow.yml2
-rw-r--r--config/feature_flags/development/dast_api_scanner.yml8
-rw-r--r--config/feature_flags/development/debian_group_packages.yml2
-rw-r--r--config/feature_flags/development/debian_packages.yml2
-rw-r--r--config/feature_flags/development/disable_metric_dashboard_refresh_rate.yml8
-rw-r--r--config/feature_flags/development/display_merge_conflicts_in_diff.yml2
-rw-r--r--config/feature_flags/development/enable_environments_search_within_folder.yml8
-rw-r--r--config/feature_flags/development/enable_minor_delay_during_project_authorizations_refresh.yml2
-rw-r--r--config/feature_flags/development/enforce_scan_result_policies_for_preexisting_vulnerabilities.yml8
-rw-r--r--config/feature_flags/development/enhanced_webhook_support_regex.yml8
-rw-r--r--config/feature_flags/development/environment_details_vue.yml8
-rw-r--r--config/feature_flags/development/fork_divergence_counts.yml8
-rw-r--r--config/feature_flags/development/forti_authenticator.yml4
-rw-r--r--config/feature_flags/development/forti_token_cloud.yml2
-rw-r--r--config/feature_flags/development/geo_container_repository_replication.yml8
-rw-r--r--config/feature_flags/development/github_client_fetch_repos_via_graphql.yml8
-rw-r--r--config/feature_flags/development/gitlab_metrics_error_rate_sli.yml8
-rw-r--r--config/feature_flags/development/gitlab_pat_auto_revocation.yml8
-rw-r--r--config/feature_flags/development/global_search_error_rate_sli.yml8
-rw-r--r--config/feature_flags/development/go_proxy.yml2
-rw-r--r--config/feature_flags/development/go_proxy_disable_gomod_validation.yml2
-rw-r--r--config/feature_flags/development/graphql_job_app.yml8
-rw-r--r--config/feature_flags/development/graphql_keyset_pagination_without_next_page_query.yml8
-rw-r--r--config/feature_flags/development/group_protected_branches.yml8
-rw-r--r--config/feature_flags/development/harbor_registry_integration.yml2
-rw-r--r--config/feature_flags/development/hash_based_cache_for_protected_branches.yml2
-rw-r--r--config/feature_flags/development/hide_public_email_on_profile.yml2
-rw-r--r--config/feature_flags/development/indifferent_wal_location_keys.yml8
-rw-r--r--config/feature_flags/development/integrated_error_tracking.yml2
-rw-r--r--config/feature_flags/development/jira_connect_oauth_self_managed_setting.yml8
-rw-r--r--config/feature_flags/development/lazy_load_commits.yml8
-rw-r--r--config/feature_flags/development/limit_assigned_issues_count.yml8
-rw-r--r--config/feature_flags/development/linear_group_descendants_finder_upto.yml2
-rw-r--r--config/feature_flags/development/linear_project_ancestors.yml2
-rw-r--r--config/feature_flags/development/linear_user_manageable_groups.yml2
-rw-r--r--config/feature_flags/development/markup_rendering_timeout.yml8
-rw-r--r--config/feature_flags/development/maven_central_request_forwarding.yml2
-rw-r--r--config/feature_flags/development/metrics_dashboard_exhaustive_validations.yml8
-rw-r--r--config/feature_flags/development/mirror_only_branches_match_regex.yml8
-rw-r--r--config/feature_flags/development/mr_compare_dropdowns.yml8
-rw-r--r--config/feature_flags/development/multiple_environment_approval_rules_fe.yml8
-rw-r--r--config/feature_flags/development/new_fonts.yml8
-rw-r--r--config/feature_flags/development/on_demand_scans_runner_tags.yml8
-rw-r--r--config/feature_flags/development/operational_vulnerabilities_filters.yml8
-rw-r--r--config/feature_flags/development/optimized_housekeeping.yml2
-rw-r--r--config/feature_flags/development/package_registry_access_level.yml4
-rw-r--r--config/feature_flags/development/pipeline_name.yml2
-rw-r--r--config/feature_flags/development/pipeline_name_search.yml8
-rw-r--r--config/feature_flags/development/pipeline_tabs_vue.yml8
-rw-r--r--config/feature_flags/development/project_language_search.yml8
-rw-r--r--config/feature_flags/development/project_list_filter_bar.yml8
-rw-r--r--config/feature_flags/development/projects_preloader_fix.yml2
-rw-r--r--config/feature_flags/development/prometheus_computed_alerts.yml8
-rw-r--r--config/feature_flags/development/rate_limit_gitlab_shell_by_ip.yml2
-rw-r--r--config/feature_flags/development/realtime_mr_status_change.yml8
-rw-r--r--config/feature_flags/development/registry_migration_guard_dynamic_pre_import_timeout.yml2
-rw-r--r--config/feature_flags/development/require_approval_on_scan_removal.yml8
-rw-r--r--config/feature_flags/development/root_statistics_worker_read_replica.yml8
-rw-r--r--config/feature_flags/development/route_hll_to_snowplow_phase3.yml8
-rw-r--r--config/feature_flags/development/route_hll_to_snowplow_phase4.yml8
-rw-r--r--config/feature_flags/development/rpm_packages.yml2
-rw-r--r--config/feature_flags/development/rubygem_packages.yml2
-rw-r--r--config/feature_flags/development/run_pipeline_graphql.yml8
-rw-r--r--config/feature_flags/development/scan_execution_rule_mode.yml8
-rw-r--r--config/feature_flags/development/scan_execution_tags.yml8
-rw-r--r--config/feature_flags/development/schema_linting.yml2
-rw-r--r--config/feature_flags/development/search_page_vertical_nav.yml2
-rw-r--r--config/feature_flags/development/secure_files_metadata_parsers.yml8
-rw-r--r--config/feature_flags/development/specialized_worker_for_group_lock_update_auth_recalculation.yml2
-rw-r--r--config/feature_flags/development/split_operations_visibility_permissions.yml8
-rw-r--r--config/feature_flags/development/ssh_commit_signatures.yml8
-rw-r--r--config/feature_flags/development/subgroups_approval_rules.yml8
-rw-r--r--config/feature_flags/development/super_sidebar_nav.yml8
-rw-r--r--config/feature_flags/development/track_and_raise_delete_source_errors.yml8
-rw-r--r--config/feature_flags/development/two_factor_for_cli.yml4
-rw-r--r--config/feature_flags/development/usage_data_ci_i_testing_coverage_report_uploaded.yml8
-rw-r--r--config/feature_flags/development/usage_data_diff_searches.yml7
-rw-r--r--config/feature_flags/development/use_ref_type_parameter.yml9
-rw-r--r--config/feature_flags/development/use_traversal_ids_for_descendants_scopes.yml2
-rw-r--r--config/feature_flags/development/user_time_settings.yml2
-rw-r--r--config/feature_flags/development/verify_gitlab_shell_worker_method_names.yml8
-rw-r--r--config/feature_flags/development/verify_mail_scheduler_notification_service_worker_method_names.yml8
-rw-r--r--config/feature_flags/development/web_hooks_disable_failed.yml8
-rw-r--r--config/feature_flags/development/web_hooks_no_rate_limit.yml8
-rw-r--r--config/feature_flags/development/webauthn.yml2
-rw-r--r--config/feature_flags/development/webhook_form_mask_url.yml8
-rw-r--r--config/feature_flags/development/webhooks_failed_callout.yml8
-rw-r--r--config/feature_flags/experiment/generic_explore_groups.yml2
-rw-r--r--config/feature_flags/ops/advanced_user_search.yml8
-rw-r--r--config/feature_flags/ops/automatic_lock_writes_on_table.yml8
-rw-r--r--config/feature_flags/ops/block_password_auth_for_saml_users.yml2
-rw-r--r--config/feature_flags/ops/dynamic_image_resizing.yml2
-rw-r--r--config/feature_flags/ops/dynamic_nonce.yml2
-rw-r--r--config/feature_flags/ops/enforce_memory_watchdog.yml2
-rw-r--r--config/feature_flags/ops/gitlab_memory_watchdog.yml2
-rw-r--r--config/feature_flags/ops/gitlab_service_measuring_projects_create_service.yml8
-rw-r--r--config/feature_flags/ops/gitlab_service_measuring_projects_import_export_export_service.yml8
-rw-r--r--config/feature_flags/ops/gitlab_service_measuring_projects_import_service.yml8
-rw-r--r--config/feature_flags/ops/jira_raise_timeouts.yml8
-rw-r--r--config/feature_flags/ops/legacy_open_source_license_available.yml2
-rw-r--r--config/feature_flags/ops/purge_stale_security_findings.yml8
-rw-r--r--config/feature_flags/ops/recaptcha_on_top_level_group_creation.yml2
-rw-r--r--config/feature_flags/ops/report_heap_dumps.yml8
-rw-r--r--config/feature_flags/ops/report_jemalloc_stats.yml4
-rw-r--r--config/feature_flags/ops/search_curation_dry_run.yml8
-rw-r--r--config/feature_flags/ops/search_index_curation_commits.yml8
-rw-r--r--config/feature_flags/ops/search_index_curation_issues.yml8
-rw-r--r--config/feature_flags/ops/search_index_curation_main_index.yml8
-rw-r--r--config/feature_flags/ops/search_index_curation_merge_requests.yml8
-rw-r--r--config/feature_flags/ops/search_index_curation_notes.yml8
-rw-r--r--config/feature_flags/ops/search_index_curation_users.yml8
-rw-r--r--config/gitlab.yml.example14
-rw-r--r--config/gitlab_loose_foreign_keys.yml8
-rw-r--r--config/initializers/1_active_record_data_types.rb63
-rw-r--r--config/initializers/1_settings.rb28
-rw-r--r--config/initializers/active_record_data_types.rb57
-rw-r--r--config/initializers/countries.rb2
-rw-r--r--config/initializers/diagnostic_reports.rb6
-rw-r--r--config/initializers/rest-client-hostname_override.rb3
-rw-r--r--config/initializers/sidekiq.rb3
-rw-r--r--config/initializers/types.rb3
-rw-r--r--config/initializers/zz_metrics.rb1
-rw-r--r--config/initializers_before_autoloader/000_inflections.rb1
-rw-r--r--config/metrics/counts_28d/20210216184454_code_review_total_unique_counts_monthly.yml6
-rwxr-xr-xconfig/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml3
-rw-r--r--config/metrics/counts_28d/20210216184957_ecosystem_total_unique_counts_monthly.yml1
-rw-r--r--config/metrics/counts_28d/20210427102618_code_review_category_monthly_active_users.yml6
-rw-r--r--config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml6
-rw-r--r--config/metrics/counts_28d/20221108092725_p_ci_templates_implicit_jobs_container_scanning_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20221108092725_p_ci_templates_jobs_container_scanning_latest_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20221108092725_p_ci_templates_jobs_container_scanning_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20221108101211_merge_request_authors_monthly.yml4
-rw-r--r--config/metrics/counts_28d/20221121115622_i_code_review_merge_request_widget_security_reports_view_monthly.yml26
-rw-r--r--config/metrics/counts_28d/20221121115623_i_code_review_merge_request_widget_security_reports_expand_monthly.yml26
-rw-r--r--config/metrics/counts_28d/20221121115623_i_code_review_merge_request_widget_security_reports_full_report_clicked_monthly.yml26
-rw-r--r--config/metrics/counts_28d/20221121115624_i_code_review_merge_request_widget_security_reports_expand_success_monthly.yml26
-rw-r--r--config/metrics/counts_28d/20221121115625_i_code_review_merge_request_widget_security_reports_expand_failed_monthly.yml26
-rw-r--r--config/metrics/counts_28d/20221121115625_i_code_review_merge_request_widget_security_reports_expand_warning_monthly.yml26
-rw-r--r--config/metrics/counts_28d/20221213182900_i_code_review_create_mr_monthly.yml26
-rw-r--r--config/metrics/counts_7d/20210216184452_code_review_total_unique_counts_weekly.yml6
-rwxr-xr-xconfig/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml3
-rw-r--r--config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml6
-rw-r--r--config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml6
-rw-r--r--config/metrics/counts_7d/20221108092725_p_ci_templates_implicit_jobs_container_scanning_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20221108092725_p_ci_templates_jobs_container_scanning_latest_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20221108092725_p_ci_templates_jobs_container_scanning_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20221121115618_i_code_review_merge_request_widget_security_reports_view_weekly.yml26
-rw-r--r--config/metrics/counts_7d/20221121115619_i_code_review_merge_request_widget_security_reports_expand_weekly.yml26
-rw-r--r--config/metrics/counts_7d/20221121115619_i_code_review_merge_request_widget_security_reports_full_report_clicked_weekly.yml26
-rw-r--r--config/metrics/counts_7d/20221121115620_i_code_review_merge_request_widget_security_reports_expand_success_weekly.yml26
-rw-r--r--config/metrics/counts_7d/20221121115621_i_code_review_merge_request_widget_security_reports_expand_failed_weekly.yml26
-rw-r--r--config/metrics/counts_7d/20221121115621_i_code_review_merge_request_widget_security_reports_expand_warning_weekly.yml26
-rw-r--r--config/metrics/counts_7d/20221213183300_i_code_review_create_mr_weekly.yml26
-rw-r--r--config/metrics/counts_all/20210204124930_servers.yml2
-rw-r--r--config/metrics/counts_all/20210204124932_clusters.yml2
-rw-r--r--config/metrics/counts_all/20210216175837_projects_flowdock_active.yml4
-rw-r--r--config/metrics/counts_all/20210216175839_groups_flowdock_active.yml4
-rw-r--r--config/metrics/counts_all/20210216175842_instances_flowdock_active.yml4
-rw-r--r--config/metrics/counts_all/20210216175844_projects_inheriting_flowdock_active.yml4
-rw-r--r--config/metrics/counts_all/20210216175846_groups_inheriting_flowdock_active.yml4
-rw-r--r--config/metrics/counts_all/20210216181038_projects_with_expiration_policy_enabled_with_older_than_set_to_7d.yml4
-rw-r--r--config/metrics/counts_all/20210216181040_projects_with_expiration_policy_enabled_with_older_than_set_to_14d.yml4
-rw-r--r--config/metrics/counts_all/20210216181042_projects_with_expiration_policy_enabled_with_older_than_set_to_30d.yml4
-rw-r--r--config/metrics/counts_all/20210216181044_projects_with_expiration_policy_enabled_with_older_than_set_to_90d.yml4
-rw-r--r--config/metrics/counts_all/20210216181048_projects_with_expiration_policy_enabled_with_older_than_unset.yml4
-rw-r--r--config/metrics/counts_all/20210216181051_vendor.yml2
-rw-r--r--config/metrics/counts_all/20210915082040_projects_with_expiration_policy_enabled_with_older_than_set_to_60d.yml4
-rw-r--r--config/metrics/counts_all/20221121113321_i_code_review_merge_request_widget_security_reports_count_view.yml26
-rw-r--r--config/metrics/counts_all/20221121113323_i_code_review_merge_request_widget_security_reports_count_expand.yml26
-rw-r--r--config/metrics/counts_all/20221121113323_i_code_review_merge_request_widget_security_reports_count_full_report_clicked.yml26
-rw-r--r--config/metrics/counts_all/20221121113324_i_code_review_merge_request_widget_security_reports_count_expand_success.yml26
-rw-r--r--config/metrics/counts_all/20221121113325_i_code_review_merge_request_widget_security_reports_count_expand_failed.yml26
-rw-r--r--config/metrics/counts_all/20221121113325_i_code_review_merge_request_widget_security_reports_count_expand_warning.yml26
-rw-r--r--config/metrics/settings/20210216175609_version.yml2
-rw-r--r--config/metrics/settings/20210216180314_gitpod_enabled.yml2
-rw-r--r--config/metrics/settings/20210216180841_background_upload.yml3
-rw-r--r--config/metrics/settings/20210216180851_background_upload.yml3
-rw-r--r--config/metrics/settings/20210216180900_background_upload.yml3
-rw-r--r--config/metrics/settings/20210216180909_background_upload.yml3
-rw-r--r--config/metrics/settings/20210216180918_background_upload.yml3
-rw-r--r--config/object_store_settings.rb6
-rw-r--r--config/open_api.yml32
-rw-r--r--config/routes.rb12
-rw-r--r--config/routes/group.rb2
-rw-r--r--config/routes/merge_requests.rb3
-rw-r--r--config/routes/project.rb1
-rw-r--r--config/routes/user.rb16
-rw-r--r--config/settings.rb8
-rw-r--r--config/sidekiq_queues.yml20
-rw-r--r--config/webpack.config.js8
-rw-r--r--danger/plugins/stable_branch.rb9
-rw-r--r--danger/plugins/user_types.rb9
-rw-r--r--danger/product_intelligence/Dangerfile2
-rw-r--r--danger/qa_selector/Dangerfile42
-rw-r--r--danger/specs/Dangerfile2
-rw-r--r--danger/stable_branch_patch/Dangerfile3
-rw-r--r--danger/user_types/Dangerfile3
-rw-r--r--data/deprecations/14-0-nfs-fot-git-repository-storage.yml2
-rw-r--r--data/deprecations/14-10-dependency-scanning-default-java-version.yml2
-rw-r--r--data/deprecations/14-10-deprecate-toggle-notes-confidentiality.yml2
-rw-r--r--data/deprecations/14-10-old-search-migration-removal.yml2
-rw-r--r--data/deprecations/14-2-deprecation-release-cli.yml2
-rw-r--r--data/deprecations/14-2-deprecation-task-runner.yml2
-rw-r--r--data/deprecations/14-3-database-deprecate-legacy-database-conf.yml2
-rw-r--r--data/deprecations/14-3-deprecation_omniauth-kerberos_gem.yml2
-rw-r--r--data/deprecations/14-3-repository-push-audit-events.yml2
-rw-r--r--data/deprecations/14-3-serverless.yml2
-rw-r--r--data/deprecations/14-5-certificate-based-integration-with-kubernetes-saas.yml2
-rw-r--r--data/deprecations/14-5-certificate-based-integration-with-kubernetes.yml2
-rw-r--r--data/deprecations/14-5-deprecate-convert-instance-runner-to-project.yml2
-rw-r--r--data/deprecations/14-5-deprecate-defaultMergeCommitMessageWithDescription-graphql.yml2
-rw-r--r--data/deprecations/14-5-deprecate-opensuse-15-2.yml2
-rw-r--r--data/deprecations/14-5-deprecate-sles-12sp2.yml2
-rw-r--r--data/deprecations/14-5-deprecation-versions-packagetype.yml2
-rw-r--r--data/deprecations/14-5-deprecation-vsa-announce-deprecation-of-vsa-filtering-calculation.yml2
-rw-r--r--data/deprecations/14-5-disable_strict_host_key_checking.yml2
-rw-r--r--data/deprecations/14-5-geo-deprecate-promote-db.yml2
-rw-r--r--data/deprecations/14-5-geo-deprecate-promote-to-primary-node.yml2
-rw-r--r--data/deprecations/14-5-package-container-registry-api-group-update.yml2
-rw-r--r--data/deprecations/14-5-remove-dependency-proxy-permissions-flag.yml2
-rw-r--r--data/deprecations/14-5-remove-package-pipelines-api.yml2
-rw-r--r--data/deprecations/14-5-remove-pipelines-from-version-field.yml2
-rw-r--r--data/deprecations/14-5-runner-api-status-does-contain-paused.yml4
-rw-r--r--data/deprecations/14-6-Enforce-validation-of-security-schemas.yml2
-rw-r--r--data/deprecations/14-6-container-scanning-schemas-below-14.yml2
-rw-r--r--data/deprecations/14-6-coverage-fuzzing-schemas-below-14.yml2
-rw-r--r--data/deprecations/14-6-dast-schemas-below-14.yml2
-rw-r--r--data/deprecations/14-6-dependency-scanning-schemas-below-14.yml2
-rw-r--r--data/deprecations/14-6-deprecate-types.yml2
-rw-r--r--data/deprecations/14-6-deprecation-license-compliance-api-terms.yml2
-rw-r--r--data/deprecations/14-6-deprecation-secure-dependency-scanning-bundler-audit.yml2
-rw-r--r--data/deprecations/14-6-job_char_limit.yml2
-rw-r--r--data/deprecations/14-6-remove-api-fuzzing-ci-configuration-create-mutation.yml2
-rw-r--r--data/deprecations/14-6-sast-schemas-below-14.yml2
-rw-r--r--data/deprecations/14-6-secret-detection-schemas-below-14.yml2
-rw-r--r--data/deprecations/14-7-deprecate-artifacts-keyword.yml2
-rw-r--r--data/deprecations/14-7-deprecate-godep-support-in-license-compliance.yml2
-rw-r--r--data/deprecations/14-7-deprecate-merged_by-api-field.yml2
-rw-r--r--data/deprecations/14-7-deprecate-static-site-editor.yml2
-rw-r--r--data/deprecations/14-7-pseudonymizer.yml2
-rw-r--r--data/deprecations/14-7-sidekiq-metrics-health-check-donfig.yml2
-rw-r--r--data/deprecations/14-8-Elasticsearch-6-8.yml2
-rw-r--r--data/deprecations/14-8-ci-build-variables.yml4
-rw-r--r--data/deprecations/14-8-compliance-required-pipeline-configuration-premium.yml2
-rw-r--r--data/deprecations/14-8-compliance-status-check-api-field.yml2
-rw-r--r--data/deprecations/14-8-deprecate-projectFingerprint-from-PipelineSecurityReportFinding-GraphQL.yml2
-rw-r--r--data/deprecations/14-8-deprecation-secure-dependency-scanning-retire-js.yml2
-rw-r--r--data/deprecations/14-8-enforce-pat-expiration.yml2
-rw-r--r--data/deprecations/14-8-enforce-ssh-expiration.yml2
-rw-r--r--data/deprecations/14-8-geo-deprecate-db-rake-tasks.yml2
-rw-r--r--data/deprecations/14-8-geo-deprecate-replication-detail-routes.yml2
-rw-r--r--data/deprecations/14-8-gitaly-deprecate-legacy-config-options.yml2
-rw-r--r--data/deprecations/14-8-gitaly-remove-per-repository-election.yml2
-rw-r--r--data/deprecations/14-8-graphql-ids.yml4
-rw-r--r--data/deprecations/14-8-grpc-proxy.yml2
-rw-r--r--data/deprecations/14-8-iteration-started-field.yml2
-rw-r--r--data/deprecations/14-8-protect-cns-chs.yml2
-rw-r--r--data/deprecations/14-8-protect-vulnerability-check.yml2
-rw-r--r--data/deprecations/14-8-remove_ff_push_rules_supersede_code_owners.yml4
-rw-r--r--data/deprecations/14-8-request-profiling.yml2
-rw-r--r--data/deprecations/14-8-runner-api-active-field-replaced-with-paused-breaking-change.yml4
-rw-r--r--data/deprecations/14-8-runner-api-status-filter-does-accept-active-or-paused.yml4
-rw-r--r--data/deprecations/14-8-sast-analyzer-removals.yml2
-rw-r--r--data/deprecations/14-8-sast-dotnet-21.yml2
-rw-r--r--data/deprecations/14-8-sast-secret-analyzer-image.yml2
-rw-r--r--data/deprecations/14-8-sast-spotbugs-java-8.yml2
-rw-r--r--data/deprecations/14-8-secret-detection-configurations.yml2
-rw-r--r--data/deprecations/14-8-secure-and-protect-analyzer-bump.yml2
-rw-r--r--data/deprecations/14-8-secure-ca-python-deprecation.yml2
-rw-r--r--data/deprecations/14-9-background-upload.yml2
-rw-r--r--data/deprecations/14-9-deprecate-composer-download-permissions.yml2
-rw-r--r--data/deprecations/14-9-deprecate-debian-9.yml2
-rw-r--r--data/deprecations/14-9-deprecate-permissions-change-package-settings.yml2
-rw-r--r--data/deprecations/14-9-deprecate-testcoveragesetting.yml2
-rw-r--r--data/deprecations/14-9-deprecation-htpassword-authentication-container-registry.yml2
-rw-r--r--data/deprecations/14-9-global-search-deprecate-user-email-lookup-limit.yml2
-rw-r--r--data/deprecations/14-9-pages-daemon.yml2
-rw-r--r--data/deprecations/14-9-system_monitoring.yml2
-rw-r--r--data/deprecations/15-0-ci-cd-settings-update-mutation-renamed.yml2
-rw-r--r--data/deprecations/15-0-deprecate-monitor-logging.yml2
-rw-r--r--data/deprecations/15-0-deprecate-monitor-metrics.yml2
-rw-r--r--data/deprecations/15-0-deprecate-monitor-tracing.yml2
-rw-r--r--data/deprecations/15-0-deprecate-postgresql-12.yml2
-rw-r--r--data/deprecations/15-0-instance-statistics-graphql-node-removal.yml2
-rw-r--r--data/deprecations/15-0-oauth-noexpiry.yml2
-rw-r--r--data/deprecations/15-0-oauth.yml2
-rw-r--r--data/deprecations/15-0-runner-status-legacy-mode.yml2
-rw-r--r--data/deprecations/15-1-deprecate-maintainer_note.yml2
-rw-r--r--data/deprecations/15-1-jira-github-enterprise-dvcs.yml2
-rw-r--r--data/deprecations/15-1-pipelinesecurityreportfinding-name.yml2
-rw-r--r--data/deprecations/15-1-pipelinesecurityreportfinding-projectfingerprint.yml2
-rw-r--r--data/deprecations/15-1-project-pipeline-securityReportFindings.yml2
-rw-r--r--data/deprecations/15-2-deprecation-vulnerability-report-state-sort.yml2
-rw-r--r--data/deprecations/15-2-job_age-deprecation.yml2
-rw-r--r--data/deprecations/15-3-deprecate-redis-5.yml2
-rw-r--r--data/deprecations/15-3-deprecation-vulnerability-report-tool-sort.yml2
-rw-r--r--data/deprecations/15-3-omniauth-cas3.yml2
-rw-r--r--data/deprecations/15-3-omniauth-crowd.yml2
-rw-r--r--data/deprecations/15-3-pipeline_activity_limit.yml2
-rw-r--r--data/deprecations/15-3-vulnerabilityFindingDismiss-mutation.yml2
-rw-r--r--data/deprecations/15-4-confidence-field-in-graphql.yml2
-rw-r--r--data/deprecations/15-4-create-deprecation-draft-quick-action-toggle.yml4
-rw-r--r--data/deprecations/15-4-cs-docker-variables.yml2
-rw-r--r--data/deprecations/15-4-deprecate-bundled-grafana.yml2
-rw-r--r--data/deprecations/15-4-non-expiring-access-tokens.yml2
-rw-r--r--data/deprecations/15-4-starboard-directive.yml2
-rw-r--r--data/deprecations/15-5-confidential-field-on-notes.yml2
-rw-r--r--data/deprecations/15-5-disable-file-type-var-expansion-ci-pipeline.yml4
-rw-r--r--data/deprecations/15-5-vulnerabilityFindingDismiss-mutation.yml2
-rw-r--r--data/deprecations/15-6-deprecate-config-fields-runner-helm-chart.yml10
-rw-r--r--data/deprecations/15-6-deprecate-merge_status-api-field.yml27
-rw-r--r--data/deprecations/15-6-deprecate-post-api-v4-runner.yml20
-rw-r--r--data/deprecations/15-6-deprecate-runner-reg-token-helm.yml16
-rw-r--r--data/deprecations/15-6-deprecate-runner-register-command.yml13
-rw-r--r--data/deprecations/15-6-deprecate-runner-register-token-k8s-operator.yml18
-rw-r--r--data/deprecations/15-7-dast-api-variable-deprecation.yml17
-rw-r--r--data/deprecations/15-7-deprecate-api-v4-runner-registration-token-reset-endpoints.yml27
-rw-r--r--data/deprecations/15-7-deprecate-dast-api-scan-in-dast-template.yml11
-rw-r--r--data/deprecations/15-7-deprecate-dast-zap-variables.yml13
-rw-r--r--data/deprecations/15-7-deprecate-gitlab-runner-exec-cmd.yml21
-rw-r--r--data/deprecations/15-7-deprecate-kas-metrics-port-in-gitlab-chart.yml22
-rw-r--r--data/deprecations/15-7-deprecate-phabricator-importer.yml12
-rw-r--r--data/deprecations/15-7-deprecate-shimo-integration.yml27
-rw-r--r--data/deprecations/15-7-deprecate-single-merge-request-changes-api-endpoint.yml14
-rw-r--r--data/deprecations/15-7-deprecate-zentao-integration.yml27
-rw-r--r--data/deprecations/15-7-enable-period-in-terraform-state-name.yml25
-rw-r--r--data/deprecations/15-8-dast-report-variables-deprecation.yml13
-rw-r--r--data/deprecations/16-0-post-ci-lint.yml44
-rw-r--r--data/deprecations/16-0-security_report_schemas_v14-x-x.yml2
-rw-r--r--data/deprecations/distribution_deprecations_14-4.yml2
-rw-r--r--data/deprecations/templates/_deprecation_template.md.erb5
-rw-r--r--data/deprecations/templates/example.yml15
-rw-r--r--data/removals/14_0/14_0-ds-deprecations.yml2
-rw-r--r--data/removals/14_0/14_0-lc-deprecations.yml2
-rw-r--r--data/removals/14_0/change_default_branch_name_to_main.yml2
-rw-r--r--data/removals/14_0/create-code-review-draft-wip.yml2
-rw-r--r--data/removals/14_0/create-code-review-w-parameter-removal.yml2
-rw-r--r--data/removals/14_0/deprecation_bump_terraform_template_version.yml2
-rw-r--r--data/removals/14_0/deprecation_manage_access_14_0.yml4
-rw-r--r--data/removals/14_0/deprecation_update_cicd_templates_to_stop_using_hardcode_master.yml2
-rw-r--r--data/removals/14_0/deuley_servicetemplates_removal.yml2
-rw-r--r--data/removals/14_0/release_announce_deprecation_of_release_notes_api.yml2
-rw-r--r--data/removals/14_0/release_deprecation_auto-deploy-image.yml2
-rw-r--r--data/removals/14_0/release_domainsource_configuration_for_gitlab_pages_deprecation.yml2
-rw-r--r--data/removals/14_0/release_legacy_feature_flags_deprecation.yml2
-rw-r--r--data/removals/14_0/release_remove_redundant_keyvalue_pair_from_the_payload_of_dora.yml2
-rw-r--r--data/removals/14_0/removal-geo-fdw-settings.yml2
-rw-r--r--data/removals/14_0/removal-graphql-fields.yml2
-rw-r--r--data/removals/14_0/removal-legacy-storage.yml2
-rw-r--r--data/removals/14_0/removal-protect-features.yml4
-rw-r--r--data/removals/14_0/removal-sidekiq_experimental_queue_selector.yml2
-rw-r--r--data/removals/14_0/removal-unicorn.yml2
-rw-r--r--data/removals/14_0/removal_ci_project_config_path.yml2
-rw-r--r--data/removals/14_0/removal_enablement_helm2.yml2
-rw-r--r--data/removals/14_0/removal_enablement_opensuse_15_1.yml2
-rw-r--r--data/removals/14_0/removal_enablement_pg11.yml2
-rw-r--r--data/removals/14_0/removal_enablement_ubuntu_16.yml2
-rw-r--r--data/removals/14_0/removal_repost_static_analysis_notices.yml8
-rw-r--r--data/removals/14_0/removal_runner_25555.yml2
-rw-r--r--data/removals/14_0/removal_runner_26036.yml2
-rw-r--r--data/removals/14_0/removal_runner_26419.yml2
-rw-r--r--data/removals/14_0/removal_runner_4845.yml2
-rw-r--r--data/removals/14_0/removal_runner_6413.yml2
-rw-r--r--data/removals/14_0/removals-14-testing-team.yml6
-rw-r--r--data/removals/14_0/removals_runner_26651.yml2
-rw-r--r--data/removals/14_0/removals_runner_26679.yml2
-rw-r--r--data/removals/14_0/removals_runner_26900.yml2
-rw-r--r--data/removals/14_0/removals_runner_27175.yml2
-rw-r--r--data/removals/14_0/removals_runner_27218.yml2
-rw-r--r--data/removals/14_0/removals_runner_27551.yml2
-rw-r--r--data/removals/14_0/removals_runner_27899.yml2
-rw-r--r--data/removals/14_0/remove-sql-elector.yml2
-rw-r--r--data/removals/14_0/remove_dast_env_variables.yml2
-rw-r--r--data/removals/14_0/remove_dast_legacy_domain_validation.yml2
-rw-r--r--data/removals/14_0/remove_dast_legacy_report_fields.yml2
-rw-r--r--data/removals/14_0/remove_dast_spider_host_reset.yml2
-rw-r--r--data/removals/14_0/remove_dast_template_stages.yml2
-rw-r--r--data/removals/14_0/remove_optimize_api.yml2
-rw-r--r--data/removals/14_0/remove_terraform_template.yml2
-rw-r--r--data/removals/14_0/verify-ci-removal-parametertrace.yml2
-rw-r--r--data/removals/14_0/verify-ci-removalpipelineservice.yml2
-rw-r--r--data/removals/14_1/removal-memory-prometheus-options-source.yml2
-rw-r--r--data/removals/14_1/removal-outdated-browser-support.yml2
-rw-r--r--data/removals/14_10/14-10-package-permissions-composer-change.yml2
-rw-r--r--data/removals/14_2/removal-verify-build-log.yml2
-rw-r--r--data/removals/14_3/removal-limit-tags-to-50.yml2
-rw-r--r--data/removals/14_3/removal-verify-pe-pipelinefindername.yml2
-rw-r--r--data/removals/14_3/removal_legacy_storage_setting.yml2
-rw-r--r--data/removals/14_6/limit_trigger_pipelines.yml2
-rw-r--r--data/removals/14_6/removal-release-cli-s3.yml2
-rw-r--r--data/removals/14_9/removal_monitor_respond_integrated_error_tracking.yml2
-rw-r--r--data/removals/15_0/15-0-Legacy-approval-status-names-from-License-Compliance-API.yml2
-rw-r--r--data/removals/15_0/15-0-Pseudonymizer.yml2
-rw-r--r--data/removals/15_0/15-0-Retire-js-analyzer.yml2
-rw-r--r--data/removals/15_0/15-0-SLES-12-SP2.yml2
-rw-r--r--data/removals/15_0/15-0-advanced-search-elasticsearch-6-8.yml2
-rw-r--r--data/removals/15_0/15-0-bundler-audit.yml2
-rw-r--r--data/removals/15_0/15-0-configure-self-managed-cert-based-kube-feature-flag.yml2
-rw-r--r--data/removals/15_0/15-0-container-registry-htpasswd.yml2
-rw-r--r--data/removals/15_0/15-0-custom_hooks_dir.yml2
-rw-r--r--data/removals/15_0/15-0-database-deprecate-legacy-database-conf.yml2
-rw-r--r--data/removals/15_0/15-0-dependency-scanning-default-java-version.yml2
-rw-r--r--data/removals/15_0/15-0-dependency-scanning-python-image.yml2
-rw-r--r--data/removals/15_0/15-0-ds-default-analyzers.yml2
-rw-r--r--data/removals/15_0/15-0-geo-remove-db-rake-tasks.yml2
-rw-r--r--data/removals/15_0/15-0-geo-remove-promote-db.yml2
-rw-r--r--data/removals/15_0/15-0-geo-remove-promote-to-primary-node.yml2
-rw-r--r--data/removals/15_0/15-0-gitaly-internal-socket-dir.yml2
-rw-r--r--data/removals/15_0/15-0-managed-cluster-applications.yml2
-rw-r--r--data/removals/15_0/15-0-merge-commit-message.yml2
-rw-r--r--data/removals/15_0/15-0-oauth-implicit-grant.yml2
-rw-r--r--data/removals/15_0/15-0-oauth-tokens-no-expiry.yml2
-rw-r--r--data/removals/15_0/15-0-omniauth-kerberos-gem.yml2
-rw-r--r--data/removals/15_0/15-0-package-container-registry-group-api.yml2
-rw-r--r--data/removals/15_0/15-0-package-settings-permissions.yml2
-rw-r--r--data/removals/15_0/15-0-praefect-database-no-proxy.yml2
-rw-r--r--data/removals/15_0/15-0-praefect-virtual-storage.yml2
-rw-r--r--data/removals/15_0/15-0-protect-cns-chs.yml2
-rw-r--r--data/removals/15_0/15-0-protect-vulnerability-check.yml2
-rw-r--r--data/removals/15_0/15-0-removal-artifacts-keyword.yml2
-rw-r--r--data/removals/15_0/15-0-removal-testcoveragesetting.yml2
-rw-r--r--data/removals/15_0/15-0-remove-background-upload-object-storage.yml2
-rw-r--r--data/removals/15_0/15-0-remove-dependency-proxy-feature-flag.yml2
-rw-r--r--data/removals/15_0/15-0-remove-replicaiton-detail-routes.yml2
-rw-r--r--data/removals/15_0/15-0-remove-versions-packagetype.yml2
-rw-r--r--data/removals/15_0/15-0-remove_ff_push_rules_supersede_code_owners.yml4
-rw-r--r--data/removals/15_0/15-0-request-profiling.yml2
-rw-r--r--data/removals/15_0/15-0-runner-api-status-renames-not_connected.yml2
-rw-r--r--data/removals/15_0/15-0-runner-disable-strict-host-key-check.yml2
-rw-r--r--data/removals/15_0/15-0-runner_api_new_stale_status_breaking_change.yml2
-rw-r--r--data/removals/15_0/15-0-sast-dotnet-21.yml2
-rw-r--r--data/removals/15_0/15-0-sast-spotbugs-java-8.yml2
-rw-r--r--data/removals/15_0/15-0-secret-detection-configurations.yml2
-rw-r--r--data/removals/15_0/15-0-serverless.yml2
-rw-r--r--data/removals/15_0/15-0-sidekiq-metrics-health-check-config.yml2
-rw-r--r--data/removals/15_0/15-0-static-site-editor.yml2
-rw-r--r--data/removals/15_0/15-0-tracing.yml2
-rw-r--r--data/removals/15_0/15-0-type.yml2
-rw-r--r--data/removals/15_0/15_0-logging.yml2
-rw-r--r--data/removals/15_0/15_0-remove-pipelines-from-version-field.yml2
-rw-r--r--data/removals/15_0/removal-manage-premium-required-pipelines.yml2
-rw-r--r--data/removals/15_0/removal_manage_optional_pat_expiration.yml2
-rw-r--r--data/removals/15_0/removal_manage_repository_push_audit_event.yml2
-rw-r--r--data/removals/15_0/removal_manage_ssh_expiration.yml2
-rw-r--r--data/removals/15_0/removal_manage_status_check_passed_status.yml2
-rw-r--r--data/removals/15_2/removal-outdated-browser-support.yml2
-rw-r--r--data/removals/15_3/15-3-vulnerability-report-state-sort.yml2
-rw-r--r--data/removals/15_3/15-3-vulnerability-report-tool-sort.yml2
-rw-r--r--data/removals/15_3/removal_debian9.yml2
-rw-r--r--data/removals/15_4/15-4-sast-analyzer-consolidation.yml2
-rw-r--r--data/removals/15_6/15-6-nfs-git-repository-storage.yml32
-rw-r--r--data/removals/15_7/15-7-remove-flowdock-integration.yml18
-rw-r--r--data/removals/15_8/15-8-auto-deploy-helm-chart-cilium-policy.yml16
-rw-r--r--data/removals/16_0/source_code-approvals-endpoint.yml4
-rw-r--r--data/removals/templates/_removal_template.md.erb5
-rw-r--r--data/removals/templates/example.yml12
-rw-r--r--data/whats_new/202211220001_15_06.yml87
-rw-r--r--db/docs/abuse_reports.yml1
-rw-r--r--db/docs/achievements.yml10
-rw-r--r--db/docs/agent_activity_events.yml1
-rw-r--r--db/docs/agent_group_authorizations.yml1
-rw-r--r--db/docs/agent_project_authorizations.yml1
-rw-r--r--db/docs/alert_management_alert_assignees.yml1
-rw-r--r--db/docs/alert_management_alert_metric_images.yml1
-rw-r--r--db/docs/alert_management_alert_user_mentions.yml1
-rw-r--r--db/docs/alert_management_alerts.yml1
-rw-r--r--db/docs/alert_management_http_integrations.yml1
-rw-r--r--db/docs/allowed_email_domains.yml1
-rw-r--r--db/docs/analytics_cycle_analytics_aggregations.yml1
-rw-r--r--db/docs/analytics_cycle_analytics_group_stages.yml1
-rw-r--r--db/docs/analytics_cycle_analytics_group_value_streams.yml1
-rw-r--r--db/docs/analytics_cycle_analytics_issue_stage_events.yml1
-rw-r--r--db/docs/analytics_cycle_analytics_merge_request_stage_events.yml1
-rw-r--r--db/docs/analytics_cycle_analytics_project_stages.yml1
-rw-r--r--db/docs/analytics_cycle_analytics_project_value_streams.yml1
-rw-r--r--db/docs/analytics_cycle_analytics_stage_event_hashes.yml1
-rw-r--r--db/docs/analytics_devops_adoption_segments.yml1
-rw-r--r--db/docs/analytics_devops_adoption_snapshots.yml1
-rw-r--r--db/docs/analytics_language_trend_repository_languages.yml1
-rw-r--r--db/docs/analytics_usage_trends_measurements.yml1
-rw-r--r--db/docs/appearances.yml1
-rw-r--r--db/docs/application_setting_terms.yml1
-rw-r--r--db/docs/application_settings.yml1
-rw-r--r--db/docs/approval_merge_request_rule_sources.yml1
-rw-r--r--db/docs/approval_merge_request_rules.yml1
-rw-r--r--db/docs/approval_merge_request_rules_approved_approvers.yml1
-rw-r--r--db/docs/approval_merge_request_rules_groups.yml1
-rw-r--r--db/docs/approval_merge_request_rules_users.yml1
-rw-r--r--db/docs/approval_project_rules.yml1
-rw-r--r--db/docs/approval_project_rules_groups.yml1
-rw-r--r--db/docs/approval_project_rules_protected_branches.yml1
-rw-r--r--db/docs/approval_project_rules_users.yml1
-rw-r--r--db/docs/approvals.yml1
-rw-r--r--db/docs/approver_groups.yml1
-rw-r--r--db/docs/approvers.yml1
-rw-r--r--db/docs/ar_internal_metadata.yml1
-rw-r--r--db/docs/atlassian_identities.yml1
-rw-r--r--db/docs/audit_events.yml1
-rw-r--r--db/docs/audit_events_external_audit_event_destinations.yml1
-rw-r--r--db/docs/audit_events_streaming_event_type_filters.yml1
-rw-r--r--db/docs/audit_events_streaming_headers.yml1
-rw-r--r--db/docs/authentication_events.yml1
-rw-r--r--db/docs/award_emoji.yml1
-rw-r--r--db/docs/aws_roles.yml1
-rw-r--r--db/docs/background_migration_jobs.yml1
-rw-r--r--db/docs/badges.yml1
-rw-r--r--db/docs/banned_users.yml1
-rw-r--r--db/docs/batched_background_migration_job_transition_logs.yml1
-rw-r--r--db/docs/batched_background_migration_jobs.yml1
-rw-r--r--db/docs/batched_background_migrations.yml1
-rw-r--r--db/docs/board_assignees.yml1
-rw-r--r--db/docs/board_group_recent_visits.yml1
-rw-r--r--db/docs/board_labels.yml1
-rw-r--r--db/docs/board_project_recent_visits.yml1
-rw-r--r--db/docs/board_user_preferences.yml1
-rw-r--r--db/docs/boards.yml1
-rw-r--r--db/docs/boards_epic_board_labels.yml1
-rw-r--r--db/docs/boards_epic_board_positions.yml1
-rw-r--r--db/docs/boards_epic_board_recent_visits.yml1
-rw-r--r--db/docs/boards_epic_boards.yml1
-rw-r--r--db/docs/boards_epic_list_user_preferences.yml1
-rw-r--r--db/docs/boards_epic_lists.yml1
-rw-r--r--db/docs/boards_epic_user_preferences.yml1
-rw-r--r--db/docs/broadcast_messages.yml1
-rw-r--r--db/docs/bulk_import_configurations.yml1
-rw-r--r--db/docs/bulk_import_entities.yml1
-rw-r--r--db/docs/bulk_import_export_uploads.yml1
-rw-r--r--db/docs/bulk_import_exports.yml1
-rw-r--r--db/docs/bulk_import_failures.yml1
-rw-r--r--db/docs/bulk_import_trackers.yml1
-rw-r--r--db/docs/bulk_imports.yml1
-rw-r--r--db/docs/chat_names.yml1
-rw-r--r--db/docs/chat_teams.yml1
-rw-r--r--db/docs/ci_build_needs.yml1
-rw-r--r--db/docs/ci_build_pending_states.yml1
-rw-r--r--db/docs/ci_build_report_results.yml1
-rw-r--r--db/docs/ci_build_trace_chunks.yml1
-rw-r--r--db/docs/ci_build_trace_metadata.yml1
-rw-r--r--db/docs/ci_builds.yml1
-rw-r--r--db/docs/ci_builds_metadata.yml1
-rw-r--r--db/docs/ci_builds_runner_session.yml1
-rw-r--r--db/docs/ci_daily_build_group_report_results.yml1
-rw-r--r--db/docs/ci_deleted_objects.yml1
-rw-r--r--db/docs/ci_freeze_periods.yml1
-rw-r--r--db/docs/ci_group_variables.yml1
-rw-r--r--db/docs/ci_instance_variables.yml1
-rw-r--r--db/docs/ci_job_artifact_states.yml1
-rw-r--r--db/docs/ci_job_artifacts.yml1
-rw-r--r--db/docs/ci_job_token_project_scope_links.yml6
-rw-r--r--db/docs/ci_job_variables.yml1
-rw-r--r--db/docs/ci_minutes_additional_packs.yml1
-rw-r--r--db/docs/ci_namespace_mirrors.yml1
-rw-r--r--db/docs/ci_namespace_monthly_usages.yml1
-rw-r--r--db/docs/ci_partitions.yml1
-rw-r--r--db/docs/ci_pending_builds.yml3
-rw-r--r--db/docs/ci_pipeline_artifacts.yml1
-rw-r--r--db/docs/ci_pipeline_chat_data.yml1
-rw-r--r--db/docs/ci_pipeline_messages.yml1
-rw-r--r--db/docs/ci_pipeline_metadata.yml5
-rw-r--r--db/docs/ci_pipeline_schedule_variables.yml1
-rw-r--r--db/docs/ci_pipeline_schedules.yml1
-rw-r--r--db/docs/ci_pipeline_variables.yml1
-rw-r--r--db/docs/ci_pipelines.yml1
-rw-r--r--db/docs/ci_pipelines_config.yml1
-rw-r--r--db/docs/ci_platform_metrics.yml1
-rw-r--r--db/docs/ci_project_mirrors.yml1
-rw-r--r--db/docs/ci_project_monthly_usages.yml1
-rw-r--r--db/docs/ci_refs.yml1
-rw-r--r--db/docs/ci_resource_groups.yml1
-rw-r--r--db/docs/ci_resources.yml1
-rw-r--r--db/docs/ci_runner_namespaces.yml1
-rw-r--r--db/docs/ci_runner_projects.yml1
-rw-r--r--db/docs/ci_runner_versions.yml1
-rw-r--r--db/docs/ci_runners.yml1
-rw-r--r--db/docs/ci_running_builds.yml9
-rw-r--r--db/docs/ci_secure_file_states.yml1
-rw-r--r--db/docs/ci_secure_files.yml1
-rw-r--r--db/docs/ci_sources_pipelines.yml1
-rw-r--r--db/docs/ci_sources_projects.yml1
-rw-r--r--db/docs/ci_stages.yml1
-rw-r--r--db/docs/ci_subscriptions_projects.yml1
-rw-r--r--db/docs/ci_trigger_requests.yml1
-rw-r--r--db/docs/ci_triggers.yml1
-rw-r--r--db/docs/ci_unit_test_failures.yml1
-rw-r--r--db/docs/ci_unit_tests.yml1
-rw-r--r--db/docs/ci_variables.yml1
-rw-r--r--db/docs/cluster_agent_tokens.yml1
-rw-r--r--db/docs/cluster_agents.yml1
-rw-r--r--db/docs/cluster_enabled_grants.yml1
-rw-r--r--db/docs/cluster_groups.yml3
-rw-r--r--db/docs/cluster_platforms_kubernetes.yml3
-rw-r--r--db/docs/cluster_projects.yml3
-rw-r--r--db/docs/cluster_providers_aws.yml3
-rw-r--r--db/docs/cluster_providers_gcp.yml3
-rw-r--r--db/docs/clusters.yml1
-rw-r--r--db/docs/clusters_applications_cert_managers.yml3
-rw-r--r--db/docs/clusters_applications_cilium.yml1
-rw-r--r--db/docs/clusters_applications_crossplane.yml3
-rw-r--r--db/docs/clusters_applications_helm.yml3
-rw-r--r--db/docs/clusters_applications_ingress.yml3
-rw-r--r--db/docs/clusters_applications_jupyter.yml3
-rw-r--r--db/docs/clusters_applications_knative.yml3
-rw-r--r--db/docs/clusters_applications_prometheus.yml1
-rw-r--r--db/docs/clusters_applications_runners.yml3
-rw-r--r--db/docs/clusters_integration_prometheus.yml1
-rw-r--r--db/docs/clusters_kubernetes_namespaces.yml3
-rw-r--r--db/docs/commit_user_mentions.yml3
-rw-r--r--db/docs/compliance_management_frameworks.yml1
-rw-r--r--db/docs/container_expiration_policies.yml1
-rw-r--r--db/docs/container_repositories.yml1
-rw-r--r--db/docs/content_blocked_states.yml1
-rw-r--r--db/docs/conversational_development_index_metrics.yml1
-rw-r--r--db/docs/coverage_fuzzing_corpuses.yml1
-rw-r--r--db/docs/csv_issue_imports.yml1
-rw-r--r--db/docs/custom_emoji.yml1
-rw-r--r--db/docs/customer_relations_contacts.yml1
-rw-r--r--db/docs/customer_relations_organizations.yml1
-rw-r--r--db/docs/dast_pre_scan_verification_steps.yml10
-rw-r--r--db/docs/dast_pre_scan_verifications.yml10
-rw-r--r--db/docs/dast_profile_schedules.yml1
-rw-r--r--db/docs/dast_profiles.yml1
-rw-r--r--db/docs/dast_profiles_pipelines.yml1
-rw-r--r--db/docs/dast_scanner_profiles.yml1
-rw-r--r--db/docs/dast_scanner_profiles_builds.yml1
-rw-r--r--db/docs/dast_scanner_profiles_tags.yml10
-rw-r--r--db/docs/dast_site_profile_secret_variables.yml1
-rw-r--r--db/docs/dast_site_profiles.yml1
-rw-r--r--db/docs/dast_site_profiles_builds.yml1
-rw-r--r--db/docs/dast_site_profiles_pipelines.yml1
-rw-r--r--db/docs/dast_site_tokens.yml1
-rw-r--r--db/docs/dast_site_validations.yml1
-rw-r--r--db/docs/dast_sites.yml1
-rw-r--r--db/docs/deleted_tables/alerts_service_data.yml9
-rw-r--r--db/docs/deleted_tables/analytics_devops_adoption_segment_selections.yml9
-rw-r--r--db/docs/deleted_tables/analytics_repository_file_commits.yml9
-rw-r--r--db/docs/deleted_tables/analytics_repository_file_edits.yml9
-rw-r--r--db/docs/deleted_tables/analytics_repository_files.yml9
-rw-r--r--db/docs/deleted_tables/audit_events_archived.yml9
-rw-r--r--db/docs/deleted_tables/audit_events_part_5fc467ac26.yml9
-rw-r--r--db/docs/deleted_tables/backup_labels.yml9
-rw-r--r--db/docs/deleted_tables/ci_build_trace_section_names.yml9
-rw-r--r--db/docs/deleted_tables/ci_build_trace_sections.yml9
-rw-r--r--db/docs/deleted_tables/ci_daily_report_results.yml9
-rw-r--r--db/docs/deleted_tables/ci_test_case_failures.yml9
-rw-r--r--db/docs/deleted_tables/ci_test_cases.yml9
-rw-r--r--db/docs/deleted_tables/clusters_applications_fluentd.yml9
-rw-r--r--db/docs/deleted_tables/forked_project_links.yml9
-rw-r--r--db/docs/deleted_tables/issue_milestones.yml9
-rw-r--r--db/docs/deleted_tables/merge_request_milestones.yml9
-rw-r--r--db/docs/deleted_tables/namespace_onboarding_actions.yml9
-rw-r--r--db/docs/deleted_tables/services.yml9
-rw-r--r--db/docs/deleted_tables/terraform_state_registry.yml9
-rw-r--r--db/docs/deleted_tables/tmp_fingerprint_sha256_migration.yml9
-rw-r--r--db/docs/deleted_tables/vulnerability_export_registry.yml9
-rw-r--r--db/docs/deleted_tables/vulnerability_export_verification_status.yml9
-rw-r--r--db/docs/deleted_tables/vulnerability_finding_fingerprints.yml9
-rw-r--r--db/docs/deleted_tables/web_hook_logs_archived.yml9
-rw-r--r--db/docs/deleted_tables/web_hook_logs_part_0c5294f417.yml9
-rw-r--r--db/docs/dependency_list_exports.yml8
-rw-r--r--db/docs/dependency_proxy_blob_states.yml1
-rw-r--r--db/docs/dependency_proxy_blobs.yml1
-rw-r--r--db/docs/dependency_proxy_group_settings.yml1
-rw-r--r--db/docs/dependency_proxy_image_ttl_group_policies.yml1
-rw-r--r--db/docs/dependency_proxy_manifest_states.yml10
-rw-r--r--db/docs/dependency_proxy_manifests.yml1
-rw-r--r--db/docs/deploy_keys_projects.yml1
-rw-r--r--db/docs/deploy_tokens.yml1
-rw-r--r--db/docs/deployment_approvals.yml5
-rw-r--r--db/docs/deployment_clusters.yml3
-rw-r--r--db/docs/deployment_merge_requests.yml1
-rw-r--r--db/docs/deployments.yml5
-rw-r--r--db/docs/description_versions.yml1
-rw-r--r--db/docs/design_management_designs.yml1
-rw-r--r--db/docs/design_management_designs_versions.yml1
-rw-r--r--db/docs/design_management_versions.yml1
-rw-r--r--db/docs/design_user_mentions.yml1
-rw-r--r--db/docs/detached_partitions.yml1
-rw-r--r--db/docs/diff_note_positions.yml1
-rw-r--r--db/docs/dingtalk_tracker_data.yml1
-rw-r--r--db/docs/dora_configurations.yml1
-rw-r--r--db/docs/dora_daily_metrics.yml1
-rw-r--r--db/docs/draft_notes.yml1
-rw-r--r--db/docs/elastic_index_settings.yml1
-rw-r--r--db/docs/elastic_reindexing_slices.yml1
-rw-r--r--db/docs/elastic_reindexing_subtasks.yml1
-rw-r--r--db/docs/elastic_reindexing_tasks.yml1
-rw-r--r--db/docs/elasticsearch_indexed_namespaces.yml1
-rw-r--r--db/docs/elasticsearch_indexed_projects.yml1
-rw-r--r--db/docs/emails.yml1
-rw-r--r--db/docs/environments.yml5
-rw-r--r--db/docs/epic_issues.yml1
-rw-r--r--db/docs/epic_metrics.yml1
-rw-r--r--db/docs/epic_user_mentions.yml1
-rw-r--r--db/docs/epics.yml1
-rw-r--r--db/docs/error_tracking_client_keys.yml1
-rw-r--r--db/docs/error_tracking_error_events.yml1
-rw-r--r--db/docs/error_tracking_errors.yml1
-rw-r--r--db/docs/events.yml1
-rw-r--r--db/docs/evidences.yml1
-rw-r--r--db/docs/experiment_subjects.yml9
-rw-r--r--db/docs/experiments.yml9
-rw-r--r--db/docs/external_approval_rules.yml1
-rw-r--r--db/docs/external_approval_rules_protected_branches.yml1
-rw-r--r--db/docs/external_pull_requests.yml1
-rw-r--r--db/docs/external_status_checks.yml1
-rw-r--r--db/docs/external_status_checks_protected_branches.yml1
-rw-r--r--db/docs/feature_gates.yml1
-rw-r--r--db/docs/features.yml1
-rw-r--r--db/docs/fork_network_members.yml1
-rw-r--r--db/docs/fork_networks.yml1
-rw-r--r--db/docs/geo_cache_invalidation_events.yml1
-rw-r--r--db/docs/geo_container_repository_updated_events.yml1
-rw-r--r--db/docs/geo_event_log.yml1
-rw-r--r--db/docs/geo_events.yml1
-rw-r--r--db/docs/geo_hashed_storage_attachments_events.yml1
-rw-r--r--db/docs/geo_hashed_storage_migrated_events.yml1
-rw-r--r--db/docs/geo_node_namespace_links.yml1
-rw-r--r--db/docs/geo_node_statuses.yml1
-rw-r--r--db/docs/geo_nodes.yml1
-rw-r--r--db/docs/geo_repositories_changed_events.yml1
-rw-r--r--db/docs/geo_repository_created_events.yml1
-rw-r--r--db/docs/geo_repository_deleted_events.yml1
-rw-r--r--db/docs/geo_repository_renamed_events.yml1
-rw-r--r--db/docs/geo_repository_updated_events.yml1
-rw-r--r--db/docs/geo_reset_checksum_events.yml1
-rw-r--r--db/docs/ghost_user_migrations.yml1
-rw-r--r--db/docs/gitlab_subscription_histories.yml1
-rw-r--r--db/docs/gitlab_subscriptions.yml1
-rw-r--r--db/docs/gpg_key_subkeys.yml1
-rw-r--r--db/docs/gpg_keys.yml1
-rw-r--r--db/docs/gpg_signatures.yml1
-rw-r--r--db/docs/grafana_integrations.yml1
-rw-r--r--db/docs/group_crm_settings.yml1
-rw-r--r--db/docs/group_custom_attributes.yml1
-rw-r--r--db/docs/group_deletion_schedules.yml1
-rw-r--r--db/docs/group_deploy_keys.yml1
-rw-r--r--db/docs/group_deploy_keys_groups.yml1
-rw-r--r--db/docs/group_deploy_tokens.yml1
-rw-r--r--db/docs/group_features.yml1
-rw-r--r--db/docs/group_group_links.yml1
-rw-r--r--db/docs/group_import_states.yml1
-rw-r--r--db/docs/group_merge_request_approval_settings.yml1
-rw-r--r--db/docs/group_repository_storage_moves.yml1
-rw-r--r--db/docs/group_wiki_repositories.yml1
-rw-r--r--db/docs/historical_data.yml1
-rw-r--r--db/docs/identities.yml1
-rw-r--r--db/docs/import_export_uploads.yml1
-rw-r--r--db/docs/import_failures.yml1
-rw-r--r--db/docs/in_product_marketing_emails.yml1
-rw-r--r--db/docs/incident_management_escalation_policies.yml1
-rw-r--r--db/docs/incident_management_escalation_rules.yml1
-rw-r--r--db/docs/incident_management_issuable_escalation_statuses.yml1
-rw-r--r--db/docs/incident_management_oncall_participants.yml1
-rw-r--r--db/docs/incident_management_oncall_rotations.yml1
-rw-r--r--db/docs/incident_management_oncall_schedules.yml1
-rw-r--r--db/docs/incident_management_oncall_shifts.yml1
-rw-r--r--db/docs/incident_management_pending_alert_escalations.yml1
-rw-r--r--db/docs/incident_management_pending_issue_escalations.yml1
-rw-r--r--db/docs/incident_management_timeline_event_tag_links.yml1
-rw-r--r--db/docs/incident_management_timeline_event_tags.yml1
-rw-r--r--db/docs/incident_management_timeline_events.yml1
-rw-r--r--db/docs/index_statuses.yml1
-rw-r--r--db/docs/insights.yml1
-rw-r--r--db/docs/integrations.yml2
-rw-r--r--db/docs/internal_ids.yml1
-rw-r--r--db/docs/ip_restrictions.yml1
-rw-r--r--db/docs/issuable_metric_images.yml1
-rw-r--r--db/docs/issuable_resource_links.yml1
-rw-r--r--db/docs/issuable_severities.yml1
-rw-r--r--db/docs/issuable_slas.yml1
-rw-r--r--db/docs/issue_assignees.yml1
-rw-r--r--db/docs/issue_customer_relations_contacts.yml1
-rw-r--r--db/docs/issue_email_participants.yml1
-rw-r--r--db/docs/issue_emails.yml1
-rw-r--r--db/docs/issue_links.yml1
-rw-r--r--db/docs/issue_metrics.yml1
-rw-r--r--db/docs/issue_search_data.yml1
-rw-r--r--db/docs/issue_tracker_data.yml1
-rw-r--r--db/docs/issue_user_mentions.yml1
-rw-r--r--db/docs/issues.yml1
-rw-r--r--db/docs/issues_prometheus_alert_events.yml1
-rw-r--r--db/docs/issues_self_managed_prometheus_alert_events.yml1
-rw-r--r--db/docs/iterations_cadences.yml1
-rw-r--r--db/docs/jira_connect_installations.yml1
-rw-r--r--db/docs/jira_connect_subscriptions.yml1
-rw-r--r--db/docs/jira_imports.yml1
-rw-r--r--db/docs/jira_tracker_data.yml1
-rw-r--r--db/docs/keys.yml1
-rw-r--r--db/docs/label_links.yml1
-rw-r--r--db/docs/label_priorities.yml1
-rw-r--r--db/docs/labels.yml1
-rw-r--r--db/docs/ldap_group_links.yml1
-rw-r--r--db/docs/lfs_file_locks.yml1
-rw-r--r--db/docs/lfs_object_states.yml1
-rw-r--r--db/docs/lfs_objects.yml1
-rw-r--r--db/docs/lfs_objects_projects.yml3
-rw-r--r--db/docs/licenses.yml1
-rw-r--r--db/docs/list_user_preferences.yml1
-rw-r--r--db/docs/lists.yml1
-rw-r--r--db/docs/loose_foreign_keys_deleted_records.yml1
-rw-r--r--db/docs/member_roles.yml1
-rw-r--r--db/docs/member_tasks.yml1
-rw-r--r--db/docs/members.yml1
-rw-r--r--db/docs/merge_request_assignees.yml1
-rw-r--r--db/docs/merge_request_blocks.yml1
-rw-r--r--db/docs/merge_request_cleanup_schedules.yml1
-rw-r--r--db/docs/merge_request_context_commit_diff_files.yml1
-rw-r--r--db/docs/merge_request_context_commits.yml1
-rw-r--r--db/docs/merge_request_diff_commit_users.yml1
-rw-r--r--db/docs/merge_request_diff_commits.yml1
-rw-r--r--db/docs/merge_request_diff_details.yml1
-rw-r--r--db/docs/merge_request_diff_files.yml1
-rw-r--r--db/docs/merge_request_diffs.yml3
-rw-r--r--db/docs/merge_request_metrics.yml1
-rw-r--r--db/docs/merge_request_predictions.yml1
-rw-r--r--db/docs/merge_request_reviewers.yml1
-rw-r--r--db/docs/merge_request_user_mentions.yml1
-rw-r--r--db/docs/merge_requests.yml1
-rw-r--r--db/docs/merge_requests_closing_issues.yml1
-rw-r--r--db/docs/merge_requests_compliance_violations.yml1
-rw-r--r--db/docs/merge_trains.yml1
-rw-r--r--db/docs/metrics_dashboard_annotations.yml1
-rw-r--r--db/docs/metrics_users_starred_dashboards.yml1
-rw-r--r--db/docs/milestone_releases.yml1
-rw-r--r--db/docs/milestones.yml1
-rw-r--r--db/docs/ml_candidate_metadata.yml11
-rw-r--r--db/docs/ml_candidate_metrics.yml1
-rw-r--r--db/docs/ml_candidate_params.yml1
-rw-r--r--db/docs/ml_candidates.yml1
-rw-r--r--db/docs/ml_experiment_metadata.yml11
-rw-r--r--db/docs/ml_experiments.yml1
-rw-r--r--db/docs/namespace_admin_notes.yml1
-rw-r--r--db/docs/namespace_aggregation_schedules.yml1
-rw-r--r--db/docs/namespace_bans.yml1
-rw-r--r--db/docs/namespace_ci_cd_settings.yml1
-rw-r--r--db/docs/namespace_commit_emails.yml1
-rw-r--r--db/docs/namespace_details.yml1
-rw-r--r--db/docs/namespace_limits.yml1
-rw-r--r--db/docs/namespace_package_settings.yml1
-rw-r--r--db/docs/namespace_root_storage_statistics.yml1
-rw-r--r--db/docs/namespace_settings.yml1
-rw-r--r--db/docs/namespace_statistics.yml1
-rw-r--r--db/docs/namespaces.yml1
-rw-r--r--db/docs/namespaces_sync_events.yml1
-rw-r--r--db/docs/note_diff_files.yml1
-rw-r--r--db/docs/notes.yml1
-rw-r--r--db/docs/notification_settings.yml1
-rw-r--r--db/docs/oauth_access_grants.yml1
-rw-r--r--db/docs/oauth_access_tokens.yml1
-rw-r--r--db/docs/oauth_applications.yml1
-rw-r--r--db/docs/oauth_openid_requests.yml1
-rw-r--r--db/docs/onboarding_progresses.yml1
-rw-r--r--db/docs/operations_feature_flag_scopes.yml1
-rw-r--r--db/docs/operations_feature_flags.yml1
-rw-r--r--db/docs/operations_feature_flags_clients.yml1
-rw-r--r--db/docs/operations_feature_flags_issues.yml1
-rw-r--r--db/docs/operations_scopes.yml1
-rw-r--r--db/docs/operations_strategies.yml1
-rw-r--r--db/docs/operations_strategies_user_lists.yml1
-rw-r--r--db/docs/operations_user_lists.yml1
-rw-r--r--db/docs/p_ci_builds_metadata.yml1
-rw-r--r--db/docs/packages_build_infos.yml1
-rw-r--r--db/docs/packages_cleanup_policies.yml1
-rw-r--r--db/docs/packages_composer_cache_files.yml1
-rw-r--r--db/docs/packages_composer_metadata.yml1
-rw-r--r--db/docs/packages_conan_file_metadata.yml1
-rw-r--r--db/docs/packages_conan_metadata.yml1
-rw-r--r--db/docs/packages_debian_file_metadata.yml1
-rw-r--r--db/docs/packages_debian_group_architectures.yml1
-rw-r--r--db/docs/packages_debian_group_component_files.yml1
-rw-r--r--db/docs/packages_debian_group_components.yml1
-rw-r--r--db/docs/packages_debian_group_distribution_keys.yml1
-rw-r--r--db/docs/packages_debian_group_distributions.yml1
-rw-r--r--db/docs/packages_debian_project_architectures.yml1
-rw-r--r--db/docs/packages_debian_project_component_files.yml1
-rw-r--r--db/docs/packages_debian_project_components.yml1
-rw-r--r--db/docs/packages_debian_project_distribution_keys.yml1
-rw-r--r--db/docs/packages_debian_project_distributions.yml1
-rw-r--r--db/docs/packages_debian_publications.yml1
-rw-r--r--db/docs/packages_dependencies.yml1
-rw-r--r--db/docs/packages_dependency_links.yml1
-rw-r--r--db/docs/packages_events.yml1
-rw-r--r--db/docs/packages_helm_file_metadata.yml1
-rw-r--r--db/docs/packages_maven_metadata.yml1
-rw-r--r--db/docs/packages_npm_metadata.yml1
-rw-r--r--db/docs/packages_nuget_dependency_link_metadata.yml1
-rw-r--r--db/docs/packages_nuget_metadata.yml1
-rw-r--r--db/docs/packages_package_file_build_infos.yml1
-rw-r--r--db/docs/packages_package_files.yml1
-rw-r--r--db/docs/packages_packages.yml1
-rw-r--r--db/docs/packages_pypi_metadata.yml1
-rw-r--r--db/docs/packages_rpm_metadata.yml1
-rw-r--r--db/docs/packages_rpm_repository_files.yml1
-rw-r--r--db/docs/packages_rubygems_metadata.yml1
-rw-r--r--db/docs/packages_tags.yml1
-rw-r--r--db/docs/pages_deployment_states.yml1
-rw-r--r--db/docs/pages_deployments.yml1
-rw-r--r--db/docs/pages_domain_acme_orders.yml1
-rw-r--r--db/docs/pages_domains.yml1
-rw-r--r--db/docs/path_locks.yml1
-rw-r--r--db/docs/personal_access_tokens.yml1
-rw-r--r--db/docs/plan_limits.yml1
-rw-r--r--db/docs/plans.yml1
-rw-r--r--db/docs/pm_licenses.yml10
-rw-r--r--db/docs/pm_package_version_licenses.yml10
-rw-r--r--db/docs/pm_package_versions.yml10
-rw-r--r--db/docs/pm_packages.yml10
-rw-r--r--db/docs/pool_repositories.yml1
-rw-r--r--db/docs/postgres_async_indexes.yml1
-rw-r--r--db/docs/postgres_reindex_actions.yml1
-rw-r--r--db/docs/postgres_reindex_queued_actions.yml1
-rw-r--r--db/docs/product_analytics_events_experimental.yml1
-rw-r--r--db/docs/programming_languages.yml1
-rw-r--r--db/docs/project_access_tokens.yml1
-rw-r--r--db/docs/project_alerting_settings.yml1
-rw-r--r--db/docs/project_aliases.yml1
-rw-r--r--db/docs/project_authorizations.yml1
-rw-r--r--db/docs/project_auto_devops.yml1
-rw-r--r--db/docs/project_build_artifacts_size_refreshes.yml1
-rw-r--r--db/docs/project_ci_cd_settings.yml1
-rw-r--r--db/docs/project_ci_feature_usages.yml1
-rw-r--r--db/docs/project_compliance_framework_settings.yml1
-rw-r--r--db/docs/project_custom_attributes.yml1
-rw-r--r--db/docs/project_daily_statistics.yml1
-rw-r--r--db/docs/project_deploy_tokens.yml1
-rw-r--r--db/docs/project_error_tracking_settings.yml1
-rw-r--r--db/docs/project_export_jobs.yml1
-rw-r--r--db/docs/project_feature_usages.yml1
-rw-r--r--db/docs/project_features.yml1
-rw-r--r--db/docs/project_group_links.yml1
-rw-r--r--db/docs/project_import_data.yml1
-rw-r--r--db/docs/project_incident_management_settings.yml1
-rw-r--r--db/docs/project_metrics_settings.yml1
-rw-r--r--db/docs/project_mirror_data.yml1
-rw-r--r--db/docs/project_pages_metadata.yml1
-rw-r--r--db/docs/project_relation_export_uploads.yml1
-rw-r--r--db/docs/project_relation_exports.yml1
-rw-r--r--db/docs/project_repositories.yml1
-rw-r--r--db/docs/project_repository_states.yml1
-rw-r--r--db/docs/project_repository_storage_moves.yml1
-rw-r--r--db/docs/project_security_settings.yml1
-rw-r--r--db/docs/project_settings.yml1
-rw-r--r--db/docs/project_statistics.yml1
-rw-r--r--db/docs/project_topics.yml1
-rw-r--r--db/docs/project_wiki_repositories.yml1
-rw-r--r--db/docs/project_wiki_repository_states.yml1
-rw-r--r--db/docs/projects.yml1
-rw-r--r--db/docs/projects_sync_events.yml1
-rw-r--r--db/docs/prometheus_alert_events.yml1
-rw-r--r--db/docs/prometheus_alerts.yml1
-rw-r--r--db/docs/prometheus_metrics.yml1
-rw-r--r--db/docs/protected_branch_merge_access_levels.yml1
-rw-r--r--db/docs/protected_branch_push_access_levels.yml1
-rw-r--r--db/docs/protected_branch_unprotect_access_levels.yml1
-rw-r--r--db/docs/protected_branches.yml1
-rw-r--r--db/docs/protected_environment_approval_rules.yml5
-rw-r--r--db/docs/protected_environment_deploy_access_levels.yml5
-rw-r--r--db/docs/protected_environments.yml5
-rw-r--r--db/docs/protected_tag_create_access_levels.yml1
-rw-r--r--db/docs/protected_tags.yml1
-rw-r--r--db/docs/push_event_payloads.yml1
-rw-r--r--db/docs/push_rules.yml1
-rw-r--r--db/docs/raw_usage_data.yml1
-rw-r--r--db/docs/redirect_routes.yml1
-rw-r--r--db/docs/related_epic_links.yml1
-rw-r--r--db/docs/release_links.yml1
-rw-r--r--db/docs/releases.yml1
-rw-r--r--db/docs/remote_mirrors.yml1
-rw-r--r--db/docs/repository_languages.yml1
-rw-r--r--db/docs/required_code_owners_sections.yml1
-rw-r--r--db/docs/requirements.yml1
-rw-r--r--db/docs/requirements_management_test_reports.yml1
-rw-r--r--db/docs/resource_iteration_events.yml1
-rw-r--r--db/docs/resource_label_events.yml1
-rw-r--r--db/docs/resource_milestone_events.yml1
-rw-r--r--db/docs/resource_state_events.yml1
-rw-r--r--db/docs/resource_weight_events.yml1
-rw-r--r--db/docs/reviews.yml1
-rw-r--r--db/docs/routes.yml1
-rw-r--r--db/docs/saml_group_links.yml1
-rw-r--r--db/docs/saml_providers.yml1
-rw-r--r--db/docs/saved_replies.yml1
-rw-r--r--db/docs/sbom_component_versions.yml1
-rw-r--r--db/docs/sbom_components.yml1
-rw-r--r--db/docs/sbom_occurrences.yml1
-rw-r--r--db/docs/sbom_sources.yml1
-rw-r--r--db/docs/sbom_vulnerable_component_versions.yml1
-rw-r--r--db/docs/schema_migrations.yml1
-rw-r--r--db/docs/scim_identities.yml1
-rw-r--r--db/docs/scim_oauth_access_tokens.yml1
-rw-r--r--db/docs/security_findings.yml1
-rw-r--r--db/docs/security_orchestration_policy_configurations.yml1
-rw-r--r--db/docs/security_orchestration_policy_rule_schedules.yml1
-rw-r--r--db/docs/security_scans.yml1
-rw-r--r--db/docs/security_training_providers.yml1
-rw-r--r--db/docs/security_trainings.yml1
-rw-r--r--db/docs/self_managed_prometheus_alert_events.yml1
-rw-r--r--db/docs/sent_notifications.yml1
-rw-r--r--db/docs/sentry_issues.yml1
-rw-r--r--db/docs/serverless_domain_cluster.yml3
-rw-r--r--db/docs/service_desk_settings.yml1
-rw-r--r--db/docs/shards.yml1
-rw-r--r--db/docs/slack_api_scopes.yml10
-rw-r--r--db/docs/slack_integrations.yml1
-rw-r--r--db/docs/slack_integrations_scopes.yml10
-rw-r--r--db/docs/smartcard_identities.yml1
-rw-r--r--db/docs/snippet_repositories.yml1
-rw-r--r--db/docs/snippet_repository_storage_moves.yml1
-rw-r--r--db/docs/snippet_statistics.yml1
-rw-r--r--db/docs/snippet_user_mentions.yml1
-rw-r--r--db/docs/snippets.yml1
-rw-r--r--db/docs/software_license_policies.yml1
-rw-r--r--db/docs/software_licenses.yml1
-rw-r--r--db/docs/spam_logs.yml1
-rw-r--r--db/docs/sprints.yml1
-rw-r--r--db/docs/ssh_signatures.yml1
-rw-r--r--db/docs/status_check_responses.yml1
-rw-r--r--db/docs/status_page_published_incidents.yml1
-rw-r--r--db/docs/status_page_settings.yml1
-rw-r--r--db/docs/subscriptions.yml1
-rw-r--r--db/docs/suggestions.yml1
-rw-r--r--db/docs/system_note_metadata.yml1
-rw-r--r--db/docs/taggings.yml3
-rw-r--r--db/docs/tags.yml1
-rw-r--r--db/docs/term_agreements.yml1
-rw-r--r--db/docs/terraform_state_versions.yml1
-rw-r--r--db/docs/terraform_states.yml1
-rw-r--r--db/docs/timelog_categories.yml1
-rw-r--r--db/docs/timelogs.yml1
-rw-r--r--db/docs/todos.yml1
-rw-r--r--db/docs/token_with_ivs.yml1
-rw-r--r--db/docs/topics.yml1
-rw-r--r--db/docs/trending_projects.yml1
-rw-r--r--db/docs/u2f_registrations.yml1
-rw-r--r--db/docs/upcoming_reconciliations.yml1
-rw-r--r--db/docs/upload_states.yml1
-rw-r--r--db/docs/uploads.yml1
-rw-r--r--db/docs/user_agent_details.yml1
-rw-r--r--db/docs/user_callouts.yml1
-rw-r--r--db/docs/user_canonical_emails.yml1
-rw-r--r--db/docs/user_credit_card_validations.yml1
-rw-r--r--db/docs/user_custom_attributes.yml1
-rw-r--r--db/docs/user_details.yml1
-rw-r--r--db/docs/user_follow_users.yml1
-rw-r--r--db/docs/user_group_callouts.yml1
-rw-r--r--db/docs/user_highest_roles.yml1
-rw-r--r--db/docs/user_interacted_projects.yml1
-rw-r--r--db/docs/user_namespace_callouts.yml2
-rw-r--r--db/docs/user_permission_export_uploads.yml1
-rw-r--r--db/docs/user_phone_number_validations.yml1
-rw-r--r--db/docs/user_preferences.yml1
-rw-r--r--db/docs/user_project_callouts.yml1
-rw-r--r--db/docs/user_statuses.yml1
-rw-r--r--db/docs/user_synced_attributes_metadata.yml1
-rw-r--r--db/docs/users.yml1
-rw-r--r--db/docs/users_ops_dashboard_projects.yml1
-rw-r--r--db/docs/users_security_dashboard_projects.yml1
-rw-r--r--db/docs/users_star_projects.yml1
-rw-r--r--db/docs/users_statistics.yml1
-rw-r--r--db/docs/verification_codes.yml1
-rw-r--r--db/docs/views/postgres_autovacuum_activity.yml10
-rw-r--r--db/docs/views/postgres_constraints.yml10
-rw-r--r--db/docs/views/postgres_foreign_keys.yml10
-rw-r--r--db/docs/views/postgres_index_bloat_estimates.yml10
-rw-r--r--db/docs/views/postgres_indexes.yml10
-rw-r--r--db/docs/views/postgres_partitioned_tables.yml10
-rw-r--r--db/docs/views/postgres_partitions.yml10
-rw-r--r--db/docs/vulnerabilities.yml1
-rw-r--r--db/docs/vulnerability_advisories.yml1
-rw-r--r--db/docs/vulnerability_exports.yml1
-rw-r--r--db/docs/vulnerability_external_issue_links.yml1
-rw-r--r--db/docs/vulnerability_feedback.yml1
-rw-r--r--db/docs/vulnerability_finding_evidences.yml1
-rw-r--r--db/docs/vulnerability_finding_links.yml1
-rw-r--r--db/docs/vulnerability_finding_signatures.yml1
-rw-r--r--db/docs/vulnerability_findings_remediations.yml1
-rw-r--r--db/docs/vulnerability_flags.yml3
-rw-r--r--db/docs/vulnerability_historical_statistics.yml1
-rw-r--r--db/docs/vulnerability_identifiers.yml1
-rw-r--r--db/docs/vulnerability_issue_links.yml1
-rw-r--r--db/docs/vulnerability_merge_request_links.yml1
-rw-r--r--db/docs/vulnerability_occurrence_identifiers.yml1
-rw-r--r--db/docs/vulnerability_occurrence_pipelines.yml1
-rw-r--r--db/docs/vulnerability_occurrences.yml1
-rw-r--r--db/docs/vulnerability_reads.yml1
-rw-r--r--db/docs/vulnerability_remediations.yml1
-rw-r--r--db/docs/vulnerability_scanners.yml1
-rw-r--r--db/docs/vulnerability_state_transitions.yml1
-rw-r--r--db/docs/vulnerability_statistics.yml1
-rw-r--r--db/docs/vulnerability_user_mentions.yml1
-rw-r--r--db/docs/web_hook_logs.yml1
-rw-r--r--db/docs/web_hooks.yml1
-rw-r--r--db/docs/webauthn_registrations.yml1
-rw-r--r--db/docs/wiki_page_meta.yml1
-rw-r--r--db/docs/wiki_page_slugs.yml1
-rw-r--r--db/docs/work_item_hierarchy_restrictions.yml10
-rw-r--r--db/docs/work_item_parent_links.yml1
-rw-r--r--db/docs/work_item_progresses.yml10
-rw-r--r--db/docs/work_item_types.yml1
-rw-r--r--db/docs/x509_certificates.yml1
-rw-r--r--db/docs/x509_commit_signatures.yml1
-rw-r--r--db/docs/x509_issuers.yml1
-rw-r--r--db/docs/zentao_tracker_data.yml1
-rw-r--r--db/docs/zoom_meetings.yml1
-rw-r--r--db/fixtures/development/14_pipelines.rb147
-rw-r--r--db/fixtures/development/50_create_work_item_hierarchy_restrictions.rb5
-rw-r--r--db/fixtures/production/020_create_work_item_hierarchy_restrictions.rb5
-rw-r--r--db/migrate/20220824082427_remove_tmp_idx_vulnerability_occurrences_on_id_and_scanner_id.rb20
-rw-r--r--db/migrate/20220908150054_add_runner_registration_enabled_to_namespace_settings.rb9
-rw-r--r--db/migrate/20221026095133_add_status_updated_at_to_container_repository.rb7
-rw-r--r--db/migrate/20221101174816_create_package_metadata.rb11
-rw-r--r--db/migrate/20221101194416_create_package_metadata_versions.rb11
-rw-r--r--db/migrate/20221101195309_create_package_metadata_licenses.rb10
-rw-r--r--db/migrate/20221101195543_create_package_metadata_package_version_licenses.rb12
-rw-r--r--db/migrate/20221102150737_index_environments_for_name_search_within_folder.rb16
-rw-r--r--db/migrate/20221102195642_create_dependency_proxy_manifest_states.rb45
-rw-r--r--db/migrate/20221103205317_create_dast_pre_scan_verification.rb22
-rw-r--r--db/migrate/20221107013943_add_accepted_reviewers_to_merge_request_predictions.rb9
-rw-r--r--db/migrate/20221110080508_add_partition_id_to_ci_unit_test_failures.rb9
-rw-r--r--db/migrate/20221110080636_add_partition_id_to_ci_sources_pipelines.rb9
-rw-r--r--db/migrate/20221110080748_add_partition_id_to_ci_build_pending_states.rb9
-rw-r--r--db/migrate/20221110080822_add_partition_id_to_ci_build_trace_chunks.rb9
-rw-r--r--db/migrate/20221110080913_add_partition_id_to_ci_build_report_results.rb9
-rw-r--r--db/migrate/20221110080956_add_partition_id_to_ci_build_needs.rb9
-rw-r--r--db/migrate/20221110081037_add_partition_id_to_ci_builds_runner_session.rb9
-rw-r--r--db/migrate/20221110081115_add_partition_id_to_ci_pending_builds.rb9
-rw-r--r--db/migrate/20221110081207_add_partition_id_to_ci_build_trace_metadata.rb9
-rw-r--r--db/migrate/20221110081348_add_partition_id_to_ci_running_builds.rb9
-rw-r--r--db/migrate/20221110081448_add_partition_id_to_ci_job_variables.rb9
-rw-r--r--db/migrate/20221110183103_add_dashboard_fields_to_namespace_details.rb10
-rw-r--r--db/migrate/20221111123146_add_onboarding_in_progress_to_users.rb15
-rw-r--r--db/migrate/20221111123147_add_onboarding_step_url_to_user_details.rb16
-rw-r--r--db/migrate/20221111123148_add_text_limit_to_onboarding_step_url.rb13
-rw-r--r--db/migrate/20221111135238_create_dependency_list_exports_table.rb23
-rw-r--r--db/migrate/20221111142921_add_hierarchy_restrictions.rb21
-rw-r--r--db/migrate/20221114131943_add_short_title_to_appearances.rb10
-rw-r--r--db/migrate/20221114145103_add_last_seat_refresh_at_to_gitlab_subscriptions.rb16
-rw-r--r--db/migrate/20221114212908_add_debug_trace_to_ci_builds_metadata.rb9
-rw-r--r--db/migrate/20221115085813_add_limit_to_appereances_short_title.rb13
-rw-r--r--db/migrate/20221116100056_add_foreign_key_to_dependency_list_exports.rb27
-rw-r--r--db/migrate/20221116113323_add_index_on_team_id_and_chat_id.rb15
-rw-r--r--db/migrate/20221116124821_add_enterprise_boolean_to_bulk_imports.rb7
-rw-r--r--db/migrate/20221116160204_create_ml_experiment_metadata_and_ml_candidate_metadata.rb29
-rw-r--r--db/migrate/20221116161126_add_auth_signing_type_to_keys.rb7
-rw-r--r--db/migrate/20221121091238_add_work_item_progress.rb18
-rw-r--r--db/migrate/20221121100431_add_partition_id_to_ci_resources.rb9
-rw-r--r--db/migrate/20221122141046_add_allow_pipeline_trigger_approve_deployment_to_project_settings.rb9
-rw-r--r--db/migrate/20221122225925_set_email_confirmation_setting_before_removing_send_user_confirmation_email_column.rb22
-rw-r--r--db/migrate/20221124113925_add_pipeline_hierarchy_size_to_plan_limits.rb7
-rw-r--r--db/migrate/20221128123514_add_source_partition_id_to_ci_sources_pipeline.rb9
-rw-r--r--db/migrate/20221129192619_increase_self_hosted_attachment_size_limit.rb13
-rw-r--r--db/migrate/20221130170433_create_dast_pre_scan_verification_step.rb18
-rw-r--r--db/migrate/20221130182056_add_plan_limits_max_size_to_requirements_v2_artifact.rb7
-rw-r--r--db/migrate/20221202144210_create_achievements.rb24
-rw-r--r--db/migrate/20221202202351_remove_index_i_ci_job_token_project_scope_links_on_source_and_target_project.rb23
-rw-r--r--db/migrate/20221205061134_add_disable_pats_to_application_settings.rb7
-rw-r--r--db/migrate/20221206163420_add_use_new_navigation_to_user_preferences.rb9
-rw-r--r--db/migrate/20221206211814_add_authorized_scopes_to_slack_integration.rb38
-rw-r--r--db/migrate/20221206222032_add_read_code_to_member_roles.rb7
-rw-r--r--db/migrate/20221206235208_add_max_terraform_state_size_bytes_to_application_settings.rb24
-rw-r--r--db/migrate/20221207140259_add_bulk_import_enabled_to_application_settings.rb7
-rw-r--r--db/migrate/20221207220120_create_dast_scanner_profiles_runner_tags.rb18
-rw-r--r--db/migrate/20221208122921_remove_constraints_from_ci_resources_for_partition_id.rb16
-rw-r--r--db/migrate/20221209110934_update_import_sources_on_application_settings.rb28
-rw-r--r--db/migrate/20221209110935_fix_update_import_sources_on_application_settings.rb25
-rw-r--r--db/migrate/20221213184314_change_enabled_default_in_dependency_proxy_group_settings.rb7
-rw-r--r--db/post_migrate/20210731132939_backfill_stage_event_hash.rb2
-rw-r--r--db/post_migrate/20220202105733_delete_service_template_records.rb1
-rw-r--r--db/post_migrate/20220420214703_schedule_backfill_draft_status_on_merge_requests_corrected_regex.rb2
-rw-r--r--db/post_migrate/20220920180451_schedule_vulnerabilities_feedback_migration.rb36
-rw-r--r--db/post_migrate/20221018095434_schedule_disable_legacy_open_source_license_for_projects_less_than_five_mb.rb33
-rw-r--r--db/post_migrate/20221104115712_backfill_project_statistics_storage_size_without_uploads_size.rb31
-rw-r--r--db/post_migrate/20221104141647_add_index_for_non_public_top_level_groups_to_namespaces.rb17
-rw-r--r--db/post_migrate/20221109160052_add_default_for_approval_project_rules_scanners.rb11
-rw-r--r--db/post_migrate/20221110152133_delete_orphans_approval_rules.rb36
-rw-r--r--db/post_migrate/20221110190340_add_partial_legacy_open_source_license_available_project_id_index.rb18
-rw-r--r--db/post_migrate/20221114142044_delete_experiments_foreign_keys.rb34
-rw-r--r--db/post_migrate/20221114142602_drop_experiment_subjects_table.rb32
-rw-r--r--db/post_migrate/20221114142616_drop_experiments_table.rb21
-rw-r--r--db/post_migrate/20221115120602_add_index_for_issues_health_status_ordering.rb23
-rw-r--r--db/post_migrate/20221115173607_ensure_work_item_type_backfill_migration_finished.rb42
-rw-r--r--db/post_migrate/20221115184525_remove_namespaces_tmp_project_id_column.rb27
-rw-r--r--db/post_migrate/20221116105434_remove_index_project_settings_on_legacy_open_source_license_available.rb18
-rw-r--r--db/post_migrate/20221116143854_add_okr_hierarchy_restrictions.rb48
-rw-r--r--db/post_migrate/20221117103015_add_async_index_author_id_created_at_on_merge_requests.rb13
-rw-r--r--db/post_migrate/20221117135032_remove_clusters_applications_job_instances.rb26
-rw-r--r--db/post_migrate/20221117153015_add_index_merge_request_id_created_at_on_scan_finding_approval_merge_request_rules.rb17
-rw-r--r--db/post_migrate/20221118103152_finalize_issues_namespace_id_backfilling.rb22
-rw-r--r--db/post_migrate/20221118103352_add_cascade_delete_fk_on_issues_namespace_id.rb41
-rw-r--r--db/post_migrate/20221118103752_add_not_null_contraint_to_issues_namespace_id.rb13
-rw-r--r--db/post_migrate/20221118104752_validate_not_null_contraint_to_issues_namespace_id.rb13
-rw-r--r--db/post_migrate/20221121000127_index_security_scans_on_created_at_and_id_for_non_purged_records.rb16
-rw-r--r--db/post_migrate/20221121000451_drop_index_security_scans_on_id_for_non_purged_records.rb16
-rw-r--r--db/post_migrate/20221121152048_remove_unused_feedback_migration_index.rb24
-rw-r--r--db/post_migrate/20221121152515_add_supporting_index_for_vulnerabilities_feedback_migration2.rb24
-rw-r--r--db/post_migrate/20221121155850_change_vulnerabilities_state_transitions_comment_limit.rb23
-rw-r--r--db/post_migrate/20221121180138_drop_index_on_vulnerabilities_state_case_id.rb21
-rw-r--r--db/post_migrate/20221121181627_drop_index_on_vulnerabilities_state_case_id_desc.rb20
-rw-r--r--db/post_migrate/20221121184931_validate_not_null_contraint_on_issues_work_item_type_id.rb13
-rw-r--r--db/post_migrate/20221122063922_remove_issue_title_trigram_index.rb20
-rw-r--r--db/post_migrate/20221122064537_remove_issue_description_trigram_index.rb20
-rw-r--r--db/post_migrate/20221122132812_schedule_prune_stale_project_export_jobs.rb22
-rw-r--r--db/post_migrate/20221122155149_add_index_for_paths_on_non_projects.rb16
-rw-r--r--db/post_migrate/20221123133054_queue_reset_status_on_container_repositories.rb25
-rw-r--r--db/post_migrate/20221124153602_add_supporting_index_for_vulnerabilities_feedback_comment_proccessing.rb24
-rw-r--r--db/post_migrate/20221125222221_add_metrics_index_to_authentication_events.rb17
-rw-r--r--db/post_migrate/20221125222341_remove_result_index_from_authentication_events.rb18
-rw-r--r--db/post_migrate/20221128120634_schedule_fixing_security_scan_statuses.rb52
-rw-r--r--db/post_migrate/20221128220043_drop_temp_work_item_type_id_backfill_index.rb15
-rw-r--r--db/post_migrate/20221128222417_add_back_issues_work_item_type_id_index.rb15
-rw-r--r--db/post_migrate/20221129124240_remove_flowdock_integration_records.rb23
-rw-r--r--db/post_migrate/20221130192239_fix_approval_project_rules_without_protected_branches.rb27
-rw-r--r--db/post_migrate/20221202031332_add_index_to_issue_assignees_on_user_id_and_issue_id.rb15
-rw-r--r--db/post_migrate/20221202031417_remove_index_to_issue_assignees_on_user_id.rb15
-rw-r--r--db/post_migrate/20221202154128_add_pipeline_metadata_name_index.rb15
-rw-r--r--db/post_migrate/20221202154151_remove_pipeline_metadata_pipeline_id_index.rb15
-rw-r--r--db/post_migrate/20221205134448_set_index_for_issues_health_status_ordering.rb25
-rw-r--r--db/post_migrate/20221205151917_schedule_backfill_environment_tier.rb21
-rw-r--r--db/post_migrate/20221205170310_add_index_for_active_members.rb18
-rw-r--r--db/post_migrate/20221206012013_add_index_author_id_created_at_on_merge_requests.rb15
-rw-r--r--db/post_migrate/20221206075631_add_unique_id_partition_id_index_to_ci_build.rb15
-rw-r--r--db/post_migrate/20221206132610_add_unique_token_encrypted_partition_id_index_to_ci_build.rb21
-rw-r--r--db/post_migrate/20221206173132_add_issues_work_item_type_id_index.rb15
-rw-r--r--db/post_migrate/20221210154044_update_active_billable_users_index.rb29
-rw-r--r--db/post_migrate/20221212103743_add_index_id_partition_id_to_ci_build.rb17
-rw-r--r--db/post_migrate/20221213064717_change_default_partition_id_on_ci_resources.rb9
-rw-r--r--db/schema_migrations/202208240824271
-rw-r--r--db/schema_migrations/202209081500541
-rw-r--r--db/schema_migrations/202209201804511
-rw-r--r--db/schema_migrations/202210180954341
-rw-r--r--db/schema_migrations/202210260951331
-rw-r--r--db/schema_migrations/202211011748161
-rw-r--r--db/schema_migrations/202211011944161
-rw-r--r--db/schema_migrations/202211011953091
-rw-r--r--db/schema_migrations/202211011955431
-rw-r--r--db/schema_migrations/202211021507371
-rw-r--r--db/schema_migrations/202211021956421
-rw-r--r--db/schema_migrations/202211032053171
-rw-r--r--db/schema_migrations/202211041157121
-rw-r--r--db/schema_migrations/202211041416471
-rw-r--r--db/schema_migrations/202211070139431
-rw-r--r--db/schema_migrations/202211091600521
-rw-r--r--db/schema_migrations/202211100805081
-rw-r--r--db/schema_migrations/202211100806361
-rw-r--r--db/schema_migrations/202211100807481
-rw-r--r--db/schema_migrations/202211100808221
-rw-r--r--db/schema_migrations/202211100809131
-rw-r--r--db/schema_migrations/202211100809561
-rw-r--r--db/schema_migrations/202211100810371
-rw-r--r--db/schema_migrations/202211100811151
-rw-r--r--db/schema_migrations/202211100812071
-rw-r--r--db/schema_migrations/202211100813481
-rw-r--r--db/schema_migrations/202211100814481
-rw-r--r--db/schema_migrations/202211101521331
-rw-r--r--db/schema_migrations/202211101831031
-rw-r--r--db/schema_migrations/202211101903401
-rw-r--r--db/schema_migrations/202211111231461
-rw-r--r--db/schema_migrations/202211111231471
-rw-r--r--db/schema_migrations/202211111231481
-rw-r--r--db/schema_migrations/202211111352381
-rw-r--r--db/schema_migrations/202211111429211
-rw-r--r--db/schema_migrations/202211141319431
-rw-r--r--db/schema_migrations/202211141420441
-rw-r--r--db/schema_migrations/202211141426021
-rw-r--r--db/schema_migrations/202211141426161
-rw-r--r--db/schema_migrations/202211141451031
-rw-r--r--db/schema_migrations/202211142129081
-rw-r--r--db/schema_migrations/202211150858131
-rw-r--r--db/schema_migrations/202211151206021
-rw-r--r--db/schema_migrations/202211151736071
-rw-r--r--db/schema_migrations/202211151845251
-rw-r--r--db/schema_migrations/202211161000561
-rw-r--r--db/schema_migrations/202211161054341
-rw-r--r--db/schema_migrations/202211161133231
-rw-r--r--db/schema_migrations/202211161248211
-rw-r--r--db/schema_migrations/202211161438541
-rw-r--r--db/schema_migrations/202211161602041
-rw-r--r--db/schema_migrations/202211161611261
-rw-r--r--db/schema_migrations/202211171030151
-rw-r--r--db/schema_migrations/202211171350321
-rw-r--r--db/schema_migrations/202211171530151
-rw-r--r--db/schema_migrations/202211181031521
-rw-r--r--db/schema_migrations/202211181033521
-rw-r--r--db/schema_migrations/202211181037521
-rw-r--r--db/schema_migrations/202211181047521
-rw-r--r--db/schema_migrations/202211210001271
-rw-r--r--db/schema_migrations/202211210004511
-rw-r--r--db/schema_migrations/202211210912381
-rw-r--r--db/schema_migrations/202211211004311
-rw-r--r--db/schema_migrations/202211211520481
-rw-r--r--db/schema_migrations/202211211525151
-rw-r--r--db/schema_migrations/202211211558501
-rw-r--r--db/schema_migrations/202211211801381
-rw-r--r--db/schema_migrations/202211211816271
-rw-r--r--db/schema_migrations/202211211849311
-rw-r--r--db/schema_migrations/202211220639221
-rw-r--r--db/schema_migrations/202211220645371
-rw-r--r--db/schema_migrations/202211221328121
-rw-r--r--db/schema_migrations/202211221410461
-rw-r--r--db/schema_migrations/202211221551491
-rw-r--r--db/schema_migrations/202211222259251
-rw-r--r--db/schema_migrations/202211231330541
-rw-r--r--db/schema_migrations/202211241139251
-rw-r--r--db/schema_migrations/202211241536021
-rw-r--r--db/schema_migrations/202211252222211
-rw-r--r--db/schema_migrations/202211252223411
-rw-r--r--db/schema_migrations/202211281206341
-rw-r--r--db/schema_migrations/202211281235141
-rw-r--r--db/schema_migrations/202211282200431
-rw-r--r--db/schema_migrations/202211282224171
-rw-r--r--db/schema_migrations/202211291242401
-rw-r--r--db/schema_migrations/202211291926191
-rw-r--r--db/schema_migrations/202211301704331
-rw-r--r--db/schema_migrations/202211301820561
-rw-r--r--db/schema_migrations/202211301922391
-rw-r--r--db/schema_migrations/202212020313321
-rw-r--r--db/schema_migrations/202212020314171
-rw-r--r--db/schema_migrations/202212021442101
-rw-r--r--db/schema_migrations/202212021541281
-rw-r--r--db/schema_migrations/202212021541511
-rw-r--r--db/schema_migrations/202212022023511
-rw-r--r--db/schema_migrations/202212050611341
-rw-r--r--db/schema_migrations/202212051344481
-rw-r--r--db/schema_migrations/202212051519171
-rw-r--r--db/schema_migrations/202212051703101
-rw-r--r--db/schema_migrations/202212060120131
-rw-r--r--db/schema_migrations/202212060756311
-rw-r--r--db/schema_migrations/202212061326101
-rw-r--r--db/schema_migrations/202212061634201
-rw-r--r--db/schema_migrations/202212061731321
-rw-r--r--db/schema_migrations/202212062118141
-rw-r--r--db/schema_migrations/202212062220321
-rw-r--r--db/schema_migrations/202212062352081
-rw-r--r--db/schema_migrations/202212071402591
-rw-r--r--db/schema_migrations/202212072201201
-rw-r--r--db/schema_migrations/202212081229211
-rw-r--r--db/schema_migrations/202212091109341
-rw-r--r--db/schema_migrations/202212091109351
-rw-r--r--db/schema_migrations/202212101540441
-rw-r--r--db/schema_migrations/202212121037431
-rw-r--r--db/schema_migrations/202212130647171
-rw-r--r--db/schema_migrations/202212131843141
-rw-r--r--db/structure.sql663
-rw-r--r--doc/.vale/gitlab/CurrentStatus.yml5
-rw-r--r--doc/.vale/gitlab/ElementDescriptors.yml17
-rw-r--r--doc/.vale/gitlab/InclusionAbleism.yml4
-rw-r--r--doc/.vale/gitlab/InclusionGender.yml4
-rw-r--r--doc/.vale/gitlab/Normal.yml14
-rw-r--r--doc/.vale/gitlab/Simplicity.yml4
-rw-r--r--doc/.vale/gitlab/SubstitutionSuggestions.yml29
-rw-r--r--doc/.vale/gitlab/SubstitutionWarning.yml27
-rw-r--r--doc/.vale/gitlab/Substitutions.yml3
-rw-r--r--doc/.vale/gitlab/Units.yml4
-rw-r--r--doc/.vale/gitlab/Uppercase.yml2
-rw-r--r--doc/.vale/gitlab/spelling-exceptions.txt309
-rw-r--r--doc/administration/application_settings_cache.md4
-rw-r--r--doc/administration/audit_event_streaming.md59
-rw-r--r--doc/administration/audit_events.md481
-rw-r--r--doc/administration/auth/ldap/index.md79
-rw-r--r--doc/administration/auth/ldap/ldap-troubleshooting.md60
-rw-r--r--doc/administration/auth/ldap/ldap_synchronization.md410
-rw-r--r--doc/administration/clusters/kas.md2
-rw-r--r--doc/administration/configure.md8
-rw-r--r--doc/administration/external_pipeline_validation.md3
-rw-r--r--doc/administration/feature_flags.md3
-rw-r--r--doc/administration/file_hooks.md4
-rw-r--r--doc/administration/geo/disaster_recovery/index.md6
-rw-r--r--doc/administration/geo/disaster_recovery/planned_failover.md2
-rw-r--r--doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md10
-rw-r--r--doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md2
-rw-r--r--doc/administration/geo/replication/configuration.md2
-rw-r--r--doc/administration/geo/replication/datatypes.md2
-rw-r--r--doc/administration/geo/replication/geo_validation_tests.md8
-rw-r--r--doc/administration/geo/replication/location_aware_git_url.md2
-rw-r--r--doc/administration/geo/replication/troubleshooting.md26
-rw-r--r--doc/administration/geo/replication/tuning.md2
-rw-r--r--doc/administration/geo/replication/version_specific_upgrades.md6
-rw-r--r--doc/administration/geo/secondary_proxy/index.md87
-rw-r--r--doc/administration/geo/setup/database.md152
-rw-r--r--doc/administration/gitaly/configure_gitaly.md62
-rw-r--r--doc/administration/gitaly/index.md56
-rw-r--r--doc/administration/gitaly/monitoring.md2
-rw-r--r--doc/administration/gitaly/praefect.md15
-rw-r--r--doc/administration/gitaly/reference.md6
-rw-r--r--doc/administration/gitaly/troubleshooting.md38
-rw-r--r--doc/administration/housekeeping.md27
-rw-r--r--doc/administration/img/audit_events_v14_5.pngbin33285 -> 0 bytes
-rw-r--r--doc/administration/img/impersonated_audit_events_v13_8.pngbin11908 -> 0 bytes
-rw-r--r--doc/administration/img/impersonated_audit_events_v15_7.pngbin0 -> 11909 bytes
-rw-r--r--doc/administration/inactive_project_deletion.md2
-rw-r--r--doc/administration/incoming_email.md2
-rw-r--r--doc/administration/index.md5
-rw-r--r--doc/administration/instance_limits.md10
-rw-r--r--doc/administration/integration/terminal.md6
-rw-r--r--doc/administration/issue_closing_pattern.md91
-rw-r--r--doc/administration/job_artifacts.md23
-rw-r--r--doc/administration/job_logs.md4
-rw-r--r--doc/administration/lfs/index.md86
-rw-r--r--doc/administration/logs/index.md67
-rw-r--r--doc/administration/maintenance_mode/index.md4
-rw-r--r--doc/administration/merge_request_diffs.md3
-rw-r--r--doc/administration/monitoring/gitlab_self_monitoring_project/index.md4
-rw-r--r--doc/administration/monitoring/performance/performance_bar.md4
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_exporter.md4
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md13
-rw-r--r--doc/administration/nfs.md49
-rw-r--r--doc/administration/object_storage.md20
-rw-r--r--doc/administration/operations/extra_sidekiq_processes.md11
-rw-r--r--doc/administration/operations/extra_sidekiq_routing.md11
-rw-r--r--doc/administration/operations/filesystem_benchmarking.md6
-rw-r--r--doc/administration/operations/index.md5
-rw-r--r--doc/administration/operations/puma.md10
-rw-r--r--doc/administration/operations/sidekiq_memory_killer.md11
-rw-r--r--doc/administration/package_information/index.md6
-rw-r--r--doc/administration/package_information/licensing.md3
-rw-r--r--doc/administration/packages/container_registry.md42
-rw-r--r--doc/administration/packages/dependency_proxy.md9
-rw-r--r--doc/administration/packages/index.md253
-rw-r--r--doc/administration/pages/index.md50
-rw-r--r--doc/administration/postgresql/database_load_balancing.md20
-rw-r--r--doc/administration/postgresql/multiple_databases.md141
-rw-r--r--doc/administration/postgresql/pgbouncer.md4
-rw-r--r--doc/administration/postgresql/replication_and_failover.md186
-rw-r--r--doc/administration/raketasks/geo.md2
-rw-r--r--doc/administration/raketasks/github_import.md2
-rw-r--r--doc/administration/raketasks/maintenance.md2
-rw-r--r--doc/administration/reference_architectures/10k_users.md66
-rw-r--r--doc/administration/reference_architectures/25k_users.md68
-rw-r--r--doc/administration/reference_architectures/2k_users.md52
-rw-r--r--doc/administration/reference_architectures/3k_users.md67
-rw-r--r--doc/administration/reference_architectures/50k_users.md70
-rw-r--r--doc/administration/reference_architectures/5k_users.md73
-rw-r--r--doc/administration/reference_architectures/index.md9
-rw-r--r--doc/administration/restart_gitlab.md16
-rw-r--r--doc/administration/server_hooks.md19
-rw-r--r--doc/administration/sidekiq.md11
-rw-r--r--doc/administration/sidekiq/extra_sidekiq_processes.md286
-rw-r--r--doc/administration/sidekiq/extra_sidekiq_routing.md166
-rw-r--r--doc/administration/sidekiq/index.md6
-rw-r--r--doc/administration/sidekiq/processing_specific_job_classes.md337
-rw-r--r--doc/administration/sidekiq/sidekiq_job_migration.md19
-rw-r--r--doc/administration/sidekiq/sidekiq_troubleshooting.md171
-rw-r--r--doc/administration/sidekiq_health_check.md11
-rw-r--r--doc/administration/snippets/index.md4
-rw-r--r--doc/administration/terraform_state.md4
-rw-r--r--doc/administration/troubleshooting/elasticsearch.md11
-rw-r--r--doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md98
-rw-r--r--doc/administration/troubleshooting/linux_cheat_sheet.md4
-rw-r--r--doc/administration/troubleshooting/postgresql.md12
-rw-r--r--doc/administration/troubleshooting/sidekiq.md11
-rw-r--r--doc/administration/troubleshooting/tracing_correlation_id.md11
-rw-r--r--doc/administration/uploads.md22
-rw-r--r--doc/administration/user_settings.md2
-rw-r--r--doc/api/alert_management_alerts.md10
-rw-r--r--doc/api/appearance.md3
-rw-r--r--doc/api/applications.md2
-rw-r--r--doc/api/bulk_imports.md3
-rw-r--r--doc/api/commits.md23
-rw-r--r--doc/api/container_registry.md39
-rw-r--r--doc/api/deployments.md23
-rw-r--r--doc/api/discussions.md2
-rw-r--r--doc/api/dora/metrics.md2
-rw-r--r--doc/api/epic_issues.md2
-rw-r--r--doc/api/geo_nodes.md36
-rw-r--r--doc/api/graphql/audit_report.md4
-rw-r--r--doc/api/graphql/index.md1
-rw-r--r--doc/api/graphql/reference/index.md636
-rw-r--r--doc/api/graphql/users_example.md4
-rw-r--r--doc/api/group_badges.md2
-rw-r--r--doc/api/group_import_export.md4
-rw-r--r--doc/api/group_wikis.md14
-rw-r--r--doc/api/groups.md62
-rw-r--r--doc/api/import.md12
-rw-r--r--doc/api/index.md8
-rw-r--r--doc/api/integrations.md54
-rw-r--r--doc/api/invitations.md2
-rw-r--r--doc/api/issue_links.md2
-rw-r--r--doc/api/issues.md4
-rw-r--r--doc/api/jobs.md57
-rw-r--r--doc/api/keys.md3
-rw-r--r--doc/api/license.md49
-rw-r--r--doc/api/members.md8
-rw-r--r--doc/api/merge_request_approvals.md12
-rw-r--r--doc/api/merge_requests.md225
-rw-r--r--doc/api/merge_trains.md66
-rw-r--r--doc/api/packages.md4
-rw-r--r--doc/api/packages/pypi.md6
-rw-r--r--doc/api/packages/terraform-modules.md2
-rw-r--r--doc/api/pipelines.md2
-rw-r--r--doc/api/product_analytics.md17
-rw-r--r--doc/api/project_badges.md2
-rw-r--r--doc/api/project_import_export.md5
-rw-r--r--doc/api/project_snippets.md2
-rw-r--r--doc/api/projects.md32
-rw-r--r--doc/api/protected_branches.md26
-rw-r--r--doc/api/repository_files.md1
-rw-r--r--doc/api/runners.md51
-rw-r--r--doc/api/saml.md8
-rw-r--r--doc/api/scim.md10
-rw-r--r--doc/api/settings.md28
-rw-r--r--doc/api/snippets.md2
-rw-r--r--doc/api/status_checks.md180
-rw-r--r--doc/api/tags.md53
-rw-r--r--doc/api/templates/gitlab_ci_ymls.md4
-rw-r--r--doc/api/todos.md16
-rw-r--r--doc/api/users.md44
-rw-r--r--doc/api/vulnerabilities.md6
-rw-r--r--doc/api/vulnerability_exports.md18
-rw-r--r--doc/api/vulnerability_findings.md8
-rw-r--r--doc/architecture/blueprints/_template.md5
-rw-r--r--doc/architecture/blueprints/ci_data_decay/index.md4
-rw-r--r--doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md85
-rw-r--r--doc/architecture/blueprints/ci_pipeline_components/index.md362
-rw-r--r--doc/architecture/blueprints/ci_scale/index.md10
-rw-r--r--doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md44
-rw-r--r--doc/architecture/blueprints/container_registry_metadata_database/index.md6
-rw-r--r--doc/architecture/blueprints/database/scalability/patterns/time_decay.md6
-rw-r--r--doc/architecture/blueprints/gitlab_observability_backend/metrics/index.md688
-rw-r--r--doc/architecture/blueprints/gitlab_observability_backend/metrics/supported-deployments.pngbin0 -> 257144 bytes
-rw-r--r--doc/architecture/blueprints/image_resizing/index.md6
-rw-r--r--doc/architecture/blueprints/object_storage/index.md6
-rw-r--r--doc/architecture/blueprints/pods/images/pods-and-fulfillment.pngbin75803 -> 20899 bytes
-rw-r--r--doc/architecture/blueprints/pods/index.md12
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-admin-area.md58
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-agent-for-kubernetes.md29
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-ci-runners.md169
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-container-registry.md131
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-contributions-forks.md120
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-dashboard.md29
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-data-migration.md50
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-git-access.md4
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-gitlab-pages.md29
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-global-search.md47
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-graphql.md2
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-personal-namespaces.md29
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-router-endpoints-classification.md2
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-schema-changes.md55
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-snippets.md29
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-uploads.md29
-rw-r--r--doc/architecture/blueprints/pods/proposal-stateless-router-with-buffering-requests.md18
-rw-r--r--doc/architecture/blueprints/pods/proposal-stateless-router-with-routes-learning.md6
-rw-r--r--doc/architecture/blueprints/rate_limiting/index.md16
-rw-r--r--doc/architecture/blueprints/remote_development/img/remote_dev_15_7.pngbin0 -> 108160 bytes
-rw-r--r--doc/architecture/blueprints/remote_development/img/remote_dev_15_7_1.pngbin0 -> 98016 bytes
-rw-r--r--doc/architecture/blueprints/remote_development/index.md315
-rw-r--r--doc/architecture/blueprints/runner_scaling/index.md12
-rw-r--r--doc/architecture/blueprints/runner_tokens/index.md295
-rw-r--r--doc/architecture/blueprints/work_items/index.md4
-rw-r--r--doc/ci/ci_cd_for_external_repos/bitbucket_integration.md2
-rw-r--r--doc/ci/cloud_services/azure/index.md2
-rw-r--r--doc/ci/directed_acyclic_graph/index.md10
-rw-r--r--doc/ci/environments/deployment_approvals.md25
-rw-r--r--doc/ci/environments/external_deployment_tools.md88
-rw-r--r--doc/ci/environments/index.md30
-rw-r--r--doc/ci/environments/protected_environments.md6
-rw-r--r--doc/ci/examples/authenticating-with-hashicorp-vault/index.md37
-rw-r--r--doc/ci/examples/deployment/composer-npm-deploy.md2
-rw-r--r--doc/ci/examples/index.md2
-rw-r--r--doc/ci/examples/php.md2
-rw-r--r--doc/ci/examples/semantic-release.md3
-rw-r--r--doc/ci/git_submodules.md10
-rw-r--r--doc/ci/jobs/ci_job_token.md2
-rw-r--r--doc/ci/jobs/index.md1
-rw-r--r--doc/ci/large_repositories/index.md10
-rw-r--r--doc/ci/migration/jenkins.md84
-rw-r--r--doc/ci/pipelines/cicd_minutes.md4
-rw-r--r--doc/ci/pipelines/downstream_pipelines.md2
-rw-r--r--doc/ci/pipelines/index.md24
-rw-r--r--doc/ci/pipelines/multi_project_pipelines.md11
-rw-r--r--doc/ci/pipelines/parent_child_pipelines.md11
-rw-r--r--doc/ci/pipelines/pipeline_efficiency.md2
-rw-r--r--doc/ci/pipelines/settings.md9
-rw-r--r--doc/ci/runners/configure_runners.md57
-rw-r--r--doc/ci/runners/saas/linux_saas_runner.md12
-rw-r--r--doc/ci/runners/saas/macos_saas_runner.md6
-rw-r--r--doc/ci/secrets/index.md2
-rw-r--r--doc/ci/secure_files/index.md8
-rw-r--r--doc/ci/services/index.md4
-rw-r--r--doc/ci/testing/code_quality.md3
-rw-r--r--doc/ci/testing/fail_fast_testing.md2
-rw-r--r--doc/ci/testing/img/code_quality_mr_diff_report_v14_2.pngbin40901 -> 0 bytes
-rw-r--r--doc/ci/testing/img/code_quality_mr_diff_report_v15_7.pngbin0 -> 24387 bytes
-rw-r--r--doc/ci/testing/unit_test_report_examples.md2
-rw-r--r--doc/ci/troubleshooting.md27
-rw-r--r--doc/ci/variables/index.md26
-rw-r--r--doc/ci/variables/predefined_variables.md4
-rw-r--r--doc/ci/variables/where_variables_can_be_used.md8
-rw-r--r--doc/ci/yaml/artifacts_reports.md20
-rw-r--r--doc/ci/yaml/includes.md7
-rw-r--r--doc/ci/yaml/index.md130
-rw-r--r--doc/ci/yaml/yaml_optimization.md24
-rw-r--r--doc/development/adding_database_indexes.md11
-rw-r--r--doc/development/api_graphql_styleguide.md6
-rw-r--r--doc/development/api_styleguide.md30
-rw-r--r--doc/development/application_limits.md2
-rw-r--r--doc/development/application_slis/rails_request_apdex.md2
-rw-r--r--doc/development/approval_rules.md4
-rw-r--r--doc/development/architecture.md4
-rw-r--r--doc/development/caching.md6
-rw-r--r--doc/development/cascading_settings.md4
-rw-r--r--doc/development/cicd/pipeline_wizard.md2
-rw-r--r--doc/development/code_review.md55
-rw-r--r--doc/development/contributing/design.md13
-rw-r--r--doc/development/contributing/index.md4
-rw-r--r--doc/development/contributing/merge_request_workflow.md3
-rw-r--r--doc/development/contributing/style_guides.md2
-rw-r--r--doc/development/creating_enums.md11
-rw-r--r--doc/development/dangerbot.md2
-rw-r--r--doc/development/database/add_foreign_key_to_existing_column.md6
-rw-r--r--doc/development/database/adding_database_indexes.md9
-rw-r--r--doc/development/database/avoiding_downtime_in_migrations.md27
-rw-r--r--doc/development/database/background_migrations.md6
-rw-r--r--doc/development/database/batched_background_migrations.md10
-rw-r--r--doc/development/database/creating_enums.md2
-rw-r--r--doc/development/database/database_debugging.md4
-rw-r--r--doc/development/database/database_dictionary.md47
-rw-r--r--doc/development/database/efficient_in_operator_queries.md11
-rw-r--r--doc/development/database/foreign_keys.md6
-rw-r--r--doc/development/database/index.md12
-rw-r--r--doc/development/database/keyset_pagination.md2
-rw-r--r--doc/development/database/loose_foreign_keys.md10
-rw-r--r--doc/development/database/migrations_for_multiple_databases.md38
-rw-r--r--doc/development/database/multiple_databases.md4
-rw-r--r--doc/development/database/not_null_constraints.md10
-rw-r--r--doc/development/database/pagination_performance_guidelines.md2
-rw-r--r--doc/development/database/polymorphic_associations.md8
-rw-r--r--doc/development/database/query_performance.md15
-rw-r--r--doc/development/database/query_recorder.md2
-rw-r--r--doc/development/database/single_table_inheritance.md4
-rw-r--r--doc/development/database/strings_and_the_text_data_type.md14
-rw-r--r--doc/development/database/table_partitioning.md20
-rw-r--r--doc/development/database/understanding_explain_plans.md2
-rw-r--r--doc/development/database_debugging.md11
-rw-r--r--doc/development/database_query_comments.md11
-rw-r--r--doc/development/database_review.md2
-rw-r--r--doc/development/db_dump.md11
-rw-r--r--doc/development/directory_structure.md97
-rw-r--r--doc/development/documentation/index.md33
-rw-r--r--doc/development/documentation/site_architecture/deployment_process.md29
-rw-r--r--doc/development/documentation/site_architecture/global_nav.md4
-rw-r--r--doc/development/documentation/structure.md11
-rw-r--r--doc/development/documentation/styleguide/img/tier_badge.pngbin9320 -> 0 bytes
-rw-r--r--doc/development/documentation/styleguide/index.md293
-rw-r--r--doc/development/documentation/styleguide/word_list.md85
-rw-r--r--doc/development/documentation/topic_types/concept.md2
-rw-r--r--doc/development/documentation/topic_types/index.md13
-rw-r--r--doc/development/documentation/topic_types/task.md6
-rw-r--r--doc/development/documentation/versions.md6
-rw-r--r--doc/development/documentation/workflow.md64
-rw-r--r--doc/development/ee_features.md19
-rw-r--r--doc/development/elasticsearch.md6
-rw-r--r--doc/development/fe_guide/accessibility.md8
-rw-r--r--doc/development/fe_guide/content_editor.md16
-rw-r--r--doc/development/fe_guide/customizable_dashboards.md16
-rw-r--r--doc/development/fe_guide/dark_mode.md4
-rw-r--r--doc/development/fe_guide/development_process.md4
-rw-r--r--doc/development/fe_guide/graphql.md4
-rw-r--r--doc/development/fe_guide/haml.md12
-rw-r--r--doc/development/fe_guide/merge_request_widget_extensions.md7
-rw-r--r--doc/development/fe_guide/security.md2
-rw-r--r--doc/development/fe_guide/source_editor.md6
-rw-r--r--doc/development/fe_guide/style/javascript.md8
-rw-r--r--doc/development/fe_guide/style/scss.md6
-rw-r--r--doc/development/fe_guide/tooling.md2
-rw-r--r--doc/development/fe_guide/troubleshooting.md4
-rw-r--r--doc/development/fe_guide/view_component.md2
-rw-r--r--doc/development/fe_guide/vue3_migration.md4
-rw-r--r--doc/development/fe_guide/widgets.md4
-rw-r--r--doc/development/feature_categorization/index.md39
-rw-r--r--doc/development/feature_development.md7
-rw-r--r--doc/development/feature_flags/controls.md45
-rw-r--r--doc/development/feature_flags/index.md44
-rw-r--r--doc/development/file_storage.md2
-rw-r--r--doc/development/filtering_by_label.md11
-rw-r--r--doc/development/fips_compliance.md21
-rw-r--r--doc/development/foreign_keys.md11
-rw-r--r--doc/development/gemfile.md5
-rw-r--r--doc/development/geo.md60
-rw-r--r--doc/development/geo/framework.md8
-rw-r--r--doc/development/geo/proxying.md16
-rw-r--r--doc/development/git_object_deduplication.md2
-rw-r--r--doc/development/gitaly.md6
-rw-r--r--doc/development/github_importer.md4
-rw-r--r--doc/development/gitlab_flavored_markdown/specification_guide/index.md8
-rw-r--r--doc/development/gitpod_internals.md30
-rw-r--r--doc/development/go_guide/index.md2
-rw-r--r--doc/development/graphql_guide/batchloader.md2
-rw-r--r--doc/development/hash_indexes.md11
-rw-r--r--doc/development/i18n/externalization.md8
-rw-r--r--doc/development/image_scaling.md6
-rw-r--r--doc/development/import_project.md2
-rw-r--r--doc/development/insert_into_tables_in_batches.md11
-rw-r--r--doc/development/integrations/codesandbox.md4
-rw-r--r--doc/development/integrations/index.md37
-rw-r--r--doc/development/integrations/jira_connect.md15
-rw-r--r--doc/development/integrations/secure.md17
-rw-r--r--doc/development/integrations/secure_partner_integration.md2
-rw-r--r--doc/development/iterating_tables_in_batches.md11
-rw-r--r--doc/development/lfs.md11
-rw-r--r--doc/development/licensing.md2
-rw-r--r--doc/development/merge_request_performance_guidelines.md18
-rw-r--r--doc/development/migration_style_guide.md82
-rw-r--r--doc/development/multi_version_compatibility.md2
-rw-r--r--doc/development/namespaces_storage_statistics.md11
-rw-r--r--doc/development/new_fe_guide/development/accessibility.md11
-rw-r--r--doc/development/new_fe_guide/development/components.md11
-rw-r--r--doc/development/new_fe_guide/development/index.md11
-rw-r--r--doc/development/new_fe_guide/development/performance.md11
-rw-r--r--doc/development/new_fe_guide/index.md11
-rw-r--r--doc/development/new_fe_guide/modules/dirty_submit.md11
-rw-r--r--doc/development/new_fe_guide/modules/index.md11
-rw-r--r--doc/development/new_fe_guide/modules/widget_extensions.md11
-rw-r--r--doc/development/new_fe_guide/tips.md11
-rw-r--r--doc/development/ordering_table_columns.md11
-rw-r--r--doc/development/packages/dependency_proxy.md4
-rw-r--r--doc/development/pages/index.md2
-rw-r--r--doc/development/performance.md6
-rw-r--r--doc/development/pipelines/index.md42
-rw-r--r--doc/development/pipelines/internals.md10
-rw-r--r--doc/development/pipelines/performance.md1
-rw-r--r--doc/development/polymorphic_associations.md11
-rw-r--r--doc/development/product_qualified_lead_guide/index.md22
-rw-r--r--doc/development/profiling.md40
-rw-r--r--doc/development/project_templates.md33
-rw-r--r--doc/development/projections.md4
-rw-r--r--doc/development/prometheus_metrics.md2
-rw-r--r--doc/development/query_count_limits.md11
-rw-r--r--doc/development/query_performance.md11
-rw-r--r--doc/development/query_recorder.md11
-rw-r--r--doc/development/reactive_caching.md2
-rw-r--r--doc/development/redis/new_redis_instance.md16
-rw-r--r--doc/development/reusing_abstractions.md2
-rw-r--r--doc/development/scalability.md2
-rw-r--r--doc/development/sec/analyzer_development_guide.md2
-rw-r--r--doc/development/sec/index.md10
-rw-r--r--doc/development/secure_coding_guidelines.md17
-rw-r--r--doc/development/serializing_data.md11
-rw-r--r--doc/development/service_ping/implement.md8
-rw-r--r--doc/development/service_ping/metrics_dictionary.md29
-rw-r--r--doc/development/service_ping/metrics_instrumentation.md12
-rw-r--r--doc/development/service_ping/troubleshooting.md2
-rw-r--r--doc/development/sha1_as_binary.md11
-rw-r--r--doc/development/sidekiq/compatibility_across_updates.md62
-rw-r--r--doc/development/sidekiq/idempotent_jobs.md4
-rw-r--r--doc/development/sidekiq/index.md6
-rw-r--r--doc/development/single_table_inheritance.md11
-rw-r--r--doc/development/snowplow/index.md2
-rw-r--r--doc/development/snowplow/schemas.md4
-rw-r--r--doc/development/snowplow/troubleshooting.md2
-rw-r--r--doc/development/software_design.md141
-rw-r--r--doc/development/spam_protection_and_captcha/exploratory_testing.md2
-rw-r--r--doc/development/sql.md4
-rw-r--r--doc/development/swapping_tables.md11
-rw-r--r--doc/development/testing_guide/best_practices.md74
-rw-r--r--doc/development/testing_guide/contract/index.md2
-rw-r--r--doc/development/testing_guide/end_to_end/feature_flags.md4
-rw-r--r--doc/development/testing_guide/end_to_end/index.md8
-rw-r--r--doc/development/testing_guide/end_to_end/page_objects.md2
-rw-r--r--doc/development/testing_guide/end_to_end/rspec_metadata_tests.md3
-rw-r--r--doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md4
-rw-r--r--doc/development/testing_guide/end_to_end/troubleshooting.md2
-rw-r--r--doc/development/testing_guide/flaky_tests.md10
-rw-r--r--doc/development/testing_guide/frontend_testing.md61
-rw-r--r--doc/development/testing_guide/img/testing_triangle.pngbin11836 -> 32902 bytes
-rw-r--r--doc/development/understanding_explain_plans.md11
-rw-r--r--doc/development/uploads/index.md35
-rw-r--r--doc/development/uploads/working_with_uploads.md28
-rw-r--r--doc/development/utilities.md34
-rw-r--r--doc/development/value_stream_analytics.md5
-rw-r--r--doc/development/value_stream_analytics/value_stream_analytics_aggregated_backend.md6
-rw-r--r--doc/development/verifying_database_capabilities.md11
-rw-r--r--doc/development/work_items.md6
-rw-r--r--doc/development/work_items_widgets.md2
-rw-r--r--doc/development/workhorse/configuration.md4
-rw-r--r--doc/gitlab-basics/start-using-git.md2
-rw-r--r--doc/index.md23
-rw-r--r--doc/install/aws/eks_clusters_aws.md2
-rw-r--r--doc/install/aws/gitlab_hybrid_on_aws.md18
-rw-r--r--doc/install/aws/gitlab_sre_for_aws.md2
-rw-r--r--doc/install/aws/index.md4
-rw-r--r--doc/install/docker.md8
-rw-r--r--doc/install/installation.md10
-rw-r--r--doc/install/openshift_and_gitlab/index.md8
-rw-r--r--doc/install/requirements.md3
-rw-r--r--doc/integration/advanced_search/elasticsearch.md97
-rw-r--r--doc/integration/advanced_search/elasticsearch_troubleshooting.md33
-rw-r--r--doc/integration/arkose.md4
-rw-r--r--doc/integration/datadog.md4
-rw-r--r--doc/integration/glab/img/glabgettingstarted.gifbin0 -> 141528 bytes
-rw-r--r--doc/integration/glab/index.md80
-rw-r--r--doc/integration/index.md2
-rw-r--r--doc/integration/jenkins.md2
-rw-r--r--doc/integration/jira/connect-app.md49
-rw-r--r--doc/integration/jira/dvcs.md3
-rw-r--r--doc/integration/kerberos.md32
-rw-r--r--doc/integration/mattermost/index.md4
-rw-r--r--doc/integration/oauth2_generic.md203
-rw-r--r--doc/integration/omniauth.md35
-rw-r--r--doc/integration/saml.md739
-rw-r--r--doc/operations/error_tracking.md14
-rw-r--r--doc/operations/incident_management/alerts.md44
-rw-r--r--doc/operations/incident_management/img/alert_detail_add_todo_v13_9.pngbin26763 -> 0 bytes
-rw-r--r--doc/operations/incident_management/img/incident_list_create_v13_3.pngbin21931 -> 0 bytes
-rw-r--r--doc/operations/incident_management/img/incident_list_v14_9.pngbin45199 -> 0 bytes
-rw-r--r--doc/operations/incident_management/img/incident_list_v15_6.pngbin0 -> 53028 bytes
-rw-r--r--doc/operations/incident_management/img/new_incident_create_v13_4.pngbin12106 -> 0 bytes
-rw-r--r--doc/operations/incident_management/img/pagerduty_incidents_integration_v13_3.pngbin13288 -> 0 bytes
-rw-r--r--doc/operations/incident_management/img/timeline_view_toggle_v14_10.pngbin8423 -> 0 bytes
-rw-r--r--doc/operations/incident_management/incident_timeline_events.md10
-rw-r--r--doc/operations/incident_management/incidents.md350
-rw-r--r--doc/operations/incident_management/integrations.md4
-rw-r--r--doc/operations/incident_management/linked_resources.md6
-rw-r--r--doc/operations/incident_management/manage_incidents.md263
-rw-r--r--doc/operations/incident_management/paging.md23
-rw-r--r--doc/operations/incident_management/slack.md117
-rw-r--r--doc/operations/metrics/alerts.md59
-rw-r--r--doc/operations/metrics/dashboards/panel_types.md44
-rw-r--r--doc/operations/metrics/dashboards/variables.md4
-rw-r--r--doc/operations/metrics/dashboards/yaml.md2
-rw-r--r--doc/operations/tracing.md14
-rw-r--r--doc/policy/maintenance.md2
-rw-r--r--doc/raketasks/backup_gitlab.md2
-rw-r--r--doc/raketasks/backup_restore.md100
-rw-r--r--doc/raketasks/cleanup.md5
-rw-r--r--doc/raketasks/generate_sample_prometheus_data.md2
-rw-r--r--doc/raketasks/index.md12
-rw-r--r--doc/raketasks/list_repos.md2
-rw-r--r--doc/raketasks/migrate_snippets.md2
-rw-r--r--doc/raketasks/sidekiq_job_migration.md11
-rw-r--r--doc/raketasks/spdx.md2
-rw-r--r--doc/raketasks/user_management.md5
-rw-r--r--doc/raketasks/web_hooks.md2
-rw-r--r--doc/raketasks/x509_signatures.md2
-rw-r--r--doc/security/crime_vulnerability.md2
-rw-r--r--doc/security/password_storage.md4
-rw-r--r--doc/security/rate_limits.md13
-rw-r--r--doc/security/reset_user_password.md14
-rw-r--r--doc/security/user_email_confirmation.md2
-rw-r--r--doc/subscriptions/gitlab_com/index.md77
-rw-r--r--doc/subscriptions/gitlab_dedicated/index.md75
-rw-r--r--doc/subscriptions/img/add-license.pngbin0 -> 16222 bytes
-rw-r--r--doc/subscriptions/index.md50
-rw-r--r--doc/subscriptions/self_managed/index.md17
-rw-r--r--doc/topics/authentication/index.md2
-rw-r--r--doc/topics/autodevops/cicd_variables.md331
-rw-r--r--doc/topics/autodevops/cloud_deployments/auto_devops_with_gke.md6
-rw-r--r--doc/topics/autodevops/customize.md350
-rw-r--r--doc/topics/autodevops/index.md2
-rw-r--r--doc/topics/autodevops/multiple_clusters_auto_devops.md6
-rw-r--r--doc/topics/autodevops/prepare_deployment.md4
-rw-r--r--doc/topics/autodevops/requirements.md4
-rw-r--r--doc/topics/autodevops/stages.md4
-rw-r--r--doc/topics/autodevops/troubleshooting.md4
-rw-r--r--doc/topics/autodevops/upgrading_auto_deploy_dependencies.md4
-rw-r--r--doc/topics/awesome_co.md2
-rw-r--r--doc/topics/git/feature_branch_development.md6
-rw-r--r--doc/topics/git/lfs/index.md6
-rw-r--r--doc/topics/git/subtree.md4
-rw-r--r--doc/topics/offline/quick_start_guide.md10
-rw-r--r--doc/topics/plan_and_track.md4
-rw-r--r--doc/tutorials/index.md12
-rw-r--r--doc/tutorials/move_personal_project_to_a_group.md36
-rw-r--r--doc/update/background_migrations.md461
-rw-r--r--doc/update/deprecations.md284
-rw-r--r--doc/update/index.md524
-rw-r--r--doc/update/mysql_to_postgresql.md305
-rw-r--r--doc/update/package/index.md7
-rw-r--r--doc/update/patch_versions.md2
-rw-r--r--doc/update/plan_your_upgrade.md4
-rw-r--r--doc/update/removals.md42
-rw-r--r--doc/update/restore_after_failure.md63
-rw-r--r--doc/update/upgrading_from_source.md2
-rw-r--r--doc/update/upgrading_postgresql_using_slony.md478
-rw-r--r--doc/update/with_downtime.md98
-rw-r--r--doc/update/zero_downtime.md161
-rw-r--r--doc/user/admin_area/external_users.md77
-rw-r--r--doc/user/admin_area/index.md2
-rw-r--r--doc/user/admin_area/license_file.md3
-rw-r--r--doc/user/admin_area/monitoring/background_migrations.md247
-rw-r--r--doc/user/admin_area/monitoring/health_check.md2
-rw-r--r--doc/user/admin_area/settings/account_and_limit_settings.md2
-rw-r--r--doc/user/admin_area/settings/continuous_integration.md25
-rw-r--r--doc/user/admin_area/settings/img/mirror_settings.pngbin9966 -> 0 bytes
-rw-r--r--doc/user/admin_area/settings/img/mirror_settings_v15_7.pngbin0 -> 10322 bytes
-rw-r--r--doc/user/admin_area/settings/index.md9
-rw-r--r--doc/user/admin_area/settings/sidekiq_job_limits.md2
-rw-r--r--doc/user/admin_area/settings/sign_in_restrictions.md10
-rw-r--r--doc/user/admin_area/settings/sign_up_restrictions.md8
-rw-r--r--doc/user/admin_area/settings/terraform_limits.md27
-rw-r--r--doc/user/admin_area/settings/visibility_and_access_controls.md4
-rw-r--r--doc/user/analytics/dora_metrics.md4
-rw-r--r--doc/user/analytics/index.md2
-rw-r--r--doc/user/analytics/value_stream_analytics.md2
-rw-r--r--doc/user/application_security/api_fuzzing/create_har_files.md2
-rw-r--r--doc/user/application_security/api_fuzzing/index.md105
-rw-r--r--doc/user/application_security/configuration/index.md2
-rw-r--r--doc/user/application_security/container_scanning/index.md26
-rw-r--r--doc/user/application_security/dast/authentication.md527
-rw-r--r--doc/user/application_security/dast/browser_based.md282
-rw-r--r--doc/user/application_security/dast/browser_based_troubleshooting.md300
-rw-r--r--doc/user/application_security/dast/checks/16.2.md2
-rw-r--r--doc/user/application_security/dast/checks/16.3.md2
-rw-r--r--doc/user/application_security/dast/checks/548.1.md4
-rw-r--r--doc/user/application_security/dast/checks/798.33.md4
-rw-r--r--doc/user/application_security/dast/checks/798.49.md4
-rw-r--r--doc/user/application_security/dast/checks/798.65.md4
-rw-r--r--doc/user/application_security/dast/checks/798.97.md4
-rw-r--r--doc/user/application_security/dast/checks/829.1.md2
-rw-r--r--doc/user/application_security/dast/checks/829.2.md2
-rw-r--r--doc/user/application_security/dast/checks/index.md8
-rw-r--r--doc/user/application_security/dast/dast_troubleshooting.md2
-rw-r--r--doc/user/application_security/dast/index.md2
-rw-r--r--doc/user/application_security/dast/proxy-based.md615
-rw-r--r--doc/user/application_security/dast/run_dast_offline.md2
-rw-r--r--doc/user/application_security/dast_api/index.md165
-rw-r--r--doc/user/application_security/dependency_scanning/index.md19
-rw-r--r--doc/user/application_security/index.md10
-rw-r--r--doc/user/application_security/offline_deployments/index.md2
-rw-r--r--doc/user/application_security/policies/index.md8
-rw-r--r--doc/user/application_security/policies/scan-execution-policies.md27
-rw-r--r--doc/user/application_security/policies/scan-result-policies.md9
-rw-r--r--doc/user/application_security/sast/customize_rulesets.md574
-rw-r--r--doc/user/application_security/sast/index.md3
-rw-r--r--doc/user/application_security/secret_detection/index.md46
-rw-r--r--doc/user/application_security/secret_detection/post_processing.md10
-rw-r--r--doc/user/application_security/terminology/index.md32
-rw-r--r--doc/user/application_security/vulnerabilities/index.md2
-rw-r--r--doc/user/application_security/vulnerability_report/index.md2
-rw-r--r--doc/user/application_security/vulnerability_report/pipeline.md17
-rw-r--r--doc/user/asciidoc.md5
-rw-r--r--doc/user/clusters/agent/ci_cd_workflow.md32
-rw-r--r--doc/user/clusters/agent/gitops.md12
-rw-r--r--doc/user/clusters/agent/gitops/helm.md9
-rw-r--r--doc/user/clusters/agent/install/index.md2
-rw-r--r--doc/user/clusters/agent/vulnerabilities.md75
-rw-r--r--doc/user/clusters/management_project_template.md2
-rw-r--r--doc/user/compliance/license_compliance/index.md4
-rw-r--r--doc/user/free_user_limit.md2
-rw-r--r--doc/user/gitlab_com/index.md19
-rw-r--r--doc/user/group/access_and_permissions.md11
-rw-r--r--doc/user/group/compliance_frameworks.md18
-rw-r--r--doc/user/group/contribution_analytics/index.md3
-rw-r--r--doc/user/group/devops_adoption/index.md2
-rw-r--r--doc/user/group/epics/manage_epics.md23
-rw-r--r--doc/user/group/import/index.md322
-rw-r--r--doc/user/group/manage.md8
-rw-r--r--doc/user/group/saml_sso/group_sync.md34
-rw-r--r--doc/user/group/saml_sso/index.md24
-rw-r--r--doc/user/group/saml_sso/troubleshooting.md27
-rw-r--r--doc/user/group/settings/group_access_tokens.md5
-rw-r--r--doc/user/group/settings/import_export.md167
-rw-r--r--doc/user/group/value_stream_analytics/index.md2
-rw-r--r--doc/user/img/markdown_logo.pngbin4398 -> 8692 bytes
-rw-r--r--doc/user/infrastructure/clusters/connect/new_gke_cluster.md2
-rw-r--r--doc/user/infrastructure/clusters/manage/management_project_applications/prometheus.md11
-rw-r--r--doc/user/infrastructure/clusters/manage/management_project_applications/sentry.md11
-rw-r--r--doc/user/infrastructure/iac/index.md10
-rw-r--r--doc/user/infrastructure/iac/terraform_state.md6
-rw-r--r--doc/user/infrastructure/iac/terraform_template_recipes.md183
-rw-r--r--doc/user/infrastructure/iac/troubleshooting.md38
-rw-r--r--doc/user/markdown.md62
-rw-r--r--doc/user/namespace/index.md16
-rw-r--r--doc/user/operations_dashboard/index.md4
-rw-r--r--doc/user/packages/conan_repository/index.md5
-rw-r--r--doc/user/packages/container_registry/index.md279
-rw-r--r--doc/user/packages/container_registry/reduce_container_registry_data_transfer.md2
-rw-r--r--doc/user/packages/container_registry/reduce_container_registry_storage.md111
-rw-r--r--doc/user/packages/container_registry/troubleshoot_container_registry.md129
-rw-r--r--doc/user/packages/generic_packages/index.md2
-rw-r--r--doc/user/packages/gradle_repository/index.md372
-rw-r--r--doc/user/packages/helm_repository/index.md2
-rw-r--r--doc/user/packages/maven_repository/index.md694
-rw-r--r--doc/user/packages/npm_registry/index.md503
-rw-r--r--doc/user/packages/nuget_repository/img/visual_studio_adding_nuget_source.pngbin36730 -> 0 bytes
-rw-r--r--doc/user/packages/nuget_repository/img/visual_studio_nuget_source_added.pngbin6234 -> 0 bytes
-rw-r--r--doc/user/packages/nuget_repository/index.md32
-rw-r--r--doc/user/packages/package_registry/index.md71
-rw-r--r--doc/user/packages/package_registry/supported_hash_types.md25
-rw-r--r--doc/user/packages/package_registry/supported_package_managers.md34
-rw-r--r--doc/user/packages/pypi_repository/index.md38
-rw-r--r--doc/user/packages/terraform_module_registry/index.md8
-rw-r--r--doc/user/packages/workflows/build_packages.md4
-rw-r--r--doc/user/packages/workflows/project_registry.md8
-rw-r--r--doc/user/packages/yarn_repository/index.md248
-rw-r--r--doc/user/permissions.md218
-rw-r--r--doc/user/product_analytics/index.md42
-rw-r--r--doc/user/profile/account/delete_account.md1
-rw-r--r--doc/user/profile/active_sessions.md2
-rw-r--r--doc/user/profile/contributions_calendar.md58
-rw-r--r--doc/user/profile/index.md4
-rw-r--r--doc/user/profile/notifications.md2
-rw-r--r--doc/user/profile/preferences.md18
-rw-r--r--doc/user/profile/user_passwords.md8
-rw-r--r--doc/user/project/badges.md2
-rw-r--r--doc/user/project/canary_deployments.md2
-rw-r--r--doc/user/project/clusters/add_eks_clusters.md2
-rw-r--r--doc/user/project/clusters/cluster_access.md6
-rw-r--r--doc/user/project/deploy_tokens/index.md16
-rw-r--r--doc/user/project/description_templates.md20
-rw-r--r--doc/user/project/import/bitbucket.md2
-rw-r--r--doc/user/project/import/bitbucket_server.md4
-rw-r--r--doc/user/project/import/github.md53
-rw-r--r--doc/user/project/import/img/gitlab_import_history_page_v14_10.pngbin28219 -> 0 bytes
-rw-r--r--doc/user/project/import/index.md139
-rw-r--r--doc/user/project/import/svn.md94
-rw-r--r--doc/user/project/integrations/bamboo.md2
-rw-r--r--doc/user/project/integrations/gitlab_slack_application.md14
-rw-r--r--doc/user/project/integrations/index.md3
-rw-r--r--doc/user/project/integrations/mlflow_client.md12
-rw-r--r--doc/user/project/integrations/slack.md4
-rw-r--r--doc/user/project/integrations/webhook_events.md6
-rw-r--r--doc/user/project/integrations/webhooks.md77
-rw-r--r--doc/user/project/issue_board.md1
-rw-r--r--doc/user/project/issues/create_issues.md221
-rw-r--r--doc/user/project/issues/index.md2
-rw-r--r--doc/user/project/issues/issue_weight.md2
-rw-r--r--doc/user/project/issues/managing_issues.md288
-rw-r--r--doc/user/project/issues/sorting_issue_lists.md13
-rw-r--r--doc/user/project/members/index.md4
-rw-r--r--doc/user/project/members/share_project_with_groups.md110
-rw-r--r--doc/user/project/merge_requests/approvals/index.md6
-rw-r--r--doc/user/project/merge_requests/approvals/rules.md2
-rw-r--r--doc/user/project/merge_requests/approvals/settings.md34
-rw-r--r--doc/user/project/merge_requests/changes.md11
-rw-r--r--doc/user/project/merge_requests/commit_templates.md4
-rw-r--r--doc/user/project/merge_requests/commits.md59
-rw-r--r--doc/user/project/merge_requests/conflicts.md4
-rw-r--r--doc/user/project/merge_requests/creating_merge_requests.md2
-rw-r--r--doc/user/project/merge_requests/img/add_previously_merged_commits_button_v14_1.pngbin19306 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/img/commit_nav_v13_11.pngbin24164 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/img/previously_merged_commits_v14_1.pngbin26788 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/merge_request_dependencies.md11
-rw-r--r--doc/user/project/merge_requests/methods/index.md130
-rw-r--r--doc/user/project/merge_requests/reviews/data_usage.md4
-rw-r--r--doc/user/project/merge_requests/reviews/img/suggestion_code_block_editor_v12_8.pngbin9917 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/reviews/index.md46
-rw-r--r--doc/user/project/merge_requests/reviews/suggestions.md8
-rw-r--r--doc/user/project/merge_requests/status_checks.md2
-rw-r--r--doc/user/project/ml/experiment_tracking/img/candidate_v15_7.pngbin0 -> 35164 bytes
-rw-r--r--doc/user/project/ml/experiment_tracking/img/candidates.pngbin62281 -> 0 bytes
-rw-r--r--doc/user/project/ml/experiment_tracking/img/candidates_v15_7.pngbin0 -> 47800 bytes
-rw-r--r--doc/user/project/ml/experiment_tracking/img/experiments.pngbin45022 -> 0 bytes
-rw-r--r--doc/user/project/ml/experiment_tracking/img/experiments_v15_7.pngbin0 -> 23475 bytes
-rw-r--r--doc/user/project/ml/experiment_tracking/index.md17
-rw-r--r--doc/user/project/pages/custom_domains_ssl_tls_certification/dns_concepts.md2
-rw-r--r--doc/user/project/pages/custom_domains_ssl_tls_certification/img/add_certificate_to_pages.pngbin14608 -> 0 bytes
-rw-r--r--doc/user/project/pages/custom_domains_ssl_tls_certification/index.md17
-rw-r--r--doc/user/project/pages/getting_started/pages_ci_cd_template.md9
-rw-r--r--doc/user/project/pages/getting_started/pages_forked_sample_project.md45
-rw-r--r--doc/user/project/pages/getting_started/pages_from_scratch.md5
-rw-r--r--doc/user/project/pages/getting_started/pages_new_project_template.md5
-rw-r--r--doc/user/project/pages/getting_started/pages_ui.md74
-rw-r--r--doc/user/project/pages/getting_started_part_one.md2
-rw-r--r--doc/user/project/pages/img/remove_fork_relationship_v13_1.pngbin11640 -> 0 bytes
-rw-r--r--doc/user/project/pages/public_folder.md59
-rw-r--r--doc/user/project/pages/redirects.md30
-rw-r--r--doc/user/project/push_options.md8
-rw-r--r--doc/user/project/quick_actions.md4
-rw-r--r--doc/user/project/releases/index.md26
-rw-r--r--doc/user/project/repository/branches/default.md21
-rw-r--r--doc/user/project/repository/branches/index.md39
-rw-r--r--doc/user/project/repository/gpg_signed_commits/index.md35
-rw-r--r--doc/user/project/repository/ssh_signed_commits/index.md174
-rw-r--r--doc/user/project/repository/web_editor.md2
-rw-r--r--doc/user/project/repository/x509_signed_commits/index.md2
-rw-r--r--doc/user/project/service_desk.md82
-rw-r--r--doc/user/project/settings/import_export.md69
-rw-r--r--doc/user/project/settings/index.md59
-rw-r--r--doc/user/project/settings/project_access_tokens.md5
-rw-r--r--doc/user/project/time_tracking.md28
-rw-r--r--doc/user/project/web_ide/index.md15
-rw-r--r--doc/user/project/web_ide_beta/img/fuzzy_finder_v15_7.pngbin0 -> 121069 bytes
-rw-r--r--doc/user/project/web_ide_beta/index.md103
-rw-r--r--doc/user/project/wiki/group.md2
-rw-r--r--doc/user/project/wiki/index.md11
-rw-r--r--doc/user/project/working_with_projects.md21
-rw-r--r--doc/user/public_access.md23
-rw-r--r--doc/user/read_only_namespaces.md48
-rw-r--r--doc/user/report_abuse.md12
-rw-r--r--doc/user/reserved_names.md9
-rw-r--r--doc/user/search/advanced_search.md20
-rw-r--r--doc/user/search/img/basic_search_results_v15_1.pngbin17833 -> 0 bytes
-rw-r--r--doc/user/search/img/basic_search_v15_1.pngbin10995 -> 0 bytes
-rw-r--r--doc/user/search/img/search_navbar_v15_7.pngbin0 -> 26983 bytes
-rw-r--r--doc/user/search/img/search_scope_v15_7.pngbin0 -> 287661 bytes
-rw-r--r--doc/user/search/index.md5
-rw-r--r--doc/user/shortcuts.md1
-rw-r--r--doc/user/snippets.md4
-rw-r--r--doc/user/ssh.md2
-rw-r--r--doc/user/tasks.md16
-rw-r--r--doc/user/todos.md3
-rw-r--r--doc/user/upgrade_email_bypass.md6
-rw-r--r--doc/user/usage_quotas.md3
-rw-r--r--doc/user/workspace/index.md6
-rw-r--r--glfm_specification/input/gitlab_flavored_markdown/glfm_example_metadata.yml17
-rw-r--r--glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml48
-rw-r--r--glfm_specification/input/gitlab_flavored_markdown/glfm_internal_extensions.md636
-rw-r--r--glfm_specification/input/gitlab_flavored_markdown/glfm_official_specification.md18
-rw-r--r--glfm_specification/output_example_snapshots/examples_index.yml171
-rw-r--r--glfm_specification/output_example_snapshots/html.yml726
-rw-r--r--glfm_specification/output_example_snapshots/markdown.yml284
-rw-r--r--glfm_specification/output_example_snapshots/prosemirror_json.yml2749
-rw-r--r--glfm_specification/output_example_snapshots/snapshot_spec.html8067
-rw-r--r--glfm_specification/output_example_snapshots/snapshot_spec.md646
-rw-r--r--glfm_specification/output_spec/spec.html445
-rw-r--r--glfm_specification/output_spec/spec.txt20
-rw-r--r--jest.config.base.js14
-rw-r--r--jest.config.contract.js6
-rw-r--r--jest.config.integration.js4
-rw-r--r--lefthook.yml30
-rw-r--r--lib/api/admin/batched_background_migrations.rb42
-rw-r--r--lib/api/admin/plan_limits.rb2
-rw-r--r--lib/api/alert_management_alerts.rb62
-rw-r--r--lib/api/api.rb71
-rw-r--r--lib/api/appearance.rb1
-rw-r--r--lib/api/award_emoji.rb2
-rw-r--r--lib/api/ci/job_artifacts.rb72
-rw-r--r--lib/api/ci/jobs.rb17
-rw-r--r--lib/api/ci/runner.rb14
-rw-r--r--lib/api/ci/runners.rb10
-rw-r--r--lib/api/ci/secure_files.rb47
-rw-r--r--lib/api/clusters/agent_tokens.rb9
-rw-r--r--lib/api/commit_statuses.rb14
-rw-r--r--lib/api/commits.rb25
-rw-r--r--lib/api/composer_packages.rb90
-rw-r--r--lib/api/conan_project_packages.rb2
-rw-r--r--lib/api/concerns/packages/conan_endpoints.rb168
-rw-r--r--lib/api/concerns/packages/debian_distribution_endpoints.rb80
-rw-r--r--lib/api/concerns/packages/debian_package_endpoints.rb90
-rw-r--r--lib/api/concerns/packages/npm_endpoints.rb92
-rw-r--r--lib/api/concerns/packages/nuget_endpoints.rb38
-rw-r--r--lib/api/container_registry_event.rb12
-rw-r--r--lib/api/container_repositories.rb7
-rw-r--r--lib/api/debian_group_packages.rb11
-rw-r--r--lib/api/debian_project_packages.rb33
-rw-r--r--lib/api/deployments.rb10
-rw-r--r--lib/api/entities/appearance.rb1
-rw-r--r--lib/api/entities/basic_success.rb12
-rw-r--r--lib/api/entities/batched_background_migration.rb12
-rw-r--r--lib/api/entities/ci/job_request/hook.rb13
-rw-r--r--lib/api/entities/ci/job_request/response.rb3
-rw-r--r--lib/api/entities/ci/runner_details.rb4
-rw-r--r--lib/api/entities/ci/secure_file.rb15
-rw-r--r--lib/api/entities/commit_signature.rb2
-rw-r--r--lib/api/entities/conan_package/conan_package_manifest.rb2
-rw-r--r--lib/api/entities/conan_package/conan_package_snapshot.rb6
-rw-r--r--lib/api/entities/conan_package/conan_recipe_manifest.rb2
-rw-r--r--lib/api/entities/conan_package/conan_recipe_snapshot.rb6
-rw-r--r--lib/api/entities/conan_package/conan_upload_urls.rb2
-rw-r--r--lib/api/entities/container_registry.rb23
-rw-r--r--lib/api/entities/event.rb16
-rw-r--r--lib/api/entities/issuable_references.rb6
-rw-r--r--lib/api/entities/issuable_time_stats.rb8
-rw-r--r--lib/api/entities/metric_image.rb8
-rw-r--r--lib/api/entities/milestone.rb2
-rw-r--r--lib/api/entities/ml/mlflow/experiment.rb1
-rw-r--r--lib/api/entities/ml/mlflow/key_value.rb14
-rw-r--r--lib/api/entities/ml/mlflow/run.rb3
-rw-r--r--lib/api/entities/ml/mlflow/run_param.rb14
-rw-r--r--lib/api/entities/namespace.rb2
-rw-r--r--lib/api/entities/namespace_basic.rb10
-rw-r--r--lib/api/entities/namespace_existence.rb3
-rw-r--r--lib/api/entities/npm_package.rb16
-rw-r--r--lib/api/entities/npm_package_tag.rb2
-rw-r--r--lib/api/entities/nuget/dependency.rb8
-rw-r--r--lib/api/entities/nuget/dependency_group.rb10
-rw-r--r--lib/api/entities/nuget/metadatum.rb6
-rw-r--r--lib/api/entities/nuget/package_metadata.rb7
-rw-r--r--lib/api/entities/nuget/package_metadata_catalog_entry.rb20
-rw-r--r--lib/api/entities/nuget/packages_metadata.rb5
-rw-r--r--lib/api/entities/nuget/packages_metadata_item.rb11
-rw-r--r--lib/api/entities/nuget/packages_versions.rb2
-rw-r--r--lib/api/entities/nuget/search_result.rb21
-rw-r--r--lib/api/entities/nuget/search_result_version.rb6
-rw-r--r--lib/api/entities/nuget/search_results.rb5
-rw-r--r--lib/api/entities/nuget/service_index.rb4
-rw-r--r--lib/api/entities/package.rb2
-rw-r--r--lib/api/entities/packages/debian/distribution.rb21
-rw-r--r--lib/api/entities/plan_limit.rb1
-rw-r--r--lib/api/entities/project.rb4
-rw-r--r--lib/api/entities/project_integration.rb4
-rw-r--r--lib/api/entities/push_event_payload.rb10
-rw-r--r--lib/api/entities/ssh_key.rb1
-rw-r--r--lib/api/entities/ssh_signature.rb10
-rw-r--r--lib/api/entities/tag_signature.rb13
-rw-r--r--lib/api/entities/todo.rb1
-rw-r--r--lib/api/events.rb14
-rw-r--r--lib/api/features.rb8
-rw-r--r--lib/api/files.rb16
-rw-r--r--lib/api/freeze_periods.rb2
-rw-r--r--lib/api/generic_packages.rb25
-rw-r--r--lib/api/group_debian_distributions.rb2
-rw-r--r--lib/api/groups.rb33
-rw-r--r--lib/api/helm_packages.rb44
-rw-r--r--lib/api/helpers.rb8
-rw-r--r--lib/api/helpers/award_emoji.rb22
-rw-r--r--lib/api/helpers/discussions_helpers.rb2
-rw-r--r--lib/api/helpers/integrations_helpers.rb9
-rw-r--r--lib/api/helpers/merge_requests_helpers.rb173
-rw-r--r--lib/api/helpers/notes_helpers.rb16
-rw-r--r--lib/api/helpers/packages/conan/api_helpers.rb10
-rw-r--r--lib/api/helpers/packages/dependency_proxy_helpers.rb6
-rw-r--r--lib/api/helpers/packages_helpers.rb14
-rw-r--r--lib/api/helpers/projects_helpers.rb8
-rw-r--r--lib/api/integrations/jira_connect/subscriptions.rb14
-rw-r--r--lib/api/internal/base.rb2
-rw-r--r--lib/api/internal/kubernetes.rb11
-rw-r--r--lib/api/markdown.rb5
-rw-r--r--lib/api/maven_packages.rb68
-rw-r--r--lib/api/members.rb12
-rw-r--r--lib/api/merge_request_approvals.rb18
-rw-r--r--lib/api/merge_requests.rb320
-rw-r--r--lib/api/ml/mlflow.rb42
-rw-r--r--lib/api/namespaces.rb35
-rw-r--r--lib/api/npm_project_packages.rb16
-rw-r--r--lib/api/nuget_group_packages.rb2
-rw-r--r--lib/api/nuget_project_packages.rb75
-rw-r--r--lib/api/pages.rb11
-rw-r--r--lib/api/project_container_repositories.rb49
-rw-r--r--lib/api/project_packages.rb27
-rw-r--r--lib/api/project_snippets.rb2
-rw-r--r--lib/api/projects.rb270
-rw-r--r--lib/api/pypi_packages.rb85
-rw-r--r--lib/api/release/links.rb4
-rw-r--r--lib/api/rpm_project_packages.rb46
-rw-r--r--lib/api/rubygem_packages.rb49
-rw-r--r--lib/api/settings.rb8
-rw-r--r--lib/api/snippets.rb2
-rw-r--r--lib/api/support/git_access_actor.rb2
-rw-r--r--lib/api/tags.rb18
-rw-r--r--lib/api/terraform/state.rb29
-rw-r--r--lib/api/time_tracking_endpoints.rb78
-rw-r--r--lib/api/unleash.rb12
-rw-r--r--lib/api/usage_data.rb29
-rw-r--r--lib/api/usage_data_non_sql_metrics.rb6
-rw-r--r--lib/api/usage_data_queries.rb6
-rw-r--r--lib/api/users.rb26
-rw-r--r--lib/api/v3/github.rb4
-rw-r--r--lib/api/validations/validators/array_none_any.rb2
-rw-r--r--lib/assets/images/bot_avatars/admin-bot.pngbin0 -> 6479 bytes
-rw-r--r--lib/atlassian/jira_connect.rb8
-rw-r--r--lib/atlassian/jira_connect/client.rb36
-rw-r--r--lib/atlassian/jira_connect/jwt/asymmetric.rb6
-rw-r--r--lib/atlassian/jira_connect/serializers/build_entity.rb10
-rw-r--r--lib/backup/files.rb2
-rw-r--r--lib/backup/manager.rb18
-rw-r--r--lib/banzai/filter/attributes_filter.rb51
-rw-r--r--lib/banzai/filter/inline_observability_filter.rb30
-rw-r--r--lib/banzai/filter/repository_link_filter.rb2
-rw-r--r--lib/banzai/filter/sanitization_filter.rb2
-rw-r--r--lib/banzai/filter/syntax_highlight_filter.rb4
-rw-r--r--lib/banzai/filter/timeout_html_pipeline_filter.rb38
-rw-r--r--lib/banzai/pipeline/ascii_doc_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb4
-rw-r--r--lib/banzai/pipeline/markup_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/wiki_pipeline.rb4
-rw-r--r--lib/banzai/reference_parser/alert_parser.rb8
-rw-r--r--lib/banzai/reference_parser/base_parser.rb8
-rw-r--r--lib/bitbucket_server/connection.rb1
-rw-r--r--lib/bulk_imports/clients/http.rb33
-rw-r--r--lib/bulk_imports/common/pipelines/uploads_pipeline.rb9
-rw-r--r--lib/bulk_imports/groups/stage.rb2
-rw-r--r--lib/bulk_imports/pipeline.rb1
-rw-r--r--lib/bulk_imports/projects/stage.rb2
-rw-r--r--lib/bulk_imports/stage.rb2
-rw-r--r--lib/extracts_ref.rb16
-rw-r--r--lib/feature.rb93
-rw-r--r--lib/feature/definition.rb6
-rw-r--r--lib/flowdock/git.rb67
-rw-r--r--lib/flowdock/git/builder.rb145
-rw-r--r--lib/gem_extensions/active_record/association.rb3
-rw-r--r--lib/gitlab.rb26
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_events.rb4
-rw-r--r--lib/gitlab/application_context.rb10
-rw-r--r--lib/gitlab/application_rate_limiter.rb5
-rw-r--r--lib/gitlab/audit/auditor.rb44
-rw-r--r--lib/gitlab/audit/type/definition.rb21
-rw-r--r--lib/gitlab/audit/type/shared.rb2
-rw-r--r--lib/gitlab/auth.rb6
-rw-r--r--lib/gitlab/auth/current_user_mode.rb12
-rw-r--r--lib/gitlab/auth/ldap/access.rb2
-rw-r--r--lib/gitlab/auth/ldap/adapter.rb2
-rw-r--r--lib/gitlab/auth/ldap/config.rb3
-rw-r--r--lib/gitlab/auth/ldap/dn.rb4
-rw-r--r--lib/gitlab/background_migration/backfill_environment_tiers.rb40
-rw-r--r--lib/gitlab/background_migration/backfill_note_discussion_id.rb4
-rw-r--r--lib/gitlab/background_migration/backfill_project_statistics_storage_size_without_uploads_size.rb14
-rw-r--r--lib/gitlab/background_migration/batched_migration_job.rb85
-rw-r--r--lib/gitlab/background_migration/delete_orphans_approval_merge_request_rules.rb18
-rw-r--r--lib/gitlab/background_migration/delete_orphans_approval_project_rules.rb16
-rw-r--r--lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_five_mb.rb26
-rw-r--r--lib/gitlab/background_migration/fix_approval_project_rules_without_protected_branches.rb15
-rw-r--r--lib/gitlab/background_migration/fix_security_scan_statuses.rb14
-rw-r--r--lib/gitlab/background_migration/migrate_vulnerabilities_feedback_to_vulnerabilities_state_transition.rb13
-rw-r--r--lib/gitlab/background_migration/prune_stale_project_export_jobs.rb17
-rw-r--r--lib/gitlab/background_migration/reset_status_on_container_repositories.rb139
-rw-r--r--lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb12
-rw-r--r--lib/gitlab/bitbucket_import/importer.rb2
-rw-r--r--lib/gitlab/bullet.rb2
-rw-r--r--lib/gitlab/changes_list.rb12
-rw-r--r--lib/gitlab/ci/ansi2html.rb4
-rw-r--r--lib/gitlab/ci/build/cache.rb4
-rw-r--r--lib/gitlab/ci/build/context/build.rb20
-rw-r--r--lib/gitlab/ci/build/hook.rb24
-rw-r--r--lib/gitlab/ci/config.rb26
-rw-r--r--lib/gitlab/ci/config/entry/artifacts.rb9
-rw-r--r--lib/gitlab/ci/config/entry/cache.rb16
-rw-r--r--lib/gitlab/ci/config/entry/default.rb26
-rw-r--r--lib/gitlab/ci/config/entry/hooks.rb25
-rw-r--r--lib/gitlab/ci/config/entry/id_token.rb28
-rw-r--r--lib/gitlab/ci/config/entry/job.rb21
-rw-r--r--lib/gitlab/ci/config/entry/reports.rb5
-rw-r--r--lib/gitlab/ci/config/entry/root.rb20
-rw-r--r--lib/gitlab/ci/config/entry/trigger.rb2
-rw-r--r--lib/gitlab/ci/config/entry/variable.rb85
-rw-r--r--lib/gitlab/ci/config/entry/variables.rb2
-rw-r--r--lib/gitlab/ci/config/external/file/base.rb6
-rw-r--r--lib/gitlab/ci/config/external/file/remote.rb2
-rw-r--r--lib/gitlab/ci/config/external/mapper.rb40
-rw-r--r--lib/gitlab/ci/config/external/mapper/base.rb36
-rw-r--r--lib/gitlab/ci/config/external/mapper/filter.rb22
-rw-r--r--lib/gitlab/ci/config/external/mapper/location_expander.rb42
-rw-r--r--lib/gitlab/ci/config/external/mapper/matcher.rb49
-rw-r--r--lib/gitlab/ci/config/external/mapper/normalizer.rb46
-rw-r--r--lib/gitlab/ci/config/external/mapper/variables_expander.rb49
-rw-r--r--lib/gitlab/ci/config/external/mapper/verifier.rb37
-rw-r--r--lib/gitlab/ci/config/external/processor.rb4
-rw-r--r--lib/gitlab/ci/environment_matcher.rb39
-rw-r--r--lib/gitlab/ci/lint.rb10
-rw-r--r--lib/gitlab/ci/parsers/security/common.rb8
-rw-r--r--lib/gitlab/ci/pipeline/chain/build/associations.rb3
-rw-r--r--lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb16
-rw-r--r--lib/gitlab/ci/pipeline/chain/command.rb12
-rw-r--r--lib/gitlab/ci/pipeline/chain/config/process.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/create.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/ensure_environments.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/seed.rb8
-rw-r--r--lib/gitlab/ci/pipeline/logger.rb92
-rw-r--r--lib/gitlab/ci/pipeline/metrics.rb3
-rw-r--r--lib/gitlab/ci/pipeline/seed/build.rb129
-rw-r--r--lib/gitlab/ci/pipeline/seed/build/cache.rb4
-rw-r--r--lib/gitlab/ci/pipeline/seed/pipeline.rb5
-rw-r--r--lib/gitlab/ci/pipeline/seed/stage.rb55
-rw-r--r--lib/gitlab/ci/reports/security/finding.rb6
-rw-r--r--lib/gitlab/ci/reports/security/finding_key.rb2
-rw-r--r--lib/gitlab/ci/reports/security/identifier.rb4
-rw-r--r--lib/gitlab/ci/reports/security/reports.rb23
-rw-r--r--lib/gitlab/ci/reports/test_suite.rb4
-rw-r--r--lib/gitlab/ci/runner_instructions.rb13
-rw-r--r--lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml10
-rw-r--r--lib/gitlab/ci/templates/Gradle.gitlab-ci.yml7
-rw-r--r--lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.latest.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Jobs/Container-Scanning.gitlab-ci.yml54
-rw-r--r--lib/gitlab/ci/templates/Jobs/Container-Scanning.latest.gitlab-ci.yml68
-rw-r--r--lib/gitlab/ci/templates/Jobs/Load-Performance-Testing.gitlab-ci.yml4
-rw-r--r--lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml4
-rw-r--r--lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml57
-rw-r--r--lib/gitlab/ci/templates/Security/Container-Scanning.latest.gitlab-ci.yml71
-rw-r--r--lib/gitlab/ci/templates/Security/Coverage-Fuzzing.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Security/Coverage-Fuzzing.latest.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml6
-rw-r--r--lib/gitlab/ci/templates/Security/Secure-Binaries.gitlab-ci.yml6
-rw-r--r--lib/gitlab/ci/templates/Verify/Browser-Performance.gitlab-ci.yml4
-rw-r--r--lib/gitlab/ci/templates/Verify/Browser-Performance.latest.gitlab-ci.yml4
-rw-r--r--lib/gitlab/ci/templates/Verify/Load-Performance-Testing.gitlab-ci.yml6
-rw-r--r--lib/gitlab/ci/variables/builder.rb5
-rw-r--r--lib/gitlab/ci/yaml_processor/result.rb2
-rw-r--r--lib/gitlab/cluster/rack_timeout_observer.rb7
-rw-r--r--lib/gitlab/color.rb12
-rw-r--r--lib/gitlab/config/entry/attributable.rb12
-rw-r--r--lib/gitlab/conflict/file.rb12
-rw-r--r--lib/gitlab/content_security_policy/config_loader.rb16
-rw-r--r--lib/gitlab/contributions_calendar.rb2
-rw-r--r--lib/gitlab/counters/buffered_counter.rb113
-rw-r--r--lib/gitlab/counters/legacy_counter.rb34
-rw-r--r--lib/gitlab/data_builder/deployment.rb2
-rw-r--r--lib/gitlab/database.rb3
-rw-r--r--lib/gitlab/database/bulk_update.rb2
-rw-r--r--lib/gitlab/database/count/exact_count_strategy.rb4
-rw-r--r--lib/gitlab/database/gitlab_schema.rb42
-rw-r--r--lib/gitlab/database/gitlab_schemas.yml606
-rw-r--r--lib/gitlab/database/load_balancing/connection_proxy.rb2
-rw-r--r--lib/gitlab/database/load_balancing/service_discovery.rb7
-rw-r--r--lib/gitlab/database/load_balancing/sidekiq_client_middleware.rb2
-rw-r--r--lib/gitlab/database/load_balancing/sidekiq_server_middleware.rb8
-rw-r--r--lib/gitlab/database/lock_writes_manager.rb22
-rw-r--r--lib/gitlab/database/migration.rb6
-rw-r--r--lib/gitlab/database/migration_helpers.rb44
-rw-r--r--lib/gitlab/database/migration_helpers/automatic_lock_writes_on_tables.rb75
-rw-r--r--lib/gitlab/database/migrations/batched_migration_last_id.rb50
-rw-r--r--lib/gitlab/database/migrations/runner.rb28
-rw-r--r--lib/gitlab/database/migrations/sidekiq_helpers.rb112
-rw-r--r--lib/gitlab/database/migrations/test_batched_background_runner.rb9
-rw-r--r--lib/gitlab/database/obsolete_ignored_columns.rb4
-rw-r--r--lib/gitlab/database/partitioning/single_numeric_list_partition.rb2
-rw-r--r--lib/gitlab/database/postgres_hll/buckets.rb2
-rw-r--r--lib/gitlab/database/query_analyzers/prevent_cross_database_modification.rb4
-rw-r--r--lib/gitlab/database/query_analyzers/query_recorder.rb16
-rw-r--r--lib/gitlab/database/schema_cache_with_renamed_table.rb6
-rw-r--r--lib/gitlab/database/schema_cleaner.rb18
-rw-r--r--lib/gitlab/database/tables_sorted_by_foreign_keys.rb27
-rw-r--r--lib/gitlab/database/tables_truncate.rb42
-rw-r--r--lib/gitlab/database/type/indifferent_jsonb.rb28
-rw-r--r--lib/gitlab/database_importers/work_items/hierarchy_restrictions_importer.rb37
-rw-r--r--lib/gitlab/diff/file_collection/compare.rb8
-rw-r--r--lib/gitlab/diff/file_collection/merge_request_diff_batch.rb35
-rw-r--r--lib/gitlab/diff/file_collection/paginated_diffs.rb48
-rw-r--r--lib/gitlab/diff/file_collection/paginated_merge_request_diff.rb35
-rw-r--r--lib/gitlab/diff/parser.rb2
-rw-r--r--lib/gitlab/email/receiver.rb2
-rw-r--r--lib/gitlab/error_tracking/error_repository/open_api_strategy.rb2
-rw-r--r--lib/gitlab/favicon.rb4
-rw-r--r--lib/gitlab/gfm/uploads_rewriter.rb2
-rw-r--r--lib/gitlab/git.rb1
-rw-r--r--lib/gitlab/git/base_error.rb46
-rw-r--r--lib/gitlab/git/cross_repo.rb49
-rw-r--r--lib/gitlab/git/cross_repo_comparer.rb56
-rw-r--r--lib/gitlab/git/repository.rb41
-rw-r--r--lib/gitlab/git_access.rb10
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb2
-rw-r--r--lib/gitlab/gitaly_client/namespace_service.rb7
-rw-r--r--lib/gitlab/gitaly_client/operation_service.rb20
-rw-r--r--lib/gitlab/gitaly_client/ref_service.rb26
-rw-r--r--lib/gitlab/gitaly_client/with_feature_flag_actors.rb11
-rw-r--r--lib/gitlab/github_gists_import/importer/gist_importer.rb84
-rw-r--r--lib/gitlab/github_gists_import/importer/gists_importer.rb95
-rw-r--r--lib/gitlab/github_gists_import/representation/gist.rb71
-rw-r--r--lib/gitlab/github_gists_import/status.rb43
-rw-r--r--lib/gitlab/github_import/bulk_importing.rb48
-rw-r--r--lib/gitlab/github_import/client.rb14
-rw-r--r--lib/gitlab/github_import/clients/proxy.rb59
-rw-r--r--lib/gitlab/github_import/clients/search_repos.rb66
-rw-r--r--lib/gitlab/github_import/importer/diff_note_importer.rb10
-rw-r--r--lib/gitlab/github_import/importer/issue_importer.rb3
-rw-r--r--lib/gitlab/github_import/importer/label_links_importer.rb8
-rw-r--r--lib/gitlab/github_import/importer/labels_importer.rb11
-rw-r--r--lib/gitlab/github_import/importer/lfs_objects_importer.rb4
-rw-r--r--lib/gitlab/github_import/importer/milestones_importer.rb11
-rw-r--r--lib/gitlab/github_import/importer/note_importer.rb3
-rw-r--r--lib/gitlab/github_import/importer/pull_request_importer.rb5
-rw-r--r--lib/gitlab/github_import/importer/pull_request_merged_by_importer.rb54
-rw-r--r--lib/gitlab/github_import/importer/pull_request_review_importer.rb10
-rw-r--r--lib/gitlab/github_import/importer/releases_importer.rb11
-rw-r--r--lib/gitlab/github_import/markdown/attachment.rb4
-rw-r--r--lib/gitlab/github_import/page_counter.rb6
-rw-r--r--lib/gitlab/github_import/representation/diff_note.rb44
-rw-r--r--lib/gitlab/github_import/representation/diff_notes/discussion_id.rb57
-rw-r--r--lib/gitlab/gl_repository/repo_type.rb8
-rw-r--r--lib/gitlab/gon_helper.rb2
-rw-r--r--lib/gitlab/graphql/expose_permissions.rb8
-rw-r--r--lib/gitlab/graphql/extensions/forward_only_externally_paginated_array_extension.rb19
-rw-r--r--lib/gitlab/graphql/limit/field_call_count.rb13
-rw-r--r--lib/gitlab/graphql/pagination/keyset/connection.rb21
-rw-r--r--lib/gitlab/group_search_results.rb16
-rw-r--r--lib/gitlab/http.rb2
-rw-r--r--lib/gitlab/http_connection_adapter.rb3
-rw-r--r--lib/gitlab/i18n.rb20
-rw-r--r--lib/gitlab/import_export/base/relation_factory.rb32
-rw-r--r--lib/gitlab/import_export/decompressed_archive_size_validator.rb2
-rw-r--r--lib/gitlab/import_export/group/import_export.yml20
-rw-r--r--lib/gitlab/import_export/json/legacy_reader.rb2
-rw-r--r--lib/gitlab/import_export/lfs_restorer.rb2
-rw-r--r--lib/gitlab/import_export/members_mapper.rb16
-rw-r--r--lib/gitlab/import_export/project/import_export.yml17
-rw-r--r--lib/gitlab/import_export/project/tree_saver.rb16
-rw-r--r--lib/gitlab/import_export/remote_stream_upload.rb2
-rw-r--r--lib/gitlab/import_export/repo_restorer.rb5
-rw-r--r--lib/gitlab/import_sources.rb1
-rw-r--r--lib/gitlab/incident_management/pager_duty/incident_issue_description.rb9
-rw-r--r--lib/gitlab/instrumentation/redis.rb3
-rw-r--r--lib/gitlab/instrumentation/redis_base.rb39
-rw-r--r--lib/gitlab/instrumentation/redis_cluster_validator.rb27
-rw-r--r--lib/gitlab/instrumentation/redis_interceptor.rb17
-rw-r--r--lib/gitlab/instrumentation/redis_payload.rb2
-rw-r--r--lib/gitlab/instrumentation_helper.rb9
-rw-r--r--lib/gitlab/issuable_metadata.rb4
-rw-r--r--lib/gitlab/jira/http_client.rb6
-rw-r--r--lib/gitlab/jira_import/issues_importer.rb2
-rw-r--r--lib/gitlab/jwt_authenticatable.rb2
-rw-r--r--lib/gitlab/jwt_token.rb2
-rw-r--r--lib/gitlab/kubernetes/helm/v2/install_command.rb14
-rw-r--r--lib/gitlab/kubernetes/helm/v2/patch_command.rb8
-rw-r--r--lib/gitlab/kubernetes/helm/v3/install_command.rb14
-rw-r--r--lib/gitlab/kubernetes/helm/v3/patch_command.rb8
-rw-r--r--lib/gitlab/kubernetes/kube_client.rb2
-rw-r--r--lib/gitlab/memory/jemalloc.rb33
-rw-r--r--lib/gitlab/memory/reporter.rb130
-rw-r--r--lib/gitlab/memory/reports/heap_dump.rb35
-rw-r--r--lib/gitlab/memory/reports/jemalloc_stats.rb59
-rw-r--r--lib/gitlab/memory/reports_daemon.rb63
-rw-r--r--lib/gitlab/memory/watchdog.rb89
-rw-r--r--lib/gitlab/memory/watchdog/configuration.rb20
-rw-r--r--lib/gitlab/memory/watchdog/configurator.rb64
-rw-r--r--lib/gitlab/memory/watchdog/event_reporter.rb68
-rw-r--r--lib/gitlab/memory/watchdog/monitor/heap_fragmentation.rb5
-rw-r--r--lib/gitlab/memory/watchdog/monitor/rss_memory_limit.rb27
-rw-r--r--lib/gitlab/memory/watchdog/monitor_state.rb19
-rw-r--r--lib/gitlab/memory/watchdog/sidekiq_event_reporter.rb53
-rw-r--r--lib/gitlab/merge_requests/commit_message_generator.rb95
-rw-r--r--lib/gitlab/merge_requests/message_generator.rb142
-rw-r--r--lib/gitlab/metrics.rb4
-rw-r--r--lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb8
-rw-r--r--lib/gitlab/metrics/dashboard/validator.rb2
-rw-r--r--lib/gitlab/metrics/global_search_slis.rb5
-rw-r--r--lib/gitlab/metrics/rails_slis.rb11
-rw-r--r--lib/gitlab/metrics/requests_rack_middleware.rb25
-rw-r--r--lib/gitlab/metrics/subscribers/ldap.rb103
-rw-r--r--lib/gitlab/metrics/subscribers/rails_cache.rb11
-rw-r--r--lib/gitlab/middleware/compressed_json.rb27
-rw-r--r--lib/gitlab/middleware/go.rb4
-rw-r--r--lib/gitlab/other_markup.rb22
-rw-r--r--lib/gitlab/pages/cache_control.rb66
-rw-r--r--lib/gitlab/pagination/cursor_based_keyset.rb2
-rw-r--r--lib/gitlab/pagination/offset_pagination.rb17
-rw-r--r--lib/gitlab/patch/prependable.rb2
-rw-r--r--lib/gitlab/phabricator_import/project_creator.rb10
-rw-r--r--lib/gitlab/process_management.rb9
-rw-r--r--lib/gitlab/process_supervisor.rb2
-rw-r--r--lib/gitlab/profiler.rb6
-rw-r--r--lib/gitlab/project_search_results.rb22
-rw-r--r--lib/gitlab/project_template.rb4
-rw-r--r--lib/gitlab/prometheus_client.rb2
-rw-r--r--lib/gitlab/quick_actions/issuable_actions.rb2
-rw-r--r--lib/gitlab/quick_actions/issue_actions.rb4
-rw-r--r--lib/gitlab/quick_actions/issue_and_merge_request_actions.rb6
-rw-r--r--lib/gitlab/rack_attack.rb2
-rw-r--r--lib/gitlab/rack_attack/request.rb76
-rw-r--r--lib/gitlab/redis/multi_store.rb2
-rw-r--r--lib/gitlab/redis/wrapper.rb15
-rw-r--r--lib/gitlab/reference_extractor.rb5
-rw-r--r--lib/gitlab/repository_size_error_message.rb2
-rw-r--r--lib/gitlab/safe_request_store.rb2
-rw-r--r--lib/gitlab/shell.rb4
-rw-r--r--lib/gitlab/sidekiq_daemon/memory_killer.rb28
-rw-r--r--lib/gitlab/sidekiq_daemon/monitor.rb21
-rw-r--r--lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb2
-rw-r--r--lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies.rb6
-rw-r--r--lib/gitlab/sidekiq_status.rb2
-rw-r--r--lib/gitlab/slash_commands/application_help.rb11
-rw-r--r--lib/gitlab/slash_commands/command.rb6
-rw-r--r--lib/gitlab/slash_commands/deploy.rb2
-rw-r--r--lib/gitlab/sql/pattern.rb10
-rw-r--r--lib/gitlab/ssh/signature.rb20
-rw-r--r--lib/gitlab/task_helpers.rb12
-rw-r--r--lib/gitlab/template/base_template.rb4
-rw-r--r--lib/gitlab/timeless.rb4
-rw-r--r--lib/gitlab/tracking/destinations/snowplow.rb28
-rw-r--r--lib/gitlab/tracking/incident_management.rb2
-rw-r--r--lib/gitlab/tracking/service_ping_context.rb48
-rw-r--r--lib/gitlab/url_blocker.rb6
-rw-r--r--lib/gitlab/usage/metrics/aggregates.rb1
-rw-r--r--lib/gitlab/usage/metrics/aggregates/aggregate.rb2
-rw-r--r--lib/gitlab/usage/metrics/aggregates/sources/calculations/intersection.rb6
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/count_merge_request_authors_metric.rb15
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/database_metric.rb2
-rw-r--r--lib/gitlab/usage/service_ping/payload_keys_processor.rb4
-rw-r--r--lib/gitlab/usage/time_frame.rb4
-rw-r--r--lib/gitlab/usage_data.rb32
-rw-r--r--lib/gitlab/usage_data_counters/editor_unique_counter.rb11
-rw-r--r--lib/gitlab/usage_data_counters/hll_redis_counter.rb60
-rw-r--r--lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb11
-rw-r--r--lib/gitlab/usage_data_counters/known_events/ci_templates.yml12
-rw-r--r--lib/gitlab/usage_data_counters/known_events/code_review_events.yml30
-rw-r--r--lib/gitlab/usage_data_counters/known_events/common.yml1
-rw-r--r--lib/gitlab/usage_data_counters/merge_request_activity_unique_counter.rb33
-rw-r--r--lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb11
-rw-r--r--lib/gitlab/utils/delegator_override/validator.rb2
-rw-r--r--lib/gitlab/utils/override.rb4
-rw-r--r--lib/gitlab/utils/sanitize_node_link.rb2
-rw-r--r--lib/gitlab/utils/strong_memoize.rb23
-rw-r--r--lib/gitlab/visibility_level.rb2
-rw-r--r--lib/gitlab/work_items/work_item_hierarchy.rb48
-rw-r--r--lib/gitlab/workhorse.rb10
-rw-r--r--lib/gitlab/x509/signature.rb17
-rw-r--r--lib/gitlab_edition.rb6
-rw-r--r--lib/google_api/cloud_platform/client.rb4
-rw-r--r--lib/kramdown/converter/commonmark.rb8
-rw-r--r--lib/pager_duty/validator/schemas/message.json101
-rw-r--r--lib/pager_duty/webhook_payload_parser.rb36
-rw-r--r--lib/security/ci_configuration/container_scanning_build_action.rb2
-rw-r--r--lib/security/ci_configuration/sast_build_action.rb2
-rw-r--r--lib/security/weak_passwords.rb12
-rw-r--r--lib/serializers/json.rb18
-rw-r--r--lib/service_ping/build_payload.rb4
-rw-r--r--lib/sidebars/groups/menus/packages_registries_menu.rb6
-rw-r--r--lib/sidebars/menu.rb3
-rw-r--r--lib/sidebars/projects/menus/analytics_menu.rb6
-rw-r--r--lib/sidebars/projects/menus/deployments_menu.rb2
-rw-r--r--lib/sidebars/projects/menus/hidden_menu.rb8
-rw-r--r--lib/sidebars/projects/menus/infrastructure_menu.rb6
-rw-r--r--lib/sidebars/projects/menus/monitor_menu.rb6
-rw-r--r--lib/sidebars/projects/menus/repository_menu.rb32
-rwxr-xr-xlib/support/init.d/gitlab6
-rw-r--r--lib/support/systemd/gitlab-sidekiq.service5
-rw-r--r--lib/system_check/app/gitlab_cable_config_exists_check.rb26
-rw-r--r--lib/system_check/app/gitlab_resque_config_exists_check.rb26
-rw-r--r--lib/system_check/helpers.rb1
-rw-r--r--lib/system_check/multi_check_helpers.rb32
-rw-r--r--lib/system_check/rake_task/app_task.rb2
-rw-r--r--lib/system_check/sidekiq_check.rb6
-rw-r--r--lib/tasks/contracts/merge_requests.rake26
-rw-r--r--lib/tasks/contracts/pipeline_schedules.rake9
-rw-r--r--lib/tasks/contracts/pipelines.rake31
-rw-r--r--lib/tasks/dev.rake15
-rw-r--r--lib/tasks/gitlab/assets.rake6
-rw-r--r--lib/tasks/gitlab/cleanup.rake10
-rw-r--r--lib/tasks/gitlab/db.rake3
-rw-r--r--lib/tasks/gitlab/db/lock_writes.rake5
-rw-r--r--lib/tasks/gitlab/feature_categories.rake80
-rw-r--r--lib/tasks/gitlab/info.rake8
-rw-r--r--lib/tasks/gitlab/shell.rake12
-rw-r--r--lib/tasks/gitlab/sidekiq.rake11
-rw-r--r--lib/tasks/gitlab/tw/codeowners.rake21
-rw-r--r--lib/tasks/gitlab/update_templates.rake13
-rw-r--r--lib/tasks/gitlab/usage_data.rake5
-rw-r--r--lib/version_check.rb15
-rw-r--r--locale/am_ET/gitlab.po1302
-rw-r--r--locale/ar_SA/gitlab.po1346
-rw-r--r--locale/as_IN/gitlab.po1302
-rw-r--r--locale/az_AZ/gitlab.po1302
-rw-r--r--locale/ba_RU/gitlab.po1291
-rw-r--r--locale/bg/gitlab.po1302
-rw-r--r--locale/bg/gitlab.po.time_stamp0
-rw-r--r--locale/bn_BD/gitlab.po1302
-rw-r--r--locale/bn_IN/gitlab.po1302
-rw-r--r--locale/br_FR/gitlab.po1335
-rw-r--r--locale/bs_BA/gitlab.po1313
-rw-r--r--locale/ca_ES/gitlab.po1302
-rw-r--r--locale/cs_CZ/gitlab.po1324
-rw-r--r--locale/cy_GB/gitlab.po1346
-rw-r--r--locale/da_DK/gitlab.po1332
-rw-r--r--locale/de/gitlab.po1320
-rw-r--r--locale/de/gitlab.po.time_stamp0
-rw-r--r--locale/el_GR/gitlab.po1302
-rw-r--r--locale/en_GB/gitlab.po1310
-rw-r--r--locale/eo/gitlab.po1302
-rw-r--r--locale/eo/gitlab.po.time_stamp0
-rw-r--r--locale/es/gitlab.po1330
-rw-r--r--locale/es/gitlab.po.time_stamp0
-rw-r--r--locale/et_EE/gitlab.po1302
-rw-r--r--locale/fa_IR/gitlab.po1302
-rw-r--r--locale/fi_FI/gitlab.po1302
-rw-r--r--locale/fil_PH/gitlab.po1302
-rw-r--r--locale/fr/gitlab.po4300
-rw-r--r--locale/fr/gitlab.po.time_stamp0
-rw-r--r--locale/gitlab.pot1616
-rw-r--r--locale/gl_ES/gitlab.po1302
-rw-r--r--locale/he_IL/gitlab.po1324
-rw-r--r--locale/hi_IN/gitlab.po1302
-rw-r--r--locale/hr_HR/gitlab.po1313
-rw-r--r--locale/hu_HU/gitlab.po1302
-rw-r--r--locale/hy_AM/gitlab.po1302
-rw-r--r--locale/id_ID/gitlab.po1291
-rw-r--r--locale/ig_NG/gitlab.po1291
-rw-r--r--locale/is_IS/gitlab.po1302
-rw-r--r--locale/it/gitlab.po1304
-rw-r--r--locale/it/gitlab.po.time_stamp0
-rw-r--r--locale/ja/gitlab.po1393
-rw-r--r--locale/ja/gitlab.po.time_stamp0
-rw-r--r--locale/ka_GE/gitlab.po1302
-rw-r--r--locale/kab/gitlab.po1302
-rw-r--r--locale/ko/gitlab.po1317
-rw-r--r--locale/ko/gitlab.po.time_stamp0
-rw-r--r--locale/ku_TR/gitlab.po1302
-rw-r--r--locale/ky_KG/gitlab.po1302
-rw-r--r--locale/lt_LT/gitlab.po1324
-rw-r--r--locale/mk_MK/gitlab.po1302
-rw-r--r--locale/ml_IN/gitlab.po1302
-rw-r--r--locale/mn_MN/gitlab.po1302
-rw-r--r--locale/nb_NO/gitlab.po1324
-rw-r--r--locale/nl_NL/gitlab.po1302
-rw-r--r--locale/or_IN/gitlab.po1302
-rw-r--r--locale/pa_IN/gitlab.po1302
-rw-r--r--locale/pa_PK/gitlab.po1302
-rw-r--r--locale/pl_PL/gitlab.po1330
-rw-r--r--locale/pt_BR/gitlab.po1434
-rw-r--r--locale/pt_BR/gitlab.po.time_stamp0
-rw-r--r--locale/pt_PT/gitlab.po1304
-rw-r--r--locale/ro_RO/gitlab.po1419
-rw-r--r--locale/ru/gitlab.po1902
-rw-r--r--locale/ru/gitlab.po.time_stamp0
-rw-r--r--locale/si_LK/gitlab.po1848
-rw-r--r--locale/sk_SK/gitlab.po1324
-rw-r--r--locale/sl_SI/gitlab.po1324
-rw-r--r--locale/sq_AL/gitlab.po1302
-rw-r--r--locale/sr_CS/gitlab.po1313
-rw-r--r--locale/sr_SP/gitlab.po1313
-rw-r--r--locale/sv_SE/gitlab.po1304
-rw-r--r--locale/sw_KE/gitlab.po1302
-rw-r--r--locale/ta_IN/gitlab.po1302
-rw-r--r--locale/th_TH/gitlab.po1291
-rw-r--r--locale/tr_TR/gitlab.po1316
-rw-r--r--locale/uk/gitlab.po1554
-rw-r--r--locale/uk/gitlab.po.time_stamp0
-rw-r--r--locale/ur_PK/gitlab.po1302
-rw-r--r--locale/uz_UZ/gitlab.po1302
-rw-r--r--locale/vi_VN/gitlab.po1291
-rw-r--r--locale/zh_CN/gitlab.po1507
-rw-r--r--locale/zh_CN/gitlab.po.time_stamp0
-rw-r--r--locale/zh_HK/gitlab.po1295
-rw-r--r--locale/zh_HK/gitlab.po.time_stamp0
-rw-r--r--locale/zh_TW/gitlab.po2035
-rw-r--r--locale/zh_TW/gitlab.po.time_stamp0
-rw-r--r--metrics_server/metrics_server.rb1
-rw-r--r--package.json40
-rw-r--r--qa/Dockerfile19
-rw-r--r--qa/Gemfile18
-rw-r--r--qa/Gemfile.lock51
-rw-r--r--qa/README.md42
-rw-r--r--qa/lib/gitlab/page/group/settings/usage_quotas.rb12
-rw-r--r--qa/qa.rb3
-rw-r--r--qa/qa/ce/strategy.rb7
-rw-r--r--qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb17
-rw-r--r--qa/qa/fixtures/package_managers/maven/group/settings_with_pat.xml.erb2
-rw-r--r--qa/qa/flow/alert_settings.rb32
-rw-r--r--qa/qa/flow/login.rb6
-rw-r--r--qa/qa/git/location.rb5
-rw-r--r--qa/qa/page/admin/menu.rb12
-rw-r--r--qa/qa/page/base.rb39
-rw-r--r--qa/qa/page/component/blob_content.rb6
-rw-r--r--qa/qa/page/component/custom_metric.rb49
-rw-r--r--qa/qa/page/component/dropdown.rb110
-rw-r--r--qa/qa/page/component/invite_members_modal.rb5
-rw-r--r--qa/qa/page/component/issuable/sidebar.rb8
-rw-r--r--qa/qa/page/component/snippet.rb2
-rw-r--r--qa/qa/page/file/edit.rb4
-rw-r--r--qa/qa/page/group/settings/general.rb40
-rw-r--r--qa/qa/page/group/settings/group_deploy_tokens.rb6
-rw-r--r--qa/qa/page/group/show.rb2
-rw-r--r--qa/qa/page/main/login.rb4
-rw-r--r--qa/qa/page/main/menu.rb2
-rw-r--r--qa/qa/page/merge_request/new.rb2
-rw-r--r--qa/qa/page/merge_request/show.rb11
-rw-r--r--qa/qa/page/profile/two_factor_auth.rb3
-rw-r--r--qa/qa/page/project/import/github.rb14
-rw-r--r--qa/qa/page/project/infrastructure/kubernetes/show.rb3
-rw-r--r--qa/qa/page/project/issue/show.rb4
-rw-r--r--qa/qa/page/project/job/show.rb2
-rw-r--r--qa/qa/page/project/monitor/alerts/index.rb21
-rw-r--r--qa/qa/page/project/monitor/metrics/show.rb134
-rw-r--r--qa/qa/page/project/pipeline/new.rb28
-rw-r--r--qa/qa/page/project/pipeline/show.rb10
-rw-r--r--qa/qa/page/project/pipeline_editor/new.rb2
-rw-r--r--qa/qa/page/project/pipeline_editor/show.rb20
-rw-r--r--qa/qa/page/project/settings/alerts.rb56
-rw-r--r--qa/qa/page/project/settings/merge_request.rb2
-rw-r--r--qa/qa/page/project/settings/monitor.rb10
-rw-r--r--qa/qa/page/project/settings/protected_branches.rb6
-rw-r--r--qa/qa/page/project/settings/repository.rb2
-rw-r--r--qa/qa/page/project/settings/services/jenkins.rb2
-rw-r--r--qa/qa/page/project/settings/services/jira.rb2
-rw-r--r--qa/qa/page/project/settings/services/pipeline_status_emails.rb3
-rw-r--r--qa/qa/page/project/settings/services/prometheus.rb36
-rw-r--r--qa/qa/page/project/show.rb5
-rw-r--r--qa/qa/page/project/sub_menus/monitor.rb8
-rw-r--r--qa/qa/page/project/web_ide/edit.rb8
-rw-r--r--qa/qa/resource/api_fabricator.rb4
-rw-r--r--qa/qa/resource/base.rb6
-rw-r--r--qa/qa/resource/bulk_import_group.rb8
-rw-r--r--qa/qa/resource/group.rb10
-rw-r--r--qa/qa/resource/group_base.rb1
-rw-r--r--qa/qa/resource/issuable.rb3
-rw-r--r--qa/qa/resource/merge_request.rb4
-rw-r--r--qa/qa/resource/protected_branch.rb4
-rw-r--r--qa/qa/resource/reusable.rb4
-rw-r--r--qa/qa/resource/runner.rb166
-rw-r--r--qa/qa/resource/sandbox.rb2
-rw-r--r--qa/qa/resource/ssh_key.rb4
-rw-r--r--qa/qa/resource/user.rb2
-rw-r--r--qa/qa/runtime/api/client.rb8
-rw-r--r--qa/qa/runtime/api/repository_storage_moves.rb2
-rw-r--r--qa/qa/runtime/browser.rb5
-rw-r--r--qa/qa/runtime/env.rb11
-rw-r--r--qa/qa/runtime/ip_address.rb13
-rw-r--r--qa/qa/runtime/logger.rb26
-rw-r--r--qa/qa/runtime/script_extensions/interceptor.js50
-rw-r--r--qa/qa/scenario/test/integration/import.rb13
-rw-r--r--qa/qa/service/docker_run/base.rb6
-rw-r--r--qa/qa/service/docker_run/gitlab_runner.rb6
-rw-r--r--qa/qa/service/praefect_manager.rb171
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb12
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb10
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb10
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb8
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb4
-rw-r--r--qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb2
-rw-r--r--qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb6
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb76
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb33
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb196
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_members_spec.rb32
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb69
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb4
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb50
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb10
-rw-r--r--qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb11
-rw-r--r--qa/qa/specs/features/api/4_verify/file_variable_spec.rb5
-rw-r--r--qa/qa/specs/features/api/4_verify/remove_runner_spec.rb11
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/group/gitlab_migration_group_spec.rb77
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/group/transfer_group_spec.rb45
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb78
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/migration/gitlab_migration_group_spec.rb40
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb94
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/design_management/archive_design_content_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/design_management/modify_design_content_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/pages/new_static_page_spec.rb17
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb15
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb8
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb14
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb8
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb5
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb11
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb9
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb17
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/server_hooks_custom_error_message_spec.rb10
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb9
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/web_terminal_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb90
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb93
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/mr_event_rule_pipeline_spec.rb14
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb25
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb77
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb9
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/rubygems_registry_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/8_monitor/.gitkeep0
-rw-r--r--qa/qa/specs/features/browser_ui/8_monitor/incident_management/http_endpoint_integration_creates_alert_spec.rb36
-rw-r--r--qa/qa/specs/features/sanity/interception_spec.rb39
-rw-r--r--qa/qa/specs/features/shared_contexts/import/github_import_shared_context.rb14
-rw-r--r--qa/qa/specs/features/shared_contexts/import/gitlab_group_migration_common.rb106
-rw-r--r--qa/qa/specs/features/shared_contexts/import/gitlab_project_migration_common.rb77
-rw-r--r--qa/qa/specs/helpers/feature_flag.rb4
-rw-r--r--qa/qa/specs/spec_helper.rb8
-rw-r--r--qa/qa/support/data/github.rb15
-rw-r--r--qa/qa/support/data/license.rb27
-rw-r--r--qa/qa/support/formatters/allure_metadata_formatter.rb11
-rw-r--r--qa/qa/support/formatters/test_metrics_formatter.rb67
-rw-r--r--qa/qa/support/helpers/mask_token.rb6
-rw-r--r--qa/qa/support/influxdb_tools.rb2
-rw-r--r--qa/qa/support/knapsack_report.rb6
-rw-r--r--qa/qa/support/loglinking.rb108
-rw-r--r--qa/qa/support/page/logging.rb5
-rw-r--r--qa/qa/support/page_error_checker.rb12
-rw-r--r--qa/qa/support/run.rb4
-rw-r--r--qa/qa/support/ssh.rb2
-rw-r--r--qa/qa/tools/long_running_spec_reporter.rb6
-rw-r--r--qa/qa/tools/reliable_report.rb6
-rw-r--r--qa/qa/tools/test_resources_handler.rb4
-rw-r--r--qa/qa/vendor/smocker/event_payload.rb12
-rw-r--r--qa/qa/vendor/smocker/smocker_api.rb1
-rw-r--r--qa/spec/page/logging_spec.rb9
-rw-r--r--qa/spec/resource/api_fabricator_spec.rb9
-rw-r--r--qa/spec/resource/base_spec.rb10
-rw-r--r--qa/spec/resource/user_spec.rb23
-rw-r--r--qa/spec/runtime/env_spec.rb2
-rw-r--r--qa/spec/support/formatters/test_metrics_formatter_spec.rb21
-rw-r--r--qa/spec/support/loglinking_spec.rb103
-rw-r--r--qa/spec/support/page_error_checker_spec.rb36
-rw-r--r--qa/spec/tools/reliable_report_spec.rb14
-rw-r--r--results.txt0
-rw-r--r--rubocop/cop/database/multiple_databases.rb1
-rw-r--r--rubocop/cop/feature_flag_usage.rb19
-rw-r--r--rubocop/cop/gitlab/strong_memoize_attr.rb73
-rw-r--r--rubocop/cop/graphql/descriptions.rb25
-rw-r--r--rubocop/cop/migration/add_column_with_default.rb23
-rw-r--r--rubocop/cop/migration/add_limit_to_text_columns.rb6
-rw-r--r--rubocop/cop/migration/batch_migrations_post_only.rb37
-rw-r--r--rubocop/cop/migration/safer_boolean_column.rb4
-rw-r--r--rubocop/cop/migration/versioned_migration_class.rb5
-rw-r--r--rubocop/cop/rspec/avoid_test_prof.rb66
-rw-r--r--rubocop/cop/rspec/timecop_freeze.rb41
-rw-r--r--rubocop/cop/rspec/timecop_travel.rb41
-rw-r--r--rubocop/migration_helpers.rb4
-rw-r--r--rubocop/rubocop-code_reuse.yml1
-rw-r--r--scripts/api/create_issue_discussion.rb32
-rwxr-xr-xscripts/api/download_job_artifact.rb94
-rwxr-xr-xscripts/build_assets_image75
-rwxr-xr-xscripts/build_qa_image4
-rwxr-xr-xscripts/check-template-changes105
-rwxr-xr-xscripts/create-pipeline-failure-incident.rb24
-rw-r--r--scripts/lib/gitlab.rb4
-rw-r--r--scripts/lib/glfm/constants.rb21
-rw-r--r--scripts/lib/glfm/render_static_html.rb4
-rw-r--r--scripts/lib/glfm/render_wysiwyg_html_and_json.js1
-rw-r--r--scripts/lib/glfm/shared.rb1
-rw-r--r--scripts/lib/glfm/specification_html_template.erb244
-rw-r--r--scripts/lib/glfm/update_specification.rb118
-rwxr-xr-xscripts/lint-doc-quality.sh24
-rwxr-xr-xscripts/review_apps/automated_cleanup.rb31
-rw-r--r--scripts/review_apps/base-config.yaml6
-rwxr-xr-xscripts/review_apps/gcp-quotas-checks.rb46
-rwxr-xr-xscripts/review_apps/gcp_cleanup.sh160
-rwxr-xr-xscripts/review_apps/k8s-resources-count-checks.sh90
-rwxr-xr-xscripts/review_apps/review-apps.sh50
-rw-r--r--scripts/rspec_helpers.sh127
-rwxr-xr-xscripts/rubocop-max-files-in-cache-check28
-rwxr-xr-xscripts/static-analysis3
-rwxr-xr-xscripts/trigger-build.rb5
-rwxr-xr-xscripts/undercoverage9
-rwxr-xr-xscripts/used-feature-flags3
-rw-r--r--scripts/utils.sh43
-rwxr-xr-xscripts/verify-tff-mapping114
-rw-r--r--sidekiq_cluster/cli.rb15
-rw-r--r--sidekiq_cluster/sidekiq_cluster.rb10
-rw-r--r--spec/bin/audit_event_type_spec.rb32
-rw-r--r--spec/bin/feature_flag_spec.rb10
-rw-r--r--spec/commands/sidekiq_cluster/cli_spec.rb49
-rw-r--r--spec/config/application_spec.rb2
-rw-r--r--spec/config/inject_enterprise_edition_module_spec.rb2
-rw-r--r--spec/config/mail_room_spec.rb2
-rw-r--r--spec/config/object_store_settings_spec.rb9
-rw-r--r--spec/config/settings_spec.rb2
-rw-r--r--spec/config/smime_signature_settings_spec.rb2
-rw-r--r--spec/contracts/consumer/.node-version1
-rw-r--r--spec/contracts/consumer/fixtures/project/merge_requests/diffs_batch.fixture.js (renamed from spec/contracts/consumer/fixtures/project/merge_request/diffs_batch.fixture.js)0
-rw-r--r--spec/contracts/consumer/fixtures/project/merge_requests/diffs_metadata.fixture.js (renamed from spec/contracts/consumer/fixtures/project/merge_request/diffs_metadata.fixture.js)0
-rw-r--r--spec/contracts/consumer/fixtures/project/merge_requests/discussions.fixture.js (renamed from spec/contracts/consumer/fixtures/project/merge_request/discussions.fixture.js)0
-rw-r--r--spec/contracts/consumer/fixtures/project/pipeline_schedules/update_pipeline_schedule.fixture.js (renamed from spec/contracts/consumer/fixtures/project/pipeline_schedule/update_pipeline_schedule.fixture.js)0
-rw-r--r--spec/contracts/consumer/fixtures/project/pipelines/create_a_new_pipeline.fixture.js (renamed from spec/contracts/consumer/fixtures/project/pipeline/create_a_new_pipeline.fixture.js)0
-rw-r--r--spec/contracts/consumer/fixtures/project/pipelines/delete_pipeline.fixture.js (renamed from spec/contracts/consumer/fixtures/project/pipeline/delete_pipeline.fixture.js)0
-rw-r--r--spec/contracts/consumer/fixtures/project/pipelines/get_list_project_pipelines.fixture.js (renamed from spec/contracts/consumer/fixtures/project/pipeline/get_list_project_pipelines.fixture.js)0
-rw-r--r--spec/contracts/consumer/fixtures/project/pipelines/get_pipeline_header_data.fixture.js (renamed from spec/contracts/consumer/fixtures/project/pipeline/get_pipeline_header_data.fixture.js)0
-rw-r--r--spec/contracts/consumer/package.json3
-rw-r--r--spec/contracts/consumer/specs/project/merge_request/show.spec.js108
-rw-r--r--spec/contracts/consumer/specs/project/merge_requests/show.spec.js108
-rw-r--r--spec/contracts/consumer/specs/project/pipeline/index.spec.js40
-rw-r--r--spec/contracts/consumer/specs/project/pipeline/new.spec.js41
-rw-r--r--spec/contracts/consumer/specs/project/pipeline/show.spec.js89
-rw-r--r--spec/contracts/consumer/specs/project/pipeline_schedule/edit.spec.js41
-rw-r--r--spec/contracts/consumer/specs/project/pipeline_schedules/edit.spec.js41
-rw-r--r--spec/contracts/consumer/specs/project/pipelines/index.spec.js40
-rw-r--r--spec/contracts/consumer/specs/project/pipelines/new.spec.js41
-rw-r--r--spec/contracts/consumer/specs/project/pipelines/show.spec.js89
-rw-r--r--spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_batch_endpoint.json229
-rw-r--r--spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_metadata_endpoint.json223
-rw-r--r--spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_discussions_endpoint.json236
-rw-r--r--spec/contracts/contracts/project/merge_requests/show/mergerequests#show-get_diffs_batch.json229
-rw-r--r--spec/contracts/contracts/project/merge_requests/show/mergerequests#show-get_diffs_metadata.json223
-rw-r--r--spec/contracts/contracts/project/merge_requests/show/mergerequests#show-get_discussions.json236
-rw-r--r--spec/contracts/contracts/project/pipeline/index/pipelines#index-get_list_project_pipelines.json478
-rw-r--r--spec/contracts/contracts/project/pipeline/new/pipelines#new-post_create_a_new_pipeline.json43
-rw-r--r--spec/contracts/contracts/project/pipeline_schedule/edit/pipelineschedules#edit-put_edit_a_pipeline_schedule.json48
-rw-r--r--spec/contracts/contracts/project/pipeline_schedules/edit/pipelineschedules#edit-put_edit_a_pipeline_schedule.json48
-rw-r--r--spec/contracts/contracts/project/pipelines/index/pipelines#index-get_list_project_pipelines.json469
-rw-r--r--spec/contracts/contracts/project/pipelines/new/pipelines#new-post_create_a_new_pipeline.json43
-rw-r--r--spec/contracts/contracts/project/pipelines/show/pipelines#show-delete_pipeline.json (renamed from spec/contracts/contracts/project/pipeline/show/pipelines#show-delete_pipeline.json)0
-rw-r--r--spec/contracts/contracts/project/pipelines/show/pipelines#show-get_pipeline_header_data.json (renamed from spec/contracts/contracts/project/pipeline/show/pipelines#show-get_pipeline_header_data.json)0
-rw-r--r--spec/contracts/provider/helpers/contract_source_helper.rb53
-rw-r--r--spec/contracts/provider/helpers/publish_contract_helper.rb12
-rw-r--r--spec/contracts/provider/pact_helpers/project/merge_request/show/diffs_batch_helper.rb18
-rw-r--r--spec/contracts/provider/pact_helpers/project/merge_request/show/diffs_metadata_helper.rb20
-rw-r--r--spec/contracts/provider/pact_helpers/project/merge_request/show/discussions_helper.rb20
-rw-r--r--spec/contracts/provider/pact_helpers/project/merge_requests/show/get_diffs_batch_helper.rb24
-rw-r--r--spec/contracts/provider/pact_helpers/project/merge_requests/show/get_diffs_metadata_helper.rb24
-rw-r--r--spec/contracts/provider/pact_helpers/project/merge_requests/show/get_discussions_helper.rb24
-rw-r--r--spec/contracts/provider/pact_helpers/project/pipeline/index/create_a_new_pipeline_helper.rb20
-rw-r--r--spec/contracts/provider/pact_helpers/project/pipeline/index/get_list_project_pipelines_helper.rb20
-rw-r--r--spec/contracts/provider/pact_helpers/project/pipeline/show/delete_pipeline_helper.rb21
-rw-r--r--spec/contracts/provider/pact_helpers/project/pipeline/show/get_pipeline_header_data_helper.rb22
-rw-r--r--spec/contracts/provider/pact_helpers/project/pipeline_schedule/update_pipeline_schedule_helper.rb20
-rw-r--r--spec/contracts/provider/pact_helpers/project/pipeline_schedules/edit/put_edit_a_pipeline_schedule_helper.rb24
-rw-r--r--spec/contracts/provider/pact_helpers/project/pipelines/index/get_list_project_pipelines_helper.rb24
-rw-r--r--spec/contracts/provider/pact_helpers/project/pipelines/new/post_create_a_new_pipeline_helper.rb24
-rw-r--r--spec/contracts/provider/pact_helpers/project/pipelines/show/delete_pipeline_helper.rb24
-rw-r--r--spec/contracts/provider/pact_helpers/project/pipelines/show/get_pipeline_header_data_helper.rb24
-rw-r--r--spec/contracts/provider/spec_helper.rb9
-rw-r--r--spec/contracts/provider/states/project/merge_request/show_state.rb47
-rw-r--r--spec/contracts/provider/states/project/merge_requests/show_state.rb47
-rw-r--r--spec/contracts/provider/states/project/pipeline_schedules/edit_state.rb (renamed from spec/contracts/provider/states/project/pipeline_schedule/edit_state.rb)0
-rw-r--r--spec/contracts/provider/states/project/pipelines/index_state.rb (renamed from spec/contracts/provider/states/project/pipeline/index_state.rb)0
-rw-r--r--spec/contracts/provider/states/project/pipelines/new_state.rb (renamed from spec/contracts/provider/states/project/pipeline/new_state.rb)0
-rw-r--r--spec/contracts/provider/states/project/pipelines/show_state.rb (renamed from spec/contracts/provider/states/project/pipeline/show_state.rb)0
-rw-r--r--spec/contracts/provider_specs/helpers/provider/contract_source_helper_spec.rb96
-rw-r--r--spec/controllers/admin/application_settings/appearances_controller_spec.rb1
-rw-r--r--spec/controllers/admin/application_settings_controller_spec.rb20
-rw-r--r--spec/controllers/admin/groups_controller_spec.rb44
-rw-r--r--spec/controllers/admin/hooks_controller_spec.rb2
-rw-r--r--spec/controllers/admin/plan_limits_controller_spec.rb20
-rw-r--r--spec/controllers/admin/runner_projects_controller_spec.rb2
-rw-r--r--spec/controllers/admin/runners_controller_spec.rb2
-rw-r--r--spec/controllers/concerns/check_rate_limit_spec.rb6
-rw-r--r--spec/controllers/concerns/issuable_actions_spec.rb6
-rw-r--r--spec/controllers/dashboard/todos_controller_spec.rb35
-rw-r--r--spec/controllers/explore/projects_controller_spec.rb1
-rw-r--r--spec/controllers/graphql_controller_spec.rb19
-rw-r--r--spec/controllers/groups/application_controller_spec.rb45
-rw-r--r--spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb8
-rw-r--r--spec/controllers/groups/labels_controller_spec.rb2
-rw-r--r--spec/controllers/groups/registry/repositories_controller_spec.rb2
-rw-r--r--spec/controllers/groups/runners_controller_spec.rb2
-rw-r--r--spec/controllers/groups/settings/repository_controller_spec.rb2
-rw-r--r--spec/controllers/import/bitbucket_controller_spec.rb24
-rw-r--r--spec/controllers/import/github_controller_spec.rb274
-rw-r--r--spec/controllers/jira_connect/events_controller_spec.rb38
-rw-r--r--spec/controllers/omniauth_callbacks_controller_spec.rb26
-rw-r--r--spec/controllers/profiles/keys_controller_spec.rb3
-rw-r--r--spec/controllers/projects/environments_controller_spec.rb28
-rw-r--r--spec/controllers/projects/graphs_controller_spec.rb44
-rw-r--r--spec/controllers/projects/hooks_controller_spec.rb24
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb38
-rw-r--r--spec/controllers/projects/labels_controller_spec.rb2
-rw-r--r--spec/controllers/projects/merge_requests/conflicts_controller_spec.rb14
-rw-r--r--spec/controllers/projects/merge_requests/creations_controller_spec.rb14
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb2
-rw-r--r--spec/controllers/projects/notes_controller_spec.rb3
-rw-r--r--spec/controllers/projects/pipeline_schedules_controller_spec.rb8
-rw-r--r--spec/controllers/projects/refs_controller_spec.rb80
-rw-r--r--spec/controllers/projects/registry/repositories_controller_spec.rb16
-rw-r--r--spec/controllers/projects/runners_controller_spec.rb2
-rw-r--r--spec/controllers/projects/service_ping_controller_spec.rb9
-rw-r--r--spec/controllers/projects/settings/ci_cd_controller_spec.rb53
-rw-r--r--spec/controllers/projects/settings/integrations_controller_spec.rb222
-rw-r--r--spec/controllers/projects/settings/repository_controller_spec.rb38
-rw-r--r--spec/controllers/projects_controller_spec.rb53
-rw-r--r--spec/controllers/registrations_controller_spec.rb130
-rw-r--r--spec/controllers/search_controller_spec.rb6
-rw-r--r--spec/controllers/sessions_controller_spec.rb16
-rw-r--r--spec/db/development/create_work_item_hierarchy_restrictions_spec.rb9
-rw-r--r--spec/db/docs_spec.rb101
-rw-r--r--spec/db/migration_spec.rb1
-rw-r--r--spec/db/production/create_work_item_hierarchy_restrictions_spec.rb9
-rw-r--r--spec/db/schema_spec.rb127
-rw-r--r--spec/factories/achievements/achievements.rb9
-rw-r--r--spec/factories/bulk_import.rb1
-rw-r--r--spec/factories/bulk_import/trackers.rb9
-rw-r--r--spec/factories/ci/build_runner_sessions.rb8
-rw-r--r--spec/factories/ci/builds.rb8
-rw-r--r--spec/factories/ci/pipelines.rb4
-rw-r--r--spec/factories/ci/resource.rb1
-rw-r--r--spec/factories/ci/sources/pipelines.rb4
-rw-r--r--spec/factories/ci/unit_test_failures.rb (renamed from spec/factories/ci/unit_test_failure.rb)0
-rw-r--r--spec/factories/ci/unit_tests.rb (renamed from spec/factories/ci/unit_test.rb)0
-rw-r--r--spec/factories/clusters/agents/group_authorizations.rb10
-rw-r--r--spec/factories/clusters/agents/project_authorizations.rb10
-rw-r--r--spec/factories/dependency_proxy.rb9
-rw-r--r--spec/factories/deploy_tokens.rb2
-rw-r--r--spec/factories/events.rb5
-rw-r--r--spec/factories/groups.rb4
-rw-r--r--spec/factories/issues.rb11
-rw-r--r--spec/factories/ml/candidate_metadata.rb10
-rw-r--r--spec/factories/ml/candidates.rb6
-rw-r--r--spec/factories/ml/experiment_metadata.rb10
-rw-r--r--spec/factories/ml/experiments.rb8
-rw-r--r--spec/factories/packages/rpm/rpm_repository_files.rb4
-rw-r--r--spec/factories/project_export_jobs.rb16
-rw-r--r--spec/factories/projects/ci_feature_usages.rb1
-rw-r--r--spec/factories/projects/import_export/relation_export_upload.rb2
-rw-r--r--spec/factories/resource_milestone_events.rb (renamed from spec/factories/resource_milestone_event.rb)0
-rw-r--r--spec/factories/resource_state_events.rb (renamed from spec/factories/resource_state_event.rb)0
-rw-r--r--spec/factories/todos.rb4
-rw-r--r--spec/factories/work_items.rb10
-rw-r--r--spec/factories/work_items/hierarchy_restrictions.rb8
-rw-r--r--spec/factories/work_items/work_item_types.rb2
-rw-r--r--spec/features/abuse_report_spec.rb2
-rw-r--r--spec/features/action_cable_logging_spec.rb2
-rw-r--r--spec/features/admin/admin_abuse_reports_spec.rb2
-rw-r--r--spec/features/admin/admin_appearance_spec.rb2
-rw-r--r--spec/features/admin/admin_broadcast_messages_spec.rb2
-rw-r--r--spec/features/admin/admin_browse_spam_logs_spec.rb9
-rw-r--r--spec/features/admin/admin_deploy_keys_spec.rb2
-rw-r--r--spec/features/admin/admin_dev_ops_reports_spec.rb2
-rw-r--r--spec/features/admin/admin_disables_git_access_protocol_spec.rb2
-rw-r--r--spec/features/admin/admin_disables_two_factor_spec.rb2
-rw-r--r--spec/features/admin/admin_groups_spec.rb2
-rw-r--r--spec/features/admin/admin_health_check_spec.rb2
-rw-r--r--spec/features/admin/admin_hook_logs_spec.rb2
-rw-r--r--spec/features/admin/admin_hooks_spec.rb15
-rw-r--r--spec/features/admin/admin_jobs_spec.rb2
-rw-r--r--spec/features/admin/admin_labels_spec.rb2
-rw-r--r--spec/features/admin/admin_manage_applications_spec.rb2
-rw-r--r--spec/features/admin/admin_mode/login_spec.rb2
-rw-r--r--spec/features/admin/admin_mode/logout_spec.rb2
-rw-r--r--spec/features/admin/admin_mode/workers_spec.rb2
-rw-r--r--spec/features/admin/admin_mode_spec.rb2
-rw-r--r--spec/features/admin/admin_projects_spec.rb2
-rw-r--r--spec/features/admin/admin_runners_spec.rb18
-rw-r--r--spec/features/admin/admin_search_settings_spec.rb2
-rw-r--r--spec/features/admin/admin_sees_background_migrations_spec.rb2
-rw-r--r--spec/features/admin/admin_sees_project_statistics_spec.rb2
-rw-r--r--spec/features/admin/admin_sees_projects_statistics_spec.rb2
-rw-r--r--spec/features/admin/admin_settings_spec.rb12
-rw-r--r--spec/features/admin/admin_system_info_spec.rb2
-rw-r--r--spec/features/admin/admin_users_impersonation_tokens_spec.rb2
-rw-r--r--spec/features/admin/admin_users_spec.rb2
-rw-r--r--spec/features/admin/admin_uses_repository_checks_spec.rb2
-rw-r--r--spec/features/admin/dashboard_spec.rb6
-rw-r--r--spec/features/admin/integrations/instance_integrations_spec.rb2
-rw-r--r--spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb3
-rw-r--r--spec/features/admin/users/user_spec.rb2
-rw-r--r--spec/features/admin/users/users_spec.rb6
-rw-r--r--spec/features/admin_variables_spec.rb2
-rw-r--r--spec/features/alert_management/alert_details_spec.rb6
-rw-r--r--spec/features/alert_management/alert_management_list_spec.rb2
-rw-r--r--spec/features/alert_management/user_filters_alerts_by_status_spec.rb2
-rw-r--r--spec/features/alert_management/user_searches_alerts_spec.rb2
-rw-r--r--spec/features/alert_management/user_updates_alert_status_spec.rb2
-rw-r--r--spec/features/alert_management_spec.rb2
-rw-r--r--spec/features/alerts_settings/user_views_alerts_settings_spec.rb2
-rw-r--r--spec/features/atom/dashboard_issues_spec.rb2
-rw-r--r--spec/features/atom/dashboard_spec.rb2
-rw-r--r--spec/features/atom/issues_spec.rb2
-rw-r--r--spec/features/atom/merge_requests_spec.rb2
-rw-r--r--spec/features/atom/users_spec.rb2
-rw-r--r--spec/features/boards/board_filters_spec.rb4
-rw-r--r--spec/features/boards/boards_spec.rb124
-rw-r--r--spec/features/boards/focus_mode_spec.rb2
-rw-r--r--spec/features/boards/issue_ordering_spec.rb2
-rw-r--r--spec/features/boards/keyboard_shortcut_spec.rb2
-rw-r--r--spec/features/boards/multi_select_spec.rb2
-rw-r--r--spec/features/boards/multiple_boards_spec.rb2
-rw-r--r--spec/features/boards/new_issue_spec.rb2
-rw-r--r--spec/features/boards/reload_boards_on_browser_back_spec.rb2
-rw-r--r--spec/features/boards/sidebar_assignee_spec.rb5
-rw-r--r--spec/features/boards/sidebar_labels_in_namespaces_spec.rb2
-rw-r--r--spec/features/boards/sidebar_labels_spec.rb2
-rw-r--r--spec/features/boards/sidebar_spec.rb2
-rw-r--r--spec/features/boards/user_adds_lists_to_board_spec.rb2
-rw-r--r--spec/features/boards/user_visits_board_spec.rb2
-rw-r--r--spec/features/breadcrumbs_schema_markup_spec.rb2
-rw-r--r--spec/features/broadcast_messages_spec.rb8
-rw-r--r--spec/features/calendar_spec.rb19
-rw-r--r--spec/features/callouts/registration_enabled_spec.rb6
-rw-r--r--spec/features/canonical_link_spec.rb2
-rw-r--r--spec/features/clusters/cluster_detail_page_spec.rb2
-rw-r--r--spec/features/clusters/cluster_health_dashboard_spec.rb3
-rw-r--r--spec/features/clusters/create_agent_spec.rb6
-rw-r--r--spec/features/commit_spec.rb2
-rw-r--r--spec/features/commits/user_uses_quick_actions_spec.rb2
-rw-r--r--spec/features/commits/user_view_commits_spec.rb2
-rw-r--r--spec/features/commits_spec.rb4
-rw-r--r--spec/features/contextual_sidebar_spec.rb2
-rw-r--r--spec/features/cycle_analytics_spec.rb2
-rw-r--r--spec/features/dashboard/activity_spec.rb2
-rw-r--r--spec/features/dashboard/archived_projects_spec.rb2
-rw-r--r--spec/features/dashboard/datetime_on_tooltips_spec.rb2
-rw-r--r--spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb2
-rw-r--r--spec/features/dashboard/group_spec.rb2
-rw-r--r--spec/features/dashboard/groups_list_spec.rb2
-rw-r--r--spec/features/dashboard/issuables_counter_spec.rb25
-rw-r--r--spec/features/dashboard/issues_filter_spec.rb2
-rw-r--r--spec/features/dashboard/issues_spec.rb2
-rw-r--r--spec/features/dashboard/label_filter_spec.rb2
-rw-r--r--spec/features/dashboard/merge_requests_spec.rb2
-rw-r--r--spec/features/dashboard/milestones_spec.rb2
-rw-r--r--spec/features/dashboard/project_member_activity_index_spec.rb2
-rw-r--r--spec/features/dashboard/projects_spec.rb10
-rw-r--r--spec/features/dashboard/root_explore_spec.rb24
-rw-r--r--spec/features/dashboard/shortcuts_spec.rb2
-rw-r--r--spec/features/dashboard/snippets_spec.rb2
-rw-r--r--spec/features/dashboard/todos/target_state_spec.rb2
-rw-r--r--spec/features/dashboard/todos/todos_filtering_spec.rb22
-rw-r--r--spec/features/dashboard/todos/todos_sorting_spec.rb2
-rw-r--r--spec/features/dashboard/todos/todos_spec.rb119
-rw-r--r--spec/features/dashboard/user_filters_projects_spec.rb177
-rw-r--r--spec/features/discussion_comments/commit_spec.rb2
-rw-r--r--spec/features/discussion_comments/issue_spec.rb2
-rw-r--r--spec/features/discussion_comments/merge_request_spec.rb2
-rw-r--r--spec/features/discussion_comments/snippets_spec.rb2
-rw-r--r--spec/features/display_system_header_and_footer_bar_spec.rb2
-rw-r--r--spec/features/error_pages_spec.rb2
-rw-r--r--spec/features/error_tracking/user_filters_errors_by_status_spec.rb3
-rw-r--r--spec/features/error_tracking/user_searches_sentry_errors_spec.rb3
-rw-r--r--spec/features/error_tracking/user_sees_error_details_spec.rb3
-rw-r--r--spec/features/error_tracking/user_sees_error_index_spec.rb3
-rw-r--r--spec/features/expand_collapse_diffs_spec.rb2
-rw-r--r--spec/features/explore/groups_list_spec.rb2
-rw-r--r--spec/features/explore/groups_spec.rb2
-rw-r--r--spec/features/explore/topics_spec.rb2
-rw-r--r--spec/features/explore/user_explores_projects_spec.rb4
-rw-r--r--spec/features/file_uploads/attachment_spec.rb2
-rw-r--r--spec/features/file_uploads/ci_artifact_spec.rb2
-rw-r--r--spec/features/file_uploads/git_lfs_spec.rb2
-rw-r--r--spec/features/file_uploads/graphql_add_design_spec.rb2
-rw-r--r--spec/features/file_uploads/group_import_spec.rb2
-rw-r--r--spec/features/file_uploads/maven_package_spec.rb2
-rw-r--r--spec/features/file_uploads/multipart_invalid_uploads_spec.rb4
-rw-r--r--spec/features/file_uploads/nuget_package_spec.rb2
-rw-r--r--spec/features/file_uploads/project_import_spec.rb2
-rw-r--r--spec/features/file_uploads/rubygem_package_spec.rb2
-rw-r--r--spec/features/file_uploads/user_avatar_spec.rb2
-rw-r--r--spec/features/frequently_visited_projects_and_groups_spec.rb2
-rw-r--r--spec/features/gitlab_experiments_spec.rb2
-rw-r--r--spec/features/global_search_spec.rb2
-rw-r--r--spec/features/graphiql_spec.rb2
-rw-r--r--spec/features/graphql_known_operations_spec.rb2
-rw-r--r--spec/features/group_variables_spec.rb2
-rw-r--r--spec/features/groups/activity_spec.rb2
-rw-r--r--spec/features/groups/board_sidebar_spec.rb2
-rw-r--r--spec/features/groups/board_spec.rb4
-rw-r--r--spec/features/groups/clusters/user_spec.rb2
-rw-r--r--spec/features/groups/container_registry_spec.rb4
-rw-r--r--spec/features/groups/crm/contacts/create_spec.rb2
-rw-r--r--spec/features/groups/dependency_proxy_for_containers_spec.rb2
-rw-r--r--spec/features/groups/dependency_proxy_spec.rb2
-rw-r--r--spec/features/groups/empty_states_spec.rb2
-rw-r--r--spec/features/groups/group_page_with_external_authorization_service_spec.rb2
-rw-r--r--spec/features/groups/group_runners_spec.rb2
-rw-r--r--spec/features/groups/group_settings_spec.rb4
-rw-r--r--spec/features/groups/import_export/connect_instance_spec.rb108
-rw-r--r--spec/features/groups/import_export/export_file_spec.rb2
-rw-r--r--spec/features/groups/import_export/import_file_spec.rb2
-rw-r--r--spec/features/groups/import_export/migration_history_spec.rb2
-rw-r--r--spec/features/groups/integrations/user_activates_mattermost_slash_command_spec.rb2
-rw-r--r--spec/features/groups/issues_spec.rb6
-rw-r--r--spec/features/groups/labels/create_spec.rb2
-rw-r--r--spec/features/groups/labels/edit_spec.rb2
-rw-r--r--spec/features/groups/labels/index_spec.rb2
-rw-r--r--spec/features/groups/labels/search_labels_spec.rb2
-rw-r--r--spec/features/groups/labels/sort_labels_spec.rb2
-rw-r--r--spec/features/groups/labels/subscription_spec.rb2
-rw-r--r--spec/features/groups/labels/user_sees_links_to_issuables_spec.rb2
-rw-r--r--spec/features/groups/members/filter_members_spec.rb2
-rw-r--r--spec/features/groups/members/leave_group_spec.rb2
-rw-r--r--spec/features/groups/members/list_members_spec.rb2
-rw-r--r--spec/features/groups/members/manage_groups_spec.rb2
-rw-r--r--spec/features/groups/members/manage_members_spec.rb4
-rw-r--r--spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb2
-rw-r--r--spec/features/groups/members/master_manages_access_requests_spec.rb2
-rw-r--r--spec/features/groups/members/request_access_spec.rb2
-rw-r--r--spec/features/groups/members/search_members_spec.rb2
-rw-r--r--spec/features/groups/members/sort_members_spec.rb2
-rw-r--r--spec/features/groups/members/tabs_spec.rb2
-rw-r--r--spec/features/groups/merge_requests_spec.rb2
-rw-r--r--spec/features/groups/milestone_spec.rb2
-rw-r--r--spec/features/groups/milestones/gfm_autocomplete_spec.rb2
-rw-r--r--spec/features/groups/milestones_sorting_spec.rb2
-rw-r--r--spec/features/groups/navbar_spec.rb2
-rw-r--r--spec/features/groups/new_group_page_spec.rb2
-rw-r--r--spec/features/groups/packages_spec.rb2
-rw-r--r--spec/features/groups/settings/access_tokens_spec.rb2
-rw-r--r--spec/features/groups/settings/ci_cd_spec.rb7
-rw-r--r--spec/features/groups/settings/group_badges_spec.rb2
-rw-r--r--spec/features/groups/settings/manage_applications_spec.rb2
-rw-r--r--spec/features/groups/settings/packages_and_registries_spec.rb2
-rw-r--r--spec/features/groups/settings/repository_spec.rb2
-rw-r--r--spec/features/groups/settings/user_searches_in_settings_spec.rb2
-rw-r--r--spec/features/groups/share_lock_spec.rb2
-rw-r--r--spec/features/groups/show_spec.rb2
-rw-r--r--spec/features/groups/user_browse_projects_group_page_spec.rb2
-rw-r--r--spec/features/groups/user_sees_package_sidebar_spec.rb2
-rw-r--r--spec/features/groups/user_sees_users_dropdowns_in_issuables_list_spec.rb3
-rw-r--r--spec/features/groups_spec.rb10
-rw-r--r--spec/features/help_dropdown_spec.rb2
-rw-r--r--spec/features/help_pages_spec.rb2
-rw-r--r--spec/features/ics/dashboard_issues_spec.rb2
-rw-r--r--spec/features/ics/group_issues_spec.rb2
-rw-r--r--spec/features/ics/project_issues_spec.rb2
-rw-r--r--spec/features/ide/clientside_preview_csp_spec.rb2
-rw-r--r--spec/features/ide/static_object_external_storage_csp_spec.rb2
-rw-r--r--spec/features/ide/user_opens_merge_request_spec.rb2
-rw-r--r--spec/features/ide_spec.rb19
-rw-r--r--spec/features/import/manifest_import_spec.rb4
-rw-r--r--spec/features/incidents/incident_details_spec.rb2
-rw-r--r--spec/features/incidents/incident_timeline_events_spec.rb10
-rw-r--r--spec/features/incidents/incidents_list_spec.rb2
-rw-r--r--spec/features/incidents/user_creates_new_incident_spec.rb2
-rw-r--r--spec/features/incidents/user_filters_incidents_by_status_spec.rb2
-rw-r--r--spec/features/incidents/user_searches_incidents_spec.rb2
-rw-r--r--spec/features/incidents/user_uses_quick_actions_spec.rb2
-rw-r--r--spec/features/incidents/user_views_incident_spec.rb4
-rw-r--r--spec/features/invites_spec.rb10
-rw-r--r--spec/features/issuables/issuable_list_spec.rb8
-rw-r--r--spec/features/issuables/markdown_references/internal_references_spec.rb2
-rw-r--r--spec/features/issuables/markdown_references/jira_spec.rb2
-rw-r--r--spec/features/issuables/shortcuts_issuable_spec.rb4
-rw-r--r--spec/features/issuables/sorting_list_spec.rb2
-rw-r--r--spec/features/issuables/user_sees_sidebar_spec.rb2
-rw-r--r--spec/features/issue_rebalancing_spec.rb6
-rw-r--r--spec/features/issues/confidential_notes_spec.rb2
-rw-r--r--spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb2
-rw-r--r--spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb2
-rw-r--r--spec/features/issues/csv_spec.rb2
-rw-r--r--spec/features/issues/discussion_lock_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_assignee_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_author_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_base_spec.rb3
-rw-r--r--spec/features/issues/filtered_search/dropdown_emoji_spec.rb3
-rw-r--r--spec/features/issues/filtered_search/dropdown_hint_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_label_spec.rb3
-rw-r--r--spec/features/issues/filtered_search/dropdown_milestone_spec.rb3
-rw-r--r--spec/features/issues/filtered_search/dropdown_release_spec.rb3
-rw-r--r--spec/features/issues/filtered_search/filter_issues_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/recent_searches_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/search_bar_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/visual_tokens_spec.rb2
-rw-r--r--spec/features/issues/form_spec.rb4
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb2
-rw-r--r--spec/features/issues/group_label_sidebar_spec.rb2
-rw-r--r--spec/features/issues/incident_issue_spec.rb2
-rw-r--r--spec/features/issues/issue_detail_spec.rb2
-rw-r--r--spec/features/issues/issue_header_spec.rb10
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb2
-rw-r--r--spec/features/issues/issue_state_spec.rb2
-rw-r--r--spec/features/issues/keyboard_shortcut_spec.rb2
-rw-r--r--spec/features/issues/markdown_toolbar_spec.rb2
-rw-r--r--spec/features/issues/move_spec.rb2
-rw-r--r--spec/features/issues/note_polling_spec.rb2
-rw-r--r--spec/features/issues/notes_on_issues_spec.rb2
-rw-r--r--spec/features/issues/related_issues_spec.rb2
-rw-r--r--spec/features/issues/resource_label_events_spec.rb2
-rw-r--r--spec/features/issues/rss_spec.rb2
-rw-r--r--spec/features/issues/service_desk_spec.rb2
-rw-r--r--spec/features/issues/spam_akismet_issue_creation_spec.rb2
-rw-r--r--spec/features/issues/todo_spec.rb2
-rw-r--r--spec/features/issues/user_bulk_edits_issues_labels_spec.rb2
-rw-r--r--spec/features/issues/user_bulk_edits_issues_spec.rb2
-rw-r--r--spec/features/issues/user_comments_on_issue_spec.rb2
-rw-r--r--spec/features/issues/user_creates_branch_and_merge_request_spec.rb6
-rw-r--r--spec/features/issues/user_creates_confidential_merge_request_spec.rb2
-rw-r--r--spec/features/issues/user_creates_issue_by_email_spec.rb2
-rw-r--r--spec/features/issues/user_creates_issue_spec.rb2
-rw-r--r--spec/features/issues/user_edits_issue_spec.rb4
-rw-r--r--spec/features/issues/user_filters_issues_spec.rb2
-rw-r--r--spec/features/issues/user_interacts_with_awards_spec.rb6
-rw-r--r--spec/features/issues/user_resets_their_incoming_email_token_spec.rb2
-rw-r--r--spec/features/issues/user_scrolls_to_deeplinked_note_spec.rb2
-rw-r--r--spec/features/issues/user_sees_breadcrumb_links_spec.rb2
-rw-r--r--spec/features/issues/user_sees_empty_state_spec.rb2
-rw-r--r--spec/features/issues/user_sees_live_update_spec.rb2
-rw-r--r--spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb2
-rw-r--r--spec/features/issues/user_sorts_issue_comments_spec.rb2
-rw-r--r--spec/features/issues/user_sorts_issues_spec.rb2
-rw-r--r--spec/features/issues/user_toggles_subscription_spec.rb2
-rw-r--r--spec/features/issues/user_uses_quick_actions_spec.rb2
-rw-r--r--spec/features/issues/user_views_issue_spec.rb2
-rw-r--r--spec/features/issues/user_views_issues_spec.rb2
-rw-r--r--spec/features/jira_connect/branches_spec.rb24
-rw-r--r--spec/features/jira_connect/subscriptions_spec.rb2
-rw-r--r--spec/features/jira_oauth_provider_authorize_spec.rb2
-rw-r--r--spec/features/labels_hierarchy_spec.rb3
-rw-r--r--spec/features/markdown/copy_as_gfm_spec.rb12
-rw-r--r--spec/features/markdown/gitlab_flavored_markdown_spec.rb2
-rw-r--r--spec/features/markdown/json_table_spec.rb2
-rw-r--r--spec/features/markdown/keyboard_shortcuts_spec.rb2
-rw-r--r--spec/features/markdown/kroki_spec.rb2
-rw-r--r--spec/features/markdown/markdown_spec.rb9
-rw-r--r--spec/features/markdown/math_spec.rb2
-rw-r--r--spec/features/markdown/metrics_spec.rb2
-rw-r--r--spec/features/markdown/observability_spec.rb83
-rw-r--r--spec/features/markdown/sandboxed_mermaid_spec.rb2
-rw-r--r--spec/features/merge_request/batch_comments_spec.rb2
-rw-r--r--spec/features/merge_request/close_reopen_report_toggle_spec.rb20
-rw-r--r--spec/features/merge_request/maintainer_edits_fork_spec.rb3
-rw-r--r--spec/features/merge_request/merge_request_discussion_lock_spec.rb2
-rw-r--r--spec/features/merge_request/user_accepts_merge_request_spec.rb2
-rw-r--r--spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb3
-rw-r--r--spec/features/merge_request/user_approves_spec.rb2
-rw-r--r--spec/features/merge_request/user_assigns_themselves_reviewer_spec.rb42
-rw-r--r--spec/features/merge_request/user_assigns_themselves_spec.rb16
-rw-r--r--spec/features/merge_request/user_awards_emoji_spec.rb2
-rw-r--r--spec/features/merge_request/user_clicks_merge_request_tabs_spec.rb2
-rw-r--r--spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb3
-rw-r--r--spec/features/merge_request/user_comments_on_commit_spec.rb2
-rw-r--r--spec/features/merge_request/user_comments_on_diff_spec.rb2
-rw-r--r--spec/features/merge_request/user_comments_on_merge_request_spec.rb2
-rw-r--r--spec/features/merge_request/user_creates_image_diff_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_creates_merge_request_spec.rb4
-rw-r--r--spec/features/merge_request/user_creates_mr_spec.rb2
-rw-r--r--spec/features/merge_request/user_customizes_merge_commit_message_spec.rb2
-rw-r--r--spec/features/merge_request/user_edits_assignees_sidebar_spec.rb2
-rw-r--r--spec/features/merge_request/user_edits_merge_request_spec.rb2
-rw-r--r--spec/features/merge_request/user_edits_mr_spec.rb2
-rw-r--r--spec/features/merge_request/user_edits_reviewers_sidebar_spec.rb2
-rw-r--r--spec/features/merge_request/user_expands_diff_spec.rb2
-rw-r--r--spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb2
-rw-r--r--spec/features/merge_request/user_locks_discussion_spec.rb2
-rw-r--r--spec/features/merge_request/user_manages_subscription_spec.rb2
-rw-r--r--spec/features/merge_request/user_marks_merge_request_as_draft_spec.rb2
-rw-r--r--spec/features/merge_request/user_merges_immediately_spec.rb2
-rw-r--r--spec/features/merge_request/user_merges_merge_request_spec.rb2
-rw-r--r--spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb2
-rw-r--r--spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb2
-rw-r--r--spec/features/merge_request/user_opens_checkout_branch_modal_spec.rb2
-rw-r--r--spec/features/merge_request/user_opens_context_commits_modal_spec.rb2
-rw-r--r--spec/features/merge_request/user_posts_diff_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_posts_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_rebases_merge_request_spec.rb2
-rw-r--r--spec/features/merge_request/user_resolves_conflicts_spec.rb2
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb2
-rw-r--r--spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb2
-rw-r--r--spec/features/merge_request/user_resolves_wip_mr_spec.rb2
-rw-r--r--spec/features/merge_request/user_reverts_merge_request_spec.rb2
-rw-r--r--spec/features/merge_request/user_reviews_image_spec.rb2
-rw-r--r--spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_breadcrumb_links_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_closing_issues_message_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_deleted_target_branch_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_deployment_widget_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_diff_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_discussions_navigation_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_discussions_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb3
-rw-r--r--spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_merge_widget_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_page_metadata_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb3
-rw-r--r--spec/features/merge_request/user_sees_pipelines_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_suggest_pipeline_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_system_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_versions_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_wip_help_message_spec.rb2
-rw-r--r--spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb2
-rw-r--r--spec/features/merge_request/user_squashes_merge_request_spec.rb2
-rw-r--r--spec/features/merge_request/user_suggests_changes_on_diff_spec.rb2
-rw-r--r--spec/features/merge_request/user_toggles_whitespace_changes_spec.rb2
-rw-r--r--spec/features/merge_request/user_tries_to_access_private_project_info_through_new_mr_spec.rb3
-rw-r--r--spec/features/merge_request/user_uses_quick_actions_spec.rb3
-rw-r--r--spec/features/merge_request/user_views_auto_expanding_diff_spec.rb2
-rw-r--r--spec/features/merge_request/user_views_diffs_commit_spec.rb2
-rw-r--r--spec/features/merge_request/user_views_diffs_file_by_file_spec.rb2
-rw-r--r--spec/features/merge_request/user_views_diffs_spec.rb2
-rw-r--r--spec/features/merge_request/user_views_merge_request_from_deleted_fork_spec.rb2
-rw-r--r--spec/features/merge_request/user_views_open_merge_request_spec.rb2
-rw-r--r--spec/features/merge_requests/filters_generic_behavior_spec.rb2
-rw-r--r--spec/features/merge_requests/rss_spec.rb2
-rw-r--r--spec/features/merge_requests/user_exports_as_csv_spec.rb2
-rw-r--r--spec/features/merge_requests/user_filters_by_approvals_spec.rb2
-rw-r--r--spec/features/merge_requests/user_filters_by_assignees_spec.rb2
-rw-r--r--spec/features/merge_requests/user_filters_by_deployments_spec.rb2
-rw-r--r--spec/features/merge_requests/user_filters_by_draft_spec.rb2
-rw-r--r--spec/features/merge_requests/user_filters_by_labels_spec.rb2
-rw-r--r--spec/features/merge_requests/user_filters_by_milestones_spec.rb2
-rw-r--r--spec/features/merge_requests/user_filters_by_multiple_criteria_spec.rb2
-rw-r--r--spec/features/merge_requests/user_filters_by_target_branch_spec.rb2
-rw-r--r--spec/features/merge_requests/user_lists_merge_requests_spec.rb2
-rw-r--r--spec/features/merge_requests/user_mass_updates_spec.rb2
-rw-r--r--spec/features/merge_requests/user_sees_empty_state_spec.rb2
-rw-r--r--spec/features/merge_requests/user_sorts_merge_requests_spec.rb2
-rw-r--r--spec/features/merge_requests/user_views_all_merge_requests_spec.rb2
-rw-r--r--spec/features/merge_requests/user_views_closed_merge_requests_spec.rb2
-rw-r--r--spec/features/merge_requests/user_views_merged_merge_requests_spec.rb2
-rw-r--r--spec/features/merge_requests/user_views_open_merge_requests_spec.rb2
-rw-r--r--spec/features/milestone_spec.rb2
-rw-r--r--spec/features/milestones/user_creates_milestone_spec.rb2
-rw-r--r--spec/features/milestones/user_deletes_milestone_spec.rb2
-rw-r--r--spec/features/milestones/user_edits_milestone_spec.rb2
-rw-r--r--spec/features/milestones/user_promotes_milestone_spec.rb2
-rw-r--r--spec/features/milestones/user_sees_breadcrumb_links_spec.rb2
-rw-r--r--spec/features/milestones/user_views_milestone_spec.rb2
-rw-r--r--spec/features/milestones/user_views_milestones_spec.rb2
-rw-r--r--spec/features/monitor_sidebar_link_spec.rb55
-rw-r--r--spec/features/nav/new_nav_toggle_spec.rb71
-rw-r--r--spec/features/nav/top_nav_responsive_spec.rb2
-rw-r--r--spec/features/nav/top_nav_tooltip_spec.rb2
-rw-r--r--spec/features/oauth_login_spec.rb2
-rw-r--r--spec/features/oauth_provider_authorize_spec.rb2
-rw-r--r--spec/features/oauth_registration_spec.rb2
-rw-r--r--spec/features/one_trust_spec.rb2
-rw-r--r--spec/features/participants_autocomplete_spec.rb2
-rw-r--r--spec/features/password_reset_spec.rb2
-rw-r--r--spec/features/populate_new_pipeline_vars_with_params_spec.rb44
-rw-r--r--spec/features/profile_spec.rb2
-rw-r--r--spec/features/profiles/account_spec.rb2
-rw-r--r--spec/features/profiles/active_sessions_spec.rb2
-rw-r--r--spec/features/profiles/chat_names_spec.rb2
-rw-r--r--spec/features/profiles/emails_spec.rb2
-rw-r--r--spec/features/profiles/gpg_keys_spec.rb2
-rw-r--r--spec/features/profiles/keys_spec.rb4
-rw-r--r--spec/features/profiles/oauth_applications_spec.rb2
-rw-r--r--spec/features/profiles/password_spec.rb2
-rw-r--r--spec/features/profiles/personal_access_tokens_spec.rb2
-rw-r--r--spec/features/profiles/two_factor_auths_spec.rb2
-rw-r--r--spec/features/profiles/user_changes_notified_of_own_activity_spec.rb3
-rw-r--r--spec/features/profiles/user_edit_preferences_spec.rb22
-rw-r--r--spec/features/profiles/user_edit_profile_spec.rb2
-rw-r--r--spec/features/profiles/user_manages_applications_spec.rb2
-rw-r--r--spec/features/profiles/user_manages_emails_spec.rb2
-rw-r--r--spec/features/profiles/user_search_settings_spec.rb2
-rw-r--r--spec/features/profiles/user_visits_notifications_tab_spec.rb2
-rw-r--r--spec/features/profiles/user_visits_profile_account_page_spec.rb2
-rw-r--r--spec/features/profiles/user_visits_profile_authentication_log_spec.rb2
-rw-r--r--spec/features/profiles/user_visits_profile_preferences_page_spec.rb2
-rw-r--r--spec/features/profiles/user_visits_profile_spec.rb2
-rw-r--r--spec/features/profiles/user_visits_profile_ssh_keys_page_spec.rb2
-rw-r--r--spec/features/project_group_variables_spec.rb2
-rw-r--r--spec/features/project_variables_spec.rb2
-rw-r--r--spec/features/projects/active_tabs_spec.rb2
-rw-r--r--spec/features/projects/activity/rss_spec.rb2
-rw-r--r--spec/features/projects/activity/user_sees_activity_spec.rb2
-rw-r--r--spec/features/projects/activity/user_sees_design_activity_spec.rb2
-rw-r--r--spec/features/projects/activity/user_sees_design_comment_spec.rb2
-rw-r--r--spec/features/projects/activity/user_sees_private_activity_spec.rb2
-rw-r--r--spec/features/projects/artifacts/file_spec.rb2
-rw-r--r--spec/features/projects/artifacts/raw_spec.rb2
-rw-r--r--spec/features/projects/artifacts/user_browses_artifacts_spec.rb2
-rw-r--r--spec/features/projects/artifacts/user_downloads_artifacts_spec.rb2
-rw-r--r--spec/features/projects/badges/coverage_spec.rb2
-rw-r--r--spec/features/projects/badges/list_spec.rb2
-rw-r--r--spec/features/projects/badges/pipeline_badge_spec.rb2
-rw-r--r--spec/features/projects/blobs/blame_spec.rb2
-rw-r--r--spec/features/projects/blobs/blob_line_permalink_updater_spec.rb2
-rw-r--r--spec/features/projects/blobs/blob_show_spec.rb83
-rw-r--r--spec/features/projects/blobs/edit_spec.rb11
-rw-r--r--spec/features/projects/blobs/shortcuts_blob_spec.rb2
-rw-r--r--spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb2
-rw-r--r--spec/features/projects/blobs/user_views_pipeline_editor_button_spec.rb2
-rw-r--r--spec/features/projects/branches/download_buttons_spec.rb2
-rw-r--r--spec/features/projects/branches/new_branch_ref_dropdown_spec.rb71
-rw-r--r--spec/features/projects/branches/user_creates_branch_spec.rb8
-rw-r--r--spec/features/projects/branches/user_deletes_branch_spec.rb2
-rw-r--r--spec/features/projects/branches/user_views_branches_spec.rb2
-rw-r--r--spec/features/projects/branches_spec.rb6
-rw-r--r--spec/features/projects/ci/editor_spec.rb2
-rw-r--r--spec/features/projects/ci/lint_spec.rb2
-rw-r--r--spec/features/projects/classification_label_on_project_pages_spec.rb2
-rw-r--r--spec/features/projects/cluster_agents_spec.rb2
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb2
-rw-r--r--spec/features/projects/clusters/user_spec.rb2
-rw-r--r--spec/features/projects/clusters_spec.rb2
-rw-r--r--spec/features/projects/commit/builds_spec.rb2
-rw-r--r--spec/features/projects/commit/cherry_pick_spec.rb2
-rw-r--r--spec/features/projects/commit/comments/user_adds_comment_spec.rb2
-rw-r--r--spec/features/projects/commit/comments/user_deletes_comments_spec.rb2
-rw-r--r--spec/features/projects/commit/comments/user_edits_comments_spec.rb2
-rw-r--r--spec/features/projects/commit/diff_notes_spec.rb2
-rw-r--r--spec/features/projects/commit/mini_pipeline_graph_spec.rb2
-rw-r--r--spec/features/projects/commit/user_comments_on_commit_spec.rb2
-rw-r--r--spec/features/projects/commit/user_reverts_commit_spec.rb2
-rw-r--r--spec/features/projects/commit/user_views_user_status_on_commit_spec.rb2
-rw-r--r--spec/features/projects/commits/multi_view_diff_spec.rb2
-rw-r--r--spec/features/projects/commits/rss_spec.rb2
-rw-r--r--spec/features/projects/commits/user_browses_commits_spec.rb23
-rw-r--r--spec/features/projects/compare_spec.rb16
-rw-r--r--spec/features/projects/confluence/user_views_confluence_page_spec.rb2
-rw-r--r--spec/features/projects/container_registry_spec.rb4
-rw-r--r--spec/features/projects/deploy_keys_spec.rb2
-rw-r--r--spec/features/projects/diffs/diff_show_spec.rb2
-rw-r--r--spec/features/projects/environments/environment_metrics_spec.rb2
-rw-r--r--spec/features/projects/environments/environment_spec.rb73
-rw-r--r--spec/features/projects/environments/environments_spec.rb2
-rw-r--r--spec/features/projects/feature_flag_user_lists/user_deletes_feature_flag_user_list_spec.rb2
-rw-r--r--spec/features/projects/feature_flag_user_lists/user_edits_feature_flag_user_list_spec.rb2
-rw-r--r--spec/features/projects/feature_flag_user_lists/user_sees_feature_flag_user_list_details_spec.rb2
-rw-r--r--spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb2
-rw-r--r--spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb6
-rw-r--r--spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb2
-rw-r--r--spec/features/projects/features_visibility_spec.rb2
-rw-r--r--spec/features/projects/files/dockerfile_dropdown_spec.rb2
-rw-r--r--spec/features/projects/files/download_buttons_spec.rb2
-rw-r--r--spec/features/projects/files/edit_file_soft_wrap_spec.rb2
-rw-r--r--spec/features/projects/files/editing_a_file_spec.rb2
-rw-r--r--spec/features/projects/files/files_sort_submodules_with_folders_spec.rb2
-rw-r--r--spec/features/projects/files/find_file_keyboard_spec.rb2
-rw-r--r--spec/features/projects/files/gitignore_dropdown_spec.rb2
-rw-r--r--spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb2
-rw-r--r--spec/features/projects/files/project_owner_creates_license_file_spec.rb2
-rw-r--r--spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb3
-rw-r--r--spec/features/projects/files/template_selector_menu_spec.rb2
-rw-r--r--spec/features/projects/files/template_type_dropdown_spec.rb2
-rw-r--r--spec/features/projects/files/undo_template_spec.rb2
-rw-r--r--spec/features/projects/files/user_browses_a_tree_with_a_folder_containing_only_a_folder_spec.rb3
-rw-r--r--spec/features/projects/files/user_browses_files_spec.rb39
-rw-r--r--spec/features/projects/files/user_browses_lfs_files_spec.rb2
-rw-r--r--spec/features/projects/files/user_creates_directory_spec.rb2
-rw-r--r--spec/features/projects/files/user_creates_files_spec.rb2
-rw-r--r--spec/features/projects/files/user_deletes_files_spec.rb2
-rw-r--r--spec/features/projects/files/user_edits_files_spec.rb2
-rw-r--r--spec/features/projects/files/user_find_file_spec.rb2
-rw-r--r--spec/features/projects/files/user_reads_pipeline_status_spec.rb2
-rw-r--r--spec/features/projects/files/user_replaces_files_spec.rb2
-rw-r--r--spec/features/projects/files/user_searches_for_files_spec.rb2
-rw-r--r--spec/features/projects/files/user_uploads_files_spec.rb2
-rw-r--r--spec/features/projects/fork_spec.rb2
-rw-r--r--spec/features/projects/forks/fork_list_spec.rb2
-rw-r--r--spec/features/projects/gfm_autocomplete_load_spec.rb2
-rw-r--r--spec/features/projects/graph_spec.rb2
-rw-r--r--spec/features/projects/hook_logs/user_reads_log_spec.rb2
-rw-r--r--spec/features/projects/import_export/export_file_spec.rb6
-rw-r--r--spec/features/projects/import_export/import_file_spec.rb2
-rw-r--r--spec/features/projects/infrastructure_registry_spec.rb2
-rw-r--r--spec/features/projects/integrations/disable_triggers_spec.rb2
-rw-r--r--spec/features/projects/integrations/project_integrations_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_asana_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_assembla_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_atlassian_bamboo_ci_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_emails_on_push_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_flowdock_spec.rb22
-rw-r--r--spec/features/projects/integrations/user_activates_irker_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_jetbrains_teamcity_ci_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_jira_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_mattermost_slash_command_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_packagist_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_pivotaltracker_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_prometheus_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_pushover_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_slack_notifications_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_slack_slash_command_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_uses_inherited_settings_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_views_services_spec.rb2
-rw-r--r--spec/features/projects/issuable_templates_spec.rb2
-rw-r--r--spec/features/projects/issues/design_management/user_links_to_designs_in_issue_spec.rb2
-rw-r--r--spec/features/projects/issues/design_management/user_paginates_designs_spec.rb2
-rw-r--r--spec/features/projects/issues/design_management/user_permissions_upload_spec.rb2
-rw-r--r--spec/features/projects/issues/design_management/user_uploads_designs_spec.rb2
-rw-r--r--spec/features/projects/issues/design_management/user_views_design_images_spec.rb2
-rw-r--r--spec/features/projects/issues/design_management/user_views_design_spec.rb2
-rw-r--r--spec/features/projects/issues/design_management/user_views_designs_spec.rb2
-rw-r--r--spec/features/projects/issues/design_management/user_views_designs_with_svg_xss_spec.rb2
-rw-r--r--spec/features/projects/issues/email_participants_spec.rb50
-rw-r--r--spec/features/projects/issues/viewing_relocated_issues_spec.rb2
-rw-r--r--spec/features/projects/jobs/permissions_spec.rb2
-rw-r--r--spec/features/projects/jobs/user_browses_job_spec.rb2
-rw-r--r--spec/features/projects/jobs/user_browses_jobs_spec.rb2
-rw-r--r--spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb2
-rw-r--r--spec/features/projects/jobs_spec.rb6
-rw-r--r--spec/features/projects/labels/issues_sorted_by_priority_spec.rb2
-rw-r--r--spec/features/projects/labels/search_labels_spec.rb2
-rw-r--r--spec/features/projects/labels/sort_labels_spec.rb2
-rw-r--r--spec/features/projects/labels/subscription_spec.rb2
-rw-r--r--spec/features/projects/labels/update_prioritization_spec.rb2
-rw-r--r--spec/features/projects/labels/user_creates_labels_spec.rb2
-rw-r--r--spec/features/projects/labels/user_edits_labels_spec.rb2
-rw-r--r--spec/features/projects/labels/user_promotes_label_spec.rb2
-rw-r--r--spec/features/projects/labels/user_removes_labels_spec.rb2
-rw-r--r--spec/features/projects/labels/user_sees_breadcrumb_links_spec.rb2
-rw-r--r--spec/features/projects/labels/user_sees_links_to_issuables_spec.rb2
-rw-r--r--spec/features/projects/labels/user_views_labels_spec.rb2
-rw-r--r--spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb2
-rw-r--r--spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb3
-rw-r--r--spec/features/projects/members/group_members_spec.rb2
-rw-r--r--spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb3
-rw-r--r--spec/features/projects/members/groups_with_access_list_spec.rb2
-rw-r--r--spec/features/projects/members/manage_groups_spec.rb2
-rw-r--r--spec/features/projects/members/manage_members_spec.rb20
-rw-r--r--spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb2
-rw-r--r--spec/features/projects/members/master_manages_access_requests_spec.rb2
-rw-r--r--spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb2
-rw-r--r--spec/features/projects/members/member_leaves_project_spec.rb2
-rw-r--r--spec/features/projects/members/owner_cannot_leave_project_spec.rb2
-rw-r--r--spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb2
-rw-r--r--spec/features/projects/members/sorting_spec.rb2
-rw-r--r--spec/features/projects/members/tabs_spec.rb2
-rw-r--r--spec/features/projects/members/user_requests_access_spec.rb2
-rw-r--r--spec/features/projects/merge_request_button_spec.rb2
-rw-r--r--spec/features/projects/milestones/gfm_autocomplete_spec.rb2
-rw-r--r--spec/features/projects/milestones/milestone_spec.rb2
-rw-r--r--spec/features/projects/milestones/milestones_sorting_spec.rb2
-rw-r--r--spec/features/projects/milestones/new_spec.rb2
-rw-r--r--spec/features/projects/milestones/user_interacts_with_labels_spec.rb2
-rw-r--r--spec/features/projects/navbar_spec.rb2
-rw-r--r--spec/features/projects/network_graph_spec.rb2
-rw-r--r--spec/features/projects/new_project_from_template_spec.rb2
-rw-r--r--spec/features/projects/new_project_spec.rb2
-rw-r--r--spec/features/projects/package_files_spec.rb2
-rw-r--r--spec/features/projects/packages_spec.rb2
-rw-r--r--spec/features/projects/pages/user_adds_domain_spec.rb2
-rw-r--r--spec/features/projects/pages/user_configures_pages_pipeline_spec.rb10
-rw-r--r--spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb2
-rw-r--r--spec/features/projects/pages/user_edits_settings_spec.rb2
-rw-r--r--spec/features/projects/pipeline_schedules_spec.rb6
-rw-r--r--spec/features/projects/pipelines/legacy_pipeline_spec.rb1315
-rw-r--r--spec/features/projects/pipelines/legacy_pipelines_spec.rb852
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb2
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb74
-rw-r--r--spec/features/projects/raw/user_interacts_with_raw_endpoint_spec.rb2
-rw-r--r--spec/features/projects/releases/user_creates_release_spec.rb2
-rw-r--r--spec/features/projects/releases/user_views_edit_release_spec.rb2
-rw-r--r--spec/features/projects/releases/user_views_release_spec.rb2
-rw-r--r--spec/features/projects/releases/user_views_releases_spec.rb2
-rw-r--r--spec/features/projects/remote_mirror_spec.rb2
-rw-r--r--spec/features/projects/settings/access_tokens_spec.rb2
-rw-r--r--spec/features/projects/settings/branch_names_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/branch_rules_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/external_authorization_service_settings_spec.rb3
-rw-r--r--spec/features/projects/settings/forked_project_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/lfs_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/merge_requests_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/monitor_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/packages_settings_spec.rb6
-rw-r--r--spec/features/projects/settings/pipelines_settings_spec.rb4
-rw-r--r--spec/features/projects/settings/project_badges_spec.rb2
-rw-r--r--spec/features/projects/settings/project_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb3
-rw-r--r--spec/features/projects/settings/registry_settings_spec.rb3
-rw-r--r--spec/features/projects/settings/repository_settings_spec.rb47
-rw-r--r--spec/features/projects/settings/secure_files_spec.rb29
-rw-r--r--spec/features/projects/settings/service_desk_setting_spec.rb2
-rw-r--r--spec/features/projects/settings/user_archives_project_spec.rb2
-rw-r--r--spec/features/projects/settings/user_changes_avatar_spec.rb2
-rw-r--r--spec/features/projects/settings/user_changes_default_branch_spec.rb2
-rw-r--r--spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb2
-rw-r--r--spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/user_manages_project_members_spec.rb4
-rw-r--r--spec/features/projects/settings/user_renames_a_project_spec.rb2
-rw-r--r--spec/features/projects/settings/user_searches_in_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb2
-rw-r--r--spec/features/projects/settings/user_tags_project_spec.rb2
-rw-r--r--spec/features/projects/settings/user_transfers_a_project_spec.rb2
-rw-r--r--spec/features/projects/settings/visibility_settings_spec.rb22
-rw-r--r--spec/features/projects/settings/webhooks_settings_spec.rb60
-rw-r--r--spec/features/projects/show/download_buttons_spec.rb2
-rw-r--r--spec/features/projects/show/no_password_spec.rb2
-rw-r--r--spec/features/projects/show/redirects_spec.rb2
-rw-r--r--spec/features/projects/show/rss_spec.rb2
-rw-r--r--spec/features/projects/show/schema_markup_spec.rb2
-rw-r--r--spec/features/projects/show/user_interacts_with_auto_devops_banner_spec.rb3
-rw-r--r--spec/features/projects/show/user_interacts_with_stars_spec.rb3
-rw-r--r--spec/features/projects/show/user_manages_notifications_spec.rb5
-rw-r--r--spec/features/projects/show/user_sees_collaboration_links_spec.rb2
-rw-r--r--spec/features/projects/show/user_sees_deletion_failure_message_spec.rb2
-rw-r--r--spec/features/projects/show/user_sees_git_instructions_spec.rb2
-rw-r--r--spec/features/projects/show/user_sees_last_commit_ci_status_spec.rb2
-rw-r--r--spec/features/projects/show/user_sees_readme_spec.rb2
-rw-r--r--spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb2
-rw-r--r--spec/features/projects/show/user_uploads_files_spec.rb2
-rw-r--r--spec/features/projects/snippets/create_snippet_spec.rb2
-rw-r--r--spec/features/projects/snippets/show_spec.rb2
-rw-r--r--spec/features/projects/snippets/user_comments_on_snippet_spec.rb2
-rw-r--r--spec/features/projects/snippets/user_deletes_snippet_spec.rb2
-rw-r--r--spec/features/projects/snippets/user_updates_snippet_spec.rb2
-rw-r--r--spec/features/projects/snippets/user_views_snippets_spec.rb2
-rw-r--r--spec/features/projects/sourcegraph_csp_spec.rb2
-rw-r--r--spec/features/projects/sub_group_issuables_spec.rb2
-rw-r--r--spec/features/projects/tags/download_buttons_spec.rb2
-rw-r--r--spec/features/projects/tags/user_edits_tags_spec.rb2
-rw-r--r--spec/features/projects/tags/user_views_tag_spec.rb2
-rw-r--r--spec/features/projects/tags/user_views_tags_spec.rb2
-rw-r--r--spec/features/projects/terraform_spec.rb2
-rw-r--r--spec/features/projects/tree/create_directory_spec.rb2
-rw-r--r--spec/features/projects/tree/create_file_spec.rb2
-rw-r--r--spec/features/projects/tree/rss_spec.rb2
-rw-r--r--spec/features/projects/tree/tree_show_spec.rb17
-rw-r--r--spec/features/projects/tree/upload_file_spec.rb2
-rw-r--r--spec/features/projects/user_changes_project_visibility_spec.rb2
-rw-r--r--spec/features/projects/user_creates_project_spec.rb2
-rw-r--r--spec/features/projects/user_sees_sidebar_spec.rb4
-rw-r--r--spec/features/projects/user_sees_user_popover_spec.rb2
-rw-r--r--spec/features/projects/user_sorts_projects_spec.rb8
-rw-r--r--spec/features/projects/user_uses_shortcuts_spec.rb2
-rw-r--r--spec/features/projects/user_views_empty_project_spec.rb2
-rw-r--r--spec/features/projects/view_on_env_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_views_wiki_empty_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb2
-rw-r--r--spec/features/projects/wikis_spec.rb2
-rw-r--r--spec/features/projects_spec.rb8
-rw-r--r--spec/features/promotion_spec.rb2
-rw-r--r--spec/features/protected_branches_spec.rb2
-rw-r--r--spec/features/protected_tags_spec.rb2
-rw-r--r--spec/features/read_only_spec.rb6
-rw-r--r--spec/features/reportable_note/issue_spec.rb2
-rw-r--r--spec/features/reportable_note/merge_request_spec.rb2
-rw-r--r--spec/features/reportable_note/snippets_spec.rb2
-rw-r--r--spec/features/runners_spec.rb54
-rw-r--r--spec/features/search/user_searches_for_code_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_comments_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_commits_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_issues_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_merge_requests_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_milestones_spec.rb3
-rw-r--r--spec/features/search/user_searches_for_projects_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_users_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_wiki_pages_spec.rb3
-rw-r--r--spec/features/search/user_uses_header_search_field_spec.rb2
-rw-r--r--spec/features/search/user_uses_search_filters_spec.rb2
-rw-r--r--spec/features/security/admin_access_spec.rb2
-rw-r--r--spec/features/security/dashboard_access_spec.rb2
-rw-r--r--spec/features/security/group/internal_access_spec.rb12
-rw-r--r--spec/features/security/group/private_access_spec.rb14
-rw-r--r--spec/features/security/group/public_access_spec.rb12
-rw-r--r--spec/features/security/profile_access_spec.rb2
-rw-r--r--spec/features/security/project/internal_access_spec.rb2
-rw-r--r--spec/features/security/project/private_access_spec.rb2
-rw-r--r--spec/features/security/project/public_access_spec.rb2
-rw-r--r--spec/features/security/project/snippet/internal_access_spec.rb2
-rw-r--r--spec/features/security/project/snippet/private_access_spec.rb2
-rw-r--r--spec/features/security/project/snippet/public_access_spec.rb2
-rw-r--r--spec/features/sentry_js_spec.rb57
-rw-r--r--spec/features/signed_commits_spec.rb2
-rw-r--r--spec/features/snippets/embedded_snippet_spec.rb2
-rw-r--r--spec/features/snippets/explore_spec.rb2
-rw-r--r--spec/features/snippets/internal_snippet_spec.rb2
-rw-r--r--spec/features/snippets/notes_on_personal_snippets_spec.rb2
-rw-r--r--spec/features/snippets/private_snippets_spec.rb2
-rw-r--r--spec/features/snippets/public_snippets_spec.rb2
-rw-r--r--spec/features/snippets/search_snippets_spec.rb2
-rw-r--r--spec/features/snippets/show_spec.rb2
-rw-r--r--spec/features/snippets/spam_snippets_spec.rb3
-rw-r--r--spec/features/snippets/user_creates_snippet_spec.rb2
-rw-r--r--spec/features/snippets/user_deletes_snippet_spec.rb2
-rw-r--r--spec/features/snippets/user_edits_snippet_spec.rb2
-rw-r--r--spec/features/snippets/user_snippets_spec.rb2
-rw-r--r--spec/features/snippets_spec.rb2
-rw-r--r--spec/features/tags/developer_creates_tag_spec.rb46
-rw-r--r--spec/features/tags/developer_deletes_tag_spec.rb2
-rw-r--r--spec/features/tags/developer_views_tags_spec.rb2
-rw-r--r--spec/features/tags/maintainer_deletes_protected_tag_spec.rb2
-rw-r--r--spec/features/task_lists_spec.rb2
-rw-r--r--spec/features/topic_show_spec.rb2
-rw-r--r--spec/features/triggers_spec.rb2
-rw-r--r--spec/features/u2f_spec.rb3
-rw-r--r--spec/features/unsubscribe_links_spec.rb2
-rw-r--r--spec/features/uploads/user_uploads_avatar_to_group_spec.rb2
-rw-r--r--spec/features/uploads/user_uploads_avatar_to_profile_spec.rb2
-rw-r--r--spec/features/uploads/user_uploads_file_to_note_spec.rb2
-rw-r--r--spec/features/usage_stats_consent_spec.rb2
-rw-r--r--spec/features/user_can_display_performance_bar_spec.rb2
-rw-r--r--spec/features/user_opens_link_to_comment_spec.rb2
-rw-r--r--spec/features/user_sees_revert_modal_spec.rb3
-rw-r--r--spec/features/user_sorts_things_spec.rb4
-rw-r--r--spec/features/users/active_sessions_spec.rb28
-rw-r--r--spec/features/users/add_email_to_existing_account_spec.rb2
-rw-r--r--spec/features/users/anonymous_sessions_spec.rb2
-rw-r--r--spec/features/users/bizible_csp_spec.rb2
-rw-r--r--spec/features/users/confirmation_spec.rb2
-rw-r--r--spec/features/users/email_verification_on_login_spec.rb10
-rw-r--r--spec/features/users/google_analytics_csp_spec.rb2
-rw-r--r--spec/features/users/login_spec.rb6
-rw-r--r--spec/features/users/logout_spec.rb2
-rw-r--r--spec/features/users/one_trust_csp_spec.rb2
-rw-r--r--spec/features/users/overview_spec.rb2
-rw-r--r--spec/features/users/password_spec.rb2
-rw-r--r--spec/features/users/rss_spec.rb2
-rw-r--r--spec/features/users/show_spec.rb2
-rw-r--r--spec/features/users/signup_spec.rb13
-rw-r--r--spec/features/users/snippets_spec.rb2
-rw-r--r--spec/features/users/terms_spec.rb2
-rw-r--r--spec/features/users/user_browses_projects_on_user_page_spec.rb2
-rw-r--r--spec/features/users/zuora_csp_spec.rb2
-rw-r--r--spec/features/webauthn_spec.rb2
-rw-r--r--spec/features/whats_new_spec.rb2
-rw-r--r--spec/features/work_items/work_item_children_spec.rb2
-rw-r--r--spec/features/work_items/work_item_spec.rb29
-rw-r--r--spec/finders/autocomplete/routes_finder_spec.rb40
-rw-r--r--spec/finders/branches_finder_spec.rb63
-rw-r--r--spec/finders/ci/auth_job_finder_spec.rb4
-rw-r--r--spec/finders/ci/freeze_periods_finder_spec.rb59
-rw-r--r--spec/finders/ci/jobs_finder_spec.rb88
-rw-r--r--spec/finders/ci/pipelines_finder_spec.rb41
-rw-r--r--spec/finders/ci/runners_finder_spec.rb447
-rw-r--r--spec/finders/clusters/agent_tokens_finder_spec.rb53
-rw-r--r--spec/finders/environments/environments_finder_spec.rb24
-rw-r--r--spec/finders/freeze_periods_finder_spec.rb59
-rw-r--r--spec/finders/issues_finder_spec.rb2
-rw-r--r--spec/finders/notes_finder_spec.rb10
-rw-r--r--spec/finders/personal_access_tokens_finder_spec.rb475
-rw-r--r--spec/finders/projects_finder_spec.rb17
-rw-r--r--spec/finders/tags_finder_spec.rb13
-rw-r--r--spec/finders/todos_finder_spec.rb4
-rw-r--r--spec/finders/users_finder_spec.rb52
-rw-r--r--spec/finders/work_items/work_items_finder_spec.rb2
-rw-r--r--spec/fixtures/api/schemas/branch.json15
-rw-r--r--spec/fixtures/api/schemas/cluster_status.json131
-rw-r--r--spec/fixtures/api/schemas/conflicts.json130
-rw-r--r--spec/fixtures/api/schemas/entities/discussion.json231
-rw-r--r--spec/fixtures/api/schemas/entities/issue.json163
-rw-r--r--spec/fixtures/api/schemas/entities/member.json125
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_metrics.json43
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_poll_widget.json223
-rw-r--r--spec/fixtures/api/schemas/entities/test_case.json57
-rw-r--r--spec/fixtures/api/schemas/entities/trigger.json13
-rw-r--r--spec/fixtures/api/schemas/environment.json138
-rw-r--r--spec/fixtures/api/schemas/group_link/group_link.json84
-rw-r--r--spec/fixtures/api/schemas/issue.json135
-rw-r--r--spec/fixtures/api/schemas/jira_connect/author.json29
-rw-r--r--spec/fixtures/api/schemas/jira_connect/branch.json37
-rw-r--r--spec/fixtures/api/schemas/jira_connect/commit.json58
-rw-r--r--spec/fixtures/api/schemas/jira_connect/file.json29
-rw-r--r--spec/fixtures/api/schemas/jira_connect/pull_request.json69
-rw-r--r--spec/fixtures/api/schemas/jira_connect/repository.json45
-rw-r--r--spec/fixtures/api/schemas/job/build_trace_line.json22
-rw-r--r--spec/fixtures/api/schemas/merge_request.json15
-rw-r--r--spec/fixtures/api/schemas/ml/get_experiment.json27
-rw-r--r--spec/fixtures/api/schemas/ml/list_experiments.json11
-rw-r--r--spec/fixtures/api/schemas/ml/run.json30
-rw-r--r--spec/fixtures/api/schemas/ml/update_run.json48
-rw-r--r--spec/fixtures/api/schemas/pipeline_schedule.json153
-rw-r--r--spec/fixtures/api/schemas/prometheus/additional_metrics_query_result.json64
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/basic_environment.json34
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/basic_environments.json6
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/branch.json43
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/deploy_key.json48
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/deploy_token.json4
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/environments.json7
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/feature_flag_scopes.json7
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/feature_flags.json7
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/integration.json76
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/issue.json326
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/issues.json7
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/labels/label_with_counts.json30
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/labels/project_label.json29
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/labels/project_label_with_counts.json16
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/merge_request.json261
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/merge_request_simple.json73
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/milestone.json89
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/milestone_with_stats.json105
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/notes.json139
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/packages/package.json7
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json61
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json63
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/project_hooks.json8
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/snippets.json111
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/system_hooks.json7
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/user/admin.json146
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/user/login.json35
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/user/public.json138
-rw-r--r--spec/fixtures/api/schemas/registry/repository.json35
-rw-r--r--spec/fixtures/api/schemas/release.json58
-rw-r--r--spec/fixtures/api/schemas/variable.json34
-rw-r--r--spec/fixtures/ce_sample_schema.json1
-rw-r--r--spec/fixtures/clusters/chain_certificates.pem136
-rw-r--r--spec/fixtures/clusters/intermediate_certificate.pem44
-rw-r--r--spec/fixtures/clusters/leaf_certificate.pem54
-rw-r--r--spec/fixtures/clusters/root_certificate.pem38
-rw-r--r--spec/fixtures/config/redis_cluster_format_host.yml29
-rw-r--r--spec/fixtures/gitlab/database/structure_example.sql14
-rw-r--r--spec/fixtures/gitlab/database/structure_example_cleaned.sql13
-rw-r--r--spec/fixtures/lib/gitlab/import_export/complex/tree/project/services.ndjson1
-rw-r--r--spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metric_label_values_variable_full_syntax.json19
-rw-r--r--spec/fixtures/markdown.md.erb4
-rw-r--r--spec/fixtures/markdown/markdown_golden_master_examples.yml10
-rw-r--r--spec/fixtures/pager_duty/webhook_incident_trigger.json284
-rw-r--r--spec/frontend/__helpers__/dom_events_helper.js8
-rw-r--r--spec/frontend/__helpers__/filtered_search_spec_helper.js4
-rw-r--r--spec/frontend/__helpers__/graphql_helpers.js14
-rw-r--r--spec/frontend/__helpers__/graphql_helpers_spec.js23
-rw-r--r--spec/frontend/__helpers__/graphql_transformer.js4
-rw-r--r--spec/frontend/__helpers__/jest_helpers.js22
-rw-r--r--spec/frontend/__helpers__/mock_window_location_helper.js15
-rw-r--r--spec/frontend/__helpers__/raw_transformer.js2
-rw-r--r--spec/frontend/__helpers__/set_timeout_promise_helper.js4
-rw-r--r--spec/frontend/__helpers__/web_worker_transformer.js10
-rw-r--r--spec/frontend/__helpers__/yaml_transformer.js2
-rw-r--r--spec/frontend/__mocks__/@gitlab/ui.js2
-rw-r--r--spec/frontend/admin/background_migrations/components/database_listbox_spec.js10
-rw-r--r--spec/frontend/admin/broadcast_messages/components/datetime_picker_spec.js46
-rw-r--r--spec/frontend/admin/broadcast_messages/components/message_form_spec.js201
-rw-r--r--spec/frontend/admin/broadcast_messages/mock_data.js8
-rw-r--r--spec/frontend/admin/signup_restrictions/components/signup_form_spec.js1
-rw-r--r--spec/frontend/admin/signup_restrictions/mock_data.js2
-rw-r--r--spec/frontend/admin/signup_restrictions/utils_spec.js1
-rw-r--r--spec/frontend/alerts_settings/components/__snapshots__/alerts_form_spec.js.snap2
-rw-r--r--spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js4
-rw-r--r--spec/frontend/analytics/cycle_analytics/__snapshots__/total_time_spec.js.snap (renamed from spec/frontend/cycle_analytics/__snapshots__/total_time_spec.js.snap)0
-rw-r--r--spec/frontend/analytics/cycle_analytics/base_spec.js265
-rw-r--r--spec/frontend/analytics/cycle_analytics/filter_bar_spec.js229
-rw-r--r--spec/frontend/analytics/cycle_analytics/formatted_stage_count_spec.js34
-rw-r--r--spec/frontend/analytics/cycle_analytics/mock_data.js261
-rw-r--r--spec/frontend/analytics/cycle_analytics/path_navigation_spec.js150
-rw-r--r--spec/frontend/analytics/cycle_analytics/stage_table_spec.js371
-rw-r--r--spec/frontend/analytics/cycle_analytics/store/actions_spec.js518
-rw-r--r--spec/frontend/analytics/cycle_analytics/store/getters_spec.js42
-rw-r--r--spec/frontend/analytics/cycle_analytics/store/mutations_spec.js132
-rw-r--r--spec/frontend/analytics/cycle_analytics/total_time_spec.js45
-rw-r--r--spec/frontend/analytics/cycle_analytics/utils_spec.js171
-rw-r--r--spec/frontend/analytics/cycle_analytics/value_stream_filters_spec.js91
-rw-r--r--spec/frontend/analytics/cycle_analytics/value_stream_metrics_spec.js (renamed from spec/frontend/cycle_analytics/value_stream_metrics_spec.js)0
-rw-r--r--spec/frontend/api_spec.js20
-rw-r--r--spec/frontend/batch_comments/components/draft_note_spec.js8
-rw-r--r--spec/frontend/batch_comments/components/preview_item_spec.js3
-rw-r--r--spec/frontend/batch_comments/components/publish_dropdown_spec.js3
-rw-r--r--spec/frontend/behaviors/markdown/render_observability_spec.js38
-rw-r--r--spec/frontend/blob/openapi/index_spec.js2
-rw-r--r--spec/frontend/blob_edit/blob_bundle_spec.js24
-rw-r--r--spec/frontend/boards/board_list_spec.js11
-rw-r--r--spec/frontend/boards/components/board_content_sidebar_spec.js16
-rw-r--r--spec/frontend/boards/components/board_content_spec.js28
-rw-r--r--spec/frontend/boards/components/board_filtered_search_spec.js16
-rw-r--r--spec/frontend/boards/components/issue_board_filtered_search_spec.js8
-rw-r--r--spec/frontend/boards/components/sidebar/board_sidebar_time_tracker_spec.js15
-rw-r--r--spec/frontend/boards/mock_data.js40
-rw-r--r--spec/frontend/boards/project_select_spec.js2
-rw-r--r--spec/frontend/captcha/captcha_modal_axios_interceptor_spec.js11
-rw-r--r--spec/frontend/ci/ci_lint/components/ci_lint_spec.js118
-rw-r--r--spec/frontend/ci/ci_lint/mock_data.js23
-rw-r--r--spec/frontend/ci/pipeline_editor/components/code_snippet_alert/code_snippet_alert_spec.js61
-rw-r--r--spec/frontend/ci/pipeline_editor/components/commit/commit_form_spec.js158
-rw-r--r--spec/frontend/ci/pipeline_editor/components/commit/commit_section_spec.js287
-rw-r--r--spec/frontend/ci/pipeline_editor/components/drawer/cards/first_pipeline_card_spec.js60
-rw-r--r--spec/frontend/ci/pipeline_editor/components/drawer/cards/getting_started_card_spec.js26
-rw-r--r--spec/frontend/ci/pipeline_editor/components/drawer/cards/pipeline_config_reference_card_spec.js89
-rw-r--r--spec/frontend/ci/pipeline_editor/components/drawer/cards/visualize_and_lint_card_spec.js26
-rw-r--r--spec/frontend/ci/pipeline_editor/components/drawer/pipeline_editor_drawer_spec.js27
-rw-r--r--spec/frontend/ci/pipeline_editor/components/drawer/ui/demo_job_pill_spec.js27
-rw-r--r--spec/frontend/ci/pipeline_editor/components/editor/ci_config_merged_preview_spec.js69
-rw-r--r--spec/frontend/ci/pipeline_editor/components/editor/ci_editor_header_spec.js115
-rw-r--r--spec/frontend/ci/pipeline_editor/components/editor/text_editor_spec.js134
-rw-r--r--spec/frontend/ci/pipeline_editor/components/file-nav/branch_switcher_spec.js432
-rw-r--r--spec/frontend/ci/pipeline_editor/components/file-nav/pipeline_editor_file_nav_spec.js126
-rw-r--r--spec/frontend/ci/pipeline_editor/components/file-tree/container_spec.js138
-rw-r--r--spec/frontend/ci/pipeline_editor/components/file-tree/file_item_spec.js52
-rw-r--r--spec/frontend/ci/pipeline_editor/components/header/pipeline_editor_header_spec.js53
-rw-r--r--spec/frontend/ci/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js109
-rw-r--r--spec/frontend/ci/pipeline_editor/components/header/pipeline_status_spec.js132
-rw-r--r--spec/frontend/ci/pipeline_editor/components/header/pipline_editor_mini_graph_spec.js109
-rw-r--r--spec/frontend/ci/pipeline_editor/components/header/validation_segment_spec.js197
-rw-r--r--spec/frontend/ci/pipeline_editor/components/lint/ci_lint_results_spec.js177
-rw-r--r--spec/frontend/ci/pipeline_editor/components/lint/ci_lint_warnings_spec.js54
-rw-r--r--spec/frontend/ci/pipeline_editor/components/pipeline_editor_tabs_spec.js342
-rw-r--r--spec/frontend/ci/pipeline_editor/components/popovers/file_tree_popover_spec.js56
-rw-r--r--spec/frontend/ci/pipeline_editor/components/popovers/validate_pipeline_popover_spec.js43
-rw-r--r--spec/frontend/ci/pipeline_editor/components/popovers/walkthrough_popover_spec.js29
-rw-r--r--spec/frontend/ci/pipeline_editor/components/ui/confirm_unsaved_changes_dialog_spec.js42
-rw-r--r--spec/frontend/ci/pipeline_editor/components/ui/editor_tab_spec.js200
-rw-r--r--spec/frontend/ci/pipeline_editor/components/ui/pipeline_editor_empty_state_spec.js92
-rw-r--r--spec/frontend/ci/pipeline_editor/components/ui/pipeline_editor_messages_spec.js149
-rw-r--r--spec/frontend/ci/pipeline_editor/components/validate/ci_validate_spec.js314
-rw-r--r--spec/frontend/ci/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap73
-rw-r--r--spec/frontend/ci/pipeline_editor/graphql/resolvers_spec.js52
-rw-r--r--spec/frontend/ci/pipeline_editor/mock_data.js541
-rw-r--r--spec/frontend/ci/pipeline_editor/pipeline_editor_app_spec.js589
-rw-r--r--spec/frontend/ci/pipeline_editor/pipeline_editor_home_spec.js330
-rw-r--r--spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_form_spec.js149
-rw-r--r--spec/frontend/ci/reports/codequality_report/components/codequality_issue_body_spec.js102
-rw-r--r--spec/frontend/ci/reports/codequality_report/mock_data.js (renamed from spec/frontend/reports/codequality_report/mock_data.js)0
-rw-r--r--spec/frontend/ci/reports/codequality_report/store/actions_spec.js185
-rw-r--r--spec/frontend/ci/reports/codequality_report/store/getters_spec.js94
-rw-r--r--spec/frontend/ci/reports/codequality_report/store/mutations_spec.js100
-rw-r--r--spec/frontend/ci/reports/codequality_report/store/utils/codequality_parser_spec.js86
-rw-r--r--spec/frontend/ci/reports/components/__snapshots__/grouped_issues_list_spec.js.snap (renamed from spec/frontend/reports/components/__snapshots__/grouped_issues_list_spec.js.snap)0
-rw-r--r--spec/frontend/ci/reports/components/__snapshots__/issue_status_icon_spec.js.snap (renamed from spec/frontend/reports/components/__snapshots__/issue_status_icon_spec.js.snap)0
-rw-r--r--spec/frontend/ci/reports/components/grouped_issues_list_spec.js87
-rw-r--r--spec/frontend/ci/reports/components/issue_status_icon_spec.js29
-rw-r--r--spec/frontend/ci/reports/components/report_item_spec.js34
-rw-r--r--spec/frontend/ci/reports/components/report_link_spec.js56
-rw-r--r--spec/frontend/ci/reports/components/report_section_spec.js285
-rw-r--r--spec/frontend/ci/reports/components/summary_row_spec.js68
-rw-r--r--spec/frontend/ci/reports/mock_data/mock_data.js (renamed from spec/frontend/reports/mock_data/mock_data.js)0
-rw-r--r--spec/frontend/ci/reports/mock_data/new_and_fixed_failures_report.json70
-rw-r--r--spec/frontend/ci/reports/mock_data/new_errors_report.json53
-rw-r--r--spec/frontend/ci/reports/mock_data/new_failures_report.json55
-rw-r--r--spec/frontend/ci/reports/mock_data/new_failures_with_null_files_report.json55
-rw-r--r--spec/frontend/ci/reports/mock_data/no_failures_report.json43
-rw-r--r--spec/frontend/ci/reports/mock_data/recent_failures_report.json70
-rw-r--r--spec/frontend/ci/reports/mock_data/resolved_failures.json73
-rw-r--r--spec/frontend/ci/runner/admin_runner_show/admin_runner_show_app_spec.js51
-rw-r--r--spec/frontend/ci/runner/admin_runners/admin_runners_app_spec.js37
-rw-r--r--spec/frontend/ci/runner/components/cells/runner_stacked_summary_cell_spec.js164
-rw-r--r--spec/frontend/ci/runner/components/cells/runner_summary_cell_spec.js183
-rw-r--r--spec/frontend/ci/runner/components/runner_filtered_search_bar_spec.js3
-rw-r--r--spec/frontend/ci/runner/components/runner_job_status_badge_spec.js51
-rw-r--r--spec/frontend/ci/runner/components/runner_list_spec.js15
-rw-r--r--spec/frontend/ci/runner/components/runner_status_badge_spec.js4
-rw-r--r--spec/frontend/ci/runner/components/search_tokens/tag_token_spec.js4
-rw-r--r--spec/frontend/ci/runner/components/stat/runner_stats_spec.js53
-rw-r--r--spec/frontend/ci/runner/group_runners/group_runners_app_spec.js16
-rw-r--r--spec/frontend/ci/runner/mock_data.js7
-rw-r--r--spec/frontend/ci/runner/runner_search_utils_spec.js16
-rw-r--r--spec/frontend/ci_lint/components/ci_lint_spec.js118
-rw-r--r--spec/frontend/ci_lint/mock_data.js23
-rw-r--r--spec/frontend/ci_variable_list/components/ci_admin_variables_spec.js1
-rw-r--r--spec/frontend/ci_variable_list/components/ci_group_variables_spec.js1
-rw-r--r--spec/frontend/ci_variable_list/components/ci_project_variables_spec.js1
-rw-r--r--spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js75
-rw-r--r--spec/frontend/ci_variable_list/components/ci_variable_settings_spec.js27
-rw-r--r--spec/frontend/ci_variable_list/components/ci_variable_shared_spec.js38
-rw-r--r--spec/frontend/ci_variable_list/components/ci_variable_table_spec.js80
-rw-r--r--spec/frontend/ci_variable_list/mocks.js16
-rw-r--r--spec/frontend/clusters_list/components/agent_token_spec.js40
-rw-r--r--spec/frontend/clusters_list/components/agents_spec.js39
-rw-r--r--spec/frontend/clusters_list/components/available_agents_dropwdown_spec.js95
-rw-r--r--spec/frontend/clusters_list/components/clusters_spec.js4
-rw-r--r--spec/frontend/clusters_list/store/actions_spec.js4
-rw-r--r--spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap8
-rw-r--r--spec/frontend/content_editor/components/content_editor_spec.js19
-rw-r--r--spec/frontend/content_editor/components/formatting_toolbar_spec.js65
-rw-r--r--spec/frontend/content_editor/components/top_toolbar_spec.js65
-rw-r--r--spec/frontend/content_editor/extensions/comment_spec.js30
-rw-r--r--spec/frontend/content_editor/services/gl_api_markdown_deserializer_spec.js12
-rw-r--r--spec/frontend/content_editor/services/markdown_serializer_spec.js53
-rw-r--r--spec/frontend/content_editor/test_utils.js4
-rw-r--r--spec/frontend/crm/contact_form_wrapper_spec.js18
-rw-r--r--spec/frontend/crm/crm_form_spec.js339
-rw-r--r--spec/frontend/crm/form_spec.js339
-rw-r--r--spec/frontend/crm/organization_form_wrapper_spec.js4
-rw-r--r--spec/frontend/cycle_analytics/base_spec.js265
-rw-r--r--spec/frontend/cycle_analytics/filter_bar_spec.js223
-rw-r--r--spec/frontend/cycle_analytics/formatted_stage_count_spec.js34
-rw-r--r--spec/frontend/cycle_analytics/mock_data.js261
-rw-r--r--spec/frontend/cycle_analytics/path_navigation_spec.js150
-rw-r--r--spec/frontend/cycle_analytics/stage_table_spec.js371
-rw-r--r--spec/frontend/cycle_analytics/store/actions_spec.js518
-rw-r--r--spec/frontend/cycle_analytics/store/getters_spec.js42
-rw-r--r--spec/frontend/cycle_analytics/store/mutations_spec.js132
-rw-r--r--spec/frontend/cycle_analytics/total_time_spec.js45
-rw-r--r--spec/frontend/cycle_analytics/utils_spec.js171
-rw-r--r--spec/frontend/cycle_analytics/value_stream_filters_spec.js91
-rw-r--r--spec/frontend/deploy_freeze/store/actions_spec.js88
-rw-r--r--spec/frontend/deploy_tokens/components/new_deploy_token_spec.js72
-rw-r--r--spec/frontend/design_management/components/design_todo_button_spec.js2
-rw-r--r--spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap342
-rw-r--r--spec/frontend/design_management/components/upload/design_version_dropdown_spec.js19
-rw-r--r--spec/frontend/diffs/components/diff_code_quality_spec.js15
-rw-r--r--spec/frontend/diffs/components/diff_discussion_reply_spec.js36
-rw-r--r--spec/frontend/diffs/components/diff_discussions_spec.js3
-rw-r--r--spec/frontend/diffs/mock_data/diff_code_quality.js28
-rw-r--r--spec/frontend/diffs/store/actions_spec.js44
-rw-r--r--spec/frontend/diffs/utils/merge_request_spec.js40
-rw-r--r--spec/frontend/editor/components/source_editor_toolbar_button_spec.js73
-rw-r--r--spec/frontend/editor/schema/ci/ci_schema_spec.js14
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/artifacts.yml13
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml31
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/hooks.yml10
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/id_tokens.yml11
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/secrets.yml39
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables/invalid_options.yml4
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/positive_tests/artifacts.yml10
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/positive_tests/cache.yml17
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/positive_tests/hooks.yml10
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/positive_tests/id_tokens.yml11
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/positive_tests/secrets.yml28
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/positive_tests/variables.yml9
-rw-r--r--spec/frontend/editor/source_editor_markdown_ext_spec.js14
-rw-r--r--spec/frontend/environment.js11
-rw-r--r--spec/frontend/environments/environment_details_page_spec.js50
-rw-r--r--spec/frontend/environments/helpers/__snapshots__/deployment_data_transformation_helper_spec.js.snap127
-rw-r--r--spec/frontend/environments/helpers/deployment_data_transformation_helper_spec.js96
-rw-r--r--spec/frontend/feature_flags/components/feature_flags_table_spec.js39
-rw-r--r--spec/frontend/feature_flags/components/strategy_label_spec.js61
-rw-r--r--spec/frontend/feature_highlight/feature_highlight_helper_spec.js6
-rw-r--r--spec/frontend/filtered_search/components/recent_searches_dropdown_content_spec.js3
-rw-r--r--spec/frontend/filtered_search/filtered_search_manager_spec.js6
-rw-r--r--spec/frontend/filtered_search/filtered_search_visual_tokens_spec.js5
-rw-r--r--spec/frontend/filtered_search/visual_token_value_spec.js4
-rw-r--r--spec/frontend/fixtures/api_merge_requests.rb8
-rw-r--r--spec/frontend/fixtures/api_projects.rb8
-rw-r--r--spec/frontend/fixtures/environments.rb53
-rw-r--r--spec/frontend/fixtures/freeze_period.rb9
-rw-r--r--spec/frontend/fixtures/releases.rb18
-rw-r--r--spec/frontend/fixtures/runner_instructions.rb43
-rw-r--r--spec/frontend/fixtures/tabs.rb8
-rw-r--r--spec/frontend/flash_spec.js24
-rw-r--r--spec/frontend/gfm_auto_complete_spec.js3
-rw-r--r--spec/frontend/gitlab_version_check/components/security_patch_upgrade_alert_modal_spec.js202
-rw-r--r--spec/frontend/gitlab_version_check/components/security_patch_upgrade_alert_spec.js84
-rw-r--r--spec/frontend/gitlab_version_check/index_spec.js138
-rw-r--r--spec/frontend/gitlab_version_check/mock_data.js22
-rw-r--r--spec/frontend/gitlab_version_check/utils_spec.js35
-rw-r--r--spec/frontend/groups/components/app_spec.js66
-rw-r--r--spec/frontend/groups/components/empty_state_spec.js78
-rw-r--r--spec/frontend/groups/components/empty_states/archived_projects_empty_state_spec.js27
-rw-r--r--spec/frontend/groups/components/empty_states/shared_projects_empty_state_spec.js27
-rw-r--r--spec/frontend/groups/components/empty_states/subgroups_and_projects_empty_state_spec.js78
-rw-r--r--spec/frontend/groups/components/group_name_and_path_spec.js2
-rw-r--r--spec/frontend/groups/components/groups_spec.js10
-rw-r--r--spec/frontend/groups/components/overview_tabs_spec.js33
-rw-r--r--spec/frontend/header_search/components/app_spec.js1
-rw-r--r--spec/frontend/ide/components/panes/right_spec.js33
-rw-r--r--spec/frontend/ide/components/pipelines/list_spec.js2
-rw-r--r--spec/frontend/ide/components/repo_editor_spec.js94
-rw-r--r--spec/frontend/ide/components/switch_editors/switch_editors_view_spec.js214
-rw-r--r--spec/frontend/ide/init_gitlab_web_ide_spec.js164
-rw-r--r--spec/frontend/ide/lib/common/model_spec.js9
-rw-r--r--spec/frontend/ide/lib/gitlab_web_ide/get_base_config_spec.js22
-rw-r--r--spec/frontend/ide/lib/gitlab_web_ide/setup_root_element_spec.js32
-rw-r--r--spec/frontend/ide/remote/index_spec.js91
-rw-r--r--spec/frontend/ide/services/index_spec.js2
-rw-r--r--spec/frontend/ide/stores/modules/terminal/actions/checks_spec.js4
-rw-r--r--spec/frontend/ide/stores/modules/terminal/actions/session_controls_spec.js4
-rw-r--r--spec/frontend/ide/stores/modules/terminal/messages_spec.js4
-rw-r--r--spec/frontend/import_entities/components/group_dropdown_spec.js76
-rw-r--r--spec/frontend/import_entities/import_groups/components/import_table_spec.js38
-rw-r--r--spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js44
-rw-r--r--spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js28
-rw-r--r--spec/frontend/import_entities/import_groups/graphql/fixtures.js39
-rw-r--r--spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js66
-rw-r--r--spec/frontend/import_entities/import_projects/components/provider_repo_table_row_spec.js64
-rw-r--r--spec/frontend/import_entities/import_projects/store/actions_spec.js230
-rw-r--r--spec/frontend/import_entities/import_projects/store/getters_spec.js19
-rw-r--r--spec/frontend/import_entities/import_projects/store/mutations_spec.js79
-rw-r--r--spec/frontend/incidents_settings/components/incidents_settings_service_spec.js4
-rw-r--r--spec/frontend/integrations/edit/components/dynamic_field_spec.js26
-rw-r--r--spec/frontend/integrations/edit/components/integration_form_actions_spec.js227
-rw-r--r--spec/frontend/integrations/edit/components/integration_form_spec.js420
-rw-r--r--spec/frontend/invite_members/components/import_project_members_modal_spec.js46
-rw-r--r--spec/frontend/invite_members/components/invite_group_notification_spec.js42
-rw-r--r--spec/frontend/invite_members/components/invite_groups_modal_spec.js63
-rw-r--r--spec/frontend/invite_members/components/invite_members_modal_spec.js81
-rw-r--r--spec/frontend/invite_members/components/invite_modal_base_spec.js86
-rw-r--r--spec/frontend/invite_members/mock_data/group_modal.js2
-rw-r--r--spec/frontend/invite_members/utils/trigger_successful_invite_alert_spec.js54
-rw-r--r--spec/frontend/issuable/bulk_update_sidebar/components/move_issues_button_spec.js554
-rw-r--r--spec/frontend/issuable/bulk_update_sidebar/components/status_dropdown_spec.js75
-rw-r--r--spec/frontend/issuable/bulk_update_sidebar/components/subscriptions_dropdown_spec.js76
-rw-r--r--spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js332
-rw-r--r--spec/frontend/issues/dashboard/mock_data.js88
-rw-r--r--spec/frontend/issues/list/components/empty_state_with_any_issues_spec.js68
-rw-r--r--spec/frontend/issues/list/components/empty_state_without_any_issues_spec.js211
-rw-r--r--spec/frontend/issues/list/components/issue_card_statistics_spec.js64
-rw-r--r--spec/frontend/issues/list/components/issues_list_app_spec.js192
-rw-r--r--spec/frontend/issues/list/mock_data.js40
-rw-r--r--spec/frontend/issues/list/utils_spec.js28
-rw-r--r--spec/frontend/issues/related_merge_requests/store/actions_spec.js6
-rw-r--r--spec/frontend/issues/show/components/app_spec.js30
-rw-r--r--spec/frontend/issues/show/components/description_spec.js10
-rw-r--r--spec/frontend/issues/show/components/header_actions_spec.js34
-rw-r--r--spec/frontend/issues/show/components/incidents/edit_timeline_event_spec.js8
-rw-r--r--spec/frontend/issues/show/components/incidents/incident_tabs_spec.js83
-rw-r--r--spec/frontend/issues/show/components/incidents/mock_data.js21
-rw-r--r--spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js41
-rw-r--r--spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js27
-rw-r--r--spec/frontend/issues/show/components/incidents/timeline_events_list_spec.js17
-rw-r--r--spec/frontend/issues/show/components/locked_warning_spec.js55
-rw-r--r--spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js82
-rw-r--r--spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/index_spec.js56
-rw-r--r--spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions_spec.js35
-rw-r--r--spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap20
-rw-r--r--spec/frontend/jobs/components/filtered_search/jobs_filtered_search_spec.js14
-rw-r--r--spec/frontend/jobs/components/filtered_search/tokens/job_status_token_spec.js8
-rw-r--r--spec/frontend/jobs/components/job/empty_state_spec.js19
-rw-r--r--spec/frontend/jobs/components/job/job_app_spec.js175
-rw-r--r--spec/frontend/jobs/components/job/job_sidebar_retry_button_spec.js1
-rw-r--r--spec/frontend/jobs/components/job/legacy_manual_variables_form_spec.js156
-rw-r--r--spec/frontend/jobs/components/job/legacy_sidebar_header_spec.js109
-rw-r--r--spec/frontend/jobs/components/job/manual_variables_form_spec.js232
-rw-r--r--spec/frontend/jobs/components/job/mock_data.js76
-rw-r--r--spec/frontend/jobs/components/job/sidebar_header_spec.js128
-rw-r--r--spec/frontend/jobs/mock_data.js6
-rw-r--r--spec/frontend/language_switcher/components/app_spec.js62
-rw-r--r--spec/frontend/language_switcher/mock_data.js26
-rw-r--r--spec/frontend/lib/dompurify_spec.js5
-rw-r--r--spec/frontend/lib/utils/common_utils_spec.js32
-rw-r--r--spec/frontend/lib/utils/create_and_submit_form_spec.js73
-rw-r--r--spec/frontend/lib/utils/dom_utils_spec.js18
-rw-r--r--spec/frontend/lib/utils/poll_until_complete_spec.js4
-rw-r--r--spec/frontend/lib/utils/url_utility_spec.js13
-rw-r--r--spec/frontend/listbox/index_spec.js4
-rw-r--r--spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js13
-rw-r--r--spec/frontend/merge_request_tabs_spec.js1
-rw-r--r--spec/frontend/merge_requests/components/target_project_dropdown_spec.js80
-rw-r--r--spec/frontend/milestones/components/milestone_combobox_spec.js4
-rw-r--r--spec/frontend/ml/experiment_tracking/components/__snapshots__/experiment_spec.js.snap223
-rw-r--r--spec/frontend/ml/experiment_tracking/components/__snapshots__/ml_candidate_spec.js.snap233
-rw-r--r--spec/frontend/ml/experiment_tracking/components/__snapshots__/ml_experiment_spec.js.snap284
-rw-r--r--spec/frontend/ml/experiment_tracking/components/experiment_spec.js44
-rw-r--r--spec/frontend/ml/experiment_tracking/components/incubation_alert_spec.js2
-rw-r--r--spec/frontend/ml/experiment_tracking/components/ml_candidate_spec.js43
-rw-r--r--spec/frontend/ml/experiment_tracking/components/ml_experiment_spec.js44
-rw-r--r--spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap4
-rw-r--r--spec/frontend/monitoring/components/refresh_button_spec.js14
-rw-r--r--spec/frontend/monitoring/requests/index_spec.js19
-rw-r--r--spec/frontend/monitoring/store/actions_spec.js11
-rw-r--r--spec/frontend/monitoring/utils_spec.js1
-rw-r--r--spec/frontend/nav/components/new_nav_toggle_spec.js98
-rw-r--r--spec/frontend/notes/components/discussion_notes_spec.js3
-rw-r--r--spec/frontend/notes/components/noteable_discussion_spec.js3
-rw-r--r--spec/frontend/notes/components/notes_app_spec.js110
-rw-r--r--spec/frontend/notes/deprecated_notes_spec.js53
-rw-r--r--spec/frontend/notes/stores/actions_spec.js67
-rw-r--r--spec/frontend/observability/observability_app_spec.js186
-rw-r--r--spec/frontend/observability/skeleton_spec.js96
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_alert_spec.js21
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js2
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js9
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js35
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js2
-rw-r--r--spec/frontend/packages_and_registries/dependency_proxy/app_spec.js3
-rw-r--r--spec/frontend/packages_and_registries/dependency_proxy/components/manifest_list_spec.js8
-rw-r--r--spec/frontend/packages_and_registries/harbor_registry/pages/details_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/packages_list_app_spec.js6
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/pypi_installation_spec.js.snap4
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/list/package_list_row_spec.js31
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js7
-rw-r--r--spec/frontend/packages_and_registries/package_registry/mock_data.js10
-rw-r--r--spec/frontend/packages_and_registries/package_registry/pages/details_spec.js32
-rw-r--r--spec/frontend/packages_and_registries/settings/project/settings/components/container_expiration_policy_form_spec.js3
-rw-r--r--spec/frontend/packages_and_registries/shared/utils_spec.js2
-rw-r--r--spec/frontend/pages/dashboard/todos/index/todos_spec.js117
-rw-r--r--spec/frontend/pages/import/fogbugz/new_user_map/components/user_select_spec.js6
-rw-r--r--spec/frontend/pages/projects/forks/new/components/fork_form_spec.js172
-rw-r--r--spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap88
-rw-r--r--spec/frontend/pages/projects/graphs/code_coverage_spec.js24
-rw-r--r--spec/frontend/pages/projects/pipeline_schedules/shared/components/timezone_dropdown_spec.js116
-rw-r--r--spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js211
-rw-r--r--spec/frontend/pages/shared/wikis/components/wiki_content_spec.js4
-rw-r--r--spec/frontend/performance_bar/components/detailed_metric_spec.js13
-rw-r--r--spec/frontend/pipeline_editor/components/code_snippet_alert/code_snippet_alert_spec.js61
-rw-r--r--spec/frontend/pipeline_editor/components/commit/commit_form_spec.js158
-rw-r--r--spec/frontend/pipeline_editor/components/commit/commit_section_spec.js287
-rw-r--r--spec/frontend/pipeline_editor/components/drawer/cards/first_pipeline_card_spec.js60
-rw-r--r--spec/frontend/pipeline_editor/components/drawer/cards/getting_started_card_spec.js26
-rw-r--r--spec/frontend/pipeline_editor/components/drawer/cards/pipeline_config_reference_card_spec.js89
-rw-r--r--spec/frontend/pipeline_editor/components/drawer/cards/visualize_and_lint_card_spec.js26
-rw-r--r--spec/frontend/pipeline_editor/components/drawer/pipeline_editor_drawer_spec.js27
-rw-r--r--spec/frontend/pipeline_editor/components/drawer/ui/demo_job_pill_spec.js27
-rw-r--r--spec/frontend/pipeline_editor/components/editor/ci_config_merged_preview_spec.js69
-rw-r--r--spec/frontend/pipeline_editor/components/editor/ci_editor_header_spec.js115
-rw-r--r--spec/frontend/pipeline_editor/components/editor/text_editor_spec.js134
-rw-r--r--spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js432
-rw-r--r--spec/frontend/pipeline_editor/components/file-nav/pipeline_editor_file_nav_spec.js126
-rw-r--r--spec/frontend/pipeline_editor/components/file-tree/container_spec.js138
-rw-r--r--spec/frontend/pipeline_editor/components/file-tree/file_item_spec.js52
-rw-r--r--spec/frontend/pipeline_editor/components/header/pipeline_editor_header_spec.js53
-rw-r--r--spec/frontend/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js109
-rw-r--r--spec/frontend/pipeline_editor/components/header/pipeline_status_spec.js132
-rw-r--r--spec/frontend/pipeline_editor/components/header/pipline_editor_mini_graph_spec.js109
-rw-r--r--spec/frontend/pipeline_editor/components/header/validation_segment_spec.js197
-rw-r--r--spec/frontend/pipeline_editor/components/lint/ci_lint_results_spec.js177
-rw-r--r--spec/frontend/pipeline_editor/components/lint/ci_lint_warnings_spec.js54
-rw-r--r--spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js342
-rw-r--r--spec/frontend/pipeline_editor/components/popovers/file_tree_popover_spec.js56
-rw-r--r--spec/frontend/pipeline_editor/components/popovers/validate_pipeline_popover_spec.js43
-rw-r--r--spec/frontend/pipeline_editor/components/popovers/walkthrough_popover_spec.js29
-rw-r--r--spec/frontend/pipeline_editor/components/ui/confirm_unsaved_changes_dialog_spec.js42
-rw-r--r--spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js200
-rw-r--r--spec/frontend/pipeline_editor/components/ui/pipeline_editor_empty_state_spec.js92
-rw-r--r--spec/frontend/pipeline_editor/components/ui/pipeline_editor_messages_spec.js149
-rw-r--r--spec/frontend/pipeline_editor/components/validate/ci_validate_spec.js314
-rw-r--r--spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap73
-rw-r--r--spec/frontend/pipeline_editor/graphql/resolvers_spec.js52
-rw-r--r--spec/frontend/pipeline_editor/mock_data.js541
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_app_spec.js589
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_home_spec.js330
-rw-r--r--spec/frontend/pipeline_new/components/legacy_pipeline_new_form_spec.js456
-rw-r--r--spec/frontend/pipeline_new/components/pipeline_new_form_spec.js4
-rw-r--r--spec/frontend/pipeline_new/mock_data.js4
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_mini_graph_spec.js12
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stage_spec.js19
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stages_spec.js15
-rw-r--r--spec/frontend/pipelines/components/pipelines_filtered_search_spec.js23
-rw-r--r--spec/frontend/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates_spec.js2
-rw-r--r--spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js2
-rw-r--r--spec/frontend/pipelines/pipelines_table_spec.js7
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_status_token_spec.js8
-rw-r--r--spec/frontend/popovers/components/popovers_spec.js3
-rw-r--r--spec/frontend/projects/commit/components/branches_dropdown_spec.js38
-rw-r--r--spec/frontend/projects/new/components/new_project_url_select_spec.js32
-rw-r--r--spec/frontend/projects/project_new_spec.js55
-rw-r--r--spec/frontend/projects/settings/branch_rules/components/view/index_spec.js15
-rw-r--r--spec/frontend/projects/settings/branch_rules/components/view/mock_data.js4
-rw-r--r--spec/frontend/projects/settings/mock_data.js57
-rw-r--r--spec/frontend/projects/settings/repository/branch_rules/app_spec.js7
-rw-r--r--spec/frontend/projects/settings/repository/branch_rules/components/branch_rule_spec.js20
-rw-r--r--spec/frontend/projects/settings/repository/branch_rules/mock_data.js68
-rw-r--r--spec/frontend/projects/settings/utils_spec.js11
-rw-r--r--spec/frontend/releases/__snapshots__/util_spec.js.snap22
-rw-r--r--spec/frontend/releases/components/release_block_footer_spec.js231
-rw-r--r--spec/frontend/releases/components/release_block_spec.js7
-rw-r--r--spec/frontend/reports/codequality_report/components/codequality_issue_body_spec.js102
-rw-r--r--spec/frontend/reports/codequality_report/store/actions_spec.js185
-rw-r--r--spec/frontend/reports/codequality_report/store/getters_spec.js94
-rw-r--r--spec/frontend/reports/codequality_report/store/mutations_spec.js100
-rw-r--r--spec/frontend/reports/codequality_report/store/utils/codequality_parser_spec.js86
-rw-r--r--spec/frontend/reports/components/grouped_issues_list_spec.js87
-rw-r--r--spec/frontend/reports/components/issue_status_icon_spec.js29
-rw-r--r--spec/frontend/reports/components/report_item_spec.js34
-rw-r--r--spec/frontend/reports/components/report_link_spec.js56
-rw-r--r--spec/frontend/reports/components/report_section_spec.js285
-rw-r--r--spec/frontend/reports/components/summary_row_spec.js68
-rw-r--r--spec/frontend/reports/mock_data/new_and_fixed_failures_report.json55
-rw-r--r--spec/frontend/reports/mock_data/new_errors_report.json38
-rw-r--r--spec/frontend/reports/mock_data/new_failures_report.json40
-rw-r--r--spec/frontend/reports/mock_data/new_failures_with_null_files_report.json40
-rw-r--r--spec/frontend/reports/mock_data/no_failures_report.json28
-rw-r--r--spec/frontend/reports/mock_data/recent_failures_report.json52
-rw-r--r--spec/frontend/reports/mock_data/resolved_failures.json58
-rw-r--r--spec/frontend/repository/components/table/index_spec.js3
-rw-r--r--spec/frontend/repository/components/table/row_spec.js3
-rw-r--r--spec/frontend/repository/components/tree_content_spec.js1
-rw-r--r--spec/frontend/repository/utils/ref_switcher_utils_spec.js22
-rw-r--r--spec/frontend/search/mock_data.js1
-rw-r--r--spec/frontend/search/sidebar/components/confidentiality_filter_spec.js63
-rw-r--r--spec/frontend/search/sidebar/components/filters_spec.js40
-rw-r--r--spec/frontend/search/sidebar/components/scope_navigation_spec.js41
-rw-r--r--spec/frontend/search/sidebar/components/status_filter_spec.js63
-rw-r--r--spec/frontend/search/topbar/components/app_spec.js76
-rw-r--r--spec/frontend/self_monitor/store/actions_spec.js6
-rw-r--r--spec/frontend/sentry/index_spec.js60
-rw-r--r--spec/frontend/sentry/legacy_index_spec.js64
-rw-r--r--spec/frontend/sentry/legacy_sentry_config_spec.js215
-rw-r--r--spec/frontend/sentry/sentry_browser_wrapper_spec.js59
-rw-r--r--spec/frontend/sentry/sentry_config_spec.js131
-rw-r--r--spec/frontend/sidebar/assignees_realtime_spec.js85
-rw-r--r--spec/frontend/sidebar/assignees_spec.js205
-rw-r--r--spec/frontend/sidebar/components/assignees/assignee_title_spec.js (renamed from spec/frontend/sidebar/assignee_title_spec.js)0
-rw-r--r--spec/frontend/sidebar/components/assignees/assignees_realtime_spec.js85
-rw-r--r--spec/frontend/sidebar/components/assignees/assignees_spec.js205
-rw-r--r--spec/frontend/sidebar/components/assignees/issuable_assignees_spec.js (renamed from spec/frontend/sidebar/issuable_assignees_spec.js)0
-rw-r--r--spec/frontend/sidebar/components/assignees/sidebar_assignees_spec.js99
-rw-r--r--spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js4
-rw-r--r--spec/frontend/sidebar/components/copy/copy_email_to_clipboard_spec.js17
-rw-r--r--spec/frontend/sidebar/components/copy/copyable_field_spec.js77
-rw-r--r--spec/frontend/sidebar/components/copy/sidebar_reference_widget_spec.js93
-rw-r--r--spec/frontend/sidebar/components/copy_email_to_clipboard_spec.js17
-rw-r--r--spec/frontend/sidebar/components/crm_contacts/crm_contacts_spec.js96
-rw-r--r--spec/frontend/sidebar/components/crm_contacts_spec.js96
-rw-r--r--spec/frontend/sidebar/components/incidents/escalation_status_spec.js6
-rw-r--r--spec/frontend/sidebar/components/incidents/escalation_utils_spec.js4
-rw-r--r--spec/frontend/sidebar/components/incidents/mock_data.js2
-rw-r--r--spec/frontend/sidebar/components/incidents/sidebar_escalation_status_spec.js83
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_button_spec.js89
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_create_view_spec.js211
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view_spec.js413
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_spec.js68
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_title_spec.js59
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_value_collapsed_spec.js75
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_value_spec.js99
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_vue/label_item_spec.js92
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_vue/labels_select_root_spec.js231
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_vue/mock_data.js (renamed from spec/frontend/vue_shared/components/sidebar/labels_select_vue/mock_data.js)0
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_vue/store/actions_spec.js265
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_vue/store/getters_spec.js74
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_vue/store/mutations_spec.js232
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view_spec.js239
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_contents_labels_view_spec.js170
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_contents_spec.js207
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_footer_spec.js57
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_header_spec.js92
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_value_spec.js104
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_widget/embedded_labels_list_spec.js77
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_widget/label_item_spec.js38
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_widget/labels_select_root_spec.js267
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_widget/mock_data.js (renamed from spec/frontend/vue_shared/components/sidebar/labels_select_widget/mock_data.js)0
-rw-r--r--spec/frontend/sidebar/components/lock/__snapshots__/edit_form_spec.js.snap (renamed from spec/frontend/sidebar/lock/__snapshots__/edit_form_spec.js.snap)0
-rw-r--r--spec/frontend/sidebar/components/lock/constants.js (renamed from spec/frontend/sidebar/lock/constants.js)0
-rw-r--r--spec/frontend/sidebar/components/lock/edit_form_buttons_spec.js (renamed from spec/frontend/sidebar/lock/edit_form_buttons_spec.js)0
-rw-r--r--spec/frontend/sidebar/components/lock/edit_form_spec.js (renamed from spec/frontend/sidebar/lock/edit_form_spec.js)0
-rw-r--r--spec/frontend/sidebar/components/lock/issuable_lock_form_spec.js (renamed from spec/frontend/sidebar/lock/issuable_lock_form_spec.js)0
-rw-r--r--spec/frontend/sidebar/components/move/issuable_move_dropdown_spec.js388
-rw-r--r--spec/frontend/sidebar/components/move/move_issues_button_spec.js554
-rw-r--r--spec/frontend/sidebar/components/participants/participants_spec.js (renamed from spec/frontend/sidebar/participants_spec.js)0
-rw-r--r--spec/frontend/sidebar/components/reference/sidebar_reference_widget_spec.js93
-rw-r--r--spec/frontend/sidebar/components/reviewers/reviewer_title_spec.js (renamed from spec/frontend/sidebar/reviewer_title_spec.js)0
-rw-r--r--spec/frontend/sidebar/components/reviewers/reviewers_spec.js (renamed from spec/frontend/sidebar/reviewers_spec.js)0
-rw-r--r--spec/frontend/sidebar/components/reviewers/sidebar_reviewers_spec.js77
-rw-r--r--spec/frontend/sidebar/components/severity/severity_spec.js2
-rw-r--r--spec/frontend/sidebar/components/severity/sidebar_severity_spec.js4
-rw-r--r--spec/frontend/sidebar/components/status/status_dropdown_spec.js75
-rw-r--r--spec/frontend/sidebar/components/subscriptions/subscriptions_dropdown_spec.js76
-rw-r--r--spec/frontend/sidebar/components/subscriptions/subscriptions_spec.js (renamed from spec/frontend/sidebar/subscriptions_spec.js)0
-rw-r--r--spec/frontend/sidebar/components/time_tracking/create_timelog_form_spec.js219
-rw-r--r--spec/frontend/sidebar/components/time_tracking/report_spec.js6
-rw-r--r--spec/frontend/sidebar/components/time_tracking/time_tracker_spec.js63
-rw-r--r--spec/frontend/sidebar/components/todo_toggle/__snapshots__/todo_spec.js.snap (renamed from spec/frontend/sidebar/__snapshots__/todo_spec.js.snap)0
-rw-r--r--spec/frontend/sidebar/components/todo_toggle/sidebar_todo_widget_spec.js2
-rw-r--r--spec/frontend/sidebar/components/todo_toggle/todo_button_spec.js68
-rw-r--r--spec/frontend/sidebar/components/todo_toggle/todo_spec.js (renamed from spec/frontend/sidebar/todo_spec.js)0
-rw-r--r--spec/frontend/sidebar/components/toggle/toggle_sidebar_spec.js46
-rw-r--r--spec/frontend/sidebar/lib/sidebar_move_issue_spec.js162
-rw-r--r--spec/frontend/sidebar/sidebar_assignees_spec.js99
-rw-r--r--spec/frontend/sidebar/sidebar_mediator_spec.js58
-rw-r--r--spec/frontend/sidebar/sidebar_move_issue_spec.js162
-rw-r--r--spec/frontend/sidebar/sidebar_store_spec.js160
-rw-r--r--spec/frontend/sidebar/stores/sidebar_store_spec.js160
-rw-r--r--spec/frontend/terms/components/app_spec.js7
-rw-r--r--spec/frontend/terraform/components/init_command_modal_spec.js21
-rw-r--r--spec/frontend/token_access/mock_data.js13
-rw-r--r--spec/frontend/token_access/token_access_spec.js109
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap6
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_closed_spec.js141
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js28
-rw-r--r--spec/frontend/vue_merge_request_widget/components/widget/action_buttons_spec.js47
-rw-r--r--spec/frontend/vue_merge_request_widget/components/widget/widget_content_row_spec.js7
-rw-r--r--spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js38
-rw-r--r--spec/frontend/vue_merge_request_widget/extensions/test_report/index_spec.js20
-rw-r--r--spec/frontend/vue_merge_request_widget/extentions/code_quality/index_spec.js55
-rw-r--r--spec/frontend/vue_merge_request_widget/extentions/code_quality/mock_data.js25
-rw-r--r--spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js19
-rw-r--r--spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap305
-rw-r--r--spec/frontend/vue_shared/components/__snapshots__/memory_graph_spec.js.snap1
-rw-r--r--spec/frontend/vue_shared/components/actions_button_spec.js35
-rw-r--r--spec/frontend/vue_shared/components/awards_list_spec.js49
-rw-r--r--spec/frontend/vue_shared/components/content_viewer/content_viewer_spec.js3
-rw-r--r--spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js4
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js33
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js109
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js333
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/base_token_spec.js18
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js4
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_contact_token_spec.js10
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_organization_token_spec.js10
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js14
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js12
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/user_token_spec.js333
-rw-r--r--spec/frontend/vue_shared/components/group_select/group_select_spec.js4
-rw-r--r--spec/frontend/vue_shared/components/listbox_input/listbox_input_spec.js132
-rw-r--r--spec/frontend/vue_shared/components/markdown/field_spec.js8
-rw-r--r--spec/frontend/vue_shared/components/markdown/field_view_spec.js8
-rw-r--r--spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js1
-rw-r--r--spec/frontend/vue_shared/components/markdown_drawer/markdown_drawer_spec.js27
-rw-r--r--spec/frontend/vue_shared/components/markdown_drawer/utils/fetch_spec.js6
-rw-r--r--spec/frontend/vue_shared/components/notes/system_note_spec.js8
-rw-r--r--spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/mocks/items_filters.json30
-rw-r--r--spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs_spec.js32
-rw-r--r--spec/frontend/vue_shared/components/registry/registry_search_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/runner_instructions/mock_data.js122
-rw-r--r--spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js87
-rw-r--r--spec/frontend/vue_shared/components/sidebar/copyable_field_spec.js77
-rw-r--r--spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js388
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_button_spec.js89
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view_spec.js211
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js413
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_spec.js68
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_title_spec.js59
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed_spec.js75
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_value_spec.js99
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/label_item_spec.js92
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js231
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/actions_spec.js265
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/getters_spec.js74
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/mutations_spec.js232
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view_spec.js239
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js170
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_spec.js207
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_footer_spec.js57
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_header_spec.js92
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_value_spec.js104
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/label_item_spec.js38
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js217
-rw-r--r--spec/frontend/vue_shared/components/sidebar/todo_button_spec.js68
-rw-r--r--spec/frontend/vue_shared/components/sidebar/toggle_sidebar_spec.js46
-rw-r--r--spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js18
-rw-r--r--spec/frontend/vue_shared/components/source_viewer/plugins/link_dependencies_spec.js53
-rw-r--r--spec/frontend/vue_shared/components/source_viewer/plugins/mock_data.js2
-rw-r--r--spec/frontend/vue_shared/components/source_viewer/plugins/utils/go_sum_linker_spec.js14
-rw-r--r--spec/frontend/vue_shared/components/user_select_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/web_ide_link_spec.js216
-rw-r--r--spec/frontend/vue_shared/issuable/create/components/issuable_form_spec.js2
-rw-r--r--spec/frontend/vue_shared/issuable/create/components/issuable_label_selector_spec.js141
-rw-r--r--spec/frontend/vue_shared/issuable/list/components/issuable_item_spec.js4
-rw-r--r--spec/frontend/vue_shared/issuable/show/components/issuable_description_spec.js18
-rw-r--r--spec/frontend/webhooks/components/__snapshots__/push_events_spec.js.snap4
-rw-r--r--spec/frontend/work_items/components/notes/system_note_spec.js111
-rw-r--r--spec/frontend/work_items/components/work_item_assignees_spec.js14
-rw-r--r--spec/frontend/work_items/components/work_item_description_rendered_spec.js8
-rw-r--r--spec/frontend/work_items/components/work_item_description_spec.js34
-rw-r--r--spec/frontend/work_items/components/work_item_detail_modal_spec.js2
-rw-r--r--spec/frontend/work_items/components/work_item_detail_spec.js91
-rw-r--r--spec/frontend/work_items/components/work_item_information_spec.js43
-rw-r--r--spec/frontend/work_items/components/work_item_labels_spec.js14
-rw-r--r--spec/frontend/work_items/components/work_item_links/okr_actions_split_button_spec.js35
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_link_child_metadata_spec.js67
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js161
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js47
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_links_spec.js188
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js147
-rw-r--r--spec/frontend/work_items/components/work_item_milestone_spec.js12
-rw-r--r--spec/frontend/work_items/components/work_item_notes_spec.js107
-rw-r--r--spec/frontend/work_items/mock_data.js588
-rw-r--r--spec/frontend/work_items/pages/work_item_root_spec.js2
-rw-r--r--spec/frontend/work_items/router_spec.js16
-rw-r--r--spec/frontend_integration/content_editor/content_editor_integration_spec.js2
-rw-r--r--spec/graphql/graphql_triggers_spec.rb14
-rw-r--r--spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb9
-rw-r--r--spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb9
-rw-r--r--spec/graphql/mutations/alert_management/create_alert_issue_spec.rb18
-rw-r--r--spec/graphql/mutations/alert_management/update_alert_status_spec.rb9
-rw-r--r--spec/graphql/mutations/ci/runner/bulk_delete_spec.rb2
-rw-r--r--spec/graphql/mutations/ci/runner/delete_spec.rb2
-rw-r--r--spec/graphql/mutations/ci/runner/update_spec.rb2
-rw-r--r--spec/graphql/mutations/container_repositories/destroy_spec.rb17
-rw-r--r--spec/graphql/mutations/incident_management/timeline_event/update_spec.rb44
-rw-r--r--spec/graphql/mutations/issues/link_alerts_spec.rb84
-rw-r--r--spec/graphql/mutations/issues/unlink_alert_spec.rb83
-rw-r--r--spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb43
-rw-r--r--spec/graphql/resolvers/ci/group_runners_resolver_spec.rb4
-rw-r--r--spec/graphql/resolvers/ci/project_runners_resolver_spec.rb86
-rw-r--r--spec/graphql/resolvers/ci/runner_groups_resolver_spec.rb37
-rw-r--r--spec/graphql/resolvers/ci/runner_jobs_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/ci/runner_platforms_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/ci/runner_setup_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/ci/runner_status_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/ci/runners_resolver_spec.rb30
-rw-r--r--spec/graphql/resolvers/design_management/design_resolver_spec.rb8
-rw-r--r--spec/graphql/resolvers/design_management/designs_resolver_spec.rb8
-rw-r--r--spec/graphql/resolvers/environments/nested_environments_resolver_spec.rb47
-rw-r--r--spec/graphql/resolvers/environments_resolver_spec.rb28
-rw-r--r--spec/graphql/resolvers/namespace_projects_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/paginated_tree_resolver_spec.rb21
-rw-r--r--spec/graphql/resolvers/project_pipeline_resolver_spec.rb8
-rw-r--r--spec/graphql/resolvers/users/participants_resolver_spec.rb3
-rw-r--r--spec/graphql/types/alert_management/alert_type_spec.rb3
-rw-r--r--spec/graphql/types/ci/detailed_status_type_spec.rb4
-rw-r--r--spec/graphql/types/ci/freeze_period_status_enum_spec.rb9
-rw-r--r--spec/graphql/types/ci/freeze_period_type_spec.rb17
-rw-r--r--spec/graphql/types/ci/pipeline_counts_type_spec.rb2
-rw-r--r--spec/graphql/types/ci/pipeline_schedule_type_spec.rb8
-rw-r--r--spec/graphql/types/ci/pipeline_schedule_variable_type_spec.rb17
-rw-r--r--spec/graphql/types/ci/runner_type_spec.rb2
-rw-r--r--spec/graphql/types/commit_signatures/ssh_signature_type_spec.rb17
-rw-r--r--spec/graphql/types/deployment_details_type_spec.rb17
-rw-r--r--spec/graphql/types/deployment_type_spec.rb7
-rw-r--r--spec/graphql/types/environment_type_spec.rb3
-rw-r--r--spec/graphql/types/issue_type_enum_spec.rb4
-rw-r--r--spec/graphql/types/key_type_spec.rb13
-rw-r--r--spec/graphql/types/merge_request_type_spec.rb4
-rw-r--r--spec/graphql/types/permission_types/base_permission_type_spec.rb21
-rw-r--r--spec/graphql/types/permission_types/deployment_spec.rb11
-rw-r--r--spec/graphql/types/permission_types/environment_spec.rb15
-rw-r--r--spec/graphql/types/permission_types/project_spec.rb2
-rw-r--r--spec/graphql/types/project_type_spec.rb2
-rw-r--r--spec/graphql/types/projects/fork_details_type_spec.rb16
-rw-r--r--spec/graphql/types/projects/service_type_enum_spec.rb1
-rw-r--r--spec/graphql/types/snippets/blob_type_spec.rb19
-rw-r--r--spec/graphql/types/subscription_type_spec.rb1
-rw-r--r--spec/graphql/types/todo_type_spec.rb117
-rw-r--r--spec/graphql/types/work_items/notes_filter_type_enum_spec.rb13
-rw-r--r--spec/graphql/types/work_items/widget_interface_spec.rb1
-rw-r--r--spec/graphql/types/work_items/widgets/hierarchy_type_spec.rb4
-rw-r--r--spec/graphql/types/work_items/widgets/notes_type_spec.rb11
-rw-r--r--spec/haml_lint/linter/documentation_links_spec.rb2
-rw-r--r--spec/helpers/application_helper_spec.rb74
-rw-r--r--spec/helpers/application_settings_helper_spec.rb8
-rw-r--r--spec/helpers/avatars_helper_spec.rb154
-rw-r--r--spec/helpers/blob_helper_spec.rb1
-rw-r--r--spec/helpers/ci/secure_files_helper_spec.rb62
-rw-r--r--spec/helpers/diff_helper_spec.rb31
-rw-r--r--spec/helpers/environment_helper_spec.rb1
-rw-r--r--spec/helpers/git_helper_spec.rb2
-rw-r--r--spec/helpers/groups/observability_helper_spec.rb10
-rw-r--r--spec/helpers/groups/settings_helper_spec.rb5
-rw-r--r--spec/helpers/ide_helper_spec.rb10
-rw-r--r--spec/helpers/invite_members_helper_spec.rb6
-rw-r--r--spec/helpers/issuables_helper_spec.rb60
-rw-r--r--spec/helpers/issues_helper_spec.rb26
-rw-r--r--spec/helpers/labels_helper_spec.rb23
-rw-r--r--spec/helpers/listbox_helper_spec.rb2
-rw-r--r--spec/helpers/markup_helper_spec.rb51
-rw-r--r--spec/helpers/nav/top_nav_helper_spec.rb24
-rw-r--r--spec/helpers/page_layout_helper_spec.rb4
-rw-r--r--spec/helpers/preferred_language_switcher_helper_spec.rb22
-rw-r--r--spec/helpers/programming_languages_helper_spec.rb71
-rw-r--r--spec/helpers/projects/ml/experiments_helper_spec.rb66
-rw-r--r--spec/helpers/projects/pipeline_helper_spec.rb1
-rw-r--r--spec/helpers/projects_helper_spec.rb26
-rw-r--r--spec/helpers/search_helper_spec.rb121
-rw-r--r--spec/helpers/sorting_helper_spec.rb99
-rw-r--r--spec/helpers/tab_helper_spec.rb2
-rw-r--r--spec/helpers/todos_helper_spec.rb118
-rw-r--r--spec/helpers/version_check_helper_spec.rb38
-rw-r--r--spec/helpers/web_hooks/web_hooks_helper_spec.rb39
-rw-r--r--spec/helpers/wiki_helper_spec.rb2
-rw-r--r--spec/helpers/x509_helper_spec.rb18
-rw-r--r--spec/initializers/database_config_spec.rb30
-rw-r--r--spec/initializers/diagnostic_reports_spec.rb32
-rw-r--r--spec/initializers/forbid_sidekiq_in_transactions_spec.rb6
-rw-r--r--spec/initializers/lograge_spec.rb2
-rw-r--r--spec/initializers/rails_yaml_safe_load_spec.rb5
-rw-r--r--spec/lib/api/ci/helpers/runner_helpers_spec.rb2
-rw-r--r--spec/lib/api/entities/package_spec.rb8
-rw-r--r--spec/lib/api/entities/plan_limit_spec.rb3
-rw-r--r--spec/lib/api/entities/ssh_key_spec.rb5
-rw-r--r--spec/lib/api/every_api_endpoint_spec.rb15
-rw-r--r--spec/lib/api/helpers/packages_helpers_spec.rb22
-rw-r--r--spec/lib/api/helpers/rate_limiter_spec.rb3
-rw-r--r--spec/lib/api/support/git_access_actor_spec.rb39
-rw-r--r--spec/lib/atlassian/jira_connect/client_spec.rb93
-rw-r--r--spec/lib/atlassian/jira_connect/jwt/asymmetric_spec.rb26
-rw-r--r--spec/lib/atlassian/jira_connect/jwt/symmetric_spec.rb2
-rw-r--r--spec/lib/atlassian/jira_connect/serializers/author_entity_spec.rb2
-rw-r--r--spec/lib/atlassian/jira_connect/serializers/base_entity_spec.rb2
-rw-r--r--spec/lib/atlassian/jira_connect/serializers/branch_entity_spec.rb2
-rw-r--r--spec/lib/atlassian/jira_connect/serializers/build_entity_spec.rb2
-rw-r--r--spec/lib/atlassian/jira_connect/serializers/deployment_entity_spec.rb2
-rw-r--r--spec/lib/atlassian/jira_connect/serializers/feature_flag_entity_spec.rb2
-rw-r--r--spec/lib/atlassian/jira_connect/serializers/pull_request_entity_spec.rb2
-rw-r--r--spec/lib/atlassian/jira_connect/serializers/repository_entity_spec.rb2
-rw-r--r--spec/lib/atlassian/jira_connect_spec.rb10
-rw-r--r--spec/lib/backup/gitaly_backup_spec.rb2
-rw-r--r--spec/lib/backup/manager_spec.rb4
-rw-r--r--spec/lib/banzai/filter/attributes_filter_spec.rb78
-rw-r--r--spec/lib/banzai/filter/commit_trailers_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/inline_observability_filter_spec.rb33
-rw-r--r--spec/lib/banzai/filter/references/reference_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/references/user_reference_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/repository_link_filter_spec.rb16
-rw-r--r--spec/lib/banzai/filter/syntax_highlight_filter_spec.rb6
-rw-r--r--spec/lib/banzai/filter/timeout_html_pipeline_filter_spec.rb15
-rw-r--r--spec/lib/banzai/pipeline/incident_management/timeline_event_pipeline_spec.rb2
-rw-r--r--spec/lib/banzai/reference_parser/base_parser_spec.rb23
-rw-r--r--spec/lib/bitbucket_server/connection_spec.rb6
-rw-r--r--spec/lib/bulk_imports/clients/http_spec.rb32
-rw-r--r--spec/lib/bulk_imports/common/pipelines/uploads_pipeline_spec.rb20
-rw-r--r--spec/lib/bulk_imports/projects/pipelines/issues_pipeline_spec.rb1
-rw-r--r--spec/lib/extracts_ref_spec.rb13
-rw-r--r--spec/lib/feature/definition_spec.rb13
-rw-r--r--spec/lib/feature_spec.rb226
-rw-r--r--spec/lib/generators/model/mocks/migration_file.txt2
-rw-r--r--spec/lib/gitlab/analytics/cycle_analytics/base_query_builder_spec.rb5
-rw-r--r--spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb8
-rw-r--r--spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb4
-rw-r--r--spec/lib/gitlab/analytics/cycle_analytics/stage_events_spec.rb14
-rw-r--r--spec/lib/gitlab/api_authentication/sent_through_builder_spec.rb4
-rw-r--r--spec/lib/gitlab/application_context_spec.rb4
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb16
-rw-r--r--spec/lib/gitlab/audit/auditor_spec.rb28
-rw-r--r--spec/lib/gitlab/audit/type/definition_spec.rb77
-rw-r--r--spec/lib/gitlab/auth/auth_finders_spec.rb4
-rw-r--r--spec/lib/gitlab/auth/current_user_mode_spec.rb31
-rw-r--r--spec/lib/gitlab/auth/ldap/config_spec.rb5
-rw-r--r--spec/lib/gitlab/auth/ldap/user_spec.rb2
-rw-r--r--spec/lib/gitlab/auth/o_auth/user_spec.rb2
-rw-r--r--spec/lib/gitlab/auth/saml/user_spec.rb10
-rw-r--r--spec/lib/gitlab/auth/unique_ips_limiter_spec.rb10
-rw-r--r--spec/lib/gitlab/background_migration/backfill_environment_tiers_spec.rb119
-rw-r--r--spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/backfill_jira_tracker_deployment_type2_spec.rb10
-rw-r--r--spec/lib/gitlab/background_migration/backfill_project_namespace_details_spec.rb6
-rw-r--r--spec/lib/gitlab/background_migration/backfill_project_namespace_on_issues_spec.rb20
-rw-r--r--spec/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/batched_migration_job_spec.rb22
-rw-r--r--spec/lib/gitlab/background_migration/batching_strategies/loose_index_scan_batching_strategy_spec.rb18
-rw-r--r--spec/lib/gitlab/background_migration/delete_orphaned_operational_vulnerabilities_spec.rb32
-rw-r--r--spec/lib/gitlab/background_migration/delete_orphans_approval_merge_request_rules_spec.rb75
-rw-r--r--spec/lib/gitlab/background_migration/delete_orphans_approval_project_rules_spec.rb71
-rw-r--r--spec/lib/gitlab/background_migration/disable_expiration_policies_linked_to_no_container_images_spec.rb8
-rw-r--r--spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_no_issues_no_repo_projects_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_five_mb_spec.rb61
-rw-r--r--spec/lib/gitlab/background_migration/drop_invalid_vulnerabilities_spec.rb36
-rw-r--r--spec/lib/gitlab/background_migration/populate_container_repository_migration_plan_spec.rb12
-rw-r--r--spec/lib/gitlab/background_migration/populate_namespace_statistics_spec.rb8
-rw-r--r--spec/lib/gitlab/background_migration/populate_vulnerability_reads_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/prune_stale_project_export_jobs_spec.rb63
-rw-r--r--spec/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid_spec.rb5
-rw-r--r--spec/lib/gitlab/background_migration/remove_backfilled_job_artifacts_expire_at_spec.rb4
-rw-r--r--spec/lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings_spec.rb5
-rw-r--r--spec/lib/gitlab/background_migration/remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb6
-rw-r--r--spec/lib/gitlab/background_migration/remove_vulnerability_finding_links_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb18
-rw-r--r--spec/lib/gitlab/background_migration/reset_status_on_container_repositories_spec.rb261
-rw-r--r--spec/lib/gitlab/background_migration/sanitize_confidential_todos_spec.rb11
-rw-r--r--spec/lib/gitlab/background_migration/update_timelogs_null_spent_at_spec.rb20
-rw-r--r--spec/lib/gitlab/blob_helper_spec.rb1
-rw-r--r--spec/lib/gitlab/bullet_spec.rb65
-rw-r--r--spec/lib/gitlab/checks/timed_logger_spec.rb44
-rw-r--r--spec/lib/gitlab/ci/build/cache_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/build/context/build_spec.rb10
-rw-r--r--spec/lib/gitlab/ci/build/hook_spec.rb20
-rw-r--r--spec/lib/gitlab/ci/config/entry/artifacts_spec.rb20
-rw-r--r--spec/lib/gitlab/ci/config/entry/bridge_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/cache_spec.rb72
-rw-r--r--spec/lib/gitlab/ci/config/entry/default_spec.rb5
-rw-r--r--spec/lib/gitlab/ci/config/entry/hooks_spec.rb37
-rw-r--r--spec/lib/gitlab/ci/config/entry/id_token_spec.rb58
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb31
-rw-r--r--spec/lib/gitlab/ci/config/entry/root_spec.rb49
-rw-r--r--spec/lib/gitlab/ci/config/entry/trigger_spec.rb44
-rw-r--r--spec/lib/gitlab/ci/config/entry/variable_spec.rb58
-rw-r--r--spec/lib/gitlab/ci/config/entry/variables_spec.rb28
-rw-r--r--spec/lib/gitlab/ci/config/external/file/remote_spec.rb13
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper/base_spec.rb40
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper/filter_spec.rb32
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper/location_expander_spec.rb72
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper/matcher_spec.rb74
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper/normalizer_spec.rb44
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper/variables_expander_spec.rb45
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper/verifier_spec.rb137
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper_spec.rb51
-rw-r--r--spec/lib/gitlab/ci/config/external/processor_spec.rb225
-rw-r--r--spec/lib/gitlab/ci/config_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/cron_parser_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/environment_matcher_spec.rb51
-rw-r--r--spec/lib/gitlab/ci/lint_spec.rb45
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/build/associations_spec.rb5
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb15
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/command_spec.rb57
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/pipeline/logger_spec.rb160
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb33
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build_spec.rb1607
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/component_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/report_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/reports_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/source_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/security/reports_spec.rb101
-rw-r--r--spec/lib/gitlab/ci/runner_instructions_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/trace/archive_spec.rb9
-rw-r--r--spec/lib/gitlab/ci/variables/builder_spec.rb27
-rw-r--r--spec/lib/gitlab/ci/yaml_processor/result_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb65
-rw-r--r--spec/lib/gitlab/cluster/rack_timeout_observer_spec.rb23
-rw-r--r--spec/lib/gitlab/config/entry/attributable_spec.rb16
-rw-r--r--spec/lib/gitlab/conflict/file_spec.rb6
-rw-r--r--spec/lib/gitlab/content_security_policy/config_loader_spec.rb58
-rw-r--r--spec/lib/gitlab/contributions_calendar_spec.rb31
-rw-r--r--spec/lib/gitlab/counters/buffered_counter_spec.rb233
-rw-r--r--spec/lib/gitlab/counters/legacy_counter_spec.rb41
-rw-r--r--spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb22
-rw-r--r--spec/lib/gitlab/data_builder/deployment_spec.rb8
-rw-r--r--spec/lib/gitlab/database/gitlab_schema_spec.rb70
-rw-r--r--spec/lib/gitlab/database/load_balancing/sidekiq_client_middleware_spec.rb45
-rw-r--r--spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb25
-rw-r--r--spec/lib/gitlab/database/lock_writes_manager_spec.rb8
-rw-r--r--spec/lib/gitlab/database/migration_helpers/automatic_lock_writes_on_tables_spec.rb334
-rw-r--r--spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb19
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb206
-rw-r--r--spec/lib/gitlab/database/migrations/batched_migration_last_id_spec.rb72
-rw-r--r--spec/lib/gitlab/database/migrations/runner_spec.rb8
-rw-r--r--spec/lib/gitlab/database/migrations/sidekiq_helpers_spec.rb276
-rw-r--r--spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb24
-rw-r--r--spec/lib/gitlab/database/partitioning/detached_partition_dropper_spec.rb2
-rw-r--r--spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb6
-rw-r--r--spec/lib/gitlab/database/postgresql_adapter/dump_schema_versions_mixin_spec.rb3
-rw-r--r--spec/lib/gitlab/database/postgresql_database_tasks/load_schema_versions_mixin_spec.rb3
-rw-r--r--spec/lib/gitlab/database/query_analyzers/query_recorder_spec.rb77
-rw-r--r--spec/lib/gitlab/database/reindexing_spec.rb2
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb58
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb22
-rw-r--r--spec/lib/gitlab/database/schema_cleaner_spec.rb9
-rw-r--r--spec/lib/gitlab/database/tables_sorted_by_foreign_keys_spec.rb34
-rw-r--r--spec/lib/gitlab/database/tables_truncate_spec.rb94
-rw-r--r--spec/lib/gitlab/database/transaction/context_spec.rb2
-rw-r--r--spec/lib/gitlab/database/type/indifferent_jsonb_spec.rb66
-rw-r--r--spec/lib/gitlab/database_importers/work_items/hierarchy_restrictions_importer_spec.rb10
-rw-r--r--spec/lib/gitlab/database_spec.rb52
-rw-r--r--spec/lib/gitlab/diff/file_collection/compare_spec.rb31
-rw-r--r--spec/lib/gitlab/diff/file_collection/merge_request_diff_batch_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/file_collection/paginated_merge_request_diff_spec.rb114
-rw-r--r--spec/lib/gitlab/email/handler/create_issue_handler_spec.rb6
-rw-r--r--spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb10
-rw-r--r--spec/lib/gitlab/email/handler/create_note_handler_spec.rb18
-rw-r--r--spec/lib/gitlab/email/handler/create_note_on_issuable_handler_spec.rb8
-rw-r--r--spec/lib/gitlab/email/handler/service_desk_handler_spec.rb9
-rw-r--r--spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb2
-rw-r--r--spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb8
-rw-r--r--spec/lib/gitlab/email/receiver_spec.rb2
-rw-r--r--spec/lib/gitlab/file_type_detection_spec.rb1
-rw-r--r--spec/lib/gitlab/gfm/uploads_rewriter_spec.rb22
-rw-r--r--spec/lib/gitlab/git/base_error_spec.rb11
-rw-r--r--spec/lib/gitlab/git/cross_repo_comparer_spec.rb117
-rw-r--r--spec/lib/gitlab/git/cross_repo_spec.rb83
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb113
-rw-r--r--spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb5
-rw-r--r--spec/lib/gitlab/git/tree_spec.rb6
-rw-r--r--spec/lib/gitlab/gitaly_client/operation_service_spec.rb45
-rw-r--r--spec/lib/gitlab/gitaly_client/ref_service_spec.rb32
-rw-r--r--spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb140
-rw-r--r--spec/lib/gitlab/github_gists_import/importer/gist_importer_spec.rb128
-rw-r--r--spec/lib/gitlab/github_gists_import/importer/gists_importer_spec.rb121
-rw-r--r--spec/lib/gitlab/github_gists_import/representation/gist_spec.rb111
-rw-r--r--spec/lib/gitlab/github_gists_import/status_spec.rb50
-rw-r--r--spec/lib/gitlab/github_import/bulk_importing_spec.rb65
-rw-r--r--spec/lib/gitlab/github_import/client_spec.rb117
-rw-r--r--spec/lib/gitlab/github_import/clients/proxy_spec.rb102
-rw-r--r--spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb17
-rw-r--r--spec/lib/gitlab/github_import/importer/issues_importer_spec.rb10
-rw-r--r--spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb27
-rw-r--r--spec/lib/gitlab/github_import/importer/labels_importer_spec.rb52
-rw-r--r--spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb8
-rw-r--r--spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb57
-rw-r--r--spec/lib/gitlab/github_import/importer/note_importer_spec.rb13
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb24
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_request_merged_by_importer_spec.rb36
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_request_review_importer_spec.rb17
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_requests/review_requests_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/releases_importer_spec.rb41
-rw-r--r--spec/lib/gitlab/github_import/markdown/attachment_spec.rb18
-rw-r--r--spec/lib/gitlab/github_import/page_counter_spec.rb12
-rw-r--r--spec/lib/gitlab/github_import/representation/diff_note_spec.rb115
-rw-r--r--spec/lib/gitlab/github_import/representation/diff_notes/discussion_id_spec.rb84
-rw-r--r--spec/lib/gitlab/gon_helper_spec.rb7
-rw-r--r--spec/lib/gitlab/graphql/limit/field_call_count_spec.rb9
-rw-r--r--spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb383
-rw-r--r--spec/lib/gitlab/http_connection_adapter_spec.rb11
-rw-r--r--spec/lib/gitlab/i18n_spec.rb8
-rw-r--r--spec/lib/gitlab/import/merge_request_helpers_spec.rb7
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml17
-rw-r--r--spec/lib/gitlab/import_export/group/legacy_tree_restorer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/group/legacy_tree_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/group/tree_restorer_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/import_test_coverage_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/json/legacy_writer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/lfs_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/model_configuration_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/project/exported_relations_merger_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/project/relation_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/project/tree_restorer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/repo_restorer_spec.rb19
-rw-r--r--spec/lib/gitlab/import_export/saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_sources_spec.rb4
-rw-r--r--spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb21
-rw-r--r--spec/lib/gitlab/instrumentation/redis_base_spec.rb80
-rw-r--r--spec/lib/gitlab/instrumentation/redis_cluster_validator_spec.rb135
-rw-r--r--spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb77
-rw-r--r--spec/lib/gitlab/instrumentation/redis_spec.rb17
-rw-r--r--spec/lib/gitlab/instrumentation_helper_spec.rb39
-rw-r--r--spec/lib/gitlab/kubernetes/kube_client_spec.rb20
-rw-r--r--spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb39
-rw-r--r--spec/lib/gitlab/memory/instrumentation_spec.rb4
-rw-r--r--spec/lib/gitlab/memory/jemalloc_spec.rb49
-rw-r--r--spec/lib/gitlab/memory/reporter_spec.rb206
-rw-r--r--spec/lib/gitlab/memory/reports/heap_dump_spec.rb56
-rw-r--r--spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb94
-rw-r--r--spec/lib/gitlab/memory/reports_daemon_spec.rb85
-rw-r--r--spec/lib/gitlab/memory/watchdog/configuration_spec.rb47
-rw-r--r--spec/lib/gitlab/memory/watchdog/configurator_spec.rb158
-rw-r--r--spec/lib/gitlab/memory/watchdog/event_reporter_spec.rb118
-rw-r--r--spec/lib/gitlab/memory/watchdog/monitor/rss_memory_limit_spec.rb29
-rw-r--r--spec/lib/gitlab/memory/watchdog/monitor_state_spec.rb11
-rw-r--r--spec/lib/gitlab/memory/watchdog/sidekiq_event_reporter_spec.rb66
-rw-r--r--spec/lib/gitlab/memory/watchdog_spec.rb233
-rw-r--r--spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb694
-rw-r--r--spec/lib/gitlab/merge_requests/message_generator_spec.rb881
-rw-r--r--spec/lib/gitlab/metrics/dashboard/validator_spec.rb52
-rw-r--r--spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/global_search_slis_spec.rb69
-rw-r--r--spec/lib/gitlab/metrics/rails_slis_spec.rb29
-rw-r--r--spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb115
-rw-r--r--spec/lib/gitlab/metrics/subscribers/active_record_spec.rb15
-rw-r--r--spec/lib/gitlab/metrics/subscribers/ldap_spec.rb124
-rw-r--r--spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb36
-rw-r--r--spec/lib/gitlab/metrics_spec.rb24
-rw-r--r--spec/lib/gitlab/middleware/compressed_json_spec.rb113
-rw-r--r--spec/lib/gitlab/middleware/go_spec.rb2
-rw-r--r--spec/lib/gitlab/other_markup_spec.rb29
-rw-r--r--spec/lib/gitlab/pages/cache_control_spec.rb30
-rw-r--r--spec/lib/gitlab/pagination/offset_pagination_spec.rb22
-rw-r--r--spec/lib/gitlab/process_management_spec.rb9
-rw-r--r--spec/lib/gitlab/process_supervisor_spec.rb2
-rw-r--r--spec/lib/gitlab/puma_logging/json_formatter_spec.rb4
-rw-r--r--spec/lib/gitlab/quick_actions/dsl_spec.rb7
-rw-r--r--spec/lib/gitlab/redis/multi_store_spec.rb2
-rw-r--r--spec/lib/gitlab/reference_extractor_spec.rb22
-rw-r--r--spec/lib/gitlab/repository_archive_rate_limiter_spec.rb3
-rw-r--r--spec/lib/gitlab/repository_cache_adapter_spec.rb3
-rw-r--r--spec/lib/gitlab/search/found_blob_spec.rb5
-rw-r--r--spec/lib/gitlab/shell_spec.rb42
-rw-r--r--spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb92
-rw-r--r--spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb23
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/client_metrics_spec.rb3
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb3
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb3
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/instrumentation_logger_spec.rb3
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/query_analyzer_spec.rb3
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb6
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb6
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb3
-rw-r--r--spec/lib/gitlab/slash_commands/application_help_spec.rb4
-rw-r--r--spec/lib/gitlab/slash_commands/deploy_spec.rb2
-rw-r--r--spec/lib/gitlab/sql/pattern_spec.rb10
-rw-r--r--spec/lib/gitlab/ssh/signature_spec.rb129
-rw-r--r--spec/lib/gitlab/ssh_public_key_spec.rb3
-rw-r--r--spec/lib/gitlab/tracking/destinations/snowplow_spec.rb25
-rw-r--r--spec/lib/gitlab/tracking/event_definition_spec.rb10
-rw-r--r--spec/lib/gitlab/tracking/service_ping_context_spec.rb49
-rw-r--r--spec/lib/gitlab/tracking_spec.rb9
-rw-r--r--spec/lib/gitlab/url_blocker_spec.rb144
-rw-r--r--spec/lib/gitlab/usage/metric_definition_spec.rb10
-rw-r--r--spec/lib/gitlab/usage/metrics/aggregates/aggregate_spec.rb6
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/count_merge_request_authors_metric_spec.rb25
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/database_metric_spec.rb16
-rw-r--r--spec/lib/gitlab/usage/metrics/name_suggestion_spec.rb6
-rw-r--r--spec/lib/gitlab/usage/metrics/names_suggestions/generator_spec.rb8
-rw-r--r--spec/lib/gitlab/usage/metrics/names_suggestions/relation_parsers/joins_spec.rb9
-rw-r--r--spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb2
-rw-r--r--spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb137
-rw-r--r--spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb53
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb58
-rw-r--r--spec/lib/gitlab/utils/delegator_override/validator_spec.rb12
-rw-r--r--spec/lib/gitlab/utils/delegator_override_spec.rb12
-rw-r--r--spec/lib/gitlab/utils/override_spec.rb3
-rw-r--r--spec/lib/gitlab/utils/strong_memoize_spec.rb32
-rw-r--r--spec/lib/gitlab/work_items/work_item_hierarchy_spec.rb109
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb162
-rw-r--r--spec/lib/gitlab/x509/signature_spec.rb19
-rw-r--r--spec/lib/google_api/cloud_platform/client_spec.rb4
-rw-r--r--spec/lib/json_web_token/hmac_token_spec.rb20
-rw-r--r--spec/lib/mattermost/session_spec.rb8
-rw-r--r--spec/lib/pager_duty/webhook_payload_parser_spec.rb90
-rw-r--r--spec/lib/peek/views/active_record_spec.rb2
-rw-r--r--spec/lib/sbom/package_url/argument_validator_spec.rb2
-rw-r--r--spec/lib/sbom/package_url/decoder_spec.rb2
-rw-r--r--spec/lib/sbom/package_url/encoder_spec.rb2
-rw-r--r--spec/lib/sbom/package_url/normalizer_spec.rb2
-rw-r--r--spec/lib/sbom/package_url_spec.rb2
-rw-r--r--spec/lib/security/ci_configuration/container_scanning_build_action_spec.rb10
-rw-r--r--spec/lib/security/weak_passwords_spec.rb3
-rw-r--r--spec/lib/serializers/json_spec.rb47
-rw-r--r--spec/lib/sidebars/projects/menus/deployments_menu_spec.rb28
-rw-r--r--spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb31
-rw-r--r--spec/lib/sidebars/projects/menus/monitor_menu_spec.rb32
-rw-r--r--spec/lib/sidebars/projects/menus/repository_menu_spec.rb70
-rw-r--r--spec/lib/system_check/app/gitlab_cable_config_exists_check_spec.rb27
-rw-r--r--spec/lib/system_check/app/gitlab_resque_config_exists_check_spec.rb27
-rw-r--r--spec/lib/system_check/base_check_spec.rb2
-rw-r--r--spec/lib/system_check/sidekiq_check_spec.rb62
-rw-r--r--spec/lib/version_check_spec.rb79
-rw-r--r--spec/mailers/emails/profile_spec.rb42
-rw-r--r--spec/mailers/notify_spec.rb32
-rw-r--r--spec/metrics_server/metrics_server_spec.rb3
-rw-r--r--spec/migrations/20210406144743_backfill_total_tuple_count_for_batched_migrations_spec.rb9
-rw-r--r--spec/migrations/20210423160427_schedule_drop_invalid_vulnerabilities_spec.rb32
-rw-r--r--spec/migrations/20210430134202_copy_adoption_snapshot_namespace_spec.rb2
-rw-r--r--spec/migrations/20210430135954_copy_adoption_segments_namespace_spec.rb2
-rw-r--r--spec/migrations/20210503105845_add_project_value_stream_id_to_project_stages_spec.rb3
-rw-r--r--spec/migrations/20210511142748_schedule_drop_invalid_vulnerabilities2_spec.rb34
-rw-r--r--spec/migrations/20210514063252_schedule_cleanup_orphaned_lfs_objects_projects_spec.rb2
-rw-r--r--spec/migrations/20210601073400_fix_total_stage_in_vsa_spec.rb2
-rw-r--r--spec/migrations/20210601080039_group_protected_environments_add_index_and_constraint_spec.rb2
-rw-r--r--spec/migrations/20210603222333_remove_builds_email_service_from_services_spec.rb2
-rw-r--r--spec/migrations/20210610153556_delete_legacy_operations_feature_flags_spec.rb2
-rw-r--r--spec/migrations/2021061716138_cascade_delete_freeze_periods_spec.rb2
-rw-r--r--spec/migrations/20210708130419_reschedule_merge_request_diff_users_background_migration_spec.rb2
-rw-r--r--spec/migrations/20210713042000_fix_ci_sources_pipelines_index_names_spec.rb2
-rw-r--r--spec/migrations/20210722042939_update_issuable_slas_where_issue_closed_spec.rb2
-rw-r--r--spec/migrations/20210722150102_operations_feature_flags_correct_flexible_rollout_values_spec.rb4
-rw-r--r--spec/migrations/20210804150320_create_base_work_item_types_spec.rb4
-rw-r--r--spec/migrations/20210805192450_update_trial_plans_ci_daily_pipeline_schedule_triggers_spec.rb2
-rw-r--r--spec/migrations/20210811122206_update_external_project_bots_spec.rb2
-rw-r--r--spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb2
-rw-r--r--spec/migrations/20210819145000_drop_temporary_columns_and_triggers_for_ci_builds_runner_session_spec.rb2
-rw-r--r--spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb4
-rw-r--r--spec/migrations/20210902144144_drop_temporary_columns_and_triggers_for_ci_build_needs_spec.rb2
-rw-r--r--spec/migrations/20210906100316_drop_temporary_columns_and_triggers_for_ci_build_trace_chunks_spec.rb2
-rw-r--r--spec/migrations/20210906130643_drop_temporary_columns_and_triggers_for_taggings_spec.rb2
-rw-r--r--spec/migrations/20210907013944_cleanup_bigint_conversion_for_ci_builds_metadata_spec.rb2
-rw-r--r--spec/migrations/20210907211557_finalize_ci_builds_bigint_conversion_spec.rb2
-rw-r--r--spec/migrations/20210910194952_update_report_type_for_existing_approval_project_rules_spec.rb2
-rw-r--r--spec/migrations/20210914095310_cleanup_orphan_project_access_tokens_spec.rb2
-rw-r--r--spec/migrations/20210915022415_cleanup_bigint_conversion_for_ci_builds_spec.rb2
-rw-r--r--spec/migrations/20210918201050_remove_old_pending_jobs_for_recalculate_vulnerabilities_occurrences_uuid_spec.rb9
-rw-r--r--spec/migrations/20210922021816_drop_int4_columns_for_ci_job_artifacts_spec.rb2
-rw-r--r--spec/migrations/20210922025631_drop_int4_column_for_ci_sources_pipelines_spec.rb2
-rw-r--r--spec/migrations/20210922082019_drop_int4_column_for_events_spec.rb2
-rw-r--r--spec/migrations/20210922091402_drop_int4_column_for_push_event_payloads_spec.rb2
-rw-r--r--spec/migrations/20211006060436_schedule_populate_topics_total_projects_count_cache_spec.rb2
-rw-r--r--spec/migrations/20211012134316_clean_up_migrate_merge_request_diff_commit_users_spec.rb2
-rw-r--r--spec/migrations/20211018152654_schedule_remove_duplicate_vulnerabilities_findings3_spec.rb8
-rw-r--r--spec/migrations/20211028155449_schedule_fix_merge_request_diff_commit_users_migration_spec.rb2
-rw-r--r--spec/migrations/20211101222614_consume_remaining_user_namespace_jobs_spec.rb2
-rw-r--r--spec/migrations/20211110143306_add_not_null_constraint_to_security_findings_uuid_spec.rb6
-rw-r--r--spec/migrations/20211110151350_schedule_drop_invalid_security_findings_spec.rb31
-rw-r--r--spec/migrations/20211116111644_schedule_remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb54
-rw-r--r--spec/migrations/20211117084814_migrate_remaining_u2f_registrations_spec.rb2
-rw-r--r--spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb2
-rw-r--r--spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb4
-rw-r--r--spec/migrations/20211130165043_backfill_sequence_column_for_sprints_table_spec.rb2
-rw-r--r--spec/migrations/20211203091642_add_index_to_projects_on_marked_for_deletion_at_spec.rb2
-rw-r--r--spec/migrations/20211207125331_remove_jobs_for_recalculate_vulnerabilities_occurrences_uuid_spec.rb5
-rw-r--r--spec/migrations/20211207135331_schedule_recalculate_uuid_on_vulnerabilities_occurrences4_spec.rb2
-rw-r--r--spec/migrations/20211210140629_encrypt_static_object_token_spec.rb6
-rw-r--r--spec/migrations/20211214012507_backfill_incident_issue_escalation_statuses_spec.rb2
-rw-r--r--spec/migrations/20211217174331_mark_recalculate_finding_signatures_as_completed_spec.rb4
-rw-r--r--spec/migrations/20220106111958_add_insert_or_update_vulnerability_reads_trigger_spec.rb4
-rw-r--r--spec/migrations/20220106112043_add_update_vulnerability_reads_trigger_spec.rb4
-rw-r--r--spec/migrations/20220106112085_add_update_vulnerability_reads_location_trigger_spec.rb4
-rw-r--r--spec/migrations/20220106163326_add_has_issues_on_vulnerability_reads_trigger_spec.rb4
-rw-r--r--spec/migrations/20220107064845_populate_vulnerability_reads_spec.rb25
-rw-r--r--spec/migrations/20220120094340_drop_position_from_security_findings_spec.rb2
-rw-r--r--spec/migrations/20220124130028_dedup_runner_projects_spec.rb3
-rw-r--r--spec/migrations/20220128155251_remove_dangling_running_builds_spec.rb3
-rw-r--r--spec/migrations/20220128155814_fix_approval_rules_code_owners_rule_type_index_spec.rb2
-rw-r--r--spec/migrations/20220202105733_delete_service_template_records_spec.rb2
-rw-r--r--spec/migrations/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size_spec.rb28
-rw-r--r--spec/migrations/20220204194347_encrypt_integration_properties_spec.rb2
-rw-r--r--spec/migrations/20220208080921_schedule_migrate_personal_namespace_project_maintainer_to_owner_spec.rb4
-rw-r--r--spec/migrations/20220211214605_update_integrations_trigger_type_new_on_insert_null_safe_spec.rb2
-rw-r--r--spec/migrations/20220213103859_remove_integrations_type_spec.rb2
-rw-r--r--spec/migrations/20220222192524_create_not_null_constraint_releases_tag_spec.rb6
-rw-r--r--spec/migrations/20220222192525_remove_null_releases_spec.rb2
-rw-r--r--spec/migrations/20220223124428_schedule_merge_topics_with_same_name_spec.rb2
-rw-r--r--spec/migrations/20220305223212_add_security_training_providers_spec.rb4
-rw-r--r--spec/migrations/20220307192610_remove_duplicate_project_tag_releases_spec.rb2
-rw-r--r--spec/migrations/20220309084954_remove_leftover_external_pull_request_deletions_spec.rb2
-rw-r--r--spec/migrations/20220310141349_remove_dependency_list_usage_data_from_redis_spec.rb3
-rw-r--r--spec/migrations/20220315171129_cleanup_draft_data_from_faulty_regex_spec.rb2
-rw-r--r--spec/migrations/20220316202640_populate_container_repositories_migration_plan_spec.rb8
-rw-r--r--spec/migrations/20220321234317_remove_all_issuable_escalation_statuses_spec.rb2
-rw-r--r--spec/migrations/20220322132242_update_pages_onboarding_state_spec.rb2
-rw-r--r--spec/migrations/20220324032250_migrate_shimo_confluence_service_category_spec.rb2
-rw-r--r--spec/migrations/20220324165436_schedule_backfill_project_settings_spec.rb4
-rw-r--r--spec/migrations/20220329175119_remove_leftover_ci_job_artifact_deletions_spec.rb2
-rw-r--r--spec/migrations/20220331133802_schedule_backfill_topics_title_spec.rb2
-rw-r--r--spec/migrations/20220412143552_consume_remaining_encrypt_integration_property_jobs_spec.rb2
-rw-r--r--spec/migrations/20220416054011_schedule_backfill_project_member_namespace_id_spec.rb4
-rw-r--r--spec/migrations/20220420135946_update_batched_background_migration_arguments_spec.rb2
-rw-r--r--spec/migrations/20220426185933_backfill_deployments_finished_at_spec.rb2
-rw-r--r--spec/migrations/20220502015011_clean_up_fix_merge_request_diff_commit_users_spec.rb2
-rw-r--r--spec/migrations/20220502173045_reset_too_many_tags_skipped_registry_imports_spec.rb2
-rw-r--r--spec/migrations/20220503035221_add_gitlab_schema_to_batched_background_migrations_spec.rb2
-rw-r--r--spec/migrations/20220505044348_fix_automatic_iterations_cadences_start_date_spec.rb2
-rw-r--r--spec/migrations/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints_spec.rb2
-rw-r--r--spec/migrations/20220506154054_create_sync_namespace_details_trigger_spec.rb2
-rw-r--r--spec/migrations/20220512190659_remove_web_hooks_web_hook_logs_web_hook_id_fk_spec.rb2
-rw-r--r--spec/migrations/20220513043344_reschedule_expire_o_auth_tokens_spec.rb4
-rw-r--r--spec/migrations/20220523171107_drop_deploy_tokens_token_column_spec.rb2
-rw-r--r--spec/migrations/20220524074947_finalize_backfill_null_note_discussion_ids_spec.rb2
-rw-r--r--spec/migrations/20220524184149_create_sync_project_namespace_details_trigger_spec.rb2
-rw-r--r--spec/migrations/20220525221133_schedule_backfill_vulnerability_reads_cluster_agent_spec.rb4
-rw-r--r--spec/migrations/20220601110011_schedule_remove_self_managed_wiki_notes_spec.rb4
-rw-r--r--spec/migrations/20220601152916_add_user_id_and_ip_address_success_index_to_authentication_events_spec.rb3
-rw-r--r--spec/migrations/20220606080509_fix_incorrect_job_artifacts_expire_at_spec.rb4
-rw-r--r--spec/migrations/20220606082910_add_tmp_index_for_potentially_misassociated_vulnerability_occurrences_spec.rb3
-rw-r--r--spec/migrations/20220607082910_add_sync_tmp_index_for_potentially_misassociated_vulnerability_occurrences_spec.rb3
-rw-r--r--spec/migrations/20220620132300_update_last_run_date_for_iterations_cadences_spec.rb2
-rw-r--r--spec/migrations/20220622080547_backfill_project_statistics_with_container_registry_size_spec.rb4
-rw-r--r--spec/migrations/20220627090231_schedule_disable_legacy_open_source_license_for_inactive_public_projects_spec.rb2
-rw-r--r--spec/migrations/20220627152642_queue_update_delayed_project_removal_to_null_for_user_namespace_spec.rb2
-rw-r--r--spec/migrations/20220628012902_finalise_project_namespace_members_spec.rb4
-rw-r--r--spec/migrations/20220629184402_unset_escalation_policies_for_alert_incidents_spec.rb4
-rw-r--r--spec/migrations/20220715163254_update_notes_in_past_spec.rb2
-rw-r--r--spec/migrations/20220721031446_schedule_disable_legacy_open_source_license_for_one_member_no_repo_projects_spec.rb2
-rw-r--r--spec/migrations/20220722084543_schedule_disable_legacy_open_source_license_for_no_issues_no_repo_projects_spec.rb2
-rw-r--r--spec/migrations/20220722110026_reschedule_set_legacy_open_source_license_available_for_non_public_projects_spec.rb2
-rw-r--r--spec/migrations/20220725150127_update_jira_tracker_data_deployment_type_based_on_url_spec.rb2
-rw-r--r--spec/migrations/20220801155858_schedule_disable_legacy_open_source_licence_for_recent_public_projects_spec.rb3
-rw-r--r--spec/migrations/20220802114351_reschedule_backfill_container_registry_size_into_project_statistics_spec.rb4
-rw-r--r--spec/migrations/20220802204737_remove_deactivated_user_highest_role_stats_spec.rb2
-rw-r--r--spec/migrations/20220816163444_update_start_date_for_iterations_cadences_spec.rb2
-rw-r--r--spec/migrations/20220819153725_add_vulnerability_advisory_foreign_key_to_sbom_vulnerable_component_versions_spec.rb3
-rw-r--r--spec/migrations/20220819162852_add_sbom_component_version_foreign_key_to_sbom_vulnerable_component_versions_spec.rb3
-rw-r--r--spec/migrations/20220906074449_schedule_disable_legacy_open_source_license_for_projects_less_than_one_mb_spec.rb6
-rw-r--r--spec/migrations/20220913030624_cleanup_attention_request_related_system_notes_spec.rb2
-rw-r--r--spec/migrations/20220920124709_backfill_internal_on_notes_spec.rb2
-rw-r--r--spec/migrations/20220920180451_schedule_vulnerabilities_feedback_migration_spec.rb31
-rw-r--r--spec/migrations/20220921093355_schedule_backfill_namespace_details_spec.rb2
-rw-r--r--spec/migrations/20220921144258_remove_orphan_group_token_users_spec.rb3
-rw-r--r--spec/migrations/20220922143143_schedule_reset_duplicate_ci_runners_token_values_spec.rb2
-rw-r--r--spec/migrations/20220922143634_schedule_reset_duplicate_ci_runners_token_encrypted_values_spec.rb4
-rw-r--r--spec/migrations/20220928225711_schedule_update_ci_pipeline_artifacts_locked_status_spec.rb5
-rw-r--r--spec/migrations/20220929213730_schedule_delete_orphaned_operational_vulnerabilities_spec.rb6
-rw-r--r--spec/migrations/20221002234454_finalize_group_member_namespace_id_migration_spec.rb4
-rw-r--r--spec/migrations/20221004094814_schedule_destroy_invalid_members_spec.rb4
-rw-r--r--spec/migrations/20221008032350_add_password_expiration_migration_spec.rb2
-rw-r--r--spec/migrations/20221012033107_add_password_last_changed_at_to_user_details_spec.rb8
-rw-r--r--spec/migrations/20221013154159_update_invalid_dormant_user_setting_spec.rb2
-rw-r--r--spec/migrations/20221018050323_add_objective_and_keyresult_to_work_item_types_spec.rb4
-rw-r--r--spec/migrations/20221018062308_schedule_backfill_project_namespace_details_spec.rb6
-rw-r--r--spec/migrations/20221018095434_schedule_disable_legacy_open_source_license_for_projects_less_than_five_mb_spec.rb62
-rw-r--r--spec/migrations/20221018193635_ensure_task_note_renaming_background_migration_finished_spec.rb4
-rw-r--r--spec/migrations/20221021145820_create_routing_table_for_builds_metadata_v2_spec.rb4
-rw-r--r--spec/migrations/20221025043930_change_default_value_on_password_last_changed_at_to_user_details_spec.rb2
-rw-r--r--spec/migrations/20221028022627_add_index_on_password_last_changed_at_to_user_details_spec.rb2
-rw-r--r--spec/migrations/20221101032521_add_default_preferred_language_to_application_settings_spec.rb2
-rw-r--r--spec/migrations/20221101032600_add_text_limit_to_default_preferred_language_on_application_settings_spec.rb2
-rw-r--r--spec/migrations/20221102090940_create_next_ci_partitions_record_spec.rb2
-rw-r--r--spec/migrations/20221102090943_create_second_partition_for_builds_metadata_spec.rb2
-rw-r--r--spec/migrations/20221104115712_backfill_project_statistics_storage_size_without_uploads_size_spec.rb43
-rw-r--r--spec/migrations/20221110152133_delete_orphans_approval_rules_spec.rb22
-rw-r--r--spec/migrations/20221115173607_ensure_work_item_type_backfill_migration_finished_spec.rb102
-rw-r--r--spec/migrations/20221122132812_schedule_prune_stale_project_export_jobs_spec.rb24
-rw-r--r--spec/migrations/20221123133054_queue_reset_status_on_container_repositories_spec.rb51
-rw-r--r--spec/migrations/20221205151917_schedule_backfill_environment_tier_spec.rb24
-rw-r--r--spec/migrations/20221209110934_update_import_sources_on_application_settings_spec.rb21
-rw-r--r--spec/migrations/20221209110935_fix_update_import_sources_on_application_settings_spec.rb34
-rw-r--r--spec/migrations/20221210154044_update_active_billable_users_index_spec.rb33
-rw-r--r--spec/migrations/active_record/schema_spec.rb2
-rw-r--r--spec/migrations/add_default_project_approval_rules_vuln_allowed_spec.rb2
-rw-r--r--spec/migrations/add_epics_relative_position_spec.rb2
-rw-r--r--spec/migrations/add_new_trail_plans_spec.rb2
-rw-r--r--spec/migrations/add_okr_hierarchy_restrictions_spec.rb35
-rw-r--r--spec/migrations/add_open_source_plan_spec.rb2
-rw-r--r--spec/migrations/add_premium_and_ultimate_plan_limits_spec.rb2
-rw-r--r--spec/migrations/add_triggers_to_integrations_type_new_spec.rb2
-rw-r--r--spec/migrations/add_upvotes_count_index_to_issues_spec.rb2
-rw-r--r--spec/migrations/add_web_hook_calls_to_plan_limits_paid_tiers_spec.rb6
-rw-r--r--spec/migrations/adjust_task_note_rename_background_migration_values_spec.rb2
-rw-r--r--spec/migrations/associate_existing_dast_builds_with_variables_spec.rb2
-rw-r--r--spec/migrations/backfill_all_project_namespaces_spec.rb4
-rw-r--r--spec/migrations/backfill_cadence_id_for_boards_scoped_to_iteration_spec.rb2
-rw-r--r--spec/migrations/backfill_clusters_integration_prometheus_enabled_spec.rb2
-rw-r--r--spec/migrations/backfill_cycle_analytics_aggregations_spec.rb2
-rw-r--r--spec/migrations/backfill_epic_cache_counts_spec.rb2
-rw-r--r--spec/migrations/backfill_escalation_policies_for_oncall_schedules_spec.rb58
-rw-r--r--spec/migrations/backfill_group_features_spec.rb2
-rw-r--r--spec/migrations/backfill_integrations_enable_ssl_verification_spec.rb6
-rw-r--r--spec/migrations/backfill_integrations_type_new_spec.rb6
-rw-r--r--spec/migrations/backfill_issues_upvotes_count_spec.rb2
-rw-r--r--spec/migrations/backfill_member_namespace_id_for_group_members_spec.rb4
-rw-r--r--spec/migrations/backfill_namespace_id_for_namespace_routes_spec.rb4
-rw-r--r--spec/migrations/backfill_namespace_id_for_project_routes_spec.rb2
-rw-r--r--spec/migrations/backfill_namespace_id_on_issues_spec.rb2
-rw-r--r--spec/migrations/backfill_nuget_temporary_packages_to_processing_status_spec.rb2
-rw-r--r--spec/migrations/backfill_project_import_level_spec.rb4
-rw-r--r--spec/migrations/backfill_project_namespaces_for_group_spec.rb4
-rw-r--r--spec/migrations/backfill_stage_event_hash_spec.rb2
-rw-r--r--spec/migrations/backfill_user_namespace_spec.rb4
-rw-r--r--spec/migrations/bulk_insert_cluster_enabled_grants_spec.rb2
-rw-r--r--spec/migrations/change_public_projects_cost_factor_spec.rb2
-rw-r--r--spec/migrations/change_task_system_note_wording_to_checklist_item_spec.rb2
-rw-r--r--spec/migrations/change_web_hook_events_default_spec.rb2
-rw-r--r--spec/migrations/clean_up_pending_builds_table_spec.rb3
-rw-r--r--spec/migrations/cleanup_after_add_primary_email_to_emails_if_user_confirmed_spec.rb2
-rw-r--r--spec/migrations/cleanup_after_fixing_issue_when_admin_changed_primary_email_spec.rb2
-rw-r--r--spec/migrations/cleanup_after_fixing_regression_with_new_users_emails_spec.rb2
-rw-r--r--spec/migrations/cleanup_backfill_integrations_enable_ssl_verification_spec.rb3
-rw-r--r--spec/migrations/cleanup_move_container_registry_enabled_to_project_feature_spec.rb2
-rw-r--r--spec/migrations/cleanup_mr_attention_request_todos_spec.rb2
-rw-r--r--spec/migrations/cleanup_orphaned_routes_spec.rb2
-rw-r--r--spec/migrations/cleanup_remaining_orphan_invites_spec.rb2
-rw-r--r--spec/migrations/cleanup_vulnerability_state_transitions_with_same_from_state_to_state_spec.rb13
-rw-r--r--spec/migrations/confirm_security_bot_spec.rb2
-rw-r--r--spec/migrations/confirm_support_bot_user_spec.rb4
-rw-r--r--spec/migrations/delete_migrate_shared_vulnerability_scanners_spec.rb2
-rw-r--r--spec/migrations/delete_security_findings_without_uuid_spec.rb3
-rw-r--r--spec/migrations/disable_expiration_policies_linked_to_no_container_images_spec.rb2
-rw-r--r--spec/migrations/disable_job_token_scope_when_unused_spec.rb2
-rw-r--r--spec/migrations/finalize_invalid_member_cleanup_spec.rb4
-rw-r--r--spec/migrations/finalize_issues_namespace_id_backfilling_spec.rb72
-rw-r--r--spec/migrations/finalize_orphaned_routes_cleanup_spec.rb4
-rw-r--r--spec/migrations/finalize_project_namespaces_backfill_spec.rb4
-rw-r--r--spec/migrations/finalize_routes_backfilling_for_projects_spec.rb4
-rw-r--r--spec/migrations/finalize_traversal_ids_background_migrations_spec.rb2
-rw-r--r--spec/migrations/fix_and_backfill_project_namespaces_for_projects_with_duplicate_name_spec.rb2
-rw-r--r--spec/migrations/fix_batched_migrations_old_format_job_arguments_spec.rb2
-rw-r--r--spec/migrations/generate_customers_dot_jwt_signing_key_spec.rb2
-rw-r--r--spec/migrations/insert_ci_daily_pipeline_schedule_triggers_plan_limits_spec.rb14
-rw-r--r--spec/migrations/migrate_elastic_index_settings_spec.rb2
-rw-r--r--spec/migrations/migrate_protected_attribute_to_pending_builds_spec.rb3
-rw-r--r--spec/migrations/move_container_registry_enabled_to_project_features3_spec.rb2
-rw-r--r--spec/migrations/move_security_findings_table_to_gitlab_partitions_dynamic_schema_spec.rb2
-rw-r--r--spec/migrations/orphaned_invite_tokens_cleanup_spec.rb2
-rw-r--r--spec/migrations/orphaned_invited_members_cleanup_spec.rb2
-rw-r--r--spec/migrations/populate_audit_event_streaming_verification_token_spec.rb2
-rw-r--r--spec/migrations/populate_dismissal_information_for_vulnerabilities_spec.rb2
-rw-r--r--spec/migrations/populate_operation_visibility_permissions_spec.rb2
-rw-r--r--spec/migrations/populate_releases_access_level_from_repository_spec.rb2
-rw-r--r--spec/migrations/queue_backfill_project_feature_package_registry_access_level_spec.rb4
-rw-r--r--spec/migrations/queue_backfill_user_details_fields_spec.rb4
-rw-r--r--spec/migrations/queue_populate_projects_star_count_spec.rb4
-rw-r--r--spec/migrations/re_schedule_latest_pipeline_id_population_with_all_security_related_artifact_types_spec.rb2
-rw-r--r--spec/migrations/recount_epic_cache_counts_spec.rb2
-rw-r--r--spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_features_spec.rb2
-rw-r--r--spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features_spec.rb2
-rw-r--r--spec/migrations/remove_duplicate_dast_site_tokens_spec.rb4
-rw-r--r--spec/migrations/remove_duplicate_dast_site_tokens_with_same_token_spec.rb6
-rw-r--r--spec/migrations/remove_flowdock_integration_records_spec.rb23
-rw-r--r--spec/migrations/remove_hipchat_service_records_spec.rb2
-rw-r--r--spec/migrations/remove_invalid_integrations_spec.rb2
-rw-r--r--spec/migrations/remove_not_null_contraint_on_title_from_sprints_spec.rb2
-rw-r--r--spec/migrations/remove_records_without_group_from_webhooks_table_spec.rb2
-rw-r--r--spec/migrations/remove_schedule_and_status_from_pending_alert_escalations_spec.rb2
-rw-r--r--spec/migrations/remove_wiki_notes_spec.rb2
-rw-r--r--spec/migrations/rename_services_to_integrations_spec.rb2
-rw-r--r--spec/migrations/replace_external_wiki_triggers_spec.rb2
-rw-r--r--spec/migrations/reschedule_backfill_imported_issue_search_data_spec.rb23
-rw-r--r--spec/migrations/reschedule_delete_orphaned_deployments_spec.rb3
-rw-r--r--spec/migrations/reschedule_issue_work_item_type_id_backfill_spec.rb10
-rw-r--r--spec/migrations/reschedule_migrate_shared_vulnerability_scanners_spec.rb2
-rw-r--r--spec/migrations/reset_job_token_scope_enabled_again_spec.rb2
-rw-r--r--spec/migrations/reset_job_token_scope_enabled_spec.rb2
-rw-r--r--spec/migrations/reset_severity_levels_to_new_default_spec.rb2
-rw-r--r--spec/migrations/retry_backfill_traversal_ids_spec.rb4
-rw-r--r--spec/migrations/sanitize_confidential_note_todos_spec.rb2
-rw-r--r--spec/migrations/schedule_add_primary_email_to_emails_if_user_confirmed_spec.rb2
-rw-r--r--spec/migrations/schedule_backfill_cluster_agents_has_vulnerabilities_spec.rb4
-rw-r--r--spec/migrations/schedule_backfill_draft_status_on_merge_requests_corrected_regex_spec.rb2
-rw-r--r--spec/migrations/schedule_backfilling_the_namespace_id_for_vulnerability_reads_spec.rb4
-rw-r--r--spec/migrations/schedule_copy_ci_builds_columns_to_security_scans2_spec.rb2
-rw-r--r--spec/migrations/schedule_disable_expiration_policies_linked_to_no_container_images_spec.rb32
-rw-r--r--spec/migrations/schedule_fix_incorrect_max_seats_used2_spec.rb2
-rw-r--r--spec/migrations/schedule_fix_incorrect_max_seats_used_spec.rb2
-rw-r--r--spec/migrations/schedule_fixing_security_scan_statuses_spec.rb77
-rw-r--r--spec/migrations/schedule_populate_requirements_issue_id_spec.rb4
-rw-r--r--spec/migrations/schedule_purging_stale_security_scans_spec.rb23
-rw-r--r--spec/migrations/schedule_recalculate_vulnerability_finding_signatures_for_findings_spec.rb35
-rw-r--r--spec/migrations/schedule_security_setting_creation_spec.rb2
-rw-r--r--spec/migrations/schedule_set_correct_vulnerability_state_spec.rb4
-rw-r--r--spec/migrations/schedule_update_timelogs_null_spent_at_spec.rb24
-rw-r--r--spec/migrations/schedule_update_timelogs_project_id_spec.rb2
-rw-r--r--spec/migrations/schedule_update_users_where_two_factor_auth_required_from_group_spec.rb2
-rw-r--r--spec/migrations/set_default_job_token_scope_true_spec.rb2
-rw-r--r--spec/migrations/set_email_confirmation_setting_before_removing_send_user_confirmation_email_column_spec.rb41
-rw-r--r--spec/migrations/set_email_confirmation_setting_from_send_user_confirmation_email_setting_spec.rb2
-rw-r--r--spec/migrations/slice_merge_request_diff_commit_migrations_spec.rb2
-rw-r--r--spec/migrations/start_backfill_ci_queuing_tables_spec.rb3
-rw-r--r--spec/migrations/steal_merge_request_diff_commit_users_migration_spec.rb2
-rw-r--r--spec/migrations/sync_new_amount_used_for_ci_namespace_monthly_usages_spec.rb3
-rw-r--r--spec/migrations/sync_new_amount_used_for_ci_project_monthly_usages_spec.rb3
-rw-r--r--spec/migrations/toggle_vsa_aggregations_enable_spec.rb2
-rw-r--r--spec/migrations/update_application_settings_container_registry_exp_pol_worker_capacity_default_spec.rb3
-rw-r--r--spec/migrations/update_application_settings_protected_paths_spec.rb9
-rw-r--r--spec/migrations/update_default_scan_method_of_dast_site_profile_spec.rb2
-rw-r--r--spec/migrations/update_integrations_trigger_type_new_on_insert_spec.rb2
-rw-r--r--spec/migrations/update_invalid_member_states_spec.rb2
-rw-r--r--spec/migrations/update_invalid_web_hooks_spec.rb2
-rw-r--r--spec/models/achievements/achievement_spec.rb33
-rw-r--r--spec/models/appearance_spec.rb1
-rw-r--r--spec/models/application_setting_spec.rb9
-rw-r--r--spec/models/badge_spec.rb4
-rw-r--r--spec/models/blob_viewer/metrics_dashboard_yml_spec.rb245
-rw-r--r--spec/models/bulk_imports/tracker_spec.rb5
-rw-r--r--spec/models/ci/bridge_spec.rb20
-rw-r--r--spec/models/ci/build_metadata_spec.rb161
-rw-r--r--spec/models/ci/build_need_spec.rb60
-rw-r--r--spec/models/ci/build_pending_state_spec.rb29
-rw-r--r--spec/models/ci/build_report_result_spec.rb32
-rw-r--r--spec/models/ci/build_runner_session_spec.rb18
-rw-r--r--spec/models/ci/build_spec.rb132
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb33
-rw-r--r--spec/models/ci/build_trace_metadata_spec.rb22
-rw-r--r--spec/models/ci/freeze_period_spec.rb129
-rw-r--r--spec/models/ci/freeze_period_status_spec.rb71
-rw-r--r--spec/models/ci/job_artifact_spec.rb44
-rw-r--r--spec/models/ci/job_token/allowlist_spec.rb81
-rw-r--r--spec/models/ci/job_token/project_scope_link_spec.rb16
-rw-r--r--spec/models/ci/job_token/scope_spec.rb66
-rw-r--r--spec/models/ci/job_variable_spec.rb62
-rw-r--r--spec/models/ci/pending_build_spec.rb22
-rw-r--r--spec/models/ci/pipeline_schedule_spec.rb65
-rw-r--r--spec/models/ci/pipeline_spec.rb46
-rw-r--r--spec/models/ci/processable_spec.rb5
-rw-r--r--spec/models/ci/resource_group_spec.rb20
-rw-r--r--spec/models/ci/runner_namespace_spec.rb23
-rw-r--r--spec/models/ci/runner_spec.rb26
-rw-r--r--spec/models/ci/runner_version_spec.rb18
-rw-r--r--spec/models/ci/running_build_spec.rb24
-rw-r--r--spec/models/ci/secure_file_spec.rb33
-rw-r--r--spec/models/ci/sources/pipeline_spec.rb18
-rw-r--r--spec/models/ci/unit_test_failure_spec.rb42
-rw-r--r--spec/models/clusters/agent_token_spec.rb8
-rw-r--r--spec/models/clusters/applications/ingress_spec.rb10
-rw-r--r--spec/models/clusters/applications/knative_spec.rb12
-rw-r--r--spec/models/commit_signatures/gpg_signature_spec.rb5
-rw-r--r--spec/models/commit_signatures/ssh_signature_spec.rb7
-rw-r--r--spec/models/commit_signatures/x509_commit_signature_spec.rb5
-rw-r--r--spec/models/commit_spec.rb17
-rw-r--r--spec/models/concerns/batch_destroy_dependent_associations_spec.rb6
-rw-r--r--spec/models/concerns/bulk_insertable_associations_spec.rb47
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb18
-rw-r--r--spec/models/concerns/ci/partitionable/partitioned_filter_spec.rb80
-rw-r--r--spec/models/concerns/ci/partitionable_spec.rb24
-rw-r--r--spec/models/concerns/commit_signature_spec.rb21
-rw-r--r--spec/models/concerns/counter_attribute_spec.rb97
-rw-r--r--spec/models/concerns/has_user_type_spec.rb8
-rw-r--r--spec/models/concerns/pg_full_text_searchable_spec.rb28
-rw-r--r--spec/models/concerns/schedulable_spec.rb2
-rw-r--r--spec/models/concerns/sensitive_serializable_hash_spec.rb33
-rw-r--r--spec/models/concerns/signature_type_spec.rb15
-rw-r--r--spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb1
-rw-r--r--spec/models/concerns/triggerable_hooks_spec.rb6
-rw-r--r--spec/models/container_repository_spec.rb26
-rw-r--r--spec/models/deploy_token_spec.rb6
-rw-r--r--spec/models/deployment_spec.rb15
-rw-r--r--spec/models/design_management/version_spec.rb8
-rw-r--r--spec/models/environment_spec.rb115
-rw-r--r--spec/models/event_spec.rb53
-rw-r--r--spec/models/factories_spec.rb2
-rw-r--r--spec/models/generic_commit_status_spec.rb30
-rw-r--r--spec/models/group_deploy_key_spec.rb12
-rw-r--r--spec/models/group_spec.rb92
-rw-r--r--spec/models/hooks/active_hook_filter_spec.rb18
-rw-r--r--spec/models/hooks/service_hook_spec.rb26
-rw-r--r--spec/models/hooks/web_hook_spec.rb64
-rw-r--r--spec/models/integration_spec.rb4
-rw-r--r--spec/models/integrations/base_chat_notification_spec.rb29
-rw-r--r--spec/models/integrations/base_slack_notification_spec.rb16
-rw-r--r--spec/models/integrations/chat_message/pipeline_message_spec.rb4
-rw-r--r--spec/models/integrations/drone_ci_spec.rb12
-rw-r--r--spec/models/integrations/flowdock_spec.rb54
-rw-r--r--spec/models/integrations/jira_spec.rb33
-rw-r--r--spec/models/integrations/slack_spec.rb6
-rw-r--r--spec/models/issue_collection_spec.rb71
-rw-r--r--spec/models/issue_spec.rb42
-rw-r--r--spec/models/jira_connect_installation_spec.rb16
-rw-r--r--spec/models/jira_import_state_spec.rb2
-rw-r--r--spec/models/key_spec.rb16
-rw-r--r--spec/models/lfs_object_spec.rb54
-rw-r--r--spec/models/member_spec.rb113
-rw-r--r--spec/models/members/group_member_spec.rb34
-rw-r--r--spec/models/members/member_role_spec.rb33
-rw-r--r--spec/models/members/project_member_spec.rb21
-rw-r--r--spec/models/merge_request_diff_spec.rb44
-rw-r--r--spec/models/merge_request_spec.rb27
-rw-r--r--spec/models/milestone_note_spec.rb2
-rw-r--r--spec/models/ml/candidate_metadata_spec.rb20
-rw-r--r--spec/models/ml/candidate_spec.rb41
-rw-r--r--spec/models/ml/experiment_metadata_spec.rb20
-rw-r--r--spec/models/ml/experiment_spec.rb1
-rw-r--r--spec/models/namespace_setting_spec.rb57
-rw-r--r--spec/models/namespace_spec.rb25
-rw-r--r--spec/models/note_spec.rb1
-rw-r--r--spec/models/notification_recipient_spec.rb2
-rw-r--r--spec/models/packages/package_file_spec.rb10
-rw-r--r--spec/models/packages/package_spec.rb12
-rw-r--r--spec/models/packages/rpm/repository_file_spec.rb28
-rw-r--r--spec/models/pages/lookup_path_spec.rb6
-rw-r--r--spec/models/pages_deployment_spec.rb2
-rw-r--r--spec/models/performance_monitoring/prometheus_dashboard_spec.rb84
-rw-r--r--spec/models/plan_limits_spec.rb1
-rw-r--r--spec/models/programming_language_spec.rb21
-rw-r--r--spec/models/project_export_job_spec.rb52
-rw-r--r--spec/models/project_feature_spec.rb1
-rw-r--r--spec/models/project_spec.rb178
-rw-r--r--spec/models/project_statistics_spec.rb18
-rw-r--r--spec/models/projects/build_artifacts_size_refresh_spec.rb6
-rw-r--r--spec/models/projects/forks/divergence_counts_spec.rb98
-rw-r--r--spec/models/release_highlight_spec.rb4
-rw-r--r--spec/models/repository_spec.rb62
-rw-r--r--spec/models/service_desk_setting_spec.rb9
-rw-r--r--spec/models/snippet_repository_spec.rb6
-rw-r--r--spec/models/state_note_spec.rb7
-rw-r--r--spec/models/todo_spec.rb20
-rw-r--r--spec/models/user_detail_spec.rb17
-rw-r--r--spec/models/user_preference_spec.rb157
-rw-r--r--spec/models/user_spec.rb254
-rw-r--r--spec/models/users/phone_number_validation_spec.rb38
-rw-r--r--spec/models/work_item_spec.rb57
-rw-r--r--spec/models/work_items/hierarchy_restriction_spec.rb18
-rw-r--r--spec/models/work_items/parent_link_spec.rb105
-rw-r--r--spec/models/work_items/type_spec.rb31
-rw-r--r--spec/models/work_items/widgets/notes_spec.rb20
-rw-r--r--spec/models/zoom_meeting_spec.rb1
-rw-r--r--spec/policies/ci/runner_policy_spec.rb2
-rw-r--r--spec/policies/concerns/archived_abilities_spec.rb27
-rw-r--r--spec/policies/concerns/readonly_abilities_spec.rb27
-rw-r--r--spec/policies/group_policy_spec.rb53
-rw-r--r--spec/policies/issue_policy_spec.rb44
-rw-r--r--spec/policies/merge_request_policy_spec.rb102
-rw-r--r--spec/policies/namespaces/user_namespace_policy_spec.rb7
-rw-r--r--spec/policies/note_policy_spec.rb29
-rw-r--r--spec/policies/project_policy_spec.rb81
-rw-r--r--spec/presenters/blob_presenter_spec.rb2
-rw-r--r--spec/presenters/ci/freeze_period_presenter_spec.rb37
-rw-r--r--spec/presenters/group_member_presenter_spec.rb18
-rw-r--r--spec/presenters/member_presenter_spec.rb14
-rw-r--r--spec/presenters/packages/pypi/simple_package_versions_presenter_spec.rb8
-rw-r--r--spec/presenters/project_member_presenter_spec.rb18
-rw-r--r--spec/presenters/project_presenter_spec.rb22
-rw-r--r--spec/presenters/projects/security/configuration_presenter_spec.rb2
-rw-r--r--spec/presenters/search_service_presenter_spec.rb8
-rw-r--r--spec/requests/abuse_reports_controller_spec.rb2
-rw-r--r--spec/requests/admin/applications_controller_spec.rb3
-rw-r--r--spec/requests/admin/background_migrations_controller_spec.rb2
-rw-r--r--spec/requests/admin/batched_jobs_controller_spec.rb2
-rw-r--r--spec/requests/admin/broadcast_messages_controller_spec.rb2
-rw-r--r--spec/requests/admin/clusters/integrations_controller_spec.rb2
-rw-r--r--spec/requests/admin/hook_logs_controller_spec.rb2
-rw-r--r--spec/requests/admin/impersonation_tokens_controller_spec.rb3
-rw-r--r--spec/requests/admin/integrations_controller_spec.rb2
-rw-r--r--spec/requests/admin/version_check_controller_spec.rb2
-rw-r--r--spec/requests/api/access_requests_spec.rb2
-rw-r--r--spec/requests/api/admin/batched_background_migrations_spec.rb2
-rw-r--r--spec/requests/api/admin/instance_clusters_spec.rb2
-rw-r--r--spec/requests/api/admin/plan_limits_spec.rb14
-rw-r--r--spec/requests/api/admin/sidekiq_spec.rb2
-rw-r--r--spec/requests/api/alert_management_alerts_spec.rb2
-rw-r--r--spec/requests/api/api_guard/admin_mode_middleware_spec.rb2
-rw-r--r--spec/requests/api/api_guard/response_coercer_middleware_spec.rb2
-rw-r--r--spec/requests/api/api_spec.rb2
-rw-r--r--spec/requests/api/appearance_spec.rb5
-rw-r--r--spec/requests/api/applications_spec.rb2
-rw-r--r--spec/requests/api/avatar_spec.rb2
-rw-r--r--spec/requests/api/award_emoji_spec.rb2
-rw-r--r--spec/requests/api/badges_spec.rb2
-rw-r--r--spec/requests/api/boards_spec.rb2
-rw-r--r--spec/requests/api/branches_spec.rb14
-rw-r--r--spec/requests/api/broadcast_messages_spec.rb2
-rw-r--r--spec/requests/api/bulk_imports_spec.rb5
-rw-r--r--spec/requests/api/ci/job_artifacts_spec.rb2
-rw-r--r--spec/requests/api/ci/jobs_spec.rb153
-rw-r--r--spec/requests/api/ci/pipeline_schedules_spec.rb2
-rw-r--r--spec/requests/api/ci/pipelines_spec.rb2
-rw-r--r--spec/requests/api/ci/resource_groups_spec.rb2
-rw-r--r--spec/requests/api/ci/runner/jobs_artifacts_spec.rb2
-rw-r--r--spec/requests/api/ci/runner/jobs_put_spec.rb2
-rw-r--r--spec/requests/api/ci/runner/jobs_request_post_spec.rb20
-rw-r--r--spec/requests/api/ci/runner/jobs_trace_spec.rb2
-rw-r--r--spec/requests/api/ci/runner/runners_delete_spec.rb2
-rw-r--r--spec/requests/api/ci/runner/runners_post_spec.rb2
-rw-r--r--spec/requests/api/ci/runner/runners_reset_spec.rb2
-rw-r--r--spec/requests/api/ci/runner/runners_verify_post_spec.rb2
-rw-r--r--spec/requests/api/ci/runners_reset_registration_token_spec.rb2
-rw-r--r--spec/requests/api/ci/runners_spec.rb2
-rw-r--r--spec/requests/api/ci/secure_files_spec.rb19
-rw-r--r--spec/requests/api/ci/triggers_spec.rb2
-rw-r--r--spec/requests/api/ci/variables_spec.rb2
-rw-r--r--spec/requests/api/clusters/agent_tokens_spec.rb38
-rw-r--r--spec/requests/api/clusters/agents_spec.rb2
-rw-r--r--spec/requests/api/commit_statuses_spec.rb62
-rw-r--r--spec/requests/api/commits_spec.rb92
-rw-r--r--spec/requests/api/composer_packages_spec.rb23
-rw-r--r--spec/requests/api/conan_instance_packages_spec.rb4
-rw-r--r--spec/requests/api/conan_project_packages_spec.rb47
-rw-r--r--spec/requests/api/container_registry_event_spec.rb2
-rw-r--r--spec/requests/api/container_repositories_spec.rb9
-rw-r--r--spec/requests/api/debian_group_packages_spec.rb2
-rw-r--r--spec/requests/api/debian_project_packages_spec.rb2
-rw-r--r--spec/requests/api/dependency_proxy_spec.rb2
-rw-r--r--spec/requests/api/deploy_keys_spec.rb2
-rw-r--r--spec/requests/api/deploy_tokens_spec.rb12
-rw-r--r--spec/requests/api/deployments_spec.rb46
-rw-r--r--spec/requests/api/discussions_spec.rb69
-rw-r--r--spec/requests/api/doorkeeper_access_spec.rb2
-rw-r--r--spec/requests/api/environments_spec.rb6
-rw-r--r--spec/requests/api/error_tracking/client_keys_spec.rb2
-rw-r--r--spec/requests/api/error_tracking/collector_spec.rb2
-rw-r--r--spec/requests/api/error_tracking/project_settings_spec.rb2
-rw-r--r--spec/requests/api/events_spec.rb2
-rw-r--r--spec/requests/api/feature_flags_spec.rb2
-rw-r--r--spec/requests/api/feature_flags_user_lists_spec.rb2
-rw-r--r--spec/requests/api/features_spec.rb52
-rw-r--r--spec/requests/api/files_spec.rb171
-rw-r--r--spec/requests/api/freeze_periods_spec.rb2
-rw-r--r--spec/requests/api/generic_packages_spec.rb8
-rw-r--r--spec/requests/api/geo_spec.rb2
-rw-r--r--spec/requests/api/go_proxy_spec.rb2
-rw-r--r--spec/requests/api/graphql/boards/board_list_issues_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/boards/board_list_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/boards/board_lists_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/boards/boards_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/application_setting_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/ci_cd_setting_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/config_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/config_variables_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/group_variables_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/groups_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/instance_variables_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/job_artifacts_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/job_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/jobs_spec.rb63
-rw-r--r--spec/requests/api/graphql/ci/manual_variables_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/pipeline_schedules_spec.rb58
-rw-r--r--spec/requests/api/graphql/ci/pipelines_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/project_variables_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/runner_spec.rb264
-rw-r--r--spec/requests/api/graphql/ci/runner_web_url_edge_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/runners_spec.rb4
-rw-r--r--spec/requests/api/graphql/ci/stages_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/template_spec.rb2
-rw-r--r--spec/requests/api/graphql/container_repository/container_repository_details_spec.rb2
-rw-r--r--spec/requests/api/graphql/crm/contacts_spec.rb2
-rw-r--r--spec/requests/api/graphql/current_user/groups_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/current_user/todos_query_spec.rb4
-rw-r--r--spec/requests/api/graphql/current_user_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/current_user_todos_spec.rb3
-rw-r--r--spec/requests/api/graphql/custom_emoji_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/environments/deployments_query_spec.rb487
-rw-r--r--spec/requests/api/graphql/environments/deployments_spec.rb524
-rw-r--r--spec/requests/api/graphql/gitlab_schema_spec.rb2
-rw-r--r--spec/requests/api/graphql/group/container_repositories_spec.rb2
-rw-r--r--spec/requests/api/graphql/group/dependency_proxy_blobs_spec.rb2
-rw-r--r--spec/requests/api/graphql/group/dependency_proxy_group_setting_spec.rb2
-rw-r--r--spec/requests/api/graphql/group/dependency_proxy_image_ttl_policy_spec.rb2
-rw-r--r--spec/requests/api/graphql/group/dependency_proxy_manifests_spec.rb2
-rw-r--r--spec/requests/api/graphql/group/group_members_spec.rb2
-rw-r--r--spec/requests/api/graphql/group/issues_spec.rb2
-rw-r--r--spec/requests/api/graphql/group/labels_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/group/merge_requests_spec.rb2
-rw-r--r--spec/requests/api/graphql/group/milestones_spec.rb2
-rw-r--r--spec/requests/api/graphql/group/packages_spec.rb2
-rw-r--r--spec/requests/api/graphql/group/recent_issue_boards_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/group/timelogs_spec.rb2
-rw-r--r--spec/requests/api/graphql/group/work_item_types_spec.rb2
-rw-r--r--spec/requests/api/graphql/group_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/issue/issue_spec.rb2
-rw-r--r--spec/requests/api/graphql/issue_status_counts_spec.rb2
-rw-r--r--spec/requests/api/graphql/issues_spec.rb128
-rw-r--r--spec/requests/api/graphql/jobs_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/merge_request/merge_request_spec.rb2
-rw-r--r--spec/requests/api/graphql/metadata_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb2
-rw-r--r--spec/requests/api/graphql/metrics/dashboard_query_spec.rb155
-rw-r--r--spec/requests/api/graphql/milestone_spec.rb2
-rw-r--r--spec/requests/api/graphql/multiplexed_queries_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/alert_management/alerts/create_alert_issue_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/alert_management/alerts/todo/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/alert_management/http_integration/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/alert_management/http_integration/destroy_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/alert_management/http_integration/reset_token_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/alert_management/http_integration/update_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/alert_management/prometheus_integration/update_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/award_emojis/add_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/boards/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/boards/destroy_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/boards/issues/issue_move_list_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/boards/lists/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/boards/lists/destroy_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/boards/lists/update_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/branches/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/ci/job/artifacts_destroy_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/ci/job/destroy_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/ci/job_artifact/destroy_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/ci/job_cancel_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/ci/job_play_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/ci/job_retry_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb4
-rw-r--r--spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb4
-rw-r--r--spec/requests/api/graphql/mutations/ci/job_unschedule_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/ci/pipeline_cancel_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/ci/pipeline_retry_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/ci/pipeline_schedule_create_spec.rb151
-rw-r--r--spec/requests/api/graphql/mutations/ci/pipeline_schedule_delete_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/ci/pipeline_schedule_play_spec.rb80
-rw-r--r--spec/requests/api/graphql/mutations/ci/pipeline_schedule_take_ownership_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/ci/runners_registration_token/reset_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/clusters/agent_tokens/agent_tokens/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/clusters/agents/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/clusters/agents/delete_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/commits/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb21
-rw-r--r--spec/requests/api/graphql/mutations/container_repository/destroy_tags_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/dependency_proxy/group_settings/update_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/dependency_proxy/image_ttl_group_policy/update_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/design_management/delete_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/design_management/move_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/design_management/upload_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/groups/update_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/incident_management/timeline_event/destroy_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/incident_management/timeline_event/update_spec.rb43
-rw-r--r--spec/requests/api/graphql/mutations/incident_management/timeline_event_tag/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/issues/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/issues/link_alerts_spec.rb65
-rw-r--r--spec/requests/api/graphql/mutations/issues/move_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/issues/set_confidential_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/issues/set_due_date_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/issues/set_escalation_status_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/issues/set_locked_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/issues/set_severity_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/issues/set_subscription_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/issues/unlink_alerts_spec.rb89
-rw-r--r--spec/requests/api/graphql/mutations/issues/update_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/jira_import/start_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/labels/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_draft_spec.rb4
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_reviewers_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/notes/create/note_spec.rb31
-rw-r--r--spec/requests/api/graphql/mutations/notes/destroy_spec.rb41
-rw-r--r--spec/requests/api/graphql/mutations/notes/reposition_image_diff_note_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/notes/update/note_spec.rb59
-rw-r--r--spec/requests/api/graphql/mutations/packages/bulk_destroy_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/packages/cleanup/policy/update_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/packages/destroy_file_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/packages/destroy_files_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/packages/destroy_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/release_asset_links/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/release_asset_links/delete_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/release_asset_links/update_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/releases/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/releases/delete_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/releases/update_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/security/ci_configuration/configure_sast_iac_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/security/ci_configuration/configure_secret_detection_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/snippets/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/snippets/destroy_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/snippets/update_spec.rb15
-rw-r--r--spec/requests/api/graphql/mutations/timelogs/create_spec.rb10
-rw-r--r--spec/requests/api/graphql/mutations/timelogs/delete_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/todos/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/todos/mark_all_done_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/todos/mark_done_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/todos/restore_many_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/todos/restore_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/uploads/delete_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/user_callouts/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/user_preferences/update_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/work_items/create_spec.rb4
-rw-r--r--spec/requests/api/graphql/mutations/work_items/delete_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/work_items/delete_task_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/work_items/update_spec.rb10
-rw-r--r--spec/requests/api/graphql/mutations/work_items/update_task_spec.rb2
-rw-r--r--spec/requests/api/graphql/namespace/package_settings_spec.rb2
-rw-r--r--spec/requests/api/graphql/namespace/projects_spec.rb2
-rw-r--r--spec/requests/api/graphql/namespace/root_storage_statistics_spec.rb2
-rw-r--r--spec/requests/api/graphql/namespace_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/packages/composer_spec.rb2
-rw-r--r--spec/requests/api/graphql/packages/conan_spec.rb2
-rw-r--r--spec/requests/api/graphql/packages/helm_spec.rb2
-rw-r--r--spec/requests/api/graphql/packages/maven_spec.rb2
-rw-r--r--spec/requests/api/graphql/packages/nuget_spec.rb2
-rw-r--r--spec/requests/api/graphql/packages/package_spec.rb13
-rw-r--r--spec/requests/api/graphql/packages/pypi_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/alert_management/alert/assignees_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/alert_management/alert/issue_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/alert_management/alert/notes_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/alert_management/alert/todos_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/alert_management/alerts_spec.rb4
-rw-r--r--spec/requests/api/graphql/project/alert_management/integrations_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/base_service_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/branch_protections/merge_access_levels_spec.rb4
-rw-r--r--spec/requests/api/graphql/project/branch_protections/push_access_levels_spec.rb4
-rw-r--r--spec/requests/api/graphql/project/branch_rules/branch_protection_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/branch_rules_spec.rb81
-rw-r--r--spec/requests/api/graphql/project/cluster_agents_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/container_expiration_policy_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/container_repositories_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/deployment_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/environments_spec.rb133
-rw-r--r--spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/fork_details_spec.rb60
-rw-r--r--spec/requests/api/graphql/project/fork_targets_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/grafana_integration_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/issue/design_collection/version_spec.rb11
-rw-r--r--spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/issue/designs/designs_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/issue/designs/notes_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/issue/notes_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/issue_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/issues_spec.rb698
-rw-r--r--spec/requests/api/graphql/project/jira_import_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/jira_projects_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/jira_service_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/job_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/jobs_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/labels_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/languages_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/merge_request/pipelines_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/merge_request_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/merge_requests_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/milestones_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/packages_cleanup_policy_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/packages_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/pipeline_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/project_members_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/project_statistics_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/recent_issue_boards_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/release_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/releases_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/repository/blobs_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/repository_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/runners_spec.rb68
-rw-r--r--spec/requests/api/graphql/project/terraform/state_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/terraform/states_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/tree/tree_spec.rb50
-rw-r--r--spec/requests/api/graphql/project/work_item_types_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/work_items_spec.rb134
-rw-r--r--spec/requests/api/graphql/project_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/query_spec.rb2
-rw-r--r--spec/requests/api/graphql/read_only_spec.rb2
-rw-r--r--spec/requests/api/graphql/snippets_spec.rb2
-rw-r--r--spec/requests/api/graphql/tasks/task_completion_status_spec.rb2
-rw-r--r--spec/requests/api/graphql/terraform/state/delete_spec.rb2
-rw-r--r--spec/requests/api/graphql/terraform/state/lock_spec.rb2
-rw-r--r--spec/requests/api/graphql/terraform/state/unlock_spec.rb2
-rw-r--r--spec/requests/api/graphql/todo_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/usage_trends_measurements_spec.rb2
-rw-r--r--spec/requests/api/graphql/user/group_member_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/user/project_member_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/user/starred_projects_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/user_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/user_spec.rb2
-rw-r--r--spec/requests/api/graphql/users_spec.rb2
-rw-r--r--spec/requests/api/graphql/work_item_spec.rb12
-rw-r--r--spec/requests/api/graphql_spec.rb6
-rw-r--r--spec/requests/api/group_avatar_spec.rb2
-rw-r--r--spec/requests/api/group_boards_spec.rb2
-rw-r--r--spec/requests/api/group_clusters_spec.rb2
-rw-r--r--spec/requests/api/group_container_repositories_spec.rb6
-rw-r--r--spec/requests/api/group_debian_distributions_spec.rb2
-rw-r--r--spec/requests/api/group_export_spec.rb2
-rw-r--r--spec/requests/api/group_import_spec.rb8
-rw-r--r--spec/requests/api/group_labels_spec.rb2
-rw-r--r--spec/requests/api/group_milestones_spec.rb2
-rw-r--r--spec/requests/api/group_packages_spec.rb2
-rw-r--r--spec/requests/api/group_variables_spec.rb2
-rw-r--r--spec/requests/api/groups_spec.rb2
-rw-r--r--spec/requests/api/helm_packages_spec.rb8
-rw-r--r--spec/requests/api/helpers_spec.rb2
-rw-r--r--spec/requests/api/import_bitbucket_server_spec.rb2
-rw-r--r--spec/requests/api/import_github_spec.rb2
-rw-r--r--spec/requests/api/integrations/jira_connect/subscriptions_spec.rb16
-rw-r--r--spec/requests/api/integrations_spec.rb2
-rw-r--r--spec/requests/api/internal/base_spec.rb48
-rw-r--r--spec/requests/api/internal/container_registry/migration_spec.rb2
-rw-r--r--spec/requests/api/internal/error_tracking_spec.rb2
-rw-r--r--spec/requests/api/internal/kubernetes_spec.rb18
-rw-r--r--spec/requests/api/internal/lfs_spec.rb2
-rw-r--r--spec/requests/api/internal/mail_room_spec.rb2
-rw-r--r--spec/requests/api/internal/pages_spec.rb2
-rw-r--r--spec/requests/api/internal/workhorse_spec.rb2
-rw-r--r--spec/requests/api/invitations_spec.rb2
-rw-r--r--spec/requests/api/issue_links_spec.rb6
-rw-r--r--spec/requests/api/issues/get_group_issues_spec.rb2
-rw-r--r--spec/requests/api/issues/get_project_issues_spec.rb2
-rw-r--r--spec/requests/api/issues/issues_spec.rb2
-rw-r--r--spec/requests/api/issues/post_projects_issues_spec.rb2
-rw-r--r--spec/requests/api/issues/put_projects_issues_spec.rb2
-rw-r--r--spec/requests/api/keys_spec.rb2
-rw-r--r--spec/requests/api/labels_spec.rb2
-rw-r--r--spec/requests/api/lint_spec.rb2
-rw-r--r--spec/requests/api/markdown_golden_master_spec.rb2
-rw-r--r--spec/requests/api/markdown_snapshot_spec.rb2
-rw-r--r--spec/requests/api/markdown_spec.rb2
-rw-r--r--spec/requests/api/maven_packages_spec.rb9
-rw-r--r--spec/requests/api/members_spec.rb2
-rw-r--r--spec/requests/api/merge_request_approvals_spec.rb83
-rw-r--r--spec/requests/api/merge_request_diffs_spec.rb2
-rw-r--r--spec/requests/api/merge_requests_spec.rb120
-rw-r--r--spec/requests/api/metadata_spec.rb2
-rw-r--r--spec/requests/api/metrics/dashboard/annotations_spec.rb2
-rw-r--r--spec/requests/api/metrics/user_starred_dashboards_spec.rb2
-rw-r--r--spec/requests/api/ml/mlflow_spec.rb135
-rw-r--r--spec/requests/api/namespaces_spec.rb2
-rw-r--r--spec/requests/api/notes_spec.rb47
-rw-r--r--spec/requests/api/notification_settings_spec.rb2
-rw-r--r--spec/requests/api/npm_instance_packages_spec.rb14
-rw-r--r--spec/requests/api/npm_project_packages_spec.rb17
-rw-r--r--spec/requests/api/nuget_group_packages_spec.rb6
-rw-r--r--spec/requests/api/nuget_project_packages_spec.rb56
-rw-r--r--spec/requests/api/oauth_tokens_spec.rb4
-rw-r--r--spec/requests/api/package_files_spec.rb2
-rw-r--r--spec/requests/api/pages/internal_access_spec.rb2
-rw-r--r--spec/requests/api/pages/pages_spec.rb2
-rw-r--r--spec/requests/api/pages/private_access_spec.rb2
-rw-r--r--spec/requests/api/pages/public_access_spec.rb2
-rw-r--r--spec/requests/api/pages_domains_spec.rb2
-rw-r--r--spec/requests/api/performance_bar_spec.rb3
-rw-r--r--spec/requests/api/personal_access_tokens/self_information_spec.rb2
-rw-r--r--spec/requests/api/personal_access_tokens_spec.rb2
-rw-r--r--spec/requests/api/project_attributes.yml6
-rw-r--r--spec/requests/api/project_clusters_spec.rb2
-rw-r--r--spec/requests/api/project_container_repositories_spec.rb34
-rw-r--r--spec/requests/api/project_debian_distributions_spec.rb8
-rw-r--r--spec/requests/api/project_events_spec.rb2
-rw-r--r--spec/requests/api/project_export_spec.rb2
-rw-r--r--spec/requests/api/project_hooks_spec.rb2
-rw-r--r--spec/requests/api/project_import_spec.rb46
-rw-r--r--spec/requests/api/project_milestones_spec.rb2
-rw-r--r--spec/requests/api/project_packages_spec.rb12
-rw-r--r--spec/requests/api/project_repository_storage_moves_spec.rb2
-rw-r--r--spec/requests/api/project_snapshots_spec.rb4
-rw-r--r--spec/requests/api/project_snippets_spec.rb2
-rw-r--r--spec/requests/api/project_statistics_spec.rb2
-rw-r--r--spec/requests/api/project_templates_spec.rb2
-rw-r--r--spec/requests/api/projects_spec.rb44
-rw-r--r--spec/requests/api/protected_branches_spec.rb2
-rw-r--r--spec/requests/api/protected_tags_spec.rb2
-rw-r--r--spec/requests/api/pypi_packages_spec.rb15
-rw-r--r--spec/requests/api/release/links_spec.rb2
-rw-r--r--spec/requests/api/releases_spec.rb2
-rw-r--r--spec/requests/api/remote_mirrors_spec.rb6
-rw-r--r--spec/requests/api/repositories_spec.rb10
-rw-r--r--spec/requests/api/resource_access_tokens_spec.rb2
-rw-r--r--spec/requests/api/resource_label_events_spec.rb2
-rw-r--r--spec/requests/api/resource_milestone_events_spec.rb2
-rw-r--r--spec/requests/api/rpm_project_packages_spec.rb22
-rw-r--r--spec/requests/api/rubygem_packages_spec.rb8
-rw-r--r--spec/requests/api/search_spec.rb2
-rw-r--r--spec/requests/api/settings_spec.rb16
-rw-r--r--spec/requests/api/sidekiq_metrics_spec.rb2
-rw-r--r--spec/requests/api/snippet_repository_storage_moves_spec.rb2
-rw-r--r--spec/requests/api/snippets_spec.rb2
-rw-r--r--spec/requests/api/statistics_spec.rb2
-rw-r--r--spec/requests/api/submodules_spec.rb2
-rw-r--r--spec/requests/api/suggestions_spec.rb2
-rw-r--r--spec/requests/api/system_hooks_spec.rb2
-rw-r--r--spec/requests/api/tags_spec.rb58
-rw-r--r--spec/requests/api/task_completion_status_spec.rb72
-rw-r--r--spec/requests/api/templates_spec.rb2
-rw-r--r--spec/requests/api/terraform/modules/v1/packages_spec.rb10
-rw-r--r--spec/requests/api/terraform/state_spec.rb321
-rw-r--r--spec/requests/api/terraform/state_version_spec.rb2
-rw-r--r--spec/requests/api/todos_spec.rb20
-rw-r--r--spec/requests/api/topics_spec.rb2
-rw-r--r--spec/requests/api/unleash_spec.rb81
-rw-r--r--spec/requests/api/usage_data_non_sql_metrics_spec.rb2
-rw-r--r--spec/requests/api/usage_data_queries_spec.rb4
-rw-r--r--spec/requests/api/usage_data_spec.rb2
-rw-r--r--spec/requests/api/user_counts_spec.rb2
-rw-r--r--spec/requests/api/users_preferences_spec.rb2
-rw-r--r--spec/requests/api/users_spec.rb25
-rw-r--r--spec/requests/api/v3/github_spec.rb2
-rw-r--r--spec/requests/api/wikis_spec.rb2
-rw-r--r--spec/requests/concerns/planning_hierarchy_spec.rb2
-rw-r--r--spec/requests/content_security_policy_spec.rb2
-rw-r--r--spec/requests/dashboard/projects_controller_spec.rb2
-rw-r--r--spec/requests/dashboard_controller_spec.rb2
-rw-r--r--spec/requests/git_http_spec.rb2
-rw-r--r--spec/requests/groups/autocomplete_sources_spec.rb2
-rw-r--r--spec/requests/groups/clusters/integrations_controller_spec.rb2
-rw-r--r--spec/requests/groups/crm/contacts_controller_spec.rb2
-rw-r--r--spec/requests/groups/crm/organizations_controller_spec.rb2
-rw-r--r--spec/requests/groups/deploy_tokens_controller_spec.rb2
-rw-r--r--spec/requests/groups/email_campaigns_controller_spec.rb2
-rw-r--r--spec/requests/groups/harbor/artifacts_controller_spec.rb2
-rw-r--r--spec/requests/groups/harbor/repositories_controller_spec.rb2
-rw-r--r--spec/requests/groups/harbor/tags_controller_spec.rb2
-rw-r--r--spec/requests/groups/milestones_controller_spec.rb2
-rw-r--r--spec/requests/groups/observability_controller_spec.rb96
-rw-r--r--spec/requests/groups/registry/repositories_controller_spec.rb2
-rw-r--r--spec/requests/groups/settings/access_tokens_controller_spec.rb2
-rw-r--r--spec/requests/groups/settings/applications_controller_spec.rb2
-rw-r--r--spec/requests/groups/usage_quotas_controller_spec.rb48
-rw-r--r--spec/requests/groups_controller_spec.rb2
-rw-r--r--spec/requests/health_controller_spec.rb2
-rw-r--r--spec/requests/ide_controller_spec.rb27
-rw-r--r--spec/requests/import/github_groups_controller_spec.rb2
-rw-r--r--spec/requests/import/gitlab_groups_controller_spec.rb2
-rw-r--r--spec/requests/import/gitlab_projects_controller_spec.rb2
-rw-r--r--spec/requests/import/url_controller_spec.rb2
-rw-r--r--spec/requests/jira_authorizations_spec.rb2
-rw-r--r--spec/requests/jira_connect/cors_preflight_checks_controller_spec.rb59
-rw-r--r--spec/requests/jira_connect/installations_controller_spec.rb83
-rw-r--r--spec/requests/jira_connect/oauth_application_ids_controller_spec.rb29
-rw-r--r--spec/requests/jira_connect/oauth_callbacks_controller_spec.rb2
-rw-r--r--spec/requests/jira_connect/public_keys_controller_spec.rb14
-rw-r--r--spec/requests/jira_connect/subscriptions_controller_spec.rb65
-rw-r--r--spec/requests/jira_connect/users_controller_spec.rb2
-rw-r--r--spec/requests/jira_routing_spec.rb2
-rw-r--r--spec/requests/jwks_controller_spec.rb2
-rw-r--r--spec/requests/jwt_controller_spec.rb2
-rw-r--r--spec/requests/lfs_http_spec.rb89
-rw-r--r--spec/requests/lfs_locks_api_spec.rb2
-rw-r--r--spec/requests/mailgun/webhooks_controller_spec.rb2
-rw-r--r--spec/requests/oauth/applications_controller_spec.rb2
-rw-r--r--spec/requests/oauth/authorizations_controller_spec.rb2
-rw-r--r--spec/requests/oauth/tokens_controller_spec.rb2
-rw-r--r--spec/requests/oauth_tokens_spec.rb2
-rw-r--r--spec/requests/openid_connect_spec.rb2
-rw-r--r--spec/requests/profiles/notifications_controller_spec.rb2
-rw-r--r--spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb2
-rw-r--r--spec/requests/projects/cluster_agents_controller_spec.rb2
-rw-r--r--spec/requests/projects/clusters/integrations_controller_spec.rb2
-rw-r--r--spec/requests/projects/commits_controller_spec.rb2
-rw-r--r--spec/requests/projects/cycle_analytics_events_spec.rb2
-rw-r--r--spec/requests/projects/environments_controller_spec.rb2
-rw-r--r--spec/requests/projects/google_cloud/configuration_controller_spec.rb2
-rw-r--r--spec/requests/projects/google_cloud/databases_controller_spec.rb2
-rw-r--r--spec/requests/projects/google_cloud/deployments_controller_spec.rb2
-rw-r--r--spec/requests/projects/google_cloud/gcp_regions_controller_spec.rb2
-rw-r--r--spec/requests/projects/google_cloud/revoke_oauth_controller_spec.rb2
-rw-r--r--spec/requests/projects/google_cloud/service_accounts_controller_spec.rb2
-rw-r--r--spec/requests/projects/harbor/artifacts_controller_spec.rb2
-rw-r--r--spec/requests/projects/harbor/repositories_controller_spec.rb2
-rw-r--r--spec/requests/projects/harbor/tags_controller_spec.rb2
-rw-r--r--spec/requests/projects/hook_logs_controller_spec.rb2
-rw-r--r--spec/requests/projects/incident_management/pagerduty_incidents_spec.rb2
-rw-r--r--spec/requests/projects/incident_management/timeline_events_spec.rb2
-rw-r--r--spec/requests/projects/integrations/shimos_controller_spec.rb2
-rw-r--r--spec/requests/projects/issue_links_controller_spec.rb2
-rw-r--r--spec/requests/projects/issues/discussions_spec.rb2
-rw-r--r--spec/requests/projects/issues_controller_spec.rb26
-rw-r--r--spec/requests/projects/merge_requests/content_spec.rb2
-rw-r--r--spec/requests/projects/merge_requests/context_commit_diffs_spec.rb2
-rw-r--r--spec/requests/projects/merge_requests/creations_spec.rb14
-rw-r--r--spec/requests/projects/merge_requests/diffs_spec.rb16
-rw-r--r--spec/requests/projects/merge_requests_controller_spec.rb21
-rw-r--r--spec/requests/projects/merge_requests_discussions_spec.rb2
-rw-r--r--spec/requests/projects/merge_requests_spec.rb2
-rw-r--r--spec/requests/projects/metrics/dashboards/builder_spec.rb2
-rw-r--r--spec/requests/projects/metrics_dashboard_spec.rb2
-rw-r--r--spec/requests/projects/ml/candidates_controller_spec.rb69
-rw-r--r--spec/requests/projects/ml/experiments_controller_spec.rb10
-rw-r--r--spec/requests/projects/network_controller_spec.rb57
-rw-r--r--spec/requests/projects/noteable_notes_spec.rb2
-rw-r--r--spec/requests/projects/packages/package_files_controller_spec.rb2
-rw-r--r--spec/requests/projects/pipelines_controller_spec.rb2
-rw-r--r--spec/requests/projects/redirect_controller_spec.rb2
-rw-r--r--spec/requests/projects/releases_controller_spec.rb2
-rw-r--r--spec/requests/projects/settings/access_tokens_controller_spec.rb2
-rw-r--r--spec/requests/projects/settings/integration_hook_logs_controller_spec.rb2
-rw-r--r--spec/requests/projects/settings/packages_and_registries_controller_spec.rb2
-rw-r--r--spec/requests/projects/tags_controller_spec.rb2
-rw-r--r--spec/requests/projects/uploads_spec.rb2
-rw-r--r--spec/requests/projects/usage_quotas_spec.rb2
-rw-r--r--spec/requests/projects/work_items_spec.rb2
-rw-r--r--spec/requests/projects_controller_spec.rb2
-rw-r--r--spec/requests/pwa_controller_spec.rb19
-rw-r--r--spec/requests/rack_attack_global_spec.rb3
-rw-r--r--spec/requests/recursive_webhook_detection_spec.rb3
-rw-r--r--spec/requests/robots_txt_spec.rb2
-rw-r--r--spec/requests/runner_setup_controller_spec.rb2
-rw-r--r--spec/requests/sandbox_controller_spec.rb2
-rw-r--r--spec/requests/search_controller_spec.rb94
-rw-r--r--spec/requests/self_monitoring_project_spec.rb2
-rw-r--r--spec/requests/sessions_spec.rb2
-rw-r--r--spec/requests/terraform/services_controller_spec.rb2
-rw-r--r--spec/requests/user_activity_spec.rb2
-rw-r--r--spec/requests/user_avatar_spec.rb2
-rw-r--r--spec/requests/user_sends_malformed_strings_spec.rb2
-rw-r--r--spec/requests/user_spoofs_ip_spec.rb2
-rw-r--r--spec/requests/users/group_callouts_spec.rb2
-rw-r--r--spec/requests/users/project_callouts_spec.rb2
-rw-r--r--spec/requests/users_controller_spec.rb2
-rw-r--r--spec/requests/verifies_with_email_spec.rb25
-rw-r--r--spec/requests/web_ide/remote_ide_controller_spec.rb145
-rw-r--r--spec/requests/whats_new_controller_spec.rb2
-rw-r--r--spec/routing/group_routing_spec.rb4
-rw-r--r--spec/routing/project_routing_spec.rb5
-rw-r--r--spec/routing/user_routing_spec.rb29
-rw-r--r--spec/routing/web_ide_routing_spec.rb22
-rw-r--r--spec/rubocop/cop/feature_flag_usage_spec.rb55
-rw-r--r--spec/rubocop/cop/filename_length_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/feature_available_usage_spec.rb2
-rw-r--r--spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb2
-rw-r--r--spec/rubocop/cop/gitlab/strong_memoize_attr_spec.rb75
-rw-r--r--spec/rubocop/cop/graphql/descriptions_spec.rb241
-rw-r--r--spec/rubocop/cop/migration/add_column_with_default_spec.rb33
-rw-r--r--spec/rubocop/cop/migration/add_limit_to_text_columns_spec.rb18
-rw-r--r--spec/rubocop/cop/migration/batch_migrations_post_only_spec.rb84
-rw-r--r--spec/rubocop/cop/migration/prevent_strings_spec.rb12
-rw-r--r--spec/rubocop/cop/migration/versioned_migration_class_spec.rb10
-rw-r--r--spec/rubocop/cop/performance/readlines_each_spec.rb4
-rw-r--r--spec/rubocop/cop/rspec/avoid_test_prof_spec.rb49
-rw-r--r--spec/rubocop/cop/rspec/timecop_freeze_spec.rb28
-rw-r--r--spec/rubocop/cop/rspec/timecop_travel_spec.rb28
-rw-r--r--spec/rubocop/cop/user_admin_spec.rb2
-rw-r--r--spec/rubocop/formatter/graceful_formatter_spec.rb4
-rw-r--r--spec/rubocop/support_workaround.rb33
-rw-r--r--spec/rubocop_spec_helper.rb4
-rw-r--r--spec/scripts/lib/glfm/shared_spec.rb6
-rw-r--r--spec/scripts/lib/glfm/update_specification_spec.rb197
-rw-r--r--spec/scripts/trigger-build_spec.rb22
-rw-r--r--spec/serializers/ci/group_variable_entity_spec.rb6
-rw-r--r--spec/serializers/ci/variable_entity_spec.rb6
-rw-r--r--spec/serializers/deploy_keys/deploy_key_entity_spec.rb3
-rw-r--r--spec/serializers/entity_date_helper_spec.rb6
-rw-r--r--spec/serializers/issue_entity_spec.rb20
-rw-r--r--spec/serializers/linked_project_issue_entity_spec.rb10
-rw-r--r--spec/serializers/member_entity_spec.rb22
-rw-r--r--spec/serializers/merge_request_poll_cached_widget_entity_spec.rb4
-rw-r--r--spec/serializers/merge_request_user_entity_spec.rb2
-rw-r--r--spec/serializers/pipeline_details_entity_spec.rb8
-rw-r--r--spec/serializers/pipeline_serializer_spec.rb24
-rw-r--r--spec/serializers/prometheus_alert_entity_spec.rb4
-rw-r--r--spec/serializers/release_serializer_spec.rb11
-rw-r--r--spec/services/admin/set_feature_flag_service_spec.rb199
-rw-r--r--spec/services/bulk_imports/create_service_spec.rb118
-rw-r--r--spec/services/bulk_imports/file_download_service_spec.rb80
-rw-r--r--spec/services/chat_names/find_user_service_spec.rb20
-rw-r--r--spec/services/ci/after_requeue_job_service_spec.rb320
-rw-r--r--spec/services/ci/append_build_trace_service_spec.rb32
-rw-r--r--spec/services/ci/create_downstream_pipeline_service_spec.rb237
-rw-r--r--spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/environment_spec.rb18
-rw-r--r--spec/services/ci/create_pipeline_service/logger_spec.rb43
-rw-r--r--spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/partitioning_spec.rb21
-rw-r--r--spec/services/ci/create_pipeline_service/rules_spec.rb8
-rw-r--r--spec/services/ci/create_pipeline_service/scripts_spec.rb112
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb33
-rw-r--r--spec/services/ci/enqueue_job_service_spec.rb81
-rw-r--r--spec/services/ci/generate_kubeconfig_service_spec.rb110
-rw-r--r--spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb2
-rw-r--r--spec/services/ci/pipeline_schedules/calculate_next_run_service_spec.rb107
-rw-r--r--spec/services/ci/pipeline_trigger_service_spec.rb3
-rw-r--r--spec/services/ci/pipelines/add_job_service_spec.rb4
-rw-r--r--spec/services/ci/process_build_service_spec.rb72
-rw-r--r--spec/services/ci/reset_skipped_jobs_service_spec.rb320
-rw-r--r--spec/services/ci/retry_job_service_spec.rb45
-rw-r--r--spec/services/ci/retry_pipeline_service_spec.rb5
-rw-r--r--spec/services/ci/runners/assign_runner_service_spec.rb2
-rw-r--r--spec/services/ci/runners/bulk_delete_runners_service_spec.rb2
-rw-r--r--spec/services/ci/runners/process_runner_version_update_service_spec.rb2
-rw-r--r--spec/services/ci/runners/reconcile_existing_runner_versions_service_spec.rb2
-rw-r--r--spec/services/ci/runners/register_runner_service_spec.rb2
-rw-r--r--spec/services/ci/runners/reset_registration_token_service_spec.rb2
-rw-r--r--spec/services/ci/runners/set_runner_associated_projects_service_spec.rb2
-rw-r--r--spec/services/ci/runners/unassign_runner_service_spec.rb2
-rw-r--r--spec/services/ci/runners/unregister_runner_service_spec.rb2
-rw-r--r--spec/services/ci/runners/update_runner_service_spec.rb2
-rw-r--r--spec/services/ci/test_failure_history_service_spec.rb37
-rw-r--r--spec/services/ci/track_failed_build_service_spec.rb23
-rw-r--r--spec/services/ci/unlock_artifacts_service_spec.rb27
-rw-r--r--spec/services/clusters/agents/filter_authorizations_service_spec.rb100
-rw-r--r--spec/services/clusters/agents/refresh_authorization_service_spec.rb10
-rw-r--r--spec/services/clusters/applications/install_service_spec.rb80
-rw-r--r--spec/services/clusters/applications/prometheus_config_service_spec.rb162
-rw-r--r--spec/services/clusters/applications/upgrade_service_spec.rb80
-rw-r--r--spec/services/database/consistency_check_service_spec.rb2
-rw-r--r--spec/services/deployments/update_environment_service_spec.rb2
-rw-r--r--spec/services/environments/create_for_build_service_spec.rb8
-rw-r--r--spec/services/environments/stop_service_spec.rb5
-rw-r--r--spec/services/event_create_service_spec.rb109
-rw-r--r--spec/services/feature_flags/hook_service_spec.rb8
-rw-r--r--spec/services/google_cloud/fetch_google_ip_list_service_spec.rb4
-rw-r--r--spec/services/groups/destroy_service_spec.rb20
-rw-r--r--spec/services/groups/import_export/import_service_spec.rb42
-rw-r--r--spec/services/import/bitbucket_server_service_spec.rb19
-rw-r--r--spec/services/import/github/gists_import_service_spec.rb47
-rw-r--r--spec/services/import/github_service_spec.rb23
-rw-r--r--spec/services/incident_management/incidents/create_service_spec.rb24
-rw-r--r--spec/services/incident_management/link_alerts/create_service_spec.rb98
-rw-r--r--spec/services/incident_management/link_alerts/destroy_service_spec.rb88
-rw-r--r--spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb14
-rw-r--r--spec/services/incident_management/pager_duty/process_webhook_service_spec.rb2
-rw-r--r--spec/services/incident_management/timeline_events/create_service_spec.rb18
-rw-r--r--spec/services/incident_management/timeline_events/destroy_service_spec.rb9
-rw-r--r--spec/services/incident_management/timeline_events/update_service_spec.rb168
-rw-r--r--spec/services/issuable/discussions_list_service_spec.rb13
-rw-r--r--spec/services/issue_links/create_service_spec.rb8
-rw-r--r--spec/services/issue_links/destroy_service_spec.rb8
-rw-r--r--spec/services/issues/close_service_spec.rb44
-rw-r--r--spec/services/issues/create_service_spec.rb27
-rw-r--r--spec/services/issues/move_service_spec.rb21
-rw-r--r--spec/services/issues/reopen_service_spec.rb8
-rw-r--r--spec/services/issues/update_service_spec.rb31
-rw-r--r--spec/services/issues/zoom_link_service_spec.rb8
-rw-r--r--spec/services/jira_connect/create_asymmetric_jwt_service_spec.rb27
-rw-r--r--spec/services/jira_connect_installations/proxy_lifecycle_event_service_spec.rb154
-rw-r--r--spec/services/jira_connect_installations/update_service_spec.rb186
-rw-r--r--spec/services/markup/rendering_service_spec.rb51
-rw-r--r--spec/services/merge_requests/after_create_service_spec.rb2
-rw-r--r--spec/services/merge_requests/approval_service_spec.rb8
-rw-r--r--spec/services/merge_requests/build_service_spec.rb99
-rw-r--r--spec/services/merge_requests/create_pipeline_service_spec.rb21
-rw-r--r--spec/services/merge_requests/remove_approval_service_spec.rb8
-rw-r--r--spec/services/ml/experiment_tracking/candidate_repository_spec.rb78
-rw-r--r--spec/services/ml/experiment_tracking/experiment_repository_spec.rb39
-rw-r--r--spec/services/notes/create_service_spec.rb8
-rw-r--r--spec/services/notification_service_spec.rb10
-rw-r--r--spec/services/packages/debian/process_changes_service_spec.rb4
-rw-r--r--spec/services/packages/debian/process_package_file_service_spec.rb161
-rw-r--r--spec/services/pages_domains/retry_acme_order_service_spec.rb36
-rw-r--r--spec/services/personal_access_tokens/revoke_service_spec.rb44
-rw-r--r--spec/services/projects/after_rename_service_spec.rb29
-rw-r--r--spec/services/projects/container_repository/destroy_service_spec.rb51
-rw-r--r--spec/services/projects/container_repository/gitlab/cleanup_tags_service_spec.rb16
-rw-r--r--spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb8
-rw-r--r--spec/services/projects/container_repository/third_party/cleanup_tags_service_spec.rb10
-rw-r--r--spec/services/projects/create_service_spec.rb35
-rw-r--r--spec/services/projects/destroy_service_spec.rb33
-rw-r--r--spec/services/projects/download_service_spec.rb4
-rw-r--r--spec/services/projects/import_export/export_service_spec.rb17
-rw-r--r--spec/services/projects/import_export/parallel_export_service_spec.rb98
-rw-r--r--spec/services/projects/import_service_spec.rb20
-rw-r--r--spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb64
-rw-r--r--spec/services/projects/lfs_pointers/lfs_download_service_spec.rb16
-rw-r--r--spec/services/projects/lfs_pointers/lfs_import_service_spec.rb14
-rw-r--r--spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb64
-rw-r--r--spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb3
-rw-r--r--spec/services/projects/transfer_service_spec.rb30
-rw-r--r--spec/services/projects/update_pages_service_spec.rb7
-rw-r--r--spec/services/projects/update_service_spec.rb39
-rw-r--r--spec/services/protected_branches/api_service_spec.rb69
-rw-r--r--spec/services/protected_branches/cache_service_spec.rb186
-rw-r--r--spec/services/protected_branches/create_service_spec.rb97
-rw-r--r--spec/services/protected_branches/destroy_service_spec.rb59
-rw-r--r--spec/services/protected_branches/update_service_spec.rb74
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb18
-rw-r--r--spec/services/repositories/housekeeping_service_spec.rb2
-rw-r--r--spec/services/search_service_spec.rb2
-rw-r--r--spec/services/security/merge_reports_service_spec.rb26
-rw-r--r--spec/services/service_ping/submit_service_ping_service_spec.rb2
-rw-r--r--spec/services/timelogs/create_service_spec.rb6
-rw-r--r--spec/services/todo_service_spec.rb99
-rw-r--r--spec/services/users/assigned_issues_count_service_spec.rb57
-rw-r--r--spec/services/users/keys_count_service_spec.rb6
-rw-r--r--spec/services/users/migrate_records_to_ghost_user_service_spec.rb14
-rw-r--r--spec/services/users/registrations_build_service_spec.rb4
-rw-r--r--spec/services/web_hooks/log_execution_service_spec.rb39
-rw-r--r--spec/services/work_items/create_and_link_service_spec.rb4
-rw-r--r--spec/services/work_items/create_service_spec.rb4
-rw-r--r--spec/services/work_items/parent_links/create_service_spec.rb6
-rw-r--r--spec/services/work_items/update_service_spec.rb2
-rw-r--r--spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb6
-rw-r--r--spec/sidekiq_cluster/sidekiq_cluster_spec.rb23
-rw-r--r--spec/simplecov_env.rb14
-rw-r--r--spec/spec_helper.rb5
-rw-r--r--spec/support/atlassian/jira_connect/schemata.rb2
-rw-r--r--spec/support/banzai/filter_timeout_shared_examples.rb37
-rw-r--r--spec/support/before_all_adapter.rb51
-rw-r--r--spec/support/capybara.rb20
-rw-r--r--spec/support/counter_attribute.rb7
-rw-r--r--spec/support/cycle_analytics_helpers/test_generation.rb28
-rw-r--r--spec/support/database/query_recorder.rb12
-rw-r--r--spec/support/db_cleaner.rb4
-rw-r--r--spec/support/finder_collection_allowlist.yml4
-rw-r--r--spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb2
-rw-r--r--spec/support/gitlab_stubs/gitlab_ci.yml3
-rw-r--r--spec/support/helpers/batch_destroy_dependent_associations_helper.rb13
-rw-r--r--spec/support/helpers/ci/partitioning_helpers.rb11
-rw-r--r--spec/support/helpers/content_security_policy_helpers.rb10
-rw-r--r--spec/support/helpers/cookie_helper.rb6
-rw-r--r--spec/support/helpers/countries_controller_test_helper.rb9
-rw-r--r--spec/support/helpers/doc_url_helper.rb2
-rw-r--r--spec/support/helpers/features/branches_helpers.rb10
-rw-r--r--spec/support/helpers/features/invite_members_modal_helper.rb29
-rw-r--r--spec/support/helpers/features/runners_helpers.rb2
-rw-r--r--spec/support/helpers/gitaly_setup.rb2
-rw-r--r--spec/support/helpers/graphql_helpers.rb4
-rw-r--r--spec/support/helpers/javascript_fixtures_helpers.rb4
-rw-r--r--spec/support/helpers/listbox_input_helper.rb18
-rw-r--r--spec/support/helpers/migrations_helpers/work_item_types_helper.rb21
-rw-r--r--spec/support/helpers/project_template_test_helper.rb16
-rw-r--r--spec/support/helpers/repo_helpers.rb24
-rw-r--r--spec/support/helpers/search_helpers.rb2
-rw-r--r--spec/support/helpers/service_desk_helper.rb9
-rw-r--r--spec/support/helpers/smime_helper.rb2
-rw-r--r--spec/support/helpers/stub_configuration.rb11
-rw-r--r--spec/support/helpers/stub_object_storage.rb3
-rw-r--r--spec/support/helpers/stub_snowplow.rb2
-rw-r--r--spec/support/helpers/test_env.rb26
-rw-r--r--spec/support/helpers/usage_data_helpers.rb230
-rw-r--r--spec/support/helpers/workhorse_helpers.rb8
-rw-r--r--spec/support/import_export/common_util.rb4
-rw-r--r--spec/support/import_export/export_file_helper.rb4
-rw-r--r--spec/support/matchers/exceed_query_limit.rb18
-rw-r--r--spec/support/memory_instrumentation_helper.rb7
-rw-r--r--spec/support/migration.rb8
-rw-r--r--spec/support/migrations_helpers/vulnerabilities_findings_helper.rb8
-rw-r--r--spec/support/models/ci/partitioning_testing/cascade_check.rb7
-rw-r--r--spec/support/models/ci/partitioning_testing/schema_helpers.rb6
-rw-r--r--spec/support/patches/rspec_mocks_prepended_methods.rb2
-rw-r--r--spec/support/prometheus/additional_metrics_shared_examples.rb12
-rw-r--r--spec/support/redis/redis_shared_examples.rb55
-rw-r--r--spec/support/rspec.rb11
-rw-r--r--spec/support/rspec_order_todo.yml192
-rw-r--r--spec/support/shared_contexts/disable_user_tracking.rb10
-rw-r--r--spec/support/shared_contexts/email_shared_context.rb10
-rw-r--r--spec/support/shared_contexts/models/ci/job_token_scope.rb21
-rw-r--r--spec/support/shared_contexts/policies/group_policy_shared_context.rb38
-rw-r--r--spec/support/shared_contexts/rack_attack_shared_context.rb2
-rw-r--r--spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb4
-rw-r--r--spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb1
-rw-r--r--spec/support/shared_contexts/rubocop_default_rspec_language_config_context.rb32
-rw-r--r--spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb2
-rw-r--r--spec/support/shared_examples/boards/destroy_service_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/ci/log_downstream_pipeline_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/ci/retryable_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/ci/stuck_builds_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/controllers/destroy_hook_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb10
-rw-r--r--spec/support/shared_examples/controllers/variables_shared_examples.rb14
-rw-r--r--spec/support/shared_examples/csp.rb15
-rw-r--r--spec/support/shared_examples/features/container_registry_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/features/content_editor_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/discussion_comments_shared_example.rb4
-rw-r--r--spec/support/shared_examples/features/inviting_members_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/features/reportable_note_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/features/runners_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/features/sidebar/sidebar_milestone_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/features/wiki/file_attachments_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/finders/issues_finder_shared_examples.rb84
-rw-r--r--spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/graphql/label_fields.rb4
-rw-r--r--spec/support/shared_examples/graphql/mutations/incident_management_timeline_events_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/graphql/mutations/timelogs/create_shared_examples.rb32
-rw-r--r--spec/support/shared_examples/graphql/mutations/work_items/update_description_widget_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/graphql/notes_creation_shared_examples.rb56
-rw-r--r--spec/support/shared_examples/initializers/uses_gitlab_url_blocker_shared_examples.rb7
-rw-r--r--spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb8
-rw-r--r--spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/lib/gitlab/middleware/multipart_shared_examples.rb42
-rw-r--r--spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb49
-rw-r--r--spec/support/shared_examples/mailers/notify_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb136
-rw-r--r--spec/support/shared_examples/models/concerns/integrations/base_slack_notification_shared_examples.rb34
-rw-r--r--spec/support/shared_examples/models/concerns/sanitizable_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/concerns/signature_type_shared_examples.rb21
-rw-r--r--spec/support/shared_examples/models/integrations/has_web_hook_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/models/label_note_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/member_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/update_highest_role_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/update_project_statistics_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/with_debian_distributions_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/observability/csp_shared_examples.rb123
-rw-r--r--spec/support/shared_examples/policies/project_policy_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb31
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb36
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb36
-rw-r--r--spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb14
-rw-r--r--spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb513
-rw-r--r--spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb17
-rw-r--r--spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb38
-rw-r--r--spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb18
-rw-r--r--spec/support/shared_examples/requests/api/notes_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb165
-rw-r--r--spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb20
-rw-r--r--spec/support/shared_examples/requests/api/packages_shared_examples.rb15
-rw-r--r--spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb20
-rw-r--r--spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/requests/rack_attack_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/security_training_providers_importer.rb2
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/incident_resolution_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/system_notes_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/alert_management_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/approval_state_updated_trigger_shared_examples.rb17
-rw-r--r--spec/support/shared_examples/services/boards/boards_create_service_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/boards/boards_recent_visit_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/boards/lists_destroy_service_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/boards/lists_list_service_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/services/container_expiration_policy_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/dependency_proxy_ttl_policies_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/incident_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/issuable/update_service_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/packages_shared_examples.rb14
-rw-r--r--spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb47
-rw-r--r--spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/timelogs/create_service_shared_examples.rb47
-rw-r--r--spec/support/shared_examples/services/users/build_service_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/services/wiki_pages/create_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/wiki_pages/update_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/work_items/widgets/milestone_service_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/work_item_base_types_importer.rb6
-rw-r--r--spec/support/shared_examples/work_item_hierarchy_restrictions_importer.rb59
-rw-r--r--spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb203
-rw-r--r--spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb227
-rw-r--r--spec/support/shared_examples/workers/schedule_bulk_repository_shard_moves_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/workers/update_repository_move_shared_examples.rb4
-rw-r--r--spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb2
-rw-r--r--spec/tasks/gitlab/db/lock_writes_rake_spec.rb46
-rw-r--r--spec/tasks/gitlab/db_rake_spec.rb10
-rw-r--r--spec/tasks/gitlab/feature_categories_rake_spec.rb51
-rw-r--r--spec/tasks/gitlab/lfs/migrate_rake_spec.rb4
-rw-r--r--spec/tasks/gitlab/refresh_project_statistics_build_artifacts_size_rake_spec.rb2
-rw-r--r--spec/tasks/gitlab/shell_rake_spec.rb19
-rw-r--r--spec/tasks/gitlab/update_templates_rake_spec.rb8
-rw-r--r--spec/tasks/gitlab/usage_data_rake_spec.rb11
-rw-r--r--spec/tooling/danger/feature_flag_spec.rb2
-rw-r--r--spec/tooling/danger/product_intelligence_spec.rb74
-rw-r--r--spec/tooling/danger/project_helper_spec.rb2
-rw-r--r--spec/tooling/danger/specs_spec.rb66
-rw-r--r--spec/tooling/danger/stable_branch_spec.rb169
-rw-r--r--spec/tooling/danger/user_types_spec.rb56
-rw-r--r--spec/tooling/docs/deprecation_handling_spec.rb4
-rw-r--r--spec/tooling/fixtures/metrics/sample_instrumentation_metric.rb15
-rw-r--r--spec/tooling/quality/test_level_spec.rb4
-rw-r--r--spec/uploaders/ci/secure_file_uploader_spec.rb6
-rw-r--r--spec/uploaders/external_diff_uploader_spec.rb25
-rw-r--r--spec/uploaders/file_mover_spec.rb6
-rw-r--r--spec/uploaders/gitlab_uploader_spec.rb15
-rw-r--r--spec/uploaders/lfs_object_uploader_spec.rb24
-rw-r--r--spec/uploaders/packages/composer/cache_uploader_spec.rb2
-rw-r--r--spec/uploaders/packages/debian/component_file_uploader_spec.rb2
-rw-r--r--spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb2
-rw-r--r--spec/uploaders/packages/package_file_uploader_spec.rb2
-rw-r--r--spec/uploaders/packages/rpm/repository_file_uploader_spec.rb2
-rw-r--r--spec/uploaders/personal_file_uploader_spec.rb8
-rw-r--r--spec/uploaders/terraform/state_uploader_spec.rb6
-rw-r--r--spec/uploaders/workers/object_storage/background_move_worker_spec.rb116
-rw-r--r--spec/validators/iso8601_date_validator_spec.rb32
-rw-r--r--spec/views/admin/application_settings/_ci_cd.html.haml_spec.rb8
-rw-r--r--spec/views/admin/application_settings/_repository_check.html.haml_spec.rb27
-rw-r--r--spec/views/admin/application_settings/general.html.haml_spec.rb12
-rw-r--r--spec/views/admin/dashboard/index.html.haml_spec.rb4
-rw-r--r--spec/views/help/index.html.haml_spec.rb2
-rw-r--r--spec/views/import/gitlab_projects/new.html.haml_spec.rb2
-rw-r--r--spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb40
-rw-r--r--spec/views/profiles/keys/_form.html.haml_spec.rb5
-rw-r--r--spec/views/profiles/keys/_key.html.haml_spec.rb20
-rw-r--r--spec/views/profiles/keys/_key_details.html.haml_spec.rb32
-rw-r--r--spec/views/projects/_files.html.haml_spec.rb73
-rw-r--r--spec/views/projects/_flash_messages.html.haml_spec.rb4
-rw-r--r--spec/views/projects/_home_panel.html.haml_spec.rb36
-rw-r--r--spec/views/projects/commit/show.html.haml_spec.rb42
-rw-r--r--spec/views/projects/issues/_related_branches.html.haml_spec.rb11
-rw-r--r--spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb6
-rw-r--r--spec/views/projects/pipelines/show.html.haml_spec.rb11
-rw-r--r--spec/views/projects/tree/show.html.haml_spec.rb2
-rw-r--r--spec/views/search/_results.html.haml_spec.rb66
-rw-r--r--spec/views/search/show.html.haml_spec.rb27
-rw-r--r--spec/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml_spec.rb20
-rw-r--r--spec/views/shared/ssh_keys/_key_delete.html.haml_spec.rb22
-rw-r--r--spec/views/shared/ssh_keys/_key_details.html.haml_spec.rb20
-rw-r--r--spec/workers/bulk_import_worker_spec.rb2
-rw-r--r--spec/workers/bulk_imports/entity_worker_spec.rb2
-rw-r--r--spec/workers/bulk_imports/export_request_worker_spec.rb90
-rw-r--r--spec/workers/bulk_imports/pipeline_worker_spec.rb236
-rw-r--r--spec/workers/ci/create_downstream_pipeline_worker_spec.rb43
-rw-r--r--spec/workers/ci/ref_delete_unlock_artifacts_worker_spec.rb27
-rw-r--r--spec/workers/ci/runners/process_runner_version_update_worker_spec.rb2
-rw-r--r--spec/workers/ci/runners/reconcile_existing_runner_versions_cron_worker_spec.rb2
-rw-r--r--spec/workers/concerns/gitlab/github_import/object_importer_spec.rb37
-rw-r--r--spec/workers/concerns/waitable_worker_spec.rb43
-rw-r--r--spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb3
-rw-r--r--spec/workers/container_registry/cleanup_worker_spec.rb14
-rw-r--r--spec/workers/container_registry/migration/guard_worker_spec.rb16
-rw-r--r--spec/workers/database/batched_background_migration/ci_execution_worker_spec.rb9
-rw-r--r--spec/workers/database/batched_background_migration/execution_worker_spec.rb141
-rw-r--r--spec/workers/database/batched_background_migration/main_execution_worker_spec.rb9
-rw-r--r--spec/workers/delete_container_repository_worker_spec.rb108
-rw-r--r--spec/workers/every_sidekiq_worker_spec.rb9
-rw-r--r--spec/workers/flush_counter_increments_worker_spec.rb21
-rw-r--r--spec/workers/gitlab/export/prune_project_export_jobs_worker_spec.rb52
-rw-r--r--spec/workers/gitlab/github_gists_import/finish_import_worker_spec.rb51
-rw-r--r--spec/workers/gitlab/github_gists_import/import_gist_worker_spec.rb94
-rw-r--r--spec/workers/gitlab/github_gists_import/start_import_worker_spec.rb110
-rw-r--r--spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb1
-rw-r--r--spec/workers/gitlab/jira_import/import_issue_worker_spec.rb3
-rw-r--r--spec/workers/gitlab_shell_worker_spec.rb26
-rw-r--r--spec/workers/incident_management/close_incident_worker_spec.rb6
-rw-r--r--spec/workers/incident_management/pager_duty/process_incident_worker_spec.rb8
-rw-r--r--spec/workers/issuable_export_csv_worker_spec.rb4
-rw-r--r--spec/workers/jira_connect/forward_event_worker_spec.rb6
-rw-r--r--spec/workers/jira_connect/send_uninstalled_hook_worker_spec.rb29
-rw-r--r--spec/workers/mail_scheduler/notification_service_worker_spec.rb30
-rw-r--r--spec/workers/merge_requests/delete_branch_worker_spec.rb65
-rw-r--r--spec/workers/merge_requests/delete_source_branch_worker_spec.rb23
-rw-r--r--spec/workers/metrics/dashboard/prune_old_annotations_worker_spec.rb13
-rw-r--r--spec/workers/metrics/dashboard/sync_dashboards_worker_spec.rb4
-rw-r--r--spec/workers/namespaces/process_sync_events_worker_spec.rb6
-rw-r--r--spec/workers/namespaces/root_statistics_worker_spec.rb1
-rw-r--r--spec/workers/namespaces/schedule_aggregation_worker_spec.rb4
-rw-r--r--spec/workers/packages/debian/process_changes_worker_spec.rb20
-rw-r--r--spec/workers/packages/debian/process_package_file_worker_spec.rb138
-rw-r--r--spec/workers/pipeline_schedule_worker_spec.rb26
-rw-r--r--spec/workers/post_receive_spec.rb39
-rw-r--r--spec/workers/process_commit_worker_spec.rb2
-rw-r--r--spec/workers/projects/delete_branch_worker_spec.rb112
-rw-r--r--spec/workers/projects/import_export/parallel_project_export_worker_spec.rb60
-rw-r--r--spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb4
-rw-r--r--spec/workers/projects/process_sync_events_worker_spec.rb4
-rw-r--r--spec/workers/releases/create_evidence_worker_spec.rb4
-rw-r--r--spec/workers/releases/manage_evidence_worker_spec.rb4
-rw-r--r--spec/workers/repository_check/single_repository_worker_spec.rb10
-rw-r--r--spec/workers/run_pipeline_schedule_worker_spec.rb10
-rw-r--r--spec/workers/tasks_to_be_done/create_worker_spec.rb4
-rw-r--r--spec/workers/update_highest_role_worker_spec.rb4
-rw-r--r--tests.yml12
-rw-r--r--tooling/config/CODEOWNERS.yml5
-rw-r--r--tooling/danger/product_intelligence.rb67
-rw-r--r--tooling/danger/project_helper.rb4
-rw-r--r--tooling/danger/specs.rb37
-rw-r--r--tooling/danger/stable_branch.rb138
-rw-r--r--tooling/danger/user_types.rb31
-rw-r--r--tooling/docs/deprecation_handling.rb2
-rw-r--r--tooling/lib/tooling/helm3_client.rb5
-rw-r--r--tooling/quality/test_level.rb1
-rw-r--r--vendor/gems/attr_encrypted/README.md2
-rw-r--r--vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb25
-rw-r--r--vendor/gems/kubeclient/.gitignore16
-rw-r--r--vendor/gems/kubeclient/CHANGELOG.md247
-rw-r--r--vendor/gems/kubeclient/Gemfile7
-rw-r--r--vendor/gems/kubeclient/LICENSE.txt22
-rw-r--r--vendor/gems/kubeclient/README.md889
-rw-r--r--vendor/gems/kubeclient/RELEASING.md69
-rw-r--r--vendor/gems/kubeclient/Rakefile9
-rw-r--r--vendor/gems/kubeclient/kubeclient.gemspec39
-rw-r--r--vendor/gems/kubeclient/lib/kubeclient.rb35
-rw-r--r--vendor/gems/kubeclient/lib/kubeclient/aws_eks_credentials.rb46
-rw-r--r--vendor/gems/kubeclient/lib/kubeclient/common.rb661
-rw-r--r--vendor/gems/kubeclient/lib/kubeclient/config.rb202
-rw-r--r--vendor/gems/kubeclient/lib/kubeclient/entity_list.rb21
-rw-r--r--vendor/gems/kubeclient/lib/kubeclient/exec_credentials.rb89
-rw-r--r--vendor/gems/kubeclient/lib/kubeclient/gcp_auth_provider.rb19
-rw-r--r--vendor/gems/kubeclient/lib/kubeclient/gcp_command_credentials.rb31
-rw-r--r--vendor/gems/kubeclient/lib/kubeclient/google_application_default_credentials.rb31
-rw-r--r--vendor/gems/kubeclient/lib/kubeclient/http_error.rb25
-rw-r--r--vendor/gems/kubeclient/lib/kubeclient/missing_kind_compatibility.rb68
-rw-r--r--vendor/gems/kubeclient/lib/kubeclient/oidc_auth_provider.rb52
-rw-r--r--vendor/gems/kubeclient/lib/kubeclient/resource.rb11
-rw-r--r--vendor/gems/kubeclient/lib/kubeclient/resource_not_found_error.rb4
-rw-r--r--vendor/gems/kubeclient/lib/kubeclient/version.rb4
-rw-r--r--vendor/gems/kubeclient/lib/kubeclient/watch_stream.rb97
-rw-r--r--vendor/gems/kubeclient/test/cassettes/kubernetes_guestbook.yml879
-rw-r--r--vendor/gems/kubeclient/test/config/allinone.kubeconfig21
-rw-r--r--vendor/gems/kubeclient/test/config/another-ca1.pem19
-rw-r--r--vendor/gems/kubeclient/test/config/another-ca2.pem19
-rw-r--r--vendor/gems/kubeclient/test/config/concatenated-ca.kubeconfig20
-rw-r--r--vendor/gems/kubeclient/test/config/concatenated-ca.pem57
-rw-r--r--vendor/gems/kubeclient/test/config/execauth.kubeconfig61
-rw-r--r--vendor/gems/kubeclient/test/config/external-ca.pem19
-rw-r--r--vendor/gems/kubeclient/test/config/external-cert.pem20
-rw-r--r--vendor/gems/kubeclient/test/config/external-key.rsa27
-rw-r--r--vendor/gems/kubeclient/test/config/external-without-ca.kubeconfig21
-rw-r--r--vendor/gems/kubeclient/test/config/external.kubeconfig20
-rw-r--r--vendor/gems/kubeclient/test/config/gcpauth.kubeconfig21
-rw-r--r--vendor/gems/kubeclient/test/config/gcpcmdauth.kubeconfig25
-rw-r--r--vendor/gems/kubeclient/test/config/insecure-custom-ca.kubeconfig22
-rw-r--r--vendor/gems/kubeclient/test/config/insecure.kubeconfig25
-rw-r--r--vendor/gems/kubeclient/test/config/nouser.kubeconfig15
-rw-r--r--vendor/gems/kubeclient/test/config/oidcauth.kubeconfig24
-rw-r--r--vendor/gems/kubeclient/test/config/secure-without-ca.kubeconfig22
-rw-r--r--vendor/gems/kubeclient/test/config/secure.kubeconfig21
-rw-r--r--vendor/gems/kubeclient/test/config/timestamps.kubeconfig25
-rwxr-xr-xvendor/gems/kubeclient/test/config/update_certs_k0s.rb53
-rw-r--r--vendor/gems/kubeclient/test/config/userauth.kubeconfig27
-rw-r--r--vendor/gems/kubeclient/test/json/bindings_list.json10
-rw-r--r--vendor/gems/kubeclient/test/json/component_status.json17
-rw-r--r--vendor/gems/kubeclient/test/json/component_status_list.json52
-rw-r--r--vendor/gems/kubeclient/test/json/config.istio.io_api_resource_list.json679
-rw-r--r--vendor/gems/kubeclient/test/json/config_map_list.json9
-rw-r--r--vendor/gems/kubeclient/test/json/core_api_resource_list.json181
-rw-r--r--vendor/gems/kubeclient/test/json/core_api_resource_list_without_kind.json129
-rw-r--r--vendor/gems/kubeclient/test/json/core_oapi_resource_list_without_kind.json197
-rw-r--r--vendor/gems/kubeclient/test/json/created_endpoint.json28
-rw-r--r--vendor/gems/kubeclient/test/json/created_namespace.json20
-rw-r--r--vendor/gems/kubeclient/test/json/created_secret.json16
-rw-r--r--vendor/gems/kubeclient/test/json/created_security_context_constraint.json65
-rw-r--r--vendor/gems/kubeclient/test/json/created_service.json31
-rw-r--r--vendor/gems/kubeclient/test/json/empty_pod_list.json9
-rw-r--r--vendor/gems/kubeclient/test/json/endpoint_list.json48
-rw-r--r--vendor/gems/kubeclient/test/json/entity_list.json56
-rw-r--r--vendor/gems/kubeclient/test/json/event_list.json35
-rw-r--r--vendor/gems/kubeclient/test/json/extensions_v1beta1_api_resource_list.json217
-rw-r--r--vendor/gems/kubeclient/test/json/limit_range.json23
-rw-r--r--vendor/gems/kubeclient/test/json/limit_range_list.json31
-rw-r--r--vendor/gems/kubeclient/test/json/namespace.json13
-rw-r--r--vendor/gems/kubeclient/test/json/namespace_exception.json8
-rw-r--r--vendor/gems/kubeclient/test/json/namespace_list.json32
-rw-r--r--vendor/gems/kubeclient/test/json/node.json29
-rw-r--r--vendor/gems/kubeclient/test/json/node_list.json37
-rw-r--r--vendor/gems/kubeclient/test/json/node_notice.json160
-rw-r--r--vendor/gems/kubeclient/test/json/persistent_volume.json37
-rw-r--r--vendor/gems/kubeclient/test/json/persistent_volume_claim.json32
-rw-r--r--vendor/gems/kubeclient/test/json/persistent_volume_claim_list.json40
-rw-r--r--vendor/gems/kubeclient/test/json/persistent_volume_claims_nil_items.json8
-rw-r--r--vendor/gems/kubeclient/test/json/persistent_volume_list.json45
-rw-r--r--vendor/gems/kubeclient/test/json/pod.json92
-rw-r--r--vendor/gems/kubeclient/test/json/pod_list.json79
-rw-r--r--vendor/gems/kubeclient/test/json/pod_template_list.json9
-rw-r--r--vendor/gems/kubeclient/test/json/pods_1.json265
-rw-r--r--vendor/gems/kubeclient/test/json/pods_2.json102
-rw-r--r--vendor/gems/kubeclient/test/json/pods_410.json9
-rw-r--r--vendor/gems/kubeclient/test/json/processed_template.json27
-rw-r--r--vendor/gems/kubeclient/test/json/replication_controller.json57
-rw-r--r--vendor/gems/kubeclient/test/json/replication_controller_list.json66
-rw-r--r--vendor/gems/kubeclient/test/json/resource_quota.json46
-rw-r--r--vendor/gems/kubeclient/test/json/resource_quota_list.json54
-rw-r--r--vendor/gems/kubeclient/test/json/secret_list.json44
-rw-r--r--vendor/gems/kubeclient/test/json/security.openshift.io_api_resource_list.json69
-rw-r--r--vendor/gems/kubeclient/test/json/security_context_constraint_list.json375
-rw-r--r--vendor/gems/kubeclient/test/json/service.json33
-rw-r--r--vendor/gems/kubeclient/test/json/service_account.json25
-rw-r--r--vendor/gems/kubeclient/test/json/service_account_list.json82
-rw-r--r--vendor/gems/kubeclient/test/json/service_illegal_json_404.json1
-rw-r--r--vendor/gems/kubeclient/test/json/service_json_patch.json26
-rw-r--r--vendor/gems/kubeclient/test/json/service_list.json97
-rw-r--r--vendor/gems/kubeclient/test/json/service_merge_patch.json26
-rw-r--r--vendor/gems/kubeclient/test/json/service_patch.json25
-rw-r--r--vendor/gems/kubeclient/test/json/service_update.json22
-rw-r--r--vendor/gems/kubeclient/test/json/template.json27
-rw-r--r--vendor/gems/kubeclient/test/json/template.openshift.io_api_resource_list.json75
-rw-r--r--vendor/gems/kubeclient/test/json/template_list.json35
-rw-r--r--vendor/gems/kubeclient/test/json/versions_list.json6
-rw-r--r--vendor/gems/kubeclient/test/json/watch_stream.json3
-rw-r--r--vendor/gems/kubeclient/test/test_common.rb95
-rw-r--r--vendor/gems/kubeclient/test/test_common_url_handling.rb160
-rw-r--r--vendor/gems/kubeclient/test/test_component_status.rb29
-rw-r--r--vendor/gems/kubeclient/test/test_config.rb271
-rw-r--r--vendor/gems/kubeclient/test/test_endpoint.rb54
-rw-r--r--vendor/gems/kubeclient/test/test_exec_credentials.rb225
-rw-r--r--vendor/gems/kubeclient/test/test_gcp_command_credentials.rb27
-rw-r--r--vendor/gems/kubeclient/test/test_google_application_default_credentials.rb15
-rw-r--r--vendor/gems/kubeclient/test/test_guestbook_go.rb237
-rw-r--r--vendor/gems/kubeclient/test/test_helper.rb28
-rw-r--r--vendor/gems/kubeclient/test/test_kubeclient.rb881
-rw-r--r--vendor/gems/kubeclient/test/test_limit_range.rb25
-rw-r--r--vendor/gems/kubeclient/test/test_missing_methods.rb80
-rw-r--r--vendor/gems/kubeclient/test/test_namespace.rb59
-rw-r--r--vendor/gems/kubeclient/test/test_node.rb70
-rw-r--r--vendor/gems/kubeclient/test/test_oidc_auth_provider.rb103
-rw-r--r--vendor/gems/kubeclient/test/test_persistent_volume.rb29
-rw-r--r--vendor/gems/kubeclient/test/test_persistent_volume_claim.rb28
-rw-r--r--vendor/gems/kubeclient/test/test_pod.rb81
-rw-r--r--vendor/gems/kubeclient/test/test_pod_log.rb157
-rw-r--r--vendor/gems/kubeclient/test/test_process_template.rb80
-rw-r--r--vendor/gems/kubeclient/test/test_real_cluster.rb162
-rw-r--r--vendor/gems/kubeclient/test/test_replication_controller.rb47
-rw-r--r--vendor/gems/kubeclient/test/test_resource_list_without_kind.rb78
-rw-r--r--vendor/gems/kubeclient/test/test_resource_quota.rb23
-rw-r--r--vendor/gems/kubeclient/test/test_secret.rb62
-rw-r--r--vendor/gems/kubeclient/test/test_security_context_constraint.rb62
-rw-r--r--vendor/gems/kubeclient/test/test_service.rb357
-rw-r--r--vendor/gems/kubeclient/test/test_service_account.rb26
-rw-r--r--vendor/gems/kubeclient/test/test_watch.rb195
-rw-r--r--vendor/gems/kubeclient/test/txt/pod_log.txt6
-rw-r--r--vendor/gems/kubeclient/test/valid_token_file1
-rw-r--r--vendor/project_templates/bridgetown.tar.gzbin0 -> 42296 bytes
-rw-r--r--vendor/project_templates/dotnetcore.tar.gzbin4923 -> 9775 bytes
-rw-r--r--vendor/project_templates/middleman.tar.gzbin10935 -> 9631 bytes
-rw-r--r--vendor/project_templates/rails.tar.gzbin150831 -> 149743 bytes
-rw-r--r--vendor/project_templates/typo3_distribution.tar.gzbin0 -> 71800 bytes
-rw-r--r--workhorse/gitaly_integration_test.go1
-rw-r--r--workhorse/gitaly_test.go2
-rw-r--r--workhorse/go.mod20
-rw-r--r--workhorse/go.sum37
-rw-r--r--workhorse/internal/api/api.go18
-rw-r--r--workhorse/internal/api/block.go3
-rw-r--r--workhorse/internal/api/block_test.go2
-rw-r--r--workhorse/internal/api/channel_settings.go7
-rw-r--r--workhorse/internal/artifacts/entry.go13
-rw-r--r--workhorse/internal/builds/register.go31
-rw-r--r--workhorse/internal/builds/register_test.go47
-rw-r--r--workhorse/internal/channel/channel.go26
-rw-r--r--workhorse/internal/channel/channel_test.go49
-rw-r--r--workhorse/internal/dependencyproxy/dependencyproxy.go10
-rw-r--r--workhorse/internal/dependencyproxy/dependencyproxy_test.go4
-rw-r--r--workhorse/internal/git/archive.go16
-rw-r--r--workhorse/internal/git/blob.go14
-rw-r--r--workhorse/internal/git/diff.go14
-rw-r--r--workhorse/internal/git/format-patch.go14
-rw-r--r--workhorse/internal/git/info-refs.go15
-rw-r--r--workhorse/internal/git/io.go178
-rw-r--r--workhorse/internal/git/io_test.go191
-rw-r--r--workhorse/internal/git/receive-pack.go11
-rw-r--r--workhorse/internal/git/snapshot.go16
-rw-r--r--workhorse/internal/git/upload-pack.go13
-rw-r--r--workhorse/internal/gitaly/gitaly.go61
-rw-r--r--workhorse/internal/gitaly/gitaly_test.go76
-rw-r--r--workhorse/internal/helper/command/command.go30
-rw-r--r--workhorse/internal/helper/context_reader.go40
-rw-r--r--workhorse/internal/helper/context_reader_test.go83
-rw-r--r--workhorse/internal/helper/exception/exception.go58
-rw-r--r--workhorse/internal/helper/fail/fail.go45
-rw-r--r--workhorse/internal/helper/fail/fail_test.go21
-rw-r--r--workhorse/internal/helper/helpers.go160
-rw-r--r--workhorse/internal/helper/helpers_test.go142
-rw-r--r--workhorse/internal/helper/nginx/nginx.go13
-rw-r--r--workhorse/internal/helper/raven.go58
-rw-r--r--workhorse/internal/helper/tempfile.go34
-rw-r--r--workhorse/internal/helper/writeafterreader.go143
-rw-r--r--workhorse/internal/helper/writeafterreader_test.go114
-rw-r--r--workhorse/internal/imageresizer/image_resizer.go7
-rw-r--r--workhorse/internal/log/logging.go16
-rw-r--r--workhorse/internal/proxy/proxy.go38
-rw-r--r--workhorse/internal/queueing/requests.go4
-rw-r--r--workhorse/internal/senddata/senddata.go3
-rw-r--r--workhorse/internal/sendfile/sendfile.go6
-rw-r--r--workhorse/internal/sendurl/sendurl.go10
-rw-r--r--workhorse/internal/staticpages/deploy_page.go4
-rw-r--r--workhorse/internal/staticpages/error_pages.go4
-rw-r--r--workhorse/internal/staticpages/static.go8
-rw-r--r--workhorse/internal/upload/artifacts_uploader.go6
-rw-r--r--workhorse/internal/upload/body_uploader.go10
-rw-r--r--workhorse/internal/upload/rewrite.go7
-rw-r--r--workhorse/internal/upload/uploads.go26
-rw-r--r--workhorse/internal/upload/uploads_test.go12
-rw-r--r--workhorse/internal/upstream/handlers.go4
-rw-r--r--workhorse/internal/upstream/routes.go2
-rw-r--r--workhorse/internal/upstream/upstream.go32
-rw-r--r--workhorse/internal/upstream/upstream_test.go30
-rw-r--r--workhorse/main_test.go9
-rw-r--r--workhorse/raven.go4
-rw-r--r--yarn.lock1950
8830 files changed, 242074 insertions, 114639 deletions
diff --git a/.dockerignore b/.dockerignore
index e145f368cb1..0782627230a 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -49,7 +49,6 @@
/lib/registry/
/lib/policy/
/lib/feature/
-/lib/flowdock/
/lib/generators/
/lib/gitaly/
/lib/api/
diff --git a/.eslintrc.yml b/.eslintrc.yml
index f814bdc6434..4a7197e3bd5 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -75,6 +75,8 @@ rules:
- sibling
- index
pathGroups:
+ - pattern: '@sentry/browser'
+ group: external
- pattern: ~/**
group: internal
- pattern: emojis/**
@@ -113,6 +115,8 @@ rules:
- error
- selector: ImportSpecifier[imported.name='GlSkeletonLoading']
message: 'Migrate to GlSkeletonLoader, or import GlDeprecatedSkeletonLoading.'
+ - selector: ImportSpecifier[imported.name='GlSafeHtmlDirective']
+ message: 'Use directive at ~/vue_shared/directives/safe_html.js instead.'
# See https://gitlab.com/gitlab-org/gitlab/-/issues/360551
vue/multi-word-component-names: off
unicorn/prefer-dom-node-dataset:
@@ -189,6 +193,6 @@ overrides:
'@graphql-eslint/no-unused-fragments': error
'@graphql-eslint/no-duplicate-fields': error
- files:
- - 'spec/contracts/consumer/**/*'
+ - '{,ee/}spec/contracts/consumer/**/*'
rules:
'@gitlab/require-i18n-strings': off
diff --git a/.gitignore b/.gitignore
index 234593b944e..ad7595dc7f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -78,6 +78,7 @@ eslint-report.html
/test_results/
/deprecations/
/knapsack/
+/query_recorder/
/rspec_flaky/
/rspec/
/locale/**/LC_MESSAGES
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index edbbe90a774..2062953c9ba 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -25,6 +25,7 @@ default:
timeout: 90m
workflow:
+ name: '$PIPELINE_NAME'
rules:
# If `$FORCE_GITLAB_CI` is set, create a pipeline.
- if: '$FORCE_GITLAB_CI'
@@ -36,21 +37,21 @@ workflow:
- if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^release-tools\/\d+\.\d+\.\d+-rc\d+$/ && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ /^[\d-]+-stable(-ee)?$/ && $CI_PROJECT_PATH == "gitlab-org/gitlab"'
when: never
# For merge requests running exclusively in Ruby 3.0
- - if: '($CI_MERGE_REQUEST_EVENT_TYPE == "merged_result" || $CI_MERGE_REQUEST_EVENT_TYPE == "merge_train") && $CI_MERGE_REQUEST_LABELS =~ /pipeline:run-in-ruby3/'
- variables:
- RUBY_VERSION: "3.0"
- # For merge requests running exclusively in Ruby 3.0
- if: '$CI_MERGE_REQUEST_LABELS =~ /pipeline:run-in-ruby3/'
variables:
RUBY_VERSION: "3.0"
+ PIPELINE_NAME: 'Ruby 3 $CI_MERGE_REQUEST_EVENT_TYPE MR pipeline'
# For (detached) merge request pipelines.
- if: '$CI_MERGE_REQUEST_IID'
+ variables:
+ PIPELINE_NAME: '$CI_MERGE_REQUEST_EVENT_TYPE MR pipeline'
# For the scheduled pipelines, we set specific variables.
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "schedule"'
variables:
CRYSTALBALL: "true"
CREATE_INCIDENT_FOR_PIPELINE_FAILURE: "true"
NOTIFY_PIPELINE_FAILURE_CHANNEL: "master-broken"
+ PIPELINE_NAME: 'Scheduled $CI_COMMIT_BRANCH pipeline'
# Run pipelines for ruby3 branch
- if: '$CI_COMMIT_BRANCH == "ruby3" && $CI_PIPELINE_SOURCE == "schedule"'
variables:
@@ -58,6 +59,7 @@ workflow:
NOTIFY_PIPELINE_FAILURE_CHANNEL: "f_ruby3"
OMNIBUS_GITLAB_RUBY3_BUILD: "true"
OMNIBUS_GITLAB_CACHE_EDITION: "GITLAB_RUBY3"
+ PIPELINE_NAME: 'Scheduled ruby 3 pipeline'
# This work around https://gitlab.com/gitlab-org/gitlab/-/issues/332411 whichs prevents usage of dependency proxy
# when pipeline is triggered by a project access token.
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $GITLAB_USER_LOGIN =~ /project_\d+_bot\d*/'
@@ -145,6 +147,10 @@ variables:
REGISTRY_HOST: "registry.gitlab.com"
REGISTRY_GROUP: "gitlab-org"
+ # Disable useless network connections when installing some NPM packages.
+ # See https://gitlab.com/gitlab-com/gl-security/engineering-and-research/inventory/-/issues/827#note_1203181407
+ DISABLE_OPENCOLLECTIVE: "true"
+
include:
- local: .gitlab/ci/*.gitlab-ci.yml
- remote: 'https://gitlab.com/gitlab-org/frontend/untamper-my-lockfile/-/raw/main/templates/merge_request_pipelines.yml'
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index 2bb47c77ba5..b3efa7c61e4 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -18,7 +18,7 @@ GITALY_SERVER_VERSION @project_278964_bot6 @gitlab-org/maintainers/rails-backend
/doc/**/*.md
/doc/**/*.png
/data/deprecations/*.yml
-/data/removals/*.yml
+/data/removals/**/*.yml
## Technical writing files that do need approval
/data/deprecations/templates/ @marcel.amirault @gitlab-org/tw-leadership @sarahgerman
@@ -108,6 +108,9 @@ Dangerfile @gl-quality/eng-prod
/ee/app/models/project_alias.rb @patrickbajao
/ee/lib/api/project_aliases.rb @patrickbajao
+^[Distribution]
+/lib/support/ @gitlab-org/distribution
+
# Secure & Threat Management ownership delineation
# https://about.gitlab.com/handbook/engineering/development/threat-management/delineate-secure-threat-management.html#technical-boundaries
^[Threat Insights]
@@ -339,11 +342,30 @@ Dangerfile @gl-quality/eng-prod
/spec/models/onboarding/ @gitlab-org/growth/engineers
/app/services/onboarding/ @gitlab-org/growth/engineers
/spec/services/onboarding/ @gitlab-org/growth/engineers
-/ee/app/controllers/registrations/ @gitlab-org/growth/engineers
/ee/app/components/namespaces/free_user_cap/ @gitlab-org/growth/engineers
/ee/spec/components/namespaces/free_user_cap/ @gitlab-org/growth/engineers
/ee/app/models/namespaces/free_user_cap/ @gitlab-org/growth/engineers
/ee/spec/models/namespaces/free_user_cap/ @gitlab-org/growth/engineers
+/app/controllers/registrations_controller.rb @gitlab-org/growth/engineers
+/spec/controllers/registrations_controller_spec.rb @gitlab-org/growth/engineers
+/app/controllers/registrations/ @gitlab-org/growth/engineers
+/spec/controllers/registrations/ @gitlab-org/growth/engineers
+/app/controllers/confirmations_controller.rb @gitlab-org/growth/engineers
+/spec/controllers/confirmations_controller_spec.rb @gitlab-org/growth/engineers
+/ee/app/controllers/trial_registrations_controller.rb @gitlab-org/growth/engineers
+/ee/spec/controllers/trial_registrations_controller_spec.rb @gitlab-org/growth/engineers
+/ee/spec/requests/trial_registrations_controller_spec.rb @gitlab-org/growth/engineers
+/ee/app/controllers/registrations/ @gitlab-org/growth/engineers
+/ee/spec/controllers/registrations/ @gitlab-org/growth/engineers
+/ee/spec/requests/registrations/ @gitlab-org/growth/engineers
+/ee/app/controllers/ee/registrations_controller.rb @gitlab-org/growth/engineers
+/ee/spec/controllers/ee/registrations_controller_spec.rb @gitlab-org/growth/engineers
+/ee/app/controllers/ee/registrations/ @gitlab-org/growth/engineers
+/ee/app/controllers/ee/confirmations_controller.rb @gitlab-org/growth/engineers
+/ee/app/controllers/subscriptions_controller.rb @gitlab-org/growth/engineers
+/ee/spec/controllers/subscriptions_controller_spec.rb @gitlab-org/growth/engineers
+/ee/app/controllers/subscriptions/ @gitlab-org/growth/engineers
+/ee/spec/controllers/subscriptions/ @gitlab-org/growth/engineers
/app/services/users/in_product_marketing_email_records.rb @gitlab-org/growth/engineers
/spec/services/users/in_product_marketing_email_records_spec.rb @gitlab-org/growth/engineers
/app/workers/namespaces/in_product_marketing_emails_worker.rb @gitlab-org/growth/engineers
@@ -922,6 +944,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/free_user_limit.md @phillipwells
/doc/user/group/ @lciutacu
/doc/user/group/clusters/ @phillipwells
+/doc/user/group/compliance_frameworks.md @eread
/doc/user/group/contribution_analytics/ @lciutacu
/doc/user/group/custom_project_templates.md @eread
/doc/user/group/devops_adoption/ @lciutacu
@@ -931,6 +954,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/group/issues_analytics/ @msedlakjakubowski
/doc/user/group/iterations/ @msedlakjakubowski
/doc/user/group/planning_hierarchy/ @msedlakjakubowski
+/doc/user/group/reporting/ @phillipwells
/doc/user/group/repositories_analytics/ @marcel.amirault
/doc/user/group/roadmap/ @msedlakjakubowski
/doc/user/group/saml_sso/ @jglassman1
@@ -1017,6 +1041,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/project/requirements/ @msedlakjakubowski
/doc/user/project/service_desk.md @msedlakjakubowski
/doc/user/project/settings/import_export.md @eread
+/doc/user/project/settings/import_export_troubleshooting.md @eread
/doc/user/project/settings/index.md @lciutacu
/doc/user/project/settings/project_access_tokens.md @jglassman1
/doc/user/project/time_tracking.md @msedlakjakubowski
@@ -1026,7 +1051,6 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/public_access.md @lciutacu
/doc/user/reserved_names.md @lciutacu
/doc/user/search/ @ashrafkhamis
-/doc/user/search/global_search/ @ashrafkhamis
/doc/user/shortcuts.md @ashrafkhamis
/doc/user/snippets.md @ashrafkhamis
/doc/user/ssh.md @jglassman1
@@ -1041,7 +1065,6 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql @gitlab-org/manage/authentication-and-authorization/approvers
/app/assets/javascripts/authentication/ @gitlab-org/manage/authentication-and-authorization/approvers
/app/assets/javascripts/ide/components/shared/tokened_input.vue @gitlab-org/manage/authentication-and-authorization/approvers
-/app/assets/javascripts/invite_members/components/members_token_select.vue @gitlab-org/manage/authentication-and-authorization/approvers
/app/assets/javascripts/packages_and_registries/package_registry/components/list/tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
/app/assets/javascripts/pages/admin/impersonation_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
/app/assets/javascripts/pages/groups/settings/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
@@ -1117,7 +1140,6 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/app/services/users/email_verification/validate_token_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
/app/services/users/refresh_authorized_projects_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
/app/services/webauthn/ @gitlab-org/manage/authentication-and-authorization/approvers
-/app/validators/json_schemas/build_metadata_id_tokens.json @gitlab-org/manage/authentication-and-authorization/approvers
/app/validators/json_schemas/cluster_agent_authorization_configuration.json @gitlab-org/manage/authentication-and-authorization/approvers
/app/views/admin/application_settings/_external_authorization_service_form.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
/app/views/admin/impersonation_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
@@ -1211,6 +1233,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/ee/app/services/ee/auth/ @gitlab-org/manage/authentication-and-authorization/approvers
/ee/app/services/ee/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
/ee/app/services/ee/resource_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/services/ee/users/authorized_build_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
/ee/app/services/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
/ee/app/services/security/token_revocation_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
/ee/app/validators/password/ @gitlab-org/manage/authentication-and-authorization/approvers
@@ -1297,7 +1320,6 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/app/controllers/projects/runner_projects_controller.rb @gitlab-org/maintainers/cicd-verify
/app/controllers/projects/runners_controller.rb @gitlab-org/maintainers/cicd-verify
/app/controllers/projects/triggers_controller.rb @gitlab-org/maintainers/cicd-verify
-/app/controllers/projects/usage_quotas_controller.rb @gitlab-org/maintainers/cicd-verify
/app/controllers/projects/variables_controller.rb @gitlab-org/maintainers/cicd-verify
/app/models/commit_status.rb @gitlab-org/maintainers/cicd-verify
/app/models/external_pull_request.rb @gitlab-org/maintainers/cicd-verify
@@ -1306,6 +1328,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/app/models/project_ci_cd_setting.rb @gitlab-org/maintainers/cicd-verify
/app/presenters/commit_status_presenter.rb @gitlab-org/maintainers/cicd-verify
/app/presenters/generic_commit_status_presenter.rb @gitlab-org/maintainers/cicd-verify
+/app/validators/json_schemas/build_metadata_id_tokens.json @gitlab-org/maintainers/cicd-verify
/app/views/projects/artifacts/ @gitlab-org/maintainers/cicd-verify
/app/views/projects/generic_commit_statuses/ @gitlab-org/maintainers/cicd-verify
/app/views/projects/jobs/ @gitlab-org/maintainers/cicd-verify
@@ -1347,17 +1370,64 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/ee/app/workers/clear_shared_runners_minutes_worker.rb @gitlab-org/maintainers/cicd-verify
/ee/lib/**/ci/ @gitlab-org/maintainers/cicd-verify
/ee/lib/ee/api/entities/merge_train.rb @gitlab-org/maintainers/cicd-verify
+/spec/**/ci @gitlab-org/maintainers/cicd-verify
+/spec/controllers/admin/jobs_controller_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/controllers/admin/runner_projects_controller_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/controllers/admin/runners_controller_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/controllers/projects/artifacts_controller_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/controllers/projects/jobs_controller_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/requests/runner_setup_controller_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/controllers/projects/pipeline_schedules_controller_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/requests/projects/pipelines_controller_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/controllers/projects/pipelines_settings_controller_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/controllers/projects/runners_controller_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/controllers/groups/variables_controller_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/models/commit_status_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/models/external_pull_request_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/models/generic_commit_status_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/models/namespace_ci_cd_setting_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/models/project_ci_cd_setting_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/presenters/commit_status_presenter_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/views/projects/jobs/ @gitlab-org/maintainers/cicd-verify
+/spec/views/projects/pipeline_schedules/ @gitlab-org/maintainers/cicd-verify
+/spec/views/projects/pipelines/ @gitlab-org/maintainers/cicd-verify
+/spec/workers/build_hooks_worker_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/workers/build_queue_worker_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/workers/build_success_worker_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/workers/ci_platform_metrics_update_cron_worker_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/workers/create_pipeline_worker_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/workers/expire_build_artifacts_worker_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/workers/pipeline_hooks_worker_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/workers/pipeline_metrics_worker_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/workers/pipeline_notification_worker_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/workers/pipeline_process_worker_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/workers/pipeline_schedule_worker_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/workers/run_pipeline_schedule_worker_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/workers/stuck_ci_jobs_worker_spec.rb @gitlab-org/maintainers/cicd-verify
+/spec/workers/update_external_pull_requests_worker_spec.rb @gitlab-org/maintainers/cicd-verify
+/ee/spec/**/ci @gitlab-org/maintainers/cicd-verify
+/ee/spec/**/merge_trains @gitlab-org/maintainers/cicd-verify
+/ee/spec/models/merge_train_spec.rb @gitlab-org/maintainers/cicd-verify
+/ee/spec/finders/merge_trains_finder_spec.rb @gitlab-org/maintainers/cicd-verify
+/ee/spec/services/auto_merge/add_to_merge_train_when_pipeline_succeeds_service_spec.rb @gitlab-org/maintainers/cicd-verify
+/ee/spec/services/auto_merge/merge_train_service_spec.rb @gitlab-org/maintainers/cicd-verify
+/ee/spec/services/system_notes/merge_train_service_spec.rb @gitlab-org/maintainers/cicd-verify
+/ee/spec/controllers/projects/subscriptions_controller_spec.rb @gitlab-org/maintainers/cicd-verify
+/ee/spec/helpers/ee/projects/pipeline_helper_spec.rb @gitlab-org/maintainers/cicd-verify
+/ee/spec/views/projects/pipelines/ @gitlab-org/maintainers/cicd-verify
+/ee/spec/views/projects/settings/ci_cd/ @gitlab-org/maintainers/cicd-verify
+/ee/spec/workers/clear_shared_runners_minutes_worker_spec.rb @gitlab-org/maintainers/cicd-verify
+/ee/spec/lib/**/ci/ @gitlab-org/maintainers/cicd-verify
+/ee/spec/lib/ee/api/entities/merge_train_spec.rb @gitlab-org/maintainers/cicd-verify
/**/javascripts/jobs/ @gitlab-org/ci-cd/verify/frontend
/**/javascripts/pipelines/ @gitlab-org/ci-cd/verify/frontend
/app/assets/javascripts/ci/ @gitlab-org/ci-cd/verify/frontend
/app/assets/javascripts/pipeline_new/ @gitlab-org/ci-cd/verify/frontend
-/app/assets/javascripts/ci_lint/ @gitlab-org/ci-cd/verify/frontend
/app/assets/javascripts/ci_variable_list/ @gitlab-org/ci-cd/verify/frontend
/app/assets/javascripts/ci/pipeline_schedules/ @gitlab-org/ci-cd/verify/frontend
-/app/assets/javascripts/pipeline_editor/ @gitlab-org/ci-cd/verify/frontend
/ee/app/assets/javascripts/ci/ @gitlab-org/ci-cd/verify/frontend
-/ee/app/assets/javascripts/reports/ @gitlab-org/ci-cd/verify/frontend
/app/assets/javascripts/token_access/ @gitlab-org/ci-cd/verify/frontend
+/app/assets/javascripts/admin/application_settings/runner_token_expiration/ @gitlab-org/ci-cd/verify/frontend
[Manage::Workspace]
lib/api/entities/basic_project_details.rb @gitlab-org/manage/manage-workspace/backend-approvers
diff --git a/.gitlab/ci/build-images.gitlab-ci.yml b/.gitlab/ci/build-images.gitlab-ci.yml
index a60a5f6040c..09ffc87f087 100644
--- a/.gitlab/ci/build-images.gitlab-ci.yml
+++ b/.gitlab/ci/build-images.gitlab-ci.yml
@@ -23,9 +23,6 @@ build-qa-image:
script:
- run_timed_command "scripts/build_qa_image"
-# This image is used by:
-# - The `CNG` pipelines (via the `review-build-cng` job): https://gitlab.com/gitlab-org/build/CNG/-/blob/cfc67136d711e1c8c409bf8e57427a644393da2f/.gitlab-ci.yml#L335
-# - The `omnibus-gitlab` pipelines (via the `e2e:package-and-test` job): https://gitlab.com/gitlab-org/omnibus-gitlab/-/blob/dfd1ad475868fc84e91ab7b5706aa03e46dc3a86/.gitlab-ci.yml#L130
build-assets-image:
extends:
- .base-image-build
@@ -33,7 +30,11 @@ build-assets-image:
stage: build-images
needs: ["compile-production-assets"]
script:
- # TODO: Change the image tag to be the MD5 of assets files and skip image building if the image exists
- # We'll also need to pass GITLAB_ASSETS_TAG to the trigerred omnibus-gitlab pipeline similarly to how we do it for trigerred CNG pipelines
- # https://gitlab.com/gitlab-org/gitlab/issues/208389
+ - skopeo login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- run_timed_command "scripts/build_assets_image"
+ artifacts:
+ expire_in: 7 days
+ paths:
+ # The `cached-assets-hash.txt` file is used in `review-build-cng-env` (`.gitlab/ci/review-apps/main.gitlab-ci.yml`)
+ # to pass the assets image tag to the CNG downstream pipeline.
+ - cached-assets-hash.txt
diff --git a/.gitlab/ci/docs.gitlab-ci.yml b/.gitlab/ci/docs.gitlab-ci.yml
index c6d2b30046c..18954e7b8e0 100644
--- a/.gitlab/ci/docs.gitlab-ci.yml
+++ b/.gitlab/ci/docs.gitlab-ci.yml
@@ -76,14 +76,16 @@ docs-code-quality:
- .docs-markdown-lint-image
stage: lint
needs: []
+ allow_failure: true
script:
- - vale --output=doc/.vale/vale-json.tmpl --minAlertLevel warning doc > gl-code-quality-report-docs.json || exit_code=$?
+ - scripts/lint-doc-quality.sh
artifacts:
reports:
codequality: gl-code-quality-report-docs.json
paths:
- gl-code-quality-report-docs.json
expire_in: 1 week
+ when: always
ui-docs-links lint:
extends:
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index 6be77fe52c8..cde023c149a 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -28,6 +28,7 @@
fi
fi
- assets_compile_script
+ - echo -n "${GITLAB_ASSETS_HASH}" > "cached-assets-hash.txt"
compile-production-assets:
extends:
@@ -43,6 +44,7 @@ compile-production-assets:
# These assets are used in multiple locations:
# - in `build-assets-image` job to create assets image for packaging systems
# - GitLab UI for integration tests: https://gitlab.com/gitlab-org/gitlab-ui/-/blob/e88493b3c855aea30bf60baee692a64606b0eb1e/.storybook/preview-head.pug#L1
+ - cached-assets-hash.txt
- public/assets/
- "${WEBPACK_COMPILE_LOG_PATH}"
when: always
@@ -73,9 +75,6 @@ update-assets-compile-production-cache:
- .assets-compile-cache-push
- .shared:rules:update-cache
stage: prepare
- script:
- - !reference [compile-production-assets, script]
- - echo -n "${GITLAB_ASSETS_HASH}" > "cached-assets-hash.txt"
artifacts: {} # This job's purpose is only to update the cache.
update-assets-compile-test-cache:
@@ -274,32 +273,6 @@ coverage-frontend:
coverage_format: cobertura
path: coverage-frontend/cobertura-coverage.xml
-.qa-frontend-node:
- extends:
- - .default-retry
- - .default-utils-before_script
- - .qa-frontend-node-cache
- - .frontend:rules:qa-frontend-node
- stage: test
- needs: []
- script:
- - yarn_install_script
- - run_timed_command "retry yarn run webpack-prod"
-
-qa-frontend-node:14:
- extends: .qa-frontend-node
- image: ${GITLAB_DEPENDENCY_PROXY_ADDRESS}node:14
-
-qa-frontend-node:16:
- extends: .qa-frontend-node
- image: ${GITLAB_DEPENDENCY_PROXY_ADDRESS}node:16
-
-qa-frontend-node:latest:
- extends:
- - .qa-frontend-node
- - .frontend:rules:qa-frontend-node-latest
- image: ${GITLAB_DEPENDENCY_PROXY_ADDRESS}node:latest
-
webpack-dev-server:
extends:
- .default-retry
diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml
index add728a9983..5e35ae6aff3 100644
--- a/.gitlab/ci/global.gitlab-ci.yml
+++ b/.gitlab/ci/global.gitlab-ci.yml
@@ -82,19 +82,6 @@
<<: *node-modules-cache
policy: push # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up.
-.assets-cache: &assets-cache
- key: "assets-debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}-node-${NODE_ENV}-v5"
- # This list should match GITLAB_ASSETS_PATHS_LIST from scripts/gitlab_component_helpers.sh
- paths:
- - cached-assets-hash.txt
- - app/assets/javascripts/locale/**/app.js
- - public/assets/
- policy: pull
-
-.assets-cache-push: &assets-cache-push
- <<: *assets-cache
- policy: push # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up.
-
.assets-tmp-cache: &assets-tmp-cache
key: "assets-tmp-debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}-node-${NODE_ENV}-v1"
paths:
@@ -214,11 +201,6 @@
cache:
- *node-modules-cache
-.qa-frontend-node-cache:
- cache:
- - *node-modules-cache
- - *assets-tmp-cache
-
.assets-compile-cache:
cache:
- *ruby-gems-cache
@@ -306,7 +288,7 @@
- name: postgres:12
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:6.0-alpine
- - name: elasticsearch:8.4.1
+ - name: elasticsearch:8.5.2
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "12"
@@ -378,3 +360,8 @@
docker run --rm --privileged ${QEMU_IMAGE} --install all;
fi
- docker buildx create --use # creates and set's to active buildkit builder
+
+.use-kube-context:
+ before_script:
+ - export KUBE_CONTEXT="gitlab-org/gitlab:review-apps"
+ - kubectl config use-context ${KUBE_CONTEXT}
diff --git a/.gitlab/ci/notify.gitlab-ci.yml b/.gitlab/ci/notify.gitlab-ci.yml
index ae77caa140a..84fb5a55ed1 100644
--- a/.gitlab/ci/notify.gitlab-ci.yml
+++ b/.gitlab/ci/notify.gitlab-ci.yml
@@ -59,7 +59,9 @@ notify-pipeline-failure:
fi
- |
scripts/generate-failed-pipeline-slack-message.rb -i ${BROKEN_MASTER_INCIDENT_JSON} -f ${FAILED_PIPELINE_SLACK_MESSAGE_FILE};
- curl -X POST -H 'Content-Type: application/json' --data @${FAILED_PIPELINE_SLACK_MESSAGE_FILE} "$CI_SLACK_WEBHOOK_URL";
+ curl -X POST -H 'Content-Type: application/json' --data @${FAILED_PIPELINE_SLACK_MESSAGE_FILE} "$CI_SLACK_WEBHOOK_URL" ||
+ scripts/slack ${SLACK_CHANNEL} "â˜ ï¸ Broken pipeline notification failed! â˜ ï¸ See ${CI_JOB_URL}" ci_failing "Failed pipeline reporter"
+
artifacts:
paths:
- ${BROKEN_MASTER_INCIDENT_JSON}
diff --git a/.gitlab/ci/package-and-test/main.gitlab-ci.yml b/.gitlab/ci/package-and-test/main.gitlab-ci.yml
index f0bf79f009d..c53c3b1d32b 100644
--- a/.gitlab/ci/package-and-test/main.gitlab-ci.yml
+++ b/.gitlab/ci/package-and-test/main.gitlab-ci.yml
@@ -38,23 +38,6 @@ stages:
extends:
- .gitlab-qa-install
-.omnibus-env:
- variables:
- BUILD_ENV: build.env
- script:
- - |
- SECURITY_SOURCES=$([[ ! "$CI_PROJECT_NAMESPACE" =~ ^gitlab-org\/security ]] || echo "true")
- echo "SECURITY_SOURCES=${SECURITY_SOURCES:-false}" > $BUILD_ENV
- echo "OMNIBUS_GITLAB_CACHE_UPDATE=${OMNIBUS_GITLAB_CACHE_UPDATE:-false}" >> $BUILD_ENV
- for version_file in *_VERSION; do echo "$version_file=$(cat $version_file)" >> $BUILD_ENV; done
- echo "OMNIBUS_GITLAB_RUBY3_BUILD=${OMNIBUS_GITLAB_RUBY3_BUILD:-false}" >> $BUILD_ENV
- echo "OMNIBUS_GITLAB_CACHE_EDITION=${OMNIBUS_GITLAB_CACHE_EDITION:-GITLAB}" >> $BUILD_ENV
- echo "Built environment file for omnibus build:"
- cat $BUILD_ENV
- artifacts:
- reports:
- dotenv: $BUILD_ENV
-
.update-script:
script:
- export QA_COMMAND="bundle exec gitlab-qa Test::Omnibus::UpdateFromPrevious $RELEASE $GITLAB_VERSION $UPDATE_TYPE -- $QA_RSPEC_TAGS $RSPEC_REPORT_OPTS"
@@ -108,9 +91,42 @@ dont-interrupt-me:
trigger-omnibus-env:
extends:
- - .omnibus-env
- .rules:omnibus-build
stage: .pre
+ needs:
+ # We need this job because we need its `cached-assets-hash.txt` artifact, so that we can pass the assets image tag to the downstream omnibus-gitlab pipeline.
+ - pipeline: $PARENT_PIPELINE_ID
+ job: build-assets-image
+ variables:
+ BUILD_ENV: build.env
+ before_script:
+ - |
+ # This is duplicating the function from `scripts/utils.sh` since `.gitlab/ci/package-and-test/main.gitlab-ci.yml` can be included in other projects.
+ function assets_image_tag() {
+ local cache_assets_hash_file="cached-assets-hash.txt"
+
+ if [[ -n "${CI_COMMIT_TAG}" ]]; then
+ echo -n "${CI_COMMIT_REF_NAME}"
+ elif [[ -f "${cache_assets_hash_file}" ]]; then
+ echo -n "assets-hash-$(cat ${cache_assets_hash_file} | cut -c1-10)"
+ else
+ echo -n "${CI_COMMIT_SHA}"
+ fi
+ }
+ script:
+ - |
+ SECURITY_SOURCES=$([[ ! "$CI_PROJECT_NAMESPACE" =~ ^gitlab-org\/security ]] || echo "true")
+ echo "SECURITY_SOURCES=${SECURITY_SOURCES:-false}" > $BUILD_ENV
+ echo "OMNIBUS_GITLAB_CACHE_UPDATE=${OMNIBUS_GITLAB_CACHE_UPDATE:-false}" >> $BUILD_ENV
+ for version_file in *_VERSION; do echo "$version_file=$(cat $version_file)" >> $BUILD_ENV; done
+ echo "OMNIBUS_GITLAB_RUBY3_BUILD=${OMNIBUS_GITLAB_RUBY3_BUILD:-false}" >> $BUILD_ENV
+ echo "OMNIBUS_GITLAB_CACHE_EDITION=${OMNIBUS_GITLAB_CACHE_EDITION:-GITLAB}" >> $BUILD_ENV
+ echo "GITLAB_ASSETS_TAG=$(assets_image_tag)" >> $BUILD_ENV
+ echo "Built environment file for omnibus build:"
+ cat $BUILD_ENV
+ artifacts:
+ reports:
+ dotenv: $BUILD_ENV
trigger-omnibus:
extends: .rules:omnibus-build
@@ -128,6 +144,7 @@ trigger-omnibus:
GITLAB_SHELL_VERSION: $GITLAB_SHELL_VERSION
GITLAB_WORKHORSE_VERSION: $GITLAB_WORKHORSE_VERSION
GITLAB_VERSION: $CI_COMMIT_SHA
+ GITLAB_ASSETS_TAG: $GITLAB_ASSETS_TAG
IMAGE_TAG: $CI_COMMIT_SHA
TOP_UPSTREAM_SOURCE_PROJECT: $CI_PROJECT_PATH
SECURITY_SOURCES: $SECURITY_SOURCES
@@ -426,6 +443,15 @@ ee:jira:
- if: $QA_SUITES =~ /Test::Integration::Jira/
- !reference [.rules:test:manual, rules]
+ee:integrations:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::Integrations
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::Integrations/
+ - !reference [.rules:test:manual, rules]
+
ee:ldap-no-server:
extends: .qa
variables:
@@ -573,6 +599,16 @@ ee:registry-object-storage-tls:
GITLAB_TLS_CERTIFICATE: $QA_GITLAB_TLS_CERTIFICATE
GITLAB_QA_OPTS: --omnibus-config registry_object_storage
+ee:importers:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::Import
+ GITLAB_QA_OPTS: --set-feature-flags bulk_import_projects=enabled
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::Import/
+ - !reference [.rules:test:manual, rules]
+
# ==========================================
# Post test stage
# ==========================================
diff --git a/.gitlab/ci/qa.gitlab-ci.yml b/.gitlab/ci/qa.gitlab-ci.yml
index 8740a5fe17d..f6668d7864e 100644
--- a/.gitlab/ci/qa.gitlab-ci.yml
+++ b/.gitlab/ci/qa.gitlab-ci.yml
@@ -74,6 +74,8 @@ e2e:package-and-test:
- build-qa-image
- e2e-test-pipeline-generate
variables:
+ # This is needed by `trigger-omnibus-env` (`.gitlab/ci/package-and-test/main.gitlab-ci.yml`).
+ PARENT_PIPELINE_ID: $CI_PIPELINE_ID
SKIP_MESSAGE: Skipping package-and-test due to mr containing only quarantine changes!
RELEASE: "${REGISTRY_HOST}/${REGISTRY_GROUP}/build/omnibus-gitlab-mirror/gitlab-ee:${CI_COMMIT_SHA}"
GITLAB_QA_IMAGE: "${CI_REGISTRY_IMAGE}/gitlab-ee-qa:${CI_COMMIT_SHA}"
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml
index f4f832b84d0..0e0aeb2954b 100644
--- a/.gitlab/ci/rails.gitlab-ci.yml
+++ b/.gitlab/ci/rails.gitlab-ci.yml
@@ -333,7 +333,7 @@ rspec:deprecations:
script:
- grep -h -R "keyword" deprecations/ | awk '{$1=$1};1' | sort | uniq -c | sort
- grep -R "keyword" deprecations/ | wc
- - run_timed_command "bundle exec rubocop --only Lint/LastKeywordArgument --parallel"
+ - run_timed_command "fail_on_warnings bundle exec rubocop --only Lint/LastKeywordArgument --parallel"
artifacts:
expire_in: 31d
when: always
@@ -878,23 +878,28 @@ fail-pipeline-early:
script:
- fail_pipeline_early
-rspec rspec-pg12-rerun-previous-failed-tests:
+.base-rspec-pg12-rerun-previous-failed-tests:
extends:
- - .rspec-base-pg12
- .rails:rules:rerun-previous-failed-tests
stage: test
needs: ["setup-test-env", "compile-test-assets", "detect-previous-failed-tests"]
script:
- !reference [.base-script, script]
- - rspec_rerun_previous_failed_tests tmp/previous_failed_tests/rspec_failed_files.txt
+ - rspec_rerun_previous_failed_tests "${PREVIOUS_FAILED_TESTS_FILE}"
+
+rspec rspec-pg12-rerun-previous-failed-tests:
+ extends:
+ - .rspec-base-pg12
+ - .base-rspec-pg12-rerun-previous-failed-tests
+ variables:
+ PREVIOUS_FAILED_TESTS_FILE: tmp/previous_failed_tests/rspec_failed_files.txt
rspec rspec-ee-pg12-rerun-previous-failed-tests:
extends:
- - "rspec rspec-pg12-rerun-previous-failed-tests"
- .rspec-ee-base-pg12
- script:
- - !reference [.base-script, script]
- - rspec_rerun_previous_failed_tests tmp/previous_failed_tests/rspec_ee_failed_files.txt
+ - .base-rspec-pg12-rerun-previous-failed-tests
+ variables:
+ PREVIOUS_FAILED_TESTS_FILE: tmp/previous_failed_tests/rspec_ee_failed_files.txt
# EE: Canonical MR pipelines
##################################################
diff --git a/.gitlab/ci/rails/shared.gitlab-ci.yml b/.gitlab/ci/rails/shared.gitlab-ci.yml
index d47bac5e433..e282781b7b5 100644
--- a/.gitlab/ci/rails/shared.gitlab-ci.yml
+++ b/.gitlab/ci/rails/shared.gitlab-ci.yml
@@ -54,10 +54,14 @@ include:
RECORD_DEPRECATIONS: "true"
GEO_SECONDARY_PROXY: 0
RSPEC_TESTS_FILTER_FILE: "${RSPEC_MATCHING_TESTS_PATH}"
+ SUCCESSFULLY_RETRIED_TEST_EXIT_CODE: 137
needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets", "detect-tests"]
script:
- !reference [.base-script, script]
- rspec_paralellized_job "--tag ~quarantine --tag ~level:migration"
+ allow_failure:
+ # the exit code listed here must match the one defined for the variable SUCCESSFULLY_RETRIED_TEST_EXIT_CODE
+ exit_codes: 137
.base-artifacts:
artifacts:
@@ -68,6 +72,7 @@ include:
- crystalball/
- deprecations/
- knapsack/
+ - query_recorder/
- rspec/
- tmp/capybara/
- log/*.log
diff --git a/.gitlab/ci/reports.gitlab-ci.yml b/.gitlab/ci/reports.gitlab-ci.yml
index 5fdcdc12fc8..b87e5ad9bba 100644
--- a/.gitlab/ci/reports.gitlab-ci.yml
+++ b/.gitlab/ci/reports.gitlab-ci.yml
@@ -88,7 +88,7 @@ yarn-audit-dependency_scanning:
extends: .default-retry
stage: test
image:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/security-products/package-hunter-cli:v1.3.2@sha256:7529deaef9ea21aab56bfb74ae1abbc121311affdb6ece49ce7b1c360f997ca2
+ name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/security-products/package-hunter-cli:v1.3.3@sha256:1d3af9a61aa01549a62be17fa655fcf06271ac9e1b1e822c2a7930fa1d4a8a6b
entrypoint: [""]
variables:
HTR_user: '$PACKAGE_HUNTER_USER'
diff --git a/.gitlab/ci/review-apps/main.gitlab-ci.yml b/.gitlab/ci/review-apps/main.gitlab-ci.yml
index 85c5c7d1b1d..b72afedc3b0 100644
--- a/.gitlab/ci/review-apps/main.gitlab-ci.yml
+++ b/.gitlab/ci/review-apps/main.gitlab-ci.yml
@@ -34,19 +34,25 @@ review-build-cng-env:
- .review:rules:review-build-cng
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}:bundler-2.3
stage: prepare
- needs: []
+ needs:
+ # We need this job because we need its `cached-assets-hash.txt` artifact, so that we can pass the assets image tag to the downstream CNG pipeline.
+ - pipeline: $PARENT_PIPELINE_ID
+ job: build-assets-image
+ variables:
+ BUILD_ENV: build.env
before_script:
- source ./scripts/utils.sh
- install_gitlab_gem
script:
- - ruby -r./scripts/trigger-build.rb -e "puts Trigger.variables_for_env_file(Trigger::CNG.new.variables)" > build.env
+ - 'ruby -r./scripts/trigger-build.rb -e "puts Trigger.variables_for_env_file(Trigger::CNG.new.variables)" > $BUILD_ENV'
+ - echo "GITLAB_ASSETS_TAG=$(assets_image_tag)" >> $BUILD_ENV
- ruby -e 'puts "FULL_RUBY_VERSION=#{RUBY_VERSION}"' >> build.env
- - cat build.env
+ - cat $BUILD_ENV
artifacts:
reports:
- dotenv: build.env
+ dotenv: $BUILD_ENV
paths:
- - build.env
+ - $BUILD_ENV
expire_in: 7 days
when: always
@@ -96,7 +102,7 @@ review-build-cng:
name: review/${CI_COMMIT_REF_SLUG}${SCHEDULE_TYPE} # No separator for SCHEDULE_TYPE so it's compatible as before and looks nice without it
url: https://gitlab-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}
on_stop: review-stop
- auto_stop_in: 48 hours
+ auto_stop_in: 6 hours
review-deploy:
extends:
@@ -108,6 +114,8 @@ review-deploy:
key: "review-deploy-dependencies-charts-${GITLAB_HELM_CHART_REF}-v1"
paths:
- "gitlab-${GITLAB_HELM_CHART_REF}"
+ environment:
+ action: start
before_script:
- export GITLAB_SHELL_VERSION=$(<GITLAB_SHELL_VERSION)
- export GITALY_VERSION=$(<GITALY_SERVER_VERSION)
@@ -115,12 +123,13 @@ review-deploy:
- echo "${CI_ENVIRONMENT_URL}" > environment_url.txt
- echo "QA_GITLAB_URL=${CI_ENVIRONMENT_URL}" > environment.env
- *base-before_script
+ - !reference [".use-kube-context", before_script]
script:
- run_timed_command "check_kube_domain"
- run_timed_command "download_chart"
- run_timed_command "deploy" || (display_deployment_debug && exit 1)
- - run_timed_command "verify_deploy"|| (display_deployment_debug && exit 1)
- - run_timed_command "disable_sign_ups"
+ - run_timed_command "verify_deploy" || (display_deployment_debug && exit 1)
+ - run_timed_command "disable_sign_ups" || (display_deployment_debug && exit 1)
after_script:
# Run seed-dast-test-data.sh only when DAST_RUN is set to true. This is to pupulate review app with data for DAST scan.
# Set DAST_RUN to true when jobs are manually scheduled.
@@ -128,6 +137,7 @@ review-deploy:
artifacts:
paths:
- environment_url.txt
+ - curl-logs/
reports:
dotenv: environment.env
expire_in: 7 days
@@ -139,12 +149,15 @@ review-deploy-sample-projects:
- .review:rules:review-deploy
stage: deploy
needs: ["review-deploy"]
+ environment:
+ action: prepare
before_script:
- export GITLAB_SHELL_VERSION=$(<GITLAB_SHELL_VERSION)
- export GITALY_VERSION=$(<GITALY_SERVER_VERSION)
- export GITLAB_WORKHORSE_VERSION=$(<GITLAB_WORKHORSE_VERSION)
- echo "${CI_ENVIRONMENT_URL}" > environment_url.txt
- *base-before_script
+ - !reference [".use-kube-context", before_script]
script:
- date
- create_sample_projects
@@ -160,7 +173,9 @@ review-deploy-sample-projects:
# See https://gitlab.com/gitlab-org/gitlab/issues/191273
GIT_DEPTH: 1
before_script:
- - *base-before_script
+ - source ./scripts/utils.sh
+ - source ./scripts/review_apps/review-apps.sh
+ - !reference [".use-kube-context", before_script]
review-delete-deployment:
extends:
@@ -174,7 +189,7 @@ review-stop:
extends:
- .review-stop-base
- .review:rules:review-stop
- resource_group: review/${CI_COMMIT_REF_SLUG}${SCHEDULE_TYPE} # CI_ENVIRONMENT_SLUG is not available here and we want this to be the same as the environment
+ resource_group: review/${CI_COMMIT_REF_SLUG}${SCHEDULE_TYPE} # CI_ENVIRONMENT_SLUG is not available here and we want this to be the same as the environment
stage: deploy
needs: []
script:
diff --git a/.gitlab/ci/review-apps/qa.gitlab-ci.yml b/.gitlab/ci/review-apps/qa.gitlab-ci.yml
index 69ce028987a..d28819208b7 100644
--- a/.gitlab/ci/review-apps/qa.gitlab-ci.yml
+++ b/.gitlab/ci/review-apps/qa.gitlab-ci.yml
@@ -99,6 +99,7 @@ review-qa-non-blocking:
variables:
QA_SCENARIO: Test::Instance::ReviewNonBlocking
QA_RUN_TYPE: review-qa-non-blocking
+ when: manual
allow_failure: true
review-qa-non-blocking-parallel:
extends:
diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml
index 35df4de6513..b6c273aeb99 100644
--- a/.gitlab/ci/review.gitlab-ci.yml
+++ b/.gitlab/ci/review.gitlab-ci.yml
@@ -6,15 +6,48 @@ review-cleanup:
stage: prepare
environment:
name: review/regular-cleanup
- action: stop
+ action: access
before_script:
- source scripts/utils.sh
- - source scripts/review_apps/gcp_cleanup.sh
+ - !reference [".use-kube-context", before_script]
- install_gitlab_gem
- - setup_gcp_dependencies
+ - setup_gcloud
script:
- - scripts/review_apps/automated_cleanup.rb
- - gcp_cleanup
+ - scripts/review_apps/automated_cleanup.rb || (scripts/slack review-apps-monitoring "â˜ ï¸ \`${CI_JOB_NAME}\` failed! â˜ ï¸ See ${CI_JOB_URL} - <https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/blob/main/runbooks/review-apps.md#review-cleanup-job-failed|📗 RUNBOOK 📕>" warning "GitLab Bot" && exit 1);
+
+.base-review-checks:
+ extends:
+ - .default-retry
+ image: ${REVIEW_APPS_IMAGE}
+ stage: prepare
+ before_script:
+ - source scripts/utils.sh
+ - setup_gcloud
+ - !reference [".use-kube-context", before_script]
+
+review-k8s-resources-count-checks:
+ extends:
+ - .base-review-checks
+ - .review:rules:review-k8s-resources-count-checks
+ needs:
+ - job: review-cleanup
+ optional: true
+ environment:
+ name: review/k8s-resources-count-checks
+ action: verify
+ script:
+ - scripts/review_apps/k8s-resources-count-checks.sh || (scripts/slack review-apps-monitoring "â˜ ï¸ \`${CI_JOB_NAME}\` failed! â˜ ï¸ See ${CI_JOB_URL} - <https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/blob/main/runbooks/review-apps.md#review-k8s-resources-count-checks-job-failed|📗 RUNBOOK 📕>" warning "GitLab Bot" && exit 1);
+
+review-gcp-quotas-checks:
+ extends:
+ - .base-review-checks
+ - .review:rules:review-gcp-quotas-checks
+ needs: []
+ environment:
+ name: review/gcp-quotas-checks
+ action: verify
+ script:
+ - ruby scripts/review_apps/gcp-quotas-checks.rb || (scripts/slack review-apps-monitoring "â˜ ï¸ \`${CI_JOB_NAME}\` failed! â˜ ï¸ See ${CI_JOB_URL} - <https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/blob/main/runbooks/review-apps.md#review-gcp-quotas-checks-job-failed|📗 RUNBOOK 📕>" warning "GitLab Bot" && exit 1);
start-review-app-pipeline:
extends:
@@ -29,6 +62,8 @@ start-review-app-pipeline:
# They need to be explicitly passed on to the child pipeline.
# https://docs.gitlab.com/ee/ci/pipelines/multi_project_pipelines.html#pass-cicd-variables-to-a-downstream-pipeline-by-using-the-variables-keyword
variables:
+ # This is needed by `review-build-cng-env` (`.gitlab/ci/review-apps/main.gitlab-ci.yml`).
+ PARENT_PIPELINE_ID: $CI_PIPELINE_ID
SCHEDULE_TYPE: $SCHEDULE_TYPE
DAST_RUN: $DAST_RUN
SKIP_MESSAGE: Skipping review-app due to mr containing only quarantine changes!
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index c6cfb491e61..8b7aee8de9d 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -38,11 +38,16 @@
# Once https://gitlab.com/gitlab-org/gitlab/-/issues/373904 is implemented, we should be able to change this back to
# if: '$CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_APPROVALS_COUNT > 0'
# or any similar condition to check that the MR has *any* approval (not just required approval).
+#
+# Temprorarily adding || $CI_MERGE_REQUEST_LABELS =~ /pipeline:run-full-rspec/ for backward compatibility,
+# remove once https://gitlab.com/gitlab-org/quality/quality-engineering/team-tasks/-/issues/1557 is fully rolled out
.if-merge-request-approved: &if-merge-request-approved
- if: '$CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_LABELS =~ /pipeline:run-full-rspec/'
+ if: '$CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_LABELS =~ /pipeline:mr-approved/ || $CI_MERGE_REQUEST_LABELS =~ /pipeline:run-full-rspec/'
+# Temprorarily adding && $CI_MERGE_REQUEST_LABELS !~ /pipeline:run-full-rspec/ for backward compatibility,
+# remove once https://gitlab.com/gitlab-org/quality/quality-engineering/team-tasks/-/issues/1557 is fully rolled out
.if-merge-request-not-approved: &if-merge-request-not-approved
- if: '$CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_LABELS !~ /pipeline:run-full-rspec/'
+ if: '$CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_LABELS !~ /pipeline:mr-approved/ && $CI_MERGE_REQUEST_LABELS !~ /pipeline:run-full-rspec/'
.if-automated-merge-request: &if-automated-merge-request
if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == "release-tools/update-gitaly" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ /stable-ee$/'
@@ -83,8 +88,8 @@
.if-merge-request-labels-group-global-search: &if-merge-request-labels-group-global-search
if: '$CI_MERGE_REQUEST_LABELS =~ /group::global search/'
-.if-merge-request-labels-pipeline-expedite-master-fixing: &if-merge-request-labels-pipeline-expedite-master-fixing
- if: '$CI_MERGE_REQUEST_LABELS =~ /master:(foss-)?broken/ && $CI_MERGE_REQUEST_LABELS =~ /pipeline:expedite-master-fixing/'
+.if-merge-request-labels-pipeline-expedite: &if-merge-request-labels-pipeline-expedite
+ if: '$CI_MERGE_REQUEST_LABELS =~ /master:(foss-)?broken/ && $CI_MERGE_REQUEST_LABELS =~ /pipeline:expedite/'
.if-merge-request-labels-frontend-and-feature-flag: &if-merge-request-labels-frontend-and-feature-flag
if: '$CI_MERGE_REQUEST_LABELS =~ /frontend/ && $CI_MERGE_REQUEST_LABELS =~ /feature flag/'
@@ -95,26 +100,26 @@
.if-fork-merge-request: &if-fork-merge-request
if: '$CI_PROJECT_NAMESPACE !~ /^gitlab(-org)?($|\/)/ && $CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_LABELS !~ /pipeline:run-all-rspec/'
-.if-default-branch-schedule-maintenance: &if-default-branch-schedule-maintenance
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "schedule" && $SCHEDULE_TYPE == "maintenance"'
+.if-schedule-pipeline: &if-schedule-pipeline
+ if: '$CI_PIPELINE_SOURCE == "schedule"'
+
+.if-schedule-maintenance: &if-schedule-maintenance
+ if: '$CI_PIPELINE_SOURCE == "schedule" && $SCHEDULE_TYPE == "maintenance"'
.if-default-branch-schedule-nightly: &if-default-branch-schedule-nightly
if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "schedule" && $SCHEDULE_TYPE == "nightly"'
+.if-ruby3-branch-schedule-nightly: &if-ruby3-branch-schedule-nightly
+ if: '$CI_COMMIT_BRANCH == "ruby3" && $CI_PIPELINE_SOURCE == "schedule" && $SCHEDULE_TYPE == "nightly"'
+
.if-security-schedule: &if-security-schedule
if: '$CI_PROJECT_NAMESPACE == "gitlab-org/security" && $CI_PIPELINE_SOURCE == "schedule"'
.if-dot-com-gitlab-org-schedule: &if-dot-com-gitlab-org-schedule
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org" && $CI_PIPELINE_SOURCE == "schedule"'
-.if-dot-com-ee-schedule: &if-dot-com-ee-schedule
- if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_PATH == "gitlab-org/gitlab" && $CI_PIPELINE_SOURCE == "schedule"'
-
-.if-dot-com-ee-schedule-maintenance: &if-dot-com-ee-schedule-maintenance
- if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_PATH == "gitlab-org/gitlab" && $CI_PIPELINE_SOURCE == "schedule" && $SCHEDULE_TYPE == "maintenance"'
-
-.if-dot-com-ee-schedule-nightly: &if-dot-com-ee-schedule-nightly
- if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_PATH == "gitlab-org/gitlab" && $CI_PIPELINE_SOURCE == "schedule" && $SCHEDULE_TYPE == "nightly"'
+.if-dot-com-ee-schedule-default-branch-maintenance: &if-dot-com-ee-schedule-default-branch-maintenance
+ if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_PATH == "gitlab-org/gitlab" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "schedule" && $SCHEDULE_TYPE == "maintenance"'
.if-dot-com-ee-schedule-nightly-child-pipeline: &if-dot-com-ee-schedule-nightly-child-pipeline
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_PATH == "gitlab-org/gitlab" && $CI_PIPELINE_SOURCE == "parent_pipeline" && $SCHEDULE_TYPE == "nightly"'
@@ -192,8 +197,7 @@
- "spec/support/gitlab-git-test.git/**/*"
.yaml-lint-patterns: &yaml-lint-patterns
- - "*.yml"
- - "**/*.yml"
+ - "**/*.{yml,yaml}{,.*}"
.lint-pipeline-yaml-patterns: &lint-pipeline-yaml-patterns
- ".gitlab-ci.yml"
@@ -315,6 +319,7 @@
- "scripts/rspec_helpers.sh"
# Mapped patterns (see tests.yml)
- "data/whats_new/*.yml"
+ - "doc/index.md"
.search-backend-patterns: &search-backend-patterns
- "{,jh/}Gemfile.lock"
@@ -332,6 +337,7 @@
- "{,ee/,jh/}{,spec/}lib/{,ee/,jh/}gitlab/background_migration/**/*"
- "{,ee/,jh/}{,spec/}lib/{,ee/,jh/}gitlab/background_migration{,_spec}.rb"
- "{,ee/,jh/}spec/support/helpers/database/**/*"
+ - "{,ee/,jh/}spec/support/helpers/migrations_helpers/**/*"
- "lib/gitlab/markdown_cache/active_record/**/*"
- "lib/api/admin/batched_background_migrations.rb"
- "spec/requests/api/admin/batched_background_migrations_spec.rb"
@@ -384,6 +390,7 @@
- ".gitlab/ci/**/*"
# Mapped patterns (see tests.yml)
- "data/whats_new/*.yml"
+ - "doc/index.md"
# .code-patterns + .backstage-patterns
.code-backstage-patterns: &code-backstage-patterns
@@ -414,6 +421,7 @@
- "{,spec/}tooling/**/*"
# Mapped patterns (see tests.yml)
- "data/whats_new/*.yml"
+ - "doc/index.md"
# .code-patterns + .qa-patterns
.code-qa-patterns: &code-qa-patterns
@@ -440,6 +448,7 @@
- "{,jh/}qa/**/*"
# Mapped patterns (see tests.yml)
- "data/whats_new/*.yml"
+ - "doc/index.md"
# .code-patterns + .backstage-patterns + .qa-patterns
.code-backstage-qa-patterns: &code-backstage-qa-patterns
@@ -474,6 +483,7 @@
- "{,jh/}qa/**/*"
# Mapped patterns (see tests.yml)
- "data/whats_new/*.yml"
+ - "doc/index.md"
# .code-backstage-qa-patterns + .workhorse-patterns
# NOTE: `setup-test-env-patterns` intentionally does not include docs files, because this would
@@ -496,7 +506,9 @@
- "config.ru"
- "{,ee/,jh/}{app,bin,config,db,generator_templates,haml_lint,lib,locale,public,scripts,storybook,symbol,vendor}/**/*"
- "doc/api/graphql/reference/*" # Files in this folder are auto-generated
+ # Mapped patterns (see tests.yml)
- "data/whats_new/*.yml"
+ - "doc/index.md"
# CI changes
- ".gitlab-ci.yml"
- ".gitlab/ci/**/*"
@@ -660,7 +672,7 @@
################
.shared:rules:update-cache:
rules:
- - <<: *if-default-branch-schedule-maintenance
+ - <<: *if-schedule-maintenance
- <<: *if-security-schedule
- <<: *if-merge-request-labels-update-caches
@@ -684,6 +696,8 @@
- <<: *if-dot-com-gitlab-org-and-security-merge-request
changes: *code-qa-patterns
- <<: *if-auto-deploy-branches
+ variables:
+ ARCH: amd64,arm64
- <<: *if-default-branch-or-tag
variables:
ARCH: amd64,arm64
@@ -713,7 +727,7 @@
rules:
# That would run for any project that has a "maintenance" pipeline schedule
# but in fact, the cache package is only uploaded for gitlab.com/gitlab-org/gitlab and jihulab.com/gitlab-cn/gitlab
- - <<: *if-default-branch-schedule-maintenance
+ - <<: *if-schedule-maintenance
- <<: *if-dot-com-gitlab-org-default-branch
changes: ["workhorse/**/*"]
- <<: *if-dot-com-gitlab-org-merge-request
@@ -730,7 +744,7 @@
when: never
# That would run for any project that has a "maintenance" pipeline schedule
# but in fact, the cache package is only uploaded for gitlab.com/gitlab-org/gitlab and jihulab.com/gitlab-cn/gitlab
- - <<: *if-default-branch-schedule-maintenance
+ - <<: *if-schedule-maintenance
- <<: *if-dot-com-gitlab-org-default-branch
changes: *assets-compilation-patterns
- <<: *if-dot-com-gitlab-org-merge-request
@@ -748,7 +762,7 @@
when: never
# That would run for any project that has a "maintenance" pipeline schedule
# but in fact, the cache package is only uploaded for gitlab.com/gitlab-org/gitlab and jihulab.com/gitlab-cn/gitlab
- - <<: *if-default-branch-schedule-maintenance
+ - <<: *if-schedule-maintenance
- <<: *if-dot-com-gitlab-org-merge-request
changes:
- ".gitlab/ci/caching.gitlab-ci.yml"
@@ -800,8 +814,7 @@
.docs:rules:docs-code-quality:
rules:
- - <<: *if-default-branch-refs
- - <<: *if-default-refs
+ - <<: *if-merge-request
changes: *docs-code-quality-patterns
.docs:rules:docs-lint:
@@ -896,6 +909,7 @@
when: never
- <<: *if-merge-request-labels-as-if-foss
- <<: *if-merge-request-labels-run-all-rspec
+ - <<: *if-merge-request-labels-frontend-and-feature-flag
- <<: *if-default-refs
changes: *code-backstage-qa-patterns
- <<: *if-default-refs
@@ -926,6 +940,7 @@
- !reference [".strict-ee-only-rules", rules]
- !reference [".frontend:rules:default-frontend-jobs-as-if-foss", rules]
- <<: *if-merge-request-labels-run-all-jest
+ - <<: *if-merge-request-labels-frontend-and-feature-flag
- <<: *if-merge-request
changes: *frontend-patterns-for-as-if-foss
@@ -1007,29 +1022,13 @@
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-labels-pipeline-expedite-master-fixing
+ - <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request
changes: *code-backstage-patterns
- <<: *if-default-branch-refs
changes: *code-backstage-patterns
-.frontend:rules:qa-frontend-node:
- rules:
- - <<: *if-default-branch-refs
- changes: *frontend-dependency-patterns
- - <<: *if-merge-request
- changes: *frontend-dependency-patterns
-
-.frontend:rules:qa-frontend-node-latest:
- rules:
- - <<: *if-default-branch-refs
- changes: *frontend-dependency-patterns
- allow_failure: true
- - <<: *if-merge-request
- changes: *frontend-dependency-patterns
- allow_failure: true
-
.frontend:rules:bundle-size-review:
rules:
- <<: *if-not-canonical-namespace
@@ -1066,7 +1065,7 @@
###############
.pages:rules:
rules:
- - <<: *if-dot-com-ee-schedule-maintenance
+ - <<: *if-dot-com-ee-schedule-default-branch-maintenance
############
# QA rules #
@@ -1116,7 +1115,7 @@
when: never
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-labels-pipeline-expedite-master-fixing
+ - <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-targeting-stable-branch
allow_failure: true
@@ -1549,24 +1548,25 @@
- <<: *if-not-ee
when: never
- <<: *if-default-branch-schedule-nightly
+ - <<: *if-ruby3-branch-schedule-nightly
- <<: *if-merge-request-labels-run-all-rspec
.rails:rules:rspec-coverage:
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-labels-pipeline-expedite-master-fixing
+ - <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request
changes: *code-backstage-patterns
- - <<: *if-default-branch-schedule-maintenance
+ - <<: *if-schedule-maintenance
- <<: *if-merge-request-labels-run-all-rspec
.rails:rules:rspec-undercoverage:
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-labels-pipeline-expedite-master-fixing
+ - <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-labels-skip-undercoverage
when: never
@@ -1601,7 +1601,7 @@
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-labels-pipeline-expedite-master-fixing
+ - <<: *if-merge-request-labels-pipeline-expedite
when: never
- if: '$SKIP_FLAKY_TESTS_AUTOMATICALLY == "true" || $RETRY_FAILED_TESTS_IN_NEW_PROCESS == "true"'
changes: *code-backstage-patterns
@@ -1849,7 +1849,7 @@
rules:
- if: "$PACKAGE_HUNTER_USER == null || $PACKAGE_HUNTER_USER == ''"
when: never
- - <<: *if-default-branch-schedule-maintenance
+ - <<: *if-schedule-maintenance
- <<: *if-merge-request
changes: ["yarn.lock"]
@@ -1857,7 +1857,7 @@
rules:
- if: "$PACKAGE_HUNTER_USER == null || $PACKAGE_HUNTER_USER == ''"
when: never
- - <<: *if-default-branch-schedule-maintenance
+ - <<: *if-schedule-maintenance
- <<: *if-merge-request
changes: ["Gemfile.lock"]
@@ -1882,37 +1882,39 @@
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-labels-pipeline-expedite-master-fixing
+ - <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-labels-run-review-app
- <<: *if-dot-com-gitlab-org-merge-request
changes: *ci-review-patterns
- when: never
+ allow_failure: true
- <<: *if-dot-com-gitlab-org-merge-request
changes: *frontend-build-patterns
variables: *review-change-pattern
- when: never
+ allow_failure: true
- <<: *if-dot-com-gitlab-org-merge-request
changes: *controllers-patterns
variables: *review-change-pattern
- when: never
+ when: manual
+ allow_failure: true
- <<: *if-dot-com-gitlab-org-merge-request
changes: *models-patterns
variables: *review-change-pattern
- when: never
+ when: manual
+ allow_failure: true
- <<: *if-dot-com-gitlab-org-merge-request
changes: *lib-gitlab-patterns
variables: *review-change-pattern
- when: never
+ when: manual
+ allow_failure: true
- <<: *if-dot-com-gitlab-org-merge-request
changes: *qa-patterns
- when: never
+ allow_failure: true
- <<: *if-dot-com-gitlab-org-merge-request
changes: *code-patterns
when: manual
allow_failure: true
- <<: *if-dot-com-gitlab-org-schedule
- when: never
allow_failure: true
variables:
KNAPSACK_GENERATE_REPORT: "true"
@@ -1948,7 +1950,7 @@
rules:
- <<: *if-not-ee
when: never
- - <<: *if-merge-request-labels-pipeline-expedite-master-fixing
+ - <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-labels-run-review-app
when: manual
@@ -1984,6 +1986,24 @@
- <<: *if-dot-com-gitlab-org-schedule
allow_failure: true
+.review:rules:review-k8s-resources-count-checks:
+ rules:
+ - <<: *if-dot-com-gitlab-org-schedule
+ allow_failure: true
+ - <<: *if-dot-com-gitlab-org-merge-request
+ changes:
+ - "scripts/review_apps/k8s-resources-count-checks.sh"
+ allow_failure: true
+
+.review:rules:review-gcp-quotas-checks:
+ rules:
+ - <<: *if-dot-com-gitlab-org-schedule
+ allow_failure: true
+ - <<: *if-dot-com-gitlab-org-merge-request
+ changes:
+ - "scripts/review_apps/gcp-quotas-checks.rb"
+ allow_failure: true
+
.review:rules:review-stop:
rules:
- when: manual
@@ -2016,6 +2036,8 @@
rules:
- <<: *if-default-branch-or-tag
allow_failure: true
+ - <<: *if-schedule-pipeline
+ allow_failure: true
- <<: *if-auto-deploy-branches
allow_failure: true
- when: manual
@@ -2061,7 +2083,7 @@
rules:
- <<: *if-not-ee
when: never
- - <<: *if-dot-com-ee-schedule-maintenance
+ - <<: *if-dot-com-ee-schedule-default-branch-maintenance
- <<: *if-default-refs
changes:
- ".gitlab/ci/setup.gitlab-ci.yml"
@@ -2083,7 +2105,7 @@
rules:
- <<: *if-not-ee
when: never
- - <<: *if-dot-com-ee-schedule-maintenance
+ - <<: *if-dot-com-ee-schedule-default-branch-maintenance
- <<: *if-default-refs
changes:
- ".gitlab/ci/test-metadata.gitlab-ci.yml"
diff --git a/.gitlab/ci/static-analysis.gitlab-ci.yml b/.gitlab/ci/static-analysis.gitlab-ci.yml
index 0a310691cd7..6df2c5f403f 100644
--- a/.gitlab/ci/static-analysis.gitlab-ci.yml
+++ b/.gitlab/ci/static-analysis.gitlab-ci.yml
@@ -22,7 +22,7 @@ update-static-analysis-cache:
# Silence cop offenses for rules with "grace period".
# This will notify Slack if offenses were silenced.
# For the moment we only cache `tmp/rubocop_cache` so we don't need to run all the tasks.
- - run_timed_command "bundle exec rake rubocop:check:graceful"
+ - run_timed_command "fail_on_warnings bundle exec rake rubocop:check:graceful"
static-analysis:
extends:
@@ -132,12 +132,12 @@ rubocop:
# We won't notify Slack if offenses were silenced to avoid frequent messages.
# Job `update-static-analysis-cache` takes care of Slack notifications every 2 hours.
unset CI_SLACK_WEBHOOK_URL
- run_timed_command "bundle exec rake rubocop:check:graceful"
+ run_timed_command "fail_on_warnings bundle exec rake rubocop:check:graceful"
else
cat "${RSPEC_CHANGED_FILES_PATH}" | ruby -e 'print $stdin.read.split(" ").select { |f| File.exist?(f) }.join(" ")' > "$RUBOCOP_TARGET_FILES"
# Skip running RuboCop if there's no target files
if [ -s "${RUBOCOP_TARGET_FILES}" ]; then
- run_timed_command "bundle exec rubocop --parallel --force-exclusion $(cat ${RUBOCOP_TARGET_FILES})"
+ run_timed_command "fail_on_warnings bundle exec rubocop --parallel --force-exclusion $(cat ${RUBOCOP_TARGET_FILES})"
else
echoinfo "Nothing interesting changed for RuboCop. Skipping."
fi
@@ -177,7 +177,7 @@ feature-flags-usage:
script:
# We need to disable the cache for this cop since it creates files under tmp/feature_flags/*.used,
# the cache would prevent these files from being created.
- - run_timed_command "bundle exec rubocop --only Gitlab/MarkUsedFeatureFlags --cache false"
+ - run_timed_command "fail_on_warnings bundle exec rubocop --only Gitlab/MarkUsedFeatureFlags --cache false"
artifacts:
expire_in: 31d
when: always
diff --git a/.gitlab/issue_templates/Broken Master - Flaky.md b/.gitlab/issue_templates/Broken Master - Flaky.md
index 6b56845ba8c..bea12615e41 100644
--- a/.gitlab/issue_templates/Broken Master - Flaky.md
+++ b/.gitlab/issue_templates/Broken Master - Flaky.md
@@ -16,10 +16,13 @@ Please read the below documentations for a workflow of triaging and resolving br
<!-- If the pipeline failure is reproducible, provide steps to recreate the issue locally. Please use an ordered list. -->
+Please refer to [Flaky tests documentation](https://docs.gitlab.com/ee/development/testing_guide/flaky_tests.html) to
+learn more about how to reproduce them.
+
### Proposed Resolution
<!-- Describe the proposed change to restore master stability. -->
Please refer to the [Resolution guidance](https://about.gitlab.com/handbook/engineering/workflow/#resolution-of-broken-master) to learn more about resolution of broken master.
-/label ~"failure::flaky-test" ~"Engineering Productivity" ~"priority::2" ~"severity::2"
+/label ~"failure::flaky-test" ~"Engineering Productivity" ~"priority::2" ~"severity::3" ~"type::bug" ~"bug::transient"
diff --git a/.gitlab/issue_templates/Broken Master - Non-flaky.md b/.gitlab/issue_templates/Broken Master - Non-flaky.md
index 97a34aa759d..43e73fc5c5a 100644
--- a/.gitlab/issue_templates/Broken Master - Non-flaky.md
+++ b/.gitlab/issue_templates/Broken Master - Non-flaky.md
@@ -21,4 +21,4 @@ Please read the below documentations for a workflow of triaging and resolving br
Please refer to the [Resolution guidance](https://about.gitlab.com/handbook/engineering/workflow/#resolution-of-broken-master) to learn more about resolution of broken master.
-/label ~"master:broken" ~"Engineering Productivity" ~"priority::1" ~"severity::1"
+/label ~"master:broken" ~"Engineering Productivity" ~"priority::1" ~"severity::1" ~"type::bug" ~"bug::transient"
diff --git a/.gitlab/issue_templates/Doc_cleanup.md b/.gitlab/issue_templates/Doc_cleanup.md
index 79cf2662b07..3ea692ed1ac 100644
--- a/.gitlab/issue_templates/Doc_cleanup.md
+++ b/.gitlab/issue_templates/Doc_cleanup.md
@@ -1,72 +1,38 @@
+/labels ~"documentation" ~"docs-only" ~"documentation" ~"docs::improvement" ~"type::maintenance" ~"maintenance::refactor" ~"Seeking community contributions" ~"quick win" ~"Technical Writing"
+
<!--
-* Use this issue template for identifying issues to work on in existing documentation, normally identified
-* with our [Vale](https://docs.gitlab.com/ee/development/documentation/testing.html#vale) or [markdownlint](https://docs.gitlab.com/ee/development/documentation/testing.html#markdownlint) tools. Much of this identified work is suitable for first-time contributors or
+* Use this template for documentation issues identified
+* by [Vale](https://docs.gitlab.com/ee/development/documentation/testing.html#vale)
+* or [markdownlint](https://docs.gitlab.com/ee/development/documentation/testing.html#markdownlint).
+* This template is meant to describe work for first-time contributors or
* for work during Hackathons.
*
-* Normal documentation updates should use the Documentation template, and documentation work as part of
-* feature development should use the Feature Request template.
+* Feature development work should not use this template. Use the Feature Request template instead.
-->
-If you are a community contributor, **do not work on the issue if it is not assigned to you yet**.
-
-Additionally, please review these points before working on this issue:
-
-1. If you would like to work on the issue, type `@gl-docsteam I would like to work on this issue.`
- in a comment. A technical writer will assign the issue to you. If someone has already chosen this issue,
- pick another issue, or view docs [in the docs directory](https://gitlab.com/gitlab-org/gitlab/-/tree/master/doc)
- and open a merge request for any page you feel can be improved.
-1. Carefully review the [merge request guidelines for contributors](https://docs.gitlab.com/ee/development/contributing/merge_request_workflow.html#merge-request-guidelines-for-contributors).
-1. Carefully review the [commit message guidelines](https://docs.gitlab.com/ee/development/contributing/merge_request_workflow.html#commit-messages-guidelines).
-1. Create a merge request for the issue:
- - If you were not assigned the issue, do not create a merge request. It will not be accepted.
- - If this is for a Hackathon, do not create the merge request before the Hackathon has started
- or it will not be counted towards the Hackathon.
- - Unless otherwise stated below, we expect one merge request per issue, so combine
- all changes together. If there is too much work for you to handle in one merge request,
- you can create more, but try to keep the number of merge requests as small as possible.
- - Select the **Documentation** merge request description template, and fill it out
- with the details of your work.
- - Copy the link to this issue and add it to the merge request's description,
- which links the merge request and the issue together.
-1. After your merge request is accepted and merged, close this issue.
+## Hi community contributors! :wave:
-If you notice things you'd like to fix that are not part of the issue, open separate merge requests for those issues.
+Do you want to work on this issue?
-We're sorry for all the rules but we want everyone to have a good experience, and it can be hard when we get an influx of contributions.
+- **If the issue is unassigned**, in a comment, type `@docs-hackathon I would like to work on this issue` and a writer will assign it to you.
-Thank you again for contributing to the GitLab documentation!
+- **If the issue is assigned to someone already**, choose another issue. Do not open a merge request for this issue if you are not assigned.
-## Identified documentation issue
+## To resolve the issue
-<!--
-* Include information about the issue that needs resolution. If the item is from an automated test,
-* be sure to include a copy/paste from the the test results. [This issue](https://gitlab.com/gitlab-org/gitlab/-/issues/339543) is an example of text to include with a Vale issue.
-*
-* Limit the identified work to be related to one another, and keep it to a reasonable amount. For example,
-* several moderate changes on one page, a few intermediate changes across five pages, or several very small
-* changes for up to 10 pages. Larger items should be broken out into other issues to better distribute
-* the opportunities for contributors.
-*
-* If you expect the work to take more than one MR to resolve, explain approximately
-* how many MRs you expect to receive for the issue.
--->
-
-## Additional information
-
-<!--
-* Any concepts, procedures, reference info we could add to make it easier to successfully use GitLab?
-* Include use cases, benefits, and/or goals for this work.
-* If adding content: What audience is it intended for? (What roles and scenarios?)
- For ideas, see personas at https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/ or the persona labels at
- https://gitlab.com/groups/gitlab-org/-/labels?subscribed=&search=persona%3A
--->
+[Follow these instructions to create a merge request](https://docs.gitlab.com/ee/development/documentation/workflow.html#how-to-update-the-docs).
-### Who can address the issue
+- Don't submit your merge request until after the Hackathon has started.
+- Try to address the issue in a single merge request.
+- Try to stick to the scope of the issue. If you see other improvements that can be made in the file, open a separate merge request.
+- When you create the merge request, select the **Documentation** merge request description template.
+- In the merge request's description, add a link to this issue.
+- Follow the [commit message guidelines](https://docs.gitlab.com/ee/development/contributing/merge_request_workflow.html#commit-messages-guidelines).
+ Use three to five words for your commit message, start with message with a capital letter, and do **not** end it in a period.
+ Other commit messages can cause the pipeline to fail.
-<!-- What if any special expertise is required to resolve this issue? -->
+Thank you again for contributing to the GitLab documentation! :tada:
-### Other links/references
+## Documentation issue
-<!-- For example, related GitLab issues/MRs -->
-/label ~documentation
diff --git a/.gitlab/issue_templates/Feature Flag Roll Out.md b/.gitlab/issue_templates/Feature Flag Roll Out.md
index 40ee1c125da..3972368ddc4 100644
--- a/.gitlab/issue_templates/Feature Flag Roll Out.md
+++ b/.gitlab/issue_templates/Feature Flag Roll Out.md
@@ -66,9 +66,12 @@ _Consider adding links to check for Sentry errors, Production logs for 5xx, 302s
## Rollout Steps
+Note: Please make sure to run the chatops commands in the slack channel that gets impacted by the command.
+
### Rollout on non-production environments
-- Ensure that the feature MRs have been deployed to non-production environments.
+- [ ] Verify the MR with the feature flag is merged to master.
+- Verify that the feature MRs have been deployed to non-production environments with:
- [ ] `/chatops run auto_deploy status <merge-commit-of-your-feature>`
- [ ] Enable the feature globally on non-production environments.
- [ ] `/chatops run feature set <feature-flag-name> true --dev --staging --staging-ref`
@@ -79,13 +82,16 @@ _Consider adding links to check for Sentry errors, Production logs for 5xx, 302s
### Specific rollout on production
+For visibility, all `/chatops` commands that target production should be executed in the `#production` slack channel and cross-posted (with the command results) to the responsible team's slack channel (`#g_TEAM_NAME`).
+
- Ensure that the feature MRs have been deployed to both production and canary.
- [ ] `/chatops run auto_deploy status <merge-commit-of-your-feature>`
-- If you're using [project-actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors), you must enable the feature on these entries:
+- Depending on the [type of actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors) you are using, pick one of these options:
+ - If you're using **project-actor**, you must enable the feature on these entries:
- [ ] `/chatops run feature set --project=gitlab-org/gitlab,gitlab-org/gitlab-foss,gitlab-com/www-gitlab-com <feature-flag-name> true`
-- If you're using [group-actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors), you must enable the feature on these entries:
+ - If you're using **group-actor**, you must enable the feature on these entries:
- [ ] `/chatops run feature set --group=gitlab-org,gitlab-com <feature-flag-name> true`
-- If you're using [user-actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors), you must enable the feature on these entries:
+ - If you're using **user-actor**, you must enable the feature on these entries:
- [ ] `/chatops run feature set --user=<your-username> <feature-flag-name> true`
- [ ] Verify that the feature works on the specific entries. Posting the QA result in this issue is preferable.
@@ -124,7 +130,7 @@ To do so, follow these steps:
- [ ] Create a merge request with the following changes. Ask for review and merge it.
- [ ] Set the `default_enabled` attribute in [the feature flag definition](https://docs.gitlab.com/ee/development/feature_flags/#feature-flag-definition-and-validation) to `true`.
- - [ ] Create [a changelog entry](https://docs.gitlab.com/ee/development/feature_flags/#changelog).
+ - [ ] Review [what warrants a changelog entry](https://docs.gitlab.com/ee/development/changelog.html#what-warrants-a-changelog-entry) and decide if [a changelog entry](https://docs.gitlab.com/ee/development/feature_flags/#changelog) is needed.
- [ ] Ensure that the default-enabling MR has been included in the release package.
If the merge request was deployed before [the monthly release was tagged](https://about.gitlab.com/handbook/engineering/releases/#self-managed-releases-1),
the feature can be officially announced in a release blog post.
@@ -165,7 +171,7 @@ You can either [create a follow-up issue for Feature Flag Cleanup](https://gitla
the feature can be officially announced in a release blog post.
- [ ] `/chatops run release check <merge-request-url> <milestone>`
- [ ] Close [the feature issue][main-issue] to indicate the feature will be released in the current milestone.
-- [ ] If not already done, clean up the feature flag from all environments by running these chatops command in `#production` channel:
+- [ ] Clean up the feature flag from all environments by running these chatops command in `#production` channel:
- [ ] `/chatops run feature delete <feature-flag-name> --dev --staging --staging-ref --production`
- [ ] Close this rollout issue.
diff --git a/.gitlab/issue_templates/Geo Replicate a new Git repository type.md b/.gitlab/issue_templates/Geo Replicate a new Git repository type.md
index 2348fa5b86f..571b0db0a30 100644
--- a/.gitlab/issue_templates/Geo Replicate a new Git repository type.md
+++ b/.gitlab/issue_templates/Geo Replicate a new Git repository type.md
@@ -94,11 +94,19 @@ Geo secondary sites have a [Geo tracking database](https://gitlab.com/gitlab-org
- [ ] If deviating from the above example, then be sure to order columns according to [our guidelines](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/development/ordering_table_columns.md).
-- [ ] Add the new table to the GitLab Schema defined in [`ee/lib/ee/gitlab/database/gitlab_schemas.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/ee/gitlab/database/gitlab_schemas.yml).
-
- ```yaml
- cool_widget_registry: :gitlab_geo
- ```
+- [ ] Add the new table to the [database dictionary](database_dictionary.md) defined in [`ee/db/docs/`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/ee/db/docs):
+
+ ```yaml
+ table_name: cool_widget_registry
+ description: Description example
+ introduced_by_url: Merge request link
+ milestone: Milestone example
+ feature_categories:
+ - Feature category example
+ classes:
+ - Class example
+ gitlab_schema: gitlab_geo
+ ```
- [ ] Run Geo tracking database migrations:
@@ -157,11 +165,19 @@ The Geo primary site needs to checksum every replicable so secondaries can verif
- [ ] If deviating from the above example, then be sure to order columns according to [our guidelines](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/development/ordering_table_columns.md).
-- [ ] Add the new table to the GitLab Schema defined in [`lib/gitlab/database/gitlab_schemas.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/database/gitlab_schemas.yml) with the databases they need to be added to.
-
- ```yaml
- cool_widget_states: :gitlab_main
- ```
+- [ ] Add the new table to the [database dictionary](database_dictionary.md) defined in [`db/docs/`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/db/docs):
+
+ ```yaml
+ table_name: cool_widget_states
+ description: Description example
+ introduced_by_url: Merge request link
+ milestone: Milestone example
+ feature_categories:
+ - Feature category example
+ classes:
+ - Class example
+ gitlab_schema: gitlab_main
+ ```
- [ ] Run database migrations:
@@ -316,6 +332,15 @@ That's all of the required database changes.
true
end
+ override :housekeeping_enabled?
+ def self.housekeeping_enabled?
+ # Remove this method if the new Git repository type supports git
+ # repository housekeeping and the ::CoolWidget#git_garbage_collect_worker_klass
+ # is implemented. If the data type requires any action to be performed
+ # before running the housekeeping override the `before_housekeeping` method
+ # (see `RepositoryReplicatorStrategy#before_housekeeping`)
+ false
+ end
end
end
```
diff --git a/.gitlab/issue_templates/Geo Replicate a new blob type.md b/.gitlab/issue_templates/Geo Replicate a new blob type.md
index 2bb8918df60..121dbdf035f 100644
--- a/.gitlab/issue_templates/Geo Replicate a new blob type.md
+++ b/.gitlab/issue_templates/Geo Replicate a new blob type.md
@@ -94,11 +94,19 @@ Geo secondary sites have a [Geo tracking database](https://gitlab.com/gitlab-org
- [ ] If deviating from the above example, then be sure to order columns according to [our guidelines](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/development/ordering_table_columns.md).
-- [ ] Add the new table to the GitLab Schema defined in [`ee/lib/ee/gitlab/database/gitlab_schemas.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/ee/gitlab/database/gitlab_schemas.yml).
-
- ```yaml
- cool_widget_registry: :gitlab_geo
- ```
+- [ ] Add the new table to the [database dictionary](database_dictionary.md) defined in [`ee/db/docs/`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/ee/db/docs):
+
+ ```yaml
+ table_name: cool_widget_registry
+ description: Description example
+ introduced_by_url: Merge request link
+ milestone: Milestone example
+ feature_categories:
+ - Feature category example
+ classes:
+ - Class example
+ gitlab_schema: gitlab_geo
+ ```
- [ ] Run Geo tracking database migrations:
@@ -159,11 +167,19 @@ The Geo primary site needs to checksum every replicable so secondaries can verif
- [ ] If deviating from the above example, then be sure to order columns according to [our guidelines](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/development/ordering_table_columns.md).
-- [ ] Add the new table to the GitLab Schema defined in [`lib/gitlab/database/gitlab_schemas.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/database/gitlab_schemas.yml) with the databases they need to be added to.
-
- ```yaml
- cool_widget_states: :gitlab_main
- ```
+- [ ] Add the new table to the database dictionary defined in [`db/docs/`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/db/docs):
+
+ ```yaml
+ table_name: cool_widget_states
+ description: Description example
+ introduced_by_url: Merge request link
+ milestone: Milestone example
+ feature_categories:
+ - Feature category example
+ classes:
+ - Class example
+ gitlab_schema: gitlab_main
+ ```
- [ ] Run database migrations:
diff --git a/.gitlab/issue_templates/Global Search - bug.md b/.gitlab/issue_templates/Global Search - bug.md
new file mode 100644
index 00000000000..2f568fe32bc
--- /dev/null
+++ b/.gitlab/issue_templates/Global Search - bug.md
@@ -0,0 +1,30 @@
+## Summary
+
+<!-- Summarize the bug encountered concisely. -->
+
+## Steps to reproduce
+
+<!-- Describe how one can reproduce the issue - this is very important. Please use an ordered list. -->
+
+## What is the current *bug* behavior?
+
+<!-- Describe what actually happens. -->
+
+## What is the expected *correct* behavior?
+
+<!-- Describe what you should see instead. -->
+
+## Relevant logs and/or screenshots
+
+<!-- Paste any relevant logs - please use code blocks (```) to format console output, logs, and code
+ as it's tough to read otherwise. -->
+
+## Possible fixes
+
+<!-- If you can, link to the line of code that might be responsible for the problem. -->
+
+<!-- Please add a label for the type of bug as per https://about.gitlab.com/handbook/engineering/metrics/#work-type-classification -->
+/label ~"type::bug"
+/label ~"group::global search"
+/label ~"workflow::solution validation"
+/milestone %Backlog \ No newline at end of file
diff --git a/.gitlab/issue_templates/Global Search - feature.md b/.gitlab/issue_templates/Global Search - feature.md
new file mode 100644
index 00000000000..ebc4248b7fd
--- /dev/null
+++ b/.gitlab/issue_templates/Global Search - feature.md
@@ -0,0 +1,13 @@
+## Problem to solve
+
+<!-- What problem do we solve? Try to define the who/what/why of the opportunity as a user story. For example, "As a (who), I want (what), so I can (why/value)." -->
+
+## Proposal
+
+<!-- Use this section to explain the feature and how it will work. It can be helpful to add technical details, design proposals, and links to related epics or issues. -->
+
+<!-- Please add a label for the type of feature as per https://about.gitlab.com/handbook/engineering/metrics/#work-type-classification -->
+/label ~"type::feature"
+/label ~"group::global search"
+/label ~"workflow::solution validation"
+/milestone %Backlog \ No newline at end of file
diff --git a/.gitlab/issue_templates/Global Search - maintenance.md b/.gitlab/issue_templates/Global Search - maintenance.md
new file mode 100644
index 00000000000..38ce56479f2
--- /dev/null
+++ b/.gitlab/issue_templates/Global Search - maintenance.md
@@ -0,0 +1,11 @@
+## Background
+
+## Proposal
+
+<!-- Use this section to explain the feature and how it will work. It can be helpful to add technical details, design proposals, and links to related epics or issues. -->
+
+<!-- Please add a label for the type of maintenance as per https://about.gitlab.com/handbook/engineering/metrics/#work-type-classification -->
+/label ~"type::maintenance"
+/label ~"group::global search"
+/label ~"workflow::solution validation"
+/milestone %Backlog \ No newline at end of file
diff --git a/.gitlab/issue_templates/Navigation - Left Sidebar Proposals.md b/.gitlab/issue_templates/Navigation - Left Sidebar Proposals.md
deleted file mode 100644
index 3939fca44f1..00000000000
--- a/.gitlab/issue_templates/Navigation - Left Sidebar Proposals.md
+++ /dev/null
@@ -1,14 +0,0 @@
-<!-- This template is used for proposing changes to the left sidebar contextual navigation. This could include additions, removals, or general changes to overall hierarchy.-->
-
-### Proposal
-
-<!-- Use this section to explain the proposed changes, including details around usage and business drivers. -->
-
-### Checklist
-
-- [ ] If your proposal includes changes to the menu items within the left sidebar, engage the [Foundations Product Manager](https://about.gitlab.com/handbook/product/categories/#foundations-group) for approval. The Foundations DRI will work with UX partners in product design, research, and technical writing, as applicable.
-- [ ] Follow the [product development workflow](https://about.gitlab.com/handbook/product-development-flow/#validation-phase-2-problem-validation) validation process to ensure you are solving a well understood problem and that the proposed change is understandable and non-disruptive to users. Navigation-specific research is strongly encouraged.
-- [ ] Engage the [Foundations](https://about.gitlab.com/handbook/product/categories/#foundations-group) team to ensure your proposal is in alignment with holistic changes happening to the left side bar.
-- [ ] Consider whether you need to communicate the change somehow, or if you will have an interim period in the UI where your nav item will live in more than one place.
-
-/label ~UX ~"UI text" ~"documentation" ~"documentation" ~"Category:Navigation & Settings" ~"Category:Foundations" ~navigation
diff --git a/.gitlab/issue_templates/Navigation Proposals.md b/.gitlab/issue_templates/Navigation Proposals.md
new file mode 100644
index 00000000000..72c8f43cc97
--- /dev/null
+++ b/.gitlab/issue_templates/Navigation Proposals.md
@@ -0,0 +1,15 @@
+<!-- This template is used for proposing changes to the left sidebar contextual navigation. This could include additions, removals, or general changes to overall hierarchy.-->
+
+### Proposal
+
+<!-- Use this section to explain the proposed changes, including details around usage and business drivers. -->
+
+### Checklist
+
+- [ ] Add relevant information to the issue description detailing your proposal, including usage and business drivers.
+- [ ] Follow the [product development workflow](https://about.gitlab.com/handbook/product-development-flow/#validation-phase-2-problem-validation) validation process to ensure you are solving a well understood problem and that the proposed change is understandable and non-disruptive to users. Navigation-specific research is strongly encouraged.
+- [ ] Engage the [Foundations Product Manager](https://about.gitlab.com/handbook/product/categories/#foundations-group) for approval. The Foundations DRI will work with UX partners in product design, research, and technical writing, as applicable.
+- [ ] Engage the [Foundations](https://about.gitlab.com/handbook/product/categories/#foundations-group) team to ensure your proposal is in alignment with holistic changes happening to the left side bar.
+- [ ] Consider whether you need to communicate the change somehow, or if you will have an interim period in the UI where your nav item will live in more than one place.
+
+/label ~UX ~"UI text" ~"documentation" ~"documentation" ~"Category:Navigation & Settings" ~"Category:Foundations" ~navigation
diff --git a/.gitlab/issue_templates/Security developer workflow.md b/.gitlab/issue_templates/Security developer workflow.md
index daad4c19803..7c6c86f5e78 100644
--- a/.gitlab/issue_templates/Security developer workflow.md
+++ b/.gitlab/issue_templates/Security developer workflow.md
@@ -10,11 +10,11 @@ Set the title to: `Description of the original issue`
- [ ] Read the [security process for developers] if you are not familiar with it.
- [ ] Make sure the [issue really needs to follow the security release workflow].
+- [ ] Add a `~severity::x` label to the issue and all associated merge requests.
- [ ] **IMPORTANT**: Mark this [issue as linked] to the Security Release Tracking Issue. You can find it [here](https://gitlab.com/gitlab-org/gitlab/-/issues?sort=created_date&state=opened&label_name[]=upcoming+security+release). This issue
MUST be linked for the release bot to know that the associated merge requests should be merged for this security release.
- Fill out the [Links section](#links):
- [ ] Next to **Issue on GitLab**, add a link to the `gitlab-org/gitlab` issue that describes the security vulnerability.
-- [ ] Add one of the `~severity::x` labels to the issue and all associated merge requests.
- [ ] If this change affects the public interface (public API or UI) of the product, post in the `#support_gitlab-com` Slack channel to explain the impact and discuss a mitigation plan for users that might be affected. If you need Support feedback or approval, reach out in `#spt_managers` Slack channel or mention `@gitlab-com/support/managers`.
## Development
@@ -38,6 +38,8 @@ After your merge request has been approved according to our [approval guidelines
## Documentation and final details
+- [ ] To avoid release delays, please nominate a developer in a different timezone who will be able to respond to any pipeline or merge failures in your absence `@gitlab-username`
+- [ ] Ensure `~severity::x` label is on this issue, all associated issues, and merge requests
- [ ] Ensure the [Links section](#links) is completed.
- [ ] Add the GitLab [versions](https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/security/developer.md#versions-affected) and editions affected to the [details section](#details)
* The Git history of the files affected may help you associate the issue with a [release](https://about.gitlab.com/releases/)
diff --git a/.gitlab/issue_templates/UX Theme.md b/.gitlab/issue_templates/UX Theme.md
index 2ba60523e20..b015c3d44e6 100644
--- a/.gitlab/issue_templates/UX Theme.md
+++ b/.gitlab/issue_templates/UX Theme.md
@@ -1,11 +1,11 @@
-<!-- A majority of the work designers do will be on themes in the (Now) Next 1-3 milestone column. These themes are comprised of high-confidence outcomes and validated needs. The UX theme issue is where collaboration should occur, including plans and discussion on subthemes, research, and design feedback. Related issues for design exploration and solution validation should stem from the theme issue.
+<!-- A majority of the work designers do will be on themes in the (Now) Next 1-3 milestone column of their UX Roadmap. These themes are comprised of high-confidence outcomes and validated needs. The UX theme issue is where collaboration should occur, including plans and discussion on subthemes, research, and design feedback. Related issues for design exploration and solution validation should stem from the theme issue.
One of the advantages of working with UX themes is that it allows us to think and design holistically by designing the theme as a whole as opposed to a single issue at a time trying to piece them together as you go. For more details please refer to this section of the handbook when creating UX Themes: https://about.gitlab.com/handbook/product/ux/product-design/ux-roadmaps/#theme-structure -->
-### UX Theme
-<!-- A theme is written as a statement that combines the beneficiary, their need, and the expected outcome when the work is delivered. Well-defined statements are concise without sacrificing the substance of the theme so that anyone can understand it at a glance. (For instance; Reduce the effort for security teams to identify and escalate business-critical risks)
+<!-- Theme Issue Title {UX Theme: <theme statement here>} -->
+<!-- Theme Statement: A theme is written as a statement that combines the beneficiary, their need, and the expected outcome when the work is delivered. Well-defined statements are concise without sacrificing the substance of the theme so that anyone can understand it at a glance. (For instance; Reduce the effort for security teams to identify and escalate business-critical risks)
-!!Note: The theme statement is the defacto title that will be used to reference the theme and serve as the theme issue title.!!
+!!Note: The theme statement is the defacto title that will be used to reference the theme and serve as the theme issue title.!! It should be something that is easily understood, that quickly communicates the intent of the theme allowing team members to easily understand and recognize the expected work that will be done.
-->
----
@@ -21,8 +21,8 @@ One of the advantages of working with UX themes is that it allows us to think an
#### Need & Primary JTBD
<!-- What is the JTBD and what are the needs related to the beneficiary and theme?
-- JTBD = The JTBD statement, for instance, (When I am triaging vulns, I want to address business-critical risks, So I can ensure there is no unattended risk in my orgs assets.)
-- Need = Abstracted from the JTBD, for instance, (Identify and escalate business-critical risks detected in my orgs assets.)
+- JTBD: The JTBD statement, for instance, (When I am triaging vulns, I want to address business-critical risks, So I can ensure there is no unattended risk in my orgs assets.)
+- Need: Abstracted from the JTBD, for instance, (Identify and escalate business-critical risks detected in my orgs assets.)
-->
- **JTBD:**
@@ -44,16 +44,29 @@ One of the advantages of working with UX themes is that it allows us to think an
| --- | --- |
| [High/Medium/Low] | [research/insight issue](Link) |
+### User-stories
+<!-- Product designers should work with their PMs to gather up all of the relevant user stories. Look for alignment with the JTBD added above. Overall, the solution you and your team come up with should help to support the user stories. -->
-### Subthemes & Requirements
-<!-- Subthemes are more granular validated needs, goals, and additional details that the theme encompasses. These are typically reserved for themes in the next (1-3 milestones) column. Subthemes may also consist of existing feature or design issues that exist in GitLab and directly relate to the theme. Subthemes answer “how†we are going to solve the user need while the theme itself answers “what†the need is and “who†will be benefiting from the solution.
+- [user-story here]
+- [user-story here]
+- [user-story here]
+- [etc.]
-Note: This is not a backlog. If the subthemes can not be delivered in the theme timeframe then the theme is too big and needs to be broken down into multiple themes. -->
+### Requirements
+<!-- Requirements can be taken from existing features or design issues that were used to build this theme. Any related issues should be linked with this issue in the Feature/solution issues section below. They are more granular validated needs, goals, and additional details that the theme encompasses. These are typically reserved for themes in the next (1-3 milestones) column. Requirements should answer “what†the beneficiary of this theme needs from the solution.
-#### Feature/solution subthemes
-<!-- Use this table to track feature issues related to this theme (if applicable). Not all themes require subthemes as subthemes are typically discovered while working on the theme itself. Think of subthemes as if they were the result of design breaking down the issue into discrete work items.
+Note: This is not a backlog. If the issue can not be delivered in the theme timeframe then the theme is too big and needs to be broken down into multiple themes. -->
-Note: if feature issues already exist then you can add them to this table. Keep in mind that subthemes require validation if they are assumptive
+The beneficieray needs to be able to:
+- [need here]
+- [need here]
+- [need here]
+- [etc.]
+
+#### Feature/solution issues
+<!-- Use this table to track feature issues related to this theme (if applicable). Not all themes require sub-issues as they are typically discovered while working on the theme itself. Think of these issues as if they were the result of breaking down the design into discrete work items.
+
+Note: if feature issues already exist then you can add them to this table. Keep in mind that these issues will require validation if they are being added to a Theme that's in the Next (1-3 milestones) container and are assumptive.
Refer to https://about.gitlab.com/handbook/product/ux/product-designer/#ux-issue-weights for calculating UX weights.
-->
@@ -64,7 +77,7 @@ Refer to https://about.gitlab.com/handbook/product/ux/product-designer/#ux-issue
| [Issue](link) | `0 - 10` |
| [Issue](link) | `0 - 10` |
-#### Research subthemes
+#### Research
<!-- Use this table to track UX research related to this theme. This may include, problem validation and/or solution validation activities.
-->
@@ -73,5 +86,17 @@ Refer to https://about.gitlab.com/handbook/product/ux/product-designer/#ux-issue
| [Issue]() | <!--Solution validation, Problem validation, etc., --> | <!-- Planned, In Progress, Complete, etc.,--> |
| [Issue]() | <!--Solution validation, Problem validation, etc., --> | <!-- Planned, In Progress, Complete, etc.,--> |
+#### Ready for design checklist
+The items are self-check suggestions; they could be contributed by designers, product managers or researchers
+* [ ] The stated `Problem to solve` has high confidence (derived from research or other data-gathering techniques)
+* [ ] Relevant issues, research, and other background information are linked to the Related issues section
+* [ ] The stated `Beneficiary` has been defined
+* [ ] There is high confidence in the stated `Need & Primary JTBD` (derived from research or other data gathering techniques)
+* [ ] The `Expected outcome` has been defined
+* [ ] The `Business objective` has been defined
+* [ ] The theme `Confidence` has been defined as High
+* [ ] `User-stories` have been defined
+* [ ] The `Requirements` have been defined and the scope has been agreed upon
+* [ ] This UX Theme contains everyhting necessary to complete a design solution and is ready for design
/label ~"UX" ~"UX Theme"
diff --git a/.gitlab/merge_request_templates/Default.md b/.gitlab/merge_request_templates/Default.md
index ecb8505e0df..f670882a72f 100644
--- a/.gitlab/merge_request_templates/Default.md
+++ b/.gitlab/merge_request_templates/Default.md
@@ -44,3 +44,5 @@ This checklist encourages us to confirm any changes have been analyzed to reduce
* [ ] I have evaluated the [MR acceptance checklist](https://docs.gitlab.com/ee/development/code_review.html#acceptance-checklist) for this MR.
<!-- template sourced from https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/merge_request_templates/Default.md -->
+
+/assign me
diff --git a/.gitlab/merge_request_templates/Deprecations.md b/.gitlab/merge_request_templates/Deprecations.md
index 6a5ee6e74e7..9b84dde72df 100644
--- a/.gitlab/merge_request_templates/Deprecations.md
+++ b/.gitlab/merge_request_templates/Deprecations.md
@@ -43,6 +43,7 @@ They are frequently updated, and everyone should make sure they are aware of the
- [ ] Set yourself as the Assignee, meaning you are the DRI.
- [ ] If the deprecation is a [breaking change](https://about.gitlab.com/handbook/product/gitlab-the-product/#breaking-change), add label `breaking change`.
- [ ] Follow the process to [create a deprecation YAML file](https://about.gitlab.com/handbook/marketing/blog/release-posts/#creating-a-deprecation-entry).
+- [ ] Make sure that the milestone dates are based on the dates in [Product milestone creation](https://about.gitlab.com/handbook/product/milestones/#product-milestone-creation).
- [ ] Add reviewers by the 10th.
- [ ] When ready to be merged and not later than the 15th, add the `~ready` label and @ message the TW for final review and merge.
@@ -73,7 +74,8 @@ yourself as a reviewer if it's not ready for merge yet.
- [ ] Title:
- Length limit: 7 words (not including articles or prepositions).
- Capitalization: ensure the title is [sentence cased](https://design.gitlab.com/content/punctuation#case).
- - Rewrite to exclude the words `deprecation`, `deprecate`, `removal`, and `remove` if necessary.
+- [ ] Dates:
+ - Make sure that the milestone dates are based on the dates in [Product milestone creation](https://about.gitlab.com/handbook/product/milestones/#product-milestone-creation).
- [ ] Consistency:
- Ensure that all resources (docs, deprecation, etc.) refer to the feature with the same term / feature name.
- [ ] Content:
diff --git a/.gitlab/merge_request_templates/New Version of gitlab-styles.md b/.gitlab/merge_request_templates/New Version of gitlab-styles.md
new file mode 100644
index 00000000000..e6cde691e19
--- /dev/null
+++ b/.gitlab/merge_request_templates/New Version of gitlab-styles.md
@@ -0,0 +1,45 @@
+<!-- Title suggestion: Upgrade `gilab-styles` to <VERSION X.Y.Z> - dry-run -->
+
+## What does this MR do and why?
+
+Validating upcoming release of `gitlab-styles` <VERSION X.Y.Z>. See <LINK TO RELEASE MR>.
+
+This MR can be reused to upgrade `gitlab-styles` in this project after a new version of `gitlab-styles` is released.
+### Checklist
+
+- [ ] Verify upcoming release of `gitlab-styles`
+ - [ ] Point to "Release" MR of `gitlab-styles` in `Gemfile`
+ - For example, `gem 'gitlab-styles', '~> 9.1.0', require: false, git: 'https://gitlab.com/gitlab-org/ruby/gems/gitlab-styles.git', ref: 'ddieulivol-upgrade_to_9.1.0'`
+ - [ ] Update [bundler's checksum file](https://docs.gitlab.com/ee/development/gemfile.html#updating-the-checksum-file) via `bundle exec bundler-checksum init`
+ - [ ] `rubocop` job
+ - [ ] Inspect any warnings/errors
+ - [ ] (Optional) [Generate TODOs](https://docs.gitlab.com/ee/development/contributing/style_guides.html#resolving-rubocop-exceptions) for pending offenses
+ - [ ] Put :new: cop rules (or if configuration is changed) in "grace period". See [docs](https://docs.gitlab.com/ee/development/contributing/style_guides.html#enabling-a-new-cop).
+ - [ ] (Optional) Remove any offenses for disabled cops
+ - Use `grep --perl-regexp -o ":\d+\d+: \w: \[\S+\] ([\w/]+)" raw_job_output.log | awk '{print $4}' | sort | uniq -c` to get a list of cop rules with offenses. Where `raw_job_output.log` is the raw output of the `rubocop` job
+ - [ ] Ignore offenses related to temporary changes in `Gemfile`
+ - [ ] (Optional) Autocorrect offenses
+ - [ ] Compare the total runtime of `rubocop --parallel` scan with previous runs
+ - [ ] Make sure CI passes :green_heart:
+ - [ ] Don't merge this MR yet!
+ - [ ] Wait for `gitlab-styles` to be released
+- [ ] Upgrade released version of `gitlab-styles`
+ - [ ] Make sure release is complete
+ - [ ] Rephrase the title and MR description to match final upgrade
+ - [ ] Point to released version in `Gemfile`
+ - [ ] `gem 'gitlab-styles', '~> 9.1.0', require: false`
+ - [ ] Update [bundler's checksum file](https://docs.gitlab.com/ee/development/gemfile.html#updating-the-checksum-file) via `bundle exec bundler-checksum init`
+ - [ ] (Optional) Regenerate TODOs for new/changed cop rules
+ - [ ] Make sure CI passes :green_heart:
+ - [ ] Let the MR being reviewed again and merged
+ - [ ] Make sure CI passes :green_heart:
+ - [ ] Let the MR being reviewed again and merged
+ - [ ] (Optional) Refine this [MR template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/merge_request_templates/New%20Version%20of%20gitlab-styles.md).
+
+## MR acceptance checklist
+
+This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
+
+* [ ] I have evaluated the [MR acceptance checklist](https://docs.gitlab.com/ee/development/code_review.html#acceptance-checklist) for this MR.
+
+/label ~"type::maintenance" ~"maintenance::dependency" ~backend ~"Engineering Productivity" ~"static code analysis"
diff --git a/.gitlab/merge_request_templates/Removals.md b/.gitlab/merge_request_templates/Removals.md
index 6f31f3cefd2..f616df56480 100644
--- a/.gitlab/merge_request_templates/Removals.md
+++ b/.gitlab/merge_request_templates/Removals.md
@@ -46,6 +46,7 @@ Please review:
- [ ] Set yourself as the Assignee, meaning you are the DRI.
- [ ] If the removal is a [breaking change](https://about.gitlab.com/handbook/product/gitlab-the-product/#breaking-change), add label `breaking change`.
- [ ] Follow the process to [create a removal YAML file](https://about.gitlab.com/handbook/marketing/blog/release-posts/#creating-a-removal-entry).
+- [ ] Make sure that the milestone dates are based on the dates in [Product milestone creation](https://about.gitlab.com/handbook/product/milestones/#product-milestone-creation).
- [ ] Add reviewers by the 10th.
- [ ] When ready to be merged and not later than the 15th, add the ~ready label and @ message the TW for final review and merge.
- Removal notices should not be merged before the code is removed from the product. Do not mark ~ready until the removal is complete, or you are certain it will be completed within the current milestone and released. If PMs are not sure, they should confirm with their Engineering Manager.
@@ -78,7 +79,8 @@ yourself as a reviewer if it's not yet ready for merge.
- [ ] Title:
- Length limit: 7 words (not including articles or prepositions).
- Capitalization: ensure the title is [sentence cased](https://design.gitlab.com/content/punctuation#case).
- - Rewrite to exclude the words `removal` and `remove` if necessary.
+- [ ] Dates:
+ - Make sure that the milestone dates are based on the dates in [Product milestone creation](https://about.gitlab.com/handbook/product/milestones/#product-milestone-creation).
- [ ] Consistency:
- Ensure that all resources (docs, removal, etc.) refer to the feature with the same term / feature name.
- [ ] Content:
diff --git a/.gitlab/merge_request_templates/Revert To Resolve Incident.md b/.gitlab/merge_request_templates/Revert To Resolve Incident.md
new file mode 100644
index 00000000000..17ff239bbd4
--- /dev/null
+++ b/.gitlab/merge_request_templates/Revert To Resolve Incident.md
@@ -0,0 +1,14 @@
+## Purpose of Revert
+
+<!-- Please link to the relevant incident -->
+
+### Check-list
+
+- [ ] Create an issue to reinstate the merge request and assign it to the author of the reverted merge request.
+- [ ] If the revert is to resolve a ['broken master' incident](https://about.gitlab.com/handbook/engineering/workflow/#broken-master), please read through the [Responsibilities of the Broken 'Master' resolution DRI](https://about.gitlab.com/handbook/engineering/workflow/#responsibilities-of-the-resolution-dri)
+- [ ] Add the appropriate labels **before** the MR is created (we can only skip CI/CD jobs if the labels are added **before** the CI/CD pipeline gets created)
+
+/label ~"pipeline:expedite" ~"master:broken"
+
+<!-- If applicable, specifying the regression label in the current milestone will skip additional CI/CD jobs (e.g. Danger changelog checks) -->
+<!-- /label ~regression: -->
diff --git a/.gitlab/merge_request_templates/Security Release.md b/.gitlab/merge_request_templates/Security Release.md
index 14130ca42c2..334bcb16023 100644
--- a/.gitlab/merge_request_templates/Security Release.md
+++ b/.gitlab/merge_request_templates/Security Release.md
@@ -25,6 +25,7 @@ See [the general developer security release guidelines](https://gitlab.com/gitla
- [ ] For a backport MR targeting a versioned stable branch (`X-Y-stable-ee`).
- [ ] Milestone is set to the version this backport applies to. A closed milestone can be assigned via [quick actions].
- [ ] Ensure it's approved by a maintainer.
+- [ ] Ensure this merge request and the related security issue have a `~severity::x` label
**Note:** Reviewer/maintainer should not be a Release Manager.
@@ -32,6 +33,7 @@ See [the general developer security release guidelines](https://gitlab.com/gitla
- [ ] Correct milestone is applied and the title is matching across all backports.
- [ ] Assigned (_not_ as reviewer) to `@gitlab-release-tools-bot` with passing CI pipelines.
+- [ ] Correct `~severity::x` label is applied to this merge request and the related security issue.
/label ~security
diff --git a/.lefthook/pre-push/merge_conflicts b/.lefthook/pre-push/merge_conflicts
new file mode 100755
index 00000000000..26623d93095
--- /dev/null
+++ b/.lefthook/pre-push/merge_conflicts
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# Adjusted from https://gitlab.com/fdegier/pre-push-hooks with hardcoded values for speed
+ORIGIN=origin
+DEFAULT_BRANCH=master
+
+if [[ -n "$ORIGIN" ]]
+then
+ # Pull the default branch from remote
+ git fetch --quiet origin "$DEFAULT_BRANCH":"$DEFAULT_BRANCH"
+fi
+
+# Check for merge conflicts and abort
+if git merge --autostash --no-commit --no-ff --no-edit "$DEFAULT_BRANCH" > /dev/null 2>&1
+then
+ # Able to merge without conflicts
+ git merge --abort > /dev/null 2>&1
+ exit 0
+else
+ echo "Merge conflicts detected when merging to $DEFAULT_BRANCH!"
+ git merge --abort > /dev/null 2>&1
+ exit 1
+fi
diff --git a/.rubocop.yml b/.rubocop.yml
index d0cf328e719..3a3150f9fbc 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -49,10 +49,20 @@ AllCops:
- 'db/ci_migrate/*.rb' # since the `db/ci_migrate` is a symlinked to `db/migrate`
# Use absolute path to avoid orphan directories with changed workspace root.
CacheRootDirectory: <%= Dir.getwd %>/tmp
- MaxFilesInCache: 35000
+ MaxFilesInCache: 1_000_000
NewCops: disable
SuggestExtensions: false
+Rails:
+ Exclude:
+ # User defined excludes on top (department) level don't cancel default
+ # includes set by child cops. Directories below are not affected (tested
+ # manually). Watch https://github.com/rubocop/rubocop/issues/11148 to
+ # know when this comment can be removed.
+ - 'danger/**/*'
+ - 'tooling/danger/**/*'
+ - 'rubocop/**/*'
+
RSpec:
Language:
Includes:
@@ -159,6 +169,7 @@ Naming/FileName:
- 'qa/tasks/**/*.rake'
- '**/*.ru'
- 'app/graphql/types/issue_connection.rb'
+ - 'app/graphql/types/group_connection.rb'
IgnoreExecutableScripts: true
AllowedAcronyms:
@@ -272,10 +283,6 @@ Rails/FindBy:
- 'spec/**/*.rb'
- 'ee/spec/**/*.rb'
-Rails/IndexBy:
- Exclude:
- - 'tooling/danger/**/*.rb'
-
Rails/InverseOf:
Include:
- app/models/**/*.rb
@@ -299,6 +306,10 @@ Rails/MailerName:
# See for the context on why it's excluded https://gitlab.com/gitlab-org/gitlab/-/issues/239356#note_956419227
- 'app/mailers/notify.rb'
+Rails/Pluck:
+ # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94047#note_1179689274
+ AutoCorrect: false
+
Rails/RakeEnvironment:
# Context on why it's disabled: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93419#note_1048223982
Enabled: false
@@ -399,6 +410,12 @@ Database/MultipleDatabases:
- 'spec/lib/gitlab/background_migration/**/*.rb'
- 'spec/lib/gitlab/database/**/*.rb'
+Migration/BatchMigrationsPostOnly:
+ Enabled: true
+ Include:
+ - 'db/migrate/*.rb'
+ - 'db/post_migrate/*.rb'
+
# See https://gitlab.com/gitlab-org/gitlab/-/issues/373194
Gitlab/RSpec/AvoidSetup:
Enabled: true
@@ -440,22 +457,6 @@ Cop/ActiveModelErrorsDirectManipulation:
Gitlab/AvoidFeatureGet:
Enabled: true
-RSpec/TimecopFreeze:
- Enabled: true
- AutoCorrect: true
- Include:
- - 'spec/**/*.rb'
- - 'ee/spec/**/*.rb'
- - 'qa/spec/**/*.rb'
-
-RSpec/TimecopTravel:
- Enabled: true
- AutoCorrect: true
- Include:
- - 'spec/**/*.rb'
- - 'ee/spec/**/*.rb'
- - 'qa/spec/**/*.rb'
-
RSpec/WebMockEnable:
Enabled: true
Include:
@@ -474,6 +475,14 @@ Naming/PredicateName:
Naming/RescuedExceptionsVariableName:
Enabled: false
+RSpec/AvoidTestProf:
+ Include:
+ - 'spec/migrations/**/*.rb'
+ - 'ee/spec/migrations/**/*.rb'
+ - 'spec/lib/gitlab/background_migration/**/*.rb'
+ - 'ee/spec/lib/gitlab/background_migration/**/*.rb'
+ - 'ee/spec/lib/ee/gitlab/background_migration/**/*.rb'
+
RSpec/FactoriesInMigrationSpecs:
Enabled: true
Include:
@@ -488,8 +497,16 @@ RSpec/FactoryBot/AvoidCreate:
Include:
- 'spec/presenters/**/*.rb'
- 'spec/serializers/**/*.rb'
+ - 'spec/helpers/**/*.rb'
+ - 'spec/views/**/*.rb'
+ - 'spec/components/**/*.rb'
+ - 'spec/mailers/**/*.rb'
- 'ee/spec/presenters/**/*.rb'
- 'ee/spec/serializers/**/*.rb'
+ - 'ee/spec/helpers/**/*.rb'
+ - 'ee/spec/views/**/*.rb'
+ - 'ee/spec/components/**/*.rb'
+ - 'ee/spec/mailers/**/*.rb'
RSpec/FactoryBot/StrategyInCallback:
Enabled: true
@@ -886,3 +903,11 @@ Rake/Require:
Include:
- '{,ee/,jh/}lib/**/*.rake'
- 'qa/tasks/**/*.rake'
+
+Cop/FeatureFlagUsage:
+ Include:
+ - 'lib/gitlab/redis/**/*.rb'
+ - 'lib/gitlab/patch/**/*.rb'
+ - 'lib/gitlab/instrumentation/**/*.rb'
+ Exclude:
+ - 'lib/gitlab/redis/multi_store.rb'
diff --git a/.rubocop_todo/cop/user_admin.yml b/.rubocop_todo/cop/user_admin.yml
index 653865e3d43..6f8cc5429c2 100644
--- a/.rubocop_todo/cop/user_admin.yml
+++ b/.rubocop_todo/cop/user_admin.yml
@@ -1,63 +1,57 @@
---
Cop/UserAdmin:
Exclude:
- - app/controllers/admin/impersonations_controller.rb
- - app/controllers/sessions_controller.rb
- - app/finders/autocomplete/routes_finder.rb
- - app/finders/ci/jobs_finder.rb
- - app/finders/ci/runners_finder.rb
- - app/finders/personal_access_tokens_finder.rb
- - app/finders/users_finder.rb
- - app/graphql/mutations/admin/sidekiq_queues/delete_jobs.rb
- - app/graphql/resolvers/admin/analytics/usage_trends/measurements_resolver.rb
- - app/helpers/application_helper.rb
- - app/helpers/import_helper.rb
- - app/helpers/nav_helper.rb
- - app/helpers/projects_helper.rb
- - app/helpers/search_helper.rb
- - app/helpers/users/callouts_helper.rb
- - app/helpers/users_helper.rb
- - app/helpers/visibility_level_helper.rb
- - app/models/concerns/protected_ref_access.rb
- - app/models/concerns/spammable.rb
- - app/models/issue_collection.rb
- - app/models/merge_requests_closing_issues.rb
- - app/models/protected_branch.rb
- - app/models/user.rb
- - app/policies/note_policy.rb
- - app/services/auth/container_registry_authentication_service.rb
- - app/services/emails/create_service.rb
- - app/services/projects/enable_deploy_key_service.rb
- - app/services/projects/fork_service.rb
- - app/services/users/build_service.rb
- - ee/app/controllers/ee/projects_controller.rb
- - ee/app/helpers/ee/dashboard_helper.rb
- - ee/app/helpers/ee/import_helper.rb
- - ee/app/helpers/ee/subscribable_banner_helper.rb
- - ee/app/helpers/ee/users/callouts_helper.rb
- - ee/app/helpers/license_monitoring_helper.rb
- - ee/app/helpers/push_rules_helper.rb
- - ee/app/models/concerns/ee/protected_ref_access.rb
- - ee/app/models/ee/user.rb
- - ee/app/models/protected_environment/deploy_access_level.rb
- - ee/app/policies/ee/group_policy.rb
- - ee/app/policies/ee/project_policy.rb
- - ee/app/services/ee/groups/create_service.rb
- - ee/app/services/ee/groups/update_service.rb
- - ee/app/services/ee/projects/update_service.rb
- - ee/lib/ee/api/helpers.rb
- - ee/lib/ee/gitlab/git_access.rb
- - lib/api/award_emoji.rb
- - lib/api/ci/runners.rb
- - lib/api/entities/ci/runner_details.rb
- - lib/api/groups.rb
- - lib/api/helpers.rb
- - lib/api/users.rb
- - lib/api/v3/github.rb
- - lib/constraints/admin_constrainer.rb
- - lib/gitlab/auth.rb
- - lib/gitlab/ci/runner_instructions.rb
- - lib/gitlab/import_export/members_mapper.rb
- - lib/gitlab/performance_bar.rb
- - lib/gitlab/visibility_level.rb
- - qa/qa/runtime/api/client.rb
+ - 'app/controllers/admin/impersonations_controller.rb'
+ - 'app/controllers/sessions_controller.rb'
+ - 'app/finders/autocomplete/routes_finder.rb'
+ - 'app/finders/ci/jobs_finder.rb'
+ - 'app/finders/ci/runners_finder.rb'
+ - 'app/finders/personal_access_tokens_finder.rb'
+ - 'app/finders/users_finder.rb'
+ - 'app/graphql/mutations/admin/sidekiq_queues/delete_jobs.rb'
+ - 'app/graphql/resolvers/admin/analytics/usage_trends/measurements_resolver.rb'
+ - 'app/helpers/application_helper.rb'
+ - 'app/helpers/import_helper.rb'
+ - 'app/helpers/projects_helper.rb'
+ - 'app/helpers/search_helper.rb'
+ - 'app/helpers/users/callouts_helper.rb'
+ - 'app/helpers/users_helper.rb'
+ - 'app/helpers/visibility_level_helper.rb'
+ - 'app/models/concerns/protected_ref_access.rb'
+ - 'app/models/concerns/spammable.rb'
+ - 'app/models/issue_collection.rb'
+ - 'app/models/merge_requests_closing_issues.rb'
+ - 'app/models/protected_branch.rb'
+ - 'app/models/user.rb'
+ - 'app/policies/note_policy.rb'
+ - 'app/services/auth/container_registry_authentication_service.rb'
+ - 'app/services/emails/create_service.rb'
+ - 'app/services/projects/enable_deploy_key_service.rb'
+ - 'app/services/projects/fork_service.rb'
+ - 'app/services/users/build_service.rb'
+ - 'ee/app/controllers/ee/projects_controller.rb'
+ - 'ee/app/helpers/ee/dashboard_helper.rb'
+ - 'ee/app/helpers/ee/import_helper.rb'
+ - 'ee/app/helpers/ee/subscribable_banner_helper.rb'
+ - 'ee/app/helpers/ee/users/callouts_helper.rb'
+ - 'ee/app/helpers/license_monitoring_helper.rb'
+ - 'ee/app/helpers/push_rules_helper.rb'
+ - 'ee/app/models/concerns/ee/protected_ref_access.rb'
+ - 'ee/app/models/ee/user.rb'
+ - 'ee/app/policies/ee/group_policy.rb'
+ - 'ee/app/services/ee/groups/create_service.rb'
+ - 'ee/app/services/ee/groups/update_service.rb'
+ - 'ee/app/services/ee/projects/update_service.rb'
+ - 'ee/lib/ee/api/helpers.rb'
+ - 'ee/lib/ee/gitlab/git_access.rb'
+ - 'lib/api/award_emoji.rb'
+ - 'lib/api/ci/runners.rb'
+ - 'lib/api/groups.rb'
+ - 'lib/api/helpers.rb'
+ - 'lib/api/users.rb'
+ - 'lib/api/v3/github.rb'
+ - 'lib/constraints/admin_constrainer.rb'
+ - 'lib/gitlab/auth.rb'
+ - 'lib/gitlab/import_export/members_mapper.rb'
+ - 'lib/gitlab/performance_bar.rb'
+ - 'lib/gitlab/visibility_level.rb'
diff --git a/.rubocop_todo/database/multiple_databases.yml b/.rubocop_todo/database/multiple_databases.yml
index 86db4e0c91f..43da6f8a5b4 100644
--- a/.rubocop_todo/database/multiple_databases.yml
+++ b/.rubocop_todo/database/multiple_databases.yml
@@ -1,21 +1,11 @@
---
Database/MultipleDatabases:
Exclude:
- - 'config/initializers/active_record_data_types.rb'
- 'db/post_migrate/20210317104032_set_iteration_cadence_automatic_to_false.rb'
- 'db/post_migrate/20210811122206_update_external_project_bots.rb'
- 'db/post_migrate/20210812013042_remove_duplicate_project_authorizations.rb'
- 'ee/spec/services/ee/merge_requests/update_service_spec.rb'
- - 'lib/gitlab/background_migration/backfill_projects_with_coverage.rb'
- - 'spec/db/schema_spec.rb'
- - 'spec/initializers/database_config_spec.rb'
- - 'spec/lib/gitlab/database_spec.rb'
- - 'spec/lib/gitlab/metrics/subscribers/active_record_spec.rb'
- - 'spec/lib/gitlab/profiler_spec.rb'
- - 'spec/lib/gitlab/usage/metrics/names_suggestions/relation_parsers/constraints_spec.rb'
- - 'spec/lib/gitlab/usage/metrics/names_suggestions/relation_parsers/joins_spec.rb'
- 'spec/support/caching.rb'
- - 'spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb'
- 'spec/support/helpers/database/database_helpers.rb'
- 'spec/support/helpers/database/table_schema_helpers.rb'
- 'spec/support/helpers/migrations_helpers.rb'
diff --git a/.rubocop_todo/database/rescue_query_canceled.yml b/.rubocop_todo/database/rescue_query_canceled.yml
index 76635ca4018..324ab498dd9 100644
--- a/.rubocop_todo/database/rescue_query_canceled.yml
+++ b/.rubocop_todo/database/rescue_query_canceled.yml
@@ -6,4 +6,3 @@ Database/RescueQueryCanceled:
- 'lib/gitlab/background_migration/backfill_work_item_type_id_for_issues.rb'
- 'lib/gitlab/database/batch_counter.rb'
- 'lib/gitlab/issuables_count_for_state.rb'
- - 'lib/tasks/gitlab/db/lock_writes.rake'
diff --git a/.rubocop_todo/fips/md5.yml b/.rubocop_todo/fips/md5.yml
index ef9e8fdde62..fc9cee17653 100644
--- a/.rubocop_todo/fips/md5.yml
+++ b/.rubocop_todo/fips/md5.yml
@@ -8,8 +8,6 @@ Fips/MD5:
- 'app/services/packages/go/create_package_service.rb'
- 'app/services/packages/maven/metadata/append_package_file_service.rb'
- 'app/services/packages/rubygems/create_gemspec_service.rb'
- - 'config/application.rb'
- - 'config/initializers/wikicloth_redos_patch.rb'
- 'ee/app/models/license.rb'
- 'ee/spec/lib/ee/gitlab/usage_data_spec.rb'
- 'lib/tasks/migrate/setup_postgresql.rake'
@@ -19,7 +17,6 @@ Fips/MD5:
- 'spec/lib/gitlab/ci/trace/remote_checksum_spec.rb'
- 'spec/models/concerns/checksummable_spec.rb'
- 'spec/services/gravatar_service_spec.rb'
- - 'spec/support/matchers/match_file.rb'
- 'spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb'
- 'spec/tooling/rspec_flaky/example_spec.rb'
- 'tooling/rspec_flaky/example.rb'
diff --git a/.rubocop_todo/fips/sha1.yml b/.rubocop_todo/fips/sha1.yml
index 934805c86d4..35221d9d3ff 100644
--- a/.rubocop_todo/fips/sha1.yml
+++ b/.rubocop_todo/fips/sha1.yml
@@ -37,7 +37,6 @@ Fips/SHA1:
- 'ee/spec/models/vulnerabilities/finding_spec.rb'
- 'ee/spec/services/alert_management/process_prometheus_alert_service_spec.rb'
- 'ee/spec/services/merge_trains/check_status_service_spec.rb'
- - 'ee/spec/services/projects/alerting/notify_service_spec.rb'
- 'ee/spec/services/security/ingestion/tasks/ingest_identifiers_spec.rb'
- 'ee/spec/services/security/override_uuids_service_spec.rb'
- 'ee/spec/services/vulnerabilities/manually_create_service_spec.rb'
@@ -85,8 +84,6 @@ Fips/SHA1:
- 'spec/lib/gitlab/ci/reports/security/locations/secret_detection_spec.rb'
- 'spec/lib/gitlab/diff/file_spec.rb'
- 'spec/lib/gitlab/diff/position_spec.rb'
- - 'spec/lib/gitlab/diff/position_tracer/image_strategy_spec.rb'
- - 'spec/lib/gitlab/diff/position_tracer/line_strategy_spec.rb'
- 'spec/lib/gitlab/git/branch_spec.rb'
- 'spec/lib/gitlab/git/tag_spec.rb'
- 'spec/migrations/20220107064845_populate_vulnerability_reads_spec.rb'
diff --git a/.rubocop_todo/gitlab/delegate_predicate_methods.yml b/.rubocop_todo/gitlab/delegate_predicate_methods.yml
index 44d603b3206..eb96914796f 100644
--- a/.rubocop_todo/gitlab/delegate_predicate_methods.yml
+++ b/.rubocop_todo/gitlab/delegate_predicate_methods.yml
@@ -1,4 +1,4 @@
---
Gitlab/DelegatePredicateMethods:
Exclude:
- - app/models/clusters/cluster.rb
+ - 'app/models/clusters/cluster.rb'
diff --git a/.rubocop_todo/gitlab/deprecate_track_redis_hll_event.yml b/.rubocop_todo/gitlab/deprecate_track_redis_hll_event.yml
index 06651a0f60a..ce2190c980c 100644
--- a/.rubocop_todo/gitlab/deprecate_track_redis_hll_event.yml
+++ b/.rubocop_todo/gitlab/deprecate_track_redis_hll_event.yml
@@ -1,29 +1,14 @@
---
Gitlab/DeprecateTrackRedisHLLEvent:
Exclude:
- - 'app/controllers/admin/dev_ops_report_controller.rb'
- - 'app/controllers/admin/usage_trends_controller.rb'
- 'app/controllers/concerns/snippets_actions.rb'
- 'app/controllers/concerns/wiki_actions.rb'
- 'app/controllers/projects/blob_controller.rb'
- - 'app/controllers/projects/cycle_analytics_controller.rb'
- - 'app/controllers/projects/graphs_controller.rb'
- 'app/controllers/projects/pipelines_controller.rb'
- 'ee/app/controllers/admin/audit_logs_controller.rb'
- 'ee/app/controllers/admin/credentials_controller.rb'
- - 'ee/app/controllers/ee/admin/dev_ops_report_controller.rb'
- 'ee/app/controllers/groups/analytics/ci_cd_analytics_controller.rb'
- - 'ee/app/controllers/groups/analytics/devops_adoption_controller.rb'
- - 'ee/app/controllers/groups/analytics/productivity_analytics_controller.rb'
- 'ee/app/controllers/groups/audit_events_controller.rb'
- - 'ee/app/controllers/groups/contribution_analytics_controller.rb'
- 'ee/app/controllers/groups/epic_boards_controller.rb'
- - 'ee/app/controllers/groups/insights_controller.rb'
- - 'ee/app/controllers/groups/issues_analytics_controller.rb'
- 'ee/app/controllers/groups/security/compliance_dashboards_controller.rb'
- - 'ee/app/controllers/projects/analytics/code_reviews_controller.rb'
- - 'ee/app/controllers/projects/analytics/issues_analytics_controller.rb'
- - 'ee/app/controllers/projects/analytics/merge_request_analytics_controller.rb'
- - 'ee/app/controllers/projects/insights_controller.rb'
- - 'ee/app/controllers/projects/integrations/jira/issues_controller.rb'
- 'spec/controllers/concerns/redis_tracking_spec.rb'
diff --git a/.rubocop_todo/gitlab/feature_available_usage.yml b/.rubocop_todo/gitlab/feature_available_usage.yml
index 0daacdfe2b1..26800923393 100644
--- a/.rubocop_todo/gitlab/feature_available_usage.yml
+++ b/.rubocop_todo/gitlab/feature_available_usage.yml
@@ -1,152 +1,116 @@
---
Gitlab/FeatureAvailableUsage:
Exclude:
- - app/controllers/projects/application_controller.rb
- - app/graphql/types/project_type.rb
- - app/helpers/events_helper.rb
- - app/helpers/labels_helper.rb
- - app/policies/project_policy.rb
- - ee/app/controllers/concerns/description_diff_actions.rb
- - ee/app/controllers/concerns/ee/boards_actions.rb
- - ee/app/controllers/concerns/security_dashboards_permissions.rb
- - ee/app/controllers/ee/boards/lists_controller.rb
- - ee/app/controllers/ee/projects/autocomplete_sources_controller.rb
- - ee/app/controllers/ee/projects/issues_controller.rb
- - ee/app/controllers/ee/projects/security/configuration_controller.rb
- - ee/app/controllers/ee/projects/settings/ci_cd_controller.rb
- - ee/app/controllers/ee/projects/settings/operations_controller.rb
- - ee/app/controllers/ee/projects/settings/repository_controller.rb
- - ee/app/controllers/projects/audit_events_controller.rb
- - ee/app/controllers/projects/iterations_controller.rb
- - ee/app/controllers/projects/path_locks_controller.rb
- - ee/app/controllers/projects/subscriptions_controller.rb
- - ee/app/finders/autocomplete/vulnerabilities_autocomplete_finder.rb
- - ee/app/finders/ee/alert_management/http_integrations_finder.rb
- - ee/app/graphql/ee/types/group_type.rb
- - ee/app/graphql/mutations/dast/profiles/create.rb
- - ee/app/graphql/mutations/dast/profiles/run.rb
- - ee/app/graphql/mutations/dast/profiles/update.rb
- - ee/app/graphql/mutations/instance_security_dashboard/remove_project.rb
- - ee/app/helpers/ee/application_helper.rb
- - ee/app/helpers/ee/boards_helper.rb
- - ee/app/helpers/ee/dashboard_helper.rb
- - ee/app/helpers/ee/form_helper.rb
- - ee/app/helpers/ee/graph_helper.rb
- - ee/app/helpers/ee/issues_helper.rb
- - ee/app/helpers/ee/lock_helper.rb
- - ee/app/helpers/ee/operations_helper.rb
- - ee/app/helpers/ee/projects/incidents_helper.rb
- - ee/app/helpers/ee/projects_helper.rb
- - ee/app/helpers/ee/releases_helper.rb
- - ee/app/helpers/ee/search_helper.rb
- - ee/app/helpers/ee/tree_helper.rb
- - ee/app/models/approval_state.rb
- - ee/app/models/concerns/ee/approvable.rb
- - ee/app/models/concerns/ee/project_security_scanners_information.rb
- - ee/app/models/concerns/ee/protected_ref_access.rb
- - ee/app/models/concerns/insights_feature.rb
- - ee/app/models/ee/board.rb
- - ee/app/models/ee/ci/build.rb
- - ee/app/models/ee/ci/build_dependencies.rb
- - ee/app/models/ee/ci/pipeline.rb
- - ee/app/models/ee/group.rb
- - ee/app/models/ee/group_member.rb
- - ee/app/models/ee/issue.rb
- - ee/app/models/ee/list.rb
- - ee/app/models/ee/merge_request.rb
- - ee/app/models/ee/milestone_release.rb
- - ee/app/models/ee/namespace.rb
- - ee/app/models/ee/namespace_setting.rb
- - ee/app/models/ee/project.rb
- - ee/app/models/ee/project_ci_cd_setting.rb
- - ee/app/models/project_security_setting.rb
- - ee/app/policies/compliance_management/framework_policy.rb
- - ee/app/policies/ee/group_policy.rb
- - ee/app/policies/ee/project_policy.rb
- - ee/app/policies/ee/protected_branch_policy.rb
- - ee/app/presenters/ee/label_presenter.rb
- - ee/app/presenters/merge_request_approver_presenter.rb
- - ee/app/serializers/dashboard_operations_project_entity.rb
- - ee/app/serializers/ee/environment_entity.rb
- - ee/app/serializers/ee/evidences/release_entity.rb
- - ee/app/serializers/ee/note_entity.rb
- - ee/app/services/boards/epic_boards/update_service.rb
- - ee/app/services/ci/audit_variable_change_service.rb
- - ee/app/services/dashboard/projects/create_service.rb
- - ee/app/services/dashboard/projects/list_service.rb
- - ee/app/services/ee/alert_management/http_integrations/create_service.rb
- - ee/app/services/ee/audit_event_service.rb
- - ee/app/services/ee/boards/issues/list_service.rb
- - ee/app/services/ee/boards/lists/create_service.rb
- - ee/app/services/ee/boards/update_service.rb
- - ee/app/services/ee/ide/schemas_config_service.rb
- - ee/app/services/ee/issuable_base_service.rb
- - ee/app/services/ee/issue_links/create_service.rb
- - ee/app/services/ee/issues/build_service.rb
- - ee/app/services/ee/lfs/lock_file_service.rb
- - ee/app/services/ee/lfs/unlock_file_service.rb
- - ee/app/services/ee/merge_requests/approval_service.rb
- - ee/app/services/ee/merge_requests/build_service.rb
- - ee/app/services/ee/merge_requests/merge_base_service.rb
- - ee/app/services/ee/merge_requests/refresh_service.rb
- - ee/app/services/ee/merge_requests/update_service.rb
- - ee/app/services/ee/projects/create_service.rb
- - ee/app/services/ee/protected_branches/create_service.rb
- - ee/app/services/ee/releases/create_evidence_service.rb
- - ee/app/services/iterations/create_service.rb
- - ee/app/services/iterations/update_service.rb
- - ee/app/services/merge_requests/sync_report_approver_approval_rules.rb
- - ee/app/services/merge_requests/update_blocks_service.rb
- - ee/app/services/projects/mark_for_deletion_service.rb
- - ee/app/services/quality_management/test_cases/create_service.rb
- - ee/app/services/requirements_management/process_test_reports_service.rb
- - ee/app/services/security/store_scans_service.rb
- - ee/app/views/projects/_merge_request_approvals_settings.html.haml
- - ee/app/views/projects/_merge_request_settings.html.haml
- - ee/app/views/projects/_merge_request_settings_description_text.html.haml
- - ee/app/views/projects/audit_events/index.html.haml
- - ee/app/views/projects/blob/_header_file_locks.html.haml
- - ee/app/views/projects/issues/_related_issues.html.haml
- - ee/app/views/projects/merge_requests/show.html.haml
- - ee/app/views/projects/pipelines/_tabs_content.html.haml
- - ee/app/views/projects/protected_branches/ee/_code_owner_approval_form.html.haml
- - ee/app/views/projects/protected_branches/ee/_code_owner_approval_table.html.haml
- - ee/app/views/projects/protected_branches/ee/_code_owner_approval_table_head.html.haml
- - ee/app/views/projects/push_rules/_index.html.haml
- - ee/app/views/projects/settings/_default_issue_template.html.haml
- - ee/app/views/projects/settings/_marked_for_removal.html.haml
- - ee/app/views/projects/settings/_restore.html.haml
- - ee/app/views/projects/settings/ci_cd/_auto_rollback.html.haml
- - ee/app/views/projects/settings/ci_cd/_pipeline_subscriptions.html.haml
- - ee/app/views/projects/settings/operations/_status_page.html.haml
- - ee/app/views/projects/settings/repository/_protected_branches.html.haml
- - ee/app/views/shared/issuable/_group_bulk_update_sidebar.html.haml
- - ee/app/views/shared/issuable/form/_default_templates.html.haml
- - ee/app/views/shared/labels/_create_label_help_text.html.haml
- - ee/app/views/shared/promotions/_promote_mr_features.html.haml
- - ee/app/views/shared/promotions/_promote_repository_features.html.haml
- - ee/app/workers/analytics/code_review_metrics_worker.rb
- - ee/app/workers/group_saml_group_sync_worker.rb
- - ee/lib/ee/api/entities/approval_state.rb
- - ee/lib/ee/api/entities/board.rb
- - ee/lib/ee/api/entities/issue.rb
- - ee/lib/ee/api/entities/project.rb
- - ee/lib/ee/api/helpers.rb
- - ee/lib/ee/api/internal/kubernetes.rb
- - ee/lib/ee/api/projects.rb
- - ee/lib/ee/gitlab/alert_management/payload/generic.rb
- - ee/lib/ee/gitlab/checks/diff_check.rb
- - ee/lib/ee/gitlab/gon_helper.rb
- - ee/lib/ee/gitlab/tree_summary.rb
- - ee/lib/gitlab/alert_management.rb
- - ee/lib/gitlab/ci/pipeline/chain/config/content/compliance.rb
- - ee/lib/gitlab/ci/project_config/compliance.rb
- - ee/lib/gitlab/code_owners.rb
- - ee/lib/gitlab/incident_management.rb
- - ee/lib/gitlab/path_locks_finder.rb
- - ee/lib/incident_management/incident_sla.rb
- - ee/spec/models/instance_security_dashboard_spec.rb
- - ee/spec/models/license_spec.rb
- - ee/spec/models/project_spec.rb
- - lib/api/helpers/related_resources_helpers.rb
- - spec/models/concerns/featurable_spec.rb
+ - 'app/controllers/projects/application_controller.rb'
+ - 'app/graphql/types/project_type.rb'
+ - 'app/helpers/events_helper.rb'
+ - 'app/helpers/labels_helper.rb'
+ - 'ee/app/controllers/concerns/description_diff_actions.rb'
+ - 'ee/app/controllers/concerns/ee/boards_actions.rb'
+ - 'ee/app/controllers/concerns/security_dashboards_permissions.rb'
+ - 'ee/app/controllers/ee/projects/autocomplete_sources_controller.rb'
+ - 'ee/app/controllers/ee/projects/issues_controller.rb'
+ - 'ee/app/controllers/ee/projects/security/configuration_controller.rb'
+ - 'ee/app/controllers/ee/projects/settings/ci_cd_controller.rb'
+ - 'ee/app/controllers/ee/projects/settings/operations_controller.rb'
+ - 'ee/app/controllers/ee/projects/settings/repository_controller.rb'
+ - 'ee/app/controllers/projects/audit_events_controller.rb'
+ - 'ee/app/controllers/projects/iterations_controller.rb'
+ - 'ee/app/controllers/projects/path_locks_controller.rb'
+ - 'ee/app/controllers/projects/subscriptions_controller.rb'
+ - 'ee/app/finders/autocomplete/vulnerabilities_autocomplete_finder.rb'
+ - 'ee/app/finders/ee/alert_management/http_integrations_finder.rb'
+ - 'ee/app/graphql/ee/types/group_type.rb'
+ - 'ee/app/graphql/mutations/instance_security_dashboard/remove_project.rb'
+ - 'ee/app/helpers/ee/application_helper.rb'
+ - 'ee/app/helpers/ee/boards_helper.rb'
+ - 'ee/app/helpers/ee/dashboard_helper.rb'
+ - 'ee/app/helpers/ee/form_helper.rb'
+ - 'ee/app/helpers/ee/graph_helper.rb'
+ - 'ee/app/helpers/ee/issues_helper.rb'
+ - 'ee/app/helpers/ee/lock_helper.rb'
+ - 'ee/app/helpers/ee/operations_helper.rb'
+ - 'ee/app/helpers/ee/projects/incidents_helper.rb'
+ - 'ee/app/helpers/ee/projects_helper.rb'
+ - 'ee/app/helpers/ee/releases_helper.rb'
+ - 'ee/app/helpers/ee/search_helper.rb'
+ - 'ee/app/helpers/ee/tree_helper.rb'
+ - 'ee/app/models/approval_state.rb'
+ - 'ee/app/models/concerns/ee/approvable.rb'
+ - 'ee/app/models/concerns/ee/project_security_scanners_information.rb'
+ - 'ee/app/models/concerns/ee/protected_ref_access.rb'
+ - 'ee/app/models/concerns/insights_feature.rb'
+ - 'ee/app/models/ee/board.rb'
+ - 'ee/app/models/ee/ci/build.rb'
+ - 'ee/app/models/ee/ci/build_dependencies.rb'
+ - 'ee/app/models/ee/ci/pipeline.rb'
+ - 'ee/app/models/ee/group.rb'
+ - 'ee/app/models/ee/group_member.rb'
+ - 'ee/app/models/ee/issue.rb'
+ - 'ee/app/models/ee/list.rb'
+ - 'ee/app/models/ee/merge_request.rb'
+ - 'ee/app/models/ee/milestone_release.rb'
+ - 'ee/app/models/ee/namespace.rb'
+ - 'ee/app/models/ee/namespace_setting.rb'
+ - 'ee/app/models/ee/project.rb'
+ - 'ee/app/models/ee/project_ci_cd_setting.rb'
+ - 'ee/app/models/project_security_setting.rb'
+ - 'ee/app/policies/compliance_management/framework_policy.rb'
+ - 'ee/app/policies/ee/group_policy.rb'
+ - 'ee/app/policies/ee/project_policy.rb'
+ - 'ee/app/policies/ee/protected_branch_policy.rb'
+ - 'ee/app/presenters/ee/label_presenter.rb'
+ - 'ee/app/presenters/merge_request_approver_presenter.rb'
+ - 'ee/app/serializers/dashboard_operations_project_entity.rb'
+ - 'ee/app/serializers/ee/environment_entity.rb'
+ - 'ee/app/serializers/ee/evidences/release_entity.rb'
+ - 'ee/app/serializers/ee/note_entity.rb'
+ - 'ee/app/services/boards/epic_boards/update_service.rb'
+ - 'ee/app/services/ci/audit_variable_change_service.rb'
+ - 'ee/app/services/dashboard/projects/create_service.rb'
+ - 'ee/app/services/dashboard/projects/list_service.rb'
+ - 'ee/app/services/ee/alert_management/http_integrations/create_service.rb'
+ - 'ee/app/services/ee/audit_event_service.rb'
+ - 'ee/app/services/ee/boards/issues/list_service.rb'
+ - 'ee/app/services/ee/boards/lists/create_service.rb'
+ - 'ee/app/services/ee/boards/update_service.rb'
+ - 'ee/app/services/ee/ide/schemas_config_service.rb'
+ - 'ee/app/services/ee/issuable_base_service.rb'
+ - 'ee/app/services/ee/issue_links/create_service.rb'
+ - 'ee/app/services/ee/issues/build_service.rb'
+ - 'ee/app/services/ee/lfs/lock_file_service.rb'
+ - 'ee/app/services/ee/lfs/unlock_file_service.rb'
+ - 'ee/app/services/ee/merge_requests/build_service.rb'
+ - 'ee/app/services/ee/merge_requests/merge_base_service.rb'
+ - 'ee/app/services/ee/merge_requests/refresh_service.rb'
+ - 'ee/app/services/ee/projects/create_service.rb'
+ - 'ee/app/services/ee/protected_branches/create_service.rb'
+ - 'ee/app/services/ee/releases/create_evidence_service.rb'
+ - 'ee/app/services/iterations/create_service.rb'
+ - 'ee/app/services/iterations/update_service.rb'
+ - 'ee/app/services/merge_requests/update_blocks_service.rb'
+ - 'ee/app/services/projects/mark_for_deletion_service.rb'
+ - 'ee/app/services/requirements_management/process_test_reports_service.rb'
+ - 'ee/app/services/security/store_scans_service.rb'
+ - 'ee/app/workers/analytics/code_review_metrics_worker.rb'
+ - 'ee/app/workers/group_saml_group_sync_worker.rb'
+ - 'ee/lib/ee/api/entities/approval_state.rb'
+ - 'ee/lib/ee/api/entities/board.rb'
+ - 'ee/lib/ee/api/entities/issue.rb'
+ - 'ee/lib/ee/api/entities/project.rb'
+ - 'ee/lib/ee/api/helpers.rb'
+ - 'ee/lib/ee/api/projects.rb'
+ - 'ee/lib/ee/gitlab/alert_management/payload/generic.rb'
+ - 'ee/lib/ee/gitlab/checks/diff_check.rb'
+ - 'ee/lib/ee/gitlab/gon_helper.rb'
+ - 'ee/lib/ee/gitlab/tree_summary.rb'
+ - 'ee/lib/gitlab/alert_management.rb'
+ - 'ee/lib/gitlab/ci/project_config/compliance.rb'
+ - 'ee/lib/gitlab/code_owners.rb'
+ - 'ee/lib/gitlab/path_locks_finder.rb'
+ - 'ee/lib/incident_management/incident_sla.rb'
+ - 'ee/spec/models/instance_security_dashboard_spec.rb'
+ - 'ee/spec/models/license_spec.rb'
+ - 'ee/spec/models/project_spec.rb'
+ - 'lib/api/helpers/related_resources_helpers.rb'
+ - 'spec/models/concerns/featurable_spec.rb'
diff --git a/.rubocop_todo/gitlab/json.yml b/.rubocop_todo/gitlab/json.yml
deleted file mode 100644
index 190778a3de7..00000000000
--- a/.rubocop_todo/gitlab/json.yml
+++ /dev/null
@@ -1,465 +0,0 @@
----
-# Cop supports --autocorrect.
-Gitlab/Json:
- Exclude:
- - 'app/controllers/admin/application_settings_controller.rb'
- - 'app/controllers/concerns/authenticates_with_two_factor.rb'
- - 'app/controllers/projects/commit_controller.rb'
- - 'app/controllers/projects/google_cloud/configuration_controller.rb'
- - 'app/controllers/projects/google_cloud/databases_controller.rb'
- - 'app/controllers/projects/google_cloud/deployments_controller.rb'
- - 'app/controllers/projects/google_cloud/gcp_regions_controller.rb'
- - 'app/controllers/projects/google_cloud/service_accounts_controller.rb'
- - 'app/controllers/projects/graphs_controller.rb'
- - 'app/controllers/projects/merge_requests_controller.rb'
- - 'app/controllers/projects/notes_controller.rb'
- - 'app/controllers/projects/settings/ci_cd_controller.rb'
- - 'app/controllers/projects/templates_controller.rb'
- - 'app/controllers/projects_controller.rb'
- - 'app/controllers/search_controller.rb'
- - 'app/helpers/access_tokens_helper.rb'
- - 'app/helpers/application_settings_helper.rb'
- - 'app/helpers/breadcrumbs_helper.rb'
- - 'app/helpers/ci/builds_helper.rb'
- - 'app/helpers/ci/pipelines_helper.rb'
- - 'app/helpers/compare_helper.rb'
- - 'app/helpers/emails_helper.rb'
- - 'app/helpers/environment_helper.rb'
- - 'app/helpers/groups_helper.rb'
- - 'app/helpers/ide_helper.rb'
- - 'app/helpers/integrations_helper.rb'
- - 'app/helpers/invite_members_helper.rb'
- - 'app/helpers/issuables_description_templates_helper.rb'
- - 'app/helpers/issuables_helper.rb'
- - 'app/helpers/jira_connect_helper.rb'
- - 'app/helpers/learn_gitlab_helper.rb'
- - 'app/helpers/namespaces_helper.rb'
- - 'app/helpers/notes_helper.rb'
- - 'app/helpers/operations_helper.rb'
- - 'app/helpers/packages_helper.rb'
- - 'app/helpers/projects/project_members_helper.rb'
- - 'app/helpers/projects_helper.rb'
- - 'app/helpers/search_helper.rb'
- - 'app/helpers/terms_helper.rb'
- - 'app/helpers/users_helper.rb'
- - 'app/mailers/emails/members.rb'
- - 'app/presenters/packages/composer/packages_presenter.rb'
- - 'app/presenters/projects/security/configuration_presenter.rb'
- - 'app/workers/google_cloud/create_cloudsql_instance_worker.rb'
- - 'config/initializers/rack_multipart_patch.rb'
- - 'ee/app/controllers/admin/geo/nodes_controller.rb'
- - 'ee/app/controllers/ee/admin/application_settings_controller.rb'
- - 'ee/app/controllers/ee/search_controller.rb'
- - 'ee/app/controllers/subscriptions_controller.rb'
- - 'ee/app/graphql/types/json_string_type.rb'
- - 'ee/app/helpers/billing_plans_helper.rb'
- - 'ee/app/helpers/ee/environments_helper.rb'
- - 'ee/app/helpers/ee/geo_helper.rb'
- - 'ee/app/helpers/ee/groups/analytics/cycle_analytics_helper.rb'
- - 'ee/app/helpers/ee/invite_members_helper.rb'
- - 'ee/app/helpers/ee/operations_helper.rb'
- - 'ee/app/helpers/ee/projects/pipeline_helper.rb'
- - 'ee/app/helpers/ee/projects_helper.rb'
- - 'ee/app/helpers/ee/security_orchestration_helper.rb'
- - 'ee/app/helpers/groups/ldap_sync_helper.rb'
- - 'ee/app/helpers/groups/security_features_helper.rb'
- - 'ee/app/helpers/incident_management/oncall_schedule_helper.rb'
- - 'ee/app/helpers/projects/on_demand_scans_helper.rb'
- - 'ee/app/helpers/projects/security/dast_profiles_helper.rb'
- - 'ee/app/helpers/security_helper.rb'
- - 'ee/app/helpers/subscriptions_helper.rb'
- - 'ee/app/helpers/users/identity_verification_helper.rb'
- - 'ee/app/helpers/vulnerabilities_helper.rb'
- - 'ee/app/models/product_analytics/jitsu_authentication.rb'
- - 'ee/app/presenters/epic_presenter.rb'
- - 'ee/app/services/arkose/blocked_users_report_service.rb'
- - 'ee/app/services/elastic/indexing_control_service.rb'
- - 'ee/app/services/elastic/process_bookkeeping_service.rb'
- - 'ee/app/services/security/token_revocation_service.rb'
- - 'ee/app/services/status_page/publish_base_service.rb'
- - 'ee/app/services/upcoming_reconciliations/update_service.rb'
- - 'ee/app/services/vulnerabilities/create_service_base.rb'
- - 'ee/app/workers/concerns/elastic/migration_state.rb'
- - 'ee/app/workers/sync_seat_link_request_worker.rb'
- - 'ee/db/fixtures/development/20_vulnerabilities.rb'
- - 'ee/lib/api/analytics/product_analytics.rb'
- - 'ee/lib/ee/gitlab/background_migration/update_vulnerability_occurrences_location.rb'
- - 'ee/lib/gitlab/elastic/indexer.rb'
- - 'ee/lib/gitlab/geo/signed_data.rb'
- - 'ee/lib/gitlab/subscription_portal/clients/graphql.rb'
- - 'ee/lib/gitlab/subscription_portal/clients/rest.rb'
- - 'ee/lib/slack/api.rb'
- - 'ee/lib/tasks/gitlab/elastic.rake'
- - 'ee/lib/tasks/gitlab/spdx.rake'
- - 'ee/spec/controllers/admin/application_settings_controller_spec.rb'
- - 'ee/spec/controllers/countries_controller_spec.rb'
- - 'ee/spec/controllers/country_states_controller_spec.rb'
- - 'ee/spec/controllers/ee/search_controller_spec.rb'
- - 'ee/spec/controllers/groups/analytics/cycle_analytics_controller_spec.rb'
- - 'ee/spec/controllers/groups/security/policies_controller_spec.rb'
- - 'ee/spec/controllers/projects/integrations/jira/issues_controller_spec.rb'
- - 'ee/spec/controllers/subscriptions_controller_spec.rb'
- - 'ee/spec/factories/vulnerabilities/findings.rb'
- - 'ee/spec/features/admin/subscriptions/admin_views_subscription_spec.rb'
- - 'ee/spec/features/billings/billing_plans_spec.rb'
- - 'ee/spec/features/billings/extend_reactivate_trial_spec.rb'
- - 'ee/spec/features/billings/qrtly_reconciliation_alert_spec.rb'
- - 'ee/spec/features/projects/integrations/jira_issues_list_spec.rb'
- - 'ee/spec/features/projects/integrations/user_activates_github_spec.rb'
- - 'ee/spec/features/projects/integrations/user_activates_jira_spec.rb'
- - 'ee/spec/frontend/fixtures/dast_profiles.rb'
- - 'ee/spec/frontend/fixtures/epic.rb'
- - 'ee/spec/graphql/api/vulnerabilities_spec.rb'
- - 'ee/spec/graphql/types/json_string_type_spec.rb'
- - 'ee/spec/helpers/ee/groups/group_members_helper_spec.rb'
- - 'ee/spec/helpers/ee/projects/pipeline_helper_spec.rb'
- - 'ee/spec/helpers/ee/security_orchestration_helper_spec.rb'
- - 'ee/spec/helpers/incident_management/oncall_schedule_helper_spec.rb'
- - 'ee/spec/helpers/projects/on_demand_scans_helper_spec.rb'
- - 'ee/spec/helpers/projects/security/dast_profiles_helper_spec.rb'
- - 'ee/spec/helpers/users/identity_verification_helper_spec.rb'
- - 'ee/spec/lib/ee/gitlab/background_migration/drop_invalid_remediations_spec.rb'
- - 'ee/spec/lib/ee/gitlab/background_migration/update_vulnerability_occurrences_location_spec.rb'
- - 'ee/spec/lib/gitlab/analytics/cycle_analytics/request_params_spec.rb'
- - 'ee/spec/lib/gitlab/ci/parsers/license_compliance/license_scanning_spec.rb'
- - 'ee/spec/lib/gitlab/ci/parsers/security/dast_spec.rb'
- - 'ee/spec/lib/gitlab/ci/parsers/security/dependency_scanning_spec.rb'
- - 'ee/spec/lib/gitlab/elastic/bulk_indexer_spec.rb'
- - 'ee/spec/lib/gitlab/elastic/indexer_spec.rb'
- - 'ee/spec/lib/gitlab/geo/replication/blob_downloader_spec.rb'
- - 'ee/spec/lib/gitlab/tracking/standard_context_spec.rb'
- - 'ee/spec/lib/slack/api_spec.rb'
- - 'ee/spec/migrations/update_vulnerability_occurrences_location_spec.rb'
- - 'ee/spec/models/ee/integrations/jira_spec.rb'
- - 'ee/spec/models/gitlab/seat_link_data_spec.rb'
- - 'ee/spec/models/group_member_spec.rb'
- - 'ee/spec/models/integrations/github/status_notifier_spec.rb'
- - 'ee/spec/models/integrations/github_spec.rb'
- - 'ee/spec/models/license_spec.rb'
- - 'ee/spec/models/product_analytics/jitsu_authentication_spec.rb'
- - 'ee/spec/models/vulnerabilities/finding_spec.rb'
- - 'ee/spec/presenters/audit_event_presenter_spec.rb'
- - 'ee/spec/requests/api/analytics/product_analytics_spec.rb'
- - 'ee/spec/requests/api/experiments_spec.rb'
- - 'ee/spec/requests/api/geo_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/alert_management/http_integration/create_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/alert_management/http_integration/update_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/vulnerabilities/create_external_issue_link_spec.rb'
- - 'ee/spec/requests/api/graphql/project/alert_management/http_integrations_spec.rb'
- - 'ee/spec/requests/api/graphql/vulnerabilities/external_issue_links_spec.rb'
- - 'ee/spec/requests/api/graphql/vulnerabilities/location_spec.rb'
- - 'ee/spec/requests/api/integrations/slack/events_spec.rb'
- - 'ee/spec/requests/api/releases_spec.rb'
- - 'ee/spec/requests/api/settings_spec.rb'
- - 'ee/spec/requests/git_http_geo_spec.rb'
- - 'ee/spec/requests/projects/on_demand_scans_controller_spec.rb'
- - 'ee/spec/requests/projects/security/policies_controller_spec.rb'
- - 'ee/spec/requests/users/identity_verification_controller_spec.rb'
- - 'ee/spec/serializers/clusters/environment_entity_spec.rb'
- - 'ee/spec/serializers/clusters/environment_serializer_spec.rb'
- - 'ee/spec/serializers/dependency_list_serializer_spec.rb'
- - 'ee/spec/serializers/epics/related_epic_entity_spec.rb'
- - 'ee/spec/serializers/evidences/evidence_entity_spec.rb'
- - 'ee/spec/serializers/issue_serializer_spec.rb'
- - 'ee/spec/serializers/licenses_list_serializer_spec.rb'
- - 'ee/spec/serializers/member_entity_spec.rb'
- - 'ee/spec/serializers/member_user_entity_spec.rb'
- - 'ee/spec/serializers/status_page/incident_entity_spec.rb'
- - 'ee/spec/serializers/status_page/incident_serializer_spec.rb'
- - 'ee/spec/serializers/test_reports_comparer_serializer_spec.rb'
- - 'ee/spec/services/arkose/blocked_users_report_service_spec.rb'
- - 'ee/spec/services/arkose/token_verification_service_spec.rb'
- - 'ee/spec/services/gitlab_subscriptions/fetch_subscription_plans_service_spec.rb'
- - 'ee/spec/services/integrations/slack_events/app_home_opened_service_spec.rb'
- - 'ee/spec/services/jira/requests/issues/list_service_spec.rb'
- - 'ee/spec/services/projects/slack_application_install_service_spec.rb'
- - 'ee/spec/services/security/token_revocation_service_spec.rb'
- - 'ee/spec/support/helpers/subscription_portal_helpers.rb'
- - 'ee/spec/support/shared_examples/controllers/cluster_metrics_shared_examples.rb'
- - 'ee/spec/support/shared_examples/requests/api/project_approval_rules_api_shared_examples.rb'
- - 'ee/spec/support/shared_examples/status_page/publish_shared_examples.rb'
- - 'ee/spec/tasks/gitlab/spdx_rake_spec.rb'
- - 'ee/spec/workers/audit_events/audit_event_streaming_worker_spec.rb'
- - 'ee/spec/workers/scan_security_report_secrets_worker_spec.rb'
- - 'ee/spec/workers/sync_seat_link_request_worker_spec.rb'
- - 'ee/spec/workers/vulnerability_exports/export_worker_spec.rb'
- - 'lib/api/api.rb'
- - 'lib/api/feature_flags_user_lists.rb'
- - 'lib/api/helpers.rb'
- - 'lib/api/terraform/state.rb'
- - 'lib/atlassian/jira_connect/client.rb'
- - 'lib/atlassian/jira_connect/serializers/base_entity.rb'
- - 'lib/backup/gitaly_backup.rb'
- - 'lib/bitbucket_server/client.rb'
- - 'lib/bulk_imports/clients/graphql.rb'
- - 'lib/error_tracking/sentry_client.rb'
- - 'lib/gitlab/alert_management/payload/prometheus.rb'
- - 'lib/gitlab/analytics/cycle_analytics/request_params.rb'
- - 'lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp.rb'
- - 'lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp.rb'
- - 'lib/gitlab/auth/otp/strategies/forti_token_cloud.rb'
- - 'lib/gitlab/background_migration/fix_vulnerability_occurrences_with_hashes_as_raw_metadata.rb'
- - 'lib/gitlab/bitbucket_import/importer.rb'
- - 'lib/gitlab/bitbucket_server_import/importer.rb'
- - 'lib/gitlab/chat/responder/mattermost.rb'
- - 'lib/gitlab/chat/responder/slack.rb'
- - 'lib/gitlab/chat_name_token.rb'
- - 'lib/gitlab/ci/ansi2html.rb'
- - 'lib/gitlab/ci/ansi2json/state.rb'
- - 'lib/gitlab/ci/build/releaser.rb'
- - 'lib/gitlab/ci/config/external/mapper.rb'
- - 'lib/gitlab/ci/pipeline/chain/validate/external.rb'
- - 'lib/gitlab/ci/reports/security/finding.rb'
- - 'lib/gitlab/composer/cache.rb'
- - 'lib/gitlab/database/background_migration/batched_migration.rb'
- - 'lib/gitlab/database/background_migration_job.rb'
- - 'lib/gitlab/database/migration_helpers.rb'
- - 'lib/gitlab/database/migrations/batched_background_migration_helpers.rb'
- - 'lib/gitlab/database/migrations/instrumentation.rb'
- - 'lib/gitlab/database/migrations/runner.rb'
- - 'lib/gitlab/database/postgres_hll/buckets.rb'
- - 'lib/gitlab/database/reindexing/grafana_notifier.rb'
- - 'lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb'
- - 'lib/gitlab/diff/highlight_cache.rb'
- - 'lib/gitlab/discussions_diff/highlight_cache.rb'
- - 'lib/gitlab/external_authorization/client.rb'
- - 'lib/gitlab/file_hook.rb'
- - 'lib/gitlab/gitaly_client/conflicts_service.rb'
- - 'lib/gitlab/graphql/pagination/active_record_array_connection.rb'
- - 'lib/gitlab/graphql/pagination/keyset/connection.rb'
- - 'lib/gitlab/health_checks/middleware.rb'
- - 'lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb'
- - 'lib/gitlab/import_export/json/legacy_writer.rb'
- - 'lib/gitlab/import_export/json/ndjson_writer.rb'
- - 'lib/gitlab/import_export/lfs_saver.rb'
- - 'lib/gitlab/jira/http_client.rb'
- - 'lib/gitlab/json_cache.rb'
- - 'lib/gitlab/legacy_github_import/importer.rb'
- - 'lib/gitlab/lfs/client.rb'
- - 'lib/gitlab/merge_requests/mergeability/redis_interface.rb'
- - 'lib/gitlab/middleware/read_only/controller.rb'
- - 'lib/gitlab/patch/hangouts_chat_http_override.rb'
- - 'lib/gitlab/puma_logging/json_formatter.rb'
- - 'lib/gitlab/sidekiq_config.rb'
- - 'lib/gitlab/sidekiq_daemon/monitor.rb'
- - 'lib/gitlab/sidekiq_logging/json_formatter.rb'
- - 'lib/gitlab/usage/metrics/aggregates/sources/postgres_hll.rb'
- - 'lib/gitlab/utils/json_size_estimator.rb'
- - 'lib/gitlab/version_info.rb'
- - 'lib/gitlab/workhorse.rb'
- - 'lib/mattermost/command.rb'
- - 'lib/mattermost/team.rb'
- - 'lib/microsoft_teams/notifier.rb'
- - 'lib/tasks/gitlab/background_migrations.rake'
- - 'lib/version_check.rb'
- - 'spec/commands/diagnostic_reports/uploader_smoke_spec.rb'
- - 'spec/controllers/admin/integrations_controller_spec.rb'
- - 'spec/controllers/concerns/product_analytics_tracking_spec.rb'
- - 'spec/controllers/groups/settings/integrations_controller_spec.rb'
- - 'spec/controllers/jira_connect/subscriptions_controller_spec.rb'
- - 'spec/controllers/profiles/personal_access_tokens_controller_spec.rb'
- - 'spec/controllers/projects/alerting/notifications_controller_spec.rb'
- - 'spec/controllers/projects/jobs_controller_spec.rb'
- - 'spec/controllers/projects/merge_requests/drafts_controller_spec.rb'
- - 'spec/factories/ci/pipeline_artifacts.rb'
- - 'spec/features/dashboard/issues_spec.rb'
- - 'spec/features/error_tracking/user_filters_errors_by_status_spec.rb'
- - 'spec/features/file_uploads/graphql_add_design_spec.rb'
- - 'spec/features/groups/dependency_proxy_for_containers_spec.rb'
- - 'spec/features/markdown/copy_as_gfm_spec.rb'
- - 'spec/features/markdown/metrics_spec.rb'
- - 'spec/features/projects/integrations/user_activates_jira_spec.rb'
- - 'spec/features/projects/settings/monitor_settings_spec.rb'
- - 'spec/frontend/fixtures/timezones.rb'
- - 'spec/helpers/access_tokens_helper_spec.rb'
- - 'spec/helpers/breadcrumbs_helper_spec.rb'
- - 'spec/helpers/ci/builds_helper_spec.rb'
- - 'spec/helpers/environment_helper_spec.rb'
- - 'spec/helpers/environments_helper_spec.rb'
- - 'spec/helpers/groups/group_members_helper_spec.rb'
- - 'spec/helpers/groups_helper_spec.rb'
- - 'spec/helpers/ide_helper_spec.rb'
- - 'spec/helpers/invite_members_helper_spec.rb'
- - 'spec/helpers/issuables_description_templates_helper_spec.rb'
- - 'spec/helpers/listbox_helper_spec.rb'
- - 'spec/helpers/namespaces_helper_spec.rb'
- - 'spec/helpers/projects/project_members_helper_spec.rb'
- - 'spec/helpers/projects_helper_spec.rb'
- - 'spec/initializers/hangouts_chat_http_override_spec.rb'
- - 'spec/lib/api/entities/merge_request_basic_spec.rb'
- - 'spec/lib/api/helpers/caching_spec.rb'
- - 'spec/lib/api/helpers/common_helpers_spec.rb'
- - 'spec/lib/atlassian/jira_connect/client_spec.rb'
- - 'spec/lib/atlassian/jira_connect/serializers/build_entity_spec.rb'
- - 'spec/lib/atlassian/jira_connect/serializers/deployment_entity_spec.rb'
- - 'spec/lib/atlassian/jira_connect/serializers/feature_flag_entity_spec.rb'
- - 'spec/lib/atlassian/jira_connect/serializers/repository_entity_spec.rb'
- - 'spec/lib/bitbucket_server/connection_spec.rb'
- - 'spec/lib/bulk_imports/common/pipelines/lfs_objects_pipeline_spec.rb'
- - 'spec/lib/bulk_imports/projects/pipelines/snippets_pipeline_spec.rb'
- - 'spec/lib/container_registry/client_spec.rb'
- - 'spec/lib/container_registry/gitlab_api_client_spec.rb'
- - 'spec/lib/gitlab/background_migration/encrypt_integration_properties_spec.rb'
- - 'spec/lib/gitlab/bitbucket_import/importer_spec.rb'
- - 'spec/lib/gitlab/chat/responder/mattermost_spec.rb'
- - 'spec/lib/gitlab/chat/responder/slack_spec.rb'
- - 'spec/lib/gitlab/ci/build/releaser_spec.rb'
- - 'spec/lib/gitlab/ci/parsers/accessibility/pa11y_spec.rb'
- - 'spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb'
- - 'spec/lib/gitlab/ci/parsers/coverage/sax_document_spec.rb'
- - 'spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb'
- - 'spec/lib/gitlab/ci/parsers/security/common_spec.rb'
- - 'spec/lib/gitlab/ci/parsers/test/junit_spec.rb'
- - 'spec/lib/gitlab/ci/runner_upgrade_check_spec.rb'
- - 'spec/lib/gitlab/composer/cache_spec.rb'
- - 'spec/lib/gitlab/composer/version_index_spec.rb'
- - 'spec/lib/gitlab/data_builder/pipeline_spec.rb'
- - 'spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb'
- - 'spec/lib/gitlab/database/postgres_hll/buckets_spec.rb'
- - 'spec/lib/gitlab/database/reindexing/grafana_notifier_spec.rb'
- - 'spec/lib/gitlab/diff/position_spec.rb'
- - 'spec/lib/gitlab/diff/stats_cache_spec.rb'
- - 'spec/lib/gitlab/discussions_diff/highlight_cache_spec.rb'
- - 'spec/lib/gitlab/error_tracking/context_payload_generator_spec.rb'
- - 'spec/lib/gitlab/error_tracking/processor/sidekiq_processor_spec.rb'
- - 'spec/lib/gitlab/external_authorization/client_spec.rb'
- - 'spec/lib/gitlab/external_authorization/response_spec.rb'
- - 'spec/lib/gitlab/file_hook_spec.rb'
- - 'spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb'
- - 'spec/lib/gitlab/github_import/client_spec.rb'
- - 'spec/lib/gitlab/gitlab_import/importer_spec.rb'
- - 'spec/lib/gitlab/grape_logging/loggers/exception_logger_spec.rb'
- - 'spec/lib/gitlab/harbor/client_spec.rb'
- - 'spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb'
- - 'spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb'
- - 'spec/lib/gitlab/json_cache_spec.rb'
- - 'spec/lib/gitlab/legacy_github_import/client_spec.rb'
- - 'spec/lib/gitlab/legacy_github_import/importer_spec.rb'
- - 'spec/lib/gitlab/lfs/client_spec.rb'
- - 'spec/lib/gitlab/merge_requests/mergeability/redis_interface_spec.rb'
- - 'spec/lib/gitlab/middleware/multipart_spec.rb'
- - 'spec/lib/gitlab/sidekiq_migrate_jobs_spec.rb'
- - 'spec/lib/gitlab/tracking/service_ping_context_spec.rb'
- - 'spec/lib/gitlab/tracking/standard_context_spec.rb'
- - 'spec/lib/gitlab/tracking_spec.rb'
- - 'spec/lib/gitlab/usage/service_ping/legacy_metric_timing_decorator_spec.rb'
- - 'spec/lib/gitlab/utils/json_size_estimator_spec.rb'
- - 'spec/lib/gitlab/version_info_spec.rb'
- - 'spec/lib/gitlab/webpack/manifest_spec.rb'
- - 'spec/lib/gitlab/workhorse_spec.rb'
- - 'spec/lib/gitlab/zentao/client_spec.rb'
- - 'spec/lib/grafana/client_spec.rb'
- - 'spec/lib/json_web_token/hmac_token_spec.rb'
- - 'spec/lib/mattermost/command_spec.rb'
- - 'spec/lib/mattermost/team_spec.rb'
- - 'spec/lib/microsoft_teams/notifier_spec.rb'
- - 'spec/lib/object_storage/direct_upload_spec.rb'
- - 'spec/lib/service_ping/devops_report_spec.rb'
- - 'spec/lib/version_check_spec.rb'
- - 'spec/mailers/notify_spec.rb'
- - 'spec/migrations/20220204194347_encrypt_integration_properties_spec.rb'
- - 'spec/migrations/20220412143552_consume_remaining_encrypt_integration_property_jobs_spec.rb'
- - 'spec/models/blob_viewer/package_json_spec.rb'
- - 'spec/models/ci/runner_spec.rb'
- - 'spec/models/concerns/prometheus_adapter_spec.rb'
- - 'spec/models/concerns/redis_cacheable_spec.rb'
- - 'spec/models/concerns/sensitive_serializable_hash_spec.rb'
- - 'spec/models/diff_discussion_spec.rb'
- - 'spec/models/diff_note_spec.rb'
- - 'spec/models/hooks/web_hook_spec.rb'
- - 'spec/models/integrations/datadog_spec.rb'
- - 'spec/models/integrations/jira_spec.rb'
- - 'spec/models/integrations/mattermost_slash_commands_spec.rb'
- - 'spec/models/integrations/mock_ci_spec.rb'
- - 'spec/models/merge_request_diff_commit_spec.rb'
- - 'spec/models/packages/composer/metadatum_spec.rb'
- - 'spec/models/terraform/state_spec.rb'
- - 'spec/presenters/packages/composer/packages_presenter_spec.rb'
- - 'spec/requests/api/ci/runner/jobs_request_post_spec.rb'
- - 'spec/requests/api/composer_packages_spec.rb'
- - 'spec/requests/api/conan_instance_packages_spec.rb'
- - 'spec/requests/api/conan_project_packages_spec.rb'
- - 'spec/requests/api/container_registry_event_spec.rb'
- - 'spec/requests/api/graphql/mutations/design_management/upload_spec.rb'
- - 'spec/requests/api/integrations/jira_connect/subscriptions_spec.rb'
- - 'spec/requests/api/internal/base_spec.rb'
- - 'spec/requests/api/merge_requests_spec.rb'
- - 'spec/requests/api/namespaces_spec.rb'
- - 'spec/requests/api/project_snapshots_spec.rb'
- - 'spec/requests/groups/settings/access_tokens_controller_spec.rb'
- - 'spec/requests/projects/incident_management/pagerduty_incidents_spec.rb'
- - 'spec/requests/projects/settings/access_tokens_controller_spec.rb'
- - 'spec/requests/users_controller_spec.rb'
- - 'spec/requests/whats_new_controller_spec.rb'
- - 'spec/scripts/pipeline_test_report_builder_spec.rb'
- - 'spec/serializers/ci/dag_job_entity_spec.rb'
- - 'spec/serializers/ci/dag_job_group_entity_spec.rb'
- - 'spec/serializers/ci/dag_pipeline_entity_spec.rb'
- - 'spec/serializers/ci/dag_pipeline_serializer_spec.rb'
- - 'spec/serializers/ci/dag_stage_entity_spec.rb'
- - 'spec/serializers/ci/daily_build_group_report_result_serializer_spec.rb'
- - 'spec/serializers/ci/lint/result_serializer_spec.rb'
- - 'spec/serializers/ci/trigger_entity_spec.rb'
- - 'spec/serializers/ci/trigger_serializer_spec.rb'
- - 'spec/serializers/diff_line_serializer_spec.rb'
- - 'spec/serializers/evidences/evidence_entity_spec.rb'
- - 'spec/serializers/feature_flags_client_serializer_spec.rb'
- - 'spec/serializers/group_link/group_group_link_entity_spec.rb'
- - 'spec/serializers/group_link/group_group_link_serializer_spec.rb'
- - 'spec/serializers/group_link/group_link_entity_spec.rb'
- - 'spec/serializers/group_link/project_group_link_entity_spec.rb'
- - 'spec/serializers/group_link/project_group_link_serializer_spec.rb'
- - 'spec/serializers/member_entity_spec.rb'
- - 'spec/serializers/member_serializer_spec.rb'
- - 'spec/serializers/member_user_entity_spec.rb'
- - 'spec/serializers/test_reports_comparer_serializer_spec.rb'
- - 'spec/services/ci/runners/process_runner_version_update_service_spec.rb'
- - 'spec/services/draft_notes/create_service_spec.rb'
- - 'spec/services/error_tracking/issue_details_service_spec.rb'
- - 'spec/services/error_tracking/issue_latest_event_service_spec.rb'
- - 'spec/services/error_tracking/list_issues_service_spec.rb'
- - 'spec/services/git/branch_push_service_spec.rb'
- - 'spec/services/jira/requests/projects/list_service_spec.rb'
- - 'spec/services/metrics/dashboard/transient_embed_service_spec.rb'
- - 'spec/services/packages/composer/create_package_service_spec.rb'
- - 'spec/services/packages/rubygems/metadata_extraction_service_spec.rb'
- - 'spec/services/projects/container_repository/third_party/cleanup_tags_service_spec.rb'
- - 'spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb'
- - 'spec/services/service_ping/submit_service_ping_service_spec.rb'
- - 'spec/services/webauthn/authenticate_service_spec.rb'
- - 'spec/services/webauthn/register_service_spec.rb'
- - 'spec/support/frontend_fixtures.rb'
- - 'spec/support/google_api/cloud_platform_helpers.rb'
- - 'spec/support/helpers/ci_artifact_metadata_generator.rb'
- - 'spec/support/helpers/dependency_proxy_helpers.rb'
- - 'spec/support/helpers/fake_webauthn_device.rb'
- - 'spec/support/helpers/features/two_factor_helpers.rb'
- - 'spec/support/helpers/graphql_helpers.rb'
- - 'spec/support/helpers/input_helper.rb'
- - 'spec/support/helpers/jira_integration_helpers.rb'
- - 'spec/support/helpers/kubernetes_helpers.rb'
- - 'spec/support/helpers/prometheus_helpers.rb'
- - 'spec/support/helpers/sentry_client_helpers.rb'
- - 'spec/support/helpers/usage_data_helpers.rb'
- - 'spec/support/import_export/configuration_helper.rb'
- - 'spec/support/shared_contexts/bulk_imports_requests_shared_context.rb'
- - 'spec/support/shared_contexts/features/error_tracking_shared_context.rb'
- - 'spec/support/shared_contexts/prometheus/alert_shared_context.rb'
- - 'spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb'
- - 'spec/support/shared_examples/blocks_unsafe_serialization_shared_examples.rb'
- - 'spec/support/shared_examples/controllers/rate_limited_endpoint_shared_examples.rb'
- - 'spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb'
- - 'spec/support/shared_examples/harbor/artifacts_controller_shared_examples.rb'
- - 'spec/support/shared_examples/harbor/repositories_controller_shared_examples.rb'
- - 'spec/support/shared_examples/harbor/tags_controller_shared_examples.rb'
- - 'spec/support/shared_examples/models/diff_positionable_note_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb'
- - 'spec/support/shared_examples/requests/rack_attack_shared_examples.rb'
- - 'spec/support_specs/helpers/graphql_helpers_spec.rb'
- - 'spec/tasks/gitlab/update_templates_rake_spec.rb'
- - 'spec/tasks/gitlab/usage_data_rake_spec.rb'
- - 'spec/tooling/lib/tooling/kubernetes_client_spec.rb'
- - 'spec/tooling/rspec_flaky/listener_spec.rb'
- - 'spec/workers/ci/runners/process_runner_version_update_worker_spec.rb'
- - 'spec/workers/gitlab/jira_import/stage/import_labels_worker_spec.rb'
- - 'spec/workers/packages/composer/cache_update_worker_spec.rb'
diff --git a/.rubocop_todo/gitlab/namespaced_class.yml b/.rubocop_todo/gitlab/namespaced_class.yml
index 3fb2df623e0..e49169bae67 100644
--- a/.rubocop_todo/gitlab/namespaced_class.yml
+++ b/.rubocop_todo/gitlab/namespaced_class.yml
@@ -162,9 +162,6 @@ Gitlab/NamespacedClass:
- 'app/models/epic.rb'
- 'app/models/event.rb'
- 'app/models/event_collection.rb'
- - 'app/models/experiment.rb'
- - 'app/models/experiment_subject.rb'
- - 'app/models/experiment_user.rb'
- 'app/models/exported_protected_branch.rb'
- 'app/models/external_issue.rb'
- 'app/models/external_pull_request.rb'
@@ -396,15 +393,15 @@ Gitlab/NamespacedClass:
- 'app/policies/personal_access_token_policy.rb'
- 'app/policies/personal_snippet_policy.rb'
- 'app/policies/project_ci_cd_setting_policy.rb'
+ - 'app/policies/project_hook_policy.rb'
- 'app/policies/project_label_policy.rb'
- 'app/policies/project_member_policy.rb'
- 'app/policies/project_policy.rb'
- 'app/policies/project_snippet_policy.rb'
- 'app/policies/project_statistics_policy.rb'
- - 'app/policies/project_hook_policy.rb'
- 'app/policies/prometheus_alert_policy.rb'
- - 'app/policies/protected_branch_policy.rb'
- 'app/policies/protected_branch_access_policy.rb'
+ - 'app/policies/protected_branch_policy.rb'
- 'app/policies/release_policy.rb'
- 'app/policies/repository_policy.rb'
- 'app/policies/resource_label_event_policy.rb'
@@ -472,8 +469,6 @@ Gitlab/NamespacedClass:
- 'app/serializers/base_discussion_entity.rb'
- 'app/serializers/base_serializer.rb'
- 'app/serializers/blob_entity.rb'
- - 'app/serializers/board_serializer.rb'
- - 'app/serializers/board_simple_entity.rb'
- 'app/serializers/build_action_entity.rb'
- 'app/serializers/build_artifact_entity.rb'
- 'app/serializers/build_coverage_entity.rb'
@@ -497,8 +492,6 @@ Gitlab/NamespacedClass:
- 'app/serializers/container_tag_entity.rb'
- 'app/serializers/container_tags_serializer.rb'
- 'app/serializers/context_commits_diff_entity.rb'
- - 'app/serializers/current_board_entity.rb'
- - 'app/serializers/current_board_serializer.rb'
- 'app/serializers/current_user_entity.rb'
- 'app/serializers/deploy_keys_project_entity.rb'
- 'app/serializers/deployment_cluster_entity.rb'
@@ -690,7 +683,7 @@ Gitlab/NamespacedClass:
- 'app/validators/addressable_url_validator.rb'
- 'app/validators/any_field_validator.rb'
- 'app/validators/array_members_validator.rb'
- - 'app/validators/branch_filter_validator.rb'
+ - 'app/validators/bytesize_validator.rb'
- 'app/validators/certificate_fingerprint_validator.rb'
- 'app/validators/certificate_key_validator.rb'
- 'app/validators/certificate_validator.rb'
@@ -708,6 +701,7 @@ Gitlab/NamespacedClass:
- 'app/validators/gitlab/zoom_url_validator.rb'
- 'app/validators/html_safety_validator.rb'
- 'app/validators/ip_address_validator.rb'
+ - 'app/validators/iso8601_date_validator.rb'
- 'app/validators/js_regex_validator.rb'
- 'app/validators/json_schema_validator.rb'
- 'app/validators/key_restriction_validator.rb'
@@ -725,15 +719,12 @@ Gitlab/NamespacedClass:
- 'app/validators/top_level_group_validator.rb'
- 'app/validators/untrusted_regexp_validator.rb'
- 'app/validators/x509_certificate_credentials_validator.rb'
- - 'app/validators/bytesize_validator.rb'
- 'app/workers/admin_email_worker.rb'
- 'app/workers/approve_blocked_pending_approval_users_worker.rb'
- - 'app/workers/archive_trace_worker.rb'
- 'app/workers/authorized_keys_worker.rb'
- 'app/workers/authorized_projects_worker.rb'
- 'app/workers/auto_merge_process_worker.rb'
- 'app/workers/background_migration_worker.rb'
- - 'app/workers/build_finished_worker.rb'
- 'app/workers/build_hooks_worker.rb'
- 'app/workers/build_queue_worker.rb'
- 'app/workers/build_success_worker.rb'
@@ -964,13 +955,13 @@ Gitlab/NamespacedClass:
- 'ee/app/policies/dast_site_validation_policy.rb'
- 'ee/app/policies/epic_policy.rb'
- 'ee/app/policies/geo_node_policy.rb'
+ - 'ee/app/policies/group_hook_policy.rb'
- 'ee/app/policies/instance_security_dashboard_policy.rb'
- 'ee/app/policies/issuable_metric_image_policy.rb'
- 'ee/app/policies/iteration_policy.rb'
- 'ee/app/policies/push_rule_policy.rb'
- 'ee/app/policies/saml_provider_policy.rb'
- 'ee/app/policies/vulnerability_policy.rb'
- - 'ee/app/policies/group_hook_policy.rb'
- 'ee/app/presenters/approval_rule_presenter.rb'
- 'ee/app/presenters/audit_event_presenter.rb'
- 'ee/app/presenters/epic_issue_presenter.rb'
@@ -982,9 +973,6 @@ Gitlab/NamespacedClass:
- 'ee/app/serializers/audit_event_entity.rb'
- 'ee/app/serializers/audit_event_serializer.rb'
- 'ee/app/serializers/blocking_merge_request_entity.rb'
- - 'ee/app/serializers/board_assignee_entity.rb'
- - 'ee/app/serializers/board_label_entity.rb'
- - 'ee/app/serializers/board_milestone_entity.rb'
- 'ee/app/serializers/dashboard_environment_entity.rb'
- 'ee/app/serializers/dashboard_environments_project_entity.rb'
- 'ee/app/serializers/dashboard_environments_serializer.rb'
@@ -1078,7 +1066,6 @@ Gitlab/NamespacedClass:
- 'ee/app/workers/sync_seat_link_worker.rb'
- 'ee/app/workers/update_all_mirrors_worker.rb'
- 'ee/app/workers/update_max_seats_used_for_gitlab_com_subscriptions_worker.rb'
- - 'ee/lib/gitlab/auth_logger.rb'
- 'ee/lib/gitlab/authority_analyzer.rb'
- 'ee/lib/gitlab/cidr.rb'
- 'ee/lib/gitlab/custom_file_templates.rb'
@@ -1088,7 +1075,6 @@ Gitlab/NamespacedClass:
- 'ee/lib/gitlab/ip_address_state.rb'
- 'ee/lib/gitlab/items_collection.rb'
- 'ee/lib/gitlab/manual_quarterly_co_term_banner.rb'
- - 'ee/lib/gitlab/pagination_delegate.rb'
- 'ee/lib/gitlab/path_locks_finder.rb'
- 'ee/lib/gitlab/proxy.rb'
- 'ee/lib/gitlab/return_to_location.rb'
@@ -1114,7 +1100,6 @@ Gitlab/NamespacedClass:
- 'lib/gitlab/avatar_cache.rb'
- 'lib/gitlab/backup_logger.rb'
- 'lib/gitlab/base_doorkeeper_controller.rb'
- - 'lib/gitlab/batch_pop_queueing.rb'
- 'lib/gitlab/batch_worker_context.rb'
- 'lib/gitlab/blame.rb'
- 'lib/gitlab/branch_push_merge_commit_analyzer.rb'
@@ -1147,7 +1132,6 @@ Gitlab/NamespacedClass:
- 'lib/gitlab/exceptions_app.rb'
- 'lib/gitlab/exclusive_lease.rb'
- 'lib/gitlab/experiment/rollout/feature.rb'
- - 'lib/gitlab/experimentation_logger.rb'
- 'lib/gitlab/fake_application_settings.rb'
- 'lib/gitlab/favicon.rb'
- 'lib/gitlab/feature_categories.rb'
@@ -1269,7 +1253,6 @@ Gitlab/NamespacedClass:
- 'lib/gitlab/wiki_file_finder.rb'
- 'lib/gitlab/workhorse.rb'
- 'lib/gitlab/zoom_link_extractor.rb'
- - 'lib/tasks/gitlab/graphql.rake'
- 'lib/tasks/gitlab/seed/group_seed.rake'
- 'lib/tasks/import.rake'
- 'lib/tasks/tokens.rake'
diff --git a/.rubocop_todo/gitlab/service_response.yml b/.rubocop_todo/gitlab/service_response.yml
index 03b73d6491d..d6ce9633882 100644
--- a/.rubocop_todo/gitlab/service_response.yml
+++ b/.rubocop_todo/gitlab/service_response.yml
@@ -64,7 +64,6 @@ Gitlab/ServiceResponse:
- 'ee/app/services/vulnerability_issue_links/create_service.rb'
- 'ee/app/services/vulnerability_issue_links/delete_service.rb'
- 'ee/spec/graphql/mutations/security/finding/dismiss_spec.rb'
- - 'spec/controllers/boards/issues_controller_spec.rb'
- 'spec/controllers/import/bulk_imports_controller_spec.rb'
- 'spec/controllers/import/fogbugz_controller_spec.rb'
- 'spec/controllers/projects/alerting/notifications_controller_spec.rb'
diff --git a/.rubocop_todo/gitlab/strong_memoize_attr.yml b/.rubocop_todo/gitlab/strong_memoize_attr.yml
new file mode 100644
index 00000000000..5afb7574ff1
--- /dev/null
+++ b/.rubocop_todo/gitlab/strong_memoize_attr.yml
@@ -0,0 +1,759 @@
+---
+# Cop supports --autocorrect.
+Gitlab/StrongMemoizeAttr:
+ Details: grace period
+ Exclude:
+ - 'app/components/pajamas/avatar_component.rb'
+ - 'app/controllers/application_controller.rb'
+ - 'app/controllers/concerns/boards_actions.rb'
+ - 'app/controllers/concerns/creates_commit.rb'
+ - 'app/controllers/concerns/find_snippet.rb'
+ - 'app/controllers/concerns/impersonation.rb'
+ - 'app/controllers/concerns/issuable_actions.rb'
+ - 'app/controllers/concerns/issuable_collections.rb'
+ - 'app/controllers/concerns/known_sign_in.rb'
+ - 'app/controllers/concerns/wiki_actions.rb'
+ - 'app/controllers/groups/dependency_proxy_for_containers_controller.rb'
+ - 'app/controllers/ide_controller.rb'
+ - 'app/controllers/import/github_controller.rb'
+ - 'app/controllers/invites_controller.rb'
+ - 'app/controllers/jira_connect/application_controller.rb'
+ - 'app/controllers/jwt_controller.rb'
+ - 'app/controllers/oauth/authorizations_controller.rb'
+ - 'app/controllers/projects/analytics/cycle_analytics/stages_controller.rb'
+ - 'app/controllers/projects/boards_controller.rb'
+ - 'app/controllers/projects/compare_controller.rb'
+ - 'app/controllers/projects/forks_controller.rb'
+ - 'app/controllers/projects/import/jira_controller.rb'
+ - 'app/controllers/projects/incidents_controller.rb'
+ - 'app/controllers/projects/merge_requests/drafts_controller.rb'
+ - 'app/controllers/projects/merge_requests_controller.rb'
+ - 'app/controllers/projects/metrics_dashboard_controller.rb'
+ - 'app/controllers/projects/milestones_controller.rb'
+ - 'app/controllers/projects/pipelines/application_controller.rb'
+ - 'app/controllers/projects/pipelines_controller.rb'
+ - 'app/controllers/projects/todos_controller.rb'
+ - 'app/controllers/repositories/git_http_client_controller.rb'
+ - 'app/controllers/repositories/lfs_api_controller.rb'
+ - 'app/controllers/sessions_controller.rb'
+ - 'app/controllers/whats_new_controller.rb'
+ - 'app/finders/autocomplete/users_finder.rb'
+ - 'app/finders/ci/commit_statuses_finder.rb'
+ - 'app/finders/ci/pipelines_for_merge_request_finder.rb'
+ - 'app/finders/cluster_ancestors_finder.rb'
+ - 'app/finders/clusters/knative_services_finder.rb'
+ - 'app/finders/concerns/finder_with_group_hierarchy.rb'
+ - 'app/finders/crm/contacts_finder.rb'
+ - 'app/finders/crm/organizations_finder.rb'
+ - 'app/finders/groups/accepting_group_transfers_finder.rb'
+ - 'app/finders/issuable_finder.rb'
+ - 'app/finders/issuable_finder/params.rb'
+ - 'app/finders/issuables/label_filter.rb'
+ - 'app/finders/issues_finder/params.rb'
+ - 'app/finders/license_template_finder.rb'
+ - 'app/finders/merge_requests_finder/params.rb'
+ - 'app/finders/projects/members/effective_access_level_finder.rb'
+ - 'app/finders/releases/evidence_pipeline_finder.rb'
+ - 'app/finders/releases_finder.rb'
+ - 'app/finders/snippets_finder.rb'
+ - 'app/finders/todos_finder.rb'
+ - 'app/graphql/resolvers/issue_status_counts_resolver.rb'
+ - 'app/graphql/resolvers/issues/base_parent_resolver.rb'
+ - 'app/graphql/resolvers/namespace_projects_resolver.rb'
+ - 'app/graphql/resolvers/work_items_resolver.rb'
+ - 'app/graphql/types/board_list_type.rb'
+ - 'app/helpers/appearances_helper.rb'
+ - 'app/helpers/broadcast_messages_helper.rb'
+ - 'app/helpers/diff_helper.rb'
+ - 'app/helpers/operations_helper.rb'
+ - 'app/helpers/page_layout_helper.rb'
+ - 'app/helpers/projects_helper.rb'
+ - 'app/helpers/sessions_helper.rb'
+ - 'app/helpers/timeboxes_helper.rb'
+ - 'app/models/alert_management/alert.rb'
+ - 'app/models/application_setting_implementation.rb'
+ - 'app/models/blob_viewer/go_mod.rb'
+ - 'app/models/blob_viewer/metrics_dashboard_yml.rb'
+ - 'app/models/bulk_imports/export.rb'
+ - 'app/models/bulk_imports/export_status.rb'
+ - 'app/models/bulk_imports/file_transfer/base_config.rb'
+ - 'app/models/ci/bridge.rb'
+ - 'app/models/ci/build.rb'
+ - 'app/models/ci/build_dependencies.rb'
+ - 'app/models/ci/build_metadata.rb'
+ - 'app/models/ci/commit_with_pipeline.rb'
+ - 'app/models/ci/group.rb'
+ - 'app/models/ci/job_artifact.rb'
+ - 'app/models/ci/pipeline.rb'
+ - 'app/models/ci/processable.rb'
+ - 'app/models/ci/runner.rb'
+ - 'app/models/clusters/cluster.rb'
+ - 'app/models/clusters/providers/aws.rb'
+ - 'app/models/commit.rb'
+ - 'app/models/commit_collection.rb'
+ - 'app/models/compare.rb'
+ - 'app/models/concerns/analytics/cycle_analytics/stage.rb'
+ - 'app/models/concerns/avatarable.rb'
+ - 'app/models/concerns/cascading_namespace_setting_attribute.rb'
+ - 'app/models/concerns/ci/contextable.rb'
+ - 'app/models/concerns/ci/partitionable.rb'
+ - 'app/models/concerns/discussion_on_diff.rb'
+ - 'app/models/concerns/has_repository.rb'
+ - 'app/models/concerns/has_wiki.rb'
+ - 'app/models/concerns/has_wiki_page_meta_attributes.rb'
+ - 'app/models/concerns/mentionable/reference_regexes.rb'
+ - 'app/models/concerns/redis_cacheable.rb'
+ - 'app/models/concerns/require_email_verification.rb'
+ - 'app/models/concerns/resolvable_discussion.rb'
+ - 'app/models/concerns/security/latest_pipeline_information.rb'
+ - 'app/models/container_registry/event.rb'
+ - 'app/models/container_repository.rb'
+ - 'app/models/customer_relations/contact_state_counts.rb'
+ - 'app/models/deploy_token.rb'
+ - 'app/models/deployment.rb'
+ - 'app/models/deployment_metrics.rb'
+ - 'app/models/design_management/design.rb'
+ - 'app/models/design_management/design_at_version.rb'
+ - 'app/models/design_management/version.rb'
+ - 'app/models/diff_note.rb'
+ - 'app/models/draft_note.rb'
+ - 'app/models/environment.rb'
+ - 'app/models/environment_status.rb'
+ - 'app/models/error_tracking/project_error_tracking_setting.rb'
+ - 'app/models/event.rb'
+ - 'app/models/event_collection.rb'
+ - 'app/models/group.rb'
+ - 'app/models/incident_management/project_incident_management_setting.rb'
+ - 'app/models/integrations/jira.rb'
+ - 'app/models/internal_id.rb'
+ - 'app/models/member.rb'
+ - 'app/models/merge_request.rb'
+ - 'app/models/merge_request_diff.rb'
+ - 'app/models/namespace.rb'
+ - 'app/models/namespaces/traversal/linear.rb'
+ - 'app/models/namespaces/traversal/recursive.rb'
+ - 'app/models/note.rb'
+ - 'app/models/onboarding/completion.rb'
+ - 'app/models/packages/go/module.rb'
+ - 'app/models/packages/go/module_version.rb'
+ - 'app/models/packages/package.rb'
+ - 'app/models/pages/lookup_path.rb'
+ - 'app/models/project.rb'
+ - 'app/models/release.rb'
+ - 'app/models/resource_event.rb'
+ - 'app/models/service_desk_setting.rb'
+ - 'app/models/snippet.rb'
+ - 'app/models/snippet_input_action_collection.rb'
+ - 'app/models/state_note.rb'
+ - 'app/models/tree.rb'
+ - 'app/models/uploads/fog.rb'
+ - 'app/models/user.rb'
+ - 'app/models/wiki_page.rb'
+ - 'app/models/work_item.rb'
+ - 'app/policies/application_setting/term_policy.rb'
+ - 'app/policies/note_policy.rb'
+ - 'app/presenters/blobs/unfold_presenter.rb'
+ - 'app/presenters/ci/build_runner_presenter.rb'
+ - 'app/presenters/ci/pipeline_artifacts/code_coverage_presenter.rb'
+ - 'app/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter.rb'
+ - 'app/presenters/ci/pipeline_presenter.rb'
+ - 'app/presenters/clusters/cluster_presenter.rb'
+ - 'app/presenters/merge_request_presenter.rb'
+ - 'app/presenters/packages/conan/package_presenter.rb'
+ - 'app/presenters/packages/nuget/packages_metadata_presenter.rb'
+ - 'app/presenters/packages/nuget/search_results_presenter.rb'
+ - 'app/presenters/project_presenter.rb'
+ - 'app/presenters/projects/settings/deploy_keys_presenter.rb'
+ - 'app/serializers/ci/pipeline_entity.rb'
+ - 'app/serializers/concerns/diff_file_conflict_type.rb'
+ - 'app/serializers/diff_file_base_entity.rb'
+ - 'app/serializers/integrations/field_entity.rb'
+ - 'app/serializers/linked_project_issue_entity.rb'
+ - 'app/serializers/suggestion_entity.rb'
+ - 'app/services/alert_management/alerts/update_service.rb'
+ - 'app/services/alert_management/create_alert_issue_service.rb'
+ - 'app/services/alert_management/process_prometheus_alert_service.rb'
+ - 'app/services/auth/dependency_proxy_authentication_service.rb'
+ - 'app/services/authorized_project_update/project_recalculate_service.rb'
+ - 'app/services/auto_merge/base_service.rb'
+ - 'app/services/award_emojis/add_service.rb'
+ - 'app/services/base_project_service.rb'
+ - 'app/services/boards/base_items_list_service.rb'
+ - 'app/services/boards/lists/base_create_service.rb'
+ - 'app/services/ci/create_downstream_pipeline_service.rb'
+ - 'app/services/ci/create_web_ide_terminal_service.rb'
+ - 'app/services/ci/job_artifacts/destroy_batch_service.rb'
+ - 'app/services/ci/parse_dotenv_artifact_service.rb'
+ - 'app/services/ci/pipeline_artifacts/coverage_report_service.rb'
+ - 'app/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service.rb'
+ - 'app/services/ci/pipeline_artifacts/destroy_all_expired_service.rb'
+ - 'app/services/ci/pipeline_processing/atomic_processing_service/status_collection.rb'
+ - 'app/services/ci/pipeline_schedules/calculate_next_run_service.rb'
+ - 'app/services/ci/pipeline_trigger_service.rb'
+ - 'app/services/ci/pipelines/hook_service.rb'
+ - 'app/services/ci/queue/build_queue_service.rb'
+ - 'app/services/ci/update_build_state_service.rb'
+ - 'app/services/clusters/agents/refresh_authorization_service.rb'
+ - 'app/services/clusters/aws/finalize_creation_service.rb'
+ - 'app/services/clusters/integrations/prometheus_health_check_service.rb'
+ - 'app/services/concerns/alert_management/alert_processing.rb'
+ - 'app/services/concerns/incident_management/settings.rb'
+ - 'app/services/concerns/issues/resolve_discussions.rb'
+ - 'app/services/concerns/suggestible.rb'
+ - 'app/services/concerns/update_repository_storage_methods.rb'
+ - 'app/services/container_expiration_policies/update_service.rb'
+ - 'app/services/dependency_proxy/image_ttl_group_policies/update_service.rb'
+ - 'app/services/discussions/resolve_service.rb'
+ - 'app/services/error_tracking/collect_error_service.rb'
+ - 'app/services/error_tracking/issue_details_service.rb'
+ - 'app/services/feature_flags/base_service.rb'
+ - 'app/services/git/base_hooks_service.rb'
+ - 'app/services/git/branch_hooks_service.rb'
+ - 'app/services/git/branch_push_service.rb'
+ - 'app/services/git/tag_hooks_service.rb'
+ - 'app/services/git/wiki_push_service/change.rb'
+ - 'app/services/groups/open_issues_count_service.rb'
+ - 'app/services/import/github_service.rb'
+ - 'app/services/import/gitlab_projects/file_acquisition_strategies/remote_file_s3.rb'
+ - 'app/services/incident_management/issuable_escalation_statuses/prepare_update_service.rb'
+ - 'app/services/incident_management/pager_duty/process_webhook_service.rb'
+ - 'app/services/integrations/test/project_service.rb'
+ - 'app/services/issuable/discussions_list_service.rb'
+ - 'app/services/issues/reorder_service.rb'
+ - 'app/services/jira_connect_subscriptions/create_service.rb'
+ - 'app/services/jira_import/users_mapper_service.rb'
+ - 'app/services/lfs/push_service.rb'
+ - 'app/services/markdown_content_rewriter_service.rb'
+ - 'app/services/members/invitation_reminder_email_service.rb'
+ - 'app/services/merge_requests/build_service.rb'
+ - 'app/services/merge_requests/merge_base_service.rb'
+ - 'app/services/merge_requests/mergeability/detailed_merge_status_service.rb'
+ - 'app/services/merge_requests/mergeability/logger.rb'
+ - 'app/services/merge_requests/mergeability/run_checks_service.rb'
+ - 'app/services/merge_requests/mergeability_check_service.rb'
+ - 'app/services/merge_requests/outdated_discussion_diff_lines_service.rb'
+ - 'app/services/merge_requests/pushed_branches_service.rb'
+ - 'app/services/merge_requests/refresh_service.rb'
+ - 'app/services/metrics/dashboard/clone_dashboard_service.rb'
+ - 'app/services/metrics/dashboard/custom_metric_embed_service.rb'
+ - 'app/services/metrics/dashboard/dynamic_embed_service.rb'
+ - 'app/services/metrics/dashboard/gitlab_alert_embed_service.rb'
+ - 'app/services/namespaces/package_settings/update_service.rb'
+ - 'app/services/packages/cleanup/execute_policy_service.rb'
+ - 'app/services/packages/cleanup/update_policy_service.rb'
+ - 'app/services/packages/composer/create_package_service.rb'
+ - 'app/services/packages/debian/extract_changes_metadata_service.rb'
+ - 'app/services/packages/debian/extract_metadata_service.rb'
+ - 'app/services/packages/debian/find_or_create_package_service.rb'
+ - 'app/services/packages/debian/generate_distribution_key_service.rb'
+ - 'app/services/packages/debian/generate_distribution_service.rb'
+ - 'app/services/packages/debian/process_changes_service.rb'
+ - 'app/services/packages/debian/process_package_file_service.rb'
+ - 'app/services/packages/helm/process_file_service.rb'
+ - 'app/services/packages/maven/metadata/base_create_xml_service.rb'
+ - 'app/services/packages/maven/metadata/create_plugins_xml_service.rb'
+ - 'app/services/packages/maven/metadata/create_versions_xml_service.rb'
+ - 'app/services/packages/maven/metadata/sync_service.rb'
+ - 'app/services/packages/npm/create_package_service.rb'
+ - 'app/services/packages/npm/create_tag_service.rb'
+ - 'app/services/packages/nuget/metadata_extraction_service.rb'
+ - 'app/services/packages/nuget/search_service.rb'
+ - 'app/services/packages/nuget/sync_metadatum_service.rb'
+ - 'app/services/packages/nuget/update_package_from_metadata_service.rb'
+ - 'app/services/packages/pypi/create_package_service.rb'
+ - 'app/services/packages/rpm/parse_package_service.rb'
+ - 'app/services/packages/rubygems/dependency_resolver_service.rb'
+ - 'app/services/packages/rubygems/process_gem_service.rb'
+ - 'app/services/packages/terraform_module/create_package_service.rb'
+ - 'app/services/packages/update_tags_service.rb'
+ - 'app/services/projects/container_repository/cleanup_tags_base_service.rb'
+ - 'app/services/projects/container_repository/third_party/cleanup_tags_service.rb'
+ - 'app/services/projects/create_from_template_service.rb'
+ - 'app/services/projects/gitlab_projects_import_service.rb'
+ - 'app/services/projects/lfs_pointers/lfs_object_download_list_service.rb'
+ - 'app/services/projects/open_issues_count_service.rb'
+ - 'app/services/projects/record_target_platforms_service.rb'
+ - 'app/services/projects/update_remote_mirror_service.rb'
+ - 'app/services/projects/update_statistics_service.rb'
+ - 'app/services/prometheus/proxy_service.rb'
+ - 'app/services/quick_actions/interpret_service.rb'
+ - 'app/services/releases/base_service.rb'
+ - 'app/services/resource_access_tokens/revoke_service.rb'
+ - 'app/services/resource_events/base_synthetic_notes_builder_service.rb'
+ - 'app/services/search/global_service.rb'
+ - 'app/services/search/project_service.rb'
+ - 'app/services/search_service.rb'
+ - 'app/services/security/ci_configuration/sast_parser_service.rb'
+ - 'app/services/test_hooks/project_service.rb'
+ - 'app/services/test_hooks/system_service.rb'
+ - 'app/uploaders/file_mover.rb'
+ - 'app/uploaders/object_storage/cdn.rb'
+ - 'app/uploaders/object_storage/cdn/google_cdn.rb'
+ - 'app/workers/concerns/each_shard_worker.rb'
+ - 'app/workers/concerns/limited_capacity/worker.rb'
+ - 'app/workers/concerns/packages/cleanup_artifact_worker.rb'
+ - 'app/workers/container_expiration_policies/cleanup_container_repository_worker.rb'
+ - 'app/workers/container_registry/delete_container_repository_worker.rb'
+ - 'app/workers/container_registry/migration/enqueuer_worker.rb'
+ - 'app/workers/database/batched_background_migration/execution_worker.rb'
+ - 'app/workers/database/batched_background_migration/single_database_worker.rb'
+ - 'app/workers/error_tracking_issue_link_worker.rb'
+ - 'app/workers/merge_request_cleanup_refs_worker.rb'
+ - 'app/workers/packages/cleanup/execute_policy_worker.rb'
+ - 'app/workers/packages/debian/generate_distribution_worker.rb'
+ - 'app/workers/packages/debian/process_changes_worker.rb'
+ - 'app/workers/packages/maven/metadata/sync_worker.rb'
+ - 'app/workers/projects/inactive_projects_deletion_cron_worker.rb'
+ - 'ee/app/controllers/admin/audit_logs_controller.rb'
+ - 'ee/app/controllers/concerns/description_diff_actions.rb'
+ - 'ee/app/controllers/concerns/ee/lfs_request.rb'
+ - 'ee/app/controllers/concerns/ee/routable_actions/sso_enforcement_redirect.rb'
+ - 'ee/app/controllers/concerns/epic_relations.rb'
+ - 'ee/app/controllers/ee/admin/health_check_controller.rb'
+ - 'ee/app/controllers/ee/groups/settings/repository_controller.rb'
+ - 'ee/app/controllers/ee/groups_controller.rb'
+ - 'ee/app/controllers/ee/registrations/welcome_controller.rb'
+ - 'ee/app/controllers/ee/repositories/git_http_controller.rb'
+ - 'ee/app/controllers/groups/audit_events_controller.rb'
+ - 'ee/app/controllers/groups/epic_boards_controller.rb'
+ - 'ee/app/controllers/groups/push_rules_controller.rb'
+ - 'ee/app/controllers/groups/todos_controller.rb'
+ - 'ee/app/controllers/projects/audit_events_controller.rb'
+ - 'ee/app/controllers/projects/subscriptions_controller.rb'
+ - 'ee/app/controllers/subscriptions_controller.rb'
+ - 'ee/app/finders/approval_rules/group_finder.rb'
+ - 'ee/app/finders/concerns/epics/with_access_check.rb'
+ - 'ee/app/finders/ee/issues_finder.rb'
+ - 'ee/app/finders/epics_finder.rb'
+ - 'ee/app/finders/incident_management/oncall_users_finder.rb'
+ - 'ee/app/finders/requirements_management/requirements_finder.rb'
+ - 'ee/app/finders/security/pipeline_vulnerabilities_finder.rb'
+ - 'ee/app/finders/security/training_providers/base_url_finder.rb'
+ - 'ee/app/graphql/resolvers/epics_resolver.rb'
+ - 'ee/app/graphql/resolvers/vulnerabilities_base_resolver.rb'
+ - 'ee/app/helpers/admin/emails_helper.rb'
+ - 'ee/app/helpers/auditor_user_helper.rb'
+ - 'ee/app/helpers/billing_plans_helper.rb'
+ - 'ee/app/helpers/ee/ci/runners_helper.rb'
+ - 'ee/app/helpers/ee/preferences_helper.rb'
+ - 'ee/app/helpers/ee/registrations_helper.rb'
+ - 'ee/app/helpers/ee/timeboxes_helper.rb'
+ - 'ee/app/helpers/ee/trial_helper.rb'
+ - 'ee/app/helpers/ee/welcome_helper.rb'
+ - 'ee/app/helpers/license_monitoring_helper.rb'
+ - 'ee/app/helpers/paid_feature_callout_helper.rb'
+ - 'ee/app/helpers/subscriptions_helper.rb'
+ - 'ee/app/helpers/trial_status_widget_helper.rb'
+ - 'ee/app/models/approval_merge_request_rule.rb'
+ - 'ee/app/models/approval_state.rb'
+ - 'ee/app/models/approval_wrapped_any_approver_rule.rb'
+ - 'ee/app/models/approval_wrapped_code_owner_rule.rb'
+ - 'ee/app/models/approval_wrapped_rule.rb'
+ - 'ee/app/models/approvals/scan_finding_wrapped_rule_set.rb'
+ - 'ee/app/models/approvals/wrapped_rule_set.rb'
+ - 'ee/app/models/burndown.rb'
+ - 'ee/app/models/ci/minutes/limit.rb'
+ - 'ee/app/models/concerns/deprecated_approvals_before_merge.rb'
+ - 'ee/app/models/concerns/ee/approvable.rb'
+ - 'ee/app/models/concerns/ee/issue_available_features.rb'
+ - 'ee/app/models/concerns/insights_feature.rb'
+ - 'ee/app/models/concerns/security/scan_execution_policy.rb'
+ - 'ee/app/models/deployments/approval_summary.rb'
+ - 'ee/app/models/ee/audit_event.rb'
+ - 'ee/app/models/ee/ci/bridge.rb'
+ - 'ee/app/models/ee/ci/build.rb'
+ - 'ee/app/models/ee/ci/build_dependencies.rb'
+ - 'ee/app/models/ee/ci/job_artifact.rb'
+ - 'ee/app/models/ee/ci/pipeline.rb'
+ - 'ee/app/models/ee/ci/runner.rb'
+ - 'ee/app/models/ee/deployment.rb'
+ - 'ee/app/models/ee/environment.rb'
+ - 'ee/app/models/ee/group.rb'
+ - 'ee/app/models/ee/integrations/jira.rb'
+ - 'ee/app/models/ee/list.rb'
+ - 'ee/app/models/ee/merge_request.rb'
+ - 'ee/app/models/ee/namespace.rb'
+ - 'ee/app/models/ee/namespace/storage/notification.rb'
+ - 'ee/app/models/ee/project.rb'
+ - 'ee/app/models/ee/snippet.rb'
+ - 'ee/app/models/ee/user.rb'
+ - 'ee/app/models/ee/work_item.rb'
+ - 'ee/app/models/gitlab/seat_link_data.rb'
+ - 'ee/app/models/gitlab_subscription.rb'
+ - 'ee/app/models/issuables_analytics.rb'
+ - 'ee/app/models/license.rb'
+ - 'ee/app/models/namespaces/storage/root_excess_size.rb'
+ - 'ee/app/models/sca/license_compliance.rb'
+ - 'ee/app/models/security/orchestration_policy_configuration.rb'
+ - 'ee/app/models/security/orchestration_policy_rule_schedule.rb'
+ - 'ee/app/models/vulnerabilities/finding.rb'
+ - 'ee/app/presenters/approval_rule_presenter.rb'
+ - 'ee/app/presenters/ci/minutes/usage_presenter.rb'
+ - 'ee/app/presenters/merge_request_approver_presenter.rb'
+ - 'ee/app/serializers/dashboard_operations_project_entity.rb'
+ - 'ee/app/serializers/ee/member_user_entity.rb'
+ - 'ee/app/services/app_sec/dast/pipelines/find_latest_service.rb'
+ - 'ee/app/services/app_sec/dast/scan_configs/build_service.rb'
+ - 'ee/app/services/approval_rules/params_filtering_service.rb'
+ - 'ee/app/services/boards/epics/position_create_service.rb'
+ - 'ee/app/services/ci/compare_license_scanning_reports_collapsed_service.rb'
+ - 'ee/app/services/ci/minutes/update_project_and_namespace_usage_service.rb'
+ - 'ee/app/services/ci/subscribe_bridge_service.rb'
+ - 'ee/app/services/ci/sync_reports_to_approval_rules_service.rb'
+ - 'ee/app/services/deployments/approval_service.rb'
+ - 'ee/app/services/ee/allowed_email_domains/update_service.rb'
+ - 'ee/app/services/ee/auto_merge_service.rb'
+ - 'ee/app/services/ee/boards/lists/create_service.rb'
+ - 'ee/app/services/ee/ci/retry_pipeline_service.rb'
+ - 'ee/app/services/ee/incident_management/issuable_escalation_statuses/prepare_update_service.rb'
+ - 'ee/app/services/ee/integrations/test/project_service.rb'
+ - 'ee/app/services/ee/ip_restrictions/update_service.rb'
+ - 'ee/app/services/ee/issuable_base_service.rb'
+ - 'ee/app/services/ee/issues/export_csv_service.rb'
+ - 'ee/app/services/ee/merge_requests/merge_base_service.rb'
+ - 'ee/app/services/ee/post_receive_service.rb'
+ - 'ee/app/services/ee/projects/create_from_template_service.rb'
+ - 'ee/app/services/ee/projects/gitlab_projects_import_service.rb'
+ - 'ee/app/services/ee/protected_branches/create_service.rb'
+ - 'ee/app/services/ee/search/global_service.rb'
+ - 'ee/app/services/ee/search/group_service.rb'
+ - 'ee/app/services/ee/search_service.rb'
+ - 'ee/app/services/ee/users/authorized_build_service.rb'
+ - 'ee/app/services/ee/users/build_service.rb'
+ - 'ee/app/services/ee/users/update_service.rb'
+ - 'ee/app/services/elastic/cluster_reindexing_service.rb'
+ - 'ee/app/services/epic_issues/list_service.rb'
+ - 'ee/app/services/epics/descendant_count_service.rb'
+ - 'ee/app/services/epics/related_epic_links/destroy_service.rb'
+ - 'ee/app/services/geo/container_repository_sync.rb'
+ - 'ee/app/services/geo/event_service.rb'
+ - 'ee/app/services/geo/file_registry_removal_service.rb'
+ - 'ee/app/services/geo/repository_destroy_service.rb'
+ - 'ee/app/services/gitlab_subscriptions/activate_service.rb'
+ - 'ee/app/services/gitlab_subscriptions/create_service.rb'
+ - 'ee/app/services/gitlab_subscriptions/fetch_purchase_eligible_namespaces_service.rb'
+ - 'ee/app/services/gitlab_subscriptions/reconciliations/calculate_seat_count_data_service.rb'
+ - 'ee/app/services/groups/sync_service.rb'
+ - 'ee/app/services/incident_management/escalation_policies/update_service.rb'
+ - 'ee/app/services/incident_management/pending_escalations/process_service.rb'
+ - 'ee/app/services/iterations/create_service.rb'
+ - 'ee/app/services/merge_commits/export_csv_service.rb'
+ - 'ee/app/services/merge_requests/update_blocks_service.rb'
+ - 'ee/app/services/projects/restore_service.rb'
+ - 'ee/app/services/projects/update_mirror_service.rb'
+ - 'ee/app/services/protected_environments/base_service.rb'
+ - 'ee/app/services/security/ingestion/tasks/ingest_vulnerabilities/mark_resolved_as_detected.rb'
+ - 'ee/app/services/security/report_fetch_service.rb'
+ - 'ee/app/services/security/report_summary_service.rb'
+ - 'ee/app/services/security/security_orchestration_policies/on_demand_scan_pipeline_configuration_service.rb'
+ - 'ee/app/services/security/security_orchestration_policies/operational_vulnerabilities_configuration_service.rb'
+ - 'ee/app/services/security/security_orchestration_policies/validate_policy_service.rb'
+ - 'ee/app/services/status_page/publish_attachments_service.rb'
+ - 'ee/app/services/status_page/publish_base_service.rb'
+ - 'ee/app/services/status_page/publish_service.rb'
+ - 'ee/app/services/status_page/trigger_publish_service.rb'
+ - 'ee/app/services/timebox_report_service.rb'
+ - 'ee/app/services/vulnerabilities/create_service.rb'
+ - 'ee/app/services/vulnerability_feedback/create_service.rb'
+ - 'ee/app/services/vulnerability_feedback/destroy_service.rb'
+ - 'ee/app/workers/auth/saml_group_sync_worker.rb'
+ - 'ee/app/workers/geo/repository_cleanup_worker.rb'
+ - 'ee/app/workers/geo/scheduler/scheduler_worker.rb'
+ - 'ee/app/workers/group_saml_group_sync_worker.rb'
+ - 'ee/app/workers/status_page/publish_worker.rb'
+ - 'ee/lib/api/analytics/project_deployment_frequency.rb'
+ - 'ee/lib/api/epic_links.rb'
+ - 'ee/lib/api/geo_nodes.rb'
+ - 'ee/lib/api/vulnerability_exports.rb'
+ - 'ee/lib/api/vulnerability_findings.rb'
+ - 'ee/lib/ee/api/geo.rb'
+ - 'ee/lib/ee/api/helpers.rb'
+ - 'ee/lib/ee/banzai/filter/references/reference_cache.rb'
+ - 'ee/lib/ee/container_registry/client.rb'
+ - 'ee/lib/ee/gitlab/alert_management/payload/generic.rb'
+ - 'ee/lib/ee/gitlab/analytics/cycle_analytics/data_collector.rb'
+ - 'ee/lib/ee/gitlab/analytics/cycle_analytics/stage_events.rb'
+ - 'ee/lib/ee/gitlab/auth/o_auth/auth_hash.rb'
+ - 'ee/lib/ee/gitlab/background_migration/backfill_project_statistics_container_repository_size.rb'
+ - 'ee/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules.rb'
+ - 'ee/lib/ee/gitlab/background_migration/populate_resolved_on_default_branch_column.rb'
+ - 'ee/lib/ee/gitlab/checks/base_checker.rb'
+ - 'ee/lib/ee/gitlab/checks/diff_check.rb'
+ - 'ee/lib/ee/gitlab/ci/matching/runner_matcher.rb'
+ - 'ee/lib/ee/gitlab/ci/pipeline/chain/validate/external.rb'
+ - 'ee/lib/ee/gitlab/ci/pipeline/quota/activity.rb'
+ - 'ee/lib/ee/gitlab/ci/pipeline/quota/size.rb'
+ - 'ee/lib/ee/gitlab/etag_caching/router/rails.rb'
+ - 'ee/lib/ee/gitlab/git_access.rb'
+ - 'ee/lib/ee/gitlab/gitaly_client/with_feature_flag_actors.rb'
+ - 'ee/lib/ee/gitlab/import_export/after_export_strategies/custom_template_export_import_strategy.rb'
+ - 'ee/lib/ee/gitlab/issuable_metadata.rb'
+ - 'ee/lib/ee/gitlab/scim/deprovision_service.rb'
+ - 'ee/lib/ee/gitlab/scim/provisioning_service.rb'
+ - 'ee/lib/ee/gitlab/security/scan_configuration.rb'
+ - 'ee/lib/ee/gitlab/web_hooks/rate_limiter.rb'
+ - 'ee/lib/ee/sidebars/groups/menus/issues_menu.rb'
+ - 'ee/lib/ee/sidebars/groups/menus/settings_menu.rb'
+ - 'ee/lib/elastic/multi_version_util.rb'
+ - 'ee/lib/gitlab/auth/group_saml/auth_hash.rb'
+ - 'ee/lib/gitlab/auth/group_saml/membership_updater.rb'
+ - 'ee/lib/gitlab/auth/group_saml/user.rb'
+ - 'ee/lib/gitlab/auth/saml/membership_updater.rb'
+ - 'ee/lib/gitlab/auth/smartcard/certificate.rb'
+ - 'ee/lib/gitlab/ci/minutes/build_consumption.rb'
+ - 'ee/lib/gitlab/ci/minutes/cached_quota.rb'
+ - 'ee/lib/gitlab/ci/minutes/gitlab_contribution_cost_factor.rb'
+ - 'ee/lib/gitlab/ci/minutes/runners_availability.rb'
+ - 'ee/lib/gitlab/ci/parsers/security/container_scanning.rb'
+ - 'ee/lib/gitlab/ci/project_config/compliance.rb'
+ - 'ee/lib/gitlab/ci/reports/license_scanning/reports_comparer.rb'
+ - 'ee/lib/gitlab/ci/reports/metrics/reports_comparer.rb'
+ - 'ee/lib/gitlab/code_owners/entry.rb'
+ - 'ee/lib/gitlab/code_owners/loader.rb'
+ - 'ee/lib/gitlab/custom_file_templates.rb'
+ - 'ee/lib/gitlab/elastic/client.rb'
+ - 'ee/lib/gitlab/elastic/document_reference.rb'
+ - 'ee/lib/gitlab/elastic/indexer.rb'
+ - 'ee/lib/gitlab/elastic/project_search_results.rb'
+ - 'ee/lib/gitlab/elastic/search_results.rb'
+ - 'ee/lib/gitlab/expiring_subscription_message.rb'
+ - 'ee/lib/gitlab/geo.rb'
+ - 'ee/lib/gitlab/geo/health_check.rb'
+ - 'ee/lib/gitlab/geo/jwt_request_decoder.rb'
+ - 'ee/lib/gitlab/geo/oauth/logout_state.rb'
+ - 'ee/lib/gitlab/geo/oauth/logout_token.rb'
+ - 'ee/lib/gitlab/geo/oauth/session.rb'
+ - 'ee/lib/gitlab/geo/replication/blob_retriever.rb'
+ - 'ee/lib/gitlab/graphql/aggregations/epics/epic_node.rb'
+ - 'ee/lib/gitlab/ingestion/bulk_insertable_task.rb'
+ - 'ee/lib/gitlab/ingestion/bulk_updatable_task.rb'
+ - 'ee/lib/gitlab/insights/finders/issuable_finder.rb'
+ - 'ee/lib/gitlab/insights/finders/projects_finder.rb'
+ - 'ee/lib/gitlab/manual_quarterly_co_term_banner.rb'
+ - 'ee/lib/gitlab/return_to_location.rb'
+ - 'ee/lib/gitlab_subscriptions/upcoming_reconciliation_entity.rb'
+ - 'ee/lib/incident_management/oncall_shift_generator.rb'
+ - 'ee/lib/sidebars/groups/menus/analytics_menu.rb'
+ - 'ee/lib/sidebars/groups/menus/epics_menu.rb'
+ - 'ee/lib/world.rb'
+ - 'lib/api/api_guard.rb'
+ - 'lib/api/ci/helpers/runner.rb'
+ - 'lib/api/ci/pipelines.rb'
+ - 'lib/api/commit_statuses.rb'
+ - 'lib/api/composer_packages.rb'
+ - 'lib/api/container_repositories.rb'
+ - 'lib/api/entities/basic_project_details.rb'
+ - 'lib/api/helpers/authentication.rb'
+ - 'lib/api/helpers/packages/basic_auth_helpers.rb'
+ - 'lib/api/helpers/packages/conan/api_helpers.rb'
+ - 'lib/api/helpers/packages/npm.rb'
+ - 'lib/api/helpers/packages_helpers.rb'
+ - 'lib/api/terraform/modules/v1/packages.rb'
+ - 'lib/api/unleash.rb'
+ - 'lib/atlassian/jira_connect/jwt/asymmetric.rb'
+ - 'lib/atlassian/jira_connect/jwt/symmetric.rb'
+ - 'lib/banzai/filter/base_sanitization_filter.rb'
+ - 'lib/banzai/filter/custom_emoji_filter.rb'
+ - 'lib/banzai/filter/inline_metrics_redactor_filter.rb'
+ - 'lib/banzai/filter/issuable_reference_expansion_filter.rb'
+ - 'lib/banzai/filter/references/reference_cache.rb'
+ - 'lib/banzai/filter/repository_link_filter.rb'
+ - 'lib/banzai/reference_parser/merge_request_parser.rb'
+ - 'lib/bulk_imports/clients/http.rb'
+ - 'lib/bulk_imports/pipeline.rb'
+ - 'lib/bulk_imports/users_mapper.rb'
+ - 'lib/container_registry/client.rb'
+ - 'lib/container_registry/gitlab_api_client.rb'
+ - 'lib/container_registry/tag.rb'
+ - 'lib/gitlab/alert_management/alert_status_counts.rb'
+ - 'lib/gitlab/alert_management/payload/base.rb'
+ - 'lib/gitlab/alert_management/payload/managed_prometheus.rb'
+ - 'lib/gitlab/analytics/cycle_analytics/aggregated/data_collector.rb'
+ - 'lib/gitlab/analytics/cycle_analytics/aggregated/records_fetcher.rb'
+ - 'lib/gitlab/analytics/cycle_analytics/average.rb'
+ - 'lib/gitlab/analytics/cycle_analytics/data_collector.rb'
+ - 'lib/gitlab/analytics/cycle_analytics/records_fetcher.rb'
+ - 'lib/gitlab/analytics/cycle_analytics/request_params.rb'
+ - 'lib/gitlab/application_context.rb'
+ - 'lib/gitlab/auth/atlassian/identity_linker.rb'
+ - 'lib/gitlab/auth/auth_finders.rb'
+ - 'lib/gitlab/auth/ip_rate_limiter.rb'
+ - 'lib/gitlab/auth/key_status_checker.rb'
+ - 'lib/gitlab/auth/otp/strategies/forti_token_cloud.rb'
+ - 'lib/gitlab/auth/request_authenticator.rb'
+ - 'lib/gitlab/background_migration/legacy_upload_mover.rb'
+ - 'lib/gitlab/bare_repository_import/repository.rb'
+ - 'lib/gitlab/blob_helper.rb'
+ - 'lib/gitlab/cache/ci/project_pipeline_status.rb'
+ - 'lib/gitlab/chat/command.rb'
+ - 'lib/gitlab/checks/changes_access.rb'
+ - 'lib/gitlab/checks/diff_check.rb'
+ - 'lib/gitlab/ci/artifacts/metrics.rb'
+ - 'lib/gitlab/ci/build/auto_retry.rb'
+ - 'lib/gitlab/ci/build/cache.rb'
+ - 'lib/gitlab/ci/build/context/base.rb'
+ - 'lib/gitlab/ci/build/context/global.rb'
+ - 'lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb'
+ - 'lib/gitlab/ci/build/rules/rule/clause/changes.rb'
+ - 'lib/gitlab/ci/config/entry/product/matrix.rb'
+ - 'lib/gitlab/ci/config/entry/root.rb'
+ - 'lib/gitlab/ci/config/extendable/entry.rb'
+ - 'lib/gitlab/ci/config/external/context.rb'
+ - 'lib/gitlab/ci/config/external/file/artifact.rb'
+ - 'lib/gitlab/ci/config/external/file/base.rb'
+ - 'lib/gitlab/ci/config/external/file/local.rb'
+ - 'lib/gitlab/ci/config/external/file/project.rb'
+ - 'lib/gitlab/ci/config/external/file/remote.rb'
+ - 'lib/gitlab/ci/config/external/file/template.rb'
+ - 'lib/gitlab/ci/config/normalizer.rb'
+ - 'lib/gitlab/ci/config/normalizer/factory.rb'
+ - 'lib/gitlab/ci/pipeline/chain/command.rb'
+ - 'lib/gitlab/ci/pipeline/chain/config/content.rb'
+ - 'lib/gitlab/ci/pipeline/chain/create.rb'
+ - 'lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules.rb'
+ - 'lib/gitlab/ci/pipeline/chain/limit/active_jobs.rb'
+ - 'lib/gitlab/ci/pipeline/chain/limit/rate_limit.rb'
+ - 'lib/gitlab/ci/pipeline/chain/seed.rb'
+ - 'lib/gitlab/ci/pipeline/chain/skip.rb'
+ - 'lib/gitlab/ci/pipeline/expression/lexer.rb'
+ - 'lib/gitlab/ci/pipeline/logger.rb'
+ - 'lib/gitlab/ci/pipeline/metrics.rb'
+ - 'lib/gitlab/ci/pipeline/quota/deployments.rb'
+ - 'lib/gitlab/ci/pipeline/seed/processable/resource_group.rb'
+ - 'lib/gitlab/ci/project_config/auto_devops.rb'
+ - 'lib/gitlab/ci/project_config/external_project.rb'
+ - 'lib/gitlab/ci/project_config/parameter.rb'
+ - 'lib/gitlab/ci/project_config/remote.rb'
+ - 'lib/gitlab/ci/project_config/repository.rb'
+ - 'lib/gitlab/ci/project_config/source.rb'
+ - 'lib/gitlab/ci/queue/metrics.rb'
+ - 'lib/gitlab/ci/reports/accessibility_reports_comparer.rb'
+ - 'lib/gitlab/ci/reports/codequality_reports_comparer.rb'
+ - 'lib/gitlab/ci/reports/security/locations/base.rb'
+ - 'lib/gitlab/ci/reports/security/vulnerability_reports_comparer.rb'
+ - 'lib/gitlab/ci/reports/test_reports_comparer.rb'
+ - 'lib/gitlab/ci/reports/test_suite_comparer.rb'
+ - 'lib/gitlab/ci/reports/test_suite_summary.rb'
+ - 'lib/gitlab/ci/runner/metrics.rb'
+ - 'lib/gitlab/ci/status/composite.rb'
+ - 'lib/gitlab/ci/tags/bulk_insert.rb'
+ - 'lib/gitlab/ci/trace.rb'
+ - 'lib/gitlab/ci/trace/archive.rb'
+ - 'lib/gitlab/ci/trace/checksum.rb'
+ - 'lib/gitlab/ci/trace/metrics.rb'
+ - 'lib/gitlab/ci/trace/remote_checksum.rb'
+ - 'lib/gitlab/ci/variables/builder.rb'
+ - 'lib/gitlab/ci/variables/builder/group.rb'
+ - 'lib/gitlab/ci/variables/builder/release.rb'
+ - 'lib/gitlab/ci/variables/collection/item.rb'
+ - 'lib/gitlab/ci/variables/collection/sort.rb'
+ - 'lib/gitlab/cleanup/orphan_job_artifact_files.rb'
+ - 'lib/gitlab/cleanup/orphan_job_artifact_files_batch.rb'
+ - 'lib/gitlab/code_navigation_path.rb'
+ - 'lib/gitlab/config/entry/composable_array.rb'
+ - 'lib/gitlab/config/loader/yaml.rb'
+ - 'lib/gitlab/conflict/file.rb'
+ - 'lib/gitlab/database/as_with_materialized.rb'
+ - 'lib/gitlab/database/background_migration/health_status/indicators/write_ahead_log.rb'
+ - 'lib/gitlab/database/background_migration/prometheus_metrics.rb'
+ - 'lib/gitlab/database/bulk_update.rb'
+ - 'lib/gitlab/database/load_balancing/srv_resolver.rb'
+ - 'lib/gitlab/database/metrics.rb'
+ - 'lib/gitlab/database/postgres_index.rb'
+ - 'lib/gitlab/diff/char_diff.rb'
+ - 'lib/gitlab/diff/file.rb'
+ - 'lib/gitlab/diff/file_collection/base.rb'
+ - 'lib/gitlab/diff/file_collection/merge_request_diff_base.rb'
+ - 'lib/gitlab/diff/highlight_cache.rb'
+ - 'lib/gitlab/diff/lines_unfolder.rb'
+ - 'lib/gitlab/diff/rendered/notebook/diff_file.rb'
+ - 'lib/gitlab/diff/stats_cache.rb'
+ - 'lib/gitlab/diff/suggestion.rb'
+ - 'lib/gitlab/discussions_diff/file_collection.rb'
+ - 'lib/gitlab/email/handler/service_desk_handler.rb'
+ - 'lib/gitlab/email/hook/delivery_metrics_observer.rb'
+ - 'lib/gitlab/email/receiver.rb'
+ - 'lib/gitlab/external_authorization/response.rb'
+ - 'lib/gitlab/gfm/reference_rewriter.rb'
+ - 'lib/gitlab/gfm/uploads_rewriter.rb'
+ - 'lib/gitlab/git/commit.rb'
+ - 'lib/gitlab/git/diff_stats_collection.rb'
+ - 'lib/gitlab/git/merge_base.rb'
+ - 'lib/gitlab/git/push.rb'
+ - 'lib/gitlab/git/repository.rb'
+ - 'lib/gitlab/git/wiki_page_version.rb'
+ - 'lib/gitlab/git_access.rb'
+ - 'lib/gitlab/git_access_project.rb'
+ - 'lib/gitlab/gitaly_client/with_feature_flag_actors.rb'
+ - 'lib/gitlab/github_import/client.rb'
+ - 'lib/gitlab/github_import/importer/repository_importer.rb'
+ - 'lib/gitlab/github_import/representation/diff_note.rb'
+ - 'lib/gitlab/github_import/representation/diff_notes/suggestion_formatter.rb'
+ - 'lib/gitlab/gl_repository/identifier.rb'
+ - 'lib/gitlab/gpg/commit.rb'
+ - 'lib/gitlab/graphql/lazy.rb'
+ - 'lib/gitlab/graphql/pagination/keyset/connection.rb'
+ - 'lib/gitlab/import_export/base/relation_factory.rb'
+ - 'lib/gitlab/import_export/base/relation_object_saver.rb'
+ - 'lib/gitlab/import_export/decompressed_archive_size_validator.rb'
+ - 'lib/gitlab/import_export/fast_hash_serializer.rb'
+ - 'lib/gitlab/import_export/group/legacy_tree_restorer.rb'
+ - 'lib/gitlab/import_export/group/tree_restorer.rb'
+ - 'lib/gitlab/import_export/importer.rb'
+ - 'lib/gitlab/import_export/json/legacy_reader.rb'
+ - 'lib/gitlab/import_export/lfs_restorer.rb'
+ - 'lib/gitlab/import_export/project/sample/date_calculator.rb'
+ - 'lib/gitlab/import_export/project/tree_restorer.rb'
+ - 'lib/gitlab/inactive_projects_deletion_warning_tracker.rb'
+ - 'lib/gitlab/instrumentation/redis_base.rb'
+ - 'lib/gitlab/instrumentation/redis_payload.rb'
+ - 'lib/gitlab/issuable_metadata.rb'
+ - 'lib/gitlab/jwt_authenticatable.rb'
+ - 'lib/gitlab/kubernetes/deployment.rb'
+ - 'lib/gitlab/kubernetes/ingress.rb'
+ - 'lib/gitlab/kubernetes/rollout_instances.rb'
+ - 'lib/gitlab/language_data.rb'
+ - 'lib/gitlab/lets_encrypt/client.rb'
+ - 'lib/gitlab/metrics/dashboard/stages/grafana_formatter.rb'
+ - 'lib/gitlab/metrics/dashboard/url.rb'
+ - 'lib/gitlab/metrics/prometheus.rb'
+ - 'lib/gitlab/metrics/subscribers/active_record.rb'
+ - 'lib/gitlab/pages/cache_control.rb'
+ - 'lib/gitlab/prometheus_client.rb'
+ - 'lib/gitlab/rack_attack/request.rb'
+ - 'lib/gitlab/redis/multi_store.rb'
+ - 'lib/gitlab/relative_positioning/ending_at.rb'
+ - 'lib/gitlab/relative_positioning/item_context.rb'
+ - 'lib/gitlab/relative_positioning/starting_from.rb'
+ - 'lib/gitlab/request_context.rb'
+ - 'lib/gitlab/search/found_blob.rb'
+ - 'lib/gitlab/search/parsed_query.rb'
+ - 'lib/gitlab/serverless/service.rb'
+ - 'lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb'
+ - 'lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/deduplicates_when_scheduling.rb'
+ - 'lib/gitlab/sidekiq_queue.rb'
+ - 'lib/gitlab/signed_commit.rb'
+ - 'lib/gitlab/ssh/signature.rb'
+ - 'lib/gitlab/suggestions/file_suggestion.rb'
+ - 'lib/gitlab/task_helpers.rb'
+ - 'lib/gitlab/template/gitlab_ci_yml_template.rb'
+ - 'lib/gitlab/tracking/destinations/snowplow_micro.rb'
+ - 'lib/gitlab/usage_data.rb'
+ - 'lib/gitlab/web_hooks/rate_limiter.rb'
+ - 'lib/gitlab/web_ide/config/entry/terminal.rb'
+ - 'lib/gitlab/webpack/graphql_known_operations.rb'
+ - 'lib/gitlab/webpack/manifest.rb'
+ - 'lib/gitlab/wiki_pages/front_matter_parser.rb'
+ - 'lib/gitlab/x509/certificate.rb'
+ - 'lib/gitlab/x509/signature.rb'
+ - 'lib/gitlab/x509/tag.rb'
+ - 'lib/grafana/time_window.rb'
+ - 'lib/object_storage/direct_upload.rb'
+ - 'lib/safe_zip/extract_params.rb'
+ - 'lib/sidebars/groups/menus/issues_menu.rb'
+ - 'lib/sidebars/groups/menus/merge_requests_menu.rb'
+ - 'lib/sidebars/projects/menus/analytics_menu.rb'
+ - 'lib/sidebars/projects/menus/issues_menu.rb'
+ - 'lib/sidebars/projects/menus/learn_gitlab_menu.rb'
+ - 'lib/unnested_in_filters/rewriter.rb'
+ - 'tooling/graphql/docs/helper.rb'
diff --git a/.rubocop_todo/graphql/argument_uniqueness.yml b/.rubocop_todo/graphql/argument_uniqueness.yml
index 036d5d8ecdb..8a1fb31af62 100644
--- a/.rubocop_todo/graphql/argument_uniqueness.yml
+++ b/.rubocop_todo/graphql/argument_uniqueness.yml
@@ -1,4 +1,4 @@
---
GraphQL/ArgumentUniqueness:
Exclude:
- - app/graphql/resolvers/merge_requests_resolver.rb
+ - 'app/graphql/resolvers/merge_requests_resolver.rb'
diff --git a/.rubocop_todo/graphql/descriptions.yml b/.rubocop_todo/graphql/descriptions.yml
new file mode 100644
index 00000000000..9d76d7d7638
--- /dev/null
+++ b/.rubocop_todo/graphql/descriptions.yml
@@ -0,0 +1,98 @@
+---
+# Cop supports --autocorrect.
+Graphql/Descriptions:
+ Safe: false
+ Details: grace period
+ Exclude:
+ - 'app/graphql/mutations/boards/lists/base_update.rb'
+ - 'app/graphql/mutations/ci/project_ci_cd_settings_update.rb'
+ - 'app/graphql/mutations/clusters/agents/create.rb'
+ - 'app/graphql/mutations/merge_requests/accept.rb'
+ - 'app/graphql/mutations/notes/create/note.rb'
+ - 'app/graphql/mutations/releases/create.rb'
+ - 'app/graphql/resolvers/admin/analytics/usage_trends/measurements_resolver.rb'
+ - 'app/graphql/resolvers/alert_management/alert_resolver.rb'
+ - 'app/graphql/resolvers/concerns/issues/sort_arguments.rb'
+ - 'app/graphql/resolvers/concerns/resolves_pipelines.rb'
+ - 'app/graphql/resolvers/container_repositories_resolver.rb'
+ - 'app/graphql/resolvers/design_management/design_at_version_resolver.rb'
+ - 'app/graphql/resolvers/group_packages_resolver.rb'
+ - 'app/graphql/resolvers/issues/base_parent_resolver.rb'
+ - 'app/graphql/resolvers/issues/base_resolver.rb'
+ - 'app/graphql/resolvers/issues_resolver.rb'
+ - 'app/graphql/resolvers/merge_requests_resolver.rb'
+ - 'app/graphql/resolvers/milestones_resolver.rb'
+ - 'app/graphql/resolvers/namespace_projects_resolver.rb'
+ - 'app/graphql/resolvers/packages_base_resolver.rb'
+ - 'app/graphql/resolvers/releases_resolver.rb'
+ - 'app/graphql/resolvers/users_resolver.rb'
+ - 'app/graphql/resolvers/work_items_resolver.rb'
+ - 'app/graphql/types/alert_management/alert_type.rb'
+ - 'app/graphql/types/award_emojis/award_emoji_type.rb'
+ - 'app/graphql/types/board_list_type.rb'
+ - 'app/graphql/types/branch_protections/base_access_level_type.rb'
+ - 'app/graphql/types/branch_rules/branch_protection_type.rb'
+ - 'app/graphql/types/ci/job_token_scope_type.rb'
+ - 'app/graphql/types/ci/pipeline_type.rb'
+ - 'app/graphql/types/ci/runner_status_enum.rb'
+ - 'app/graphql/types/ci/runner_type.rb'
+ - 'app/graphql/types/ci/runner_web_url_edge.rb'
+ - 'app/graphql/types/ci/status_action_type.rb'
+ - 'app/graphql/types/clusters/agent_token_type.rb'
+ - 'app/graphql/types/clusters/agent_type.rb'
+ - 'app/graphql/types/container_expiration_policy_type.rb'
+ - 'app/graphql/types/container_repository_tag_type.rb'
+ - 'app/graphql/types/container_repository_type.rb'
+ - 'app/graphql/types/deployment_tag_type.rb'
+ - 'app/graphql/types/design_management/design_at_version_type.rb'
+ - 'app/graphql/types/design_management/design_fields.rb'
+ - 'app/graphql/types/diff_stats_type.rb'
+ - 'app/graphql/types/event_type.rb'
+ - 'app/graphql/types/group_type.rb'
+ - 'app/graphql/types/issue_type.rb'
+ - 'app/graphql/types/issues/negated_issue_filter_input_type.rb'
+ - 'app/graphql/types/label_type.rb'
+ - 'app/graphql/types/merge_request_type.rb'
+ - 'app/graphql/types/merge_requests/interacts_with_merge_request.rb'
+ - 'app/graphql/types/milestone_sort_enum.rb'
+ - 'app/graphql/types/milestone_type.rb'
+ - 'app/graphql/types/namespace/package_settings_type.rb'
+ - 'app/graphql/types/notes/discussion_type.rb'
+ - 'app/graphql/types/notes/note_type.rb'
+ - 'app/graphql/types/notes/noteable_interface.rb'
+ - 'app/graphql/types/packages/cleanup/policy_type.rb'
+ - 'app/graphql/types/packages/package_details_type.rb'
+ - 'app/graphql/types/project_type.rb'
+ - 'app/graphql/types/projects/branch_rule_type.rb'
+ - 'app/graphql/types/release_links_type.rb'
+ - 'app/graphql/types/repository/blob_type.rb'
+ - 'app/graphql/types/snippet_type.rb'
+ - 'app/graphql/types/terraform/state_version_type.rb'
+ - 'app/graphql/types/todo_type.rb'
+ - 'app/graphql/types/todoable_interface.rb'
+ - 'app/graphql/types/user_interface.rb'
+ - 'app/graphql/types/user_merge_request_interaction_type.rb'
+ - 'ee/app/graphql/ee/types/branch_protections/base_access_level_type.rb'
+ - 'ee/app/graphql/ee/types/branch_rules/branch_protection_type.rb'
+ - 'ee/app/graphql/ee/types/issue_type.rb'
+ - 'ee/app/graphql/ee/types/merge_request_type.rb'
+ - 'ee/app/graphql/ee/types/projects/branch_rule_type.rb'
+ - 'ee/app/graphql/ee/types/user_merge_request_interaction_type.rb'
+ - 'ee/app/graphql/resolvers/epics_resolver.rb'
+ - 'ee/app/graphql/types/access_levels/user_type.rb'
+ - 'ee/app/graphql/types/boards/epic_list_type.rb'
+ - 'ee/app/graphql/types/branch_rules/approval_project_rule_type.rb'
+ - 'ee/app/graphql/types/burnup_chart_daily_totals_type.rb'
+ - 'ee/app/graphql/types/ci/namespace_ci_cd_setting_type.rb'
+ - 'ee/app/graphql/types/compliance_management/compliance_framework_input_type.rb'
+ - 'ee/app/graphql/types/compliance_management/merge_requests/compliance_violation_input_type.rb'
+ - 'ee/app/graphql/types/epic_descendant_weight_sum_type.rb'
+ - 'ee/app/graphql/types/epic_type.rb'
+ - 'ee/app/graphql/types/geo/geo_node_type.rb'
+ - 'ee/app/graphql/types/iterations/cadence_type.rb'
+ - 'ee/app/graphql/types/network_policy_type.rb'
+ - 'ee/app/graphql/types/path_lock_type.rb'
+ - 'ee/app/graphql/types/security_orchestration/group_security_policy_source_type.rb'
+ - 'ee/app/graphql/types/security_orchestration/orchestration_policy_type.rb'
+ - 'ee/app/graphql/types/vulnerability_type.rb'
+ - 'ee/app/graphql/types/vulnerable_projects_by_grade_type.rb'
diff --git a/.rubocop_todo/graphql/field_definitions.yml b/.rubocop_todo/graphql/field_definitions.yml
index 0e2399ba243..fa230afc8d0 100644
--- a/.rubocop_todo/graphql/field_definitions.yml
+++ b/.rubocop_todo/graphql/field_definitions.yml
@@ -1,4 +1,5 @@
---
+# Cop supports --autocorrect.
GraphQL/FieldDefinitions:
Exclude:
- - ee/app/graphql/types/vulnerability_type.rb
+ - 'ee/app/graphql/types/vulnerability_type.rb'
diff --git a/.rubocop_todo/graphql/field_hash_key.yml b/.rubocop_todo/graphql/field_hash_key.yml
index 806920e24f6..13460170c07 100644
--- a/.rubocop_todo/graphql/field_hash_key.yml
+++ b/.rubocop_todo/graphql/field_hash_key.yml
@@ -1,7 +1,5 @@
---
+# Cop supports --autocorrect.
GraphQL/FieldHashKey:
Exclude:
- - app/graphql/types/ci/config/job_type.rb
- - app/graphql/types/ci/status_action_type.rb
- - app/graphql/types/error_tracking/sentry_error_stack_trace_entry_type.rb
- - app/graphql/types/packages/helm/dependency_type.rb
+ - 'app/graphql/types/error_tracking/sentry_error_stack_trace_entry_type.rb'
diff --git a/.rubocop_todo/graphql/field_method.yml b/.rubocop_todo/graphql/field_method.yml
index 6a05b3c23b8..f9e9f46e7d6 100644
--- a/.rubocop_todo/graphql/field_method.yml
+++ b/.rubocop_todo/graphql/field_method.yml
@@ -1,4 +1,5 @@
---
+# Cop supports --autocorrect.
GraphQL/FieldMethod:
Exclude:
- - app/graphql/types/packages/package_details_type.rb
+ - 'app/graphql/types/packages/package_details_type.rb'
diff --git a/.rubocop_todo/graphql/ordered_fields.yml b/.rubocop_todo/graphql/ordered_fields.yml
deleted file mode 100644
index b7135d7aabb..00000000000
--- a/.rubocop_todo/graphql/ordered_fields.yml
+++ /dev/null
@@ -1,12 +0,0 @@
----
-GraphQL/OrderedFields:
- Exclude:
- - app/graphql/types/ci/runner_type.rb
- - app/graphql/types/container_repository_type.rb
- - app/graphql/types/dependency_proxy/manifest_type.rb
- - app/graphql/types/merge_request_type.rb
- - app/graphql/types/project_statistics_type.rb
- - app/graphql/types/release_type.rb
- - app/graphql/types/root_storage_statistics_type.rb
- - ee/app/graphql/types/scan_type.rb
- - ee/app/graphql/types/timebox_report_type.rb
diff --git a/.rubocop_todo/graphql/resolver_method_length.yml b/.rubocop_todo/graphql/resolver_method_length.yml
index e2183bb0894..f27246096a7 100644
--- a/.rubocop_todo/graphql/resolver_method_length.yml
+++ b/.rubocop_todo/graphql/resolver_method_length.yml
@@ -1,6 +1,6 @@
---
GraphQL/ResolverMethodLength:
Exclude:
- - app/graphql/types/ci/detailed_status_type.rb
- - app/graphql/types/ci/runner_type.rb
- - app/graphql/types/ci/stage_type.rb \ No newline at end of file
+ - 'app/graphql/types/ci/detailed_status_type.rb'
+ - 'app/graphql/types/ci/runner_type.rb'
+ - 'app/graphql/types/ci/stage_type.rb'
diff --git a/.rubocop_todo/graphql/unused_argument.yml b/.rubocop_todo/graphql/unused_argument.yml
index c55d8551591..d03c6799471 100644
--- a/.rubocop_todo/graphql/unused_argument.yml
+++ b/.rubocop_todo/graphql/unused_argument.yml
@@ -1,5 +1,6 @@
---
+# Cop supports --autocorrect.
GraphQL/UnusedArgument:
Exclude:
- - app/graphql/mutations/jira_import/start.rb
- - app/graphql/resolvers/packages_base_resolver.rb
+ - 'app/graphql/mutations/jira_import/start.rb'
+ - 'app/graphql/resolvers/packages_base_resolver.rb'
diff --git a/.rubocop_todo/layout/argument_alignment.yml b/.rubocop_todo/layout/argument_alignment.yml
index 4b5fb5ad1a6..98633709bee 100644
--- a/.rubocop_todo/layout/argument_alignment.yml
+++ b/.rubocop_todo/layout/argument_alignment.yml
@@ -1,49 +1,66 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Layout/ArgumentAlignment:
- # Offense count: 2502
+ # Offense count: 4159
# Temporarily disabled due to too many offenses
Enabled: false
Exclude:
+ - 'app/controllers/admin/cohorts_controller.rb'
+ - 'app/controllers/admin/dev_ops_report_controller.rb'
+ - 'app/controllers/admin/usage_trends_controller.rb'
- 'app/controllers/admin/users_controller.rb'
- 'app/controllers/application_controller.rb'
+ - 'app/controllers/concerns/import/github_oauth.rb'
- 'app/controllers/concerns/notes_actions.rb'
- 'app/controllers/concerns/page_limiter.rb'
+ - 'app/controllers/concerns/sends_blob.rb'
- 'app/controllers/concerns/snippets_actions.rb'
- 'app/controllers/concerns/wiki_actions.rb'
- 'app/controllers/groups_controller.rb'
+ - 'app/controllers/ide_controller.rb'
- 'app/controllers/import/gitea_controller.rb'
- 'app/controllers/import/github_controller.rb'
- 'app/controllers/import/gitlab_groups_controller.rb'
+ - 'app/controllers/oauth/authorizations_controller.rb'
- 'app/controllers/passwords_controller.rb'
- 'app/controllers/profiles_controller.rb'
- 'app/controllers/projects/branches_controller.rb'
+ - 'app/controllers/projects/cycle_analytics_controller.rb'
+ - 'app/controllers/projects/graphs_controller.rb'
- 'app/controllers/projects/jobs_controller.rb'
- 'app/controllers/projects/merge_requests/creations_controller.rb'
- 'app/controllers/projects/pages_domains_controller.rb'
- 'app/controllers/projects/pipeline_schedules_controller.rb'
+ - 'app/controllers/projects/pipelines_controller.rb'
- 'app/controllers/projects/uploads_controller.rb'
- 'app/controllers/projects/web_ide_terminals_controller.rb'
- - 'app/controllers/projects_controller.rb'
- 'app/controllers/registrations_controller.rb'
- 'app/controllers/repositories/git_http_controller.rb'
+ - 'app/controllers/search_controller.rb'
- 'app/controllers/sessions_controller.rb'
+ - 'app/finders/context_commits_finder.rb'
+ - 'app/finders/deployments_finder.rb'
- 'app/finders/issues_finder.rb'
- 'app/finders/projects/prometheus/alerts_finder.rb'
+ - 'app/graphql/mutations/award_emojis/toggle.rb'
- 'app/graphql/mutations/boards/destroy.rb'
- 'app/graphql/mutations/boards/lists/base_update.rb'
- 'app/graphql/mutations/boards/lists/destroy.rb'
- 'app/graphql/mutations/boards/lists/update.rb'
- - 'app/graphql/mutations/ci/ci_cd_settings_update.rb'
- 'app/graphql/mutations/ci/job_token_scope/add_project.rb'
- 'app/graphql/mutations/ci/job_token_scope/remove_project.rb'
- 'app/graphql/mutations/ci/pipeline/base.rb'
+ - 'app/graphql/mutations/ci/project_ci_cd_settings_update.rb'
+ - 'app/graphql/mutations/ci/runner/update.rb'
- 'app/graphql/mutations/ci/runners_registration_token/reset.rb'
+ - 'app/graphql/mutations/concerns/mutations/work_items/widgetable.rb'
- 'app/graphql/mutations/customer_relations/contacts/update.rb'
- 'app/graphql/mutations/customer_relations/organizations/update.rb'
- 'app/graphql/mutations/dependency_proxy/group_settings/update.rb'
- 'app/graphql/mutations/dependency_proxy/image_ttl_group_policy/update.rb'
+ - 'app/graphql/mutations/design_management/move.rb'
- 'app/graphql/mutations/discussions/toggle_resolve.rb'
+ - 'app/graphql/mutations/issues/set_severity.rb'
- 'app/graphql/mutations/metrics/dashboard/annotations/create.rb'
- 'app/graphql/mutations/metrics/dashboard/annotations/delete.rb'
- 'app/graphql/mutations/namespace/package_settings/update.rb'
@@ -52,18 +69,24 @@ Layout/ArgumentAlignment:
- 'app/graphql/mutations/notes/create/image_diff_note.rb'
- 'app/graphql/mutations/notes/create/note.rb'
- 'app/graphql/mutations/notes/reposition_image_diff_note.rb'
+ - 'app/graphql/mutations/packages/cleanup/policy/update.rb'
+ - 'app/graphql/mutations/packages/destroy_files.rb'
+ - 'app/graphql/mutations/pages/base.rb'
+ - 'app/graphql/mutations/pages/mark_onboarding_complete.rb'
+ - 'app/graphql/mutations/security/ci_configuration/base_security_analyzer.rb'
- 'app/graphql/mutations/security/ci_configuration/configure_sast.rb'
- 'app/graphql/mutations/terraform/state/base.rb'
- 'app/graphql/mutations/user_callouts/create.rb'
- 'app/graphql/resolvers/admin/analytics/usage_trends/measurements_resolver.rb'
- 'app/graphql/resolvers/alert_management/alert_resolver.rb'
- 'app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb'
- - 'app/graphql/resolvers/base_issues_resolver.rb'
+ - 'app/graphql/resolvers/ci/all_jobs_resolver.rb'
- 'app/graphql/resolvers/ci/jobs_resolver.rb'
- 'app/graphql/resolvers/ci/project_pipeline_counts_resolver.rb'
- 'app/graphql/resolvers/ci/test_suite_resolver.rb'
- 'app/graphql/resolvers/clusters/agents_resolver.rb'
- 'app/graphql/resolvers/concerns/group_issuable_resolver.rb'
+ - 'app/graphql/resolvers/concerns/issues/sort_arguments.rb'
- 'app/graphql/resolvers/concerns/search_arguments.rb'
- 'app/graphql/resolvers/container_repositories_resolver.rb'
- 'app/graphql/resolvers/container_repository_tags_resolver.rb'
@@ -73,6 +96,9 @@ Layout/ArgumentAlignment:
- 'app/graphql/resolvers/error_tracking/sentry_errors_resolver.rb'
- 'app/graphql/resolvers/group_members_resolver.rb'
- 'app/graphql/resolvers/group_packages_resolver.rb'
+ - 'app/graphql/resolvers/issues/base_parent_resolver.rb'
+ - 'app/graphql/resolvers/issues/base_resolver.rb'
+ - 'app/graphql/resolvers/issues_resolver.rb'
- 'app/graphql/resolvers/members_resolver.rb'
- 'app/graphql/resolvers/namespace_projects_resolver.rb'
- 'app/graphql/resolvers/package_details_resolver.rb'
@@ -80,6 +106,7 @@ Layout/ArgumentAlignment:
- 'app/graphql/resolvers/paginated_tree_resolver.rb'
- 'app/graphql/resolvers/project_jobs_resolver.rb'
- 'app/graphql/resolvers/project_members_resolver.rb'
+ - 'app/graphql/resolvers/projects/fork_targets_resolver.rb'
- 'app/graphql/resolvers/projects/snippets_resolver.rb'
- 'app/graphql/resolvers/release_resolver.rb'
- 'app/graphql/resolvers/repository_branch_names_resolver.rb'
@@ -88,17 +115,40 @@ Layout/ArgumentAlignment:
- 'app/graphql/resolvers/tree_resolver.rb'
- 'app/graphql/resolvers/users_resolver.rb'
- 'app/graphql/subscriptions/issuable_updated.rb'
+ - 'app/graphql/types/alert_management/domain_filter_enum.rb'
+ - 'app/graphql/types/ci/group_variable_connection_type.rb'
+ - 'app/graphql/types/ci/project_variable_connection_type.rb'
- 'app/graphql/types/ci_configuration/sast/analyzers_entity_input_type.rb'
- 'app/graphql/types/ci_configuration/sast/input_type.rb'
+ - 'app/graphql/types/commit_signature_interface.rb'
+ - 'app/graphql/types/commit_signatures/gpg_signature_type.rb'
+ - 'app/graphql/types/commit_signatures/ssh_signature_type.rb'
+ - 'app/graphql/types/commit_signatures/x509_signature_type.rb'
+ - 'app/graphql/types/customer_relations/contact_sort_enum.rb'
+ - 'app/graphql/types/customer_relations/organization_sort_enum.rb'
+ - 'app/graphql/types/deployment_type.rb'
+ - 'app/graphql/types/environment_type.rb'
- 'app/graphql/types/error_tracking/sentry_error_frequency_type.rb'
- 'app/graphql/types/error_tracking/sentry_error_tags_type.rb'
- 'app/graphql/types/issues/negated_issue_filter_input_type.rb'
+ - 'app/graphql/types/limited_countable_connection_type.rb'
- 'app/graphql/types/merge_request_state_event_enum.rb'
+ - 'app/graphql/types/namespace/package_settings_type.rb'
+ - 'app/graphql/types/packages/package_base_type.rb'
+ - 'app/graphql/types/permission_types/group_enum.rb'
- 'app/graphql/types/project_type.rb'
- 'app/graphql/types/release_asset_link_shared_input_arguments.rb'
- 'app/graphql/types/saved_reply_type.rb'
+ - 'app/graphql/types/upload_type.rb'
- 'app/graphql/types/user_preferences_type.rb'
- - 'app/helpers/application_helper.rb'
+ - 'app/graphql/types/work_items/widget_interface.rb'
+ - 'app/graphql/types/work_items/widgets/assignees_input_type.rb'
+ - 'app/graphql/types/work_items/widgets/assignees_type.rb'
+ - 'app/graphql/types/work_items/widgets/hierarchy_type.rb'
+ - 'app/graphql/types/work_items/widgets/labels_type.rb'
+ - 'app/graphql/types/work_items/widgets/start_and_due_date_type.rb'
+ - 'app/graphql/types/x509_certificate_type.rb'
+ - 'app/graphql/types/x509_issuer_type.rb'
- 'app/helpers/blob_helper.rb'
- 'app/helpers/button_helper.rb'
- 'app/helpers/ci/status_helper.rb'
@@ -107,6 +157,8 @@ Layout/ArgumentAlignment:
- 'app/helpers/members_helper.rb'
- 'app/helpers/notes_helper.rb'
- 'app/helpers/repository_languages_helper.rb'
+ - 'app/helpers/snippets_helper.rb'
+ - 'app/helpers/todos_helper.rb'
- 'app/helpers/wiki_helper.rb'
- 'app/models/alert_management/http_integration.rb'
- 'app/models/alerting/project_alerting_setting.rb'
@@ -121,6 +173,7 @@ Layout/ArgumentAlignment:
- 'app/models/ci/processable.rb'
- 'app/models/ci/resource_group.rb'
- 'app/models/ci/runner.rb'
+ - 'app/models/ci/stage.rb'
- 'app/models/ci_platform_metric.rb'
- 'app/models/clusters/agent.rb'
- 'app/models/clusters/applications/helm.rb'
@@ -136,8 +189,9 @@ Layout/ArgumentAlignment:
- 'app/models/concerns/bulk_insert_safe.rb'
- 'app/models/concerns/ci/has_variable.rb'
- 'app/models/concerns/ci/new_has_variable.rb'
+ - 'app/models/concerns/ci/partitionable.rb'
- 'app/models/concerns/each_batch.rb'
- - 'app/models/concerns/integrations/has_issue_tracker_fields.rb'
+ - 'app/models/concerns/integrations/slack_mattermost_notifier.rb'
- 'app/models/concerns/issuable.rb'
- 'app/models/concerns/merge_request_reviewer_state.rb'
- 'app/models/concerns/nullify_if_blank.rb'
@@ -147,6 +201,7 @@ Layout/ArgumentAlignment:
- 'app/models/concerns/packages/debian/distribution_key.rb'
- 'app/models/concerns/repository_storage_movable.rb'
- 'app/models/concerns/storage/legacy_namespace.rb'
+ - 'app/models/concerns/taskable.rb'
- 'app/models/concerns/with_uploads.rb'
- 'app/models/custom_emoji.rb'
- 'app/models/deploy_token.rb'
@@ -159,10 +214,34 @@ Layout/ArgumentAlignment:
- 'app/models/gpg_key_subkey.rb'
- 'app/models/grafana_integration.rb'
- 'app/models/group.rb'
+ - 'app/models/hooks/web_hook.rb'
- 'app/models/incident_management/project_incident_management_setting.rb'
+ - 'app/models/incident_management/timeline_event.rb'
+ - 'app/models/incident_management/timeline_event_tag.rb'
+ - 'app/models/integrations/asana.rb'
+ - 'app/models/integrations/assembla.rb'
- 'app/models/integrations/bamboo.rb'
- - 'app/models/integrations/jira.rb'
+ - 'app/models/integrations/buildkite.rb'
+ - 'app/models/integrations/campfire.rb'
+ - 'app/models/integrations/confluence.rb'
+ - 'app/models/integrations/datadog.rb'
+ - 'app/models/integrations/discord.rb'
+ - 'app/models/integrations/drone_ci.rb'
+ - 'app/models/integrations/emails_on_push.rb'
+ - 'app/models/integrations/external_wiki.rb'
+ - 'app/models/integrations/flowdock.rb'
+ - 'app/models/integrations/harbor.rb'
+ - 'app/models/integrations/irker.rb'
+ - 'app/models/integrations/jenkins.rb'
+ - 'app/models/integrations/mock_ci.rb'
+ - 'app/models/integrations/packagist.rb'
+ - 'app/models/integrations/pipelines_email.rb'
+ - 'app/models/integrations/pivotaltracker.rb'
+ - 'app/models/integrations/prometheus.rb'
+ - 'app/models/integrations/pushover.rb'
+ - 'app/models/integrations/shimo.rb'
- 'app/models/integrations/teamcity.rb'
+ - 'app/models/integrations/zentao.rb'
- 'app/models/issue.rb'
- 'app/models/key.rb'
- 'app/models/lfs_object.rb'
@@ -187,6 +266,8 @@ Layout/ArgumentAlignment:
- 'app/models/project_ci_cd_setting.rb'
- 'app/models/project_feature.rb'
- 'app/models/project_metrics_setting.rb'
+ - 'app/models/projects/import_export/relation_export.rb'
+ - 'app/models/projects/import_export/relation_export_upload.rb'
- 'app/models/redirect_route.rb'
- 'app/models/route.rb'
- 'app/models/serverless/domain_cluster.rb'
@@ -197,14 +278,18 @@ Layout/ArgumentAlignment:
- 'app/models/users/saved_reply.rb'
- 'app/presenters/ci/pipeline_presenter.rb'
- 'app/presenters/gitlab/blame_presenter.rb'
+ - 'app/presenters/key_presenter.rb'
- 'app/presenters/label_presenter.rb'
- 'app/presenters/merge_request_presenter.rb'
- 'app/presenters/project_presenter.rb'
- 'app/serializers/build_details_entity.rb'
+ - 'app/serializers/detailed_status_entity.rb'
- 'app/serializers/diffs_entity.rb'
- 'app/serializers/environment_entity.rb'
- 'app/serializers/error_tracking/error_entity.rb'
- 'app/serializers/stage_entity.rb'
+ - 'app/serializers/test_suite_entity.rb'
+ - 'app/serializers/test_suite_summary_entity.rb'
- 'app/serializers/triggered_pipeline_entity.rb'
- 'app/services/ci/archive_trace_service.rb'
- 'app/services/ci/job_artifacts/create_service.rb'
@@ -218,18 +303,27 @@ Layout/ArgumentAlignment:
- 'app/services/gravatar_service.rb'
- 'app/services/issues/clone_service.rb'
- 'app/services/jira/requests/base.rb'
+ - 'app/services/members/creator_service.rb'
+ - 'app/services/merge_requests/create_pipeline_service.rb'
- 'app/services/merge_requests/merge_to_ref_service.rb'
- 'app/services/metrics/dashboard/update_dashboard_service.rb'
+ - 'app/services/notification_service.rb'
- 'app/services/packages/conan/create_package_service.rb'
- 'app/services/packages/create_temporary_package_service.rb'
- 'app/services/packages/go/create_package_service.rb'
- 'app/services/packages/maven/create_package_service.rb'
- 'app/services/prometheus/proxy_variable_substitution_service.rb'
+ - 'app/services/protected_branches/api_service.rb'
+ - 'app/services/quick_actions/interpret_service.rb'
- 'app/services/web_hook_service.rb'
- 'app/services/webauthn/authenticate_service.rb'
- 'app/uploaders/object_storage.rb'
+ - 'app/workers/bulk_imports/entity_worker.rb'
+ - 'app/workers/bulk_imports/export_request_worker.rb'
+ - 'app/workers/bulk_imports/pipeline_worker.rb'
- 'app/workers/run_pipeline_schedule_worker.rb'
- 'config/application.rb'
+ - 'config/initializers/sawyer_patch.rb'
- 'config/initializers/zz_metrics.rb'
- 'config/routes.rb'
- 'config/routes/legacy_builds.rb'
@@ -262,6 +356,47 @@ Layout/ArgumentAlignment:
- 'db/migrate/20211109101010_support_partition_query_in_loose_fk_table.rb'
- 'db/migrate/20220119170426_remove_temporary_vulnerability_occurrences_deduplication_index.rb'
- 'db/migrate/20220301003502_add_security_orchestration_policy_configuration_namespace_index.rb'
+ - 'db/migrate/20220401071609_add_campaign_to_in_product_marketing_email.rb'
+ - 'db/migrate/20220401110443_add_on_hold_until_column_for_batched_migration.rb'
+ - 'db/migrate/20220404170446_add_index_for_non_requested_non_invited_awaiting_members.rb'
+ - 'db/migrate/20220406113217_add_inactive_project_deletion_to_application_settings.rb'
+ - 'db/migrate/20220408135815_update_index_on_greated_done_at_on_container_repositories.rb'
+ - 'db/migrate/20220422220507_remove_tmp_index_supporting_leaky_regex_cleanup.rb'
+ - 'db/migrate/20220505092254_add_allow_stale_runner_pruning_index_to_namespace_ci_cd_settings.rb'
+ - 'db/migrate/20220512020500_index_batched_migrations_on_gitlab_schema_and_configuration.rb'
+ - 'db/migrate/20220513093614_add_ding_talk_into_application_settings.rb'
+ - 'db/migrate/20220516054002_temp_index_for_project_namespace_member_backfill.rb'
+ - 'db/migrate/20220601223501_add_vulnerability_related_columns.rb'
+ - 'db/migrate/20220608074738_add_max_repository_downloads_limit_to_application_settings.rb'
+ - 'db/migrate/20220613054349_add_unique_project_download_limit_settings_to_namespace_settings.rb'
+ - 'db/migrate/20220616182015_create_sbom_component_versions.rb'
+ - 'db/migrate/20220616183240_add_sbom_component_versions_foreign_key_to_sbom_occurrences.rb'
+ - 'db/migrate/20220627061008_add_fei_shu_integration.rb'
+ - 'db/migrate/20220627122230_add_foreign_keys_to_user_namespace_callouts.rb'
+ - 'db/migrate/20220627171538_add_error_tracking_settings.rb'
+ - 'db/migrate/20220704034050_add_users_allowlist_to_git_rate_limits.rb'
+ - 'db/migrate/20220708150315_add_vulnerabilities_foreign_key_to_vulnerability_merge_request_links.rb'
+ - 'db/migrate/20220718083945_add_unique_project_download_limit_allowlist_to_namespace_settings.rb'
+ - 'db/migrate/20220726025516_add_namespace_settings_unique_project_download_limit_allowlist_size_constraint.rb'
+ - 'db/migrate/20220726154015_add_component_foreign_key_to_sbom_occurrences.rb'
+ - 'db/migrate/20220726230052_remove_tmp_index_project_membership_namespace_id_column.rb'
+ - 'db/migrate/20220803004853_add_auto_ban_user_to_namespace_settings.rb'
+ - 'db/migrate/20220803235114_add_auto_ban_user_to_application_settings.rb'
+ - 'db/migrate/20220805154101_add_allow_run_pipelines_in_the_parent_project_setting.rb'
+ - 'db/migrate/20220907115806_add_security_orchestration_policy_configuration_id.rb'
+ - 'db/migrate/20220915140802_create_merge_request_predictions.rb'
+ - 'db/migrate/20220921201347_add_maven_package_requests_forwarding_to_namespace_package_settings.rb'
+ - 'db/migrate/20220928201920_create_project_wiki_repository_states.rb'
+ - 'db/migrate/20220929171925_add_pypi_package_requests_forwarding_to_namespace_package_settings.rb'
+ - 'db/migrate/20220929172356_add_npm_package_requests_forwarding_to_namespace_package_settings.rb'
+ - 'db/migrate/20221005094926_create_incident_management_timeline_event_tag_links.rb'
+ - 'db/migrate/20221005103010_add_index_project_id_on_scan_finding_approval_project_rules.rb'
+ - 'db/migrate/20221011162637_add_partial_index_project_incident_management_settings_on_project_id_and_sla_timer.rb'
+ - 'db/migrate/20221018202524_create_dependency_proxy_blob_states.rb'
+ - 'db/migrate/20221102150737_index_environments_for_name_search_within_folder.rb'
+ - 'db/migrate/20221102195642_create_dependency_proxy_manifest_states.rb'
+ - 'db/migrate/20221108185442_add_project_wiki_repository_id_to_project_wiki_repository_states.rb'
+ - 'db/migrate/20221110150942_add_project_id_lower_name_index_remove_old_index.rb'
- 'db/post_migrate/20210330130420_drop_finding_fingerprint_table.rb'
- 'db/post_migrate/20210413130011_add_partitioned_web_hook_log_fk.rb'
- 'db/post_migrate/20210415101228_backfill_ci_build_needs_for_bigint_conversion.rb'
@@ -271,7 +406,6 @@ Layout/ArgumentAlignment:
- 'db/post_migrate/20210701033622_finalize_ci_builds_needs_bigint_conversion.rb'
- 'db/post_migrate/20210706212710_finalize_ci_job_artifacts_bigint_conversion.rb'
- 'db/post_migrate/20210713042153_finalize_ci_sources_pipelines_bigint_conversion.rb'
- - 'db/post_migrate/20210804151444_prepare_indexes_for_ci_job_artifact_bigint_conversion.rb'
- 'db/post_migrate/20210804153307_prepare_indexes_for_tagging_bigint_conversion.rb'
- 'db/post_migrate/20210809143931_finalize_job_id_conversion_to_bigint_for_ci_job_artifacts.rb'
- 'db/post_migrate/20210817024335_prepare_indexes_for_events_bigint_conversion.rb'
@@ -284,102 +418,314 @@ Layout/ArgumentAlignment:
- 'db/post_migrate/20211031154919_add_indexes_to_merge_request_stage_events.rb'
- 'db/post_migrate/20211109112454_drop_old_loose_fk_deleted_records_index.rb'
- 'db/post_migrate/20220310134207_add_index_project_id_and_released_at_and_id_on_releases.rb'
+ - 'db/post_migrate/20220315171027_add_tmp_index_to_support_leaky_regex_cleanup.rb'
+ - 'db/post_migrate/20220322035654_add_migration_plan_index_to_container_repositories.rb'
+ - 'db/post_migrate/20220323152202_add_index_on_visible_deployments.rb'
+ - 'db/post_migrate/20220404183350_add_forbidden_state_index_to_users.rb'
+ - 'db/post_migrate/20220412143551_add_partial_index_on_unencrypted_integrations.rb'
+ - 'db/post_migrate/20220413011328_remove_partial_index_on_unencrypted_integrations.rb'
+ - 'db/post_migrate/20220420214703_schedule_backfill_draft_status_on_merge_requests_corrected_regex.rb'
+ - 'db/post_migrate/20220425121435_backfill_integrations_enable_ssl_verification.rb'
+ - 'db/post_migrate/20220503134610_remove_requirements_management_test_reports_requirement_id.rb'
+ - 'db/post_migrate/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints.rb'
+ - 'db/post_migrate/20220525172001_migrate_cluster_integration_worker_queues.rb'
+ - 'db/post_migrate/20220530044712_add_index_for_vulnerability_reads_location_image.rb'
+ - 'db/post_migrate/20220610223040_add_index_on_available_pypi_packages.rb'
+ - 'db/post_migrate/20220621082245_remove_tmp_index_on_routes_namespace_id_column.rb'
+ - 'db/post_migrate/20220622070547_add_temp_index_for_container_registry_size_migration.rb'
+ - 'db/post_migrate/20220628110823_add_issues_namespace_id_fk_and_index.rb'
+ - 'db/post_migrate/20220630050050_index_vulnerability_reads_on_casted_cluster_agent_id_full.rb'
+ - 'db/post_migrate/20220630085003_drop_project_successfull_pages_deploy_index_from_ci_builds.rb'
+ - 'db/post_migrate/20220704044408_remove_foreign_key_in_project_tracing_settings.rb'
+ - 'db/post_migrate/20220706065245_remove_foreign_key_in_clusters_applications_elastic_stacks.rb'
+ - 'db/post_migrate/20220706065611_remove_foreign_key_in_clusters_integration_elasticstack.rb'
+ - 'db/post_migrate/20220707192420_remove_tmp_idx_merge_requests_draft_and_status.rb'
+ - 'db/post_migrate/20220715054506_add_parent_link_unique_work_item_index.rb'
+ - 'db/post_migrate/20220721140252_prepare_partial_trigram_indexes_for_issues.rb'
+ - 'db/post_migrate/20220726225114_remove_tmp_index_group_membership_namespace_id_column.rb'
+ - 'db/post_migrate/20220801144713_add_partial_trigram_index_for_issue_title.rb'
+ - 'db/post_migrate/20220801150028_add_partial_trigram_index_for_issue_description.rb'
+ - 'db/post_migrate/20220803042351_add_tmp_index_todos_attention_request_action.rb'
+ - 'db/post_migrate/20220810093742_add_async_tmp_index_job_artifacts_id_and_expire_at.rb'
+ - 'db/post_migrate/20220824114218_add_tmp_index_approval_merge_request_rules.rb'
+ - 'db/post_migrate/20220825061250_drop_tmp_index_todos_attention_request_action_idx.rb'
+ - 'db/post_migrate/20220901071310_add_tmp_index_user_callouts_on_attention_request_feature_names.rb'
+ - 'db/post_migrate/20220901073300_remove_partial_trigram_indexes_for_issues.rb'
+ - 'db/post_migrate/20220905090300_add_tmp_index_merge_request_reviewers_attention_request_state.rb'
+ - 'db/post_migrate/20220913030552_add_tmp_index_system_note_metadata_on_attention_request_actions.rb'
+ - 'db/post_migrate/20220915192521_prepare_async_trigram_index_for_vulnerability_reads_container_images.rb'
+ - 'db/post_migrate/20220916112841_remove_unused_aggregation_columns.rb'
+ - 'db/post_migrate/20220919023208_drop_unused_fields_from_merge_request_assignees.rb'
+ - 'db/post_migrate/20220919041604_drop_unused_fields_from_merge_request_reviewers.rb'
+ - 'db/post_migrate/20220919050555_drop_tmp_index_user_callouts_on_attention_request_feature_names.rb'
+ - 'db/post_migrate/20220923052531_remove_tmp_index_merge_request_reviewers_on_attention_requested_state.rb'
+ - 'db/post_migrate/20220923060226_remove_tmp_index_system_note_metadata_on_attention_request_actions.rb'
+ - 'db/post_migrate/20220929081645_tmp_idx_null_member_namespace_id.rb'
+ - 'db/post_migrate/20220929091500_add_tmp_index_vulns_on_report_type.rb'
+ - 'db/post_migrate/20221004092038_tmp_index_members_on_id_where_namespace_id_null.rb'
+ - 'db/post_migrate/20221005103000_add_index_merge_request_id_on_scan_finding_approval_merge_request_rules.rb'
+ - 'db/post_migrate/20221006083240_prepare_partial_trigram_indexes_for_issues_attempt_2.rb'
+ - 'db/post_migrate/20221019102426_remove_tmp_index_approval_merge_request_rules_on_report_type.rb'
+ - 'db/post_migrate/20221024034228_remove_sprints_project_id_column.rb'
+ - 'db/post_migrate/20221025220607_add_index_id_on_scan_finding_approval_merge_request_rules.rb'
+ - 'db/post_migrate/20221028000603_prepare_partial_trigram_indexes_for_issues_attempt_3.rb'
+ - 'db/post_migrate/20221104042137_add_partial_trigram_index_for_issue_title_attempt_2.rb'
+ - 'db/post_migrate/20221104042159_add_partial_trigram_index_for_issue_description_attempt_2.rb'
+ - 'db/post_migrate/20221104074652_add_temp_index_for_project_statistics_upload_size_migration.rb'
+ - 'db/post_migrate/20221104100203_recreate_async_trigram_index_for_vulnerability_reads_container_images.rb'
+ - 'db/post_migrate/20221115120602_add_index_for_issues_health_status_ordering.rb'
+ - 'db/post_migrate/20221117153015_add_index_merge_request_id_created_at_on_scan_finding_approval_merge_request_rules.rb'
- 'ee/app/controllers/concerns/insights_actions.rb'
- 'ee/app/controllers/customers_dot/proxy_controller.rb'
+ - 'ee/app/controllers/ee/admin/dev_ops_report_controller.rb'
- 'ee/app/controllers/ee/groups_controller.rb'
- 'ee/app/controllers/ee/passwords_controller.rb'
+ - 'ee/app/controllers/groups/analytics/devops_adoption_controller.rb'
- 'ee/app/controllers/groups/analytics/productivity_analytics_controller.rb'
+ - 'ee/app/controllers/groups/contribution_analytics_controller.rb'
+ - 'ee/app/controllers/groups/insights_controller.rb'
+ - 'ee/app/controllers/groups/issues_analytics_controller.rb'
+ - 'ee/app/controllers/groups/two_factor_auths_controller.rb'
+ - 'ee/app/controllers/projects/analytics/code_reviews_controller.rb'
+ - 'ee/app/controllers/projects/analytics/issues_analytics_controller.rb'
+ - 'ee/app/controllers/projects/analytics/merge_request_analytics_controller.rb'
+ - 'ee/app/controllers/projects/insights_controller.rb'
- 'ee/app/controllers/projects/integrations/jira/issues_controller.rb'
- 'ee/app/controllers/subscriptions_controller.rb'
- 'ee/app/finders/geo/registry_finder.rb'
- 'ee/app/graphql/ee/mutations/boards/issues/issue_move_list.rb'
+ - 'ee/app/graphql/ee/mutations/ci/project_ci_cd_settings_update.rb'
+ - 'ee/app/graphql/ee/resolvers/clusters/agents_resolver.rb'
+ - 'ee/app/graphql/ee/resolvers/issues/base_resolver.rb'
+ - 'ee/app/graphql/ee/types/board_list_type.rb'
+ - 'ee/app/graphql/ee/types/board_type.rb'
+ - 'ee/app/graphql/ee/types/boards/board_issue_input_type.rb'
- 'ee/app/graphql/ee/types/ci/pipeline_type.rb'
+ - 'ee/app/graphql/ee/types/ci/runner_type.rb'
- 'ee/app/graphql/ee/types/deprecated_mutations.rb'
+ - 'ee/app/graphql/ee/types/group_type.rb'
+ - 'ee/app/graphql/ee/types/issue_type.rb'
+ - 'ee/app/graphql/ee/types/merge_request_type.rb'
+ - 'ee/app/graphql/ee/types/query_type.rb'
+ - 'ee/app/graphql/ee/types/repository/blob_type.rb'
- 'ee/app/graphql/mutations/app_sec/fuzzing/api/ci_configuration/create.rb'
- 'ee/app/graphql/mutations/app_sec/fuzzing/coverage/corpus/create.rb'
- 'ee/app/graphql/mutations/boards/epic_boards/epic_move_list.rb'
- 'ee/app/graphql/mutations/boards/epic_lists/update.rb'
+ - 'ee/app/graphql/mutations/ci/namespace_ci_cd_settings_update.rb'
- 'ee/app/graphql/mutations/concerns/mutations/shared_epic_arguments.rb'
- 'ee/app/graphql/mutations/dast/profiles/create.rb'
- 'ee/app/graphql/mutations/dast_scanner_profiles/create.rb'
- 'ee/app/graphql/mutations/epics/update.rb'
+ - 'ee/app/graphql/mutations/incident_management/issuable_resource_link/create.rb'
+ - 'ee/app/graphql/mutations/incident_management/issuable_resource_link/destroy.rb'
+ - 'ee/app/graphql/mutations/iterations/cadences/create.rb'
+ - 'ee/app/graphql/mutations/iterations/cadences/update.rb'
+ - 'ee/app/graphql/mutations/iterations/delete.rb'
+ - 'ee/app/graphql/mutations/projects/set_locked.rb'
- 'ee/app/graphql/mutations/requirements_management/update_requirement.rb'
+ - 'ee/app/graphql/mutations/security/finding/create_issue.rb'
+ - 'ee/app/graphql/mutations/security/finding/dismiss.rb'
+ - 'ee/app/graphql/mutations/users/abuse/namespace_bans/destroy.rb'
- 'ee/app/graphql/mutations/vulnerabilities/create.rb'
+ - 'ee/app/graphql/mutations/vulnerabilities/finding/dismiss.rb'
- 'ee/app/graphql/resolvers/boards/board_list_epics_resolver.rb'
+ - 'ee/app/graphql/resolvers/boards/epic_list_resolver.rb'
- 'ee/app/graphql/resolvers/ci/code_coverage_activities_resolver.rb'
- 'ee/app/graphql/resolvers/concerns/common_requirement_arguments.rb'
- - 'ee/app/graphql/resolvers/epics_resolver.rb'
+ - 'ee/app/graphql/resolvers/gitlab_subscriptions/preview_billable_user_change_resolver.rb'
+ - 'ee/app/graphql/resolvers/iterations/cadences_resolver.rb'
- 'ee/app/graphql/resolvers/iterations_resolver.rb'
- - 'ee/app/graphql/resolvers/security_orchestration/scan_execution_policy_resolver.rb'
- 'ee/app/graphql/resolvers/security_training_urls_resolver.rb'
+ - 'ee/app/graphql/resolvers/vulnerabilities_count_per_day_resolver.rb'
- 'ee/app/graphql/resolvers/vulnerabilities_grade_resolver.rb'
+ - 'ee/app/graphql/types/access_levels/group_type.rb'
+ - 'ee/app/graphql/types/access_levels/user_type.rb'
+ - 'ee/app/graphql/types/admin/cloud_licenses/current_license_type.rb'
+ - 'ee/app/graphql/types/admin/cloud_licenses/license_type.rb'
+ - 'ee/app/graphql/types/admin/cloud_licenses/subscription_future_entry_type.rb'
- 'ee/app/graphql/types/alert_management/payload_alert_field_input_type.rb'
+ - 'ee/app/graphql/types/analytics/contribution_analytics/contribution_metadata_type.rb'
+ - 'ee/app/graphql/types/analytics/devops_adoption/enabled_namespace_type.rb'
+ - 'ee/app/graphql/types/analytics/devops_adoption/snapshot_type.rb'
+ - 'ee/app/graphql/types/app_sec/fuzzing/api/ci_configuration_type.rb'
+ - 'ee/app/graphql/types/app_sec/fuzzing/api/scan_profile_type.rb'
+ - 'ee/app/graphql/types/app_sec/fuzzing/coverage/corpus_type.rb'
+ - 'ee/app/graphql/types/boards/epic_board_type.rb'
+ - 'ee/app/graphql/types/boards/epic_list_type.rb'
+ - 'ee/app/graphql/types/boards/epic_user_preferences_type.rb'
+ - 'ee/app/graphql/types/burnup_chart_daily_totals_type.rb'
+ - 'ee/app/graphql/types/ci/code_quality_degradation_type.rb'
+ - 'ee/app/graphql/types/ci/namespace_ci_cd_setting_type.rb'
+ - 'ee/app/graphql/types/compliance_management/merge_requests/compliance_violation_type.rb'
- 'ee/app/graphql/types/dast/profile_cadence_input_type.rb'
- 'ee/app/graphql/types/dast/profile_schedule_input_type.rb'
+ - 'ee/app/graphql/types/dast/profile_schedule_type.rb'
+ - 'ee/app/graphql/types/dast/profile_type.rb'
+ - 'ee/app/graphql/types/dast_scanner_profile_type.rb'
+ - 'ee/app/graphql/types/dast_site_profile_type.rb'
+ - 'ee/app/graphql/types/dast_site_validation_type.rb'
+ - 'ee/app/graphql/types/dora_type.rb'
+ - 'ee/app/graphql/types/epic_issue_type.rb'
+ - 'ee/app/graphql/types/epic_type.rb'
+ - 'ee/app/graphql/types/external_issue_type.rb'
+ - 'ee/app/graphql/types/geo/dependency_proxy_manifest_registry_type.rb'
- 'ee/app/graphql/types/incident_management/escalation_rule_input_type.rb'
- 'ee/app/graphql/types/incident_management/oncall_participant_type.rb'
- 'ee/app/graphql/types/incident_management/oncall_rotation_active_period_input_type.rb'
- 'ee/app/graphql/types/incident_management/oncall_rotation_date_input_type.rb'
- 'ee/app/graphql/types/incident_management/oncall_rotation_length_input_type.rb'
- 'ee/app/graphql/types/incident_management/oncall_user_input_type.rb'
+ - 'ee/app/graphql/types/iteration_type.rb'
+ - 'ee/app/graphql/types/iterations/cadence_type.rb'
+ - 'ee/app/graphql/types/merge_requests/approval_state_type.rb'
+ - 'ee/app/graphql/types/namespaces/namespace_ban_type.rb'
- 'ee/app/graphql/types/push_rules_type.rb'
+ - 'ee/app/graphql/types/requirements_management/requirement_type.rb'
+ - 'ee/app/graphql/types/requirements_management/test_report_type.rb'
+ - 'ee/app/graphql/types/security/training_type.rb'
+ - 'ee/app/graphql/types/security_orchestration/security_policy_relation_type_enum.rb'
+ - 'ee/app/graphql/types/time_report_stats_type.rb'
+ - 'ee/app/graphql/types/timebox_report_interface.rb'
+ - 'ee/app/graphql/types/timebox_report_type.rb'
+ - 'ee/app/graphql/types/vulnerabilities/asset_type.rb'
+ - 'ee/app/graphql/types/vulnerabilities/container_image_type.rb'
+ - 'ee/app/graphql/types/vulnerability/external_issue_link_type.rb'
+ - 'ee/app/graphql/types/vulnerability/issue_link_type.rb'
+ - 'ee/app/graphql/types/vulnerability_details/base_type.rb'
+ - 'ee/app/graphql/types/vulnerability_details/diff_type.rb'
+ - 'ee/app/graphql/types/vulnerability_details/file_location_type.rb'
+ - 'ee/app/graphql/types/vulnerability_details/markdown_type.rb'
+ - 'ee/app/graphql/types/vulnerability_details/module_location_type.rb'
- 'ee/app/graphql/types/vulnerability_identifier_input_type.rb'
+ - 'ee/app/graphql/types/vulnerability_location/container_scanning_type.rb'
+ - 'ee/app/graphql/types/vulnerability_location/coverage_fuzzing_type.rb'
+ - 'ee/app/graphql/types/vulnerability_location/dast_type.rb'
+ - 'ee/app/graphql/types/vulnerability_location/dependency_scanning_type.rb'
+ - 'ee/app/graphql/types/vulnerability_location/generic_type.rb'
+ - 'ee/app/graphql/types/vulnerability_location/sast_type.rb'
+ - 'ee/app/graphql/types/vulnerability_location/secret_detection_type.rb'
+ - 'ee/app/graphql/types/vulnerability_request_response_header_type.rb'
+ - 'ee/app/graphql/types/vulnerability_request_type.rb'
+ - 'ee/app/graphql/types/vulnerability_response_type.rb'
- 'ee/app/graphql/types/vulnerability_scanner_input_type.rb'
+ - 'ee/app/graphql/types/vulnerability_scanner_type.rb'
- 'ee/app/graphql/types/vulnerability_scanner_vendor_input_type.rb'
+ - 'ee/app/graphql/types/vulnerability_type.rb'
+ - 'ee/app/graphql/types/vulnerable_dependency_type.rb'
+ - 'ee/app/graphql/types/vulnerable_kubernetes_resource_type.rb'
+ - 'ee/app/graphql/types/vulnerable_projects_by_grade_type.rb'
+ - 'ee/app/graphql/types/work_items/widgets/status_type.rb'
+ - 'ee/app/graphql/types/work_items/widgets/weight_type.rb'
- 'ee/app/helpers/ee/button_helper.rb'
- 'ee/app/models/concerns/approval_rule_like.rb'
- 'ee/app/models/concerns/security/scan_execution_policy.rb'
+ - 'ee/app/models/concerns/security/scan_result_policy.rb'
- 'ee/app/models/dast/site_profile_secret_variable.rb'
+ - 'ee/app/models/ee/dependency_proxy/blob.rb'
+ - 'ee/app/models/ee/dependency_proxy/manifest.rb'
+ - 'ee/app/models/ee/epic.rb'
+ - 'ee/app/models/ee/group.rb'
- 'ee/app/models/ee/identity.rb'
- 'ee/app/models/ee/incident_management/project_incident_management_setting.rb'
- 'ee/app/models/ee/issue.rb'
- - 'ee/app/models/ee/lfs_object.rb'
- 'ee/app/models/ee/list.rb'
- - 'ee/app/models/ee/merge_request_diff.rb'
- 'ee/app/models/ee/namespace.rb'
- - 'ee/app/models/ee/pages_deployment.rb'
+ - 'ee/app/models/ee/namespace_setting.rb'
- 'ee/app/models/ee/project.rb'
+ - 'ee/app/models/ee/project_group_link.rb'
+ - 'ee/app/models/geo/dependency_proxy_manifest_state.rb'
- 'ee/app/models/geo/event_log.rb'
+ - 'ee/app/models/geo/project_wiki_repository_state.rb'
- 'ee/app/models/geo/repository_renamed_event.rb'
+ - 'ee/app/models/integrations/github.rb'
+ - 'ee/app/models/package_metadata/package_version_license.rb'
- 'ee/app/models/project_alias.rb'
+ - 'ee/app/models/slack_integration.rb'
- 'ee/app/models/status_page/project_setting.rb'
+ - 'ee/app/serializers/ee/evidences/release_entity.rb'
- 'ee/app/serializers/vulnerability_entity.rb'
- 'ee/app/services/ci/minutes/update_project_and_namespace_usage_service.rb'
- - 'ee/app/services/ee/ci/queue/build_queue_service.rb'
+ - 'ee/app/services/ee/merge_requests/create_pipeline_service.rb'
- 'ee/app/services/geo/event_store.rb'
- 'ee/app/services/geo/repository_base_sync_service.rb'
+ - 'ee/app/services/geo/repository_sync_service.rb'
+ - 'ee/app/services/geo/wiki_sync_service.rb'
+ - 'ee/app/services/incident_management/issuable_resource_links/create_service.rb'
+ - 'ee/app/services/incident_management/issuable_resource_links/destroy_service.rb'
+ - 'ee/app/services/incident_management/issuable_resource_links/zoom_link_service.rb'
- 'ee/app/services/timebox_report_service.rb'
+ - 'ee/app/services/vulnerabilities/findings/find_or_create_from_security_finding_service.rb'
+ - 'ee/app/workers/elastic/project_transfer_worker.rb'
- 'ee/db/geo/migrate/20180405074130_add_partial_index_project_repository_verification.rb'
+ - 'ee/db/geo/migrate/20221017195204_create_project_wiki_repository_registry.rb'
+ - 'ee/db/geo/migrate/20221018201808_create_dependency_proxy_blob_registry.rb'
+ - 'ee/db/geo/migrate/20221102195145_create_dependency_proxy_manifest_registry.rb'
+ - 'ee/db/seeds/awesome_co/awesome_co.rb'
+ - 'ee/lib/api/dora/metrics.rb'
- 'ee/lib/api/epic_issues.rb'
+ - 'ee/lib/api/geo_replication.rb'
+ - 'ee/lib/api/iterations.rb'
- 'ee/lib/api/managed_licenses.rb'
+ - 'ee/lib/api/merge_trains.rb'
+ - 'ee/lib/api/protected_environments.rb'
+ - 'ee/lib/api/related_epic_links.rb'
+ - 'ee/lib/api/vulnerability_exports.rb'
+ - 'ee/lib/api/vulnerability_findings.rb'
+ - 'ee/lib/audit/project_changes_auditor.rb'
+ - 'ee/lib/audit/project_setting_changes_auditor.rb'
- 'ee/lib/bulk_imports/groups/pipelines/iterations_pipeline.rb'
- 'ee/lib/ee/api/entities/approval_settings.rb'
- - 'ee/lib/ee/api/members.rb'
+ - 'ee/lib/ee/api/entities/approval_state.rb'
+ - 'ee/lib/ee/api/entities/epic.rb'
+ - 'ee/lib/ee/api/entities/group.rb'
+ - 'ee/lib/ee/api/entities/group_detail.rb'
+ - 'ee/lib/ee/api/entities/merge_train.rb'
+ - 'ee/lib/ee/api/helpers/groups_helpers.rb'
+ - 'ee/lib/ee/api/helpers/issues_helpers.rb'
+ - 'ee/lib/ee/api/helpers/merge_requests_helpers.rb'
+ - 'ee/lib/ee/api/helpers/protected_branches_helpers.rb'
+ - 'ee/lib/ee/api/merge_requests.rb'
+ - 'ee/lib/ee/gitlab/background_migration/backfill_project_statistics_container_repository_size.rb'
- 'ee/lib/ee/gitlab/background_migration/populate_latest_pipeline_ids.rb'
- 'ee/lib/ee/gitlab/background_migration/populate_resolved_on_default_branch_column.rb'
- 'ee/lib/ee/gitlab/ci/config/entry/job.rb'
- 'ee/lib/ee/gitlab/ci/config/entry/need.rb'
- 'ee/lib/ee/gitlab/event_store.rb'
+ - 'ee/lib/ee/gitlab/quick_actions/issue_actions.rb'
- 'ee/lib/ee/gitlab/web_ide/config/entry/global.rb'
+ - 'ee/lib/elastic/latest/config.rb'
- 'ee/lib/gitlab/analytics/cycle_analytics/summary/group/stage_summary.rb'
+ - 'ee/lib/gitlab/contribution_analytics/data_collector.rb'
+ - 'ee/lib/gitlab/ingestion/bulk_updatable_task.rb'
+ - 'ee/lib/gitlab/insights/loader.rb'
- 'ee/lib/gitlab/web_ide/config/entry/schema.rb'
+ - 'ee/lib/slack/block_kit/app_home_opened.rb'
- 'ee/spec/controllers/admin/clusters_controller_spec.rb'
+ - 'ee/spec/controllers/ee/admin/sessions_controller_spec.rb'
- 'ee/spec/controllers/ee/groups_controller_spec.rb'
- 'ee/spec/controllers/ee/projects/variables_controller_spec.rb'
- 'ee/spec/controllers/groups/clusters_controller_spec.rb'
+ - 'ee/spec/controllers/groups/security/policies_controller_spec.rb'
- 'ee/spec/controllers/groups/todos_controller_spec.rb'
+ - 'ee/spec/controllers/projects/iterations_controller_spec.rb'
+ - 'ee/spec/controllers/projects/legacy_pipelines_controller_spec.rb'
- 'ee/spec/controllers/projects/merge_requests/creations_controller_spec.rb'
- 'ee/spec/controllers/projects/merge_requests_controller_spec.rb'
- 'ee/spec/controllers/projects/protected_environments_controller_spec.rb'
+ - 'ee/spec/controllers/projects/security/sast_configuration_controller_spec.rb'
- 'ee/spec/controllers/projects/security/scanned_resources_controller_spec.rb'
+ - 'ee/spec/controllers/projects/settings/merge_requests_controller_spec.rb'
- 'ee/spec/controllers/projects_controller_spec.rb'
- 'ee/spec/controllers/subscriptions_controller_spec.rb'
- 'ee/spec/factories/epic_tree_nodes.rb'
- 'ee/spec/factories/groups.rb'
- 'ee/spec/factories/namespaces.rb'
- 'ee/spec/factories/users.rb'
+ - 'ee/spec/features/account_recovery_regular_check_spec.rb'
- 'ee/spec/features/billings/billing_plans_spec.rb'
- - 'ee/spec/features/ide/user_commits_changes_spec.rb'
+ - 'ee/spec/features/groups/contribution_analytics_spec.rb'
+ - 'ee/spec/features/issues/filtered_search/filter_issues_by_health_spec.rb'
- 'ee/spec/features/merge_request/user_approves_spec.rb'
- 'ee/spec/features/merge_request/user_merges_immediately_spec.rb'
- 'ee/spec/features/merge_request/user_sees_approval_widget_spec.rb'
@@ -390,23 +736,42 @@ Layout/ArgumentAlignment:
- 'ee/spec/features/merge_trains/user_adds_to_merge_train_when_pipeline_succeeds_spec.rb'
- 'ee/spec/features/projects/environments/environments_spec.rb'
- 'ee/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb'
+ - 'ee/spec/features/projects/pipelines/legacy_pipeline_spec.rb'
- 'ee/spec/features/projects/pipelines/pipeline_spec.rb'
+ - 'ee/spec/features/registrations/saas/standard_flow_company_creating_project_spec.rb'
- 'ee/spec/finders/security/pipeline_vulnerabilities_finder_spec.rb'
+ - 'ee/spec/finders/security/training_urls_finder_spec.rb'
+ - 'ee/spec/frontend/fixtures/oncall_schedule.rb'
- 'ee/spec/frontend/fixtures/search.rb'
+ - 'ee/spec/graphql/ee/types/clusters/agent_type_spec.rb'
- 'ee/spec/graphql/mutations/requirements_management/export_requirements_spec.rb'
- - 'ee/spec/helpers/billing_plans_helper_spec.rb'
+ - 'ee/spec/helpers/ee/environments_helper_spec.rb'
+ - 'ee/spec/helpers/ee/namespace_user_cap_reached_alert_helper_spec.rb'
- 'ee/spec/helpers/ee/users/callouts_helper_spec.rb'
- - 'ee/spec/helpers/routing/pseudonymization_helper_spec.rb'
+ - 'ee/spec/helpers/projects_helper_spec.rb'
- 'ee/spec/helpers/trial_status_widget_helper_spec.rb'
- - 'ee/spec/lib/analytics/group_activity_calculator_spec.rb'
+ - 'ee/spec/lib/audit/protected_branches_changes_auditor_spec.rb'
- 'ee/spec/lib/banzai/filter/cross_project_issuable_information_filter_spec.rb'
- 'ee/spec/lib/ee/api/entities/analytics/group_activity_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/background_migration/backfill_project_statistics_container_repository_size_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/checks/push_rules/commit_check_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/ci/status/build/manual_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/database/gitlab_schema_spec.rb'
- 'ee/spec/lib/ee/gitlab/usage_data_spec.rb'
- 'ee/spec/lib/gitlab/ci/minutes/cost_factor_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/minutes/gitlab_contribution_cost_factor_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/parsers/security/validators/default_branch_image_validator_spec.rb'
- 'ee/spec/lib/gitlab/ci/pipeline/chain/limit/size_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/container_scanning_latest_gitlab_ci_yaml_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/dependency_scanning_latest_gitlab_ci_yaml_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/license_scanning_latest_gitlab_ci_yaml_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/sast_latest_gitlab_ci_yaml_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/secret_detection_latest_gitlab_ci_yaml_spec.rb'
- 'ee/spec/lib/gitlab/git_access_spec.rb'
- 'ee/spec/lib/gitlab/graphql/aggregations/security_orchestration_policies/lazy_dast_profile_aggregate_spec.rb'
+ - 'ee/spec/lib/gitlab/usage/metrics/instrumentations/count_user_merge_requests_for_projects_with_applied_scan_result_policies_metric_spec.rb'
- 'ee/spec/lib/incident_management/oncall_shift_generator_spec.rb'
+ - 'ee/spec/lib/slack/block_kit/app_home_opened_spec.rb'
- 'ee/spec/models/approval_wrapped_code_owner_rule_spec.rb'
- 'ee/spec/models/ci/minutes/limit_spec.rb'
- 'ee/spec/models/ci/minutes/namespace_monthly_usage_spec.rb'
@@ -414,13 +779,18 @@ Layout/ArgumentAlignment:
- 'ee/spec/models/ee/audit_event_spec.rb'
- 'ee/spec/models/ee/ci/build_dependencies_spec.rb'
- 'ee/spec/models/ee/ci/runner_spec.rb'
+ - 'ee/spec/models/ee/event_spec.rb'
- 'ee/spec/models/ee/namespace_spec.rb'
- 'ee/spec/models/ee/service_desk_setting_spec.rb'
+ - 'ee/spec/models/integrations/github/status_message_spec.rb'
- 'ee/spec/models/integrations/github_spec.rb'
- 'ee/spec/models/ldap_group_link_spec.rb'
- 'ee/spec/models/merge_request_spec.rb'
- 'ee/spec/models/merge_train_spec.rb'
- 'ee/spec/models/project_spec.rb'
+ - 'ee/spec/models/protected_environments/approval_rule_spec.rb'
+ - 'ee/spec/models/protected_environments/deploy_access_level_spec.rb'
+ - 'ee/spec/models/security/orchestration_policy_configuration_spec.rb'
- 'ee/spec/models/vulnerabilities/read_spec.rb'
- 'ee/spec/policies/merge_request_policy_spec.rb'
- 'ee/spec/requests/api/ci/runner/jobs_put_spec.rb'
@@ -428,8 +798,10 @@ Layout/ArgumentAlignment:
- 'ee/spec/requests/api/graphql/boards/epic_board_list_epics_query_spec.rb'
- 'ee/spec/requests/api/graphql/boards/epic_boards_query_spec.rb'
- 'ee/spec/requests/api/graphql/boards/epic_lists_query_spec.rb'
+ - 'ee/spec/requests/api/graphql/ci/minutes/usage_spec.rb'
- 'ee/spec/requests/api/graphql/group/dast_profile_schedule_spec.rb'
- 'ee/spec/requests/api/graphql/group/epic/epic_issues_spec.rb'
+ - 'ee/spec/requests/api/graphql/group/epics_spec.rb'
- 'ee/spec/requests/api/graphql/mutations/boards/epic_boards/epic_move_list_spec.rb'
- 'ee/spec/requests/api/graphql/mutations/epics/create_spec.rb'
- 'ee/spec/requests/api/graphql/mutations/epics/update_spec.rb'
@@ -444,78 +816,134 @@ Layout/ArgumentAlignment:
- 'ee/spec/requests/api/graphql/mutations/vulnerabilities/destroy_external_issue_link_spec.rb'
- 'ee/spec/requests/api/graphql/mutations/vulnerabilities/finding_dismiss_spec.rb'
- 'ee/spec/requests/api/graphql/namespace/projects_spec.rb'
+ - 'ee/spec/requests/api/graphql/project/branch_rules/approval_project_rules_spec.rb'
+ - 'ee/spec/requests/api/graphql/project/deployment_spec.rb'
+ - 'ee/spec/requests/api/graphql/project/environments_spec.rb'
- 'ee/spec/requests/api/graphql/project/issues_spec.rb'
+ - 'ee/spec/requests/api/graphql/project/pipeline/security_report_finding_spec.rb'
- 'ee/spec/requests/api/graphql/project/pipeline/security_report_summary_spec.rb'
- 'ee/spec/requests/api/graphql/project/requirements_management/test_reports_spec.rb'
+ - 'ee/spec/requests/api/graphql/project/security_orchestration/scan_result_policy_spec.rb'
- 'ee/spec/requests/api/group_hooks_spec.rb'
- 'ee/spec/requests/api/groups_spec.rb'
+ - 'ee/spec/requests/api/internal/base_spec.rb'
- 'ee/spec/requests/api/invitations_spec.rb'
- 'ee/spec/requests/api/issues_spec.rb'
- 'ee/spec/requests/api/managed_licenses_spec.rb'
- 'ee/spec/requests/api/project_push_rule_spec.rb'
+ - 'ee/spec/requests/api/protected_environments_spec.rb'
+ - 'ee/spec/requests/api/provider_identity_spec.rb'
- 'ee/spec/requests/api/settings_spec.rb'
+ - 'ee/spec/requests/groups/protected_environments_controller_spec.rb'
- 'ee/spec/requests/groups_controller_spec.rb'
- - 'ee/spec/requests/projects/mirrors_controller_spec.rb'
- 'ee/spec/requests/projects/requirements_management/requirements_controller_spec.rb'
+ - 'ee/spec/serializers/protected_environments/deploy_access_level_entity_spec.rb'
- 'ee/spec/serializers/vulnerabilities/finding_entity_spec.rb'
- 'ee/spec/services/approval_rules/update_service_spec.rb'
- 'ee/spec/services/audit_events/export_csv_service_spec.rb'
- 'ee/spec/services/auto_merge/add_to_merge_train_when_pipeline_succeeds_service_spec.rb'
- 'ee/spec/services/auto_merge/merge_train_service_spec.rb'
+ - 'ee/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb'
- 'ee/spec/services/ci/create_pipeline_service/cross_needs_artifacts_spec.rb'
- 'ee/spec/services/ci/external_pull_requests/process_github_event_service_spec.rb'
- 'ee/spec/services/ci/minutes/batch_reset_service_spec.rb'
- 'ee/spec/services/ci/minutes/update_build_minutes_service_spec.rb'
- 'ee/spec/services/ci/pipeline_creation/drop_not_runnable_builds_service_spec.rb'
+ - 'ee/spec/services/ci/subscribe_bridge_service_spec.rb'
- 'ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb'
- 'ee/spec/services/ee/merge_requests/refresh_service_spec.rb'
- 'ee/spec/services/ee/protected_branches/create_service_spec.rb'
- 'ee/spec/services/epics/update_service_spec.rb'
- 'ee/spec/services/geo/file_registry_removal_service_spec.rb'
- 'ee/spec/services/geo/repository_verification_primary_service_spec.rb'
+ - 'ee/spec/services/incident_management/issuable_resource_links/create_service_spec.rb'
+ - 'ee/spec/services/incident_management/issuable_resource_links/destroy_service_spec.rb'
+ - 'ee/spec/services/issuable/discussions_list_service_spec.rb'
+ - 'ee/spec/services/merge_request_approval_settings/update_service_spec.rb'
- 'ee/spec/services/merge_requests/reset_approvals_service_spec.rb'
+ - 'ee/spec/services/merge_trains/check_status_service_spec.rb'
- 'ee/spec/services/merge_trains/create_pipeline_service_spec.rb'
+ - 'ee/spec/services/merge_trains/refresh_merge_request_service_spec.rb'
+ - 'ee/spec/services/merge_trains/refresh_service_spec.rb'
- 'ee/spec/services/projects/mark_for_deletion_service_spec.rb'
- 'ee/spec/services/projects/restore_service_spec.rb'
+ - 'ee/spec/services/security/findings/dismiss_service_spec.rb'
+ - 'ee/spec/services/security/ingestion/tasks/ingest_vulnerabilities/mark_resolved_as_detected_spec.rb'
+ - 'ee/spec/services/security/ingestion/tasks/ingest_vulnerabilities_spec.rb'
- 'ee/spec/services/security/merge_reports_service_spec.rb'
+ - 'ee/spec/services/security/orchestration/unassign_service_spec.rb'
- 'ee/spec/services/security/report_summary_service_spec.rb'
- 'ee/spec/services/security/security_orchestration_policies/rule_schedule_service_spec.rb'
+ - 'ee/spec/services/security/security_orchestration_policies/sync_scan_result_policies_service_spec.rb'
- 'ee/spec/services/todo_service_spec.rb'
+ - 'ee/spec/services/users/abuse/git_abuse/namespace_throttle_service_spec.rb'
+ - 'ee/spec/services/users/email_verification/send_custom_confirmation_instructions_service_spec.rb'
+ - 'ee/spec/services/vulnerabilities/find_or_create_from_security_finding_service_spec.rb'
+ - 'ee/spec/services/vulnerabilities/findings/find_or_create_from_security_finding_service_spec.rb'
+ - 'ee/spec/services/vulnerabilities/security_finding/create_issue_service_spec.rb'
+ - 'ee/spec/services/vulnerabilities/starboard_vulnerability_resolve_service_spec.rb'
- 'ee/spec/support/helpers/ee/geo_helpers.rb'
- 'ee/spec/support/shared_examples/features/credentials_inventory_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/features/password_complexity_shared_examples.rb'
- 'ee/spec/views/groups/feature_discovery_moments/advanced_features_dashboard.html.haml_spec.rb'
+ - 'ee/spec/views/groups/settings/reporting/show.html.haml_spec.rb'
- 'ee/spec/views/layouts/_search.html.haml_spec.rb'
+ - 'ee/spec/views/shared/credentials_inventory/resource_access_tokens/_resource_access_token.html.haml_spec.rb'
- 'ee/spec/workers/adjourned_group_deletion_worker_spec.rb'
+ - 'ee/spec/workers/analytics/cycle_analytics/consistency_worker_spec.rb'
- 'ee/spec/workers/ci/batch_reset_minutes_worker_spec.rb'
+ - 'ee/spec/workers/concerns/update_orchestration_policy_configuration_spec.rb'
- 'ee/spec/workers/geo/repository_verification/primary/shard_worker_spec.rb'
+ - 'ee/spec/workers/namespaces/sync_namespace_name_worker_spec.rb'
+ - 'lib/api/admin/batched_background_migrations.rb'
- 'lib/api/admin/ci/variables.rb'
- - 'lib/api/ci/job_artifacts.rb'
+ - 'lib/api/alert_management_alerts.rb'
+ - 'lib/api/ci/runners.rb'
- 'lib/api/commits.rb'
- 'lib/api/concerns/packages/npm_endpoints.rb'
- 'lib/api/deploy_keys.rb'
- 'lib/api/deployments.rb'
+ - 'lib/api/entities/ci/job.rb'
+ - 'lib/api/entities/ci/job_artifact.rb'
+ - 'lib/api/entities/ci/pipeline.rb'
- 'lib/api/entities/ci/pipeline_schedule_details.rb'
+ - 'lib/api/entities/ci/runner.rb'
+ - 'lib/api/entities/ci/secure_file.rb'
+ - 'lib/api/entities/compare.rb'
+ - 'lib/api/entities/deploy_key.rb'
- 'lib/api/entities/diff_position.rb'
- 'lib/api/entities/event.rb'
+ - 'lib/api/entities/group_detail.rb'
- 'lib/api/entities/internal/pages/lookup_path.rb'
- 'lib/api/entities/merge_request_diff.rb'
+ - 'lib/api/entities/merge_request_simple.rb'
- 'lib/api/entities/pages_domain.rb'
- 'lib/api/entities/pages_domain_basic.rb'
+ - 'lib/api/entities/protected_ref_access.rb'
+ - 'lib/api/entities/pull_mirror.rb'
+ - 'lib/api/entities/release.rb'
+ - 'lib/api/environments.rb'
+ - 'lib/api/error_tracking/project_settings.rb'
- 'lib/api/feature_flags.rb'
- 'lib/api/feature_flags_user_lists.rb'
- 'lib/api/features.rb'
+ - 'lib/api/freeze_periods.rb'
- 'lib/api/helm_packages.rb'
- 'lib/api/helpers/internal_helpers.rb'
- - 'lib/api/helpers/merge_requests_helpers.rb'
+ - 'lib/api/internal/pages.rb'
- 'lib/api/issues.rb'
- 'lib/api/merge_requests.rb'
- 'lib/api/project_container_repositories.rb'
- 'lib/api/project_import.rb'
+ - 'lib/api/protected_branches.rb'
+ - 'lib/api/release/links.rb'
- 'lib/api/releases.rb'
- 'lib/api/remote_mirrors.rb'
- 'lib/api/repositories.rb'
- 'lib/api/search.rb'
+ - 'lib/api/submodules.rb'
- 'lib/api/tags.rb'
+ - 'lib/api/unleash.rb'
- 'lib/api/users.rb'
- 'lib/api/wikis.rb'
- 'lib/banzai/filter/commit_trailers_filter.rb'
@@ -529,8 +957,10 @@ Layout/ArgumentAlignment:
- 'lib/gem_extensions/active_record/disable_joins/associations/association_scope.rb'
- 'lib/generators/gitlab/usage_metric_definition_generator.rb'
- 'lib/gitlab/background_migration/backfill_ci_queuing_tables.rb'
+ - 'lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification.rb'
- 'lib/gitlab/background_migration/backfill_project_repositories.rb'
- 'lib/gitlab/background_migration/batching_strategies/base_strategy.rb'
+ - 'lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy.rb'
- 'lib/gitlab/background_migration/legacy_upload_mover.rb'
- 'lib/gitlab/background_migration/remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings.rb'
- 'lib/gitlab/ci/config/entry/artifacts.rb'
@@ -538,7 +968,8 @@ Layout/ArgumentAlignment:
- 'lib/gitlab/ci/config/entry/cache.rb'
- 'lib/gitlab/ci/config/entry/default.rb'
- 'lib/gitlab/ci/config/entry/environment.rb'
- - 'lib/gitlab/ci/config/entry/image.rb'
+ - 'lib/gitlab/ci/config/entry/hooks.rb'
+ - 'lib/gitlab/ci/config/entry/imageable.rb'
- 'lib/gitlab/ci/config/entry/include.rb'
- 'lib/gitlab/ci/config/entry/inherit.rb'
- 'lib/gitlab/ci/config/entry/job.rb'
@@ -553,7 +984,6 @@ Layout/ArgumentAlignment:
- 'lib/gitlab/ci/config/entry/workflow.rb'
- 'lib/gitlab/ci/pipeline/chain/config/process.rb'
- 'lib/gitlab/ci/trace.rb'
- - 'lib/gitlab/ci/variables/builder.rb'
- 'lib/gitlab/config/loader/yaml.rb'
- 'lib/gitlab/conflict/file.rb'
- 'lib/gitlab/data_builder/pipeline.rb'
@@ -562,6 +992,7 @@ Layout/ArgumentAlignment:
- 'lib/gitlab/database/load_balancing/resolver.rb'
- 'lib/gitlab/database/partitioning/replace_table.rb'
- 'lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb'
+ - 'lib/gitlab/database/query_analyzers/restrict_allowed_schemas.rb'
- 'lib/gitlab/database_importers/instance_administrators/create_group.rb'
- 'lib/gitlab/database_importers/self_monitoring/project/create_service.rb'
- 'lib/gitlab/database_importers/self_monitoring/project/delete_service.rb'
@@ -569,21 +1000,27 @@ Layout/ArgumentAlignment:
- 'lib/gitlab/diff/file_collection/base.rb'
- 'lib/gitlab/diff/suggestion.rb'
- 'lib/gitlab/error_tracking/error.rb'
+ - 'lib/gitlab/event_store.rb'
- 'lib/gitlab/git/repository.rb'
- 'lib/gitlab/gitaly_client/operation_service.rb'
+ - 'lib/gitlab/gitaly_client/remote_service.rb'
- 'lib/gitlab/github_import/importer/pull_request_importer.rb'
- 'lib/gitlab/github_import/representation/diff_note.rb'
- 'lib/gitlab/gpg.rb'
- 'lib/gitlab/graphs/commits.rb'
- 'lib/gitlab/kubernetes/kube_client.rb'
- 'lib/gitlab/legacy_github_import/client.rb'
+ - 'lib/gitlab/metrics/methods.rb'
- 'lib/gitlab/middleware/read_only/controller.rb'
- 'lib/gitlab/net_http_adapter.rb'
+ - 'lib/gitlab/octokit/middleware.rb'
- 'lib/gitlab/phabricator_import/cache/map.rb'
- 'lib/gitlab/quick_actions/command_definition.rb'
+ - 'lib/gitlab/redis/multi_store.rb'
- 'lib/gitlab/search/abuse_detection.rb'
+ - 'lib/gitlab/sidekiq_daemon/memory_killer.rb'
- 'lib/gitlab/spamcheck/client.rb'
- - 'lib/gitlab/usage_data.rb'
+ - 'lib/gitlab/url_builder.rb'
- 'lib/gitlab/utils/delegator_override/validator.rb'
- 'lib/gitlab/web_ide/config.rb'
- 'lib/gitlab/web_ide/config/entry/global.rb'
@@ -592,12 +1029,53 @@ Layout/ArgumentAlignment:
- 'lib/safe_zip/extract.rb'
- 'lib/tasks/gitlab/shell.rake'
- 'lib/tasks/gitlab/update_templates.rake'
- - 'qa/qa/page/base.rb'
+ - 'qa/qa/ee/resource/license.rb'
+ - 'qa/qa/ee/resource/vulnerability_item.rb'
+ - 'qa/qa/page/component/groups_filter.rb'
+ - 'qa/qa/resource/members.rb'
+ - 'qa/qa/resource/personal_access_token.rb'
+ - 'qa/qa/resource/user.rb'
- 'qa/qa/scenario/shared_attributes.rb'
- - 'qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb'
+ - 'qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb'
+ - 'qa/qa/specs/features/api/3_create/repository/storage_size_spec.rb'
+ - 'qa/qa/specs/features/api/8_monitor/metrics_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/pages/new_static_page_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_content_creation_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_content_manipulation_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_directory_management_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_file_upload_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_list_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_page_deletion_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/source_editor/source_editor_toolbar_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/5_package/package_registry/rubygems_registry_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/10_govern/fix_vulnerability_workflow_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/10_govern/policies_list_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/10_govern/scan_result_policy_vulnerabilities_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/10_govern/vulnerabilities_jira_integration_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/license/cloud_activation_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/utilization/free_namespace_storage_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/rename_replication_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/14_model_ops/suggested_reviewer_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/3_create/group_wiki/delete_group_wiki_page_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/3_create/group_wiki/file_upload_group_wiki_page_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/3_create/repository/file_locking_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_ssh_with_key_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/4_verify/job_trace_archival_spec.rb'
+ - 'qa/qa/tools/delete_test_snippets.rb'
+ - 'qa/qa/tools/delete_user_projects.rb'
- 'qa/qa/tools/generate_perf_testdata.rb'
+ - 'qa/qa/tools/revoke_user_personal_access_tokens.rb'
- 'qa/spec/git/repository_spec.rb'
- 'qa/spec/runtime/env_spec.rb'
- 'scripts/changed-feature-flags'
@@ -605,7 +1083,9 @@ Layout/ArgumentAlignment:
- 'spec/bin/feature_flag_spec.rb'
- 'spec/controllers/admin/ci/variables_controller_spec.rb'
- 'spec/controllers/admin/clusters_controller_spec.rb'
+ - 'spec/controllers/concerns/redis_tracking_spec.rb'
- 'spec/controllers/concerns/renders_commits_spec.rb'
+ - 'spec/controllers/graphql_controller_spec.rb'
- 'spec/controllers/groups/clusters_controller_spec.rb'
- 'spec/controllers/groups/milestones_controller_spec.rb'
- 'spec/controllers/groups/variables_controller_spec.rb'
@@ -617,6 +1097,7 @@ Layout/ArgumentAlignment:
- 'spec/controllers/projects/commit_controller_spec.rb'
- 'spec/controllers/projects/design_management/designs/raw_images_controller_spec.rb'
- 'spec/controllers/projects/design_management/designs/resized_image_controller_spec.rb'
+ - 'spec/controllers/projects/environments_controller_spec.rb'
- 'spec/controllers/projects/feature_flags_clients_controller_spec.rb'
- 'spec/controllers/projects/forks_controller_spec.rb'
- 'spec/controllers/projects/grafana_api_controller_spec.rb'
@@ -627,6 +1108,8 @@ Layout/ArgumentAlignment:
- 'spec/controllers/projects/pipeline_schedules_controller_spec.rb'
- 'spec/controllers/projects/pipelines/tests_controller_spec.rb'
- 'spec/controllers/projects/pipelines_controller_spec.rb'
+ - 'spec/controllers/projects/service_desk_controller_spec.rb'
+ - 'spec/controllers/projects/settings/ci_cd_controller_spec.rb'
- 'spec/controllers/projects/settings/operations_controller_spec.rb'
- 'spec/controllers/projects/tags_controller_spec.rb'
- 'spec/controllers/projects/todos_controller_spec.rb'
@@ -638,9 +1121,11 @@ Layout/ArgumentAlignment:
- 'spec/factories/integrations.rb'
- 'spec/factories/merge_requests.rb'
- 'spec/factories/notes.rb'
- - 'spec/factories/projects.rb'
- - 'spec/factories/snippets.rb'
+ - 'spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb'
+ - 'spec/features/broadcast_messages_spec.rb'
+ - 'spec/features/clusters/cluster_health_dashboard_spec.rb'
- 'spec/features/dashboard/merge_requests_spec.rb'
+ - 'spec/features/help_dropdown_spec.rb'
- 'spec/features/issuables/issuable_list_spec.rb'
- 'spec/features/issues/filtered_search/filter_issues_spec.rb'
- 'spec/features/merge_request/user_creates_mr_spec.rb'
@@ -655,22 +1140,31 @@ Layout/ArgumentAlignment:
- 'spec/features/merge_request/user_sees_versions_spec.rb'
- 'spec/features/merge_requests/user_lists_merge_requests_spec.rb'
- 'spec/features/merge_requests/user_views_open_merge_requests_spec.rb'
+ - 'spec/features/nav/top_nav_tooltip_spec.rb'
+ - 'spec/features/profiles/user_changes_notified_of_own_activity_spec.rb'
+ - 'spec/features/projects/branches_spec.rb'
- 'spec/features/projects/commit/mini_pipeline_graph_spec.rb'
- 'spec/features/projects/environments/environment_spec.rb'
- 'spec/features/projects/environments/environments_spec.rb'
+ - 'spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb'
- 'spec/features/projects/files/editing_a_file_spec.rb'
- 'spec/features/projects/files/project_owner_creates_license_file_spec.rb'
+ - 'spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb'
+ - 'spec/features/projects/files/user_browses_a_tree_with_a_folder_containing_only_a_folder_spec.rb'
- 'spec/features/projects/files/user_reads_pipeline_status_spec.rb'
- 'spec/features/projects/issues/viewing_issues_with_external_authorization_enabled_spec.rb'
- 'spec/features/projects/jobs_spec.rb'
- - 'spec/features/projects/milestones/milestones_sorting_spec.rb'
+ - 'spec/features/projects/pipelines/legacy_pipeline_spec.rb'
+ - 'spec/features/projects/pipelines/legacy_pipelines_spec.rb'
- 'spec/features/projects/pipelines/pipeline_spec.rb'
- 'spec/features/projects/pipelines/pipelines_spec.rb'
+ - 'spec/features/tags/developer_deletes_tag_spec.rb'
- 'spec/finders/ci/commit_statuses_finder_spec.rb'
- 'spec/finders/ci/pipelines_for_merge_request_finder_spec.rb'
- 'spec/finders/clusters/knative_services_finder_spec.rb'
- 'spec/finders/clusters/kubernetes_namespace_finder_spec.rb'
- 'spec/finders/group_descendants_finder_spec.rb'
+ - 'spec/finders/incident_management/timeline_event_tags_finder_spec.rb'
- 'spec/finders/keys_finder_spec.rb'
- 'spec/finders/merge_requests_finder_spec.rb'
- 'spec/finders/personal_access_tokens_finder_spec.rb'
@@ -684,12 +1178,17 @@ Layout/ArgumentAlignment:
- 'spec/graphql/mutations/customer_relations/organizations/create_spec.rb'
- 'spec/graphql/mutations/dependency_proxy/group_settings/update_spec.rb'
- 'spec/graphql/mutations/dependency_proxy/image_ttl_group_policy/update_spec.rb'
+ - 'spec/graphql/mutations/incident_management/timeline_event/create_spec.rb'
- 'spec/graphql/mutations/namespace/package_settings/update_spec.rb'
+ - 'spec/graphql/resolvers/environments/last_deployment_resolver_spec.rb'
+ - 'spec/graphql/resolvers/incident_management/timeline_event_tags_resolver_spec.rb'
- 'spec/graphql/types/project_statistics_type_spec.rb'
+ - 'spec/graphql/types/project_type_spec.rb'
- 'spec/graphql/types/root_storage_statistics_type_spec.rb'
- 'spec/helpers/notes_helper_spec.rb'
- - 'spec/helpers/search_helper_spec.rb'
- 'spec/helpers/todos_helper_spec.rb'
+ - 'spec/initializers/00_rails_disable_joins_spec.rb'
+ - 'spec/initializers/net_http_response_patch_spec.rb'
- 'spec/initializers/secret_token_spec.rb'
- 'spec/lib/atlassian/jira_connect/serializers/feature_flag_entity_spec.rb'
- 'spec/lib/backup/manager_spec.rb'
@@ -700,11 +1199,18 @@ Layout/ArgumentAlignment:
- 'spec/lib/constraints/group_url_constrainer_spec.rb'
- 'spec/lib/constraints/project_url_constrainer_spec.rb'
- 'spec/lib/constraints/user_url_constrainer_spec.rb'
+ - 'spec/lib/feature/definition_spec.rb'
- 'spec/lib/feature_spec.rb'
- 'spec/lib/gitlab/alert_management/payload/managed_prometheus_spec.rb'
- 'spec/lib/gitlab/api_authentication/builder_spec.rb'
+ - 'spec/lib/gitlab/application_rate_limiter/increment_per_actioned_resource_spec.rb'
- 'spec/lib/gitlab/asciidoc_spec.rb'
+ - 'spec/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_with_corrected_regex_spec.rb'
+ - 'spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb'
+ - 'spec/lib/gitlab/background_migration/batched_migration_job_spec.rb'
+ - 'spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_inactive_public_projects_spec.rb'
- 'spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb'
+ - 'spec/lib/gitlab/background_migration/reset_too_many_tags_skipped_registry_imports_spec.rb'
- 'spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb'
- 'spec/lib/gitlab/checks/matching_merge_request_spec.rb'
- 'spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb'
@@ -716,87 +1222,123 @@ Layout/ArgumentAlignment:
- 'spec/lib/gitlab/ci/config/external/file/artifact_spec.rb'
- 'spec/lib/gitlab/ci/parsers/security/common_spec.rb'
- 'spec/lib/gitlab/ci/parsers/test/junit_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/chain/command_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/quota/deployments_spec.rb'
- - 'spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb'
- 'spec/lib/gitlab/ci/status/bridge/common_spec.rb'
- 'spec/lib/gitlab/ci/status/build/factory_spec.rb'
- 'spec/lib/gitlab/ci/variables/builder/group_spec.rb'
- 'spec/lib/gitlab/ci/variables/builder/project_spec.rb'
- 'spec/lib/gitlab/ci/variables/builder_spec.rb'
+ - 'spec/lib/gitlab/cleanup/personal_access_tokens_spec.rb'
- 'spec/lib/gitlab/config/entry/composable_hash_spec.rb'
- 'spec/lib/gitlab/config/entry/configurable_spec.rb'
- 'spec/lib/gitlab/current_settings_spec.rb'
+ - 'spec/lib/gitlab/data_builder/note_spec.rb'
- 'spec/lib/gitlab/data_builder/pipeline_spec.rb'
- 'spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb'
- 'spec/lib/gitlab/database/background_migration/batched_migration_spec.rb'
- 'spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb'
- 'spec/lib/gitlab/database/batch_count_spec.rb'
+ - 'spec/lib/gitlab/database/each_database_spec.rb'
- 'spec/lib/gitlab/database/gitlab_schema_spec.rb'
+ - 'spec/lib/gitlab/database/loose_foreign_keys_spec.rb'
- 'spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb'
- 'spec/lib/gitlab/database/migration_helpers_spec.rb'
- 'spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb'
+ - 'spec/lib/gitlab/database/migrations/batched_background_migration_helpers_spec.rb'
- 'spec/lib/gitlab/database/partitioning/monthly_strategy_spec.rb'
+ - 'spec/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table_spec.rb'
- 'spec/lib/gitlab/database/partitioning_migration_helpers/foreign_key_helpers_spec.rb'
- - 'spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb'
- 'spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb'
- 'spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb'
- 'spec/lib/gitlab/email/message/repository_push_spec.rb'
- - 'spec/lib/gitlab/encoding_helper_spec.rb'
- 'spec/lib/gitlab/external_authorization/client_spec.rb'
- 'spec/lib/gitlab/fogbugz_import/project_creator_spec.rb'
- 'spec/lib/gitlab/git/repository_spec.rb'
- 'spec/lib/gitlab/git_access_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb'
- 'spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb'
- 'spec/lib/gitlab/grape_logging/loggers/exception_logger_spec.rb'
- 'spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb'
+ - 'spec/lib/gitlab/grape_logging/loggers/token_logger_spec.rb'
+ - 'spec/lib/gitlab/graphql/deprecation_spec.rb'
- 'spec/lib/gitlab/import_export/importer_spec.rb'
- 'spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb'
+ - 'spec/lib/gitlab/import_export/project/exported_relations_merger_spec.rb'
- 'spec/lib/gitlab/import_export/project/object_builder_spec.rb'
+ - 'spec/lib/gitlab/import_export/project/relation_saver_spec.rb'
- 'spec/lib/gitlab/import_export/project/tree_restorer_spec.rb'
- 'spec/lib/gitlab/import_export/project/tree_saver_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/importer_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/validator/post_schema_validator_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/validator_spec.rb'
+ - 'spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb'
+ - 'spec/lib/gitlab/pagination/keyset/column_order_definition_spec.rb'
- 'spec/lib/gitlab/phabricator_import/conduit/response_spec.rb'
+ - 'spec/lib/gitlab/redis/duplicate_jobs_spec.rb'
+ - 'spec/lib/gitlab/redis/multi_store_spec.rb'
+ - 'spec/lib/gitlab/redis/sidekiq_status_spec.rb'
+ - 'spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb'
- 'spec/lib/gitlab/spamcheck/client_spec.rb'
+ - 'spec/lib/gitlab/tracking_spec.rb'
- 'spec/lib/gitlab/url_blocker_spec.rb'
- 'spec/lib/gitlab/usage/metric_spec.rb'
+ - 'spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb'
+ - 'spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_metric_spec.rb'
+ - 'spec/lib/gitlab/usage/metrics/instrumentations/merge_request_widget_extension_metric_spec.rb'
- 'spec/lib/gitlab/usage/metrics/query_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/designs_counter_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/snippet_counter_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/wiki_page_counter_spec.rb'
- 'spec/lib/gitlab/usage_data_queries_spec.rb'
+ - 'spec/lib/gitlab/web_hooks/rate_limiter_spec.rb'
- 'spec/lib/mattermost/session_spec.rb'
- 'spec/lib/peek/views/detailed_view_spec.rb'
- 'spec/lib/system_check/base_check_spec.rb'
+ - 'spec/lib/system_check/incoming_email_check_spec.rb'
- 'spec/mailers/emails/pipelines_spec.rb'
- 'spec/mailers/notify_spec.rb'
- 'spec/models/active_session_spec.rb'
+ - 'spec/models/bulk_imports/export_status_spec.rb'
- 'spec/models/ci/build_dependencies_spec.rb'
- 'spec/models/ci/build_spec.rb'
- 'spec/models/ci/build_trace_metadata_spec.rb'
- 'spec/models/ci/commit_with_pipeline_spec.rb'
+ - 'spec/models/ci/job_artifact_spec.rb'
- 'spec/models/ci/job_token/project_scope_link_spec.rb'
- 'spec/models/ci/pipeline_spec.rb'
+ - 'spec/models/ci/processable_spec.rb'
- 'spec/models/clusters/applications/knative_spec.rb'
+ - 'spec/models/commit_spec.rb'
+ - 'spec/models/commit_status_spec.rb'
- 'spec/models/concerns/bulk_insert_safe_spec.rb'
+ - 'spec/models/concerns/ci/partitionable/switch_spec.rb'
- 'spec/models/concerns/deployment_platform_spec.rb'
+ - 'spec/models/customer_relations/contact_spec.rb'
+ - 'spec/models/deployment_spec.rb'
- 'spec/models/design_management/design_spec.rb'
- 'spec/models/diff_note_spec.rb'
- 'spec/models/environment_spec.rb'
- 'spec/models/external_pull_request_spec.rb'
- 'spec/models/instance_configuration_spec.rb'
+ - 'spec/models/integrations/every_integration_spec.rb'
- 'spec/models/integrations/pipelines_email_spec.rb'
- 'spec/models/merge_request_spec.rb'
+ - 'spec/models/milestone_spec.rb'
- 'spec/models/note_spec.rb'
+ - 'spec/models/personal_access_token_spec.rb'
- 'spec/models/project_spec.rb'
- 'spec/models/repository_spec.rb'
- 'spec/models/u2f_registration_spec.rb'
+ - 'spec/models/user_spec.rb'
- 'spec/presenters/blob_presenter_spec.rb'
+ - 'spec/requests/admin/batched_jobs_controller_spec.rb'
- 'spec/requests/api/admin/ci/variables_spec.rb'
+ - 'spec/requests/api/alert_management_alerts_spec.rb'
- 'spec/requests/api/badges_spec.rb'
- 'spec/requests/api/broadcast_messages_spec.rb'
- 'spec/requests/api/ci/job_artifacts_spec.rb'
@@ -804,50 +1346,73 @@ Layout/ArgumentAlignment:
- 'spec/requests/api/ci/pipelines_spec.rb'
- 'spec/requests/api/ci/runner/jobs_request_post_spec.rb'
- 'spec/requests/api/ci/triggers_spec.rb'
+ - 'spec/requests/api/ci/variables_spec.rb'
- 'spec/requests/api/commit_statuses_spec.rb'
+ - 'spec/requests/api/commits_spec.rb'
- 'spec/requests/api/discussions_spec.rb'
+ - 'spec/requests/api/feature_flags_spec.rb'
+ - 'spec/requests/api/graphql/ci/group_variables_spec.rb'
+ - 'spec/requests/api/graphql/ci/runner_spec.rb'
+ - 'spec/requests/api/graphql/ci/runners_spec.rb'
- 'spec/requests/api/graphql/group/dependency_proxy_manifests_spec.rb'
- 'spec/requests/api/graphql/group/group_members_spec.rb'
- 'spec/requests/api/graphql/group/milestones_spec.rb'
- 'spec/requests/api/graphql/mutations/award_emojis/add_spec.rb'
- 'spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb'
+ - 'spec/requests/api/graphql/mutations/ci/job_retry_spec.rb'
- 'spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb'
+ - 'spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb'
- 'spec/requests/api/graphql/mutations/commits/create_spec.rb'
- 'spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb'
+ - 'spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb'
+ - 'spec/requests/api/graphql/mutations/incident_management/timeline_event/update_spec.rb'
- 'spec/requests/api/graphql/mutations/labels/create_spec.rb'
- 'spec/requests/api/graphql/mutations/merge_requests/create_spec.rb'
- 'spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb'
- 'spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb'
- 'spec/requests/api/graphql/mutations/notes/create/note_spec.rb'
+ - 'spec/requests/api/graphql/mutations/notes/update/note_spec.rb'
+ - 'spec/requests/api/graphql/mutations/releases/update_spec.rb'
+ - 'spec/requests/api/graphql/mutations/work_items/create_spec.rb'
+ - 'spec/requests/api/graphql/mutations/work_items/update_spec.rb'
- 'spec/requests/api/graphql/project/container_repositories_spec.rb'
+ - 'spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb'
- 'spec/requests/api/graphql/project/issue/design_collection/version_spec.rb'
- 'spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb'
+ - 'spec/requests/api/graphql/project/issues_spec.rb'
+ - 'spec/requests/api/graphql/project/milestones_spec.rb'
- 'spec/requests/api/graphql/project/project_members_spec.rb'
- 'spec/requests/api/graphql/project/release_spec.rb'
- 'spec/requests/api/graphql/project/releases_spec.rb'
- 'spec/requests/api/graphql/project/terraform/states_spec.rb'
- 'spec/requests/api/graphql/tasks/task_completion_status_spec.rb'
- - 'spec/requests/api/import_github_spec.rb'
+ - 'spec/requests/api/group_variables_spec.rb'
+ - 'spec/requests/api/import_bitbucket_server_spec.rb'
+ - 'spec/requests/api/internal/lfs_spec.rb'
+ - 'spec/requests/api/invitations_spec.rb'
- 'spec/requests/api/issues/get_group_issues_spec.rb'
- 'spec/requests/api/issues/get_project_issues_spec.rb'
- 'spec/requests/api/issues/post_projects_issues_spec.rb'
- 'spec/requests/api/issues/put_projects_issues_spec.rb'
- 'spec/requests/api/labels_spec.rb'
+ - 'spec/requests/api/members_spec.rb'
- 'spec/requests/api/merge_requests_spec.rb'
- 'spec/requests/api/namespaces_spec.rb'
- 'spec/requests/api/notes_spec.rb'
- 'spec/requests/api/oauth_tokens_spec.rb'
- - 'spec/requests/api/project_hooks_spec.rb'
+ - 'spec/requests/api/personal_access_tokens_spec.rb'
+ - 'spec/requests/api/project_export_spec.rb'
- 'spec/requests/api/projects_spec.rb'
- 'spec/requests/api/protected_tags_spec.rb'
- 'spec/requests/api/releases_spec.rb'
- 'spec/requests/api/settings_spec.rb'
- - 'spec/requests/api/system_hooks_spec.rb'
+ - 'spec/requests/api/suggestions_spec.rb'
+ - 'spec/requests/api/unleash_spec.rb'
- 'spec/requests/api/users_spec.rb'
- 'spec/requests/git_http_spec.rb'
- - 'spec/requests/lfs_http_spec.rb'
- 'spec/requests/oauth_tokens_spec.rb'
- 'spec/requests/openid_connect_spec.rb'
+ - 'spec/requests/projects/environments_controller_spec.rb'
- 'spec/requests/self_monitoring_project_spec.rb'
- 'spec/routing/project_routing_spec.rb'
- 'spec/rubocop/cop/rspec/be_success_matcher_spec.rb'
@@ -859,20 +1424,32 @@ Layout/ArgumentAlignment:
- 'spec/services/ci/archive_trace_service_spec.rb'
- 'spec/services/ci/create_downstream_pipeline_service_spec.rb'
- 'spec/services/ci/create_pipeline_service/custom_config_content_spec.rb'
+ - 'spec/services/ci/create_pipeline_service/environment_spec.rb'
+ - 'spec/services/ci/create_pipeline_service/partitioning_spec.rb'
+ - 'spec/services/ci/create_pipeline_service/rate_limit_spec.rb'
- 'spec/services/ci/create_pipeline_service_spec.rb'
- 'spec/services/ci/job_artifacts/create_service_spec.rb'
+ - 'spec/services/ci/job_artifacts/update_unknown_locked_status_service_spec.rb'
- 'spec/services/ci/job_token_scope/remove_project_service_spec.rb'
- 'spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb'
- 'spec/services/ci/register_job_service_spec.rb'
+ - 'spec/services/ci/retry_job_service_spec.rb'
- 'spec/services/ci/retry_pipeline_service_spec.rb'
- 'spec/services/dependency_proxy/group_settings/update_service_spec.rb'
- 'spec/services/dependency_proxy/image_ttl_group_policies/update_service_spec.rb'
- 'spec/services/deployments/update_environment_service_spec.rb'
- 'spec/services/discussions/capture_diff_note_positions_service_spec.rb'
+ - 'spec/services/environments/create_for_build_service_spec.rb'
- 'spec/services/environments/stop_service_spec.rb'
- 'spec/services/files/delete_service_spec.rb'
- 'spec/services/files/update_service_spec.rb'
+ - 'spec/services/google_cloud/fetch_google_ip_list_service_spec.rb'
+ - 'spec/services/incident_management/timeline_event_tags/create_service_spec.rb'
+ - 'spec/services/incident_management/timeline_events/update_service_spec.rb'
+ - 'spec/services/issues/relative_position_rebalancing_service_spec.rb'
- 'spec/services/issues/resolve_discussions_spec.rb'
+ - 'spec/services/jira_connect_subscriptions/create_service_spec.rb'
+ - 'spec/services/merge_requests/build_service_spec.rb'
- 'spec/services/merge_requests/create_pipeline_service_spec.rb'
- 'spec/services/merge_requests/merge_service_spec.rb'
- 'spec/services/merge_requests/post_merge_service_spec.rb'
@@ -883,13 +1460,17 @@ Layout/ArgumentAlignment:
- 'spec/services/merge_requests/update_service_spec.rb'
- 'spec/services/metrics/dashboard/clone_dashboard_service_spec.rb'
- 'spec/services/namespaces/package_settings/update_service_spec.rb'
+ - 'spec/services/notes/destroy_service_spec.rb'
- 'spec/services/notification_service_spec.rb'
- 'spec/services/packages/debian/extract_metadata_service_spec.rb'
- 'spec/services/projects/destroy_service_spec.rb'
+ - 'spec/services/projects/update_service_spec.rb'
- 'spec/services/security/merge_reports_service_spec.rb'
+ - 'spec/services/suggestions/apply_service_spec.rb'
- 'spec/services/system_notes/design_management_service_spec.rb'
- 'spec/services/todo_service_spec.rb'
- 'spec/services/upload_service_spec.rb'
+ - 'spec/services/work_items/task_list_reference_removal_service_spec.rb'
- 'spec/sidekiq_cluster/sidekiq_cluster_spec.rb'
- 'spec/support/database/prevent_cross_database_modification.rb'
- 'spec/support/database/prevent_cross_joins.rb'
@@ -902,32 +1483,51 @@ Layout/ArgumentAlignment:
- 'spec/support/shared_examples/controllers/concerns/integrations/integrations_actions_shared_examples.rb'
- 'spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb'
- 'spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb'
+ - 'spec/support/shared_examples/features/search/redacted_search_results_shared_examples.rb'
- 'spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb'
- 'spec/support/shared_examples/graphql/sorted_paginated_query_shared_examples.rb'
+ - 'spec/support/shared_examples/harbor/artifacts_controller_shared_examples.rb'
+ - 'spec/support/shared_examples/harbor/tags_controller_shared_examples.rb'
+ - 'spec/support/shared_examples/lib/gitlab/template/template_shared_examples.rb'
- 'spec/support/shared_examples/models/active_record_enum_shared_examples.rb'
- 'spec/support/shared_examples/models/concerns/from_set_operator_shared_examples.rb'
- 'spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb'
- 'spec/support/shared_examples/models/diff_note_after_commit_shared_examples.rb'
+ - 'spec/support/shared_examples/models/member_shared_examples.rb'
+ - 'spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb'
- 'spec/support/shared_examples/quick_actions/merge_request/merge_quick_action_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/diff_discussions_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/discussions_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/graphql/group_and_project_boards_query_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/hooks_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/merge_requests_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/notes_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb'
- 'spec/support/shared_examples/requests/graphql_shared_examples.rb'
- 'spec/support/shared_examples/requests/rack_attack_shared_examples.rb'
- 'spec/support/shared_examples/services/boards/boards_recent_visit_shared_examples.rb'
+ - 'spec/support/shared_examples/services/issuable/discussions_list_shared_examples.rb'
+ - 'spec/support/shared_examples/workers/background_migration_worker_shared_examples.rb'
- 'spec/support/trace/trace_helpers.rb'
+ - 'spec/tasks/gitlab/sidekiq_rake_spec.rb'
- 'spec/tooling/quality/test_level_spec.rb'
- 'spec/uploaders/file_uploader_spec.rb'
+ - 'spec/uploaders/object_storage/cdn/google_cdn_spec.rb'
+ - 'spec/uploaders/object_storage/cdn/google_ip_cache_spec.rb'
- 'spec/uploaders/packages/composer/cache_uploader_spec.rb'
- 'spec/views/layouts/_search.html.haml_spec.rb'
- 'spec/views/projects/merge_requests/_commits.html.haml_spec.rb'
- 'spec/views/projects/merge_requests/edit.html.haml_spec.rb'
- 'spec/views/projects/tags/index.html.haml_spec.rb'
+ - 'spec/workers/ci/job_artifacts/track_artifact_report_worker_spec.rb'
+ - 'spec/workers/database/batched_background_migration/ci_execution_worker_spec.rb'
+ - 'spec/workers/database/batched_background_migration/main_execution_worker_spec.rb'
+ - 'spec/workers/merge_requests/create_pipeline_worker_spec.rb'
+ - 'spec/workers/pages/invalidate_domain_cache_worker_spec.rb'
+ - 'spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb'
- 'spec/workers/pipeline_notification_worker_spec.rb'
- 'spec/workers/remove_unreferenced_lfs_objects_worker_spec.rb'
- 'spec/workers/update_external_pull_requests_worker_spec.rb'
+ - 'spec/workers/update_merge_requests_worker_spec.rb'
- 'spec/workers/web_hook_worker_spec.rb'
diff --git a/.rubocop_todo/layout/closing_parenthesis_indentation.yml b/.rubocop_todo/layout/closing_parenthesis_indentation.yml
index 79397e92312..3abbb024db6 100644
--- a/.rubocop_todo/layout/closing_parenthesis_indentation.yml
+++ b/.rubocop_todo/layout/closing_parenthesis_indentation.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Layout/ClosingParenthesisIndentation:
Exclude:
- 'app/presenters/project_presenter.rb'
diff --git a/.rubocop_todo/layout/empty_line_between_defs.yml b/.rubocop_todo/layout/empty_line_between_defs.yml
deleted file mode 100644
index ba69fc4c51b..00000000000
--- a/.rubocop_todo/layout/empty_line_between_defs.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-# Cop supports --auto-correct.
-Layout/EmptyLineBetweenDefs:
- Exclude:
- - 'db/post_migrate/20220202105733_delete_service_template_records.rb'
- - 'ee/lib/ee/gitlab/background_migration/populate_resolved_on_default_branch_column.rb'
- - 'lib/gitlab/import_export/remote_stream_upload.rb'
- - 'lib/gitlab/redis/multi_store.rb'
diff --git a/.rubocop_todo/layout/first_array_element_indentation.yml b/.rubocop_todo/layout/first_array_element_indentation.yml
deleted file mode 100644
index d4a3d2f5524..00000000000
--- a/.rubocop_todo/layout/first_array_element_indentation.yml
+++ /dev/null
@@ -1,25 +0,0 @@
----
-# Cop supports --auto-correct.
-Layout/FirstArrayElementIndentation:
- Exclude:
- - 'spec/lib/gitlab/github_import/importer/issues_importer_spec.rb'
- - 'spec/lib/gitlab/search/found_blob_spec.rb'
- - 'spec/models/ci/runner_version_spec.rb'
- - 'spec/models/repository_spec.rb'
- - 'spec/requests/api/task_completion_status_spec.rb'
- - 'spec/services/security/merge_reports_service_spec.rb'
- - 'spec/simplecov_env.rb'
- - 'spec/support/atlassian/jira_connect/schemata.rb'
- - 'spec/support/capybara.rb'
- - 'spec/support/helpers/project_template_test_helper.rb'
- - 'spec/support/helpers/test_env.rb'
- - 'spec/support/helpers/usage_data_helpers.rb'
- - 'spec/support/matchers/exceed_query_limit.rb'
- - 'spec/support/migrations_helpers/vulnerabilities_findings_helper.rb'
- - 'spec/support/prometheus/additional_metrics_shared_examples.rb'
- - 'spec/support/shared_contexts/policies/group_policy_shared_context.rb'
- - 'spec/support/shared_examples/graphql/label_fields.rb'
- - 'spec/support/shared_examples/lib/gitlab/middleware/multipart_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb'
- - 'spec/views/projects/issues/_related_branches.html.haml_spec.rb'
- - 'tooling/lib/tooling/helm3_client.rb'
diff --git a/.rubocop_todo/layout/first_hash_element_indentation.yml b/.rubocop_todo/layout/first_hash_element_indentation.yml
index 50cd90019b6..e1ac4ccb650 100644
--- a/.rubocop_todo/layout/first_hash_element_indentation.yml
+++ b/.rubocop_todo/layout/first_hash_element_indentation.yml
@@ -1,31 +1,12 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Layout/FirstHashElementIndentation:
Exclude:
- - 'app/components/diffs/stats_component.rb'
- - 'app/controllers/admin/ci/variables_controller.rb'
- - 'app/controllers/admin/system_info_controller.rb'
- - 'app/controllers/boards/issues_controller.rb'
- - 'app/controllers/concerns/issuable_actions.rb'
- - 'app/controllers/concerns/milestone_actions.rb'
- - 'app/controllers/concerns/render_service_results.rb'
- - 'app/controllers/concerns/sourcegraph_decorator.rb'
- - 'app/controllers/profiles/two_factor_auths_controller.rb'
- - 'app/controllers/projects/badges_controller.rb'
- - 'app/controllers/repositories/lfs_locks_api_controller.rb'
- - 'app/experiments/concerns/project_commit_count.rb'
- - 'app/graphql/mutations/clusters/agent_tokens/create.rb'
- - 'app/graphql/mutations/notes/create/diff_note.rb'
- - 'app/graphql/mutations/notes/create/image_diff_note.rb'
- - 'app/graphql/mutations/notes/create/note.rb'
- - 'app/graphql/mutations/todos/restore_many.rb'
- - 'app/graphql/resolvers/group_packages_resolver.rb'
- 'app/helpers/avatars_helper.rb'
- 'app/helpers/breadcrumbs_helper.rb'
- 'app/helpers/broadcast_messages_helper.rb'
- 'app/helpers/commits_helper.rb'
- 'app/helpers/environments_helper.rb'
- - 'app/helpers/icons_helper.rb'
- 'app/helpers/keyset_helper.rb'
- 'app/helpers/listbox_helper.rb'
- 'app/helpers/page_layout_helper.rb'
@@ -51,7 +32,6 @@ Layout/FirstHashElementIndentation:
- 'app/models/concerns/subscribable.rb'
- 'app/models/concerns/taskable.rb'
- 'app/models/diff_note.rb'
- - 'app/models/integrations/datadog.rb'
- 'app/models/integrations/jira.rb'
- 'app/models/jira_connect_installation.rb'
- 'app/models/milestone.rb'
@@ -81,10 +61,8 @@ Layout/FirstHashElementIndentation:
- 'app/services/timelogs/base_service.rb'
- 'app/validators/addressable_url_validator.rb'
- 'app/workers/concerns/cluster_cleanup_methods.rb'
- - 'ee/app/components/namespaces/free_user_cap/alert_component.rb'
- - 'ee/app/components/namespaces/free_user_cap/personal_alert_component.rb'
- - 'ee/app/components/namespaces/free_user_cap/personal_preview_alert_component.rb'
- - 'ee/app/components/namespaces/free_user_cap/preview_alert_component.rb'
+ - 'ee/app/components/namespaces/free_user_cap/enforcement_alert_component.rb'
+ - 'ee/app/components/namespaces/free_user_cap/notification_alert_component.rb'
- 'ee/app/controllers/groups/analytics/tasks_by_type_controller.rb'
- 'ee/app/graphql/mutations/boards/epic_lists/destroy.rb'
- 'ee/app/graphql/mutations/boards/epics/create.rb'
@@ -97,7 +75,6 @@ Layout/FirstHashElementIndentation:
- 'ee/app/helpers/ee/ci/jobs_helper.rb'
- 'ee/app/helpers/ee/geo_helper.rb'
- 'ee/app/helpers/ee/groups/group_members_helper.rb'
- - 'ee/app/helpers/ee/members_helper.rb'
- 'ee/app/helpers/ee/namespaces_helper.rb'
- 'ee/app/helpers/ee/projects_helper.rb'
- 'ee/app/helpers/ee/sidebars_helper.rb'
@@ -111,7 +88,6 @@ Layout/FirstHashElementIndentation:
- 'ee/app/serializers/ee/environment_serializer.rb'
- 'ee/app/services/app_sec/dast/profiles/update_service.rb'
- 'ee/app/services/app_sec/dast/site_profiles/update_service.rb'
- - 'ee/app/services/audit_events/build_service.rb'
- 'ee/app/services/ee/auth/container_registry_authentication_service.rb'
- 'ee/app/services/ee/ci/register_job_service.rb'
- 'ee/app/services/ee/issues/export_csv_service.rb'
@@ -135,17 +111,12 @@ Layout/FirstHashElementIndentation:
- 'ee/lib/gitlab/graphql/aggregations/epics/lazy_epic_aggregate.rb'
- 'ee/lib/gitlab/status_page/storage/s3_multipart_upload.rb'
- 'ee/spec/controllers/admin/application_settings_controller_spec.rb'
- - 'ee/spec/controllers/boards/issues_controller_spec.rb'
- - 'ee/spec/controllers/boards/lists_controller_spec.rb'
- - 'ee/spec/controllers/boards/users_controller_spec.rb'
- 'ee/spec/controllers/ee/projects/jobs_controller_spec.rb'
- 'ee/spec/controllers/ee/projects/variables_controller_spec.rb'
- 'ee/spec/controllers/groups/analytics/cycle_analytics/summary_controller_spec.rb'
- 'ee/spec/controllers/groups/epic_boards_controller_spec.rb'
- 'ee/spec/controllers/groups/issues_controller_spec.rb'
- - 'ee/spec/controllers/projects/boards_controller_spec.rb'
- 'ee/spec/controllers/projects/feature_flag_issues_controller_spec.rb'
- - 'ee/spec/controllers/projects/imports_controller_spec.rb'
- 'ee/spec/controllers/projects/integrations/jira/issues_controller_spec.rb'
- 'ee/spec/controllers/projects/licenses_controller_spec.rb'
- 'ee/spec/controllers/projects/vulnerability_feedback_controller_spec.rb'
@@ -154,18 +125,14 @@ Layout/FirstHashElementIndentation:
- 'ee/spec/elastic/migrate/20220119120500_populate_commit_permissions_in_main_index_spec.rb'
- 'ee/spec/factories/dependencies.rb'
- 'ee/spec/factories/licenses.rb'
- - 'ee/spec/features/registrations/saas_user_registration_spec.rb'
- 'ee/spec/finders/epics_finder_spec.rb'
- - 'ee/spec/finders/security/scan_execution_policies_finder_spec.rb'
- 'ee/spec/frontend/fixtures/dast_profiles.rb'
- 'ee/spec/frontend/fixtures/on_demand_dast_scans.rb'
- 'ee/spec/frontend/fixtures/search.rb'
- 'ee/spec/graphql/mutations/app_sec/fuzzing/api/ci_configuration/create_spec.rb'
- 'ee/spec/graphql/types/vulnerability_request_response_header_type_spec.rb'
- - 'ee/spec/helpers/billing_plans_helper_spec.rb'
- 'ee/spec/helpers/ee/access_tokens_helper_spec.rb'
- 'ee/spec/helpers/ee/groups_helper_spec.rb'
- - 'ee/spec/helpers/ee/invite_members_helper_spec.rb'
- 'ee/spec/helpers/ee/labels_helper_spec.rb'
- 'ee/spec/helpers/ee/namespaces_helper_spec.rb'
- 'ee/spec/helpers/ee/projects/pipeline_helper_spec.rb'
@@ -175,7 +142,6 @@ Layout/FirstHashElementIndentation:
- 'ee/spec/helpers/groups/sso_helper_spec.rb'
- 'ee/spec/helpers/nav/new_dropdown_helper_spec.rb'
- 'ee/spec/helpers/projects_helper_spec.rb'
- - 'ee/spec/helpers/routing/pseudonymization_helper_spec.rb'
- 'ee/spec/helpers/search_helper_spec.rb'
- 'ee/spec/helpers/security_helper_spec.rb'
- 'ee/spec/helpers/trial_registrations/reassurances_helper_spec.rb'
@@ -230,9 +196,7 @@ Layout/FirstHashElementIndentation:
- 'ee/spec/requests/api/members_spec.rb'
- 'ee/spec/requests/api/merge_requests_spec.rb'
- 'ee/spec/requests/groups/group_members_controller_spec.rb'
- - 'ee/spec/requests/groups/usage_quotas_spec.rb'
- 'ee/spec/requests/projects/issue_feature_flags_controller_spec.rb'
- - 'ee/spec/requests/projects/mirrors_controller_spec.rb'
- 'ee/spec/serializers/issues/linked_issue_feature_flag_entity_spec.rb'
- 'ee/spec/serializers/license_entity_spec.rb'
- 'ee/spec/serializers/linked_feature_flag_issue_entity_spec.rb'
@@ -253,18 +217,14 @@ Layout/FirstHashElementIndentation:
- 'ee/spec/services/app_sec/fuzzing/coverage/corpuses/create_service_spec.rb'
- 'ee/spec/services/approval_rules/create_service_spec.rb'
- 'ee/spec/services/approval_rules/update_service_spec.rb'
- - 'ee/spec/services/arkose/user_verification_service_spec.rb'
- 'ee/spec/services/audit_event_service_spec.rb'
- 'ee/spec/services/ci/create_pipeline_service_spec.rb'
- - 'ee/spec/services/ci/runners/stale_group_runners_prune_service_spec.rb'
- 'ee/spec/services/ee/alert_management/http_integrations/create_service_spec.rb'
- 'ee/spec/services/ee/alert_management/http_integrations/update_service_spec.rb'
- 'ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb'
- - 'ee/spec/services/ee/merge_requests/update_service_spec.rb'
- 'ee/spec/services/ee/post_receive_service_spec.rb'
- 'ee/spec/services/external_status_checks/create_service_spec.rb'
- 'ee/spec/services/geo/node_status_request_service_spec.rb'
- - 'ee/spec/services/geo/registry_consistency_service_spec.rb'
- 'ee/spec/services/gitlab_subscriptions/check_future_renewal_service_spec.rb'
- 'ee/spec/services/gitlab_subscriptions/create_trial_or_lead_service_spec.rb'
- 'ee/spec/services/gitlab_subscriptions/plan_upgrade_service_spec.rb'
@@ -274,18 +234,14 @@ Layout/FirstHashElementIndentation:
- 'ee/spec/services/groups/create_service_spec.rb'
- 'ee/spec/services/groups/destroy_service_spec.rb'
- 'ee/spec/services/iterations/create_service_spec.rb'
- - 'ee/spec/services/namespaces/free_user_cap/deactivate_members_over_limit_service_spec.rb'
- - 'ee/spec/services/namespaces/free_user_cap/remove_group_group_links_outside_hierarchy_service_spec.rb'
- 'ee/spec/services/projects/create_service_spec.rb'
- 'ee/spec/services/projects/group_links/create_service_spec.rb'
- 'ee/spec/services/projects/group_links/destroy_service_spec.rb'
- 'ee/spec/services/projects/transfer_service_spec.rb'
- 'ee/spec/services/security/report_summary_service_spec.rb'
- - 'ee/spec/services/security/security_orchestration_policies/create_pipeline_service_spec.rb'
- 'ee/spec/services/security/token_revocation_service_spec.rb'
- 'ee/spec/services/security/track_scan_service_spec.rb'
- 'ee/spec/services/timebox_report_service_spec.rb'
- - 'ee/spec/services/users/abuse/excessive_projects_download_ban_service_spec.rb'
- 'ee/spec/services/users/abuse/git_abuse/namespace_throttle_service_spec.rb'
- 'ee/spec/support/helpers/ee/login_helpers.rb'
- 'ee/spec/support/shared_contexts/fixtures/analytics_shared_context.rb'
@@ -320,7 +276,6 @@ Layout/FirstHashElementIndentation:
- 'lib/gitlab/kubernetes/namespace.rb'
- 'lib/gitlab/kubernetes/rollout_instances.rb'
- 'lib/gitlab/legacy_github_import/client.rb'
- - 'lib/gitlab/nav/top_nav_view_model_builder.rb'
- 'lib/gitlab/push_options.rb'
- 'lib/gitlab/quick_actions/issuable_actions.rb'
- 'lib/gitlab/usage_data_counters/track_unique_events.rb'
@@ -343,20 +298,14 @@ Layout/FirstHashElementIndentation:
- 'qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb'
- 'qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb'
- 'qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/13_secure/security_reports_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/4_verify/new_discussion_not_dropping_merge_trains_mr_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/9_enablement/elasticsearch/elasticsearch_reindexing_spec.rb'
- 'spec/components/diffs/stats_component_spec.rb'
- 'spec/components/pajamas/component_spec.rb'
- 'spec/config/object_store_settings_spec.rb'
- 'spec/config/smime_signature_settings_spec.rb'
- - 'spec/controllers/admin/groups_controller_spec.rb'
- 'spec/controllers/application_controller_spec.rb'
- - 'spec/controllers/boards/issues_controller_spec.rb'
- - 'spec/controllers/boards/lists_controller_spec.rb'
- 'spec/controllers/concerns/import_url_params_spec.rb'
- 'spec/controllers/concerns/issuable_collections_spec.rb'
- - 'spec/controllers/groups/boards_controller_spec.rb'
- 'spec/controllers/groups/group_members_controller_spec.rb'
- 'spec/controllers/groups/packages_controller_spec.rb'
- 'spec/controllers/groups/registry/repositories_controller_spec.rb'
@@ -416,13 +365,11 @@ Layout/FirstHashElementIndentation:
- 'spec/helpers/routing/pseudonymization_helper_spec.rb'
- 'spec/helpers/search_helper_spec.rb'
- 'spec/helpers/sorting_helper_spec.rb'
- - 'spec/helpers/storage_helper_spec.rb'
- 'spec/initializers/direct_upload_support_spec.rb'
- 'spec/lib/api/entities/bulk_imports/export_status_spec.rb'
- 'spec/lib/api/entities/design_management/design_spec.rb'
- 'spec/lib/api/entities/merge_request_approvals_spec.rb'
- 'spec/lib/api/entities/personal_access_token_spec.rb'
- - 'spec/lib/api/entities/personal_access_token_with_details_spec.rb'
- 'spec/lib/atlassian/jira_connect/client_spec.rb'
- 'spec/lib/backup/database_spec.rb'
- 'spec/lib/backup/repositories_spec.rb'
@@ -461,7 +408,6 @@ Layout/FirstHashElementIndentation:
- 'spec/lib/gitlab/ci/config/yaml/tags/resolver_spec.rb'
- 'spec/lib/gitlab/ci/config_spec.rb'
- 'spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb'
- - 'spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb'
- 'spec/lib/gitlab/ci/parsers/security/common_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb'
@@ -478,11 +424,8 @@ Layout/FirstHashElementIndentation:
- 'spec/lib/gitlab/database/migration_helpers_spec.rb'
- 'spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb'
- 'spec/lib/gitlab/database_spec.rb'
- - 'spec/lib/gitlab/diff/position_spec.rb'
- 'spec/lib/gitlab/error_tracking/error_repository/open_api_strategy_spec.rb'
- 'spec/lib/gitlab/error_tracking_spec.rb'
- - 'spec/lib/gitlab/experimentation/controller_concern_spec.rb'
- - 'spec/lib/gitlab/experimentation_spec.rb'
- 'spec/lib/gitlab/git/conflict/file_spec.rb'
- 'spec/lib/gitlab/git/hook_env_spec.rb'
- 'spec/lib/gitlab/git/repository_spec.rb'
@@ -504,7 +447,6 @@ Layout/FirstHashElementIndentation:
- 'spec/lib/gitlab/kubernetes/kubeconfig/template_spec.rb'
- 'spec/lib/gitlab/kubernetes/rollout_instances_spec.rb'
- 'spec/lib/gitlab/legacy_github_import/label_formatter_spec.rb'
- - 'spec/lib/gitlab/memory/watchdog_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb'
- 'spec/lib/gitlab/metrics/subscribers/action_cable_spec.rb'
- 'spec/lib/gitlab/middleware/multipart/handler_spec.rb'
@@ -536,7 +478,6 @@ Layout/FirstHashElementIndentation:
- 'spec/models/integrations/chat_message/pipeline_message_spec.rb'
- 'spec/models/integrations/chat_message/push_message_spec.rb'
- 'spec/models/integrations/chat_message/wiki_page_message_spec.rb'
- - 'spec/models/integrations/packagist_spec.rb'
- 'spec/models/integrations/prometheus_spec.rb'
- 'spec/models/loose_foreign_keys/modification_tracker_spec.rb'
- 'spec/models/merge_request_diff_commit_spec.rb'
@@ -583,7 +524,6 @@ Layout/FirstHashElementIndentation:
- 'spec/services/ci/play_manual_stage_service_spec.rb'
- 'spec/services/ci/runners/reconcile_existing_runner_versions_service_spec.rb'
- 'spec/services/clusters/agents/create_service_spec.rb'
- - 'spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb'
- 'spec/services/clusters/aws/authorize_role_service_spec.rb'
- 'spec/services/clusters/update_service_spec.rb'
- 'spec/services/commits/tag_service_spec.rb'
@@ -631,7 +571,6 @@ Layout/FirstHashElementIndentation:
- 'spec/support_specs/graphql/arguments_spec.rb'
- 'spec/support_specs/graphql/field_selection_spec.rb'
- 'spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb'
- - 'spec/tooling/danger/datateam_spec.rb'
- 'spec/tooling/lib/tooling/kubernetes_client_spec.rb'
- 'spec/views/projects/issues/_issue.html.haml_spec.rb'
- 'spec/workers/ci/runners/reconcile_existing_runner_versions_cron_worker_spec.rb'
diff --git a/.rubocop_todo/layout/hash_alignment.yml b/.rubocop_todo/layout/hash_alignment.yml
index 62d877624a7..7aa46e3fd05 100644
--- a/.rubocop_todo/layout/hash_alignment.yml
+++ b/.rubocop_todo/layout/hash_alignment.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Layout/HashAlignment:
Exclude:
- 'ee/spec/lib/ee/gitlab/usage_data_spec.rb'
diff --git a/.rubocop_todo/layout/leading_comment_space.yml b/.rubocop_todo/layout/leading_comment_space.yml
index fda1d4ff30b..9dc79492f2a 100644
--- a/.rubocop_todo/layout/leading_comment_space.yml
+++ b/.rubocop_todo/layout/leading_comment_space.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Layout/LeadingCommentSpace:
Exclude:
- 'config/initializers/kaminari_active_record_relation_methods_with_limit.rb'
diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml
index b5fc199c0ca..07e7b374db2 100644
--- a/.rubocop_todo/layout/line_length.yml
+++ b/.rubocop_todo/layout/line_length.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Layout/LineLength:
Exclude:
- 'app/controllers/abuse_reports_controller.rb'
@@ -13,7 +13,6 @@ Layout/LineLength:
- 'app/controllers/admin/topics_controller.rb'
- 'app/controllers/admin/users_controller.rb'
- 'app/controllers/application_controller.rb'
- - 'app/controllers/boards/lists_controller.rb'
- 'app/controllers/concerns/access_tokens_actions.rb'
- 'app/controllers/concerns/analytics/cycle_analytics/stage_actions.rb'
- 'app/controllers/concerns/clientside_preview_csp.rb'
@@ -93,7 +92,6 @@ Layout/LineLength:
- 'app/controllers/projects/pipeline_schedules_controller.rb'
- 'app/controllers/projects/pipelines_controller.rb'
- 'app/controllers/projects/prometheus/metrics_controller.rb'
- - 'app/controllers/projects/raw_controller.rb'
- 'app/controllers/projects/settings/ci_cd_controller.rb'
- 'app/controllers/projects/settings/repository_controller.rb'
- 'app/controllers/projects/templates_controller.rb'
@@ -165,7 +163,6 @@ Layout/LineLength:
- 'app/graphql/mutations/snippets/update.rb'
- 'app/graphql/mutations/todos/create.rb'
- 'app/graphql/mutations/todos/mark_all_done.rb'
- - 'app/graphql/resolvers/concerns/issue_resolver_arguments.rb'
- 'app/graphql/resolvers/concerns/time_frame_arguments.rb'
- 'app/graphql/resolvers/container_repository_tags_resolver.rb'
- 'app/graphql/resolvers/project_merge_requests_resolver.rb'
@@ -355,7 +352,6 @@ Layout/LineLength:
- 'app/models/concerns/sortable.rb'
- 'app/models/concerns/storage/legacy_namespace.rb'
- 'app/models/concerns/subscribable.rb'
- - 'app/models/concerns/timebox.rb'
- 'app/models/concerns/token_authenticatable_strategies/base.rb'
- 'app/models/concerns/token_authenticatable_strategies/encrypted.rb'
- 'app/models/concerns/token_authenticatable_strategies/encryption_helper.rb'
@@ -395,7 +391,6 @@ Layout/LineLength:
- 'app/models/integrations/emails_on_push.rb'
- 'app/models/integrations/ewm.rb'
- 'app/models/integrations/external_wiki.rb'
- - 'app/models/integrations/flowdock.rb'
- 'app/models/integrations/hangouts_chat.rb'
- 'app/models/integrations/harbor.rb'
- 'app/models/integrations/jenkins.rb'
@@ -516,7 +511,6 @@ Layout/LineLength:
- 'app/services/ci/drop_pipeline_service.rb'
- 'app/services/ci/generate_coverage_reports_service.rb'
- 'app/services/ci/job_artifacts/destroy_all_expired_service.rb'
- - 'app/services/ci/job_artifacts/destroy_batch_service.rb'
- 'app/services/ci/job_artifacts/expire_project_build_artifacts_service.rb'
- 'app/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service.rb'
- 'app/services/ci/pipelines/add_job_service.rb'
@@ -527,8 +521,6 @@ Layout/LineLength:
- 'app/services/clusters/agent_tokens/create_service.rb'
- 'app/services/clusters/agents/delete_service.rb'
- 'app/services/clusters/applications/check_progress_service.rb'
- - 'app/services/clusters/applications/check_uninstall_progress_service.rb'
- - 'app/services/clusters/applications/prometheus_update_service.rb'
- 'app/services/clusters/aws/finalize_creation_service.rb'
- 'app/services/clusters/aws/verify_provision_status_service.rb'
- 'app/services/clusters/build_kubernetes_namespace_service.rb'
@@ -683,7 +675,6 @@ Layout/LineLength:
- 'app/services/system_notes/zoom_service.rb'
- 'app/services/tags/destroy_service.rb'
- 'app/services/tasks_to_be_done/base_service.rb'
- - 'app/services/two_factor/destroy_service.rb'
- 'app/services/users/approve_service.rb'
- 'app/services/users/banned_user_base_service.rb'
- 'app/services/users/build_service.rb'
@@ -712,8 +703,6 @@ Layout/LineLength:
- 'app/workers/database/batched_background_migration/single_database_worker.rb'
- 'app/workers/error_tracking_issue_link_worker.rb'
- 'app/workers/gitlab/github_import/stage/finish_import_worker.rb'
- - 'app/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker.rb'
- - 'app/workers/gitlab/github_import/stage/import_notes_worker.rb'
- 'app/workers/gitlab/github_import/stage/import_repository_worker.rb'
- 'app/workers/gitlab/import/stuck_import_job.rb'
- 'app/workers/gitlab/jira_import/stage/import_attachments_worker.rb'
@@ -759,6 +748,7 @@ Layout/LineLength:
- 'config/initializers/validate_puma.rb'
- 'config/initializers/zz_metrics.rb'
- 'config/object_store_settings.rb'
+ - 'config/puma.rb'
- 'config/routes.rb'
- 'config/routes/admin.rb'
- 'config/routes/api.rb'
@@ -1057,7 +1047,6 @@ Layout/LineLength:
- 'ee/app/controllers/admin/geo/application_controller.rb'
- 'ee/app/controllers/admin/geo/projects_controller.rb'
- 'ee/app/controllers/admin/licenses_controller.rb'
- - 'ee/app/controllers/concerns/audit_events/audit_events_params.rb'
- 'ee/app/controllers/concerns/audit_events/date_range.rb'
- 'ee/app/controllers/concerns/credentials_inventory_actions.rb'
- 'ee/app/controllers/concerns/ee/issuable_collections.rb'
@@ -1097,7 +1086,6 @@ Layout/LineLength:
- 'ee/app/enums/vulnerabilities/dismissal_reason_enum.rb'
- 'ee/app/finders/compliance_management/merge_requests/compliance_violations_finder.rb'
- 'ee/app/finders/incident_management/escalation_rules_finder.rb'
- - 'ee/app/finders/iterations_finder.rb'
- 'ee/app/finders/merge_requests/by_approvers_finder.rb'
- 'ee/app/finders/projects/integrations/jira/by_ids_finder.rb'
- 'ee/app/finders/projects/integrations/jira/issues_finder.rb'
@@ -1105,7 +1093,6 @@ Layout/LineLength:
- 'ee/app/finders/security/vulnerabilities_finder.rb'
- 'ee/app/graphql/ee/mutations/boards/lists/create.rb'
- 'ee/app/graphql/ee/mutations/ci/runner/update.rb'
- - 'ee/app/graphql/ee/resolvers/base_issues_resolver.rb'
- 'ee/app/graphql/mutations/analytics/devops_adoption/enabled_namespaces/bulk_enable.rb'
- 'ee/app/graphql/mutations/audit_events/external_audit_event_destinations/create.rb'
- 'ee/app/graphql/mutations/audit_events/external_audit_event_destinations/update.rb'
@@ -1136,7 +1123,6 @@ Layout/LineLength:
- 'ee/app/graphql/resolvers/boards/epic_lists_resolver.rb'
- 'ee/app/graphql/resolvers/ci/code_coverage_activities_resolver.rb'
- 'ee/app/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver.rb'
- - 'ee/app/graphql/resolvers/concerns/resolves_orchestration_policy.rb'
- 'ee/app/graphql/resolvers/dora_metrics_resolver.rb'
- 'ee/app/graphql/resolvers/external_issue_resolver.rb'
- 'ee/app/graphql/resolvers/incident_management/oncall_rotations_resolver.rb'
@@ -1283,7 +1269,6 @@ Layout/LineLength:
- 'ee/app/models/elastic/migration_record.rb'
- 'ee/app/models/elastic/reindexing_slice.rb'
- 'ee/app/models/epic_issue.rb'
- - 'ee/app/models/geo/container_repository_registry.rb'
- 'ee/app/models/geo/project_registry.rb'
- 'ee/app/models/geo/secondary_usage_data.rb'
- 'ee/app/models/geo_node.rb'
@@ -1371,7 +1356,6 @@ Layout/LineLength:
- 'ee/app/services/ee/analytics/cycle_analytics/stages/list_service.rb'
- 'ee/app/services/ee/application_settings/update_service.rb'
- 'ee/app/services/ee/applications/create_service.rb'
- - 'ee/app/services/ee/auth/container_registry_authentication_service.rb'
- 'ee/app/services/ee/boards/base_service.rb'
- 'ee/app/services/ee/ci/job_artifacts/destroy_batch_service.rb'
- 'ee/app/services/ee/ci/register_job_service.rb'
@@ -1458,7 +1442,6 @@ Layout/LineLength:
- 'ee/app/services/security/report_summary_service.rb'
- 'ee/app/services/security/scanned_resources_counting_service.rb'
- 'ee/app/services/security/scanned_resources_service.rb'
- - 'ee/app/services/security/security_orchestration_policies/create_pipeline_service.rb'
- 'ee/app/services/security/security_orchestration_policies/fetch_policy_approvers_service.rb'
- 'ee/app/services/security/security_orchestration_policies/on_demand_scan_pipeline_configuration_service.rb'
- 'ee/app/services/security/security_orchestration_policies/policy_commit_service.rb'
@@ -1473,9 +1456,7 @@ Layout/LineLength:
- 'ee/app/services/system_notes/escalations_service.rb'
- 'ee/app/services/timebox_report_service.rb'
- 'ee/app/services/vulnerabilities/base_service.rb'
- - 'ee/app/services/vulnerabilities/dismiss_service.rb'
- 'ee/app/services/vulnerabilities/historical_statistics/adjustment_service.rb'
- - 'ee/app/services/vulnerabilities/resolve_service.rb'
- 'ee/app/services/vulnerabilities/revert_to_detected_service.rb'
- 'ee/app/services/vulnerabilities/statistics/adjustment_service.rb'
- 'ee/app/services/vulnerabilities/update_service.rb'
@@ -1508,7 +1489,6 @@ Layout/LineLength:
- 'ee/app/workers/repository_update_mirror_worker.rb'
- 'ee/app/workers/security/orchestration_policy_rule_schedule_namespace_worker.rb'
- 'ee/app/workers/security/orchestration_policy_rule_schedule_worker.rb'
- - 'ee/app/workers/update_all_mirrors_worker.rb'
- 'ee/app/workers/update_max_seats_used_for_gitlab_com_subscriptions_worker.rb'
- 'ee/config/routes/group.rb'
- 'ee/config/routes/project.rb'
@@ -1537,13 +1517,11 @@ Layout/LineLength:
- 'ee/db/geo/post_migrate/20220202101354_migrate_job_artifact_registry.rb'
- 'ee/lib/analytics/devops_adoption/snapshot_calculator.rb'
- 'ee/lib/analytics/productivity_analytics_request_params.rb'
- - 'ee/lib/api/analytics/code_review_analytics.rb'
- 'ee/lib/api/audit_events.rb'
- 'ee/lib/api/ci/minutes.rb'
- 'ee/lib/api/dependencies.rb'
- 'ee/lib/api/epic_issues.rb'
- 'ee/lib/api/epic_links.rb'
- - 'ee/lib/api/epics.rb'
- 'ee/lib/api/geo_nodes.rb'
- 'ee/lib/api/group_hooks.rb'
- 'ee/lib/api/group_push_rule.rb'
@@ -1588,8 +1566,6 @@ Layout/LineLength:
- 'ee/lib/ee/api/merge_request_approvals.rb'
- 'ee/lib/ee/api/merge_requests.rb'
- 'ee/lib/ee/api/namespaces.rb'
- - 'ee/lib/ee/api/protected_branches.rb'
- - 'ee/lib/ee/audit/project_changes_auditor.rb'
- 'ee/lib/ee/banzai/filter/references/iteration_reference_filter.rb'
- 'ee/lib/ee/gitlab/analytics/cycle_analytics/aggregated/base_query_builder.rb'
- 'ee/lib/ee/gitlab/analytics/cycle_analytics/data_collector.rb'
@@ -1627,9 +1603,7 @@ Layout/LineLength:
- 'ee/lib/ee/gitlab/repository_size_checker.rb'
- 'ee/lib/ee/gitlab/scim/deprovision_service.rb'
- 'ee/lib/ee/gitlab/usage_data.rb'
- - 'ee/lib/ee/sidebars/groups/menus/issues_menu.rb'
- 'ee/lib/ee/sidebars/groups/panel.rb'
- - 'ee/lib/ee/sidebars/projects/menus/issues_menu.rb'
- 'ee/lib/ee/sidebars/projects/menus/security_compliance_menu.rb'
- 'ee/lib/elastic/latest/application_class_proxy.rb'
- 'ee/lib/elastic/latest/config.rb'
@@ -1664,7 +1638,6 @@ Layout/LineLength:
- 'ee/lib/gitlab/ci/reports/security/locations/cluster_image_scanning.rb'
- 'ee/lib/gitlab/contribution_analytics/data_collector.rb'
- 'ee/lib/gitlab/elastic/group_search_results.rb'
- - 'ee/lib/gitlab/elastic/indexer.rb'
- 'ee/lib/gitlab/elastic/project_search_results.rb'
- 'ee/lib/gitlab/elastic/search_results.rb'
- 'ee/lib/gitlab/email/message/account_validation.rb'
@@ -1713,7 +1686,6 @@ Layout/LineLength:
- 'ee/spec/controllers/admin/licenses_controller_spec.rb'
- 'ee/spec/controllers/admin/projects_controller_spec.rb'
- 'ee/spec/controllers/admin/users_controller_spec.rb'
- - 'ee/spec/controllers/boards/issues_controller_spec.rb'
- 'ee/spec/controllers/concerns/ee/routable_actions/sso_enforcement_redirect_spec.rb'
- 'ee/spec/controllers/ee/dashboard/projects_controller_spec.rb'
- 'ee/spec/controllers/ee/groups_controller_spec.rb'
@@ -1721,7 +1693,6 @@ Layout/LineLength:
- 'ee/spec/controllers/ee/search_controller_spec.rb'
- 'ee/spec/controllers/ee/uploads_controller_spec.rb'
- 'ee/spec/controllers/groups/analytics/cycle_analytics/value_streams_controller_spec.rb'
- - 'ee/spec/controllers/groups/analytics/cycle_analytics_controller_spec.rb'
- 'ee/spec/controllers/groups/analytics/tasks_by_type_controller_spec.rb'
- 'ee/spec/controllers/groups/audit_events_controller_spec.rb'
- 'ee/spec/controllers/groups/epic_issues_controller_spec.rb'
@@ -1764,7 +1735,6 @@ Layout/LineLength:
- 'ee/spec/factories/ci/reports/security/locations/container_scanning.rb'
- 'ee/spec/factories/ci/reports/security/locations/dependency_scanning.rb'
- 'ee/spec/factories/compliance_management/frameworks.rb'
- - 'ee/spec/factories/geo/container_repository_registry.rb'
- 'ee/spec/factories/iterations.rb'
- 'ee/spec/factories/namespaces.rb'
- 'ee/spec/factories/vulnerabilities/exports.rb'
@@ -1803,7 +1773,6 @@ Layout/LineLength:
- 'ee/spec/features/groups/group_settings_spec.rb'
- 'ee/spec/features/groups/groups_security_credentials_spec.rb'
- 'ee/spec/features/groups/hooks/user_tests_hooks_spec.rb'
- - 'ee/spec/features/groups/iterations/iterations_list_spec.rb'
- 'ee/spec/features/groups/iterations/user_creates_iteration_in_cadence_spec.rb'
- 'ee/spec/features/groups/iterations/user_edits_iteration_cadence_spec.rb'
- 'ee/spec/features/groups/iterations/user_edits_iteration_spec.rb'
@@ -1839,7 +1808,6 @@ Layout/LineLength:
- 'ee/spec/features/projects/integrations/prometheus_custom_metrics_spec.rb'
- 'ee/spec/features/projects/integrations/user_activates_jira_spec.rb'
- 'ee/spec/features/projects/iterations/iteration_cadences_list_spec.rb'
- - 'ee/spec/features/projects/iterations/iterations_list_spec.rb'
- 'ee/spec/features/projects/iterations/user_views_iteration_spec.rb'
- 'ee/spec/features/projects/licenses/maintainer_views_policies_spec.rb'
- 'ee/spec/features/projects/members/member_is_removed_from_project_spec.rb'
@@ -1857,7 +1825,6 @@ Layout/LineLength:
- 'ee/spec/features/projects_spec.rb'
- 'ee/spec/features/promotion_spec.rb'
- 'ee/spec/features/read_only_spec.rb'
- - 'ee/spec/features/search/elastic/global_search_spec.rb'
- 'ee/spec/features/search/elastic/project_search_spec.rb'
- 'ee/spec/features/search/elastic/snippet_search_spec.rb'
- 'ee/spec/features/subscriptions_spec.rb'
@@ -1874,7 +1841,6 @@ Layout/LineLength:
- 'ee/spec/finders/ee/group_members_finder_spec.rb'
- 'ee/spec/finders/ee/projects_finder_spec.rb'
- 'ee/spec/finders/epics_finder_spec.rb'
- - 'ee/spec/finders/geo/container_repository_legacy_registry_finder_spec.rb'
- 'ee/spec/finders/geo/project_registry_finder_spec.rb'
- 'ee/spec/finders/geo/project_registry_status_finder_spec.rb'
- 'ee/spec/finders/group_projects_finder_spec.rb'
@@ -2048,8 +2014,6 @@ Layout/LineLength:
- 'ee/spec/lib/ee/api/entities/deployment_extended_spec.rb'
- 'ee/spec/lib/ee/api/entities/vulnerability_export_spec.rb'
- 'ee/spec/lib/ee/api/helpers_spec.rb'
- - 'ee/spec/lib/ee/audit/compliance_framework_changes_auditor_spec.rb'
- - 'ee/spec/lib/ee/audit/protected_branches_changes_auditor_spec.rb'
- 'ee/spec/lib/ee/gitlab/alert_management/payload/generic_spec.rb'
- 'ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb'
- 'ee/spec/lib/ee/gitlab/background_migration/backfill_iteration_cadence_id_for_boards_spec.rb'
@@ -2071,7 +2035,6 @@ Layout/LineLength:
- 'ee/spec/lib/ee/gitlab/ci/parsers/security/common_spec.rb'
- 'ee/spec/lib/ee/gitlab/ci/parsers/security/validators/schema_validator_spec.rb'
- 'ee/spec/lib/ee/gitlab/ci/pipeline/chain/validate/security_orchestration_policy_spec.rb'
- - 'ee/spec/lib/ee/gitlab/ci/pipeline/quota/job_activity_spec.rb'
- 'ee/spec/lib/ee/gitlab/ci/reports/security/reports_spec.rb'
- 'ee/spec/lib/ee/gitlab/ci/status/build/manual_spec.rb'
- 'ee/spec/lib/ee/gitlab/elastic/helper_spec.rb'
@@ -2139,7 +2102,6 @@ Layout/LineLength:
- 'ee/spec/lib/gitlab/ci/parsers/security/dast_spec.rb'
- 'ee/spec/lib/gitlab/ci/parsers/security/dependency_list_spec.rb'
- 'ee/spec/lib/gitlab/ci/parsers/security/dependency_scanning_spec.rb'
- - 'ee/spec/lib/gitlab/ci/parsers/security/formatters/dast_spec.rb'
- 'ee/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb'
- 'ee/spec/lib/gitlab/ci/pipeline/chain/create_cross_database_associations_spec.rb'
- 'ee/spec/lib/gitlab/ci/reports/dependency_list/dependency_spec.rb'
@@ -2467,7 +2429,6 @@ Layout/LineLength:
- 'ee/spec/requests/api/graphql/project/issues_spec.rb'
- 'ee/spec/requests/api/graphql/project/pipeline/security_report_summary_spec.rb'
- 'ee/spec/requests/api/graphql/project/requirements_management/requirements_spec.rb'
- - 'ee/spec/requests/api/graphql/project/requirements_management/test_reports_spec.rb'
- 'ee/spec/requests/api/graphql/project/vulnerability_severities_count_spec.rb'
- 'ee/spec/requests/api/graphql/projects/compliance_frameworks_spec.rb'
- 'ee/spec/requests/api/graphql/vulnerabilities/description_spec.rb'
@@ -2609,7 +2570,6 @@ Layout/LineLength:
- 'ee/spec/services/deployments/auto_rollback_service_spec.rb'
- 'ee/spec/services/ee/alert_management/http_integrations/update_service_spec.rb'
- 'ee/spec/services/ee/allowed_email_domains/update_service_spec.rb'
- - 'ee/spec/services/ee/auth/container_registry_authentication_service_spec.rb'
- 'ee/spec/services/ee/boards/issues/create_service_spec.rb'
- 'ee/spec/services/ee/boards/issues/list_service_spec.rb'
- 'ee/spec/services/ee/boards/lists/max_limits_spec.rb'
@@ -2640,7 +2600,6 @@ Layout/LineLength:
- 'ee/spec/services/ee/resource_events/merge_into_notes_service_spec.rb'
- 'ee/spec/services/ee/system_notes/issuables_service_spec.rb'
- 'ee/spec/services/ee/users/destroy_service_spec.rb'
- - 'ee/spec/services/ee/users/migrate_to_ghost_user_service_spec.rb'
- 'ee/spec/services/ee/users/update_service_spec.rb'
- 'ee/spec/services/elastic/cluster_reindexing_service_spec.rb'
- 'ee/spec/services/elastic/data_migration_service_spec.rb'
@@ -2692,7 +2651,6 @@ Layout/LineLength:
- 'ee/spec/services/iterations/cadences/create_iterations_in_advance_service_spec.rb'
- 'ee/spec/services/iterations/cadences/destroy_service_spec.rb'
- 'ee/spec/services/iterations/cadences/update_service_spec.rb'
- - 'ee/spec/services/iterations/create_service_spec.rb'
- 'ee/spec/services/iterations/delete_service_spec.rb'
- 'ee/spec/services/jira/jql_builder_service_spec.rb'
- 'ee/spec/services/jira/requests/issues/list_service_spec.rb'
@@ -2700,13 +2658,11 @@ Layout/LineLength:
- 'ee/spec/services/members/activate_service_spec.rb'
- 'ee/spec/services/merge_commits/export_csv_service_spec.rb'
- 'ee/spec/services/merge_requests/merge_service_spec.rb'
- - 'ee/spec/services/merge_requests/merge_to_ref_service_spec.rb'
- 'ee/spec/services/merge_requests/push_options_handler_service_spec.rb'
- 'ee/spec/services/merge_requests/reset_approvals_service_spec.rb'
- 'ee/spec/services/merge_requests/sync_report_approver_approval_rules_spec.rb'
- 'ee/spec/services/merge_trains/create_pipeline_service_spec.rb'
- 'ee/spec/services/merge_trains/refresh_merge_request_service_spec.rb'
- - 'ee/spec/services/merge_trains/refresh_service_spec.rb'
- 'ee/spec/services/personal_access_tokens/create_service_audit_log_spec.rb'
- 'ee/spec/services/personal_access_tokens/rotation_verifier_service_spec.rb'
- 'ee/spec/services/projects/alerting/notify_service_spec.rb'
@@ -2725,7 +2681,6 @@ Layout/LineLength:
- 'ee/spec/services/search/group_service_spec.rb'
- 'ee/spec/services/search/project_service_spec.rb'
- 'ee/spec/services/search/snippet_service_spec.rb'
- - 'ee/spec/services/search_service_spec.rb'
- 'ee/spec/services/security/dependency_list_service_spec.rb'
- 'ee/spec/services/security/ingestion/finding_map_collection_spec.rb'
- 'ee/spec/services/security/ingestion/ingest_report_service_spec.rb'
@@ -2783,7 +2738,6 @@ Layout/LineLength:
- 'ee/spec/services/wiki_pages/create_service_spec.rb'
- 'ee/spec/services/wiki_pages/destroy_service_spec.rb'
- 'ee/spec/services/wiki_pages/update_service_spec.rb'
- - 'ee/spec/support/elastic.rb'
- 'ee/spec/support/features/redacted_search_results_examples.rb'
- 'ee/spec/support/helpers/search_results_helpers.rb'
- 'ee/spec/support/helpers/subscription_portal_helpers.rb'
@@ -2855,7 +2809,6 @@ Layout/LineLength:
- 'ee/spec/views/shared/billings/_eoa_bronze_plan_banner.html.haml_spec.rb'
- 'ee/spec/views/shared/billings/_trial_status.html.haml_spec.rb'
- 'ee/spec/views/shared/credentials_inventory/personal_access_tokens/_personal_access_token.html.haml_spec.rb'
- - 'ee/spec/views/shared/credentials_inventory/project_access_tokens/_project_access_token.html.haml_spec.rb'
- 'ee/spec/views/shared/promotions/_promotion_link_project.html.haml_spec.rb'
- 'ee/spec/workers/active_user_count_threshold_worker_spec.rb'
- 'ee/spec/workers/adjourned_group_deletion_worker_spec.rb'
@@ -2874,7 +2827,6 @@ Layout/LineLength:
- 'ee/spec/workers/elastic_association_indexer_worker_spec.rb'
- 'ee/spec/workers/elastic_commit_indexer_worker_spec.rb'
- 'ee/spec/workers/geo/batch/project_registry_scheduler_worker_spec.rb'
- - 'ee/spec/workers/geo/container_repository_sync_dispatch_worker_spec.rb'
- 'ee/spec/workers/geo/destroy_worker_spec.rb'
- 'ee/spec/workers/geo/project_sync_worker_spec.rb'
- 'ee/spec/workers/geo/prune_event_log_worker_spec.rb'
@@ -3014,7 +2966,6 @@ Layout/LineLength:
- 'lib/api/settings.rb'
- 'lib/api/snippet_repository_storage_moves.rb'
- 'lib/api/snippets.rb'
- - 'lib/api/submodules.rb'
- 'lib/api/suggestions.rb'
- 'lib/api/tags.rb'
- 'lib/api/templates.rb'
@@ -3112,7 +3063,6 @@ Layout/LineLength:
- 'lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings.rb'
- 'lib/gitlab/background_migration/reset_duplicate_ci_runners_token_encrypted_values_on_projects.rb'
- 'lib/gitlab/background_migration/update_vulnerability_occurrences_location.rb'
- - 'lib/gitlab/batch_pop_queueing.rb'
- 'lib/gitlab/bitbucket_import/importer.rb'
- 'lib/gitlab/bitbucket_server_import/importer.rb'
- 'lib/gitlab/buffered_io.rb'
@@ -3208,7 +3158,6 @@ Layout/LineLength:
- 'lib/gitlab/database/reindexing/coordinator.rb'
- 'lib/gitlab/database/reindexing/grafana_notifier.rb'
- 'lib/gitlab/database/reindexing/reindex_concurrently.rb'
- - 'lib/gitlab/database/schema_cleaner.rb'
- 'lib/gitlab/database/schema_migrations/context.rb'
- 'lib/gitlab/database/similarity_score.rb'
- 'lib/gitlab/database/with_lock_retries.rb'
@@ -3239,8 +3188,6 @@ Layout/LineLength:
- 'lib/gitlab/event_store/event.rb'
- 'lib/gitlab/event_store/store.rb'
- 'lib/gitlab/event_store/subscription.rb'
- - 'lib/gitlab/experimentation.rb'
- - 'lib/gitlab/experimentation/controller_concern.rb'
- 'lib/gitlab/external_authorization/client.rb'
- 'lib/gitlab/faraday/error_callback.rb'
- 'lib/gitlab/file_detector.rb'
@@ -3255,7 +3202,6 @@ Layout/LineLength:
- 'lib/gitlab/git/repository.rb'
- 'lib/gitlab/git/rugged_impl/repository.rb'
- 'lib/gitlab/git/user.rb'
- - 'lib/gitlab/git/wiki.rb'
- 'lib/gitlab/git_access.rb'
- 'lib/gitlab/git_access_project.rb'
- 'lib/gitlab/git_access_snippet.rb'
@@ -3270,10 +3216,8 @@ Layout/LineLength:
- 'lib/gitlab/gitaly_client/remote_service.rb'
- 'lib/gitlab/gitaly_client/repository_service.rb'
- 'lib/gitlab/gitaly_client/server_service.rb'
- - 'lib/gitlab/gitaly_client/wiki_service.rb'
- 'lib/gitlab/github_import.rb'
- 'lib/gitlab/github_import/importer/pull_request_importer.rb'
- - 'lib/gitlab/github_import/issuable_finder.rb'
- 'lib/gitlab/github_import/parallel_scheduling.rb'
- 'lib/gitlab/gitlab_import/client.rb'
- 'lib/gitlab/gitlab_import/importer.rb'
@@ -3323,7 +3267,6 @@ Layout/LineLength:
- 'lib/gitlab/lograge/custom_options.rb'
- 'lib/gitlab/mail_room/authenticator.rb'
- 'lib/gitlab/markdown_cache/active_record/extension.rb'
- - 'lib/gitlab/merge_requests/commit_message_generator.rb'
- 'lib/gitlab/metrics/dashboard/importer.rb'
- 'lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb'
- 'lib/gitlab/metrics/dashboard/stages/cluster_endpoint_inserter.rb'
@@ -3342,7 +3285,6 @@ Layout/LineLength:
- 'lib/gitlab/middleware/read_only/controller.rb'
- 'lib/gitlab/middleware/speedscope.rb'
- 'lib/gitlab/object_hierarchy.rb'
- - 'lib/gitlab/octokit/middleware.rb'
- 'lib/gitlab/pagination/keyset/column_order_definition.rb'
- 'lib/gitlab/pagination/keyset/in_operator_optimization/order_by_columns.rb'
- 'lib/gitlab/pagination/keyset/in_operator_optimization/query_builder.rb'
@@ -3354,7 +3296,6 @@ Layout/LineLength:
- 'lib/gitlab/pagination/offset_pagination.rb'
- 'lib/gitlab/patch/database_config.rb'
- 'lib/gitlab/path_regex.rb'
- - 'lib/gitlab/profiler.rb'
- 'lib/gitlab/project_search_results.rb'
- 'lib/gitlab/project_template.rb'
- 'lib/gitlab/prometheus/queries/base_query.rb'
@@ -3382,7 +3323,6 @@ Layout/LineLength:
- 'lib/gitlab/sidekiq_config/worker_router.rb'
- 'lib/gitlab/sidekiq_daemon/memory_killer.rb'
- 'lib/gitlab/sidekiq_daemon/monitor.rb'
- - 'lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb'
- 'lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/deduplicates_when_scheduling.rb'
- 'lib/gitlab/sidekiq_middleware/server_metrics.rb'
- 'lib/gitlab/sidekiq_middleware/size_limiter/compressor.rb'
@@ -3470,7 +3410,6 @@ Layout/LineLength:
- 'qa/qa/ee/resource/license.rb'
- 'qa/qa/flow/sign_up.rb'
- 'qa/qa/git/repository.rb'
- - 'qa/qa/page/base.rb'
- 'qa/qa/page/component/ci_badge_link.rb'
- 'qa/qa/page/component/issuable/sidebar.rb'
- 'qa/qa/page/component/select2.rb'
@@ -3485,7 +3424,6 @@ Layout/LineLength:
- 'qa/qa/page/project/web_ide/edit.rb'
- 'qa/qa/resource/api_fabricator.rb'
- 'qa/qa/resource/file.rb'
- - 'qa/qa/resource/members.rb'
- 'qa/qa/resource/protected_branch.rb'
- 'qa/qa/resource/registry_repository.rb'
- 'qa/qa/resource/repository/push.rb'
@@ -3511,7 +3449,6 @@ Layout/LineLength:
- 'qa/qa/specs/features/api/1_manage/rate_limits_spec.rb'
- 'qa/qa/specs/features/api/1_manage/user_access_termination_spec.rb'
- 'qa/qa/specs/features/api/1_manage/users_spec.rb'
- - 'qa/qa/specs/features/api/3_create/integrations/webhook_events_spec.rb'
- 'qa/qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb'
- 'qa/qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb'
- 'qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb'
@@ -3532,7 +3469,6 @@ Layout/LineLength:
- 'qa/qa/specs/features/browser_ui/1_manage/project/create_project_badge_spec.rb'
- 'qa/qa/specs/features/browser_ui/1_manage/project/invite_group_to_project_spec.rb'
- 'qa/qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb'
- - 'qa/qa/specs/features/browser_ui/1_manage/user/user_access_termination_spec.rb'
- 'qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb'
- 'qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb'
- 'qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb'
@@ -3540,12 +3476,9 @@ Layout/LineLength:
- 'qa/qa/specs/features/browser_ui/2_plan/issue/export_as_csv_spec.rb'
- 'qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb'
- 'qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb'
- - 'qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb'
- 'qa/qa/specs/features/browser_ui/2_plan/issue/real_time_assignee_spec.rb'
- 'qa/qa/specs/features/browser_ui/2_plan/related_issues/related_issues_spec.rb'
- 'qa/qa/specs/features/browser_ui/2_plan/transient/comment_on_discussion_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/jira/jira_basic_integration_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/merge_request/merge_when_pipeline_succeeds_spec.rb'
@@ -3564,11 +3497,8 @@ Layout/LineLength:
- 'qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/repository/push_to_canary_gitaly_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb'
@@ -3587,11 +3517,6 @@ Layout/LineLength:
- 'qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/wiki/content_editor_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/wiki/project_based_content_creation_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/wiki/project_based_content_manipulation_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/wiki/project_based_list_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/wiki/project_based_page_deletion_spec.rb'
- 'qa/qa/specs/features/browser_ui/4_verify/ci_variable/pipeline_with_protected_variable_spec.rb'
- 'qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb'
- 'qa/qa/specs/features/browser_ui/4_verify/pipeline/include_local_config_file_paths_with_wildcard_spec.rb'
@@ -3622,7 +3547,6 @@ Layout/LineLength:
- 'qa/qa/specs/features/ee/api/2_plan/epics_milestone_dates_spec.rb'
- 'qa/qa/specs/features/ee/api/3_create/wiki/group_wiki_repository_storage_move_spec.rb'
- 'qa/qa/specs/features/ee/api/7_configure/kubernetes/kubernetes_agent_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/10_protect/policies_list_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/license/cloud_activation_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/license/license_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/free_trial_spec.rb'
@@ -3630,21 +3554,14 @@ Layout/LineLength:
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/purchase_storage_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/upgrade_group_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/user_registration_billing_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/13_secure/create_merge_request_with_secure_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/13_secure/enable_scanning_from_configuration_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/13_secure/license_compliance_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/13_secure/security_reports_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/13_secure/vulnerability_management_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_audit_logs_1_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_audit_logs_2_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_saml_enforced_sso_git_access_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_saml_enforced_sso_new_account_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_saml_non_enforced_sso_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/group/prevent_forking_outside_group_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/group/share_group_with_group_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/instance/instance_audit_logs_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/ldap/admin_ldap_sync_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/project/project_audit_logs_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/user/minimal_access_user_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/2_plan/burndown_chart/burndown_chart_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/2_plan/custom_email/custom_email_spec.rb'
@@ -3674,13 +3591,11 @@ Layout/LineLength:
- 'qa/qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_ssh_with_key_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/3_create/repository/push_rules_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/3_create/repository/restrict_push_protected_branch_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/4_verify/pipeline_status_on_operation_dashboard_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/4_verify/pipeline_subscription_with_group_owned_project_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/4_verify/transient/merge_trains_transient_bug_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/5_package/dependency_proxy_sso_spec.rb'
- 'qa/qa/specs/helpers/context_selector.rb'
- 'qa/qa/specs/parallel_runner.rb'
- - 'qa/qa/support/loglinking.rb'
- 'qa/qa/support/run.rb'
- 'qa/qa/support/ssh.rb'
- 'qa/qa/tools/delete_projects.rb'
@@ -3696,9 +3611,7 @@ Layout/LineLength:
- 'qa/spec/specs/helpers/context_selector_spec.rb'
- 'qa/spec/specs/helpers/quarantine_spec.rb'
- 'qa/spec/specs/runner_spec.rb'
- - 'qa/spec/support/loglinking_spec.rb'
- 'qa/spec/support/page_error_checker_spec.rb'
- - 'qa/spec/support/repeater_spec.rb'
- 'qa/spec/support/run_spec.rb'
- 'qa/spec/support/ssh_spec.rb'
- 'rubocop/cop/active_model_errors_direct_manipulation.rb'
@@ -3710,14 +3623,12 @@ Layout/LineLength:
- 'rubocop/cop/migration/add_limit_to_text_columns.rb'
- 'rubocop/cop/migration/add_reference.rb'
- 'rubocop/cop/migration/prevent_global_enable_lock_retries_with_disable_ddl_transaction.rb'
- - 'rubocop/cop/migration/safer_boolean_column.rb'
- 'rubocop/cop/migration/versioned_migration_class.rb'
- 'rubocop/cop/migration/with_lock_retries_disallowed_method.rb'
- 'rubocop/cop/qa/selector_usage.rb'
- 'rubocop/cop/rspec/top_level_describe_path.rb'
- 'rubocop/cop/usage_data/large_table.rb'
- 'scripts/api/cancel_pipeline.rb'
- - 'scripts/api/download_job_artifact.rb'
- 'scripts/api/get_job_id.rb'
- 'scripts/changed-feature-flags'
- 'scripts/failed_tests.rb'
@@ -3728,7 +3639,6 @@ Layout/LineLength:
- 'scripts/perf/query_limiting_report.rb'
- 'scripts/pipeline_test_report_builder.rb'
- 'scripts/review_apps/automated_cleanup.rb'
- - 'scripts/rubocop-max-files-in-cache-check'
- 'scripts/security-harness'
- 'scripts/static-analysis'
- 'scripts/trigger-build.rb'
@@ -3737,18 +3647,14 @@ Layout/LineLength:
- 'sidekiq_cluster/sidekiq_cluster.rb'
- 'spec/benchmarks/banzai_benchmark.rb'
- 'spec/commands/sidekiq_cluster/cli_spec.rb'
- - 'spec/config/metrics/aggregates/aggregated_metrics_spec.rb'
- 'spec/config/settings_spec.rb'
- 'spec/controllers/admin/application_settings_controller_spec.rb'
- - 'spec/controllers/admin/groups_controller_spec.rb'
- 'spec/controllers/admin/impersonations_controller_spec.rb'
- 'spec/controllers/admin/projects_controller_spec.rb'
- 'spec/controllers/admin/users_controller_spec.rb'
- 'spec/controllers/application_controller_spec.rb'
- - 'spec/controllers/boards/issues_controller_spec.rb'
- 'spec/controllers/concerns/check_rate_limit_spec.rb'
- 'spec/controllers/concerns/confirm_email_warning_spec.rb'
- - 'spec/controllers/concerns/issuable_actions_spec.rb'
- 'spec/controllers/concerns/metrics_dashboard_spec.rb'
- 'spec/controllers/concerns/product_analytics_tracking_spec.rb'
- 'spec/controllers/concerns/send_file_upload_spec.rb'
@@ -3842,7 +3748,6 @@ Layout/LineLength:
- 'spec/db/schema_spec.rb'
- 'spec/deprecation_toolkit_env.rb'
- 'spec/experiments/concerns/project_commit_count_spec.rb'
- - 'spec/experiments/require_verification_for_namespace_creation_experiment_spec.rb'
- 'spec/factories/ci/builds.rb'
- 'spec/factories/ci/job_artifacts.rb'
- 'spec/factories/ci/pipelines.rb'
@@ -3932,7 +3837,6 @@ Layout/LineLength:
- 'spec/features/issues/user_creates_issue_spec.rb'
- 'spec/features/issues/user_edits_issue_spec.rb'
- 'spec/features/issues/user_interacts_with_awards_spec.rb'
- - 'spec/features/issues/user_sees_empty_state_spec.rb'
- 'spec/features/issues/user_sees_live_update_spec.rb'
- 'spec/features/issues/user_views_issue_spec.rb'
- 'spec/features/labels_hierarchy_spec.rb'
@@ -3993,7 +3897,6 @@ Layout/LineLength:
- 'spec/features/profiles/two_factor_auths_spec.rb'
- 'spec/features/profiles/user_edit_profile_spec.rb'
- 'spec/features/projects/artifacts/file_spec.rb'
- - 'spec/features/projects/artifacts/user_browses_artifacts_spec.rb'
- 'spec/features/projects/artifacts/user_downloads_artifacts_spec.rb'
- 'spec/features/projects/blobs/blob_line_permalink_updater_spec.rb'
- 'spec/features/projects/blobs/blob_show_spec.rb'
@@ -4055,7 +3958,6 @@ Layout/LineLength:
- 'spec/features/projects_spec.rb'
- 'spec/features/search/user_searches_for_comments_spec.rb'
- 'spec/features/search/user_searches_for_merge_requests_spec.rb'
- - 'spec/features/search/user_searches_for_projects_spec.rb'
- 'spec/features/search/user_uses_header_search_field_spec.rb'
- 'spec/features/security/project/internal_access_spec.rb'
- 'spec/features/security/project/public_access_spec.rb'
@@ -4152,7 +4054,6 @@ Layout/LineLength:
- 'spec/graphql/mutations/issues/set_escalation_status_spec.rb'
- 'spec/graphql/mutations/issues/update_spec.rb'
- 'spec/graphql/mutations/merge_requests/set_labels_spec.rb'
- - 'spec/graphql/mutations/namespace/package_settings/update_spec.rb'
- 'spec/graphql/mutations/release_asset_links/update_spec.rb'
- 'spec/graphql/mutations/releases/delete_spec.rb'
- 'spec/graphql/mutations/releases/update_spec.rb'
@@ -4437,7 +4338,6 @@ Layout/LineLength:
- 'spec/lib/gitlab/background_migration/update_timelogs_project_id_spec.rb'
- 'spec/lib/gitlab/background_migration/update_users_where_two_factor_auth_required_from_group_spec.rb'
- 'spec/lib/gitlab/bare_repository_import/importer_spec.rb'
- - 'spec/lib/gitlab/batch_pop_queueing_spec.rb'
- 'spec/lib/gitlab/bitbucket_import/importer_spec.rb'
- 'spec/lib/gitlab/bitbucket_server_import/importer_spec.rb'
- 'spec/lib/gitlab/buffered_io_spec.rb'
@@ -4473,7 +4373,6 @@ Layout/LineLength:
- 'spec/lib/gitlab/ci/parsers/coverage/cobertura_spec.rb'
- 'spec/lib/gitlab/ci/parsers/coverage/sax_document_spec.rb'
- 'spec/lib/gitlab/ci/parsers/security/common_spec.rb'
- - 'spec/lib/gitlab/ci/parsers/security/sast_spec.rb'
- 'spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb'
- 'spec/lib/gitlab/ci/parsers/test/junit_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb'
@@ -4500,7 +4399,6 @@ Layout/LineLength:
- 'spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb'
- 'spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb'
- 'spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb'
- - 'spec/lib/gitlab/ci/templates/npm_spec.rb'
- 'spec/lib/gitlab/ci/variables/builder_spec.rb'
- 'spec/lib/gitlab/ci/yaml_processor_spec.rb'
- 'spec/lib/gitlab/cleanup/orphan_lfs_file_references_spec.rb'
@@ -4607,13 +4505,11 @@ Layout/LineLength:
- 'spec/lib/gitlab/etag_caching/router/graphql_spec.rb'
- 'spec/lib/gitlab/etag_caching/router/rails_spec.rb'
- 'spec/lib/gitlab/exception_log_formatter_spec.rb'
- - 'spec/lib/gitlab/experimentation/controller_concern_spec.rb'
- 'spec/lib/gitlab/form_builders/gitlab_ui_form_builder_spec.rb'
- 'spec/lib/gitlab/git/base_error_spec.rb'
- 'spec/lib/gitlab/git/commit_spec.rb'
- 'spec/lib/gitlab/git/compare_spec.rb'
- 'spec/lib/gitlab/git/conflict/file_spec.rb'
- - 'spec/lib/gitlab/git/cross_repo_comparer_spec.rb'
- 'spec/lib/gitlab/git/diff_spec.rb'
- 'spec/lib/gitlab/git/raw_diff_change_spec.rb'
- 'spec/lib/gitlab/git/remote_mirror_spec.rb'
@@ -4681,7 +4577,6 @@ Layout/LineLength:
- 'spec/lib/gitlab/import_export/uploads_manager_spec.rb'
- 'spec/lib/gitlab/import_export/version_checker_spec.rb'
- 'spec/lib/gitlab/import_sources_spec.rb'
- - 'spec/lib/gitlab/incoming_email_spec.rb'
- 'spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb'
- 'spec/lib/gitlab/issuable_metadata_spec.rb'
- 'spec/lib/gitlab/issues/rebalancing/state_spec.rb'
@@ -4706,7 +4601,6 @@ Layout/LineLength:
- 'spec/lib/gitlab/legacy_github_import/pull_request_formatter_spec.rb'
- 'spec/lib/gitlab/lfs/client_spec.rb'
- 'spec/lib/gitlab/mail_room/authenticator_spec.rb'
- - 'spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb'
- 'spec/lib/gitlab/metrics/background_transaction_spec.rb'
- 'spec/lib/gitlab/metrics/boot_time_tracker_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/finder_spec.rb'
@@ -4804,8 +4698,6 @@ Layout/LineLength:
- 'spec/lib/gitlab/usage/metrics/instrumentations/redis_hll_metric_spec.rb'
- 'spec/lib/gitlab/usage/metrics/name_suggestion_spec.rb'
- 'spec/lib/gitlab/usage/metrics/names_suggestions/generator_spec.rb'
- - 'spec/lib/gitlab/usage/metrics/names_suggestions/relation_parsers/constraints_spec.rb'
- - 'spec/lib/gitlab/usage/metrics/names_suggestions/relation_parsers/joins_spec.rb'
- 'spec/lib/gitlab/usage/metrics/query_spec.rb'
- 'spec/lib/gitlab/usage/service_ping/payload_keys_processor_spec.rb'
- 'spec/lib/gitlab/usage/service_ping_report_spec.rb'
@@ -4968,7 +4860,6 @@ Layout/LineLength:
- 'spec/models/concerns/bulk_insert_safe_spec.rb'
- 'spec/models/concerns/cache_markdown_field_spec.rb'
- 'spec/models/concerns/cacheable_attributes_spec.rb'
- - 'spec/models/concerns/cascading_namespace_setting_attribute_spec.rb'
- 'spec/models/concerns/ci/artifactable_spec.rb'
- 'spec/models/concerns/clusters/agents/authorization_config_scopes_spec.rb'
- 'spec/models/concerns/deployment_platform_spec.rb'
@@ -5005,7 +4896,6 @@ Layout/LineLength:
- 'spec/models/environment_spec.rb'
- 'spec/models/error_tracking/error_spec.rb'
- 'spec/models/event_spec.rb'
- - 'spec/models/experiment_spec.rb'
- 'spec/models/gpg_key_spec.rb'
- 'spec/models/grafana_integration_spec.rb'
- 'spec/models/group_deploy_key_spec.rb'
@@ -5031,9 +4921,7 @@ Layout/LineLength:
- 'spec/models/integrations/drone_ci_spec.rb'
- 'spec/models/integrations/emails_on_push_spec.rb'
- 'spec/models/integrations/jira_spec.rb'
- - 'spec/models/integrations/packagist_spec.rb'
- 'spec/models/integrations/prometheus_spec.rb'
- - 'spec/models/integrations/slack_spec.rb'
- 'spec/models/issue_spec.rb'
- 'spec/models/jira_import_state_spec.rb'
- 'spec/models/key_spec.rb'
@@ -5239,7 +5127,6 @@ Layout/LineLength:
- 'spec/requests/api/graphql/project/cluster_agents_spec.rb'
- 'spec/requests/api/graphql/project/container_repositories_spec.rb'
- 'spec/requests/api/graphql/project/issue/designs/designs_spec.rb'
- - 'spec/requests/api/graphql/project/issues_spec.rb'
- 'spec/requests/api/graphql/project/jira_import_spec.rb'
- 'spec/requests/api/graphql/project/jobs_spec.rb'
- 'spec/requests/api/graphql/project/milestones_spec.rb'
@@ -5379,8 +5266,6 @@ Layout/LineLength:
- 'spec/serializers/context_commits_diff_entity_spec.rb'
- 'spec/serializers/diff_file_base_entity_spec.rb'
- 'spec/serializers/diff_file_entity_spec.rb'
- - 'spec/serializers/diffs_entity_spec.rb'
- - 'spec/serializers/diffs_metadata_entity_spec.rb'
- 'spec/serializers/discussion_entity_spec.rb'
- 'spec/serializers/environment_entity_spec.rb'
- 'spec/serializers/environment_serializer_spec.rb'
@@ -5398,7 +5283,6 @@ Layout/LineLength:
- 'spec/serializers/merge_request_poll_widget_entity_spec.rb'
- 'spec/serializers/merge_request_serializer_spec.rb'
- 'spec/serializers/merge_request_widget_entity_spec.rb'
- - 'spec/serializers/paginated_diff_entity_spec.rb'
- 'spec/serializers/pipeline_serializer_spec.rb'
- 'spec/serializers/review_app_setup_entity_spec.rb'
- 'spec/services/alert_management/alerts/update_service_spec.rb'
@@ -5436,15 +5320,10 @@ Layout/LineLength:
- 'spec/services/ci/test_failure_history_service_spec.rb'
- 'spec/services/ci/unlock_artifacts_service_spec.rb'
- 'spec/services/ci/update_pending_build_service_spec.rb'
- - 'spec/services/clusters/applications/check_installation_progress_service_spec.rb'
- - 'spec/services/clusters/applications/check_uninstall_progress_service_spec.rb'
- - 'spec/services/clusters/applications/create_service_spec.rb'
- - 'spec/services/clusters/applications/uninstall_service_spec.rb'
- 'spec/services/clusters/aws/fetch_credentials_service_spec.rb'
- 'spec/services/clusters/aws/provision_service_spec.rb'
- 'spec/services/clusters/create_service_spec.rb'
- 'spec/services/clusters/integrations/prometheus_health_check_service_spec.rb'
- - 'spec/services/clusters/kubernetes/configure_istio_ingress_service_spec.rb'
- 'spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb'
- 'spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb'
- 'spec/services/clusters/management/validate_management_project_permissions_service_spec.rb'
@@ -5493,7 +5372,6 @@ Layout/LineLength:
- 'spec/services/import/gitlab_projects/file_acquisition_strategies/remote_file_spec.rb'
- 'spec/services/import/validate_remote_git_endpoint_service_spec.rb'
- 'spec/services/incident_management/incidents/create_service_spec.rb'
- - 'spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb'
- 'spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb'
- 'spec/services/incident_management/pager_duty/process_webhook_service_spec.rb'
- 'spec/services/integrations/propagate_service_spec.rb'
@@ -5563,7 +5441,6 @@ Layout/LineLength:
- 'spec/services/milestones/transfer_service_spec.rb'
- 'spec/services/namespace_settings/update_service_spec.rb'
- 'spec/services/namespaces/in_product_marketing_emails_service_spec.rb'
- - 'spec/services/namespaces/package_settings/update_service_spec.rb'
- 'spec/services/notes/build_service_spec.rb'
- 'spec/services/notes/copy_service_spec.rb'
- 'spec/services/notes/create_service_spec.rb'
@@ -5599,7 +5476,6 @@ Layout/LineLength:
- 'spec/services/pages/migrate_from_legacy_storage_service_spec.rb'
- 'spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb'
- 'spec/services/personal_access_tokens/create_service_spec.rb'
- - 'spec/services/personal_access_tokens/revoke_service_spec.rb'
- 'spec/services/post_receive_service_spec.rb'
- 'spec/services/projects/apple_target_platform_detector_service_spec.rb'
- 'spec/services/projects/autocomplete_service_spec.rb'
@@ -5666,11 +5542,9 @@ Layout/LineLength:
- 'spec/services/users/approve_service_spec.rb'
- 'spec/services/users/ban_service_spec.rb'
- 'spec/services/users/create_service_spec.rb'
- - 'spec/services/users/migrate_to_ghost_user_service_spec.rb'
- 'spec/services/users/reject_service_spec.rb'
- 'spec/services/users/unban_service_spec.rb'
- 'spec/services/users/upsert_credit_card_validation_service_spec.rb'
- - 'spec/services/web_hook_service_spec.rb'
- 'spec/services/web_hooks/log_execution_service_spec.rb'
- 'spec/services/work_items/create_and_link_service_spec.rb'
- 'spec/services/work_items/create_from_task_service_spec.rb'
@@ -5683,7 +5557,6 @@ Layout/LineLength:
- 'spec/support/atlassian/jira_connect/schemata.rb'
- 'spec/support/capybara.rb'
- 'spec/support/cycle_analytics_helpers/test_generation.rb'
- - 'spec/support/database/multiple_databases.rb'
- 'spec/support/database/prevent_cross_database_modification.rb'
- 'spec/support/database/prevent_cross_joins.rb'
- 'spec/support/db_cleaner.rb'
@@ -5866,7 +5739,6 @@ Layout/LineLength:
- 'spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb'
- 'spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb'
- 'spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb'
- - 'spec/support/shared_examples/services/packages_shared_examples.rb'
- 'spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb'
- 'spec/support/shared_examples/services/rate_limited_service_shared_examples.rb'
- 'spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb'
@@ -5879,7 +5751,6 @@ Layout/LineLength:
- 'spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb'
- 'spec/support/shared_examples/workers/gitlab/jira_import/jira_import_workers_shared_examples.rb'
- 'spec/support/shared_examples/workers/project_export_shared_examples.rb'
- - 'spec/support_specs/database/multiple_databases_spec.rb'
- 'spec/support_specs/database/prevent_cross_joins_spec.rb'
- 'spec/support_specs/helpers/active_record/query_recorder_spec.rb'
- 'spec/support_specs/helpers/graphql_helpers_spec.rb'
@@ -5918,11 +5789,9 @@ Layout/LineLength:
- 'spec/tooling/rspec_flaky/flaky_examples_collection_spec.rb'
- 'spec/tooling/rspec_flaky/report_spec.rb'
- 'spec/uploaders/ci/pipeline_artifact_uploader_spec.rb'
- - 'spec/uploaders/external_diff_uploader_spec.rb'
- 'spec/uploaders/file_mover_spec.rb'
- 'spec/uploaders/file_uploader_spec.rb'
- 'spec/uploaders/job_artifact_uploader_spec.rb'
- - 'spec/uploaders/lfs_object_uploader_spec.rb'
- 'spec/uploaders/namespace_file_uploader_spec.rb'
- 'spec/uploaders/object_storage_spec.rb'
- 'spec/uploaders/pages/deployment_uploader_spec.rb'
@@ -5942,7 +5811,6 @@ Layout/LineLength:
- 'spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb'
- 'spec/views/notify/push_to_merge_request_email.text.haml_spec.rb'
- 'spec/views/profiles/keys/_form.html.haml_spec.rb'
- - 'spec/views/projects/artifacts/_artifact.html.haml_spec.rb'
- 'spec/views/projects/commits/_commit.html.haml_spec.rb'
- 'spec/views/projects/imports/new.html.haml_spec.rb'
- 'spec/views/projects/jobs/_build.html.haml_spec.rb'
@@ -5977,7 +5845,6 @@ Layout/LineLength:
- 'spec/workers/ci/pipeline_success_unlock_artifacts_worker_spec.rb'
- 'spec/workers/ci/ref_delete_unlock_artifacts_worker_spec.rb'
- 'spec/workers/ci/resource_groups/assign_resource_from_resource_group_worker_spec.rb'
- - 'spec/workers/cluster_wait_for_app_update_worker_spec.rb'
- 'spec/workers/clusters/integrations/check_prometheus_health_worker_spec.rb'
- 'spec/workers/concerns/application_worker_spec.rb'
- 'spec/workers/concerns/project_import_options_spec.rb'
diff --git a/.rubocop_todo/layout/multiline_operation_indentation.yml b/.rubocop_todo/layout/multiline_operation_indentation.yml
index cdfa560ef2e..a000ec0dcda 100644
--- a/.rubocop_todo/layout/multiline_operation_indentation.yml
+++ b/.rubocop_todo/layout/multiline_operation_indentation.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Layout/MultilineOperationIndentation:
Exclude:
- 'app/components/pajamas/concerns/checkbox_radio_label_with_help_text.rb'
@@ -16,43 +16,36 @@ Layout/MultilineOperationIndentation:
- 'app/helpers/whats_new_helper.rb'
- 'app/models/concerns/admin_changed_password_notifier.rb'
- 'app/models/integrations/prometheus.rb'
- - 'app/models/merge_request_diff_file.rb'
- 'app/models/namespaces/traversal/linear_scopes.rb'
- 'app/models/packages/conan/metadatum.rb'
- 'app/models/packages/sem_ver.rb'
- 'app/models/project.rb'
- - 'app/models/project_statistics.rb'
- 'app/models/user.rb'
- 'app/models/work_items/parent_link.rb'
- 'app/policies/project_policy.rb'
- 'app/serializers/deploy_keys/deploy_key_entity.rb'
- 'app/services/ci/create_downstream_pipeline_service.rb'
- - 'app/services/ci/create_pipeline_service.rb'
- 'app/services/git/branch_hooks_service.rb'
- 'app/services/groups/transfer_service.rb'
- 'app/services/issues/update_service.rb'
- 'app/services/labels/promote_service.rb'
- 'app/services/labels/transfer_service.rb'
- 'app/services/members/approve_access_request_service.rb'
- - 'app/services/projects/container_repository/cleanup_tags_service.rb'
- 'app/services/webauthn/authenticate_service.rb'
- 'app/validators/feature_flag_strategies_validator.rb'
- 'app/workers/container_expiration_policies/cleanup_container_repository_worker.rb'
- 'app/workers/container_registry/migration/guard_worker.rb'
- 'config/initializers/devise_dynamic_password_length_validation.rb'
- 'danger/utility_css/Dangerfile'
- - 'ee/app/controllers/ee/admin/application_settings_controller.rb'
- 'ee/app/controllers/projects/integrations/jira/issues_controller.rb'
- 'ee/app/controllers/smartcard_controller.rb'
- 'ee/app/graphql/resolvers/boards/epic_lists_resolver.rb'
- 'ee/app/helpers/ee/application_settings_helper.rb'
- 'ee/app/helpers/ee/boards_helper.rb'
- 'ee/app/helpers/ee/groups/group_members_helper.rb'
- - 'ee/app/helpers/ee/groups/reporting_helper.rb'
- 'ee/app/helpers/ee/projects/project_members_helper.rb'
- 'ee/app/helpers/groups/security_features_helper.rb'
- 'ee/app/helpers/groups/sso_helper.rb'
- - 'ee/app/models/approval_project_rule.rb'
- 'ee/app/models/concerns/ee/issuable.rb'
- 'ee/app/models/ee/namespace.rb'
- 'ee/app/models/ee/project.rb'
@@ -68,52 +61,9 @@ Layout/MultilineOperationIndentation:
- 'ee/lib/ee/api/geo.rb'
- 'ee/lib/ee/api/helpers.rb'
- 'ee/lib/ee/api/settings.rb'
- - 'ee/lib/ee/gitlab/git_access_project.rb'
- 'ee/lib/ee/gitlab/middleware/read_only/controller.rb'
- 'ee/lib/ee/gitlab/quick_actions/issue_actions.rb'
- 'ee/lib/ee/sidebars/projects/menus/ci_cd_menu.rb'
- 'ee/lib/ee/sidebars/projects/menus/issues_menu.rb'
- - 'ee/lib/gitlab/incident_management.rb'
- 'ee/lib/sidebars/groups/menus/analytics_menu.rb'
- - 'ee/lib/sidebars/groups/menus/security_compliance_menu.rb'
- 'ee/spec/services/ci/create_pipeline_service/dast_configuration_spec.rb'
- - 'lib/api/maven_packages.rb'
- - 'lib/api/users.rb'
- - 'lib/api/validations/validators/array_none_any.rb'
- - 'lib/gitlab/auth/o_auth/user.rb'
- - 'lib/gitlab/ci/reports/security/finding_key.rb'
- - 'lib/gitlab/database/load_balancing/connection_proxy.rb'
- - 'lib/gitlab/database/query_analyzers/prevent_cross_database_modification.rb'
- - 'lib/gitlab/error_tracking/error_repository/open_api_strategy.rb'
- - 'lib/gitlab/git_access.rb'
- - 'lib/gitlab/gl_repository/repo_type.rb'
- - 'lib/gitlab/jwt_token.rb'
- - 'lib/gitlab/kubernetes/helm/v2/install_command.rb'
- - 'lib/gitlab/kubernetes/helm/v2/patch_command.rb'
- - 'lib/gitlab/kubernetes/helm/v3/install_command.rb'
- - 'lib/gitlab/kubernetes/helm/v3/patch_command.rb'
- - 'lib/gitlab/pagination/cursor_based_keyset.rb'
- - 'lib/gitlab/quick_actions/issue_and_merge_request_actions.rb'
- - 'lib/gitlab/rack_attack/request.rb'
- - 'lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb'
- - 'lib/gitlab/sidekiq_status.rb'
- - 'lib/gitlab/x509/signature.rb'
- - 'lib/gitlab_edition.rb'
- - 'lib/kramdown/converter/commonmark.rb'
- - 'lib/sidebars/groups/menus/packages_registries_menu.rb'
- - 'lib/sidebars/projects/menus/analytics_menu.rb'
- - 'lib/sidebars/projects/menus/deployments_menu.rb'
- - 'lib/sidebars/projects/menus/hidden_menu.rb'
- - 'lib/sidebars/projects/menus/monitor_menu.rb'
- - 'qa/qa/ee/page/group/roadmap.rb'
- - 'qa/qa/page/component/snippet.rb'
- - 'qa/qa/runtime/api/repository_storage_moves.rb'
- - 'rubocop/cop/gitlab/keys_first_and_values_first.rb'
- - 'rubocop/migration_helpers.rb'
- - 'spec/controllers/graphql_controller_spec.rb'
- - 'spec/frontend/fixtures/tabs.rb'
- - 'spec/lib/gitlab/ci/pipeline/seed/build_spec.rb'
- - 'spec/services/ci/create_pipeline_service_spec.rb'
- - 'spec/services/projects/import_export/export_service_spec.rb'
- - 'spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb'
- - 'spec/support/shared_examples/models/with_debian_distributions_shared_examples.rb'
diff --git a/.rubocop_todo/layout/space_around_operators.yml b/.rubocop_todo/layout/space_around_operators.yml
index 9f6c0145723..1ddce55213e 100644
--- a/.rubocop_todo/layout/space_around_operators.yml
+++ b/.rubocop_todo/layout/space_around_operators.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Layout/SpaceAroundOperators:
Exclude:
- 'lib/gitlab/utils/strong_memoize.rb'
diff --git a/.rubocop_todo/layout/space_before_block_braces.yml b/.rubocop_todo/layout/space_before_block_braces.yml
index 3be90947fee..ff99ad7187a 100644
--- a/.rubocop_todo/layout/space_before_block_braces.yml
+++ b/.rubocop_todo/layout/space_before_block_braces.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Layout/SpaceBeforeBlockBraces:
Exclude:
- 'app/helpers/colors_helper.rb'
diff --git a/.rubocop_todo/layout/space_in_lambda_literal.yml b/.rubocop_todo/layout/space_in_lambda_literal.yml
index f025680c209..73b8a354a58 100644
--- a/.rubocop_todo/layout/space_in_lambda_literal.yml
+++ b/.rubocop_todo/layout/space_in_lambda_literal.yml
@@ -1,29 +1,8 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Layout/SpaceInLambdaLiteral:
- # Offense count: 986
- # Temporarily disabled due to too many offenses
- Enabled: false
+ Details: grace period
Exclude:
- - 'app/controllers/concerns/issuable_actions.rb'
- - 'app/controllers/projects/ci/daily_build_group_report_results_controller.rb'
- - 'app/controllers/projects/merge_requests/diffs_controller.rb'
- - 'app/controllers/projects/merge_requests_controller.rb'
- - 'app/finders/releases/group_releases_finder.rb'
- - 'app/finders/user_recent_events_finder.rb'
- - 'app/graphql/types/permission_types/base_permission_type.rb'
- - 'app/models/abuse_report.rb'
- - 'app/models/alert_management/alert.rb'
- - 'app/models/alert_management/http_integration.rb'
- - 'app/models/analytics/cycle_analytics/aggregation.rb'
- - 'app/models/analytics/usage_trends/measurement.rb'
- - 'app/models/application_setting.rb'
- - 'app/models/audit_event.rb'
- - 'app/models/award_emoji.rb'
- - 'app/models/board_group_recent_visit.rb'
- - 'app/models/board_project_recent_visit.rb'
- - 'app/models/bulk_import.rb'
- - 'app/models/bulk_imports/entity.rb'
- 'app/models/bulk_imports/tracker.rb'
- 'app/models/ci/build.rb'
- 'app/models/ci/daily_build_group_report_result.rb'
@@ -47,7 +26,7 @@ Layout/SpaceInLambdaLiteral:
- 'app/models/commit_status.rb'
- 'app/models/concerns/analytics/cycle_analytics/stage.rb'
- 'app/models/concerns/analytics/cycle_analytics/stage_event_model.rb'
- - 'app/models/concerns/approvable_base.rb'
+ - 'app/models/concerns/approvable.rb'
- 'app/models/concerns/ci/artifactable.rb'
- 'app/models/concerns/ci/has_status.rb'
- 'app/models/concerns/ci/has_variable.rb'
@@ -55,6 +34,7 @@ Layout/SpaceInLambdaLiteral:
- 'app/models/concerns/has_unique_internal_users.rb'
- 'app/models/concerns/id_in_ordered.rb'
- 'app/models/concerns/incident_management/escalatable.rb'
+ - 'app/models/concerns/issuable.rb'
- 'app/models/concerns/mentionable.rb'
- 'app/models/concerns/milestoneable.rb'
- 'app/models/concerns/protected_ref_access.rb'
@@ -68,10 +48,14 @@ Layout/SpaceInLambdaLiteral:
- 'app/models/design_management/version.rb'
- 'app/models/diff_note.rb'
- 'app/models/environment.rb'
+ - 'app/models/error_tracking/client_key.rb'
- 'app/models/error_tracking/error.rb'
- 'app/models/external_pull_request.rb'
- 'app/models/group.rb'
+ - 'app/models/group_group_link.rb'
+ - 'app/models/incident_management/timeline_event_tag.rb'
- 'app/models/integration.rb'
+ - 'app/models/integrations/base_chat_notification.rb'
- 'app/models/integrations/datadog.rb'
- 'app/models/internal_id.rb'
- 'app/models/issue.rb'
@@ -107,6 +91,7 @@ Layout/SpaceInLambdaLiteral:
- 'app/models/project_feature_usage.rb'
- 'app/models/project_group_link.rb'
- 'app/models/project_statistics.rb'
+ - 'app/models/projects/import_export/relation_export.rb'
- 'app/models/projects/topic.rb'
- 'app/models/prometheus_alert.rb'
- 'app/models/prometheus_alert_event.rb'
@@ -116,14 +101,15 @@ Layout/SpaceInLambdaLiteral:
- 'app/models/release.rb'
- 'app/models/route.rb'
- 'app/models/service_desk_setting.rb'
- - 'app/models/snippet.rb'
- 'app/models/snippet_input_action.rb'
- 'app/models/terraform/state.rb'
- 'app/models/timelog.rb'
- 'app/models/todo.rb'
- 'app/models/user.rb'
- 'app/models/user_highest_role.rb'
+ - 'app/models/users/callout.rb'
- 'app/models/users/in_product_marketing_email.rb'
+ - 'app/models/users/phone_number_validation.rb'
- 'app/models/users_star_project.rb'
- 'app/models/zoom_meeting.rb'
- 'app/serializers/analytics/cycle_analytics/stage_entity.rb'
@@ -139,6 +125,7 @@ Layout/SpaceInLambdaLiteral:
- 'app/serializers/concerns/user_status_tooltip.rb'
- 'app/serializers/container_repository_entity.rb'
- 'app/serializers/container_tag_entity.rb'
+ - 'app/serializers/deploy_keys/basic_deploy_key_entity.rb'
- 'app/serializers/deployment_cluster_entity.rb'
- 'app/serializers/deployment_entity.rb'
- 'app/serializers/detailed_status_entity.rb'
@@ -160,6 +147,7 @@ Layout/SpaceInLambdaLiteral:
- 'app/serializers/merge_request_poll_cached_widget_entity.rb'
- 'app/serializers/merge_request_poll_widget_entity.rb'
- 'app/serializers/merge_request_widget_entity.rb'
+ - 'app/serializers/merge_requests/pipeline_entity.rb'
- 'app/serializers/note_entity.rb'
- 'app/serializers/project_note_entity.rb'
- 'app/serializers/review_app_setup_entity.rb'
@@ -173,13 +161,16 @@ Layout/SpaceInLambdaLiteral:
- 'app/services/ci/job_artifacts/expire_project_build_artifacts_service.rb'
- 'app/services/ci/register_job_service.rb'
- 'app/services/environments/auto_stop_service.rb'
+ - 'app/services/incident_management/timeline_events/create_service.rb'
- 'app/services/issues/export_csv_service.rb'
- 'app/services/issues/referenced_merge_requests_service.rb'
- 'app/services/jira_connect_subscriptions/create_service.rb'
- 'app/services/merge_requests/export_csv_service.rb'
+ - 'app/services/packages/mark_packages_for_destruction_service.rb'
- 'app/services/projects/fork_service.rb'
- 'app/services/resource_access_tokens/create_service.rb'
- 'app/workers/issues/reschedule_stuck_issue_rebalances_worker.rb'
+ - 'app/workers/process_commit_worker.rb'
- 'config/initializers/deprecations.rb'
- 'config/routes/project.rb'
- 'db/post_migrate/20210303121224_update_gitlab_subscriptions_start_at_post_eoa.rb'
@@ -196,27 +187,32 @@ Layout/SpaceInLambdaLiteral:
- 'ee/app/models/ci/minutes/project_monthly_usage.rb'
- 'ee/app/models/concerns/approval_rule_like.rb'
- 'ee/app/models/concerns/ee/protected_ref.rb'
+ - 'ee/app/models/concerns/filterable_by_test_reports.rb'
- 'ee/app/models/concerns/issue_widgets/acts_like_requirement.rb'
- 'ee/app/models/dast/profile.rb'
- 'ee/app/models/dast/profile_schedule.rb'
- - 'ee/app/models/dast/site_profile_secret_variable.rb'
- 'ee/app/models/dast_scanner_profile.rb'
- 'ee/app/models/dast_site_profile.rb'
- 'ee/app/models/dast_site_validation.rb'
- 'ee/app/models/dora/daily_metrics.rb'
+ - 'ee/app/models/ee/application_setting.rb'
- 'ee/app/models/ee/audit_event.rb'
- 'ee/app/models/ee/ci/build.rb'
- 'ee/app/models/ee/ci/job_artifact.rb'
+ - 'ee/app/models/ee/clusters/agent.rb'
- 'ee/app/models/ee/environment.rb'
- 'ee/app/models/ee/epic.rb'
- 'ee/app/models/ee/gpg_key.rb'
- 'ee/app/models/ee/group.rb'
+ - 'ee/app/models/ee/group_group_link.rb'
- 'ee/app/models/ee/issue.rb'
- 'ee/app/models/ee/iteration.rb'
- 'ee/app/models/ee/list.rb'
- 'ee/app/models/ee/member.rb'
- 'ee/app/models/ee/namespace.rb'
+ - 'ee/app/models/ee/namespace_setting.rb'
- 'ee/app/models/ee/project.rb'
+ - 'ee/app/models/ee/project_group_link.rb'
- 'ee/app/models/ee/release.rb'
- 'ee/app/models/ee/vulnerability.rb'
- 'ee/app/models/elasticsearch_indexed_namespace.rb'
@@ -232,6 +228,7 @@ Layout/SpaceInLambdaLiteral:
- 'ee/app/models/merge_request_block.rb'
- 'ee/app/models/merge_requests/compliance_violation.rb'
- 'ee/app/models/merge_train.rb'
+ - 'ee/app/models/namespaces/namespace_ban.rb'
- 'ee/app/models/requirements_management/requirement.rb'
- 'ee/app/models/resource_iteration_event.rb'
- 'ee/app/models/saml_group_link.rb'
@@ -241,6 +238,7 @@ Layout/SpaceInLambdaLiteral:
- 'ee/app/models/security/scan.rb'
- 'ee/app/models/security/training.rb'
- 'ee/app/models/security/training_provider.rb'
+ - 'ee/app/models/slack_integration.rb'
- 'ee/app/models/software_license.rb'
- 'ee/app/models/software_license_policy.rb'
- 'ee/app/models/vulnerabilities/external_issue_link.rb'
@@ -255,6 +253,7 @@ Layout/SpaceInLambdaLiteral:
- 'ee/app/models/vulnerabilities/read.rb'
- 'ee/app/models/vulnerabilities/remediation.rb'
- 'ee/app/models/vulnerabilities/scanner.rb'
+ - 'ee/app/models/vulnerabilities/statistic.rb'
- 'ee/app/serializers/blocking_merge_request_entity.rb'
- 'ee/app/serializers/clusters/environment_entity.rb'
- 'ee/app/serializers/dashboard_operations_project_entity.rb'
@@ -289,22 +288,26 @@ Layout/SpaceInLambdaLiteral:
- 'ee/app/services/user_permissions/export_service.rb'
- 'ee/app/services/vulnerability_exports/exporters/csv_service.rb'
- 'ee/app/workers/update_all_mirrors_worker.rb'
- - 'ee/lib/api/entities/iteration.rb'
- 'ee/lib/api/entities/pending_member.rb'
+ - 'ee/lib/api/ml/ai_assist.rb'
- 'ee/lib/ee/api/entities/ci/job_request/response.rb'
- 'ee/lib/ee/api/entities/epic.rb'
- - 'ee/lib/ee/api/entities/geo_node_status.rb'
- 'ee/lib/ee/api/entities/issue.rb'
- 'ee/lib/ee/api/entities/list.rb'
- 'ee/lib/ee/api/entities/member.rb'
- 'ee/lib/ee/api/entities/project_approval_rule.rb'
- 'ee/lib/ee/api/entities/user_basic.rb'
- 'ee/lib/ee/api/entities/vulnerability_issue_link.rb'
+ - 'ee/lib/ee/gitlab/background_migration/backfill_epic_cache_counts.rb'
+ - 'ee/lib/ee/gitlab/background_migration/delete_approval_rules_with_vulnerability.rb'
+ - 'ee/lib/ee/gitlab/background_migration/migrate_shared_vulnerability_scanners.rb'
+ - 'ee/lib/ee/gitlab/background_migration/populate_approval_merge_request_rules_with_security_orchestration.rb'
+ - 'ee/lib/ee/gitlab/background_migration/populate_approval_project_rules_with_security_orchestration.rb'
- 'ee/lib/ee/gitlab/background_migration/populate_resolved_on_default_branch_column.rb'
- - 'ee/lib/ee/gitlab/background_migration/populate_status_column_of_security_scans.rb'
- 'ee/lib/ee/gitlab/ci/config/entry/need.rb'
- 'ee/lib/ee/gitlab/event_store.rb'
- 'ee/lib/gitlab/ci/config/entry/vault/secret.rb'
+ - 'ee/lib/gitlab/insights/executors/dora_executor.rb'
- 'ee/spec/frontend/fixtures/dast_profiles.rb'
- 'ee/spec/lib/gitlab/ci/reports/security/locations/container_scanning_spec.rb'
- 'ee/spec/requests/api/merge_request_approval_rules_spec.rb'
@@ -318,7 +321,6 @@ Layout/SpaceInLambdaLiteral:
- 'lib/api/entities/ci/variable.rb'
- 'lib/api/entities/container_registry.rb'
- 'lib/api/entities/deploy_key.rb'
- - 'lib/api/entities/environment.rb'
- 'lib/api/entities/event.rb'
- 'lib/api/entities/group.rb'
- 'lib/api/entities/invitation.rb'
@@ -335,18 +337,27 @@ Layout/SpaceInLambdaLiteral:
- 'lib/api/files.rb'
- 'lib/api/helm_packages.rb'
- 'lib/api/helpers/caching.rb'
+ - 'lib/api/merge_requests.rb'
+ - 'lib/api/metadata.rb'
- 'lib/api/metrics/dashboard/annotations.rb'
+ - 'lib/api/ml/mlflow.rb'
- 'lib/api/releases.rb'
- - 'lib/api/repositories.rb'
- 'lib/api/settings.rb'
- 'lib/api/tags.rb'
+ - 'lib/api/unleash.rb'
- 'lib/api/users.rb'
- - 'lib/api/version.rb'
- 'lib/atlassian/jira_connect/serializers/author_entity.rb'
+ - 'lib/bulk_imports/groups/transformers/group_attributes_transformer.rb'
- 'lib/container_registry/base_client.rb'
- 'lib/container_registry/client.rb'
+ - 'lib/event_filter.rb'
- 'lib/file_size_validator.rb'
+ - 'lib/gitlab/analytics/date_filler.rb'
+ - 'lib/gitlab/background_migration/backfill_internal_on_notes.rb'
+ - 'lib/gitlab/background_migration/backfill_project_namespace_on_issues.rb'
+ - 'lib/gitlab/background_migration/backfill_work_item_type_id_for_issues.rb'
- 'lib/gitlab/background_migration/populate_latest_pipeline_ids.rb'
+ - 'lib/gitlab/background_migration/purge_stale_security_scans.rb'
- 'lib/gitlab/background_migration/remove_all_trace_expiration_dates.rb'
- 'lib/gitlab/cache/helpers.rb'
- 'lib/gitlab/ci/config/entry/inherit/default.rb'
@@ -356,29 +367,33 @@ Layout/SpaceInLambdaLiteral:
- 'lib/gitlab/ci/config/entry/policy.rb'
- 'lib/gitlab/ci/config/entry/product/parallel.rb'
- 'lib/gitlab/ci/config/entry/retry.rb'
+ - 'lib/gitlab/ci/config/entry/rules/rule/changes.rb'
- 'lib/gitlab/ci/config/entry/trigger.rb'
+ - 'lib/gitlab/ci/config/entry/variable.rb'
- 'lib/gitlab/database/background_migration_job.rb'
- 'lib/gitlab/email/handler/unsubscribe_handler.rb'
+ - 'lib/gitlab/event_store.rb'
- 'lib/gitlab/gl_repository.rb'
+ - 'lib/gitlab/health_checks/server.rb'
- 'lib/gitlab/import_export/import_failure_service.rb'
- - 'lib/gitlab/merge_requests/commit_message_generator.rb'
+ - 'lib/gitlab/merge_requests/message_generator.rb'
- 'lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics.rb'
- 'lib/gitlab/metrics/exporter/base_exporter.rb'
- 'lib/gitlab/visibility_level.rb'
- - 'rubocop/cop/rspec/modify_sidekiq_middleware.rb'
- - 'rubocop/cop/rspec/timecop_freeze.rb'
- - 'rubocop/cop/rspec/timecop_travel.rb'
- 'spec/deprecation_toolkit_env.rb'
- 'spec/features/admin/users/user_spec.rb'
- 'spec/helpers/namespaces_helper_spec.rb'
- 'spec/lib/backup/gitaly_backup_spec.rb'
- 'spec/lib/container_registry/client_spec.rb'
- 'spec/lib/csv_builder_spec.rb'
+ - 'spec/lib/gitlab/analytics/date_filler_spec.rb'
+ - 'spec/lib/gitlab/background_migration/batched_migration_job_spec.rb'
- 'spec/lib/gitlab/batch_worker_context_spec.rb'
- 'spec/lib/gitlab/config/entry/simplifiable_spec.rb'
- 'spec/lib/gitlab/database/consistency_spec.rb'
+ - 'spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb'
+ - 'spec/lib/gitlab/database/query_analyzers/gitlab_schemas_validate_connection_spec.rb'
- 'spec/lib/gitlab/event_store/store_spec.rb'
- - 'spec/lib/gitlab/graphql/markdown_field_spec.rb'
- 'spec/lib/gitlab/middleware/handle_ip_spoof_attack_error_spec.rb'
- 'spec/lib/gitlab/middleware/request_context_spec.rb'
- 'spec/lib/gitlab/pagination/keyset/in_operator_optimization/query_builder_spec.rb'
@@ -393,4 +408,5 @@ Layout/SpaceInLambdaLiteral:
- 'spec/models/concerns/uniquify_spec.rb'
- 'spec/models/merge_request_spec.rb'
- 'spec/support/shared_examples/lib/cache_helpers_shared_examples.rb'
+ - 'spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb'
- 'spec/workers/concerns/worker_context_spec.rb'
diff --git a/.rubocop_todo/layout/space_inside_block_braces.yml b/.rubocop_todo/layout/space_inside_block_braces.yml
index c775ad94e46..07cd163103e 100644
--- a/.rubocop_todo/layout/space_inside_block_braces.yml
+++ b/.rubocop_todo/layout/space_inside_block_braces.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Layout/SpaceInsideBlockBraces:
Exclude:
- 'spec/requests/api/groups_spec.rb'
diff --git a/.rubocop_todo/layout/space_inside_parens.yml b/.rubocop_todo/layout/space_inside_parens.yml
index e6238992619..69508e309e3 100644
--- a/.rubocop_todo/layout/space_inside_parens.yml
+++ b/.rubocop_todo/layout/space_inside_parens.yml
@@ -1,34 +1,43 @@
---
# Cop supports --autocorrect.
Layout/SpaceInsideParens:
- # Offense count: 701
- # Temporarily disabled due to too many offenses
- Enabled: false
+ Details: grace period
Exclude:
- - 'config/initializers/wikicloth_redos_patch.rb'
- 'db/post_migrate/20210722042939_update_issuable_slas_where_issue_closed.rb'
+ - 'ee/app/models/ee/dependency_proxy/blob.rb'
+ - 'ee/app/models/ee/dependency_proxy/manifest.rb'
+ - 'ee/app/services/gitlab_subscriptions/notify_seats_exceeded_batch_service.rb'
- 'ee/lib/ee/gitlab/auth/ldap/access.rb'
- 'ee/lib/gitlab/auth/smartcard/session.rb'
+ - 'ee/lib/system_check/geo/current_node_check.rb'
+ - 'ee/spec/controllers/projects/mirrors_controller_spec.rb'
+ - 'ee/spec/features/groups/saml_enforcement_spec.rb'
- 'ee/spec/finders/ee/alert_management/http_integrations_finder_spec.rb'
- 'ee/spec/finders/epics_finder_spec.rb'
- 'ee/spec/finders/security/pipeline_vulnerabilities_finder_spec.rb'
+ - 'ee/spec/finders/security/vulnerability_feedbacks_finder_spec.rb'
- 'ee/spec/frontend/fixtures/analytics/devops_reports/devops_adoption/enabled_namespaces.rb'
- 'ee/spec/frontend/fixtures/epic.rb'
+ - 'ee/spec/frontend/fixtures/namespace.rb'
- 'ee/spec/frontend/fixtures/projects.rb'
+ - 'ee/spec/lib/gitlab/search/index_curator_spec.rb'
+ - 'ee/spec/lib/world_spec.rb'
- 'ee/spec/mailers/notify_spec.rb'
- 'ee/spec/migrations/add_non_null_constraint_for_escalation_rule_on_pending_alert_escalations_spec.rb'
- 'ee/spec/migrations/drop_invalid_remediations_spec.rb'
- 'ee/spec/models/allowed_email_domain_spec.rb'
+ - 'ee/spec/models/audit_events/external_audit_event_destination_spec.rb'
- 'ee/spec/models/boards/epic_board_position_spec.rb'
- 'ee/spec/models/dora/change_failure_rate_metric_spec.rb'
- 'ee/spec/models/ee/integrations/jira_spec.rb'
- 'ee/spec/models/ee/iteration_spec.rb'
+ - 'ee/spec/models/ee/iterations/cadence_spec.rb'
- 'ee/spec/models/ee/key_spec.rb'
+ - 'ee/spec/models/ee/project_setting_spec.rb'
- 'ee/spec/models/ee/system_note_metadata_spec.rb'
- 'ee/spec/models/geo/every_geo_event_spec.rb'
- 'ee/spec/models/incident_management/escalation_rule_spec.rb'
- 'ee/spec/models/ip_restriction_spec.rb'
- - 'ee/spec/models/issue_spec.rb'
- 'ee/spec/models/ldap_group_link_spec.rb'
- 'ee/spec/models/license_spec.rb'
- 'ee/spec/models/member_spec.rb'
@@ -39,6 +48,7 @@ Layout/SpaceInsideParens:
- 'ee/spec/requests/api/boards_spec.rb'
- 'ee/spec/requests/api/epics_spec.rb'
- 'ee/spec/requests/api/graphql/group/epics_spec.rb'
+ - 'ee/spec/requests/api/graphql/iteration_spec.rb'
- 'ee/spec/requests/api/graphql/mutations/iterations/update_spec.rb'
- 'ee/spec/requests/api/graphql/projects/compliance_frameworks_spec.rb'
- 'ee/spec/requests/api/group_boards_spec.rb'
@@ -51,48 +61,47 @@ Layout/SpaceInsideParens:
- 'ee/spec/services/app_sec/dast/site_profile_secret_variables/create_or_update_service_spec.rb'
- 'ee/spec/services/app_sec/dast/site_validations/runner_service_spec.rb'
- 'ee/spec/services/app_sec/fuzzing/coverage/corpuses/create_service_spec.rb'
+ - 'ee/spec/services/audit_events/streaming/headers/base_spec.rb'
+ - 'ee/spec/services/audit_events/streaming/headers/create_service_spec.rb'
+ - 'ee/spec/services/audit_events/streaming/headers/destroy_service_spec.rb'
+ - 'ee/spec/services/audit_events/streaming/headers/update_service_spec.rb'
- 'ee/spec/services/ci_cd/github_integration_setup_service_spec.rb'
- 'ee/spec/services/ci_cd/github_setup_service_spec.rb'
- 'ee/spec/services/ee/boards/issues/list_service_spec.rb'
- 'ee/spec/services/ee/notification_service_spec.rb'
+ - 'ee/spec/services/ee/todos/destroy/entity_leave_service_spec.rb'
- 'ee/spec/services/ee/users/update_service_spec.rb'
- 'ee/spec/services/epic_issues/update_service_spec.rb'
+ - 'ee/spec/services/epics/related_epic_links/destroy_service_spec.rb'
+ - 'ee/spec/services/epics/tree_reorder_service_spec.rb'
- 'ee/spec/services/geo/container_repository_sync_spec.rb'
- 'ee/spec/services/geo/replication_toggle_request_service_spec.rb'
- 'ee/spec/services/gitlab_subscriptions/create_service_spec.rb'
+ - 'ee/spec/services/groups/update_service_spec.rb'
+ - 'ee/spec/services/merge_requests/build_service_spec.rb'
- 'ee/spec/services/projects/update_mirror_service_spec.rb'
+ - 'ee/spec/services/search/global_service_spec.rb'
- 'ee/spec/services/search/group_service_spec.rb'
- 'ee/spec/services/search/project_service_spec.rb'
+ - 'ee/spec/services/security/ingestion/tasks/update_vulnerability_uuids_spec.rb'
- 'ee/spec/services/security/merge_reports_service_spec.rb'
- - 'ee/spec/services/vulnerability_exports/exporters/csv_service_spec.rb'
+ - 'ee/spec/support/shared_examples/finders/epics_findable_examples.rb'
+ - 'ee/spec/support/shared_examples/models/concerns/blob_replicator_strategy_shared_examples.rb'
- 'ee/spec/support/shared_examples/services/geo/geo_request_service_shared_examples.rb'
- 'ee/spec/workers/elastic/migration_worker_spec.rb'
- - 'ee/spec/workers/geo/container_repository_sync_dispatch_worker_spec.rb'
- 'ee/spec/workers/security/auto_fix_worker_spec.rb'
- - 'ee/spec/workers/security/create_orchestration_policy_worker_spec.rb'
- - 'lib/backup/files.rb'
- - 'lib/gitlab/ci/reports/security/finding.rb'
- - 'lib/gitlab/ci/runner_instructions.rb'
- - 'lib/gitlab/database/partitioning/single_numeric_list_partition.rb'
- - 'lib/gitlab/database/postgres_hll/buckets.rb'
- - 'lib/gitlab/diff/parser.rb'
- - 'lib/gitlab/diff/rendered/notebook/diff_file.rb'
- - 'lib/gitlab/gitaly_client/commit_service.rb'
- - 'lib/gitlab/prometheus_client.rb'
- - 'lib/gitlab/sidekiq_daemon/memory_killer.rb'
- - 'lib/gitlab/tracking/incident_management.rb'
- - 'lib/gitlab/visibility_level.rb'
- - 'lib/security/ci_configuration/sast_build_action.rb'
- 'qa/qa/page/group/settings/group_deploy_tokens.rb'
- - 'qa/qa/page/merge_request/show.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/10_govern/scan_result_policy_vulnerabilities_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/10_govern/vulnerability_management_spec.rb'
- 'qa/qa/tools/delete_subgroups.rb'
- 'qa/spec/runtime/feature_spec.rb'
- - 'qa/spec/scenario/template_spec.rb'
- 'spec/dependencies/omniauth_saml_spec.rb'
+ - 'spec/factories/ml/candidates.rb'
- 'spec/factories/usage_data.rb'
- 'spec/finders/alert_management/http_integrations_finder_spec.rb'
- 'spec/finders/events_finder_spec.rb'
- 'spec/finders/labels_finder_spec.rb'
+ - 'spec/helpers/application_helper_spec.rb'
- 'spec/helpers/badges_helper_spec.rb'
- 'spec/helpers/ci/builds_helper_spec.rb'
- 'spec/helpers/ci/runners_helper_spec.rb'
@@ -112,30 +121,33 @@ Layout/SpaceInsideParens:
- 'spec/lib/banzai/filter/repository_link_filter_spec.rb'
- 'spec/lib/bitbucket_server/representation/comment_spec.rb'
- 'spec/lib/bulk_imports/common/pipelines/lfs_objects_pipeline_spec.rb'
+ - 'spec/lib/bulk_imports/groups/pipelines/namespace_settings_pipeline_spec.rb'
- 'spec/lib/error_tracking/sentry_client/projects_spec.rb'
- 'spec/lib/error_tracking/sentry_client/repo_spec.rb'
+ - 'spec/lib/feature/gitaly_spec.rb'
- 'spec/lib/gitlab/app_text_logger_spec.rb'
- 'spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb'
- - 'spec/lib/gitlab/auth/o_auth/user_spec.rb'
- 'spec/lib/gitlab/auth/saml/auth_hash_spec.rb'
- 'spec/lib/gitlab/ci/build/image_spec.rb'
- 'spec/lib/gitlab/ci/config/entry/reports_spec.rb'
- 'spec/lib/gitlab/ci/config/entry/trigger_spec.rb'
+ - 'spec/lib/gitlab/ci/parsers/security/common_spec.rb'
- 'spec/lib/gitlab/ci/parsers_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/seed/build_spec.rb'
- 'spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb'
- 'spec/lib/gitlab/ci/reports/test_suite_spec.rb'
- 'spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb'
- 'spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/MATLAB_spec.rb'
- 'spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb'
- 'spec/lib/gitlab/ci/templates/Terraform/base_latest_gitlab_ci_yaml_spec.rb'
- 'spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb'
- 'spec/lib/gitlab/ci/templates/flutter_gitlab_ci_yaml_spec.rb'
- 'spec/lib/gitlab/ci/templates/kaniko_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/katalon_gitlab_ci_yaml_spec.rb'
- 'spec/lib/gitlab/ci/templates/npm_spec.rb'
- 'spec/lib/gitlab/ci/templates/terraform_gitlab_ci_yaml_spec.rb'
- 'spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb'
- - 'spec/lib/gitlab/database/background_migration/batched_job_spec.rb'
- 'spec/lib/gitlab/database/migrations/runner_spec.rb'
- 'spec/lib/gitlab/database/reindexing/reindex_concurrently_spec.rb'
- 'spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb'
@@ -146,9 +158,10 @@ Layout/SpaceInsideParens:
- 'spec/lib/gitlab/git/commit_spec.rb'
- 'spec/lib/gitlab/git/diff_spec.rb'
- 'spec/lib/gitlab/git/repository_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/pull_request_merged_by_importer_spec.rb'
- 'spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb'
+ - 'spec/lib/gitlab/grape_logging/loggers/token_logger_spec.rb'
- 'spec/lib/gitlab/graphql/lazy_spec.rb'
- - 'spec/lib/gitlab/graphql/markdown_field_spec.rb'
- 'spec/lib/gitlab/health_checks/simple_check_shared.rb'
- 'spec/lib/gitlab/highlight_spec.rb'
- 'spec/lib/gitlab/import_export/attributes_permitter_spec.rb'
@@ -156,11 +169,11 @@ Layout/SpaceInsideParens:
- 'spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb'
- 'spec/lib/gitlab/import_export/project/export_task_spec.rb'
- 'spec/lib/gitlab/import_export/project/tree_saver_spec.rb'
+ - 'spec/lib/gitlab/import_export/recursive_merge_folders_spec.rb'
- 'spec/lib/gitlab/issuables_count_for_state_spec.rb'
- 'spec/lib/gitlab/kubernetes/rollout_status_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/processor_spec.rb'
- 'spec/lib/gitlab/middleware/same_site_cookies_spec.rb'
- - 'spec/lib/gitlab/puma_logging/json_formatter_spec.rb'
- 'spec/lib/gitlab/rack_attack/instrumented_cache_store_spec.rb'
- 'spec/lib/gitlab/redis/cache_spec.rb'
- 'spec/lib/gitlab/redis/queues_spec.rb'
@@ -173,6 +186,7 @@ Layout/SpaceInsideParens:
- 'spec/lib/gitlab/usage_data_spec.rb'
- 'spec/lib/gitlab/utils/delegator_override/validator_spec.rb'
- 'spec/lib/gitlab/utils/usage_data_spec.rb'
+ - 'spec/lib/gitlab/x509/commit_spec.rb'
- 'spec/lib/security/ci_configuration/container_scanning_build_action_spec.rb'
- 'spec/lib/security/ci_configuration/sast_build_action_spec.rb'
- 'spec/lib/security/ci_configuration/sast_iac_build_action_spec.rb'
@@ -180,8 +194,12 @@ Layout/SpaceInsideParens:
- 'spec/mailers/emails/profile_spec.rb'
- 'spec/migrations/20211130165043_backfill_sequence_column_for_sprints_table_spec.rb'
- 'spec/migrations/backfill_issues_upvotes_count_spec.rb'
- - 'spec/migrations/schedule_copy_ci_builds_columns_to_security_scans2_spec.rb'
+ - 'spec/models/ci/pending_build_spec.rb'
+ - 'spec/models/ci/running_build_spec.rb'
+ - 'spec/models/ml/candidate_metric_spec.rb'
+ - 'spec/models/ml/candidate_spec.rb'
- 'spec/policies/clusters/agent_policy_spec.rb'
+ - 'spec/policies/user_policy_spec.rb'
- 'spec/presenters/ci/build_presenter_spec.rb'
- 'spec/presenters/packages/conan/package_presenter_spec.rb'
- 'spec/requests/jwt_controller_spec.rb'
@@ -190,16 +208,20 @@ Layout/SpaceInsideParens:
- 'spec/requests/projects/releases_controller_spec.rb'
- 'spec/requests/search_controller_spec.rb'
- 'spec/serializers/analytics_build_entity_spec.rb'
- - 'spec/serializers/merge_request_user_entity_spec.rb'
+ - 'spec/services/bulk_imports/create_service_spec.rb'
+ - 'spec/services/jira_connect_installations/proxy_lifecycle_event_service_spec.rb'
+ - 'spec/services/users/destroy_service_spec.rb'
- 'spec/support/helpers/database/partitioning_helpers.rb'
- 'spec/support/helpers/dependency_proxy_helpers.rb'
- 'spec/support/helpers/javascript_fixtures_helpers.rb'
+ - 'spec/support/helpers/kubernetes_helpers.rb'
- 'spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb'
- 'spec/support/shared_examples/ci/badge_template_shared_examples.rb'
- 'spec/support/shared_examples/controllers/destroy_hook_shared_examples.rb'
- 'spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb'
- 'spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb'
- 'spec/support/shared_examples/finders/packages/debian/distributions_finder_shared_examples.rb'
+ - 'spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb'
- 'spec/support/shared_examples/lib/gitlab/position_formatters_shared_examples.rb'
- 'spec/support/shared_examples/lib/gitlab/sidekiq_middleware/strategy_shared_examples.rb'
- 'spec/support/shared_examples/mailers/notify_shared_examples.rb'
@@ -210,7 +232,11 @@ Layout/SpaceInsideParens:
- 'spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb'
- 'spec/tasks/gitlab/db_rake_spec.rb'
- 'spec/validators/devise_email_validator_spec.rb'
+ - 'spec/views/dashboard/projects/_blank_state_welcome.html.haml_spec.rb'
+ - 'spec/views/profiles/keys/_form.html.haml_spec.rb'
+ - 'spec/views/search/_results.html.haml_spec.rb'
- 'spec/views/shared/runners/_runner_details.html.haml_spec.rb'
+ - 'spec/workers/concerns/gitlab/github_import/object_importer_spec.rb'
- 'spec/workers/gitlab/jira_import/stage/import_labels_worker_spec.rb'
- 'spec/workers/pipeline_schedule_worker_spec.rb'
- 'spec/workers/purge_dependency_proxy_cache_worker_spec.rb'
diff --git a/.rubocop_todo/layout/trailing_whitespace.yml b/.rubocop_todo/layout/trailing_whitespace.yml
index 8e3e0795c03..2cb6c81256c 100644
--- a/.rubocop_todo/layout/trailing_whitespace.yml
+++ b/.rubocop_todo/layout/trailing_whitespace.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Layout/TrailingWhitespace:
Exclude:
- 'app/models/concerns/analytics/cycle_analytics/stage_event_model.rb'
@@ -9,7 +9,6 @@ Layout/TrailingWhitespace:
- 'lib/gitlab/background_migration/fix_duplicate_project_name_and_path.rb'
- 'lib/gitlab/background_migration/populate_topics_non_private_projects_count.rb'
- 'lib/gitlab/pagination/keyset/sql_type_missing_error.rb'
- - 'qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb'
- 'spec/lib/banzai/filter/front_matter_filter_spec.rb'
- 'spec/services/suggestions/apply_service_spec.rb'
- 'spec/support/helpers/x509_helpers.rb'
diff --git a/.rubocop_todo/lint/constant_definition_in_block.yml b/.rubocop_todo/lint/constant_definition_in_block.yml
index fc70f4870b1..ff3f6b2afbc 100644
--- a/.rubocop_todo/lint/constant_definition_in_block.yml
+++ b/.rubocop_todo/lint/constant_definition_in_block.yml
@@ -1,8 +1,6 @@
---
Lint/ConstantDefinitionInBlock:
- # Offense count: 105
- # Temporarily disabled due to too many offenses
- Enabled: false
+ Details: grace period
Exclude:
- 'app/models/concerns/ignorable_columns.rb'
- 'app/models/concerns/partitioned_table.rb'
@@ -10,13 +8,16 @@ Lint/ConstantDefinitionInBlock:
- 'config/application.rb'
- 'config/initializers/direct_upload_support.rb'
- 'config/initializers/elastic_client_setup.rb'
+ - 'ee/app/graphql/ee/types/work_items/widget_interface.rb'
- 'ee/app/models/concerns/ee/issuable_link.rb'
- 'ee/app/models/ee/application_setting.rb'
- 'ee/app/models/ee/ci/job_artifact.rb'
- 'ee/app/models/ee/ci/pipeline.rb'
- 'ee/app/models/ee/epic.rb'
+ - 'ee/app/models/ee/group.rb'
- 'ee/app/models/ee/issue.rb'
- 'ee/app/models/ee/merge_request_diff.rb'
+ - 'ee/app/models/ee/namespace_setting.rb'
- 'ee/app/models/ee/plan.rb'
- 'ee/app/models/ee/project_import_state.rb'
- 'ee/app/models/ee/user.rb'
@@ -31,8 +32,13 @@ Lint/ConstantDefinitionInBlock:
- 'lib/gitlab/quick_actions/issue_actions.rb'
- 'lib/tasks/cache.rake'
- 'lib/tasks/dev.rake'
+ - 'lib/tasks/gitlab/backup.rake'
+ - 'lib/tasks/gitlab/db.rake'
+ - 'lib/tasks/gitlab/db/decomposition/rollback/bump_ci_sequences.rake'
+ - 'lib/tasks/gitlab/db/validate_config.rake'
- 'lib/tasks/gitlab/docs/compile_deprecations.rake'
- 'lib/tasks/gitlab/graphql.rake'
+ - 'lib/tasks/gitlab/metrics_exporter.rake'
- 'lib/tasks/gitlab/refresh_project_statistics_build_artifacts_size.rake'
- 'lib/tasks/gitlab/snippets.rake'
- 'lib/tasks/gitlab/tw/codeowners.rake'
@@ -47,5 +53,4 @@ Lint/ConstantDefinitionInBlock:
- 'spec/models/concerns/bulk_insertable_associations_spec.rb'
- 'spec/models/concerns/triggerable_hooks_spec.rb'
- 'spec/models/repository_spec.rb'
- - 'spec/services/clusters/applications/check_installation_progress_service_spec.rb'
- 'spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb'
diff --git a/.rubocop_todo/lint/missing_cop_enable_directive.yml b/.rubocop_todo/lint/missing_cop_enable_directive.yml
index 5e1421c0f3e..e0b2afe8256 100644
--- a/.rubocop_todo/lint/missing_cop_enable_directive.yml
+++ b/.rubocop_todo/lint/missing_cop_enable_directive.yml
@@ -147,7 +147,6 @@ Lint/MissingCopEnableDirective:
- 'ee/lib/api/scim.rb'
- 'ee/lib/ee/gitlab/background_migration/backfill_project_statistics_container_repository_size.rb'
- 'ee/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules.rb'
- - 'ee/lib/ee/gitlab/background_migration/purge_stale_security_scans.rb'
- 'ee/lib/ee/gitlab/usage_data.rb'
- 'ee/lib/gitlab/spdx/license.rb'
- 'ee/spec/controllers/projects/legacy_pipelines_controller_spec.rb'
@@ -189,7 +188,6 @@ Lint/MissingCopEnableDirective:
- 'lib/gitlab/github_import/client.rb'
- 'lib/gitlab/github_import/importer/diff_note_importer.rb'
- 'lib/gitlab/gon_helper.rb'
- - 'lib/gitlab/graphql/pagination/keyset/last_items.rb'
- 'lib/gitlab/graphql/standard_graphql_error.rb'
- 'lib/gitlab/metrics/methods.rb'
- 'lib/gitlab/patch/action_cable_redis_listener.rb'
diff --git a/.rubocop_todo/lint/mixed_regexp_capture_types.yml b/.rubocop_todo/lint/mixed_regexp_capture_types.yml
index 3dcb9427ef8..70f3773eb31 100644
--- a/.rubocop_todo/lint/mixed_regexp_capture_types.yml
+++ b/.rubocop_todo/lint/mixed_regexp_capture_types.yml
@@ -12,4 +12,4 @@ Lint/MixedRegexpCaptureTypes:
- 'lib/gitlab/metrics/system.rb'
- 'lib/gitlab/slash_commands/issue_move.rb'
- 'lib/gitlab/slash_commands/issue_new.rb'
- - 'lib/gitlab/slash_commands/run.rb' \ No newline at end of file
+ - 'lib/gitlab/slash_commands/run.rb'
diff --git a/.rubocop_todo/lint/redundant_cop_disable_directive.yml b/.rubocop_todo/lint/redundant_cop_disable_directive.yml
index b0b5697536e..a2dd002b54f 100644
--- a/.rubocop_todo/lint/redundant_cop_disable_directive.yml
+++ b/.rubocop_todo/lint/redundant_cop_disable_directive.yml
@@ -1,12 +1,12 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Lint/RedundantCopDisableDirective:
- # Used to be enabled in "grace period" and is now disabled due to too many
- # silenced offenses.
- # See https://gitlab.com/gitlab-org/gitlab/-/issues/376133
+ # Offense count: 357
+ # Temporarily disabled due to too many offenses
Enabled: false
Exclude:
- 'app/controllers/concerns/enforces_two_factor_authentication.rb'
+ - 'app/controllers/concerns/issuable_actions.rb'
- 'app/controllers/concerns/web_hooks/hook_log_actions.rb'
- 'app/controllers/groups/autocomplete_sources_controller.rb'
- 'app/controllers/groups/labels_controller.rb'
@@ -14,7 +14,6 @@ Lint/RedundantCopDisableDirective:
- 'app/finders/autocomplete/acts_as_taggable_on/tags_finder.rb'
- 'app/finders/autocomplete/move_to_project_finder.rb'
- 'app/finders/autocomplete/routes_finder.rb'
- - 'app/finders/autocomplete/users_finder.rb'
- 'app/finders/ci/daily_build_group_report_results_finder.rb'
- 'app/finders/ci/runner_jobs_finder.rb'
- 'app/finders/groups_finder.rb'
@@ -24,6 +23,7 @@ Lint/RedundantCopDisableDirective:
- 'app/graphql/resolvers/project_milestones_resolver.rb'
- 'app/graphql/types/base_enum.rb'
- 'app/graphql/types/ci/runner_web_url_edge.rb'
+ - 'app/graphql/types/commit_signatures/verification_status_enum.rb'
- 'app/graphql/types/packages/helm/dependency_type.rb'
- 'app/graphql/types/projects/service_type_enum.rb'
- 'app/helpers/diff_helper.rb'
@@ -34,6 +34,7 @@ Lint/RedundantCopDisableDirective:
- 'app/models/concerns/from_except.rb'
- 'app/models/concerns/from_intersect.rb'
- 'app/models/concerns/from_union.rb'
+ - 'app/models/concerns/redis_cacheable.rb'
- 'app/models/project_statistics.rb'
- 'app/models/user.rb'
- 'app/presenters/dev_ops_report/metric_presenter.rb'
@@ -43,7 +44,9 @@ Lint/RedundantCopDisableDirective:
- 'app/services/ci/job_artifacts/destroy_batch_service.rb'
- 'app/services/ci/register_job_service.rb'
- 'app/services/ci/retry_job_service.rb'
+ - 'app/services/ci/runners/bulk_delete_runners_service.rb'
- 'app/services/database/consistency_check_service.rb'
+ - 'app/services/groups/import_export/import_service.rb'
- 'app/services/issues/export_csv_service.rb'
- 'app/services/labels/transfer_service.rb'
- 'app/services/members/create_service.rb'
@@ -52,8 +55,9 @@ Lint/RedundantCopDisableDirective:
- 'app/services/members/standard_member_builder.rb'
- 'app/services/projects/auto_devops/disable_service.rb'
- 'app/services/projects/open_issues_count_service.rb'
+ - 'app/services/releases/create_service.rb'
+ - 'app/services/releases/update_service.rb'
- 'app/services/spam/spam_action_service.rb'
- - 'app/services/users/migrate_to_ghost_user_service.rb'
- 'app/services/web_hook_service.rb'
- 'app/uploaders/object_storage/cdn/google_ip_cache.rb'
- 'app/workers/authorized_project_update/user_refresh_over_user_range_worker.rb'
@@ -85,6 +89,7 @@ Lint/RedundantCopDisableDirective:
- 'db/post_migrate/20220328100457_schedule20220328_reset_duplicate_ci_runners_token_values_on_projects.rb'
- 'db/post_migrate/20220720090354_remove_pending_builds_covering_index_from_ci_builds.rb'
- 'db/post_migrate/20220902204048_move_security_findings_table_to_gitlab_partitions_dynamic_schema.rb'
+ - 'ee/app/controllers/ee/admin/application_settings_controller.rb'
- 'ee/app/controllers/ee/groups/group_members_controller.rb'
- 'ee/app/controllers/ee/projects/settings/ci_cd_controller.rb'
- 'ee/app/controllers/groups/todos_controller.rb'
@@ -103,13 +108,12 @@ Lint/RedundantCopDisableDirective:
- 'ee/app/models/dast/branch.rb'
- 'ee/app/models/ee/vulnerability.rb'
- 'ee/app/models/geo/event_log.rb'
+ - 'ee/app/models/protected_environments/approval_rules/summarizable.rb'
- 'ee/app/services/analytics/cycle_analytics/consistency_check_service.rb'
- 'ee/app/services/analytics/cycle_analytics/data_loader_service.rb'
- 'ee/app/services/ee/boards/issues/list_service.rb'
- 'ee/app/services/ee/ci/queue/build_queue_service.rb'
- 'ee/app/services/ee/search_service.rb'
- - 'ee/app/services/ee/users/migrate_to_ghost_user_service.rb'
- - 'ee/app/services/geo/repository_base_sync_service.rb'
- 'ee/app/workers/ee/issuable_export_csv_worker.rb'
- 'ee/app/workers/ee/namespaces/in_product_marketing_emails_worker.rb'
- 'ee/app/workers/geo/design_repository_shard_sync_worker.rb'
@@ -120,6 +124,7 @@ Lint/RedundantCopDisableDirective:
- 'ee/db/geo/migrate/20210504143244_add_verification_to_merge_request_diff_registry.rb'
- 'ee/lib/analytics/merge_request_metrics_calculator.rb'
- 'ee/lib/api/audit_events.rb'
+ - 'ee/lib/api/dora/metrics.rb'
- 'ee/lib/api/scim.rb'
- 'ee/lib/ee/api/entities/analytics/code_review/merge_request.rb'
- 'ee/lib/ee/gitlab/background_migration/backfill_epic_cache_counts.rb'
@@ -131,12 +136,15 @@ Lint/RedundantCopDisableDirective:
- 'ee/lib/gitlab/elastic/bool_expr.rb'
- 'ee/lib/gitlab/spdx/license.rb'
- 'ee/lib/gitlab/status_page/storage/object.rb'
+ - 'ee/spec/contracts/provider/pact_helpers/project/merge_request/show/suggested_reviewers_helper.rb'
+ - 'ee/spec/elastic/migrate/20221026082700_backfill_users_spec.rb'
- 'ee/spec/features/boards/user_visits_board_spec.rb'
- 'ee/spec/features/groups/settings/domain_verification_spec.rb'
- 'ee/spec/helpers/ee/releases_helper_spec.rb'
- 'ee/spec/lib/ee/gitlab/background_migration/backfill_project_statistics_container_repository_size_spec.rb'
- 'ee/spec/lib/ee/gitlab/issuable_metadata_spec.rb'
- 'ee/spec/lib/elastic/latest/project_instance_proxy_spec.rb'
+ - 'ee/spec/lib/gitlab/usage/metrics/instrumentations/count_user_merge_requests_for_projects_with_applied_scan_result_policies_metric_spec.rb'
- 'ee/spec/lib/gitlab/usage/metrics/instrumentations/protected_environment_approval_rules_required_approvals_average_metric_spec.rb'
- 'ee/spec/requests/api/graphql/mutations/boards/epic_boards/epic_move_list_spec.rb'
- 'ee/spec/services/security/merge_reports_service_spec.rb'
@@ -181,13 +189,14 @@ Lint/RedundantCopDisableDirective:
- 'lib/gitlab/database/migrations/observation.rb'
- 'lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb'
- 'lib/gitlab/diff/file.rb'
- - 'lib/gitlab/diff/file_collection/merge_request_diff_batch.rb'
+ - 'lib/gitlab/diff/file_collection/paginated_diffs.rb'
- 'lib/gitlab/diff/pair_selector.rb'
- 'lib/gitlab/diff/parser.rb'
- 'lib/gitlab/encrypted_ldap_command.rb'
- 'lib/gitlab/encrypted_smtp_command.rb'
- 'lib/gitlab/git/commit.rb'
- 'lib/gitlab/git/patches/collection.rb'
+ - 'lib/gitlab/github_import/markdown_text.rb'
- 'lib/gitlab/github_import/user_finder.rb'
- 'lib/gitlab/gitlab_import/importer.rb'
- 'lib/gitlab/graphql/pagination/keyset/connection.rb'
@@ -211,8 +220,9 @@ Lint/RedundantCopDisableDirective:
- 'lib/gitlab/utils/usage_data.rb'
- 'lib/gitlab/x509/signature.rb'
- 'lib/tasks/gitlab/cleanup.rake'
- - 'qa/tasks/ci.rake'
- - 'scripts/lib/glfm/render_static_html.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/10_govern/create_merge_request_with_secure_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/10_govern/security_reports_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/10_govern/vulnerability_management_spec.rb'
- 'scripts/security-harness'
- 'sidekiq_cluster/cli.rb'
- 'sidekiq_cluster/sidekiq_cluster.rb'
@@ -225,7 +235,6 @@ Lint/RedundantCopDisableDirective:
- 'spec/graphql/mutations/commits/create_spec.rb'
- 'spec/graphql/resolvers/base_resolver_spec.rb'
- 'spec/helpers/releases_helper_spec.rb'
- - 'spec/initializers/memory_watchdog_spec.rb'
- 'spec/lib/gitlab/avatar_cache_spec.rb'
- 'spec/lib/gitlab/background_migration/backfill_cluster_agents_has_vulnerabilities_spec.rb'
- 'spec/lib/gitlab/background_migration/backfill_project_member_namespace_id_spec.rb'
@@ -241,6 +250,7 @@ Lint/RedundantCopDisableDirective:
- 'spec/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512_spec.rb'
- 'spec/lib/gitlab/git/tree_spec.rb'
- 'spec/lib/gitlab/import_export/project/relation_saver_spec.rb'
+ - 'spec/lib/gitlab/memory/watchdog/configurator_spec.rb'
- 'spec/lib/gitlab/pagination/keyset/iterator_spec.rb'
- 'spec/lib/gitlab/rack_attack/request_spec.rb'
- 'spec/lib/gitlab/shard_health_cache_spec.rb'
@@ -252,6 +262,7 @@ Lint/RedundantCopDisableDirective:
- 'spec/migrations/reschedule_backfill_imported_issue_search_data_spec.rb'
- 'spec/models/ci/build_trace_chunk_spec.rb'
- 'spec/models/ci/pipeline_spec.rb'
+ - 'spec/models/concerns/encrypted_user_password_spec.rb'
- 'spec/models/namespace/package_setting_spec.rb'
- 'spec/models/namespace_spec.rb'
- 'spec/models/project_feature_spec.rb'
@@ -263,6 +274,7 @@ Lint/RedundantCopDisableDirective:
- 'spec/requests/api/graphql/ci/config_spec.rb'
- 'spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb'
- 'spec/services/alert_management/metric_images/upload_service_spec.rb'
+ - 'spec/services/boards/lists/list_service_spec.rb'
- 'spec/services/projects/update_statistics_service_spec.rb'
- 'spec/services/spam/spam_verdict_service_spec.rb'
- 'spec/services/topics/merge_service_spec.rb'
diff --git a/.rubocop_todo/metrics/abc_size.yml b/.rubocop_todo/metrics/abc_size.yml
index f5646151592..01e7a5c9688 100644
--- a/.rubocop_todo/metrics/abc_size.yml
+++ b/.rubocop_todo/metrics/abc_size.yml
@@ -1,15 +1,11 @@
---
Metrics/AbcSize:
Exclude:
- - 'app/controllers/concerns/issuable_actions.rb'
- 'app/helpers/issuables_helper.rb'
- 'app/helpers/nav/top_nav_helper.rb'
- - 'app/helpers/search_helper.rb'
- 'app/models/instance_configuration.rb'
- 'app/services/projects/create_service.rb'
- - 'ee/app/workers/elastic/migration_worker.rb'
- - 'ee/lib/ee/audit/project_changes_auditor.rb'
+ - 'ee/db/seeds/awesome_co/awesome_co.rb'
- 'lib/gitlab/analytics/cycle_analytics/request_params.rb'
- 'lib/gitlab/sidekiq_middleware/server_metrics.rb'
- 'qa/qa/resource/repository/push.rb'
- - 'ee/db/seeds/awesome_co/**/*.rb'
diff --git a/.rubocop_todo/metrics/cyclomatic_complexity.yml b/.rubocop_todo/metrics/cyclomatic_complexity.yml
index 71efe9d5a3a..634ec6221f4 100644
--- a/.rubocop_todo/metrics/cyclomatic_complexity.yml
+++ b/.rubocop_todo/metrics/cyclomatic_complexity.yml
@@ -13,11 +13,9 @@ Metrics/CyclomaticComplexity:
- 'lib/banzai/filter/references/abstract_reference_filter.rb'
- 'lib/gitlab/conflict/file.rb'
- 'lib/gitlab/database/query_analyzers/prevent_cross_database_modification.rb'
- - 'lib/gitlab/diff/parser.rb'
- 'lib/gitlab/utils/merge_hash.rb'
- 'lib/kramdown/parser/atlassian_document_format.rb'
- 'qa/qa/runtime/browser.rb'
- 'qa/qa/support/repeater.rb'
- - 'rubocop/cop/gitlab/mark_used_feature_flags.rb'
- 'sidekiq_cluster/cli.rb'
- 'spec/support/cycle_analytics_helpers/test_generation.rb'
diff --git a/.rubocop_todo/metrics/perceived_complexity.yml b/.rubocop_todo/metrics/perceived_complexity.yml
index 0f2d3030064..5f5f1858ed6 100644
--- a/.rubocop_todo/metrics/perceived_complexity.yml
+++ b/.rubocop_todo/metrics/perceived_complexity.yml
@@ -11,13 +11,10 @@ Metrics/PerceivedComplexity:
- 'ee/lib/elastic/latest/git_class_proxy.rb'
- 'lib/banzai/filter/references/abstract_reference_filter.rb'
- 'lib/banzai/renderer.rb'
- - 'lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb'
- 'lib/gitlab/conflict/file.rb'
- 'lib/gitlab/database/query_analyzers/prevent_cross_database_modification.rb'
- - 'lib/gitlab/diff/parser.rb'
- 'lib/gitlab/utils/merge_hash.rb'
- 'qa/qa/runtime/browser.rb'
- - 'qa/qa/specs/runner.rb'
- 'qa/qa/support/repeater.rb'
- 'rubocop/cop/gitlab/mark_used_feature_flags.rb'
- 'sidekiq_cluster/cli.rb'
diff --git a/.rubocop_todo/migration/background_migration_base_class.yml b/.rubocop_todo/migration/background_migration_base_class.yml
index 9e42e85cce8..56b6a4294d4 100644
--- a/.rubocop_todo/migration/background_migration_base_class.yml
+++ b/.rubocop_todo/migration/background_migration_base_class.yml
@@ -2,10 +2,9 @@
Migration/BackgroundMigrationBaseClass:
Exclude:
- 'lib/gitlab/background_migration/add_primary_email_to_emails_if_user_confirmed.rb'
- - 'lib/gitlab/background_migration/backfill_ci_namespace_mirrors.rb'
- - 'lib/gitlab/background_migration/backfill_ci_project_mirrors.rb'
- 'lib/gitlab/background_migration/backfill_ci_queuing_tables.rb'
- 'lib/gitlab/background_migration/backfill_draft_status_on_merge_requests.rb'
+ - 'lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification.rb'
- 'lib/gitlab/background_migration/backfill_integrations_type_new.rb'
- 'lib/gitlab/background_migration/backfill_issue_search_data.rb'
- 'lib/gitlab/background_migration/backfill_iteration_cadence_id_for_boards.rb'
@@ -16,14 +15,12 @@ Migration/BackgroundMigrationBaseClass:
- 'lib/gitlab/background_migration/backfill_namespace_traversal_ids_children.rb'
- 'lib/gitlab/background_migration/backfill_namespace_traversal_ids_roots.rb'
- 'lib/gitlab/background_migration/backfill_note_discussion_id.rb'
- - 'lib/gitlab/background_migration/backfill_projects_with_coverage.rb'
- 'lib/gitlab/background_migration/backfill_project_repositories.rb'
- 'lib/gitlab/background_migration/backfill_project_settings.rb'
- 'lib/gitlab/background_migration/backfill_snippet_repositories.rb'
- 'lib/gitlab/background_migration/backfill_topics_title.rb'
- 'lib/gitlab/background_migration/backfill_upvotes_count_on_issues.rb'
- 'lib/gitlab/background_migration/backfill_user_namespace.rb'
- - 'lib/gitlab/background_migration/backfill_work_item_type_id_for_issues.rb'
- 'lib/gitlab/background_migration/cleanup_draft_data_from_faulty_regex.rb'
- 'lib/gitlab/background_migration/cleanup_orphaned_lfs_objects_projects.rb'
- 'lib/gitlab/background_migration/create_security_setting.rb'
@@ -31,7 +28,6 @@ Migration/BackgroundMigrationBaseClass:
- 'lib/gitlab/background_migration/disable_expiration_policies_linked_to_no_container_images.rb'
- 'lib/gitlab/background_migration/drop_invalid_remediations.rb'
- 'lib/gitlab/background_migration/drop_invalid_security_findings.rb'
- - 'lib/gitlab/background_migration/drop_invalid_vulnerabilities.rb'
- 'lib/gitlab/background_migration/encrypt_integration_properties.rb'
- 'lib/gitlab/background_migration/encrypt_static_object_token.rb'
- 'lib/gitlab/background_migration/extract_project_topics_into_separate_table.rb'
@@ -42,8 +38,9 @@ Migration/BackgroundMigrationBaseClass:
- 'lib/gitlab/background_migration/fix_projects_without_project_feature.rb'
- 'lib/gitlab/background_migration/fix_projects_without_prometheus_service.rb'
- 'lib/gitlab/background_migration/fix_vulnerability_occurrences_with_hashes_as_raw_metadata.rb'
- - 'lib/gitlab/background_migration/legacy_uploads_migrator.rb'
- 'lib/gitlab/background_migration/legacy_upload_mover.rb'
+ - 'lib/gitlab/background_migration/legacy_uploads_migrator.rb'
+ - 'lib/gitlab/background_migration/mailers/unconfirm_mailer.rb'
- 'lib/gitlab/background_migration/merge_topics_with_same_name.rb'
- 'lib/gitlab/background_migration/migrate_approver_to_approval_rules.rb'
- 'lib/gitlab/background_migration/migrate_approver_to_approval_rules_check_progress.rb'
@@ -63,28 +60,21 @@ Migration/BackgroundMigrationBaseClass:
- 'lib/gitlab/background_migration/populate_latest_pipeline_ids.rb'
- 'lib/gitlab/background_migration/populate_namespace_statistics.rb'
- 'lib/gitlab/background_migration/populate_resolved_on_default_branch_column.rb'
- - 'lib/gitlab/background_migration/populate_status_column_of_security_scans.rb'
- 'lib/gitlab/background_migration/populate_test_reports_issue_id.rb'
- 'lib/gitlab/background_migration/populate_topics_non_private_projects_count.rb'
- 'lib/gitlab/background_migration/populate_topics_total_projects_count_cache.rb'
- 'lib/gitlab/background_migration/populate_uuids_for_security_findings.rb'
- 'lib/gitlab/background_migration/populate_vulnerability_reads.rb'
- - 'lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb'
+ - 'lib/gitlab/background_migration/project_namespaces/backfill_project_namespaces.rb'
+ - 'lib/gitlab/background_migration/project_namespaces/models/namespace.rb'
+ - 'lib/gitlab/background_migration/project_namespaces/models/project.rb'
- 'lib/gitlab/background_migration/recalculate_vulnerability_finding_signatures_for_findings.rb'
- 'lib/gitlab/background_migration/remove_all_trace_expiration_dates.rb'
- - 'lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings.rb'
- - 'lib/gitlab/background_migration/remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings.rb'
- 'lib/gitlab/background_migration/remove_vulnerability_finding_links.rb'
- 'lib/gitlab/background_migration/reset_duplicate_ci_runners_token_encrypted_values_on_projects.rb'
- 'lib/gitlab/background_migration/reset_duplicate_ci_runners_token_values_on_projects.rb'
- 'lib/gitlab/background_migration/steal_migrate_merge_request_diff_commit_users.rb'
- - 'lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb'
- 'lib/gitlab/background_migration/update_timelogs_null_spent_at.rb'
- 'lib/gitlab/background_migration/update_timelogs_project_id.rb'
- 'lib/gitlab/background_migration/update_users_where_two_factor_auth_required_from_group.rb'
- 'lib/gitlab/background_migration/update_vulnerability_occurrences_location.rb'
- - 'lib/gitlab/background_migration/mailers/unconfirm_mailer.rb'
- - 'lib/gitlab/background_migration/project_namespaces/models/project.rb'
- - 'lib/gitlab/background_migration/project_namespaces/models/namespace.rb'
- - 'lib/gitlab/background_migration/project_namespaces/backfill_project_namespaces.rb'
- - 'lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification.rb'
diff --git a/.rubocop_todo/migration/background_migration_record.yml b/.rubocop_todo/migration/background_migration_record.yml
index b4feb944d75..567926e33a2 100644
--- a/.rubocop_todo/migration/background_migration_record.yml
+++ b/.rubocop_todo/migration/background_migration_record.yml
@@ -1,54 +1,50 @@
---
Migration/BackgroundMigrationRecord:
Exclude:
- - lib/gitlab/background_migration/add_primary_email_to_emails_if_user_confirmed.rb
- - lib/gitlab/background_migration/backfill_ci_namespace_mirrors.rb
- - lib/gitlab/background_migration/backfill_ci_project_mirrors.rb
- - lib/gitlab/background_migration/backfill_ci_queuing_tables.rb
- - lib/gitlab/background_migration/backfill_draft_status_on_merge_requests.rb
- - lib/gitlab/background_migration/backfill_namespace_traversal_ids_children.rb
- - lib/gitlab/background_migration/backfill_namespace_traversal_ids_roots.rb
- - lib/gitlab/background_migration/backfill_project_repositories.rb
- - lib/gitlab/background_migration/backfill_projects_with_coverage.rb
- - lib/gitlab/background_migration/backfill_topics_title.rb
- - lib/gitlab/background_migration/cleanup_draft_data_from_faulty_regex.rb
- - lib/gitlab/background_migration/cleanup_orphaned_lfs_objects_projects.rb
- - lib/gitlab/background_migration/disable_expiration_policies_linked_to_no_container_images.rb
- - lib/gitlab/background_migration/drop_invalid_security_findings.rb
- - lib/gitlab/background_migration/drop_invalid_vulnerabilities.rb
- - lib/gitlab/background_migration/encrypt_integration_properties.rb
- - lib/gitlab/background_migration/encrypt_static_object_token.rb
- - lib/gitlab/background_migration/extract_project_topics_into_separate_table.rb
- - lib/gitlab/background_migration/fix_duplicate_project_name_and_path.rb
- - lib/gitlab/background_migration/fix_first_mentioned_in_commit_at.rb
- - lib/gitlab/background_migration/fix_projects_without_prometheus_service.rb
- - lib/gitlab/background_migration/fix_vulnerability_occurrences_with_hashes_as_raw_metadata.rb
- - lib/gitlab/background_migration/merge_topics_with_same_name.rb
- - lib/gitlab/background_migration/migrate_merge_request_diff_commit_users.rb
- - lib/gitlab/background_migration/migrate_null_private_profile_to_false.rb
- - lib/gitlab/background_migration/migrate_project_taggings_context_from_tags_to_topics.rb
- - lib/gitlab/background_migration/migrate_u2f_webauthn.rb
- - lib/gitlab/background_migration/populate_latest_pipeline_ids.rb
- - lib/gitlab/background_migration/populate_topics_non_private_projects_count.rb
- - lib/gitlab/background_migration/populate_topics_total_projects_count_cache.rb
- - lib/gitlab/background_migration/project_namespaces/models/namespace.rb
- - lib/gitlab/background_migration/project_namespaces/models/project.rb
- - lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb
- - lib/gitlab/background_migration/remove_all_trace_expiration_dates.rb
- - lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings.rb
- - lib/gitlab/background_migration/remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings.rb
- - lib/gitlab/background_migration/reset_duplicate_ci_runners_token_encrypted_values_on_projects.rb
- - lib/gitlab/background_migration/reset_duplicate_ci_runners_token_values_on_projects.rb
- - lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb
- - ee/lib/ee/gitlab/background_migration/backfill_iteration_cadence_id_for_boards.rb
- - ee/lib/ee/gitlab/background_migration/create_security_setting.rb
- - ee/lib/ee/gitlab/background_migration/drop_invalid_remediations.rb
- - ee/lib/ee/gitlab/background_migration/fix_incorrect_max_seats_used.rb
- - ee/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules.rb
- - ee/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules_in_batch.rb
- - ee/lib/ee/gitlab/background_migration/migrate_requirements_to_work_items.rb
- - ee/lib/ee/gitlab/background_migration/populate_latest_pipeline_ids.rb
- - ee/lib/ee/gitlab/background_migration/populate_resolved_on_default_branch_column.rb
- - ee/lib/ee/gitlab/background_migration/populate_status_column_of_security_scans.rb
- - ee/lib/ee/gitlab/background_migration/populate_uuids_for_security_findings.rb
- - ee/lib/ee/gitlab/background_migration/update_vulnerability_occurrences_location.rb
+ - 'ee/lib/ee/gitlab/background_migration/backfill_iteration_cadence_id_for_boards.rb'
+ - 'ee/lib/ee/gitlab/background_migration/create_security_setting.rb'
+ - 'ee/lib/ee/gitlab/background_migration/drop_invalid_remediations.rb'
+ - 'ee/lib/ee/gitlab/background_migration/fix_incorrect_max_seats_used.rb'
+ - 'ee/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules.rb'
+ - 'ee/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules_in_batch.rb'
+ - 'ee/lib/ee/gitlab/background_migration/migrate_requirements_to_work_items.rb'
+ - 'ee/lib/ee/gitlab/background_migration/populate_latest_pipeline_ids.rb'
+ - 'ee/lib/ee/gitlab/background_migration/populate_resolved_on_default_branch_column.rb'
+ - 'ee/lib/ee/gitlab/background_migration/populate_uuids_for_security_findings.rb'
+ - 'ee/lib/ee/gitlab/background_migration/update_vulnerability_occurrences_location.rb'
+ - 'lib/gitlab/background_migration/add_primary_email_to_emails_if_user_confirmed.rb'
+ - 'lib/gitlab/background_migration/backfill_ci_queuing_tables.rb'
+ - 'lib/gitlab/background_migration/backfill_draft_status_on_merge_requests.rb'
+ - 'lib/gitlab/background_migration/backfill_namespace_traversal_ids_children.rb'
+ - 'lib/gitlab/background_migration/backfill_namespace_traversal_ids_roots.rb'
+ - 'lib/gitlab/background_migration/backfill_project_repositories.rb'
+ - 'lib/gitlab/background_migration/backfill_topics_title.rb'
+ - 'lib/gitlab/background_migration/cleanup_draft_data_from_faulty_regex.rb'
+ - 'lib/gitlab/background_migration/cleanup_orphaned_lfs_objects_projects.rb'
+ - 'lib/gitlab/background_migration/disable_expiration_policies_linked_to_no_container_images.rb'
+ - 'lib/gitlab/background_migration/drop_invalid_security_findings.rb'
+ - 'lib/gitlab/background_migration/drop_invalid_vulnerabilities.rb'
+ - 'lib/gitlab/background_migration/encrypt_integration_properties.rb'
+ - 'lib/gitlab/background_migration/encrypt_static_object_token.rb'
+ - 'lib/gitlab/background_migration/extract_project_topics_into_separate_table.rb'
+ - 'lib/gitlab/background_migration/fix_duplicate_project_name_and_path.rb'
+ - 'lib/gitlab/background_migration/fix_first_mentioned_in_commit_at.rb'
+ - 'lib/gitlab/background_migration/fix_projects_without_prometheus_service.rb'
+ - 'lib/gitlab/background_migration/fix_vulnerability_occurrences_with_hashes_as_raw_metadata.rb'
+ - 'lib/gitlab/background_migration/merge_topics_with_same_name.rb'
+ - 'lib/gitlab/background_migration/migrate_merge_request_diff_commit_users.rb'
+ - 'lib/gitlab/background_migration/migrate_null_private_profile_to_false.rb'
+ - 'lib/gitlab/background_migration/migrate_project_taggings_context_from_tags_to_topics.rb'
+ - 'lib/gitlab/background_migration/migrate_u2f_webauthn.rb'
+ - 'lib/gitlab/background_migration/populate_latest_pipeline_ids.rb'
+ - 'lib/gitlab/background_migration/populate_topics_non_private_projects_count.rb'
+ - 'lib/gitlab/background_migration/populate_topics_total_projects_count_cache.rb'
+ - 'lib/gitlab/background_migration/project_namespaces/models/namespace.rb'
+ - 'lib/gitlab/background_migration/project_namespaces/models/project.rb'
+ - 'lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb'
+ - 'lib/gitlab/background_migration/remove_all_trace_expiration_dates.rb'
+ - 'lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings.rb'
+ - 'lib/gitlab/background_migration/remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings.rb'
+ - 'lib/gitlab/background_migration/reset_duplicate_ci_runners_token_encrypted_values_on_projects.rb'
+ - 'lib/gitlab/background_migration/reset_duplicate_ci_runners_token_values_on_projects.rb'
+ - 'lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb'
diff --git a/.rubocop_todo/migration/background_migrations.yml b/.rubocop_todo/migration/background_migrations.yml
index 17548ef6b87..2219f37901d 100644
--- a/.rubocop_todo/migration/background_migrations.yml
+++ b/.rubocop_todo/migration/background_migrations.yml
@@ -1,25 +1,10 @@
---
Migration/BackgroundMigrations:
Exclude:
- - 'ee/db/geo/post_migrate/20220202101354_migrate_job_artifact_registry.rb'
- - 'db/post_migrate/20220506124021_schedule_populate_requirements_issue_id.rb'
- - 'db/post_migrate/20220502173045_reset_too_many_tags_skipped_registry_imports.rb'
- - 'db/post_migrate/20220425121435_backfill_integrations_enable_ssl_verification.rb'
- - 'db/post_migrate/20220420214703_schedule_backfill_draft_status_on_merge_requests_corrected_regex.rb'
- - 'db/post_migrate/20220420061450_backfill_null_note_discussion_ids.rb'
- - 'db/post_migrate/20220407163559_schedule_purging_stale_security_scans.rb'
- - 'db/post_migrate/20220331133802_schedule_backfill_topics_title.rb'
- - 'db/post_migrate/20220328100457_schedule20220328_reset_duplicate_ci_runners_token_values_on_projects.rb'
- - 'db/post_migrate/20220328100456_schedule20220328_reset_duplicate_ci_runners_token_encrypted_values_on_projects.rb'
- - 'db/post_migrate/20220324081709_fix_and_backfill_project_namespaces_for_projects_with_duplicate_name.rb'
- - 'db/post_migrate/20220324032250_migrate_shimo_confluence_service_category.rb'
- - 'db/post_migrate/20220316202640_populate_container_repositories_migration_plan.rb'
- - 'db/post_migrate/20220315171129_cleanup_draft_data_from_faulty_regex.rb'
- 'db/migrate/20210519154058_schedule_update_users_where_two_factor_auth_required_from_group.rb'
- - 'db/post_migrate/20210302150310_schedule_migrate_pages_to_zip_storage.rb'
+ - 'db/post_migrate/20210302074524_backfill_namespace_statistics_with_wiki_size.rb'
- 'db/post_migrate/20210304133508_schedule_remove_duplicate_vulnerabilities_findings2.rb'
- 'db/post_migrate/20210415155043_move_container_registry_enabled_to_project_features3.rb'
- - 'db/post_migrate/20210421163509_schedule_update_jira_tracker_data_deployment_type_based_on_url.rb'
- 'db/post_migrate/20210423160427_schedule_drop_invalid_vulnerabilities.rb'
- 'db/post_migrate/20210427212034_schedule_update_timelogs_project_id.rb'
- 'db/post_migrate/20210506065000_schedule_backfill_traversal_ids.rb'
@@ -27,55 +12,63 @@ Migration/BackgroundMigrations:
- 'db/post_migrate/20210511142748_schedule_drop_invalid_vulnerabilities2.rb'
- 'db/post_migrate/20210514063252_schedule_cleanup_orphaned_lfs_objects_projects.rb'
- 'db/post_migrate/20210518074332_schedule_disable_expiration_policies_linked_to_no_container_images.rb'
+ - 'db/post_migrate/20210604070207_retry_backfill_traversal_ids.rb'
- 'db/post_migrate/20210611080951_fix_missing_traversal_ids.rb'
- 'db/post_migrate/20210701111909_backfill_issues_upvotes_count.rb'
+ - 'db/post_migrate/20210708130419_reschedule_merge_request_diff_users_background_migration.rb'
- 'db/post_migrate/20210722010101_reschedule_delete_orphaned_deployments.rb'
- 'db/post_migrate/20210729163312_re_schedule_latest_pipeline_id_population_with_all_security_related_artifact_types.rb'
- 'db/post_migrate/20210730104800_schedule_extract_project_topics_into_separate_table.rb'
- 'db/post_migrate/20210730170823_schedule_security_setting_creation.rb'
- - 'db/post_migrate/20210816183304_schedule_copy_ci_builds_columns_to_security_scans2.rb'
- - 'db/post_migrate/20210818185845_backfill_projects_with_coverage.rb'
+ - 'db/post_migrate/20210823113259_steal_merge_request_diff_commit_users_migration.rb'
+ - 'db/post_migrate/20210825193652_backfill_cadence_id_for_boards_scoped_to_iteration.rb'
+ - 'db/post_migrate/20210830104800_reschedule_extract_project_topics_into_separate_table.rb'
+ - 'db/post_migrate/20210901153324_slice_merge_request_diff_commit_migrations.rb'
+ - 'db/post_migrate/20210909104800_reschedule_extract_project_topics_into_separate_table_2.rb'
- 'db/post_migrate/20211004110927_schedule_fix_first_mentioned_in_commit_at_job.rb'
- 'db/post_migrate/20211004120135_schedule_add_primary_email_to_emails_if_user_confirmed.rb'
+ - 'db/post_migrate/20211005010101_rereschedule_delete_orphaned_deployments.rb'
- 'db/post_migrate/20211005194425_schedule_requirements_migration.rb'
- 'db/post_migrate/20211006060436_schedule_populate_topics_total_projects_count_cache.rb'
- - 'db/post_migrate/20211007155221_schedule_populate_status_column_of_security_scans.rb'
- 'db/post_migrate/20211018152654_schedule_remove_duplicate_vulnerabilities_findings3.rb'
+ - 'db/post_migrate/20211028155449_schedule_fix_merge_request_diff_commit_users_migration.rb'
- 'db/post_migrate/20211102114802_update_vulnerability_occurrences_location.rb'
- 'db/post_migrate/20211110151350_schedule_drop_invalid_security_findings.rb'
- 'db/post_migrate/20211116111644_schedule_remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings.rb'
- 'db/post_migrate/20211118194239_drop_invalid_remediations.rb'
- - 'db/post_migrate/20211208122200_schedule_backfill_ci_namespace_mirrors.rb'
- - 'db/post_migrate/20211208122201_schedule_backfill_ci_project_mirrors.rb'
+ - 'db/post_migrate/20211207135331_schedule_recalculate_uuid_on_vulnerabilities_occurrences4.rb'
+ - 'db/post_migrate/20211208171402_reschedule_recalculate_vulnerability_finding_signatures_for_findings.rb'
- 'db/post_migrate/20211209203821_convert_stringified_raw_metadata_hash_to_json.rb'
- 'db/post_migrate/20211210140629_encrypt_static_object_token.rb'
- 'db/post_migrate/20211215090620_schedule_update_timelogs_null_spent_at.rb'
- 'db/post_migrate/20220107064845_populate_vulnerability_reads.rb'
- 'db/post_migrate/20220110171049_schedule_populate_test_reports_issue_id.rb'
+ - 'db/post_migrate/20220113111440_schedule_fix_incorrect_max_seats_used.rb'
+ - 'db/post_migrate/20220118204039_self_managed_reschedule_recalculate_vulnerability_finding_signatures_for_findings.rb'
- 'db/post_migrate/20220125122640_schedule_populate_topics_non_private_projects_count.rb'
- 'db/post_migrate/20220131000001_schedule_trace_expiry_removal.rb'
+ - 'db/post_migrate/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size.rb'
- 'db/post_migrate/20220204194347_encrypt_integration_properties.rb'
- 'db/post_migrate/20220208115439_start_backfill_ci_queuing_tables.rb'
+ - 'db/post_migrate/20220212120735_schedule_fix_incorrect_max_seats_used2.rb'
- 'db/post_migrate/20220215190020_rerun_convert_stringified_raw_metadata_hash_to_json.rb'
+ - 'db/post_migrate/20220223124428_schedule_merge_topics_with_same_name.rb'
- 'db/post_migrate/20220308115219_schedule_reset_duplicate_ci_runners_token_encrypted_values_on_projects.rb'
- 'db/post_migrate/20220308115502_schedule_reset_duplicate_ci_runners_token_values_on_projects.rb'
- - 'db/post_migrate/20211207135331_schedule_recalculate_uuid_on_vulnerabilities_occurrences4.rb'
- - 'db/post_migrate/20210604070207_retry_backfill_traversal_ids.rb'
- - 'db/post_migrate/20210708130419_reschedule_merge_request_diff_users_background_migration.rb'
- - 'db/post_migrate/20210823113259_steal_merge_request_diff_commit_users_migration.rb'
- - 'db/post_migrate/20210825193652_backfill_cadence_id_for_boards_scoped_to_iteration.rb'
- - 'db/post_migrate/20210830104800_reschedule_extract_project_topics_into_separate_table.rb'
- - 'db/post_migrate/20210901153324_slice_merge_request_diff_commit_migrations.rb'
- - 'db/post_migrate/20210909104800_reschedule_extract_project_topics_into_separate_table_2.rb'
- - 'db/post_migrate/20211005010101_rereschedule_delete_orphaned_deployments.rb'
- - 'db/post_migrate/20211028155449_schedule_fix_merge_request_diff_commit_users_migration.rb'
- - 'db/post_migrate/20211208171402_reschedule_recalculate_vulnerability_finding_signatures_for_findings.rb'
- - 'db/post_migrate/20220113111440_schedule_fix_incorrect_max_seats_used.rb'
- - 'db/post_migrate/20220118204039_self_managed_reschedule_recalculate_vulnerability_finding_signatures_for_findings.rb'
- - 'db/post_migrate/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size.rb'
- - 'db/post_migrate/20220212120735_schedule_fix_incorrect_max_seats_used2.rb'
- - 'db/post_migrate/20220223124428_schedule_merge_topics_with_same_name.rb'
- - 'db/post_migrate/20210302074524_backfill_namespace_statistics_with_wiki_size.rb'
+ - 'db/post_migrate/20220315171129_cleanup_draft_data_from_faulty_regex.rb'
+ - 'db/post_migrate/20220316202640_populate_container_repositories_migration_plan.rb'
+ - 'db/post_migrate/20220324032250_migrate_shimo_confluence_service_category.rb'
+ - 'db/post_migrate/20220324081709_fix_and_backfill_project_namespaces_for_projects_with_duplicate_name.rb'
+ - 'db/post_migrate/20220328100456_schedule20220328_reset_duplicate_ci_runners_token_encrypted_values_on_projects.rb'
+ - 'db/post_migrate/20220328100457_schedule20220328_reset_duplicate_ci_runners_token_values_on_projects.rb'
+ - 'db/post_migrate/20220331133802_schedule_backfill_topics_title.rb'
+ - 'db/post_migrate/20220407163559_schedule_purging_stale_security_scans.rb'
+ - 'db/post_migrate/20220420061450_backfill_null_note_discussion_ids.rb'
+ - 'db/post_migrate/20220420214703_schedule_backfill_draft_status_on_merge_requests_corrected_regex.rb'
+ - 'db/post_migrate/20220425121435_backfill_integrations_enable_ssl_verification.rb'
+ - 'db/post_migrate/20220502173045_reset_too_many_tags_skipped_registry_imports.rb'
+ - 'db/post_migrate/20220506124021_schedule_populate_requirements_issue_id.rb'
+ - 'ee/db/geo/post_migrate/20220202101354_migrate_job_artifact_registry.rb'
- 'lib/gitlab/database/migrations/background_migration_helpers.rb'
- 'lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb'
- 'spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb'
diff --git a/.rubocop_todo/naming/heredoc_delimiter_case.yml b/.rubocop_todo/naming/heredoc_delimiter_case.yml
index 8779831da21..2d2337b82ee 100644
--- a/.rubocop_todo/naming/heredoc_delimiter_case.yml
+++ b/.rubocop_todo/naming/heredoc_delimiter_case.yml
@@ -1,4 +1,5 @@
---
+# Cop supports --autocorrect.
Naming/HeredocDelimiterCase:
Exclude:
- 'spec/lib/gitlab/diff/parser_spec.rb'
diff --git a/.rubocop_todo/naming/heredoc_delimiter_naming.yml b/.rubocop_todo/naming/heredoc_delimiter_naming.yml
index 29276e529af..d57905c8607 100644
--- a/.rubocop_todo/naming/heredoc_delimiter_naming.yml
+++ b/.rubocop_todo/naming/heredoc_delimiter_naming.yml
@@ -29,7 +29,6 @@ Naming/HeredocDelimiterNaming:
- 'ee/spec/support/helpers/ee/ldap_helpers.rb'
- 'ee/spec/tasks/gitlab/elastic_rake_spec.rb'
- 'lib/api/metadata.rb'
- - 'lib/api/version.rb'
- 'lib/backup/helper.rb'
- 'lib/feature/shared.rb'
- 'lib/gitlab/cache/import/caching.rb'
@@ -38,12 +37,10 @@ Naming/HeredocDelimiterNaming:
- 'lib/gitlab/database/migration_helpers.rb'
- 'lib/gitlab/database/migration_helpers/v2.rb'
- 'lib/gitlab/exclusive_lease.rb'
- - 'lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb'
- 'lib/gitlab/sql/set_operator.rb'
- 'lib/gitlab/utils/delegator_override/validator.rb'
- 'lib/tasks/gitlab/docs/compile_deprecations.rake'
- 'lib/tasks/gitlab/password.rake'
- - 'qa/qa/scenario/test/sanity/selectors.rb'
- 'qa/qa/specs/features/browser_ui/3_create/web_ide/web_terminal_spec.rb'
- 'qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb'
- 'qa/qa/specs/features/browser_ui/5_package/package_registry/generic_repository_spec.rb'
@@ -65,7 +62,6 @@ Naming/HeredocDelimiterNaming:
- 'spec/factories/packages/debian/file_metadatum.rb'
- 'spec/features/projects/commit/user_comments_on_commit_spec.rb'
- 'spec/features/task_lists_spec.rb'
- - 'spec/helpers/markup_helper_spec.rb'
- 'spec/initializers/100_patch_omniauth_oauth2_spec.rb'
- 'spec/initializers/net_http_response_patch_spec.rb'
- 'spec/initializers/rack_multipart_patch_spec.rb'
diff --git a/.rubocop_todo/performance/active_record_subtransaction_methods.yml b/.rubocop_todo/performance/active_record_subtransaction_methods.yml
index 2644f08c4d1..1eeb37ff3fd 100644
--- a/.rubocop_todo/performance/active_record_subtransaction_methods.yml
+++ b/.rubocop_todo/performance/active_record_subtransaction_methods.yml
@@ -43,7 +43,6 @@ Performance/ActiveRecordSubtransactionMethods:
- 'ee/db/fixtures/production/027_plans.rb'
- 'ee/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules.rb'
- 'ee/lib/gitlab/elastic/indexer.rb'
- - 'lib/gitlab/ci/pipeline/seed/environment.rb'
- 'lib/gitlab/ci/pipeline/seed/processable/resource_group.rb'
- 'lib/gitlab/ci/trace/chunked_io.rb'
- 'lib/gitlab/composer/cache.rb'
diff --git a/.rubocop_todo/performance/bind_call.yml b/.rubocop_todo/performance/bind_call.yml
deleted file mode 100644
index 5adb519793d..00000000000
--- a/.rubocop_todo/performance/bind_call.yml
+++ /dev/null
@@ -1,10 +0,0 @@
----
-# Cop supports --auto-correct.
-Performance/BindCall:
- Exclude:
- - 'app/services/metrics/dashboard/grafana_metric_embed_service.rb'
- - 'ee/spec/features/issues/form_spec.rb'
- - 'lib/gitlab/patch/prependable.rb'
- - 'spec/features/issues/form_spec.rb'
- - 'spec/mailers/notify_spec.rb'
- - 'spec/support/patches/rspec_mocks_prepended_methods.rb'
diff --git a/.rubocop_todo/performance/method_object_as_block.yml b/.rubocop_todo/performance/method_object_as_block.yml
index acb1e2d621b..8524376772e 100644
--- a/.rubocop_todo/performance/method_object_as_block.yml
+++ b/.rubocop_todo/performance/method_object_as_block.yml
@@ -11,7 +11,6 @@ Performance/MethodObjectAsBlock:
- 'app/models/container_repository.rb'
- 'app/models/programming_language.rb'
- 'app/presenters/packages/detail/package_presenter.rb'
- - 'app/services/bulk_imports/file_download_service.rb'
- 'app/services/ci/pipeline_processing/atomic_processing_service.rb'
- 'app/services/ci/prometheus_metrics/observe_histograms_service.rb'
- 'app/services/concerns/users/participable_service.rb'
@@ -26,7 +25,6 @@ Performance/MethodObjectAsBlock:
- 'ee/app/finders/security/findings_finder.rb'
- 'ee/app/graphql/resolvers/vulnerabilities/scanners_resolver.rb'
- 'ee/app/services/dashboard/projects/create_service.rb'
- - 'ee/app/services/security/findings/cleanup_service.rb'
- 'ee/app/services/security/ingestion/ingest_reports_service.rb'
- 'ee/app/services/security/ingestion/tasks/ingest_vulnerability_statistics.rb'
- 'ee/app/services/security/store_grouped_scans_service.rb'
@@ -75,20 +73,17 @@ Performance/MethodObjectAsBlock:
- 'lib/gitlab/uploads/migration_helper.rb'
- 'lib/gitlab/utils.rb'
- 'lib/peek/views/detailed_view.rb'
- - 'lib/tasks/gitlab/assets.rake'
- 'lib/unnested_in_filters/rewriter.rb'
- 'qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb'
- 'rubocop/cop/avoid_return_from_blocks.rb'
- 'rubocop/cop/gitlab/mark_used_feature_flags.rb'
- 'rubocop/rubocop.rb'
- - 'scripts/api/download_job_artifact.rb'
- 'spec/graphql/resolvers/concerns/caching_array_resolver_spec.rb'
- 'spec/lib/api/entities/merge_request_basic_spec.rb'
- 'spec/lib/gitlab/import_export/import_test_coverage_spec.rb'
- 'spec/lib/gitlab/search_context/builder_spec.rb'
- 'spec/models/design_management/version_spec.rb'
- 'spec/services/notification_service_spec.rb'
- - 'spec/services/projects/container_repository/cleanup_tags_service_spec.rb'
- 'spec/support/helpers/migrations_helpers.rb'
- 'spec/support/shared_examples/models/active_record_enum_shared_examples.rb'
- 'spec/support_specs/helpers/stub_feature_flags_spec.rb'
diff --git a/.rubocop_todo/performance/redundant_block_call.yml b/.rubocop_todo/performance/redundant_block_call.yml
deleted file mode 100644
index 89a67e3fd30..00000000000
--- a/.rubocop_todo/performance/redundant_block_call.yml
+++ /dev/null
@@ -1,12 +0,0 @@
----
-# Cop supports --auto-correct.
-Performance/RedundantBlockCall:
- Exclude:
- - 'ee/app/models/gitlab_subscription.rb'
- - 'ee/lib/ee/gitlab/auth/ldap/sync/proxy.rb'
- - 'lib/gitlab/auth/ldap/access.rb'
- - 'lib/gitlab/auth/ldap/adapter.rb'
- - 'lib/gitlab/database/bulk_update.rb'
- - 'lib/gitlab/http.rb'
- - 'lib/gitlab/safe_request_store.rb'
- - 'lib/gitlab/timeless.rb'
diff --git a/.rubocop_todo/performance/string_include.yml b/.rubocop_todo/performance/string_include.yml
index f477593c02a..2a2d0559397 100644
--- a/.rubocop_todo/performance/string_include.yml
+++ b/.rubocop_todo/performance/string_include.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Performance/StringInclude:
Exclude:
- 'app/models/snippet_repository.rb'
@@ -7,7 +7,6 @@ Performance/StringInclude:
- 'config/spring.rb'
- 'ee/app/models/ee/container_registry/event.rb'
- 'ee/lib/gitlab/auth/smartcard/certificate.rb'
- - 'lib/gitlab/database/migration_helpers.rb'
- 'lib/kramdown/parser/atlassian_document_format.rb'
- 'lib/prometheus/pid_provider.rb'
- 'spec/features/projects/jobs_spec.rb'
diff --git a/.rubocop_todo/rails/active_record_callbacks_order.yml b/.rubocop_todo/rails/active_record_callbacks_order.yml
deleted file mode 100644
index baeba86c4b9..00000000000
--- a/.rubocop_todo/rails/active_record_callbacks_order.yml
+++ /dev/null
@@ -1,24 +0,0 @@
----
-# Cop supports --auto-correct.
-Rails/ActiveRecordCallbacksOrder:
- Exclude:
- - 'app/models/award_emoji.rb'
- - 'app/models/gpg_key.rb'
- - 'app/models/group.rb'
- - 'app/models/issue.rb'
- - 'app/models/key.rb'
- - 'app/models/merge_request.rb'
- - 'app/models/namespace.rb'
- - 'app/models/namespace_statistics.rb'
- - 'app/models/note.rb'
- - 'app/models/pages_domain.rb'
- - 'app/models/personal_access_token.rb'
- - 'app/models/project.rb'
- - 'app/models/prometheus_alert.rb'
- - 'app/models/remote_mirror.rb'
- - 'app/models/resource_label_event.rb'
- - 'app/models/snippet_statistics.rb'
- - 'app/models/upload.rb'
- - 'app/models/user.rb'
- - 'ee/app/models/dast_site_profile.rb'
- - 'ee/app/models/geo_node.rb'
diff --git a/.rubocop_todo/rails/content_tag.yml b/.rubocop_todo/rails/content_tag.yml
deleted file mode 100644
index d54bd97cc08..00000000000
--- a/.rubocop_todo/rails/content_tag.yml
+++ /dev/null
@@ -1,9 +0,0 @@
----
-# Cop supports --autocorrect.
-Rails/ContentTag:
- Details: grace period
- Exclude:
- - 'app/helpers/avatars_helper.rb'
- - 'app/helpers/page_layout_helper.rb'
- - 'lib/gitlab/middleware/go.rb'
- - 'spec/helpers/avatars_helper_spec.rb'
diff --git a/.rubocop_todo/rails/file_path.yml b/.rubocop_todo/rails/file_path.yml
index 898d303bd3d..0a652631e0b 100644
--- a/.rubocop_todo/rails/file_path.yml
+++ b/.rubocop_todo/rails/file_path.yml
@@ -21,9 +21,6 @@ Rails/FilePath:
- 'db/post_migrate/20210630025020_migrate_push_event_payloads_event_id_back_to_integer_for_gitlab_com.rb'
- 'ee/app/helpers/ee/application_helper.rb'
- 'ee/app/services/security/security_orchestration_policies/project_create_service.rb'
- - 'ee/db/fixtures/development/20_vulnerabilities.rb'
- - 'ee/db/fixtures/development/21_dast_profiles.rb'
- - 'ee/db/fixtures/development/32_compliance_report_violations.rb'
- 'ee/lib/ee/feature/definition.rb'
- 'ee/lib/ee/gitlab/audit/type/definition.rb'
- 'ee/lib/ee/gitlab/usage/metric_definition.rb'
@@ -40,7 +37,6 @@ Rails/FilePath:
- 'ee/spec/models/release_highlight_spec.rb'
- 'ee/spec/requests/api/experiments_spec.rb'
- 'ee/spec/services/ee/merge_requests/refresh_service_spec.rb'
- - 'ee/spec/support/factory_bot.rb'
- 'ee/spec/validators/json_schema_validator_spec.rb'
- 'lib/api/api.rb'
- 'lib/error_tracking/collector/payload_validator.rb'
diff --git a/.rubocop_todo/rails/helper_instance_variable.yml b/.rubocop_todo/rails/helper_instance_variable.yml
index 53e376730fd..164edaece4e 100644
--- a/.rubocop_todo/rails/helper_instance_variable.yml
+++ b/.rubocop_todo/rails/helper_instance_variable.yml
@@ -61,7 +61,6 @@ Rails/HelperInstanceVariable:
- 'ee/app/helpers/ee/feature_flags_helper.rb'
- 'ee/app/helpers/ee/form_helper.rb'
- 'ee/app/helpers/ee/graph_helper.rb'
- - 'ee/app/helpers/ee/groups/group_members_helper.rb'
- 'ee/app/helpers/ee/groups/settings_helper.rb'
- 'ee/app/helpers/ee/groups_helper.rb'
- 'ee/app/helpers/ee/integrations_helper.rb'
@@ -75,7 +74,6 @@ Rails/HelperInstanceVariable:
- 'ee/app/helpers/ee/projects/security/configuration_helper.rb'
- 'ee/app/helpers/ee/projects_helper.rb'
- 'ee/app/helpers/ee/search_helper.rb'
- - 'ee/app/helpers/ee/selects_helper.rb'
- 'ee/app/helpers/ee/sorting_helper.rb'
- 'ee/app/helpers/ee/subscribable_banner_helper.rb'
- 'ee/app/helpers/ee/wiki_helper.rb'
diff --git a/.rubocop_todo/rails/http_status.yml b/.rubocop_todo/rails/http_status.yml
deleted file mode 100644
index 1a8ece49304..00000000000
--- a/.rubocop_todo/rails/http_status.yml
+++ /dev/null
@@ -1,9 +0,0 @@
----
-# Cop supports --auto-correct.
-Rails/HttpStatus:
- Exclude:
- - 'app/controllers/concerns/invisible_captcha_on_signup.rb'
- - 'app/controllers/projects/runner_projects_controller.rb'
- - 'app/controllers/projects/service_ping_controller.rb'
- - 'app/controllers/repositories/lfs_storage_controller.rb'
- - 'ee/app/controllers/trials_controller.rb'
diff --git a/.rubocop_todo/rails/index_with.yml b/.rubocop_todo/rails/index_with.yml
deleted file mode 100644
index b7bc2a26959..00000000000
--- a/.rubocop_todo/rails/index_with.yml
+++ /dev/null
@@ -1,53 +0,0 @@
----
-# Cop supports --auto-correct.
-Rails/IndexWith:
- Exclude:
- - 'app/helpers/ci/jobs_helper.rb'
- - 'app/models/ci/build_trace_chunk.rb'
- - 'app/models/ci/processable.rb'
- - 'app/models/concerns/cached_commit.rb'
- - 'app/models/customer_relations/organization.rb'
- - 'app/models/environment.rb'
- - 'app/services/concerns/rate_limited_service.rb'
- - 'app/services/packages/rpm/parse_package_service.rb'
- - 'db/post_migrate/20210731132939_backfill_stage_event_hash.rb'
- - 'ee/app/models/concerns/identity_verifiable.rb'
- - 'ee/app/models/vulnerabilities/projects_grade.rb'
- - 'ee/lib/ee/gitlab/usage_data.rb'
- - 'ee/lib/gitlab/auth/group_saml/auth_hash.rb'
- - 'ee/lib/gitlab/custom_file_templates.rb'
- - 'ee/lib/gitlab/insights/reducers/count_per_label_reducer.rb'
- - 'ee/spec/lib/ee/gitlab/application_context_spec.rb'
- - 'ee/spec/models/sca/license_compliance_spec.rb'
- - 'ee/spec/views/admin/dashboard/index.html.haml_spec.rb'
- - 'lib/api/entities/project_integration.rb'
- - 'lib/api/helpers/packages/conan/api_helpers.rb'
- - 'lib/banzai/filter/repository_link_filter.rb'
- - 'lib/gitlab/background_migration/backfill_note_discussion_id.rb'
- - 'lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb'
- - 'lib/gitlab/ci/ansi2html.rb'
- - 'lib/gitlab/ci/reports/security/finding.rb'
- - 'lib/gitlab/ci/reports/security/identifier.rb'
- - 'lib/gitlab/ci/reports/test_suite.rb'
- - 'lib/gitlab/database/count/exact_count_strategy.rb'
- - 'lib/gitlab/database/migration_helpers.rb'
- - 'lib/gitlab/database/obsolete_ignored_columns.rb'
- - 'lib/gitlab/database/tables_sorted_by_foreign_keys.rb'
- - 'lib/gitlab/issuable_metadata.rb'
- - 'lib/gitlab/template/base_template.rb'
- - 'lib/gitlab/usage_data.rb'
- - 'lib/google_api/cloud_platform/client.rb'
- - 'lib/tasks/gitlab/db.rake'
- - 'qa/qa/resource/reusable.rb'
- - 'scripts/trigger-build.rb'
- - 'spec/lib/gitlab/api_authentication/sent_through_builder_spec.rb'
- - 'spec/lib/gitlab/conflict/file_spec.rb'
- - 'spec/lib/gitlab/import_export/model_configuration_spec.rb'
- - 'spec/lib/gitlab/import_export/project/tree_restorer_spec.rb'
- - 'spec/lib/google_api/cloud_platform/client_spec.rb'
- - 'spec/models/event_spec.rb'
- - 'spec/presenters/projects/security/configuration_presenter_spec.rb'
- - 'spec/support/database/multiple_databases.rb'
- - 'spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb'
- - 'spec/support/shared_examples/models/concerns/sanitizable_shared_examples.rb'
- - 'spec/views/admin/dashboard/index.html.haml_spec.rb'
diff --git a/.rubocop_todo/rails/inverse_of.yml b/.rubocop_todo/rails/inverse_of.yml
index 2ad8d6204c8..a1f49aaf2f5 100644
--- a/.rubocop_todo/rails/inverse_of.yml
+++ b/.rubocop_todo/rails/inverse_of.yml
@@ -9,7 +9,6 @@ Rails/InverseOf:
- 'app/models/board.rb'
- 'app/models/bulk_imports/entity.rb'
- 'app/models/bulk_imports/tracker.rb'
- - 'app/models/ci/bridge.rb'
- 'app/models/ci/build.rb'
- 'app/models/ci/build_pending_state.rb'
- 'app/models/ci/build_trace_chunk.rb'
diff --git a/.rubocop_todo/rails/negate_include.yml b/.rubocop_todo/rails/negate_include.yml
index c3f9ac25e7e..74f469f325b 100644
--- a/.rubocop_todo/rails/negate_include.yml
+++ b/.rubocop_todo/rails/negate_include.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Rails/NegateInclude:
Details: grace period
Exclude:
diff --git a/.rubocop_todo/rails/pluck.yml b/.rubocop_todo/rails/pluck.yml
index 5e875daa569..e094a3397a6 100644
--- a/.rubocop_todo/rails/pluck.yml
+++ b/.rubocop_todo/rails/pluck.yml
@@ -1,10 +1,9 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Rails/Pluck:
- # Offense count: 155
- # Temporarily disabled due to too many offenses
- Enabled: false
+ Details: grace period
Exclude:
+ - 'app/controllers/ldap/omniauth_callbacks_controller.rb'
- 'app/finders/merge_requests/oldest_per_commit_finder.rb'
- 'app/helpers/issuables_description_templates_helper.rb'
- 'app/models/ci/unit_test.rb'
@@ -12,67 +11,184 @@ Rails/Pluck:
- 'app/models/concerns/sensitive_serializable_hash.rb'
- 'app/models/integrations/chat_message/pipeline_message.rb'
- 'app/models/list.rb'
+ - 'app/models/packages/go/module_version.rb'
+ - 'app/models/work_items/parent_link.rb'
- 'app/services/ci/pipeline_processing/atomic_processing_service/status_collection.rb'
- 'app/services/feature_flags/update_service.rb'
+ - 'app/services/todos/destroy/destroyed_issuable_service.rb'
+ - 'app/workers/database/ci_namespace_mirrors_consistency_check_worker.rb'
+ - 'app/workers/database/ci_project_mirrors_consistency_check_worker.rb'
+ - 'db/post_migrate/20210811122206_update_external_project_bots.rb'
+ - 'ee/app/graphql/mutations/incident_management/escalation_policy/base.rb'
- 'ee/app/graphql/mutations/incident_management/oncall_rotation/base.rb'
- 'ee/app/models/boards/epic_list.rb'
+ - 'ee/app/models/concerns/geo/verification_state.rb'
- 'ee/app/services/concerns/incident_management/oncall_rotations/shared_rotation_logic.rb'
- - 'ee/app/workers/geo/container_repository_sync_dispatch_worker.rb'
- 'ee/app/workers/geo/registry_sync_worker.rb'
- 'ee/app/workers/geo/repository_shard_sync_worker.rb'
- 'ee/app/workers/geo/repository_verification/secondary/shard_worker.rb'
- 'ee/app/workers/geo/scheduler/scheduler_worker.rb'
- 'ee/lib/ee/banzai/filter/references/iteration_reference_filter.rb'
- 'ee/lib/ee/gitlab/auth/ldap/person.rb'
+ - 'ee/lib/ee/gitlab/background_migration/delete_invalid_epic_issues.rb'
- 'ee/lib/ee/gitlab/background_migration/populate_uuids_for_security_findings.rb'
- 'ee/lib/ee/gitlab/checks/push_rules/file_size_check.rb'
+ - 'ee/lib/elastic/latest/custom_language_analyzers.rb'
- 'ee/lib/gitlab/ci/reports/license_scanning/report.rb'
+ - 'ee/lib/gitlab/search/index_curator.rb'
+ - 'ee/spec/controllers/autocomplete_controller_spec.rb'
+ - 'ee/spec/controllers/countries_controller_spec.rb'
- 'ee/spec/controllers/groups/audit_events_controller_spec.rb'
+ - 'ee/spec/controllers/operations_controller_spec.rb'
- 'ee/spec/controllers/projects/audit_events_controller_spec.rb'
+ - 'ee/spec/controllers/projects/feature_flag_issues_controller_spec.rb'
+ - 'ee/spec/controllers/projects/licenses_controller_spec.rb'
+ - 'ee/spec/controllers/projects/security/configuration_controller_spec.rb'
+ - 'ee/spec/features/projects/new_project_spec.rb'
+ - 'ee/spec/graphql/api/vulnerabilities_spec.rb'
+ - 'ee/spec/graphql/types/vulnerability_scanner_type_spec.rb'
- 'ee/spec/helpers/ee/geo_helper_spec.rb'
+ - 'ee/spec/helpers/ee/operations_helper_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/import_export/group/legacy_tree_saver_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/import_export/group/tree_saver_spec.rb'
- 'ee/spec/lib/gitlab/custom_file_templates_spec.rb'
- 'ee/spec/lib/gitlab/spdx/catalogue_spec.rb'
- 'ee/spec/models/analytics/cycle_analytics/group_level_spec.rb'
+ - 'ee/spec/models/concerns/geo/verification_state_spec.rb'
- 'ee/spec/models/dast_site_profile_spec.rb'
- 'ee/spec/models/integrations/chat_message/vulnerability_message_spec.rb'
+ - 'ee/spec/models/release_highlight_spec.rb'
+ - 'ee/spec/requests/api/analytics/code_review_analytics_spec.rb'
+ - 'ee/spec/requests/api/epic_links_spec.rb'
+ - 'ee/spec/requests/api/epics_spec.rb'
+ - 'ee/spec/requests/api/graphql/boards/board_lists_query_spec.rb'
+ - 'ee/spec/requests/api/graphql/boards/epic_boards_query_spec.rb'
+ - 'ee/spec/requests/api/graphql/boards/epic_lists_query_spec.rb'
+ - 'ee/spec/requests/api/graphql/ci/runners_spec.rb'
+ - 'ee/spec/requests/api/graphql/epics/epic_resolver_spec.rb'
+ - 'ee/spec/requests/api/graphql/iterations/cadences_spec.rb'
+ - 'ee/spec/requests/api/graphql/iterations/iterations_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/analytics/devops_adoption/enabled_namespaces/bulk_enable_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/epics/update_spec.rb'
- 'ee/spec/requests/api/graphql/mutations/releases/create_spec.rb'
- 'ee/spec/requests/api/graphql/mutations/releases/update_spec.rb'
+ - 'ee/spec/requests/api/graphql/project/dast_profiles_spec.rb'
+ - 'ee/spec/requests/api/graphql/project/dast_site_validations_spec.rb'
+ - 'ee/spec/requests/api/graphql/project/incident_management/escalation_policy/rules_spec.rb'
+ - 'ee/spec/requests/api/graphql/project/pipeline/code_quality_reports_spec.rb'
+ - 'ee/spec/requests/api/graphql/vulnerabilities/issue_links_spec.rb'
+ - 'ee/spec/requests/api/groups_spec.rb'
+ - 'ee/spec/requests/api/iterations_spec.rb'
+ - 'ee/spec/requests/api/members_spec.rb'
+ - 'ee/spec/requests/api/merge_request_approval_rules_spec.rb'
+ - 'ee/spec/requests/api/project_approval_rules_spec.rb'
+ - 'ee/spec/requests/api/project_approval_settings_spec.rb'
+ - 'ee/spec/requests/api/projects_spec.rb'
+ - 'ee/spec/requests/api/protected_branches_spec.rb'
+ - 'ee/spec/requests/api/protected_environments_spec.rb'
+ - 'ee/spec/requests/api/protected_tags_spec.rb'
+ - 'ee/spec/requests/api/releases_spec.rb'
+ - 'ee/spec/requests/api/search_spec.rb'
+ - 'ee/spec/requests/api/status_checks_spec.rb'
+ - 'ee/spec/requests/api/users_spec.rb'
+ - 'ee/spec/requests/api/vulnerabilities_spec.rb'
+ - 'ee/spec/requests/api/vulnerability_findings_spec.rb'
+ - 'ee/spec/requests/api/vulnerability_issue_links_spec.rb'
+ - 'ee/spec/requests/ee/groups/autocomplete_sources_spec.rb'
+ - 'ee/spec/requests/groups/protected_environments_controller_spec.rb'
+ - 'ee/spec/requests/projects/issue_feature_flags_controller_spec.rb'
- 'ee/spec/services/analytics/cycle_analytics/stages/update_service_spec.rb'
- 'ee/spec/services/analytics/cycle_analytics/value_streams/create_service_spec.rb'
- 'ee/spec/services/analytics/cycle_analytics/value_streams/update_service_spec.rb'
- 'ee/spec/services/ee/groups/autocomplete_service_spec.rb'
+ - 'ee/spec/services/ee/releases/create_evidence_service_spec.rb'
+ - 'ee/spec/services/iterations/cadences/create_iterations_in_advance_service_spec.rb'
- 'ee/spec/services/quick_actions/interpret_service_spec.rb'
+ - 'ee/spec/support/elastic.rb'
+ - 'ee/spec/support/helpers/feature_approval_helper.rb'
+ - 'ee/spec/support/shared_examples/controllers/analytics/cycle_analytics/shared_stage_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/requests/api/graphql/incident_management/escalation_policies_shared_examples.rb'
+ - 'lib/atlassian/jira_connect/client.rb'
- 'lib/banzai/filter/references/label_reference_filter.rb'
- 'lib/banzai/filter/references/milestone_reference_filter.rb'
- 'lib/banzai/renderer.rb'
- 'lib/gitlab/access.rb'
- 'lib/gitlab/analytics/cycle_analytics/default_stages.rb'
+ - 'lib/gitlab/bullet/exclusions.rb'
- 'lib/gitlab/checks/lfs_check.rb'
- 'lib/gitlab/ci/pipeline/chain/build/associations.rb'
- 'lib/gitlab/ci/pipeline/chain/validate/external.rb'
- 'lib/gitlab/ci/variables/collection/sort.rb'
- 'lib/gitlab/config/entry/validators.rb'
+ - 'lib/gitlab/cycle_analytics/updater.rb'
+ - 'lib/gitlab/database/migration_helpers.rb'
+ - 'lib/gitlab/database/partitioning_migration_helpers/index_helpers.rb'
- 'lib/gitlab/git_access.rb'
+ - 'lib/gitlab/github_import/representation/issue.rb'
+ - 'lib/gitlab/jira_import/metadata_collector.rb'
+ - 'lib/gitlab/merge_requests/message_generator.rb'
- 'lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb'
- 'lib/gitlab/metrics/dashboard/stages/custom_metrics_details_inserter.rb'
- 'lib/gitlab/sidekiq_config/cli_methods.rb'
+ - 'lib/gitlab/sql/pattern.rb'
- 'lib/gitlab/usage/metrics/name_suggestion.rb'
- 'lib/gitlab/usage_data_counters/hll_redis_counter.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/jira/jira_basic_integration_spec.rb'
- - 'qa/qa/support/page_error_checker.rb'
+ - 'lib/gitlab/zentao/client.rb'
+ - 'lib/tasks/gitlab/info.rake'
+ - 'qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb'
+ - 'qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/1_manage/integrations/jira/jira_basic_integration_spec.rb'
+ - 'qa/qa/tools/ci/qa_changes.rb'
+ - 'qa/qa/tools/delete_projects.rb'
+ - 'qa/qa/tools/delete_test_snippets.rb'
+ - 'qa/qa/tools/delete_test_ssh_keys.rb'
+ - 'qa/qa/tools/delete_test_users.rb'
+ - 'qa/qa/tools/delete_user_projects.rb'
- 'scripts/perf/query_limiting_report.rb'
- 'spec/config/mail_room_spec.rb'
- - 'spec/config/metrics/aggregates/aggregated_metrics_spec.rb'
+ - 'spec/controllers/autocomplete_controller_spec.rb'
+ - 'spec/controllers/dashboard/milestones_controller_spec.rb'
+ - 'spec/controllers/groups/labels_controller_spec.rb'
+ - 'spec/controllers/groups/milestones_controller_spec.rb'
+ - 'spec/controllers/groups/releases_controller_spec.rb'
+ - 'spec/controllers/groups/shared_projects_controller_spec.rb'
+ - 'spec/controllers/projects/analytics/cycle_analytics/stages_controller_spec.rb'
+ - 'spec/controllers/projects/autocomplete_sources_controller_spec.rb'
+ - 'spec/controllers/projects/environments_controller_spec.rb'
+ - 'spec/controllers/projects/feature_flags_controller_spec.rb'
+ - 'spec/controllers/projects/issues_controller_spec.rb'
+ - 'spec/controllers/projects/jobs_controller_spec.rb'
+ - 'spec/controllers/projects/merge_requests/conflicts_controller_spec.rb'
+ - 'spec/controllers/projects/merge_requests/diffs_controller_spec.rb'
+ - 'spec/controllers/projects/pipelines/tests_controller_spec.rb'
+ - 'spec/controllers/projects/releases_controller_spec.rb'
+ - 'spec/controllers/projects/starrers_controller_spec.rb'
+ - 'spec/db/schema_spec.rb'
+ - 'spec/features/issues/csv_spec.rb'
+ - 'spec/features/merge_request/user_sees_versions_spec.rb'
- 'spec/finders/license_template_finder_spec.rb'
- 'spec/graphql/resolvers/ci/test_suite_resolver_spec.rb'
+ - 'spec/graphql/resolvers/concerns/looks_ahead_spec.rb'
+ - 'spec/graphql/resolvers/namespace_projects_resolver_spec.rb'
+ - 'spec/graphql/types/ci/job_token_scope_type_spec.rb'
+ - 'spec/graphql/types/snippet_type_spec.rb'
- 'spec/helpers/groups/group_members_helper_spec.rb'
+ - 'spec/helpers/projects/project_members_helper_spec.rb'
- 'spec/lib/atlassian/jira_connect/serializers/pull_request_entity_spec.rb'
- 'spec/lib/banzai/reference_redactor_spec.rb'
+ - 'spec/lib/bulk_imports/groups/stage_spec.rb'
+ - 'spec/lib/bulk_imports/projects/stage_spec.rb'
- 'spec/lib/gitlab/ci/ansi2json/line_spec.rb'
- 'spec/lib/gitlab/ci/yaml_processor_spec.rb'
- 'spec/lib/gitlab/conflict/file_spec.rb'
+ - 'spec/lib/gitlab/database/load_balancing/transaction_leaking_spec.rb'
+ - 'spec/lib/gitlab/database/similarity_score_spec.rb'
+ - 'spec/lib/gitlab/database_importers/common_metrics/importer_spec.rb'
- 'spec/lib/gitlab/git/blame_spec.rb'
- 'spec/lib/gitlab/git/conflict/parser_spec.rb'
+ - 'spec/lib/gitlab/import_export/group/legacy_tree_saver_spec.rb'
+ - 'spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb'
+ - 'spec/lib/gitlab/import_export/project/tree_saver_spec.rb'
- 'spec/lib/gitlab/language_detection_spec.rb'
- 'spec/lib/gitlab/lograge/custom_options_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/processor_spec.rb'
@@ -80,9 +196,11 @@ Rails/Pluck:
- 'spec/lib/gitlab/relative_positioning/item_context_spec.rb'
- 'spec/lib/gitlab/search/query_spec.rb'
- 'spec/lib/gitlab/sidekiq_config/worker_matcher_spec.rb'
+ - 'spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb'
- 'spec/lib/gitlab/tree_summary_spec.rb'
- 'spec/lib/peek/views/rugged_spec.rb'
- 'spec/migrations/20210713042000_fix_ci_sources_pipelines_index_names_spec.rb'
+ - 'spec/models/bulk_imports/entity_spec.rb'
- 'spec/models/ci/bridge_spec.rb'
- 'spec/models/ci/build_spec.rb'
- 'spec/models/ci/pipeline_spec.rb'
@@ -94,12 +212,98 @@ Rails/Pluck:
- 'spec/models/project_spec.rb'
- 'spec/presenters/packages/detail/package_presenter_spec.rb'
- 'spec/presenters/packages/nuget/service_index_presenter_spec.rb'
+ - 'spec/requests/api/admin/instance_clusters_spec.rb'
+ - 'spec/requests/api/branches_spec.rb'
+ - 'spec/requests/api/ci/pipeline_schedules_spec.rb'
+ - 'spec/requests/api/ci/pipelines_spec.rb'
+ - 'spec/requests/api/commit_statuses_spec.rb'
+ - 'spec/requests/api/commits_spec.rb'
+ - 'spec/requests/api/deploy_tokens_spec.rb'
+ - 'spec/requests/api/deployments_spec.rb'
+ - 'spec/requests/api/events_spec.rb'
+ - 'spec/requests/api/feature_flags_spec.rb'
+ - 'spec/requests/api/feature_flags_user_lists_spec.rb'
+ - 'spec/requests/api/files_spec.rb'
+ - 'spec/requests/api/graphql/boards/board_list_issues_query_spec.rb'
+ - 'spec/requests/api/graphql/ci/manual_variables_spec.rb'
+ - 'spec/requests/api/graphql/ci/pipelines_spec.rb'
+ - 'spec/requests/api/graphql/ci/runners_spec.rb'
+ - 'spec/requests/api/graphql/group/timelogs_spec.rb'
+ - 'spec/requests/api/graphql/group_query_spec.rb'
+ - 'spec/requests/api/graphql/mutations/design_management/move_spec.rb'
+ - 'spec/requests/api/graphql/packages/package_spec.rb'
+ - 'spec/requests/api/graphql/project/alert_management/alerts_spec.rb'
+ - 'spec/requests/api/graphql/project/base_service_spec.rb'
+ - 'spec/requests/api/graphql/project/issue/design_collection/version_spec.rb'
+ - 'spec/requests/api/graphql/project/issue/designs/designs_spec.rb'
+ - 'spec/requests/api/graphql/project/issues_spec.rb'
+ - 'spec/requests/api/graphql/project/jira_import_spec.rb'
+ - 'spec/requests/api/graphql/project/jira_projects_spec.rb'
+ - 'spec/requests/api/graphql/project/release_spec.rb'
+ - 'spec/requests/api/graphql/project/releases_spec.rb'
+ - 'spec/requests/api/group_clusters_spec.rb'
+ - 'spec/requests/api/group_labels_spec.rb'
+ - 'spec/requests/api/group_milestones_spec.rb'
+ - 'spec/requests/api/group_packages_spec.rb'
+ - 'spec/requests/api/groups_spec.rb'
+ - 'spec/requests/api/invitations_spec.rb'
+ - 'spec/requests/api/issues/get_project_issues_spec.rb'
+ - 'spec/requests/api/labels_spec.rb'
+ - 'spec/requests/api/members_spec.rb'
+ - 'spec/requests/api/merge_requests_spec.rb'
+ - 'spec/requests/api/namespaces_spec.rb'
+ - 'spec/requests/api/package_files_spec.rb'
+ - 'spec/requests/api/pages_domains_spec.rb'
+ - 'spec/requests/api/personal_access_tokens_spec.rb'
+ - 'spec/requests/api/project_clusters_spec.rb'
+ - 'spec/requests/api/project_container_repositories_spec.rb'
+ - 'spec/requests/api/project_events_spec.rb'
+ - 'spec/requests/api/project_milestones_spec.rb'
+ - 'spec/requests/api/project_snippets_spec.rb'
+ - 'spec/requests/api/project_templates_spec.rb'
+ - 'spec/requests/api/projects_spec.rb'
+ - 'spec/requests/api/protected_branches_spec.rb'
+ - 'spec/requests/api/protected_tags_spec.rb'
+ - 'spec/requests/api/releases_spec.rb'
+ - 'spec/requests/api/repositories_spec.rb'
+ - 'spec/requests/api/resource_access_tokens_spec.rb'
+ - 'spec/requests/api/snippets_spec.rb'
+ - 'spec/requests/api/tags_spec.rb'
+ - 'spec/requests/api/templates_spec.rb'
+ - 'spec/requests/api/todos_spec.rb'
+ - 'spec/requests/api/topics_spec.rb'
+ - 'spec/requests/api/unleash_spec.rb'
+ - 'spec/requests/api/users_spec.rb'
+ - 'spec/requests/api/v3/github_spec.rb'
+ - 'spec/requests/groups/autocomplete_sources_spec.rb'
+ - 'spec/requests/groups/milestones_controller_spec.rb'
+ - 'spec/requests/jwks_controller_spec.rb'
+ - 'spec/requests/lfs_http_spec.rb'
- 'spec/serializers/ci/dag_pipeline_entity_spec.rb'
- 'spec/serializers/ci/pipeline_entity_spec.rb'
- 'spec/serializers/diff_file_entity_spec.rb'
- 'spec/serializers/stage_entity_spec.rb'
+ - 'spec/services/ci/compare_test_reports_service_spec.rb'
- 'spec/services/ci/pipeline_processing/atomic_processing_service/status_collection_spec.rb'
+ - 'spec/services/clusters/applications/prometheus_config_service_spec.rb'
+ - 'spec/services/issues/export_csv_service_spec.rb'
- 'spec/services/metrics/dashboard/custom_metric_embed_service_spec.rb'
- 'spec/services/projects/participants_service_spec.rb'
+ - 'spec/support/helpers/api_helpers.rb'
+ - 'spec/support/helpers/graphql_helpers.rb'
+ - 'spec/support/matchers/background_migrations_matchers.rb'
- 'spec/support/shared_contexts/markdown_golden_master_shared_examples.rb'
+ - 'spec/support/shared_examples/graphql/mutation_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/labels_api_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/milestones_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/notes_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb'
+ - 'spec/support/shared_examples/services/packages_shared_examples.rb'
+ - 'spec/tooling/docs/deprecation_handling_spec.rb'
- 'tooling/danger/sidekiq_queues.rb'
+ - 'tooling/docs/deprecation_handling.rb'
diff --git a/.rubocop_todo/rails/redundant_foreign_key.yml b/.rubocop_todo/rails/redundant_foreign_key.yml
index 0d23c51caae..3251429876e 100644
--- a/.rubocop_todo/rails/redundant_foreign_key.yml
+++ b/.rubocop_todo/rails/redundant_foreign_key.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Rails/RedundantForeignKey:
Exclude:
- 'app/models/alert_management/metric_image.rb'
diff --git a/.rubocop_todo/rails/time_zone.yml b/.rubocop_todo/rails/time_zone.yml
index f1e7d519a8f..378c00c1063 100644
--- a/.rubocop_todo/rails/time_zone.yml
+++ b/.rubocop_todo/rails/time_zone.yml
@@ -1,126 +1,110 @@
---
+# Cop supports --autocorrect.
Rails/TimeZone:
- Enabled: true
Exclude:
- - lib/gitlab/popen.rb
- - ee/lib/delay.rb
- - ee/lib/gitlab/elastic/helper.rb
- - ee/lib/gitlab/elastic/indexer.rb
- - ee/lib/gitlab/geo/event_gap_tracking.rb
- - ee/lib/gitlab/geo/log_cursor/events/design_repository_updated_event.rb
- - ee/lib/gitlab/geo/log_cursor/events/repository_updated_event.rb
- - ee/lib/gitlab/geo/log_cursor/logger.rb
- - ee/lib/gitlab/geo/oauth/login_state.rb
- - ee/lib/gitlab/prometheus/queries/cluster_query.rb
- - ee/spec/lib/ee/gitlab/checks/push_rules/commit_check_spec.rb
- - ee/spec/lib/ee/gitlab/ci/pipeline/quota/job_activity_spec.rb
- - ee/spec/lib/gitlab/analytics/cycle_analytics/data_collector_spec.rb
- - ee/spec/lib/gitlab/analytics/cycle_analytics/summary/group/stage_summary_spec.rb
- - ee/spec/lib/gitlab/auth/ldap/access_spec.rb
- - ee/spec/lib/gitlab/auth/smartcard/san_extension_spec.rb
- - ee/spec/lib/gitlab/auth/smartcard/session_spec.rb
- - ee/spec/lib/gitlab/elastic/client_spec.rb
- - ee/spec/lib/gitlab/geo/base_request_spec.rb
- - ee/spec/lib/gitlab/geo/log_cursor/events/cache_invalidation_event_spec.rb
- - ee/spec/lib/gitlab/geo/log_cursor/events/container_repository_updated_event_spec.rb
- - ee/spec/lib/gitlab/geo/log_cursor/events/design_repository_updated_event_spec.rb
- - ee/spec/lib/gitlab/geo/log_cursor/events/event_spec.rb
- - ee/spec/lib/gitlab/geo/log_cursor/events/hashed_storage_attachments_event_spec.rb
- - ee/spec/lib/gitlab/geo/log_cursor/events/hashed_storage_migrated_event_spec.rb
- - ee/spec/lib/gitlab/geo/log_cursor/events/repositories_changed_event_spec.rb
- - ee/spec/lib/gitlab/geo/log_cursor/events/repository_created_event_spec.rb
- - ee/spec/lib/gitlab/geo/log_cursor/events/repository_deleted_event_spec.rb
- - ee/spec/lib/gitlab/geo/log_cursor/events/repository_renamed_event_spec.rb
- - ee/spec/lib/gitlab/geo/log_cursor/events/repository_updated_event_spec.rb
- - ee/spec/lib/gitlab/geo/log_cursor/events/reset_checksum_event_spec.rb
- - ee/spec/lib/gitlab/geo/log_cursor/logger_spec.rb
- - ee/spec/lib/gitlab/git_access_spec.rb
- - ee/spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb
- - ee/spec/lib/gitlab/prometheus/queries/cluster_query_spec.rb
- - lib/api/helpers.rb
- - lib/api/sidekiq_metrics.rb
- - lib/backup/manager.rb
- - lib/bitbucket_server/representation/base.rb
- - lib/gitlab/auth/current_user_mode.rb
- - lib/gitlab/auth/ldap/access.rb
- - lib/gitlab/chaos.rb
- - lib/gitlab/checks/timed_logger.rb
- - lib/gitlab/ci/pipeline/duration.rb
- - lib/gitlab/database.rb
- - lib/gitlab/external_authorization/access.rb
- - lib/gitlab/external_authorization/cache.rb
- - lib/gitlab/gitaly_client.rb
- - lib/gitlab/gitaly_client/ref_service.rb
- - lib/gitlab/github_import/representation.rb
- - lib/gitlab/health_checks/base_abstract_check.rb
- - lib/gitlab/import_export.rb
- - lib/gitlab/instrumentation/elasticsearch_transport.rb
- - lib/gitlab/instrumentation_helper.rb
- - lib/gitlab/lfs_token.rb
- - lib/gitlab/loop_helpers.rb
- - lib/gitlab/phabricator_import/representation/task.rb
- - lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb
- - lib/gitlab/prometheus/queries/matched_metric_query.rb
- - lib/gitlab/prometheus_client.rb
- - lib/gitlab/task_helpers.rb
- - lib/gitlab/x509/tag.rb
- - lib/grafana/time_window.rb
- - lib/json_web_token/token.rb
- - lib/object_storage/direct_upload.rb
- - lib/quality/seeders/issues.rb
- - lib/tasks/gitlab/assets.rake
- - lib/tasks/gitlab/backup.rake
- - lib/tasks/gitlab/cleanup.rake
- - lib/tasks/gitlab/list_repos.rake
- - spec/lib/api/helpers_spec.rb
- - spec/lib/gitlab/analytics/cycle_analytics/base_query_builder_spec.rb
- - spec/lib/gitlab/app_json_logger_spec.rb
- - spec/lib/gitlab/app_text_logger_spec.rb
- - spec/lib/gitlab/auth/current_user_mode_spec.rb
- - spec/lib/gitlab/bitbucket_import/importer_spec.rb
- - spec/lib/gitlab/bitbucket_server_import/importer_spec.rb
- - spec/lib/gitlab/checks/timed_logger_spec.rb
- - spec/lib/gitlab/ci/cron_parser_spec.rb
- - spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
- - spec/lib/gitlab/data_builder/note_spec.rb
- - spec/lib/gitlab/database/background_migration_job_spec.rb
- - spec/lib/gitlab/database_spec.rb
- - spec/lib/gitlab/discussions_diff/file_collection_spec.rb
- - spec/lib/gitlab/external_authorization/access_spec.rb
- - spec/lib/gitlab/external_authorization/cache_spec.rb
- - spec/lib/gitlab/external_authorization/logger_spec.rb
- - spec/lib/gitlab/fogbugz_import/importer_spec.rb
- - spec/lib/gitlab/git/branch_spec.rb
- - spec/lib/gitlab/git/commit_spec.rb
- - spec/lib/gitlab/git/repository_spec.rb
- - spec/lib/gitlab/git_access_spec.rb
- - spec/lib/gitlab/github_import/importer/issue_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/issues_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/note_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/releases_importer_spec.rb
- - spec/lib/gitlab/github_import/representation/diff_note_spec.rb
- - spec/lib/gitlab/github_import/representation/issue_spec.rb
- - spec/lib/gitlab/github_import/representation/note_spec.rb
- - spec/lib/gitlab/github_import/representation/pull_request_spec.rb
- - spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb
- - spec/lib/gitlab/grape_logging/loggers/cloudflare_logger_spec.rb
- - spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb
- - spec/lib/gitlab/graphql_logger_spec.rb
- - spec/lib/gitlab/graphs/commits_spec.rb
- - spec/lib/gitlab/import_export/project/relation_factory_spec.rb
- - spec/lib/gitlab/json_logger_spec.rb
- - spec/lib/gitlab/lfs_token_spec.rb
- - spec/lib/gitlab/log_timestamp_formatter_spec.rb
- - spec/lib/gitlab/middleware/rails_queue_duration_spec.rb
- - spec/lib/gitlab/phabricator_import/issues/task_importer_spec.rb
- - spec/lib/gitlab/phabricator_import/representation/task_spec.rb
- - spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb
- - spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb
- - spec/lib/gitlab/prometheus/queries/validate_query_spec.rb
- - spec/lib/gitlab/sidekiq_logging/json_formatter_spec.rb
- - spec/lib/gitlab/utils/json_size_estimator_spec.rb
- - spec/lib/gitlab/x509/signature_spec.rb
- - spec/lib/grafana/time_window_spec.rb
- - spec/lib/json_web_token/hmac_token_spec.rb
+ - 'ee/lib/delay.rb'
+ - 'ee/lib/gitlab/elastic/indexer.rb'
+ - 'ee/lib/gitlab/geo/event_gap_tracking.rb'
+ - 'ee/lib/gitlab/geo/log_cursor/events/design_repository_updated_event.rb'
+ - 'ee/lib/gitlab/geo/log_cursor/events/repository_updated_event.rb'
+ - 'ee/lib/gitlab/geo/log_cursor/logger.rb'
+ - 'ee/lib/gitlab/geo/oauth/login_state.rb'
+ - 'ee/spec/lib/gitlab/geo/base_request_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/cache_invalidation_event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/design_repository_updated_event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/hashed_storage_attachments_event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/hashed_storage_migrated_event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/repositories_changed_event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/repository_created_event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/repository_deleted_event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/repository_renamed_event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/repository_updated_event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/reset_checksum_event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/logger_spec.rb'
+ - 'ee/spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb'
+ - 'ee/spec/lib/gitlab/prometheus/queries/cluster_query_spec.rb'
+ - 'lib/api/helpers.rb'
+ - 'lib/api/sidekiq_metrics.rb'
+ - 'lib/bitbucket_server/representation/base.rb'
+ - 'lib/gitlab/auth/current_user_mode.rb'
+ - 'lib/gitlab/auth/ldap/access.rb'
+ - 'lib/gitlab/chaos.rb'
+ - 'lib/gitlab/checks/timed_logger.rb'
+ - 'lib/gitlab/ci/pipeline/duration.rb'
+ - 'lib/gitlab/database.rb'
+ - 'lib/gitlab/external_authorization/access.rb'
+ - 'lib/gitlab/external_authorization/cache.rb'
+ - 'lib/gitlab/gitaly_client.rb'
+ - 'lib/gitlab/gitaly_client/ref_service.rb'
+ - 'lib/gitlab/github_import/representation.rb'
+ - 'lib/gitlab/health_checks/base_abstract_check.rb'
+ - 'lib/gitlab/import_export.rb'
+ - 'lib/gitlab/instrumentation/elasticsearch_transport.rb'
+ - 'lib/gitlab/instrumentation_helper.rb'
+ - 'lib/gitlab/lfs_token.rb'
+ - 'lib/gitlab/loop_helpers.rb'
+ - 'lib/gitlab/phabricator_import/representation/task.rb'
+ - 'lib/gitlab/popen.rb'
+ - 'lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb'
+ - 'lib/gitlab/prometheus/queries/matched_metric_query.rb'
+ - 'lib/gitlab/prometheus_client.rb'
+ - 'lib/gitlab/task_helpers.rb'
+ - 'lib/gitlab/x509/tag.rb'
+ - 'lib/grafana/time_window.rb'
+ - 'lib/json_web_token/token.rb'
+ - 'lib/object_storage/direct_upload.rb'
+ - 'lib/quality/seeders/issues.rb'
+ - 'lib/tasks/gitlab/assets.rake'
+ - 'lib/tasks/gitlab/backup.rake'
+ - 'lib/tasks/gitlab/cleanup.rake'
+ - 'lib/tasks/gitlab/list_repos.rake'
+ - 'spec/lib/api/helpers_spec.rb'
+ - 'spec/lib/gitlab/analytics/cycle_analytics/base_query_builder_spec.rb'
+ - 'spec/lib/gitlab/app_json_logger_spec.rb'
+ - 'spec/lib/gitlab/app_text_logger_spec.rb'
+ - 'spec/lib/gitlab/auth/current_user_mode_spec.rb'
+ - 'spec/lib/gitlab/bitbucket_import/importer_spec.rb'
+ - 'spec/lib/gitlab/bitbucket_server_import/importer_spec.rb'
+ - 'spec/lib/gitlab/checks/timed_logger_spec.rb'
+ - 'spec/lib/gitlab/ci/cron_parser_spec.rb'
+ - 'spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb'
+ - 'spec/lib/gitlab/data_builder/note_spec.rb'
+ - 'spec/lib/gitlab/database/background_migration_job_spec.rb'
+ - 'spec/lib/gitlab/database_spec.rb'
+ - 'spec/lib/gitlab/discussions_diff/file_collection_spec.rb'
+ - 'spec/lib/gitlab/external_authorization/access_spec.rb'
+ - 'spec/lib/gitlab/external_authorization/cache_spec.rb'
+ - 'spec/lib/gitlab/external_authorization/logger_spec.rb'
+ - 'spec/lib/gitlab/fogbugz_import/importer_spec.rb'
+ - 'spec/lib/gitlab/git/commit_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/issue_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/issues_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/note_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/releases_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/representation/diff_note_spec.rb'
+ - 'spec/lib/gitlab/github_import/representation/issue_spec.rb'
+ - 'spec/lib/gitlab/github_import/representation/note_spec.rb'
+ - 'spec/lib/gitlab/github_import/representation/pull_request_spec.rb'
+ - 'spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb'
+ - 'spec/lib/gitlab/grape_logging/loggers/cloudflare_logger_spec.rb'
+ - 'spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb'
+ - 'spec/lib/gitlab/graphql_logger_spec.rb'
+ - 'spec/lib/gitlab/graphs/commits_spec.rb'
+ - 'spec/lib/gitlab/import_export/project/relation_factory_spec.rb'
+ - 'spec/lib/gitlab/json_logger_spec.rb'
+ - 'spec/lib/gitlab/lfs_token_spec.rb'
+ - 'spec/lib/gitlab/log_timestamp_formatter_spec.rb'
+ - 'spec/lib/gitlab/middleware/rails_queue_duration_spec.rb'
+ - 'spec/lib/gitlab/phabricator_import/issues/task_importer_spec.rb'
+ - 'spec/lib/gitlab/phabricator_import/representation/task_spec.rb'
+ - 'spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb'
+ - 'spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb'
+ - 'spec/lib/gitlab/prometheus/queries/validate_query_spec.rb'
+ - 'spec/lib/gitlab/sidekiq_logging/json_formatter_spec.rb'
+ - 'spec/lib/gitlab/utils/json_size_estimator_spec.rb'
+ - 'spec/lib/gitlab/x509/signature_spec.rb'
+ - 'spec/lib/grafana/time_window_spec.rb'
+ - 'spec/lib/json_web_token/hmac_token_spec.rb'
diff --git a/.rubocop_todo/rake/require.yml b/.rubocop_todo/rake/require.yml
index 5042f0d504e..07138d6b622 100644
--- a/.rubocop_todo/rake/require.yml
+++ b/.rubocop_todo/rake/require.yml
@@ -22,5 +22,4 @@ Rake/Require:
- 'lib/tasks/gitlab/x509/update.rake'
- 'lib/tasks/import.rake'
- 'lib/tasks/tokens.rake'
- - 'qa/tasks/ci.rake'
- 'qa/tasks/webdrivers.rake'
diff --git a/.rubocop_todo/rspec/any_instance_of.yml b/.rubocop_todo/rspec/any_instance_of.yml
index 5ee00cb0e24..f24fbbfbed2 100644
--- a/.rubocop_todo/rspec/any_instance_of.yml
+++ b/.rubocop_todo/rspec/any_instance_of.yml
@@ -1,463 +1,401 @@
---
+# Cop supports --autocorrect.
RSpec/AnyInstanceOf:
Exclude:
- - ee/spec/controllers/admin/geo/nodes_controller_spec.rb
- - ee/spec/controllers/ee/groups_controller_spec.rb
- - ee/spec/controllers/groups/analytics/productivity_analytics_controller_spec.rb
- - ee/spec/controllers/groups/epics/notes_controller_spec.rb
- - ee/spec/controllers/groups/omniauth_callbacks_controller_spec.rb
- - ee/spec/controllers/oauth/geo_auth_controller_spec.rb
- - ee/spec/controllers/projects/environments_controller_spec.rb
- - ee/spec/controllers/projects/integrations/jira/issues_controller_spec.rb
- - ee/spec/controllers/projects/merge_requests_controller_spec.rb
- - ee/spec/controllers/projects/path_locks_controller_spec.rb
- - ee/spec/controllers/projects_controller_spec.rb
- - ee/spec/controllers/subscriptions_controller_spec.rb
- - ee/spec/features/admin/admin_audit_logs_spec.rb
- - ee/spec/features/admin/admin_reset_pipeline_minutes_spec.rb
- - ee/spec/features/admin/admin_users_spec.rb
- - ee/spec/features/boards/scoped_issue_board_spec.rb
- - ee/spec/features/ci_shared_runner_warnings_spec.rb
- - ee/spec/features/groups/group_settings_spec.rb
- - ee/spec/features/groups/navbar_spec.rb
- - ee/spec/features/groups/saml_providers_spec.rb
- - ee/spec/features/issues/form_spec.rb
- - ee/spec/features/merge_request/user_creates_merge_request_spec.rb
- - ee/spec/features/projects/new_project_spec.rb
- - ee/spec/features/registrations/welcome_spec.rb
- - ee/spec/features/security/project/internal_access_spec.rb
- - ee/spec/features/security/project/private_access_spec.rb
- - ee/spec/features/security/project/public_access_spec.rb
- - ee/spec/features/trials/select_namespace_spec.rb
- - ee/spec/features/users/login_spec.rb
- - ee/spec/graphql/mutations/dast_on_demand_scans/create_spec.rb
- - ee/spec/graphql/mutations/incident_management/oncall_schedule/create_spec.rb
- - ee/spec/graphql/mutations/incident_management/oncall_schedule/destroy_spec.rb
- - ee/spec/graphql/mutations/incident_management/oncall_schedule/update_spec.rb
- - ee/spec/helpers/application_helper_spec.rb
- - ee/spec/lib/ee/api/helpers_spec.rb
- - ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb
- - ee/spec/lib/ee/gitlab/checks/push_rule_check_spec.rb
- - ee/spec/lib/ee/gitlab/checks/push_rules/commit_check_spec.rb
- - ee/spec/lib/gitlab/auth/group_saml/membership_enforcer_spec.rb
- - ee/spec/lib/gitlab/auth/ldap/access_spec.rb
- - ee/spec/lib/gitlab/ci/templates/Jobs/browser_performance_testing_gitlab_ci_yaml_spec.rb
- - ee/spec/lib/gitlab/ci/templates/Jobs/dast_default_branch_gitlab_ci_yaml_spec.rb
- - ee/spec/lib/gitlab/ci/templates/Jobs/load_performance_testing_gitlab_ci_yaml_spec.rb
- - ee/spec/lib/gitlab/ci/templates/Verify/browser_performance_testing_gitlab_ci_yaml_spec.rb
- - ee/spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb
- - ee/spec/lib/gitlab/ci/templates/container_scanning_gitlab_ci_yaml_spec.rb
- - ee/spec/lib/gitlab/ci/templates/coverage_fuzzing_gitlab_ci_yaml_spec.rb
- - ee/spec/lib/gitlab/ci/templates/dast_gitlab_ci_yaml_spec.rb
- - ee/spec/lib/gitlab/ci/templates/dependency_scanning_gitlab_ci_yaml_spec.rb
- - ee/spec/lib/gitlab/ci/templates/license_scanning_gitlab_ci_yaml_spec.rb
- - ee/spec/lib/gitlab/ci/templates/sast_gitlab_ci_yaml_spec.rb
- - ee/spec/lib/gitlab/elastic/project_search_results_spec.rb
- - ee/spec/lib/gitlab/expiring_subscription_message_spec.rb
- - ee/spec/lib/gitlab/geo/log_cursor/daemon_spec.rb
- - ee/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
- - ee/spec/lib/omni_auth/strategies/group_saml_spec.rb
- - ee/spec/lib/system_check/geo/geo_database_configured_check_spec.rb
- - ee/spec/models/ee/namespace_spec.rb
- - ee/spec/models/geo_node_status_spec.rb
- - ee/spec/models/issue_spec.rb
- - ee/spec/models/merge_request_spec.rb
- - ee/spec/models/project_import_state_spec.rb
- - ee/spec/models/push_rule_spec.rb
- - ee/spec/presenters/ci/pipeline_presenter_spec.rb
- - ee/spec/requests/api/geo_nodes_spec.rb
- - ee/spec/requests/api/graphql/mutations/dast_on_demand_scans/create_spec.rb
- - ee/spec/requests/api/graphql/mutations/dast_site_profiles/delete_spec.rb
- - ee/spec/requests/api/issues_spec.rb
- - ee/spec/requests/api/projects_spec.rb
- - ee/spec/requests/git_http_spec.rb
- - ee/spec/requests/groups_controller_spec.rb
- - ee/spec/requests/omniauth_kerberos_spec.rb
- - ee/spec/requests/repositories/git_http_controller_spec.rb
- - ee/spec/services/ee/git/branch_push_service_spec.rb
- - ee/spec/services/ee/merge_requests/create_from_vulnerability_data_service_spec.rb
- - ee/spec/services/ee/merge_requests/refresh_service_spec.rb
- - ee/spec/services/ee/users/create_service_spec.rb
- - ee/spec/services/ee/users/destroy_service_spec.rb
- - ee/spec/services/geo/container_repository_sync_service_spec.rb
- - ee/spec/services/geo/design_repository_sync_service_spec.rb
- - ee/spec/services/geo/framework_repository_sync_service_spec.rb
- - ee/spec/services/geo/hashed_storage_migration_service_spec.rb
- - ee/spec/services/geo/metrics_update_service_spec.rb
- - ee/spec/services/geo/move_repository_service_spec.rb
- - ee/spec/services/geo/project_housekeeping_service_spec.rb
- - ee/spec/services/geo/rename_repository_service_spec.rb
- - ee/spec/services/geo/repository_destroy_service_spec.rb
- - ee/spec/services/geo/repository_sync_service_spec.rb
- - ee/spec/services/geo/wiki_sync_service_spec.rb
- - ee/spec/services/groups/destroy_service_spec.rb
- - ee/spec/services/groups/update_service_spec.rb
- - ee/spec/services/merge_trains/check_status_service_spec.rb
- - ee/spec/services/projects/destroy_service_spec.rb
- - ee/spec/services/projects/group_links/destroy_service_spec.rb
- - ee/spec/services/projects/update_service_spec.rb
- - ee/spec/services/slash_commands/global_slack_handler_spec.rb
- - ee/spec/support/helpers/ee/stub_configuration.rb
- - ee/spec/support/shared_examples/controllers/analytics/cycle_analytics/shared_stage_shared_examples.rb
- - ee/spec/support/shared_examples/features/ultimate_trial_callout_shared_examples.rb
- - ee/spec/support/shared_examples/lib/gitlab/geo/geo_logs_event_source_info_shared_examples.rb
- - ee/spec/support/shared_examples/models/member_shared_examples.rb
- - ee/spec/support/shared_examples/services/base_sync_service_shared_examples.rb
- - ee/spec/support/shared_examples/services/geo/geo_request_service_shared_examples.rb
- - ee/spec/workers/concerns/elastic/indexing_control_spec.rb
- - ee/spec/workers/elastic_commit_indexer_worker_spec.rb
- - ee/spec/workers/geo/design_repository_shard_sync_worker_spec.rb
- - ee/spec/workers/geo/registry_sync_worker_spec.rb
- - ee/spec/workers/geo/repository_cleanup_worker_spec.rb
- - ee/spec/workers/geo/repository_shard_sync_worker_spec.rb
- - ee/spec/workers/project_cache_worker_spec.rb
- - ee/spec/workers/repository_import_worker_spec.rb
- - ee/spec/workers/vulnerability_exports/export_deletion_worker_spec.rb
- - qa/spec/runtime/release_spec.rb
- - spec/controllers/admin/sessions_controller_spec.rb
- - spec/controllers/application_controller_spec.rb
- - spec/controllers/concerns/issuable_actions_spec.rb
- - spec/controllers/concerns/static_object_external_storage_spec.rb
- - spec/controllers/explore/projects_controller_spec.rb
- - spec/controllers/groups/clusters_controller_spec.rb
- - spec/controllers/groups/settings/ci_cd_controller_spec.rb
- - spec/controllers/groups_controller_spec.rb
- - spec/controllers/import/bitbucket_controller_spec.rb
- - spec/controllers/oauth/jira_dvcs/authorizations_controller_spec.rb
- - spec/controllers/omniauth_callbacks_controller_spec.rb
- - spec/controllers/projects/artifacts_controller_spec.rb
- - spec/controllers/projects/branches_controller_spec.rb
- - spec/controllers/projects/clusters_controller_spec.rb
- - spec/controllers/projects/commit_controller_spec.rb
- - spec/controllers/projects/commits_controller_spec.rb
- - spec/controllers/projects/environments_controller_spec.rb
- - spec/controllers/projects/imports_controller_spec.rb
- - spec/controllers/projects/issues_controller_spec.rb
- - spec/controllers/projects/jobs_controller_spec.rb
- - spec/controllers/projects/labels_controller_spec.rb
- - spec/controllers/projects/merge_requests_controller_spec.rb
- - spec/controllers/projects/pipelines_controller_spec.rb
- - spec/controllers/projects/settings/integration_hook_logs_controller_spec.rb
- - spec/controllers/projects/settings/integrations_controller_spec.rb
- - spec/controllers/projects/tags_controller_spec.rb
- - spec/controllers/registrations_controller_spec.rb
- - spec/controllers/sessions_controller_spec.rb
- - spec/controllers/snippets/notes_controller_spec.rb
- - spec/controllers/snippets_controller_spec.rb
- - spec/features/admin/admin_mode/login_spec.rb
- - spec/features/groups/members/tabs_spec.rb
- - spec/features/ide/static_object_external_storage_csp_spec.rb
- - spec/features/issuables/issuable_list_spec.rb
- - spec/features/issues/form_spec.rb
- - spec/features/merge_request/user_creates_image_diff_notes_spec.rb
- - spec/features/merge_request/user_reviews_image_spec.rb
- - spec/features/merge_request/user_sees_diff_spec.rb
- - spec/features/merge_request/user_sees_merge_widget_spec.rb
- - spec/features/profiles/personal_access_tokens_spec.rb
- - spec/features/projects/clusters/gcp_spec.rb
- - spec/features/projects/clusters_spec.rb
- - spec/features/projects/container_registry_spec.rb
- - spec/features/projects/files/user_browses_lfs_files_spec.rb
- - spec/features/projects/jobs_spec.rb
- - spec/features/projects/navbar_spec.rb
- - spec/features/projects/settings/service_desk_setting_spec.rb
- - spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
- - spec/features/snippets/embedded_snippet_spec.rb
- - spec/features/usage_stats_consent_spec.rb
- - spec/finders/prometheus_metrics_finder_spec.rb
- - spec/graphql/mutations/alert_management/create_alert_issue_spec.rb
- - spec/graphql/mutations/alert_management/http_integration/create_spec.rb
- - spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb
- - spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb
- - spec/graphql/mutations/alert_management/http_integration/update_spec.rb
- - spec/graphql/mutations/alert_management/prometheus_integration/create_spec.rb
- - spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb
- - spec/graphql/mutations/alert_management/prometheus_integration/update_spec.rb
- - spec/helpers/projects_helper_spec.rb
- - spec/initializers/lograge_spec.rb
- - spec/lib/api/entities/merge_request_basic_spec.rb
- - spec/lib/api/entities/merge_request_changes_spec.rb
- - spec/lib/api/helpers_spec.rb
- - spec/lib/backup/files_spec.rb
- - spec/lib/backup/manager_spec.rb
- - spec/lib/banzai/commit_renderer_spec.rb
- - spec/lib/banzai/filter/references/external_issue_reference_filter_spec.rb
- - spec/lib/banzai/filter/references/issue_reference_filter_spec.rb
- - spec/lib/banzai/filter/repository_link_filter_spec.rb
- - spec/lib/banzai/pipeline/gfm_pipeline_spec.rb
- - spec/lib/extracts_ref_spec.rb
- - spec/lib/feature_spec.rb
- - spec/lib/gitlab/app_logger_spec.rb
- - spec/lib/gitlab/asciidoc_spec.rb
- - spec/lib/gitlab/auth/auth_finders_spec.rb
- - spec/lib/gitlab/auth/blocked_user_tracker_spec.rb
- - spec/lib/gitlab/auth/request_authenticator_spec.rb
- - spec/lib/gitlab/auth_spec.rb
- - spec/lib/gitlab/checks/diff_check_spec.rb
- - spec/lib/gitlab/checks/lfs_check_spec.rb
- - spec/lib/gitlab/checks/lfs_integrity_spec.rb
- - spec/lib/gitlab/ci/config/external/file/base_spec.rb
- - spec/lib/gitlab/ci/config/external/file/local_spec.rb
- - spec/lib/gitlab/ci/config/external/processor_spec.rb
- - spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
- - spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
- - spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb
- - spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb
- - spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb
- - spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb
- - spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb
- - spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb
- - spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
- - spec/lib/gitlab/ci/templates/npm_spec.rb
- - spec/lib/gitlab/ci/trace_spec.rb
- - spec/lib/gitlab/current_settings_spec.rb
- - spec/lib/gitlab/diff/highlight_cache_spec.rb
- - spec/lib/gitlab/diff/highlight_spec.rb
- - spec/lib/gitlab/diff/position_spec.rb
- - spec/lib/gitlab/email/handler/create_issue_handler_spec.rb
- - spec/lib/gitlab/email/handler/create_note_handler_spec.rb
- - spec/lib/gitlab/etag_caching/middleware_spec.rb
- - spec/lib/gitlab/exclusive_lease_helpers_spec.rb
- - spec/lib/gitlab/fogbugz_import/importer_spec.rb
- - spec/lib/gitlab/gfm/reference_rewriter_spec.rb
- - spec/lib/gitlab/git/repository_spec.rb
- - spec/lib/gitlab/gitaly_client/blob_service_spec.rb
- - spec/lib/gitlab/gitaly_client/commit_service_spec.rb
- - spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb
- - spec/lib/gitlab/gitaly_client/health_check_service_spec.rb
- - spec/lib/gitlab/gitaly_client/operation_service_spec.rb
- - spec/lib/gitlab/gitaly_client/praefect_info_service_spec.rb
- - spec/lib/gitlab/gitaly_client/ref_service_spec.rb
- - spec/lib/gitlab/gitaly_client/remote_service_spec.rb
- - spec/lib/gitlab/gitaly_client/repository_service_spec.rb
- - spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
- - spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
- - spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
- - spec/lib/gitlab/hashed_storage/migrator_spec.rb
- - spec/lib/gitlab/import/merge_request_helpers_spec.rb
- - spec/lib/gitlab/import_export/config_spec.rb
- - spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb
- - spec/lib/gitlab/import_export/importer_spec.rb
- - spec/lib/gitlab/import_export/lfs_restorer_spec.rb
- - spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
- - spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb
- - spec/lib/gitlab/import_export/snippets_repo_restorer_spec.rb
- - spec/lib/gitlab/import_export/version_checker_spec.rb
- - spec/lib/gitlab/job_waiter_spec.rb
- - spec/lib/gitlab/legacy_github_import/importer_spec.rb
- - spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
- - spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb
- - spec/lib/gitlab/metrics/rack_middleware_spec.rb
- - spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
- - spec/lib/gitlab/metrics_spec.rb
- - spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb
- - spec/lib/gitlab/sidekiq_middleware_spec.rb
- - spec/lib/gitlab/tracking/destinations/snowplow_spec.rb
- - spec/lib/gitlab/tracking_spec.rb
- - spec/lib/gitlab/usage_data_spec.rb
- - spec/lib/gitlab/workhorse_spec.rb
- - spec/lib/gitlab/x509/commit_spec.rb
- - spec/lib/gitlab/x509/signature_spec.rb
- - spec/lib/google_api/cloud_platform/client_spec.rb
- - spec/lib/json_web_token/rsa_token_spec.rb
- - spec/lib/mattermost/command_spec.rb
- - spec/lib/mattermost/team_spec.rb
- - spec/lib/system_check/simple_executor_spec.rb
- - spec/models/ci/build_spec.rb
- - spec/models/ci/runner_spec.rb
- - spec/models/commit_spec.rb
- - spec/models/environment_spec.rb
- - spec/models/group_spec.rb
- - spec/models/hooks/service_hook_spec.rb
- - spec/models/hooks/system_hook_spec.rb
- - spec/models/hooks/web_hook_spec.rb
- - spec/models/integrations/jira_spec.rb
- - spec/models/integrations/mattermost_slash_commands_spec.rb
- - spec/models/issue_spec.rb
- - spec/models/key_spec.rb
- - spec/models/member_spec.rb
- - spec/models/merge_request_diff_spec.rb
- - spec/models/merge_request_spec.rb
- - spec/models/note_spec.rb
- - spec/models/project_import_state_spec.rb
- - spec/models/project_spec.rb
- - spec/models/repository_spec.rb
- - spec/models/user_spec.rb
- - spec/models/x509_certificate_spec.rb
- - spec/policies/ci/build_policy_spec.rb
- - spec/policies/ci/pipeline_policy_spec.rb
- - spec/presenters/gitlab/blame_presenter_spec.rb
- - spec/presenters/merge_request_presenter_spec.rb
- - spec/requests/api/ci/runner/jobs_artifacts_spec.rb
- - spec/requests/api/ci/runner/jobs_put_spec.rb
- - spec/requests/api/ci/runner/jobs_request_post_spec.rb
- - spec/requests/api/ci/runner/jobs_trace_spec.rb
- - spec/requests/api/ci/runner/runners_delete_spec.rb
- - spec/requests/api/ci/runner/runners_post_spec.rb
- - spec/requests/api/ci/runner/runners_verify_post_spec.rb
- - spec/requests/api/graphql/gitlab_schema_spec.rb
- - spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb
- - spec/requests/api/graphql_spec.rb
- - spec/requests/api/helpers_spec.rb
- - spec/requests/api/internal/base_spec.rb
- - spec/requests/api/maven_packages_spec.rb
- - spec/requests/api/merge_requests_spec.rb
- - spec/requests/api/pages/pages_spec.rb
- - spec/requests/api/project_export_spec.rb
- - spec/requests/api/project_import_spec.rb
- - spec/requests/api/projects_spec.rb
- - spec/requests/api/snippets_spec.rb
- - spec/requests/api/todos_spec.rb
- - spec/requests/git_http_spec.rb
- - spec/requests/import/gitlab_projects_controller_spec.rb
- - spec/routing/routing_spec.rb
- - spec/serializers/merge_request_poll_cached_widget_entity_spec.rb
- - spec/serializers/merge_request_poll_widget_entity_spec.rb
- - spec/services/application_settings/update_service_spec.rb
- - spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb
- - spec/services/boards/lists/update_service_spec.rb
- - spec/services/ci/create_pipeline_service_spec.rb
- - spec/services/ci/expire_pipeline_cache_service_spec.rb
- - spec/services/ci/list_config_variables_service_spec.rb
- - spec/services/ci/register_job_service_spec.rb
- - spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb
- - spec/services/ci/retry_pipeline_service_spec.rb
- - spec/services/clusters/applications/create_service_spec.rb
- - spec/services/clusters/cleanup/project_namespace_service_spec.rb
- - spec/services/clusters/cleanup/service_account_service_spec.rb
- - spec/services/deployments/older_deployments_drop_service_spec.rb
- - spec/services/deployments/update_environment_service_spec.rb
- - spec/services/draft_notes/destroy_service_spec.rb
- - spec/services/events/render_service_spec.rb
- - spec/services/git/branch_push_service_spec.rb
- - spec/services/git/process_ref_changes_service_spec.rb
- - spec/services/groups/create_service_spec.rb
- - spec/services/groups/update_service_spec.rb
- - spec/services/integrations/test/project_service_spec.rb
- - spec/services/issuable/destroy_service_spec.rb
- - spec/services/issues/close_service_spec.rb
- - spec/services/issues/reopen_service_spec.rb
- - spec/services/members/destroy_service_spec.rb
- - spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
- - spec/services/merge_requests/build_service_spec.rb
- - spec/services/merge_requests/merge_service_spec.rb
- - spec/services/merge_requests/mergeability_check_service_spec.rb
- - spec/services/merge_requests/refresh_service_spec.rb
- - spec/services/merge_requests/reload_diffs_service_spec.rb
- - spec/services/merge_requests/resolved_discussion_notification_service_spec.rb
- - spec/services/metrics/dashboard/custom_dashboard_service_spec.rb
- - spec/services/metrics/dashboard/transient_embed_service_spec.rb
- - spec/services/notes/create_service_spec.rb
- - spec/services/notes/render_service_spec.rb
- - spec/services/packages/conan/create_package_file_service_spec.rb
- - spec/services/packages/nuget/metadata_extraction_service_spec.rb
- - spec/services/packages/nuget/update_package_from_metadata_service_spec.rb
- - spec/services/post_receive_service_spec.rb
- - spec/services/projects/after_rename_service_spec.rb
- - spec/services/projects/container_repository/cleanup_tags_service_spec.rb
- - spec/services/projects/container_repository/delete_tags_service_spec.rb
- - spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb
- - spec/services/projects/container_repository/third_party/delete_tags_service_spec.rb
- - spec/services/projects/destroy_service_spec.rb
- - spec/services/projects/fork_service_spec.rb
- - spec/services/projects/import_service_spec.rb
- - spec/services/projects/lfs_pointers/lfs_download_service_spec.rb
- - spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb
- - spec/services/projects/prometheus/alerts/notify_service_spec.rb
- - spec/services/projects/transfer_service_spec.rb
- - spec/services/projects/update_remote_mirror_service_spec.rb
- - spec/services/projects/update_service_spec.rb
- - spec/services/projects/update_statistics_service_spec.rb
- - spec/services/resource_events/change_labels_service_spec.rb
- - spec/services/search_service_spec.rb
- - spec/services/snippets/create_service_spec.rb
- - spec/services/test_hooks/project_service_spec.rb
- - spec/services/test_hooks/system_service_spec.rb
- - spec/services/todo_service_spec.rb
- - spec/services/users/destroy_service_spec.rb
- - spec/services/users/migrate_to_ghost_user_service_spec.rb
- - spec/spec_helper.rb
- - spec/support/capybara.rb
- - spec/support/helpers/api_helpers.rb
- - spec/support/helpers/graphql_helpers.rb
- - spec/support/helpers/ldap_helpers.rb
- - spec/support/helpers/login_helpers.rb
- - spec/support/helpers/metrics_dashboard_url_helpers.rb
- - spec/support/helpers/rake_helpers.rb
- - spec/support/helpers/stub_configuration.rb
- - spec/support/helpers/stub_gitlab_calls.rb
- - spec/support/helpers/test_env.rb
- - spec/support/import_export/common_util.rb
- - spec/support/services/migrate_to_ghost_user_service_shared_examples.rb
- - spec/support/shared_contexts/email_shared_context.rb
- - spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb
- - spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
- - spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb
- - spec/support/shared_examples/controllers/issuables_requiring_filter_shared_examples.rb
- - spec/support/shared_examples/controllers/repository_lfs_file_load_shared_examples.rb
- - spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb
- - spec/support/shared_examples/controllers/unique_visits_shared_examples.rb
- - spec/support/shared_examples/controllers/update_invalid_issuable_shared_examples.rb
- - spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
- - spec/support/shared_examples/features/archive_download_buttons_shared_examples.rb
- - spec/support/shared_examples/features/snippets_shared_examples.rb
- - spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb
- - spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb
- - spec/support/shared_examples/models/diff_note_after_commit_shared_examples.rb
- - spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb
- - spec/support/shared_examples/models/mentionable_shared_examples.rb
- - spec/support/shared_examples/models/with_uploads_shared_examples.rb
- - spec/support/shared_examples/path_extraction_shared_examples.rb
- - spec/support/shared_examples/requests/api/discussions_shared_examples.rb
- - spec/support/shared_examples/requests/api/snippets_shared_examples.rb
- - spec/support/shared_examples/requests/rack_attack_shared_examples.rb
- - spec/support/shared_examples/requests/snippet_shared_examples.rb
- - spec/support/shared_examples/services/alert_management_shared_examples.rb
- - spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb
- - spec/support/shared_examples/services/boards/issues_list_service_shared_examples.rb
- - spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb
- - spec/support/shared_examples/services/issuable_shared_examples.rb
- - spec/support/shared_examples/uploaders/object_storage_shared_examples.rb
- - spec/support/shared_examples/workers/authorized_projects_worker_shared_example.rb
- - spec/support/shared_examples/workers/reactive_cacheable_shared_examples.rb
- - spec/tasks/gitlab/cleanup_rake_spec.rb
- - spec/tasks/gitlab/container_registry_rake_spec.rb
- - spec/tasks/gitlab/db_rake_spec.rb
- - spec/tasks/gitlab/git_rake_spec.rb
- - spec/tasks/gitlab/praefect_rake_spec.rb
- - spec/tasks/gitlab/shell_rake_spec.rb
- - spec/tasks/gitlab/x509/update_rake_spec.rb
- - spec/uploaders/file_mover_spec.rb
- - spec/uploaders/records_uploads_spec.rb
- - spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb
- - spec/views/layouts/_head.html.haml_spec.rb
- - spec/views/projects/artifacts/_artifact.html.haml_spec.rb
- - spec/workers/archive_trace_worker_spec.rb
- - spec/workers/build_hooks_worker_spec.rb
- - spec/workers/ci/build_schedule_worker_spec.rb
- - spec/workers/ci/daily_build_group_report_results_worker_spec.rb
- - spec/workers/cluster_configure_istio_worker_spec.rb
- - spec/workers/cluster_provision_worker_spec.rb
- - spec/workers/clusters/cleanup/project_namespace_worker_spec.rb
- - spec/workers/clusters/cleanup/service_account_worker_spec.rb
- - spec/workers/concerns/project_import_options_spec.rb
- - spec/workers/create_commit_signature_worker_spec.rb
- - spec/workers/create_note_diff_file_worker_spec.rb
- - spec/workers/delete_diff_files_worker_spec.rb
- - spec/workers/email_receiver_worker_spec.rb
- - spec/workers/emails_on_push_worker_spec.rb
- - spec/workers/error_tracking_issue_link_worker_spec.rb
- - spec/workers/group_export_worker_spec.rb
- - spec/workers/group_import_worker_spec.rb
- - spec/workers/namespaces/root_statistics_worker_spec.rb
- - spec/workers/new_note_worker_spec.rb
- - spec/workers/object_pool/create_worker_spec.rb
- - spec/workers/packages/nuget/extraction_worker_spec.rb
- - spec/workers/pipeline_hooks_worker_spec.rb
- - spec/workers/pipeline_process_worker_spec.rb
- - spec/workers/pipeline_schedule_worker_spec.rb
- - spec/workers/project_cache_worker_spec.rb
- - spec/workers/stage_update_worker_spec.rb
- - spec/workers/stuck_ci_jobs_worker_spec.rb
- - spec/workers/wait_for_cluster_creation_worker_spec.rb
- - ee/spec/workers/security/auto_fix_worker_spec.rb
+ - 'ee/spec/controllers/admin/geo/nodes_controller_spec.rb'
+ - 'ee/spec/controllers/ee/groups_controller_spec.rb'
+ - 'ee/spec/controllers/groups/analytics/productivity_analytics_controller_spec.rb'
+ - 'ee/spec/controllers/groups/epics/notes_controller_spec.rb'
+ - 'ee/spec/controllers/groups/omniauth_callbacks_controller_spec.rb'
+ - 'ee/spec/controllers/oauth/geo_auth_controller_spec.rb'
+ - 'ee/spec/controllers/projects/integrations/jira/issues_controller_spec.rb'
+ - 'ee/spec/controllers/projects/merge_requests_controller_spec.rb'
+ - 'ee/spec/controllers/projects/path_locks_controller_spec.rb'
+ - 'ee/spec/controllers/projects_controller_spec.rb'
+ - 'ee/spec/controllers/subscriptions_controller_spec.rb'
+ - 'ee/spec/features/admin/admin_audit_logs_spec.rb'
+ - 'ee/spec/features/admin/admin_reset_pipeline_minutes_spec.rb'
+ - 'ee/spec/features/admin/admin_users_spec.rb'
+ - 'ee/spec/features/ci_shared_runner_warnings_spec.rb'
+ - 'ee/spec/features/groups/group_settings_spec.rb'
+ - 'ee/spec/features/issues/form_spec.rb'
+ - 'ee/spec/features/merge_request/user_creates_merge_request_spec.rb'
+ - 'ee/spec/features/projects/new_project_spec.rb'
+ - 'ee/spec/features/registrations/welcome_spec.rb'
+ - 'ee/spec/features/security/project/internal_access_spec.rb'
+ - 'ee/spec/features/security/project/private_access_spec.rb'
+ - 'ee/spec/features/security/project/public_access_spec.rb'
+ - 'ee/spec/features/users/login_spec.rb'
+ - 'ee/spec/graphql/mutations/dast_on_demand_scans/create_spec.rb'
+ - 'ee/spec/graphql/mutations/incident_management/oncall_schedule/create_spec.rb'
+ - 'ee/spec/graphql/mutations/incident_management/oncall_schedule/destroy_spec.rb'
+ - 'ee/spec/graphql/mutations/incident_management/oncall_schedule/update_spec.rb'
+ - 'ee/spec/helpers/application_helper_spec.rb'
+ - 'ee/spec/lib/ee/api/helpers_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/checks/push_rule_check_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/checks/push_rules/commit_check_spec.rb'
+ - 'ee/spec/lib/gitlab/auth/group_saml/membership_enforcer_spec.rb'
+ - 'ee/spec/lib/gitlab/auth/ldap/access_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/daemon_spec.rb'
+ - 'ee/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb'
+ - 'ee/spec/models/ee/namespace_spec.rb'
+ - 'ee/spec/models/geo_node_status_spec.rb'
+ - 'ee/spec/models/issue_spec.rb'
+ - 'ee/spec/models/merge_request_spec.rb'
+ - 'ee/spec/models/project_import_state_spec.rb'
+ - 'ee/spec/models/push_rule_spec.rb'
+ - 'ee/spec/presenters/ci/pipeline_presenter_spec.rb'
+ - 'ee/spec/requests/api/geo_nodes_spec.rb'
+ - 'ee/spec/requests/api/issues_spec.rb'
+ - 'ee/spec/requests/api/projects_spec.rb'
+ - 'ee/spec/requests/git_http_spec.rb'
+ - 'ee/spec/requests/groups_controller_spec.rb'
+ - 'ee/spec/requests/omniauth_kerberos_spec.rb'
+ - 'ee/spec/requests/repositories/git_http_controller_spec.rb'
+ - 'ee/spec/services/ee/git/branch_push_service_spec.rb'
+ - 'ee/spec/services/ee/merge_requests/create_from_vulnerability_data_service_spec.rb'
+ - 'ee/spec/services/ee/merge_requests/refresh_service_spec.rb'
+ - 'ee/spec/services/ee/users/create_service_spec.rb'
+ - 'ee/spec/services/geo/container_repository_sync_service_spec.rb'
+ - 'ee/spec/services/geo/design_repository_sync_service_spec.rb'
+ - 'ee/spec/services/geo/framework_repository_sync_service_spec.rb'
+ - 'ee/spec/services/geo/hashed_storage_migration_service_spec.rb'
+ - 'ee/spec/services/geo/metrics_update_service_spec.rb'
+ - 'ee/spec/services/geo/move_repository_service_spec.rb'
+ - 'ee/spec/services/geo/project_housekeeping_service_spec.rb'
+ - 'ee/spec/services/geo/rename_repository_service_spec.rb'
+ - 'ee/spec/services/geo/repository_destroy_service_spec.rb'
+ - 'ee/spec/services/geo/repository_sync_service_spec.rb'
+ - 'ee/spec/services/groups/destroy_service_spec.rb'
+ - 'ee/spec/services/groups/update_service_spec.rb'
+ - 'ee/spec/services/merge_trains/check_status_service_spec.rb'
+ - 'ee/spec/services/projects/destroy_service_spec.rb'
+ - 'ee/spec/services/projects/group_links/destroy_service_spec.rb'
+ - 'ee/spec/services/projects/update_service_spec.rb'
+ - 'ee/spec/services/slash_commands/global_slack_handler_spec.rb'
+ - 'ee/spec/support/helpers/ee/stub_configuration.rb'
+ - 'ee/spec/support/shared_examples/controllers/analytics/cycle_analytics/shared_stage_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/features/ultimate_trial_callout_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/lib/gitlab/geo/geo_logs_event_source_info_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/models/member_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/services/base_sync_service_shared_examples.rb'
+ - 'ee/spec/workers/concerns/elastic/indexing_control_spec.rb'
+ - 'ee/spec/workers/geo/design_repository_shard_sync_worker_spec.rb'
+ - 'ee/spec/workers/geo/registry_sync_worker_spec.rb'
+ - 'ee/spec/workers/geo/repository_cleanup_worker_spec.rb'
+ - 'ee/spec/workers/geo/repository_shard_sync_worker_spec.rb'
+ - 'ee/spec/workers/project_cache_worker_spec.rb'
+ - 'ee/spec/workers/repository_import_worker_spec.rb'
+ - 'ee/spec/workers/security/auto_fix_worker_spec.rb'
+ - 'ee/spec/workers/vulnerability_exports/export_deletion_worker_spec.rb'
+ - 'spec/controllers/admin/sessions_controller_spec.rb'
+ - 'spec/controllers/application_controller_spec.rb'
+ - 'spec/controllers/concerns/issuable_actions_spec.rb'
+ - 'spec/controllers/concerns/static_object_external_storage_spec.rb'
+ - 'spec/controllers/explore/projects_controller_spec.rb'
+ - 'spec/controllers/groups/clusters_controller_spec.rb'
+ - 'spec/controllers/groups/settings/ci_cd_controller_spec.rb'
+ - 'spec/controllers/groups_controller_spec.rb'
+ - 'spec/controllers/import/bitbucket_controller_spec.rb'
+ - 'spec/controllers/oauth/jira_dvcs/authorizations_controller_spec.rb'
+ - 'spec/controllers/omniauth_callbacks_controller_spec.rb'
+ - 'spec/controllers/projects/artifacts_controller_spec.rb'
+ - 'spec/controllers/projects/branches_controller_spec.rb'
+ - 'spec/controllers/projects/clusters_controller_spec.rb'
+ - 'spec/controllers/projects/commit_controller_spec.rb'
+ - 'spec/controllers/projects/commits_controller_spec.rb'
+ - 'spec/controllers/projects/environments_controller_spec.rb'
+ - 'spec/controllers/projects/imports_controller_spec.rb'
+ - 'spec/controllers/projects/jobs_controller_spec.rb'
+ - 'spec/controllers/projects/labels_controller_spec.rb'
+ - 'spec/controllers/projects/merge_requests_controller_spec.rb'
+ - 'spec/controllers/projects/pipelines_controller_spec.rb'
+ - 'spec/controllers/projects/settings/integration_hook_logs_controller_spec.rb'
+ - 'spec/controllers/projects/settings/integrations_controller_spec.rb'
+ - 'spec/controllers/projects/tags_controller_spec.rb'
+ - 'spec/controllers/registrations_controller_spec.rb'
+ - 'spec/controllers/sessions_controller_spec.rb'
+ - 'spec/controllers/snippets/notes_controller_spec.rb'
+ - 'spec/controllers/snippets_controller_spec.rb'
+ - 'spec/features/admin/admin_mode/login_spec.rb'
+ - 'spec/features/groups/members/tabs_spec.rb'
+ - 'spec/features/ide/static_object_external_storage_csp_spec.rb'
+ - 'spec/features/issuables/issuable_list_spec.rb'
+ - 'spec/features/issues/form_spec.rb'
+ - 'spec/features/merge_request/user_creates_image_diff_notes_spec.rb'
+ - 'spec/features/merge_request/user_reviews_image_spec.rb'
+ - 'spec/features/merge_request/user_sees_diff_spec.rb'
+ - 'spec/features/merge_request/user_sees_merge_widget_spec.rb'
+ - 'spec/features/projects/clusters/gcp_spec.rb'
+ - 'spec/features/projects/clusters_spec.rb'
+ - 'spec/features/projects/container_registry_spec.rb'
+ - 'spec/features/projects/files/user_browses_lfs_files_spec.rb'
+ - 'spec/features/projects/jobs_spec.rb'
+ - 'spec/features/projects/settings/service_desk_setting_spec.rb'
+ - 'spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb'
+ - 'spec/features/snippets/embedded_snippet_spec.rb'
+ - 'spec/features/usage_stats_consent_spec.rb'
+ - 'spec/finders/prometheus_metrics_finder_spec.rb'
+ - 'spec/graphql/mutations/alert_management/create_alert_issue_spec.rb'
+ - 'spec/graphql/mutations/alert_management/http_integration/create_spec.rb'
+ - 'spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb'
+ - 'spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb'
+ - 'spec/graphql/mutations/alert_management/http_integration/update_spec.rb'
+ - 'spec/graphql/mutations/alert_management/prometheus_integration/create_spec.rb'
+ - 'spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb'
+ - 'spec/graphql/mutations/alert_management/prometheus_integration/update_spec.rb'
+ - 'spec/helpers/projects_helper_spec.rb'
+ - 'spec/initializers/lograge_spec.rb'
+ - 'spec/lib/api/entities/merge_request_basic_spec.rb'
+ - 'spec/lib/api/entities/merge_request_changes_spec.rb'
+ - 'spec/lib/api/helpers_spec.rb'
+ - 'spec/lib/backup/files_spec.rb'
+ - 'spec/lib/backup/manager_spec.rb'
+ - 'spec/lib/banzai/commit_renderer_spec.rb'
+ - 'spec/lib/banzai/filter/references/external_issue_reference_filter_spec.rb'
+ - 'spec/lib/banzai/filter/references/issue_reference_filter_spec.rb'
+ - 'spec/lib/banzai/filter/repository_link_filter_spec.rb'
+ - 'spec/lib/banzai/pipeline/gfm_pipeline_spec.rb'
+ - 'spec/lib/extracts_ref_spec.rb'
+ - 'spec/lib/feature_spec.rb'
+ - 'spec/lib/gitlab/app_logger_spec.rb'
+ - 'spec/lib/gitlab/asciidoc_spec.rb'
+ - 'spec/lib/gitlab/auth/auth_finders_spec.rb'
+ - 'spec/lib/gitlab/auth/blocked_user_tracker_spec.rb'
+ - 'spec/lib/gitlab/auth/request_authenticator_spec.rb'
+ - 'spec/lib/gitlab/auth_spec.rb'
+ - 'spec/lib/gitlab/checks/lfs_check_spec.rb'
+ - 'spec/lib/gitlab/checks/lfs_integrity_spec.rb'
+ - 'spec/lib/gitlab/ci/config/external/file/base_spec.rb'
+ - 'spec/lib/gitlab/ci/config/external/file/local_spec.rb'
+ - 'spec/lib/gitlab/ci/config/external/processor_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/chain/build_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/chain/command_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/npm_spec.rb'
+ - 'spec/lib/gitlab/ci/trace_spec.rb'
+ - 'spec/lib/gitlab/current_settings_spec.rb'
+ - 'spec/lib/gitlab/diff/highlight_cache_spec.rb'
+ - 'spec/lib/gitlab/diff/highlight_spec.rb'
+ - 'spec/lib/gitlab/diff/position_spec.rb'
+ - 'spec/lib/gitlab/email/handler/create_issue_handler_spec.rb'
+ - 'spec/lib/gitlab/etag_caching/middleware_spec.rb'
+ - 'spec/lib/gitlab/exclusive_lease_helpers_spec.rb'
+ - 'spec/lib/gitlab/gfm/reference_rewriter_spec.rb'
+ - 'spec/lib/gitlab/git/repository_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/blob_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/commit_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/health_check_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/operation_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/praefect_info_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/ref_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/remote_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/repository_service_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb'
+ - 'spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb'
+ - 'spec/lib/gitlab/hashed_storage/migrator_spec.rb'
+ - 'spec/lib/gitlab/import/merge_request_helpers_spec.rb'
+ - 'spec/lib/gitlab/import_export/config_spec.rb'
+ - 'spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb'
+ - 'spec/lib/gitlab/import_export/importer_spec.rb'
+ - 'spec/lib/gitlab/import_export/lfs_restorer_spec.rb'
+ - 'spec/lib/gitlab/import_export/project/tree_restorer_spec.rb'
+ - 'spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb'
+ - 'spec/lib/gitlab/import_export/snippets_repo_restorer_spec.rb'
+ - 'spec/lib/gitlab/import_export/version_checker_spec.rb'
+ - 'spec/lib/gitlab/job_waiter_spec.rb'
+ - 'spec/lib/gitlab/legacy_github_import/importer_spec.rb'
+ - 'spec/lib/gitlab/legacy_github_import/project_creator_spec.rb'
+ - 'spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb'
+ - 'spec/lib/gitlab/metrics/rack_middleware_spec.rb'
+ - 'spec/lib/gitlab/metrics_spec.rb'
+ - 'spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb'
+ - 'spec/lib/gitlab/tracking/destinations/snowplow_spec.rb'
+ - 'spec/lib/gitlab/tracking_spec.rb'
+ - 'spec/lib/gitlab/usage_data_spec.rb'
+ - 'spec/lib/gitlab/workhorse_spec.rb'
+ - 'spec/lib/gitlab/x509/commit_spec.rb'
+ - 'spec/lib/gitlab/x509/signature_spec.rb'
+ - 'spec/lib/google_api/cloud_platform/client_spec.rb'
+ - 'spec/lib/json_web_token/rsa_token_spec.rb'
+ - 'spec/lib/mattermost/command_spec.rb'
+ - 'spec/lib/mattermost/team_spec.rb'
+ - 'spec/lib/system_check/simple_executor_spec.rb'
+ - 'spec/models/ci/build_spec.rb'
+ - 'spec/models/ci/runner_spec.rb'
+ - 'spec/models/commit_spec.rb'
+ - 'spec/models/environment_spec.rb'
+ - 'spec/models/hooks/service_hook_spec.rb'
+ - 'spec/models/hooks/system_hook_spec.rb'
+ - 'spec/models/integrations/jira_spec.rb'
+ - 'spec/models/key_spec.rb'
+ - 'spec/models/member_spec.rb'
+ - 'spec/models/merge_request_diff_spec.rb'
+ - 'spec/models/merge_request_spec.rb'
+ - 'spec/models/note_spec.rb'
+ - 'spec/models/project_import_state_spec.rb'
+ - 'spec/models/project_spec.rb'
+ - 'spec/models/repository_spec.rb'
+ - 'spec/models/user_spec.rb'
+ - 'spec/models/x509_certificate_spec.rb'
+ - 'spec/policies/ci/build_policy_spec.rb'
+ - 'spec/policies/ci/pipeline_policy_spec.rb'
+ - 'spec/presenters/gitlab/blame_presenter_spec.rb'
+ - 'spec/presenters/merge_request_presenter_spec.rb'
+ - 'spec/requests/api/ci/runner/jobs_artifacts_spec.rb'
+ - 'spec/requests/api/ci/runner/jobs_put_spec.rb'
+ - 'spec/requests/api/ci/runner/jobs_request_post_spec.rb'
+ - 'spec/requests/api/ci/runner/jobs_trace_spec.rb'
+ - 'spec/requests/api/ci/runner/runners_delete_spec.rb'
+ - 'spec/requests/api/ci/runner/runners_post_spec.rb'
+ - 'spec/requests/api/ci/runner/runners_verify_post_spec.rb'
+ - 'spec/requests/api/graphql/gitlab_schema_spec.rb'
+ - 'spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb'
+ - 'spec/requests/api/helpers_spec.rb'
+ - 'spec/requests/api/internal/base_spec.rb'
+ - 'spec/requests/api/maven_packages_spec.rb'
+ - 'spec/requests/api/merge_requests_spec.rb'
+ - 'spec/requests/api/project_export_spec.rb'
+ - 'spec/requests/api/project_import_spec.rb'
+ - 'spec/requests/api/projects_spec.rb'
+ - 'spec/requests/api/snippets_spec.rb'
+ - 'spec/requests/api/todos_spec.rb'
+ - 'spec/requests/git_http_spec.rb'
+ - 'spec/requests/import/gitlab_projects_controller_spec.rb'
+ - 'spec/routing/routing_spec.rb'
+ - 'spec/serializers/merge_request_poll_cached_widget_entity_spec.rb'
+ - 'spec/serializers/merge_request_poll_widget_entity_spec.rb'
+ - 'spec/services/application_settings/update_service_spec.rb'
+ - 'spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb'
+ - 'spec/services/ci/create_pipeline_service_spec.rb'
+ - 'spec/services/ci/register_job_service_spec.rb'
+ - 'spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb'
+ - 'spec/services/ci/retry_pipeline_service_spec.rb'
+ - 'spec/services/clusters/cleanup/project_namespace_service_spec.rb'
+ - 'spec/services/clusters/cleanup/service_account_service_spec.rb'
+ - 'spec/services/deployments/older_deployments_drop_service_spec.rb'
+ - 'spec/services/deployments/update_environment_service_spec.rb'
+ - 'spec/services/draft_notes/destroy_service_spec.rb'
+ - 'spec/services/events/render_service_spec.rb'
+ - 'spec/services/git/branch_push_service_spec.rb'
+ - 'spec/services/git/process_ref_changes_service_spec.rb'
+ - 'spec/services/groups/create_service_spec.rb'
+ - 'spec/services/groups/update_service_spec.rb'
+ - 'spec/services/issuable/destroy_service_spec.rb'
+ - 'spec/services/issues/close_service_spec.rb'
+ - 'spec/services/issues/reopen_service_spec.rb'
+ - 'spec/services/members/destroy_service_spec.rb'
+ - 'spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb'
+ - 'spec/services/merge_requests/build_service_spec.rb'
+ - 'spec/services/merge_requests/merge_service_spec.rb'
+ - 'spec/services/merge_requests/mergeability_check_service_spec.rb'
+ - 'spec/services/merge_requests/refresh_service_spec.rb'
+ - 'spec/services/merge_requests/reload_diffs_service_spec.rb'
+ - 'spec/services/merge_requests/resolved_discussion_notification_service_spec.rb'
+ - 'spec/services/metrics/dashboard/custom_dashboard_service_spec.rb'
+ - 'spec/services/metrics/dashboard/transient_embed_service_spec.rb'
+ - 'spec/services/notes/create_service_spec.rb'
+ - 'spec/services/notes/render_service_spec.rb'
+ - 'spec/services/packages/conan/create_package_file_service_spec.rb'
+ - 'spec/services/packages/nuget/metadata_extraction_service_spec.rb'
+ - 'spec/services/post_receive_service_spec.rb'
+ - 'spec/services/projects/after_rename_service_spec.rb'
+ - 'spec/services/projects/container_repository/delete_tags_service_spec.rb'
+ - 'spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb'
+ - 'spec/services/projects/container_repository/third_party/delete_tags_service_spec.rb'
+ - 'spec/services/projects/destroy_service_spec.rb'
+ - 'spec/services/projects/fork_service_spec.rb'
+ - 'spec/services/projects/import_service_spec.rb'
+ - 'spec/services/projects/lfs_pointers/lfs_download_service_spec.rb'
+ - 'spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb'
+ - 'spec/services/projects/transfer_service_spec.rb'
+ - 'spec/services/projects/update_remote_mirror_service_spec.rb'
+ - 'spec/services/projects/update_service_spec.rb'
+ - 'spec/services/projects/update_statistics_service_spec.rb'
+ - 'spec/services/resource_events/change_labels_service_spec.rb'
+ - 'spec/services/search_service_spec.rb'
+ - 'spec/services/snippets/create_service_spec.rb'
+ - 'spec/services/todo_service_spec.rb'
+ - 'spec/services/users/destroy_service_spec.rb'
+ - 'spec/spec_helper.rb'
+ - 'spec/support/capybara.rb'
+ - 'spec/support/helpers/api_helpers.rb'
+ - 'spec/support/helpers/graphql_helpers.rb'
+ - 'spec/support/helpers/ldap_helpers.rb'
+ - 'spec/support/helpers/login_helpers.rb'
+ - 'spec/support/helpers/metrics_dashboard_url_helpers.rb'
+ - 'spec/support/helpers/rake_helpers.rb'
+ - 'spec/support/helpers/stub_configuration.rb'
+ - 'spec/support/helpers/stub_gitlab_calls.rb'
+ - 'spec/support/import_export/common_util.rb'
+ - 'spec/support/services/migrate_to_ghost_user_service_shared_examples.rb'
+ - 'spec/support/shared_contexts/email_shared_context.rb'
+ - 'spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb'
+ - 'spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb'
+ - 'spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb'
+ - 'spec/support/shared_examples/controllers/issuables_requiring_filter_shared_examples.rb'
+ - 'spec/support/shared_examples/controllers/repository_lfs_file_load_shared_examples.rb'
+ - 'spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb'
+ - 'spec/support/shared_examples/controllers/update_invalid_issuable_shared_examples.rb'
+ - 'spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb'
+ - 'spec/support/shared_examples/features/archive_download_buttons_shared_examples.rb'
+ - 'spec/support/shared_examples/features/snippets_shared_examples.rb'
+ - 'spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb'
+ - 'spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb'
+ - 'spec/support/shared_examples/models/diff_note_after_commit_shared_examples.rb'
+ - 'spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb'
+ - 'spec/support/shared_examples/models/mentionable_shared_examples.rb'
+ - 'spec/support/shared_examples/models/with_uploads_shared_examples.rb'
+ - 'spec/support/shared_examples/path_extraction_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/discussions_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/snippets_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/rack_attack_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/snippet_shared_examples.rb'
+ - 'spec/support/shared_examples/services/boards/issues_list_service_shared_examples.rb'
+ - 'spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb'
+ - 'spec/support/shared_examples/services/issuable_shared_examples.rb'
+ - 'spec/support/shared_examples/uploaders/object_storage_shared_examples.rb'
+ - 'spec/support/shared_examples/workers/authorized_projects_worker_shared_example.rb'
+ - 'spec/support/shared_examples/workers/reactive_cacheable_shared_examples.rb'
+ - 'spec/tasks/gitlab/cleanup_rake_spec.rb'
+ - 'spec/tasks/gitlab/container_registry_rake_spec.rb'
+ - 'spec/tasks/gitlab/db_rake_spec.rb'
+ - 'spec/tasks/gitlab/praefect_rake_spec.rb'
+ - 'spec/tasks/gitlab/shell_rake_spec.rb'
+ - 'spec/tasks/gitlab/x509/update_rake_spec.rb'
+ - 'spec/uploaders/file_mover_spec.rb'
+ - 'spec/uploaders/records_uploads_spec.rb'
+ - 'spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb'
+ - 'spec/views/layouts/_head.html.haml_spec.rb'
+ - 'spec/workers/build_hooks_worker_spec.rb'
+ - 'spec/workers/ci/build_schedule_worker_spec.rb'
+ - 'spec/workers/ci/daily_build_group_report_results_worker_spec.rb'
+ - 'spec/workers/cluster_provision_worker_spec.rb'
+ - 'spec/workers/clusters/cleanup/project_namespace_worker_spec.rb'
+ - 'spec/workers/clusters/cleanup/service_account_worker_spec.rb'
+ - 'spec/workers/concerns/project_import_options_spec.rb'
+ - 'spec/workers/create_commit_signature_worker_spec.rb'
+ - 'spec/workers/create_note_diff_file_worker_spec.rb'
+ - 'spec/workers/delete_diff_files_worker_spec.rb'
+ - 'spec/workers/email_receiver_worker_spec.rb'
+ - 'spec/workers/emails_on_push_worker_spec.rb'
+ - 'spec/workers/error_tracking_issue_link_worker_spec.rb'
+ - 'spec/workers/group_export_worker_spec.rb'
+ - 'spec/workers/group_import_worker_spec.rb'
+ - 'spec/workers/namespaces/root_statistics_worker_spec.rb'
+ - 'spec/workers/new_note_worker_spec.rb'
+ - 'spec/workers/object_pool/create_worker_spec.rb'
+ - 'spec/workers/packages/nuget/extraction_worker_spec.rb'
+ - 'spec/workers/pipeline_process_worker_spec.rb'
+ - 'spec/workers/pipeline_schedule_worker_spec.rb'
+ - 'spec/workers/project_cache_worker_spec.rb'
+ - 'spec/workers/stage_update_worker_spec.rb'
+ - 'spec/workers/wait_for_cluster_creation_worker_spec.rb'
diff --git a/.rubocop_todo/rspec/context_wording.yml b/.rubocop_todo/rspec/context_wording.yml
index 5e9e80402b4..5a01f229482 100644
--- a/.rubocop_todo/rspec/context_wording.yml
+++ b/.rubocop_todo/rspec/context_wording.yml
@@ -1,7 +1,6 @@
---
RSpec/ContextWording:
Exclude:
- - 'ee/spec/components/namespaces/free_user_cap/personal_usage_quota_limitations_alert_component_spec.rb'
- 'ee/spec/controllers/admin/application_settings_controller_spec.rb'
- 'ee/spec/controllers/admin/audit_logs_controller_spec.rb'
- 'ee/spec/controllers/admin/dev_ops_report_controller_spec.rb'
@@ -11,7 +10,6 @@ RSpec/ContextWording:
- 'ee/spec/controllers/admin/push_rules_controller_spec.rb'
- 'ee/spec/controllers/admin/users_controller_spec.rb'
- 'ee/spec/controllers/autocomplete_controller_spec.rb'
- - 'ee/spec/controllers/boards/lists_controller_spec.rb'
- 'ee/spec/controllers/dashboard_controller_spec.rb'
- 'ee/spec/controllers/ee/groups/variables_controller_spec.rb'
- 'ee/spec/controllers/ee/groups_controller_spec.rb'
@@ -24,7 +22,6 @@ RSpec/ContextWording:
- 'ee/spec/controllers/groups/analytics/cycle_analytics_controller_spec.rb'
- 'ee/spec/controllers/groups/audit_events_controller_spec.rb'
- 'ee/spec/controllers/groups/billings_controller_spec.rb'
- - 'ee/spec/controllers/groups/boards_controller_spec.rb'
- 'ee/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb'
- 'ee/spec/controllers/groups/epic_boards_controller_spec.rb'
- 'ee/spec/controllers/groups/epics/notes_controller_spec.rb'
@@ -36,7 +33,6 @@ RSpec/ContextWording:
- 'ee/spec/controllers/groups/push_rules_controller_spec.rb'
- 'ee/spec/controllers/groups/saml_providers_controller_spec.rb'
- 'ee/spec/controllers/groups/scim_oauth_controller_spec.rb'
- - 'ee/spec/controllers/groups/security/merge_commit_reports_controller_spec.rb'
- 'ee/spec/controllers/groups/sso_controller_spec.rb'
- 'ee/spec/controllers/ldap/omniauth_callbacks_controller_spec.rb'
- 'ee/spec/controllers/oauth/applications_controller_spec.rb'
@@ -44,7 +40,6 @@ RSpec/ContextWording:
- 'ee/spec/controllers/operations_controller_spec.rb'
- 'ee/spec/controllers/profiles_controller_spec.rb'
- 'ee/spec/controllers/projects/audit_events_controller_spec.rb'
- - 'ee/spec/controllers/projects/boards_controller_spec.rb'
- 'ee/spec/controllers/projects/environments_controller_spec.rb'
- 'ee/spec/controllers/projects/imports_controller_spec.rb'
- 'ee/spec/controllers/projects/incident_management/escalation_policies_controller_spec.rb'
@@ -115,18 +110,15 @@ RSpec/ContextWording:
- 'ee/spec/features/groups/group_settings_spec.rb'
- 'ee/spec/features/groups/groups_security_credentials_spec.rb'
- 'ee/spec/features/groups/issues_spec.rb'
- - 'ee/spec/features/groups/iteration_spec.rb'
- 'ee/spec/features/groups/iterations/user_edits_iteration_spec.rb'
- 'ee/spec/features/groups/ldap_group_links_spec.rb'
- 'ee/spec/features/groups/members/manage_groups_spec.rb'
- - 'ee/spec/features/groups/members/manage_members_spec.rb'
- 'ee/spec/features/groups/push_rules_spec.rb'
- 'ee/spec/features/groups/saml_enforcement_spec.rb'
- 'ee/spec/features/groups/saml_providers_spec.rb'
- 'ee/spec/features/groups/security/compliance_dashboards_spec.rb'
- 'ee/spec/features/groups/sso_spec.rb'
- 'ee/spec/features/groups_spec.rb'
- - 'ee/spec/features/ide/user_commits_changes_spec.rb'
- 'ee/spec/features/ide/user_opens_ide_spec.rb'
- 'ee/spec/features/issues/epic_in_issue_sidebar_spec.rb'
- 'ee/spec/features/issues/filtered_search/filter_issues_by_iteration_spec.rb'
@@ -153,7 +145,6 @@ RSpec/ContextWording:
- 'ee/spec/features/merge_requests/user_filters_by_approvers_spec.rb'
- 'ee/spec/features/oncall_schedules/user_creates_schedule_spec.rb'
- 'ee/spec/features/profiles/account_spec.rb'
- - 'ee/spec/features/profiles/usage_quotas_spec.rb'
- 'ee/spec/features/projects/active_tabs_spec.rb'
- 'ee/spec/features/projects/audit_events_spec.rb'
- 'ee/spec/features/projects/custom_projects_template_spec.rb'
@@ -180,9 +171,7 @@ RSpec/ContextWording:
- 'ee/spec/features/projects/settings/push_rules_settings_spec.rb'
- 'ee/spec/features/promotion_spec.rb'
- 'ee/spec/features/protected_branches_spec.rb'
- - 'ee/spec/features/registrations/saas_user_registration_spec.rb'
- 'ee/spec/features/registrations/welcome_spec.rb'
- - 'ee/spec/features/search/elastic/global_search_spec.rb'
- 'ee/spec/features/signup_spec.rb'
- 'ee/spec/features/trial_registrations/company_information_spec.rb'
- 'ee/spec/features/trials/select_namespace_spec.rb'
@@ -289,7 +278,6 @@ RSpec/ContextWording:
- 'ee/spec/helpers/ee/gitlab_routing_helper_spec.rb'
- 'ee/spec/helpers/ee/groups/group_members_helper_spec.rb'
- 'ee/spec/helpers/ee/groups_helper_spec.rb'
- - 'ee/spec/helpers/ee/integrations_helper_spec.rb'
- 'ee/spec/helpers/ee/issuables_helper_spec.rb'
- 'ee/spec/helpers/ee/issues_helper_spec.rb'
- 'ee/spec/helpers/ee/lock_helper_spec.rb'
@@ -318,7 +306,6 @@ RSpec/ContextWording:
- 'ee/spec/lib/banzai/reference_parser/iteration_parser_spec.rb'
- 'ee/spec/lib/container_registry/client_spec.rb'
- 'ee/spec/lib/ee/api/entities/analytics/group_activity_spec.rb'
- - 'ee/spec/lib/ee/api/entities/billable_member_spec.rb'
- 'ee/spec/lib/ee/api/entities/geo_node_status_spec.rb'
- 'ee/spec/lib/ee/api/entities/project_spec.rb'
- 'ee/spec/lib/ee/api/entities/user_with_admin_spec.rb'
@@ -378,23 +365,17 @@ RSpec/ContextWording:
- 'ee/spec/lib/gitlab/auth/smartcard/ldap_certificate_spec.rb'
- 'ee/spec/lib/gitlab/auth/smartcard/san_extension_spec.rb'
- 'ee/spec/lib/gitlab/checks/diff_check_spec.rb'
- - 'ee/spec/lib/gitlab/ci/minutes/cost_factor_spec.rb'
- 'ee/spec/lib/gitlab/ci/minutes/runners_availability_spec.rb'
- 'ee/spec/lib/gitlab/ci/pipeline/chain/create_cross_database_associations_spec.rb'
- 'ee/spec/lib/gitlab/ci/reports/dependency_list/dependency_spec.rb'
- 'ee/spec/lib/gitlab/ci/templates/Jobs/dast_default_branch_gitlab_ci_yaml_spec.rb'
- 'ee/spec/lib/gitlab/ci/templates/api_security_gitlab_ci_yaml_spec.rb'
- - 'ee/spec/lib/gitlab/ci/templates/api_security_latest_gitlab_ci_yaml_spec.rb'
- - 'ee/spec/lib/gitlab/ci/templates/container_scanning_gitlab_ci_yaml_spec.rb'
- - 'ee/spec/lib/gitlab/ci/templates/coverage_fuzzing_gitlab_ci_yaml_spec.rb'
- 'ee/spec/lib/gitlab/ci/templates/dast_api_gitlab_ci_yaml_spec.rb'
- - 'ee/spec/lib/gitlab/ci/templates/dast_api_latest_gitlab_ci_yaml_spec.rb'
- 'ee/spec/lib/gitlab/ci/templates/dast_latest_gitlab_ci_yaml_spec.rb'
- 'ee/spec/lib/gitlab/ci/templates/dependency_scanning_gitlab_ci_yaml_spec.rb'
- 'ee/spec/lib/gitlab/ci/templates/license_scanning_gitlab_ci_yaml_spec.rb'
- 'ee/spec/lib/gitlab/ci/templates/sast_gitlab_ci_yaml_spec.rb'
- 'ee/spec/lib/gitlab/ci/templates/sast_iac_gitlab_ci_yaml_spec.rb'
- - 'ee/spec/lib/gitlab/ci/templates/sast_iac_latest_gitlab_ci_yaml_spec.rb'
- 'ee/spec/lib/gitlab/ci/templates/sast_latest_gitlab_ci_yaml_spec.rb'
- 'ee/spec/lib/gitlab/ci/templates/secret_detection_gitlab_ci_yaml_spec.rb'
- 'ee/spec/lib/gitlab/ci/yaml_processor_spec.rb'
@@ -438,7 +419,6 @@ RSpec/ContextWording:
- 'ee/spec/lib/gitlab/insights/finders/issuable_finder_spec.rb'
- 'ee/spec/lib/gitlab/insights/project_insights_config_spec.rb'
- 'ee/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb'
- - 'ee/spec/lib/gitlab/pagination_delegate_spec.rb'
- 'ee/spec/lib/gitlab/search/aggregation_parser_spec.rb'
- 'ee/spec/lib/gitlab/sitemaps/url_extractor_spec.rb'
- 'ee/spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb'
@@ -546,7 +526,6 @@ RSpec/ContextWording:
- 'ee/spec/models/project_spec.rb'
- 'ee/spec/models/project_team_spec.rb'
- 'ee/spec/models/protected_environment_spec.rb'
- - 'ee/spec/models/protected_environments/approval_summary_spec.rb'
- 'ee/spec/models/push_rule_spec.rb'
- 'ee/spec/models/release_highlight_spec.rb'
- 'ee/spec/models/requirements_management/test_report_spec.rb'
@@ -670,7 +649,6 @@ RSpec/ContextWording:
- 'ee/spec/requests/git_http_geo_spec.rb'
- 'ee/spec/requests/groups/roadmap_controller_spec.rb'
- 'ee/spec/requests/groups/security/credentials_controller_spec.rb'
- - 'ee/spec/requests/groups/settings/reporting_controller_spec.rb'
- 'ee/spec/requests/groups_controller_spec.rb'
- 'ee/spec/requests/jwt_controller_spec.rb'
- 'ee/spec/requests/projects/on_demand_scans_controller_spec.rb'
@@ -687,7 +665,6 @@ RSpec/ContextWording:
- 'ee/spec/routing/user_routing_spec.rb'
- 'ee/spec/serializers/analytics/cycle_analytics/value_stream_errors_serializer_spec.rb'
- 'ee/spec/serializers/blocking_merge_request_entity_spec.rb'
- - 'ee/spec/serializers/board_serializer_spec.rb'
- 'ee/spec/serializers/clusters/environment_entity_spec.rb'
- 'ee/spec/serializers/dashboard_operations_project_entity_spec.rb'
- 'ee/spec/serializers/ee/group_child_entity_spec.rb'
@@ -743,10 +720,8 @@ RSpec/ContextWording:
- 'ee/spec/services/ee/ip_restrictions/update_service_spec.rb'
- 'ee/spec/services/ee/issuable/bulk_update_service_spec.rb'
- 'ee/spec/services/ee/issues/clone_service_spec.rb'
- - 'ee/spec/services/ee/issues/close_service_spec.rb'
- 'ee/spec/services/ee/issues/create_service_spec.rb'
- 'ee/spec/services/ee/issues/move_service_spec.rb'
- - 'ee/spec/services/ee/issues/reopen_service_spec.rb'
- 'ee/spec/services/ee/issues/update_service_spec.rb'
- 'ee/spec/services/ee/keys/destroy_service_spec.rb'
- 'ee/spec/services/ee/members/create_service_spec.rb'
@@ -769,8 +744,6 @@ RSpec/ContextWording:
- 'ee/spec/services/ee/resource_events/merge_into_notes_service_spec.rb'
- 'ee/spec/services/ee/users/build_service_spec.rb'
- 'ee/spec/services/ee/users/create_service_spec.rb'
- - 'ee/spec/services/ee/users/destroy_service_spec.rb'
- - 'ee/spec/services/ee/users/migrate_to_ghost_user_service_spec.rb'
- 'ee/spec/services/ee/users/reject_service_spec.rb'
- 'ee/spec/services/ee/users/update_service_spec.rb'
- 'ee/spec/services/ee/vulnerability_feedback_module/update_service_spec.rb'
@@ -792,7 +765,6 @@ RSpec/ContextWording:
- 'ee/spec/services/geo/container_repository_sync_service_spec.rb'
- 'ee/spec/services/geo/container_repository_sync_spec.rb'
- 'ee/spec/services/geo/design_repository_sync_service_spec.rb'
- - 'ee/spec/services/geo/file_registry_removal_service_spec.rb'
- 'ee/spec/services/geo/framework_repository_sync_service_spec.rb'
- 'ee/spec/services/geo/hashed_storage_migration_service_spec.rb'
- 'ee/spec/services/geo/move_repository_service_spec.rb'
@@ -804,8 +776,6 @@ RSpec/ContextWording:
- 'ee/spec/services/geo/repository_sync_service_spec.rb'
- 'ee/spec/services/geo/repository_verification_reset_spec.rb'
- 'ee/spec/services/geo/wiki_sync_service_spec.rb'
- - 'ee/spec/services/gitlab_subscriptions/activate_awaiting_users_service_spec.rb'
- - 'ee/spec/services/gitlab_subscriptions/apply_trial_service_spec.rb'
- 'ee/spec/services/gitlab_subscriptions/create_hand_raise_lead_service_spec.rb'
- 'ee/spec/services/gitlab_subscriptions/create_service_spec.rb'
- 'ee/spec/services/gitlab_subscriptions/extend_reactivate_trial_service_spec.rb'
@@ -830,7 +800,6 @@ RSpec/ContextWording:
- 'ee/spec/services/incident_management/pending_escalations/create_service_spec.rb'
- 'ee/spec/services/incident_management/pending_escalations/process_service_spec.rb'
- 'ee/spec/services/issues/build_service_spec.rb'
- - 'ee/spec/services/issues/export_csv_service_spec.rb'
- 'ee/spec/services/iterations/cadences/create_service_spec.rb'
- 'ee/spec/services/iterations/create_service_spec.rb'
- 'ee/spec/services/iterations/update_service_spec.rb'
@@ -839,7 +808,6 @@ RSpec/ContextWording:
- 'ee/spec/services/members/activate_service_spec.rb'
- 'ee/spec/services/merge_commits/export_csv_service_spec.rb'
- 'ee/spec/services/merge_request_approval_settings/update_service_spec.rb'
- - 'ee/spec/services/merge_requests/approval_service_spec.rb'
- 'ee/spec/services/merge_requests/build_service_spec.rb'
- 'ee/spec/services/merge_requests/merge_service_spec.rb'
- 'ee/spec/services/merge_requests/reload_merge_head_diff_service_spec.rb'
@@ -868,14 +836,12 @@ RSpec/ContextWording:
- 'ee/spec/services/protected_environments/update_service_spec.rb'
- 'ee/spec/services/quick_actions/interpret_service_spec.rb'
- 'ee/spec/services/requirements_management/export_csv_service_spec.rb'
- - 'ee/spec/services/requirements_management/update_requirement_service_spec.rb'
- 'ee/spec/services/resource_access_tokens/create_service_spec.rb'
- 'ee/spec/services/resource_access_tokens/revoke_service_spec.rb'
- 'ee/spec/services/search/global_service_spec.rb'
- 'ee/spec/services/search/group_service_spec.rb'
- 'ee/spec/services/search/project_service_spec.rb'
- 'ee/spec/services/search/snippet_service_spec.rb'
- - 'ee/spec/services/search_service_spec.rb'
- 'ee/spec/services/security/dependency_list_service_spec.rb'
- 'ee/spec/services/security/ingestion/tasks/ingest_vulnerabilities/create_spec.rb'
- 'ee/spec/services/security/ingestion/tasks/update_vulnerability_uuids_spec.rb'
@@ -959,7 +925,6 @@ RSpec/ContextWording:
- 'ee/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb'
- 'ee/spec/views/layouts/nav/sidebar/_push_rules_link.html.haml_spec.rb'
- 'ee/spec/views/profiles/preferences/show.html.haml_spec.rb'
- - 'ee/spec/views/projects/edit.html.haml_spec.rb'
- 'ee/spec/views/projects/security/discover/show.html.haml_spec.rb'
- 'ee/spec/views/registrations/welcome/show.html.haml_spec.rb'
- 'ee/spec/views/search/_category.html.haml_spec.rb'
@@ -968,7 +933,6 @@ RSpec/ContextWording:
- 'ee/spec/views/shared/_mirror_update_button.html.haml_spec.rb'
- 'ee/spec/views/shared/credentials_inventory/gpg_keys/_gpg_key.html.haml_spec.rb'
- 'ee/spec/views/shared/credentials_inventory/personal_access_tokens/_personal_access_token.html.haml_spec.rb'
- - 'ee/spec/views/shared/credentials_inventory/project_access_tokens/_project_access_token.html.haml_spec.rb'
- 'ee/spec/views/shared/credentials_inventory/ssh_keys/_ssh_key.html.haml_spec.rb'
- 'ee/spec/views/shared/issuable/_sidebar.html.haml_spec.rb'
- 'ee/spec/views/subscriptions/groups/edit.html.haml_spec.rb'
@@ -1006,60 +970,29 @@ RSpec/ContextWording:
- 'ee/spec/workers/sync_seat_link_worker_spec.rb'
- 'ee/spec/workers/update_max_seats_used_for_gitlab_com_subscriptions_worker_spec.rb'
- 'qa/qa/specs/features/api/1_manage/user_access_termination_spec.rb'
- - 'qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb'
- - 'qa/qa/specs/features/api/3_create/gitaly/backend_node_recovery_spec.rb'
- - 'qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb'
- - 'qa/qa/specs/features/api/3_create/gitaly/gitaly_mtls_spec.rb'
- - 'qa/qa/specs/features/api/3_create/gitaly/praefect_connectivity_spec.rb'
- - 'qa/qa/specs/features/api/3_create/gitaly/praefect_dataloss_spec.rb'
- - 'qa/qa/specs/features/api/3_create/gitaly/praefect_replication_queue_spec.rb'
- - 'qa/qa/specs/features/api/3_create/gitaly/praefect_repo_sync_spec.rb'
- 'qa/qa/specs/features/browser_ui/1_manage/login/2fa_ssh_recovery_spec.rb'
- 'qa/qa/specs/features/browser_ui/1_manage/project/invite_group_to_project_spec.rb'
- - 'qa/qa/specs/features/browser_ui/1_manage/user/user_access_termination_spec.rb'
- 'qa/qa/specs/features/browser_ui/2_plan/milestone/assign_milestone_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/merge_request/suggestions/batch_suggestion_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/merge_request/suggestions/custom_commit_suggestion_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/web_ide/server_hooks_custom_error_message_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/wiki/content_editor_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/wiki/project_based_content_creation_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/wiki/project_based_content_manipulation_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/wiki/project_based_directory_management_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/wiki/project_based_list_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/wiki/project_based_page_deletion_spec.rb'
- 'qa/qa/specs/features/browser_ui/4_verify/pipeline/merge_mr_when_pipline_is_blocked_spec.rb'
- 'qa/qa/specs/features/browser_ui/4_verify/pipeline/mr_event_rule_pipeline_spec.rb'
- 'qa/qa/specs/features/browser_ui/4_verify/testing/endpoint_coverage_spec.rb'
- 'qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb'
- 'qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb'
- - 'qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/license/cloud_activation_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/license/license_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/free_tier_group_namespace_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/purchase_ci_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/purchase_storage_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/12_geo/http_push_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/12_geo/http_push_to_secondary_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/12_geo/ssh_push_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/12_geo/ssh_push_to_secondary_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/12_geo/wiki_http_push_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/12_geo/wiki_ssh_push_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/13_secure/enable_scanning_from_configuration_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/13_secure/license_compliance_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_audit_logs_1_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_audit_logs_2_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_ldap_sync_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/insights/default_insights_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/instance/instance_audit_logs_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/project/project_audit_logs_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/2_plan/epic/epics_management_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/2_plan/issue_boards/project_issue_boards_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/3_create/repository/push_rules_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/3_create/wiki/create_group_wiki_page_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/4_verify/new_discussion_not_dropping_merge_trains_mr_spec.rb'
- 'qa/spec/git/repository_spec.rb'
- 'qa/spec/page/element_spec.rb'
@@ -1134,7 +1067,6 @@ RSpec/ContextWording:
- 'spec/controllers/passwords_controller_spec.rb'
- 'spec/controllers/profiles/emails_controller_spec.rb'
- 'spec/controllers/profiles/notifications_controller_spec.rb'
- - 'spec/controllers/profiles/personal_access_tokens_controller_spec.rb'
- 'spec/controllers/projects/alerting/notifications_controller_spec.rb'
- 'spec/controllers/projects/artifacts_controller_spec.rb'
- 'spec/controllers/projects/badges_controller_spec.rb'
@@ -1143,7 +1075,6 @@ RSpec/ContextWording:
- 'spec/controllers/projects/boards_controller_spec.rb'
- 'spec/controllers/projects/branches_controller_spec.rb'
- 'spec/controllers/projects/ci/lints_controller_spec.rb'
- - 'spec/controllers/projects/ci/secure_files_controller_spec.rb'
- 'spec/controllers/projects/clusters_controller_spec.rb'
- 'spec/controllers/projects/commit_controller_spec.rb'
- 'spec/controllers/projects/commits_controller_spec.rb'
@@ -1175,7 +1106,6 @@ RSpec/ContextWording:
- 'spec/controllers/projects/pages_domains_controller_spec.rb'
- 'spec/controllers/projects/performance_monitoring/dashboards_controller_spec.rb'
- 'spec/controllers/projects/pipelines_controller_spec.rb'
- - 'spec/controllers/projects/product_analytics_controller_spec.rb'
- 'spec/controllers/projects/project_members_controller_spec.rb'
- 'spec/controllers/projects/prometheus/alerts_controller_spec.rb'
- 'spec/controllers/projects/prometheus/metrics_controller_spec.rb'
@@ -1342,7 +1272,6 @@ RSpec/ContextWording:
- 'spec/features/merge_requests/user_lists_merge_requests_spec.rb'
- 'spec/features/merge_requests/user_mass_updates_spec.rb'
- 'spec/features/milestones/user_views_milestone_spec.rb'
- - 'spec/features/monitor_sidebar_link_spec.rb'
- 'spec/features/nav/top_nav_responsive_spec.rb'
- 'spec/features/oauth_login_spec.rb'
- 'spec/features/one_trust_spec.rb'
@@ -1481,7 +1410,6 @@ RSpec/ContextWording:
- 'spec/finders/license_template_finder_spec.rb'
- 'spec/finders/merge_request_target_project_finder_spec.rb'
- 'spec/finders/merge_requests/by_approvals_finder_spec.rb'
- - 'spec/finders/merge_requests_finder/params_spec.rb'
- 'spec/finders/merge_requests_finder_spec.rb'
- 'spec/finders/metrics/dashboards/annotations_finder_spec.rb'
- 'spec/finders/metrics/users_starred_dashboards_finder_spec.rb'
@@ -1519,7 +1447,6 @@ RSpec/ContextWording:
- 'spec/frontend/fixtures/search.rb'
- 'spec/frontend/fixtures/u2f.rb'
- 'spec/frontend/fixtures/webauthn.rb'
- - 'spec/graphql/features/feature_flag_spec.rb'
- 'spec/graphql/mutations/alert_management/create_alert_issue_spec.rb'
- 'spec/graphql/mutations/alert_management/http_integration/create_spec.rb'
- 'spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb'
@@ -1630,7 +1557,6 @@ RSpec/ContextWording:
- 'spec/helpers/routing/pseudonymization_helper_spec.rb'
- 'spec/helpers/search_helper_spec.rb'
- 'spec/helpers/sorting_helper_spec.rb'
- - 'spec/helpers/storage_helper_spec.rb'
- 'spec/helpers/submodule_helper_spec.rb'
- 'spec/helpers/timeboxes_helper_spec.rb'
- 'spec/helpers/todos_helper_spec.rb'
@@ -1752,10 +1678,7 @@ RSpec/ContextWording:
- 'spec/lib/container_registry/migration_spec.rb'
- 'spec/lib/container_registry/registry_spec.rb'
- 'spec/lib/container_registry/tag_spec.rb'
- - 'spec/lib/error_tracking/sentry_client/event_spec.rb'
- - 'spec/lib/error_tracking/sentry_client/issue_link_spec.rb'
- 'spec/lib/error_tracking/sentry_client/projects_spec.rb'
- - 'spec/lib/error_tracking/sentry_client/repo_spec.rb'
- 'spec/lib/expand_variables_spec.rb'
- 'spec/lib/extracts_path_spec.rb'
- 'spec/lib/extracts_ref_spec.rb'
@@ -1792,7 +1715,6 @@ RSpec/ContextWording:
- 'spec/lib/gitlab/avatar_cache_spec.rb'
- 'spec/lib/gitlab/background_migration/backfill_ci_queuing_tables_spec.rb'
- 'spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb'
- - 'spec/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy_spec.rb'
- 'spec/lib/gitlab/background_migration/cleanup_draft_data_from_faulty_regex_spec.rb'
- 'spec/lib/gitlab/background_migration/copy_column_using_background_migration_job_spec.rb'
- 'spec/lib/gitlab/background_migration/disable_expiration_policies_linked_to_no_container_images_spec.rb'
@@ -1983,8 +1905,6 @@ RSpec/ContextWording:
- 'spec/lib/gitlab/error_tracking/processor/sidekiq_processor_spec.rb'
- 'spec/lib/gitlab/error_tracking_spec.rb'
- 'spec/lib/gitlab/exclusive_lease_helpers/sleeping_lock_spec.rb'
- - 'spec/lib/gitlab/experimentation/controller_concern_spec.rb'
- - 'spec/lib/gitlab/experimentation_spec.rb'
- 'spec/lib/gitlab/external_authorization/access_spec.rb'
- 'spec/lib/gitlab/favicon_spec.rb'
- 'spec/lib/gitlab/file_hook_spec.rb'
@@ -1999,7 +1919,6 @@ RSpec/ContextWording:
- 'spec/lib/gitlab/git/commit_spec.rb'
- 'spec/lib/gitlab/git/compare_spec.rb'
- 'spec/lib/gitlab/git/conflict/parser_spec.rb'
- - 'spec/lib/gitlab/git/cross_repo_comparer_spec.rb'
- 'spec/lib/gitlab/git/diff_collection_spec.rb'
- 'spec/lib/gitlab/git/diff_spec.rb'
- 'spec/lib/gitlab/git/merge_base_spec.rb'
@@ -2076,9 +1995,7 @@ RSpec/ContextWording:
- 'spec/lib/gitlab/import_export/project/tree_restorer_spec.rb'
- 'spec/lib/gitlab/import_export/project/tree_saver_spec.rb'
- 'spec/lib/gitlab/import_export/version_checker_spec.rb'
- - 'spec/lib/gitlab/incoming_email_spec.rb'
- 'spec/lib/gitlab/instrumentation/redis_base_spec.rb'
- - 'spec/lib/gitlab/instrumentation/redis_cluster_validator_spec.rb'
- 'spec/lib/gitlab/instrumentation_helper_spec.rb'
- 'spec/lib/gitlab/internal_post_receive/response_spec.rb'
- 'spec/lib/gitlab/issuable/clone/attributes_rewriter_spec.rb'
@@ -2105,7 +2022,7 @@ RSpec/ContextWording:
- 'spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb'
- 'spec/lib/gitlab/memory/reports_daemon_spec.rb'
- 'spec/lib/gitlab/memory/watchdog_spec.rb'
- - 'spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb'
+ - 'spec/lib/gitlab/merge_requests/message_generator_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/cache_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/importer_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb'
@@ -2115,7 +2032,6 @@ RSpec/ContextWording:
- 'spec/lib/gitlab/metrics/dashboard/validator/post_schema_validator_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/validator_spec.rb'
- 'spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb'
- - 'spec/lib/gitlab/metrics/method_call_spec.rb'
- 'spec/lib/gitlab/metrics/methods_spec.rb'
- 'spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb'
- 'spec/lib/gitlab/metrics/samplers/threads_sampler_spec.rb'
@@ -2179,7 +2095,6 @@ RSpec/ContextWording:
- 'spec/lib/gitlab/search/sort_options_spec.rb'
- 'spec/lib/gitlab/search_context/controller_concern_spec.rb'
- 'spec/lib/gitlab/search_results_spec.rb'
- - 'spec/lib/gitlab/service_desk_email_spec.rb'
- 'spec/lib/gitlab/sidekiq_config/worker_router_spec.rb'
- 'spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb'
- 'spec/lib/gitlab/sidekiq_logging/json_formatter_spec.rb'
@@ -2207,7 +2122,6 @@ RSpec/ContextWording:
- 'spec/lib/gitlab/spamcheck/client_spec.rb'
- 'spec/lib/gitlab/ssh_public_key_spec.rb'
- 'spec/lib/gitlab/submodule_links_spec.rb'
- - 'spec/lib/gitlab/subscription_portal_spec.rb'
- 'spec/lib/gitlab/suggestions/commit_message_spec.rb'
- 'spec/lib/gitlab/template/finders/global_template_finder_spec.rb'
- 'spec/lib/gitlab/time_tracking_formatter_spec.rb'
@@ -2223,7 +2137,6 @@ RSpec/ContextWording:
- 'spec/lib/gitlab/url_sanitizer_spec.rb'
- 'spec/lib/gitlab/usage/metric_definition_spec.rb'
- 'spec/lib/gitlab/usage/metric_spec.rb'
- - 'spec/lib/gitlab/usage/metrics/aggregates/aggregate_spec.rb'
- 'spec/lib/gitlab/usage/metrics/aggregates/sources/postgres_hll_spec.rb'
- 'spec/lib/gitlab/usage/metrics/instrumentations/redis_hll_metric_spec.rb'
- 'spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb'
@@ -2232,7 +2145,6 @@ RSpec/ContextWording:
- 'spec/lib/gitlab/usage/service_ping/payload_keys_processor_spec.rb'
- 'spec/lib/gitlab/usage/service_ping_report_spec.rb'
- 'spec/lib/gitlab/usage_data/topology_spec.rb'
- - 'spec/lib/gitlab/usage_data_counters/ci_template_unique_counter_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/ipynb_diff_activity_counter_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/kubernetes_agent_counter_spec.rb'
@@ -2360,7 +2272,6 @@ RSpec/ContextWording:
- 'spec/models/concerns/ci/artifactable_spec.rb'
- 'spec/models/concerns/ci/has_ref_spec.rb'
- 'spec/models/concerns/ci/has_status_spec.rb'
- - 'spec/models/concerns/cross_database_modification_spec.rb'
- 'spec/models/concerns/deployment_platform_spec.rb'
- 'spec/models/concerns/ignorable_columns_spec.rb'
- 'spec/models/concerns/issuable_spec.rb'
@@ -2397,13 +2308,11 @@ RSpec/ContextWording:
- 'spec/models/environment_status_spec.rb'
- 'spec/models/error_tracking/error_spec.rb'
- 'spec/models/event_spec.rb'
- - 'spec/models/experiment_spec.rb'
- 'spec/models/external_pull_request_spec.rb'
- 'spec/models/gpg_key_spec.rb'
- 'spec/models/grafana_integration_spec.rb'
- 'spec/models/group_label_spec.rb'
- 'spec/models/group_spec.rb'
- - 'spec/models/hooks/active_hook_filter_spec.rb'
- 'spec/models/hooks/project_hook_spec.rb'
- 'spec/models/hooks/system_hook_spec.rb'
- 'spec/models/identity_spec.rb'
@@ -2429,7 +2338,6 @@ RSpec/ContextWording:
- 'spec/models/integrations/microsoft_teams_spec.rb'
- 'spec/models/integrations/pipelines_email_spec.rb'
- 'spec/models/integrations/prometheus_spec.rb'
- - 'spec/models/integrations/slack_spec.rb'
- 'spec/models/integrations/teamcity_spec.rb'
- 'spec/models/internal_id_spec.rb'
- 'spec/models/issue/metrics_spec.rb'
@@ -2446,7 +2354,6 @@ RSpec/ContextWording:
- 'spec/models/merge_request_assignee_spec.rb'
- 'spec/models/merge_request_diff_file_spec.rb'
- 'spec/models/merge_request_diff_spec.rb'
- - 'spec/models/merge_request_reviewer_spec.rb'
- 'spec/models/merge_request_spec.rb'
- 'spec/models/metrics/dashboard/annotation_spec.rb'
- 'spec/models/metrics/users_starred_dashboard_spec.rb'
@@ -2610,7 +2517,6 @@ RSpec/ContextWording:
- 'spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb'
- 'spec/requests/api/graphql/mutations/alert_management/alerts/todo/create_spec.rb'
- 'spec/requests/api/graphql/mutations/boards/issues/issue_move_list_spec.rb'
- - 'spec/requests/api/graphql/mutations/branches/create_spec.rb'
- 'spec/requests/api/graphql/mutations/ci/runners_registration_token/reset_spec.rb'
- 'spec/requests/api/graphql/mutations/commits/create_spec.rb'
- 'spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb'
@@ -2620,19 +2526,16 @@ RSpec/ContextWording:
- 'spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb'
- 'spec/requests/api/graphql/mutations/issues/update_spec.rb'
- 'spec/requests/api/graphql/mutations/merge_requests/create_spec.rb'
- - 'spec/requests/api/graphql/mutations/merge_requests/request_attention_spec.rb'
- 'spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb'
- 'spec/requests/api/graphql/mutations/releases/create_spec.rb'
- 'spec/requests/api/graphql/mutations/releases/delete_spec.rb'
- 'spec/requests/api/graphql/mutations/releases/update_spec.rb'
- - 'spec/requests/api/graphql/mutations/remove_attention_request_spec.rb'
- 'spec/requests/api/graphql/mutations/todos/create_spec.rb'
- 'spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb'
- 'spec/requests/api/graphql/mutations/work_items/create_spec.rb'
- 'spec/requests/api/graphql/mutations/work_items/delete_task_spec.rb'
- 'spec/requests/api/graphql/mutations/work_items/update_spec.rb'
- 'spec/requests/api/graphql/mutations/work_items/update_task_spec.rb'
- - 'spec/requests/api/graphql/mutations/work_items/update_widgets_spec.rb'
- 'spec/requests/api/graphql/namespace_query_spec.rb'
- 'spec/requests/api/graphql/packages/maven_spec.rb'
- 'spec/requests/api/graphql/packages/package_spec.rb'
@@ -2643,7 +2546,6 @@ RSpec/ContextWording:
- 'spec/requests/api/graphql/project/issue/design_collection/version_spec.rb'
- 'spec/requests/api/graphql/project/issue/designs/designs_spec.rb'
- 'spec/requests/api/graphql/project/issue_spec.rb'
- - 'spec/requests/api/graphql/project/issues_spec.rb'
- 'spec/requests/api/graphql/project/jira_import_spec.rb'
- 'spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb'
- 'spec/requests/api/graphql/project/milestones_spec.rb'
@@ -2720,7 +2622,6 @@ RSpec/ContextWording:
- 'spec/requests/api/terraform/state_version_spec.rb'
- 'spec/requests/api/todos_spec.rb'
- 'spec/requests/api/usage_data_spec.rb'
- - 'spec/requests/api/user_counts_spec.rb'
- 'spec/requests/api/users_preferences_spec.rb'
- 'spec/requests/api/users_spec.rb'
- 'spec/requests/api/v3/github_spec.rb'
@@ -2744,8 +2645,6 @@ RSpec/ContextWording:
- 'spec/requests/mailgun/webhooks_controller_spec.rb'
- 'spec/requests/oauth_tokens_spec.rb'
- 'spec/requests/openid_connect_spec.rb'
- - 'spec/requests/product_analytics/collector_app_attack_spec.rb'
- - 'spec/requests/product_analytics/collector_app_spec.rb'
- 'spec/requests/projects/clusters/integrations_controller_spec.rb'
- 'spec/requests/projects/commits_controller_spec.rb'
- 'spec/requests/projects/google_cloud/configuration_controller_spec.rb'
@@ -2795,12 +2694,10 @@ RSpec/ContextWording:
- 'spec/rubocop/cop/rspec/factory_bot/inline_association_spec.rb'
- 'spec/scripts/pipeline_test_report_builder_spec.rb'
- 'spec/serializers/analytics_build_entity_spec.rb'
- - 'spec/serializers/board_serializer_spec.rb'
- 'spec/serializers/ci/pipeline_entity_spec.rb'
- 'spec/serializers/cluster_application_entity_spec.rb'
- 'spec/serializers/deploy_keys/basic_deploy_key_entity_spec.rb'
- 'spec/serializers/diff_file_base_entity_spec.rb'
- - 'spec/serializers/diff_file_entity_spec.rb'
- 'spec/serializers/diffs_entity_spec.rb'
- 'spec/serializers/environment_entity_spec.rb'
- 'spec/serializers/environment_serializer_spec.rb'
@@ -2811,8 +2708,6 @@ RSpec/ContextWording:
- 'spec/serializers/member_serializer_spec.rb'
- 'spec/serializers/merge_request_diff_entity_spec.rb'
- 'spec/serializers/merge_request_serializer_spec.rb'
- - 'spec/serializers/merge_request_user_entity_spec.rb'
- - 'spec/serializers/paginated_diff_entity_spec.rb'
- 'spec/serializers/pipeline_details_entity_spec.rb'
- 'spec/serializers/user_serializer_spec.rb'
- 'spec/services/access_token_validation_service_spec.rb'
@@ -2838,7 +2733,6 @@ RSpec/ContextWording:
- 'spec/services/ci/create_pipeline_service/cache_spec.rb'
- 'spec/services/ci/create_pipeline_service/custom_config_content_spec.rb'
- 'spec/services/ci/create_pipeline_service/include_spec.rb'
- - 'spec/services/ci/create_pipeline_service/logger_spec.rb'
- 'spec/services/ci/create_pipeline_service/merge_requests_spec.rb'
- 'spec/services/ci/create_pipeline_service/needs_spec.rb'
- 'spec/services/ci/create_pipeline_service/parallel_spec.rb'
@@ -2865,13 +2759,7 @@ RSpec/ContextWording:
- 'spec/services/clusters/agent_tokens/track_usage_service_spec.rb'
- 'spec/services/clusters/agents/delete_expired_events_service_spec.rb'
- 'spec/services/clusters/agents/refresh_authorization_service_spec.rb'
- - 'spec/services/clusters/applications/check_uninstall_progress_service_spec.rb'
- - 'spec/services/clusters/applications/create_service_spec.rb'
- - 'spec/services/clusters/applications/destroy_service_spec.rb'
- 'spec/services/clusters/applications/install_service_spec.rb'
- - 'spec/services/clusters/applications/patch_service_spec.rb'
- - 'spec/services/clusters/applications/uninstall_service_spec.rb'
- - 'spec/services/clusters/applications/update_service_spec.rb'
- 'spec/services/clusters/applications/upgrade_service_spec.rb'
- 'spec/services/clusters/aws/authorize_role_service_spec.rb'
- 'spec/services/clusters/aws/fetch_credentials_service_spec.rb'
@@ -2881,7 +2769,6 @@ RSpec/ContextWording:
- 'spec/services/clusters/build_kubernetes_namespace_service_spec.rb'
- 'spec/services/clusters/create_service_spec.rb'
- 'spec/services/clusters/gcp/finalize_creation_service_spec.rb'
- - 'spec/services/clusters/kubernetes/configure_istio_ingress_service_spec.rb'
- 'spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb'
- 'spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb'
- 'spec/services/clusters/management/validate_management_project_permissions_service_spec.rb'
@@ -2942,7 +2829,6 @@ RSpec/ContextWording:
- 'spec/services/import/gitlab_projects/file_acquisition_strategies/remote_file_spec.rb'
- 'spec/services/incident_management/incidents/create_service_spec.rb'
- 'spec/services/incident_management/issuable_escalation_statuses/create_service_spec.rb'
- - 'spec/services/incident_management/timeline_events/destroy_service_spec.rb'
- 'spec/services/integrations/test/project_service_spec.rb'
- 'spec/services/issuable/common_system_notes_service_spec.rb'
- 'spec/services/issue_links/list_service_spec.rb'
@@ -2974,7 +2860,6 @@ RSpec/ContextWording:
- 'spec/services/merge_requests/approval_service_spec.rb'
- 'spec/services/merge_requests/base_service_spec.rb'
- 'spec/services/merge_requests/build_service_spec.rb'
- - 'spec/services/merge_requests/bulk_remove_attention_requested_service_spec.rb'
- 'spec/services/merge_requests/cleanup_refs_service_spec.rb'
- 'spec/services/merge_requests/close_service_spec.rb'
- 'spec/services/merge_requests/create_from_issue_service_spec.rb'
@@ -2993,8 +2878,6 @@ RSpec/ContextWording:
- 'spec/services/merge_requests/reload_diffs_service_spec.rb'
- 'spec/services/merge_requests/reopen_service_spec.rb'
- 'spec/services/merge_requests/squash_service_spec.rb'
- - 'spec/services/merge_requests/toggle_attention_requested_service_spec.rb'
- - 'spec/services/merge_requests/update_assignees_service_spec.rb'
- 'spec/services/merge_requests/update_service_spec.rb'
- 'spec/services/metrics/dashboard/annotations/create_service_spec.rb'
- 'spec/services/metrics/dashboard/annotations/delete_service_spec.rb'
@@ -3034,7 +2917,6 @@ RSpec/ContextWording:
- 'spec/services/packages/conan/create_package_service_spec.rb'
- 'spec/services/packages/create_package_file_service_spec.rb'
- 'spec/services/packages/debian/create_distribution_service_spec.rb'
- - 'spec/services/packages/debian/create_package_file_service_spec.rb'
- 'spec/services/packages/debian/extract_metadata_service_spec.rb'
- 'spec/services/packages/debian/find_or_create_incoming_service_spec.rb'
- 'spec/services/packages/debian/find_or_create_package_service_spec.rb'
@@ -3129,7 +3011,6 @@ RSpec/ContextWording:
- 'spec/services/users/approve_service_spec.rb'
- 'spec/services/users/ban_service_spec.rb'
- 'spec/services/users/destroy_service_spec.rb'
- - 'spec/services/users/migrate_to_ghost_user_service_spec.rb'
- 'spec/services/users/refresh_authorized_projects_service_spec.rb'
- 'spec/services/users/reject_service_spec.rb'
- 'spec/services/users/set_status_service_spec.rb'
@@ -3285,7 +3166,6 @@ RSpec/ContextWording:
- 'spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb'
- 'spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb'
- 'spec/support/shared_examples/models/concerns/repository_storage_movable_shared_examples.rb'
- - 'spec/support/shared_examples/models/concerns/timebox_shared_examples.rb'
- 'spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb'
- 'spec/support/shared_examples/models/diff_positionable_note_shared_examples.rb'
- 'spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb'
@@ -3446,7 +3326,6 @@ RSpec/ContextWording:
- 'spec/workers/authorized_project_update/user_refresh_over_user_range_worker_spec.rb'
- 'spec/workers/ci/build_prepare_worker_spec.rb'
- 'spec/workers/cleanup_container_repository_worker_spec.rb'
- - 'spec/workers/cluster_update_app_worker_spec.rb'
- 'spec/workers/clusters/agents/delete_expired_events_worker_spec.rb'
- 'spec/workers/concerns/application_worker_spec.rb'
- 'spec/workers/concerns/waitable_worker_spec.rb'
diff --git a/.rubocop_todo/rspec/described_class.yml b/.rubocop_todo/rspec/described_class.yml
index 516eda12675..729423d8abd 100644
--- a/.rubocop_todo/rspec/described_class.yml
+++ b/.rubocop_todo/rspec/described_class.yml
@@ -1,28 +1,7 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
RSpec/DescribedClass:
Exclude:
- - 'ee/spec/controllers/concerns/gitlab_subscriptions/seat_count_alert_spec.rb'
- - 'ee/spec/controllers/concerns/registrations/verification_spec.rb'
- - 'ee/spec/controllers/concerns/routable_actions_spec.rb'
- - 'ee/spec/controllers/repositories/git_http_controller_spec.rb'
- - 'ee/spec/frontend/fixtures/epic.rb'
- - 'ee/spec/graphql/ee/types/todoable_interface_spec.rb'
- - 'ee/spec/lib/ee/gitlab/background_migration/migrate_shared_vulnerability_scanners_spec.rb'
- - 'ee/spec/lib/ee/gitlab/elastic/helper_spec.rb'
- - 'ee/spec/lib/ee/gitlab/git_access_snippet_spec.rb'
- - 'ee/spec/lib/gitlab/auth/saml/config_spec.rb'
- - 'ee/spec/lib/gitlab/checks/changes_access_spec.rb'
- - 'ee/spec/lib/gitlab/geo/log_helpers_spec.rb'
- - 'ee/spec/lib/gitlab/geo/replicator_spec.rb'
- - 'ee/spec/lib/gitlab/geo_spec.rb'
- - 'ee/spec/lib/gitlab/gl_repository/repo_type_spec.rb'
- - 'ee/spec/lib/gitlab/instrumentation/elasticsearch_transport_spec.rb'
- - 'ee/spec/lib/gitlab/vulnerabilities/findings_preloader_spec.rb'
- - 'ee/spec/lib/omni_auth/strategies/group_saml_spec.rb'
- - 'ee/spec/models/ci/minutes/namespace_monthly_usage_spec.rb'
- - 'ee/spec/models/ci/processable_spec.rb'
- - 'ee/spec/models/concerns/elastic/issue_spec.rb'
- 'ee/spec/models/concerns/elastic/merge_request_spec.rb'
- 'ee/spec/models/concerns/elastic/note_spec.rb'
- 'ee/spec/models/concerns/elastic/project_spec.rb'
@@ -63,7 +42,6 @@ RSpec/DescribedClass:
- 'ee/spec/models/vulnerabilities/flag_spec.rb'
- 'ee/spec/models/vulnerabilities/read_spec.rb'
- 'ee/spec/services/arkose/blocked_users_report_service_spec.rb'
- - 'ee/spec/services/arkose/user_verification_service_spec.rb'
- 'ee/spec/services/ee/resource_events/synthetic_weight_notes_builder_service_spec.rb'
- 'ee/spec/services/ee/users/reject_service_spec.rb'
- 'ee/spec/services/resource_access_tokens/create_service_spec.rb'
@@ -74,7 +52,6 @@ RSpec/DescribedClass:
- 'ee/spec/workers/geo/secondary/registry_consistency_worker_spec.rb'
- 'ee/spec/workers/geo/verification_state_backfill_worker_spec.rb'
- 'qa/spec/service/docker_run/base_spec.rb'
- - 'qa/spec/support/formatters/test_stats_formatter_spec.rb'
- 'qa/spec/support/loglinking_spec.rb'
- 'qa/spec/support/page_error_checker_spec.rb'
- 'spec/config/settings_spec.rb'
@@ -136,7 +113,6 @@ RSpec/DescribedClass:
- 'spec/lib/gitlab/email/hook/smime_signature_interceptor_spec.rb'
- 'spec/lib/gitlab/encrypted_configuration_spec.rb'
- 'spec/lib/gitlab/error_tracking/logger_spec.rb'
- - 'spec/lib/gitlab/experimentation/controller_concern_spec.rb'
- 'spec/lib/gitlab/git/blame_spec.rb'
- 'spec/lib/gitlab/git/blob_spec.rb'
- 'spec/lib/gitlab/git/commit_spec.rb'
@@ -205,7 +181,6 @@ RSpec/DescribedClass:
- 'spec/models/customer_relations/organization_spec.rb'
- 'spec/models/dependency_proxy/manifest_spec.rb'
- 'spec/models/deployment_spec.rb'
- - 'spec/models/experiment_spec.rb'
- 'spec/models/group_spec.rb'
- 'spec/models/import_failure_spec.rb'
- 'spec/models/integration_spec.rb'
@@ -259,7 +234,6 @@ RSpec/DescribedClass:
- 'spec/services/auth/dependency_proxy_authentication_service_spec.rb'
- 'spec/services/ci/register_job_service_spec.rb'
- 'spec/services/incident_management/issuable_escalation_statuses/after_update_service_spec.rb'
- - 'spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb'
- 'spec/services/issuable/process_assignees_spec.rb'
- 'spec/services/loose_foreign_keys/cleaner_service_spec.rb'
- 'spec/services/merge_requests/update_service_spec.rb'
@@ -267,7 +241,6 @@ RSpec/DescribedClass:
- 'spec/services/projects/create_service_spec.rb'
- 'spec/services/resource_access_tokens/create_service_spec.rb'
- 'spec/services/resource_events/synthetic_label_notes_builder_service_spec.rb'
- - 'spec/services/service_ping/submit_service_ping_service_spec.rb'
- 'spec/services/snippets/update_service_spec.rb'
- 'spec/services/system_hooks_service_spec.rb'
- 'spec/services/user_project_access_changed_service_spec.rb'
diff --git a/.rubocop_todo/rspec/empty_example_group.yml b/.rubocop_todo/rspec/empty_example_group.yml
deleted file mode 100644
index 09906638cd8..00000000000
--- a/.rubocop_todo/rspec/empty_example_group.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-RSpec/EmptyExampleGroup:
- Exclude:
- - 'ee/spec/services/personal_access_tokens/revoke_invalid_tokens_spec.rb'
- - 'spec/services/projects/prometheus/alerts/notify_service_spec.rb'
diff --git a/.rubocop_todo/rspec/empty_line_after_example_group.yml b/.rubocop_todo/rspec/empty_line_after_example_group.yml
deleted file mode 100644
index 80d60ee181e..00000000000
--- a/.rubocop_todo/rspec/empty_line_after_example_group.yml
+++ /dev/null
@@ -1,39 +0,0 @@
----
-# Cop supports --auto-correct.
-RSpec/EmptyLineAfterExampleGroup:
- Exclude:
- - 'ee/spec/controllers/groups/clusters_controller_spec.rb'
- - 'ee/spec/controllers/groups/groups_controller_spec.rb'
- - 'ee/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb'
- - 'ee/spec/features/security/group/private_access_spec.rb'
- - 'ee/spec/lib/gitlab/vulnerabilities/container_scanning_vulnerability_spec.rb'
- - 'ee/spec/services/ee/gpg_keys/create_service_spec.rb'
- - 'ee/spec/services/ee/issues/create_from_vulnerability_data_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/confirm_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/dismiss_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/resolve_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/revert_to_detected_service_spec.rb'
- - 'ee/spec/services/vulnerability_issue_links/create_service_spec.rb'
- - 'ee/spec/services/vulnerability_issue_links/delete_service_spec.rb'
- - 'qa/spec/support/loglinking_spec.rb'
- - 'spec/controllers/explore/projects_controller_spec.rb'
- - 'spec/controllers/projects/notes_controller_spec.rb'
- - 'spec/factories/projects/ci_feature_usages.rb'
- - 'spec/features/security/group/internal_access_spec.rb'
- - 'spec/features/security/group/private_access_spec.rb'
- - 'spec/features/security/group/public_access_spec.rb'
- - 'spec/helpers/blob_helper_spec.rb'
- - 'spec/helpers/git_helper_spec.rb'
- - 'spec/lib/bulk_imports/projects/pipelines/issues_pipeline_spec.rb'
- - 'spec/lib/gitlab/blob_helper_spec.rb'
- - 'spec/lib/gitlab/file_type_detection_spec.rb'
- - 'spec/lib/gitlab/git/repository_spec.rb'
- - 'spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb'
- - 'spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb'
- - 'spec/models/note_spec.rb'
- - 'spec/models/project_feature_spec.rb'
- - 'spec/models/user_spec.rb'
- - 'spec/models/zoom_meeting_spec.rb'
- - 'spec/requests/api/graphql/project/issues_spec.rb'
- - 'spec/requests/api/projects_spec.rb'
- - 'spec/routing/project_routing_spec.rb'
diff --git a/.rubocop_todo/rspec/empty_line_after_hook.yml b/.rubocop_todo/rspec/empty_line_after_hook.yml
index 125055044de..263fde8f38e 100644
--- a/.rubocop_todo/rspec/empty_line_after_hook.yml
+++ b/.rubocop_todo/rspec/empty_line_after_hook.yml
@@ -1,13 +1,11 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
RSpec/EmptyLineAfterHook:
Exclude:
- 'ee/spec/controllers/projects/integrations/zentao/issues_controller_spec.rb'
- 'ee/spec/controllers/projects/push_rules_controller_spec.rb'
- 'ee/spec/features/issues/user_bulk_edits_issues_spec.rb'
- - 'ee/spec/features/profiles/usage_quotas_spec.rb'
- 'ee/spec/lib/ee/api/entities/user_with_admin_spec.rb'
- - 'ee/spec/lib/ee/audit/compliance_framework_changes_auditor_spec.rb'
- 'ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb'
- 'ee/spec/lib/ee/gitlab/scim/provisioning_service_spec.rb'
- 'ee/spec/lib/gitlab/audit/auditor_spec.rb'
@@ -16,17 +14,13 @@ RSpec/EmptyLineAfterHook:
- 'ee/spec/models/ee/key_spec.rb'
- 'ee/spec/models/project_spec.rb'
- 'ee/spec/requests/api/users_spec.rb'
- - 'ee/spec/requests/search_controller_spec.rb'
- 'ee/spec/services/ci/sync_reports_to_approval_rules_service_spec.rb'
- 'ee/spec/services/ee/gpg_keys/destroy_service_spec.rb'
- - 'ee/spec/services/ee/two_factor/destroy_service_spec.rb'
- 'ee/spec/services/external_status_checks/update_service_spec.rb'
- 'ee/spec/services/group_saml/saml_group_links/destroy_service_spec.rb'
- 'ee/spec/services/groups/memberships/export_service_spec.rb'
- 'ee/spec/services/merge_requests/approval_service_spec.rb'
- 'ee/spec/support/shared_examples/policies/protected_environments_shared_examples.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_audit_logs_1_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/project/project_audit_logs_spec.rb'
- 'qa/spec/specs/helpers/quarantine_spec.rb'
- 'qa/spec/support/page_error_checker_spec.rb'
- 'spec/controllers/admin/spam_logs_controller_spec.rb'
diff --git a/.rubocop_todo/rspec/expect_change.yml b/.rubocop_todo/rspec/expect_change.yml
index 83892031a6c..23bb61a5d68 100644
--- a/.rubocop_todo/rspec/expect_change.yml
+++ b/.rubocop_todo/rspec/expect_change.yml
@@ -1,913 +1,464 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
RSpec/ExpectChange:
Details: grace period
Exclude:
- - 'ee/spec/controllers/admin/applications_controller_spec.rb'
- - 'ee/spec/controllers/ee/groups_controller_spec.rb'
- - 'ee/spec/controllers/ee/omniauth_callbacks_controller_spec.rb'
- - 'ee/spec/controllers/ee/registrations_controller_spec.rb'
- - 'ee/spec/controllers/groups/analytics/cycle_analytics/value_streams_controller_spec.rb'
- - 'ee/spec/controllers/groups/epic_issues_controller_spec.rb'
- - 'ee/spec/controllers/groups/epics/notes_controller_spec.rb'
- - 'ee/spec/controllers/groups/epics_controller_spec.rb'
- - 'ee/spec/controllers/groups/issues_controller_spec.rb'
- - 'ee/spec/controllers/groups/saml_providers_controller_spec.rb'
- - 'ee/spec/controllers/groups/scim_oauth_controller_spec.rb'
- - 'ee/spec/controllers/oauth/applications_controller_spec.rb'
- - 'ee/spec/controllers/passwords_controller_spec.rb'
- - 'ee/spec/controllers/profiles/keys_controller_spec.rb'
- - 'ee/spec/controllers/projects/deploy_keys_controller_spec.rb'
- - 'ee/spec/controllers/projects/mirrors_controller_spec.rb'
- - 'ee/spec/controllers/projects/path_locks_controller_spec.rb'
- - 'ee/spec/controllers/projects/protected_environments_controller_spec.rb'
- - 'ee/spec/controllers/projects/repositories_controller_spec.rb'
- - 'ee/spec/controllers/projects/security/vulnerabilities/notes_controller_spec.rb'
- - 'ee/spec/controllers/projects/vulnerability_feedback_controller_spec.rb'
- - 'ee/spec/controllers/projects_controller_spec.rb'
- - 'ee/spec/controllers/trials_controller_spec.rb'
- - 'ee/spec/elastic/migrate/20220119120500_populate_commit_permissions_in_main_index_spec.rb'
- - 'ee/spec/elastic/migrate/20220512150000_pause_indexing_for_unsupported_es_versions_spec.rb'
- - 'ee/spec/features/groups/group_settings_spec.rb'
- - 'ee/spec/features/projects_spec.rb'
- - 'ee/spec/features/signup_spec.rb'
- - 'ee/spec/features/users/login_spec.rb'
- - 'ee/spec/graphql/ee/mutations/ci/runner/update_spec.rb'
- - 'ee/spec/graphql/mutations/boards/epics/create_spec.rb'
- - 'ee/spec/graphql/mutations/compliance_management/frameworks/destroy_spec.rb'
- - 'ee/spec/graphql/mutations/compliance_management/frameworks/update_spec.rb'
- - 'ee/spec/graphql/mutations/dast/profiles/delete_spec.rb'
- - 'ee/spec/graphql/mutations/dast_scanner_profiles/delete_spec.rb'
- - 'ee/spec/graphql/mutations/dast_site_profiles/delete_spec.rb'
- - 'ee/spec/graphql/mutations/dast_site_validations/revoke_spec.rb'
- - 'ee/spec/graphql/types/dast_scanner_profile_type_spec.rb'
- - 'ee/spec/graphql/types/dast_site_profile_type_spec.rb'
- - 'ee/spec/helpers/paid_feature_callout_helper_spec.rb'
- - 'ee/spec/lib/audit/changes_spec.rb'
- - 'ee/spec/lib/audit/external_status_check_changes_auditor_spec.rb'
- - 'ee/spec/lib/audit/group_merge_request_approval_setting_changes_auditor_spec.rb'
- - 'ee/spec/lib/audit/group_push_rules_changes_auditor_spec.rb'
- - 'ee/spec/lib/bulk_imports/projects/pipelines/issues_pipeline_spec.rb'
- - 'ee/spec/lib/bulk_imports/projects/pipelines/push_rule_pipeline_spec.rb'
- - 'ee/spec/lib/ee/audit/compliance_framework_changes_auditor_spec.rb'
- - 'ee/spec/lib/ee/audit/group_changes_auditor_spec.rb'
- - 'ee/spec/lib/ee/audit/project_changes_auditor_spec.rb'
- - 'ee/spec/lib/ee/audit/project_ci_cd_setting_changes_auditor_spec.rb'
- - 'ee/spec/lib/ee/audit/project_feature_changes_auditor_spec.rb'
- - 'ee/spec/lib/ee/audit/project_setting_changes_auditor_spec.rb'
- - 'ee/spec/lib/ee/audit/protected_branches_changes_auditor_spec.rb'
- - 'ee/spec/lib/ee/gitlab/background_migration/create_security_setting_spec.rb'
- - 'ee/spec/lib/ee/gitlab/background_migration/delete_invalid_epic_issues_spec.rb'
- - 'ee/spec/lib/ee/gitlab/background_migration/drop_invalid_remediations_spec.rb'
- - 'ee/spec/lib/ee/gitlab/background_migration/populate_latest_pipeline_ids_spec.rb'
- - 'ee/spec/lib/ee/gitlab/background_migration/recalculate_vulnerability_finding_signatures_for_findings_spec.rb'
- - 'ee/spec/lib/ee/gitlab/cleanup/orphan_job_artifact_files_batch_spec.rb'
- - 'ee/spec/lib/ee/gitlab/elastic/helper_spec.rb'
- - 'ee/spec/lib/ee/gitlab/import_export/project/tree_restorer_spec.rb'
- - 'ee/spec/lib/ee/gitlab/import_export/repo_restorer_spec.rb'
- - 'ee/spec/lib/ee/gitlab/issuable/clone/copy_resource_events_service_spec.rb'
- - 'ee/spec/lib/ee/gitlab/scim/deprovision_service_spec.rb'
- - 'ee/spec/lib/ee/gitlab/scim/provisioning_service_spec.rb'
- - 'ee/spec/lib/gitlab/analytics/cycle_analytics/distinct_stage_loader_spec.rb'
+ - 'ee/spec/graphql/mutations/boards/update_spec.rb'
+ - 'ee/spec/graphql/mutations/incident_management/issuable_resource_link/create_spec.rb'
+ - 'ee/spec/lib/audit/compliance_framework_changes_auditor_spec.rb'
+ - 'ee/spec/lib/audit/group_changes_auditor_spec.rb'
+ - 'ee/spec/lib/audit/project_changes_auditor_spec.rb'
+ - 'ee/spec/lib/audit/project_ci_cd_setting_changes_auditor_spec.rb'
+ - 'ee/spec/lib/audit/project_feature_changes_auditor_spec.rb'
+ - 'ee/spec/lib/audit/project_setting_changes_auditor_spec.rb'
+ - 'ee/spec/lib/audit/protected_branches_changes_auditor_spec.rb'
+ - 'ee/spec/lib/bulk_imports/common/pipelines/boards_pipeline_spec.rb'
+ - 'ee/spec/lib/bulk_imports/groups/pipelines/iterations_cadences_pipeline_spec.rb'
+ - 'ee/spec/lib/bulk_imports/groups/pipelines/iterations_pipeline_spec.rb'
+ - 'ee/spec/lib/ee/feature_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/background_migration/delete_approval_rules_with_vulnerability_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/background_migration/fix_incorrect_max_seats_used_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/background_migration/migrate_shared_vulnerability_scanners_spec.rb'
- 'ee/spec/lib/gitlab/audit/auditor_spec.rb'
- - 'ee/spec/lib/gitlab/auth/group_saml/identity_linker_spec.rb'
- - 'ee/spec/lib/gitlab/auth/group_saml/user_spec.rb'
- - 'ee/spec/lib/gitlab/auth/o_auth/user_spec.rb'
- - 'ee/spec/lib/gitlab/auth/smartcard/certificate_spec.rb'
- - 'ee/spec/lib/gitlab/auth/smartcard/ldap_certificate_spec.rb'
- - 'ee/spec/lib/gitlab/background_migration/migrate_requirements_to_work_items_spec.rb'
- - 'ee/spec/lib/gitlab/cache_spec.rb'
- - 'ee/spec/lib/gitlab/geo/replicator_spec.rb'
- - 'ee/spec/lib/gitlab/mirror_spec.rb'
- - 'ee/spec/migrations/20220411173544_cleanup_orphans_approval_project_rules_spec.rb'
- - 'ee/spec/models/ci/bridge_spec.rb'
- - 'ee/spec/models/ci/minutes/namespace_monthly_usage_spec.rb'
- - 'ee/spec/models/ci/minutes/project_monthly_usage_spec.rb'
- - 'ee/spec/models/concerns/geo/eventable_spec.rb'
- - 'ee/spec/models/concerns/geo/replicable_model_spec.rb'
- - 'ee/spec/models/concerns/geo/verification_state_spec.rb'
- - 'ee/spec/models/container_registry/event_spec.rb'
- - 'ee/spec/models/dast/profile_schedule_spec.rb'
- - 'ee/spec/models/dast_site_spec.rb'
- - 'ee/spec/models/ee/ci/job_artifact_spec.rb'
- - 'ee/spec/models/ee/event_spec.rb'
- - 'ee/spec/models/ee/lfs_object_spec.rb'
- - 'ee/spec/models/ee/merge_request_diff_spec.rb'
- - 'ee/spec/models/ee/pages_deployment_spec.rb'
- - 'ee/spec/models/elastic/index_setting_spec.rb'
- - 'ee/spec/models/elastic/migration_record_spec.rb'
- - 'ee/spec/models/epic_spec.rb'
- - 'ee/spec/models/geo/project_registry_spec.rb'
- - 'ee/spec/models/geo/secondary_usage_data_spec.rb'
- - 'ee/spec/models/issue_spec.rb'
+ - 'ee/spec/lib/gitlab/auth/ldap/access_spec.rb'
+ - 'ee/spec/lib/gitlab/compliance_management/violations/approved_by_committer_spec.rb'
+ - 'ee/spec/lib/gitlab/compliance_management/violations/approved_by_insufficient_users_spec.rb'
+ - 'ee/spec/lib/gitlab/compliance_management/violations/approved_by_merge_request_author_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/hashed_storage_attachments_event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/hashed_storage_migrated_event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/repository_created_event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/repository_deleted_event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/repository_renamed_event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/repository_updated_event_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/events/reset_checksum_event_spec.rb'
+ - 'ee/spec/lib/gitlab/instrumentation/elasticsearch_transport_spec.rb'
+ - 'ee/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb'
+ - 'ee/spec/lib/quality/seeders/vulnerabilities_spec.rb'
+ - 'ee/spec/mailers/license_mailer_spec.rb'
+ - 'ee/spec/migrations/20220517144749_remove_vulnerability_approval_rules_spec.rb'
+ - 'ee/spec/migrations/add_non_null_constraint_for_escalation_rule_on_pending_alert_escalations_spec.rb'
+ - 'ee/spec/models/ee/ci/pending_build_spec.rb'
+ - 'ee/spec/models/ee/group_spec.rb'
+ - 'ee/spec/models/ee/namespace_spec.rb'
+ - 'ee/spec/models/ee/project_group_link_spec.rb'
+ - 'ee/spec/models/ee/user_spec.rb'
+ - 'ee/spec/models/geo_node_spec.rb'
+ - 'ee/spec/models/gitlab_subscription_spec.rb'
+ - 'ee/spec/models/group_wiki_spec.rb'
+ - 'ee/spec/models/incident_management/issuable_escalation_status_spec.rb'
+ - 'ee/spec/models/member_spec.rb'
+ - 'ee/spec/models/product_analytics/jitsu_authentication_spec.rb'
- 'ee/spec/models/project_import_state_spec.rb'
- - 'ee/spec/models/project_member_spec.rb'
- - 'ee/spec/models/project_spec.rb'
- - 'ee/spec/models/repository_spec.rb'
- - 'ee/spec/models/requirements_management/requirement_spec.rb'
- - 'ee/spec/models/requirements_management/test_report_spec.rb'
- - 'ee/spec/models/status_page/published_incident_spec.rb'
- - 'ee/spec/models/vulnerabilities/export_spec.rb'
- - 'ee/spec/models/vulnerabilities/read_spec.rb'
- - 'ee/spec/models/vulnerabilities/statistic_spec.rb'
- - 'ee/spec/replicators/geo/pipeline_replicator_spec.rb'
- - 'ee/spec/requests/api/ci/pipelines_spec.rb'
- - 'ee/spec/requests/api/deployments_spec.rb'
- - 'ee/spec/requests/api/epic_issues_spec.rb'
- - 'ee/spec/requests/api/epics_spec.rb'
- - 'ee/spec/requests/api/geo_spec.rb'
- - 'ee/spec/requests/api/graphql/audit_events/streaming/headers/update_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/create_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/destroy_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/update_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/boards/epic_boards/destroy_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/boards/epics/create_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/compliance_management/frameworks/destroy_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/compliance_management/frameworks/update_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/dast/profiles/create_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/dast/profiles/delete_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/dast_scanner_profiles/delete_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/dast_site_profiles/delete_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/dast_site_tokens/create_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/dast_site_validations/create_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/dast_site_validations/revoke_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/quality_management/test_cases/create_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/requirements_management/update_requirement_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/security_policy/create_security_policy_project_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/security_policy/unassign_security_policy_project_spec.rb'
+ - 'ee/spec/models/push_rule_spec.rb'
+ - 'ee/spec/models/security/orchestration_policy_configuration_spec.rb'
+ - 'ee/spec/models/upload_spec.rb'
+ - 'ee/spec/replicators/geo/container_repository_replicator_spec.rb'
+ - 'ee/spec/requests/api/ci/minutes_spec.rb'
+ - 'ee/spec/requests/api/ci/triggers_spec.rb'
+ - 'ee/spec/requests/api/ci/variables_spec.rb'
+ - 'ee/spec/requests/api/features_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/boards/create_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/epics/create_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/iterations/cadences/create_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/iterations/cadences/destroy_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/iterations/cadences/update_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/iterations/create_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/iterations/delete_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/iterations/update_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/requirements_management/create_requirement_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/security/finding/create_issue_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/vulnerabilities/create_external_issue_link_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/vulnerabilities/destroy_external_issue_link_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/vulnerabilities/finding_dismiss_spec.rb'
- 'ee/spec/requests/api/graphql/mutations/work_items/update_spec.rb'
- - 'ee/spec/requests/api/group_push_rule_spec.rb'
+ - 'ee/spec/requests/api/graphql/vulnerabilities/external_issue_links_spec.rb'
- 'ee/spec/requests/api/groups_spec.rb'
- - 'ee/spec/requests/api/invitations_spec.rb'
+ - 'ee/spec/requests/api/internal/upcoming_reconciliations_spec.rb'
- 'ee/spec/requests/api/issues_spec.rb'
- - 'ee/spec/requests/api/members_spec.rb'
- - 'ee/spec/requests/api/project_mirror_spec.rb'
- - 'ee/spec/requests/api/project_push_rule_spec.rb'
- - 'ee/spec/requests/api/projects_spec.rb'
- - 'ee/spec/requests/api/releases_spec.rb'
- - 'ee/spec/requests/api/repositories_spec.rb'
- - 'ee/spec/requests/api/status_checks_spec.rb'
- - 'ee/spec/requests/api/todos_spec.rb'
- - 'ee/spec/requests/api/users_spec.rb'
- - 'ee/spec/requests/ee/projects/deploy_tokens_controller_spec.rb'
+ - 'ee/spec/requests/api/provider_identity_spec.rb'
+ - 'ee/spec/requests/api/visual_review_discussions_spec.rb'
- 'ee/spec/requests/groups/epics/related_epic_links_controller_spec.rb'
- 'ee/spec/requests/groups/protected_environments_controller_spec.rb'
- 'ee/spec/requests/groups_controller_spec.rb'
- - 'ee/spec/requests/smartcard_controller_spec.rb'
- - 'ee/spec/services/analytics/cycle_analytics/stages/list_service_spec.rb'
- - 'ee/spec/services/analytics/devops_adoption/enabled_namespaces/bulk_find_or_create_service_spec.rb'
- - 'ee/spec/services/analytics/devops_adoption/enabled_namespaces/find_or_create_service_spec.rb'
- - 'ee/spec/services/app_sec/dast/profiles/create_service_spec.rb'
- - 'ee/spec/services/app_sec/dast/profiles/destroy_service_spec.rb'
- - 'ee/spec/services/app_sec/dast/profiles/update_service_spec.rb'
- - 'ee/spec/services/app_sec/dast/scanner_profiles/destroy_service_spec.rb'
- - 'ee/spec/services/app_sec/dast/site_profile_secret_variables/create_or_update_service_spec.rb'
- - 'ee/spec/services/app_sec/dast/site_profile_secret_variables/destroy_service_spec.rb'
- - 'ee/spec/services/app_sec/dast/site_profiles/audit/update_service_spec.rb'
- - 'ee/spec/services/app_sec/dast/site_profiles/destroy_service_spec.rb'
- - 'ee/spec/services/app_sec/dast/site_tokens/find_or_create_service_spec.rb'
- - 'ee/spec/services/app_sec/dast/site_validations/find_or_create_service_spec.rb'
- - 'ee/spec/services/app_sec/dast/site_validations/revoke_service_spec.rb'
- - 'ee/spec/services/app_sec/dast/site_validations/runner_service_spec.rb'
- - 'ee/spec/services/app_sec/fuzzing/coverage/corpuses/create_service_spec.rb'
- - 'ee/spec/services/application_settings/update_service_spec.rb'
- - 'ee/spec/services/approval_rules/finalize_service_spec.rb'
- - 'ee/spec/services/approval_rules/project_rule_destroy_service_spec.rb'
- - 'ee/spec/services/approval_rules/update_service_spec.rb'
+ - 'ee/spec/requests/registrations/project_creation_spec.rb'
+ - 'ee/spec/requests/registrations_controller_spec.rb'
+ - 'ee/spec/services/app_sec/dast/scanner_profiles/create_service_spec.rb'
+ - 'ee/spec/services/app_sec/dast/scans/run_service_spec.rb'
+ - 'ee/spec/services/app_sec/dast/site_profiles/create_service_spec.rb'
+ - 'ee/spec/services/app_sec/dast/sites/find_or_create_service_spec.rb'
+ - 'ee/spec/services/applications/create_service_spec.rb'
+ - 'ee/spec/services/approval_rules/create_service_spec.rb'
- 'ee/spec/services/audit_event_service_spec.rb'
- - 'ee/spec/services/audit_events/streaming/headers/create_service_spec.rb'
- - 'ee/spec/services/audit_events/streaming/headers/destroy_service_spec.rb'
- - 'ee/spec/services/audit_events/streaming/headers/update_service_spec.rb'
- - 'ee/spec/services/audit_events/user_impersonation_group_audit_event_service_spec.rb'
- - 'ee/spec/services/auto_merge/merge_train_service_spec.rb'
- - 'ee/spec/services/boards/epic_user_preferences/update_service_spec.rb'
- - 'ee/spec/services/boards/epics/create_service_spec.rb'
- - 'ee/spec/services/boards/epics/move_service_spec.rb'
- - 'ee/spec/services/boards/epics/position_create_service_spec.rb'
- - 'ee/spec/services/ci/destroy_pipeline_service_spec.rb'
- - 'ee/spec/services/ci/external_pull_requests/process_github_event_service_spec.rb'
- - 'ee/spec/services/ci/minutes/update_build_minutes_service_spec.rb'
- - 'ee/spec/services/ci/pipeline_trigger_service_spec.rb'
- - 'ee/spec/services/ci/process_build_service_spec.rb'
- - 'ee/spec/services/ci/retry_job_service_spec.rb'
- - 'ee/spec/services/ci/runners/stale_group_runners_prune_service_spec.rb'
- - 'ee/spec/services/ci/subscribe_bridge_service_spec.rb'
- - 'ee/spec/services/ci/trigger_downstream_subscription_service_spec.rb'
- - 'ee/spec/services/compliance_management/frameworks/create_service_spec.rb'
- - 'ee/spec/services/compliance_management/frameworks/destroy_service_spec.rb'
- - 'ee/spec/services/compliance_management/frameworks/update_service_spec.rb'
- - 'ee/spec/services/deploy_keys/create_service_spec.rb'
- - 'ee/spec/services/deployments/approval_service_spec.rb'
- - 'ee/spec/services/deployments/auto_rollback_service_spec.rb'
- - 'ee/spec/services/ee/ci/job_artifacts/destroy_all_expired_service_spec.rb'
- - 'ee/spec/services/ee/ci/job_artifacts/destroy_batch_service_spec.rb'
+ - 'ee/spec/services/audit_events/impersonation_audit_event_service_spec.rb'
+ - 'ee/spec/services/audit_events/protected_branch_audit_event_service_spec.rb'
+ - 'ee/spec/services/audit_events/runner_custom_audit_event_service_spec.rb'
+ - 'ee/spec/services/audit_events/runners_token_audit_event_service_spec.rb'
+ - 'ee/spec/services/boards/create_service_spec.rb'
+ - 'ee/spec/services/ci/audit_variable_change_service_spec.rb'
+ - 'ee/spec/services/ci/create_pipeline_service/compliance_spec.rb'
+ - 'ee/spec/services/ci/create_pipeline_service/cross_needs_artifacts_spec.rb'
+ - 'ee/spec/services/ci/create_pipeline_service/needs_spec.rb'
+ - 'ee/spec/services/ci/minutes/additional_packs/create_service_spec.rb'
+ - 'ee/spec/services/ee/alert_management/alerts/update_service_spec.rb'
+ - 'ee/spec/services/ee/git/wiki_push_service_spec.rb'
+ - 'ee/spec/services/ee/gpg_keys/create_service_spec.rb'
- 'ee/spec/services/ee/gpg_keys/destroy_service_spec.rb'
- - 'ee/spec/services/ee/groups/deploy_tokens/create_service_spec.rb'
- - 'ee/spec/services/ee/groups/deploy_tokens/destroy_service_spec.rb'
- - 'ee/spec/services/ee/groups/deploy_tokens/revoke_service_spec.rb'
- - 'ee/spec/services/ee/issuable/common_system_notes_service_spec.rb'
+ - 'ee/spec/services/ee/incident_management/issuable_escalation_statuses/after_update_service_spec.rb'
+ - 'ee/spec/services/ee/issue_links/create_service_spec.rb'
- 'ee/spec/services/ee/issues/create_service_spec.rb'
- 'ee/spec/services/ee/issues/update_service_spec.rb'
- 'ee/spec/services/ee/keys/destroy_service_spec.rb'
- - 'ee/spec/services/ee/members/create_service_spec.rb'
- 'ee/spec/services/ee/members/destroy_service_spec.rb'
- 'ee/spec/services/ee/members/invite_service_spec.rb'
- - 'ee/spec/services/ee/members/update_service_spec.rb'
- - 'ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb'
- - 'ee/spec/services/ee/merge_requests/update_service_spec.rb'
+ - 'ee/spec/services/ee/merge_requests/create_service_spec.rb'
+ - 'ee/spec/services/ee/merge_requests/update_assignees_service_spec.rb'
+ - 'ee/spec/services/ee/merge_requests/update_reviewers_service_spec.rb'
- 'ee/spec/services/ee/notes/quick_actions_service_spec.rb'
- - 'ee/spec/services/ee/projects/deploy_tokens/create_service_spec.rb'
- - 'ee/spec/services/ee/projects/deploy_tokens/destroy_service_spec.rb'
- - 'ee/spec/services/ee/todos/destroy/entity_leave_service_spec.rb'
+ - 'ee/spec/services/ee/notification_service_spec.rb'
+ - 'ee/spec/services/ee/projects/unlink_fork_service_spec.rb'
+ - 'ee/spec/services/ee/protected_branches/create_service_spec.rb'
+ - 'ee/spec/services/ee/protected_branches/destroy_service_spec.rb'
+ - 'ee/spec/services/ee/protected_branches/update_service_spec.rb'
- 'ee/spec/services/ee/two_factor/destroy_service_spec.rb'
- - 'ee/spec/services/ee/users/approve_service_spec.rb'
- 'ee/spec/services/ee/users/block_service_spec.rb'
- - 'ee/spec/services/ee/users/reject_service_spec.rb'
- - 'ee/spec/services/ee/users/update_service_spec.rb'
+ - 'ee/spec/services/ee/users/create_service_spec.rb'
+ - 'ee/spec/services/ee/users/destroy_service_spec.rb'
- 'ee/spec/services/elastic/indexing_control_service_spec.rb'
- - 'ee/spec/services/elastic/process_initial_bookkeeping_service_spec.rb'
- - 'ee/spec/services/emails/create_service_spec.rb'
- - 'ee/spec/services/emails/destroy_service_spec.rb'
+ - 'ee/spec/services/elastic/process_bookkeeping_service_spec.rb'
- 'ee/spec/services/epic_issues/create_service_spec.rb'
- - 'ee/spec/services/epic_issues/destroy_service_spec.rb'
- - 'ee/spec/services/epics/close_service_spec.rb'
- - 'ee/spec/services/epics/create_service_spec.rb'
- - 'ee/spec/services/epics/reopen_service_spec.rb'
- - 'ee/spec/services/epics/update_service_spec.rb'
- - 'ee/spec/services/external_status_checks/create_service_spec.rb'
- - 'ee/spec/services/external_status_checks/destroy_service_spec.rb'
- - 'ee/spec/services/external_status_checks/update_service_spec.rb'
- - 'ee/spec/services/geo/blob_download_service_spec.rb'
- - 'ee/spec/services/geo/metrics_update_service_spec.rb'
+ - 'ee/spec/services/epics/issue_promote_service_spec.rb'
+ - 'ee/spec/services/epics/related_epic_links/create_service_spec.rb'
+ - 'ee/spec/services/epics/transfer_service_spec.rb'
+ - 'ee/spec/services/geo/container_repository_registry_removal_service_spec.rb'
+ - 'ee/spec/services/geo/file_registry_removal_service_spec.rb'
+ - 'ee/spec/services/geo/node_create_service_spec.rb'
+ - 'ee/spec/services/geo/node_update_service_spec.rb'
- 'ee/spec/services/geo/project_housekeeping_service_spec.rb'
- - 'ee/spec/services/geo/prune_event_log_service_spec.rb'
- - 'ee/spec/services/geo/registry_consistency_service_spec.rb'
- - 'ee/spec/services/geo/repository_registry_removal_service_spec.rb'
- 'ee/spec/services/geo/repository_sync_service_spec.rb'
- - 'ee/spec/services/group_saml/group_managed_accounts/transfer_membership_service_spec.rb'
+ - 'ee/spec/services/geo/repository_updated_service_spec.rb'
+ - 'ee/spec/services/geo/repository_verification_primary_service_spec.rb'
+ - 'ee/spec/services/geo/repository_verification_secondary_service_spec.rb'
+ - 'ee/spec/services/geo/wiki_sync_service_spec.rb'
+ - 'ee/spec/services/gitlab_subscriptions/activate_service_spec.rb'
+ - 'ee/spec/services/gitlab_subscriptions/create_service_spec.rb'
- 'ee/spec/services/group_saml/identity/destroy_service_spec.rb'
- - 'ee/spec/services/group_saml/sign_up_service_spec.rb'
- - 'ee/spec/services/groups/destroy_service_spec.rb'
- - 'ee/spec/services/groups/mark_for_deletion_service_spec.rb'
- - 'ee/spec/services/groups/restore_service_spec.rb'
- - 'ee/spec/services/groups/sync_service_spec.rb'
- - 'ee/spec/services/incident_management/issuable_resource_links/create_service_spec.rb'
+ - 'ee/spec/services/groups/update_repository_storage_service_spec.rb'
+ - 'ee/spec/services/incident_management/escalation_policies/create_service_spec.rb'
+ - 'ee/spec/services/incident_management/incidents/create_sla_service_spec.rb'
+ - 'ee/spec/services/incident_management/incidents/upload_metric_service_spec.rb'
+ - 'ee/spec/services/incident_management/oncall_rotations/create_service_spec.rb'
- 'ee/spec/services/incident_management/oncall_rotations/edit_service_spec.rb'
- - 'ee/spec/services/incident_management/pending_escalations/create_service_spec.rb'
- - 'ee/spec/services/iterations/cadences/create_service_spec.rb'
- - 'ee/spec/services/iterations/update_service_spec.rb'
- - 'ee/spec/services/keys/create_service_spec.rb'
- - 'ee/spec/services/lfs/lock_file_service_spec.rb'
- - 'ee/spec/services/lfs/unlock_file_service_spec.rb'
- - 'ee/spec/services/members/activate_service_spec.rb'
- - 'ee/spec/services/merge_request_approval_settings/update_service_spec.rb'
- - 'ee/spec/services/merge_requests/update_blocks_service_spec.rb'
+ - 'ee/spec/services/incident_management/pending_escalations/process_service_spec.rb'
+ - 'ee/spec/services/iterations/cadences/destroy_service_spec.rb'
+ - 'ee/spec/services/iterations/cadences/update_service_spec.rb'
+ - 'ee/spec/services/iterations/delete_service_spec.rb'
+ - 'ee/spec/services/iterations/roll_over_issues_service_spec.rb'
+ - 'ee/spec/services/projects/after_rename_service_spec.rb'
+ - 'ee/spec/services/projects/alerting/notify_service_spec.rb'
+ - 'ee/spec/services/projects/create_service_spec.rb'
- 'ee/spec/services/projects/destroy_service_spec.rb'
- - 'ee/spec/services/projects/disable_deploy_key_service_spec.rb'
- - 'ee/spec/services/projects/enable_deploy_key_service_spec.rb'
- 'ee/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb'
- 'ee/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb'
- - 'ee/spec/services/projects/import_service_spec.rb'
- - 'ee/spec/services/projects/mark_for_deletion_service_spec.rb'
- - 'ee/spec/services/projects/restore_service_spec.rb'
- 'ee/spec/services/projects/transfer_service_spec.rb'
- - 'ee/spec/services/projects/update_service_spec.rb'
- - 'ee/spec/services/protected_environments/destroy_service_spec.rb'
- - 'ee/spec/services/protected_environments/update_service_spec.rb'
- - 'ee/spec/services/push_rules/create_or_update_service_spec.rb'
- - 'ee/spec/services/requirements_management/process_test_reports_service_spec.rb'
- - 'ee/spec/services/resource_access_tokens/create_service_spec.rb'
- - 'ee/spec/services/resource_access_tokens/revoke_service_spec.rb'
- - 'ee/spec/services/resource_events/change_weight_service_spec.rb'
- - 'ee/spec/services/security/ingestion/finding_map_spec.rb'
- - 'ee/spec/services/security/ingestion/ingest_report_service_spec.rb'
- - 'ee/spec/services/security/ingestion/tasks/ingest_finding_evidence_spec.rb'
- - 'ee/spec/services/security/ingestion/tasks/ingest_finding_identifiers_spec.rb'
- - 'ee/spec/services/security/ingestion/tasks/ingest_finding_links_spec.rb'
- - 'ee/spec/services/security/ingestion/tasks/ingest_finding_signatures_spec.rb'
- - 'ee/spec/services/security/ingestion/tasks/ingest_findings_spec.rb'
- - 'ee/spec/services/security/ingestion/tasks/ingest_identifiers_spec.rb'
- - 'ee/spec/services/security/ingestion/tasks/ingest_remediations_spec.rb'
- - 'ee/spec/services/security/ingestion/tasks/ingest_vulnerabilities/mark_resolved_as_detected_spec.rb'
- - 'ee/spec/services/security/ingestion/tasks/ingest_vulnerabilities_spec.rb'
- - 'ee/spec/services/security/orchestration/assign_service_spec.rb'
- - 'ee/spec/services/security/override_uuids_service_spec.rb'
- - 'ee/spec/services/security/security_orchestration_policies/sync_opened_merge_requests_service_spec.rb'
- - 'ee/spec/services/security/store_scan_service_spec.rb'
- - 'ee/spec/services/start_pull_mirroring_service_spec.rb'
- - 'ee/spec/services/status_page/mark_for_publication_service_spec.rb'
- - 'ee/spec/services/system_notes/epics_service_spec.rb'
- - 'ee/spec/services/system_notes/merge_train_service_spec.rb'
- - 'ee/spec/services/todo_service_spec.rb'
- - 'ee/spec/services/todos/destroy/confidential_epic_service_spec.rb'
- - 'ee/spec/services/upcoming_reconciliations/update_service_spec.rb'
- - 'ee/spec/services/users_ops_dashboard_projects/destroy_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/confirm_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/destroy_dismissal_feedback_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/historical_statistics/adjustment_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/historical_statistics/deletion_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/resolve_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/revert_to_detected_service_spec.rb'
+ - 'ee/spec/services/protected_environments/create_service_spec.rb'
+ - 'ee/spec/services/quality_management/test_cases/create_service_spec.rb'
+ - 'ee/spec/services/registrations/import_namespace_create_service_spec.rb'
+ - 'ee/spec/services/registrations/standard_namespace_create_service_spec.rb'
+ - 'ee/spec/services/requirements_management/export_csv_service_spec.rb'
+ - 'ee/spec/services/sbom/ingestion/tasks/ingest_component_versions_spec.rb'
+ - 'ee/spec/services/sbom/ingestion/tasks/ingest_components_spec.rb'
+ - 'ee/spec/services/sbom/ingestion/tasks/ingest_occurrences_spec.rb'
+ - 'ee/spec/services/sbom/ingestion/tasks/ingest_sources_spec.rb'
+ - 'ee/spec/services/security/orchestration/unassign_service_spec.rb'
+ - 'ee/spec/services/security/security_orchestration_policies/create_pipeline_service_spec.rb'
+ - 'ee/spec/services/security/security_orchestration_policies/process_rule_service_spec.rb'
+ - 'ee/spec/services/security/security_orchestration_policies/rule_schedule_service_spec.rb'
+ - 'ee/spec/services/security/security_orchestration_policies/sync_scan_result_policies_service_spec.rb'
+ - 'ee/spec/services/security/store_findings_service_spec.rb'
+ - 'ee/spec/services/users/email_verification/send_custom_confirmation_instructions_service_spec.rb'
+ - 'ee/spec/services/vulnerabilities/dismiss_service_spec.rb'
+ - 'ee/spec/services/vulnerabilities/find_or_create_from_security_finding_service_spec.rb'
+ - 'ee/spec/services/vulnerabilities/finding_dismiss_service_spec.rb'
+ - 'ee/spec/services/vulnerabilities/manually_create_service_spec.rb'
- 'ee/spec/services/vulnerabilities/security_finding/create_issue_service_spec.rb'
+ - 'ee/spec/services/vulnerabilities/security_finding/create_merge_request_service_spec.rb'
- 'ee/spec/services/vulnerabilities/starboard_vulnerability_create_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/statistics/adjustment_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/statistics/update_service_spec.rb'
- - 'ee/spec/services/vulnerability_exports/create_service_spec.rb'
- - 'ee/spec/services/vulnerability_exports/export_service_spec.rb'
- - 'ee/spec/services/vulnerability_external_issue_links/create_service_spec.rb'
- - 'ee/spec/services/vulnerability_external_issue_links/destroy_service_spec.rb'
- 'ee/spec/services/vulnerability_feedback/create_service_spec.rb'
- - 'ee/spec/services/vulnerability_feedback/destroy_service_spec.rb'
- - 'ee/spec/services/vulnerability_issue_links/create_service_spec.rb'
- - 'ee/spec/services/vulnerability_issue_links/delete_service_spec.rb'
- - 'ee/spec/support/shared_contexts/audit_event_not_licensed_shared_context.rb'
- - 'ee/spec/support/shared_examples/graphql/mutations/update_health_status_shared_examples.rb'
- - 'ee/spec/support/shared_examples/models/concerns/blob_replicator_strategy_shared_examples.rb'
- - 'ee/spec/support/shared_examples/models/concerns/replicable_model_with_separate_table_shared_examples.rb'
- - 'ee/spec/support/shared_examples/models/concerns/repository_replicator_strategy_shared_examples.rb'
- - 'ee/spec/support/shared_examples/models/concerns/verifiable_replicator_shared_examples.rb'
- - 'ee/spec/support/shared_examples/models/geo_verifiable_registry_shared_examples.rb'
- - 'ee/spec/support/shared_examples/services/group_saml/saml_provider/base_service_shared_examples.rb'
- - 'ee/spec/support/shared_examples/services/update_issuable_health_status_shared_examples.rb'
- - 'ee/spec/support/shared_examples/services/vulnerabilities/removes_dismissal_feedback_from_associated_findings_shared_example.rb'
- - 'ee/spec/tasks/geo/git_rake_spec.rb'
- - 'ee/spec/tasks/gitlab/elastic_rake_spec.rb'
- - 'ee/spec/workers/app_sec/dast/scanner_profiles_builds/consistency_worker_spec.rb'
- - 'ee/spec/workers/app_sec/dast/scans/consistency_worker_spec.rb'
- - 'ee/spec/workers/app_sec/dast/site_profiles_builds/consistency_worker_spec.rb'
- - 'ee/spec/workers/ci/runners/stale_group_runners_prune_cron_worker_spec.rb'
- - 'ee/spec/workers/ci/upstream_projects_subscriptions_cleanup_worker_spec.rb'
- - 'ee/spec/workers/ee/projects/inactive_projects_deletion_cron_worker_spec.rb'
- - 'ee/spec/workers/elastic_remove_expired_namespace_subscriptions_from_index_cron_worker_spec.rb'
- - 'ee/spec/workers/geo/create_repository_updated_event_worker_spec.rb'
- - 'ee/spec/workers/geo/prune_event_log_worker_spec.rb'
- - 'ee/spec/workers/geo/verification_state_backfill_service_spec.rb'
- - 'ee/spec/workers/incident_management/oncall_rotations/persist_shifts_job_spec.rb'
- - 'ee/spec/workers/new_epic_worker_spec.rb'
- - 'ee/spec/workers/store_security_reports_worker_spec.rb'
- - 'ee/spec/workers/vulnerability_exports/export_deletion_worker_spec.rb'
- - 'spec/controllers/admin/applications_controller_spec.rb'
- - 'spec/controllers/admin/clusters_controller_spec.rb'
+ - 'ee/spec/services/vulnerability_merge_request_links/create_service_spec.rb'
+ - 'ee/spec/services/work_items/widgets/iteration_service/update_service_spec.rb'
+ - 'ee/spec/support/shared_examples/graphql/mutations/work_items/update_weight_widget_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/lib/gitlab/geo/geo_log_cursor_event_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/models/incident_management/pending_escalations_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/services/audit_event_logging_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/services/boards/create_boards_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/services/geo_event_store_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/services/issuable_links/create_links_with_link_type.rb'
+ - 'ee/spec/support/shared_examples/services/protected_environments_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/services/vulnerabilities/does_not_create_state_transition_for_same_state.rb'
+ - 'ee/spec/workers/concerns/update_orchestration_policy_configuration_spec.rb'
+ - 'ee/spec/workers/ee/issuable_export_csv_worker_spec.rb'
+ - 'ee/spec/workers/geo/batch/project_registry_scheduler_worker_spec.rb'
+ - 'ee/spec/workers/geo/destroy_worker_spec.rb'
+ - 'ee/spec/workers/geo/file_registry_removal_worker_spec.rb'
+ - 'ee/spec/workers/groups/create_event_worker_spec.rb'
+ - 'ee/spec/workers/import_software_licenses_worker_spec.rb'
+ - 'ee/spec/workers/sync_seat_link_request_worker_spec.rb'
+ - 'ee/spec/workers/update_max_seats_used_for_gitlab_com_subscriptions_worker_spec.rb'
+ - 'ee/spec/workers/vulnerabilities/mark_dropped_as_resolved_worker_spec.rb'
- 'spec/controllers/admin/groups_controller_spec.rb'
- - 'spec/controllers/admin/runners_controller_spec.rb'
- - 'spec/controllers/admin/spam_logs_controller_spec.rb'
- - 'spec/controllers/admin/topics_controller_spec.rb'
- 'spec/controllers/admin/users_controller_spec.rb'
- - 'spec/controllers/groups/clusters_controller_spec.rb'
- - 'spec/controllers/groups/runners_controller_spec.rb'
- - 'spec/controllers/groups/settings/applications_controller_spec.rb'
+ - 'spec/controllers/groups/boards_controller_spec.rb'
+ - 'spec/controllers/groups/group_links_controller_spec.rb'
- 'spec/controllers/groups_controller_spec.rb'
- 'spec/controllers/import/bitbucket_controller_spec.rb'
- 'spec/controllers/import/gitlab_controller_spec.rb'
- - 'spec/controllers/jira_connect/events_controller_spec.rb'
- - 'spec/controllers/ldap/omniauth_callbacks_controller_spec.rb'
- - 'spec/controllers/omniauth_callbacks_controller_spec.rb'
- - 'spec/controllers/profiles/gpg_keys_controller_spec.rb'
- - 'spec/controllers/profiles/keys_controller_spec.rb'
- - 'spec/controllers/projects/artifacts_controller_spec.rb'
- - 'spec/controllers/projects/clusters_controller_spec.rb'
+ - 'spec/controllers/projects/boards_controller_spec.rb'
- 'spec/controllers/projects/deploy_keys_controller_spec.rb'
- - 'spec/controllers/projects/feature_flags_controller_spec.rb'
+ - 'spec/controllers/projects/hooks_controller_spec.rb'
- 'spec/controllers/projects/issues_controller_spec.rb'
- - 'spec/controllers/projects/jobs_controller_spec.rb'
- - 'spec/controllers/projects/merge_requests/creations_controller_spec.rb'
- - 'spec/controllers/projects/merge_requests/drafts_controller_spec.rb'
- - 'spec/controllers/projects/merge_requests_controller_spec.rb'
- 'spec/controllers/projects/mirrors_controller_spec.rb'
- - 'spec/controllers/projects/notes_controller_spec.rb'
- 'spec/controllers/projects/pages_domains_controller_spec.rb'
- - 'spec/controllers/projects/pipeline_schedules_controller_spec.rb'
- - 'spec/controllers/projects/pipelines_controller_spec.rb'
- - 'spec/controllers/projects/raw_controller_spec.rb'
- - 'spec/controllers/projects/runners_controller_spec.rb'
- - 'spec/controllers/projects/settings/ci_cd_controller_spec.rb'
+ - 'spec/controllers/projects/protected_branches_controller_spec.rb'
- 'spec/controllers/projects_controller_spec.rb'
- - 'spec/controllers/repositories/lfs_storage_controller_spec.rb'
- - 'spec/controllers/sessions_controller_spec.rb'
- - 'spec/controllers/snippets/notes_controller_spec.rb'
- - 'spec/controllers/uploads_controller_spec.rb'
- - 'spec/controllers/users/callouts_controller_spec.rb'
- - 'spec/features/admin/users/users_spec.rb'
- - 'spec/features/file_uploads/attachment_spec.rb'
- - 'spec/features/file_uploads/ci_artifact_spec.rb'
- - 'spec/features/file_uploads/git_lfs_spec.rb'
- - 'spec/features/file_uploads/graphql_add_design_spec.rb'
- - 'spec/features/file_uploads/group_import_spec.rb'
- - 'spec/features/file_uploads/maven_package_spec.rb'
- - 'spec/features/file_uploads/nuget_package_spec.rb'
- - 'spec/features/file_uploads/project_import_spec.rb'
- - 'spec/features/file_uploads/rubygem_package_spec.rb'
- - 'spec/features/groups/import_export/import_file_spec.rb'
- - 'spec/features/groups_spec.rb'
- - 'spec/features/invites_spec.rb'
- - 'spec/features/projects/environments/environment_spec.rb'
- - 'spec/features/projects/environments/environments_spec.rb'
- - 'spec/features/projects/import_export/import_file_spec.rb'
- - 'spec/features/projects/pipelines/legacy_pipelines_spec.rb'
- - 'spec/features/projects/pipelines/pipelines_spec.rb'
- - 'spec/features/projects_spec.rb'
- - 'spec/features/users/signup_spec.rb'
- - 'spec/finders/environments/environments_by_deployments_finder_spec.rb'
- - 'spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb'
- - 'spec/graphql/mutations/ci/job_token_scope/remove_project_spec.rb'
- - 'spec/graphql/mutations/ci/runner/bulk_delete_spec.rb'
- - 'spec/graphql/mutations/ci/runner/delete_spec.rb'
- - 'spec/graphql/mutations/clusters/agent_tokens/create_spec.rb'
- - 'spec/graphql/mutations/clusters/agents/create_spec.rb'
- - 'spec/graphql/mutations/clusters/agents/delete_spec.rb'
- - 'spec/graphql/mutations/container_repositories/destroy_spec.rb'
- - 'spec/graphql/mutations/container_repositories/destroy_tags_spec.rb'
- - 'spec/graphql/mutations/dependency_proxy/image_ttl_group_policy/update_spec.rb'
- - 'spec/graphql/mutations/issues/set_severity_spec.rb'
- - 'spec/graphql/mutations/releases/delete_spec.rb'
- - 'spec/graphql/mutations/timelogs/delete_spec.rb'
- - 'spec/helpers/markup_helper_spec.rb'
- - 'spec/helpers/projects_helper_spec.rb'
- - 'spec/initializers/active_record_locking_spec.rb'
+ - 'spec/controllers/registrations_controller_spec.rb'
+ - 'spec/features/admin/admin_hooks_spec.rb'
+ - 'spec/features/projects/settings/webhooks_settings_spec.rb'
+ - 'spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb'
+ - 'spec/graphql/mutations/alert_management/prometheus_integration/create_spec.rb'
+ - 'spec/graphql/mutations/custom_emoji/create_spec.rb'
+ - 'spec/graphql/mutations/incident_management/timeline_event/create_spec.rb'
+ - 'spec/graphql/mutations/incident_management/timeline_event_tag/create_spec.rb'
+ - 'spec/graphql/mutations/merge_requests/create_spec.rb'
+ - 'spec/graphql/mutations/user_callouts/create_spec.rb'
+ - 'spec/graphql/types/base_enum_spec.rb'
+ - 'spec/initializers/1_acts_as_taggable_spec.rb'
+ - 'spec/initializers/load_balancing_spec.rb'
- 'spec/lib/api/helpers_spec.rb'
- - 'spec/lib/banzai/filter/repository_link_filter_spec.rb'
- - 'spec/lib/bulk_imports/projects/pipelines/project_pipeline_spec.rb'
- - 'spec/lib/declarative_enum_spec.rb'
- - 'spec/lib/gitaly/server_spec.rb'
- - 'spec/lib/gitlab/auth/ldap/access_spec.rb'
- - 'spec/lib/gitlab/auth/ldap/user_spec.rb'
- - 'spec/lib/gitlab/auth/o_auth/identity_linker_spec.rb'
- - 'spec/lib/gitlab/auth/saml/identity_linker_spec.rb'
- - 'spec/lib/gitlab/background_migration/add_primary_email_to_emails_if_user_confirmed_spec.rb'
- - 'spec/lib/gitlab/background_migration/backfill_group_features_spec.rb'
- - 'spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb'
- - 'spec/lib/gitlab/background_migration/backfill_projects_with_coverage_spec.rb'
- - 'spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb'
- - 'spec/lib/gitlab/background_migration/migrate_u2f_webauthn_spec.rb'
- - 'spec/lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings_spec.rb'
- - 'spec/lib/gitlab/background_migration/remove_vulnerability_finding_links_spec.rb'
- - 'spec/lib/gitlab/background_task_spec.rb'
- - 'spec/lib/gitlab/bitbucket_import/importer_spec.rb'
- - 'spec/lib/gitlab/bitbucket_server_import/importer_spec.rb'
- - 'spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb'
- - 'spec/lib/gitlab/checks/matching_merge_request_spec.rb'
- - 'spec/lib/gitlab/ci/ansi2json/line_spec.rb'
- - 'spec/lib/gitlab/ci/config/external/context_spec.rb'
- - 'spec/lib/gitlab/ci/parsers/security/common_spec.rb'
- - 'spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb'
- - 'spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb'
- - 'spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb'
- - 'spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb'
- - 'spec/lib/gitlab/ci/pipeline/seed/processable/resource_group_spec.rb'
- - 'spec/lib/gitlab/ci/reports/security/report_spec.rb'
- - 'spec/lib/gitlab/ci/status/factory_spec.rb'
- - 'spec/lib/gitlab/ci/trace/archive_spec.rb'
- - 'spec/lib/gitlab/ci/trace/chunked_io_spec.rb'
- - 'spec/lib/gitlab/composer/cache_spec.rb'
- - 'spec/lib/gitlab/config/entry/validatable_spec.rb'
- - 'spec/lib/gitlab/daemon_spec.rb'
- - 'spec/lib/gitlab/database/async_indexes/index_creator_spec.rb'
- - 'spec/lib/gitlab/database/async_indexes/index_destructor_spec.rb'
+ - 'spec/lib/backup/repositories_spec.rb'
+ - 'spec/lib/bulk_imports/common/pipelines/badges_pipeline_spec.rb'
+ - 'spec/lib/bulk_imports/common/pipelines/boards_pipeline_spec.rb'
+ - 'spec/lib/bulk_imports/common/pipelines/entity_finisher_spec.rb'
+ - 'spec/lib/bulk_imports/common/pipelines/labels_pipeline_spec.rb'
+ - 'spec/lib/bulk_imports/common/pipelines/members_pipeline_spec.rb'
+ - 'spec/lib/bulk_imports/common/pipelines/milestones_pipeline_spec.rb'
+ - 'spec/lib/bulk_imports/groups/pipelines/project_entities_pipeline_spec.rb'
+ - 'spec/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline_spec.rb'
+ - 'spec/lib/bulk_imports/pipeline/runner_spec.rb'
+ - 'spec/lib/gitlab/audit/auditor_spec.rb'
+ - 'spec/lib/gitlab/auth_spec.rb'
+ - 'spec/lib/gitlab/background_migration/backfill_project_repositories_spec.rb'
+ - 'spec/lib/gitlab/background_migration/delete_orphaned_operational_vulnerabilities_spec.rb'
+ - 'spec/lib/gitlab/background_migration/drop_invalid_security_findings_spec.rb'
+ - 'spec/lib/gitlab/background_migration/drop_invalid_vulnerabilities_spec.rb'
+ - 'spec/lib/gitlab/background_migration/populate_namespace_statistics_spec.rb'
+ - 'spec/lib/gitlab/background_migration/project_namespaces/backfill_project_namespaces_spec.rb'
+ - 'spec/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid_spec.rb'
+ - 'spec/lib/gitlab/background_migration/remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb'
+ - 'spec/lib/gitlab/background_migration/remove_self_managed_wiki_notes_spec.rb'
+ - 'spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb'
+ - 'spec/lib/gitlab/background_migration/sanitize_confidential_todos_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb'
+ - 'spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb'
+ - 'spec/lib/gitlab/ci/reports/test_suite_spec.rb'
- 'spec/lib/gitlab/database/background_migration/batched_job_spec.rb'
- - 'spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb'
- 'spec/lib/gitlab/database/background_migration/batched_migration_spec.rb'
- - 'spec/lib/gitlab/database/load_balancing/connection_proxy_spec.rb'
- - 'spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb'
- - 'spec/lib/gitlab/database/migration_helpers_spec.rb'
- - 'spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb'
- - 'spec/lib/gitlab/database/migrations/batched_background_migration_helpers_spec.rb'
- - 'spec/lib/gitlab/database/migrations/observers/query_log_spec.rb'
- - 'spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb'
- - 'spec/lib/gitlab/database/migrations/observers/total_database_size_change_spec.rb'
- - 'spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb'
- - 'spec/lib/gitlab/database/partitioning_spec.rb'
- - 'spec/lib/gitlab/database/reindexing/reindex_action_spec.rb'
- - 'spec/lib/gitlab/email/handler/service_desk_handler_spec.rb'
- - 'spec/lib/gitlab/fogbugz_import/importer_spec.rb'
- - 'spec/lib/gitlab/git/commit_spec.rb'
- - 'spec/lib/gitlab/git/tag_spec.rb'
- - 'spec/lib/gitlab/git_access_project_spec.rb'
- - 'spec/lib/gitlab/github_import/importer/events/changed_assignee_spec.rb'
- - 'spec/lib/gitlab/github_import/importer/events/renamed_spec.rb'
- - 'spec/lib/gitlab/github_import/importer/releases_importer_spec.rb'
+ - 'spec/lib/gitlab/database/tables_truncate_spec.rb'
+ - 'spec/lib/gitlab/diff/file_spec.rb'
+ - 'spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb'
+ - 'spec/lib/gitlab/email/hook/validate_addresses_interceptor_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/events/changed_reviewer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/note_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/protected_branch_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/pull_request_merged_by_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/pull_request_review_importer_spec.rb'
- 'spec/lib/gitlab/hashed_storage/migrator_spec.rb'
- - 'spec/lib/gitlab/import/merge_request_creator_spec.rb'
- - 'spec/lib/gitlab/import_export/base/object_builder_spec.rb'
- - 'spec/lib/gitlab/import_export/lfs_restorer_spec.rb'
- - 'spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb'
- - 'spec/lib/gitlab/issues/rebalancing/state_spec.rb'
- - 'spec/lib/gitlab/jira_import/handle_labels_service_spec.rb'
- - 'spec/lib/gitlab/jira_import/issue_serializer_spec.rb'
- - 'spec/lib/gitlab/manifest_import/project_creator_spec.rb'
- - 'spec/lib/gitlab/metrics/dashboard/importer_spec.rb'
- - 'spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb'
- - 'spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb'
- - 'spec/lib/gitlab/middleware/request_context_spec.rb'
- - 'spec/lib/gitlab/process_memory_cache/helper_spec.rb'
- - 'spec/lib/gitlab/process_supervisor_spec.rb'
- - 'spec/lib/gitlab/query_limiting_spec.rb'
- - 'spec/lib/gitlab/reference_counter_spec.rb'
- - 'spec/lib/gitlab/seeders/ci/daily_build_group_report_result_spec.rb'
- - 'spec/lib/gitlab/slash_commands/issue_move_spec.rb'
- - 'spec/lib/gitlab/word_diff/positions_counter_spec.rb'
- - 'spec/migrations/20210805192450_update_trial_plans_ci_daily_pipeline_schedule_triggers_spec.rb'
- - 'spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb'
- - 'spec/migrations/20211117084814_migrate_remaining_u2f_registrations_spec.rb'
- - 'spec/migrations/20220106111958_add_insert_or_update_vulnerability_reads_trigger_spec.rb'
- - 'spec/migrations/20220106112043_add_update_vulnerability_reads_trigger_spec.rb'
- - 'spec/migrations/20220124130028_dedup_runner_projects_spec.rb'
- - 'spec/migrations/20220202105733_delete_service_template_records_spec.rb'
- - 'spec/migrations/20220222192525_remove_null_releases_spec.rb'
- - 'spec/migrations/20220629184402_unset_escalation_policies_for_alert_incidents_spec.rb'
- - 'spec/migrations/add_new_trail_plans_spec.rb'
- - 'spec/migrations/add_open_source_plan_spec.rb'
- - 'spec/migrations/backfill_cycle_analytics_aggregations_spec.rb'
- - 'spec/migrations/bulk_insert_cluster_enabled_grants_spec.rb'
- - 'spec/migrations/cleanup_after_add_primary_email_to_emails_if_user_confirmed_spec.rb'
- - 'spec/migrations/cleanup_after_fixing_issue_when_admin_changed_primary_email_spec.rb'
- - 'spec/migrations/cleanup_after_fixing_regression_with_new_users_emails_spec.rb'
- - 'spec/migrations/delete_security_findings_without_uuid_spec.rb'
- - 'spec/migrations/populate_audit_event_streaming_verification_token_spec.rb'
- - 'spec/migrations/remove_records_without_group_from_webhooks_table_spec.rb'
- - 'spec/migrations/remove_wiki_notes_spec.rb'
- - 'spec/models/alert_management/alert_spec.rb'
- - 'spec/models/analytics/cycle_analytics/aggregation_spec.rb'
- - 'spec/models/analytics/cycle_analytics/stage_event_hash_spec.rb'
- - 'spec/models/application_setting_spec.rb'
- - 'spec/models/broadcast_message_spec.rb'
- - 'spec/models/chat_name_spec.rb'
- - 'spec/models/ci/bridge_spec.rb'
+ - 'spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb'
+ - 'spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb'
+ - 'spec/lib/gitlab/import_export/importer_spec.rb'
+ - 'spec/lib/gitlab/legacy_github_import/label_formatter_spec.rb'
+ - 'spec/lib/gitlab/legacy_github_import/project_creator_spec.rb'
+ - 'spec/lib/gitlab/pages/cache_control_spec.rb'
+ - 'spec/lib/gitlab/query_limiting/transaction_spec.rb'
+ - 'spec/lib/gitlab/usage_data_counters/kubernetes_agent_counter_spec.rb'
+ - 'spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb'
+ - 'spec/migrations/20220321234317_remove_all_issuable_escalation_statuses_spec.rb'
+ - 'spec/migrations/20220506154054_create_sync_namespace_details_trigger_spec.rb'
+ - 'spec/migrations/20220512190659_remove_web_hooks_web_hook_logs_web_hook_id_fk_spec.rb'
+ - 'spec/migrations/20220524184149_create_sync_project_namespace_details_trigger_spec.rb'
+ - 'spec/migrations/20220802204737_remove_deactivated_user_highest_role_stats_spec.rb'
+ - 'spec/migrations/20220913030624_cleanup_attention_request_related_system_notes_spec.rb'
+ - 'spec/migrations/20221018050323_add_objective_and_keyresult_to_work_item_types_spec.rb'
+ - 'spec/migrations/20221018193635_ensure_task_note_renaming_background_migration_finished_spec.rb'
+ - 'spec/migrations/20221102090940_create_next_ci_partitions_record_spec.rb'
+ - 'spec/migrations/backfill_escalation_policies_for_oncall_schedules_spec.rb'
+ - 'spec/migrations/cleanup_mr_attention_request_todos_spec.rb'
+ - 'spec/migrations/cleanup_vulnerability_state_transitions_with_same_from_state_to_state_spec.rb'
+ - 'spec/migrations/delete_migrate_shared_vulnerability_scanners_spec.rb'
+ - 'spec/migrations/remove_duplicate_dast_site_tokens_spec.rb'
+ - 'spec/migrations/remove_duplicate_dast_site_tokens_with_same_token_spec.rb'
+ - 'spec/models/awareness_session_spec.rb'
+ - 'spec/models/ci/build_metadata_spec.rb'
- 'spec/models/ci/build_spec.rb'
- - 'spec/models/ci/deleted_object_spec.rb'
- - 'spec/models/ci/namespace_mirror_spec.rb'
- - 'spec/models/ci/pipeline_artifact_spec.rb'
- - 'spec/models/ci/pipeline_schedule_spec.rb'
+ - 'spec/models/ci/job_artifact_spec.rb'
- 'spec/models/ci/pipeline_spec.rb'
+ - 'spec/models/ci/pipeline_variable_spec.rb'
- 'spec/models/ci/processable_spec.rb'
- - 'spec/models/ci/project_mirror_spec.rb'
- - 'spec/models/ci/ref_spec.rb'
- 'spec/models/ci/runner_spec.rb'
- - 'spec/models/clusters/applications/runner_spec.rb'
- - 'spec/models/clusters/cluster_spec.rb'
+ - 'spec/models/ci/stage_spec.rb'
- 'spec/models/commit_status_spec.rb'
- - 'spec/models/concerns/atomic_internal_id_spec.rb'
- 'spec/models/concerns/bulk_insert_safe_spec.rb'
- - 'spec/models/concerns/bulk_insertable_associations_spec.rb'
- - 'spec/models/concerns/delete_with_limit_spec.rb'
- - 'spec/models/concerns/ignorable_columns_spec.rb'
- - 'spec/models/concerns/issuable_spec.rb'
- - 'spec/models/concerns/require_email_verification_spec.rb'
- - 'spec/models/concerns/resolvable_discussion_spec.rb'
- - 'spec/models/concerns/resolvable_note_spec.rb'
- - 'spec/models/concerns/routable_spec.rb'
- - 'spec/models/concerns/schedulable_spec.rb'
+ - 'spec/models/concerns/token_authenticatable_spec.rb'
- 'spec/models/container_repository_spec.rb'
- - 'spec/models/customer_relations/issue_contact_spec.rb'
- - 'spec/models/design_management/version_spec.rb'
- - 'spec/models/error_tracking/project_error_tracking_setting_spec.rb'
- - 'spec/models/event_spec.rb'
- - 'spec/models/experiment_spec.rb'
- - 'spec/models/group_spec.rb'
+ - 'spec/models/diff_note_spec.rb'
+ - 'spec/models/environment_spec.rb'
+ - 'spec/models/hooks/web_hook_log_spec.rb'
- 'spec/models/hooks/web_hook_spec.rb'
- - 'spec/models/incident_management/timeline_event_spec.rb'
- - 'spec/models/integrations/prometheus_spec.rb'
- - 'spec/models/internal_id_spec.rb'
- - 'spec/models/issue_spec.rb'
- - 'spec/models/jira_import_state_spec.rb'
- - 'spec/models/lfs_objects_project_spec.rb'
- - 'spec/models/member_spec.rb'
+ - 'spec/models/integration_spec.rb'
+ - 'spec/models/integrations/confluence_spec.rb'
+ - 'spec/models/integrations/drone_ci_spec.rb'
+ - 'spec/models/integrations/shimo_spec.rb'
+ - 'spec/models/label_spec.rb'
+ - 'spec/models/members/last_group_owner_assigner_spec.rb'
+ - 'spec/models/merge_request/cleanup_schedule_spec.rb'
+ - 'spec/models/merge_request_diff_spec.rb'
- 'spec/models/merge_request_spec.rb'
- - 'spec/models/packages/package_file_spec.rb'
+ - 'spec/models/namespace_spec.rb'
+ - 'spec/models/note_spec.rb'
+ - 'spec/models/oauth_access_grant_spec.rb'
+ - 'spec/models/onboarding/progress_spec.rb'
- 'spec/models/packages/package_spec.rb'
- - 'spec/models/pages_domain_spec.rb'
- - 'spec/models/plan_spec.rb'
- - 'spec/models/project_auto_devops_spec.rb'
- - 'spec/models/project_import_state_spec.rb'
- 'spec/models/project_spec.rb'
- - 'spec/models/project_statistics_spec.rb'
- - 'spec/models/projects/build_artifacts_size_refresh_spec.rb'
- - 'spec/models/projects/ci_feature_usage_spec.rb'
- 'spec/models/release_spec.rb'
- - 'spec/models/remote_mirror_spec.rb'
- - 'spec/models/repository_spec.rb'
- - 'spec/models/route_spec.rb'
- - 'spec/models/sent_notification_spec.rb'
- 'spec/models/snippet_spec.rb'
- - 'spec/models/terraform/state_spec.rb'
- - 'spec/models/upload_spec.rb'
- - 'spec/models/user_interacted_project_spec.rb'
+ - 'spec/models/todo_spec.rb'
- 'spec/models/user_spec.rb'
- - 'spec/models/user_status_spec.rb'
- - 'spec/models/x509_certificate_spec.rb'
- - 'spec/models/x509_issuer_spec.rb'
- - 'spec/requests/abuse_reports_controller_spec.rb'
- - 'spec/requests/api/admin/ci/variables_spec.rb'
- - 'spec/requests/api/applications_spec.rb'
- - 'spec/requests/api/broadcast_messages_spec.rb'
- - 'spec/requests/api/ci/pipeline_schedules_spec.rb'
- - 'spec/requests/api/ci/pipelines_spec.rb'
- - 'spec/requests/api/ci/runner/jobs_trace_spec.rb'
+ - 'spec/models/wiki_page_spec.rb'
+ - 'spec/models/work_items/type_spec.rb'
+ - 'spec/requests/api/ci/triggers_spec.rb'
- 'spec/requests/api/commit_statuses_spec.rb'
- 'spec/requests/api/deploy_keys_spec.rb'
- - 'spec/requests/api/deploy_tokens_spec.rb'
- - 'spec/requests/api/error_tracking/collector_spec.rb'
- - 'spec/requests/api/feature_flags_spec.rb'
- - 'spec/requests/api/freeze_periods_spec.rb'
- - 'spec/requests/api/generic_packages_spec.rb'
- - 'spec/requests/api/graphql/mutations/award_emojis/add_spec.rb'
- - 'spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb'
- - 'spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb'
- - 'spec/requests/api/graphql/mutations/boards/destroy_spec.rb'
- 'spec/requests/api/graphql/mutations/clusters/agent_tokens/agent_tokens/create_spec.rb'
- 'spec/requests/api/graphql/mutations/clusters/agents/create_spec.rb'
- - 'spec/requests/api/graphql/mutations/clusters/agents/delete_spec.rb'
- - 'spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb'
- - 'spec/requests/api/graphql/mutations/container_repository/destroy_tags_spec.rb'
- - 'spec/requests/api/graphql/mutations/labels/create_spec.rb'
- - 'spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb'
- - 'spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb'
- - 'spec/requests/api/graphql/mutations/notes/destroy_spec.rb'
- - 'spec/requests/api/graphql/mutations/packages/cleanup/policy/update_spec.rb'
- - 'spec/requests/api/graphql/mutations/releases/delete_spec.rb'
- - 'spec/requests/api/graphql/mutations/snippets/create_spec.rb'
- - 'spec/requests/api/graphql/mutations/snippets/destroy_spec.rb'
- - 'spec/requests/api/graphql/mutations/snippets/update_spec.rb'
- - 'spec/requests/api/group_import_spec.rb'
+ - 'spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb'
+ - 'spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb'
+ - 'spec/requests/api/graphql/mutations/issues/create_spec.rb'
+ - 'spec/requests/api/graphql/mutations/issues/update_spec.rb'
+ - 'spec/requests/api/graphql/mutations/timelogs/delete_spec.rb'
+ - 'spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb'
+ - 'spec/requests/api/graphql/mutations/work_items/create_spec.rb'
+ - 'spec/requests/api/graphql/mutations/work_items/delete_spec.rb'
+ - 'spec/requests/api/graphql/mutations/work_items/delete_task_spec.rb'
+ - 'spec/requests/api/graphql/mutations/work_items/update_spec.rb'
+ - 'spec/requests/api/graphql/mutations/work_items/update_task_spec.rb'
- 'spec/requests/api/groups_spec.rb'
- - 'spec/requests/api/internal/base_spec.rb'
- - 'spec/requests/api/maven_packages_spec.rb'
+ - 'spec/requests/api/issues/post_projects_issues_spec.rb'
+ - 'spec/requests/api/labels_spec.rb'
- 'spec/requests/api/merge_requests_spec.rb'
- - 'spec/requests/api/notes_spec.rb'
- - 'spec/requests/api/npm_project_packages_spec.rb'
- - 'spec/requests/api/project_snippets_spec.rb'
+ - 'spec/requests/api/pages_domains_spec.rb'
+ - 'spec/requests/api/project_milestones_spec.rb'
- 'spec/requests/api/projects_spec.rb'
- - 'spec/requests/api/pypi_packages_spec.rb'
- - 'spec/requests/api/release/links_spec.rb'
- - 'spec/requests/api/releases_spec.rb'
- - 'spec/requests/api/rubygem_packages_spec.rb'
- - 'spec/requests/api/snippets_spec.rb'
- - 'spec/requests/api/terraform/modules/v1/packages_spec.rb'
- - 'spec/requests/api/terraform/state_spec.rb'
- - 'spec/requests/api/terraform/state_version_spec.rb'
- - 'spec/requests/api/users_spec.rb'
- - 'spec/requests/groups/settings/access_tokens_controller_spec.rb'
- - 'spec/requests/import/gitlab_groups_controller_spec.rb'
- - 'spec/requests/oauth_tokens_spec.rb'
- - 'spec/requests/product_analytics/collector_app_spec.rb'
- - 'spec/requests/projects/issue_links_controller_spec.rb'
- - 'spec/requests/projects/merge_requests_discussions_spec.rb'
- - 'spec/requests/projects/settings/access_tokens_controller_spec.rb'
- - 'spec/requests/users/group_callouts_spec.rb'
- - 'spec/requests/users/namespace_callouts_spec.rb'
- - 'spec/requests/users/project_callouts_spec.rb'
- - 'spec/services/alert_management/alerts/todo/create_service_spec.rb'
+ - 'spec/serializers/member_serializer_spec.rb'
- 'spec/services/alert_management/alerts/update_service_spec.rb'
+ - 'spec/services/alert_management/create_alert_issue_service_spec.rb'
+ - 'spec/services/alert_management/metric_images/upload_service_spec.rb'
- 'spec/services/application_settings/update_service_spec.rb'
- - 'spec/services/applications/create_service_spec.rb'
- - 'spec/services/authorized_project_update/project_recalculate_per_user_service_spec.rb'
- - 'spec/services/authorized_project_update/project_recalculate_service_spec.rb'
- - 'spec/services/award_emojis/add_service_spec.rb'
- - 'spec/services/award_emojis/copy_service_spec.rb'
- - 'spec/services/award_emojis/destroy_service_spec.rb'
- - 'spec/services/award_emojis/toggle_service_spec.rb'
- - 'spec/services/bulk_imports/create_service_spec.rb'
- - 'spec/services/bulk_imports/relation_export_service_spec.rb'
- - 'spec/services/ci/build_report_result_service_spec.rb'
+ - 'spec/services/audit_event_service_spec.rb'
+ - 'spec/services/boards/issues/create_service_spec.rb'
+ - 'spec/services/ci/change_variable_service_spec.rb'
- 'spec/services/ci/create_downstream_pipeline_service_spec.rb'
- - 'spec/services/ci/create_pipeline_service_spec.rb'
- - 'spec/services/ci/daily_build_group_report_result_service_spec.rb'
- - 'spec/services/ci/delete_objects_service_spec.rb'
- - 'spec/services/ci/destroy_pipeline_service_spec.rb'
- - 'spec/services/ci/ensure_stage_service_spec.rb'
- - 'spec/services/ci/job_artifacts/create_service_spec.rb'
- - 'spec/services/ci/job_artifacts/delete_service_spec.rb'
- - 'spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb'
- - 'spec/services/ci/job_artifacts/destroy_associations_service_spec.rb'
- - 'spec/services/ci/job_artifacts/destroy_batch_service_spec.rb'
- - 'spec/services/ci/job_artifacts/update_unknown_locked_status_service_spec.rb'
- - 'spec/services/ci/job_token_scope/add_project_service_spec.rb'
- - 'spec/services/ci/job_token_scope/remove_project_service_spec.rb'
- - 'spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb'
- - 'spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb'
- - 'spec/services/ci/pipeline_artifacts/destroy_all_expired_service_spec.rb'
- - 'spec/services/ci/pipeline_trigger_service_spec.rb'
- - 'spec/services/ci/play_build_service_spec.rb'
- - 'spec/services/ci/process_build_service_spec.rb'
- - 'spec/services/ci/retry_job_service_spec.rb'
- - 'spec/services/ci/retry_pipeline_service_spec.rb'
- - 'spec/services/ci/run_scheduled_build_service_spec.rb'
- - 'spec/services/ci/runners/bulk_delete_runners_service_spec.rb'
- - 'spec/services/ci/runners/unregister_runner_service_spec.rb'
- - 'spec/services/ci/update_build_queue_service_spec.rb'
- - 'spec/services/ci/update_build_state_service_spec.rb'
- - 'spec/services/ci/update_instance_variables_service_spec.rb'
+ - 'spec/services/ci/pipelines/add_job_service_spec.rb'
+ - 'spec/services/ci/process_sync_events_service_spec.rb'
+ - 'spec/services/ci/runners/process_runner_version_update_service_spec.rb'
- 'spec/services/clusters/agent_tokens/create_service_spec.rb'
- - 'spec/services/clusters/agents/create_service_spec.rb'
- - 'spec/services/clusters/agents/delete_service_spec.rb'
- - 'spec/services/clusters/create_service_spec.rb'
- - 'spec/services/clusters/destroy_service_spec.rb'
- - 'spec/services/container_expiration_policies/cleanup_service_spec.rb'
- - 'spec/services/deployments/create_for_build_service_spec.rb'
- - 'spec/services/deployments/update_environment_service_spec.rb'
- - 'spec/services/design_management/copy_design_collection/copy_service_spec.rb'
- - 'spec/services/design_management/delete_designs_service_spec.rb'
- - 'spec/services/design_management/design_user_notes_count_service_spec.rb'
+ - 'spec/services/clusters/agents/create_activity_event_service_spec.rb'
+ - 'spec/services/clusters/agents/delete_expired_events_service_spec.rb'
+ - 'spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb'
+ - 'spec/services/design_management/copy_design_collection/queue_service_spec.rb'
- 'spec/services/design_management/save_designs_service_spec.rb'
- - 'spec/services/draft_notes/destroy_service_spec.rb'
- - 'spec/services/draft_notes/publish_service_spec.rb'
- - 'spec/services/emails/create_service_spec.rb'
- - 'spec/services/environments/create_for_build_service_spec.rb'
- - 'spec/services/error_tracking/collect_error_service_spec.rb'
- - 'spec/services/error_tracking/list_projects_service_spec.rb'
- 'spec/services/event_create_service_spec.rb'
- - 'spec/services/feature_flags/create_service_spec.rb'
- - 'spec/services/feature_flags/destroy_service_spec.rb'
- - 'spec/services/feature_flags/update_service_spec.rb'
+ - 'spec/services/events/destroy_service_spec.rb'
- 'spec/services/git/branch_push_service_spec.rb'
- - 'spec/services/git/process_ref_changes_service_spec.rb'
- - 'spec/services/git/tag_hooks_service_spec.rb'
+ - 'spec/services/git/wiki_push_service_spec.rb'
+ - 'spec/services/gpg_keys/destroy_service_spec.rb'
- 'spec/services/groups/create_service_spec.rb'
- - 'spec/services/groups/import_export/import_service_spec.rb'
- - 'spec/services/groups/transfer_service_spec.rb'
- - 'spec/services/groups/update_service_spec.rb'
- - 'spec/services/groups/update_statistics_service_spec.rb'
+ - 'spec/services/import/gitlab_projects/create_project_service_spec.rb'
+ - 'spec/services/incident_management/incidents/create_service_spec.rb'
+ - 'spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb'
+ - 'spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb'
- 'spec/services/incident_management/timeline_events/create_service_spec.rb'
- - 'spec/services/incident_management/timeline_events/update_service_spec.rb'
+ - 'spec/services/issuable/bulk_update_service_spec.rb'
- 'spec/services/issuable/common_system_notes_service_spec.rb'
- - 'spec/services/issues/close_service_spec.rb'
- 'spec/services/issues/create_service_spec.rb'
- - 'spec/services/issues/import_csv_service_spec.rb'
+ - 'spec/services/issues/export_csv_service_spec.rb'
- 'spec/services/issues/move_service_spec.rb'
- - 'spec/services/issues/reopen_service_spec.rb'
- 'spec/services/issues/update_service_spec.rb'
- - 'spec/services/jira_import/start_import_service_spec.rb'
+ - 'spec/services/jira_connect_installations/destroy_service_spec.rb'
+ - 'spec/services/keys/destroy_service_spec.rb'
+ - 'spec/services/labels/find_or_create_service_spec.rb'
- 'spec/services/labels/promote_service_spec.rb'
- - 'spec/services/lfs/lock_file_service_spec.rb'
- - 'spec/services/merge_requests/add_spent_time_service_spec.rb'
- - 'spec/services/merge_requests/close_service_spec.rb'
- - 'spec/services/merge_requests/create_pipeline_service_spec.rb'
- - 'spec/services/merge_requests/create_service_spec.rb'
- - 'spec/services/merge_requests/ff_merge_service_spec.rb'
- - 'spec/services/merge_requests/merge_service_spec.rb'
+ - 'spec/services/labels/transfer_service_spec.rb'
+ - 'spec/services/members/invite_service_spec.rb'
+ - 'spec/services/merge_requests/base_service_spec.rb'
+ - 'spec/services/merge_requests/create_from_issue_service_spec.rb'
- 'spec/services/merge_requests/merge_to_ref_service_spec.rb'
- - 'spec/services/merge_requests/post_merge_service_spec.rb'
- - 'spec/services/merge_requests/push_options_handler_service_spec.rb'
- - 'spec/services/merge_requests/reopen_service_spec.rb'
- - 'spec/services/merge_requests/squash_service_spec.rb'
+ - 'spec/services/merge_requests/mergeability_check_service_spec.rb'
+ - 'spec/services/merge_requests/rebase_service_spec.rb'
+ - 'spec/services/merge_requests/update_assignees_service_spec.rb'
+ - 'spec/services/merge_requests/update_reviewers_service_spec.rb'
- 'spec/services/merge_requests/update_service_spec.rb'
- - 'spec/services/milestones/destroy_service_spec.rb'
- - 'spec/services/namespace_settings/update_service_spec.rb'
- - 'spec/services/namespaces/in_product_marketing_emails_service_spec.rb'
- - 'spec/services/notes/create_service_spec.rb'
- - 'spec/services/notes/destroy_service_spec.rb'
- - 'spec/services/notes/quick_actions_service_spec.rb'
- - 'spec/services/packages/cleanup/update_policy_service_spec.rb'
- - 'spec/services/packages/composer/create_package_service_spec.rb'
- - 'spec/services/packages/create_dependency_service_spec.rb'
- - 'spec/services/packages/create_event_service_spec.rb'
- - 'spec/services/packages/create_temporary_package_service_spec.rb'
- - 'spec/services/packages/debian/find_or_create_package_service_spec.rb'
+ - 'spec/services/milestones/find_or_create_service_spec.rb'
+ - 'spec/services/milestones/transfer_service_spec.rb'
+ - 'spec/services/namespaces/statistics_refresher_service_spec.rb'
- 'spec/services/packages/debian/process_changes_service_spec.rb'
- - 'spec/services/packages/generic/create_package_file_service_spec.rb'
- - 'spec/services/packages/go/create_package_service_spec.rb'
- - 'spec/services/packages/helm/process_file_service_spec.rb'
- - 'spec/services/packages/mark_package_for_destruction_service_spec.rb'
- - 'spec/services/packages/maven/find_or_create_package_service_spec.rb'
- - 'spec/services/packages/npm/create_package_service_spec.rb'
- - 'spec/services/packages/npm/create_tag_service_spec.rb'
- - 'spec/services/packages/nuget/create_dependency_service_spec.rb'
- - 'spec/services/packages/nuget/sync_metadatum_service_spec.rb'
- - 'spec/services/packages/nuget/update_package_from_metadata_service_spec.rb'
- - 'spec/services/packages/pypi/create_package_service_spec.rb'
- - 'spec/services/packages/remove_tag_service_spec.rb'
- - 'spec/services/packages/rubygems/create_dependencies_service_spec.rb'
- - 'spec/services/packages/rubygems/metadata_extraction_service_spec.rb'
- - 'spec/services/packages/terraform_module/create_package_service_spec.rb'
- - 'spec/services/packages/update_package_file_service_spec.rb'
- - 'spec/services/packages/update_tags_service_spec.rb'
- - 'spec/services/pages/delete_service_spec.rb'
- - 'spec/services/pages/destroy_deployments_service_spec.rb'
- - 'spec/services/pages_domains/retry_acme_order_service_spec.rb'
- - 'spec/services/personal_access_tokens/last_used_service_spec.rb'
- - 'spec/services/projects/auto_devops/disable_service_spec.rb'
+ - 'spec/services/packages/debian/process_package_file_service_spec.rb'
+ - 'spec/services/pages_domains/create_service_spec.rb'
+ - 'spec/services/post_receive_service_spec.rb'
+ - 'spec/services/projects/cleanup_service_spec.rb'
- 'spec/services/projects/destroy_service_spec.rb'
- - 'spec/services/projects/fetch_statistics_increment_service_spec.rb'
- - 'spec/services/projects/hashed_storage/migration_service_spec.rb'
- - 'spec/services/projects/hashed_storage/rollback_repository_service_spec.rb'
- - 'spec/services/projects/hashed_storage/rollback_service_spec.rb'
- - 'spec/services/projects/in_product_marketing_campaign_emails_service_spec.rb'
- - 'spec/services/projects/lfs_pointers/lfs_download_service_spec.rb'
- - 'spec/services/projects/record_target_platforms_service_spec.rb'
+ - 'spec/services/projects/detect_repository_languages_service_spec.rb'
+ - 'spec/services/projects/operations/update_service_spec.rb'
+ - 'spec/services/projects/repository_languages_service_spec.rb'
- 'spec/services/projects/transfer_service_spec.rb'
- 'spec/services/projects/update_pages_service_spec.rb'
+ - 'spec/services/projects/update_repository_storage_service_spec.rb'
- 'spec/services/projects/update_service_spec.rb'
- - 'spec/services/quick_actions/interpret_service_spec.rb'
+ - 'spec/services/protected_branches/create_service_spec.rb'
+ - 'spec/services/protected_tags/create_service_spec.rb'
- 'spec/services/releases/create_service_spec.rb'
- - 'spec/services/releases/destroy_service_spec.rb'
- - 'spec/services/resource_access_tokens/create_service_spec.rb'
- - 'spec/services/serverless/associate_domain_service_spec.rb'
- 'spec/services/service_ping/submit_service_ping_service_spec.rb'
+ - 'spec/services/snippets/bulk_destroy_service_spec.rb'
- 'spec/services/snippets/create_service_spec.rb'
- - 'spec/services/snippets/destroy_service_spec.rb'
- - 'spec/services/snippets/update_service_spec.rb'
- - 'spec/services/spam/ham_service_spec.rb'
- - 'spec/services/system_notes/issuables_service_spec.rb'
- - 'spec/services/timelogs/delete_service_spec.rb'
+ - 'spec/services/snippets/update_repository_storage_service_spec.rb'
+ - 'spec/services/snippets/update_statistics_service_spec.rb'
+ - 'spec/services/spam/spam_action_service_spec.rb'
+ - 'spec/services/suggestions/create_service_spec.rb'
+ - 'spec/services/tasks_to_be_done/base_service_spec.rb'
+ - 'spec/services/terraform/states/trigger_destroy_service_spec.rb'
- 'spec/services/todo_service_spec.rb'
- - 'spec/services/todos/destroy/confidential_issue_service_spec.rb'
- - 'spec/services/todos/destroy/design_service_spec.rb'
- - 'spec/services/todos/destroy/entity_leave_service_spec.rb'
- - 'spec/services/todos/destroy/group_private_service_spec.rb'
- - 'spec/services/todos/destroy/project_private_service_spec.rb'
- - 'spec/services/todos/destroy/unauthorized_features_service_spec.rb'
- - 'spec/services/uploads/destroy_service_spec.rb'
+ - 'spec/services/todos/destroy/destroyed_issuable_service_spec.rb'
+ - 'spec/services/users/activity_service_spec.rb'
- 'spec/services/users/approve_service_spec.rb'
- - 'spec/services/users/ban_service_spec.rb'
- - 'spec/services/users/batch_status_cleaner_service_spec.rb'
- - 'spec/services/users/block_service_spec.rb'
- - 'spec/services/users/set_status_service_spec.rb'
- - 'spec/services/users/unban_service_spec.rb'
- - 'spec/services/users/update_highest_member_role_service_spec.rb'
- - 'spec/services/users/update_service_spec.rb'
- - 'spec/services/verify_pages_domain_service_spec.rb'
+ - 'spec/services/users/reject_service_spec.rb'
+ - 'spec/services/users/saved_replies/create_service_spec.rb'
+ - 'spec/services/users/saved_replies/destroy_service_spec.rb'
+ - 'spec/services/users/saved_replies/update_service_spec.rb'
+ - 'spec/services/users/update_todo_count_cache_service_spec.rb'
+ - 'spec/services/web_hook_service_spec.rb'
+ - 'spec/services/web_hooks/destroy_service_spec.rb'
+ - 'spec/services/web_hooks/log_destroy_service_spec.rb'
+ - 'spec/services/web_hooks/log_execution_service_spec.rb'
+ - 'spec/services/wiki_pages/event_create_service_spec.rb'
+ - 'spec/services/work_items/create_and_link_service_spec.rb'
+ - 'spec/services/work_items/create_from_task_service_spec.rb'
+ - 'spec/services/work_items/create_service_spec.rb'
+ - 'spec/services/work_items/delete_task_service_spec.rb'
- 'spec/services/work_items/parent_links/create_service_spec.rb'
- - 'spec/support/services/clusters/create_service_shared.rb'
- - 'spec/support/services/deploy_token_shared_examples.rb'
- - 'spec/support/services/issuable_import_csv_service_shared_examples.rb'
- - 'spec/support/shared_contexts/email_shared_context.rb'
- - 'spec/support/shared_examples/controllers/clusters_controller_shared_examples.rb'
- - 'spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb'
- - 'spec/support/shared_examples/controllers/variables_shared_examples.rb'
- - 'spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb'
- - 'spec/support/shared_examples/graphql/notes_creation_shared_examples.rb'
- - 'spec/support/shared_examples/incident_management/issuable_escalation_statuses/build_examples.rb'
- - 'spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb'
- - 'spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb'
- - 'spec/support/shared_examples/models/commit_signature_shared_examples.rb'
- - 'spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb'
- - 'spec/support/shared_examples/models/concerns/can_move_repository_storage_shared_examples.rb'
- - 'spec/support/shared_examples/models/concerns/cron_schedulable_shared_examples.rb'
- - 'spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb'
- - 'spec/support/shared_examples/models/concerns/limitable_shared_examples.rb'
- - 'spec/support/shared_examples/models/concerns/repository_storage_movable_shared_examples.rb'
- - 'spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb'
- - 'spec/support/shared_examples/models/member_shared_examples.rb'
- - 'spec/support/shared_examples/models/packages/debian/component_file_shared_example.rb'
- - 'spec/support/shared_examples/models/with_uploads_shared_examples.rb'
- - 'spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/graphql/mutations/destroy_list_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/graphql/read_only_instance_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/npm_packages_tags_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/packages_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb'
- - 'spec/support/shared_examples/services/common_system_notes_shared_examples.rb'
- - 'spec/support/shared_examples/services/container_expiration_policy_shared_examples.rb'
- - 'spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb'
- - 'spec/support/shared_examples/services/dependency_proxy_ttl_policies_shared_examples.rb'
- - 'spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb'
- - 'spec/support/shared_examples/services/merge_request_shared_examples.rb'
- - 'spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb'
- - 'spec/support/shared_examples/services/packages_shared_examples.rb'
- - 'spec/support/shared_examples/services/rate_limited_service_shared_examples.rb'
- - 'spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb'
- - 'spec/support/shared_examples/services/updating_mentions_shared_examples.rb'
- - 'spec/support/shared_examples/services/wiki_pages/create_service_shared_examples.rb'
- - 'spec/support/shared_examples/services/wiki_pages/destroy_service_shared_examples.rb'
- - 'spec/support/shared_examples/services/wiki_pages/update_service_shared_examples.rb'
- - 'spec/support/shared_examples/uploaders/object_storage_shared_examples.rb'
- - 'spec/support/shared_examples/uploaders/upload_type_shared_examples.rb'
- - 'spec/tasks/cache/clear/redis_spec.rb'
- - 'spec/tooling/rspec_flaky/listener_spec.rb'
- - 'spec/uploaders/file_uploader_spec.rb'
- - 'spec/uploaders/records_uploads_spec.rb'
- - 'spec/workers/analytics/usage_trends/counter_job_worker_spec.rb'
- - 'spec/workers/destroy_pages_deployments_worker_spec.rb'
- - 'spec/workers/environments/auto_delete_cron_worker_spec.rb'
- - 'spec/workers/gitlab/phabricator_import/base_worker_spec.rb'
- - 'spec/workers/gitlab_service_ping_worker_spec.rb'
- - 'spec/workers/group_import_worker_spec.rb'
- - 'spec/workers/incident_management/process_alert_worker_v2_spec.rb'
- - 'spec/workers/namespaces/root_statistics_worker_spec.rb'
- - 'spec/workers/namespaces/schedule_aggregation_worker_spec.rb'
- - 'spec/workers/new_issue_worker_spec.rb'
- - 'spec/workers/new_merge_request_worker_spec.rb'
- - 'spec/workers/packages/cleanup_package_file_worker_spec.rb'
- - 'spec/workers/packages/composer/cache_cleanup_worker_spec.rb'
- - 'spec/workers/packages/composer/cache_update_worker_spec.rb'
- - 'spec/workers/packages/debian/process_changes_worker_spec.rb'
- - 'spec/workers/packages/go/sync_packages_worker_spec.rb'
- - 'spec/workers/packages/helm/extraction_worker_spec.rb'
- - 'spec/workers/packages/maven/metadata/sync_worker_spec.rb'
- - 'spec/workers/packages/nuget/extraction_worker_spec.rb'
- - 'spec/workers/packages/rubygems/extraction_worker_spec.rb'
- - 'spec/workers/pages_domain_removal_cron_worker_spec.rb'
- - 'spec/workers/projects/post_creation_worker_spec.rb'
- - 'spec/workers/prune_old_events_worker_spec.rb'
- - 'spec/workers/purge_dependency_proxy_cache_worker_spec.rb'
- - 'spec/workers/remove_expired_group_links_worker_spec.rb'
- - 'spec/workers/remove_expired_members_worker_spec.rb'
- - 'spec/workers/remove_unaccepted_member_invites_worker_spec.rb'
- - 'spec/workers/update_highest_role_worker_spec.rb'
- - 'spec/workers/user_status_cleanup/batch_worker_spec.rb'
- - 'spec/workers/users/create_statistics_worker_spec.rb'
- - 'spec/workers/web_hooks/log_destroy_worker_spec.rb'
+ - 'spec/services/work_items/parent_links/destroy_service_spec.rb'
+ - 'spec/services/work_items/task_list_reference_removal_service_spec.rb'
+ - 'spec/services/work_items/task_list_reference_replacement_service_spec.rb'
+ - 'spec/services/work_items/update_service_spec.rb'
+ - 'spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb'
+ - 'spec/services/work_items/widgets/milestone_service/update_service_spec.rb'
+ - 'spec/services/work_items/widgets/start_and_due_date_service/update_service_spec.rb'
+ - 'spec/services/x509_certificate_revoke_service_spec.rb'
diff --git a/.rubocop_todo/rspec/expect_in_hook.yml b/.rubocop_todo/rspec/expect_in_hook.yml
index 70b4105fc89..aa0f5f005d7 100644
--- a/.rubocop_todo/rspec/expect_in_hook.yml
+++ b/.rubocop_todo/rspec/expect_in_hook.yml
@@ -4,7 +4,6 @@ RSpec/ExpectInHook:
- 'ee/spec/controllers/ee/projects/merge_requests/content_controller_spec.rb'
- 'ee/spec/controllers/groups/analytics/productivity_analytics_controller_spec.rb'
- 'ee/spec/controllers/groups/seat_usage_controller_spec.rb'
- - 'ee/spec/controllers/projects/boards_controller_spec.rb'
- 'ee/spec/controllers/projects/settings/slacks_controller_spec.rb'
- 'ee/spec/controllers/subscriptions_controller_spec.rb'
- 'ee/spec/elastic/migrate/20220118150500_delete_orphaned_commits_spec.rb'
@@ -14,11 +13,8 @@ RSpec/ExpectInHook:
- 'ee/spec/features/projects/feature_flags/user_creates_feature_flag_spec.rb'
- 'ee/spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb'
- 'ee/spec/features/projects/settings/ee/service_desk_setting_spec.rb'
- - 'ee/spec/features/registrations/saas_user_registration_spec.rb'
- - 'ee/spec/features/registrations/trial_during_signup_flow_spec.rb'
- 'ee/spec/features/signup_spec.rb'
- 'ee/spec/features/trial_registrations/company_information_spec.rb'
- - 'ee/spec/features/trials/select_namespace_spec.rb'
- 'ee/spec/finders/license_template_finder_spec.rb'
- 'ee/spec/finders/projects/integrations/jira/issues_finder_spec.rb'
- 'ee/spec/finders/template_finder_spec.rb'
@@ -60,7 +56,6 @@ RSpec/ExpectInHook:
- 'ee/spec/models/ee/namespace_spec.rb'
- 'ee/spec/models/gitlab_subscription_spec.rb'
- 'ee/spec/models/license_spec.rb'
- - 'ee/spec/models/member_spec.rb'
- 'ee/spec/models/project_import_state_spec.rb'
- 'ee/spec/models/project_spec.rb'
- 'ee/spec/presenters/group_member_presenter_spec.rb'
@@ -72,7 +67,6 @@ RSpec/ExpectInHook:
- 'ee/spec/services/app_sec/dast/profiles/update_service_spec.rb'
- 'ee/spec/services/auto_merge/add_to_merge_train_when_pipeline_succeeds_service_spec.rb'
- 'ee/spec/services/ci/minutes/batch_reset_service_spec.rb'
- - 'ee/spec/services/ci/runners/assign_runner_service_spec.rb'
- 'ee/spec/services/ci/runners/register_runner_service_spec.rb'
- 'ee/spec/services/ci/runners/reset_registration_token_service_spec.rb'
- 'ee/spec/services/ci/runners/unassign_runner_service_spec.rb'
@@ -89,14 +83,12 @@ RSpec/ExpectInHook:
- 'ee/spec/services/gitlab_subscriptions/reconciliations/calculate_seat_count_data_service_spec.rb'
- 'ee/spec/services/groups/update_repository_storage_service_spec.rb'
- 'ee/spec/services/members/await_service_spec.rb'
- - 'ee/spec/services/merge_requests/approval_service_spec.rb'
- 'ee/spec/services/merge_requests/mergeability/check_approved_service_spec.rb'
- 'ee/spec/services/merge_requests/mergeability/check_blocked_by_other_mrs_service_spec.rb'
- 'ee/spec/services/merge_requests/mergeability/check_denied_policies_service_spec.rb'
- 'ee/spec/services/projects/create_from_template_service_spec.rb'
- 'ee/spec/services/projects/mark_for_deletion_service_spec.rb'
- 'ee/spec/services/projects/update_mirror_service_spec.rb'
- - 'ee/spec/services/security/findings/cleanup_service_spec.rb'
- 'ee/spec/services/upcoming_reconciliations/update_service_spec.rb'
- 'ee/spec/support/shared_examples/lib/gitlab/graphql/issuables_lazy_links_aggregate_shared_examples.rb'
- 'ee/spec/support/shared_examples/models/concerns/elastic/cannot_read_cross_project_shared_examples.rb'
@@ -112,16 +104,11 @@ RSpec/ExpectInHook:
- 'ee/spec/workers/elastic_remove_expired_namespace_subscriptions_from_index_cron_worker_spec.rb'
- 'ee/spec/workers/geo/secondary/registry_consistency_worker_spec.rb'
- 'ee/spec/workers/geo/verification_state_backfill_worker_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/jira/jira_basic_integration_spec.rb'
- 'qa/qa/specs/features/browser_ui/5_package/dependency_proxy/dependency_proxy_spec.rb'
- 'qa/qa/specs/features/browser_ui/5_package/package_registry/composer_registry_spec.rb'
- 'qa/qa/specs/features/browser_ui/5_package/package_registry/generic_repository_spec.rb'
- 'qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/license/cloud_activation_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/12_geo/wiki_http_push_to_secondary_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/instance/instance_audit_logs_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/project/project_audit_logs_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/project/project_templates_spec.rb'
- 'spec/commands/metrics_server/metrics_server_spec.rb'
- 'spec/controllers/admin/runners_controller_spec.rb'
- 'spec/controllers/autocomplete_controller_spec.rb'
@@ -340,7 +327,6 @@ RSpec/ExpectInHook:
- 'spec/models/member_spec.rb'
- 'spec/models/merge_request_diff_spec.rb'
- 'spec/models/merge_request_spec.rb'
- - 'spec/models/personal_access_token_spec.rb'
- 'spec/models/postgresql/replication_slot_spec.rb'
- 'spec/models/project_import_state_spec.rb'
- 'spec/models/project_spec.rb'
@@ -381,7 +367,6 @@ RSpec/ExpectInHook:
- 'spec/requests/api/project_debian_distributions_spec.rb'
- 'spec/requests/api/project_packages_spec.rb'
- 'spec/requests/api/projects_spec.rb'
- - 'spec/requests/api/protected_branches_spec.rb'
- 'spec/requests/api/v3/github_spec.rb'
- 'spec/requests/health_controller_spec.rb'
- 'spec/requests/import/gitlab_groups_controller_spec.rb'
@@ -396,25 +381,17 @@ RSpec/ExpectInHook:
- 'spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb'
- 'spec/services/award_emojis/add_service_spec.rb'
- 'spec/services/captcha/captcha_verification_service_spec.rb'
- - 'spec/services/ci/create_pipeline_service/logger_spec.rb'
- 'spec/services/ci/create_pipeline_service_spec.rb'
- 'spec/services/ci/delete_objects_service_spec.rb'
- 'spec/services/ci/generate_kubeconfig_service_spec.rb'
- 'spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb'
- 'spec/services/ci/job_artifacts/destroy_batch_service_spec.rb'
- 'spec/services/ci/register_job_service_spec.rb'
- - 'spec/services/clusters/applications/check_installation_progress_service_spec.rb'
- - 'spec/services/clusters/applications/check_uninstall_progress_service_spec.rb'
- - 'spec/services/clusters/applications/create_service_spec.rb'
- 'spec/services/clusters/applications/install_service_spec.rb'
- - 'spec/services/clusters/applications/patch_service_spec.rb'
- - 'spec/services/clusters/applications/prometheus_update_service_spec.rb'
- - 'spec/services/clusters/applications/uninstall_service_spec.rb'
- 'spec/services/clusters/applications/upgrade_service_spec.rb'
- 'spec/services/clusters/aws/fetch_credentials_service_spec.rb'
- 'spec/services/clusters/integrations/prometheus_health_check_service_spec.rb'
- 'spec/services/container_expiration_policies/cleanup_service_spec.rb'
- - 'spec/services/database/consistency_check_service_spec.rb'
- 'spec/services/dependency_proxy/find_cached_manifest_service_spec.rb'
- 'spec/services/dependency_proxy/image_ttl_group_policies/update_service_spec.rb'
- 'spec/services/design_management/copy_design_collection/copy_service_spec.rb'
@@ -464,7 +441,6 @@ RSpec/ExpectInHook:
- 'spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb'
- 'spec/services/projects/after_rename_service_spec.rb'
- 'spec/services/projects/branches_by_mode_service_spec.rb'
- - 'spec/services/projects/container_repository/cleanup_tags_service_spec.rb'
- 'spec/services/projects/container_repository/delete_tags_service_spec.rb'
- 'spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb'
- 'spec/services/projects/container_repository/third_party/delete_tags_service_spec.rb'
@@ -480,9 +456,6 @@ RSpec/ExpectInHook:
- 'spec/services/projects/update_pages_service_spec.rb'
- 'spec/services/projects/update_remote_mirror_service_spec.rb'
- 'spec/services/projects/update_repository_storage_service_spec.rb'
- - 'spec/services/protected_branches/create_service_spec.rb'
- - 'spec/services/protected_branches/destroy_service_spec.rb'
- - 'spec/services/protected_branches/update_service_spec.rb'
- 'spec/services/search_service_spec.rb'
- 'spec/services/serverless/associate_domain_service_spec.rb'
- 'spec/services/snippets/update_repository_storage_service_spec.rb'
@@ -491,8 +464,6 @@ RSpec/ExpectInHook:
- 'spec/services/system_notes/commit_service_spec.rb'
- 'spec/services/system_notes/issuables_service_spec.rb'
- 'spec/services/user_project_access_changed_service_spec.rb'
- - 'spec/support/services/issuable_update_service_shared_examples.rb'
- - 'spec/support/services/migrate_to_ghost_user_service_shared_examples.rb'
- 'spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb'
- 'spec/support/shared_examples/csp.rb'
- 'spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb'
@@ -506,7 +477,6 @@ RSpec/ExpectInHook:
- 'spec/support/shared_examples/models/concerns/has_wiki_shared_examples.rb'
- 'spec/support/shared_examples/models/concerns/update_namespace_statistics_shared_examples.rb'
- 'spec/support/shared_examples/models/mentionable_shared_examples.rb'
- - 'spec/support/shared_examples/models/wiki_shared_examples.rb'
- 'spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/repositories_shared_context.rb'
@@ -525,14 +495,11 @@ RSpec/ExpectInHook:
- 'spec/tasks/gitlab/praefect_rake_spec.rb'
- 'spec/tasks/gitlab/task_helpers_spec.rb'
- 'spec/tooling/danger/feature_flag_spec.rb'
- - 'spec/tooling/danger/specs_spec.rb'
- 'spec/tooling/rspec_flaky/listener_spec.rb'
- 'spec/uploaders/file_mover_spec.rb'
- 'spec/uploaders/gitlab_uploader_spec.rb'
- 'spec/uploaders/object_storage_spec.rb'
- - 'spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb'
- 'spec/views/shared/runners/_runner_details.html.haml_spec.rb'
- - 'spec/workers/build_finished_worker_spec.rb'
- 'spec/workers/bulk_imports/pipeline_worker_spec.rb'
- 'spec/workers/ci/build_finished_worker_spec.rb'
- 'spec/workers/concerns/gitlab/github_import/object_importer_spec.rb'
diff --git a/.rubocop_todo/rspec/factory_bot/avoid_create.yml b/.rubocop_todo/rspec/factory_bot/avoid_create.yml
index b41233d83af..3201d5a8370 100644
--- a/.rubocop_todo/rspec/factory_bot/avoid_create.yml
+++ b/.rubocop_todo/rspec/factory_bot/avoid_create.yml
@@ -1,6 +1,107 @@
---
RSpec/FactoryBot/AvoidCreate:
Exclude:
+ - 'ee/spec/components/namespaces/free_user_cap/enforcement_alert_component_spec.rb'
+ - 'ee/spec/components/namespaces/free_user_cap/non_owner_alert_component_spec.rb'
+ - 'ee/spec/components/namespaces/free_user_cap/notification_alert_component_spec.rb'
+ - 'ee/spec/components/namespaces/free_user_cap/usage_quota_alert_component_spec.rb'
+ - 'ee/spec/components/namespaces/free_user_cap/usage_quota_trial_alert_component_spec.rb'
+ - 'ee/spec/components/namespaces/storage/limit_alert_component_spec.rb'
+ - 'ee/spec/components/namespaces/storage/pre_enforcement_alert_component_spec.rb'
+ - 'ee/spec/components/namespaces/storage/project_pre_enforcement_alert_component_spec.rb'
+ - 'ee/spec/components/namespaces/storage/subgroup_pre_enforcement_alert_component_spec.rb'
+ - 'ee/spec/components/namespaces/storage/user_pre_enforcement_alert_component_spec.rb'
+ - 'ee/spec/helpers/admin/ip_restriction_helper_spec.rb'
+ - 'ee/spec/helpers/application_helper_spec.rb'
+ - 'ee/spec/helpers/billing_plans_helper_spec.rb'
+ - 'ee/spec/helpers/boards_helper_spec.rb'
+ - 'ee/spec/helpers/compliance_management/compliance_framework/group_settings_helper_spec.rb'
+ - 'ee/spec/helpers/ee/admin/identities_helper_spec.rb'
+ - 'ee/spec/helpers/ee/blob_helper_spec.rb'
+ - 'ee/spec/helpers/ee/branches_helper_spec.rb'
+ - 'ee/spec/helpers/ee/ci/pipeline_editor_helper_spec.rb'
+ - 'ee/spec/helpers/ee/ci/runners_helper_spec.rb'
+ - 'ee/spec/helpers/ee/dashboard_helper_spec.rb'
+ - 'ee/spec/helpers/ee/environments_helper_spec.rb'
+ - 'ee/spec/helpers/ee/events_helper_spec.rb'
+ - 'ee/spec/helpers/ee/feature_flags_helper_spec.rb'
+ - 'ee/spec/helpers/ee/gitlab_routing_helper_spec.rb'
+ - 'ee/spec/helpers/ee/graph_helper_spec.rb'
+ - 'ee/spec/helpers/ee/groups/analytics/cycle_analytics_helper_spec.rb'
+ - 'ee/spec/helpers/ee/groups/group_members_helper_spec.rb'
+ - 'ee/spec/helpers/ee/groups_helper_spec.rb'
+ - 'ee/spec/helpers/ee/hooks_helper_spec.rb'
+ - 'ee/spec/helpers/ee/integrations_helper_spec.rb'
+ - 'ee/spec/helpers/ee/invite_members_helper_spec.rb'
+ - 'ee/spec/helpers/ee/issuables_helper_spec.rb'
+ - 'ee/spec/helpers/ee/issues_helper_spec.rb'
+ - 'ee/spec/helpers/ee/labels_helper_spec.rb'
+ - 'ee/spec/helpers/ee/learn_gitlab_helper_spec.rb'
+ - 'ee/spec/helpers/ee/lock_helper_spec.rb'
+ - 'ee/spec/helpers/ee/namespace_user_cap_reached_alert_helper_spec.rb'
+ - 'ee/spec/helpers/ee/namespaces_helper_spec.rb'
+ - 'ee/spec/helpers/ee/operations_helper_spec.rb'
+ - 'ee/spec/helpers/ee/personal_access_tokens_helper_spec.rb'
+ - 'ee/spec/helpers/ee/projects/pipeline_helper_spec.rb'
+ - 'ee/spec/helpers/ee/projects/security/api_fuzzing_configuration_helper_spec.rb'
+ - 'ee/spec/helpers/ee/projects/security/configuration_helper_spec.rb'
+ - 'ee/spec/helpers/ee/projects/security/dast_configuration_helper_spec.rb'
+ - 'ee/spec/helpers/ee/projects/security/sast_configuration_helper_spec.rb'
+ - 'ee/spec/helpers/ee/releases_helper_spec.rb'
+ - 'ee/spec/helpers/ee/security_orchestration_helper_spec.rb'
+ - 'ee/spec/helpers/ee/subscribable_banner_helper_spec.rb'
+ - 'ee/spec/helpers/ee/todos_helper_spec.rb'
+ - 'ee/spec/helpers/ee/trial_helper_spec.rb'
+ - 'ee/spec/helpers/ee/users/callouts_helper_spec.rb'
+ - 'ee/spec/helpers/ee/welcome_helper_spec.rb'
+ - 'ee/spec/helpers/ee/wiki_helper_spec.rb'
+ - 'ee/spec/helpers/epics_helper_spec.rb'
+ - 'ee/spec/helpers/gitlab_subscriptions/upcoming_reconciliation_helper_spec.rb'
+ - 'ee/spec/helpers/groups/feature_discovery_moments_helper_spec.rb'
+ - 'ee/spec/helpers/groups/security_features_helper_spec.rb'
+ - 'ee/spec/helpers/incident_management/escalation_policy_helper_spec.rb'
+ - 'ee/spec/helpers/incident_management/oncall_schedule_helper_spec.rb'
+ - 'ee/spec/helpers/license_helper_spec.rb'
+ - 'ee/spec/helpers/license_monitoring_helper_spec.rb'
+ - 'ee/spec/helpers/manual_quarterly_co_term_banner_helper_spec.rb'
+ - 'ee/spec/helpers/markup_helper_spec.rb'
+ - 'ee/spec/helpers/notes_helper_spec.rb'
+ - 'ee/spec/helpers/paid_feature_callout_helper_spec.rb'
+ - 'ee/spec/helpers/path_locks_helper_spec.rb'
+ - 'ee/spec/helpers/prevent_forking_helper_spec.rb'
+ - 'ee/spec/helpers/projects/on_demand_scans_helper_spec.rb'
+ - 'ee/spec/helpers/projects/project_members_helper_spec.rb'
+ - 'ee/spec/helpers/projects/security/dast_profiles_helper_spec.rb'
+ - 'ee/spec/helpers/projects/security/discover_helper_spec.rb'
+ - 'ee/spec/helpers/projects_helper_spec.rb'
+ - 'ee/spec/helpers/push_rules_helper_spec.rb'
+ - 'ee/spec/helpers/routing/pseudonymization_helper_spec.rb'
+ - 'ee/spec/helpers/search_helper_spec.rb'
+ - 'ee/spec/helpers/seat_count_alert_helper_spec.rb'
+ - 'ee/spec/helpers/security_helper_spec.rb'
+ - 'ee/spec/helpers/subscriptions_helper_spec.rb'
+ - 'ee/spec/helpers/timeboxes_helper_spec.rb'
+ - 'ee/spec/helpers/trial_status_widget_helper_spec.rb'
+ - 'ee/spec/helpers/users/identity_verification_helper_spec.rb'
+ - 'ee/spec/helpers/users_helper_spec.rb'
+ - 'ee/spec/helpers/vulnerabilities_helper_spec.rb'
+ - 'ee/spec/mailers/ci_minutes_usage_mailer_spec.rb'
+ - 'ee/spec/mailers/credentials_inventory_mailer_spec.rb'
+ - 'ee/spec/mailers/devise_mailer_spec.rb'
+ - 'ee/spec/mailers/ee/emails/admin_notification_spec.rb'
+ - 'ee/spec/mailers/ee/emails/issues_spec.rb'
+ - 'ee/spec/mailers/ee/emails/merge_requests_spec.rb'
+ - 'ee/spec/mailers/ee/emails/profile_spec.rb'
+ - 'ee/spec/mailers/ee/emails/projects_spec.rb'
+ - 'ee/spec/mailers/emails/free_user_cap_spec.rb'
+ - 'ee/spec/mailers/emails/group_memberships_spec.rb'
+ - 'ee/spec/mailers/emails/in_product_marketing_spec.rb'
+ - 'ee/spec/mailers/emails/merge_commits_spec.rb'
+ - 'ee/spec/mailers/emails/namespace_storage_usage_mailer_spec.rb'
+ - 'ee/spec/mailers/emails/requirements_spec.rb'
+ - 'ee/spec/mailers/emails/user_cap_spec.rb'
+ - 'ee/spec/mailers/license_mailer_spec.rb'
+ - 'ee/spec/mailers/notify_spec.rb'
- 'ee/spec/presenters/approval_rule_presenter_spec.rb'
- 'ee/spec/presenters/audit_event_presenter_spec.rb'
- 'ee/spec/presenters/ci/build_runner_presenter_spec.rb'
@@ -10,12 +111,10 @@ RSpec/FactoryBot/AvoidCreate:
- 'ee/spec/presenters/ee/blob_presenter_spec.rb'
- 'ee/spec/presenters/ee/clusters/cluster_presenter_spec.rb'
- 'ee/spec/presenters/ee/instance_clusterable_presenter_spec.rb'
- - 'ee/spec/presenters/ee/issue_presenter_spec.rb'
- 'ee/spec/presenters/ee/projects/security/configuration_presenter_spec.rb'
- 'ee/spec/presenters/epic_issue_presenter_spec.rb'
- 'ee/spec/presenters/epic_presenter_spec.rb'
- 'ee/spec/presenters/group_clusterable_presenter_spec.rb'
- - 'ee/spec/presenters/label_presenter_spec.rb'
- 'ee/spec/presenters/merge_request_approver_presenter_spec.rb'
- 'ee/spec/presenters/merge_request_presenter_spec.rb'
- 'ee/spec/presenters/project_clusterable_presenter_spec.rb'
@@ -92,6 +191,178 @@ RSpec/FactoryBot/AvoidCreate:
- 'ee/spec/serializers/vulnerabilities/scanner_entity_spec.rb'
- 'ee/spec/serializers/vulnerability_entity_spec.rb'
- 'ee/spec/serializers/vulnerability_note_entity_spec.rb'
+ - 'ee/spec/views/admin/application_settings/_elasticsearch_form.html.haml_spec.rb'
+ - 'ee/spec/views/admin/application_settings/_git_abuse_rate_limit.html.haml_spec.rb'
+ - 'ee/spec/views/admin/application_settings/general.html.haml_spec.rb'
+ - 'ee/spec/views/admin/dashboard/index.html.haml_spec.rb'
+ - 'ee/spec/views/admin/groups/_form.html.haml_spec.rb'
+ - 'ee/spec/views/admin/identities/index.html.haml_spec.rb'
+ - 'ee/spec/views/admin/users/_credit_card_info.html.haml_spec.rb'
+ - 'ee/spec/views/admin/users/index.html.haml_spec.rb'
+ - 'ee/spec/views/admin/users/show.html.haml_spec.rb'
+ - 'ee/spec/views/clusters/clusters/show.html.haml_spec.rb'
+ - 'ee/spec/views/compliance_management/compliance_framework/_project_settings.html.haml_spec.rb'
+ - 'ee/spec/views/groups/billings/index.html.haml_spec.rb'
+ - 'ee/spec/views/groups/edit.html.haml_spec.rb'
+ - 'ee/spec/views/groups/feature_discovery_moments/advanced_features_dashboard.html.haml_spec.rb'
+ - 'ee/spec/views/groups/group_members/index.html.haml_spec.rb'
+ - 'ee/spec/views/groups/hook_logs/show.html.haml_spec.rb'
+ - 'ee/spec/views/groups/hooks/edit.html.haml_spec.rb'
+ - 'ee/spec/views/groups/security/discover/show.html.haml_spec.rb'
+ - 'ee/spec/views/groups/settings/_remove.html.haml_spec.rb'
+ - 'ee/spec/views/groups/settings/reporting/show.html.haml_spec.rb'
+ - 'ee/spec/views/layouts/_search.html.haml_spec.rb'
+ - 'ee/spec/views/layouts/application.html.haml_spec.rb'
+ - 'ee/spec/views/layouts/group.html.haml_spec.rb'
+ - 'ee/spec/views/layouts/header/_current_user_dropdown.html.haml_spec.rb'
+ - 'ee/spec/views/layouts/header/_new_dropdown.haml_spec.rb'
+ - 'ee/spec/views/layouts/header/_read_only_banner.html.haml_spec.rb'
+ - 'ee/spec/views/layouts/header/help_dropdown/_cross_stage_fdm.html.haml_spec.rb'
+ - 'ee/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb'
+ - 'ee/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb'
+ - 'ee/spec/views/layouts/project.html.haml_spec.rb'
+ - 'ee/spec/views/projects/edit.html.haml_spec.rb'
+ - 'ee/spec/views/projects/issues/show.html.haml_spec.rb'
+ - 'ee/spec/views/projects/on_demand_scans/index.html.haml_spec.rb'
+ - 'ee/spec/views/projects/pipelines/_tabs_content.html.haml_spec.rb'
+ - 'ee/spec/views/projects/project_members/index.html.haml_spec.rb'
+ - 'ee/spec/views/projects/security/corpus_management/show.html.haml_spec.rb'
+ - 'ee/spec/views/projects/security/dast_profiles/show.html.haml_spec.rb'
+ - 'ee/spec/views/projects/security/dast_scanner_profiles/edit.html.haml_spec.rb'
+ - 'ee/spec/views/projects/security/dast_scanner_profiles/new.html.haml_spec.rb'
+ - 'ee/spec/views/projects/security/dast_site_profiles/edit.html.haml_spec.rb'
+ - 'ee/spec/views/projects/security/dast_site_profiles/new.html.haml_spec.rb'
+ - 'ee/spec/views/projects/security/discover/show.html.haml_spec.rb'
+ - 'ee/spec/views/projects/security/policies/index.html.haml_spec.rb'
+ - 'ee/spec/views/projects/security/sast_configuration/show.html.haml_spec.rb'
+ - 'ee/spec/views/projects/settings/subscriptions/_index.html.haml_spec.rb'
+ - 'ee/spec/views/registrations/groups_projects/new.html.haml_spec.rb'
+ - 'ee/spec/views/registrations/welcome/continuous_onboarding_getting_started.html.haml_spec.rb'
+ - 'ee/spec/views/registrations/welcome/show.html.haml_spec.rb'
+ - 'ee/spec/views/search/_category.html.haml_spec.rb'
+ - 'ee/spec/views/shared/_clone_panel.html.haml_spec.rb'
+ - 'ee/spec/views/shared/_kerberos_clone_button.html.haml_spec.rb'
+ - 'ee/spec/views/shared/_mirror_status.html.haml_spec.rb'
+ - 'ee/spec/views/shared/_mirror_update_button.html.haml_spec.rb'
+ - 'ee/spec/views/shared/_namespace_user_cap_reached_alert.html.haml_spec.rb'
+ - 'ee/spec/views/shared/billings/_eoa_bronze_plan_banner.html.haml_spec.rb'
+ - 'ee/spec/views/shared/billings/_trial_status.html.haml_spec.rb'
+ - 'ee/spec/views/shared/credentials_inventory/_expiry_date.html.haml_spec.rb'
+ - 'ee/spec/views/shared/credentials_inventory/gpg_keys/_gpg_key.html.haml_spec.rb'
+ - 'ee/spec/views/shared/credentials_inventory/personal_access_tokens/_personal_access_token.html.haml_spec.rb'
+ - 'ee/spec/views/shared/credentials_inventory/resource_access_tokens/_resource_access_token.html.haml_spec.rb'
+ - 'ee/spec/views/shared/credentials_inventory/ssh_keys/_ssh_key.html.haml_spec.rb'
+ - 'ee/spec/views/shared/issuable/_approver_suggestion.html.haml_spec.rb'
+ - 'ee/spec/views/shared/issuable/_sidebar.html.haml_spec.rb'
+ - 'ee/spec/views/shared/labels/_create_label_help_text.html.haml_spec.rb'
+ - 'ee/spec/views/shared/milestones/_milestone.html.haml_spec.rb'
+ - 'ee/spec/views/shared/promotions/_promotion_link_project.html.haml_spec.rb'
+ - 'spec/components/diffs/overflow_warning_component_spec.rb'
+ - 'spec/components/diffs/stats_component_spec.rb'
+ - 'spec/components/pajamas/avatar_component_spec.rb'
+ - 'spec/helpers/admin/identities_helper_spec.rb'
+ - 'spec/helpers/admin/user_actions_helper_spec.rb'
+ - 'spec/helpers/analytics/cycle_analytics_helper_spec.rb'
+ - 'spec/helpers/appearances_helper_spec.rb'
+ - 'spec/helpers/application_helper_spec.rb'
+ - 'spec/helpers/application_settings_helper_spec.rb'
+ - 'spec/helpers/auth_helper_spec.rb'
+ - 'spec/helpers/auto_devops_helper_spec.rb'
+ - 'spec/helpers/avatars_helper_spec.rb'
+ - 'spec/helpers/award_emoji_helper_spec.rb'
+ - 'spec/helpers/blob_helper_spec.rb'
+ - 'spec/helpers/boards_helper_spec.rb'
+ - 'spec/helpers/branches_helper_spec.rb'
+ - 'spec/helpers/broadcast_messages_helper_spec.rb'
+ - 'spec/helpers/button_helper_spec.rb'
+ - 'spec/helpers/calendar_helper_spec.rb'
+ - 'spec/helpers/ci/builds_helper_spec.rb'
+ - 'spec/helpers/ci/jobs_helper_spec.rb'
+ - 'spec/helpers/ci/pipeline_editor_helper_spec.rb'
+ - 'spec/helpers/ci/pipelines_helper_spec.rb'
+ - 'spec/helpers/ci/runners_helper_spec.rb'
+ - 'spec/helpers/ci/secure_files_helper_spec.rb'
+ - 'spec/helpers/clusters_helper_spec.rb'
+ - 'spec/helpers/commits_helper_spec.rb'
+ - 'spec/helpers/diff_helper_spec.rb'
+ - 'spec/helpers/emails_helper_spec.rb'
+ - 'spec/helpers/environment_helper_spec.rb'
+ - 'spec/helpers/environments_helper_spec.rb'
+ - 'spec/helpers/events_helper_spec.rb'
+ - 'spec/helpers/feature_flags_helper_spec.rb'
+ - 'spec/helpers/gitlab_routing_helper_spec.rb'
+ - 'spec/helpers/graph_helper_spec.rb'
+ - 'spec/helpers/groups/group_members_helper_spec.rb'
+ - 'spec/helpers/groups/settings_helper_spec.rb'
+ - 'spec/helpers/groups_helper_spec.rb'
+ - 'spec/helpers/ide_helper_spec.rb'
+ - 'spec/helpers/import_helper_spec.rb'
+ - 'spec/helpers/integrations_helper_spec.rb'
+ - 'spec/helpers/invite_members_helper_spec.rb'
+ - 'spec/helpers/issuables_description_templates_helper_spec.rb'
+ - 'spec/helpers/issuables_helper_spec.rb'
+ - 'spec/helpers/issues_helper_spec.rb'
+ - 'spec/helpers/jira_connect_helper_spec.rb'
+ - 'spec/helpers/keyset_helper_spec.rb'
+ - 'spec/helpers/labels_helper_spec.rb'
+ - 'spec/helpers/lazy_image_tag_helper_spec.rb'
+ - 'spec/helpers/learn_gitlab_helper_spec.rb'
+ - 'spec/helpers/markup_helper_spec.rb'
+ - 'spec/helpers/members_helper_spec.rb'
+ - 'spec/helpers/merge_requests_helper_spec.rb'
+ - 'spec/helpers/namespaces_helper_spec.rb'
+ - 'spec/helpers/nav/top_nav_helper_spec.rb'
+ - 'spec/helpers/nav_helper_spec.rb'
+ - 'spec/helpers/notes_helper_spec.rb'
+ - 'spec/helpers/notifications_helper_spec.rb'
+ - 'spec/helpers/notify_helper_spec.rb'
+ - 'spec/helpers/operations_helper_spec.rb'
+ - 'spec/helpers/packages_helper_spec.rb'
+ - 'spec/helpers/profiles_helper_spec.rb'
+ - 'spec/helpers/projects/alert_management_helper_spec.rb'
+ - 'spec/helpers/projects/cluster_agents_helper_spec.rb'
+ - 'spec/helpers/projects/ml/experiments_helper_spec.rb'
+ - 'spec/helpers/projects/pages_helper_spec.rb'
+ - 'spec/helpers/projects/pipeline_helper_spec.rb'
+ - 'spec/helpers/projects/project_members_helper_spec.rb'
+ - 'spec/helpers/projects/security/configuration_helper_spec.rb'
+ - 'spec/helpers/projects/terraform_helper_spec.rb'
+ - 'spec/helpers/projects_helper_spec.rb'
+ - 'spec/helpers/releases_helper_spec.rb'
+ - 'spec/helpers/routing/pseudonymization_helper_spec.rb'
+ - 'spec/helpers/rss_helper_spec.rb'
+ - 'spec/helpers/search_helper_spec.rb'
+ - 'spec/helpers/snippets_helper_spec.rb'
+ - 'spec/helpers/storage_helper_spec.rb'
+ - 'spec/helpers/submodule_helper_spec.rb'
+ - 'spec/helpers/timeboxes_helper_spec.rb'
+ - 'spec/helpers/todos_helper_spec.rb'
+ - 'spec/helpers/tree_helper_spec.rb'
+ - 'spec/helpers/users/callouts_helper_spec.rb'
+ - 'spec/helpers/users/group_callouts_helper_spec.rb'
+ - 'spec/helpers/users_helper_spec.rb'
+ - 'spec/helpers/version_check_helper_spec.rb'
+ - 'spec/helpers/visibility_level_helper_spec.rb'
+ - 'spec/helpers/web_hooks/web_hooks_helper_spec.rb'
+ - 'spec/helpers/whats_new_helper_spec.rb'
+ - 'spec/helpers/wiki_helper_spec.rb'
+ - 'spec/helpers/wiki_page_version_helper_spec.rb'
+ - 'spec/mailers/abuse_report_mailer_spec.rb'
+ - 'spec/mailers/devise_mailer_spec.rb'
+ - 'spec/mailers/emails/auto_devops_spec.rb'
+ - 'spec/mailers/emails/groups_spec.rb'
+ - 'spec/mailers/emails/in_product_marketing_spec.rb'
+ - 'spec/mailers/emails/issues_spec.rb'
+ - 'spec/mailers/emails/merge_requests_spec.rb'
+ - 'spec/mailers/emails/pages_domains_spec.rb'
+ - 'spec/mailers/emails/pipelines_spec.rb'
+ - 'spec/mailers/emails/profile_spec.rb'
+ - 'spec/mailers/emails/projects_spec.rb'
+ - 'spec/mailers/emails/releases_spec.rb'
+ - 'spec/mailers/emails/service_desk_spec.rb'
+ - 'spec/mailers/notify_spec.rb'
+ - 'spec/mailers/previews_spec.rb'
+ - 'spec/mailers/repository_check_mailer_spec.rb'
- 'spec/presenters/alert_management/alert_presenter_spec.rb'
- 'spec/presenters/blob_presenter_spec.rb'
- 'spec/presenters/blobs/notebook_presenter_spec.rb'
@@ -219,7 +490,6 @@ RSpec/FactoryBot/AvoidCreate:
- 'spec/serializers/impersonation_access_token_serializer_spec.rb'
- 'spec/serializers/import/manifest_provider_repo_entity_spec.rb'
- 'spec/serializers/integrations/event_entity_spec.rb'
- - 'spec/serializers/integrations/field_entity_spec.rb'
- 'spec/serializers/integrations/harbor_serializers/artifact_entity_spec.rb'
- 'spec/serializers/integrations/harbor_serializers/repository_entity_spec.rb'
- 'spec/serializers/integrations/harbor_serializers/tag_entity_spec.rb'
@@ -265,8 +535,6 @@ RSpec/FactoryBot/AvoidCreate:
- 'spec/serializers/project_mirror_entity_spec.rb'
- 'spec/serializers/project_note_entity_spec.rb'
- 'spec/serializers/project_serializer_spec.rb'
- - 'spec/serializers/prometheus_alert_entity_spec.rb'
- - 'spec/serializers/release_serializer_spec.rb'
- 'spec/serializers/review_app_setup_entity_spec.rb'
- 'spec/serializers/runner_entity_spec.rb'
- 'spec/serializers/serverless/domain_entity_spec.rb'
@@ -283,3 +551,104 @@ RSpec/FactoryBot/AvoidCreate:
- 'spec/serializers/user_serializer_spec.rb'
- 'spec/serializers/web_ide_terminal_entity_spec.rb'
- 'spec/serializers/web_ide_terminal_serializer_spec.rb'
+ - 'spec/views/admin/application_settings/_ci_cd.html.haml_spec.rb'
+ - 'spec/views/admin/application_settings/_eks.html.haml_spec.rb'
+ - 'spec/views/admin/application_settings/_jira_connect.html.haml_spec.rb'
+ - 'spec/views/admin/application_settings/_package_registry.html.haml_spec.rb'
+ - 'spec/views/admin/application_settings/_repository_check.html.haml_spec.rb'
+ - 'spec/views/admin/application_settings/ci_cd.html.haml_spec.rb'
+ - 'spec/views/admin/application_settings/general.html.haml_spec.rb'
+ - 'spec/views/admin/application_settings/repository.html.haml_spec.rb'
+ - 'spec/views/admin/broadcast_messages/index.html.haml_spec.rb'
+ - 'spec/views/admin/dashboard/index.html.haml_spec.rb'
+ - 'spec/views/admin/identities/index.html.haml_spec.rb'
+ - 'spec/views/admin/sessions/new.html.haml_spec.rb'
+ - 'spec/views/admin/sessions/two_factor.html.haml_spec.rb'
+ - 'spec/views/ci/status/_badge.html.haml_spec.rb'
+ - 'spec/views/ci/status/_icon.html.haml_spec.rb'
+ - 'spec/views/dashboard/projects/_blank_state_admin_welcome.haml_spec.rb'
+ - 'spec/views/dashboard/projects/_blank_state_welcome.html.haml_spec.rb'
+ - 'spec/views/events/event/_common.html.haml_spec.rb'
+ - 'spec/views/groups/_home_panel.html.haml_spec.rb'
+ - 'spec/views/groups/edit.html.haml_spec.rb'
+ - 'spec/views/groups/group_members/index.html.haml_spec.rb'
+ - 'spec/views/groups/new.html.haml_spec.rb'
+ - 'spec/views/help/instance_configuration.html.haml_spec.rb'
+ - 'spec/views/layouts/_search.html.haml_spec.rb'
+ - 'spec/views/layouts/application.html.haml_spec.rb'
+ - 'spec/views/layouts/devise.html.haml_spec.rb'
+ - 'spec/views/layouts/fullscreen.html.haml_spec.rb'
+ - 'spec/views/layouts/header/_new_dropdown.haml_spec.rb'
+ - 'spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb'
+ - 'spec/views/layouts/nav/sidebar/_profile.html.haml_spec.rb'
+ - 'spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb'
+ - 'spec/views/layouts/profile.html.haml_spec.rb'
+ - 'spec/views/layouts/terms.html.haml_spec.rb'
+ - 'spec/views/notify/approved_merge_request_email.html.haml_spec.rb'
+ - 'spec/views/notify/autodevops_disabled_email.text.erb_spec.rb'
+ - 'spec/views/notify/change_in_merge_request_draft_status_email.html.haml_spec.rb'
+ - 'spec/views/notify/change_in_merge_request_draft_status_email.text.erb_spec.rb'
+ - 'spec/views/notify/changed_milestone_email.html.haml_spec.rb'
+ - 'spec/views/notify/import_issues_csv_email.html.haml_spec.rb'
+ - 'spec/views/notify/pipeline_failed_email.text.erb_spec.rb'
+ - 'spec/views/notify/push_to_merge_request_email.text.haml_spec.rb'
+ - 'spec/views/profiles/audit_log.html.haml_spec.rb'
+ - 'spec/views/profiles/keys/_key.html.haml_spec.rb'
+ - 'spec/views/profiles/keys/_key_details.html.haml_spec.rb'
+ - 'spec/views/profiles/notifications/show.html.haml_spec.rb'
+ - 'spec/views/profiles/show.html.haml_spec.rb'
+ - 'spec/views/projects/_files.html.haml_spec.rb'
+ - 'spec/views/projects/_flash_messages.html.haml_spec.rb'
+ - 'spec/views/projects/_home_panel.html.haml_spec.rb'
+ - 'spec/views/projects/branches/index.html.haml_spec.rb'
+ - 'spec/views/projects/commit/_commit_box.html.haml_spec.rb'
+ - 'spec/views/projects/commit/branches.html.haml_spec.rb'
+ - 'spec/views/projects/commit/show.html.haml_spec.rb'
+ - 'spec/views/projects/commits/_commit.html.haml_spec.rb'
+ - 'spec/views/projects/commits/show.html.haml_spec.rb'
+ - 'spec/views/projects/diffs/_viewer.html.haml_spec.rb'
+ - 'spec/views/projects/edit.html.haml_spec.rb'
+ - 'spec/views/projects/empty.html.haml_spec.rb'
+ - 'spec/views/projects/environments/terminal.html.haml_spec.rb'
+ - 'spec/views/projects/hooks/edit.html.haml_spec.rb'
+ - 'spec/views/projects/hooks/index.html.haml_spec.rb'
+ - 'spec/views/projects/imports/new.html.haml_spec.rb'
+ - 'spec/views/projects/issues/_issue.html.haml_spec.rb'
+ - 'spec/views/projects/issues/_service_desk_info_content.html.haml_spec.rb'
+ - 'spec/views/projects/issues/show.html.haml_spec.rb'
+ - 'spec/views/projects/jobs/_build.html.haml_spec.rb'
+ - 'spec/views/projects/jobs/_generic_commit_status.html.haml_spec.rb'
+ - 'spec/views/projects/jobs/show.html.haml_spec.rb'
+ - 'spec/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml_spec.rb'
+ - 'spec/views/projects/merge_requests/_commits.html.haml_spec.rb'
+ - 'spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb'
+ - 'spec/views/projects/merge_requests/edit.html.haml_spec.rb'
+ - 'spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb'
+ - 'spec/views/projects/pages/new.html.haml_spec.rb'
+ - 'spec/views/projects/pages/show.html.haml_spec.rb'
+ - 'spec/views/projects/pages_domains/show.html.haml_spec.rb'
+ - 'spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb'
+ - 'spec/views/projects/pipelines/show.html.haml_spec.rb'
+ - 'spec/views/projects/project_members/index.html.haml_spec.rb'
+ - 'spec/views/projects/runners/_specific_runners.html.haml_spec.rb'
+ - 'spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb'
+ - 'spec/views/projects/settings/integrations/edit.html.haml_spec.rb'
+ - 'spec/views/projects/settings/merge_requests/show.html.haml_spec.rb'
+ - 'spec/views/projects/settings/operations/show.html.haml_spec.rb'
+ - 'spec/views/projects/tags/index.html.haml_spec.rb'
+ - 'spec/views/projects/tree/show.html.haml_spec.rb'
+ - 'spec/views/registrations/welcome/show.html.haml_spec.rb'
+ - 'spec/views/search/_results.html.haml_spec.rb'
+ - 'spec/views/shared/_label_row.html.haml_spec.rb'
+ - 'spec/views/shared/issuable/_sidebar.html.haml_spec.rb'
+ - 'spec/views/shared/milestones/_issuable.html.haml_spec.rb'
+ - 'spec/views/shared/milestones/_top.html.haml_spec.rb'
+ - 'spec/views/shared/nav/_sidebar.html.haml_spec.rb'
+ - 'spec/views/shared/notes/_form.html.haml_spec.rb'
+ - 'spec/views/shared/projects/_inactive_project_deletion_alert.html.haml_spec.rb'
+ - 'spec/views/shared/projects/_list.html.haml_spec.rb'
+ - 'spec/views/shared/projects/_project.html.haml_spec.rb'
+ - 'spec/views/shared/runners/_runner_details.html.haml_spec.rb'
+ - 'spec/views/shared/snippets/_snippet.html.haml_spec.rb'
+ - 'spec/views/shared/web_hooks/_web_hook_disabled_alert.html.haml_spec.rb'
+ - 'spec/views/shared/wikis/_sidebar.html.haml_spec.rb'
diff --git a/.rubocop_todo/rspec/file_path.yml b/.rubocop_todo/rspec/file_path.yml
index 8930b709bfd..9cc2e1b5b6c 100644
--- a/.rubocop_todo/rspec/file_path.yml
+++ b/.rubocop_todo/rspec/file_path.yml
@@ -18,7 +18,6 @@ RSpec/FilePath:
- 'ee/spec/services/ci/create_pipeline_service/compliance_spec.rb'
- 'ee/spec/services/ci/create_pipeline_service/cross_needs_artifacts_spec.rb'
- 'ee/spec/services/ci/create_pipeline_service/dast_configuration_spec.rb'
- - 'ee/spec/services/ci/create_pipeline_service/limit_active_jobs_spec.rb'
- 'ee/spec/services/ci/create_pipeline_service/needs_spec.rb'
- 'ee/spec/services/ci/create_pipeline_service/runnable_builds_spec.rb'
- 'spec/benchmarks/banzai_benchmark.rb'
@@ -53,7 +52,6 @@ RSpec/FilePath:
- 'spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb'
- 'spec/services/ci/create_pipeline_service/include_spec.rb'
- 'spec/services/ci/create_pipeline_service/limit_active_jobs_spec.rb'
- - 'spec/services/ci/create_pipeline_service/logger_spec.rb'
- 'spec/services/ci/create_pipeline_service/merge_requests_spec.rb'
- 'spec/services/ci/create_pipeline_service/needs_spec.rb'
- 'spec/services/ci/create_pipeline_service/parallel_spec.rb'
@@ -63,5 +61,6 @@ RSpec/FilePath:
- 'spec/services/ci/create_pipeline_service/pre_post_stages_spec.rb'
- 'spec/services/ci/create_pipeline_service/rate_limit_spec.rb'
- 'spec/services/ci/create_pipeline_service/rules_spec.rb'
+ - 'spec/services/ci/create_pipeline_service/scripts_spec.rb'
- 'spec/services/ci/create_pipeline_service/tags_spec.rb'
- 'spec/services/ci/create_pipeline_service/variables_spec.rb'
diff --git a/.rubocop_todo/rspec/hooks_before_examples.yml b/.rubocop_todo/rspec/hooks_before_examples.yml
index 0de9ff4b6c0..333687e1cc4 100644
--- a/.rubocop_todo/rspec/hooks_before_examples.yml
+++ b/.rubocop_todo/rspec/hooks_before_examples.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
RSpec/HooksBeforeExamples:
Exclude:
- 'ee/spec/features/boards/swimlanes/epics_swimlanes_spec.rb'
@@ -30,47 +30,17 @@ RSpec/HooksBeforeExamples:
- 'qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb'
- 'qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb'
- 'qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb'
- - 'qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb'
- 'qa/qa/specs/features/browser_ui/1_manage/project/project_access_token_spec.rb'
- 'qa/qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb'
- - 'qa/qa/specs/features/browser_ui/1_manage/user/user_access_termination_spec.rb'
- 'qa/qa/specs/features/browser_ui/1_manage/user/user_inherited_access_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb'
- 'qa/qa/specs/features/ee/api/1_manage/user/minimal_access_user_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/13_secure/security_reports_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_saml_enforced_sso_git_access_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_saml_enforced_sso_new_account_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_saml_non_enforced_sso_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/group/prevent_forking_outside_group_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/instance/instance_audit_logs_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/ldap/admin_ldap_sync_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/user/minimal_access_user_spec.rb'
- 'qa/spec/resource/reusable_collection_spec.rb'
- 'qa/spec/specs/runner_spec.rb'
- - 'spec/controllers/import/github_controller_spec.rb'
- - 'spec/features/projects/pages/user_configures_pages_pipeline_spec.rb'
- - 'spec/graphql/resolvers/design_management/design_resolver_spec.rb'
- - 'spec/graphql/resolvers/design_management/designs_resolver_spec.rb'
- - 'spec/graphql/resolvers/project_pipeline_resolver_spec.rb'
- - 'spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb'
- - 'spec/lib/banzai/filter/references/user_reference_filter_spec.rb'
- - 'spec/lib/feature/definition_spec.rb'
- - 'spec/lib/gitlab/auth/saml/user_spec.rb'
- - 'spec/lib/gitlab/email/handler/create_issue_handler_spec.rb'
- - 'spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb'
- - 'spec/lib/gitlab/email/handler/create_note_handler_spec.rb'
- - 'spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb'
- - 'spec/lib/gitlab/tracking/event_definition_spec.rb'
- - 'spec/lib/gitlab/usage/metric_definition_spec.rb'
- - 'spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb'
- - 'spec/lib/mattermost/session_spec.rb'
- - 'spec/models/ci/build_trace_chunk_spec.rb'
- - 'spec/models/clusters/applications/ingress_spec.rb'
- - 'spec/models/clusters/applications/knative_spec.rb'
- - 'spec/models/integrations/chat_message/pipeline_message_spec.rb'
- - 'spec/models/repository_spec.rb'
- - 'spec/serializers/pipeline_details_entity_spec.rb'
- - 'spec/services/feature_flags/hook_service_spec.rb'
- - 'spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb'
- - 'spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb'
diff --git a/.rubocop_todo/rspec/instance_variable.yml b/.rubocop_todo/rspec/instance_variable.yml
index 2cc146a0625..b1b4a6c16ce 100644
--- a/.rubocop_todo/rspec/instance_variable.yml
+++ b/.rubocop_todo/rspec/instance_variable.yml
@@ -1,198 +1,172 @@
---
RSpec/InstanceVariable:
Exclude:
- - ee/spec/controllers/admin/application_settings_controller_spec.rb
- - ee/spec/controllers/admin/geo/settings_controller_spec.rb
- - ee/spec/controllers/ee/sessions_controller_spec.rb
- - ee/spec/controllers/groups/groups_controller_spec.rb
- - ee/spec/controllers/groups/omniauth_callbacks_controller_spec.rb
- - ee/spec/controllers/passwords_controller_spec.rb
- - ee/spec/features/markdown/markdown_spec.rb
- - ee/spec/frontend/fixtures/dast_profiles.rb
- - ee/spec/frontend/fixtures/epic.rb
- - ee/spec/graphql/types/vulnerability_request_response_header_type_spec.rb
- - ee/spec/graphql/types/vulnerability_request_type_spec.rb
- - ee/spec/graphql/types/vulnerability_response_type_spec.rb
- - ee/spec/helpers/ee/issuables_helper_spec.rb
- - ee/spec/helpers/ee/wiki_helper_spec.rb
- - ee/spec/helpers/notes_helper_spec.rb
- - ee/spec/helpers/search_helper_spec.rb
- - ee/spec/lib/ee/gitlab/elastic/helper_spec.rb
- - ee/spec/lib/gitlab/elastic/search_results_spec.rb
- - ee/spec/lib/gitlab/reference_extractor_spec.rb
- - ee/spec/services/ee/merge_requests/update_service_spec.rb
- - ee/spec/services/ee/notification_service_spec.rb
- - ee/spec/services/ee/users/create_service_spec.rb
- - ee/spec/services/ee/users/destroy_service_spec.rb
- - ee/spec/services/geo/metrics_update_service_spec.rb
- - ee/spec/services/groups/create_service_spec.rb
- - ee/spec/services/groups/participants_service_spec.rb
- - ee/spec/services/projects/create_from_template_service_spec.rb
- - ee/spec/services/projects/create_service_spec.rb
- - ee/spec/support/shared_examples/views/subscription_shared_examples.rb
- - ee/spec/tasks/geo_rake_spec.rb
- - ee/spec/views/projects/security/corpus_management/show.html.haml_spec.rb
- - ee/spec/views/projects/security/dast_profiles/show.html.haml_spec.rb
- - ee/spec/views/projects/security/dast_scanner_profiles/new.html.haml_spec.rb
- - ee/spec/views/projects/security/dast_site_profiles/new.html.haml_spec.rb
- - qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb
- - qa/qa/specs/features/api/1_manage/project_access_token_spec.rb
- - qa/qa/specs/features/api/1_manage/user_access_termination_spec.rb
- - qa/qa/specs/features/browser_ui/1_manage/login/2fa_recovery_spec.rb
- - qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb
- - qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
- - qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb
- - qa/qa/specs/features/browser_ui/1_manage/project/invite_group_to_project_spec.rb
- - qa/qa/specs/features/browser_ui/1_manage/project/protected_tags_spec.rb
- - qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
- - qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb
- - qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb
- - qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb
- - qa/qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb
- - qa/qa/specs/features/browser_ui/3_create/web_ide/web_terminal_spec.rb
- - qa/qa/specs/features/ee/api/1_manage/user/minimal_access_user_spec.rb
- - qa/qa/specs/features/ee/browser_ui/12_geo/attachment_replication_spec.rb
- - qa/qa/specs/features/ee/browser_ui/12_geo/geo_replication_ci_job_log_artifacts_spec.rb
- - qa/qa/specs/features/ee/browser_ui/12_geo/rename_replication_spec.rb
- - qa/qa/specs/features/ee/browser_ui/13_secure/create_merge_request_with_secure_spec.rb
- - qa/qa/specs/features/ee/browser_ui/13_secure/license_compliance_spec.rb
- - qa/qa/specs/features/ee/browser_ui/13_secure/merge_request_license_widget_spec.rb
- - qa/qa/specs/features/ee/browser_ui/13_secure/project_security_dashboard_spec.rb
- - qa/qa/specs/features/ee/browser_ui/13_secure/security_reports_spec.rb
- - qa/qa/specs/features/ee/browser_ui/13_secure/vulnerability_management_spec.rb
- - qa/qa/specs/features/ee/browser_ui/1_manage/group/group_audit_logs_1_spec.rb
- - qa/qa/specs/features/ee/browser_ui/1_manage/group/group_file_template_spec.rb
- - qa/qa/specs/features/ee/browser_ui/1_manage/group/group_ldap_sync_spec.rb
- - qa/qa/specs/features/ee/browser_ui/1_manage/group/group_saml_non_enforced_sso_spec.rb
- - qa/qa/specs/features/ee/browser_ui/1_manage/group/restrict_by_ip_address_spec.rb
- - qa/qa/specs/features/ee/browser_ui/1_manage/project/project_templates_spec.rb
- - qa/qa/specs/features/ee/browser_ui/2_plan/integrations/jira_issues_list_spec.rb
- - qa/qa/specs/features/ee/browser_ui/2_plan/issue_boards/project_issue_boards_spec.rb
- - qa/qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/more_than_four_assignees_spec.rb
- - qa/qa/specs/features/ee/browser_ui/3_create/repository/code_owners_spec.rb
- - qa/qa/specs/features/ee/browser_ui/3_create/repository/push_rules_spec.rb
- - qa/spec/support/repeater_spec.rb
- - spec/commands/metrics_server/metrics_server_spec.rb
- - spec/controllers/admin/clusters_controller_spec.rb
- - spec/controllers/admin/topics/avatars_controller_spec.rb
- - spec/controllers/concerns/renders_commits_spec.rb
- - spec/controllers/confirmations_controller_spec.rb
- - spec/controllers/groups/avatars_controller_spec.rb
- - spec/controllers/groups/clusters_controller_spec.rb
- - spec/controllers/import/bitbucket_controller_spec.rb
- - spec/controllers/import/bitbucket_server_controller_spec.rb
- - spec/controllers/metrics_controller_spec.rb
- - spec/controllers/omniauth_callbacks_controller_spec.rb
- - spec/controllers/passwords_controller_spec.rb
- - spec/controllers/profiles/avatars_controller_spec.rb
- - spec/controllers/projects/clusters_controller_spec.rb
- - spec/controllers/sessions_controller_spec.rb
- - spec/features/calendar_spec.rb
- - spec/features/issues/user_filters_issues_spec.rb
- - spec/features/markdown/copy_as_gfm_spec.rb
- - spec/features/markdown/gitlab_flavored_markdown_spec.rb
- - spec/features/markdown/keyboard_shortcuts_spec.rb
- - spec/features/markdown/markdown_spec.rb
- - spec/features/merge_request/batch_comments_spec.rb
- - spec/features/merge_request/user_sees_pipelines_spec.rb
- - spec/features/merge_requests/user_lists_merge_requests_spec.rb
- - spec/features/projects/diffs/diff_show_spec.rb
- - spec/features/triggers_spec.rb
- - spec/features/u2f_spec.rb
- - spec/finders/admin/plans_finder_spec.rb
- - spec/finders/groups_finder_spec.rb
- - spec/finders/issues_finder_spec.rb
- - spec/frontend/fixtures/listbox.rb
- - spec/frontend/fixtures/raw.rb
- - spec/frontend/fixtures/sessions.rb
- - spec/frontend/fixtures/tabs.rb
- - spec/frontend/fixtures/timezones.rb
- - spec/frontend/fixtures/u2f.rb
- - spec/frontend/fixtures/webauthn.rb
- - spec/helpers/application_helper_spec.rb
- - spec/helpers/award_emoji_helper_spec.rb
- - spec/helpers/issuables_description_templates_helper_spec.rb
- - spec/helpers/issuables_helper_spec.rb
- - spec/helpers/notes_helper_spec.rb
- - spec/helpers/projects_helper_spec.rb
- - spec/helpers/search_helper_spec.rb
- - spec/helpers/tree_helper_spec.rb
- - spec/helpers/wiki_helper_spec.rb
- - spec/lib/api/helpers/authentication_spec.rb
- - spec/lib/banzai/filter/asset_proxy_filter_spec.rb
- - spec/lib/extracts_path_spec.rb
- - spec/lib/extracts_ref_spec.rb
- - spec/lib/gitlab/auth/auth_finders_spec.rb
- - spec/lib/gitlab/auth/ldap/person_spec.rb
- - spec/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid_spec.rb
- - spec/lib/gitlab/bitbucket_import/importer_spec.rb
- - spec/lib/gitlab/chat_name_token_spec.rb
- - spec/lib/gitlab/ci/lint_spec.rb
- - spec/lib/gitlab/ci/status/composite_spec.rb
- - spec/lib/gitlab/contributions_calendar_spec.rb
- - spec/lib/gitlab/diff/parser_spec.rb
- - spec/lib/gitlab/email/hook/smime_signature_interceptor_spec.rb
- - spec/lib/gitlab/git/commit_spec.rb
- - spec/lib/gitlab/git/diff_collection_spec.rb
- - spec/lib/gitlab/git/diff_spec.rb
- - spec/lib/gitlab/git/repository_spec.rb
- - spec/lib/gitlab/http_spec.rb
- - spec/lib/gitlab/import_export/group/legacy_tree_restorer_spec.rb
- - spec/lib/gitlab/import_export/group/tree_restorer_spec.rb
- - spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
- - spec/lib/gitlab/patch/prependable_spec.rb
- - spec/lib/gitlab/popen_spec.rb
- - spec/lib/gitlab/project_transfer_spec.rb
- - spec/lib/gitlab/reference_extractor_spec.rb
- - spec/lib/gitlab/tcp_checker_spec.rb
- - spec/lib/gitlab/user_access_spec.rb
- - spec/lib/gitlab/version_info_spec.rb
- - spec/lib/gitlab/x509/certificate_spec.rb
- - spec/mailers/emails/issues_spec.rb
- - spec/migrations/20220106163326_add_has_issues_on_vulnerability_reads_trigger_spec.rb
- - spec/migrations/rename_services_to_integrations_spec.rb
- - spec/migrations/replace_external_wiki_triggers_spec.rb
- - spec/models/group_spec.rb
- - spec/models/integrations/assembla_spec.rb
- - spec/models/integrations/campfire_spec.rb
- - spec/models/member_spec.rb
- - spec/models/members/project_member_spec.rb
- - spec/models/namespace_spec.rb
- - spec/models/note_spec.rb
- - spec/models/postgresql/replication_slot_spec.rb
- - spec/models/project_spec.rb
- - spec/models/user_spec.rb
- - spec/models/users/in_product_marketing_email_spec.rb
- - spec/rack_servers/puma_spec.rb
- - spec/requests/api/admin/plan_limits_spec.rb
- - spec/requests/api/merge_requests_spec.rb
- - spec/requests/api/users_spec.rb
- - spec/requests/git_http_spec.rb
- - spec/requests/openid_connect_spec.rb
- - spec/requests/projects/issues/discussions_spec.rb
- - spec/rubocop/cop/migration/update_column_in_batches_spec.rb
- - spec/services/ci/create_pipeline_service/logger_spec.rb
- - spec/services/ci/process_sync_events_service_spec.rb
- - spec/services/labels/update_service_spec.rb
- - spec/services/members/destroy_service_spec.rb
- - spec/services/merge_requests/close_service_spec.rb
- - spec/services/merge_requests/refresh_service_spec.rb
- - spec/services/merge_requests/reopen_service_spec.rb
- - spec/services/merge_requests/update_service_spec.rb
- - spec/services/milestones/create_service_spec.rb
- - spec/services/notes/post_process_service_spec.rb
- - spec/services/notes/update_service_spec.rb
- - spec/services/notification_service_spec.rb
- - spec/services/pages/zip_directory_service_spec.rb
- - spec/services/projects/create_from_template_service_spec.rb
- - spec/services/projects/download_service_spec.rb
- - spec/services/projects/fork_service_spec.rb
- - spec/services/upload_service_spec.rb
- - spec/support/shared_contexts/controllers/ldap_omniauth_callbacks_controller_shared_context.rb
- - spec/support/shared_contexts/email_shared_context.rb
- - spec/support/shared_examples/features/wiki/user_views_wiki_empty_shared_examples.rb
- - spec/support/shared_examples/path_extraction_shared_examples.rb
- - spec/support/shared_examples/requests/api/notes_shared_examples.rb
- - spec/support_specs/helpers/stub_feature_flags_spec.rb
- - spec/views/search/_results.html.haml_spec.rb
- - spec/workers/emails_on_push_worker_spec.rb
+ - 'ee/spec/controllers/admin/application_settings_controller_spec.rb'
+ - 'ee/spec/controllers/admin/geo/settings_controller_spec.rb'
+ - 'ee/spec/controllers/ee/sessions_controller_spec.rb'
+ - 'ee/spec/controllers/groups/groups_controller_spec.rb'
+ - 'ee/spec/controllers/groups/omniauth_callbacks_controller_spec.rb'
+ - 'ee/spec/controllers/passwords_controller_spec.rb'
+ - 'ee/spec/features/markdown/markdown_spec.rb'
+ - 'ee/spec/frontend/fixtures/dast_profiles.rb'
+ - 'ee/spec/frontend/fixtures/epic.rb'
+ - 'ee/spec/graphql/types/vulnerability_request_response_header_type_spec.rb'
+ - 'ee/spec/graphql/types/vulnerability_request_type_spec.rb'
+ - 'ee/spec/graphql/types/vulnerability_response_type_spec.rb'
+ - 'ee/spec/helpers/ee/issuables_helper_spec.rb'
+ - 'ee/spec/helpers/ee/wiki_helper_spec.rb'
+ - 'ee/spec/helpers/notes_helper_spec.rb'
+ - 'ee/spec/helpers/search_helper_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/elastic/helper_spec.rb'
+ - 'ee/spec/lib/gitlab/elastic/search_results_spec.rb'
+ - 'ee/spec/lib/gitlab/reference_extractor_spec.rb'
+ - 'ee/spec/services/ee/merge_requests/update_service_spec.rb'
+ - 'ee/spec/services/ee/notification_service_spec.rb'
+ - 'ee/spec/services/ee/users/create_service_spec.rb'
+ - 'ee/spec/services/geo/metrics_update_service_spec.rb'
+ - 'ee/spec/services/groups/create_service_spec.rb'
+ - 'ee/spec/services/groups/participants_service_spec.rb'
+ - 'ee/spec/services/projects/create_from_template_service_spec.rb'
+ - 'ee/spec/services/projects/create_service_spec.rb'
+ - 'ee/spec/support/shared_examples/views/subscription_shared_examples.rb'
+ - 'ee/spec/tasks/geo_rake_spec.rb'
+ - 'ee/spec/views/projects/security/corpus_management/show.html.haml_spec.rb'
+ - 'ee/spec/views/projects/security/dast_profiles/show.html.haml_spec.rb'
+ - 'ee/spec/views/projects/security/dast_scanner_profiles/new.html.haml_spec.rb'
+ - 'ee/spec/views/projects/security/dast_site_profiles/new.html.haml_spec.rb'
+ - 'qa/qa/specs/features/api/1_manage/project_access_token_spec.rb'
+ - 'qa/qa/specs/features/api/1_manage/user_access_termination_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/3_create/web_ide/web_terminal_spec.rb'
+ - 'qa/qa/specs/features/ee/api/1_manage/user/minimal_access_user_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/13_secure/license_compliance_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/13_secure/merge_request_license_widget_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_ldap_sync_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_saml_non_enforced_sso_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/1_manage/group/restrict_by_ip_address_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/2_plan/issue_boards/project_issue_boards_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/more_than_four_assignees_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/3_create/repository/code_owners_spec.rb'
+ - 'qa/qa/specs/features/ee/browser_ui/3_create/repository/push_rules_spec.rb'
+ - 'spec/commands/metrics_server/metrics_server_spec.rb'
+ - 'spec/controllers/admin/topics/avatars_controller_spec.rb'
+ - 'spec/controllers/concerns/renders_commits_spec.rb'
+ - 'spec/controllers/confirmations_controller_spec.rb'
+ - 'spec/controllers/groups/avatars_controller_spec.rb'
+ - 'spec/controllers/import/bitbucket_controller_spec.rb'
+ - 'spec/controllers/import/bitbucket_server_controller_spec.rb'
+ - 'spec/controllers/metrics_controller_spec.rb'
+ - 'spec/controllers/omniauth_callbacks_controller_spec.rb'
+ - 'spec/controllers/passwords_controller_spec.rb'
+ - 'spec/controllers/profiles/avatars_controller_spec.rb'
+ - 'spec/controllers/sessions_controller_spec.rb'
+ - 'spec/features/calendar_spec.rb'
+ - 'spec/features/issues/user_filters_issues_spec.rb'
+ - 'spec/features/markdown/copy_as_gfm_spec.rb'
+ - 'spec/features/markdown/gitlab_flavored_markdown_spec.rb'
+ - 'spec/features/markdown/keyboard_shortcuts_spec.rb'
+ - 'spec/features/markdown/markdown_spec.rb'
+ - 'spec/features/merge_request/batch_comments_spec.rb'
+ - 'spec/features/merge_request/user_sees_pipelines_spec.rb'
+ - 'spec/features/merge_requests/user_lists_merge_requests_spec.rb'
+ - 'spec/features/projects/diffs/diff_show_spec.rb'
+ - 'spec/features/triggers_spec.rb'
+ - 'spec/features/u2f_spec.rb'
+ - 'spec/finders/admin/plans_finder_spec.rb'
+ - 'spec/finders/groups_finder_spec.rb'
+ - 'spec/frontend/fixtures/listbox.rb'
+ - 'spec/frontend/fixtures/raw.rb'
+ - 'spec/frontend/fixtures/sessions.rb'
+ - 'spec/frontend/fixtures/tabs.rb'
+ - 'spec/frontend/fixtures/timezones.rb'
+ - 'spec/frontend/fixtures/u2f.rb'
+ - 'spec/frontend/fixtures/webauthn.rb'
+ - 'spec/helpers/application_helper_spec.rb'
+ - 'spec/helpers/award_emoji_helper_spec.rb'
+ - 'spec/helpers/issuables_description_templates_helper_spec.rb'
+ - 'spec/helpers/issuables_helper_spec.rb'
+ - 'spec/helpers/notes_helper_spec.rb'
+ - 'spec/helpers/projects_helper_spec.rb'
+ - 'spec/helpers/search_helper_spec.rb'
+ - 'spec/helpers/tree_helper_spec.rb'
+ - 'spec/helpers/wiki_helper_spec.rb'
+ - 'spec/lib/api/helpers/authentication_spec.rb'
+ - 'spec/lib/banzai/filter/asset_proxy_filter_spec.rb'
+ - 'spec/lib/extracts_path_spec.rb'
+ - 'spec/lib/extracts_ref_spec.rb'
+ - 'spec/lib/gitlab/auth/auth_finders_spec.rb'
+ - 'spec/lib/gitlab/auth/ldap/person_spec.rb'
+ - 'spec/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid_spec.rb'
+ - 'spec/lib/gitlab/bitbucket_import/importer_spec.rb'
+ - 'spec/lib/gitlab/chat_name_token_spec.rb'
+ - 'spec/lib/gitlab/ci/lint_spec.rb'
+ - 'spec/lib/gitlab/ci/status/composite_spec.rb'
+ - 'spec/lib/gitlab/contributions_calendar_spec.rb'
+ - 'spec/lib/gitlab/diff/parser_spec.rb'
+ - 'spec/lib/gitlab/email/hook/smime_signature_interceptor_spec.rb'
+ - 'spec/lib/gitlab/git/diff_collection_spec.rb'
+ - 'spec/lib/gitlab/git/diff_spec.rb'
+ - 'spec/lib/gitlab/git/repository_spec.rb'
+ - 'spec/lib/gitlab/http_spec.rb'
+ - 'spec/lib/gitlab/import_export/group/legacy_tree_restorer_spec.rb'
+ - 'spec/lib/gitlab/import_export/group/tree_restorer_spec.rb'
+ - 'spec/lib/gitlab/import_export/project/tree_restorer_spec.rb'
+ - 'spec/lib/gitlab/patch/prependable_spec.rb'
+ - 'spec/lib/gitlab/popen_spec.rb'
+ - 'spec/lib/gitlab/project_transfer_spec.rb'
+ - 'spec/lib/gitlab/reference_extractor_spec.rb'
+ - 'spec/lib/gitlab/tcp_checker_spec.rb'
+ - 'spec/lib/gitlab/user_access_spec.rb'
+ - 'spec/lib/gitlab/version_info_spec.rb'
+ - 'spec/lib/gitlab/x509/certificate_spec.rb'
+ - 'spec/mailers/emails/issues_spec.rb'
+ - 'spec/migrations/20220106163326_add_has_issues_on_vulnerability_reads_trigger_spec.rb'
+ - 'spec/migrations/rename_services_to_integrations_spec.rb'
+ - 'spec/migrations/replace_external_wiki_triggers_spec.rb'
+ - 'spec/models/group_spec.rb'
+ - 'spec/models/integrations/assembla_spec.rb'
+ - 'spec/models/integrations/campfire_spec.rb'
+ - 'spec/models/member_spec.rb'
+ - 'spec/models/members/project_member_spec.rb'
+ - 'spec/models/namespace_spec.rb'
+ - 'spec/models/note_spec.rb'
+ - 'spec/models/postgresql/replication_slot_spec.rb'
+ - 'spec/models/project_spec.rb'
+ - 'spec/models/user_spec.rb'
+ - 'spec/models/users/in_product_marketing_email_spec.rb'
+ - 'spec/rack_servers/puma_spec.rb'
+ - 'spec/requests/api/admin/plan_limits_spec.rb'
+ - 'spec/requests/api/users_spec.rb'
+ - 'spec/requests/git_http_spec.rb'
+ - 'spec/requests/openid_connect_spec.rb'
+ - 'spec/requests/projects/issues/discussions_spec.rb'
+ - 'spec/rubocop/cop/migration/update_column_in_batches_spec.rb'
+ - 'spec/services/ci/process_sync_events_service_spec.rb'
+ - 'spec/services/labels/update_service_spec.rb'
+ - 'spec/services/members/destroy_service_spec.rb'
+ - 'spec/services/merge_requests/close_service_spec.rb'
+ - 'spec/services/merge_requests/refresh_service_spec.rb'
+ - 'spec/services/merge_requests/reopen_service_spec.rb'
+ - 'spec/services/merge_requests/update_service_spec.rb'
+ - 'spec/services/milestones/create_service_spec.rb'
+ - 'spec/services/notes/post_process_service_spec.rb'
+ - 'spec/services/notes/update_service_spec.rb'
+ - 'spec/services/notification_service_spec.rb'
+ - 'spec/services/pages/zip_directory_service_spec.rb'
+ - 'spec/services/projects/create_from_template_service_spec.rb'
+ - 'spec/services/projects/download_service_spec.rb'
+ - 'spec/services/projects/fork_service_spec.rb'
+ - 'spec/services/upload_service_spec.rb'
+ - 'spec/support/shared_contexts/controllers/ldap_omniauth_callbacks_controller_shared_context.rb'
+ - 'spec/support/shared_contexts/email_shared_context.rb'
+ - 'spec/support/shared_examples/features/wiki/user_views_wiki_empty_shared_examples.rb'
+ - 'spec/support/shared_examples/path_extraction_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/notes_shared_examples.rb'
+ - 'spec/support_specs/helpers/stub_feature_flags_spec.rb'
+ - 'spec/views/search/_results.html.haml_spec.rb'
+ - 'spec/workers/emails_on_push_worker_spec.rb'
diff --git a/.rubocop_todo/rspec/leaky_constant_declaration.yml b/.rubocop_todo/rspec/leaky_constant_declaration.yml
index b8b3980ef4f..824be5765c8 100644
--- a/.rubocop_todo/rspec/leaky_constant_declaration.yml
+++ b/.rubocop_todo/rspec/leaky_constant_declaration.yml
@@ -1,15 +1,8 @@
---
RSpec/LeakyConstantDeclaration:
Exclude:
- - spec/db/schema_spec.rb
- - spec/lib/gitlab/config/entry/simplifiable_spec.rb
- - spec/lib/gitlab/quick_actions/dsl_spec.rb
- - spec/lib/marginalia_spec.rb
- - spec/mailers/notify_spec.rb
- - spec/models/concerns/batch_destroy_dependent_associations_spec.rb
- - spec/models/concerns/bulk_insert_safe_spec.rb
- - spec/models/concerns/bulk_insertable_associations_spec.rb
- - spec/models/concerns/triggerable_hooks_spec.rb
- - spec/models/repository_spec.rb
- - spec/services/clusters/applications/check_installation_progress_service_spec.rb
- - spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb
+ - 'spec/db/schema_spec.rb'
+ - 'spec/lib/gitlab/config/entry/simplifiable_spec.rb'
+ - 'spec/lib/marginalia_spec.rb'
+ - 'spec/models/concerns/batch_destroy_dependent_associations_spec.rb'
+ - 'spec/models/concerns/bulk_insert_safe_spec.rb'
diff --git a/.rubocop_todo/rspec/let_before_examples.yml b/.rubocop_todo/rspec/let_before_examples.yml
deleted file mode 100644
index f84df4bdb5f..00000000000
--- a/.rubocop_todo/rspec/let_before_examples.yml
+++ /dev/null
@@ -1,17 +0,0 @@
----
-# Cop supports --auto-correct.
-RSpec/LetBeforeExamples:
- Exclude:
- - 'ee/spec/lib/ee/gitlab/scim/provisioning_service_spec.rb'
- - 'ee/spec/models/resource_weight_event_spec.rb'
- - 'ee/spec/models/vulnerabilities/feedback_spec.rb'
- - 'ee/spec/requests/api/internal/kubernetes_spec.rb'
- - 'ee/spec/requests/api/scim_spec.rb'
- - 'spec/graphql/types/ci/detailed_status_type_spec.rb'
- - 'spec/graphql/types/merge_request_type_spec.rb'
- - 'spec/graphql/types/snippets/blob_type_spec.rb'
- - 'spec/models/ci/job_token/project_scope_link_spec.rb'
- - 'spec/models/ci/runner_version_spec.rb'
- - 'spec/models/group_deploy_key_spec.rb'
- - 'spec/models/snippet_repository_spec.rb'
- - 'spec/requests/api/issue_links_spec.rb'
diff --git a/.rubocop_todo/rspec/overwriting_setup.yml b/.rubocop_todo/rspec/overwriting_setup.yml
index 630f08eb72f..2bca06918fc 100644
--- a/.rubocop_todo/rspec/overwriting_setup.yml
+++ b/.rubocop_todo/rspec/overwriting_setup.yml
@@ -3,7 +3,6 @@ RSpec/OverwritingSetup:
Exclude:
- 'ee/spec/features/groups/analytics/ci_cd_analytics_spec.rb'
- 'ee/spec/lib/gitlab/analytics/type_of_work/tasks_by_type_spec.rb'
- - 'ee/spec/models/ee/iteration_spec.rb'
- 'ee/spec/requests/api/namespaces_spec.rb'
- 'ee/spec/services/epics/descendant_count_service_spec.rb'
- 'spec/finders/packages/helm/packages_finder_spec.rb'
diff --git a/.rubocop_todo/rspec/predicate_matcher.yml b/.rubocop_todo/rspec/predicate_matcher.yml
deleted file mode 100644
index 5847080dcde..00000000000
--- a/.rubocop_todo/rspec/predicate_matcher.yml
+++ /dev/null
@@ -1,516 +0,0 @@
----
-# Cop supports --auto-correct.
-RSpec/PredicateMatcher:
- Exclude:
- - 'ee/spec/controllers/admin/elasticsearch_controller_spec.rb'
- - 'ee/spec/controllers/admin/geo/projects_controller_spec.rb'
- - 'ee/spec/controllers/ee/sent_notifications_controller_spec.rb'
- - 'ee/spec/controllers/groups/group_members_controller_spec.rb'
- - 'ee/spec/controllers/groups/ldaps_controller_spec.rb'
- - 'ee/spec/controllers/projects_controller_spec.rb'
- - 'ee/spec/elastic/migrate/20220118150500_delete_orphaned_commits_spec.rb'
- - 'ee/spec/elastic/migrate/20220119120500_populate_commit_permissions_in_main_index_spec.rb'
- - 'ee/spec/elastic/migrate/20220613120500_migrate_commits_to_separate_index_spec.rb'
- - 'ee/spec/elastic/migrate/20220713103500_delete_commits_from_original_index_spec.rb'
- - 'ee/spec/elastic/migrate/migration_shared_examples.rb'
- - 'ee/spec/features/admin/admin_settings_spec.rb'
- - 'ee/spec/features/projects/members/member_is_removed_from_project_spec.rb'
- - 'ee/spec/features/projects/mirror_spec.rb'
- - 'ee/spec/features/signup_spec.rb'
- - 'ee/spec/finders/epics_finder_spec.rb'
- - 'ee/spec/graphql/resolvers/path_locks_resolver_spec.rb'
- - 'ee/spec/helpers/ee/groups_helper_spec.rb'
- - 'ee/spec/helpers/ee/issues_helper_spec.rb'
- - 'ee/spec/helpers/projects_helper_spec.rb'
- - 'ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb'
- - 'ee/spec/lib/ee/gitlab/ci/pipeline/chain/validate/after_config_spec.rb'
- - 'ee/spec/lib/ee/gitlab/ci/pipeline/quota/size_spec.rb'
- - 'ee/spec/lib/ee/gitlab/database_spec.rb'
- - 'ee/spec/lib/ee/gitlab/elastic/helper_spec.rb'
- - 'ee/spec/lib/gitlab/auth/group_saml/gma_membership_enforcer_spec.rb'
- - 'ee/spec/lib/gitlab/auth/group_saml/membership_enforcer_spec.rb'
- - 'ee/spec/lib/gitlab/auth/ldap/access_spec.rb'
- - 'ee/spec/lib/gitlab/checks/diff_check_spec.rb'
- - 'ee/spec/lib/gitlab/email/handler/create_note_handler_spec.rb'
- - 'ee/spec/lib/gitlab/geo/geo_node_status_check_spec.rb'
- - 'ee/spec/lib/gitlab/geo/jwt_request_decoder_spec.rb'
- - 'ee/spec/lib/gitlab/geo/replicator_spec.rb'
- - 'ee/spec/lib/gitlab/geo_spec.rb'
- - 'ee/spec/lib/gitlab/mirror_spec.rb'
- - 'ee/spec/lib/gitlab/user_access_spec.rb'
- - 'ee/spec/lib/system_check/geo/authorized_keys_flag_check_spec.rb'
- - 'ee/spec/lib/system_check/geo/current_node_check_spec.rb'
- - 'ee/spec/lib/system_check/geo/http_connection_check_spec.rb'
- - 'ee/spec/models/allowed_email_domain_spec.rb'
- - 'ee/spec/models/application_setting_spec.rb'
- - 'ee/spec/models/approval_state_spec.rb'
- - 'ee/spec/models/ci/minutes/notification_spec.rb'
- - 'ee/spec/models/concerns/approval_rule_like_spec.rb'
- - 'ee/spec/models/concerns/elastic/issue_spec.rb'
- - 'ee/spec/models/concerns/elastic/note_spec.rb'
- - 'ee/spec/models/concerns/elastic/project_spec.rb'
- - 'ee/spec/models/concerns/geo/verification_state_spec.rb'
- - 'ee/spec/models/dast_site_profile_spec.rb'
- - 'ee/spec/models/ee/ci/runner_spec.rb'
- - 'ee/spec/models/ee/group_spec.rb'
- - 'ee/spec/models/ee/label_spec.rb'
- - 'ee/spec/models/ee/list_spec.rb'
- - 'ee/spec/models/ee/namespace_spec.rb'
- - 'ee/spec/models/ee/user_spec.rb'
- - 'ee/spec/models/epic_spec.rb'
- - 'ee/spec/models/geo/container_repository_registry_spec.rb'
- - 'ee/spec/models/geo/project_registry_spec.rb'
- - 'ee/spec/models/geo_node_spec.rb'
- - 'ee/spec/models/ip_restriction_spec.rb'
- - 'ee/spec/models/issue_spec.rb'
- - 'ee/spec/models/license_spec.rb'
- - 'ee/spec/models/namespace_setting_spec.rb'
- - 'ee/spec/models/note_spec.rb'
- - 'ee/spec/models/path_lock_spec.rb'
- - 'ee/spec/models/preloaders/environments/protected_environment_preloader_spec.rb'
- - 'ee/spec/models/project_import_state_spec.rb'
- - 'ee/spec/models/project_spec.rb'
- - 'ee/spec/models/saml_provider_spec.rb'
- - 'ee/spec/models/security/orchestration_policy_configuration_spec.rb'
- - 'ee/spec/presenters/ci/minutes/usage_presenter_spec.rb'
- - 'ee/spec/requests/api/boards_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/epics/set_subscription_spec.rb'
- - 'ee/spec/requests/api/groups_spec.rb'
- - 'ee/spec/requests/api/members_spec.rb'
- - 'ee/spec/requests/api/projects_spec.rb'
- - 'ee/spec/services/approval_rules/params_filtering_service_spec.rb'
- - 'ee/spec/services/audit_event_service_spec.rb'
- - 'ee/spec/services/audit_events/register_runner_audit_event_service_spec.rb'
- - 'ee/spec/services/ci/process_build_service_spec.rb'
- - 'ee/spec/services/ci/runners/register_runner_service_spec.rb'
- - 'ee/spec/services/ee/allowed_email_domains/update_service_spec.rb'
- - 'ee/spec/services/ee/ip_restrictions/update_service_spec.rb'
- - 'ee/spec/services/ee/issuable/bulk_update_service_spec.rb'
- - 'ee/spec/services/geo/container_repository_sync_service_spec.rb'
- - 'ee/spec/services/geo/event_service_spec.rb'
- - 'ee/spec/services/geo/files_expire_service_spec.rb'
- - 'ee/spec/services/geo/hashed_storage_attachments_migration_service_spec.rb'
- - 'ee/spec/services/geo/move_repository_service_spec.rb'
- - 'ee/spec/services/geo/repository_destroy_service_spec.rb'
- - 'ee/spec/services/groups/mark_for_deletion_service_spec.rb'
- - 'ee/spec/services/groups/restore_service_spec.rb'
- - 'ee/spec/services/iterations/cadences/create_service_spec.rb'
- - 'ee/spec/services/iterations/create_service_spec.rb'
- - 'ee/spec/services/jira/requests/issues/list_service_spec.rb'
- - 'ee/spec/services/milestones/promote_service_spec.rb'
- - 'ee/spec/services/protected_environments/create_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/manually_create_service_spec.rb'
- - 'ee/spec/services/vulnerability_exports/export_service_spec.rb'
- - 'ee/spec/support/shared_examples/graphql/mutations/dast_on_demand_scans_shared_examples.rb'
- - 'ee/spec/support/shared_examples/lib/gitlab/geo/geo_log_cursor_event_shared_examples.rb'
- - 'ee/spec/support/shared_examples/models/concerns/elastic/limited_indexing_shared_examples.rb'
- - 'ee/spec/support/shared_examples/models/concerns/verifiable_replicator_shared_examples.rb'
- - 'ee/spec/support/shared_examples/models/geo_verifiable_registry_shared_examples.rb'
- - 'ee/spec/support/shared_examples/models/member_shared_examples.rb'
- - 'ee/spec/tasks/geo_rake_spec.rb'
- - 'ee/spec/workers/concerns/elastic/indexing_control_spec.rb'
- - 'ee/spec/workers/elastic/migration_worker_spec.rb'
- - 'ee/spec/workers/geo/batch/project_registry_worker_spec.rb'
- - 'qa/qa/specs/features/browser_ui/1_manage/project/create_project_badge_spec.rb'
- - 'qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/12_geo/database_delete_replication_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/2_plan/epic/roadmap_spec.rb'
- - 'qa/spec/runtime/env_spec.rb'
- - 'qa/spec/specs/helpers/context_selector_spec.rb'
- - 'spec/components/diffs/overflow_warning_component_spec.rb'
- - 'spec/controllers/admin/dev_ops_report_controller_spec.rb'
- - 'spec/controllers/admin/topics/avatars_controller_spec.rb'
- - 'spec/controllers/admin/users_controller_spec.rb'
- - 'spec/controllers/application_controller_spec.rb'
- - 'spec/controllers/concerns/checks_collaboration_spec.rb'
- - 'spec/controllers/groups/avatars_controller_spec.rb'
- - 'spec/controllers/groups/clusters_controller_spec.rb'
- - 'spec/controllers/groups/group_links_controller_spec.rb'
- - 'spec/controllers/groups/group_members_controller_spec.rb'
- - 'spec/controllers/groups/settings/applications_controller_spec.rb'
- - 'spec/controllers/omniauth_callbacks_controller_spec.rb'
- - 'spec/controllers/profiles/avatars_controller_spec.rb'
- - 'spec/controllers/profiles_controller_spec.rb'
- - 'spec/controllers/projects/avatars_controller_spec.rb'
- - 'spec/controllers/projects/clusters_controller_spec.rb'
- - 'spec/controllers/projects/issues_controller_spec.rb'
- - 'spec/controllers/projects/jobs_controller_spec.rb'
- - 'spec/controllers/projects/merge_requests_controller_spec.rb'
- - 'spec/controllers/projects/pipelines_controller_spec.rb'
- - 'spec/controllers/projects/project_members_controller_spec.rb'
- - 'spec/controllers/projects_controller_spec.rb'
- - 'spec/controllers/sent_notifications_controller_spec.rb'
- - 'spec/controllers/sessions_controller_spec.rb'
- - 'spec/controllers/snippets/notes_controller_spec.rb'
- - 'spec/features/admin/admin_settings_spec.rb'
- - 'spec/features/admin/users/user_spec.rb'
- - 'spec/features/admin/users/users_spec.rb'
- - 'spec/features/groups/members/request_access_spec.rb'
- - 'spec/features/groups/share_lock_spec.rb'
- - 'spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb'
- - 'spec/features/password_reset_spec.rb'
- - 'spec/features/profile_spec.rb'
- - 'spec/features/profiles/emails_spec.rb'
- - 'spec/features/projects/integrations/user_activates_mattermost_slash_command_spec.rb'
- - 'spec/features/projects/jobs/user_browses_job_spec.rb'
- - 'spec/features/projects/members/member_leaves_project_spec.rb'
- - 'spec/features/projects/members/user_requests_access_spec.rb'
- - 'spec/features/projects/pages/user_edits_settings_spec.rb'
- - 'spec/features/projects/settings/forked_project_settings_spec.rb'
- - 'spec/features/unsubscribe_links_spec.rb'
- - 'spec/features/users/signup_spec.rb'
- - 'spec/finders/group_descendants_finder_spec.rb'
- - 'spec/finders/merge_request_target_project_finder_spec.rb'
- - 'spec/helpers/application_helper_spec.rb'
- - 'spec/helpers/application_settings_helper_spec.rb'
- - 'spec/helpers/auth_helper_spec.rb'
- - 'spec/helpers/blob_helper_spec.rb'
- - 'spec/helpers/clusters_helper_spec.rb'
- - 'spec/helpers/groups_helper_spec.rb'
- - 'spec/helpers/issues_helper_spec.rb'
- - 'spec/helpers/projects_helper_spec.rb'
- - 'spec/helpers/recaptcha_helper_spec.rb'
- - 'spec/helpers/sessions_helper_spec.rb'
- - 'spec/helpers/sorting_helper_spec.rb'
- - 'spec/lib/backup/files_spec.rb'
- - 'spec/lib/bitbucket/connection_spec.rb'
- - 'spec/lib/bitbucket/page_spec.rb'
- - 'spec/lib/bitbucket/representation/pull_request_comment_spec.rb'
- - 'spec/lib/bitbucket/representation/repo_spec.rb'
- - 'spec/lib/bitbucket_server/page_spec.rb'
- - 'spec/lib/bitbucket_server/paginator_spec.rb'
- - 'spec/lib/bitbucket_server/representation/activity_spec.rb'
- - 'spec/lib/bitbucket_server/representation/pull_request_comment_spec.rb'
- - 'spec/lib/bitbucket_server/representation/pull_request_spec.rb'
- - 'spec/lib/constraints/group_url_constrainer_spec.rb'
- - 'spec/lib/constraints/project_url_constrainer_spec.rb'
- - 'spec/lib/constraints/user_url_constrainer_spec.rb'
- - 'spec/lib/feature_spec.rb'
- - 'spec/lib/gitlab/auth/ip_rate_limiter_spec.rb'
- - 'spec/lib/gitlab/auth/ldap/access_spec.rb'
- - 'spec/lib/gitlab/auth/ldap/config_spec.rb'
- - 'spec/lib/gitlab/auth/ldap/user_spec.rb'
- - 'spec/lib/gitlab/auth/o_auth/provider_spec.rb'
- - 'spec/lib/gitlab/auth/o_auth/user_spec.rb'
- - 'spec/lib/gitlab/auth/result_spec.rb'
- - 'spec/lib/gitlab/auth/two_factor_auth_verifier_spec.rb'
- - 'spec/lib/gitlab/auth_spec.rb'
- - 'spec/lib/gitlab/authorized_keys_spec.rb'
- - 'spec/lib/gitlab/background_migration/backfill_jira_tracker_deployment_type2_spec.rb'
- - 'spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb'
- - 'spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb'
- - 'spec/lib/gitlab/background_migration/merge_topics_with_same_name_spec.rb'
- - 'spec/lib/gitlab/blob_helper_spec.rb'
- - 'spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb'
- - 'spec/lib/gitlab/checks/lfs_integrity_spec.rb'
- - 'spec/lib/gitlab/ci/ansi2json/line_spec.rb'
- - 'spec/lib/gitlab/ci/ansi2json/parser_spec.rb'
- - 'spec/lib/gitlab/ci/config/entry/job_spec.rb'
- - 'spec/lib/gitlab/ci/lint_spec.rb'
- - 'spec/lib/gitlab/ci/matching/build_matcher_spec.rb'
- - 'spec/lib/gitlab/ci/matching/runner_matcher_spec.rb'
- - 'spec/lib/gitlab/ci/parsers/test/junit_spec.rb'
- - 'spec/lib/gitlab/ci/pipeline/chain/limit/rate_limit_spec.rb'
- - 'spec/lib/gitlab/ci/pipeline/chain/pipeline/process_spec.rb'
- - 'spec/lib/gitlab/ci/reports/test_case_spec.rb'
- - 'spec/lib/gitlab/ci/status/build/failed_spec.rb'
- - 'spec/lib/gitlab/ci/status/build/waiting_for_approval_spec.rb'
- - 'spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb'
- - 'spec/lib/gitlab/ci/trace/archive_spec.rb'
- - 'spec/lib/gitlab/ci/trace_spec.rb'
- - 'spec/lib/gitlab/ci_access_spec.rb'
- - 'spec/lib/gitlab/cleanup/orphan_job_artifact_files_batch_spec.rb'
- - 'spec/lib/gitlab/cleanup/orphan_lfs_file_references_spec.rb'
- - 'spec/lib/gitlab/cleanup/project_uploads_spec.rb'
- - 'spec/lib/gitlab/content_security_policy/config_loader_spec.rb'
- - 'spec/lib/gitlab/cross_project_access/check_collection_spec.rb'
- - 'spec/lib/gitlab/cross_project_access/check_info_spec.rb'
- - 'spec/lib/gitlab/current_settings_spec.rb'
- - 'spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb'
- - 'spec/lib/gitlab/database/migration_helpers_spec.rb'
- - 'spec/lib/gitlab/database/migration_spec.rb'
- - 'spec/lib/gitlab/database/migrations/runner_spec.rb'
- - 'spec/lib/gitlab/database/partitioning/time_partition_spec.rb'
- - 'spec/lib/gitlab/database/postgresql_adapter/force_disconnectable_mixin_spec.rb'
- - 'spec/lib/gitlab/database/reflection_spec.rb'
- - 'spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb'
- - 'spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb'
- - 'spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb'
- - 'spec/lib/gitlab/dependency_linker/cargo_toml_linker_spec.rb'
- - 'spec/lib/gitlab/dependency_linker/cartfile_linker_spec.rb'
- - 'spec/lib/gitlab/dependency_linker/composer_json_linker_spec.rb'
- - 'spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb'
- - 'spec/lib/gitlab/dependency_linker/gemspec_linker_spec.rb'
- - 'spec/lib/gitlab/dependency_linker/go_mod_linker_spec.rb'
- - 'spec/lib/gitlab/dependency_linker/go_sum_linker_spec.rb'
- - 'spec/lib/gitlab/dependency_linker/godeps_json_linker_spec.rb'
- - 'spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb'
- - 'spec/lib/gitlab/dependency_linker/podfile_linker_spec.rb'
- - 'spec/lib/gitlab/dependency_linker/podspec_json_linker_spec.rb'
- - 'spec/lib/gitlab/dependency_linker/podspec_linker_spec.rb'
- - 'spec/lib/gitlab/dependency_linker/requirements_txt_linker_spec.rb'
- - 'spec/lib/gitlab/deploy_key_access_spec.rb'
- - 'spec/lib/gitlab/diff/file_spec.rb'
- - 'spec/lib/gitlab/diff/position_spec.rb'
- - 'spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb'
- - 'spec/lib/gitlab/email/handler/create_issue_handler_spec.rb'
- - 'spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb'
- - 'spec/lib/gitlab/email/handler/create_note_handler_spec.rb'
- - 'spec/lib/gitlab/email/handler/service_desk_handler_spec.rb'
- - 'spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb'
- - 'spec/lib/gitlab/experimentation/group_types_spec.rb'
- - 'spec/lib/gitlab/external_authorization_spec.rb'
- - 'spec/lib/gitlab/fake_application_settings_spec.rb'
- - 'spec/lib/gitlab/git/blob_spec.rb'
- - 'spec/lib/gitlab/git/branch_spec.rb'
- - 'spec/lib/gitlab/git/keep_around_spec.rb'
- - 'spec/lib/gitlab/git/repository_spec.rb'
- - 'spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb'
- - 'spec/lib/gitlab/git/tag_spec.rb'
- - 'spec/lib/gitlab/git/tree_spec.rb'
- - 'spec/lib/gitlab/git_access_snippet_spec.rb'
- - 'spec/lib/gitlab/git_post_receive_spec.rb'
- - 'spec/lib/gitlab/gitaly_client/repository_service_spec.rb'
- - 'spec/lib/gitlab/gitaly_client/storage_settings_spec.rb'
- - 'spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb'
- - 'spec/lib/gitlab/gl_repository/repo_type_spec.rb'
- - 'spec/lib/gitlab/gpg/commit_spec.rb'
- - 'spec/lib/gitlab/graphql/query_analyzers/ast/recursion_analyzer_spec.rb'
- - 'spec/lib/gitlab/hashed_storage/migrator_spec.rb'
- - 'spec/lib/gitlab/i18n/translation_entry_spec.rb'
- - 'spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb'
- - 'spec/lib/gitlab/import_export/fork_spec.rb'
- - 'spec/lib/gitlab/import_export/group/tree_restorer_spec.rb'
- - 'spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb'
- - 'spec/lib/gitlab/import_export/snippet_repo_saver_spec.rb'
- - 'spec/lib/gitlab/import_export/snippets_repo_saver_spec.rb'
- - 'spec/lib/gitlab/kubernetes/deployment_spec.rb'
- - 'spec/lib/gitlab/kubernetes/kube_client_spec.rb'
- - 'spec/lib/gitlab/kubernetes/namespace_spec.rb'
- - 'spec/lib/gitlab/legacy_github_import/pull_request_formatter_spec.rb'
- - 'spec/lib/gitlab/mail_room/mail_room_spec.rb'
- - 'spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb'
- - 'spec/lib/gitlab/markdown_cache/field_data_spec.rb'
- - 'spec/lib/gitlab/markup_helper_spec.rb'
- - 'spec/lib/gitlab/metrics/prometheus_spec.rb'
- - 'spec/lib/gitlab/null_request_store_spec.rb'
- - 'spec/lib/gitlab/pagination/cursor_based_keyset_spec.rb'
- - 'spec/lib/gitlab/pagination/keyset_spec.rb'
- - 'spec/lib/gitlab/performance_bar_spec.rb'
- - 'spec/lib/gitlab/project_transfer_spec.rb'
- - 'spec/lib/gitlab/reference_extractor_spec.rb'
- - 'spec/lib/gitlab/request_forgery_protection_spec.rb'
- - 'spec/lib/gitlab/sanitizers/svg_spec.rb'
- - 'spec/lib/gitlab/search/found_blob_spec.rb'
- - 'spec/lib/gitlab/search/found_wiki_page_spec.rb'
- - 'spec/lib/gitlab/service_desk_email_spec.rb'
- - 'spec/lib/gitlab/shard_health_cache_spec.rb'
- - 'spec/lib/gitlab/uploads_transfer_spec.rb'
- - 'spec/lib/gitlab/usage/metric_definition_spec.rb'
- - 'spec/lib/gitlab/usage/service_ping/legacy_metric_timing_decorator_spec.rb'
- - 'spec/lib/gitlab/user_access_snippet_spec.rb'
- - 'spec/lib/gitlab/user_access_spec.rb'
- - 'spec/lib/gitlab/utils/sanitize_node_link_spec.rb'
- - 'spec/lib/gitlab/version_info_spec.rb'
- - 'spec/lib/gitlab/view/presenter/base_spec.rb'
- - 'spec/lib/gitlab/visibility_level_spec.rb'
- - 'spec/lib/object_storage/direct_upload_spec.rb'
- - 'spec/lib/sidebars/projects/menus/external_issue_tracker_menu_spec.rb'
- - 'spec/lib/sidebars/projects/menus/external_wiki_menu_spec.rb'
- - 'spec/lib/sidebars/projects/menus/shimo_menu_spec.rb'
- - 'spec/lib/system_check/app/hashed_storage_all_projects_check_spec.rb'
- - 'spec/lib/system_check/app/hashed_storage_enabled_check_spec.rb'
- - 'spec/migrations/20210713042000_fix_ci_sources_pipelines_index_names_spec.rb'
- - 'spec/migrations/20210907211557_finalize_ci_builds_bigint_conversion_spec.rb'
- - 'spec/migrations/20220128155814_fix_approval_rules_code_owners_rule_type_index_spec.rb'
- - 'spec/migrations/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints_spec.rb'
- - 'spec/models/blob_spec.rb'
- - 'spec/models/blob_viewer/base_spec.rb'
- - 'spec/models/ci/build_spec.rb'
- - 'spec/models/ci/build_trace_chunk_spec.rb'
- - 'spec/models/ci/job_artifact_spec.rb'
- - 'spec/models/ci/pipeline_spec.rb'
- - 'spec/models/ci/processable_spec.rb'
- - 'spec/models/ci/runner_spec.rb'
- - 'spec/models/clusters/agent_spec.rb'
- - 'spec/models/commit_spec.rb'
- - 'spec/models/concerns/access_requestable_spec.rb'
- - 'spec/models/concerns/awardable_spec.rb'
- - 'spec/models/concerns/chronic_duration_attribute_spec.rb'
- - 'spec/models/concerns/ci/has_deployment_name_spec.rb'
- - 'spec/models/concerns/counter_attribute_spec.rb'
- - 'spec/models/concerns/featurable_spec.rb'
- - 'spec/models/concerns/ignorable_columns_spec.rb'
- - 'spec/models/concerns/integrations/has_data_fields_spec.rb'
- - 'spec/models/concerns/issuable_spec.rb'
- - 'spec/models/concerns/mentionable_spec.rb'
- - 'spec/models/concerns/milestoneable_spec.rb'
- - 'spec/models/concerns/resolvable_discussion_spec.rb'
- - 'spec/models/concerns/resolvable_note_spec.rb'
- - 'spec/models/concerns/routable_spec.rb'
- - 'spec/models/concerns/spammable_spec.rb'
- - 'spec/models/concerns/subscribable_spec.rb'
- - 'spec/models/container_repository_spec.rb'
- - 'spec/models/customer_relations/contact_spec.rb'
- - 'spec/models/deploy_token_spec.rb'
- - 'spec/models/diff_note_spec.rb'
- - 'spec/models/diff_viewer/base_spec.rb'
- - 'spec/models/email_spec.rb'
- - 'spec/models/event_spec.rb'
- - 'spec/models/gpg_key_spec.rb'
- - 'spec/models/group_spec.rb'
- - 'spec/models/hooks/web_hook_log_spec.rb'
- - 'spec/models/identity_spec.rb'
- - 'spec/models/integration_spec.rb'
- - 'spec/models/integrations/base_issue_tracker_spec.rb'
- - 'spec/models/integrations/base_third_party_wiki_spec.rb'
- - 'spec/models/integrations/jira_spec.rb'
- - 'spec/models/issue_spec.rb'
- - 'spec/models/members/project_member_spec.rb'
- - 'spec/models/merge_request_diff_spec.rb'
- - 'spec/models/merge_request_spec.rb'
- - 'spec/models/milestone_spec.rb'
- - 'spec/models/namespace/aggregation_schedule_spec.rb'
- - 'spec/models/namespace_setting_spec.rb'
- - 'spec/models/namespace_spec.rb'
- - 'spec/models/note_spec.rb'
- - 'spec/models/postgresql/replication_slot_spec.rb'
- - 'spec/models/project_feature_spec.rb'
- - 'spec/models/project_spec.rb'
- - 'spec/models/project_statistics_spec.rb'
- - 'spec/models/project_team_spec.rb'
- - 'spec/models/remote_mirror_spec.rb'
- - 'spec/models/repository_spec.rb'
- - 'spec/models/route_spec.rb'
- - 'spec/models/sent_notification_spec.rb'
- - 'spec/models/snippet_spec.rb'
- - 'spec/models/todo_spec.rb'
- - 'spec/models/upload_spec.rb'
- - 'spec/models/uploads/local_spec.rb'
- - 'spec/models/user_agent_detail_spec.rb'
- - 'spec/models/user_spec.rb'
- - 'spec/models/wiki_page_spec.rb'
- - 'spec/policies/project_policy_spec.rb'
- - 'spec/presenters/blob_presenter_spec.rb'
- - 'spec/presenters/ci/build_presenter_spec.rb'
- - 'spec/presenters/label_presenter_spec.rb'
- - 'spec/requests/api/admin/instance_clusters_spec.rb'
- - 'spec/requests/api/ci/jobs_spec.rb'
- - 'spec/requests/api/ci/runner/jobs_request_post_spec.rb'
- - 'spec/requests/api/ci/runners_spec.rb'
- - 'spec/requests/api/features_spec.rb'
- - 'spec/requests/api/group_clusters_spec.rb'
- - 'spec/requests/api/integrations_spec.rb'
- - 'spec/requests/api/internal/base_spec.rb'
- - 'spec/requests/api/merge_requests_spec.rb'
- - 'spec/requests/api/project_clusters_spec.rb'
- - 'spec/requests/api/project_export_spec.rb'
- - 'spec/requests/api/project_snippets_spec.rb'
- - 'spec/requests/api/projects_spec.rb'
- - 'spec/requests/api/resource_access_tokens_spec.rb'
- - 'spec/requests/api/snippets_spec.rb'
- - 'spec/requests/api/users_spec.rb'
- - 'spec/requests/git_http_spec.rb'
- - 'spec/requests/lfs_http_spec.rb'
- - 'spec/services/branches/create_service_spec.rb'
- - 'spec/services/ci/create_pipeline_service/needs_spec.rb'
- - 'spec/services/ci/create_pipeline_service/rate_limit_spec.rb'
- - 'spec/services/ci/delete_objects_service_spec.rb'
- - 'spec/services/ci/destroy_pipeline_service_spec.rb'
- - 'spec/services/ci/expire_pipeline_cache_service_spec.rb'
- - 'spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb'
- - 'spec/services/ci/job_artifacts/expire_project_build_artifacts_service_spec.rb'
- - 'spec/services/ci/runners/register_runner_service_spec.rb'
- - 'spec/services/clusters/destroy_service_spec.rb'
- - 'spec/services/concerns/exclusive_lease_guard_spec.rb'
- - 'spec/services/concerns/merge_requests/assigns_merge_params_spec.rb'
- - 'spec/services/container_expiration_policies/cleanup_service_spec.rb'
- - 'spec/services/container_expiration_policies/update_service_spec.rb'
- - 'spec/services/customer_relations/contacts/update_service_spec.rb'
- - 'spec/services/customer_relations/organizations/update_service_spec.rb'
- - 'spec/services/deployments/create_for_build_service_spec.rb'
- - 'spec/services/deployments/older_deployments_drop_service_spec.rb'
- - 'spec/services/draft_notes/publish_service_spec.rb'
- - 'spec/services/environments/schedule_to_delete_review_apps_service_spec.rb'
- - 'spec/services/groups/destroy_service_spec.rb'
- - 'spec/services/groups/group_links/create_service_spec.rb'
- - 'spec/services/groups/group_links/destroy_service_spec.rb'
- - 'spec/services/groups/transfer_service_spec.rb'
- - 'spec/services/groups/update_service_spec.rb'
- - 'spec/services/issuable/bulk_update_service_spec.rb'
- - 'spec/services/jira/requests/projects/list_service_spec.rb'
- - 'spec/services/jira_import/users_importer_spec.rb'
- - 'spec/services/merge_requests/build_service_spec.rb'
- - 'spec/services/merge_requests/conflicts/list_service_spec.rb'
- - 'spec/services/merge_requests/create_from_issue_service_spec.rb'
- - 'spec/services/merge_requests/merge_service_spec.rb'
- - 'spec/services/merge_requests/refresh_service_spec.rb'
- - 'spec/services/metrics/dashboard/cluster_metrics_embed_service_spec.rb'
- - 'spec/services/metrics/dashboard/panel_preview_service_spec.rb'
- - 'spec/services/metrics/users_starred_dashboards/delete_service_spec.rb'
- - 'spec/services/milestones/promote_service_spec.rb'
- - 'spec/services/milestones/transfer_service_spec.rb'
- - 'spec/services/namespaces/package_settings/update_service_spec.rb'
- - 'spec/services/note_summary_spec.rb'
- - 'spec/services/notes/build_service_spec.rb'
- - 'spec/services/notes/quick_actions_service_spec.rb'
- - 'spec/services/packages/cleanup/update_policy_service_spec.rb'
- - 'spec/services/packages/debian/find_or_create_incoming_service_spec.rb'
- - 'spec/services/packages/nuget/update_package_from_metadata_service_spec.rb'
- - 'spec/services/projects/after_rename_service_spec.rb'
- - 'spec/services/projects/cleanup_service_spec.rb'
- - 'spec/services/projects/create_service_spec.rb'
- - 'spec/services/projects/destroy_service_spec.rb'
- - 'spec/services/projects/fork_service_spec.rb'
- - 'spec/services/projects/hashed_storage/base_attachment_service_spec.rb'
- - 'spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb'
- - 'spec/services/projects/hashed_storage/migrate_repository_service_spec.rb'
- - 'spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb'
- - 'spec/services/projects/hashed_storage/rollback_repository_service_spec.rb'
- - 'spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb'
- - 'spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb'
- - 'spec/services/projects/update_pages_service_spec.rb'
- - 'spec/services/projects/update_service_spec.rb'
- - 'spec/services/releases/create_service_spec.rb'
- - 'spec/services/repositories/destroy_service_spec.rb'
- - 'spec/services/repository_archive_clean_up_service_spec.rb'
- - 'spec/services/resource_access_tokens/revoke_service_spec.rb'
- - 'spec/services/snippets/create_service_spec.rb'
- - 'spec/services/snippets/destroy_service_spec.rb'
- - 'spec/services/snippets/update_service_spec.rb'
- - 'spec/services/spam/akismet_service_spec.rb'
- - 'spec/services/system_notes/issuables_service_spec.rb'
- - 'spec/services/todo_service_spec.rb'
- - 'spec/services/users/destroy_service_spec.rb'
- - 'spec/support/shared_contexts/email_shared_context.rb'
- - 'spec/support/shared_examples/ci/auto_merge_merge_requests_shared_examples.rb'
- - 'spec/support/shared_examples/finders/issues_finder_shared_examples.rb'
- - 'spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb'
- - 'spec/support/shared_examples/models/application_setting_shared_examples.rb'
- - 'spec/support/shared_examples/models/cluster_application_core_shared_examples.rb'
- - 'spec/support/shared_examples/models/concerns/timebox_shared_examples.rb'
- - 'spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb'
- - 'spec/support/shared_examples/models/member_shared_examples.rb'
- - 'spec/support/shared_examples/models/note_access_check_shared_examples.rb'
- - 'spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/hooks_shared_examples.rb'
- - 'spec/support/shared_examples/uploaders/object_storage_shared_examples.rb'
- - 'spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb'
- - 'spec/tasks/gitlab/cleanup_rake_spec.rb'
- - 'spec/uploaders/object_storage_spec.rb'
- - 'spec/validators/any_field_validator_spec.rb'
- - 'spec/validators/array_members_validator_spec.rb'
- - 'spec/validators/cron_validator_spec.rb'
- - 'spec/validators/namespace_path_validator_spec.rb'
- - 'spec/validators/project_path_validator_spec.rb'
- - 'spec/workers/bulk_imports/entity_worker_spec.rb'
- - 'spec/workers/bulk_imports/pipeline_worker_spec.rb'
- - 'spec/workers/ci/delete_objects_worker_spec.rb'
- - 'spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb'
- - 'spec/workers/group_destroy_worker_spec.rb'
- - 'spec/workers/hashed_storage/migrator_worker_spec.rb'
- - 'spec/workers/hashed_storage/rollbacker_worker_spec.rb'
- - 'spec/workers/project_destroy_worker_spec.rb'
- - 'spec/workers/remote_mirror_notification_worker_spec.rb'
- - 'spec/workers/remove_expired_group_links_worker_spec.rb'
- - 'spec/workers/x509_issuer_crl_check_worker_spec.rb'
diff --git a/.rubocop_todo/rspec/repeated_example_group_body.yml b/.rubocop_todo/rspec/repeated_example_group_body.yml
index 6fdeb9a2094..93cbe77765a 100644
--- a/.rubocop_todo/rspec/repeated_example_group_body.yml
+++ b/.rubocop_todo/rspec/repeated_example_group_body.yml
@@ -43,16 +43,11 @@ RSpec/RepeatedExampleGroupBody:
- 'spec/lib/gitlab/sanitizers/exif_spec.rb'
- 'spec/models/ci/build_spec.rb'
- 'spec/models/deploy_token_spec.rb'
- - 'spec/models/group_spec.rb'
- - 'spec/models/merge_request_spec.rb'
- 'spec/models/project_spec.rb'
- 'spec/policies/project_policy_spec.rb'
- - 'spec/presenters/project_hook_presenter_spec.rb'
- - 'spec/requests/api/graphql/ci/runners_spec.rb'
- 'spec/services/boards/lists/update_service_spec.rb'
- 'spec/services/ci/create_web_ide_terminal_service_spec.rb'
- 'spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb'
- - 'spec/services/ci/register_job_service_spec.rb'
- 'spec/services/merge_requests/create_service_spec.rb'
- 'spec/services/merge_requests/mark_reviewer_reviewed_service_spec.rb'
- 'spec/services/merge_requests/mergeability/check_base_service_spec.rb'
diff --git a/.rubocop_todo/rspec/repeated_example_group_description.yml b/.rubocop_todo/rspec/repeated_example_group_description.yml
index 31c17c994a6..826ac2a6838 100644
--- a/.rubocop_todo/rspec/repeated_example_group_description.yml
+++ b/.rubocop_todo/rspec/repeated_example_group_description.yml
@@ -2,10 +2,8 @@
RSpec/RepeatedExampleGroupDescription:
Exclude:
- 'ee/spec/finders/merge_trains_finder_spec.rb'
- - 'ee/spec/finders/security/vulnerability_reads_finder_spec.rb'
- 'ee/spec/graphql/resolvers/vulnerabilities_grade_resolver_spec.rb'
- 'ee/spec/graphql/resolvers/vulnerability_severities_count_resolver_spec.rb'
- - 'ee/spec/helpers/ee/auth_helper_spec.rb'
- 'ee/spec/lib/gitlab/auth/ldap/person_spec.rb'
- 'ee/spec/lib/gitlab/usage/metrics/instrumentations/approval_project_rules_with_user_metric_spec.rb'
- 'ee/spec/models/approval_merge_request_rule_spec.rb'
@@ -23,16 +21,13 @@ RSpec/RepeatedExampleGroupDescription:
- 'ee/spec/policies/app_sec/fuzzing/coverage/corpus_policy_spec.rb'
- 'ee/spec/policies/group_policy_spec.rb'
- 'ee/spec/requests/api/graphql/iteration_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/iterations/create_spec.rb'
- 'ee/spec/requests/groups/security/credentials_controller_spec.rb'
- - 'ee/spec/requests/groups/settings/reporting_controller_spec.rb'
- 'ee/spec/services/app_sec/dast/profiles/create_associations_service_spec.rb'
- 'ee/spec/services/app_sec/dast/site_validations/find_or_create_service_spec.rb'
- 'ee/spec/services/audit_event_service_spec.rb'
- 'ee/spec/services/todo_service_spec.rb'
- 'ee/spec/support/shared_examples/models/concerns/verifiable_replicator_shared_examples.rb'
- 'ee/spec/support/shared_examples/services/scoped_label_shared_examples.rb'
- - 'ee/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb'
- 'spec/controllers/profiles/notifications_controller_spec.rb'
- 'spec/controllers/projects/issues_controller_spec.rb'
- 'spec/controllers/projects/merge_requests/drafts_controller_spec.rb'
@@ -61,7 +56,6 @@ RSpec/RepeatedExampleGroupDescription:
- 'spec/lib/gitlab/ci/config/entry/processable_spec.rb'
- 'spec/lib/gitlab/ci/config/entry/release_spec.rb'
- 'spec/lib/gitlab/ci/config_spec.rb'
- - 'spec/lib/gitlab/ci/parsers/security/common_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/seed/build_spec.rb'
- 'spec/lib/gitlab/ci/yaml_processor_spec.rb'
- 'spec/lib/gitlab/data_builder/push_spec.rb'
@@ -73,7 +67,6 @@ RSpec/RepeatedExampleGroupDescription:
- 'spec/lib/gitlab/import_export/project/sample/relation_factory_spec.rb'
- 'spec/lib/gitlab/kubernetes/rollout_status_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb'
- - 'spec/lib/gitlab/redis/multi_store_spec.rb'
- 'spec/lib/gitlab/sanitizers/exif_spec.rb'
- 'spec/lib/gitlab/template/finders/global_template_finder_spec.rb'
- 'spec/lib/gitlab/usage_data_spec.rb'
@@ -83,14 +76,11 @@ RSpec/RepeatedExampleGroupDescription:
- 'spec/models/concerns/ci/has_ref_spec.rb'
- 'spec/models/concerns/issuable_spec.rb'
- 'spec/models/integrations/chat_message/pipeline_message_spec.rb'
- - 'spec/models/merge_request_assignee_spec.rb'
- - 'spec/models/merge_request_reviewer_spec.rb'
- 'spec/models/merge_request_spec.rb'
- 'spec/models/personal_access_token_spec.rb'
- 'spec/models/project_spec.rb'
- 'spec/models/ssh_host_key_spec.rb'
- 'spec/requests/api/files_spec.rb'
- - 'spec/requests/api/graphql/ci/runners_spec.rb'
- 'spec/requests/api/graphql/project/release_spec.rb'
- 'spec/requests/api/group_clusters_spec.rb'
- 'spec/requests/api/internal/base_spec.rb'
@@ -100,8 +90,6 @@ RSpec/RepeatedExampleGroupDescription:
- 'spec/requests/api/users_spec.rb'
- 'spec/routing/project_routing_spec.rb'
- 'spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb'
- - 'spec/services/ci/register_job_service_spec.rb'
- - 'spec/services/import/github_service_spec.rb'
- 'spec/services/merge_requests/refresh_service_spec.rb'
- 'spec/services/metrics/dashboard/gitlab_alert_embed_service_spec.rb'
- 'spec/services/verify_pages_domain_service_spec.rb'
diff --git a/.rubocop_todo/rspec/return_from_stub.yml b/.rubocop_todo/rspec/return_from_stub.yml
index 215660f31d3..99da72936c6 100644
--- a/.rubocop_todo/rspec/return_from_stub.yml
+++ b/.rubocop_todo/rspec/return_from_stub.yml
@@ -1,27 +1,7 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
RSpec/ReturnFromStub:
Exclude:
- - 'ee/spec/controllers/admin/geo/nodes_controller_spec.rb'
- - 'ee/spec/controllers/groups/billings_controller_spec.rb'
- - 'ee/spec/controllers/groups/group_members_controller_spec.rb'
- - 'ee/spec/controllers/profiles/billings_controller_spec.rb'
- - 'ee/spec/controllers/projects/branches_controller_spec.rb'
- - 'ee/spec/features/account_recovery_regular_check_spec.rb'
- - 'ee/spec/features/admin/groups/admin_changes_plan_spec.rb'
- - 'ee/spec/features/burndown_charts_spec.rb'
- - 'ee/spec/features/groups/group_settings_spec.rb'
- - 'ee/spec/features/merge_trains/two_merge_requests_on_train_spec.rb'
- - 'ee/spec/features/projects/integrations/user_activates_jira_spec.rb'
- - 'ee/spec/features/projects/milestones/milestone_spec.rb'
- - 'ee/spec/features/projects/new_project_spec.rb'
- - 'ee/spec/features/projects/pipelines/legacy_pipeline_spec.rb'
- - 'ee/spec/features/projects/pipelines/pipeline_spec.rb'
- - 'ee/spec/features/projects/settings/ee/service_desk_setting_spec.rb'
- - 'ee/spec/features/promotion_spec.rb'
- - 'ee/spec/features/trials/select_namespace_spec.rb'
- - 'ee/spec/graphql/mutations/projects/set_locked_spec.rb'
- - 'ee/spec/helpers/application_helper_spec.rb'
- 'ee/spec/helpers/ee/auth_helper_spec.rb'
- 'ee/spec/helpers/ee/ci/pipelines_helper_spec.rb'
- 'ee/spec/helpers/ee/groups_helper_spec.rb'
@@ -37,7 +17,6 @@ RSpec/ReturnFromStub:
- 'ee/spec/lib/ee/feature_spec.rb'
- 'ee/spec/lib/ee/gitlab/checks/push_rules/branch_check_spec.rb'
- 'ee/spec/lib/ee/gitlab/database_spec.rb'
- - 'ee/spec/lib/ee/gitlab/git_access_project_spec.rb'
- 'ee/spec/lib/gitlab/ci/minutes/build_consumption_spec.rb'
- 'ee/spec/lib/gitlab/ci/minutes/cost_factor_spec.rb'
- 'ee/spec/lib/gitlab/geo/health_check_spec.rb'
@@ -53,7 +32,6 @@ RSpec/ReturnFromStub:
- 'ee/spec/models/ee/namespace_spec.rb'
- 'ee/spec/models/ee/user_spec.rb'
- 'ee/spec/models/license_spec.rb'
- - 'ee/spec/models/merge_request/blocking_spec.rb'
- 'ee/spec/models/project_spec.rb'
- 'ee/spec/models/vulnerabilities/finding_spec.rb'
- 'ee/spec/policies/project_policy_spec.rb'
@@ -92,7 +70,6 @@ RSpec/ReturnFromStub:
- 'ee/spec/views/layouts/application.html.haml_spec.rb'
- 'ee/spec/views/shared/_mirror_update_button.html.haml_spec.rb'
- 'ee/spec/workers/ee/ci/build_finished_worker_spec.rb'
- - 'ee/spec/workers/geo/container_repository_sync_dispatch_worker_spec.rb'
- 'ee/spec/workers/geo/design_repository_shard_sync_worker_spec.rb'
- 'ee/spec/workers/geo/repository_shard_sync_worker_spec.rb'
- 'ee/spec/workers/geo/repository_verification/primary/shard_worker_spec.rb'
@@ -122,7 +99,6 @@ RSpec/ReturnFromStub:
- 'spec/controllers/projects/merge_requests_controller_spec.rb'
- 'spec/controllers/projects/service_desk_controller_spec.rb'
- 'spec/controllers/projects_controller_spec.rb'
- - 'spec/controllers/search_controller_spec.rb'
- 'spec/features/groups/clusters/user_spec.rb'
- 'spec/features/groups/container_registry_spec.rb'
- 'spec/features/markdown/markdown_spec.rb'
@@ -163,7 +139,6 @@ RSpec/ReturnFromStub:
- 'spec/lib/gitlab/auth/saml/user_spec.rb'
- 'spec/lib/gitlab/auth_spec.rb'
- 'spec/lib/gitlab/background_migration/encrypt_static_object_token_spec.rb'
- - 'spec/lib/gitlab/batch_pop_queueing_spec.rb'
- 'spec/lib/gitlab/bitbucket_import/importer_spec.rb'
- 'spec/lib/gitlab/ci/build/policy/changes_spec.rb'
- 'spec/lib/gitlab/ci/config/external/mapper_spec.rb'
@@ -180,7 +155,6 @@ RSpec/ReturnFromStub:
- 'spec/lib/gitlab/error_tracking_spec.rb'
- 'spec/lib/gitlab/exclusive_lease_helpers/sleeping_lock_spec.rb'
- 'spec/lib/gitlab/exclusive_lease_helpers_spec.rb'
- - 'spec/lib/gitlab/experimentation/controller_concern_spec.rb'
- 'spec/lib/gitlab/external_authorization_spec.rb'
- 'spec/lib/gitlab/git/blob_spec.rb'
- 'spec/lib/gitlab/git_access_spec.rb'
@@ -194,7 +168,6 @@ RSpec/ReturnFromStub:
- 'spec/lib/gitlab/memory/reports_daemon_spec.rb'
- 'spec/lib/gitlab/metrics/system_spec.rb'
- 'spec/lib/gitlab/middleware/read_only_spec.rb'
- - 'spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb'
- 'spec/lib/gitlab/prometheus_client_spec.rb'
- 'spec/lib/gitlab/redis/cache_spec.rb'
- 'spec/lib/gitlab/redis/duplicate_jobs_spec.rb'
@@ -250,10 +223,8 @@ RSpec/ReturnFromStub:
- 'spec/services/captcha/captcha_verification_service_spec.rb'
- 'spec/services/ci/archive_trace_service_spec.rb'
- 'spec/services/ci/create_pipeline_service/logger_spec.rb'
- - 'spec/services/ci/list_config_variables_service_spec.rb'
- 'spec/services/ci/pipeline_artifacts/destroy_all_expired_service_spec.rb'
- 'spec/services/ci/pipeline_trigger_service_spec.rb'
- - 'spec/services/ci/process_build_service_spec.rb'
- 'spec/services/ci/register_job_service_spec.rb'
- 'spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb'
- 'spec/services/environments/auto_stop_service_spec.rb'
@@ -282,7 +253,6 @@ RSpec/ReturnFromStub:
- 'spec/services/projects/in_product_marketing_campaign_emails_service_spec.rb'
- 'spec/services/projects/update_remote_mirror_service_spec.rb'
- 'spec/services/projects/update_service_spec.rb'
- - 'spec/services/suggestions/apply_service_spec.rb'
- 'spec/services/suggestions/create_service_spec.rb'
- 'spec/services/verify_pages_domain_service_spec.rb'
- 'spec/support/redis/redis_shared_examples.rb'
@@ -292,7 +262,6 @@ RSpec/ReturnFromStub:
- 'spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_instance_shared_examples.rb'
- 'spec/support/shared_examples/lib/gitlab/sidekiq_middleware/strategy_shared_examples.rb'
- 'spec/support/shared_examples/models/concerns/can_move_repository_storage_shared_examples.rb'
- - 'spec/support/shared_examples/models/wiki_shared_examples.rb'
- 'spec/support/shared_examples/path_extraction_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb'
- 'spec/support/shared_examples/services/boards/boards_create_service_shared_examples.rb'
@@ -302,7 +271,6 @@ RSpec/ReturnFromStub:
- 'spec/uploaders/file_mover_spec.rb'
- 'spec/uploaders/gitlab_uploader_spec.rb'
- 'spec/uploaders/object_storage_spec.rb'
- - 'spec/uploaders/workers/object_storage/background_move_worker_spec.rb'
- 'spec/views/admin/application_settings/_ci_cd.html.haml_spec.rb'
- 'spec/views/admin/application_settings/_eks.html.haml_spec.rb'
- 'spec/views/admin/application_settings/_package_registry.html.haml_spec.rb'
diff --git a/.rubocop_todo/rspec/scattered_let.yml b/.rubocop_todo/rspec/scattered_let.yml
index 9a272ec31cc..9d049dd4a26 100644
--- a/.rubocop_todo/rspec/scattered_let.yml
+++ b/.rubocop_todo/rspec/scattered_let.yml
@@ -1,26 +1,7 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
RSpec/ScatteredLet:
Exclude:
- - 'ee/spec/features/boards/user_visits_board_spec.rb'
- - 'ee/spec/features/groups/group_roadmap_spec.rb'
- - 'ee/spec/features/milestones/user_views_milestone_spec.rb'
- - 'ee/spec/finders/analytics/devops_adoption/enabled_namespaces_finder_spec.rb'
- - 'ee/spec/finders/ee/alert_management/http_integrations_finder_spec.rb'
- - 'ee/spec/finders/issues_finder_spec.rb'
- - 'ee/spec/finders/security/pipeline_vulnerabilities_finder_spec.rb'
- - 'ee/spec/frontend/fixtures/runner.rb'
- - 'ee/spec/graphql/ee/types/clusters/agent_type_spec.rb'
- - 'ee/spec/graphql/mutations/boards/epics/create_spec.rb'
- - 'ee/spec/graphql/mutations/merge_requests/accept_spec.rb'
- - 'ee/spec/graphql/mutations/releases/update_spec.rb'
- - 'ee/spec/graphql/resolvers/analytics/devops_adoption/enabled_namespaces_resolver_spec.rb'
- - 'ee/spec/graphql/resolvers/clusters/agents_resolver_spec.rb'
- - 'ee/spec/graphql/resolvers/security_orchestration/scan_execution_policy_resolver_spec.rb'
- - 'ee/spec/graphql/types/asset_type_spec.rb'
- - 'ee/spec/graphql/types/boards/board_epic_type_spec.rb'
- - 'ee/spec/graphql/types/instance_security_dashboard_type_spec.rb'
- - 'ee/spec/graphql/types/vulnerability_evidence_source_type_spec.rb'
- 'ee/spec/graphql/types/vulnerability_evidence_supporting_message_type_spec.rb'
- 'ee/spec/graphql/types/vulnerability_evidence_type_spec.rb'
- 'ee/spec/graphql/types/vulnerability_request_response_header_type_spec.rb'
@@ -30,7 +11,6 @@ RSpec/ScatteredLet:
- 'ee/spec/helpers/ee/subscribable_banner_helper_spec.rb'
- 'ee/spec/lib/audit/external_status_check_changes_auditor_spec.rb'
- 'ee/spec/lib/ee/api/helpers/members_helpers_spec.rb'
- - 'ee/spec/lib/ee/audit/compliance_framework_changes_auditor_spec.rb'
- 'ee/spec/lib/ee/gitlab/scim/provisioning_service_spec.rb'
- 'ee/spec/lib/gitlab/background_migration/migrate_requirements_to_work_items_spec.rb'
- 'ee/spec/lib/gitlab/ci/parsers/security/dast_spec.rb'
@@ -62,7 +42,6 @@ RSpec/ScatteredLet:
- 'ee/spec/requests/api/graphql/project/dast_profile_schedule_spec.rb'
- 'ee/spec/requests/api/graphql/project/environments_spec.rb'
- 'ee/spec/requests/api/graphql/project/pipeline/dast_profile_spec.rb'
- - 'ee/spec/requests/api/graphql/project/pipeline/security_report_finding_spec.rb'
- 'ee/spec/requests/api/graphql/project/pipeline/security_report_summary_spec.rb'
- 'ee/spec/requests/api/graphql/project/pipelines/dast_profile_spec.rb'
- 'ee/spec/requests/api/graphql/project/requirements_management/requirements_spec.rb'
@@ -83,7 +62,6 @@ RSpec/ScatteredLet:
- 'ee/spec/services/ee/issue_links/create_service_spec.rb'
- 'ee/spec/services/ee/issues/create_service_spec.rb'
- 'ee/spec/services/ee/merge_requests/base_service_spec.rb'
- - 'ee/spec/services/ee/merge_requests/update_assignees_service_spec.rb'
- 'ee/spec/services/ee/merge_requests/update_reviewers_service_spec.rb'
- 'ee/spec/services/epic_issues/create_service_spec.rb'
- 'ee/spec/services/epics/issue_promote_service_spec.rb'
@@ -95,12 +73,10 @@ RSpec/ScatteredLet:
- 'ee/spec/services/incident_management/oncall_rotations/remove_participant_service_spec.rb'
- 'ee/spec/services/members/activate_service_spec.rb'
- 'ee/spec/services/personal_access_tokens/groups/update_lifetime_service_spec.rb'
- - 'ee/spec/services/sbom/ingestion/occurrence_map_spec.rb'
- 'ee/spec/services/security/report_summary_service_spec.rb'
- 'ee/spec/services/vulnerabilities/security_finding/create_issue_service_spec.rb'
- 'ee/spec/views/subscriptions/groups/edit.html.haml_spec.rb'
- 'ee/spec/workers/concerns/update_orchestration_policy_configuration_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_audit_logs_1_spec.rb'
- 'spec/controllers/concerns/metrics_dashboard_spec.rb'
- 'spec/controllers/import/bitbucket_server_controller_spec.rb'
- 'spec/controllers/projects/deploy_keys_controller_spec.rb'
@@ -166,7 +142,6 @@ RSpec/ScatteredLet:
- 'spec/lib/gitlab/ci/yaml_processor_spec.rb'
- 'spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb'
- 'spec/lib/gitlab/database/background_migration/batched_migration_spec.rb'
- - 'spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb'
- 'spec/lib/gitlab/database/partitioning/partition_manager_spec.rb'
- 'spec/lib/gitlab/database/postgresql_adapter/empty_query_ping_spec.rb'
- 'spec/lib/gitlab/database/reindexing/grafana_notifier_spec.rb'
@@ -205,7 +180,6 @@ RSpec/ScatteredLet:
- 'spec/lib/gitlab/tree_summary_spec.rb'
- 'spec/lib/gitlab/usage/metrics/instrumentations/gitlab_for_jira_app_proxy_installations_count_metric_spec.rb'
- 'spec/lib/gitlab/usage_data_metrics_spec.rb'
- - 'spec/lib/gitlab/usage_data_spec.rb'
- 'spec/lib/gitlab/utils/measuring_spec.rb'
- 'spec/lib/gitlab/zentao/client_spec.rb'
- 'spec/lib/peek/views/external_http_spec.rb'
@@ -263,8 +237,6 @@ RSpec/ScatteredLet:
- 'spec/serializers/ci/job_entity_spec.rb'
- 'spec/serializers/merge_requests/pipeline_entity_spec.rb'
- 'spec/services/ci/create_downstream_pipeline_service_spec.rb'
- - 'spec/services/ci/create_pipeline_service/include_spec.rb'
- - 'spec/services/ci/create_pipeline_service/logger_spec.rb'
- 'spec/services/ci/create_pipeline_service/rules_spec.rb'
- 'spec/services/ci/create_pipeline_service_spec.rb'
- 'spec/services/ci/destroy_pipeline_service_spec.rb'
diff --git a/.rubocop_todo/rspec/shared_examples.yml b/.rubocop_todo/rspec/shared_examples.yml
deleted file mode 100644
index 612692bdb55..00000000000
--- a/.rubocop_todo/rspec/shared_examples.yml
+++ /dev/null
@@ -1,24 +0,0 @@
----
-# Cop supports --auto-correct.
-RSpec/SharedExamples:
- Exclude:
- - 'ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb'
- - 'ee/spec/lib/ee/gitlab/email/handler/service_desk_handler_spec.rb'
- - 'ee/spec/lib/gitlab/email/handler/create_note_handler_spec.rb'
- - 'ee/spec/services/approval_rules/params_filtering_service_spec.rb'
- - 'spec/lib/banzai/filter/repository_link_filter_spec.rb'
- - 'spec/lib/gitlab/asciidoc_spec.rb'
- - 'spec/lib/gitlab/email/handler/create_issue_handler_spec.rb'
- - 'spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb'
- - 'spec/lib/gitlab/email/handler/create_note_handler_spec.rb'
- - 'spec/lib/gitlab/email/handler/create_note_on_issuable_handler_spec.rb'
- - 'spec/lib/gitlab/email/handler/service_desk_handler_spec.rb'
- - 'spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb'
- - 'spec/lib/gitlab/email/receiver_spec.rb'
- - 'spec/lib/gitlab/git/tree_spec.rb'
- - 'spec/models/design_management/version_spec.rb'
- - 'spec/models/integrations/drone_ci_spec.rb'
- - 'spec/models/user_spec.rb'
- - 'spec/requests/api/graphql/project/issue/design_collection/version_spec.rb'
- - 'spec/rubocop/cop/performance/readlines_each_spec.rb'
- - 'spec/support/shared_contexts/email_shared_context.rb'
diff --git a/.rubocop_todo/rspec/timecop_freeze.yml b/.rubocop_todo/rspec/timecop_freeze.yml
deleted file mode 100644
index 2967793e42a..00000000000
--- a/.rubocop_todo/rspec/timecop_freeze.yml
+++ /dev/null
@@ -1,24 +0,0 @@
----
-RSpec/TimecopFreeze:
- Exclude:
- - ee/spec/models/merge_train_spec.rb
- - ee/spec/support/shared_contexts/lib/gitlab/insights/reducers/reducers_shared_contexts.rb
- - qa/spec/support/repeater_spec.rb
- - spec/features/users/active_sessions_spec.rb
- - spec/lib/gitlab/analytics/cycle_analytics/base_query_builder_spec.rb
- - spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb
- - spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb
- - spec/lib/gitlab/auth/unique_ips_limiter_spec.rb
- - spec/lib/gitlab/checks/timed_logger_spec.rb
- - spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
- - spec/lib/gitlab/puma_logging/json_formatter_spec.rb
- - spec/lib/json_web_token/hmac_token_spec.rb
- - spec/models/active_session_spec.rb
- - spec/serializers/entity_date_helper_spec.rb
- - spec/support/cycle_analytics_helpers/test_generation.rb
- - spec/support/helpers/cycle_analytics_helpers.rb
- - spec/support/helpers/javascript_fixtures_helpers.rb
- - spec/support/shared_contexts/rack_attack_shared_context.rb
- - spec/support/shared_examples/workers/concerns/reenqueuer_shared_examples.rb
- - spec/workers/concerns/reenqueuer_spec.rb
- - spec/workers/metrics/dashboard/prune_old_annotations_worker_spec.rb
diff --git a/.rubocop_todo/rspec/timecop_travel.yml b/.rubocop_todo/rspec/timecop_travel.yml
deleted file mode 100644
index 3a9ebc443fd..00000000000
--- a/.rubocop_todo/rspec/timecop_travel.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-RSpec/TimecopTravel:
- Exclude:
- - qa/spec/support/repeater_spec.rb
diff --git a/.rubocop_todo/rspec/variable_definition.yml b/.rubocop_todo/rspec/variable_definition.yml
index 797fa763ba1..187064b9bb4 100644
--- a/.rubocop_todo/rspec/variable_definition.yml
+++ b/.rubocop_todo/rspec/variable_definition.yml
@@ -1,6 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
RSpec/VariableDefinition:
Exclude:
- - 'spec/lib/gitlab/usage_data_spec.rb'
- 'spec/presenters/packages/npm/package_presenter_spec.rb'
diff --git a/.rubocop_todo/rspec/verified_doubles.yml b/.rubocop_todo/rspec/verified_doubles.yml
index c9f405a8283..8fa03c775be 100644
--- a/.rubocop_todo/rspec/verified_doubles.yml
+++ b/.rubocop_todo/rspec/verified_doubles.yml
@@ -1,1149 +1,1070 @@
---
RSpec/VerifiedDoubles:
Exclude:
- - ee/spec/controllers/boards/issues_controller_spec.rb
- - ee/spec/controllers/concerns/ee/routable_actions/sso_enforcement_redirect_spec.rb
- - ee/spec/controllers/groups/clusters_controller_spec.rb
- - ee/spec/controllers/groups/sso_controller_spec.rb
- - ee/spec/controllers/oauth/geo_auth_controller_spec.rb
- - ee/spec/controllers/projects/clusters_controller_spec.rb
- - ee/spec/db/production/license_spec.rb
- - ee/spec/elastic/migrate/20210510113500_delete_merge_requests_from_original_index_spec.rb
- - ee/spec/elastic/migrate/20210510143200_delete_notes_from_original_index_spec.rb
- - ee/spec/features/admin/groups/admin_subscription_alerts_spec.rb
- - ee/spec/features/billings/billing_plans_spec.rb
- - ee/spec/features/merge_trains/two_merge_requests_on_train_spec.rb
- - ee/spec/features/profiles/account_spec.rb
- - ee/spec/finders/license_template_finder_spec.rb
- - ee/spec/finders/projects/integrations/jira/issues_finder_spec.rb
- - ee/spec/finders/security/pipeline_vulnerabilities_finder_spec.rb
- - ee/spec/finders/template_finder_spec.rb
- - ee/spec/graphql/ee/resolvers/board_lists_resolver_spec.rb
- - ee/spec/graphql/mutations/dast_scanner_profiles/create_spec.rb
- - ee/spec/graphql/mutations/dast_site_profiles/create_spec.rb
- - ee/spec/graphql/mutations/dast_site_profiles/delete_spec.rb
- - ee/spec/graphql/mutations/dast_site_profiles/update_spec.rb
- - ee/spec/graphql/mutations/incident_management/oncall_rotation/create_spec.rb
- - ee/spec/graphql/mutations/issues/promote_to_epic_spec.rb
- - ee/spec/graphql/mutations/issues/set_epic_spec.rb
- - ee/spec/graphql/mutations/merge_requests/accept_spec.rb
- - ee/spec/graphql/mutations/vulnerabilities/create_external_issue_link_spec.rb
- - ee/spec/graphql/mutations/vulnerabilities/destroy_external_issue_link_spec.rb
- - ee/spec/graphql/resolvers/board_groupings/epics_resolvers_spec.rb
- - ee/spec/graphql/resolvers/external_issue_resolver_spec.rb
- - ee/spec/graphql/resolvers/security_report_summary_resolver_spec.rb
- - ee/spec/graphql/resolvers/vulnerabilities/details_resolver_spec.rb
- - ee/spec/graphql/types/network_policy_type_spec.rb
- - ee/spec/graphql/types/security/training_type_spec.rb
- - ee/spec/helpers/billing_plans_helper_spec.rb
- - ee/spec/helpers/ee/ci/runners_helper_spec.rb
- - ee/spec/helpers/ee/integrations_helper_spec.rb
- - ee/spec/helpers/ee/subscribable_banner_helper_spec.rb
- - ee/spec/helpers/ee/trial_helper_spec.rb
- - ee/spec/helpers/ee/trial_registration_helper_spec.rb
- - ee/spec/helpers/kerberos_helper_spec.rb
- - ee/spec/helpers/license_helper_spec.rb
- - ee/spec/helpers/roadmaps_helper_spec.rb
- - ee/spec/helpers/routing/pseudonymization_helper_spec.rb
- - ee/spec/helpers/subscriptions_helper_spec.rb
- - ee/spec/helpers/timeboxes_helper_spec.rb
- - ee/spec/helpers/vulnerabilities_helper_spec.rb
- - ee/spec/lib/ee/backup/repositories_spec.rb
- - ee/spec/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules_in_batch_spec.rb
- - ee/spec/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules_spec.rb
- - ee/spec/lib/ee/gitlab/ci/matching/runner_matcher_spec.rb
- - ee/spec/lib/ee/gitlab/ci/pipeline/quota/size_spec.rb
- - ee/spec/lib/ee/gitlab/etag_caching/router/rails_spec.rb
- - ee/spec/lib/ee/gitlab/gon_helper_spec.rb
- - ee/spec/lib/elastic/latest/config_shared_examples.rb
- - ee/spec/lib/elastic/latest/git_instance_proxy_spec.rb
- - ee/spec/lib/elastic/migration_spec.rb
- - ee/spec/lib/elastic/multi_version_class_proxy_spec.rb
- - ee/spec/lib/elastic/multi_version_instance_proxy_spec.rb
- - ee/spec/lib/gitlab/audit/target_spec.rb
- - ee/spec/lib/gitlab/auth/group_saml/response_check_spec.rb
- - ee/spec/lib/gitlab/auth/group_saml/token_actor_spec.rb
- - ee/spec/lib/gitlab/auth/otp/session_enforcer_spec.rb
- - ee/spec/lib/gitlab/authority_analyzer_spec.rb
- - ee/spec/lib/gitlab/cache_spec.rb
- - ee/spec/lib/gitlab/ci/pipeline/chain/limit/activity_spec.rb
- - ee/spec/lib/gitlab/ci/pipeline/chain/limit/size_spec.rb
- - ee/spec/lib/gitlab/code_owners/groups_loader_spec.rb
- - ee/spec/lib/gitlab/code_owners/users_loader_spec.rb
- - ee/spec/lib/gitlab/custom_file_templates_spec.rb
- - ee/spec/lib/gitlab/elastic/client_spec.rb
- - ee/spec/lib/gitlab/elastic/search_results_spec.rb
- - ee/spec/lib/gitlab/expiring_subscription_message_spec.rb
- - ee/spec/lib/gitlab/geo/git_ssh_proxy_spec.rb
- - ee/spec/lib/gitlab/geo/log_cursor/lease_spec.rb
- - ee/spec/lib/gitlab/geo/oauth/logout_token_spec.rb
- - ee/spec/lib/gitlab/geo/oauth/session_spec.rb
- - ee/spec/lib/gitlab/geo/replicator_spec.rb
- - ee/spec/lib/gitlab/geo_spec.rb
- - ee/spec/lib/gitlab/git_access_spec.rb
- - ee/spec/lib/gitlab/graphql/aggregations/issues/lazy_links_aggregate_spec.rb
- - ee/spec/lib/gitlab/import_export/group/relation_factory_spec.rb
- - ee/spec/lib/gitlab/middleware/ip_restrictor_spec.rb
- - ee/spec/lib/gitlab/prometheus/queries/cluster_query_spec.rb
- - ee/spec/lib/gitlab/subscription_portal/clients/rest_spec.rb
- - ee/spec/lib/sidebars/groups/menus/analytics_menu_spec.rb
- - ee/spec/lib/system_check/geo/geo_database_configured_check_spec.rb
- - ee/spec/models/app_sec/fuzzing/api/ci_configuration_spec.rb
- - ee/spec/models/concerns/geo/verification_state_spec.rb
- - ee/spec/models/ee/ci/job_artifact_spec.rb
- - ee/spec/models/ee/user_spec.rb
- - ee/spec/models/elastic/index_setting_spec.rb
- - ee/spec/models/geo/project_registry_spec.rb
- - ee/spec/models/geo/secondary_usage_data_spec.rb
- - ee/spec/models/geo_node_status_spec.rb
- - ee/spec/models/integrations/github/status_message_spec.rb
- - ee/spec/models/integrations/github_spec.rb
- - ee/spec/models/project_spec.rb
- - ee/spec/models/push_rule_spec.rb
- - ee/spec/presenters/audit_event_presenter_spec.rb
- - ee/spec/presenters/group_member_presenter_spec.rb
- - ee/spec/presenters/merge_request_approver_presenter_spec.rb
- - ee/spec/presenters/project_member_presenter_spec.rb
- - ee/spec/requests/api/geo_spec.rb
- - ee/spec/requests/api/graphql/mutations/dast_site_profiles/delete_spec.rb
- - ee/spec/requests/api/internal/base_spec.rb
- - ee/spec/requests/api/ldap_spec.rb
- - ee/spec/requests/api/visual_review_discussions_spec.rb
- - ee/spec/requests/api/vulnerability_findings_spec.rb
- - ee/spec/requests/callout_spec.rb
- - ee/spec/requests/rack_attack_spec.rb
- - ee/spec/serializers/blocking_merge_request_entity_spec.rb
- - ee/spec/serializers/clusters/environment_entity_spec.rb
- - ee/spec/serializers/dashboard_operations_project_entity_spec.rb
- - ee/spec/serializers/dependency_entity_spec.rb
- - ee/spec/serializers/ee/admin/user_entity_spec.rb
- - ee/spec/serializers/ee/build_details_entity_spec.rb
- - ee/spec/serializers/ee/ci/job_entity_spec.rb
- - ee/spec/serializers/ee/ci/pipeline_entity_spec.rb
- - ee/spec/serializers/ee/group_child_entity_spec.rb
- - ee/spec/serializers/ee/issue_board_entity_spec.rb
- - ee/spec/serializers/ee/issue_entity_spec.rb
- - ee/spec/serializers/ee/issue_sidebar_extras_entity_spec.rb
- - ee/spec/serializers/ee/merge_request_poll_cached_widget_entity_spec.rb
- - ee/spec/serializers/ee/note_entity_spec.rb
- - ee/spec/serializers/environment_entity_spec.rb
- - ee/spec/serializers/epic_entity_spec.rb
- - ee/spec/serializers/epic_note_entity_spec.rb
- - ee/spec/serializers/integrations/jira_serializers/issue_detail_entity_spec.rb
- - ee/spec/serializers/integrations/jira_serializers/issue_entity_spec.rb
- - ee/spec/serializers/issuable_sidebar_extras_entity_spec.rb
- - ee/spec/serializers/issues/linked_issue_feature_flag_entity_spec.rb
- - ee/spec/serializers/linked_feature_flag_issue_entity_spec.rb
- - ee/spec/serializers/merge_request_poll_widget_entity_spec.rb
- - ee/spec/serializers/merge_request_sidebar_basic_entity_spec.rb
- - ee/spec/serializers/merge_request_widget_entity_spec.rb
- - ee/spec/serializers/test_reports_comparer_serializer_spec.rb
- - ee/spec/serializers/user_analytics_entity_spec.rb
- - ee/spec/serializers/vulnerabilities/feedback_entity_spec.rb
- - ee/spec/serializers/vulnerabilities/finding_entity_spec.rb
- - ee/spec/serializers/vulnerabilities/finding_reports_comparer_entity_spec.rb
- - ee/spec/serializers/vulnerabilities/finding_serializer_spec.rb
- - ee/spec/serializers/vulnerability_note_entity_spec.rb
- - ee/spec/services/alert_management/extract_alert_payload_fields_service_spec.rb
- - ee/spec/services/app_sec/dast/scans/create_service_spec.rb
- - ee/spec/services/app_sec/dast/site_profiles/update_service_spec.rb
- - ee/spec/services/app_sec/fuzzing/api/ci_configuration_create_service_spec.rb
- - ee/spec/services/ci/create_pipeline_service/dast_configuration_spec.rb
- - ee/spec/services/ci/minutes/track_live_consumption_service_spec.rb
- - ee/spec/services/ci/minutes/update_project_and_namespace_usage_service_spec.rb
- - ee/spec/services/ci/pipeline_creation/drop_not_runnable_builds_service_spec.rb
- - ee/spec/services/ci/pipeline_creation/start_pipeline_service_spec.rb
- - ee/spec/services/dashboard/operations/list_service_spec.rb
- - ee/spec/services/dashboard/projects/create_service_spec.rb
- - ee/spec/services/ee/ci/job_artifacts/destroy_all_expired_service_spec.rb
- - ee/spec/services/ee/ci/job_artifacts/destroy_batch_service_spec.rb
- - ee/spec/services/ee/issues/create_service_spec.rb
- - ee/spec/services/ee/issues/update_service_spec.rb
- - ee/spec/services/ee/merge_requests/base_service_spec.rb
- - ee/spec/services/ee/merge_requests/post_merge_service_spec.rb
- - ee/spec/services/ee/merge_requests/refresh_service_spec.rb
- - ee/spec/services/ee/notification_service_spec.rb
- - ee/spec/services/ee/post_receive_service_spec.rb
- - ee/spec/services/geo/blob_download_service_spec.rb
- - ee/spec/services/geo/graphql_request_service_spec.rb
- - ee/spec/services/geo/node_status_request_service_spec.rb
- - ee/spec/services/geo/replication_toggle_request_service_spec.rb
- - ee/spec/services/geo/repository_verification_primary_service_spec.rb
- - ee/spec/services/gitlab_subscriptions/fetch_subscription_plans_service_spec.rb
- - ee/spec/services/group_saml/group_managed_accounts/clean_up_members_service_spec.rb
- - ee/spec/services/group_saml/sign_up_service_spec.rb
- - ee/spec/services/groups/update_repository_storage_service_spec.rb
- - ee/spec/services/ide/schemas_config_service_spec.rb
- - ee/spec/services/incident_management/oncall_schedules/update_service_spec.rb
- - ee/spec/services/incident_management/pending_escalations/process_service_spec.rb
- - ee/spec/services/merge_requests/approval_service_spec.rb
- - ee/spec/services/merge_requests/build_service_spec.rb
- - ee/spec/services/merge_requests/reset_approvals_service_spec.rb
- - ee/spec/services/namespaces/in_product_marketing_emails_service_spec.rb
- - ee/spec/services/projects/update_mirror_service_spec.rb
- - ee/spec/services/projects/update_pages_service_spec.rb
- - ee/spec/services/security/ingestion/ingest_report_slice_service_spec.rb
- - ee/spec/services/security/orchestration/assign_service_spec.rb
- - ee/spec/services/security/security_orchestration_policies/on_demand_scan_pipeline_configuration_service_spec.rb
- - ee/spec/services/slash_commands/global_slack_handler_spec.rb
- - ee/spec/services/status_page/publish_details_service_spec.rb
- - ee/spec/services/status_page/publish_service_spec.rb
- - ee/spec/services/status_page/trigger_publish_service_spec.rb
- - ee/spec/services/system_note_service_spec.rb
- - ee/spec/services/vulnerability_exports/export_service_spec.rb
- - ee/spec/services/vulnerability_external_issue_links/create_service_spec.rb
- - ee/spec/support/helpers/ee/ldap_helpers.rb
- - ee/spec/support/prometheus/additional_metrics_shared_examples.rb
- - ee/spec/support/shared_examples/controllers/analytics/cycle_analytics/shared_stage_shared_examples.rb
- - ee/spec/support/shared_examples/controllers/cluster_metrics_shared_examples.rb
- - ee/spec/support/shared_examples/models/concerns/blob_replicator_strategy_shared_examples.rb
- - ee/spec/support/shared_examples/models/concerns/verifiable_replicator_shared_examples.rb
- - ee/spec/support/shared_examples/models/geo_verifiable_registry_shared_examples.rb
- - ee/spec/support/shared_examples/serializers/report_status_shared_examples.rb
- - ee/spec/support/shared_examples/services/alert_management/alert_processing/oncall_notifications_shared_examples.rb
- - ee/spec/support/shared_examples/services/base_sync_service_shared_examples.rb
- - ee/spec/support/shared_examples/services/geo/geo_request_service_shared_examples.rb
- - ee/spec/support/shared_examples/status_page/reference_links_examples.rb
- - ee/spec/validators/json_schema_validator_spec.rb
- - ee/spec/views/layouts/header/_ee_subscribable_banner.html.haml_spec.rb
- - ee/spec/workers/ci/sync_reports_to_report_approval_rules_worker_spec.rb
- - ee/spec/workers/geo/container_repository_sync_worker_spec.rb
- - ee/spec/workers/compliance_management/chain_of_custody_report_worker_spec.rb
- - ee/spec/workers/geo/design_repository_sync_worker_spec.rb
- - ee/spec/workers/geo/destroy_worker_spec.rb
- - ee/spec/workers/geo/event_worker_spec.rb
- - ee/spec/workers/geo/metrics_update_worker_spec.rb
- - ee/spec/workers/geo/prune_event_log_worker_spec.rb
- - ee/spec/workers/geo/registry_sync_worker_spec.rb
- - ee/spec/workers/geo/reverification_batch_worker_spec.rb
- - ee/spec/workers/geo/sidekiq_cron_config_worker_spec.rb
- - ee/spec/workers/geo/sync_timeout_cron_worker_spec.rb
- - ee/spec/workers/geo/verification_batch_worker_spec.rb
- - ee/spec/workers/geo/verification_cron_worker_spec.rb
- - ee/spec/workers/geo/verification_timeout_worker_spec.rb
- - ee/spec/workers/geo/verification_worker_spec.rb
- - ee/spec/workers/iterations/cadences/create_iterations_worker_spec.rb
- - ee/spec/workers/iterations/roll_over_issues_worker_spec.rb
- - ee/spec/workers/ldap_group_sync_worker_spec.rb
- - ee/spec/workers/merge_request_reset_approvals_worker_spec.rb
- - ee/spec/workers/new_epic_worker_spec.rb
- - ee/spec/workers/update_max_seats_used_for_gitlab_com_subscriptions_worker_spec.rb
- - qa/spec/git/repository_spec.rb
- - qa/spec/page/base_spec.rb
- - qa/spec/page/validator_spec.rb
- - qa/spec/page/view_spec.rb
- - qa/spec/resource/api_fabricator_spec.rb
- - qa/spec/resource/base_spec.rb
- - qa/spec/runtime/application_settings_spec.rb
- - qa/spec/runtime/feature_spec.rb
- - qa/spec/runtime/release_spec.rb
- - qa/spec/scenario/template_spec.rb
- - qa/spec/scenario/test/integration/github_spec.rb
- - qa/spec/scenario/test/sanity/selectors_spec.rb
- - qa/spec/specs/allure_report_spec.rb
- - qa/spec/support/formatters/allure_metadata_formatter_spec.rb
- - qa/spec/support/page_error_checker_spec.rb
- - qa/spec/support/run_spec.rb
- - qa/spec/tools/long_running_spec_reporter_spec.rb
- - spec/benchmarks/banzai_benchmark.rb
- - spec/bin/feature_flag_spec.rb
- - spec/controllers/admin/clusters_controller_spec.rb
- - spec/controllers/application_controller_spec.rb
- - spec/controllers/boards/issues_controller_spec.rb
- - spec/controllers/boards/lists_controller_spec.rb
- - spec/controllers/concerns/checks_collaboration_spec.rb
- - spec/controllers/concerns/import_url_params_spec.rb
- - spec/controllers/concerns/issuable_actions_spec.rb
- - spec/controllers/concerns/issuable_collections_spec.rb
- - spec/controllers/concerns/page_limiter_spec.rb
- - spec/controllers/concerns/spammable_actions/akismet_mark_as_spam_action_spec.rb
- - spec/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support_spec.rb
- - spec/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support_spec.rb
- - spec/controllers/dashboard/snippets_controller_spec.rb
- - spec/controllers/explore/projects_controller_spec.rb
- - spec/controllers/groups/clusters_controller_spec.rb
- - spec/controllers/groups/dependency_proxy_auth_controller_spec.rb
- - spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb
- - spec/controllers/help_controller_spec.rb
- - spec/controllers/import/bitbucket_controller_spec.rb
- - spec/controllers/import/bitbucket_server_controller_spec.rb
- - spec/controllers/import/bulk_imports_controller_spec.rb
- - spec/controllers/import/fogbugz_controller_spec.rb
- - spec/controllers/import/gitea_controller_spec.rb
- - spec/controllers/import/github_controller_spec.rb
- - spec/controllers/import/gitlab_controller_spec.rb
- - spec/controllers/oauth/jira_dvcs/authorizations_controller_spec.rb
- - spec/controllers/omniauth_callbacks_controller_spec.rb
- - spec/controllers/profiles/two_factor_auths_controller_spec.rb
- - spec/controllers/projects/blob_controller_spec.rb
- - spec/controllers/projects/branches_controller_spec.rb
- - spec/controllers/projects/clusters_controller_spec.rb
- - spec/controllers/projects/import/jira_controller_spec.rb
- - spec/controllers/projects/jobs_controller_spec.rb
- - spec/controllers/projects/merge_requests/diffs_controller_spec.rb
- - spec/controllers/projects/merge_requests_controller_spec.rb
- - spec/controllers/projects/notes_controller_spec.rb
- - spec/controllers/projects/pages_controller_spec.rb
- - spec/controllers/projects/performance_monitoring/dashboards_controller_spec.rb
- - spec/controllers/projects/pipelines_controller_spec.rb
- - spec/controllers/projects/prometheus/metrics_controller_spec.rb
- - spec/controllers/projects/registry/tags_controller_spec.rb
- - spec/controllers/projects/settings/operations_controller_spec.rb
- - spec/controllers/projects/snippets_controller_spec.rb
- - spec/controllers/sessions_controller_spec.rb
- - spec/dependencies/omniauth_saml_spec.rb
- - spec/experiments/concerns/project_commit_count_spec.rb
- - spec/factories/ci/job_artifacts.rb
- - spec/factories/clusters/applications/helm.rb
- - spec/features/admin/admin_system_info_spec.rb
- - spec/features/clusters/create_agent_spec.rb
- - spec/features/file_uploads/maven_package_spec.rb
- - spec/features/groups/container_registry_spec.rb
- - spec/features/help_pages_spec.rb
- - spec/features/issuables/markdown_references/jira_spec.rb
- - spec/features/markdown/markdown_spec.rb
- - spec/features/profiles/personal_access_tokens_spec.rb
- - spec/features/projects/clusters/gcp_spec.rb
- - spec/features/projects/clusters_spec.rb
- - spec/features/projects/container_registry_spec.rb
- - spec/features/projects/integrations/user_activates_jira_spec.rb
- - spec/finders/ci/auth_job_finder_spec.rb
- - spec/finders/merge_requests/oldest_per_commit_finder_spec.rb
- - spec/finders/repositories/changelog_commits_finder_spec.rb
- - spec/finders/repositories/changelog_tag_finder_spec.rb
- - spec/graphql/features/authorization_spec.rb
- - spec/graphql/features/feature_flag_spec.rb
- - spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb
- - spec/graphql/mutations/alert_management/update_alert_status_spec.rb
- - spec/graphql/mutations/branches/create_spec.rb
- - spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb
- - spec/graphql/mutations/ci/job_token_scope/remove_project_spec.rb
- - spec/graphql/mutations/concerns/mutations/finds_by_gid_spec.rb
- - spec/graphql/mutations/design_management/upload_spec.rb
- - spec/graphql/mutations/environments/canary_ingress/update_spec.rb
- - spec/graphql/mutations/merge_requests/accept_spec.rb
- - spec/graphql/mutations/merge_requests/create_spec.rb
- - spec/graphql/resolvers/design_management/versions_resolver_spec.rb
- - spec/graphql/resolvers/kas/agent_connections_resolver_spec.rb
- - spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb
- - spec/graphql/types/ci/detailed_status_type_spec.rb
- - spec/graphql/types/ci/status_action_type_spec.rb
- - spec/graphql/types/kas/agent_connection_type_spec.rb
- - spec/graphql/types/permission_types/base_permission_type_spec.rb
- - spec/graphql/types/project_type_spec.rb
- - spec/graphql/types/range_input_type_spec.rb
- - spec/helpers/blame_helper_spec.rb
- - spec/helpers/blob_helper_spec.rb
- - spec/helpers/ci/pipelines_helper_spec.rb
- - spec/helpers/ci/status_helper_spec.rb
- - spec/helpers/ci/triggers_helper_spec.rb
- - spec/helpers/commits_helper_spec.rb
- - spec/helpers/dev_ops_report_helper_spec.rb
- - spec/helpers/diff_helper_spec.rb
- - spec/helpers/emails_helper_spec.rb
- - spec/helpers/environments_helper_spec.rb
- - spec/helpers/form_helper_spec.rb
- - spec/helpers/gitlab_routing_helper_spec.rb
- - spec/helpers/integrations_helper_spec.rb
- - spec/helpers/issuables_helper_spec.rb
- - spec/helpers/markup_helper_spec.rb
- - spec/helpers/merge_requests_helper_spec.rb
- - spec/helpers/notes_helper_spec.rb
- - spec/helpers/numbers_helper_spec.rb
- - spec/helpers/preferences_helper_spec.rb
- - spec/helpers/projects_helper_spec.rb
- - spec/helpers/routing/pseudonymization_helper_spec.rb
- - spec/helpers/sorting_helper_spec.rb
- - spec/helpers/submodule_helper_spec.rb
- - spec/helpers/todos_helper_spec.rb
- - spec/helpers/tree_helper_spec.rb
- - spec/helpers/version_check_helper_spec.rb
- - spec/initializers/doorkeeper_spec.rb
- - spec/initializers/global_id_spec.rb
- - spec/initializers/hangouts_chat_http_override_spec.rb
- - spec/lib/api/base_spec.rb
- - spec/lib/api/entities/ci/job_request/image_spec.rb
- - spec/lib/api/entities/ci/job_request/port_spec.rb
- - spec/lib/api/helpers/authentication_spec.rb
- - spec/lib/api/helpers/caching_spec.rb
- - spec/lib/api/helpers/graphql_helpers_spec.rb
- - spec/lib/api/helpers/pagination_spec.rb
- - spec/lib/api/helpers/pagination_strategies_spec.rb
- - spec/lib/api/helpers/variables_helpers_spec.rb
- - spec/lib/api/helpers_spec.rb
- - spec/lib/atlassian/jira_connect/client_spec.rb
- - spec/lib/backup/files_spec.rb
- - spec/lib/backup/repositories_spec.rb
- - spec/lib/banzai/cross_project_reference_spec.rb
- - spec/lib/banzai/filter/gollum_tags_filter_spec.rb
- - spec/lib/banzai/filter/repository_link_filter_spec.rb
- - spec/lib/banzai/pipeline/wiki_pipeline_spec.rb
- - spec/lib/banzai/querying_spec.rb
- - spec/lib/banzai/reference_parser/base_parser_spec.rb
- - spec/lib/banzai/reference_parser/commit_parser_spec.rb
- - spec/lib/banzai/reference_parser/commit_range_parser_spec.rb
- - spec/lib/banzai/render_context_spec.rb
- - spec/lib/banzai/renderer_spec.rb
- - spec/lib/bitbucket/connection_spec.rb
- - spec/lib/bitbucket/paginator_spec.rb
- - spec/lib/bitbucket_server/paginator_spec.rb
- - spec/lib/bulk_imports/clients/http_spec.rb
- - spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb
- - spec/lib/bulk_imports/common/extractors/rest_extractor_spec.rb
- - spec/lib/bulk_imports/ndjson_pipeline_spec.rb
- - spec/lib/bulk_imports/network_error_spec.rb
- - spec/lib/bulk_imports/projects/pipelines/snippets_repository_pipeline_spec.rb
- - spec/lib/bulk_imports/projects/transformers/project_attributes_transformer_spec.rb
- - spec/lib/constraints/admin_constrainer_spec.rb
- - spec/lib/constraints/group_url_constrainer_spec.rb
- - spec/lib/constraints/jira_encoded_url_constrainer_spec.rb
- - spec/lib/constraints/project_url_constrainer_spec.rb
- - spec/lib/constraints/user_url_constrainer_spec.rb
- - spec/lib/csv_builder_spec.rb
- - spec/lib/csv_builders/stream_spec.rb
- - spec/lib/extracts_path_spec.rb
- - spec/lib/feature_spec.rb
- - spec/lib/gitaly/server_spec.rb
- - spec/lib/gitlab/api_authentication/token_locator_spec.rb
- - spec/lib/gitlab/application_context_spec.rb
- - spec/lib/gitlab/application_rate_limiter_spec.rb
- - spec/lib/gitlab/asciidoc/include_processor_spec.rb
- - spec/lib/gitlab/auth/auth_finders_spec.rb
- - spec/lib/gitlab/auth/blocked_user_tracker_spec.rb
- - spec/lib/gitlab/auth/ldap/adapter_spec.rb
- - spec/lib/gitlab/auth/ldap/authentication_spec.rb
- - spec/lib/gitlab/authorized_keys_spec.rb
- - spec/lib/gitlab/avatar_cache_spec.rb
- - spec/lib/gitlab/background_migration/base_job_spec.rb
- - spec/lib/gitlab/background_migration/batching_strategies/base_strategy_spec.rb
- - spec/lib/gitlab/background_migration/fix_merge_request_diff_commit_users_spec.rb
- - spec/lib/gitlab/background_migration/job_coordinator_spec.rb
- - spec/lib/gitlab/background_migration/migrate_merge_request_diff_commit_users_spec.rb
- - spec/lib/gitlab/background_migration_spec.rb
- - spec/lib/gitlab/bitbucket_import/importer_spec.rb
- - spec/lib/gitlab/bitbucket_import/project_creator_spec.rb
- - spec/lib/gitlab/bitbucket_server_import/importer_spec.rb
- - spec/lib/gitlab/cache/import/caching_spec.rb
- - spec/lib/gitlab/changelog/committer_spec.rb
- - spec/lib/gitlab/chat/responder/base_spec.rb
- - spec/lib/gitlab/chat/responder_spec.rb
- - spec/lib/gitlab/ci/badge/coverage/metadata_spec.rb
- - spec/lib/gitlab/ci/badge/coverage/template_spec.rb
- - spec/lib/gitlab/ci/badge/pipeline/metadata_spec.rb
- - spec/lib/gitlab/ci/badge/pipeline/template_spec.rb
- - spec/lib/gitlab/ci/build/cache_spec.rb
- - spec/lib/gitlab/ci/build/policy/changes_spec.rb
- - spec/lib/gitlab/ci/build/policy/variables_spec.rb
- - spec/lib/gitlab/ci/build/policy_spec.rb
- - spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
- - spec/lib/gitlab/ci/build/rules/rule_spec.rb
- - spec/lib/gitlab/ci/build/rules_spec.rb
- - spec/lib/gitlab/ci/build/status/reason_spec.rb
- - spec/lib/gitlab/ci/config/entry/default_spec.rb
- - spec/lib/gitlab/ci/config/entry/job_spec.rb
- - spec/lib/gitlab/ci/config/entry/processable_spec.rb
- - spec/lib/gitlab/ci/config/external/context_spec.rb
- - spec/lib/gitlab/ci/config/external/file/local_spec.rb
- - spec/lib/gitlab/ci/config/external/file/project_spec.rb
- - spec/lib/gitlab/ci/config/external/rules_spec.rb
- - spec/lib/gitlab/ci/parsers/test/junit_spec.rb
- - spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb
- - spec/lib/gitlab/ci/pipeline/chain/helpers_spec.rb
- - spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb
- - spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb
- - spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
- - spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb
- - spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb
- - spec/lib/gitlab/ci/pipeline/expression/lexeme/and_spec.rb
- - spec/lib/gitlab/ci/pipeline/expression/lexeme/equals_spec.rb
- - spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb
- - spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb
- - spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb
- - spec/lib/gitlab/ci/pipeline/expression/lexeme/or_spec.rb
- - spec/lib/gitlab/ci/pipeline/preloader_spec.rb
- - spec/lib/gitlab/ci/pipeline/quota/deployments_spec.rb
- - spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
- - spec/lib/gitlab/ci/reports/security/report_spec.rb
- - spec/lib/gitlab/ci/status/build/action_spec.rb
- - spec/lib/gitlab/ci/status/build/cancelable_spec.rb
- - spec/lib/gitlab/ci/status/build/canceled_spec.rb
- - spec/lib/gitlab/ci/status/build/created_spec.rb
- - spec/lib/gitlab/ci/status/build/erased_spec.rb
- - spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb
- - spec/lib/gitlab/ci/status/build/failed_spec.rb
- - spec/lib/gitlab/ci/status/build/pending_spec.rb
- - spec/lib/gitlab/ci/status/build/preparing_spec.rb
- - spec/lib/gitlab/ci/status/build/retried_spec.rb
- - spec/lib/gitlab/ci/status/build/retryable_spec.rb
- - spec/lib/gitlab/ci/status/build/skipped_spec.rb
- - spec/lib/gitlab/ci/status/build/stop_spec.rb
- - spec/lib/gitlab/ci/status/build/unschedule_spec.rb
- - spec/lib/gitlab/ci/status/canceled_spec.rb
- - spec/lib/gitlab/ci/status/core_spec.rb
- - spec/lib/gitlab/ci/status/created_spec.rb
- - spec/lib/gitlab/ci/status/factory_spec.rb
- - spec/lib/gitlab/ci/status/failed_spec.rb
- - spec/lib/gitlab/ci/status/manual_spec.rb
- - spec/lib/gitlab/ci/status/pending_spec.rb
- - spec/lib/gitlab/ci/status/pipeline/blocked_spec.rb
- - spec/lib/gitlab/ci/status/pipeline/delayed_spec.rb
- - spec/lib/gitlab/ci/status/preparing_spec.rb
- - spec/lib/gitlab/ci/status/running_spec.rb
- - spec/lib/gitlab/ci/status/scheduled_spec.rb
- - spec/lib/gitlab/ci/status/skipped_spec.rb
- - spec/lib/gitlab/ci/status/stage/play_manual_spec.rb
- - spec/lib/gitlab/ci/status/success_spec.rb
- - spec/lib/gitlab/ci/status/success_warning_spec.rb
- - spec/lib/gitlab/ci/status/waiting_for_resource_spec.rb
- - spec/lib/gitlab/ci/trace/archive_spec.rb
- - spec/lib/gitlab/ci/trace/remote_checksum_spec.rb
- - spec/lib/gitlab/ci/trace/stream_spec.rb
- - spec/lib/gitlab/ci/variables/builder_spec.rb
- - spec/lib/gitlab/cleanup/orphan_lfs_file_references_spec.rb
- - spec/lib/gitlab/cleanup/project_uploads_spec.rb
- - spec/lib/gitlab/cleanup/remote_uploads_spec.rb
- - spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb
- - spec/lib/gitlab/cluster/rack_timeout_observer_spec.rb
- - spec/lib/gitlab/color_schemes_spec.rb
- - spec/lib/gitlab/conan_token_spec.rb
- - spec/lib/gitlab/config/entry/configurable_spec.rb
- - spec/lib/gitlab/config/entry/factory_spec.rb
- - spec/lib/gitlab/config/entry/simplifiable_spec.rb
- - spec/lib/gitlab/config/entry/unspecified_spec.rb
- - spec/lib/gitlab/config/entry/validator_spec.rb
- - spec/lib/gitlab/conflict/file_spec.rb
- - spec/lib/gitlab/cross_project_access/check_collection_spec.rb
- - spec/lib/gitlab/database/async_indexes_spec.rb
- - spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb
- - spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
- - spec/lib/gitlab/database/batch_count_spec.rb
- - spec/lib/gitlab/database/count_spec.rb
- - spec/lib/gitlab/database/each_database_spec.rb
- - spec/lib/gitlab/database/load_balancing/configuration_spec.rb
- - spec/lib/gitlab/database/load_balancing/connection_proxy_spec.rb
- - spec/lib/gitlab/database/load_balancing/host_list_spec.rb
- - spec/lib/gitlab/database/load_balancing/host_spec.rb
- - spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb
- - spec/lib/gitlab/database/load_balancing/rack_middleware_spec.rb
- - spec/lib/gitlab/database/load_balancing/resolver_spec.rb
- - spec/lib/gitlab/database/load_balancing/service_discovery_spec.rb
- - spec/lib/gitlab/database/load_balancing/setup_spec.rb
- - spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb
- - spec/lib/gitlab/database/load_balancing_spec.rb
- - spec/lib/gitlab/database/migration_helpers_spec.rb
- - spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
- - spec/lib/gitlab/database/migrations/instrumentation_spec.rb
- - spec/lib/gitlab/database/migrations/lock_retry_mixin_spec.rb
- - spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb
- - spec/lib/gitlab/database/migrations/observers/total_database_size_change_spec.rb
- - spec/lib/gitlab/database/migrations/runner_spec.rb
- - spec/lib/gitlab/database/partitioning/monthly_strategy_spec.rb
- - spec/lib/gitlab/database/partitioning/partition_manager_spec.rb
- - spec/lib/gitlab/database/partitioning/partition_monitoring_spec.rb
- - spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb
- - spec/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table_spec.rb
- - spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb
- - spec/lib/gitlab/database/partitioning_spec.rb
- - spec/lib/gitlab/database/postgresql_adapter/empty_query_ping_spec.rb
- - spec/lib/gitlab/database/postgresql_database_tasks/load_schema_versions_mixin_spec.rb
- - spec/lib/gitlab/database/query_analyzer_spec.rb
- - spec/lib/gitlab/database/reindexing/grafana_notifier_spec.rb
- - spec/lib/gitlab/database/reindexing/reindex_concurrently_spec.rb
- - spec/lib/gitlab/database/shared_model_spec.rb
- - spec/lib/gitlab/database_spec.rb
- - spec/lib/gitlab/diff/file_collection_sorter_spec.rb
- - spec/lib/gitlab/diff/file_spec.rb
- - spec/lib/gitlab/diff/line_spec.rb
- - spec/lib/gitlab/diff/position_tracer_spec.rb
- - spec/lib/gitlab/doctor/secrets_spec.rb
- - spec/lib/gitlab/email/handler/service_desk_handler_spec.rb
- - spec/lib/gitlab/email/receiver_spec.rb
- - spec/lib/gitlab/email/service_desk_receiver_spec.rb
- - spec/lib/gitlab/error_tracking/processor/sidekiq_processor_spec.rb
- - spec/lib/gitlab/error_tracking_spec.rb
- - spec/lib/gitlab/etag_caching/middleware_spec.rb
- - spec/lib/gitlab/etag_caching/router/graphql_spec.rb
- - spec/lib/gitlab/etag_caching/router/rails_spec.rb
- - spec/lib/gitlab/etag_caching/router_spec.rb
- - spec/lib/gitlab/event_store/store_spec.rb
- - spec/lib/gitlab/experiment/rollout/feature_spec.rb
- - spec/lib/gitlab/experimentation/controller_concern_spec.rb
- - spec/lib/gitlab/experimentation/experiment_spec.rb
- - spec/lib/gitlab/experimentation_spec.rb
- - spec/lib/gitlab/external_authorization/access_spec.rb
- - spec/lib/gitlab/external_authorization/logger_spec.rb
- - spec/lib/gitlab/faraday/error_callback_spec.rb
- - spec/lib/gitlab/feature_categories_spec.rb
- - spec/lib/gitlab/git/blob_spec.rb
- - spec/lib/gitlab/git/commit_spec.rb
- - spec/lib/gitlab/git/repository_spec.rb
- - spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb
- - spec/lib/gitlab/git/tag_spec.rb
- - spec/lib/gitlab/git_access_snippet_spec.rb
- - spec/lib/gitlab/gitaly_client/commit_service_spec.rb
- - spec/lib/gitlab/gitaly_client/conflict_files_stitcher_spec.rb
- - spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb
- - spec/lib/gitlab/gitaly_client/health_check_service_spec.rb
- - spec/lib/gitlab/gitaly_client/ref_service_spec.rb
- - spec/lib/gitlab/gitaly_client/remote_service_spec.rb
- - spec/lib/gitlab/gitaly_client/repository_service_spec.rb
- - spec/lib/gitlab/gitaly_client_spec.rb
- - spec/lib/gitlab/github_import/bulk_importing_spec.rb
- - spec/lib/gitlab/github_import/client_spec.rb
- - spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/diff_notes_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/issue_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/issues_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/labels_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/note_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/notes_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/pull_request_merged_by_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/pull_request_review_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/pull_requests_merged_by_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/pull_requests_reviews_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/releases_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/single_endpoint_diff_notes_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/single_endpoint_issue_notes_importer_spec.rb
- - spec/lib/gitlab/github_import/importer/single_endpoint_merge_request_notes_importer_spec.rb
- - spec/lib/gitlab/github_import/issuable_finder_spec.rb
- - spec/lib/gitlab/github_import/markdown_text_spec.rb
- - spec/lib/gitlab/github_import/milestone_finder_spec.rb
- - spec/lib/gitlab/github_import/object_counter_spec.rb
- - spec/lib/gitlab/github_import/page_counter_spec.rb
- - spec/lib/gitlab/github_import/parallel_importer_spec.rb
- - spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
- - spec/lib/gitlab/github_import/representation/diff_note_spec.rb
- - spec/lib/gitlab/github_import/representation/issue_spec.rb
- - spec/lib/gitlab/github_import/representation/note_spec.rb
- - spec/lib/gitlab/github_import/representation/pull_request_review_spec.rb
- - spec/lib/gitlab/github_import/representation/pull_request_spec.rb
- - spec/lib/gitlab/github_import/representation/to_hash_spec.rb
- - spec/lib/gitlab/github_import/representation/user_spec.rb
- - spec/lib/gitlab/github_import/sequential_importer_spec.rb
- - spec/lib/gitlab/github_import/user_finder_spec.rb
- - spec/lib/gitlab/github_import_spec.rb
- - spec/lib/gitlab/gon_helper_spec.rb
- - spec/lib/gitlab/gpg/commit_spec.rb
- - spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
- - spec/lib/gitlab/gpg_spec.rb
- - spec/lib/gitlab/grape_logging/loggers/exception_logger_spec.rb
- - spec/lib/gitlab/grape_logging/loggers/perf_logger_spec.rb
- - spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb
- - spec/lib/gitlab/grape_logging/loggers/urgency_logger_spec.rb
- - spec/lib/gitlab/graphql/authorize/object_authorization_spec.rb
- - spec/lib/gitlab/graphql/batch_key_spec.rb
- - spec/lib/gitlab/graphql/generic_tracing_spec.rb
- - spec/lib/gitlab/graphql/lazy_spec.rb
- - spec/lib/gitlab/graphql/loaders/issuable_loader_spec.rb
- - spec/lib/gitlab/graphql/pagination/keyset/conditions/not_null_condition_spec.rb
- - spec/lib/gitlab/graphql/pagination/keyset/conditions/null_condition_spec.rb
- - spec/lib/gitlab/graphql/pagination/keyset/connection_generic_keyset_spec.rb
- - spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb
- - spec/lib/gitlab/graphql/present/field_extension_spec.rb
- - spec/lib/gitlab/graphql/timeout_spec.rb
- - spec/lib/gitlab/graphql/tracers/application_context_tracer_spec.rb
- - spec/lib/gitlab/graphql/tracers/timer_tracer_spec.rb
- - spec/lib/gitlab/health_checks/gitaly_check_spec.rb
- - spec/lib/gitlab/hook_data/base_builder_spec.rb
- - spec/lib/gitlab/hotlinking_detector_spec.rb
- - spec/lib/gitlab/import/import_failure_service_spec.rb
- - spec/lib/gitlab/import/metrics_spec.rb
- - spec/lib/gitlab/import_export/attribute_cleaner_spec.rb
- - spec/lib/gitlab/import_export/base/relation_factory_spec.rb
- - spec/lib/gitlab/import_export/decompressed_archive_size_validator_spec.rb
- - spec/lib/gitlab/import_export/group/relation_factory_spec.rb
- - spec/lib/gitlab/import_export/importer_spec.rb
- - spec/lib/gitlab/import_export/project/relation_factory_spec.rb
- - spec/lib/gitlab/import_export/project/sample/relation_factory_spec.rb
- - spec/lib/gitlab/import_export/project/tree_saver_spec.rb
- - spec/lib/gitlab/issuables_count_for_state_spec.rb
- - spec/lib/gitlab/issues/rebalancing/state_spec.rb
- - spec/lib/gitlab/jira/middleware_spec.rb
- - spec/lib/gitlab/jira_import/issue_serializer_spec.rb
- - spec/lib/gitlab/jira_import/labels_importer_spec.rb
- - spec/lib/gitlab/jira_import/metadata_collector_spec.rb
- - spec/lib/gitlab/jira_import_spec.rb
- - spec/lib/gitlab/job_waiter_spec.rb
- - spec/lib/gitlab/json_cache_spec.rb
- - spec/lib/gitlab/kas/client_spec.rb
- - spec/lib/gitlab/kubernetes/config_map_spec.rb
- - spec/lib/gitlab/kubernetes/default_namespace_spec.rb
- - spec/lib/gitlab/kubernetes/helm/api_spec.rb
- - spec/lib/gitlab/kubernetes/namespace_spec.rb
- - spec/lib/gitlab/lazy_spec.rb
- - spec/lib/gitlab/legacy_github_import/branch_formatter_spec.rb
- - spec/lib/gitlab/legacy_github_import/comment_formatter_spec.rb
- - spec/lib/gitlab/legacy_github_import/importer_spec.rb
- - spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb
- - spec/lib/gitlab/legacy_github_import/issue_formatter_spec.rb
- - spec/lib/gitlab/legacy_github_import/label_formatter_spec.rb
- - spec/lib/gitlab/legacy_github_import/milestone_formatter_spec.rb
- - spec/lib/gitlab/legacy_github_import/pull_request_formatter_spec.rb
- - spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb
- - spec/lib/gitlab/legacy_github_import/user_formatter_spec.rb
- - spec/lib/gitlab/lets_encrypt/client_spec.rb
- - spec/lib/gitlab/mail_room/mail_room_spec.rb
- - spec/lib/gitlab/manifest_import/metadata_spec.rb
- - spec/lib/gitlab/markdown_cache/field_data_spec.rb
- - spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb
- - spec/lib/gitlab/merge_requests/mergeability/redis_interface_spec.rb
- - spec/lib/gitlab/metrics/boot_time_tracker_spec.rb
- - spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb
- - spec/lib/gitlab/metrics/elasticsearch_rack_middleware_spec.rb
- - spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb
- - spec/lib/gitlab/metrics/exporter/gc_request_middleware_spec.rb
- - spec/lib/gitlab/metrics/exporter/metrics_middleware_spec.rb
- - spec/lib/gitlab/metrics/rack_middleware_spec.rb
- - spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
- - spec/lib/gitlab/metrics/samplers/database_sampler_spec.rb
- - spec/lib/gitlab/metrics/samplers/puma_sampler_spec.rb
- - spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
- - spec/lib/gitlab/metrics/sli_spec.rb
- - spec/lib/gitlab/metrics/subscribers/action_cable_spec.rb
- - spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
- - spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
- - spec/lib/gitlab/metrics/subscribers/external_http_spec.rb
- - spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb
- - spec/lib/gitlab/metrics/subscribers/rack_attack_spec.rb
- - spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
- - spec/lib/gitlab/metrics/web_transaction_spec.rb
- - spec/lib/gitlab/middleware/basic_health_check_spec.rb
- - spec/lib/gitlab/middleware/compressed_json_spec.rb
- - spec/lib/gitlab/middleware/go_spec.rb
- - spec/lib/gitlab/middleware/handle_malformed_strings_spec.rb
- - spec/lib/gitlab/middleware/multipart_spec.rb
- - spec/lib/gitlab/middleware/query_analyzer_spec.rb
- - spec/lib/gitlab/middleware/rails_queue_duration_spec.rb
- - spec/lib/gitlab/middleware/release_env_spec.rb
- - spec/lib/gitlab/middleware/sidekiq_web_static_spec.rb
- - spec/lib/gitlab/middleware/speedscope_spec.rb
- - spec/lib/gitlab/middleware/webhook_recursion_detection_spec.rb
- - spec/lib/gitlab/octokit/middleware_spec.rb
- - spec/lib/gitlab/optimistic_locking_spec.rb
- - spec/lib/gitlab/pages/settings_spec.rb
- - spec/lib/gitlab/pagination/cursor_based_keyset_spec.rb
- - spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb
- - spec/lib/gitlab/pagination/keyset/cursor_based_request_context_spec.rb
- - spec/lib/gitlab/pagination/keyset/cursor_pager_spec.rb
- - spec/lib/gitlab/pagination/keyset/pager_spec.rb
- - spec/lib/gitlab/pagination/keyset/request_context_spec.rb
- - spec/lib/gitlab/pagination/keyset_spec.rb
- - spec/lib/gitlab/pagination/offset_header_builder_spec.rb
- - spec/lib/gitlab/pagination/offset_pagination_spec.rb
- - spec/lib/gitlab/performance_bar/stats_spec.rb
- - spec/lib/gitlab/phabricator_import/conduit/maniphest_spec.rb
- - spec/lib/gitlab/phabricator_import/conduit/response_spec.rb
- - spec/lib/gitlab/phabricator_import/conduit/user_spec.rb
- - spec/lib/gitlab/polling_interval_spec.rb
- - spec/lib/gitlab/popen/runner_spec.rb
- - spec/lib/gitlab/process_management_spec.rb
- - spec/lib/gitlab/profiler_spec.rb
- - spec/lib/gitlab/prometheus/adapter_spec.rb
- - spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb
- - spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb
- - spec/lib/gitlab/prometheus/queries/matched_metric_query_spec.rb
- - spec/lib/gitlab/query_limiting/middleware_spec.rb
- - spec/lib/gitlab/quick_actions/dsl_spec.rb
- - spec/lib/gitlab/repository_cache_spec.rb
- - spec/lib/gitlab/routing_spec.rb
- - spec/lib/gitlab/runtime_spec.rb
- - spec/lib/gitlab/sanitizers/svg_spec.rb
- - spec/lib/gitlab/search/abuse_validators/no_abusive_coercion_from_string_validator_spec.rb
- - spec/lib/gitlab/search/abuse_validators/no_abusive_term_length_validator_spec.rb
- - spec/lib/gitlab/serializer/pagination_spec.rb
- - spec/lib/gitlab/sidekiq_config/cli_methods_spec.rb
- - spec/lib/gitlab/sidekiq_config/worker_spec.rb
- - spec/lib/gitlab/sidekiq_middleware/client_metrics_spec.rb
- - spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb
- - spec/lib/gitlab/sidekiq_middleware/query_analyzer_spec.rb
- - spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb
- - spec/lib/gitlab/sidekiq_middleware_spec.rb
- - spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb
- - spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb
- - spec/lib/gitlab/slash_commands/command_spec.rb
- - spec/lib/gitlab/slash_commands/deploy_spec.rb
- - spec/lib/gitlab/slash_commands/issue_close_spec.rb
- - spec/lib/gitlab/slash_commands/issue_comment_spec.rb
- - spec/lib/gitlab/slash_commands/issue_new_spec.rb
- - spec/lib/gitlab/slash_commands/issue_search_spec.rb
- - spec/lib/gitlab/slash_commands/issue_show_spec.rb
- - spec/lib/gitlab/slash_commands/presenters/run_spec.rb
- - spec/lib/gitlab/slash_commands/run_spec.rb
- - spec/lib/gitlab/spamcheck/client_spec.rb
- - spec/lib/gitlab/submodule_links_spec.rb
- - spec/lib/gitlab/suggestions/file_suggestion_spec.rb
- - spec/lib/gitlab/tab_width_spec.rb
- - spec/lib/gitlab/themes_spec.rb
- - spec/lib/gitlab/tracking_spec.rb
- - spec/lib/gitlab/usage/metric_spec.rb
- - spec/lib/gitlab/usage/metrics/instrumentations/database_metric_spec.rb
- - spec/lib/gitlab/usage/service_ping/payload_keys_processor_spec.rb
- - spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb
- - spec/lib/gitlab/usage_data_spec.rb
- - spec/lib/gitlab/utils/usage_data_spec.rb
- - spec/lib/gitlab/verify/job_artifacts_spec.rb
- - spec/lib/gitlab/verify/lfs_objects_spec.rb
- - spec/lib/gitlab/verify/uploads_spec.rb
- - spec/lib/gitlab/view/presenter/base_spec.rb
- - spec/lib/gitlab/view/presenter/delegated_spec.rb
- - spec/lib/gitlab/view/presenter/simple_spec.rb
- - spec/lib/gitlab/workhorse_spec.rb
- - spec/lib/gitlab_edition_spec.rb
- - spec/lib/gitlab_spec.rb
- - spec/lib/google_api/cloud_platform/client_spec.rb
- - spec/lib/peek/views/active_record_spec.rb
- - spec/lib/peek/views/bullet_detailed_spec.rb
- - spec/lib/peek/views/external_http_spec.rb
- - spec/lib/safe_zip/entry_spec.rb
- - spec/lib/serializers/unsafe_json_spec.rb
- - spec/lib/sidebars/projects/menus/analytics_menu_spec.rb
- - spec/mailers/emails/service_desk_spec.rb
- - spec/mailers/notify_spec.rb
- - spec/metrics_server/metrics_server_spec.rb
- - spec/migrations/20210406144743_backfill_total_tuple_count_for_batched_migrations_spec.rb
- - spec/models/active_session_spec.rb
- - spec/models/application_record_spec.rb
- - spec/models/badge_spec.rb
- - spec/models/badges/project_badge_spec.rb
- - spec/models/ci/build_spec.rb
- - spec/models/ci/build_trace_chunk_spec.rb
- - spec/models/ci/commit_with_pipeline_spec.rb
- - spec/models/ci/group_spec.rb
- - spec/models/ci/pipeline_spec.rb
- - spec/models/clusters/applications/runner_spec.rb
- - spec/models/clusters/cluster_spec.rb
- - spec/models/clusters/platforms/kubernetes_spec.rb
- - spec/models/commit_signatures/gpg_signature_spec.rb
- - spec/models/commit_spec.rb
- - spec/models/commit_status_spec.rb
- - spec/models/concerns/atomic_internal_id_spec.rb
- - spec/models/concerns/legacy_bulk_insert_spec.rb
- - spec/models/concerns/prometheus_adapter_spec.rb
- - spec/models/concerns/sha_attribute_spec.rb
- - spec/models/concerns/token_authenticatable_strategies/base_spec.rb
- - spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
- - spec/models/concerns/triggerable_hooks_spec.rb
- - spec/models/concerns/x509_serial_number_attribute_spec.rb
- - spec/models/design_management/design_action_spec.rb
- - spec/models/design_management/design_at_version_spec.rb
- - spec/models/diff_viewer/image_spec.rb
- - spec/models/environment_spec.rb
- - spec/models/event_spec.rb
- - spec/models/external_issue_spec.rb
- - spec/models/hooks/web_hook_spec.rb
- - spec/models/integrations/asana_spec.rb
- - spec/models/integrations/chat_message/pipeline_message_spec.rb
- - spec/models/integrations/jira_spec.rb
- - spec/models/integrations/microsoft_teams_spec.rb
- - spec/models/integrations/pipelines_email_spec.rb
- - spec/models/issue_spec.rb
- - spec/models/key_spec.rb
- - spec/models/merge_request_diff_commit_spec.rb
- - spec/models/merge_request_spec.rb
- - spec/models/packages/package_spec.rb
- - spec/models/plan_limits_spec.rb
- - spec/models/project_import_state_spec.rb
- - spec/models/project_spec.rb
- - spec/models/ref_matcher_spec.rb
- - spec/models/release_highlight_spec.rb
- - spec/models/repository_spec.rb
- - spec/models/shard_spec.rb
- - spec/models/snippet_spec.rb
- - spec/models/ssh_host_key_spec.rb
- - spec/models/upload_spec.rb
- - spec/models/user_spec.rb
- - spec/policies/ci/bridge_policy_spec.rb
- - spec/presenters/ci/build_presenter_spec.rb
- - spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb
- - spec/presenters/group_member_presenter_spec.rb
- - spec/presenters/merge_request_presenter_spec.rb
- - spec/presenters/packages/nuget/search_results_presenter_spec.rb
- - spec/presenters/project_member_presenter_spec.rb
- - spec/presenters/project_presenter_spec.rb
- - spec/requests/api/avatar_spec.rb
- - spec/requests/api/container_registry_event_spec.rb
- - spec/requests/api/graphql/mutations/design_management/delete_spec.rb
- - spec/requests/api/graphql/mutations/snippets/create_spec.rb
- - spec/requests/api/graphql/project/cluster_agents_spec.rb
- - spec/requests/api/graphql/project/pipeline_spec.rb
- - spec/requests/api/helpers_spec.rb
- - spec/requests/api/import_bitbucket_server_spec.rb
- - spec/requests/api/import_github_spec.rb
- - spec/requests/api/internal/base_spec.rb
- - spec/requests/api/maven_packages_spec.rb
- - spec/requests/api/project_container_repositories_spec.rb
- - spec/requests/api/users_preferences_spec.rb
- - spec/requests/jwt_controller_spec.rb
- - spec/requests/whats_new_controller_spec.rb
- - spec/rubocop/migration_helpers_spec.rb
- - spec/scripts/setup/find_jh_branch_spec.rb
- - spec/serializers/accessibility_reports_comparer_serializer_spec.rb
- - spec/serializers/admin/user_entity_spec.rb
- - spec/serializers/base_discussion_entity_spec.rb
- - spec/serializers/build_action_entity_spec.rb
- - spec/serializers/build_details_entity_spec.rb
- - spec/serializers/build_trace_entity_spec.rb
- - spec/serializers/ci/dag_job_entity_spec.rb
- - spec/serializers/ci/dag_job_group_entity_spec.rb
- - spec/serializers/ci/dag_pipeline_entity_spec.rb
- - spec/serializers/ci/dag_stage_entity_spec.rb
- - spec/serializers/ci/daily_build_group_report_result_entity_spec.rb
- - spec/serializers/ci/daily_build_group_report_result_serializer_spec.rb
- - spec/serializers/ci/job_entity_spec.rb
- - spec/serializers/ci/job_serializer_spec.rb
- - spec/serializers/ci/pipeline_entity_spec.rb
- - spec/serializers/codequality_reports_comparer_serializer_spec.rb
- - spec/serializers/commit_entity_spec.rb
- - spec/serializers/container_repositories_serializer_spec.rb
- - spec/serializers/container_repository_entity_spec.rb
- - spec/serializers/container_tag_entity_spec.rb
- - spec/serializers/deployment_cluster_entity_spec.rb
- - spec/serializers/deployment_entity_spec.rb
- - spec/serializers/detailed_status_entity_spec.rb
- - spec/serializers/diff_file_entity_spec.rb
- - spec/serializers/diffs_entity_spec.rb
- - spec/serializers/diffs_metadata_entity_spec.rb
- - spec/serializers/discussion_entity_spec.rb
- - spec/serializers/environment_entity_spec.rb
- - spec/serializers/environment_serializer_spec.rb
- - spec/serializers/environment_status_entity_spec.rb
- - spec/serializers/feature_flag_entity_spec.rb
- - spec/serializers/feature_flag_summary_entity_spec.rb
- - spec/serializers/group_child_entity_spec.rb
- - spec/serializers/group_child_serializer_spec.rb
- - spec/serializers/import/manifest_provider_repo_entity_spec.rb
- - spec/serializers/issuable_sidebar_extras_entity_spec.rb
- - spec/serializers/issue_board_entity_spec.rb
- - spec/serializers/issue_entity_spec.rb
- - spec/serializers/lfs_file_lock_entity_spec.rb
- - spec/serializers/linked_project_issue_entity_spec.rb
- - spec/serializers/merge_request_poll_cached_widget_entity_spec.rb
- - spec/serializers/merge_request_poll_widget_entity_spec.rb
- - spec/serializers/merge_request_sidebar_basic_entity_spec.rb
- - spec/serializers/merge_request_sidebar_extras_entity_spec.rb
- - spec/serializers/merge_request_widget_commit_entity_spec.rb
- - spec/serializers/merge_request_widget_entity_spec.rb
- - spec/serializers/merge_requests/pipeline_entity_spec.rb
- - spec/serializers/note_entity_spec.rb
- - spec/serializers/paginated_diff_entity_spec.rb
- - spec/serializers/pipeline_details_entity_spec.rb
- - spec/serializers/pipeline_serializer_spec.rb
- - spec/serializers/project_note_entity_spec.rb
- - spec/serializers/prometheus_alert_entity_spec.rb
- - spec/serializers/review_app_setup_entity_spec.rb
- - spec/serializers/runner_entity_spec.rb
- - spec/serializers/stage_entity_spec.rb
- - spec/serializers/suggestion_entity_spec.rb
- - spec/serializers/test_reports_comparer_serializer_spec.rb
- - spec/serializers/test_suite_entity_spec.rb
- - spec/serializers/trigger_variable_entity_spec.rb
- - spec/services/access_token_validation_service_spec.rb
- - spec/services/authorized_project_update/find_records_due_for_refresh_service_spec.rb
- - spec/services/award_emojis/toggle_service_spec.rb
- - spec/services/base_count_service_spec.rb
- - spec/services/bulk_imports/file_download_service_spec.rb
- - spec/services/ci/change_variables_service_spec.rb
- - spec/services/ci/create_pipeline_service_spec.rb
- - spec/services/ci/pipeline_creation/start_pipeline_service_spec.rb
- - spec/services/ci/prepare_build_service_spec.rb
- - spec/services/ci/process_pipeline_service_spec.rb
- - spec/services/ci/register_job_service_spec.rb
- - spec/services/ci/test_failure_history_service_spec.rb
- - spec/services/ci/update_build_queue_service_spec.rb
- - spec/services/ci/update_build_state_service_spec.rb
- - spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb
- - spec/services/clusters/applications/create_service_spec.rb
- - spec/services/clusters/applications/prometheus_update_service_spec.rb
- - spec/services/clusters/applications/update_service_spec.rb
- - spec/services/clusters/aws/finalize_creation_service_spec.rb
- - spec/services/clusters/aws/provision_service_spec.rb
- - spec/services/clusters/aws/verify_provision_status_service_spec.rb
- - spec/services/clusters/build_kubernetes_namespace_service_spec.rb
- - spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb
- - spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb
- - spec/services/container_expiration_policies/cleanup_service_spec.rb
- - spec/services/deployments/create_service_spec.rb
- - spec/services/discussions/capture_diff_note_position_service_spec.rb
- - spec/services/event_create_service_spec.rb
- - spec/services/git/base_hooks_service_spec.rb
- - spec/services/git/process_ref_changes_service_spec.rb
- - spec/services/git/wiki_push_service/change_spec.rb
- - spec/services/ide/schemas_config_service_spec.rb
- - spec/services/import/bitbucket_server_service_spec.rb
- - spec/services/import/github_service_spec.rb
- - spec/services/issues/create_service_spec.rb
- - spec/services/issues/related_branches_service_spec.rb
- - spec/services/jira_connect_subscriptions/create_service_spec.rb
- - spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
- - spec/services/merge_requests/approval_service_spec.rb
- - spec/services/merge_requests/build_service_spec.rb
- - spec/services/merge_requests/close_service_spec.rb
- - spec/services/merge_requests/merge_service_spec.rb
- - spec/services/merge_requests/post_merge_service_spec.rb
- - spec/services/merge_requests/refresh_service_spec.rb
- - spec/services/merge_requests/reopen_service_spec.rb
- - spec/services/merge_requests/request_review_service_spec.rb
- - spec/services/merge_requests/toggle_attention_requested_service_spec.rb
- - spec/services/metrics/dashboard/clone_dashboard_service_spec.rb
- - spec/services/metrics/dashboard/update_dashboard_service_spec.rb
- - spec/services/metrics/users_starred_dashboards/create_service_spec.rb
- - spec/services/milestones/update_service_spec.rb
- - spec/services/namespaces/in_product_marketing_emails_service_spec.rb
- - spec/services/notes/create_service_spec.rb
- - spec/services/notes/render_service_spec.rb
- - spec/services/notification_service_spec.rb
- - spec/services/packages/generic/create_package_file_service_spec.rb
- - spec/services/packages/maven/find_or_create_package_service_spec.rb
- - spec/services/packages/maven/metadata/sync_service_spec.rb
- - spec/services/packages/nuget/metadata_extraction_service_spec.rb
- - spec/services/pages/zip_directory_service_spec.rb
- - spec/services/post_receive_service_spec.rb
- - spec/workers/projects/after_import_worker_spec.rb
- - spec/services/projects/branches_by_mode_service_spec.rb
- - spec/services/projects/create_service_spec.rb
- - spec/services/projects/destroy_service_spec.rb
- - spec/services/projects/import_service_spec.rb
- - spec/services/projects/operations/update_service_spec.rb
- - spec/services/projects/overwrite_project_service_spec.rb
- - spec/services/projects/update_pages_service_spec.rb
- - spec/services/projects/update_remote_mirror_service_spec.rb
- - spec/services/projects/update_repository_storage_service_spec.rb
- - spec/services/projects/update_service_spec.rb
- - spec/services/repositories/changelog_service_spec.rb
- - spec/services/search_service_spec.rb
- - spec/services/service_ping/submit_service_ping_service_spec.rb
- - spec/services/snippets/update_repository_storage_service_spec.rb
- - spec/services/spam/akismet_mark_as_spam_service_spec.rb
- - spec/services/spam/akismet_service_spec.rb
- - spec/services/spam/ham_service_spec.rb
- - spec/services/spam/spam_action_service_spec.rb
- - spec/services/spam/spam_params_spec.rb
- - spec/services/system_note_service_spec.rb
- - spec/services/system_notes/commit_service_spec.rb
- - spec/services/system_notes/issuables_service_spec.rb
- - spec/services/update_merge_request_metrics_service_spec.rb
- - spec/services/users/activity_service_spec.rb
- - spec/services/users/create_service_spec.rb
- - spec/services/users/refresh_authorized_projects_service_spec.rb
- - spec/services/users/update_service_spec.rb
- - spec/services/web_hook_service_spec.rb
- - spec/services/wiki_pages/base_service_spec.rb
- - spec/spam/concerns/has_spam_action_response_fields_spec.rb
- - spec/support/helpers/graphql_helpers.rb
- - spec/support/helpers/import_spec_helper.rb
- - spec/support/helpers/ldap_helpers.rb
- - spec/support/helpers/project_forks_helper.rb
- - spec/support/helpers/stub_metrics.rb
- - spec/support/helpers/stub_spam_services.rb
- - spec/support/import_export/common_util.rb
- - spec/support/prometheus/additional_metrics_shared_examples.rb
- - spec/support/shared_contexts/lib/gitlab/sidekiq_middleware/server_metrics_shared_context.rb
- - spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb
- - spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
- - spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb
- - spec/support/shared_examples/graphql/mutations/http_integrations_shared_examples.rb
- - spec/support/shared_examples/lib/gitlab/config/inheritable_shared_examples.rb
- - spec/support/shared_examples/lib/gitlab/diff_file_collections_shared_examples.rb
- - spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb
- - spec/support/shared_examples/metrics/sampler_shared_examples.rb
- - spec/support/shared_examples/models/chat_integration_shared_examples.rb
- - spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb
- - spec/support/shared_examples/models/members_notifications_shared_example.rb
- - spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb
- - spec/support/shared_examples/namespaces/hierarchy_examples.rb
- - spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb
- - spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb
- - spec/support/shared_examples/requests/api/debian_common_shared_examples.rb
- - spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb
- - spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
- - spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
- - spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb
- - spec/support/shared_examples/requests/rack_attack_shared_examples.rb
- - spec/support/shared_examples/serializers/diff_file_entity_shared_examples.rb
- - spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb
- - spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb
- - spec/support/shared_examples/services/alert_management/alert_processing/notifications_shared_examples.rb
- - spec/support/shared_examples/services/alert_management_shared_examples.rb
- - spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb
- - spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb
- - spec/support/shared_examples/services/jira/requests/base_shared_examples.rb
- - spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb
- - spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
- - spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb
- - spec/support/shared_examples/workers/background_migration_worker_shared_examples.rb
- - spec/support/shared_examples/workers/update_repository_move_shared_examples.rb
- - spec/tasks/gettext_rake_spec.rb
- - spec/tasks/gitlab/background_migrations_rake_spec.rb
- - spec/tasks/gitlab/check_rake_spec.rb
- - spec/tasks/gitlab/cleanup_rake_spec.rb
- - spec/tasks/gitlab/db_rake_spec.rb
- - spec/tasks/gitlab/packages/events_rake_spec.rb
- - spec/tasks/gitlab/setup_rake_spec.rb
- - spec/tooling/danger/project_helper_spec.rb
- - spec/tooling/danger/specs_spec.rb
- - spec/tooling/lib/tooling/helm3_client_spec.rb
- - spec/tooling/lib/tooling/kubernetes_client_spec.rb
- - spec/tooling/rspec_flaky/example_spec.rb
- - spec/tooling/rspec_flaky/listener_spec.rb
- - spec/uploaders/file_uploader_spec.rb
- - spec/uploaders/object_storage_spec.rb
- - spec/uploaders/personal_file_uploader_spec.rb
- - spec/uploaders/records_uploads_spec.rb
- - spec/views/projects/issues/show.html.haml_spec.rb
- - spec/views/shared/milestones/_issuables.html.haml_spec.rb
- - spec/views/shared/wikis/_sidebar.html.haml_spec.rb
- - spec/workers/bulk_imports/export_request_worker_spec.rb
- - spec/workers/chat_notification_worker_spec.rb
- - spec/workers/ci/build_prepare_worker_spec.rb
- - spec/workers/ci/create_cross_project_pipeline_worker_spec.rb
- - spec/workers/ci/create_downstream_pipeline_worker_spec.rb
- - spec/workers/ci/pipeline_bridge_status_worker_spec.rb
- - spec/workers/ci/pipeline_success_unlock_artifacts_worker_spec.rb
- - spec/workers/ci/ref_delete_unlock_artifacts_worker_spec.rb
- - spec/workers/clusters/agents/delete_expired_events_worker_spec.rb
- - spec/workers/concerns/application_worker_spec.rb
- - spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
- - spec/workers/concerns/gitlab/github_import/stage_methods_spec.rb
- - spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb
- - spec/workers/create_commit_signature_worker_spec.rb
- - spec/workers/environments/auto_stop_worker_spec.rb
- - spec/workers/error_tracking_issue_link_worker_spec.rb
- - spec/workers/gitlab/github_import/advance_stage_worker_spec.rb
- - spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb
- - spec/workers/gitlab/github_import/import_issue_worker_spec.rb
- - spec/workers/gitlab/github_import/import_note_worker_spec.rb
- - spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb
- - spec/workers/gitlab/github_import/stage/finish_import_worker_spec.rb
- - spec/workers/gitlab/github_import/stage/import_base_data_worker_spec.rb
- - spec/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker_spec.rb
- - spec/workers/gitlab/github_import/stage/import_lfs_objects_worker_spec.rb
- - spec/workers/gitlab/github_import/stage/import_notes_worker_spec.rb
- - spec/workers/gitlab/github_import/stage/import_pull_requests_merged_by_worker_spec.rb
- - spec/workers/gitlab/github_import/stage/import_pull_requests_reviews_worker_spec.rb
- - spec/workers/gitlab/github_import/stage/import_pull_requests_worker_spec.rb
- - spec/workers/gitlab/github_import/stage/import_repository_worker_spec.rb
- - spec/workers/gitlab_performance_bar_stats_worker_spec.rb
- - spec/workers/invalid_gpg_signature_update_worker_spec.rb
- - spec/workers/issues/rebalancing_worker_spec.rb
- - spec/workers/merge_request_mergeability_check_worker_spec.rb
- - spec/workers/new_issue_worker_spec.rb
- - spec/workers/new_merge_request_worker_spec.rb
- - spec/workers/pages_domain_ssl_renewal_worker_spec.rb
- - spec/workers/pages_domain_verification_worker_spec.rb
- - spec/workers/post_receive_spec.rb
- - spec/workers/project_cache_worker_spec.rb
- - spec/workers/propagate_integration_group_worker_spec.rb
- - spec/workers/propagate_integration_inherit_descendant_worker_spec.rb
- - spec/workers/propagate_integration_inherit_worker_spec.rb
- - spec/workers/propagate_integration_project_worker_spec.rb
- - spec/workers/purge_dependency_proxy_cache_worker_spec.rb
- - spec/workers/repository_import_worker_spec.rb
- - spec/workers/system_hook_push_worker_spec.rb
- -
+ - 'ee/spec/controllers/concerns/ee/routable_actions/sso_enforcement_redirect_spec.rb'
+ - 'ee/spec/controllers/groups/clusters_controller_spec.rb'
+ - 'ee/spec/controllers/groups/sso_controller_spec.rb'
+ - 'ee/spec/controllers/oauth/geo_auth_controller_spec.rb'
+ - 'ee/spec/controllers/projects/clusters_controller_spec.rb'
+ - 'ee/spec/db/production/license_spec.rb'
+ - 'ee/spec/features/admin/groups/admin_subscription_alerts_spec.rb'
+ - 'ee/spec/features/billings/billing_plans_spec.rb'
+ - 'ee/spec/features/merge_trains/two_merge_requests_on_train_spec.rb'
+ - 'ee/spec/features/profiles/account_spec.rb'
+ - 'ee/spec/finders/license_template_finder_spec.rb'
+ - 'ee/spec/finders/projects/integrations/jira/issues_finder_spec.rb'
+ - 'ee/spec/finders/security/pipeline_vulnerabilities_finder_spec.rb'
+ - 'ee/spec/finders/template_finder_spec.rb'
+ - 'ee/spec/graphql/mutations/dast_scanner_profiles/create_spec.rb'
+ - 'ee/spec/graphql/mutations/dast_site_profiles/create_spec.rb'
+ - 'ee/spec/graphql/mutations/dast_site_profiles/delete_spec.rb'
+ - 'ee/spec/graphql/mutations/dast_site_profiles/update_spec.rb'
+ - 'ee/spec/graphql/mutations/incident_management/oncall_rotation/create_spec.rb'
+ - 'ee/spec/graphql/mutations/issues/promote_to_epic_spec.rb'
+ - 'ee/spec/graphql/mutations/issues/set_epic_spec.rb'
+ - 'ee/spec/graphql/mutations/vulnerabilities/create_external_issue_link_spec.rb'
+ - 'ee/spec/graphql/mutations/vulnerabilities/destroy_external_issue_link_spec.rb'
+ - 'ee/spec/graphql/resolvers/external_issue_resolver_spec.rb'
+ - 'ee/spec/graphql/resolvers/security_report_summary_resolver_spec.rb'
+ - 'ee/spec/graphql/resolvers/vulnerabilities/details_resolver_spec.rb'
+ - 'ee/spec/helpers/billing_plans_helper_spec.rb'
+ - 'ee/spec/helpers/ee/ci/runners_helper_spec.rb'
+ - 'ee/spec/helpers/ee/integrations_helper_spec.rb'
+ - 'ee/spec/helpers/ee/subscribable_banner_helper_spec.rb'
+ - 'ee/spec/helpers/ee/trial_helper_spec.rb'
+ - 'ee/spec/helpers/ee/trial_registration_helper_spec.rb'
+ - 'ee/spec/helpers/kerberos_helper_spec.rb'
+ - 'ee/spec/helpers/license_helper_spec.rb'
+ - 'ee/spec/helpers/roadmaps_helper_spec.rb'
+ - 'ee/spec/helpers/routing/pseudonymization_helper_spec.rb'
+ - 'ee/spec/helpers/subscriptions_helper_spec.rb'
+ - 'ee/spec/helpers/timeboxes_helper_spec.rb'
+ - 'ee/spec/helpers/vulnerabilities_helper_spec.rb'
+ - 'ee/spec/lib/ee/backup/repositories_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules_in_batch_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/ci/matching/runner_matcher_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/ci/pipeline/quota/size_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/etag_caching/router/rails_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/gon_helper_spec.rb'
+ - 'ee/spec/lib/elastic/latest/config_shared_examples.rb'
+ - 'ee/spec/lib/elastic/latest/git_instance_proxy_spec.rb'
+ - 'ee/spec/lib/elastic/migration_spec.rb'
+ - 'ee/spec/lib/elastic/multi_version_class_proxy_spec.rb'
+ - 'ee/spec/lib/elastic/multi_version_instance_proxy_spec.rb'
+ - 'ee/spec/lib/gitlab/auth/group_saml/response_check_spec.rb'
+ - 'ee/spec/lib/gitlab/auth/group_saml/token_actor_spec.rb'
+ - 'ee/spec/lib/gitlab/auth/otp/session_enforcer_spec.rb'
+ - 'ee/spec/lib/gitlab/authority_analyzer_spec.rb'
+ - 'ee/spec/lib/gitlab/cache_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/pipeline/chain/limit/activity_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/pipeline/chain/limit/size_spec.rb'
+ - 'ee/spec/lib/gitlab/code_owners/groups_loader_spec.rb'
+ - 'ee/spec/lib/gitlab/code_owners/users_loader_spec.rb'
+ - 'ee/spec/lib/gitlab/custom_file_templates_spec.rb'
+ - 'ee/spec/lib/gitlab/elastic/client_spec.rb'
+ - 'ee/spec/lib/gitlab/elastic/search_results_spec.rb'
+ - 'ee/spec/lib/gitlab/expiring_subscription_message_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/git_ssh_proxy_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/lease_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/oauth/logout_token_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/oauth/session_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/replicator_spec.rb'
+ - 'ee/spec/lib/gitlab/geo_spec.rb'
+ - 'ee/spec/lib/gitlab/git_access_spec.rb'
+ - 'ee/spec/lib/gitlab/import_export/group/relation_factory_spec.rb'
+ - 'ee/spec/lib/gitlab/middleware/ip_restrictor_spec.rb'
+ - 'ee/spec/lib/gitlab/prometheus/queries/cluster_query_spec.rb'
+ - 'ee/spec/lib/gitlab/subscription_portal/clients/rest_spec.rb'
+ - 'ee/spec/lib/sidebars/groups/menus/analytics_menu_spec.rb'
+ - 'ee/spec/lib/system_check/geo/geo_database_configured_check_spec.rb'
+ - 'ee/spec/models/app_sec/fuzzing/api/ci_configuration_spec.rb'
+ - 'ee/spec/models/concerns/geo/verification_state_spec.rb'
+ - 'ee/spec/models/ee/ci/job_artifact_spec.rb'
+ - 'ee/spec/models/ee/user_spec.rb'
+ - 'ee/spec/models/elastic/index_setting_spec.rb'
+ - 'ee/spec/models/geo/project_registry_spec.rb'
+ - 'ee/spec/models/geo/secondary_usage_data_spec.rb'
+ - 'ee/spec/models/geo_node_status_spec.rb'
+ - 'ee/spec/models/integrations/github/status_message_spec.rb'
+ - 'ee/spec/models/integrations/github_spec.rb'
+ - 'ee/spec/models/project_spec.rb'
+ - 'ee/spec/models/push_rule_spec.rb'
+ - 'ee/spec/presenters/audit_event_presenter_spec.rb'
+ - 'ee/spec/presenters/group_member_presenter_spec.rb'
+ - 'ee/spec/presenters/merge_request_approver_presenter_spec.rb'
+ - 'ee/spec/presenters/project_member_presenter_spec.rb'
+ - 'ee/spec/requests/api/geo_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/dast_site_profiles/delete_spec.rb'
+ - 'ee/spec/requests/api/internal/base_spec.rb'
+ - 'ee/spec/requests/api/ldap_spec.rb'
+ - 'ee/spec/requests/api/visual_review_discussions_spec.rb'
+ - 'ee/spec/requests/api/vulnerability_findings_spec.rb'
+ - 'ee/spec/requests/callout_spec.rb'
+ - 'ee/spec/requests/rack_attack_spec.rb'
+ - 'ee/spec/serializers/blocking_merge_request_entity_spec.rb'
+ - 'ee/spec/serializers/clusters/environment_entity_spec.rb'
+ - 'ee/spec/serializers/dashboard_operations_project_entity_spec.rb'
+ - 'ee/spec/serializers/dependency_entity_spec.rb'
+ - 'ee/spec/serializers/ee/admin/user_entity_spec.rb'
+ - 'ee/spec/serializers/ee/build_details_entity_spec.rb'
+ - 'ee/spec/serializers/ee/ci/job_entity_spec.rb'
+ - 'ee/spec/serializers/ee/ci/pipeline_entity_spec.rb'
+ - 'ee/spec/serializers/ee/group_child_entity_spec.rb'
+ - 'ee/spec/serializers/ee/issue_board_entity_spec.rb'
+ - 'ee/spec/serializers/ee/issue_entity_spec.rb'
+ - 'ee/spec/serializers/ee/issue_sidebar_extras_entity_spec.rb'
+ - 'ee/spec/serializers/ee/merge_request_poll_cached_widget_entity_spec.rb'
+ - 'ee/spec/serializers/ee/note_entity_spec.rb'
+ - 'ee/spec/serializers/environment_entity_spec.rb'
+ - 'ee/spec/serializers/epic_entity_spec.rb'
+ - 'ee/spec/serializers/epic_note_entity_spec.rb'
+ - 'ee/spec/serializers/integrations/jira_serializers/issue_detail_entity_spec.rb'
+ - 'ee/spec/serializers/integrations/jira_serializers/issue_entity_spec.rb'
+ - 'ee/spec/serializers/issuable_sidebar_extras_entity_spec.rb'
+ - 'ee/spec/serializers/issues/linked_issue_feature_flag_entity_spec.rb'
+ - 'ee/spec/serializers/linked_feature_flag_issue_entity_spec.rb'
+ - 'ee/spec/serializers/merge_request_poll_widget_entity_spec.rb'
+ - 'ee/spec/serializers/merge_request_sidebar_basic_entity_spec.rb'
+ - 'ee/spec/serializers/merge_request_widget_entity_spec.rb'
+ - 'ee/spec/serializers/test_reports_comparer_serializer_spec.rb'
+ - 'ee/spec/serializers/user_analytics_entity_spec.rb'
+ - 'ee/spec/serializers/vulnerabilities/feedback_entity_spec.rb'
+ - 'ee/spec/serializers/vulnerabilities/finding_entity_spec.rb'
+ - 'ee/spec/serializers/vulnerabilities/finding_reports_comparer_entity_spec.rb'
+ - 'ee/spec/serializers/vulnerabilities/finding_serializer_spec.rb'
+ - 'ee/spec/serializers/vulnerability_note_entity_spec.rb'
+ - 'ee/spec/services/alert_management/extract_alert_payload_fields_service_spec.rb'
+ - 'ee/spec/services/app_sec/dast/scans/create_service_spec.rb'
+ - 'ee/spec/services/app_sec/dast/site_profiles/update_service_spec.rb'
+ - 'ee/spec/services/app_sec/fuzzing/api/ci_configuration_create_service_spec.rb'
+ - 'ee/spec/services/ci/create_pipeline_service/dast_configuration_spec.rb'
+ - 'ee/spec/services/ci/minutes/track_live_consumption_service_spec.rb'
+ - 'ee/spec/services/ci/minutes/update_project_and_namespace_usage_service_spec.rb'
+ - 'ee/spec/services/ci/pipeline_creation/drop_not_runnable_builds_service_spec.rb'
+ - 'ee/spec/services/ci/pipeline_creation/start_pipeline_service_spec.rb'
+ - 'ee/spec/services/dashboard/operations/list_service_spec.rb'
+ - 'ee/spec/services/dashboard/projects/create_service_spec.rb'
+ - 'ee/spec/services/ee/issues/create_service_spec.rb'
+ - 'ee/spec/services/ee/issues/update_service_spec.rb'
+ - 'ee/spec/services/ee/merge_requests/base_service_spec.rb'
+ - 'ee/spec/services/ee/merge_requests/post_merge_service_spec.rb'
+ - 'ee/spec/services/ee/merge_requests/refresh_service_spec.rb'
+ - 'ee/spec/services/ee/notification_service_spec.rb'
+ - 'ee/spec/services/ee/post_receive_service_spec.rb'
+ - 'ee/spec/services/geo/blob_download_service_spec.rb'
+ - 'ee/spec/services/geo/graphql_request_service_spec.rb'
+ - 'ee/spec/services/geo/node_status_request_service_spec.rb'
+ - 'ee/spec/services/geo/replication_toggle_request_service_spec.rb'
+ - 'ee/spec/services/geo/repository_verification_primary_service_spec.rb'
+ - 'ee/spec/services/gitlab_subscriptions/fetch_subscription_plans_service_spec.rb'
+ - 'ee/spec/services/group_saml/group_managed_accounts/clean_up_members_service_spec.rb'
+ - 'ee/spec/services/group_saml/sign_up_service_spec.rb'
+ - 'ee/spec/services/groups/update_repository_storage_service_spec.rb'
+ - 'ee/spec/services/ide/schemas_config_service_spec.rb'
+ - 'ee/spec/services/incident_management/oncall_schedules/update_service_spec.rb'
+ - 'ee/spec/services/incident_management/pending_escalations/process_service_spec.rb'
+ - 'ee/spec/services/merge_requests/approval_service_spec.rb'
+ - 'ee/spec/services/merge_requests/build_service_spec.rb'
+ - 'ee/spec/services/merge_requests/reset_approvals_service_spec.rb'
+ - 'ee/spec/services/namespaces/in_product_marketing_emails_service_spec.rb'
+ - 'ee/spec/services/projects/update_mirror_service_spec.rb'
+ - 'ee/spec/services/security/ingestion/ingest_report_slice_service_spec.rb'
+ - 'ee/spec/services/security/orchestration/assign_service_spec.rb'
+ - 'ee/spec/services/security/security_orchestration_policies/on_demand_scan_pipeline_configuration_service_spec.rb'
+ - 'ee/spec/services/slash_commands/global_slack_handler_spec.rb'
+ - 'ee/spec/services/status_page/publish_details_service_spec.rb'
+ - 'ee/spec/services/status_page/publish_service_spec.rb'
+ - 'ee/spec/services/status_page/trigger_publish_service_spec.rb'
+ - 'ee/spec/services/system_note_service_spec.rb'
+ - 'ee/spec/services/vulnerability_exports/export_service_spec.rb'
+ - 'ee/spec/services/vulnerability_external_issue_links/create_service_spec.rb'
+ - 'ee/spec/support/helpers/ee/ldap_helpers.rb'
+ - 'ee/spec/support/prometheus/additional_metrics_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/controllers/analytics/cycle_analytics/shared_stage_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/controllers/cluster_metrics_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/models/concerns/blob_replicator_strategy_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/models/concerns/verifiable_replicator_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/models/geo_verifiable_registry_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/serializers/report_status_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/services/base_sync_service_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/services/geo/geo_request_service_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/status_page/reference_links_examples.rb'
+ - 'ee/spec/validators/json_schema_validator_spec.rb'
+ - 'ee/spec/views/layouts/header/_ee_subscribable_banner.html.haml_spec.rb'
+ - 'ee/spec/workers/ci/sync_reports_to_report_approval_rules_worker_spec.rb'
+ - 'ee/spec/workers/compliance_management/chain_of_custody_report_worker_spec.rb'
+ - 'ee/spec/workers/geo/container_repository_sync_worker_spec.rb'
+ - 'ee/spec/workers/geo/design_repository_sync_worker_spec.rb'
+ - 'ee/spec/workers/geo/destroy_worker_spec.rb'
+ - 'ee/spec/workers/geo/event_worker_spec.rb'
+ - 'ee/spec/workers/geo/metrics_update_worker_spec.rb'
+ - 'ee/spec/workers/geo/prune_event_log_worker_spec.rb'
+ - 'ee/spec/workers/geo/registry_sync_worker_spec.rb'
+ - 'ee/spec/workers/geo/reverification_batch_worker_spec.rb'
+ - 'ee/spec/workers/geo/sidekiq_cron_config_worker_spec.rb'
+ - 'ee/spec/workers/geo/sync_timeout_cron_worker_spec.rb'
+ - 'ee/spec/workers/geo/verification_batch_worker_spec.rb'
+ - 'ee/spec/workers/geo/verification_cron_worker_spec.rb'
+ - 'ee/spec/workers/geo/verification_timeout_worker_spec.rb'
+ - 'ee/spec/workers/geo/verification_worker_spec.rb'
+ - 'ee/spec/workers/iterations/cadences/create_iterations_worker_spec.rb'
+ - 'ee/spec/workers/iterations/roll_over_issues_worker_spec.rb'
+ - 'ee/spec/workers/ldap_group_sync_worker_spec.rb'
+ - 'ee/spec/workers/merge_request_reset_approvals_worker_spec.rb'
+ - 'ee/spec/workers/new_epic_worker_spec.rb'
+ - 'ee/spec/workers/update_max_seats_used_for_gitlab_com_subscriptions_worker_spec.rb'
+ - 'qa/spec/git/repository_spec.rb'
+ - 'qa/spec/page/base_spec.rb'
+ - 'qa/spec/page/validator_spec.rb'
+ - 'qa/spec/page/view_spec.rb'
+ - 'qa/spec/resource/api_fabricator_spec.rb'
+ - 'qa/spec/resource/base_spec.rb'
+ - 'qa/spec/runtime/application_settings_spec.rb'
+ - 'qa/spec/runtime/feature_spec.rb'
+ - 'qa/spec/runtime/release_spec.rb'
+ - 'qa/spec/scenario/test/sanity/selectors_spec.rb'
+ - 'qa/spec/support/formatters/allure_metadata_formatter_spec.rb'
+ - 'qa/spec/support/page_error_checker_spec.rb'
+ - 'qa/spec/support/run_spec.rb'
+ - 'qa/spec/tools/long_running_spec_reporter_spec.rb'
+ - 'spec/benchmarks/banzai_benchmark.rb'
+ - 'spec/bin/feature_flag_spec.rb'
+ - 'spec/controllers/application_controller_spec.rb'
+ - 'spec/controllers/concerns/checks_collaboration_spec.rb'
+ - 'spec/controllers/concerns/import_url_params_spec.rb'
+ - 'spec/controllers/concerns/issuable_actions_spec.rb'
+ - 'spec/controllers/concerns/issuable_collections_spec.rb'
+ - 'spec/controllers/concerns/page_limiter_spec.rb'
+ - 'spec/controllers/concerns/spammable_actions/akismet_mark_as_spam_action_spec.rb'
+ - 'spec/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support_spec.rb'
+ - 'spec/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support_spec.rb'
+ - 'spec/controllers/dashboard/snippets_controller_spec.rb'
+ - 'spec/controllers/explore/projects_controller_spec.rb'
+ - 'spec/controllers/groups/dependency_proxy_auth_controller_spec.rb'
+ - 'spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb'
+ - 'spec/controllers/help_controller_spec.rb'
+ - 'spec/controllers/import/bitbucket_controller_spec.rb'
+ - 'spec/controllers/import/bitbucket_server_controller_spec.rb'
+ - 'spec/controllers/import/bulk_imports_controller_spec.rb'
+ - 'spec/controllers/import/fogbugz_controller_spec.rb'
+ - 'spec/controllers/import/gitea_controller_spec.rb'
+ - 'spec/controllers/import/github_controller_spec.rb'
+ - 'spec/controllers/import/gitlab_controller_spec.rb'
+ - 'spec/controllers/oauth/jira_dvcs/authorizations_controller_spec.rb'
+ - 'spec/controllers/omniauth_callbacks_controller_spec.rb'
+ - 'spec/controllers/profiles/two_factor_auths_controller_spec.rb'
+ - 'spec/controllers/projects/blob_controller_spec.rb'
+ - 'spec/controllers/projects/branches_controller_spec.rb'
+ - 'spec/controllers/projects/import/jira_controller_spec.rb'
+ - 'spec/controllers/projects/merge_requests/diffs_controller_spec.rb'
+ - 'spec/controllers/projects/merge_requests_controller_spec.rb'
+ - 'spec/controllers/projects/notes_controller_spec.rb'
+ - 'spec/controllers/projects/pages_controller_spec.rb'
+ - 'spec/controllers/projects/performance_monitoring/dashboards_controller_spec.rb'
+ - 'spec/controllers/projects/pipelines_controller_spec.rb'
+ - 'spec/controllers/projects/prometheus/metrics_controller_spec.rb'
+ - 'spec/controllers/projects/registry/tags_controller_spec.rb'
+ - 'spec/controllers/projects/settings/operations_controller_spec.rb'
+ - 'spec/controllers/projects/snippets_controller_spec.rb'
+ - 'spec/controllers/sessions_controller_spec.rb'
+ - 'spec/dependencies/omniauth_saml_spec.rb'
+ - 'spec/experiments/concerns/project_commit_count_spec.rb'
+ - 'spec/factories/ci/job_artifacts.rb'
+ - 'spec/features/admin/admin_system_info_spec.rb'
+ - 'spec/features/clusters/create_agent_spec.rb'
+ - 'spec/features/file_uploads/maven_package_spec.rb'
+ - 'spec/features/groups/container_registry_spec.rb'
+ - 'spec/features/help_pages_spec.rb'
+ - 'spec/features/issuables/markdown_references/jira_spec.rb'
+ - 'spec/features/markdown/markdown_spec.rb'
+ - 'spec/features/profiles/personal_access_tokens_spec.rb'
+ - 'spec/features/projects/container_registry_spec.rb'
+ - 'spec/features/projects/integrations/user_activates_jira_spec.rb'
+ - 'spec/finders/ci/auth_job_finder_spec.rb'
+ - 'spec/finders/merge_requests/oldest_per_commit_finder_spec.rb'
+ - 'spec/finders/repositories/changelog_commits_finder_spec.rb'
+ - 'spec/finders/repositories/changelog_tag_finder_spec.rb'
+ - 'spec/graphql/features/authorization_spec.rb'
+ - 'spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb'
+ - 'spec/graphql/mutations/alert_management/update_alert_status_spec.rb'
+ - 'spec/graphql/mutations/branches/create_spec.rb'
+ - 'spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb'
+ - 'spec/graphql/mutations/ci/job_token_scope/remove_project_spec.rb'
+ - 'spec/graphql/mutations/design_management/upload_spec.rb'
+ - 'spec/graphql/mutations/environments/canary_ingress/update_spec.rb'
+ - 'spec/graphql/resolvers/kas/agent_connections_resolver_spec.rb'
+ - 'spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb'
+ - 'spec/graphql/types/kas/agent_connection_type_spec.rb'
+ - 'spec/graphql/types/permission_types/base_permission_type_spec.rb'
+ - 'spec/graphql/types/project_type_spec.rb'
+ - 'spec/helpers/blame_helper_spec.rb'
+ - 'spec/helpers/blob_helper_spec.rb'
+ - 'spec/helpers/ci/pipelines_helper_spec.rb'
+ - 'spec/helpers/ci/status_helper_spec.rb'
+ - 'spec/helpers/ci/triggers_helper_spec.rb'
+ - 'spec/helpers/commits_helper_spec.rb'
+ - 'spec/helpers/dev_ops_report_helper_spec.rb'
+ - 'spec/helpers/diff_helper_spec.rb'
+ - 'spec/helpers/emails_helper_spec.rb'
+ - 'spec/helpers/environments_helper_spec.rb'
+ - 'spec/helpers/form_helper_spec.rb'
+ - 'spec/helpers/gitlab_routing_helper_spec.rb'
+ - 'spec/helpers/integrations_helper_spec.rb'
+ - 'spec/helpers/issuables_helper_spec.rb'
+ - 'spec/helpers/markup_helper_spec.rb'
+ - 'spec/helpers/merge_requests_helper_spec.rb'
+ - 'spec/helpers/notes_helper_spec.rb'
+ - 'spec/helpers/numbers_helper_spec.rb'
+ - 'spec/helpers/preferences_helper_spec.rb'
+ - 'spec/helpers/projects_helper_spec.rb'
+ - 'spec/helpers/routing/pseudonymization_helper_spec.rb'
+ - 'spec/helpers/sorting_helper_spec.rb'
+ - 'spec/helpers/submodule_helper_spec.rb'
+ - 'spec/helpers/todos_helper_spec.rb'
+ - 'spec/helpers/version_check_helper_spec.rb'
+ - 'spec/initializers/doorkeeper_spec.rb'
+ - 'spec/initializers/global_id_spec.rb'
+ - 'spec/initializers/hangouts_chat_http_override_spec.rb'
+ - 'spec/lib/api/base_spec.rb'
+ - 'spec/lib/api/entities/ci/job_request/image_spec.rb'
+ - 'spec/lib/api/entities/ci/job_request/port_spec.rb'
+ - 'spec/lib/api/helpers/authentication_spec.rb'
+ - 'spec/lib/api/helpers/caching_spec.rb'
+ - 'spec/lib/api/helpers/graphql_helpers_spec.rb'
+ - 'spec/lib/api/helpers/pagination_spec.rb'
+ - 'spec/lib/api/helpers/pagination_strategies_spec.rb'
+ - 'spec/lib/api/helpers/variables_helpers_spec.rb'
+ - 'spec/lib/api/helpers_spec.rb'
+ - 'spec/lib/atlassian/jira_connect/client_spec.rb'
+ - 'spec/lib/backup/files_spec.rb'
+ - 'spec/lib/backup/repositories_spec.rb'
+ - 'spec/lib/banzai/cross_project_reference_spec.rb'
+ - 'spec/lib/banzai/filter/gollum_tags_filter_spec.rb'
+ - 'spec/lib/banzai/filter/repository_link_filter_spec.rb'
+ - 'spec/lib/banzai/pipeline/wiki_pipeline_spec.rb'
+ - 'spec/lib/banzai/querying_spec.rb'
+ - 'spec/lib/banzai/reference_parser/base_parser_spec.rb'
+ - 'spec/lib/banzai/reference_parser/commit_parser_spec.rb'
+ - 'spec/lib/banzai/reference_parser/commit_range_parser_spec.rb'
+ - 'spec/lib/banzai/render_context_spec.rb'
+ - 'spec/lib/banzai/renderer_spec.rb'
+ - 'spec/lib/bitbucket/connection_spec.rb'
+ - 'spec/lib/bitbucket/paginator_spec.rb'
+ - 'spec/lib/bitbucket_server/paginator_spec.rb'
+ - 'spec/lib/bulk_imports/clients/http_spec.rb'
+ - 'spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb'
+ - 'spec/lib/bulk_imports/common/extractors/rest_extractor_spec.rb'
+ - 'spec/lib/bulk_imports/ndjson_pipeline_spec.rb'
+ - 'spec/lib/bulk_imports/network_error_spec.rb'
+ - 'spec/lib/bulk_imports/projects/pipelines/snippets_repository_pipeline_spec.rb'
+ - 'spec/lib/bulk_imports/projects/transformers/project_attributes_transformer_spec.rb'
+ - 'spec/lib/constraints/admin_constrainer_spec.rb'
+ - 'spec/lib/constraints/group_url_constrainer_spec.rb'
+ - 'spec/lib/constraints/jira_encoded_url_constrainer_spec.rb'
+ - 'spec/lib/constraints/project_url_constrainer_spec.rb'
+ - 'spec/lib/constraints/user_url_constrainer_spec.rb'
+ - 'spec/lib/csv_builder_spec.rb'
+ - 'spec/lib/csv_builders/stream_spec.rb'
+ - 'spec/lib/extracts_path_spec.rb'
+ - 'spec/lib/feature_spec.rb'
+ - 'spec/lib/gitaly/server_spec.rb'
+ - 'spec/lib/gitlab/api_authentication/token_locator_spec.rb'
+ - 'spec/lib/gitlab/application_context_spec.rb'
+ - 'spec/lib/gitlab/application_rate_limiter_spec.rb'
+ - 'spec/lib/gitlab/asciidoc/include_processor_spec.rb'
+ - 'spec/lib/gitlab/auth/auth_finders_spec.rb'
+ - 'spec/lib/gitlab/auth/blocked_user_tracker_spec.rb'
+ - 'spec/lib/gitlab/auth/ldap/adapter_spec.rb'
+ - 'spec/lib/gitlab/auth/ldap/authentication_spec.rb'
+ - 'spec/lib/gitlab/authorized_keys_spec.rb'
+ - 'spec/lib/gitlab/avatar_cache_spec.rb'
+ - 'spec/lib/gitlab/background_migration/base_job_spec.rb'
+ - 'spec/lib/gitlab/background_migration/batching_strategies/base_strategy_spec.rb'
+ - 'spec/lib/gitlab/background_migration/job_coordinator_spec.rb'
+ - 'spec/lib/gitlab/background_migration/migrate_merge_request_diff_commit_users_spec.rb'
+ - 'spec/lib/gitlab/background_migration_spec.rb'
+ - 'spec/lib/gitlab/bitbucket_import/importer_spec.rb'
+ - 'spec/lib/gitlab/bitbucket_import/project_creator_spec.rb'
+ - 'spec/lib/gitlab/bitbucket_server_import/importer_spec.rb'
+ - 'spec/lib/gitlab/cache/import/caching_spec.rb'
+ - 'spec/lib/gitlab/changelog/committer_spec.rb'
+ - 'spec/lib/gitlab/chat/responder/base_spec.rb'
+ - 'spec/lib/gitlab/chat/responder_spec.rb'
+ - 'spec/lib/gitlab/ci/badge/coverage/metadata_spec.rb'
+ - 'spec/lib/gitlab/ci/badge/coverage/template_spec.rb'
+ - 'spec/lib/gitlab/ci/badge/pipeline/metadata_spec.rb'
+ - 'spec/lib/gitlab/ci/badge/pipeline/template_spec.rb'
+ - 'spec/lib/gitlab/ci/build/cache_spec.rb'
+ - 'spec/lib/gitlab/ci/build/policy/changes_spec.rb'
+ - 'spec/lib/gitlab/ci/build/policy/variables_spec.rb'
+ - 'spec/lib/gitlab/ci/build/policy_spec.rb'
+ - 'spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb'
+ - 'spec/lib/gitlab/ci/build/rules/rule_spec.rb'
+ - 'spec/lib/gitlab/ci/build/rules_spec.rb'
+ - 'spec/lib/gitlab/ci/build/status/reason_spec.rb'
+ - 'spec/lib/gitlab/ci/config/entry/default_spec.rb'
+ - 'spec/lib/gitlab/ci/config/entry/job_spec.rb'
+ - 'spec/lib/gitlab/ci/config/entry/processable_spec.rb'
+ - 'spec/lib/gitlab/ci/config/external/context_spec.rb'
+ - 'spec/lib/gitlab/ci/config/external/file/local_spec.rb'
+ - 'spec/lib/gitlab/ci/config/external/file/project_spec.rb'
+ - 'spec/lib/gitlab/ci/config/external/rules_spec.rb'
+ - 'spec/lib/gitlab/ci/parsers/test/junit_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/chain/helpers_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/expression/lexeme/and_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/expression/lexeme/equals_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/expression/lexeme/or_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/preloader_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/quota/deployments_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/seed/build_spec.rb'
+ - 'spec/lib/gitlab/ci/reports/security/report_spec.rb'
+ - 'spec/lib/gitlab/ci/status/build/action_spec.rb'
+ - 'spec/lib/gitlab/ci/status/build/cancelable_spec.rb'
+ - 'spec/lib/gitlab/ci/status/build/canceled_spec.rb'
+ - 'spec/lib/gitlab/ci/status/build/created_spec.rb'
+ - 'spec/lib/gitlab/ci/status/build/erased_spec.rb'
+ - 'spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb'
+ - 'spec/lib/gitlab/ci/status/build/failed_spec.rb'
+ - 'spec/lib/gitlab/ci/status/build/pending_spec.rb'
+ - 'spec/lib/gitlab/ci/status/build/preparing_spec.rb'
+ - 'spec/lib/gitlab/ci/status/build/retried_spec.rb'
+ - 'spec/lib/gitlab/ci/status/build/retryable_spec.rb'
+ - 'spec/lib/gitlab/ci/status/build/skipped_spec.rb'
+ - 'spec/lib/gitlab/ci/status/build/stop_spec.rb'
+ - 'spec/lib/gitlab/ci/status/build/unschedule_spec.rb'
+ - 'spec/lib/gitlab/ci/status/canceled_spec.rb'
+ - 'spec/lib/gitlab/ci/status/core_spec.rb'
+ - 'spec/lib/gitlab/ci/status/created_spec.rb'
+ - 'spec/lib/gitlab/ci/status/factory_spec.rb'
+ - 'spec/lib/gitlab/ci/status/failed_spec.rb'
+ - 'spec/lib/gitlab/ci/status/manual_spec.rb'
+ - 'spec/lib/gitlab/ci/status/pending_spec.rb'
+ - 'spec/lib/gitlab/ci/status/pipeline/blocked_spec.rb'
+ - 'spec/lib/gitlab/ci/status/pipeline/delayed_spec.rb'
+ - 'spec/lib/gitlab/ci/status/preparing_spec.rb'
+ - 'spec/lib/gitlab/ci/status/running_spec.rb'
+ - 'spec/lib/gitlab/ci/status/scheduled_spec.rb'
+ - 'spec/lib/gitlab/ci/status/skipped_spec.rb'
+ - 'spec/lib/gitlab/ci/status/stage/play_manual_spec.rb'
+ - 'spec/lib/gitlab/ci/status/success_spec.rb'
+ - 'spec/lib/gitlab/ci/status/success_warning_spec.rb'
+ - 'spec/lib/gitlab/ci/status/waiting_for_resource_spec.rb'
+ - 'spec/lib/gitlab/ci/trace/archive_spec.rb'
+ - 'spec/lib/gitlab/ci/trace/remote_checksum_spec.rb'
+ - 'spec/lib/gitlab/ci/trace/stream_spec.rb'
+ - 'spec/lib/gitlab/ci/variables/builder_spec.rb'
+ - 'spec/lib/gitlab/cleanup/orphan_lfs_file_references_spec.rb'
+ - 'spec/lib/gitlab/cleanup/project_uploads_spec.rb'
+ - 'spec/lib/gitlab/cleanup/remote_uploads_spec.rb'
+ - 'spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb'
+ - 'spec/lib/gitlab/cluster/rack_timeout_observer_spec.rb'
+ - 'spec/lib/gitlab/color_schemes_spec.rb'
+ - 'spec/lib/gitlab/conan_token_spec.rb'
+ - 'spec/lib/gitlab/config/entry/configurable_spec.rb'
+ - 'spec/lib/gitlab/config/entry/factory_spec.rb'
+ - 'spec/lib/gitlab/config/entry/simplifiable_spec.rb'
+ - 'spec/lib/gitlab/config/entry/unspecified_spec.rb'
+ - 'spec/lib/gitlab/config/entry/validator_spec.rb'
+ - 'spec/lib/gitlab/conflict/file_spec.rb'
+ - 'spec/lib/gitlab/cross_project_access/check_collection_spec.rb'
+ - 'spec/lib/gitlab/database/async_indexes_spec.rb'
+ - 'spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb'
+ - 'spec/lib/gitlab/database/batch_count_spec.rb'
+ - 'spec/lib/gitlab/database/count_spec.rb'
+ - 'spec/lib/gitlab/database/each_database_spec.rb'
+ - 'spec/lib/gitlab/database/load_balancing/configuration_spec.rb'
+ - 'spec/lib/gitlab/database/load_balancing/connection_proxy_spec.rb'
+ - 'spec/lib/gitlab/database/load_balancing/host_list_spec.rb'
+ - 'spec/lib/gitlab/database/load_balancing/host_spec.rb'
+ - 'spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb'
+ - 'spec/lib/gitlab/database/load_balancing/rack_middleware_spec.rb'
+ - 'spec/lib/gitlab/database/load_balancing/resolver_spec.rb'
+ - 'spec/lib/gitlab/database/load_balancing/service_discovery_spec.rb'
+ - 'spec/lib/gitlab/database/load_balancing/setup_spec.rb'
+ - 'spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb'
+ - 'spec/lib/gitlab/database/load_balancing_spec.rb'
+ - 'spec/lib/gitlab/database/migration_helpers_spec.rb'
+ - 'spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb'
+ - 'spec/lib/gitlab/database/migrations/instrumentation_spec.rb'
+ - 'spec/lib/gitlab/database/migrations/lock_retry_mixin_spec.rb'
+ - 'spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb'
+ - 'spec/lib/gitlab/database/migrations/observers/total_database_size_change_spec.rb'
+ - 'spec/lib/gitlab/database/migrations/runner_spec.rb'
+ - 'spec/lib/gitlab/database/partitioning/monthly_strategy_spec.rb'
+ - 'spec/lib/gitlab/database/partitioning/partition_manager_spec.rb'
+ - 'spec/lib/gitlab/database/partitioning/partition_monitoring_spec.rb'
+ - 'spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb'
+ - 'spec/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table_spec.rb'
+ - 'spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb'
+ - 'spec/lib/gitlab/database/partitioning_spec.rb'
+ - 'spec/lib/gitlab/database/postgresql_adapter/empty_query_ping_spec.rb'
+ - 'spec/lib/gitlab/database/postgresql_database_tasks/load_schema_versions_mixin_spec.rb'
+ - 'spec/lib/gitlab/database/query_analyzer_spec.rb'
+ - 'spec/lib/gitlab/database/reindexing/grafana_notifier_spec.rb'
+ - 'spec/lib/gitlab/database/reindexing/reindex_concurrently_spec.rb'
+ - 'spec/lib/gitlab/database/shared_model_spec.rb'
+ - 'spec/lib/gitlab/database_spec.rb'
+ - 'spec/lib/gitlab/diff/file_collection_sorter_spec.rb'
+ - 'spec/lib/gitlab/diff/file_spec.rb'
+ - 'spec/lib/gitlab/diff/line_spec.rb'
+ - 'spec/lib/gitlab/diff/position_tracer_spec.rb'
+ - 'spec/lib/gitlab/doctor/secrets_spec.rb'
+ - 'spec/lib/gitlab/email/handler/service_desk_handler_spec.rb'
+ - 'spec/lib/gitlab/email/receiver_spec.rb'
+ - 'spec/lib/gitlab/email/service_desk_receiver_spec.rb'
+ - 'spec/lib/gitlab/error_tracking/processor/sidekiq_processor_spec.rb'
+ - 'spec/lib/gitlab/etag_caching/middleware_spec.rb'
+ - 'spec/lib/gitlab/etag_caching/router/graphql_spec.rb'
+ - 'spec/lib/gitlab/etag_caching/router/rails_spec.rb'
+ - 'spec/lib/gitlab/etag_caching/router_spec.rb'
+ - 'spec/lib/gitlab/event_store/store_spec.rb'
+ - 'spec/lib/gitlab/experiment/rollout/feature_spec.rb'
+ - 'spec/lib/gitlab/external_authorization/access_spec.rb'
+ - 'spec/lib/gitlab/external_authorization/logger_spec.rb'
+ - 'spec/lib/gitlab/faraday/error_callback_spec.rb'
+ - 'spec/lib/gitlab/feature_categories_spec.rb'
+ - 'spec/lib/gitlab/git/blob_spec.rb'
+ - 'spec/lib/gitlab/git/commit_spec.rb'
+ - 'spec/lib/gitlab/git/repository_spec.rb'
+ - 'spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb'
+ - 'spec/lib/gitlab/git/tag_spec.rb'
+ - 'spec/lib/gitlab/git_access_snippet_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/commit_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/conflict_files_stitcher_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/health_check_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/ref_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/remote_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/repository_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client_spec.rb'
+ - 'spec/lib/gitlab/github_import/bulk_importing_spec.rb'
+ - 'spec/lib/gitlab/github_import/client_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/diff_notes_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/issue_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/issues_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/labels_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/note_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/notes_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/pull_request_merged_by_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/pull_requests_merged_by_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/pull_requests_reviews_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/releases_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/repository_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/single_endpoint_diff_notes_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/single_endpoint_issue_notes_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/single_endpoint_merge_request_notes_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/issuable_finder_spec.rb'
+ - 'spec/lib/gitlab/github_import/markdown_text_spec.rb'
+ - 'spec/lib/gitlab/github_import/milestone_finder_spec.rb'
+ - 'spec/lib/gitlab/github_import/object_counter_spec.rb'
+ - 'spec/lib/gitlab/github_import/page_counter_spec.rb'
+ - 'spec/lib/gitlab/github_import/parallel_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/parallel_scheduling_spec.rb'
+ - 'spec/lib/gitlab/github_import/representation/diff_note_spec.rb'
+ - 'spec/lib/gitlab/github_import/representation/to_hash_spec.rb'
+ - 'spec/lib/gitlab/github_import/sequential_importer_spec.rb'
+ - 'spec/lib/gitlab/github_import/user_finder_spec.rb'
+ - 'spec/lib/gitlab/github_import_spec.rb'
+ - 'spec/lib/gitlab/gon_helper_spec.rb'
+ - 'spec/lib/gitlab/gpg/commit_spec.rb'
+ - 'spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb'
+ - 'spec/lib/gitlab/gpg_spec.rb'
+ - 'spec/lib/gitlab/grape_logging/loggers/exception_logger_spec.rb'
+ - 'spec/lib/gitlab/grape_logging/loggers/perf_logger_spec.rb'
+ - 'spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb'
+ - 'spec/lib/gitlab/grape_logging/loggers/urgency_logger_spec.rb'
+ - 'spec/lib/gitlab/graphql/authorize/object_authorization_spec.rb'
+ - 'spec/lib/gitlab/graphql/batch_key_spec.rb'
+ - 'spec/lib/gitlab/graphql/generic_tracing_spec.rb'
+ - 'spec/lib/gitlab/graphql/lazy_spec.rb'
+ - 'spec/lib/gitlab/graphql/loaders/issuable_loader_spec.rb'
+ - 'spec/lib/gitlab/graphql/present/field_extension_spec.rb'
+ - 'spec/lib/gitlab/graphql/timeout_spec.rb'
+ - 'spec/lib/gitlab/graphql/tracers/application_context_tracer_spec.rb'
+ - 'spec/lib/gitlab/graphql/tracers/timer_tracer_spec.rb'
+ - 'spec/lib/gitlab/health_checks/gitaly_check_spec.rb'
+ - 'spec/lib/gitlab/hook_data/base_builder_spec.rb'
+ - 'spec/lib/gitlab/hotlinking_detector_spec.rb'
+ - 'spec/lib/gitlab/import/import_failure_service_spec.rb'
+ - 'spec/lib/gitlab/import/metrics_spec.rb'
+ - 'spec/lib/gitlab/import_export/attribute_cleaner_spec.rb'
+ - 'spec/lib/gitlab/import_export/base/relation_factory_spec.rb'
+ - 'spec/lib/gitlab/import_export/decompressed_archive_size_validator_spec.rb'
+ - 'spec/lib/gitlab/import_export/group/relation_factory_spec.rb'
+ - 'spec/lib/gitlab/import_export/importer_spec.rb'
+ - 'spec/lib/gitlab/import_export/project/relation_factory_spec.rb'
+ - 'spec/lib/gitlab/import_export/project/sample/relation_factory_spec.rb'
+ - 'spec/lib/gitlab/import_export/project/tree_saver_spec.rb'
+ - 'spec/lib/gitlab/issuables_count_for_state_spec.rb'
+ - 'spec/lib/gitlab/issues/rebalancing/state_spec.rb'
+ - 'spec/lib/gitlab/jira/middleware_spec.rb'
+ - 'spec/lib/gitlab/jira_import/issue_serializer_spec.rb'
+ - 'spec/lib/gitlab/jira_import/labels_importer_spec.rb'
+ - 'spec/lib/gitlab/jira_import/metadata_collector_spec.rb'
+ - 'spec/lib/gitlab/jira_import_spec.rb'
+ - 'spec/lib/gitlab/job_waiter_spec.rb'
+ - 'spec/lib/gitlab/json_cache_spec.rb'
+ - 'spec/lib/gitlab/kas/client_spec.rb'
+ - 'spec/lib/gitlab/kubernetes/config_map_spec.rb'
+ - 'spec/lib/gitlab/kubernetes/default_namespace_spec.rb'
+ - 'spec/lib/gitlab/kubernetes/helm/api_spec.rb'
+ - 'spec/lib/gitlab/kubernetes/namespace_spec.rb'
+ - 'spec/lib/gitlab/lazy_spec.rb'
+ - 'spec/lib/gitlab/legacy_github_import/importer_spec.rb'
+ - 'spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb'
+ - 'spec/lib/gitlab/lets_encrypt/client_spec.rb'
+ - 'spec/lib/gitlab/mail_room/mail_room_spec.rb'
+ - 'spec/lib/gitlab/manifest_import/metadata_spec.rb'
+ - 'spec/lib/gitlab/markdown_cache/field_data_spec.rb'
+ - 'spec/lib/gitlab/merge_requests/message_generator_spec.rb'
+ - 'spec/lib/gitlab/merge_requests/mergeability/redis_interface_spec.rb'
+ - 'spec/lib/gitlab/metrics/boot_time_tracker_spec.rb'
+ - 'spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb'
+ - 'spec/lib/gitlab/metrics/elasticsearch_rack_middleware_spec.rb'
+ - 'spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb'
+ - 'spec/lib/gitlab/metrics/exporter/gc_request_middleware_spec.rb'
+ - 'spec/lib/gitlab/metrics/exporter/metrics_middleware_spec.rb'
+ - 'spec/lib/gitlab/metrics/rack_middleware_spec.rb'
+ - 'spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb'
+ - 'spec/lib/gitlab/metrics/samplers/database_sampler_spec.rb'
+ - 'spec/lib/gitlab/metrics/samplers/puma_sampler_spec.rb'
+ - 'spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb'
+ - 'spec/lib/gitlab/metrics/sli_spec.rb'
+ - 'spec/lib/gitlab/metrics/subscribers/action_cable_spec.rb'
+ - 'spec/lib/gitlab/metrics/subscribers/action_view_spec.rb'
+ - 'spec/lib/gitlab/metrics/subscribers/active_record_spec.rb'
+ - 'spec/lib/gitlab/metrics/subscribers/external_http_spec.rb'
+ - 'spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb'
+ - 'spec/lib/gitlab/metrics/subscribers/rack_attack_spec.rb'
+ - 'spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb'
+ - 'spec/lib/gitlab/metrics/web_transaction_spec.rb'
+ - 'spec/lib/gitlab/middleware/basic_health_check_spec.rb'
+ - 'spec/lib/gitlab/middleware/compressed_json_spec.rb'
+ - 'spec/lib/gitlab/middleware/go_spec.rb'
+ - 'spec/lib/gitlab/middleware/handle_malformed_strings_spec.rb'
+ - 'spec/lib/gitlab/middleware/multipart_spec.rb'
+ - 'spec/lib/gitlab/middleware/query_analyzer_spec.rb'
+ - 'spec/lib/gitlab/middleware/rails_queue_duration_spec.rb'
+ - 'spec/lib/gitlab/middleware/release_env_spec.rb'
+ - 'spec/lib/gitlab/middleware/sidekiq_web_static_spec.rb'
+ - 'spec/lib/gitlab/middleware/speedscope_spec.rb'
+ - 'spec/lib/gitlab/middleware/webhook_recursion_detection_spec.rb'
+ - 'spec/lib/gitlab/octokit/middleware_spec.rb'
+ - 'spec/lib/gitlab/optimistic_locking_spec.rb'
+ - 'spec/lib/gitlab/pages/settings_spec.rb'
+ - 'spec/lib/gitlab/pagination/cursor_based_keyset_spec.rb'
+ - 'spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb'
+ - 'spec/lib/gitlab/pagination/keyset/cursor_based_request_context_spec.rb'
+ - 'spec/lib/gitlab/pagination/keyset/cursor_pager_spec.rb'
+ - 'spec/lib/gitlab/pagination/keyset/pager_spec.rb'
+ - 'spec/lib/gitlab/pagination/keyset/request_context_spec.rb'
+ - 'spec/lib/gitlab/pagination/keyset_spec.rb'
+ - 'spec/lib/gitlab/pagination/offset_header_builder_spec.rb'
+ - 'spec/lib/gitlab/pagination/offset_pagination_spec.rb'
+ - 'spec/lib/gitlab/performance_bar/stats_spec.rb'
+ - 'spec/lib/gitlab/phabricator_import/conduit/maniphest_spec.rb'
+ - 'spec/lib/gitlab/phabricator_import/conduit/response_spec.rb'
+ - 'spec/lib/gitlab/phabricator_import/conduit/user_spec.rb'
+ - 'spec/lib/gitlab/polling_interval_spec.rb'
+ - 'spec/lib/gitlab/popen/runner_spec.rb'
+ - 'spec/lib/gitlab/process_management_spec.rb'
+ - 'spec/lib/gitlab/profiler_spec.rb'
+ - 'spec/lib/gitlab/prometheus/adapter_spec.rb'
+ - 'spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb'
+ - 'spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb'
+ - 'spec/lib/gitlab/prometheus/queries/matched_metric_query_spec.rb'
+ - 'spec/lib/gitlab/query_limiting/middleware_spec.rb'
+ - 'spec/lib/gitlab/quick_actions/dsl_spec.rb'
+ - 'spec/lib/gitlab/repository_cache_spec.rb'
+ - 'spec/lib/gitlab/routing_spec.rb'
+ - 'spec/lib/gitlab/runtime_spec.rb'
+ - 'spec/lib/gitlab/sanitizers/svg_spec.rb'
+ - 'spec/lib/gitlab/search/abuse_validators/no_abusive_coercion_from_string_validator_spec.rb'
+ - 'spec/lib/gitlab/search/abuse_validators/no_abusive_term_length_validator_spec.rb'
+ - 'spec/lib/gitlab/serializer/pagination_spec.rb'
+ - 'spec/lib/gitlab/sidekiq_config/cli_methods_spec.rb'
+ - 'spec/lib/gitlab/sidekiq_config/worker_spec.rb'
+ - 'spec/lib/gitlab/sidekiq_middleware/client_metrics_spec.rb'
+ - 'spec/lib/gitlab/sidekiq_middleware/query_analyzer_spec.rb'
+ - 'spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb'
+ - 'spec/lib/gitlab/sidekiq_middleware_spec.rb'
+ - 'spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb'
+ - 'spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb'
+ - 'spec/lib/gitlab/slash_commands/command_spec.rb'
+ - 'spec/lib/gitlab/slash_commands/deploy_spec.rb'
+ - 'spec/lib/gitlab/slash_commands/issue_close_spec.rb'
+ - 'spec/lib/gitlab/slash_commands/issue_comment_spec.rb'
+ - 'spec/lib/gitlab/slash_commands/issue_new_spec.rb'
+ - 'spec/lib/gitlab/slash_commands/issue_search_spec.rb'
+ - 'spec/lib/gitlab/slash_commands/issue_show_spec.rb'
+ - 'spec/lib/gitlab/slash_commands/presenters/run_spec.rb'
+ - 'spec/lib/gitlab/slash_commands/run_spec.rb'
+ - 'spec/lib/gitlab/spamcheck/client_spec.rb'
+ - 'spec/lib/gitlab/submodule_links_spec.rb'
+ - 'spec/lib/gitlab/suggestions/file_suggestion_spec.rb'
+ - 'spec/lib/gitlab/tab_width_spec.rb'
+ - 'spec/lib/gitlab/themes_spec.rb'
+ - 'spec/lib/gitlab/tracking_spec.rb'
+ - 'spec/lib/gitlab/usage/metric_spec.rb'
+ - 'spec/lib/gitlab/usage/metrics/instrumentations/database_metric_spec.rb'
+ - 'spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb'
+ - 'spec/lib/gitlab/usage_data_spec.rb'
+ - 'spec/lib/gitlab/utils/usage_data_spec.rb'
+ - 'spec/lib/gitlab/verify/job_artifacts_spec.rb'
+ - 'spec/lib/gitlab/verify/lfs_objects_spec.rb'
+ - 'spec/lib/gitlab/verify/uploads_spec.rb'
+ - 'spec/lib/gitlab/view/presenter/base_spec.rb'
+ - 'spec/lib/gitlab/view/presenter/delegated_spec.rb'
+ - 'spec/lib/gitlab/view/presenter/simple_spec.rb'
+ - 'spec/lib/gitlab/workhorse_spec.rb'
+ - 'spec/lib/gitlab_edition_spec.rb'
+ - 'spec/lib/gitlab_spec.rb'
+ - 'spec/lib/google_api/cloud_platform/client_spec.rb'
+ - 'spec/lib/peek/views/active_record_spec.rb'
+ - 'spec/lib/peek/views/bullet_detailed_spec.rb'
+ - 'spec/lib/peek/views/external_http_spec.rb'
+ - 'spec/lib/safe_zip/entry_spec.rb'
+ - 'spec/lib/serializers/unsafe_json_spec.rb'
+ - 'spec/lib/sidebars/projects/menus/analytics_menu_spec.rb'
+ - 'spec/mailers/emails/service_desk_spec.rb'
+ - 'spec/mailers/notify_spec.rb'
+ - 'spec/metrics_server/metrics_server_spec.rb'
+ - 'spec/migrations/20210406144743_backfill_total_tuple_count_for_batched_migrations_spec.rb'
+ - 'spec/models/active_session_spec.rb'
+ - 'spec/models/application_record_spec.rb'
+ - 'spec/models/badge_spec.rb'
+ - 'spec/models/badges/project_badge_spec.rb'
+ - 'spec/models/ci/build_spec.rb'
+ - 'spec/models/ci/build_trace_chunk_spec.rb'
+ - 'spec/models/ci/commit_with_pipeline_spec.rb'
+ - 'spec/models/ci/group_spec.rb'
+ - 'spec/models/ci/pipeline_spec.rb'
+ - 'spec/models/clusters/applications/runner_spec.rb'
+ - 'spec/models/clusters/cluster_spec.rb'
+ - 'spec/models/clusters/platforms/kubernetes_spec.rb'
+ - 'spec/models/commit_spec.rb'
+ - 'spec/models/concerns/atomic_internal_id_spec.rb'
+ - 'spec/models/concerns/legacy_bulk_insert_spec.rb'
+ - 'spec/models/concerns/prometheus_adapter_spec.rb'
+ - 'spec/models/concerns/token_authenticatable_strategies/base_spec.rb'
+ - 'spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb'
+ - 'spec/models/concerns/triggerable_hooks_spec.rb'
+ - 'spec/models/concerns/x509_serial_number_attribute_spec.rb'
+ - 'spec/models/design_management/design_action_spec.rb'
+ - 'spec/models/design_management/design_at_version_spec.rb'
+ - 'spec/models/diff_viewer/image_spec.rb'
+ - 'spec/models/environment_spec.rb'
+ - 'spec/models/event_spec.rb'
+ - 'spec/models/external_issue_spec.rb'
+ - 'spec/models/hooks/web_hook_spec.rb'
+ - 'spec/models/integrations/asana_spec.rb'
+ - 'spec/models/integrations/chat_message/pipeline_message_spec.rb'
+ - 'spec/models/integrations/jira_spec.rb'
+ - 'spec/models/integrations/microsoft_teams_spec.rb'
+ - 'spec/models/integrations/pipelines_email_spec.rb'
+ - 'spec/models/issue_spec.rb'
+ - 'spec/models/key_spec.rb'
+ - 'spec/models/merge_request_diff_commit_spec.rb'
+ - 'spec/models/merge_request_spec.rb'
+ - 'spec/models/packages/package_spec.rb'
+ - 'spec/models/plan_limits_spec.rb'
+ - 'spec/models/project_spec.rb'
+ - 'spec/models/ref_matcher_spec.rb'
+ - 'spec/models/release_highlight_spec.rb'
+ - 'spec/models/repository_spec.rb'
+ - 'spec/models/shard_spec.rb'
+ - 'spec/models/snippet_spec.rb'
+ - 'spec/models/ssh_host_key_spec.rb'
+ - 'spec/models/upload_spec.rb'
+ - 'spec/models/user_spec.rb'
+ - 'spec/policies/ci/bridge_policy_spec.rb'
+ - 'spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb'
+ - 'spec/presenters/group_member_presenter_spec.rb'
+ - 'spec/presenters/merge_request_presenter_spec.rb'
+ - 'spec/presenters/packages/nuget/search_results_presenter_spec.rb'
+ - 'spec/presenters/project_member_presenter_spec.rb'
+ - 'spec/presenters/project_presenter_spec.rb'
+ - 'spec/requests/api/avatar_spec.rb'
+ - 'spec/requests/api/container_registry_event_spec.rb'
+ - 'spec/requests/api/graphql/mutations/design_management/delete_spec.rb'
+ - 'spec/requests/api/graphql/mutations/snippets/create_spec.rb'
+ - 'spec/requests/api/graphql/project/cluster_agents_spec.rb'
+ - 'spec/requests/api/graphql/project/pipeline_spec.rb'
+ - 'spec/requests/api/helpers_spec.rb'
+ - 'spec/requests/api/import_bitbucket_server_spec.rb'
+ - 'spec/requests/api/import_github_spec.rb'
+ - 'spec/requests/api/internal/base_spec.rb'
+ - 'spec/requests/api/maven_packages_spec.rb'
+ - 'spec/requests/api/project_container_repositories_spec.rb'
+ - 'spec/requests/api/users_preferences_spec.rb'
+ - 'spec/requests/jwt_controller_spec.rb'
+ - 'spec/requests/whats_new_controller_spec.rb'
+ - 'spec/rubocop/migration_helpers_spec.rb'
+ - 'spec/scripts/setup/find_jh_branch_spec.rb'
+ - 'spec/serializers/accessibility_reports_comparer_serializer_spec.rb'
+ - 'spec/serializers/admin/user_entity_spec.rb'
+ - 'spec/serializers/base_discussion_entity_spec.rb'
+ - 'spec/serializers/build_action_entity_spec.rb'
+ - 'spec/serializers/build_details_entity_spec.rb'
+ - 'spec/serializers/build_trace_entity_spec.rb'
+ - 'spec/serializers/ci/dag_job_entity_spec.rb'
+ - 'spec/serializers/ci/dag_job_group_entity_spec.rb'
+ - 'spec/serializers/ci/dag_pipeline_entity_spec.rb'
+ - 'spec/serializers/ci/dag_stage_entity_spec.rb'
+ - 'spec/serializers/ci/daily_build_group_report_result_entity_spec.rb'
+ - 'spec/serializers/ci/daily_build_group_report_result_serializer_spec.rb'
+ - 'spec/serializers/ci/job_entity_spec.rb'
+ - 'spec/serializers/ci/pipeline_entity_spec.rb'
+ - 'spec/serializers/codequality_reports_comparer_serializer_spec.rb'
+ - 'spec/serializers/commit_entity_spec.rb'
+ - 'spec/serializers/container_repositories_serializer_spec.rb'
+ - 'spec/serializers/container_repository_entity_spec.rb'
+ - 'spec/serializers/container_tag_entity_spec.rb'
+ - 'spec/serializers/deployment_cluster_entity_spec.rb'
+ - 'spec/serializers/deployment_entity_spec.rb'
+ - 'spec/serializers/detailed_status_entity_spec.rb'
+ - 'spec/serializers/diffs_entity_spec.rb'
+ - 'spec/serializers/diffs_metadata_entity_spec.rb'
+ - 'spec/serializers/discussion_entity_spec.rb'
+ - 'spec/serializers/environment_entity_spec.rb'
+ - 'spec/serializers/environment_serializer_spec.rb'
+ - 'spec/serializers/environment_status_entity_spec.rb'
+ - 'spec/serializers/feature_flag_entity_spec.rb'
+ - 'spec/serializers/feature_flag_summary_entity_spec.rb'
+ - 'spec/serializers/group_child_entity_spec.rb'
+ - 'spec/serializers/group_child_serializer_spec.rb'
+ - 'spec/serializers/import/manifest_provider_repo_entity_spec.rb'
+ - 'spec/serializers/issuable_sidebar_extras_entity_spec.rb'
+ - 'spec/serializers/issue_board_entity_spec.rb'
+ - 'spec/serializers/issue_entity_spec.rb'
+ - 'spec/serializers/lfs_file_lock_entity_spec.rb'
+ - 'spec/serializers/linked_project_issue_entity_spec.rb'
+ - 'spec/serializers/merge_request_poll_cached_widget_entity_spec.rb'
+ - 'spec/serializers/merge_request_poll_widget_entity_spec.rb'
+ - 'spec/serializers/merge_request_sidebar_basic_entity_spec.rb'
+ - 'spec/serializers/merge_request_sidebar_extras_entity_spec.rb'
+ - 'spec/serializers/merge_request_widget_commit_entity_spec.rb'
+ - 'spec/serializers/merge_request_widget_entity_spec.rb'
+ - 'spec/serializers/merge_requests/pipeline_entity_spec.rb'
+ - 'spec/serializers/note_entity_spec.rb'
+ - 'spec/serializers/paginated_diff_entity_spec.rb'
+ - 'spec/serializers/pipeline_details_entity_spec.rb'
+ - 'spec/serializers/pipeline_serializer_spec.rb'
+ - 'spec/serializers/project_note_entity_spec.rb'
+ - 'spec/serializers/prometheus_alert_entity_spec.rb'
+ - 'spec/serializers/review_app_setup_entity_spec.rb'
+ - 'spec/serializers/runner_entity_spec.rb'
+ - 'spec/serializers/stage_entity_spec.rb'
+ - 'spec/serializers/suggestion_entity_spec.rb'
+ - 'spec/serializers/test_reports_comparer_serializer_spec.rb'
+ - 'spec/serializers/test_suite_entity_spec.rb'
+ - 'spec/serializers/trigger_variable_entity_spec.rb'
+ - 'spec/services/access_token_validation_service_spec.rb'
+ - 'spec/services/authorized_project_update/find_records_due_for_refresh_service_spec.rb'
+ - 'spec/services/award_emojis/toggle_service_spec.rb'
+ - 'spec/services/base_count_service_spec.rb'
+ - 'spec/services/bulk_imports/file_download_service_spec.rb'
+ - 'spec/services/ci/change_variables_service_spec.rb'
+ - 'spec/services/ci/create_pipeline_service_spec.rb'
+ - 'spec/services/ci/pipeline_creation/start_pipeline_service_spec.rb'
+ - 'spec/services/ci/prepare_build_service_spec.rb'
+ - 'spec/services/ci/process_pipeline_service_spec.rb'
+ - 'spec/services/ci/register_job_service_spec.rb'
+ - 'spec/services/ci/test_failure_history_service_spec.rb'
+ - 'spec/services/ci/update_build_queue_service_spec.rb'
+ - 'spec/services/ci/update_build_state_service_spec.rb'
+ - 'spec/services/clusters/aws/finalize_creation_service_spec.rb'
+ - 'spec/services/clusters/aws/provision_service_spec.rb'
+ - 'spec/services/clusters/aws/verify_provision_status_service_spec.rb'
+ - 'spec/services/clusters/build_kubernetes_namespace_service_spec.rb'
+ - 'spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb'
+ - 'spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb'
+ - 'spec/services/container_expiration_policies/cleanup_service_spec.rb'
+ - 'spec/services/deployments/create_service_spec.rb'
+ - 'spec/services/discussions/capture_diff_note_position_service_spec.rb'
+ - 'spec/services/event_create_service_spec.rb'
+ - 'spec/services/git/base_hooks_service_spec.rb'
+ - 'spec/services/git/process_ref_changes_service_spec.rb'
+ - 'spec/services/git/wiki_push_service/change_spec.rb'
+ - 'spec/services/ide/schemas_config_service_spec.rb'
+ - 'spec/services/import/bitbucket_server_service_spec.rb'
+ - 'spec/services/issues/create_service_spec.rb'
+ - 'spec/services/jira_connect_subscriptions/create_service_spec.rb'
+ - 'spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb'
+ - 'spec/services/merge_requests/approval_service_spec.rb'
+ - 'spec/services/merge_requests/build_service_spec.rb'
+ - 'spec/services/merge_requests/close_service_spec.rb'
+ - 'spec/services/merge_requests/merge_service_spec.rb'
+ - 'spec/services/merge_requests/post_merge_service_spec.rb'
+ - 'spec/services/merge_requests/refresh_service_spec.rb'
+ - 'spec/services/merge_requests/reopen_service_spec.rb'
+ - 'spec/services/merge_requests/request_review_service_spec.rb'
+ - 'spec/services/metrics/dashboard/clone_dashboard_service_spec.rb'
+ - 'spec/services/metrics/dashboard/update_dashboard_service_spec.rb'
+ - 'spec/services/metrics/users_starred_dashboards/create_service_spec.rb'
+ - 'spec/services/milestones/update_service_spec.rb'
+ - 'spec/services/namespaces/in_product_marketing_emails_service_spec.rb'
+ - 'spec/services/notes/create_service_spec.rb'
+ - 'spec/services/notes/render_service_spec.rb'
+ - 'spec/services/notification_service_spec.rb'
+ - 'spec/services/packages/generic/create_package_file_service_spec.rb'
+ - 'spec/services/packages/maven/find_or_create_package_service_spec.rb'
+ - 'spec/services/packages/maven/metadata/sync_service_spec.rb'
+ - 'spec/services/packages/nuget/metadata_extraction_service_spec.rb'
+ - 'spec/services/pages/zip_directory_service_spec.rb'
+ - 'spec/services/post_receive_service_spec.rb'
+ - 'spec/services/projects/branches_by_mode_service_spec.rb'
+ - 'spec/services/projects/create_service_spec.rb'
+ - 'spec/services/projects/destroy_service_spec.rb'
+ - 'spec/services/projects/import_service_spec.rb'
+ - 'spec/services/projects/operations/update_service_spec.rb'
+ - 'spec/services/projects/overwrite_project_service_spec.rb'
+ - 'spec/services/projects/update_pages_service_spec.rb'
+ - 'spec/services/projects/update_remote_mirror_service_spec.rb'
+ - 'spec/services/projects/update_repository_storage_service_spec.rb'
+ - 'spec/services/projects/update_service_spec.rb'
+ - 'spec/services/repositories/changelog_service_spec.rb'
+ - 'spec/services/search_service_spec.rb'
+ - 'spec/services/snippets/update_repository_storage_service_spec.rb'
+ - 'spec/services/spam/akismet_mark_as_spam_service_spec.rb'
+ - 'spec/services/spam/akismet_service_spec.rb'
+ - 'spec/services/spam/ham_service_spec.rb'
+ - 'spec/services/spam/spam_action_service_spec.rb'
+ - 'spec/services/spam/spam_params_spec.rb'
+ - 'spec/services/system_note_service_spec.rb'
+ - 'spec/services/system_notes/commit_service_spec.rb'
+ - 'spec/services/system_notes/issuables_service_spec.rb'
+ - 'spec/services/update_merge_request_metrics_service_spec.rb'
+ - 'spec/services/users/activity_service_spec.rb'
+ - 'spec/services/users/create_service_spec.rb'
+ - 'spec/services/users/refresh_authorized_projects_service_spec.rb'
+ - 'spec/services/users/update_service_spec.rb'
+ - 'spec/services/web_hook_service_spec.rb'
+ - 'spec/services/wiki_pages/base_service_spec.rb'
+ - 'spec/spam/concerns/has_spam_action_response_fields_spec.rb'
+ - 'spec/support/helpers/graphql_helpers.rb'
+ - 'spec/support/helpers/import_spec_helper.rb'
+ - 'spec/support/helpers/ldap_helpers.rb'
+ - 'spec/support/helpers/project_forks_helper.rb'
+ - 'spec/support/helpers/stub_metrics.rb'
+ - 'spec/support/helpers/stub_spam_services.rb'
+ - 'spec/support/import_export/common_util.rb'
+ - 'spec/support/prometheus/additional_metrics_shared_examples.rb'
+ - 'spec/support/shared_contexts/lib/gitlab/sidekiq_middleware/server_metrics_shared_context.rb'
+ - 'spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb'
+ - 'spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb'
+ - 'spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb'
+ - 'spec/support/shared_examples/graphql/mutations/http_integrations_shared_examples.rb'
+ - 'spec/support/shared_examples/lib/gitlab/config/inheritable_shared_examples.rb'
+ - 'spec/support/shared_examples/lib/gitlab/diff_file_collections_shared_examples.rb'
+ - 'spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb'
+ - 'spec/support/shared_examples/metrics/sampler_shared_examples.rb'
+ - 'spec/support/shared_examples/models/chat_integration_shared_examples.rb'
+ - 'spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb'
+ - 'spec/support/shared_examples/models/members_notifications_shared_example.rb'
+ - 'spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb'
+ - 'spec/support/shared_examples/namespaces/hierarchy_examples.rb'
+ - 'spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/debian_common_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/rack_attack_shared_examples.rb'
+ - 'spec/support/shared_examples/serializers/diff_file_entity_shared_examples.rb'
+ - 'spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb'
+ - 'spec/support/shared_examples/services/alert_management/alert_processing/notifications_shared_examples.rb'
+ - 'spec/support/shared_examples/services/alert_management_shared_examples.rb'
+ - 'spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb'
+ - 'spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb'
+ - 'spec/support/shared_examples/services/jira/requests/base_shared_examples.rb'
+ - 'spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb'
+ - 'spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb'
+ - 'spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb'
+ - 'spec/support/shared_examples/workers/background_migration_worker_shared_examples.rb'
+ - 'spec/support/shared_examples/workers/update_repository_move_shared_examples.rb'
+ - 'spec/tasks/gettext_rake_spec.rb'
+ - 'spec/tasks/gitlab/background_migrations_rake_spec.rb'
+ - 'spec/tasks/gitlab/check_rake_spec.rb'
+ - 'spec/tasks/gitlab/cleanup_rake_spec.rb'
+ - 'spec/tasks/gitlab/db_rake_spec.rb'
+ - 'spec/tasks/gitlab/packages/events_rake_spec.rb'
+ - 'spec/tasks/gitlab/setup_rake_spec.rb'
+ - 'spec/tooling/danger/project_helper_spec.rb'
+ - 'spec/tooling/lib/tooling/helm3_client_spec.rb'
+ - 'spec/tooling/lib/tooling/kubernetes_client_spec.rb'
+ - 'spec/tooling/rspec_flaky/example_spec.rb'
+ - 'spec/tooling/rspec_flaky/listener_spec.rb'
+ - 'spec/uploaders/file_uploader_spec.rb'
+ - 'spec/uploaders/object_storage_spec.rb'
+ - 'spec/uploaders/personal_file_uploader_spec.rb'
+ - 'spec/uploaders/records_uploads_spec.rb'
+ - 'spec/views/projects/issues/show.html.haml_spec.rb'
+ - 'spec/views/shared/milestones/_issuables.html.haml_spec.rb'
+ - 'spec/views/shared/wikis/_sidebar.html.haml_spec.rb'
+ - 'spec/workers/bulk_imports/export_request_worker_spec.rb'
+ - 'spec/workers/chat_notification_worker_spec.rb'
+ - 'spec/workers/ci/build_prepare_worker_spec.rb'
+ - 'spec/workers/ci/create_cross_project_pipeline_worker_spec.rb'
+ - 'spec/workers/ci/create_downstream_pipeline_worker_spec.rb'
+ - 'spec/workers/ci/pipeline_bridge_status_worker_spec.rb'
+ - 'spec/workers/ci/pipeline_success_unlock_artifacts_worker_spec.rb'
+ - 'spec/workers/ci/ref_delete_unlock_artifacts_worker_spec.rb'
+ - 'spec/workers/clusters/agents/delete_expired_events_worker_spec.rb'
+ - 'spec/workers/concerns/application_worker_spec.rb'
+ - 'spec/workers/concerns/gitlab/github_import/object_importer_spec.rb'
+ - 'spec/workers/concerns/gitlab/github_import/stage_methods_spec.rb'
+ - 'spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb'
+ - 'spec/workers/create_commit_signature_worker_spec.rb'
+ - 'spec/workers/environments/auto_stop_worker_spec.rb'
+ - 'spec/workers/error_tracking_issue_link_worker_spec.rb'
+ - 'spec/workers/gitlab/github_import/advance_stage_worker_spec.rb'
+ - 'spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb'
+ - 'spec/workers/gitlab/github_import/import_issue_worker_spec.rb'
+ - 'spec/workers/gitlab/github_import/import_note_worker_spec.rb'
+ - 'spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb'
+ - 'spec/workers/gitlab/github_import/stage/finish_import_worker_spec.rb'
+ - 'spec/workers/gitlab/github_import/stage/import_base_data_worker_spec.rb'
+ - 'spec/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker_spec.rb'
+ - 'spec/workers/gitlab/github_import/stage/import_lfs_objects_worker_spec.rb'
+ - 'spec/workers/gitlab/github_import/stage/import_notes_worker_spec.rb'
+ - 'spec/workers/gitlab/github_import/stage/import_pull_requests_merged_by_worker_spec.rb'
+ - 'spec/workers/gitlab/github_import/stage/import_pull_requests_reviews_worker_spec.rb'
+ - 'spec/workers/gitlab/github_import/stage/import_pull_requests_worker_spec.rb'
+ - 'spec/workers/gitlab/github_import/stage/import_repository_worker_spec.rb'
+ - 'spec/workers/gitlab_performance_bar_stats_worker_spec.rb'
+ - 'spec/workers/invalid_gpg_signature_update_worker_spec.rb'
+ - 'spec/workers/issues/rebalancing_worker_spec.rb'
+ - 'spec/workers/merge_request_mergeability_check_worker_spec.rb'
+ - 'spec/workers/new_issue_worker_spec.rb'
+ - 'spec/workers/new_merge_request_worker_spec.rb'
+ - 'spec/workers/pages_domain_ssl_renewal_worker_spec.rb'
+ - 'spec/workers/pages_domain_verification_worker_spec.rb'
+ - 'spec/workers/post_receive_spec.rb'
+ - 'spec/workers/project_cache_worker_spec.rb'
+ - 'spec/workers/projects/after_import_worker_spec.rb'
+ - 'spec/workers/propagate_integration_group_worker_spec.rb'
+ - 'spec/workers/propagate_integration_inherit_descendant_worker_spec.rb'
+ - 'spec/workers/propagate_integration_inherit_worker_spec.rb'
+ - 'spec/workers/propagate_integration_project_worker_spec.rb'
+ - 'spec/workers/purge_dependency_proxy_cache_worker_spec.rb'
+ - 'spec/workers/repository_import_worker_spec.rb'
+ - 'spec/workers/system_hook_push_worker_spec.rb'
diff --git a/.rubocop_todo/security/io_methods.yml b/.rubocop_todo/security/io_methods.yml
new file mode 100644
index 00000000000..936bb21d5dc
--- /dev/null
+++ b/.rubocop_todo/security/io_methods.yml
@@ -0,0 +1,6 @@
+---
+# Cop supports --autocorrect.
+Security/IoMethods:
+ Details: grace period
+ Exclude:
+ - 'db/migrate/20210301200959_init_schema.rb'
diff --git a/.rubocop_todo/style/accessor_grouping.yml b/.rubocop_todo/style/accessor_grouping.yml
index a4fae856953..a5586813885 100644
--- a/.rubocop_todo/style/accessor_grouping.yml
+++ b/.rubocop_todo/style/accessor_grouping.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/AccessorGrouping:
Exclude:
- 'app/finders/template_finder.rb'
@@ -57,7 +57,6 @@ Style/AccessorGrouping:
- 'lib/gitlab/http_io.rb'
- 'lib/gitlab/import_export/group/legacy_tree_restorer.rb'
- 'lib/gitlab/import_export/project/tree_restorer.rb'
- - 'lib/gitlab/merge_requests/commit_message_generator.rb'
- 'lib/gitlab/sidekiq_daemon/monitor.rb'
- 'lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb'
- 'lib/gitlab/suggestions/file_suggestion.rb'
diff --git a/.rubocop_todo/style/bare_percent_literals.yml b/.rubocop_todo/style/bare_percent_literals.yml
index cb40669ca02..e98660024db 100644
--- a/.rubocop_todo/style/bare_percent_literals.yml
+++ b/.rubocop_todo/style/bare_percent_literals.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/BarePercentLiterals:
Exclude:
- 'app/models/commit.rb'
@@ -75,7 +75,6 @@ Style/BarePercentLiterals:
- 'spec/lib/banzai/pipeline/full_pipeline_spec.rb'
- 'spec/lib/banzai/pipeline/incident_management/timeline_event_pipeline_spec.rb'
- 'spec/lib/banzai/pipeline/plain_markdown_pipeline_spec.rb'
- - 'spec/lib/banzai/reference_parser/commit_parser_spec.rb'
- 'spec/lib/banzai/reference_parser/issue_parser_spec.rb'
- 'spec/lib/banzai/reference_parser/merge_request_parser_spec.rb'
- 'spec/lib/gitlab/diff/highlight_spec.rb'
diff --git a/.rubocop_todo/style/class_and_module_children.yml b/.rubocop_todo/style/class_and_module_children.yml
index bff827ba5d3..2303c5a1652 100644
--- a/.rubocop_todo/style/class_and_module_children.yml
+++ b/.rubocop_todo/style/class_and_module_children.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/ClassAndModuleChildren:
Exclude:
- 'app/components/pajamas/toggle_component.rb'
@@ -8,11 +8,6 @@ Style/ClassAndModuleChildren:
- 'app/controllers/admin/application_settings/appearances_controller.rb'
- 'app/controllers/admin/application_settings_controller.rb'
- 'app/controllers/admin/applications_controller.rb'
- - 'app/controllers/admin/background_jobs_controller.rb'
- - 'app/controllers/admin/background_migrations_controller.rb'
- - 'app/controllers/admin/batched_jobs_controller.rb'
- - 'app/controllers/admin/broadcast_messages_controller.rb'
- - 'app/controllers/admin/ci/variables_controller.rb'
- 'app/controllers/admin/clusters/integrations_controller.rb'
- 'app/controllers/admin/clusters_controller.rb'
- 'app/controllers/admin/cohorts_controller.rb'
@@ -22,7 +17,6 @@ Style/ClassAndModuleChildren:
- 'app/controllers/admin/gitaly_servers_controller.rb'
- 'app/controllers/admin/groups_controller.rb'
- 'app/controllers/admin/health_check_controller.rb'
- - 'app/controllers/admin/hook_logs_controller.rb'
- 'app/controllers/admin/hooks_controller.rb'
- 'app/controllers/admin/identities_controller.rb'
- 'app/controllers/admin/impersonation_tokens_controller.rb'
@@ -47,7 +41,6 @@ Style/ClassAndModuleChildren:
- 'app/controllers/clusters/base_controller.rb'
- 'app/controllers/clusters/clusters_controller.rb'
- 'app/controllers/concerns/integrations/actions.rb'
- - 'app/controllers/concerns/integrations/hooks_execution.rb'
- 'app/controllers/concerns/metrics/dashboard/prometheus_api_proxy.rb'
- 'app/controllers/concerns/snippets/blobs_actions.rb'
- 'app/controllers/concerns/snippets/send_blob.rb'
@@ -149,7 +142,6 @@ Style/ClassAndModuleChildren:
- 'app/controllers/projects/ci/daily_build_group_report_results_controller.rb'
- 'app/controllers/projects/ci/lints_controller.rb'
- 'app/controllers/projects/ci/pipeline_editor_controller.rb'
- - 'app/controllers/projects/ci/secure_files_controller.rb'
- 'app/controllers/projects/cluster_agents_controller.rb'
- 'app/controllers/projects/clusters/integrations_controller.rb'
- 'app/controllers/projects/clusters_controller.rb'
@@ -178,7 +170,6 @@ Style/ClassAndModuleChildren:
- 'app/controllers/projects/google_cloud/gcp_regions_controller.rb'
- 'app/controllers/projects/google_cloud/revoke_oauth_controller.rb'
- 'app/controllers/projects/google_cloud/service_accounts_controller.rb'
- - 'app/controllers/projects/google_cloud_controller.rb'
- 'app/controllers/projects/grafana_api_controller.rb'
- 'app/controllers/projects/graphs_controller.rb'
- 'app/controllers/projects/group_links_controller.rb'
@@ -207,7 +198,6 @@ Style/ClassAndModuleChildren:
- 'app/controllers/projects/pipeline_schedules_controller.rb'
- 'app/controllers/projects/pipelines_controller.rb'
- 'app/controllers/projects/pipelines_settings_controller.rb'
- - 'app/controllers/projects/product_analytics_controller.rb'
- 'app/controllers/projects/project_members_controller.rb'
- 'app/controllers/projects/protected_branches_controller.rb'
- 'app/controllers/projects/protected_refs_controller.rb'
@@ -225,7 +215,6 @@ Style/ClassAndModuleChildren:
- 'app/controllers/projects/snippets/blobs_controller.rb'
- 'app/controllers/projects/snippets_controller.rb'
- 'app/controllers/projects/starrers_controller.rb'
- - 'app/controllers/projects/tags/releases_controller.rb'
- 'app/controllers/projects/tags_controller.rb'
- 'app/controllers/projects/templates_controller.rb'
- 'app/controllers/projects/terraform_controller.rb'
@@ -361,7 +350,6 @@ Style/ClassAndModuleChildren:
- 'app/workers/merge_requests/delete_source_branch_worker.rb'
- 'app/workers/merge_requests/handle_assignees_change_worker.rb'
- 'app/workers/merge_requests/resolve_todos_worker.rb'
- - 'config/initializers/active_record_data_types.rb'
- 'config/initializers/http_hostname_override.rb'
- 'config/initializers/httpclient_patch.rb'
- 'config/initializers/omniauth.rb'
@@ -429,12 +417,10 @@ Style/ClassAndModuleChildren:
- 'ee/app/controllers/groups/security/merge_commit_reports_controller.rb'
- 'ee/app/controllers/groups/sso_controller.rb'
- 'ee/app/controllers/groups/todos_controller.rb'
- - 'ee/app/controllers/groups/usage_quotas_controller.rb'
- 'ee/app/controllers/groups/wikis_controller.rb'
- 'ee/app/controllers/oauth/geo_auth_controller.rb'
- 'ee/app/controllers/profiles/billings_controller.rb'
- 'ee/app/controllers/profiles/slacks_controller.rb'
- - 'ee/app/controllers/profiles/usage_quotas_controller.rb'
- 'ee/app/controllers/projects/analytics/issues_analytics_controller.rb'
- 'ee/app/controllers/projects/analytics/merge_request_analytics_controller.rb'
- 'ee/app/controllers/projects/approver_groups_controller.rb'
@@ -500,7 +486,6 @@ Style/ClassAndModuleChildren:
- 'ee/app/models/geo/upload_registry.rb'
- 'ee/app/models/protected_branch/required_code_owners_section.rb'
- 'ee/app/models/protected_branch/unprotect_access_level.rb'
- - 'ee/app/models/protected_environment/deploy_access_level.rb'
- 'ee/app/serializers/vulnerabilities/feedback_entity.rb'
- 'ee/app/serializers/vulnerabilities/feedback_serializer.rb'
- 'ee/app/serializers/vulnerabilities/finding_diff_serializer.rb'
@@ -515,9 +500,7 @@ Style/ClassAndModuleChildren:
- 'ee/app/services/ee/projects/after_rename_service.rb'
- 'ee/app/services/ee/projects/disable_deploy_key_service.rb'
- 'ee/app/services/ee/projects/enable_deploy_key_service.rb'
- - 'ee/app/services/ee/projects/update_pages_service.rb'
- 'ee/db/fixtures/development/20_burndown.rb'
- - 'ee/db/fixtures/development/20_vulnerabilities.rb'
- 'ee/db/fixtures/development/21_dast_profiles.rb'
- 'ee/db/fixtures/development/30_customizable_cycle_analytics.rb'
- 'ee/db/fixtures/development/32_compliance_report_violations.rb'
@@ -536,7 +519,6 @@ Style/ClassAndModuleChildren:
- 'lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb'
- 'lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings.rb'
- 'lib/gitlab/background_migration/remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings.rb'
- - 'lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb'
- 'lib/gitlab/ci/badge/base.rb'
- 'lib/gitlab/ci/badge/coverage/metadata.rb'
- 'lib/gitlab/ci/badge/coverage/report.rb'
diff --git a/.rubocop_todo/style/conditional_assignment.yml b/.rubocop_todo/style/conditional_assignment.yml
index c9bbaaeb175..d16f2f7c3a7 100644
--- a/.rubocop_todo/style/conditional_assignment.yml
+++ b/.rubocop_todo/style/conditional_assignment.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/ConditionalAssignment:
Exclude:
- 'app/helpers/icons_helper.rb'
diff --git a/.rubocop_todo/style/each_for_simple_loop.yml b/.rubocop_todo/style/each_for_simple_loop.yml
index 5d495f890d2..e7f99d69528 100644
--- a/.rubocop_todo/style/each_for_simple_loop.yml
+++ b/.rubocop_todo/style/each_for_simple_loop.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/EachForSimpleLoop:
Exclude:
- 'ee/spec/lib/gitlab/insights/reducers/count_per_period_reducer_spec.rb'
diff --git a/.rubocop_todo/style/empty_else.yml b/.rubocop_todo/style/empty_else.yml
index 07e42692f60..bd30b4a7c50 100644
--- a/.rubocop_todo/style/empty_else.yml
+++ b/.rubocop_todo/style/empty_else.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/EmptyElse:
Exclude:
- 'app/controllers/concerns/issuable_collections_action.rb'
@@ -24,7 +24,6 @@ Style/EmptyElse:
- 'app/models/resource_timebox_event.rb'
- 'app/services/award_emojis/add_service.rb'
- 'app/services/merge_requests/update_service.rb'
- - 'app/workers/gitlab/github_import/stage/import_issue_events_worker.rb'
- 'app/workers/post_receive.rb'
- 'config/initializers/doorkeeper_openid_connect.rb'
- 'ee/app/controllers/admin/audit_logs_controller.rb'
@@ -37,7 +36,6 @@ Style/EmptyElse:
- 'ee/app/services/gitlab_subscriptions/check_future_renewal_service.rb'
- 'ee/app/services/projects/update_mirror_service.rb'
- 'ee/app/workers/gitlab_subscriptions/notify_seats_exceeded_worker.rb'
- - 'ee/db/fixtures/development/20_vulnerabilities.rb'
- 'ee/lib/elastic/latest/note_instance_proxy.rb'
- 'ee/lib/gitlab/analytics/cycle_analytics/summary/change_failure_rate.rb'
- 'ee/lib/gitlab/geo/oauth/logout_token.rb'
diff --git a/.rubocop_todo/style/empty_method.yml b/.rubocop_todo/style/empty_method.yml
index 300d8678719..0c7eb5c0d92 100644
--- a/.rubocop_todo/style/empty_method.yml
+++ b/.rubocop_todo/style/empty_method.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/EmptyMethod:
Exclude:
- 'app/controllers/admin/application_settings/appearances_controller.rb'
@@ -11,7 +11,6 @@ Style/EmptyMethod:
- 'app/controllers/admin/runners_controller.rb'
- 'app/controllers/admin/topics_controller.rb'
- 'app/controllers/admin/usage_trends_controller.rb'
- - 'app/controllers/admin/users_controller.rb'
- 'app/controllers/concerns/boards_actions.rb'
- 'app/controllers/groups/milestones_controller.rb'
- 'app/controllers/groups/runners_controller.rb'
@@ -44,7 +43,6 @@ Style/EmptyMethod:
- 'app/controllers/projects/mattermosts_controller.rb'
- 'app/controllers/projects/pages_domains_controller.rb'
- 'app/controllers/projects/pipeline_schedules_controller.rb'
- - 'app/controllers/projects/product_analytics_controller.rb'
- 'app/controllers/projects/runners_controller.rb'
- 'app/controllers/projects/settings/integrations_controller.rb'
- 'app/controllers/projects/settings/packages_and_registries_controller.rb'
@@ -165,30 +163,3 @@ Style/EmptyMethod:
- 'qa/qa/service/cluster_provider/k3d.rb'
- 'qa/qa/service/cluster_provider/k3s.rb'
- 'qa/qa/service/cluster_provider/minikube.rb'
- - 'spec/controllers/concerns/check_rate_limit_spec.rb'
- - 'spec/controllers/concerns/issuable_actions_spec.rb'
- - 'spec/initializers/forbid_sidekiq_in_transactions_spec.rb'
- - 'spec/lib/api/helpers/rate_limiter_spec.rb'
- - 'spec/lib/gitlab/database/load_balancing/sidekiq_client_middleware_spec.rb'
- - 'spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb'
- - 'spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb'
- - 'spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb'
- - 'spec/lib/gitlab/database/postgresql_adapter/dump_schema_versions_mixin_spec.rb'
- - 'spec/lib/gitlab/database/postgresql_database_tasks/load_schema_versions_mixin_spec.rb'
- - 'spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb'
- - 'spec/lib/gitlab/repository_archive_rate_limiter_spec.rb'
- - 'spec/lib/gitlab/repository_cache_adapter_spec.rb'
- - 'spec/lib/gitlab/sidekiq_middleware/client_metrics_spec.rb'
- - 'spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb'
- - 'spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb'
- - 'spec/lib/gitlab/sidekiq_middleware/instrumentation_logger_spec.rb'
- - 'spec/lib/gitlab/sidekiq_middleware/query_analyzer_spec.rb'
- - 'spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb'
- - 'spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb'
- - 'spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb'
- - 'spec/lib/gitlab/ssh_public_key_spec.rb'
- - 'spec/lib/gitlab/utils/delegator_override/validator_spec.rb'
- - 'spec/lib/gitlab/utils/delegator_override_spec.rb'
- - 'spec/lib/gitlab/utils/override_spec.rb'
- - 'spec/lib/gitlab/utils/strong_memoize_spec.rb'
- - 'spec/workers/concerns/waitable_worker_spec.rb'
diff --git a/.rubocop_todo/style/explicit_block_argument.yml b/.rubocop_todo/style/explicit_block_argument.yml
index 20e8c976fb7..50874266979 100644
--- a/.rubocop_todo/style/explicit_block_argument.yml
+++ b/.rubocop_todo/style/explicit_block_argument.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/ExplicitBlockArgument:
Exclude:
- 'app/controllers/admin/background_migrations_controller.rb'
@@ -38,7 +38,6 @@ Style/ExplicitBlockArgument:
- 'lib/gitlab/application_context.rb'
- 'lib/gitlab/authorized_keys.rb'
- 'lib/gitlab/cache.rb'
- - 'lib/gitlab/ci/build/artifacts/metadata/entry.rb'
- 'lib/gitlab/ci/reports/test_suite.rb'
- 'lib/gitlab/ci/variables/collection.rb'
- 'lib/gitlab/cleanup/remote_uploads.rb'
@@ -74,16 +73,6 @@ Style/ExplicitBlockArgument:
- 'lib/gitlab/sidekiq_status.rb'
- 'lib/gitlab/utils/measuring.rb'
- 'lib/tasks/config_lint.rake'
- - 'qa/qa/ee/page/insights/show.rb'
- - 'qa/qa/ee/page/operations_dashboard.rb'
- - 'qa/qa/ee/page/project/issue/show.rb'
- - 'qa/qa/ee/page/project/show.rb'
- - 'qa/qa/ee/page/project/wiki/show.rb'
- - 'qa/qa/flow/login.rb'
- - 'qa/qa/page/admin/menu.rb'
- - 'qa/qa/page/base.rb'
- - 'qa/qa/page/component/blob_content.rb'
- - 'qa/qa/page/group/settings/group_deploy_tokens.rb'
- 'qa/qa/page/profile/menu.rb'
- 'qa/qa/page/project/settings/deploy_keys.rb'
- 'qa/qa/page/project/settings/deploy_tokens.rb'
diff --git a/.rubocop_todo/style/float_division.yml b/.rubocop_todo/style/float_division.yml
index 7fd0cda469d..b5cc208b836 100644
--- a/.rubocop_todo/style/float_division.yml
+++ b/.rubocop_todo/style/float_division.yml
@@ -1,7 +1,7 @@
---
+# Cop supports --autocorrect.
Style/FloatDivision:
Exclude:
- 'ee/app/models/geo_node_status.rb'
- 'ee/app/models/namespaces/storage/root_size.rb'
- - 'qa/qa/support/formatters/allure_metadata_formatter.rb'
- 'qa/qa/tools/reliable_report.rb'
diff --git a/.rubocop_todo/style/format_string.yml b/.rubocop_todo/style/format_string.yml
index 4da3c32416c..c1ba754edca 100644
--- a/.rubocop_todo/style/format_string.yml
+++ b/.rubocop_todo/style/format_string.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/FormatString:
Exclude:
- 'app/components/diffs/overflow_warning_component.rb'
@@ -14,7 +14,6 @@ Style/FormatString:
- 'app/controllers/concerns/confirm_email_warning.rb'
- 'app/controllers/concerns/enforces_two_factor_authentication.rb'
- 'app/controllers/concerns/integrations/actions.rb'
- - 'app/controllers/concerns/integrations/hooks_execution.rb'
- 'app/controllers/concerns/membership_actions.rb'
- 'app/controllers/concerns/redirects_for_missing_path_on_tree.rb'
- 'app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb'
@@ -28,7 +27,6 @@ Style/FormatString:
- 'app/controllers/import/gitlab_groups_controller.rb'
- 'app/controllers/import/gitlab_projects_controller.rb'
- 'app/controllers/invites_controller.rb'
- - 'app/controllers/jwt_controller.rb'
- 'app/controllers/omniauth_callbacks_controller.rb'
- 'app/controllers/profiles/chat_names_controller.rb'
- 'app/controllers/profiles/emails_controller.rb'
@@ -48,7 +46,6 @@ Style/FormatString:
- 'app/finders/todos_finder.rb'
- 'app/graphql/mutations/release_asset_links/create.rb'
- 'app/helpers/auth_helper.rb'
- - 'app/helpers/blob_helper.rb'
- 'app/helpers/button_helper.rb'
- 'app/helpers/ci/builds_helper.rb'
- 'app/helpers/ci/pipelines_helper.rb'
@@ -65,7 +62,6 @@ Style/FormatString:
- 'app/helpers/members_helper.rb'
- 'app/helpers/merge_requests_helper.rb'
- 'app/helpers/mirror_helper.rb'
- - 'app/helpers/notify_helper.rb'
- 'app/helpers/preferences_helper.rb'
- 'app/helpers/profiles_helper.rb'
- 'app/helpers/projects/project_members_helper.rb'
@@ -74,7 +70,6 @@ Style/FormatString:
- 'app/helpers/reminder_emails_helper.rb'
- 'app/helpers/search_helper.rb'
- 'app/helpers/ssh_keys_helper.rb'
- - 'app/helpers/storage_helper.rb'
- 'app/helpers/tags_helper.rb'
- 'app/helpers/time_helper.rb'
- 'app/helpers/timeboxes_helper.rb'
@@ -91,7 +86,6 @@ Style/FormatString:
- 'app/models/concerns/limitable.rb'
- 'app/models/concerns/metric_image_uploading.rb'
- 'app/models/concerns/spammable.rb'
- - 'app/models/concerns/timebox.rb'
- 'app/models/concerns/token_authenticatable_strategies/encrypted.rb'
- 'app/models/container_expiration_policy.rb'
- 'app/models/custom_emoji.rb'
@@ -111,7 +105,6 @@ Style/FormatString:
- 'app/models/integrations/emails_on_push.rb'
- 'app/models/integrations/ewm.rb'
- 'app/models/integrations/external_wiki.rb'
- - 'app/models/integrations/flowdock.rb'
- 'app/models/integrations/hangouts_chat.rb'
- 'app/models/integrations/irker.rb'
- 'app/models/integrations/jenkins.rb'
@@ -126,7 +119,6 @@ Style/FormatString:
- 'app/models/integrations/webex_teams.rb'
- 'app/models/integrations/youtrack.rb'
- 'app/models/integrations/zentao.rb'
- - 'app/models/merge_request.rb'
- 'app/models/merge_request_diff.rb'
- 'app/models/milestone.rb'
- 'app/models/pages_domain.rb'
@@ -134,30 +126,25 @@ Style/FormatString:
- 'app/models/resource_event.rb'
- 'app/models/sent_notification.rb'
- 'app/models/serverless/domain.rb'
- - 'app/models/snippet.rb'
- 'app/models/user.rb'
- 'app/models/wiki.rb'
- 'app/models/wiki_page.rb'
- 'app/models/work_items/parent_link.rb'
- 'app/presenters/ci/pipeline_presenter.rb'
+ - 'app/presenters/key_presenter.rb'
- 'app/presenters/merge_request_presenter.rb'
- 'app/presenters/project_presenter.rb'
- - 'app/presenters/key_presenter.rb'
- 'app/serializers/build_details_entity.rb'
- 'app/services/alert_management/alerts/update_service.rb'
- 'app/services/boards/lists/base_create_service.rb'
- - 'app/services/bulk_imports/file_download_service.rb'
- 'app/services/clusters/applications/check_progress_service.rb'
- - 'app/services/clusters/applications/check_uninstall_progress_service.rb'
- 'app/services/clusters/applications/install_service.rb'
- - 'app/services/clusters/applications/patch_service.rb'
- 'app/services/clusters/applications/upgrade_service.rb'
- 'app/services/clusters/aws/authorize_role_service.rb'
- 'app/services/clusters/aws/finalize_creation_service.rb'
- 'app/services/clusters/aws/verify_provision_status_service.rb'
- 'app/services/clusters/gcp/finalize_creation_service.rb'
- 'app/services/clusters/gcp/verify_provision_status_service.rb'
- - 'app/services/clusters/kubernetes/configure_istio_ingress_service.rb'
- 'app/services/concerns/update_repository_storage_methods.rb'
- 'app/services/concerns/validates_classification_label.rb'
- 'app/services/gravatar_service.rb'
@@ -195,12 +182,8 @@ Style/FormatString:
- 'config/initializers/rack_lineprof.rb'
- 'danger/roulette/Dangerfile'
- 'ee/app/components/billing/plan_component.rb'
- - 'ee/app/components/namespaces/free_user_cap/alert_component.rb'
- - 'ee/app/components/namespaces/free_user_cap/personable.rb'
- - 'ee/app/components/namespaces/free_user_cap/personal_alert_component.rb'
- - 'ee/app/components/namespaces/free_user_cap/personal_preview_alert_component.rb'
- - 'ee/app/components/namespaces/free_user_cap/personal_usage_quota_limitations_alert_component.rb'
- - 'ee/app/components/namespaces/free_user_cap/preview_alert_component.rb'
+ - 'ee/app/components/namespaces/free_user_cap/enforcement_alert_component.rb'
+ - 'ee/app/components/namespaces/free_user_cap/notification_alert_component.rb'
- 'ee/app/components/namespaces/free_user_cap/usage_quota_alert_component.rb'
- 'ee/app/components/namespaces/free_user_cap/usage_quota_trial_alert_component.rb'
- 'ee/app/controllers/admin/elasticsearch_controller.rb'
@@ -213,7 +196,6 @@ Style/FormatString:
- 'ee/app/controllers/ee/repositories/git_http_client_controller.rb'
- 'ee/app/controllers/ee/repositories/lfs_api_controller.rb'
- 'ee/app/controllers/groups/saml_group_links_controller.rb'
- - 'ee/app/controllers/groups/settings/reporting_controller.rb'
- 'ee/app/controllers/groups/sso_controller.rb'
- 'ee/app/controllers/projects/requirements_management/requirements_controller.rb'
- 'ee/app/controllers/subscriptions/groups_controller.rb'
@@ -221,7 +203,6 @@ Style/FormatString:
- 'ee/app/helpers/billing_plans_helper.rb'
- 'ee/app/helpers/ee/application_helper.rb'
- 'ee/app/helpers/ee/geo_helper.rb'
- - 'ee/app/helpers/ee/groups/reporting_helper.rb'
- 'ee/app/helpers/ee/groups/settings_helper.rb'
- 'ee/app/helpers/ee/groups_helper.rb'
- 'ee/app/helpers/ee/import_helper.rb'
@@ -271,8 +252,6 @@ Style/FormatString:
- 'ee/app/services/vulnerability_external_issue_links/create_service.rb'
- 'ee/app/validators/user_existence_validator.rb'
- 'ee/lib/audit/details.rb'
- - 'ee/lib/ee/audit/project_changes_auditor.rb'
- - 'ee/lib/ee/audit/project_setting_changes_auditor.rb'
- 'ee/lib/ee/gitlab/checks/push_rules/branch_check.rb'
- 'ee/lib/ee/gitlab/checks/push_rules/commit_check.rb'
- 'ee/lib/ee/gitlab/ci/pipeline/chain/validate/security_orchestration_policy.rb'
@@ -301,7 +280,6 @@ Style/FormatString:
- 'lib/api/helpers/packages/conan/api_helpers.rb'
- 'lib/bulk_imports/network_error.rb'
- 'lib/bulk_imports/users_mapper.rb'
- - 'lib/flowdock/git/builder.rb'
- 'lib/gitlab/bitbucket_server_import/importer.rb'
- 'lib/gitlab/checks/push_file_count_check.rb'
- 'lib/gitlab/ci/ansi2json/line.rb'
@@ -314,7 +292,6 @@ Style/FormatString:
- 'lib/gitlab/console.rb'
- 'lib/gitlab/database/async_indexes/index_creator.rb'
- 'lib/gitlab/database/background_migration/batched_migration.rb'
- - 'lib/gitlab/database/migration_helpers.rb'
- 'lib/gitlab/database/partitioning/single_numeric_list_partition.rb'
- 'lib/gitlab/database/partitioning/time_partition.rb'
- 'lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb'
@@ -371,7 +348,6 @@ Style/FormatString:
- 'spec/models/integrations/datadog_spec.rb'
- 'spec/models/serverless/domain_spec.rb'
- 'spec/requests/api/graphql/project/jira_projects_spec.rb'
- - 'spec/services/clusters/applications/patch_service_spec.rb'
- 'spec/services/clusters/applications/upgrade_service_spec.rb'
- 'spec/services/groups/import_export/export_service_spec.rb'
- 'spec/services/projects/import_export/export_service_spec.rb'
diff --git a/.rubocop_todo/style/guard_clause.yml b/.rubocop_todo/style/guard_clause.yml
index f46f8c8f4f6..59aa38fc9f5 100644
--- a/.rubocop_todo/style/guard_clause.yml
+++ b/.rubocop_todo/style/guard_clause.yml
@@ -1,4 +1,5 @@
---
+# Cop supports --autocorrect.
Style/GuardClause:
Exclude:
- 'app/controllers/admin/users_controller.rb'
@@ -16,18 +17,9 @@ Style/GuardClause:
- 'app/controllers/concerns/issuable_actions.rb'
- 'app/controllers/concerns/issuable_collections.rb'
- 'app/controllers/concerns/oauth_applications.rb'
- - 'app/controllers/concerns/page_limiter.rb'
- - 'app/controllers/concerns/product_analytics_tracking.rb'
- - 'app/controllers/concerns/record_user_last_activity.rb'
- - 'app/controllers/concerns/routable_actions.rb'
- - 'app/controllers/concerns/snippets/blobs_actions.rb'
- - 'app/controllers/concerns/uploads_actions.rb'
- - 'app/controllers/dashboard/todos_controller.rb'
- 'app/controllers/groups/application_controller.rb'
- 'app/controllers/groups_controller.rb'
- - 'app/controllers/ide_controller.rb'
- 'app/controllers/import/gitea_controller.rb'
- - 'app/controllers/import/github_controller.rb'
- 'app/controllers/import/gitlab_controller.rb'
- 'app/controllers/import/manifest_controller.rb'
- 'app/controllers/omniauth_callbacks_controller.rb'
@@ -35,7 +27,6 @@ Style/GuardClause:
- 'app/controllers/profiles/two_factor_auths_controller.rb'
- 'app/controllers/projects/application_controller.rb'
- 'app/controllers/projects/blob_controller.rb'
- - 'app/controllers/projects/branches_controller.rb'
- 'app/controllers/projects/commit_controller.rb'
- 'app/controllers/projects/compare_controller.rb'
- 'app/controllers/projects/design_management/designs/resized_image_controller.rb'
@@ -69,7 +60,6 @@ Style/GuardClause:
- 'app/graphql/resolvers/blobs_resolver.rb'
- 'app/graphql/resolvers/board_list_issues_resolver.rb'
- 'app/graphql/resolvers/concerns/board_item_filterable.rb'
- - 'app/graphql/resolvers/concerns/issue_resolver_arguments.rb'
- 'app/graphql/resolvers/concerns/time_frame_arguments.rb'
- 'app/graphql/resolvers/projects/jira_projects_resolver.rb'
- 'app/graphql/types/ci/job_type.rb'
@@ -123,22 +113,17 @@ Style/GuardClause:
- 'app/models/diff_viewer/base.rb'
- 'app/models/environment.rb'
- 'app/models/error_tracking/project_error_tracking_setting.rb'
- - 'app/models/experiment_subject.rb'
- 'app/models/external_pull_request.rb'
- 'app/models/generic_commit_status.rb'
- 'app/models/grafana_integration.rb'
- - 'app/models/integrations/bamboo.rb'
- 'app/models/integrations/base_issue_tracker.rb'
- 'app/models/integrations/base_third_party_wiki.rb'
- 'app/models/integrations/confluence.rb'
- 'app/models/integrations/datadog.rb'
- 'app/models/integrations/emails_on_push.rb'
- 'app/models/integrations/field.rb'
- - 'app/models/integrations/harbor.rb'
- - 'app/models/integrations/jenkins.rb'
- 'app/models/integrations/jira.rb'
- 'app/models/integrations/pipelines_email.rb'
- - 'app/models/integrations/teamcity.rb'
- 'app/models/internal_id.rb'
- 'app/models/issue.rb'
- 'app/models/member.rb'
@@ -170,7 +155,6 @@ Style/GuardClause:
- 'app/models/snippet_input_action.rb'
- 'app/models/user.rb'
- 'app/models/users/in_product_marketing_email.rb'
- - 'app/models/wiki.rb'
- 'app/models/work_item.rb'
- 'app/models/work_items/parent_link.rb'
- 'app/presenters/ci/pipeline_presenter.rb'
@@ -194,7 +178,6 @@ Style/GuardClause:
- 'app/services/commits/create_service.rb'
- 'app/services/concerns/alert_management/alert_processing.rb'
- 'app/services/concerns/ci/job_token_scope/edit_scope_validations.rb'
- - 'app/services/concerns/rate_limited_service.rb'
- 'app/services/concerns/update_repository_storage_methods.rb'
- 'app/services/concerns/validates_classification_label.rb'
- 'app/services/deployments/update_environment_service.rb'
@@ -221,7 +204,6 @@ Style/GuardClause:
- 'app/services/merge_requests/add_spent_time_service.rb'
- 'app/services/merge_requests/base_service.rb'
- 'app/services/merge_requests/build_service.rb'
- - 'app/services/merge_requests/handle_assignees_change_service.rb'
- 'app/services/merge_requests/merge_base_service.rb'
- 'app/services/merge_requests/merge_service.rb'
- 'app/services/merge_requests/mergeability_check_service.rb'
@@ -260,8 +242,6 @@ Style/GuardClause:
- 'app/uploaders/file_uploader.rb'
- 'app/validators/abstract_path_validator.rb'
- 'app/validators/any_field_validator.rb'
- - 'app/validators/array_members_validator.rb'
- - 'app/validators/branch_filter_validator.rb'
- 'app/validators/certificate_fingerprint_validator.rb'
- 'app/validators/certificate_key_validator.rb'
- 'app/validators/certificate_validator.rb'
@@ -292,7 +272,6 @@ Style/GuardClause:
- 'config/initializers/devise_dynamic_password_length_validation.rb'
- 'config/initializers/google_api_client_patch.rb'
- 'config/initializers/postgresql_cte.rb'
- - 'config/initializers/wikicloth_redos_patch.rb'
- 'config/object_store_settings.rb'
- 'danger/feature_flag/Dangerfile'
- 'db/migrate/20210302212623_rename_vuln_fingerprints_indexes.rb'
@@ -322,13 +301,11 @@ Style/GuardClause:
- 'ee/app/controllers/projects/security/policies_controller.rb'
- 'ee/app/controllers/projects/settings/slacks_controller.rb'
- 'ee/app/controllers/smartcard_controller.rb'
- - 'ee/app/finders/ee/notes_finder.rb'
- 'ee/app/finders/ee/template_finder.rb'
- 'ee/app/finders/iterations_finder.rb'
- 'ee/app/finders/security/vulnerabilities_finder.rb'
- 'ee/app/graphql/mutations/concerns/mutations/shared_epic_arguments.rb'
- 'ee/app/graphql/mutations/iterations/create.rb'
- - 'ee/app/graphql/mutations/iterations/update.rb'
- 'ee/app/graphql/mutations/projects/set_locked.rb'
- 'ee/app/graphql/resolvers/analytics/devops_adoption/enabled_namespaces_resolver.rb'
- 'ee/app/graphql/resolvers/epics_resolver.rb'
@@ -374,12 +351,10 @@ Style/GuardClause:
- 'ee/app/models/incident_management/escalation_rule.rb'
- 'ee/app/models/incident_management/oncall_rotation.rb'
- 'ee/app/models/ip_restriction.rb'
- - 'ee/app/models/iterations/cadence.rb'
- 'ee/app/models/namespace_limit.rb'
- 'ee/app/models/preloaders/environments/protected_environment_preloader.rb'
- 'ee/app/models/protected_environment.rb'
- 'ee/app/models/protected_environments/deploy_access_level.rb'
- - 'ee/app/models/sbom/occurrence.rb'
- 'ee/app/models/users_security_dashboard_project.rb'
- 'ee/app/models/vulnerabilities/feedback.rb'
- 'ee/app/presenters/ee/merge_request_presenter.rb'
@@ -426,7 +401,6 @@ Style/GuardClause:
- 'ee/app/services/iterations/delete_service.rb'
- 'ee/app/services/merge_trains/check_status_service.rb'
- 'ee/app/services/merge_trains/refresh_merge_request_service.rb'
- - 'ee/app/services/merge_trains/refresh_service.rb'
- 'ee/app/services/namespaces/storage/email_notification_service.rb'
- 'ee/app/services/projects/update_mirror_service.rb'
- 'ee/app/services/security/override_uuids_service.rb'
@@ -445,12 +419,10 @@ Style/GuardClause:
- 'ee/db/geo/migrate/20180314175612_add_partial_index_to_project_registy_verification_failure_columns.rb'
- 'ee/db/geo/migrate/20180315222132_add_partial_index_to_project_registy_checksum_columns.rb'
- 'ee/db/geo/migrate/20180412213305_add_index_to_artifact_id_on_job_artifact_registry.rb'
- - 'ee/lib/ee/api/ci/job_artifacts.rb'
- 'ee/lib/ee/api/features.rb'
- 'ee/lib/ee/api/helpers/projects_helpers.rb'
- 'ee/lib/ee/api/projects.rb'
- 'ee/lib/ee/api/search.rb'
- - 'ee/lib/ee/audit/project_ci_cd_setting_changes_auditor.rb'
- 'ee/lib/ee/gitlab/auth/ldap/access.rb'
- 'ee/lib/ee/gitlab/auth/ldap/sync/group.rb'
- 'ee/lib/ee/gitlab/auth/o_auth/user.rb'
@@ -586,7 +558,6 @@ Style/GuardClause:
- 'lib/gitlab/gitaly_client/blobs_stitcher.rb'
- 'lib/gitlab/gitaly_client/conflicts_service.rb'
- 'lib/gitlab/gitaly_client/operation_service.rb'
- - 'lib/gitlab/gitaly_client/wiki_service.rb'
- 'lib/gitlab/github_import.rb'
- 'lib/gitlab/github_import/client.rb'
- 'lib/gitlab/github_import/importer/pull_request_importer.rb'
@@ -596,7 +567,6 @@ Style/GuardClause:
- 'lib/gitlab/gitlab_import/client.rb'
- 'lib/gitlab/gitlab_import/importer.rb'
- 'lib/gitlab/graphql/query_analyzers/ast/recursion_analyzer.rb'
- - 'lib/gitlab/i18n/metadata_entry.rb'
- 'lib/gitlab/i18n/po_linter.rb'
- 'lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb'
- 'lib/gitlab/import_export/base/object_builder.rb'
@@ -616,7 +586,6 @@ Style/GuardClause:
- 'lib/gitlab/legacy_github_import/client.rb'
- 'lib/gitlab/legacy_github_import/issuable_formatter.rb'
- 'lib/gitlab/marginalia.rb'
- - 'lib/gitlab/memory/watchdog.rb'
- 'lib/gitlab/metrics/samplers/ruby_sampler.rb'
- 'lib/gitlab/metrics/subscribers/action_cable.rb'
- 'lib/gitlab/metrics/subscribers/active_record.rb'
@@ -659,7 +628,6 @@ Style/GuardClause:
- 'lib/gitlab/utils/override.rb'
- 'lib/gitlab/webpack/manifest.rb'
- 'lib/mattermost/session.rb'
- - 'lib/release_highlights/validator/entry.rb'
- 'lib/safe_zip/entry.rb'
- 'lib/service_ping/devops_report.rb'
- 'lib/system_check/app/systemd_unit_files_or_init_script_up_to_date_check.rb'
@@ -670,7 +638,6 @@ Style/GuardClause:
- 'qa/qa/ee/resource/license.rb'
- 'qa/qa/mobile/page/main/menu.rb'
- 'qa/qa/mobile/page/sub_menus/common.rb'
- - 'qa/qa/page/base.rb'
- 'qa/qa/page/component/invite_members_modal.rb'
- 'qa/qa/page/component/select2.rb'
- 'qa/qa/page/component/snippet.rb'
@@ -679,7 +646,6 @@ Style/GuardClause:
- 'qa/qa/page/project/settings/deploy_tokens.rb'
- 'qa/qa/resource/api_fabricator.rb'
- 'qa/qa/resource/package.rb'
- - 'qa/qa/resource/project.rb'
- 'qa/qa/resource/registry_repository.rb'
- 'qa/qa/resource/reusable.rb'
- 'qa/qa/resource/user_gpg.rb'
@@ -688,20 +654,15 @@ Style/GuardClause:
- 'qa/qa/service/cluster_provider/gcloud.rb'
- 'qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/free_trial_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_audit_logs_1_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/3_create/repository/group_file_template_spec.rb'
- 'qa/qa/specs/helpers/feature_flag.rb'
- 'qa/qa/vendor/jenkins/job.rb'
- 'rubocop/cop/api/grape_array_missing_coerce.rb'
- 'rubocop/cop/gitlab/event_store_subscriber.rb'
- - 'rubocop/cop/gitlab/finder_with_find_by.rb'
- - 'rubocop/cop/gitlab/keys_first_and_values_first.rb'
- 'rubocop/cop/gitlab/policy_rule_boolean.rb'
- 'rubocop/cop/ignored_columns.rb'
- 'rubocop/cop/migration/add_limit_to_text_columns.rb'
- - 'rubocop/cop/migration/update_column_in_batches.rb'
- 'rubocop/cop/rspec/web_mock_enable.rb'
- - 'rubocop/cop/usage_data/histogram_with_large_table.rb'
- 'rubocop/cop/usage_data/large_table.rb'
- 'rubocop/routes_under_scope.rb'
- 'scripts/lint_templates_bash.rb'
diff --git a/.rubocop_todo/style/hash_as_last_array_item.yml b/.rubocop_todo/style/hash_as_last_array_item.yml
index aa22e9ed82b..911ed2f02c4 100644
--- a/.rubocop_todo/style/hash_as_last_array_item.yml
+++ b/.rubocop_todo/style/hash_as_last_array_item.yml
@@ -1,11 +1,10 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/HashAsLastArrayItem:
Exclude:
- 'app/controllers/admin/application_settings_controller.rb'
- 'app/controllers/admin/groups_controller.rb'
- 'app/controllers/admin/users_controller.rb'
- - 'app/controllers/boards/issues_controller.rb'
- 'app/controllers/concerns/issuable_actions.rb'
- 'app/controllers/concerns/issuable_collections.rb'
- 'app/controllers/profiles_controller.rb'
@@ -17,7 +16,6 @@ Style/HashAsLastArrayItem:
- 'app/controllers/projects/settings/operations_controller.rb'
- 'app/controllers/projects_controller.rb'
- 'app/graphql/resolvers/clusters/agents_resolver.rb'
- - 'app/graphql/resolvers/concerns/issue_resolver_arguments.rb'
- 'app/graphql/types/boards/board_issuable_input_base_type.rb'
- 'app/graphql/types/boards/board_issue_input_base_type.rb'
- 'app/helpers/namespaces_helper.rb'
@@ -35,7 +33,6 @@ Style/HashAsLastArrayItem:
- 'ee/app/controllers/ee/admin/groups_controller.rb'
- 'ee/app/controllers/ee/admin/users_controller.rb'
- 'ee/app/controllers/groups/epics_controller.rb'
- - 'ee/app/graphql/ee/resolvers/base_issues_resolver.rb'
- 'ee/app/graphql/types/epics/negated_epic_filter_input_type.rb'
- 'ee/app/models/ee/merge_request.rb'
- 'ee/app/models/ee/vulnerability.rb'
@@ -43,7 +40,6 @@ Style/HashAsLastArrayItem:
- 'ee/app/serializers/dashboard_environments_serializer.rb'
- 'ee/spec/finders/projects/integrations/jira/by_ids_finder_spec.rb'
- 'ee/spec/lib/ee/gitlab/ci/config/entry/needs_spec.rb'
- - 'ee/spec/lib/gitlab/geo/log_cursor/events/container_repository_updated_event_spec.rb'
- 'ee/spec/lib/gitlab/geo/log_cursor/events/design_repository_updated_event_spec.rb'
- 'ee/spec/models/ee/ci/job_artifact_spec.rb'
- 'lib/api/entities/project.rb'
diff --git a/.rubocop_todo/style/hash_each_methods.yml b/.rubocop_todo/style/hash_each_methods.yml
index cebfe5c0c7d..41e6c4608cc 100644
--- a/.rubocop_todo/style/hash_each_methods.yml
+++ b/.rubocop_todo/style/hash_each_methods.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/HashEachMethods:
Exclude:
- 'app/graphql/resolvers/concerns/caching_array_resolver.rb'
@@ -52,7 +52,6 @@ Style/HashEachMethods:
- 'ee/spec/helpers/application_helper_spec.rb'
- 'ee/spec/lib/gitlab/geo_spec.rb'
- 'lib/api/todos.rb'
- - 'lib/backup/manager.rb'
- 'lib/gitlab/changelog/release.rb'
- 'lib/gitlab/ci/parsers.rb'
- 'lib/gitlab/ci/reports/test_suite.rb'
@@ -60,16 +59,12 @@ Style/HashEachMethods:
- 'lib/gitlab/metrics/subscribers/active_record.rb'
- 'lib/gitlab/metrics/subscribers/load_balancing.rb'
- 'lib/gitlab/middleware/multipart.rb'
- - 'lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb'
- - 'lib/gitlab/usage_data.rb'
- 'spec/controllers/projects_controller_spec.rb'
- 'spec/factories/ci/reports/codequality_degradations.rb'
- 'spec/finders/packages/group_packages_finder_spec.rb'
- 'spec/helpers/application_helper_spec.rb'
- 'spec/lib/gitlab/ci/status/build/failed_spec.rb'
- 'spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb'
- - 'spec/lib/gitlab/usage_data_spec.rb'
- - 'spec/models/ci/build_spec.rb'
- 'spec/models/ci/job_artifact_spec.rb'
- 'spec/models/ci/resource_group_spec.rb'
- 'spec/models/clusters/cluster_spec.rb'
diff --git a/.rubocop_todo/style/if_inside_else.yml b/.rubocop_todo/style/if_inside_else.yml
deleted file mode 100644
index 40b71d7de04..00000000000
--- a/.rubocop_todo/style/if_inside_else.yml
+++ /dev/null
@@ -1,48 +0,0 @@
----
-Style/IfInsideElse:
- Exclude:
- - 'app/controllers/application_controller.rb'
- - 'app/controllers/passwords_controller.rb'
- - 'app/finders/projects_finder.rb'
- - 'app/helpers/diff_helper.rb'
- - 'app/helpers/issuables_helper.rb'
- - 'app/helpers/members_helper.rb'
- - 'app/helpers/search_helper.rb'
- - 'app/models/namespace.rb'
- - 'app/presenters/project_presenter.rb'
- - 'app/services/system_notes/commit_service.rb'
- - 'app/services/task_list_toggle_service.rb'
- - 'app/services/user_project_access_changed_service.rb'
- - 'app/uploaders/gitlab_uploader.rb'
- - 'config/settings.rb'
- - 'ee/app/controllers/ee/registrations/welcome_controller.rb'
- - 'ee/app/controllers/groups/omniauth_callbacks_controller.rb'
- - 'ee/app/models/ee/namespace.rb'
- - 'ee/app/models/iterations/cadence.rb'
- - 'ee/app/models/protected_environments/authorizable.rb'
- - 'ee/app/policies/ee/group_policy.rb'
- - 'ee/app/services/app_sec/dast/site_profiles/audit/update_service.rb'
- - 'ee/app/services/deployments/approval_service.rb'
- - 'ee/app/services/geo/framework_repository_sync_service.rb'
- - 'ee/app/services/geo/repository_base_sync_service.rb'
- - 'ee/app/services/gitlab_subscriptions/fetch_subscription_plans_service.rb'
- - 'ee/app/services/vulnerability_external_issue_links/create_service.rb'
- - 'ee/app/workers/elastic/project_transfer_worker.rb'
- - 'ee/app/workers/project_import_schedule_worker.rb'
- - 'ee/lib/gitlab/geo/base_batcher.rb'
- - 'lib/api/projects.rb'
- - 'lib/gitlab/auth.rb'
- - 'lib/gitlab/conflict/file.rb'
- - 'lib/gitlab/sql/pattern.rb'
- - 'lib/gitlab/usage/service_ping/payload_keys_processor.rb'
- - 'lib/service_ping/build_payload.rb'
- - 'lib/tasks/gitlab/cleanup.rake'
- - 'lib/tasks/gitlab/shell.rake'
- - 'qa/qa/resource/protected_branch.rb'
- - 'qa/qa/specs/helpers/feature_flag.rb'
- - 'rubocop/cop/migration/add_limit_to_text_columns.rb'
- - 'scripts/review_apps/automated_cleanup.rb'
- - 'spec/controllers/projects/merge_requests/conflicts_controller_spec.rb'
- - 'spec/support/import_export/export_file_helper.rb'
- - 'spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb'
- - 'spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb'
diff --git a/.rubocop_todo/style/if_unless_modifier.yml b/.rubocop_todo/style/if_unless_modifier.yml
index fc05a8bc163..5804fda39e1 100644
--- a/.rubocop_todo/style/if_unless_modifier.yml
+++ b/.rubocop_todo/style/if_unless_modifier.yml
@@ -1,26 +1,7 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/IfUnlessModifier:
Exclude:
- - 'app/channels/graphql_channel.rb'
- - 'app/controllers/admin/application_settings_controller.rb'
- - 'app/controllers/admin/projects_controller.rb'
- - 'app/controllers/admin/users_controller.rb'
- - 'app/controllers/application_controller.rb'
- - 'app/controllers/concerns/controller_with_cross_project_access_check.rb'
- - 'app/controllers/concerns/cycle_analytics_params.rb'
- - 'app/controllers/concerns/enforces_two_factor_authentication.rb'
- - 'app/controllers/concerns/issuable_actions.rb'
- - 'app/controllers/concerns/issuable_collections.rb'
- - 'app/controllers/concerns/issues_calendar.rb'
- - 'app/controllers/concerns/labels_as_hash.rb'
- - 'app/controllers/concerns/metrics/dashboard/prometheus_api_proxy.rb'
- - 'app/controllers/concerns/metrics_dashboard.rb'
- - 'app/controllers/concerns/notes_actions.rb'
- - 'app/controllers/concerns/oauth_applications.rb'
- - 'app/controllers/concerns/paginated_collection.rb'
- - 'app/controllers/concerns/record_user_last_activity.rb'
- - 'app/controllers/concerns/sorting_preference.rb'
- 'app/controllers/concerns/uploads_actions.rb'
- 'app/controllers/concerns/verifies_with_email.rb'
- 'app/controllers/concerns/wiki_actions.rb'
@@ -31,7 +12,6 @@ Style/IfUnlessModifier:
- 'app/controllers/groups_controller.rb'
- 'app/controllers/import/fogbugz_controller.rb'
- 'app/controllers/import/gitea_controller.rb'
- - 'app/controllers/import/github_controller.rb'
- 'app/controllers/import/gitlab_controller.rb'
- 'app/controllers/import/manifest_controller.rb'
- 'app/controllers/omniauth_callbacks_controller.rb'
@@ -43,7 +23,6 @@ Style/IfUnlessModifier:
- 'app/controllers/projects/branches_controller.rb'
- 'app/controllers/projects/commits_controller.rb'
- 'app/controllers/projects/cycle_analytics_controller.rb'
- - 'app/controllers/projects/deploy_keys_controller.rb'
- 'app/controllers/projects/design_management/designs_controller.rb'
- 'app/controllers/projects/imports_controller.rb'
- 'app/controllers/projects/issues_controller.rb'
@@ -93,7 +72,6 @@ Style/IfUnlessModifier:
- 'app/graphql/mutations/todos/mark_all_done.rb'
- 'app/graphql/resolvers/blobs_resolver.rb'
- 'app/graphql/resolvers/concerns/board_item_filterable.rb'
- - 'app/graphql/resolvers/concerns/issue_resolver_arguments.rb'
- 'app/graphql/resolvers/concerns/time_frame_arguments.rb'
- 'app/graphql/resolvers/project_pipeline_resolver.rb'
- 'app/graphql/resolvers/timelog_resolver.rb'
@@ -189,14 +167,10 @@ Style/IfUnlessModifier:
- 'app/models/grafana_integration.rb'
- 'app/models/group.rb'
- 'app/models/integrations/asana.rb'
- - 'app/models/integrations/bamboo.rb'
- 'app/models/integrations/base_chat_notification.rb'
- 'app/models/integrations/datadog.rb'
- - 'app/models/integrations/harbor.rb'
- - 'app/models/integrations/jenkins.rb'
- 'app/models/integrations/jira.rb'
- 'app/models/integrations/pushover.rb'
- - 'app/models/integrations/teamcity.rb'
- 'app/models/issue.rb'
- 'app/models/issue_email_participant.rb'
- 'app/models/label_note.rb'
@@ -259,7 +233,6 @@ Style/IfUnlessModifier:
- 'app/services/boards/lists/base_create_service.rb'
- 'app/services/boards/lists/list_service.rb'
- 'app/services/branches/validate_new_service.rb'
- - 'app/services/ci/create_pipeline_service.rb'
- 'app/services/ci/create_web_ide_terminal_service.rb'
- 'app/services/ci/job_token_scope/remove_project_service.rb'
- 'app/services/ci/parse_dotenv_artifact_service.rb'
@@ -271,7 +244,6 @@ Style/IfUnlessModifier:
- 'app/services/ci/runners/register_runner_service.rb'
- 'app/services/ci/update_build_state_service.rb'
- 'app/services/clusters/applications/base_service.rb'
- - 'app/services/clusters/applications/prometheus_update_service.rb'
- 'app/services/clusters/aws/fetch_credentials_service.rb'
- 'app/services/clusters/gcp/provision_service.rb'
- 'app/services/clusters/update_service.rb'
@@ -292,13 +264,11 @@ Style/IfUnlessModifier:
- 'app/services/error_tracking/collect_error_service.rb'
- 'app/services/git/process_ref_changes_service.rb'
- 'app/services/google_cloud/generate_pipeline_service.rb'
- - 'app/services/google_cloud/setup_cloudsql_instance_service.rb'
- 'app/services/groups/create_service.rb'
- 'app/services/groups/transfer_service.rb'
- 'app/services/groups/update_statistics_service.rb'
- 'app/services/ide/base_config_service.rb'
- 'app/services/import/bitbucket_server_service.rb'
- - 'app/services/import/github_service.rb'
- 'app/services/import/gitlab_projects/file_acquisition_strategies/remote_file.rb'
- 'app/services/issuable/bulk_update_service.rb'
- 'app/services/issuable/common_system_notes_service.rb'
@@ -368,12 +338,10 @@ Style/IfUnlessModifier:
- 'app/services/spam/spam_verdict_service.rb'
- 'app/services/system_notes/issuables_service.rb'
- 'app/services/tags/destroy_service.rb'
- - 'app/services/two_factor/destroy_service.rb'
- 'app/services/users/approve_service.rb'
- 'app/services/users/build_service.rb'
- 'app/services/users/respond_to_terms_service.rb'
- 'app/services/wikis/create_attachment_service.rb'
- - 'app/services/work_items/create_service.rb'
- 'app/services/work_items/parent_links/create_service.rb'
- 'app/services/work_items/task_list_reference_removal_service.rb'
- 'app/services/work_items/task_list_reference_replacement_service.rb'
@@ -382,7 +350,6 @@ Style/IfUnlessModifier:
- 'app/validators/abstract_path_validator.rb'
- 'app/validators/addressable_url_validator.rb'
- 'app/validators/any_field_validator.rb'
- - 'app/validators/branch_filter_validator.rb'
- 'app/validators/certificate_key_validator.rb'
- 'app/validators/certificate_validator.rb'
- 'app/validators/cluster_name_validator.rb'
@@ -399,9 +366,7 @@ Style/IfUnlessModifier:
- 'app/workers/concerns/application_worker.rb'
- 'app/workers/concerns/packages/cleanup_artifact_worker.rb'
- 'app/workers/concerns/project_start_import.rb'
- - 'app/workers/concerns/waitable_worker.rb'
- 'app/workers/concerns/worker_attributes.rb'
- - 'app/workers/database/batched_background_migration/single_database_worker.rb'
- 'app/workers/delete_container_repository_worker.rb'
- 'app/workers/file_hook_worker.rb'
- 'app/workers/google_cloud/create_cloudsql_instance_worker.rb'
@@ -427,10 +392,8 @@ Style/IfUnlessModifier:
- 'config/initializers/google_api_client_patch.rb'
- 'config/initializers/jira.rb'
- 'config/initializers/kaminari_active_record_relation_methods_with_limit.rb'
- - 'config/initializers/load_balancing.rb'
- 'config/initializers/remove_active_job_execute_callback.rb'
- 'config/initializers/seed_fu.rb'
- - 'config/initializers/sidekiq.rb'
- 'config/initializers/stackprof.rb'
- 'config/initializers/validate_database_config.rb'
- 'config/initializers_before_autoloader/002_sidekiq.rb'
@@ -477,15 +440,12 @@ Style/IfUnlessModifier:
- 'ee/app/controllers/projects/push_rules_controller.rb'
- 'ee/app/controllers/projects/settings/slacks_controller.rb'
- 'ee/app/controllers/trials_controller.rb'
- - 'ee/app/finders/iterations_finder.rb'
- 'ee/app/finders/merge_trains_finder.rb'
- 'ee/app/finders/security/pipeline_vulnerabilities_finder.rb'
- 'ee/app/finders/security/vulnerabilities_finder.rb'
- - 'ee/app/graphql/ee/resolvers/base_issues_resolver.rb'
- 'ee/app/graphql/mutations/audit_events/external_audit_event_destinations/create.rb'
- 'ee/app/graphql/mutations/audit_events/external_audit_event_destinations/destroy.rb'
- 'ee/app/graphql/mutations/boards/scoped_board_mutation.rb'
- - 'ee/app/graphql/mutations/dast_site_profiles/create.rb'
- 'ee/app/graphql/mutations/iterations/update.rb'
- 'ee/app/graphql/mutations/projects/set_locked.rb'
- 'ee/app/graphql/resolvers/analytics/devops_adoption/enabled_namespaces_resolver.rb'
@@ -598,8 +558,6 @@ Style/IfUnlessModifier:
- 'ee/app/services/geo/metrics_update_service.rb'
- 'ee/app/services/geo/move_repository_service.rb'
- 'ee/app/services/geo/prune_event_log_service.rb'
- - 'ee/app/services/geo/repository_verification_primary_service.rb'
- - 'ee/app/services/geo/repository_verification_secondary_service.rb'
- 'ee/app/services/gitlab_subscriptions/plan_upgrade_service.rb'
- 'ee/app/services/groups/memberships/export_service.rb'
- 'ee/app/services/groups/update_repository_storage_service.rb'
@@ -659,9 +617,7 @@ Style/IfUnlessModifier:
- 'ee/lib/ee/api/helpers/variables_helpers.rb'
- 'ee/lib/ee/api/internal/base.rb'
- 'ee/lib/ee/api/merge_request_approvals.rb'
- - 'ee/lib/ee/api/protected_branches.rb'
- 'ee/lib/ee/api/settings.rb'
- - 'ee/lib/ee/audit/project_changes_auditor.rb'
- 'ee/lib/ee/banzai/filter/references/iteration_reference_filter.rb'
- 'ee/lib/ee/container_registry/client.rb'
- 'ee/lib/ee/gitlab/auth/ldap/access.rb'
@@ -740,7 +696,6 @@ Style/IfUnlessModifier:
- 'ee/spec/support/helpers/feature_approval_helper.rb'
- 'ee/spec/support/helpers/search_results_helpers.rb'
- 'ee/spec/support/http_io/http_io_helpers.rb'
- - 'ee/spec/support/shared_examples/requests/api/graphql/geo/registries_shared_examples.rb'
- 'ee/spec/views/layouts/header/help_dropdown/_cross_stage_fdm.html.haml_spec.rb'
- 'ee/spec/workers/elastic/migration_worker_spec.rb'
- 'lib/api/api_guard.rb'
@@ -865,7 +820,6 @@ Style/IfUnlessModifier:
- 'lib/gitlab/ci/config/extendable/entry.rb'
- 'lib/gitlab/ci/config/external/file/base.rb'
- 'lib/gitlab/ci/config/external/file/template.rb'
- - 'lib/gitlab/ci/config/external/mapper.rb'
- 'lib/gitlab/ci/config/normalizer.rb'
- 'lib/gitlab/ci/parsers/coverage/sax_document.rb'
- 'lib/gitlab/ci/parsers/security/common.rb'
@@ -1023,7 +977,6 @@ Style/IfUnlessModifier:
- 'lib/gitlab/template_parser/eval_state.rb'
- 'lib/gitlab/untrusted_regexp.rb'
- 'lib/gitlab/url_blocker.rb'
- - 'lib/gitlab/usage_data.rb'
- 'lib/gitlab/usage_data_counters/base_counter.rb'
- 'lib/gitlab/usage_data_counters/hll_redis_counter.rb'
- 'lib/gitlab/utils.rb'
@@ -1055,7 +1008,6 @@ Style/IfUnlessModifier:
- 'lib/tasks/gitlab/storage.rake'
- 'lib/tasks/gitlab/update_templates.rake'
- 'qa/qa/ee/resource/settings/elasticsearch.rb'
- - 'qa/qa/flow/sign_up.rb'
- 'qa/qa/page/component/select2.rb'
- 'qa/qa/page/component/snippet.rb'
- 'qa/qa/page/element.rb'
@@ -1080,7 +1032,6 @@ Style/IfUnlessModifier:
- 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_ldap_sync_spec.rb'
- 'qa/qa/specs/helpers/context_selector.rb'
- 'qa/qa/specs/parallel_runner.rb'
- - 'qa/qa/support/loglinking.rb'
- 'qa/qa/tools/delete_projects.rb'
- 'qa/qa/tools/delete_user_projects.rb'
- 'qa/qa/tools/generate_perf_testdata.rb'
@@ -1125,7 +1076,6 @@ Style/IfUnlessModifier:
- 'spec/helpers/invite_members_helper_spec.rb'
- 'spec/lib/container_registry/gitlab_api_client_spec.rb'
- 'spec/lib/gitlab/background_migration/disable_expiration_policies_linked_to_no_container_images_spec.rb'
- - 'spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb'
- 'spec/lib/gitlab/config/entry/validators/nested_array_helpers_spec.rb'
- 'spec/lib/gitlab/conflict/file_spec.rb'
- 'spec/lib/gitlab/database/load_balancing_spec.rb'
@@ -1148,10 +1098,8 @@ Style/IfUnlessModifier:
- 'spec/requests/api/integrations_spec.rb'
- 'spec/requests/users_controller_spec.rb'
- 'spec/routing/import_routing_spec.rb'
- - 'spec/rubocop_spec_helper.rb'
- 'spec/serializers/issue_sidebar_basic_entity_spec.rb'
- 'spec/services/application_settings/update_service_spec.rb'
- - 'spec/services/clusters/applications/create_service_spec.rb'
- 'spec/services/git/process_ref_changes_service_spec.rb'
- 'spec/services/members/destroy_service_spec.rb'
- 'spec/services/notification_recipients/builder/default_spec.rb'
@@ -1162,7 +1110,6 @@ Style/IfUnlessModifier:
- 'spec/services/projects/create_service_spec.rb'
- 'spec/spec_helper.rb'
- 'spec/support/capybara.rb'
- - 'spec/support/database/multiple_databases.rb'
- 'spec/support/external_authorization_service_helpers.rb'
- 'spec/support/flaky_tests.rb'
- 'spec/support/generate-seed-repo-rb'
diff --git a/.rubocop_todo/style/keyword_parameters_order.yml b/.rubocop_todo/style/keyword_parameters_order.yml
index ca6cb416b93..3730a86221e 100644
--- a/.rubocop_todo/style/keyword_parameters_order.yml
+++ b/.rubocop_todo/style/keyword_parameters_order.yml
@@ -1,30 +1,7 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/KeywordParametersOrder:
Exclude:
- - 'app/controllers/concerns/product_analytics_tracking.rb'
- - 'app/finders/group_descendants_finder.rb'
- - 'app/finders/merge_request_target_project_finder.rb'
- - 'app/graphql/resolvers/package_pipelines_resolver.rb'
- - 'app/helpers/timeboxes_helper.rb'
- - 'app/models/concerns/sortable.rb'
- - 'app/services/clusters/kubernetes/create_or_update_service_account_service.rb'
- - 'app/services/import/gitlab_projects/file_acquisition_strategies/file_upload.rb'
- - 'app/services/import/gitlab_projects/file_acquisition_strategies/remote_file.rb'
- - 'app/services/import/gitlab_projects/file_acquisition_strategies/remote_file_s3.rb'
- - 'app/services/issues/create_service.rb'
- - 'app/services/merge_requests/push_options_handler_service.rb'
- - 'app/services/snippets/create_service.rb'
- - 'app/services/work_items/create_and_link_service.rb'
- - 'app/services/work_items/create_from_task_service.rb'
- - 'app/services/work_items/create_service.rb'
- - 'app/services/work_items/delete_task_service.rb'
- - 'ee/app/graphql/mutations/dast/profiles/create.rb'
- - 'ee/app/graphql/mutations/dast_scanner_profiles/create.rb'
- - 'ee/app/graphql/mutations/dast_site_profiles/update.rb'
- - 'ee/app/models/license.rb'
- - 'ee/app/models/requirements_management/test_report.rb'
- - 'ee/app/services/analytics/devops_adoption/enabled_namespaces/bulk_find_or_create_service.rb'
- 'ee/app/services/analytics/devops_adoption/enabled_namespaces/create_service.rb'
- 'ee/app/services/analytics/devops_adoption/enabled_namespaces/find_or_create_service.rb'
- 'ee/app/services/audit_events/user_impersonation_group_audit_event_service.rb'
@@ -46,30 +23,3 @@ Style/KeywordParametersOrder:
- 'lib/gitlab/merge_requests/mergeability/results_store.rb'
- 'lib/gitlab/usage_data_counters/editor_unique_counter.rb'
- 'lib/microsoft_teams/notifier.rb'
- - 'qa/qa/specs/features/ee/browser_ui/3_create/repository/file_locking_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/3_create/repository/push_rules_spec.rb'
- - 'qa/spec/runtime/env_spec.rb'
- - 'spec/graphql/types/ci/pipeline_counts_type_spec.rb'
- - 'spec/lib/gitlab/background_migration/populate_vulnerability_reads_spec.rb'
- - 'spec/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid_spec.rb'
- - 'spec/lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings_spec.rb'
- - 'spec/lib/gitlab/background_migration/remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb'
- - 'spec/lib/gitlab/database/partitioning/detached_partition_dropper_spec.rb'
- - 'spec/migrations/20211018152654_schedule_remove_duplicate_vulnerabilities_findings3_spec.rb'
- - 'spec/migrations/20211116111644_schedule_remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb'
- - 'spec/migrations/20220106111958_add_insert_or_update_vulnerability_reads_trigger_spec.rb'
- - 'spec/migrations/20220106112043_add_update_vulnerability_reads_trigger_spec.rb'
- - 'spec/migrations/20220106112085_add_update_vulnerability_reads_location_trigger_spec.rb'
- - 'spec/migrations/20220106163326_add_has_issues_on_vulnerability_reads_trigger_spec.rb'
- - 'spec/migrations/20220107064845_populate_vulnerability_reads_spec.rb'
- - 'spec/migrations/confirm_support_bot_user_spec.rb'
- - 'spec/services/service_ping/submit_service_ping_service_spec.rb'
- - 'spec/support/helpers/doc_url_helper.rb'
- - 'spec/support/helpers/smime_helper.rb'
- - 'spec/support/helpers/workhorse_helpers.rb'
- - 'spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb'
- - 'spec/support/shared_examples/services/container_expiration_policy_shared_examples.rb'
- - 'spec/support/shared_examples/services/dependency_proxy_ttl_policies_shared_examples.rb'
- - 'spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb'
- - 'spec/tasks/gitlab/usage_data_rake_spec.rb'
- - 'spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb'
diff --git a/.rubocop_todo/style/lambda.yml b/.rubocop_todo/style/lambda.yml
index f37c2c4967e..e1c297ac5f6 100644
--- a/.rubocop_todo/style/lambda.yml
+++ b/.rubocop_todo/style/lambda.yml
@@ -49,7 +49,6 @@ Style/Lambda:
- 'lib/gitlab/action_cable/request_store_callbacks.rb'
- 'lib/gitlab/checks/diff_check.rb'
- 'lib/gitlab/database/load_balancing/action_cable_callbacks.rb'
- - 'lib/gitlab/memory/watchdog/configurator.rb'
- 'lib/gitlab/middleware/rack_multipart_tempfile_factory.rb'
- 'lib/gitlab/omniauth_initializer.rb'
- 'lib/gitlab/prometheus/queries/query_additional_metrics.rb'
@@ -57,7 +56,6 @@ Style/Lambda:
- 'lib/gitlab/sidekiq_config/worker_matcher.rb'
- 'lib/gitlab/sidekiq_middleware.rb'
- 'lib/gitlab/utils/usage_data.rb'
- - 'qa/qa/page/base.rb'
- 'qa/qa/runtime/allure_report.rb'
- 'qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb'
- 'qa/qa/support/api.rb'
diff --git a/.rubocop_todo/style/next.yml b/.rubocop_todo/style/next.yml
index 295aa2f6878..6800ba2baf3 100644
--- a/.rubocop_todo/style/next.yml
+++ b/.rubocop_todo/style/next.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/Next:
Exclude:
- 'lib/gitlab/fogbugz_import/importer.rb'
diff --git a/.rubocop_todo/style/numeric_literal_prefix.yml b/.rubocop_todo/style/numeric_literal_prefix.yml
index 4e8b608e424..0060919d7b6 100644
--- a/.rubocop_todo/style/numeric_literal_prefix.yml
+++ b/.rubocop_todo/style/numeric_literal_prefix.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/NumericLiteralPrefix:
Exclude:
- 'app/models/container_repository.rb'
diff --git a/.rubocop_todo/style/percent_literal_delimiters.yml b/.rubocop_todo/style/percent_literal_delimiters.yml
index bac2e807053..2f042829e35 100644
--- a/.rubocop_todo/style/percent_literal_delimiters.yml
+++ b/.rubocop_todo/style/percent_literal_delimiters.yml
@@ -1,31 +1,8 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/PercentLiteralDelimiters:
Exclude:
- 'Guardfile'
- - 'app/controllers/abuse_reports_controller.rb'
- - 'app/controllers/admin/application_settings_controller.rb'
- - 'app/controllers/admin/broadcast_messages_controller.rb'
- - 'app/controllers/application_controller.rb'
- - 'app/controllers/concerns/impersonation.rb'
- - 'app/controllers/concerns/oauth_applications.rb'
- - 'app/controllers/concerns/uploads_actions.rb'
- - 'app/controllers/groups_controller.rb'
- - 'app/controllers/import/bulk_imports_controller.rb'
- - 'app/controllers/import/fogbugz_controller.rb'
- - 'app/controllers/import/gitea_controller.rb'
- - 'app/controllers/jira_connect/app_descriptor_controller.rb'
- - 'app/controllers/jira_connect/subscriptions_controller.rb'
- - 'app/controllers/profiles/two_factor_auths_controller.rb'
- - 'app/controllers/projects/performance_monitoring/dashboards_controller.rb'
- - 'app/controllers/projects/product_analytics_controller.rb'
- - 'app/controllers/projects/service_desk_controller.rb'
- - 'app/controllers/repositories/lfs_locks_api_controller.rb'
- - 'app/finders/group_members_finder.rb'
- - 'app/finders/members_finder.rb'
- - 'app/finders/notes_finder.rb'
- - 'app/finders/todos_finder.rb'
- - 'app/helpers/application_helper.rb'
- 'app/helpers/auth_helper.rb'
- 'app/helpers/ci/variables_helper.rb'
- 'app/helpers/clusters_helper.rb'
@@ -33,7 +10,6 @@ Style/PercentLiteralDelimiters:
- 'app/helpers/diff_helper.rb'
- 'app/helpers/emails_helper.rb'
- 'app/helpers/external_link_helper.rb'
- - 'app/helpers/icons_helper.rb'
- 'app/helpers/labels_helper.rb'
- 'app/helpers/markup_helper.rb'
- 'app/helpers/nav_helper.rb'
@@ -82,7 +58,6 @@ Style/PercentLiteralDelimiters:
- 'app/models/concerns/clusters/agents/authorization_config_scopes.rb'
- 'app/models/concerns/diff_positionable_note.rb'
- 'app/models/concerns/enums/prometheus_metric.rb'
- - 'app/models/concerns/integrations/base_data_fields.rb'
- 'app/models/concerns/issuable.rb'
- 'app/models/concerns/issue_available_features.rb'
- 'app/models/concerns/mentionable/reference_regexes.rb'
@@ -112,7 +87,6 @@ Style/PercentLiteralDelimiters:
- 'app/models/integrations/emails_on_push.rb'
- 'app/models/integrations/external_wiki.rb'
- 'app/models/integrations/field.rb'
- - 'app/models/integrations/flowdock.rb'
- 'app/models/integrations/jenkins.rb'
- 'app/models/integrations/jira.rb'
- 'app/models/integrations/packagist.rb'
@@ -159,13 +133,11 @@ Style/PercentLiteralDelimiters:
- 'app/services/import_export_clean_up_service.rb'
- 'app/services/incident_management/pager_duty/process_webhook_service.rb'
- 'app/services/issuable/bulk_update_service.rb'
- - 'app/services/issues/export_csv_service.rb'
- 'app/services/merge_requests/update_service.rb'
- 'app/services/metrics/dashboard/default_embed_service.rb'
- 'app/services/packages/debian/generate_distribution_service.rb'
- 'app/services/preview_markdown_service.rb'
- 'app/services/projects/apple_target_platform_detector_service.rb'
- - 'app/services/projects/container_repository/cleanup_tags_service.rb'
- 'app/services/projects/download_service.rb'
- 'app/services/projects/hashed_storage/migrate_attachments_service.rb'
- 'app/services/projects/lfs_pointers/lfs_object_download_list_service.rb'
@@ -201,7 +173,6 @@ Style/PercentLiteralDelimiters:
- 'config/initializers/invisible_captcha.rb'
- 'config/initializers/lograge.rb'
- 'config/initializers/rspec_profiling.rb'
- - 'config/initializers/sidekiq.rb'
- 'config/initializers_before_autoloader/000_inflections.rb'
- 'config/object_store_settings.rb'
- 'config/spring.rb'
@@ -269,7 +240,6 @@ Style/PercentLiteralDelimiters:
- 'ee/app/models/geo_node_status.rb'
- 'ee/app/models/incident_management/issuable_resource_link.rb'
- 'ee/app/models/integrations/github.rb'
- - 'ee/app/models/integrations/gitlab_slack_application.rb'
- 'ee/app/models/merge_requests/status_check_response.rb'
- 'ee/app/models/saml_provider.rb'
- 'ee/app/models/security/orchestration_policy_configuration.rb'
@@ -285,7 +255,6 @@ Style/PercentLiteralDelimiters:
- 'ee/app/services/epics/tree_reorder_service.rb'
- 'ee/app/services/iterations/update_service.rb'
- 'ee/app/services/jira/jql_builder_service.rb'
- - 'ee/app/services/requirements_management/export_csv_service.rb'
- 'ee/app/services/security/configuration/save_auto_fix_service.rb'
- 'ee/app/services/security/dependency_list_service.rb'
- 'ee/app/services/security/ingestion/tasks/update_vulnerability_uuids.rb'
@@ -296,7 +265,6 @@ Style/PercentLiteralDelimiters:
- 'ee/lib/ee/api/helpers/projects_helpers.rb'
- 'ee/lib/ee/api/members.rb'
- 'ee/lib/ee/api/search.rb'
- - 'ee/lib/ee/audit/group_changes_auditor.rb'
- 'ee/lib/ee/gitlab/alert_management/payload/generic.rb'
- 'ee/lib/ee/gitlab/auth/ldap/adapter.rb'
- 'ee/lib/ee/gitlab/background_migration/fix_incorrect_max_seats_used.rb'
@@ -306,7 +274,6 @@ Style/PercentLiteralDelimiters:
- 'ee/lib/ee/gitlab/etag_caching/router/rails.rb'
- 'ee/lib/ee/gitlab/middleware/read_only/controller.rb'
- 'ee/lib/ee/gitlab/path_regex.rb'
- - 'ee/lib/ee/gitlab/uploads/migration_helper.rb'
- 'ee/lib/ee/gitlab/usage_data.rb'
- 'ee/lib/elastic/class_proxy_util.rb'
- 'ee/lib/elastic/latest/config.rb'
@@ -315,8 +282,6 @@ Style/PercentLiteralDelimiters:
- 'ee/lib/elastic/latest/project_instance_proxy.rb'
- 'ee/lib/elastic/latest/snippet_class_proxy.rb'
- 'ee/lib/gitlab/auth/group_saml/auth_hash.rb'
- - 'ee/lib/gitlab/ci/parsers/security/formatters/dast.rb'
- - 'ee/lib/gitlab/geo.rb'
- 'ee/lib/gitlab/geo/replicator.rb'
- 'ee/lib/gitlab/usage/metrics/instrumentations/license_metric.rb'
- 'ee/lib/tasks/gitlab/elastic/test.rake'
@@ -339,7 +304,6 @@ Style/PercentLiteralDelimiters:
- 'ee/spec/features/projects/environments/environments_spec.rb'
- 'ee/spec/features/projects/integrations/user_activates_jira_spec.rb'
- 'ee/spec/features/projects/settings/protected_environments_spec.rb'
- - 'ee/spec/features/protected_branches_spec.rb'
- 'ee/spec/features/users/login_spec.rb'
- 'ee/spec/finders/template_finder_spec.rb'
- 'ee/spec/frontend/fixtures/saml_providers.rb'
@@ -347,7 +311,6 @@ Style/PercentLiteralDelimiters:
- 'ee/spec/graphql/ee/types/boards/board_issue_input_type_spec.rb'
- 'ee/spec/graphql/mutations/boards/update_spec.rb'
- 'ee/spec/graphql/resolvers/board_groupings/epics_resolvers_spec.rb'
- - 'ee/spec/graphql/resolvers/epics_resolver_spec.rb'
- 'ee/spec/graphql/resolvers/pipeline_security_report_findings_resolver_spec.rb'
- 'ee/spec/graphql/types/dast/profile_cadence_enum_spec.rb'
- 'ee/spec/graphql/types/dast/scan_method_type_enum_spec.rb'
@@ -439,7 +402,6 @@ Style/PercentLiteralDelimiters:
- 'ee/spec/policies/group_policy_spec.rb'
- 'ee/spec/policies/project_policy_spec.rb'
- 'ee/spec/presenters/merge_request_approver_presenter_spec.rb'
- - 'ee/spec/presenters/merge_request_presenter_spec.rb'
- 'ee/spec/requests/admin/user_permission_exports_controller_spec.rb'
- 'ee/spec/requests/api/dependencies_spec.rb'
- 'ee/spec/requests/api/epics_spec.rb'
@@ -483,7 +445,6 @@ Style/PercentLiteralDelimiters:
- 'ee/spec/validators/json_schema_validator_spec.rb'
- 'ee/spec/views/operations/index.html.haml_spec.rb'
- 'ee/spec/views/shared/credentials_inventory/personal_access_tokens/_personal_access_token.html.haml_spec.rb'
- - 'ee/spec/views/shared/credentials_inventory/project_access_tokens/_project_access_token.html.haml_spec.rb'
- 'ee/spec/workers/ee/issuable_export_csv_worker_spec.rb'
- 'ee/spec/workers/project_cache_worker_spec.rb'
- 'ee/spec/workers/repository_import_worker_spec.rb'
@@ -523,7 +484,6 @@ Style/PercentLiteralDelimiters:
- 'lib/bitbucket/representation/issue.rb'
- 'lib/container_registry/path.rb'
- 'lib/feature.rb'
- - 'lib/flowdock/git/builder.rb'
- 'lib/generators/gitlab/usage_metric_definition_generator.rb'
- 'lib/generators/gitlab/usage_metric_generator.rb'
- 'lib/gitlab.rb'
@@ -591,11 +551,9 @@ Style/PercentLiteralDelimiters:
- 'lib/gitlab/sanitizers/exif.rb'
- 'lib/gitlab/search/abuse_detection.rb'
- 'lib/gitlab/search_context.rb'
- - 'lib/gitlab/sidekiq_daemon/memory_killer.rb'
- 'lib/gitlab/slash_commands/presenters/base.rb'
- 'lib/gitlab/ssh_public_key.rb'
- 'lib/gitlab/task_helpers.rb'
- - 'lib/gitlab/uploads/migration_helper.rb'
- 'lib/gitlab/url_blocker.rb'
- 'lib/gitlab/usage/metrics/instrumentations/count_imported_projects_total_metric.rb'
- 'lib/gitlab/usage/metrics/instrumentations/database_metric.rb'
@@ -626,7 +584,6 @@ Style/PercentLiteralDelimiters:
- 'qa/qa/ee/page/project/issue/show.rb'
- 'qa/qa/ee/page/project/job/show.rb'
- 'qa/qa/ee/page/project/packages/index.rb'
- - 'qa/qa/ee/page/project/pipeline/show.rb'
- 'qa/qa/ee/page/project/show.rb'
- 'qa/qa/ee/page/project/snippet/index.rb'
- 'qa/qa/ee/page/project/wiki/show.rb'
@@ -635,7 +592,6 @@ Style/PercentLiteralDelimiters:
- 'qa/qa/resource/visibility.rb'
- 'qa/qa/service/cluster_provider/gcloud.rb'
- 'qa/qa/service/praefect_manager.rb'
- - 'qa/qa/specs/features/api/3_create/gitaly/praefect_repo_sync_spec.rb'
- 'qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb'
- 'qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb'
- 'qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb'
@@ -704,7 +660,6 @@ Style/PercentLiteralDelimiters:
- 'spec/features/projects/pipelines/legacy_pipelines_spec.rb'
- 'spec/features/projects/pipelines/pipeline_spec.rb'
- 'spec/features/projects/pipelines/pipelines_spec.rb'
- - 'spec/features/protected_branches_spec.rb'
- 'spec/features/tags/developer_views_tags_spec.rb'
- 'spec/features/users/login_spec.rb'
- 'spec/finders/alert_management/alerts_finder_spec.rb'
@@ -742,7 +697,6 @@ Style/PercentLiteralDelimiters:
- 'spec/helpers/page_layout_helper_spec.rb'
- 'spec/helpers/profiles_helper_spec.rb'
- 'spec/helpers/releases_helper_spec.rb'
- - 'spec/helpers/storage_helper_spec.rb'
- 'spec/helpers/tracking_helper_spec.rb'
- 'spec/initializers/direct_upload_support_spec.rb'
- 'spec/initializers/enumerator_next_patch_spec.rb'
@@ -814,7 +768,6 @@ Style/PercentLiteralDelimiters:
- 'spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/expression/lexeme/equals_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/seed/build_spec.rb'
- - 'spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb'
- 'spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb'
- 'spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb'
- 'spec/lib/gitlab/ci/reports/security/reports_spec.rb'
@@ -1083,7 +1036,6 @@ Style/PercentLiteralDelimiters:
- 'spec/requests/api/task_completion_status_spec.rb'
- 'spec/requests/api/unleash_spec.rb'
- 'spec/requests/api/users_spec.rb'
- - 'spec/requests/api/version_spec.rb'
- 'spec/requests/api/wikis_spec.rb'
- 'spec/requests/ide_controller_spec.rb'
- 'spec/requests/jwt_controller_spec.rb'
@@ -1111,7 +1063,6 @@ Style/PercentLiteralDelimiters:
- 'spec/services/ci/stuck_builds/drop_pending_service_spec.rb'
- 'spec/services/ci/stuck_builds/drop_running_service_spec.rb'
- 'spec/services/ci/stuck_builds/drop_scheduled_service_spec.rb'
- - 'spec/services/clusters/applications/create_service_spec.rb'
- 'spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb'
- 'spec/services/deployments/update_environment_service_spec.rb'
- 'spec/services/design_management/copy_design_collection/copy_service_spec.rb'
@@ -1137,7 +1088,6 @@ Style/PercentLiteralDelimiters:
- 'spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb'
- 'spec/services/product_analytics/build_graph_service_spec.rb'
- 'spec/services/projects/branches_by_mode_service_spec.rb'
- - 'spec/services/projects/container_repository/cleanup_tags_service_spec.rb'
- 'spec/services/projects/lfs_pointers/lfs_link_service_spec.rb'
- 'spec/services/projects/operations/update_service_spec.rb'
- 'spec/services/projects/record_target_platforms_service_spec.rb'
@@ -1191,7 +1141,6 @@ Style/PercentLiteralDelimiters:
- 'spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb'
- 'spec/tasks/gitlab/db_rake_spec.rb'
- 'spec/tasks/gitlab/task_helpers_spec.rb'
- - 'spec/tasks/gitlab/uploads/migrate_rake_spec.rb'
- 'spec/tooling/danger/customer_success_spec.rb'
- 'spec/tooling/danger/datateam_spec.rb'
- 'spec/tooling/danger/sidekiq_queues_spec.rb'
@@ -1226,7 +1175,6 @@ Style/PercentLiteralDelimiters:
- 'spec/workers/post_receive_spec.rb'
- 'spec/workers/project_cache_worker_spec.rb'
- 'spec/workers/projects/record_target_platforms_worker_spec.rb'
- - 'spec/workers/repository_import_worker_spec.rb'
- 'spec/workers/stuck_merge_jobs_worker_spec.rb'
- 'spec/workers/update_project_statistics_worker_spec.rb'
- 'tooling/danger/datateam.rb'
diff --git a/.rubocop_todo/style/redundant_begin.yml b/.rubocop_todo/style/redundant_begin.yml
index d2851de201b..8f490002fd6 100644
--- a/.rubocop_todo/style/redundant_begin.yml
+++ b/.rubocop_todo/style/redundant_begin.yml
@@ -1,56 +1,6 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/RedundantBegin:
Exclude:
- - 'app/controllers/concerns/membership_actions.rb'
- 'app/controllers/concerns/metrics_dashboard.rb'
- - 'app/controllers/concerns/renders_ldap_servers.rb'
- - 'app/helpers/auth_helper.rb'
- - 'app/models/event.rb'
- - 'app/models/integrations/asana.rb'
- - 'app/models/integrations/jira.rb'
- - 'app/models/user.rb'
- - 'app/services/ci/register_job_service.rb'
- - 'app/services/design_management/generate_image_versions_service.rb'
- - 'app/services/error_tracking/list_projects_service.rb'
- - 'app/services/issue_links/create_service.rb'
- - 'app/services/merge_requests/assign_issues_service.rb'
- - 'app/services/projects/batch_forks_count_service.rb'
- - 'app/services/projects/batch_open_issues_count_service.rb'
- - 'app/services/projects/lfs_pointers/lfs_object_download_list_service.rb'
- - 'app/services/users/update_highest_member_role_service.rb'
- - 'ee/app/controllers/groups/sso_controller.rb'
- - 'ee/app/finders/security/findings_finder.rb'
- - 'ee/app/services/epics/epic_links/create_service.rb'
- - 'ee/app/services/epics/related_epic_links/create_service.rb'
- - 'ee/app/services/geo/container_repository_sync_service.rb'
- - 'ee/app/validators/ee/json_schema_validator.rb'
- - 'ee/lib/ee/api/helpers/award_emoji.rb'
- 'ee/lib/gem_extensions/elasticsearch/model/adapter/multiple/records.rb'
- - 'ee/lib/gitlab/ci/parsers/security/dast.rb'
- - 'ee/lib/omni_auth/strategies/group_saml.rb'
- - 'ee/lib/system_check/geo/authorized_keys_check.rb'
- - 'lib/api/helpers/award_emoji.rb'
- - 'lib/api/time_tracking_endpoints.rb'
- - 'lib/atlassian/jira_connect/serializers/build_entity.rb'
- - 'lib/backup/manager.rb'
- - 'lib/banzai/pipeline/wiki_pipeline.rb'
- - 'lib/gem_extensions/active_record/association.rb'
- - 'lib/gitlab.rb'
- - 'lib/gitlab/changes_list.rb'
- - 'lib/gitlab/color.rb'
- - 'lib/gitlab/database/migration_helpers.rb'
- - 'lib/gitlab/database/schema_cache_with_renamed_table.rb'
- - 'lib/gitlab/favicon.rb'
- - 'lib/gitlab/import_export/base/relation_factory.rb'
- - 'lib/gitlab/import_export/members_mapper.rb'
- - 'lib/gitlab/import_export/project/tree_saver.rb'
- - 'lib/gitlab/instrumentation/redis_interceptor.rb'
- - 'lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb'
- - 'lib/gitlab/phabricator_import/project_creator.rb'
- - 'lib/gitlab/project_search_results.rb'
- - 'lib/gitlab/shell.rb'
- - 'lib/gitlab/usage/metrics/aggregates/sources/calculations/intersection.rb'
- - 'lib/sidebars/menu.rb'
- - 'qa/qa/resource/api_fabricator.rb'
- - 'qa/qa/runtime/api/client.rb'
diff --git a/.rubocop_todo/style/redundant_condition.yml b/.rubocop_todo/style/redundant_condition.yml
index 86be40f7ffa..c94a65c202c 100644
--- a/.rubocop_todo/style/redundant_condition.yml
+++ b/.rubocop_todo/style/redundant_condition.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/RedundantCondition:
Exclude:
- 'ee/app/models/license.rb'
diff --git a/.rubocop_todo/style/redundant_interpolation.yml b/.rubocop_todo/style/redundant_interpolation.yml
index ca7e01ffa61..b0d69cd2950 100644
--- a/.rubocop_todo/style/redundant_interpolation.yml
+++ b/.rubocop_todo/style/redundant_interpolation.yml
@@ -1,62 +1,7 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/RedundantInterpolation:
Exclude:
- - 'app/components/diffs/stats_component.rb'
- - 'app/helpers/blob_helper.rb'
- - 'app/helpers/ci/runners_helper.rb'
- - 'app/helpers/commits_helper.rb'
- - 'app/helpers/dropdowns_helper.rb'
- - 'app/helpers/environments_helper.rb'
- - 'app/helpers/icons_helper.rb'
- - 'app/helpers/issuables_helper.rb'
- - 'app/helpers/nav_helper.rb'
- - 'app/helpers/projects_helper.rb'
- - 'app/helpers/search_helper.rb'
- - 'app/helpers/tooling/visual_review_helper.rb'
- - 'app/models/concerns/issuable.rb'
- - 'app/models/integrations/base_chat_notification.rb'
- - 'app/models/integrations/hangouts_chat.rb'
- - 'app/models/integrations/microsoft_teams.rb'
- - 'app/models/integrations/pushover.rb'
- - 'app/models/merge_request.rb'
- - 'app/services/jira_import/start_import_service.rb'
- - 'app/services/users/approve_service.rb'
- - 'app/services/users/banned_user_base_service.rb'
- - 'app/services/users/reject_service.rb'
- - 'db/post_migrate/20220420214703_schedule_backfill_draft_status_on_merge_requests_corrected_regex.rb'
- - 'ee/app/graphql/types/compliance_management/merge_requests/compliance_violation_reason_enum.rb'
- - 'ee/app/graphql/types/incident_management/oncall_rotation_length_unit_enum.rb'
- - 'ee/app/helpers/ee/ci/runners_helper.rb'
- - 'ee/app/helpers/ee/geo_helper.rb'
- - 'ee/app/services/geo/event_store.rb'
- - 'ee/app/services/geo/repository_updated_service.rb'
- - 'ee/app/services/security/security_orchestration_policies/create_pipeline_service.rb'
- - 'ee/app/services/users/abuse/excessive_projects_download_ban_service.rb'
- - 'ee/app/services/users/abuse/git_abuse/namespace_throttle_service.rb'
- - 'ee/db/fixtures/development/31_devops_adoption.rb'
- - 'ee/lib/gitlab/insights/serializers/chartjs/base_serializer.rb'
- - 'ee/spec/features/epics/epic_issues_spec.rb'
- - 'ee/spec/features/projects/requirements_management/requirements_list_spec.rb'
- - 'ee/spec/features/registrations/one_trust_spec.rb'
- - 'ee/spec/graphql/ee/mutations/boards/lists/create_spec.rb'
- - 'ee/spec/lib/banzai/filter/issuable_reference_expansion_filter_spec.rb'
- - 'ee/spec/lib/banzai/filter/references/iteration_reference_filter_spec.rb'
- - 'ee/spec/lib/ee/gitlab/import_export/group/tree_saver_spec.rb'
- - 'ee/spec/lib/gitlab/insights/serializers/chartjs/line_serializer_spec.rb'
- - 'ee/spec/lib/gitlab/insights/serializers/chartjs/multi_series_serializer_spec.rb'
- - 'ee/spec/mailers/ci_minutes_usage_mailer_spec.rb'
- - 'ee/spec/mailers/emails/namespace_storage_usage_mailer_spec.rb'
- - 'ee/spec/mailers/notify_spec.rb'
- - 'ee/spec/models/incident_management/issuable_escalation_status_spec.rb'
- - 'ee/spec/requests/api/issues_spec.rb'
- - 'ee/spec/requests/api/search_spec.rb'
- - 'ee/spec/services/analytics/cycle_analytics/consistency_check_service_spec.rb'
- - 'ee/spec/services/search_service_spec.rb'
- - 'ee/spec/services/timebox_report_service_spec.rb'
- - 'ee/spec/support/shared_contexts/lib/gitlab/insights/serializers/serializers_shared_context.rb'
- - 'ee/spec/support/shared_examples/lib/gitlab/elastic/standalone_indices_shared_examples.rb'
- - 'ee/spec/tasks/gitlab/elastic_rake_spec.rb'
- 'lib/backup/manager.rb'
- 'lib/backup/task.rb'
- 'lib/gitlab/application_rate_limiter.rb'
@@ -72,7 +17,6 @@ Style/RedundantInterpolation:
- 'lib/gitlab/repository_cache_adapter.rb'
- 'lib/gitlab/repository_hash_cache.rb'
- 'lib/gitlab/repository_set_cache.rb'
- - 'lib/gitlab/usage/metrics/names_suggestions/relation_parsers/constraints.rb'
- 'lib/gitlab/usage/metrics/names_suggestions/relation_parsers/joins.rb'
- 'lib/gitlab/usage_data_counters/hll_redis_counter.rb'
- 'lib/gitlab/usage_data_counters/search_counter.rb'
@@ -88,7 +32,6 @@ Style/RedundantInterpolation:
- 'qa/qa/service/praefect_manager.rb'
- 'qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb'
- 'qa/qa/specs/features/browser_ui/5_package/container_registry/container_registry_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/13_secure/scan_result_policy_vulnerabilities_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/3_create/repository/project_templates_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/4_verify/parent_child_pipelines_dependent_relationship_spec.rb'
- 'qa/qa/tools/generate_perf_testdata.rb'
diff --git a/.rubocop_todo/style/redundant_parentheses.yml b/.rubocop_todo/style/redundant_parentheses.yml
deleted file mode 100644
index 1ca658776b8..00000000000
--- a/.rubocop_todo/style/redundant_parentheses.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-# Cop supports --auto-correct.
-Style/RedundantParentheses:
- Exclude:
- - 'lib/gitlab/database/tables_truncate.rb'
- - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/utilization/billing_seats_usage_data_spec.rb'
diff --git a/.rubocop_todo/style/redundant_regexp_escape.yml b/.rubocop_todo/style/redundant_regexp_escape.yml
index 5c97275ee4b..22608a8e1f5 100644
--- a/.rubocop_todo/style/redundant_regexp_escape.yml
+++ b/.rubocop_todo/style/redundant_regexp_escape.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/RedundantRegexpEscape:
Exclude:
- 'app/controllers/help_controller.rb'
@@ -11,7 +11,6 @@ Style/RedundantRegexpEscape:
- 'app/models/deploy_token.rb'
- 'app/models/design_management/design.rb'
- 'app/models/integrations/chat_message/base_message.rb'
- - 'app/models/integrations/datadog.rb'
- 'app/models/integrations/teamcity.rb'
- 'app/models/issue.rb'
- 'app/models/label.rb'
@@ -23,7 +22,6 @@ Style/RedundantRegexpEscape:
- 'app/models/snippet.rb'
- 'app/services/metrics/dashboard/grafana_metric_embed_service.rb'
- 'app/uploaders/file_uploader.rb'
- - 'config/initializers/wikicloth_redos_patch.rb'
- 'config/routes/project.rb'
- 'config/routes/uploads.rb'
- 'ee/app/models/ee/epic.rb'
@@ -60,7 +58,6 @@ Style/RedundantRegexpEscape:
- 'lib/gitlab/git/repository.rb'
- 'lib/gitlab/gitaly_client.rb'
- 'lib/gitlab/harbor/query.rb'
- - 'lib/gitlab/incoming_email.rb'
- 'lib/gitlab/jira/dvcs.rb'
- 'lib/gitlab/path_regex.rb'
- 'lib/gitlab/private_commit_email.rb'
@@ -68,7 +65,6 @@ Style/RedundantRegexpEscape:
- 'lib/gitlab/quick_actions/extractor.rb'
- 'lib/gitlab/regex.rb'
- 'lib/gitlab/search/abuse_detection.rb'
- - 'lib/gitlab/service_desk_email.rb'
- 'lib/gitlab/task_helpers.rb'
- 'lib/gitlab/url_sanitizer.rb'
- 'lib/gitlab/utils.rb'
@@ -78,40 +74,8 @@ Style/RedundantRegexpEscape:
- 'lib/tasks/gettext.rake'
- 'lib/tasks/gitlab/info.rake'
- 'qa/qa/specs/features/ee/browser_ui/13_secure/license_compliance_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/13_secure/security_reports_spec.rb'
- 'qa/spec/runtime/key/ecdsa_spec.rb'
- 'qa/spec/runtime/key/ed25519_spec.rb'
- 'qa/spec/runtime/key/rsa_spec.rb'
- - 'rubocop/cop/gitlab/finder_with_find_by.rb'
- 'scripts/qa/testcases-check'
- 'scripts/setup/find-jh-branch.rb'
- - 'spec/controllers/projects_controller_spec.rb'
- - 'spec/features/read_only_spec.rb'
- - 'spec/helpers/tab_helper_spec.rb'
- - 'spec/lib/banzai/filter/references/reference_filter_spec.rb'
- - 'spec/lib/banzai/filter/syntax_highlight_filter_spec.rb'
- - 'spec/lib/banzai/pipeline/incident_management/timeline_event_pipeline_spec.rb'
- - 'spec/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues_spec.rb'
- - 'spec/lib/gitlab/ci/config_spec.rb'
- - 'spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb'
- - 'spec/lib/gitlab/import_export/saver_spec.rb'
- - 'spec/lib/gitlab/kubernetes/kube_client_spec.rb'
- - 'spec/lib/gitlab/sql/pattern_spec.rb'
- - 'spec/lib/gitlab/usage/metrics/name_suggestion_spec.rb'
- - 'spec/lib/gitlab/usage/metrics/names_suggestions/generator_spec.rb'
- - 'spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb'
- - 'spec/mailers/emails/profile_spec.rb'
- - 'spec/models/release_highlight_spec.rb'
- - 'spec/requests/api/graphql/mutations/merge_requests/set_draft_spec.rb'
- - 'spec/requests/api/graphql_spec.rb'
- - 'spec/requests/api/project_debian_distributions_spec.rb'
- - 'spec/requests/api/repositories_spec.rb'
- - 'spec/services/projects/download_service_spec.rb'
- - 'spec/support/matchers/exceed_query_limit.rb'
- - 'spec/support/shared_examples/features/wiki/file_attachments_shared_examples.rb'
- - 'spec/support/shared_examples/mailers/notify_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb'
- - 'spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb'
- - 'spec/uploaders/personal_file_uploader_spec.rb'
- - 'spec/views/help/index.html.haml_spec.rb'
- - 'tooling/danger/project_helper.rb'
diff --git a/.rubocop_todo/style/redundant_self.yml b/.rubocop_todo/style/redundant_self.yml
index 8c688dc89c1..ec9aaac85dc 100644
--- a/.rubocop_todo/style/redundant_self.yml
+++ b/.rubocop_todo/style/redundant_self.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/RedundantSelf:
Exclude:
- 'app/channels/awareness_channel.rb'
@@ -71,7 +71,6 @@ Style/RedundantSelf:
- 'app/models/concerns/mentionable.rb'
- 'app/models/concerns/merge_request_reviewer_state.rb'
- 'app/models/concerns/noteable.rb'
- - 'app/models/concerns/packages/debian/distribution.rb'
- 'app/models/concerns/participable.rb'
- 'app/models/concerns/pg_full_text_searchable.rb'
- 'app/models/concerns/protected_ref.rb'
@@ -112,7 +111,6 @@ Style/RedundantSelf:
- 'app/models/integrations/emails_on_push.rb'
- 'app/models/integrations/jira.rb'
- 'app/models/integrations/pipelines_email.rb'
- - 'app/models/integrations/slack.rb'
- 'app/models/integrations/zentao.rb'
- 'app/models/internal_id.rb'
- 'app/models/issue.rb'
@@ -190,7 +188,6 @@ Style/RedundantSelf:
- 'config/initializers/active_record_keyset_pagination.rb'
- 'config/initializers/forbid_sidekiq_in_transactions.rb'
- 'config/initializers/mail_encoding_patch.rb'
- - 'config/initializers/wikicloth_redos_patch.rb'
- 'db/post_migrate/20210329102724_add_new_trail_plans.rb'
- 'db/post_migrate/20211029102822_add_open_source_plan.rb'
- 'ee/app/graphql/resolvers/geo/registries_resolver.rb'
@@ -223,7 +220,6 @@ Style/RedundantSelf:
- 'ee/app/models/ee/issue.rb'
- 'ee/app/models/ee/iteration.rb'
- 'ee/app/models/ee/member.rb'
- - 'ee/app/models/ee/merge_request.rb'
- 'ee/app/models/ee/namespace.rb'
- 'ee/app/models/ee/packages/package_file.rb'
- 'ee/app/models/ee/project.rb'
@@ -420,7 +416,6 @@ Style/RedundantSelf:
- 'qa/qa/resource/user_gpg.rb'
- 'qa/qa/runtime/release.rb'
- 'qa/qa/scenario/bootable.rb'
- - 'qa/qa/scenario/test/instance.rb'
- 'sidekiq_cluster/sidekiq_cluster.rb'
- 'spec/graphql/resolvers/group_labels_resolver_spec.rb'
- 'spec/helpers/emails_helper_spec.rb'
diff --git a/.rubocop_todo/style/single_argument_dig.yml b/.rubocop_todo/style/single_argument_dig.yml
index a85039a45f5..8d35373a2c0 100644
--- a/.rubocop_todo/style/single_argument_dig.yml
+++ b/.rubocop_todo/style/single_argument_dig.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/SingleArgumentDig:
Exclude:
- 'app/graphql/resolvers/namespace_projects_resolver.rb'
@@ -38,7 +38,6 @@ Style/SingleArgumentDig:
- 'lib/gitlab/auth/o_auth/auth_hash.rb'
- 'lib/gitlab/ci/badge/coverage/template.rb'
- 'lib/gitlab/ci/badge/template.rb'
- - 'lib/gitlab/ci/lint.rb'
- 'lib/gitlab/ci/parsers/accessibility/pa11y.rb'
- 'lib/gitlab/ci/parsers/security/common.rb'
- 'lib/gitlab/ci/reports/codequality_reports.rb'
diff --git a/.rubocop_todo/style/sole_nested_conditional.yml b/.rubocop_todo/style/sole_nested_conditional.yml
index 3c663b5f89a..20b70d36ce2 100644
--- a/.rubocop_todo/style/sole_nested_conditional.yml
+++ b/.rubocop_todo/style/sole_nested_conditional.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/SoleNestedConditional:
Exclude:
- 'app/controllers/admin/application_settings_controller.rb'
@@ -24,7 +24,6 @@ Style/SoleNestedConditional:
- 'app/services/projects/create_service.rb'
- 'app/services/projects/hashed_storage/migration_service.rb'
- 'app/services/projects/hashed_storage/rollback_service.rb'
- - 'app/workers/merge_requests/delete_source_branch_worker.rb'
- 'ee/app/finders/ee/snippets_finder.rb'
- 'ee/app/services/ee/issue_links/create_service.rb'
- 'ee/app/services/ee/lfs/unlock_file_service.rb'
@@ -39,7 +38,6 @@ Style/SoleNestedConditional:
- 'ee/lib/elastic/latest/application_class_proxy.rb'
- 'ee/lib/elastic/latest/issue_class_proxy.rb'
- 'ee/lib/gitlab/code_owners/groups_loader.rb'
- - 'lib/api/ci/helpers/runner.rb'
- 'lib/api/deploy_keys.rb'
- 'lib/api/helpers/label_helpers.rb'
- 'lib/api/maven_packages.rb'
diff --git a/.rubocop_todo/style/string_concatenation.yml b/.rubocop_todo/style/string_concatenation.yml
index 65389d798d3..91eb45f73cf 100644
--- a/.rubocop_todo/style/string_concatenation.yml
+++ b/.rubocop_todo/style/string_concatenation.yml
@@ -1,24 +1,9 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/StringConcatenation:
Exclude:
- - 'app/components/pajamas/button_component.rb'
- - 'app/controllers/concerns/creates_commit.rb'
- - 'app/controllers/groups/dependency_proxy_for_containers_controller.rb'
- - 'app/controllers/jira_connect/app_descriptor_controller.rb'
- 'app/controllers/projects/labels_controller.rb'
- 'app/controllers/projects/milestones_controller.rb'
- - 'app/graphql/types/global_id_type.rb'
- - 'app/helpers/application_helper.rb'
- - 'app/helpers/blob_helper.rb'
- - 'app/helpers/dropdowns_helper.rb'
- - 'app/helpers/emails_helper.rb'
- - 'app/helpers/events_helper.rb'
- - 'app/helpers/numbers_helper.rb'
- - 'app/helpers/submodule_helper.rb'
- - 'app/helpers/todos_helper.rb'
- - 'app/models/application_setting.rb'
- - 'app/models/commit_range.rb'
- 'app/models/concerns/counter_attribute.rb'
- 'app/models/concerns/cross_database_modification.rb'
- 'app/models/concerns/from_set_operator.rb'
@@ -66,7 +51,6 @@ Style/StringConcatenation:
- 'ee/app/services/merge_requests/update_blocks_service.rb'
- 'ee/app/workers/scan_security_report_secrets_worker.rb'
- 'ee/lib/api/project_mirror.rb'
- - 'ee/lib/ee/audit/project_changes_auditor.rb'
- 'ee/lib/ee/gitlab/auth/ldap/person.rb'
- 'ee/lib/ee/gitlab/background_migration/backfill_project_statistics_container_repository_size.rb'
- 'ee/lib/ee/gitlab/background_migration/populate_latest_pipeline_ids.rb'
@@ -77,7 +61,6 @@ Style/StringConcatenation:
- 'ee/lib/omni_auth/strategies/kerberos.rb'
- 'ee/lib/tasks/gitlab/elastic.rake'
- 'ee/lib/tasks/gitlab/license.rake'
- - 'ee/spec/controllers/trial_registrations_controller_spec.rb'
- 'ee/spec/features/boards/boards_spec.rb'
- 'ee/spec/features/projects/pipelines/pipeline_spec.rb'
- 'ee/spec/helpers/search_helper_spec.rb'
@@ -124,7 +107,6 @@ Style/StringConcatenation:
- 'lib/gitlab/git.rb'
- 'lib/gitlab/git/branch.rb'
- 'lib/gitlab/git/tag.rb'
- - 'lib/gitlab/git/wiki.rb'
- 'lib/gitlab/github_import/importer/labels_importer.rb'
- 'lib/gitlab/graphql/negatable_arguments.rb'
- 'lib/gitlab/graphql/queries.rb'
@@ -184,7 +166,6 @@ Style/StringConcatenation:
- 'spec/features/projects/commits/user_browses_commits_spec.rb'
- 'spec/features/projects/files/files_sort_submodules_with_folders_spec.rb'
- 'spec/features/projects/import_export/import_file_spec.rb'
- - 'spec/features/search/user_searches_for_code_spec.rb'
- 'spec/finders/packages/go/module_finder_spec.rb'
- 'spec/graphql/mutations/issues/create_spec.rb'
- 'spec/helpers/application_helper_spec.rb'
@@ -227,7 +208,6 @@ Style/StringConcatenation:
- 'spec/lib/gitlab/gitaly_client/ref_service_spec.rb'
- 'spec/lib/gitlab/gitaly_client/remote_service_spec.rb'
- 'spec/lib/gitlab/gitaly_client/repository_service_spec.rb'
- - 'spec/lib/gitlab/gitaly_client/wiki_service_spec.rb'
- 'spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb'
- 'spec/lib/gitlab/lfs/client_spec.rb'
- 'spec/lib/gitlab/other_markup_spec.rb'
@@ -262,9 +242,9 @@ Style/StringConcatenation:
- 'spec/models/custom_emoji_spec.rb'
- 'spec/models/grafana_integration_spec.rb'
- 'spec/models/integrations/campfire_spec.rb'
- - 'spec/models/integrations/datadog_spec.rb'
- 'spec/models/integrations/chat_message/pipeline_message_spec.rb'
- 'spec/models/integrations/chat_message/push_message_spec.rb'
+ - 'spec/models/integrations/datadog_spec.rb'
- 'spec/models/integrations/jenkins_spec.rb'
- 'spec/models/merge_request_diff_spec.rb'
- 'spec/models/merge_request_spec.rb'
@@ -292,7 +272,6 @@ Style/StringConcatenation:
- 'spec/routing/git_http_routing_spec.rb'
- 'spec/services/ci/find_exposed_artifacts_service_spec.rb'
- 'spec/services/clusters/gcp/finalize_creation_service_spec.rb'
- - 'spec/services/clusters/kubernetes/configure_istio_ingress_service_spec.rb'
- 'spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb'
- 'spec/services/error_tracking/list_projects_service_spec.rb'
- 'spec/services/groups/update_service_spec.rb'
@@ -311,7 +290,6 @@ Style/StringConcatenation:
- 'spec/services/snippets/bulk_destroy_service_spec.rb'
- 'spec/services/snippets/update_service_spec.rb'
- 'spec/services/todo_service_spec.rb'
- - 'spec/services/users/destroy_service_spec.rb'
- 'spec/services/verify_pages_domain_service_spec.rb'
- 'spec/support/capybara.rb'
- 'spec/support/helpers/ci_artifact_metadata_generator.rb'
diff --git a/.rubocop_todo/style/string_literals_in_interpolation.yml b/.rubocop_todo/style/string_literals_in_interpolation.yml
index 29e94e77345..f601674a4bf 100644
--- a/.rubocop_todo/style/string_literals_in_interpolation.yml
+++ b/.rubocop_todo/style/string_literals_in_interpolation.yml
@@ -1,10 +1,9 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/StringLiteralsInInterpolation:
Exclude:
- 'app/graphql/mutations/base_mutation.rb'
- 'app/helpers/colors_helper.rb'
- - 'app/helpers/todos_helper.rb'
- 'app/models/application_setting_implementation.rb'
- 'app/models/ci/namespace_mirror.rb'
- 'app/models/integrations/campfire.rb'
@@ -21,7 +20,6 @@ Style/StringLiteralsInInterpolation:
- 'ee/app/models/license.rb'
- 'ee/app/services/epics/tree_reorder_service.rb'
- 'ee/lib/ee/api/helpers/issues_helpers.rb'
- - 'ee/lib/gitlab/elastic/helper.rb'
- 'ee/lib/tasks/gitlab/elastic.rake'
- 'ee/spec/features/admin/admin_settings_spec.rb'
- 'ee/spec/features/subscriptions/expiring_subscription_message_spec.rb'
@@ -29,7 +27,6 @@ Style/StringLiteralsInInterpolation:
- 'lib/api/helpers/snippets_helpers.rb'
- 'lib/api/validations/validators/check_assignees_count.rb'
- 'lib/banzai/filter/references/abstract_reference_filter.rb'
- - 'lib/banzai/filter/syntax_highlight_filter.rb'
- 'lib/generators/gitlab/usage_metric_definition_generator.rb'
- 'lib/gitlab/background_migration/fix_projects_without_prometheus_service.rb'
- 'lib/gitlab/ci/config/entry/job.rb'
diff --git a/.rubocop_todo/style/symbol_proc.yml b/.rubocop_todo/style/symbol_proc.yml
index d373788dbc6..074b400c4bb 100644
--- a/.rubocop_todo/style/symbol_proc.yml
+++ b/.rubocop_todo/style/symbol_proc.yml
@@ -1,5 +1,5 @@
---
-# Cop supports --auto-correct.
+# Cop supports --autocorrect.
Style/SymbolProc:
Exclude:
- 'app/controllers/admin/users_controller.rb'
@@ -13,7 +13,6 @@ Style/SymbolProc:
- 'app/graphql/mutations/commits/create.rb'
- 'app/graphql/mutations/concerns/mutations/work_items/widgetable.rb'
- 'app/graphql/mutations/jira_import/start.rb'
- - 'app/graphql/mutations/work_items/update_widgets.rb'
- 'app/graphql/types/work_items/widgets/description_type.rb'
- 'app/helpers/graph_helper.rb'
- 'app/models/ci/build_metadata.rb'
@@ -54,7 +53,6 @@ Style/SymbolProc:
- 'app/serializers/linked_project_issue_entity.rb'
- 'app/serializers/member_entity.rb'
- 'app/serializers/member_user_entity.rb'
- - 'app/serializers/merge_request_poll_widget_entity.rb'
- 'app/serializers/merge_request_widget_entity.rb'
- 'app/serializers/project_entity.rb'
- 'app/serializers/project_mirror_entity.rb'
@@ -172,7 +170,6 @@ Style/SymbolProc:
- 'lib/gitlab/diff/rendered/notebook/diff_file_helper.rb'
- 'lib/gitlab/git/diff_stats_collection.rb'
- 'lib/gitlab/gitaly_client/commit_service.rb'
- - 'lib/gitlab/gitaly_client/wiki_service.rb'
- 'lib/gitlab/import_export/attributes_finder.rb'
- 'lib/gitlab/import_export/base/relation_object_saver.rb'
- 'lib/gitlab/import_export/fast_hash_serializer.rb'
@@ -199,12 +196,10 @@ Style/SymbolProc:
- 'qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb'
- 'qa/qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb'
- - 'qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb'
- 'qa/qa/specs/features/browser_ui/5_package/container_registry/online_garbage_collection_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/13_secure/enable_scanning_from_configuration_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/3_create/merge_request/approval_rules_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/3_create/repository/file_locking_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/4_verify/pipeline_status_on_operation_dashboard_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/4_verify/pipeline_subscription_with_group_owned_project_spec.rb'
- 'rubocop/cop/gitlab/mark_used_feature_flags.rb'
- 'rubocop/cop/gitlab/namespaced_class.rb'
diff --git a/.ruby-version b/.ruby-version
index a603bb50a29..1f7da99d4e1 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-2.7.5
+2.7.7
diff --git a/.secretsignore b/.secretsignore
deleted file mode 100644
index 071423bd3c1..00000000000
--- a/.secretsignore
+++ /dev/null
@@ -1,66 +0,0 @@
-# This file is for defining paths and secrets that will be ignored by ripsecret
-
-doc/*
-spec/*
-ee/spec/*
-qa/*
-*_spec.rb
-config/gitlab.yml.example
-workhorse/testdata/localhost.key
-db/fixtures/**/*.rb
-
-[secrets]
-AUTO_DEVOPS_DOMAIN
-BACKWARD_DIRECTION
-CI_BUILD_BEFORE_SHA
-CI_BUILD_REF_NAME
-CI_BUILD_REF_SLUG
-CI_COMMIT_BRANCH
-CI_COMMIT_REF_SLUG
-CI_DEFAULT_BRANCH
-CI_DEPLOY_FREEZE
-CI_DEPLOY_PASSWORD
-CI_ENVIRONMENT_SLUG
-CI_ENVIRONMENT_URL
-CI_GITLAB_FIPS_MODE
-CI_JOB_NAME_SLUG
-CI_JOB_STARTED_AT
-CI_PAGES_DOMAIN
-CI_PROJECT_NAME
-CI_PROJECT_PATH
-CI_PROJECT_PATH_SLUG
-CI_PROJECT_VISIBILITY
-CI_REGISTRY_IMAGE
-CI_REGISTRY_PASSWORD
-CI_REPOSITORY_URL
-CROWDIN_API_KEY
-DAST_API_PROFILE
-DAST_PASSWORD_BASE64
-DAST_SUBMIT_FIELD
-DAST_USERNAME_FIELD
-DORA_METRICS_KEYS
-ESCALATION_STATUS
-FIFTY_PACKAGE_FILES
-FORTY_PACKAGE_FILES
-FORWARD_DIRECTION
-GITLAB_FEATURES
-GITLAB_USER_EMAIL
-GITLAB_USER_LOGIN
-GITLAB_USER_NAME
-HARBOR_PASSWORD
-HARBOR_USERNAME
-KUBE_CA_PEM_FILE
-KUBE_SERVICE_ACCOUNT
-NAVSOURCE_VALUE
-ONE_HUNDRED_TAGS
-ONE_PACKAGE_FILE
-STAGING_ENABLED
-TEN_PACKAGE_FILES
-THIRTY_PACKAGE_FILES
-TRIGGER_PAYLOAD
-TWENTY_FIVE_TAGS
-TWENTY_PACKAGE_FILES
-YOUR-ACCESSKEYID
-YOUR-CLIENT-SECRET
-YOUR_AUTH0_CLIENT_SECRET
-sbdMsxcgW2Xs75Q2uHc9FhUCZSEV3fSg
diff --git a/.yamllint b/.yamllint
index 2fddf9ee3c4..5b49a617a57 100644
--- a/.yamllint
+++ b/.yamllint
@@ -2,6 +2,15 @@
extends: default
+yaml-files:
+ # defaults
+ - '*.yaml'
+ - '*.yml'
+ - '.yamllint'
+ # match more extensions
+ - '*.yaml.*'
+ - '*.yml.*'
+
# Ideally, we should have nothing in this ignore section.
#
# Please consider removing entries below by fixing them.
@@ -19,19 +28,32 @@ ignore: |
# Broken on purpose (for testing)
spec/fixtures/lib/gitlab/metrics/dashboard/broken_yml_syntax.yml
+ # Dynamic YAML files have syntax errors sometimes.
+ *.erb
+
+ # Vim temporary files.
+ *.sw[pon]
+
+ # Zipped files (by e.g. asset pipeline)
+ *.gz
+ *.bz2
+
#### Folders ####
node_modules/
tmp/
-# Why disabling all of those rules?
-#
-# For the scope of https://gitlab.com/gitlab-org/gitlab/-/issues/359968,
-# we would like to catch syntax errors as soon as possible.
-# Style "errors" are not as important right now, but they should ideally be added later on.
+# In CI some YAML files are linted using different rules.
+# See `.gitlab/ci/yaml.gitlab-ci.yml`.
#
-# Please consider enabling a rule, and fixing the issues you'll see in an MR.
+# https://gitlab.com/gitlab-org/gitlab/-/issues/385693 tracks to enable all
+# rules below:
rules:
- braces: disable
+ braces:
+ min-spaces-inside: 1
+ max-spaces-inside: 1
+ min-spaces-inside-empty: 0
+ max-spaces-inside-empty: 0
+
colons: disable
comments-indentation: disable
comments: disable
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dbd2866c885..ef9957af9af 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -641,6 +641,26 @@ entry.
- [Update Gitlab Shell to 14.13.0](gitlab-org/gitlab@691262f5c25c17efcfa50307862afa66d07366a4) ([merge request](gitlab-org/gitlab!101372))
- [Migrate card to Pajamas](gitlab-org/gitlab@10577294ed64b13d7668be0c2041ec133e8f7f87) ([merge request](gitlab-org/gitlab!98861)) **GitLab Enterprise Edition**
+## 15.5.6 (2022-12-07)
+
+No changes.
+
+## 15.5.5 (2022-11-30)
+
+### Security (11 changes)
+
+- [Send resolved_address param to gitaly during repository import](gitlab-org/security/gitlab@768edcdca74fa09f7ba50c324aacd86fb71ed7e7) ([merge request](gitlab-org/security/gitlab!2939))
+- [Add size validation during nuspec file extraction](gitlab-org/security/gitlab@27f79d015684896b66e0418db253613e3efa1df7) ([merge request](gitlab-org/security/gitlab!2936))
+- [Cross-site scripting in Jira Integration](gitlab-org/security/gitlab@efcb2fc3110b7cf997b3e1a1e173e6462a54f208) ([merge request](gitlab-org/security/gitlab!2931))
+- [Protect web-hook secret tokens after changing URL](gitlab-org/security/gitlab@00b75ba0c52c10a578091ad89440e8ae78cbe066) ([merge request](gitlab-org/security/gitlab!2921))
+- [Redact secret tokens from web-hook logs](gitlab-org/security/gitlab@27699db7e44e7808f5ec415860ed03c55ae554b0) ([merge request](gitlab-org/security/gitlab!2917))
+- [Prevent unauthorized users from seeing Release information on tag pages](gitlab-org/security/gitlab@112d45bdba5e0d34f77eec1ffaf86443e28b2c8c) ([merge request](gitlab-org/security/gitlab!2926))
+- [Update after_import to expire cache before removing prohibited branches](gitlab-org/security/gitlab@5e84ca50689dceb7614e181ee7addbc3671dc935) ([merge request](gitlab-org/security/gitlab!2904))
+- [Deny all package permissions when group access is restricted by IP](gitlab-org/security/gitlab@23a8ba46641053317c45f58037499235438b5ad8) ([merge request](gitlab-org/security/gitlab!2901))
+- [Redact user emails from project webhook data](gitlab-org/security/gitlab@9f49c4d34fffd598af19d2db548281847855f987) ([merge request](gitlab-org/security/gitlab!2907))
+- [Disallow local URls for build_runner_session if dictated by app setting](gitlab-org/security/gitlab@087415cf7a780c97b1d4055590858a98c673c64b) ([merge request](gitlab-org/security/gitlab!2867))
+- [Prevent token bypass for extenal authorisation](gitlab-org/security/gitlab@96a6193a6e03bd1f76c2792cca404d2e672dfcf4) ([merge request](gitlab-org/security/gitlab!2884))
+
## 15.5.4 (2022-11-11)
### Fixed (3 changes)
@@ -1337,6 +1357,22 @@ entry.
- [Add environment keyword to pages job](gitlab-org/gitlab@73af406f9101da0a2f076ac023de5dfd60c85445) by @edith007 ([merge request](gitlab-org/gitlab!98283))
- [Remove feature flag ci_variables_refactoring_to_variable](gitlab-org/gitlab@f5d1e8277fb8c326082e58536aeae21ab3fd289c) ([merge request](gitlab-org/gitlab!97967))
+## 15.4.6 (2022-11-30)
+
+### Security (11 changes)
+
+- [Send resolved_address param to gitaly during repository import](gitlab-org/security/gitlab@6f1547edf1540be3f5cbb03e1ae210e8a0fd2bab) ([merge request](gitlab-org/security/gitlab!2940))
+- [Add size validation during nuspec file extraction](gitlab-org/security/gitlab@23109b73e8e2570d53401ff97deb893d61764ee1) ([merge request](gitlab-org/security/gitlab!2937))
+- [Cross-site scripting in Jira Integration](gitlab-org/security/gitlab@11182e3f7dfb288473bd6974e80b0caf22b838d8) ([merge request](gitlab-org/security/gitlab!2932))
+- [Protect web-hook secret tokens after changing URL](gitlab-org/security/gitlab@693efefacfbc960b9b9725aef482759e635c9233) ([merge request](gitlab-org/security/gitlab!2922))
+- [Redact secret tokens from web-hook logs](gitlab-org/security/gitlab@28fae3d04c458602a04bf01bc34cf81ff05b897d) ([merge request](gitlab-org/security/gitlab!2918))
+- [Prevent unauthorized users from seeing Release information on tag pages](gitlab-org/security/gitlab@c766fbf97b931f767e98c6c734aa273e85045766) ([merge request](gitlab-org/security/gitlab!2925))
+- [Update after_import to expire cache before removing prohibited branches](gitlab-org/security/gitlab@178b2e45a5792480854d50cdb19b6cafeb38b30d) ([merge request](gitlab-org/security/gitlab!2903))
+- [Deny all package permissions when group access is restricted by IP](gitlab-org/security/gitlab@d2e007409076a96eaef6a0ae3256a63260e019f7) ([merge request](gitlab-org/security/gitlab!2900))
+- [Redact user emails from project webhook data](gitlab-org/security/gitlab@28a93bd8a7d4e55720031e7195c9c2e73ed5056d) ([merge request](gitlab-org/security/gitlab!2908))
+- [Disallow local URls for build_runner_session if dictated by app setting](gitlab-org/security/gitlab@581da1e429952d50ac6762423c2be8bc7fbd957b) ([merge request](gitlab-org/security/gitlab!2868))
+- [Prevent token bypass for extenal authorisation](gitlab-org/security/gitlab@e47447744cfe3b7e91b411c7ac64b20f916222a8) ([merge request](gitlab-org/security/gitlab!2887))
+
## 15.4.5 (2022-11-15)
### Fixed (1 change)
diff --git a/Dockerfile.assets b/Dockerfile.assets
index 403d16cc4ab..ba69a614e88 100644
--- a/Dockerfile.assets
+++ b/Dockerfile.assets
@@ -1,4 +1,4 @@
# Simple container to store assets for later use
FROM scratch
-ADD public/assets /assets/
+COPY public/assets /assets/
CMD /bin/true
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index a219e163842..e0e439caea0 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-15.6.2 \ No newline at end of file
+93bf486f9e7c9afdc19de19763a83783d3742c33
diff --git a/GITLAB_KAS_VERSION b/GITLAB_KAS_VERSION
index 9f6d8f2fdb4..3a3b5071621 100644
--- a/GITLAB_KAS_VERSION
+++ b/GITLAB_KAS_VERSION
@@ -1 +1 @@
-15.6.0
+15.7.0
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION
index af92bdd9f58..efa693ea0ec 100644
--- a/GITLAB_PAGES_VERSION
+++ b/GITLAB_PAGES_VERSION
@@ -1 +1 @@
-1.63.0
+c47edc13a5c4ceb1bc0eca0e9361e2d883d1106c
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index ea212aadaf3..e51d2581626 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-14.13.0
+14.14.0
diff --git a/Gemfile b/Gemfile
index c0af288c39e..417dc56637d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -15,11 +15,11 @@ gem 'bundler-checksum', '~> 0.1.0', path: 'vendor/gems/bundler-checksum', requir
# https://gitlab.com/gitlab-org/gitlab/-/issues/375713
gem 'rails', '~> 6.1.6.1'
-gem 'bootsnap', '~> 1.13.0', require: false
+gem 'bootsnap', '~> 1.15.0', require: false
# Pin openssl to match the version bundled with our supported Rubies.
# See https://stdgems.org/openssl/#gem-version.
-gem 'openssl', '2.2.1'
+gem 'openssl', '2.2.2'
# This gem was originally bundled with Ruby 2.7, but is unbundled as of Ruby 3.
# Since the latest version caused problems with GitLab, we pin this to an older
# version for now.
@@ -52,8 +52,8 @@ gem 'declarative_policy', '~> 1.1.0'
gem 'devise', '~> 4.8.1'
gem 'devise-pbkdf2-encryptable', '~> 0.0.0', path: 'vendor/gems/devise-pbkdf2-encryptable'
gem 'bcrypt', '~> 3.1', '>= 3.1.14'
-gem 'doorkeeper', '~> 5.5.0.rc2'
-gem 'doorkeeper-openid_connect', '~> 1.7.5'
+gem 'doorkeeper', '~> 5.5'
+gem 'doorkeeper-openid_connect', '~> 1.8'
gem 'rexml', '~> 3.2.5'
gem 'ruby-saml', '~> 1.13.0'
gem 'omniauth', '~> 2.1.0'
@@ -107,7 +107,7 @@ gem 'browser', '~> 5.3.1'
gem 'ohai', '~> 16.10'
# GPG
-gem 'gpgme', '~> 2.0.19'
+gem 'gpgme', '~> 2.0.22'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
@@ -166,9 +166,9 @@ gem 'seed-fu', '~> 2.3.7'
gem 'elasticsearch-model', '~> 7.2'
gem 'elasticsearch-rails', '~> 7.2', require: 'elasticsearch/rails/instrumentation'
gem 'elasticsearch-api', '7.13.3'
-gem 'aws-sdk-core', '~> 3.167.0'
+gem 'aws-sdk-core', '~> 3.168.4'
gem 'aws-sdk-cloudformation', '~> 1'
-gem 'aws-sdk-s3', '~> 1.117.1'
+gem 'aws-sdk-s3', '~> 1.117.2'
gem 'faraday_middleware-aws-sigv4', '~>0.3.0'
gem 'typhoeus', '~> 1.4.0' # Used with Elasticsearch to support http keep-alive connections
@@ -190,7 +190,7 @@ gem 'asciidoctor-kroki', '~> 0.7.0', require: false
gem 'rouge', '~> 3.30.0'
gem 'truncato', '~> 0.7.12'
gem 'bootstrap_form', '~> 4.2.0'
-gem 'nokogiri', '~> 1.13.9'
+gem 'nokogiri', '~> 1.13.10'
# Calendar rendering
gem 'icalendar'
@@ -242,7 +242,7 @@ gem 're2', '~> 1.6.0'
# Misc
-gem 'version_sorter', '~> 2.2.4'
+gem 'version_sorter', '~> 2.3'
# Export Ruby Regex to Javascript
gem 'js_regex', '~> 3.8'
@@ -264,9 +264,6 @@ gem 'discordrb-webhooks', '~> 3.4', require: false
gem 'jira-ruby', '~> 2.1.4'
gem 'atlassian-jwt', '~> 0.2.0'
-# Flowdock integration
-gem 'flowdock', '~> 0.7'
-
# Slack integration
gem 'slack-messenger', '~> 2.3.4'
@@ -280,14 +277,14 @@ gem 'asana', '~> 0.10.13'
gem 'ruby-fogbugz', '~> 0.3.0'
# Kubernetes integration
-gem 'kubeclient', '~> 4.9.3'
+gem 'kubeclient', '~> 4.9.3', path: 'vendor/gems/kubeclient'
# Sanitize user input
gem 'sanitize', '~> 6.0'
gem 'babosa', '~> 1.0.4'
# Sanitizes SVG input
-gem 'loofah', '~> 2.19.0'
+gem 'loofah', '~> 2.19.1'
# Working with license
# Detects the open source license the repository includes
@@ -352,16 +349,16 @@ gem 'batch-loader', '~> 2.0.1'
gem 'peek', '~> 1.1'
# Snowplow events tracking
-gem 'snowplow-tracker', '~> 0.6.1'
+gem 'snowplow-tracker', '~> 0.8.0'
# Metrics
gem 'webrick', '~> 1.6.1', require: false
-gem 'prometheus-client-mmap', '~> 0.16', require: 'prometheus/client'
+gem 'prometheus-client-mmap', '~> 0.17', require: 'prometheus/client'
gem 'warning', '~> 1.3.0'
group :development do
- gem 'lefthook', '~> 1.2.0', require: false
+ gem 'lefthook', '~> 1.2.6', require: false
gem 'rubocop'
gem 'solargraph', '~> 0.47.2', require: false
@@ -372,6 +369,8 @@ group :development do
gem 'better_errors', '~> 2.9.1'
gem 'sprite-factory', '~> 1.7'
+
+ gem "listen", "~> 3.7"
end
group :development, :test do
@@ -393,10 +392,10 @@ group :development, :test do
# Generate Fake data
gem 'ffaker', '~> 2.10'
- gem 'spring', '~> 2.1.0'
+ gem 'spring', '~> 4.1.0'
gem 'spring-commands-rspec', '~> 1.0.4'
- gem 'gitlab-styles', '~> 9.0.0', require: false
+ gem 'gitlab-styles', '~> 9.1.0', require: false
gem 'haml_lint', '~> 0.40.0', require: false
gem 'bundler-audit', '~> 0.7.0.1', require: false
@@ -410,8 +409,6 @@ group :development, :test do
gem 'simple_po_parser', '~> 1.1.6', require: false
- gem 'timecop', '~> 0.9.1'
-
gem 'png_quantizator', '~> 0.2.1', require: false
gem 'parallel', '~> 1.19', require: false
@@ -424,7 +421,7 @@ group :development, :test do
end
group :development, :test, :danger do
- gem 'gitlab-dangerfiles', '~> 3.6.2', require: false
+ gem 'gitlab-dangerfiles', '~> 3.6.4', require: false
end
group :development, :test, :coverage do
@@ -506,7 +503,7 @@ gem 'kas-grpc', '~> 0.0.2'
gem 'grpc', '~> 1.42.0'
-gem 'google-protobuf', '~> 3.21', '>= 3.21.9'
+gem 'google-protobuf', '~> 3.21', '>= 3.21.12'
gem 'toml-rb', '~> 2.2.0'
@@ -525,7 +522,7 @@ gem 'grape_logging', '~> 1.8'
gem 'gitlab-net-dns', '~> 0.9.1'
# Countries list
-gem 'countries', '~> 3.0'
+gem 'countries', '~> 4.0.0'
gem 'retriable', '~> 3.1.2'
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 9f19cf6b67f..1816d46b4dd 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -33,11 +33,11 @@
{"name":"awesome_print","version":"1.9.2","platform":"ruby","checksum":"e99b32b704acff16d768b3468680793ced40bfdc4537eb07e06a4be11133786e"},
{"name":"awrence","version":"1.1.1","platform":"ruby","checksum":"9be584c97408ed92d5e1ca11740853646fe270de675f2f8dd44e8233226dfc97"},
{"name":"aws-eventstream","version":"1.2.0","platform":"ruby","checksum":"ffa53482c92880b001ff2fb06919b9bb82fd847cbb0fa244985d2ebb6dd0d1df"},
-{"name":"aws-partitions","version":"1.658.0","platform":"ruby","checksum":"bba2e21fc87c4e68c7ba5c09e3cd2b81d59ca86111ab236eaf9c5a8ae207b3fc"},
+{"name":"aws-partitions","version":"1.674.0","platform":"ruby","checksum":"f96e70d85490bbabc2d4b911bad62412fa5e0a643701499e59f8e38d4ab69128"},
{"name":"aws-sdk-cloudformation","version":"1.41.0","platform":"ruby","checksum":"31e47539719734413671edf9b1a31f8673fbf9688549f50c41affabbcb1c6b26"},
-{"name":"aws-sdk-core","version":"3.167.0","platform":"ruby","checksum":"d371856ad86f8bff08928059ee09b7cb9bca8ebf36bf5081f12424e4f491b624"},
-{"name":"aws-sdk-kms","version":"1.59.0","platform":"ruby","checksum":"6c002ebf8e404625c8338ca12ae69b1329399f9dc1b0ebca474e00ff06700153"},
-{"name":"aws-sdk-s3","version":"1.117.1","platform":"ruby","checksum":"76f6dac5baeb2b78616eb34c6af650c1b7a15c1078b169d1b27e8421904c509d"},
+{"name":"aws-sdk-core","version":"3.168.4","platform":"ruby","checksum":"2c9bf6cb0c19f9d23fe2a9d5eca15381b0b904d19f2dd7801d094528f8632a8c"},
+{"name":"aws-sdk-kms","version":"1.61.0","platform":"ruby","checksum":"fe6f50aed34f38bd421e43fe997780c86beeecef2898573b30ad2467b73f572a"},
+{"name":"aws-sdk-s3","version":"1.117.2","platform":"ruby","checksum":"2159b3cbc45fc4a129f178ce54770023684fad078ce5c0577e8005fe1143ebf6"},
{"name":"aws-sigv4","version":"1.5.1","platform":"ruby","checksum":"d68c87fff4ee843b4b92b23c7f31f957f254ec6eb064181f7119124aab8b8bb4"},
{"name":"azure-storage-blob","version":"2.0.3","platform":"ruby","checksum":"61b76118843c91776bd24bee22c74adafeb7c4bb3a858a325047dae3b59d0363"},
{"name":"azure-storage-common","version":"2.0.4","platform":"ruby","checksum":"608f4daab0e06b583b73dcffd3246ea39e78056de31630286b0cf97af7d6956b"},
@@ -57,7 +57,7 @@
{"name":"bindata","version":"2.4.11","platform":"ruby","checksum":"c38e0c99ffcd80c10a0a7ae6c8586d2fe26bf245cbefac90bec8764523220f6a"},
{"name":"binding_ninja","version":"0.2.3","platform":"java","checksum":"bbcf70b211d6e397493bf57c249bbec6aaf28fa7dafeb78e447b1b2f0610484f"},
{"name":"binding_ninja","version":"0.2.3","platform":"ruby","checksum":"4a85550a0066ee4721506b4e150857486808e50c9ddfeed04bdc896bb61eca9d"},
-{"name":"bootsnap","version":"1.13.0","platform":"ruby","checksum":"c673282ec0f48506f093ca9acefe0f666d1ab9fda716e49fb95c9fe677653e78"},
+{"name":"bootsnap","version":"1.15.0","platform":"ruby","checksum":"f246bb1152159098f5d5619b92e373c73db77769bf3e0c4b6336feeb934bc8d2"},
{"name":"bootstrap_form","version":"4.2.0","platform":"ruby","checksum":"f578b3c900d2cf15fab641064d357318b29e285bd5fdf090f903727912889710"},
{"name":"browser","version":"5.3.1","platform":"ruby","checksum":"62745301701ff2c6c5d32d077bb12532b20be261929dcb52c6781ed0d5658b3c"},
{"name":"builder","version":"3.2.4","platform":"ruby","checksum":"99caf08af60c8d7f3a6b004029c4c3c0bdaebced6c949165fe98f1db27fbbc10"},
@@ -83,10 +83,9 @@
{"name":"commonmarker","version":"0.23.6","platform":"ruby","checksum":"c8aeaaaff4ba497bf180f762db63a0069794fafb6eff221224c9c8199d337b38"},
{"name":"concurrent-ruby","version":"1.1.10","platform":"ruby","checksum":"244cb1ca0d91ec2c15ca2209507c39fb163336994428e16fbd3f465c87bd8e68"},
{"name":"connection_pool","version":"2.3.0","platform":"ruby","checksum":"677985be912f33c90f98f229aaa0c0ddb2ef8776f21929a36eeeb25251c944da"},
-{"name":"contracts","version":"0.11.0","platform":"ruby","checksum":"df6e438efa89c31dd3095851c3f7a25dfdae36b35ff1d4547f1d92941b3c7286"},
{"name":"cork","version":"0.3.0","platform":"ruby","checksum":"a0a0ac50e262f8514d1abe0a14e95e71c98b24e3378690e5d044daf0013ad4bc"},
{"name":"cose","version":"1.0.0","platform":"ruby","checksum":"520ebaad97b56d2873de02ff4e2c973f5e77ce2f8edbda454af9ee3073643bc0"},
-{"name":"countries","version":"3.0.0","platform":"ruby","checksum":"ecb4287436f83f4bb098a9462828b145bec3143fa49e7ce5b1714d0ee5454770"},
+{"name":"countries","version":"4.0.1","platform":"ruby","checksum":"d32e8a3c0b22949f1a41ea6d9005f5168ffce226f8fe077d1d6be785fffa81c5"},
{"name":"crack","version":"0.4.3","platform":"ruby","checksum":"5318ba8cd9cf7e0b5feb38948048503ba4b1fdc1b6ff30a39f0a00feb6036b29"},
{"name":"crass","version":"1.0.6","platform":"ruby","checksum":"dc516022a56e7b3b156099abc81b6d2b08ea1ed12676ac7a5657617f012bd45d"},
{"name":"creole","version":"0.5.0","platform":"ruby","checksum":"951701e2d80760f156b1cb2a93471ca97c076289becc067a33b745133ed32c03"},
@@ -113,8 +112,8 @@
{"name":"discordrb-webhooks","version":"3.4.2","platform":"ruby","checksum":"cfdba8a4b28236b6ab34e37389f881a59c241aeb5be0a4447249efd4e4383c6e"},
{"name":"docile","version":"1.4.0","platform":"ruby","checksum":"5f1734bde23721245c20c3d723e76c104208e1aa01277a69901ce770f0ebb8d3"},
{"name":"domain_name","version":"0.5.20190701","platform":"ruby","checksum":"000a600454cb4a344769b2f10b531765ea7bd3a304fe47ed12e5ca1eab969851"},
-{"name":"doorkeeper","version":"5.5.0.rc2","platform":"ruby","checksum":"93a322ffca3cadbfb862b0199f78674d8372780afdd7471e657064610bb7b2d5"},
-{"name":"doorkeeper-openid_connect","version":"1.7.5","platform":"ruby","checksum":"2dea201ffd9e4bec573609c90bb638254a48bfa3de3ec4af892ec64e0b5947b2"},
+{"name":"doorkeeper","version":"5.5.4","platform":"ruby","checksum":"7fe233a96f93bf0d5496e2284abf431f38ab465fd65d1972b90cbec7c45b1ea1"},
+{"name":"doorkeeper-openid_connect","version":"1.8.3","platform":"ruby","checksum":"0df2e714508f1f43fdb4669e97b38b90d365a072908427416da943a1a8e00b6e"},
{"name":"dotenv","version":"2.7.6","platform":"ruby","checksum":"2451ed5e8e43776d7a787e51d6f8903b98e446146c7ad143d5678cc2c409d547"},
{"name":"dry-configurable","version":"0.12.0","platform":"ruby","checksum":"87a9579a04dfbae73e401d694282800d64bbdb8631cb3e987bfb79b673df7c67"},
{"name":"dry-container","version":"0.7.2","platform":"ruby","checksum":"a071824ba3451048b23500210f96a2b9facd6e46ac687f65e49c75d18786f6da"},
@@ -178,7 +177,6 @@
{"name":"flipper","version":"0.25.0","platform":"ruby","checksum":"ccb2776752b8378bc994c9d873ccde290c090341940761b873494695ee697add"},
{"name":"flipper-active_record","version":"0.25.0","platform":"ruby","checksum":"85a5c99465e2cc6a09e91931a9998b0dbd463cd6c80dd513129377132e3eb67f"},
{"name":"flipper-active_support_cache_store","version":"0.25.0","platform":"ruby","checksum":"7282bf994b08d1a076b65c6f3b51e3dc04fcb00fa6e7b20089e60db25c7b531b"},
-{"name":"flowdock","version":"0.7.1","platform":"ruby","checksum":"cfa95b2ac96e5f883f6e419d7a891f76cfcc17a28c416b6b714bbdffc8dbd912"},
{"name":"fog-aliyun","version":"0.3.3","platform":"ruby","checksum":"d0aa317f7c1473a1d684fff51699f216bb9cb78b9ee9ce55a81c9bcc93fb85ee"},
{"name":"fog-aws","version":"3.15.0","platform":"ruby","checksum":"09752931ea0c6165b018e1a89253248d86b246645086ccf19bc44fabe3381e8c"},
{"name":"fog-core","version":"2.1.0","platform":"ruby","checksum":"53e5d793554d7080d015ef13cd44b54027e421d924d9dba4ce3d83f95f37eda9"},
@@ -200,9 +198,9 @@
{"name":"gettext_i18n_rails_js","version":"1.3.0","platform":"ruby","checksum":"5d10afe4be3639bff78c50a56768c20f39aecdabc580c08aa45573911c2bd687"},
{"name":"git","version":"1.11.0","platform":"ruby","checksum":"7e95ba4da8298a0373ef1a6862aa22007d761f3c8274b675aa787966fecea0f1"},
{"name":"gitaly","version":"15.5.2","platform":"ruby","checksum":"62babe0596a4505bf95051ea50f17160055e6cf6cacf209273691542120d7881"},
-{"name":"gitlab","version":"4.16.1","platform":"ruby","checksum":"13fd7059cbdad5a1a21b15fa2cf9070b97d92e27f8c688581fe3d84dc038074f"},
+{"name":"gitlab","version":"4.19.0","platform":"ruby","checksum":"3f645e3e195dbc24f0834fbf83e8ccfb2056d8e9712b01a640aad418a6949679"},
{"name":"gitlab-chronic","version":"0.10.5","platform":"ruby","checksum":"f80f18dc699b708870a80685243331290bc10cfeedb6b99c92219722f729c875"},
-{"name":"gitlab-dangerfiles","version":"3.6.2","platform":"ruby","checksum":"88585532bbb5c0e862ad0776b3804a32129eab06c6a8a7bc96b577baa7aac6c5"},
+{"name":"gitlab-dangerfiles","version":"3.6.4","platform":"ruby","checksum":"864ea24440349ef233ede0d767537d33be4e3c719b298dfd3244b70b4d01756c"},
{"name":"gitlab-experiment","version":"0.7.1","platform":"ruby","checksum":"166dddb3aa83428bcaa93c35684ed01dc4d61f321fd2ae40b020806dc54a7824"},
{"name":"gitlab-fog-azure-rm","version":"1.4.0","platform":"ruby","checksum":"af4163c32b028aa5208814a3f4765a5817d50527e6c61931f766bf18a2e0eb7e"},
{"name":"gitlab-labkit","version":"0.29.0","platform":"ruby","checksum":"eb19ac5c11698683775ab847a3441d7af87d72fbaec38d635149fb65c5d9b427"},
@@ -212,7 +210,7 @@
{"name":"gitlab-net-dns","version":"0.9.1","platform":"ruby","checksum":"bcd1a08dcb31b731e8ff602d828de619d2d9f53f5812f6abacf11c720873d4cb"},
{"name":"gitlab-omniauth-openid-connect","version":"0.10.0","platform":"ruby","checksum":"ea44a23ea93457057bba6a9912e883f5aefab36a941c6c58512c8a7095fb1153"},
{"name":"gitlab-sidekiq-fetcher","version":"0.9.0","platform":"ruby","checksum":"54041aec059f20c8e6dfce394e1b60e0c0a9c7cef32da912a58abbd333e13897"},
-{"name":"gitlab-styles","version":"9.0.0","platform":"ruby","checksum":"ef0edfab8f807a5be2309ba24dfc44fec5ba52ed68b87167c051e9ffdadb3bad"},
+{"name":"gitlab-styles","version":"9.1.0","platform":"ruby","checksum":"46fd4e9f7fc74b0dfdb0bd7aa2f5796fb4d5b01e5886d7779726f8b53a3c02b2"},
{"name":"gitlab_chronic_duration","version":"0.10.6.2","platform":"ruby","checksum":"6dda4cfe7dca9b958f163ac8835c3d9cc70cf8df8cbb89bb2fbf9ba4375105fb"},
{"name":"gitlab_omniauth-ldap","version":"2.2.0","platform":"ruby","checksum":"bb4d20acb3b123ed654a8f6a47d3fac673ece7ed0b6992edb92dca14bad2838c"},
{"name":"globalid","version":"1.0.0","platform":"ruby","checksum":"1253641b1dc3392721c964351773755d75135d3d3c5cc65d88b0a3880a60bed8"},
@@ -229,17 +227,17 @@
{"name":"google-apis-sqladmin_v1beta4","version":"0.38.0","platform":"ruby","checksum":"d00279cdcc5548bf4f4e40cc29cbd942b79708011e59c75a18726b6826be1665"},
{"name":"google-apis-storage_v1","version":"0.20.0","platform":"ruby","checksum":"8a1ace07fc909966d6f76e777d6adc7d86dddd91a629fef8914ebd5baf86d850"},
{"name":"google-cloud-env","version":"1.6.0","platform":"ruby","checksum":"6179acb946975892c7908748df5722a4ebadfc8cf5bb7b0d8d933ca67183fa15"},
-{"name":"google-protobuf","version":"3.21.9","platform":"java","checksum":"8483ab2487170434f7a139d6534b3a166e4ec244a6fd8929f758d87abbb82fee"},
-{"name":"google-protobuf","version":"3.21.9","platform":"ruby","checksum":"5a656c159aa2c85008af7eab3f603cf22921b748e09438f6682dcf696d518adc"},
-{"name":"google-protobuf","version":"3.21.9","platform":"x64-mingw-ucrt","checksum":"7cb37b76241150212703f0ac582555f6fda1c7c66f58c1164667e783141e25fe"},
-{"name":"google-protobuf","version":"3.21.9","platform":"x64-mingw32","checksum":"54df7b9df435cc5c715261fbe8897fe03dd4b0e68e052aa0bb814c31bc66ef35"},
-{"name":"google-protobuf","version":"3.21.9","platform":"x86-linux","checksum":"11f28f344f6b6afa78fa0688379e39fbc86da4c199f04a51da7a29cf2db8205d"},
-{"name":"google-protobuf","version":"3.21.9","platform":"x86-mingw32","checksum":"a2dce43556196b6bb0fce2cf28df70fdca4255607fb9e1ffb7ee611953436a9a"},
-{"name":"google-protobuf","version":"3.21.9","platform":"x86_64-darwin","checksum":"9e948a08ee27cca8acf794c798db16d918ce503eae06525d7551dc05ac3324c0"},
-{"name":"google-protobuf","version":"3.21.9","platform":"x86_64-linux","checksum":"d4053012022f7bf47cd54c7c19416f600325e6cc1e1604a631c2fde69dd920a4"},
+{"name":"google-protobuf","version":"3.21.12","platform":"java","checksum":"35362ef8abf98ad597dffee588390b8b3b2f0f3d70261c3eed3f99e564f3289d"},
+{"name":"google-protobuf","version":"3.21.12","platform":"ruby","checksum":"4b09bb7e3168cda689efebcd3373304e124b14aabf776fbf1f0a7615259c8fb5"},
+{"name":"google-protobuf","version":"3.21.12","platform":"x64-mingw-ucrt","checksum":"e4444119acd56bf4661b3f38dc2795abae2cd5c2ade88154d5fc405008fbdcf7"},
+{"name":"google-protobuf","version":"3.21.12","platform":"x64-mingw32","checksum":"e6a879e1100f04506aea352d22f70a0ed77899fc64af3ff8c24a242331be923d"},
+{"name":"google-protobuf","version":"3.21.12","platform":"x86-linux","checksum":"54bbacbca58323fab222746df30e60a55df89f699e319ce0774d5bdd637b3a54"},
+{"name":"google-protobuf","version":"3.21.12","platform":"x86-mingw32","checksum":"979e6388dd5f3171043c5a00ac2f66b2789d7fc67b18207d1aabfa1dc27d9558"},
+{"name":"google-protobuf","version":"3.21.12","platform":"x86_64-darwin","checksum":"d7e59bd1040e510fd67fb96d08be84a4e362641f5229bf3fd870e383b2913574"},
+{"name":"google-protobuf","version":"3.21.12","platform":"x86_64-linux","checksum":"cb6820a68c7807e12ca1e6b69689b833d675ed81435a2179d502575ed5db3de0"},
{"name":"googleapis-common-protos-types","version":"1.3.0","platform":"ruby","checksum":"c5411f3197cc3e02547ded1858303b1f830b4dc89c588c142ad6c8a231050671"},
{"name":"googleauth","version":"1.3.0","platform":"ruby","checksum":"51dd7362353cf1e90a2d01e1fb94321ae3926c776d4dc4a79db65230217ffcc2"},
-{"name":"gpgme","version":"2.0.20","platform":"ruby","checksum":"fc194689cff40cd4ccafb3086031e930650b3efc15348bbfdf7a2f8b5a826f75"},
+{"name":"gpgme","version":"2.0.22","platform":"ruby","checksum":"7c6904952afdd0bf2c7c3ed6de98a5143f86c6b7390dbcd9d7012bddfa3ec862"},
{"name":"grape","version":"1.5.2","platform":"ruby","checksum":"1df3b734c3862e235174232bc629587eddda9ef3df648230827575186700ae29"},
{"name":"grape-entity","version":"0.10.0","platform":"ruby","checksum":"9aed1e7cbbc96d9e73f72e5f32c776d4ba8a5baf54c3acda2682008dba2b2cfe"},
{"name":"grape-path-helpers","version":"1.7.1","platform":"ruby","checksum":"2e27271a20d4073e3a3b2b955425c7f803e198be3ba8f6e59e3d59643c5381e2"},
@@ -276,15 +274,14 @@
{"name":"html2text","version":"0.2.0","platform":"ruby","checksum":"31c2f0be9ab7aa4fc780b07d5f84882ebc22a9024c29a45f4f5adfe42e92ad4f"},
{"name":"htmlbeautifier","version":"1.4.2","platform":"ruby","checksum":"9de0c98480fe80d795ed5734a11f183563cd969686f25a04609c0f5a446fa5f8"},
{"name":"htmlentities","version":"4.3.4","platform":"ruby","checksum":"125a73c6c9f2d1b62100b7c3c401e3624441b663762afa7fe428476435a673da"},
-{"name":"http","version":"4.4.1","platform":"ruby","checksum":"be10f1d054fcc732ac32410553767ac3e4141b4182a4d8f5bf93d23cedae1b7d"},
+{"name":"http","version":"5.1.0","platform":"ruby","checksum":"b21e4f0dac51f52df001f1fa3dd3b0a8aadf3d8468d3c520c0caddeeb4f1c14a"},
{"name":"http-accept","version":"1.7.0","platform":"ruby","checksum":"c626860682bfbb3b46462f8c39cd470fd7b0584f61b3cc9df5b2e9eb9972a126"},
{"name":"http-cookie","version":"1.0.5","platform":"ruby","checksum":"73756d46c7dbdc7023deecdb8a171348ea95a1b99810b31cfe8b4fb4e9a6318f"},
{"name":"http-form_data","version":"2.3.0","platform":"ruby","checksum":"cc4eeb1361d9876821e31d7b1cf0b68f1cf874b201d27903480479d86448a5f3"},
-{"name":"http-parser","version":"1.2.3","platform":"ruby","checksum":"414dec1f443d68e1068509f184ee4b93e3442f626645071182ce49bc27db18a3"},
{"name":"httparty","version":"0.20.0","platform":"ruby","checksum":"490d2a028a5accc611f1685d479d80ef80b129140d24a93c53c119f578614867"},
{"name":"httpclient","version":"2.8.3","platform":"ruby","checksum":"2951e4991214464c3e92107e46438527d23048e634f3aee91c719e0bdfaebda6"},
{"name":"i18n","version":"1.12.0","platform":"ruby","checksum":"91e3cc1b97616d308707eedee413d82ee021d751c918661fb82152793e64aced"},
-{"name":"i18n_data","version":"0.8.0","platform":"ruby","checksum":"92d942cc193dc4a54a95b68f44e52c79e024fa72e09f26a982bc61153b6f0c6c"},
+{"name":"i18n_data","version":"0.13.1","platform":"ruby","checksum":"e5aa99b09a69b463bb0443fc1f9540351a49f3d1541c5e91316bafa035c63f66"},
{"name":"icalendar","version":"2.8.0","platform":"ruby","checksum":"e404f970c7572bdebf6f09f9890970b68aab400ba9e609dc7d46098f28d0ee87"},
{"name":"ice_cube","version":"0.16.4","platform":"ruby","checksum":"da117e5de24bdc33931be629f9b55048641924442c7e9b72fedc05e5592531b7"},
{"name":"imagen","version":"0.1.8","platform":"ruby","checksum":"fde7b727d4fe79c6bb5ac46c1f7184bf87a6d54df54d712ad2be039d2f93a162"},
@@ -295,7 +292,7 @@
{"name":"jaro_winkler","version":"1.5.4","platform":"java","checksum":"0454333a50b44a09745878bfe57859893631ff7dfe48c029827894944514fe7c"},
{"name":"jaro_winkler","version":"1.5.4","platform":"ruby","checksum":"50c3e83c5a9e8769c1cf5b73c8b51bb6eebbf8852a0ee53bf6ad6e4dc63414f9"},
{"name":"jira-ruby","version":"2.1.4","platform":"ruby","checksum":"4267c095cac8323b9eef3ba866eb28bb1388b7623a5abb60c1e7caf12d4adb9e"},
-{"name":"jmespath","version":"1.6.1","platform":"ruby","checksum":"40ca83f4141bdd1e503db5485de68b84237183d84cf7a159fbeebcc6005adbd6"},
+{"name":"jmespath","version":"1.6.2","platform":"ruby","checksum":"238d774a58723d6c090494c8879b5e9918c19485f7e840f2c1c7532cf84ebcb1"},
{"name":"js_regex","version":"3.8.0","platform":"ruby","checksum":"7934bcdd5a0e6d5af4a520288fd4684a02a472ae55831d9178ccaf82356344b5"},
{"name":"json","version":"2.5.1","platform":"java","checksum":"be284a0c4a9d0373e81b0d5dfe71ed5b18d0479f05970e60a77be89a2978ce6c"},
{"name":"json","version":"2.5.1","platform":"ruby","checksum":"918d8c41dacb7cfdbe0c7bbd6014a5372f0cf1c454ca150e9f4010fe80cc3153"},
@@ -311,19 +308,19 @@
{"name":"knapsack","version":"1.21.1","platform":"ruby","checksum":"82f70422adebcacec1b514f6ebff65265fc85d836e3c320718a160d8ac41cf14"},
{"name":"kramdown","version":"2.3.2","platform":"ruby","checksum":"cb4530c2e9d16481591df2c9336723683c354e5416a5dd3e447fa48215a6a71c"},
{"name":"kramdown-parser-gfm","version":"1.1.0","platform":"ruby","checksum":"fb39745516427d2988543bf01fc4cf0ab1149476382393e0e9c48592f6581729"},
-{"name":"kubeclient","version":"4.9.3","platform":"ruby","checksum":"d5d38e719fbac44f396851aa57cd1b9f4f7dab4410ab680ccd21c9b741230046"},
{"name":"launchy","version":"2.5.0","platform":"ruby","checksum":"954243c4255920982ce682f89a42e76372dba94770bf09c23a523e204bdebef5"},
-{"name":"lefthook","version":"1.2.0","platform":"ruby","checksum":"189e8c2c91eac4ed115ab67e4d9a3f6b7f280967c45c4ea5fdca7612088c73ab"},
+{"name":"lefthook","version":"1.2.6","platform":"ruby","checksum":"8d8ab03a559d1f5d40b4416072edf5ebb22eddfc74b75479458edd5318a3de63"},
{"name":"letter_opener","version":"1.7.0","platform":"ruby","checksum":"095bc0d58e006e5b43ea7d219e64ecf2de8d1f7d9dafc432040a845cf59b4725"},
{"name":"letter_opener_web","version":"2.0.0","platform":"ruby","checksum":"33860ad41e1785d75456500e8ca8bba8ed71ee6eaf08a98d06bbab67c5577b6f"},
{"name":"libyajl2","version":"1.2.0","platform":"ruby","checksum":"1117cd1e48db013b626e36269bbf1cef210538ca6d2e62d3fa3db9ded005b258"},
{"name":"license_finder","version":"7.0.1","platform":"ruby","checksum":"0b22c9567e2a8b102c7245da49ebeddaec60f66d237d2bb91b9feddf5d242f6a"},
{"name":"licensee","version":"9.15.2","platform":"ruby","checksum":"4b6959b544da88499d3be0d9f486179c90b93d5049ef500ae340ac1420493ded"},
{"name":"listen","version":"3.7.1","platform":"ruby","checksum":"3b80caa7aa77fae836916c2f9e3fbcafbd15f5d695dd487c1f5b5e7e465efe29"},
+{"name":"llhttp-ffi","version":"0.4.0","platform":"ruby","checksum":"e5f7327db3cf8007e648342ef76347d6e0ae545a8402e519cca9c886eb37b001"},
{"name":"locale","version":"2.1.3","platform":"ruby","checksum":"b6ddee011e157817cb98e521b3ce7cb626424d5882f1e844aafdee3e8b212725"},
{"name":"lockbox","version":"0.6.2","platform":"ruby","checksum":"0136677875c3d6e27cef87cd7bd66610404e2b3cd7f07f1ac8ed34e48f18dc3c"},
{"name":"lograge","version":"0.11.2","platform":"ruby","checksum":"4cbd1554b86f545d795eff15a0c24fd25057d2ac4e1caa5fc186168b3da932ef"},
-{"name":"loofah","version":"2.19.0","platform":"ruby","checksum":"302791371f473611e342f9e469e7f2fbf1155bb1b3a978a83ac7df625298feba"},
+{"name":"loofah","version":"2.19.1","platform":"ruby","checksum":"6c6469efdefe3496010000a346f9d3bf710e11ac4661e353cf56852326fb1023"},
{"name":"lookbook","version":"1.2.1","platform":"ruby","checksum":"742844b625798b689215d1660f711aa79ff54084f5e8735fe674fe771fc165d7"},
{"name":"lru_redux","version":"1.1.0","platform":"ruby","checksum":"ee71d0ccab164c51de146c27b480a68b3631d5b4297b8ffe8eda1c72de87affb"},
{"name":"lumberjack","version":"1.2.7","platform":"ruby","checksum":"a5c6aae6b4234f1420dbcd80b23e3bca0817bd239440dde097ebe3fa63c63b1f"},
@@ -367,16 +364,16 @@
{"name":"nio4r","version":"2.5.8","platform":"java","checksum":"b2b1800f6bf7ce4b797ca8b639ad278a99c9c904fb087a91d944f38e4bd71401"},
{"name":"nio4r","version":"2.5.8","platform":"ruby","checksum":"3becb4ad95ab8ac0a9bd2e1b16466869402be62848082bf6329ae9091f276676"},
{"name":"no_proxy_fix","version":"0.1.2","platform":"ruby","checksum":"4e9b4c31bb146de7fcf347dc1087bb13ac2039b56d50aa019e61036256abcd00"},
-{"name":"nokogiri","version":"1.13.9","platform":"aarch64-linux","checksum":"9b69829561d30c4461ea803baeaf3460e8b145cff7a26ce397119577a4083a02"},
-{"name":"nokogiri","version":"1.13.9","platform":"arm64-darwin","checksum":"e76ebb4b7b2e02c72b2d1541289f8b0679fb5984867cf199d89b8ef485764956"},
-{"name":"nokogiri","version":"1.13.9","platform":"java","checksum":"15bae7d08bddeaa898d8e3f558723300137c26a2dc2632a1f89c8574c4467165"},
-{"name":"nokogiri","version":"1.13.9","platform":"ruby","checksum":"96f37c1baf0234d3ae54c2c89aef7220d4a8a1b03d2675ff7723565b0a095531"},
-{"name":"nokogiri","version":"1.13.9","platform":"x64-mingw-ucrt","checksum":"f6a1dbc7229184357f3129503530af73cc59ceba4932c700a458a561edbe04b9"},
-{"name":"nokogiri","version":"1.13.9","platform":"x64-mingw32","checksum":"36d935d799baa4dc488024f71881ff0bc8b172cecdfc54781169c40ec02cbdb3"},
-{"name":"nokogiri","version":"1.13.9","platform":"x86-linux","checksum":"ebaf82aa9a11b8fafb67873d19ee48efb565040f04c898cdce8ca0cd53ff1a12"},
-{"name":"nokogiri","version":"1.13.9","platform":"x86-mingw32","checksum":"11789a2a11b28bc028ee111f23311461104d8c4468d5b901ab7536b282504154"},
-{"name":"nokogiri","version":"1.13.9","platform":"x86_64-darwin","checksum":"01830e1646803ff91c0fe94bc768ff40082c6de8cfa563dafd01b3f7d5f9d795"},
-{"name":"nokogiri","version":"1.13.9","platform":"x86_64-linux","checksum":"8e93b8adec22958013799c8690d81c2cdf8a90b6f6e8150ab22e11895844d781"},
+{"name":"nokogiri","version":"1.13.10","platform":"aarch64-linux","checksum":"777ce2e80f64772e91459b943e531dfef387e768f2255f9bc7a1655f254bbaa1"},
+{"name":"nokogiri","version":"1.13.10","platform":"arm64-darwin","checksum":"b432ff47c51386e07f7e275374fe031c1349e37eaef2216759063bc5fa5624aa"},
+{"name":"nokogiri","version":"1.13.10","platform":"java","checksum":"73ac581ddcb680a912e92da928ffdbac7b36afd3368418f2cee861b96e8c830b"},
+{"name":"nokogiri","version":"1.13.10","platform":"ruby","checksum":"d3ee00f26c151763da1691c7fc6871ddd03e532f74f85101f5acedc2d099e958"},
+{"name":"nokogiri","version":"1.13.10","platform":"x64-mingw-ucrt","checksum":"916aa17e624611dddbf2976ecce1b4a80633c6378f8465cff0efab022ebc2900"},
+{"name":"nokogiri","version":"1.13.10","platform":"x64-mingw32","checksum":"0f85a1ad8c2b02c166a6637237133505b71a05f1bb41b91447005449769bced0"},
+{"name":"nokogiri","version":"1.13.10","platform":"x86-linux","checksum":"91fa3a8724a1ce20fccbd718dafd9acbde099258183ac486992a61b00bb17020"},
+{"name":"nokogiri","version":"1.13.10","platform":"x86-mingw32","checksum":"d6663f5900ccd8f72d43660d7f082565b7ffcaade0b9a59a74b3ef8791034168"},
+{"name":"nokogiri","version":"1.13.10","platform":"x86_64-darwin","checksum":"81755fc4b8130ef9678c76a2e5af3db7a0a6664b3cba7d9fe8ef75e7d979e91b"},
+{"name":"nokogiri","version":"1.13.10","platform":"x86_64-linux","checksum":"51d5246705dedad0a09b374d09cc193e7383a5dd32136a690a3cd56e95adf0a3"},
{"name":"notiffany","version":"0.1.3","platform":"ruby","checksum":"d37669605b7f8dcb04e004e6373e2a780b98c776f8eb503ac9578557d7808738"},
{"name":"numerizer","version":"0.2.0","platform":"ruby","checksum":"e58076d5ee5370417b7e52d9cb25836d62acd1b8d9a194c308707986c1705d7b"},
{"name":"oauth","version":"0.5.6","platform":"ruby","checksum":"4085fe28e0c5e2434135e00a6555294fd2a4ff96a98d1bdecdcd619fc6368dff"},
@@ -403,7 +400,7 @@
{"name":"omniauth-twitter","version":"1.4.0","platform":"ruby","checksum":"c5cc6c77cd767745ffa9ebbd5fbd694a3fa99d1d2d82a4d7def0bf3b6131b264"},
{"name":"open4","version":"1.3.4","platform":"ruby","checksum":"a1df037310624ecc1ea1d81264b11c83e96d0c3c1c6043108d37d396dcd0f4b1"},
{"name":"openid_connect","version":"1.3.0","platform":"ruby","checksum":"a796855096850cc01140e37ea6ae9fd14f2be818b9b5bc698418063dfe228770"},
-{"name":"openssl","version":"2.2.1","platform":"ruby","checksum":"f6afbf4b66f3fcd3c08dc1da1ddd2245b76c19d0ea2dd7e2c8b55794ca1a7d72"},
+{"name":"openssl","version":"2.2.2","platform":"ruby","checksum":"53f72382bac046c36c37049c7ec9d5597d42628d140b5cfbcd61e0226c0ca077"},
{"name":"openssl-signature_algorithm","version":"0.4.0","platform":"ruby","checksum":"e53a225b773784935249cf4c61238c6cf0e1e464e78ae2f8ddaf995fb22ca991"},
{"name":"opentracing","version":"0.5.0","platform":"ruby","checksum":"deb5d7abe6b0e7631d866d8cb5ee7bb9352650a504a32f61591302bc510b9286"},
{"name":"optimist","version":"3.0.1","platform":"ruby","checksum":"336b753676d6117cad9301fac7e91dab4228f747d4e7179891ad3a163c64e2ed"},
@@ -414,7 +411,7 @@
{"name":"pact-mock_service","version":"3.10.0","platform":"ruby","checksum":"898ec3b8d96f1934d15941c701ca7d5fef5ccff32022d9a196fb82073cd95e27"},
{"name":"pact-support","version":"1.18.1","platform":"ruby","checksum":"4a25961c8b1c4132e433a8eaa838b1e6914c6d3aae48eee705b9860a5e8b0476"},
{"name":"parallel","version":"1.22.1","platform":"ruby","checksum":"ebdf1f0c51f182df38522f70ba770214940bef998cdb6e00f36492b29699761f"},
-{"name":"parser","version":"3.1.2.1","platform":"ruby","checksum":"57e49821b52d5fe7baffaca44ed77e9754688c9bbc68443b5293a722fdb161e0"},
+{"name":"parser","version":"3.1.3.0","platform":"ruby","checksum":"4593da6a6c0dc1b0a0b47b68aa79c36655e19b9d8636f7c27d02a76cb7840e9f"},
{"name":"parslet","version":"1.8.2","platform":"ruby","checksum":"08d1ab3721cd3f175bfbee8788b2ddff71f92038f2d69bd65454c22bb9fbd98a"},
{"name":"pastel","version":"0.8.0","platform":"ruby","checksum":"481da9fb7d2f6e6b1a08faf11fa10363172dc40fd47848f096ae21209f805a75"},
{"name":"peek","version":"1.1.0","platform":"ruby","checksum":"d6501ead8cde46d8d8ed0d59eb6f0ba713d0a41c11a2c4a81447b2dce37b3ecc"},
@@ -429,7 +426,7 @@
{"name":"premailer","version":"1.16.0","platform":"ruby","checksum":"03e4402c448e6bae13fb5f6301a8bde4f3508e1bff90ae7c0972c7be94694786"},
{"name":"premailer-rails","version":"1.10.3","platform":"ruby","checksum":"7cdcb97027866f7a81c490c6d15ada7f39666b5f6375f0821b7e97e0483b112f"},
{"name":"proc_to_ast","version":"0.1.0","platform":"ruby","checksum":"92a73fa66e2250a83f8589f818b0751bcf227c68f85916202df7af85082f8691"},
-{"name":"prometheus-client-mmap","version":"0.16.2","platform":"ruby","checksum":"36e7e96fdd603c2d1fed050ec71504797f3f8b2560123306ba72018ee3561165"},
+{"name":"prometheus-client-mmap","version":"0.17.0","platform":"ruby","checksum":"766d3706f7b26fed5a177843ab15b5b0dc108f9677d8bdbe0c4b5d9375c2af24"},
{"name":"pry","version":"0.13.1","platform":"java","checksum":"9612d825e2c3bc160633b2a4fae21041126ee33f1ac8035c851417e561b2b46c"},
{"name":"pry","version":"0.13.1","platform":"ruby","checksum":"1393918c415af46b6d09044d2b78dde92b29bc834fd85c369a950bab0826dc47"},
{"name":"pry-byebug","version":"3.9.0","platform":"ruby","checksum":"3bba08f97fea15b89cc299f3b5136e3b85763cd18cf84960eac4fbfbeb2ede24"},
@@ -441,8 +438,8 @@
{"name":"puma_worker_killer","version":"0.3.1","platform":"ruby","checksum":"9c5534d296b5e92d1ad4a578f2daf2aa71563003c84f7263f0a8dfd22b5c614a"},
{"name":"pyu-ruby-sasl","version":"0.0.3.3","platform":"ruby","checksum":"5683a6bc5738db5a1bf5ceddeaf545405fb241b4184dd4f2587e679a7e9497e5"},
{"name":"raabro","version":"1.4.0","platform":"ruby","checksum":"d4fa9ff5172391edb92b242eed8be802d1934b1464061ae5e70d80962c5da882"},
-{"name":"racc","version":"1.6.0","platform":"java","checksum":"d449a3c279026451b9fd5f34e829dc5f6e0ef6b9b472b7ff89fd3877fe8fe8cf"},
-{"name":"racc","version":"1.6.0","platform":"ruby","checksum":"2dede3b136eeabd0f7b8c9356b958b3d743c00158e2615acab431af141354551"},
+{"name":"racc","version":"1.6.1","platform":"java","checksum":"b3e9cc3892367fdd7eeef0c9210e9ab7f54f106b9202ed00efec892367f5bb27"},
+{"name":"racc","version":"1.6.1","platform":"ruby","checksum":"c8226cc9788c8a43329b75f031dec9ae0423591534bd04e8a117653a442cc85c"},
{"name":"rack","version":"2.2.4","platform":"ruby","checksum":"ea2232b638cbd919129c8c8ad8012ecaccc09f848152a7e705d2139d0137ac2b"},
{"name":"rack-accept","version":"0.4.5","platform":"ruby","checksum":"66247b5449db64ebb93ae2ec4af4764b87d1ae8a7463c7c68893ac13fa8d4da2"},
{"name":"rack-attack","version":"6.6.1","platform":"ruby","checksum":"187e5d248c6a162ed8cafa8241a7b5947d9b9cf122a4870eb1cdd0db861f3a11"},
@@ -504,13 +501,13 @@
{"name":"rspec-support","version":"3.10.3","platform":"ruby","checksum":"65c88f8cbe579461f411097682e6402960eae327eef08e86ef581b8c609e4c5e"},
{"name":"rspec_junit_formatter","version":"0.6.0","platform":"ruby","checksum":"40dde674e6ae4e6cc0ff560da25497677e34fefd2338cc467a8972f602b62b15"},
{"name":"rspec_profiling","version":"0.0.6","platform":"ruby","checksum":"7a45697f79dcec9a174a0e26703465f6bd52ee78e8d798741240bfcef38f6e6e"},
-{"name":"rubocop","version":"1.36.0","platform":"ruby","checksum":"368e47dcab8417419949bbadb11ec41fd94e6b785f8bff4f9cc56a1ddf60ffac"},
-{"name":"rubocop-ast","version":"1.21.0","platform":"ruby","checksum":"8f5d98611343498602de2d41bc583aca71599daad16daeadaeeee60f134c9568"},
+{"name":"rubocop","version":"1.38.0","platform":"ruby","checksum":"64a64a66d746bd417224c0292d08d8bf5affcfe8fbfc3d50a36810ee8c8a1eba"},
+{"name":"rubocop-ast","version":"1.23.0","platform":"ruby","checksum":"fe4bafaa0a6ccf400849fb720f9dd2428b07b00fcdeeec33a8f1146e0c1e38e2"},
{"name":"rubocop-gitlab-security","version":"0.1.1","platform":"ruby","checksum":"96f6ed727847a5876ddfc89ee0399438a1aef7934db773c7efce907e2720006c"},
-{"name":"rubocop-graphql","version":"0.14.6","platform":"ruby","checksum":"b40f2cbac9990ece44eb85eec5c5ae04fca1e197c07c790ac1ca60600b55bdad"},
-{"name":"rubocop-performance","version":"1.14.3","platform":"ruby","checksum":"ee45ae3e40388ff809d9c5e2ef6ef9d59dc86c59c97110f96d5540267f860751"},
-{"name":"rubocop-rails","version":"2.15.2","platform":"ruby","checksum":"1891ab46a6eaf36b841ad27c9c8a22e77a2c3ae85bc334111d3f8075e417643c"},
-{"name":"rubocop-rspec","version":"2.12.1","platform":"ruby","checksum":"9278d22d4525261caf30d591eef3d47910a125e74f75f41ffa470acd208423f9"},
+{"name":"rubocop-graphql","version":"0.18.0","platform":"ruby","checksum":"f1c43999fb9ef0e32b30e2ce2fa0ddb1f3a6215c85baf3fdf9753a0bb96bc998"},
+{"name":"rubocop-performance","version":"1.15.0","platform":"ruby","checksum":"8cd8ff22c567dfacf0292963c05f26c503638d0805a109e8c1f2a2a7955006c0"},
+{"name":"rubocop-rails","version":"2.17.2","platform":"ruby","checksum":"d14008d3a082f05300ff5f6cc21f8217f226ce0c1fc67e01b66bb5131f8f5b14"},
+{"name":"rubocop-rspec","version":"2.15.0","platform":"ruby","checksum":"a476c7671bbeabc9706068be0aa0443a559db6d2b84bfdc582fe65f64b1d455e"},
{"name":"ruby-fogbugz","version":"0.3.0","platform":"ruby","checksum":"5e04cde474648f498a71cf1e1a7ab42c66b953862fbe224f793ec0a7a1d5f657"},
{"name":"ruby-magic","version":"0.5.4","platform":"ruby","checksum":"2c17b185130d10a83791f63a40baa358c4b138af37da3f4dab53690121c421d5"},
{"name":"ruby-progressbar","version":"1.11.0","platform":"ruby","checksum":"cc127db3866dc414ffccbf92928a241e585b3aa2b758a5563e74a6ee0f57d50a"},
@@ -531,7 +528,7 @@
{"name":"sassc","version":"2.4.0","platform":"x64-mingw32","checksum":"8773b917cb52c7e92c94d4bf324c1c0be3e50d9092f9f5ed4c3c6e454b451c5e"},
{"name":"sassc-rails","version":"2.1.0","platform":"ruby","checksum":"764dcc74e06930e3483caf0d595084d11f2b0fefd6539abf487cdddfba6cafa2"},
{"name":"sawyer","version":"0.9.2","platform":"ruby","checksum":"fa3a72d62a4525517b18857ddb78926aab3424de0129be6772a8e2ba240e7aca"},
-{"name":"sd_notify","version":"0.1.0","platform":"ruby","checksum":"22b68623635175dfdb856d43c9480f9ad13eafa604ab410dc91168102287a663"},
+{"name":"sd_notify","version":"0.1.1","platform":"ruby","checksum":"cbc7ac6caa7cedd26b30a72b5eeb6f36050dc0752df263452ea24fb5a4ad3131"},
{"name":"securecompare","version":"1.0.0","platform":"ruby","checksum":"cb0c6599deaaedf6d28f8d88538b06e7198c4826b1b8edb1dbeb44a2162fc62b"},
{"name":"seed-fu","version":"2.3.7","platform":"ruby","checksum":"f19673443e9af799b730e3d4eca6a89b39e5a36825015dffd00d02ea3365cf74"},
{"name":"selenium-webdriver","version":"3.142.7","platform":"ruby","checksum":"dea0993e0e4fdb364f0453144814c0e6099a411d17396807c6cac666d0ddac29"},
@@ -558,12 +555,12 @@
{"name":"sixarm_ruby_unaccent","version":"1.2.0","platform":"ruby","checksum":"0043a6077bdf2c4b03040152676a07f8bf77144f9b007b1960ee5c94d13a4384"},
{"name":"slack-messenger","version":"2.3.4","platform":"ruby","checksum":"49c611d2be5b0f9c250a3a957b9cc09b9c07b81dacb9843642d87b6fa35609c1"},
{"name":"snaky_hash","version":"2.0.0","platform":"ruby","checksum":"fe8b2e39e8ff69320f7812af73ea06401579e29ff1734a7009567391600687de"},
-{"name":"snowplow-tracker","version":"0.6.1","platform":"ruby","checksum":"9cec52fd060619f4974b3dc1f7d9a2776c5e31b668a6ead53145b9780e312314"},
+{"name":"snowplow-tracker","version":"0.8.0","platform":"ruby","checksum":"7ba6f4f1443a829845fd28e63eda72d9d3d247f485310ddcccaebbc52b734a38"},
{"name":"solargraph","version":"0.47.2","platform":"ruby","checksum":"87ca4b799b9155c2c31c15954c483e952fdacd800f52d6709b901dd447bcac6a"},
{"name":"sorted_set","version":"1.0.3","platform":"java","checksum":"996283f2e5c6e838825bcdcee31d6306515ae5f24bcb0ee4ce09dfff32919b8c"},
{"name":"sorted_set","version":"1.0.3","platform":"ruby","checksum":"4f2b8bee6e8c59cbd296228c0f1f81679357177a8b6859dcc2a99e86cce6372f"},
{"name":"spamcheck","version":"1.0.0","platform":"ruby","checksum":"dfeea085184091353e17d729d2f3d714b07cba36aaf64c32dfc35ce9b466fc9c"},
-{"name":"spring","version":"2.1.1","platform":"ruby","checksum":"0d0ccd42eb6ac035b26a6791d10715b3b59c069d1fcd34693d7782257cf85cf4"},
+{"name":"spring","version":"4.1.0","platform":"ruby","checksum":"f17f080fb0df558d663c897a6229ed3d5cc54819ab51876ea6eef49a67f0a3cb"},
{"name":"spring-commands-rspec","version":"1.0.4","platform":"ruby","checksum":"6202e54fa4767452e3641461a83347645af478bf45dddcca9737b43af0dd1a2c"},
{"name":"sprite-factory","version":"1.7.1","platform":"ruby","checksum":"5586524a1aec003241f1abc6852b61433e988aba5ee2b55f906387bf49b01ba2"},
{"name":"sprockets","version":"3.7.2","platform":"ruby","checksum":"5ea1d7facd09203c1aa196afd6178208cd25abdbcc2a9978810a2f0754e152a0"},
@@ -586,7 +583,7 @@
{"name":"telesignenterprise","version":"2.2.2","platform":"ruby","checksum":"f147a03263a8c2fe0a0db1a7a9454a6ee37d9e8abd58eaca305bdd8081f9f1b3"},
{"name":"temple","version":"0.8.2","platform":"ruby","checksum":"c12071214346c606dbd219b4117276d04a9f2c20d65e66a66b2c4ec18efc1f18"},
{"name":"term-ansicolor","version":"1.7.1","platform":"ruby","checksum":"92339ffec77c4bddc786a29385c91601dd52fc68feda23609bba0491229b05f7"},
-{"name":"terminal-table","version":"1.8.0","platform":"ruby","checksum":"13371f069af18e9baa4e44d404a4ada9301899ce0530c237ac1a96c19f652294"},
+{"name":"terminal-table","version":"3.0.2","platform":"ruby","checksum":"f951b6af5f3e00203fb290a669e0a85c5dd5b051b3b023392ccfd67ba5abae91"},
{"name":"terser","version":"1.0.2","platform":"ruby","checksum":"80c2e0bc7e2db4e12e8529658f9e0820e13d685ae67d745bf981f269743bb28e"},
{"name":"test-prof","version":"1.0.7","platform":"ruby","checksum":"7df2ece7acf4f14c52788abdfdefae689b4d1cd84530b2fe7cacadf89cfce0ed"},
{"name":"test_file_finder","version":"0.1.4","platform":"ruby","checksum":"bc36d8339eac4fb9dc36514a7c5f4d389ac2fb6d010716fc715c5c8fbb98eacd"},
@@ -594,7 +591,6 @@
{"name":"thor","version":"1.2.1","platform":"ruby","checksum":"b1752153dc9c6b8d3fcaa665e9e1a00a3e73f28da5e238b81c404502e539d446"},
{"name":"thrift","version":"0.16.0","platform":"ruby","checksum":"d023286ea89e30444c9f1c28dd76107f87d8aaf85fe1742da1d8cd3b5417dcce"},
{"name":"tilt","version":"2.0.11","platform":"ruby","checksum":"7b180fc472cbdeb186c85d31c0f2d1e61a2c0d77e1d9fd0ca28482a9d972d6a0"},
-{"name":"timecop","version":"0.9.1","platform":"ruby","checksum":"374b543f0961dbd487e96d09ac812d4fdfeb603ec705bbff241ba060d0a9f534"},
{"name":"timeliness","version":"0.3.10","platform":"ruby","checksum":"c357233ce19dc53148e8b29dfddde134689f18f52b32928e9dfe12ebcf4a773f"},
{"name":"timfel-krb5-auth","version":"0.8.3","platform":"ruby","checksum":"ab388c9d747fa3cd95baf2cc1c03253e372d8c680adcc543670f4f099854bb80"},
{"name":"tins","version":"1.31.1","platform":"ruby","checksum":"51c4a347c25c630d310cbc2c040ffb84e266c8227f2ade881f1130ee4f9fbecf"},
@@ -631,7 +627,7 @@
{"name":"validate_url","version":"1.0.15","platform":"ruby","checksum":"72fe164c0713d63a9970bd6700bea948babbfbdcec392f2342b6704042f57451"},
{"name":"validates_hostname","version":"1.0.11","platform":"ruby","checksum":"d506bae0342ec14c920eb319e057fc1886c321a59b85b4b6e966ee4b88fab8c3"},
{"name":"version_gem","version":"1.1.0","platform":"ruby","checksum":"6b009518020db57f51ec7b410213fae2bf692baea9f1b51770db97fbc93d9a80"},
-{"name":"version_sorter","version":"2.2.4","platform":"ruby","checksum":"7ad071609edfaa3cf28c42d83b1a03096e43512244ae5a9e2fce1404f7e06d41"},
+{"name":"version_sorter","version":"2.3.0","platform":"ruby","checksum":"2147f2a1a3804fbb8f60d268b7d7c1ec717e6dd727ffe2c165b4e05e82efe1da"},
{"name":"view_component","version":"2.74.1","platform":"ruby","checksum":"0bbd47a9c11455a45043dc01aa604db708654718a4d8755c911425482e8392c0"},
{"name":"vmstat","version":"2.3.0","platform":"ruby","checksum":"ab5446a3e3bd0a9cdb9d9ac69a0bbd119c4f161d945a0846a519dd7018af656d"},
{"name":"warden","version":"1.2.9","platform":"ruby","checksum":"46684f885d35a69dbb883deabf85a222c8e427a957804719e143005df7a1efd0"},
@@ -651,5 +647,5 @@
{"name":"xpath","version":"3.2.0","platform":"ruby","checksum":"6dfda79d91bb3b949b947ecc5919f042ef2f399b904013eb3ef6d20dd3a4082e"},
{"name":"yajl-ruby","version":"1.4.3","platform":"ruby","checksum":"8c974d9c11ae07b0a3b6d26efea8407269b02e4138118fbe3ef0d2ec9724d1d2"},
{"name":"yard","version":"0.9.26","platform":"ruby","checksum":"30594aa05cf737aa725c73444c7be3d54a443d0e258e1503da4eb1a0822cf963"},
-{"name":"zeitwerk","version":"2.6.0","platform":"ruby","checksum":"6cb2ee4645c6e597640d6f2d8cc91a59a6699ab38896a5c3fac3eefeb5c84d76"}
+{"name":"zeitwerk","version":"2.6.6","platform":"ruby","checksum":"bb397b50c31127f8dab372fa9b21da1e7c453c5b57da172ed858136c6283f826"}
]
diff --git a/Gemfile.lock b/Gemfile.lock
index 019ed103508..087d4d8aeec 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -31,6 +31,15 @@ PATH
oj (~> 3.13.16)
PATH
+ remote: vendor/gems/kubeclient
+ specs:
+ kubeclient (4.9.4.pre.gitlab1)
+ http (>= 3.0, < 6.0)
+ jsonpath (~> 1.0)
+ recursive-open-struct (~> 1.1, >= 1.1.1)
+ rest-client (~> 2.0)
+
+PATH
remote: vendor/gems/mail-smtp_pool
specs:
mail-smtp_pool (0.1.0)
@@ -185,19 +194,19 @@ GEM
awesome_print (1.9.2)
awrence (1.1.1)
aws-eventstream (1.2.0)
- aws-partitions (1.658.0)
+ aws-partitions (1.674.0)
aws-sdk-cloudformation (1.41.0)
aws-sdk-core (~> 3, >= 3.99.0)
aws-sigv4 (~> 1.1)
- aws-sdk-core (3.167.0)
+ aws-sdk-core (3.168.4)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1)
- aws-sdk-kms (1.59.0)
+ aws-sdk-kms (1.61.0)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sigv4 (~> 1.1)
- aws-sdk-s3 (1.117.1)
+ aws-sdk-s3 (1.117.2)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
@@ -229,7 +238,7 @@ GEM
rack (>= 0.9.0)
bindata (2.4.11)
binding_ninja (0.2.3)
- bootsnap (1.13.0)
+ bootsnap (1.15.0)
msgpack (~> 1.2)
bootstrap_form (4.2.0)
actionpack (>= 5.0)
@@ -284,16 +293,14 @@ GEM
commonmarker (0.23.6)
concurrent-ruby (1.1.10)
connection_pool (2.3.0)
- contracts (0.11.0)
cork (0.3.0)
colored2 (~> 3.1)
cose (1.0.0)
cbor (~> 0.5.9)
openssl-signature_algorithm (~> 0.4.0)
- countries (3.0.0)
- i18n_data (~> 0.8.0)
+ countries (4.0.1)
+ i18n_data (~> 0.13.0)
sixarm_ruby_unaccent (~> 1.1)
- unicode_utils (~> 1.4)
crack (0.4.3)
safe_yaml (~> 1.0.0)
crass (1.0.6)
@@ -363,11 +370,11 @@ GEM
docile (1.4.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
- doorkeeper (5.5.0.rc2)
+ doorkeeper (5.5.4)
railties (>= 5)
- doorkeeper-openid_connect (1.7.5)
- doorkeeper (>= 5.2, < 5.5)
- json-jwt (>= 1.11.0)
+ doorkeeper-openid_connect (1.8.3)
+ doorkeeper (>= 5.5, < 5.7)
+ json-jwt (>= 1.15.0)
dotenv (2.7.6)
dry-configurable (0.12.0)
concurrent-ruby (~> 1.0)
@@ -484,9 +491,6 @@ GEM
flipper-active_support_cache_store (0.25.0)
activesupport (>= 4.2, < 8)
flipper (~> 0.25.0)
- flowdock (0.7.1)
- httparty (~> 0.7)
- multi_json
fog-aliyun (0.3.3)
fog-core
fog-json
@@ -556,12 +560,12 @@ GEM
rchardet (~> 1.8)
gitaly (15.5.2)
grpc (~> 1.0)
- gitlab (4.16.1)
- httparty (~> 0.14, >= 0.14.0)
- terminal-table (~> 1.5, >= 1.5.1)
+ gitlab (4.19.0)
+ httparty (~> 0.20)
+ terminal-table (>= 1.5.1)
gitlab-chronic (0.10.5)
numerizer (~> 0.2)
- gitlab-dangerfiles (3.6.2)
+ gitlab-dangerfiles (3.6.4)
danger (>= 8.4.5)
danger-gitlab (>= 8.0.0)
rake
@@ -594,13 +598,13 @@ GEM
gitlab-sidekiq-fetcher (0.9.0)
json (>= 2.5)
sidekiq (~> 6.1)
- gitlab-styles (9.0.0)
- rubocop (~> 1.36.0)
+ gitlab-styles (9.1.0)
+ rubocop (~> 1.38.0)
rubocop-gitlab-security (~> 0.1.1)
rubocop-graphql (~> 0.14)
rubocop-performance (~> 1.14)
rubocop-rails (~> 2.15)
- rubocop-rspec (~> 2.12)
+ rubocop-rspec (~> 2.15)
gitlab_chronic_duration (0.10.6.2)
numerizer (~> 0.2)
gitlab_omniauth-ldap (2.2.0)
@@ -651,7 +655,7 @@ GEM
google-apis-core (>= 0.9.1, < 2.a)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
- google-protobuf (3.21.9)
+ google-protobuf (3.21.12)
googleapis-common-protos-types (1.3.0)
google-protobuf (~> 3.14)
googleauth (1.3.0)
@@ -661,8 +665,8 @@ GEM
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
- gpgme (2.0.20)
- mini_portile2 (~> 2.3)
+ gpgme (2.0.22)
+ mini_portile2 (~> 2.7)
grape (1.5.2)
activesupport
builder
@@ -753,24 +757,22 @@ GEM
nokogiri (~> 1.6)
htmlbeautifier (1.4.2)
htmlentities (4.3.4)
- http (4.4.1)
- addressable (~> 2.3)
+ http (5.1.0)
+ addressable (~> 2.8)
http-cookie (~> 1.0)
http-form_data (~> 2.2)
- http-parser (~> 1.2.0)
+ llhttp-ffi (~> 0.4.0)
http-accept (1.7.0)
http-cookie (1.0.5)
domain_name (~> 0.5)
http-form_data (2.3.0)
- http-parser (1.2.3)
- ffi-compiler (>= 1.0, < 2.0)
httparty (0.20.0)
mime-types (~> 3.0)
multi_xml (>= 0.5.2)
httpclient (2.8.3)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
- i18n_data (0.8.0)
+ i18n_data (0.13.1)
icalendar (2.8.0)
ice_cube (~> 0.16)
ice_cube (0.16.4)
@@ -789,7 +791,7 @@ GEM
atlassian-jwt
multipart-post
oauth (~> 0.5, >= 0.5.0)
- jmespath (1.6.1)
+ jmespath (1.6.2)
js_regex (3.8.0)
character_set (~> 1.4)
regexp_parser (~> 2.5)
@@ -828,14 +830,9 @@ GEM
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
- kubeclient (4.9.3)
- http (>= 3.0, < 5.0)
- jsonpath (~> 1.0)
- recursive-open-struct (~> 1.1, >= 1.1.1)
- rest-client (~> 2.0)
launchy (2.5.0)
addressable (~> 2.7)
- lefthook (1.2.0)
+ lefthook (1.2.6)
letter_opener (1.7.0)
launchy (~> 2.2)
letter_opener_web (2.0.0)
@@ -860,6 +857,9 @@ GEM
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
+ llhttp-ffi (0.4.0)
+ ffi-compiler (~> 1.0)
+ rake (~> 13.0)
locale (2.1.3)
lockbox (0.6.2)
lograge (0.11.2)
@@ -867,7 +867,7 @@ GEM
activesupport (>= 4)
railties (>= 4)
request_store (~> 1.0)
- loofah (2.19.0)
+ loofah (2.19.1)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
lookbook (1.2.1)
@@ -938,7 +938,7 @@ GEM
netrc (0.11.0)
nio4r (2.5.8)
no_proxy_fix (0.1.2)
- nokogiri (1.13.9)
+ nokogiri (1.13.10)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
notiffany (0.1.3)
@@ -1028,7 +1028,7 @@ GEM
validate_email
validate_url
webfinger (>= 1.0.1)
- openssl (2.2.1)
+ openssl (2.2.2)
ipaddr
openssl-signature_algorithm (0.4.0)
opentracing (0.5.0)
@@ -1061,7 +1061,7 @@ GEM
expgen (~> 0.1)
rainbow (~> 3.1.1)
parallel (1.22.1)
- parser (3.1.2.1)
+ parser (3.1.3.0)
ast (~> 2.4.1)
parslet (1.8.2)
pastel (0.8.0)
@@ -1086,7 +1086,7 @@ GEM
coderay
parser
unparser
- prometheus-client-mmap (0.16.2)
+ prometheus-client-mmap (0.17.0)
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
@@ -1107,7 +1107,7 @@ GEM
puma (>= 2.7)
pyu-ruby-sasl (0.0.3.3)
raabro (1.4.0)
- racc (1.6.0)
+ racc (1.6.1)
rack (2.2.4)
rack-accept (0.4.5)
rack (>= 0.4)
@@ -1258,31 +1258,31 @@ GEM
pg
rails
sqlite3
- rubocop (1.36.0)
+ rubocop (1.38.0)
json (~> 2.3)
parallel (~> 1.10)
parser (>= 3.1.2.1)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
- rubocop-ast (>= 1.20.1, < 2.0)
+ rubocop-ast (>= 1.23.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0)
- rubocop-ast (1.21.0)
+ rubocop-ast (1.23.0)
parser (>= 3.1.1.0)
rubocop-gitlab-security (0.1.1)
rubocop (>= 0.51)
- rubocop-graphql (0.14.6)
+ rubocop-graphql (0.18.0)
rubocop (>= 0.87, < 2)
- rubocop-performance (1.14.3)
+ rubocop-performance (1.15.0)
rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0)
- rubocop-rails (2.15.2)
+ rubocop-rails (2.17.2)
activesupport (>= 4.2.0)
rack (>= 1.1)
- rubocop (>= 1.7.0, < 2.0)
- rubocop-rspec (2.12.1)
- rubocop (~> 1.31)
+ rubocop (>= 1.33.0, < 2.0)
+ rubocop-rspec (2.15.0)
+ rubocop (~> 1.33)
ruby-fogbugz (0.3.0)
crack (~> 0.4)
multipart-post (~> 2.0)
@@ -1322,7 +1322,7 @@ GEM
sawyer (0.9.2)
addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3)
- sd_notify (0.1.0)
+ sd_notify (0.1.1)
securecompare (1.0.0)
seed-fu (2.3.7)
activerecord (>= 3.1)
@@ -1377,8 +1377,7 @@ GEM
snaky_hash (2.0.0)
hashie
version_gem (~> 1.1)
- snowplow-tracker (0.6.1)
- contracts (~> 0.7, <= 0.11)
+ snowplow-tracker (0.8.0)
solargraph (0.47.2)
backport (~> 1.2)
benchmark
@@ -1399,7 +1398,7 @@ GEM
set (~> 1.0)
spamcheck (1.0.0)
grpc (~> 1.0)
- spring (2.1.1)
+ spring (4.1.0)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
sprite-factory (1.7.1)
@@ -1442,8 +1441,8 @@ GEM
temple (0.8.2)
term-ansicolor (1.7.1)
tins (~> 1.0)
- terminal-table (1.8.0)
- unicode-display_width (~> 1.1, >= 1.1.1)
+ terminal-table (3.0.2)
+ unicode-display_width (>= 1.1.1, < 3)
terser (1.0.2)
execjs (>= 0.3.0, < 3)
test-prof (1.0.7)
@@ -1453,7 +1452,6 @@ GEM
thor (1.2.1)
thrift (0.16.0)
tilt (2.0.11)
- timecop (0.9.1)
timeliness (0.3.10)
timfel-krb5-auth (0.8.3)
tins (1.31.1)
@@ -1526,7 +1524,7 @@ GEM
activerecord (>= 3.0)
activesupport (>= 3.0)
version_gem (1.1.0)
- version_sorter (2.2.4)
+ version_sorter (2.3.0)
view_component (2.74.1)
activesupport (>= 5.0.0, < 8.0)
concurrent-ruby (~> 1.0)
@@ -1569,7 +1567,7 @@ GEM
nokogiri (~> 1.8)
yajl-ruby (1.4.3)
yard (0.9.26)
- zeitwerk (2.6.0)
+ zeitwerk (2.6.6)
PLATFORMS
ruby
@@ -1594,8 +1592,8 @@ DEPENDENCIES
autoprefixer-rails (= 10.2.5.1)
awesome_print
aws-sdk-cloudformation (~> 1)
- aws-sdk-core (~> 3.167.0)
- aws-sdk-s3 (~> 1.117.1)
+ aws-sdk-core (~> 3.168.4)
+ aws-sdk-s3 (~> 1.117.2)
babosa (~> 1.0.4)
base32 (~> 0.3.0)
batch-loader (~> 2.0.1)
@@ -1603,7 +1601,7 @@ DEPENDENCIES
benchmark-ips (~> 2.3.0)
benchmark-memory (~> 0.1)
better_errors (~> 2.9.1)
- bootsnap (~> 1.13.0)
+ bootsnap (~> 1.15.0)
bootstrap_form (~> 4.2.0)
browser (~> 5.3.1)
bullet (~> 7.0.2)
@@ -1616,7 +1614,7 @@ DEPENDENCIES
commonmarker (~> 0.23.6)
concurrent-ruby (~> 1.1)
connection_pool (~> 2.0)
- countries (~> 3.0)
+ countries (~> 4.0.0)
creole (~> 0.5.0)
crystalball (~> 0.7.0)
cvss-suite (~> 3.0.1)
@@ -1633,8 +1631,8 @@ DEPENDENCIES
diff_match_patch (~> 0.1.0)
diffy (~> 3.4)
discordrb-webhooks (~> 3.4)
- doorkeeper (~> 5.5.0.rc2)
- doorkeeper-openid_connect (~> 1.7.5)
+ doorkeeper (~> 5.5)
+ doorkeeper-openid_connect (~> 1.8)
ed25519 (~> 1.3.0)
elasticsearch-api (= 7.13.3)
elasticsearch-model (~> 7.2)
@@ -1650,7 +1648,6 @@ DEPENDENCIES
flipper (~> 0.25.0)
flipper-active_record (~> 0.25.0)
flipper-active_support_cache_store (~> 0.25.0)
- flowdock (~> 0.7)
fog-aliyun (~> 0.3)
fog-aws (~> 3.15)
fog-core (= 2.1.0)
@@ -1665,7 +1662,7 @@ DEPENDENCIES
gettext_i18n_rails_js (~> 1.3)
gitaly (~> 15.5.2)
gitlab-chronic (~> 0.10.5)
- gitlab-dangerfiles (~> 3.6.2)
+ gitlab-dangerfiles (~> 3.6.4)
gitlab-experiment (~> 0.7.1)
gitlab-fog-azure-rm (~> 1.4.0)
gitlab-labkit (~> 0.29.0)
@@ -1675,13 +1672,13 @@ DEPENDENCIES
gitlab-net-dns (~> 0.9.1)
gitlab-omniauth-openid-connect (~> 0.10.0)
gitlab-sidekiq-fetcher (= 0.9.0)
- gitlab-styles (~> 9.0.0)
+ gitlab-styles (~> 9.1.0)
gitlab_chronic_duration (~> 0.10.6.2)
gitlab_omniauth-ldap (~> 2.2.0)
gon (~> 6.4.0)
google-api-client (~> 0.33)
- google-protobuf (~> 3.21, >= 3.21.9)
- gpgme (~> 2.0.19)
+ google-protobuf (~> 3.21, >= 3.21.12)
+ gpgme (~> 2.0.22)
grape (~> 1.5.2)
grape-entity (~> 0.10.0)
grape-path-helpers (~> 1.7.1)
@@ -1718,14 +1715,15 @@ DEPENDENCIES
kas-grpc (~> 0.0.2)
knapsack (~> 1.21.1)
kramdown (~> 2.3.1)
- kubeclient (~> 4.9.3)
- lefthook (~> 1.2.0)
+ kubeclient (~> 4.9.3)!
+ lefthook (~> 1.2.6)
letter_opener_web (~> 2.0.0)
license_finder (~> 7.0)
licensee (~> 9.15)
+ listen (~> 3.7)
lockbox (~> 0.6.2)
lograge (~> 0.5)
- loofah (~> 2.19.0)
+ loofah (~> 2.19.1)
lookbook (~> 1.2, >= 1.2.1)
lru_redux
mail (= 2.7.1)
@@ -1738,7 +1736,7 @@ DEPENDENCIES
multi_json (~> 1.14.1)
net-ldap (~> 0.16.3)
net-ntp
- nokogiri (~> 1.13.9)
+ nokogiri (~> 1.13.10)
oauth2 (~> 2.0)
octokit (~> 4.15)
ohai (~> 16.10)
@@ -1763,7 +1761,7 @@ DEPENDENCIES
omniauth-shibboleth (~> 1.3.0)
omniauth-twitter (~> 1.4)
omniauth_crowd (~> 2.4.0)!
- openssl (= 2.2.1)
+ openssl (= 2.2.2)
org-ruby (~> 0.9.12)
pact (~> 1.63)
parallel (~> 1.19)
@@ -1773,7 +1771,7 @@ DEPENDENCIES
pg_query (~> 2.2)
png_quantizator (~> 0.2.1)
premailer-rails (~> 1.10.3)
- prometheus-client-mmap (~> 0.16)
+ prometheus-client-mmap (~> 0.17)
pry-byebug
pry-rails (~> 0.3.9)
pry-shell (~> 0.5.1)
@@ -1835,10 +1833,10 @@ DEPENDENCIES
simplecov-cobertura (~> 1.3.1)
simplecov-lcov (~> 0.8.0)
slack-messenger (~> 2.3.4)
- snowplow-tracker (~> 0.6.1)
+ snowplow-tracker (~> 0.8.0)
solargraph (~> 0.47.2)
spamcheck (~> 1.0.0)
- spring (~> 2.1.0)
+ spring (~> 4.1.0)
spring-commands-rspec (~> 1.0.4)
sprite-factory (~> 1.7)
sprockets (~> 3.7.0)
@@ -1852,7 +1850,6 @@ DEPENDENCIES
test-prof (~> 1.0.7)
test_file_finder (~> 0.1.3)
thrift (>= 0.16.0)
- timecop (~> 0.9.1)
timfel-krb5-auth (~> 0.8)
toml-rb (~> 2.2.0)
truncato (~> 0.7.12)
@@ -1863,7 +1860,7 @@ DEPENDENCIES
unleash (~> 3.2.2)
valid_email (~> 0.1)
validates_hostname (~> 1.0.11)
- version_sorter (~> 2.2.4)
+ version_sorter (~> 2.3)
view_component (~> 2.74.1)
vmstat (~> 2.3.0)
warning (~> 1.3.0)
@@ -1874,4 +1871,4 @@ DEPENDENCIES
yajl-ruby (~> 1.4.3)
BUNDLED WITH
- 2.3.25
+ 2.3.26
diff --git a/app/assets/images/web-ide-promo-popover.svg b/app/assets/images/web-ide-promo-popover.svg
new file mode 100644
index 00000000000..3ced89860da
--- /dev/null
+++ b/app/assets/images/web-ide-promo-popover.svg
@@ -0,0 +1,101 @@
+<svg width="280" height="140" viewBox="0 0 280 140" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_187_122567)">
+<g clip-path="url(#clip1_187_122567)">
+<circle cx="189.5" cy="-42.5" r="131.5" fill="url(#paint0_radial_187_122567)"/>
+<circle cx="-41.5" cy="-97.5" r="198.5" fill="url(#paint1_radial_187_122567)"/>
+<circle cx="309.5" cy="-7.5" r="121.5" fill="url(#paint2_radial_187_122567)"/>
+<g filter="url(#filter0_b_187_122567)">
+<path d="M0 4C0 1.79086 1.79086 0 4 0H276C278.209 0 280 1.79086 280 4V130H0V4Z" fill="white" fill-opacity="0.01"/>
+</g>
+</g>
+<path d="M183.948 47.9647H100.897V100.482H183.948V47.9647Z" fill="white"/>
+<path d="M100.64 47.9647H184.06V98.9817C184.06 104.1 179.908 108.256 174.793 108.256H100.638V47.9647H100.64Z" fill="white"/>
+<path d="M184.314 34.7452H100.64V47.9676H184.314V34.7452Z" fill="#AEA5D6"/>
+<path d="M109.594 43.2574C110.644 43.2574 111.495 42.4056 111.495 41.3549C111.495 40.3043 110.644 39.4525 109.594 39.4525C108.544 39.4525 107.693 40.3043 107.693 41.3549C107.693 42.4056 108.544 43.2574 109.594 43.2574Z" fill="#10B1B1"/>
+<path d="M116.482 43.2574C117.532 43.2574 118.383 42.4056 118.383 41.3549C118.383 40.3043 117.532 39.4525 116.482 39.4525C115.432 39.4525 114.581 40.3043 114.581 41.3549C114.581 42.4056 115.432 43.2574 116.482 43.2574Z" fill="#A888F4"/>
+<path d="M123.368 43.2574C124.418 43.2574 125.269 42.4056 125.269 41.3549C125.269 40.3043 124.418 39.4525 123.368 39.4525C122.318 39.4525 121.467 40.3043 121.467 41.3549C121.467 42.4056 122.318 43.2574 123.368 43.2574Z" fill="#FF9D73"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M100.556 34.4038H165.377V35.0858H101.238V61.7159H100.556V34.4038Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M101.238 66.3383V83.4486H100.556V66.3383H101.238Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M183.721 99.0231V89.5341H184.403V99.0231C184.403 104.311 180.118 108.599 174.834 108.599H120.244V107.917H174.834C179.741 107.917 183.721 103.935 183.721 99.0231Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M183.721 83.1296V62.2422H184.403V83.1296H183.721Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M109.596 39.7936C108.734 39.7936 108.036 40.4924 108.036 41.355C108.036 42.2176 108.734 42.9164 109.596 42.9164C110.457 42.9164 111.155 42.2176 111.155 41.355C111.155 40.4924 110.457 39.7936 109.596 39.7936ZM107.354 41.355C107.354 40.1162 108.357 39.1116 109.596 39.1116C110.834 39.1116 111.837 40.1162 111.837 41.355C111.837 42.5938 110.834 43.5985 109.596 43.5985C108.357 43.5985 107.354 42.5938 107.354 41.355Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M116.48 39.7936C115.619 39.7936 114.92 40.4924 114.92 41.355C114.92 42.2176 115.619 42.9164 116.48 42.9164C117.342 42.9164 118.04 42.2176 118.04 41.355C118.04 40.4924 117.342 39.7936 116.48 39.7936ZM114.238 41.355C114.238 40.1162 115.242 39.1116 116.48 39.1116C117.719 39.1116 118.722 40.1162 118.722 41.355C118.722 42.5938 117.719 43.5985 116.48 43.5985C115.242 43.5985 114.238 42.5938 114.238 41.355Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M123.367 39.7936C122.506 39.7936 121.808 40.4924 121.808 41.355C121.808 42.2176 122.506 42.9164 123.367 42.9164C124.229 42.9164 124.927 42.2176 124.927 41.355C124.927 40.4924 124.229 39.7936 123.367 39.7936ZM121.125 41.355C121.125 40.1162 122.129 39.1116 123.367 39.1116C124.606 39.1116 125.609 40.1162 125.609 41.355C125.609 42.5938 124.606 43.5985 123.367 43.5985C122.129 43.5985 121.125 42.5938 121.125 41.355Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M160.466 48.3058H100.897V47.6238H160.466V48.3058Z" fill="#171321"/>
+<path d="M173.511 80.7124H152.485V83.4598H173.511V80.7124Z" fill="#D0C5E2"/>
+<path d="M149.08 80.7009H111.703V83.4484H149.08V80.7009Z" fill="#E7E4F2"/>
+<path d="M111.702 68.5293H132.728V65.7819H111.702V68.5293Z" fill="#E7E4F2"/>
+<path d="M136.131 68.5336H173.508V65.7861H136.131V68.5336Z" fill="#AEA5D6"/>
+<path d="M111.703 75.9889H120.838V73.2415H111.703V75.9889Z" fill="#AEA5D6"/>
+<path d="M155.891 75.9978H173.512V73.2504H155.891V75.9978Z" fill="#E7E4F2"/>
+<path d="M124.244 75.9978H152.485V73.2504H124.244V75.9978Z" fill="#D0C5E2"/>
+<path d="M141.099 58.3217H124.332V61.0691H141.099V58.3217Z" fill="#D0C5E2"/>
+<path d="M173.512 58.3217H144.31V61.0691H173.512V58.3217Z" fill="#E7E4F2"/>
+<path d="M120.926 58.3198H111.703V61.0673H120.926V58.3198Z" fill="#AEA5D6"/>
+<path d="M144.115 90.9242H160.882V88.1768H144.115V90.9242Z" fill="#AEA5D6"/>
+<path d="M111.703 90.908H140.905V88.1605H111.703V90.908Z" fill="#D0C5E2"/>
+<path d="M164.288 90.9242H173.512V88.1768H164.288V90.9242Z" fill="#D0C5E2"/>
+<path d="M173.508 95.6178H158.224V98.3652H173.508V95.6178Z" fill="#D0C5E2"/>
+<path d="M154.82 95.6199H111.703V98.3673H154.82V95.6199Z" fill="#E7E4F2"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M167.801 34.8965L168.463 35.0598L162.398 59.6461L189.091 57.5112L189.145 58.1911L161.509 60.4014L167.801 34.8965Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M134.795 81.6335L109.428 116.595L108.876 116.195L133.312 82.5167L92.5055 87.8873L92.4165 87.2111L134.795 81.6335Z" fill="#171321"/>
+<path d="M187.019 57.9366C197.646 57.9366 206.262 49.3147 206.262 38.6791C206.262 28.0435 197.646 19.4216 187.019 19.4216C176.392 19.4216 167.777 28.0435 167.777 38.6791C167.777 49.3147 176.392 57.9366 187.019 57.9366Z" fill="white"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M187.019 19.6948C176.543 19.6948 168.05 28.1943 168.05 38.6794C168.05 49.1646 176.543 57.6641 187.019 57.6641C197.495 57.6641 205.989 49.1646 205.989 38.6794C205.989 28.1943 197.495 19.6948 187.019 19.6948ZM167.504 38.6794C167.504 27.8934 176.241 19.1492 187.019 19.1492C197.797 19.1492 206.534 27.8934 206.534 38.6794C206.534 49.4655 197.797 58.2097 187.019 58.2097C176.241 58.2097 167.504 49.4655 167.504 38.6794Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M212.255 38.6791C212.255 24.7294 200.956 13.4218 187.018 13.4218V12.677C201.368 12.677 213 24.3186 213 38.6791H212.255Z" fill="#AEA5D6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M67.0944 108.321C67.0944 122.27 78.3936 133.578 92.3316 133.578V134.323C77.9817 134.323 66.3496 122.681 66.3496 108.321H67.0944Z" fill="#AEA5D6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M181.794 28.4035C183.212 28.0221 184.673 28.8643 185.054 30.2837L184.395 30.4605C184.112 29.4049 183.026 28.7787 181.971 29.0622L181.794 28.4035Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M192.066 29.0622C191.011 28.7787 189.925 29.4049 189.642 30.4605L188.983 30.2837C189.364 28.8643 190.824 28.0221 192.243 28.4035L192.066 29.0622Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M195.892 33.5398V36.1862L192.04 37.4786L191.823 36.832L195.21 35.6956V33.5398H195.892Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M195.213 44.8355L192.045 43.7774L192.262 43.1305L195.895 44.3442V46.991H195.213V44.8355Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M178.827 35.6957V33.5398H178.145V36.1861L181.995 37.4786L182.212 36.832L178.827 35.6957Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M181.776 43.1305L181.992 43.7773L178.827 44.8354V46.991H178.145V44.3443L181.776 43.1305Z" fill="#171321"/>
+<path d="M182.675 34.3942L181.129 37.5941C179.03 41.9415 182.192 46.9908 187.019 46.9908C191.845 46.9908 195.008 41.9415 192.908 37.5941L191.363 34.3942" fill="#A888F4"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M186.678 46.9908V34.3942H187.36V46.9908H186.678Z" fill="#171321"/>
+<path d="M191.25 34.3939H182.788V33.2534C182.788 28.2918 191.25 28.2987 191.25 33.2534V34.3939Z" fill="#7759C1"/>
+<path d="M92.4612 126.064C103.088 126.064 111.704 117.442 111.704 106.806C111.704 96.1706 103.088 87.5487 92.4612 87.5487C81.8339 87.5487 73.2188 96.1706 73.2188 106.806C73.2188 117.442 81.8339 126.064 92.4612 126.064Z" fill="white"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M92.4604 87.89C82.0217 87.89 73.559 96.359 73.559 106.806C73.559 117.254 82.0217 125.723 92.4604 125.723C102.899 125.723 111.362 117.254 111.362 106.806C111.362 96.359 102.899 87.89 92.4604 87.89ZM72.877 106.806C72.877 95.9828 81.6445 87.208 92.4604 87.208C103.276 87.208 112.044 95.9828 112.044 106.806C112.044 117.63 103.276 126.405 92.4604 126.405C81.6445 126.405 72.877 117.63 72.877 106.806Z" fill="#171321"/>
+<path d="M98.2885 105.28H95.9653V101.385C95.9653 99.3302 94.2951 97.6587 92.2419 97.6587C90.1887 97.6587 88.5184 99.3302 88.5184 101.385V105.28H86.1953V101.385C86.1953 98.0489 88.9083 95.3337 92.2419 95.3337C95.5754 95.3337 98.2885 98.0489 98.2885 101.385V105.28Z" fill="#7759C1"/>
+<path d="M100.894 104.186H83.3677V116.836H100.894V104.186Z" fill="#A888F4"/>
+<path d="M92.1343 111.015C92.8235 111.015 93.3823 110.456 93.3823 109.766C93.3823 109.076 92.8235 108.517 92.1343 108.517C91.445 108.517 90.8862 109.076 90.8862 109.766C90.8862 110.456 91.445 111.015 92.1343 111.015Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M91.792 112.209V110.722H92.474V112.209H91.792Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M87.2401 53.2567V60.9103C87.2401 61.9966 88.1212 62.878 89.2061 62.878H121.061C122.523 62.878 123.709 64.0653 123.709 65.5278V67.1601H123.027V65.5278C123.027 64.4415 122.146 63.5601 121.061 63.5601H89.2061C87.7441 63.5601 86.5581 62.3728 86.5581 60.9103V53.2567H87.2401Z" fill="#171321"/>
+<path d="M123.368 67.735C123.685 67.735 123.942 67.4776 123.942 67.1601C123.942 66.8426 123.685 66.5852 123.368 66.5852C123.051 66.5852 122.793 66.8426 122.793 67.1601C122.793 67.4776 123.051 67.735 123.368 67.735Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M123.368 66.9262C123.239 66.9262 123.134 67.0306 123.134 67.16C123.134 67.2895 123.239 67.3939 123.368 67.3939C123.496 67.3939 123.601 67.2895 123.601 67.16C123.601 67.0306 123.496 66.9262 123.368 66.9262ZM122.452 67.16C122.452 66.6545 122.862 66.2441 123.368 66.2441C123.873 66.2441 124.283 66.6545 124.283 67.16C124.283 67.6656 123.873 68.0759 123.368 68.0759C122.862 68.0759 122.452 67.6656 122.452 67.16Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M167.208 82.5551V84.88C167.208 85.5854 167.778 86.1551 168.482 86.1551H193.305V86.8371H168.482C167.4 86.8371 166.526 85.9616 166.526 84.88V82.5551H167.208Z" fill="#171321"/>
+<path d="M166.867 83.13C167.184 83.13 167.441 82.8726 167.441 82.5551C167.441 82.2376 167.184 81.9802 166.867 81.9802C166.55 81.9802 166.292 82.2376 166.292 82.5551C166.292 82.8726 166.55 83.13 166.867 83.13Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M166.866 82.3209C166.737 82.3209 166.633 82.4254 166.633 82.5548C166.633 82.6842 166.737 82.7887 166.866 82.7887C166.995 82.7887 167.1 82.6842 167.1 82.5548C167.1 82.4255 166.995 82.3209 166.866 82.3209ZM165.951 82.5548C165.951 82.0492 166.36 81.6389 166.866 81.6389C167.372 81.6389 167.782 82.0492 167.782 82.5548C167.782 83.0605 167.372 83.4707 166.866 83.4707C166.36 83.4707 165.951 83.0604 165.951 82.5548Z" fill="#171321"/>
+<path d="M86.9023 37.1553C82.8536 41.5743 77.6099 40.6 77.6099 40.6V48.5768C77.6099 49.962 77.8867 51.3404 78.4796 52.5917C80.8973 57.6918 86.9023 59.5873 86.9023 59.5873C86.9023 59.5873 92.9051 57.6918 95.3251 52.5917C95.918 51.3404 96.1948 49.962 96.1948 48.5768V40.6C96.1948 40.6 90.9511 41.5743 86.9023 37.1553V37.1553Z" fill="#10B1B1"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M90.6411 45.6638L85.4674 50.8415L83.1592 48.5289L83.6419 48.0471L85.4677 49.8764L90.1586 45.1818L90.6411 45.6638Z" fill="#171321"/>
+<path d="M197.955 76.3168C193.902 80.7427 188.651 79.7684 188.651 79.7684V87.7567C188.651 89.1443 188.928 90.5249 189.521 91.7786C191.943 96.8856 197.955 98.7834 197.955 98.7834C197.955 98.7834 203.967 96.8856 206.39 91.7786C206.985 90.5249 207.259 89.1443 207.259 87.7567V79.7684C207.259 79.7684 202.009 80.7427 197.955 76.3168Z" fill="#FC6D26"/>
+<path d="M197.19 83.1044L197.566 88.4584H198.343L198.719 83.1044H197.19Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M196.806 82.7468H199.102L198.676 88.8156H197.232L196.806 82.7468ZM197.572 83.4616L197.898 88.1009H198.009L198.335 83.4616H197.572Z" fill="#171321"/>
+<path d="M198.343 90.2083H197.566V91.1248H198.343V90.2083Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M197.208 89.851H198.7V91.4823H197.208V89.851ZM197.922 90.5657V90.7675H197.985V90.5657H197.922Z" fill="#171321"/>
+</g>
+<defs>
+<filter id="filter0_b_187_122567" x="-50" y="-50" width="380" height="230" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feGaussianBlur in="BackgroundImageFix" stdDeviation="25"/>
+<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_187_122567"/>
+<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_187_122567" result="shape"/>
+</filter>
+<radialGradient id="paint0_radial_187_122567" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(189.5 -42.5) rotate(89.5818) scale(125.986)">
+<stop stop-color="#7759C2"/>
+<stop offset="1" stop-color="#7759C2" stop-opacity="0"/>
+</radialGradient>
+<radialGradient id="paint1_radial_187_122567" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(-41.5 -97.5) rotate(89.5818) scale(190.176)">
+<stop stop-color="#D64028"/>
+<stop offset="1" stop-color="#D64028" stop-opacity="0"/>
+</radialGradient>
+<radialGradient id="paint2_radial_187_122567" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(309.5 -7.5) rotate(89.5818) scale(116.405)">
+<stop stop-color="#EF76F1"/>
+<stop offset="1" stop-color="#EF76F1" stop-opacity="0"/>
+</radialGradient>
+<clipPath id="clip0_187_122567">
+<rect width="280" height="140" fill="white"/>
+</clipPath>
+<clipPath id="clip1_187_122567">
+<path d="M0 4C0 1.79086 1.79086 0 4 0H276C278.209 0 280 1.79086 280 4V140H0V4Z" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/app/assets/javascripts/admin/background_migrations/components/database_listbox.vue b/app/assets/javascripts/admin/background_migrations/components/database_listbox.vue
index 80c216024a0..8e814cd55ef 100644
--- a/app/assets/javascripts/admin/background_migrations/components/database_listbox.vue
+++ b/app/assets/javascripts/admin/background_migrations/components/database_listbox.vue
@@ -1,5 +1,5 @@
<script>
-import { GlListbox } from '@gitlab/ui';
+import { GlCollapsibleListbox } from '@gitlab/ui';
import { s__ } from '~/locale';
import { setUrlParams, visitUrl } from '~/lib/utils/url_utility';
@@ -9,7 +9,7 @@ export default {
database: s__('BackgroundMigrations|Database'),
},
components: {
- GlListbox,
+ GlCollapsibleListbox,
},
props: {
databases: {
@@ -39,7 +39,7 @@ export default {
<label id="label" class="gl-font-weight-bold gl-mr-4 gl-mb-0">{{
$options.i18n.database
}}</label>
- <gl-listbox
+ <gl-collapsible-listbox
v-model="selected"
:items="databases"
right
diff --git a/app/assets/javascripts/admin/broadcast_messages/components/base.vue b/app/assets/javascripts/admin/broadcast_messages/components/base.vue
index b7bafe46327..f869d21d55f 100644
--- a/app/assets/javascripts/admin/broadcast_messages/components/base.vue
+++ b/app/assets/javascripts/admin/broadcast_messages/components/base.vue
@@ -5,14 +5,18 @@ import { buildUrlWithCurrentLocation } from '~/lib/utils/common_utils';
import { createAlert, VARIANT_DANGER } from '~/flash';
import { s__ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
+import { NEW_BROADCAST_MESSAGE } from '../constants';
+import MessageForm from './message_form.vue';
import MessagesTable from './messages_table.vue';
const PER_PAGE = 20;
export default {
name: 'BroadcastMessagesBase',
+ NEW_BROADCAST_MESSAGE,
components: {
GlPagination,
+ MessageForm,
MessagesTable,
},
@@ -97,6 +101,7 @@ export default {
<template>
<div>
+ <message-form :broadcast-message="$options.NEW_BROADCAST_MESSAGE" />
<messages-table
v-if="hasVisibleMessages"
:messages="visibleMessages"
diff --git a/app/assets/javascripts/admin/broadcast_messages/components/datetime_picker.vue b/app/assets/javascripts/admin/broadcast_messages/components/datetime_picker.vue
new file mode 100644
index 00000000000..07814ef2511
--- /dev/null
+++ b/app/assets/javascripts/admin/broadcast_messages/components/datetime_picker.vue
@@ -0,0 +1,47 @@
+<script>
+import { GlDatepicker, GlFormInput } from '@gitlab/ui';
+import { dateToTimeInputValue, timeToHoursMinutes } from '~/lib/utils/datetime/date_format_utility';
+
+export default {
+ name: 'DatetimePicker',
+ components: {
+ GlDatepicker,
+ GlFormInput,
+ },
+ props: {
+ value: {
+ type: Date,
+ required: true,
+ },
+ },
+ computed: {
+ date: {
+ get() {
+ return this.value;
+ },
+ set(val) {
+ const dup = new Date(this.value.getTime());
+ dup.setFullYear(val.getFullYear(), val.getMonth(), val.getDate());
+ this.$emit('input', dup);
+ },
+ },
+ time: {
+ get() {
+ return dateToTimeInputValue(this.value);
+ },
+ set(val) {
+ const dup = new Date(this.value.getTime());
+ const { hours, minutes } = timeToHoursMinutes(val);
+ dup.setHours(hours, minutes);
+ this.$emit('input', dup);
+ },
+ },
+ },
+};
+</script>
+<template>
+ <div class="gl-display-flex gl-gap-3 gl-align-items-center">
+ <gl-datepicker v-model="date" />
+ <gl-form-input v-model="time" size="sm" type="time" data-testid="time-picker" />
+ </div>
+</template>
diff --git a/app/assets/javascripts/admin/broadcast_messages/components/message_form.vue b/app/assets/javascripts/admin/broadcast_messages/components/message_form.vue
new file mode 100644
index 00000000000..36796708e78
--- /dev/null
+++ b/app/assets/javascripts/admin/broadcast_messages/components/message_form.vue
@@ -0,0 +1,225 @@
+<script>
+import {
+ GlButton,
+ GlBroadcastMessage,
+ GlForm,
+ GlFormCheckbox,
+ GlFormCheckboxGroup,
+ GlFormInput,
+ GlFormSelect,
+ GlFormText,
+ GlFormTextarea,
+} from '@gitlab/ui';
+import axios from '~/lib/utils/axios_utils';
+import { s__ } from '~/locale';
+import { createAlert, VARIANT_DANGER } from '~/flash';
+import { redirectTo } from '~/lib/utils/url_utility';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import { BROADCAST_MESSAGES_PATH, THEMES, TYPES, TYPE_BANNER } from '../constants';
+import MessageFormGroup from './message_form_group.vue';
+import DatetimePicker from './datetime_picker.vue';
+
+const FORM_HEADERS = { headers: { 'Content-Type': 'application/json; charset=utf-8' } };
+
+export default {
+ name: 'MessageForm',
+ components: {
+ DatetimePicker,
+ GlButton,
+ GlBroadcastMessage,
+ GlForm,
+ GlFormCheckbox,
+ GlFormCheckboxGroup,
+ GlFormInput,
+ GlFormSelect,
+ GlFormText,
+ GlFormTextarea,
+ MessageFormGroup,
+ },
+ mixins: [glFeatureFlagsMixin()],
+ inject: ['targetAccessLevelOptions'],
+ i18n: {
+ message: s__('BroadcastMessages|Message'),
+ messagePlaceholder: s__('BroadcastMessages|Your message here'),
+ type: s__('BroadcastMessages|Type'),
+ theme: s__('BroadcastMessages|Theme'),
+ dismissable: s__('BroadcastMessages|Dismissable'),
+ dismissableDescription: s__('BroadcastMessages|Allow users to dismiss the broadcast message'),
+ targetRoles: s__('BroadcastMessages|Target roles'),
+ targetRolesDescription: s__(
+ 'BroadcastMessages|The broadcast message displays only to users in projects and groups who have these roles.',
+ ),
+ targetPath: s__('BroadcastMessages|Target Path'),
+ targetPathDescription: s__('BroadcastMessages|Paths can contain wildcards, like */welcome'),
+ startsAt: s__('BroadcastMessages|Starts at'),
+ endsAt: s__('BroadcastMessages|Ends at'),
+ add: s__('BroadcastMessages|Add broadcast message'),
+ addError: s__('BroadcastMessages|There was an error adding broadcast message.'),
+ update: s__('BroadcastMessages|Update broadcast message'),
+ updateError: s__('BroadcastMessages|There was an error updating broadcast message.'),
+ },
+ messageThemes: THEMES,
+ messageTypes: TYPES,
+ props: {
+ broadcastMessage: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ loading: false,
+ message: this.broadcastMessage.message,
+ type: this.broadcastMessage.broadcastType,
+ theme: this.broadcastMessage.theme,
+ dismissable: this.broadcastMessage.dismissable || false,
+ targetPath: this.broadcastMessage.targetPath,
+ targetAccessLevels: this.broadcastMessage.targetAccessLevels,
+ targetAccessLevelOptions: this.targetAccessLevelOptions.map(([text, value]) => ({
+ text,
+ value,
+ })),
+ startsAt: new Date(this.broadcastMessage.startsAt.getTime()),
+ endsAt: new Date(this.broadcastMessage.endsAt.getTime()),
+ };
+ },
+ computed: {
+ isBanner() {
+ return this.type === TYPE_BANNER;
+ },
+ messageBlank() {
+ return this.message.trim() === '';
+ },
+ messagePreview() {
+ return this.messageBlank ? this.$options.i18n.messagePlaceholder : this.message;
+ },
+ isAddForm() {
+ return !this.broadcastMessage.id;
+ },
+ formPath() {
+ return this.isAddForm
+ ? BROADCAST_MESSAGES_PATH
+ : `${BROADCAST_MESSAGES_PATH}/${this.broadcastMessage.id}`;
+ },
+ formPayload() {
+ return JSON.stringify({
+ message: this.message,
+ broadcast_type: this.type,
+ theme: this.theme,
+ dismissable: this.dismissable,
+ target_path: this.targetPath,
+ target_access_levels: this.targetAccessLevels,
+ starts_at: this.startsAt.toISOString(),
+ ends_at: this.endsAt.toISOString(),
+ });
+ },
+ },
+ methods: {
+ async onSubmit() {
+ this.loading = true;
+
+ const success = await this.submitForm();
+ if (success) {
+ redirectTo(BROADCAST_MESSAGES_PATH);
+ } else {
+ this.loading = false;
+ }
+ },
+
+ async submitForm() {
+ const requestMethod = this.isAddForm ? 'post' : 'patch';
+
+ try {
+ await axios[requestMethod](this.formPath, this.formPayload, FORM_HEADERS);
+ } catch (e) {
+ const message = this.isAddForm
+ ? this.$options.i18n.addError
+ : this.$options.i18n.updateError;
+ createAlert({ message, variant: VARIANT_DANGER });
+ return false;
+ }
+ return true;
+ },
+ },
+};
+</script>
+<template>
+ <gl-form @submit.prevent="onSubmit">
+ <gl-broadcast-message class="gl-my-6" :type="type" :theme="theme" :dismissible="dismissable">
+ {{ messagePreview }}
+ </gl-broadcast-message>
+
+ <message-form-group :label="$options.i18n.message" label-for="message-textarea">
+ <gl-form-textarea
+ id="message-textarea"
+ v-model="message"
+ size="sm"
+ :placeholder="$options.i18n.messagePlaceholder"
+ />
+ </message-form-group>
+
+ <message-form-group :label="$options.i18n.type" label-for="type-select">
+ <gl-form-select id="type-select" v-model="type" :options="$options.messageTypes" />
+ </message-form-group>
+
+ <template v-if="isBanner">
+ <message-form-group :label="$options.i18n.theme" label-for="theme-select">
+ <gl-form-select
+ id="theme-select"
+ v-model="theme"
+ :options="$options.messageThemes"
+ data-testid="theme-select"
+ />
+ </message-form-group>
+
+ <message-form-group :label="$options.i18n.dismissable" label-for="dismissable-checkbox">
+ <gl-form-checkbox
+ id="dismissable-checkbox"
+ v-model="dismissable"
+ class="gl-mt-3"
+ data-testid="dismissable-checkbox"
+ >
+ <span>{{ $options.i18n.dismissableDescription }}</span>
+ </gl-form-checkbox>
+ </message-form-group>
+ </template>
+
+ <message-form-group
+ v-if="glFeatures.roleTargetedBroadcastMessages"
+ :label="$options.i18n.targetRoles"
+ data-testid="target-roles-checkboxes"
+ >
+ <gl-form-checkbox-group v-model="targetAccessLevels" :options="targetAccessLevelOptions" />
+ <gl-form-text>
+ {{ $options.i18n.targetRolesDescription }}
+ </gl-form-text>
+ </message-form-group>
+
+ <message-form-group :label="$options.i18n.targetPath" label-for="target-path-input">
+ <gl-form-input id="target-path-input" v-model="targetPath" />
+ <gl-form-text>
+ {{ $options.i18n.targetPathDescription }}
+ </gl-form-text>
+ </message-form-group>
+
+ <message-form-group :label="$options.i18n.startsAt">
+ <datetime-picker v-model="startsAt" />
+ </message-form-group>
+
+ <message-form-group :label="$options.i18n.endsAt">
+ <datetime-picker v-model="endsAt" />
+ </message-form-group>
+
+ <div class="form-actions gl-mb-3">
+ <gl-button
+ type="submit"
+ variant="confirm"
+ :loading="loading"
+ :disabled="messageBlank"
+ data-testid="submit-button"
+ >
+ {{ isAddForm ? $options.i18n.add : $options.i18n.update }}
+ </gl-button>
+ </div>
+ </gl-form>
+</template>
diff --git a/app/assets/javascripts/admin/broadcast_messages/components/message_form_group.vue b/app/assets/javascripts/admin/broadcast_messages/components/message_form_group.vue
new file mode 100644
index 00000000000..eec51c0c28b
--- /dev/null
+++ b/app/assets/javascripts/admin/broadcast_messages/components/message_form_group.vue
@@ -0,0 +1,34 @@
+<script>
+import { GlFormGroup } from '@gitlab/ui';
+
+export default {
+ name: 'MessageFormGroup',
+ components: {
+ GlFormGroup,
+ },
+ props: {
+ label: {
+ type: String,
+ required: true,
+ },
+ labelFor: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <gl-form-group
+ :label="label"
+ :label-for="labelFor"
+ label-cols-sm="2"
+ label-class="gl-mt-3"
+ label-align-sm="right"
+ >
+ <slot></slot>
+ </gl-form-group>
+ </div>
+</template>
diff --git a/app/assets/javascripts/admin/broadcast_messages/components/messages_table.vue b/app/assets/javascripts/admin/broadcast_messages/components/messages_table.vue
index 1408312d3e4..a523dd3b391 100644
--- a/app/assets/javascripts/admin/broadcast_messages/components/messages_table.vue
+++ b/app/assets/javascripts/admin/broadcast_messages/components/messages_table.vue
@@ -1,6 +1,8 @@
<script>
-import { GlButton, GlTableLite, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlButton, GlTableLite } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { __ } from '~/locale';
+import { formatDate } from '~/lib/utils/datetime/date_format_utility';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
const DEFAULT_TD_CLASSES = 'gl-vertical-align-middle!';
@@ -12,7 +14,7 @@ export default {
GlTableLite,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [glFeatureFlagsMixin()],
i18n: {
@@ -77,6 +79,11 @@ export default {
safeHtmlConfig: {
ADD_TAGS: ['use'],
},
+ methods: {
+ formatDate(dateString) {
+ return formatDate(new Date(dateString));
+ },
+ },
};
</script>
<template>
@@ -90,6 +97,14 @@ export default {
<div v-safe-html:[$options.safeHtmlConfig]="preview"></div>
</template>
+ <template #cell(starts_at)="{ item: { starts_at } }">
+ {{ formatDate(starts_at) }}
+ </template>
+
+ <template #cell(ends_at)="{ item: { ends_at } }">
+ {{ formatDate(ends_at) }}
+ </template>
+
<template #cell(buttons)="{ item: { id, edit_path, disable_delete } }">
<gl-button
icon="pencil"
diff --git a/app/assets/javascripts/admin/broadcast_messages/constants.js b/app/assets/javascripts/admin/broadcast_messages/constants.js
new file mode 100644
index 00000000000..6250d5a943d
--- /dev/null
+++ b/app/assets/javascripts/admin/broadcast_messages/constants.js
@@ -0,0 +1,35 @@
+import { s__ } from '~/locale';
+
+export const BROADCAST_MESSAGES_PATH = '/admin/broadcast_messages';
+
+export const TYPE_BANNER = 'banner';
+export const TYPE_NOTIFICATION = 'notification';
+
+export const TYPES = [
+ { value: TYPE_BANNER, text: s__('BroadcastMessages|Banner') },
+ { value: TYPE_NOTIFICATION, text: s__('BroadcastMessages|Notification') },
+];
+
+export const THEMES = [
+ { value: 'indigo', text: s__('BroadcastMessages|Indigo') },
+ { value: 'light-indigo', text: s__('BroadcastMessages|Light Indigo') },
+ { value: 'blue', text: s__('BroadcastMessages|Blue') },
+ { value: 'light-blue', text: s__('BroadcastMessages|Light Blue') },
+ { value: 'green', text: s__('BroadcastMessages|Green') },
+ { value: 'light-green', text: s__('BroadcastMessages|Light Green') },
+ { value: 'red', text: s__('BroadcastMessages|Red') },
+ { value: 'light-red', text: s__('BroadcastMessages|Light Red') },
+ { value: 'dark', text: s__('BroadcastMessages|Dark') },
+ { value: 'light', text: s__('BroadcastMessages|Light') },
+];
+
+export const NEW_BROADCAST_MESSAGE = {
+ message: '',
+ broadcastType: TYPES[0].value,
+ theme: THEMES[0].value,
+ dismissable: false,
+ targetPath: '',
+ targetAccessLevels: [],
+ startsAt: new Date(),
+ endsAt: new Date(),
+};
diff --git a/app/assets/javascripts/admin/broadcast_messages/edit.js b/app/assets/javascripts/admin/broadcast_messages/edit.js
new file mode 100644
index 00000000000..70a270f7a56
--- /dev/null
+++ b/app/assets/javascripts/admin/broadcast_messages/edit.js
@@ -0,0 +1,43 @@
+import Vue from 'vue';
+import MessageForm from './components/message_form.vue';
+
+export default () => {
+ const el = document.querySelector('#js-broadcast-message');
+ const {
+ id,
+ message,
+ broadcastType,
+ theme,
+ dismissable,
+ targetAccessLevels,
+ targetAccessLevelOptions,
+ targetPath,
+ startsAt,
+ endsAt,
+ } = el.dataset;
+
+ return new Vue({
+ el,
+ name: 'EditBroadcastMessage',
+ provide: {
+ targetAccessLevelOptions: JSON.parse(targetAccessLevelOptions),
+ },
+ render(createElement) {
+ return createElement(MessageForm, {
+ props: {
+ broadcastMessage: {
+ id: parseInt(id, 10),
+ message,
+ broadcastType,
+ theme,
+ dismissable: dismissable === 'true',
+ targetAccessLevels: JSON.parse(targetAccessLevels),
+ targetPath,
+ startsAt: new Date(startsAt),
+ endsAt: new Date(endsAt),
+ },
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/admin/broadcast_messages/index.js b/app/assets/javascripts/admin/broadcast_messages/index.js
index 81952d2033e..fd8b2aad4ec 100644
--- a/app/assets/javascripts/admin/broadcast_messages/index.js
+++ b/app/assets/javascripts/admin/broadcast_messages/index.js
@@ -3,11 +3,14 @@ import BroadcastMessagesBase from './components/base.vue';
export default () => {
const el = document.querySelector('#js-broadcast-messages');
- const { page, messagesCount, messages } = el.dataset;
+ const { page, targetAccessLevelOptions, messagesCount, messages } = el.dataset;
return new Vue({
el,
name: 'BroadcastMessages',
+ provide: {
+ targetAccessLevelOptions: JSON.parse(targetAccessLevelOptions),
+ },
render(createElement) {
return createElement(BroadcastMessagesBase, {
props: {
diff --git a/app/assets/javascripts/alert_management/components/alert_management_table.vue b/app/assets/javascripts/alert_management/components/alert_management_table.vue
index c0cac958a42..5229d4c9ae2 100644
--- a/app/assets/javascripts/alert_management/components/alert_management_table.vue
+++ b/app/assets/javascripts/alert_management/components/alert_management_table.vue
@@ -17,6 +17,7 @@ import { fetchPolicies } from '~/lib/graphql';
import { joinPaths, visitUrl } from '~/lib/utils/url_utility';
import { s__, __, n__ } from '~/locale';
import AlertStatus from '~/vue_shared/alert_details/components/alert_status.vue';
+import { TOKEN_TYPE_ASSIGNEE } from '~/vue_shared/components/filtered_search_bar/constants';
import {
tdClass,
thClass,
@@ -96,6 +97,7 @@ export default {
sortable: true,
},
],
+ filterSearchTokens: [TOKEN_TYPE_ASSIGNEE],
severityLabels: SEVERITY_LEVELS,
statusTabs: ALERTS_STATUS_TABS,
components: {
@@ -294,9 +296,7 @@ export default {
:status-tabs="$options.statusTabs"
:track-views-options="$options.trackAlertListViewsOptions"
:server-error-message="serverErrorMessage"
- :filter-search-tokens="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
- 'assignee_username',
- ] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
+ :filter-search-tokens="$options.filterSearchTokens"
filter-search-key="alerts"
@page-changed="pageChanged"
@tabs-changed="statusChanged"
@@ -312,6 +312,7 @@ export default {
<template #table>
<gl-table
class="alert-management-table"
+ data-qa-selector="alert_table_container"
:items="
alerts
? alerts.list
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_form.vue b/app/assets/javascripts/alerts_settings/components/alerts_form.vue
index 388d925196b..a0d5cb7f4c3 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_form.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_form.vue
@@ -83,7 +83,7 @@ export default {
</p>
<form ref="settingsForm" @submit.prevent="updateAlertsIntegrationSettings">
<gl-form-group class="gl-pl-0">
- <gl-form-checkbox v-model="createIssueEnabled" data-qa-selector="create_issue_checkbox">
+ <gl-form-checkbox v-model="createIssueEnabled" data-qa-selector="create_incident_checkbox">
<span>{{ $options.i18n.createIncident.label }}</span>
</gl-form-checkbox>
</gl-form-group>
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
index 03bc4b825ae..65c3bc732ed 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
@@ -430,6 +430,7 @@ export default {
v-model="integrationForm.type"
:disabled="isSelectDisabled"
class="gl-max-w-full"
+ data-qa-selector="integration_type_dropdown"
:options="integrationTypesOptions"
/>
@@ -461,6 +462,7 @@ export default {
v-model="integrationForm.name"
type="text"
:placeholder="$options.i18n.integrationFormSteps.nameIntegration.placeholder"
+ data-qa-selector="integration_name_field"
@input="validateName"
/>
</gl-form-group>
@@ -483,6 +485,7 @@ export default {
v-model="integrationForm.active"
:is-loading="loading"
:label="$options.i18n.integrationFormSteps.nameIntegration.activeToggle"
+ data-qa-selector="active_toggle_container"
class="gl-mt-4 gl-font-weight-normal"
/>
</gl-form-group>
@@ -594,6 +597,7 @@ export default {
category="secondary"
class="gl-ml-3 js-no-auto-disable"
data-testid="integration-form-test-and-submit"
+ data-qa-selector="save_and_create_alert_button"
@click="submit(true)"
>
{{ $options.i18n.saveAndTestIntegration }}
@@ -695,6 +699,7 @@ export default {
:debounce="$options.JSON_VALIDATE_DELAY"
rows="6"
max-rows="10"
+ data-qa-selector="test_payload_field"
@input="validateJson(false)"
/>
</gl-form-group>
@@ -706,6 +711,7 @@ export default {
data-testid="send-test-alert"
variant="confirm"
class="js-no-auto-disable"
+ data-qa-selector="send_test_alert_button"
@click="isFormDirty ? null : sendTestAlert()"
>
{{ $options.i18n.send }}
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
index bf456b6adaa..010cb5721a1 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
@@ -375,6 +375,7 @@ export default {
category="secondary"
variant="confirm"
data-testid="add-integration-btn"
+ data-qa-selector="add_integration_button"
class="gl-mt-3"
@click="setFormVisibility(true)"
>
diff --git a/app/assets/javascripts/analytics/cycle_analytics/components/base.vue b/app/assets/javascripts/analytics/cycle_analytics/components/base.vue
new file mode 100644
index 00000000000..a688e2f497b
--- /dev/null
+++ b/app/assets/javascripts/analytics/cycle_analytics/components/base.vue
@@ -0,0 +1,192 @@
+<script>
+import { GlLoadingIcon } from '@gitlab/ui';
+import { mapActions, mapState, mapGetters } from 'vuex';
+import { getCookie, setCookie } from '~/lib/utils/common_utils';
+import ValueStreamMetrics from '~/analytics/shared/components/value_stream_metrics.vue';
+import { VSA_METRICS_GROUPS } from '~/analytics/shared/constants';
+import { toYmd } from '~/analytics/shared/utils';
+import PathNavigation from '~/analytics/cycle_analytics/components/path_navigation.vue';
+import StageTable from '~/analytics/cycle_analytics/components/stage_table.vue';
+import ValueStreamFilters from '~/analytics/cycle_analytics/components/value_stream_filters.vue';
+import UrlSync from '~/vue_shared/components/url_sync.vue';
+import { __ } from '~/locale';
+import { SUMMARY_METRICS_REQUEST, METRICS_REQUESTS } from '../constants';
+
+const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
+
+export default {
+ name: 'CycleAnalytics',
+ components: {
+ GlLoadingIcon,
+ PathNavigation,
+ StageTable,
+ ValueStreamFilters,
+ ValueStreamMetrics,
+ UrlSync,
+ },
+ props: {
+ noDataSvgPath: {
+ type: String,
+ required: true,
+ },
+ noAccessSvgPath: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ isOverviewDialogDismissed: getCookie(OVERVIEW_DIALOG_COOKIE),
+ };
+ },
+ computed: {
+ ...mapState([
+ 'isLoading',
+ 'isLoadingStage',
+ 'isEmptyStage',
+ 'selectedStage',
+ 'selectedStageEvents',
+ 'selectedStageError',
+ 'stageCounts',
+ 'endpoints',
+ 'features',
+ 'createdBefore',
+ 'createdAfter',
+ 'pagination',
+ 'hasNoAccessError',
+ ]),
+ ...mapGetters(['pathNavigationData', 'filterParams']),
+ isLoaded() {
+ return !this.isLoading && !this.isLoadingStage;
+ },
+ displayStageEvents() {
+ const { selectedStageEvents, isLoadingStage, isEmptyStage } = this;
+ return selectedStageEvents.length && !isLoadingStage && !isEmptyStage;
+ },
+ displayNotEnoughData() {
+ return !this.isLoadingStage && this.isEmptyStage;
+ },
+ displayNoAccess() {
+ return !this.isLoadingStage && this.hasNoAccessError;
+ },
+ displayPathNavigation() {
+ return this.isLoading || (this.selectedStage && this.pathNavigationData.length);
+ },
+ emptyStageTitle() {
+ if (this.displayNoAccess) {
+ return __('You need permission.');
+ }
+ return this.selectedStageError
+ ? this.selectedStageError
+ : __("We don't have enough data to show this stage.");
+ },
+ emptyStageText() {
+ if (this.displayNoAccess) {
+ return __('Want to see the data? Please ask an administrator for access.');
+ }
+ return !this.selectedStageError && this.selectedStage?.emptyStageText
+ ? this.selectedStage?.emptyStageText
+ : '';
+ },
+ selectedStageCount() {
+ if (this.selectedStage) {
+ const {
+ stageCounts,
+ selectedStage: { id },
+ } = this;
+ return stageCounts[id];
+ }
+ return 0;
+ },
+ metricsRequests() {
+ return this.features?.cycleAnalyticsForGroups ? METRICS_REQUESTS : SUMMARY_METRICS_REQUEST;
+ },
+ query() {
+ return {
+ created_after: toYmd(this.createdAfter),
+ created_before: toYmd(this.createdBefore),
+ stage_id: this.selectedStage?.id || null,
+ sort: this.pagination?.sort || null,
+ direction: this.pagination?.direction || null,
+ page: this.pagination?.page || null,
+ };
+ },
+ },
+ methods: {
+ ...mapActions([
+ 'fetchStageData',
+ 'setSelectedStage',
+ 'setDateRange',
+ 'updateStageTablePagination',
+ ]),
+ onSetDateRange({ startDate, endDate }) {
+ this.setDateRange({
+ createdAfter: new Date(startDate),
+ createdBefore: new Date(endDate),
+ });
+ },
+ onSelectStage(stage) {
+ this.setSelectedStage(stage);
+ this.updateStageTablePagination({ ...this.pagination, page: 1 });
+ },
+ dismissOverviewDialog() {
+ this.isOverviewDialogDismissed = true;
+ setCookie(OVERVIEW_DIALOG_COOKIE, '1');
+ },
+ onHandleUpdatePagination(data) {
+ this.updateStageTablePagination(data);
+ },
+ },
+ dayRangeOptions: [7, 30, 90],
+ i18n: {
+ dropdownText: __('Last %{days} days'),
+ pageTitle: __('Value Stream Analytics'),
+ recentActivity: __('Recent Project Activity'),
+ },
+ VSA_METRICS_GROUPS,
+};
+</script>
+<template>
+ <div>
+ <h3>{{ $options.i18n.pageTitle }}</h3>
+ <value-stream-filters
+ :group-id="endpoints.groupId"
+ :group-path="endpoints.groupPath"
+ :has-project-filter="false"
+ :start-date="createdAfter"
+ :end-date="createdBefore"
+ @setDateRange="onSetDateRange"
+ />
+ <div class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row">
+ <path-navigation
+ v-if="displayPathNavigation"
+ data-testid="vsa-path-navigation"
+ class="gl-w-full gl-mt-4"
+ :loading="isLoading || isLoadingStage"
+ :stages="pathNavigationData"
+ :selected-stage="selectedStage"
+ @selected="onSelectStage"
+ />
+ </div>
+ <value-stream-metrics
+ :request-path="endpoints.fullPath"
+ :request-params="filterParams"
+ :requests="metricsRequests"
+ :group-by="$options.VSA_METRICS_GROUPS"
+ />
+ <gl-loading-icon v-if="isLoading" size="lg" />
+ <stage-table
+ v-else
+ :is-loading="isLoading || isLoadingStage"
+ :stage-events="selectedStageEvents"
+ :selected-stage="selectedStage"
+ :stage-count="selectedStageCount"
+ :empty-state-title="emptyStageTitle"
+ :empty-state-message="emptyStageText"
+ :no-data-svg-path="noDataSvgPath"
+ :pagination="pagination"
+ @handleUpdatePagination="onHandleUpdatePagination"
+ />
+ <url-sync v-if="isLoaded" :query="query" />
+ </div>
+</template>
diff --git a/app/assets/javascripts/analytics/cycle_analytics/components/filter_bar.vue b/app/assets/javascripts/analytics/cycle_analytics/components/filter_bar.vue
new file mode 100644
index 00000000000..54b632968e2
--- /dev/null
+++ b/app/assets/javascripts/analytics/cycle_analytics/components/filter_bar.vue
@@ -0,0 +1,153 @@
+<script>
+import { mapActions, mapState } from 'vuex';
+import {
+ OPERATORS_IS,
+ OPTIONS_NONE_ANY,
+ TOKEN_TITLE_ASSIGNEE,
+ TOKEN_TITLE_AUTHOR,
+ TOKEN_TITLE_LABEL,
+ TOKEN_TITLE_MILESTONE,
+ TOKEN_TYPE_ASSIGNEE,
+ TOKEN_TYPE_AUTHOR,
+ TOKEN_TYPE_LABEL,
+ TOKEN_TYPE_MILESTONE,
+} from '~/vue_shared/components/filtered_search_bar/constants';
+import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
+import {
+ prepareTokens,
+ processFilters,
+ filterToQueryObject,
+} from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
+import UserToken from '~/vue_shared/components/filtered_search_bar/tokens/user_token.vue';
+import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
+import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
+import UrlSync from '~/vue_shared/components/url_sync.vue';
+
+export default {
+ name: 'FilterBar',
+ components: {
+ FilteredSearchBar,
+ UrlSync,
+ },
+ props: {
+ groupPath: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapState('filters', {
+ selectedMilestone: (state) => state.milestones.selected,
+ selectedAuthor: (state) => state.authors.selected,
+ selectedLabelList: (state) => state.labels.selectedList,
+ selectedAssigneeList: (state) => state.assignees.selectedList,
+ milestonesData: (state) => state.milestones.data,
+ labelsData: (state) => state.labels.data,
+ authorsData: (state) => state.authors.data,
+ assigneesData: (state) => state.assignees.data,
+ }),
+ tokens() {
+ return [
+ {
+ icon: 'clock',
+ title: TOKEN_TITLE_MILESTONE,
+ type: TOKEN_TYPE_MILESTONE,
+ token: MilestoneToken,
+ initialMilestones: this.milestonesData,
+ unique: true,
+ symbol: '%',
+ operators: OPERATORS_IS,
+ fetchMilestones: this.fetchMilestones,
+ },
+ {
+ icon: 'labels',
+ title: TOKEN_TITLE_LABEL,
+ type: TOKEN_TYPE_LABEL,
+ token: LabelToken,
+ defaultLabels: OPTIONS_NONE_ANY,
+ initialLabels: this.labelsData,
+ unique: false,
+ symbol: '~',
+ operators: OPERATORS_IS,
+ fetchLabels: this.fetchLabels,
+ },
+ {
+ icon: 'pencil',
+ title: TOKEN_TITLE_AUTHOR,
+ type: TOKEN_TYPE_AUTHOR,
+ token: UserToken,
+ initialUsers: this.authorsData,
+ unique: true,
+ operators: OPERATORS_IS,
+ fetchUsers: this.fetchAuthors,
+ },
+ {
+ icon: 'user',
+ title: TOKEN_TITLE_ASSIGNEE,
+ type: TOKEN_TYPE_ASSIGNEE,
+ token: UserToken,
+ initialUsers: this.assigneesData,
+ unique: false,
+ operators: OPERATORS_IS,
+ fetchUsers: this.fetchAssignees,
+ },
+ ];
+ },
+ query() {
+ return filterToQueryObject({
+ milestone_title: this.selectedMilestone,
+ author_username: this.selectedAuthor,
+ label_name: this.selectedLabelList,
+ assignee_username: this.selectedAssigneeList,
+ });
+ },
+ },
+ methods: {
+ ...mapActions('filters', [
+ 'setFilters',
+ 'fetchMilestones',
+ 'fetchLabels',
+ 'fetchAuthors',
+ 'fetchAssignees',
+ ]),
+ initialFilterValue() {
+ return prepareTokens({
+ [TOKEN_TYPE_MILESTONE]: this.selectedMilestone,
+ [TOKEN_TYPE_AUTHOR]: this.selectedAuthor,
+ [TOKEN_TYPE_ASSIGNEE]: this.selectedAssigneeList,
+ [TOKEN_TYPE_LABEL]: this.selectedLabelList,
+ });
+ },
+ handleFilter(filters) {
+ const {
+ [TOKEN_TYPE_LABEL]: labels,
+ [TOKEN_TYPE_MILESTONE]: milestone,
+ [TOKEN_TYPE_AUTHOR]: author,
+ [TOKEN_TYPE_ASSIGNEE]: assignees,
+ } = processFilters(filters);
+
+ this.setFilters({
+ selectedAuthor: author ? author[0] : null,
+ selectedMilestone: milestone ? milestone[0] : null,
+ selectedAssigneeList: assignees || [],
+ selectedLabelList: labels || [],
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <filtered-search-bar
+ class="gl-flex-grow-1"
+ :namespace="groupPath"
+ recent-searches-storage-key="value-stream-analytics"
+ :search-input-placeholder="__('Filter results')"
+ :tokens="tokens"
+ :initial-filter-value="initialFilterValue()"
+ @onFilter="handleFilter"
+ />
+ <url-sync :query="query" />
+ </div>
+</template>
diff --git a/app/assets/javascripts/cycle_analytics/components/formatted_stage_count.vue b/app/assets/javascripts/analytics/cycle_analytics/components/formatted_stage_count.vue
index b622b0441e2..b622b0441e2 100644
--- a/app/assets/javascripts/cycle_analytics/components/formatted_stage_count.vue
+++ b/app/assets/javascripts/analytics/cycle_analytics/components/formatted_stage_count.vue
diff --git a/app/assets/javascripts/cycle_analytics/components/metric_tile.vue b/app/assets/javascripts/analytics/cycle_analytics/components/metric_tile.vue
index a5c20b237b3..a5c20b237b3 100644
--- a/app/assets/javascripts/cycle_analytics/components/metric_tile.vue
+++ b/app/assets/javascripts/analytics/cycle_analytics/components/metric_tile.vue
diff --git a/app/assets/javascripts/analytics/cycle_analytics/components/path_navigation.vue b/app/assets/javascripts/analytics/cycle_analytics/components/path_navigation.vue
new file mode 100644
index 00000000000..ac41bc4917c
--- /dev/null
+++ b/app/assets/javascripts/analytics/cycle_analytics/components/path_navigation.vue
@@ -0,0 +1,114 @@
+<script>
+import { GlPath, GlPopover, GlSkeletonLoader } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
+import Tracking from '~/tracking';
+import { OVERVIEW_STAGE_ID } from '../constants';
+import FormattedStageCount from './formatted_stage_count.vue';
+
+export default {
+ name: 'PathNavigation',
+ components: {
+ GlPath,
+ GlSkeletonLoader,
+ GlPopover,
+ FormattedStageCount,
+ },
+ directives: {
+ SafeHtml,
+ },
+ mixins: [Tracking.mixin()],
+ props: {
+ loading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ stages: {
+ type: Array,
+ required: true,
+ },
+ selectedStage: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ },
+ methods: {
+ showPopover({ id }) {
+ return id && id !== OVERVIEW_STAGE_ID;
+ },
+ onSelectStage($event) {
+ this.$emit('selected', $event);
+ this.track('click_path_navigation', {
+ extra: {
+ stage_id: $event.id,
+ },
+ });
+ },
+ },
+ popoverOptions: {
+ triggers: 'hover',
+ placement: 'bottom',
+ },
+};
+</script>
+<template>
+ <gl-skeleton-loader v-if="loading" :width="235" :lines="2" />
+ <gl-path v-else :key="selectedStage.id" :items="stages" @selected="onSelectStage">
+ <template #default="{ pathItem, pathId }">
+ <gl-popover
+ v-if="showPopover(pathItem)"
+ v-bind="$options.popoverOptions"
+ :target="pathId"
+ :css-classes="['stage-item-popover']"
+ data-testid="stage-item-popover"
+ >
+ <template #title>{{ pathItem.title }}</template>
+ <div class="gl-px-4">
+ <div class="gl-display-flex gl-justify-content-space-between">
+ <div class="gl-pr-4 gl-pb-4">
+ {{ s__('ValueStreamEvent|Stage time (median)') }}
+ </div>
+ <div class="gl-pb-4 gl-font-weight-bold">{{ pathItem.metric }}</div>
+ </div>
+ </div>
+ <div class="gl-px-4">
+ <div class="gl-display-flex gl-justify-content-space-between">
+ <div class="gl-pr-4 gl-pb-4">
+ {{ s__('ValueStreamEvent|Items in stage') }}
+ </div>
+ <div class="gl-pb-4 gl-font-weight-bold">
+ <formatted-stage-count :stage-count="pathItem.stageCount" />
+ </div>
+ </div>
+ </div>
+ <div class="gl-px-4 gl-pt-4 gl-border-t-1 gl-border-t-solid gl-border-gray-50">
+ <div
+ v-if="pathItem.startEventHtmlDescription"
+ class="gl-display-flex gl-flex-direction-row"
+ >
+ <div class="gl-display-flex gl-flex-direction-column gl-pr-4 gl-pb-4 metric-label">
+ {{ s__('ValueStreamEvent|Start') }}
+ </div>
+ <div
+ v-safe-html="pathItem.startEventHtmlDescription"
+ class="gl-display-flex gl-flex-direction-column gl-pb-4 stage-event-description"
+ ></div>
+ </div>
+ <div
+ v-if="pathItem.endEventHtmlDescription"
+ class="gl-display-flex gl-flex-direction-row"
+ >
+ <div class="gl-display-flex gl-flex-direction-column gl-pr-4 metric-label">
+ {{ s__('ValueStreamEvent|Stop') }}
+ </div>
+ <div
+ v-safe-html="pathItem.endEventHtmlDescription"
+ class="gl-display-flex gl-flex-direction-column stage-event-description"
+ ></div>
+ </div>
+ </div>
+ </gl-popover>
+ </template>
+ </gl-path>
+</template>
diff --git a/app/assets/javascripts/analytics/cycle_analytics/components/stage_table.vue b/app/assets/javascripts/analytics/cycle_analytics/components/stage_table.vue
new file mode 100644
index 00000000000..78ac29426d9
--- /dev/null
+++ b/app/assets/javascripts/analytics/cycle_analytics/components/stage_table.vue
@@ -0,0 +1,305 @@
+<script>
+import {
+ GlEmptyState,
+ GlIcon,
+ GlLink,
+ GlLoadingIcon,
+ GlPagination,
+ GlTable,
+ GlBadge,
+} from '@gitlab/ui';
+import FormattedStageCount from '~/analytics/cycle_analytics/components/formatted_stage_count.vue';
+import { __ } from '~/locale';
+import Tracking from '~/tracking';
+import {
+ NOT_ENOUGH_DATA_ERROR,
+ FIELD_KEY_TITLE,
+ PAGINATION_SORT_FIELD_END_EVENT,
+ PAGINATION_SORT_FIELD_DURATION,
+ PAGINATION_SORT_DIRECTION_ASC,
+ PAGINATION_SORT_DIRECTION_DESC,
+} from '../constants';
+import TotalTime from './total_time.vue';
+
+const DEFAULT_WORKFLOW_TITLE_PROPERTIES = {
+ thClass: 'gl-w-half',
+ key: FIELD_KEY_TITLE,
+ sortable: false,
+};
+
+const WORKFLOW_COLUMN_TITLES = {
+ issues: { ...DEFAULT_WORKFLOW_TITLE_PROPERTIES, label: __('Issues') },
+ jobs: { ...DEFAULT_WORKFLOW_TITLE_PROPERTIES, label: __('Jobs') },
+ deployments: { ...DEFAULT_WORKFLOW_TITLE_PROPERTIES, label: __('Deployments') },
+ mergeRequests: { ...DEFAULT_WORKFLOW_TITLE_PROPERTIES, label: __('Merge requests') },
+};
+
+const fullProjectPath = ({ namespaceFullPath = '', projectPath }) =>
+ namespaceFullPath.split('/').length > 1 ? `${namespaceFullPath}/${projectPath}` : projectPath;
+
+export default {
+ name: 'StageTable',
+ components: {
+ GlEmptyState,
+ GlIcon,
+ GlLink,
+ GlLoadingIcon,
+ GlPagination,
+ GlTable,
+ GlBadge,
+ TotalTime,
+ FormattedStageCount,
+ },
+ mixins: [Tracking.mixin()],
+ props: {
+ selectedStage: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ isLoading: {
+ type: Boolean,
+ required: true,
+ },
+ stageEvents: {
+ type: Array,
+ required: true,
+ },
+ stageCount: {
+ type: Number,
+ required: false,
+ default: null,
+ },
+ noDataSvgPath: {
+ type: String,
+ required: true,
+ },
+ emptyStateTitle: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ emptyStateMessage: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ pagination: {
+ type: Object,
+ required: false,
+ default: null,
+ },
+ sortable: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ includeProjectName: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ data() {
+ if (this.pagination) {
+ const {
+ pagination: { sort, direction },
+ } = this;
+ return {
+ sort,
+ direction,
+ sortDesc: direction === PAGINATION_SORT_DIRECTION_DESC,
+ };
+ }
+ return { sort: null, direction: null, sortDesc: null };
+ },
+ computed: {
+ isEmptyStage() {
+ return !this.selectedStage || !this.stageEvents.length;
+ },
+ emptyStateTitleText() {
+ return this.emptyStateTitle || NOT_ENOUGH_DATA_ERROR;
+ },
+ isMergeRequestStage() {
+ const [firstEvent] = this.stageEvents;
+ return this.isMrLink(firstEvent.url);
+ },
+ workflowTitle() {
+ if (this.isMergeRequestStage) {
+ return WORKFLOW_COLUMN_TITLES.mergeRequests;
+ }
+ return WORKFLOW_COLUMN_TITLES.issues;
+ },
+ fields() {
+ return [
+ this.workflowTitle,
+ {
+ key: PAGINATION_SORT_FIELD_END_EVENT,
+ label: __('Last event'),
+ sortable: this.sortable,
+ },
+ {
+ key: PAGINATION_SORT_FIELD_DURATION,
+ label: __('Duration'),
+ sortable: this.sortable,
+ },
+ ];
+ },
+ prevPage() {
+ return Math.max(this.pagination.page - 1, 0);
+ },
+ nextPage() {
+ return this.pagination.hasNextPage ? this.pagination.page + 1 : null;
+ },
+ },
+ methods: {
+ isMrLink(url = '') {
+ return url.includes('/merge_request');
+ },
+ itemId({ iid, projectPath, namespaceFullPath = '' }, separator = '#') {
+ const prefix = this.includeProjectName
+ ? fullProjectPath({ namespaceFullPath, projectPath })
+ : '';
+ return `${prefix}${separator}${iid}`;
+ },
+ itemDisplayName(item) {
+ const separator = this.isMrLink(item.url) ? '!' : '#';
+ return this.itemId(item, separator);
+ },
+ itemTitle(item) {
+ return item.title || item.name;
+ },
+ onSelectPage(page) {
+ const { sort, direction } = this.pagination;
+ this.track('click_button', { label: 'pagination' });
+ this.$emit('handleUpdatePagination', { sort, direction, page });
+ },
+ onSort({ sortBy, sortDesc }) {
+ const direction = sortDesc ? PAGINATION_SORT_DIRECTION_DESC : PAGINATION_SORT_DIRECTION_ASC;
+ this.sort = sortBy;
+ this.sortDesc = sortDesc;
+ this.$emit('handleUpdatePagination', { sort: sortBy, direction });
+ this.track('click_button', { label: `sort_${sortBy}_${direction}` });
+ },
+ },
+};
+</script>
+<template>
+ <div data-testid="vsa-stage-table">
+ <gl-loading-icon v-if="isLoading" class="gl-mt-4" size="lg" />
+ <gl-empty-state
+ v-else-if="isEmptyStage"
+ :title="emptyStateTitleText"
+ :description="emptyStateMessage"
+ :svg-path="noDataSvgPath"
+ />
+ <gl-table
+ v-else
+ stacked="lg"
+ show-empty
+ :sort-by.sync="sort"
+ :sort-direction.sync="direction"
+ :sort-desc.sync="sortDesc"
+ :fields="fields"
+ :items="stageEvents"
+ :empty-text="emptyStateMessage"
+ @sort-changed="onSort"
+ >
+ <template v-if="stageCount" #head(title)="data">
+ <span>{{ data.label }}</span
+ ><gl-badge class="gl-ml-2" size="sm"
+ ><formatted-stage-count :stage-count="stageCount"
+ /></gl-badge>
+ </template>
+ <template #head(duration)="data">
+ <span data-testid="vsa-stage-header-duration">{{ data.label }}</span>
+ </template>
+ <template #head(end_event)="data">
+ <span data-testid="vsa-stage-header-last-event">{{ data.label }}</span>
+ </template>
+ <template #cell(title)="{ item }">
+ <div data-testid="vsa-stage-event">
+ <div v-if="item.id" data-testid="vsa-stage-content">
+ <p class="gl-m-0">
+ <gl-link
+ data-testid="vsa-stage-event-link"
+ class="gl-text-black-normal"
+ :href="item.url"
+ >{{ itemId(item.id, '#') }}</gl-link
+ >
+ <gl-icon :size="16" name="fork" />
+ <gl-link
+ v-if="item.branch"
+ :href="item.branch.url"
+ class="gl-text-black-normal ref-name"
+ >{{ item.branch.name }}</gl-link
+ >
+ <span class="icon-branch gl-text-gray-400">
+ <gl-icon name="commit" :size="14" />
+ </span>
+ <gl-link
+ class="commit-sha"
+ :href="item.commitUrl"
+ data-testid="vsa-stage-event-build-sha"
+ >{{ item.shortSha }}</gl-link
+ >
+ </p>
+ <p class="gl-m-0">
+ <span data-testid="vsa-stage-event-build-author-and-date">
+ <gl-link class="gl-text-black-normal" :href="item.url">{{ item.date }}</gl-link>
+ {{ s__('ByAuthor|by') }}
+ <gl-link
+ class="gl-text-black-normal issue-author-link"
+ :href="item.author.webUrl"
+ >{{ item.author.name }}</gl-link
+ >
+ </span>
+ </p>
+ </div>
+ <div v-else data-testid="vsa-stage-content">
+ <h5 class="gl-font-weight-bold gl-my-1" data-testid="vsa-stage-event-title">
+ <gl-link class="gl-text-black-normal" :href="item.url">{{ itemTitle(item) }}</gl-link>
+ </h5>
+ <p class="gl-m-0">
+ <gl-link
+ data-testid="vsa-stage-event-link"
+ class="gl-text-black-normal"
+ :href="item.url"
+ >{{ itemDisplayName(item) }}</gl-link
+ >
+ <span class="gl-font-lg">&middot;</span>
+ <span data-testid="vsa-stage-event-date">
+ {{ s__('OpenedNDaysAgo|Created') }}
+ <gl-link class="gl-text-black-normal" :href="item.url">{{
+ item.createdAt
+ }}</gl-link>
+ </span>
+ <span data-testid="vsa-stage-event-author">
+ {{ s__('ByAuthor|by') }}
+ <gl-link class="gl-text-black-normal" :href="item.author.webUrl">{{
+ item.author.name
+ }}</gl-link>
+ </span>
+ </p>
+ </div>
+ </div>
+ </template>
+ <template #cell(duration)="{ item }">
+ <total-time :time="item.totalTime" data-testid="vsa-stage-event-time" />
+ </template>
+ <template #cell(end_event)="{ item }">
+ <span data-testid="vsa-stage-last-event">{{ item.endEventTimestamp }}</span>
+ </template>
+ </gl-table>
+ <gl-pagination
+ v-if="pagination && !isLoading && !isEmptyStage"
+ :value="pagination.page"
+ :prev-page="prevPage"
+ :next-page="nextPage"
+ align="center"
+ class="gl-mt-3"
+ data-testid="vsa-stage-pagination"
+ @input="onSelectPage"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/cycle_analytics/components/total_time.vue b/app/assets/javascripts/analytics/cycle_analytics/components/total_time.vue
index 725952c3518..725952c3518 100644
--- a/app/assets/javascripts/cycle_analytics/components/total_time.vue
+++ b/app/assets/javascripts/analytics/cycle_analytics/components/total_time.vue
diff --git a/app/assets/javascripts/cycle_analytics/components/value_stream_filters.vue b/app/assets/javascripts/analytics/cycle_analytics/components/value_stream_filters.vue
index 17decb6b448..17decb6b448 100644
--- a/app/assets/javascripts/cycle_analytics/components/value_stream_filters.vue
+++ b/app/assets/javascripts/analytics/cycle_analytics/components/value_stream_filters.vue
diff --git a/app/assets/javascripts/cycle_analytics/constants.js b/app/assets/javascripts/analytics/cycle_analytics/constants.js
index 2758d686fb1..2758d686fb1 100644
--- a/app/assets/javascripts/cycle_analytics/constants.js
+++ b/app/assets/javascripts/analytics/cycle_analytics/constants.js
diff --git a/app/assets/javascripts/analytics/cycle_analytics/index.js b/app/assets/javascripts/analytics/cycle_analytics/index.js
new file mode 100644
index 00000000000..df161f7e563
--- /dev/null
+++ b/app/assets/javascripts/analytics/cycle_analytics/index.js
@@ -0,0 +1,50 @@
+import Vue from 'vue';
+import {
+ extractFilterQueryParameters,
+ extractPaginationQueryParameters,
+} from '~/analytics/shared/utils';
+import Translate from '~/vue_shared/translate';
+import CycleAnalytics from './components/base.vue';
+import createStore from './store';
+import { buildCycleAnalyticsInitialData } from './utils';
+
+Vue.use(Translate);
+
+export default () => {
+ const store = createStore();
+ const el = document.querySelector('#js-cycle-analytics');
+ const { noAccessSvgPath, noDataSvgPath } = el.dataset;
+ const initialData = buildCycleAnalyticsInitialData({ ...el.dataset, gon });
+
+ const pagination = extractPaginationQueryParameters(window.location.search);
+ const {
+ selectedAuthor,
+ selectedMilestone,
+ selectedAssigneeList,
+ selectedLabelList,
+ } = extractFilterQueryParameters(window.location.search);
+
+ store.dispatch('initializeVsa', {
+ ...initialData,
+ selectedAuthor,
+ selectedMilestone,
+ selectedAssigneeList,
+ selectedLabelList,
+ pagination,
+ });
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el,
+ name: 'CycleAnalytics',
+ apolloProvider: {},
+ store,
+ render: (createElement) =>
+ createElement(CycleAnalytics, {
+ props: {
+ noDataSvgPath,
+ noAccessSvgPath,
+ },
+ }),
+ });
+};
diff --git a/app/assets/javascripts/cycle_analytics/store/actions.js b/app/assets/javascripts/analytics/cycle_analytics/store/actions.js
index 4a201e00582..4a201e00582 100644
--- a/app/assets/javascripts/cycle_analytics/store/actions.js
+++ b/app/assets/javascripts/analytics/cycle_analytics/store/actions.js
diff --git a/app/assets/javascripts/cycle_analytics/store/getters.js b/app/assets/javascripts/analytics/cycle_analytics/store/getters.js
index 83068cabf0f..83068cabf0f 100644
--- a/app/assets/javascripts/cycle_analytics/store/getters.js
+++ b/app/assets/javascripts/analytics/cycle_analytics/store/getters.js
diff --git a/app/assets/javascripts/cycle_analytics/store/index.js b/app/assets/javascripts/analytics/cycle_analytics/store/index.js
index 76e3e835016..76e3e835016 100644
--- a/app/assets/javascripts/cycle_analytics/store/index.js
+++ b/app/assets/javascripts/analytics/cycle_analytics/store/index.js
diff --git a/app/assets/javascripts/cycle_analytics/store/mutation_types.js b/app/assets/javascripts/analytics/cycle_analytics/store/mutation_types.js
index 9376d81f317..9376d81f317 100644
--- a/app/assets/javascripts/cycle_analytics/store/mutation_types.js
+++ b/app/assets/javascripts/analytics/cycle_analytics/store/mutation_types.js
diff --git a/app/assets/javascripts/cycle_analytics/store/mutations.js b/app/assets/javascripts/analytics/cycle_analytics/store/mutations.js
index 8567529caf2..8567529caf2 100644
--- a/app/assets/javascripts/cycle_analytics/store/mutations.js
+++ b/app/assets/javascripts/analytics/cycle_analytics/store/mutations.js
diff --git a/app/assets/javascripts/analytics/cycle_analytics/store/state.js b/app/assets/javascripts/analytics/cycle_analytics/store/state.js
new file mode 100644
index 00000000000..00dd2e53883
--- /dev/null
+++ b/app/assets/javascripts/analytics/cycle_analytics/store/state.js
@@ -0,0 +1,31 @@
+import {
+ PAGINATION_SORT_FIELD_END_EVENT,
+ PAGINATION_SORT_DIRECTION_DESC,
+} from '~/analytics/cycle_analytics/constants';
+
+export default () => ({
+ id: null,
+ features: {},
+ endpoints: {},
+ createdAfter: null,
+ createdBefore: null,
+ stages: [],
+ analytics: [],
+ valueStreams: [],
+ selectedValueStream: {},
+ selectedStage: {},
+ selectedStageEvents: [],
+ selectedStageError: '',
+ medians: {},
+ stageCounts: {},
+ hasNoAccessError: false,
+ isLoading: false,
+ isLoadingStage: false,
+ isEmptyStage: false,
+ pagination: {
+ page: null,
+ hasNextPage: false,
+ sort: PAGINATION_SORT_FIELD_END_EVENT,
+ direction: PAGINATION_SORT_DIRECTION_DESC,
+ },
+});
diff --git a/app/assets/javascripts/cycle_analytics/utils.js b/app/assets/javascripts/analytics/cycle_analytics/utils.js
index 428bb11b950..428bb11b950 100644
--- a/app/assets/javascripts/cycle_analytics/utils.js
+++ b/app/assets/javascripts/analytics/cycle_analytics/utils.js
diff --git a/app/assets/javascripts/api/analytics_api.js b/app/assets/javascripts/api/analytics_api.js
index 15457f28eff..66ed30130bb 100644
--- a/app/assets/javascripts/api/analytics_api.js
+++ b/app/assets/javascripts/api/analytics_api.js
@@ -7,6 +7,11 @@ const PROJECT_VSA_PATH_BASE = '/:request_path/-/analytics/value_stream_analytics
const PROJECT_VSA_STAGES_PATH = `${PROJECT_VSA_PATH_BASE}/:value_stream_id/stages`;
const PROJECT_VSA_STAGE_DATA_PATH = `${PROJECT_VSA_STAGES_PATH}/:stage_id`;
+export const LEAD_TIME_METRIC_TYPE = 'lead_time';
+export const CYCLE_TIME_METRIC_TYPE = 'cycle_time';
+export const ISSUES_METRIC_TYPE = 'issues';
+export const DEPLOYS_METRIC_TYPE = 'deploys';
+
export const METRIC_TYPE_SUMMARY = 'summary';
export const METRIC_TYPE_TIME_SUMMARY = 'time_summary';
diff --git a/app/assets/javascripts/artifacts/graphql/queries/get_job_artifacts.query.graphql b/app/assets/javascripts/artifacts/graphql/queries/get_job_artifacts.query.graphql
index 89a24d7891e..9777153999e 100644
--- a/app/assets/javascripts/artifacts/graphql/queries/get_job_artifacts.query.graphql
+++ b/app/assets/javascripts/artifacts/graphql/queries/get_job_artifacts.query.graphql
@@ -10,6 +10,7 @@ query getJobArtifacts(
project(fullPath: $projectPath) {
id
jobs(
+ withArtifacts: true
statuses: [SUCCESS, FAILED]
first: $firstPageSize
last: $lastPageSize
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 9ab1d6bfd80..1855fb9ed8c 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -2,7 +2,7 @@
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import $ from 'jquery';
-import { uniq } from 'lodash';
+import { uniq, escape } from 'lodash';
import { getEmojiScoreWithIntent } from '~/emoji/utils';
import { getCookie, setCookie, scrollToElement } from '~/lib/utils/common_utils';
import * as Emoji from '~/emoji';
@@ -149,7 +149,7 @@ export class AwardsHandler {
let frequentlyUsedCatgegory = '';
if (frequentlyUsedEmojis.length > 0) {
frequentlyUsedCatgegory = this.renderCategory('Frequently used', frequentlyUsedEmojis, {
- menuListClass: 'frequent-emojis',
+ frequentEmojis: true,
});
}
@@ -228,9 +228,9 @@ export class AwardsHandler {
renderCategory(name, emojiList, opts = {}) {
return `
<h5 class="emoji-menu-title">
- ${name}
+ ${escape(name)}
</h5>
- <ul class="clearfix emoji-menu-list ${opts.menuListClass || ''}">
+ <ul class="clearfix emoji-menu-list ${opts.frequentEmojis ? 'frequent-emojis' : ''}">
${emojiList
.map(
(emojiName) => `
diff --git a/app/assets/javascripts/badges/components/badge_form.vue b/app/assets/javascripts/badges/components/badge_form.vue
index f68666f8a0c..c95c90d5daf 100644
--- a/app/assets/javascripts/badges/components/badge_form.vue
+++ b/app/assets/javascripts/badges/components/badge_form.vue
@@ -1,10 +1,12 @@
<script>
-import { GlLoadingIcon, GlFormInput, GlFormGroup, GlButton, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlLoadingIcon, GlFormInput, GlFormGroup, GlButton } from '@gitlab/ui';
import { escape, debounce } from 'lodash';
import { mapActions, mapState } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { createAlert, VARIANT_INFO } from '~/flash';
import { s__, sprintf } from '~/locale';
import createEmptyBadge from '../empty_badge';
+import { PLACEHOLDERS } from '../constants';
import Badge from './badge.vue';
const badgePreviewDelayInMilliseconds = 1500;
@@ -19,7 +21,7 @@ export default {
GlFormGroup,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
isEditing: {
@@ -49,9 +51,9 @@ export default {
return this.badgeInAddForm;
},
helpText() {
- const placeholders = ['project_path', 'project_id', 'default_branch', 'commit_sha']
- .map((placeholder) => `<code>%{${placeholder}}</code>`)
- .join(', ');
+ const placeholders = PLACEHOLDERS.map((placeholder) => `<code>%{${placeholder}}</code>`).join(
+ ', ',
+ );
return sprintf(
s__('Badges|Supported %{docsLinkStart}variables%{docsLinkEnd}: %{placeholders}'),
{
diff --git a/app/assets/javascripts/badges/constants.js b/app/assets/javascripts/badges/constants.js
index 8fbe3db5ef1..709436abca6 100644
--- a/app/assets/javascripts/badges/constants.js
+++ b/app/assets/javascripts/badges/constants.js
@@ -1,2 +1,10 @@
export const GROUP_BADGE = 'group';
export const PROJECT_BADGE = 'project';
+export const PLACEHOLDERS = [
+ 'project_path',
+ 'project_title',
+ 'project_name',
+ 'project_id',
+ 'default_branch',
+ 'commit_sha',
+];
diff --git a/app/assets/javascripts/batch_comments/components/draft_note.vue b/app/assets/javascripts/batch_comments/components/draft_note.vue
index e5408d0734a..5bb310afac7 100644
--- a/app/assets/javascripts/batch_comments/components/draft_note.vue
+++ b/app/assets/javascripts/batch_comments/components/draft_note.vue
@@ -1,6 +1,7 @@
<script>
-import { GlButton, GlSafeHtmlDirective, GlBadge } from '@gitlab/ui';
+import { GlButton, GlBadge } from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import NoteableNote from '~/notes/components/noteable_note.vue';
import PublishButton from './publish_button.vue';
@@ -13,7 +14,7 @@ export default {
GlBadge,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [glFeatureFlagMixin()],
props: {
@@ -84,32 +85,25 @@ export default {
};
</script>
<template>
- <article
- class="draft-note-component note-wrapper"
- @mouseenter="handleMouseEnter(draft)"
- @mouseleave="handleMouseLeave(draft)"
+ <noteable-note
+ :note="draft"
+ :line="line"
+ :discussion-root="true"
+ :class="{ 'gl-mb-0!': glFeatures.mrReviewSubmitComment }"
+ class="draft-note-component draft-note"
+ @handleEdit="handleEditing"
+ @cancelForm="handleNotEditing"
+ @updateSuccess="handleNotEditing"
+ @handleDeleteNote="deleteDraft"
+ @handleUpdateNote="update"
+ @toggleResolveStatus="toggleResolveDiscussion(draft.id)"
+ @mouseenter.native="handleMouseEnter(draft)"
+ @mouseleave.native="handleMouseLeave(draft)"
>
- <ul class="notes draft-notes">
- <noteable-note
- :note="draft"
- :line="line"
- :discussion-root="true"
- :class="{ 'gl-mb-0!': glFeatures.mrReviewSubmitComment }"
- class="draft-note"
- @handleEdit="handleEditing"
- @cancelForm="handleNotEditing"
- @updateSuccess="handleNotEditing"
- @handleDeleteNote="deleteDraft"
- @handleUpdateNote="update"
- @toggleResolveStatus="toggleResolveDiscussion(draft.id)"
- >
- <template #note-header-info>
- <gl-badge variant="warning" class="gl-mr-2">{{ __('Pending') }}</gl-badge>
- </template>
- </noteable-note>
- </ul>
-
- <template v-if="!isEditingDraft">
+ <template #note-header-info>
+ <gl-badge variant="warning" class="gl-mr-2">{{ __('Pending') }}</gl-badge>
+ </template>
+ <template v-if="!isEditingDraft" #after-note-body>
<div
v-if="draftCommands"
v-safe-html:[$options.safeHtmlConfig]="draftCommands"
@@ -133,5 +127,5 @@ export default {
</gl-button>
</p>
</template>
- </article>
+ </noteable-note>
</template>
diff --git a/app/assets/javascripts/behaviors/copy_code.js b/app/assets/javascripts/behaviors/copy_code.js
index ae186aba32d..0c81ae63f21 100644
--- a/app/assets/javascripts/behaviors/copy_code.js
+++ b/app/assets/javascripts/behaviors/copy_code.js
@@ -7,7 +7,10 @@ class CopyCodeButton extends HTMLElement {
connectedCallback() {
this.for = uniqueId('code-');
- this.parentNode.querySelector('pre').setAttribute('id', this.for);
+ const target = this.parentNode.querySelector('pre');
+ if (!target) return;
+
+ target.setAttribute('id', this.for);
this.appendChild(this.createButton());
}
diff --git a/app/assets/javascripts/behaviors/index.js b/app/assets/javascripts/behaviors/index.js
index 30160248a77..220064e6673 100644
--- a/app/assets/javascripts/behaviors/index.js
+++ b/app/assets/javascripts/behaviors/index.js
@@ -1,6 +1,5 @@
import $ from 'jquery';
import './autosize';
-import './markdown/render_gfm';
import initCollapseSidebarOnWindowResize from './collapse_sidebar_on_window_resize';
import initCopyToClipboard from './copy_to_clipboard';
import installGlEmojiElement from './gl_emoji';
diff --git a/app/assets/javascripts/behaviors/markdown/init_gfm.js b/app/assets/javascripts/behaviors/markdown/init_gfm.js
new file mode 100644
index 00000000000..d9c7cee50da
--- /dev/null
+++ b/app/assets/javascripts/behaviors/markdown/init_gfm.js
@@ -0,0 +1,13 @@
+import $ from 'jquery';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
+
+$.fn.renderGFM = function plugin() {
+ this.get().forEach(renderGFM);
+ return this;
+};
+requestIdleCallback(
+ () => {
+ renderGFM(document.body);
+ },
+ { timeout: 500 },
+);
diff --git a/app/assets/javascripts/behaviors/markdown/render_gfm.js b/app/assets/javascripts/behaviors/markdown/render_gfm.js
index a08cf48c327..2eab5b84e3e 100644
--- a/app/assets/javascripts/behaviors/markdown/render_gfm.js
+++ b/app/assets/javascripts/behaviors/markdown/render_gfm.js
@@ -1,45 +1,52 @@
-import $ from 'jquery';
import syntaxHighlight from '~/syntax_highlight';
import highlightCurrentUser from './highlight_current_user';
import { renderKroki } from './render_kroki';
import renderMath from './render_math';
import renderSandboxedMermaid from './render_sandboxed_mermaid';
import renderMetrics from './render_metrics';
+import renderObservability from './render_observability';
import { renderJSONTable } from './render_json_table';
-// Render GitLab flavoured Markdown
-//
-// Delegates to syntax highlight and render math & mermaid diagrams.
-//
-$.fn.renderGFM = function renderGFM() {
- syntaxHighlight(this.find('.js-syntax-highlight').get());
- renderKroki(this.find('.js-render-kroki[hidden]').get());
- renderMath(this.find('.js-render-math'));
- renderSandboxedMermaid(this.find('.js-render-mermaid').get());
- renderJSONTable(
- Array.from(this.find('[lang="json"][data-lang-params="table"]').get()).map((e) => e.parentNode),
- );
-
- highlightCurrentUser(this.find('.gfm-project_member').get());
+function initPopovers(elements) {
+ if (!elements.length) return;
+ import(/* webpackChunkName: 'IssuablePopoverBundle' */ '~/issuable/popover')
+ .then(({ default: initIssuablePopovers }) => {
+ initIssuablePopovers(elements);
+ })
+ .catch(() => {});
+}
- const issuablePopoverElements = this.find('.gfm-issue, .gfm-merge_request').get();
- if (issuablePopoverElements.length) {
- import(/* webpackChunkName: 'IssuablePopoverBundle' */ '~/issuable/popover')
- .then(({ default: initIssuablePopovers }) => {
- initIssuablePopovers(issuablePopoverElements);
- })
- .catch(() => {});
- }
-
- renderMetrics(this.find('.js-render-metrics').get());
- return this;
-};
+// Render GitLab flavoured Markdown
+export function renderGFM(element) {
+ const [
+ highlightEls,
+ krokiEls,
+ mathEls,
+ mermaidEls,
+ tableEls,
+ userEls,
+ popoverEls,
+ metricsEls,
+ observabilityEls,
+ ] = [
+ '.js-syntax-highlight',
+ '.js-render-kroki[hidden]',
+ '.js-render-math',
+ '.js-render-mermaid',
+ '[lang="json"][data-lang-params="table"]',
+ '.gfm-project_member',
+ '.gfm-issue, .gfm-merge_request',
+ '.js-render-metrics',
+ '.js-render-observability',
+ ].map((selector) => Array.from(element.querySelectorAll(selector)));
-$(() => {
- window.requestIdleCallback(
- () => {
- $('body').renderGFM();
- },
- { timeout: 500 },
- );
-});
+ syntaxHighlight(highlightEls);
+ renderKroki(krokiEls);
+ renderMath(mathEls);
+ renderSandboxedMermaid(mermaidEls);
+ renderJSONTable(tableEls.map((e) => e.parentNode));
+ highlightCurrentUser(userEls);
+ renderMetrics(metricsEls);
+ renderObservability(observabilityEls);
+ initPopovers(popoverEls);
+}
diff --git a/app/assets/javascripts/behaviors/markdown/render_math.js b/app/assets/javascripts/behaviors/markdown/render_math.js
index ac41af4df7a..7852a909160 100644
--- a/app/assets/javascripts/behaviors/markdown/render_math.js
+++ b/app/assets/javascripts/behaviors/markdown/render_math.js
@@ -175,14 +175,14 @@ class SafeMathRenderer {
}
}
-export default function renderMath($els) {
- if (!$els.length) return;
+export default function renderMath(elements) {
+ if (!elements.length) return;
Promise.all([
import(/* webpackChunkName: 'katex' */ 'katex'),
import(/* webpackChunkName: 'katex' */ 'katex/dist/katex.min.css'),
])
.then(([katex]) => {
- const renderer = new SafeMathRenderer($els.get(), katex);
+ const renderer = new SafeMathRenderer(elements, katex);
renderer.render();
renderer.attachEvents();
})
diff --git a/app/assets/javascripts/behaviors/markdown/render_observability.js b/app/assets/javascripts/behaviors/markdown/render_observability.js
new file mode 100644
index 00000000000..704d85cf22e
--- /dev/null
+++ b/app/assets/javascripts/behaviors/markdown/render_observability.js
@@ -0,0 +1,33 @@
+import Vue from 'vue';
+import { darkModeEnabled } from '~/lib/utils/color_utils';
+import { setUrlParams } from '~/lib/utils/url_utility';
+
+export function getFrameSrc(url) {
+ return `${setUrlParams({ theme: darkModeEnabled() ? 'dark' : 'light' }, url)}&kiosk`;
+}
+
+const mountVueComponent = (element) => {
+ const url = [element.dataset.frameUrl];
+
+ return new Vue({
+ el: element,
+ render(h) {
+ return h('iframe', {
+ style: {
+ height: '366px',
+ width: '768px',
+ },
+ attrs: {
+ src: getFrameSrc(url),
+ frameBorder: '0',
+ },
+ });
+ },
+ });
+};
+
+export default function renderObservability(elements) {
+ elements.forEach((element) => {
+ mountVueComponent(element);
+ });
+}
diff --git a/app/assets/javascripts/behaviors/preview_markdown.js b/app/assets/javascripts/behaviors/preview_markdown.js
index 68f5180cc03..86a05f24dfc 100644
--- a/app/assets/javascripts/behaviors/preview_markdown.js
+++ b/app/assets/javascripts/behaviors/preview_markdown.js
@@ -4,6 +4,7 @@ import $ from 'jquery';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
+import '~/behaviors/markdown/init_gfm';
// MarkdownPreview
//
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
index 97ba9e15c0f..64297da39cd 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
@@ -3,7 +3,7 @@ import ClipboardJS from 'clipboard';
import Mousetrap from 'mousetrap';
import { getSelectedFragment } from '~/lib/utils/common_utils';
import { isElementVisible } from '~/lib/utils/dom_utils';
-import { DEBOUNCE_DROPDOWN_DELAY } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
+import { DEBOUNCE_DROPDOWN_DELAY } from '~/sidebar/components/labels/labels_select_widget/constants';
import toast from '~/vue_shared/plugins/global_toast';
import { s__ } from '~/locale';
import Sidebar from '~/right_sidebar';
diff --git a/app/assets/javascripts/blob/components/blob_header.vue b/app/assets/javascripts/blob/components/blob_header.vue
index 716321430d2..361d736f740 100644
--- a/app/assets/javascripts/blob/components/blob_header.vue
+++ b/app/assets/javascripts/blob/components/blob_header.vue
@@ -1,5 +1,5 @@
<script>
-import DefaultActions from './blob_header_default_actions.vue';
+import DefaultActions from 'jh_else_ce/blob/components/blob_header_default_actions.vue';
import BlobFilepath from './blob_header_filepath.vue';
import ViewerSwitcher from './blob_header_viewer_switcher.vue';
import { SIMPLE_BLOB_VIEWER } from './constants';
diff --git a/app/assets/javascripts/blob/openapi/index.js b/app/assets/javascripts/blob/openapi/index.js
index 24a54358de5..8cfdc00bb40 100644
--- a/app/assets/javascripts/blob/openapi/index.js
+++ b/app/assets/javascripts/blob/openapi/index.js
@@ -5,7 +5,7 @@ const createSandbox = () => {
const iframeEl = document.createElement('iframe');
setAttributes(iframeEl, {
src: '/-/sandbox/swagger',
- sandbox: 'allow-scripts allow-popups',
+ sandbox: 'allow-scripts allow-popups allow-forms',
frameBorder: 0,
width: '100%',
// The height will be adjusted dynamically.
diff --git a/app/assets/javascripts/blob/viewer/index.js b/app/assets/javascripts/blob/viewer/index.js
index 8d323c335d3..439c4258805 100644
--- a/app/assets/javascripts/blob/viewer/index.js
+++ b/app/assets/javascripts/blob/viewer/index.js
@@ -1,5 +1,5 @@
import $ from 'jquery';
-import '~/behaviors/markdown/render_gfm';
+import '~/behaviors/markdown/init_gfm';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
import {
diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js
index 4741dd53708..509d399273d 100644
--- a/app/assets/javascripts/blob_edit/blob_bundle.js
+++ b/app/assets/javascripts/blob_edit/blob_bundle.js
@@ -66,7 +66,7 @@ export default () => {
})
.catch((e) =>
createAlert({
- message: e,
+ message: e.message,
}),
);
diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js
index 97d8b206307..46b3f16df77 100644
--- a/app/assets/javascripts/blob_edit/edit_blob.js
+++ b/app/assets/javascripts/blob_edit/edit_blob.js
@@ -9,6 +9,7 @@ import { addEditorMarkdownListeners } from '~/lib/utils/text_markdown';
import { insertFinalNewline } from '~/lib/utils/text_utility';
import TemplateSelectorMediator from '../blob/file_template_mediator';
import { BLOB_EDITOR_ERROR, BLOB_PREVIEW_ERROR } from './constants';
+import '~/behaviors/markdown/init_gfm';
export default class EditBlob {
// The options object has:
diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue
index 150378f7a7d..ca86894ca40 100644
--- a/app/assets/javascripts/boards/components/board_content.vue
+++ b/app/assets/javascripts/boards/components/board_content.vue
@@ -1,8 +1,10 @@
<script>
import { GlAlert } from '@gitlab/ui';
+import { breakpoints } from '@gitlab/ui/dist/utils';
import { sortBy, throttle } from 'lodash';
import Draggable from 'vuedraggable';
import { mapState, mapGetters, mapActions } from 'vuex';
+import { contentTop } from '~/lib/utils/common_utils';
import { s__ } from '~/locale';
import { formatBoardLists } from 'ee_else_ce/boards/boards_util';
import BoardAddNewColumn from 'ee_else_ce/boards/components/board_add_new_column.vue';
@@ -114,6 +116,8 @@ export default {
group: 'boards-list',
tag: 'div',
value: this.boardListsToUse,
+ delay: 100,
+ delayOnTouchOnly: true,
};
return this.canDragColumns ? options : {};
@@ -142,7 +146,11 @@ export default {
el.scrollTo({ left: el.scrollWidth, behavior: 'smooth' });
},
setBoardHeight() {
- this.boardHeight = `${window.innerHeight - this.$el.getBoundingClientRect().top}px`;
+ if (window.innerWidth < breakpoints.md) {
+ this.boardHeight = `${window.innerHeight - contentTop()}px`;
+ } else {
+ this.boardHeight = `${window.innerHeight - this.$el.getBoundingClientRect().top}px`;
+ }
},
},
};
diff --git a/app/assets/javascripts/boards/components/board_content_sidebar.vue b/app/assets/javascripts/boards/components/board_content_sidebar.vue
index 00b4e6c96a9..392a73b5859 100644
--- a/app/assets/javascripts/boards/components/board_content_sidebar.vue
+++ b/app/assets/javascripts/boards/components/board_content_sidebar.vue
@@ -14,8 +14,8 @@ import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue
import SidebarSeverity from '~/sidebar/components/severity/sidebar_severity.vue';
import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue';
import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
-import SidebarLabelsWidget from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue';
-import { LabelType } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
+import SidebarLabelsWidget from '~/sidebar/components/labels/labels_select_widget/labels_select_root.vue';
+import { LabelType } from '~/sidebar/components/labels/labels_select_widget/constants';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
@@ -32,10 +32,12 @@ export default {
SidebarTodoWidget,
SidebarSeverity,
MountingPortal,
+ SidebarHealthStatusWidget: () =>
+ import('ee_component/sidebar/components/health_status/sidebar_health_status_widget.vue'),
+ SidebarIterationWidget: () =>
+ import('ee_component/sidebar/components/iteration/sidebar_iteration_widget.vue'),
SidebarWeightWidget: () =>
import('ee_component/sidebar/components/weight/sidebar_weight_widget.vue'),
- IterationSidebarDropdownWidget: () =>
- import('ee_component/sidebar/components/iteration_sidebar_dropdown_widget.vue'),
},
mixins: [glFeatureFlagMixin()],
inject: {
@@ -51,6 +53,9 @@ export default {
weightFeatureAvailable: {
default: false,
},
+ healthStatusFeatureAvailable: {
+ default: false,
+ },
allowLabelEdit: {
default: false,
},
@@ -115,6 +120,7 @@ export default {
'setActiveItemConfidential',
'setActiveBoardItemLabels',
'setActiveItemWeight',
+ 'setActiveItemHealthStatus',
]),
handleClose() {
this.toggleBoardItem({ boardItem: this.activeBoardItem, sidebarType: this.sidebarType });
@@ -143,7 +149,7 @@ export default {
<gl-drawer
v-bind="$attrs"
:open="showSidebar"
- class="boards-sidebar gl-absolute"
+ class="boards-sidebar"
variant="sidebar"
@close="handleClose"
>
@@ -187,7 +193,7 @@ export default {
:issuable-type="issuableType"
data-testid="sidebar-milestones"
/>
- <iteration-sidebar-dropdown-widget
+ <sidebar-iteration-widget
v-if="iterationFeatureAvailable && !isIncidentSidebar"
:iid="activeBoardItem.iid"
:workspace-path="projectPathForActiveIssue"
@@ -236,6 +242,13 @@ export default {
:issuable-type="issuableType"
@weightUpdated="setActiveItemWeight($event)"
/>
+ <sidebar-health-status-widget
+ v-if="healthStatusFeatureAvailable"
+ :iid="activeBoardItem.iid"
+ :full-path="fullPath"
+ :issuable-type="issuableType"
+ @statusUpdated="setActiveItemHealthStatus($event)"
+ />
<sidebar-confidentiality-widget
:iid="activeBoardItem.iid"
:full-path="fullPath"
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 816b22e4dc6..215691c7ba2 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -133,6 +133,8 @@ export default {
'ghost-class': 'board-card-drag-active',
'data-list-id': this.list.id,
value: this.boardItems,
+ delay: 100,
+ delayOnTouchOnly: true,
};
return this.canMoveIssue ? options : {};
@@ -317,7 +319,7 @@ export default {
>
<!-- TODO: remove the condition when https://gitlab.com/gitlab-org/gitlab/-/issues/377862 is resolved -->
<board-card-move-to-position
- v-if="!isEpicBoard"
+ v-if="!isEpicBoard && !disabled"
:item="item"
:index="index"
:list="list"
diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue
index eaf3facb450..4f90d77c0be 100644
--- a/app/assets/javascripts/boards/components/boards_selector.vue
+++ b/app/assets/javascripts/boards/components/boards_selector.vue
@@ -237,7 +237,7 @@ export default {
:text="board.name"
@show="loadBoards"
>
- <p class="gl-new-dropdown-header-top" @mousedown.prevent>
+ <p class="gl-dropdown-header-top" @mousedown.prevent>
{{ s__('IssueBoards|Switch board') }}
</p>
<gl-search-box-by-type ref="searchBox" v-model="filterTerm" class="m-2" />
diff --git a/app/assets/javascripts/boards/components/issue_board_filtered_search.vue b/app/assets/javascripts/boards/components/issue_board_filtered_search.vue
index 605e11d1590..bc68c2e0e99 100644
--- a/app/assets/javascripts/boards/components/issue_board_filtered_search.vue
+++ b/app/assets/javascripts/boards/components/issue_board_filtered_search.vue
@@ -12,8 +12,8 @@ import { TYPE_USER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { __ } from '~/locale';
import {
- OPERATOR_IS_AND_IS_NOT,
- OPERATOR_IS_ONLY,
+ OPERATORS_IS_NOT,
+ OPERATORS_IS,
TOKEN_TITLE_ASSIGNEE,
TOKEN_TITLE_AUTHOR,
TOKEN_TITLE_CONFIDENTIAL,
@@ -31,7 +31,7 @@ import {
TOKEN_TYPE_RELEASE,
TOKEN_TYPE_TYPE,
} from '~/vue_shared/components/filtered_search_bar/constants';
-import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
+import UserToken from '~/vue_shared/components/filtered_search_bar/tokens/user_token.vue';
import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue';
import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
@@ -60,7 +60,7 @@ export default {
tokensCE() {
const { issue, incident } = this.$options.i18n;
const { types } = this.$options;
- const { fetchAuthors, fetchLabels } = issueBoardFilters(
+ const { fetchUsers, fetchLabels } = issueBoardFilters(
this.$apollo,
this.fullPath,
this.boardType,
@@ -71,28 +71,28 @@ export default {
icon: 'user',
title: TOKEN_TITLE_ASSIGNEE,
type: TOKEN_TYPE_ASSIGNEE,
- operators: OPERATOR_IS_AND_IS_NOT,
- token: AuthorToken,
+ operators: OPERATORS_IS_NOT,
+ token: UserToken,
unique: true,
- fetchAuthors,
- preloadedAuthors: this.preloadedAuthors(),
+ fetchUsers,
+ preloadedUsers: this.preloadedUsers(),
},
{
icon: 'pencil',
title: TOKEN_TITLE_AUTHOR,
type: TOKEN_TYPE_AUTHOR,
- operators: OPERATOR_IS_AND_IS_NOT,
+ operators: OPERATORS_IS_NOT,
symbol: '@',
- token: AuthorToken,
+ token: UserToken,
unique: true,
- fetchAuthors,
- preloadedAuthors: this.preloadedAuthors(),
+ fetchUsers,
+ preloadedUsers: this.preloadedUsers(),
},
{
icon: 'labels',
title: TOKEN_TITLE_LABEL,
type: TOKEN_TYPE_LABEL,
- operators: OPERATOR_IS_AND_IS_NOT,
+ operators: OPERATORS_IS_NOT,
token: LabelToken,
unique: false,
symbol: '~',
@@ -128,7 +128,7 @@ export default {
title: TOKEN_TITLE_CONFIDENTIAL,
unique: true,
token: GlFilteredSearchToken,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
options: [
{ icon: 'eye-slash', value: 'yes', title: __('Yes') },
{ icon: 'eye', value: 'no', title: __('No') },
@@ -186,7 +186,7 @@ export default {
},
methods: {
...mapActions(['fetchMilestones']),
- preloadedAuthors() {
+ preloadedUsers() {
return gon?.current_user_id
? [
{
diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_time_tracker.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_time_tracker.vue
index a35b3f14be4..b70294c9db3 100644
--- a/app/assets/javascripts/boards/components/sidebar/board_sidebar_time_tracker.vue
+++ b/app/assets/javascripts/boards/components/sidebar/board_sidebar_time_tracker.vue
@@ -6,7 +6,7 @@ export default {
components: {
IssuableTimeTracker,
},
- inject: ['timeTrackingLimitToHours'],
+ inject: ['timeTrackingLimitToHours', 'canUpdate'],
computed: {
...mapGetters(['activeBoardItem']),
initialTimeTracking() {
@@ -34,5 +34,6 @@ export default {
:limit-to-hours="timeTrackingLimitToHours"
:initial-time-tracking="initialTimeTracking"
:show-collapsed="false"
+ :can-add-time-entries="canUpdate"
/>
</template>
diff --git a/app/assets/javascripts/boards/issue_board_filters.js b/app/assets/javascripts/boards/issue_board_filters.js
index 699d7e12de4..4bfd92fb748 100644
--- a/app/assets/javascripts/boards/issue_board_filters.js
+++ b/app/assets/javascripts/boards/issue_board_filters.js
@@ -14,13 +14,13 @@ export default function issueBoardFilters(apollo, fullPath, boardType) {
return isGroupBoard ? groupBoardMembers : projectBoardMembers;
};
- const fetchAuthors = (authorsSearchTerm) => {
+ const fetchUsers = (usersSearchTerm) => {
return apollo
.query({
query: boardAssigneesQuery(),
variables: {
fullPath,
- search: authorsSearchTerm,
+ search: usersSearchTerm,
},
})
.then(({ data }) => data.workspace?.assignees.nodes.map(({ user }) => user));
@@ -42,6 +42,6 @@ export default function issueBoardFilters(apollo, fullPath, boardType) {
return {
fetchLabels,
- fetchAuthors,
+ fetchUsers,
};
}
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index e5437690fd4..07b127d86e2 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -928,4 +928,5 @@ export default {
// EE action needs CE empty equivalent
setActiveItemWeight: () => {},
+ setActiveItemHealthStatus: () => {},
};
diff --git a/app/assets/javascripts/branches/components/sort_dropdown.vue b/app/assets/javascripts/branches/components/sort_dropdown.vue
index 5f782b5e652..263efcaa788 100644
--- a/app/assets/javascripts/branches/components/sort_dropdown.vue
+++ b/app/assets/javascripts/branches/components/sort_dropdown.vue
@@ -1,5 +1,5 @@
<script>
-import { GlDropdown, GlDropdownItem, GlSearchBoxByClick } from '@gitlab/ui';
+import { GlCollapsibleListbox, GlSearchBoxByClick } from '@gitlab/ui';
import { mergeUrlParams, visitUrl, getParameterValues } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
@@ -10,8 +10,7 @@ export default {
searchPlaceholder: s__('Branches|Filter by branch name'),
},
components: {
- GlDropdown,
- GlDropdownItem,
+ GlCollapsibleListbox,
GlSearchBoxByClick,
},
inject: ['projectBranchesFilteredPath', 'sortOptions', 'mode'],
@@ -28,6 +27,9 @@ export default {
selectedSortMethodName() {
return this.sortOptions[this.selectedKey];
},
+ listboxItems() {
+ return Object.entries(this.sortOptions).map(([value, text]) => ({ value, text }));
+ },
},
created() {
const sortValue = getParameterValues('sort');
@@ -42,9 +44,6 @@ export default {
}
},
methods: {
- isSortMethodSelected(sortKey) {
- return sortKey === this.selectedKey;
- },
visitUrlFromOption(sortKey) {
this.selectedKey = sortKey;
const urlParams = {};
@@ -70,20 +69,15 @@ export default {
data-testid="branch-search"
@submit="visitUrlFromOption(selectedKey)"
/>
- <gl-dropdown
+
+ <gl-collapsible-listbox
v-if="shouldShowDropdown"
- :text="selectedSortMethodName"
+ v-model="selectedKey"
+ :items="listboxItems"
+ :toggle-text="selectedSortMethodName"
class="gl-mr-3"
data-testid="branches-dropdown"
- >
- <gl-dropdown-item
- v-for="(value, key) in sortOptions"
- :key="key"
- :is-checked="isSortMethodSelected(key)"
- is-check-item
- @click="visitUrlFromOption(key)"
- >{{ value }}</gl-dropdown-item
- >
- </gl-dropdown>
+ @select="visitUrlFromOption(selectedKey)"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/branches/init_new_branch_ref_selector.js b/app/assets/javascripts/branches/init_new_branch_ref_selector.js
new file mode 100644
index 00000000000..aad3fbb9982
--- /dev/null
+++ b/app/assets/javascripts/branches/init_new_branch_ref_selector.js
@@ -0,0 +1,25 @@
+import Vue from 'vue';
+import RefSelector from '~/ref/components/ref_selector.vue';
+
+export default function initNewBranchRefSelector() {
+ const el = document.querySelector('.js-new-branch-ref-selector');
+
+ if (!el) {
+ return false;
+ }
+
+ const { projectId, defaultBranchName, hiddenInputName } = el.dataset;
+
+ return new Vue({
+ el,
+ render(createComponent) {
+ return createComponent(RefSelector, {
+ props: {
+ value: defaultBranchName,
+ name: hiddenInputName,
+ projectId,
+ },
+ });
+ },
+ });
+}
diff --git a/app/assets/javascripts/ci/ci_lint/components/ci_lint.vue b/app/assets/javascripts/ci/ci_lint/components/ci_lint.vue
new file mode 100644
index 00000000000..49a314e067c
--- /dev/null
+++ b/app/assets/javascripts/ci/ci_lint/components/ci_lint.vue
@@ -0,0 +1,131 @@
+<script>
+import { GlButton, GlFormCheckbox, GlIcon, GlLink, GlAlert } from '@gitlab/ui';
+import CiLintResults from '~/ci/pipeline_editor/components/lint/ci_lint_results.vue';
+import lintCiMutation from '~/ci/pipeline_editor/graphql/mutations/client/lint_ci.mutation.graphql';
+import SourceEditor from '~/vue_shared/components/source_editor.vue';
+
+export default {
+ components: {
+ GlButton,
+ GlFormCheckbox,
+ GlIcon,
+ GlLink,
+ GlAlert,
+ CiLintResults,
+ SourceEditor,
+ },
+ props: {
+ endpoint: {
+ type: String,
+ required: true,
+ },
+ lintHelpPagePath: {
+ type: String,
+ required: true,
+ },
+ pipelineSimulationHelpPagePath: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ content: '',
+ loading: false,
+ isValid: false,
+ errors: null,
+ warnings: null,
+ jobs: [],
+ dryRun: false,
+ showingResults: false,
+ apiError: null,
+ isErrorDismissed: false,
+ };
+ },
+ computed: {
+ shouldShowError() {
+ return this.apiError && !this.isErrorDismissed;
+ },
+ },
+ methods: {
+ async lint() {
+ this.loading = true;
+ try {
+ const {
+ data: {
+ lintCI: { valid, errors, warnings, jobs },
+ },
+ } = await this.$apollo.mutate({
+ mutation: lintCiMutation,
+ variables: { endpoint: this.endpoint, content: this.content, dry: this.dryRun },
+ });
+
+ this.showingResults = true;
+ this.isValid = valid;
+ this.errors = errors;
+ this.warnings = warnings;
+ this.jobs = jobs;
+ } catch (error) {
+ this.apiError = error;
+ this.isErrorDismissed = false;
+ } finally {
+ this.loading = false;
+ }
+ },
+ clear() {
+ this.content = '';
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="row">
+ <div class="col-sm-12">
+ <gl-alert
+ v-if="shouldShowError"
+ class="gl-mb-3"
+ variant="danger"
+ @dismiss="isErrorDismissed = true"
+ >{{ apiError }}</gl-alert
+ >
+ <div class="file-holder gl-mb-3">
+ <div class="js-file-title file-title clearfix">
+ {{ __('Contents of .gitlab-ci.yml') }}
+ </div>
+ <source-editor v-model="content" file-name="*.yml" />
+ </div>
+ </div>
+
+ <div class="col-sm-12 gl-display-flex gl-justify-content-space-between">
+ <div class="gl-display-flex gl-align-items-center">
+ <gl-button
+ class="gl-mr-4"
+ :loading="loading"
+ category="primary"
+ variant="confirm"
+ data-testid="ci-lint-validate"
+ @click="lint"
+ >{{ __('Validate') }}</gl-button
+ >
+ <gl-form-checkbox v-model="dryRun"
+ >{{ __('Simulate a pipeline created for the default branch') }}
+ <gl-link :href="pipelineSimulationHelpPagePath" target="_blank"
+ ><gl-icon class="gl-text-blue-600" name="question-o" /></gl-link
+ ></gl-form-checkbox>
+ </div>
+ <gl-button data-testid="ci-lint-clear" @click="clear">{{ __('Clear') }}</gl-button>
+ </div>
+
+ <ci-lint-results
+ v-if="showingResults"
+ class="col-sm-12 gl-mt-5"
+ :is-valid="isValid"
+ :jobs="jobs"
+ :errors="errors"
+ :warnings="warnings"
+ :dry-run="dryRun"
+ :lint-help-page-path="lintHelpPagePath"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/ci/ci_lint/index.js b/app/assets/javascripts/ci/ci_lint/index.js
new file mode 100644
index 00000000000..382059eb17e
--- /dev/null
+++ b/app/assets/javascripts/ci/ci_lint/index.js
@@ -0,0 +1,31 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
+import { resolvers } from '~/ci/pipeline_editor/graphql/resolvers';
+
+import CiLint from './components/ci_lint.vue';
+
+Vue.use(VueApollo);
+
+const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient(resolvers),
+});
+
+export default (containerId = '#js-ci-lint') => {
+ const containerEl = document.querySelector(containerId);
+ const { endpoint, lintHelpPagePath, pipelineSimulationHelpPagePath } = containerEl.dataset;
+
+ return new Vue({
+ el: containerEl,
+ apolloProvider,
+ render(createElement) {
+ return createElement(CiLint, {
+ props: {
+ endpoint,
+ lintHelpPagePath,
+ pipelineSimulationHelpPagePath,
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/pipeline_editor/components/code_snippet_alert/code_snippet_alert.vue b/app/assets/javascripts/ci/pipeline_editor/components/code_snippet_alert/code_snippet_alert.vue
index 7b33d98bca0..7b33d98bca0 100644
--- a/app/assets/javascripts/pipeline_editor/components/code_snippet_alert/code_snippet_alert.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/code_snippet_alert/code_snippet_alert.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/code_snippet_alert/constants.js b/app/assets/javascripts/ci/pipeline_editor/components/code_snippet_alert/constants.js
index e4fd423249b..e4fd423249b 100644
--- a/app/assets/javascripts/pipeline_editor/components/code_snippet_alert/constants.js
+++ b/app/assets/javascripts/ci/pipeline_editor/components/code_snippet_alert/constants.js
diff --git a/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue b/app/assets/javascripts/ci/pipeline_editor/components/commit/commit_form.vue
index 4775836fcc6..4775836fcc6 100644
--- a/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/commit/commit_form.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/commit/commit_section.vue b/app/assets/javascripts/ci/pipeline_editor/components/commit/commit_section.vue
index 9cbf60b1c8f..9cbf60b1c8f 100644
--- a/app/assets/javascripts/pipeline_editor/components/commit/commit_section.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/commit/commit_section.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/drawer/cards/first_pipeline_card.vue b/app/assets/javascripts/ci/pipeline_editor/components/drawer/cards/first_pipeline_card.vue
index 0b57433e894..0b57433e894 100644
--- a/app/assets/javascripts/pipeline_editor/components/drawer/cards/first_pipeline_card.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/drawer/cards/first_pipeline_card.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/drawer/cards/getting_started_card.vue b/app/assets/javascripts/ci/pipeline_editor/components/drawer/cards/getting_started_card.vue
index d2682cf6326..d2682cf6326 100644
--- a/app/assets/javascripts/pipeline_editor/components/drawer/cards/getting_started_card.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/drawer/cards/getting_started_card.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/drawer/cards/pipeline_config_reference_card.vue b/app/assets/javascripts/ci/pipeline_editor/components/drawer/cards/pipeline_config_reference_card.vue
index bc9203b9c5b..bc9203b9c5b 100644
--- a/app/assets/javascripts/pipeline_editor/components/drawer/cards/pipeline_config_reference_card.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/drawer/cards/pipeline_config_reference_card.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/drawer/cards/visualize_and_lint_card.vue b/app/assets/javascripts/ci/pipeline_editor/components/drawer/cards/visualize_and_lint_card.vue
index aeeb52319d2..aeeb52319d2 100644
--- a/app/assets/javascripts/pipeline_editor/components/drawer/cards/visualize_and_lint_card.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/drawer/cards/visualize_and_lint_card.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/drawer/pipeline_editor_drawer.vue b/app/assets/javascripts/ci/pipeline_editor/components/drawer/pipeline_editor_drawer.vue
index 375db7f3054..375db7f3054 100644
--- a/app/assets/javascripts/pipeline_editor/components/drawer/pipeline_editor_drawer.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/drawer/pipeline_editor_drawer.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/drawer/ui/demo_job_pill.vue b/app/assets/javascripts/ci/pipeline_editor/components/drawer/ui/demo_job_pill.vue
index 049504181c4..049504181c4 100644
--- a/app/assets/javascripts/pipeline_editor/components/drawer/ui/demo_job_pill.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/drawer/ui/demo_job_pill.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue b/app/assets/javascripts/ci/pipeline_editor/components/editor/ci_config_merged_preview.vue
index 42e2d34fa3a..42e2d34fa3a 100644
--- a/app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/editor/ci_config_merged_preview.vue
diff --git a/app/assets/javascripts/ci/pipeline_editor/components/editor/ci_editor_header.vue b/app/assets/javascripts/ci/pipeline_editor/components/editor/ci_editor_header.vue
new file mode 100644
index 00000000000..201fba837e2
--- /dev/null
+++ b/app/assets/javascripts/ci/pipeline_editor/components/editor/ci_editor_header.vue
@@ -0,0 +1,68 @@
+<script>
+import { GlButton } from '@gitlab/ui';
+import { __ } from '~/locale';
+import Tracking from '~/tracking';
+import { pipelineEditorTrackingOptions, TEMPLATE_REPOSITORY_URL } from '../../constants';
+
+export default {
+ i18n: {
+ browseTemplates: __('Browse templates'),
+ help: __('Help'),
+ },
+ TEMPLATE_REPOSITORY_URL,
+ components: {
+ GlButton,
+ },
+ mixins: [Tracking.mixin()],
+ props: {
+ showDrawer: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ methods: {
+ toggleDrawer() {
+ if (this.showDrawer) {
+ this.$emit('close-drawer');
+ } else {
+ this.$emit('open-drawer');
+ this.trackHelpDrawerClick();
+ }
+ },
+ trackHelpDrawerClick() {
+ const { label, actions } = pipelineEditorTrackingOptions;
+ this.track(actions.openHelpDrawer, { label });
+ },
+ trackTemplateBrowsing() {
+ const { label, actions } = pipelineEditorTrackingOptions;
+
+ this.track(actions.browseTemplates, { label });
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="gl-display-flex gl-p-3 gl-gap-3 gl-border-solid gl-border-gray-100 gl-border-1">
+ <gl-button
+ :href="$options.TEMPLATE_REPOSITORY_URL"
+ size="small"
+ icon="external-link"
+ target="_blank"
+ data-testid="template-repo-link"
+ data-qa-selector="template_repo_link"
+ @click="trackTemplateBrowsing"
+ >
+ {{ $options.i18n.browseTemplates }}
+ </gl-button>
+ <gl-button
+ icon="information-o"
+ size="small"
+ data-testid="drawer-toggle"
+ data-qa-selector="drawer_toggle"
+ @click="toggleDrawer"
+ >
+ {{ $options.i18n.help }}
+ </gl-button>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/editor/text_editor.vue b/app/assets/javascripts/ci/pipeline_editor/components/editor/text_editor.vue
index 255e3cb31f1..255e3cb31f1 100644
--- a/app/assets/javascripts/pipeline_editor/components/editor/text_editor.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/editor/text_editor.vue
diff --git a/app/assets/javascripts/ci/pipeline_editor/components/file_nav/branch_switcher.vue b/app/assets/javascripts/ci/pipeline_editor/components/file_nav/branch_switcher.vue
new file mode 100644
index 00000000000..ef9acc1f8f1
--- /dev/null
+++ b/app/assets/javascripts/ci/pipeline_editor/components/file_nav/branch_switcher.vue
@@ -0,0 +1,254 @@
+<script>
+import {
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownSectionHeader,
+ GlInfiniteScroll,
+ GlLoadingIcon,
+ GlSearchBoxByType,
+ GlTooltipDirective,
+} from '@gitlab/ui';
+import { produce } from 'immer';
+import { historyPushState } from '~/lib/utils/common_utils';
+import { setUrlParams } from '~/lib/utils/url_utility';
+import { __ } from '~/locale';
+import {
+ BRANCH_PAGINATION_LIMIT,
+ BRANCH_SEARCH_DEBOUNCE,
+ DEFAULT_FAILURE,
+} from '~/ci/pipeline_editor/constants';
+import updateCurrentBranchMutation from '~/ci/pipeline_editor/graphql/mutations/client/update_current_branch.mutation.graphql';
+import getAvailableBranchesQuery from '~/ci/pipeline_editor/graphql/queries/available_branches.query.graphql';
+import getCurrentBranch from '~/ci/pipeline_editor/graphql/queries/client/current_branch.query.graphql';
+import getLastCommitBranch from '~/ci/pipeline_editor/graphql/queries/client/last_commit_branch.query.graphql';
+
+export default {
+ i18n: {
+ dropdownHeader: __('Switch branch'),
+ title: __('Branches'),
+ fetchError: __('Unable to fetch branch list for this project.'),
+ },
+ inputDebounce: BRANCH_SEARCH_DEBOUNCE,
+ components: {
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownSectionHeader,
+ GlInfiniteScroll,
+ GlLoadingIcon,
+ GlSearchBoxByType,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ inject: ['projectFullPath', 'totalBranches'],
+ props: {
+ hasUnsavedChanges: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ paginationLimit: {
+ type: Number,
+ required: false,
+ default: BRANCH_PAGINATION_LIMIT,
+ },
+ shouldLoadNewBranch: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ availableBranches: [],
+ branchSelected: null,
+ pageLimit: this.paginationLimit,
+ pageCounter: 0,
+ searchTerm: '',
+ lastCommitBranch: '',
+ };
+ },
+ apollo: {
+ availableBranches: {
+ query: getAvailableBranchesQuery,
+ variables() {
+ return {
+ offset: 0,
+ projectFullPath: this.projectFullPath,
+ ...this.availableBranchesVariables,
+ };
+ },
+ update(data) {
+ return data.project?.repository?.branchNames || [];
+ },
+ result() {
+ this.pageCounter += 1;
+ },
+ error() {
+ this.showFetchError();
+ },
+ },
+ currentBranch: {
+ query: getCurrentBranch,
+ update(data) {
+ return data.workBranches.current.name;
+ },
+ },
+ lastCommitBranch: {
+ query: getLastCommitBranch,
+ update(data) {
+ return data.workBranches.lastCommit.name;
+ },
+ result({ data }) {
+ if (data) {
+ const { name: lastCommitBranch } = data.workBranches.lastCommit;
+ if (lastCommitBranch === '' || this.availableBranches.includes(lastCommitBranch)) {
+ return;
+ }
+
+ this.availableBranches.unshift(lastCommitBranch);
+ }
+ },
+ },
+ },
+ computed: {
+ availableBranchesVariables() {
+ if (this.searchTerm.length > 0) {
+ return {
+ limit: this.totalBranches,
+ searchPattern: `*${this.searchTerm}*`,
+ };
+ }
+
+ return {
+ limit: this.paginationLimit,
+ searchPattern: '*',
+ };
+ },
+ enableBranchSwitcher() {
+ return this.availableBranches.length > 0 || this.searchTerm.length > 0;
+ },
+ isBranchesLoading() {
+ return this.$apollo.queries.availableBranches.loading;
+ },
+ },
+ watch: {
+ shouldLoadNewBranch(flag) {
+ if (flag) {
+ this.changeBranch(this.branchSelected);
+ }
+ },
+ },
+ methods: {
+ // if there is no searchPattern, paginate by {paginationLimit} branches
+ fetchNextBranches() {
+ if (
+ this.isBranchesLoading ||
+ this.searchTerm.length > 0 ||
+ this.availableBranches.length >= this.totalBranches
+ ) {
+ return;
+ }
+
+ this.$apollo.queries.availableBranches
+ .fetchMore({
+ variables: {
+ offset: this.pageCounter * this.paginationLimit,
+ },
+ updateQuery(previousResult, { fetchMoreResult }) {
+ const previousBranches = previousResult.project.repository.branchNames;
+ const newBranches = fetchMoreResult.project.repository.branchNames;
+
+ return produce(fetchMoreResult, (draftData) => {
+ draftData.project.repository.branchNames = previousBranches.concat(newBranches);
+ });
+ },
+ })
+ .catch(this.showFetchError);
+ },
+ async changeBranch(newBranch) {
+ this.updateCurrentBranch(newBranch);
+ const updatedPath = setUrlParams({ branch_name: newBranch });
+ historyPushState(updatedPath);
+
+ // refetching the content will cause a lot of components to re-render,
+ // including the text editor which uses the commit sha to register the CI schema
+ // so we need to make sure the currentBranch (and consequently, the commitSha) are updated first
+ await this.$nextTick();
+ this.$emit('refetchContent');
+ },
+ selectBranch(newBranch) {
+ if (newBranch !== this.currentBranch) {
+ // If there are unsaved changes, we want to show the user
+ // a modal to confirm what to do with these before changing
+ // branches.
+ if (this.hasUnsavedChanges) {
+ this.branchSelected = newBranch;
+ this.$emit('select-branch', newBranch);
+ } else {
+ this.changeBranch(newBranch);
+ }
+ }
+ },
+ async setSearchTerm(newSearchTerm) {
+ this.pageCounter = 0;
+ this.searchTerm = newSearchTerm.trim();
+ },
+ showFetchError() {
+ this.$emit('showError', {
+ type: DEFAULT_FAILURE,
+ reasons: [this.$options.i18n.fetchError],
+ });
+ },
+ updateCurrentBranch(currentBranch) {
+ this.$apollo.mutate({
+ mutation: updateCurrentBranchMutation,
+ variables: { currentBranch },
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-dropdown
+ v-gl-tooltip.hover
+ :title="$options.i18n.dropdownHeader"
+ :header-text="$options.i18n.dropdownHeader"
+ :text="currentBranch"
+ :disabled="!enableBranchSwitcher"
+ icon="branch"
+ data-qa-selector="branch_selector_button"
+ data-testid="branch-selector"
+ >
+ <gl-search-box-by-type :debounce="$options.inputDebounce" @input="setSearchTerm" />
+ <gl-dropdown-section-header>
+ {{ $options.i18n.title }}
+ </gl-dropdown-section-header>
+
+ <gl-infinite-scroll
+ :fetched-items="availableBranches.length"
+ :max-list-height="250"
+ data-qa-selector="branch_menu_container"
+ @bottomReached="fetchNextBranches"
+ >
+ <template #items>
+ <gl-dropdown-item
+ v-for="branch in availableBranches"
+ :key="branch"
+ :is-checked="currentBranch === branch"
+ is-check-item
+ data-qa-selector="branch_menu_item_button"
+ @click="selectBranch(branch)"
+ >
+ {{ branch }}
+ </gl-dropdown-item>
+ </template>
+ <template #default>
+ <gl-dropdown-item v-if="isBranchesLoading" key="loading">
+ <gl-loading-icon size="lg" />
+ </gl-dropdown-item>
+ </template>
+ </gl-infinite-scroll>
+ </gl-dropdown>
+</template>
diff --git a/app/assets/javascripts/ci/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue b/app/assets/javascripts/ci/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue
new file mode 100644
index 00000000000..84c29e48114
--- /dev/null
+++ b/app/assets/javascripts/ci/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue
@@ -0,0 +1,72 @@
+<script>
+import { GlButton } from '@gitlab/ui';
+import getAppStatus from '~/ci/pipeline_editor/graphql/queries/client/app_status.query.graphql';
+import { EDITOR_APP_STATUS_EMPTY, EDITOR_APP_STATUS_LOADING } from '../../constants';
+import FileTreePopover from '../popovers/file_tree_popover.vue';
+import BranchSwitcher from './branch_switcher.vue';
+
+export default {
+ components: {
+ BranchSwitcher,
+ FileTreePopover,
+ GlButton,
+ },
+ props: {
+ hasUnsavedChanges: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ isNewCiConfigFile: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ shouldLoadNewBranch: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ apollo: {
+ appStatus: {
+ query: getAppStatus,
+ update(data) {
+ return data.app.status;
+ },
+ },
+ },
+ computed: {
+ isAppLoading() {
+ return this.appStatus === EDITOR_APP_STATUS_LOADING;
+ },
+ showFileTreeToggle() {
+ return !this.isNewCiConfigFile && this.appStatus !== EDITOR_APP_STATUS_EMPTY;
+ },
+ },
+ methods: {
+ onFileTreeBtnClick() {
+ this.$emit('toggle-file-tree');
+ },
+ },
+};
+</script>
+<template>
+ <div class="gl-mb-4">
+ <gl-button
+ v-if="showFileTreeToggle"
+ id="file-tree-toggle"
+ icon="file-tree"
+ data-testid="file-tree-toggle"
+ :aria-label="__('File Tree')"
+ :loading="isAppLoading"
+ @click="onFileTreeBtnClick"
+ />
+ <file-tree-popover v-if="showFileTreeToggle" />
+ <branch-switcher
+ :has-unsaved-changes="hasUnsavedChanges"
+ :should-load-new-branch="shouldLoadNewBranch"
+ v-on="$listeners"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/file_tree/container.vue b/app/assets/javascripts/ci/pipeline_editor/components/file_tree/container.vue
index 280cd729a43..280cd729a43 100644
--- a/app/assets/javascripts/pipeline_editor/components/file_tree/container.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/file_tree/container.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/file_tree/file_item.vue b/app/assets/javascripts/ci/pipeline_editor/components/file_tree/file_item.vue
index 786d483b5b9..786d483b5b9 100644
--- a/app/assets/javascripts/pipeline_editor/components/file_tree/file_item.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/file_tree/file_item.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_header.vue b/app/assets/javascripts/ci/pipeline_editor/components/header/pipeline_editor_header.vue
index ec6ee52b6b2..ec6ee52b6b2 100644
--- a/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_header.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/header/pipeline_editor_header.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue b/app/assets/javascripts/ci/pipeline_editor/components/header/pipeline_editor_mini_graph.vue
index feadc60a22a..feadc60a22a 100644
--- a/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/header/pipeline_editor_mini_graph.vue
diff --git a/app/assets/javascripts/ci/pipeline_editor/components/header/pipeline_status.vue b/app/assets/javascripts/ci/pipeline_editor/components/header/pipeline_status.vue
new file mode 100644
index 00000000000..372f04075ab
--- /dev/null
+++ b/app/assets/javascripts/ci/pipeline_editor/components/header/pipeline_status.vue
@@ -0,0 +1,188 @@
+<script>
+import { GlButton, GlIcon, GlLink, GlLoadingIcon, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { truncateSha } from '~/lib/utils/text_utility';
+import { s__ } from '~/locale';
+import getPipelineQuery from '~/ci/pipeline_editor/graphql/queries/pipeline.query.graphql';
+import getPipelineEtag from '~/ci/pipeline_editor/graphql/queries/client/pipeline_etag.query.graphql';
+import {
+ getQueryHeaders,
+ toggleQueryPollingByVisibility,
+} from '~/pipelines/components/graph/utils';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import PipelineEditorMiniGraph from './pipeline_editor_mini_graph.vue';
+
+const POLL_INTERVAL = 10000;
+export const i18n = {
+ fetchError: s__('Pipeline|We are currently unable to fetch pipeline data'),
+ fetchLoading: s__('Pipeline|Checking pipeline status'),
+ pipelineInfo: s__(
+ `Pipeline|Pipeline %{idStart}#%{idEnd} %{statusStart}%{statusEnd} for %{commitStart}%{commitEnd}`,
+ ),
+ viewBtn: s__('Pipeline|View pipeline'),
+ viewCommit: s__('Pipeline|View commit'),
+};
+
+export default {
+ i18n,
+ components: {
+ CiIcon,
+ GlButton,
+ GlIcon,
+ GlLink,
+ GlLoadingIcon,
+ GlSprintf,
+ PipelineEditorMiniGraph,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ inject: ['projectFullPath'],
+ props: {
+ commitSha: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ apollo: {
+ pipelineEtag: {
+ query: getPipelineEtag,
+ update(data) {
+ return data.etags?.pipeline;
+ },
+ },
+ pipeline: {
+ context() {
+ return getQueryHeaders(this.pipelineEtag);
+ },
+ query: getPipelineQuery,
+ variables() {
+ return {
+ fullPath: this.projectFullPath,
+ sha: this.commitSha,
+ };
+ },
+ update(data) {
+ const { id, iid, commit = {}, detailedStatus = {}, stages, status } =
+ data.project?.pipeline || {};
+
+ return {
+ id,
+ iid,
+ commit,
+ detailedStatus,
+ stages,
+ status,
+ };
+ },
+ result(res) {
+ if (res.data?.project?.pipeline) {
+ this.hasError = false;
+ }
+ },
+ error() {
+ this.hasError = true;
+ },
+ pollInterval: POLL_INTERVAL,
+ },
+ },
+ data() {
+ return {
+ hasError: false,
+ };
+ },
+ computed: {
+ commitText() {
+ const shortSha = truncateSha(this.commitSha);
+ const commitTitle = this.pipeline.commit.title || '';
+
+ if (commitTitle.length > 0) {
+ return `${shortSha}: ${commitTitle}`;
+ }
+
+ return shortSha;
+ },
+ hasPipelineData() {
+ return Boolean(this.pipeline?.id);
+ },
+ pipelineId() {
+ return getIdFromGraphQLId(this.pipeline.id);
+ },
+ showLoadingState() {
+ // the query is set to poll regularly, so if there is no pipeline data
+ // (e.g. pipeline is null during fetch when the pipeline hasn't been
+ // triggered yet), we can just show the loading state until the pipeline
+ // details are ready to be fetched
+ return (
+ this.$apollo.queries.pipeline.loading ||
+ this.commitSha.length === 0 ||
+ (!this.hasPipelineData && !this.hasError)
+ );
+ },
+ shortSha() {
+ return truncateSha(this.commitSha);
+ },
+ status() {
+ return this.pipeline.detailedStatus;
+ },
+ },
+ mounted() {
+ toggleQueryPollingByVisibility(this.$apollo.queries.pipeline, POLL_INTERVAL);
+ },
+};
+</script>
+
+<template>
+ <div class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-flex-wrap">
+ <template v-if="showLoadingState">
+ <div>
+ <gl-loading-icon class="gl-mr-auto gl-display-inline-block" size="sm" />
+ <span data-testid="pipeline-loading-msg">{{ $options.i18n.fetchLoading }}</span>
+ </div>
+ </template>
+ <template v-else-if="hasError">
+ <gl-icon class="gl-mr-auto" name="warning-solid" />
+ <span data-testid="pipeline-error-msg">{{ $options.i18n.fetchError }}</span>
+ </template>
+ <template v-else>
+ <div class="gl-text-truncate gl-md-max-w-50p gl-mr-1">
+ <a :href="status.detailsPath" class="gl-mr-auto">
+ <ci-icon :status="status" :size="16" data-testid="pipeline-status-icon" />
+ </a>
+ <span class="gl-font-weight-bold">
+ <gl-sprintf :message="$options.i18n.pipelineInfo">
+ <template #id="{ content }">
+ <span data-testid="pipeline-id" data-qa-selector="pipeline_id_content">
+ {{ content }}{{ pipelineId }}
+ </span>
+ </template>
+ <template #status>{{ status.text }}</template>
+ <template #commit>
+ <gl-link
+ v-gl-tooltip.hover
+ :href="pipeline.commit.webPath"
+ :title="$options.i18n.viewCommit"
+ data-testid="pipeline-commit"
+ >
+ {{ commitText }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </span>
+ </div>
+ <div class="gl-display-flex gl-flex-wrap">
+ <pipeline-editor-mini-graph :pipeline="pipeline" v-on="$listeners" />
+ <gl-button
+ class="gl-ml-3"
+ category="secondary"
+ variant="confirm"
+ :href="status.detailsPath"
+ data-testid="pipeline-view-btn"
+ >
+ {{ $options.i18n.viewBtn }}
+ </gl-button>
+ </div>
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ci/pipeline_editor/components/header/validation_segment.vue b/app/assets/javascripts/ci/pipeline_editor/components/header/validation_segment.vue
new file mode 100644
index 00000000000..84c0eef441f
--- /dev/null
+++ b/app/assets/javascripts/ci/pipeline_editor/components/header/validation_segment.vue
@@ -0,0 +1,126 @@
+<script>
+import { GlIcon, GlLink, GlLoadingIcon } from '@gitlab/ui';
+import { __, s__, sprintf } from '~/locale';
+import getAppStatus from '~/ci/pipeline_editor/graphql/queries/client/app_status.query.graphql';
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
+import {
+ EDITOR_APP_STATUS_EMPTY,
+ EDITOR_APP_STATUS_LINT_UNAVAILABLE,
+ EDITOR_APP_STATUS_LOADING,
+ EDITOR_APP_STATUS_VALID,
+} from '../../constants';
+
+export const i18n = {
+ empty: __(
+ "We'll continuously validate your pipeline configuration. The validation results will appear here.",
+ ),
+ learnMore: __('Learn more'),
+ loading: s__('Pipelines|Validating GitLab CI configuration…'),
+ invalid: s__('Pipelines|This GitLab CI configuration is invalid.'),
+ invalidWithReason: s__('Pipelines|This GitLab CI configuration is invalid: %{reason}.'),
+ unavailableValidation: s__('Pipelines|Configuration validation currently not available.'),
+ valid: s__('Pipelines|Pipeline syntax is correct.'),
+};
+
+export default {
+ i18n,
+ components: {
+ GlIcon,
+ GlLink,
+ GlLoadingIcon,
+ TooltipOnTruncate,
+ },
+ inject: {
+ lintUnavailableHelpPagePath: {
+ default: '',
+ },
+ ymlHelpPagePath: {
+ default: '',
+ },
+ },
+ props: {
+ ciConfig: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ },
+ apollo: {
+ appStatus: {
+ query: getAppStatus,
+ update(data) {
+ return data.app.status;
+ },
+ },
+ },
+ computed: {
+ helpPath() {
+ return this.isLintUnavailable ? this.lintUnavailableHelpPagePath : this.ymlHelpPagePath;
+ },
+ isEmpty() {
+ return this.appStatus === EDITOR_APP_STATUS_EMPTY;
+ },
+ isLintUnavailable() {
+ return this.appStatus === EDITOR_APP_STATUS_LINT_UNAVAILABLE;
+ },
+ isLoading() {
+ return this.appStatus === EDITOR_APP_STATUS_LOADING;
+ },
+ isValid() {
+ return this.appStatus === EDITOR_APP_STATUS_VALID;
+ },
+ icon() {
+ switch (this.appStatus) {
+ case EDITOR_APP_STATUS_EMPTY:
+ return 'check';
+ case EDITOR_APP_STATUS_LINT_UNAVAILABLE:
+ return 'time-out';
+ case EDITOR_APP_STATUS_VALID:
+ return 'check';
+ default:
+ return 'warning-solid';
+ }
+ },
+ message() {
+ const [reason] = this.ciConfig?.errors || [];
+
+ switch (this.appStatus) {
+ case EDITOR_APP_STATUS_EMPTY:
+ return this.$options.i18n.empty;
+ case EDITOR_APP_STATUS_LINT_UNAVAILABLE:
+ return this.$options.i18n.unavailableValidation;
+ case EDITOR_APP_STATUS_VALID:
+ return this.$options.i18n.valid;
+ default:
+ // Only display first error as a reason
+ return this.ciConfig?.errors?.length > 0
+ ? sprintf(this.$options.i18n.invalidWithReason, { reason }, false)
+ : this.$options.i18n.invalid;
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <template v-if="isLoading">
+ <gl-loading-icon size="sm" inline />
+ {{ $options.i18n.loading }}
+ </template>
+
+ <span v-else class="gl-display-inline-flex gl-white-space-nowrap gl-max-w-full">
+ <tooltip-on-truncate :title="message" class="gl-text-truncate">
+ <gl-icon :name="icon" />
+ <span data-qa-selector="validation_message_content" data-testid="validationMsg">
+ {{ message }}
+ </span>
+ </tooltip-on-truncate>
+ <span v-if="!isEmpty" class="gl-flex-shrink-0 gl-pl-2">
+ <gl-link data-testid="learnMoreLink" :href="helpPath">
+ {{ $options.i18n.learnMore }}
+ </gl-link>
+ </span>
+ </span>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results.vue b/app/assets/javascripts/ci/pipeline_editor/components/lint/ci_lint_results.vue
index 0f19b9386e6..0f19b9386e6 100644
--- a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/lint/ci_lint_results.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_param.vue b/app/assets/javascripts/ci/pipeline_editor/components/lint/ci_lint_results_param.vue
index 49225a7cac7..49225a7cac7 100644
--- a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_param.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/lint/ci_lint_results_param.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_value.vue b/app/assets/javascripts/ci/pipeline_editor/components/lint/ci_lint_results_value.vue
index ef2be2a5fba..ef2be2a5fba 100644
--- a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_value.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/lint/ci_lint_results_value.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_warnings.vue b/app/assets/javascripts/ci/pipeline_editor/components/lint/ci_lint_warnings.vue
index ac0332cb0bd..ac0332cb0bd 100644
--- a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_warnings.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/lint/ci_lint_warnings.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue b/app/assets/javascripts/ci/pipeline_editor/components/pipeline_editor_tabs.vue
index ed5466ff99c..ed5466ff99c 100644
--- a/app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/pipeline_editor_tabs.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/popovers/file_tree_popover.vue b/app/assets/javascripts/ci/pipeline_editor/components/popovers/file_tree_popover.vue
index efa6a54c638..efa6a54c638 100644
--- a/app/assets/javascripts/pipeline_editor/components/popovers/file_tree_popover.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/popovers/file_tree_popover.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/popovers/validate_pipeline_popover.vue b/app/assets/javascripts/ci/pipeline_editor/components/popovers/validate_pipeline_popover.vue
index 4730a521227..4730a521227 100644
--- a/app/assets/javascripts/pipeline_editor/components/popovers/validate_pipeline_popover.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/popovers/validate_pipeline_popover.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/popovers/walkthrough_popover.vue b/app/assets/javascripts/ci/pipeline_editor/components/popovers/walkthrough_popover.vue
index c636d8b8e34..c636d8b8e34 100644
--- a/app/assets/javascripts/pipeline_editor/components/popovers/walkthrough_popover.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/popovers/walkthrough_popover.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/ui/confirm_unsaved_changes_dialog.vue b/app/assets/javascripts/ci/pipeline_editor/components/ui/confirm_unsaved_changes_dialog.vue
index bc076fbe349..bc076fbe349 100644
--- a/app/assets/javascripts/pipeline_editor/components/ui/confirm_unsaved_changes_dialog.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/ui/confirm_unsaved_changes_dialog.vue
diff --git a/app/assets/javascripts/ci/pipeline_editor/components/ui/editor_tab.vue b/app/assets/javascripts/ci/pipeline_editor/components/ui/editor_tab.vue
new file mode 100644
index 00000000000..22b82f2e96f
--- /dev/null
+++ b/app/assets/javascripts/ci/pipeline_editor/components/ui/editor_tab.vue
@@ -0,0 +1,156 @@
+<script>
+import { GlAlert, GlBadge, GlTab } from '@gitlab/ui';
+import { __, s__ } from '~/locale';
+/**
+ * Wrapper of <gl-tab> to optionally lazily render this tab's content
+ * when its shown **without dismounting after its hidden**.
+ *
+ * Usage:
+ *
+ * API is the same as <gl-tab>, for example:
+ *
+ * <gl-tabs>
+ * <editor-tab title="Tab 1" lazy>
+ * lazily mounted content (gets mounted if this is first tab)
+ * </editor-tab>
+ * <editor-tab title="Tab 2" lazy>
+ * lazily mounted content
+ * </editor-tab>
+ * <editor-tab title="Tab 3">
+ * eagerly mounted content
+ * </editor-tab>
+ * </gl-tabs>
+ *
+ * Once the tab is selected it is permanently set as "not-lazy"
+ * so it's contents are not dismounted.
+ *
+ * lazy is "false" by default, as in <gl-tab>.
+ *
+ * It is also possible to pass the `isEmpty` and or `isInvalid` to let
+ * the tab component handle that state on its own. For example:
+ *
+ * * <gl-tabs>
+ * <editor-tab-with-status title="Tab 1" :is-empty="isEmpty" :is-invalid="isInvalid">
+ * ...
+ * </editor-tab-with-status>
+ * Will be the same as normal, except it will only render the slot component
+ * if the status is not empty and not invalid. In any of these 2 cases, it will render
+ * a generic component and avoid mounting whatever it received in the slot.
+ * </gl-tabs>
+ */
+
+export default {
+ i18n: {
+ invalid: __(
+ 'Your CI/CD configuration syntax is invalid. Select the Validate tab for more details.',
+ ),
+ unavailable: __(
+ "We're experiencing difficulties and this tab content is currently unavailable.",
+ ),
+ },
+ components: {
+ GlAlert,
+ GlBadge,
+ GlTab,
+ // Use a small renderless component to know when the tab content mounts because:
+ // - gl-tab always gets mounted, even if lazy is `true`. See:
+ // https://github.com/bootstrap-vue/bootstrap-vue/blob/dev/src/components/tabs/tab.js#L180
+ // - we cannot listen to events on <slot />
+ MountSpy: {
+ render: () => null,
+ },
+ },
+ inheritAttrs: false,
+ props: {
+ badgeTitle: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ badgeVariant: {
+ type: String,
+ required: false,
+ default: 'info',
+ },
+ emptyMessage: {
+ type: String,
+ required: false,
+ default: s__(
+ 'PipelineEditor|This tab will be usable when the CI/CD configuration file is populated with valid syntax.',
+ ),
+ },
+ isEmpty: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ isInvalid: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ isUnavailable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ keepComponentMounted: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ lazy: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ title: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ isLazy: this.lazy,
+ };
+ },
+ computed: {
+ hasBadgeTitle() {
+ return this.badgeTitle.length > 0;
+ },
+ slots() {
+ // eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
+ return Object.keys(this.$slots);
+ },
+ },
+ methods: {
+ onContentMounted() {
+ // When a child is first mounted make the entire tab
+ // permanently mounted by setting 'lazy' to false unless
+ // explicitly opted out.
+ if (this.keepComponentMounted) {
+ this.isLazy = false;
+ }
+ },
+ },
+};
+</script>
+<template>
+ <gl-tab :lazy="isLazy" v-bind="$attrs" v-on="$listeners">
+ <template #title>
+ <span>{{ title }}</span>
+ <gl-badge v-if="hasBadgeTitle" class="gl-ml-2" size="sm" :variant="badgeVariant">{{
+ badgeTitle
+ }}</gl-badge>
+ </template>
+ <gl-alert v-if="isEmpty" variant="tip">{{ emptyMessage }}</gl-alert>
+ <gl-alert v-else-if="isUnavailable" variant="danger" :dismissible="false">
+ {{ $options.i18n.unavailable }}</gl-alert
+ >
+ <gl-alert v-else-if="isInvalid" variant="danger">{{ $options.i18n.invalid }}</gl-alert>
+ <template v-else>
+ <slot v-for="slot in slots" :name="slot"></slot>
+ <mount-spy @hook:mounted="onContentMounted" />
+ </template>
+ </gl-tab>
+</template>
diff --git a/app/assets/javascripts/ci/pipeline_editor/components/ui/pipeline_editor_empty_state.vue b/app/assets/javascripts/ci/pipeline_editor/components/ui/pipeline_editor_empty_state.vue
new file mode 100644
index 00000000000..d7b8e7151d9
--- /dev/null
+++ b/app/assets/javascripts/ci/pipeline_editor/components/ui/pipeline_editor_empty_state.vue
@@ -0,0 +1,72 @@
+<script>
+import { GlButton, GlSprintf } from '@gitlab/ui';
+import { __ } from '~/locale';
+import PipelineEditorFileNav from '~/ci/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue';
+
+export default {
+ components: {
+ GlButton,
+ GlSprintf,
+ PipelineEditorFileNav,
+ },
+ i18n: {
+ title: __('Optimize your workflow with CI/CD Pipelines'),
+ body: __(
+ 'Create a new %{codeStart}.gitlab-ci.yml%{codeEnd} file at the root of the repository to get started.',
+ ),
+ btnText: __('Configure pipeline'),
+ externalCiNote: __("This project's pipeline configuration is located outside this repository"),
+ externalCiInstructions: __(
+ 'To edit the pipeline configuration, you must go to the project or external site that hosts the file.',
+ ),
+ },
+ inject: {
+ emptyStateIllustrationPath: {
+ default: '',
+ },
+ usesExternalConfig: {
+ default: false,
+ type: Boolean,
+ required: false,
+ },
+ },
+ methods: {
+ createEmptyConfigFile() {
+ this.$emit('createEmptyConfigFile');
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <pipeline-editor-file-nav v-on="$listeners" />
+ <div class="gl-display-flex gl-flex-direction-column gl-align-items-center gl-mt-11">
+ <img :src="emptyStateIllustrationPath" />
+ <div
+ v-if="usesExternalConfig"
+ class="gl-display-flex gl-flex-direction-column gl-align-items-center"
+ >
+ <h1 class="gl-font-size-h1">{{ $options.i18n.externalCiNote }}</h1>
+ <p class="gl-mt-3">{{ $options.i18n.externalCiInstructions }}</p>
+ </div>
+ <div v-else class="gl-display-flex gl-flex-direction-column gl-align-items-center">
+ <h1 class="gl-font-size-h1">{{ $options.i18n.title }}</h1>
+ <p class="gl-mt-3">
+ <gl-sprintf :message="$options.i18n.body">
+ <template #code="{ content }">
+ <code>{{ content }}</code>
+ </template>
+ </gl-sprintf>
+ </p>
+ <gl-button
+ variant="confirm"
+ class="gl-mt-3"
+ data-qa-selector="create_new_ci_button"
+ @click="createEmptyConfigFile"
+ >
+ {{ $options.i18n.btnText }}
+ </gl-button>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_messages.vue b/app/assets/javascripts/ci/pipeline_editor/components/ui/pipeline_editor_messages.vue
index c72cff4c6f8..c72cff4c6f8 100644
--- a/app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_messages.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/ui/pipeline_editor_messages.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/validate/ci_validate.vue b/app/assets/javascripts/ci/pipeline_editor/components/validate/ci_validate.vue
index 83fcab4b343..83fcab4b343 100644
--- a/app/assets/javascripts/pipeline_editor/components/validate/ci_validate.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/validate/ci_validate.vue
diff --git a/app/assets/javascripts/pipeline_editor/constants.js b/app/assets/javascripts/ci/pipeline_editor/constants.js
index dd25c4d433b..dd25c4d433b 100644
--- a/app/assets/javascripts/pipeline_editor/constants.js
+++ b/app/assets/javascripts/ci/pipeline_editor/constants.js
diff --git a/app/assets/javascripts/pipeline_editor/graphql/mutations/client/lint_ci.mutation.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/mutations/client/lint_ci.mutation.graphql
index 2d42ebb6ac3..2d42ebb6ac3 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/mutations/client/lint_ci.mutation.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/mutations/client/lint_ci.mutation.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/mutations/client/update_app_status.mutation.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/mutations/client/update_app_status.mutation.graphql
index 7487e328668..7487e328668 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/mutations/client/update_app_status.mutation.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/mutations/client/update_app_status.mutation.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/mutations/client/update_current_branch.mutation.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/mutations/client/update_current_branch.mutation.graphql
index b722c147f5f..b722c147f5f 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/mutations/client/update_current_branch.mutation.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/mutations/client/update_current_branch.mutation.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/mutations/client/update_last_commit_branch.mutation.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/mutations/client/update_last_commit_branch.mutation.graphql
index 9561312f2b6..9561312f2b6 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/mutations/client/update_last_commit_branch.mutation.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/mutations/client/update_last_commit_branch.mutation.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/mutations/client/update_pipeline_etag.mutation.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/mutations/client/update_pipeline_etag.mutation.graphql
index 9025f00b343..9025f00b343 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/mutations/client/update_pipeline_etag.mutation.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/mutations/client/update_pipeline_etag.mutation.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql
index 3495ca51283..3495ca51283 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/available_branches.query.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/available_branches.query.graphql
index 359b4a846c7..359b4a846c7 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/queries/available_branches.query.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/available_branches.query.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/blob_content.query.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/blob_content.query.graphql
index 5928d90f7c4..5928d90f7c4 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/queries/blob_content.query.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/blob_content.query.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/ci_config.query.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/ci_config.query.graphql
index 5354ed7c2d5..5354ed7c2d5 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/queries/ci_config.query.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/ci_config.query.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/client/app_status.query.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/client/app_status.query.graphql
index 0df8cafa3cb..0df8cafa3cb 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/queries/client/app_status.query.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/client/app_status.query.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/client/current_branch.query.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/client/current_branch.query.graphql
index 1f4f9d26f24..1f4f9d26f24 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/queries/client/current_branch.query.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/client/current_branch.query.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/client/last_commit_branch.query.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/client/last_commit_branch.query.graphql
index a83129759de..a83129759de 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/queries/client/last_commit_branch.query.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/client/last_commit_branch.query.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline_etag.query.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/client/pipeline_etag.query.graphql
index 8df6e74a5d9..8df6e74a5d9 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline_etag.query.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/client/pipeline_etag.query.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/get_starter_template.query.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/get_starter_template.query.graphql
index a34c8f365f4..a34c8f365f4 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/queries/get_starter_template.query.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/get_starter_template.query.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/latest_commit_sha.query.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/latest_commit_sha.query.graphql
index d62fda40237..d62fda40237 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/queries/latest_commit_sha.query.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/latest_commit_sha.query.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/pipeline.query.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/pipeline.query.graphql
index 021b858d72e..021b858d72e 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/queries/pipeline.query.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/queries/pipeline.query.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/resolvers.js b/app/assets/javascripts/ci/pipeline_editor/graphql/resolvers.js
index fa1c70c1994..fa1c70c1994 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/resolvers.js
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/resolvers.js
diff --git a/app/assets/javascripts/pipeline_editor/graphql/typedefs.graphql b/app/assets/javascripts/ci/pipeline_editor/graphql/typedefs.graphql
index 508ff22c46e..508ff22c46e 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/typedefs.graphql
+++ b/app/assets/javascripts/ci/pipeline_editor/graphql/typedefs.graphql
diff --git a/app/assets/javascripts/pipeline_editor/index.js b/app/assets/javascripts/ci/pipeline_editor/index.js
index 6d91c339833..6d91c339833 100644
--- a/app/assets/javascripts/pipeline_editor/index.js
+++ b/app/assets/javascripts/ci/pipeline_editor/index.js
diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue b/app/assets/javascripts/ci/pipeline_editor/pipeline_editor_app.vue
index ff848a973e3..ff848a973e3 100644
--- a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/pipeline_editor_app.vue
diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue b/app/assets/javascripts/ci/pipeline_editor/pipeline_editor_home.vue
index 1972125ed56..1972125ed56 100644
--- a/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/pipeline_editor_home.vue
diff --git a/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue b/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue
index 6e24ac6b8d4..a4ef7827f73 100644
--- a/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue
+++ b/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue
@@ -1,18 +1,321 @@
<script>
-import { GlForm } from '@gitlab/ui';
+import {
+ GlButton,
+ GlDropdown,
+ GlDropdownItem,
+ GlFormCheckbox,
+ GlForm,
+ GlFormGroup,
+ GlFormInput,
+ GlFormTextarea,
+ GlLink,
+ GlSprintf,
+} from '@gitlab/ui';
+import { uniqueId } from 'lodash';
+import Vue from 'vue';
+import { __, s__ } from '~/locale';
+import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants';
+import RefSelector from '~/ref/components/ref_selector.vue';
+import TimezoneDropdown from '~/vue_shared/components/timezone_dropdown/timezone_dropdown.vue';
+import IntervalPatternInput from '~/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue';
+import { VARIABLE_TYPE, FILE_TYPE } from '../constants';
export default {
components: {
+ GlButton,
+ GlDropdown,
+ GlDropdownItem,
GlForm,
+ GlFormCheckbox,
+ GlFormGroup,
+ GlFormInput,
+ GlFormTextarea,
+ GlLink,
+ GlSprintf,
+ RefSelector,
+ TimezoneDropdown,
+ IntervalPatternInput,
},
- inject: {
- fullPath: {
+ inject: [
+ 'fullPath',
+ 'projectId',
+ 'defaultBranch',
+ 'cron',
+ 'cronTimezone',
+ 'dailyLimit',
+ 'settingsLink',
+ ],
+ props: {
+ timezoneData: {
+ type: Array,
+ required: true,
+ },
+ refParam: {
+ type: String,
+ required: false,
default: '',
},
},
+ data() {
+ return {
+ refValue: {
+ shortName: this.refParam,
+ // this is needed until we add support for ref type in url query strings
+ // ensure default branch is called with full ref on load
+ // https://gitlab.com/gitlab-org/gitlab/-/issues/287815
+ fullName: this.refParam === this.defaultBranch ? `refs/heads/${this.refParam}` : undefined,
+ },
+ description: '',
+ scheduleRef: this.defaultBranch,
+ activated: true,
+ timezone: this.cronTimezone,
+ formCiVariables: {},
+ // TODO: Add the GraphQL query to help populate the predefined variables
+ // app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue#131
+ predefinedValueOptions: {},
+ };
+ },
+ i18n: {
+ activated: __('Activated'),
+ cronTimezone: s__('PipelineSchedules|Cron timezone'),
+ description: s__('PipelineSchedules|Description'),
+ shortDescriptionPipeline: s__(
+ 'PipelineSchedules|Provide a short description for this pipeline',
+ ),
+ savePipelineSchedule: s__('PipelineSchedules|Save pipeline schedule'),
+ cancel: __('Cancel'),
+ targetBranchTag: __('Select target branch or tag'),
+ intervalPattern: s__('PipelineSchedules|Interval Pattern'),
+ variablesDescription: s__(
+ 'Pipeline|Specify variable values to be used in this run. The values specified in %{linkStart}CI/CD settings%{linkEnd} will be used by default.',
+ ),
+ removeVariableLabel: s__('CiVariables|Remove variable'),
+ variables: s__('Pipeline|Variables'),
+ },
+ typeOptions: {
+ [VARIABLE_TYPE]: __('Variable'),
+ [FILE_TYPE]: __('File'),
+ },
+ formElementClasses: 'gl-md-mr-3 gl-mb-3 gl-flex-basis-quarter gl-flex-shrink-0 gl-flex-grow-0',
+ computed: {
+ dropdownTranslations() {
+ return {
+ dropdownHeader: this.$options.i18n.targetBranchTag,
+ };
+ },
+ refFullName() {
+ return this.refValue.fullName;
+ },
+ variables() {
+ return this.formCiVariables[this.refFullName]?.variables ?? [];
+ },
+ descriptions() {
+ return this.formCiVariables[this.refFullName]?.descriptions ?? {};
+ },
+ typeOptionsListbox() {
+ return [
+ {
+ text: __('Variable'),
+ value: VARIABLE_TYPE,
+ },
+ {
+ text: __('File'),
+ value: FILE_TYPE,
+ },
+ ];
+ },
+ getEnabledRefTypes() {
+ return [REF_TYPE_BRANCHES, REF_TYPE_TAGS];
+ },
+ },
+ created() {
+ Vue.set(this.formCiVariables, this.refFullName, {
+ variables: [],
+ descriptions: {},
+ });
+
+ this.addEmptyVariable(this.refFullName);
+ },
+ methods: {
+ addEmptyVariable(refValue) {
+ const { variables } = this.formCiVariables[refValue];
+
+ const lastVar = variables[variables.length - 1];
+ if (lastVar?.key === '' && lastVar?.value === '') {
+ return;
+ }
+
+ variables.push({
+ uniqueId: uniqueId(`var-${refValue}`),
+ variable_type: VARIABLE_TYPE,
+ key: '',
+ value: '',
+ });
+ },
+ setVariableAttribute(key, attribute, value) {
+ const { variables } = this.formCiVariables[this.refFullName];
+ const variable = variables.find((v) => v.key === key);
+ variable[attribute] = value;
+ },
+ shouldShowValuesDropdown(key) {
+ return this.predefinedValueOptions[key]?.length > 1;
+ },
+ removeVariable(index) {
+ this.variables.splice(index, 1);
+ },
+ canRemove(index) {
+ return index < this.variables.length - 1;
+ },
+ },
};
</script>
<template>
- <gl-form />
+ <div class="col-lg-8">
+ <gl-form>
+ <!--Description-->
+ <gl-form-group :label="$options.i18n.description" label-for="schedule-description">
+ <gl-form-input
+ id="schedule-description"
+ v-model="description"
+ type="text"
+ :placeholder="$options.i18n.shortDescriptionPipeline"
+ data-testid="schedule-description"
+ />
+ </gl-form-group>
+ <!--Interval Pattern-->
+ <gl-form-group :label="$options.i18n.intervalPattern" label-for="schedule-interval">
+ <interval-pattern-input
+ id="schedule-interval"
+ :initial-cron-interval="cron"
+ :daily-limit="dailyLimit"
+ :send-native-errors="false"
+ />
+ </gl-form-group>
+ <!--Timezone-->
+ <gl-form-group :label="$options.i18n.cronTimezone" label-for="schedule-timezone">
+ <timezone-dropdown
+ id="schedule-timezone"
+ :value="timezone"
+ :timezone-data="timezoneData"
+ name="schedule-timezone"
+ />
+ </gl-form-group>
+ <!--Branch/Tag Selector-->
+ <gl-form-group :label="$options.i18n.targetBranchTag" label-for="schedule-target-branch-tag">
+ <ref-selector
+ id="schedule-target-branch-tag"
+ :enabled-ref-types="getEnabledRefTypes"
+ :project-id="projectId"
+ :value="scheduleRef"
+ :use-symbolic-ref-names="true"
+ :translations="dropdownTranslations"
+ class="gl-w-full"
+ />
+ </gl-form-group>
+ <!--Variable List-->
+ <gl-form-group :label="$options.i18n.variables">
+ <div
+ v-for="(variable, index) in variables"
+ :key="variable.uniqueId"
+ class="gl-mb-3 gl-pb-2"
+ data-testid="ci-variable-row"
+ data-qa-selector="ci_variable_row_container"
+ >
+ <div
+ class="gl-display-flex gl-align-items-stretch gl-flex-direction-column gl-md-flex-direction-row"
+ >
+ <gl-dropdown
+ :text="$options.typeOptions[variable.variable_type]"
+ :class="$options.formElementClasses"
+ data-testid="pipeline-form-ci-variable-type"
+ >
+ <gl-dropdown-item
+ v-for="type in Object.keys($options.typeOptions)"
+ :key="type"
+ @click="setVariableAttribute(variable.key, 'variable_type', type)"
+ >
+ {{ $options.typeOptions[type] }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+ <gl-form-input
+ v-model="variable.key"
+ :placeholder="s__('CiVariables|Input variable key')"
+ :class="$options.formElementClasses"
+ data-testid="pipeline-form-ci-variable-key"
+ data-qa-selector="ci_variable_key_field"
+ @change="addEmptyVariable(refFullName)"
+ />
+ <gl-dropdown
+ v-if="shouldShowValuesDropdown(variable.key)"
+ :text="variable.value"
+ :class="$options.formElementClasses"
+ class="gl-flex-grow-1 gl-mr-0!"
+ data-testid="pipeline-form-ci-variable-value-dropdown"
+ >
+ <gl-dropdown-item
+ v-for="value in predefinedValueOptions[variable.key]"
+ :key="value"
+ data-testid="pipeline-form-ci-variable-value-dropdown-items"
+ @click="setVariableAttribute(variable.key, 'value', value)"
+ >
+ {{ value }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+ <gl-form-textarea
+ v-else
+ v-model="variable.value"
+ :placeholder="s__('CiVariables|Input variable value')"
+ class="gl-mb-3 gl-h-7!"
+ :style="$options.textAreaStyle"
+ :no-resize="false"
+ data-testid="pipeline-form-ci-variable-value"
+ data-qa-selector="ci_variable_value_field"
+ />
+
+ <template v-if="variables.length > 1">
+ <gl-button
+ v-if="canRemove(index)"
+ class="gl-md-ml-3 gl-mb-3"
+ data-testid="remove-ci-variable-row"
+ variant="danger"
+ category="secondary"
+ icon="clear"
+ :aria-label="$options.i18n.removeVariableLabel"
+ @click="removeVariable(index)"
+ />
+ <gl-button
+ v-else
+ class="gl-md-ml-3 gl-mb-3 gl-display-none gl-md-display-block gl-visibility-hidden"
+ icon="clear"
+ :aria-label="$options.i18n.removeVariableLabel"
+ />
+ </template>
+ </div>
+ <div v-if="descriptions[variable.key]" class="gl-text-gray-500 gl-mb-3">
+ {{ descriptions[variable.key] }}
+ </div>
+ </div>
+
+ <template #description
+ ><gl-sprintf :message="$options.i18n.variablesDescription">
+ <template #link="{ content }">
+ <gl-link :href="settingsLink">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf></template
+ >
+ </gl-form-group>
+ <!--Activated-->
+ <gl-form-checkbox id="schedule-active" v-model="activated" class="gl-mb-3">{{
+ $options.i18n.activated
+ }}</gl-form-checkbox>
+
+ <gl-button type="submit" variant="confirm" data-testid="schedule-submit-button">{{
+ $options.i18n.savePipelineSchedule
+ }}</gl-button>
+ <gl-button type="reset" data-testid="schedule-cancel-button">{{
+ $options.i18n.cancel
+ }}</gl-button>
+ </gl-form>
+ </div>
</template>
diff --git a/app/assets/javascripts/ci/pipeline_schedules/constants.js b/app/assets/javascripts/ci/pipeline_schedules/constants.js
new file mode 100644
index 00000000000..b4ab1143f60
--- /dev/null
+++ b/app/assets/javascripts/ci/pipeline_schedules/constants.js
@@ -0,0 +1,2 @@
+export const VARIABLE_TYPE = 'env_var';
+export const FILE_TYPE = 'file';
diff --git a/app/assets/javascripts/ci/pipeline_schedules/mount_pipeline_schedules_form_app.js b/app/assets/javascripts/ci/pipeline_schedules/mount_pipeline_schedules_form_app.js
index d83417ab84a..445161f99cb 100644
--- a/app/assets/javascripts/ci/pipeline_schedules/mount_pipeline_schedules_form_app.js
+++ b/app/assets/javascripts/ci/pipeline_schedules/mount_pipeline_schedules_form_app.js
@@ -16,7 +16,16 @@ export default (selector) => {
return false;
}
- const { fullPath } = containerEl.dataset;
+ const {
+ fullPath,
+ cron,
+ dailyLimit,
+ timezoneData,
+ cronTimezone,
+ projectId,
+ defaultBranch,
+ settingsLink,
+ } = containerEl.dataset;
return new Vue({
el: containerEl,
@@ -24,9 +33,20 @@ export default (selector) => {
apolloProvider,
provide: {
fullPath,
+ projectId,
+ defaultBranch,
+ dailyLimit: dailyLimit ?? '',
+ cronTimezone: cronTimezone ?? '',
+ cron: cron ?? '',
+ settingsLink,
},
render(createElement) {
- return createElement(PipelineSchedulesForm);
+ return createElement(PipelineSchedulesForm, {
+ props: {
+ timezoneData: JSON.parse(timezoneData),
+ refParam: defaultBranch,
+ },
+ });
},
});
};
diff --git a/app/assets/javascripts/ci/reports/codequality_report/components/codequality_issue_body.vue b/app/assets/javascripts/ci/reports/codequality_report/components/codequality_issue_body.vue
new file mode 100644
index 00000000000..5a7ee9c9b28
--- /dev/null
+++ b/app/assets/javascripts/ci/reports/codequality_report/components/codequality_issue_body.vue
@@ -0,0 +1,76 @@
+<script>
+/**
+ * Renders Code quality body text
+ * Fixed: [name] in [link]:[line]
+ */
+import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import ReportLink from '~/ci/reports/components/report_link.vue';
+import { STATUS_SUCCESS, STATUS_NEUTRAL } from '~/ci/reports/constants';
+import { SEVERITY_CLASSES, SEVERITY_ICONS } from '../constants';
+
+export default {
+ name: 'CodequalityIssueBody',
+ components: {
+ GlIcon,
+ ReportLink,
+ },
+ directives: {
+ tooltip: GlTooltipDirective,
+ },
+ props: {
+ status: {
+ type: String,
+ required: false,
+ default: STATUS_NEUTRAL,
+ },
+ issue: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ issueName() {
+ return `${this.severityLabel} - ${this.issue.name}`;
+ },
+ issueSeverity() {
+ return this.issue.severity?.toLowerCase();
+ },
+ isStatusSuccess() {
+ return this.status === STATUS_SUCCESS;
+ },
+ severityClass() {
+ return SEVERITY_CLASSES[this.issueSeverity] || SEVERITY_CLASSES.unknown;
+ },
+ severityIcon() {
+ return SEVERITY_ICONS[this.issueSeverity] || SEVERITY_ICONS.unknown;
+ },
+ severityLabel() {
+ return this.$options.severityText[this.issueSeverity] || this.$options.severityText.unknown;
+ },
+ },
+ severityText: {
+ info: s__('severity|Info'),
+ minor: s__('severity|Minor'),
+ major: s__('severity|Major'),
+ critical: s__('severity|Critical'),
+ blocker: s__('severity|Blocker'),
+ unknown: s__('severity|Unknown'),
+ },
+};
+</script>
+<template>
+ <div class="gl-display-flex gl-mt-2 gl-mb-2 gl-w-full">
+ <span :class="severityClass" class="gl-mr-5" data-testid="codequality-severity-icon">
+ <gl-icon v-tooltip="severityLabel" :name="severityIcon" :size="12" />
+ </span>
+ <div class="gl-flex-grow-1">
+ <div>
+ <strong v-if="isStatusSuccess">{{ s__('ciReport|Fixed:') }}</strong>
+ {{ issueName }}
+ </div>
+
+ <report-link v-if="issue.path" :issue="issue" />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ci/reports/codequality_report/constants.js b/app/assets/javascripts/ci/reports/codequality_report/constants.js
new file mode 100644
index 00000000000..5e81245037f
--- /dev/null
+++ b/app/assets/javascripts/ci/reports/codequality_report/constants.js
@@ -0,0 +1,53 @@
+export const SEVERITY_CLASSES = {
+ info: 'text-primary-400',
+ minor: 'text-warning-200',
+ major: 'text-warning-400',
+ critical: 'text-danger-600',
+ blocker: 'text-danger-800',
+ unknown: 'text-secondary-400',
+};
+
+export const SEVERITY_ICONS = {
+ info: 'severity-info',
+ minor: 'severity-low',
+ major: 'severity-medium',
+ critical: 'severity-high',
+ blocker: 'severity-critical',
+ unknown: 'severity-unknown',
+};
+
+export const SEVERITY_ICONS_MR_WIDGET = {
+ info: 'severityInfo',
+ minor: 'severityLow',
+ major: 'severityMedium',
+ critical: 'severityHigh',
+ blocker: 'severityCritical',
+ unknown: 'severityUnknown',
+};
+
+export const SEVERITIES = {
+ info: {
+ class: SEVERITY_CLASSES.info,
+ name: SEVERITY_ICONS.info,
+ },
+ minor: {
+ class: SEVERITY_CLASSES.minor,
+ name: SEVERITY_ICONS.minor,
+ },
+ major: {
+ class: SEVERITY_CLASSES.major,
+ name: SEVERITY_ICONS.major,
+ },
+ critical: {
+ class: SEVERITY_CLASSES.critical,
+ name: SEVERITY_ICONS.critical,
+ },
+ blocker: {
+ class: SEVERITY_CLASSES.blocker,
+ name: SEVERITY_ICONS.blocker,
+ },
+ unknown: {
+ class: SEVERITY_CLASSES.unknown,
+ name: SEVERITY_ICONS.unknown,
+ },
+};
diff --git a/app/assets/javascripts/reports/codequality_report/store/actions.js b/app/assets/javascripts/ci/reports/codequality_report/store/actions.js
index 04aca11b945..04aca11b945 100644
--- a/app/assets/javascripts/reports/codequality_report/store/actions.js
+++ b/app/assets/javascripts/ci/reports/codequality_report/store/actions.js
diff --git a/app/assets/javascripts/reports/codequality_report/store/getters.js b/app/assets/javascripts/ci/reports/codequality_report/store/getters.js
index 70d11e96a54..70d11e96a54 100644
--- a/app/assets/javascripts/reports/codequality_report/store/getters.js
+++ b/app/assets/javascripts/ci/reports/codequality_report/store/getters.js
diff --git a/app/assets/javascripts/reports/codequality_report/store/index.js b/app/assets/javascripts/ci/reports/codequality_report/store/index.js
index 5bfcd69edec..5bfcd69edec 100644
--- a/app/assets/javascripts/reports/codequality_report/store/index.js
+++ b/app/assets/javascripts/ci/reports/codequality_report/store/index.js
diff --git a/app/assets/javascripts/reports/codequality_report/store/mutation_types.js b/app/assets/javascripts/ci/reports/codequality_report/store/mutation_types.js
index c362c973ae1..c362c973ae1 100644
--- a/app/assets/javascripts/reports/codequality_report/store/mutation_types.js
+++ b/app/assets/javascripts/ci/reports/codequality_report/store/mutation_types.js
diff --git a/app/assets/javascripts/reports/codequality_report/store/mutations.js b/app/assets/javascripts/ci/reports/codequality_report/store/mutations.js
index 249c2f35c0b..249c2f35c0b 100644
--- a/app/assets/javascripts/reports/codequality_report/store/mutations.js
+++ b/app/assets/javascripts/ci/reports/codequality_report/store/mutations.js
diff --git a/app/assets/javascripts/reports/codequality_report/store/state.js b/app/assets/javascripts/ci/reports/codequality_report/store/state.js
index f68dbc2a5fa..f68dbc2a5fa 100644
--- a/app/assets/javascripts/reports/codequality_report/store/state.js
+++ b/app/assets/javascripts/ci/reports/codequality_report/store/state.js
diff --git a/app/assets/javascripts/reports/codequality_report/store/utils/codequality_parser.js b/app/assets/javascripts/ci/reports/codequality_report/store/utils/codequality_parser.js
index 417297df43c..417297df43c 100644
--- a/app/assets/javascripts/reports/codequality_report/store/utils/codequality_parser.js
+++ b/app/assets/javascripts/ci/reports/codequality_report/store/utils/codequality_parser.js
diff --git a/app/assets/javascripts/ci/reports/components/grouped_issues_list.vue b/app/assets/javascripts/ci/reports/components/grouped_issues_list.vue
new file mode 100644
index 00000000000..b21a486e259
--- /dev/null
+++ b/app/assets/javascripts/ci/reports/components/grouped_issues_list.vue
@@ -0,0 +1,106 @@
+<script>
+import { s__ } from '~/locale';
+import ReportItem from '~/ci/reports/components/report_item.vue';
+import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
+
+export default {
+ components: {
+ ReportItem,
+ SmartVirtualList,
+ },
+ props: {
+ component: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ nestedLevel: {
+ type: Number,
+ required: false,
+ default: 0,
+ validator: (value) => [0, 1, 2].includes(value),
+ },
+ resolvedIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ unresolvedIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ resolvedHeading: {
+ type: String,
+ required: false,
+ default: s__('ciReport|Fixed'),
+ },
+ unresolvedHeading: {
+ type: String,
+ required: false,
+ default: s__('ciReport|New'),
+ },
+ },
+ groups: ['unresolved', 'resolved'],
+ typicalReportItemHeight: 32,
+ maxShownReportItems: 20,
+ computed: {
+ groups() {
+ return this.$options.groups
+ .map((group) => ({
+ name: group,
+ issues: this[`${group}Issues`],
+ heading: this[`${group}Heading`],
+ }))
+ .filter(({ issues }) => issues.length > 0);
+ },
+ listLength() {
+ // every group has a header which is rendered as a list item
+ const groupsCount = this.groups.length;
+ const issuesCount = this.groups.reduce(
+ (totalIssues, { issues }) => totalIssues + issues.length,
+ 0,
+ );
+
+ return groupsCount + issuesCount;
+ },
+ listClasses() {
+ return {
+ 'gl-pl-9': this.nestedLevel === 1,
+ 'gl-pl-11-5': this.nestedLevel === 2,
+ };
+ },
+ },
+};
+</script>
+
+<template>
+ <smart-virtual-list
+ :length="listLength"
+ :remain="$options.maxShownReportItems"
+ :size="$options.typicalReportItemHeight"
+ :class="listClasses"
+ class="report-block-container"
+ wtag="ul"
+ wclass="report-block-list"
+ >
+ <template v-for="(group, groupIndex) in groups">
+ <h2
+ :key="group.name"
+ :data-testid="`${group.name}Heading`"
+ :class="[groupIndex > 0 ? 'mt-2' : 'mt-0']"
+ class="h5 mb-1"
+ >
+ {{ group.heading }}
+ </h2>
+ <report-item
+ v-for="(issue, issueIndex) in group.issues"
+ :key="`${group.name}-${issue.name}-${group.name}-${issueIndex}`"
+ :issue="issue"
+ :show-report-section-status-icon="false"
+ :component="component"
+ status="none"
+ />
+ </template>
+ </smart-virtual-list>
+</template>
diff --git a/app/assets/javascripts/ci/reports/components/issue_body.js b/app/assets/javascripts/ci/reports/components/issue_body.js
new file mode 100644
index 00000000000..daff1be30ff
--- /dev/null
+++ b/app/assets/javascripts/ci/reports/components/issue_body.js
@@ -0,0 +1,17 @@
+import IssueStatusIcon from '~/ci/reports/components/issue_status_icon.vue';
+
+export const components = {
+ CodequalityIssueBody: () => import('../codequality_report/components/codequality_issue_body.vue'),
+};
+
+export const componentNames = {
+ CodequalityIssueBody: 'CodequalityIssueBody',
+};
+
+export const iconComponents = {
+ IssueStatusIcon,
+};
+
+export const iconComponentNames = {
+ IssueStatusIcon: IssueStatusIcon.name,
+};
diff --git a/app/assets/javascripts/reports/components/issue_status_icon.vue b/app/assets/javascripts/ci/reports/components/issue_status_icon.vue
index bd41b8d23f1..bd41b8d23f1 100644
--- a/app/assets/javascripts/reports/components/issue_status_icon.vue
+++ b/app/assets/javascripts/ci/reports/components/issue_status_icon.vue
diff --git a/app/assets/javascripts/ci/reports/components/issues_list.vue b/app/assets/javascripts/ci/reports/components/issues_list.vue
new file mode 100644
index 00000000000..ababd4b5e49
--- /dev/null
+++ b/app/assets/javascripts/ci/reports/components/issues_list.vue
@@ -0,0 +1,119 @@
+<script>
+import ReportItem from '~/ci/reports/components/report_item.vue';
+import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '~/ci/reports/constants';
+import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
+
+const wrapIssueWithState = (status, isNew = false) => (issue) => ({
+ status: issue.status || status,
+ isNew,
+ issue,
+});
+
+/**
+ * Renders block of issues
+ */
+export default {
+ components: {
+ SmartVirtualList,
+ ReportItem,
+ },
+ // Typical height of a report item in px
+ typicalReportItemHeight: 32,
+ /*
+ The maximum amount of shown issues. This is calculated by
+ ( max-height of report-block-list / typicalReportItemHeight ) + some safety margin
+ We will use VirtualList if we have more items than this number.
+ For entries lower than this number, the virtual scroll list calculates the total height of the element wrongly.
+ */
+ maxShownReportItems: 20,
+ props: {
+ newIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ unresolvedIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ resolvedIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ neutralIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ component: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ showReportSectionStatusIcon: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ issuesUlElementClass: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ issueItemClass: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ nestedLevel: {
+ type: Number,
+ required: false,
+ default: 0,
+ validator: (value) => [0, 1, 2].includes(value),
+ },
+ },
+ computed: {
+ issuesWithState() {
+ return [
+ ...this.newIssues.map(wrapIssueWithState(STATUS_FAILED, true)),
+ ...this.unresolvedIssues.map(wrapIssueWithState(STATUS_FAILED)),
+ ...this.neutralIssues.map(wrapIssueWithState(STATUS_NEUTRAL)),
+ ...this.resolvedIssues.map(wrapIssueWithState(STATUS_SUCCESS)),
+ ];
+ },
+ wclass() {
+ return `report-block-list ${this.issuesUlElementClass}`;
+ },
+ listClasses() {
+ return {
+ 'gl-pl-9': this.nestedLevel === 1,
+ 'gl-pl-11-5': this.nestedLevel === 2,
+ };
+ },
+ },
+};
+</script>
+<template>
+ <smart-virtual-list
+ :length="issuesWithState.length"
+ :remain="$options.maxShownReportItems"
+ :size="$options.typicalReportItemHeight"
+ class="report-block-container"
+ :class="listClasses"
+ wtag="ul"
+ :wclass="wclass"
+ >
+ <report-item
+ v-for="(wrapped, index) in issuesWithState"
+ :key="index"
+ :issue="wrapped.issue"
+ :status="wrapped.status"
+ :component="component"
+ :is-new="wrapped.isNew"
+ :show-report-section-status-icon="showReportSectionStatusIcon"
+ :class="issueItemClass"
+ />
+ </smart-virtual-list>
+</template>
diff --git a/app/assets/javascripts/ci/reports/components/report_item.vue b/app/assets/javascripts/ci/reports/components/report_item.vue
new file mode 100644
index 00000000000..97d4ac7bf6f
--- /dev/null
+++ b/app/assets/javascripts/ci/reports/components/report_item.vue
@@ -0,0 +1,67 @@
+<script>
+import {
+ components,
+ componentNames,
+ iconComponents,
+ iconComponentNames,
+} from 'ee_else_ce/ci/reports/components/issue_body';
+
+export default {
+ name: 'ReportItem',
+ components: {
+ ...components,
+ ...iconComponents,
+ },
+ props: {
+ issue: {
+ type: Object,
+ required: true,
+ },
+ component: {
+ type: String,
+ required: false,
+ default: '',
+ validator: (value) => value === '' || Object.values(componentNames).includes(value),
+ },
+ iconComponent: {
+ type: String,
+ required: false,
+ default: iconComponentNames.IssueStatusIcon,
+ validator: (value) => Object.values(iconComponentNames).includes(value),
+ },
+ // failed || success
+ status: {
+ type: String,
+ required: true,
+ },
+ statusIconSize: {
+ type: Number,
+ required: false,
+ default: 24,
+ },
+ isNew: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ showReportSectionStatusIcon: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ },
+};
+</script>
+<template>
+ <li class="report-block-list-issue align-items-center" data-qa-selector="report_item_row">
+ <component
+ :is="iconComponent"
+ v-if="showReportSectionStatusIcon"
+ :status="status"
+ :status-icon-size="statusIconSize"
+ class="gl-mr-2"
+ />
+
+ <component :is="component" v-if="component" :issue="issue" :status="status" :is-new="isNew" />
+ </li>
+</template>
diff --git a/app/assets/javascripts/reports/components/report_link.vue b/app/assets/javascripts/ci/reports/components/report_link.vue
index 1f68f79e487..1f68f79e487 100644
--- a/app/assets/javascripts/reports/components/report_link.vue
+++ b/app/assets/javascripts/ci/reports/components/report_link.vue
diff --git a/app/assets/javascripts/reports/components/report_section.vue b/app/assets/javascripts/ci/reports/components/report_section.vue
index 468c8916b8d..468c8916b8d 100644
--- a/app/assets/javascripts/reports/components/report_section.vue
+++ b/app/assets/javascripts/ci/reports/components/report_section.vue
diff --git a/app/assets/javascripts/reports/components/summary_row.vue b/app/assets/javascripts/ci/reports/components/summary_row.vue
index ee55368c829..ee55368c829 100644
--- a/app/assets/javascripts/reports/components/summary_row.vue
+++ b/app/assets/javascripts/ci/reports/components/summary_row.vue
diff --git a/app/assets/javascripts/reports/constants.js b/app/assets/javascripts/ci/reports/constants.js
index bad6fa1e7b9..bad6fa1e7b9 100644
--- a/app/assets/javascripts/reports/constants.js
+++ b/app/assets/javascripts/ci/reports/constants.js
diff --git a/app/assets/javascripts/ci/runner/admin_runner_show/admin_runner_show_app.vue b/app/assets/javascripts/ci/runner/admin_runner_show/admin_runner_show_app.vue
index 9fa4b521ebc..66d790acb00 100644
--- a/app/assets/javascripts/ci/runner/admin_runner_show/admin_runner_show_app.vue
+++ b/app/assets/javascripts/ci/runner/admin_runner_show/admin_runner_show_app.vue
@@ -1,5 +1,6 @@
<script>
-import { GlBadge, GlTabs, GlTab, GlTooltipDirective } from '@gitlab/ui';
+import { GlBadge, GlTabs, GlTab } from '@gitlab/ui';
+import VueRouter from 'vue-router';
import { createAlert, VARIANT_SUCCESS } from '~/flash';
import { TYPE_CI_RUNNER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
@@ -11,11 +12,28 @@ import RunnerPauseButton from '../components/runner_pause_button.vue';
import RunnerHeader from '../components/runner_header.vue';
import RunnerDetails from '../components/runner_details.vue';
import RunnerJobs from '../components/runner_jobs.vue';
-import { I18N_DETAILS, I18N_FETCH_ERROR } from '../constants';
+import { I18N_DETAILS, I18N_JOBS, I18N_FETCH_ERROR } from '../constants';
import runnerQuery from '../graphql/show/runner.query.graphql';
import { captureException } from '../sentry_utils';
import { saveAlertToLocalStorage } from '../local_storage_alert/save_alert_to_local_storage';
+const ROUTE_DETAILS = 'details';
+const ROUTE_JOBS = 'jobs';
+
+const routes = [
+ {
+ path: '/',
+ name: ROUTE_DETAILS,
+ component: RunnerDetails,
+ },
+ {
+ path: '/jobs',
+ name: ROUTE_JOBS,
+ component: RunnerJobs,
+ },
+ { path: '*', redirect: { name: ROUTE_DETAILS } },
+];
+
export default {
name: 'AdminRunnerShowApp',
components: {
@@ -26,12 +44,10 @@ export default {
RunnerEditButton,
RunnerPauseButton,
RunnerHeader,
- RunnerDetails,
- RunnerJobs,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
},
+ router: new VueRouter({
+ routes,
+ }),
props: {
runnerId: {
type: String,
@@ -72,11 +88,17 @@ export default {
jobCount() {
return formatJobCount(this.runner?.jobCount);
},
+ tabIndex() {
+ return routes.findIndex(({ name }) => name === this.$route.name);
+ },
},
errorCaptured(error) {
this.reportToSentry(error);
},
methods: {
+ goTo(name) {
+ this.$router.push({ name });
+ },
reportToSentry(error) {
captureException({ error, component: this.$options.name });
},
@@ -85,7 +107,10 @@ export default {
redirectTo(this.runnersPath);
},
},
+ ROUTE_DETAILS,
+ ROUTE_JOBS,
I18N_DETAILS,
+ I18N_JOBS,
};
</script>
<template>
@@ -98,15 +123,13 @@ export default {
</template>
</runner-header>
- <gl-tabs>
- <gl-tab>
+ <gl-tabs :value="tabIndex">
+ <gl-tab @click="goTo($options.ROUTE_DETAILS)">
<template #title>{{ $options.I18N_DETAILS }}</template>
-
- <runner-details v-if="runner" :runner="runner" />
</gl-tab>
- <gl-tab>
+ <gl-tab @click="goTo($options.ROUTE_JOBS)">
<template #title>
- {{ s__('Runners|Jobs') }}
+ {{ $options.I18N_JOBS }}
<gl-badge
v-if="jobCount"
data-testid="job-count-badge"
@@ -116,9 +139,9 @@ export default {
{{ jobCount }}
</gl-badge>
</template>
-
- <runner-jobs v-if="runner" :runner="runner" />
</gl-tab>
+
+ <router-view v-if="runner" :runner="runner" />
</gl-tabs>
</div>
</template>
diff --git a/app/assets/javascripts/ci/runner/admin_runner_show/index.js b/app/assets/javascripts/ci/runner/admin_runner_show/index.js
index ea455416648..cbd25819303 100644
--- a/app/assets/javascripts/ci/runner/admin_runner_show/index.js
+++ b/app/assets/javascripts/ci/runner/admin_runner_show/index.js
@@ -1,10 +1,12 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
+import VueRouter from 'vue-router';
import createDefaultClient from '~/lib/graphql';
import { showAlertFromLocalStorage } from '../local_storage_alert/show_alert_from_local_storage';
import AdminRunnerShowApp from './admin_runner_show_app.vue';
Vue.use(VueApollo);
+Vue.use(VueRouter);
export const initAdminRunnerShow = (selector = '#js-admin-runner-show') => {
showAlertFromLocalStorage();
diff --git a/app/assets/javascripts/ci/runner/admin_runners/admin_runners_app.vue b/app/assets/javascripts/ci/runner/admin_runners/admin_runners_app.vue
index 2915e460085..3bd20dff9cc 100644
--- a/app/assets/javascripts/ci/runner/admin_runners/admin_runners_app.vue
+++ b/app/assets/javascripts/ci/runner/admin_runners/admin_runners_app.vue
@@ -23,6 +23,7 @@ import RunnerStats from '../components/stat/runner_stats.vue';
import RunnerPagination from '../components/runner_pagination.vue';
import RunnerTypeTabs from '../components/runner_type_tabs.vue';
import RunnerActionsCell from '../components/cells/runner_actions_cell.vue';
+import RunnerJobStatusBadge from '../components/runner_job_status_badge.vue';
import { pausedTokenConfig } from '../components/search_tokens/paused_token_config';
import { statusTokenConfig } from '../components/search_tokens/status_token_config';
@@ -48,6 +49,7 @@ export default {
RunnerPagination,
RunnerTypeTabs,
RunnerActionsCell,
+ RunnerJobStatusBadge,
},
mixins: [glFeatureFlagMixin()],
inject: ['emptyStateSvgPath', 'emptyStateFilteredSvgPath'],
@@ -69,6 +71,9 @@ export default {
apollo: {
runners: {
query: allRunnersQuery,
+ context: {
+ isSingleRequest: true,
+ },
fetchPolicy: fetchPolicies.NETWORK_ONLY,
variables() {
return this.variables;
@@ -134,6 +139,12 @@ export default {
this.reportToSentry(error);
},
methods: {
+ jobsUrl(runner) {
+ const url = new URL(runner.adminUrl);
+ url.hash = '#/jobs';
+
+ return url.href;
+ },
onToggledPaused() {
// When a runner becomes Paused, the tab count can
// become stale, refetch outdated counts.
@@ -208,6 +219,12 @@ export default {
<runner-name :runner="runner" />
</gl-link>
</template>
+ <template #runner-job-status-badge="{ runner }">
+ <runner-job-status-badge
+ :href="jobsUrl(runner)"
+ :job-status="runner.jobExecutionStatus"
+ />
+ </template>
<template #runner-actions-cell="{ runner }">
<runner-actions-cell
:runner="runner"
diff --git a/app/assets/javascripts/ci/runner/components/cells/runner_stacked_summary_cell.vue b/app/assets/javascripts/ci/runner/components/cells/runner_stacked_summary_cell.vue
deleted file mode 100644
index 1e44d5fccc2..00000000000
--- a/app/assets/javascripts/ci/runner/components/cells/runner_stacked_summary_cell.vue
+++ /dev/null
@@ -1,112 +0,0 @@
-<script>
-import { GlIcon, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
-
-import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
-import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
-import RunnerName from '../runner_name.vue';
-import RunnerTags from '../runner_tags.vue';
-import RunnerTypeBadge from '../runner_type_badge.vue';
-
-import { formatJobCount } from '../../utils';
-import {
- I18N_LOCKED_RUNNER_DESCRIPTION,
- I18N_VERSION_LABEL,
- I18N_LAST_CONTACT_LABEL,
- I18N_CREATED_AT_LABEL,
-} from '../../constants';
-import RunnerSummaryField from './runner_summary_field.vue';
-
-export default {
- components: {
- GlIcon,
- GlSprintf,
- TimeAgo,
- RunnerSummaryField,
- RunnerName,
- RunnerTags,
- RunnerTypeBadge,
- RunnerUpgradeStatusIcon: () =>
- import('ee_component/ci/runner/components/runner_upgrade_status_icon.vue'),
- TooltipOnTruncate,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- runner: {
- type: Object,
- required: true,
- },
- },
- computed: {
- jobCount() {
- return formatJobCount(this.runner.jobCount);
- },
- },
- i18n: {
- I18N_LOCKED_RUNNER_DESCRIPTION,
- I18N_VERSION_LABEL,
- I18N_LAST_CONTACT_LABEL,
- I18N_CREATED_AT_LABEL,
- },
-};
-</script>
-
-<template>
- <div>
- <div>
- <slot :runner="runner" name="runner-name">
- <runner-name :runner="runner" />
- </slot>
- <gl-icon
- v-if="runner.locked"
- v-gl-tooltip
- :title="$options.i18n.I18N_LOCKED_RUNNER_DESCRIPTION"
- name="lock"
- />
- <runner-type-badge :type="runner.runnerType" size="sm" class="gl-vertical-align-middle" />
- </div>
-
- <div class="gl-ml-auto gl-display-inline-flex gl-max-w-full gl-py-2">
- <div class="gl-flex-shrink-0">
- <runner-upgrade-status-icon :runner="runner" />
- <gl-sprintf v-if="runner.version" :message="$options.i18n.I18N_VERSION_LABEL">
- <template #version>{{ runner.version }}</template>
- </gl-sprintf>
- </div>
- <div class="gl-text-secondary gl-mx-2" aria-hidden="true">·</div>
- <tooltip-on-truncate class="gl-text-truncate gl-display-block" :title="runner.description">
- {{ runner.description }}
- </tooltip-on-truncate>
- </div>
-
- <div>
- <runner-summary-field icon="clock">
- <gl-sprintf :message="$options.i18n.I18N_LAST_CONTACT_LABEL">
- <template #timeAgo>
- <time-ago v-if="runner.contactedAt" :time="runner.contactedAt" />
- <template v-else>{{ __('Never') }}</template>
- </template>
- </gl-sprintf>
- </runner-summary-field>
-
- <runner-summary-field v-if="runner.ipAddress" icon="disk" :tooltip="__('IP Address')">
- {{ runner.ipAddress }}
- </runner-summary-field>
-
- <runner-summary-field icon="pipeline" data-testid="job-count" :tooltip="__('Jobs')">
- {{ jobCount }}
- </runner-summary-field>
-
- <runner-summary-field icon="calendar">
- <gl-sprintf :message="$options.i18n.I18N_CREATED_AT_LABEL">
- <template #timeAgo>
- <time-ago v-if="runner.createdAt" :time="runner.createdAt" />
- </template>
- </gl-sprintf>
- </runner-summary-field>
- </div>
-
- <runner-tags class="gl-display-block gl-pt-2" :tag-list="runner.tagList" size="sm" />
- </div>
-</template>
diff --git a/app/assets/javascripts/ci/runner/components/cells/runner_status_cell.vue b/app/assets/javascripts/ci/runner/components/cells/runner_status_cell.vue
index 67b9b0a266f..cfbe37f5ba2 100644
--- a/app/assets/javascripts/ci/runner/components/cells/runner_status_cell.vue
+++ b/app/assets/javascripts/ci/runner/components/cells/runner_status_cell.vue
@@ -7,8 +7,6 @@ import RunnerPausedBadge from '../runner_paused_badge.vue';
export default {
components: {
RunnerStatusBadge,
- RunnerUpgradeStatusBadge: () =>
- import('ee_component/ci/runner/components/runner_upgrade_status_badge.vue'),
RunnerPausedBadge,
},
directives: {
@@ -34,10 +32,6 @@ export default {
:runner="runner"
class="gl-display-inline-block gl-max-w-full gl-text-truncate"
/>
- <runner-upgrade-status-badge
- :runner="runner"
- class="gl-display-inline-block gl-max-w-full gl-text-truncate"
- />
<runner-paused-badge
v-if="paused"
class="gl-display-inline-block gl-max-w-full gl-text-truncate"
diff --git a/app/assets/javascripts/ci/runner/components/cells/runner_summary_cell.vue b/app/assets/javascripts/ci/runner/components/cells/runner_summary_cell.vue
new file mode 100644
index 00000000000..4a72023b6a0
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/components/cells/runner_summary_cell.vue
@@ -0,0 +1,125 @@
+<script>
+import { GlIcon, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
+
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
+import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
+import RunnerName from '../runner_name.vue';
+import RunnerTags from '../runner_tags.vue';
+import RunnerTypeBadge from '../runner_type_badge.vue';
+import RunnerJobStatusBadge from '../runner_job_status_badge.vue';
+
+import { formatJobCount } from '../../utils';
+import {
+ I18N_NO_DESCRIPTION,
+ I18N_LOCKED_RUNNER_DESCRIPTION,
+ I18N_VERSION_LABEL,
+ I18N_LAST_CONTACT_LABEL,
+ I18N_CREATED_AT_LABEL,
+} from '../../constants';
+import RunnerSummaryField from './runner_summary_field.vue';
+
+export default {
+ components: {
+ GlIcon,
+ GlSprintf,
+ TimeAgo,
+ RunnerSummaryField,
+ RunnerName,
+ RunnerTags,
+ RunnerTypeBadge,
+ RunnerJobStatusBadge,
+ RunnerUpgradeStatusIcon: () =>
+ import('ee_component/ci/runner/components/runner_upgrade_status_icon.vue'),
+ TooltipOnTruncate,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ runner: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ jobCount() {
+ return formatJobCount(this.runner.jobCount);
+ },
+ },
+ i18n: {
+ I18N_NO_DESCRIPTION,
+ I18N_LOCKED_RUNNER_DESCRIPTION,
+ I18N_VERSION_LABEL,
+ I18N_LAST_CONTACT_LABEL,
+ I18N_CREATED_AT_LABEL,
+ },
+};
+</script>
+
+<template>
+ <div>
+ <div>
+ <slot :runner="runner" name="runner-name">
+ <runner-name :runner="runner" />
+ </slot>
+ <gl-icon
+ v-if="runner.locked"
+ v-gl-tooltip
+ :title="$options.i18n.I18N_LOCKED_RUNNER_DESCRIPTION"
+ name="lock"
+ />
+ <runner-type-badge :type="runner.runnerType" size="sm" class="gl-vertical-align-middle" />
+ </div>
+
+ <div class="gl-ml-auto gl-display-inline-flex gl-max-w-full gl-py-2">
+ <div class="gl-flex-shrink-0">
+ <runner-upgrade-status-icon :runner="runner" />
+ <gl-sprintf v-if="runner.version" :message="$options.i18n.I18N_VERSION_LABEL">
+ <template #version>{{ runner.version }}</template>
+ </gl-sprintf>
+ </div>
+ <div class="gl-text-secondary gl-mx-2" aria-hidden="true">·</div>
+ <tooltip-on-truncate
+ v-if="runner.description"
+ class="gl-text-truncate gl-display-block"
+ :title="runner.description"
+ >
+ {{ runner.description }}
+ </tooltip-on-truncate>
+ <span v-else class="gl-text-secondary">{{ $options.i18n.I18N_NO_DESCRIPTION }}</span>
+ </div>
+
+ <div>
+ <slot :runner="runner" name="runner-job-status-badge">
+ <runner-job-status-badge :job-status="runner.jobExecutionStatus" />
+ </slot>
+
+ <runner-summary-field icon="clock">
+ <gl-sprintf :message="$options.i18n.I18N_LAST_CONTACT_LABEL">
+ <template #timeAgo>
+ <time-ago v-if="runner.contactedAt" :time="runner.contactedAt" />
+ <template v-else>{{ __('Never') }}</template>
+ </template>
+ </gl-sprintf>
+ </runner-summary-field>
+
+ <runner-summary-field v-if="runner.ipAddress" icon="disk" :tooltip="__('IP Address')">
+ {{ runner.ipAddress }}
+ </runner-summary-field>
+
+ <runner-summary-field icon="pipeline" data-testid="job-count" :tooltip="__('Jobs')">
+ {{ jobCount }}
+ </runner-summary-field>
+
+ <runner-summary-field icon="calendar">
+ <gl-sprintf :message="$options.i18n.I18N_CREATED_AT_LABEL">
+ <template #timeAgo>
+ <time-ago v-if="runner.createdAt" :time="runner.createdAt" />
+ </template>
+ </gl-sprintf>
+ </runner-summary-field>
+ </div>
+
+ <runner-tags class="gl-display-block gl-pt-2" :tag-list="runner.tagList" size="sm" />
+ </div>
+</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_detail.vue b/app/assets/javascripts/ci/runner/components/runner_detail.vue
index c260670b517..9e8055a8432 100644
--- a/app/assets/javascripts/ci/runner/components/runner_detail.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_detail.vue
@@ -49,7 +49,7 @@ export default {
<template v-if="value || $scopedSlots.value">
<slot name="value">{{ value }}</slot>
</template>
- <span v-else class="gl-text-gray-500">{{ emptyValue }}</span>
+ <span v-else class="gl-text-secondary">{{ emptyValue }}</span>
</dd>
</div>
</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_groups.vue b/app/assets/javascripts/ci/runner/components/runner_groups.vue
index c3b35bd52a9..8501d165157 100644
--- a/app/assets/javascripts/ci/runner/components/runner_groups.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_groups.vue
@@ -32,6 +32,6 @@ export default {
:avatar-url="group.avatarUrl"
/>
</template>
- <span v-else class="gl-text-gray-500">{{ __('None') }}</span>
+ <span v-else class="gl-text-secondary">{{ __('None') }}</span>
</div>
</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_job_status_badge.vue b/app/assets/javascripts/ci/runner/components/runner_job_status_badge.vue
new file mode 100644
index 00000000000..1e52acecfb8
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/components/runner_job_status_badge.vue
@@ -0,0 +1,55 @@
+<script>
+import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
+import {
+ I18N_JOB_STATUS_RUNNING,
+ I18N_JOB_STATUS_IDLE,
+ JOB_STATUS_RUNNING,
+ JOB_STATUS_IDLE,
+} from '../constants';
+
+export default {
+ components: {
+ GlBadge,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ jobStatus: {
+ required: false,
+ default: null,
+ type: String,
+ },
+ },
+ computed: {
+ badge() {
+ switch (this.jobStatus) {
+ case JOB_STATUS_RUNNING:
+ return {
+ classes: 'gl-text-blue-600! gl-border gl-border-blue-600!',
+ label: I18N_JOB_STATUS_RUNNING,
+ };
+ case JOB_STATUS_IDLE:
+ return {
+ classes: 'gl-text-gray-700! gl-border gl-border-gray-500!',
+ label: I18N_JOB_STATUS_IDLE,
+ };
+ default:
+ return null;
+ }
+ },
+ },
+};
+</script>
+<template>
+ <gl-badge
+ v-if="badge"
+ v-bind="$attrs"
+ size="sm"
+ class="gl-mr-3 gl-bg-transparent!"
+ variant="muted"
+ :class="badge.classes"
+ >
+ {{ badge.label }}
+ </gl-badge>
+</template>
diff --git a/app/assets/javascripts/ci/runner/components/runner_list.vue b/app/assets/javascripts/ci/runner/components/runner_list.vue
index e895537dcdc..b2aad0aac4f 100644
--- a/app/assets/javascripts/ci/runner/components/runner_list.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_list.vue
@@ -7,7 +7,7 @@ import checkedRunnerIdsQuery from '../graphql/list/checked_runner_ids.query.grap
import { formatJobCount, tableField } from '../utils';
import RunnerBulkDelete from './runner_bulk_delete.vue';
import RunnerBulkDeleteCheckbox from './runner_bulk_delete_checkbox.vue';
-import RunnerStackedSummaryCell from './cells/runner_stacked_summary_cell.vue';
+import RunnerSummaryCell from './cells/runner_summary_cell.vue';
import RunnerStatusPopover from './runner_status_popover.vue';
import RunnerStatusCell from './cells/runner_status_cell.vue';
import RunnerOwnerCell from './cells/runner_owner_cell.vue';
@@ -28,7 +28,7 @@ export default {
RunnerBulkDelete,
RunnerBulkDeleteCheckbox,
RunnerStatusPopover,
- RunnerStackedSummaryCell,
+ RunnerSummaryCell,
RunnerStatusCell,
RunnerOwnerCell,
},
@@ -154,11 +154,14 @@ export default {
</template>
<template #cell(summary)="{ item, index }">
- <runner-stacked-summary-cell :runner="item">
+ <runner-summary-cell :runner="item">
<template #runner-name="{ runner }">
<slot name="runner-name" :runner="runner" :index="index"></slot>
</template>
- </runner-stacked-summary-cell>
+ <template #runner-job-status-badge="{ runner }">
+ <slot name="runner-job-status-badge" :runner="runner" :index="index"></slot>
+ </template>
+ </runner-summary-cell>
</template>
<template #head(owner)="{ label }">
diff --git a/app/assets/javascripts/ci/runner/components/runner_projects.vue b/app/assets/javascripts/ci/runner/components/runner_projects.vue
index 84008e8eee8..4a6e90b44a9 100644
--- a/app/assets/javascripts/ci/runner/components/runner_projects.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_projects.vue
@@ -133,7 +133,7 @@ export default {
:is-owner="isOwner(project.id)"
/>
</template>
- <div v-else class="gl-py-5 gl-text-gray-500">{{ $options.I18N_NO_PROJECTS_FOUND }}</div>
+ <div v-else class="gl-py-5 gl-text-secondary">{{ $options.I18N_NO_PROJECTS_FOUND }}</div>
<runner-pagination
:disabled="loading"
diff --git a/app/assets/javascripts/ci/runner/components/runner_type_tabs.vue b/app/assets/javascripts/ci/runner/components/runner_type_tabs.vue
index 584236168ac..70226074993 100644
--- a/app/assets/javascripts/ci/runner/components/runner_type_tabs.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_type_tabs.vue
@@ -59,21 +59,20 @@ export default {
return [
{
title: I18N_ALL_TYPES,
- runnerType: null,
},
...tabs,
];
},
},
methods: {
- onTabSelected({ runnerType }) {
+ onTabSelected(runnerType) {
this.$emit('input', {
...this.value,
runnerType,
pagination: { page: 1 },
});
},
- isTabActive({ runnerType }) {
+ isTabActive(runnerType = null) {
return runnerType === this.value.runnerType;
},
tabBadgeCountVariables(runnerType) {
@@ -102,8 +101,8 @@ export default {
<gl-tab
v-for="tab in tabs"
:key="`${tab.runnerType}`"
- :active="isTabActive(tab)"
- @click="onTabSelected(tab)"
+ :active="isTabActive(tab.runnerType)"
+ @click="onTabSelected(tab.runnerType)"
>
<template #title>
{{ tab.title }}
diff --git a/app/assets/javascripts/ci/runner/components/search_tokens/paused_token_config.js b/app/assets/javascripts/ci/runner/components/search_tokens/paused_token_config.js
index 97ee8ec3eef..71a145dd4a3 100644
--- a/app/assets/javascripts/ci/runner/components/search_tokens/paused_token_config.js
+++ b/app/assets/javascripts/ci/runner/components/search_tokens/paused_token_config.js
@@ -1,5 +1,5 @@
import { __ } from '~/locale';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
+import { OPERATORS_IS } from '~/vue_shared/components/filtered_search_bar/constants';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
import { PARAM_KEY_PAUSED, I18N_PAUSED } from '../../constants';
@@ -24,5 +24,5 @@ export const pausedTokenConfig = {
// see: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1438
title: title.replace(/\s/g, '\u00a0'),
})),
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
};
diff --git a/app/assets/javascripts/ci/runner/components/search_tokens/status_token_config.js b/app/assets/javascripts/ci/runner/components/search_tokens/status_token_config.js
index 117a630719e..4bc32909777 100644
--- a/app/assets/javascripts/ci/runner/components/search_tokens/status_token_config.js
+++ b/app/assets/javascripts/ci/runner/components/search_tokens/status_token_config.js
@@ -1,5 +1,5 @@
import {
- OPERATOR_IS_ONLY,
+ OPERATORS_IS,
TOKEN_TITLE_STATUS,
} from '~/vue_shared/components/filtered_search_bar/constants';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
@@ -38,5 +38,5 @@ export const statusTokenConfig = {
// see: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1438
title: title.replace(/\s/g, '\u00a0'),
})),
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
};
diff --git a/app/assets/javascripts/ci/runner/components/search_tokens/tag_token_config.js b/app/assets/javascripts/ci/runner/components/search_tokens/tag_token_config.js
index fdeba714385..369b214f952 100644
--- a/app/assets/javascripts/ci/runner/components/search_tokens/tag_token_config.js
+++ b/app/assets/javascripts/ci/runner/components/search_tokens/tag_token_config.js
@@ -1,5 +1,5 @@
import { s__ } from '~/locale';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
+import { OPERATORS_IS } from '~/vue_shared/components/filtered_search_bar/constants';
import { PARAM_KEY_TAG } from '../../constants';
import TagToken from './tag_token.vue';
@@ -8,5 +8,5 @@ export const tagTokenConfig = {
title: s__('Runners|Tags'),
type: PARAM_KEY_TAG,
token: TagToken,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
};
diff --git a/app/assets/javascripts/ci/runner/components/stat/runner_count.vue b/app/assets/javascripts/ci/runner/components/stat/runner_count.vue
index 4ad9259f59d..c33c42f3afe 100644
--- a/app/assets/javascripts/ci/runner/components/stat/runner_count.vue
+++ b/app/assets/javascripts/ci/runner/components/stat/runner_count.vue
@@ -16,13 +16,13 @@ import { INSTANCE_TYPE, GROUP_TYPE } from '../../constants';
* <strong/> tag.
*
* ```vue
- * <runner-count-stat
+ * <runner-count
* #default="{ count }"
* :scope="INSTANCE_TYPE"
* :variables="{ status: 'ONLINE' }"
* >
* <strong>{{ count }}</strong>
- * </runner-count-stat>
+ * </runner-count>
* ```
*
* Use `:skip="true"` to prevent data from being fetched and
diff --git a/app/assets/javascripts/ci/runner/components/stat/runner_stats.vue b/app/assets/javascripts/ci/runner/components/stat/runner_stats.vue
index 3965e5551f1..2e50dc13d2d 100644
--- a/app/assets/javascripts/ci/runner/components/stat/runner_stats.vue
+++ b/app/assets/javascripts/ci/runner/components/stat/runner_stats.vue
@@ -1,5 +1,4 @@
<script>
-import RunnerSingleStat from '~/ci/runner/components/stat/runner_single_stat.vue';
import {
I18N_STATUS_ONLINE,
I18N_STATUS_OFFLINE,
@@ -8,9 +7,19 @@ import {
STATUS_OFFLINE,
STATUS_STALE,
} from '../../constants';
+import RunnerSingleStat from './runner_single_stat.vue';
+import RunnerCount from './runner_count.vue';
+
+/**
+ * Shows general stats about the runners.
+ *
+ * First it checks if there are any runners in this context, and if so,
+ * shows more details for different status.
+ */
export default {
components: {
+ RunnerCount,
RunnerSingleStat,
RunnerUpgradeStatusStats: () =>
import('ee_component/ci/runner/components/stat/runner_upgrade_status_stats.vue'),
@@ -71,19 +80,21 @@ export default {
};
</script>
<template>
- <div class="gl-display-flex gl-flex-wrap gl-py-6">
- <runner-single-stat
- v-for="stat in stats"
- :key="stat.key"
- :scope="scope"
- v-bind="stat.props"
- class="gl-px-5"
- />
+ <runner-count #default="{ count }" :scope="scope" :variables="variables">
+ <div v-if="count" class="gl-display-flex gl-flex-wrap gl-py-6">
+ <runner-single-stat
+ v-for="stat in stats"
+ :key="stat.key"
+ :scope="scope"
+ v-bind="stat.props"
+ class="gl-px-5"
+ />
- <runner-upgrade-status-stats
- class="gl-display-contents"
- :scope="scope"
- :variables="variables"
- />
- </div>
+ <runner-upgrade-status-stats
+ class="gl-display-contents"
+ :scope="scope"
+ :variables="variables"
+ />
+ </div>
+ </runner-count>
</template>
diff --git a/app/assets/javascripts/ci/runner/constants.js b/app/assets/javascripts/ci/runner/constants.js
index dfc5f0c4152..31900a1fe89 100644
--- a/app/assets/javascripts/ci/runner/constants.js
+++ b/app/assets/javascripts/ci/runner/constants.js
@@ -32,6 +32,10 @@ export const I18N_STATUS_NEVER_CONTACTED = s__('Runners|Never contacted');
export const I18N_STATUS_OFFLINE = s__('Runners|Offline');
export const I18N_STATUS_STALE = s__('Runners|Stale');
+// Executor Status
+export const I18N_JOB_STATUS_RUNNING = s__('Runners|Running');
+export const I18N_JOB_STATUS_IDLE = s__('Runners|Idle');
+
// Status help popover
export const I18N_STATUS_POPOVER_TITLE = s__('Runners|Runner statuses');
@@ -82,6 +86,7 @@ export const I18N_DELETE_RUNNER = s__('Runners|Delete runner');
export const I18N_DELETED_TOAST = s__('Runners|Runner %{name} was deleted');
// List
+export const I18N_NO_DESCRIPTION = s__('Runners|No description');
export const I18N_LOCKED_RUNNER_DESCRIPTION = s__(
'Runners|Runner is locked and available for currently assigned projects only. Only administrators can change the assigned projects.',
);
@@ -94,6 +99,7 @@ export const I18N_ADMIN = s__('Runners|Administrator');
// Runner details
export const I18N_DETAILS = s__('Runners|Details');
+export const I18N_JOBS = s__('Runners|Jobs');
export const I18N_ASSIGNED_PROJECTS = s__('Runners|Assigned Projects (%{projectCount})');
export const I18N_FILTER_PROJECTS = s__('Runners|Filter projects');
export const I18N_CLEAR_FILTER_PROJECTS = __('Clear');
@@ -134,6 +140,11 @@ export const STATUS_NEVER_CONTACTED = 'NEVER_CONTACTED';
export const STATUS_OFFLINE = 'OFFLINE';
export const STATUS_STALE = 'STALE';
+// CiRunnerJobExecutionStatus
+
+export const JOB_STATUS_RUNNING = 'RUNNING';
+export const JOB_STATUS_IDLE = 'IDLE';
+
// CiRunnerAccessLevel
export const ACCESS_LEVEL_NOT_PROTECTED = 'NOT_PROTECTED';
diff --git a/app/assets/javascripts/ci/runner/graphql/list/list_item_shared.fragment.graphql b/app/assets/javascripts/ci/runner/graphql/list/list_item_shared.fragment.graphql
index 0dff011daaa..6f72509f599 100644
--- a/app/assets/javascripts/ci/runner/graphql/list/list_item_shared.fragment.graphql
+++ b/app/assets/javascripts/ci/runner/graphql/list/list_item_shared.fragment.graphql
@@ -12,6 +12,7 @@ fragment ListItemShared on CiRunner {
createdAt
contactedAt
status(legacyMode: null)
+ jobExecutionStatus
userPermissions {
updateRunner
deleteRunner
diff --git a/app/assets/javascripts/ci/runner/group_runners/group_runners_app.vue b/app/assets/javascripts/ci/runner/group_runners/group_runners_app.vue
index 91c22923075..57ceaa24b6e 100644
--- a/app/assets/javascripts/ci/runner/group_runners/group_runners_app.vue
+++ b/app/assets/javascripts/ci/runner/group_runners/group_runners_app.vue
@@ -82,6 +82,9 @@ export default {
apollo: {
runners: {
query: groupRunnersQuery,
+ context: {
+ isSingleRequest: true,
+ },
fetchPolicy: fetchPolicies.NETWORK_ONLY,
variables() {
return this.variables;
diff --git a/app/assets/javascripts/ci/runner/runner_search_utils.js b/app/assets/javascripts/ci/runner/runner_search_utils.js
index adc832b0600..3dc99baa329 100644
--- a/app/assets/javascripts/ci/runner/runner_search_utils.js
+++ b/app/assets/javascripts/ci/runner/runner_search_utils.js
@@ -176,6 +176,7 @@ export const fromSearchToUrl = (
[PARAM_KEY_RUNNER_TYPE]: [],
[PARAM_KEY_MEMBERSHIP]: [],
[PARAM_KEY_TAG]: [],
+ [PARAM_KEY_PAUSED]: [],
// Current filters
...filterToQueryObject(processFilters(filters), {
filteredSearchTermKey: PARAM_KEY_SEARCH,
diff --git a/app/assets/javascripts/ci_lint/components/ci_lint.vue b/app/assets/javascripts/ci_lint/components/ci_lint.vue
deleted file mode 100644
index 8db4cba529f..00000000000
--- a/app/assets/javascripts/ci_lint/components/ci_lint.vue
+++ /dev/null
@@ -1,131 +0,0 @@
-<script>
-import { GlButton, GlFormCheckbox, GlIcon, GlLink, GlAlert } from '@gitlab/ui';
-import CiLintResults from '~/pipeline_editor/components/lint/ci_lint_results.vue';
-import lintCiMutation from '~/pipeline_editor/graphql/mutations/client/lint_ci.mutation.graphql';
-import SourceEditor from '~/vue_shared/components/source_editor.vue';
-
-export default {
- components: {
- GlButton,
- GlFormCheckbox,
- GlIcon,
- GlLink,
- GlAlert,
- CiLintResults,
- SourceEditor,
- },
- props: {
- endpoint: {
- type: String,
- required: true,
- },
- lintHelpPagePath: {
- type: String,
- required: true,
- },
- pipelineSimulationHelpPagePath: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- content: '',
- loading: false,
- isValid: false,
- errors: null,
- warnings: null,
- jobs: [],
- dryRun: false,
- showingResults: false,
- apiError: null,
- isErrorDismissed: false,
- };
- },
- computed: {
- shouldShowError() {
- return this.apiError && !this.isErrorDismissed;
- },
- },
- methods: {
- async lint() {
- this.loading = true;
- try {
- const {
- data: {
- lintCI: { valid, errors, warnings, jobs },
- },
- } = await this.$apollo.mutate({
- mutation: lintCiMutation,
- variables: { endpoint: this.endpoint, content: this.content, dry: this.dryRun },
- });
-
- this.showingResults = true;
- this.isValid = valid;
- this.errors = errors;
- this.warnings = warnings;
- this.jobs = jobs;
- } catch (error) {
- this.apiError = error;
- this.isErrorDismissed = false;
- } finally {
- this.loading = false;
- }
- },
- clear() {
- this.content = '';
- },
- },
-};
-</script>
-
-<template>
- <div class="row">
- <div class="col-sm-12">
- <gl-alert
- v-if="shouldShowError"
- class="gl-mb-3"
- variant="danger"
- @dismiss="isErrorDismissed = true"
- >{{ apiError }}</gl-alert
- >
- <div class="file-holder gl-mb-3">
- <div class="js-file-title file-title clearfix">
- {{ __('Contents of .gitlab-ci.yml') }}
- </div>
- <source-editor v-model="content" file-name="*.yml" />
- </div>
- </div>
-
- <div class="col-sm-12 gl-display-flex gl-justify-content-space-between">
- <div class="gl-display-flex gl-align-items-center">
- <gl-button
- class="gl-mr-4"
- :loading="loading"
- category="primary"
- variant="confirm"
- data-testid="ci-lint-validate"
- @click="lint"
- >{{ __('Validate') }}</gl-button
- >
- <gl-form-checkbox v-model="dryRun"
- >{{ __('Simulate a pipeline created for the default branch') }}
- <gl-link :href="pipelineSimulationHelpPagePath" target="_blank"
- ><gl-icon class="gl-text-blue-600" name="question-o" /></gl-link
- ></gl-form-checkbox>
- </div>
- <gl-button data-testid="ci-lint-clear" @click="clear">{{ __('Clear') }}</gl-button>
- </div>
-
- <ci-lint-results
- v-if="showingResults"
- class="col-sm-12 gl-mt-5"
- :is-valid="isValid"
- :jobs="jobs"
- :errors="errors"
- :warnings="warnings"
- :dry-run="dryRun"
- :lint-help-page-path="lintHelpPagePath"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/ci_lint/index.js b/app/assets/javascripts/ci_lint/index.js
deleted file mode 100644
index 274aab45deb..00000000000
--- a/app/assets/javascripts/ci_lint/index.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createDefaultClient from '~/lib/graphql';
-import { resolvers } from '~/pipeline_editor/graphql/resolvers';
-
-import CiLint from './components/ci_lint.vue';
-
-Vue.use(VueApollo);
-
-const apolloProvider = new VueApollo({
- defaultClient: createDefaultClient(resolvers),
-});
-
-export default (containerId = '#js-ci-lint') => {
- const containerEl = document.querySelector(containerId);
- const { endpoint, lintHelpPagePath, pipelineSimulationHelpPagePath } = containerEl.dataset;
-
- return new Vue({
- el: containerEl,
- apolloProvider,
- render(createElement) {
- return createElement(CiLint, {
- props: {
- endpoint,
- lintHelpPagePath,
- pipelineSimulationHelpPagePath,
- },
- });
- },
- });
-};
diff --git a/app/assets/javascripts/ci_secure_files/components/secure_files_list.vue b/app/assets/javascripts/ci_secure_files/components/secure_files_list.vue
index 9d8cb40b60a..661389f4059 100644
--- a/app/assets/javascripts/ci_secure_files/components/secure_files_list.vue
+++ b/app/assets/javascripts/ci_secure_files/components/secure_files_list.vue
@@ -13,7 +13,7 @@ import {
} from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import Api, { DEFAULT_PER_PAGE } from '~/api';
-import httpStatusCodes from '~/lib/utils/http_status';
+import { HTTP_STATUS_PAYLOAD_TOO_LARGE } from '~/lib/utils/http_status';
import { __, s__, sprintf } from '~/locale';
import Tracking from '~/tracking';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
@@ -145,7 +145,7 @@ export default {
let message = '';
if (error?.response?.data?.message?.name) {
message = this.$options.i18n.uploadErrorMessages.duplicate;
- } else if (error.response.status === httpStatusCodes.PAYLOAD_TOO_LARGE) {
+ } else if (error.response.status === HTTP_STATUS_PAYLOAD_TOO_LARGE) {
message = sprintf(this.$options.i18n.uploadErrorMessages.tooLarge, {
limit: this.fileSizeLimit,
});
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_group_variables.vue b/app/assets/javascripts/ci_variable_list/components/ci_group_variables.vue
index c8f5ac1736d..4466a6a8081 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_group_variables.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_group_variables.vue
@@ -46,6 +46,7 @@ export default {
:id="graphqlId"
:are-scoped-variables-available="areScopedVariablesAvailable"
component-name="GroupVariables"
+ entity="group"
:full-path="groupPath"
:mutation-data="$options.mutationData"
:query-data="$options.queryData"
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_project_variables.vue b/app/assets/javascripts/ci_variable_list/components/ci_project_variables.vue
index 2c4818e20c1..6326940148a 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_project_variables.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_project_variables.vue
@@ -48,6 +48,7 @@ export default {
:id="graphqlId"
:are-scoped-variables-available="true"
component-name="ProjectVariables"
+ entity="project"
:full-path="projectFullPath"
:mutation-data="$options.mutationData"
:query-data="$options.queryData"
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
index 94f8cb9e906..00177539cdc 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
@@ -29,6 +29,7 @@ import {
ENVIRONMENT_SCOPE_LINK_TITLE,
EVENT_LABEL,
EVENT_ACTION,
+ EXPANDED_VARIABLES_NOTE,
EDIT_VARIABLE_ACTION,
VARIABLE_ACTIONS,
variableOptions,
@@ -46,6 +47,7 @@ export default {
awsTipMessage: AWS_TIP_MESSAGE,
containsVariableReferenceMessage: CONTAINS_VARIABLE_REFERENCE_MESSAGE,
environmentScopeLinkTitle: ENVIRONMENT_SCOPE_LINK_TITLE,
+ expandedVariablesNote: EXPANDED_VARIABLES_NOTE,
components: {
CiEnvironmentsDropdown,
GlAlert,
@@ -127,7 +129,7 @@ export default {
},
containsVariableReference() {
const regex = /\$/;
- return regex.test(this.variable.value);
+ return regex.test(this.variable.value) && this.isExpanded;
},
displayMaskedError() {
return !this.canMask && this.variable.masked;
@@ -135,6 +137,9 @@ export default {
isEditing() {
return this.mode === EDIT_VARIABLE_ACTION;
},
+ isExpanded() {
+ return !this.variable.raw;
+ },
isTipVisible() {
return !this.isTipDismissed && AWS_TOKEN_CONSTANTS.includes(this.variable.key);
},
@@ -208,6 +213,9 @@ export default {
hideModal() {
this.$refs.modal.hide();
},
+ onShow() {
+ this.setVariableProtectedByDefault();
+ },
resetModalHandler() {
this.resetVariableData();
this.resetValidationErrorEvents();
@@ -220,6 +228,9 @@ export default {
setEnvironmentScope(scope) {
this.variable = { ...this.variable, environmentScope: scope };
},
+ setVariableRaw(expanded) {
+ this.variable = { ...this.variable, raw: !expanded };
+ },
setVariableProtected() {
this.variable = { ...this.variable, protected: true };
},
@@ -275,7 +286,7 @@ export default {
static
lazy
@hidden="resetModalHandler"
- @shown="setVariableProtectedByDefault"
+ @shown="onShow"
>
<form>
<gl-form-combobox
@@ -304,6 +315,13 @@ export default {
class="gl-font-monospace!"
spellcheck="false"
/>
+ <p
+ v-if="variable.raw"
+ class="gl-mt-2 gl-mb-0 text-secondary"
+ data-testid="raw-variable-tip"
+ >
+ {{ __('Variable value will be evaluated as raw string.') }}
+ </p>
</gl-form-group>
<div class="gl-display-flex">
@@ -361,7 +379,6 @@ export default {
{{ __('Export variable to pipelines running on protected branches and tags only.') }}
</p>
</gl-form-checkbox>
-
<gl-form-checkbox
ref="masked-ci-variable"
v-model="variable.masked"
@@ -371,7 +388,7 @@ export default {
<gl-link target="_blank" :href="maskedEnvironmentVariablesLink">
<gl-icon name="question" :size="12" />
</gl-link>
- <p class="gl-mt-2 gl-mb-0 text-secondary">
+ <p class="gl-mt-2 text-secondary">
{{ __('Variable will be masked in job logs.') }}
<span
:class="{
@@ -385,6 +402,24 @@ export default {
}}</gl-link>
</p>
</gl-form-checkbox>
+ <gl-form-checkbox
+ ref="expanded-ci-variable"
+ :checked="isExpanded"
+ data-testid="ci-variable-expanded-checkbox"
+ @change="setVariableRaw"
+ >
+ {{ __('Expand variable reference') }}
+ <gl-link target="_blank" :href="containsVariableReferenceLink">
+ <gl-icon name="question" :size="12" />
+ </gl-link>
+ <p class="gl-mt-2 gl-mb-0 gl-text-secondary">
+ <gl-sprintf :message="$options.expandedVariablesNote">
+ <template #code="{ content }">
+ <code>{{ content }}</code>
+ </template>
+ </gl-sprintf>
+ </p>
+ </gl-form-checkbox>
</gl-form-group>
</form>
<gl-collapse :visible="isTipVisible">
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue
index 94fd6c3892c..3c6114b38ce 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue
@@ -14,6 +14,11 @@ export default {
required: false,
default: false,
},
+ entity: {
+ type: String,
+ required: false,
+ default: '',
+ },
environments: {
type: Array,
required: false,
@@ -27,7 +32,11 @@ export default {
isLoading: {
type: Boolean,
required: false,
- default: false,
+ },
+ maxVariableLimit: {
+ type: Number,
+ required: false,
+ default: 0,
},
variables: {
type: Array,
@@ -75,7 +84,9 @@ export default {
<div class="row">
<div class="col-lg-12">
<ci-variable-table
+ :entity="entity"
:is-loading="isLoading"
+ :max-variable-limit="maxVariableLimit"
:variables="variables"
@set-selected-variable="setSelectedVariable"
/>
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_shared.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_shared.vue
index 7ee250cea98..6e39bda0b07 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_variable_shared.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_shared.vue
@@ -26,6 +26,11 @@ export default {
required: true,
type: String,
},
+ entity: {
+ required: false,
+ type: String,
+ default: '',
+ },
fullPath: {
required: false,
type: String,
@@ -90,6 +95,7 @@ export default {
isInitialLoading: true,
isLoadingMoreItems: false,
loadingCounter: 0,
+ maxVariableLimit: 0,
pageInfo: {},
};
},
@@ -107,6 +113,8 @@ export default {
return this.queryData.ciVariables.lookup(data)?.nodes || [];
},
result({ data }) {
+ this.maxVariableLimit = this.queryData.ciVariables.lookup(data)?.limit || 0;
+
this.pageInfo = this.queryData.ciVariables.lookup(data)?.pageInfo || this.pageInfo;
this.hasNextPage = this.pageInfo?.hasNextPage || false;
@@ -221,9 +229,11 @@ export default {
<template>
<ci-variable-settings
:are-scoped-variables-available="areScopedVariablesAvailable"
+ :entity="entity"
:hide-environment-scope="hideEnvironmentScope"
:is-loading="isLoading"
:variables="ciVariables"
+ :max-variable-limit="maxVariableLimit"
:environments="environments"
@add-variable="addVariable"
@delete-variable="deleteVariable"
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue
index 3cdcb68e919..345a8def49d 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue
@@ -1,8 +1,21 @@
<script>
-import { GlButton, GlLoadingIcon, GlModalDirective, GlTable, GlTooltipDirective } from '@gitlab/ui';
-import { s__, __ } from '~/locale';
+import {
+ GlAlert,
+ GlButton,
+ GlLoadingIcon,
+ GlModalDirective,
+ GlTable,
+ GlTooltipDirective,
+} from '@gitlab/ui';
+import { __, s__, sprintf } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import { ADD_CI_VARIABLE_MODAL_ID, variableText } from '../constants';
+import {
+ ADD_CI_VARIABLE_MODAL_ID,
+ DEFAULT_EXCEEDS_VARIABLE_LIMIT_TEXT,
+ EXCEEDS_VARIABLE_LIMIT_TEXT,
+ MAXIMUM_VARIABLE_LIMIT_REACHED,
+ variableText,
+} from '../constants';
import { convertEnvironmentScope } from '../utils';
export default {
@@ -41,6 +54,7 @@ export default {
},
],
components: {
+ GlAlert,
GlButton,
GlLoadingIcon,
GlTable,
@@ -51,10 +65,19 @@ export default {
},
mixins: [glFeatureFlagsMixin()],
props: {
+ entity: {
+ type: String,
+ required: false,
+ default: '',
+ },
isLoading: {
type: Boolean,
required: true,
},
+ maxVariableLimit: {
+ type: Number,
+ required: true,
+ },
variables: {
type: Array,
required: true,
@@ -66,6 +89,23 @@ export default {
};
},
computed: {
+ exceedsVariableLimit() {
+ return this.maxVariableLimit > 0 && this.variables.length >= this.maxVariableLimit;
+ },
+ exceedsVariableLimitText() {
+ if (this.exceedsVariableLimit && this.entity) {
+ return sprintf(EXCEEDS_VARIABLE_LIMIT_TEXT, {
+ entity: this.entity,
+ currentVariableCount: this.variables.length,
+ maxVariableLimit: this.maxVariableLimit,
+ });
+ }
+
+ return DEFAULT_EXCEEDS_VARIABLE_LIMIT_TEXT;
+ },
+ showAlert() {
+ return !this.isLoading && this.exceedsVariableLimit;
+ },
valuesButtonText() {
return this.areValuesHidden ? __('Reveal values') : __('Hide values');
},
@@ -104,17 +144,29 @@ export default {
if (item.masked) {
options.push(s__('CiVariables|Masked'));
}
+ if (!item.raw) {
+ options.push(s__('CiVariables|Expanded'));
+ }
return options.join(', ');
},
},
+ maximumVariableLimitReached: MAXIMUM_VARIABLE_LIMIT_REACHED,
};
</script>
<template>
<div class="ci-variable-table" data-testid="ci-variable-table">
<gl-loading-icon v-if="isLoading" />
+ <gl-alert
+ v-if="showAlert"
+ :dismissible="false"
+ :title="$options.maximumVariableLimitReached"
+ variant="info"
+ >
+ {{ exceedsVariableLimitText }}
+ </gl-alert>
<gl-table
- v-else
+ v-if="!isLoading"
:fields="fields"
:items="variablesWithOptions"
tbody-tr-class="js-ci-variable-row"
@@ -178,7 +230,7 @@ export default {
</div>
</template>
<template #cell(options)="{ item }">
- <span>{{ item.options }}</span>
+ <span data-testid="ci-variable-table-row-options">{{ item.options }}</span>
</template>
<template #cell(environmentScope)="{ item }">
<div
@@ -215,6 +267,14 @@ export default {
</p>
</template>
</gl-table>
+ <gl-alert
+ v-if="showAlert"
+ :dismissible="false"
+ :title="$options.maximumVariableLimitReached"
+ variant="info"
+ >
+ {{ exceedsVariableLimitText }}
+ </gl-alert>
<div class="ci-variable-actions gl-display-flex gl-mt-5">
<gl-button
v-gl-modal-directive="$options.modalId"
@@ -223,6 +283,7 @@ export default {
variant="confirm"
category="primary"
:aria-label="__('Add')"
+ :disabled="exceedsVariableLimit"
@click="setSelectedVariable()"
>{{ __('Add variable') }}</gl-button
>
diff --git a/app/assets/javascripts/ci_variable_list/constants.js b/app/assets/javascripts/ci_variable_list/constants.js
index ccad08ef8b6..828d0724d93 100644
--- a/app/assets/javascripts/ci_variable_list/constants.js
+++ b/app/assets/javascripts/ci_variable_list/constants.js
@@ -1,4 +1,4 @@
-import { __ } from '~/locale';
+import { __, s__ } from '~/locale';
export const ADD_CI_VARIABLE_MODAL_ID = 'add-ci-variable';
@@ -43,6 +43,7 @@ export const defaultVariableState = {
key: '',
masked: false,
protected: false,
+ raw: false,
value: '',
variableType: variableTypes.envType,
};
@@ -69,10 +70,19 @@ export const AWS_SECRET_ACCESS_KEY = 'AWS_SECRET_ACCESS_KEY';
export const AWS_TOKEN_CONSTANTS = [AWS_ACCESS_KEY_ID, AWS_DEFAULT_REGION, AWS_SECRET_ACCESS_KEY];
export const CONTAINS_VARIABLE_REFERENCE_MESSAGE = __(
- 'Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}',
+ 'Unselect "Expand variable reference" if you want to use the variable value as a raw string.',
);
export const ENVIRONMENT_SCOPE_LINK_TITLE = __('Learn more');
+export const EXCEEDS_VARIABLE_LIMIT_TEXT = s__(
+ 'CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables.',
+);
+export const DEFAULT_EXCEEDS_VARIABLE_LIMIT_TEXT = s__(
+ 'CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables.',
+);
+export const MAXIMUM_VARIABLE_LIMIT_REACHED = s__(
+ 'CiVariables|Maximum number of variables reached.',
+);
export const ADD_VARIABLE_ACTION = 'ADD_VARIABLE';
export const EDIT_VARIABLE_ACTION = 'EDIT_VARIABLE';
@@ -85,6 +95,10 @@ export const ADD_MUTATION_ACTION = 'add';
export const UPDATE_MUTATION_ACTION = 'update';
export const DELETE_MUTATION_ACTION = 'delete';
+export const EXPANDED_VARIABLES_NOTE = __(
+ '%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable.',
+);
+
export const environmentFetchErrorText = __(
'There was an error fetching the environments information.',
);
diff --git a/app/assets/javascripts/ci_variable_list/graphql/mutations/group_add_variable.mutation.graphql b/app/assets/javascripts/ci_variable_list/graphql/mutations/group_add_variable.mutation.graphql
index c44ee2ecc1d..24388637672 100644
--- a/app/assets/javascripts/ci_variable_list/graphql/mutations/group_add_variable.mutation.graphql
+++ b/app/assets/javascripts/ci_variable_list/graphql/mutations/group_add_variable.mutation.graphql
@@ -16,6 +16,7 @@ mutation addGroupVariable($variable: CiVariable!, $endpoint: String!, $fullPath:
environmentScope
masked
protected
+ raw
}
}
}
diff --git a/app/assets/javascripts/ci_variable_list/graphql/mutations/group_delete_variable.mutation.graphql b/app/assets/javascripts/ci_variable_list/graphql/mutations/group_delete_variable.mutation.graphql
index 53e9b411dd2..f7c8e209ccd 100644
--- a/app/assets/javascripts/ci_variable_list/graphql/mutations/group_delete_variable.mutation.graphql
+++ b/app/assets/javascripts/ci_variable_list/graphql/mutations/group_delete_variable.mutation.graphql
@@ -16,6 +16,7 @@ mutation deleteGroupVariable($variable: CiVariable!, $endpoint: String!, $fullPa
environmentScope
masked
protected
+ raw
}
}
}
diff --git a/app/assets/javascripts/ci_variable_list/graphql/mutations/group_update_variable.mutation.graphql b/app/assets/javascripts/ci_variable_list/graphql/mutations/group_update_variable.mutation.graphql
index 2dddca14bd8..757e61a5cd3 100644
--- a/app/assets/javascripts/ci_variable_list/graphql/mutations/group_update_variable.mutation.graphql
+++ b/app/assets/javascripts/ci_variable_list/graphql/mutations/group_update_variable.mutation.graphql
@@ -16,6 +16,7 @@ mutation updateGroupVariable($variable: CiVariable!, $endpoint: String!, $fullPa
environmentScope
masked
protected
+ raw
}
}
}
diff --git a/app/assets/javascripts/ci_variable_list/graphql/mutations/project_add_variable.mutation.graphql b/app/assets/javascripts/ci_variable_list/graphql/mutations/project_add_variable.mutation.graphql
index 39504770e33..fa315084d86 100644
--- a/app/assets/javascripts/ci_variable_list/graphql/mutations/project_add_variable.mutation.graphql
+++ b/app/assets/javascripts/ci_variable_list/graphql/mutations/project_add_variable.mutation.graphql
@@ -16,6 +16,7 @@ mutation addProjectVariable($variable: CiVariable!, $endpoint: String!, $fullPat
environmentScope
masked
protected
+ raw
}
}
}
diff --git a/app/assets/javascripts/ci_variable_list/graphql/mutations/project_delete_variable.mutation.graphql b/app/assets/javascripts/ci_variable_list/graphql/mutations/project_delete_variable.mutation.graphql
index f55c255e332..c3358cc35b9 100644
--- a/app/assets/javascripts/ci_variable_list/graphql/mutations/project_delete_variable.mutation.graphql
+++ b/app/assets/javascripts/ci_variable_list/graphql/mutations/project_delete_variable.mutation.graphql
@@ -21,6 +21,7 @@ mutation deleteProjectVariable(
environmentScope
masked
protected
+ raw
}
}
}
diff --git a/app/assets/javascripts/ci_variable_list/graphql/mutations/project_update_variable.mutation.graphql b/app/assets/javascripts/ci_variable_list/graphql/mutations/project_update_variable.mutation.graphql
index fc589e8a939..fde92cef4cb 100644
--- a/app/assets/javascripts/ci_variable_list/graphql/mutations/project_update_variable.mutation.graphql
+++ b/app/assets/javascripts/ci_variable_list/graphql/mutations/project_update_variable.mutation.graphql
@@ -21,6 +21,7 @@ mutation updateProjectVariable(
environmentScope
masked
protected
+ raw
}
}
}
diff --git a/app/assets/javascripts/ci_variable_list/graphql/queries/group_variables.query.graphql b/app/assets/javascripts/ci_variable_list/graphql/queries/group_variables.query.graphql
index b5555fe4401..900154cd24d 100644
--- a/app/assets/javascripts/ci_variable_list/graphql/queries/group_variables.query.graphql
+++ b/app/assets/javascripts/ci_variable_list/graphql/queries/group_variables.query.graphql
@@ -5,6 +5,7 @@ query getGroupVariables($after: String, $first: Int = 100, $fullPath: ID!) {
group(fullPath: $fullPath) {
id
ciVariables(after: $after, first: $first) {
+ limit
pageInfo {
...PageInfo
}
@@ -14,6 +15,7 @@ query getGroupVariables($after: String, $first: Int = 100, $fullPath: ID!) {
environmentScope
masked
protected
+ raw
}
}
}
diff --git a/app/assets/javascripts/ci_variable_list/graphql/queries/project_variables.query.graphql b/app/assets/javascripts/ci_variable_list/graphql/queries/project_variables.query.graphql
index 08b5bf7af16..ee75eba7547 100644
--- a/app/assets/javascripts/ci_variable_list/graphql/queries/project_variables.query.graphql
+++ b/app/assets/javascripts/ci_variable_list/graphql/queries/project_variables.query.graphql
@@ -5,6 +5,7 @@ query getProjectVariables($after: String, $first: Int = 100, $fullPath: ID!) {
project(fullPath: $fullPath) {
id
ciVariables(after: $after, first: $first) {
+ limit
pageInfo {
...PageInfo
}
@@ -13,6 +14,7 @@ query getProjectVariables($after: String, $first: Int = 100, $fullPath: ID!) {
environmentScope
masked
protected
+ raw
}
}
}
diff --git a/app/assets/javascripts/ci_variable_list/graphql/queries/variables.query.graphql b/app/assets/javascripts/ci_variable_list/graphql/queries/variables.query.graphql
index 2667d6606fe..9b255c3c182 100644
--- a/app/assets/javascripts/ci_variable_list/graphql/queries/variables.query.graphql
+++ b/app/assets/javascripts/ci_variable_list/graphql/queries/variables.query.graphql
@@ -11,6 +11,7 @@ query getVariables($after: String, $first: Int = 100) {
... on CiInstanceVariable {
masked
protected
+ raw
}
}
}
diff --git a/app/assets/javascripts/clusters_list/clusters_util.js b/app/assets/javascripts/clusters_list/clusters_util.js
index ee36a295513..25a8426500e 100644
--- a/app/assets/javascripts/clusters_list/clusters_util.js
+++ b/app/assets/javascripts/clusters_list/clusters_util.js
@@ -1,10 +1,14 @@
-import { ACTIVE_CONNECTION_TIME } from './constants';
+import { ACTIVE_CONNECTION_TIME, NAME_MAX_LENGTH } from './constants';
+
+function getTruncatedName(name) {
+ return name.substring(0, NAME_MAX_LENGTH);
+}
export function generateAgentRegistrationCommand({ name, token, version, address }) {
return `helm repo add gitlab https://charts.gitlab.io
helm repo update
helm upgrade --install ${name} gitlab/gitlab-agent \\
- --namespace gitlab-agent \\
+ --namespace gitlab-agent-${getTruncatedName(name)} \\
--create-namespace \\
--set image.tag=v${version} \\
--set config.token=${token} \\
diff --git a/app/assets/javascripts/clusters_list/components/agent_token.vue b/app/assets/javascripts/clusters_list/components/agent_token.vue
index 4dd6d84566c..93c37226a09 100644
--- a/app/assets/javascripts/clusters_list/components/agent_token.vue
+++ b/app/assets/javascripts/clusters_list/components/agent_token.vue
@@ -1,22 +1,24 @@
<script>
-import { GlAlert, GlFormInputGroup, GlLink, GlSprintf } from '@gitlab/ui';
+import { GlAlert, GlFormInputGroup, GlLink, GlSprintf, GlIcon } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
import CodeBlock from '~/vue_shared/components/code_block.vue';
import { generateAgentRegistrationCommand } from '../clusters_util';
-import { I18N_AGENT_TOKEN } from '../constants';
+import { I18N_AGENT_TOKEN, HELM_VERSION_POLICY_URL } from '../constants';
export default {
i18n: I18N_AGENT_TOKEN,
advancedInstallPath: helpPagePath('user/clusters/agent/install/index', {
anchor: 'advanced-installation-method',
}),
+ HELM_VERSION_POLICY_URL,
components: {
GlAlert,
CodeBlock,
GlFormInputGroup,
GlLink,
GlSprintf,
+ GlIcon,
ModalCopyButton,
},
inject: ['kasAddress', 'kasVersion'],
@@ -77,6 +79,11 @@ export default {
<p>
{{ $options.i18n.basicInstallBody }}
+ <gl-sprintf :message="$options.i18n.helmVersionText">
+ <template #link="{ content }"
+ ><gl-link :href="$options.HELM_VERSION_POLICY_URL" target="_blank"
+ >{{ content }} <gl-icon name="external-link" :size="12" /></gl-link></template
+ ></gl-sprintf>
</p>
<p class="gl-display-flex gl-align-items-flex-start">
diff --git a/app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue b/app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue
index bde76c46b4b..365e0384d87 100644
--- a/app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue
+++ b/app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue
@@ -1,23 +1,13 @@
<script>
-import {
- GlDropdown,
- GlDropdownItem,
- GlDropdownDivider,
- GlDropdownText,
- GlSearchBoxByType,
- GlSprintf,
-} from '@gitlab/ui';
+import { GlCollapsibleListbox, GlButton, GlSprintf } from '@gitlab/ui';
import { I18N_AVAILABLE_AGENTS_DROPDOWN } from '../constants';
export default {
name: 'AvailableAgentsDropdown',
i18n: I18N_AVAILABLE_AGENTS_DROPDOWN,
components: {
- GlDropdown,
- GlDropdownItem,
- GlDropdownDivider,
- GlDropdownText,
- GlSearchBoxByType,
+ GlCollapsibleListbox,
+ GlButton,
GlSprintf,
},
props: {
@@ -46,13 +36,21 @@ export default {
return this.selectedAgent;
},
+ dropdownItems() {
+ return this.availableAgents.map((agent) => {
+ return {
+ value: agent,
+ text: agent,
+ };
+ });
+ },
shouldRenderCreateButton() {
return this.searchTerm && !this.availableAgents.includes(this.searchTerm);
},
filteredResults() {
const lowerCasedSearchTerm = this.searchTerm.toLowerCase();
- return this.availableAgents.filter((resultString) =>
- resultString.toLowerCase().includes(lowerCasedSearchTerm),
+ return this.dropdownItems.filter((item) =>
+ item.value.toLowerCase().includes(lowerCasedSearchTerm),
);
},
},
@@ -60,59 +58,48 @@ export default {
selectAgent(agent) {
this.$emit('agentSelected', agent);
this.selectedAgent = agent;
- this.clearSearch();
- },
- isSelected(agent) {
- return this.selectedAgent === agent;
- },
- clearSearch() {
- this.searchTerm = '';
- },
- focusSearch() {
- this.$refs.searchInput.focusInput();
- },
- handleShow() {
- this.clearSearch();
- this.focusSearch();
+
+ this.$refs.dropdown.closeAndFocus();
},
onKeyEnter() {
if (!this.searchTerm?.length) {
return;
}
- this.$refs.dropdown.hide();
this.selectAgent(this.searchTerm);
},
+ searchAgent(searchQuery) {
+ this.searchTerm = searchQuery;
+ },
},
};
</script>
<template>
- <gl-dropdown ref="dropdown" :text="dropdownText" :loading="isRegistering" @shown="handleShow">
- <template #header>
- <gl-search-box-by-type
- ref="searchInput"
- v-model.trim="searchTerm"
- @keydown.enter.stop.prevent="onKeyEnter"
- />
- </template>
- <gl-dropdown-item
- v-for="agent in filteredResults"
- :key="agent"
- :is-checked="isSelected(agent)"
- is-check-item
- @click="selectAgent(agent)"
+ <div @keydown.enter.stop.prevent="onKeyEnter">
+ <gl-collapsible-listbox
+ ref="dropdown"
+ v-model="selectedAgent"
+ class="gl-w-full"
+ toggle-class="select-agent-dropdown"
+ :items="filteredResults"
+ :toggle-text="dropdownText"
+ :loading="isRegistering"
+ :searchable="true"
+ :no-results-text="$options.i18n.noResults"
+ @search="searchAgent"
+ @select="selectAgent"
>
- {{ agent }}
- </gl-dropdown-item>
- <gl-dropdown-text v-if="!filteredResults.length" ref="noMatchingResults">{{
- $options.i18n.noResults
- }}</gl-dropdown-text>
- <template v-if="shouldRenderCreateButton">
- <gl-dropdown-divider />
- <gl-dropdown-item data-testid="create-config-button" @click="selectAgent(searchTerm)">
- <gl-sprintf :message="$options.i18n.createButton">
- <template #searchTerm>{{ searchTerm }}</template>
- </gl-sprintf>
- </gl-dropdown-item>
- </template>
- </gl-dropdown>
+ <template v-if="shouldRenderCreateButton" #footer>
+ <gl-button
+ category="tertiary"
+ class="gl-justify-content-start! gl-border-t-1! gl-border-t-solid gl-border-t-gray-200 gl-pl-7! gl-rounded-top-left-none! gl-rounded-top-right-none!"
+ :class="{ 'gl-mt-3': !filteredResults.length }"
+ @click="selectAgent(searchTerm)"
+ >
+ <gl-sprintf :message="$options.i18n.createButton">
+ <template #searchTerm>{{ searchTerm }}</template>
+ </gl-sprintf>
+ </gl-button>
+ </template>
+ </gl-collapsible-listbox>
+ </div>
</template>
diff --git a/app/assets/javascripts/clusters_list/constants.js b/app/assets/javascripts/clusters_list/constants.js
index 7bc8a1a7304..615754459d6 100644
--- a/app/assets/javascripts/clusters_list/constants.js
+++ b/app/assets/javascripts/clusters_list/constants.js
@@ -4,6 +4,7 @@ import { helpPagePath } from '~/helpers/help_page_helper';
export const MAX_LIST_COUNT = 25;
export const INSTALL_AGENT_MODAL_ID = 'install-agent';
export const ACTIVE_CONNECTION_TIME = 480000;
+export const NAME_MAX_LENGTH = 50;
export const CLUSTER_ERRORS = {
default: {
@@ -100,6 +101,9 @@ export const I18N_AGENT_TOKEN = {
basicInstallBody: s__(
'ClusterAgents|From a terminal, connect to your cluster and run this command. The token is included in the command.',
),
+ helmVersionText: s__(
+ 'ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd}).',
+ ),
advancedInstallTitle: s__('ClusterAgents|Advanced installation methods'),
advancedInstallBody: s__(
@@ -107,6 +111,8 @@ export const I18N_AGENT_TOKEN = {
),
};
+export const HELM_VERSION_POLICY_URL = 'https://helm.sh/docs/topics/version_skew/';
+
export const I18N_AGENT_MODAL = {
registerAgentButton: s__('ClusterAgents|Register'),
close: __('Close'),
diff --git a/app/assets/javascripts/constants.js b/app/assets/javascripts/constants.js
new file mode 100644
index 00000000000..c56d45166a0
--- /dev/null
+++ b/app/assets/javascripts/constants.js
@@ -0,0 +1,3 @@
+import { s__ } from '~/locale';
+
+export const MODIFIER_KEY = window.gl?.client?.isMac ? '⌘' : s__('KeyboardKey|Ctrl+');
diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue b/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue
index a9668ebdb69..98b7203778f 100644
--- a/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue
+++ b/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue
@@ -166,9 +166,7 @@ export default {
icon="arrow-left"
@click.prevent.stop="showCustomLanguageInput = false"
/>
- <p
- class="gl-text-center gl-new-dropdown-header-top gl-mb-0! gl-border-none! gl-pb-1!"
- >
+ <p class="gl-text-center gl-dropdown-header-top gl-mb-0! gl-border-none! gl-pb-1!">
{{ __('Create custom type') }}
</p>
</div>
diff --git a/app/assets/javascripts/content_editor/components/content_editor.vue b/app/assets/javascripts/content_editor/components/content_editor.vue
index 22381377389..53a37fc0c51 100644
--- a/app/assets/javascripts/content_editor/components/content_editor.vue
+++ b/app/assets/javascripts/content_editor/components/content_editor.vue
@@ -11,7 +11,7 @@ import FormattingBubbleMenu from './bubble_menus/formatting_bubble_menu.vue';
import CodeBlockBubbleMenu from './bubble_menus/code_block_bubble_menu.vue';
import LinkBubbleMenu from './bubble_menus/link_bubble_menu.vue';
import MediaBubbleMenu from './bubble_menus/media_bubble_menu.vue';
-import TopToolbar from './top_toolbar.vue';
+import FormattingToolbar from './formatting_toolbar.vue';
import LoadingIndicator from './loading_indicator.vue';
export default {
@@ -20,7 +20,7 @@ export default {
ContentEditorAlert,
ContentEditorProvider,
TiptapEditorContent,
- TopToolbar,
+ FormattingToolbar,
FormattingBubbleMenu,
CodeBlockBubbleMenu,
LinkBubbleMenu,
@@ -57,6 +57,11 @@ export default {
default: false,
validator: (autofocus) => TIPTAP_AUTOFOCUS_OPTIONS.includes(autofocus),
},
+ useBottomToolbar: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -163,8 +168,8 @@ export default {
class="md-area"
:class="{ 'is-focused': focused }"
>
- <top-toolbar ref="toolbar" class="gl-mb-4" />
- <div class="gl-relative">
+ <formatting-toolbar v-if="!useBottomToolbar" ref="toolbar" class="gl-border-b" />
+ <div class="gl-relative gl-mt-4">
<formatting-bubble-menu />
<code-block-bubble-menu />
<link-bubble-menu />
@@ -176,6 +181,7 @@ export default {
/>
<loading-indicator v-if="isLoading" />
</div>
+ <formatting-toolbar v-if="useBottomToolbar" ref="toolbar" class="gl-border-t" />
</div>
</div>
</content-editor-provider>
diff --git a/app/assets/javascripts/content_editor/components/formatting_toolbar.vue b/app/assets/javascripts/content_editor/components/formatting_toolbar.vue
new file mode 100644
index 00000000000..8a25ad3fd96
--- /dev/null
+++ b/app/assets/javascripts/content_editor/components/formatting_toolbar.vue
@@ -0,0 +1,110 @@
+<script>
+import trackUIControl from '../services/track_ui_control';
+import ToolbarButton from './toolbar_button.vue';
+import ToolbarImageButton from './toolbar_image_button.vue';
+import ToolbarLinkButton from './toolbar_link_button.vue';
+import ToolbarTableButton from './toolbar_table_button.vue';
+import ToolbarTextStyleDropdown from './toolbar_text_style_dropdown.vue';
+import ToolbarMoreDropdown from './toolbar_more_dropdown.vue';
+
+export default {
+ components: {
+ ToolbarButton,
+ ToolbarTextStyleDropdown,
+ ToolbarLinkButton,
+ ToolbarTableButton,
+ ToolbarImageButton,
+ ToolbarMoreDropdown,
+ },
+ methods: {
+ trackToolbarControlExecution({ contentType, value }) {
+ trackUIControl({ property: contentType, value });
+ },
+ },
+};
+</script>
+<template>
+ <div class="gl-display-flex gl-flex-wrap gl-pb-3 gl-pt-3">
+ <toolbar-text-style-dropdown
+ data-testid="text-styles"
+ class="gl-mr-3"
+ @execute="trackToolbarControlExecution"
+ />
+ <toolbar-button
+ data-testid="bold"
+ content-type="bold"
+ icon-name="bold"
+ class="gl-mx-2"
+ editor-command="toggleBold"
+ :label="__('Bold text')"
+ @execute="trackToolbarControlExecution"
+ />
+ <toolbar-button
+ data-testid="italic"
+ content-type="italic"
+ icon-name="italic"
+ class="gl-mx-2"
+ editor-command="toggleItalic"
+ :label="__('Italic text')"
+ @execute="trackToolbarControlExecution"
+ />
+ <toolbar-button
+ data-testid="blockquote"
+ content-type="blockquote"
+ icon-name="quote"
+ class="gl-mx-2"
+ editor-command="toggleBlockquote"
+ :label="__('Insert a quote')"
+ @execute="trackToolbarControlExecution"
+ />
+ <toolbar-button
+ data-testid="code"
+ content-type="code"
+ icon-name="code"
+ class="gl-mx-2"
+ editor-command="toggleCode"
+ :label="__('Code')"
+ @execute="trackToolbarControlExecution"
+ />
+ <toolbar-link-button data-testid="link" @execute="trackToolbarControlExecution" />
+ <toolbar-button
+ data-testid="bullet-list"
+ content-type="bulletList"
+ icon-name="list-bulleted"
+ class="gl-mx-2 gl-display-none gl-sm-display-inline"
+ editor-command="toggleBulletList"
+ :label="__('Add a bullet list')"
+ @execute="trackToolbarControlExecution"
+ />
+ <toolbar-button
+ data-testid="ordered-list"
+ content-type="orderedList"
+ icon-name="list-numbered"
+ class="gl-mx-2 gl-display-none gl-sm-display-inline"
+ editor-command="toggleOrderedList"
+ :label="__('Add a numbered list')"
+ @execute="trackToolbarControlExecution"
+ />
+ <toolbar-button
+ data-testid="task-list"
+ content-type="taskList"
+ icon-name="list-task"
+ class="gl-mx-2 gl-display-none gl-sm-display-inline"
+ editor-command="toggleTaskList"
+ :label="__('Add a checklist')"
+ @execute="trackToolbarControlExecution"
+ />
+ <toolbar-image-button
+ ref="imageButton"
+ data-testid="image"
+ @execute="trackToolbarControlExecution"
+ />
+ <toolbar-table-button data-testid="table" @execute="trackToolbarControlExecution" />
+ <toolbar-more-dropdown data-testid="more" @execute="trackToolbarControlExecution" />
+ </div>
+</template>
+<style>
+.gl-spinner-container {
+ text-align: left;
+}
+</style>
diff --git a/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue b/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue
index 001b34a00fa..37e6ef61d50 100644
--- a/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue
+++ b/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue
@@ -210,10 +210,10 @@ export default {
<template>
<ul
:class="{ show: items.length > 0 }"
- class="gl-new-dropdown dropdown-menu gl-relative"
+ class="gl-dropdown dropdown-menu gl-relative"
data-testid="content-editor-suggestions-dropdown"
>
- <div class="gl-new-dropdown-inner gl-overflow-y-auto">
+ <div class="gl-dropdown-inner gl-overflow-y-auto">
<gl-dropdown-item
v-for="(item, index) in items"
ref="dropdownItems"
diff --git a/app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue b/app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue
index 6bb122153ef..93b31ea7d20 100644
--- a/app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue
+++ b/app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue
@@ -58,6 +58,9 @@ export default {
right
lazy
>
+ <gl-dropdown-item @click="insert('comment')">
+ {{ __('Comment') }}
+ </gl-dropdown-item>
<gl-dropdown-item @click="insert('codeBlock')">
{{ __('Code block') }}
</gl-dropdown-item>
diff --git a/app/assets/javascripts/content_editor/components/top_toolbar.vue b/app/assets/javascripts/content_editor/components/top_toolbar.vue
deleted file mode 100644
index 460368b6a11..00000000000
--- a/app/assets/javascripts/content_editor/components/top_toolbar.vue
+++ /dev/null
@@ -1,112 +0,0 @@
-<script>
-import trackUIControl from '../services/track_ui_control';
-import ToolbarButton from './toolbar_button.vue';
-import ToolbarImageButton from './toolbar_image_button.vue';
-import ToolbarLinkButton from './toolbar_link_button.vue';
-import ToolbarTableButton from './toolbar_table_button.vue';
-import ToolbarTextStyleDropdown from './toolbar_text_style_dropdown.vue';
-import ToolbarMoreDropdown from './toolbar_more_dropdown.vue';
-
-export default {
- components: {
- ToolbarButton,
- ToolbarTextStyleDropdown,
- ToolbarLinkButton,
- ToolbarTableButton,
- ToolbarImageButton,
- ToolbarMoreDropdown,
- },
- methods: {
- trackToolbarControlExecution({ contentType, value }) {
- trackUIControl({ property: contentType, value });
- },
- },
-};
-</script>
-<template>
- <div
- class="gl-display-flex gl-flex-wrap gl-pb-3 gl-pt-3 gl-border-b-solid gl-border-b-1 gl-border-b-gray-200"
- >
- <toolbar-text-style-dropdown
- data-testid="text-styles"
- class="gl-mr-3"
- @execute="trackToolbarControlExecution"
- />
- <toolbar-button
- data-testid="bold"
- content-type="bold"
- icon-name="bold"
- class="gl-mx-2"
- editor-command="toggleBold"
- :label="__('Bold text')"
- @execute="trackToolbarControlExecution"
- />
- <toolbar-button
- data-testid="italic"
- content-type="italic"
- icon-name="italic"
- class="gl-mx-2"
- editor-command="toggleItalic"
- :label="__('Italic text')"
- @execute="trackToolbarControlExecution"
- />
- <toolbar-button
- data-testid="blockquote"
- content-type="blockquote"
- icon-name="quote"
- class="gl-mx-2"
- editor-command="toggleBlockquote"
- :label="__('Insert a quote')"
- @execute="trackToolbarControlExecution"
- />
- <toolbar-button
- data-testid="code"
- content-type="code"
- icon-name="code"
- class="gl-mx-2"
- editor-command="toggleCode"
- :label="__('Code')"
- @execute="trackToolbarControlExecution"
- />
- <toolbar-link-button data-testid="link" @execute="trackToolbarControlExecution" />
- <toolbar-button
- data-testid="bullet-list"
- content-type="bulletList"
- icon-name="list-bulleted"
- class="gl-mx-2 gl-display-none gl-sm-display-inline"
- editor-command="toggleBulletList"
- :label="__('Add a bullet list')"
- @execute="trackToolbarControlExecution"
- />
- <toolbar-button
- data-testid="ordered-list"
- content-type="orderedList"
- icon-name="list-numbered"
- class="gl-mx-2 gl-display-none gl-sm-display-inline"
- editor-command="toggleOrderedList"
- :label="__('Add a numbered list')"
- @execute="trackToolbarControlExecution"
- />
- <toolbar-button
- data-testid="task-list"
- content-type="taskList"
- icon-name="list-task"
- class="gl-mx-2 gl-display-none gl-sm-display-inline"
- editor-command="toggleTaskList"
- :label="__('Add a checklist')"
- @execute="trackToolbarControlExecution"
- />
- <toolbar-image-button
- ref="imageButton"
- data-testid="image"
- @execute="trackToolbarControlExecution"
- />
- <toolbar-table-button data-testid="table" @execute="trackToolbarControlExecution" />
- <toolbar-more-dropdown data-testid="more" @execute="trackToolbarControlExecution" />
- </div>
-</template>
-<style>
-.gl-spinner-container {
- text-align: left;
-}
-</style>
diff --git a/app/assets/javascripts/content_editor/extensions/code_block_highlight.js b/app/assets/javascripts/content_editor/extensions/code_block_highlight.js
index 27432b1e18b..1d85bfcc965 100644
--- a/app/assets/javascripts/content_editor/extensions/code_block_highlight.js
+++ b/app/assets/javascripts/content_editor/extensions/code_block_highlight.js
@@ -23,6 +23,10 @@ export default CodeBlockLowlight.extend({
// eslint-disable-next-line @gitlab/require-i18n-strings
default: 'code highlight',
},
+ langParams: {
+ default: null,
+ parseHTML: (element) => element.dataset.langParams,
+ },
};
},
addInputRules() {
diff --git a/app/assets/javascripts/content_editor/extensions/comment.js b/app/assets/javascripts/content_editor/extensions/comment.js
new file mode 100644
index 00000000000..8e247e552a3
--- /dev/null
+++ b/app/assets/javascripts/content_editor/extensions/comment.js
@@ -0,0 +1,49 @@
+import { Node, textblockTypeInputRule } from '@tiptap/core';
+
+export const commentInputRegex = /^<!--[\s\n]$/;
+
+export default Node.create({
+ name: 'comment',
+ content: 'text*',
+ marks: '',
+ group: 'block',
+ code: true,
+ isolating: true,
+ defining: true,
+
+ parseHTML() {
+ return [
+ {
+ tag: 'comment',
+ preserveWhitespace: 'full',
+ getContent(element, schema) {
+ const node = schema.node('paragraph', {}, [
+ schema.text(
+ element.textContent.replace(/&#x([0-9A-F]{2,4});/gi, (_, code) =>
+ String.fromCharCode(parseInt(code, 16)),
+ ) || ' ',
+ ),
+ ]);
+ return node.content;
+ },
+ },
+ ];
+ },
+
+ renderHTML() {
+ return [
+ 'pre',
+ { class: 'gl-p-0 gl-border-0 gl-bg-transparent gl-text-gray-300' },
+ ['span', { class: 'content-editor-comment' }, 0],
+ ];
+ },
+
+ addInputRules() {
+ return [
+ textblockTypeInputRule({
+ find: commentInputRegex,
+ type: this.type,
+ }),
+ ];
+ },
+});
diff --git a/app/assets/javascripts/content_editor/extensions/image.js b/app/assets/javascripts/content_editor/extensions/image.js
index 65849ec4d0d..fc4c108b773 100644
--- a/app/assets/javascripts/content_editor/extensions/image.js
+++ b/app/assets/javascripts/content_editor/extensions/image.js
@@ -52,6 +52,22 @@ export default Image.extend({
return img.getAttribute('title');
},
},
+ width: {
+ default: null,
+ parseHTML: (element) => {
+ const img = resolveImageEl(element);
+
+ return img.getAttribute('width');
+ },
+ },
+ height: {
+ default: null,
+ parseHTML: (element) => {
+ const img = resolveImageEl(element);
+
+ return img.getAttribute('height');
+ },
+ },
isReference: {
default: false,
renderHTML: () => '',
@@ -76,6 +92,8 @@ export default Image.extend({
src: HTMLAttributes.src,
alt: HTMLAttributes.alt,
title: HTMLAttributes.title,
+ width: HTMLAttributes.width,
+ height: HTMLAttributes.height,
},
];
},
diff --git a/app/assets/javascripts/content_editor/extensions/reference_label.js b/app/assets/javascripts/content_editor/extensions/reference_label.js
index 716e191c3d5..9dff0b7a689 100644
--- a/app/assets/javascripts/content_editor/extensions/reference_label.js
+++ b/app/assets/javascripts/content_editor/extensions/reference_label.js
@@ -1,5 +1,5 @@
import { VueNodeViewRenderer } from '@tiptap/vue-2';
-import { SCOPED_LABEL_DELIMITER } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
+import { SCOPED_LABEL_DELIMITER } from '~/sidebar/components/labels/labels_select_widget/constants';
import LabelWrapper from '../components/wrappers/label.vue';
import Reference from './reference';
diff --git a/app/assets/javascripts/content_editor/services/create_content_editor.js b/app/assets/javascripts/content_editor/services/create_content_editor.js
index ba9ce705c62..61c6be574d0 100644
--- a/app/assets/javascripts/content_editor/services/create_content_editor.js
+++ b/app/assets/javascripts/content_editor/services/create_content_editor.js
@@ -10,6 +10,7 @@ import BulletList from '../extensions/bullet_list';
import Code from '../extensions/code';
import CodeBlockHighlight from '../extensions/code_block_highlight';
import ColorChip from '../extensions/color_chip';
+import Comment from '../extensions/comment';
import DescriptionItem from '../extensions/description_item';
import DescriptionList from '../extensions/description_list';
import Details from '../extensions/details';
@@ -100,6 +101,7 @@ export const createContentEditor = ({
BulletList,
Code,
ColorChip,
+ Comment,
CodeBlockHighlight,
DescriptionItem,
DescriptionList,
diff --git a/app/assets/javascripts/content_editor/services/gl_api_markdown_deserializer.js b/app/assets/javascripts/content_editor/services/gl_api_markdown_deserializer.js
index fa46bd9ff81..796dc06ad93 100644
--- a/app/assets/javascripts/content_editor/services/gl_api_markdown_deserializer.js
+++ b/app/assets/javascripts/content_editor/services/gl_api_markdown_deserializer.js
@@ -1,4 +1,5 @@
import { DOMParser as ProseMirrorDOMParser } from 'prosemirror-model';
+import { replaceCommentsWith } from '~/lib/utils/dom_utils';
export default ({ render }) => {
/**
@@ -22,7 +23,9 @@ export default ({ render }) => {
if (!html) return {};
const parser = new DOMParser();
- const { body } = parser.parseFromString(html, 'text/html');
+ const { body } = parser.parseFromString(`<body>${html}</body>`, 'text/html');
+
+ replaceCommentsWith(body, 'comment');
// append original source as a comment that nodes can access
body.append(document.createComment(markdown));
diff --git a/app/assets/javascripts/content_editor/services/markdown_serializer.js b/app/assets/javascripts/content_editor/services/markdown_serializer.js
index 958c27c281a..4e29f85004b 100644
--- a/app/assets/javascripts/content_editor/services/markdown_serializer.js
+++ b/app/assets/javascripts/content_editor/services/markdown_serializer.js
@@ -12,6 +12,7 @@ import DescriptionItem from '../extensions/description_item';
import DescriptionList from '../extensions/description_list';
import Details from '../extensions/details';
import DetailsContent from '../extensions/details_content';
+import Comment from '../extensions/comment';
import Diagram from '../extensions/diagram';
import Emoji from '../extensions/emoji';
import Figure from '../extensions/figure';
@@ -50,6 +51,7 @@ import Text from '../extensions/text';
import Video from '../extensions/video';
import WordBreak from '../extensions/word_break';
import {
+ renderComment,
renderCodeBlock,
renderHardBreak,
renderTable,
@@ -130,6 +132,7 @@ const defaultSerializerConfig = {
}),
[BulletList.name]: preserveUnchanged(renderBulletList),
[CodeBlockHighlight.name]: preserveUnchanged(renderCodeBlock),
+ [Comment.name]: renderComment,
[Diagram.name]: preserveUnchanged(renderCodeBlock),
[DescriptionList.name]: renderHTMLNode('dl', true),
[DescriptionItem.name]: (state, node, parent, index) => {
diff --git a/app/assets/javascripts/content_editor/services/serialization_helpers.js b/app/assets/javascripts/content_editor/services/serialization_helpers.js
index 5c0cb21075a..131c79357bf 100644
--- a/app/assets/javascripts/content_editor/services/serialization_helpers.js
+++ b/app/assets/javascripts/content_editor/services/serialization_helpers.js
@@ -308,7 +308,7 @@ export function renderHardBreak(state, node, parent, index) {
}
export function renderImage(state, node) {
- const { alt, canonicalSrc, src, title, isReference } = node.attrs;
+ const { alt, canonicalSrc, src, title, width, height, isReference } = node.attrs;
if (isString(src) || isString(canonicalSrc)) {
const quotedTitle = title ? ` ${state.quote(title)}` : '';
@@ -316,7 +316,17 @@ export function renderImage(state, node) {
? `[${canonicalSrc}]`
: `(${state.esc(canonicalSrc || src)}${quotedTitle})`;
- state.write(`![${state.esc(alt || '')}]${sourceExpression}`);
+ const sizeAttributes = [];
+ if (width) {
+ sizeAttributes.push(`width=${JSON.stringify(width)}`);
+ }
+ if (height) {
+ sizeAttributes.push(`height=${JSON.stringify(height)}`);
+ }
+
+ const attributes = sizeAttributes.length ? `{${sizeAttributes.join(' ')}}` : '';
+
+ state.write(`![${state.esc(alt || '')}]${sourceExpression}${attributes}`);
}
}
@@ -324,8 +334,19 @@ export function renderPlayable(state, node) {
renderImage(state, node);
}
+export function renderComment(state, node) {
+ state.text('<!--');
+ state.text(node.textContent);
+ state.text('-->');
+ state.closeBlock(node);
+}
+
export function renderCodeBlock(state, node) {
- state.write(`\`\`\`${node.attrs.language || ''}\n`);
+ state.write(
+ `\`\`\`${
+ (node.attrs.language || '') + (node.attrs.langParams ? `:${node.attrs.langParams}` : '')
+ }\n`,
+ );
state.text(node.textContent, false);
state.ensureNewLine();
state.write('```');
diff --git a/app/assets/javascripts/crm/components/form.vue b/app/assets/javascripts/crm/components/crm_form.vue
index ea6a6892bbd..ea6a6892bbd 100644
--- a/app/assets/javascripts/crm/components/form.vue
+++ b/app/assets/javascripts/crm/components/crm_form.vue
diff --git a/app/assets/javascripts/crm/contacts/components/contact_form_wrapper.vue b/app/assets/javascripts/crm/contacts/components/contact_form_wrapper.vue
index b29089519e2..a851c7a9e85 100644
--- a/app/assets/javascripts/crm/contacts/components/contact_form_wrapper.vue
+++ b/app/assets/javascripts/crm/contacts/components/contact_form_wrapper.vue
@@ -2,7 +2,7 @@
import { s__, __ } from '~/locale';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { TYPE_CRM_CONTACT, TYPE_GROUP } from '~/graphql_shared/constants';
-import ContactForm from '../../components/form.vue';
+import CrmForm from '../../components/crm_form.vue';
import getGroupOrganizationsQuery from '../../organizations/components/graphql/get_group_organizations.query.graphql';
import getGroupContactsQuery from './graphql/get_group_contacts.query.graphql';
import createContactMutation from './graphql/create_contact.mutation.graphql';
@@ -10,7 +10,7 @@ import updateContactMutation from './graphql/update_contact.mutation.graphql';
export default {
components: {
- ContactForm,
+ CrmForm,
},
inject: ['groupFullPath', 'groupId'],
props: {
@@ -111,7 +111,7 @@ export default {
</script>
<template>
- <contact-form
+ <crm-form
:drawer-open="true"
:get-query="getQuery"
get-query-node-path="group.contacts"
diff --git a/app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue b/app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue
index 32900d45f22..01bff4b69d6 100644
--- a/app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue
+++ b/app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue
@@ -2,14 +2,14 @@
import { s__, __ } from '~/locale';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { TYPE_CRM_ORGANIZATION, TYPE_GROUP } from '~/graphql_shared/constants';
-import OrganizationForm from '../../components/form.vue';
+import CrmForm from '../../components/crm_form.vue';
import getGroupOrganizationsQuery from './graphql/get_group_organizations.query.graphql';
import createOrganizationMutation from './graphql/create_organization.mutation.graphql';
import updateOrganizationMutation from './graphql/update_organization.mutation.graphql';
export default {
components: {
- OrganizationForm,
+ CrmForm,
},
inject: ['groupFullPath', 'groupId'],
props: {
@@ -73,7 +73,7 @@ export default {
</script>
<template>
- <organization-form
+ <crm-form
:drawer-open="true"
:get-query="getQuery"
get-query-node-path="group.organizations"
diff --git a/app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue b/app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue
index 411e482b0ce..c6aeb6c726d 100644
--- a/app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue
+++ b/app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue
@@ -185,7 +185,6 @@ export default {
name="prometheus_metric[title]"
class="form-control"
:placeholder="s__('Metrics|e.g. Throughput')"
- data-qa-selector="custom_metric_prometheus_title_field"
required
/>
<span class="form-text text-muted">{{ s__('Metrics|Used as a title for the chart') }}</span>
@@ -209,7 +208,6 @@ export default {
<gl-form-input
id="prometheus_metric_query"
v-model.trim="query"
- data-qa-selector="custom_metric_prometheus_query_field"
name="prometheus_metric[query]"
class="form-control"
:placeholder="s__('Metrics|e.g. rate(http_requests_total[5m])')"
@@ -247,7 +245,6 @@ export default {
<gl-form-input
id="prometheus_metric_y_label"
v-model="yLabel"
- data-qa-selector="custom_metric_prometheus_y_label_field"
name="prometheus_metric[y_label]"
class="form-control"
:placeholder="s__('Metrics|e.g. Requests/second')"
@@ -267,7 +264,6 @@ export default {
<gl-form-input
id="prometheus_metric_unit"
v-model="unit"
- data-qa-selector="custom_metric_prometheus_unit_label_field"
name="prometheus_metric[unit]"
class="form-control"
:placeholder="s__('Metrics|e.g. req/sec')"
@@ -282,7 +278,6 @@ export default {
<gl-form-input
id="prometheus_metric_legend"
v-model="legend"
- data-qa-selector="custom_metric_prometheus_legend_label_field"
name="prometheus_metric[legend]"
class="form-control"
:placeholder="s__('Metrics|e.g. HTTP requests')"
diff --git a/app/assets/javascripts/cycle_analytics/components/base.vue b/app/assets/javascripts/cycle_analytics/components/base.vue
deleted file mode 100644
index f06544f50c6..00000000000
--- a/app/assets/javascripts/cycle_analytics/components/base.vue
+++ /dev/null
@@ -1,192 +0,0 @@
-<script>
-import { GlLoadingIcon } from '@gitlab/ui';
-import { mapActions, mapState, mapGetters } from 'vuex';
-import { getCookie, setCookie } from '~/lib/utils/common_utils';
-import ValueStreamMetrics from '~/analytics/shared/components/value_stream_metrics.vue';
-import { VSA_METRICS_GROUPS } from '~/analytics/shared/constants';
-import { toYmd } from '~/analytics/shared/utils';
-import PathNavigation from '~/cycle_analytics/components/path_navigation.vue';
-import StageTable from '~/cycle_analytics/components/stage_table.vue';
-import ValueStreamFilters from '~/cycle_analytics/components/value_stream_filters.vue';
-import UrlSync from '~/vue_shared/components/url_sync.vue';
-import { __ } from '~/locale';
-import { SUMMARY_METRICS_REQUEST, METRICS_REQUESTS } from '../constants';
-
-const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
-
-export default {
- name: 'CycleAnalytics',
- components: {
- GlLoadingIcon,
- PathNavigation,
- StageTable,
- ValueStreamFilters,
- ValueStreamMetrics,
- UrlSync,
- },
- props: {
- noDataSvgPath: {
- type: String,
- required: true,
- },
- noAccessSvgPath: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- isOverviewDialogDismissed: getCookie(OVERVIEW_DIALOG_COOKIE),
- };
- },
- computed: {
- ...mapState([
- 'isLoading',
- 'isLoadingStage',
- 'isEmptyStage',
- 'selectedStage',
- 'selectedStageEvents',
- 'selectedStageError',
- 'stageCounts',
- 'endpoints',
- 'features',
- 'createdBefore',
- 'createdAfter',
- 'pagination',
- 'hasNoAccessError',
- ]),
- ...mapGetters(['pathNavigationData', 'filterParams']),
- isLoaded() {
- return !this.isLoading && !this.isLoadingStage;
- },
- displayStageEvents() {
- const { selectedStageEvents, isLoadingStage, isEmptyStage } = this;
- return selectedStageEvents.length && !isLoadingStage && !isEmptyStage;
- },
- displayNotEnoughData() {
- return !this.isLoadingStage && this.isEmptyStage;
- },
- displayNoAccess() {
- return !this.isLoadingStage && this.hasNoAccessError;
- },
- displayPathNavigation() {
- return this.isLoading || (this.selectedStage && this.pathNavigationData.length);
- },
- emptyStageTitle() {
- if (this.displayNoAccess) {
- return __('You need permission.');
- }
- return this.selectedStageError
- ? this.selectedStageError
- : __("We don't have enough data to show this stage.");
- },
- emptyStageText() {
- if (this.displayNoAccess) {
- return __('Want to see the data? Please ask an administrator for access.');
- }
- return !this.selectedStageError && this.selectedStage?.emptyStageText
- ? this.selectedStage?.emptyStageText
- : '';
- },
- selectedStageCount() {
- if (this.selectedStage) {
- const {
- stageCounts,
- selectedStage: { id },
- } = this;
- return stageCounts[id];
- }
- return 0;
- },
- metricsRequests() {
- return this.features?.cycleAnalyticsForGroups ? METRICS_REQUESTS : SUMMARY_METRICS_REQUEST;
- },
- query() {
- return {
- created_after: toYmd(this.createdAfter),
- created_before: toYmd(this.createdBefore),
- stage_id: this.selectedStage?.id || null,
- sort: this.pagination?.sort || null,
- direction: this.pagination?.direction || null,
- page: this.pagination?.page || null,
- };
- },
- },
- methods: {
- ...mapActions([
- 'fetchStageData',
- 'setSelectedStage',
- 'setDateRange',
- 'updateStageTablePagination',
- ]),
- onSetDateRange({ startDate, endDate }) {
- this.setDateRange({
- createdAfter: new Date(startDate),
- createdBefore: new Date(endDate),
- });
- },
- onSelectStage(stage) {
- this.setSelectedStage(stage);
- this.updateStageTablePagination({ ...this.pagination, page: 1 });
- },
- dismissOverviewDialog() {
- this.isOverviewDialogDismissed = true;
- setCookie(OVERVIEW_DIALOG_COOKIE, '1');
- },
- onHandleUpdatePagination(data) {
- this.updateStageTablePagination(data);
- },
- },
- dayRangeOptions: [7, 30, 90],
- i18n: {
- dropdownText: __('Last %{days} days'),
- pageTitle: __('Value Stream Analytics'),
- recentActivity: __('Recent Project Activity'),
- },
- VSA_METRICS_GROUPS,
-};
-</script>
-<template>
- <div>
- <h3>{{ $options.i18n.pageTitle }}</h3>
- <value-stream-filters
- :group-id="endpoints.groupId"
- :group-path="endpoints.groupPath"
- :has-project-filter="false"
- :start-date="createdAfter"
- :end-date="createdBefore"
- @setDateRange="onSetDateRange"
- />
- <div class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row">
- <path-navigation
- v-if="displayPathNavigation"
- data-testid="vsa-path-navigation"
- class="gl-w-full gl-mt-4"
- :loading="isLoading || isLoadingStage"
- :stages="pathNavigationData"
- :selected-stage="selectedStage"
- @selected="onSelectStage"
- />
- </div>
- <value-stream-metrics
- :request-path="endpoints.fullPath"
- :request-params="filterParams"
- :requests="metricsRequests"
- :group-by="$options.VSA_METRICS_GROUPS"
- />
- <gl-loading-icon v-if="isLoading" size="lg" />
- <stage-table
- v-else
- :is-loading="isLoading || isLoadingStage"
- :stage-events="selectedStageEvents"
- :selected-stage="selectedStage"
- :stage-count="selectedStageCount"
- :empty-state-title="emptyStageTitle"
- :empty-state-message="emptyStageText"
- :no-data-svg-path="noDataSvgPath"
- :pagination="pagination"
- @handleUpdatePagination="onHandleUpdatePagination"
- />
- <url-sync v-if="isLoaded" :query="query" />
- </div>
-</template>
diff --git a/app/assets/javascripts/cycle_analytics/components/filter_bar.vue b/app/assets/javascripts/cycle_analytics/components/filter_bar.vue
deleted file mode 100644
index 0ad325a8523..00000000000
--- a/app/assets/javascripts/cycle_analytics/components/filter_bar.vue
+++ /dev/null
@@ -1,144 +0,0 @@
-<script>
-import { mapActions, mapState } from 'vuex';
-import {
- OPERATOR_IS_ONLY,
- DEFAULT_NONE_ANY,
- TOKEN_TITLE_ASSIGNEE,
- TOKEN_TITLE_AUTHOR,
- TOKEN_TITLE_LABEL,
- TOKEN_TITLE_MILESTONE,
-} from '~/vue_shared/components/filtered_search_bar/constants';
-import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
-import {
- prepareTokens,
- processFilters,
- filterToQueryObject,
-} from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
-import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
-import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
-import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
-import UrlSync from '~/vue_shared/components/url_sync.vue';
-
-export default {
- name: 'FilterBar',
- components: {
- FilteredSearchBar,
- UrlSync,
- },
- props: {
- groupPath: {
- type: String,
- required: true,
- },
- },
- computed: {
- ...mapState('filters', {
- selectedMilestone: (state) => state.milestones.selected,
- selectedAuthor: (state) => state.authors.selected,
- selectedLabelList: (state) => state.labels.selectedList,
- selectedAssigneeList: (state) => state.assignees.selectedList,
- milestonesData: (state) => state.milestones.data,
- labelsData: (state) => state.labels.data,
- authorsData: (state) => state.authors.data,
- assigneesData: (state) => state.assignees.data,
- }),
- tokens() {
- return [
- {
- icon: 'clock',
- title: TOKEN_TITLE_MILESTONE,
- type: 'milestone',
- token: MilestoneToken,
- initialMilestones: this.milestonesData,
- unique: true,
- symbol: '%',
- operators: OPERATOR_IS_ONLY,
- fetchMilestones: this.fetchMilestones,
- },
- {
- icon: 'labels',
- title: TOKEN_TITLE_LABEL,
- type: 'labels',
- token: LabelToken,
- defaultLabels: DEFAULT_NONE_ANY,
- initialLabels: this.labelsData,
- unique: false,
- symbol: '~',
- operators: OPERATOR_IS_ONLY,
- fetchLabels: this.fetchLabels,
- },
- {
- icon: 'pencil',
- title: TOKEN_TITLE_AUTHOR,
- type: 'author',
- token: AuthorToken,
- initialAuthors: this.authorsData,
- unique: true,
- operators: OPERATOR_IS_ONLY,
- fetchAuthors: this.fetchAuthors,
- },
- {
- icon: 'user',
- title: TOKEN_TITLE_ASSIGNEE,
- type: 'assignees',
- token: AuthorToken,
- initialAuthors: this.assigneesData,
- unique: false,
- operators: OPERATOR_IS_ONLY,
- fetchAuthors: this.fetchAssignees,
- },
- ];
- },
- query() {
- return filterToQueryObject({
- milestone_title: this.selectedMilestone,
- author_username: this.selectedAuthor,
- label_name: this.selectedLabelList,
- assignee_username: this.selectedAssigneeList,
- });
- },
- },
- methods: {
- ...mapActions('filters', [
- 'setFilters',
- 'fetchMilestones',
- 'fetchLabels',
- 'fetchAuthors',
- 'fetchAssignees',
- ]),
- initialFilterValue() {
- return prepareTokens({
- milestone: this.selectedMilestone,
- author: this.selectedAuthor,
- assignees: this.selectedAssigneeList,
- labels: this.selectedLabelList,
- });
- },
- handleFilter(filters) {
- const { labels, milestone, author, assignees } = processFilters(filters);
-
- this.setFilters({
- selectedAuthor: author ? author[0] : null,
- selectedMilestone: milestone ? milestone[0] : null,
- selectedAssigneeList: assignees || [],
- selectedLabelList: labels || [],
- });
- },
- },
-};
-</script>
-
-<template>
- <div>
- <filtered-search-bar
- class="gl-flex-grow-1"
- :namespace="groupPath"
- recent-searches-storage-key="value-stream-analytics"
- :search-input-placeholder="__('Filter results')"
- :tokens="tokens"
- :initial-filter-value="initialFilterValue()"
- @onFilter="handleFilter"
- />
- <url-sync :query="query" />
- </div>
-</template>
diff --git a/app/assets/javascripts/cycle_analytics/components/path_navigation.vue b/app/assets/javascripts/cycle_analytics/components/path_navigation.vue
deleted file mode 100644
index 72a7659aac0..00000000000
--- a/app/assets/javascripts/cycle_analytics/components/path_navigation.vue
+++ /dev/null
@@ -1,113 +0,0 @@
-<script>
-import { GlPath, GlPopover, GlSkeletonLoader, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
-import Tracking from '~/tracking';
-import { OVERVIEW_STAGE_ID } from '../constants';
-import FormattedStageCount from './formatted_stage_count.vue';
-
-export default {
- name: 'PathNavigation',
- components: {
- GlPath,
- GlSkeletonLoader,
- GlPopover,
- FormattedStageCount,
- },
- directives: {
- SafeHtml,
- },
- mixins: [Tracking.mixin()],
- props: {
- loading: {
- type: Boolean,
- required: false,
- default: false,
- },
- stages: {
- type: Array,
- required: true,
- },
- selectedStage: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- },
- methods: {
- showPopover({ id }) {
- return id && id !== OVERVIEW_STAGE_ID;
- },
- onSelectStage($event) {
- this.$emit('selected', $event);
- this.track('click_path_navigation', {
- extra: {
- stage_id: $event.id,
- },
- });
- },
- },
- popoverOptions: {
- triggers: 'hover',
- placement: 'bottom',
- },
-};
-</script>
-<template>
- <gl-skeleton-loader v-if="loading" :width="235" :lines="2" />
- <gl-path v-else :key="selectedStage.id" :items="stages" @selected="onSelectStage">
- <template #default="{ pathItem, pathId }">
- <gl-popover
- v-if="showPopover(pathItem)"
- v-bind="$options.popoverOptions"
- :target="pathId"
- :css-classes="['stage-item-popover']"
- data-testid="stage-item-popover"
- >
- <template #title>{{ pathItem.title }}</template>
- <div class="gl-px-4">
- <div class="gl-display-flex gl-justify-content-space-between">
- <div class="gl-pr-4 gl-pb-4">
- {{ s__('ValueStreamEvent|Stage time (median)') }}
- </div>
- <div class="gl-pb-4 gl-font-weight-bold">{{ pathItem.metric }}</div>
- </div>
- </div>
- <div class="gl-px-4">
- <div class="gl-display-flex gl-justify-content-space-between">
- <div class="gl-pr-4 gl-pb-4">
- {{ s__('ValueStreamEvent|Items in stage') }}
- </div>
- <div class="gl-pb-4 gl-font-weight-bold">
- <formatted-stage-count :stage-count="pathItem.stageCount" />
- </div>
- </div>
- </div>
- <div class="gl-px-4 gl-pt-4 gl-border-t-1 gl-border-t-solid gl-border-gray-50">
- <div
- v-if="pathItem.startEventHtmlDescription"
- class="gl-display-flex gl-flex-direction-row"
- >
- <div class="gl-display-flex gl-flex-direction-column gl-pr-4 gl-pb-4 metric-label">
- {{ s__('ValueStreamEvent|Start') }}
- </div>
- <div
- v-safe-html="pathItem.startEventHtmlDescription"
- class="gl-display-flex gl-flex-direction-column gl-pb-4 stage-event-description"
- ></div>
- </div>
- <div
- v-if="pathItem.endEventHtmlDescription"
- class="gl-display-flex gl-flex-direction-row"
- >
- <div class="gl-display-flex gl-flex-direction-column gl-pr-4 metric-label">
- {{ s__('ValueStreamEvent|Stop') }}
- </div>
- <div
- v-safe-html="pathItem.endEventHtmlDescription"
- class="gl-display-flex gl-flex-direction-column stage-event-description"
- ></div>
- </div>
- </div>
- </gl-popover>
- </template>
- </gl-path>
-</template>
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_table.vue b/app/assets/javascripts/cycle_analytics/components/stage_table.vue
deleted file mode 100644
index f1fdffd4b72..00000000000
--- a/app/assets/javascripts/cycle_analytics/components/stage_table.vue
+++ /dev/null
@@ -1,305 +0,0 @@
-<script>
-import {
- GlEmptyState,
- GlIcon,
- GlLink,
- GlLoadingIcon,
- GlPagination,
- GlTable,
- GlBadge,
-} from '@gitlab/ui';
-import FormattedStageCount from '~/cycle_analytics/components/formatted_stage_count.vue';
-import { __ } from '~/locale';
-import Tracking from '~/tracking';
-import {
- NOT_ENOUGH_DATA_ERROR,
- FIELD_KEY_TITLE,
- PAGINATION_SORT_FIELD_END_EVENT,
- PAGINATION_SORT_FIELD_DURATION,
- PAGINATION_SORT_DIRECTION_ASC,
- PAGINATION_SORT_DIRECTION_DESC,
-} from '../constants';
-import TotalTime from './total_time.vue';
-
-const DEFAULT_WORKFLOW_TITLE_PROPERTIES = {
- thClass: 'gl-w-half',
- key: FIELD_KEY_TITLE,
- sortable: false,
-};
-
-const WORKFLOW_COLUMN_TITLES = {
- issues: { ...DEFAULT_WORKFLOW_TITLE_PROPERTIES, label: __('Issues') },
- jobs: { ...DEFAULT_WORKFLOW_TITLE_PROPERTIES, label: __('Jobs') },
- deployments: { ...DEFAULT_WORKFLOW_TITLE_PROPERTIES, label: __('Deployments') },
- mergeRequests: { ...DEFAULT_WORKFLOW_TITLE_PROPERTIES, label: __('Merge requests') },
-};
-
-const fullProjectPath = ({ namespaceFullPath = '', projectPath }) =>
- namespaceFullPath.split('/').length > 1 ? `${namespaceFullPath}/${projectPath}` : projectPath;
-
-export default {
- name: 'StageTable',
- components: {
- GlEmptyState,
- GlIcon,
- GlLink,
- GlLoadingIcon,
- GlPagination,
- GlTable,
- GlBadge,
- TotalTime,
- FormattedStageCount,
- },
- mixins: [Tracking.mixin()],
- props: {
- selectedStage: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- isLoading: {
- type: Boolean,
- required: true,
- },
- stageEvents: {
- type: Array,
- required: true,
- },
- stageCount: {
- type: Number,
- required: false,
- default: null,
- },
- noDataSvgPath: {
- type: String,
- required: true,
- },
- emptyStateTitle: {
- type: String,
- required: false,
- default: null,
- },
- emptyStateMessage: {
- type: String,
- required: false,
- default: '',
- },
- pagination: {
- type: Object,
- required: false,
- default: null,
- },
- sortable: {
- type: Boolean,
- required: false,
- default: true,
- },
- includeProjectName: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- data() {
- if (this.pagination) {
- const {
- pagination: { sort, direction },
- } = this;
- return {
- sort,
- direction,
- sortDesc: direction === PAGINATION_SORT_DIRECTION_DESC,
- };
- }
- return { sort: null, direction: null, sortDesc: null };
- },
- computed: {
- isEmptyStage() {
- return !this.selectedStage || !this.stageEvents.length;
- },
- emptyStateTitleText() {
- return this.emptyStateTitle || NOT_ENOUGH_DATA_ERROR;
- },
- isMergeRequestStage() {
- const [firstEvent] = this.stageEvents;
- return this.isMrLink(firstEvent.url);
- },
- workflowTitle() {
- if (this.isMergeRequestStage) {
- return WORKFLOW_COLUMN_TITLES.mergeRequests;
- }
- return WORKFLOW_COLUMN_TITLES.issues;
- },
- fields() {
- return [
- this.workflowTitle,
- {
- key: PAGINATION_SORT_FIELD_END_EVENT,
- label: __('Last event'),
- sortable: this.sortable,
- },
- {
- key: PAGINATION_SORT_FIELD_DURATION,
- label: __('Duration'),
- sortable: this.sortable,
- },
- ];
- },
- prevPage() {
- return Math.max(this.pagination.page - 1, 0);
- },
- nextPage() {
- return this.pagination.hasNextPage ? this.pagination.page + 1 : null;
- },
- },
- methods: {
- isMrLink(url = '') {
- return url.includes('/merge_request');
- },
- itemId({ iid, projectPath, namespaceFullPath = '' }, separator = '#') {
- const prefix = this.includeProjectName
- ? fullProjectPath({ namespaceFullPath, projectPath })
- : '';
- return `${prefix}${separator}${iid}`;
- },
- itemDisplayName(item) {
- const separator = this.isMrLink(item.url) ? '!' : '#';
- return this.itemId(item, separator);
- },
- itemTitle(item) {
- return item.title || item.name;
- },
- onSelectPage(page) {
- const { sort, direction } = this.pagination;
- this.track('click_button', { label: 'pagination' });
- this.$emit('handleUpdatePagination', { sort, direction, page });
- },
- onSort({ sortBy, sortDesc }) {
- const direction = sortDesc ? PAGINATION_SORT_DIRECTION_DESC : PAGINATION_SORT_DIRECTION_ASC;
- this.sort = sortBy;
- this.sortDesc = sortDesc;
- this.$emit('handleUpdatePagination', { sort: sortBy, direction });
- this.track('click_button', { label: `sort_${sortBy}_${direction}` });
- },
- },
-};
-</script>
-<template>
- <div data-testid="vsa-stage-table">
- <gl-loading-icon v-if="isLoading" class="gl-mt-4" size="lg" />
- <gl-empty-state
- v-else-if="isEmptyStage"
- :title="emptyStateTitleText"
- :description="emptyStateMessage"
- :svg-path="noDataSvgPath"
- />
- <gl-table
- v-else
- stacked="lg"
- show-empty
- :sort-by.sync="sort"
- :sort-direction.sync="direction"
- :sort-desc.sync="sortDesc"
- :fields="fields"
- :items="stageEvents"
- :empty-text="emptyStateMessage"
- @sort-changed="onSort"
- >
- <template v-if="stageCount" #head(title)="data">
- <span>{{ data.label }}</span
- ><gl-badge class="gl-ml-2" size="sm"
- ><formatted-stage-count :stage-count="stageCount"
- /></gl-badge>
- </template>
- <template #head(duration)="data">
- <span data-testid="vsa-stage-header-duration">{{ data.label }}</span>
- </template>
- <template #head(end_event)="data">
- <span data-testid="vsa-stage-header-last-event">{{ data.label }}</span>
- </template>
- <template #cell(title)="{ item }">
- <div data-testid="vsa-stage-event">
- <div v-if="item.id" data-testid="vsa-stage-content">
- <p class="gl-m-0">
- <gl-link
- data-testid="vsa-stage-event-link"
- class="gl-text-black-normal"
- :href="item.url"
- >{{ itemId(item.id, '#') }}</gl-link
- >
- <gl-icon :size="16" name="fork" />
- <gl-link
- v-if="item.branch"
- :href="item.branch.url"
- class="gl-text-black-normal ref-name"
- >{{ item.branch.name }}</gl-link
- >
- <span class="icon-branch gl-text-gray-400">
- <gl-icon name="commit" :size="14" />
- </span>
- <gl-link
- class="commit-sha"
- :href="item.commitUrl"
- data-testid="vsa-stage-event-build-sha"
- >{{ item.shortSha }}</gl-link
- >
- </p>
- <p class="gl-m-0">
- <span data-testid="vsa-stage-event-build-author-and-date">
- <gl-link class="gl-text-black-normal" :href="item.url">{{ item.date }}</gl-link>
- {{ s__('ByAuthor|by') }}
- <gl-link
- class="gl-text-black-normal issue-author-link"
- :href="item.author.webUrl"
- >{{ item.author.name }}</gl-link
- >
- </span>
- </p>
- </div>
- <div v-else data-testid="vsa-stage-content">
- <h5 class="gl-font-weight-bold gl-my-1" data-testid="vsa-stage-event-title">
- <gl-link class="gl-text-black-normal" :href="item.url">{{ itemTitle(item) }}</gl-link>
- </h5>
- <p class="gl-m-0">
- <gl-link
- data-testid="vsa-stage-event-link"
- class="gl-text-black-normal"
- :href="item.url"
- >{{ itemDisplayName(item) }}</gl-link
- >
- <span class="gl-font-lg">&middot;</span>
- <span data-testid="vsa-stage-event-date">
- {{ s__('OpenedNDaysAgo|Created') }}
- <gl-link class="gl-text-black-normal" :href="item.url">{{
- item.createdAt
- }}</gl-link>
- </span>
- <span data-testid="vsa-stage-event-author">
- {{ s__('ByAuthor|by') }}
- <gl-link class="gl-text-black-normal" :href="item.author.webUrl">{{
- item.author.name
- }}</gl-link>
- </span>
- </p>
- </div>
- </div>
- </template>
- <template #cell(duration)="{ item }">
- <total-time :time="item.totalTime" data-testid="vsa-stage-event-time" />
- </template>
- <template #cell(end_event)="{ item }">
- <span data-testid="vsa-stage-last-event">{{ item.endEventTimestamp }}</span>
- </template>
- </gl-table>
- <gl-pagination
- v-if="pagination && !isLoading && !isEmptyStage"
- :value="pagination.page"
- :prev-page="prevPage"
- :next-page="nextPage"
- align="center"
- class="gl-mt-3"
- data-testid="vsa-stage-pagination"
- @input="onSelectPage"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/cycle_analytics/index.js b/app/assets/javascripts/cycle_analytics/index.js
deleted file mode 100644
index 3da8696edeb..00000000000
--- a/app/assets/javascripts/cycle_analytics/index.js
+++ /dev/null
@@ -1,50 +0,0 @@
-import Vue from 'vue';
-import {
- extractFilterQueryParameters,
- extractPaginationQueryParameters,
-} from '~/analytics/shared/utils';
-import Translate from '../vue_shared/translate';
-import CycleAnalytics from './components/base.vue';
-import createStore from './store';
-import { buildCycleAnalyticsInitialData } from './utils';
-
-Vue.use(Translate);
-
-export default () => {
- const store = createStore();
- const el = document.querySelector('#js-cycle-analytics');
- const { noAccessSvgPath, noDataSvgPath } = el.dataset;
- const initialData = buildCycleAnalyticsInitialData({ ...el.dataset, gon });
-
- const pagination = extractPaginationQueryParameters(window.location.search);
- const {
- selectedAuthor,
- selectedMilestone,
- selectedAssigneeList,
- selectedLabelList,
- } = extractFilterQueryParameters(window.location.search);
-
- store.dispatch('initializeVsa', {
- ...initialData,
- selectedAuthor,
- selectedMilestone,
- selectedAssigneeList,
- selectedLabelList,
- pagination,
- });
-
- // eslint-disable-next-line no-new
- new Vue({
- el,
- name: 'CycleAnalytics',
- apolloProvider: {},
- store,
- render: (createElement) =>
- createElement(CycleAnalytics, {
- props: {
- noDataSvgPath,
- noAccessSvgPath,
- },
- }),
- });
-};
diff --git a/app/assets/javascripts/cycle_analytics/store/state.js b/app/assets/javascripts/cycle_analytics/store/state.js
deleted file mode 100644
index 8d662333afa..00000000000
--- a/app/assets/javascripts/cycle_analytics/store/state.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import {
- PAGINATION_SORT_FIELD_END_EVENT,
- PAGINATION_SORT_DIRECTION_DESC,
-} from '~/cycle_analytics/constants';
-
-export default () => ({
- id: null,
- features: {},
- endpoints: {},
- createdAfter: null,
- createdBefore: null,
- stages: [],
- analytics: [],
- valueStreams: [],
- selectedValueStream: {},
- selectedStage: {},
- selectedStageEvents: [],
- selectedStageError: '',
- medians: {},
- stageCounts: {},
- hasNoAccessError: false,
- isLoading: false,
- isLoadingStage: false,
- isEmptyStage: false,
- pagination: {
- page: null,
- hasNextPage: false,
- sort: PAGINATION_SORT_FIELD_END_EVENT,
- direction: PAGINATION_SORT_DIRECTION_DESC,
- },
-});
diff --git a/app/assets/javascripts/deploy_tokens/components/new_deploy_token.vue b/app/assets/javascripts/deploy_tokens/components/new_deploy_token.vue
index 81d74c64124..48ab9ce0a3c 100644
--- a/app/assets/javascripts/deploy_tokens/components/new_deploy_token.vue
+++ b/app/assets/javascripts/deploy_tokens/components/new_deploy_token.vue
@@ -13,27 +13,7 @@ import { createAlert, VARIANT_INFO } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { formatDate } from '~/lib/utils/datetime_utility';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import { s__ } from '~/locale';
-
-function defaultData() {
- return {
- expiresAt: null,
- name: '',
- newTokenDetails: null,
- readRepository: false,
- writeRepository: false,
- readRegistry: false,
- writeRegistry: false,
- readPackageRegistry: false,
- writePackageRegistry: false,
- username: '',
- placeholders: {
- link: { link: ['link_start', 'link_end'] },
- i: { i: ['i_start', 'i_end'] },
- code: { code: ['code_start', 'code_end'] },
- },
- };
-}
+import translations from '../deploy_token_translations';
export default {
components: {
@@ -72,45 +52,9 @@ export default {
},
data() {
- return defaultData();
- },
- translations: {
- addTokenButton: s__('DeployTokens|Create deploy token'),
- addTokenExpiryLabel: s__('DeployTokens|Expiration date (optional)'),
- addTokenExpiryDescription: s__(
- 'DeployTokens|Enter an expiration date for your token. Defaults to never expire.',
- ),
- addTokenHeader: s__('DeployTokens|New deploy token'),
- addTokenDescription: s__(
- 'DeployTokens|Create a new deploy token for all projects in this group. %{link_start}What are deploy tokens?%{link_end}',
- ),
- addTokenNameLabel: s__('DeployTokens|Name'),
- addTokenNameDescription: s__('DeployTokens|Enter a unique name for your deploy token.'),
- addTokenScopesLabel: s__('DeployTokens|Scopes (select at least one)'),
- addTokenUsernameDescription: s__(
- 'DeployTokens|Enter a username for your token. Defaults to %{code_start}gitlab+deploy-token-{n}%{code_end}.',
- ),
- addTokenUsernameLabel: s__('DeployTokens|Username (optional)'),
- newTokenCopyMessage: s__('DeployTokens|Copy deploy token'),
- newProjectTokenCreated: s__('DeployTokens|Your new project deploy token has been created.'),
- newGroupTokenCreated: s__('DeployTokens|Your new group deploy token has been created.'),
- newTokenDescription: s__(
- 'DeployTokens|Use this token as a password. Save it. This password can %{i_start}not%{i_end} be recovered.',
- ),
- newTokenMessage: s__('DeployTokens|Your New Deploy Token'),
- newTokenUsernameCopy: s__('DeployTokens|Copy username'),
- newTokenUsernameDescription: s__(
- 'DeployTokens|This username supports access. %{link_start}What kind of access?%{link_end}',
- ),
- readRepositoryHelp: s__('DeployTokens|Allows read-only access to the repository.'),
- readRegistryHelp: s__('DeployTokens|Allows read-only access to registry images.'),
- writeRegistryHelp: s__('DeployTokens|Allows read and write access to registry images.'),
- readPackageRegistryHelp: s__('DeployTokens|Allows read-only access to the package registry.'),
- writePackageRegistryHelp: s__(
- 'DeployTokens|Allows read and write access to the package registry.',
- ),
- createTokenFailedAlert: s__('DeployTokens|Failed to create a new deployment token'),
+ return this.defaultData();
},
+ translations,
computed: {
formattedExpiryDate() {
return this.expiresAt ? formatDate(this.expiresAt, 'yyyy-mm-dd') : '';
@@ -122,20 +66,78 @@ export default {
},
},
methods: {
+ defaultData() {
+ return {
+ expiresAt: null,
+ name: '',
+ newTokenDetails: null,
+ readRepository: false,
+ writeRepository: false,
+ readRegistry: false,
+ writeRegistry: false,
+ readPackageRegistry: false,
+ writePackageRegistry: false,
+ scopes: [
+ {
+ id: 'deploy_token_read_repository',
+ isShown: true,
+ value: false,
+ helpText: this.$options.translations.readRepositoryHelp,
+ scopeName: 'read_repository',
+ },
+ {
+ id: 'deploy_token_read_registry',
+ isShown: this.$props.containerRegistryEnabled,
+ value: false,
+ helpText: this.$options.translations.readRegistryHelp,
+ scopeName: 'read_registry',
+ },
+ {
+ id: 'deploy_token_write_registry',
+ isShown: this.$props.containerRegistryEnabled,
+ value: false,
+ helpText: this.$options.translations.writeRegistryHelp,
+ scopeName: 'write_registry',
+ },
+ {
+ id: 'deploy_token_read_package_registry',
+ isShown: this.$props.packagesRegistryEnabled,
+ value: false,
+ helpText: this.$options.translations.readPackageRegistryHelp,
+ scopeName: 'read_package_registry',
+ },
+ {
+ id: 'deploy_token_write_package_registry',
+ isShown: this.$props.packagesRegistryEnabled,
+ value: false,
+ helpText: this.$options.translations.writePackageRegistryHelp,
+ scopeName: 'write_package_registry',
+ },
+ ],
+ username: '',
+ placeholders: {
+ link: { link: ['link_start', 'link_end'] },
+ i: { i: ['i_start', 'i_end'] },
+ code: { code: ['code_start', 'code_end'] },
+ },
+ };
+ },
createDeployToken() {
+ const scopes = {};
+ this.scopes.forEach((scope) => {
+ scopes[scope.scopeName] = scope.value;
+ });
+ const body = {
+ deploy_token: {
+ expires_at: this.expiresAt,
+ name: this.name,
+ username: this.username,
+ ...scopes,
+ },
+ };
+
return axios
- .post(this.createNewTokenPath, {
- deploy_token: {
- expires_at: this.expiresAt,
- name: this.name,
- read_repository: this.readRepository,
- read_registry: this.readRegistry,
- write_registry: this.writeRegistry,
- read_package_registry: this.readPackageRegistry,
- write_package_registry: this.writePackageRegistry,
- username: this.username,
- },
- })
+ .post(this.createNewTokenPath, body)
.then((response) => {
this.newTokenDetails = response.data;
this.resetData();
@@ -152,7 +154,7 @@ export default {
});
},
resetData() {
- const newData = defaultData();
+ const newData = this.defaultData();
delete newData.newTokenDetails;
Object.keys(newData).forEach((k) => {
this[k] = newData[k];
@@ -269,55 +271,19 @@ export default {
>
<div id="deploy-token-scopes">
<!-- eslint-disable @gitlab/vue-require-i18n-strings -->
- <gl-form-checkbox
- id="deploy_token_read_repository"
- v-model="readRepository"
- name="deploy_token_read_repository"
- data-qa-selector="deploy_token_read_repository_checkbox"
- >
- read_repository
- <template #help>{{ $options.translations.readRepositoryHelp }}</template>
- </gl-form-checkbox>
- <gl-form-checkbox
- v-if="containerRegistryEnabled"
- id="deploy_token_read_registry"
- v-model="readRegistry"
- name="deploy_token_read_registry"
- data-qa-selector="deploy_token_read_registry_checkbox"
- >
- read_registry
- <template #help>{{ $options.translations.readRegistryHelp }}</template>
- </gl-form-checkbox>
- <gl-form-checkbox
- v-if="containerRegistryEnabled"
- id="deploy_token_write_registry"
- v-model="writeRegistry"
- name="deploy_token_write_registry"
- data-qa-selector="deploy_token_write_registry_checkbox"
- >
- write_registry
- <template #help>{{ $options.translations.writeRegistryHelp }}</template>
- </gl-form-checkbox>
- <gl-form-checkbox
- v-if="packagesRegistryEnabled"
- id="deploy_token_read_package_registry"
- v-model="readPackageRegistry"
- name="deploy_token_read_package_registry"
- data-qa-selector="deploy_token_read_package_registry_checkbox"
- >
- read_package_registry
- <template #help>{{ $options.translations.readPackageRegistryHelp }}</template>
- </gl-form-checkbox>
- <gl-form-checkbox
- v-if="packagesRegistryEnabled"
- id="deploy_token_write_package_registry"
- v-model="writePackageRegistry"
- name="deploy_token_write_package_registry"
- data-qa-selector="deploy_token_write_package_registry_checkbox"
- >
- write_package_registry
- <template #help>{{ $options.translations.writePackageRegistryHelp }}</template>
- </gl-form-checkbox>
+ <template v-for="scope in scopes">
+ <gl-form-checkbox
+ v-if="scope.isShown"
+ :id="scope.id"
+ :key="scope.id"
+ v-model="scope.value"
+ :name="scope.id"
+ :data-qa-selector="`${scope.id}_checkbox`"
+ >
+ {{ scope.scopeName }}
+ <template #help>{{ scope.helpText }}</template>
+ </gl-form-checkbox>
+ </template>
<!-- eslint-enable @gitlab/vue-require-i18n-strings -->
</div>
</gl-form-group>
diff --git a/app/assets/javascripts/deploy_tokens/deploy_token_translations.js b/app/assets/javascripts/deploy_tokens/deploy_token_translations.js
new file mode 100644
index 00000000000..3767e9e6170
--- /dev/null
+++ b/app/assets/javascripts/deploy_tokens/deploy_token_translations.js
@@ -0,0 +1,41 @@
+import { s__ } from '~/locale';
+
+const translations = {
+ addTokenButton: s__('DeployTokens|Create deploy token'),
+ addTokenExpiryLabel: s__('DeployTokens|Expiration date (optional)'),
+ addTokenExpiryDescription: s__(
+ 'DeployTokens|Enter an expiration date for your token. Defaults to never expire.',
+ ),
+ addTokenHeader: s__('DeployTokens|New deploy token'),
+ addTokenDescription: s__(
+ 'DeployTokens|Create a new deploy token for all projects in this group. %{link_start}What are deploy tokens?%{link_end}',
+ ),
+ addTokenNameLabel: s__('DeployTokens|Name'),
+ addTokenNameDescription: s__('DeployTokens|Enter a unique name for your deploy token.'),
+ addTokenScopesLabel: s__('DeployTokens|Scopes (select at least one)'),
+ addTokenUsernameDescription: s__(
+ 'DeployTokens|Enter a username for your token. Defaults to %{code_start}gitlab+deploy-token-{n}%{code_end}.',
+ ),
+ addTokenUsernameLabel: s__('DeployTokens|Username (optional)'),
+ newTokenCopyMessage: s__('DeployTokens|Copy deploy token'),
+ newProjectTokenCreated: s__('DeployTokens|Your new project deploy token has been created.'),
+ newGroupTokenCreated: s__('DeployTokens|Your new group deploy token has been created.'),
+ newTokenDescription: s__(
+ 'DeployTokens|Use this token as a password. Save it. This password can %{i_start}not%{i_end} be recovered.',
+ ),
+ newTokenMessage: s__('DeployTokens|Your New Deploy Token'),
+ newTokenUsernameCopy: s__('DeployTokens|Copy username'),
+ newTokenUsernameDescription: s__(
+ 'DeployTokens|This username supports access. %{link_start}What kind of access?%{link_end}',
+ ),
+ readRepositoryHelp: s__('DeployTokens|Allows read-only access to the repository.'),
+ readRegistryHelp: s__('DeployTokens|Allows read-only access to registry images.'),
+ writeRegistryHelp: s__('DeployTokens|Allows read and write access to registry images.'),
+ readPackageRegistryHelp: s__('DeployTokens|Allows read-only access to the package registry.'),
+ writePackageRegistryHelp: s__(
+ 'DeployTokens|Allows read and write access to the package registry.',
+ ),
+ createTokenFailedAlert: s__('DeployTokens|Failed to create a new deployment token'),
+};
+
+export default translations;
diff --git a/app/assets/javascripts/deprecated_jquery_dropdown/render.js b/app/assets/javascripts/deprecated_jquery_dropdown/render.js
index 0f612989bb4..97698d55011 100644
--- a/app/assets/javascripts/deprecated_jquery_dropdown/render.js
+++ b/app/assets/javascripts/deprecated_jquery_dropdown/render.js
@@ -149,7 +149,7 @@ function renderLink(row, data, { options, group, index }) {
}
function getOptionRenderer({ options, instance }) {
- return options.renderRow && ((li, data) => options.renderRow(data, instance));
+ return options.renderRow && ((li, data, params) => options.renderRow(data, instance, params));
}
function getRenderer(data, params) {
diff --git a/app/assets/javascripts/deprecated_notes.js b/app/assets/javascripts/deprecated_notes.js
index 2ac62b9b927..c090a66a69d 100644
--- a/app/assets/javascripts/deprecated_notes.js
+++ b/app/assets/javascripts/deprecated_notes.js
@@ -15,6 +15,7 @@ import Autosize from 'autosize';
import $ from 'jquery';
import { escape, uniqueId } from 'lodash';
import Vue from 'vue';
+import { createAlert, VARIANT_INFO } from '~/flash';
import '~/lib/utils/jquery_at_who';
import AjaxCache from '~/lib/utils/ajax_cache';
import { loadingIconForLegacyJS } from '~/loading_icon_for_legacy_js';
@@ -24,7 +25,6 @@ import * as constants from '~/notes/constants';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import Autosave from './autosave';
import loadAwardsHandler from './awards_handler';
-import createFlash from './flash';
import { defaultAutocompleteConfig } from './gfm_auto_complete';
import GLForm from './gl_form';
import axios from './lib/utils/axios_utils';
@@ -40,6 +40,7 @@ import { localTimeAgo } from './lib/utils/datetime_utility';
import { getLocationHash } from './lib/utils/url_utility';
import { sprintf, s__, __ } from './locale';
import TaskList from './task_list';
+import '~/behaviors/markdown/init_gfm';
window.autosize = Autosize;
@@ -81,7 +82,7 @@ export default class Notes {
this.keydownNoteText = this.keydownNoteText.bind(this);
this.toggleCommitList = this.toggleCommitList.bind(this);
this.postComment = this.postComment.bind(this);
- this.clearFlashWrapper = this.clearFlash.bind(this);
+ this.clearAlertWrapper = this.clearAlert.bind(this);
this.onHashChange = this.onHashChange.bind(this);
this.notes_url = notes_url;
@@ -431,9 +432,9 @@ export default class Notes {
if (noteEntity.commands_changes && Object.keys(noteEntity.commands_changes).length > 0) {
$notesList.find('.system-note.being-posted').remove();
}
- this.addFlash({
+ this.addAlert({
message: noteEntity.errors.commands_only,
- type: 'notice',
+ variant: VARIANT_INFO,
parent: this.parentTimeline.get(0),
});
this.refresh();
@@ -656,7 +657,7 @@ export default class Notes {
} else if ($form.hasClass('js-discussion-note-form')) {
formParentTimeline = $form.closest('.discussion-notes').find('.notes');
}
- return this.addFlash({
+ return this.addAlert({
message: __(
'Your comment could not be submitted! Please check your network connection and try again.',
),
@@ -665,7 +666,7 @@ export default class Notes {
}
updateNoteError() {
- createFlash({
+ createAlert({
message: __(
'Your comment could not be updated! Please check your network connection and try again.',
),
@@ -1338,15 +1339,12 @@ export default class Notes {
});
}
- addFlash(...flashParams) {
- this.flashContainer = createFlash(...flashParams);
+ addAlert(...alertParams) {
+ this.alert = createAlert(...alertParams);
}
- clearFlash() {
- if (this.flashContainer) {
- this.flashContainer.style.display = 'none';
- this.flashContainer = null;
- }
+ clearAlert() {
+ this.alert?.dismiss();
}
cleanForm($form) {
@@ -1535,7 +1533,7 @@ export default class Notes {
* b. Reset comment form to original state.
* b) If request failed
* 1. Remove placeholder element
- * 2. Show error Flash message about failure
+ * 2. Show error alert message about failure
*/
postComment(e) {
e.preventDefault();
@@ -1645,7 +1643,7 @@ export default class Notes {
}
// Clear previous form errors
- this.clearFlashWrapper();
+ this.clearAlertWrapper();
// Check if this was discussion comment
if (isDiscussionForm) {
diff --git a/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue b/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue
index a4430b15752..3091c6703b4 100644
--- a/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue
+++ b/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue
@@ -4,7 +4,7 @@ import { ApolloMutation } from 'vue-apollo';
import { createAlert } from '~/flash';
import { s__ } from '~/locale';
import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue';
-import { updateGlobalTodoCount } from '~/vue_shared/components/sidebar/todo_toggle/utils';
+import { updateGlobalTodoCount } from '~/sidebar/utils';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import DesignNotePin from '~/vue_shared/components/design_management/design_note_pin.vue';
import { isLoggedIn } from '~/lib/utils/common_utils';
diff --git a/app/assets/javascripts/design_management/components/design_notes/design_note.vue b/app/assets/javascripts/design_management/components/design_notes/design_note.vue
index e629f74ba02..af4bf7eb14d 100644
--- a/app/assets/javascripts/design_management/components/design_notes/design_note.vue
+++ b/app/assets/javascripts/design_management/components/design_notes/design_note.vue
@@ -1,13 +1,7 @@
<script>
-import {
- GlAvatar,
- GlAvatarLink,
- GlButton,
- GlLink,
- GlSafeHtmlDirective,
- GlTooltipDirective,
-} from '@gitlab/ui';
+import { GlAvatar, GlAvatarLink, GlButton, GlLink, GlTooltipDirective } from '@gitlab/ui';
import { ApolloMutation } from 'vue-apollo';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { __ } from '~/locale';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
@@ -33,7 +27,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
note: {
diff --git a/app/assets/javascripts/design_management/components/design_todo_button.vue b/app/assets/javascripts/design_management/components/design_todo_button.vue
index 013dd1d89f3..a1a23d61093 100644
--- a/app/assets/javascripts/design_management/components/design_todo_button.vue
+++ b/app/assets/javascripts/design_management/components/design_todo_button.vue
@@ -1,6 +1,6 @@
<script>
import todoMarkDoneMutation from '~/graphql_shared/mutations/todo_mark_done.mutation.graphql';
-import TodoButton from '~/vue_shared/components/sidebar/todo_toggle/todo_button.vue';
+import TodoButton from '~/sidebar/components/todo_toggle/todo_button.vue';
import createDesignTodoMutation from '../graphql/mutations/create_design_todo.mutation.graphql';
import getDesignQuery from '../graphql/queries/get_design.query.graphql';
import allVersionsMixin from '../mixins/all_versions';
diff --git a/app/assets/javascripts/design_management/components/upload/design_version_dropdown.vue b/app/assets/javascripts/design_management/components/upload/design_version_dropdown.vue
index f10545faea6..c96487d0d08 100644
--- a/app/assets/javascripts/design_management/components/upload/design_version_dropdown.vue
+++ b/app/assets/javascripts/design_management/components/upload/design_version_dropdown.vue
@@ -1,5 +1,5 @@
<script>
-import { GlDropdown, GlDropdownItem, GlSprintf } from '@gitlab/ui';
+import { GlAvatar, GlCollapsibleListbox } from '@gitlab/ui';
import defaultAvatarUrl from 'images/no_avatar.png';
import { __, sprintf } from '~/locale';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
@@ -8,13 +8,19 @@ import { findVersionId } from '../../utils/design_management_utils';
export default {
components: {
- GlDropdown,
- GlDropdownItem,
- GlSprintf,
+ GlAvatar,
+ GlCollapsibleListbox,
TimeAgo,
},
mixins: [allVersionsMixin],
computed: {
+ allVersionsList() {
+ return this.allVersions.map(({ id, ...item }, index) => ({
+ value: id,
+ index,
+ ...item,
+ }));
+ },
queryVersion() {
return this.$route.query.version;
},
@@ -29,17 +35,11 @@ export default {
// then return the latest version (index 0)
return idx !== -1 ? idx : 0;
},
- currentVersionId() {
- if (this.queryVersion) return this.queryVersion;
-
- const currentVersion = this.allVersions[this.currentVersionIdx];
- return this.findVersionId(currentVersion.id);
- },
dropdownText() {
if (this.isLatestVersion) {
return __('Showing latest version');
}
- // allVersions is sorted in reverse chronological order (latest first)
+ // allVersions is sorted in reverse chronological order (the latest first)
const currentVersionNumber = this.allVersions.length - this.currentVersionIdx;
return sprintf(__('Showing version #%{versionNumber}'), {
@@ -55,47 +55,49 @@ export default {
query: { version: this.findVersionId(versionId) },
});
},
- versionText(versionId) {
- if (this.findVersionId(versionId) === this.latestVersionId) {
- return __('Version %{versionNumber} (latest)');
- }
- return __('Version %{versionNumber}');
+ versionText(item) {
+ const versionNumber = this.allVersions.length - item.index;
+ const message =
+ this.findVersionId(item.value) === this.latestVersionId
+ ? __('Version %{versionNumber} (latest)')
+ : __('Version %{versionNumber}');
+ return sprintf(message, { versionNumber });
},
getAvatarUrl(version) {
return version?.author?.avatarUrl || defaultAvatarUrl;
},
+ getAuthorName(author) {
+ return author?.name;
+ },
},
};
</script>
<template>
- <gl-dropdown :text="dropdownText" size="small">
- <gl-dropdown-item
- v-for="(version, index) in allVersions"
- :key="version.id"
- is-check-item
- is-check-centered
- :is-checked="findVersionId(version.id) === currentVersionId"
- :avatar-url="getAvatarUrl(version)"
- @click="routeToVersion(version.id)"
- >
- <strong>
- <gl-sprintf :message="versionText(version.id)">
- <template #versionNumber>
- {{ allVersions.length - index }}
- </template>
- </gl-sprintf>
- </strong>
-
- <div v-if="version.author" class="gl-text-gray-600 gl-mt-1">
- <div>{{ version.author.name }}</div>
- <time-ago
- v-if="version.createdAt"
- class="text-1"
- :time="version.createdAt"
- tooltip-placement="bottom"
- />
- </div>
- </gl-dropdown-item>
- </gl-dropdown>
+ <gl-collapsible-listbox
+ is-check-centered
+ :items="allVersionsList"
+ :toggle-text="dropdownText"
+ :selected="designsVersion"
+ size="small"
+ @select="routeToVersion"
+ >
+ <template #list-item="{ item }">
+ <span class="gl-display-flex gl-align-items-center">
+ <gl-avatar :alt="getAuthorName(item.author)" :size="32" :src="getAvatarUrl(item)" />
+ <span class="gl-display-flex gl-flex-direction-column">
+ <span class="gl-font-weight-bold">{{ versionText(item) }}</span>
+ <span v-if="item.author" class="gl-text-gray-600 gl-mt-1">
+ <span class="gl-display-block">{{ getAuthorName(item.author) }}</span>
+ <time-ago
+ v-if="item.createdAt"
+ class="text-1"
+ :time="item.createdAt"
+ tooltip-placement="bottom"
+ />
+ </span>
+ </span>
+ </span>
+ </template>
+ </gl-collapsible-listbox>
</template>
diff --git a/app/assets/javascripts/design_management/pages/design/index.vue b/app/assets/javascripts/design_management/pages/design/index.vue
index d4c177e2e5f..f448e2f9e3d 100644
--- a/app/assets/javascripts/design_management/pages/design/index.vue
+++ b/app/assets/javascripts/design_management/pages/design/index.vue
@@ -6,7 +6,7 @@ import { ApolloMutation } from 'vue-apollo';
import { keysFor, ISSUE_CLOSE_DESIGN } from '~/behaviors/shortcuts/keybindings';
import { createAlert } from '~/flash';
import { fetchPolicies } from '~/lib/graphql';
-import { updateGlobalTodoCount } from '~/vue_shared/components/sidebar/todo_toggle/utils';
+import { updateGlobalTodoCount } from '~/sidebar/utils';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import DesignDestroyer from '../../components/design_destroyer.vue';
import DesignReplyForm from '../../components/design_notes/design_reply_form.vue';
diff --git a/app/assets/javascripts/diffs/components/commit_item.vue b/app/assets/javascripts/diffs/components/commit_item.vue
index 5a45797ed98..1857ff557e6 100644
--- a/app/assets/javascripts/diffs/components/commit_item.vue
+++ b/app/assets/javascripts/diffs/components/commit_item.vue
@@ -1,5 +1,6 @@
<script>
-import { GlButtonGroup, GlButton, GlTooltipDirective, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlButtonGroup, GlButton, GlTooltipDirective } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import CommitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
@@ -32,7 +33,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [glFeatureFlagsMixin()],
props: {
diff --git a/app/assets/javascripts/diffs/components/diff_code_quality.vue b/app/assets/javascripts/diffs/components/diff_code_quality.vue
index 8498724740f..11aa856619b 100644
--- a/app/assets/javascripts/diffs/components/diff_code_quality.vue
+++ b/app/assets/javascripts/diffs/components/diff_code_quality.vue
@@ -1,8 +1,12 @@
<script>
import { GlButton, GlIcon } from '@gitlab/ui';
-import { SEVERITY_CLASSES, SEVERITY_ICONS } from '~/reports/codequality_report/constants';
+import { SEVERITY_CLASSES, SEVERITY_ICONS } from '~/ci/reports/codequality_report/constants';
+import { NEW_CODE_QUALITY_FINDINGS } from '../i18n';
export default {
+ i18n: {
+ newFindings: NEW_CODE_QUALITY_FINDINGS,
+ },
components: { GlButton, GlIcon },
props: {
codeQuality: {
@@ -22,22 +26,33 @@ export default {
</script>
<template>
- <div data-testid="diff-codequality" class="gl-relative">
- <ul
- class="gl-list-style-none gl-mb-0 gl-p-0 codequality-findings-list gl-border-top-1 gl-border-bottom-1 gl-bg-gray-10"
+ <div
+ data-testid="diff-codequality"
+ class="gl-relative codequality-findings-list gl-border-top-1 gl-border-bottom-1 gl-bg-gray-10 gl-pl-5 gl-pt-4 gl-pb-4"
+ >
+ <h4
+ data-testid="diff-codequality-findings-heading"
+ class="gl-mt-0 gl-mb-0 gl-font-base gl-font-regular"
>
+ {{ $options.i18n.newFindings }}
+ </h4>
+ <ul class="gl-list-style-none gl-mb-0 gl-p-0">
<li
v-for="finding in codeQuality"
:key="finding.description"
- class="gl-pt-1 gl-pb-1 gl-pl-3 gl-border-solid gl-border-bottom-0 gl-border-right-0 gl-border-1 gl-border-gray-100 gl-font-regular"
+ class="gl-pt-1 gl-pb-1 gl-font-regular gl-display-flex"
>
- <gl-icon
- :size="12"
- :name="severityIcon(finding.severity)"
- :class="severityClass(finding.severity)"
- class="codequality-severity-icon"
- />
- {{ finding.description }}
+ <span class="gl-mr-3">
+ <gl-icon
+ :size="12"
+ :name="severityIcon(finding.severity)"
+ :class="severityClass(finding.severity)"
+ class="codequality-severity-icon"
+ />
+ </span>
+ <span>
+ <span class="severity-copy">{{ finding.severity }}</span> - {{ finding.description }}
+ </span>
</li>
</ul>
<gl-button
diff --git a/app/assets/javascripts/diffs/components/diff_discussion_reply.vue b/app/assets/javascripts/diffs/components/diff_discussion_reply.vue
index 3766c125325..8b747aa08dd 100644
--- a/app/assets/javascripts/diffs/components/diff_discussion_reply.vue
+++ b/app/assets/javascripts/diffs/components/diff_discussion_reply.vue
@@ -1,13 +1,18 @@
<script>
+import { GlButton } from '@gitlab/ui';
import { mapGetters } from 'vuex';
-import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue';
import NoteSignedOutWidget from '~/notes/components/note_signed_out_widget.vue';
+import { START_THREAD } from '../i18n';
+
export default {
name: 'DiffDiscussionReply',
+ i18n: {
+ START_THREAD,
+ },
components: {
+ GlButton,
NoteSignedOutWidget,
- ReplyPlaceholder,
},
props: {
hasForm: {
@@ -34,11 +39,9 @@ export default {
<template v-if="userCanReply">
<slot v-if="hasForm" name="form"></slot>
<template v-else-if="renderReplyPlaceholder">
- <reply-placeholder
- :placeholder-text="__('Start a new discussion…')"
- :label-text="__('New discussion')"
- @focus="$emit('showNewDiscussionForm')"
- />
+ <gl-button @click="$emit('showNewDiscussionForm')">
+ {{ $options.i18n.START_THREAD }}
+ </gl-button>
</template>
</template>
<note-signed-out-widget v-else />
diff --git a/app/assets/javascripts/diffs/components/diff_expansion_cell.vue b/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
index b2098b9e82d..8fcbc4b5cce 100644
--- a/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
+++ b/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
@@ -1,6 +1,7 @@
<script>
-import { GlTooltipDirective, GlSafeHtmlDirective, GlIcon, GlLoadingIcon } from '@gitlab/ui';
+import { GlTooltipDirective, GlIcon, GlLoadingIcon } from '@gitlab/ui';
import { mapActions } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { createAlert } from '~/flash';
import { s__, sprintf } from '~/locale';
import { UNFOLD_COUNT, INLINE_DIFF_LINES_KEY } from '../constants';
@@ -21,7 +22,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
file: {
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 8f041d1e670..564f776edd2 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -1,13 +1,8 @@
<script>
-import {
- GlButton,
- GlLoadingIcon,
- GlSafeHtmlDirective as SafeHtml,
- GlSprintf,
- GlAlert,
-} from '@gitlab/ui';
+import { GlButton, GlLoadingIcon, GlSprintf, GlAlert } from '@gitlab/ui';
import { escape } from 'lodash';
import { mapActions, mapGetters, mapState } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { IdState } from 'vendor/vue-virtual-scroller';
import DiffContent from 'jh_else_ce/diffs/components/diff_content.vue';
import { createAlert } from '~/flash';
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index 91c3df39e32..dff61acdfba 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -1,7 +1,6 @@
<script>
import {
GlTooltipDirective,
- GlSafeHtmlDirective,
GlIcon,
GlBadge,
GlButton,
@@ -14,6 +13,7 @@ import {
} from '@gitlab/ui';
import { escape } from 'lodash';
import { mapActions, mapGetters, mapState } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { IdState } from 'vendor/vue-virtual-scroller';
import { scrollToElement } from '~/lib/utils/common_utils';
import { truncateSha } from '~/lib/utils/text_utility';
@@ -44,7 +44,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [IdState({ idProp: (vm) => vm.diffFile.file_hash }), glFeatureFlagsMixin()],
i18n: {
diff --git a/app/assets/javascripts/diffs/components/diff_view.vue b/app/assets/javascripts/diffs/components/diff_view.vue
index 5ea118afe78..aa9a17d18e3 100644
--- a/app/assets/javascripts/diffs/components/diff_view.vue
+++ b/app/assets/javascripts/diffs/components/diff_view.vue
@@ -1,5 +1,4 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import { mapGetters, mapState, mapActions } from 'vuex';
import { IdState } from 'vendor/vue-virtual-scroller';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@@ -22,9 +21,6 @@ export default {
DiffCommentCell,
DraftNote,
},
- directives: {
- SafeHtml,
- },
mixins: [
draftCommentsMixin,
IdState({ idProp: (vm) => vm.diffFile.file_hash }),
@@ -307,7 +303,11 @@ export default {
class="diff-td notes-content parallel old"
>
<div v-for="draft in lineDrafts(line, 'left')" :key="draft.id" class="content">
- <draft-note :draft="draft" :line="line.left" />
+ <article class="note-wrapper">
+ <ul class="notes draft-notes">
+ <draft-note :draft="draft" :line="line.left" />
+ </ul>
+ </article>
</div>
</div>
<div
@@ -315,7 +315,11 @@ export default {
class="diff-td notes-content parallel new"
>
<div v-for="draft in lineDrafts(line, 'right')" :key="draft.id" class="content">
- <draft-note :draft="draft" :line="line.right" />
+ <article class="note-wrapper">
+ <ul class="notes draft-notes">
+ <draft-note :draft="draft" :line="line.right" />
+ </ul>
+ </article>
</div>
</div>
</div>
diff --git a/app/assets/javascripts/diffs/i18n.js b/app/assets/javascripts/diffs/i18n.js
index f7f4aad3ad0..0f44eb06cb3 100644
--- a/app/assets/javascripts/diffs/i18n.js
+++ b/app/assets/javascripts/diffs/i18n.js
@@ -19,6 +19,7 @@ export const DIFF_FILE = {
autoCollapsed: __('Files with large changes are collapsed by default.'),
expand: __('Expand file'),
};
+export const START_THREAD = __('Start another thread');
export const SETTINGS_DROPDOWN = {
whitespace: __('Show whitespace changes'),
@@ -49,3 +50,5 @@ export const CONFLICT_TEXT = {
};
export const HIDE_COMMENTS = __('Hide comments');
+
+export const NEW_CODE_QUALITY_FINDINGS = __('New code quality findings');
diff --git a/app/assets/javascripts/diffs/index.js b/app/assets/javascripts/diffs/index.js
index b4ff5e4f250..7da5ef54b80 100644
--- a/app/assets/javascripts/diffs/index.js
+++ b/app/assets/javascripts/diffs/index.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
import { mapActions, mapState, mapGetters } from 'vuex';
import { getCookie, parseBoolean, removeCookie } from '~/lib/utils/common_utils';
+import notesStore from '~/mr_notes/stores';
import eventHub from '../notes/event_hub';
import DiffsApp from './components/app.vue';
@@ -9,7 +10,7 @@ import { TREE_LIST_STORAGE_KEY, DIFF_WHITESPACE_COOKIE_NAME } from './constants'
import { getReviewsForMergeRequest } from './utils/file_reviews';
import { getDerivedMergeRequestInformation } from './utils/merge_request';
-export default function initDiffsApp(store) {
+export default function initDiffsApp(store = notesStore) {
const vm = new Vue({
el: '#js-diffs-app',
name: 'MergeRequestDiffs',
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index c73012527a2..96a73917820 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -52,7 +52,7 @@ import { isCollapsed } from '../utils/diff_file';
import { markFileReview, setReviewsForMergeRequest } from '../utils/file_reviews';
import { getDerivedMergeRequestInformation } from '../utils/merge_request';
import { queueRedisHllEvents } from '../utils/queue_events';
-import TreeWorker from '../workers/tree_worker';
+import TreeWorker from '../workers/tree_worker?worker';
import * as types from './mutation_types';
import {
getDiffPositionByLineCode,
@@ -444,20 +444,27 @@ export const scrollToLineIfNeededParallel = (_, line) => {
}
};
-export const loadCollapsedDiff = ({ commit, getters, state }, file) =>
- axios
- .get(file.load_collapsed_diff_url, {
- params: {
- commit_id: getters.commitId,
- w: state.showWhitespace ? '0' : '1',
- },
- })
- .then((res) => {
- commit(types.ADD_COLLAPSED_DIFFS, {
- file,
- data: res.data,
- });
+export const loadCollapsedDiff = ({ commit, getters, state }, file) => {
+ const versionPath = state.mergeRequestDiff?.version_path;
+ const loadParams = {
+ commit_id: getters.commitId,
+ w: state.showWhitespace ? '0' : '1',
+ };
+
+ if (versionPath) {
+ const { diffId, startSha } = getDerivedMergeRequestInformation({ endpoint: versionPath });
+
+ loadParams.diff_id = diffId;
+ loadParams.start_sha = startSha;
+ }
+
+ return axios.get(file.load_collapsed_diff_url, { params: loadParams }).then((res) => {
+ commit(types.ADD_COLLAPSED_DIFFS, {
+ file,
+ data: res.data,
});
+ });
+};
/**
* Toggles the file discussions after user clicked on the toggle discussions button.
diff --git a/app/assets/javascripts/diffs/utils/merge_request.js b/app/assets/javascripts/diffs/utils/merge_request.js
index edb4304f558..43e04a814c5 100644
--- a/app/assets/javascripts/diffs/utils/merge_request.js
+++ b/app/assets/javascripts/diffs/utils/merge_request.js
@@ -1,14 +1,30 @@
const endpointRE = /^(\/?(.+?)\/(.+?)\/-\/merge_requests\/(\d+)).*$/i;
+function getVersionInfo({ endpoint } = {}) {
+ const dummyRoot = 'https://gitlab.com';
+ const endpointUrl = new URL(endpoint, dummyRoot);
+ const params = Object.fromEntries(endpointUrl.searchParams.entries());
+
+ const { start_sha: startSha, diff_id: diffId } = params;
+
+ return {
+ diffId,
+ startSha,
+ };
+}
+
export function getDerivedMergeRequestInformation({ endpoint } = {}) {
let mrPath;
let userOrGroup;
let project;
let id;
+ let diffId;
+ let startSha;
const matches = endpointRE.exec(endpoint);
if (matches) {
[, mrPath, userOrGroup, project, id] = matches;
+ ({ diffId, startSha } = getVersionInfo({ endpoint }));
}
return {
@@ -16,5 +32,7 @@ export function getDerivedMergeRequestInformation({ endpoint } = {}) {
userOrGroup,
project,
id,
+ diffId,
+ startSha,
};
}
diff --git a/app/assets/javascripts/editor/components/source_editor_toolbar.vue b/app/assets/javascripts/editor/components/source_editor_toolbar.vue
index 2c177634bbe..c72145f9d2f 100644
--- a/app/assets/javascripts/editor/components/source_editor_toolbar.vue
+++ b/app/assets/javascripts/editor/components/source_editor_toolbar.vue
@@ -57,13 +57,12 @@ export default {
>
<div v-for="group in $options.groups" :key="group">
<gl-button-group v-if="hasGroupItems(group)">
- <template v-for="item in getGroupItems(group)">
- <source-editor-toolbar-button
- :key="item.id"
- :button="item"
- @click="$emit('click', item)"
- />
- </template>
+ <source-editor-toolbar-button
+ v-for="item in getGroupItems(group)"
+ :key="item.id"
+ :button="item"
+ @click="$emit('click', item)"
+ />
</gl-button-group>
</div>
</section>
diff --git a/app/assets/javascripts/editor/components/source_editor_toolbar_button.vue b/app/assets/javascripts/editor/components/source_editor_toolbar_button.vue
index 6ce48ddf89a..38f586f0773 100644
--- a/app/assets/javascripts/editor/components/source_editor_toolbar_button.vue
+++ b/app/assets/javascripts/editor/components/source_editor_toolbar_button.vue
@@ -31,12 +31,19 @@ export default {
return Object.entries(this.button).length > 0;
},
},
+ mounted() {
+ if (this.button.data) {
+ Object.entries(this.button.data).forEach(([attr, value]) => {
+ this.$el.dataset[attr] = value;
+ });
+ }
+ },
methods: {
- clickHandler() {
+ clickHandler(event) {
if (this.button.onClick) {
- this.button.onClick();
+ this.button.onClick(event);
}
- this.$emit('click');
+ this.$emit('click', event);
},
},
};
@@ -52,7 +59,7 @@ export default {
:icon="icon"
:title="label"
:aria-label="label"
- data-qa-selector="editor_toolbar_button"
- @click="clickHandler"
+ :class="button.class"
+ @click="clickHandler($event)"
/>
</template>
diff --git a/app/assets/javascripts/editor/constants.js b/app/assets/javascripts/editor/constants.js
index 83cfdd25757..d0649ecccba 100644
--- a/app/assets/javascripts/editor/constants.js
+++ b/app/assets/javascripts/editor/constants.js
@@ -1,5 +1,6 @@
+import { MODIFIER_KEY } from '~/constants';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
-import { s__, __ } from '~/locale';
+import { s__, __, sprintf } from '~/locale';
export const URI_PREFIX = 'gitlab';
export const CONTENT_UPDATE_DEBOUNCE = DEFAULT_DEBOUNCE_AND_THROTTLE_MS;
@@ -62,3 +63,104 @@ export const EXTENSION_MARKDOWN_PREVIEW_PANEL_WIDTH = 0.5; // 50% of the width
export const EXTENSION_MARKDOWN_PREVIEW_UPDATE_DELAY = 250; // ms
export const EXTENSION_MARKDOWN_PREVIEW_LABEL = __('Preview Markdown');
export const EXTENSION_MARKDOWN_HIDE_PREVIEW_LABEL = __('Hide Live Preview');
+export const EXTENSION_MARKDOWN_BUTTONS = [
+ {
+ id: 'bold',
+ label: sprintf(s__('MarkdownEditor|Add bold text (%{modifierKey}B)'), {
+ modifierKey: MODIFIER_KEY,
+ }),
+ data: {
+ mdTag: '**',
+ mdShortcuts: '["mod+b"]',
+ },
+ },
+ {
+ id: 'italic',
+ label: sprintf(s__('MarkdownEditor|Add italic text (%{modifierKey}I)'), {
+ modifierKey: MODIFIER_KEY,
+ }),
+ data: {
+ mdTag: '_',
+ mdShortcuts: '["mod+i"]',
+ },
+ },
+ {
+ id: 'strikethrough',
+ label: sprintf(s__('MarkdownEditor|Add strikethrough text (%{modifierKey}⇧X)'), {
+ modifierKey: MODIFIER_KEY,
+ }),
+ data: {
+ mdTag: '~~',
+ mdShortcuts: '["mod+shift+x]',
+ },
+ },
+ {
+ id: 'quote',
+ label: __('Insert a quote'),
+ data: {
+ mdTag: '> ',
+ mdPrepend: true,
+ },
+ },
+ {
+ id: 'code',
+ label: __('Insert code'),
+ data: {
+ mdTag: '`',
+ mdBlock: '```',
+ },
+ },
+ {
+ id: 'link',
+ label: sprintf(s__('MarkdownEditor|Add a link (%{modifier_key}K)'), {
+ modifierKey: MODIFIER_KEY,
+ }),
+ data: {
+ mdTag: '[{text}](url)',
+ mdSelect: 'url',
+ mdShortcuts: '["mod+k"]',
+ },
+ },
+ {
+ id: 'list-bulleted',
+ label: __('Add a bullet list'),
+ data: {
+ mdTag: '- ',
+ mdPrepend: true,
+ },
+ },
+ {
+ id: 'list-numbered',
+ label: __('Add a numbered list'),
+ data: {
+ mdTag: '1. ',
+ mdPrepend: true,
+ },
+ },
+ {
+ id: 'list-task',
+ label: __('Add a checklist'),
+ data: {
+ mdTag: '- [ ] ',
+ mdPrepend: true,
+ },
+ },
+ {
+ id: 'details-block',
+ label: __('Add a collapsible section'),
+ data: {
+ mdTag: '<details><summary>Click to expand</summary>\n{text}\n</details>',
+ mdPrepend: true,
+ mdSelect: __('Click to expand'),
+ },
+ },
+ {
+ id: 'table',
+ label: __('Add a table'),
+ data: {
+ /* eslint-disable-next-line @gitlab/require-i18n-strings */
+ mdTag: '| header | header |\n| ------ | ------ |\n| | |\n| | |',
+ mdPrepend: true,
+ },
+ },
+];
diff --git a/app/assets/javascripts/editor/extensions/source_editor_markdown_ext.js b/app/assets/javascripts/editor/extensions/source_editor_markdown_ext.js
index a16fe93026e..6105a577996 100644
--- a/app/assets/javascripts/editor/extensions/source_editor_markdown_ext.js
+++ b/app/assets/javascripts/editor/extensions/source_editor_markdown_ext.js
@@ -1,8 +1,37 @@
+import { insertMarkdownText } from '~/lib/utils/text_markdown';
+import { EDITOR_TOOLBAR_RIGHT_GROUP, EXTENSION_MARKDOWN_BUTTONS } from '../constants';
+
export class EditorMarkdownExtension {
static get extensionName() {
return 'EditorMarkdown';
}
+ onSetup(instance) {
+ this.toolbarButtons = [];
+ if (instance.toolbar) {
+ this.setupToolbar(instance);
+ }
+ }
+ onBeforeUnuse(instance) {
+ const ids = this.toolbarButtons.map((item) => item.id);
+ if (instance.toolbar) {
+ instance.toolbar.removeItems(ids);
+ }
+ }
+
+ setupToolbar(instance) {
+ this.toolbarButtons = EXTENSION_MARKDOWN_BUTTONS.map((btn) => {
+ return {
+ ...btn,
+ icon: btn.id,
+ group: EDITOR_TOOLBAR_RIGHT_GROUP,
+ category: 'tertiary',
+ onClick: (e) => instance.insertMarkdown(e),
+ };
+ });
+ instance.toolbar.addItems(this.toolbarButtons);
+ }
+
// eslint-disable-next-line class-methods-use-this
provides() {
return {
@@ -36,6 +65,25 @@ export class EditorMarkdownExtension {
pos.lineNumber += dy;
instance.setPosition(pos);
},
+ insertMarkdown: (instance, e) => {
+ const {
+ mdTag: tag,
+ mdBlock: blockTag,
+ mdPrepend,
+ mdSelect: select,
+ } = e.currentTarget.dataset;
+
+ insertMarkdownText({
+ tag,
+ blockTag,
+ wrap: !mdPrepend,
+ select,
+ selected: instance.getSelectedText(),
+ text: instance.getValue(),
+ editor: instance,
+ });
+ instance.focus();
+ },
/**
* Adjust existing selection to select text within the original selection.
* - If `selectedText` is not supplied, we fetch selected text with
diff --git a/app/assets/javascripts/editor/extensions/source_editor_markdown_livepreview_ext.js b/app/assets/javascripts/editor/extensions/source_editor_markdown_livepreview_ext.js
index dd4a7a689d7..58ddaa94d5e 100644
--- a/app/assets/javascripts/editor/extensions/source_editor_markdown_livepreview_ext.js
+++ b/app/assets/javascripts/editor/extensions/source_editor_markdown_livepreview_ext.js
@@ -120,6 +120,9 @@ export class EditorMarkdownPreviewExtension {
category: 'primary',
selectedLabel: EXTENSION_MARKDOWN_HIDE_PREVIEW_LABEL,
onClick: () => instance.togglePreview(),
+ data: {
+ qaSelector: 'editor_toolbar_button',
+ },
},
];
instance.toolbar.addItems(this.toolbarButtons);
diff --git a/app/assets/javascripts/editor/schema/ci.json b/app/assets/javascripts/editor/schema/ci.json
index 45f063a2048..d94aa73e43a 100644
--- a/app/assets/javascripts/editor/schema/ci.json
+++ b/app/assets/javascripts/editor/schema/ci.json
@@ -41,6 +41,9 @@
"before_script": {
"$ref": "#/definitions/before_script"
},
+ "hooks": {
+ "$ref": "#/definitions/hooks"
+ },
"cache": {
"$ref": "#/definitions/cache"
},
@@ -202,25 +205,11 @@
"when": {
"markdownDescription": "Configure when artifacts are uploaded depended on job status. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#artifactswhen).",
"default": "on_success",
- "oneOf": [
- {
- "enum": [
- "on_success"
- ],
- "description": "Upload artifacts only when the job succeeds (this is the default)."
- },
- {
- "enum": [
- "on_failure"
- ],
- "description": "Upload artifacts only when the job fails."
- },
- {
- "enum": [
- "always"
- ],
- "description": "Upload artifacts regardless of job status."
- }
+ "type": "string",
+ "enum": [
+ "on_success",
+ "on_failure",
+ "always"
]
},
"expire_in": {
@@ -347,10 +336,10 @@
"include_item": {
"oneOf": [
{
- "description": "Will infer the method based on the value. E.g. `https://...` strings will be of type `include:remote`, and `/templates/...` will be of type `include:local`.",
+ "description": "Will infer the method based on the value. E.g. `https://...` strings will be of type `include:remote`, and `/templates/...` or `templates/...` will be of type `include:local`.",
"type": "string",
"format": "uri-reference",
- "pattern": "^(https?://|/).+\\.ya?ml$"
+ "pattern": "^(https?://|/?.?-?(?!\\w+://)\\w).+\\.ya?ml$"
},
{
"type": "object",
@@ -585,56 +574,98 @@
]
}
},
+ "id_tokens": {
+ "type": "object",
+ "markdownDescription": "Defines JWTs to be injected as environment variables.",
+ "patternProperties": {
+ ".*": {
+ "type": "object",
+ "properties": {
+ "aud": {
+ "oneOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "minItems": 1,
+ "uniqueItems": true
+ }
+ ]
+ }
+ },
+ "required": [
+ "aud"
+ ],
+ "additionalProperties": false
+ }
+ }
+ },
"secrets": {
"type": "object",
"markdownDescription": "Defines secrets to be injected as environment variables. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#secrets).",
- "additionalProperties": {
- "type": "object",
- "description": "Environment variable name",
- "properties": {
- "vault": {
- "oneOf": [
- {
- "type": "string",
- "description": "The secret to be fetched from Vault (e.g. 'production/db/password@ops' translates to secret 'ops/data/production/db', field `password`)"
- },
- {
- "type": "object",
- "properties": {
- "engine": {
- "type": "object",
- "properties": {
- "name": {
- "type": "string"
+ "patternProperties": {
+ ".*": {
+ "type": "object",
+ "properties": {
+ "vault": {
+ "oneOf": [
+ {
+ "type": "string",
+ "markdownDescription": "The secret to be fetched from Vault (e.g. 'production/db/password@ops' translates to secret 'ops/data/production/db', field `password`). [Learn More](https://docs.gitlab.com/ee/ci/yaml/#secretsvault)"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "engine": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "path": {
+ "type": "string"
+ }
},
- "path": {
- "type": "string"
- }
+ "required": [
+ "name",
+ "path"
+ ]
},
- "required": [
- "name",
- "path"
- ]
- },
- "path": {
- "type": "string"
+ "path": {
+ "type": "string"
+ },
+ "field": {
+ "type": "string"
+ }
},
- "field": {
- "type": "string"
- }
- },
- "required": [
- "engine",
- "path",
- "field"
- ]
- }
- ]
- }
- },
- "required": [
- "vault"
- ]
+ "required": [
+ "engine",
+ "path",
+ "field"
+ ],
+ "additionalProperties": false
+ }
+ ]
+ },
+ "file": {
+ "type": "boolean",
+ "default": true,
+ "markdownDescription": "Configures the secret to be stored as either a file or variable type CI/CD variable. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#secretsfile)"
+ },
+ "token": {
+ "type": "string",
+ "description": "Specifies the JWT variable that should be used to authenticate with Hashicorp Vault."
+ }
+ },
+ "required": [
+ "vault"
+ ],
+ "additionalProperties": false
+ }
}
},
"before_script": {
@@ -739,7 +770,17 @@
"type": "object",
"properties": {
"value": {
- "type": "string"
+ "type": "string",
+ "markdownDescription": "Default value of the variable. If used with `options`, `value` must be included in the array. [Learn More](https://docs.gitlab.com/ee/ci/pipelines/index.html#prefill-variables-in-manual-pipelines)"
+ },
+ "options": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "minItems": 1,
+ "uniqueItems": true,
+ "markdownDescription": "A list of predefined values that users can select from in the **Run pipeline** page when running a pipeline manually. [Learn More](https://docs.gitlab.com/ee/ci/pipelines/index.html#configure-a-list-of-selectable-values-for-a-prefilled-variable)"
},
"description": {
"type": "string",
@@ -959,6 +1000,7 @@
"default": false
},
"when": {
+ "type": "string",
"markdownDescription": "Defines when to save the cache, based on the status of the job. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#cachewhen).",
"default": "on_success",
"enum": [
@@ -1200,6 +1242,9 @@
"after_script": {
"$ref": "#/definitions/after_script"
},
+ "hooks": {
+ "$ref": "#/definitions/hooks"
+ },
"rules": {
"$ref": "#/definitions/rules"
},
@@ -1209,6 +1254,9 @@
"cache": {
"$ref": "#/definitions/cache"
},
+ "id_tokens": {
+ "$ref": "#/definitions/id_tokens"
+ },
"secrets": {
"$ref": "#/definitions/secrets"
},
@@ -1861,6 +1909,39 @@
}
]
}
+ },
+ "hooks": {
+ "type": "object",
+ "markdownDescription": "Specifies lists of commands to execute on the runner at certain stages of job execution. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#hooks).",
+ "properties": {
+ "pre_get_sources_script": {
+ "markdownDescription": "Specifies a list of commands to execute on the runner before updating the Git repository and any submodules. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#hookspre_get_sources_script).",
+ "oneOf": [
+ {
+ "type": "string",
+ "minLength": 1
+ },
+ {
+ "type": "array",
+ "items": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ ]
+ },
+ "minItems": 1
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
}
}
} \ No newline at end of file
diff --git a/app/assets/javascripts/environments/components/deploy_board.vue b/app/assets/javascripts/environments/components/deploy_board.vue
index f22a0705b3d..31bc462f0b9 100644
--- a/app/assets/javascripts/environments/components/deploy_board.vue
+++ b/app/assets/javascripts/environments/components/deploy_board.vue
@@ -15,10 +15,10 @@ import {
GlLink,
GlTooltip,
GlTooltipDirective,
- GlSafeHtmlDirective as SafeHtml,
GlSprintf,
} from '@gitlab/ui';
import { isEmpty } from 'lodash';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { s__, n__ } from '~/locale';
import InstanceComponent from '~/vue_shared/components/deployment_instance.vue';
import { STATUS_MAP, CANARY_STATUS } from '../constants';
diff --git a/app/assets/javascripts/environments/environment_details/constants.js b/app/assets/javascripts/environments/environment_details/constants.js
new file mode 100644
index 00000000000..56c70c354b7
--- /dev/null
+++ b/app/assets/javascripts/environments/environment_details/constants.js
@@ -0,0 +1,47 @@
+import { __ } from '~/locale';
+
+export const ENVIRONMENT_DETAILS_PAGE_SIZE = 20;
+export const ENVIRONMENT_DETAILS_TABLE_FIELDS = [
+ {
+ key: 'status',
+ label: __('Status'),
+ columnClass: 'gl-w-10p',
+ tdClass: 'gl-vertical-align-middle!',
+ },
+ {
+ key: 'id',
+ label: __('ID'),
+ columnClass: 'gl-w-5p',
+ tdClass: 'gl-vertical-align-middle!',
+ },
+ {
+ key: 'triggerer',
+ label: __('Triggerer'),
+ columnClass: 'gl-w-10p',
+ tdClass: 'gl-vertical-align-middle!',
+ },
+ {
+ key: 'commit',
+ label: __('Commit'),
+ columnClass: 'gl-w-20p',
+ tdClass: 'gl-vertical-align-middle!',
+ },
+ {
+ key: 'job',
+ label: __('Job'),
+ columnClass: 'gl-w-20p',
+ tdClass: 'gl-vertical-align-middle!',
+ },
+ {
+ key: 'created',
+ label: __('Created'),
+ columnClass: 'gl-w-10p',
+ tdClass: 'gl-vertical-align-middle! gl-white-space-nowrap',
+ },
+ {
+ key: 'deployed',
+ label: __('Deployed'),
+ columnClass: 'gl-w-10p',
+ tdClass: 'gl-vertical-align-middle! gl-white-space-nowrap',
+ },
+];
diff --git a/app/assets/javascripts/environments/environment_details/index.vue b/app/assets/javascripts/environments/environment_details/index.vue
new file mode 100644
index 00000000000..435d3fd820e
--- /dev/null
+++ b/app/assets/javascripts/environments/environment_details/index.vue
@@ -0,0 +1,118 @@
+<script>
+import {
+ GlTableLite,
+ GlAvatarLink,
+ GlAvatar,
+ GlLink,
+ GlTooltipDirective,
+ GlTruncate,
+ GlBadge,
+ GlLoadingIcon,
+} from '@gitlab/ui';
+import Commit from '~/vue_shared/components/commit.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import environmentDetailsQuery from '../graphql/queries/environment_details.query.graphql';
+import { convertToDeploymentTableRow } from '../helpers/deployment_data_transformation_helper';
+import DeploymentStatusBadge from '../components/deployment_status_badge.vue';
+import { ENVIRONMENT_DETAILS_PAGE_SIZE, ENVIRONMENT_DETAILS_TABLE_FIELDS } from './constants';
+
+export default {
+ components: {
+ GlLoadingIcon,
+ GlBadge,
+ DeploymentStatusBadge,
+ TimeAgoTooltip,
+ GlTableLite,
+ GlAvatarLink,
+ GlAvatar,
+ GlLink,
+ GlTruncate,
+ Commit,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ projectFullPath: {
+ type: String,
+ required: true,
+ },
+ environmentName: {
+ type: String,
+ required: true,
+ },
+ },
+ apollo: {
+ project: {
+ query: environmentDetailsQuery,
+ variables() {
+ return {
+ projectFullPath: this.projectFullPath,
+ environmentName: this.environmentName,
+ pageSize: ENVIRONMENT_DETAILS_PAGE_SIZE,
+ };
+ },
+ },
+ },
+ data() {
+ return {
+ project: {
+ loading: true,
+ },
+ loading: 0,
+ tableFields: ENVIRONMENT_DETAILS_TABLE_FIELDS,
+ };
+ },
+ computed: {
+ deployments() {
+ return this.project.environment?.deployments.nodes.map(convertToDeploymentTableRow) || [];
+ },
+ isLoading() {
+ return this.$apollo.queries.project.loading;
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <gl-loading-icon v-if="isLoading" size="lg" class="mt-3" />
+ <gl-table-lite v-else :items="deployments" :fields="tableFields" fixed stacked="lg">
+ <template #table-colgroup="{ fields }">
+ <col v-for="field in fields" :key="field.key" :class="field.columnClass" />
+ </template>
+ <template #cell(status)="{ item }">
+ <div>
+ <deployment-status-badge :status="item.status" />
+ </div>
+ </template>
+ <template #cell(id)="{ item }">
+ <strong>{{ item.id }}</strong>
+ </template>
+ <template #cell(triggerer)="{ item }">
+ <gl-avatar-link :href="item.triggerer.webUrl">
+ <gl-avatar
+ v-gl-tooltip
+ :title="item.triggerer.name"
+ :src="item.triggerer.avatarUrl"
+ :size="24"
+ />
+ </gl-avatar-link>
+ </template>
+ <template #cell(commit)="{ item }">
+ <commit v-bind="item.commit" />
+ </template>
+ <template #cell(job)="{ item }">
+ <gl-link v-if="item.job" :href="item.job.webPath">
+ <gl-truncate :text="item.job.label" />
+ </gl-link>
+ <gl-badge v-else variant="info">{{ __('API') }}</gl-badge>
+ </template>
+ <template #cell(created)="{ item }">
+ <time-ago-tooltip :time="item.created" />
+ </template>
+ <template #cell(deployed)="{ item }">
+ <time-ago-tooltip :time="item.deployed" />
+ </template>
+ </gl-table-lite>
+ </div>
+</template>
diff --git a/app/assets/javascripts/environments/graphql/queries/environment_details.query.graphql b/app/assets/javascripts/environments/graphql/queries/environment_details.query.graphql
new file mode 100644
index 00000000000..e8f2a2cdf7f
--- /dev/null
+++ b/app/assets/javascripts/environments/graphql/queries/environment_details.query.graphql
@@ -0,0 +1,48 @@
+query getEnvironmentDetails($projectFullPath: ID!, $environmentName: String, $pageSize: Int) {
+ project(fullPath: $projectFullPath) {
+ id
+ name
+ fullPath
+ environment(name: $environmentName) {
+ id
+ name
+ deployments(orderBy: { createdAt: DESC }, first: $pageSize) {
+ nodes {
+ id
+ iid
+ status
+ ref
+ tag
+ job {
+ name
+ id
+ webPath
+ }
+ commit {
+ id
+ shortId
+ message
+ webUrl
+ authorGravatar
+ authorName
+ authorEmail
+ author {
+ id
+ name
+ avatarUrl
+ webUrl
+ }
+ }
+ triggerer {
+ id
+ webUrl
+ name
+ avatarUrl
+ }
+ createdAt
+ finishedAt
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/environments/helpers/deployment_data_transformation_helper.js b/app/assets/javascripts/environments/helpers/deployment_data_transformation_helper.js
new file mode 100644
index 00000000000..bfe92fe3125
--- /dev/null
+++ b/app/assets/javascripts/environments/helpers/deployment_data_transformation_helper.js
@@ -0,0 +1,62 @@
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+
+/**
+ * This function transforms Commit object coming from GraphQL to object compatible with app/assets/javascripts/vue_shared/components/commit.vue author object
+ * @param {Object} Commit
+ * @returns {Object}
+ */
+export const getAuthorFromCommit = (commit) => {
+ if (commit.author) {
+ return {
+ username: commit.author.name,
+ path: commit.author.webUrl,
+ avatar_url: commit.author.avatarUrl,
+ };
+ }
+ return {
+ username: commit.authorName,
+ path: `mailto:${commit.authorEmail}`,
+ avatar_url: commit.authorGravatar,
+ };
+};
+
+/**
+ * This function transforms deploymentNode object coming from GraphQL to object compatible with app/assets/javascripts/vue_shared/components/commit.vue
+ * @param {Object} deploymentNode
+ * @returns {Object}
+ */
+export const getCommitFromDeploymentNode = (deploymentNode) => {
+ if (!deploymentNode.commit) {
+ throw new Error("deploymentNode argument doesn't have 'commit' field", deploymentNode);
+ }
+ return {
+ title: deploymentNode.commit.message,
+ commitUrl: deploymentNode.commit.webUrl,
+ shortSha: deploymentNode.commit.shortId,
+ tag: deploymentNode.tag,
+ commitRef: {
+ name: deploymentNode.ref,
+ },
+ author: getAuthorFromCommit(deploymentNode.commit),
+ };
+};
+
+/**
+ * This function transforms deploymentNode object coming from GraphQL to object compatible with app/assets/javascripts/environments/environment_details/page.vue table
+ * @param {Object} deploymentNode
+ * @returns {Object}
+ */
+export const convertToDeploymentTableRow = (deploymentNode) => {
+ return {
+ status: deploymentNode.status.toLowerCase(),
+ id: deploymentNode.iid,
+ triggerer: deploymentNode.triggerer,
+ commit: getCommitFromDeploymentNode(deploymentNode),
+ job: deploymentNode.job && {
+ webPath: deploymentNode.job.webPath,
+ label: `${deploymentNode.job.name} (#${getIdFromGraphQLId(deploymentNode.job.id)})`,
+ },
+ created: deploymentNode.createdAt || '',
+ deployed: deploymentNode.finishedAt || '',
+ };
+};
diff --git a/app/assets/javascripts/environments/mount_show.js b/app/assets/javascripts/environments/mount_show.js
index 6df4fad83f2..ba816599ac2 100644
--- a/app/assets/javascripts/environments/mount_show.js
+++ b/app/assets/javascripts/environments/mount_show.js
@@ -1,6 +1,8 @@
import Vue from 'vue';
+import VueApollo from 'vue-apollo';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import EnvironmentsDetailHeader from './components/environments_detail_header.vue';
+import { apolloProvider } from './graphql/client';
import environmentsMixin from './mixins/environments_mixin';
export const initHeader = () => {
@@ -41,7 +43,33 @@ export const initHeader = () => {
cancelAutoStopPath: dataset.environmentCancelAutoStopPath,
terminalPath: dataset.environmentTerminalPath,
metricsPath: dataset.environmentMetricsPath,
- updatePath: dataset.environmentEditPath,
+ updatePath: dataset.tnvironmentEditPath,
+ },
+ });
+ },
+ });
+};
+
+export const initPage = async () => {
+ if (!gon.features.environmentDetailsVue) {
+ return null;
+ }
+ const EnvironmentsDetailPageModule = await import('./environment_details/index.vue');
+ const EnvironmentsDetailPage = EnvironmentsDetailPageModule.default;
+ const dataElement = document.getElementById('environments-detail-view');
+ const dataSet = convertObjectPropsToCamelCase(JSON.parse(dataElement.dataset.details));
+
+ Vue.use(VueApollo);
+ const el = document.getElementById('environment_details_page');
+ return new Vue({
+ el,
+ apolloProvider: apolloProvider(),
+ provide: {},
+ render(createElement) {
+ return createElement(EnvironmentsDetailPage, {
+ props: {
+ projectFullPath: dataSet.projectFullPath,
+ environmentName: dataSet.name,
},
});
},
diff --git a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
index de4b11699fc..122c7c005e9 100644
--- a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
+++ b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
@@ -357,7 +357,7 @@ export default {
>
<span class="d-flex">
<gl-icon
- class="gl-new-dropdown-item-check-icon"
+ class="gl-dropdown-item-check-icon"
:class="{ invisible: !isCurrentStatusFilter(status) }"
name="mobile-issue-close"
/>
@@ -374,7 +374,7 @@ export default {
>
<span class="d-flex">
<gl-icon
- class="gl-new-dropdown-item-check-icon"
+ class="gl-dropdown-item-check-icon"
:class="{ invisible: !isCurrentSortField(field) }"
name="mobile-issue-close"
/>
diff --git a/app/assets/javascripts/error_tracking/components/stacktrace_entry.vue b/app/assets/javascripts/error_tracking/components/stacktrace_entry.vue
index 34d01f21da2..6ddd982ebf1 100644
--- a/app/assets/javascripts/error_tracking/components/stacktrace_entry.vue
+++ b/app/assets/javascripts/error_tracking/components/stacktrace_entry.vue
@@ -1,5 +1,6 @@
<script>
-import { GlTooltip, GlSprintf, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlTooltip, GlSprintf, GlIcon } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import FileIcon from '~/vue_shared/components/file_icon.vue';
diff --git a/app/assets/javascripts/feature_flags/components/feature_flags_table.vue b/app/assets/javascripts/feature_flags/components/feature_flags_table.vue
index f0f42d19ea5..286b214b511 100644
--- a/app/assets/javascripts/feature_flags/components/feature_flags_table.vue
+++ b/app/assets/javascripts/feature_flags/components/feature_flags_table.vue
@@ -4,6 +4,8 @@ import { __, s__, sprintf } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { labelForStrategy } from '../utils';
+import StrategyLabel from './strategy_label.vue';
+
export default {
i18n: {
deleteLabel: __('Delete'),
@@ -15,6 +17,7 @@ export default {
GlButton,
GlModal,
GlToggle,
+ StrategyLabel,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -166,14 +169,13 @@ export default {
<div
class="table-mobile-content d-flex flex-wrap justify-content-end justify-content-md-start js-feature-flag-environments"
>
- <gl-badge
+ <strategy-label
v-for="strategy in featureFlag.strategies"
:key="strategy.id"
- data-testid="strategy-badge"
- variant="info"
- class="gl-mr-3 gl-mt-2 gl-white-space-normal gl-text-left gl-px-5"
- >{{ strategyBadgeText(strategy) }}</gl-badge
- >
+ data-testid="strategy-label"
+ class="w-100 gl-mr-3 gl-mt-2 gl-white-space-normal gl-text-left"
+ v-bind="strategyBadgeText(strategy)"
+ />
</div>
</div>
diff --git a/app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue b/app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue
index 1a470d74b59..0fde87dd0ba 100644
--- a/app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue
+++ b/app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue
@@ -90,10 +90,10 @@ export default {
:id="inputId"
:value="percentage"
:state="isValid"
- class="rollout-percentage gl-text-right gl-w-9"
type="number"
min="0"
max="100"
+ size="xs"
@input="onPercentageChange"
/>
<span class="ml-1">%</span>
diff --git a/app/assets/javascripts/feature_flags/components/strategies/percent_rollout.vue b/app/assets/javascripts/feature_flags/components/strategies/percent_rollout.vue
index 91e1b85d66e..0acb0d4366c 100644
--- a/app/assets/javascripts/feature_flags/components/strategies/percent_rollout.vue
+++ b/app/assets/javascripts/feature_flags/components/strategies/percent_rollout.vue
@@ -56,10 +56,10 @@ export default {
:id="inputId"
:value="percentage"
:state="isValid"
- class="rollout-percentage gl-text-right gl-w-9"
type="number"
min="0"
max="100"
+ size="xs"
@input="onPercentageChange"
/>
<span class="gl-ml-2">%</span>
diff --git a/app/assets/javascripts/feature_flags/components/strategy_label.vue b/app/assets/javascripts/feature_flags/components/strategy_label.vue
new file mode 100644
index 00000000000..c2d3ec5708f
--- /dev/null
+++ b/app/assets/javascripts/feature_flags/components/strategy_label.vue
@@ -0,0 +1,29 @@
+<script>
+export default {
+ props: {
+ name: {
+ type: String,
+ required: true,
+ },
+ scopes: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ parameters: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <strong class="gl-fw-bold"
+ >{{ name }}<span v-if="parameters"> - {{ parameters }}</span
+ >:</strong
+ >
+ <span v-if="scopes">{{ scopes }}</span>
+ </div>
+</template>
diff --git a/app/assets/javascripts/feature_flags/utils.js b/app/assets/javascripts/feature_flags/utils.js
index e77cb8406cc..47deeab0571 100644
--- a/app/assets/javascripts/feature_flags/utils.js
+++ b/app/assets/javascripts/feature_flags/utils.js
@@ -50,17 +50,11 @@ const scopeName = ({ environment_scope: scope }) =>
export const labelForStrategy = (strategy) => {
const { name, parameters } = badgeTextByType[strategy.name];
+ const scopes = strategy.scopes.map(scopeName).join(', ');
- if (parameters) {
- return sprintf('%{name} - %{parameters}: %{scopes}', {
- name,
- parameters: parameters(strategy),
- scopes: strategy.scopes.map(scopeName).join(', '),
- });
- }
-
- return sprintf('%{name}: %{scopes}', {
+ return {
name,
- scopes: strategy.scopes.map(scopeName).join(', '),
- });
+ parameters: parameters ? parameters(strategy) : null,
+ scopes,
+ };
};
diff --git a/app/assets/javascripts/feature_highlight/feature_highlight_popover.vue b/app/assets/javascripts/feature_highlight/feature_highlight_popover.vue
index 79d7eb94569..1c6e6380e76 100644
--- a/app/assets/javascripts/feature_highlight/feature_highlight_popover.vue
+++ b/app/assets/javascripts/feature_highlight/feature_highlight_popover.vue
@@ -1,12 +1,7 @@
<script>
import clusterPopover from '@gitlab/svgs/dist/illustrations/cluster_popover.svg';
-import {
- GlPopover,
- GlSprintf,
- GlLink,
- GlButton,
- GlSafeHtmlDirective as SafeHtml,
-} from '@gitlab/ui';
+import { GlPopover, GlSprintf, GlLink, GlButton } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { __ } from '~/locale';
import { POPOVER_TARGET_ID } from './constants';
import { dismiss } from './feature_highlight_helper';
diff --git a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js
index d9c627f5c93..397ba879866 100644
--- a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js
+++ b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js
@@ -1,9 +1,16 @@
-import { __, s__ } from '~/locale';
+import { __ } from '~/locale';
+import {
+ TOKEN_TITLE_APPROVED_BY,
+ TOKEN_TITLE_REVIEWER,
+ TOKEN_TYPE_APPROVED_BY,
+ TOKEN_TYPE_REVIEWER,
+ TOKEN_TYPE_TARGET_BRANCH,
+} from '~/vue_shared/components/filtered_search_bar/constants';
export default (IssuableTokenKeys, disableTargetBranchFilter = false) => {
const reviewerToken = {
- formattedKey: s__('SearchToken|Reviewer'),
- key: 'reviewer',
+ formattedKey: TOKEN_TITLE_REVIEWER,
+ key: TOKEN_TYPE_REVIEWER,
type: 'string',
param: 'username',
symbol: '@',
@@ -53,7 +60,7 @@ export default (IssuableTokenKeys, disableTargetBranchFilter = false) => {
if (!disableTargetBranchFilter) {
const targetBranchToken = {
formattedKey: __('Target-Branch'),
- key: 'target-branch',
+ key: TOKEN_TYPE_TARGET_BRANCH,
type: 'string',
param: '',
symbol: '',
@@ -67,8 +74,8 @@ export default (IssuableTokenKeys, disableTargetBranchFilter = false) => {
const approvedBy = {
token: {
- formattedKey: __('Approved-By'),
- key: 'approved-by',
+ formattedKey: TOKEN_TITLE_APPROVED_BY,
+ key: TOKEN_TYPE_APPROVED_BY,
type: 'array',
param: 'usernames[]',
symbol: '@',
@@ -76,8 +83,8 @@ export default (IssuableTokenKeys, disableTargetBranchFilter = false) => {
tag: '@approved-by',
},
tokenAlternative: {
- formattedKey: __('Approved-By'),
- key: 'approved-by',
+ formattedKey: TOKEN_TITLE_APPROVED_BY,
+ key: TOKEN_TYPE_APPROVED_BY,
type: 'string',
param: 'usernames',
symbol: '@',
@@ -85,25 +92,25 @@ export default (IssuableTokenKeys, disableTargetBranchFilter = false) => {
condition: [
{
url: 'approved_by_usernames[]=None',
- tokenKey: 'approved-by',
+ tokenKey: TOKEN_TYPE_APPROVED_BY,
value: __('None'),
operator: '=',
},
{
url: 'not[approved_by_usernames][]=None',
- tokenKey: 'approved-by',
+ tokenKey: TOKEN_TYPE_APPROVED_BY,
value: __('None'),
operator: '!=',
},
{
url: 'approved_by_usernames[]=Any',
- tokenKey: 'approved-by',
+ tokenKey: TOKEN_TYPE_APPROVED_BY,
value: __('Any'),
operator: '=',
},
{
url: 'not[approved_by_usernames][]=Any',
- tokenKey: 'approved-by',
+ tokenKey: TOKEN_TYPE_APPROVED_BY,
value: __('Any'),
operator: '!=',
},
diff --git a/app/assets/javascripts/filtered_search/available_dropdown_mappings.js b/app/assets/javascripts/filtered_search/available_dropdown_mappings.js
index 3913e4e8d81..1f8baa470d8 100644
--- a/app/assets/javascripts/filtered_search/available_dropdown_mappings.js
+++ b/app/assets/javascripts/filtered_search/available_dropdown_mappings.js
@@ -1,5 +1,17 @@
import { sortMilestonesByDueDate } from '~/milestones/utils';
-import { mergeUrlParams } from '../lib/utils/url_utility';
+import { mergeUrlParams } from '~/lib/utils/url_utility';
+import {
+ TOKEN_TYPE_APPROVED_BY,
+ TOKEN_TYPE_ASSIGNEE,
+ TOKEN_TYPE_AUTHOR,
+ TOKEN_TYPE_CONFIDENTIAL,
+ TOKEN_TYPE_LABEL,
+ TOKEN_TYPE_MILESTONE,
+ TOKEN_TYPE_MY_REACTION,
+ TOKEN_TYPE_RELEASE,
+ TOKEN_TYPE_REVIEWER,
+ TOKEN_TYPE_TARGET_BRANCH,
+} from '~/vue_shared/components/filtered_search_bar/constants';
import DropdownEmoji from './dropdown_emoji';
import DropdownHint from './dropdown_hint';
import DropdownNonUser from './dropdown_non_user';
@@ -58,17 +70,17 @@ export default class AvailableDropdownMappings {
getMappings() {
return {
- author: {
+ [TOKEN_TYPE_AUTHOR]: {
reference: null,
gl: DropdownUser,
element: this.container.querySelector('#js-dropdown-author'),
},
- assignee: {
+ [TOKEN_TYPE_ASSIGNEE]: {
reference: null,
gl: DropdownUser,
element: this.container.querySelector('#js-dropdown-assignee'),
},
- reviewer: {
+ [TOKEN_TYPE_REVIEWER]: {
reference: null,
gl: DropdownUser,
element: this.container.querySelector('#js-dropdown-reviewer'),
@@ -78,12 +90,12 @@ export default class AvailableDropdownMappings {
gl: DropdownUser,
element: this.container.getElementById('js-dropdown-attention-requested'),
},
- 'approved-by': {
+ [TOKEN_TYPE_APPROVED_BY]: {
reference: null,
gl: DropdownUser,
element: this.container.querySelector('#js-dropdown-approved-by'),
},
- milestone: {
+ [TOKEN_TYPE_MILESTONE]: {
reference: null,
gl: DropdownNonUser,
extraArguments: {
@@ -93,7 +105,7 @@ export default class AvailableDropdownMappings {
},
element: this.container.querySelector('#js-dropdown-milestone'),
},
- release: {
+ [TOKEN_TYPE_RELEASE]: {
reference: null,
gl: DropdownNonUser,
extraArguments: {
@@ -106,7 +118,7 @@ export default class AvailableDropdownMappings {
},
element: this.container.querySelector('#js-dropdown-release'),
},
- label: {
+ [TOKEN_TYPE_LABEL]: {
reference: null,
gl: DropdownNonUser,
extraArguments: {
@@ -116,7 +128,7 @@ export default class AvailableDropdownMappings {
},
element: this.container.querySelector('#js-dropdown-label'),
},
- 'my-reaction': {
+ [TOKEN_TYPE_MY_REACTION]: {
reference: null,
gl: DropdownEmoji,
element: this.container.querySelector('#js-dropdown-my-reaction'),
@@ -126,12 +138,12 @@ export default class AvailableDropdownMappings {
gl: DropdownNonUser,
element: this.container.querySelector('#js-dropdown-wip'),
},
- confidential: {
+ [TOKEN_TYPE_CONFIDENTIAL]: {
reference: null,
gl: DropdownNonUser,
element: this.container.querySelector('#js-dropdown-confidential'),
},
- 'target-branch': {
+ [TOKEN_TYPE_TARGET_BRANCH]: {
reference: null,
gl: DropdownNonUser,
extraArguments: {
diff --git a/app/assets/javascripts/filtered_search/constants.js b/app/assets/javascripts/filtered_search/constants.js
index e07dccd11e8..b328ae6a872 100644
--- a/app/assets/javascripts/filtered_search/constants.js
+++ b/app/assets/javascripts/filtered_search/constants.js
@@ -1,4 +1,17 @@
-export const USER_TOKEN_TYPES = ['author', 'assignee', 'approved-by', 'reviewer', 'attention'];
+import {
+ TOKEN_TYPE_APPROVED_BY,
+ TOKEN_TYPE_ASSIGNEE,
+ TOKEN_TYPE_AUTHOR,
+ TOKEN_TYPE_REVIEWER,
+} from '~/vue_shared/components/filtered_search_bar/constants';
+
+export const USER_TOKEN_TYPES = [
+ TOKEN_TYPE_AUTHOR,
+ TOKEN_TYPE_ASSIGNEE,
+ TOKEN_TYPE_APPROVED_BY,
+ TOKEN_TYPE_REVIEWER,
+ 'attention',
+];
export const DROPDOWN_TYPE = {
hint: 'hint',
diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js
index 22e1604871a..38909db0555 100644
--- a/app/assets/javascripts/filtered_search/dropdown_utils.js
+++ b/app/assets/javascripts/filtered_search/dropdown_utils.js
@@ -1,4 +1,5 @@
import { last } from 'lodash';
+import { TOKEN_TYPE_LABEL } from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchContainer from './container';
import FilteredSearchDropdownManager from './filtered_search_dropdown_manager';
import FilteredSearchTokenizer from './filtered_search_tokenizer';
@@ -113,7 +114,7 @@ export default class DropdownUtils {
visualToken &&
visualToken.querySelector('.value') &&
visualToken.querySelector('.value').textContent.trim();
- if (tokenName === 'label' && tokenValue) {
+ if (tokenName === TOKEN_TYPE_LABEL && tokenValue) {
// remove leading symbol and wrapping quotes
tokenValue = tokenValue.replace(/^~("|')?(.*)/, '$2').replace(/("|')$/, '');
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index bc0f5398b4c..16c70fdd069 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -10,8 +10,12 @@ import {
DOWN_KEY_CODE,
} from '~/lib/utils/keycodes';
import { __ } from '~/locale';
-import { addClassIfElementExists } from '../lib/utils/dom_utils';
-import { visitUrl, getUrlParamsArray, getParameterByName } from '../lib/utils/url_utility';
+import { addClassIfElementExists } from '~/lib/utils/dom_utils';
+import { visitUrl, getUrlParamsArray, getParameterByName } from '~/lib/utils/url_utility';
+import {
+ TOKEN_TYPE_ASSIGNEE,
+ TOKEN_TYPE_AUTHOR,
+} from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchContainer from './container';
import DropdownUtils from './dropdown_utils';
import eventHub from './event_hub';
@@ -675,7 +679,7 @@ export default class FilteredSearchManager {
const id = parseInt(value, 10);
if (usernameParams[id]) {
hasFilteredSearch = true;
- const tokenName = 'assignee';
+ const tokenName = TOKEN_TYPE_ASSIGNEE;
const canEdit = this.canEdit && this.canEdit(tokenName);
const operator = FilteredSearchVisualTokens.getOperatorToken(usernameParams[id]);
const valueToken = FilteredSearchVisualTokens.getValueToken(usernameParams[id]);
@@ -688,7 +692,7 @@ export default class FilteredSearchManager {
const id = parseInt(value, 10);
if (usernameParams[id]) {
hasFilteredSearch = true;
- const tokenName = 'author';
+ const tokenName = TOKEN_TYPE_AUTHOR;
const canEdit = this.canEdit && this.canEdit(tokenName);
const operator = FilteredSearchVisualTokens.getOperatorToken(usernameParams[id]);
const valueToken = FilteredSearchVisualTokens.getValueToken(usernameParams[id]);
diff --git a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
index 0c01220a7be..4994559e923 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
@@ -1,5 +1,6 @@
import { spriteIcon } from '~/lib/utils/common_utils';
import { objectToQuery } from '~/lib/utils/url_utility';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchContainer from './container';
import VisualTokenValue from './visual_token_value';
@@ -38,7 +39,7 @@ export default class FilteredSearchVisualTokens {
lastVisualToken,
isLastVisualTokenValid:
lastVisualToken === null ||
- lastVisualToken.className.indexOf('filtered-search-term') !== -1 ||
+ lastVisualToken.className.indexOf(FILTERED_SEARCH_TERM) !== -1 ||
(lastVisualToken &&
lastVisualToken.querySelector('.operator') !== null &&
lastVisualToken.querySelector('.value') !== null),
@@ -113,7 +114,7 @@ export default class FilteredSearchVisualTokens {
} = options;
const li = document.createElement('li');
li.classList.add('js-visual-token');
- li.classList.add(isSearchTerm ? 'filtered-search-term' : 'filtered-search-token');
+ li.classList.add(isSearchTerm ? FILTERED_SEARCH_TERM : 'filtered-search-token');
if (!isSearchTerm) {
li.classList.add(tokenClass);
@@ -239,7 +240,7 @@ export default class FilteredSearchVisualTokens {
static addSearchVisualToken(searchTerm) {
const { lastVisualToken } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
- if (lastVisualToken && lastVisualToken.classList.contains('filtered-search-term')) {
+ if (lastVisualToken && lastVisualToken.classList.contains(FILTERED_SEARCH_TERM)) {
lastVisualToken.querySelector('.name').textContent += ` ${searchTerm}`;
} else {
FilteredSearchVisualTokens.addVisualTokenElement({
diff --git a/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js
index d6e7887f93f..8aa99ec52f9 100644
--- a/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js
+++ b/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js
@@ -7,13 +7,20 @@ import {
TOKEN_TITLE_MILESTONE,
TOKEN_TITLE_MY_REACTION,
TOKEN_TITLE_RELEASE,
+ TOKEN_TYPE_ASSIGNEE,
+ TOKEN_TYPE_AUTHOR,
+ TOKEN_TYPE_LABEL,
+ TOKEN_TYPE_MILESTONE,
+ TOKEN_TYPE_MY_REACTION,
+ TOKEN_TYPE_RELEASE,
+ TOKEN_TYPE_REVIEWER,
} from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchTokenKeys from './filtered_search_token_keys';
export const tokenKeys = [
{
formattedKey: TOKEN_TITLE_AUTHOR,
- key: 'author',
+ key: TOKEN_TYPE_AUTHOR,
type: 'string',
param: 'username',
symbol: '@',
@@ -22,7 +29,7 @@ export const tokenKeys = [
},
{
formattedKey: TOKEN_TITLE_ASSIGNEE,
- key: 'assignee',
+ key: TOKEN_TYPE_ASSIGNEE,
type: 'string',
param: 'username',
symbol: '@',
@@ -31,7 +38,7 @@ export const tokenKeys = [
},
{
formattedKey: TOKEN_TITLE_MILESTONE,
- key: 'milestone',
+ key: TOKEN_TYPE_MILESTONE,
type: 'string',
param: 'title',
symbol: '%',
@@ -40,7 +47,7 @@ export const tokenKeys = [
},
{
formattedKey: TOKEN_TITLE_RELEASE,
- key: 'release',
+ key: TOKEN_TYPE_RELEASE,
type: 'string',
param: 'tag',
symbol: '',
@@ -49,7 +56,7 @@ export const tokenKeys = [
},
{
formattedKey: TOKEN_TITLE_LABEL,
- key: 'label',
+ key: TOKEN_TYPE_LABEL,
type: 'array',
param: 'name[]',
symbol: '~',
@@ -62,7 +69,7 @@ if (gon.current_user_id) {
// Appending tokenkeys only logged-in
tokenKeys.push({
formattedKey: TOKEN_TITLE_MY_REACTION,
- key: 'my-reaction',
+ key: TOKEN_TYPE_MY_REACTION,
type: 'string',
param: 'emoji',
symbol: '',
@@ -74,7 +81,7 @@ if (gon.current_user_id) {
export const alternativeTokenKeys = [
{
formattedKey: TOKEN_TITLE_LABEL,
- key: 'label',
+ key: TOKEN_TYPE_LABEL,
type: 'string',
param: 'name',
symbol: '~',
@@ -85,77 +92,77 @@ export const conditions = flattenDeep(
[
{
url: 'assignee_id=None',
- tokenKey: 'assignee',
+ tokenKey: TOKEN_TYPE_ASSIGNEE,
value: __('None'),
},
{
url: 'assignee_id=Any',
- tokenKey: 'assignee',
+ tokenKey: TOKEN_TYPE_ASSIGNEE,
value: __('Any'),
},
{
url: 'reviewer_id=None',
- tokenKey: 'reviewer',
+ tokenKey: TOKEN_TYPE_REVIEWER,
value: __('None'),
},
{
url: 'reviewer_id=Any',
- tokenKey: 'reviewer',
+ tokenKey: TOKEN_TYPE_REVIEWER,
value: __('Any'),
},
{
url: 'author_username=support-bot',
- tokenKey: 'author',
+ tokenKey: TOKEN_TYPE_AUTHOR,
value: 'support-bot',
},
{
url: 'milestone_title=None',
- tokenKey: 'milestone',
+ tokenKey: TOKEN_TYPE_MILESTONE,
value: __('None'),
},
{
url: 'milestone_title=Any',
- tokenKey: 'milestone',
+ tokenKey: TOKEN_TYPE_MILESTONE,
value: __('Any'),
},
{
url: 'milestone_title=%23upcoming',
- tokenKey: 'milestone',
+ tokenKey: TOKEN_TYPE_MILESTONE,
value: __('Upcoming'),
},
{
url: 'milestone_title=%23started',
- tokenKey: 'milestone',
+ tokenKey: TOKEN_TYPE_MILESTONE,
value: __('Started'),
},
{
url: 'release_tag=None',
- tokenKey: 'release',
+ tokenKey: TOKEN_TYPE_RELEASE,
value: __('None'),
},
{
url: 'release_tag=Any',
- tokenKey: 'release',
+ tokenKey: TOKEN_TYPE_RELEASE,
value: __('Any'),
},
{
url: 'label_name[]=None',
- tokenKey: 'label',
+ tokenKey: TOKEN_TYPE_LABEL,
value: __('None'),
},
{
url: 'label_name[]=Any',
- tokenKey: 'label',
+ tokenKey: TOKEN_TYPE_LABEL,
value: __('Any'),
},
{
url: 'my_reaction_emoji=None',
- tokenKey: 'my-reaction',
+ tokenKey: TOKEN_TYPE_MY_REACTION,
value: __('None'),
},
{
url: 'my_reaction_emoji=Any',
- tokenKey: 'my-reaction',
+ tokenKey: TOKEN_TYPE_MY_REACTION,
value: __('Any'),
},
].map((condition) => {
diff --git a/app/assets/javascripts/filtered_search/visual_token_value.js b/app/assets/javascripts/filtered_search/visual_token_value.js
index 1ad2006d689..33fda7533e4 100644
--- a/app/assets/javascripts/filtered_search/visual_token_value.js
+++ b/app/assets/javascripts/filtered_search/visual_token_value.js
@@ -8,6 +8,7 @@ import { createAlert } from '~/flash';
import AjaxCache from '~/lib/utils/ajax_cache';
import UsersCache from '~/lib/utils/users_cache';
import { __ } from '~/locale';
+import { TOKEN_TYPE_LABEL } from '~/vue_shared/components/filtered_search_bar/constants';
export default class VisualTokenValue {
constructor(tokenValue, tokenType, tokenOperator) {
@@ -23,7 +24,7 @@ export default class VisualTokenValue {
return;
}
- if (tokenType === 'label') {
+ if (tokenType === TOKEN_TYPE_LABEL) {
this.updateLabelTokenColor(tokenValueContainer);
} else if (USER_TOKEN_TYPES.includes(tokenType)) {
this.updateUserTokenAppearance(tokenValueContainer, tokenValueElement);
diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js
index dc6c4642e94..9e804b60d59 100644
--- a/app/assets/javascripts/flash.js
+++ b/app/assets/javascripts/flash.js
@@ -114,6 +114,7 @@ const addDismissFlashClickListener = (flashEl, fadeTransition) => {
* @param {object} [options.parent] - Reference to parent element under which alert needs to appear. Defaults to `document`.
* @param {Function} [options.onDismiss] - Handler to call when this alert is dismissed.
* @param {string} [options.containerSelector] - Selector for the container of the alert
+ * @param {boolean} [options.preservePrevious] - Set to `true` to preserve previous alerts. Defaults to `false`.
* @param {object} [options.primaryButton] - Object describing primary button of alert
* @param {string} [options.primaryButton.link] - Href of primary button
* @param {string} [options.primaryButton.text] - Text of primary button
@@ -131,6 +132,7 @@ const createAlert = function createAlert({
variant = VARIANT_DANGER,
parent = document,
containerSelector = '.flash-container',
+ preservePrevious = false,
primaryButton = null,
secondaryButton = null,
onDismiss = null,
@@ -143,7 +145,11 @@ const createAlert = function createAlert({
if (!alertContainer) return null;
const el = document.createElement('div');
- alertContainer.appendChild(el);
+ if (preservePrevious) {
+ alertContainer.appendChild(el);
+ } else {
+ alertContainer.replaceChildren(el);
+ }
return new Vue({
el,
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
index 33ab1d5cd7f..89b6885091c 100644
--- a/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
@@ -1,6 +1,7 @@
<script>
-import { GlButton, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlButton } from '@gitlab/ui';
import { snakeCase } from 'lodash';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import highlight from '~/lib/utils/highlight';
import { truncateNamespace } from '~/lib/utils/text_utility';
import { mapVuexModuleState } from '~/lib/utils/vuex_module_mappers';
@@ -15,7 +16,7 @@ export default {
ProjectAvatar,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [trackingMixin],
inject: ['vuexModule'],
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 49c47e9d778..293cd2df16f 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -538,7 +538,12 @@ class GfmAutoComplete {
setupLabels($input) {
const instance = this;
const fetchData = this.fetchData.bind(this);
- const LABEL_COMMAND = { LABEL: '/label', UNLABEL: '/unlabel', RELABEL: '/relabel' };
+ const LABEL_COMMAND = {
+ LABEL: '/label',
+ LABELS: '/labels',
+ UNLABEL: '/unlabel',
+ RELABEL: '/relabel',
+ };
let command = '';
$input.atwho({
@@ -570,13 +575,9 @@ class GfmAutoComplete {
matcher(flag, subtext) {
const subtextNodes = subtext.split(/\n+/g).pop().split(GfmAutoComplete.regexSubtext);
- // Check if ~ is followed by '/label', '/relabel' or '/unlabel' commands.
+ // Check if ~ is followed by '/label', '/labels', '/relabel' or '/unlabel' commands.
command = subtextNodes.find((node) => {
- if (
- node === LABEL_COMMAND.LABEL ||
- node === LABEL_COMMAND.RELABEL ||
- node === LABEL_COMMAND.UNLABEL
- ) {
+ if (Object.values(LABEL_COMMAND).includes(node)) {
return node;
}
return null;
@@ -621,7 +622,7 @@ class GfmAutoComplete {
// The `LABEL_COMMAND.RELABEL` is intentionally skipped
// because we want to return all the labels (unfiltered) for that command.
- if (command === LABEL_COMMAND.LABEL) {
+ if (command === LABEL_COMMAND.LABEL || command === LABEL_COMMAND.LABELS) {
// Return labels with set: undefined.
return data.filter((label) => !label.set);
} else if (command === LABEL_COMMAND.UNLABEL) {
@@ -996,7 +997,7 @@ GfmAutoComplete.Issues = {
return value.reference || '${atwho-at}${id}';
},
templateFunction({ id, title, reference }) {
- return `<li><small>${reference || id}</small> ${escape(title)}</li>`;
+ return `<li><small>${escape(reference || id)}</small> ${escape(title)}</li>`;
},
};
// Milestones
diff --git a/app/assets/javascripts/gitlab_pages/components/pages_pipeline_wizard.vue b/app/assets/javascripts/gitlab_pages/components/pages_pipeline_wizard.vue
index f17a05999b0..bf71f682048 100644
--- a/app/assets/javascripts/gitlab_pages/components/pages_pipeline_wizard.vue
+++ b/app/assets/javascripts/gitlab_pages/components/pages_pipeline_wizard.vue
@@ -2,7 +2,7 @@
import { GlLoadingIcon } from '@gitlab/ui';
import { captureException } from '@sentry/browser';
import PipelineWizard from '~/pipeline_wizard/pipeline_wizard.vue';
-import PagesWizardTemplate from '~/pipeline_wizard/templates/pages.yml';
+import PagesWizardTemplate from '~/pipeline_wizard/templates/pages.yml?raw';
import { logError } from '~/lib/logger';
import { s__ } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility';
diff --git a/app/assets/javascripts/gitlab_version_check/components/security_patch_upgrade_alert.vue b/app/assets/javascripts/gitlab_version_check/components/security_patch_upgrade_alert.vue
new file mode 100644
index 00000000000..89dc68ec73e
--- /dev/null
+++ b/app/assets/javascripts/gitlab_version_check/components/security_patch_upgrade_alert.vue
@@ -0,0 +1,76 @@
+<script>
+import { GlAlert, GlSprintf, GlLink, GlButton } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import Tracking from '~/tracking';
+import { UPGRADE_DOCS_URL, ABOUT_RELEASES_PAGE } from '../constants';
+
+export default {
+ name: 'SecurityPatchUpgradeAlert',
+ i18n: {
+ alertTitle: s__('VersionCheck|Critical security upgrade available'),
+ alertBody: s__(
+ 'VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}',
+ ),
+ learnMore: s__('VersionCheck|Learn more about this critical security release.'),
+ primaryButtonText: s__('VersionCheck|Upgrade now'),
+ },
+ components: {
+ GlAlert,
+ GlSprintf,
+ GlLink,
+ GlButton,
+ },
+ mixins: [Tracking.mixin()],
+ props: {
+ currentVersion: {
+ type: String,
+ required: true,
+ },
+ },
+ mounted() {
+ this.track('render', {
+ label: 'security_patch_upgrade_alert',
+ property: this.currentVersion,
+ });
+ },
+ methods: {
+ trackLearnMoreClick() {
+ this.track('click_link', {
+ label: 'security_patch_upgrade_alert_learn_more',
+ property: this.currentVersion,
+ });
+ },
+ trackUpgradeNowClick() {
+ this.track('click_link', {
+ label: 'security_patch_upgrade_alert_upgrade_now',
+ property: this.currentVersion,
+ });
+ },
+ },
+ UPGRADE_DOCS_URL,
+ ABOUT_RELEASES_PAGE,
+};
+</script>
+
+<template>
+ <gl-alert :title="$options.i18n.alertTitle" variant="danger" :dismissible="false">
+ <gl-sprintf :message="$options.i18n.alertBody">
+ <template #currentVersion>
+ <span class="gl-font-weight-bold">{{ currentVersion }}</span>
+ </template>
+ <template #link>
+ <gl-link :href="$options.ABOUT_RELEASES_PAGE" @click="trackLearnMoreClick">{{
+ $options.i18n.learnMore
+ }}</gl-link>
+ </template>
+ </gl-sprintf>
+ <template #actions>
+ <gl-button
+ :href="$options.UPGRADE_DOCS_URL"
+ variant="confirm"
+ @click="trackUpgradeNowClick"
+ >{{ $options.i18n.primaryButtonText }}</gl-button
+ >
+ </template>
+ </gl-alert>
+</template>
diff --git a/app/assets/javascripts/gitlab_version_check/components/security_patch_upgrade_alert_modal.vue b/app/assets/javascripts/gitlab_version_check/components/security_patch_upgrade_alert_modal.vue
new file mode 100644
index 00000000000..4638ba8a268
--- /dev/null
+++ b/app/assets/javascripts/gitlab_version_check/components/security_patch_upgrade_alert_modal.vue
@@ -0,0 +1,160 @@
+<script>
+import { GlModal, GlSprintf, GlLink, GlButton } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
+import { glEmojiTag } from '~/emoji';
+import { s__, sprintf } from '~/locale';
+import Tracking from '~/tracking';
+import { getHideAlertModalCookie, setHideAlertModalCookie } from '../utils';
+import {
+ UPGRADE_DOCS_URL,
+ ABOUT_RELEASES_PAGE,
+ ALERT_MODAL_ID,
+ TRACKING_ACTIONS,
+ TRACKING_LABELS,
+} from '../constants';
+
+export default {
+ name: 'SecurityPatchUpgradeAlertModal',
+ i18n: {
+ modalTitle: s__('VersionCheck|Important notice - Critical security release'),
+ modalBodyNoStableVersions: s__(
+ 'VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately.',
+ ),
+ modalBodyStableVersions: s__(
+ 'VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}.',
+ ),
+ modalDetails: s__('VersionCheck|%{details}'),
+ learnMore: s__('VersionCheck|Learn more about this critical security release.'),
+ primaryButtonText: s__('VersionCheck|Upgrade now'),
+ secondaryButtonText: s__('VersionCheck|Remind me again in 3 days'),
+ },
+ components: {
+ GlModal,
+ GlSprintf,
+ GlLink,
+ GlButton,
+ },
+ directives: {
+ SafeHtml,
+ },
+ mixins: [Tracking.mixin()],
+ props: {
+ currentVersion: {
+ type: String,
+ required: true,
+ },
+ details: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ latestStableVersions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ },
+ data() {
+ return {
+ visible: true,
+ };
+ },
+ computed: {
+ alertEmoji() {
+ return glEmojiTag('rotating_light');
+ },
+ modalBody() {
+ if (this.latestStableVersions?.length > 0) {
+ return this.$options.i18n.modalBodyStableVersions;
+ }
+
+ return this.$options.i18n.modalBodyNoStableVersions;
+ },
+ modalDetails() {
+ return sprintf(this.$options.i18n.modalDetails, { details: this.details });
+ },
+ latestStableVersionsStrings() {
+ return this.latestStableVersions?.length > 0 ? this.latestStableVersions.join(', ') : '';
+ },
+ },
+ created() {
+ if (getHideAlertModalCookie(this.currentVersion)) {
+ this.visible = false;
+ return;
+ }
+
+ this.dispatchTrackingEvent(TRACKING_ACTIONS.RENDER, TRACKING_LABELS.MODAL);
+ },
+ methods: {
+ dispatchTrackingEvent(action, label) {
+ this.track(action, {
+ label,
+ property: this.currentVersion,
+ });
+ },
+ trackLearnMoreClick() {
+ this.dispatchTrackingEvent(TRACKING_ACTIONS.CLICK_LINK, TRACKING_LABELS.LEARN_MORE_LINK);
+ },
+ trackRemindMeLaterClick() {
+ this.dispatchTrackingEvent(TRACKING_ACTIONS.CLICK_BUTTON, TRACKING_LABELS.REMIND_ME_BTN);
+ setHideAlertModalCookie(this.currentVersion);
+ this.$refs.alertModal.hide();
+ },
+ trackUpgradeNowClick() {
+ this.dispatchTrackingEvent(TRACKING_ACTIONS.CLICK_LINK, TRACKING_LABELS.UPGRADE_BTN_LINK);
+ setHideAlertModalCookie(this.currentVersion);
+ },
+ trackModalDismissed() {
+ this.dispatchTrackingEvent(TRACKING_ACTIONS.CLICK_BUTTON, TRACKING_LABELS.DISMISS);
+ },
+ },
+ safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
+ UPGRADE_DOCS_URL,
+ ABOUT_RELEASES_PAGE,
+ ALERT_MODAL_ID,
+};
+</script>
+
+<template>
+ <gl-modal
+ ref="alertModal"
+ :modal-id="$options.ALERT_MODAL_ID"
+ :visible="visible"
+ @close="trackModalDismissed"
+ >
+ <template #modal-title>
+ <span v-safe-html:[$options.safeHtmlConfig]="alertEmoji"></span>
+ <span data-testid="alert-modal-title">{{ $options.i18n.modalTitle }}</span>
+ </template>
+ <template #default>
+ <div data-testid="alert-modal-body" class="gl-mb-6">
+ <gl-sprintf :message="modalBody">
+ <template #currentVersion>
+ <span class="gl-font-weight-bold">{{ currentVersion }}</span>
+ </template>
+ <template #latestStableVersions>
+ <span class="gl-font-weight-bold">{{ latestStableVersionsStrings }}</span>
+ </template>
+ </gl-sprintf>
+ </div>
+ <div v-if="details" data-testid="alert-modal-details" class="gl-mb-6">
+ {{ modalDetails }}
+ </div>
+ <gl-link :href="$options.ABOUT_RELEASES_PAGE" @click="trackLearnMoreClick">{{
+ $options.i18n.learnMore
+ }}</gl-link>
+ </template>
+ <template #modal-footer>
+ <gl-button data-testid="alert-modal-remind-button" @click="trackRemindMeLaterClick">{{
+ $options.i18n.secondaryButtonText
+ }}</gl-button>
+ <gl-button
+ data-testid="alert-modal-upgrade-button"
+ :href="$options.UPGRADE_DOCS_URL"
+ variant="confirm"
+ @click="trackUpgradeNowClick"
+ >{{ $options.i18n.primaryButtonText }}</gl-button
+ >
+ </template>
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/gitlab_version_check/constants.js b/app/assets/javascripts/gitlab_version_check/constants.js
index 259723a4e22..049397148ab 100644
--- a/app/assets/javascripts/gitlab_version_check/constants.js
+++ b/app/assets/javascripts/gitlab_version_check/constants.js
@@ -7,3 +7,25 @@ export const STATUS_TYPES = {
};
export const UPGRADE_DOCS_URL = helpPagePath('update/index');
+
+export const ABOUT_RELEASES_PAGE = 'https://about.gitlab.com/releases/categories/releases/';
+
+export const ALERT_MODAL_ID = 'security-patch-upgrade-alert-modal';
+
+export const COOKIE_EXPIRATION = 3;
+
+export const COOKIE_SUFFIX = '-hide-alert-modal';
+
+export const TRACKING_ACTIONS = {
+ RENDER: 'render',
+ CLICK_LINK: 'click_link',
+ CLICK_BUTTON: 'click_button',
+};
+
+export const TRACKING_LABELS = {
+ MODAL: 'security_patch_upgrade_alert_modal',
+ LEARN_MORE_LINK: 'security_patch_upgrade_alert_modal_learn_more',
+ REMIND_ME_BTN: 'security_patch_upgrade_alert_modal_remind_3_days',
+ UPGRADE_BTN_LINK: 'security_patch_upgrade_alert_modal_upgrade_now',
+ DISMISS: 'security_patch_upgrade_alert_modal_close',
+};
diff --git a/app/assets/javascripts/gitlab_version_check/index.js b/app/assets/javascripts/gitlab_version_check/index.js
index 203ce10ef57..edb7e9abe49 100644
--- a/app/assets/javascripts/gitlab_version_check/index.js
+++ b/app/assets/javascripts/gitlab_version_check/index.js
@@ -1,50 +1,98 @@
import Vue from 'vue';
-import * as Sentry from '@sentry/browser';
-import { parseBoolean } from '~/lib/utils/common_utils';
-import axios from '~/lib/utils/axios_utils';
-import { joinPaths } from '~/lib/utils/url_utility';
+import { parseBoolean, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import GitlabVersionCheckBadge from './components/gitlab_version_check_badge.vue';
+import SecurityPatchUpgradeAlert from './components/security_patch_upgrade_alert.vue';
+import SecurityPatchUpgradeAlertModal from './components/security_patch_upgrade_alert_modal.vue';
-const mountGitlabVersionCheckBadge = ({ el, status }) => {
- const { size } = el.dataset;
+const mountGitlabVersionCheckBadge = (el) => {
+ const { size, version } = el.dataset;
const actionable = parseBoolean(el.dataset.actionable);
- return new Vue({
- el,
- render(createElement) {
- return createElement(GitlabVersionCheckBadge, {
- props: {
- size,
- actionable,
- status,
- },
- });
- },
- });
+ try {
+ const { severity } = JSON.parse(version);
+
+ // If no severity (status) data don't worry about rendering
+ if (!severity) {
+ return null;
+ }
+
+ return new Vue({
+ el,
+ render(createElement) {
+ return createElement(GitlabVersionCheckBadge, {
+ props: {
+ size,
+ actionable,
+ status: severity,
+ },
+ });
+ },
+ });
+ } catch {
+ return null;
+ }
};
-export default async () => {
- const versionCheckBadges = [...document.querySelectorAll('.js-gitlab-version-check-badge')];
+const mountSecurityPatchUpgradeAlert = (el) => {
+ const { currentVersion } = el.dataset;
- // If there are no version check elements, exit out
- if (versionCheckBadges?.length <= 0) {
+ try {
+ return new Vue({
+ el,
+ render(createElement) {
+ return createElement(SecurityPatchUpgradeAlert, {
+ props: {
+ currentVersion,
+ },
+ });
+ },
+ });
+ } catch {
return null;
}
+};
- const status = await axios
- .get(joinPaths('/', gon.relative_url_root, '/admin/version_check.json'))
- .then((res) => {
- return res.data?.severity;
- })
- .catch((e) => {
- Sentry.captureException(e);
- return null;
+const mountSecurityPatchUpgradeAlertModal = (el) => {
+ const { currentVersion, version } = el.dataset;
+
+ try {
+ const { details, latestStableVersions } = convertObjectPropsToCamelCase(JSON.parse(version));
+
+ return new Vue({
+ el,
+ render(createElement) {
+ return createElement(SecurityPatchUpgradeAlertModal, {
+ props: {
+ currentVersion,
+ details,
+ latestStableVersions,
+ },
+ });
+ },
});
+ } catch {
+ return null;
+ }
+};
+
+export default () => {
+ const renderedApps = [];
- // If we don't have a status there is nothing to render
- if (status) {
- return versionCheckBadges.map((el) => mountGitlabVersionCheckBadge({ el, status }));
+ const securityPatchUpgradeAlert = document.getElementById('js-security-patch-upgrade-alert');
+ const securityPatchUpgradeAlertModal = document.getElementById(
+ 'js-security-patch-upgrade-alert-modal',
+ );
+ const versionCheckBadges = [...document.querySelectorAll('.js-gitlab-version-check-badge')];
+
+ if (securityPatchUpgradeAlert) {
+ renderedApps.push(mountSecurityPatchUpgradeAlert(securityPatchUpgradeAlert));
}
- return null;
+ if (securityPatchUpgradeAlertModal) {
+ renderedApps.push(mountSecurityPatchUpgradeAlertModal(securityPatchUpgradeAlertModal));
+ }
+
+ renderedApps.push(...versionCheckBadges.map((el) => mountGitlabVersionCheckBadge(el)));
+
+ return renderedApps;
};
diff --git a/app/assets/javascripts/gitlab_version_check/utils.js b/app/assets/javascripts/gitlab_version_check/utils.js
new file mode 100644
index 00000000000..d2f4349483c
--- /dev/null
+++ b/app/assets/javascripts/gitlab_version_check/utils.js
@@ -0,0 +1,18 @@
+import { setCookie, getCookie, parseBoolean } from '~/lib/utils/common_utils';
+import { COOKIE_EXPIRATION, COOKIE_SUFFIX } from './constants';
+
+const buildKey = (currentVersion) => {
+ return `${currentVersion}${COOKIE_SUFFIX}`;
+};
+
+export const setHideAlertModalCookie = (currentVersion) => {
+ const key = buildKey(currentVersion);
+
+ setCookie(key, true, { expires: COOKIE_EXPIRATION });
+};
+
+export const getHideAlertModalCookie = (currentVersion) => {
+ const key = buildKey(currentVersion);
+
+ return parseBoolean(getCookie(key));
+};
diff --git a/app/assets/javascripts/graphql_shared/fragments/alert.fragment.graphql b/app/assets/javascripts/graphql_shared/fragments/alert.fragment.graphql
index 64f547f933a..3ecaee435e2 100644
--- a/app/assets/javascripts/graphql_shared/fragments/alert.fragment.graphql
+++ b/app/assets/javascripts/graphql_shared/fragments/alert.fragment.graphql
@@ -1,4 +1,5 @@
fragment AlertListItem on AlertManagementAlert {
+ id
iid
title
severity
diff --git a/app/assets/javascripts/graphql_shared/mutations/alert_status_update.mutation.graphql b/app/assets/javascripts/graphql_shared/mutations/alert_status_update.mutation.graphql
index ba1e607bc10..9ec87ba291d 100644
--- a/app/assets/javascripts/graphql_shared/mutations/alert_status_update.mutation.graphql
+++ b/app/assets/javascripts/graphql_shared/mutations/alert_status_update.mutation.graphql
@@ -4,6 +4,7 @@ mutation updateAlertStatus($projectPath: ID!, $status: AlertManagementStatus!, $
updateAlertStatus(input: { iid: $iid, status: $status, projectPath: $projectPath }) {
errors
alert {
+ id
iid
status
endedAt
diff --git a/app/assets/javascripts/graphql_shared/possible_types.json b/app/assets/javascripts/graphql_shared/possible_types.json
index e8b0174b8f6..5467105ac3c 100644
--- a/app/assets/javascripts/graphql_shared/possible_types.json
+++ b/app/assets/javascripts/graphql_shared/possible_types.json
@@ -7,10 +7,12 @@
"CiGroupVariable",
"CiInstanceVariable",
"CiManualVariable",
- "CiProjectVariable"
+ "CiProjectVariable",
+ "PipelineScheduleVariable"
],
"CommitSignature": [
"GpgSignature",
+ "SshSignature",
"X509Signature"
],
"CurrentUserTodos": [
@@ -144,10 +146,13 @@
"WorkItemWidget": [
"WorkItemWidgetAssignees",
"WorkItemWidgetDescription",
+ "WorkItemWidgetHealthStatus",
"WorkItemWidgetHierarchy",
"WorkItemWidgetIteration",
"WorkItemWidgetLabels",
"WorkItemWidgetMilestone",
+ "WorkItemWidgetNotes",
+ "WorkItemWidgetProgress",
"WorkItemWidgetStartAndDueDate",
"WorkItemWidgetStatus",
"WorkItemWidgetWeight"
diff --git a/app/assets/javascripts/graphql_shared/queries/alert_details.query.graphql b/app/assets/javascripts/graphql_shared/queries/alert_details.query.graphql
index 8debc6113d1..77b95bb8910 100644
--- a/app/assets/javascripts/graphql_shared/queries/alert_details.query.graphql
+++ b/app/assets/javascripts/graphql_shared/queries/alert_details.query.graphql
@@ -5,6 +5,7 @@ query alertDetails($fullPath: ID!, $alertId: String) {
id
alertManagementAlerts(iid: $alertId) {
nodes {
+ id
...AlertDetailItem
}
}
diff --git a/app/assets/javascripts/groups/components/app.vue b/app/assets/javascripts/groups/components/app.vue
index 15f5a3518a5..46d5341ea97 100644
--- a/app/assets/javascripts/groups/components/app.vue
+++ b/app/assets/javascripts/groups/components/app.vue
@@ -1,21 +1,25 @@
<script>
-import { GlLoadingIcon, GlModal } from '@gitlab/ui';
+import { GlLoadingIcon, GlModal, GlEmptyState } from '@gitlab/ui';
import { createAlert } from '~/flash';
import { mergeUrlParams, getParameterByName } from '~/lib/utils/url_utility';
-import { HIDDEN_CLASS } from '~/lib/utils/constants';
import { __, s__, sprintf } from '~/locale';
-import { COMMON_STR, CONTENT_LIST_CLASS } from '../constants';
+import { COMMON_STR } from '../constants';
import eventHub from '../event_hub';
import GroupsComponent from './groups.vue';
-import EmptyState from './empty_state.vue';
export default {
+ i18n: {
+ searchEmptyState: {
+ title: __('No results found'),
+ description: __('Edit your search and try again'),
+ },
+ },
components: {
GroupsComponent,
GlModal,
GlLoadingIcon,
- EmptyState,
+ GlEmptyState,
},
props: {
action: {
@@ -40,20 +44,14 @@ export default {
type: Boolean,
required: true,
},
- renderEmptyState: {
- type: Boolean,
- required: false,
- default: false,
- },
},
data() {
return {
isModalVisible: false,
isLoading: true,
- isSearchEmpty: false,
+ fromSearch: false,
targetGroup: null,
targetParentGroup: null,
- showEmptyState: false,
};
},
computed: {
@@ -79,6 +77,9 @@ export default {
groups() {
return this.store.getGroups();
},
+ hasGroups() {
+ return this.groups && this.groups.length > 0;
+ },
pageInfo() {
return this.store.getPaginationInfo();
},
@@ -231,47 +232,17 @@ export default {
this.targetGroup.isBeingRemoved = false;
});
},
- showLegacyEmptyState() {
- const { containerEl } = this;
-
- if (!containerEl) return;
-
- const contentListEl = containerEl.querySelector(CONTENT_LIST_CLASS);
- const emptyStateEl = containerEl.querySelector('.empty-state');
-
- if (contentListEl) {
- contentListEl.remove();
- }
-
- if (emptyStateEl) {
- emptyStateEl.classList.remove(HIDDEN_CLASS);
- }
- },
updatePagination(headers) {
this.store.setPaginationInfo(headers);
},
updateGroups(groups, fromSearch) {
- const hasGroups = groups && groups.length > 0;
-
- if (this.renderEmptyState) {
- this.isSearchEmpty = fromSearch && !hasGroups;
- } else {
- this.isSearchEmpty = !hasGroups;
- }
+ this.fromSearch = fromSearch;
if (fromSearch) {
this.store.setSearchedGroups(groups);
} else {
this.store.setGroups(groups);
}
-
- if (this.action && !hasGroups && !fromSearch) {
- if (this.renderEmptyState) {
- this.showEmptyState = true;
- } else {
- this.showLegacyEmptyState();
- }
- }
},
},
};
@@ -285,14 +256,16 @@ export default {
size="lg"
class="loading-animation prepend-top-20"
/>
- <groups-component
- v-else
- :groups="groups"
- :search-empty="isSearchEmpty"
- :page-info="pageInfo"
- :action="action"
- />
- <empty-state v-if="showEmptyState" />
+ <template v-else>
+ <groups-component v-if="hasGroups" :groups="groups" :page-info="pageInfo" :action="action" />
+ <gl-empty-state
+ v-else-if="fromSearch"
+ :title="$options.i18n.searchEmptyState.title"
+ :description="$options.i18n.searchEmptyState.description"
+ data-testid="search-empty-state"
+ />
+ <slot v-else name="empty-state"></slot>
+ </template>
<gl-modal
modal-id="leave-group-modal"
:visible="isModalVisible"
diff --git a/app/assets/javascripts/groups/components/empty_state.vue b/app/assets/javascripts/groups/components/empty_state.vue
deleted file mode 100644
index 4219b52737d..00000000000
--- a/app/assets/javascripts/groups/components/empty_state.vue
+++ /dev/null
@@ -1,91 +0,0 @@
-<script>
-import { GlLink, GlEmptyState } from '@gitlab/ui';
-
-import { s__ } from '~/locale';
-
-export default {
- components: { GlLink, GlEmptyState },
- i18n: {
- withLinks: {
- subgroup: {
- title: s__('GroupsEmptyState|Create new subgroup'),
- description: s__(
- 'GroupsEmptyState|Groups are the best way to manage multiple projects and members.',
- ),
- },
- project: {
- title: s__('GroupsEmptyState|Create new project'),
- description: s__(
- 'GroupsEmptyState|Projects are where you can store your code, access issues, wiki, and other features of Gitlab.',
- ),
- },
- },
- withoutLinks: {
- title: s__('GroupsEmptyState|No subgroups or projects.'),
- description: s__(
- 'GroupsEmptyState|You do not have necessary permissions to create a subgroup or project in this group. Please contact an owner of this group to create a new subgroup or project.',
- ),
- },
- },
- linkClasses: [
- 'gl-border',
- 'gl-text-decoration-none!',
- 'gl-rounded-base',
- 'gl-p-7',
- 'gl-display-flex',
- 'gl-h-full',
- 'gl-align-items-center',
- 'gl-text-purple-600',
- 'gl-hover-bg-gray-50',
- ],
- inject: [
- 'newSubgroupPath',
- 'newProjectPath',
- 'newSubgroupIllustration',
- 'newProjectIllustration',
- 'emptySubgroupIllustration',
- 'canCreateSubgroups',
- 'canCreateProjects',
- ],
-};
-</script>
-
-<template>
- <div v-if="canCreateSubgroups || canCreateProjects" class="gl-mt-5">
- <div class="gl-display-flex gl-mx-n3 gl-my-n3 gl-flex-wrap">
- <div v-if="canCreateSubgroups" class="gl-p-3 gl-w-full gl-sm-w-half">
- <gl-link :href="newSubgroupPath" :class="$options.linkClasses">
- <div class="svg-content gl-w-15 gl-flex-shrink-0 gl-mr-5">
- <img :src="newSubgroupIllustration" :alt="$options.i18n.withLinks.subgroup.title" />
- </div>
- <div>
- <h4 class="gl-reset-color">{{ $options.i18n.withLinks.subgroup.title }}</h4>
- <p class="gl-text-body">
- {{ $options.i18n.withLinks.subgroup.description }}
- </p>
- </div>
- </gl-link>
- </div>
- <div v-if="canCreateProjects" class="gl-p-3 gl-w-full gl-sm-w-half">
- <gl-link :href="newProjectPath" :class="$options.linkClasses">
- <div class="svg-content gl-w-13 gl-flex-shrink-0 gl-mr-5">
- <img :src="newProjectIllustration" :alt="$options.i18n.withLinks.project.title" />
- </div>
- <div>
- <h4 class="gl-reset-color">{{ $options.i18n.withLinks.project.title }}</h4>
- <p class="gl-text-body">
- {{ $options.i18n.withLinks.project.description }}
- </p>
- </div>
- </gl-link>
- </div>
- </div>
- </div>
- <gl-empty-state
- v-else
- class="gl-mt-5"
- :title="$options.i18n.withoutLinks.title"
- :svg-path="emptySubgroupIllustration"
- :description="$options.i18n.withoutLinks.description"
- />
-</template>
diff --git a/app/assets/javascripts/groups/components/empty_states/archived_projects_empty_state.vue b/app/assets/javascripts/groups/components/empty_states/archived_projects_empty_state.vue
new file mode 100644
index 00000000000..535758750f9
--- /dev/null
+++ b/app/assets/javascripts/groups/components/empty_states/archived_projects_empty_state.vue
@@ -0,0 +1,21 @@
+<script>
+import { GlEmptyState } from '@gitlab/ui';
+
+import { s__ } from '~/locale';
+
+export default {
+ components: { GlEmptyState },
+ i18n: {
+ title: s__('GroupsEmptyState|No archived projects.'),
+ },
+ inject: ['newProjectIllustration'],
+};
+</script>
+
+<template>
+ <gl-empty-state
+ :title="$options.i18n.title"
+ :svg-path="newProjectIllustration"
+ :svg-height="100"
+ />
+</template>
diff --git a/app/assets/javascripts/groups/components/empty_states/shared_projects_empty_state.vue b/app/assets/javascripts/groups/components/empty_states/shared_projects_empty_state.vue
new file mode 100644
index 00000000000..7223321bf3e
--- /dev/null
+++ b/app/assets/javascripts/groups/components/empty_states/shared_projects_empty_state.vue
@@ -0,0 +1,21 @@
+<script>
+import { GlEmptyState } from '@gitlab/ui';
+
+import { s__ } from '~/locale';
+
+export default {
+ components: { GlEmptyState },
+ i18n: {
+ title: s__('GroupsEmptyState|No shared projects.'),
+ },
+ inject: ['newProjectIllustration'],
+};
+</script>
+
+<template>
+ <gl-empty-state
+ :title="$options.i18n.title"
+ :svg-path="newProjectIllustration"
+ :svg-height="100"
+ />
+</template>
diff --git a/app/assets/javascripts/groups/components/empty_states/subgroups_and_projects_empty_state.vue b/app/assets/javascripts/groups/components/empty_states/subgroups_and_projects_empty_state.vue
new file mode 100644
index 00000000000..955cb1ca63e
--- /dev/null
+++ b/app/assets/javascripts/groups/components/empty_states/subgroups_and_projects_empty_state.vue
@@ -0,0 +1,90 @@
+<script>
+import { GlLink, GlEmptyState } from '@gitlab/ui';
+
+import { s__ } from '~/locale';
+
+export default {
+ components: { GlLink, GlEmptyState },
+ i18n: {
+ withLinks: {
+ subgroup: {
+ title: s__('GroupsEmptyState|Create new subgroup'),
+ description: s__(
+ 'GroupsEmptyState|Groups are the best way to manage multiple projects and members.',
+ ),
+ },
+ project: {
+ title: s__('GroupsEmptyState|Create new project'),
+ description: s__(
+ 'GroupsEmptyState|Projects are where you can store your code, access issues, wiki, and other features of Gitlab.',
+ ),
+ },
+ },
+ withoutLinks: {
+ title: s__('GroupsEmptyState|No subgroups or projects.'),
+ description: s__(
+ 'GroupsEmptyState|You do not have necessary permissions to create a subgroup or project in this group. Please contact an owner of this group to create a new subgroup or project.',
+ ),
+ },
+ },
+ linkClasses: [
+ 'gl-border',
+ 'gl-text-decoration-none!',
+ 'gl-rounded-base',
+ 'gl-p-7',
+ 'gl-display-flex',
+ 'gl-h-full',
+ 'gl-align-items-center',
+ 'gl-text-purple-600',
+ 'gl-hover-bg-gray-50',
+ ],
+ inject: [
+ 'newSubgroupPath',
+ 'newProjectPath',
+ 'newSubgroupIllustration',
+ 'newProjectIllustration',
+ 'emptySubgroupIllustration',
+ 'canCreateSubgroups',
+ 'canCreateProjects',
+ ],
+};
+</script>
+
+<template>
+ <div v-if="canCreateSubgroups || canCreateProjects" class="gl-mt-5">
+ <div class="gl-display-flex gl-mx-n3 gl-my-n3 gl-flex-wrap">
+ <div v-if="canCreateSubgroups" class="gl-p-3 gl-w-full gl-sm-w-half">
+ <gl-link :href="newSubgroupPath" :class="$options.linkClasses">
+ <div class="svg-content gl-w-15 gl-flex-shrink-0 gl-mr-5">
+ <img :src="newSubgroupIllustration" :alt="$options.i18n.withLinks.subgroup.title" />
+ </div>
+ <div>
+ <h4 class="gl-reset-color">{{ $options.i18n.withLinks.subgroup.title }}</h4>
+ <p class="gl-text-body">
+ {{ $options.i18n.withLinks.subgroup.description }}
+ </p>
+ </div>
+ </gl-link>
+ </div>
+ <div v-if="canCreateProjects" class="gl-p-3 gl-w-full gl-sm-w-half">
+ <gl-link :href="newProjectPath" :class="$options.linkClasses">
+ <div class="svg-content gl-w-13 gl-flex-shrink-0 gl-mr-5">
+ <img :src="newProjectIllustration" :alt="$options.i18n.withLinks.project.title" />
+ </div>
+ <div>
+ <h4 class="gl-reset-color">{{ $options.i18n.withLinks.project.title }}</h4>
+ <p class="gl-text-body">
+ {{ $options.i18n.withLinks.project.description }}
+ </p>
+ </div>
+ </gl-link>
+ </div>
+ </div>
+ </div>
+ <gl-empty-state
+ v-else
+ :title="$options.i18n.withoutLinks.title"
+ :svg-path="emptySubgroupIllustration"
+ :description="$options.i18n.withoutLinks.description"
+ />
+</template>
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
index 961af800971..d9781ef9c84 100644
--- a/app/assets/javascripts/groups/components/group_item.vue
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -9,8 +9,8 @@ import {
GlPopover,
GlLink,
GlTooltipDirective,
- GlSafeHtmlDirective,
} from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { visitUrl } from '~/lib/utils/url_utility';
import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.vue';
import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
@@ -29,7 +29,7 @@ import ItemTypeIcon from './item_type_icon.vue';
export default {
directives: {
GlTooltip: GlTooltipDirective,
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
components: {
GlAvatar,
@@ -200,11 +200,9 @@ export default {
class="no-expand gl-mr-3 gl-text-gray-900!"
:itemprop="microdata.nameItemprop"
>
- {{
- // ending bracket must be by closing tag to prevent
- // link hover text-decoration from over-extending
- group.name
- }}
+ <!-- ending bracket must be by closing tag to prevent -->
+ <!-- link hover text-decoration from over-extending -->
+ {{ group.name }}
</a>
<gl-icon
v-gl-tooltip.hover.bottom
diff --git a/app/assets/javascripts/groups/components/group_name_and_path.vue b/app/assets/javascripts/groups/components/group_name_and_path.vue
index 9a1ea2f1812..5f997ecc7ba 100644
--- a/app/assets/javascripts/groups/components/group_name_and_path.vue
+++ b/app/assets/javascripts/groups/components/group_name_and_path.vue
@@ -59,7 +59,7 @@ export default {
learnMore: s__('Groups|Learn more'),
},
inputSize: { md: 'lg' },
- changingGroupPathHelpPagePath: helpPagePath('user/group/index', {
+ changingGroupPathHelpPagePath: helpPagePath('user/group/manage', {
anchor: 'change-a-groups-path',
}),
mattermostDataBindName: 'create_chat_team',
diff --git a/app/assets/javascripts/groups/components/groups.vue b/app/assets/javascripts/groups/components/groups.vue
index 43aa0753082..5075be62214 100644
--- a/app/assets/javascripts/groups/components/groups.vue
+++ b/app/assets/javascripts/groups/components/groups.vue
@@ -1,5 +1,4 @@
<script>
-import { GlEmptyState } from '@gitlab/ui';
import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
import { getParameterByName } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
@@ -12,7 +11,6 @@ export default {
},
components: {
PaginationLinks,
- GlEmptyState,
},
props: {
groups: {
@@ -23,10 +21,6 @@ export default {
type: Object,
required: true,
},
- searchEmpty: {
- type: Boolean,
- required: true,
- },
action: {
type: String,
required: false,
@@ -46,18 +40,11 @@ export default {
<template>
<div class="groups-list-tree-container" data-qa-selector="groups_list_tree_container">
- <gl-empty-state
- v-if="searchEmpty"
- :title="$options.i18n.emptyStateTitle"
- :description="$options.i18n.emptyStateDescription"
+ <group-folder :groups="groups" :action="action" />
+ <pagination-links
+ :change="change"
+ :page-info="pageInfo"
+ class="d-flex justify-content-center gl-mt-3"
/>
- <template v-else>
- <group-folder :groups="groups" :action="action" />
- <pagination-links
- :change="change"
- :page-info="pageInfo"
- class="d-flex justify-content-center gl-mt-3"
- />
- </template>
</div>
</template>
diff --git a/app/assets/javascripts/groups/components/overview_tabs.vue b/app/assets/javascripts/groups/components/overview_tabs.vue
index 46ab30367a0..79a2e11b0bb 100644
--- a/app/assets/javascripts/groups/components/overview_tabs.vue
+++ b/app/assets/javascripts/groups/components/overview_tabs.vue
@@ -13,19 +13,32 @@ import {
} from '../constants';
import eventHub from '../event_hub';
import GroupsApp from './app.vue';
+import SubgroupsAndProjectsEmptyState from './empty_states/subgroups_and_projects_empty_state.vue';
+import SharedProjectsEmptyState from './empty_states/shared_projects_empty_state.vue';
+import ArchivedProjectsEmptyState from './empty_states/archived_projects_empty_state.vue';
const [SORTING_ITEM_NAME] = OVERVIEW_TABS_SORTING_ITEMS;
const MIN_SEARCH_LENGTH = 3;
export default {
- components: { GlTabs, GlTab, GroupsApp, GlSearchBoxByType, GlSorting, GlSortingItem },
+ components: {
+ GlTabs,
+ GlTab,
+ GroupsApp,
+ GlSearchBoxByType,
+ GlSorting,
+ GlSortingItem,
+ SubgroupsAndProjectsEmptyState,
+ SharedProjectsEmptyState,
+ ArchivedProjectsEmptyState,
+ },
inject: ['endpoints', 'initialSort'],
data() {
const tabs = [
{
title: this.$options.i18n[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS],
key: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
- renderEmptyState: true,
+ emptyStateComponent: SubgroupsAndProjectsEmptyState,
lazy: this.$route.name !== ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
service: new GroupsService(this.endpoints[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS]),
store: new GroupsStore({ showSchemaMarkup: true }),
@@ -33,7 +46,7 @@ export default {
{
title: this.$options.i18n[ACTIVE_TAB_SHARED],
key: ACTIVE_TAB_SHARED,
- renderEmptyState: false,
+ emptyStateComponent: SharedProjectsEmptyState,
lazy: this.$route.name !== ACTIVE_TAB_SHARED,
service: new GroupsService(this.endpoints[ACTIVE_TAB_SHARED]),
store: new GroupsStore(),
@@ -41,7 +54,7 @@ export default {
{
title: this.$options.i18n[ACTIVE_TAB_ARCHIVED],
key: ACTIVE_TAB_ARCHIVED,
- renderEmptyState: false,
+ emptyStateComponent: ArchivedProjectsEmptyState,
lazy: this.$route.name !== ACTIVE_TAB_ARCHIVED,
service: new GroupsService(this.endpoints[ACTIVE_TAB_ARCHIVED]),
store: new GroupsStore(),
@@ -158,18 +171,16 @@ export default {
<template>
<gl-tabs content-class="gl-pt-0" :value="activeTabIndex" @input="handleTabInput">
<gl-tab
- v-for="{ key, title, renderEmptyState, lazy, service, store } in tabs"
+ v-for="{ key, title, emptyStateComponent, lazy, service, store } in tabs"
:key="key"
:title="title"
:lazy="lazy"
>
- <groups-app
- :action="key"
- :service="service"
- :store="store"
- :hide-projects="false"
- :render-empty-state="renderEmptyState"
- />
+ <groups-app :action="key" :service="service" :store="store" :hide-projects="false">
+ <template v-if="emptyStateComponent" #empty-state>
+ <component :is="emptyStateComponent" />
+ </template>
+ </groups-app>
</gl-tab>
<template #tabs-end>
<li class="gl-flex-grow-1 gl-align-self-center gl-w-full gl-lg-w-auto gl-py-2">
diff --git a/app/assets/javascripts/groups/components/transfer_group_form.vue b/app/assets/javascripts/groups/components/transfer_group_form.vue
index 15a193f7cb8..3da417ebf0a 100644
--- a/app/assets/javascripts/groups/components/transfer_group_form.vue
+++ b/app/assets/javascripts/groups/components/transfer_group_form.vue
@@ -73,6 +73,7 @@ export default {
:disabled="disableSubmitButton"
:phrase="confirmationPhrase"
:button-text="confirmButtonText"
+ button-qa-selector="transfer_group_button"
@confirm="$emit('confirm')"
/>
</div>
diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js
index 4d03a523486..f58781fa9ec 100644
--- a/app/assets/javascripts/header.js
+++ b/app/assets/javascripts/header.js
@@ -1,7 +1,9 @@
import Vue from 'vue';
+import NewNavToggle from '~/nav/components/new_nav_toggle.vue';
import { highCountTrim } from '~/lib/utils/text_utility';
import Tracking from '~/tracking';
import Translate from '~/vue_shared/translate';
+import { parseBoolean } from '~/lib/utils/common_utils';
/**
* Updates todo counter when todos are toggled.
@@ -99,6 +101,7 @@ function trackShowUserDropdownLink(trackEvent, elToTrack, el) {
});
});
}
+
export function initNavUserDropdownTracking() {
const el = document.querySelector('.js-nav-user-dropdown');
const buyEl = document.querySelector('.js-buy-pipeline-minutes-link');
@@ -108,5 +111,23 @@ export function initNavUserDropdownTracking() {
}
}
+function initNewNavToggle() {
+ const el = document.querySelector('.js-new-nav-toggle');
+ if (!el) return false;
+
+ return new Vue({
+ el,
+ render(h) {
+ return h(NewNavToggle, {
+ props: {
+ enabled: parseBoolean(el.dataset.enabled),
+ endpoint: el.dataset.endpoint,
+ },
+ });
+ },
+ });
+}
+
requestIdleCallback(initStatusTriggers);
requestIdleCallback(initNavUserDropdownTracking);
+requestIdleCallback(initNewNavToggle);
diff --git a/app/assets/javascripts/header_search/components/app.vue b/app/assets/javascripts/header_search/components/app.vue
index 8fc0ce48e61..bf5daf29b21 100644
--- a/app/assets/javascripts/header_search/components/app.vue
+++ b/app/assets/javascripts/header_search/components/app.vue
@@ -4,7 +4,6 @@ import {
GlOutsideDirective as Outside,
GlIcon,
GlToken,
- GlSafeHtmlDirective as SafeHtml,
GlTooltipDirective,
GlResizeObserverDirective,
} from '@gitlab/ui';
@@ -56,7 +55,7 @@ export default {
false,
),
},
- directives: { SafeHtml, Outside, GlTooltip: GlTooltipDirective, GlResizeObserverDirective },
+ directives: { Outside, GlTooltip: GlTooltipDirective, GlResizeObserverDirective },
components: {
GlSearchBoxByType,
HeaderSearchDefaultItems,
diff --git a/app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue b/app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue
index 025c48f355d..c85fb4f4158 100644
--- a/app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue
+++ b/app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue
@@ -6,9 +6,9 @@ import {
GlAvatar,
GlAlert,
GlLoadingIcon,
- GlSafeHtmlDirective as SafeHtml,
} from '@gitlab/ui';
import { mapState, mapGetters } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { s__ } from '~/locale';
import highlight from '~/lib/utils/highlight';
import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
diff --git a/app/assets/javascripts/header_search/constants.js b/app/assets/javascripts/header_search/constants.js
index 332ccee510f..cda3379309c 100644
--- a/app/assets/javascripts/header_search/constants.js
+++ b/app/assets/javascripts/header_search/constants.js
@@ -26,6 +26,8 @@ export const GROUPS_CATEGORY = s__('GlobalSearch|Groups');
export const PROJECTS_CATEGORY = s__('GlobalSearch|Projects');
+export const USERS_CATEGORY = s__('GlobalSearch|Users');
+
export const ISSUES_CATEGORY = s__('GlobalSearch|Recent issues');
export const MERGE_REQUEST_CATEGORY = s__('GlobalSearch|Recent merge requests');
@@ -68,6 +70,7 @@ export const DROPDOWN_ORDER = [
RECENT_EPICS_CATEGORY,
GROUPS_CATEGORY,
PROJECTS_CATEGORY,
+ USERS_CATEGORY,
IN_THIS_PROJECT_CATEGORY,
SETTINGS_CATEGORY,
HELP_CATEGORY,
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/form.vue b/app/assets/javascripts/ide/components/commit_sidebar/form.vue
index d02dc67d933..ef3da57c240 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/form.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/form.vue
@@ -1,6 +1,7 @@
<script>
-import { GlModal, GlSafeHtmlDirective, GlButton, GlTooltipDirective } from '@gitlab/ui';
+import { GlModal, GlButton, GlTooltipDirective } from '@gitlab/ui';
import { mapState, mapActions, mapGetters } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { n__ } from '~/locale';
import { leftSidebarViews, MAX_WINDOW_HEIGHT_COMPACT } from '../../constants';
import { createUnexpectedCommitError } from '../../lib/errors';
@@ -17,7 +18,7 @@ export default {
GlButton,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
GlTooltip: GlTooltipDirective,
},
data() {
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/success_message.vue b/app/assets/javascripts/ide/components/commit_sidebar/success_message.vue
index 5272c4310d8..dd343bc5f79 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/success_message.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/success_message.vue
@@ -1,6 +1,6 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import { mapState } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
export default {
directives: {
diff --git a/app/assets/javascripts/ide/components/error_message.vue b/app/assets/javascripts/ide/components/error_message.vue
index 67eedc6b37f..eba9bbcdf09 100644
--- a/app/assets/javascripts/ide/components/error_message.vue
+++ b/app/assets/javascripts/ide/components/error_message.vue
@@ -1,6 +1,7 @@
<script>
-import { GlAlert, GlLoadingIcon, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import { mapActions } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
export default {
components: {
@@ -8,7 +9,7 @@ export default {
GlLoadingIcon,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
message: {
diff --git a/app/assets/javascripts/ide/components/jobs/detail.vue b/app/assets/javascripts/ide/components/jobs/detail.vue
index 8d6a0b99e0c..9676233a443 100644
--- a/app/assets/javascripts/ide/components/jobs/detail.vue
+++ b/app/assets/javascripts/ide/components/jobs/detail.vue
@@ -1,7 +1,8 @@
<script>
-import { GlTooltipDirective, GlButton, GlIcon, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlTooltipDirective, GlButton, GlIcon } from '@gitlab/ui';
import { throttle } from 'lodash';
import { mapActions, mapState } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { __ } from '~/locale';
import JobDescription from './detail/description.vue';
import ScrollButton from './detail/scroll_button.vue';
@@ -14,7 +15,7 @@ const scrollPositions = {
export default {
directives: {
GlTooltip: GlTooltipDirective,
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
components: {
GlButton,
diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue
index 9a529bdcee1..ea1dbee4669 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/index.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue
@@ -80,7 +80,7 @@ export default {
@click="createNewItem('blob')"
/>
</li>
- <li><upload :path="path" @create="createTempEntry" /></li>
+ <upload :path="path" @create="createTempEntry" />
<li>
<item-button
:label="__('New directory')"
diff --git a/app/assets/javascripts/ide/components/new_dropdown/upload.vue b/app/assets/javascripts/ide/components/new_dropdown/upload.vue
index 76d8a0aff3d..7c10e055e91 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/upload.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/upload.vue
@@ -65,7 +65,7 @@ export default {
</script>
<template>
- <div>
+ <li>
<item-button
:class="buttonCssClasses"
:show-label="showLabel"
@@ -84,5 +84,5 @@ export default {
data-qa-selector="file_upload_field"
@change="openFile"
/>
- </div>
+ </li>
</template>
diff --git a/app/assets/javascripts/ide/components/panes/right.vue b/app/assets/javascripts/ide/components/panes/right.vue
index c74a5052573..da2d4fbe7f0 100644
--- a/app/assets/javascripts/ide/components/panes/right.vue
+++ b/app/assets/javascripts/ide/components/panes/right.vue
@@ -7,7 +7,6 @@ import PipelinesList from '../pipelines/list.vue';
import Clientside from '../preview/clientside.vue';
import ResizablePanel from '../resizable_panel.vue';
import TerminalView from '../terminal/view.vue';
-import SwitchEditorsView from '../switch_editors/switch_editors_view.vue';
import CollapsibleSidebar from './collapsible_sidebar.vue';
// Need to add the width of the nav buttons since the resizable container contains those as well
@@ -21,7 +20,7 @@ export default {
},
computed: {
...mapState('terminal', { isTerminalVisible: 'isVisible' }),
- ...mapState(['currentMergeRequestId', 'clientsidePreviewEnabled', 'canUseNewWebIde']),
+ ...mapState(['currentMergeRequestId', 'clientsidePreviewEnabled']),
...mapGetters(['packageJson']),
...mapState('rightPane', ['isOpen']),
showLivePreview() {
@@ -30,12 +29,6 @@ export default {
rightExtensionTabs() {
return [
{
- show: this.canUseNewWebIde,
- title: __('Switch editors'),
- views: [{ component: SwitchEditorsView, ...rightSidebarViews.switchEditors }],
- icon: 'bullhorn',
- },
- {
show: true,
title: __('Pipelines'),
views: [
@@ -60,7 +53,6 @@ export default {
},
},
WIDTH,
- SWITCH_EDITORS_VIEW_NAME: rightSidebarViews.switchEditors.name,
};
</script>
@@ -72,11 +64,6 @@ export default {
:min-size="$options.WIDTH"
:resizable="isOpen"
>
- <collapsible-sidebar
- class="gl-w-full"
- :extension-tabs="rightExtensionTabs"
- :init-open-view="$options.SWITCH_EDITORS_VIEW_NAME"
- side="right"
- />
+ <collapsible-sidebar class="gl-w-full" :extension-tabs="rightExtensionTabs" side="right" />
</resizable-panel>
</template>
diff --git a/app/assets/javascripts/ide/components/pipelines/list.vue b/app/assets/javascripts/ide/components/pipelines/list.vue
index 7f513afe82e..7f662f528d7 100644
--- a/app/assets/javascripts/ide/components/pipelines/list.vue
+++ b/app/assets/javascripts/ide/components/pipelines/list.vue
@@ -1,17 +1,8 @@
<script>
-import {
- GlLoadingIcon,
- GlIcon,
- GlSafeHtmlDirective as SafeHtml,
- GlTabs,
- GlTab,
- GlBadge,
- GlAlert,
-} from '@gitlab/ui';
-import { escape } from 'lodash';
+import { GlLoadingIcon, GlIcon, GlTabs, GlTab, GlBadge, GlAlert } from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import IDEServices from '~/ide/services';
-import { sprintf, __ } from '~/locale';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import JobsList from '../jobs/list.vue';
import EmptyState from './empty_state.vue';
@@ -48,16 +39,6 @@ export default {
'stages',
'isLoadingJobs',
]),
- ciLintText() {
- return sprintf(
- __('You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}.'),
- {
- linkStart: `<a href="${escape(this.currentProject.web_url)}/-/ci/lint">`,
- linkEnd: '</a>',
- },
- false,
- );
- },
showLoadingIcon() {
return this.isLoadingPipeline && !this.hasLoadedPipeline;
},
@@ -101,9 +82,8 @@ export default {
:dismissible="false"
class="gl-mt-5"
>
- <p class="gl-mb-0">{{ __('Found errors in your .gitlab-ci.yml:') }}</p>
+ <p class="gl-mb-0">{{ __('Unable to create pipeline') }}</p>
<p class="gl-mb-0 break-word">{{ latestPipeline.yamlError }}</p>
- <p v-safe-html="ciLintText" class="gl-mb-0"></p>
</gl-alert>
<gl-tabs v-else>
<gl-tab :active="!pipelineFailed">
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index 5f35dbdc5e7..3c9c0b1ade1 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -7,6 +7,7 @@ import {
EDITOR_TYPE_CODE,
EDITOR_CODE_INSTANCE_FN,
EDITOR_DIFF_INSTANCE_FN,
+ EXTENSION_CI_SCHEMA_FILE_NAME_MATCH,
} from '~/editor/constants';
import { SourceEditorExtension } from '~/editor/extensions/source_editor_extension_base';
import { EditorWebIdeExtension } from '~/editor/extensions/source_editor_webide_ext';
@@ -26,6 +27,7 @@ import { performanceMarkAndMeasure } from '~/performance/utils';
import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import {
leftSidebarViews,
viewerTypes,
@@ -53,6 +55,7 @@ export default {
DiffViewer,
FileTemplatesBar,
},
+ mixins: [glFeatureFlagMixin()],
props: {
file: {
type: Object,
@@ -145,6 +148,12 @@ export default {
showTabs() {
return !this.shouldHideEditor && this.isEditModeActive && this.previewMode;
},
+ isCiConfigFile() {
+ return (
+ this.file.path === EXTENSION_CI_SCHEMA_FILE_NAME_MATCH &&
+ this.editor?.getEditorType() === EDITOR_TYPE_CODE
+ );
+ },
},
watch: {
'file.name': {
@@ -232,8 +241,6 @@ export default {
return;
}
- this.registerSchemaForFile();
-
Promise.all([this.fetchFileData(), this.fetchEditorconfigRules()])
.then(() => {
this.createEditorInstance();
@@ -357,6 +364,8 @@ export default {
this.model.updateOptions(this.rules);
+ this.registerSchemaForFile();
+
this.model.onChange((model) => {
const { file } = model;
if (!file.active) return;
@@ -446,8 +455,33 @@ export default {
return Promise.resolve();
},
registerSchemaForFile() {
- const schema = this.getJsonSchemaForPath(this.file.path);
- registerSchema(schema);
+ const registerExternalSchema = () => {
+ const schema = this.getJsonSchemaForPath(this.file.path);
+ return registerSchema(schema);
+ };
+ const registerLocalSchema = async () => {
+ if (!this.CiSchemaExtension) {
+ const { CiSchemaExtension } = await import(
+ '~/editor/extensions/source_editor_ci_schema_ext'
+ ).catch((e) =>
+ createAlert({
+ message: e,
+ }),
+ );
+ this.CiSchemaExtension = CiSchemaExtension;
+ }
+ this.editor.use({ definition: this.CiSchemaExtension });
+ this.editor.registerCiSchema();
+ };
+
+ if (this.isCiConfigFile && this.glFeatures.schemaLinting) {
+ registerLocalSchema();
+ } else {
+ if (this.CiSchemaExtension) {
+ this.editor.unuse(this.CiSchemaExtension);
+ }
+ registerExternalSchema();
+ }
},
updateEditor(data) {
// Looks like our model wrapper `.dispose` causes the monaco editor to emit some position changes after
diff --git a/app/assets/javascripts/ide/components/switch_editors/switch_editors_view.vue b/app/assets/javascripts/ide/components/switch_editors/switch_editors_view.vue
deleted file mode 100644
index 00164f65e33..00000000000
--- a/app/assets/javascripts/ide/components/switch_editors/switch_editors_view.vue
+++ /dev/null
@@ -1,103 +0,0 @@
-<script>
-import { GlButton, GlEmptyState, GlLink } from '@gitlab/ui';
-import { mapState } from 'vuex';
-import { createAlert } from '~/flash';
-import { logError } from '~/lib/logger';
-import axios from '~/lib/utils/axios_utils';
-import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
-import { ignoreWhilePending } from '~/lib/utils/ignore_while_pending';
-import { s__, __ } from '~/locale';
-import eventHub from '../../eventhub';
-
-export const MSG_DESCRIPTION = s__('WebIDE|You are invited to experience the new Web IDE.');
-export const MSG_BUTTON_TEXT = s__('WebIDE|Switch to new Web IDE');
-export const MSG_LEARN_MORE = __('Learn more');
-export const MSG_TITLE = s__('WebIDE|Ready for something new?');
-
-export const MSG_CONFIRM = s__(
- 'WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes.',
-);
-export const MSG_ERROR_ALERT = s__(
- 'WebIDE|Something went wrong while updating the user preferences. Please see developer console for details.',
-);
-
-export default {
- components: {
- GlButton,
- GlEmptyState,
- GlLink,
- },
- data() {
- return {
- loading: false,
- };
- },
- computed: {
- ...mapState(['switchEditorSvgPath', 'links', 'userPreferencesPath']),
- },
- methods: {
- async submitSwitch() {
- const confirmed = await confirmAction(MSG_CONFIRM, {
- primaryBtnText: __('Switch editors'),
- cancelBtnText: __('Cancel'),
- });
-
- if (!confirmed) {
- return;
- }
-
- try {
- await axios.put(this.userPreferencesPath, {
- user: { use_legacy_web_ide: false },
- });
- } catch (e) {
- // why: We do not want to translate console logs
- // eslint-disable-next-line @gitlab/require-i18n-strings
- logError('Error while updating user preferences', e);
- createAlert({
- message: MSG_ERROR_ALERT,
- });
- return;
- }
-
- eventHub.$emit('skip-beforeunload');
- window.location.reload();
- },
- // what: ignoreWhilePending prevents double confirmation boxes
- onSwitchClicked: ignoreWhilePending(async function onSwitchClicked() {
- this.loading = true;
-
- try {
- await this.submitSwitch();
- } finally {
- this.loading = false;
- }
- }),
- },
- MSG_TITLE,
- MSG_DESCRIPTION,
- MSG_BUTTON_TEXT,
- MSG_LEARN_MORE,
-};
-</script>
-
-<template>
- <div class="gl-h-full gl-display-flex gl-flex-direction-column gl-justify-content-center">
- <gl-empty-state :svg-path="switchEditorSvgPath" :svg-height="150" :title="$options.MSG_TITLE">
- <template #description>
- <span>{{ $options.MSG_DESCRIPTION }}</span>
- <gl-link :href="links.newWebIDEHelpPagePath">{{ $options.MSG_LEARN_MORE }}</gl-link
- >.
- </template>
- <template #actions>
- <gl-button
- category="primary"
- variant="confirm"
- :loading="loading"
- @click="onSwitchClicked"
- >{{ $options.MSG_BUTTON_TEXT }}</gl-button
- >
- </template>
- </gl-empty-state>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/terminal/empty_state.vue b/app/assets/javascripts/ide/components/terminal/empty_state.vue
index 623ba719b28..fa93f6d42a5 100644
--- a/app/assets/javascripts/ide/components/terminal/empty_state.vue
+++ b/app/assets/javascripts/ide/components/terminal/empty_state.vue
@@ -1,5 +1,6 @@
<script>
-import { GlLoadingIcon, GlButton, GlAlert, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlLoadingIcon, GlButton, GlAlert } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
export default {
components: {
@@ -8,7 +9,7 @@ export default {
GlAlert,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
isLoading: {
diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js
index c8e737fa6f5..01ce5fa07ee 100644
--- a/app/assets/javascripts/ide/constants.js
+++ b/app/assets/javascripts/ide/constants.js
@@ -61,7 +61,6 @@ export const leftSidebarViews = {
};
export const rightSidebarViews = {
- switchEditors: { name: 'switch-editors', keepAlive: true },
pipelines: { name: 'pipelines-list', keepAlive: true },
jobsDetail: { name: 'jobs-detail', keepAlive: false },
mergeRequestInfo: { name: 'merge-request-info', keepAlive: true },
@@ -119,3 +118,5 @@ export const DEFAULT_BRANCH = 'main';
// Ping Usage Metrics Keys
export const PING_USAGE_PREVIEW_KEY = 'web_ide_clientside_preview';
export const PING_USAGE_PREVIEW_SUCCESS_KEY = 'web_ide_clientside_preview_success';
+
+export const GITLAB_WEB_IDE_FEEDBACK_ISSUE = 'https://gitlab.com/gitlab-org/gitlab/-/issues/377367';
diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js
index dec282239d9..1347d92b3b7 100644
--- a/app/assets/javascripts/ide/index.js
+++ b/app/assets/javascripts/ide/index.js
@@ -8,7 +8,6 @@ import { parseBoolean } from '../lib/utils/common_utils';
import { resetServiceWorkersPublicPath } from '../lib/utils/webpack';
import ide from './components/ide.vue';
import { createRouter } from './ide_router';
-import { initGitlabWebIDE } from './init_gitlab_web_ide';
import { DEFAULT_THEME } from './lib/themes';
import { createStore } from './stores';
@@ -74,7 +73,6 @@ export const initLegacyWebIDE = (el, options = {}) => {
codesandboxBundlerUrl: el.dataset.codesandboxBundlerUrl,
environmentsGuidanceAlertDismissed: !parseBoolean(el.dataset.enableEnvironmentsGuidance),
previewMarkdownPath: el.dataset.previewMarkdownPath,
- canUseNewWebIde: parseBoolean(el.dataset.canUseNewWebIde),
userPreferencesPath: el.dataset.userPreferencesPath,
});
},
@@ -96,7 +94,7 @@ export const initLegacyWebIDE = (el, options = {}) => {
*
* @param {Objects} options - Extra options for the IDE (Used by EE).
*/
-export function startIde(options) {
+export async function startIde(options) {
const ideElement = document.getElementById('ide');
if (!ideElement) {
@@ -106,6 +104,7 @@ export function startIde(options) {
const useNewWebIde = parseBoolean(ideElement.dataset.useNewWebIde);
if (useNewWebIde) {
+ const { initGitlabWebIDE } = await import('./init_gitlab_web_ide');
initGitlabWebIDE(ideElement);
} else {
resetServiceWorkersPublicPath();
diff --git a/app/assets/javascripts/ide/init_gitlab_web_ide.js b/app/assets/javascripts/ide/init_gitlab_web_ide.js
index 140f2895a29..d3c64754e8a 100644
--- a/app/assets/javascripts/ide/init_gitlab_web_ide.js
+++ b/app/assets/javascripts/ide/init_gitlab_web_ide.js
@@ -1,29 +1,89 @@
-import { cleanTrailingSlash } from './stores/utils';
+import { start } from '@gitlab/web-ide';
+import { __ } from '~/locale';
+import { cleanLeadingSeparator } from '~/lib/utils/url_utility';
+import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_action';
+import { createAndSubmitForm } from '~/lib/utils/create_and_submit_form';
+import csrf from '~/lib/utils/csrf';
+import { getBaseConfig } from './lib/gitlab_web_ide/get_base_config';
+import { setupRootElement } from './lib/gitlab_web_ide/setup_root_element';
+import { GITLAB_WEB_IDE_FEEDBACK_ISSUE } from './constants';
-export const initGitlabWebIDE = async (el) => {
- const { start } = await import('@gitlab/web-ide');
+const buildRemoteIdeURL = (ideRemotePath, remoteHost, remotePathArg) => {
+ const remotePath = cleanLeadingSeparator(remotePathArg);
- const { gitlab_url: gitlabUrl } = window.gon;
- const baseUrl = new URL(process.env.GITLAB_WEB_IDE_PUBLIC_PATH, window.location.origin);
+ const replacers = {
+ ':remote_host': encodeURIComponent(remoteHost),
+ ':remote_path': encodeURIComponent(remotePath).replaceAll('%2F', '/'),
+ };
- // what: Pull what we need from the element. We will replace it soon.
- const { cspNonce: nonce, branchName: ref, projectPath } = el.dataset;
+ // why: Use the function callback of "replace" so we replace both keys at once
+ return ideRemotePath.replace(/(:remote_host|:remote_path)/g, (key) => {
+ return replacers[key];
+ });
+};
+
+const getMRTargetProject = () => {
+ const url = new URL(window.location.href);
+
+ return url.searchParams.get('target_project') || '';
+};
- // what: Clean up the element, but preserve id.
- // why: This way we don't inherit any `ide-loading` side-effects. This
- // mirrors the behavior of Vue when it mounts to an element.
- const newEl = document.createElement(el.tagName);
- newEl.id = el.id;
- newEl.classList.add('gl--flex-center', 'gl-relative', 'gl-h-full');
+export const initGitlabWebIDE = async (el) => {
+ // what: Pull what we need from the element. We will replace it soon.
+ const {
+ cspNonce: nonce,
+ branchName: ref,
+ projectPath,
+ ideRemotePath,
+ filePath,
+ mergeRequest: mrId,
+ forkInfo: forkInfoJSON,
+ } = el.dataset;
- el.replaceWith(newEl);
+ const rootEl = setupRootElement(el);
+ const forkInfo = forkInfoJSON ? JSON.parse(forkInfoJSON) : null;
- // what: Trigger start on our new mounting element
- await start(newEl, {
- baseUrl: cleanTrailingSlash(baseUrl.href),
+ // See ClientOnlyConfig https://gitlab.com/gitlab-org/gitlab-web-ide/-/blob/main/packages/web-ide-types/src/config.ts#L17
+ start(rootEl, {
+ ...getBaseConfig(),
+ nonce,
+ // Use same headers as defined in axios_utils
+ httpHeaders: {
+ [csrf.headerKey]: csrf.token,
+ 'X-Requested-With': 'XMLHttpRequest',
+ },
projectPath,
- gitlabUrl,
ref,
- nonce,
+ filePath,
+ mrId,
+ mrTargetProject: getMRTargetProject(),
+ // note: At the time of writing this, forkInfo isn't expected by `@gitlab/web-ide`,
+ // but it will be soon.
+ forkInfo,
+ links: {
+ feedbackIssue: GITLAB_WEB_IDE_FEEDBACK_ISSUE,
+ userPreferences: el.dataset.userPreferencesPath,
+ },
+ async handleStartRemote({ remoteHost, remotePath, connectionToken }) {
+ const confirmed = await confirmAction(
+ __('Are you sure you want to leave the Web IDE? All unsaved changes will be lost.'),
+ {
+ primaryBtnText: __('Start remote connection'),
+ cancelBtnText: __('Continue editing'),
+ },
+ );
+
+ if (!confirmed) {
+ return;
+ }
+
+ createAndSubmitForm({
+ url: buildRemoteIdeURL(ideRemotePath, remoteHost, remotePath),
+ data: {
+ connection_token: connectionToken,
+ return_url: window.location.href,
+ },
+ });
+ },
});
};
diff --git a/app/assets/javascripts/ide/lib/diff/controller.js b/app/assets/javascripts/ide/lib/diff/controller.js
index 682914df9ec..7595a1cedf1 100644
--- a/app/assets/javascripts/ide/lib/diff/controller.js
+++ b/app/assets/javascripts/ide/lib/diff/controller.js
@@ -2,7 +2,7 @@ import { throttle } from 'lodash';
import { Range } from 'monaco-editor';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import Disposable from '../common/disposable';
-import DirtyDiffWorker from './diff_worker';
+import DirtyDiffWorker from './diff_worker?worker';
export const getDiffChangeType = (change) => {
if (change.modified) {
diff --git a/app/assets/javascripts/ide/lib/editor_options.js b/app/assets/javascripts/ide/lib/editor_options.js
index 525afcb2083..289027c3054 100644
--- a/app/assets/javascripts/ide/lib/editor_options.js
+++ b/app/assets/javascripts/ide/lib/editor_options.js
@@ -1,3 +1,12 @@
+import { useNewFonts } from '~/lib/utils/common_utils';
+import { getCssVariable } from '~/lib/utils/css_utils';
+
+const fontOptions = {};
+
+if (useNewFonts()) {
+ fontOptions.fontFamily = getCssVariable('--code-editor-font');
+}
+
export const defaultEditorOptions = {
model: null,
readOnly: false,
@@ -9,6 +18,7 @@ export const defaultEditorOptions = {
wordWrap: 'on',
glyphMargin: true,
automaticLayout: true,
+ ...fontOptions,
};
export const defaultDiffOptions = {
@@ -27,7 +37,6 @@ export const defaultDiffEditorOptions = {
};
export const defaultModelOptions = {
- endOfLine: 0,
insertFinalNewline: true,
trimTrailingWhitespace: false,
};
diff --git a/app/assets/javascripts/ide/lib/gitlab_web_ide/get_base_config.js b/app/assets/javascripts/ide/lib/gitlab_web_ide/get_base_config.js
new file mode 100644
index 00000000000..fbd2ce4ce69
--- /dev/null
+++ b/app/assets/javascripts/ide/lib/gitlab_web_ide/get_base_config.js
@@ -0,0 +1,12 @@
+import { cleanEndingSeparator } from '~/lib/utils/url_utility';
+
+const getBaseUrl = () => {
+ const baseUrlObj = new URL(process.env.GITLAB_WEB_IDE_PUBLIC_PATH, window.location.origin);
+
+ return cleanEndingSeparator(baseUrlObj.href);
+};
+
+export const getBaseConfig = () => ({
+ baseUrl: getBaseUrl(),
+ gitlabUrl: window.gon.gitlab_url,
+});
diff --git a/app/assets/javascripts/ide/lib/gitlab_web_ide/index.js b/app/assets/javascripts/ide/lib/gitlab_web_ide/index.js
new file mode 100644
index 00000000000..8311e11672e
--- /dev/null
+++ b/app/assets/javascripts/ide/lib/gitlab_web_ide/index.js
@@ -0,0 +1,2 @@
+export * from './get_base_config';
+export * from './setup_root_element';
diff --git a/app/assets/javascripts/ide/lib/gitlab_web_ide/setup_root_element.js b/app/assets/javascripts/ide/lib/gitlab_web_ide/setup_root_element.js
new file mode 100644
index 00000000000..b0e06c88d26
--- /dev/null
+++ b/app/assets/javascripts/ide/lib/gitlab_web_ide/setup_root_element.js
@@ -0,0 +1,14 @@
+/**
+ * Cleans up the given element and prepares it for mounting to `@gitlab/web-ide`
+ *
+ * @param {Element} root The original root element
+ * @returns {Element} A new element ready to be used by `@gitlab/web-ide`
+ */
+export const setupRootElement = (el) => {
+ const newEl = document.createElement(el.tagName);
+ newEl.id = el.id;
+ newEl.classList.add('gl--flex-center', 'gl-relative', 'gl-h-full');
+ el.replaceWith(newEl);
+
+ return newEl;
+};
diff --git a/app/assets/javascripts/ide/remote/index.js b/app/assets/javascripts/ide/remote/index.js
new file mode 100644
index 00000000000..fb8db20c0c1
--- /dev/null
+++ b/app/assets/javascripts/ide/remote/index.js
@@ -0,0 +1,40 @@
+import { startRemote } from '@gitlab/web-ide';
+import { getBaseConfig, setupRootElement } from '~/ide/lib/gitlab_web_ide';
+import { isSameOriginUrl, joinPaths } from '~/lib/utils/url_utility';
+
+/**
+ * @param {Element} rootEl
+ */
+export const mountRemoteIDE = async (el) => {
+ const {
+ remoteHost: remoteAuthority,
+ remotePath: hostPath,
+ cspNonce,
+ connectionToken,
+ returnUrl,
+ } = el.dataset;
+
+ const rootEl = setupRootElement(el);
+
+ const visitReturnUrl = () => {
+ // security: Only change `href` if of the same origin as current page
+ if (returnUrl && isSameOriginUrl(returnUrl)) {
+ window.location.href = returnUrl;
+ } else {
+ window.location.reload();
+ }
+ };
+
+ startRemote(rootEl, {
+ ...getBaseConfig(),
+ nonce: cspNonce,
+ connectionToken,
+ // remoteAuthority must start with "/"
+ remoteAuthority: joinPaths('/', remoteAuthority),
+ // hostPath must start with "/"
+ hostPath: joinPaths('/', hostPath),
+ // TODO Handle error better
+ handleError: visitReturnUrl,
+ handleClose: visitReturnUrl,
+ });
+};
diff --git a/app/assets/javascripts/ide/services/index.js b/app/assets/javascripts/ide/services/index.js
index 805476c71bc..1f9bc834140 100644
--- a/app/assets/javascripts/ide/services/index.js
+++ b/app/assets/javascripts/ide/services/index.js
@@ -4,7 +4,7 @@ import dismissUserCallout from '~/graphql_shared/mutations/dismiss_user_callout.
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import axios from '~/lib/utils/axios_utils';
import { joinPaths, escapeFileUrl } from '~/lib/utils/url_utility';
-import ciConfig from '~/pipeline_editor/graphql/queries/ci_config.query.graphql';
+import ciConfig from '~/ci/pipeline_editor/graphql/queries/ci_config.query.graphql';
import { query, mutate } from './gql';
export default {
diff --git a/app/assets/javascripts/ide/stores/modules/terminal/actions/session_controls.js b/app/assets/javascripts/ide/stores/modules/terminal/actions/session_controls.js
index 91868132a5a..a510ec0847b 100644
--- a/app/assets/javascripts/ide/stores/modules/terminal/actions/session_controls.js
+++ b/app/assets/javascripts/ide/stores/modules/terminal/actions/session_controls.js
@@ -1,6 +1,6 @@
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
-import httpStatus from '~/lib/utils/http_status';
+import httpStatus, { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
import * as terminalService from '../../../../services/terminals';
import { STARTING, STOPPING, STOPPED } from '../constants';
import * as messages from '../messages';
@@ -108,7 +108,7 @@ export const restartSession = ({ state, dispatch, rootState }) => {
// We may have removed the build, in this case we'll just create a new session
if (
responseStatus === httpStatus.NOT_FOUND ||
- responseStatus === httpStatus.UNPROCESSABLE_ENTITY
+ responseStatus === HTTP_STATUS_UNPROCESSABLE_ENTITY
) {
dispatch('startSession');
} else {
diff --git a/app/assets/javascripts/ide/stores/modules/terminal/messages.js b/app/assets/javascripts/ide/stores/modules/terminal/messages.js
index ec05ca84754..fa1c7f23677 100644
--- a/app/assets/javascripts/ide/stores/modules/terminal/messages.js
+++ b/app/assets/javascripts/ide/stores/modules/terminal/messages.js
@@ -1,5 +1,5 @@
import { escape } from 'lodash';
-import httpStatus from '~/lib/utils/http_status';
+import httpStatus, { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
import { __, sprintf } from '~/locale';
export const UNEXPECTED_ERROR_CONFIG = __(
@@ -28,7 +28,7 @@ export const ERROR_PERMISSION = __(
);
export const configCheckError = (status, helpUrl) => {
- if (status === httpStatus.UNPROCESSABLE_ENTITY) {
+ if (status === HTTP_STATUS_UNPROCESSABLE_ENTITY) {
return sprintf(
ERROR_CONFIG,
{
diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js
index 70efda970bf..b89d9d38a1a 100644
--- a/app/assets/javascripts/ide/stores/state.js
+++ b/app/assets/javascripts/ide/stores/state.js
@@ -34,5 +34,4 @@ export default () => ({
environmentsGuidanceAlertDetected: false,
previewMarkdownPath: '',
userPreferencesPath: '',
- canUseNewWebIde: false,
});
diff --git a/app/assets/javascripts/import_entities/components/group_dropdown.vue b/app/assets/javascripts/import_entities/components/group_dropdown.vue
index 25d4037bbe5..f351a9a392f 100644
--- a/app/assets/javascripts/import_entities/components/group_dropdown.vue
+++ b/app/assets/javascripts/import_entities/components/group_dropdown.vue
@@ -1,5 +1,21 @@
<script>
import { GlDropdown, GlSearchBoxByType } from '@gitlab/ui';
+import { debounce } from 'lodash';
+
+import { s__ } from '~/locale';
+import { createAlert } from '~/flash';
+import searchNamespacesWhereUserCanCreateProjectsQuery from '~/projects/new/queries/search_namespaces_where_user_can_create_projects.query.graphql';
+import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants';
+import { MINIMUM_SEARCH_LENGTH } from '~/graphql_shared/constants';
+import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
+
+const reportNamespaceLoadError = debounce(
+ () =>
+ createAlert({
+ message: s__('ImportProjects|Requesting namespaces failed'),
+ }),
+ DEFAULT_DEBOUNCE_AND_THROTTLE_MS,
+);
export default {
components: {
@@ -7,18 +23,32 @@ export default {
GlSearchBoxByType,
},
inheritAttrs: false,
- props: {
- namespaces: {
- type: Array,
- required: true,
- },
- },
data() {
return { searchTerm: '' };
},
+ apollo: {
+ namespaces: {
+ query: searchNamespacesWhereUserCanCreateProjectsQuery,
+ variables() {
+ return {
+ search: this.searchTerm,
+ };
+ },
+ skip() {
+ const hasNotEnoughSearchCharacters =
+ this.searchTerm.length > 0 && this.searchTerm.length < MINIMUM_SEARCH_LENGTH;
+ return hasNotEnoughSearchCharacters;
+ },
+ update(data) {
+ return data.currentUser.groups.nodes;
+ },
+ error: reportNamespaceLoadError,
+ debounce: DEBOUNCE_DELAY,
+ },
+ },
computed: {
filteredNamespaces() {
- return this.namespaces.filter((ns) =>
+ return (this.namespaces ?? []).filter((ns) =>
ns.fullPath.toLowerCase().includes(this.searchTerm.toLowerCase()),
);
},
diff --git a/app/assets/javascripts/import_entities/components/import_status.vue b/app/assets/javascripts/import_entities/components/import_status.vue
index 5455a034106..bd69165f0ca 100644
--- a/app/assets/javascripts/import_entities/components/import_status.vue
+++ b/app/assets/javascripts/import_entities/components/import_status.vue
@@ -49,7 +49,7 @@ const STATUS_MAP = {
text: __('Timeout'),
variant: 'danger',
},
- [STATUSES.CANCELLED]: {
+ [STATUSES.CANCELED]: {
icon: 'status-stopped',
text: __('Cancelled'),
variant: 'neutral',
diff --git a/app/assets/javascripts/import_entities/constants.js b/app/assets/javascripts/import_entities/constants.js
index c470da21765..48b7febca4b 100644
--- a/app/assets/javascripts/import_entities/constants.js
+++ b/app/assets/javascripts/import_entities/constants.js
@@ -9,6 +9,10 @@ export const STATUSES = {
STARTED: 'started',
NONE: 'none',
SCHEDULING: 'scheduling',
- CANCELLED: 'cancelled',
+ CANCELED: 'canceled',
TIMEOUT: 'timeout',
};
+
+export const PROVIDERS = {
+ GITHUB: 'github',
+};
diff --git a/app/assets/javascripts/import_entities/import_groups/components/import_table.vue b/app/assets/javascripts/import_entities/import_groups/components/import_table.vue
index 66dff77eef8..6412f26fde7 100644
--- a/app/assets/javascripts/import_entities/import_groups/components/import_table.vue
+++ b/app/assets/javascripts/import_entities/import_groups/components/import_table.vue
@@ -21,12 +21,13 @@ import { getGroupPathAvailability } from '~/rest_api';
import axios from '~/lib/utils/axios_utils';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import { helpPagePath } from '~/helpers/help_page_helper';
+import searchNamespacesWhereUserCanCreateProjectsQuery from '~/projects/new/queries/search_namespaces_where_user_can_create_projects.query.graphql';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { STATUSES } from '../../constants';
import ImportStatusCell from '../../components/import_status.vue';
import importGroupsMutation from '../graphql/mutations/import_groups.mutation.graphql';
import updateImportStatusMutation from '../graphql/mutations/update_import_status.mutation.graphql';
-import availableNamespacesQuery from '../graphql/queries/available_namespaces.query.graphql';
import bulkImportSourceGroupsQuery from '../graphql/queries/bulk_import_source_groups.query.graphql';
import { NEW_NAME_FIELD, ROOT_NAMESPACE, i18n } from '../constants';
import { StatusPoller } from '../services/status_poller';
@@ -107,7 +108,12 @@ export default {
return { page: this.page, filter: this.filter, perPage: this.perPage };
},
},
- availableNamespaces: availableNamespacesQuery,
+ availableNamespaces: {
+ query: searchNamespacesWhereUserCanCreateProjectsQuery,
+ update(data) {
+ return data.currentUser.groups.nodes;
+ },
+ },
},
fields: [
@@ -158,7 +164,7 @@ export default {
}
return this.groups.map((group) => {
- const importTarget = this.getImportTarget(group);
+ const importTarget = this.importTargets[group.id];
const status = this.getStatus(group);
const flags = {
@@ -250,10 +256,14 @@ export default {
this.page = 1;
},
- groupsTableData() {
+ groups() {
const table = this.getTableRef();
const matches = new Set();
- this.groupsTableData.forEach((g, idx) => {
+ this.groups.forEach((g, idx) => {
+ if (!this.importGroups[g.id]) {
+ this.setDefaultImportTarget(g);
+ }
+
if (this.selectedGroupsIds.includes(g.id)) {
matches.add(g.id);
this.$nextTick(() => {
@@ -421,7 +431,7 @@ export default {
data: { exists },
} = await getGroupPathAvailability(
importTarget.newName,
- importTarget.targetNamespace.id,
+ getIdFromGraphQLId(importTarget.targetNamespace.id),
{
cancelToken: importTarget.cancellationToken?.token,
},
@@ -444,11 +454,7 @@ export default {
importTarget.validationErrors = newValidationErrors;
}, VALIDATION_DEBOUNCE_TIME),
- getImportTarget(group) {
- if (this.importTargets[group.id]) {
- return this.importTargets[group.id];
- }
-
+ setDefaultImportTarget(group) {
// If we've reached this Vue application we have at least one potential import destination
const defaultTargetNamespace =
// first option: namespace id was explicitly provided
@@ -482,9 +488,13 @@ export default {
validationErrors: [],
});
- getGroupPathAvailability(importTarget.newName, importTarget.targetNamespace.id, {
- cancelToken: cancellationToken.token,
- })
+ getGroupPathAvailability(
+ importTarget.newName,
+ getIdFromGraphQLId(importTarget.targetNamespace.id),
+ {
+ cancelToken: cancellationToken.token,
+ },
+ )
.then(({ data: { exists, suggests: suggestions } }) => {
if (!exists) return;
@@ -505,7 +515,6 @@ export default {
.catch(() => {
// empty catch intended
});
- return this.importTargets[group.id];
},
},
@@ -692,7 +701,6 @@ export default {
<template #cell(importTarget)="{ item: group }">
<import-target-cell
:group="group"
- :available-namespaces="availableNamespaces"
:group-path-regex="groupPathRegex"
@update-target-namespace="updateImportTarget(group, { targetNamespace: $event })"
@update-new-name="updateImportTarget(group, { newName: $event })"
diff --git a/app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue b/app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue
index 4fbbd5b239c..04a90d9c20c 100644
--- a/app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue
+++ b/app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue
@@ -22,10 +22,6 @@ export default {
type: Object,
required: true,
},
- availableNamespaces: {
- type: Array,
- required: true,
- },
},
computed: {
@@ -53,7 +49,6 @@ export default {
#default="{ namespaces }"
:text="fullPath"
:disabled="!group.flags.isAvailableForImport"
- :namespaces="availableNamespaces"
toggle-class="gl-rounded-top-right-none! gl-rounded-bottom-right-none!"
class="gl-h-7 gl-flex-grow-1"
data-qa-selector="target_namespace_selector_dropdown"
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js b/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js
index 36da996ea17..913a5a659b3 100644
--- a/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js
@@ -10,7 +10,6 @@ import typeDefs from './typedefs.graphql';
export const clientTypenames = {
BulkImportSourceGroupConnection: 'ClientBulkImportSourceGroupConnection',
BulkImportSourceGroup: 'ClientBulkImportSourceGroup',
- AvailableNamespace: 'ClientAvailableNamespace',
BulkImportPageInfo: 'ClientBulkImportPageInfo',
BulkImportTarget: 'ClientBulkImportTarget',
BulkImportProgress: 'ClientBulkImportProgress',
@@ -110,15 +109,6 @@ export function createResolvers({ endpoints }) {
};
return response;
},
-
- availableNamespaces: () =>
- axios.get(endpoints.availableNamespaces).then(({ data }) =>
- data.map((namespace) => ({
- __typename: clientTypenames.AvailableNamespace,
- id: namespace.id,
- fullPath: namespace.full_path,
- })),
- ),
},
Mutation: {
async updateImportStatus(_, { id, status: newStatus }, { client, getCacheKey }) {
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/queries/available_namespaces.query.graphql b/app/assets/javascripts/import_entities/import_groups/graphql/queries/available_namespaces.query.graphql
deleted file mode 100644
index b0741dfbe5c..00000000000
--- a/app/assets/javascripts/import_entities/import_groups/graphql/queries/available_namespaces.query.graphql
+++ /dev/null
@@ -1,6 +0,0 @@
-query availableNamespaces {
- availableNamespaces @client {
- id
- fullPath
- }
-}
diff --git a/app/assets/javascripts/import_entities/import_groups/index.js b/app/assets/javascripts/import_entities/import_groups/index.js
index 5d7e7911f5a..494a845b1f9 100644
--- a/app/assets/javascripts/import_entities/import_groups/index.js
+++ b/app/assets/javascripts/import_entities/import_groups/index.js
@@ -12,7 +12,6 @@ export function mountImportGroupsApp(mountElement) {
const {
statusPath,
- availableNamespacesPath,
createBulkImportPath,
jobsPath,
historyPath,
@@ -25,7 +24,6 @@ export function mountImportGroupsApp(mountElement) {
sourceUrl,
endpoints: {
status: statusPath,
- availableNamespaces: availableNamespacesPath,
createBulkImport: createBulkImportPath,
},
}),
diff --git a/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue b/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue
index 97a7ed4bf55..63a36f1a79f 100644
--- a/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue
+++ b/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue
@@ -37,6 +37,11 @@ export default {
required: false,
default: false,
},
+ cancelable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
optionalStages: {
type: Array,
required: false,
@@ -58,9 +63,8 @@ export default {
},
computed: {
- ...mapState(['filter', 'repositories', 'namespaces', 'defaultTargetNamespace', 'pageInfo']),
+ ...mapState(['filter', 'repositories', 'defaultTargetNamespace', 'pageInfo', 'isLoadingRepos']),
...mapGetters([
- 'isLoading',
'isImportingAnyRepo',
'importingRepoCount',
'hasImportableRepos',
@@ -98,7 +102,6 @@ export default {
},
mounted() {
- this.fetchNamespaces();
this.fetchJobs();
if (!this.paginatable) {
@@ -115,7 +118,6 @@ export default {
...mapActions([
'fetchRepos',
'fetchJobs',
- 'fetchNamespaces',
'stopJobsPolling',
'clearJobsEtagPoll',
'setFilter',
@@ -196,22 +198,22 @@ export default {
<provider-repo-table-row
:key="repo.importSource.providerLink"
:repo="repo"
- :available-namespaces="namespaces"
:user-namespace="defaultTargetNamespace"
:optional-stages="optionalStagesSelection"
+ :cancelable="cancelable"
/>
</template>
</tbody>
</table>
</div>
<gl-intersection-observer
- v-if="paginatable"
+ v-if="paginatable && pageInfo.hasNextPage"
:key="pagePaginationStateKey"
@appear="fetchRepos"
/>
- <gl-loading-icon v-if="isLoading" class="gl-mt-7" size="lg" />
+ <gl-loading-icon v-if="isLoadingRepos" class="gl-mt-7" size="lg" />
- <div v-if="!isLoading && repositories.length === 0" class="gl-text-center">
+ <div v-if="!isLoadingRepos && repositories.length === 0" class="gl-text-center">
<strong>{{ emptyStateText }}</strong>
</div>
</div>
diff --git a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue
index 458e0fb1cb1..b8faf349375 100644
--- a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue
+++ b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue
@@ -8,13 +8,15 @@ import {
GlDropdownItem,
GlDropdownDivider,
GlDropdownSectionHeader,
+ GlTooltip,
} from '@gitlab/ui';
import { mapState, mapGetters, mapActions } from 'vuex';
import { __ } from '~/locale';
+import { helpPagePath } from '~/helpers/help_page_helper';
import ImportGroupDropdown from '../../components/group_dropdown.vue';
import ImportStatus from '../../components/import_status.vue';
import { STATUSES } from '../../constants';
-import { isProjectImportable, isIncompatible, getImportStatus } from '../utils';
+import { isProjectImportable, isImporting, isIncompatible, getImportStatus } from '../utils';
export default {
name: 'ProviderRepoTableRow',
@@ -29,6 +31,7 @@ export default {
GlIcon,
GlBadge,
GlLink,
+ GlTooltip,
},
props: {
repo: {
@@ -39,14 +42,15 @@ export default {
type: String,
required: true,
},
- availableNamespaces: {
- type: Array,
- required: true,
- },
optionalStages: {
type: Object,
required: true,
},
+ cancelable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
@@ -73,6 +77,14 @@ export default {
return getImportStatus(this.repo);
},
+ isImporting() {
+ return isImporting(this.repo);
+ },
+
+ isCancelable() {
+ return this.cancelable && this.isImporting && this.importStatus !== STATUSES.SCHEDULING;
+ },
+
stats() {
return this.repo.importedProject?.stats;
},
@@ -96,7 +108,7 @@ export default {
},
methods: {
- ...mapActions(['fetchImport', 'setImportTarget']),
+ ...mapActions(['fetchImport', 'cancelImport', 'setImportTarget']),
updateImportTarget(changedValues) {
this.setImportTarget({
repoId: this.repo.importSource.id,
@@ -104,6 +116,8 @@ export default {
});
},
},
+
+ helpUrl: helpPagePath('/user/project/import/github.md'),
};
</script>
@@ -127,11 +141,7 @@ export default {
<template v-if="repo.importSource.target">{{ repo.importSource.target }}</template>
<template v-else-if="isImportNotStarted">
<div class="import-entities-target-select gl-display-flex gl-align-items-stretch gl-w-full">
- <import-group-dropdown
- #default="{ namespaces }"
- :text="importTarget.targetNamespace"
- :namespaces="availableNamespaces"
- >
+ <import-group-dropdown #default="{ namespaces }" :text="importTarget.targetNamespace">
<template v-if="namespaces.length">
<gl-dropdown-section-header>{{ __('Groups') }}</gl-dropdown-section-header>
<gl-dropdown-item
@@ -168,6 +178,26 @@ export default {
<import-status :status="importStatus" :stats="stats" />
</td>
<td data-testid="actions" class="gl-vertical-align-top gl-pt-4">
+ <gl-tooltip :target="() => $refs.cancelButton.$el">
+ <div class="gl-text-left">
+ <p class="gl-mb-5 gl-font-weight-bold">{{ s__('ImportProjects|Cancel import') }}</p>
+ {{
+ s__(
+ 'ImportProjects|Imported files will be kept. You can import this repository again later.',
+ )
+ }}
+ <gl-link :href="$options.helpUrl" target="_blank">{{ __('Learn more.') }}</gl-link>
+ </div>
+ </gl-tooltip>
+ <gl-button
+ v-show="isCancelable"
+ ref="cancelButton"
+ variant="danger"
+ category="secondary"
+ icon="cancel"
+ :aria-label="__('Cancel')"
+ @click="cancelImport({ repoId: repo.importSource.id })"
+ />
<gl-button
v-if="isFinished"
class="btn btn-default"
diff --git a/app/assets/javascripts/import_entities/import_projects/index.js b/app/assets/javascripts/import_entities/import_projects/index.js
index df26d6ac4f6..197fb03af2c 100644
--- a/app/assets/javascripts/import_entities/import_projects/index.js
+++ b/app/assets/javascripts/import_entities/import_projects/index.js
@@ -1,10 +1,14 @@
import Vue from 'vue';
+import VueApollo from 'vue-apollo';
import { parseBoolean } from '~/lib/utils/common_utils';
import Translate from '~/vue_shared/translate';
+import createDefaultClient from '~/lib/graphql';
import ImportProjectsTable from './components/import_projects_table.vue';
+
import createStore from './store';
Vue.use(Translate);
+Vue.use(VueApollo);
export function initStoreFromElement(element) {
const {
@@ -15,7 +19,7 @@ export function initStoreFromElement(element) {
reposPath,
jobsPath,
importPath,
- namespacesPath,
+ cancelPath,
defaultTargetNamespace,
paginatable,
} = element.dataset;
@@ -31,7 +35,7 @@ export function initStoreFromElement(element) {
reposPath,
jobsPath,
importPath,
- namespacesPath,
+ cancelPath,
},
hasPagination: parseBoolean(paginatable),
});
@@ -43,9 +47,16 @@ export function initPropsFromElement(element) {
filterable: parseBoolean(element.dataset.filterable),
paginatable: parseBoolean(element.dataset.paginatable),
optionalStages: JSON.parse(element.dataset.optionalStages),
+ cancelable: Boolean(element.dataset.cancelPath),
};
}
+const defaultClient = createDefaultClient();
+
+const apolloProvider = new VueApollo({
+ defaultClient,
+});
+
export default function mountImportProjectsTable(mountElement) {
if (!mountElement) return undefined;
@@ -55,6 +66,7 @@ export default function mountImportProjectsTable(mountElement) {
return new Vue({
el: mountElement,
store,
+ apolloProvider,
render(createElement) {
return createElement(ImportProjectsTable, { props });
},
diff --git a/app/assets/javascripts/import_entities/import_projects/store/actions.js b/app/assets/javascripts/import_entities/import_projects/store/actions.js
index a30c14f9d28..e0db585eb3e 100644
--- a/app/assets/javascripts/import_entities/import_projects/store/actions.js
+++ b/app/assets/javascripts/import_entities/import_projects/store/actions.js
@@ -1,20 +1,22 @@
import Visibility from 'visibilityjs';
+import _ from 'lodash';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
-import httpStatusCodes from '~/lib/utils/http_status';
+import { HTTP_STATUS_TOO_MANY_REQUESTS } from '~/lib/utils/http_status';
import Poll from '~/lib/utils/poll';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
import { visitUrl, objectToQuery } from '~/lib/utils/url_utility';
import { s__, sprintf } from '~/locale';
import { isProjectImportable } from '../utils';
+import { PROVIDERS } from '../../constants';
import * as types from './mutation_types';
let eTagPoll;
const hasRedirectInError = (e) => e?.response?.data?.error?.redirect;
const redirectToUrlInError = (e) => visitUrl(e.response.data.error.redirect);
-const tooManyRequests = (e) => e.response.status === httpStatusCodes.TOO_MANY_REQUESTS;
+const tooManyRequests = (e) => e.response.status === HTTP_STATUS_TOO_MANY_REQUESTS;
const pathWithParams = ({ path, ...params }) => {
const filteredParams = Object.fromEntries(
Object.entries(params).filter(([, value]) => value !== ''),
@@ -22,6 +24,24 @@ const pathWithParams = ({ path, ...params }) => {
const queryString = objectToQuery(filteredParams);
return queryString ? `${path}?${queryString}` : path;
};
+const commitPaginationData = ({ state, commit, data }) => {
+ const cursorsGitHubResponse = !_.isEmpty(data.pageInfo || {});
+
+ if (state.provider === PROVIDERS.GITHUB && cursorsGitHubResponse) {
+ commit(types.SET_PAGE_CURSORS, data.pageInfo);
+ } else {
+ const nextPage = state.pageInfo.page + 1;
+ commit(types.SET_PAGE, nextPage);
+ }
+};
+const paginationParams = ({ state }) => {
+ if (state.provider === PROVIDERS.GITHUB && state.pageInfo.endCursor) {
+ return { after: state.pageInfo.endCursor };
+ }
+
+ const nextPage = state.pageInfo.page + 1;
+ return { page: nextPage === 1 ? '' : nextPage.toString() };
+};
const isRequired = () => {
// eslint-disable-next-line @gitlab/require-i18n-strings
@@ -55,7 +75,6 @@ const importAll = ({ state, dispatch }, config = {}) => {
};
const fetchReposFactory = ({ reposPath = isRequired() }) => ({ state, commit }) => {
- const nextPage = state.pageInfo.page + 1;
commit(types.REQUEST_REPOS);
const { provider, filter } = state;
@@ -65,12 +84,13 @@ const fetchReposFactory = ({ reposPath = isRequired() }) => ({ state, commit })
pathWithParams({
path: reposPath,
filter: filter ?? '',
- page: nextPage === 1 ? '' : nextPage.toString(),
+ ...paginationParams({ state }),
}),
)
.then(({ data }) => {
- commit(types.SET_PAGE, nextPage);
- commit(types.RECEIVE_REPOS_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true }));
+ const camelData = convertObjectPropsToCamelCase(data, { deep: true });
+ commitPaginationData({ state, commit, data: camelData });
+ commit(types.RECEIVE_REPOS_SUCCESS, camelData);
})
.catch((e) => {
if (hasRedirectInError(e)) {
@@ -139,6 +159,42 @@ const fetchImportFactory = (importPath = isRequired()) => (
});
};
+export const cancelImportFactory = (cancelImportPath) => ({ state, commit }, { repoId }) => {
+ const existingRepo = state.repositories.find((r) => r.importSource.id === repoId);
+
+ if (!existingRepo?.importedProject) {
+ throw new Error(`Attempting to cancel project which is not started: ${repoId}`);
+ }
+
+ const { id } = existingRepo.importedProject;
+
+ return axios
+ .post(cancelImportPath, {
+ project_id: id,
+ })
+ .then(() => {
+ commit(types.CANCEL_IMPORT_SUCCESS, {
+ repoId,
+ });
+ })
+ .catch((e) => {
+ const serverErrorMessage = e?.response?.data?.errors;
+ const flashMessage = serverErrorMessage
+ ? sprintf(
+ s__('ImportProjects|Cancelling project import failed: %{reason}'),
+ {
+ reason: serverErrorMessage,
+ },
+ false,
+ )
+ : s__('ImportProjects|Cancelling project import failed');
+
+ createAlert({
+ message: flashMessage,
+ });
+ });
+};
+
export const fetchJobsFactory = (jobsPath = isRequired()) => ({ state, commit, dispatch }) => {
if (eTagPoll) {
stopJobsPolling();
@@ -176,22 +232,6 @@ export const fetchJobsFactory = (jobsPath = isRequired()) => ({ state, commit, d
});
};
-const fetchNamespacesFactory = (namespacesPath = isRequired()) => ({ commit }) => {
- commit(types.REQUEST_NAMESPACES);
- axios
- .get(namespacesPath)
- .then(({ data }) =>
- commit(types.RECEIVE_NAMESPACES_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true })),
- )
- .catch(() => {
- createAlert({
- message: s__('ImportProjects|Requesting namespaces failed'),
- });
-
- commit(types.RECEIVE_NAMESPACES_ERROR);
- });
-};
-
const setFilter = ({ commit, dispatch }, filter) => {
commit(types.SET_FILTER, filter);
@@ -207,6 +247,6 @@ export default ({ endpoints = isRequired() }) => ({
importAll,
fetchRepos: fetchReposFactory({ reposPath: endpoints.reposPath }),
fetchImport: fetchImportFactory(endpoints.importPath),
+ cancelImport: cancelImportFactory(endpoints.cancelPath),
fetchJobs: fetchJobsFactory(endpoints.jobsPath),
- fetchNamespaces: fetchNamespacesFactory(endpoints.namespacesPath),
});
diff --git a/app/assets/javascripts/import_entities/import_projects/store/getters.js b/app/assets/javascripts/import_entities/import_projects/store/getters.js
index ef01a67ec94..31ddffd4eb4 100644
--- a/app/assets/javascripts/import_entities/import_projects/store/getters.js
+++ b/app/assets/javascripts/import_entities/import_projects/store/getters.js
@@ -1,7 +1,5 @@
import { isProjectImportable, isIncompatible, isImporting } from '../utils';
-export const isLoading = (state) => state.isLoadingRepos || state.isLoadingNamespaces;
-
export const importingRepoCount = (state) => state.repositories.filter(isImporting).length;
export const isImportingAnyRepo = (state) => state.repositories.some(isImporting);
diff --git a/app/assets/javascripts/import_entities/import_projects/store/mutation_types.js b/app/assets/javascripts/import_entities/import_projects/store/mutation_types.js
index 6adf5e59cff..74832a03ac1 100644
--- a/app/assets/javascripts/import_entities/import_projects/store/mutation_types.js
+++ b/app/assets/javascripts/import_entities/import_projects/store/mutation_types.js
@@ -2,14 +2,12 @@ export const REQUEST_REPOS = 'REQUEST_REPOS';
export const RECEIVE_REPOS_SUCCESS = 'RECEIVE_REPOS_SUCCESS';
export const RECEIVE_REPOS_ERROR = 'RECEIVE_REPOS_ERROR';
-export const REQUEST_NAMESPACES = 'REQUEST_NAMESPACES';
-export const RECEIVE_NAMESPACES_SUCCESS = 'RECEIVE_NAMESPACES_SUCCESS';
-export const RECEIVE_NAMESPACES_ERROR = 'RECEIVE_NAMESPACES_ERROR';
-
export const REQUEST_IMPORT = 'REQUEST_IMPORT';
export const RECEIVE_IMPORT_SUCCESS = 'RECEIVE_IMPORT_SUCCESS';
export const RECEIVE_IMPORT_ERROR = 'RECEIVE_IMPORT_ERROR';
+export const CANCEL_IMPORT_SUCCESS = 'CANCEL_IMPORT_SUCCESS';
+
export const RECEIVE_JOBS_SUCCESS = 'RECEIVE_JOBS_SUCCESS';
export const SET_FILTER = 'SET_FILTER';
@@ -18,4 +16,4 @@ export const SET_IMPORT_TARGET = 'SET_IMPORT_TARGET';
export const SET_PAGE = 'SET_PAGE';
-export const SET_PAGE_INFO = 'SET_PAGE_INFO';
+export const SET_PAGE_CURSORS = 'SET_PAGE_CURSORS';
diff --git a/app/assets/javascripts/import_entities/import_projects/store/mutations.js b/app/assets/javascripts/import_entities/import_projects/store/mutations.js
index 163a19976de..8b2e0364d7a 100644
--- a/app/assets/javascripts/import_entities/import_projects/store/mutations.js
+++ b/app/assets/javascripts/import_entities/import_projects/store/mutations.js
@@ -36,7 +36,12 @@ export default {
[types.SET_FILTER](state, filter) {
state.filter = filter;
state.repositories = [];
- state.pageInfo.page = 0;
+ state.pageInfo = {
+ page: 0,
+ startCursor: null,
+ endCursor: null,
+ hasNextPage: true,
+ };
},
[types.REQUEST_REPOS](state) {
@@ -51,7 +56,9 @@ export default {
// https://gitlab.com/gitlab-org/gitlab/-/issues/27370#note_379034091
const newImportedProjects = processLegacyEntries({
- newRepositories: repositories.importedProjects,
+ newRepositories: repositories.importedProjects.filter(
+ (p) => p.importStatus !== STATUSES.CANCELED,
+ ),
existingRepositories: state.repositories,
factory: makeNewImportedProject,
});
@@ -122,17 +129,9 @@ export default {
});
},
- [types.REQUEST_NAMESPACES](state) {
- state.isLoadingNamespaces = true;
- },
-
- [types.RECEIVE_NAMESPACES_SUCCESS](state, namespaces) {
- state.isLoadingNamespaces = false;
- state.namespaces = namespaces;
- },
-
- [types.RECEIVE_NAMESPACES_ERROR](state) {
- state.isLoadingNamespaces = false;
+ [types.CANCEL_IMPORT_SUCCESS](state, { repoId }) {
+ const existingRepo = state.repositories.find((r) => r.importSource.id === repoId);
+ existingRepo.importedProject.importStatus = STATUSES.CANCELED;
},
[types.SET_IMPORT_TARGET](state, { repoId, importTarget }) {
@@ -151,4 +150,9 @@ export default {
[types.SET_PAGE](state, page) {
state.pageInfo.page = page;
},
+
+ [types.SET_PAGE_CURSORS](state, pageInfo) {
+ const { startCursor, endCursor, hasNextPage } = pageInfo;
+ state.pageInfo = { ...state.pageInfo, startCursor, endCursor, hasNextPage };
+ },
};
diff --git a/app/assets/javascripts/import_entities/import_projects/store/state.js b/app/assets/javascripts/import_entities/import_projects/store/state.js
index ecd93561d52..c384848f0a0 100644
--- a/app/assets/javascripts/import_entities/import_projects/store/state.js
+++ b/app/assets/javascripts/import_entities/import_projects/store/state.js
@@ -1,13 +1,14 @@
export default () => ({
provider: '',
repositories: [],
- namespaces: [],
customImportTargets: {},
isLoadingRepos: false,
- isLoadingNamespaces: false,
ciCdOnly: false,
filter: '',
pageInfo: {
page: 0,
+ startCursor: null,
+ endCursor: null,
+ hasNextPage: true,
},
});
diff --git a/app/assets/javascripts/import_entities/import_projects/utils.js b/app/assets/javascripts/import_entities/import_projects/utils.js
index 38bd529321a..c4c9e544c1e 100644
--- a/app/assets/javascripts/import_entities/import_projects/utils.js
+++ b/app/assets/javascripts/import_entities/import_projects/utils.js
@@ -9,7 +9,10 @@ export function getImportStatus(project) {
}
export function isProjectImportable(project) {
- return !isIncompatible(project) && getImportStatus(project) === STATUSES.NONE;
+ return (
+ !isIncompatible(project) &&
+ [STATUSES.NONE, STATUSES.CANCELED].includes(getImportStatus(project))
+ );
}
export function isImporting(repo) {
diff --git a/app/assets/javascripts/incidents/components/incidents_list.vue b/app/assets/javascripts/incidents/components/incidents_list.vue
index dbd2225167a..14ab7b2dc1e 100644
--- a/app/assets/javascripts/incidents/components/incidents_list.vue
+++ b/app/assets/javascripts/incidents/components/incidents_list.vue
@@ -14,7 +14,7 @@ import {
import { isValidSlaDueAt } from 'ee_else_ce/vue_shared/components/incidents/utils';
import { visitUrl, mergeUrlParams, joinPaths } from '~/lib/utils/url_utility';
import { s__, n__ } from '~/locale';
-import { INCIDENT_SEVERITY } from '~/sidebar/components/severity/constants';
+import { INCIDENT_SEVERITY } from '~/sidebar/constants';
import SeverityToken from '~/sidebar/components/severity/severity.vue';
import Tracking from '~/tracking';
import {
diff --git a/app/assets/javascripts/incidents_settings/incidents_settings_service.js b/app/assets/javascripts/incidents_settings/incidents_settings_service.js
index 93baa54956a..d3850114350 100644
--- a/app/assets/javascripts/incidents_settings/incidents_settings_service.js
+++ b/app/assets/javascripts/incidents_settings/incidents_settings_service.js
@@ -1,4 +1,4 @@
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { refreshCurrentPage } from '~/lib/utils/url_utility';
import { ERROR_MSG } from './constants';
@@ -22,7 +22,7 @@ export default class IncidentsSettingsService {
.catch(({ response }) => {
const message = response?.data?.message || '';
- createFlash({
+ createAlert({
message: `${ERROR_MSG} ${message}`,
});
});
diff --git a/app/assets/javascripts/integrations/edit/components/dynamic_field.vue b/app/assets/javascripts/integrations/edit/components/dynamic_field.vue
index fe687ea9767..904e5639cac 100644
--- a/app/assets/javascripts/integrations/edit/components/dynamic_field.vue
+++ b/app/assets/javascripts/integrations/edit/components/dynamic_field.vue
@@ -1,14 +1,8 @@
<script>
-import {
- GlFormGroup,
- GlFormCheckbox,
- GlFormInput,
- GlFormSelect,
- GlFormTextarea,
- GlSafeHtmlDirective as SafeHtml,
-} from '@gitlab/ui';
+import { GlFormGroup, GlFormCheckbox, GlFormInput, GlFormSelect, GlFormTextarea } from '@gitlab/ui';
import { capitalize, lowerCase, isEmpty } from 'lodash';
import { mapGetters } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
export default {
name: 'DynamicField',
@@ -80,7 +74,7 @@ export default {
};
},
computed: {
- ...mapGetters(['isInheriting']),
+ ...mapGetters(['isInheriting', 'propsSource']),
isCheckbox() {
return this.type === 'checkbox';
},
@@ -122,11 +116,18 @@ export default {
name: this.fieldName,
state: this.valid,
readonly: this.isInheriting,
+ disabled: this.isDisabled,
};
},
valid() {
return !this.required || !isEmpty(this.model) || this.isNonEmptyPassword || !this.isValidated;
},
+ isInheritingOrDisabled() {
+ return this.isInheriting || this.isDisabled;
+ },
+ isDisabled() {
+ return !this.propsSource.editable;
+ },
},
created() {
if (this.isNonEmptyPassword) {
@@ -149,7 +150,7 @@ export default {
<template v-if="isCheckbox">
<input :name="fieldName" type="hidden" :value="model || false" />
- <gl-form-checkbox :id="fieldId" v-model="model" :disabled="isInheriting">
+ <gl-form-checkbox :id="fieldId" v-model="model" :disabled="isInheritingOrDisabled">
{{ checkboxLabel || humanizedTitle }}
<template #help>
<span v-safe-html="help"></span>
@@ -158,7 +159,12 @@ export default {
</template>
<template v-else-if="isSelect">
<input type="hidden" :name="fieldName" :value="model" />
- <gl-form-select :id="fieldId" v-model="model" :options="options" :disabled="isInheriting" />
+ <gl-form-select
+ :id="fieldId"
+ v-model="model"
+ :options="options"
+ :disabled="isInheritingOrDisabled"
+ />
</template>
<gl-form-textarea
v-else-if="isTextarea"
diff --git a/app/assets/javascripts/integrations/edit/components/integration_form.vue b/app/assets/javascripts/integrations/edit/components/integration_form.vue
index 4bf2b8d4468..d86e6326f64 100644
--- a/app/assets/javascripts/integrations/edit/components/integration_form.vue
+++ b/app/assets/javascripts/integrations/edit/components/integration_form.vue
@@ -1,22 +1,15 @@
<script>
-import {
- GlAlert,
- GlBadge,
- GlButton,
- GlModalDirective,
- GlSafeHtmlDirective as SafeHtml,
- GlForm,
-} from '@gitlab/ui';
+import { GlAlert, GlBadge, GlButton, GlForm } from '@gitlab/ui';
import axios from 'axios';
import * as Sentry from '@sentry/browser';
import { mapState, mapActions, mapGetters } from 'vuex';
import { s__ } from '~/locale';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import {
I18N_FETCH_TEST_SETTINGS_DEFAULT_ERROR_MESSAGE,
I18N_DEFAULT_ERROR_MESSAGE,
I18N_SUCCESSFUL_CONNECTION_MESSAGE,
INTEGRATION_FORM_TYPE_SLACK,
- integrationLevels,
integrationFormSectionComponents,
billingPlanNames,
} from '~/integrations/constants';
@@ -25,11 +18,10 @@ import csrf from '~/lib/utils/csrf';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { testIntegrationSettings } from '../api';
import ActiveCheckbox from './active_checkbox.vue';
-import ConfirmationModal from './confirmation_modal.vue';
import DynamicField from './dynamic_field.vue';
import OverrideDropdown from './override_dropdown.vue';
-import ResetConfirmationModal from './reset_confirmation_modal.vue';
import TriggerFields from './trigger_fields.vue';
+import IntegrationFormActions from './integration_form_actions.vue';
export default {
name: 'IntegrationForm',
@@ -38,8 +30,7 @@ export default {
ActiveCheckbox,
TriggerFields,
DynamicField,
- ConfirmationModal,
- ResetConfirmationModal,
+ IntegrationFormActions,
IntegrationSectionConfiguration: () =>
import(
/* webpackChunkName: 'integrationSectionConfiguration' */ '~/integrations/edit/components/sections/configuration.vue'
@@ -66,7 +57,6 @@ export default {
GlForm,
},
directives: {
- GlModal: GlModalDirective,
SafeHtml,
},
mixins: [glFeatureFlagsMixin()],
@@ -78,10 +68,10 @@ export default {
data() {
return {
integrationActive: false,
- isTesting: false,
+ isValidated: false,
isSaving: false,
+ isTesting: false,
isResetting: false,
- isValidated: false,
};
},
computed: {
@@ -90,21 +80,6 @@ export default {
isEditable() {
return this.propsSource.editable;
},
- isInstanceOrGroupLevel() {
- return (
- this.customState.integrationLevel === integrationLevels.INSTANCE ||
- this.customState.integrationLevel === integrationLevels.GROUP
- );
- },
- showResetButton() {
- return this.isInstanceOrGroupLevel && this.propsSource.resetPath;
- },
- showTestButton() {
- return this.propsSource.canTest;
- },
- disableButtons() {
- return Boolean(this.isSaving || this.isResetting || this.isTesting);
- },
hasSections() {
if (this.hasSlackNotificationsDisabled) {
return false;
@@ -134,6 +109,14 @@ export default {
}
return !this.hasSections && this.helpHtml;
},
+ shouldUpgradeSlack() {
+ return (
+ this.isSlackIntegration &&
+ this.glFeatures.integrationSlackAppNotifications &&
+ this.customState.shouldUpgradeSlack &&
+ (this.hasFieldsWithoutSection || this.hasSections)
+ );
+ },
},
methods: {
...mapActions(['setOverride', 'requestJiraIssueTypes']),
@@ -148,7 +131,6 @@ export default {
},
onSaveClick() {
this.isSaving = true;
-
if (this.integrationActive && !this.form().checkValidity()) {
this.isSaving = false;
this.setIsValidated();
@@ -194,7 +176,6 @@ export default {
},
onResetClick() {
this.isResetting = true;
-
return axios
.post(this.propsSource.resetPath)
.then(() => {
@@ -227,7 +208,10 @@ export default {
billingPlanNames,
slackUpgradeInfo: {
title: s__(
- `SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app`,
+ `SlackIntegration|Update to the latest version of GitLab for Slack to get notifications`,
+ ),
+ text: s__(
+ `SlackIntegration|Update to the latest version to receive notifications from GitLab.`,
),
btnText: s__('SlackIntegration|Update to the latest version'),
},
@@ -284,16 +268,18 @@ export default {
</div>
</section>
+ <div v-if="shouldUpgradeSlack" class="gl-border-t">
+ <gl-alert
+ :dismissible="false"
+ :title="$options.slackUpgradeInfo.title"
+ :primary-button-link="customState.upgradeSlackUrl"
+ :primary-button-text="$options.slackUpgradeInfo.btnText"
+ class="gl-mb-8 gl-mt-5"
+ >{{ $options.slackUpgradeInfo.text }}</gl-alert
+ >
+ </div>
+
<template v-if="hasSections">
- <div v-if="customState.shouldUpgradeSlack && isSlackIntegration" class="gl-border-t">
- <gl-alert
- :title="$options.slackUpgradeInfo.title"
- variant="warning"
- :primary-button-link="customState.upgradeSlackUrl"
- :primary-button-text="$options.slackUpgradeInfo.btnText"
- class="gl-mb-8 gl-mt-5"
- />
- </div>
<div
v-for="(section, index) in customState.sections"
:key="section.type"
@@ -344,71 +330,16 @@ export default {
</div>
</section>
- <section v-if="isEditable" :class="!hasSections && 'gl-lg-display-flex gl-justify-content-end'">
- <div :class="!hasSections && 'gl-flex-basis-two-thirds'">
- <div
- class="footer-block row-content-block gl-lg-display-flex gl-justify-content-space-between"
- >
- <div>
- <template v-if="isInstanceOrGroupLevel">
- <gl-button
- v-gl-modal.confirmSaveIntegration
- category="primary"
- variant="confirm"
- :loading="isSaving"
- :disabled="disableButtons"
- data-testid="save-button-instance-group"
- data-qa-selector="save_changes_button"
- >
- {{ __('Save changes') }}
- </gl-button>
- <confirmation-modal @submit="onSaveClick" />
- </template>
- <gl-button
- v-else
- category="primary"
- variant="confirm"
- type="submit"
- :loading="isSaving"
- :disabled="disableButtons"
- data-testid="save-button"
- data-qa-selector="save_changes_button"
- @click.prevent="onSaveClick"
- >
- {{ __('Save changes') }}
- </gl-button>
-
- <gl-button
- v-if="showTestButton"
- category="secondary"
- variant="confirm"
- :loading="isTesting"
- :disabled="disableButtons"
- data-testid="test-button"
- @click.prevent="onTestClick"
- >
- {{ __('Test settings') }}
- </gl-button>
-
- <gl-button :href="propsSource.cancelPath">{{ __('Cancel') }}</gl-button>
- </div>
-
- <template v-if="showResetButton">
- <gl-button
- v-gl-modal.confirmResetIntegration
- category="tertiary"
- variant="danger"
- :loading="isResetting"
- :disabled="disableButtons"
- data-testid="reset-button"
- >
- {{ __('Reset') }}
- </gl-button>
-
- <reset-confirmation-modal @reset="onResetClick" />
- </template>
- </div>
- </div>
- </section>
+ <integration-form-actions
+ v-if="isEditable"
+ :has-sections="hasSections"
+ :class="{ 'gl-lg-display-flex gl-justify-content-end': !hasSections }"
+ :is-saving="isSaving"
+ :is-testing="isTesting"
+ :is-resetting="isResetting"
+ @save="onSaveClick"
+ @test="onTestClick"
+ @reset="onResetClick"
+ />
</gl-form>
</template>
diff --git a/app/assets/javascripts/integrations/edit/components/integration_form_actions.vue b/app/assets/javascripts/integrations/edit/components/integration_form_actions.vue
new file mode 100644
index 00000000000..e5ad5149cf7
--- /dev/null
+++ b/app/assets/javascripts/integrations/edit/components/integration_form_actions.vue
@@ -0,0 +1,143 @@
+<script>
+import { GlButton, GlModalDirective } from '@gitlab/ui';
+import { mapState, mapGetters } from 'vuex';
+import { integrationLevels } from '~/integrations/constants';
+import ConfirmationModal from './confirmation_modal.vue';
+import ResetConfirmationModal from './reset_confirmation_modal.vue';
+
+export default {
+ name: 'IntegrationFormActions',
+ components: {
+ GlButton,
+ ConfirmationModal,
+ ResetConfirmationModal,
+ },
+ directives: {
+ GlModal: GlModalDirective,
+ },
+ props: {
+ hasSections: {
+ type: Boolean,
+ required: true,
+ },
+ isSaving: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ isTesting: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ isResetting: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ ...mapGetters(['propsSource']),
+ ...mapState(['customState']),
+ isInstanceOrGroupLevel() {
+ return (
+ this.customState.integrationLevel === integrationLevels.INSTANCE ||
+ this.customState.integrationLevel === integrationLevels.GROUP
+ );
+ },
+ showResetButton() {
+ return this.isInstanceOrGroupLevel && this.propsSource.resetPath;
+ },
+ showTestButton() {
+ return this.propsSource.canTest;
+ },
+ disableButtons() {
+ return Boolean(this.isSaving || this.isResetting || this.isTesting);
+ },
+ },
+ methods: {
+ onSaveClick() {
+ this.$emit('save');
+ },
+ onTestClick() {
+ this.$emit('test');
+ },
+ onResetClick() {
+ this.$emit('reset');
+ },
+ },
+};
+</script>
+<template>
+ <section>
+ <div :class="{ 'gl-flex-basis-two-thirds': !hasSections }">
+ <div
+ class="footer-block row-content-block gl-lg-display-flex gl-justify-content-space-between"
+ >
+ <div>
+ <template v-if="isInstanceOrGroupLevel">
+ <gl-button
+ v-gl-modal.confirmSaveIntegration
+ category="primary"
+ variant="confirm"
+ :loading="isSaving"
+ :disabled="disableButtons"
+ data-testid="save-button"
+ data-qa-selector="save_changes_button"
+ >
+ {{ __('Save changes') }}
+ </gl-button>
+ <confirmation-modal @submit="onSaveClick" />
+ </template>
+ <gl-button
+ v-else
+ category="primary"
+ variant="confirm"
+ type="submit"
+ :loading="isSaving"
+ :disabled="disableButtons"
+ data-testid="save-button"
+ data-qa-selector="save_changes_button"
+ @click.prevent="onSaveClick"
+ >
+ {{ __('Save changes') }}
+ </gl-button>
+
+ <gl-button
+ v-if="showTestButton"
+ category="secondary"
+ variant="confirm"
+ :loading="isTesting"
+ :disabled="disableButtons"
+ data-testid="test-button"
+ @click.prevent="onTestClick"
+ >
+ {{ __('Test settings') }}
+ </gl-button>
+
+ <gl-button
+ :href="propsSource.cancelPath"
+ data-testid="cancel-button"
+ :disabled="disableButtons"
+ >{{ __('Cancel') }}</gl-button
+ >
+ </div>
+
+ <template v-if="showResetButton">
+ <gl-button
+ v-gl-modal.confirmResetIntegration
+ category="tertiary"
+ variant="danger"
+ :loading="isResetting"
+ :disabled="disableButtons"
+ data-testid="reset-button"
+ >
+ {{ __('Reset') }}
+ </gl-button>
+
+ <reset-confirmation-modal @reset="onResetClick" />
+ </template>
+ </div>
+ </div>
+ </section>
+</template>
diff --git a/app/assets/javascripts/integrations/edit/index.js b/app/assets/javascripts/integrations/edit/index.js
index f15ad5e052e..b53bcd50f16 100644
--- a/app/assets/javascripts/integrations/edit/index.js
+++ b/app/assets/javascripts/integrations/edit/index.js
@@ -108,6 +108,7 @@ export default function initIntegrationSettingsForm() {
const initialState = {
defaultState: null,
customState: customSettingsProps,
+ editable: customSettingsProps.editable && !customSettingsProps.shouldUpgradeSlack,
};
if (defaultSettingsEl) {
initialState.defaultState = Object.freeze(parseDatasetToProps(defaultSettingsEl.dataset));
diff --git a/app/assets/javascripts/invite_members/components/import_project_members_modal.vue b/app/assets/javascripts/invite_members/components/import_project_members_modal.vue
index 31b7fd4cc42..b4e9a3a1559 100644
--- a/app/assets/javascripts/invite_members/components/import_project_members_modal.vue
+++ b/app/assets/javascripts/invite_members/components/import_project_members_modal.vue
@@ -5,6 +5,10 @@ import { importProjectMembers } from '~/api/projects_api';
import { BV_SHOW_MODAL } from '~/lib/utils/constants';
import { s__, __, sprintf } from '~/locale';
import eventHub from '../event_hub';
+import {
+ displaySuccessfulInvitationAlert,
+ reloadOnInvitationSuccess,
+} from '../utils/trigger_successful_invite_alert';
import ProjectSelect from './project_select.vue';
export default {
@@ -24,6 +28,11 @@ export default {
type: String,
required: true,
},
+ reloadPageOnSubmit: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -59,6 +68,10 @@ export default {
},
},
mounted() {
+ if (this.reloadPageOnSubmit) {
+ displaySuccessfulInvitationAlert();
+ }
+
eventHub.$on('openProjectMembersModal', () => {
this.openModal();
});
@@ -74,16 +87,22 @@ export default {
submitImport() {
this.isLoading = true;
return importProjectMembers(this.projectId, this.projectToBeImported.id)
- .then(this.showToastMessage)
+ .then(this.onInviteSuccess)
.catch(this.showErrorAlert)
.finally(() => {
this.isLoading = false;
this.projectToBeImported = {};
});
},
+ onInviteSuccess() {
+ if (this.reloadPageOnSubmit) {
+ reloadOnInvitationSuccess();
+ } else {
+ this.showToastMessage();
+ }
+ },
showToastMessage() {
this.$toast.show(this.$options.i18n.successMessage, this.$options.toastOptions);
-
this.closeModal();
},
showErrorAlert() {
diff --git a/app/assets/javascripts/invite_members/components/invite_group_notification.vue b/app/assets/javascripts/invite_members/components/invite_group_notification.vue
new file mode 100644
index 00000000000..767675cc64c
--- /dev/null
+++ b/app/assets/javascripts/invite_members/components/invite_group_notification.vue
@@ -0,0 +1,37 @@
+<script>
+import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
+import { GROUP_MODAL_ALERT_BODY } from '../constants';
+
+const SHARE_GROUP_LINK =
+ 'https://docs.gitlab.com/ee/user/group/manage.html#share-a-group-with-another-group';
+
+export default {
+ SHARE_GROUP_LINK,
+ name: 'InviteGroupNotification',
+ components: { GlAlert, GlSprintf, GlLink },
+ inject: ['freeUsersLimit'],
+ props: {
+ name: {
+ type: String,
+ required: true,
+ },
+ },
+ i18n: {
+ body: GROUP_MODAL_ALERT_BODY,
+ },
+};
+</script>
+
+<template>
+ <gl-alert variant="warning" :dismissible="false">
+ <gl-sprintf :message="$options.i18n.body">
+ <template #link="{ content }">
+ <gl-link :href="$options.SHARE_GROUP_LINK" target="_blank" class="gl-label-link">{{
+ content
+ }}</gl-link>
+ </template>
+
+ <template #count>{{ freeUsersLimit }}</template>
+ </gl-sprintf>
+ </gl-alert>
+</template>
diff --git a/app/assets/javascripts/invite_members/components/invite_groups_modal.vue b/app/assets/javascripts/invite_members/components/invite_groups_modal.vue
index 2ad4bb1a11a..3be3b9df747 100644
--- a/app/assets/javascripts/invite_members/components/invite_groups_modal.vue
+++ b/app/assets/javascripts/invite_members/components/invite_groups_modal.vue
@@ -6,13 +6,19 @@ import InviteModalBase from 'ee_else_ce/invite_members/components/invite_modal_b
import { GROUP_FILTERS, GROUP_MODAL_LABELS } from '../constants';
import eventHub from '../event_hub';
import { getInvalidFeedbackMessage } from '../utils/get_invalid_feedback_message';
+import {
+ displaySuccessfulInvitationAlert,
+ reloadOnInvitationSuccess,
+} from '../utils/trigger_successful_invite_alert';
import GroupSelect from './group_select.vue';
+import InviteGroupNotification from './invite_group_notification.vue';
export default {
name: 'InviteMembersModal',
components: {
GroupSelect,
InviteModalBase,
+ InviteGroupNotification,
},
props: {
id: {
@@ -31,6 +37,10 @@ export default {
type: String,
required: true,
},
+ fullPath: {
+ type: String,
+ required: true,
+ },
accessLevels: {
type: Object,
required: true,
@@ -57,6 +67,15 @@ export default {
type: Array,
required: true,
},
+ freeUserCapEnabled: {
+ type: Boolean,
+ required: true,
+ },
+ reloadPageOnSubmit: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -85,6 +104,10 @@ export default {
},
},
mounted() {
+ if (this.reloadPageOnSubmit) {
+ displaySuccessfulInvitationAlert();
+ }
+
eventHub.$on('openGroupModal', () => {
this.openModal();
});
@@ -114,7 +137,7 @@ export default {
expires_at: expiresAt,
})
.then(() => {
- this.showSuccessMessage();
+ this.onInviteSuccess();
})
.catch((e) => {
this.showInvalidFeedbackMessage(e);
@@ -128,6 +151,13 @@ export default {
this.isLoading = false;
this.groupToBeSharedWith = {};
},
+ onInviteSuccess() {
+ if (this.reloadPageOnSubmit) {
+ reloadOnInvitationSuccess();
+ } else {
+ this.showSuccessMessage();
+ }
+ },
showSuccessMessage() {
this.$toast.show(this.$options.labels.toastMessageSuccessful, this.toastOptions);
this.closeModal();
@@ -155,9 +185,14 @@ export default {
:root-group-id="rootId"
:invalid-feedback-message="invalidFeedbackMessage"
:is-loading="isLoading"
+ :full-path="fullPath"
@reset="resetFields"
@submit="sendInvite"
>
+ <template #alert>
+ <invite-group-notification v-if="freeUserCapEnabled" :name="name" />
+ </template>
+
<template #select>
<group-select
v-model="groupToBeSharedWith"
diff --git a/app/assets/javascripts/invite_members/components/invite_members_modal.vue b/app/assets/javascripts/invite_members/components/invite_members_modal.vue
index f61e822bf7e..fbb547c28ff 100644
--- a/app/assets/javascripts/invite_members/components/invite_members_modal.vue
+++ b/app/assets/javascripts/invite_members/components/invite_members_modal.vue
@@ -29,6 +29,10 @@ import eventHub from '../event_hub';
import { responseFromSuccess } from '../utils/response_message_parser';
import { memberName } from '../utils/member_utils';
import { getInvalidFeedbackMessage } from '../utils/get_invalid_feedback_message';
+import {
+ displaySuccessfulInvitationAlert,
+ reloadOnInvitationSuccess,
+} from '../utils/trigger_successful_invite_alert';
import ModalConfetti from './confetti.vue';
import MembersTokenSelect from './members_token_select.vue';
import UserLimitNotification from './user_limit_notification.vue';
@@ -98,11 +102,20 @@ export default {
type: Array,
required: true,
},
+ fullPath: {
+ type: String,
+ required: true,
+ },
usersLimitDataset: {
type: Object,
required: false,
default: () => ({}),
},
+ reloadPageOnSubmit: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -119,7 +132,7 @@ export default {
selectedAccessLevel: undefined,
errorsLimit: 2,
isErrorsSectionExpanded: false,
- emptyInvitesError: false,
+ shouldShowEmptyInvitesAlert: false,
};
},
computed: {
@@ -204,12 +217,15 @@ export default {
count: this.errorsExpanded.length,
});
},
+ formGroupDescription() {
+ return this.invalidFeedbackMessage ? null : this.$options.labels.placeHolder;
+ },
},
watch: {
isEmptyInvites: {
handler(updatedValue) {
// nothing to do if the invites are **still** empty and the emptyInvites were never set from submit
- if (!updatedValue && !this.emptyInvitesError) {
+ if (!updatedValue && !this.shouldShowEmptyInvitesAlert) {
return;
}
@@ -218,6 +234,10 @@ export default {
},
},
mounted() {
+ if (this.reloadPageOnSubmit) {
+ displaySuccessfulInvitationAlert();
+ }
+
eventHub.$on('openModal', (options) => {
this.openModal(options);
if (this.isOnLearnGitlab) {
@@ -258,16 +278,17 @@ export default {
const tracking = new ExperimentTracking(experimentName);
tracking.event(eventName);
},
- showEmptyInvitesError() {
- this.invalidFeedbackMessage = this.$options.labels.emptyInvitesErrorText;
- this.emptyInvitesError = true;
+ showEmptyInvitesAlert() {
+ this.invalidFeedbackMessage = this.$options.labels.placeHolder;
+ this.shouldShowEmptyInvitesAlert = true;
+ this.$refs.alerts.focus();
},
sendInvite({ accessLevel, expiresAt }) {
this.isLoading = true;
this.clearValidation();
if (!this.isEmptyInvites) {
- this.showEmptyInvitesError();
+ this.showEmptyInvitesAlert();
return;
}
@@ -298,7 +319,7 @@ export default {
if (error) {
this.showMemberErrors(message);
} else {
- this.showSuccessMessage();
+ this.onInviteSuccess();
}
})
.catch((e) => this.showInvalidFeedbackMessage(e))
@@ -308,6 +329,7 @@ export default {
},
showMemberErrors(message) {
this.invalidMembers = message;
+ this.$refs.alerts.focus();
},
tokenName(username) {
// initial token creation hits this and nothing is found... so safe navigation
@@ -322,6 +344,7 @@ export default {
resetFields() {
this.clearValidation();
this.isLoading = false;
+ this.shouldShowEmptyInvitesAlert = false;
this.newUsersToInvite = [];
this.selectedTasksToBeDone = [];
[this.selectedTaskProject] = this.projects;
@@ -329,6 +352,13 @@ export default {
changeSelectedTaskProject(project) {
this.selectedTaskProject = project;
},
+ onInviteSuccess() {
+ if (this.reloadPageOnSubmit) {
+ reloadOnInvitationSuccess();
+ } else {
+ this.showSuccessMessage();
+ }
+ },
showSuccessMessage() {
if (this.isOnLearnGitlab) {
eventHub.$emit('showSuccessfulInvitationsAlert');
@@ -347,7 +377,7 @@ export default {
},
clearEmptyInviteError() {
this.invalidFeedbackMessage = '';
- this.emptyInvitesError = false;
+ this.shouldShowEmptyInvitesAlert = false;
},
removeToken(token) {
delete this.invalidMembers[memberName(token)];
@@ -370,12 +400,13 @@ export default {
:help-link="helpLink"
:label-intro-text="labelIntroText"
:label-search-field="$options.labels.searchField"
- :form-group-description="$options.labels.placeHolder"
+ :form-group-description="formGroupDescription"
:invalid-feedback-message="invalidFeedbackMessage"
:is-loading="isLoading"
:new-users-to-invite="newUsersToInvite"
:root-group-id="rootId"
:users-limit-dataset="usersLimitDataset"
+ :full-path="fullPath"
@reset="resetFields"
@submit="sendInvite"
@access-level="onAccessLevelUpdate"
@@ -390,59 +421,77 @@ export default {
</template>
<template #alert>
- <gl-alert
- v-if="hasInvalidMembers"
- variant="danger"
- :dismissible="false"
- :title="memberErrorTitle"
- data-testid="alert-member-error"
- >
- {{ $options.labels.memberErrorListText }}
- <ul class="gl-pl-5 gl-mb-0">
- <li v-for="error in errorsLimited" :key="error.member" data-testid="errors-limited-item">
- <strong>{{ error.displayedMemberName }}:</strong> {{ error.message }}
- </li>
- </ul>
- <template v-if="shouldErrorsSectionExpand">
- <gl-collapse v-model="isErrorsSectionExpanded">
- <ul class="gl-pl-5 gl-mb-0">
- <li
- v-for="error in errorsExpanded"
- :key="error.member"
- data-testid="errors-expanded-item"
- >
- <strong>{{ error.displayedMemberName }}:</strong> {{ error.message }}
- </li>
- </ul>
- </gl-collapse>
- <gl-button
- class="gl-text-decoration-none! gl-shadow-none! gl-mt-3"
- data-testid="accordion-button"
- variant="link"
- @click="toggleErrorExpansion"
- >
- {{ errorCollapseText }}
- <gl-icon
- name="chevron-down"
- class="gl-transition-medium"
- :class="{ 'gl-rotate-180': isErrorsSectionExpanded }"
- />
- </gl-button>
- </template>
- </gl-alert>
- <user-limit-notification
- v-else-if="showUserLimitNotification"
- :limit-variant="limitVariant"
- :users-limit-dataset="usersLimitDataset"
- />
+ <div ref="alerts" tabindex="-1">
+ <gl-alert
+ v-if="shouldShowEmptyInvitesAlert"
+ id="empty-invites-alert"
+ class="gl-mb-4"
+ variant="danger"
+ :dismissible="false"
+ data-testid="empty-invites-alert"
+ >
+ {{ $options.labels.emptyInvitesAlertText }}
+ </gl-alert>
+ <gl-alert
+ v-if="hasInvalidMembers"
+ class="gl-mb-4"
+ variant="danger"
+ :dismissible="false"
+ :title="memberErrorTitle"
+ data-testid="alert-member-error"
+ >
+ {{ $options.labels.memberErrorListText }}
+ <ul class="gl-pl-5 gl-mb-0">
+ <li
+ v-for="error in errorsLimited"
+ :key="error.member"
+ data-testid="errors-limited-item"
+ >
+ <strong>{{ error.displayedMemberName }}:</strong> {{ error.message }}
+ </li>
+ </ul>
+ <template v-if="shouldErrorsSectionExpand">
+ <gl-collapse v-model="isErrorsSectionExpanded">
+ <ul class="gl-pl-5 gl-mb-0">
+ <li
+ v-for="error in errorsExpanded"
+ :key="error.member"
+ data-testid="errors-expanded-item"
+ >
+ <strong>{{ error.displayedMemberName }}:</strong> {{ error.message }}
+ </li>
+ </ul>
+ </gl-collapse>
+ <gl-button
+ class="gl-text-decoration-none! gl-shadow-none! gl-mt-3"
+ data-testid="accordion-button"
+ variant="link"
+ @click="toggleErrorExpansion"
+ >
+ {{ errorCollapseText }}
+ <gl-icon
+ name="chevron-down"
+ class="gl-transition-medium"
+ :class="{ 'gl-rotate-180': isErrorsSectionExpanded }"
+ />
+ </gl-button>
+ </template>
+ </gl-alert>
+ <user-limit-notification
+ v-else-if="showUserLimitNotification"
+ :limit-variant="limitVariant"
+ :users-limit-dataset="usersLimitDataset"
+ />
+ </div>
</template>
- <template #select="{ exceptionState, labelId }">
+ <template #select="{ exceptionState, inputId }">
<members-token-select
v-model="newUsersToInvite"
class="gl-mb-2"
+ aria-labelledby="empty-invites-alert"
+ :input-id="inputId"
:exception-state="exceptionState"
- :aria-labelledby="labelId"
:users-filter="usersFilter"
:filter-id="filterId"
:invalid-members="invalidMembers"
diff --git a/app/assets/javascripts/invite_members/components/invite_modal_base.vue b/app/assets/javascripts/invite_members/components/invite_modal_base.vue
index e3511a49fc5..2cbd681c67d 100644
--- a/app/assets/javascripts/invite_members/components/invite_modal_base.vue
+++ b/app/assets/javascripts/invite_members/components/invite_modal_base.vue
@@ -1,14 +1,5 @@
<script>
-import {
- GlFormGroup,
- GlModal,
- GlDropdown,
- GlDropdownItem,
- GlDatepicker,
- GlLink,
- GlSprintf,
- GlFormInput,
-} from '@gitlab/ui';
+import { GlFormGroup, GlFormSelect, GlModal, GlDatepicker, GlLink, GlSprintf } from '@gitlab/ui';
import Tracking from '~/tracking';
import { sprintf } from '~/locale';
import ContentTransition from '~/vue_shared/components/content_transition.vue';
@@ -37,13 +28,11 @@ const DEFAULT_SLOTS = [
export default {
components: {
GlFormGroup,
+ GlFormSelect,
GlDatepicker,
GlLink,
GlModal,
- GlDropdown,
- GlDropdownItem,
GlSprintf,
- GlFormInput,
ContentTransition,
},
mixins: [Tracking.mixin()],
@@ -141,14 +130,23 @@ export default {
};
},
computed: {
+ accessLevelsOptions() {
+ return Object.entries(this.accessLevels).map(([text, value]) => ({ text, value }));
+ },
introText() {
return sprintf(this.labelIntroText, { name: this.name });
},
exceptionState() {
return this.invalidFeedbackMessage ? false : null;
},
- selectLabelId() {
- return `${this.modalId}_select`;
+ selectId() {
+ return `${this.modalId}_search`;
+ },
+ dropdownId() {
+ return `${this.modalId}_dropdown`;
+ },
+ datepickerId() {
+ return `${this.modalId}_expires_at`;
},
selectedRoleName() {
return Object.keys(this.accessLevels).find(
@@ -218,9 +216,6 @@ export default {
this.$emit('cancel');
},
- changeSelectedItem(item) {
- this.selectedAccessLevel = item;
- },
onSubmit(e) {
// We never want to hide when submitting
e.preventDefault();
@@ -279,64 +274,50 @@ export default {
<slot name="alert"></slot>
<gl-form-group
+ :label="labelSearchField"
+ :label-for="selectId"
:invalid-feedback="invalidFeedbackMessage"
:state="exceptionState"
:description="formGroupDescription"
data-testid="members-form-group"
>
- <label :id="selectLabelId" class="col-form-label">{{ labelSearchField }}</label>
- <slot name="select" v-bind="{ exceptionState, labelId: selectLabelId }"></slot>
+ <slot name="select" v-bind="{ exceptionState, inputId: selectId }"></slot>
</gl-form-group>
- <label class="gl-font-weight-bold">{{ $options.ACCESS_LEVEL }}</label>
- <div class="gl-mt-2 gl-w-half gl-xs-w-full">
- <gl-dropdown
- class="gl-shadow-none gl-w-full"
+ <gl-form-group
+ class="gl-w-half gl-xs-w-full"
+ :label="$options.ACCESS_LEVEL"
+ :label-for="dropdownId"
+ >
+ <template #description>
+ <gl-sprintf :message="$options.READ_MORE_TEXT">
+ <template #link="{ content }">
+ <gl-link :href="helpLink" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </template>
+ <gl-form-select
+ :id="dropdownId"
+ v-model="selectedAccessLevel"
data-qa-selector="access_level_dropdown"
- v-bind="$attrs"
- :text="selectedRoleName"
- >
- <template v-for="(key, item) in accessLevels">
- <gl-dropdown-item
- :key="key"
- active-class="is-active"
- is-check-item
- :is-checked="key === selectedAccessLevel"
- @click="changeSelectedItem(key)"
- >
- <div>{{ item }}</div>
- </gl-dropdown-item>
- </template>
- </gl-dropdown>
- </div>
-
- <div class="gl-mt-2 gl-w-half gl-xs-w-full">
- <gl-sprintf :message="$options.READ_MORE_TEXT">
- <template #link="{ content }">
- <gl-link :href="helpLink" target="_blank">{{ content }}</gl-link>
- </template>
- </gl-sprintf>
- </div>
+ :options="accessLevelsOptions"
+ />
+ </gl-form-group>
- <label class="gl-mt-5 gl-display-block" for="expires_at">{{
- $options.ACCESS_EXPIRE_DATE
- }}</label>
- <div class="gl-mt-2 gl-w-half gl-xs-w-full gl-display-inline-block">
+ <gl-form-group
+ class="gl-w-half gl-xs-w-full"
+ :label="$options.ACCESS_EXPIRE_DATE"
+ :label-for="datepickerId"
+ >
<gl-datepicker
v-model="selectedDate"
- class="gl-display-inline!"
+ :input-id="datepickerId"
+ class="gl-display-block!"
:min-date="minDate"
:target="null"
- >
- <template #default="{ formattedDate }">
- <gl-form-input
- class="gl-w-full"
- :value="formattedDate"
- :placeholder="__(`YYYY-MM-DD`)"
- />
- </template>
- </gl-datepicker>
- </div>
+ />
+ </gl-form-group>
+
<slot name="form-after"></slot>
</template>
diff --git a/app/assets/javascripts/invite_members/components/members_token_select.vue b/app/assets/javascripts/invite_members/components/members_token_select.vue
index 2ddb04e1eeb..68602068699 100644
--- a/app/assets/javascripts/invite_members/components/members_token_select.vue
+++ b/app/assets/javascripts/invite_members/components/members_token_select.vue
@@ -49,6 +49,11 @@ export default {
type: Object,
required: true,
},
+ inputId: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
data() {
return {
@@ -84,6 +89,13 @@ export default {
hasInvalidMembers() {
return !isEmpty(this.invalidMembers);
},
+ textInputAttrs() {
+ return {
+ 'data-testid': 'members-token-select-input',
+ 'data-qa-selector': 'members_token_select_input',
+ id: this.inputId,
+ };
+ },
},
watch: {
// We might not really want this to be *reactive* since we want the "class" state to be
@@ -183,10 +195,7 @@ export default {
:hide-dropdown-with-no-items="hideDropdownWithNoItems"
:placeholder="placeholderText"
:aria-labelledby="ariaLabelledby"
- :text-input-attrs="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
- 'data-testid': 'members-token-select-input',
- 'data-qa-selector': 'members_token_select_input',
- } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
+ :text-input-attrs="textInputAttrs"
@blur="handleBlur"
@text-input="handleTextInput"
@input="handleInput"
diff --git a/app/assets/javascripts/invite_members/constants.js b/app/assets/javascripts/invite_members/constants.js
index de7b1019782..a894eb24d38 100644
--- a/app/assets/javascripts/invite_members/constants.js
+++ b/app/assets/javascripts/invite_members/constants.js
@@ -9,6 +9,7 @@ export const INVITE_MEMBERS_FOR_TASK = {
view: 'modal_opened_from_email',
submit: 'submit',
};
+export const TOAST_MESSAGE_LOCALSTORAGE_KEY = 'members_invited_successfully';
export const GROUP_FILTERS = {
ALL: 'all',
@@ -57,6 +58,10 @@ export const GROUP_MODAL_TO_PROJECT_DEFAULT_INTRO_TEXT = s__(
"InviteMembersModal|You're inviting a group to the %{strongStart}%{name}%{strongEnd} project.",
);
+export const GROUP_MODAL_ALERT_BODY = s__(
+ 'InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit.',
+);
+
export const GROUP_SEARCH_FIELD = s__('InviteMembersModal|Select a group to invite');
export const GROUP_PLACEHOLDER = s__('InviteMembersModal|Search for a group to invite');
@@ -77,9 +82,7 @@ export const MEMBER_ERROR_LIST_TEXT = s__(
);
export const COLLAPSED_ERRORS = s__('InviteMembersModal|Show more (%{count})');
export const EXPANDED_ERRORS = s__('InviteMembersModal|Show less');
-export const EMPTY_INVITES_ERROR_TEXT = s__(
- 'InviteMembersModal|Please select members or type email addresses to invite',
-);
+export const EMPTY_INVITES_ALERT_TEXT = s__('InviteMembersModal|Please add members to invite');
export const MEMBER_MODAL_LABELS = {
modal: {
@@ -117,7 +120,7 @@ export const MEMBER_MODAL_LABELS = {
memberErrorListText: MEMBER_ERROR_LIST_TEXT,
collapsedErrors: COLLAPSED_ERRORS,
expandedErrors: EXPANDED_ERRORS,
- emptyInvitesErrorText: EMPTY_INVITES_ERROR_TEXT,
+ emptyInvitesAlertText: EMPTY_INVITES_ALERT_TEXT,
};
export const GROUP_MODAL_LABELS = {
diff --git a/app/assets/javascripts/invite_members/init_import_project_members_modal.js b/app/assets/javascripts/invite_members/init_import_project_members_modal.js
index daaa1315884..227d8395250 100644
--- a/app/assets/javascripts/invite_members/init_import_project_members_modal.js
+++ b/app/assets/javascripts/invite_members/init_import_project_members_modal.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import ImportProjectMembersModal from '~/invite_members/components/import_project_members_modal.vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
export default function initImportProjectMembersModal() {
const el = document.querySelector('.js-import-project-members-modal');
@@ -8,7 +9,7 @@ export default function initImportProjectMembersModal() {
return false;
}
- const { projectId, projectName } = el.dataset;
+ const { projectId, projectName, reloadPageOnSubmit } = el.dataset;
return new Vue({
el,
@@ -17,6 +18,7 @@ export default function initImportProjectMembersModal() {
props: {
projectId,
projectName,
+ reloadPageOnSubmit: parseBoolean(reloadPageOnSubmit),
},
}),
});
diff --git a/app/assets/javascripts/invite_members/init_invite_groups_modal.js b/app/assets/javascripts/invite_members/init_invite_groups_modal.js
index be1576ad0b0..53b756b610f 100644
--- a/app/assets/javascripts/invite_members/init_invite_groups_modal.js
+++ b/app/assets/javascripts/invite_members/init_invite_groups_modal.js
@@ -28,6 +28,9 @@ export default function initInviteGroupsModal() {
return new Vue({
el,
+ provide: {
+ freeUsersLimit: parseInt(el.dataset.freeUsersLimit, 10),
+ },
render: (createElement) =>
createElement(InviteGroupsModal, {
props: {
@@ -38,6 +41,8 @@ export default function initInviteGroupsModal() {
groupSelectFilter: el.dataset.groupsFilter,
groupSelectParentId: parseInt(el.dataset.parentId, 10),
invalidGroups: JSON.parse(el.dataset.invalidGroups || '[]'),
+ freeUserCapEnabled: parseBoolean(el.dataset.freeUserCapEnabled),
+ reloadPageOnSubmit: parseBoolean(el.dataset.reloadPageOnSubmit),
},
}),
});
diff --git a/app/assets/javascripts/invite_members/init_invite_members_modal.js b/app/assets/javascripts/invite_members/init_invite_members_modal.js
index a4be3f205a3..842ab07f368 100644
--- a/app/assets/javascripts/invite_members/init_invite_members_modal.js
+++ b/app/assets/javascripts/invite_members/init_invite_members_modal.js
@@ -41,6 +41,7 @@ export default (function initInviteMembersModal() {
usersLimitDataset: convertObjectPropsToCamelCase(
JSON.parse(el.dataset.usersLimitDataset || '{}'),
),
+ reloadPageOnSubmit: parseBoolean(el.dataset.reloadPageOnSubmit),
},
}),
});
diff --git a/app/assets/javascripts/invite_members/utils/trigger_successful_invite_alert.js b/app/assets/javascripts/invite_members/utils/trigger_successful_invite_alert.js
new file mode 100644
index 00000000000..4d3a7951265
--- /dev/null
+++ b/app/assets/javascripts/invite_members/utils/trigger_successful_invite_alert.js
@@ -0,0 +1,23 @@
+import { createAlert } from '~/flash';
+import AccessorUtilities from '~/lib/utils/accessor';
+
+import { TOAST_MESSAGE_LOCALSTORAGE_KEY, TOAST_MESSAGE_SUCCESSFUL } from '../constants';
+
+export function displaySuccessfulInvitationAlert() {
+ if (!AccessorUtilities.canUseLocalStorage()) {
+ return;
+ }
+
+ const showAlert = Boolean(localStorage.getItem(TOAST_MESSAGE_LOCALSTORAGE_KEY));
+ if (showAlert) {
+ localStorage.removeItem(TOAST_MESSAGE_LOCALSTORAGE_KEY);
+ createAlert({ message: TOAST_MESSAGE_SUCCESSFUL, variant: 'info' });
+ }
+}
+
+export function reloadOnInvitationSuccess() {
+ if (AccessorUtilities.canUseLocalStorage()) {
+ localStorage.setItem(TOAST_MESSAGE_LOCALSTORAGE_KEY, 'true');
+ }
+ window.location.reload();
+}
diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/components/move_issues_button.vue b/app/assets/javascripts/issuable/bulk_update_sidebar/components/move_issues_button.vue
deleted file mode 100644
index 6e287ac3bb7..00000000000
--- a/app/assets/javascripts/issuable/bulk_update_sidebar/components/move_issues_button.vue
+++ /dev/null
@@ -1,171 +0,0 @@
-<script>
-import { GlAlert } from '@gitlab/ui';
-import IssuableMoveDropdown from '~/vue_shared/components/sidebar/issuable_move_dropdown.vue';
-import createFlash from '~/flash';
-import { logError } from '~/lib/logger';
-import { s__ } from '~/locale';
-import {
- WORK_ITEM_TYPE_ENUM_ISSUE,
- WORK_ITEM_TYPE_ENUM_INCIDENT,
- WORK_ITEM_TYPE_ENUM_TASK,
- WORK_ITEM_TYPE_ENUM_TEST_CASE,
-} from '~/work_items/constants';
-import issuableEventHub from '~/issues/list/eventhub';
-import getIssuesQuery from 'ee_else_ce/issues/list/queries/get_issues.query.graphql';
-import getIssuesCountQuery from 'ee_else_ce/issues/list/queries/get_issues_counts.query.graphql';
-import moveIssueMutation from './graphql/mutations/move_issue.mutation.graphql';
-
-export default {
- name: 'MoveIssuesButton',
- components: {
- IssuableMoveDropdown,
- GlAlert,
- },
- props: {
- projectFullPath: {
- type: String,
- required: true,
- },
- projectsFetchPath: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- selectedIssuables: [],
- moveInProgress: false,
- };
- },
- computed: {
- cannotMoveTasksWarningTitle() {
- if (this.tasksSelected && this.testCasesSelected) {
- return s__('Issues|Tasks and test cases can not be moved.');
- }
-
- if (this.testCasesSelected) {
- return s__('Issues|Test cases can not be moved.');
- }
-
- return s__('Issues|Tasks can not be moved.');
- },
- issuesSelected() {
- return this.selectedIssuables.some((item) => item.type === WORK_ITEM_TYPE_ENUM_ISSUE);
- },
- incidentsSelected() {
- return this.selectedIssuables.some((item) => item.type === WORK_ITEM_TYPE_ENUM_INCIDENT);
- },
- tasksSelected() {
- return this.selectedIssuables.some((item) => item.type === WORK_ITEM_TYPE_ENUM_TASK);
- },
- testCasesSelected() {
- return this.selectedIssuables.some((item) => item.type === WORK_ITEM_TYPE_ENUM_TEST_CASE);
- },
- },
- mounted() {
- issuableEventHub.$on('issuables:issuableChecked', this.handleIssuableChecked);
- },
- beforeDestroy() {
- issuableEventHub.$off('issuables:issuableChecked', this.handleIssuableChecked);
- },
- methods: {
- handleIssuableChecked(issuable, value) {
- if (value) {
- this.selectedIssuables.push(issuable);
- } else {
- const index = this.selectedIssuables.indexOf(issuable);
- if (index > -1) {
- this.selectedIssuables.splice(index, 1);
- }
- }
- },
- moveIssues(targetProject) {
- const iids = this.selectedIssuables.reduce((result, issueData) => {
- if (
- issueData.type === WORK_ITEM_TYPE_ENUM_ISSUE ||
- issueData.type === WORK_ITEM_TYPE_ENUM_INCIDENT
- ) {
- result.push(issueData.iid);
- }
- return result;
- }, []);
-
- if (iids.length === 0) {
- return;
- }
-
- this.moveInProgress = true;
- issuableEventHub.$emit('issuables:bulkMoveStarted');
-
- const promises = iids.map((id) => {
- return this.moveIssue(id, targetProject);
- });
-
- Promise.all(promises)
- .then((promisesResult) => {
- let foundError = false;
-
- for (const promiseResult of promisesResult) {
- if (promiseResult.data.issueMove?.errors?.length) {
- foundError = true;
- logError(
- `Error moving issue. Error message: ${promiseResult.data.issueMove.errors[0].message}`,
- );
- }
- }
-
- if (!foundError) {
- const client = this.$apollo.provider.defaultClient;
- client.refetchQueries({
- include: [getIssuesQuery, getIssuesCountQuery],
- });
- this.moveInProgress = false;
- this.selectedIssuables = [];
- issuableEventHub.$emit('issuables:bulkMoveEnded');
- } else {
- throw new Error();
- }
- })
- .catch(() => {
- this.moveInProgress = false;
- issuableEventHub.$emit('issuables:bulkMoveEnded');
-
- createFlash({
- message: s__(`Issues|There was an error while moving the issues.`),
- });
- });
- },
- moveIssue(issueIid, targetProject) {
- return this.$apollo.mutate({
- mutation: moveIssueMutation,
- variables: {
- moveIssueInput: {
- projectPath: this.projectFullPath,
- iid: issueIid,
- targetProjectPath: targetProject.full_path,
- },
- },
- });
- },
- },
- i18n: {
- dropdownButtonTitle: s__('Issues|Move selected'),
- },
-};
-</script>
-<template>
- <div>
- <issuable-move-dropdown
- :project-full-path="projectFullPath"
- :projects-fetch-path="projectsFetchPath"
- :move-in-progress="moveInProgress"
- :disabled="!issuesSelected && !incidentsSelected"
- :dropdown-header-title="$options.i18n.dropdownButtonTitle"
- :dropdown-button-title="$options.i18n.dropdownButtonTitle"
- @move-issuable="moveIssues"
- />
- <gl-alert v-if="tasksSelected || testCasesSelected" :dismissible="false" variant="warning">
- {{ cannotMoveTasksWarningTitle }}
- </gl-alert>
- </div>
-</template>
diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/components/status_dropdown.vue b/app/assets/javascripts/issuable/bulk_update_sidebar/components/status_dropdown.vue
deleted file mode 100644
index ba94932289e..00000000000
--- a/app/assets/javascripts/issuable/bulk_update_sidebar/components/status_dropdown.vue
+++ /dev/null
@@ -1,57 +0,0 @@
-<script>
-import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
-import { __ } from '~/locale';
-import { statusDropdownOptions } from '../constants';
-
-export default {
- components: {
- GlDropdown,
- GlDropdownItem,
- },
- data() {
- return {
- status: null,
- };
- },
- computed: {
- dropdownText() {
- return this.status?.text ?? this.$options.i18n.defaultDropdownText;
- },
- selectedValue() {
- return this.status?.value;
- },
- },
- methods: {
- onDropdownItemClick(statusOption) {
- // clear status if the currently checked status is clicked again
- if (this.status?.value === statusOption.value) {
- this.status = null;
- } else {
- this.status = statusOption;
- }
- },
- },
- i18n: {
- dropdownTitle: __('Change status'),
- defaultDropdownText: __('Select status'),
- },
- statusDropdownOptions,
-};
-</script>
-<template>
- <div>
- <input type="hidden" name="update[state_event]" :value="selectedValue" />
- <gl-dropdown :text="dropdownText" :title="$options.i18n.dropdownTitle" class="gl-w-full">
- <gl-dropdown-item
- v-for="statusOption in $options.statusDropdownOptions"
- :key="statusOption.value"
- :is-checked="selectedValue === statusOption.value"
- is-check-item
- :title="statusOption.text"
- @click="onDropdownItemClick(statusOption)"
- >
- {{ statusOption.text }}
- </gl-dropdown-item>
- </gl-dropdown>
- </div>
-</template>
diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/components/subscriptions_dropdown.vue b/app/assets/javascripts/issuable/bulk_update_sidebar/components/subscriptions_dropdown.vue
deleted file mode 100644
index 8774b065c22..00000000000
--- a/app/assets/javascripts/issuable/bulk_update_sidebar/components/subscriptions_dropdown.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-<script>
-import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
-import { __ } from '~/locale';
-import { subscriptionsDropdownOptions } from '../constants';
-
-export default {
- subscriptionsDropdownOptions,
- i18n: {
- defaultDropdownText: __('Select subscription'),
- headerText: __('Change subscription'),
- },
- components: {
- GlDropdown,
- GlDropdownItem,
- },
- data() {
- return {
- subscription: undefined,
- };
- },
- computed: {
- dropdownText() {
- return this.subscription?.text ?? this.$options.i18n.defaultDropdownText;
- },
- selectedValue() {
- return this.subscription?.value;
- },
- },
- methods: {
- handleClick(option) {
- this.subscription = option.value === this.subscription?.value ? undefined : option;
- },
- },
-};
-</script>
-<template>
- <div>
- <input type="hidden" name="update[subscription_event]" :value="selectedValue" />
- <gl-dropdown class="gl-w-full" :header-text="$options.i18n.headerText" :text="dropdownText">
- <gl-dropdown-item
- v-for="subscriptionsOption in $options.subscriptionsDropdownOptions"
- :key="subscriptionsOption.value"
- is-check-item
- :is-checked="selectedValue === subscriptionsOption.value"
- @click="handleClick(subscriptionsOption)"
- >
- {{ subscriptionsOption.text }}
- </gl-dropdown-item>
- </gl-dropdown>
- </div>
-</template>
diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/constants.js b/app/assets/javascripts/issuable/bulk_update_sidebar/constants.js
deleted file mode 100644
index 68133ceb3c7..00000000000
--- a/app/assets/javascripts/issuable/bulk_update_sidebar/constants.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import { __ } from '~/locale';
-
-export const statusDropdownOptions = [
- {
- text: __('Open'),
- value: 'reopen',
- },
- {
- text: __('Closed'),
- value: 'close',
- },
-];
-
-export const subscriptionsDropdownOptions = [
- {
- text: __('Subscribe'),
- value: 'subscribe',
- },
- {
- text: __('Unsubscribe'),
- value: 'unsubscribe',
- },
-];
diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/index.js b/app/assets/javascripts/issuable/bulk_update_sidebar/index.js
deleted file mode 100644
index b7cb805ee37..00000000000
--- a/app/assets/javascripts/issuable/bulk_update_sidebar/index.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import { gqlClient } from '../../issues/list/graphql';
-import StatusDropdown from './components/status_dropdown.vue';
-import SubscriptionsDropdown from './components/subscriptions_dropdown.vue';
-import MoveIssuesButton from './components/move_issues_button.vue';
-import issuableBulkUpdateActions from './issuable_bulk_update_actions';
-import IssuableBulkUpdateSidebar from './issuable_bulk_update_sidebar';
-
-export function initBulkUpdateSidebar(prefixId) {
- const el = document.querySelector('.issues-bulk-update');
-
- if (!el) {
- return;
- }
-
- issuableBulkUpdateActions.init({ prefixId });
- new IssuableBulkUpdateSidebar(); // eslint-disable-line no-new
-}
-
-export function initStatusDropdown() {
- const el = document.querySelector('.js-status-dropdown');
-
- if (!el) {
- return null;
- }
-
- return new Vue({
- el,
- name: 'StatusDropdownRoot',
- render: (createElement) => createElement(StatusDropdown),
- });
-}
-
-export function initSubscriptionsDropdown() {
- const el = document.querySelector('.js-subscriptions-dropdown');
-
- if (!el) {
- return null;
- }
-
- return new Vue({
- el,
- name: 'SubscriptionsDropdownRoot',
- render: (createElement) => createElement(SubscriptionsDropdown),
- });
-}
-
-export function initMoveIssuesButton() {
- const el = document.querySelector('.js-move-issues');
-
- if (!el) {
- return null;
- }
-
- const { dataset } = el;
-
- Vue.use(VueApollo);
- const apolloProvider = new VueApollo({
- defaultClient: gqlClient,
- });
-
- return new Vue({
- el,
- name: 'MoveIssuesRoot',
- apolloProvider,
- render: (createElement) =>
- createElement(MoveIssuesButton, {
- props: {
- projectFullPath: dataset.projectFullPath,
- projectsFetchPath: dataset.projectsFetchPath,
- },
- }),
- });
-}
diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_actions.js
deleted file mode 100644
index 14824820c0d..00000000000
--- a/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_actions.js
+++ /dev/null
@@ -1,126 +0,0 @@
-import $ from 'jquery';
-import { difference, intersection, union } from 'lodash';
-import createFlash from '~/flash';
-import axios from '~/lib/utils/axios_utils';
-import { __ } from '~/locale';
-
-export default {
- init({ form, issues, prefixId } = {}) {
- this.prefixId = prefixId || 'issue_';
- this.form = form || this.getElement('.bulk-update');
- this.$labelDropdown = this.form.find('.js-label-select');
- this.issues = issues || this.getElement('.issues-list .issue');
- this.willUpdateLabels = false;
- this.bindEvents();
- },
-
- bindEvents() {
- // eslint-disable-next-line @gitlab/no-global-event-off
- return this.form.off('submit').on('submit', this.onFormSubmit.bind(this));
- },
-
- onFormSubmit(e) {
- e.preventDefault();
- return this.submit();
- },
-
- submit() {
- axios[this.form.attr('method')](this.form.attr('action'), this.getFormDataAsObject())
- .then(() => window.location.reload())
- .catch(() => this.onFormSubmitFailure());
- },
-
- onFormSubmitFailure() {
- this.form.find('[type="submit"]').enable();
- return createFlash({
- message: __('Issue update failed'),
- });
- },
-
- /**
- * Simple form serialization, it will return just what we need
- * Returns key/value pairs from form data
- */
-
- getFormDataAsObject() {
- const formData = {
- update: {
- state_event: this.form.find('input[name="update[state_event]"]').val(),
- assignee_ids: [this.form.find('input[name="update[assignee_ids][]"]').val()],
- milestone_id: this.form.find('input[name="update[milestone_id]"]').val(),
- issuable_ids: this.form.find('input[name="update[issuable_ids]"]').val(),
- subscription_event: this.form.find('input[name="update[subscription_event]"]').val(),
- health_status: this.form.find('input[name="update[health_status]"]').val(),
- epic_id: this.form.find('input[name="update[epic_id]"]').val(),
- sprint_id: this.form.find('input[name="update[iteration_id]"]').val(),
- add_label_ids: [],
- remove_label_ids: [],
- },
- };
- if (this.willUpdateLabels) {
- formData.update.add_label_ids = this.$labelDropdown.data('user-checked');
- formData.update.remove_label_ids = this.$labelDropdown.data('user-unchecked');
- }
- return formData;
- },
-
- setOriginalDropdownData() {
- const $labelSelect = $('.bulk-update .js-label-select');
- const userCheckedIds = $labelSelect.data('user-checked') || [];
- const userUncheckedIds = $labelSelect.data('user-unchecked') || [];
-
- // Common labels plus user checked labels minus user unchecked labels
- const checkedIdsToShow = difference(
- union(this.getOriginalCommonIds(), userCheckedIds),
- userUncheckedIds,
- );
-
- // Indeterminate labels minus user checked labels minus user unchecked labels
- const indeterminateIdsToShow = difference(
- this.getOriginalIndeterminateIds(),
- userCheckedIds,
- userUncheckedIds,
- );
-
- $labelSelect.data('marked', checkedIdsToShow);
- $labelSelect.data('indeterminate', indeterminateIdsToShow);
- },
-
- // From issuable's initial bulk selection
- getOriginalCommonIds() {
- const labelIds = [];
- this.getElement('.issuable-list input[type="checkbox"]:checked').each((i, el) => {
- labelIds.push(this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels'));
- });
- return intersection.apply(this, labelIds);
- },
-
- // From issuable's initial bulk selection
- getOriginalIndeterminateIds() {
- const uniqueIds = [];
- const labelIds = [];
- let issuableLabels = [];
-
- // Collect unique label IDs for all checked issues
- this.getElement('.issuable-list input[type="checkbox"]:checked').each((i, el) => {
- issuableLabels = this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels');
- issuableLabels.forEach((labelId) => {
- // Store unique IDs
- if (uniqueIds.indexOf(labelId) === -1) {
- uniqueIds.push(labelId);
- }
- });
- // Store array of IDs per issuable
- labelIds.push(issuableLabels);
- });
- // Add uniqueIds to add it as argument for _.intersection
- labelIds.unshift(uniqueIds);
- // Return IDs that are present but not in all selected issuables
- return uniqueIds.filter((x) => !intersection.apply(this, labelIds).includes(x));
- },
-
- getElement(selector) {
- this.scopeEl = this.scopeEl || $('.content');
- return this.scopeEl.find(selector);
- },
-};
diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_sidebar.js
deleted file mode 100644
index b46a95c7dfa..00000000000
--- a/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_sidebar.js
+++ /dev/null
@@ -1,159 +0,0 @@
-/* eslint-disable class-methods-use-this, no-new */
-
-import $ from 'jquery';
-import issuableEventHub from '~/issues/list/eventhub';
-import LabelsSelect from '~/labels/labels_select';
-import { mountMilestoneDropdown } from '~/sidebar/mount_sidebar';
-import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
-
-const HIDDEN_CLASS = 'hidden';
-const DISABLED_CONTENT_CLASS = 'disabled-content';
-const SIDEBAR_EXPANDED_CLASS = 'right-sidebar-expanded issuable-bulk-update-sidebar';
-const SIDEBAR_COLLAPSED_CLASS = 'right-sidebar-collapsed issuable-bulk-update-sidebar';
-
-export default class IssuableBulkUpdateSidebar {
- constructor() {
- this.initDomElements();
- this.bindEvents();
- this.initDropdowns();
- this.setupBulkUpdateActions();
- }
-
- initDomElements() {
- this.$page = $('.layout-page');
- this.$sidebar = $('.right-sidebar');
- this.$sidebarInnerContainer = this.$sidebar.find('.issuable-sidebar');
- this.$bulkEditCancelBtn = $('.js-bulk-update-menu-hide');
- this.$bulkEditSubmitBtn = $('.js-update-selected-issues');
- this.$bulkUpdateEnableBtn = $('.js-bulk-update-toggle');
- this.$otherFilters = $('.issues-other-filters');
- this.$checkAllContainer = $('.check-all-holder');
- this.$issueChecks = $('.issue-check');
- this.$issuesList = $('.issuable-list input[type="checkbox"]');
- this.$issuableIdsInput = $('#update_issuable_ids');
- }
-
- bindEvents() {
- this.$bulkUpdateEnableBtn.on('click', (e) => this.toggleBulkEdit(e, true));
- this.$bulkEditCancelBtn.on('click', (e) => this.toggleBulkEdit(e, false));
- this.$checkAllContainer.on('click', (e) => this.selectAll(e));
- this.$issuesList.on('change', () => this.updateFormState());
- this.$bulkEditSubmitBtn.on('click', () => this.prepForSubmit());
- this.$checkAllContainer.on('click', () => this.updateFormState());
-
- // The event hub connects this bulk update logic with `issues_list_app.vue`.
- // We can remove it once we've refactored the issues list page bulk edit sidebar to Vue.
- // https://gitlab.com/gitlab-org/gitlab/-/issues/325874
- issuableEventHub.$on('issuables:enableBulkEdit', () => this.toggleBulkEdit(null, true));
- issuableEventHub.$on('issuables:updateBulkEdit', () => this.updateFormState());
-
- // These events are connected to the logic inside `move_issues_button.vue`,
- // so that only one action can be performed at a time
- issuableEventHub.$on('issuables:bulkMoveStarted', () => this.toggleSubmitButtonDisabled(true));
- issuableEventHub.$on('issuables:bulkMoveEnded', () => this.updateFormState());
- }
-
- initDropdowns() {
- new LabelsSelect();
- mountMilestoneDropdown();
-
- // Checking IS_EE and using ee_else_ce is odd, but we do it here to satisfy
- // the import/no-unresolved lint rule when FOSS_ONLY=1, even though at
- // runtime this block won't execute.
- if (IS_EE) {
- import('ee_else_ce/sidebar/mount_sidebar')
- .then(({ mountEpicDropdown, mountHealthStatusDropdown, mountIterationDropdown }) => {
- mountEpicDropdown();
- mountHealthStatusDropdown();
- mountIterationDropdown();
- })
- .catch(() => {});
- }
- }
-
- setupBulkUpdateActions() {
- IssuableBulkUpdateActions.setOriginalDropdownData();
- }
-
- updateFormState() {
- const noCheckedIssues = !$('.issuable-list input[type="checkbox"]:checked').length;
-
- this.toggleSubmitButtonDisabled(noCheckedIssues);
- this.updateSelectedIssuableIds();
-
- IssuableBulkUpdateActions.setOriginalDropdownData();
-
- issuableEventHub.$emit('issuables:selectionChanged', !noCheckedIssues);
- }
-
- prepForSubmit() {
- // if submit button is disabled, submission is blocked. This ensures we disable after
- // form submission is carried out
- setTimeout(() => this.$bulkEditSubmitBtn.disable());
- this.updateSelectedIssuableIds();
- }
-
- toggleBulkEdit(e, enable) {
- e?.preventDefault();
-
- issuableEventHub.$emit('issuables:toggleBulkEdit', enable);
-
- this.toggleSidebarDisplay(enable);
- this.toggleBulkEditButtonDisabled(enable);
- this.toggleOtherFiltersDisabled(enable);
- this.toggleCheckboxDisplay(enable);
- }
-
- updateSelectedIssuableIds() {
- this.$issuableIdsInput.val(IssuableBulkUpdateSidebar.getCheckedIssueIds());
- }
-
- selectAll() {
- const checkAllButtonState = this.$checkAllContainer.find('input').prop('checked');
-
- this.$issuesList.prop('checked', checkAllButtonState);
- }
-
- toggleSidebarDisplay(show) {
- this.$page.toggleClass(SIDEBAR_EXPANDED_CLASS, show);
- this.$page.toggleClass(SIDEBAR_COLLAPSED_CLASS, !show);
- this.$sidebarInnerContainer.toggleClass(HIDDEN_CLASS, !show);
- this.$sidebar.toggleClass(SIDEBAR_EXPANDED_CLASS, show);
- this.$sidebar.toggleClass(SIDEBAR_COLLAPSED_CLASS, !show);
- }
-
- toggleBulkEditButtonDisabled(disable) {
- if (disable) {
- this.$bulkUpdateEnableBtn.disable();
- } else {
- this.$bulkUpdateEnableBtn.enable();
- }
- }
-
- toggleCheckboxDisplay(show) {
- this.$checkAllContainer.toggleClass(HIDDEN_CLASS, !show);
- this.$issueChecks.toggleClass(HIDDEN_CLASS, !show);
- }
-
- toggleOtherFiltersDisabled(disable) {
- this.$otherFilters.toggleClass(DISABLED_CONTENT_CLASS, disable);
- }
-
- toggleSubmitButtonDisabled(disable) {
- if (disable) {
- this.$bulkEditSubmitBtn.disable();
- } else {
- this.$bulkEditSubmitBtn.enable();
- }
- }
-
- static getCheckedIssueIds() {
- const $checkedIssues = $('.issuable-list input[type="checkbox"]:checked');
-
- if ($checkedIssues.length > 0) {
- return $.map($checkedIssues, (value) => $(value).data('id'));
- }
-
- return [];
- }
-}
diff --git a/app/assets/javascripts/issuable/components/related_issuable_item.vue b/app/assets/javascripts/issuable/components/related_issuable_item.vue
index 254248ef1d4..fd55f05e955 100644
--- a/app/assets/javascripts/issuable/components/related_issuable_item.vue
+++ b/app/assets/javascripts/issuable/components/related_issuable_item.vue
@@ -1,13 +1,7 @@
<script>
import '~/commons/bootstrap';
-import {
- GlIcon,
- GlLink,
- GlTooltip,
- GlTooltipDirective,
- GlButton,
- GlSafeHtmlDirective as SafeHtml,
-} from '@gitlab/ui';
+import { GlIcon, GlLink, GlTooltip, GlTooltipDirective, GlButton } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import IssueDueDate from '~/boards/components/issue_due_date.vue';
import { TYPE_WORK_ITEM } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
diff --git a/app/assets/javascripts/issuable/index.js b/app/assets/javascripts/issuable/index.js
index 10dbefce503..ed336deb2ed 100644
--- a/app/assets/javascripts/issuable/index.js
+++ b/app/assets/javascripts/issuable/index.js
@@ -1,12 +1,25 @@
import { GlToast } from '@gitlab/ui';
import Vue from 'vue';
-import IssuableContext from '~/issuable/issuable_context';
import { parseBoolean } from '~/lib/utils/common_utils';
import Sidebar from '~/right_sidebar';
import { getSidebarOptions } from '~/sidebar/mount_sidebar';
import CsvImportExportButtons from './components/csv_import_export_buttons.vue';
import IssuableByEmail from './components/issuable_by_email.vue';
import IssuableHeaderWarnings from './components/issuable_header_warnings.vue';
+import issuableBulkUpdateActions from './issuable_bulk_update_actions';
+import IssuableBulkUpdateSidebar from './issuable_bulk_update_sidebar';
+import IssuableContext from './issuable_context';
+
+export function initBulkUpdateSidebar(prefixId) {
+ const el = document.querySelector('.issues-bulk-update');
+
+ if (!el) {
+ return;
+ }
+
+ issuableBulkUpdateActions.init({ prefixId });
+ new IssuableBulkUpdateSidebar(); // eslint-disable-line no-new
+}
export function initCsvImportExportButtons() {
const el = document.querySelector('.js-csv-import-export-buttons');
diff --git a/app/assets/javascripts/issuable/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable/issuable_bulk_update_actions.js
new file mode 100644
index 00000000000..c386267501a
--- /dev/null
+++ b/app/assets/javascripts/issuable/issuable_bulk_update_actions.js
@@ -0,0 +1,126 @@
+import $ from 'jquery';
+import { difference, intersection, union } from 'lodash';
+import { createAlert } from '~/flash';
+import axios from '~/lib/utils/axios_utils';
+import { __ } from '~/locale';
+
+export default {
+ init({ form, issues, prefixId } = {}) {
+ this.prefixId = prefixId || 'issue_';
+ this.form = form || this.getElement('.bulk-update');
+ this.$labelDropdown = this.form.find('.js-label-select');
+ this.issues = issues || this.getElement('.issues-list .issue');
+ this.willUpdateLabels = false;
+ this.bindEvents();
+ },
+
+ bindEvents() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
+ return this.form.off('submit').on('submit', this.onFormSubmit.bind(this));
+ },
+
+ onFormSubmit(e) {
+ e.preventDefault();
+ return this.submit();
+ },
+
+ submit() {
+ axios[this.form.attr('method')](this.form.attr('action'), this.getFormDataAsObject())
+ .then(() => window.location.reload())
+ .catch(() => this.onFormSubmitFailure());
+ },
+
+ onFormSubmitFailure() {
+ this.form.find('[type="submit"]').enable();
+ return createAlert({
+ message: __('Issue update failed'),
+ });
+ },
+
+ /**
+ * Simple form serialization, it will return just what we need
+ * Returns key/value pairs from form data
+ */
+
+ getFormDataAsObject() {
+ const formData = {
+ update: {
+ state_event: this.form.find('input[name="update[state_event]"]').val(),
+ assignee_ids: [this.form.find('input[name="update[assignee_ids][]"]').val()],
+ milestone_id: this.form.find('input[name="update[milestone_id]"]').val(),
+ issuable_ids: this.form.find('input[name="update[issuable_ids]"]').val(),
+ subscription_event: this.form.find('input[name="update[subscription_event]"]').val(),
+ health_status: this.form.find('input[name="update[health_status]"]').val(),
+ epic_id: this.form.find('input[name="update[epic_id]"]').val(),
+ sprint_id: this.form.find('input[name="update[iteration_id]"]').val(),
+ add_label_ids: [],
+ remove_label_ids: [],
+ },
+ };
+ if (this.willUpdateLabels) {
+ formData.update.add_label_ids = this.$labelDropdown.data('user-checked');
+ formData.update.remove_label_ids = this.$labelDropdown.data('user-unchecked');
+ }
+ return formData;
+ },
+
+ setOriginalDropdownData() {
+ const $labelSelect = $('.bulk-update .js-label-select');
+ const userCheckedIds = $labelSelect.data('user-checked') || [];
+ const userUncheckedIds = $labelSelect.data('user-unchecked') || [];
+
+ // Common labels plus user checked labels minus user unchecked labels
+ const checkedIdsToShow = difference(
+ union(this.getOriginalCommonIds(), userCheckedIds),
+ userUncheckedIds,
+ );
+
+ // Indeterminate labels minus user checked labels minus user unchecked labels
+ const indeterminateIdsToShow = difference(
+ this.getOriginalIndeterminateIds(),
+ userCheckedIds,
+ userUncheckedIds,
+ );
+
+ $labelSelect.data('marked', checkedIdsToShow);
+ $labelSelect.data('indeterminate', indeterminateIdsToShow);
+ },
+
+ // From issuable's initial bulk selection
+ getOriginalCommonIds() {
+ const labelIds = [];
+ this.getElement('.issuable-list input[type="checkbox"]:checked').each((i, el) => {
+ labelIds.push(this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels'));
+ });
+ return intersection.apply(this, labelIds);
+ },
+
+ // From issuable's initial bulk selection
+ getOriginalIndeterminateIds() {
+ const uniqueIds = [];
+ const labelIds = [];
+ let issuableLabels = [];
+
+ // Collect unique label IDs for all checked issues
+ this.getElement('.issuable-list input[type="checkbox"]:checked').each((i, el) => {
+ issuableLabels = this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels');
+ issuableLabels.forEach((labelId) => {
+ // Store unique IDs
+ if (uniqueIds.indexOf(labelId) === -1) {
+ uniqueIds.push(labelId);
+ }
+ });
+ // Store array of IDs per issuable
+ labelIds.push(issuableLabels);
+ });
+ // Add uniqueIds to add it as argument for _.intersection
+ labelIds.unshift(uniqueIds);
+ // Return IDs that are present but not in all selected issuables
+ return uniqueIds.filter((x) => !intersection.apply(this, labelIds).includes(x));
+ },
+
+ getElement(selector) {
+ this.scopeEl = this.scopeEl || $('.content');
+ return this.scopeEl.find(selector);
+ },
+};
diff --git a/app/assets/javascripts/issuable/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable/issuable_bulk_update_sidebar.js
new file mode 100644
index 00000000000..095da60a583
--- /dev/null
+++ b/app/assets/javascripts/issuable/issuable_bulk_update_sidebar.js
@@ -0,0 +1,167 @@
+/* eslint-disable class-methods-use-this, no-new */
+
+import $ from 'jquery';
+import issuableEventHub from '~/issues/list/eventhub';
+import LabelsSelect from '~/labels/labels_select';
+import {
+ mountMilestoneDropdown,
+ mountMoveIssuesButton,
+ mountStatusDropdown,
+ mountSubscriptionsDropdown,
+} from '~/sidebar/mount_sidebar';
+import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
+
+const HIDDEN_CLASS = 'hidden';
+const DISABLED_CONTENT_CLASS = 'disabled-content';
+const SIDEBAR_EXPANDED_CLASS = 'right-sidebar-expanded issuable-bulk-update-sidebar';
+const SIDEBAR_COLLAPSED_CLASS = 'right-sidebar-collapsed issuable-bulk-update-sidebar';
+
+export default class IssuableBulkUpdateSidebar {
+ constructor() {
+ this.initDomElements();
+ this.bindEvents();
+ this.initDropdowns();
+ this.setupBulkUpdateActions();
+ }
+
+ initDomElements() {
+ this.$page = $('.layout-page');
+ this.$sidebar = $('.right-sidebar');
+ this.$sidebarInnerContainer = this.$sidebar.find('.issuable-sidebar');
+ this.$bulkEditCancelBtn = $('.js-bulk-update-menu-hide');
+ this.$bulkEditSubmitBtn = $('.js-update-selected-issues');
+ this.$bulkUpdateEnableBtn = $('.js-bulk-update-toggle');
+ this.$otherFilters = $('.issues-other-filters');
+ this.$checkAllContainer = $('.check-all-holder');
+ this.$issueChecks = $('.issue-check');
+ this.$issuesList = $('.issuable-list input[type="checkbox"]');
+ this.$issuableIdsInput = $('#update_issuable_ids');
+ }
+
+ bindEvents() {
+ this.$bulkUpdateEnableBtn.on('click', (e) => this.toggleBulkEdit(e, true));
+ this.$bulkEditCancelBtn.on('click', (e) => this.toggleBulkEdit(e, false));
+ this.$checkAllContainer.on('click', (e) => this.selectAll(e));
+ this.$issuesList.on('change', () => this.updateFormState());
+ this.$bulkEditSubmitBtn.on('click', () => this.prepForSubmit());
+ this.$checkAllContainer.on('click', () => this.updateFormState());
+
+ // The event hub connects this bulk update logic with `issues_list_app.vue`.
+ // We can remove it once we've refactored the issues list page bulk edit sidebar to Vue.
+ // https://gitlab.com/gitlab-org/gitlab/-/issues/325874
+ issuableEventHub.$on('issuables:enableBulkEdit', () => this.toggleBulkEdit(null, true));
+ issuableEventHub.$on('issuables:updateBulkEdit', () => this.updateFormState());
+
+ // These events are connected to the logic inside `move_issues_button.vue`,
+ // so that only one action can be performed at a time
+ issuableEventHub.$on('issuables:bulkMoveStarted', () => this.toggleSubmitButtonDisabled(true));
+ issuableEventHub.$on('issuables:bulkMoveEnded', () => this.updateFormState());
+ }
+
+ initDropdowns() {
+ new LabelsSelect();
+ mountMilestoneDropdown();
+ mountMoveIssuesButton();
+ mountStatusDropdown();
+ mountSubscriptionsDropdown();
+
+ // Checking IS_EE and using ee_else_ce is odd, but we do it here to satisfy
+ // the import/no-unresolved lint rule when FOSS_ONLY=1, even though at
+ // runtime this block won't execute.
+ if (IS_EE) {
+ import('ee_else_ce/sidebar/mount_sidebar')
+ .then(({ mountEpicDropdown, mountHealthStatusDropdown, mountIterationDropdown }) => {
+ mountEpicDropdown();
+ mountHealthStatusDropdown();
+ mountIterationDropdown();
+ })
+ .catch(() => {});
+ }
+ }
+
+ setupBulkUpdateActions() {
+ IssuableBulkUpdateActions.setOriginalDropdownData();
+ }
+
+ updateFormState() {
+ const noCheckedIssues = !$('.issuable-list input[type="checkbox"]:checked').length;
+
+ this.toggleSubmitButtonDisabled(noCheckedIssues);
+ this.updateSelectedIssuableIds();
+
+ IssuableBulkUpdateActions.setOriginalDropdownData();
+
+ issuableEventHub.$emit('issuables:selectionChanged', !noCheckedIssues);
+ }
+
+ prepForSubmit() {
+ // if submit button is disabled, submission is blocked. This ensures we disable after
+ // form submission is carried out
+ setTimeout(() => this.$bulkEditSubmitBtn.disable());
+ this.updateSelectedIssuableIds();
+ }
+
+ toggleBulkEdit(e, enable) {
+ e?.preventDefault();
+
+ issuableEventHub.$emit('issuables:toggleBulkEdit', enable);
+
+ this.toggleSidebarDisplay(enable);
+ this.toggleBulkEditButtonDisabled(enable);
+ this.toggleOtherFiltersDisabled(enable);
+ this.toggleCheckboxDisplay(enable);
+ }
+
+ updateSelectedIssuableIds() {
+ this.$issuableIdsInput.val(IssuableBulkUpdateSidebar.getCheckedIssueIds());
+ }
+
+ selectAll() {
+ const checkAllButtonState = this.$checkAllContainer.find('input').prop('checked');
+
+ this.$issuesList.prop('checked', checkAllButtonState);
+ }
+
+ toggleSidebarDisplay(show) {
+ this.$page.toggleClass(SIDEBAR_EXPANDED_CLASS, show);
+ this.$page.toggleClass(SIDEBAR_COLLAPSED_CLASS, !show);
+ this.$sidebarInnerContainer.toggleClass(HIDDEN_CLASS, !show);
+ this.$sidebar.toggleClass(SIDEBAR_EXPANDED_CLASS, show);
+ this.$sidebar.toggleClass(SIDEBAR_COLLAPSED_CLASS, !show);
+ }
+
+ toggleBulkEditButtonDisabled(disable) {
+ if (disable) {
+ this.$bulkUpdateEnableBtn.disable();
+ } else {
+ this.$bulkUpdateEnableBtn.enable();
+ }
+ }
+
+ toggleCheckboxDisplay(show) {
+ this.$checkAllContainer.toggleClass(HIDDEN_CLASS, !show);
+ this.$issueChecks.toggleClass(HIDDEN_CLASS, !show);
+ }
+
+ toggleOtherFiltersDisabled(disable) {
+ this.$otherFilters.toggleClass(DISABLED_CONTENT_CLASS, disable);
+ }
+
+ toggleSubmitButtonDisabled(disable) {
+ if (disable) {
+ this.$bulkEditSubmitBtn.disable();
+ } else {
+ this.$bulkEditSubmitBtn.enable();
+ }
+ }
+
+ static getCheckedIssueIds() {
+ const $checkedIssues = $('.issuable-list input[type="checkbox"]:checked');
+
+ if ($checkedIssues.length > 0) {
+ return $.map($checkedIssues, (value) => $(value).data('id'));
+ }
+
+ return [];
+ }
+}
diff --git a/app/assets/javascripts/issuable/issuable_label_selector.js b/app/assets/javascripts/issuable/issuable_label_selector.js
new file mode 100644
index 00000000000..ad8bbf04d6f
--- /dev/null
+++ b/app/assets/javascripts/issuable/issuable_label_selector.js
@@ -0,0 +1,56 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
+import {
+ DropdownVariant,
+ LabelType,
+} from '~/sidebar/components/labels/labels_select_widget/constants';
+import { WorkspaceType } from '~/issues/constants';
+import IssuableLabelSelector from '~/vue_shared/issuable/create/components/issuable_label_selector.vue';
+
+Vue.use(VueApollo);
+
+const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient(),
+});
+
+export default () => {
+ const el = document.querySelector('.js-issuable-form-label-selector');
+
+ if (!el) {
+ return false;
+ }
+
+ const {
+ fieldName,
+ fullPath,
+ initialLabels,
+ issuableType,
+ labelsFilterBasePath,
+ labelsManagePath,
+ } = el.dataset;
+
+ return new Vue({
+ el,
+ apolloProvider,
+ provide: {
+ allowLabelCreate: true,
+ allowLabelEdit: true,
+ allowLabelRemove: true,
+ allowScopedLabels: true,
+ attrWorkspacePath: fullPath,
+ fieldName,
+ fullPath,
+ initialLabels: JSON.parse(initialLabels),
+ issuableType,
+ labelType: LabelType.project,
+ labelsFilterBasePath,
+ labelsManagePath,
+ variant: DropdownVariant.Embedded,
+ workspaceType: WorkspaceType.project,
+ },
+ render(createElement) {
+ return createElement(IssuableLabelSelector);
+ },
+ });
+};
diff --git a/app/assets/javascripts/issues/create_merge_request_dropdown.js b/app/assets/javascripts/issues/create_merge_request_dropdown.js
index 92ff7f21eff..977a505437d 100644
--- a/app/assets/javascripts/issues/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/issues/create_merge_request_dropdown.js
@@ -7,7 +7,7 @@ import {
import confidentialMergeRequestState from '~/confidential_merge_request/state';
import DropLab from '~/filtered_search/droplab/drop_lab_deprecated';
import ISetter from '~/filtered_search/droplab/plugins/input_setter';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { __, sprintf } from '~/locale';
import { mergeUrlParams } from '~/lib/utils/url_utility';
@@ -141,7 +141,7 @@ export default class CreateMergeRequestDropdown {
.catch(() => {
this.unavailable();
this.disable();
- createFlash({
+ createAlert({
message: __('Failed to check related branches.'),
});
});
@@ -162,7 +162,7 @@ export default class CreateMergeRequestDropdown {
}
})
.catch(() =>
- createFlash({
+ createAlert({
message: __('Failed to create a branch for this issue. Please try again.'),
}),
);
@@ -293,7 +293,7 @@ export default class CreateMergeRequestDropdown {
}
this.unavailable();
this.disable();
- createFlash({
+ createAlert({
message: __('Failed to get ref.'),
});
diff --git a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue
index 29f6aecca03..b9d876ef72f 100644
--- a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue
+++ b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue
@@ -1,13 +1,50 @@
<script>
-import { GlButton, GlEmptyState } from '@gitlab/ui';
+import { GlButton, GlEmptyState, GlTooltipDirective } from '@gitlab/ui';
+import * as Sentry from '@sentry/browser';
+import getIssuesQuery from 'ee_else_ce/issues/dashboard/queries/get_issues.query.graphql';
+import IssueCardStatistics from 'ee_else_ce/issues/list/components/issue_card_statistics.vue';
+import IssueCardTimeInfo from 'ee_else_ce/issues/list/components/issue_card_time_info.vue';
+import { IssuableStatus } from '~/issues/constants';
+import {
+ CREATED_DESC,
+ PAGE_SIZE,
+ PARAM_STATE,
+ UPDATED_DESC,
+ urlSortParams,
+} from '~/issues/list/constants';
+import setSortPreferenceMutation from '~/issues/list/queries/set_sort_preference.mutation.graphql';
+import {
+ convertToApiParams,
+ convertToSearchQuery,
+ convertToUrlParams,
+ getFilterTokens,
+ getInitialPageParams,
+ getSortKey,
+ getSortOptions,
+ isSortKey,
+} from '~/issues/list/utils';
+import axios from '~/lib/utils/axios_utils';
+import { scrollUp } from '~/lib/utils/scroll_utils';
+import { getParameterByName } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
+import {
+ TOKEN_TITLE_ASSIGNEE,
+ TOKEN_TITLE_AUTHOR,
+ TOKEN_TYPE_ASSIGNEE,
+ TOKEN_TYPE_AUTHOR,
+} from '~/vue_shared/components/filtered_search_bar/constants';
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/constants';
+const UserToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/user_token.vue');
+
export default {
i18n: {
calendarButtonText: __('Subscribe to calendar'),
+ closed: __('CLOSED'),
+ closedMoved: __('CLOSED (MOVED)'),
emptyStateTitle: __('Please select at least one filter to see results'),
+ errorFetchingIssues: __('An error occurred while loading issues'),
rssButtonText: __('Subscribe to RSS feed'),
searchInputPlaceholder: __('Search or filter results...'),
},
@@ -16,29 +53,237 @@ export default {
GlButton,
GlEmptyState,
IssuableList,
+ IssueCardStatistics,
+ IssueCardTimeInfo,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
},
- inject: ['calendarPath', 'emptyStateSvgPath', 'isSignedIn', 'rssPath'],
+ inject: [
+ 'calendarPath',
+ 'emptyStateSvgPath',
+ 'hasBlockedIssuesFeature',
+ 'hasIssuableHealthStatusFeature',
+ 'hasIssueWeightsFeature',
+ 'hasScopedLabelsFeature',
+ 'initialSort',
+ 'isPublicVisibilityRestricted',
+ 'isSignedIn',
+ 'rssPath',
+ ],
data() {
+ const state = getParameterByName(PARAM_STATE);
+
+ const defaultSortKey = state === IssuableStates.Closed ? UPDATED_DESC : CREATED_DESC;
+ const dashboardSortKey = getSortKey(this.initialSort);
+ const graphQLSortKey =
+ isSortKey(this.initialSort?.toUpperCase()) && this.initialSort.toUpperCase();
+
+ // The initial sort is an old enum value when it is saved on the dashboard issues page.
+ // The initial sort is a GraphQL enum value when it is saved on the Vue issues list page.
+ const sortKey = dashboardSortKey || graphQLSortKey || defaultSortKey;
+
return {
+ filterTokens: getFilterTokens(window.location.search),
issues: [],
- searchTokens: [],
- sortOptions: [],
- state: IssuableStates.Opened,
+ issuesError: null,
+ pageInfo: {},
+ pageParams: getInitialPageParams(),
+ sortKey,
+ state: state || IssuableStates.Opened,
};
},
+ apollo: {
+ issues: {
+ query: getIssuesQuery,
+ variables() {
+ return {
+ hideUsers: this.isPublicVisibilityRestricted && !this.isSignedIn,
+ isSignedIn: this.isSignedIn,
+ search: this.searchQuery,
+ sort: this.sortKey,
+ state: this.state,
+ ...this.pageParams,
+ ...this.apiFilterParams,
+ };
+ },
+ update(data) {
+ return data.issues.nodes ?? [];
+ },
+ result({ data }) {
+ this.pageInfo = data?.issues.pageInfo ?? {};
+ },
+ error(error) {
+ this.issuesError = this.$options.i18n.errorFetchingIssues;
+ Sentry.captureException(error);
+ },
+ debounce: 200,
+ },
+ },
+ computed: {
+ apiFilterParams() {
+ return convertToApiParams(this.filterTokens);
+ },
+ searchQuery() {
+ return convertToSearchQuery(this.filterTokens);
+ },
+ searchTokens() {
+ const preloadedUsers = [];
+
+ if (gon.current_user_id) {
+ preloadedUsers.push({
+ id: gon.current_user_id,
+ name: gon.current_user_fullname,
+ username: gon.current_username,
+ avatar_url: gon.current_user_avatar_url,
+ });
+ }
+
+ const tokens = [
+ {
+ type: TOKEN_TYPE_ASSIGNEE,
+ title: TOKEN_TITLE_ASSIGNEE,
+ icon: 'user',
+ token: UserToken,
+ fetchUsers: this.fetchUsers,
+ preloadedUsers,
+ recentSuggestionsStorageKey: 'dashboard-issues-recent-tokens-assignee',
+ },
+ {
+ type: TOKEN_TYPE_AUTHOR,
+ title: TOKEN_TITLE_AUTHOR,
+ icon: 'pencil',
+ token: UserToken,
+ fetchUsers: this.fetchUsers,
+ defaultUsers: [],
+ preloadedUsers,
+ recentSuggestionsStorageKey: 'dashboard-issues-recent-tokens-author',
+ },
+ ];
+
+ return tokens;
+ },
+ showPaginationControls() {
+ return this.issues.length > 0 && (this.pageInfo.hasNextPage || this.pageInfo.hasPreviousPage);
+ },
+ sortOptions() {
+ return getSortOptions({
+ hasBlockedIssuesFeature: this.hasBlockedIssuesFeature,
+ hasIssuableHealthStatusFeature: this.hasIssuableHealthStatusFeature,
+ hasIssueWeightsFeature: this.hasIssueWeightsFeature,
+ });
+ },
+ urlFilterParams() {
+ return convertToUrlParams(this.filterTokens);
+ },
+ urlParams() {
+ return {
+ search: this.searchQuery,
+ sort: urlSortParams[this.sortKey],
+ state: this.state,
+ ...this.urlFilterParams,
+ };
+ },
+ },
+ methods: {
+ fetchUsers(search) {
+ return axios.get('/-/autocomplete/users.json', { params: { active: true, search } });
+ },
+ getStatus(issue) {
+ if (issue.state === IssuableStatus.Closed && issue.moved) {
+ return this.$options.i18n.closedMoved;
+ }
+ if (issue.state === IssuableStatus.Closed) {
+ return this.$options.i18n.closed;
+ }
+ return undefined;
+ },
+ handleClickTab(state) {
+ if (this.state === state) {
+ return;
+ }
+ this.state = state;
+ this.pageParams = getInitialPageParams();
+ },
+ handleDismissAlert() {
+ this.issuesError = null;
+ },
+ handleFilter(tokens) {
+ this.filterTokens = tokens;
+ this.pageParams = getInitialPageParams();
+ },
+ handleNextPage() {
+ this.pageParams = {
+ afterCursor: this.pageInfo.endCursor,
+ firstPageSize: PAGE_SIZE,
+ };
+ scrollUp();
+ },
+ handlePreviousPage() {
+ this.pageParams = {
+ beforeCursor: this.pageInfo.startCursor,
+ lastPageSize: PAGE_SIZE,
+ };
+ scrollUp();
+ },
+ handleSort(sortKey) {
+ if (this.sortKey === sortKey) {
+ return;
+ }
+
+ this.sortKey = sortKey;
+ this.pageParams = getInitialPageParams();
+
+ if (this.isSignedIn) {
+ this.saveSortPreference(sortKey);
+ }
+ },
+ saveSortPreference(sortKey) {
+ this.$apollo
+ .mutate({
+ mutation: setSortPreferenceMutation,
+ variables: { input: { issuesSort: sortKey } },
+ })
+ .then(({ data }) => {
+ if (data.userPreferencesUpdate.errors.length) {
+ throw new Error(data.userPreferencesUpdate.errors);
+ }
+ })
+ .catch((error) => {
+ Sentry.captureException(error);
+ });
+ },
+ },
};
</script>
<template>
<issuable-list
+ :current-tab="state"
+ :error="issuesError"
+ :has-next-page="pageInfo.hasNextPage"
+ :has-previous-page="pageInfo.hasPreviousPage"
+ :has-scoped-labels-feature="hasScopedLabelsFeature"
+ :initial-filter-value="filterTokens"
+ :initial-sort-by="sortKey"
+ :issuables="issues"
+ :issuables-loading="$apollo.queries.issues.loading"
namespace="dashboard"
recent-searches-storage-key="issues"
:search-input-placeholder="$options.i18n.searchInputPlaceholder"
:search-tokens="searchTokens"
+ :show-pagination-controls="showPaginationControls"
+ show-work-item-type-icon
:sort-options="sortOptions"
- :issuables="issues"
:tabs="$options.IssuableListTabs"
- :current-tab="state"
+ :url-params="urlParams"
+ use-keyset-pagination
+ @click-tab="handleClickTab"
+ @dismiss-alert="handleDismissAlert"
+ @filter="handleFilter"
+ @next-page="handleNextPage"
+ @previous-page="handlePreviousPage"
+ @sort="handleSort"
>
<template #nav-actions>
<gl-button :href="rssPath" icon="rss">
@@ -49,6 +294,18 @@ export default {
</gl-button>
</template>
+ <template #timeframe="{ issuable = {} }">
+ <issue-card-time-info :issue="issuable" />
+ </template>
+
+ <template #status="{ issuable = {} }">
+ {{ getStatus(issuable) }}
+ </template>
+
+ <template #statistics="{ issuable = {} }">
+ <issue-card-statistics :issue="issuable" />
+ </template>
+
<template #empty-state>
<gl-empty-state :svg-path="emptyStateSvgPath" :title="$options.i18n.emptyStateTitle" />
</template>
diff --git a/app/assets/javascripts/issues/dashboard/index.js b/app/assets/javascripts/issues/dashboard/index.js
index a1ae3b93f7d..e3e5cc614cb 100644
--- a/app/assets/javascripts/issues/dashboard/index.js
+++ b/app/assets/javascripts/issues/dashboard/index.js
@@ -1,4 +1,6 @@
import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
import { parseBoolean } from '~/lib/utils/common_utils';
import IssuesDashboardApp from './components/issues_dashboard_app.vue';
@@ -9,14 +11,36 @@ export function mountIssuesDashboardApp() {
return null;
}
- const { calendarPath, emptyStateSvgPath, isSignedIn, rssPath } = el.dataset;
+ Vue.use(VueApollo);
+
+ const {
+ calendarPath,
+ emptyStateSvgPath,
+ hasBlockedIssuesFeature,
+ hasIssuableHealthStatusFeature,
+ hasIssueWeightsFeature,
+ hasScopedLabelsFeature,
+ initialSort,
+ isPublicVisibilityRestricted,
+ isSignedIn,
+ rssPath,
+ } = el.dataset;
return new Vue({
el,
name: 'IssuesDashboardRoot',
+ apolloProvider: new VueApollo({
+ defaultClient: createDefaultClient(),
+ }),
provide: {
calendarPath,
emptyStateSvgPath,
+ hasBlockedIssuesFeature: parseBoolean(hasBlockedIssuesFeature),
+ hasIssuableHealthStatusFeature: parseBoolean(hasIssuableHealthStatusFeature),
+ hasIssueWeightsFeature: parseBoolean(hasIssueWeightsFeature),
+ hasScopedLabelsFeature: parseBoolean(hasScopedLabelsFeature),
+ initialSort,
+ isPublicVisibilityRestricted: parseBoolean(isPublicVisibilityRestricted),
isSignedIn: parseBoolean(isSignedIn),
rssPath,
},
diff --git a/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql b/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql
new file mode 100644
index 00000000000..8ffcb456755
--- /dev/null
+++ b/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql
@@ -0,0 +1,36 @@
+#import "~/graphql_shared/fragments/page_info.fragment.graphql"
+#import "~/issues/list/queries/issue.fragment.graphql"
+
+query getDashboardIssues(
+ $hideUsers: Boolean = false
+ $isSignedIn: Boolean = false
+ $search: String
+ $sort: IssueSort
+ $state: IssuableState
+ $assigneeUsernames: [String!]
+ $authorUsername: String
+ $afterCursor: String
+ $beforeCursor: String
+ $firstPageSize: Int
+ $lastPageSize: Int
+) {
+ issues(
+ search: $search
+ sort: $sort
+ state: $state
+ assigneeUsernames: $assigneeUsernames
+ authorUsername: $authorUsername
+ after: $afterCursor
+ before: $beforeCursor
+ first: $firstPageSize
+ last: $lastPageSize
+ ) {
+ nodes {
+ ...IssueFragment
+ reference(full: true)
+ }
+ pageInfo {
+ ...PageInfo
+ }
+ }
+}
diff --git a/app/assets/javascripts/issues/index.js b/app/assets/javascripts/issues/index.js
index a785790169d..e3716d0e111 100644
--- a/app/assets/javascripts/issues/index.js
+++ b/app/assets/javascripts/issues/index.js
@@ -1,5 +1,6 @@
import $ from 'jquery';
import IssuableForm from 'ee_else_ce/issuable/issuable_form';
+import IssuableLabelSelector from '~/issuable/issuable_label_selector';
import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import GLForm from '~/gl_form';
@@ -39,6 +40,7 @@ export function initFilteredSearchServiceDesk() {
export function initForm() {
new GLForm($('.issue-form')); // eslint-disable-line no-new
new IssuableForm($('.issue-form')); // eslint-disable-line no-new
+ IssuableLabelSelector();
new IssuableTemplateSelectors({ warnTemplateOverride: true }); // eslint-disable-line no-new
new LabelsSelect(); // eslint-disable-line no-new
new ShortcutsNavigation(); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/issues/issue.js b/app/assets/javascripts/issues/issue.js
index a9321cf200d..de1c689e590 100644
--- a/app/assets/javascripts/issues/issue.js
+++ b/app/assets/javascripts/issues/issue.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
import { joinPaths } from '~/lib/utils/url_utility';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import { EVENT_ISSUABLE_VUE_APP_CHANGE } from '~/issuable/constants';
import axios from '~/lib/utils/axios_utils';
import { addDelimiter } from '~/lib/utils/text_utility';
@@ -68,7 +68,7 @@ export default class Issue {
this.createMergeRequestDropdown.checkAbilityToCreateBranch();
}
} else {
- createFlash({
+ createAlert({
message: issueFailMessage,
});
}
@@ -105,7 +105,7 @@ export default class Issue {
}
})
.catch(() =>
- createFlash({
+ createAlert({
message: __('Failed to load related branches'),
}),
);
diff --git a/app/assets/javascripts/issues/list/components/empty_state_with_any_issues.vue b/app/assets/javascripts/issues/list/components/empty_state_with_any_issues.vue
new file mode 100644
index 00000000000..8aece24de0c
--- /dev/null
+++ b/app/assets/javascripts/issues/list/components/empty_state_with_any_issues.vue
@@ -0,0 +1,53 @@
+<script>
+import { GlButton, GlEmptyState } from '@gitlab/ui';
+import { i18n } from '../constants';
+
+export default {
+ i18n,
+ components: {
+ GlButton,
+ GlEmptyState,
+ },
+ inject: ['emptyStateSvgPath', 'newIssuePath', 'showNewIssueLink'],
+ props: {
+ hasSearch: {
+ type: Boolean,
+ required: true,
+ },
+ isOpenTab: {
+ type: Boolean,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-empty-state
+ v-if="hasSearch"
+ :description="$options.i18n.noSearchResultsDescription"
+ :title="$options.i18n.noSearchResultsTitle"
+ :svg-path="emptyStateSvgPath"
+ >
+ <template #actions>
+ <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm">
+ {{ $options.i18n.newIssueLabel }}
+ </gl-button>
+ </template>
+ </gl-empty-state>
+
+ <gl-empty-state
+ v-else-if="isOpenTab"
+ :description="$options.i18n.noOpenIssuesDescription"
+ :title="$options.i18n.noOpenIssuesTitle"
+ :svg-path="emptyStateSvgPath"
+ >
+ <template #actions>
+ <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm">
+ {{ $options.i18n.newIssueLabel }}
+ </gl-button>
+ </template>
+ </gl-empty-state>
+
+ <gl-empty-state v-else :title="$options.i18n.noClosedIssuesTitle" :svg-path="emptyStateSvgPath" />
+</template>
diff --git a/app/assets/javascripts/issues/list/components/empty_state_without_any_issues.vue b/app/assets/javascripts/issues/list/components/empty_state_without_any_issues.vue
new file mode 100644
index 00000000000..5a37751410a
--- /dev/null
+++ b/app/assets/javascripts/issues/list/components/empty_state_without_any_issues.vue
@@ -0,0 +1,110 @@
+<script>
+import { GlButton, GlEmptyState, GlLink, GlSprintf } from '@gitlab/ui';
+import { helpPagePath } from '~/helpers/help_page_helper';
+import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue';
+import { i18n } from '../constants';
+import NewIssueDropdown from './new_issue_dropdown.vue';
+
+export default {
+ i18n,
+ issuesHelpPagePath: helpPagePath('user/project/issues/index'),
+ components: {
+ CsvImportExportButtons,
+ GlButton,
+ GlEmptyState,
+ GlLink,
+ GlSprintf,
+ NewIssueDropdown,
+ },
+ inject: [
+ 'canCreateProjects',
+ 'emptyStateSvgPath',
+ 'isSignedIn',
+ 'jiraIntegrationPath',
+ 'newIssuePath',
+ 'newProjectPath',
+ 'showNewIssueLink',
+ 'signInPath',
+ ],
+ props: {
+ currentTabCount: {
+ type: Number,
+ required: false,
+ default: undefined,
+ },
+ exportCsvPathWithQuery: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ showCsvButtons: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ showNewIssueDropdown: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+};
+</script>
+
+<template>
+ <div v-if="isSignedIn">
+ <gl-empty-state :title="$options.i18n.noIssuesTitle" :svg-path="emptyStateSvgPath">
+ <template #description>
+ <gl-link :href="$options.issuesHelpPagePath">
+ {{ $options.i18n.noIssuesDescription }}
+ </gl-link>
+ <p v-if="canCreateProjects">
+ <strong>{{ $options.i18n.noGroupIssuesSignedInDescription }}</strong>
+ </p>
+ </template>
+ <template #actions>
+ <gl-button v-if="canCreateProjects" :href="newProjectPath" variant="confirm">
+ {{ $options.i18n.newProjectLabel }}
+ </gl-button>
+ <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm">
+ {{ $options.i18n.newIssueLabel }}
+ </gl-button>
+ <csv-import-export-buttons
+ v-if="showCsvButtons"
+ class="gl-w-full gl-sm-w-auto gl-sm-mr-3"
+ :export-csv-path="exportCsvPathWithQuery"
+ :issuable-count="currentTabCount"
+ />
+ <new-issue-dropdown v-if="showNewIssueDropdown" class="gl-align-self-center" />
+ </template>
+ </gl-empty-state>
+ <hr />
+ <p class="gl-text-center gl-font-weight-bold gl-mb-0">
+ {{ $options.i18n.jiraIntegrationTitle }}
+ </p>
+ <p class="gl-text-center gl-mb-0">
+ <gl-sprintf :message="$options.i18n.jiraIntegrationMessage">
+ <template #jiraDocsLink="{ content }">
+ <gl-link :href="jiraIntegrationPath">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </p>
+ <p class="gl-text-center gl-text-secondary">
+ {{ $options.i18n.jiraIntegrationSecondaryMessage }}
+ </p>
+ </div>
+
+ <gl-empty-state
+ v-else
+ :title="$options.i18n.noIssuesTitle"
+ :svg-path="emptyStateSvgPath"
+ :primary-button-text="$options.i18n.noIssuesSignedOutButtonText"
+ :primary-button-link="signInPath"
+ >
+ <template #description>
+ <gl-link :href="$options.issuesHelpPagePath">
+ {{ $options.i18n.noIssuesDescription }}
+ </gl-link>
+ </template>
+ </gl-empty-state>
+</template>
diff --git a/app/assets/javascripts/issues/list/components/issue_card_statistics.vue b/app/assets/javascripts/issues/list/components/issue_card_statistics.vue
new file mode 100644
index 00000000000..2d00c3e549d
--- /dev/null
+++ b/app/assets/javascripts/issues/list/components/issue_card_statistics.vue
@@ -0,0 +1,56 @@
+<script>
+import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
+import { i18n } from '../constants';
+
+export default {
+ i18n,
+ components: {
+ GlIcon,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ issue: {
+ type: Object,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <ul class="gl-display-contents">
+ <li
+ v-if="issue.mergeRequestsCount"
+ v-gl-tooltip
+ class="gl-display-none gl-sm-display-block gl-mr-3"
+ :title="$options.i18n.relatedMergeRequests"
+ data-testid="merge-requests"
+ >
+ <gl-icon name="merge-request" />
+ {{ issue.mergeRequestsCount }}
+ </li>
+ <li
+ v-if="issue.upvotes"
+ v-gl-tooltip
+ class="gl-display-none gl-sm-display-block gl-mr-3"
+ :title="$options.i18n.upvotes"
+ data-testid="issuable-upvotes"
+ >
+ <gl-icon name="thumb-up" />
+ {{ issue.upvotes }}
+ </li>
+ <li
+ v-if="issue.downvotes"
+ v-gl-tooltip
+ class="gl-display-none gl-sm-display-block gl-mr-3"
+ :title="$options.i18n.downvotes"
+ data-testid="issuable-downvotes"
+ >
+ <gl-icon name="thumb-down" />
+ {{ issue.downvotes }}
+ </li>
+ <slot></slot>
+ </ul>
+</template>
diff --git a/app/assets/javascripts/issues/list/components/issues_list_app.vue b/app/assets/javascripts/issues/list/components/issues_list_app.vue
index 64de4b1947b..12a83f06453 100644
--- a/app/assets/javascripts/issues/list/components/issues_list_app.vue
+++ b/app/assets/javascripts/issues/list/components/issues_list_app.vue
@@ -1,19 +1,12 @@
<script>
-import {
- GlButton,
- GlEmptyState,
- GlFilteredSearchToken,
- GlIcon,
- GlLink,
- GlSprintf,
- GlTooltipDirective,
-} from '@gitlab/ui';
+import { GlButton, GlFilteredSearchToken, GlTooltipDirective } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import fuzzaldrinPlus from 'fuzzaldrin-plus';
+import IssueCardStatistics from 'ee_else_ce/issues/list/components/issue_card_statistics.vue';
import IssueCardTimeInfo from 'ee_else_ce/issues/list/components/issue_card_time_info.vue';
import getIssuesQuery from 'ee_else_ce/issues/list/queries/get_issues.query.graphql';
import getIssuesCountsQuery from 'ee_else_ce/issues/list/queries/get_issues_counts.query.graphql';
-import createFlash, { FLASH_TYPES } from '~/flash';
+import { createAlert, VARIANT_INFO } from '~/flash';
import { TYPE_USER } from '~/graphql_shared/constants';
import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
import { ITEM_TYPE } from '~/groups/constants';
@@ -24,11 +17,11 @@ import axios from '~/lib/utils/axios_utils';
import { isPositiveInteger } from '~/lib/utils/number_utils';
import { scrollUp } from '~/lib/utils/scroll_utils';
import { getParameterByName, joinPaths } from '~/lib/utils/url_utility';
-import { helpPagePath } from '~/helpers/help_page_helper';
import {
- DEFAULT_NONE_ANY,
FILTERED_SEARCH_TERM,
- OPERATOR_IS_ONLY,
+ OPERATORS_IS,
+ OPERATORS_IS_NOT,
+ OPERATORS_IS_NOT_OR,
TOKEN_TITLE_ASSIGNEE,
TOKEN_TITLE_AUTHOR,
TOKEN_TITLE_CONFIDENTIAL,
@@ -38,9 +31,8 @@ import {
TOKEN_TITLE_MY_REACTION,
TOKEN_TITLE_ORGANIZATION,
TOKEN_TITLE_RELEASE,
+ TOKEN_TITLE_SEARCH_WITHIN,
TOKEN_TITLE_TYPE,
- OPERATOR_IS_NOT_OR,
- OPERATOR_IS_AND_IS_NOT,
TOKEN_TYPE_ASSIGNEE,
TOKEN_TYPE_AUTHOR,
TOKEN_TYPE_CONFIDENTIAL,
@@ -50,6 +42,7 @@ import {
TOKEN_TYPE_MY_REACTION,
TOKEN_TYPE_ORGANIZATION,
TOKEN_TYPE_RELEASE,
+ TOKEN_TYPE_SEARCH_WITHIN,
TOKEN_TYPE_TYPE,
} from '~/vue_shared/components/filtered_search_bar/constants';
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
@@ -70,11 +63,9 @@ import {
PARAM_SORT,
PARAM_STATE,
RELATIVE_POSITION_ASC,
- TYPE_TOKEN_TASK_OPTION,
UPDATED_DESC,
urlSortParams,
} from '../constants';
-
import eventHub from '../eventhub';
import reorderIssuesMutation from '../queries/reorder_issues.mutation.graphql';
import searchLabelsQuery from '../queries/search_labels.query.graphql';
@@ -91,10 +82,11 @@ import {
getSortOptions,
isSortKey,
} from '../utils';
+import EmptyStateWithAnyIssues from './empty_state_with_any_issues.vue';
+import EmptyStateWithoutAnyIssues from './empty_state_without_any_issues.vue';
import NewIssueDropdown from './new_issue_dropdown.vue';
-const AuthorToken = () =>
- import('~/vue_shared/components/filtered_search_bar/tokens/author_token.vue');
+const UserToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/user_token.vue');
const EmojiToken = () =>
import('~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue');
const LabelToken = () =>
@@ -113,13 +105,12 @@ export default {
IssuableListTabs,
components: {
CsvImportExportButtons,
+ EmptyStateWithAnyIssues,
+ EmptyStateWithoutAnyIssues,
GlButton,
- GlEmptyState,
- GlIcon,
- GlLink,
- GlSprintf,
IssuableByEmail,
IssuableList,
+ IssueCardStatistics,
IssueCardTimeInfo,
NewIssueDropdown,
},
@@ -131,15 +122,14 @@ export default {
'autocompleteAwardEmojisPath',
'calendarPath',
'canBulkUpdate',
- 'canCreateProjects',
'canReadCrmContact',
'canReadCrmOrganization',
- 'emptyStateSvgPath',
'exportCsvPath',
'fullPath',
'hasAnyIssues',
'hasAnyProjects',
'hasBlockedIssuesFeature',
+ 'hasIssuableHealthStatusFeature',
'hasIssueWeightsFeature',
'hasScopedLabelsFeature',
'initialEmail',
@@ -149,13 +139,10 @@ export default {
'isProject',
'isPublicVisibilityRestricted',
'isSignedIn',
- 'jiraIntegrationPath',
'newIssuePath',
- 'newProjectPath',
'releasesPath',
'rssPath',
'showNewIssueLink',
- 'signInPath',
],
props: {
eeSearchTokens: {
@@ -163,6 +150,21 @@ export default {
required: false,
default: () => [],
},
+ eeTypeTokenOptions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ eeWorkItemTypes: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ eeIsOkrsEnabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -189,10 +191,7 @@ export default {
return data[this.namespace]?.issues.nodes ?? [];
},
result({ data }) {
- if (!data) {
- return;
- }
- this.pageInfo = data[this.namespace]?.issues.pageInfo ?? {};
+ this.pageInfo = data?.[this.namespace]?.issues.pageInfo ?? {};
this.exportCsvPathWithQuery = this.getExportCsvPathWithQuery();
},
error(error) {
@@ -239,24 +238,27 @@ export default {
state: this.state,
...this.pageParams,
...this.apiFilterParams,
- types: this.apiFilterParams.types || defaultWorkItemTypes,
+ types: this.apiFilterParams.types || this.defaultWorkItemTypes,
};
},
namespace() {
return this.isProject ? ITEM_TYPE.PROJECT : ITEM_TYPE.GROUP;
},
+ defaultWorkItemTypes() {
+ return [...defaultWorkItemTypes, ...this.eeWorkItemTypes];
+ },
typeTokenOptions() {
- return defaultTypeTokenOptions.concat(TYPE_TOKEN_TASK_OPTION);
+ return [...defaultTypeTokenOptions, ...this.eeTypeTokenOptions];
},
hasOrFeature() {
return this.glFeatures.orIssuableQueries;
},
hasSearch() {
- return (
+ return Boolean(
this.searchQuery ||
- Object.keys(this.urlFilterParams).length ||
- this.pageParams.afterCursor ||
- this.pageParams.beforeCursor
+ Object.keys(this.urlFilterParams).length ||
+ this.pageParams.afterCursor ||
+ this.pageParams.beforeCursor,
);
},
isBulkEditButtonDisabled() {
@@ -284,13 +286,13 @@ export default {
return convertToUrlParams(this.filterTokens);
},
searchQuery() {
- return convertToSearchQuery(this.filterTokens) || undefined;
+ return convertToSearchQuery(this.filterTokens);
},
searchTokens() {
- const preloadedAuthors = [];
+ const preloadedUsers = [];
if (gon.current_user_id) {
- preloadedAuthors.push({
+ preloadedUsers.push({
id: convertToGraphQLId(TYPE_USER, gon.current_user_id),
name: gon.current_user_fullname,
username: gon.current_username,
@@ -300,28 +302,41 @@ export default {
const tokens = [
{
+ type: TOKEN_TYPE_SEARCH_WITHIN,
+ title: TOKEN_TITLE_SEARCH_WITHIN,
+ icon: 'search',
+ token: GlFilteredSearchToken,
+ unique: true,
+ operators: OPERATORS_IS,
+ options: [
+ { icon: 'title', value: 'TITLE', title: this.$options.i18n.titles },
+ {
+ icon: 'text-description',
+ value: 'DESCRIPTION',
+ title: this.$options.i18n.descriptions,
+ },
+ ],
+ },
+ {
type: TOKEN_TYPE_AUTHOR,
title: TOKEN_TITLE_AUTHOR,
icon: 'pencil',
- token: AuthorToken,
- dataType: 'user',
- unique: true,
- defaultAuthors: [],
- fetchAuthors: this.fetchUsers,
+ token: UserToken,
+ defaultUsers: [],
+ operators: this.hasOrFeature ? OPERATORS_IS_NOT_OR : OPERATORS_IS_NOT,
+ fetchUsers: this.fetchUsers,
recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-author`,
- preloadedAuthors,
+ preloadedUsers,
},
{
type: TOKEN_TYPE_ASSIGNEE,
title: TOKEN_TITLE_ASSIGNEE,
icon: 'user',
- token: AuthorToken,
- dataType: 'user',
- defaultAuthors: DEFAULT_NONE_ANY,
- operators: this.hasOrFeature ? OPERATOR_IS_NOT_OR : OPERATOR_IS_AND_IS_NOT,
- fetchAuthors: this.fetchUsers,
+ token: UserToken,
+ operators: this.hasOrFeature ? OPERATORS_IS_NOT_OR : OPERATORS_IS_NOT,
+ fetchUsers: this.fetchUsers,
recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-assignee`,
- preloadedAuthors,
+ preloadedUsers,
},
{
type: TOKEN_TYPE_MILESTONE,
@@ -337,7 +352,6 @@ export default {
title: TOKEN_TITLE_LABEL,
icon: 'labels',
token: LabelToken,
- defaultLabels: DEFAULT_NONE_ANY,
fetchLabels: this.fetchLabels,
recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-label`,
},
@@ -378,7 +392,7 @@ export default {
icon: 'eye-slash',
token: GlFilteredSearchToken,
unique: true,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
options: [
{ icon: 'eye-slash', value: 'yes', title: this.$options.i18n.confidentialYes },
{ icon: 'eye', value: 'no', title: this.$options.i18n.confidentialNo },
@@ -394,9 +408,8 @@ export default {
token: CrmContactToken,
fullPath: this.fullPath,
isProject: this.isProject,
- defaultContacts: DEFAULT_NONE_ANY,
recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-crm-contacts`,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
unique: true,
});
}
@@ -409,9 +422,8 @@ export default {
token: CrmOrganizationToken,
fullPath: this.fullPath,
isProject: this.isProject,
- defaultOrganizations: DEFAULT_NONE_ANY,
recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-crm-organizations`,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
unique: true,
});
}
@@ -428,11 +440,14 @@ export default {
return this.issues.length > 0 && (this.pageInfo.hasNextPage || this.pageInfo.hasPreviousPage);
},
showPageSizeControls() {
- /** only show page size controls when the tab count is greater than the default/minimum page size control i.e 20 in this case */
return this.currentTabCount > PAGE_SIZE;
},
sortOptions() {
- return getSortOptions(this.hasIssueWeightsFeature, this.hasBlockedIssuesFeature);
+ return getSortOptions({
+ hasBlockedIssuesFeature: this.hasBlockedIssuesFeature,
+ hasIssuableHealthStatusFeature: this.hasIssuableHealthStatusFeature,
+ hasIssueWeightsFeature: this.hasIssueWeightsFeature,
+ });
},
tabCounts() {
const { openedIssues, closedIssues, allIssues } = this.issuesCounts;
@@ -457,10 +472,7 @@ export default {
page_before: this.pageParams.beforeCursor ?? undefined,
};
},
- issuesHelpPagePath() {
- return helpPagePath('user/project/issues/index');
- },
- shouldDisableSomeFilters() {
+ shouldDisableTextSearch() {
return this.isAnonymousSearchDisabled && !this.isSignedIn;
},
},
@@ -482,18 +494,17 @@ export default {
eventHub.$off('issuables:toggleBulkEdit', this.toggleBulkEditSidebar);
},
methods: {
- fetchWithCache(path, cacheName, searchKey, search, wrapData = false) {
+ fetchWithCache(path, cacheName, searchKey, search) {
if (this.cache[cacheName]) {
const data = search
? fuzzaldrinPlus.filter(this.cache[cacheName], search, { key: searchKey })
: this.cache[cacheName].slice(0, MAX_LIST_SIZE);
- return wrapData ? Promise.resolve({ data }) : Promise.resolve(data);
+ return Promise.resolve(data);
}
return axios.get(path).then(({ data }) => {
this.cache[cacheName] = data;
- const result = data.slice(0, MAX_LIST_SIZE);
- return wrapData ? { data: result } : result;
+ return data.slice(0, MAX_LIST_SIZE);
});
},
fetchEmojis(search) {
@@ -554,14 +565,10 @@ export default {
},
async handleBulkUpdateClick() {
if (!this.hasInitBulkEdit) {
- const bulkUpdateSidebar = await import('~/issuable/bulk_update_sidebar');
+ const bulkUpdateSidebar = await import('~/issuable');
bulkUpdateSidebar.initBulkUpdateSidebar('issuable_');
- bulkUpdateSidebar.initStatusDropdown();
- bulkUpdateSidebar.initSubscriptionsDropdown();
- bulkUpdateSidebar.initMoveIssuesButton();
- const usersSelect = await import('~/users_select');
- const UsersSelect = usersSelect.default;
+ const UsersSelect = (await import('~/users_select')).default;
new UsersSelect(); // eslint-disable-line no-new
this.hasInitBulkEdit = true;
@@ -570,19 +577,20 @@ export default {
eventHub.$emit('issuables:enableBulkEdit');
},
handleClickTab(state) {
- if (this.state !== state) {
- this.pageParams = getInitialPageParams(this.pageSize);
+ if (this.state === state) {
+ return;
}
+
this.state = state;
+ this.pageParams = getInitialPageParams(this.pageSize);
this.$router.push({ query: this.urlParams });
},
handleDismissAlert() {
this.issuesError = null;
},
- handleFilter(filter) {
- this.setFilterTokens(filter);
-
+ handleFilter(tokens) {
+ this.setFilterTokens(tokens);
this.pageParams = getInitialPageParams(this.pageSize);
this.$router.push({ query: this.urlParams });
@@ -642,15 +650,17 @@ export default {
});
},
handleSort(sortKey) {
+ if (this.sortKey === sortKey) {
+ return;
+ }
+
if (this.isIssueRepositioningDisabled && sortKey === RELATIVE_POSITION_ASC) {
this.showIssueRepositioningMessage();
return;
}
- if (this.sortKey !== sortKey) {
- this.pageParams = getInitialPageParams(this.pageSize);
- }
this.sortKey = sortKey;
+ this.pageParams = getInitialPageParams(this.pageSize);
if (this.isSignedIn) {
this.saveSortPreference(sortKey);
@@ -673,49 +683,36 @@ export default {
Sentry.captureException(error);
});
},
- setFilterTokens(filtersArg) {
- const filters = this.removeDisabledSearchTerms(filtersArg);
+ setFilterTokens(tokens) {
+ this.filterTokens = this.removeDisabledSearchTerms(tokens);
- this.filterTokens = filters;
-
- // If we filtered something out, let's show a warning message
- if (filters.length < filtersArg.length) {
+ if (this.filterTokens.length < tokens.length) {
this.showAnonymousSearchingMessage();
}
},
removeDisabledSearchTerms(filters) {
- // If we shouldn't disable anything, let's return the same thing
- if (!this.shouldDisableSomeFilters) {
- return filters;
- }
-
- const filtersWithoutSearchTerms = filters.filter(
- (token) => !(token.type === FILTERED_SEARCH_TERM && token.value?.data),
- );
-
- return filtersWithoutSearchTerms;
+ return this.shouldDisableTextSearch
+ ? filters.filter((token) => !(token.type === FILTERED_SEARCH_TERM && token.value?.data))
+ : filters;
},
showAnonymousSearchingMessage() {
- createFlash({
+ createAlert({
message: this.$options.i18n.anonymousSearchingMessage,
- type: FLASH_TYPES.NOTICE,
+ variant: VARIANT_INFO,
});
},
showIssueRepositioningMessage() {
- createFlash({
+ createAlert({
message: this.$options.i18n.issueRepositioningMessage,
- type: FLASH_TYPES.NOTICE,
+ variant: VARIANT_INFO,
});
},
toggleBulkEditSidebar(showBulkEditSidebar) {
this.showBulkEditSidebar = showBulkEditSidebar;
},
handlePageSizeChange(newPageSize) {
- /** make sure the page number is preserved so that the current context is not lost* */
- const lastPageSize = getParameterByName(PARAM_LAST_PAGE_SIZE);
- const pageNumberSize = lastPageSize ? 'lastPageSize' : 'firstPageSize';
- /** depending upon what page or page size we are dynamically set pageParams * */
- this.pageParams[pageNumberSize] = newPageSize;
+ const pageParam = getParameterByName(PARAM_LAST_PAGE_SIZE) ? 'lastPageSize' : 'firstPageSize';
+ this.pageParams[pageParam] = newPageSize;
this.pageSize = newPageSize;
scrollUp();
@@ -724,16 +721,14 @@ export default {
updateData(sortValue) {
const firstPageSize = getParameterByName(PARAM_FIRST_PAGE_SIZE);
const lastPageSize = getParameterByName(PARAM_LAST_PAGE_SIZE);
- const pageAfter = getParameterByName(PARAM_PAGE_AFTER);
- const pageBefore = getParameterByName(PARAM_PAGE_BEFORE);
const state = getParameterByName(PARAM_STATE);
const defaultSortKey = state === IssuableStates.Closed ? UPDATED_DESC : CREATED_DESC;
const dashboardSortKey = getSortKey(sortValue);
const graphQLSortKey = isSortKey(sortValue?.toUpperCase()) && sortValue.toUpperCase();
- // The initial sort is an old enum value when it is saved on the dashboard issues page.
- // The initial sort is a GraphQL enum value when it is saved on the Vue issues list page.
+ // The initial sort is an old enum value when it is saved on the Haml dashboard issues page.
+ // The initial sort is a GraphQL enum value when it is saved on the Vue group/project issues page.
let sortKey = dashboardSortKey || graphQLSortKey || defaultSortKey;
if (this.isIssueRepositioningDisabled && sortKey === RELATIVE_POSITION_ASC) {
@@ -741,15 +736,15 @@ export default {
sortKey = defaultSortKey;
}
- this.exportCsvPathWithQuery = this.getExportCsvPathWithQuery();
this.setFilterTokens(getFilterTokens(window.location.search));
+ this.exportCsvPathWithQuery = this.getExportCsvPathWithQuery();
this.pageParams = getInitialPageParams(
this.pageSize,
isPositiveInteger(firstPageSize) ? parseInt(firstPageSize, 10) : undefined,
isPositiveInteger(lastPageSize) ? parseInt(lastPageSize, 10) : undefined,
- pageAfter,
- pageBefore,
+ getParameterByName(PARAM_PAGE_AFTER),
+ getParameterByName(PARAM_PAGE_BEFORE),
);
this.sortKey = sortKey;
this.state = state || IssuableStates.Opened;
@@ -827,9 +822,14 @@ export default {
>
{{ $options.i18n.editIssues }}
</gl-button>
- <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm">
+ <gl-button
+ v-if="showNewIssueLink && !eeIsOkrsEnabled"
+ :href="newIssuePath"
+ variant="confirm"
+ >
{{ $options.i18n.newIssueLabel }}
</gl-button>
+ <slot name="new-objective-button"></slot>
<new-issue-dropdown v-if="showNewIssueDropdown" />
</template>
@@ -842,129 +842,25 @@ export default {
</template>
<template #statistics="{ issuable = {} }">
- <li
- v-if="issuable.mergeRequestsCount"
- v-gl-tooltip
- class="gl-display-none gl-sm-display-block"
- :title="$options.i18n.relatedMergeRequests"
- data-testid="merge-requests"
- >
- <gl-icon name="merge-request" />
- {{ issuable.mergeRequestsCount }}
- </li>
- <li
- v-if="issuable.upvotes"
- v-gl-tooltip
- class="issuable-upvotes gl-display-none gl-sm-display-block"
- :title="$options.i18n.upvotes"
- data-testid="issuable-upvotes"
- >
- <gl-icon name="thumb-up" />
- {{ issuable.upvotes }}
- </li>
- <li
- v-if="issuable.downvotes"
- v-gl-tooltip
- class="issuable-downvotes gl-display-none gl-sm-display-block"
- :title="$options.i18n.downvotes"
- data-testid="issuable-downvotes"
- >
- <gl-icon name="thumb-down" />
- {{ issuable.downvotes }}
- </li>
- <slot :issuable="issuable"></slot>
+ <issue-card-statistics :issue="issuable" />
</template>
<template #empty-state>
- <gl-empty-state
- v-if="hasSearch"
- :description="$options.i18n.noSearchResultsDescription"
- :title="$options.i18n.noSearchResultsTitle"
- :svg-path="emptyStateSvgPath"
- >
- <template #actions>
- <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm">
- {{ $options.i18n.newIssueLabel }}
- </gl-button>
- </template>
- </gl-empty-state>
-
- <gl-empty-state
- v-else-if="isOpenTab"
- :description="$options.i18n.noOpenIssuesDescription"
- :title="$options.i18n.noOpenIssuesTitle"
- :svg-path="emptyStateSvgPath"
- >
- <template #actions>
- <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm">
- {{ $options.i18n.newIssueLabel }}
- </gl-button>
- </template>
- </gl-empty-state>
-
- <gl-empty-state
- v-else
- :title="$options.i18n.noClosedIssuesTitle"
- :svg-path="emptyStateSvgPath"
- />
+ <empty-state-with-any-issues :has-search="hasSearch" :is-open-tab="isOpenTab" />
+ </template>
+
+ <template #list-body>
+ <slot name="list-body"></slot>
</template>
</issuable-list>
- <template v-else-if="isSignedIn">
- <gl-empty-state :title="$options.i18n.noIssuesSignedInTitle" :svg-path="emptyStateSvgPath">
- <template #description>
- <gl-link :href="issuesHelpPagePath" target="_blank">{{
- $options.i18n.noIssuesSignedInDescription
- }}</gl-link>
- <p v-if="canCreateProjects">
- <strong>{{ $options.i18n.noGroupIssuesSignedInDescription }}</strong>
- </p>
- </template>
- <template #actions>
- <gl-button v-if="canCreateProjects" :href="newProjectPath" variant="confirm">
- {{ $options.i18n.newProjectLabel }}
- </gl-button>
- <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm">
- {{ $options.i18n.newIssueLabel }}
- </gl-button>
- <csv-import-export-buttons
- v-if="showCsvButtons"
- class="gl-w-full gl-sm-w-auto gl-sm-mr-3"
- :export-csv-path="exportCsvPathWithQuery"
- :issuable-count="currentTabCount"
- />
- <new-issue-dropdown v-if="showNewIssueDropdown" class="gl-align-self-center" />
- </template>
- </gl-empty-state>
- <hr />
- <p class="gl-text-center gl-font-weight-bold gl-mb-0">
- {{ $options.i18n.jiraIntegrationTitle }}
- </p>
- <p class="gl-text-center gl-mb-0">
- <gl-sprintf :message="$options.i18n.jiraIntegrationMessage">
- <template #jiraDocsLink="{ content }">
- <gl-link :href="jiraIntegrationPath">{{ content }}</gl-link>
- </template>
- </gl-sprintf>
- </p>
- <p class="gl-text-center gl-text-gray-500">
- {{ $options.i18n.jiraIntegrationSecondaryMessage }}
- </p>
- </template>
-
- <gl-empty-state
+ <empty-state-without-any-issues
v-else
- :title="$options.i18n.noIssuesSignedOutTitle"
- :svg-path="emptyStateSvgPath"
- :primary-button-text="$options.i18n.noIssuesSignedOutButtonText"
- :primary-button-link="signInPath"
- >
- <template #description>
- <gl-link :href="issuesHelpPagePath" target="_blank">{{
- $options.i18n.noIssuesSignedOutDescription
- }}</gl-link>
- </template>
- </gl-empty-state>
+ :current-tab-count="currentTabCount"
+ :export-csv-path-with-query="exportCsvPathWithQuery"
+ :show-csv-buttons="showCsvButtons"
+ :show-new-issue-dropdown="showNewIssueDropdown"
+ />
<issuable-by-email v-if="showIssuableByEmail" class="gl-text-center gl-pt-5 gl-pb-7" />
</div>
diff --git a/app/assets/javascripts/issues/list/components/new_issue_dropdown.vue b/app/assets/javascripts/issues/list/components/new_issue_dropdown.vue
index 666e80dfd4b..e420c21a11f 100644
--- a/app/assets/javascripts/issues/list/components/new_issue_dropdown.vue
+++ b/app/assets/javascripts/issues/list/components/new_issue_dropdown.vue
@@ -6,7 +6,7 @@ import {
GlLoadingIcon,
GlSearchBoxByType,
} from '@gitlab/ui';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import { DASH_SCOPE, joinPaths } from '~/lib/utils/url_utility';
import { __, sprintf } from '~/locale';
import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants';
@@ -45,7 +45,7 @@ export default {
},
update: ({ group }) => group.projects.nodes ?? [],
error(error) {
- createFlash({
+ createAlert({
message: __('An error occurred while loading projects.'),
captureError: true,
error,
diff --git a/app/assets/javascripts/issues/list/constants.js b/app/assets/javascripts/issues/list/constants.js
index 5ed9ceea856..49a953cad43 100644
--- a/app/assets/javascripts/issues/list/constants.js
+++ b/app/assets/javascripts/issues/list/constants.js
@@ -6,7 +6,7 @@ import {
FILTER_STARTED,
FILTER_UPCOMING,
OPERATOR_IS,
- OPERATOR_IS_NOT,
+ OPERATOR_NOT,
OPERATOR_OR,
TOKEN_TYPE_ASSIGNEE,
TOKEN_TYPE_AUTHOR,
@@ -22,6 +22,7 @@ import {
TOKEN_TYPE_RELEASE,
TOKEN_TYPE_TYPE,
TOKEN_TYPE_WEIGHT,
+ TOKEN_TYPE_SEARCH_WITHIN,
} from '~/vue_shared/components/filtered_search_bar/constants';
import {
WORK_ITEM_TYPE_ENUM_INCIDENT,
@@ -30,6 +31,50 @@ import {
WORK_ITEM_TYPE_ENUM_TASK,
} from '~/work_items/constants';
+export const ISSUE_REFERENCE = /^#\d+$/;
+export const MAX_LIST_SIZE = 10;
+export const PAGE_SIZE = 20;
+export const PARAM_ASSIGNEE_ID = 'assignee_id';
+export const PARAM_FIRST_PAGE_SIZE = 'first_page_size';
+export const PARAM_LAST_PAGE_SIZE = 'last_page_size';
+export const PARAM_PAGE_AFTER = 'page_after';
+export const PARAM_PAGE_BEFORE = 'page_before';
+export const PARAM_SORT = 'sort';
+export const PARAM_STATE = 'state';
+export const RELATIVE_POSITION = 'relative_position';
+
+export const BLOCKING_ISSUES_ASC = 'BLOCKING_ISSUES_ASC';
+export const BLOCKING_ISSUES_DESC = 'BLOCKING_ISSUES_DESC';
+export const CLOSED_AT_ASC = 'CLOSED_AT_ASC';
+export const CLOSED_AT_DESC = 'CLOSED_AT_DESC';
+export const CREATED_ASC = 'CREATED_ASC';
+export const CREATED_DESC = 'CREATED_DESC';
+export const DUE_DATE_ASC = 'DUE_DATE_ASC';
+export const DUE_DATE_DESC = 'DUE_DATE_DESC';
+export const HEALTH_STATUS_ASC = 'HEALTH_STATUS_ASC';
+export const HEALTH_STATUS_DESC = 'HEALTH_STATUS_DESC';
+export const LABEL_PRIORITY_ASC = 'LABEL_PRIORITY_ASC';
+export const LABEL_PRIORITY_DESC = 'LABEL_PRIORITY_DESC';
+export const MILESTONE_DUE_ASC = 'MILESTONE_DUE_ASC';
+export const MILESTONE_DUE_DESC = 'MILESTONE_DUE_DESC';
+export const POPULARITY_ASC = 'POPULARITY_ASC';
+export const POPULARITY_DESC = 'POPULARITY_DESC';
+export const PRIORITY_ASC = 'PRIORITY_ASC';
+export const PRIORITY_DESC = 'PRIORITY_DESC';
+export const RELATIVE_POSITION_ASC = 'RELATIVE_POSITION_ASC';
+export const TITLE_ASC = 'TITLE_ASC';
+export const TITLE_DESC = 'TITLE_DESC';
+export const UPDATED_ASC = 'UPDATED_ASC';
+export const UPDATED_DESC = 'UPDATED_DESC';
+export const WEIGHT_ASC = 'WEIGHT_ASC';
+export const WEIGHT_DESC = 'WEIGHT_DESC';
+
+export const API_PARAM = 'apiParam';
+export const URL_PARAM = 'urlParam';
+export const NORMAL_FILTER = 'normalFilter';
+export const SPECIAL_FILTER = 'specialFilter';
+export const ALTERNATIVE_FILTER = 'alternativeFilter';
+
export const i18n = {
anonymousSearchingMessage: __('You must sign in to search for specific terms.'),
calendarLabel: __('Subscribe to calendar'),
@@ -57,11 +102,9 @@ export const i18n = {
),
noOpenIssuesDescription: __('To keep this project going, create a new issue'),
noOpenIssuesTitle: __('There are no open issues'),
- noIssuesSignedInDescription: __('Learn more about issues.'),
- noIssuesSignedInTitle: __('Use issues to collaborate on ideas, solve problems, and plan work'),
+ noIssuesDescription: __('Learn more about issues.'),
+ noIssuesTitle: __('Use issues to collaborate on ideas, solve problems, and plan work'),
noIssuesSignedOutButtonText: __('Register / Sign In'),
- noIssuesSignedOutDescription: __('Learn more about issues.'),
- noIssuesSignedOutTitle: __('Use issues to collaborate on ideas, solve problems, and plan work'),
noSearchResultsDescription: __('To widen your search, change or remove filters above'),
noSearchResultsTitle: __('Sorry, your filter produced no results'),
relatedMergeRequests: __('Related merge requests'),
@@ -69,45 +112,10 @@ export const i18n = {
rssLabel: __('Subscribe to RSS feed'),
searchPlaceholder: __('Search or filter results...'),
upvotes: __('Upvotes'),
+ titles: __('Titles'),
+ descriptions: __('Descriptions'),
};
-export const ISSUE_REFERENCE = /^#\d+$/;
-export const MAX_LIST_SIZE = 10;
-export const PAGE_SIZE = 20;
-export const PAGE_SIZE_MANUAL = 100;
-export const PARAM_ASSIGNEE_ID = 'assignee_id';
-export const PARAM_FIRST_PAGE_SIZE = 'first_page_size';
-export const PARAM_LAST_PAGE_SIZE = 'last_page_size';
-export const PARAM_PAGE_AFTER = 'page_after';
-export const PARAM_PAGE_BEFORE = 'page_before';
-export const PARAM_SORT = 'sort';
-export const PARAM_STATE = 'state';
-export const RELATIVE_POSITION = 'relative_position';
-
-export const BLOCKING_ISSUES_ASC = 'BLOCKING_ISSUES_ASC';
-export const BLOCKING_ISSUES_DESC = 'BLOCKING_ISSUES_DESC';
-export const CREATED_ASC = 'CREATED_ASC';
-export const CREATED_DESC = 'CREATED_DESC';
-export const DUE_DATE_ASC = 'DUE_DATE_ASC';
-export const DUE_DATE_DESC = 'DUE_DATE_DESC';
-export const LABEL_PRIORITY_ASC = 'LABEL_PRIORITY_ASC';
-export const LABEL_PRIORITY_DESC = 'LABEL_PRIORITY_DESC';
-export const MILESTONE_DUE_ASC = 'MILESTONE_DUE_ASC';
-export const MILESTONE_DUE_DESC = 'MILESTONE_DUE_DESC';
-export const POPULARITY_ASC = 'POPULARITY_ASC';
-export const POPULARITY_DESC = 'POPULARITY_DESC';
-export const PRIORITY_ASC = 'PRIORITY_ASC';
-export const PRIORITY_DESC = 'PRIORITY_DESC';
-export const RELATIVE_POSITION_ASC = 'RELATIVE_POSITION_ASC';
-export const TITLE_ASC = 'TITLE_ASC';
-export const TITLE_DESC = 'TITLE_DESC';
-export const UPDATED_ASC = 'UPDATED_ASC';
-export const UPDATED_DESC = 'UPDATED_DESC';
-export const WEIGHT_ASC = 'WEIGHT_ASC';
-export const WEIGHT_DESC = 'WEIGHT_DESC';
-export const CLOSED_ASC = 'CLOSED_AT_ASC';
-export const CLOSED_DESC = 'CLOSED_AT_DESC';
-
export const urlSortParams = {
[PRIORITY_ASC]: 'priority',
[PRIORITY_DESC]: 'priority_desc',
@@ -115,8 +123,8 @@ export const urlSortParams = {
[CREATED_DESC]: 'created_date',
[UPDATED_ASC]: 'updated_asc',
[UPDATED_DESC]: 'updated_desc',
- [CLOSED_ASC]: 'closed_asc',
- [CLOSED_DESC]: 'closed_desc',
+ [CLOSED_AT_ASC]: 'closed_at',
+ [CLOSED_AT_DESC]: 'closed_at_desc',
[MILESTONE_DUE_ASC]: 'milestone',
[MILESTONE_DUE_DESC]: 'milestone_due_desc',
[DUE_DATE_ASC]: 'due_date',
@@ -126,20 +134,16 @@ export const urlSortParams = {
[LABEL_PRIORITY_ASC]: 'label_priority',
[LABEL_PRIORITY_DESC]: 'label_priority_desc',
[RELATIVE_POSITION_ASC]: RELATIVE_POSITION,
+ [TITLE_ASC]: 'title_asc',
+ [TITLE_DESC]: 'title_desc',
+ [HEALTH_STATUS_ASC]: 'health_status_asc',
+ [HEALTH_STATUS_DESC]: 'health_status_desc',
[WEIGHT_ASC]: 'weight',
[WEIGHT_DESC]: 'weight_desc',
[BLOCKING_ISSUES_ASC]: 'blocking_issues_asc',
[BLOCKING_ISSUES_DESC]: 'blocking_issues_desc',
- [TITLE_ASC]: 'title_asc',
- [TITLE_DESC]: 'title_desc',
};
-export const API_PARAM = 'apiParam';
-export const URL_PARAM = 'urlParam';
-export const NORMAL_FILTER = 'normalFilter';
-export const SPECIAL_FILTER = 'specialFilter';
-export const ALTERNATIVE_FILTER = 'alternativeFilter';
-
export const specialFilterValues = [
FILTER_NONE,
FILTER_ANY,
@@ -148,7 +152,17 @@ export const specialFilterValues = [
FILTER_STARTED,
];
-export const TYPE_TOKEN_TASK_OPTION = { icon: 'issue-type-task', title: 'task', value: 'task' };
+export const TYPE_TOKEN_OBJECTIVE_OPTION = {
+ icon: 'issue-type-objective',
+ title: 'objective',
+ value: 'objective',
+};
+
+export const TYPE_TOKEN_KEY_RESULT_OPTION = {
+ icon: 'issue-type-key-result',
+ title: 'key_result',
+ value: 'key_result',
+};
// This should be consistent with Issue::TYPES_FOR_LIST in the backend
// https://gitlab.com/gitlab-org/gitlab/-/blob/1379c2d7bffe2a8d809f23ac5ef9b4114f789c07/app/models/issue.rb#L48
@@ -163,20 +177,35 @@ export const defaultTypeTokenOptions = [
{ icon: 'issue-type-issue', title: 'issue', value: 'issue' },
{ icon: 'issue-type-incident', title: 'incident', value: 'incident' },
{ icon: 'issue-type-test-case', title: 'test_case', value: 'test_case' },
+ { icon: 'issue-type-task', title: 'task', value: 'task' },
];
export const filters = {
[TOKEN_TYPE_AUTHOR]: {
[API_PARAM]: {
[NORMAL_FILTER]: 'authorUsername',
+ [ALTERNATIVE_FILTER]: 'authorUsernames',
},
[URL_PARAM]: {
[OPERATOR_IS]: {
[NORMAL_FILTER]: 'author_username',
},
- [OPERATOR_IS_NOT]: {
+ [OPERATOR_NOT]: {
[NORMAL_FILTER]: 'not[author_username]',
},
+ [OPERATOR_OR]: {
+ [ALTERNATIVE_FILTER]: 'or[author_username]',
+ },
+ },
+ },
+ [TOKEN_TYPE_SEARCH_WITHIN]: {
+ [API_PARAM]: {
+ [NORMAL_FILTER]: 'in',
+ },
+ [URL_PARAM]: {
+ [OPERATOR_IS]: {
+ [NORMAL_FILTER]: 'in',
+ },
},
},
[TOKEN_TYPE_ASSIGNEE]: {
@@ -190,7 +219,7 @@ export const filters = {
[SPECIAL_FILTER]: 'assignee_id',
[ALTERNATIVE_FILTER]: 'assignee_username',
},
- [OPERATOR_IS_NOT]: {
+ [OPERATOR_NOT]: {
[NORMAL_FILTER]: 'not[assignee_username][]',
},
[OPERATOR_OR]: {
@@ -208,7 +237,7 @@ export const filters = {
[NORMAL_FILTER]: 'milestone_title',
[SPECIAL_FILTER]: 'milestone_title',
},
- [OPERATOR_IS_NOT]: {
+ [OPERATOR_NOT]: {
[NORMAL_FILTER]: 'not[milestone_title]',
[SPECIAL_FILTER]: 'not[milestone_title]',
},
@@ -225,7 +254,7 @@ export const filters = {
[SPECIAL_FILTER]: 'label_name[]',
[ALTERNATIVE_FILTER]: 'label_name',
},
- [OPERATOR_IS_NOT]: {
+ [OPERATOR_NOT]: {
[NORMAL_FILTER]: 'not[label_name][]',
},
},
@@ -238,7 +267,7 @@ export const filters = {
[OPERATOR_IS]: {
[NORMAL_FILTER]: 'type[]',
},
- [OPERATOR_IS_NOT]: {
+ [OPERATOR_NOT]: {
[NORMAL_FILTER]: 'not[type][]',
},
},
@@ -253,7 +282,7 @@ export const filters = {
[NORMAL_FILTER]: 'release_tag',
[SPECIAL_FILTER]: 'release_tag',
},
- [OPERATOR_IS_NOT]: {
+ [OPERATOR_NOT]: {
[NORMAL_FILTER]: 'not[release_tag]',
},
},
@@ -268,7 +297,7 @@ export const filters = {
[NORMAL_FILTER]: 'my_reaction_emoji',
[SPECIAL_FILTER]: 'my_reaction_emoji',
},
- [OPERATOR_IS_NOT]: {
+ [OPERATOR_NOT]: {
[NORMAL_FILTER]: 'not[my_reaction_emoji]',
},
},
@@ -293,7 +322,7 @@ export const filters = {
[NORMAL_FILTER]: 'iteration_id',
[SPECIAL_FILTER]: 'iteration_id',
},
- [OPERATOR_IS_NOT]: {
+ [OPERATOR_NOT]: {
[NORMAL_FILTER]: 'not[iteration_id]',
[SPECIAL_FILTER]: 'not[iteration_id]',
},
@@ -309,7 +338,7 @@ export const filters = {
[NORMAL_FILTER]: 'epic_id',
[SPECIAL_FILTER]: 'epic_id',
},
- [OPERATOR_IS_NOT]: {
+ [OPERATOR_NOT]: {
[NORMAL_FILTER]: 'not[epic_id]',
},
},
@@ -324,7 +353,7 @@ export const filters = {
[NORMAL_FILTER]: 'weight',
[SPECIAL_FILTER]: 'weight',
},
- [OPERATOR_IS_NOT]: {
+ [OPERATOR_NOT]: {
[NORMAL_FILTER]: 'not[weight]',
},
},
diff --git a/app/assets/javascripts/issues/list/index.js b/app/assets/javascripts/issues/list/index.js
index 5e04dd1971c..7b68b7432c9 100644
--- a/app/assets/javascripts/issues/list/index.js
+++ b/app/assets/javascripts/issues/list/index.js
@@ -2,16 +2,15 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import VueRouter from 'vue-router';
import IssuesListApp from 'ee_else_ce/issues/list/components/issues_list_app.vue';
-import createDefaultClient from '~/lib/graphql';
import { parseBoolean } from '~/lib/utils/common_utils';
-import JiraIssuesImportStatusRoot from './components/jira_issues_import_status_app.vue';
+import JiraIssuesImportStatusApp from './components/jira_issues_import_status_app.vue';
import { gqlClient } from './graphql';
export function mountJiraIssuesListApp() {
- const el = document.querySelector('.js-jira-issues-import-status');
+ const el = document.querySelector('.js-jira-issues-import-status-root');
if (!el) {
- return false;
+ return null;
}
const { issuesPath, projectPath } = el.dataset;
@@ -19,21 +18,19 @@ export function mountJiraIssuesListApp() {
const isJiraConfigured = parseBoolean(el.dataset.isJiraConfigured);
if (!isJiraConfigured || !canEdit) {
- return false;
+ return null;
}
Vue.use(VueApollo);
- const defaultClient = createDefaultClient();
- const apolloProvider = new VueApollo({
- defaultClient,
- });
return new Vue({
el,
name: 'JiraIssuesImportStatusRoot',
- apolloProvider,
+ apolloProvider: new VueApollo({
+ defaultClient: gqlClient,
+ }),
render(createComponent) {
- return createComponent(JiraIssuesImportStatusRoot, {
+ return createComponent(JiraIssuesImportStatusApp, {
props: {
canEdit,
isJiraConfigured,
@@ -46,10 +43,10 @@ export function mountJiraIssuesListApp() {
}
export function mountIssuesListApp() {
- const el = document.querySelector('.js-issues-list');
+ const el = document.querySelector('.js-issues-list-root');
if (!el) {
- return false;
+ return null;
}
Vue.use(VueApollo);
@@ -77,6 +74,7 @@ export function mountIssuesListApp() {
hasIssueWeightsFeature,
hasIterationsFeature,
hasScopedLabelsFeature,
+ hasOkrsFeature,
importCsvIssuesPath,
initialEmail,
initialSort,
@@ -127,6 +125,7 @@ export function mountIssuesListApp() {
hasIssueWeightsFeature: parseBoolean(hasIssueWeightsFeature),
hasIterationsFeature: parseBoolean(hasIterationsFeature),
hasScopedLabelsFeature: parseBoolean(hasScopedLabelsFeature),
+ hasOkrsFeature: parseBoolean(hasOkrsFeature),
initialSort,
isAnonymousSearchDisabled: parseBoolean(isAnonymousSearchDisabled),
isIssueRepositioningDisabled: parseBoolean(isIssueRepositioningDisabled),
diff --git a/app/assets/javascripts/issues/list/queries/get_issues.query.graphql b/app/assets/javascripts/issues/list/queries/get_issues.query.graphql
index b447289b425..ee97fb6edca 100644
--- a/app/assets/javascripts/issues/list/queries/get_issues.query.graphql
+++ b/app/assets/javascripts/issues/list/queries/get_issues.query.graphql
@@ -10,6 +10,7 @@ query getIssues(
$search: String
$sort: IssueSort
$state: IssuableState
+ $in: [IssuableSearchableField!]
$assigneeId: String
$assigneeUsernames: [String!]
$authorUsername: String
@@ -38,6 +39,7 @@ query getIssues(
search: $search
sort: $sort
state: $state
+ in: $in
assigneeId: $assigneeId
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
@@ -72,6 +74,7 @@ query getIssues(
search: $search
sort: $sort
state: $state
+ in: $in
assigneeId: $assigneeId
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
diff --git a/app/assets/javascripts/issues/list/utils.js b/app/assets/javascripts/issues/list/utils.js
index 2f9ab9d62ee..b566e08731c 100644
--- a/app/assets/javascripts/issues/list/utils.js
+++ b/app/assets/javascripts/issues/list/utils.js
@@ -4,9 +4,10 @@ import { getParameterByName } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import {
FILTERED_SEARCH_TERM,
- OPERATOR_IS_NOT,
+ OPERATOR_NOT,
OPERATOR_OR,
TOKEN_TYPE_ASSIGNEE,
+ TOKEN_TYPE_AUTHOR,
TOKEN_TYPE_CONFIDENTIAL,
TOKEN_TYPE_ITERATION,
TOKEN_TYPE_MILESTONE,
@@ -14,14 +15,19 @@ import {
TOKEN_TYPE_TYPE,
} from '~/vue_shared/components/filtered_search_bar/constants';
import {
+ ALTERNATIVE_FILTER,
API_PARAM,
BLOCKING_ISSUES_ASC,
BLOCKING_ISSUES_DESC,
+ CLOSED_AT_ASC,
+ CLOSED_AT_DESC,
CREATED_ASC,
CREATED_DESC,
DUE_DATE_ASC,
DUE_DATE_DESC,
filters,
+ HEALTH_STATUS_ASC,
+ HEALTH_STATUS_DESC,
LABEL_PRIORITY_ASC,
LABEL_PRIORITY_DESC,
MILESTONE_DUE_ASC,
@@ -44,8 +50,6 @@ import {
urlSortParams,
WEIGHT_ASC,
WEIGHT_DESC,
- CLOSED_ASC,
- CLOSED_DESC,
} from './constants';
export const getInitialPageParams = (
@@ -66,7 +70,11 @@ export const getSortKey = (sort) =>
export const isSortKey = (sort) => Object.keys(urlSortParams).includes(sort);
-export const getSortOptions = (hasIssueWeightsFeature, hasBlockedIssuesFeature) => {
+export const getSortOptions = ({
+ hasBlockedIssuesFeature,
+ hasIssuableHealthStatusFeature,
+ hasIssueWeightsFeature,
+}) => {
const sortOptions = [
{
id: 1,
@@ -96,8 +104,8 @@ export const getSortOptions = (hasIssueWeightsFeature, hasBlockedIssuesFeature)
id: 4,
title: __('Closed date'),
sortDirection: {
- ascending: CLOSED_ASC,
- descending: CLOSED_DESC,
+ ascending: CLOSED_AT_ASC,
+ descending: CLOSED_AT_DESC,
},
},
{
@@ -150,6 +158,17 @@ export const getSortOptions = (hasIssueWeightsFeature, hasBlockedIssuesFeature)
},
];
+ if (hasIssuableHealthStatusFeature) {
+ sortOptions.push({
+ id: sortOptions.length + 1,
+ title: __('Health'),
+ sortDirection: {
+ ascending: HEALTH_STATUS_ASC,
+ descending: HEALTH_STATUS_DESC,
+ },
+ });
+ }
+
if (hasIssueWeightsFeature) {
sortOptions.push({
id: sortOptions.length + 1,
@@ -223,13 +242,24 @@ export const getFilterTokens = (locationSearch) => {
return tokens.length ? tokens : [createTerm()];
};
-const getFilterType = (data, tokenType = '') => {
+const isSpecialFilter = (type, data) => {
const isAssigneeIdParam =
- tokenType === TOKEN_TYPE_ASSIGNEE &&
+ type === TOKEN_TYPE_ASSIGNEE &&
isPositiveInteger(data) &&
getParameterByName(PARAM_ASSIGNEE_ID) === data;
+ return specialFilterValues.includes(data) || isAssigneeIdParam;
+};
+
+const getFilterType = ({ type, value: { data, operator } }) => {
+ const isUnionedAuthor = type === TOKEN_TYPE_AUTHOR && operator === OPERATOR_OR;
- return specialFilterValues.includes(data) || isAssigneeIdParam ? SPECIAL_FILTER : NORMAL_FILTER;
+ if (isUnionedAuthor) {
+ return ALTERNATIVE_FILTER;
+ }
+ if (isSpecialFilter(type, data)) {
+ return SPECIAL_FILTER;
+ }
+ return NORMAL_FILTER;
};
const wildcardTokens = [TOKEN_TYPE_ITERATION, TOKEN_TYPE_MILESTONE, TOKEN_TYPE_RELEASE];
@@ -258,10 +288,10 @@ export const convertToApiParams = (filterTokens) => {
filterTokens
.filter((token) => token.type !== FILTERED_SEARCH_TERM)
.forEach((token) => {
- const filterType = getFilterType(token.value.data, token.type);
- const field = filters[token.type][API_PARAM][filterType];
+ const filterType = getFilterType(token);
+ const apiField = filters[token.type][API_PARAM][filterType];
let obj;
- if (token.value.operator === OPERATOR_IS_NOT) {
+ if (token.value.operator === OPERATOR_NOT) {
obj = not;
} else if (token.value.operator === OPERATOR_OR) {
obj = or;
@@ -270,7 +300,7 @@ export const convertToApiParams = (filterTokens) => {
}
const data = formatData(token);
Object.assign(obj, {
- [field]: obj[field] ? [obj[field], data].flat() : data,
+ [apiField]: obj[apiField] ? [obj[apiField], data].flat() : data,
});
});
@@ -289,10 +319,10 @@ export const convertToUrlParams = (filterTokens) =>
filterTokens
.filter((token) => token.type !== FILTERED_SEARCH_TERM)
.reduce((acc, token) => {
- const filterType = getFilterType(token.value.data, token.type);
- const param = filters[token.type][URL_PARAM][token.value.operator]?.[filterType];
+ const filterType = getFilterType(token);
+ const urlParam = filters[token.type][URL_PARAM][token.value.operator]?.[filterType];
return Object.assign(acc, {
- [param]: acc[param] ? [acc[param], token.value.data].flat() : token.value.data,
+ [urlParam]: acc[urlParam] ? [acc[urlParam], token.value.data].flat() : token.value.data,
});
}, {});
@@ -300,4 +330,4 @@ export const convertToSearchQuery = (filterTokens) =>
filterTokens
.filter((token) => token.type === FILTERED_SEARCH_TERM && token.value.data)
.map((token) => token.value.data)
- .join(' ');
+ .join(' ') || undefined;
diff --git a/app/assets/javascripts/issues/manual_ordering.js b/app/assets/javascripts/issues/manual_ordering.js
index bc1cffef943..1bb53dfd50d 100644
--- a/app/assets/javascripts/issues/manual_ordering.js
+++ b/app/assets/javascripts/issues/manual_ordering.js
@@ -1,5 +1,5 @@
import Sortable from 'sortablejs';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { s__ } from '~/locale';
import { getSortableDefaultOptions, sortableStart } from '~/sortable/utils';
@@ -11,7 +11,7 @@ const updateIssue = (url, { move_before_id, move_after_id }) =>
move_after_id,
})
.catch(() => {
- createFlash({
+ createAlert({
message: s__("ManualOrdering|Couldn't save the order of the issues"),
});
});
diff --git a/app/assets/javascripts/issues/related_merge_requests/store/actions.js b/app/assets/javascripts/issues/related_merge_requests/store/actions.js
index 94abb50de89..4c81f1d9bc1 100644
--- a/app/assets/javascripts/issues/related_merge_requests/store/actions.js
+++ b/app/assets/javascripts/issues/related_merge_requests/store/actions.js
@@ -1,4 +1,4 @@
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { normalizeHeaders } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
@@ -29,7 +29,7 @@ export const fetchMergeRequests = ({ state, dispatch }) => {
})
.catch(() => {
dispatch('receiveDataError');
- createFlash({
+ createAlert({
message: __('Something went wrong while fetching related merge requests.'),
});
});
diff --git a/app/assets/javascripts/issues/show/components/app.vue b/app/assets/javascripts/issues/show/components/app.vue
index 0daf77e03dc..e5428f87095 100644
--- a/app/assets/javascripts/issues/show/components/app.vue
+++ b/app/assets/javascripts/issues/show/components/app.vue
@@ -1,7 +1,7 @@
<script>
import { GlIcon, GlBadge, GlIntersectionObserver, GlTooltipDirective } from '@gitlab/ui';
import Visibility from 'visibilityjs';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import {
IssuableStatus,
IssuableStatusText,
@@ -327,7 +327,7 @@ export default {
this.store.updateState(data);
})
.catch(() => {
- createFlash({
+ createAlert({
message: this.defaultErrorMessage,
});
});
@@ -362,7 +362,7 @@ export default {
this.updateAndShowForm(res.data);
})
.catch(() => {
- createFlash({
+ createAlert({
message: this.defaultErrorMessage,
});
this.updateAndShowForm();
@@ -429,7 +429,7 @@ export default {
errMsg += `. ${message}`;
}
- this.flashContainer = createFlash({
+ this.flashContainer = createAlert({
message: errMsg,
});
})
diff --git a/app/assets/javascripts/issues/show/components/description.vue b/app/assets/javascripts/issues/show/components/description.vue
index 5c2a154362f..78e729b97da 100644
--- a/app/assets/javascripts/issues/show/components/description.vue
+++ b/app/assets/javascripts/issues/show/components/description.vue
@@ -1,11 +1,12 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml, GlToast, GlTooltip, GlModalDirective } from '@gitlab/ui';
+import { GlToast, GlTooltip, GlModalDirective } from '@gitlab/ui';
import $ from 'jquery';
import Sortable from 'sortablejs';
import Vue from 'vue';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { getIdFromGraphQLId, convertToGraphQLId } from '~/graphql_shared/utils';
import { TYPE_WORK_ITEM } from '~/graphql_shared/constants';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import { IssuableType } from '~/issues/constants';
import { isMetaKey } from '~/lib/utils/common_utils';
import { isPositiveInteger } from '~/lib/utils/number_utils';
@@ -27,6 +28,7 @@ import {
TASK_TYPE_NAME,
WIDGET_TYPE_DESCRIPTION,
} from '~/work_items/constants';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import animateMixin from '../mixins/animate';
import { convertDescriptionWithNewSort } from '../utils';
@@ -165,7 +167,7 @@ export default {
this.renderGFM();
this.updateTaskStatusText();
- if (this.workItemId) {
+ if (this.workItemId && this.workItemsEnabled) {
const taskLink = this.$el.querySelector(
`.gfm-issue[data-issue="${getIdFromGraphQLId(this.workItemId)}"]`,
);
@@ -177,7 +179,7 @@ export default {
},
methods: {
renderGFM() {
- $(this.$refs['gfm-content']).renderGFM();
+ renderGFM(this.$refs['gfm-content']);
if (this.canUpdate) {
// eslint-disable-next-line no-new
@@ -283,7 +285,7 @@ export default {
},
taskListUpdateError() {
- createFlash({
+ createAlert({
message: sprintf(
__(
'Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again.',
@@ -467,7 +469,7 @@ export default {
this.workItemId = newWorkItem.id;
this.openWorkItemDetailModal(el);
} catch (error) {
- createFlash({
+ createAlert({
message: sprintfWorkItem(I18N_WORK_ITEM_ERROR_CREATING, workItemTypes.TASK),
error,
captureError: true,
diff --git a/app/assets/javascripts/issues/show/components/fields/description.vue b/app/assets/javascripts/issues/show/components/fields/description.vue
index 180dea77003..04c5007dbec 100644
--- a/app/assets/javascripts/issues/show/components/fields/description.vue
+++ b/app/assets/javascripts/issues/show/components/fields/description.vue
@@ -67,6 +67,7 @@ export default {
:quick-actions-docs-path="quickActionsDocsPath"
:enable-autocomplete="enableAutocomplete"
supports-quick-actions
+ use-bottom-toolbar
autofocus
@input="$emit('input', $event)"
@keydown.meta.enter="updateIssuable"
diff --git a/app/assets/javascripts/issues/show/components/form.vue b/app/assets/javascripts/issues/show/components/form.vue
index 0c6b61fb893..b56c91d7983 100644
--- a/app/assets/javascripts/issues/show/components/form.vue
+++ b/app/assets/javascripts/issues/show/components/form.vue
@@ -164,7 +164,7 @@ export default {
<template>
<form data-testid="issuable-form">
- <locked-warning v-if="showLockedWarning" />
+ <locked-warning v-if="showLockedWarning" :issuable-type="issuableType" />
<gl-alert
v-if="showOutdatedDescriptionWarning"
class="gl-mb-5"
diff --git a/app/assets/javascripts/issues/show/components/header_actions.vue b/app/assets/javascripts/issues/show/components/header_actions.vue
index c01de63ced9..983e2e6530e 100644
--- a/app/assets/javascripts/issues/show/components/header_actions.vue
+++ b/app/assets/javascripts/issues/show/components/header_actions.vue
@@ -10,7 +10,7 @@ import {
GlTooltipDirective,
} from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
-import createFlash, { FLASH_TYPES } from '~/flash';
+import { createAlert, VARIANT_SUCCESS } from '~/flash';
import { EVENT_ISSUABLE_VUE_APP_CHANGE } from '~/issuable/constants';
import { IssuableStatus, IssueType } from '~/issues/constants';
import { ISSUE_STATE_EVENT_CLOSE, ISSUE_STATE_EVENT_REOPEN } from '~/issues/show/constants';
@@ -40,6 +40,7 @@ export default {
promoteSuccessMessage: __(
'The issue was successfully promoted to an epic. Redirecting to epic...',
),
+ reportAbuse: __('Report abuse to administrator'),
},
components: {
DeleteIssueModal,
@@ -191,7 +192,7 @@ export default {
// Dispatch event which updates open/close state, shared among the issue show page
document.dispatchEvent(new CustomEvent(EVENT_ISSUABLE_VUE_APP_CHANGE, payload));
})
- .catch(() => createFlash({ message: __('Error occurred while updating the issue status') }))
+ .catch(() => createAlert({ message: __('Error occurred while updating the issue status') }))
.finally(() => {
this.toggleStateButtonLoading(false);
});
@@ -214,14 +215,14 @@ export default {
throw new Error();
}
- createFlash({
+ createAlert({
message: this.$options.i18n.promoteSuccessMessage,
- type: FLASH_TYPES.SUCCESS,
+ variant: VARIANT_SUCCESS,
});
visitUrl(data.promoteToEpic.epic.webPath);
})
- .catch(() => createFlash({ message: this.$options.i18n.promoteErrorMessage }))
+ .catch(() => createAlert({ message: this.$options.i18n.promoteErrorMessage }))
.finally(() => {
this.toggleStateButtonLoading(false);
});
@@ -255,7 +256,7 @@ export default {
{{ __('Promote to epic') }}
</gl-dropdown-item>
<gl-dropdown-item v-if="!isIssueAuthor" :href="reportAbusePath">
- {{ __('Report abuse') }}
+ {{ $options.i18n.reportAbuse }}
</gl-dropdown-item>
<gl-dropdown-item
v-if="canReportSpam"
@@ -314,7 +315,7 @@ export default {
{{ __('Promote to epic') }}
</gl-dropdown-item>
<gl-dropdown-item v-if="!isIssueAuthor" :href="reportAbusePath">
- {{ __('Report abuse') }}
+ {{ $options.i18n.reportAbuse }}
</gl-dropdown-item>
<gl-dropdown-item
v-if="canReportSpam"
diff --git a/app/assets/javascripts/issues/show/components/incidents/constants.js b/app/assets/javascripts/issues/show/components/incidents/constants.js
index db846009409..22db19610c1 100644
--- a/app/assets/javascripts/issues/show/components/incidents/constants.js
+++ b/app/assets/javascripts/issues/show/components/incidents/constants.js
@@ -14,6 +14,7 @@ export const timelineFormI18n = Object.freeze({
areaPlaceholder: s__('Incident|Timeline text...'),
save: __('Save'),
cancel: __('Cancel'),
+ delete: __('Delete'),
description: __('Description'),
hint: __('You can enter up to 280 characters'),
textRemaining: (count) => n__('%d character remaining', '%d characters remaining', count),
diff --git a/app/assets/javascripts/issues/show/components/incidents/edit_timeline_event.vue b/app/assets/javascripts/issues/show/components/incidents/edit_timeline_event.vue
index 60fa8cb949b..8cdd62ca9ef 100644
--- a/app/assets/javascripts/issues/show/components/incidents/edit_timeline_event.vue
+++ b/app/assets/javascripts/issues/show/components/incidents/edit_timeline_event.vue
@@ -40,8 +40,10 @@ export default {
:is-event-processed="editTimelineEventActive"
:previous-occurred-at="event.occurredAt"
:previous-note="event.note"
+ show-delete
@save-event="saveEvent"
@cancel="$emit('hide-edit')"
+ @delete="$emit('delete')"
/>
</div>
</template>
diff --git a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql
index f1fc27dcb2a..4a8786b04b1 100644
--- a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql
+++ b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql
@@ -7,6 +7,12 @@ mutation CreateTimelineEvent($input: TimelineEventCreateInput!) {
action
occurredAt
createdAt
+ timelineEventTags {
+ nodes {
+ id
+ name
+ }
+ }
}
errors
}
diff --git a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_alert.graphql b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_alert.graphql
index d88633f2ae9..e057267b006 100644
--- a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_alert.graphql
+++ b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_alert.graphql
@@ -4,6 +4,7 @@ query getAlert($iid: String!, $fullPath: ID!) {
issue(iid: $iid) {
id
alertManagementAlert {
+ id
iid
title
detailsUrl
diff --git a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql
index bc4e8414bfc..baeb81745ab 100644
--- a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql
+++ b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql
@@ -9,6 +9,12 @@ query GetTimelineEvents($fullPath: ID!, $incidentId: IssueID!) {
action
occurredAt
createdAt
+ timelineEventTags {
+ nodes {
+ id
+ name
+ }
+ }
}
}
}
diff --git a/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue b/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue
index 5725d0f8d6a..53956fcb4b2 100644
--- a/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue
+++ b/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue
@@ -1,16 +1,29 @@
<script>
import { GlTab, GlTabs } from '@gitlab/ui';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import { trackIncidentDetailsViewsOptions } from '~/incidents/constants';
import { s__ } from '~/locale';
import Tracking from '~/tracking';
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import DescriptionComponent from '../description.vue';
import getAlert from './graphql/queries/get_alert.graphql';
import HighlightBar from './highlight_bar.vue';
import TimelineTab from './timeline_events_tab.vue';
+export const incidentTabsI18n = Object.freeze({
+ summaryTitle: s__('Incident|Summary'),
+ metricsTitle: s__('Incident|Metrics'),
+ alertsTitle: s__('Incident|Alert details'),
+ timelineTitle: s__('Incident|Timeline'),
+});
+
+export const TAB_NAMES = Object.freeze({
+ SUMMARY: '',
+ ALERTS: 'alerts',
+ METRICS: 'metrics',
+ TIMELINE: 'timeline',
+});
+
export default {
components: {
AlertDetailsTable,
@@ -22,8 +35,8 @@ export default {
IncidentMetricTab: () =>
import('ee_component/issues/show/components/incidents/incident_metric_tab.vue'),
},
- mixins: [glFeatureFlagsMixin()],
- inject: ['fullPath', 'iid'],
+ inject: ['fullPath', 'iid', 'uploadMetricsFeatureAvailable'],
+ i18n: incidentTabsI18n,
apollo: {
alert: {
query: getAlert,
@@ -37,7 +50,7 @@ export default {
return data?.project?.issue?.alertManagementAlert;
},
error() {
- createFlash({
+ createAlert({
message: s__('Incident|There was an issue loading alert data. Please try again.'),
});
},
@@ -46,12 +59,44 @@ export default {
data() {
return {
alert: null,
+ activeTabIndex: 0,
};
},
computed: {
loading() {
return this.$apollo.queries.alert.loading;
},
+ tabMapping() {
+ const availableTabs = [TAB_NAMES.SUMMARY];
+
+ if (this.uploadMetricsFeatureAvailable) {
+ availableTabs.push(TAB_NAMES.METRICS);
+ }
+ if (this.alert) {
+ availableTabs.push(TAB_NAMES.ALERTS);
+ }
+
+ availableTabs.push(TAB_NAMES.TIMELINE);
+
+ const tabNamesToIndex = {};
+ const tabIndexToName = {};
+
+ availableTabs.forEach((item, index) => {
+ tabNamesToIndex[item] = index;
+ tabIndexToName[index] = item;
+ });
+
+ return { tabNamesToIndex, tabIndexToName };
+ },
+ currentTabIndex: {
+ get() {
+ return this.activeTabIndex;
+ },
+ set(index) {
+ this.handleTabChange(index);
+ this.activeTabIndex = index;
+ },
+ },
},
mounted() {
this.trackPageViews();
@@ -91,25 +136,33 @@ export default {
<template>
<div>
<gl-tabs
+ v-model="currentTabIndex"
content-class="gl-reset-line-height"
class="gl-mt-n3"
data-testid="incident-tabs"
- @input="handleTabChange"
>
- <gl-tab :title="s__('Incident|Summary')">
+ <gl-tab :title="$options.i18n.summaryTitle" data-testid="summary-tab">
<highlight-bar :alert="alert" />
<description-component v-bind="$attrs" v-on="$listeners" />
</gl-tab>
- <incident-metric-tab />
+ <gl-tab
+ v-if="uploadMetricsFeatureAvailable"
+ :title="$options.i18n.metricsTitle"
+ data-testid="metrics-tab"
+ >
+ <incident-metric-tab />
+ </gl-tab>
<gl-tab
v-if="alert"
class="alert-management-details"
- :title="s__('Incident|Alert details')"
+ :title="$options.i18n.alertsTitle"
data-testid="alert-details-tab"
>
<alert-details-table :alert="alert" :loading="loading" />
</gl-tab>
- <timeline-tab />
+ <gl-tab :title="$options.i18n.timelineTitle" data-testid="timeline-tab">
+ <timeline-tab />
+ </gl-tab>
</gl-tabs>
</div>
</template>
diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue
index 72dfccca467..f1a3aebc990 100644
--- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue
+++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue
@@ -1,7 +1,6 @@
<script>
import { GlDatepicker, GlFormInput, GlFormGroup, GlButton } from '@gitlab/ui';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
-import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
import { MAX_TEXT_LENGTH, timelineFormI18n } from './constants';
import { getUtcShiftedDate } from './utils';
@@ -27,15 +26,17 @@ export default {
},
i18n: timelineFormI18n,
MAX_TEXT_LENGTH,
- directives: {
- autofocusonshow,
- },
props: {
showSaveAndAdd: {
type: Boolean,
required: false,
default: false,
},
+ showDelete: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
isEventProcessed: {
type: Boolean,
required: true,
@@ -97,7 +98,7 @@ export default {
this.timelineText = '';
},
focusDate() {
- this.$refs.datepicker.$el.querySelector('input').focus();
+ this.$refs.datepicker.$el.querySelector('input')?.focus();
},
handleSave(addAnotherEvent) {
const event = {
@@ -185,32 +186,42 @@ export default {
</gl-form-group>
</div>
<gl-form-group class="gl-mb-0">
- <gl-button
- variant="confirm"
- category="primary"
- class="gl-mr-3"
- data-testid="save-button"
- :disabled="!isTimelineTextValid"
- :loading="isEventProcessed"
- @click="handleSave(false)"
- >
- {{ $options.i18n.save }}
- </gl-button>
- <gl-button
- v-if="showSaveAndAdd"
- variant="confirm"
- category="secondary"
- class="gl-mr-3 gl-ml-n2"
- data-testid="save-and-add-button"
- :disabled="!isTimelineTextValid"
- :loading="isEventProcessed"
- @click="handleSave(true)"
- >
- {{ $options.i18n.saveAndAdd }}
- </gl-button>
- <gl-button class="gl-ml-n2" :disabled="isEventProcessed" @click="$emit('cancel')">
- {{ $options.i18n.cancel }}
- </gl-button>
+ <div class="gl-display-flex">
+ <gl-button
+ variant="confirm"
+ category="primary"
+ class="gl-mr-3"
+ data-testid="save-button"
+ :disabled="!isTimelineTextValid"
+ :loading="isEventProcessed"
+ @click="handleSave(false)"
+ >
+ {{ $options.i18n.save }}
+ </gl-button>
+ <gl-button
+ v-if="showSaveAndAdd"
+ variant="confirm"
+ category="secondary"
+ class="gl-mr-3 gl-ml-n2"
+ data-testid="save-and-add-button"
+ :disabled="!isTimelineTextValid"
+ :loading="isEventProcessed"
+ @click="handleSave(true)"
+ >
+ {{ $options.i18n.saveAndAdd }}
+ </gl-button>
+ <gl-button class="gl-ml-n2" :disabled="isEventProcessed" @click="$emit('cancel')">
+ {{ $options.i18n.cancel }}
+ </gl-button>
+ <gl-button
+ v-if="showDelete"
+ class="gl-ml-auto btn-danger"
+ :disabled="isEventProcessed"
+ @click="$emit('delete')"
+ >
+ {{ $options.i18n.delete }}
+ </gl-button>
+ </div>
<div class="timeline-event-bottom-border"></div>
</gl-form-group>
</form>
diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue
index cbf3c387fa3..90ee4351e39 100644
--- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue
+++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue
@@ -1,5 +1,6 @@
<script>
-import { GlDropdown, GlDropdownItem, GlIcon, GlSafeHtmlDirective, GlSprintf } from '@gitlab/ui';
+import { GlDropdown, GlDropdownItem, GlIcon, GlSprintf, GlBadge } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { formatDate } from '~/lib/utils/datetime_utility';
import { timelineItemI18n } from './constants';
import { getEventIcon } from './utils';
@@ -12,9 +13,10 @@ export default {
GlDropdownItem,
GlIcon,
GlSprintf,
+ GlBadge,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
inject: ['canUpdateTimelineEvent'],
props: {
@@ -30,6 +32,11 @@ export default {
type: String,
required: true,
},
+ eventTag: {
+ type: String,
+ required: false,
+ default: null,
+ },
},
computed: {
time() {
@@ -42,41 +49,41 @@ export default {
};
</script>
<template>
- <div class="gl-display-flex gl-align-items-start">
+ <div class="timeline-event gl-display-grid">
<div
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-bg-white gl-text-gray-200 gl-border-gray-100 gl-border-1 gl-border-solid gl-rounded-full gl-mt-2 gl-mr-3 gl-w-8 gl-h-8 gl-p-3 gl-z-index-1"
>
<gl-icon :name="getEventIcon(action)" class="note-icon" />
</div>
- <div
- class="timeline-event-note timeline-event-border gl-w-full gl-display-flex gl-flex-direction-row"
- data-testid="event-text-container"
- >
- <div>
+ <div class="timeline-event-note timeline-event-border" data-testid="event-text-container">
+ <div class="gl-display-flex gl-align-items-center gl-mb-3">
<strong class="gl-font-lg" data-testid="event-time">
<gl-sprintf :message="$options.i18n.timeUTC">
<template #time>{{ time }}</template>
</gl-sprintf>
</strong>
- <div v-safe-html="noteHtml"></div>
+ <gl-badge v-if="eventTag" variant="muted" icon="tag" class="gl-ml-3">
+ {{ eventTag }}
+ </gl-badge>
</div>
- <gl-dropdown
- v-if="canUpdateTimelineEvent"
- right
- class="event-note-actions gl-ml-auto gl-align-self-start"
- icon="ellipsis_v"
- text-sr-only
- :text="$options.i18n.moreActions"
- category="tertiary"
- no-caret
- >
- <gl-dropdown-item @click="$emit('edit')">
- {{ $options.i18n.edit }}
- </gl-dropdown-item>
- <gl-dropdown-item @click="$emit('delete')">
- {{ $options.i18n.delete }}
- </gl-dropdown-item>
- </gl-dropdown>
+ <div v-safe-html="noteHtml" class="md"></div>
</div>
+ <gl-dropdown
+ v-if="canUpdateTimelineEvent"
+ right
+ class="event-note-actions gl-ml-auto gl-align-self-start"
+ icon="ellipsis_v"
+ text-sr-only
+ :text="$options.i18n.moreActions"
+ category="tertiary"
+ no-caret
+ >
+ <gl-dropdown-item @click="$emit('edit')">
+ {{ $options.i18n.edit }}
+ </gl-dropdown-item>
+ <gl-dropdown-item @click="$emit('delete')">
+ {{ $options.i18n.delete }}
+ </gl-dropdown-item>
+ </gl-dropdown>
</div>
</template>
diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue
index 321b7ccc14a..c6b93201c97 100644
--- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue
+++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue
@@ -50,6 +50,9 @@ export default {
},
},
methods: {
+ getFirstTag(eventTag) {
+ return eventTag.nodes?.[0]?.name;
+ },
handleEditSelection(event) {
this.eventToEdit = event.id;
this.$emit('hide-new-incident-timeline-event-form');
@@ -153,6 +156,7 @@ export default {
:edit-timeline-event-active="editTimelineEventActive"
@handle-save-edit="handleSaveEdit"
@hide-edit="hideEdit()"
+ @delete="handleDelete(event)"
/>
<incident-timeline-event-item
v-else
@@ -160,6 +164,7 @@ export default {
:action="event.action"
:occurred-at="event.occurredAt"
:note-html="event.noteHtml"
+ :event-tag="getFirstTag(event.timelineEventTags)"
@delete="handleDelete(event)"
@edit="handleEditSelection(event)"
/>
diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue
index 5f70d9acac9..c8237766505 100644
--- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue
+++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue
@@ -1,5 +1,5 @@
<script>
-import { GlButton, GlEmptyState, GlLoadingIcon, GlTab } from '@gitlab/ui';
+import { GlButton, GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { TYPE_ISSUE } from '~/graphql_shared/constants';
import { fetchPolicies } from '~/lib/graphql';
@@ -15,7 +15,6 @@ export default {
GlButton,
GlEmptyState,
GlLoadingIcon,
- GlTab,
CreateTimelineEvent,
IncidentTimelineEventsList,
},
@@ -77,7 +76,7 @@ export default {
</script>
<template>
- <gl-tab :title="$options.i18n.title">
+ <div>
<gl-loading-icon v-if="timelineEventLoading" size="lg" color="dark" class="gl-mt-5" />
<gl-empty-state
v-else-if="showEmptyState"
@@ -106,5 +105,5 @@ export default {
>
{{ $options.i18n.addEventButton }}
</gl-button>
- </gl-tab>
+ </div>
</template>
diff --git a/app/assets/javascripts/issues/show/components/locked_warning.vue b/app/assets/javascripts/issues/show/components/locked_warning.vue
index 12feacb027b..4414e693ed0 100644
--- a/app/assets/javascripts/issues/show/components/locked_warning.vue
+++ b/app/assets/javascripts/issues/show/components/locked_warning.vue
@@ -1,29 +1,44 @@
<script>
import { GlSprintf, GlLink, GlAlert } from '@gitlab/ui';
-import { __ } from '~/locale';
+import { __, sprintf } from '~/locale';
+import { IssuableType } from '~/issues/constants';
-const alertMessage = __(
- 'Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs.',
-);
+export const i18n = Object.freeze({
+ alertMessage: __(
+ "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes.",
+ ),
+});
export default {
- alertMessage,
components: {
GlSprintf,
GlLink,
GlAlert,
},
+ props: {
+ issuableType: {
+ type: String,
+ required: true,
+ validator(value) {
+ return Object.values(IssuableType).includes(value);
+ },
+ },
+ },
computed: {
currentPath() {
return window.location.pathname;
},
+ alertMessage() {
+ return sprintf(this.$options.i18n.alertMessage, { issuableType: this.issuableType });
+ },
},
+ i18n,
};
</script>
<template>
<gl-alert variant="danger" class="gl-mb-5" :dismissible="false">
- <gl-sprintf :message="$options.alertMessage">
+ <gl-sprintf :message="alertMessage">
<template #link="{ content }">
<gl-link :href="currentPath" target="_blank" rel="nofollow">
{{ content }}
diff --git a/app/assets/javascripts/issues/show/components/title.vue b/app/assets/javascripts/issues/show/components/title.vue
index 307d9f9f69a..6978f730e1d 100644
--- a/app/assets/javascripts/issues/show/components/title.vue
+++ b/app/assets/javascripts/issues/show/components/title.vue
@@ -1,5 +1,6 @@
<script>
-import { GlButton, GlTooltipDirective, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlButton, GlTooltipDirective } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { __ } from '~/locale';
import eventHub from '../event_hub';
import animateMixin from '../mixins/animate';
diff --git a/app/assets/javascripts/jira_connect/branches/components/source_branch_dropdown.vue b/app/assets/javascripts/jira_connect/branches/components/source_branch_dropdown.vue
index 0e2d8821f36..dac807dceb0 100644
--- a/app/assets/javascripts/jira_connect/branches/components/source_branch_dropdown.vue
+++ b/app/assets/javascripts/jira_connect/branches/components/source_branch_dropdown.vue
@@ -1,5 +1,6 @@
<script>
-import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlLoadingIcon } from '@gitlab/ui';
+import { GlCollapsibleListbox } from '@gitlab/ui';
+import { debounce } from 'lodash';
import { __ } from '~/locale';
import { BRANCHES_PER_PAGE } from '../constants';
import getProjectQuery from '../graphql/queries/get_project.query.graphql';
@@ -7,10 +8,7 @@ import getProjectQuery from '../graphql/queries/get_project.query.graphql';
export default {
BRANCHES_PER_PAGE,
components: {
- GlDropdown,
- GlDropdownItem,
- GlSearchBoxByType,
- GlLoadingIcon,
+ GlCollapsibleListbox,
},
props: {
selectedProject: {
@@ -26,7 +24,6 @@ export default {
},
data() {
return {
- sourceBranchSearchQuery: '',
initialSourceBranchNamesLoading: false,
sourceBranchNamesLoading: false,
sourceBranchNames: [],
@@ -59,6 +56,9 @@ export default {
onSourceBranchSelect(branchName) {
this.$emit('change', branchName);
},
+ onSearch: debounce(function debouncedSearch(branchSearchQuery) {
+ this.onSourceBranchSearchQuery(branchSearchQuery);
+ }, 250),
onSourceBranchSearchQuery(branchSearchQuery) {
this.branchSearchQuery = branchSearchQuery;
this.fetchSourceBranchNames({
@@ -83,7 +83,10 @@ export default {
});
const { branchNames, rootRef } = data?.project.repository || {};
- this.sourceBranchNames = branchNames || [];
+ this.sourceBranchNames =
+ branchNames.map((value) => {
+ return { text: value, value };
+ }) || [];
// Use root ref as the default selection
if (rootRef && !this.hasSelectedSourceBranch) {
@@ -102,33 +105,15 @@ export default {
</script>
<template>
- <gl-dropdown
- :text="branchDropdownText"
- :loading="initialSourceBranchNamesLoading"
- :disabled="!hasSelectedProject"
+ <gl-collapsible-listbox
:class="{ 'gl-font-monospace': hasSelectedSourceBranch }"
- >
- <template #header>
- <gl-search-box-by-type
- :debounce="250"
- :value="sourceBranchSearchQuery"
- @input="onSourceBranchSearchQuery"
- />
- </template>
-
- <gl-loading-icon v-show="sourceBranchNamesLoading" />
- <template v-if="!sourceBranchNamesLoading">
- <gl-dropdown-item
- v-for="branchName in sourceBranchNames"
- v-show="!sourceBranchNamesLoading"
- :key="branchName"
- :is-checked="branchName === selectedBranchName"
- is-check-item
- class="gl-font-monospace"
- @click="onSourceBranchSelect(branchName)"
- >
- {{ branchName }}
- </gl-dropdown-item>
- </template>
- </gl-dropdown>
+ :disabled="!hasSelectedProject"
+ :items="sourceBranchNames"
+ :loading="initialSourceBranchNamesLoading"
+ :searchable="true"
+ :searching="sourceBranchNamesLoading"
+ :toggle-text="branchDropdownText"
+ @search="onSearch"
+ @select="onSourceBranchSelect"
+ />
</template>
diff --git a/app/assets/javascripts/jira_connect/subscriptions/constants.js b/app/assets/javascripts/jira_connect/subscriptions/constants.js
index fc365746b54..01bc5dfc66b 100644
--- a/app/assets/javascripts/jira_connect/subscriptions/constants.js
+++ b/app/assets/javascripts/jira_connect/subscriptions/constants.js
@@ -38,7 +38,7 @@ export const INTEGRATIONS_DOC_LINK = helpPagePath('integration/jira/development_
anchor: 'use-the-integration',
});
export const OAUTH_SELF_MANAGED_DOC_LINK = helpPagePath('integration/jira/connect-app', {
- anchor: 'install-the-gitlabcom-for-jira-cloud-app-for-self-managed-instances',
+ anchor: 'connect-the-gitlabcom-for-jira-cloud-app-for-self-managed-instances',
});
export const GITLAB_COM_BASE_PATH = 'https://gitlab.com';
diff --git a/app/assets/javascripts/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/index.vue b/app/assets/javascripts/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/index.vue
index 5ff75e19425..7c6ff002014 100644
--- a/app/assets/javascripts/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/index.vue
+++ b/app/assets/javascripts/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/index.vue
@@ -5,10 +5,14 @@ import { s__ } from '~/locale';
import { reloadPage, persistBaseUrl, retrieveBaseUrl } from '~/jira_connect/subscriptions/utils';
import { updateInstallation, setApiBaseURL } from '~/jira_connect/subscriptions/api';
-import { I18N_UPDATE_INSTALLATION_ERROR_MESSAGE } from '~/jira_connect/subscriptions/constants';
+import {
+ GITLAB_COM_BASE_PATH,
+ I18N_UPDATE_INSTALLATION_ERROR_MESSAGE,
+} from '~/jira_connect/subscriptions/constants';
import { SET_ALERT } from '~/jira_connect/subscriptions/store/mutation_types';
import SignInOauthButton from '../../../components/sign_in_oauth_button.vue';
+import SetupInstructions from './setup_instructions.vue';
import VersionSelectForm from './version_select_form.vue';
export default {
@@ -16,12 +20,14 @@ export default {
components: {
GlButton,
SignInOauthButton,
+ SetupInstructions,
VersionSelectForm,
},
data() {
return {
gitlabBasePath: null,
loadingVersionSelect: false,
+ showSetupInstructions: false,
};
},
computed: {
@@ -37,6 +43,9 @@ export default {
mounted() {
this.gitlabBasePath = retrieveBaseUrl();
setApiBaseURL(this.gitlabBasePath);
+ if (this.gitlabBasePath !== GITLAB_COM_BASE_PATH) {
+ this.showSetupInstructions = true;
+ }
},
methods: {
...mapMutations({
@@ -61,6 +70,9 @@ export default {
this.loadingVersionSelect = false;
});
},
+ onSetupNext() {
+ this.showSetupInstructions = false;
+ },
onSignInError() {
this.$emit('error');
},
@@ -88,19 +100,23 @@ export default {
@submit="onVersionSelect"
/>
- <div v-else class="gl-text-center">
- <sign-in-oauth-button
- class="gl-mb-5"
- :gitlab-base-path="gitlabBasePath"
- @sign-in="$emit('sign-in-oauth', $event)"
- @error="onSignInError"
- />
+ <template v-else>
+ <setup-instructions v-if="showSetupInstructions" @next="onSetupNext" />
+
+ <div v-else class="gl-text-center">
+ <sign-in-oauth-button
+ class="gl-mb-5"
+ :gitlab-base-path="gitlabBasePath"
+ @sign-in="$emit('sign-in-oauth', $event)"
+ @error="onSignInError"
+ />
- <div>
- <gl-button category="tertiary" variant="confirm" @click="resetGitlabBasePath">
- {{ $options.i18n.changeVersionButtonText }}
- </gl-button>
+ <div>
+ <gl-button category="tertiary" variant="confirm" @click="resetGitlabBasePath">
+ {{ $options.i18n.changeVersionButtonText }}
+ </gl-button>
+ </div>
</div>
- </div>
+ </template>
</div>
</template>
diff --git a/app/assets/javascripts/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions.vue b/app/assets/javascripts/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions.vue
new file mode 100644
index 00000000000..00fa739b518
--- /dev/null
+++ b/app/assets/javascripts/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions.vue
@@ -0,0 +1,35 @@
+<script>
+import { GlButton, GlLink } from '@gitlab/ui';
+import { OAUTH_SELF_MANAGED_DOC_LINK } from '~/jira_connect/subscriptions/constants';
+
+export default {
+ components: {
+ GlButton,
+ GlLink,
+ },
+ OAUTH_SELF_MANAGED_DOC_LINK,
+};
+</script>
+
+<template>
+ <div class="gl-max-w-62 gl-mx-auto gl-mt-7">
+ <h3>{{ s__('JiraService|Continue setup in GitLab') }}</h3>
+ <p>
+ {{
+ s__(
+ 'JiraService|In order to complete the set up, you’ll need to complete a few steps in GitLab.',
+ )
+ }}
+ <gl-link
+ class="gl-reset-font-size!"
+ :href="$options.OAUTH_SELF_MANAGED_DOC_LINK"
+ target="_blank"
+ >{{ __('Learn more') }}</gl-link
+ >
+ </p>
+
+ <gl-button variant="confirm" @click="$emit('next')">
+ {{ __('Next') }}
+ </gl-button>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/version_select_form.vue b/app/assets/javascripts/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/version_select_form.vue
index 6b32225ed11..37a65946b3f 100644
--- a/app/assets/javascripts/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/version_select_form.vue
+++ b/app/assets/javascripts/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/version_select_form.vue
@@ -55,7 +55,6 @@ export default {
},
radioOptions: RADIO_OPTIONS,
i18n: {
- title: s__('JiraService|Welcome to GitLab for Jira'),
saasRadioLabel: __('GitLab.com (SaaS)'),
saasRadioHelp: __('Most common'),
selfManagedRadioLabel: __('GitLab (self-managed)'),
diff --git a/app/assets/javascripts/jobs/components/filtered_search/jobs_filtered_search.vue b/app/assets/javascripts/jobs/components/filtered_search/jobs_filtered_search.vue
index e498a735898..67cdca6aa0a 100644
--- a/app/assets/javascripts/jobs/components/filtered_search/jobs_filtered_search.vue
+++ b/app/assets/javascripts/jobs/components/filtered_search/jobs_filtered_search.vue
@@ -1,13 +1,13 @@
<script>
import { GlFilteredSearch } from '@gitlab/ui';
-import { s__ } from '~/locale';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
+import {
+ OPERATORS_IS,
+ TOKEN_TITLE_STATUS,
+ TOKEN_TYPE_STATUS,
+} from '~/vue_shared/components/filtered_search_bar/constants';
import JobStatusToken from './tokens/job_status_token.vue';
export default {
- tokenTypes: {
- status: 'status',
- },
components: {
GlFilteredSearch,
},
@@ -22,12 +22,12 @@ export default {
tokens() {
return [
{
- type: this.$options.tokenTypes.status,
+ type: TOKEN_TYPE_STATUS,
icon: 'status',
- title: s__('Jobs|Status'),
+ title: TOKEN_TITLE_STATUS,
unique: true,
token: JobStatusToken,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
},
];
},
@@ -35,7 +35,7 @@ export default {
if (this.queryString?.statuses) {
return [
{
- type: 'status',
+ type: TOKEN_TYPE_STATUS,
value: {
data: this.queryString?.statuses,
operator: '=',
diff --git a/app/assets/javascripts/jobs/components/job/empty_state.vue b/app/assets/javascripts/jobs/components/job/empty_state.vue
index 65b9600e664..d0a39025807 100644
--- a/app/assets/javascripts/jobs/components/job/empty_state.vue
+++ b/app/assets/javascripts/jobs/components/job/empty_state.vue
@@ -1,16 +1,12 @@
<script>
import { GlLink } from '@gitlab/ui';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import LegacyManualVariablesForm from '~/jobs/components/job/legacy_manual_variables_form.vue';
import ManualVariablesForm from '~/jobs/components/job/manual_variables_form.vue';
export default {
components: {
GlLink,
- LegacyManualVariablesForm,
ManualVariablesForm,
},
- mixins: [glFeatureFlagsMixin()],
props: {
illustrationPath: {
type: String,
@@ -20,6 +16,14 @@ export default {
type: String,
required: true,
},
+ isRetryable: {
+ type: Boolean,
+ required: true,
+ },
+ jobId: {
+ type: Number,
+ required: true,
+ },
title: {
type: String,
required: true,
@@ -54,9 +58,6 @@ export default {
},
},
computed: {
- isGraphQL() {
- return this.glFeatures?.graphqlJobApp;
- },
shouldRenderManualVariables() {
return this.playable && !this.scheduled;
},
@@ -77,14 +78,14 @@ export default {
<p v-if="content" data-testid="job-empty-state-content">{{ content }}</p>
</div>
- <template v-if="isGraphQL">
- <manual-variables-form v-if="shouldRenderManualVariables" :action="action" />
- </template>
- <template v-else>
- <legacy-manual-variables-form v-if="shouldRenderManualVariables" :action="action" />
- </template>
- <div class="text-content">
- <div v-if="action && !shouldRenderManualVariables" class="text-center">
+ <manual-variables-form
+ v-if="shouldRenderManualVariables"
+ :is-retryable="isRetryable"
+ :job-id="jobId"
+ @hideManualVariablesForm="$emit('hideManualVariablesForm')"
+ />
+ <div v-if="action && !shouldRenderManualVariables" class="text-content">
+ <div class="text-center">
<gl-link
:href="action.path"
:data-method="action.method"
diff --git a/app/assets/javascripts/jobs/components/job/graphql/mutations/job_retry_with_variables.mutation.graphql b/app/assets/javascripts/jobs/components/job/graphql/mutations/job_retry_with_variables.mutation.graphql
new file mode 100644
index 00000000000..2b79892a072
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job/graphql/mutations/job_retry_with_variables.mutation.graphql
@@ -0,0 +1,16 @@
+mutation retryJobWithVariables($id: CiBuildID!, $variables: [CiVariableInput!]) {
+ jobRetry(input: { id: $id, variables: $variables }) {
+ job {
+ id
+ manualVariables {
+ nodes {
+ id
+ key
+ value
+ }
+ }
+ webPath
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/jobs/components/job/graphql/queries/get_job.query.graphql b/app/assets/javascripts/jobs/components/job/graphql/queries/get_job.query.graphql
new file mode 100644
index 00000000000..aaf1dec8e0f
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job/graphql/queries/get_job.query.graphql
@@ -0,0 +1,17 @@
+query getJob($fullPath: ID!, $id: JobID!) {
+ project(fullPath: $fullPath) {
+ id
+ job(id: $id) {
+ id
+ manualJob
+ manualVariables {
+ nodes {
+ id
+ key
+ value
+ }
+ }
+ name
+ }
+ }
+}
diff --git a/app/assets/javascripts/jobs/components/job/job_app.vue b/app/assets/javascripts/jobs/components/job/job_app.vue
index 81b65d175a7..c6d900ef13e 100644
--- a/app/assets/javascripts/jobs/components/job/job_app.vue
+++ b/app/assets/javascripts/jobs/components/job/job_app.vue
@@ -1,8 +1,9 @@
<script>
-import { GlLoadingIcon, GlIcon, GlSafeHtmlDirective as SafeHtml, GlAlert } from '@gitlab/ui';
+import { GlLoadingIcon, GlIcon, GlAlert } from '@gitlab/ui';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import { throttle, isEmpty } from 'lodash';
import { mapGetters, mapState, mapActions } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
import { __, sprintf } from '~/locale';
import CiHeader from '~/vue_shared/components/header_ci_component.vue';
@@ -71,6 +72,7 @@ export default {
data() {
return {
searchResults: [],
+ showUpdateVariablesState: false,
};
},
computed: {
@@ -121,6 +123,10 @@ export default {
return this.shouldRenderCalloutMessage && !this.hasUnmetPrerequisitesFailure;
},
+ isJobRetryable() {
+ return Boolean(this.job.retry_path);
+ },
+
itemName() {
return sprintf(__('Job %{jobName}'), { jobName: this.job.name });
},
@@ -168,10 +174,16 @@ export default {
'toggleScrollButtons',
'toggleScrollAnimation',
]),
+ onHideManualVariablesForm() {
+ this.showUpdateVariablesState = false;
+ },
onResize() {
this.updateSidebar();
this.updateScroll();
},
+ onUpdateVariables() {
+ this.showUpdateVariablesState = true;
+ },
updateSidebar() {
const breakpoint = bp.getBreakpointSize();
if (breakpoint === 'xs' || breakpoint === 'sm') {
@@ -271,14 +283,12 @@ export default {
</div>
<!-- job log -->
<div
- v-if="hasJobLog"
+ v-if="hasJobLog && !showUpdateVariablesState"
class="build-log-container gl-relative"
:class="{ 'gl-mt-3': !job.archived }"
>
<log-top-bar
:class="{
- 'sidebar-expanded': isSidebarOpen,
- 'sidebar-collapsed': !isSidebarOpen,
'has-archived-block': job.archived,
}"
:size="jobLogSize"
@@ -299,14 +309,17 @@ export default {
<!-- empty state -->
<empty-state
- v-if="!hasJobLog"
+ v-if="!hasJobLog || showUpdateVariablesState"
:illustration-path="emptyStateIllustration.image"
:illustration-size-class="emptyStateIllustration.size"
+ :is-retryable="isJobRetryable"
+ :job-id="job.id"
:title="emptyStateTitle"
:content="emptyStateIllustration.content"
:action="emptyStateAction"
:playable="job.playable"
:scheduled="job.scheduled"
+ @hideManualVariablesForm="onHideManualVariablesForm()"
/>
<!-- EO empty state -->
@@ -320,9 +333,9 @@ export default {
'right-sidebar-expanded': isSidebarOpen,
'right-sidebar-collapsed': !isSidebarOpen,
}"
- :erase-path="job.erase_path"
:artifact-help-url="artifactHelpUrl"
data-testid="job-sidebar"
+ @updateVariables="onUpdateVariables()"
/>
</div>
</template>
diff --git a/app/assets/javascripts/jobs/components/job/legacy_manual_variables_form.vue b/app/assets/javascripts/jobs/components/job/legacy_manual_variables_form.vue
deleted file mode 100644
index 1898e02c94e..00000000000
--- a/app/assets/javascripts/jobs/components/job/legacy_manual_variables_form.vue
+++ /dev/null
@@ -1,192 +0,0 @@
-<script>
-import {
- GlFormInputGroup,
- GlInputGroupText,
- GlFormInput,
- GlButton,
- GlLink,
- GlSprintf,
-} from '@gitlab/ui';
-import { uniqueId } from 'lodash';
-import { mapActions } from 'vuex';
-import { helpPagePath } from '~/helpers/help_page_helper';
-import { s__ } from '~/locale';
-
-export default {
- name: 'ManualVariablesForm',
- components: {
- GlFormInputGroup,
- GlInputGroupText,
- GlFormInput,
- GlButton,
- GlLink,
- GlSprintf,
- },
- props: {
- action: {
- type: Object,
- required: false,
- default: null,
- validator(value) {
- return (
- value === null ||
- (Object.prototype.hasOwnProperty.call(value, 'path') &&
- Object.prototype.hasOwnProperty.call(value, 'method') &&
- Object.prototype.hasOwnProperty.call(value, 'button_title'))
- );
- },
- },
- },
- inputTypes: {
- key: 'key',
- value: 'value',
- },
- i18n: {
- header: s__('CiVariables|Variables'),
- keyLabel: s__('CiVariables|Key'),
- valueLabel: s__('CiVariables|Value'),
- keyPlaceholder: s__('CiVariables|Input variable key'),
- valuePlaceholder: s__('CiVariables|Input variable value'),
- formHelpText: s__(
- 'CiVariables|Specify variable values to be used in this run. The values specified in %{linkStart}CI/CD settings%{linkEnd} will be used as default',
- ),
- },
- data() {
- return {
- variables: [
- {
- key: '',
- secretValue: '',
- id: uniqueId(),
- },
- ],
- triggerBtnDisabled: false,
- };
- },
- computed: {
- variableSettings() {
- return helpPagePath('ci/variables/index', { anchor: 'add-a-cicd-variable-to-a-project' });
- },
- preparedVariables() {
- // we need to ensure no empty variables are passed to the API
- // and secretValue should be snake_case when passed to the API
- return this.variables
- .filter((variable) => variable.key !== '')
- .map(({ key, secretValue }) => ({ key, secret_value: secretValue }));
- },
- },
- methods: {
- ...mapActions(['triggerManualJob']),
- addEmptyVariable() {
- const lastVar = this.variables[this.variables.length - 1];
-
- if (lastVar.key === '') {
- return;
- }
-
- this.variables.push({
- key: '',
- secret_value: '',
- id: uniqueId(),
- });
- },
- canRemove(index) {
- return index < this.variables.length - 1;
- },
- deleteVariable(id) {
- this.variables.splice(
- this.variables.findIndex((el) => el.id === id),
- 1,
- );
- },
- inputRef(type, id) {
- return `${this.$options.inputTypes[type]}-${id}`;
- },
- trigger() {
- this.triggerBtnDisabled = true;
-
- this.triggerManualJob(this.preparedVariables);
- },
- },
-};
-</script>
-<template>
- <div class="row gl-justify-content-center">
- <div class="col-10" data-testid="manual-vars-form">
- <label>{{ $options.i18n.header }}</label>
-
- <div
- v-for="(variable, index) in variables"
- :key="variable.id"
- class="gl-display-flex gl-align-items-center gl-mb-4"
- data-testid="ci-variable-row"
- >
- <gl-form-input-group class="gl-mr-4 gl-flex-grow-1">
- <template #prepend>
- <gl-input-group-text>
- {{ $options.i18n.keyLabel }}
- </gl-input-group-text>
- </template>
- <gl-form-input
- :ref="inputRef('key', variable.id)"
- v-model="variable.key"
- :placeholder="$options.i18n.keyPlaceholder"
- data-testid="ci-variable-key"
- @change="addEmptyVariable"
- />
- </gl-form-input-group>
-
- <gl-form-input-group class="gl-flex-grow-2">
- <template #prepend>
- <gl-input-group-text>
- {{ $options.i18n.valueLabel }}
- </gl-input-group-text>
- </template>
- <gl-form-input
- :ref="inputRef('value', variable.id)"
- v-model="variable.secretValue"
- :placeholder="$options.i18n.valuePlaceholder"
- data-testid="ci-variable-value"
- />
- </gl-form-input-group>
-
- <gl-button
- v-if="canRemove(index)"
- class="gl-flex-grow-0 gl-flex-basis-0"
- category="tertiary"
- variant="danger"
- icon="clear"
- :aria-label="__('Delete variable')"
- data-testid="delete-variable-btn"
- @click="deleteVariable(variable.id)"
- />
-
- <!-- delete variable button placeholder to not break flex layout -->
- <div v-else class="gl-w-7 gl-mr-3" data-testid="delete-variable-btn-placeholder"></div>
- </div>
-
- <div class="gl-text-center gl-mt-5">
- <gl-sprintf :message="$options.i18n.formHelpText">
- <template #link="{ content }">
- <gl-link :href="variableSettings" target="_blank">
- {{ content }}
- </gl-link>
- </template>
- </gl-sprintf>
- </div>
- <div class="gl-display-flex gl-justify-content-center gl-mt-5">
- <gl-button
- class="gl-mt-5"
- variant="confirm"
- category="primary"
- :aria-label="__('Trigger manual job')"
- :disabled="triggerBtnDisabled"
- data-testid="trigger-manual-job-btn"
- @click="trigger"
- >
- {{ action.button_title }}
- </gl-button>
- </div>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/jobs/components/job/manual_variables_form.vue b/app/assets/javascripts/jobs/components/job/manual_variables_form.vue
index 2f97301979c..d7bbd6daed2 100644
--- a/app/assets/javascripts/jobs/components/job/manual_variables_form.vue
+++ b/app/assets/javascripts/jobs/components/job/manual_variables_form.vue
@@ -5,15 +5,24 @@ import {
GlFormInput,
GlButton,
GlLink,
+ GlLoadingIcon,
GlSprintf,
+ GlTooltipDirective,
} from '@gitlab/ui';
-import { uniqueId } from 'lodash';
+import { cloneDeep, uniqueId } from 'lodash';
import { mapActions } from 'vuex';
+import { fetchPolicies } from '~/lib/graphql';
+import { createAlert } from '~/flash';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+import { JOB_GRAPHQL_ERRORS, GRAPHQL_ID_TYPES } from '~/jobs/constants';
import { helpPagePath } from '~/helpers/help_page_helper';
+import { redirectTo } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
+import GetJob from './graphql/queries/get_job.query.graphql';
+import retryJobWithVariablesMutation from './graphql/mutations/job_retry_with_variables.mutation.graphql';
// This component is a port of ~/jobs/components/job/legacy_manual_variables_form.vue
-// It is meant to fetch the job information via GraphQL instead of REST API.
+// It is meant to fetch/update the job information via GraphQL instead of REST API.
export default {
name: 'ManualVariablesForm',
@@ -23,59 +32,93 @@ export default {
GlFormInput,
GlButton,
GlLink,
+ GlLoadingIcon,
GlSprintf,
},
- props: {
- action: {
- type: Object,
- required: false,
- default: null,
- validator(value) {
- return (
- value === null ||
- (Object.prototype.hasOwnProperty.call(value, 'path') &&
- Object.prototype.hasOwnProperty.call(value, 'method') &&
- Object.prototype.hasOwnProperty.call(value, 'button_title'))
- );
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ inject: ['projectPath'],
+ apollo: {
+ variables: {
+ query: GetJob,
+ variables() {
+ return {
+ fullPath: this.projectPath,
+ id: convertToGraphQLId(GRAPHQL_ID_TYPES.commitStatus, this.jobId),
+ };
+ },
+ fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
+ update(data) {
+ const jobVariables = cloneDeep(data?.project?.job?.manualVariables?.nodes);
+ return [...jobVariables.reverse(), ...this.variables];
+ },
+ error() {
+ createAlert({ message: JOB_GRAPHQL_ERRORS.jobQueryErrorText });
},
},
},
+ props: {
+ isRetryable: {
+ type: Boolean,
+ required: true,
+ },
+ jobId: {
+ type: Number,
+ required: true,
+ },
+ },
inputTypes: {
key: 'key',
value: 'value',
},
i18n: {
+ clearInputs: s__('CiVariables|Clear inputs'),
+ formHelpText: s__(
+ 'CiVariables|Specify variable values to be used in this run. The values specified in %{linkStart}CI/CD settings%{linkEnd} will be used as default',
+ ),
header: s__('CiVariables|Variables'),
keyLabel: s__('CiVariables|Key'),
- valueLabel: s__('CiVariables|Value'),
keyPlaceholder: s__('CiVariables|Input variable key'),
+ runAgainButtonText: s__('CiVariables|Run job again'),
+ triggerButtonText: s__('CiVariables|Trigger this manual action'),
+ valueLabel: s__('CiVariables|Value'),
valuePlaceholder: s__('CiVariables|Input variable value'),
- formHelpText: s__(
- 'CiVariables|Specify variable values to be used in this run. The values specified in %{linkStart}CI/CD settings%{linkEnd} will be used as default',
- ),
+ },
+ variableValueKeys: {
+ rest: 'secret_value',
+ gql: 'value',
},
data() {
return {
+ job: {},
variables: [
{
- key: '',
- secretValue: '',
id: uniqueId(),
+ key: '',
+ value: '',
},
],
+ runAgainBtnDisabled: false,
triggerBtnDisabled: false,
};
},
computed: {
- variableSettings() {
- return helpPagePath('ci/variables/index', { anchor: 'add-a-cicd-variable-to-a-project' });
- },
preparedVariables() {
- // we need to ensure no empty variables are passed to the API
- // and secretValue should be snake_case when passed to the API
+ // filtering out 'id' along with empty variables to send only key, value in the mutation.
+ // This will be removed in: https://gitlab.com/gitlab-org/gitlab/-/issues/377268
+
return this.variables
.filter((variable) => variable.key !== '')
- .map(({ key, secretValue }) => ({ key, secret_value: secretValue }));
+ .map(({ key, value }) => ({ key, [this.valueKey]: value }));
+ },
+ valueKey() {
+ return this.isRetryable
+ ? this.$options.variableValueKeys.gql
+ : this.$options.variableValueKeys.rest;
+ },
+ variableSettings() {
+ return helpPagePath('ci/variables/index', { anchor: 'add-a-cicd-variable-to-a-project' });
},
},
methods: {
@@ -88,9 +131,9 @@ export default {
}
this.variables.push({
- key: '',
- secret_value: '',
id: uniqueId(),
+ key: '',
+ value: '',
});
},
canRemove(index) {
@@ -105,7 +148,34 @@ export default {
inputRef(type, id) {
return `${this.$options.inputTypes[type]}-${id}`;
},
- trigger() {
+ navigateToRetriedJob(retryPath) {
+ redirectTo(retryPath);
+ },
+ async retryJob() {
+ try {
+ const { data } = await this.$apollo.mutate({
+ mutation: retryJobWithVariablesMutation,
+ variables: {
+ id: convertToGraphQLId(GRAPHQL_ID_TYPES.ciBuild, this.jobId),
+ // we need to ensure no empty variables are passed to the API
+ variables: this.preparedVariables,
+ },
+ });
+ if (data.jobRetry?.errors?.length) {
+ createAlert({ message: data.jobRetry.errors[0] });
+ } else {
+ this.navigateToRetriedJob(data.jobRetry?.job?.webPath);
+ }
+ } catch (error) {
+ createAlert({ message: JOB_GRAPHQL_ERRORS.retryMutationErrorText });
+ }
+ },
+ runAgain() {
+ this.runAgainBtnDisabled = true;
+
+ this.retryJob();
+ },
+ triggerJob() {
this.triggerBtnDisabled = true;
this.triggerManualJob(this.preparedVariables);
@@ -114,7 +184,8 @@ export default {
};
</script>
<template>
- <div class="row gl-justify-content-center">
+ <gl-loading-icon v-if="$apollo.queries.variables.loading" class="gl-mt-9" size="lg" />
+ <div v-else class="row gl-justify-content-center">
<div class="col-10" data-testid="manual-vars-form">
<label>{{ $options.i18n.header }}</label>
@@ -147,7 +218,7 @@ export default {
</template>
<gl-form-input
:ref="inputRef('value', variable.id)"
- v-model="variable.secretValue"
+ v-model="variable.value"
:placeholder="$options.i18n.valuePlaceholder"
data-testid="ci-variable-value"
/>
@@ -155,11 +226,13 @@ export default {
<gl-button
v-if="canRemove(index)"
+ v-gl-tooltip
+ :aria-label="$options.i18n.clearInputs"
+ :title="$options.i18n.clearInputs"
class="gl-flex-grow-0 gl-flex-basis-0"
category="tertiary"
variant="danger"
icon="clear"
- :aria-label="__('Delete variable')"
data-testid="delete-variable-btn"
@click="deleteVariable(variable.id)"
/>
@@ -177,7 +250,27 @@ export default {
</template>
</gl-sprintf>
</div>
- <div class="gl-display-flex gl-justify-content-center gl-mt-5">
+ <div v-if="isRetryable" class="gl-display-flex gl-justify-content-center gl-mt-5">
+ <gl-button
+ class="gl-mt-5"
+ :aria-label="__('Cancel')"
+ data-testid="cancel-btn"
+ @click="$emit('hideManualVariablesForm')"
+ >{{ __('Cancel') }}</gl-button
+ >
+ <gl-button
+ class="gl-mt-5"
+ variant="confirm"
+ category="primary"
+ :aria-label="__('Run manual job again')"
+ :disabled="runAgainBtnDisabled"
+ data-testid="run-manual-job-btn"
+ @click="runAgain"
+ >
+ {{ $options.i18n.runAgainButtonText }}
+ </gl-button>
+ </div>
+ <div v-else class="gl-display-flex gl-justify-content-center gl-mt-5">
<gl-button
class="gl-mt-5"
variant="confirm"
@@ -185,9 +278,9 @@ export default {
:aria-label="__('Trigger manual job')"
:disabled="triggerBtnDisabled"
data-testid="trigger-manual-job-btn"
- @click="trigger"
+ @click="triggerJob"
>
- {{ action.button_title }}
+ {{ $options.i18n.triggerButtonText }}
</gl-button>
</div>
</div>
diff --git a/app/assets/javascripts/jobs/components/job/sidebar/job_sidebar_retry_button.vue b/app/assets/javascripts/jobs/components/job/sidebar/job_sidebar_retry_button.vue
index dd620977f0c..7183a8b5d03 100644
--- a/app/assets/javascripts/jobs/components/job/sidebar/job_sidebar_retry_button.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/job_sidebar_retry_button.vue
@@ -1,15 +1,17 @@
<script>
-import { GlButton, GlModalDirective } from '@gitlab/ui';
+import { GlButton, GlDropdown, GlDropdownItem, GlModalDirective } from '@gitlab/ui';
import { mapGetters } from 'vuex';
import { JOB_SIDEBAR_COPY } from '~/jobs/constants';
export default {
name: 'JobSidebarRetryButton',
i18n: {
- retryLabel: JOB_SIDEBAR_COPY.retry,
+ ...JOB_SIDEBAR_COPY,
},
components: {
GlButton,
+ GlDropdown,
+ GlDropdownItem,
},
directives: {
GlModal: GlModalDirective,
@@ -23,6 +25,10 @@ export default {
type: String,
required: true,
},
+ isManualJob: {
+ type: Boolean,
+ required: true,
+ },
},
computed: {
...mapGetters(['hasForwardDeploymentFailure']),
@@ -33,17 +39,30 @@ export default {
<gl-button
v-if="hasForwardDeploymentFailure"
v-gl-modal="modalId"
- :aria-label="$options.i18n.retryLabel"
+ :aria-label="$options.i18n.retryJobLabel"
category="primary"
variant="confirm"
icon="retry"
data-testid="retry-job-button"
/>
-
+ <gl-dropdown
+ v-else-if="isManualJob"
+ icon="retry"
+ category="primary"
+ :right="true"
+ variant="confirm"
+ >
+ <gl-dropdown-item :href="href" data-method="post">
+ {{ $options.i18n.runAgainJobButtonLabel }}
+ </gl-dropdown-item>
+ <gl-dropdown-item @click="$emit('updateVariablesClicked')">
+ {{ $options.i18n.updateVariables }}
+ </gl-dropdown-item>
+ </gl-dropdown>
<gl-button
v-else
:href="href"
- :aria-label="$options.i18n.retryLabel"
+ :aria-label="$options.i18n.retryJobLabel"
category="primary"
variant="confirm"
icon="retry"
diff --git a/app/assets/javascripts/jobs/components/job/sidebar/legacy_sidebar_header.vue b/app/assets/javascripts/jobs/components/job/sidebar/legacy_sidebar_header.vue
deleted file mode 100644
index 64b497c3550..00000000000
--- a/app/assets/javascripts/jobs/components/job/sidebar/legacy_sidebar_header.vue
+++ /dev/null
@@ -1,104 +0,0 @@
-<script>
-import { GlButton, GlTooltipDirective } from '@gitlab/ui';
-import { mapActions } from 'vuex';
-import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
-import { JOB_SIDEBAR_COPY, forwardDeploymentFailureModalId } from '~/jobs/constants';
-import JobSidebarRetryButton from './job_sidebar_retry_button.vue';
-
-export default {
- name: 'LegacySidebarHeader',
- i18n: {
- ...JOB_SIDEBAR_COPY,
- },
- forwardDeploymentFailureModalId,
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- components: {
- GlButton,
- JobSidebarRetryButton,
- TooltipOnTruncate,
- },
- props: {
- job: {
- type: Object,
- required: true,
- default: () => ({}),
- },
- erasePath: {
- type: String,
- required: false,
- default: null,
- },
- },
- computed: {
- retryButtonCategory() {
- return this.job.status && this.job.recoverable ? 'primary' : 'secondary';
- },
- buttonTitle() {
- return this.job.status && this.job.status.text === 'passed'
- ? this.$options.i18n.runAgainJobButtonLabel
- : this.$options.i18n.retryJobButtonLabel;
- },
- },
- methods: {
- ...mapActions(['toggleSidebar']),
- },
-};
-</script>
-
-<template>
- <div class="gl-py-5 gl-display-flex gl-align-items-center">
- <tooltip-on-truncate :title="job.name" truncate-target="child"
- ><h4 class="gl-my-0 gl-mr-3 gl-text-truncate">
- {{ job.name }}
- </h4>
- </tooltip-on-truncate>
- <div class="gl-flex-grow-1 gl-flex-shrink-0 gl-text-right">
- <gl-button
- v-if="erasePath"
- v-gl-tooltip.left
- :title="$options.i18n.eraseLogButtonLabel"
- :aria-label="$options.i18n.eraseLogButtonLabel"
- :href="erasePath"
- :data-confirm="$options.i18n.eraseLogConfirmText"
- class="gl-mr-2"
- data-testid="job-log-erase-link"
- data-confirm-btn-variant="danger"
- data-method="post"
- icon="remove"
- />
- <job-sidebar-retry-button
- v-if="job.retry_path"
- v-gl-tooltip.left
- :title="buttonTitle"
- :aria-label="buttonTitle"
- :category="retryButtonCategory"
- :href="job.retry_path"
- :modal-id="$options.forwardDeploymentFailureModalId"
- variant="confirm"
- data-qa-selector="retry_button"
- data-testid="retry-button"
- />
- <gl-button
- v-if="job.cancel_path"
- v-gl-tooltip.left
- :title="$options.i18n.cancelJobButtonLabel"
- :aria-label="$options.i18n.cancelJobButtonLabel"
- :href="job.cancel_path"
- variant="danger"
- icon="cancel"
- data-method="post"
- data-testid="cancel-button"
- rel="nofollow"
- />
- <gl-button
- :aria-label="$options.i18n.toggleSidebar"
- category="tertiary"
- class="gl-md-display-none gl-ml-2"
- icon="chevron-double-lg-right"
- @click="toggleSidebar"
- />
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/jobs/components/job/sidebar/sidebar.vue b/app/assets/javascripts/jobs/components/job/sidebar/sidebar.vue
index aac6a0ad6d3..69271cc9022 100644
--- a/app/assets/javascripts/jobs/components/job/sidebar/sidebar.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/sidebar.vue
@@ -2,14 +2,12 @@
import { GlButton, GlIcon } from '@gitlab/ui';
import { isEmpty } from 'lodash';
import { mapActions, mapGetters, mapState } from 'vuex';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { JOB_SIDEBAR_COPY, forwardDeploymentFailureModalId } from '~/jobs/constants';
+import ArtifactsBlock from './artifacts_block.vue';
import CommitBlock from './commit_block.vue';
import JobsContainer from './jobs_container.vue';
import JobRetryForwardDeploymentModal from './job_retry_forward_deployment_modal.vue';
import JobSidebarDetailsContainer from './sidebar_job_details_container.vue';
-import ArtifactsBlock from './artifacts_block.vue';
-import LegacySidebarHeader from './legacy_sidebar_header.vue';
import SidebarHeader from './sidebar_header.vue';
import StagesDropdown from './stages_dropdown.vue';
import TriggerBlock from './trigger_block.vue';
@@ -29,23 +27,16 @@ export default {
JobsContainer,
JobRetryForwardDeploymentModal,
JobSidebarDetailsContainer,
- LegacySidebarHeader,
SidebarHeader,
StagesDropdown,
TriggerBlock,
},
- mixins: [glFeatureFlagsMixin()],
props: {
artifactHelpUrl: {
type: String,
required: false,
default: '',
},
- erasePath: {
- type: String,
- required: false,
- default: null,
- },
},
computed: {
...mapGetters(['hasForwardDeploymentFailure']),
@@ -57,9 +48,6 @@ export default {
hasTriggers() {
return !isEmpty(this.job.trigger);
},
- isGraphQL() {
- return this.glFeatures?.graphqlJobApp;
- },
commit() {
return this.job?.pipeline?.commit || {};
},
@@ -89,8 +77,11 @@ export default {
<aside class="right-sidebar build-sidebar" data-offset-top="101" data-spy="affix">
<div class="sidebar-container">
<div class="blocks-container">
- <sidebar-header v-if="isGraphQL" :erase-path="erasePath" :job="job" />
- <legacy-sidebar-header v-else :erase-path="erasePath" :job="job" />
+ <sidebar-header
+ :rest-job="job"
+ :job-id="job.id"
+ @updateVariables="$emit('updateVariables')"
+ />
<div
v-if="job.terminal_path || job.new_issue_path"
class="gl-py-5"
diff --git a/app/assets/javascripts/jobs/components/job/sidebar/sidebar_header.vue b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_header.vue
index 523710598bf..40aec0b0536 100644
--- a/app/assets/javascripts/jobs/components/job/sidebar/sidebar_header.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_header.vue
@@ -1,13 +1,19 @@
<script>
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import { mapActions } from 'vuex';
+import { createAlert } from '~/flash';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
-import { JOB_SIDEBAR_COPY, forwardDeploymentFailureModalId } from '~/jobs/constants';
+import {
+ JOB_GRAPHQL_ERRORS,
+ GRAPHQL_ID_TYPES,
+ JOB_SIDEBAR_COPY,
+ forwardDeploymentFailureModalId,
+ PASSED_STATUS,
+} from '~/jobs/constants';
+import GetJob from '../graphql/queries/get_job.query.graphql';
import JobSidebarRetryButton from './job_sidebar_retry_button.vue';
-// This component is a port of ~/jobs/components/job/sidebar/legacy_sidebar_header.vue
-// It is meant to fetch the job information via GraphQL instead of REST API.
-
export default {
name: 'SidebarHeader',
i18n: {
@@ -22,21 +28,58 @@ export default {
JobSidebarRetryButton,
TooltipOnTruncate,
},
- props: {
+ inject: ['projectPath'],
+ apollo: {
job: {
+ query: GetJob,
+ variables() {
+ return {
+ fullPath: this.projectPath,
+ id: convertToGraphQLId(GRAPHQL_ID_TYPES.commitStatus, this.jobId),
+ };
+ },
+ update(data) {
+ const { name, manualJob } = data?.project?.job || {};
+ return {
+ name,
+ manualJob,
+ };
+ },
+ error() {
+ createAlert({ message: JOB_GRAPHQL_ERRORS.jobQueryErrorText });
+ },
+ },
+ },
+ props: {
+ jobId: {
+ type: Number,
+ required: true,
+ },
+ restJob: {
type: Object,
required: true,
default: () => ({}),
},
- erasePath: {
- type: String,
- required: false,
- default: null,
- },
+ },
+ data() {
+ return {
+ job: {},
+ };
},
computed: {
+ buttonTitle() {
+ return this.restJob.status?.text === PASSED_STATUS
+ ? this.$options.i18n.runAgainJobButtonLabel
+ : this.$options.i18n.retryJobLabel;
+ },
+ canShowJobRetryButton() {
+ return this.restJob.retry_path && !this.$apollo.queries.job.loading;
+ },
+ isManualJob() {
+ return this.job?.manualJob;
+ },
retryButtonCategory() {
- return this.job.status && this.job.recoverable ? 'primary' : 'secondary';
+ return this.restJob.status && this.restJob.recoverable ? 'primary' : 'secondary';
},
},
methods: {
@@ -48,17 +91,15 @@ export default {
<template>
<div class="gl-py-5 gl-display-flex gl-align-items-center">
<tooltip-on-truncate :title="job.name" truncate-target="child"
- ><h4 class="gl-my-0 gl-mr-3 gl-text-truncate">
- {{ job.name }}
- </h4>
+ ><h4 class="gl-my-0 gl-mr-3 gl-text-truncate" data-testid="job-name">{{ job.name }}</h4>
</tooltip-on-truncate>
<div class="gl-flex-grow-1 gl-flex-shrink-0 gl-text-right">
<gl-button
- v-if="erasePath"
+ v-if="restJob.erase_path"
v-gl-tooltip.left
:title="$options.i18n.eraseLogButtonLabel"
:aria-label="$options.i18n.eraseLogButtonLabel"
- :href="erasePath"
+ :href="restJob.erase_path"
:data-confirm="$options.i18n.eraseLogConfirmText"
class="gl-mr-2"
data-testid="job-log-erase-link"
@@ -67,23 +108,25 @@ export default {
icon="remove"
/>
<job-sidebar-retry-button
- v-if="job.retry_path"
+ v-if="canShowJobRetryButton"
v-gl-tooltip.left
- :title="$options.i18n.retryJobButtonLabel"
- :aria-label="$options.i18n.retryJobButtonLabel"
+ :title="buttonTitle"
+ :aria-label="buttonTitle"
+ :is-manual-job="isManualJob"
:category="retryButtonCategory"
- :href="job.retry_path"
+ :href="restJob.retry_path"
:modal-id="$options.forwardDeploymentFailureModalId"
variant="confirm"
data-qa-selector="retry_button"
data-testid="retry-button"
+ @updateVariablesClicked="$emit('updateVariables')"
/>
<gl-button
- v-if="job.cancel_path"
+ v-if="restJob.cancel_path"
v-gl-tooltip.left
:title="$options.i18n.cancelJobButtonLabel"
:aria-label="$options.i18n.cancelJobButtonLabel"
- :href="job.cancel_path"
+ :href="restJob.cancel_path"
variant="danger"
icon="cancel"
data-method="post"
diff --git a/app/assets/javascripts/jobs/components/job/sidebar/sidebar_job_details_container.vue b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_job_details_container.vue
index 3b1509e5be5..8300a22cb67 100644
--- a/app/assets/javascripts/jobs/components/job/sidebar/sidebar_job_details_container.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_job_details_container.vue
@@ -1,6 +1,7 @@
<script>
import { mapState } from 'vuex';
import { GlBadge } from '@gitlab/ui';
+import { helpPagePath } from '~/helpers/help_page_helper';
import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
import { __, sprintf } from '~/locale';
import timeagoMixin from '~/vue_shared/mixins/timeago';
@@ -79,7 +80,9 @@ export default {
TAGS: __('Tags:'),
TIMEOUT: __('Timeout'),
},
- RUNNER_HELP_URL: 'https://docs.gitlab.com/runner/register/index.html',
+ TIMEOUT_HELP_URL: helpPagePath('/ci/pipelines/settings.md', {
+ anchor: 'set-a-limit-for-how-long-jobs-can-run',
+ }),
};
</script>
@@ -96,7 +99,7 @@ export default {
<detail-row v-if="job.queued_duration" :value="queuedDuration" :title="$options.i18n.QUEUED" />
<detail-row
v-if="hasTimeout"
- :help-url="$options.RUNNER_HELP_URL"
+ :help-url="$options.TIMEOUT_HELP_URL"
:value="timeout"
data-testid="job-timeout"
:title="$options.i18n.TIMEOUT"
diff --git a/app/assets/javascripts/jobs/components/job/sidebar/trigger_block.vue b/app/assets/javascripts/jobs/components/job/sidebar/trigger_block.vue
index 1afc1c9a595..c9172fe0322 100644
--- a/app/assets/javascripts/jobs/components/job/sidebar/trigger_block.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/trigger_block.vue
@@ -2,9 +2,7 @@
import { GlButton, GlTableLite } from '@gitlab/ui';
import { __ } from '~/locale';
-const DEFAULT_TD_CLASSES = 'gl-w-half gl-font-sm! gl-border-gray-200!';
-const DEFAULT_TH_CLASSES =
- 'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-200! gl-border-b-1!';
+const DEFAULT_TD_CLASSES = 'gl-font-sm!';
export default {
fields: [
@@ -13,14 +11,12 @@ export default {
label: __('Key'),
tdAttr: { 'data-testid': 'trigger-build-key' },
tdClass: DEFAULT_TD_CLASSES,
- thClass: DEFAULT_TH_CLASSES,
},
{
key: 'value',
label: __('Value'),
tdAttr: { 'data-testid': 'trigger-build-value' },
tdClass: DEFAULT_TD_CLASSES,
- thClass: DEFAULT_TH_CLASSES,
},
],
components: {
diff --git a/app/assets/javascripts/jobs/constants.js b/app/assets/javascripts/jobs/constants.js
index e9475994e8b..405aea11181 100644
--- a/app/assets/javascripts/jobs/constants.js
+++ b/app/assets/javascripts/jobs/constants.js
@@ -5,6 +5,11 @@ const moreInfo = __('More information');
export const forwardDeploymentFailureModalId = 'forward-deployment-failure';
+export const GRAPHQL_ID_TYPES = {
+ commitStatus: 'CommitStatus',
+ ciBuild: 'Ci::Build',
+};
+
export const JOB_SIDEBAR_COPY = {
cancel,
cancelJobButtonLabel: s__('Job|Cancel'),
@@ -12,10 +17,15 @@ export const JOB_SIDEBAR_COPY = {
eraseLogButtonLabel: s__('Job|Erase job log and artifacts'),
eraseLogConfirmText: s__('Job|Are you sure you want to erase this job log and artifacts?'),
newIssue: __('New issue'),
- retry: __('Retry'),
- retryJobButtonLabel: s__('Job|Retry'),
+ retryJobLabel: s__('Job|Retry'),
toggleSidebar: __('Toggle Sidebar'),
runAgainJobButtonLabel: s__('Job|Run again'),
+ updateVariables: s__('Job|Update CI/CD variables'),
+};
+
+export const JOB_GRAPHQL_ERRORS = {
+ retryMutationErrorText: __('There was an error running the job. Please try again.'),
+ jobQueryErrorText: __('There was an error fetching the job.'),
};
export const JOB_RETRY_FORWARD_DEPLOYMENT_MODAL = {
@@ -31,3 +41,4 @@ export const JOB_RETRY_FORWARD_DEPLOYMENT_MODAL = {
};
export const SUCCESS_STATUS = 'SUCCESS';
+export const PASSED_STATUS = 'passed';
diff --git a/app/assets/javascripts/jobs/index.js b/app/assets/javascripts/jobs/index.js
index 9dd47f4046c..44bb1ffb1bc 100644
--- a/app/assets/javascripts/jobs/index.js
+++ b/app/assets/javascripts/jobs/index.js
@@ -1,10 +1,17 @@
import { GlToast } from '@gitlab/ui';
import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
import JobApp from './components/job/job_app.vue';
import createStore from './store';
+Vue.use(VueApollo);
Vue.use(GlToast);
+const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient(),
+});
+
const initializeJobPage = (element) => {
const store = createStore();
@@ -26,11 +33,13 @@ const initializeJobPage = (element) => {
return new Vue({
el: element,
+ apolloProvider,
store,
components: {
JobApp,
},
provide: {
+ projectPath,
retryOutdatedJobDocsUrl,
},
render(createElement) {
diff --git a/app/assets/javascripts/labels/labels_select.js b/app/assets/javascripts/labels/labels_select.js
index 65dda804a20..515b0a79a03 100644
--- a/app/assets/javascripts/labels/labels_select.js
+++ b/app/assets/javascripts/labels/labels_select.js
@@ -4,7 +4,7 @@
import $ from 'jquery';
import { difference, isEqual, escape, sortBy, template, union } from 'lodash';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
-import IssuableBulkUpdateActions from '~/issuable/bulk_update_sidebar/issuable_bulk_update_actions';
+import IssuableBulkUpdateActions from '~/issuable/issuable_bulk_update_actions';
import { isScopedLabel } from '~/lib/utils/common_utils';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
diff --git a/app/assets/javascripts/language_switcher/components/app.vue b/app/assets/javascripts/language_switcher/components/app.vue
new file mode 100644
index 00000000000..71babe6c614
--- /dev/null
+++ b/app/assets/javascripts/language_switcher/components/app.vue
@@ -0,0 +1,49 @@
+<script>
+import { GlCollapsibleListbox } from '@gitlab/ui';
+import { setCookie } from '~/lib/utils/common_utils';
+import { PREFERRED_LANGUAGE_COOKIE_KEY } from '../constants';
+
+export default {
+ components: {
+ GlCollapsibleListbox,
+ },
+ inject: {
+ locales: {
+ default: [],
+ },
+ preferredLocale: {
+ default: {},
+ },
+ },
+ data() {
+ return {
+ selected: this.preferredLocale.value,
+ };
+ },
+ methods: {
+ onLanguageSelected(code) {
+ setCookie(PREFERRED_LANGUAGE_COOKIE_KEY, code);
+ window.location.reload();
+ },
+ },
+};
+</script>
+<template>
+ <gl-collapsible-listbox
+ v-model="selected"
+ :toggle-text="preferredLocale.text"
+ :items="locales"
+ category="tertiary"
+ right
+ icon="earth"
+ size="small"
+ toggle-class="py-0 gl-h-6"
+ @select="onLanguageSelected"
+ >
+ <template #list-item="{ item: locale }">
+ <span :data-testid="`language_switcher_lang_${locale.value}`">
+ {{ locale.text }}
+ </span>
+ </template>
+ </gl-collapsible-listbox>
+</template>
diff --git a/app/assets/javascripts/language_switcher/constants.js b/app/assets/javascripts/language_switcher/constants.js
new file mode 100644
index 00000000000..b5c0613ac01
--- /dev/null
+++ b/app/assets/javascripts/language_switcher/constants.js
@@ -0,0 +1 @@
+export const PREFERRED_LANGUAGE_COOKIE_KEY = 'preferred_language';
diff --git a/app/assets/javascripts/language_switcher/index.js b/app/assets/javascripts/language_switcher/index.js
new file mode 100644
index 00000000000..b224e2510bb
--- /dev/null
+++ b/app/assets/javascripts/language_switcher/index.js
@@ -0,0 +1,23 @@
+import Vue from 'vue';
+import { getCookie } from '~/lib/utils/common_utils';
+import LanguageSwitcher from './components/app.vue';
+import { PREFERRED_LANGUAGE_COOKIE_KEY } from './constants';
+
+export const initLanguageSwitcher = () => {
+ const el = document.querySelector('.js-language-switcher');
+ if (!el) return false;
+ const locales = JSON.parse(el.dataset.locales);
+ const preferredLangCode = getCookie(PREFERRED_LANGUAGE_COOKIE_KEY);
+ const preferredLocale = locales.find((locale) => locale.value === preferredLangCode);
+
+ return new Vue({
+ el,
+ provide: {
+ locales,
+ preferredLocale,
+ },
+ render(createElement) {
+ return createElement(LanguageSwitcher);
+ },
+ });
+};
diff --git a/app/assets/javascripts/lib/dompurify.js b/app/assets/javascripts/lib/dompurify.js
index 27760e483aa..5372f6555d2 100644
--- a/app/assets/javascripts/lib/dompurify.js
+++ b/app/assets/javascripts/lib/dompurify.js
@@ -18,7 +18,7 @@ export const defaultConfig = {
'data-disable',
'data-turbo',
],
- FORBID_TAGS: ['style', 'mstyle'],
+ FORBID_TAGS: ['style', 'mstyle', 'form'],
ALLOW_UNKNOWN_PROTOCOLS: true,
};
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index beced4f9144..4ce63d518a6 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -4,9 +4,9 @@
import { GlBreakpointInstance as breakpointInstance } from '@gitlab/ui/dist/utils';
import $ from 'jquery';
-import { isFunction, defer } from 'lodash';
+import { isFunction, defer, escape } from 'lodash';
import Cookies from '~/lib/utils/cookies';
-import { SCOPED_LABEL_DELIMITER } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
+import { SCOPED_LABEL_DELIMITER } from '~/sidebar/components/labels/labels_select_widget/constants';
import { convertToCamelCase, convertToSnakeCase } from './text_utility';
import { isObject } from './type_utility';
import { getLocationHash } from './url_utility';
@@ -28,16 +28,12 @@ export const checkPageAndAction = (page, action) => {
export const isInIncidentPage = () => checkPageAndAction('incidents', 'show');
export const isInIssuePage = () => checkPageAndAction('issues', 'show');
export const isInDesignPage = () => checkPageAndAction('issues', 'designs');
-export const isInMRPage = () => checkPageAndAction('merge_requests', 'show');
+export const isInMRPage = () =>
+ checkPageAndAction('merge_requests', 'show') || checkPageAndAction('merge_requests', 'diffs');
export const isInEpicPage = () => checkPageAndAction('epics', 'show');
export const getDashPath = (path = window.location.pathname) => path.split('/-/')[1] || null;
-export const getCspNonceValue = () => {
- const metaTag = document.querySelector('meta[name=csp-nonce]');
- return metaTag && metaTag.content;
-};
-
export const rstrip = (val) => {
if (val) {
return val.replace(/\s+$/, '');
@@ -469,7 +465,7 @@ export const backOff = (fn, timeout = 60000) => {
export const spriteIcon = (icon, className = '') => {
const classAttribute = className.length > 0 ? `class="${className}"` : '';
- return `<svg ${classAttribute}><use xlink:href="${gon.sprite_icons}#${icon}" /></svg>`;
+ return `<svg ${classAttribute}><use xlink:href="${gon.sprite_icons}#${escape(icon)}" /></svg>`;
};
/**
@@ -715,3 +711,16 @@ export const getFirstPropertyValue = (data) => {
return data[key];
};
+
+// TODO: remove when FF `new_fonts` is removed https://gitlab.com/gitlab-org/gitlab/-/issues/379147
+/**
+ * This method checks the FF `new_fonts`
+ * as well as a query parameter `new_fonts`.
+ * If either of them is enabled, new fonts will be applied.
+ *
+ * @returns Boolean Whether to apply new fonts
+ */
+export const useNewFonts = () => {
+ const hasQueryParam = new URLSearchParams(window.location.search).has('new_fonts');
+ return window?.gon.features?.newFonts || hasQueryParam;
+};
diff --git a/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue b/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue
index 3788d8ab20c..ea91ccec546 100644
--- a/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue
+++ b/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue
@@ -1,10 +1,11 @@
<script>
-import { GlModal, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlModal } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { __ } from '~/locale';
export default {
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
components: {
GlModal,
diff --git a/app/assets/javascripts/lib/utils/constants.js b/app/assets/javascripts/lib/utils/constants.js
index 379c57f3945..2c8953237cf 100644
--- a/app/assets/javascripts/lib/utils/constants.js
+++ b/app/assets/javascripts/lib/utils/constants.js
@@ -1,6 +1,5 @@
export const BYTES_IN_KIB = 1024;
export const DEFAULT_DEBOUNCE_AND_THROTTLE_MS = 250;
-export const HIDDEN_CLASS = 'hidden';
export const THOUSAND = 1000;
export const TRUNCATE_WIDTH_DEFAULT_WIDTH = 80;
export const TRUNCATE_WIDTH_DEFAULT_FONT_SIZE = 12;
diff --git a/app/assets/javascripts/lib/utils/create_and_submit_form.js b/app/assets/javascripts/lib/utils/create_and_submit_form.js
new file mode 100644
index 00000000000..fce4f898f2f
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/create_and_submit_form.js
@@ -0,0 +1,26 @@
+import csrf from '~/lib/utils/csrf';
+
+export const createAndSubmitForm = ({ url, data }) => {
+ const form = document.createElement('form');
+
+ form.action = url;
+ // For now we only support 'post'.
+ // `form.method` doesn't support other methods so we would need to
+ // use a hidden `_method` input, which is out of scope for now.
+ form.method = 'post';
+ form.style.display = 'none';
+
+ Object.entries(data)
+ .concat([['authenticity_token', csrf.token]])
+ .forEach(([key, value]) => {
+ const input = document.createElement('input');
+ input.type = 'hidden';
+ input.name = key;
+ input.value = value;
+
+ form.appendChild(input);
+ });
+
+ document.body.appendChild(form);
+ form.submit();
+};
diff --git a/app/assets/javascripts/lib/utils/dom_utils.js b/app/assets/javascripts/lib/utils/dom_utils.js
index cafee641174..317c401e404 100644
--- a/app/assets/javascripts/lib/utils/dom_utils.js
+++ b/app/assets/javascripts/lib/utils/dom_utils.js
@@ -118,3 +118,24 @@ export const getContentWrapperHeight = (contentWrapperClass) => {
const wrapperEl = document.querySelector(contentWrapperClass);
return wrapperEl ? `${wrapperEl.offsetTop}px` : '';
};
+
+/**
+ * Replaces comment nodes in a DOM tree with a different element
+ * containing the text of the comment.
+ *
+ * @param {*} el
+ * @param {*} tagName
+ */
+export const replaceCommentsWith = (el, tagName) => {
+ const iterator = document.createNodeIterator(el, NodeFilter.SHOW_COMMENT);
+ let commentNode = iterator.nextNode();
+
+ while (commentNode) {
+ const newNode = document.createElement(tagName);
+ newNode.textContent = commentNode.textContent;
+
+ commentNode.parentNode.replaceChild(newNode, commentNode);
+
+ commentNode = iterator.nextNode();
+ }
+};
diff --git a/app/assets/javascripts/lib/utils/http_status.js b/app/assets/javascripts/lib/utils/http_status.js
index c5190592bb6..ec0d8d433a5 100644
--- a/app/assets/javascripts/lib/utils/http_status.js
+++ b/app/assets/javascripts/lib/utils/http_status.js
@@ -1,45 +1,43 @@
-/**
- * exports HTTP status codes
- */
+export const HTTP_STATUS_ABORTED = 0;
+export const HTTP_STATUS_CREATED = 201;
+export const HTTP_STATUS_ACCEPTED = 202;
+export const HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203;
+export const HTTP_STATUS_NO_CONTENT = 204;
+export const HTTP_STATUS_RESET_CONTENT = 205;
+export const HTTP_STATUS_PARTIAL_CONTENT = 206;
+export const HTTP_STATUS_MULTI_STATUS = 207;
+export const HTTP_STATUS_ALREADY_REPORTED = 208;
+export const HTTP_STATUS_IM_USED = 226;
+export const HTTP_STATUS_METHOD_NOT_ALLOWED = 405;
+export const HTTP_STATUS_CONFLICT = 409;
+export const HTTP_STATUS_GONE = 410;
+export const HTTP_STATUS_PAYLOAD_TOO_LARGE = 413;
+export const HTTP_STATUS_UNPROCESSABLE_ENTITY = 422;
+export const HTTP_STATUS_TOO_MANY_REQUESTS = 429;
+// TODO move the rest of the status codes to primitive constants
+// https://docs.gitlab.com/ee/development/fe_guide/style/javascript.html#export-constants-as-primitives
const httpStatusCodes = {
- ABORTED: 0,
OK: 200,
- CREATED: 201,
- ACCEPTED: 202,
- NON_AUTHORITATIVE_INFORMATION: 203,
- NO_CONTENT: 204,
- RESET_CONTENT: 205,
- PARTIAL_CONTENT: 206,
- MULTI_STATUS: 207,
- ALREADY_REPORTED: 208,
- IM_USED: 226,
- MULTIPLE_CHOICES: 300,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
NOT_FOUND: 404,
- METHOD_NOT_ALLOWED: 405,
- CONFLICT: 409,
- GONE: 410,
- PAYLOAD_TOO_LARGE: 413,
- UNPROCESSABLE_ENTITY: 422,
- TOO_MANY_REQUESTS: 429,
INTERNAL_SERVER_ERROR: 500,
SERVICE_UNAVAILABLE: 503,
};
export const successCodes = [
httpStatusCodes.OK,
- httpStatusCodes.CREATED,
- httpStatusCodes.ACCEPTED,
- httpStatusCodes.NON_AUTHORITATIVE_INFORMATION,
- httpStatusCodes.NO_CONTENT,
- httpStatusCodes.RESET_CONTENT,
- httpStatusCodes.PARTIAL_CONTENT,
- httpStatusCodes.MULTI_STATUS,
- httpStatusCodes.ALREADY_REPORTED,
- httpStatusCodes.IM_USED,
+ HTTP_STATUS_CREATED,
+ HTTP_STATUS_ACCEPTED,
+ HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION,
+ HTTP_STATUS_NO_CONTENT,
+ HTTP_STATUS_RESET_CONTENT,
+ HTTP_STATUS_PARTIAL_CONTENT,
+ HTTP_STATUS_MULTI_STATUS,
+ HTTP_STATUS_ALREADY_REPORTED,
+ HTTP_STATUS_IM_USED,
];
export default httpStatusCodes;
diff --git a/app/assets/javascripts/lib/utils/poll.js b/app/assets/javascripts/lib/utils/poll.js
index 71782c9a4ce..73add1e37ee 100644
--- a/app/assets/javascripts/lib/utils/poll.js
+++ b/app/assets/javascripts/lib/utils/poll.js
@@ -1,5 +1,5 @@
import { normalizeHeaders } from './common_utils';
-import httpStatusCodes, { successCodes } from './http_status';
+import { HTTP_STATUS_ABORTED, successCodes } from './http_status';
/**
* Polling utility for handling realtime updates.
@@ -108,7 +108,7 @@ export default class Poll {
})
.catch((error) => {
notificationCallback(false);
- if (error.status === httpStatusCodes.ABORTED) {
+ if (error.status === HTTP_STATUS_ABORTED) {
return;
}
errorCallback(error);
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index b1a0baf8150..f33484f4192 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -86,7 +86,7 @@ export function cleanLeadingSeparator(path) {
return path.replace(PATH_SEPARATOR_LEADING_REGEX, '');
}
-function cleanEndingSeparator(path) {
+export function cleanEndingSeparator(path) {
return path.replace(PATH_SEPARATOR_ENDING_REGEX, '');
}
diff --git a/app/assets/javascripts/listbox/index.js b/app/assets/javascripts/listbox/index.js
index 7eacbf7fcdd..7e8fc4b637b 100644
--- a/app/assets/javascripts/listbox/index.js
+++ b/app/assets/javascripts/listbox/index.js
@@ -1,4 +1,4 @@
-import { GlListbox } from '@gitlab/ui';
+import { GlCollapsibleListbox } from '@gitlab/ui';
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
@@ -31,7 +31,7 @@ export function initListbox(el, { onChange } = {}) {
},
},
render(h) {
- return h(GlListbox, {
+ return h(GlCollapsibleListbox, {
props: {
items,
right,
diff --git a/app/assets/javascripts/listbox/redirect_behavior.js b/app/assets/javascripts/listbox/redirect_behavior.js
index 7e0ea2c4dfd..38d9d84f889 100644
--- a/app/assets/javascripts/listbox/redirect_behavior.js
+++ b/app/assets/javascripts/listbox/redirect_behavior.js
@@ -2,7 +2,7 @@ import { initListbox } from '~/listbox';
import { redirectTo } from '~/lib/utils/url_utility';
/**
- * Instantiates GlListbox components with redirect behavior for tags created
+ * Instantiates GlCollapsibleListbox components with redirect behavior for tags created
* with the `gl_redirect_listbox_tag` HAML helper.
*
* NOTE: Do not import this script explicitly. Using `gl_redirect_listbox_tag`
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 8e4ebd510aa..df3b55ed2ad 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -37,6 +37,7 @@ import initBroadcastNotifications from './broadcast_notification';
import { initTopNav } from './nav';
import { initCopyCodeButton } from './behaviors/copy_code';
import initHeaderSearch from './header_search/init';
+import initGitlabVersionCheck from './gitlab_version_check';
import 'ee_else_ce/main_ee';
import 'jh_else_ce/main_jh';
@@ -100,21 +101,7 @@ function deferredInitialisation() {
initDefaultTrackers();
initFeatureHighlight();
initCopyCodeButton();
-
- const helpToggle = document.querySelector('.header-help-dropdown-toggle');
- if (helpToggle) {
- helpToggle.addEventListener(
- 'click',
- () => {
- import(/* webpackChunkName: 'versionCheck' */ './gitlab_version_check')
- .then(({ default: initGitlabVersionCheck }) => {
- initGitlabVersionCheck();
- })
- .catch(() => {});
- },
- { once: true },
- );
- }
+ initGitlabVersionCheck();
addSelectOnFocusBehaviour('.js-select-on-focus');
diff --git a/app/assets/javascripts/members/components/avatars/user_avatar.vue b/app/assets/javascripts/members/components/avatars/user_avatar.vue
index ec59f0f681c..4260ee14a14 100644
--- a/app/assets/javascripts/members/components/avatars/user_avatar.vue
+++ b/app/assets/javascripts/members/components/avatars/user_avatar.vue
@@ -1,10 +1,6 @@
<script>
-import {
- GlAvatarLink,
- GlAvatarLabeled,
- GlBadge,
- GlSafeHtmlDirective as SafeHtml,
-} from '@gitlab/ui';
+import { GlAvatarLink, GlAvatarLabeled, GlBadge } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { generateBadges } from 'ee_else_ce/members/utils';
import { glEmojiTag } from '~/emoji';
import { __ } from '~/locale';
diff --git a/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue b/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue
index cb7b963b698..76b286f94ad 100644
--- a/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue
+++ b/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue
@@ -7,11 +7,11 @@ import {
redirectTo,
} from '~/lib/utils/url_utility';
import {
- SEARCH_TOKEN_TYPE,
SORT_QUERY_PARAM_NAME,
ACTIVE_TAB_QUERY_PARAM_NAME,
AVAILABLE_FILTERED_SEARCH_TOKENS,
} from 'ee_else_ce/members/constants';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
export default {
@@ -65,7 +65,7 @@ export default {
if (query[this.filteredSearchBar.searchParam]) {
tokens.push({
- type: SEARCH_TOKEN_TYPE,
+ type: FILTERED_SEARCH_TERM,
value: {
data: query[this.filteredSearchBar.searchParam],
},
@@ -83,7 +83,7 @@ export default {
return accumulator;
}
- if (type === SEARCH_TOKEN_TYPE) {
+ if (type === FILTERED_SEARCH_TERM) {
if (value.data !== '') {
const { searchParam } = this.filteredSearchBar;
const { [searchParam]: searchParamValue } = accumulator;
diff --git a/app/assets/javascripts/members/constants.js b/app/assets/javascripts/members/constants.js
index 3135ec602be..dab544c7cbc 100644
--- a/app/assets/javascripts/members/constants.js
+++ b/app/assets/javascripts/members/constants.js
@@ -1,7 +1,7 @@
import { GlFilteredSearchToken } from '@gitlab/ui';
import { __, s__ } from '~/locale';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
+import { OPERATORS_IS } from '~/vue_shared/components/filtered_search_bar/constants';
// Overridden in EE
export const EE_APP_OPTIONS = {};
@@ -117,7 +117,7 @@ export const FILTERED_SEARCH_TOKEN_TWO_FACTOR = {
title: s__('Members|2FA'),
token: GlFilteredSearchToken,
unique: true,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
options: [
{ value: 'enabled', title: s__('Members|Enabled') },
{ value: 'disabled', title: s__('Members|Disabled') },
@@ -131,7 +131,7 @@ export const FILTERED_SEARCH_TOKEN_WITH_INHERITED_PERMISSIONS = {
title: s__('Members|Membership'),
token: GlFilteredSearchToken,
unique: true,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
options: [
{ value: 'exclude', title: s__('Members|Direct') },
{ value: 'only', title: s__('Members|Inherited') },
@@ -187,8 +187,6 @@ export const LEAVE_MODAL_ID = 'member-leave-modal';
export const REMOVE_GROUP_LINK_MODAL_ID = 'remove-group-link-modal-id';
-export const SEARCH_TOKEN_TYPE = 'filtered-search-term';
-
export const SORT_QUERY_PARAM_NAME = 'sort';
export const ACTIVE_TAB_QUERY_PARAM_NAME = 'tab';
diff --git a/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.vue b/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.vue
index 87eeb272659..6c431dc8af3 100644
--- a/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.vue
+++ b/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.vue
@@ -1,6 +1,6 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import { mapActions } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import syntaxHighlight from '~/syntax_highlight';
import { SYNTAX_HIGHLIGHT_CLASS } from '../constants';
import utilsMixin from '../mixins/line_conflict_utils';
diff --git a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.vue b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.vue
index 2c59e7bfa2f..f8a097a3a0f 100644
--- a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.vue
+++ b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.vue
@@ -1,6 +1,6 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import { mapActions } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import syntaxHighlight from '~/syntax_highlight';
import { SYNTAX_HIGHLIGHT_CLASS } from '../constants';
import utilsMixin from '../mixins/line_conflict_utils';
diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js
index 57b5e9809d2..80eb94a5364 100644
--- a/app/assets/javascripts/merge_request.js
+++ b/app/assets/javascripts/merge_request.js
@@ -94,7 +94,11 @@ MergeRequest.prototype.initMRBtnListeners = function () {
.put(draftToggle.href, null, { params: { format: 'json' } })
.then(({ data }) => {
draftToggle.removeAttribute('disabled');
- eventHub.$emit('MRWidgetUpdateRequested');
+
+ if (!window.gon?.features?.realtimeMrStatusChange) {
+ eventHub.$emit('MRWidgetUpdateRequested');
+ }
+
MergeRequest.toggleDraftStatus(data.title, wipEvent === 'ready');
})
.catch(() => {
@@ -173,7 +177,7 @@ MergeRequest.toggleDraftStatus = function (title, isReady) {
);
draftToggle.setAttribute('href', url);
- draftToggle.querySelector('.gl-new-dropdown-item-text-wrapper').textContent = isReady
+ draftToggle.querySelector('.gl-dropdown-item-text-wrapper').textContent = isReady
? __('Mark as draft')
: __('Mark as ready');
});
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 0ddf5def8ee..5a1410ceeba 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -5,6 +5,7 @@ import { createAlert } from '~/flash';
import { getCookie, isMetaClick, parseBoolean, scrollToElement } from '~/lib/utils/common_utils';
import { parseUrlPathname } from '~/lib/utils/url_utility';
import createEventHub from '~/helpers/event_hub_factory';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import BlobForkSuggestion from './blob/blob_fork_suggestion';
import Diff from './diff';
import { initDiffStatsDropdown } from './init_diff_stats_dropdown';
@@ -161,6 +162,23 @@ function toggleLoader(state) {
$('.mr-loading-status .loading').toggleClass('hide', !state);
}
+function getActionFromHref(href) {
+ let action = new URL(href).pathname.match(/\/(commits|diffs|pipelines).*$/);
+
+ if (action) {
+ action = action[0].replace(/(^\/|\.html)/g, '');
+ } else {
+ action = 'show';
+ }
+
+ return action;
+}
+
+const pageBundles = {
+ show: () => import(/* webpackPrefetch: true */ '~/mr_notes/init_notes'),
+ diffs: () => import(/* webpackPrefetch: true */ '~/diffs'),
+};
+
export default class MergeRequestTabs {
constructor({ action, setUrl, stubLocation } = {}) {
this.mergeRequestTabs = document.querySelector('.merge-request-tabs-container');
@@ -186,10 +204,10 @@ export default class MergeRequestTabs {
this.currentTab = null;
this.diffsLoaded = false;
- this.pipelinesLoaded = false;
this.commitsLoaded = false;
this.fixedLayoutPref = null;
this.eventHub = createEventHub();
+ this.loadedPages = { [action]: true };
this.setUrl = setUrl !== undefined ? setUrl : true;
this.setCurrentAction = this.setCurrentAction.bind(this);
@@ -206,12 +224,11 @@ export default class MergeRequestTabs {
bindEvents() {
$('.merge-request-tabs a[data-toggle="tabvue"]').on('click', this.clickTab);
- window.addEventListener('popstate', (event) => {
- if (event.state && event.state.action) {
- this.tabShown(event.state.action, event.target.location);
- this.currentAction = event.state.action;
- this.eventHub.$emit('MergeRequestTabChange', this.getCurrentAction());
- }
+ window.addEventListener('popstate', () => {
+ const action = getActionFromHref(location.href);
+
+ this.tabShown(action, location.href);
+ this.eventHub.$emit('MergeRequestTabChange', action);
});
}
@@ -252,17 +269,18 @@ export default class MergeRequestTabs {
} else if (action) {
const href = e.currentTarget.getAttribute('href');
this.tabShown(action, href);
-
- if (this.setUrl) {
- this.setCurrentAction(action);
- }
}
}
}
tabShown(action, href, shouldScroll = true) {
+ toggleLoader(false);
+
if (action !== this.currentTab && this.mergeRequestTabs) {
this.currentTab = action;
+ if (this.setUrl) {
+ this.setCurrentAction(action);
+ }
if (this.mergeRequestTabPanesAll) {
this.mergeRequestTabPanesAll.forEach((el) => {
@@ -282,6 +300,20 @@ export default class MergeRequestTabs {
const tab = this.mergeRequestTabs.querySelector(`.${action}-tab`);
if (tab) tab.classList.add('active');
+ if (!this.loadedPages[action] && action in pageBundles) {
+ toggleLoader(true);
+ pageBundles[action]()
+ .then(({ default: init }) => {
+ toggleLoader(false);
+ init();
+ this.loadedPages[action] = true;
+ })
+ .catch(() => {
+ toggleLoader(false);
+ createAlert({ message: __('MergeRequest|Failed to load the page') });
+ });
+ }
+
if (window.gon?.features?.movedMrSidebar) {
this.expandSidebar?.forEach((el) =>
el.classList.toggle('gl-display-none!', action !== 'show'),
@@ -334,7 +366,7 @@ export default class MergeRequestTabs {
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
}
- $('.detail-page-description').renderGFM();
+ renderGFM(document.querySelector('.detail-page-description'));
if (shouldScroll) this.recallScroll(action);
} else if (action === this.currentAction) {
@@ -398,7 +430,7 @@ export default class MergeRequestTabs {
// Ensure parameters and hash come along for the ride
newState += location.search + location.hash;
- if (window.history.state && window.history.state.url && window.location.pathname !== newState) {
+ if (window.location.pathname !== newState) {
window.history.pushState(
{
url: newState,
@@ -477,8 +509,6 @@ export default class MergeRequestTabs {
return;
}
- toggleLoader(true);
-
loadDiffs({
// We extract pathname for the current Changes tab anchor href
// some pages like MergeRequestsController#new has query parameters on that anchor
@@ -496,9 +526,6 @@ export default class MergeRequestTabs {
createAlert({
message: __('An error occurred while fetching this tab.'),
});
- })
- .finally(() => {
- toggleLoader(false);
});
}
diff --git a/app/assets/javascripts/merge_requests/components/sticky_header.vue b/app/assets/javascripts/merge_requests/components/sticky_header.vue
index b7629ba001f..4a675cf7563 100644
--- a/app/assets/javascripts/merge_requests/components/sticky_header.vue
+++ b/app/assets/javascripts/merge_requests/components/sticky_header.vue
@@ -1,12 +1,7 @@
<script>
-import {
- GlIntersectionObserver,
- GlLink,
- GlSprintf,
- GlBadge,
- GlSafeHtmlDirective,
-} from '@gitlab/ui';
+import { GlIntersectionObserver, GlLink, GlSprintf, GlBadge } from '@gitlab/ui';
import { mapGetters, mapState } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { TYPE_MERGE_REQUEST } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@@ -28,7 +23,7 @@ export default {
ClipboardButton,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [glFeatureFlagsMixin()],
inject: {
diff --git a/app/assets/javascripts/merge_requests/components/target_project_dropdown.vue b/app/assets/javascripts/merge_requests/components/target_project_dropdown.vue
new file mode 100644
index 00000000000..cd2e25793f4
--- /dev/null
+++ b/app/assets/javascripts/merge_requests/components/target_project_dropdown.vue
@@ -0,0 +1,87 @@
+<script>
+import { GlListbox } from '@gitlab/ui';
+import { debounce } from 'lodash';
+import { createAlert } from '~/flash';
+import { __ } from '~/locale';
+import axios from '~/lib/utils/axios_utils';
+
+export default {
+ components: {
+ GlListbox,
+ },
+ inject: {
+ targetProjectsPath: {
+ type: String,
+ required: true,
+ },
+ currentProject: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ currentProject: this.currentProject,
+ selected: this.currentProject.value,
+ isLoading: false,
+ projects: [],
+ };
+ },
+ methods: {
+ async fetchProjects(search = '') {
+ this.isLoading = true;
+
+ try {
+ const { data } = await axios.get(this.targetProjectsPath, {
+ params: { search },
+ });
+
+ this.projects = data.map((p) => ({
+ value: `${p.id}`,
+ text: p.full_path.replace(/^\//, ''),
+ refsUrl: p.refs_url,
+ }));
+ this.isLoading = false;
+ } catch {
+ createAlert({
+ message: __('Error fetching target projects. Please try again.'),
+ primaryButton: { text: __('Try again'), clickHandler: () => this.fetchProjects(search) },
+ });
+ }
+ },
+ searchProjects: debounce(function searchProjects(search) {
+ this.fetchProjects(search);
+ }, 500),
+ selectProject(projectId) {
+ this.currentProject = this.projects.find((p) => p.value === projectId);
+
+ this.$emit('project-selected', this.currentProject.refsUrl);
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <input
+ id="merge_request_target_project_id"
+ type="hidden"
+ :value="currentProject.value"
+ name="merge_request[target_project_id]"
+ data-testid="target-project-input"
+ />
+ <gl-listbox
+ v-model="selected"
+ :items="projects"
+ :toggle-text="currentProject.text"
+ :header-text="__('Select target project')"
+ :searching="isLoading"
+ searchable
+ class="gl-w-full dropdown-target-project"
+ toggle-class="gl-align-items-flex-start! gl-justify-content-start! mr-compare-dropdown js-target-project"
+ @shown="fetchProjects"
+ @search="searchProjects"
+ @select="selectProject"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/ml/experiment_tracking/components/experiment.vue b/app/assets/javascripts/ml/experiment_tracking/components/experiment.vue
deleted file mode 100644
index 73cdfbc44b0..00000000000
--- a/app/assets/javascripts/ml/experiment_tracking/components/experiment.vue
+++ /dev/null
@@ -1,36 +0,0 @@
-<script>
-import { GlTable } from '@gitlab/ui';
-import IncubationAlert from './incubation_alert.vue';
-
-export default {
- name: 'ShowMlExperiment',
- components: {
- GlTable,
- IncubationAlert,
- },
- inject: ['candidates', 'metricNames', 'paramNames'],
- computed: {
- fields() {
- return [...this.paramNames, ...this.metricNames];
- },
- },
-};
-</script>
-
-<template>
- <div>
- <incubation-alert />
-
- <h3>
- {{ __('Experiment Candidates') }}
- </h3>
-
- <gl-table
- :fields="fields"
- :items="candidates"
- :empty-text="__('This Experiment has no logged Candidates')"
- show-empty
- class="gl-mt-0!"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/ml/experiment_tracking/components/incubation_alert.vue b/app/assets/javascripts/ml/experiment_tracking/components/incubation_alert.vue
index 51c1e935677..42f6394ed68 100644
--- a/app/assets/javascripts/ml/experiment_tracking/components/incubation_alert.vue
+++ b/app/assets/javascripts/ml/experiment_tracking/components/incubation_alert.vue
@@ -8,8 +8,8 @@ export default {
contentLabel: __(
'GitLab incubates features to explore new use cases. These features are updated regularly, and support is limited',
),
- learnMoreLabel: __('Learn More'),
- feedbackLabel: __('Feedback and Updates'),
+ learnMoreLabel: __('Learn more'),
+ feedbackLabel: __('Feedback'),
},
name: 'MlopsIncubationAlert',
components: { GlAlert, GlLink },
@@ -37,7 +37,7 @@ export default {
:title="$options.i18n.titleLabel"
variant="warning"
:primary-button-text="$options.i18n.feedbackLabel"
- primary-button-link="https://gitlab.com/groups/gitlab-org/-/epics/8560"
+ primary-button-link="https://gitlab.com/gitlab-org/gitlab/-/issues/381660"
@dismiss="dismissAlert"
>
{{ $options.i18n.contentLabel }}
diff --git a/app/assets/javascripts/ml/experiment_tracking/components/ml_candidate.vue b/app/assets/javascripts/ml/experiment_tracking/components/ml_candidate.vue
new file mode 100644
index 00000000000..5f54f24e24c
--- /dev/null
+++ b/app/assets/javascripts/ml/experiment_tracking/components/ml_candidate.vue
@@ -0,0 +1,94 @@
+<script>
+import { GlLink } from '@gitlab/ui';
+import { __ } from '~/locale';
+import IncubationAlert from './incubation_alert.vue';
+
+export default {
+ name: 'MlCandidate',
+ components: {
+ IncubationAlert,
+ GlLink,
+ },
+ inject: ['candidate'],
+ i18n: {
+ titleLabel: __('Model candidate details'),
+ infoLabel: __('Info'),
+ idLabel: __('ID'),
+ statusLabel: __('Status'),
+ experimentLabel: __('Experiment'),
+ artifactsLabel: __('Artifacts'),
+ parametersLabel: __('Parameters'),
+ metricsLabel: __('Metrics'),
+ },
+};
+</script>
+
+<template>
+ <div>
+ <incubation-alert />
+
+ <h3>
+ {{ $options.i18n.titleLabel }}
+ </h3>
+
+ <table class="candidate-details">
+ <tbody>
+ <tr class="divider"></tr>
+
+ <tr>
+ <td class="gl-text-secondary gl-font-weight-bold">{{ $options.i18n.infoLabel }}</td>
+ <td class="gl-font-weight-bold">{{ $options.i18n.idLabel }}</td>
+ <td>{{ candidate.info.iid }}</td>
+ </tr>
+
+ <tr>
+ <td></td>
+ <td class="gl-font-weight-bold">{{ $options.i18n.statusLabel }}</td>
+ <td>{{ candidate.info.status }}</td>
+ </tr>
+
+ <tr>
+ <td></td>
+ <td class="gl-font-weight-bold">{{ $options.i18n.experimentLabel }}</td>
+ <td>
+ <gl-link :href="candidate.info.path_to_experiment">{{
+ candidate.info.experiment_name
+ }}</gl-link>
+ </td>
+ </tr>
+
+ <tr v-if="candidate.info.path_to_artifact">
+ <td></td>
+ <td class="gl-font-weight-bold">{{ $options.i18n.artifactsLabel }}</td>
+ <td>
+ <gl-link :href="candidate.info.path_to_artifact">{{
+ $options.i18n.artifactsLabel
+ }}</gl-link>
+ </td>
+ </tr>
+
+ <tr class="divider"></tr>
+
+ <tr v-for="(param, index) in candidate.params" :key="param.name">
+ <td v-if="index == 0" class="gl-text-secondary gl-font-weight-bold">
+ {{ $options.i18n.parametersLabel }}
+ </td>
+ <td v-else></td>
+ <td class="gl-font-weight-bold">{{ param.name }}</td>
+ <td>{{ param.value }}</td>
+ </tr>
+
+ <tr class="divider"></tr>
+
+ <tr v-for="(metric, index) in candidate.metrics" :key="metric.name">
+ <td v-if="index == 0" class="gl-text-secondary gl-font-weight-bold">
+ {{ $options.i18n.metricsLabel }}
+ </td>
+ <td v-else></td>
+ <td class="gl-font-weight-bold">{{ metric.name }}</td>
+ <td>{{ metric.value }}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ml/experiment_tracking/components/ml_experiment.vue b/app/assets/javascripts/ml/experiment_tracking/components/ml_experiment.vue
new file mode 100644
index 00000000000..f8e269d3b57
--- /dev/null
+++ b/app/assets/javascripts/ml/experiment_tracking/components/ml_experiment.vue
@@ -0,0 +1,59 @@
+<script>
+import { GlTable, GlLink } from '@gitlab/ui';
+import { __ } from '~/locale';
+import IncubationAlert from './incubation_alert.vue';
+
+export default {
+ name: 'MlExperiment',
+ components: {
+ GlTable,
+ GlLink,
+ IncubationAlert,
+ },
+ inject: ['candidates', 'metricNames', 'paramNames'],
+ computed: {
+ fields() {
+ return [
+ ...this.paramNames,
+ ...this.metricNames,
+ { key: 'details', label: '' },
+ { key: 'artifact', label: '' },
+ ];
+ },
+ },
+ i18n: {
+ titleLabel: __('Experiment candidates'),
+ emptyStateLabel: __('This experiment has no logged candidates'),
+ artifactsLabel: __('Artifacts'),
+ detailsLabel: __('Details'),
+ },
+};
+</script>
+
+<template>
+ <div>
+ <incubation-alert />
+
+ <h3>
+ {{ $options.i18n.titleLabel }}
+ </h3>
+
+ <gl-table
+ :fields="fields"
+ :items="candidates"
+ :empty-text="$options.i18n.emptyStateLabel"
+ show-empty
+ class="gl-mt-0!"
+ >
+ <template #cell(artifact)="data">
+ <gl-link v-if="data.value" :href="data.value" target="_blank">{{
+ $options.i18n.artifactsLabel
+ }}</gl-link>
+ </template>
+
+ <template #cell(details)="data">
+ <gl-link :href="data.value">{{ $options.i18n.detailsLabel }}</gl-link>
+ </template>
+ </gl-table>
+ </div>
+</template>
diff --git a/app/assets/javascripts/monitoring/components/charts/empty_chart.vue b/app/assets/javascripts/monitoring/components/charts/empty_chart.vue
index ae079da0b0b..da4c92df711 100644
--- a/app/assets/javascripts/monitoring/components/charts/empty_chart.vue
+++ b/app/assets/javascripts/monitoring/components/charts/empty_chart.vue
@@ -1,11 +1,11 @@
<script>
import chartEmptyStateIllustration from '@gitlab/svgs/dist/illustrations/chart-empty-state.svg';
-import { GlSafeHtmlDirective } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { chartHeight } from '../../constants';
export default {
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
data() {
return {
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index b6ad2d21757..2c185794d17 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -391,11 +391,7 @@ export default {
};
</script>
<template>
- <div
- class="prometheus-graphs"
- data-qa-selector="prometheus_graphs_content"
- data-testid="prometheus-graphs"
- >
+ <div class="prometheus-graphs" data-testid="prometheus-graphs">
<div>
<gl-alert
v-if="!isDeprecationNoticeDismissed"
diff --git a/app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue b/app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue
index 7f8fb3c223d..d67154b7697 100644
--- a/app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue
@@ -146,7 +146,6 @@ export default {
<gl-dropdown
v-gl-tooltip
data-testid="actions-menu"
- data-qa-selector="actions_menu_dropdown"
right
no-caret
toggle-class="gl-px-3!"
@@ -223,7 +222,6 @@ export default {
<gl-dropdown-item
v-if="isMenuItemEnabled.editDashboard"
:href="selectedDashboard ? selectedDashboard.project_blob_path : null"
- data-qa-selector="edit_dashboard_button_enabled"
data-testid="edit-dashboard-item-enabled"
>
{{ $options.i18n.editDashboard }}
diff --git a/app/assets/javascripts/monitoring/components/dashboard_header.vue b/app/assets/javascripts/monitoring/components/dashboard_header.vue
index 90d2498ac19..7bb0d3874d1 100644
--- a/app/assets/javascripts/monitoring/components/dashboard_header.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard_header.vue
@@ -173,7 +173,6 @@ export default {
<div class="gl-mb-3 gl-mr-3 gl-display-flex gl-sm-display-block">
<dashboards-dropdown
id="monitor-dashboards-dropdown"
- data-qa-selector="dashboards_filter_dropdown"
class="flex-grow-1"
toggle-class="dropdown-menu-toggle"
:default-branch="defaultBranch"
@@ -188,7 +187,6 @@ export default {
id="monitor-environments-dropdown"
ref="monitorEnvironmentsDropdown"
class="flex-grow-1"
- data-qa-selector="environments_dropdown"
data-testid="environments-dropdown"
toggle-class="dropdown-menu-toggle"
menu-class="monitor-environment-dropdown-menu"
@@ -225,7 +223,6 @@ export default {
<date-time-picker
ref="dateTimePicker"
class="flex-grow-1 show-last-dropdown"
- data-qa-selector="range_picker_dropdown"
:value="selectedTimeRange"
:options="$options.timeRanges"
:utc="displayUtc"
diff --git a/app/assets/javascripts/monitoring/components/dashboard_panel.vue b/app/assets/javascripts/monitoring/components/dashboard_panel.vue
index 7e7dcef7639..9ad6da35d6b 100644
--- a/app/assets/javascripts/monitoring/components/dashboard_panel.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard_panel.vue
@@ -292,11 +292,7 @@ export default {
<div v-if="graphDataIsLoading" class="mx-1 mt-1">
<gl-loading-icon size="sm" />
</div>
- <div
- v-if="isContextualMenuShown"
- ref="contextualMenu"
- data-qa-selector="prometheus_graph_widgets"
- >
+ <div v-if="isContextualMenuShown" ref="contextualMenu">
<div data-testid="dropdown-wrapper" class="d-flex align-items-center">
<!--
This component should be replaced with a variant developed
@@ -310,7 +306,6 @@ export default {
:text-sr-only="true"
toggle-class="gl-px-3!"
no-caret
- data-qa-selector="prometheus_widgets_dropdown"
right
:title="__('More actions')"
>
@@ -339,7 +334,6 @@ export default {
ref="copyChartLink"
v-track-event="generateLinkToChartOptions(clipboardText)"
:data-clipboard-text="clipboardText"
- data-qa-selector="generate_chart_link_menu_item"
@click="showToast(clipboardText)"
>
{{ __('Copy link to chart') }}
diff --git a/app/assets/javascripts/monitoring/components/dashboard_panel_builder.vue b/app/assets/javascripts/monitoring/components/dashboard_panel_builder.vue
index 8efea2bfc3e..e8a9c24f5c2 100644
--- a/app/assets/javascripts/monitoring/components/dashboard_panel_builder.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard_panel_builder.vue
@@ -100,7 +100,7 @@ export default {
<gl-form-textarea
id="panel-yml-input"
v-model="yml"
- class="gl-h-200! gl-font-monospace! gl-font-size-monospace!"
+ class="gl-h-200! gl-font-monospace!"
/>
</gl-form-group>
<div class="gl-text-right">
diff --git a/app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue b/app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue
index a63008aa382..9ad14b3d52e 100644
--- a/app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue
+++ b/app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue
@@ -104,13 +104,7 @@ export default {
label-size="sm"
label-for="fileName"
>
- <gl-form-input
- id="fileName"
- ref="fileName"
- v-model="form.fileName"
- data-qa-selector="duplicate_dashboard_filename_field"
- :required="true"
- />
+ <gl-form-input id="fileName" ref="fileName" v-model="form.fileName" :required="true" />
</gl-form-group>
<gl-form-group :label="__('Branch')" label-size="sm" label-for="branch">
<gl-form-radio-group
diff --git a/app/assets/javascripts/monitoring/components/group_empty_state.vue b/app/assets/javascripts/monitoring/components/group_empty_state.vue
index 0365fc66331..a67770b93be 100644
--- a/app/assets/javascripts/monitoring/components/group_empty_state.vue
+++ b/app/assets/javascripts/monitoring/components/group_empty_state.vue
@@ -1,5 +1,6 @@
<script>
-import { GlEmptyState, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlEmptyState } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { __, sprintf } from '~/locale';
import { metricStates } from '../constants';
diff --git a/app/assets/javascripts/monitoring/components/refresh_button.vue b/app/assets/javascripts/monitoring/components/refresh_button.vue
index 544fe10f26e..55c602db33d 100644
--- a/app/assets/javascripts/monitoring/components/refresh_button.vue
+++ b/app/assets/javascripts/monitoring/components/refresh_button.vue
@@ -11,8 +11,6 @@ import Visibility from 'visibilityjs';
import { mapActions } from 'vuex';
import { n__, __, s__ } from '~/locale';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-
const makeInterval = (length = 0, unit = 's') => {
const shortLabel = `${length}${unit}`;
switch (unit) {
@@ -58,7 +56,6 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
- mixins: [glFeatureFlagsMixin()],
data() {
return {
refreshInterval: null,
@@ -66,12 +63,6 @@ export default {
};
},
computed: {
- disableMetricDashboardRefreshRate() {
- // Can refresh rates impact performance?
- // Add "negative" feature flag called `disable_metric_dashboard_refresh_rate`
- // See more at: https://gitlab.com/gitlab-org/gitlab/-/issues/229831
- return this.glFeatures.disableMetricDashboardRefreshRate;
- },
dropdownText() {
return this.refreshInterval?.shortLabel ?? __('Off');
},
@@ -156,12 +147,7 @@ export default {
icon="retry"
@click="refresh"
/>
- <gl-dropdown
- v-if="!disableMetricDashboardRefreshRate"
- v-gl-tooltip
- :title="s__('Metrics|Set refresh rate')"
- :text="dropdownText"
- >
+ <gl-dropdown v-gl-tooltip :title="s__('Metrics|Set refresh rate')" :text="dropdownText">
<gl-dropdown-item
is-check-item
:is-checked="refreshInterval === null"
diff --git a/app/assets/javascripts/monitoring/components/variables_section.vue b/app/assets/javascripts/monitoring/components/variables_section.vue
index 493d37ce263..971f188e9f3 100644
--- a/app/assets/javascripts/monitoring/components/variables_section.vue
+++ b/app/assets/javascripts/monitoring/components/variables_section.vue
@@ -37,11 +37,7 @@ export default {
};
</script>
<template>
- <div
- ref="variablesSection"
- class="d-sm-flex flex-sm-wrap pt-2 pr-1 pb-0 pl-2 variables-section"
- data-qa-selector="variables_content"
- >
+ <div ref="variablesSection" class="d-sm-flex flex-sm-wrap pt-2 pr-1 pb-0 pl-2 variables-section">
<div v-for="variable in variables" :key="variable.name" class="mb-1 pr-2 d-flex d-sm-block">
<component
:is="variableField(variable.type)"
@@ -50,7 +46,6 @@ export default {
:value="variable.value"
:name="variable.name"
:options="variable.options"
- data-qa-selector="variable_item"
@input="refreshDashboard(variable, $event)"
/>
</div>
diff --git a/app/assets/javascripts/monitoring/csv_export.js b/app/assets/javascripts/monitoring/csv_export.js
index eaeed4a54d4..7e15b659767 100644
--- a/app/assets/javascripts/monitoring/csv_export.js
+++ b/app/assets/javascripts/monitoring/csv_export.js
@@ -110,7 +110,7 @@ const csvData = (metricHeaders, metricValues) => {
// "If double-quotes are used to enclose fields, then a double-quote
// appearing inside a field must be escaped by preceding it with
// another double quote."
- // https://tools.ietf.org/html/rfc4180#page-2
+ // https://www.rfc-editor.org/rfc/rfc4180#page-2
const headers = metricHeaders.map((header) => `"${header.replace(/"/g, '""')}"`);
return {
diff --git a/app/assets/javascripts/monitoring/requests/index.js b/app/assets/javascripts/monitoring/requests/index.js
index 26fedb9c81c..8b65eec051f 100644
--- a/app/assets/javascripts/monitoring/requests/index.js
+++ b/app/assets/javascripts/monitoring/requests/index.js
@@ -1,13 +1,16 @@
import axios from '~/lib/utils/axios_utils';
import { backOff } from '~/lib/utils/common_utils';
-import statusCodes from '~/lib/utils/http_status';
+import statusCodes, {
+ HTTP_STATUS_NO_CONTENT,
+ HTTP_STATUS_UNPROCESSABLE_ENTITY,
+} from '~/lib/utils/http_status';
import { PROMETHEUS_TIMEOUT } from '../constants';
const cancellableBackOffRequest = (makeRequestCallback) =>
backOff((next, stop) => {
makeRequestCallback()
.then((resp) => {
- if (resp.status === statusCodes.NO_CONTENT) {
+ if (resp.status === HTTP_STATUS_NO_CONTENT) {
next();
} else {
stop(resp);
@@ -34,7 +37,7 @@ export const getPrometheusQueryData = (prometheusEndpoint, params, opts) =>
const { response = {} } = error;
if (
response.status === statusCodes.BAD_REQUEST ||
- response.status === statusCodes.UNPROCESSABLE_ENTITY ||
+ response.status === HTTP_STATUS_UNPROCESSABLE_ENTITY ||
response.status === statusCodes.SERVICE_UNAVAILABLE
) {
const { data } = response;
diff --git a/app/assets/javascripts/monitoring/utils.js b/app/assets/javascripts/monitoring/utils.js
index fd8749625da..0d849e1a2d8 100644
--- a/app/assets/javascripts/monitoring/utils.js
+++ b/app/assets/javascripts/monitoring/utils.js
@@ -39,7 +39,6 @@ export const stateAndPropsFromDataset = (dataset = {}) => {
// HTML attributes are always strings, parse other types.
dataProps.hasMetrics = parseBoolean(dataProps.hasMetrics);
dataProps.customMetricsAvailable = parseBoolean(dataProps.customMetricsAvailable);
- dataProps.prometheusAlertsAvailable = parseBoolean(dataProps.prometheusAlertsAvailable);
return {
initState: {
diff --git a/app/assets/javascripts/mr_notes/discussion_counter.js b/app/assets/javascripts/mr_notes/discussion_counter.js
new file mode 100644
index 00000000000..0bb63a7c0f9
--- /dev/null
+++ b/app/assets/javascripts/mr_notes/discussion_counter.js
@@ -0,0 +1,28 @@
+import Vue from 'vue';
+import DiscussionCounter from '~/notes/components/discussion_counter.vue';
+import store from '~/mr_notes/stores';
+
+export function initDiscussionCounter() {
+ const el = document.getElementById('js-vue-discussion-counter');
+
+ if (el) {
+ const { blocksMerge } = el.dataset;
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el,
+ name: 'DiscussionCounter',
+ components: {
+ DiscussionCounter,
+ },
+ store,
+ render(createElement) {
+ return createElement('discussion-counter', {
+ props: {
+ blocksMerge: blocksMerge === 'true',
+ },
+ });
+ },
+ });
+ }
+}
diff --git a/app/assets/javascripts/mr_notes/index.js b/app/assets/javascripts/mr_notes/index.js
index c32a1f4c2ac..a202923bd21 100644
--- a/app/assets/javascripts/mr_notes/index.js
+++ b/app/assets/javascripts/mr_notes/index.js
@@ -1,12 +1,8 @@
-import Vue from 'vue';
-import store from '~/mr_notes/stores';
import initCherryPickCommitModal from '~/projects/commit/init_cherry_pick_commit_modal';
import initRevertCommitModal from '~/projects/commit/init_revert_commit_modal';
-import initDiffsApp from '../diffs';
-import { resetServiceWorkersPublicPath } from '../lib/utils/webpack';
+import { initMrStateLazyLoad } from '~/mr_notes/init';
import MergeRequest from '../merge_request';
-import DiscussionCounter from '../notes/components/discussion_counter.vue';
-import initNotesApp from './init_notes';
+import { resetServiceWorkersPublicPath } from '../lib/utils/webpack';
export default function initMrNotes() {
resetServiceWorkersPublicPath();
@@ -17,36 +13,10 @@ export default function initMrNotes() {
action: mrShowNode.dataset.mrAction,
});
- initDiffsApp(store);
- initNotesApp();
+ initMrStateLazyLoad();
document.addEventListener('merged:UpdateActions', () => {
initRevertCommitModal('i_code_review_post_merge_submit_revert_modal');
initCherryPickCommitModal('i_code_review_post_merge_submit_cherry_pick_modal');
});
-
- requestIdleCallback(() => {
- const el = document.getElementById('js-vue-discussion-counter');
-
- if (el) {
- const { blocksMerge } = el.dataset;
-
- // eslint-disable-next-line no-new
- new Vue({
- el,
- name: 'DiscussionCounter',
- components: {
- DiscussionCounter,
- },
- store,
- render(createElement) {
- return createElement('discussion-counter', {
- props: {
- blocksMerge: blocksMerge === 'true',
- },
- });
- },
- });
- }
- });
}
diff --git a/app/assets/javascripts/mr_notes/init.js b/app/assets/javascripts/mr_notes/init.js
new file mode 100644
index 00000000000..aab3c41b4cf
--- /dev/null
+++ b/app/assets/javascripts/mr_notes/init.js
@@ -0,0 +1,52 @@
+import { parseBoolean } from '~/lib/utils/common_utils';
+import store from '~/mr_notes/stores';
+import { getLocationHash } from '~/lib/utils/url_utility';
+import eventHub from '~/notes/event_hub';
+import { initReviewBar } from '~/batch_comments';
+import { initDiscussionCounter } from '~/mr_notes/discussion_counter';
+import { initOverviewTabCounter } from '~/mr_notes/init_count';
+
+function setupMrNotesState(notesDataset) {
+ const noteableData = JSON.parse(notesDataset.noteableData);
+ noteableData.noteableType = notesDataset.noteableType;
+ noteableData.targetType = notesDataset.targetType;
+ noteableData.discussion_locked = parseBoolean(notesDataset.isLocked);
+ const notesData = JSON.parse(notesDataset.notesData);
+ const currentUserData = JSON.parse(notesDataset.currentUserData);
+ const endpoints = { metadata: notesDataset.endpointMetadata };
+
+ store.dispatch('setNotesData', notesData);
+ store.dispatch('setNoteableData', noteableData);
+ store.dispatch('setUserData', currentUserData);
+ store.dispatch('setTargetNoteHash', getLocationHash());
+ store.dispatch('setEndpoints', endpoints);
+ eventHub.$once('fetchNotesData', () => store.dispatch('fetchNotes'));
+}
+
+export function initMrStateLazyLoad() {
+ store.dispatch('setActiveTab', window.mrTabs.getCurrentAction());
+ window.mrTabs.eventHub.$on('MergeRequestTabChange', (value) =>
+ store.dispatch('setActiveTab', value),
+ );
+
+ const discussionsEl = document.getElementById('js-vue-mr-discussions');
+ const notesDataset = discussionsEl.dataset;
+ let stop = () => {};
+ stop = store.watch(
+ (state) => state.page.activeTab,
+ (activeTab) => {
+ // prevent loading MR state on commits and pipelines pages
+ // this is due to them having a shared controller with the Overview page
+ if (['diffs', 'show'].includes(activeTab)) {
+ setupMrNotesState(notesDataset);
+ requestIdleCallback(() => {
+ initReviewBar();
+ initOverviewTabCounter();
+ initDiscussionCounter();
+ });
+ stop();
+ }
+ },
+ { immediate: true },
+ );
+}
diff --git a/app/assets/javascripts/mr_notes/init_count.js b/app/assets/javascripts/mr_notes/init_count.js
new file mode 100644
index 00000000000..3e924ebd9d5
--- /dev/null
+++ b/app/assets/javascripts/mr_notes/init_count.js
@@ -0,0 +1,13 @@
+import store from '~/mr_notes/stores';
+
+export function initOverviewTabCounter() {
+ const discussionsCount = document.querySelector('.js-discussions-count');
+ store.watch(
+ (state, getters) => getters.discussionTabCounter,
+ (val) => {
+ if (typeof val !== 'undefined') {
+ discussionsCount.textContent = val;
+ }
+ },
+ );
+}
diff --git a/app/assets/javascripts/mr_notes/init_notes.js b/app/assets/javascripts/mr_notes/init_notes.js
index 3a67e7925c3..e10605609b0 100644
--- a/app/assets/javascripts/mr_notes/init_notes.js
+++ b/app/assets/javascripts/mr_notes/init_notes.js
@@ -1,8 +1,8 @@
-import $ from 'jquery';
import Vue from 'vue';
import { mapActions, mapState, mapGetters } from 'vuex';
import { parseBoolean } from '~/lib/utils/common_utils';
import store from '~/mr_notes/stores';
+import notesEventHub from '~/notes/event_hub';
import discussionNavigator from '../notes/components/discussion_navigator.vue';
import NotesApp from '../notes/components/notes_app.vue';
import { getNotesFilterData } from '../notes/utils/get_notes_filter_data';
@@ -36,13 +36,12 @@ export default () => {
endpoints: {
metadata: notesDataset.endpointMetadata,
},
- currentUserData: JSON.parse(notesDataset.currentUserData),
notesData: JSON.parse(notesDataset.notesData),
helpPagePath: notesDataset.helpPagePath,
};
},
computed: {
- ...mapGetters(['discussionTabCounter']),
+ ...mapGetters(['isNotesFetched']),
...mapState({
activeTab: (state) => state.page.activeTab,
}),
@@ -51,15 +50,6 @@ export default () => {
},
},
watch: {
- discussionTabCounter() {
- if (window.gon?.features?.paginatedMrDiscussions) {
- if (this.$store.state.notes.doneFetchingBatchDiscussions) {
- this.updateDiscussionTabCounter();
- }
- } else {
- this.updateDiscussionTabCounter();
- }
- },
isShowTabActive: {
handler(newVal) {
if (newVal) {
@@ -70,25 +60,16 @@ export default () => {
},
},
created() {
- this.setActiveTab(window.mrTabs.getCurrentAction());
this.setEndpoints(this.endpoints);
+ if (!this.isNotesFetched) {
+ notesEventHub.$emit('fetchNotesData');
+ }
+
this.fetchMrMetadata();
},
- mounted() {
- this.notesCountBadge = $('.issuable-details').find('.notes-tab .badge');
- $(document).on('visibilitychange', this.updateDiscussionTabCounter);
- window.mrTabs.eventHub.$on('MergeRequestTabChange', this.setActiveTab);
- },
- beforeDestroy() {
- $(document).off('visibilitychange', this.updateDiscussionTabCounter);
- window.mrTabs.eventHub.$off('MergeRequestTabChange', this.setActiveTab);
- },
methods: {
- ...mapActions(['setActiveTab', 'setEndpoints', 'fetchMrMetadata']),
- updateDiscussionTabCounter() {
- this.notesCountBadge.text(this.discussionTabCounter);
- },
+ ...mapActions(['setEndpoints', 'fetchMrMetadata']),
},
render(createElement) {
// NOTE: Even though `discussionNavigator` is added to the `notes-app`,
diff --git a/app/assets/javascripts/nav/components/new_nav_toggle.vue b/app/assets/javascripts/nav/components/new_nav_toggle.vue
new file mode 100644
index 00000000000..ef59140115d
--- /dev/null
+++ b/app/assets/javascripts/nav/components/new_nav_toggle.vue
@@ -0,0 +1,71 @@
+<script>
+import { GlBadge, GlToggle } from '@gitlab/ui';
+import axios from '~/lib/utils/axios_utils';
+import { createAlert } from '~/flash';
+import { s__ } from '~/locale';
+
+export default {
+ i18n: {
+ badgeLabel: s__('NorthstarNavigation|Alpha'),
+ sectionTitle: s__('NorthstarNavigation|Navigation redesign'),
+ toggleMenuItemLabel: s__('NorthstarNavigation|New navigation'),
+ toggleLabel: s__('NorthstarNavigation|Toggle new navigation'),
+ updateError: s__(
+ 'NorthstarNavigation|Could not update the new navigation preference. Please try again later.',
+ ),
+ },
+ components: {
+ GlBadge,
+ GlToggle,
+ },
+ props: {
+ enabled: {
+ type: Boolean,
+ required: true,
+ },
+ endpoint: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ isEnabled: this.enabled,
+ };
+ },
+ methods: {
+ async toggleNav() {
+ try {
+ await axios.put(this.endpoint, { user: { use_new_navigation: !this.enabled } });
+ window.location.reload();
+ } catch (error) {
+ createAlert({
+ message: this.$options.i18n.updateError,
+ error,
+ });
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <li>
+ <div
+ class="gl-px-4 gl-py-2 gl-display-flex gl-justify-content-space-between gl-align-items-center"
+ >
+ <b>{{ $options.i18n.sectionTitle }}</b>
+ <gl-badge>{{ $options.i18n.badgeLabel }}</gl-badge>
+ </div>
+
+ <div class="menu-item gl-display-flex! gl-justify-content-space-between gl-align-items-center">
+ {{ $options.i18n.toggleMenuItemLabel }}
+ <gl-toggle
+ v-model="isEnabled"
+ :label="$options.i18n.toggleLabel"
+ label-position="hidden"
+ @change="toggleNav"
+ />
+ </div>
+ </li>
+</template>
diff --git a/app/assets/javascripts/new_branch_form.js b/app/assets/javascripts/new_branch_form.js
index ef36e58374c..a7c2e572037 100644
--- a/app/assets/javascripts/new_branch_form.js
+++ b/app/assets/javascripts/new_branch_form.js
@@ -1,15 +1,9 @@
/* eslint-disable func-names, no-return-assign, @gitlab/require-i18n-strings */
-
-import $ from 'jquery';
-import RefSelectDropdown from './ref_select_dropdown';
-
export default class NewBranchForm {
- constructor(form, availableRefs) {
+ constructor(form) {
this.validate = this.validate.bind(this);
this.branchNameError = form.querySelector('.js-branch-name-error');
this.name = form.querySelector('.js-branch-name');
- this.ref = form.querySelector('#ref');
- new RefSelectDropdown($('.js-branch-select'), availableRefs); // eslint-disable-line no-new
this.setupRestrictions();
this.addBinding();
this.init();
diff --git a/app/assets/javascripts/notebook/cells/markdown.vue b/app/assets/javascripts/notebook/cells/markdown.vue
index 9aa6abd9d8c..2caa93c3c93 100644
--- a/app/assets/javascripts/notebook/cells/markdown.vue
+++ b/app/assets/javascripts/notebook/cells/markdown.vue
@@ -1,7 +1,7 @@
<script>
import katex from 'katex';
import { marked } from 'marked';
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { sanitize } from '~/lib/dompurify';
import { hasContent, markdownConfig } from '~/lib/utils/text_utility';
import Prompt from './prompt.vue';
diff --git a/app/assets/javascripts/notebook/cells/output/html.vue b/app/assets/javascripts/notebook/cells/output/html.vue
index 5437a607e8a..74a5dd3806d 100644
--- a/app/assets/javascripts/notebook/cells/output/html.vue
+++ b/app/assets/javascripts/notebook/cells/output/html.vue
@@ -1,14 +1,10 @@
<script>
-import { GlSafeHtmlDirective } from '@gitlab/ui';
import Prompt from '../prompt.vue';
export default {
components: {
Prompt,
},
- directives: {
- SafeHtml: GlSafeHtmlDirective,
- },
props: {
count: {
type: Number,
@@ -28,12 +24,6 @@ export default {
return this.index === 0;
},
},
- safeHtmlConfig: {
- ADD_TAGS: ['use'], // to support icon SVGs
- FORBID_TAGS: ['style'],
- FORBID_ATTR: ['style'],
- ALLOW_DATA_ATTR: false,
- },
};
</script>
diff --git a/app/assets/javascripts/notebook/cells/output/latex.vue b/app/assets/javascripts/notebook/cells/output/latex.vue
index d0ed963b55d..55f97fee3dc 100644
--- a/app/assets/javascripts/notebook/cells/output/latex.vue
+++ b/app/assets/javascripts/notebook/cells/output/latex.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import 'mathjax/es5/tex-svg';
import Prompt from '../prompt.vue';
diff --git a/app/assets/javascripts/notebook/cells/output/markdown.vue b/app/assets/javascripts/notebook/cells/output/markdown.vue
index 5da057dee72..ad74e28ac74 100644
--- a/app/assets/javascripts/notebook/cells/output/markdown.vue
+++ b/app/assets/javascripts/notebook/cells/output/markdown.vue
@@ -1,5 +1,4 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import Prompt from '../prompt.vue';
import Markdown from '../markdown.vue';
@@ -9,9 +8,6 @@ export default {
Prompt,
Markdown,
},
- directives: {
- SafeHtml,
- },
props: {
count: {
type: Number,
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index 0d7ff022f8f..2ccb9a0b514 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -7,7 +7,7 @@ import Autosave from '~/autosave';
import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
import { createAlert } from '~/flash';
import { badgeState } from '~/issuable/components/status_box.vue';
-import httpStatusCodes from '~/lib/utils/http_status';
+import { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
import {
capitalizeFirstCharacter,
convertToCamelCase,
@@ -28,8 +28,6 @@ import CommentTypeDropdown from './comment_type_dropdown.vue';
import DiscussionLockedWidget from './discussion_locked_widget.vue';
import NoteSignedOutWidget from './note_signed_out_widget.vue';
-const { UNPROCESSABLE_ENTITY } = httpStatusCodes;
-
export default {
name: 'CommentForm',
i18n: COMMENT_FORM,
@@ -198,7 +196,7 @@ export default {
'toggleIssueLocalState',
]),
handleSaveError({ data, status }) {
- if (status === UNPROCESSABLE_ENTITY && data.errors?.commands_only?.length) {
+ if (status === HTTP_STATUS_UNPROCESSABLE_ENTITY && data.errors?.commands_only?.length) {
this.errors = data.errors.commands_only;
} else {
this.errors = [this.$options.i18n.GENERIC_UNSUBMITTABLE_NETWORK];
diff --git a/app/assets/javascripts/notes/components/diff_discussion_header.vue b/app/assets/javascripts/notes/components/diff_discussion_header.vue
index cf6474270a2..f949142d90a 100644
--- a/app/assets/javascripts/notes/components/diff_discussion_header.vue
+++ b/app/assets/javascripts/notes/components/diff_discussion_header.vue
@@ -1,7 +1,8 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml, GlAvatar, GlAvatarLink } from '@gitlab/ui';
+import { GlAvatar, GlAvatarLink } from '@gitlab/ui';
import { escape } from 'lodash';
import { mapActions } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { truncateSha } from '~/lib/utils/text_utility';
import { s__, __, sprintf } from '~/locale';
import NoteEditedText from './note_edited_text.vue';
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index 3bdf8349a12..aabdc1c99b6 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -1,6 +1,7 @@
<script>
-import { GlSkeletonLoader, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlSkeletonLoader } from '@gitlab/ui';
import { mapState, mapActions } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue';
import { getDiffMode } from '~/diffs/store/utils';
diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue
index 930876e90b1..c15c11ed9db 100644
--- a/app/assets/javascripts/notes/components/note_actions.vue
+++ b/app/assets/javascripts/notes/components/note_actions.vue
@@ -19,6 +19,7 @@ export default {
editCommentLabel: __('Edit comment'),
deleteCommentLabel: __('Delete comment'),
moreActionsLabel: __('More actions'),
+ reportAbuse: __('Report abuse to administrator'),
},
name: 'NoteActions',
components: {
@@ -362,7 +363,7 @@ export default {
<!-- eslint-enable @gitlab/vue-no-data-toggle -->
<ul class="dropdown-menu more-actions-dropdown dropdown-open-left">
<gl-dropdown-item v-if="canReportAsAbuse" :href="reportAbusePath">
- {{ __('Report abuse to admin') }}
+ {{ $options.i18n.reportAbuse }}
</gl-dropdown-item>
<gl-dropdown-item
v-if="noteUrl"
diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue
index 82c125b79ce..20cf21cd1b6 100644
--- a/app/assets/javascripts/notes/components/note_body.vue
+++ b/app/assets/javascripts/notes/components/note_body.vue
@@ -1,12 +1,10 @@
<script>
-import $ from 'jquery';
-import { GlSafeHtmlDirective } from '@gitlab/ui';
import { escape } from 'lodash';
import { mapActions, mapGetters, mapState } from 'vuex';
-
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { __ } from '~/locale';
-import '~/behaviors/markdown/render_gfm';
import Suggestions from '~/vue_shared/components/markdown/suggestions.vue';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import autosave from '../mixins/autosave';
import NoteAttachment from './note_attachment.vue';
import NoteAwardsList from './note_awards_list.vue';
@@ -22,7 +20,7 @@ export default {
Suggestions,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [autosave],
props: {
@@ -122,7 +120,7 @@ export default {
'removeSuggestionInfoFromBatch',
]),
renderGFM() {
- $(this.$refs['note-body']).renderGFM();
+ renderGFM(this.$refs['note-body']);
},
handleFormUpdate(noteText, parentElement, callback, resolveDiscussion) {
this.$emit('handleFormUpdate', { noteText, parentElement, callback, resolveDiscussion });
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue
index 63c7010983e..36f7d720e48 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -1,17 +1,10 @@
<script>
-import {
- GlIcon,
- GlBadge,
- GlLoadingIcon,
- GlTooltipDirective,
- GlSafeHtmlDirective as SafeHtml,
-} from '@gitlab/ui';
+import { GlIcon, GlBadge, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
import { mapActions } from 'vuex';
import { __, s__ } from '~/locale';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
export default {
- safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
components: {
TimeAgoTooltip,
GitlabTeamMemberBadge: () =>
@@ -21,7 +14,6 @@ export default {
GlLoadingIcon,
},
directives: {
- SafeHtml,
GlTooltip: GlTooltipDirective,
},
props: {
diff --git a/app/assets/javascripts/notes/components/note_signed_out_widget.vue b/app/assets/javascripts/notes/components/note_signed_out_widget.vue
index 593933016e1..94636b3e47b 100644
--- a/app/assets/javascripts/notes/components/note_signed_out_widget.vue
+++ b/app/assets/javascripts/notes/components/note_signed_out_widget.vue
@@ -1,6 +1,6 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import { mapGetters } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { __, sprintf } from '~/locale';
export default {
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index b668d6ec182..ff801cdccea 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -235,7 +235,7 @@ export default {
this.saveNote(replyData)
.then((res) => {
- if (res.hasFlash !== true) {
+ if (res.hasAlert !== true) {
this.isReplying = false;
clearDraft(this.autosaveKey);
}
@@ -307,7 +307,7 @@ export default {
:draft="draftForDiscussion(discussion.reply_id)"
:line="line"
/>
- <div
+ <li
v-else-if="canShowReplyActions && showReplies"
:class="{ 'is-replying': isReplying }"
class="discussion-reply-holder gl-border-t-0! clearfix"
@@ -334,7 +334,7 @@ export default {
@cancelForm="cancelReplyForm"
/>
<note-signed-out-widget v-if="!isLoggedIn" />
- </div>
+ </li>
</template>
</discussion-notes>
</component>
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index 8ce0c2f8648..826e7e5a3d0 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -1,16 +1,18 @@
<script>
-import { GlSprintf, GlSafeHtmlDirective as SafeHtml, GlAvatarLink, GlAvatar } from '@gitlab/ui';
+import { GlSprintf, GlAvatarLink, GlAvatar } from '@gitlab/ui';
import $ from 'jquery';
import { escape, isEmpty } from 'lodash';
import { mapGetters, mapActions } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import { INLINE_DIFF_LINES_KEY } from '~/diffs/constants';
import { createAlert } from '~/flash';
-import httpStatusCodes from '~/lib/utils/http_status';
+import { HTTP_STATUS_GONE } from '~/lib/utils/http_status';
import { ignoreWhilePending } from '~/lib/utils/ignore_while_pending';
import { truncateSha } from '~/lib/utils/text_utility';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import { __, s__, sprintf } from '~/locale';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import eventHub from '../event_hub';
import noteable from '../mixins/noteable';
import resolvable from '../mixins/resolvable';
@@ -286,7 +288,7 @@ export default {
this.isEditing = false;
this.isRequesting = false;
this.oldContent = null;
- $(this.$refs.noteBody.$el).renderGFM();
+ renderGFM(this.$refs.noteBody.$el);
this.$refs.noteBody.resetAutoSave();
this.$emit('updateSuccess');
},
@@ -336,7 +338,7 @@ export default {
callback();
})
.catch((response) => {
- if (response.status === httpStatusCodes.GONE) {
+ if (response.status === HTTP_STATUS_GONE) {
this.removeNote(this.note);
this.updateSuccess();
callback();
@@ -515,6 +517,9 @@ export default {
@handleFormUpdate="formUpdateHandler"
@cancelForm="formCancelHandler"
/>
+ <div class="timeline-discussion-body-footer">
+ <slot name="after-note-body"></slot>
+ </div>
</div>
</div>
</timeline-entry-item>
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index 7bb1a1a1bfe..fcf37217902 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -1,13 +1,11 @@
<script>
import { mapGetters, mapActions } from 'vuex';
import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
-import { createAlert } from '~/flash';
-import { __ } from '~/locale';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import OrderedLayout from '~/vue_shared/components/ordered_layout.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import DraftNote from '~/batch_comments/components/draft_note.vue';
-import { getLocationHash, doesHashExistInUrl } from '~/lib/utils/url_utility';
+import { getLocationHash } from '~/lib/utils/url_utility';
import PlaceholderNote from '~/vue_shared/components/notes/placeholder_note.vue';
import PlaceholderSystemNote from '~/vue_shared/components/notes/placeholder_system_note.vue';
import SkeletonLoadingContainer from '~/vue_shared/components/notes/skeleton_note.vue';
@@ -57,11 +55,6 @@ export default {
default: undefined,
required: false,
},
- userData: {
- type: Object,
- required: false,
- default: () => ({}),
- },
shouldShow: {
type: Boolean,
required: false,
@@ -90,16 +83,12 @@ export default {
'commentsDisabled',
'getNoteableData',
'userCanReply',
- 'discussionTabCounter',
'sortDirection',
'timelineEnabled',
]),
sortDirDesc() {
return this.sortDirection === constants.DESC;
},
- discussionTabCounterText() {
- return this.isLoading ? '' : this.discussionTabCounter;
- },
noteableType() {
return this.noteableData.noteableType;
},
@@ -147,11 +136,6 @@ export default {
this.renderSkeleton = !this.shouldShow;
});
},
- discussionTabCounterText(val) {
- if (this.discussionsCount) {
- this.discussionsCount.textContent = val;
- }
- },
isAppReady: {
handler(isReady) {
if (!isReady) return;
@@ -162,20 +146,7 @@ export default {
immediate: true,
},
},
- created() {
- this.discussionsCount = document.querySelector('.js-discussions-count');
-
- this.setNotesData(this.notesData);
- this.setNoteableData(this.noteableData);
- this.setUserData(this.userData);
- this.setTargetNoteHash(getLocationHash());
- eventHub.$once('fetchNotesData', this.fetchNotes);
- },
mounted() {
- if (this.shouldShow) {
- this.fetchNotes();
- }
-
const { parentElement } = this.$el;
if (parentElement && parentElement.classList.contains('js-vue-notes-event')) {
parentElement.addEventListener('toggleAward', (event) => {
@@ -200,23 +171,16 @@ export default {
},
methods: {
...mapActions([
- 'setFetchingState',
- 'setLoadingState',
- 'fetchDiscussions',
- 'poll',
'toggleAward',
- 'setNotesData',
- 'setNoteableData',
- 'setUserData',
'setLastFetchedAt',
'setTargetNoteHash',
'toggleDiscussion',
- 'setNotesFetchedState',
'expandDiscussion',
'startTaskList',
'convertToDiscussion',
'stopPolling',
'setConfidentiality',
+ 'fetchNotes',
]),
discussionIsIndividualNoteAndNotConverted(discussion) {
return discussion.individual_note && !this.convertedDisscussionIds.includes(discussion.id);
@@ -228,37 +192,6 @@ export default {
this.setTargetNoteHash(getLocationHash());
}
},
- fetchNotes() {
- if (this.isFetching) return null;
-
- this.setFetchingState(true);
-
- return this.fetchDiscussions(this.getFetchDiscussionsConfig())
- .then(this.initPolling)
- .then(() => {
- this.setLoadingState(false);
- this.setNotesFetchedState(true);
- eventHub.$emit('fetchedNotesData');
- this.setFetchingState(false);
- })
- .catch(() => {
- this.setLoadingState(false);
- this.setNotesFetchedState(true);
- createAlert({
- message: __('Something went wrong while fetching comments. Please try again.'),
- });
- });
- },
- initPolling() {
- if (this.isPollingInitialized) {
- return;
- }
-
- this.setLastFetchedAt(this.getNotesDataByProp('lastFetchedAt'));
-
- this.poll();
- this.isPollingInitialized = true;
- },
checkLocationHash() {
const hash = getLocationHash();
const noteId = hash && hash.replace(/^note_/, '');
@@ -278,24 +211,6 @@ export default {
.then(this.$nextTick)
.then(() => eventHub.$emit('startReplying', discussionId));
},
- getFetchDiscussionsConfig() {
- const defaultConfig = { path: this.getNotesDataByProp('discussionsPath') };
-
- const currentFilter =
- this.getNotesDataByProp('notesFilter') || constants.DISCUSSION_FILTERS_DEFAULT_VALUE;
-
- if (
- doesHashExistInUrl(constants.NOTE_UNDERSCORE) &&
- currentFilter !== constants.DISCUSSION_FILTERS_DEFAULT_VALUE
- ) {
- return {
- ...defaultConfig,
- filter: constants.DISCUSSION_FILTERS_DEFAULT_VALUE,
- persistFilter: false,
- };
- }
- return defaultConfig;
- },
},
systemNote: constants.SYSTEM_NOTE,
};
diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js
index defcb0533b7..95263e666b2 100644
--- a/app/assets/javascripts/notes/index.js
+++ b/app/assets/javascripts/notes/index.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
+import { getLocationHash } from '~/lib/utils/url_utility';
import NotesApp from './components/notes_app.vue';
import { store } from './stores';
import { getNotesFilterData } from './utils/get_notes_filter_data';
@@ -13,6 +14,34 @@ export default () => {
const notesFilterProps = getNotesFilterData(el);
const showTimelineViewToggle = parseBoolean(el.dataset.showTimelineViewToggle);
+ const notesDataset = el.dataset;
+ const parsedUserData = JSON.parse(notesDataset.currentUserData);
+ const noteableData = JSON.parse(notesDataset.noteableData);
+ let currentUserData = {};
+
+ noteableData.noteableType = notesDataset.noteableType;
+ noteableData.targetType = notesDataset.targetType;
+ noteableData.discussion_locked = parseBoolean(noteableData.discussion_locked);
+
+ if (parsedUserData) {
+ currentUserData = {
+ id: parsedUserData.id,
+ name: parsedUserData.name,
+ username: parsedUserData.username,
+ avatar_url: parsedUserData.avatar_path || parsedUserData.avatar_url,
+ path: parsedUserData.path,
+ can_add_timeline_events: parseBoolean(notesDataset.canAddTimelineEvents),
+ };
+ }
+
+ const notesData = JSON.parse(notesDataset.notesData);
+
+ store.dispatch('setNotesData', notesData);
+ store.dispatch('setNoteableData', noteableData);
+ store.dispatch('setUserData', currentUserData);
+ store.dispatch('setTargetNoteHash', getLocationHash());
+ store.dispatch('fetchNotes');
+
// eslint-disable-next-line no-new
new Vue({
el,
@@ -25,30 +54,6 @@ export default () => {
showTimelineViewToggle,
},
data() {
- const notesDataset = el.dataset;
- const parsedUserData = JSON.parse(notesDataset.currentUserData);
- const noteableData = JSON.parse(notesDataset.noteableData);
- let currentUserData = {};
-
- noteableData.noteableType = notesDataset.noteableType;
- noteableData.targetType = notesDataset.targetType;
- if (noteableData.discussion_locked === null) {
- // discussion_locked has never been set for this issuable.
- // set to `false` for safety.
- noteableData.discussion_locked = false;
- }
-
- if (parsedUserData) {
- currentUserData = {
- id: parsedUserData.id,
- name: parsedUserData.name,
- username: parsedUserData.username,
- avatar_url: parsedUserData.avatar_path || parsedUserData.avatar_url,
- path: parsedUserData.path,
- can_add_timeline_events: parseBoolean(notesDataset.canAddTimelineEvents),
- };
- }
-
return {
noteableData,
currentUserData,
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index fcef26d720c..d290a8ccb84 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -2,14 +2,14 @@ import $ from 'jquery';
import Visibility from 'visibilityjs';
import Vue from 'vue';
import Api from '~/api';
-import createFlash from '~/flash';
+import { createAlert, VARIANT_INFO } from '~/flash';
import { EVENT_ISSUABLE_VUE_APP_CHANGE } from '~/issuable/constants';
import axios from '~/lib/utils/axios_utils';
import { __, sprintf } from '~/locale';
import toast from '~/vue_shared/plugins/global_toast';
import { confidentialWidget } from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
-import updateIssueLockMutation from '~/sidebar/components/lock/mutations/update_issue_lock.mutation.graphql';
-import updateMergeRequestLockMutation from '~/sidebar/components/lock/mutations/update_merge_request_lock.mutation.graphql';
+import updateIssueLockMutation from '~/sidebar/queries/update_issue_lock.mutation.graphql';
+import updateMergeRequestLockMutation from '~/sidebar/queries/update_merge_request_lock.mutation.graphql';
import loadAwardsHandler from '~/awards_handler';
import { isInViewport, scrollToElement, isInMRPage } from '~/lib/utils/common_utils';
import Poll from '~/lib/utils/poll';
@@ -114,6 +114,39 @@ export const fetchDiscussions = (
});
};
+export const fetchNotes = ({ dispatch, getters }) => {
+ if (getters.isFetching) return null;
+
+ dispatch('setFetchingState', true);
+
+ return dispatch('fetchDiscussions', getters.getFetchDiscussionsConfig)
+ .then(() => dispatch('initPolling'))
+ .then(() => {
+ dispatch('setLoadingState', false);
+ dispatch('setNotesFetchedState', true);
+ notesEventHub.$emit('fetchedNotesData');
+ dispatch('setFetchingState', false);
+ })
+ .catch(() => {
+ dispatch('setLoadingState', false);
+ dispatch('setNotesFetchedState', true);
+ createAlert({
+ message: __('Something went wrong while fetching comments. Please try again.'),
+ });
+ });
+};
+
+export const initPolling = ({ state, dispatch, getters, commit }) => {
+ if (state.isPollingInitialized) {
+ return;
+ }
+
+ dispatch('setLastFetchedAt', getters.getNotesDataByProp('lastFetchedAt'));
+
+ dispatch('poll');
+ commit(types.SET_IS_POLLING_INITIALIZED, true);
+};
+
export const fetchDiscussionsBatch = ({ commit, dispatch }, { path, config, cursor, perPage }) => {
const params = { ...config?.params, per_page: perPage };
@@ -270,7 +303,7 @@ export const promoteCommentToTimelineEvent = (
errorObj = error;
}
- createFlash({
+ createAlert({
message,
captureError,
error: errorObj,
@@ -465,9 +498,9 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
$('.js-gfm-input').trigger('clear-commands-cache.atwho');
- createFlash({
+ createAlert({
message: message || __('Commands applied'),
- type: 'notice',
+ variant: VARIANT_INFO,
parent: noteData.flashContainer,
});
}
@@ -490,7 +523,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
awardsHandler.scrollToAwards();
})
.catch(() => {
- createFlash({
+ createAlert({
message: __('Something went wrong while adding your award. Please try again.'),
parent: noteData.flashContainer,
});
@@ -529,11 +562,11 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
const errorMsg = sprintf(__('Your comment could not be submitted because %{error}'), {
error: base[0].toLowerCase(),
});
- createFlash({
+ createAlert({
message: errorMsg,
parent: noteData.flashContainer,
});
- return { ...data, hasFlash: true };
+ return { ...data, hasAlert: true };
}
}
@@ -580,7 +613,7 @@ const getFetchDataParams = (state) => {
export const poll = ({ commit, state, getters, dispatch }) => {
const notePollOccurrenceTracking = create();
- let flashContainer;
+ let alert;
notePollOccurrenceTracking.handle(1, () => {
// Since polling halts internally after 1 failure, we manually try one more time
@@ -588,7 +621,7 @@ export const poll = ({ commit, state, getters, dispatch }) => {
});
notePollOccurrenceTracking.handle(2, () => {
// On the second failure in a row, show the alert and try one more time (hoping to succeed and clear the error)
- flashContainer = createFlash({
+ alert = createAlert({
message: __('Something went wrong while fetching latest comments.'),
});
setTimeout(() => eTagPoll.restart(), NOTES_POLLING_INTERVAL);
@@ -608,7 +641,7 @@ export const poll = ({ commit, state, getters, dispatch }) => {
if (notePollOccurrenceTracking.count) {
notePollOccurrenceTracking.reset();
}
- flashContainer?.close();
+ alert?.dismiss();
},
errorCallback: () => notePollOccurrenceTracking.occur(),
});
@@ -681,7 +714,7 @@ export const filterDiscussion = ({ commit, dispatch }, { path, filter, persistFi
.catch(() => {
dispatch('setLoadingState', false);
dispatch('setNotesFetchedState', true);
- createFlash({
+ createAlert({
message: __('Something went wrong while fetching comments. Please try again.'),
});
});
@@ -726,7 +759,7 @@ export const submitSuggestion = (
const flashMessage = errorMessage || defaultMessage;
- createFlash({
+ createAlert({
message: flashMessage,
parent: flashContainer,
});
@@ -762,7 +795,7 @@ export const submitSuggestionBatch = ({ commit, dispatch, state }, { message, fl
const flashMessage = errorMessage || defaultMessage;
- createFlash({
+ createAlert({
message: flashMessage,
parent: flashContainer,
});
@@ -804,7 +837,7 @@ export const fetchDescriptionVersion = ({ dispatch }, { endpoint, startingVersio
})
.catch((error) => {
dispatch('receiveDescriptionVersionError', error);
- createFlash({
+ createAlert({
message: __('Something went wrong while fetching description changes. Please try again.'),
});
});
@@ -838,7 +871,7 @@ export const softDeleteDescriptionVersion = (
})
.catch((error) => {
dispatch('receiveDeleteDescriptionVersionError', error);
- createFlash({
+ createAlert({
message: __('Something went wrong while deleting description changes. Please try again.'),
});
diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js
index 5ad7a811726..f6373f24b74 100644
--- a/app/assets/javascripts/notes/stores/getters.js
+++ b/app/assets/javascripts/notes/stores/getters.js
@@ -2,6 +2,7 @@ import { flattenDeep, clone } from 'lodash';
import { match } from '~/diffs/utils/diff_file';
import { badgeState } from '~/issuable/components/status_box.vue';
import { isInMRPage } from '~/lib/utils/common_utils';
+import { doesHashExistInUrl } from '~/lib/utils/url_utility';
import * as constants from '../constants';
import { collapseSystemNotes } from './collapse_utils';
@@ -314,3 +315,22 @@ export const getSuggestionsFilePaths = (state) => () =>
return acc;
}, []);
+
+export const getFetchDiscussionsConfig = (state, getters) => {
+ const defaultConfig = { path: getters.getNotesDataByProp('discussionsPath') };
+
+ const currentFilter =
+ getters.getNotesDataByProp('notesFilter') || constants.DISCUSSION_FILTERS_DEFAULT_VALUE;
+
+ if (
+ doesHashExistInUrl(constants.NOTE_UNDERSCORE) &&
+ currentFilter !== constants.DISCUSSION_FILTERS_DEFAULT_VALUE
+ ) {
+ return {
+ ...defaultConfig,
+ filter: constants.DISCUSSION_FILTERS_DEFAULT_VALUE,
+ persistFilter: false,
+ };
+ }
+ return defaultConfig;
+};
diff --git a/app/assets/javascripts/notes/stores/modules/index.js b/app/assets/javascripts/notes/stores/modules/index.js
index 7ba1f470b05..81c4c42a49a 100644
--- a/app/assets/javascripts/notes/stores/modules/index.js
+++ b/app/assets/javascripts/notes/stores/modules/index.js
@@ -50,6 +50,7 @@ export default () => ({
descriptionVersions: {},
isTimelineEnabled: false,
isFetching: false,
+ isPollingInitialized: false,
},
actions,
getters,
diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js
index 42df6bc0980..bc1d5b5bba4 100644
--- a/app/assets/javascripts/notes/stores/mutation_types.js
+++ b/app/assets/javascripts/notes/stores/mutation_types.js
@@ -27,6 +27,7 @@ export const CLEAR_SUGGESTION_BATCH = 'CLEAR_SUGGESTION_BATCH';
export const CONVERT_TO_DISCUSSION = 'CONVERT_TO_DISCUSSION';
export const REMOVE_CONVERTED_DISCUSSION = 'REMOVE_CONVERTED_DISCUSSION';
export const UPDATE_ASSIGNEES = 'UPDATE_ASSIGNEES';
+export const SET_IS_POLLING_INITIALIZED = 'SET_IS_POLLING_INITIALIZED';
// DISCUSSION
export const COLLAPSE_DISCUSSION = 'COLLAPSE_DISCUSSION';
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index 83c15c12eac..5d532b68f1b 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -428,4 +428,7 @@ export default {
[types.SET_PROMOTE_COMMENT_TO_TIMELINE_PROGRESS](state, value) {
state.isPromoteCommentToTimelineEventInProgress = value;
},
+ [types.SET_IS_POLLING_INITIALIZED](state, value) {
+ state.isPollingInitialized = value;
+ },
};
diff --git a/app/assets/javascripts/observability/components/observability_app.vue b/app/assets/javascripts/observability/components/observability_app.vue
index 4f5e27be46f..33d23ea043b 100644
--- a/app/assets/javascripts/observability/components/observability_app.vue
+++ b/app/assets/javascripts/observability/components/observability_app.vue
@@ -1,21 +1,69 @@
<script>
+import { darkModeEnabled } from '~/lib/utils/color_utils';
+import { setUrlParams } from '~/lib/utils/url_utility';
+
+import { MESSAGE_EVENT_TYPE, OBSERVABILITY_ROUTES, SKELETON_VARIANT } from '../constants';
+import ObservabilitySkeleton from './skeleton/index.vue';
+
export default {
+ components: {
+ ObservabilitySkeleton,
+ },
props: {
observabilityIframeSrc: {
type: String,
required: true,
},
},
+ computed: {
+ iframeSrcWithParams() {
+ return setUrlParams(
+ { theme: darkModeEnabled() ? 'dark' : 'light', username: gon?.current_username },
+ this.observabilityIframeSrc,
+ );
+ },
+ getSkeletonVariant() {
+ switch (this.$route.path) {
+ case OBSERVABILITY_ROUTES.DASHBOARDS:
+ return SKELETON_VARIANT.DASHBOARDS;
+ case OBSERVABILITY_ROUTES.EXPLORE:
+ return SKELETON_VARIANT.EXPLORE;
+ case OBSERVABILITY_ROUTES.MANAGE:
+ return SKELETON_VARIANT.MANAGE;
+ default:
+ return SKELETON_VARIANT.DASHBOARDS;
+ }
+ },
+ },
mounted() {
window.addEventListener('message', this.messageHandler);
},
+ destroyed() {
+ window.removeEventListener('message', this.messageHandler);
+ },
methods: {
messageHandler(e) {
const isExpectedOrigin = e.origin === new URL(this.observabilityIframeSrc)?.origin;
+ if (!isExpectedOrigin) return;
- const isNewObservabilityPath = this.$route?.query?.observability_path !== e.data?.url;
+ const {
+ data: { type, payload },
+ } = e;
+ switch (type) {
+ case MESSAGE_EVENT_TYPE.GOUI_LOADED:
+ this.$refs.iframeSkeleton.handleSkeleton();
+ break;
+ case MESSAGE_EVENT_TYPE.GOUI_ROUTE_UPDATE:
+ this.routeUpdateHandler(payload);
+ break;
+ default:
+ break;
+ }
+ },
+ routeUpdateHandler(payload) {
+ const isNewObservabilityPath = this.$route?.query?.observability_path !== payload?.url;
- const shouldNotHandleMessage = !isExpectedOrigin || !e.data.url || !isNewObservabilityPath;
+ const shouldNotHandleMessage = !payload.url || !isNewObservabilityPath;
if (shouldNotHandleMessage) {
return;
@@ -24,7 +72,7 @@ export default {
// this will update the `observability_path` query param on each route change inside Observability UI
this.$router.replace({
name: this.$route.pathname,
- query: { ...this.$route.query, observability_path: e.data.url },
+ query: { ...this.$route.query, observability_path: payload.url },
});
},
},
@@ -32,11 +80,14 @@ export default {
</script>
<template>
- <iframe
- id="observability-ui-iframe"
- data-testid="observability-ui-iframe"
- frameborder="0"
- height="100%"
- :src="observabilityIframeSrc"
- ></iframe>
+ <observability-skeleton ref="iframeSkeleton" :variant="getSkeletonVariant">
+ <iframe
+ id="observability-ui-iframe"
+ data-testid="observability-ui-iframe"
+ frameborder="0"
+ height="100%"
+ :src="iframeSrcWithParams"
+ sandbox="allow-same-origin allow-forms allow-scripts"
+ ></iframe>
+ </observability-skeleton>
</template>
diff --git a/app/assets/javascripts/observability/components/skeleton/dashboards.vue b/app/assets/javascripts/observability/components/skeleton/dashboards.vue
new file mode 100644
index 00000000000..8b106407953
--- /dev/null
+++ b/app/assets/javascripts/observability/components/skeleton/dashboards.vue
@@ -0,0 +1,29 @@
+<script>
+import { GlSkeletonLoader } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlSkeletonLoader,
+ },
+};
+</script>
+<template>
+ <gl-skeleton-loader :height="200">
+ <!-- Top left -->
+ <rect y="2" width="10" height="8" />
+ <rect y="2" x="15" width="15" height="8" />
+ <rect y="2" x="35" width="15" height="8" />
+
+ <!-- Top right -->
+ <rect y="2" x="354" width="10" height="8" />
+ <rect y="2" x="366" width="10" height="8" />
+ <rect y="2" x="378" width="10" height="8" />
+ <rect y="2" x="390" width="10" height="8" />
+
+ <!-- Middle header -->
+ <rect y="15" width="400" height="30" rx="2" ry="2" />
+
+ <!-- Dashboard container -->
+ <rect y="50" width="200" height="100" rx="2" ry="2" />
+ </gl-skeleton-loader>
+</template>
diff --git a/app/assets/javascripts/observability/components/skeleton/explore.vue b/app/assets/javascripts/observability/components/skeleton/explore.vue
new file mode 100644
index 00000000000..1fcbd4fb1cb
--- /dev/null
+++ b/app/assets/javascripts/observability/components/skeleton/explore.vue
@@ -0,0 +1,27 @@
+<script>
+import { GlSkeletonLoader } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlSkeletonLoader,
+ },
+};
+</script>
+<template>
+ <gl-skeleton-loader :height="200">
+ <!-- Top left -->
+ <circle y="2" cx="6" cy="6" r="4" />
+ <rect y="2" x="15" width="15" height="8" />
+ <rect y="2" x="35" width="40" height="8" />
+
+ <!-- Top right -->
+
+ <rect y="2" x="263" width="13" height="8" />
+ <rect y="2" x="278" width="8" height="8" />
+ <rect y="2" x="288" width="50" height="8" />
+ <rect y="2" x="340" width="18" height="8" />
+ <rect y="2" x="360" width="30" height="8" />
+
+ <rect y="15" width="400" height="30" rx="2" ry="2" />
+ </gl-skeleton-loader>
+</template>
diff --git a/app/assets/javascripts/observability/components/skeleton/index.vue b/app/assets/javascripts/observability/components/skeleton/index.vue
new file mode 100644
index 00000000000..1e2671c8166
--- /dev/null
+++ b/app/assets/javascripts/observability/components/skeleton/index.vue
@@ -0,0 +1,89 @@
+<script>
+import { GlSkeletonLoader } from '@gitlab/ui';
+import { SKELETON_VARIANT } from '../../constants';
+import DashboardsSkeleton from './dashboards.vue';
+import ExploreSkeleton from './explore.vue';
+import ManageSkeleton from './manage.vue';
+
+export default {
+ SKELETON_VARIANT,
+ components: {
+ GlSkeletonLoader,
+ DashboardsSkeleton,
+ ExploreSkeleton,
+ ManageSkeleton,
+ },
+ props: {
+ variant: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ data() {
+ return {
+ loading: null,
+ timerId: null,
+ };
+ },
+ mounted() {
+ this.timerId = setTimeout(() => {
+ /**
+ * If observability UI is not loaded then this.loading would be null
+ * we will show skeleton in that case
+ */
+ if (this.loading !== false) {
+ this.showSkeleton();
+ }
+ }, 500);
+ },
+ methods: {
+ handleSkeleton() {
+ if (this.loading === null) {
+ /**
+ * If observability UI content loads with in 500ms
+ * do not show skeleton.
+ */
+ clearTimeout(this.timerId);
+ return;
+ }
+
+ /**
+ * If observability UI content loads after 500ms
+ * wait for 400ms to hide skeleton.
+ * This is mostly to avoid the flashing effect If content loads imediately after skeleton
+ */
+ setTimeout(this.hideSkeleton, 400);
+ },
+ hideSkeleton() {
+ this.loading = false;
+ },
+ showSkeleton() {
+ this.loading = true;
+ },
+ },
+};
+</script>
+<template>
+ <div class="gl-flex-grow-1 gl-display-flex gl-flex-direction-column gl-flex-align-items-stretch">
+ <div v-show="loading" class="gl-px-5">
+ <dashboards-skeleton v-if="variant === $options.SKELETON_VARIANT.DASHBOARDS" />
+ <explore-skeleton v-else-if="variant === $options.SKELETON_VARIANT.EXPLORE" />
+ <manage-skeleton v-else-if="variant === $options.SKELETON_VARIANT.MANAGE" />
+
+ <gl-skeleton-loader v-else>
+ <rect y="2" width="10" height="8" />
+ <rect y="2" x="15" width="15" height="8" />
+ <rect y="2" x="35" width="15" height="8" />
+ <rect y="15" width="400" height="30" />
+ </gl-skeleton-loader>
+ </div>
+
+ <div
+ v-show="!loading"
+ class="gl-flex-grow-1 gl-display-flex gl-flex-direction-column gl-flex-align-items-stretch"
+ >
+ <slot></slot>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/observability/components/skeleton/manage.vue b/app/assets/javascripts/observability/components/skeleton/manage.vue
new file mode 100644
index 00000000000..4b029120328
--- /dev/null
+++ b/app/assets/javascripts/observability/components/skeleton/manage.vue
@@ -0,0 +1,25 @@
+<script>
+import { GlSkeletonLoader } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlSkeletonLoader,
+ },
+};
+</script>
+<template>
+ <gl-skeleton-loader :height="200">
+ <!-- Top header-->
+ <rect y="2" width="400" height="30" />
+
+ <rect y="35" x="65" width="80" height="8" />
+ <rect y="35" x="205" width="30" height="8" />
+ <rect y="35" x="240" width="25" height="8" />
+ <rect y="35" x="270" width="20" height="8" />
+
+ <rect y="55" x="65" width="100" height="8" />
+ <rect y="55" x="225" width="65" height="8" />
+
+ <rect y="65" x="65" width="225" height="200" rx="2" ry="2" />
+ </gl-skeleton-loader>
+</template>
diff --git a/app/assets/javascripts/observability/constants.js b/app/assets/javascripts/observability/constants.js
new file mode 100644
index 00000000000..74dd543e285
--- /dev/null
+++ b/app/assets/javascripts/observability/constants.js
@@ -0,0 +1,16 @@
+export const MESSAGE_EVENT_TYPE = Object.freeze({
+ GOUI_LOADED: 'GOUI_LOADED',
+ GOUI_ROUTE_UPDATE: 'GOUI_ROUTE_UPDATE',
+});
+
+export const OBSERVABILITY_ROUTES = Object.freeze({
+ DASHBOARDS: '/groups/gitlab-org/-/observability/dashboards',
+ EXPLORE: '/groups/gitlab-org/-/observability/explore',
+ MANAGE: '/groups/gitlab-org/-/observability/manage',
+});
+
+export const SKELETON_VARIANT = Object.freeze({
+ DASHBOARDS: 'dashboards',
+ EXPLORE: 'explore',
+ MANAGE: 'manage',
+});
diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/delete_alert.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/delete_alert.vue
index 1b7d5af6134..56d2ff86fb7 100644
--- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/delete_alert.vue
+++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/delete_alert.vue
@@ -1,11 +1,7 @@
<script>
import { GlSprintf, GlAlert, GlLink } from '@gitlab/ui';
-import {
- ALERT_MESSAGES,
- ADMIN_GARBAGE_COLLECTION_TIP,
- ALERT_DANGER_IMPORTING,
-} from '../../constants/index';
+import { ALERT_MESSAGES, ADMIN_GARBAGE_COLLECTION_TIP } from '../../constants/index';
export default {
components: {
@@ -27,7 +23,6 @@ export default {
},
},
garbageCollectionHelpPagePath: { type: String, required: false, default: '' },
- containerRegistryImportingHelpPagePath: { type: String, required: false, default: '' },
isAdmin: {
type: Boolean,
default: false,
@@ -53,11 +48,6 @@ export default {
}
return config;
},
- alertHref() {
- return this.deleteAlertType === ALERT_DANGER_IMPORTING
- ? this.containerRegistryImportingHelpPagePath
- : this.garbageCollectionHelpPagePath;
- },
},
};
</script>
@@ -71,7 +61,7 @@ export default {
>
<gl-sprintf :message="deleteAlertConfig.message">
<template #docLink="{ content }">
- <gl-link :href="alertHref" target="_blank">
+ <gl-link :href="garbageCollectionHelpPagePath" target="_blank">
{{ content }}
</gl-link>
</template>
diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list.vue
index 597df2b9bc3..c10d8be69a0 100644
--- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list.vue
+++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list.vue
@@ -6,8 +6,8 @@ import { joinPaths } from '~/lib/utils/url_utility';
import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue';
import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
-import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import TagsLoader from '~/packages_and_registries/shared/components/tags_loader.vue';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import {
REMOVE_TAGS_BUTTON_TITLE,
TAGS_LIST_TITLE,
diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/details.js b/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/details.js
index 98c24350f09..7bb69363743 100644
--- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/details.js
+++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/details.js
@@ -93,10 +93,6 @@ export const DETAILS_DELETE_IMAGE_ERROR_MESSAGE = s__(
'ContainerRegistry|Something went wrong while scheduling the image for deletion.',
);
-export const DETAILS_IMPORTING_ERROR_MESSAGE = s__(
- 'ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}.',
-);
-
export const DELETE_IMAGE_CONFIRMATION_TITLE = s__('ContainerRegistry|Delete image repository?');
export const DELETE_IMAGE_CONFIRMATION_TEXT = s__(
'ContainerRegistry|Deleting the image repository will delete all images and tags inside. This action cannot be undone. Please type the following to confirm: %{code}',
@@ -137,7 +133,6 @@ export const ALERT_DANGER_TAG = 'danger_tag';
export const ALERT_SUCCESS_TAGS = 'success_tags';
export const ALERT_DANGER_TAGS = 'danger_tags';
export const ALERT_DANGER_IMAGE = 'danger_image';
-export const ALERT_DANGER_IMPORTING = 'danger_importing';
export const DELETE_SCHEDULED = 'DELETE_SCHEDULED';
export const DELETE_FAILED = 'DELETE_FAILED';
@@ -148,7 +143,6 @@ export const ALERT_MESSAGES = {
[ALERT_SUCCESS_TAGS]: DELETE_TAGS_SUCCESS_MESSAGE,
[ALERT_DANGER_TAGS]: DELETE_TAGS_ERROR_MESSAGE,
[ALERT_DANGER_IMAGE]: DETAILS_DELETE_IMAGE_ERROR_MESSAGE,
- [ALERT_DANGER_IMPORTING]: DETAILS_IMPORTING_ERROR_MESSAGE,
};
export const UNFINISHED_STATUS = 'UNFINISHED';
diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/details.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/details.vue
index b339c8c8371..83c0d2cdfca 100644
--- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/details.vue
+++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/details.vue
@@ -20,7 +20,6 @@ import {
ALERT_SUCCESS_TAGS,
ALERT_DANGER_TAGS,
ALERT_DANGER_IMAGE,
- ALERT_DANGER_IMPORTING,
FETCH_IMAGES_LIST_ERROR_MESSAGE,
UNFINISHED_STATUS,
MISSING_OR_DELETED_IMAGE_BREADCRUMB,
@@ -33,8 +32,6 @@ import getContainerRepositoryDetailsQuery from '../graphql/queries/get_container
import getContainerRepositoryTagsQuery from '../graphql/queries/get_container_repository_tags.query.graphql';
import getContainerRepositoriesDetails from '../graphql/queries/get_container_repositories_details.query.graphql';
-const REPOSITORY_IMPORTING_ERROR_MESSAGE = 'repository importing';
-
export default {
name: 'RegistryDetailsPage',
components: {
@@ -157,17 +154,12 @@ export default {
});
if (data?.destroyContainerRepositoryTags?.errors[0]) {
- throw new Error(data.destroyContainerRepositoryTags.errors[0]);
+ throw new Error();
}
this.deleteAlertType =
itemsToBeDeleted.length === 0 ? ALERT_SUCCESS_TAG : ALERT_SUCCESS_TAGS;
} catch (e) {
- if (e.message === REPOSITORY_IMPORTING_ERROR_MESSAGE) {
- this.deleteAlertType = ALERT_DANGER_IMPORTING;
- } else {
- this.deleteAlertType =
- itemsToBeDeleted.length === 0 ? ALERT_DANGER_TAG : ALERT_DANGER_TAGS;
- }
+ this.deleteAlertType = itemsToBeDeleted.length === 0 ? ALERT_DANGER_TAG : ALERT_DANGER_TAGS;
}
this.mutationLoading = false;
@@ -203,7 +195,6 @@ export default {
<delete-alert
v-model="deleteAlertType"
:garbage-collection-help-page-path="config.garbageCollectionHelpPagePath"
- :container-registry-importing-help-page-path="config.containerRegistryImportingHelpPagePath"
:is-admin="config.isAdmin"
class="gl-my-2"
/>
diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue
index 794be8d5195..8a038d7c974 100644
--- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue
+++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue
@@ -11,9 +11,9 @@ import {
import { get } from 'lodash';
import getContainerRepositoriesQuery from 'shared_queries/container_registry/get_container_repositories.query.graphql';
import { createAlert } from '~/flash';
-import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import Tracking from '~/tracking';
import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import DeleteImage from '../components/delete_image.vue';
import RegistryHeader from '../components/list_page/registry_header.vue';
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/pages/details.vue b/app/assets/javascripts/packages_and_registries/harbor_registry/pages/details.vue
index c6ab746b9f4..bafcd78ad5d 100644
--- a/app/assets/javascripts/packages_and_registries/harbor_registry/pages/details.vue
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/pages/details.vue
@@ -8,7 +8,7 @@ import {
TOKEN_TYPE_TAG_NAME,
TAG_LABEL,
} from '~/packages_and_registries/harbor_registry/constants/index';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
+import { OPERATORS_IS } from '~/vue_shared/components/filtered_search_bar/constants';
import { createAlert } from '~/flash';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import TagsLoader from '~/packages_and_registries/shared/components/tags_loader.vue';
@@ -39,7 +39,7 @@ export default {
title: TAG_LABEL,
unique: true,
token: GlFilteredSearchToken,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
},
],
data() {
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/utils.js b/app/assets/javascripts/packages_and_registries/harbor_registry/utils.js
index 13df303cffe..2ae5957343b 100644
--- a/app/assets/javascripts/packages_and_registries/harbor_registry/utils.js
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/utils.js
@@ -3,8 +3,8 @@ import {
SORT_FIELD_MAPPING,
TOKEN_TYPE_TAG_NAME,
} from '~/packages_and_registries/harbor_registry/constants';
-import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import { normalizeHeaders, parseIntPagination } from '~/lib/utils/common_utils';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
export const extractSortingDetail = (parsedSorting = '') => {
const [orderBy, sortOrder] = parsedSorting.split('_');
diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list_app.vue b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list_app.vue
index 2adf6187c4b..0aeeb2c3d15 100644
--- a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list_app.vue
+++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list_app.vue
@@ -4,16 +4,14 @@ import { mapActions, mapState } from 'vuex';
import { createAlert, VARIANT_INFO } from '~/flash';
import { historyReplaceState } from '~/lib/utils/common_utils';
import { s__ } from '~/locale';
-import {
- SHOW_DELETE_SUCCESS_ALERT,
- FILTERED_SEARCH_TERM,
-} from '~/packages_and_registries/shared/constants';
+import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages_and_registries/shared/constants';
import { getQueryParams, extractFilterAndSorting } from '~/packages_and_registries/shared/utils';
import InfrastructureTitle from '~/packages_and_registries/infrastructure_registry/list/components/infrastructure_title.vue';
import InfrastructureSearch from '~/packages_and_registries/infrastructure_registry/list/components/infrastructure_search.vue';
import PackageList from '~/packages_and_registries/infrastructure_registry/list/components/packages_list.vue';
import { DELETE_PACKAGE_SUCCESS_MESSAGE } from '~/packages_and_registries/infrastructure_registry/list/constants';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
export default {
components: {
diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/actions.js b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/actions.js
index 37b51797490..7a452abdc26 100644
--- a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/actions.js
+++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/actions.js
@@ -2,6 +2,7 @@ import Api from '~/api';
import { createAlert, VARIANT_SUCCESS } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { DELETE_PACKAGE_ERROR_MESSAGE } from '~/packages_and_registries/shared/constants';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import {
FETCH_PACKAGES_LIST_ERROR_MESSAGE,
DELETE_PACKAGE_SUCCESS_MESSAGE,
@@ -31,7 +32,7 @@ export const requestPackagesList = ({ dispatch, state }, params = {}) => {
const type = state.config.forceTerraform
? TERRAFORM_SEARCH_TYPE
: state.filter.find((f) => f.type === 'type');
- const name = state.filter.find((f) => f.type === 'filtered-search-term');
+ const name = state.filter.find((f) => f.type === FILTERED_SEARCH_TERM);
const packageFilters = { package_type: type?.value?.data, package_name: name?.value?.data };
const apiMethod = state.config.isGroupPage ? 'groupPackages' : 'projectPackages';
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue
index 4553dd3421b..7ad1ebac11e 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue
@@ -54,6 +54,9 @@ export default {
},
},
computed: {
+ containsWebPathLink() {
+ return Boolean(this.packageEntity?._links?.webPath);
+ },
packageType() {
return getPackageTypeLabel(this.packageEntity.packageType);
},
@@ -109,6 +112,7 @@ export default {
<template #left-primary>
<div class="gl-display-flex gl-align-items-center gl-mr-3 gl-min-w-0">
<router-link
+ v-if="containsWebPathLink"
:class="errorPackageStyle"
class="gl-text-body gl-min-w-0"
data-testid="details-link"
@@ -118,6 +122,7 @@ export default {
>
<gl-truncate :text="packageEntity.name" />
</router-link>
+ <gl-truncate v-else :text="packageEntity.name" />
<package-tags
v-if="showTags"
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_search.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_search.vue
index d28847c7900..0cf49b25bf2 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_search.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_search.vue
@@ -1,14 +1,14 @@
<script>
-import { s__ } from '~/locale';
import { sortableFields } from '~/packages_and_registries/package_registry/utils';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
+import {
+ FILTERED_SEARCH_TERM,
+ OPERATORS_IS,
+ TOKEN_TITLE_TYPE,
+ TOKEN_TYPE_TYPE,
+} from '~/vue_shared/components/filtered_search_bar/constants';
import RegistrySearch from '~/vue_shared/components/registry/registry_search.vue';
import UrlSync from '~/vue_shared/components/url_sync.vue';
import { getQueryParams, extractFilterAndSorting } from '~/packages_and_registries/shared/utils';
-import {
- FILTERED_SEARCH_TERM,
- FILTERED_SEARCH_TYPE,
-} from '~/packages_and_registries/shared/constants';
import { LIST_KEY_CREATED_AT } from '~/packages_and_registries/package_registry/constants';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import PackageTypeToken from './tokens/package_type_token.vue';
@@ -16,12 +16,12 @@ import PackageTypeToken from './tokens/package_type_token.vue';
export default {
tokens: [
{
- type: 'type',
+ type: TOKEN_TYPE_TYPE,
icon: 'package',
- title: s__('PackageRegistry|Type'),
+ title: TOKEN_TITLE_TYPE,
unique: true,
token: PackageTypeToken,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
},
],
components: { RegistrySearch, UrlSync, LocalStorageSync },
@@ -51,7 +51,7 @@ export default {
};
return this.filters.reduce((acc, filter) => {
- if (filter.type === FILTERED_SEARCH_TYPE && filter.value?.data) {
+ if (filter.type === TOKEN_TYPE_TYPE && filter.value?.data) {
return {
...acc,
packageType: filter.value.data.toUpperCase(),
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/graphql/fragments/package_data.fragment.graphql b/app/assets/javascripts/packages_and_registries/package_registry/graphql/fragments/package_data.fragment.graphql
index b5695a01376..2d405f3e9cc 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/graphql/fragments/package_data.fragment.graphql
+++ b/app/assets/javascripts/packages_and_registries/package_registry/graphql/fragments/package_data.fragment.graphql
@@ -29,4 +29,7 @@ fragment PackageData on Package {
fullPath
webUrl
}
+ _links {
+ webPath
+ }
}
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql b/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql
index 51e0ab5aba8..9153906a38c 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql
+++ b/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql
@@ -62,6 +62,7 @@ query getPackageDetails(
}
}
versions(after: $after, before: $before, first: $first, last: $last) {
+ count
nodes {
id
name
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/pages/details.vue b/app/assets/javascripts/packages_and_registries/package_registry/pages/details.vue
index c59dcaee411..03352f01aca 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/pages/details.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/pages/details.vue
@@ -304,7 +304,7 @@ export default {
deleteFileModalContent: s__(
`PackageRegistry|You are about to delete %{filename}. This is a destructive action that may render your package unusable. Are you sure?`,
),
- otherVersionsTabTitle: __('Other versions'),
+ otherVersionsTabTitle: s__('PackageRegistry|Other versions'),
},
modal: {
packageDeletePrimaryAction: {
@@ -380,7 +380,9 @@ export default {
<gl-tab v-if="showDependencies">
<template #title>
<span>{{ __('Dependencies') }}</span>
- <gl-badge size="sm">{{ packageDependencies.length }}</gl-badge>
+ <gl-badge size="sm" data-testid="dependencies-badge">{{
+ packageDependencies.length
+ }}</gl-badge>
</template>
<template v-if="packageDependencies.length > 0">
@@ -392,7 +394,14 @@ export default {
</p>
</gl-tab>
- <gl-tab :title="$options.i18n.otherVersionsTabTitle" title-item-class="js-versions-tab" lazy>
+ <gl-tab title-item-class="js-versions-tab" lazy>
+ <template #title>
+ <span>{{ $options.i18n.otherVersionsTabTitle }}</span>
+ <gl-badge size="sm" class="gl-tab-counter-badge" data-testid="other-versions-badge">{{
+ packageEntity.versions.count
+ }}</gl-badge>
+ </template>
+
<package-versions-list
:is-loading="isLoading"
:page-info="versionPageInfo"
diff --git a/app/assets/javascripts/packages_and_registries/shared/components/package_path.vue b/app/assets/javascripts/packages_and_registries/shared/components/package_path.vue
index 6fb001e5e92..0a94f67ea5e 100644
--- a/app/assets/javascripts/packages_and_registries/shared/components/package_path.vue
+++ b/app/assets/javascripts/packages_and_registries/shared/components/package_path.vue
@@ -47,7 +47,7 @@ export default {
</script>
<template>
- <div data-qa-selector="package-path" class="gl-display-flex gl-align-items-center">
+ <div data-qa-selector="package_path" class="gl-display-flex gl-align-items-center">
<gl-icon data-testid="base-icon" name="project" class="gl-mx-3 gl-min-w-0" />
<gl-link
diff --git a/app/assets/javascripts/packages_and_registries/shared/constants/package_registry.js b/app/assets/javascripts/packages_and_registries/shared/constants/package_registry.js
index f3ce967b756..fe6e06ad830 100644
--- a/app/assets/javascripts/packages_and_registries/shared/constants/package_registry.js
+++ b/app/assets/javascripts/packages_and_registries/shared/constants/package_registry.js
@@ -1,7 +1,5 @@
import { s__ } from '~/locale';
-export const FILTERED_SEARCH_TERM = 'filtered-search-term';
-export const FILTERED_SEARCH_TYPE = 'type';
export const HISTORY_PIPELINES_LIMIT = 5;
export const DELETE_PACKAGE_TRACKING_ACTION = 'delete_package';
diff --git a/app/assets/javascripts/packages_and_registries/shared/utils.js b/app/assets/javascripts/packages_and_registries/shared/utils.js
index 7e963cd0b08..76623377d90 100644
--- a/app/assets/javascripts/packages_and_registries/shared/utils.js
+++ b/app/assets/javascripts/packages_and_registries/shared/utils.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import { queryToObject } from '~/lib/utils/url_utility';
-import { FILTERED_SEARCH_TERM } from './constants';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
export const getQueryParams = (query) =>
queryToObject(query, { gatherArrays: true, legacySpacesDecode: true });
diff --git a/app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue b/app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue
index b68148e5461..96477b9f476 100644
--- a/app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue
+++ b/app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue
@@ -43,7 +43,6 @@ export default {
'settingsPath',
'signupEnabled',
'requireAdminApprovalAfterUserSignup',
- 'sendUserConfirmationEmail',
'emailConfirmationSetting',
'minimumPasswordLength',
'minimumPasswordLengthMin',
@@ -68,7 +67,6 @@ export default {
form: {
signupEnabled: this.signupEnabled,
requireAdminApproval: this.requireAdminApprovalAfterUserSignup,
- sendConfirmationEmail: this.sendUserConfirmationEmail,
emailConfirmationSetting: this.emailConfirmationSetting,
minimumPasswordLength: this.minimumPasswordLength,
minimumPasswordLengthMin: this.minimumPasswordLengthMin,
@@ -204,7 +202,6 @@ export default {
buttonText: s__('ApplicationSettings|Save changes'),
signupEnabledLabel: s__('ApplicationSettings|Sign-up enabled'),
requireAdminApprovalLabel: s__('ApplicationSettings|Require admin approval for new sign-ups'),
- sendConfirmationEmailLabel: s__('ApplicationSettings|Send confirmation email on sign-up'),
emailConfirmationSettingsLabel: s__('ApplicationSettings|Email confirmation settings'),
emailConfirmationSettingsOffLabel: s__('ApplicationSettings|Off'),
emailConfirmationSettingsOffHelpText: s__(
@@ -284,13 +281,6 @@ export default {
data-testid="require-admin-approval-checkbox"
/>
- <signup-checkbox
- v-model="form.sendConfirmationEmail"
- class="gl-mb-5"
- name="application_setting[send_user_confirmation_email]"
- :label="$options.i18n.sendConfirmationEmailLabel"
- />
-
<gl-form-group :label="$options.i18n.emailConfirmationSettingsLabel">
<gl-form-radio-group
v-model="form.emailConfirmationSetting"
diff --git a/app/assets/javascripts/pages/admin/application_settings/signup_restrictions.js b/app/assets/javascripts/pages/admin/application_settings/signup_restrictions.js
index 0d5c55cb87b..395d8a38bf7 100644
--- a/app/assets/javascripts/pages/admin/application_settings/signup_restrictions.js
+++ b/app/assets/javascripts/pages/admin/application_settings/signup_restrictions.js
@@ -14,7 +14,6 @@ export default function initSignupRestrictions(elementSelector = '#js-signup-for
booleanAttributes: [
'signupEnabled',
'requireAdminApprovalAfterUserSignup',
- 'sendUserConfirmationEmail',
'domainDenylistEnabled',
'denylistTypeRawSelected',
'emailRestrictionsEnabled',
diff --git a/app/assets/javascripts/pages/admin/broadcast_messages/edit/index.js b/app/assets/javascripts/pages/admin/broadcast_messages/edit/index.js
new file mode 100644
index 00000000000..25036984082
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/broadcast_messages/edit/index.js
@@ -0,0 +1,8 @@
+import initEditBroadcastMessage from '~/admin/broadcast_messages/edit';
+import initBroadcastMessagesForm from '../broadcast_message';
+
+if (gon.features.vueBroadcastMessages) {
+ initEditBroadcastMessage();
+} else {
+ initBroadcastMessagesForm();
+}
diff --git a/app/assets/javascripts/pages/admin/broadcast_messages/index.js b/app/assets/javascripts/pages/admin/broadcast_messages/index.js
deleted file mode 100644
index ffd976be8c6..00000000000
--- a/app/assets/javascripts/pages/admin/broadcast_messages/index.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import initBroadcastMessages from '~/admin/broadcast_messages';
-import initDeprecatedRemoveRowBehavior from '~/behaviors/deprecated_remove_row_behavior';
-import initBroadcastMessagesForm from './broadcast_message';
-
-if (gon.features.vueBroadcastMessages) {
- initBroadcastMessages();
-} else {
- initBroadcastMessagesForm();
- initDeprecatedRemoveRowBehavior();
-}
diff --git a/app/assets/javascripts/pages/admin/broadcast_messages/index/index.js b/app/assets/javascripts/pages/admin/broadcast_messages/index/index.js
new file mode 100644
index 00000000000..1f37df2b340
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/broadcast_messages/index/index.js
@@ -0,0 +1,10 @@
+import initBroadcastMessages from '~/admin/broadcast_messages';
+import initDeprecatedRemoveRowBehavior from '~/behaviors/deprecated_remove_row_behavior';
+import initBroadcastMessagesForm from '../broadcast_message';
+
+if (gon.features.vueBroadcastMessages) {
+ initBroadcastMessages();
+} else {
+ initBroadcastMessagesForm();
+ initDeprecatedRemoveRowBehavior();
+}
diff --git a/app/assets/javascripts/pages/admin/dashboard/index.js b/app/assets/javascripts/pages/admin/dashboard/index.js
deleted file mode 100644
index b63e612be47..00000000000
--- a/app/assets/javascripts/pages/admin/dashboard/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import initGitlabVersionCheck from '~/gitlab_version_check';
-
-initGitlabVersionCheck();
diff --git a/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue b/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue
index b06c804f3ca..48241a213ef 100644
--- a/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue
+++ b/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue
@@ -1,6 +1,7 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml, GlModal } from '@gitlab/ui';
+import { GlModal } from '@gitlab/ui';
import { escape } from 'lodash';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { __, s__, sprintf } from '~/locale';
export default {
diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
index 2a7619da8cc..c5d62ae5daf 100644
--- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js
+++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
@@ -6,9 +6,7 @@ import { getProjects } from '~/api/projects_api';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
-import { isMetaClick } from '~/lib/utils/common_utils';
import { addDelimiter } from '~/lib/utils/text_utility';
-import { visitUrl } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import UsersSelect from '~/users_select';
@@ -34,10 +32,6 @@ export default class Todos {
document.querySelectorAll('.js-todos-mark-all, .js-todos-undo-all').forEach((el) => {
el.removeEventListener('click', this.updateallStateClickedWrapper);
});
- document.querySelectorAll('.todo').forEach((el) => {
- el.removeEventListener('click', this.goToTodoUrl);
- el.removeEventListener('auxclick', this.goToTodoUrl);
- });
}
bindEvents() {
@@ -50,10 +44,6 @@ export default class Todos {
document.querySelectorAll('.js-todos-mark-all, .js-todos-undo-all').forEach((el) => {
el.addEventListener('click', this.updateAllStateClickedWrapper);
});
- document.querySelectorAll('.todo').forEach((el) => {
- el.addEventListener('click', this.goToTodoUrl);
- el.addEventListener('auxclick', this.goToTodoUrl);
- });
}
initFilters() {
@@ -106,19 +96,22 @@ export default class Todos {
e.stopPropagation();
e.preventDefault();
- const { target } = e;
- target.setAttribute('disabled', true);
- target.classList.add('disabled');
+ let { currentTarget } = e;
+ if (currentTarget.tagName === 'svg' || currentTarget.tagName === 'use') {
+ currentTarget = currentTarget.closest('a');
+ }
+ currentTarget.setAttribute('disabled', true);
+ currentTarget.classList.add('disabled');
- target.querySelector('.gl-spinner-container').classList.add('gl-mr-2');
+ currentTarget.querySelector('.js-todo-button-icon').classList.add('hidden');
- axios[target.dataset.method](target.dataset.href)
+ axios[currentTarget.dataset.method](currentTarget.href)
.then(({ data }) => {
- this.updateRowState(target);
+ this.updateRowState(currentTarget);
this.updateBadges(data);
})
.catch(() => {
- this.updateRowState(target, true);
+ this.updateRowState(currentTarget, true);
return createAlert({
message: __('Error updating status of to-do item.'),
});
@@ -134,7 +127,7 @@ export default class Todos {
target.removeAttribute('disabled');
target.classList.remove('disabled');
- target.querySelector('.gl-spinner-container').classList.remove('gl-mr-2');
+ target.querySelector('.js-todo-button-icon').classList.remove('hidden');
if (isInactive === true) {
restoreBtn.classList.add('hidden');
@@ -209,25 +202,4 @@ export default class Todos {
data.done_count,
);
}
-
- goToTodoUrl(e) {
- const todoLink = this.dataset.url;
-
- if (!todoLink || e.target.closest('a')) {
- return;
- }
-
- e.stopPropagation();
- e.preventDefault();
-
- const isPrimaryClick = e.button === 0;
-
- if (isMetaClick(e)) {
- const windowTarget = '_blank';
-
- window.open(todoLink, windowTarget);
- } else if (isPrimaryClick) {
- visitUrl(todoLink);
- }
- }
}
diff --git a/app/assets/javascripts/pages/groups/merge_requests/index.js b/app/assets/javascripts/pages/groups/merge_requests/index.js
index 377ba0f13a9..bf0147ca885 100644
--- a/app/assets/javascripts/pages/groups/merge_requests/index.js
+++ b/app/assets/javascripts/pages/groups/merge_requests/index.js
@@ -1,11 +1,7 @@
import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra_tokens_for_merge_requests';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
-import {
- initBulkUpdateSidebar,
- initStatusDropdown,
- initSubscriptionsDropdown,
-} from '~/issuable/bulk_update_sidebar';
import { FILTERED_SEARCH } from '~/filtered_search/constants';
+import { initBulkUpdateSidebar } from '~/issuable';
import initFilteredSearch from '~/pages/search/init_filtered_search';
import projectSelect from '~/project_select';
@@ -13,8 +9,6 @@ const ISSUABLE_BULK_UPDATE_PREFIX = 'merge_request_';
addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys);
initBulkUpdateSidebar(ISSUABLE_BULK_UPDATE_PREFIX);
-initStatusDropdown();
-initSubscriptionsDropdown();
initFilteredSearch({
page: FILTERED_SEARCH.MERGE_REQUESTS,
diff --git a/app/assets/javascripts/pages/help/index/index.js b/app/assets/javascripts/pages/help/index/index.js
index a8e67c57307..da748223440 100644
--- a/app/assets/javascripts/pages/help/index/index.js
+++ b/app/assets/javascripts/pages/help/index/index.js
@@ -1,5 +1,3 @@
import docs from '~/docs/docs_bundle';
-import initGitlabVersionCheck from '~/gitlab_version_check';
docs();
-initGitlabVersionCheck();
diff --git a/app/assets/javascripts/pages/import/fogbugz/new_user_map/components/user_select.vue b/app/assets/javascripts/pages/import/fogbugz/new_user_map/components/user_select.vue
index 20ce296bbec..912b84dbae6 100644
--- a/app/assets/javascripts/pages/import/fogbugz/new_user_map/components/user_select.vue
+++ b/app/assets/javascripts/pages/import/fogbugz/new_user_map/components/user_select.vue
@@ -1,5 +1,5 @@
<script>
-import { GlAvatarLabeled, GlListbox } from '@gitlab/ui';
+import { GlAvatarLabeled, GlCollapsibleListbox } from '@gitlab/ui';
import { __ } from '~/locale';
import searchUsersQuery from '~/graphql_shared/queries/users_search_all.query.graphql';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
@@ -11,7 +11,7 @@ const USERS_PER_PAGE = 20;
export default {
components: {
GlAvatarLabeled,
- GlListbox,
+ GlCollapsibleListbox,
},
props: {
name: {
@@ -70,7 +70,7 @@ export default {
</script>
<template>
<div>
- <gl-listbox
+ <gl-collapsible-listbox
ref="listbox"
v-model="user"
:items="users"
@@ -89,7 +89,7 @@ export default {
:sub-label="item.username"
/>
</template>
- </gl-listbox>
+ </gl-collapsible-listbox>
<input type="hidden" :name="name" :value="userId" />
</div>
</template>
diff --git a/app/assets/javascripts/pages/import/gitlab_projects/new/index.js b/app/assets/javascripts/pages/import/gitlab_projects/new/index.js
index 870c14f99ae..d0560af5b3f 100644
--- a/app/assets/javascripts/pages/import/gitlab_projects/new/index.js
+++ b/app/assets/javascripts/pages/import/gitlab_projects/new/index.js
@@ -1,3 +1,5 @@
import initGitLabImportProject from '~/projects/project_import_gitlab_project';
+import { initNewProjectUrlSelect } from '~/projects/new';
+initNewProjectUrlSelect();
initGitLabImportProject();
diff --git a/app/assets/javascripts/pages/import/manifest/new/index.js b/app/assets/javascripts/pages/import/manifest/new/index.js
new file mode 100644
index 00000000000..0bb70a7364e
--- /dev/null
+++ b/app/assets/javascripts/pages/import/manifest/new/index.js
@@ -0,0 +1,3 @@
+import { initNewProjectUrlSelect } from '~/projects/new';
+
+initNewProjectUrlSelect();
diff --git a/app/assets/javascripts/pages/import/phabricator/new/index.js b/app/assets/javascripts/pages/import/phabricator/new/index.js
new file mode 100644
index 00000000000..0bb70a7364e
--- /dev/null
+++ b/app/assets/javascripts/pages/import/phabricator/new/index.js
@@ -0,0 +1,3 @@
+import { initNewProjectUrlSelect } from '~/projects/new';
+
+initNewProjectUrlSelect();
diff --git a/app/assets/javascripts/pages/projects/branches/new/index.js b/app/assets/javascripts/pages/projects/branches/new/index.js
index dbae89b5ade..f2b03468b0b 100644
--- a/app/assets/javascripts/pages/projects/branches/new/index.js
+++ b/app/assets/javascripts/pages/projects/branches/new/index.js
@@ -1,7 +1,6 @@
import NewBranchForm from '~/new_branch_form';
+import initNewBranchRefSelector from '~/branches/init_new_branch_ref_selector';
+initNewBranchRefSelector();
// eslint-disable-next-line no-new
-new NewBranchForm(
- document.querySelector('.js-create-branch-form'),
- JSON.parse(document.getElementById('availableRefs').innerHTML),
-);
+new NewBranchForm(document.querySelector('.js-create-branch-form'));
diff --git a/app/assets/javascripts/pages/projects/ci/lints/show/index.js b/app/assets/javascripts/pages/projects/ci/lints/show/index.js
index 6e1cdf557b5..caac76fc6d7 100644
--- a/app/assets/javascripts/pages/projects/ci/lints/show/index.js
+++ b/app/assets/javascripts/pages/projects/ci/lints/show/index.js
@@ -1,3 +1,3 @@
-import initCiLint from '~/ci_lint';
+import initCiLint from '~/ci/ci_lint';
initCiLint();
diff --git a/app/assets/javascripts/pages/projects/ci/pipeline_editor/show/index.js b/app/assets/javascripts/pages/projects/ci/pipeline_editor/show/index.js
index 67d32648ce8..7e91f23dd7f 100644
--- a/app/assets/javascripts/pages/projects/ci/pipeline_editor/show/index.js
+++ b/app/assets/javascripts/pages/projects/ci/pipeline_editor/show/index.js
@@ -1,3 +1,3 @@
-import { initPipelineEditor } from '~/pipeline_editor';
+import { initPipelineEditor } from '~/ci/pipeline_editor';
initPipelineEditor();
diff --git a/app/assets/javascripts/pages/projects/commits/show/index.js b/app/assets/javascripts/pages/projects/commits/show/index.js
index ee74628a994..f5ecf9be591 100644
--- a/app/assets/javascripts/pages/projects/commits/show/index.js
+++ b/app/assets/javascripts/pages/projects/commits/show/index.js
@@ -1,9 +1,10 @@
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import CommitsList from '~/commits';
import GpgBadges from '~/gpg_badges';
-import mountCommits from '~/projects/commits';
+import { mountCommits, initCommitsRefSwitcher } from '~/projects/commits';
new CommitsList(document.querySelector('.js-project-commits-show').dataset.commitsLimit); // eslint-disable-line no-new
new ShortcutsNavigation(); // eslint-disable-line no-new
GpgBadges.fetch();
mountCommits(document.getElementById('js-author-dropdown'));
+initCommitsRefSwitcher();
diff --git a/app/assets/javascripts/pages/projects/cycle_analytics/show/index.js b/app/assets/javascripts/pages/projects/cycle_analytics/show/index.js
index bef21ef8fdf..05a1bbc69ed 100644
--- a/app/assets/javascripts/pages/projects/cycle_analytics/show/index.js
+++ b/app/assets/javascripts/pages/projects/cycle_analytics/show/index.js
@@ -1,3 +1,3 @@
-import initCycleAnalytics from '~/cycle_analytics';
+import initCycleAnalytics from '~/analytics/cycle_analytics';
initCycleAnalytics();
diff --git a/app/assets/javascripts/pages/projects/environments/show/index.js b/app/assets/javascripts/pages/projects/environments/show/index.js
index 53e48ad8d86..1ce8899ac63 100644
--- a/app/assets/javascripts/pages/projects/environments/show/index.js
+++ b/app/assets/javascripts/pages/projects/environments/show/index.js
@@ -1,5 +1,6 @@
import initConfirmRollBackModal from '~/environments/init_confirm_rollback_modal';
-import { initHeader } from '~/environments/mount_show';
+import { initHeader, initPage } from '~/environments/mount_show';
initHeader();
+initPage();
initConfirmRollBackModal();
diff --git a/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue b/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
index 30cefa3d717..91650003d4a 100644
--- a/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
+++ b/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
@@ -23,6 +23,7 @@ import {
VISIBILITY_LEVEL_INTERNAL_STRING,
VISIBILITY_LEVEL_PUBLIC_STRING,
VISIBILITY_LEVELS_STRING_TO_INTEGER,
+ VISIBILITY_LEVELS_INTEGER_TO_STRING,
} from '~/visibility_level/constants';
import ProjectNamespace from './project_namespace.vue';
@@ -105,39 +106,8 @@ export default {
};
},
computed: {
- projectVisibilityLevel() {
- return VISIBILITY_LEVELS_STRING_TO_INTEGER[this.projectVisibility];
- },
- namespaceVisibilityLevel() {
- const visibility =
- this.form.fields.namespace.value?.visibility || VISIBILITY_LEVEL_PUBLIC_STRING;
- return VISIBILITY_LEVELS_STRING_TO_INTEGER[visibility];
- },
- visibilityLevelCap() {
- return Math.min(this.projectVisibilityLevel, this.namespaceVisibilityLevel);
- },
- restrictedVisibilityLevelsSet() {
- return new Set(this.restrictedVisibilityLevels);
- },
allowedVisibilityLevels() {
- const allowedLevels = Object.entries(VISIBILITY_LEVELS_STRING_TO_INTEGER).reduce(
- (levels, [levelName, levelValue]) => {
- if (
- !this.restrictedVisibilityLevelsSet.has(levelValue) &&
- levelValue <= this.visibilityLevelCap
- ) {
- levels.push(levelName);
- }
- return levels;
- },
- [],
- );
-
- if (!allowedLevels.length) {
- return [VISIBILITY_LEVEL_PRIVATE_STRING];
- }
-
- return allowedLevels;
+ return this.getAllowedVisibilityLevels();
},
visibilityLevels() {
return [
@@ -178,13 +148,60 @@ export default {
return !this.allowedVisibilityLevels.includes(visibility);
},
getInitialVisibilityValue() {
- return this.restrictedVisibilityLevels.length !== 0 ? null : this.projectVisibility;
+ return this.getMaximumAllowedVisibilityLevel(this.projectVisibility);
},
setNamespace(namespace) {
- this.form.fields.visibility.value =
- this.restrictedVisibilityLevels.length !== 0 ? null : VISIBILITY_LEVEL_PRIVATE_STRING;
this.form.fields.namespace.value = namespace;
this.form.fields.namespace.state = true;
+ this.form.fields.visibility.value = this.getMaximumAllowedVisibilityLevel(
+ this.form.fields.visibility.value,
+ );
+ },
+ getProjectVisibilityLevel() {
+ return VISIBILITY_LEVELS_STRING_TO_INTEGER[this.projectVisibility];
+ },
+ getNamespaceVisibilityLevel() {
+ const visibility =
+ this.form?.fields?.namespace?.value?.visibility || VISIBILITY_LEVEL_PUBLIC_STRING;
+ return VISIBILITY_LEVELS_STRING_TO_INTEGER[visibility];
+ },
+ getVisibilityLevelCap() {
+ return Math.min(this.getProjectVisibilityLevel(), this.getNamespaceVisibilityLevel());
+ },
+ getRestrictedVisibilityLevelsSet() {
+ return new Set(this.restrictedVisibilityLevels);
+ },
+ getAllowedVisibilityLevels() {
+ const allowedLevels = Object.entries(VISIBILITY_LEVELS_STRING_TO_INTEGER).reduce(
+ (levels, [levelName, levelValue]) => {
+ if (
+ !this.getRestrictedVisibilityLevelsSet().has(levelValue) &&
+ levelValue <= this.getVisibilityLevelCap()
+ ) {
+ levels.push(levelName);
+ }
+ return levels;
+ },
+ [],
+ );
+
+ if (!allowedLevels.length) {
+ return [VISIBILITY_LEVEL_PRIVATE_STRING];
+ }
+
+ return allowedLevels;
+ },
+ getMaximumAllowedVisibilityLevel(visibility) {
+ const allowedVisibilities = this.getAllowedVisibilityLevels().map(
+ (s) => VISIBILITY_LEVELS_STRING_TO_INTEGER[s],
+ );
+ const current = VISIBILITY_LEVELS_STRING_TO_INTEGER[visibility];
+ const lower = allowedVisibilities.filter((l) => l <= current);
+ if (lower.length) {
+ return VISIBILITY_LEVELS_INTEGER_TO_STRING[Math.max(...lower)];
+ }
+ const higher = allowedVisibilities.filter((l) => l >= current);
+ return VISIBILITY_LEVELS_INTEGER_TO_STRING[Math.min(...higher)];
},
async onSubmit() {
this.form.showValidation = true;
diff --git a/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue b/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue
index 08d24344ffc..10bfcdc2294 100644
--- a/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue
+++ b/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue
@@ -1,5 +1,5 @@
<script>
-import { GlAlert, GlButton, GlDropdown, GlDropdownItem, GlSprintf } from '@gitlab/ui';
+import { GlAlert, GlButton, GlListbox, GlSprintf } from '@gitlab/ui';
import { GlAreaChart } from '@gitlab/ui/dist/charts';
import { get } from 'lodash';
import { formatDate } from '~/lib/utils/datetime_utility';
@@ -12,8 +12,7 @@ export default {
GlAlert,
GlAreaChart,
GlButton,
- GlDropdown,
- GlDropdownItem,
+ GlListbox,
GlSprintf,
},
props: {
@@ -96,6 +95,14 @@ export default {
formattedData() {
return this.sortedData.map((value) => [value.date, value.coverage]);
},
+ mappedCoverages() {
+ return this.dailyCoverageData?.map((item, index) => ({
+ // A numerical index makes an item into a group header, so
+ // convert these to strings to get non-header GlListbox items
+ value: index.toString(),
+ text: item.group_name,
+ }));
+ },
chartData() {
return [
{
@@ -175,18 +182,13 @@ export default {
{{ __('It seems that there is currently no available data for code coverage') }}
</span>
</gl-alert>
- <gl-dropdown v-if="canShowData" :text="selectedDailyCoverageName">
- <gl-dropdown-item
- v-for="({ group_name }, index) in dailyCoverageData"
- :key="index"
- :value="group_name"
- is-check-item
- :is-checked="index === selectedCoverageIndex"
- @click="setSelectedCoverage(index)"
- >
- {{ group_name }}
- </gl-dropdown-item>
- </gl-dropdown>
+ <gl-listbox
+ v-if="canShowData"
+ :items="mappedCoverages"
+ :selected="selectedCoverageIndex.toString()"
+ :toggle-text="selectedDailyCoverageName"
+ @select="setSelectedCoverage"
+ />
</div>
<gl-area-chart
v-if="!isLoading"
diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/new/compare.js b/app/assets/javascripts/pages/projects/merge_requests/creations/new/compare.js
index 2d26d3922bf..653f903c6d1 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/creations/new/compare.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/creations/new/compare.js
@@ -26,7 +26,10 @@ const updateCommitList = (url, $emptyState, $loadingIndicator, $commitList, para
export default (mrNewCompareNode) => {
const { sourceBranchUrl, targetBranchUrl } = mrNewCompareNode.dataset;
- initTargetProjectDropdown();
+
+ if (!window.gon?.features?.mrCompareDropdowns) {
+ initTargetProjectDropdown();
+ }
const updateSourceBranchCommitList = () =>
updateCommitList(
diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js b/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js
index 9aecd154483..b3868653d6a 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js
@@ -1,10 +1,37 @@
+import $ from 'jquery';
+import Vue from 'vue';
import initPipelines from '~/commit/pipelines/pipelines_bundle';
import MergeRequest from '~/merge_request';
+import TargetProjectDropdown from '~/merge_requests/components/target_project_dropdown.vue';
import initCompare from './compare';
const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare');
if (mrNewCompareNode) {
initCompare(mrNewCompareNode);
+
+ const el = document.getElementById('js-target-project-dropdown');
+ const { targetProjectsPath, currentProject } = el.dataset;
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el,
+ name: 'TargetProjectDropdown',
+ provide: {
+ targetProjectsPath,
+ currentProject: JSON.parse(currentProject),
+ },
+ render(h) {
+ return h(TargetProjectDropdown, {
+ on: {
+ 'project-selected': function projectSelectedFunction(refsUrl) {
+ const $targetBranchDropdown = $('.js-target-branch');
+ $targetBranchDropdown.data('refsUrl', refsUrl);
+ $targetBranchDropdown.data('deprecatedJQueryDropdown').clearMenu();
+ },
+ },
+ });
+ },
+ });
} else {
const mrNewSubmitNode = document.querySelector('.js-merge-request-new-submit');
// eslint-disable-next-line no-new
diff --git a/app/assets/javascripts/pages/projects/merge_requests/diffs/index.js b/app/assets/javascripts/pages/projects/merge_requests/diffs/index.js
new file mode 100644
index 00000000000..77294c0fb9e
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/merge_requests/diffs/index.js
@@ -0,0 +1,5 @@
+import initDiffsApp from '~/diffs';
+import { initMrPage } from '../page';
+
+initMrPage();
+initDiffsApp();
diff --git a/app/assets/javascripts/pages/projects/merge_requests/index/index.js b/app/assets/javascripts/pages/projects/merge_requests/index/index.js
index 2399aafc9b5..b3a09cc0be3 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/index/index.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/index/index.js
@@ -1,20 +1,13 @@
import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra_tokens_for_merge_requests';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
-import { initCsvImportExportButtons, initIssuableByEmail } from '~/issuable';
-import {
- initBulkUpdateSidebar,
- initStatusDropdown,
- initSubscriptionsDropdown,
-} from '~/issuable/bulk_update_sidebar';
import { FILTERED_SEARCH } from '~/filtered_search/constants';
+import { initBulkUpdateSidebar, initCsvImportExportButtons, initIssuableByEmail } from '~/issuable';
import { ISSUABLE_INDEX } from '~/issuable/constants';
import initFilteredSearch from '~/pages/search/init_filtered_search';
import UsersSelect from '~/users_select';
initBulkUpdateSidebar(ISSUABLE_INDEX.MERGE_REQUEST);
-initStatusDropdown();
-initSubscriptionsDropdown();
addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys);
IssuableFilteredSearchTokenKeys.removeTokensForKeys('iteration');
diff --git a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js
index 42fa306d226..a4e3ddfc506 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js
@@ -2,6 +2,7 @@
import $ from 'jquery';
import IssuableForm from 'ee_else_ce/issuable/issuable_form';
+import IssuableLabelSelector from '~/issuable/issuable_label_selector';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import Diff from '~/diff';
import GLForm from '~/gl_form';
@@ -14,6 +15,7 @@ export default () => {
new ShortcutsNavigation();
new GLForm($('.merge-request-form'));
new IssuableForm($('.merge-request-form'));
+ IssuableLabelSelector();
new LabelsSelect();
new IssuableTemplateSelectors({
warnTemplateOverride: true,
diff --git a/app/assets/javascripts/pages/projects/merge_requests/page.js b/app/assets/javascripts/pages/projects/merge_requests/page.js
new file mode 100644
index 00000000000..a8699b350f8
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/merge_requests/page.js
@@ -0,0 +1,45 @@
+import Vue from 'vue';
+import StickyHeader from '~/merge_requests/components/sticky_header.vue';
+import { initIssuableHeaderWarnings } from '~/issuable';
+import initMrNotes from '~/mr_notes';
+import store from '~/mr_notes/stores';
+import initSidebarBundle from '~/sidebar/sidebar_bundle';
+import { apolloProvider } from '~/graphql_shared/issuable_client';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import initShow from './init_merge_request_show';
+import getStateQuery from './queries/get_state.query.graphql';
+
+export function initMrPage() {
+ initMrNotes();
+ initShow();
+}
+
+requestIdleCallback(() => {
+ initSidebarBundle(store);
+ initIssuableHeaderWarnings(store);
+
+ const el = document.getElementById('js-merge-sticky-header');
+
+ if (el) {
+ const { data } = el.dataset;
+ const { iid, projectPath, title, tabs, isFluidLayout } = JSON.parse(data);
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el,
+ store,
+ apolloProvider,
+ provide: {
+ query: getStateQuery,
+ iid,
+ projectPath,
+ title,
+ tabs,
+ isFluidLayout: parseBoolean(isFluidLayout),
+ },
+ render(h) {
+ return h(StickyHeader);
+ },
+ });
+ }
+});
diff --git a/app/assets/javascripts/pages/projects/merge_requests/show/index.js b/app/assets/javascripts/pages/projects/merge_requests/show/index.js
index cc5c393ff8c..568bf19b55e 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/show/index.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/show/index.js
@@ -1,45 +1,5 @@
-import Vue from 'vue';
-import StickyHeader from '~/merge_requests/components/sticky_header.vue';
-import { initReviewBar } from '~/batch_comments';
-import { initIssuableHeaderWarnings } from '~/issuable';
-import initMrNotes from '~/mr_notes';
-import store from '~/mr_notes/stores';
-import initSidebarBundle from '~/sidebar/sidebar_bundle';
-import { apolloProvider } from '~/graphql_shared/issuable_client';
-import { parseBoolean } from '~/lib/utils/common_utils';
-import initShow from '../init_merge_request_show';
-import getStateQuery from '../queries/get_state.query.graphql';
+import initNotesApp from '~/mr_notes/init_notes';
+import { initMrPage } from '../page';
-initMrNotes();
-initShow();
-
-requestIdleCallback(() => {
- initSidebarBundle(store);
- initReviewBar();
- initIssuableHeaderWarnings(store);
-
- const el = document.getElementById('js-merge-sticky-header');
-
- if (el) {
- const { data } = el.dataset;
- const { iid, projectPath, title, tabs, isFluidLayout } = JSON.parse(data);
-
- // eslint-disable-next-line no-new
- new Vue({
- el,
- store,
- apolloProvider,
- provide: {
- query: getStateQuery,
- iid,
- projectPath,
- title,
- tabs,
- isFluidLayout: parseBoolean(isFluidLayout),
- },
- render(h) {
- return h(StickyHeader);
- },
- });
- }
-});
+initMrPage();
+initNotesApp();
diff --git a/app/assets/javascripts/pages/projects/ml/candidates/show/index.js b/app/assets/javascripts/pages/projects/ml/candidates/show/index.js
new file mode 100644
index 00000000000..c1acef5ac13
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/ml/candidates/show/index.js
@@ -0,0 +1,27 @@
+import Vue from 'vue';
+import MlCandidate from '~/ml/experiment_tracking/components/ml_candidate.vue';
+
+const initShowCandidate = () => {
+ const element = document.querySelector('#js-show-ml-candidate');
+ if (!element) {
+ return;
+ }
+
+ const container = document.createElement('div');
+ element.appendChild(container);
+
+ const candidate = JSON.parse(element.dataset.candidate);
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: container,
+ provide: {
+ candidate,
+ },
+ render(h) {
+ return h(MlCandidate);
+ },
+ });
+};
+
+initShowCandidate();
diff --git a/app/assets/javascripts/pages/projects/ml/experiments/show/index.js b/app/assets/javascripts/pages/projects/ml/experiments/show/index.js
index 0a9d9f4c987..97e436920c7 100644
--- a/app/assets/javascripts/pages/projects/ml/experiments/show/index.js
+++ b/app/assets/javascripts/pages/projects/ml/experiments/show/index.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import ShowExperiment from '~/ml/experiment_tracking/components/experiment.vue';
+import MlExperiment from '~/ml/experiment_tracking/components/ml_experiment.vue';
const initShowExperiment = () => {
const element = document.querySelector('#js-show-ml-experiment');
@@ -23,7 +23,7 @@ const initShowExperiment = () => {
paramNames,
},
render(h) {
- return h(ShowExperiment);
+ return h(MlExperiment);
},
});
};
diff --git a/app/assets/javascripts/pages/projects/new/index.js b/app/assets/javascripts/pages/projects/new/index.js
index 50733d8a145..d022428df98 100644
--- a/app/assets/javascripts/pages/projects/new/index.js
+++ b/app/assets/javascripts/pages/projects/new/index.js
@@ -4,10 +4,8 @@ import {
initDeploymentTargetSelect,
} from '~/projects/new';
import initProjectVisibilitySelector from '~/projects/project_visibility';
-import initProjectNew from '~/projects/project_new';
initProjectVisibilitySelector();
-initProjectNew.bindEvents();
initNewProjectCreation();
initNewProjectUrlSelect();
initDeploymentTargetSelect();
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
index 85443843684..fd8b1a6290f 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
@@ -39,6 +39,11 @@ export default {
required: false,
default: '',
},
+ sendNativeErrors: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
data() {
return {
@@ -114,9 +119,11 @@ export default {
cronInterval() {
// updates field validation state when model changes, as
// glFieldError only updates on input.
- this.$nextTick(() => {
- gl.pipelineScheduleFieldErrors.updateFormValidityState();
- });
+ if (this.sendNativeErrors) {
+ this.$nextTick(() => {
+ gl.pipelineScheduleFieldErrors.updateFormValidityState();
+ });
+ }
},
radioValue: {
immediate: true,
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js
deleted file mode 100644
index bc467952551..00000000000
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js
+++ /dev/null
@@ -1,82 +0,0 @@
-import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
-import { formatTimezone } from '~/lib/utils/datetime_utility';
-
-const defaultTimezone = { identifier: 'Etc/UTC', name: 'UTC', offset: 0 };
-const defaults = {
- $inputEl: null,
- $dropdownEl: null,
- onSelectTimezone: null,
- displayFormat: (item) => item.name,
-};
-
-export const formatUtcOffset = (offset) => {
- const parsed = parseInt(offset, 10);
- if (Number.isNaN(parsed) || parsed === 0) {
- return `0`;
- }
- const prefix = offset > 0 ? '+' : '-';
- return `${prefix} ${Math.abs(offset / 3600)}`;
-};
-
-export const findTimezoneByIdentifier = (tzList = [], identifier = null) => {
- if (tzList && tzList.length && identifier && identifier.length) {
- return tzList.find((tz) => tz.identifier === identifier) || null;
- }
- return null;
-};
-
-export default class TimezoneDropdown {
- constructor({
- $dropdownEl,
- $inputEl,
- onSelectTimezone,
- displayFormat,
- allowEmpty = false,
- } = defaults) {
- this.$dropdown = $dropdownEl;
- this.$dropdownToggle = this.$dropdown.find('.dropdown-toggle-text');
- this.$input = $inputEl;
- this.timezoneData = this.$dropdown.data('data') || [];
-
- this.onSelectTimezone = onSelectTimezone;
- this.displayFormat = displayFormat || defaults.displayFormat;
- this.allowEmpty = allowEmpty;
-
- this.initDropdown();
- }
-
- initDropdown() {
- initDeprecatedJQueryDropdown(this.$dropdown, {
- data: this.timezoneData,
- filterable: true,
- selectable: true,
- toggleLabel: this.displayFormat,
- search: {
- fields: ['name'],
- },
- clicked: (cfg) => this.handleDropdownChange(cfg),
- text: (item) => formatTimezone(item),
- });
-
- const initialTimezone = findTimezoneByIdentifier(this.timezoneData, this.$input.val());
-
- if (initialTimezone !== null) {
- this.setDropdownValue(initialTimezone);
- } else if (!this.allowEmpty) {
- this.setDropdownValue(defaultTimezone);
- }
- }
-
- setDropdownValue(timezone) {
- this.$dropdownToggle.text(this.displayFormat(timezone));
- this.$input.val(timezone.identifier);
- }
-
- handleDropdownChange({ selectedObj, e }) {
- e.preventDefault();
- this.$input.val(selectedObj.identifier);
- if (this.onSelectTimezone) {
- this.onSelectTimezone({ selectedObj, e });
- }
- }
-}
diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js
index d177c67f133..4c9eb830ff6 100644
--- a/app/assets/javascripts/pages/projects/project.js
+++ b/app/assets/javascripts/pages/projects/project.js
@@ -11,10 +11,14 @@ import { mergeUrlParams } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import projectSelect from '~/project_select';
+const BRANCH_REF_TYPE = 'heads';
+const TAG_REF_TYPE = 'tags';
+const BRANCH_GROUP_NAME = __('Branches');
+const TAG_GROUP_NAME = __('Tags');
+
export default class Project {
constructor() {
initClonePanel();
-
// Ref switcher
if (document.querySelector('.js-project-refs-dropdown')) {
Project.initRefSwitcher();
@@ -62,6 +66,7 @@ export default class Project {
return $('.js-project-refs-dropdown').each(function () {
const $dropdown = $(this);
const selected = $dropdown.data('selected');
+ const refType = $dropdown.data('refType');
const fieldName = $dropdown.data('fieldName');
const shouldVisit = Boolean($dropdown.data('visit'));
const $form = $dropdown.closest('form');
@@ -91,18 +96,32 @@ export default class Project {
filterByText: true,
inputFieldName: $dropdown.data('inputFieldName'),
fieldName,
- renderRow(ref) {
+ renderRow(ref, _, params) {
const li = refListItem.cloneNode(false);
const link = refLink.cloneNode(false);
if (ref === selected) {
- link.className = 'is-active';
+ // Check group and current ref type to avoid adding a class when tags and branches share the same name
+ if (
+ (refType === BRANCH_REF_TYPE && params.group === BRANCH_GROUP_NAME) ||
+ (refType === TAG_REF_TYPE && params.group === TAG_GROUP_NAME) ||
+ !refType
+ ) {
+ link.className = 'is-active';
+ }
}
+
link.textContent = ref;
link.dataset.ref = ref;
if (ref.length > 0 && shouldVisit) {
- link.href = mergeUrlParams({ [fieldName]: ref }, linkTarget);
+ const urlParams = { [fieldName]: ref };
+ if (params.group === BRANCH_GROUP_NAME) {
+ urlParams.ref_type = BRANCH_REF_TYPE;
+ } else {
+ urlParams.ref_type = TAG_REF_TYPE;
+ }
+ link.href = mergeUrlParams(urlParams, linkTarget);
}
li.appendChild(link);
diff --git a/app/assets/javascripts/pages/projects/settings/merge_requests/index.js b/app/assets/javascripts/pages/projects/settings/merge_requests/index.js
index 739e666644c..0f7ede8ed42 100644
--- a/app/assets/javascripts/pages/projects/settings/merge_requests/index.js
+++ b/app/assets/javascripts/pages/projects/settings/merge_requests/index.js
@@ -1,9 +1,6 @@
import groupsSelect from '~/groups_select';
import UserCallout from '~/user_callout';
-import UsersSelect from '~/users_select';
-// eslint-disable-next-line no-new
-new UsersSelect();
groupsSelect();
// eslint-disable-next-line no-new
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
index c37b4cc643a..5fa3288bbef 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
@@ -23,6 +23,12 @@ import ProjectSettingRow from './project_setting_row.vue';
const FEATURE_ACCESS_LEVEL_ANONYMOUS = [30, s__('ProjectSettings|Everyone')];
+const PACKAGE_REGISTRY_ACCESS_LEVEL_DEFAULT_BY_PROJECT_VISIBILITY = {
+ [VISIBILITY_LEVEL_PRIVATE_INTEGER]: featureAccessLevel.PROJECT_MEMBERS,
+ [VISIBILITY_LEVEL_INTERNAL_INTEGER]: featureAccessLevel.EVERYONE,
+ [VISIBILITY_LEVEL_PUBLIC_INTEGER]: FEATURE_ACCESS_LEVEL_ANONYMOUS[0],
+};
+
export default {
i18n: {
...CVE_ID_REQUEST_BUTTON_I18N,
@@ -32,7 +38,6 @@ export default {
issuesLabel: s__('ProjectSettings|Issues'),
lfsLabel: s__('ProjectSettings|Git Large File Storage (LFS)'),
mergeRequestsLabel: s__('ProjectSettings|Merge requests'),
- operationsLabel: s__('ProjectSettings|Operations'),
environmentsLabel: s__('ProjectSettings|Environments'),
environmentsHelpText: s__(
'ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access.',
@@ -47,11 +52,15 @@ export default {
packagesHelpText: s__(
'ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public.',
),
- packageRegistryHelpText: s__(
- 'ProjectSettings|Every project can have its own space to store its packages.',
+ packageRegistryHelpText: s__('ProjectSettings|Publish, store, and view packages in a project.'),
+ packageRegistryForEveryoneHelpText: s__(
+ 'ProjectSettings|Anyone can pull packages with a package manager API.',
),
packagesLabel: s__('ProjectSettings|Packages'),
packageRegistryLabel: s__('ProjectSettings|Package registry'),
+ packageRegistryForEveryoneLabel: s__(
+ 'ProjectSettings|Allow anyone to pull from Package Registry',
+ ),
pagesLabel: s__('ProjectSettings|Pages'),
ciCdLabel: __('CI/CD'),
repositoryLabel: s__('ProjectSettings|Repository'),
@@ -249,7 +258,6 @@ export default {
analyticsAccessLevel: featureAccessLevel.EVERYONE,
requirementsAccessLevel: featureAccessLevel.EVERYONE,
securityAndComplianceAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
- operationsAccessLevel: featureAccessLevel.EVERYONE,
environmentsAccessLevel: featureAccessLevel.EVERYONE,
featureFlagsAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
infrastructureAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
@@ -287,18 +295,6 @@ export default {
);
},
- packageRegistryFeatureAccessLevelOptions() {
- const options = [FEATURE_ACCESS_LEVEL_ANONYMOUS];
-
- if (this.visibilityLevel === VISIBILITY_LEVEL_PRIVATE_INTEGER) {
- options.unshift(featureAccessLevelMembers);
- } else if (this.visibilityLevel === VISIBILITY_LEVEL_INTERNAL_INTEGER) {
- options.unshift(featureAccessLevelEveryone);
- }
-
- return options;
- },
-
pagesFeatureAccessLevelOptions() {
const options = [featureAccessLevelMembers];
@@ -318,10 +314,6 @@ export default {
return options;
},
- operationsEnabled() {
- return this.operationsAccessLevel > featureAccessLevel.NOT_ENABLED;
- },
-
environmentsEnabled() {
return this.environmentsAccessLevel > featureAccessLevel.NOT_ENABLED;
},
@@ -351,7 +343,7 @@ export default {
}
return s__(
- 'ProjectSettings|View and edit files in this project. Non-project members have only read access.',
+ 'ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access.',
);
},
cveIdRequestIsDisabled() {
@@ -366,16 +358,17 @@ export default {
packageRegistryAccessLevelEnabled() {
return this.glFeatures.packageRegistryAccessLevel;
},
- splitOperationsEnabled() {
- return this.glFeatures.splitOperationsVisibilityPermissions;
+ packageRegistryEnabled() {
+ return this.packageRegistryAccessLevel > featureAccessLevel.NOT_ENABLED;
+ },
+ packageRegistryApiForEveryoneEnabled() {
+ return this.packageRegistryAccessLevel === FEATURE_ACCESS_LEVEL_ANONYMOUS[0];
+ },
+ packageRegistryApiForEveryoneEnabledShown() {
+ return this.visibilityLevel !== VISIBILITY_LEVEL_PUBLIC_INTEGER;
},
monitorOperationsFeatureAccessLevelOptions() {
- if (this.splitOperationsEnabled) {
- return this.featureAccessLevelOptions.filter(([value]) => value <= this.monitorAccessLevel);
- }
- return this.featureAccessLevelOptions.filter(
- ([value]) => value <= this.operationsAccessLevel,
- );
+ return this.featureAccessLevelOptions.filter(([value]) => value <= this.monitorAccessLevel);
},
},
@@ -429,10 +422,6 @@ export default {
featureAccessLevel.PROJECT_MEMBERS,
this.securityAndComplianceAccessLevel,
);
- this.operationsAccessLevel = Math.min(
- featureAccessLevel.PROJECT_MEMBERS,
- this.operationsAccessLevel,
- );
this.environmentsAccessLevel = Math.min(
featureAccessLevel.PROJECT_MEMBERS,
this.environmentsAccessLevel,
@@ -474,9 +463,8 @@ export default {
this.packageRegistryAccessLevelEnabled &&
this.packageRegistryAccessLevel === featureAccessLevel.PROJECT_MEMBERS
) {
- this.packageRegistryAccessLevel = Math.min(
- ...this.packageRegistryFeatureAccessLevelOptions.map((option) => option[0]),
- );
+ this.packageRegistryAccessLevel =
+ PACKAGE_REGISTRY_ACCESS_LEVEL_DEFAULT_BY_PROJECT_VISIBILITY[value];
}
if (this.buildsAccessLevel > featureAccessLevel.NOT_ENABLED)
this.buildsAccessLevel = featureAccessLevel.EVERYONE;
@@ -492,8 +480,6 @@ export default {
this.metricsDashboardAccessLevel = featureAccessLevel.EVERYONE;
if (this.requirementsAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
this.requirementsAccessLevel = featureAccessLevel.EVERYONE;
- if (this.operationsAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
- this.operationsAccessLevel = featureAccessLevel.EVERYONE;
if (this.environmentsAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
this.environmentsAccessLevel = featureAccessLevel.EVERYONE;
if (this.monitorAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
@@ -532,10 +518,6 @@ export default {
toggleHiddenClassBySelector('.merge-requests-feature', false);
},
- operationsAccessLevel(value, oldValue) {
- this.updateSubFeatureAccessLevel(value, oldValue);
- },
-
monitorAccessLevel(value, oldValue) {
this.updateSubFeatureAccessLevel(value, oldValue);
},
@@ -561,6 +543,22 @@ export default {
visibilityAllowed(option) {
return this.allowedVisibilityOptions.includes(option);
},
+ onPackageRegistryEnabledToggle(value) {
+ this.packageRegistryAccessLevel = value
+ ? this.packageRegistryAccessLevelDefault()
+ : featureAccessLevel.NOT_ENABLED;
+ },
+ onPackageRegistryApiForEveryoneEnabledToggle(value) {
+ this.packageRegistryAccessLevel = value
+ ? FEATURE_ACCESS_LEVEL_ANONYMOUS[0]
+ : this.packageRegistryAccessLevelDefault();
+ },
+ packageRegistryAccessLevelDefault() {
+ return (
+ PACKAGE_REGISTRY_ACCESS_LEVEL_DEFAULT_BY_PROJECT_VISIBILITY[this.visibilityLevel] ??
+ featureAccessLevel.NOT_ENABLED
+ );
+ },
},
};
</script>
@@ -897,10 +895,36 @@ export default {
:help-text="$options.i18n.packageRegistryHelpText"
data-testid="package-registry-access-level"
>
- <project-feature-setting
- v-model="packageRegistryAccessLevel"
+ <gl-toggle
+ class="gl-my-2"
+ :value="packageRegistryEnabled"
:label="$options.i18n.packageRegistryLabel"
- :options="packageRegistryFeatureAccessLevelOptions"
+ label-position="hidden"
+ name="package_registry_enabled"
+ @change="onPackageRegistryEnabledToggle"
+ />
+ <div
+ v-if="packageRegistryApiForEveryoneEnabledShown"
+ class="project-feature-setting-group gl-pl-7 gl-sm-pl-5 gl-my-3"
+ >
+ <project-setting-row
+ :label="$options.i18n.packageRegistryForEveryoneLabel"
+ :help-text="$options.i18n.packageRegistryForEveryoneHelpText"
+ >
+ <gl-toggle
+ class="gl-my-2"
+ :value="packageRegistryApiForEveryoneEnabled"
+ :disabled="!packageRegistryEnabled"
+ :label="$options.i18n.packageRegistryForEveryoneLabel"
+ label-position="hidden"
+ name="package_registry_api_for_everyone_enabled"
+ @change="onPackageRegistryApiForEveryoneEnabledToggle"
+ />
+ </project-setting-row>
+ </div>
+ <input
+ :value="packageRegistryAccessLevel"
+ type="hidden"
name="project[project_feature_attributes][package_registry_access_level]"
/>
</project-setting-row>
@@ -923,11 +947,10 @@ export default {
/>
</project-setting-row>
<project-setting-row
- v-if="splitOperationsEnabled"
ref="monitor-settings"
:label="$options.i18n.monitorLabel"
:help-text="
- s__('ProjectSettings|Configure your project resources and monitor their health.')
+ s__('ProjectSettings|Monitor the health of your project and respond to incidents.')
"
>
<project-feature-setting
@@ -937,21 +960,6 @@ export default {
name="project[project_feature_attributes][monitor_access_level]"
/>
</project-setting-row>
- <project-setting-row
- v-else
- ref="operations-settings"
- :label="$options.i18n.operationsLabel"
- :help-text="
- s__('ProjectSettings|Configure your project resources and monitor their health.')
- "
- >
- <project-feature-setting
- v-model="operationsAccessLevel"
- :label="$options.i18n.operationsLabel"
- :options="featureAccessLevelOptions"
- name="project[project_feature_attributes][operations_access_level]"
- />
- </project-setting-row>
<div class="project-feature-setting-group gl-pl-7 gl-sm-pl-5">
<project-setting-row
ref="metrics-visibility-settings"
@@ -966,47 +974,45 @@ export default {
/>
</project-setting-row>
</div>
- <template v-if="splitOperationsEnabled">
- <project-setting-row
- ref="environments-settings"
+ <project-setting-row
+ ref="environments-settings"
+ :label="$options.i18n.environmentsLabel"
+ :help-text="$options.i18n.environmentsHelpText"
+ :help-path="environmentsHelpPath"
+ >
+ <project-feature-setting
+ v-model="environmentsAccessLevel"
:label="$options.i18n.environmentsLabel"
- :help-text="$options.i18n.environmentsHelpText"
- :help-path="environmentsHelpPath"
- >
- <project-feature-setting
- v-model="environmentsAccessLevel"
- :label="$options.i18n.environmentsLabel"
- :options="featureAccessLevelOptions"
- name="project[project_feature_attributes][environments_access_level]"
- />
- </project-setting-row>
- <project-setting-row
- ref="feature-flags-settings"
+ :options="featureAccessLevelOptions"
+ name="project[project_feature_attributes][environments_access_level]"
+ />
+ </project-setting-row>
+ <project-setting-row
+ ref="feature-flags-settings"
+ :label="$options.i18n.featureFlagsLabel"
+ :help-text="$options.i18n.featureFlagsHelpText"
+ :help-path="featureFlagsHelpPath"
+ >
+ <project-feature-setting
+ v-model="featureFlagsAccessLevel"
:label="$options.i18n.featureFlagsLabel"
- :help-text="$options.i18n.featureFlagsHelpText"
- :help-path="featureFlagsHelpPath"
- >
- <project-feature-setting
- v-model="featureFlagsAccessLevel"
- :label="$options.i18n.featureFlagsLabel"
- :options="featureAccessLevelOptions"
- name="project[project_feature_attributes][feature_flags_access_level]"
- />
- </project-setting-row>
- <project-setting-row
- ref="infrastructure-settings"
+ :options="featureAccessLevelOptions"
+ name="project[project_feature_attributes][feature_flags_access_level]"
+ />
+ </project-setting-row>
+ <project-setting-row
+ ref="infrastructure-settings"
+ :label="$options.i18n.infrastructureLabel"
+ :help-text="$options.i18n.infrastructureHelpText"
+ :help-path="infrastructureHelpPath"
+ >
+ <project-feature-setting
+ v-model="infrastructureAccessLevel"
:label="$options.i18n.infrastructureLabel"
- :help-text="$options.i18n.infrastructureHelpText"
- :help-path="infrastructureHelpPath"
- >
- <project-feature-setting
- v-model="infrastructureAccessLevel"
- :label="$options.i18n.infrastructureLabel"
- :options="featureAccessLevelOptions"
- name="project[project_feature_attributes][infrastructure_access_level]"
- />
- </project-setting-row>
- </template>
+ :options="featureAccessLevelOptions"
+ name="project[project_feature_attributes][infrastructure_access_level]"
+ />
+ </project-setting-row>
<project-setting-row
ref="releases-settings"
:label="$options.i18n.releasesLabel"
diff --git a/app/assets/javascripts/pages/projects/shared/web_ide_link/index.js b/app/assets/javascripts/pages/projects/shared/web_ide_link/index.js
index 5f08943d211..84ff802c268 100644
--- a/app/assets/javascripts/pages/projects/shared/web_ide_link/index.js
+++ b/app/assets/javascripts/pages/projects/shared/web_ide_link/index.js
@@ -1,7 +1,15 @@
import Vue from 'vue';
+import VueApollo from 'vue-apollo';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { joinPaths, webIDEUrl } from '~/lib/utils/url_utility';
import WebIdeButton from '~/vue_shared/components/web_ide_link.vue';
+import createDefaultClient from '~/lib/graphql';
+
+Vue.use(VueApollo);
+
+const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient(),
+});
export default ({ el, router }) => {
if (!el) return;
@@ -9,15 +17,18 @@ export default ({ el, router }) => {
const { projectPath, ref, isBlob, webIdeUrl, ...options } = convertObjectPropsToCamelCase(
JSON.parse(el.dataset.options),
);
+ const { webIdePromoPopoverImg } = el.dataset;
// eslint-disable-next-line no-new
new Vue({
el,
router,
+ apolloProvider,
render(h) {
return h(WebIdeButton, {
props: {
isBlob,
+ webIdePromoPopoverImg,
webIdeUrl: isBlob
? webIdeUrl
: webIDEUrl(
diff --git a/app/assets/javascripts/pages/projects/tags/new/index.js b/app/assets/javascripts/pages/projects/tags/new/index.js
index 9ef1017f9f2..eb1f705eab9 100644
--- a/app/assets/javascripts/pages/projects/tags/new/index.js
+++ b/app/assets/javascripts/pages/projects/tags/new/index.js
@@ -1,8 +1,8 @@
import $ from 'jquery';
import GLForm from '~/gl_form';
-import RefSelectDropdown from '~/ref_select_dropdown';
import ZenMode from '~/zen_mode';
+import initNewTagRefSelector from '~/tags/init_new_tag_ref_selector';
+initNewTagRefSelector();
new ZenMode(); // eslint-disable-line no-new
new GLForm($('.tag-form')); // eslint-disable-line no-new
-new RefSelectDropdown($('.js-branch-select')); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/registrations/new/index.js b/app/assets/javascripts/pages/registrations/new/index.js
index 897acf9b02c..eaafc0235a8 100644
--- a/app/assets/javascripts/pages/registrations/new/index.js
+++ b/app/assets/javascripts/pages/registrations/new/index.js
@@ -4,6 +4,7 @@ import NoEmojiValidator from '~/emoji/no_emoji_validator';
import LengthValidator from '~/pages/sessions/new/length_validator';
import UsernameValidator from '~/pages/sessions/new/username_validator';
import EmailFormatValidator from '~/pages/sessions/new/email_format_validator';
+import { initLanguageSwitcher } from '~/language_switcher';
import Tracking from '~/tracking';
new UsernameValidator(); // eslint-disable-line no-new
@@ -19,3 +20,5 @@ trackNewRegistrations();
Tracking.enableFormTracking({
forms: { allow: ['new_user'] },
});
+
+initLanguageSwitcher();
diff --git a/app/assets/javascripts/pages/sessions/new/index.js b/app/assets/javascripts/pages/sessions/new/index.js
index b62417cf595..a84ed5f01ad 100644
--- a/app/assets/javascripts/pages/sessions/new/index.js
+++ b/app/assets/javascripts/pages/sessions/new/index.js
@@ -1,6 +1,7 @@
import $ from 'jquery';
import initVueAlerts from '~/vue_alerts';
import NoEmojiValidator from '~/emoji/no_emoji_validator';
+import { initLanguageSwitcher } from '~/language_switcher';
import LengthValidator from './length_validator';
import OAuthRememberMe from './oauth_remember_me';
import preserveUrlFragment from './preserve_url_fragment';
@@ -20,3 +21,4 @@ new OAuthRememberMe({
// redirected to sign-in after attempting to access a protected URL that included a fragment.
preserveUrlFragment(window.location.hash);
initVueAlerts();
+initLanguageSwitcher();
diff --git a/app/assets/javascripts/pages/shared/wikis/components/wiki_content.vue b/app/assets/javascripts/pages/shared/wikis/components/wiki_content.vue
index b72579276e8..b19809aff53 100644
--- a/app/assets/javascripts/pages/shared/wikis/components/wiki_content.vue
+++ b/app/assets/javascripts/pages/shared/wikis/components/wiki_content.vue
@@ -1,10 +1,11 @@
<script>
-import { GlSkeletonLoader, GlSafeHtmlDirective, GlAlert } from '@gitlab/ui';
+import { GlSkeletonLoader, GlAlert } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
import { handleLocationHash } from '~/lib/utils/common_utils';
-import { renderGFM } from '../render_gfm_facade';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
export default {
components: {
@@ -12,7 +13,7 @@ export default {
GlAlert,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
getWikiContentUrl: {
@@ -86,9 +87,9 @@ export default {
<div
v-else-if="!loadingContentFailed && !isLoadingContent"
ref="content"
+ v-safe-html="content"
data-qa-selector="wiki_page_content"
data-testid="wiki-page-content"
class="js-wiki-page-content md"
- v-html="content /* eslint-disable-line vue/no-v-html */"
></div>
</template>
diff --git a/app/assets/javascripts/pages/shared/wikis/render_gfm_facade.js b/app/assets/javascripts/pages/shared/wikis/render_gfm_facade.js
deleted file mode 100644
index 90cc2983153..00000000000
--- a/app/assets/javascripts/pages/shared/wikis/render_gfm_facade.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import $ from 'jquery';
-
-export const renderGFM = (el) => {
- return $(el).renderGFM();
-};
diff --git a/app/assets/javascripts/pages/web_ide/remote_ide/index.js b/app/assets/javascripts/pages/web_ide/remote_ide/index.js
new file mode 100644
index 00000000000..463798e85b9
--- /dev/null
+++ b/app/assets/javascripts/pages/web_ide/remote_ide/index.js
@@ -0,0 +1,3 @@
+import { mountRemoteIDE } from '~/ide/remote';
+
+mountRemoteIDE(document.getElementById('ide'));
diff --git a/app/assets/javascripts/performance_bar/components/detailed_metric.vue b/app/assets/javascripts/performance_bar/components/detailed_metric.vue
index 0640faae8b7..ea8005e8dfb 100644
--- a/app/assets/javascripts/performance_bar/components/detailed_metric.vue
+++ b/app/assets/javascripts/performance_bar/components/detailed_metric.vue
@@ -1,5 +1,5 @@
<script>
-import { GlButton, GlDropdown, GlDropdownItem, GlModal, GlModalDirective } from '@gitlab/ui';
+import { GlButton, GlModal, GlModalDirective, GlCollapsibleListbox } from '@gitlab/ui';
import { __, s__ } from '~/locale';
import { sortOrders, sortOrderOptions } from '../constants';
@@ -9,9 +9,8 @@ export default {
components: {
RequestWarning,
GlButton,
- GlDropdown,
- GlDropdownItem,
GlModal,
+ GlCollapsibleListbox,
},
directives: {
'gl-modal': GlModalDirective,
@@ -119,9 +118,6 @@ export default {
itemHasOpenedBacktrace(toggledIndex) {
return this.openedBacktraces.find((openedIndex) => openedIndex === toggledIndex) >= 0;
},
- changeSortOrder(order) {
- this.sortOrder = order;
- },
sortDetailByDuration(a, b) {
return a.duration < b.duration ? 1 : -1;
},
@@ -157,19 +153,14 @@ export default {
</div>
</div>
</div>
- <gl-dropdown
+ <gl-collapsible-listbox
v-if="displaySortOrder"
- :text="$options.sortOrderOptions[sortOrder]"
+ v-model="sortOrder"
+ :toggle-text="$options.sortOrderOptions[sortOrder].text"
+ :items="Object.values($options.sortOrderOptions)"
right
data-testid="performance-bar-sort-order"
- >
- <gl-dropdown-item
- v-for="option in Object.keys($options.sortOrderOptions)"
- :key="option"
- @click="changeSortOrder(option)"
- >{{ $options.sortOrderOptions[option] }}</gl-dropdown-item
- >
- </gl-dropdown>
+ />
</div>
<hr />
<table class="table gl-table">
diff --git a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
index a5fa85f1ed5..dbca8bc9be7 100644
--- a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
+++ b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSafeHtmlDirective } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { glEmojiTag } from '~/emoji';
import { mergeUrlParams } from '~/lib/utils/url_utility';
@@ -15,7 +15,7 @@ export default {
RequestSelector,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
store: {
diff --git a/app/assets/javascripts/performance_bar/components/request_warning.vue b/app/assets/javascripts/performance_bar/components/request_warning.vue
index 3ebd222029b..91e905d62e6 100644
--- a/app/assets/javascripts/performance_bar/components/request_warning.vue
+++ b/app/assets/javascripts/performance_bar/components/request_warning.vue
@@ -1,5 +1,6 @@
<script>
-import { GlPopover, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlPopover } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { glEmojiTag } from '~/emoji';
export default {
@@ -7,7 +8,7 @@ export default {
GlPopover,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
htmlId: {
diff --git a/app/assets/javascripts/performance_bar/constants.js b/app/assets/javascripts/performance_bar/constants.js
index 09745797424..6f4ddd5c242 100644
--- a/app/assets/javascripts/performance_bar/constants.js
+++ b/app/assets/javascripts/performance_bar/constants.js
@@ -6,6 +6,12 @@ export const sortOrders = {
};
export const sortOrderOptions = {
- [sortOrders.DURATION]: s__('PerformanceBar|Sort by duration'),
- [sortOrders.CHRONOLOGICAL]: s__('PerformanceBar|Sort chronologically'),
+ [sortOrders.DURATION]: {
+ value: sortOrders.DURATION,
+ text: s__('PerformanceBar|Sort by duration'),
+ },
+ [sortOrders.CHRONOLOGICAL]: {
+ value: sortOrders.CHRONOLOGICAL,
+ text: s__('PerformanceBar|Sort chronologically'),
+ },
};
diff --git a/app/assets/javascripts/pipeline_editor/components/editor/ci_editor_header.vue b/app/assets/javascripts/pipeline_editor/components/editor/ci_editor_header.vue
deleted file mode 100644
index 189690ce2c3..00000000000
--- a/app/assets/javascripts/pipeline_editor/components/editor/ci_editor_header.vue
+++ /dev/null
@@ -1,70 +0,0 @@
-<script>
-import { GlButton } from '@gitlab/ui';
-import { __ } from '~/locale';
-import Tracking from '~/tracking';
-import { pipelineEditorTrackingOptions, TEMPLATE_REPOSITORY_URL } from '../../constants';
-
-export default {
- i18n: {
- browseTemplates: __('Browse templates'),
- help: __('Help'),
- },
- TEMPLATE_REPOSITORY_URL,
- components: {
- GlButton,
- },
- mixins: [Tracking.mixin()],
- props: {
- showDrawer: {
- type: Boolean,
- required: true,
- },
- },
- methods: {
- toggleDrawer() {
- if (this.showDrawer) {
- this.$emit('close-drawer');
- } else {
- this.$emit('open-drawer');
- this.trackHelpDrawerClick();
- }
- },
- trackHelpDrawerClick() {
- const { label, actions } = pipelineEditorTrackingOptions;
- this.track(actions.openHelpDrawer, { label });
- },
- trackTemplateBrowsing() {
- const { label, actions } = pipelineEditorTrackingOptions;
-
- this.track(actions.browseTemplates, { label });
- },
- },
-};
-</script>
-
-<template>
- <div
- class="gl-bg-gray-10 gl-display-flex gl-p-3 gl-gap-3 gl-border-solid gl-border-gray-100 gl-border-1"
- >
- <gl-button
- :href="$options.TEMPLATE_REPOSITORY_URL"
- size="small"
- icon="external-link"
- target="_blank"
- data-testid="template-repo-link"
- data-qa-selector="template_repo_link"
- @click="trackTemplateBrowsing"
- >
- {{ $options.i18n.browseTemplates }}
- </gl-button>
- <gl-button
- icon="information-o"
- size="small"
- data-testid="drawer-toggle"
- data-qa-selector="drawer_toggle"
- @click="toggleDrawer"
- >
- {{ $options.i18n.help }}
- </gl-button>
- </div>
-</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue b/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue
deleted file mode 100644
index 1f8ddae3696..00000000000
--- a/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue
+++ /dev/null
@@ -1,254 +0,0 @@
-<script>
-import {
- GlDropdown,
- GlDropdownItem,
- GlDropdownSectionHeader,
- GlInfiniteScroll,
- GlLoadingIcon,
- GlSearchBoxByType,
- GlTooltipDirective,
-} from '@gitlab/ui';
-import { produce } from 'immer';
-import { historyPushState } from '~/lib/utils/common_utils';
-import { setUrlParams } from '~/lib/utils/url_utility';
-import { __ } from '~/locale';
-import {
- BRANCH_PAGINATION_LIMIT,
- BRANCH_SEARCH_DEBOUNCE,
- DEFAULT_FAILURE,
-} from '~/pipeline_editor/constants';
-import updateCurrentBranchMutation from '~/pipeline_editor/graphql/mutations/client/update_current_branch.mutation.graphql';
-import getAvailableBranchesQuery from '~/pipeline_editor/graphql/queries/available_branches.query.graphql';
-import getCurrentBranch from '~/pipeline_editor/graphql/queries/client/current_branch.query.graphql';
-import getLastCommitBranch from '~/pipeline_editor/graphql/queries/client/last_commit_branch.query.graphql';
-
-export default {
- i18n: {
- dropdownHeader: __('Switch branch'),
- title: __('Branches'),
- fetchError: __('Unable to fetch branch list for this project.'),
- },
- inputDebounce: BRANCH_SEARCH_DEBOUNCE,
- components: {
- GlDropdown,
- GlDropdownItem,
- GlDropdownSectionHeader,
- GlInfiniteScroll,
- GlLoadingIcon,
- GlSearchBoxByType,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- inject: ['projectFullPath', 'totalBranches'],
- props: {
- hasUnsavedChanges: {
- type: Boolean,
- required: false,
- default: false,
- },
- paginationLimit: {
- type: Number,
- required: false,
- default: BRANCH_PAGINATION_LIMIT,
- },
- shouldLoadNewBranch: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- data() {
- return {
- availableBranches: [],
- branchSelected: null,
- pageLimit: this.paginationLimit,
- pageCounter: 0,
- searchTerm: '',
- lastCommitBranch: '',
- };
- },
- apollo: {
- availableBranches: {
- query: getAvailableBranchesQuery,
- variables() {
- return {
- offset: 0,
- projectFullPath: this.projectFullPath,
- ...this.availableBranchesVariables,
- };
- },
- update(data) {
- return data.project?.repository?.branchNames || [];
- },
- result() {
- this.pageCounter += 1;
- },
- error() {
- this.showFetchError();
- },
- },
- currentBranch: {
- query: getCurrentBranch,
- update(data) {
- return data.workBranches.current.name;
- },
- },
- lastCommitBranch: {
- query: getLastCommitBranch,
- update(data) {
- return data.workBranches.lastCommit.name;
- },
- result({ data }) {
- if (data) {
- const { name: lastCommitBranch } = data.workBranches.lastCommit;
- if (lastCommitBranch === '' || this.availableBranches.includes(lastCommitBranch)) {
- return;
- }
-
- this.availableBranches.unshift(lastCommitBranch);
- }
- },
- },
- },
- computed: {
- availableBranchesVariables() {
- if (this.searchTerm.length > 0) {
- return {
- limit: this.totalBranches,
- searchPattern: `*${this.searchTerm}*`,
- };
- }
-
- return {
- limit: this.paginationLimit,
- searchPattern: '*',
- };
- },
- enableBranchSwitcher() {
- return this.availableBranches.length > 0 || this.searchTerm.length > 0;
- },
- isBranchesLoading() {
- return this.$apollo.queries.availableBranches.loading;
- },
- },
- watch: {
- shouldLoadNewBranch(flag) {
- if (flag) {
- this.changeBranch(this.branchSelected);
- }
- },
- },
- methods: {
- // if there is no searchPattern, paginate by {paginationLimit} branches
- fetchNextBranches() {
- if (
- this.isBranchesLoading ||
- this.searchTerm.length > 0 ||
- this.availableBranches.length >= this.totalBranches
- ) {
- return;
- }
-
- this.$apollo.queries.availableBranches
- .fetchMore({
- variables: {
- offset: this.pageCounter * this.paginationLimit,
- },
- updateQuery(previousResult, { fetchMoreResult }) {
- const previousBranches = previousResult.project.repository.branchNames;
- const newBranches = fetchMoreResult.project.repository.branchNames;
-
- return produce(fetchMoreResult, (draftData) => {
- draftData.project.repository.branchNames = previousBranches.concat(newBranches);
- });
- },
- })
- .catch(this.showFetchError);
- },
- async changeBranch(newBranch) {
- this.updateCurrentBranch(newBranch);
- const updatedPath = setUrlParams({ branch_name: newBranch });
- historyPushState(updatedPath);
-
- // refetching the content will cause a lot of components to re-render,
- // including the text editor which uses the commit sha to register the CI schema
- // so we need to make sure the currentBranch (and consequently, the commitSha) are updated first
- await this.$nextTick();
- this.$emit('refetchContent');
- },
- selectBranch(newBranch) {
- if (newBranch !== this.currentBranch) {
- // If there are unsaved changes, we want to show the user
- // a modal to confirm what to do with these before changing
- // branches.
- if (this.hasUnsavedChanges) {
- this.branchSelected = newBranch;
- this.$emit('select-branch', newBranch);
- } else {
- this.changeBranch(newBranch);
- }
- }
- },
- async setSearchTerm(newSearchTerm) {
- this.pageCounter = 0;
- this.searchTerm = newSearchTerm.trim();
- },
- showFetchError() {
- this.$emit('showError', {
- type: DEFAULT_FAILURE,
- reasons: [this.$options.i18n.fetchError],
- });
- },
- updateCurrentBranch(currentBranch) {
- this.$apollo.mutate({
- mutation: updateCurrentBranchMutation,
- variables: { currentBranch },
- });
- },
- },
-};
-</script>
-
-<template>
- <gl-dropdown
- v-gl-tooltip.hover
- :title="$options.i18n.dropdownHeader"
- :header-text="$options.i18n.dropdownHeader"
- :text="currentBranch"
- :disabled="!enableBranchSwitcher"
- icon="branch"
- data-qa-selector="branch_selector_button"
- data-testid="branch-selector"
- >
- <gl-search-box-by-type :debounce="$options.inputDebounce" @input="setSearchTerm" />
- <gl-dropdown-section-header>
- {{ $options.i18n.title }}
- </gl-dropdown-section-header>
-
- <gl-infinite-scroll
- :fetched-items="availableBranches.length"
- :max-list-height="250"
- data-qa-selector="branch_menu_container"
- @bottomReached="fetchNextBranches"
- >
- <template #items>
- <gl-dropdown-item
- v-for="branch in availableBranches"
- :key="branch"
- :is-checked="currentBranch === branch"
- is-check-item
- data-qa-selector="branch_menu_item_button"
- @click="selectBranch(branch)"
- >
- {{ branch }}
- </gl-dropdown-item>
- </template>
- <template #default>
- <gl-dropdown-item v-if="isBranchesLoading" key="loading">
- <gl-loading-icon size="lg" />
- </gl-dropdown-item>
- </template>
- </gl-infinite-scroll>
- </gl-dropdown>
-</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue b/app/assets/javascripts/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue
deleted file mode 100644
index 8e95fad1e48..00000000000
--- a/app/assets/javascripts/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue
+++ /dev/null
@@ -1,72 +0,0 @@
-<script>
-import { GlButton } from '@gitlab/ui';
-import getAppStatus from '~/pipeline_editor/graphql/queries/client/app_status.query.graphql';
-import { EDITOR_APP_STATUS_EMPTY, EDITOR_APP_STATUS_LOADING } from '../../constants';
-import FileTreePopover from '../popovers/file_tree_popover.vue';
-import BranchSwitcher from './branch_switcher.vue';
-
-export default {
- components: {
- BranchSwitcher,
- FileTreePopover,
- GlButton,
- },
- props: {
- hasUnsavedChanges: {
- type: Boolean,
- required: false,
- default: false,
- },
- isNewCiConfigFile: {
- type: Boolean,
- required: false,
- default: false,
- },
- shouldLoadNewBranch: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- apollo: {
- appStatus: {
- query: getAppStatus,
- update(data) {
- return data.app.status;
- },
- },
- },
- computed: {
- isAppLoading() {
- return this.appStatus === EDITOR_APP_STATUS_LOADING;
- },
- showFileTreeToggle() {
- return !this.isNewCiConfigFile && this.appStatus !== EDITOR_APP_STATUS_EMPTY;
- },
- },
- methods: {
- onFileTreeBtnClick() {
- this.$emit('toggle-file-tree');
- },
- },
-};
-</script>
-<template>
- <div class="gl-mb-4">
- <gl-button
- v-if="showFileTreeToggle"
- id="file-tree-toggle"
- icon="file-tree"
- data-testid="file-tree-toggle"
- :aria-label="__('File Tree')"
- :loading="isAppLoading"
- @click="onFileTreeBtnClick"
- />
- <file-tree-popover v-if="showFileTreeToggle" />
- <branch-switcher
- :has-unsaved-changes="hasUnsavedChanges"
- :should-load-new-branch="shouldLoadNewBranch"
- v-on="$listeners"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue b/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue
deleted file mode 100644
index 137dfca68d6..00000000000
--- a/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue
+++ /dev/null
@@ -1,188 +0,0 @@
-<script>
-import { GlButton, GlIcon, GlLink, GlLoadingIcon, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { truncateSha } from '~/lib/utils/text_utility';
-import { s__ } from '~/locale';
-import getPipelineQuery from '~/pipeline_editor/graphql/queries/pipeline.query.graphql';
-import getPipelineEtag from '~/pipeline_editor/graphql/queries/client/pipeline_etag.query.graphql';
-import {
- getQueryHeaders,
- toggleQueryPollingByVisibility,
-} from '~/pipelines/components/graph/utils';
-import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import PipelineEditorMiniGraph from './pipeline_editor_mini_graph.vue';
-
-const POLL_INTERVAL = 10000;
-export const i18n = {
- fetchError: s__('Pipeline|We are currently unable to fetch pipeline data'),
- fetchLoading: s__('Pipeline|Checking pipeline status'),
- pipelineInfo: s__(
- `Pipeline|Pipeline %{idStart}#%{idEnd} %{statusStart}%{statusEnd} for %{commitStart}%{commitEnd}`,
- ),
- viewBtn: s__('Pipeline|View pipeline'),
- viewCommit: s__('Pipeline|View commit'),
-};
-
-export default {
- i18n,
- components: {
- CiIcon,
- GlButton,
- GlIcon,
- GlLink,
- GlLoadingIcon,
- GlSprintf,
- PipelineEditorMiniGraph,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- inject: ['projectFullPath'],
- props: {
- commitSha: {
- type: String,
- required: false,
- default: '',
- },
- },
- apollo: {
- pipelineEtag: {
- query: getPipelineEtag,
- update(data) {
- return data.etags?.pipeline;
- },
- },
- pipeline: {
- context() {
- return getQueryHeaders(this.pipelineEtag);
- },
- query: getPipelineQuery,
- variables() {
- return {
- fullPath: this.projectFullPath,
- sha: this.commitSha,
- };
- },
- update(data) {
- const { id, iid, commit = {}, detailedStatus = {}, stages, status } =
- data.project?.pipeline || {};
-
- return {
- id,
- iid,
- commit,
- detailedStatus,
- stages,
- status,
- };
- },
- result(res) {
- if (res.data?.project?.pipeline) {
- this.hasError = false;
- }
- },
- error() {
- this.hasError = true;
- },
- pollInterval: POLL_INTERVAL,
- },
- },
- data() {
- return {
- hasError: false,
- };
- },
- computed: {
- commitText() {
- const shortSha = truncateSha(this.commitSha);
- const commitTitle = this.pipeline.commit.title || '';
-
- if (commitTitle.length > 0) {
- return `${shortSha}: ${commitTitle}`;
- }
-
- return shortSha;
- },
- hasPipelineData() {
- return Boolean(this.pipeline?.id);
- },
- pipelineId() {
- return getIdFromGraphQLId(this.pipeline.id);
- },
- showLoadingState() {
- // the query is set to poll regularly, so if there is no pipeline data
- // (e.g. pipeline is null during fetch when the pipeline hasn't been
- // triggered yet), we can just show the loading state until the pipeline
- // details are ready to be fetched
- return (
- this.$apollo.queries.pipeline.loading ||
- this.commitSha.length === 0 ||
- (!this.hasPipelineData && !this.hasError)
- );
- },
- shortSha() {
- return truncateSha(this.commitSha);
- },
- status() {
- return this.pipeline.detailedStatus;
- },
- },
- mounted() {
- toggleQueryPollingByVisibility(this.$apollo.queries.pipeline, POLL_INTERVAL);
- },
-};
-</script>
-
-<template>
- <div class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-flex-wrap">
- <template v-if="showLoadingState">
- <div>
- <gl-loading-icon class="gl-mr-auto gl-display-inline-block" size="sm" />
- <span data-testid="pipeline-loading-msg">{{ $options.i18n.fetchLoading }}</span>
- </div>
- </template>
- <template v-else-if="hasError">
- <gl-icon class="gl-mr-auto" name="warning-solid" />
- <span data-testid="pipeline-error-msg">{{ $options.i18n.fetchError }}</span>
- </template>
- <template v-else>
- <div class="gl-text-truncate gl-md-max-w-50p gl-mr-1">
- <a :href="status.detailsPath" class="gl-mr-auto">
- <ci-icon :status="status" :size="16" data-testid="pipeline-status-icon" />
- </a>
- <span class="gl-font-weight-bold">
- <gl-sprintf :message="$options.i18n.pipelineInfo">
- <template #id="{ content }">
- <span data-testid="pipeline-id" data-qa-selector="pipeline_id_content">
- {{ content }}{{ pipelineId }}
- </span>
- </template>
- <template #status>{{ status.text }}</template>
- <template #commit>
- <gl-link
- v-gl-tooltip.hover
- :href="pipeline.commit.webPath"
- :title="$options.i18n.viewCommit"
- data-testid="pipeline-commit"
- >
- {{ commitText }}
- </gl-link>
- </template>
- </gl-sprintf>
- </span>
- </div>
- <div class="gl-display-flex gl-flex-wrap">
- <pipeline-editor-mini-graph :pipeline="pipeline" v-on="$listeners" />
- <gl-button
- class="gl-ml-3"
- category="secondary"
- variant="confirm"
- :href="status.detailsPath"
- data-testid="pipeline-view-btn"
- >
- {{ $options.i18n.viewBtn }}
- </gl-button>
- </div>
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/header/validation_segment.vue b/app/assets/javascripts/pipeline_editor/components/header/validation_segment.vue
deleted file mode 100644
index 610a570c4ce..00000000000
--- a/app/assets/javascripts/pipeline_editor/components/header/validation_segment.vue
+++ /dev/null
@@ -1,126 +0,0 @@
-<script>
-import { GlIcon, GlLink, GlLoadingIcon } from '@gitlab/ui';
-import { __, s__, sprintf } from '~/locale';
-import getAppStatus from '~/pipeline_editor/graphql/queries/client/app_status.query.graphql';
-import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
-import {
- EDITOR_APP_STATUS_EMPTY,
- EDITOR_APP_STATUS_LINT_UNAVAILABLE,
- EDITOR_APP_STATUS_LOADING,
- EDITOR_APP_STATUS_VALID,
-} from '../../constants';
-
-export const i18n = {
- empty: __(
- "We'll continuously validate your pipeline configuration. The validation results will appear here.",
- ),
- learnMore: __('Learn more'),
- loading: s__('Pipelines|Validating GitLab CI configuration…'),
- invalid: s__('Pipelines|This GitLab CI configuration is invalid.'),
- invalidWithReason: s__('Pipelines|This GitLab CI configuration is invalid: %{reason}.'),
- unavailableValidation: s__('Pipelines|Configuration validation currently not available.'),
- valid: s__('Pipelines|Pipeline syntax is correct.'),
-};
-
-export default {
- i18n,
- components: {
- GlIcon,
- GlLink,
- GlLoadingIcon,
- TooltipOnTruncate,
- },
- inject: {
- lintUnavailableHelpPagePath: {
- default: '',
- },
- ymlHelpPagePath: {
- default: '',
- },
- },
- props: {
- ciConfig: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- },
- apollo: {
- appStatus: {
- query: getAppStatus,
- update(data) {
- return data.app.status;
- },
- },
- },
- computed: {
- helpPath() {
- return this.isLintUnavailable ? this.lintUnavailableHelpPagePath : this.ymlHelpPagePath;
- },
- isEmpty() {
- return this.appStatus === EDITOR_APP_STATUS_EMPTY;
- },
- isLintUnavailable() {
- return this.appStatus === EDITOR_APP_STATUS_LINT_UNAVAILABLE;
- },
- isLoading() {
- return this.appStatus === EDITOR_APP_STATUS_LOADING;
- },
- isValid() {
- return this.appStatus === EDITOR_APP_STATUS_VALID;
- },
- icon() {
- switch (this.appStatus) {
- case EDITOR_APP_STATUS_EMPTY:
- return 'check';
- case EDITOR_APP_STATUS_LINT_UNAVAILABLE:
- return 'time-out';
- case EDITOR_APP_STATUS_VALID:
- return 'check';
- default:
- return 'warning-solid';
- }
- },
- message() {
- const [reason] = this.ciConfig?.errors || [];
-
- switch (this.appStatus) {
- case EDITOR_APP_STATUS_EMPTY:
- return this.$options.i18n.empty;
- case EDITOR_APP_STATUS_LINT_UNAVAILABLE:
- return this.$options.i18n.unavailableValidation;
- case EDITOR_APP_STATUS_VALID:
- return this.$options.i18n.valid;
- default:
- // Only display first error as a reason
- return this.ciConfig?.errors?.length > 0
- ? sprintf(this.$options.i18n.invalidWithReason, { reason }, false)
- : this.$options.i18n.invalid;
- }
- },
- },
-};
-</script>
-
-<template>
- <div>
- <template v-if="isLoading">
- <gl-loading-icon size="sm" inline />
- {{ $options.i18n.loading }}
- </template>
-
- <span v-else class="gl-display-inline-flex gl-white-space-nowrap gl-max-w-full">
- <tooltip-on-truncate :title="message" class="gl-text-truncate">
- <gl-icon :name="icon" />
- <span data-qa-selector="validation_message_content" data-testid="validationMsg">
- {{ message }}
- </span>
- </tooltip-on-truncate>
- <span v-if="!isEmpty" class="gl-flex-shrink-0 gl-pl-2">
- <gl-link data-testid="learnMoreLink" :href="helpPath">
- {{ $options.i18n.learnMore }}
- </gl-link>
- </span>
- </span>
- </div>
-</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/ui/editor_tab.vue b/app/assets/javascripts/pipeline_editor/components/ui/editor_tab.vue
deleted file mode 100644
index 65f399d1912..00000000000
--- a/app/assets/javascripts/pipeline_editor/components/ui/editor_tab.vue
+++ /dev/null
@@ -1,154 +0,0 @@
-<script>
-import { GlAlert, GlBadge, GlTab } from '@gitlab/ui';
-import { __, s__ } from '~/locale';
-/**
- * Wrapper of <gl-tab> to optionally lazily render this tab's content
- * when its shown **without dismounting after its hidden**.
- *
- * Usage:
- *
- * API is the same as <gl-tab>, for example:
- *
- * <gl-tabs>
- * <editor-tab title="Tab 1" lazy>
- * lazily mounted content (gets mounted if this is first tab)
- * </editor-tab>
- * <editor-tab title="Tab 2" lazy>
- * lazily mounted content
- * </editor-tab>
- * <editor-tab title="Tab 3">
- * eagerly mounted content
- * </editor-tab>
- * </gl-tabs>
- *
- * Once the tab is selected it is permanently set as "not-lazy"
- * so it's contents are not dismounted.
- *
- * lazy is "false" by default, as in <gl-tab>.
- *
- * It is also possible to pass the `isEmpty` and or `isInvalid` to let
- * the tab component handle that state on its own. For example:
- *
- * * <gl-tabs>
- * <editor-tab-with-status title="Tab 1" :is-empty="isEmpty" :is-invalid="isInvalid">
- * ...
- * </editor-tab-with-status>
- * Will be the same as normal, except it will only render the slot component
- * if the status is not empty and not invalid. In any of these 2 cases, it will render
- * a generic component and avoid mounting whatever it received in the slot.
- * </gl-tabs>
- */
-
-export default {
- i18n: {
- invalid: __('Your CI/CD configuration syntax is invalid. View Lint tab for more details.'),
- unavailable: __(
- "We're experiencing difficulties and this tab content is currently unavailable.",
- ),
- },
- components: {
- GlAlert,
- GlBadge,
- GlTab,
- // Use a small renderless component to know when the tab content mounts because:
- // - gl-tab always gets mounted, even if lazy is `true`. See:
- // https://github.com/bootstrap-vue/bootstrap-vue/blob/dev/src/components/tabs/tab.js#L180
- // - we cannot listen to events on <slot />
- MountSpy: {
- render: () => null,
- },
- },
- inheritAttrs: false,
- props: {
- badgeTitle: {
- type: String,
- required: false,
- default: '',
- },
- badgeVariant: {
- type: String,
- required: false,
- default: 'info',
- },
- emptyMessage: {
- type: String,
- required: false,
- default: s__(
- 'PipelineEditor|This tab will be usable when the CI/CD configuration file is populated with valid syntax.',
- ),
- },
- isEmpty: {
- type: Boolean,
- required: false,
- default: false,
- },
- isInvalid: {
- type: Boolean,
- required: false,
- default: false,
- },
- isUnavailable: {
- type: Boolean,
- required: false,
- default: false,
- },
- keepComponentMounted: {
- type: Boolean,
- required: false,
- default: true,
- },
- lazy: {
- type: Boolean,
- required: false,
- default: false,
- },
- title: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- isLazy: this.lazy,
- };
- },
- computed: {
- hasBadgeTitle() {
- return this.badgeTitle.length > 0;
- },
- slots() {
- // eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
- return Object.keys(this.$slots);
- },
- },
- methods: {
- onContentMounted() {
- // When a child is first mounted make the entire tab
- // permanently mounted by setting 'lazy' to false unless
- // explicitly opted out.
- if (this.keepComponentMounted) {
- this.isLazy = false;
- }
- },
- },
-};
-</script>
-<template>
- <gl-tab :lazy="isLazy" v-bind="$attrs" v-on="$listeners">
- <template #title>
- <span>{{ title }}</span>
- <gl-badge v-if="hasBadgeTitle" class="gl-ml-2" size="sm" :variant="badgeVariant">{{
- badgeTitle
- }}</gl-badge>
- </template>
- <gl-alert v-if="isEmpty" variant="tip">{{ emptyMessage }}</gl-alert>
- <gl-alert v-else-if="isUnavailable" variant="danger" :dismissible="false">
- {{ $options.i18n.unavailable }}</gl-alert
- >
- <gl-alert v-else-if="isInvalid" variant="danger">{{ $options.i18n.invalid }}</gl-alert>
- <template v-else>
- <slot v-for="slot in slots" :name="slot"></slot>
- <mount-spy @hook:mounted="onContentMounted" />
- </template>
- </gl-tab>
-</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_empty_state.vue b/app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_empty_state.vue
deleted file mode 100644
index 7d2b9cd3d42..00000000000
--- a/app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_empty_state.vue
+++ /dev/null
@@ -1,72 +0,0 @@
-<script>
-import { GlButton, GlSprintf } from '@gitlab/ui';
-import { __ } from '~/locale';
-import PipelineEditorFileNav from '~/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue';
-
-export default {
- components: {
- GlButton,
- GlSprintf,
- PipelineEditorFileNav,
- },
- i18n: {
- title: __('Optimize your workflow with CI/CD Pipelines'),
- body: __(
- 'Create a new %{codeStart}.gitlab-ci.yml%{codeEnd} file at the root of the repository to get started.',
- ),
- btnText: __('Configure pipeline'),
- externalCiNote: __("This project's pipeline configuration is located outside this repository"),
- externalCiInstructions: __(
- 'To edit the pipeline configuration, you must go to the project or external site that hosts the file.',
- ),
- },
- inject: {
- emptyStateIllustrationPath: {
- default: '',
- },
- usesExternalConfig: {
- default: false,
- type: Boolean,
- required: false,
- },
- },
- methods: {
- createEmptyConfigFile() {
- this.$emit('createEmptyConfigFile');
- },
- },
-};
-</script>
-<template>
- <div>
- <pipeline-editor-file-nav v-on="$listeners" />
- <div class="gl-display-flex gl-flex-direction-column gl-align-items-center gl-mt-11">
- <img :src="emptyStateIllustrationPath" />
- <div
- v-if="usesExternalConfig"
- class="gl-display-flex gl-flex-direction-column gl-align-items-center"
- >
- <h1 class="gl-font-size-h1">{{ $options.i18n.externalCiNote }}</h1>
- <p class="gl-mt-3">{{ $options.i18n.externalCiInstructions }}</p>
- </div>
- <div v-else class="gl-display-flex gl-flex-direction-column gl-align-items-center">
- <h1 class="gl-font-size-h1">{{ $options.i18n.title }}</h1>
- <p class="gl-mt-3">
- <gl-sprintf :message="$options.i18n.body">
- <template #code="{ content }">
- <code>{{ content }}</code>
- </template>
- </gl-sprintf>
- </p>
- <gl-button
- variant="confirm"
- class="gl-mt-3"
- data-qa-selector="create_new_ci_button"
- @click="createEmptyConfigFile"
- >
- {{ $options.i18n.btnText }}
- </gl-button>
- </div>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/pipeline_new/components/legacy_pipeline_new_form.vue b/app/assets/javascripts/pipeline_new/components/legacy_pipeline_new_form.vue
deleted file mode 100644
index cd7cb7f8393..00000000000
--- a/app/assets/javascripts/pipeline_new/components/legacy_pipeline_new_form.vue
+++ /dev/null
@@ -1,490 +0,0 @@
-<script>
-import {
- GlAlert,
- GlIcon,
- GlButton,
- GlDropdown,
- GlDropdownItem,
- GlForm,
- GlFormGroup,
- GlFormInput,
- GlFormTextarea,
- GlLink,
- GlSprintf,
- GlLoadingIcon,
- GlSafeHtmlDirective as SafeHtml,
-} from '@gitlab/ui';
-import * as Sentry from '@sentry/browser';
-import { uniqueId } from 'lodash';
-import Vue from 'vue';
-import axios from '~/lib/utils/axios_utils';
-import { backOff } from '~/lib/utils/common_utils';
-import httpStatusCodes from '~/lib/utils/http_status';
-import { redirectTo } from '~/lib/utils/url_utility';
-import { s__, __, n__ } from '~/locale';
-import {
- VARIABLE_TYPE,
- FILE_TYPE,
- CONFIG_VARIABLES_TIMEOUT,
- CC_VALIDATION_REQUIRED_ERROR,
-} from '../constants';
-import filterVariables from '../utils/filter_variables';
-import RefsDropdown from './refs_dropdown.vue';
-
-const i18n = {
- variablesDescription: s__(
- 'Pipeline|Specify variable values to be used in this run. The values specified in %{linkStart}CI/CD settings%{linkEnd} will be used by default.',
- ),
- defaultError: __('Something went wrong on our end. Please try again.'),
- refsLoadingErrorTitle: s__('Pipeline|Branches or tags could not be loaded.'),
- submitErrorTitle: s__('Pipeline|Pipeline cannot be run.'),
- warningTitle: __('The form contains the following warning:'),
- maxWarningsSummary: __('%{total} warnings found: showing first %{warningsDisplayed}'),
- removeVariableLabel: s__('CiVariables|Remove variable'),
-};
-
-export default {
- typeOptions: {
- [VARIABLE_TYPE]: __('Variable'),
- [FILE_TYPE]: __('File'),
- },
- i18n,
- formElementClasses: 'gl-mr-3 gl-mb-3 gl-flex-basis-quarter gl-flex-shrink-0 gl-flex-grow-0',
- // this height value is used inline on the textarea to match the input field height
- // it's used to prevent the overwrite if 'gl-h-7' or 'gl-h-7!' were used
- textAreaStyle: { height: '32px' },
- components: {
- GlAlert,
- GlIcon,
- GlButton,
- GlDropdown,
- GlDropdownItem,
- GlForm,
- GlFormGroup,
- GlFormInput,
- GlFormTextarea,
- GlLink,
- GlSprintf,
- GlLoadingIcon,
- RefsDropdown,
- CcValidationRequiredAlert: () =>
- import('ee_component/billings/components/cc_validation_required_alert.vue'),
- },
- directives: { SafeHtml },
- props: {
- pipelinesPath: {
- type: String,
- required: true,
- },
- configVariablesPath: {
- type: String,
- required: true,
- },
- defaultBranch: {
- type: String,
- required: true,
- },
- projectId: {
- type: String,
- required: true,
- },
- settingsLink: {
- type: String,
- required: true,
- },
- fileParams: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- refParam: {
- type: String,
- required: false,
- default: '',
- },
- variableParams: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- maxWarnings: {
- type: Number,
- required: true,
- },
- },
- data() {
- return {
- refValue: {
- shortName: this.refParam,
- },
- form: {},
- errorTitle: null,
- error: null,
- warnings: [],
- totalWarnings: 0,
- isWarningDismissed: false,
- isLoading: false,
- submitted: false,
- ccAlertDismissed: false,
- };
- },
- computed: {
- overMaxWarningsLimit() {
- return this.totalWarnings > this.maxWarnings;
- },
- warningsSummary() {
- return n__('%d warning found:', '%d warnings found:', this.warnings.length);
- },
- summaryMessage() {
- return this.overMaxWarningsLimit ? i18n.maxWarningsSummary : this.warningsSummary;
- },
- shouldShowWarning() {
- return this.warnings.length > 0 && !this.isWarningDismissed;
- },
- refShortName() {
- return this.refValue.shortName;
- },
- refFullName() {
- return this.refValue.fullName;
- },
- variables() {
- return this.form[this.refFullName]?.variables ?? [];
- },
- descriptions() {
- return this.form[this.refFullName]?.descriptions ?? {};
- },
- ccRequiredError() {
- return this.error === CC_VALIDATION_REQUIRED_ERROR && !this.ccAlertDismissed;
- },
- },
- watch: {
- refValue() {
- this.loadConfigVariablesForm();
- },
- },
- created() {
- // this is needed until we add support for ref type in url query strings
- // ensure default branch is called with full ref on load
- // https://gitlab.com/gitlab-org/gitlab/-/issues/287815
- if (this.refValue.shortName === this.defaultBranch) {
- this.refValue.fullName = `refs/heads/${this.refValue.shortName}`;
- }
-
- this.loadConfigVariablesForm();
- },
- methods: {
- addEmptyVariable(refValue) {
- const { variables } = this.form[refValue];
-
- const lastVar = variables[variables.length - 1];
- if (lastVar?.key === '' && lastVar?.value === '') {
- return;
- }
-
- variables.push({
- uniqueId: uniqueId(`var-${refValue}`),
- variable_type: VARIABLE_TYPE,
- key: '',
- value: '',
- });
- },
- setVariable(refValue, type, key, value) {
- const { variables } = this.form[refValue];
-
- const variable = variables.find((v) => v.key === key);
- if (variable) {
- variable.type = type;
- variable.value = value;
- } else {
- variables.push({
- uniqueId: uniqueId(`var-${refValue}`),
- key,
- value,
- variable_type: type,
- });
- }
- },
- setVariableType(key, type) {
- const { variables } = this.form[this.refFullName];
- const variable = variables.find((v) => v.key === key);
- variable.variable_type = type;
- },
- setVariableParams(refValue, type, paramsObj) {
- Object.entries(paramsObj).forEach(([key, value]) => {
- this.setVariable(refValue, type, key, value);
- });
- },
- removeVariable(index) {
- this.variables.splice(index, 1);
- },
- canRemove(index) {
- return index < this.variables.length - 1;
- },
- loadConfigVariablesForm() {
- // Skip when variables already cached in `form`
- if (this.form[this.refFullName]) {
- return;
- }
-
- this.fetchConfigVariables(this.refFullName || this.refShortName)
- .then(({ descriptions, params }) => {
- Vue.set(this.form, this.refFullName, {
- variables: [],
- descriptions,
- });
-
- // Add default variables from yml
- this.setVariableParams(this.refFullName, VARIABLE_TYPE, params);
- })
- .catch(() => {
- Vue.set(this.form, this.refFullName, {
- variables: [],
- descriptions: {},
- });
- })
- .finally(() => {
- // Add/update variables, e.g. from query string
- if (this.variableParams) {
- this.setVariableParams(this.refFullName, VARIABLE_TYPE, this.variableParams);
- }
- if (this.fileParams) {
- this.setVariableParams(this.refFullName, FILE_TYPE, this.fileParams);
- }
-
- // Adds empty var at the end of the form
- this.addEmptyVariable(this.refFullName);
- });
- },
- fetchConfigVariables(refValue) {
- this.isLoading = true;
-
- return backOff((next, stop) => {
- axios
- .get(this.configVariablesPath, {
- params: {
- sha: refValue,
- },
- })
- .then(({ data, status }) => {
- if (status === httpStatusCodes.NO_CONTENT) {
- next();
- } else {
- this.isLoading = false;
- stop(data);
- }
- })
- .catch((error) => {
- stop(error);
- });
- }, CONFIG_VARIABLES_TIMEOUT)
- .then((data) => {
- const params = {};
- const descriptions = {};
-
- Object.entries(data).forEach(([key, { value, description }]) => {
- if (description) {
- params[key] = value;
- descriptions[key] = description;
- }
- });
-
- return { params, descriptions };
- })
- .catch((error) => {
- this.isLoading = false;
-
- Sentry.captureException(error);
-
- return { params: {}, descriptions: {} };
- });
- },
- createPipeline() {
- this.submitted = true;
- this.ccAlertDismissed = false;
-
- return axios
- .post(this.pipelinesPath, {
- // send shortName as fall back for query params
- // https://gitlab.com/gitlab-org/gitlab/-/issues/287815
- ref: this.refValue.fullName || this.refShortName,
- variables_attributes: filterVariables(this.variables),
- })
- .then(({ data }) => {
- redirectTo(`${this.pipelinesPath}/${data.id}`);
- })
- .catch((err) => {
- // always re-enable submit button
- this.submitted = false;
-
- const {
- errors = [],
- warnings = [],
- total_warnings: totalWarnings = 0,
- } = err.response.data;
- const [error] = errors;
-
- this.reportError({
- title: i18n.submitErrorTitle,
- error,
- warnings,
- totalWarnings,
- });
- });
- },
- onRefsLoadingError(error) {
- this.reportError({ title: i18n.refsLoadingErrorTitle });
-
- Sentry.captureException(error);
- },
- reportError({ title = null, error = i18n.defaultError, warnings = [], totalWarnings = 0 }) {
- this.errorTitle = title;
- this.error = error;
- this.warnings = warnings;
- this.totalWarnings = totalWarnings;
- },
- dismissError() {
- this.ccAlertDismissed = true;
- this.error = null;
- },
- },
-};
-</script>
-
-<template>
- <gl-form @submit.prevent="createPipeline">
- <cc-validation-required-alert v-if="ccRequiredError" class="gl-pb-5" @dismiss="dismissError" />
- <gl-alert
- v-else-if="error"
- :title="errorTitle"
- :dismissible="false"
- variant="danger"
- class="gl-mb-4"
- data-testid="run-pipeline-error-alert"
- >
- <span v-safe-html="error"></span>
- </gl-alert>
- <gl-alert
- v-if="shouldShowWarning"
- :title="$options.i18n.warningTitle"
- variant="warning"
- class="gl-mb-4"
- data-testid="run-pipeline-warning-alert"
- @dismiss="isWarningDismissed = true"
- >
- <details>
- <summary>
- <gl-sprintf :message="summaryMessage">
- <template #total>
- {{ totalWarnings }}
- </template>
- <template #warningsDisplayed>
- {{ maxWarnings }}
- </template>
- </gl-sprintf>
- </summary>
- <p
- v-for="(warning, index) in warnings"
- :key="`warning-${index}`"
- data-testid="run-pipeline-warning"
- >
- {{ warning }}
- </p>
- </details>
- </gl-alert>
- <gl-form-group :label="s__('Pipeline|Run for branch name or tag')">
- <refs-dropdown v-model="refValue" @loadingError="onRefsLoadingError" />
- </gl-form-group>
-
- <gl-loading-icon v-if="isLoading" class="gl-mb-5" size="lg" />
-
- <gl-form-group v-else :label="s__('Pipeline|Variables')">
- <div
- v-for="(variable, index) in variables"
- :key="variable.uniqueId"
- class="gl-mb-3 gl-pb-2"
- data-testid="ci-variable-row"
- data-qa-selector="ci_variable_row_container"
- >
- <div
- class="gl-display-flex gl-align-items-stretch gl-flex-direction-column gl-md-flex-direction-row"
- >
- <gl-dropdown
- :text="$options.typeOptions[variable.variable_type]"
- :class="$options.formElementClasses"
- data-testid="pipeline-form-ci-variable-type"
- >
- <gl-dropdown-item
- v-for="type in Object.keys($options.typeOptions)"
- :key="type"
- @click="setVariableType(variable.key, type)"
- >
- {{ $options.typeOptions[type] }}
- </gl-dropdown-item>
- </gl-dropdown>
- <gl-form-input
- v-model="variable.key"
- :placeholder="s__('CiVariables|Input variable key')"
- :class="$options.formElementClasses"
- data-testid="pipeline-form-ci-variable-key"
- data-qa-selector="ci_variable_key_field"
- @change="addEmptyVariable(refFullName)"
- />
- <gl-form-textarea
- v-model="variable.value"
- :placeholder="s__('CiVariables|Input variable value')"
- class="gl-mb-3"
- :style="$options.textAreaStyle"
- :no-resize="false"
- data-testid="pipeline-form-ci-variable-value"
- data-qa-selector="ci_variable_value_field"
- />
-
- <template v-if="variables.length > 1">
- <gl-button
- v-if="canRemove(index)"
- class="gl-md-ml-3 gl-mb-3"
- data-testid="remove-ci-variable-row"
- variant="danger"
- category="secondary"
- :aria-label="$options.i18n.removeVariableLabel"
- @click="removeVariable(index)"
- >
- <gl-icon class="gl-mr-0! gl-display-none gl-md-display-block" name="clear" />
- <span class="gl-md-display-none">{{ $options.i18n.removeVariableLabel }}</span>
- </gl-button>
- <gl-button
- v-else
- class="gl-md-ml-3 gl-mb-3 gl-display-none gl-md-display-block gl-visibility-hidden"
- icon="clear"
- :aria-label="$options.i18n.removeVariableLabel"
- />
- </template>
- </div>
- <div v-if="descriptions[variable.key]" class="gl-text-gray-500 gl-mb-3">
- {{ descriptions[variable.key] }}
- </div>
- </div>
-
- <template #description
- ><gl-sprintf :message="$options.i18n.variablesDescription">
- <template #link="{ content }">
- <gl-link :href="settingsLink">{{ content }}</gl-link>
- </template>
- </gl-sprintf></template
- >
- </gl-form-group>
- <div class="gl-pt-5 gl-display-flex">
- <gl-button
- type="submit"
- category="primary"
- variant="confirm"
- class="js-no-auto-disable gl-mr-3"
- data-qa-selector="run_pipeline_button"
- data-testid="run_pipeline_button"
- :disabled="submitted"
- >{{ s__('Pipeline|Run pipeline') }}</gl-button
- >
- <gl-button :href="pipelinesPath">{{ __('Cancel') }}</gl-button>
- </div>
- </gl-form>
-</template>
diff --git a/app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue b/app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue
index a9af1181027..5692627abef 100644
--- a/app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue
+++ b/app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue
@@ -12,11 +12,11 @@ import {
GlLink,
GlSprintf,
GlLoadingIcon,
- GlSafeHtmlDirective as SafeHtml,
} from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { uniqueId } from 'lodash';
import Vue from 'vue';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { redirectTo } from '~/lib/utils/url_utility';
import { s__, __, n__ } from '~/locale';
import { VARIABLE_TYPE, FILE_TYPE, CC_VALIDATION_REQUIRED_ERROR } from '../constants';
@@ -400,11 +400,13 @@ export default {
:class="$options.formElementClasses"
class="gl-flex-grow-1 gl-mr-0!"
data-testid="pipeline-form-ci-variable-value-dropdown"
+ data-qa-selector="ci_variable_value_dropdown"
>
<gl-dropdown-item
v-for="value in predefinedValueOptions[variable.key]"
:key="value"
data-testid="pipeline-form-ci-variable-value-dropdown-items"
+ data-qa-selector="ci_variable_value_dropdown_item"
@click="setVariableAttribute(variable.key, 'value', value)"
>
{{ value }}
diff --git a/app/assets/javascripts/pipeline_new/index.js b/app/assets/javascripts/pipeline_new/index.js
index 60b4c93d1d5..71c76aeab36 100644
--- a/app/assets/javascripts/pipeline_new/index.js
+++ b/app/assets/javascripts/pipeline_new/index.js
@@ -1,53 +1,9 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
-import LegacyPipelineNewForm from './components/legacy_pipeline_new_form.vue';
import PipelineNewForm from './components/pipeline_new_form.vue';
import { resolvers } from './graphql/resolvers';
-const mountLegacyPipelineNewForm = (el) => {
- const {
- // provide/inject
- projectRefsEndpoint,
-
- // props
- configVariablesPath,
- defaultBranch,
- fileParam,
- maxWarnings,
- pipelinesPath,
- projectId,
- refParam,
- settingsLink,
- varParam,
- } = el.dataset;
-
- const variableParams = JSON.parse(varParam);
- const fileParams = JSON.parse(fileParam);
-
- return new Vue({
- el,
- provide: {
- projectRefsEndpoint,
- },
- render(createElement) {
- return createElement(LegacyPipelineNewForm, {
- props: {
- configVariablesPath,
- defaultBranch,
- fileParams,
- maxWarnings: Number(maxWarnings),
- pipelinesPath,
- projectId,
- refParam,
- settingsLink,
- variableParams,
- },
- });
- },
- });
-};
-
const mountPipelineNewForm = (el) => {
const {
// provide/inject
@@ -101,9 +57,5 @@ const mountPipelineNewForm = (el) => {
export default () => {
const el = document.getElementById('js-new-pipeline');
- if (gon.features?.runPipelineGraphql) {
- mountPipelineNewForm(el);
- } else {
- mountLegacyPipelineNewForm(el);
- }
+ mountPipelineNewForm(el);
};
diff --git a/app/assets/javascripts/pipeline_wizard/components/step_nav.vue b/app/assets/javascripts/pipeline_wizard/components/step_nav.vue
index 8f9198855c6..e3d825bbcc7 100644
--- a/app/assets/javascripts/pipeline_wizard/components/step_nav.vue
+++ b/app/assets/javascripts/pipeline_wizard/components/step_nav.vue
@@ -27,12 +27,13 @@ export default {
</script>
<template>
- <div>
+ <div class="gl-display-flex">
<slot name="before"></slot>
<gl-button
v-if="showBackButton"
category="secondary"
data-testid="back-button"
+ class="gl-mr-3"
@click="$emit('back')"
>
{{ __('Back') }}
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
index f822e2c0874..4d7596e6e16 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
@@ -148,12 +148,13 @@ export default {
reportMessageToSentry(
this.$options.name,
- `| type: ${LOAD_FAILURE} , info: ${serializeLoadErrors(err)}`,
+ `| type: ${LOAD_FAILURE} , info: ${JSON.stringify(err)}`,
{
+ graphViewType: this.graphViewType,
+ graphqlResourceEtag: this.graphqlResourceEtag,
+ metricsPath: this.metricsPath,
projectPath: this.pipelineProjectPath,
pipelineIid: this.pipelineIid,
- pipelineStages: this.pipeline?.stages?.length || 0,
- nbOfDownstreams: this.pipeline?.downstream?.length || 0,
},
);
},
diff --git a/app/assets/javascripts/pipelines/components/graph/job_item.vue b/app/assets/javascripts/pipelines/components/graph/job_item.vue
index 377f21b299f..4f2be27486c 100644
--- a/app/assets/javascripts/pipelines/components/graph/job_item.vue
+++ b/app/assets/javascripts/pipelines/components/graph/job_item.vue
@@ -252,7 +252,7 @@ export default {
@click="jobItemClick"
@mouseout="hideTooltips"
>
- <div class="ci-job-name-component gl-display-flex gl-align-items-center">
+ <div class="gl-display-flex gl-align-items-center gl-flex-grow-1">
<ci-icon :size="24" :status="job.status" class="gl-line-height-0" />
<div class="gl-pl-3 gl-pr-3 gl-display-flex gl-flex-direction-column gl-pipeline-job-width">
<div class="gl-text-truncate gl-pr-9 gl-line-height-normal">{{ job.name }}</div>
diff --git a/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue b/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue
index 18607bfae1c..c56537f4039 100644
--- a/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue
+++ b/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue
@@ -1,5 +1,6 @@
<script>
-import { GlButton, GlLink, GlSafeHtmlDirective, GlTableLite } from '@gitlab/ui';
+import { GlButton, GlLink, GlTableLite } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { __, s__ } from '~/locale';
import { createAlert } from '~/flash';
import { redirectTo } from '~/lib/utils/url_utility';
@@ -17,7 +18,7 @@ export default {
GlTableLite,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
failedJobs: {
diff --git a/app/assets/javascripts/pipelines/components/jobs_shared/action_component.vue b/app/assets/javascripts/pipelines/components/jobs_shared/action_component.vue
index 7ee5ec48f44..387b01aee7e 100644
--- a/app/assets/javascripts/pipelines/components/jobs_shared/action_component.vue
+++ b/app/assets/javascripts/pipelines/components/jobs_shared/action_component.vue
@@ -70,7 +70,6 @@ export default {
axios
.post(`${this.link}.json`)
.then(() => {
- this.isDisabled = false;
this.isLoading = false;
this.$emit('pipelineActionRequestComplete');
diff --git a/app/assets/javascripts/pipelines/components/jobs_shared/job_name_component.vue b/app/assets/javascripts/pipelines/components/jobs_shared/job_name_component.vue
index f4fc6893520..1c7f5a7476d 100644
--- a/app/assets/javascripts/pipelines/components/jobs_shared/job_name_component.vue
+++ b/app/assets/javascripts/pipelines/components/jobs_shared/job_name_component.vue
@@ -29,7 +29,7 @@ export default {
};
</script>
<template>
- <span class="ci-job-name-component mw-100 gl-display-flex gl-align-items-center">
+ <span class="mw-100 gl-display-flex gl-align-items-center gl-flex-grow-1">
<ci-icon :size="iconSize" :status="status" class="gl-line-height-0" />
<span class="gl-text-truncate mw-70p gl-pl-3 gl-display-inline-block">
{{ name }}
diff --git a/app/assets/javascripts/pipelines/components/pipeline_mini_graph/job_item.vue b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/job_item.vue
index 211c5f117c7..51b46f25048 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_mini_graph/job_item.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/job_item.vue
@@ -137,9 +137,6 @@ export default {
hideTooltips() {
this.$root.$emit(BV_HIDE_TOOLTIP);
},
- pipelineActionRequestComplete() {
- this.$emit('pipelineActionRequestComplete');
- },
},
};
</script>
@@ -163,7 +160,7 @@ export default {
@click.stop="hideTooltips"
@mouseout="hideTooltips"
>
- <job-name-component :name="job.name" :status="job.status" :icon-size="24" />
+ <job-name-component :name="job.name" :status="job.status" />
</gl-link>
<div
@@ -175,7 +172,7 @@ export default {
data-testid="job-without-link"
@mouseout="hideTooltips"
>
- <job-name-component :name="job.name" :status="job.status" :icon-size="24" />
+ <job-name-component :name="job.name" :status="job.status" />
</div>
<action-component
@@ -184,7 +181,6 @@ export default {
:link="status.action.path"
:action-icon="status.action.icon"
data-qa-selector="action_button"
- @pipelineActionRequestComplete="pipelineActionRequestComplete"
/>
</div>
</template>
diff --git a/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue
index 993fa121d89..827adf9f7f7 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue
@@ -35,11 +35,6 @@ export default {
required: true,
default: () => [],
},
- stagesClass: {
- type: [Array, Object, String],
- required: false,
- default: '',
- },
updateDropdown: {
type: Boolean,
required: false,
@@ -56,15 +51,10 @@ export default {
return Boolean(this.downstreamPipelines.length);
},
},
- methods: {
- onPipelineActionRequestComplete() {
- this.$emit('pipelineActionRequestComplete');
- },
- },
};
</script>
<template>
- <div class="stage-cell" data-testid="pipeline-mini-graph">
+ <div data-testid="pipeline-mini-graph">
<linked-pipelines-mini-list
v-if="upstreamPipeline"
:triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
@@ -82,9 +72,7 @@ export default {
:is-merge-train="isMergeTrain"
:stages="stages"
:update-dropdown="updateDropdown"
- :stages-class="stagesClass"
data-testid="pipeline-stages"
- @pipelineActionRequestComplete="onPipelineActionRequestComplete"
@miniGraphStageClick="$emit('miniGraphStageClick')"
/>
<gl-icon
diff --git a/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stage.vue b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stage.vue
index ba150919e58..ec42b738e03 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stage.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stage.vue
@@ -100,13 +100,6 @@ export default {
});
});
},
- pipelineActionRequestComplete() {
- // close the dropdown in MR widget
- this.$refs.dropdown.hide();
-
- // warn the pipelines table to update
- this.$emit('pipelineActionRequestComplete');
- },
stageAriaLabel(title) {
return sprintf(__('View Stage: %{title}'), { title });
},
@@ -149,7 +142,7 @@ export default {
class="js-builds-dropdown-list scrollable-menu"
data-testid="mini-pipeline-graph-dropdown-menu-list"
>
- <div class="gl--flex-center gl-border-b gl-font-weight-bold gl-pb-3">
+ <div class="gl--flex-center gl-border-b gl-font-weight-bold gl-mb-3 gl-pb-3">
<span class="gl-mr-1">{{ $options.i18n.stage }}</span>
<span data-testid="pipeline-stage-dropdown-menu-title">{{ stageName }}</span>
</div>
@@ -158,11 +151,10 @@ export default {
:dropdown-length="dropdownContent.length"
:job="job"
css-class-job-name="mini-pipeline-graph-dropdown-item"
- @pipelineActionRequestComplete="pipelineActionRequestComplete"
/>
</li>
<template v-if="isMergeTrain">
- <li class="gl-new-dropdown-divider" role="presentation">
+ <li class="gl-dropdown-divider" role="presentation">
<hr role="separator" aria-orientation="horizontal" class="dropdown-divider" />
</li>
<li>
diff --git a/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stages.vue b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stages.vue
index e965dc5e6b0..ba549d9b423 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stages.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stages.vue
@@ -17,22 +17,12 @@ export default {
required: false,
default: false,
},
- stagesClass: {
- type: [Array, Object, String],
- required: false,
- default: '',
- },
isMergeTrain: {
type: Boolean,
required: false,
default: false,
},
},
- methods: {
- onPipelineActionRequestComplete() {
- this.$emit('pipelineActionRequestComplete');
- },
- },
};
</script>
<template>
@@ -40,14 +30,12 @@ export default {
<div
v-for="stage in stages"
:key="stage.name"
- :class="stagesClass"
- class="dropdown gl-display-inline-block gl-mr-2 gl-my-2 gl-vertical-align-middle stage-container"
+ class="pipeline-mini-graph-stage-container dropdown gl-display-inline-block gl-mr-2 gl-my-2 gl-vertical-align-middle"
>
<pipeline-stage
:stage="stage"
:update-dropdown="updateDropdown"
:is-merge-train="isMergeTrain"
- @pipelineActionRequestComplete="onPipelineActionRequestComplete"
@miniGraphStageClick="$emit('miniGraphStageClick')"
/>
</div>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates.vue b/app/assets/javascripts/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates.vue
index 3eafb36bd1d..03a2eac89e4 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates.vue
@@ -8,7 +8,7 @@ import {
RUNNERS_DOCUMENTATION_LINK_CLICKED_EVENT,
RUNNERS_SETTINGS_BUTTON_CLICKED_EVENT,
I18N,
-} from '~/pipeline_editor/constants';
+} from '~/ci/pipeline_editor/constants';
import Tracking from '~/tracking';
import { helpPagePath } from '~/helpers/help_page_helper';
import { isExperimentVariant } from '~/experimentation/utils';
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_filtered_search.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_filtered_search.vue
index af089aebbbe..7dc1e60610e 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_filtered_search.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_filtered_search.vue
@@ -3,7 +3,7 @@ import { GlFilteredSearch } from '@gitlab/ui';
import { map } from 'lodash';
import { s__ } from '~/locale';
import Tracking from '~/tracking';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
+import { OPERATORS_IS } from '~/vue_shared/components/filtered_search_bar/constants';
import { TRACKING_CATEGORIES } from '../../constants';
import PipelineBranchNameToken from './tokens/pipeline_branch_name_token.vue';
import PipelineSourceToken from './tokens/pipeline_source_token.vue';
@@ -54,7 +54,7 @@ export default {
title: s__('Pipeline|Trigger author'),
unique: true,
token: PipelineTriggerAuthorToken,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
projectId: this.projectId,
},
{
@@ -63,7 +63,7 @@ export default {
title: s__('Pipeline|Branch name'),
unique: true,
token: PipelineBranchNameToken,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
projectId: this.projectId,
defaultBranchName: this.defaultBranchName,
disabled: this.selectedTypes.includes(this.$options.tagType),
@@ -74,7 +74,7 @@ export default {
title: s__('Pipeline|Tag name'),
unique: true,
token: PipelineTagNameToken,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
projectId: this.projectId,
disabled: this.selectedTypes.includes(this.$options.branchType),
},
@@ -84,7 +84,7 @@ export default {
title: s__('Pipeline|Status'),
unique: true,
token: PipelineStatusToken,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
},
{
type: this.$options.sourceType,
@@ -92,7 +92,7 @@ export default {
title: s__('Pipeline|Source'),
unique: true,
token: PipelineSourceToken,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
},
];
},
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
index f6e46c090d3..346f5735576 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
@@ -124,9 +124,6 @@ export default {
eventHub.$emit('postAction', this.endpoint);
this.cancelingPipeline = this.pipelineId;
},
- onPipelineActionRequestComplete() {
- eventHub.$emit('refreshPipelinesTable');
- },
trackPipelineMiniGraph() {
this.track('click_minigraph', { label: TRACKING_CATEGORIES.table });
},
@@ -179,7 +176,6 @@ export default {
:stages="item.details.stages"
:update-dropdown="updateGraphDropdown"
:upstream-pipeline="item.triggered_by"
- @pipelineActionRequestComplete="onPipelineActionRequestComplete"
@miniGraphStageClick="trackPipelineMiniGraph"
/>
</template>
diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_reports.vue b/app/assets/javascripts/pipelines/components/test_reports/test_reports.vue
index e5666f7a658..3f2c013d44a 100644
--- a/app/assets/javascripts/pipelines/components/test_reports/test_reports.vue
+++ b/app/assets/javascripts/pipelines/components/test_reports/test_reports.vue
@@ -1,8 +1,6 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
-import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import createTestReportsStore from '../../stores/test_reports';
import EmptyState from './empty_state.vue';
import TestSuiteTable from './test_suite_table.vue';
import TestSummary from './test_summary.vue';
@@ -17,7 +15,6 @@ export default {
TestSummary,
TestSummaryTable,
},
- mixins: [glFeatureFlagMixin()],
inject: ['blobPath', 'summaryEndpoint', 'suiteEndpoint'],
computed: {
...mapState('testReports', ['isLoading', 'selectedSuiteIndex', 'testReports']),
@@ -31,17 +28,6 @@ export default {
},
},
created() {
- if (!this.glFeatures.pipelineTabsVue) {
- this.$store.registerModule(
- 'testReports',
- createTestReportsStore({
- blobPath: this.blobPath,
- summaryEndpoint: this.summaryEndpoint,
- suiteEndpoint: this.suiteEndpoint,
- }),
- );
- }
-
this.fetchSummary();
},
methods: {
diff --git a/app/assets/javascripts/pipelines/mixins/pipelines_mixin.js b/app/assets/javascripts/pipelines/mixins/pipelines_mixin.js
index 9602ca1ba88..07551c2342f 100644
--- a/app/assets/javascripts/pipelines/mixins/pipelines_mixin.js
+++ b/app/assets/javascripts/pipelines/mixins/pipelines_mixin.js
@@ -55,7 +55,6 @@ export default {
eventHub.$on('retryPipeline', this.postAction);
eventHub.$on('clickedDropdown', this.updateTable);
eventHub.$on('updateTable', this.updateTable);
- eventHub.$on('refreshPipelinesTable', this.fetchPipelines);
eventHub.$on('runMergeRequestPipeline', this.runMergeRequestPipeline);
},
beforeDestroy() {
@@ -63,7 +62,6 @@ export default {
eventHub.$off('retryPipeline', this.postAction);
eventHub.$off('clickedDropdown', this.updateTable);
eventHub.$off('updateTable', this.updateTable);
- eventHub.$off('refreshPipelinesTable', this.fetchPipelines);
eventHub.$off('runMergeRequestPipeline', this.runMergeRequestPipeline);
},
destroyed() {
diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
index 1bbdd3625be..f00378733fc 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
@@ -1,36 +1,33 @@
import VueRouter from 'vue-router';
import { createAlert } from '~/flash';
-import { __, s__ } from '~/locale';
-import createDagApp from './pipeline_details_dag';
-import { createPipelinesDetailApp } from './pipeline_details_graph';
+import { __ } from '~/locale';
import { createPipelineHeaderApp } from './pipeline_details_header';
-import { createPipelineJobsApp } from './pipeline_details_jobs';
-import { createPipelineFailedJobsApp } from './pipeline_details_failed_jobs';
import { apolloProvider } from './pipeline_shared_client';
-import { createTestDetails } from './pipeline_test_details';
const SELECTORS = {
- PIPELINE_DETAILS: '.js-pipeline-details-vue',
- PIPELINE_GRAPH: '#js-pipeline-graph-vue',
PIPELINE_HEADER: '#js-pipeline-header-vue',
PIPELINE_TABS: '#js-pipeline-tabs',
- PIPELINE_TESTS: '#js-pipeline-tests-detail',
- PIPELINE_JOBS: '#js-pipeline-jobs-vue',
- PIPELINE_FAILED_JOBS: '#js-pipeline-failed-jobs-vue',
};
export default async function initPipelineDetailsBundle() {
- const { dataset } = document.querySelector(SELECTORS.PIPELINE_DETAILS);
+ const { dataset: headerDataset } = document.querySelector(SELECTORS.PIPELINE_HEADER);
try {
- createPipelineHeaderApp(SELECTORS.PIPELINE_HEADER, apolloProvider, dataset.graphqlResourceEtag);
+ createPipelineHeaderApp(
+ SELECTORS.PIPELINE_HEADER,
+ apolloProvider,
+ headerDataset.graphqlResourceEtag,
+ );
} catch {
createAlert({
message: __('An error occurred while loading a section of this page.'),
});
}
- if (gon.features?.pipelineTabsVue) {
+ const tabsEl = document.querySelector(SELECTORS.PIPELINE_TABS);
+
+ if (tabsEl) {
+ const { dataset } = tabsEl;
const { createAppOptions } = await import('ee_else_ce/pipelines/pipeline_tabs');
const { createPipelineTabs } = await import('./pipeline_tabs');
const { routes } = await import('ee_else_ce/pipelines/routes');
@@ -49,45 +46,5 @@ export default async function initPipelineDetailsBundle() {
message: __('An error occurred while loading a section of this page.'),
});
}
- } else {
- try {
- createPipelinesDetailApp(SELECTORS.PIPELINE_GRAPH, apolloProvider, dataset);
- } catch {
- createAlert({
- message: __('An error occurred while loading the pipeline.'),
- });
- }
-
- try {
- createDagApp(apolloProvider);
- } catch {
- createAlert({
- message: __('An error occurred while loading the Needs tab.'),
- });
- }
-
- try {
- createTestDetails(SELECTORS.PIPELINE_TESTS);
- } catch {
- createAlert({
- message: __('An error occurred while loading the Test Reports tab.'),
- });
- }
-
- try {
- createPipelineJobsApp(SELECTORS.PIPELINE_JOBS);
- } catch {
- createAlert({
- message: __('An error occurred while loading the Jobs tab.'),
- });
- }
-
- try {
- createPipelineFailedJobsApp(SELECTORS.PIPELINE_FAILED_JOBS);
- } catch {
- createAlert({
- message: s__('Jobs|An error occurred while loading the Failed Jobs tab.'),
- });
- }
}
}
diff --git a/app/assets/javascripts/pipelines/pipeline_details_dag.js b/app/assets/javascripts/pipelines/pipeline_details_dag.js
deleted file mode 100644
index b2cb0457c4d..00000000000
--- a/app/assets/javascripts/pipelines/pipeline_details_dag.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import Dag from './components/dag/dag.vue';
-
-Vue.use(VueApollo);
-
-const createDagApp = (apolloProvider) => {
- const el = document.querySelector('#js-pipeline-dag-vue');
-
- if (!el) {
- return;
- }
-
- const {
- aboutDagDocPath,
- dagDocPath,
- emptySvgPath,
- pipelineProjectPath,
- pipelineIid,
- } = el.dataset;
-
- // eslint-disable-next-line no-new
- new Vue({
- el,
- components: {
- Dag,
- },
- apolloProvider,
- provide: {
- aboutDagDocPath,
- dagDocPath,
- emptySvgPath,
- pipelineProjectPath,
- pipelineIid,
- },
- render(createElement) {
- return createElement('dag', {});
- },
- });
-};
-
-export default createDagApp;
diff --git a/app/assets/javascripts/pipelines/pipeline_details_failed_jobs.js b/app/assets/javascripts/pipelines/pipeline_details_failed_jobs.js
deleted file mode 100644
index 7bf3b64bf47..00000000000
--- a/app/assets/javascripts/pipelines/pipeline_details_failed_jobs.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createDefaultClient from '~/lib/graphql';
-import FailedJobsApp from './components/jobs/failed_jobs_app.vue';
-
-Vue.use(VueApollo);
-
-const apolloProvider = new VueApollo({
- defaultClient: createDefaultClient(),
-});
-
-export const createPipelineFailedJobsApp = (selector) => {
- const containerEl = document.querySelector(selector);
-
- if (!containerEl) {
- return false;
- }
-
- const { fullPath, pipelineIid, failedJobsSummaryData } = containerEl.dataset;
-
- return new Vue({
- el: containerEl,
- apolloProvider,
- provide: {
- fullPath,
- pipelineIid,
- },
- render(createElement) {
- return createElement(FailedJobsApp, {
- props: {
- failedJobsSummary: JSON.parse(failedJobsSummaryData),
- },
- });
- },
- });
-};
diff --git a/app/assets/javascripts/pipelines/pipeline_details_graph.js b/app/assets/javascripts/pipelines/pipeline_details_graph.js
deleted file mode 100644
index 9dd5cd7b281..00000000000
--- a/app/assets/javascripts/pipelines/pipeline_details_graph.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import PipelineGraphWrapper from './components/graph/graph_component_wrapper.vue';
-import { reportToSentry } from './utils';
-
-Vue.use(VueApollo);
-
-const createPipelinesDetailApp = (
- selector,
- apolloProvider,
- { pipelineProjectPath, pipelineIid, metricsPath, graphqlResourceEtag } = {},
-) => {
- // eslint-disable-next-line no-new
- new Vue({
- el: selector,
- components: {
- PipelineGraphWrapper,
- },
- apolloProvider,
- provide: {
- metricsPath,
- pipelineProjectPath,
- pipelineIid,
- graphqlResourceEtag,
- },
- errorCaptured(err, _vm, info) {
- reportToSentry('pipeline_details_graph', `error: ${err}, info: ${info}`);
- },
- render(createElement) {
- return createElement(PipelineGraphWrapper);
- },
- });
-};
-
-export { createPipelinesDetailApp };
diff --git a/app/assets/javascripts/pipelines/pipeline_details_jobs.js b/app/assets/javascripts/pipelines/pipeline_details_jobs.js
deleted file mode 100644
index a1294a484f0..00000000000
--- a/app/assets/javascripts/pipelines/pipeline_details_jobs.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import { GlToast } from '@gitlab/ui';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createDefaultClient from '~/lib/graphql';
-import JobsApp from './components/jobs/jobs_app.vue';
-
-Vue.use(VueApollo);
-Vue.use(GlToast);
-
-const apolloProvider = new VueApollo({
- defaultClient: createDefaultClient(),
-});
-
-export const createPipelineJobsApp = (selector) => {
- const containerEl = document.querySelector(selector);
-
- if (!containerEl) {
- return false;
- }
-
- const { fullPath, pipelineIid } = containerEl.dataset;
-
- return new Vue({
- el: containerEl,
- apolloProvider,
- provide: {
- fullPath,
- pipelineIid,
- },
- render(createElement) {
- return createElement(JobsApp);
- },
- });
-};
diff --git a/app/assets/javascripts/pipelines/pipeline_test_details.js b/app/assets/javascripts/pipelines/pipeline_test_details.js
deleted file mode 100644
index fe4ca8e9529..00000000000
--- a/app/assets/javascripts/pipelines/pipeline_test_details.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import Vue from 'vue';
-import Vuex from 'vuex';
-import { parseBoolean } from '~/lib/utils/common_utils';
-import Translate from '~/vue_shared/translate';
-import TestReports from './components/test_reports/test_reports.vue';
-
-Vue.use(Vuex);
-Vue.use(Translate);
-
-export const createTestDetails = (selector) => {
- const el = document.querySelector(selector);
- const {
- blobPath,
- emptyStateImagePath,
- hasTestReport,
- summaryEndpoint,
- suiteEndpoint,
- artifactsExpiredImagePath,
- } = el?.dataset || {};
-
- // eslint-disable-next-line no-new
- new Vue({
- el,
- components: {
- TestReports,
- },
- provide: {
- emptyStateImagePath,
- artifactsExpiredImagePath,
- hasTestReport: parseBoolean(hasTestReport),
- blobPath,
- summaryEndpoint,
- suiteEndpoint,
- },
- store: new Vuex.Store(),
- render(createElement) {
- return createElement('test-reports');
- },
- });
-};
diff --git a/app/assets/javascripts/popovers/components/popovers.vue b/app/assets/javascripts/popovers/components/popovers.vue
index a758503b56b..7ec54231e65 100644
--- a/app/assets/javascripts/popovers/components/popovers.vue
+++ b/app/assets/javascripts/popovers/components/popovers.vue
@@ -1,5 +1,6 @@
<script>
-import { GlPopover, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlPopover } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
const newPopover = (element) => {
const { content, html, placement, title, triggers = 'focus' } = element.dataset;
@@ -19,7 +20,7 @@ export default {
GlPopover,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
data() {
return {
diff --git a/app/assets/javascripts/profile/account/components/update_username.vue b/app/assets/javascripts/profile/account/components/update_username.vue
index b038b78088f..51e62984715 100644
--- a/app/assets/javascripts/profile/account/components/update_username.vue
+++ b/app/assets/javascripts/profile/account/components/update_username.vue
@@ -1,6 +1,7 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml, GlButton, GlModal, GlModalDirective } from '@gitlab/ui';
+import { GlButton, GlModal, GlModalDirective } from '@gitlab/ui';
import { escape } from 'lodash';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { createAlert, VARIANT_INFO } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { __, s__, sprintf } from '~/locale';
diff --git a/app/assets/javascripts/projects/commit/components/branches_dropdown.vue b/app/assets/javascripts/projects/commit/components/branches_dropdown.vue
index 52da8aaba4d..a037e721677 100644
--- a/app/assets/javascripts/projects/commit/components/branches_dropdown.vue
+++ b/app/assets/javascripts/projects/commit/components/branches_dropdown.vue
@@ -28,6 +28,11 @@ export default {
required: false,
default: '',
},
+ blanked: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
i18n: {
noResultsMessage: I18N_NO_RESULTS_MESSAGE,
@@ -36,7 +41,7 @@ export default {
},
data() {
return {
- searchTerm: this.value,
+ searchTerm: this.blanked ? '' : this.value,
};
},
computed: {
diff --git a/app/assets/javascripts/projects/commit/components/form_modal.vue b/app/assets/javascripts/projects/commit/components/form_modal.vue
index d9aaa574fec..1febe8ceaab 100644
--- a/app/assets/javascripts/projects/commit/components/form_modal.vue
+++ b/app/assets/javascripts/projects/commit/components/form_modal.vue
@@ -41,6 +41,11 @@ export default {
required: false,
default: false,
},
+ isRevert: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
primaryActionEventName: {
type: String,
required: false,
@@ -150,7 +155,12 @@ export default {
>
<input id="start_branch" type="hidden" name="start_branch" :value="branch" />
- <branches-dropdown class="gl-w-half" :value="branch" @selectBranch="setBranch" />
+ <branches-dropdown
+ class="gl-w-half"
+ :value="branch"
+ :blanked="isRevert"
+ @selectBranch="setBranch"
+ />
</gl-form-group>
<gl-form-checkbox
diff --git a/app/assets/javascripts/projects/commit/init_revert_commit_modal.js b/app/assets/javascripts/projects/commit/init_revert_commit_modal.js
index 849b2f4858c..41be71932e5 100644
--- a/app/assets/javascripts/projects/commit/init_revert_commit_modal.js
+++ b/app/assets/javascripts/projects/commit/init_revert_commit_modal.js
@@ -49,6 +49,7 @@ export default function initInviteMembersModal(primaryActionEventName) {
i18n: { ...I18N_REVERT_MODAL, ...I18N_MODAL },
openModal: OPEN_REVERT_MODAL,
modalId: REVERT_MODAL_ID,
+ isRevert: true,
primaryActionEventName,
},
}),
diff --git a/app/assets/javascripts/projects/commits/index.js b/app/assets/javascripts/projects/commits/index.js
index 03b94fde0f3..53169f689c9 100644
--- a/app/assets/javascripts/projects/commits/index.js
+++ b/app/assets/javascripts/projects/commits/index.js
@@ -1,11 +1,13 @@
import Vue from 'vue';
import Vuex from 'vuex';
+import { visitUrl } from '~/lib/utils/url_utility';
+import RefSelector from '~/ref/components/ref_selector.vue';
import AuthorSelectApp from './components/author_select.vue';
import store from './store';
Vue.use(Vuex);
-export default (el) => {
+export const mountCommits = (el) => {
if (!el) {
return null;
}
@@ -24,3 +26,30 @@ export default (el) => {
},
});
};
+
+export const initCommitsRefSwitcher = () => {
+ const el = document.getElementById('js-project-commits-ref-switcher');
+ const COMMITS_PATH_REGEX = /^(.*?)\/-\/commits/g;
+
+ if (!el) return false;
+
+ const { projectId, ref, commitsPath } = el.dataset;
+ const commitsPathPrefix = commitsPath.match(COMMITS_PATH_REGEX)?.[0];
+
+ return new Vue({
+ el,
+ render(createElement) {
+ return createElement(RefSelector, {
+ props: {
+ projectId,
+ value: ref,
+ },
+ on: {
+ input(selected) {
+ visitUrl(`${commitsPathPrefix}/${selected}`);
+ },
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/projects/compare/components/repo_dropdown.vue b/app/assets/javascripts/projects/compare/components/repo_dropdown.vue
index ba1e00a2b36..c00e75db722 100644
--- a/app/assets/javascripts/projects/compare/components/repo_dropdown.vue
+++ b/app/assets/javascripts/projects/compare/components/repo_dropdown.vue
@@ -57,7 +57,7 @@ export default {
<gl-dropdown
:text="selectedProject.name"
:header-text="s__(`CompareRevisions|Select target project`)"
- class="gl-w-full gl-font-monospace gl-sm-pr-3"
+ class="gl-w-full gl-font-monospace"
toggle-class="gl-min-w-0"
:disabled="disableRepoDropdown"
>
diff --git a/app/assets/javascripts/projects/compare/components/revision_card.vue b/app/assets/javascripts/projects/compare/components/revision_card.vue
index d6ada24604d..162aca44f9d 100644
--- a/app/assets/javascripts/projects/compare/components/revision_card.vue
+++ b/app/assets/javascripts/projects/compare/components/revision_card.vue
@@ -43,7 +43,7 @@ export default {
<h2 class="gl-font-size-h2">
{{ s__(`CompareRevisions|${revisionText}`) }}
</h2>
- <div class="gl-sm-display-flex gl-align-items-center">
+ <div class="gl-sm-display-flex gl-align-items-center gl-gap-3">
<repo-dropdown
class="gl-sm-w-half"
:params-name="paramsName"
diff --git a/app/assets/javascripts/projects/default_project_templates.js b/app/assets/javascripts/projects/default_project_templates.js
index 3671b24b502..a44855c14d5 100644
--- a/app/assets/javascripts/projects/default_project_templates.js
+++ b/app/assets/javascripts/projects/default_project_templates.js
@@ -113,4 +113,12 @@ export default {
text: s__('ProjectTemplates|Jsonnet for Dynamic Child Pipelines'),
icon: '.template-option .icon-gitlab_logo',
},
+ bridgetown: {
+ text: s__('ProjectTemplates|Pages/Bridgetown'),
+ icon: '.template-option .icon-gitlab_logo',
+ },
+ typo3_distribution: {
+ text: s__('ProjectTemplates|TYPO3 Distribution'),
+ icon: '.template-option .icon-typo3',
+ },
};
diff --git a/app/assets/javascripts/projects/new/components/app.vue b/app/assets/javascripts/projects/new/components/app.vue
index 59ca393fe92..3100029eb31 100644
--- a/app/assets/javascripts/projects/new/components/app.vue
+++ b/app/assets/javascripts/projects/new/components/app.vue
@@ -3,7 +3,7 @@ import createFromTemplateIllustration from '@gitlab/svgs/dist/illustrations/proj
import blankProjectIllustration from '@gitlab/svgs/dist/illustrations/project-create-new-sm.svg';
import importProjectIllustration from '@gitlab/svgs/dist/illustrations/project-import-sm.svg';
import ciCdProjectIllustration from '@gitlab/svgs/dist/illustrations/project-run-CICD-pipelines-sm.svg';
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { s__ } from '~/locale';
import NewNamespacePage from '~/vue_shared/new_namespace/new_namespace_page.vue';
import NewProjectPushTipPopover from './new_project_push_tip_popover.vue';
diff --git a/app/assets/javascripts/projects/new/components/new_project_url_select.vue b/app/assets/javascripts/projects/new/components/new_project_url_select.vue
index eccfb3d844c..d6d88b5b297 100644
--- a/app/assets/javascripts/projects/new/components/new_project_url_select.vue
+++ b/app/assets/javascripts/projects/new/components/new_project_url_select.vue
@@ -46,7 +46,15 @@ export default {
debounce: DEBOUNCE_DELAY,
},
},
- inject: ['namespaceFullPath', 'namespaceId', 'rootUrl', 'trackLabel', 'userNamespaceId'],
+ inject: [
+ 'namespaceFullPath',
+ 'namespaceId',
+ 'rootUrl',
+ 'trackLabel',
+ 'userNamespaceId',
+ 'inputName',
+ 'inputId',
+ ],
data() {
return {
currentUser: {},
@@ -124,6 +132,11 @@ export default {
}
: this.$options.emptyNameSpace;
},
+ trackDropdownShow() {
+ if (this.trackLabel) {
+ this.track('activate_form_input', { label: this.trackLabel, property: 'project_path' });
+ }
+ },
},
emptyNameSpace: {
id: undefined,
@@ -145,7 +158,7 @@ export default {
class="js-group-namespace-dropdown gl-flex-grow-1"
:toggle-class="`gl-rounded-top-right-base! gl-rounded-bottom-right-base! gl-w-20 ${dropdownPlaceholderClass}`"
data-qa-selector="select_namespace_dropdown"
- @show="track('activate_form_input', { label: trackLabel, property: 'project_path' })"
+ @show="trackDropdownShow"
@shown="handleDropdownShown"
>
<template #button-text>
@@ -173,7 +186,7 @@ export default {
{{ group.fullPath }}
</gl-dropdown-item>
</template>
- <template v-if="hasNamespaceMatches">
+ <template v-if="hasNamespaceMatches && userNamespaceId">
<gl-dropdown-section-header>{{ __('Users') }}</gl-dropdown-section-header>
<gl-dropdown-item @click="handleDropdownItemClick(userNamespace)">
{{ userNamespace.fullPath }}
@@ -186,9 +199,9 @@ export default {
<input type="hidden" name="project[selected_namespace_id]" :value="selectedNamespace.id" />
<input
- id="project_namespace_id"
+ :id="inputId"
type="hidden"
- name="project[namespace_id]"
+ :name="inputName"
:value="selectedNamespace.id || userNamespaceId"
/>
</gl-button-group>
diff --git a/app/assets/javascripts/projects/new/constants.js b/app/assets/javascripts/projects/new/constants.js
index e52a84dc07e..7b6b2cfc7ca 100644
--- a/app/assets/javascripts/projects/new/constants.js
+++ b/app/assets/javascripts/projects/new/constants.js
@@ -12,6 +12,8 @@ export const DEPLOYMENT_TARGET_SELECTIONS = [
s__('DeploymentTarget|Registry (package or container)'),
s__('DeploymentTarget|Infrastructure provider (Terraform, Cloudformation, and so on)'),
s__('DeploymentTarget|Serverless backend (Lambda, Cloud functions)'),
+ s__('DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)'),
+ s__('DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)'),
s__('DeploymentTarget|GitLab Pages'),
s__('DeploymentTarget|Other hosting service'),
s__('DeploymentTarget|No deployment planned'),
diff --git a/app/assets/javascripts/projects/new/index.js b/app/assets/javascripts/projects/new/index.js
index a72172a4f5e..910244c657b 100644
--- a/app/assets/javascripts/projects/new/index.js
+++ b/app/assets/javascripts/projects/new/index.js
@@ -59,6 +59,8 @@ export function initNewProjectUrlSelect() {
rootUrl: el.dataset.rootUrl,
trackLabel: el.dataset.trackLabel,
userNamespaceId: el.dataset.userNamespaceId,
+ inputId: el.dataset.inputId,
+ inputName: el.dataset.inputName,
},
render: (createElement) => createElement(NewProjectUrlSelect),
}),
diff --git a/app/assets/javascripts/projects/project_name_rules.js b/app/assets/javascripts/projects/project_name_rules.js
new file mode 100644
index 00000000000..eeef1fb5afc
--- /dev/null
+++ b/app/assets/javascripts/projects/project_name_rules.js
@@ -0,0 +1,28 @@
+import { __ } from '~/locale';
+
+const rulesReg = [
+ {
+ reg: /^[a-zA-Z0-9\u{00A9}-\u{1f9ff}_]/u,
+ msg: __("Name must start with a letter, digit, emoji, or '_'"),
+ },
+ {
+ reg: /^[a-zA-Z0-9\p{Pd}\u{002B}\u{00A9}-\u{1f9ff}_. ]+$/u,
+ msg: __("Name can contain only letters, digits, emojis, '_', '.', '+', dashes, or spaces"),
+ },
+];
+
+/**
+ *
+ * @param {string} text
+ * @returns {string} msg
+ */
+function checkRules(text) {
+ for (const item of rulesReg) {
+ if (!item.reg.test(text)) {
+ return item.msg;
+ }
+ }
+ return '';
+}
+
+export { checkRules };
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index 424ea3b61c5..d71e80dffcf 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -12,6 +12,7 @@ import {
slugify,
convertUnicodeToAscii,
} from '../lib/utils/text_utility';
+import { checkRules } from './project_name_rules';
let hasUserDefinedProjectPath = false;
let hasUserDefinedProjectName = false;
@@ -87,10 +88,23 @@ const validateGroupNamespaceDropdown = (e) => {
}
};
+const checkProjectName = (projectNameInput) => {
+ const msg = checkRules(projectNameInput.value);
+ const projectNameError = document.querySelector('#project_name_error');
+ if (!projectNameError) return;
+ if (msg) {
+ projectNameError.innerText = msg;
+ projectNameError.classList.remove('hidden');
+ } else {
+ projectNameError.classList.add('hidden');
+ }
+};
+
const setProjectNamePathHandlers = ($projectNameInput, $projectPathInput) => {
const specialRepo = document.querySelector('.js-user-readme-repo');
const projectNameInputListener = () => {
onProjectNameChange($projectNameInput, $projectPathInput);
+ checkProjectName($projectNameInput);
hasUserDefinedProjectName = $projectNameInput.value.trim().length > 0;
hasUserDefinedProjectPath = $projectPathInput.value.trim().length > 0;
};
diff --git a/app/assets/javascripts/projects/settings/access_dropdown.js b/app/assets/javascripts/projects/settings/access_dropdown.js
index 335545c802a..dcf7415a444 100644
--- a/app/assets/javascripts/projects/settings/access_dropdown.js
+++ b/app/assets/javascripts/projects/settings/access_dropdown.js
@@ -580,7 +580,7 @@ export default class AccessDropdown {
return `
<li>
<a href="#" class="${isActiveClass} item-${role.type}" data-role-id="${role.id}">
- ${role.text}
+ ${escape(role.text)}
</a>
</li>
`;
diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js b/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js
index 6da058ebc9c..61c37a2348a 100644
--- a/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js
+++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js
@@ -6,6 +6,7 @@ export const I18N = {
branchNameOrPattern: s__('BranchRules|Branch name or pattern'),
branch: s__('BranchRules|Target Branch'),
allBranches: s__('BranchRules|All branches'),
+ matchingBranchesLinkTitle: s__('BranchRules|%{total} matching %{subject}'),
protectBranchTitle: s__('BranchRules|Protect branch'),
protectBranchDescription: s__(
'BranchRules|Keep stable branches secure and force developers to use merge requests. %{linkStart}What are protected branches?%{linkEnd}',
diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue b/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue
index eb11e17dd1b..626ed67c466 100644
--- a/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue
+++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue
@@ -1,9 +1,10 @@
<script>
import { GlSprintf, GlLink, GlLoadingIcon } from '@gitlab/ui';
-import { sprintf } from '~/locale';
-import { getParameterByName } from '~/lib/utils/url_utility';
+import { sprintf, n__ } from '~/locale';
+import { getParameterByName, mergeUrlParams } from '~/lib/utils/url_utility';
import { helpPagePath } from '~/helpers/help_page_helper';
import branchRulesQuery from '../../queries/branch_rules_details.query.graphql';
+import { getAccessLevels } from '../../../utils';
import Protection from './protection.vue';
import {
I18N,
@@ -41,6 +42,9 @@ export default {
statusChecksPath: {
default: '',
},
+ branchesPath: {
+ default: '',
+ },
},
apollo: {
project: {
@@ -55,6 +59,7 @@ export default {
this.branchProtection = branchRule?.branchProtection;
this.approvalRules = branchRule?.approvalRules;
this.statusChecks = branchRule?.externalStatusChecks?.nodes || [];
+ this.matchingBranchesCount = branchRule?.matchingBranchesCount;
},
},
},
@@ -64,6 +69,7 @@ export default {
branchProtection: {},
approvalRules: {},
statusChecks: [],
+ matchingBranchesCount: null,
};
},
computed: {
@@ -115,28 +121,20 @@ export default {
? this.$options.i18n.targetBranch
: this.$options.i18n.branchNameOrPattern;
},
+ matchingBranchesLinkHref() {
+ return mergeUrlParams({ state: 'all', search: this.branch }, this.branchesPath);
+ },
+ matchingBranchesLinkTitle() {
+ const total = this.matchingBranchesCount;
+ const subject = n__('branch', 'branches', total);
+ return sprintf(this.$options.i18n.matchingBranchesLinkTitle, { total, subject });
+ },
approvals() {
return this.approvalRules?.nodes || [];
},
},
methods: {
- getAccessLevels(accessLevels = {}) {
- const total = accessLevels.edges?.length;
- const accessLevelTypes = { total, users: [], groups: [], roles: [] };
-
- accessLevels.edges?.forEach(({ node }) => {
- if (node.user) {
- const src = node.user.avatarUrl;
- accessLevelTypes.users.push({ src, ...node.user });
- } else if (node.group) {
- accessLevelTypes.groups.push(node);
- } else {
- accessLevelTypes.roles.push(node);
- }
- });
-
- return accessLevelTypes;
- },
+ getAccessLevels,
},
};
</script>
@@ -161,6 +159,10 @@ export default {
</div>
<code v-else class="gl-mt-2" data-testid="branch">{{ branch }}</code>
+ <p v-if="matchingBranchesCount" class="gl-mt-3">
+ <gl-link :href="matchingBranchesLinkHref">{{ matchingBranchesLinkTitle }}</gl-link>
+ </p>
+
<h4 class="gl-mb-1 gl-mt-5">{{ $options.i18n.protectBranchTitle }}</h4>
<gl-sprintf :message="$options.i18n.protectBranchDescription">
<template #link="{ content }">
diff --git a/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js b/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js
index 89cfb1e1c8e..7639acc1181 100644
--- a/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js
+++ b/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js
@@ -14,7 +14,13 @@ export default function mountBranchRules(el) {
defaultClient: createDefaultClient(),
});
- const { projectPath, protectedBranchesPath, approvalRulesPath, statusChecksPath } = el.dataset;
+ const {
+ projectPath,
+ protectedBranchesPath,
+ approvalRulesPath,
+ statusChecksPath,
+ branchesPath,
+ } = el.dataset;
return new Vue({
el,
@@ -24,6 +30,7 @@ export default function mountBranchRules(el) {
protectedBranchesPath,
approvalRulesPath,
statusChecksPath,
+ branchesPath,
},
render(h) {
return h(View);
diff --git a/app/assets/javascripts/projects/settings/branch_rules/queries/branch_rules_details.query.graphql b/app/assets/javascripts/projects/settings/branch_rules/queries/branch_rules_details.query.graphql
index aa1e4923aa8..a832e59aa67 100644
--- a/app/assets/javascripts/projects/settings/branch_rules/queries/branch_rules_details.query.graphql
+++ b/app/assets/javascripts/projects/settings/branch_rules/queries/branch_rules_details.query.graphql
@@ -68,6 +68,7 @@ query getBranchRulesDetails($projectPath: ID!) {
externalUrl
}
}
+ matchingBranchesCount
}
}
}
diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue b/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue
index a9eb2a53fbf..9b669024a8b 100644
--- a/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue
+++ b/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue
@@ -1,7 +1,7 @@
<script>
import { s__ } from '~/locale';
import { createAlert } from '~/flash';
-import branchRulesQuery from './graphql/queries/branch_rules.query.graphql';
+import branchRulesQuery from 'ee_else_ce/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql';
import BranchRule from './components/branch_rule.vue';
export const i18n = {
@@ -51,13 +51,14 @@ export default {
<template>
<div class="settings-content">
<branch-rule
- v-for="rule in branchRules"
- :key="rule.name"
+ v-for="(rule, index) in branchRules"
+ :key="`${rule.name}-${index}`"
:name="rule.name"
:is-default="rule.isDefault"
:branch-protection="rule.branchProtection"
- :status-checks-total="rule.externalStatusChecks.nodes.length"
- :approval-rules-total="rule.approvalRules.nodes.length"
+ :status-checks-total="rule.externalStatusChecks ? rule.externalStatusChecks.nodes.length : 0"
+ :approval-rules-total="rule.approvalRules ? rule.approvalRules.nodes.length : 0"
+ :matching-branches-count="rule.matchingBranchesCount"
/>
<span v-if="!branchRules.length" data-testid="empty">{{ $options.i18n.emptyState }}</span>
diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue b/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue
index 78c824c66d1..41947834bdb 100644
--- a/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue
+++ b/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue
@@ -1,6 +1,7 @@
<script>
import { GlBadge, GlButton } from '@gitlab/ui';
import { s__, sprintf, n__ } from '~/locale';
+import { getAccessLevels } from '../../../utils';
export const i18n = {
defaultLabel: s__('BranchRules|default'),
@@ -9,6 +10,9 @@ export const i18n = {
codeOwnerApprovalRequired: s__('BranchRules|Requires CODEOWNERS approval'),
statusChecks: s__('BranchRules|%{total} status %{subject}'),
approvalRules: s__('BranchRules|%{total} approval %{subject}'),
+ matchingBranches: s__('BranchRules|%{total} matching %{subject}'),
+ pushAccessLevels: s__('BranchRules|Allowed to merge'),
+ mergeAccessLevels: s__('BranchRules|Allowed to push'),
};
export default {
@@ -48,8 +52,16 @@ export default {
required: false,
default: 0,
},
+ matchingBranchesCount: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
},
computed: {
+ isWildcard() {
+ return this.name.includes('*');
+ },
hasApprovalDetails() {
return this.approvalDetails.length;
},
@@ -68,8 +80,31 @@ export default {
subject: n__('rule', 'rules', this.approvalRulesTotal),
});
},
+ matchingBranchesText() {
+ return sprintf(this.$options.i18n.matchingBranches, {
+ total: this.matchingBranchesCount,
+ subject: n__('branch', 'branches', this.matchingBranchesCount),
+ });
+ },
+ mergeAccessLevels() {
+ const { mergeAccessLevels } = this.branchProtection || {};
+ return this.getAccessLevels(mergeAccessLevels);
+ },
+ pushAccessLevels() {
+ const { pushAccessLevels } = this.branchProtection || {};
+ return this.getAccessLevels(pushAccessLevels);
+ },
+ pushAccessLevelsText() {
+ return this.getAccessLevelsText(this.$options.i18n.pushAccessLevels, this.pushAccessLevels);
+ },
+ mergeAccessLevelsText() {
+ return this.getAccessLevelsText(this.$options.i18n.mergeAccessLevels, this.mergeAccessLevels);
+ },
approvalDetails() {
const approvalDetails = [];
+ if (this.isWildcard) {
+ approvalDetails.push(this.matchingBranchesText);
+ }
if (this.branchProtection.allowForcePush) {
approvalDetails.push(this.$options.i18n.allowForcePush);
}
@@ -82,9 +117,31 @@ export default {
if (this.approvalRulesTotal) {
approvalDetails.push(this.approvalRulesText);
}
+ if (this.mergeAccessLevels.total > 0) {
+ approvalDetails.push(this.mergeAccessLevelsText);
+ }
+ if (this.pushAccessLevels.total > 0) {
+ approvalDetails.push(this.pushAccessLevelsText);
+ }
return approvalDetails;
},
},
+ methods: {
+ getAccessLevels,
+ getAccessLevelsText(beginString = '', accessLevels) {
+ const textParts = [];
+ if (accessLevels.roles.length) {
+ textParts.push(n__('1 role', '%d roles', accessLevels.roles.length));
+ }
+ if (accessLevels.groups.length) {
+ textParts.push(n__('1 group', '%d groups', accessLevels.groups.length));
+ }
+ if (accessLevels.users.length) {
+ textParts.push(n__('1 user', '%d users', accessLevels.users.length));
+ }
+ return `${beginString}: ${textParts.join(', ')}`;
+ },
+ },
};
</script>
diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql b/app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql
index 49e089e7805..a8cdda5505f 100644
--- a/app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql
+++ b/app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql
@@ -5,18 +5,24 @@ query getBranchRules($projectPath: ID!) {
nodes {
name
isDefault
+ matchingBranchesCount
branchProtection {
allowForcePush
- codeOwnerApprovalRequired
- }
- externalStatusChecks {
- nodes {
- id
+ mergeAccessLevels {
+ edges {
+ node {
+ accessLevel
+ accessLevelDescription
+ }
+ }
}
- }
- approvalRules {
- nodes {
- id
+ pushAccessLevels {
+ edges {
+ node {
+ accessLevel
+ accessLevelDescription
+ }
+ }
}
}
}
diff --git a/app/assets/javascripts/projects/settings/utils.js b/app/assets/javascripts/projects/settings/utils.js
new file mode 100644
index 00000000000..7bcfde39178
--- /dev/null
+++ b/app/assets/javascripts/projects/settings/utils.js
@@ -0,0 +1,17 @@
+export const getAccessLevels = (accessLevels = {}) => {
+ const total = accessLevels.edges?.length;
+ const accessLevelTypes = { total, users: [], groups: [], roles: [] };
+
+ accessLevels.edges?.forEach(({ node }) => {
+ if (node.user) {
+ const src = node.user.avatarUrl;
+ accessLevelTypes.users.push({ src, ...node.user });
+ } else if (node.group) {
+ accessLevelTypes.groups.push(node);
+ } else {
+ accessLevelTypes.roles.push(node);
+ }
+ });
+
+ return accessLevelTypes;
+};
diff --git a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue
index 71ff3e892b1..b79b3fa4573 100644
--- a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue
+++ b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue
@@ -1,5 +1,6 @@
<script>
-import { GlAlert, GlSprintf, GlLink, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import axios from '~/lib/utils/axios_utils';
import { helpPagePath } from '~/helpers/help_page_helper';
import { __, sprintf } from '~/locale';
@@ -16,7 +17,7 @@ export default {
ServiceDeskSetting,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
inject: {
initialIsEnabled: {
diff --git a/app/assets/javascripts/protected_tags/protected_tag_create.js b/app/assets/javascripts/protected_tags/protected_tag_create.js
index e3f427b8408..75fd11cd074 100644
--- a/app/assets/javascripts/protected_tags/protected_tag_create.js
+++ b/app/assets/javascripts/protected_tags/protected_tag_create.js
@@ -42,7 +42,7 @@ export default class ProtectedTagCreate {
const $allowedToCreateInput = this.$form.find('#create_access_levels_attributes');
this.$form
- .find('input[type="submit"]')
+ .find('button[type="submit"]')
.prop('disabled', !($tagInput.val() && $allowedToCreateInput.length));
}
diff --git a/app/assets/javascripts/releases/components/app_index.vue b/app/assets/javascripts/releases/components/app_index.vue
index 6dc8240e680..1b360b79b0c 100644
--- a/app/assets/javascripts/releases/components/app_index.vue
+++ b/app/assets/javascripts/releases/components/app_index.vue
@@ -263,6 +263,7 @@ export default {
v-for="(release, index) in releases"
:key="getReleaseKey(release, index)"
:release="release"
+ :sort="sort"
:class="{ 'linked-card': releases.length > 1 && index !== releases.length - 1 }"
/>
diff --git a/app/assets/javascripts/releases/components/release_block.vue b/app/assets/javascripts/releases/components/release_block.vue
index b2bd405574f..49c349e7a7b 100644
--- a/app/assets/javascripts/releases/components/release_block.vue
+++ b/app/assets/javascripts/releases/components/release_block.vue
@@ -1,12 +1,12 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
-import $ from 'jquery';
import { isEmpty } from 'lodash';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { scrollToElement } from '~/lib/utils/common_utils';
import { slugify } from '~/lib/utils/text_utility';
import { getLocationHash } from '~/lib/utils/url_utility';
+import { CREATED_ASC } from '~/releases/constants';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import '~/behaviors/markdown/render_gfm';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import EvidenceBlock from './evidence_block.vue';
import ReleaseBlockAssets from './release_block_assets.vue';
import ReleaseBlockFooter from './release_block_footer.vue';
@@ -32,6 +32,11 @@ export default {
required: true,
default: () => ({}),
},
+ sort: {
+ type: String,
+ required: false,
+ default: CREATED_ASC,
+ },
},
data() {
return {
@@ -80,7 +85,7 @@ export default {
},
methods: {
renderGFM() {
- $(this.$refs['gfm-content']).renderGFM();
+ renderGFM(this.$refs['gfm-content']);
},
},
safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
@@ -119,6 +124,8 @@ export default {
:tag-path="release.tagPath"
:author="release.author"
:released-at="release.releasedAt"
+ :created-at="release.createdAt"
+ :sort="sort"
/>
</div>
</template>
diff --git a/app/assets/javascripts/releases/components/release_block_footer.vue b/app/assets/javascripts/releases/components/release_block_footer.vue
index 3881c83b5c2..85fb7d02a37 100644
--- a/app/assets/javascripts/releases/components/release_block_footer.vue
+++ b/app/assets/javascripts/releases/components/release_block_footer.vue
@@ -3,6 +3,7 @@ import { GlTooltipDirective, GlLink, GlIcon } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
+import { RELEASED_AT_ASC, RELEASED_AT_DESC } from '~/releases/constants';
export default {
name: 'ReleaseBlockFooter',
@@ -46,10 +47,26 @@ export default {
required: false,
default: null,
},
+ createdAt: {
+ type: Date,
+ required: false,
+ default: null,
+ },
+ sort: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
computed: {
- releasedAtTimeAgo() {
- return this.timeFormatted(this.releasedAt);
+ isSortedByReleaseDate() {
+ return this.sort === RELEASED_AT_ASC || this.sort === RELEASED_AT_DESC;
+ },
+ timeAt() {
+ return this.isSortedByReleaseDate ? this.releasedAt : this.createdAt;
+ },
+ atTimeAgo() {
+ return this.timeFormatted(this.timeAt);
},
userImageAltDescription() {
return this.author && this.author.username
@@ -58,7 +75,10 @@ export default {
},
createdTime() {
const now = new Date();
- const isFuture = now < new Date(this.releasedAt);
+ const isFuture = now < new Date(this.timeAt);
+ if (this.isSortedByReleaseDate) {
+ return isFuture ? __('Will be released') : __('Released');
+ }
return isFuture ? __('Will be created') : __('Created');
},
},
@@ -93,17 +113,17 @@ export default {
</div>
<div
- v-if="releasedAt || author"
+ v-if="timeAt || author"
class="gl-float-left gl-display-flex gl-align-items-center js-author-date-info"
>
<span class="gl-text-secondary">{{ createdTime }}&nbsp;</span>
- <template v-if="releasedAt">
+ <template v-if="timeAt">
<span
v-gl-tooltip.bottom
- :title="tooltipTitle(releasedAt)"
+ :title="tooltipTitle(timeAt)"
class="gl-text-secondary gl-flex-shrink-0"
>
- {{ releasedAtTimeAgo }}&nbsp;
+ {{ atTimeAgo }}&nbsp;
</span>
</template>
diff --git a/app/assets/javascripts/releases/graphql/fragments/release_for_editing.fragment.graphql b/app/assets/javascripts/releases/graphql/fragments/release_for_editing.fragment.graphql
index 3ad66afa259..177dff1823e 100644
--- a/app/assets/javascripts/releases/graphql/fragments/release_for_editing.fragment.graphql
+++ b/app/assets/javascripts/releases/graphql/fragments/release_for_editing.fragment.graphql
@@ -4,6 +4,7 @@ fragment ReleaseForEditing on Release {
tagName
description
releasedAt
+ createdAt
tagPath
assets {
links {
diff --git a/app/assets/javascripts/releases/util.js b/app/assets/javascripts/releases/util.js
index a1027ef08d7..10d7887c0b1 100644
--- a/app/assets/javascripts/releases/util.js
+++ b/app/assets/javascripts/releases/util.js
@@ -15,7 +15,8 @@ const convertScalarProperties = (graphQLRelease) =>
'historicalRelease',
]);
-const convertDateProperties = ({ releasedAt }) => ({
+const convertDateProperties = ({ createdAt, releasedAt }) => ({
+ createdAt: new Date(createdAt),
releasedAt: new Date(releasedAt),
});
diff --git a/app/assets/javascripts/reports/codequality_report/components/codequality_issue_body.vue b/app/assets/javascripts/reports/codequality_report/components/codequality_issue_body.vue
deleted file mode 100644
index fb2ef850e4f..00000000000
--- a/app/assets/javascripts/reports/codequality_report/components/codequality_issue_body.vue
+++ /dev/null
@@ -1,76 +0,0 @@
-<script>
-/**
- * Renders Code quality body text
- * Fixed: [name] in [link]:[line]
- */
-import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
-import { s__ } from '~/locale';
-import ReportLink from '~/reports/components/report_link.vue';
-import { STATUS_SUCCESS, STATUS_NEUTRAL } from '~/reports/constants';
-import { SEVERITY_CLASSES, SEVERITY_ICONS } from '../constants';
-
-export default {
- name: 'CodequalityIssueBody',
- components: {
- GlIcon,
- ReportLink,
- },
- directives: {
- tooltip: GlTooltipDirective,
- },
- props: {
- status: {
- type: String,
- required: false,
- default: STATUS_NEUTRAL,
- },
- issue: {
- type: Object,
- required: true,
- },
- },
- computed: {
- issueName() {
- return `${this.severityLabel} - ${this.issue.name}`;
- },
- issueSeverity() {
- return this.issue.severity?.toLowerCase();
- },
- isStatusSuccess() {
- return this.status === STATUS_SUCCESS;
- },
- severityClass() {
- return SEVERITY_CLASSES[this.issueSeverity] || SEVERITY_CLASSES.unknown;
- },
- severityIcon() {
- return SEVERITY_ICONS[this.issueSeverity] || SEVERITY_ICONS.unknown;
- },
- severityLabel() {
- return this.$options.severityText[this.issueSeverity] || this.$options.severityText.unknown;
- },
- },
- severityText: {
- info: s__('severity|Info'),
- minor: s__('severity|Minor'),
- major: s__('severity|Major'),
- critical: s__('severity|Critical'),
- blocker: s__('severity|Blocker'),
- unknown: s__('severity|Unknown'),
- },
-};
-</script>
-<template>
- <div class="gl-display-flex gl-mt-2 gl-mb-2 gl-w-full">
- <span :class="severityClass" class="gl-mr-5" data-testid="codequality-severity-icon">
- <gl-icon v-tooltip="severityLabel" :name="severityIcon" :size="12" />
- </span>
- <div class="gl-flex-grow-1">
- <div>
- <strong v-if="isStatusSuccess">{{ s__('ciReport|Fixed:') }}</strong>
- {{ issueName }}
- </div>
-
- <report-link v-if="issue.path" :issue="issue" />
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/reports/codequality_report/constants.js b/app/assets/javascripts/reports/codequality_report/constants.js
deleted file mode 100644
index 0c472b24471..00000000000
--- a/app/assets/javascripts/reports/codequality_report/constants.js
+++ /dev/null
@@ -1,31 +0,0 @@
-export const SEVERITY_CLASSES = {
- info: 'text-primary-400',
- minor: 'text-warning-200',
- major: 'text-warning-400',
- critical: 'text-danger-600',
- blocker: 'text-danger-800',
- unknown: 'text-secondary-400',
-};
-
-export const SEVERITY_ICONS = {
- info: 'severity-info',
- minor: 'severity-low',
- major: 'severity-medium',
- critical: 'severity-high',
- blocker: 'severity-critical',
- unknown: 'severity-unknown',
-};
-
-// This is the icons mapping for the code Quality Merge-Request Widget Extension
-// once the refactor_mr_widgets_extensions flag is activated the above SEVERITY_ICONS
-// need be removed and this variable needs to be rename to SEVERITY_ICONS
-// Rollout Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/341759
-
-export const SEVERITY_ICONS_EXTENSION = {
- info: 'severityInfo',
- minor: 'severityLow',
- major: 'severityMedium',
- critical: 'severityHigh',
- blocker: 'severityCritical',
- unknown: 'severityUnknown',
-};
diff --git a/app/assets/javascripts/reports/components/grouped_issues_list.vue b/app/assets/javascripts/reports/components/grouped_issues_list.vue
deleted file mode 100644
index ca369022938..00000000000
--- a/app/assets/javascripts/reports/components/grouped_issues_list.vue
+++ /dev/null
@@ -1,106 +0,0 @@
-<script>
-import { s__ } from '~/locale';
-import ReportItem from '~/reports/components/report_item.vue';
-import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
-
-export default {
- components: {
- ReportItem,
- SmartVirtualList,
- },
- props: {
- component: {
- type: String,
- required: false,
- default: '',
- },
- nestedLevel: {
- type: Number,
- required: false,
- default: 0,
- validator: (value) => [0, 1, 2].includes(value),
- },
- resolvedIssues: {
- type: Array,
- required: false,
- default: () => [],
- },
- unresolvedIssues: {
- type: Array,
- required: false,
- default: () => [],
- },
- resolvedHeading: {
- type: String,
- required: false,
- default: s__('ciReport|Fixed'),
- },
- unresolvedHeading: {
- type: String,
- required: false,
- default: s__('ciReport|New'),
- },
- },
- groups: ['unresolved', 'resolved'],
- typicalReportItemHeight: 32,
- maxShownReportItems: 20,
- computed: {
- groups() {
- return this.$options.groups
- .map((group) => ({
- name: group,
- issues: this[`${group}Issues`],
- heading: this[`${group}Heading`],
- }))
- .filter(({ issues }) => issues.length > 0);
- },
- listLength() {
- // every group has a header which is rendered as a list item
- const groupsCount = this.groups.length;
- const issuesCount = this.groups.reduce(
- (totalIssues, { issues }) => totalIssues + issues.length,
- 0,
- );
-
- return groupsCount + issuesCount;
- },
- listClasses() {
- return {
- 'gl-pl-9': this.nestedLevel === 1,
- 'gl-pl-11-5': this.nestedLevel === 2,
- };
- },
- },
-};
-</script>
-
-<template>
- <smart-virtual-list
- :length="listLength"
- :remain="$options.maxShownReportItems"
- :size="$options.typicalReportItemHeight"
- :class="listClasses"
- class="report-block-container"
- wtag="ul"
- wclass="report-block-list"
- >
- <template v-for="(group, groupIndex) in groups">
- <h2
- :key="group.name"
- :data-testid="`${group.name}Heading`"
- :class="[groupIndex > 0 ? 'mt-2' : 'mt-0']"
- class="h5 mb-1"
- >
- {{ group.heading }}
- </h2>
- <report-item
- v-for="(issue, issueIndex) in group.issues"
- :key="`${group.name}-${issue.name}-${group.name}-${issueIndex}`"
- :issue="issue"
- :show-report-section-status-icon="false"
- :component="component"
- status="none"
- />
- </template>
- </smart-virtual-list>
-</template>
diff --git a/app/assets/javascripts/reports/components/issue_body.js b/app/assets/javascripts/reports/components/issue_body.js
deleted file mode 100644
index 4f418216024..00000000000
--- a/app/assets/javascripts/reports/components/issue_body.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import IssueStatusIcon from '~/reports/components/issue_status_icon.vue';
-
-export const components = {
- CodequalityIssueBody: () => import('../codequality_report/components/codequality_issue_body.vue'),
-};
-
-export const componentNames = {
- CodequalityIssueBody: 'CodequalityIssueBody',
-};
-
-export const iconComponents = {
- IssueStatusIcon,
-};
-
-export const iconComponentNames = {
- IssueStatusIcon: IssueStatusIcon.name,
-};
diff --git a/app/assets/javascripts/reports/components/issues_list.vue b/app/assets/javascripts/reports/components/issues_list.vue
deleted file mode 100644
index 9df0a1953b6..00000000000
--- a/app/assets/javascripts/reports/components/issues_list.vue
+++ /dev/null
@@ -1,119 +0,0 @@
-<script>
-import ReportItem from '~/reports/components/report_item.vue';
-import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '~/reports/constants';
-import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
-
-const wrapIssueWithState = (status, isNew = false) => (issue) => ({
- status: issue.status || status,
- isNew,
- issue,
-});
-
-/**
- * Renders block of issues
- */
-export default {
- components: {
- SmartVirtualList,
- ReportItem,
- },
- // Typical height of a report item in px
- typicalReportItemHeight: 32,
- /*
- The maximum amount of shown issues. This is calculated by
- ( max-height of report-block-list / typicalReportItemHeight ) + some safety margin
- We will use VirtualList if we have more items than this number.
- For entries lower than this number, the virtual scroll list calculates the total height of the element wrongly.
- */
- maxShownReportItems: 20,
- props: {
- newIssues: {
- type: Array,
- required: false,
- default: () => [],
- },
- unresolvedIssues: {
- type: Array,
- required: false,
- default: () => [],
- },
- resolvedIssues: {
- type: Array,
- required: false,
- default: () => [],
- },
- neutralIssues: {
- type: Array,
- required: false,
- default: () => [],
- },
- component: {
- type: String,
- required: false,
- default: '',
- },
- showReportSectionStatusIcon: {
- type: Boolean,
- required: false,
- default: true,
- },
- issuesUlElementClass: {
- type: String,
- required: false,
- default: '',
- },
- issueItemClass: {
- type: String,
- required: false,
- default: null,
- },
- nestedLevel: {
- type: Number,
- required: false,
- default: 0,
- validator: (value) => [0, 1, 2].includes(value),
- },
- },
- computed: {
- issuesWithState() {
- return [
- ...this.newIssues.map(wrapIssueWithState(STATUS_FAILED, true)),
- ...this.unresolvedIssues.map(wrapIssueWithState(STATUS_FAILED)),
- ...this.neutralIssues.map(wrapIssueWithState(STATUS_NEUTRAL)),
- ...this.resolvedIssues.map(wrapIssueWithState(STATUS_SUCCESS)),
- ];
- },
- wclass() {
- return `report-block-list ${this.issuesUlElementClass}`;
- },
- listClasses() {
- return {
- 'gl-pl-9': this.nestedLevel === 1,
- 'gl-pl-11-5': this.nestedLevel === 2,
- };
- },
- },
-};
-</script>
-<template>
- <smart-virtual-list
- :length="issuesWithState.length"
- :remain="$options.maxShownReportItems"
- :size="$options.typicalReportItemHeight"
- class="report-block-container"
- :class="listClasses"
- wtag="ul"
- :wclass="wclass"
- >
- <report-item
- v-for="(wrapped, index) in issuesWithState"
- :key="index"
- :issue="wrapped.issue"
- :status="wrapped.status"
- :component="component"
- :is-new="wrapped.isNew"
- :show-report-section-status-icon="showReportSectionStatusIcon"
- :class="issueItemClass"
- />
- </smart-virtual-list>
-</template>
diff --git a/app/assets/javascripts/reports/components/report_item.vue b/app/assets/javascripts/reports/components/report_item.vue
deleted file mode 100644
index 918263bfb5c..00000000000
--- a/app/assets/javascripts/reports/components/report_item.vue
+++ /dev/null
@@ -1,67 +0,0 @@
-<script>
-import {
- components,
- componentNames,
- iconComponents,
- iconComponentNames,
-} from 'ee_else_ce/reports/components/issue_body';
-
-export default {
- name: 'ReportItem',
- components: {
- ...components,
- ...iconComponents,
- },
- props: {
- issue: {
- type: Object,
- required: true,
- },
- component: {
- type: String,
- required: false,
- default: '',
- validator: (value) => value === '' || Object.values(componentNames).includes(value),
- },
- iconComponent: {
- type: String,
- required: false,
- default: iconComponentNames.IssueStatusIcon,
- validator: (value) => Object.values(iconComponentNames).includes(value),
- },
- // failed || success
- status: {
- type: String,
- required: true,
- },
- statusIconSize: {
- type: Number,
- required: false,
- default: 24,
- },
- isNew: {
- type: Boolean,
- required: false,
- default: false,
- },
- showReportSectionStatusIcon: {
- type: Boolean,
- required: false,
- default: true,
- },
- },
-};
-</script>
-<template>
- <li class="report-block-list-issue align-items-center" data-qa-selector="report_item_row">
- <component
- :is="iconComponent"
- v-if="showReportSectionStatusIcon"
- :status="status"
- :status-icon-size="statusIconSize"
- class="gl-mr-2"
- />
-
- <component :is="component" v-if="component" :issue="issue" :status="status" :is-new="isNew" />
- </li>
-</template>
diff --git a/app/assets/javascripts/repository/components/last_commit.vue b/app/assets/javascripts/repository/components/last_commit.vue
index 05d64077866..4d3c1521559 100644
--- a/app/assets/javascripts/repository/components/last_commit.vue
+++ b/app/assets/javascripts/repository/components/last_commit.vue
@@ -1,12 +1,6 @@
<script>
-import {
- GlTooltipDirective,
- GlLink,
- GlButton,
- GlButtonGroup,
- GlLoadingIcon,
- GlSafeHtmlDirective,
-} from '@gitlab/ui';
+import { GlTooltipDirective, GlLink, GlButton, GlButtonGroup, GlLoadingIcon } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import defaultAvatarUrl from 'images/no_avatar.png';
import pathLastCommitQuery from 'shared_queries/repository/path_last_commit.query.graphql';
import { sprintf, s__ } from '~/locale';
@@ -32,7 +26,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [getRefMixin],
apollo: {
diff --git a/app/assets/javascripts/repository/components/preview/index.vue b/app/assets/javascripts/repository/components/preview/index.vue
index 4935b8029f9..8feac6b8e35 100644
--- a/app/assets/javascripts/repository/components/preview/index.vue
+++ b/app/assets/javascripts/repository/components/preview/index.vue
@@ -1,8 +1,8 @@
<script>
-import { GlIcon, GlLink, GlLoadingIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
-import $ from 'jquery';
-import '~/behaviors/markdown/render_gfm';
+import { GlIcon, GlLink, GlLoadingIcon } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { handleLocationHash } from '~/lib/utils/common_utils';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import readmeQuery from '../../queries/readme.query.graphql';
export default {
@@ -42,7 +42,7 @@ export default {
if (newVal) {
this.$nextTick(() => {
handleLocationHash();
- $(this.$refs.readme).renderGFM();
+ renderGFM(this.$refs.readme);
});
}
},
diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue
index 99eb167172b..46d546c2ee4 100644
--- a/app/assets/javascripts/repository/components/table/index.vue
+++ b/app/assets/javascripts/repository/components/table/index.vue
@@ -1,6 +1,5 @@
<script>
import { GlSkeletonLoader, GlButton } from '@gitlab/ui';
-import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { sprintf, __ } from '~/locale';
import { joinPaths } from '~/lib/utils/url_utility';
import getRefMixin from '../../mixins/get_ref';
@@ -17,7 +16,7 @@ export default {
ParentRow,
GlButton,
},
- mixins: [getRefMixin, glFeatureFlagMixin()],
+ mixins: [getRefMixin],
apollo: {
projectPath: {
query: projectPathQuery,
@@ -93,9 +92,6 @@ export default {
},
generateRowNumber(path, id, index) {
const key = `${path}-${id}-${index}`;
- if (!this.glFeatures.lazyLoadCommits) {
- return 0;
- }
if (!this.rowNumbers[key] && this.rowNumbers[key] !== 0) {
this.$options.totalRowsLoaded += 1;
@@ -105,10 +101,6 @@ export default {
return this.rowNumbers[key];
},
getCommit(fileName) {
- if (!this.glFeatures.lazyLoadCommits) {
- return {};
- }
-
return this.commits.find(
(commitEntry) => commitEntry.filePath === joinPaths(this.path, fileName),
);
diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue
index f3c5ace75fc..27ac11f3c58 100644
--- a/app/assets/javascripts/repository/components/table/row.vue
+++ b/app/assets/javascripts/repository/components/table/row.vue
@@ -7,10 +7,10 @@ import {
GlLoadingIcon,
GlIcon,
GlHoverLoadDirective,
- GlSafeHtmlDirective,
GlIntersectionObserver,
} from '@gitlab/ui';
import { escapeRegExp } from 'lodash';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import paginatedTreeQuery from 'shared_queries/repository/paginated_tree.query.graphql';
import { escapeFileUrl } from '~/lib/utils/url_utility';
import { TREE_PAGE_SIZE, ROW_APPEAR_DELAY } from '~/repository/constants';
@@ -19,7 +19,6 @@ import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import blobInfoQuery from 'shared_queries/repository/blob_info.query.graphql';
import getRefMixin from '../../mixins/get_ref';
-import commitQuery from '../../queries/commit.query.graphql';
export default {
components: {
@@ -35,23 +34,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
GlHoverLoad: GlHoverLoadDirective,
- SafeHtml: GlSafeHtmlDirective,
- },
- apollo: {
- commit: {
- query: commitQuery,
- variables() {
- return {
- fileName: this.name,
- path: this.currentPath,
- projectPath: this.projectPath,
- maxOffset: this.totalEntries,
- };
- },
- skip() {
- return this.glFeatures.lazyLoadCommits;
- },
- },
+ SafeHtml,
},
mixins: [getRefMixin, glFeatureFlagMixin()],
props: {
@@ -125,14 +108,13 @@ export default {
},
data() {
return {
- commit: null,
hasRowAppeared: false,
delayedRowAppear: null,
};
},
computed: {
commitData() {
- return this.glFeatures.lazyLoadCommits ? this.commitInfo : this.commit;
+ return this.commitInfo;
},
routerLinkTo() {
const blobRouteConfig = { path: `/-/blob/${this.escapedRef}/${escapeFileUrl(this.path)}` };
@@ -200,12 +182,10 @@ export default {
return;
}
- if (this.glFeatures.lazyLoadCommits) {
- this.delayedRowAppear = setTimeout(
- () => this.$emit('row-appear', this.rowNumber),
- ROW_APPEAR_DELAY,
- );
- }
+ this.delayedRowAppear = setTimeout(
+ () => this.$emit('row-appear', this.rowNumber),
+ ROW_APPEAR_DELAY,
+ );
},
rowDisappeared() {
clearTimeout(this.delayedRowAppear);
diff --git a/app/assets/javascripts/repository/components/tree_content.vue b/app/assets/javascripts/repository/components/tree_content.vue
index 8a45a351c35..4a8f83458f4 100644
--- a/app/assets/javascripts/repository/components/tree_content.vue
+++ b/app/assets/javascripts/repository/components/tree_content.vue
@@ -157,7 +157,7 @@ export default {
.find(({ hasNextPage }) => hasNextPage);
},
handleRowAppear(rowNumber) {
- if (!this.glFeatures.lazyLoadCommits || isRequested(rowNumber)) {
+ if (isRequested(rowNumber)) {
return;
}
diff --git a/app/assets/javascripts/repository/constants.js b/app/assets/javascripts/repository/constants.js
index 3a6d7d2f779..e194bddcc56 100644
--- a/app/assets/javascripts/repository/constants.js
+++ b/app/assets/javascripts/repository/constants.js
@@ -99,5 +99,4 @@ export const LEGACY_FILE_TYPES = [
'requirements_txt',
'cargo_toml',
'go_mod',
- 'go_sum',
];
diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js
index 1d295e18332..e9214e3acff 100644
--- a/app/assets/javascripts/repository/index.js
+++ b/app/assets/javascripts/repository/index.js
@@ -2,11 +2,12 @@ import { GlButton } from '@gitlab/ui';
import Vue from 'vue';
import Vuex from 'vuex';
import { parseBoolean } from '~/lib/utils/common_utils';
-import { escapeFileUrl } from '~/lib/utils/url_utility';
+import { escapeFileUrl, visitUrl } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import initWebIdeLink from '~/pages/projects/shared/web_ide_link';
import PerformancePlugin from '~/performance/vue_performance_plugin';
import createStore from '~/code_navigation/store';
+import RefSelector from '~/ref/components/ref_selector.vue';
import App from './components/app.vue';
import Breadcrumbs from './components/breadcrumbs.vue';
import DirectoryDownloadLinks from './components/directory_download_links.vue';
@@ -20,6 +21,7 @@ import refsQuery from './queries/ref.query.graphql';
import createRouter from './router';
import { updateFormAction } from './utils/dom';
import { setTitle } from './utils/title';
+import { generateRefDestinationPath } from './utils/ref_switcher_utils';
Vue.use(Vuex);
Vue.use(PerformancePlugin, {
@@ -89,9 +91,34 @@ export default function setupVueRepositoryList() {
},
});
- initLastCommitApp();
+ const initRefSwitcher = () => {
+ const refSwitcherEl = document.getElementById('js-tree-ref-switcher');
+
+ if (!refSwitcherEl) return false;
+
+ const { projectId, projectRootPath } = refSwitcherEl.dataset;
+
+ return new Vue({
+ el: refSwitcherEl,
+ render(createElement) {
+ return createElement(RefSelector, {
+ props: {
+ projectId,
+ value: ref,
+ },
+ on: {
+ input(selectedRef) {
+ visitUrl(generateRefDestinationPath(projectRootPath, selectedRef));
+ },
+ },
+ });
+ },
+ });
+ };
+ initLastCommitApp();
initBlobControlsApp();
+ initRefSwitcher();
router.afterEach(({ params: { path } }) => {
setTitle(path, ref, fullName);
diff --git a/app/assets/javascripts/repository/queries/commit.query.graphql b/app/assets/javascripts/repository/queries/commit.query.graphql
deleted file mode 100644
index 1a01462bd19..00000000000
--- a/app/assets/javascripts/repository/queries/commit.query.graphql
+++ /dev/null
@@ -1,7 +0,0 @@
-#import "ee_else_ce/repository/queries/commit.fragment.graphql"
-
-query getCommit($fileName: String!, $path: String!, $maxOffset: Number!) {
- commit(path: $path, fileName: $fileName, maxOffset: $maxOffset) @client {
- ...TreeEntryCommit
- }
-}
diff --git a/app/assets/javascripts/repository/utils/ref_switcher_utils.js b/app/assets/javascripts/repository/utils/ref_switcher_utils.js
new file mode 100644
index 00000000000..8ff52104c93
--- /dev/null
+++ b/app/assets/javascripts/repository/utils/ref_switcher_utils.js
@@ -0,0 +1,30 @@
+import { joinPaths } from '~/lib/utils/url_utility';
+
+/**
+ * Matches the namespace and target directory/blob in a path
+ * Example: /root/Flight/-/blob/fix/main/test/spec/utils_spec.js
+ * Group 1: /-/blob
+ * Group 2: blob
+ * Group 3: main/test/spec/utils_spec.js
+ */
+const NAMESPACE_TARGET_REGEX = /(\/-\/(blob|tree))\/.*?\/(.*)/;
+
+/**
+ * Generates a ref destination path based on the selected ref and current path.
+ * A user could either be in the project root, a directory on the blob view.
+ * @param {string} projectRootPath - The root path for a project.
+ * @param {string} selectedRef - The selected ref from the ref dropdown.
+ */
+export function generateRefDestinationPath(projectRootPath, selectedRef) {
+ const currentPath = window.location.pathname;
+ let namespace = '/-/tree';
+ let target;
+ const match = NAMESPACE_TARGET_REGEX.exec(currentPath);
+ if (match) {
+ [, namespace, , target] = match;
+ }
+
+ const destinationPath = joinPaths(projectRootPath, namespace, selectedRef, target);
+
+ return `${destinationPath}${window.location.hash}`;
+}
diff --git a/app/assets/javascripts/search/sidebar/components/confidentiality_filter.vue b/app/assets/javascripts/search/sidebar/components/confidentiality_filter.vue
index 38dccb9675d..4ddf695f61a 100644
--- a/app/assets/javascripts/search/sidebar/components/confidentiality_filter.vue
+++ b/app/assets/javascripts/search/sidebar/components/confidentiality_filter.vue
@@ -1,5 +1,5 @@
<script>
-import { mapState } from 'vuex';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { confidentialFilterData } from '../constants/confidential_filter_data';
import RadioFilter from './radio_filter.vue';
@@ -8,10 +8,10 @@ export default {
components: {
RadioFilter,
},
+ mixins: [glFeatureFlagsMixin()],
computed: {
- ...mapState(['query']),
- showDropdown() {
- return Object.values(confidentialFilterData.scopes).includes(this.query.scope);
+ ffBasedXPadding() {
+ return this.glFeatures.searchPageVerticalNav ? 'gl-px-5' : 'gl-px-0';
},
},
confidentialFilterData,
@@ -19,8 +19,8 @@ export default {
</script>
<template>
- <div v-if="showDropdown">
- <radio-filter :filter-data="$options.confidentialFilterData" />
+ <div>
+ <radio-filter :class="ffBasedXPadding" :filter-data="$options.confidentialFilterData" />
<hr class="gl-my-5 gl-border-gray-100" />
</div>
</template>
diff --git a/app/assets/javascripts/search/sidebar/components/results_filters.vue b/app/assets/javascripts/search/sidebar/components/results_filters.vue
index 5b53f94bb53..9b993ab9a86 100644
--- a/app/assets/javascripts/search/sidebar/components/results_filters.vue
+++ b/app/assets/javascripts/search/sidebar/components/results_filters.vue
@@ -2,6 +2,8 @@
import { GlButton, GlLink } from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import { confidentialFilterData } from '../constants/confidential_filter_data';
+import { stateFilterData } from '../constants/state_filter_data';
import ConfidentialityFilter from './confidentiality_filter.vue';
import StatusFilter from './status_filter.vue';
@@ -22,6 +24,15 @@ export default {
searchPageVerticalNavFeatureFlag() {
return this.glFeatures.searchPageVerticalNav;
},
+ showConfidentialityFilter() {
+ return Object.values(confidentialFilterData.scopes).includes(this.urlQuery.scope);
+ },
+ showStatusFilter() {
+ return Object.values(stateFilterData.scopes).includes(this.urlQuery.scope);
+ },
+ ffBasedXPadding() {
+ return this.glFeatures.searchPageVerticalNav ? 'gl-px-5' : 'gl-px-0';
+ },
},
methods: {
...mapActions(['applyQuery', 'resetQuery']),
@@ -30,14 +41,14 @@ export default {
</script>
<template>
- <form
- :class="searchPageVerticalNavFeatureFlag ? 'gl-px-5' : 'gl-px-0'"
- @submit.prevent="applyQuery"
- >
- <hr v-if="searchPageVerticalNavFeatureFlag" class="gl-my-5 gl-border-gray-100" />
- <status-filter />
- <confidentiality-filter />
- <div class="gl-display-flex gl-align-items-center gl-mt-4">
+ <form class="gl-pt-5 gl-md-pt-0" @submit.prevent="applyQuery">
+ <hr
+ v-if="searchPageVerticalNavFeatureFlag"
+ class="gl-my-5 gl-border-gray-100 gl-display-none gl-md-display-block"
+ />
+ <status-filter v-if="showStatusFilter" />
+ <confidentiality-filter v-if="showConfidentialityFilter" />
+ <div class="gl-display-flex gl-align-items-center gl-mt-4" :class="ffBasedXPadding">
<gl-button category="primary" variant="confirm" type="submit" :disabled="!sidebarDirty">
{{ __('Apply') }}
</gl-button>
diff --git a/app/assets/javascripts/search/sidebar/components/scope_navigation.vue b/app/assets/javascripts/search/sidebar/components/scope_navigation.vue
index f5e1525090e..7a03306e2f9 100644
--- a/app/assets/javascripts/search/sidebar/components/scope_navigation.vue
+++ b/app/assets/javascripts/search/sidebar/components/scope_navigation.vue
@@ -1,15 +1,23 @@
<script>
-import { GlNav, GlNavItem } from '@gitlab/ui';
+import { GlNav, GlNavItem, GlIcon } from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
-import { formatNumber } from '~/locale';
+import { formatNumber, s__ } from '~/locale';
import Tracking from '~/tracking';
-import { NAV_LINK_DEFAULT_CLASSES, NUMBER_FORMATING_OPTIONS } from '../constants';
+import {
+ NAV_LINK_DEFAULT_CLASSES,
+ NUMBER_FORMATING_OPTIONS,
+ NAV_LINK_COUNT_DEFAULT_CLASSES,
+} from '../constants';
export default {
name: 'ScopeNavigation',
+ i18n: {
+ countOverLimitLabel: s__('GlobalSearch|Result count is over limit.'),
+ },
components: {
GlNav,
GlNavItem,
+ GlIcon,
},
mixins: [Tracking.mixin()],
computed: {
@@ -20,9 +28,6 @@ export default {
},
methods: {
...mapActions(['fetchSidebarCount']),
- activeClasses(currentScope) {
- return currentScope === this.urlQuery.scope ? 'gl-font-weight-bold' : '';
- },
showFormatedCount(count) {
if (!count) {
return '0';
@@ -30,17 +35,27 @@ export default {
const countNumber = parseInt(count.replace(/,/g, ''), 10);
return formatNumber(countNumber, NUMBER_FORMATING_OPTIONS);
},
+ isCountOverLimit(count) {
+ return count.includes('+');
+ },
handleClick(scope) {
this.track('click_menu_item', { label: `vertical_navigation_${scope}` });
},
- linkClasses(scope) {
+ linkClasses(isHighlighted) {
+ return [...this.$options.NAV_LINK_DEFAULT_CLASSES, { 'gl-font-weight-bold': isHighlighted }];
+ },
+ countClasses(isHighlighted) {
return [
- { 'gl-font-weight-bold': scope === this.urlQuery.scope },
- ...this.$options.NAV_LINK_DEFAULT_CLASSES,
+ ...this.$options.NAV_LINK_COUNT_DEFAULT_CLASSES,
+ isHighlighted ? 'gl-text-gray-900' : 'gl-text-gray-500',
];
},
+ isActive(scope, index) {
+ return this.urlQuery.scope ? this.urlQuery.scope === scope : index === 0;
+ },
},
NAV_LINK_DEFAULT_CLASSES,
+ NAV_LINK_COUNT_DEFAULT_CLASSES,
};
</script>
@@ -50,14 +65,20 @@ export default {
<gl-nav-item
v-for="(item, scope, index) in navigation"
:key="scope"
- :link-classes="linkClasses(scope)"
+ :link-classes="linkClasses(isActive(scope, index))"
class="gl-mb-1"
:href="item.link"
- :active="urlQuery.scope ? urlQuery.scope === scope : index === 0"
+ :active="isActive(scope, index)"
@click="handleClick(scope)"
><span>{{ item.label }}</span
- ><span v-if="item.count" class="gl-font-sm gl-font-weight-normal">
- {{ showFormatedCount(item.count) }}
+ ><span v-if="item.count" :class="countClasses(isActive(scope, index))">
+ {{ showFormatedCount(item.count)
+ }}<gl-icon
+ v-if="isCountOverLimit(item.count)"
+ name="plus"
+ :aria-label="$options.i18n.countOverLimitLabel"
+ :size="8"
+ />
</span>
</gl-nav-item>
</gl-nav>
diff --git a/app/assets/javascripts/search/sidebar/components/status_filter.vue b/app/assets/javascripts/search/sidebar/components/status_filter.vue
index 5cec2090906..eaf7d95822a 100644
--- a/app/assets/javascripts/search/sidebar/components/status_filter.vue
+++ b/app/assets/javascripts/search/sidebar/components/status_filter.vue
@@ -1,5 +1,5 @@
<script>
-import { mapState } from 'vuex';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { stateFilterData } from '../constants/state_filter_data';
import RadioFilter from './radio_filter.vue';
@@ -8,10 +8,10 @@ export default {
components: {
RadioFilter,
},
+ mixins: [glFeatureFlagsMixin()],
computed: {
- ...mapState(['query']),
- showDropdown() {
- return Object.values(stateFilterData.scopes).includes(this.query.scope);
+ ffBasedXPadding() {
+ return this.glFeatures.searchPageVerticalNav ? 'gl-px-5' : 'gl-px-0';
},
},
stateFilterData,
@@ -19,8 +19,8 @@ export default {
</script>
<template>
- <div v-if="showDropdown">
- <radio-filter :filter-data="$options.stateFilterData" />
+ <div>
+ <radio-filter :class="ffBasedXPadding" :filter-data="$options.stateFilterData" />
<hr class="gl-my-5 gl-border-gray-100" />
</div>
</template>
diff --git a/app/assets/javascripts/search/sidebar/constants/index.js b/app/assets/javascripts/search/sidebar/constants/index.js
index 3621138afe4..a9c031f91a4 100644
--- a/app/assets/javascripts/search/sidebar/constants/index.js
+++ b/app/assets/javascripts/search/sidebar/constants/index.js
@@ -9,3 +9,5 @@ export const NAV_LINK_DEFAULT_CLASSES = [
'gl-justify-content-space-between',
'gl-text-gray-900',
];
+
+export const NAV_LINK_COUNT_DEFAULT_CLASSES = ['gl-font-sm', 'gl-font-weight-normal'];
diff --git a/app/assets/javascripts/search/topbar/components/app.vue b/app/assets/javascripts/search/topbar/components/app.vue
index d0fcbb0d83b..0629bea3239 100644
--- a/app/assets/javascripts/search/topbar/components/app.vue
+++ b/app/assets/javascripts/search/topbar/components/app.vue
@@ -1,9 +1,11 @@
<script>
-import { GlSearchBoxByClick } from '@gitlab/ui';
+import { GlSearchBoxByClick, GlButton } from '@gitlab/ui';
import { mapState, mapActions } from 'vuex';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { s__ } from '~/locale';
import { parseBoolean } from '~/lib/utils/common_utils';
+import MarkdownDrawer from '~/vue_shared/components/markdown_drawer/markdown_drawer.vue';
+import { SYNTAX_OPTIONS_DOCUMENT } from '../constants';
import GroupFilter from './group_filter.vue';
import ProjectFilter from './project_filter.vue';
@@ -12,24 +14,45 @@ export default {
i18n: {
searchPlaceholder: s__(`GlobalSearch|Search for projects, issues, etc.`),
searchLabel: s__(`GlobalSearch|What are you searching for?`),
+ documentFetchErrorMessage: s__(
+ 'GlobalSearch|There was an error fetching the "Syntax Options" document.',
+ ),
+ searchFieldLabel: s__('GlobalSearch|What are you searching for?'),
+ syntaxOptionsLabel: s__('GlobalSearch|Syntax options'),
+ groupFieldLabel: s__('GlobalSearch|Group'),
+ projectFieldLabel: s__('GlobalSearch|Project'),
+ searchButtonLabel: s__('GlobalSearch|Search'),
+ closeButtonLabel: s__('GlobalSearch|Close'),
},
components: {
+ GlButton,
GlSearchBoxByClick,
GroupFilter,
ProjectFilter,
+ MarkdownDrawer,
},
mixins: [glFeatureFlagsMixin()],
props: {
- groupInitialData: {
+ groupInitialJson: {
type: Object,
required: false,
default: () => ({}),
},
- projectInitialData: {
+ projectInitialJson: {
type: Object,
required: false,
default: () => ({}),
},
+ elasticsearchEnabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ defaultBranchName: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
computed: {
...mapState(['query']),
@@ -44,16 +67,26 @@ export default {
showFilters() {
return !parseBoolean(this.query.snippets);
},
+ showSyntaxOptions() {
+ return this.elasticsearchEnabled && this.isDefaultBranch;
+ },
hasVerticalNav() {
return this.glFeatures.searchPageVerticalNav;
},
+ isDefaultBranch() {
+ return !this.query.repository_ref || this.query.repository_ref === this.defaultBranchName;
+ },
},
created() {
this.preloadStoredFrequentItems();
},
methods: {
...mapActions(['applyQuery', 'setQuery', 'preloadStoredFrequentItems']),
+ onToggleDrawer() {
+ this.$refs.markdownDrawer.toggleDrawer();
+ },
},
+ SYNTAX_OPTIONS_DOCUMENT,
};
</script>
@@ -61,7 +94,25 @@ export default {
<section class="search-page-form gl-lg-display-flex gl-flex-direction-column">
<div class="gl-lg-display-flex gl-flex-direction-row gl-align-items-flex-end">
<div class="gl-flex-grow-1 gl-mb-4 gl-lg-mb-0 gl-lg-mr-2">
- <label>{{ $options.i18n.searchLabel }}</label>
+ <div
+ class="gl-sm-display-flex gl-flex-direction-row gl-justify-content-space-between gl-mb-4 gl-md-mb-0"
+ >
+ <label>{{ $options.i18n.searchLabel }}</label>
+ <template v-if="showSyntaxOptions">
+ <gl-button
+ category="tertiary"
+ variant="link"
+ size="small"
+ button-text-classes="gl-font-sm!"
+ @click="onToggleDrawer"
+ >{{ $options.i18n.syntaxOptionsLabel }}
+ </gl-button>
+ <markdown-drawer
+ ref="markdownDrawer"
+ :document-path="$options.SYNTAX_OPTIONS_DOCUMENT"
+ />
+ </template>
+ </div>
<gl-search-box-by-click
id="dashboard_search"
v-model="search"
@@ -70,13 +121,13 @@ export default {
@submit="applyQuery"
/>
</div>
- <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
- <label class="gl-display-block">{{ __('Group') }}</label>
- <group-filter :initial-data="groupInitialData" />
+ <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-3">
+ <label class="gl-display-block">{{ $options.i18n.groupFieldLabel }}</label>
+ <group-filter :initial-data="groupInitialJson" />
</div>
- <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
- <label class="gl-display-block">{{ __('Project') }}</label>
- <project-filter :initial-data="projectInitialData" />
+ <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-ml-3">
+ <label class="gl-display-block">{{ $options.i18n.projectFieldLabel }}</label>
+ <project-filter :initial-data="projectInitialJson" />
</div>
</div>
<hr v-if="hasVerticalNav" class="gl-mt-5 gl-mb-0 gl-border-gray-100" />
diff --git a/app/assets/javascripts/search/topbar/components/searchable_dropdown_item.vue b/app/assets/javascripts/search/topbar/components/searchable_dropdown_item.vue
index 70156142365..c1e33df3c42 100644
--- a/app/assets/javascripts/search/topbar/components/searchable_dropdown_item.vue
+++ b/app/assets/javascripts/search/topbar/components/searchable_dropdown_item.vue
@@ -1,5 +1,6 @@
<script>
-import { GlDropdownItem, GlAvatar, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlDropdownItem, GlAvatar } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import highlight from '~/lib/utils/highlight';
import { truncateNamespace } from '~/lib/utils/text_utility';
import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
diff --git a/app/assets/javascripts/search/topbar/constants.js b/app/assets/javascripts/search/topbar/constants.js
index dc040fdef34..121c15199dd 100644
--- a/app/assets/javascripts/search/topbar/constants.js
+++ b/app/assets/javascripts/search/topbar/constants.js
@@ -19,3 +19,5 @@ export const PROJECT_DATA = {
name: 'name',
fullName: 'name_with_namespace',
};
+
+export const SYNTAX_OPTIONS_DOCUMENT = 'drawers/user/search/advanced_search.md';
diff --git a/app/assets/javascripts/search/topbar/index.js b/app/assets/javascripts/search/topbar/index.js
index 87316e10e8d..d6e16085c28 100644
--- a/app/assets/javascripts/search/topbar/index.js
+++ b/app/assets/javascripts/search/topbar/index.js
@@ -11,10 +11,18 @@ export const initTopbar = (store) => {
return false;
}
- let { groupInitialData, projectInitialData } = el.dataset;
+ const {
+ groupInitialJson,
+ projectInitialJson,
+ elasticsearchEnabled,
+ defaultBranchName,
+ } = el.dataset;
- groupInitialData = JSON.parse(groupInitialData);
- projectInitialData = JSON.parse(projectInitialData);
+ const groupInitialJsonParsed = JSON.parse(groupInitialJson);
+ const projectInitialJsonParsed = JSON.parse(projectInitialJson);
+ const elasticsearchEnabledParsed = elasticsearchEnabled
+ ? JSON.parse(elasticsearchEnabled)
+ : false;
return new Vue({
el,
@@ -22,8 +30,10 @@ export const initTopbar = (store) => {
render(createElement) {
return createElement(GlobalSearchTopbar, {
props: {
- groupInitialData,
- projectInitialData,
+ groupInitialJson: groupInitialJsonParsed,
+ projectInitialJson: projectInitialJsonParsed,
+ elasticsearchEnabled: elasticsearchEnabledParsed,
+ defaultBranchName,
},
});
},
diff --git a/app/assets/javascripts/security_configuration/components/training_provider_list.vue b/app/assets/javascripts/security_configuration/components/training_provider_list.vue
index 0bcb2bb6720..6dae8e50908 100644
--- a/app/assets/javascripts/security_configuration/components/training_provider_list.vue
+++ b/app/assets/javascripts/security_configuration/components/training_provider_list.vue
@@ -8,9 +8,9 @@ import {
GlLink,
GlSkeletonLoader,
GlIcon,
- GlSafeHtmlDirective,
} from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import Tracking from '~/tracking';
import { __, s__ } from '~/locale';
import {
@@ -54,7 +54,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [Tracking.mixin()],
inject: ['projectFullPath'],
diff --git a/app/assets/javascripts/self_monitor/components/self_monitor_form.vue b/app/assets/javascripts/self_monitor/components/self_monitor_form.vue
index ffba3aac681..d9e969e2278 100644
--- a/app/assets/javascripts/self_monitor/components/self_monitor_form.vue
+++ b/app/assets/javascripts/self_monitor/components/self_monitor_form.vue
@@ -1,15 +1,8 @@
<script>
-import {
- GlFormGroup,
- GlButton,
- GlModal,
- GlToast,
- GlToggle,
- GlLink,
- GlSafeHtmlDirective,
-} from '@gitlab/ui';
+import { GlFormGroup, GlButton, GlModal, GlToast, GlToggle, GlLink } from '@gitlab/ui';
import Vue from 'vue';
import { mapState, mapActions } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { helpPagePath } from '~/helpers/help_page_helper';
import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants';
import { visitUrl, getBaseURL } from '~/lib/utils/url_utility';
@@ -26,7 +19,7 @@ export default {
GlLink,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
formLabels: {
createProject: __('Self-monitoring'),
diff --git a/app/assets/javascripts/self_monitor/store/actions.js b/app/assets/javascripts/self_monitor/store/actions.js
index 5b9e994290c..7198dbe8b04 100644
--- a/app/assets/javascripts/self_monitor/store/actions.js
+++ b/app/assets/javascripts/self_monitor/store/actions.js
@@ -1,6 +1,6 @@
import axios from '~/lib/utils/axios_utils';
import { backOff } from '~/lib/utils/common_utils';
-import statusCodes from '~/lib/utils/http_status';
+import statusCodes, { HTTP_STATUS_ACCEPTED } from '~/lib/utils/http_status';
import { __, s__ } from '~/locale';
import * as types from './mutation_types';
@@ -10,7 +10,7 @@ function backOffRequest(makeRequestCallback) {
return backOff((next, stop) => {
makeRequestCallback()
.then((resp) => {
- if (resp.status === statusCodes.ACCEPTED) {
+ if (resp.status === HTTP_STATUS_ACCEPTED) {
next();
} else {
stop(resp);
@@ -31,7 +31,7 @@ export const requestCreateProject = ({ dispatch, state, commit }) => {
axios
.post(state.createProjectEndpoint)
.then((resp) => {
- if (resp.status === statusCodes.ACCEPTED) {
+ if (resp.status === HTTP_STATUS_ACCEPTED) {
dispatch('requestCreateProjectStatus', resp.data.job_id);
}
})
@@ -83,7 +83,7 @@ export const requestDeleteProject = ({ dispatch, state, commit }) => {
axios
.delete(state.deleteProjectEndpoint)
.then((resp) => {
- if (resp.status === statusCodes.ACCEPTED) {
+ if (resp.status === HTTP_STATUS_ACCEPTED) {
dispatch('requestDeleteProjectStatus', resp.data.job_id);
}
})
diff --git a/app/assets/javascripts/sentry/constants.js b/app/assets/javascripts/sentry/constants.js
index fd96da5faf6..5531c4f56db 100644
--- a/app/assets/javascripts/sentry/constants.js
+++ b/app/assets/javascripts/sentry/constants.js
@@ -1,5 +1,6 @@
import { __ } from '~/locale';
+// TODO: Remove in favor of https://gitlab.com/gitlab-org/gitlab/issues/35144
export const IGNORE_ERRORS = [
// Random plugins/extensions
'top.GLOBALS',
diff --git a/app/assets/javascripts/sentry/index.js b/app/assets/javascripts/sentry/index.js
index 176745b4177..5539a061726 100644
--- a/app/assets/javascripts/sentry/index.js
+++ b/app/assets/javascripts/sentry/index.js
@@ -1,26 +1,34 @@
import '../webpack';
+import * as Sentry from 'sentrybrowser7';
import SentryConfig from './sentry_config';
const index = function index() {
+ // Configuration for newer versions of Sentry SDK (v7)
SentryConfig.init({
dsn: gon.sentry_dsn,
+ environment: gon.sentry_environment,
currentUserId: gon.current_user_id,
- whitelistUrls:
+ allowUrls:
process.env.NODE_ENV === 'production'
? [gon.gitlab_url]
: [gon.gitlab_url, 'webpack-internal://'],
- environment: gon.sentry_environment,
release: gon.revision,
tags: {
revision: gon.revision,
feature_category: gon.feature_category,
},
});
-
- return SentryConfig;
};
index();
+// The _Sentry object is globally exported so it can be used by
+// ./sentry_browser_wrapper.js
+// This hack allows us to load a single version of `@sentry/browser`
+// in the browser, see app/views/layouts/_head.html.haml to find how it is imported.
+
+// eslint-disable-next-line no-underscore-dangle
+window._Sentry = Sentry;
+
export default index;
diff --git a/app/assets/javascripts/sentry/legacy_index.js b/app/assets/javascripts/sentry/legacy_index.js
new file mode 100644
index 00000000000..604b982e128
--- /dev/null
+++ b/app/assets/javascripts/sentry/legacy_index.js
@@ -0,0 +1,34 @@
+import '../webpack';
+
+import * as Sentry5 from 'sentrybrowser5';
+import LegacySentryConfig from './legacy_sentry_config';
+
+const index = function index() {
+ // Configuration for legacy versions of Sentry SDK (v5)
+ LegacySentryConfig.init({
+ dsn: gon.sentry_dsn,
+ currentUserId: gon.current_user_id,
+ whitelistUrls:
+ process.env.NODE_ENV === 'production'
+ ? [gon.gitlab_url]
+ : [gon.gitlab_url, 'webpack-internal://'],
+ environment: gon.sentry_environment,
+ release: gon.revision,
+ tags: {
+ revision: gon.revision,
+ feature_category: gon.feature_category,
+ },
+ });
+};
+
+index();
+
+// The _Sentry object is globally exported so it can be used by
+// ./sentry_browser_wrapper.js
+// This hack allows us to load a single version of `@sentry/browser`
+// in the browser, see app/views/layouts/_head.html.haml to find how it is imported.
+
+// eslint-disable-next-line no-underscore-dangle
+window._Sentry = Sentry5;
+
+export default index;
diff --git a/app/assets/javascripts/sentry/legacy_sentry_config.js b/app/assets/javascripts/sentry/legacy_sentry_config.js
new file mode 100644
index 00000000000..50a943886db
--- /dev/null
+++ b/app/assets/javascripts/sentry/legacy_sentry_config.js
@@ -0,0 +1,64 @@
+import * as Sentry5 from 'sentrybrowser5';
+import $ from 'jquery';
+import { __ } from '~/locale';
+import { IGNORE_ERRORS, DENY_URLS, SAMPLE_RATE } from './constants';
+
+const SentryConfig = {
+ IGNORE_ERRORS,
+ BLACKLIST_URLS: DENY_URLS,
+ SAMPLE_RATE,
+ init(options = {}) {
+ this.options = options;
+
+ this.configure();
+ this.bindSentryErrors();
+ if (this.options.currentUserId) this.setUser();
+ },
+
+ configure() {
+ const { dsn, release, tags, whitelistUrls, environment } = this.options;
+
+ Sentry5.init({
+ dsn,
+ release,
+ whitelistUrls,
+ environment,
+ ignoreErrors: this.IGNORE_ERRORS, // TODO: Remove in favor of https://gitlab.com/gitlab-org/gitlab/issues/35144
+ blacklistUrls: this.BLACKLIST_URLS,
+ sampleRate: SAMPLE_RATE,
+ });
+
+ Sentry5.setTags(tags);
+ },
+
+ setUser() {
+ Sentry5.setUser({
+ id: this.options.currentUserId,
+ });
+ },
+
+ bindSentryErrors() {
+ $(document).on('ajaxError.sentry', this.handleSentryErrors);
+ },
+
+ handleSentryErrors(event, req, config, err) {
+ const error = err || req.statusText;
+ const { responseText = __('Unknown response text') } = req;
+ const { type, url, data } = config;
+ const { status } = req;
+
+ Sentry5.captureMessage(error, {
+ extra: {
+ type,
+ url,
+ data,
+ status,
+ response: responseText,
+ error,
+ event,
+ },
+ });
+ },
+};
+
+export default SentryConfig;
diff --git a/app/assets/javascripts/sentry/sentry_browser_wrapper.js b/app/assets/javascripts/sentry/sentry_browser_wrapper.js
new file mode 100644
index 00000000000..0382827f82c
--- /dev/null
+++ b/app/assets/javascripts/sentry/sentry_browser_wrapper.js
@@ -0,0 +1,27 @@
+// The _Sentry object is globally exported so it can be used here
+// This hack allows us to load a single version of `@sentry/browser`
+// in the browser (or none). See app/views/layouts/_head.html.haml
+// to find how it is imported.
+
+// This module wraps methods used by our production code.
+// Each export is names as we cannot export the entire namespace from *.
+export const captureException = (...args) => {
+ // eslint-disable-next-line no-underscore-dangle
+ const Sentry = window._Sentry;
+
+ Sentry?.captureException(...args);
+};
+
+export const captureMessage = (...args) => {
+ // eslint-disable-next-line no-underscore-dangle
+ const Sentry = window._Sentry;
+
+ Sentry?.captureMessage(...args);
+};
+
+export const withScope = (...args) => {
+ // eslint-disable-next-line no-underscore-dangle
+ const Sentry = window._Sentry;
+
+ Sentry?.withScope(...args);
+};
diff --git a/app/assets/javascripts/sentry/sentry_config.js b/app/assets/javascripts/sentry/sentry_config.js
index 4c5b8dbad5a..ed8a55b7d44 100644
--- a/app/assets/javascripts/sentry/sentry_config.js
+++ b/app/assets/javascripts/sentry/sentry_config.js
@@ -1,30 +1,24 @@
-import * as Sentry from '@sentry/browser';
-import $ from 'jquery';
-import { __ } from '~/locale';
+import * as Sentry from 'sentrybrowser7';
import { IGNORE_ERRORS, DENY_URLS, SAMPLE_RATE } from './constants';
const SentryConfig = {
- IGNORE_ERRORS,
- BLACKLIST_URLS: DENY_URLS,
- SAMPLE_RATE,
init(options = {}) {
this.options = options;
this.configure();
- this.bindSentryErrors();
if (this.options.currentUserId) this.setUser();
},
configure() {
- const { dsn, release, tags, whitelistUrls, environment } = this.options;
+ const { dsn, release, tags, allowUrls, environment } = this.options;
Sentry.init({
dsn,
release,
- whitelistUrls,
+ allowUrls,
environment,
- ignoreErrors: this.IGNORE_ERRORS, // TODO: Remove in favor of https://gitlab.com/gitlab-org/gitlab/issues/35144
- blacklistUrls: this.BLACKLIST_URLS,
+ ignoreErrors: IGNORE_ERRORS,
+ denyUrls: DENY_URLS,
sampleRate: SAMPLE_RATE,
});
@@ -36,29 +30,6 @@ const SentryConfig = {
id: this.options.currentUserId,
});
},
-
- bindSentryErrors() {
- $(document).on('ajaxError.sentry', this.handleSentryErrors);
- },
-
- handleSentryErrors(event, req, config, err) {
- const error = err || req.statusText;
- const { responseText = __('Unknown response text') } = req;
- const { type, url, data } = config;
- const { status } = req;
-
- Sentry.captureMessage(error, {
- extra: {
- type,
- url,
- data,
- status,
- response: responseText,
- error,
- event,
- },
- });
- },
};
export default SentryConfig;
diff --git a/app/assets/javascripts/set_status_modal/set_status_form.vue b/app/assets/javascripts/set_status_modal/set_status_form.vue
index 86049a2b781..dd27a12cbee 100644
--- a/app/assets/javascripts/set_status_modal/set_status_form.vue
+++ b/app/assets/javascripts/set_status_modal/set_status_form.vue
@@ -10,9 +10,9 @@ import {
GlDropdownItem,
GlSprintf,
GlFormGroup,
- GlSafeHtmlDirective,
} from '@gitlab/ui';
import $ from 'jquery';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
import * as Emoji from '~/emoji';
import { s__ } from '~/locale';
@@ -33,7 +33,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
defaultEmoji: {
diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
index 80158c55dbc..5becc03646e 100644
--- a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
+++ b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
@@ -1,5 +1,5 @@
<script>
-import { GlToast, GlTooltipDirective, GlSafeHtmlDirective, GlModal } from '@gitlab/ui';
+import { GlToast, GlTooltipDirective, GlModal } from '@gitlab/ui';
import Vue from 'vue';
import { createAlert } from '~/flash';
import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants';
@@ -19,7 +19,6 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
- SafeHtml: GlSafeHtmlDirective,
},
mixins: [glFeatureFlagsMixin()],
props: {
@@ -110,7 +109,6 @@ export default {
this.availability = value;
},
},
- safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
actionPrimary: { text: s__('SetStatusModal|Set status') },
actionSecondary: { text: s__('SetStatusModal|Remove status') },
};
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees_realtime.vue b/app/assets/javascripts/sidebar/components/assignees/assignees_realtime.vue
index 78d12ac113b..93fcf2cf1c9 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignees_realtime.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignees_realtime.vue
@@ -1,7 +1,7 @@
<script>
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { IssuableType } from '~/issues/constants';
-import { assigneesQueries } from '~/sidebar/constants';
+import { assigneesQueries } from '../../constants';
export default {
subscription: null,
diff --git a/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue b/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue
index 4408ebb881b..fd51cd5bb16 100644
--- a/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue
@@ -1,7 +1,7 @@
<script>
import { GlButton } from '@gitlab/ui';
import { n__ } from '~/locale';
-import UncollapsedAssigneeList from '~/sidebar/components/assignees/uncollapsed_assignee_list.vue';
+import UncollapsedAssigneeList from './uncollapsed_assignee_list.vue';
export default {
components: {
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
index 15fd365b4da..7979f450fdd 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
@@ -2,9 +2,9 @@
import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
-import eventHub from '~/sidebar/event_hub';
-import Store from '~/sidebar/stores/sidebar_store';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import eventHub from '../../event_hub';
+import Store from '../../stores/sidebar_store';
import AssigneeTitle from './assignee_title.vue';
import Assignees from './assignees.vue';
import AssigneesRealtime from './assignees_realtime.vue';
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
index 395dcf73693..d6c679f2f07 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
@@ -4,12 +4,12 @@ import Vue from 'vue';
import { createAlert } from '~/flash';
import { IssuableType } from '~/issues/constants';
import { __, n__ } from '~/locale';
-import SidebarAssigneesRealtime from '~/sidebar/components/assignees/assignees_realtime.vue';
-import IssuableAssignees from '~/sidebar/components/assignees/issuable_assignees.vue';
-import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
-import { assigneesQueries } from '~/sidebar/constants';
import UserSelect from '~/vue_shared/components/user_select/user_select.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import { assigneesQueries } from '../../constants';
+import SidebarEditableItem from '../sidebar_editable_item.vue';
+import SidebarAssigneesRealtime from './assignees_realtime.vue';
+import IssuableAssignees from './issuable_assignees.vue';
import SidebarInviteMembers from './sidebar_invite_members.vue';
export const assigneesWidget = Vue.observable({
diff --git a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue
index 3532b75b6e7..dbedfe57325 100644
--- a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue
@@ -3,7 +3,7 @@ import { GlSprintf, GlButton } from '@gitlab/ui';
import { createAlert } from '~/flash';
import { IssuableType } from '~/issues/constants';
import { __, sprintf } from '~/locale';
-import { confidentialityQueries } from '~/sidebar/constants';
+import { confidentialityQueries } from '../../constants';
export default {
i18n: {
diff --git a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue
index f3bd58c11d4..c2f239b56c7 100644
--- a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue
@@ -3,8 +3,8 @@ import produce from 'immer';
import Vue from 'vue';
import { createAlert } from '~/flash';
import { __, sprintf } from '~/locale';
-import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
-import { confidentialityQueries, Tracking } from '~/sidebar/constants';
+import { confidentialityQueries, Tracking } from '../../constants';
+import SidebarEditableItem from '../sidebar_editable_item.vue';
import SidebarConfidentialityContent from './sidebar_confidentiality_content.vue';
import SidebarConfidentialityForm from './sidebar_confidentiality_form.vue';
diff --git a/app/assets/javascripts/sidebar/components/copy/copy_email_to_clipboard.vue b/app/assets/javascripts/sidebar/components/copy/copy_email_to_clipboard.vue
new file mode 100644
index 00000000000..96ecdc84ef5
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/copy/copy_email_to_clipboard.vue
@@ -0,0 +1,24 @@
+<script>
+import CopyableField from './copyable_field.vue';
+
+export default {
+ components: {
+ CopyableField,
+ },
+ props: {
+ issueEmailAddress: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <copyable-field
+ data-testid="copy-forward-email"
+ :name="s__('RightSidebar|Issue email')"
+ :clipboard-tooltip-text="s__('RightSidebar|Copy email address')"
+ :value="issueEmailAddress"
+ />
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/copyable_field.vue b/app/assets/javascripts/sidebar/components/copy/copyable_field.vue
index 6538de085b0..6538de085b0 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/copyable_field.vue
+++ b/app/assets/javascripts/sidebar/components/copy/copyable_field.vue
diff --git a/app/assets/javascripts/sidebar/components/copy/sidebar_reference_widget.vue b/app/assets/javascripts/sidebar/components/copy/sidebar_reference_widget.vue
new file mode 100644
index 00000000000..3287539e502
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/copy/sidebar_reference_widget.vue
@@ -0,0 +1,59 @@
+<script>
+import { __ } from '~/locale';
+import { referenceQueries } from '../../constants';
+import CopyableField from './copyable_field.vue';
+
+export default {
+ components: {
+ CopyableField,
+ },
+ inject: ['fullPath', 'iid'],
+ props: {
+ issuableType: {
+ required: true,
+ type: String,
+ },
+ },
+ data() {
+ return {
+ reference: '',
+ };
+ },
+ apollo: {
+ reference: {
+ query() {
+ return referenceQueries[this.issuableType].query;
+ },
+ variables() {
+ return {
+ fullPath: this.fullPath,
+ iid: this.iid,
+ };
+ },
+ update(data) {
+ return data.workspace?.issuable?.reference || '';
+ },
+ error(error) {
+ this.$emit('fetch-error', {
+ message: __('An error occurred while fetching reference'),
+ error,
+ });
+ },
+ },
+ },
+ computed: {
+ isLoading() {
+ return this.$apollo.queries.reference.loading;
+ },
+ },
+};
+</script>
+
+<template>
+ <copyable-field
+ class="sub-block"
+ :is-loading="isLoading"
+ :name="__('Reference')"
+ :value="reference"
+ />
+</template>
diff --git a/app/assets/javascripts/sidebar/components/copy_email_to_clipboard.vue b/app/assets/javascripts/sidebar/components/copy_email_to_clipboard.vue
deleted file mode 100644
index fd652583f76..00000000000
--- a/app/assets/javascripts/sidebar/components/copy_email_to_clipboard.vue
+++ /dev/null
@@ -1,24 +0,0 @@
-<script>
-import CopyableField from '~/vue_shared/components/sidebar/copyable_field.vue';
-
-export default {
- components: {
- CopyableField,
- },
- props: {
- issueEmailAddress: {
- type: String,
- required: true,
- },
- },
-};
-</script>
-
-<template>
- <copyable-field
- data-testid="copy-forward-email"
- :name="s__('RightSidebar|Issue email')"
- :clipboard-tooltip-text="s__('RightSidebar|Copy email address')"
- :value="issueEmailAddress"
- />
-</template>
diff --git a/app/assets/javascripts/sidebar/components/crm_contacts/crm_contacts.vue b/app/assets/javascripts/sidebar/components/crm_contacts/crm_contacts.vue
index 81090bfa062..0660e4f58e4 100644
--- a/app/assets/javascripts/sidebar/components/crm_contacts/crm_contacts.vue
+++ b/app/assets/javascripts/sidebar/components/crm_contacts/crm_contacts.vue
@@ -4,8 +4,8 @@ import { __, n__, sprintf } from '~/locale';
import { createAlert } from '~/flash';
import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
import { TYPE_ISSUE } from '~/graphql_shared/constants';
-import getIssueCrmContactsQuery from './queries/get_issue_crm_contacts.query.graphql';
-import issueCrmContactsSubscription from './queries/issue_crm_contacts.subscription.graphql';
+import getIssueCrmContactsQuery from '../../queries/get_issue_crm_contacts.query.graphql';
+import issueCrmContactsSubscription from '../../queries/issue_crm_contacts.subscription.graphql';
export default {
components: {
diff --git a/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue b/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue
index c262d65f6ce..eb48732f558 100644
--- a/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue
+++ b/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue
@@ -4,14 +4,8 @@ import { createAlert } from '~/flash';
import { IssuableType } from '~/issues/constants';
import { dateInWords, formatDate, parsePikadayDate } from '~/lib/utils/datetime_utility';
import { __, sprintf } from '~/locale';
-import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
-import {
- dateFields,
- dateTypes,
- dueDateQueries,
- startDateQueries,
- Tracking,
-} from '~/sidebar/constants';
+import { dateFields, dateTypes, dueDateQueries, startDateQueries, Tracking } from '../../constants';
+import SidebarEditableItem from '../sidebar_editable_item.vue';
import SidebarFormattedDate from './sidebar_formatted_date.vue';
import SidebarInheritDate from './sidebar_inherit_date.vue';
diff --git a/app/assets/javascripts/sidebar/components/incidents/constants.js b/app/assets/javascripts/sidebar/components/incidents/constants.js
deleted file mode 100644
index cd05a6099fd..00000000000
--- a/app/assets/javascripts/sidebar/components/incidents/constants.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { s__ } from '~/locale';
-
-export const STATUS_TRIGGERED = 'TRIGGERED';
-export const STATUS_ACKNOWLEDGED = 'ACKNOWLEDGED';
-export const STATUS_RESOLVED = 'RESOLVED';
-
-export const STATUS_TRIGGERED_LABEL = s__('IncidentManagement|Triggered');
-export const STATUS_ACKNOWLEDGED_LABEL = s__('IncidentManagement|Acknowledged');
-export const STATUS_RESOLVED_LABEL = s__('IncidentManagement|Resolved');
-
-export const STATUS_LABELS = {
- [STATUS_TRIGGERED]: STATUS_TRIGGERED_LABEL,
- [STATUS_ACKNOWLEDGED]: STATUS_ACKNOWLEDGED_LABEL,
- [STATUS_RESOLVED]: STATUS_RESOLVED_LABEL,
-};
-
-export const i18n = {
- fetchError: s__(
- 'IncidentManagement|An error occurred while fetching the incident status. Please reload the page.',
- ),
- title: s__('IncidentManagement|Status'),
- updateError: s__(
- 'IncidentManagement|An error occurred while updating the incident status. Please reload the page and try again.',
- ),
-};
diff --git a/app/assets/javascripts/sidebar/components/incidents/escalation_status.vue b/app/assets/javascripts/sidebar/components/incidents/escalation_status.vue
index 9c41db98c63..72a572087c7 100644
--- a/app/assets/javascripts/sidebar/components/incidents/escalation_status.vue
+++ b/app/assets/javascripts/sidebar/components/incidents/escalation_status.vue
@@ -1,7 +1,12 @@
<script>
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
-import { i18n, STATUS_ACKNOWLEDGED, STATUS_TRIGGERED, STATUS_RESOLVED } from './constants';
-import { getStatusLabel } from './utils';
+import {
+ INCIDENTS_I18N as i18n,
+ STATUS_ACKNOWLEDGED,
+ STATUS_TRIGGERED,
+ STATUS_RESOLVED,
+} from '../../constants';
+import { getStatusLabel } from '../../utils';
const STATUS_LIST = [STATUS_TRIGGERED, STATUS_ACKNOWLEDGED, STATUS_RESOLVED];
diff --git a/app/assets/javascripts/sidebar/components/incidents/sidebar_escalation_status.vue b/app/assets/javascripts/sidebar/components/incidents/sidebar_escalation_status.vue
index 67ae1e6fcab..f7daad63f45 100644
--- a/app/assets/javascripts/sidebar/components/incidents/sidebar_escalation_status.vue
+++ b/app/assets/javascripts/sidebar/components/incidents/sidebar_escalation_status.vue
@@ -1,12 +1,15 @@
<script>
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
-import { escalationStatusQuery, escalationStatusMutation } from '~/sidebar/constants';
import { createAlert } from '~/flash';
import { logError } from '~/lib/logger';
import EscalationStatus from 'ee_else_ce/sidebar/components/incidents/escalation_status.vue';
+import {
+ escalationStatusQuery,
+ escalationStatusMutation,
+ INCIDENTS_I18N as i18n,
+} from '../../constants';
+import { getStatusLabel } from '../../utils';
import SidebarEditableItem from '../sidebar_editable_item.vue';
-import { i18n } from './constants';
-import { getStatusLabel } from './utils';
export default {
i18n,
diff --git a/app/assets/javascripts/sidebar/components/incidents/utils.js b/app/assets/javascripts/sidebar/components/incidents/utils.js
deleted file mode 100644
index 59bf1ea466c..00000000000
--- a/app/assets/javascripts/sidebar/components/incidents/utils.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { s__ } from '~/locale';
-
-import { STATUS_LABELS } from './constants';
-
-export const getStatusLabel = (status) => STATUS_LABELS[status] ?? s__('IncidentManagement|None');
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/constants.js
index 00c54313292..00c54313292 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/constants.js
diff --git a/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_button.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_button.vue
new file mode 100644
index 00000000000..864d9b308e7
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_button.vue
@@ -0,0 +1,45 @@
+<script>
+import { GlButton, GlIcon } from '@gitlab/ui';
+import { mapActions, mapGetters } from 'vuex';
+
+// @deprecated This component should only be used when there is no GraphQL API.
+// In most cases you should use
+// `app/assets/javascripts/sidebar/components/labels/labels_select_widget` instead.
+export default {
+ components: {
+ GlButton,
+ GlIcon,
+ },
+ computed: {
+ ...mapGetters([
+ 'dropdownButtonText',
+ 'isDropdownVariantStandalone',
+ 'isDropdownVariantEmbedded',
+ ]),
+ },
+ methods: {
+ ...mapActions(['toggleDropdownContents']),
+ handleButtonClick(e) {
+ if (this.isDropdownVariantStandalone || this.isDropdownVariantEmbedded) {
+ this.toggleDropdownContents();
+ }
+
+ if (this.isDropdownVariantStandalone) {
+ e.stopPropagation();
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-button
+ class="labels-select-dropdown-button js-dropdown-button w-100 text-left"
+ @click="handleButtonClick"
+ >
+ <span class="dropdown-toggle-text gl-pointer-events-none flex-fill">
+ {{ dropdownButtonText }}
+ </span>
+ <gl-icon name="chevron-down" class="gl-pointer-events-none float-right" />
+ </gl-button>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_contents.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_contents.vue
new file mode 100644
index 00000000000..89a976d45fa
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_contents.vue
@@ -0,0 +1,48 @@
+<script>
+import { mapGetters, mapState } from 'vuex';
+
+import DropdownContentsCreateView from './dropdown_contents_create_view.vue';
+import DropdownContentsLabelsView from './dropdown_contents_labels_view.vue';
+
+// @deprecated This component should only be used when there is no GraphQL API.
+// In most cases you should use
+// `app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents.vue` instead.
+export default {
+ components: {
+ DropdownContentsLabelsView,
+ DropdownContentsCreateView,
+ },
+ props: {
+ renderOnTop: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ ...mapState(['showDropdownContentsCreateView']),
+ ...mapGetters(['isDropdownVariantSidebar']),
+ dropdownContentsView() {
+ if (this.showDropdownContentsCreateView) {
+ return 'dropdown-contents-create-view';
+ }
+ return 'dropdown-contents-labels-view';
+ },
+ directionStyle() {
+ const bottom = this.isDropdownVariantSidebar ? '3rem' : '2rem';
+ return this.renderOnTop ? { bottom } : {};
+ },
+ },
+};
+</script>
+
+<template>
+ <div
+ class="labels-select-dropdown-contents gl-w-full gl-my-2 gl-py-3 gl-rounded-base gl-absolute"
+ data-testid="labels-select-dropdown-contents"
+ data-qa-selector="labels_dropdown_content"
+ :style="directionStyle"
+ >
+ <component :is="dropdownContentsView" />
+ </div>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_contents_create_view.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_contents_create_view.vue
new file mode 100644
index 00000000000..b8afa67a947
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_contents_create_view.vue
@@ -0,0 +1,122 @@
+<script>
+import { GlTooltipDirective, GlButton, GlFormInput, GlLink, GlLoadingIcon } from '@gitlab/ui';
+import { mapState, mapActions } from 'vuex';
+
+// @deprecated This component should only be used when there is no GraphQL API.
+// In most cases you should use
+// `app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view.vue` instead.
+export default {
+ components: {
+ GlButton,
+ GlFormInput,
+ GlLink,
+ GlLoadingIcon,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ data() {
+ return {
+ labelTitle: '',
+ selectedColor: '',
+ };
+ },
+ computed: {
+ ...mapState(['labelsCreateTitle', 'labelCreateInProgress']),
+ disableCreate() {
+ return !this.labelTitle.length || !this.selectedColor.length || this.labelCreateInProgress;
+ },
+ suggestedColors() {
+ const colorsMap = gon.suggested_label_colors;
+ return Object.keys(colorsMap).map((color) => ({ [color]: colorsMap[color] }));
+ },
+ },
+ methods: {
+ ...mapActions(['toggleDropdownContents', 'toggleDropdownContentsCreateView', 'createLabel']),
+ getColorCode(color) {
+ return Object.keys(color).pop();
+ },
+ getColorName(color) {
+ return Object.values(color).pop();
+ },
+ handleColorClick(color) {
+ this.selectedColor = this.getColorCode(color);
+ },
+ handleCreateClick() {
+ this.createLabel({
+ title: this.labelTitle,
+ color: this.selectedColor,
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="labels-select-contents-create js-labels-create">
+ <div class="dropdown-title d-flex align-items-center pt-0 pb-2 gl-mb-0">
+ <gl-button
+ :aria-label="__('Go back')"
+ category="tertiary"
+ size="small"
+ class="js-btn-back dropdown-header-button p-0"
+ icon="arrow-left"
+ @click="toggleDropdownContentsCreateView"
+ />
+ <span class="flex-grow-1">{{ labelsCreateTitle }}</span>
+ <gl-button
+ :aria-label="__('Close')"
+ category="tertiary"
+ size="small"
+ class="dropdown-header-button p-0"
+ icon="close"
+ @click="toggleDropdownContents"
+ />
+ </div>
+ <div class="dropdown-input">
+ <gl-form-input
+ v-model.trim="labelTitle"
+ :placeholder="__('Name new label')"
+ :autofocus="true"
+ />
+ </div>
+ <div class="dropdown-content px-2">
+ <div class="suggest-colors suggest-colors-dropdown mt-0 mb-2">
+ <gl-link
+ v-for="(color, index) in suggestedColors"
+ :key="index"
+ v-gl-tooltip:tooltipcontainer
+ :style="{ backgroundColor: getColorCode(color) }"
+ :title="getColorName(color)"
+ @click.prevent="handleColorClick(color)"
+ />
+ </div>
+ <div class="color-input-container gl-display-flex">
+ <span
+ class="dropdown-label-color-preview position-relative position-relative d-inline-block"
+ :style="{ backgroundColor: selectedColor }"
+ ></span>
+ <gl-form-input
+ v-model.trim="selectedColor"
+ class="gl-rounded-top-left-none gl-rounded-bottom-left-none gl-mb-2"
+ :placeholder="__('Use custom color #FF0000')"
+ />
+ </div>
+ </div>
+ <div class="dropdown-actions clearfix pt-2 px-2">
+ <gl-button
+ :disabled="disableCreate"
+ category="primary"
+ variant="confirm"
+ class="float-left d-flex align-items-center"
+ @click="handleCreateClick"
+ >
+ <gl-loading-icon v-show="labelCreateInProgress" size="sm" :inline="true" class="mr-1" />
+ {{ __('Create') }}
+ </gl-button>
+ <gl-button class="float-right js-btn-cancel-create" @click="toggleDropdownContentsCreateView">
+ {{ __('Cancel') }}
+ </gl-button>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view.vue
new file mode 100644
index 00000000000..ee6b531c1ca
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view.vue
@@ -0,0 +1,230 @@
+<script>
+import {
+ GlIntersectionObserver,
+ GlLoadingIcon,
+ GlButton,
+ GlSearchBoxByType,
+ GlLink,
+} from '@gitlab/ui';
+import fuzzaldrinPlus from 'fuzzaldrin-plus';
+import { mapState, mapGetters, mapActions } from 'vuex';
+
+import { UP_KEY_CODE, DOWN_KEY_CODE, ENTER_KEY_CODE, ESC_KEY_CODE } from '~/lib/utils/keycodes';
+
+import LabelItem from './label_item.vue';
+
+// @deprecated This component should only be used when there is no GraphQL API.
+// In most cases you should use
+// `app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents_labels_view.vue` instead.
+export default {
+ components: {
+ GlIntersectionObserver,
+ GlLoadingIcon,
+ GlButton,
+ GlSearchBoxByType,
+ GlLink,
+ LabelItem,
+ },
+ data() {
+ return {
+ searchKey: '',
+ currentHighlightItem: -1,
+ };
+ },
+ computed: {
+ ...mapState([
+ 'allowLabelCreate',
+ 'allowMultiselect',
+ 'labelsManagePath',
+ 'labels',
+ 'labelsFetchInProgress',
+ 'labelsListTitle',
+ 'footerCreateLabelTitle',
+ 'footerManageLabelTitle',
+ ]),
+ ...mapGetters(['selectedLabelsList', 'isDropdownVariantSidebar', 'isDropdownVariantEmbedded']),
+ visibleLabels() {
+ if (this.searchKey) {
+ return fuzzaldrinPlus.filter(this.labels, this.searchKey, {
+ key: ['title'],
+ });
+ }
+ return this.labels;
+ },
+ showDropdownFooter() {
+ return (
+ (this.isDropdownVariantSidebar || this.isDropdownVariantEmbedded) &&
+ (this.allowLabelCreate || this.labelsManagePath)
+ );
+ },
+ showNoMatchingResultsMessage() {
+ return Boolean(this.searchKey) && this.visibleLabels.length === 0;
+ },
+ },
+ watch: {
+ searchKey(value) {
+ // When there is search string present
+ // and there are matching results,
+ // highlight first item by default.
+ if (value && this.visibleLabels.length) {
+ this.currentHighlightItem = 0;
+ }
+ },
+ },
+ methods: {
+ ...mapActions([
+ 'toggleDropdownContents',
+ 'toggleDropdownContentsCreateView',
+ 'fetchLabels',
+ 'receiveLabelsSuccess',
+ 'updateSelectedLabels',
+ 'toggleDropdownContents',
+ ]),
+ isLabelSelected(label) {
+ return this.selectedLabelsList.includes(label.id);
+ },
+ /**
+ * This method scrolls item from dropdown into
+ * the view if it is off the viewable area of the
+ * container.
+ */
+ scrollIntoViewIfNeeded() {
+ const highlightedLabel = this.$refs.labelsListContainer.querySelector('.is-focused');
+
+ if (highlightedLabel) {
+ const container = this.$refs.labelsListContainer.getBoundingClientRect();
+ const label = highlightedLabel.getBoundingClientRect();
+
+ if (label.bottom > container.bottom) {
+ this.$refs.labelsListContainer.scrollTop += label.bottom - container.bottom;
+ } else if (label.top < container.top) {
+ this.$refs.labelsListContainer.scrollTop -= container.top - label.top;
+ }
+ }
+ },
+ handleComponentAppear() {
+ // We can avoid putting `catch` block here
+ // as failure is handled within actions.js already.
+ return this.fetchLabels().then(() => {
+ this.$refs.searchInput.focusInput();
+ });
+ },
+ /**
+ * We want to remove loaded labels to ensure component
+ * fetches fresh set of labels every time when shown.
+ */
+ handleComponentDisappear() {
+ this.receiveLabelsSuccess([]);
+ },
+ handleCreateLabelClick() {
+ this.receiveLabelsSuccess([]);
+ this.toggleDropdownContentsCreateView();
+ },
+ /**
+ * This method enables keyboard navigation support for
+ * the dropdown.
+ */
+ handleKeyDown(e) {
+ if (e.keyCode === UP_KEY_CODE && this.currentHighlightItem > 0) {
+ this.currentHighlightItem -= 1;
+ } else if (
+ e.keyCode === DOWN_KEY_CODE &&
+ this.currentHighlightItem < this.visibleLabels.length - 1
+ ) {
+ this.currentHighlightItem += 1;
+ } else if (e.keyCode === ENTER_KEY_CODE && this.currentHighlightItem > -1) {
+ this.updateSelectedLabels([this.visibleLabels[this.currentHighlightItem]]);
+ this.searchKey = '';
+
+ // Prevent parent form submission upon hitting enter.
+ e.preventDefault();
+ } else if (e.keyCode === ESC_KEY_CODE) {
+ this.toggleDropdownContents();
+ }
+
+ if (e.keyCode !== ESC_KEY_CODE) {
+ // Scroll the list only after highlighting
+ // styles are rendered completely.
+ this.$nextTick(() => {
+ this.scrollIntoViewIfNeeded();
+ });
+ }
+ },
+ handleLabelClick(label) {
+ this.updateSelectedLabels([label]);
+ if (!this.allowMultiselect) this.toggleDropdownContents();
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-intersection-observer @appear="handleComponentAppear" @disappear="handleComponentDisappear">
+ <div class="labels-select-contents-list js-labels-list" @keydown="handleKeyDown">
+ <div
+ v-if="isDropdownVariantSidebar || isDropdownVariantEmbedded"
+ class="dropdown-title gl-display-flex gl-align-items-center gl-pt-0 gl-pb-3!"
+ data-testid="dropdown-title"
+ >
+ <span class="flex-grow-1">{{ labelsListTitle }}</span>
+ <gl-button
+ :aria-label="__('Close')"
+ category="tertiary"
+ size="small"
+ class="dropdown-header-button gl-p-0!"
+ icon="close"
+ @click="toggleDropdownContents"
+ />
+ </div>
+ <div class="dropdown-input" @click.stop="() => {}">
+ <gl-search-box-by-type
+ ref="searchInput"
+ v-model="searchKey"
+ :disabled="labelsFetchInProgress"
+ data-qa-selector="dropdown_input_field"
+ />
+ </div>
+ <div ref="labelsListContainer" class="dropdown-content" data-testid="dropdown-content">
+ <gl-loading-icon
+ v-if="labelsFetchInProgress"
+ class="labels-fetch-loading gl-align-items-center w-100 h-100"
+ size="lg"
+ />
+ <ul v-else class="list-unstyled gl-mb-0 gl-word-break-word">
+ <label-item
+ v-for="(label, index) in visibleLabels"
+ :key="label.id"
+ :label="label"
+ :is-label-set="label.set"
+ :is-label-indeterminate="label.indeterminate"
+ :highlight="index === currentHighlightItem"
+ @clickLabel="handleLabelClick(label)"
+ />
+ <li v-show="showNoMatchingResultsMessage" class="gl-p-3 gl-text-center">
+ {{ __('No matching results') }}
+ </li>
+ </ul>
+ </div>
+ <div v-if="showDropdownFooter" class="dropdown-footer" data-testid="dropdown-footer">
+ <ul class="list-unstyled">
+ <li v-if="allowLabelCreate">
+ <gl-link
+ class="gl-display-flex w-100 flex-row text-break-word label-item"
+ @click="handleCreateLabelClick"
+ >
+ {{ footerCreateLabelTitle }}
+ </gl-link>
+ </li>
+ <li v-if="labelsManagePath">
+ <gl-link
+ :href="labelsManagePath"
+ class="gl-display-flex flex-row text-break-word label-item"
+ >
+ {{ footerManageLabelTitle }}
+ </gl-link>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </gl-intersection-observer>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_title.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_title.vue
new file mode 100644
index 00000000000..1e9edd222c5
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_title.vue
@@ -0,0 +1,46 @@
+<script>
+import { GlButton, GlLoadingIcon } from '@gitlab/ui';
+import { mapState, mapActions } from 'vuex';
+
+// @deprecated This component should only be used when there is no GraphQL API.
+// In most cases you should use
+// `app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_header.vue` instead.
+export default {
+ components: {
+ GlButton,
+ GlLoadingIcon,
+ },
+ props: {
+ labelsSelectInProgress: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapState(['allowLabelEdit', 'labelsFetchInProgress']),
+ },
+ methods: {
+ ...mapActions(['toggleDropdownContents']),
+ },
+};
+</script>
+
+<template>
+ <div
+ class="hide-collapsed gl-line-height-20 gl-mb-2 gl-text-gray-900 gl-font-weight-bold gl-mb-0"
+ >
+ {{ __('Labels') }}
+ <template v-if="allowLabelEdit">
+ <gl-loading-icon v-show="labelsSelectInProgress" size="sm" inline />
+ <gl-button
+ category="tertiary"
+ size="small"
+ class="float-right js-sidebar-dropdown-toggle gl-mr-n2"
+ data-qa-selector="labels_edit_button"
+ @click="toggleDropdownContents"
+ >
+ {{ __('Edit') }}
+ </gl-button>
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_value.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_value.vue
new file mode 100644
index 00000000000..583f060be8a
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_value.vue
@@ -0,0 +1,74 @@
+<script>
+import { GlLabel } from '@gitlab/ui';
+import { sortBy } from 'lodash';
+import { mapState } from 'vuex';
+
+import { isScopedLabel } from '~/lib/utils/common_utils';
+
+// @deprecated This component should only be used when there is no GraphQL API.
+// In most cases you should use
+// `app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_value.vue` instead.
+export default {
+ components: {
+ GlLabel,
+ },
+ props: {
+ disableLabels: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ ...mapState([
+ 'selectedLabels',
+ 'allowLabelRemove',
+ 'allowScopedLabels',
+ 'labelsFilterBasePath',
+ 'labelsFilterParam',
+ ]),
+ sortedSelectedLabels() {
+ return sortBy(this.selectedLabels, (label) => (isScopedLabel(label) ? 0 : 1));
+ },
+ },
+ methods: {
+ labelFilterUrl(label) {
+ return `${this.labelsFilterBasePath}?${this.labelsFilterParam}[]=${encodeURIComponent(
+ label.title,
+ )}`;
+ },
+ scopedLabel(label) {
+ return this.allowScopedLabels && isScopedLabel(label);
+ },
+ },
+};
+</script>
+
+<template>
+ <div
+ :class="{
+ 'has-labels': selectedLabels.length,
+ }"
+ class="hide-collapsed value issuable-show-labels js-value"
+ >
+ <span v-if="!selectedLabels.length" class="text-secondary">
+ <slot></slot>
+ </span>
+ <template v-for="label in sortedSelectedLabels" v-else>
+ <gl-label
+ :key="label.id"
+ data-qa-selector="selected_label_content"
+ :data-qa-label-name="label.title"
+ :title="label.title"
+ :description="label.description"
+ :background-color="label.color"
+ :target="labelFilterUrl(label)"
+ :scoped="scopedLabel(label)"
+ :show-close-button="allowLabelRemove"
+ :disabled="disableLabels"
+ tooltip-placement="top"
+ @close="$emit('onLabelRemove', label.id)"
+ />
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_value_collapsed.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_value_collapsed.vue
new file mode 100644
index 00000000000..e84da6ee12b
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_value_collapsed.vue
@@ -0,0 +1,53 @@
+<script>
+import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
+import { s__, sprintf } from '~/locale';
+
+// @deprecated This component should only be used when there is no GraphQL API.
+// In most cases you should use
+// `app/assets/javascripts/sidebar/components/labels/labels_select_widget` instead.
+export default {
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ components: {
+ GlIcon,
+ },
+ props: {
+ labels: {
+ type: Array,
+ required: true,
+ },
+ },
+ computed: {
+ labelsList() {
+ const labelsString = this.labels.length
+ ? this.labels
+ .slice(0, 5)
+ .map((label) => label.title)
+ .join(', ')
+ : s__('LabelSelect|Labels');
+
+ if (this.labels.length > 5) {
+ return sprintf(s__('LabelSelect|%{labelsString}, and %{remainingLabelCount} more'), {
+ labelsString,
+ remainingLabelCount: this.labels.length - 5,
+ });
+ }
+
+ return labelsString;
+ },
+ },
+ methods: {
+ handleClick() {
+ this.$emit('onValueClick');
+ },
+ },
+};
+</script>
+
+<template>
+ <div v-gl-tooltip.left.viewport="labelsList" class="sidebar-collapsed-icon" @click="handleClick">
+ <gl-icon name="labels" />
+ <span>{{ labels.length }}</span>
+ </div>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/labels/labels_select_vue/label_item.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/label_item.vue
new file mode 100644
index 00000000000..135fa9f6228
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/label_item.vue
@@ -0,0 +1,109 @@
+<script>
+import { GlLink, GlIcon } from '@gitlab/ui';
+import { __ } from '~/locale';
+
+// @deprecated This component should only be used when there is no GraphQL API.
+// In most cases you should use
+// `app/assets/javascripts/sidebar/components/labels/labels_select_widget/label_item.vue` instead.
+export default {
+ functional: true,
+ props: {
+ label: {
+ type: Object,
+ required: true,
+ },
+ isLabelSet: {
+ type: Boolean,
+ required: true,
+ },
+ isLabelIndeterminate: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ highlight: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ render(h, { props, listeners }) {
+ const { label, highlight, isLabelSet, isLabelIndeterminate } = props;
+
+ const labelColorBox = h('span', {
+ class: 'dropdown-label-box gl-flex-shrink-0 gl-top-0 gl-mr-3',
+ style: {
+ backgroundColor: label.color,
+ },
+ attrs: {
+ 'data-testid': 'label-color-box',
+ },
+ });
+
+ const checkedIcon = h(GlIcon, {
+ class: {
+ 'gl-mr-3 gl-flex-shrink-0 has-tooltip': true,
+ hidden: !isLabelSet,
+ },
+ attrs: {
+ title: __('Selected for all items.'),
+ 'data-testid': 'checked-icon',
+ },
+ props: {
+ name: 'mobile-issue-close',
+ },
+ });
+
+ const indeterminateIcon = h(GlIcon, {
+ class: {
+ 'gl-mr-3 gl-flex-shrink-0 has-tooltip': true,
+ hidden: !isLabelIndeterminate,
+ },
+ attrs: {
+ title: __('Selected for some items.'),
+ 'data-testid': 'indeterminate-icon',
+ },
+ props: {
+ name: 'dash',
+ },
+ });
+
+ const noIcon = h('span', {
+ class: {
+ 'gl-mr-5 gl-pr-3': true,
+ hidden: isLabelSet || isLabelIndeterminate,
+ },
+ attrs: {
+ 'data-testid': 'no-icon',
+ },
+ });
+
+ const labelTitle = h('span', label.title);
+
+ const labelLink = h(
+ GlLink,
+ {
+ class: 'gl-display-flex gl-align-items-center label-item gl-text-body',
+ on: {
+ click: () => {
+ listeners.clickLabel(label);
+ },
+ },
+ },
+ [noIcon, checkedIcon, indeterminateIcon, labelColorBox, labelTitle],
+ );
+
+ return h(
+ 'li',
+ {
+ class: {
+ 'gl-display-block': true,
+ 'gl-text-left': true,
+ 'is-focused': highlight,
+ },
+ },
+ [labelLink],
+ );
+ },
+};
+</script>
diff --git a/app/assets/javascripts/sidebar/components/labels/labels_select_vue/labels_select_root.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/labels_select_root.vue
new file mode 100644
index 00000000000..2a78db352d7
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/labels_select_root.vue
@@ -0,0 +1,345 @@
+<script>
+import $ from 'jquery';
+import Vue from 'vue';
+import Vuex, { mapState, mapActions, mapGetters } from 'vuex';
+import { isInViewport } from '~/lib/utils/common_utils';
+import { __ } from '~/locale';
+
+import { DropdownVariant } from './constants';
+import DropdownButton from './dropdown_button.vue';
+import DropdownContents from './dropdown_contents.vue';
+import DropdownTitle from './dropdown_title.vue';
+import DropdownValue from './dropdown_value.vue';
+import DropdownValueCollapsed from './dropdown_value_collapsed.vue';
+import labelsSelectModule from './store';
+
+Vue.use(Vuex);
+
+// @deprecated This component should only be used when there is no GraphQL API.
+// In most cases you should use
+// `app/assets/javascripts/sidebar/components/labels/labels_select_widget/labels_select_root.vue` instead.
+export default {
+ store: new Vuex.Store(labelsSelectModule()),
+ components: {
+ DropdownTitle,
+ DropdownValue,
+ DropdownButton,
+ DropdownContents,
+ DropdownValueCollapsed,
+ },
+ props: {
+ allowLabelRemove: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ allowLabelEdit: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ allowLabelCreate: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ allowMultiselect: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ allowScopedLabels: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ allowMultipleScopedLabels: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ variant: {
+ type: String,
+ required: false,
+ default: DropdownVariant.Sidebar,
+ },
+ selectedLabels: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ hideCollapsedView: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ labelsSelectInProgress: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ labelsFetchPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ labelsManagePath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ labelsFilterBasePath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ labelsFilterParam: {
+ type: String,
+ required: false,
+ default: 'label_name',
+ },
+ dropdownButtonText: {
+ type: String,
+ required: false,
+ default: __('Label'),
+ },
+ labelsListTitle: {
+ type: String,
+ required: false,
+ default: __('Assign labels'),
+ },
+ labelsCreateTitle: {
+ type: String,
+ required: false,
+ default: __('Create group label'),
+ },
+ footerCreateLabelTitle: {
+ type: String,
+ required: false,
+ default: __('Create group label'),
+ },
+ footerManageLabelTitle: {
+ type: String,
+ required: false,
+ default: __('Manage group labels'),
+ },
+ isEditing: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ contentIsOnViewport: true,
+ };
+ },
+ computed: {
+ ...mapState(['showDropdownButton', 'showDropdownContents']),
+ ...mapGetters([
+ 'isDropdownVariantSidebar',
+ 'isDropdownVariantStandalone',
+ 'isDropdownVariantEmbedded',
+ ]),
+ dropdownButtonVisible() {
+ return this.isDropdownVariantSidebar ? this.showDropdownButton : true;
+ },
+ },
+ watch: {
+ selectedLabels(selectedLabels) {
+ this.setInitialState({
+ selectedLabels,
+ });
+ setTimeout(() => this.updateLabelsSetState(), 100);
+ },
+ showDropdownContents(showDropdownContents) {
+ this.setContentIsOnViewport(showDropdownContents);
+ },
+ isEditing(newVal) {
+ if (newVal) {
+ this.toggleDropdownContents();
+ }
+ },
+ },
+ mounted() {
+ this.setInitialState({
+ variant: this.variant,
+ allowLabelRemove: this.allowLabelRemove,
+ allowLabelEdit: this.allowLabelEdit,
+ allowLabelCreate: this.allowLabelCreate,
+ allowMultiselect: this.allowMultiselect,
+ allowScopedLabels: this.allowScopedLabels,
+ allowMultipleScopedLabels: this.allowMultipleScopedLabels,
+ dropdownButtonText: this.dropdownButtonText,
+ selectedLabels: this.selectedLabels,
+ labelsFetchPath: this.labelsFetchPath,
+ labelsManagePath: this.labelsManagePath,
+ labelsFilterBasePath: this.labelsFilterBasePath,
+ labelsFilterParam: this.labelsFilterParam,
+ labelsListTitle: this.labelsListTitle,
+ labelsCreateTitle: this.labelsCreateTitle,
+ footerCreateLabelTitle: this.footerCreateLabelTitle,
+ footerManageLabelTitle: this.footerManageLabelTitle,
+ });
+
+ this.$store.subscribeAction({
+ after: this.handleVuexActionDispatch,
+ });
+
+ document.addEventListener('mousedown', this.handleDocumentMousedown);
+ document.addEventListener('click', this.handleDocumentClick);
+
+ this.updateLabelsSetState();
+ },
+ beforeDestroy() {
+ document.removeEventListener('mousedown', this.handleDocumentMousedown);
+ document.removeEventListener('click', this.handleDocumentClick);
+ },
+ methods: {
+ ...mapActions(['setInitialState', 'toggleDropdownContents', 'updateLabelsSetState']),
+ /**
+ * This method differentiates between
+ * dispatched actions and calls necessary method.
+ */
+ handleVuexActionDispatch(action, state) {
+ if (
+ action.type === 'toggleDropdownContents' &&
+ !state.showDropdownButton &&
+ !state.showDropdownContents
+ ) {
+ const filterTouchedLabelsFn = (label) => label.touched;
+ const filterSetLabelsFn = (label) => label.set;
+ const labels = this.isDropdownVariantEmbedded
+ ? state.labels.filter(filterSetLabelsFn)
+ : state.labels.filter(filterTouchedLabelsFn);
+ this.handleDropdownClose(labels, state.labels.filter(filterTouchedLabelsFn));
+ }
+ },
+ /**
+ * This method stores a mousedown event's target.
+ * Required by the click listener because the click
+ * event itself has no reference to this element.
+ */
+ handleDocumentMousedown({ target }) {
+ this.mousedownTarget = target;
+ },
+ /**
+ * This method listens for document-wide click event
+ * and toggle dropdown if user clicks anywhere outside
+ * the dropdown while dropdown is visible.
+ */
+ handleDocumentClick({ target }) {
+ // We also perform the toggle exception check for the
+ // last mousedown event's target to avoid hiding the
+ // box when the mousedown happened inside the box and
+ // only the mouseup did not.
+ if (
+ this.showDropdownContents &&
+ !this.preventDropdownToggleOnClick(target) &&
+ !this.preventDropdownToggleOnClick(this.mousedownTarget)
+ ) {
+ this.toggleDropdownContents();
+ }
+ },
+ /**
+ * This method checks whether a given click target
+ * should prevent the dropdown from being toggled.
+ */
+ preventDropdownToggleOnClick(target) {
+ // This approach of element detection is needed
+ // as the dropdown wrapper is not using `GlDropdown` as
+ // it will also require us to use `BDropdownForm`
+ // which is yet to be implemented in GitLab UI.
+ const hasExceptionClass = [
+ 'js-dropdown-button',
+ 'js-btn-cancel-create',
+ 'js-sidebar-dropdown-toggle',
+ ].some(
+ (className) =>
+ target?.classList.contains(className) ||
+ target?.parentElement?.classList.contains(className),
+ );
+
+ const hasExceptionParent = ['.js-btn-back', '.js-labels-list'].some(
+ (className) => $(target).parents(className).length,
+ );
+
+ const isInDropdownButtonCollapsed = this.$refs.dropdownButtonCollapsed?.$el.contains(target);
+
+ const isInDropdownContents = this.$refs.dropdownContents?.$el.contains(target);
+
+ return (
+ hasExceptionClass ||
+ hasExceptionParent ||
+ isInDropdownButtonCollapsed ||
+ isInDropdownContents
+ );
+ },
+ handleDropdownClose(labels, touchedLabels) {
+ // Only emit label updates if there are any
+ // labels to update on UI.
+ if (labels.length) this.$emit('updateSelectedLabels', labels);
+ this.$emit('onDropdownClose', touchedLabels);
+ },
+ handleCollapsedValueClick() {
+ this.$emit('toggleCollapse');
+ },
+ setContentIsOnViewport(showDropdownContents) {
+ if (!showDropdownContents) {
+ this.contentIsOnViewport = true;
+
+ return;
+ }
+
+ this.$nextTick(() => {
+ if (this.$refs.dropdownContents) {
+ this.contentIsOnViewport = isInViewport(this.$refs.dropdownContents.$el);
+ }
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <div
+ class="labels-select-wrapper position-relative"
+ :class="{
+ 'is-standalone': isDropdownVariantStandalone,
+ 'is-embedded': isDropdownVariantEmbedded,
+ }"
+ >
+ <template v-if="isDropdownVariantSidebar">
+ <dropdown-value-collapsed
+ v-if="!hideCollapsedView"
+ ref="dropdownButtonCollapsed"
+ :labels="selectedLabels"
+ @onValueClick="handleCollapsedValueClick"
+ />
+ <dropdown-title
+ :allow-label-edit="allowLabelEdit"
+ :labels-select-in-progress="labelsSelectInProgress"
+ />
+ <dropdown-value
+ :disable-labels="labelsSelectInProgress"
+ @onLabelRemove="$emit('onLabelRemove', $event)"
+ >
+ <slot></slot>
+ </dropdown-value>
+ <dropdown-button v-show="dropdownButtonVisible" class="gl-mt-2" />
+ <dropdown-contents
+ v-if="dropdownButtonVisible && showDropdownContents"
+ ref="dropdownContents"
+ :render-on-top="!contentIsOnViewport"
+ />
+ </template>
+ <template v-if="isDropdownVariantStandalone || isDropdownVariantEmbedded">
+ <dropdown-button v-show="dropdownButtonVisible" />
+ <dropdown-contents
+ v-if="dropdownButtonVisible && showDropdownContents"
+ ref="dropdownContents"
+ :render-on-top="!contentIsOnViewport"
+ />
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/actions.js
index 2dab97826b9..2dab97826b9 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/actions.js
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/getters.js b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/getters.js
index ef3eedd9bb2..ef3eedd9bb2 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/getters.js
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/getters.js
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/index.js b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/index.js
index 5f61cb732c8..5f61cb732c8 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/index.js
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/index.js
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutation_types.js b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/mutation_types.js
index f26e36031f4..f26e36031f4 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutation_types.js
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/mutation_types.js
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/mutations.js
index c85d9befcbb..c85d9befcbb 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/mutations.js
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/state.js b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/state.js
index 0185d5f88e1..0185d5f88e1 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/state.js
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_vue/store/state.js
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/constants.js b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/constants.js
index cd671b4d8f5..cd671b4d8f5 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/constants.js
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/constants.js
diff --git a/app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents.vue
new file mode 100644
index 00000000000..83df9056af2
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents.vue
@@ -0,0 +1,244 @@
+<script>
+import { GlButton, GlDropdown, GlDropdownItem, GlLink } from '@gitlab/ui';
+import { debounce } from 'lodash';
+import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
+import { __, s__, sprintf } from '~/locale';
+import DropdownContentsCreateView from './dropdown_contents_create_view.vue';
+import DropdownContentsLabelsView from './dropdown_contents_labels_view.vue';
+import DropdownFooter from './dropdown_footer.vue';
+import DropdownHeader from './dropdown_header.vue';
+import { isDropdownVariantStandalone, isDropdownVariantSidebar } from './utils';
+
+export default {
+ components: {
+ DropdownContentsLabelsView,
+ DropdownContentsCreateView,
+ DropdownHeader,
+ DropdownFooter,
+ GlButton,
+ GlDropdown,
+ GlDropdownItem,
+ GlLink,
+ },
+ props: {
+ labelsCreateTitle: {
+ type: String,
+ required: true,
+ },
+ selectedLabels: {
+ type: Array,
+ required: true,
+ },
+ allowMultiselect: {
+ type: Boolean,
+ required: true,
+ },
+ labelsListTitle: {
+ type: String,
+ required: true,
+ },
+ dropdownButtonText: {
+ type: String,
+ required: true,
+ },
+ footerCreateLabelTitle: {
+ type: String,
+ required: true,
+ },
+ footerManageLabelTitle: {
+ type: String,
+ required: true,
+ },
+ variant: {
+ type: String,
+ required: true,
+ },
+ isVisible: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ fullPath: {
+ type: String,
+ required: true,
+ },
+ workspaceType: {
+ type: String,
+ required: true,
+ },
+ attrWorkspacePath: {
+ type: String,
+ required: true,
+ },
+ labelCreateType: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ showDropdownContentsCreateView: false,
+ localSelectedLabels: [...this.selectedLabels],
+ isDirty: false,
+ searchKey: '',
+ };
+ },
+ computed: {
+ dropdownContentsView() {
+ if (this.showDropdownContentsCreateView) {
+ return 'dropdown-contents-create-view';
+ }
+ return 'dropdown-contents-labels-view';
+ },
+ dropdownTitle() {
+ return this.showDropdownContentsCreateView ? this.labelsCreateTitle : this.labelsListTitle;
+ },
+ buttonText() {
+ if (!this.localSelectedLabels.length) {
+ return this.dropdownButtonText || __('Label');
+ } else if (this.localSelectedLabels.length > 1) {
+ return sprintf(s__('LabelSelect|%{firstLabelName} +%{remainingLabelCount} more'), {
+ firstLabelName: this.localSelectedLabels[0].title,
+ remainingLabelCount: this.localSelectedLabels.length - 1,
+ });
+ }
+ return this.localSelectedLabels[0].title;
+ },
+ showDropdownFooter() {
+ return !this.showDropdownContentsCreateView && !this.isStandalone;
+ },
+ isStandalone() {
+ return isDropdownVariantStandalone(this.variant);
+ },
+ isSidebar() {
+ return isDropdownVariantSidebar(this.variant);
+ },
+ },
+ watch: {
+ localSelectedLabels: {
+ handler() {
+ this.isDirty = true;
+ },
+ deep: true,
+ },
+ isVisible(newVal) {
+ if (newVal) {
+ this.$refs.dropdown.show();
+ this.isDirty = false;
+ this.localSelectedLabels = this.selectedLabels;
+ } else {
+ this.$refs.dropdown.hide();
+ this.setLabels();
+ }
+ },
+ selectedLabels(newVal) {
+ if (!this.isDirty || !this.isSidebar) {
+ this.localSelectedLabels = newVal;
+ }
+ },
+ },
+ created() {
+ this.debouncedSearchKeyUpdate = debounce(this.setSearchKey, DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
+ },
+ beforeDestroy() {
+ this.debouncedSearchKeyUpdate.cancel();
+ },
+ methods: {
+ toggleDropdownContentsCreateView() {
+ this.showDropdownContentsCreateView = !this.showDropdownContentsCreateView;
+ },
+ toggleDropdownContent() {
+ this.toggleDropdownContentsCreateView();
+ // Required to recalculate dropdown position as its size changes
+ if (this.$refs.dropdown?.$refs.dropdown) {
+ this.$refs.dropdown.$refs.dropdown.$_popper.scheduleUpdate();
+ }
+ },
+ setLabels() {
+ if (!this.isDirty) {
+ return;
+ }
+ this.$emit('setLabels', this.localSelectedLabels);
+ },
+ handleDropdownHide() {
+ this.$emit('closeDropdown');
+ if (!this.isSidebar) {
+ this.setLabels();
+ }
+ },
+ setSearchKey(value) {
+ this.searchKey = value;
+ },
+ setFocus() {
+ this.$refs.header.focusInput();
+ },
+ hideDropdown() {
+ this.$refs.dropdown.hide();
+ },
+ showDropdown() {
+ this.$refs.dropdown.show();
+ },
+ clearSearch() {
+ if (!this.allowMultiselect || this.isStandalone) {
+ return;
+ }
+ this.searchKey = '';
+ this.setFocus();
+ },
+ selectFirstItem() {
+ this.$refs.dropdownContentsView.selectFirstItem();
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-dropdown
+ ref="dropdown"
+ :text="buttonText"
+ class="gl-w-full"
+ block
+ data-testid="labels-select-dropdown-contents"
+ data-qa-selector="labels_dropdown_content"
+ @hide="handleDropdownHide"
+ @shown="setFocus"
+ >
+ <template #header>
+ <dropdown-header
+ ref="header"
+ :search-key="searchKey"
+ :labels-create-title="labelsCreateTitle"
+ :labels-list-title="labelsListTitle"
+ :show-dropdown-contents-create-view="showDropdownContentsCreateView"
+ :is-standalone="isStandalone"
+ @toggleDropdownContentsCreateView="toggleDropdownContent"
+ @closeDropdown="hideDropdown"
+ @input="debouncedSearchKeyUpdate"
+ @searchEnter="selectFirstItem"
+ />
+ </template>
+ <template #default>
+ <component
+ :is="dropdownContentsView"
+ ref="dropdownContentsView"
+ v-model="localSelectedLabels"
+ :search-key="searchKey"
+ :allow-multiselect="allowMultiselect"
+ :full-path="fullPath"
+ :workspace-type="workspaceType"
+ :attr-workspace-path="attrWorkspacePath"
+ :label-create-type="labelCreateType"
+ @hideCreateView="toggleDropdownContent"
+ @input="clearSearch"
+ />
+ </template>
+ <template #footer>
+ <dropdown-footer
+ v-if="showDropdownFooter"
+ :footer-create-label-title="footerCreateLabelTitle"
+ :footer-manage-label-title="footerManageLabelTitle"
+ @toggleDropdownContentsCreateView="toggleDropdownContent"
+ />
+ </template>
+ </gl-dropdown>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view.vue
new file mode 100644
index 00000000000..aa1184ed314
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view.vue
@@ -0,0 +1,200 @@
+<script>
+import {
+ GlAlert,
+ GlTooltipDirective,
+ GlButton,
+ GlFormInput,
+ GlLink,
+ GlLoadingIcon,
+} from '@gitlab/ui';
+import produce from 'immer';
+import { createAlert } from '~/flash';
+import { __ } from '~/locale';
+import { workspaceLabelsQueries } from '../../../constants';
+import createLabelMutation from './graphql/create_label.mutation.graphql';
+import { LabelType } from './constants';
+
+const errorMessage = __('Error creating label.');
+
+export default {
+ components: {
+ GlAlert,
+ GlButton,
+ GlFormInput,
+ GlLink,
+ GlLoadingIcon,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ fullPath: {
+ type: String,
+ required: true,
+ },
+ attrWorkspacePath: {
+ type: String,
+ required: true,
+ },
+ labelCreateType: {
+ type: String,
+ required: true,
+ },
+ workspaceType: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ labelTitle: '',
+ selectedColor: '',
+ labelCreateInProgress: false,
+ error: undefined,
+ };
+ },
+ computed: {
+ disableCreate() {
+ return !this.labelTitle.length || !this.selectedColor.length || this.labelCreateInProgress;
+ },
+ suggestedColors() {
+ const colorsMap = gon.suggested_label_colors;
+ return Object.keys(colorsMap).map((color) => ({ [color]: colorsMap[color] }));
+ },
+ mutationVariables() {
+ const attributePath = this.labelCreateType === LabelType.group ? 'groupPath' : 'projectPath';
+
+ return {
+ title: this.labelTitle,
+ color: this.selectedColor,
+ [attributePath]: this.attrWorkspacePath,
+ };
+ },
+ },
+ methods: {
+ getColorCode(color) {
+ return Object.keys(color).pop();
+ },
+ getColorName(color) {
+ return Object.values(color).pop();
+ },
+ handleColorClick(color) {
+ this.selectedColor = this.getColorCode(color);
+ },
+ updateLabelsInCache(store, label) {
+ const { query } = workspaceLabelsQueries[this.workspaceType];
+
+ const sourceData = store.readQuery({
+ query,
+ variables: { fullPath: this.fullPath, searchTerm: '' },
+ });
+
+ const collator = new Intl.Collator('en');
+ const data = produce(sourceData, (draftData) => {
+ const { nodes } = draftData.workspace.labels;
+ nodes.push(label);
+ nodes.sort((a, b) => collator.compare(a.title, b.title));
+ });
+
+ store.writeQuery({
+ query,
+ variables: { fullPath: this.fullPath, searchTerm: '' },
+ data,
+ });
+ },
+ async createLabel() {
+ this.labelCreateInProgress = true;
+ try {
+ const {
+ data: { labelCreate },
+ } = await this.$apollo.mutate({
+ mutation: createLabelMutation,
+ variables: this.mutationVariables,
+ update: (
+ store,
+ {
+ data: {
+ labelCreate: { label },
+ },
+ },
+ ) => {
+ if (label) {
+ this.updateLabelsInCache(store, label);
+ }
+ },
+ });
+ if (labelCreate.errors.length) {
+ [this.error] = labelCreate.errors;
+ } else {
+ this.$emit('hideCreateView');
+ }
+ } catch {
+ createAlert({ message: errorMessage });
+ }
+ this.labelCreateInProgress = false;
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="labels-select-contents-create js-labels-create">
+ <div class="dropdown-input">
+ <gl-alert v-if="error" variant="danger" :dismissible="false" class="gl-mt-3">
+ {{ error }}
+ </gl-alert>
+ <gl-form-input
+ v-model.trim="labelTitle"
+ class="gl-mt-3"
+ :placeholder="__('Name new label')"
+ :autofocus="true"
+ data-testid="label-title-input"
+ />
+ </div>
+ <div class="dropdown-content gl-px-3">
+ <div class="suggest-colors suggest-colors-dropdown gl-mt-0! gl-mb-3! gl-mb-0">
+ <gl-link
+ v-for="(color, index) in suggestedColors"
+ :key="index"
+ v-gl-tooltip:tooltipcontainer
+ :style="{ backgroundColor: getColorCode(color) }"
+ :title="getColorName(color)"
+ @click.prevent="handleColorClick(color)"
+ />
+ </div>
+ <div class="color-input-container gl-display-flex">
+ <span
+ class="dropdown-label-color-preview gl-relative gl-display-inline-block"
+ data-testid="selected-color"
+ :style="{ backgroundColor: selectedColor }"
+ ></span>
+ <gl-form-input
+ v-model.trim="selectedColor"
+ class="gl-rounded-top-left-none gl-rounded-bottom-left-none gl-mb-2"
+ :placeholder="__('Use custom color #FF0000')"
+ data-testid="selected-color-text"
+ />
+ </div>
+ </div>
+ <div class="dropdown-actions gl-display-flex gl-justify-content-space-between gl-pt-3 gl-px-3">
+ <gl-button
+ :disabled="disableCreate"
+ category="primary"
+ variant="confirm"
+ class="gl-display-flex gl-align-items-center"
+ data-testid="create-button"
+ @click="createLabel"
+ >
+ <gl-loading-icon v-if="labelCreateInProgress" size="sm" :inline="true" class="mr-1" />
+ {{ __('Create') }}
+ </gl-button>
+ <gl-button
+ class="js-btn-cancel-create"
+ data-testid="cancel-button"
+ @click.stop="$emit('hideCreateView')"
+ >
+ {{ __('Cancel') }}
+ </gl-button>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents_labels_view.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents_labels_view.vue
new file mode 100644
index 00000000000..c1939dc7785
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents_labels_view.vue
@@ -0,0 +1,177 @@
+<script>
+import { GlDropdownForm, GlDropdownItem, GlLoadingIcon, GlIntersectionObserver } from '@gitlab/ui';
+import fuzzaldrinPlus from 'fuzzaldrin-plus';
+import { createAlert } from '~/flash';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { __ } from '~/locale';
+import { workspaceLabelsQueries } from '../../../constants';
+import LabelItem from './label_item.vue';
+
+export default {
+ components: {
+ GlDropdownForm,
+ GlDropdownItem,
+ GlLoadingIcon,
+ GlIntersectionObserver,
+ LabelItem,
+ },
+ model: {
+ prop: 'localSelectedLabels',
+ },
+ props: {
+ allowMultiselect: {
+ type: Boolean,
+ required: true,
+ },
+ localSelectedLabels: {
+ type: Array,
+ required: true,
+ },
+ fullPath: {
+ type: String,
+ required: true,
+ },
+ searchKey: {
+ type: String,
+ required: true,
+ },
+ workspaceType: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ labels: [],
+ isVisible: false,
+ };
+ },
+ apollo: {
+ labels: {
+ query() {
+ return workspaceLabelsQueries[this.workspaceType].query;
+ },
+ variables() {
+ return {
+ fullPath: this.fullPath,
+ searchTerm: this.searchKey,
+ };
+ },
+ skip() {
+ return this.searchKey.length === 1 || !this.isVisible;
+ },
+ update: (data) => data.workspace?.labels?.nodes || [],
+ error() {
+ createAlert({ message: __('Error fetching labels.') });
+ },
+ },
+ },
+ computed: {
+ labelsFetchInProgress() {
+ return this.$apollo.queries.labels.loading;
+ },
+ localSelectedLabelsIds() {
+ return this.localSelectedLabels.map((label) => getIdFromGraphQLId(label.id));
+ },
+ visibleLabels() {
+ if (this.searchKey) {
+ return fuzzaldrinPlus.filter(this.labels, this.searchKey, {
+ key: ['title'],
+ });
+ }
+ return this.labels;
+ },
+ showNoMatchingResultsMessage() {
+ return Boolean(this.searchKey) && this.visibleLabels.length === 0;
+ },
+ shouldHighlightFirstItem() {
+ return this.searchKey !== '' && this.visibleLabels.length > 0;
+ },
+ },
+ methods: {
+ isLabelSelected(label) {
+ return this.localSelectedLabelsIds.includes(getIdFromGraphQLId(label.id));
+ },
+ /**
+ * This method scrolls item from dropdown into
+ * the view if it is off the viewable area of the
+ * container.
+ */
+ scrollIntoViewIfNeeded() {
+ const highlightedLabel = this.$refs.labelsListContainer.querySelector('.is-focused');
+
+ if (highlightedLabel) {
+ const container = this.$refs.labelsListContainer.getBoundingClientRect();
+ const label = highlightedLabel.getBoundingClientRect();
+
+ if (label.bottom > container.bottom) {
+ this.$refs.labelsListContainer.scrollTop += label.bottom - container.bottom;
+ } else if (label.top < container.top) {
+ this.$refs.labelsListContainer.scrollTop -= container.top - label.top;
+ }
+ }
+ },
+ updateSelectedLabels(label) {
+ let labels;
+ if (this.isLabelSelected(label)) {
+ labels = this.localSelectedLabels.filter(
+ ({ id }) => id !== getIdFromGraphQLId(label.id) && id !== label.id,
+ );
+ } else {
+ labels = [...this.localSelectedLabels, label];
+ }
+ this.$emit('input', labels);
+ },
+ handleLabelClick(label) {
+ this.updateSelectedLabels(label);
+ if (!this.allowMultiselect) {
+ this.$emit('closeDropdown', this.localSelectedLabels);
+ }
+ },
+ onDropdownAppear() {
+ this.isVisible = true;
+ },
+ selectFirstItem() {
+ if (this.shouldHighlightFirstItem) {
+ this.handleLabelClick(this.visibleLabels[0]);
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-intersection-observer @appear="onDropdownAppear">
+ <gl-dropdown-form class="labels-select-contents-list js-labels-list">
+ <div ref="labelsListContainer" data-testid="dropdown-content">
+ <gl-loading-icon
+ v-if="labelsFetchInProgress"
+ class="labels-fetch-loading gl-align-items-center gl-w-full gl-h-full gl-mb-3"
+ size="lg"
+ />
+ <template v-else>
+ <gl-dropdown-item
+ v-for="(label, index) in visibleLabels"
+ :key="label.id"
+ :is-checked="isLabelSelected(label)"
+ is-check-centered
+ is-check-item
+ :active="shouldHighlightFirstItem && index === 0"
+ active-class="is-focused"
+ data-testid="labels-list"
+ @click.native.capture.stop="handleLabelClick(label)"
+ >
+ <label-item :label="label" />
+ </gl-dropdown-item>
+ <gl-dropdown-item
+ v-show="showNoMatchingResultsMessage"
+ class="gl-p-3 gl-text-center"
+ data-testid="no-results"
+ >
+ {{ __('No matching results') }}
+ </gl-dropdown-item>
+ </template>
+ </div>
+ </gl-dropdown-form>
+ </gl-intersection-observer>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_footer.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_footer.vue
index e67e704ffb8..e67e704ffb8 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_footer.vue
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_footer.vue
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_header.vue
index 154a8e866d0..154a8e866d0 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_header.vue
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_value.vue
index 57e3ee4aaa5..57e3ee4aaa5 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_value.vue
diff --git a/app/assets/javascripts/sidebar/components/labels/labels_select_widget/embedded_labels_list.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/embedded_labels_list.vue
new file mode 100644
index 00000000000..3a93fc7f3b2
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/embedded_labels_list.vue
@@ -0,0 +1,73 @@
+<script>
+import { GlLabel } from '@gitlab/ui';
+import { sortBy } from 'lodash';
+import { isScopedLabel } from '~/lib/utils/common_utils';
+
+export default {
+ components: {
+ GlLabel,
+ },
+ inject: ['allowScopedLabels'],
+ props: {
+ disabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ selectedLabels: {
+ type: Array,
+ required: true,
+ },
+ allowLabelRemove: {
+ type: Boolean,
+ required: true,
+ },
+ labelsFilterBasePath: {
+ type: String,
+ required: true,
+ },
+ labelsFilterParam: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ sortedSelectedLabels() {
+ return sortBy(this.selectedLabels, (label) => isScopedLabel(label));
+ },
+ },
+ methods: {
+ buildFilterUrl({ title }) {
+ const { labelsFilterBasePath: basePath, labelsFilterParam: filterParam } = this;
+
+ return `${basePath}?${filterParam}[]=${encodeURIComponent(title)}`;
+ },
+ showScopedLabel(label) {
+ return this.allowScopedLabels && isScopedLabel(label);
+ },
+ removeLabel(labelId) {
+ this.$emit('onLabelRemove', labelId);
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-label
+ v-for="label in sortedSelectedLabels"
+ :key="label.id"
+ class="gl-mr-2 gl-mb-2"
+ :data-qa-label-name="label.title"
+ :title="label.title"
+ :description="label.description"
+ :background-color="label.color"
+ :target="buildFilterUrl(label)"
+ :scoped="showScopedLabel(label)"
+ :show-close-button="allowLabelRemove"
+ :disabled="disabled"
+ tooltip-placement="top"
+ @close="removeLabel(label.id)"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/create_label.mutation.graphql b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/create_label.mutation.graphql
index a9c791091fc..a9c791091fc 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/create_label.mutation.graphql
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/create_label.mutation.graphql
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_labels.query.graphql b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/epic_labels.query.graphql
index c442c17eb88..c442c17eb88 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_labels.query.graphql
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/epic_labels.query.graphql
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_update_labels.mutation.graphql b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/epic_update_labels.mutation.graphql
index cb054e2968f..cb054e2968f 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_update_labels.mutation.graphql
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/epic_update_labels.mutation.graphql
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/group_labels.query.graphql
index ce1a69f84c0..ce1a69f84c0 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/group_labels.query.graphql
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/issue_labels.query.graphql
index 2904857270e..2904857270e 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/issue_labels.query.graphql
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/merge_request_labels.query.graphql b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/merge_request_labels.query.graphql
index e0cdfd91658..e0cdfd91658 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/merge_request_labels.query.graphql
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/merge_request_labels.query.graphql
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/project_labels.query.graphql
index a7c24620aad..a7c24620aad 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/graphql/project_labels.query.graphql
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/label_item.vue
index 314ffbaf84c..314ffbaf84c 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/label_item.vue
diff --git a/app/assets/javascripts/sidebar/components/labels/labels_select_widget/labels_select_root.vue b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/labels_select_root.vue
new file mode 100644
index 00000000000..b7b4bbac661
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/labels_select_root.vue
@@ -0,0 +1,441 @@
+<script>
+import { debounce } from 'lodash';
+import issuableLabelsSubscription from 'ee_else_ce/sidebar/queries/issuable_labels.subscription.graphql';
+import { MutationOperationMode, getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { createAlert } from '~/flash';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import { IssuableType } from '~/issues/constants';
+
+import { __ } from '~/locale';
+import { issuableLabelsQueries } from '../../../constants';
+import SidebarEditableItem from '../../sidebar_editable_item.vue';
+import { DEBOUNCE_DROPDOWN_DELAY, DropdownVariant } from './constants';
+import DropdownContents from './dropdown_contents.vue';
+import DropdownValue from './dropdown_value.vue';
+import EmbeddedLabelsList from './embedded_labels_list.vue';
+import {
+ isDropdownVariantSidebar,
+ isDropdownVariantStandalone,
+ isDropdownVariantEmbedded,
+} from './utils';
+
+export default {
+ components: {
+ DropdownValue,
+ DropdownContents,
+ EmbeddedLabelsList,
+ SidebarEditableItem,
+ },
+ mixins: [glFeatureFlagsMixin()],
+ inject: {
+ allowLabelEdit: {
+ default: false,
+ },
+ },
+ props: {
+ iid: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ fullPath: {
+ type: String,
+ required: true,
+ },
+ allowLabelRemove: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ allowMultiselect: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ showEmbeddedLabelsList: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ variant: {
+ type: String,
+ required: false,
+ default: DropdownVariant.Sidebar,
+ },
+ labelsFilterBasePath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ labelsFilterParam: {
+ type: String,
+ required: false,
+ default: 'label_name',
+ },
+ dropdownButtonText: {
+ type: String,
+ required: false,
+ default: __('Label'),
+ },
+ labelsListTitle: {
+ type: String,
+ required: false,
+ default: __('Assign labels'),
+ },
+ labelsCreateTitle: {
+ type: String,
+ required: false,
+ default: __('Create group label'),
+ },
+ footerCreateLabelTitle: {
+ type: String,
+ required: false,
+ default: __('Create group label'),
+ },
+ footerManageLabelTitle: {
+ type: String,
+ required: false,
+ default: __('Manage group labels'),
+ },
+ issuableType: {
+ type: String,
+ required: true,
+ },
+ workspaceType: {
+ type: String,
+ required: true,
+ },
+ attrWorkspacePath: {
+ type: String,
+ required: true,
+ },
+ labelCreateType: {
+ type: String,
+ required: true,
+ },
+ selectedLabels: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ },
+ data() {
+ return {
+ contentIsOnViewport: true,
+ issuable: null,
+ labelsSelectInProgress: false,
+ oldIid: null,
+ sidebarExpandedOnClick: false,
+ };
+ },
+ computed: {
+ isLoading() {
+ return this.labelsSelectInProgress || this.$apollo.queries.issuable.loading;
+ },
+ issuableLabelIds() {
+ return this.issuableLabels.map((label) => label.id);
+ },
+ issuableLabels() {
+ if (this.iid !== '') {
+ return this.issuable?.labels.nodes || [];
+ }
+
+ return this.selectedLabels || [];
+ },
+ issuableId() {
+ return this.issuable?.id;
+ },
+ isRealtimeEnabled() {
+ return this.glFeatures.realtimeLabels;
+ },
+ isLabelListEnabled() {
+ return this.showEmbeddedLabelsList && isDropdownVariantEmbedded(this.variant);
+ },
+ },
+ apollo: {
+ issuable: {
+ query() {
+ return issuableLabelsQueries[this.issuableType].issuableQuery;
+ },
+ skip() {
+ return !isDropdownVariantSidebar(this.variant);
+ },
+ variables() {
+ return {
+ iid: this.iid,
+ fullPath: this.fullPath,
+ };
+ },
+ update(data) {
+ return data.workspace?.issuable;
+ },
+ error() {
+ createAlert({ message: __('Error fetching labels.') });
+ },
+ subscribeToMore: {
+ document() {
+ return issuableLabelsSubscription;
+ },
+ variables() {
+ return {
+ issuableId: this.issuableId,
+ };
+ },
+ skip() {
+ return !this.issuableId || !this.isDropdownVariantSidebar;
+ },
+ updateQuery(
+ _,
+ {
+ subscriptionData: {
+ data: { issuableLabelsUpdated },
+ },
+ },
+ ) {
+ if (issuableLabelsUpdated) {
+ const {
+ id,
+ labels: { nodes },
+ } = issuableLabelsUpdated;
+ this.$emit('updateSelectedLabels', { id, labels: nodes });
+ }
+ },
+ },
+ },
+ },
+ watch: {
+ iid(_, oldVal) {
+ this.oldIid = oldVal;
+ },
+ },
+ mounted() {
+ document.addEventListener('toggleSidebarRevealLabelsDropdown', this.handleCollapsedValueClick);
+ },
+ beforeDestroy() {
+ document.removeEventListener(
+ 'toggleSidebarRevealLabelsDropdown',
+ this.handleCollapsedValueClick,
+ );
+ },
+ methods: {
+ handleDropdownClose(labels) {
+ if (this.iid !== '') {
+ this.updateSelectedLabels(this.getUpdateVariables(labels));
+ } else {
+ this.$emit('updateSelectedLabels', { labels });
+ }
+
+ this.collapseEditableItem();
+ },
+ collapseEditableItem() {
+ this.$refs.editable?.collapse();
+ if (this.sidebarExpandedOnClick) {
+ this.sidebarExpandedOnClick = false;
+ this.$emit('toggleCollapse');
+ }
+ },
+ handleCollapsedValueClick() {
+ this.sidebarExpandedOnClick = true;
+ this.$emit('toggleCollapse');
+ debounce(() => {
+ this.$refs.editable.toggle();
+ this.$refs.dropdownContents.showDropdown();
+ }, DEBOUNCE_DROPDOWN_DELAY)();
+ },
+ getUpdateVariables(labels) {
+ let labelIds = [];
+
+ labelIds = labels.map(({ id }) => id);
+ const currentIid = this.oldIid || this.iid;
+
+ const updateVariables = {
+ iid: currentIid,
+ projectPath: this.fullPath,
+ labelIds,
+ };
+
+ switch (this.issuableType) {
+ case IssuableType.Issue:
+ return updateVariables;
+ case IssuableType.MergeRequest:
+ return {
+ ...updateVariables,
+ operationMode: MutationOperationMode.Replace,
+ };
+ case IssuableType.Epic:
+ return {
+ iid: currentIid,
+ groupPath: this.fullPath,
+ addLabelIds: labelIds.map((id) => getIdFromGraphQLId(id)),
+ removeLabelIds: this.issuableLabelIds
+ .filter((id) => !labelIds.includes(id))
+ .map((id) => getIdFromGraphQLId(id)),
+ };
+ default:
+ return {};
+ }
+ },
+ updateSelectedLabels(inputVariables) {
+ this.labelsSelectInProgress = true;
+
+ this.$apollo
+ .mutate({
+ mutation: issuableLabelsQueries[this.issuableType].mutation,
+ variables: { input: inputVariables },
+ })
+ .then(({ data }) => {
+ if (data.updateIssuableLabels?.errors?.length) {
+ throw new Error();
+ }
+
+ this.$emit('updateSelectedLabels', {
+ id: data.updateIssuableLabels?.issuable?.id,
+ labels: data.updateIssuableLabels?.issuable?.labels?.nodes,
+ });
+ })
+ .catch((error) =>
+ createAlert({
+ message: __('An error occurred while updating labels.'),
+ captureError: true,
+ error,
+ }),
+ )
+ .finally(() => {
+ this.labelsSelectInProgress = false;
+ });
+ },
+ getRemoveVariables(labelId) {
+ const removeVariables = {
+ iid: this.iid,
+ projectPath: this.fullPath,
+ };
+
+ switch (this.issuableType) {
+ case IssuableType.Issue:
+ return {
+ ...removeVariables,
+ removeLabelIds: [labelId],
+ };
+ case IssuableType.MergeRequest:
+ return {
+ ...removeVariables,
+ labelIds: [labelId],
+ operationMode: MutationOperationMode.Remove,
+ };
+ case IssuableType.Epic:
+ return {
+ iid: this.iid,
+ removeLabelIds: [getIdFromGraphQLId(labelId)],
+ groupPath: this.fullPath,
+ };
+ default:
+ return {};
+ }
+ },
+ handleLabelRemove(labelId) {
+ if (this.iid !== '') {
+ this.updateSelectedLabels(this.getRemoveVariables(labelId));
+ }
+
+ this.$emit('onLabelRemove', labelId);
+ },
+ isDropdownVariantSidebar,
+ isDropdownVariantStandalone,
+ isDropdownVariantEmbedded,
+ },
+};
+</script>
+
+<template>
+ <div
+ class="labels-select-wrapper gl-relative"
+ :class="{
+ 'is-standalone': isDropdownVariantStandalone(variant),
+ 'is-embedded': isDropdownVariantEmbedded(variant),
+ }"
+ data-testid="sidebar-labels"
+ data-qa-selector="labels_block"
+ >
+ <template v-if="isDropdownVariantSidebar(variant)">
+ <sidebar-editable-item
+ ref="editable"
+ :title="__('Labels')"
+ :loading="isLoading"
+ :can-edit="allowLabelEdit"
+ @open="oldIid = null"
+ >
+ <template #collapsed>
+ <dropdown-value
+ :disable-labels="labelsSelectInProgress"
+ :selected-labels="issuableLabels"
+ :allow-label-remove="allowLabelRemove"
+ :labels-filter-base-path="labelsFilterBasePath"
+ :labels-filter-param="labelsFilterParam"
+ @onLabelRemove="handleLabelRemove"
+ @onCollapsedValueClick="handleCollapsedValueClick"
+ >
+ <slot></slot>
+ </dropdown-value>
+ </template>
+ <template #default="{ edit }">
+ <dropdown-value
+ :disable-labels="labelsSelectInProgress"
+ :selected-labels="issuableLabels"
+ :allow-label-remove="allowLabelRemove"
+ :labels-filter-base-path="labelsFilterBasePath"
+ :labels-filter-param="labelsFilterParam"
+ class="gl-mb-2"
+ @onLabelRemove="handleLabelRemove"
+ >
+ <slot></slot>
+ </dropdown-value>
+ <dropdown-contents
+ ref="dropdownContents"
+ :dropdown-button-text="dropdownButtonText"
+ :allow-multiselect="allowMultiselect"
+ :labels-list-title="labelsListTitle"
+ :footer-create-label-title="footerCreateLabelTitle"
+ :footer-manage-label-title="footerManageLabelTitle"
+ :labels-create-title="labelsCreateTitle"
+ :selected-labels="issuableLabels"
+ :variant="variant"
+ :is-visible="edit"
+ :full-path="fullPath"
+ :workspace-type="workspaceType"
+ :attr-workspace-path="attrWorkspacePath"
+ :label-create-type="labelCreateType"
+ @setLabels="handleDropdownClose"
+ @closeDropdown="collapseEditableItem"
+ />
+ </template>
+ </sidebar-editable-item>
+ </template>
+ <template v-else>
+ <dropdown-contents
+ ref="dropdownContents"
+ :allow-multiselect="allowMultiselect"
+ :dropdown-button-text="dropdownButtonText"
+ :labels-list-title="labelsListTitle"
+ :footer-create-label-title="footerCreateLabelTitle"
+ :footer-manage-label-title="footerManageLabelTitle"
+ :labels-create-title="labelsCreateTitle"
+ :selected-labels="issuableLabels"
+ :variant="variant"
+ :full-path="fullPath"
+ :workspace-type="workspaceType"
+ :attr-workspace-path="attrWorkspacePath"
+ :label-create-type="labelCreateType"
+ @setLabels="handleDropdownClose"
+ />
+ <embedded-labels-list
+ v-if="isLabelListEnabled"
+ :disabled="labelsSelectInProgress"
+ :selected-labels="issuableLabels"
+ :allow-label-remove="allowLabelRemove"
+ :labels-filter-base-path="labelsFilterBasePath"
+ :labels-filter-param="labelsFilterParam"
+ @onLabelRemove="handleLabelRemove"
+ />
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/utils.js b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/utils.js
index b5cd946a189..b5cd946a189 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/utils.js
+++ b/app/assets/javascripts/sidebar/components/labels/labels_select_widget/utils.js
diff --git a/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue b/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
index d32d8a7b044..cdce6617591 100644
--- a/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
+++ b/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
@@ -4,8 +4,8 @@ import { mapGetters, mapActions } from 'vuex';
import { __, sprintf } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { createAlert } from '~/flash';
-import eventHub from '~/sidebar/event_hub';
import toast from '~/vue_shared/plugins/global_toast';
+import eventHub from '../../event_hub';
import EditForm from './edit_form.vue';
export default {
@@ -111,9 +111,9 @@ export default {
</script>
<template>
- <li v-if="isMergeRequest" class="gl-new-dropdown-item">
+ <li v-if="isMergeRequest" class="gl-dropdown-item">
<button type="button" class="dropdown-item" @click="toggleLocked">
- <span class="gl-new-dropdown-item-text-wrapper">
+ <span class="gl-dropdown-item-text-wrapper">
<template v-if="isLocked">
{{ __('Unlock merge request') }}
</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue b/app/assets/javascripts/sidebar/components/move/issuable_move_dropdown.vue
index 02323e5a0c6..02323e5a0c6 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue
+++ b/app/assets/javascripts/sidebar/components/move/issuable_move_dropdown.vue
diff --git a/app/assets/javascripts/sidebar/components/move/move_issues_button.vue b/app/assets/javascripts/sidebar/components/move/move_issues_button.vue
new file mode 100644
index 00000000000..ab4ac9500ad
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/move/move_issues_button.vue
@@ -0,0 +1,171 @@
+<script>
+import { GlAlert } from '@gitlab/ui';
+import { createAlert } from '~/flash';
+import { logError } from '~/lib/logger';
+import { s__ } from '~/locale';
+import {
+ WORK_ITEM_TYPE_ENUM_ISSUE,
+ WORK_ITEM_TYPE_ENUM_INCIDENT,
+ WORK_ITEM_TYPE_ENUM_TASK,
+ WORK_ITEM_TYPE_ENUM_TEST_CASE,
+} from '~/work_items/constants';
+import issuableEventHub from '~/issues/list/eventhub';
+import getIssuesQuery from 'ee_else_ce/issues/list/queries/get_issues.query.graphql';
+import getIssuesCountQuery from 'ee_else_ce/issues/list/queries/get_issues_counts.query.graphql';
+import moveIssueMutation from '../../queries/move_issue.mutation.graphql';
+import IssuableMoveDropdown from './issuable_move_dropdown.vue';
+
+export default {
+ name: 'MoveIssuesButton',
+ components: {
+ IssuableMoveDropdown,
+ GlAlert,
+ },
+ props: {
+ projectFullPath: {
+ type: String,
+ required: true,
+ },
+ projectsFetchPath: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ selectedIssuables: [],
+ moveInProgress: false,
+ };
+ },
+ computed: {
+ cannotMoveTasksWarningTitle() {
+ if (this.tasksSelected && this.testCasesSelected) {
+ return s__('Issues|Tasks and test cases can not be moved.');
+ }
+
+ if (this.testCasesSelected) {
+ return s__('Issues|Test cases can not be moved.');
+ }
+
+ return s__('Issues|Tasks can not be moved.');
+ },
+ issuesSelected() {
+ return this.selectedIssuables.some((item) => item.type === WORK_ITEM_TYPE_ENUM_ISSUE);
+ },
+ incidentsSelected() {
+ return this.selectedIssuables.some((item) => item.type === WORK_ITEM_TYPE_ENUM_INCIDENT);
+ },
+ tasksSelected() {
+ return this.selectedIssuables.some((item) => item.type === WORK_ITEM_TYPE_ENUM_TASK);
+ },
+ testCasesSelected() {
+ return this.selectedIssuables.some((item) => item.type === WORK_ITEM_TYPE_ENUM_TEST_CASE);
+ },
+ },
+ mounted() {
+ issuableEventHub.$on('issuables:issuableChecked', this.handleIssuableChecked);
+ },
+ beforeDestroy() {
+ issuableEventHub.$off('issuables:issuableChecked', this.handleIssuableChecked);
+ },
+ methods: {
+ handleIssuableChecked(issuable, value) {
+ if (value) {
+ this.selectedIssuables.push(issuable);
+ } else {
+ const index = this.selectedIssuables.indexOf(issuable);
+ if (index > -1) {
+ this.selectedIssuables.splice(index, 1);
+ }
+ }
+ },
+ moveIssues(targetProject) {
+ const iids = this.selectedIssuables.reduce((result, issueData) => {
+ if (
+ issueData.type === WORK_ITEM_TYPE_ENUM_ISSUE ||
+ issueData.type === WORK_ITEM_TYPE_ENUM_INCIDENT
+ ) {
+ result.push(issueData.iid);
+ }
+ return result;
+ }, []);
+
+ if (iids.length === 0) {
+ return;
+ }
+
+ this.moveInProgress = true;
+ issuableEventHub.$emit('issuables:bulkMoveStarted');
+
+ const promises = iids.map((id) => {
+ return this.moveIssue(id, targetProject);
+ });
+
+ Promise.all(promises)
+ .then((promisesResult) => {
+ let foundError = false;
+
+ for (const promiseResult of promisesResult) {
+ if (promiseResult.data.issueMove?.errors?.length) {
+ foundError = true;
+ logError(
+ `Error moving issue. Error message: ${promiseResult.data.issueMove.errors[0].message}`,
+ );
+ }
+ }
+
+ if (!foundError) {
+ const client = this.$apollo.provider.defaultClient;
+ client.refetchQueries({
+ include: [getIssuesQuery, getIssuesCountQuery],
+ });
+ this.moveInProgress = false;
+ this.selectedIssuables = [];
+ issuableEventHub.$emit('issuables:bulkMoveEnded');
+ } else {
+ throw new Error();
+ }
+ })
+ .catch(() => {
+ this.moveInProgress = false;
+ issuableEventHub.$emit('issuables:bulkMoveEnded');
+
+ createAlert({
+ message: s__(`Issues|There was an error while moving the issues.`),
+ });
+ });
+ },
+ moveIssue(issueIid, targetProject) {
+ return this.$apollo.mutate({
+ mutation: moveIssueMutation,
+ variables: {
+ moveIssueInput: {
+ projectPath: this.projectFullPath,
+ iid: issueIid,
+ targetProjectPath: targetProject.full_path,
+ },
+ },
+ });
+ },
+ },
+ i18n: {
+ dropdownButtonTitle: s__('Issues|Move selected'),
+ },
+};
+</script>
+<template>
+ <div>
+ <issuable-move-dropdown
+ :project-full-path="projectFullPath"
+ :projects-fetch-path="projectsFetchPath"
+ :move-in-progress="moveInProgress"
+ :disabled="!issuesSelected && !incidentsSelected"
+ :dropdown-header-title="$options.i18n.dropdownButtonTitle"
+ :dropdown-button-title="$options.i18n.dropdownButtonTitle"
+ @move-issuable="moveIssues"
+ />
+ <gl-alert v-if="tasksSelected || testCasesSelected" :dismissible="false" variant="warning">
+ {{ cannotMoveTasksWarningTitle }}
+ </gl-alert>
+ </div>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/participants/sidebar_participants_widget.vue b/app/assets/javascripts/sidebar/components/participants/sidebar_participants_widget.vue
index 46a04725a49..b0556e22a8d 100644
--- a/app/assets/javascripts/sidebar/components/participants/sidebar_participants_widget.vue
+++ b/app/assets/javascripts/sidebar/components/participants/sidebar_participants_widget.vue
@@ -1,6 +1,6 @@
<script>
import { __ } from '~/locale';
-import { participantsQueries } from '~/sidebar/constants';
+import { participantsQueries } from '../../constants';
import Participants from './participants.vue';
export default {
diff --git a/app/assets/javascripts/sidebar/components/reference/sidebar_reference_widget.vue b/app/assets/javascripts/sidebar/components/reference/sidebar_reference_widget.vue
deleted file mode 100644
index d07c6e0cbd2..00000000000
--- a/app/assets/javascripts/sidebar/components/reference/sidebar_reference_widget.vue
+++ /dev/null
@@ -1,59 +0,0 @@
-<script>
-import { __ } from '~/locale';
-import { referenceQueries } from '~/sidebar/constants';
-import CopyableField from '~/vue_shared/components/sidebar/copyable_field.vue';
-
-export default {
- components: {
- CopyableField,
- },
- inject: ['fullPath', 'iid'],
- props: {
- issuableType: {
- required: true,
- type: String,
- },
- },
- data() {
- return {
- reference: '',
- };
- },
- apollo: {
- reference: {
- query() {
- return referenceQueries[this.issuableType].query;
- },
- variables() {
- return {
- fullPath: this.fullPath,
- iid: this.iid,
- };
- },
- update(data) {
- return data.workspace?.issuable?.reference || '';
- },
- error(error) {
- this.$emit('fetch-error', {
- message: __('An error occurred while fetching reference'),
- error,
- });
- },
- },
- },
- computed: {
- isLoading() {
- return this.$apollo.queries.reference.loading;
- },
- },
-};
-</script>
-
-<template>
- <copyable-field
- class="sub-block"
- :is-loading="isLoading"
- :name="__('Reference')"
- :value="reference"
- />
-</template>
diff --git a/app/assets/javascripts/sidebar/components/reviewers/reviewers.vue b/app/assets/javascripts/sidebar/components/reviewers/reviewers.vue
index 5e1172ad835..7af8dcb4e3e 100644
--- a/app/assets/javascripts/sidebar/components/reviewers/reviewers.vue
+++ b/app/assets/javascripts/sidebar/components/reviewers/reviewers.vue
@@ -58,11 +58,21 @@ export default {
<collapsed-reviewer-list :users="sortedReviewers" :issuable-type="issuableType" />
<div class="value hide-collapsed">
- <template v-if="hasNoUsers">
- <span class="no-value">
- {{ __('None') }}
- </span>
- </template>
+ <span v-if="hasNoUsers" class="no-value" data-testid="no-value">
+ {{ __('None') }}
+ <template v-if="editable">
+ -
+ <button
+ type="button"
+ class="gl-button btn-link gl-reset-color!"
+ data-testid="assign-yourself"
+ data-qa-selector="assign_yourself_button"
+ @click="assignSelf"
+ >
+ {{ __('assign yourself') }}
+ </button>
+ </template>
+ </span>
<uncollapsed-reviewer-list
v-else
diff --git a/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue b/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue
index 5f1350690eb..faa36f3d8d2 100644
--- a/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue
+++ b/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue
@@ -5,12 +5,12 @@ import Vue from 'vue';
import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
-import eventHub from '~/sidebar/event_hub';
-import Store from '~/sidebar/stores/sidebar_store';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import getMergeRequestReviewersQuery from '~/vue_shared/components/sidebar/queries/get_merge_request_reviewers.query.graphql';
-import mergeRequestReviewersUpdatedSubscription from '~/vue_shared/components/sidebar/queries/merge_request_reviewers.subscription.graphql';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import eventHub from '../../event_hub';
+import getMergeRequestReviewersQuery from '../../queries/get_merge_request_reviewers.query.graphql';
+import mergeRequestReviewersUpdatedSubscription from '../../queries/merge_request_reviewers.subscription.graphql';
+import Store from '../../stores/sidebar_store';
import ReviewerTitle from './reviewer_title.vue';
import Reviewers from './reviewers.vue';
@@ -143,6 +143,13 @@ export default {
eventHub.$off('sidebar.saveReviewers', this.saveReviewers);
},
methods: {
+ reviewBySelf() {
+ // Notify gl dropdown that we are now assigning to current user
+ this.$el.parentElement.dispatchEvent(new Event('assignYourself'));
+
+ this.mediator.addSelfReview();
+ this.saveReviewers();
+ },
saveReviewers() {
this.loading = true;
@@ -181,6 +188,7 @@ export default {
:editable="canUpdate"
:issuable-type="issuableType"
@request-review="requestReview"
+ @assign-self="reviewBySelf"
/>
</div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/severity/constants.js b/app/assets/javascripts/sidebar/components/severity/constants.js
deleted file mode 100644
index 4f58ff38121..00000000000
--- a/app/assets/javascripts/sidebar/components/severity/constants.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import { __, s__ } from '~/locale';
-
-export const INCIDENT_SEVERITY = {
- CRITICAL: {
- value: 'CRITICAL',
- icon: 'critical',
- label: s__('IncidentManagement|Critical - S1'),
- },
- HIGH: {
- value: 'HIGH',
- icon: 'high',
- label: s__('IncidentManagement|High - S2'),
- },
- MEDIUM: {
- value: 'MEDIUM',
- icon: 'medium',
- label: s__('IncidentManagement|Medium - S3'),
- },
- LOW: {
- value: 'LOW',
- icon: 'low',
- label: s__('IncidentManagement|Low - S4'),
- },
- UNKNOWN: {
- value: 'UNKNOWN',
- icon: 'unknown',
- label: s__('IncidentManagement|Unknown'),
- },
-};
-
-export const ISSUABLE_TYPES = {
- INCIDENT: 'incident',
-};
-
-export const I18N = {
- UPDATE_SEVERITY_ERROR: s__('SeverityWidget|There was an error while updating severity.'),
- TRY_AGAIN: __('Please try again'),
- EDIT: __('Edit'),
- SEVERITY: s__('SeverityWidget|Severity'),
- SEVERITY_VALUE: s__('SeverityWidget|Severity: %{severity}'),
-};
diff --git a/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue b/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue
index f02e0c783e1..5b624c17b0c 100644
--- a/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue
+++ b/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue
@@ -8,8 +8,8 @@ import {
GlButton,
} from '@gitlab/ui';
import { createAlert } from '~/flash';
-import { INCIDENT_SEVERITY, ISSUABLE_TYPES, I18N } from './constants';
-import updateIssuableSeverity from './graphql/mutations/update_issuable_severity.mutation.graphql';
+import updateIssuableSeverity from '../../queries/update_issuable_severity.mutation.graphql';
+import { INCIDENT_SEVERITY, ISSUABLE_TYPES, SEVERITY_I18N as I18N } from '../../constants';
import SeverityToken from './severity.vue';
export default {
diff --git a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
index a685929cdea..35667495ace 100644
--- a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
+++ b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
@@ -6,7 +6,6 @@ import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { IssuableType } from '~/issues/constants';
import { timeFor } from '~/lib/utils/datetime_utility';
import { __ } from '~/locale';
-import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import {
dropdowni18nText,
@@ -17,6 +16,7 @@ import {
Tracking,
} from 'ee_else_ce/sidebar/constants';
import SidebarDropdown from './sidebar_dropdown.vue';
+import SidebarEditableItem from './sidebar_editable_item.vue';
export default {
i18n: {
diff --git a/app/assets/javascripts/sidebar/components/status/status_dropdown.vue b/app/assets/javascripts/sidebar/components/status/status_dropdown.vue
new file mode 100644
index 00000000000..7763ec00091
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/status/status_dropdown.vue
@@ -0,0 +1,57 @@
+<script>
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { __ } from '~/locale';
+import { statusDropdownOptions } from '../../constants';
+
+export default {
+ components: {
+ GlDropdown,
+ GlDropdownItem,
+ },
+ data() {
+ return {
+ status: null,
+ };
+ },
+ computed: {
+ dropdownText() {
+ return this.status?.text ?? this.$options.i18n.defaultDropdownText;
+ },
+ selectedValue() {
+ return this.status?.value;
+ },
+ },
+ methods: {
+ onDropdownItemClick(statusOption) {
+ // clear status if the currently checked status is clicked again
+ if (this.status?.value === statusOption.value) {
+ this.status = null;
+ } else {
+ this.status = statusOption;
+ }
+ },
+ },
+ i18n: {
+ dropdownTitle: __('Change status'),
+ defaultDropdownText: __('Select status'),
+ },
+ statusDropdownOptions,
+};
+</script>
+<template>
+ <div>
+ <input type="hidden" name="update[state_event]" :value="selectedValue" />
+ <gl-dropdown :text="dropdownText" :title="$options.i18n.dropdownTitle" class="gl-w-full">
+ <gl-dropdown-item
+ v-for="statusOption in $options.statusDropdownOptions"
+ :key="statusOption.value"
+ :is-checked="selectedValue === statusOption.value"
+ is-check-item
+ :title="statusOption.text"
+ @click="onDropdownItemClick(statusOption)"
+ >
+ {{ statusOption.text }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+ </div>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
index 99e7c825b72..0fba1cb5e4e 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
@@ -4,10 +4,10 @@ import { createAlert } from '~/flash';
import { IssuableType } from '~/issues/constants';
import { isLoggedIn } from '~/lib/utils/common_utils';
import { __, sprintf } from '~/locale';
-import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import toast from '~/vue_shared/plugins/global_toast';
-import { subscribedQueries, Tracking } from '~/sidebar/constants';
+import { subscribedQueries, Tracking } from '../../constants';
+import SidebarEditableItem from '../sidebar_editable_item.vue';
const ICON_ON = 'notifications';
const ICON_OFF = 'notifications-off';
@@ -182,7 +182,7 @@ export default {
</script>
<template>
- <gl-dropdown-form v-if="isMergeRequest" class="gl-new-dropdown-item">
+ <gl-dropdown-form v-if="isMergeRequest" class="gl-dropdown-item">
<div class="gl-px-5 gl-pb-2 gl-pt-1">
<gl-toggle
:value="subscribed"
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue
new file mode 100644
index 00000000000..4c3ba76d12d
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue
@@ -0,0 +1,51 @@
+<script>
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { __ } from '~/locale';
+import { subscriptionsDropdownOptions } from '../../constants';
+
+export default {
+ subscriptionsDropdownOptions,
+ i18n: {
+ defaultDropdownText: __('Select subscription'),
+ headerText: __('Change subscription'),
+ },
+ components: {
+ GlDropdown,
+ GlDropdownItem,
+ },
+ data() {
+ return {
+ subscription: undefined,
+ };
+ },
+ computed: {
+ dropdownText() {
+ return this.subscription?.text ?? this.$options.i18n.defaultDropdownText;
+ },
+ selectedValue() {
+ return this.subscription?.value;
+ },
+ },
+ methods: {
+ handleClick(option) {
+ this.subscription = option.value === this.subscription?.value ? undefined : option;
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <input type="hidden" name="update[subscription_event]" :value="selectedValue" />
+ <gl-dropdown class="gl-w-full" :header-text="$options.i18n.headerText" :text="dropdownText">
+ <gl-dropdown-item
+ v-for="subscriptionsOption in $options.subscriptionsDropdownOptions"
+ :key="subscriptionsOption.value"
+ is-check-item
+ :is-checked="selectedValue === subscriptionsOption.value"
+ @click="handleClick(subscriptionsOption)"
+ >
+ {{ subscriptionsOption.text }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+ </div>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/constants.js b/app/assets/javascripts/sidebar/components/time_tracking/constants.js
new file mode 100644
index 00000000000..56e986e3b27
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/time_tracking/constants.js
@@ -0,0 +1 @@
+export const CREATE_TIMELOG_MODAL_ID = 'create-timelog-modal';
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/create_timelog_form.vue b/app/assets/javascripts/sidebar/components/time_tracking/create_timelog_form.vue
new file mode 100644
index 00000000000..ec8e1ee9952
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/time_tracking/create_timelog_form.vue
@@ -0,0 +1,227 @@
+<script>
+import {
+ GlFormGroup,
+ GlFormInput,
+ GlDatepicker,
+ GlFormTextarea,
+ GlModal,
+ GlAlert,
+ GlLink,
+ GlSprintf,
+} from '@gitlab/ui';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+import { formatDate } from '~/lib/utils/datetime_utility';
+import { TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/graphql_shared/constants';
+import { joinPaths } from '~/lib/utils/url_utility';
+import { s__ } from '~/locale';
+import createTimelogMutation from '../../queries/create_timelog.mutation.graphql';
+import { CREATE_TIMELOG_MODAL_ID } from './constants';
+
+export default {
+ components: {
+ GlDatepicker,
+ GlFormGroup,
+ GlFormInput,
+ GlFormTextarea,
+ GlModal,
+ GlAlert,
+ GlLink,
+ GlSprintf,
+ },
+ inject: ['issuableType'],
+ props: {
+ issuableId: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ timeSpent: '',
+ spentAt: null,
+ summary: '',
+ isLoading: false,
+ saveError: '',
+ };
+ },
+ computed: {
+ submitDisabled() {
+ return this.isLoading || this.timeSpent.length === 0;
+ },
+ primaryProps() {
+ return {
+ text: s__('CreateTimelogForm|Save'),
+ attributes: [
+ {
+ variant: 'confirm',
+ disabled: this.submitDisabled,
+ loading: this.isLoading,
+ },
+ ],
+ };
+ },
+ cancelProps() {
+ return {
+ text: s__('CreateTimelogForm|Cancel'),
+ };
+ },
+ timeTrackingDocsPath() {
+ return joinPaths(gon.relative_url_root || '', '/help/user/project/time_tracking.md');
+ },
+ issuableTypeName() {
+ return this.isIssue()
+ ? s__('CreateTimelogForm|issue')
+ : s__('CreateTimelogForm|merge request');
+ },
+ },
+ methods: {
+ resetModal() {
+ this.isLoading = false;
+ this.timeSpent = '';
+ this.spentAt = null;
+ this.summary = '';
+ this.saveError = '';
+ },
+ close() {
+ this.resetModal();
+ this.$refs.modal.close();
+ },
+ registerTimeSpent(event) {
+ event.preventDefault();
+
+ if (this.timeSpent.length === 0) {
+ return;
+ }
+
+ this.isLoading = true;
+ this.saveError = '';
+
+ this.$apollo
+ .mutate({
+ mutation: createTimelogMutation,
+ variables: {
+ input: {
+ timeSpent: this.timeSpent,
+ spentAt: this.spentAt
+ ? formatDate(this.spentAt, 'isoDateTime')
+ : formatDate(Date.now(), 'isoDateTime'),
+ summary: this.summary,
+ issuableId: this.getIssuableId(),
+ },
+ },
+ })
+ .then(({ data }) => {
+ if (data.timelogCreate?.errors.length) {
+ this.saveError = data.timelogCreate.errors[0].message || data.timelogCreate.errors[0];
+ } else {
+ this.close();
+ }
+ })
+ .catch((error) => {
+ this.saveError =
+ error?.message ||
+ s__('CreateTimelogForm|An error occurred while saving the time entry.');
+ })
+ .finally(() => {
+ this.isLoading = false;
+ });
+ },
+ isIssue() {
+ return this.issuableType === 'issue';
+ },
+ getGraphQLEntityType() {
+ return this.isIssue() ? TYPE_ISSUE : TYPE_MERGE_REQUEST;
+ },
+ updateSpentAtDate(val) {
+ this.spentAt = val;
+ },
+ getIssuableId() {
+ return convertToGraphQLId(this.getGraphQLEntityType(), this.issuableId);
+ },
+ },
+ CREATE_TIMELOG_MODAL_ID,
+};
+</script>
+
+<template>
+ <gl-modal
+ ref="modal"
+ :title="s__('CreateTimelogForm|Add time entry')"
+ :modal-id="$options.CREATE_TIMELOG_MODAL_ID"
+ size="sm"
+ data-testid="create-timelog-modal"
+ :action-primary="primaryProps"
+ :action-cancel="cancelProps"
+ @primary="registerTimeSpent"
+ @cancel="close"
+ @close="close"
+ @hide="close"
+ >
+ <p data-testid="timetracking-docs-link">
+ <gl-sprintf
+ :message="
+ s__(
+ 'CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}',
+ )
+ "
+ >
+ <template #issuableTypeName>{{ issuableTypeName }}</template>
+ <template #timeTrackingDocsLink>
+ <gl-link :href="timeTrackingDocsPath" target="_blank">{{
+ s__('CreateTimelogForm|How do I track and estimate time?')
+ }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </p>
+ <form
+ class="gl-display-flex gl-flex-direction-column js-quick-submit"
+ @submit.prevent="registerTimeSpent"
+ >
+ <div class="gl-display-flex gl-gap-3">
+ <gl-form-group
+ key="time-spent"
+ label-for="time-spent"
+ :label="s__(`CreateTimelogForm|Time spent`)"
+ :description="s__(`CreateTimelogForm|Example: 1h 30m`)"
+ >
+ <gl-form-input
+ id="time-spent"
+ ref="timeSpent"
+ v-model="timeSpent"
+ class="gl-form-input-sm"
+ autocomplete="off"
+ />
+ </gl-form-group>
+ <gl-form-group
+ key="spent-at"
+ optional
+ label-for="spent-at"
+ :label="s__(`CreateTimelogForm|Spent at`)"
+ >
+ <gl-datepicker
+ :target="null"
+ :value="spentAt"
+ show-clear-button
+ autocomplete="off"
+ size="small"
+ @input="updateSpentAtDate"
+ @clear="updateSpentAtDate(null)"
+ />
+ </gl-form-group>
+ </div>
+ <gl-form-group
+ :label="s__('CreateTimelogForm|Summary')"
+ optional
+ label-for="summary"
+ class="gl-mb-0"
+ >
+ <gl-form-textarea id="summary" v-model="summary" rows="3" :no-resize="true" />
+ </gl-form-group>
+ <gl-alert v-if="saveError" variant="danger" class="gl-mt-5" :dismissible="false">
+ {{ saveError }}
+ </gl-alert>
+ <!-- This is needed to have the quick-submit behaviour (with Ctrl + Enter or Cmd + Enter) -->
+ <input type="submit" hidden />
+ </form>
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue b/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
index 91c15061fb9..6cd9596e43f 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
@@ -1,5 +1,6 @@
<script>
-import { GlButton, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlButton } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { joinPaths } from '~/lib/utils/url_utility';
import { sprintf, s__ } from '~/locale';
@@ -9,7 +10,7 @@ export default {
GlButton,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
computed: {
href() {
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/report.vue b/app/assets/javascripts/sidebar/components/time_tracking/report.vue
index 124464088cf..6f4ced06ddf 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/report.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/report.vue
@@ -5,8 +5,8 @@ import { TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { formatDate, parseSeconds, stringifyTime } from '~/lib/utils/datetime_utility';
import { __, s__ } from '~/locale';
-import { timelogQueries } from '~/sidebar/constants';
-import deleteTimelogMutation from './graphql/mutations/delete_timelog.mutation.graphql';
+import { timelogQueries } from '../../constants';
+import deleteTimelogMutation from '../../queries/delete_timelog.mutation.graphql';
const TIME_DATE_FORMAT = 'mmmm d, yyyy, HH:MM ("UTC:" o)';
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue
index 62b05421884..06adc048942 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue
@@ -30,6 +30,11 @@ export default {
required: false,
default: false,
},
+ canAddTimeEntries: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
mounted() {
this.listenForQuickActions();
@@ -67,6 +72,7 @@ export default {
:issuable-id="issuableId"
:issuable-iid="issuableIid"
:limit-to-hours="limitToHours"
+ :can-add-time-entries="canAddTimeEntries"
/>
</div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
index 13981c477c6..b32836dc87d 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
@@ -9,15 +9,17 @@ import {
GlTooltipDirective,
} from '@gitlab/ui';
import { IssuableType } from '~/issues/constants';
+import { BV_SHOW_MODAL } from '~/lib/utils/constants';
import { s__, __ } from '~/locale';
-import { HOW_TO_TRACK_TIME, timeTrackingQueries } from '~/sidebar/constants';
+import { HOW_TO_TRACK_TIME, timeTrackingQueries } from '../../constants';
import eventHub from '../../event_hub';
import TimeTrackingCollapsedState from './collapsed_state.vue';
import TimeTrackingComparisonPane from './comparison_pane.vue';
-import TimeTrackingHelpState from './help_state.vue';
import TimeTrackingReport from './report.vue';
import TimeTrackingSpentOnlyPane from './spent_only_pane.vue';
+import { CREATE_TIMELOG_MODAL_ID } from './constants';
+import CreateTimelogForm from './create_timelog_form.vue';
export default {
name: 'IssuableTimeTracker',
@@ -34,8 +36,8 @@ export default {
TimeTrackingCollapsedState,
TimeTrackingSpentOnlyPane,
TimeTrackingComparisonPane,
- TimeTrackingHelpState,
TimeTrackingReport,
+ CreateTimelogForm,
},
directives: {
GlModal: GlModalDirective,
@@ -87,6 +89,11 @@ export default {
default: true,
required: false,
},
+ canAddTimeEntries: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
data() {
return {
@@ -192,12 +199,12 @@ export default {
eventHub.$on('timeTracker:refresh', this.refresh);
},
methods: {
- toggleHelpState(show) {
- this.showHelp = show;
- },
refresh() {
this.$apollo.queries.issuableTimeTracking.refetch();
},
+ openRegisterTimeSpentModal() {
+ this.$root.$emit(BV_SHOW_MODAL, CREATE_TIMELOG_MODAL_ID);
+ },
},
};
</script>
@@ -215,24 +222,21 @@ export default {
:time-estimate-human-readable="humanTimeEstimate"
/>
<div
- class="hide-collapsed gl-line-height-20 gl-text-gray-900 gl-display-flex gl-align-items-center gl-font-weight-bold gl-mr-3"
+ class="hide-collapsed gl-line-height-20 gl-text-gray-900 gl-display-flex gl-align-items-center gl-font-weight-bold"
>
{{ __('Time tracking') }}
<gl-loading-icon v-if="isTimeTrackingInfoLoading" size="sm" class="gl-ml-2" inline />
<gl-button
- :data-testid="showHelpState ? 'closeHelpButton' : 'helpButton'"
+ v-if="canAddTimeEntries"
+ v-gl-tooltip.left
category="tertiary"
size="small"
- variant="link"
class="gl-ml-auto"
- @click="toggleHelpState(!showHelpState)"
+ data-testid="add-time-entry-button"
+ :title="__('Add time entry')"
+ @click="openRegisterTimeSpentModal()"
>
- <gl-icon
- v-gl-tooltip.left
- :title="timeTrackingIconTitle"
- :name="timeTrackingIconName"
- class="gl-text-gray-900!"
- />
+ <gl-icon name="plus" class="gl-text-gray-900!" />
</gl-button>
</div>
<div v-if="!isTimeTrackingInfoLoading" class="hide-collapsed">
@@ -272,9 +276,7 @@ export default {
<time-tracking-report :limit-to-hours="limitToHours" :issuable-id="issuableId" />
</gl-modal>
</template>
- <transition name="help-state-toggle">
- <time-tracking-help-state v-if="showHelpState" />
- </transition>
+ <create-timelog-form :issuable-id="issuableId" />
</div>
</div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue b/app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue
index 5da2d65723a..b86ff279fd8 100644
--- a/app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue
+++ b/app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue
@@ -3,11 +3,11 @@ import { GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { produce } from 'immer';
import { createAlert } from '~/flash';
import { __, sprintf } from '~/locale';
-import { todoQueries, TodoMutationTypes, todoMutations } from '~/sidebar/constants';
-import { todoLabel } from '~/vue_shared/components/sidebar/todo_toggle//utils';
-import TodoButton from '~/vue_shared/components/sidebar/todo_toggle/todo_button.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import Tracking from '~/tracking';
+import { todoQueries, TodoMutationTypes, todoMutations } from '../../constants';
+import { todoLabel } from '../../utils';
+import TodoButton from './todo_button.vue';
const trackingMixin = Tracking.mixin();
diff --git a/app/assets/javascripts/sidebar/components/todo_toggle/todo_button.vue b/app/assets/javascripts/sidebar/components/todo_toggle/todo_button.vue
new file mode 100644
index 00000000000..b49b8fc389b
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/todo_toggle/todo_button.vue
@@ -0,0 +1,44 @@
+<script>
+import { GlButton } from '@gitlab/ui';
+import { todoLabel, updateGlobalTodoCount } from '../../utils';
+
+export default {
+ components: {
+ GlButton,
+ },
+ props: {
+ isTodo: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ },
+ computed: {
+ buttonLabel() {
+ return todoLabel(this.isTodo);
+ },
+ },
+ methods: {
+ incrementGlobalTodoCount() {
+ updateGlobalTodoCount(1);
+ },
+ decrementGlobalTodoCount() {
+ updateGlobalTodoCount(-1);
+ },
+ onToggle(event) {
+ if (this.isTodo) {
+ this.decrementGlobalTodoCount();
+ } else {
+ this.incrementGlobalTodoCount();
+ }
+ this.$emit('click', event);
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-button v-bind="$attrs" :aria-label="buttonLabel" @click="onToggle($event)">
+ {{ buttonLabel }}
+ </gl-button>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue b/app/assets/javascripts/sidebar/components/toggle/toggle_sidebar.vue
index 6dacf4e10d3..6dacf4e10d3 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/toggle/toggle_sidebar.vue
diff --git a/app/assets/javascripts/sidebar/constants.js b/app/assets/javascripts/sidebar/constants.js
index 67b9b540e91..825a89daf58 100644
--- a/app/assets/javascripts/sidebar/constants.js
+++ b/app/assets/javascripts/sidebar/constants.js
@@ -4,55 +4,55 @@ import updateIssueLabelsMutation from '~/boards/graphql/issue_set_labels.mutatio
import userSearchQuery from '~/graphql_shared/queries/users_search.query.graphql';
import userSearchWithMRPermissionsQuery from '~/graphql_shared/queries/users_search_with_mr_permissions.graphql';
import { IssuableType, WorkspaceType } from '~/issues/constants';
-import epicConfidentialQuery from '~/sidebar/queries/epic_confidential.query.graphql';
-import epicDueDateQuery from '~/sidebar/queries/epic_due_date.query.graphql';
-import epicParticipantsQuery from '~/sidebar/queries/epic_participants.query.graphql';
-import epicReferenceQuery from '~/sidebar/queries/epic_reference.query.graphql';
-import epicStartDateQuery from '~/sidebar/queries/epic_start_date.query.graphql';
-import epicSubscribedQuery from '~/sidebar/queries/epic_subscribed.query.graphql';
-import epicTodoQuery from '~/sidebar/queries/epic_todo.query.graphql';
-import issuableAssigneesSubscription from '~/sidebar/queries/issuable_assignees.subscription.graphql';
-import issueConfidentialQuery from '~/sidebar/queries/issue_confidential.query.graphql';
-import issueDueDateQuery from '~/sidebar/queries/issue_due_date.query.graphql';
-import issueReferenceQuery from '~/sidebar/queries/issue_reference.query.graphql';
-import issueSubscribedQuery from '~/sidebar/queries/issue_subscribed.query.graphql';
-import issueTimeTrackingQuery from '~/sidebar/queries/issue_time_tracking.query.graphql';
-import issueTodoQuery from '~/sidebar/queries/issue_todo.query.graphql';
-import mergeRequestMilestone from '~/sidebar/queries/merge_request_milestone.query.graphql';
-import mergeRequestReferenceQuery from '~/sidebar/queries/merge_request_reference.query.graphql';
-import mergeRequestSubscribed from '~/sidebar/queries/merge_request_subscribed.query.graphql';
-import mergeRequestTimeTrackingQuery from '~/sidebar/queries/merge_request_time_tracking.query.graphql';
-import mergeRequestTodoQuery from '~/sidebar/queries/merge_request_todo.query.graphql';
-import todoCreateMutation from '~/sidebar/queries/todo_create.mutation.graphql';
-import todoMarkDoneMutation from '~/sidebar/queries/todo_mark_done.mutation.graphql';
-import updateEpicConfidentialMutation from '~/sidebar/queries/update_epic_confidential.mutation.graphql';
-import updateEpicDueDateMutation from '~/sidebar/queries/update_epic_due_date.mutation.graphql';
-import updateEpicStartDateMutation from '~/sidebar/queries/update_epic_start_date.mutation.graphql';
-import updateEpicSubscriptionMutation from '~/sidebar/queries/update_epic_subscription.mutation.graphql';
-import updateIssueConfidentialMutation from '~/sidebar/queries/update_issue_confidential.mutation.graphql';
-import updateIssueDueDateMutation from '~/sidebar/queries/update_issue_due_date.mutation.graphql';
-import updateIssueSubscriptionMutation from '~/sidebar/queries/update_issue_subscription.mutation.graphql';
-import mergeRequestMilestoneMutation from '~/sidebar/queries/update_merge_request_milestone.mutation.graphql';
-import updateMergeRequestLabelsMutation from '~/sidebar/queries/update_merge_request_labels.mutation.graphql';
-import updateMergeRequestSubscriptionMutation from '~/sidebar/queries/update_merge_request_subscription.mutation.graphql';
import updateAlertAssigneesMutation from '~/vue_shared/alert_details/graphql/mutations/alert_set_assignees.mutation.graphql';
-import epicLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/epic_labels.query.graphql';
-import updateEpicLabelsMutation from '~/vue_shared/components/sidebar/labels_select_widget/graphql/epic_update_labels.mutation.graphql';
-import groupLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql';
-import issueLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql';
-import mergeRequestLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/merge_request_labels.query.graphql';
-import projectLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql';
-import getAlertAssignees from '~/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql';
-import getIssueAssignees from '~/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql';
-import issueParticipantsQuery from '~/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql';
-import getIssueTimelogsQuery from '~/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql';
-import getMergeRequestAssignees from '~/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql';
-import getMergeRequestParticipants from '~/vue_shared/components/sidebar/queries/get_mr_participants.query.graphql';
-import getMrTimelogsQuery from '~/vue_shared/components/sidebar/queries/get_mr_timelogs.query.graphql';
-import updateIssueAssigneesMutation from '~/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql';
-import updateMergeRequestAssigneesMutation from '~/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql';
-import getEscalationStatusQuery from '~/sidebar/queries/escalation_status.query.graphql';
-import updateEscalationStatusMutation from '~/sidebar/queries/update_escalation_status.mutation.graphql';
+import epicLabelsQuery from './components/labels/labels_select_widget/graphql/epic_labels.query.graphql';
+import updateEpicLabelsMutation from './components/labels/labels_select_widget/graphql/epic_update_labels.mutation.graphql';
+import groupLabelsQuery from './components/labels/labels_select_widget/graphql/group_labels.query.graphql';
+import issueLabelsQuery from './components/labels/labels_select_widget/graphql/issue_labels.query.graphql';
+import mergeRequestLabelsQuery from './components/labels/labels_select_widget/graphql/merge_request_labels.query.graphql';
+import projectLabelsQuery from './components/labels/labels_select_widget/graphql/project_labels.query.graphql';
+import epicConfidentialQuery from './queries/epic_confidential.query.graphql';
+import epicDueDateQuery from './queries/epic_due_date.query.graphql';
+import epicParticipantsQuery from './queries/epic_participants.query.graphql';
+import epicReferenceQuery from './queries/epic_reference.query.graphql';
+import epicStartDateQuery from './queries/epic_start_date.query.graphql';
+import epicSubscribedQuery from './queries/epic_subscribed.query.graphql';
+import epicTodoQuery from './queries/epic_todo.query.graphql';
+import issuableAssigneesSubscription from './queries/issuable_assignees.subscription.graphql';
+import issueConfidentialQuery from './queries/issue_confidential.query.graphql';
+import issueDueDateQuery from './queries/issue_due_date.query.graphql';
+import issueReferenceQuery from './queries/issue_reference.query.graphql';
+import issueSubscribedQuery from './queries/issue_subscribed.query.graphql';
+import issueTimeTrackingQuery from './queries/issue_time_tracking.query.graphql';
+import issueTodoQuery from './queries/issue_todo.query.graphql';
+import mergeRequestMilestone from './queries/merge_request_milestone.query.graphql';
+import mergeRequestReferenceQuery from './queries/merge_request_reference.query.graphql';
+import mergeRequestSubscribed from './queries/merge_request_subscribed.query.graphql';
+import mergeRequestTimeTrackingQuery from './queries/merge_request_time_tracking.query.graphql';
+import mergeRequestTodoQuery from './queries/merge_request_todo.query.graphql';
+import todoCreateMutation from './queries/todo_create.mutation.graphql';
+import todoMarkDoneMutation from './queries/todo_mark_done.mutation.graphql';
+import updateEpicConfidentialMutation from './queries/update_epic_confidential.mutation.graphql';
+import updateEpicDueDateMutation from './queries/update_epic_due_date.mutation.graphql';
+import updateEpicStartDateMutation from './queries/update_epic_start_date.mutation.graphql';
+import updateEpicSubscriptionMutation from './queries/update_epic_subscription.mutation.graphql';
+import updateIssueConfidentialMutation from './queries/update_issue_confidential.mutation.graphql';
+import updateIssueDueDateMutation from './queries/update_issue_due_date.mutation.graphql';
+import updateIssueSubscriptionMutation from './queries/update_issue_subscription.mutation.graphql';
+import mergeRequestMilestoneMutation from './queries/update_merge_request_milestone.mutation.graphql';
+import updateMergeRequestLabelsMutation from './queries/update_merge_request_labels.mutation.graphql';
+import updateMergeRequestSubscriptionMutation from './queries/update_merge_request_subscription.mutation.graphql';
+import getAlertAssignees from './queries/get_alert_assignees.query.graphql';
+import getIssueAssignees from './queries/get_issue_assignees.query.graphql';
+import issueParticipantsQuery from './queries/get_issue_participants.query.graphql';
+import getIssueTimelogsQuery from './queries/get_issue_timelogs.query.graphql';
+import getMergeRequestAssignees from './queries/get_mr_assignees.query.graphql';
+import getMergeRequestParticipants from './queries/get_mr_participants.query.graphql';
+import getMrTimelogsQuery from './queries/get_mr_timelogs.query.graphql';
+import updateIssueAssigneesMutation from './queries/update_issue_assignees.mutation.graphql';
+import updateMergeRequestAssigneesMutation from './queries/update_mr_assignees.mutation.graphql';
+import getEscalationStatusQuery from './queries/escalation_status.query.graphql';
+import updateEscalationStatusMutation from './queries/update_escalation_status.mutation.graphql';
import groupMilestonesQuery from './queries/group_milestones.query.graphql';
import projectIssueMilestoneMutation from './queries/project_issue_milestone.mutation.graphql';
import projectIssueMilestoneQuery from './queries/project_issue_milestone.query.graphql';
@@ -350,3 +350,94 @@ export const escalationStatusQuery = getEscalationStatusQuery;
export const escalationStatusMutation = updateEscalationStatusMutation;
export const HOW_TO_TRACK_TIME = __('How to track time');
+
+export const statusDropdownOptions = [
+ {
+ text: __('Open'),
+ value: 'reopen',
+ },
+ {
+ text: __('Closed'),
+ value: 'close',
+ },
+];
+
+export const subscriptionsDropdownOptions = [
+ {
+ text: __('Subscribe'),
+ value: 'subscribe',
+ },
+ {
+ text: __('Unsubscribe'),
+ value: 'unsubscribe',
+ },
+];
+
+export const INCIDENT_SEVERITY = {
+ CRITICAL: {
+ value: 'CRITICAL',
+ icon: 'critical',
+ label: s__('IncidentManagement|Critical - S1'),
+ },
+ HIGH: {
+ value: 'HIGH',
+ icon: 'high',
+ label: s__('IncidentManagement|High - S2'),
+ },
+ MEDIUM: {
+ value: 'MEDIUM',
+ icon: 'medium',
+ label: s__('IncidentManagement|Medium - S3'),
+ },
+ LOW: {
+ value: 'LOW',
+ icon: 'low',
+ label: s__('IncidentManagement|Low - S4'),
+ },
+ UNKNOWN: {
+ value: 'UNKNOWN',
+ icon: 'unknown',
+ label: s__('IncidentManagement|Unknown'),
+ },
+};
+
+export const ISSUABLE_TYPES = {
+ INCIDENT: 'incident',
+};
+
+export const MILESTONE_STATE = {
+ ACTIVE: 'active',
+ CLOSED: 'closed',
+};
+
+export const SEVERITY_I18N = {
+ UPDATE_SEVERITY_ERROR: s__('SeverityWidget|There was an error while updating severity.'),
+ TRY_AGAIN: __('Please try again'),
+ EDIT: __('Edit'),
+ SEVERITY: s__('SeverityWidget|Severity'),
+ SEVERITY_VALUE: s__('SeverityWidget|Severity: %{severity}'),
+};
+
+export const STATUS_TRIGGERED = 'TRIGGERED';
+export const STATUS_ACKNOWLEDGED = 'ACKNOWLEDGED';
+export const STATUS_RESOLVED = 'RESOLVED';
+
+export const STATUS_TRIGGERED_LABEL = s__('IncidentManagement|Triggered');
+export const STATUS_ACKNOWLEDGED_LABEL = s__('IncidentManagement|Acknowledged');
+export const STATUS_RESOLVED_LABEL = s__('IncidentManagement|Resolved');
+
+export const STATUS_LABELS = {
+ [STATUS_TRIGGERED]: STATUS_TRIGGERED_LABEL,
+ [STATUS_ACKNOWLEDGED]: STATUS_ACKNOWLEDGED_LABEL,
+ [STATUS_RESOLVED]: STATUS_RESOLVED_LABEL,
+};
+
+export const INCIDENTS_I18N = {
+ fetchError: s__(
+ 'IncidentManagement|An error occurred while fetching the incident status. Please reload the page.',
+ ),
+ title: s__('IncidentManagement|Status'),
+ updateError: s__(
+ 'IncidentManagement|An error occurred while updating the incident status. Please reload the page and try again.',
+ ),
+};
diff --git a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
index afce59d304f..b908cf0cd9e 100644
--- a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
@@ -39,6 +39,7 @@ export default class SidebarMilestone {
humanTimeEstimate,
humanTotalTimeSpent: humanTimeSpent,
},
+ canAddTimeEntries: false,
},
}),
});
diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js
index b37486283ca..a308dc8d13c 100644
--- a/app/assets/javascripts/sidebar/mount_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_sidebar.js
@@ -6,6 +6,7 @@ import { convertToGraphQLId } from '~/graphql_shared/utils';
import initInviteMembersModal from '~/invite_members/init_invite_members_modal';
import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger';
import { IssuableType } from '~/issues/constants';
+import { gqlClient } from '~/issues/list/graphql';
import {
isInIssuePage,
isInDesignPage,
@@ -14,33 +15,36 @@ import {
parseBoolean,
} from '~/lib/utils/common_utils';
import { __ } from '~/locale';
-import CollapsedAssigneeList from '~/sidebar/components/assignees/collapsed_assignee_list.vue';
-import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assignees_widget.vue';
-import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
-import SidebarDueDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue';
-import MilestoneDropdown from '~/sidebar/components/milestone/milestone_dropdown.vue';
-import SidebarParticipantsWidget from '~/sidebar/components/participants/sidebar_participants_widget.vue';
-import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue';
-import SidebarDropdownWidget from '~/sidebar/components/sidebar_dropdown_widget.vue';
-import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
import { apolloProvider } from '~/graphql_shared/issuable_client';
-import trackShowInviteMemberLink from '~/sidebar/track_invite_members';
-import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_vue/constants';
-import LabelsSelectWidget from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue';
-import { LabelType } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
-import Translate from '../vue_shared/translate';
+import Translate from '~/vue_shared/translate';
+import CollapsedAssigneeList from './components/assignees/collapsed_assignee_list.vue';
import SidebarAssignees from './components/assignees/sidebar_assignees.vue';
-import CopyEmailToClipboard from './components/copy_email_to_clipboard.vue';
+import SidebarAssigneesWidget from './components/assignees/sidebar_assignees_widget.vue';
+import SidebarConfidentialityWidget from './components/confidential/sidebar_confidentiality_widget.vue';
+import CopyEmailToClipboard from './components/copy/copy_email_to_clipboard.vue';
+import SidebarDueDateWidget from './components/date/sidebar_date_widget.vue';
import SidebarEscalationStatus from './components/incidents/sidebar_escalation_status.vue';
+import { DropdownVariant } from './components/labels/labels_select_vue/constants';
+import { LabelType } from './components/labels/labels_select_widget/constants';
+import LabelsSelectWidget from './components/labels/labels_select_widget/labels_select_root.vue';
import IssuableLockForm from './components/lock/issuable_lock_form.vue';
+import MilestoneDropdown from './components/milestone/milestone_dropdown.vue';
+import MoveIssuesButton from './components/move/move_issues_button.vue';
+import SidebarParticipantsWidget from './components/participants/sidebar_participants_widget.vue';
+import SidebarReferenceWidget from './components/copy/sidebar_reference_widget.vue';
import SidebarReviewers from './components/reviewers/sidebar_reviewers.vue';
import SidebarReviewersInputs from './components/reviewers/sidebar_reviewers_inputs.vue';
import SidebarSeverity from './components/severity/sidebar_severity.vue';
+import SidebarDropdownWidget from './components/sidebar_dropdown_widget.vue';
+import StatusDropdown from './components/status/status_dropdown.vue';
import SidebarSubscriptionsWidget from './components/subscriptions/sidebar_subscriptions_widget.vue';
+import SubscriptionsDropdown from './components/subscriptions/subscriptions_dropdown.vue';
import SidebarTimeTracking from './components/time_tracking/sidebar_time_tracking.vue';
+import SidebarTodoWidget from './components/todo_toggle/sidebar_todo_widget.vue';
import { IssuableAttributeType } from './constants';
-import SidebarMoveIssue from './lib/sidebar_move_issue';
import CrmContacts from './components/crm_contacts/crm_contacts.vue';
+import SidebarMoveIssue from './lib/sidebar_move_issue';
+import trackShowInviteMemberLink from './track_invite_members';
Vue.use(Translate);
Vue.use(VueApollo);
@@ -540,7 +544,15 @@ function mountSidebarSubscriptionsWidget() {
function mountSidebarTimeTracking() {
const el = document.querySelector('.js-sidebar-time-tracking-root');
- const { id, iid, fullPath, issuableType, timeTrackingLimitToHours } = getSidebarOptions();
+
+ const {
+ id,
+ iid,
+ fullPath,
+ issuableType,
+ timeTrackingLimitToHours,
+ canCreateTimelogs,
+ } = getSidebarOptions();
if (!el) {
return null;
@@ -558,6 +570,7 @@ function mountSidebarTimeTracking() {
issuableId: id.toString(),
issuableIid: iid.toString(),
limitToHours: timeTrackingLimitToHours,
+ canAddTimeEntries: canCreateTimelogs,
},
}),
});
@@ -635,6 +648,59 @@ function mountCopyEmailToClipboard() {
});
}
+export function mountMoveIssuesButton() {
+ const el = document.querySelector('.js-move-issues');
+
+ if (!el) {
+ return null;
+ }
+
+ Vue.use(VueApollo);
+
+ return new Vue({
+ el,
+ name: 'MoveIssuesRoot',
+ apolloProvider: new VueApollo({
+ defaultClient: gqlClient,
+ }),
+ render: (createElement) =>
+ createElement(MoveIssuesButton, {
+ props: {
+ projectFullPath: el.dataset.projectFullPath,
+ projectsFetchPath: el.dataset.projectsFetchPath,
+ },
+ }),
+ });
+}
+
+export function mountStatusDropdown() {
+ const el = document.querySelector('.js-status-dropdown');
+
+ if (!el) {
+ return null;
+ }
+
+ return new Vue({
+ el,
+ name: 'StatusDropdownRoot',
+ render: (createElement) => createElement(StatusDropdown),
+ });
+}
+
+export function mountSubscriptionsDropdown() {
+ const el = document.querySelector('.js-subscriptions-dropdown');
+
+ if (!el) {
+ return null;
+ }
+
+ return new Vue({
+ el,
+ name: 'SubscriptionsDropdownRoot',
+ render: (createElement) => createElement(SubscriptionsDropdown),
+ });
+}
+
const isAssigneesWidgetShown =
(isInIssuePage() || isInDesignPage() || isInMRPage()) && gon.features.issueAssigneesWidget;
diff --git a/app/assets/javascripts/sidebar/queries/create_timelog.mutation.graphql b/app/assets/javascripts/sidebar/queries/create_timelog.mutation.graphql
new file mode 100644
index 00000000000..a8692387a46
--- /dev/null
+++ b/app/assets/javascripts/sidebar/queries/create_timelog.mutation.graphql
@@ -0,0 +1,17 @@
+#import "~/graphql_shared/fragments/issue_time_tracking.fragment.graphql"
+#import "~/graphql_shared/fragments/merge_request_time_tracking.fragment.graphql"
+
+mutation createTimelog($input: TimelogCreateInput!) {
+ timelogCreate(input: $input) {
+ errors
+ timelog {
+ id
+ issue {
+ ...IssueTimeTrackingFragment
+ }
+ mergeRequest {
+ ...MergeRequestTimeTrackingFragment
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql b/app/assets/javascripts/sidebar/queries/delete_timelog.mutation.graphql
index 6e916893b5a..6e916893b5a 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql
+++ b/app/assets/javascripts/sidebar/queries/delete_timelog.mutation.graphql
diff --git a/app/assets/javascripts/sidebar/queries/get_alert_assignees.query.graphql b/app/assets/javascripts/sidebar/queries/get_alert_assignees.query.graphql
new file mode 100644
index 00000000000..171eca50eab
--- /dev/null
+++ b/app/assets/javascripts/sidebar/queries/get_alert_assignees.query.graphql
@@ -0,0 +1,22 @@
+#import "~/graphql_shared/fragments/user.fragment.graphql"
+#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
+
+query alertAssignees(
+ $domain: AlertManagementDomainFilter = threat_monitoring
+ $fullPath: ID!
+ $iid: String!
+) {
+ workspace: project(fullPath: $fullPath) {
+ id
+ issuable: alertManagementAlert(domain: $domain, iid: $iid) {
+ id
+ iid
+ assignees {
+ nodes {
+ ...User
+ ...UserAvailability
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql b/app/assets/javascripts/sidebar/queries/get_issue_assignees.query.graphql
index 4af07366a6d..4af07366a6d 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql
+++ b/app/assets/javascripts/sidebar/queries/get_issue_assignees.query.graphql
diff --git a/app/assets/javascripts/sidebar/components/crm_contacts/queries/get_issue_crm_contacts.query.graphql b/app/assets/javascripts/sidebar/queries/get_issue_crm_contacts.query.graphql
index 30a0af10d56..30a0af10d56 100644
--- a/app/assets/javascripts/sidebar/components/crm_contacts/queries/get_issue_crm_contacts.query.graphql
+++ b/app/assets/javascripts/sidebar/queries/get_issue_crm_contacts.query.graphql
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql b/app/assets/javascripts/sidebar/queries/get_issue_participants.query.graphql
index eae5e96ac46..eae5e96ac46 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql
+++ b/app/assets/javascripts/sidebar/queries/get_issue_participants.query.graphql
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql b/app/assets/javascripts/sidebar/queries/get_issue_timelogs.query.graphql
index b127b8ec5a9..b127b8ec5a9 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql
+++ b/app/assets/javascripts/sidebar/queries/get_issue_timelogs.query.graphql
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_merge_request_reviewers.query.graphql b/app/assets/javascripts/sidebar/queries/get_merge_request_reviewers.query.graphql
index f087ca6c982..f087ca6c982 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_merge_request_reviewers.query.graphql
+++ b/app/assets/javascripts/sidebar/queries/get_merge_request_reviewers.query.graphql
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql b/app/assets/javascripts/sidebar/queries/get_mr_assignees.query.graphql
index f70cd723f2e..f70cd723f2e 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql
+++ b/app/assets/javascripts/sidebar/queries/get_mr_assignees.query.graphql
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_participants.query.graphql b/app/assets/javascripts/sidebar/queries/get_mr_participants.query.graphql
index 2781ac71f31..2781ac71f31 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_participants.query.graphql
+++ b/app/assets/javascripts/sidebar/queries/get_mr_participants.query.graphql
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_timelogs.query.graphql b/app/assets/javascripts/sidebar/queries/get_mr_timelogs.query.graphql
index 17f548b44b5..17f548b44b5 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_timelogs.query.graphql
+++ b/app/assets/javascripts/sidebar/queries/get_mr_timelogs.query.graphql
diff --git a/app/assets/javascripts/sidebar/components/crm_contacts/queries/issue_crm_contacts.fragment.graphql b/app/assets/javascripts/sidebar/queries/issue_crm_contacts.fragment.graphql
index 750e1f1d1af..750e1f1d1af 100644
--- a/app/assets/javascripts/sidebar/components/crm_contacts/queries/issue_crm_contacts.fragment.graphql
+++ b/app/assets/javascripts/sidebar/queries/issue_crm_contacts.fragment.graphql
diff --git a/app/assets/javascripts/sidebar/components/crm_contacts/queries/issue_crm_contacts.subscription.graphql b/app/assets/javascripts/sidebar/queries/issue_crm_contacts.subscription.graphql
index f3b6e4ec06f..f3b6e4ec06f 100644
--- a/app/assets/javascripts/sidebar/components/crm_contacts/queries/issue_crm_contacts.subscription.graphql
+++ b/app/assets/javascripts/sidebar/queries/issue_crm_contacts.subscription.graphql
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/merge_request_reviewers.subscription.graphql b/app/assets/javascripts/sidebar/queries/merge_request_reviewers.subscription.graphql
index a1b16b378b3..a1b16b378b3 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/merge_request_reviewers.subscription.graphql
+++ b/app/assets/javascripts/sidebar/queries/merge_request_reviewers.subscription.graphql
diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/components/graphql/mutations/move_issue.mutation.graphql b/app/assets/javascripts/sidebar/queries/move_issue.mutation.graphql
index d350072425b..d350072425b 100644
--- a/app/assets/javascripts/issuable/bulk_update_sidebar/components/graphql/mutations/move_issue.mutation.graphql
+++ b/app/assets/javascripts/sidebar/queries/move_issue.mutation.graphql
diff --git a/app/assets/javascripts/sidebar/components/severity/graphql/mutations/update_issuable_severity.mutation.graphql b/app/assets/javascripts/sidebar/queries/update_issuable_severity.mutation.graphql
index c9d36dfdb67..c9d36dfdb67 100644
--- a/app/assets/javascripts/sidebar/components/severity/graphql/mutations/update_issuable_severity.mutation.graphql
+++ b/app/assets/javascripts/sidebar/queries/update_issuable_severity.mutation.graphql
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql b/app/assets/javascripts/sidebar/queries/update_issue_assignees.mutation.graphql
index 24de5ea4fe3..24de5ea4fe3 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql
+++ b/app/assets/javascripts/sidebar/queries/update_issue_assignees.mutation.graphql
diff --git a/app/assets/javascripts/sidebar/components/lock/mutations/update_issue_lock.mutation.graphql b/app/assets/javascripts/sidebar/queries/update_issue_lock.mutation.graphql
index cb9ee6abc9b..cb9ee6abc9b 100644
--- a/app/assets/javascripts/sidebar/components/lock/mutations/update_issue_lock.mutation.graphql
+++ b/app/assets/javascripts/sidebar/queries/update_issue_lock.mutation.graphql
diff --git a/app/assets/javascripts/sidebar/components/lock/mutations/update_merge_request_lock.mutation.graphql b/app/assets/javascripts/sidebar/queries/update_merge_request_lock.mutation.graphql
index 11eb3611006..11eb3611006 100644
--- a/app/assets/javascripts/sidebar/components/lock/mutations/update_merge_request_lock.mutation.graphql
+++ b/app/assets/javascripts/sidebar/queries/update_merge_request_lock.mutation.graphql
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql b/app/assets/javascripts/sidebar/queries/update_mr_assignees.mutation.graphql
index 5fec2ccbdfb..5fec2ccbdfb 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql
+++ b/app/assets/javascripts/sidebar/queries/update_mr_assignees.mutation.graphql
diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js
index 912f0fdcbef..c6a66ab2275 100644
--- a/app/assets/javascripts/sidebar/sidebar_mediator.js
+++ b/app/assets/javascripts/sidebar/sidebar_mediator.js
@@ -1,9 +1,9 @@
-import Store from '~/sidebar/stores/sidebar_store';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
import toast from '~/vue_shared/plugins/global_toast';
-import { visitUrl } from '../lib/utils/url_utility';
+import { visitUrl } from '~/lib/utils/url_utility';
import Service from './services/sidebar_service';
+import Store from './stores/sidebar_store';
export default class SidebarMediator {
constructor(options) {
@@ -31,6 +31,9 @@ export default class SidebarMediator {
assignYourself() {
this.store.addAssignee(this.store.currentUser);
}
+ addSelfReview() {
+ this.store.addReviewer(this.store.currentUser);
+ }
async saveAssignees(field) {
const selected = this.store.assignees.map((u) => u.id);
@@ -56,12 +59,14 @@ export default class SidebarMediator {
}
async saveReviewers(field) {
- const selected = this.store.reviewers.map((u) => u.id);
+ const selectedReviewers = this.store.reviewers;
+ const selectedIds = selectedReviewers.map((u) => u.id);
+ const suggestedSelectedIds = selectedReviewers.filter((u) => u.suggested).map((u) => u.id);
// If there are no ids, that means we have to unassign (which is id = 0)
// And it only accepts an array, hence [0]
- const reviewers = selected.length === 0 ? [0] : selected;
- const data = { reviewer_ids: reviewers };
+ const reviewers = selectedIds.length === 0 ? [0] : selectedIds;
+ const data = { reviewer_ids: reviewers, suggested_reviewer_ids: suggestedSelectedIds };
try {
const res = await this.service.update(field, data);
diff --git a/app/assets/javascripts/sidebar/utils.js b/app/assets/javascripts/sidebar/utils.js
new file mode 100644
index 00000000000..6b90fb80abf
--- /dev/null
+++ b/app/assets/javascripts/sidebar/utils.js
@@ -0,0 +1,24 @@
+import { __, s__ } from '~/locale';
+import { STATUS_LABELS } from './constants';
+
+export const getStatusLabel = (status) => STATUS_LABELS[status] ?? s__('IncidentManagement|None');
+
+export const todoLabel = (hasTodo) => {
+ return hasTodo ? __('Mark as done') : __('Add a to do');
+};
+
+export const updateGlobalTodoCount = (additionalTodoCount) => {
+ const countContainer = document.querySelector('.js-todos-count');
+
+ if (countContainer === null) return;
+
+ const currentCount = parseInt(countContainer.innerText, 10);
+
+ const todoToggleEvent = new CustomEvent('todo:toggle', {
+ detail: {
+ count: Math.max(currentCount + additionalTodoCount, 0),
+ },
+ });
+
+ document.dispatchEvent(todoToggleEvent);
+};
diff --git a/app/assets/javascripts/snippets/components/snippet_description_view.vue b/app/assets/javascripts/snippets/components/snippet_description_view.vue
index 737a131ce7c..ab2ff6e0ef8 100644
--- a/app/assets/javascripts/snippets/components/snippet_description_view.vue
+++ b/app/assets/javascripts/snippets/components/snippet_description_view.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import MarkdownFieldView from '~/vue_shared/components/markdown/field_view.vue';
export default {
diff --git a/app/assets/javascripts/surveys/merge_request_experience/app.vue b/app/assets/javascripts/surveys/merge_request_experience/app.vue
index df114c27908..6e90ad2e0fd 100644
--- a/app/assets/javascripts/surveys/merge_request_experience/app.vue
+++ b/app/assets/javascripts/surveys/merge_request_experience/app.vue
@@ -1,6 +1,7 @@
<script>
-import { GlButton, GlSprintf, GlSafeHtmlDirective, GlTooltipDirective } from '@gitlab/ui';
+import { GlButton, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
import gitlabLogo from '@gitlab/svgs/dist/illustrations/gitlab_logo.svg';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { s__, __ } from '~/locale';
import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
import SatisfactionRate from '~/surveys/components/satisfaction_rate.vue';
@@ -30,7 +31,7 @@ export default {
SatisfactionRate,
},
directives: {
- safeHtml: GlSafeHtmlDirective,
+ SafeHtml,
tooltip: GlTooltipDirective,
},
mixins: [Tracking.mixin()],
diff --git a/app/assets/javascripts/tags/init_new_tag_ref_selector.js b/app/assets/javascripts/tags/init_new_tag_ref_selector.js
new file mode 100644
index 00000000000..11c7516f16c
--- /dev/null
+++ b/app/assets/javascripts/tags/init_new_tag_ref_selector.js
@@ -0,0 +1,23 @@
+import Vue from 'vue';
+import RefSelector from '~/ref/components/ref_selector.vue';
+
+export default function initNewTagRefSelector() {
+ const el = document.querySelector('.js-new-tag-ref-selector');
+
+ if (el) {
+ const { projectId, defaultBranchName, hiddenInputName } = el.dataset;
+ // eslint-disable-next-line no-new
+ new Vue({
+ el,
+ render(createComponent) {
+ return createComponent(RefSelector, {
+ props: {
+ value: defaultBranchName,
+ name: hiddenInputName,
+ projectId,
+ },
+ });
+ },
+ });
+ }
+}
diff --git a/app/assets/javascripts/terms/components/app.vue b/app/assets/javascripts/terms/components/app.vue
index a54a198faed..eecf32f83df 100644
--- a/app/assets/javascripts/terms/components/app.vue
+++ b/app/assets/javascripts/terms/components/app.vue
@@ -1,13 +1,13 @@
<script>
-import $ from 'jquery';
-import { GlButton, GlIntersectionObserver, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlButton, GlIntersectionObserver } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { FLASH_TYPES, FLASH_CLOSED_EVENT } from '~/flash';
import { isLoggedIn } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
import csrf from '~/lib/utils/csrf';
-import '~/behaviors/markdown/render_gfm';
import { trackTrialAcceptTerms } from '~/google_tag_manager';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
export default {
name: 'TermsApp',
@@ -54,7 +54,7 @@ export default {
},
methods: {
renderGFM() {
- $(this.$refs.gfmContainer).renderGFM();
+ renderGFM(this.$refs.gfmContainer);
},
handleBottomReached() {
this.acceptDisabled = false;
@@ -81,7 +81,7 @@ export default {
<template>
<div>
- <div class="gl-card-body gl-relative gl-pb-0 gl-px-0" data-qa-selector="terms_content">
+ <div class="gl-relative gl-pb-0 gl-px-0" data-qa-selector="terms_content">
<div
class="terms-fade gl-absolute gl-left-5 gl-right-5 gl-bottom-0 gl-h-11 gl-pointer-events-none"
></div>
@@ -96,7 +96,7 @@ export default {
</gl-intersection-observer>
</div>
</div>
- <div v-if="isLoggedIn" class="gl-card-footer gl-display-flex gl-justify-content-end">
+ <div v-if="isLoggedIn" class="gl-display-flex gl-justify-content-end">
<form v-if="permissions.canDecline" method="post" :action="paths.decline">
<gl-button type="submit">{{ $options.i18n.decline }}</gl-button>
<input :value="$options.csrf.token" type="hidden" name="authenticity_token" />
diff --git a/app/assets/javascripts/terraform/components/init_command_modal.vue b/app/assets/javascripts/terraform/components/init_command_modal.vue
index 2cb10d4ae23..0d8a883972f 100644
--- a/app/assets/javascripts/terraform/components/init_command_modal.vue
+++ b/app/assets/javascripts/terraform/components/init_command_modal.vue
@@ -39,11 +39,13 @@ export default {
},
methods: {
getModalInfoCopyStr() {
+ const stateNameEncoded = encodeURIComponent(this.stateName);
+
return `export GITLAB_ACCESS_TOKEN=<YOUR-ACCESS-TOKEN>
terraform init \\
- -backend-config="address=${this.terraformApiUrl}/${this.stateName}" \\
- -backend-config="lock_address=${this.terraformApiUrl}/${this.stateName}/lock" \\
- -backend-config="unlock_address=${this.terraformApiUrl}/${this.stateName}/lock" \\
+ -backend-config="address=${this.terraformApiUrl}/${stateNameEncoded}" \\
+ -backend-config="lock_address=${this.terraformApiUrl}/${stateNameEncoded}/lock" \\
+ -backend-config="unlock_address=${this.terraformApiUrl}/${stateNameEncoded}/lock" \\
-backend-config="username=${this.username}" \\
-backend-config="password=$GITLAB_ACCESS_TOKEN" \\
-backend-config="lock_method=POST" \\
diff --git a/app/assets/javascripts/tooltips/components/tooltips.vue b/app/assets/javascripts/tooltips/components/tooltips.vue
index 1ad18508294..a4dc783f1e4 100644
--- a/app/assets/javascripts/tooltips/components/tooltips.vue
+++ b/app/assets/javascripts/tooltips/components/tooltips.vue
@@ -1,6 +1,7 @@
<script>
-import { GlTooltip, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlTooltip } from '@gitlab/ui';
import { uniqueId } from 'lodash';
+import SafeHtml from '~/vue_shared/directives/safe_html';
const getTooltipTitle = (element) => {
return element.getAttribute('title') || element.dataset.title;
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue
index 2cfeb7a4bcb..eb93f42e2f3 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue
@@ -189,8 +189,11 @@ export default {
.then((data) => {
this.mr.setApprovals(data);
- eventHub.$emit('MRWidgetUpdateRequested');
- eventHub.$emit('ApprovalUpdated');
+ if (!window.gon?.features?.realtimeMrStatusChange) {
+ eventHub.$emit('MRWidgetUpdateRequested');
+ eventHub.$emit('ApprovalUpdated');
+ }
+
this.$emit('updated');
})
.catch(errFn)
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment/memory_usage.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment/memory_usage.vue
index 1256b3a8e52..c7d34d45f06 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/deployment/memory_usage.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment/memory_usage.vue
@@ -1,7 +1,7 @@
<script>
import { GlLoadingIcon, GlSprintf, GlLink } from '@gitlab/ui';
import { backOff } from '~/lib/utils/common_utils';
-import statusCodes from '~/lib/utils/http_status';
+import { HTTP_STATUS_NO_CONTENT } from '~/lib/utils/http_status';
import { bytesToMiB } from '~/lib/utils/number_utils';
import { s__ } from '~/locale';
import MemoryGraph from '~/vue_shared/components/memory_graph.vue';
@@ -107,7 +107,7 @@ export default {
backOff((next, stop) => {
MRWidgetService.fetchMetrics(this.metricsUrl)
.then((res) => {
- if (res.status === statusCodes.NO_CONTENT) {
+ if (res.status === HTTP_STATUS_NO_CONTENT) {
this.backOffRequestCounter += 1;
/* eslint-disable no-unused-expressions */
this.backOffRequestCounter < 3 ? next() : stop(res);
@@ -118,7 +118,7 @@ export default {
.catch(stop);
})
.then((res) => {
- if (res.status === statusCodes.NO_CONTENT) {
+ if (res.status === HTTP_STATUS_NO_CONTENT) {
return res;
}
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue b/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue
index 3d03dbd9db3..e8cc9b2eb2a 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue
@@ -1,12 +1,7 @@
<script>
-import {
- GlButton,
- GlLoadingIcon,
- GlSafeHtmlDirective,
- GlTooltipDirective,
- GlIntersectionObserver,
-} from '@gitlab/ui';
+import { GlButton, GlLoadingIcon, GlTooltipDirective, GlIntersectionObserver } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { DynamicScroller, DynamicScrollerItem } from 'vendor/vue-virtual-scroller';
import { sprintf, s__, __ } from '~/locale';
import Poll from '~/lib/utils/poll';
@@ -40,7 +35,7 @@ export default {
StateContainer,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
GlTooltip: GlTooltipDirective,
},
data() {
@@ -323,19 +318,23 @@ export default {
@mouseup="onRowMouseUp"
>
<div
+ :class="{ 'gl-h-full': isLoadingSummary }"
class="media-body gl-display-flex gl-flex-direction-row! gl-w-full"
data-testid="widget-extension-top-level"
>
- <div class="gl-flex-grow-1" data-testid="widget-extension-top-level-summary">
+ <div
+ class="gl-flex-grow-1 gl-display-flex gl-align-items-center"
+ data-testid="widget-extension-top-level-summary"
+ >
<template v-if="isLoadingSummary">{{ widgetLoadingText }}</template>
<template v-else-if="hasFetchError">{{ widgetErrorText }}</template>
- <div v-else>
+ <template v-else>
<span v-safe-html="hydratedSummary.subject"></span>
<template v-if="hydratedSummary.meta">
<br />
<span v-safe-html="hydratedSummary.meta" class="gl-font-sm"></span>
</template>
- </div>
+ </template>
</div>
<actions
:widget="$options.label || $options.name"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/child_content.vue b/app/assets/javascripts/vue_merge_request_widget/components/extensions/child_content.vue
index a10e5efa0e7..fa369d23b6c 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/child_content.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/child_content.vue
@@ -1,6 +1,7 @@
<script>
-import { GlBadge, GlLink, GlSafeHtmlDirective, GlModalDirective } from '@gitlab/ui';
+import { GlBadge, GlLink, GlModalDirective } from '@gitlab/ui';
import { isArray } from 'lodash';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import Actions from '../action_buttons.vue';
import StatusIcon from './status_icon.vue';
import { generateText } from './utils';
@@ -14,7 +15,7 @@ export default {
Actions,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
GlModal: GlModalDirective,
},
props: {
@@ -97,7 +98,12 @@ export default {
<div v-if="data.supportingText">
<p v-safe-html="generateText(data.supportingText)" class="gl-m-0"></p>
</div>
- <gl-badge v-if="data.badge" :variant="data.badge.variant || 'info'">
+ <gl-badge
+ v-if="data.badge"
+ :variant="data.badge.variant || 'info'"
+ size="sm"
+ class="gl-ml-2"
+ >
{{ data.badge.text }}
</gl-badge>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue
index f71b1fbc539..79ea2624ec5 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue
@@ -1,8 +1,11 @@
<script>
-import { GlTooltipDirective } from '@gitlab/ui';
+import { GlTooltipDirective, GlLink } from '@gitlab/ui';
export default {
name: 'MrWidgetAuthor',
+ components: {
+ GlLink,
+ },
directives: {
GlTooltip: GlTooltipDirective,
},
@@ -28,13 +31,16 @@ export default {
};
</script>
<template>
- <a
+ <gl-link
v-gl-tooltip
:href="authorUrl"
:title="showAuthorName ? null : author.name"
- class="author-link inline"
+ class="mr-widget-author"
>
- <img :src="avatarUrl" class="avatar avatar-inline s16" />
- <span v-if="showAuthorName" class="author">{{ author.name }}</span>
- </a>
+ <img :src="avatarUrl" :alt="author.name" class="avatar avatar-inline s16" /><span
+ v-if="showAuthorName"
+ class="author"
+ >{{ author.name }}</span
+ >
+ </gl-link>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
index 97c6de37054..d8a361066f4 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
@@ -7,8 +7,8 @@ import {
GlSprintf,
GlTooltip,
GlTooltipDirective,
- GlSafeHtmlDirective,
} from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { s__, n__ } from '~/locale';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import PipelineArtifacts from '~/pipelines/components/pipelines_list/pipelines_artifacts.vue';
@@ -33,7 +33,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
pipeline: {
@@ -190,7 +190,7 @@ export default {
</template>
<template v-else-if="hasPipeline">
<a :href="status.details_path" class="gl-align-self-center gl-mr-3">
- <ci-icon :status="status" :size="24" />
+ <ci-icon :status="status" :size="24" class="gl-display-flex" />
</a>
<div class="ci-widget-container d-flex">
<div class="ci-widget-content">
@@ -277,9 +277,9 @@ export default {
v-if="pipeline.details.stages"
:downstream-pipelines="pipeline.triggered"
:is-merge-train="isMergeTrain"
+ :pipeline-path="pipeline.path"
:stages="pipeline.details.stages"
:upstream-pipeline="pipeline.triggered_by"
- stages-class="mr-widget-pipeline-stages"
/>
<pipeline-artifacts :pipeline-id="pipeline.id" :artifacts="artifacts" class="gl-ml-3" />
</span>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue
index 870972156c5..1fd1e264c25 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue
@@ -1,5 +1,6 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml, GlLink } from '@gitlab/ui';
+import { GlLink } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { s__, n__ } from '~/locale';
export default {
@@ -54,16 +55,16 @@ export default {
</script>
<template>
<section>
- <p v-if="relatedLinks.closing" class="gl-display-inline gl-m-0">
+ <p v-if="relatedLinks.closing" class="gl-display-inline gl-m-0 gl-font-sm!">
{{ closesText }}
<span v-safe-html="relatedLinks.closing"></span>
</p>
- <p v-if="relatedLinks.mentioned" class="gl-display-inline gl-m-0">
+ <p v-if="relatedLinks.mentioned" class="gl-display-inline gl-m-0 gl-font-sm!">
<span v-if="relatedLinks.closing">&middot;</span>
{{ n__('mrWidget|Mentions issue', 'mrWidget|Mentions issues', relatedLinks.mentionedCount) }}
<span v-safe-html="relatedLinks.mentioned"></span>
</p>
- <p v-if="shouldShowAssignToMeLink" class="gl-display-inline gl-m-0">
+ <p v-if="shouldShowAssignToMeLink" class="gl-display-inline gl-m-0 gl-font-sm!">
<span>
<gl-link rel="nofollow" data-method="post" :href="relatedLinks.assignToMe">{{
assignIssueText
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/state_container.vue b/app/assets/javascripts/vue_merge_request_widget/components/state_container.vue
index 66e33a08a12..9a3555d3e11 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/state_container.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/state_container.vue
@@ -54,7 +54,7 @@ export default {
<template>
<div
- class="mr-widget-body media mr-widget-body-line-height-1 gl-line-height-normal"
+ class="mr-widget-body media gl-display-flex gl-align-items-center"
:class="wrapperClasses"
v-on="$listeners"
>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/merge_checks_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/merge_checks_failed.vue
index 38b99dae264..e5688091cc7 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/merge_checks_failed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/merge_checks_failed.vue
@@ -1,6 +1,6 @@
<script>
import { s__ } from '~/locale';
-import StatusIcon from '../mr_widget_status_icon.vue';
+import StateContainer from '../state_container.vue';
import { DETAILED_MERGE_STATUS } from '../../constants';
export default {
@@ -12,7 +12,7 @@ export default {
externalStatusChecksFailed: s__('mrWidget|Merge blocked: all status checks must pass.'),
},
components: {
- StatusIcon,
+ StateContainer,
},
props: {
mr: {
@@ -37,10 +37,11 @@ export default {
</script>
<template>
- <div class="mr-widget-body media gl-flex-wrap">
- <status-icon status="failed" />
- <p class="media-body gl-m-0! gl-font-weight-bold gl-text-black-normal!">
+ <state-container :mr="mr" status="failed">
+ <span
+ class="gl-ml-3 gl-font-weight-bold gl-w-100 gl-flex-grow-1 gl-md-mr-3 gl-ml-0! gl-text-body!"
+ >
{{ failedText }}
- </p>
- </div>
+ </span>
+ </state-container>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
index 806f8f939a6..6bcf88713a5 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
@@ -1,7 +1,17 @@
<script>
+import api from '~/api';
+import showGlobalToast from '~/vue_shared/plugins/global_toast';
+
import MrWidgetAuthorTime from '../mr_widget_author_time.vue';
import StateContainer from '../state_container.vue';
+import {
+ MR_WIDGET_CLOSED_REOPEN,
+ MR_WIDGET_CLOSED_REOPENING,
+ MR_WIDGET_CLOSED_RELOADING,
+ MR_WIDGET_CLOSED_REOPEN_FAILURE,
+} from '../../i18n';
+
export default {
name: 'MRWidgetClosed',
components: {
@@ -14,10 +24,62 @@ export default {
required: true,
},
},
+ data() {
+ return {
+ isPending: false,
+ isReloading: false,
+ };
+ },
+ computed: {
+ reopenText() {
+ let text = MR_WIDGET_CLOSED_REOPEN;
+
+ if (this.isPending) {
+ text = MR_WIDGET_CLOSED_REOPENING;
+ } else if (this.isReloading) {
+ text = MR_WIDGET_CLOSED_RELOADING;
+ }
+
+ return text;
+ },
+ actions() {
+ if (!window.gon?.current_user_id) {
+ return [];
+ }
+
+ return [
+ {
+ text: this.reopenText,
+ loading: this.isPending || this.isReloading,
+ onClick: this.reopen,
+ testId: 'extension-actions-reopen-button',
+ },
+ ];
+ },
+ },
+ methods: {
+ reopen() {
+ this.isPending = true;
+
+ api
+ .updateMergeRequest(this.mr.targetProjectId, this.mr.iid, { state_event: 'reopen' })
+ .then(() => {
+ this.isReloading = true;
+
+ window.location.reload();
+ })
+ .catch(() => {
+ showGlobalToast(MR_WIDGET_CLOSED_REOPEN_FAILURE);
+ })
+ .finally(() => {
+ this.isPending = false;
+ });
+ },
+ },
};
</script>
<template>
- <state-container :mr="mr" status="closed">
+ <state-container :mr="mr" status="closed" :actions="actions">
<mr-widget-author-time
:action-text="s__('mrWidget|Closed by')"
:author="mr.metrics.closedBy"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
index 4902c9b45e8..850a4e2fd56 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
@@ -1,5 +1,6 @@
<script>
-import { GlButton, GlSprintf, GlLink, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlButton, GlSprintf, GlLink } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import emptyStateSVG from 'icons/_mr_widget_empty_state.svg';
import api from '~/api';
import { helpPagePath } from '~/helpers/help_page_helper';
@@ -12,7 +13,7 @@ export default {
GlLink,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
mr: {
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 c54672cd0f8..23b163e2c6a 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
@@ -20,6 +20,8 @@ import simplePoll from '~/lib/utils/simple_poll';
import { __, s__, n__ } from '~/locale';
import SmartInterval from '~/smart_interval';
import { helpPagePath } from '~/helpers/help_page_helper';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+import readyToMergeSubscription from '~/vue_merge_request_widget/queries/states/ready_to_merge.subscription.graphql';
import {
AUTO_MERGE_STRATEGIES,
WARNING,
@@ -87,6 +89,31 @@ export default {
this.initPolling();
}
},
+ subscribeToMore: {
+ document() {
+ return readyToMergeSubscription;
+ },
+ skip() {
+ return !this.mr?.id || this.loading || !window.gon?.features?.realtimeMrStatusChange;
+ },
+ variables() {
+ return {
+ issuableId: convertToGraphQLId('MergeRequest', this.mr?.id),
+ };
+ },
+ updateQuery(
+ _,
+ {
+ subscriptionData: {
+ data: { mergeRequestMergeStatusUpdated },
+ },
+ },
+ ) {
+ if (mergeRequestMergeStatusUpdated) {
+ this.state = mergeRequestMergeStatusUpdated;
+ }
+ },
+ },
},
},
components: {
@@ -295,7 +322,7 @@ export default {
return this.mr.divergedCommitsCount > 0;
},
showMergeDetailsHeader() {
- return ['readyToMerge'].indexOf(this.mr.state) >= 0;
+ return !['readyToMerge'].includes(this.mr.state);
},
},
mounted() {
@@ -467,8 +494,9 @@ export default {
<template>
<div
+ :class="{ 'gl-bg-gray-10': mr.state !== 'closed' && mr.state !== 'merged' }"
data-testid="ready_to_merge_state"
- class="gl-border-t-1 gl-border-t-solid gl-border-gray-100 gl-bg-gray-10 gl-pl-7"
+ class="gl-border-t-1 gl-border-t-solid gl-border-gray-100 gl-pl-7"
>
<div v-if="loading" class="mr-widget-body">
<div class="gl-w-full mr-ready-to-merge-loader">
@@ -481,7 +509,9 @@ export default {
</div>
</div>
<template v-else>
- <div class="mr-widget-body mr-widget-body-ready-merge media mr-widget-body-line-height-1">
+ <div
+ class="mr-widget-body mr-widget-body-ready-merge media gl-display-flex gl-align-items-center"
+ >
<div class="media-body">
<div class="mr-widget-body-controls gl-display-flex gl-align-items-center gl-flex-wrap">
<template v-if="shouldShowMergeControls">
@@ -555,7 +585,19 @@ export default {
</li>
</ul>
</div>
- <div class="gl-w-full gl-text-gray-500 gl-mb-3 gl-md-mb-0 gl-md-pb-5">
+ <div
+ class="gl-w-full gl-text-gray-500 gl-mb-3 gl-md-mb-0 gl-md-pb-5 mr-widget-merge-details"
+ >
+ <template v-if="sourceHasDivergedFromTarget">
+ <gl-sprintf :message="$options.i18n.sourceDivergedFromTargetText">
+ <template #link>
+ <gl-link :href="mr.targetBranchPath">{{
+ $options.i18n.divergedCommits(mr.divergedCommitsCount)
+ }}</gl-link>
+ </template>
+ </gl-sprintf>
+ &middot;
+ </template>
<added-commit-message
:is-squash-enabled="squashBeforeMerge"
:is-fast-forward-enabled="!shouldShowMergeEdit"
@@ -631,7 +673,7 @@ export default {
class="gl-w-full gl-order-n1 mr-widget-merge-details"
data-qa-selector="merged_status_content"
>
- <p v-if="showMergeDetailsHeader" class="gl-mb-3 gl-text-gray-900">
+ <p v-if="showMergeDetailsHeader" class="gl-mb-2 gl-text-gray-900">
{{ __('Merge details') }}
</p>
<ul class="gl-pl-4 gl-mb-0 gl-ml-3 gl-text-gray-600">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/unresolved_discussions.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/unresolved_discussions.vue
index 074758e33b2..9f3748599dc 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/unresolved_discussions.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/unresolved_discussions.vue
@@ -26,7 +26,7 @@ export default {
<template>
<state-container :mr="mr" status="failed">
<span
- class="gl-ml-3 gl-font-weight-bold gl-w-100 gl-flex-grow-1 gl-md-mr-3 gl-ml-0! gl-text-body! gl-align-self-start"
+ class="gl-ml-3 gl-font-weight-bold gl-w-100 gl-flex-grow-1 gl-md-mr-3 gl-ml-0! gl-text-body!"
>
{{ s__('mrWidget|Merge blocked: all threads must be resolved.') }}
</span>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
index ef5be0fbfcd..01f9b4757a0 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
@@ -94,6 +94,7 @@ export default {
errors: [],
mergeRequest: {
__typename: 'MergeRequest',
+ id: this.mr.issuableId,
mergeableDiscussionsState: true,
title: this.mr.title,
draft: false,
@@ -111,7 +112,10 @@ export default {
}) => {
toast(__('Marked as ready. Merging is now allowed.'));
$('.merge-request .detail-page-description .title').text(title);
- eventHub.$emit('MRWidgetUpdateRequested');
+
+ if (!window.gon?.features?.realtimeMrStatusChange) {
+ eventHub.$emit('MRWidgetUpdateRequested');
+ }
},
)
.catch(() =>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/action_buttons.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/action_buttons.vue
new file mode 100644
index 00000000000..6655af92a55
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/action_buttons.vue
@@ -0,0 +1,134 @@
+<script>
+import { GlButton, GlDropdown, GlDropdownItem, GlTooltipDirective } from '@gitlab/ui';
+import { sprintf, __ } from '~/locale';
+
+export default {
+ components: {
+ GlButton,
+ GlDropdown,
+ GlDropdownItem,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ widget: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ tertiaryButtons: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ },
+ data: () => {
+ return {
+ timeout: null,
+ updatingTooltip: false,
+ };
+ },
+ computed: {
+ dropdownLabel() {
+ if (!this.widget) return undefined;
+
+ return sprintf(__('%{widget} options'), { widget: this.widget });
+ },
+ },
+ methods: {
+ onClickAction(action) {
+ this.$emit('clickedAction', action);
+
+ if (action.onClick) {
+ action.onClick();
+ }
+
+ if (action.tooltipOnClick) {
+ this.updatingTooltip = true;
+ this.$root.$emit('bv::show::tooltip', action.id);
+
+ clearTimeout(this.timeout);
+
+ this.timeout = setTimeout(() => {
+ this.updatingTooltip = false;
+ this.$root.$emit('bv::hide::tooltip', action.id);
+ }, 1000);
+ }
+ },
+ setTooltip(btn) {
+ if (this.updatingTooltip && btn.tooltipOnClick) {
+ return btn.tooltipOnClick;
+ }
+
+ return btn.tooltipText;
+ },
+ actionButtonQaSelector(btn) {
+ if (btn.dataQaSelector) {
+ return btn.dataQaSelector;
+ }
+ return 'mr_widget_extension_actions_button';
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="gl-display-flex gl-align-items-flex-start">
+ <gl-dropdown
+ v-if="tertiaryButtons.length"
+ v-gl-tooltip
+ :title="__('Options')"
+ :text="dropdownLabel"
+ icon="ellipsis_v"
+ no-caret
+ category="tertiary"
+ right
+ lazy
+ text-sr-only
+ size="small"
+ toggle-class="gl-p-2!"
+ class="gl-display-block gl-md-display-none!"
+ >
+ <gl-dropdown-item
+ v-for="(btn, index) in tertiaryButtons"
+ :key="index"
+ :href="btn.href"
+ :target="btn.target"
+ :data-clipboard-text="btn.dataClipboardText"
+ :data-method="btn.dataMethod"
+ @click="onClickAction(btn)"
+ >
+ {{ btn.text }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+ <template v-if="tertiaryButtons.length">
+ <gl-button
+ v-for="(btn, index) in tertiaryButtons"
+ :id="btn.id"
+ :key="index"
+ v-gl-tooltip.hover
+ :title="setTooltip(btn)"
+ :href="btn.href"
+ :target="btn.target"
+ :class="[{ 'gl-mr-3': index !== tertiaryButtons.length - 1 }, btn.class]"
+ :data-clipboard-text="btn.dataClipboardText"
+ :data-qa-selector="actionButtonQaSelector(btn)"
+ :data-method="btn.dataMethod"
+ :icon="btn.icon"
+ :data-testid="btn.testId || 'extension-actions-button'"
+ :variant="btn.variant || 'confirm'"
+ :loading="btn.loading"
+ :disabled="btn.loading"
+ category="tertiary"
+ size="small"
+ class="gl-display-none gl-md-display-block gl-float-left"
+ @click="onClickAction(btn)"
+ >
+ <template v-if="btn.text">
+ {{ btn.text }}
+ </template>
+ </gl-button>
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue
index 2f52ac70833..18aa85484ea 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue
@@ -20,13 +20,14 @@ export default {
role="region"
:aria-label="__('Merge request reports')"
data-testid="mr-widget-app"
+ class="mr-widget-section"
>
<component
:is="widget"
v-for="(widget, index) in widgets"
:key="widget.name || index"
:mr="mr"
- :class="{ 'mr-widget-border-top': index === 0 }"
+ class="mr-widget-section"
/>
</section>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/dynamic_content.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/dynamic_content.vue
index 4d66c75719b..cdce7c6625a 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/widget/dynamic_content.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/dynamic_content.vue
@@ -1,8 +1,9 @@
<script>
-import { GlBadge, GlLink, GlSafeHtmlDirective } from '@gitlab/ui';
-import Actions from '../action_buttons.vue';
+import { GlBadge, GlLink } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { generateText } from '../extensions/utils';
import ContentRow from './widget_content_row.vue';
+import Actions from './action_buttons.vue';
export default {
name: 'DynamicContent',
@@ -13,7 +14,7 @@ export default {
ContentRow,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
data: {
@@ -81,10 +82,8 @@ export default {
v-if="data.children && data.children.length > 0 && level === 2"
class="gl-m-0 gl-p-0 gl-list-style-none"
>
- <li>
+ <li v-for="(childData, index) in data.children" :key="childData.id || index">
<dynamic-content
- v-for="(childData, index) in data.children"
- :key="childData.id || index"
:data="childData"
:widget-name="widgetName"
:level="3"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/status_icon.vue
index 181b8cfad9a..6d17ac98d7f 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/widget/status_icon.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/status_icon.vue
@@ -48,9 +48,9 @@ export default {
:class="{
[iconClassNameText]: !isLoading,
[`mr-widget-status-icon-level-${level}`]: !isLoading,
- 'gl-mr-3': level === 1,
+ 'gl-w-6 gl-h-6 gl--flex-center': level === 1,
}"
- class="gl-relative gl-w-6 gl-h-6 gl-rounded-full gl--flex-center"
+ class="gl-relative gl-rounded-full gl-mr-3"
>
<gl-loading-icon v-if="isLoading" size="md" inline />
<gl-icon
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue
index cea7fb8260a..cdf35033021 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue
@@ -1,22 +1,18 @@
<script>
-import {
- GlButton,
- GlLink,
- GlTooltipDirective,
- GlLoadingIcon,
- GlSafeHtmlDirective,
-} from '@gitlab/ui';
+import { GlButton, GlLink, GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { normalizeHeaders } from '~/lib/utils/common_utils';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { sprintf, __ } from '~/locale';
import Poll from '~/lib/utils/poll';
import HelpPopover from '~/vue_shared/components/help_popover.vue';
-import ActionButtons from '../action_buttons.vue';
+import { DynamicScroller, DynamicScrollerItem } from 'vendor/vue-virtual-scroller';
import { EXTENSION_ICONS } from '../../constants';
import { createTelemetryHub } from '../extensions/telemetry';
import ContentRow from './widget_content_row.vue';
import DynamicContent from './dynamic_content.vue';
import StatusIcon from './status_icon.vue';
+import ActionButtons from './action_buttons.vue';
const FETCH_TYPE_COLLAPSED = 'collapsed';
const FETCH_TYPE_EXPANDED = 'expanded';
@@ -31,11 +27,13 @@ export default {
GlLoadingIcon,
ContentRow,
DynamicContent,
+ DynamicScroller,
+ DynamicScrollerItem,
HelpPopover,
},
directives: {
GlTooltip: GlTooltipDirective,
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
/**
@@ -258,6 +256,7 @@ export default {
<div class="gl-display-flex">
<help-popover
v-if="helpPopover"
+ icon="information-o"
:options="helpPopover.options"
:class="{ 'gl-mr-3': actionButtons.length > 0 }"
>
@@ -309,7 +308,7 @@ export default {
<div v-if="isLoadingExpandedContent" class="report-block-container gl-text-center">
<gl-loading-icon size="sm" inline /> {{ loadingText }}
</div>
- <div v-else class="gl-px-5 gl-display-flex">
+ <div v-else class="gl-pl-5 gl-display-flex" :class="{ 'gl-pr-5': $scopedSlots.content }">
<content-row
v-if="contentError"
:level="2"
@@ -322,12 +321,25 @@ export default {
</content-row>
<div v-else class="gl-w-full">
<slot name="content">
- <dynamic-content
- v-for="(data, index) in content"
- :key="data.id || index"
- :data="data"
- :widget-name="widgetName"
- />
+ <dynamic-scroller
+ v-if="content"
+ :items="content"
+ :min-item-size="32"
+ :style="{ maxHeight: '170px' }"
+ data-testid="dynamic-content-scroller"
+ class="gl-pr-5"
+ >
+ <template #default="{ item, index, active }">
+ <dynamic-scroller-item :item="item" :active="active">
+ <dynamic-content
+ :key="item.id || index"
+ :data="item"
+ :widget-name="widgetName"
+ :level="2"
+ />
+ </dynamic-scroller-item>
+ </template>
+ </dynamic-scroller>
</slot>
</div>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_row.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_row.vue
index 1fd1e325863..543136dc659 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_row.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_row.vue
@@ -1,10 +1,11 @@
<script>
-import { GlSafeHtmlDirective, GlLink } from '@gitlab/ui';
+import { GlLink } from '@gitlab/ui';
import { __ } from '~/locale';
import HelpPopover from '~/vue_shared/components/help_popover.vue';
-import ActionButtons from '../action_buttons.vue';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { EXTENSION_ICONS } from '../../constants';
import { generateText } from '../extensions/utils';
+import ActionButtons from './action_buttons.vue';
import StatusIcon from './status_icon.vue';
export default {
@@ -15,7 +16,7 @@ export default {
ActionButtons,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
level: {
@@ -67,6 +68,9 @@ export default {
shouldShowHeaderActions() {
return Boolean(this.helpPopover) || this.actionButtons?.length > 0;
},
+ hasActionButtons() {
+ return this.actionButtons.length > 0;
+ },
},
i18n: {
learnMore: __('Learn more'),
@@ -75,10 +79,15 @@ export default {
</script>
<template>
<div
- class="gl-w-full gl-display-flex mr-widget-content-row gl-align-items-baseline"
+ class="gl-w-full gl-display-flex gl-align-items-baseline"
:class="{ 'gl-border-t gl-py-3 gl-pl-7': level === 2 }"
>
- <status-icon v-if="statusIconName" :level="2" :name="widgetName" :icon-name="statusIconName" />
+ <status-icon
+ v-if="statusIconName && !header"
+ :level="2"
+ :name="widgetName"
+ :icon-name="statusIconName"
+ />
<div class="gl-w-full">
<div class="gl-display-flex">
<slot name="header">
@@ -95,7 +104,12 @@ export default {
v-if="shouldShowHeaderActions"
class="gl-ml-auto gl-display-flex gl-align-items-baseline"
>
- <help-popover v-if="helpPopover" :options="helpPopover.options">
+ <help-popover
+ v-if="helpPopover"
+ :options="helpPopover.options"
+ :class="{ 'gl-mr-3': hasActionButtons }"
+ icon="information-o"
+ >
<template v-if="helpPopover.content">
<p
v-if="helpPopover.content.text"
@@ -112,14 +126,19 @@ export default {
</template>
</help-popover>
<action-buttons
- v-if="actionButtons.length > 0"
+ v-if="hasActionButtons"
:widget="widgetName"
:tertiary-buttons="actionButtons"
- :class="{ 'gl-ml-2': helpPopover }"
/>
</div>
</div>
<div class="gl-display-flex gl-align-items-baseline gl-w-full">
+ <status-icon
+ v-if="statusIconName && header"
+ :level="2"
+ :name="widgetName"
+ :icon-name="statusIconName"
+ />
<slot name="body"></slot>
</div>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/extensions/code_quality/constants.js b/app/assets/javascripts/vue_merge_request_widget/extensions/code_quality/constants.js
new file mode 100644
index 00000000000..03af21a5019
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/extensions/code_quality/constants.js
@@ -0,0 +1,31 @@
+import { n__, s__, sprintf } from '~/locale';
+
+export const i18n = {
+ label: s__('ciReport|Code Quality'),
+ loading: s__('ciReport|Code Quality is loading'),
+ error: s__('ciReport|Code Quality failed to load results'),
+ noChanges: s__(`ciReport|Code Quality hasn't changed.`),
+ prependText: s__(`ciReport|in`),
+ fixed: s__(`ciReport|Fixed`),
+ pluralReport: (errors) =>
+ sprintf(
+ n__(
+ '%{strong_start}%{errors}%{strong_end} point',
+ '%{strong_start}%{errors}%{strong_end} points',
+ errors.length,
+ ),
+ {
+ errors: errors.length,
+ },
+ false,
+ ),
+ singularReport: (errors) => n__('%d point', '%d points', errors.length),
+ improvementAndDegradationCopy: (improvement, degradation) =>
+ sprintf(
+ s__(`ciReport|Code Quality improved on ${improvement} and degraded on ${degradation}.`),
+ ),
+ improvedCopy: (improvements) =>
+ sprintf(s__(`ciReport|Code Quality improved on ${improvements}.`)),
+ degradedCopy: (degradations) =>
+ sprintf(s__(`ciReport|Code Quality degraded on ${degradations}.`)),
+};
diff --git a/app/assets/javascripts/vue_merge_request_widget/extensions/code_quality/index.js b/app/assets/javascripts/vue_merge_request_widget/extensions/code_quality/index.js
index 68347ac269e..394f8979a53 100644
--- a/app/assets/javascripts/vue_merge_request_widget/extensions/code_quality/index.js
+++ b/app/assets/javascripts/vue_merge_request_widget/extensions/code_quality/index.js
@@ -1,54 +1,33 @@
-import { n__, s__, sprintf } from '~/locale';
import axios from '~/lib/utils/axios_utils';
import { EXTENSION_ICONS } from '~/vue_merge_request_widget/constants';
-import { SEVERITY_ICONS_EXTENSION } from '~/reports/codequality_report/constants';
-import { parseCodeclimateMetrics } from '~/reports/codequality_report/store/utils/codequality_parser';
+import { SEVERITY_ICONS_MR_WIDGET } from '~/ci/reports/codequality_report/constants';
+import { HTTP_STATUS_NO_CONTENT } from '~/lib/utils/http_status';
+import { parseCodeclimateMetrics } from '~/ci/reports/codequality_report/store/utils/codequality_parser';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
+import { i18n } from './constants';
export default {
name: 'WidgetCodeQuality',
+ enablePolling: true,
props: ['codeQuality', 'blobPath'],
- i18n: {
- label: s__('ciReport|Code Quality'),
- loading: s__('ciReport|Code Quality test metrics results are being parsed'),
- error: s__('ciReport|Code Quality failed loading results'),
- },
+ i18n,
computed: {
- summary() {
- const { newErrors, resolvedErrors, errorSummary } = this.collapsedData;
- if (errorSummary.errored >= 1 && errorSummary.resolved >= 1) {
- const improvements = sprintf(
- n__(
- '%{strong_start}%{errors}%{strong_end} point',
- '%{strong_start}%{errors}%{strong_end} points',
- resolvedErrors.length,
- ),
- {
- errors: resolvedErrors.length,
- },
- false,
- );
+ summary(data) {
+ const { newErrors, resolvedErrors, errorSummary, parsingInProgress } = data;
- const degradations = sprintf(
- n__(
- '%{strong_start}%{errors}%{strong_end} point',
- '%{strong_start}%{errors}%{strong_end} points',
- newErrors.length,
- ),
- { errors: newErrors.length },
- false,
- );
- return sprintf(
- s__(`ciReport|Code Quality improved on ${improvements} and degraded on ${degradations}.`),
+ if (parsingInProgress) {
+ return i18n.loading;
+ } else if (errorSummary.errored >= 1 && errorSummary.resolved >= 1) {
+ return i18n.improvementAndDegradationCopy(
+ i18n.pluralReport(resolvedErrors),
+ i18n.pluralReport(newErrors),
);
} else if (errorSummary.resolved >= 1) {
- const improvements = n__('%d point', '%d points', resolvedErrors.length);
- return sprintf(s__(`ciReport|Code Quality improved on ${improvements}.`));
+ return i18n.improvedCopy(i18n.singularReport(resolvedErrors));
} else if (errorSummary.errored >= 1) {
- const degradations = n__('%d point', '%d points', newErrors.length);
- return sprintf(s__(`ciReport|Code Quality degraded on ${degradations}.`));
+ return i18n.degradedCopy(i18n.singularReport(newErrors));
}
- return s__(`ciReport|No changes to Code Quality.`);
+ return i18n.noChanges;
},
statusIcon() {
if (this.collapsedData.errorSummary?.errored >= 1) {
@@ -59,18 +38,17 @@ export default {
},
methods: {
fetchCollapsedData() {
- return Promise.all([this.fetchReport(this.codeQuality)]).then((values) => {
+ return axios.get(this.codeQuality).then((response) => {
+ const { data = {}, status } = response;
return {
- resolvedErrors: parseCodeclimateMetrics(
- values[0].resolved_errors,
- this.blobPath.head_path,
- ),
- newErrors: parseCodeclimateMetrics(values[0].new_errors, this.blobPath.head_path),
- existingErrors: parseCodeclimateMetrics(
- values[0].existing_errors,
- this.blobPath.head_path,
- ),
- errorSummary: values[0].summary,
+ ...response,
+ data: {
+ parsingInProgress: status === HTTP_STATUS_NO_CONTENT,
+ resolvedErrors: parseCodeclimateMetrics(data.resolved_errors, this.blobPath.head_path),
+ newErrors: parseCodeclimateMetrics(data.new_errors, this.blobPath.head_path),
+ existingErrors: parseCodeclimateMetrics(data.existing_errors, this.blobPath.head_path),
+ errorSummary: data.summary,
+ },
};
});
},
@@ -81,12 +59,12 @@ export default {
return fullData.push({
text: `${capitalizeFirstCharacter(e.severity)} - ${e.description}`,
subtext: {
- prependText: s__(`ciReport|in`),
+ prependText: i18n.prependText,
text: `${e.file_path}:${e.line}`,
href: e.urlPath,
},
icon: {
- name: SEVERITY_ICONS_EXTENSION[e.severity],
+ name: SEVERITY_ICONS_MR_WIDGET[e.severity],
},
});
});
@@ -95,12 +73,16 @@ export default {
return fullData.push({
text: `${capitalizeFirstCharacter(e.severity)} - ${e.description}`,
subtext: {
- prependText: s__(`ciReport|in`),
+ prependText: i18n.prependText,
text: `${e.file_path}:${e.line}`,
href: e.urlPath,
},
icon: {
- name: SEVERITY_ICONS_EXTENSION[e.severity],
+ name: SEVERITY_ICONS_MR_WIDGET[e.severity],
+ },
+ badge: {
+ variant: 'neutral',
+ text: i18n.fixed,
},
});
});
diff --git a/app/assets/javascripts/vue_merge_request_widget/i18n.js b/app/assets/javascripts/vue_merge_request_widget/i18n.js
index 454a14faabb..5380bcae003 100644
--- a/app/assets/javascripts/vue_merge_request_widget/i18n.js
+++ b/app/assets/javascripts/vue_merge_request_widget/i18n.js
@@ -25,3 +25,10 @@ export const MERGE_TRAIN_BUTTON_TEXT = {
failed: __('Start merge train...'),
passed: __('Start merge train'),
};
+
+export const MR_WIDGET_CLOSED_REOPEN = __('Reopen');
+export const MR_WIDGET_CLOSED_REOPENING = __('Reopening...');
+export const MR_WIDGET_CLOSED_RELOADING = __('Refreshing...');
+export const MR_WIDGET_CLOSED_REOPEN_FAILURE = __(
+ 'An error occurred. Unable to reopen this merge request.',
+);
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index b96bdcb3833..00024a594dc 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -1,10 +1,10 @@
<script>
-import { GlSafeHtmlDirective } from '@gitlab/ui';
import { isEmpty } from 'lodash';
import {
registerExtension,
registeredExtensions,
} from '~/vue_merge_request_widget/components/extensions';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import MrWidgetApprovals from 'ee_else_ce/vue_merge_request_widget/components/approvals/approvals.vue';
import MRWidgetService from 'ee_else_ce/vue_merge_request_widget/services/mr_widget_service';
import MRWidgetStore from 'ee_else_ce/vue_merge_request_widget/stores/mr_widget_store';
@@ -15,6 +15,7 @@ import notify from '~/lib/utils/notify';
import { sprintf, s__, __ } from '~/locale';
import Project from '~/pages/projects/project';
import SmartInterval from '~/smart_interval';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
import { setFaviconOverlay } from '../lib/utils/favicon';
import Loading from './components/loading.vue';
import MrWidgetAlertMessage from './components/mr_widget_alert_message.vue';
@@ -46,18 +47,20 @@ import { STATE_MACHINE, stateToComponentMap } from './constants';
import eventHub from './event_hub';
import mergeRequestQueryVariablesMixin from './mixins/merge_request_query_variables';
import getStateQuery from './queries/get_state.query.graphql';
+import getStateSubscription from './queries/get_state.subscription.graphql';
import terraformExtension from './extensions/terraform';
import accessibilityExtension from './extensions/accessibility';
import codeQualityExtension from './extensions/code_quality';
import testReportExtension from './extensions/test_report';
import ReportWidgetContainer from './components/report_widget_container.vue';
+import MrWidgetReadyToMerge from './components/states/new_ready_to_merge.vue';
export default {
// False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/25
// eslint-disable-next-line @gitlab/require-i18n-strings
name: 'MRWidget',
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
components: {
Loading,
@@ -76,7 +79,7 @@ export default {
MrWidgetNothingToMerge: NothingToMergeState,
MrWidgetNotAllowed: NotAllowedState,
MrWidgetMissingBranch: MissingBranchState,
- MrWidgetReadyToMerge: () => import('./components/states/new_ready_to_merge.vue'),
+ MrWidgetReadyToMerge,
ShaMismatch,
MrWidgetChecking: CheckingState,
MrWidgetUnresolvedDiscussions: UnresolvedDiscussionsState,
@@ -108,6 +111,31 @@ export default {
this.loading = false;
}
},
+ subscribeToMore: {
+ document() {
+ return getStateSubscription;
+ },
+ skip() {
+ return !this.mr?.id || this.loading || !window.gon?.features?.realtimeMrStatusChange;
+ },
+ variables() {
+ return {
+ issuableId: convertToGraphQLId('MergeRequest', this.mr?.id),
+ };
+ },
+ updateQuery(
+ _,
+ {
+ subscriptionData: {
+ data: { mergeRequestMergeStatusUpdated },
+ },
+ },
+ ) {
+ if (mergeRequestMergeStatusUpdated) {
+ this.mr.setGraphqlSubscriptionData(mergeRequestMergeStatusUpdated);
+ }
+ },
+ },
},
},
mixins: [mergeRequestQueryVariablesMixin],
@@ -128,6 +156,7 @@ export default {
machineState: store?.machineValue || STATE_MACHINE.definition.initial,
loading: true,
recomputeComponentName: 0,
+ issuableId: false,
};
},
computed: {
@@ -545,6 +574,7 @@ export default {
<mr-widget-approvals v-if="shouldRenderApprovals" :mr="mr" :service="service" />
<report-widget-container>
<extensions-container v-if="hasExtensions" :mr="mr" />
+ <widget-container v-if="mr && shouldShowSecurityExtension" :mr="mr" />
<security-reports-app
v-if="shouldRenderSecurityReport && !shouldShowSecurityExtension"
:pipeline-id="mr.pipeline.id"
@@ -580,8 +610,6 @@ export default {
</mr-widget-alert-message>
</div>
- <widget-container v-if="mr" :mr="mr" />
-
<div class="mr-widget-section" data-qa-selector="mr_widget_content">
<component :is="componentName" :mr="mr" :service="service" />
<ready-to-merge
diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/get_state.subscription.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/get_state.subscription.graphql
new file mode 100644
index 00000000000..c7b53db1221
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/queries/get_state.subscription.graphql
@@ -0,0 +1,7 @@
+subscription getStateSubscription($issuableId: IssuableID!) {
+ mergeRequestMergeStatusUpdated(issuableId: $issuableId) {
+ ... on MergeRequest {
+ detailedMergeStatus
+ }
+ }
+}
diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.fragment.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.fragment.graphql
index 54770e6579a..9b0420cc7fa 100644
--- a/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.fragment.graphql
+++ b/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.fragment.graphql
@@ -1,44 +1,11 @@
+#import "./ready_to_merge_merge_request.fragment.graphql"
+
fragment ReadyToMerge on Project {
id
onlyAllowMergeIfPipelineSucceeds
mergeRequestsFfOnlyEnabled
squashReadOnly
mergeRequest(iid: $iid) {
- id
- autoMergeEnabled
- shouldRemoveSourceBranch
- forceRemoveSourceBranch
- defaultMergeCommitMessage
- defaultSquashCommitMessage
- squash
- squashOnMerge
- availableAutoMergeStrategies
- hasCi
- mergeable
- mergeWhenPipelineSucceeds
- commitCount
- diffHeadSha
- userPermissions {
- canMerge
- removeSourceBranch
- updateMergeRequest
- }
- targetBranch
- mergeError
- commitsWithoutMergeCommits {
- nodes {
- id
- sha
- shortId
- title
- message
- }
- }
- headPipeline {
- id
- status
- path
- active
- }
+ ...ReadyToMergeMergeRequest
}
}
diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.subscription.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.subscription.graphql
new file mode 100644
index 00000000000..8aba172e09c
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.subscription.graphql
@@ -0,0 +1,9 @@
+#import "./ready_to_merge_merge_request.fragment.graphql"
+
+subscription readyToMergeSubscription($issuableId: IssuableID!) {
+ mergeRequestMergeStatusUpdated(issuableId: $issuableId) {
+ ... on MergeRequest {
+ ...ReadyToMergeMergeRequest
+ }
+ }
+}
diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge_merge_request.fragment.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge_merge_request.fragment.graphql
new file mode 100644
index 00000000000..276e2d4d63f
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge_merge_request.fragment.graphql
@@ -0,0 +1,39 @@
+fragment ReadyToMergeMergeRequest on MergeRequest {
+ id
+ detailedMergeStatus
+ autoMergeEnabled
+ shouldRemoveSourceBranch
+ forceRemoveSourceBranch
+ defaultMergeCommitMessage
+ defaultSquashCommitMessage
+ squash
+ squashOnMerge
+ availableAutoMergeStrategies
+ hasCi
+ mergeable
+ mergeWhenPipelineSucceeds
+ commitCount
+ diffHeadSha
+ userPermissions {
+ canMerge
+ removeSourceBranch
+ updateMergeRequest
+ }
+ targetBranch
+ mergeError
+ commitsWithoutMergeCommits {
+ nodes {
+ id
+ sha
+ shortId
+ title
+ message
+ }
+ }
+ headPipeline {
+ id
+ status
+ path
+ active
+ }
+}
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
index 86ce032ea3d..85df2ea63c8 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
@@ -30,6 +30,7 @@ export default class MergeRequestStore {
this.machineValue = this.stateMachine.value;
this.mergeDetailsCollapsed = window.innerWidth < 768;
this.mergeError = data.mergeError;
+ this.id = data.id;
this.setPaths(data);
@@ -177,6 +178,7 @@ export default class MergeRequestStore {
this.updateStatusState(mergeRequest.state);
+ this.issuableId = mergeRequest.id;
this.projectArchived = project.archived;
this.onlyAllowMergeIfPipelineSucceeds = project.onlyAllowMergeIfPipelineSucceeds;
this.allowMergeOnSkippedPipeline = project.allowMergeOnSkippedPipeline;
@@ -206,6 +208,12 @@ export default class MergeRequestStore {
this.setState();
}
+ setGraphqlSubscriptionData(data) {
+ this.detailedMergeStatus = data.detailedMergeStatus;
+
+ this.setState();
+ }
+
updateStatusState(state) {
if (this.mergeRequestState !== state && badgeState.updateStatus) {
badgeState.updateStatus();
diff --git a/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue b/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue
index 96c2ffa929c..6803d609dbc 100644
--- a/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue
+++ b/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue
@@ -9,9 +9,9 @@ import {
GlTabs,
GlTab,
GlButton,
- GlSafeHtmlDirective,
} from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
import { fetchPolicies } from '~/lib/graphql';
import { toggleContainerClasses } from '~/lib/utils/dom_utils';
@@ -41,7 +41,7 @@ export default {
reportedAtWithTool: s__('AlertManagement|Reported %{when} by %{tool}'),
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
severityLabels: SEVERITY_LEVELS,
tabsConfig: [
@@ -369,10 +369,10 @@ export default {
<alert-details-table :alert="alert" :loading="loading" :statuses="statuses" />
</gl-tab>
- <metric-images-tab
- :data-testid="$options.tabsConfig[1].id"
- :title="$options.tabsConfig[1].title"
- />
+ <gl-tab :title="$options.tabsConfig[1].title">
+ <metric-images-tab :data-testid="$options.tabsConfig[1].id" />
+ </gl-tab>
+
<gl-tab :data-testid="$options.tabsConfig[2].id" :title="$options.tabsConfig[2].title">
<div v-if="alert.notes.nodes.length > 0" class="issuable-discussion">
<ul class="notes main-notes-list timeline">
diff --git a/app/assets/javascripts/vue_shared/alert_details/components/alert_status.vue b/app/assets/javascripts/vue_shared/alert_details/components/alert_status.vue
index 672761af1cf..8d2ef20b381 100644
--- a/app/assets/javascripts/vue_shared/alert_details/components/alert_status.vue
+++ b/app/assets/javascripts/vue_shared/alert_details/components/alert_status.vue
@@ -106,7 +106,7 @@ export default {
@keydown.esc.native="$emit('hide-dropdown')"
@hide="$emit('hide-dropdown')"
>
- <p v-if="isSidebar" class="gl-new-dropdown-header-top" data-testid="dropdown-header">
+ <p v-if="isSidebar" class="gl-dropdown-header-top" data-testid="dropdown-header">
{{ s__('AlertManagement|Assign status') }}
</p>
<div class="dropdown-content dropdown-body">
diff --git a/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue b/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue
index 72dcc16b57a..4ec301b946b 100644
--- a/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue
+++ b/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue
@@ -242,7 +242,7 @@ export default {
@keydown.esc.native="hideDropdown"
@hide="hideDropdown"
>
- <p class="gl-new-dropdown-header-top">
+ <p class="gl-dropdown-header-top">
{{ __('Assign To') }}
</p>
<gl-search-box-by-type v-model.trim="search" :placeholder="__('Search users')" />
diff --git a/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_header.vue b/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_header.vue
index 832b154b312..b3ee01f3a24 100644
--- a/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_header.vue
+++ b/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_header.vue
@@ -1,5 +1,5 @@
<script>
-import ToggleSidebar from '~/vue_shared/components/sidebar/toggle_sidebar.vue';
+import ToggleSidebar from '~/sidebar/components/toggle/toggle_sidebar.vue';
import SidebarTodo from './sidebar_todo.vue';
export default {
diff --git a/app/assets/javascripts/vue_shared/alert_details/components/system_notes/system_note.vue b/app/assets/javascripts/vue_shared/alert_details/components/system_notes/system_note.vue
index 6b774b2a734..3c73f42b6b1 100644
--- a/app/assets/javascripts/vue_shared/alert_details/components/system_notes/system_note.vue
+++ b/app/assets/javascripts/vue_shared/alert_details/components/system_notes/system_note.vue
@@ -1,5 +1,6 @@
<script>
-import { GlIcon, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlIcon } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import NoteHeader from '~/notes/components/note_header.vue';
export default {
@@ -8,7 +9,7 @@ export default {
GlIcon,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
note: {
diff --git a/app/assets/javascripts/vue_shared/alert_details/graphql/mutations/alert_set_assignees.mutation.graphql b/app/assets/javascripts/vue_shared/alert_details/graphql/mutations/alert_set_assignees.mutation.graphql
index 33091f1ba5e..b04d5773a37 100644
--- a/app/assets/javascripts/vue_shared/alert_details/graphql/mutations/alert_set_assignees.mutation.graphql
+++ b/app/assets/javascripts/vue_shared/alert_details/graphql/mutations/alert_set_assignees.mutation.graphql
@@ -8,6 +8,7 @@ mutation alertSetAssignees($fullPath: ID!, $assigneeUsernames: [String!]!, $iid:
) {
errors
issuable: alert {
+ id
iid
assignees {
nodes {
diff --git a/app/assets/javascripts/vue_shared/components/actions_button.vue b/app/assets/javascripts/vue_shared/components/actions_button.vue
index c6c22f9c61f..175aef59ae5 100644
--- a/app/assets/javascripts/vue_shared/components/actions_button.vue
+++ b/app/assets/javascripts/vue_shared/components/actions_button.vue
@@ -1,11 +1,5 @@
<script>
-import {
- GlDropdown,
- GlDropdownItem,
- GlDropdownDivider,
- GlButton,
- GlTooltipDirective,
-} from '@gitlab/ui';
+import { GlDropdown, GlDropdownItem, GlDropdownDivider, GlButton, GlTooltip } from '@gitlab/ui';
export default {
components: {
@@ -13,11 +7,14 @@ export default {
GlDropdownItem,
GlDropdownDivider,
GlButton,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
+ GlTooltip,
},
props: {
+ id: {
+ type: String,
+ required: false,
+ default: '',
+ },
actions: {
type: Array,
required: true,
@@ -37,6 +34,11 @@ export default {
required: false,
default: 'default',
},
+ showActionTooltip: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
computed: {
hasMultipleActions() {
@@ -51,6 +53,7 @@ export default {
this.$emit('select', action.key);
},
handleClick(action, evt) {
+ this.$emit('actionClicked', { action });
return action.handle?.(evt);
},
},
@@ -58,46 +61,51 @@ export default {
</script>
<template>
- <gl-dropdown
- v-if="hasMultipleActions"
- v-gl-tooltip="selectedAction.tooltip"
- :text="selectedAction.text"
- :split-href="selectedAction.href"
- :variant="variant"
- :category="category"
- split
- data-qa-selector="action_dropdown"
- @click="handleClick(selectedAction, $event)"
- >
- <template #button-content>
- <span class="gl-new-dropdown-button-text" v-bind="selectedAction.attrs">
- {{ selectedAction.text }}
- </span>
- </template>
- <template v-for="(action, index) in actions">
- <gl-dropdown-item
- :key="action.key"
- is-check-item
- :is-checked="action.key === selectedAction.key"
- :secondary-text="action.secondaryText"
- :data-qa-selector="`${action.key}_menu_item`"
- :data-testid="`action_${action.key}`"
- @click="handleItemClick(action)"
- >
- <span class="gl-font-weight-bold">{{ action.text }}</span>
- </gl-dropdown-item>
- <gl-dropdown-divider v-if="index != actions.length - 1" :key="action.key + '_divider'" />
- </template>
- </gl-dropdown>
- <gl-button
- v-else-if="selectedAction"
- v-gl-tooltip="selectedAction.tooltip"
- v-bind="selectedAction.attrs"
- :variant="variant"
- :category="category"
- :href="selectedAction.href"
- @click="handleClick(selectedAction, $event)"
- >
- {{ selectedAction.text }}
- </gl-button>
+ <span>
+ <gl-dropdown
+ v-if="hasMultipleActions"
+ :id="id"
+ :text="selectedAction.text"
+ :split-href="selectedAction.href"
+ :variant="variant"
+ :category="category"
+ split
+ data-qa-selector="action_dropdown"
+ @click="handleClick(selectedAction, $event)"
+ >
+ <template #button-content>
+ <span class="gl-dropdown-button-text" v-bind="selectedAction.attrs">
+ {{ selectedAction.text }}
+ </span>
+ </template>
+ <template v-for="(action, index) in actions">
+ <gl-dropdown-item
+ :key="action.key"
+ is-check-item
+ :is-checked="action.key === selectedAction.key"
+ :secondary-text="action.secondaryText"
+ :data-qa-selector="`${action.key}_menu_item`"
+ :data-testid="`action_${action.key}`"
+ @click="handleItemClick(action)"
+ >
+ <span class="gl-font-weight-bold">{{ action.text }}</span>
+ </gl-dropdown-item>
+ <gl-dropdown-divider v-if="index != actions.length - 1" :key="action.key + '_divider'" />
+ </template>
+ </gl-dropdown>
+ <gl-button
+ v-else-if="selectedAction"
+ :id="id"
+ v-bind="selectedAction.attrs"
+ :variant="variant"
+ :category="category"
+ :href="selectedAction.href"
+ @click="handleClick(selectedAction, $event)"
+ >
+ {{ selectedAction.text }}
+ </gl-button>
+ <gl-tooltip v-if="selectedAction.tooltip && showActionTooltip" :target="id">
+ {{ selectedAction.tooltip }}
+ </gl-tooltip>
+ </span>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/awards_list.vue b/app/assets/javascripts/vue_shared/components/awards_list.vue
index f5d8811e83c..cb38b3e13bb 100644
--- a/app/assets/javascripts/vue_shared/components/awards_list.vue
+++ b/app/assets/javascripts/vue_shared/components/awards_list.vue
@@ -1,6 +1,7 @@
<script>
-import { GlIcon, GlButton, GlTooltipDirective, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlIcon, GlButton, GlTooltipDirective } from '@gitlab/ui';
import { groupBy } from 'lodash';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import EmojiPicker from '~/emoji/components/picker.vue';
import { __, sprintf } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@@ -17,7 +18,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [glFeatureFlagsMixin()],
props: {
@@ -158,10 +159,7 @@ export default {
return;
}
- // 100 and 1234 emoji are a number. Callback for v-for click sends it as a string
- const parsedName = /^[0-9]+$/.test(awardName) ? Number(awardName) : awardName;
-
- this.$emit('award', parsedName);
+ this.$emit('award', awardName);
if (document.activeElement) document.activeElement.blur();
},
diff --git a/app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue b/app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue
index ed0eb9cc0b8..49181bb847d 100644
--- a/app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { handleBlobRichViewer } from '~/blob/viewer';
import MarkdownFieldView from '~/vue_shared/components/markdown/field_view.vue';
import ViewerMixin from './mixins';
diff --git a/app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue b/app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue
index 0117c06c3d5..c7a76af7f74 100644
--- a/app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue
@@ -1,5 +1,6 @@
<script>
-import { GlIcon, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlIcon } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { HIGHLIGHT_CLASS_NAME } from './constants';
import ViewerMixin from './mixins';
@@ -9,7 +10,7 @@ export default {
GlIcon,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [ViewerMixin],
inject: ['blobHash'],
diff --git a/app/assets/javascripts/vue_shared/components/code_block_highlighted.vue b/app/assets/javascripts/vue_shared/components/code_block_highlighted.vue
index 65b08b608e8..352d03befc3 100644
--- a/app/assets/javascripts/vue_shared/components/code_block_highlighted.vue
+++ b/app/assets/javascripts/vue_shared/components/code_block_highlighted.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSafeHtmlDirective } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import languageLoader from '~/content_editor/services/highlight_js_language_loader';
import CodeBlock from './code_block.vue';
@@ -7,7 +7,7 @@ import CodeBlock from './code_block.vue';
export default {
name: 'CodeBlockHighlighted',
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
components: {
CodeBlock,
diff --git a/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue b/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue
index 7a982bc035a..d0a634d8e54 100644
--- a/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue
@@ -1,12 +1,6 @@
<script>
-import {
- GlAlert,
- GlModal,
- GlFormGroup,
- GlFormInput,
- GlSafeHtmlDirective as SafeHtml,
- GlSprintf,
-} from '@gitlab/ui';
+import { GlAlert, GlModal, GlFormGroup, GlFormInput, GlSprintf } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import {
CONFIRM_DANGER_MODAL_BUTTON,
CONFIRM_DANGER_MODAL_TITLE,
diff --git a/app/assets/javascripts/vue_shared/components/confirm_modal.vue b/app/assets/javascripts/vue_shared/components/confirm_modal.vue
index 72504e5bc50..664c3578785 100644
--- a/app/assets/javascripts/vue_shared/components/confirm_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/confirm_modal.vue
@@ -1,6 +1,7 @@
<script>
-import { GlModal, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlModal } from '@gitlab/ui';
import { uniqueId } from 'lodash';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import csrf from '~/lib/utils/csrf';
import eventHub, { EVENT_OPEN_CONFIRM_MODAL } from './confirm_modal_eventhub';
import DomElementListener from './dom_element_listener.vue';
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
index 3ecfac10f9c..00d12654ee3 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
@@ -1,10 +1,10 @@
<script>
-import { GlSkeletonLoader, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
-import $ from 'jquery';
-import '~/behaviors/markdown/render_gfm';
+import { GlSkeletonLoader } from '@gitlab/ui';
import { forEach, escape } from 'lodash';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
const { CancelToken } = axios;
let axiosSource;
@@ -96,7 +96,7 @@ export default {
this.isLoading = false;
this.$nextTick(() => {
- $(this.$refs.markdownPreview).renderGFM();
+ renderGFM(this.$refs.markdownPreview);
});
})
.catch(() => {
diff --git a/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue
index 181c1b89e31..d8a2789a419 100644
--- a/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue
+++ b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue
@@ -265,7 +265,6 @@ export default {
<gl-dropdown-item
v-for="(option, index) in options"
:key="index"
- data-qa-selector="quick_range_item"
:active="isOptionActive(option)"
active-class="active"
@click="setQuickRange(option)"
diff --git a/app/assets/javascripts/vue_shared/components/dismissible_alert.vue b/app/assets/javascripts/vue_shared/components/dismissible_alert.vue
index 0621ec14c6c..8395bc89790 100644
--- a/app/assets/javascripts/vue_shared/components/dismissible_alert.vue
+++ b/app/assets/javascripts/vue_shared/components/dismissible_alert.vue
@@ -1,5 +1,6 @@
<script>
-import { GlAlert, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlAlert } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
export default {
name: 'DismissibleAlert',
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js
index 755ce004aa9..993b4c11c0e 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js
@@ -8,52 +8,44 @@ export const FILTER_ANY = 'Any';
export const FILTER_CURRENT = 'Current';
export const FILTER_UPCOMING = 'Upcoming';
export const FILTER_STARTED = 'Started';
-export const FILTER_NONE_ANY = [FILTER_NONE, FILTER_ANY];
+
+export const FILTERS_NONE_ANY = [FILTER_NONE, FILTER_ANY];
export const OPERATOR_IS = '=';
export const OPERATOR_IS_TEXT = __('is');
-export const OPERATOR_IS_NOT = '!=';
-export const OPERATOR_IS_NOT_TEXT = __('is not one of');
+export const OPERATOR_NOT = '!=';
+export const OPERATOR_NOT_TEXT = __('is not one of');
export const OPERATOR_OR = '||';
export const OPERATOR_OR_TEXT = __('is one of');
-export const OPERATOR_IS_ONLY = [{ value: OPERATOR_IS, description: OPERATOR_IS_TEXT }];
-export const OPERATOR_IS_NOT_ONLY = [{ value: OPERATOR_IS_NOT, description: OPERATOR_IS_NOT_TEXT }];
-export const OPERATOR_OR_ONLY = [{ value: OPERATOR_OR, description: OPERATOR_OR_TEXT }];
-export const OPERATOR_IS_AND_IS_NOT = [...OPERATOR_IS_ONLY, ...OPERATOR_IS_NOT_ONLY];
-export const OPERATOR_IS_NOT_OR = [
- ...OPERATOR_IS_ONLY,
- ...OPERATOR_IS_NOT_ONLY,
- ...OPERATOR_OR_ONLY,
-];
-
-export const DEFAULT_LABEL_NONE = { value: FILTER_NONE, text: __('None'), title: __('None') };
-export const DEFAULT_LABEL_ANY = { value: FILTER_ANY, text: __('Any'), title: __('Any') };
-export const DEFAULT_NONE_ANY = [DEFAULT_LABEL_NONE, DEFAULT_LABEL_ANY];
+export const OPERATORS_IS = [{ value: OPERATOR_IS, description: OPERATOR_IS_TEXT }];
+export const OPERATORS_NOT = [{ value: OPERATOR_NOT, description: OPERATOR_NOT_TEXT }];
+export const OPERATORS_OR = [{ value: OPERATOR_OR, description: OPERATOR_OR_TEXT }];
+export const OPERATORS_IS_NOT = [...OPERATORS_IS, ...OPERATORS_NOT];
+export const OPERATORS_IS_NOT_OR = [...OPERATORS_IS, ...OPERATORS_NOT, ...OPERATORS_OR];
-export const DEFAULT_MILESTONE_UPCOMING = {
+export const OPTION_NONE = { value: FILTER_NONE, text: __('None'), title: __('None') };
+export const OPTION_ANY = { value: FILTER_ANY, text: __('Any'), title: __('Any') };
+export const OPTION_CURRENT = { value: FILTER_CURRENT, text: __('Current') };
+export const OPTION_STARTED = { value: FILTER_STARTED, text: __('Started'), title: __('Started') };
+export const OPTION_UPCOMING = {
value: FILTER_UPCOMING,
text: __('Upcoming'),
title: __('Upcoming'),
};
-export const DEFAULT_MILESTONE_STARTED = {
- value: FILTER_STARTED,
- text: __('Started'),
- title: __('Started'),
-};
-export const DEFAULT_MILESTONES = DEFAULT_NONE_ANY.concat([
- DEFAULT_MILESTONE_UPCOMING,
- DEFAULT_MILESTONE_STARTED,
-]);
-export const SortDirection = {
+export const OPTIONS_NONE_ANY = [OPTION_NONE, OPTION_ANY];
+
+export const DEFAULT_MILESTONES = OPTIONS_NONE_ANY.concat([OPTION_UPCOMING, OPTION_STARTED]);
+
+export const SORT_DIRECTION = {
descending: 'descending',
ascending: 'ascending',
};
-export const FILTERED_SEARCH_LABELS = 'labels';
export const FILTERED_SEARCH_TERM = 'filtered-search-term';
+export const TOKEN_TITLE_APPROVED_BY = __('Approved-By');
export const TOKEN_TITLE_ASSIGNEE = s__('SearchToken|Assignee');
export const TOKEN_TITLE_AUTHOR = __('Author');
export const TOKEN_TITLE_CONFIDENTIAL = __('Confidential');
@@ -63,11 +55,14 @@ export const TOKEN_TITLE_MILESTONE = __('Milestone');
export const TOKEN_TITLE_MY_REACTION = __('My-Reaction');
export const TOKEN_TITLE_ORGANIZATION = s__('Crm|Organization');
export const TOKEN_TITLE_RELEASE = __('Release');
+export const TOKEN_TITLE_REVIEWER = s__('SearchToken|Reviewer');
export const TOKEN_TITLE_SOURCE_BRANCH = __('Source Branch');
export const TOKEN_TITLE_STATUS = __('Status');
export const TOKEN_TITLE_TARGET_BRANCH = __('Target Branch');
export const TOKEN_TITLE_TYPE = __('Type');
+export const TOKEN_TITLE_SEARCH_WITHIN = __('Search Within');
+export const TOKEN_TYPE_APPROVED_BY = 'approved-by';
export const TOKEN_TYPE_ASSIGNEE = 'assignee';
export const TOKEN_TYPE_AUTHOR = 'author';
export const TOKEN_TYPE_CONFIDENTIAL = 'confidential';
@@ -84,5 +79,11 @@ export const TOKEN_TYPE_MILESTONE = 'milestone';
export const TOKEN_TYPE_MY_REACTION = 'my-reaction';
export const TOKEN_TYPE_ORGANIZATION = 'organization';
export const TOKEN_TYPE_RELEASE = 'release';
+export const TOKEN_TYPE_REVIEWER = 'reviewer';
+export const TOKEN_TYPE_SOURCE_BRANCH = 'source-branch';
+export const TOKEN_TYPE_STATUS = 'status';
+export const TOKEN_TYPE_TARGET_BRANCH = 'target-branch';
export const TOKEN_TYPE_TYPE = 'type';
export const TOKEN_TYPE_WEIGHT = 'weight';
+
+export const TOKEN_TYPE_SEARCH_WITHIN = 'in';
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue
index 0d0787e7033..34f64dddc41 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue
@@ -15,7 +15,7 @@ import RecentSearchesStore from '~/filtered_search/stores/recent_searches_store'
import { createAlert } from '~/flash';
import { __ } from '~/locale';
-import { SortDirection } from './constants';
+import { SORT_DIRECTION } from './constants';
import { filterEmptySearchTerm, stripQuotes, uniqueTokens } from './filtered_search_utils';
export default {
@@ -107,7 +107,7 @@ export default {
recentSearches: [],
filterValue: this.initialFilterValue,
selectedSortOption: this.sortOptions[0],
- selectedSortDirection: SortDirection.descending,
+ selectedSortDirection: SORT_DIRECTION.descending,
};
},
computed: {
@@ -130,12 +130,12 @@ export default {
);
},
sortDirectionIcon() {
- return this.selectedSortDirection === SortDirection.ascending
+ return this.selectedSortDirection === SORT_DIRECTION.ascending
? 'sort-lowest'
: 'sort-highest';
},
sortDirectionTooltip() {
- return this.selectedSortDirection === SortDirection.ascending
+ return this.selectedSortDirection === SORT_DIRECTION.ascending
? __('Sort direction: Ascending')
: __('Sort direction: Descending');
},
@@ -267,9 +267,9 @@ export default {
},
handleSortDirectionClick() {
this.selectedSortDirection =
- this.selectedSortDirection === SortDirection.ascending
- ? SortDirection.descending
- : SortDirection.ascending;
+ this.selectedSortDirection === SORT_DIRECTION.ascending
+ ? SORT_DIRECTION.descending
+ : SORT_DIRECTION.ascending;
this.$emit('onSort', this.selectedSortOption.sortDirection[this.selectedSortDirection]);
},
handleHistoryItemSelected(filters) {
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue
deleted file mode 100644
index 7c184a3c391..00000000000
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue
+++ /dev/null
@@ -1,119 +0,0 @@
-<script>
-import { GlAvatar, GlFilteredSearchSuggestion } from '@gitlab/ui';
-import { compact } from 'lodash';
-import { createAlert } from '~/flash';
-import { __ } from '~/locale';
-
-import { DEFAULT_NONE_ANY } from '../constants';
-
-import BaseToken from './base_token.vue';
-
-export default {
- components: {
- BaseToken,
- GlAvatar,
- GlFilteredSearchSuggestion,
- },
- props: {
- config: {
- type: Object,
- required: true,
- },
- value: {
- type: Object,
- required: true,
- },
- active: {
- type: Boolean,
- required: true,
- },
- },
- data() {
- return {
- authors: this.config.initialAuthors || [],
- loading: false,
- };
- },
- computed: {
- defaultAuthors() {
- return this.config.defaultAuthors || DEFAULT_NONE_ANY;
- },
- preloadedAuthors() {
- return this.config.preloadedAuthors || [];
- },
- },
- methods: {
- getActiveAuthor(authors, data) {
- return authors.find((author) => author.username.toLowerCase() === data.toLowerCase());
- },
- getAvatarUrl(author) {
- return author.avatarUrl || author.avatar_url;
- },
- fetchAuthors(searchTerm) {
- this.loading = true;
- const fetchPromise = this.config.fetchPath
- ? this.config.fetchAuthors(this.config.fetchPath, searchTerm)
- : this.config.fetchAuthors(searchTerm);
-
- fetchPromise
- .then((res) => {
- // We'd want to avoid doing this check but
- // users.json and /groups/:id/members & /projects/:id/users
- // return response differently
-
- // TODO: rm when completed https://gitlab.com/gitlab-org/gitlab/-/issues/345756
- this.authors = Array.isArray(res) ? compact(res) : compact(res.data);
- })
- .catch(() =>
- createAlert({
- message: __('There was a problem fetching users.'),
- }),
- )
- .finally(() => {
- this.loading = false;
- });
- },
- },
-};
-</script>
-
-<template>
- <base-token
- :config="config"
- :value="value"
- :active="active"
- :suggestions-loading="loading"
- :suggestions="authors"
- :get-active-token-value="getActiveAuthor"
- :default-suggestions="defaultAuthors"
- :preloaded-suggestions="preloadedAuthors"
- v-bind="$attrs"
- @fetch-suggestions="fetchAuthors"
- v-on="$listeners"
- >
- <template #view="{ viewTokenProps: { inputValue, activeTokenValue } }">
- <gl-avatar
- v-if="activeTokenValue"
- :size="16"
- :src="getAvatarUrl(activeTokenValue)"
- class="gl-mr-2"
- />
- {{ activeTokenValue ? activeTokenValue.name : inputValue }}
- </template>
- <template #suggestions-list="{ suggestions }">
- <gl-filtered-search-suggestion
- v-for="author in suggestions"
- :key="author.username"
- :value="author.username"
- >
- <div class="gl-display-flex">
- <gl-avatar :size="32" :src="getAvatarUrl(author)" />
- <div>
- <div>{{ author.name }}</div>
- <div>@{{ author.username }}</div>
- </div>
- </div>
- </gl-filtered-search-suggestion>
- </template>
- </base-token>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue
index 6a4ff07c999..b0fa3e4c27e 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue
@@ -9,7 +9,7 @@ import {
} from '@gitlab/ui';
import { debounce } from 'lodash';
-import { DEBOUNCE_DELAY, FILTER_NONE_ANY, OPERATOR_IS_NOT } from '../constants';
+import { DEBOUNCE_DELAY, FILTERS_NONE_ANY, OPERATOR_NOT } from '../constants';
import {
getRecentlyUsedSuggestions,
setTokenValueToRecentlyUsed,
@@ -100,9 +100,9 @@ export default {
return this.getActiveTokenValue(this.suggestions, this.value.data);
},
availableDefaultSuggestions() {
- if (this.value.operator === OPERATOR_IS_NOT) {
+ if (this.value.operator === OPERATOR_NOT) {
return this.defaultSuggestions.filter(
- (suggestion) => !FILTER_NONE_ANY.includes(suggestion.value),
+ (suggestion) => !FILTERS_NONE_ANY.includes(suggestion.value),
);
}
return this.defaultSuggestions;
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue
index d34cfb922a9..e0fa06c159e 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue
@@ -8,7 +8,7 @@ import { isPositiveInteger } from '~/lib/utils/number_utils';
import { __ } from '~/locale';
import searchCrmContactsQuery from '../queries/search_crm_contacts.query.graphql';
-import { DEFAULT_NONE_ANY } from '../constants';
+import { OPTIONS_NONE_ANY } from '../constants';
import BaseToken from './base_token.vue';
@@ -39,7 +39,7 @@ export default {
},
computed: {
defaultContacts() {
- return this.config.defaultContacts || DEFAULT_NONE_ANY;
+ return this.config.defaultContacts || OPTIONS_NONE_ANY;
},
namespace() {
return this.config.isProject ? ITEM_TYPE.PROJECT : ITEM_TYPE.GROUP;
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue
index c7c9350ee93..3f030c8698c 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue
@@ -8,7 +8,7 @@ import { isPositiveInteger } from '~/lib/utils/number_utils';
import { __ } from '~/locale';
import searchCrmOrganizationsQuery from '../queries/search_crm_organizations.query.graphql';
-import { DEFAULT_NONE_ANY } from '../constants';
+import { OPTIONS_NONE_ANY } from '../constants';
import BaseToken from './base_token.vue';
@@ -39,7 +39,7 @@ export default {
},
computed: {
defaultOrganizations() {
- return this.config.defaultOrganizations || DEFAULT_NONE_ANY;
+ return this.config.defaultOrganizations || OPTIONS_NONE_ANY;
},
namespace() {
return this.config.isProject ? ITEM_TYPE.PROJECT : ITEM_TYPE.GROUP;
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue
index 929823f7308..74905dc2ae0 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue
@@ -3,7 +3,7 @@ import { GlFilteredSearchSuggestion } from '@gitlab/ui';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
-import { DEFAULT_NONE_ANY } from '../constants';
+import { OPTIONS_NONE_ANY } from '../constants';
import { stripQuotes } from '../filtered_search_utils';
export default {
@@ -33,7 +33,7 @@ export default {
},
computed: {
defaultEmojis() {
- return this.config.defaultEmojis || DEFAULT_NONE_ANY;
+ return this.config.defaultEmojis || OPTIONS_NONE_ANY;
},
},
methods: {
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue
index bce0c11aafd..71c50ef292a 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue
@@ -5,7 +5,7 @@ import { createAlert } from '~/flash';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
-import { DEFAULT_NONE_ANY } from '../constants';
+import { OPTIONS_NONE_ANY } from '../constants';
import { stripQuotes } from '../filtered_search_utils';
import BaseToken from './base_token.vue';
@@ -38,7 +38,7 @@ export default {
},
computed: {
defaultLabels() {
- return this.config.defaultLabels || DEFAULT_NONE_ANY;
+ return this.config.defaultLabels || OPTIONS_NONE_ANY;
},
},
methods: {
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue
index 59701b4959e..6d681aab3ca 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue
@@ -3,7 +3,7 @@ import { GlFilteredSearchSuggestion } from '@gitlab/ui';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
-import { DEFAULT_NONE_ANY } from '../constants';
+import { OPTIONS_NONE_ANY } from '../constants';
export default {
components: {
@@ -32,7 +32,7 @@ export default {
},
computed: {
defaultReleases() {
- return this.config.defaultReleases || DEFAULT_NONE_ANY;
+ return this.config.defaultReleases || OPTIONS_NONE_ANY;
},
},
methods: {
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/user_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/user_token.vue
new file mode 100644
index 00000000000..28e65c1185f
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/user_token.vue
@@ -0,0 +1,119 @@
+<script>
+import { GlAvatar, GlFilteredSearchSuggestion } from '@gitlab/ui';
+import { compact } from 'lodash';
+import { createAlert } from '~/flash';
+import { __ } from '~/locale';
+
+import { OPTIONS_NONE_ANY } from '../constants';
+
+import BaseToken from './base_token.vue';
+
+export default {
+ components: {
+ BaseToken,
+ GlAvatar,
+ GlFilteredSearchSuggestion,
+ },
+ props: {
+ config: {
+ type: Object,
+ required: true,
+ },
+ value: {
+ type: Object,
+ required: true,
+ },
+ active: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ users: this.config.initialUsers || [],
+ loading: false,
+ };
+ },
+ computed: {
+ defaultUsers() {
+ return this.config.defaultUsers || OPTIONS_NONE_ANY;
+ },
+ preloadedUsers() {
+ return this.config.preloadedUsers || [];
+ },
+ },
+ methods: {
+ getActiveUser(users, data) {
+ return users.find((user) => user.username.toLowerCase() === data.toLowerCase());
+ },
+ getAvatarUrl(user) {
+ return user.avatarUrl || user.avatar_url;
+ },
+ fetchUsers(searchTerm) {
+ this.loading = true;
+ const fetchPromise = this.config.fetchPath
+ ? this.config.fetchUsers(this.config.fetchPath, searchTerm)
+ : this.config.fetchUsers(searchTerm);
+
+ fetchPromise
+ .then((res) => {
+ // We'd want to avoid doing this check but
+ // users.json and /groups/:id/members & /projects/:id/users
+ // return response differently
+
+ // TODO: rm when completed https://gitlab.com/gitlab-org/gitlab/-/issues/345756
+ this.users = Array.isArray(res) ? compact(res) : compact(res.data);
+ })
+ .catch(() =>
+ createAlert({
+ message: __('There was a problem fetching users.'),
+ }),
+ )
+ .finally(() => {
+ this.loading = false;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <base-token
+ :config="config"
+ :value="value"
+ :active="active"
+ :suggestions-loading="loading"
+ :suggestions="users"
+ :get-active-token-value="getActiveUser"
+ :default-suggestions="defaultUsers"
+ :preloaded-suggestions="preloadedUsers"
+ v-bind="$attrs"
+ @fetch-suggestions="fetchUsers"
+ v-on="$listeners"
+ >
+ <template #view="{ viewTokenProps: { inputValue, activeTokenValue } }">
+ <gl-avatar
+ v-if="activeTokenValue"
+ :size="16"
+ :src="getAvatarUrl(activeTokenValue)"
+ class="gl-mr-2"
+ />
+ {{ activeTokenValue ? activeTokenValue.name : inputValue }}
+ </template>
+ <template #suggestions-list="{ suggestions }">
+ <gl-filtered-search-suggestion
+ v-for="user in suggestions"
+ :key="user.username"
+ :value="user.username"
+ >
+ <div class="gl-display-flex">
+ <gl-avatar :size="32" :src="getAvatarUrl(user)" />
+ <div>
+ <div>{{ user.name }}</div>
+ <div>@{{ user.username }}</div>
+ </div>
+ </div>
+ </gl-filtered-search-suggestion>
+ </template>
+ </base-token>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/group_select/group_select.vue b/app/assets/javascripts/vue_shared/components/group_select/group_select.vue
index 1de6c0121bc..5db723e1e5a 100644
--- a/app/assets/javascripts/vue_shared/components/group_select/group_select.vue
+++ b/app/assets/javascripts/vue_shared/components/group_select/group_select.vue
@@ -1,6 +1,6 @@
<script>
import { debounce } from 'lodash';
-import { GlListbox } from '@gitlab/ui';
+import { GlCollapsibleListbox } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
import Api from '~/api';
import { __ } from '~/locale';
@@ -18,7 +18,7 @@ const MINIMUM_QUERY_LENGTH = 3;
export default {
components: {
- GlListbox,
+ GlCollapsibleListbox,
},
props: {
inputName: {
@@ -167,7 +167,7 @@ export default {
<template>
<div>
- <gl-listbox
+ <gl-collapsible-listbox
ref="listbox"
v-model="selected"
:header-text="$options.i18n.selectGroup"
@@ -188,7 +188,7 @@ export default {
</div>
<div class="gl-text-gray-300">{{ item.full_path }}</div>
</template>
- </gl-listbox>
+ </gl-collapsible-listbox>
<div class="flash-container"></div>
<input :id="inputId" data-testid="input" type="hidden" :name="inputName" :value="inputValue" />
</div>
diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue
index 96f7427dda1..3c4ae08d2f7 100644
--- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue
+++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue
@@ -1,12 +1,6 @@
<script>
-import {
- GlTooltipDirective,
- GlButton,
- GlSafeHtmlDirective,
- GlAvatarLink,
- GlAvatarLabeled,
- GlTooltip,
-} from '@gitlab/ui';
+import { GlTooltipDirective, GlButton, GlAvatarLink, GlAvatarLabeled, GlTooltip } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { isGid, getIdFromGraphQLId } from '~/graphql_shared/utils';
import { glEmojiTag } from '~/emoji';
import { __, sprintf } from '~/locale';
@@ -31,7 +25,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
EMOJI_REF: 'EMOJI_REF',
props: {
diff --git a/app/assets/javascripts/vue_shared/components/help_popover.vue b/app/assets/javascripts/vue_shared/components/help_popover.vue
index f349aa78bac..92d468cf970 100644
--- a/app/assets/javascripts/vue_shared/components/help_popover.vue
+++ b/app/assets/javascripts/vue_shared/components/help_popover.vue
@@ -1,5 +1,6 @@
<script>
-import { GlButton, GlPopover, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlButton, GlPopover } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
/**
* Render a button with a question mark icon
@@ -12,7 +13,7 @@ export default {
GlPopover,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
options: {
diff --git a/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.stories.js b/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.stories.js
new file mode 100644
index 00000000000..4106de371cb
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.stories.js
@@ -0,0 +1,26 @@
+import ListboxInput from './listbox_input.vue';
+
+export default {
+ component: ListboxInput,
+ title: 'vue_shared/listbox_input',
+};
+
+const Template = (args, { argTypes }) => ({
+ components: { ListboxInput },
+ data() {
+ return { selected: null };
+ },
+ props: Object.keys(argTypes),
+ template: '<listbox-input v-model="selected" v-bind="$props" />',
+});
+
+export const Default = Template.bind({});
+Default.args = {
+ name: 'input_name',
+ defaultToggleText: 'Select an option',
+ items: [
+ { text: 'Option 1', value: '1' },
+ { text: 'Option 2', value: '2' },
+ { text: 'Option 3', value: '3' },
+ ],
+};
diff --git a/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.vue b/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.vue
new file mode 100644
index 00000000000..b1809e6a9f3
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.vue
@@ -0,0 +1,110 @@
+<script>
+import { GlListbox } from '@gitlab/ui';
+import { __ } from '~/locale';
+
+const MIN_ITEMS_COUNT_FOR_SEARCHING = 20;
+
+export default {
+ i18n: {
+ noResultsText: __('No results found'),
+ },
+ components: {
+ GlListbox,
+ },
+ model: GlListbox.model,
+ props: {
+ name: {
+ type: String,
+ required: true,
+ },
+ defaultToggleText: {
+ type: String,
+ required: true,
+ },
+ selected: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ items: {
+ type: GlListbox.props.items.type,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ searchString: '',
+ };
+ },
+ computed: {
+ allOptions() {
+ const allOptions = [];
+
+ const getOptions = (options) => {
+ for (let i = 0; i < options.length; i += 1) {
+ const option = options[i];
+ if (option.options) {
+ getOptions(option.options);
+ } else {
+ allOptions.push(option);
+ }
+ }
+ };
+ getOptions(this.items);
+
+ return allOptions;
+ },
+ isGrouped() {
+ return this.items.some((item) => item.options !== undefined);
+ },
+ isSearchable() {
+ return this.allOptions.length > MIN_ITEMS_COUNT_FOR_SEARCHING;
+ },
+ filteredItems() {
+ const searchString = this.searchString.toLowerCase();
+
+ if (!searchString) {
+ return this.items;
+ }
+
+ if (this.isGrouped) {
+ return this.items
+ .map(({ text, options }) => {
+ return {
+ text,
+ options: options.filter((option) => option.text.toLowerCase().includes(searchString)),
+ };
+ })
+ .filter(({ options }) => options.length);
+ }
+
+ return this.items.filter((item) => item.text.toLowerCase().includes(searchString));
+ },
+ toggleText() {
+ return this.selected
+ ? this.allOptions.find((option) => option.value === this.selected).text
+ : this.defaultToggleText;
+ },
+ },
+ methods: {
+ search(searchString) {
+ this.searchString = searchString;
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-listbox
+ :selected="selected"
+ :toggle-text="toggleText"
+ :items="filteredItems"
+ :searchable="isSearchable"
+ :no-results-text="$options.i18n.noResultsText"
+ @search="search"
+ @select="$emit($options.model.event, $event)"
+ />
+ <input ref="input" type="hidden" :name="name" :value="selected" />
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/apply_suggestion.vue b/app/assets/javascripts/vue_shared/components/markdown/apply_suggestion.vue
index caec49c557a..f51ec715678 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/apply_suggestion.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/apply_suggestion.vue
@@ -74,7 +74,7 @@ export default {
@submit="onApply"
/>
<gl-button
- class="gl-w-auto! gl-mt-3 gl-text-center! gl-hover-text-white! gl-transition-medium! float-right"
+ class="gl-w-auto! gl-mt-3 gl-text-center! gl-transition-medium! float-right"
category="primary"
variant="confirm"
data-qa-selector="commit_with_custom_message_button"
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index 657e4498b53..b5f2602af5e 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -1,15 +1,16 @@
<script>
-import { GlIcon, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlIcon } from '@gitlab/ui';
import $ from 'jquery';
-import '~/behaviors/markdown/render_gfm';
import { debounce, unescape } from 'lodash';
import { createAlert } from '~/flash';
import GLForm from '~/gl_form';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import axios from '~/lib/utils/axios_utils';
import { stripHtml } from '~/lib/utils/text_utility';
import { __, sprintf } from '~/locale';
import Suggestions from '~/vue_shared/components/markdown/suggestions.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import MarkdownHeader from './header.vue';
import MarkdownToolbar from './toolbar.vue';
@@ -25,7 +26,7 @@ export default {
Suggestions,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [glFeatureFlagsMixin()],
props: {
@@ -313,7 +314,9 @@ export default {
this.markdownPreview = data.body || __('Nothing to preview.');
this.$nextTick()
- .then(() => $(this.$refs['markdown-preview']).renderGFM())
+ .then(() => {
+ renderGFM(this.$refs['markdown-preview']);
+ })
.catch(() =>
createAlert({
message: __('Error rendering Markdown preview'),
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field_view.vue b/app/assets/javascripts/vue_shared/components/markdown/field_view.vue
index d77123371f2..84d40db07bb 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field_view.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field_view.vue
@@ -1,15 +1,9 @@
<script>
-import $ from 'jquery';
-import '~/behaviors/markdown/render_gfm';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
export default {
mounted() {
- this.renderGFM();
- },
- methods: {
- renderGFM() {
- $(this.$el).renderGFM();
- },
+ renderGFM(this.$el);
},
};
</script>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue b/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue
index c0712e46613..d01eae0308f 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue
@@ -82,6 +82,11 @@ export default {
required: false,
default: false,
},
+ useBottomToolbar: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -197,6 +202,7 @@ export default {
:uploads-path="uploadsPath"
:markdown="value"
:autofocus="contentEditorAutofocused"
+ :use-bottom-toolbar="useBottomToolbar"
@initialized="setEditorAsAutofocused"
@change="updateMarkdownFromContentEditor"
@loading="disableSwitchEditingControl"
diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_row.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_row.vue
index a04f8616acb..0b598d3acaf 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_row.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_row.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
export default {
name: 'SuggestionDiffRow',
diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
index 30d72332c90..c307601e670 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
@@ -1,6 +1,6 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import Vue from 'vue';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
import SuggestionDiff from './suggestion_diff.vue';
diff --git a/app/assets/javascripts/vue_shared/components/markdown_drawer/makrdown_drawer.stories.js b/app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.stories.js
index 03bd64e2a57..03bd64e2a57 100644
--- a/app/assets/javascripts/vue_shared/components/markdown_drawer/makrdown_drawer.stories.js
+++ b/app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.stories.js
diff --git a/app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.vue b/app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.vue
index a4b509f8656..379f22fdc6f 100644
--- a/app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.vue
@@ -1,9 +1,9 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml, GlDrawer, GlAlert, GlSkeletonLoader } from '@gitlab/ui';
-import $ from 'jquery';
-import '~/behaviors/markdown/render_gfm';
+import { GlDrawer, GlAlert, GlSkeletonLoader } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { s__ } from '~/locale';
import { contentTop } from '~/lib/utils/common_utils';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import { getRenderedMarkdown } from './utils/fetch';
export const cache = {};
@@ -34,13 +34,9 @@ export default {
title: '',
body: null,
open: false,
+ drawerTop: '0px',
};
},
- computed: {
- drawerOffsetTop() {
- return `${contentTop()}px`;
- },
- },
watch: {
documentPath: {
immediate: true,
@@ -76,18 +72,23 @@ export default {
cache[this.documentPath] = { title, body };
}
},
+ getDrawerTop() {
+ this.drawerTop = `${contentTop()}px`;
+ },
renderGLFM() {
this.$nextTick(() => {
- $(this.$refs['content-element']).renderGFM();
+ renderGFM(this.$refs['content-element']);
});
},
closeDrawer() {
this.open = false;
},
toggleDrawer() {
+ this.getDrawerTop();
this.open = !this.open;
},
openDrawer() {
+ this.getDrawerTop();
this.open = true;
},
},
@@ -97,7 +98,7 @@ export default {
};
</script>
<template>
- <gl-drawer :header-height="drawerOffsetTop" :open="open" header-sticky @close="closeDrawer">
+ <gl-drawer :header-height="drawerTop" :open="open" header-sticky @close="closeDrawer">
<template #title>
<h4 data-testid="title-element" class="gl-m-0">{{ title }}</h4>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/markdown_drawer/utils/fetch.js b/app/assets/javascripts/vue_shared/components/markdown_drawer/utils/fetch.js
index 7c8e1bc160a..27237f2f16b 100644
--- a/app/assets/javascripts/vue_shared/components/markdown_drawer/utils/fetch.js
+++ b/app/assets/javascripts/vue_shared/components/markdown_drawer/utils/fetch.js
@@ -16,7 +16,7 @@ export const getRenderedMarkdown = (documentPath) => {
return axios
.get(helpPagePath(documentPath))
.then(({ data }) => {
- const { body, title } = splitDocument(data.html);
+ const { body, title } = splitDocument(data);
return {
body,
title,
diff --git a/app/assets/javascripts/vue_shared/components/metric_images/metric_images_tab.vue b/app/assets/javascripts/vue_shared/components/metric_images/metric_images_tab.vue
index e23721da223..2cadc87eca3 100644
--- a/app/assets/javascripts/vue_shared/components/metric_images/metric_images_tab.vue
+++ b/app/assets/javascripts/vue_shared/components/metric_images/metric_images_tab.vue
@@ -1,5 +1,5 @@
<script>
-import { GlFormGroup, GlFormInput, GlLoadingIcon, GlModal, GlTab } from '@gitlab/ui';
+import { GlFormGroup, GlFormInput, GlLoadingIcon, GlModal } from '@gitlab/ui';
import { mapState, mapActions } from 'vuex';
import { __, s__ } from '~/locale';
import UploadDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue';
@@ -11,7 +11,6 @@ export default {
GlFormInput,
GlLoadingIcon,
GlModal,
- GlTab,
MetricImagesTable,
UploadDropzone,
},
@@ -82,7 +81,7 @@ export default {
</script>
<template>
- <gl-tab :title="s__('Incident|Metrics')" data-testid="metrics-tab">
+ <div>
<div v-if="isLoadingMetricImages">
<gl-loading-icon class="gl-p-5" size="sm" />
</div>
@@ -117,5 +116,5 @@ export default {
:drop-description-message="$options.i18n.dropDescription"
@change="openMetricDialog"
/>
- </gl-tab>
+ </div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
index cf34a60c363..748d6082abd 100644
--- a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
@@ -16,8 +16,9 @@
* :note="{body: 'This is a note'}"
* />
*/
-import { GlSafeHtmlDirective as SafeHtml, GlAvatarLink, GlAvatar } from '@gitlab/ui';
+import { GlAvatarLink, GlAvatar } from '@gitlab/ui';
import { mapGetters } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { renderMarkdown } from '~/notes/utils';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
diff --git a/app/assets/javascripts/vue_shared/components/notes/system_note.vue b/app/assets/javascripts/vue_shared/components/notes/system_note.vue
index 1ae5045b34f..1cbbdf0deb0 100644
--- a/app/assets/javascripts/vue_shared/components/notes/system_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/system_note.vue
@@ -16,22 +16,17 @@
* }"
* />
*/
-import {
- GlButton,
- GlSkeletonLoader,
- GlTooltipDirective,
- GlIcon,
- GlSafeHtmlDirective as SafeHtml,
-} from '@gitlab/ui';
+import { GlButton, GlSkeletonLoader, GlTooltipDirective, GlIcon } from '@gitlab/ui';
import $ from 'jquery';
import { mapGetters, mapActions, mapState } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import descriptionVersionHistoryMixin from 'ee_else_ce/notes/mixins/description_version_history';
-import '~/behaviors/markdown/render_gfm';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import NoteHeader from '~/notes/components/note_header.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { spriteIcon } from '~/lib/utils/common_utils';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import TimelineEntryItem from './timeline_entry_item.vue';
const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
@@ -94,7 +89,7 @@ export default {
},
},
mounted() {
- $(this.$refs['gfm-content']).renderGFM();
+ renderGFM(this.$refs['gfm-content']);
},
methods: {
...mapActions(['fetchDescriptionVersion', 'softDeleteDescriptionVersion']),
@@ -205,7 +200,7 @@ export default {
<tr v-for="line in lines" v-once :key="line.line_code" class="line_holder">
<td
:class="line.type"
- class="diff-line-num old_line gl-border-bottom-0! gl-border-top-0!"
+ class="diff-line-num old_line gl-border-bottom-0! gl-border-top-0! gl-border-0! gl-rounded-0!"
>
{{ line.old_line }}
</td>
@@ -217,7 +212,7 @@ export default {
</td>
<td
:class="line.type"
- class="line_content gl-display-table-cell!"
+ class="line_content gl-display-table-cell! gl-border-0! gl-rounded-0!"
v-html="line.rich_text /* eslint-disable-line vue/no-v-html */"
></td>
</tr>
diff --git a/app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue b/app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue
index 867222279b2..57e3a97244e 100644
--- a/app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue
+++ b/app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue
@@ -1,22 +1,19 @@
<script>
-import {
- GlAlert,
- GlBadge,
- GlPagination,
- GlTab,
- GlTabs,
- GlSafeHtmlDirective as SafeHtml,
-} from '@gitlab/ui';
+import { GlAlert, GlBadge, GlPagination, GlTab, GlTabs } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import Api from '~/api';
import { updateHistory, setUrlParams } from '~/lib/utils/url_utility';
import Tracking from '~/tracking';
import {
- OPERATOR_IS_ONLY,
+ FILTERED_SEARCH_TERM,
+ OPERATORS_IS,
TOKEN_TITLE_ASSIGNEE,
TOKEN_TITLE_AUTHOR,
+ TOKEN_TYPE_ASSIGNEE,
+ TOKEN_TYPE_AUTHOR,
} from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
-import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
+import UserToken from '~/vue_shared/components/filtered_search_bar/tokens/user_token.vue';
import { initialPaginationState, defaultI18n, defaultPageSize } from './constants';
import { isAny } from './utils';
@@ -95,7 +92,7 @@ export default {
filterSearchTokens: {
type: Array,
required: false,
- default: () => ['author_username', 'assignee_username'],
+ default: () => [TOKEN_TYPE_AUTHOR, TOKEN_TYPE_ASSIGNEE],
},
},
data() {
@@ -113,26 +110,26 @@ export default {
defaultTokens() {
return [
{
- type: 'author_username',
+ type: TOKEN_TYPE_AUTHOR,
icon: 'user',
title: TOKEN_TITLE_AUTHOR,
unique: true,
symbol: '@',
- token: AuthorToken,
- operators: OPERATOR_IS_ONLY,
+ token: UserToken,
+ operators: OPERATORS_IS,
fetchPath: this.projectPath,
- fetchAuthors: Api.projectUsers.bind(Api),
+ fetchUsers: Api.projectUsers.bind(Api),
},
{
- type: 'assignee_username',
+ type: TOKEN_TYPE_ASSIGNEE,
icon: 'user',
title: TOKEN_TITLE_ASSIGNEE,
unique: true,
symbol: '@',
- token: AuthorToken,
- operators: OPERATOR_IS_ONLY,
+ token: UserToken,
+ operators: OPERATORS_IS,
fetchPath: this.projectPath,
- fetchAuthors: Api.projectUsers.bind(Api),
+ fetchUsers: Api.projectUsers.bind(Api),
},
];
},
@@ -144,14 +141,14 @@ export default {
if (this.authorUsername) {
value.push({
- type: 'author_username',
+ type: TOKEN_TYPE_AUTHOR,
value: { data: this.authorUsername },
});
}
if (this.assigneeUsername) {
value.push({
- type: 'assignee_username',
+ type: TOKEN_TYPE_ASSIGNEE,
value: { data: this.assigneeUsername },
});
}
@@ -226,13 +223,13 @@ export default {
filters.forEach((filter) => {
if (typeof filter === 'object') {
switch (filter.type) {
- case 'author_username':
+ case TOKEN_TYPE_AUTHOR:
filterParams.authorUsername = isAny(filter.value.data);
break;
- case 'assignee_username':
+ case TOKEN_TYPE_ASSIGNEE:
filterParams.assigneeUsername = isAny(filter.value.data);
break;
- case 'filtered-search-term':
+ case FILTERED_SEARCH_TERM:
if (filter.value.data !== '') filterParams.search = filter.value.data;
break;
default:
diff --git a/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue b/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue
index 66643ff4026..16bc8070dc1 100644
--- a/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue
+++ b/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue
@@ -1,9 +1,10 @@
<script>
-import { GlButton, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlButton, GlIcon } from '@gitlab/ui';
import { isString } from 'lodash';
import highlight from '~/lib/utils/highlight';
import { truncateNamespace } from '~/lib/utils/text_utility';
import ProjectAvatar from '~/vue_shared/components/project_avatar.vue';
+import SafeHtml from '~/vue_shared/directives/safe_html';
export default {
name: 'ProjectListItem',
diff --git a/app/assets/javascripts/vue_shared/components/registry/registry_search.vue b/app/assets/javascripts/vue_shared/components/registry/registry_search.vue
index 8c9c7c63db1..c990baaa2f3 100644
--- a/app/assets/javascripts/vue_shared/components/registry/registry_search.vue
+++ b/app/assets/javascripts/vue_shared/components/registry/registry_search.vue
@@ -1,7 +1,7 @@
<script>
import { GlSorting, GlSortingItem, GlFilteredSearch } from '@gitlab/ui';
-import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import { SORT_DIRECTION_UI } from '~/search/sort/constants';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
const ASCENDING_ORDER = 'asc';
const DESCENDING_ORDER = 'desc';
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_button.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_button.vue
deleted file mode 100644
index 9388ef4ba45..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_button.vue
+++ /dev/null
@@ -1,45 +0,0 @@
-<script>
-import { GlButton, GlIcon } from '@gitlab/ui';
-import { mapActions, mapGetters } from 'vuex';
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget` instead.
-export default {
- components: {
- GlButton,
- GlIcon,
- },
- computed: {
- ...mapGetters([
- 'dropdownButtonText',
- 'isDropdownVariantStandalone',
- 'isDropdownVariantEmbedded',
- ]),
- },
- methods: {
- ...mapActions(['toggleDropdownContents']),
- handleButtonClick(e) {
- if (this.isDropdownVariantStandalone || this.isDropdownVariantEmbedded) {
- this.toggleDropdownContents();
- }
-
- if (this.isDropdownVariantStandalone) {
- e.stopPropagation();
- }
- },
- },
-};
-</script>
-
-<template>
- <gl-button
- class="labels-select-dropdown-button js-dropdown-button w-100 text-left"
- @click="handleButtonClick"
- >
- <span class="dropdown-toggle-text gl-pointer-events-none flex-fill">
- {{ dropdownButtonText }}
- </span>
- <gl-icon name="chevron-down" class="gl-pointer-events-none float-right" />
- </gl-button>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents.vue
deleted file mode 100644
index 1064cbc26e3..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents.vue
+++ /dev/null
@@ -1,48 +0,0 @@
-<script>
-import { mapGetters, mapState } from 'vuex';
-
-import DropdownContentsCreateView from './dropdown_contents_create_view.vue';
-import DropdownContentsLabelsView from './dropdown_contents_labels_view.vue';
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue` instead.
-export default {
- components: {
- DropdownContentsLabelsView,
- DropdownContentsCreateView,
- },
- props: {
- renderOnTop: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- computed: {
- ...mapState(['showDropdownContentsCreateView']),
- ...mapGetters(['isDropdownVariantSidebar']),
- dropdownContentsView() {
- if (this.showDropdownContentsCreateView) {
- return 'dropdown-contents-create-view';
- }
- return 'dropdown-contents-labels-view';
- },
- directionStyle() {
- const bottom = this.isDropdownVariantSidebar ? '3rem' : '2rem';
- return this.renderOnTop ? { bottom } : {};
- },
- },
-};
-</script>
-
-<template>
- <div
- class="labels-select-dropdown-contents gl-w-full gl-my-2 gl-py-3 gl-rounded-base gl-absolute"
- data-testid="labels-select-dropdown-contents"
- data-qa-selector="labels_dropdown_content"
- :style="directionStyle"
- >
- <component :is="dropdownContentsView" />
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue
deleted file mode 100644
index 3ff3755de46..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue
+++ /dev/null
@@ -1,122 +0,0 @@
-<script>
-import { GlTooltipDirective, GlButton, GlFormInput, GlLink, GlLoadingIcon } from '@gitlab/ui';
-import { mapState, mapActions } from 'vuex';
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue` instead.
-export default {
- components: {
- GlButton,
- GlFormInput,
- GlLink,
- GlLoadingIcon,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- data() {
- return {
- labelTitle: '',
- selectedColor: '',
- };
- },
- computed: {
- ...mapState(['labelsCreateTitle', 'labelCreateInProgress']),
- disableCreate() {
- return !this.labelTitle.length || !this.selectedColor.length || this.labelCreateInProgress;
- },
- suggestedColors() {
- const colorsMap = gon.suggested_label_colors;
- return Object.keys(colorsMap).map((color) => ({ [color]: colorsMap[color] }));
- },
- },
- methods: {
- ...mapActions(['toggleDropdownContents', 'toggleDropdownContentsCreateView', 'createLabel']),
- getColorCode(color) {
- return Object.keys(color).pop();
- },
- getColorName(color) {
- return Object.values(color).pop();
- },
- handleColorClick(color) {
- this.selectedColor = this.getColorCode(color);
- },
- handleCreateClick() {
- this.createLabel({
- title: this.labelTitle,
- color: this.selectedColor,
- });
- },
- },
-};
-</script>
-
-<template>
- <div class="labels-select-contents-create js-labels-create">
- <div class="dropdown-title d-flex align-items-center pt-0 pb-2 gl-mb-0">
- <gl-button
- :aria-label="__('Go back')"
- category="tertiary"
- size="small"
- class="js-btn-back dropdown-header-button p-0"
- icon="arrow-left"
- @click="toggleDropdownContentsCreateView"
- />
- <span class="flex-grow-1">{{ labelsCreateTitle }}</span>
- <gl-button
- :aria-label="__('Close')"
- category="tertiary"
- size="small"
- class="dropdown-header-button p-0"
- icon="close"
- @click="toggleDropdownContents"
- />
- </div>
- <div class="dropdown-input">
- <gl-form-input
- v-model.trim="labelTitle"
- :placeholder="__('Name new label')"
- :autofocus="true"
- />
- </div>
- <div class="dropdown-content px-2">
- <div class="suggest-colors suggest-colors-dropdown mt-0 mb-2">
- <gl-link
- v-for="(color, index) in suggestedColors"
- :key="index"
- v-gl-tooltip:tooltipcontainer
- :style="{ backgroundColor: getColorCode(color) }"
- :title="getColorName(color)"
- @click.prevent="handleColorClick(color)"
- />
- </div>
- <div class="color-input-container gl-display-flex">
- <span
- class="dropdown-label-color-preview position-relative position-relative d-inline-block"
- :style="{ backgroundColor: selectedColor }"
- ></span>
- <gl-form-input
- v-model.trim="selectedColor"
- class="gl-rounded-top-left-none gl-rounded-bottom-left-none gl-mb-2"
- :placeholder="__('Use custom color #FF0000')"
- />
- </div>
- </div>
- <div class="dropdown-actions clearfix pt-2 px-2">
- <gl-button
- :disabled="disableCreate"
- category="primary"
- variant="confirm"
- class="float-left d-flex align-items-center"
- @click="handleCreateClick"
- >
- <gl-loading-icon v-show="labelCreateInProgress" size="sm" :inline="true" class="mr-1" />
- {{ __('Create') }}
- </gl-button>
- <gl-button class="float-right js-btn-cancel-create" @click="toggleDropdownContentsCreateView">
- {{ __('Cancel') }}
- </gl-button>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue
deleted file mode 100644
index e235bfde394..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue
+++ /dev/null
@@ -1,230 +0,0 @@
-<script>
-import {
- GlIntersectionObserver,
- GlLoadingIcon,
- GlButton,
- GlSearchBoxByType,
- GlLink,
-} from '@gitlab/ui';
-import fuzzaldrinPlus from 'fuzzaldrin-plus';
-import { mapState, mapGetters, mapActions } from 'vuex';
-
-import { UP_KEY_CODE, DOWN_KEY_CODE, ENTER_KEY_CODE, ESC_KEY_CODE } from '~/lib/utils/keycodes';
-
-import LabelItem from './label_item.vue';
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue` instead.
-export default {
- components: {
- GlIntersectionObserver,
- GlLoadingIcon,
- GlButton,
- GlSearchBoxByType,
- GlLink,
- LabelItem,
- },
- data() {
- return {
- searchKey: '',
- currentHighlightItem: -1,
- };
- },
- computed: {
- ...mapState([
- 'allowLabelCreate',
- 'allowMultiselect',
- 'labelsManagePath',
- 'labels',
- 'labelsFetchInProgress',
- 'labelsListTitle',
- 'footerCreateLabelTitle',
- 'footerManageLabelTitle',
- ]),
- ...mapGetters(['selectedLabelsList', 'isDropdownVariantSidebar', 'isDropdownVariantEmbedded']),
- visibleLabels() {
- if (this.searchKey) {
- return fuzzaldrinPlus.filter(this.labels, this.searchKey, {
- key: ['title'],
- });
- }
- return this.labels;
- },
- showDropdownFooter() {
- return (
- (this.isDropdownVariantSidebar || this.isDropdownVariantEmbedded) &&
- (this.allowLabelCreate || this.labelsManagePath)
- );
- },
- showNoMatchingResultsMessage() {
- return Boolean(this.searchKey) && this.visibleLabels.length === 0;
- },
- },
- watch: {
- searchKey(value) {
- // When there is search string present
- // and there are matching results,
- // highlight first item by default.
- if (value && this.visibleLabels.length) {
- this.currentHighlightItem = 0;
- }
- },
- },
- methods: {
- ...mapActions([
- 'toggleDropdownContents',
- 'toggleDropdownContentsCreateView',
- 'fetchLabels',
- 'receiveLabelsSuccess',
- 'updateSelectedLabels',
- 'toggleDropdownContents',
- ]),
- isLabelSelected(label) {
- return this.selectedLabelsList.includes(label.id);
- },
- /**
- * This method scrolls item from dropdown into
- * the view if it is off the viewable area of the
- * container.
- */
- scrollIntoViewIfNeeded() {
- const highlightedLabel = this.$refs.labelsListContainer.querySelector('.is-focused');
-
- if (highlightedLabel) {
- const container = this.$refs.labelsListContainer.getBoundingClientRect();
- const label = highlightedLabel.getBoundingClientRect();
-
- if (label.bottom > container.bottom) {
- this.$refs.labelsListContainer.scrollTop += label.bottom - container.bottom;
- } else if (label.top < container.top) {
- this.$refs.labelsListContainer.scrollTop -= container.top - label.top;
- }
- }
- },
- handleComponentAppear() {
- // We can avoid putting `catch` block here
- // as failure is handled within actions.js already.
- return this.fetchLabels().then(() => {
- this.$refs.searchInput.focusInput();
- });
- },
- /**
- * We want to remove loaded labels to ensure component
- * fetches fresh set of labels every time when shown.
- */
- handleComponentDisappear() {
- this.receiveLabelsSuccess([]);
- },
- handleCreateLabelClick() {
- this.receiveLabelsSuccess([]);
- this.toggleDropdownContentsCreateView();
- },
- /**
- * This method enables keyboard navigation support for
- * the dropdown.
- */
- handleKeyDown(e) {
- if (e.keyCode === UP_KEY_CODE && this.currentHighlightItem > 0) {
- this.currentHighlightItem -= 1;
- } else if (
- e.keyCode === DOWN_KEY_CODE &&
- this.currentHighlightItem < this.visibleLabels.length - 1
- ) {
- this.currentHighlightItem += 1;
- } else if (e.keyCode === ENTER_KEY_CODE && this.currentHighlightItem > -1) {
- this.updateSelectedLabels([this.visibleLabels[this.currentHighlightItem]]);
- this.searchKey = '';
-
- // Prevent parent form submission upon hitting enter.
- e.preventDefault();
- } else if (e.keyCode === ESC_KEY_CODE) {
- this.toggleDropdownContents();
- }
-
- if (e.keyCode !== ESC_KEY_CODE) {
- // Scroll the list only after highlighting
- // styles are rendered completely.
- this.$nextTick(() => {
- this.scrollIntoViewIfNeeded();
- });
- }
- },
- handleLabelClick(label) {
- this.updateSelectedLabels([label]);
- if (!this.allowMultiselect) this.toggleDropdownContents();
- },
- },
-};
-</script>
-
-<template>
- <gl-intersection-observer @appear="handleComponentAppear" @disappear="handleComponentDisappear">
- <div class="labels-select-contents-list js-labels-list" @keydown="handleKeyDown">
- <div
- v-if="isDropdownVariantSidebar || isDropdownVariantEmbedded"
- class="dropdown-title gl-display-flex gl-align-items-center gl-pt-0 gl-pb-3!"
- data-testid="dropdown-title"
- >
- <span class="flex-grow-1">{{ labelsListTitle }}</span>
- <gl-button
- :aria-label="__('Close')"
- category="tertiary"
- size="small"
- class="dropdown-header-button gl-p-0!"
- icon="close"
- @click="toggleDropdownContents"
- />
- </div>
- <div class="dropdown-input" @click.stop="() => {}">
- <gl-search-box-by-type
- ref="searchInput"
- v-model="searchKey"
- :disabled="labelsFetchInProgress"
- data-qa-selector="dropdown_input_field"
- />
- </div>
- <div ref="labelsListContainer" class="dropdown-content" data-testid="dropdown-content">
- <gl-loading-icon
- v-if="labelsFetchInProgress"
- class="labels-fetch-loading gl-align-items-center w-100 h-100"
- size="lg"
- />
- <ul v-else class="list-unstyled gl-mb-0 gl-word-break-word">
- <label-item
- v-for="(label, index) in visibleLabels"
- :key="label.id"
- :label="label"
- :is-label-set="label.set"
- :is-label-indeterminate="label.indeterminate"
- :highlight="index === currentHighlightItem"
- @clickLabel="handleLabelClick(label)"
- />
- <li v-show="showNoMatchingResultsMessage" class="gl-p-3 gl-text-center">
- {{ __('No matching results') }}
- </li>
- </ul>
- </div>
- <div v-if="showDropdownFooter" class="dropdown-footer" data-testid="dropdown-footer">
- <ul class="list-unstyled">
- <li v-if="allowLabelCreate">
- <gl-link
- class="gl-display-flex w-100 flex-row text-break-word label-item"
- @click="handleCreateLabelClick"
- >
- {{ footerCreateLabelTitle }}
- </gl-link>
- </li>
- <li v-if="labelsManagePath">
- <gl-link
- :href="labelsManagePath"
- class="gl-display-flex flex-row text-break-word label-item"
- >
- {{ footerManageLabelTitle }}
- </gl-link>
- </li>
- </ul>
- </div>
- </div>
- </gl-intersection-observer>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue
deleted file mode 100644
index e4325492334..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue
+++ /dev/null
@@ -1,46 +0,0 @@
-<script>
-import { GlButton, GlLoadingIcon } from '@gitlab/ui';
-import { mapState, mapActions } from 'vuex';
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue` instead.
-export default {
- components: {
- GlButton,
- GlLoadingIcon,
- },
- props: {
- labelsSelectInProgress: {
- type: Boolean,
- required: true,
- },
- },
- computed: {
- ...mapState(['allowLabelEdit', 'labelsFetchInProgress']),
- },
- methods: {
- ...mapActions(['toggleDropdownContents']),
- },
-};
-</script>
-
-<template>
- <div
- class="hide-collapsed gl-line-height-20 gl-mb-2 gl-text-gray-900 gl-font-weight-bold gl-mb-0"
- >
- {{ __('Labels') }}
- <template v-if="allowLabelEdit">
- <gl-loading-icon v-show="labelsSelectInProgress" size="sm" inline />
- <gl-button
- category="tertiary"
- size="small"
- class="float-right js-sidebar-dropdown-toggle gl-mr-n2"
- data-qa-selector="labels_edit_button"
- @click="toggleDropdownContents"
- >
- {{ __('Edit') }}
- </gl-button>
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value.vue
deleted file mode 100644
index e59d150dd43..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value.vue
+++ /dev/null
@@ -1,74 +0,0 @@
-<script>
-import { GlLabel } from '@gitlab/ui';
-import { sortBy } from 'lodash';
-import { mapState } from 'vuex';
-
-import { isScopedLabel } from '~/lib/utils/common_utils';
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue` instead.
-export default {
- components: {
- GlLabel,
- },
- props: {
- disableLabels: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- computed: {
- ...mapState([
- 'selectedLabels',
- 'allowLabelRemove',
- 'allowScopedLabels',
- 'labelsFilterBasePath',
- 'labelsFilterParam',
- ]),
- sortedSelectedLabels() {
- return sortBy(this.selectedLabels, (label) => (isScopedLabel(label) ? 0 : 1));
- },
- },
- methods: {
- labelFilterUrl(label) {
- return `${this.labelsFilterBasePath}?${this.labelsFilterParam}[]=${encodeURIComponent(
- label.title,
- )}`;
- },
- scopedLabel(label) {
- return this.allowScopedLabels && isScopedLabel(label);
- },
- },
-};
-</script>
-
-<template>
- <div
- :class="{
- 'has-labels': selectedLabels.length,
- }"
- class="hide-collapsed value issuable-show-labels js-value"
- >
- <span v-if="!selectedLabels.length" class="text-secondary">
- <slot></slot>
- </span>
- <template v-for="label in sortedSelectedLabels" v-else>
- <gl-label
- :key="label.id"
- data-qa-selector="selected_label_content"
- :data-qa-label-name="label.title"
- :title="label.title"
- :description="label.description"
- :background-color="label.color"
- :target="labelFilterUrl(label)"
- :scoped="scopedLabel(label)"
- :show-close-button="allowLabelRemove"
- :disabled="disableLabels"
- tooltip-placement="top"
- @close="$emit('onLabelRemove', label.id)"
- />
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed.vue
deleted file mode 100644
index 5966c78aa51..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed.vue
+++ /dev/null
@@ -1,53 +0,0 @@
-<script>
-import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
-import { s__, sprintf } from '~/locale';
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget` instead.
-export default {
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- components: {
- GlIcon,
- },
- props: {
- labels: {
- type: Array,
- required: true,
- },
- },
- computed: {
- labelsList() {
- const labelsString = this.labels.length
- ? this.labels
- .slice(0, 5)
- .map((label) => label.title)
- .join(', ')
- : s__('LabelSelect|Labels');
-
- if (this.labels.length > 5) {
- return sprintf(s__('LabelSelect|%{labelsString}, and %{remainingLabelCount} more'), {
- labelsString,
- remainingLabelCount: this.labels.length - 5,
- });
- }
-
- return labelsString;
- },
- },
- methods: {
- handleClick() {
- this.$emit('onValueClick');
- },
- },
-};
-</script>
-
-<template>
- <div v-gl-tooltip.left.viewport="labelsList" class="sidebar-collapsed-icon" @click="handleClick">
- <gl-icon name="labels" />
- <span>{{ labels.length }}</span>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue
deleted file mode 100644
index 154e3013acd..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue
+++ /dev/null
@@ -1,109 +0,0 @@
-<script>
-import { GlLink, GlIcon } from '@gitlab/ui';
-import { __ } from '~/locale';
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue` instead.
-export default {
- functional: true,
- props: {
- label: {
- type: Object,
- required: true,
- },
- isLabelSet: {
- type: Boolean,
- required: true,
- },
- isLabelIndeterminate: {
- type: Boolean,
- required: false,
- default: false,
- },
- highlight: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- render(h, { props, listeners }) {
- const { label, highlight, isLabelSet, isLabelIndeterminate } = props;
-
- const labelColorBox = h('span', {
- class: 'dropdown-label-box gl-flex-shrink-0 gl-top-0 gl-mr-3',
- style: {
- backgroundColor: label.color,
- },
- attrs: {
- 'data-testid': 'label-color-box',
- },
- });
-
- const checkedIcon = h(GlIcon, {
- class: {
- 'gl-mr-3 gl-flex-shrink-0 has-tooltip': true,
- hidden: !isLabelSet,
- },
- attrs: {
- title: __('Selected for all items.'),
- 'data-testid': 'checked-icon',
- },
- props: {
- name: 'mobile-issue-close',
- },
- });
-
- const indeterminateIcon = h(GlIcon, {
- class: {
- 'gl-mr-3 gl-flex-shrink-0 has-tooltip': true,
- hidden: !isLabelIndeterminate,
- },
- attrs: {
- title: __('Selected for some items.'),
- 'data-testid': 'indeterminate-icon',
- },
- props: {
- name: 'dash',
- },
- });
-
- const noIcon = h('span', {
- class: {
- 'gl-mr-5 gl-pr-3': true,
- hidden: isLabelSet || isLabelIndeterminate,
- },
- attrs: {
- 'data-testid': 'no-icon',
- },
- });
-
- const labelTitle = h('span', label.title);
-
- const labelLink = h(
- GlLink,
- {
- class: 'gl-display-flex gl-align-items-center label-item gl-text-body',
- on: {
- click: () => {
- listeners.clickLabel(label);
- },
- },
- },
- [noIcon, checkedIcon, indeterminateIcon, labelColorBox, labelTitle],
- );
-
- return h(
- 'li',
- {
- class: {
- 'gl-display-block': true,
- 'gl-text-left': true,
- 'is-focused': highlight,
- },
- },
- [labelLink],
- );
- },
-};
-</script>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
deleted file mode 100644
index e6c29e24f0c..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
+++ /dev/null
@@ -1,345 +0,0 @@
-<script>
-import $ from 'jquery';
-import Vue from 'vue';
-import Vuex, { mapState, mapActions, mapGetters } from 'vuex';
-import { isInViewport } from '~/lib/utils/common_utils';
-import { __ } from '~/locale';
-
-import { DropdownVariant } from './constants';
-import DropdownButton from './dropdown_button.vue';
-import DropdownContents from './dropdown_contents.vue';
-import DropdownTitle from './dropdown_title.vue';
-import DropdownValue from './dropdown_value.vue';
-import DropdownValueCollapsed from './dropdown_value_collapsed.vue';
-import labelsSelectModule from './store';
-
-Vue.use(Vuex);
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue` instead.
-export default {
- store: new Vuex.Store(labelsSelectModule()),
- components: {
- DropdownTitle,
- DropdownValue,
- DropdownButton,
- DropdownContents,
- DropdownValueCollapsed,
- },
- props: {
- allowLabelRemove: {
- type: Boolean,
- required: false,
- default: false,
- },
- allowLabelEdit: {
- type: Boolean,
- required: false,
- default: false,
- },
- allowLabelCreate: {
- type: Boolean,
- required: false,
- default: false,
- },
- allowMultiselect: {
- type: Boolean,
- required: false,
- default: false,
- },
- allowScopedLabels: {
- type: Boolean,
- required: false,
- default: false,
- },
- allowMultipleScopedLabels: {
- type: Boolean,
- required: false,
- default: false,
- },
- variant: {
- type: String,
- required: false,
- default: DropdownVariant.Sidebar,
- },
- selectedLabels: {
- type: Array,
- required: false,
- default: () => [],
- },
- hideCollapsedView: {
- type: Boolean,
- required: false,
- default: false,
- },
- labelsSelectInProgress: {
- type: Boolean,
- required: false,
- default: false,
- },
- labelsFetchPath: {
- type: String,
- required: false,
- default: '',
- },
- labelsManagePath: {
- type: String,
- required: false,
- default: '',
- },
- labelsFilterBasePath: {
- type: String,
- required: false,
- default: '',
- },
- labelsFilterParam: {
- type: String,
- required: false,
- default: 'label_name',
- },
- dropdownButtonText: {
- type: String,
- required: false,
- default: __('Label'),
- },
- labelsListTitle: {
- type: String,
- required: false,
- default: __('Assign labels'),
- },
- labelsCreateTitle: {
- type: String,
- required: false,
- default: __('Create group label'),
- },
- footerCreateLabelTitle: {
- type: String,
- required: false,
- default: __('Create group label'),
- },
- footerManageLabelTitle: {
- type: String,
- required: false,
- default: __('Manage group labels'),
- },
- isEditing: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- data() {
- return {
- contentIsOnViewport: true,
- };
- },
- computed: {
- ...mapState(['showDropdownButton', 'showDropdownContents']),
- ...mapGetters([
- 'isDropdownVariantSidebar',
- 'isDropdownVariantStandalone',
- 'isDropdownVariantEmbedded',
- ]),
- dropdownButtonVisible() {
- return this.isDropdownVariantSidebar ? this.showDropdownButton : true;
- },
- },
- watch: {
- selectedLabels(selectedLabels) {
- this.setInitialState({
- selectedLabels,
- });
- setTimeout(() => this.updateLabelsSetState(), 100);
- },
- showDropdownContents(showDropdownContents) {
- this.setContentIsOnViewport(showDropdownContents);
- },
- isEditing(newVal) {
- if (newVal) {
- this.toggleDropdownContents();
- }
- },
- },
- mounted() {
- this.setInitialState({
- variant: this.variant,
- allowLabelRemove: this.allowLabelRemove,
- allowLabelEdit: this.allowLabelEdit,
- allowLabelCreate: this.allowLabelCreate,
- allowMultiselect: this.allowMultiselect,
- allowScopedLabels: this.allowScopedLabels,
- allowMultipleScopedLabels: this.allowMultipleScopedLabels,
- dropdownButtonText: this.dropdownButtonText,
- selectedLabels: this.selectedLabels,
- labelsFetchPath: this.labelsFetchPath,
- labelsManagePath: this.labelsManagePath,
- labelsFilterBasePath: this.labelsFilterBasePath,
- labelsFilterParam: this.labelsFilterParam,
- labelsListTitle: this.labelsListTitle,
- labelsCreateTitle: this.labelsCreateTitle,
- footerCreateLabelTitle: this.footerCreateLabelTitle,
- footerManageLabelTitle: this.footerManageLabelTitle,
- });
-
- this.$store.subscribeAction({
- after: this.handleVuexActionDispatch,
- });
-
- document.addEventListener('mousedown', this.handleDocumentMousedown);
- document.addEventListener('click', this.handleDocumentClick);
-
- this.updateLabelsSetState();
- },
- beforeDestroy() {
- document.removeEventListener('mousedown', this.handleDocumentMousedown);
- document.removeEventListener('click', this.handleDocumentClick);
- },
- methods: {
- ...mapActions(['setInitialState', 'toggleDropdownContents', 'updateLabelsSetState']),
- /**
- * This method differentiates between
- * dispatched actions and calls necessary method.
- */
- handleVuexActionDispatch(action, state) {
- if (
- action.type === 'toggleDropdownContents' &&
- !state.showDropdownButton &&
- !state.showDropdownContents
- ) {
- const filterTouchedLabelsFn = (label) => label.touched;
- const filterSetLabelsFn = (label) => label.set;
- const labels = this.isDropdownVariantEmbedded
- ? state.labels.filter(filterSetLabelsFn)
- : state.labels.filter(filterTouchedLabelsFn);
- this.handleDropdownClose(labels, state.labels.filter(filterTouchedLabelsFn));
- }
- },
- /**
- * This method stores a mousedown event's target.
- * Required by the click listener because the click
- * event itself has no reference to this element.
- */
- handleDocumentMousedown({ target }) {
- this.mousedownTarget = target;
- },
- /**
- * This method listens for document-wide click event
- * and toggle dropdown if user clicks anywhere outside
- * the dropdown while dropdown is visible.
- */
- handleDocumentClick({ target }) {
- // We also perform the toggle exception check for the
- // last mousedown event's target to avoid hiding the
- // box when the mousedown happened inside the box and
- // only the mouseup did not.
- if (
- this.showDropdownContents &&
- !this.preventDropdownToggleOnClick(target) &&
- !this.preventDropdownToggleOnClick(this.mousedownTarget)
- ) {
- this.toggleDropdownContents();
- }
- },
- /**
- * This method checks whether a given click target
- * should prevent the dropdown from being toggled.
- */
- preventDropdownToggleOnClick(target) {
- // This approach of element detection is needed
- // as the dropdown wrapper is not using `GlDropdown` as
- // it will also require us to use `BDropdownForm`
- // which is yet to be implemented in GitLab UI.
- const hasExceptionClass = [
- 'js-dropdown-button',
- 'js-btn-cancel-create',
- 'js-sidebar-dropdown-toggle',
- ].some(
- (className) =>
- target?.classList.contains(className) ||
- target?.parentElement?.classList.contains(className),
- );
-
- const hasExceptionParent = ['.js-btn-back', '.js-labels-list'].some(
- (className) => $(target).parents(className).length,
- );
-
- const isInDropdownButtonCollapsed = this.$refs.dropdownButtonCollapsed?.$el.contains(target);
-
- const isInDropdownContents = this.$refs.dropdownContents?.$el.contains(target);
-
- return (
- hasExceptionClass ||
- hasExceptionParent ||
- isInDropdownButtonCollapsed ||
- isInDropdownContents
- );
- },
- handleDropdownClose(labels, touchedLabels) {
- // Only emit label updates if there are any
- // labels to update on UI.
- if (labels.length) this.$emit('updateSelectedLabels', labels);
- this.$emit('onDropdownClose', touchedLabels);
- },
- handleCollapsedValueClick() {
- this.$emit('toggleCollapse');
- },
- setContentIsOnViewport(showDropdownContents) {
- if (!showDropdownContents) {
- this.contentIsOnViewport = true;
-
- return;
- }
-
- this.$nextTick(() => {
- if (this.$refs.dropdownContents) {
- this.contentIsOnViewport = isInViewport(this.$refs.dropdownContents.$el);
- }
- });
- },
- },
-};
-</script>
-
-<template>
- <div
- class="labels-select-wrapper position-relative"
- :class="{
- 'is-standalone': isDropdownVariantStandalone,
- 'is-embedded': isDropdownVariantEmbedded,
- }"
- >
- <template v-if="isDropdownVariantSidebar">
- <dropdown-value-collapsed
- v-if="!hideCollapsedView"
- ref="dropdownButtonCollapsed"
- :labels="selectedLabels"
- @onValueClick="handleCollapsedValueClick"
- />
- <dropdown-title
- :allow-label-edit="allowLabelEdit"
- :labels-select-in-progress="labelsSelectInProgress"
- />
- <dropdown-value
- :disable-labels="labelsSelectInProgress"
- @onLabelRemove="$emit('onLabelRemove', $event)"
- >
- <slot></slot>
- </dropdown-value>
- <dropdown-button v-show="dropdownButtonVisible" class="gl-mt-2" />
- <dropdown-contents
- v-if="dropdownButtonVisible && showDropdownContents"
- ref="dropdownContents"
- :render-on-top="!contentIsOnViewport"
- />
- </template>
- <template v-if="isDropdownVariantStandalone || isDropdownVariantEmbedded">
- <dropdown-button v-show="dropdownButtonVisible" />
- <dropdown-contents
- v-if="dropdownButtonVisible && showDropdownContents"
- ref="dropdownContents"
- :render-on-top="!contentIsOnViewport"
- />
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue
deleted file mode 100644
index 27186281c42..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue
+++ /dev/null
@@ -1,241 +0,0 @@
-<script>
-import { GlButton, GlDropdown, GlDropdownItem, GlLink } from '@gitlab/ui';
-import { debounce } from 'lodash';
-import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
-import { __, s__, sprintf } from '~/locale';
-import DropdownContentsCreateView from './dropdown_contents_create_view.vue';
-import DropdownContentsLabelsView from './dropdown_contents_labels_view.vue';
-import DropdownFooter from './dropdown_footer.vue';
-import DropdownHeader from './dropdown_header.vue';
-import { isDropdownVariantStandalone, isDropdownVariantSidebar } from './utils';
-
-export default {
- components: {
- DropdownContentsLabelsView,
- DropdownContentsCreateView,
- DropdownHeader,
- DropdownFooter,
- GlButton,
- GlDropdown,
- GlDropdownItem,
- GlLink,
- },
- props: {
- labelsCreateTitle: {
- type: String,
- required: true,
- },
- selectedLabels: {
- type: Array,
- required: true,
- },
- allowMultiselect: {
- type: Boolean,
- required: true,
- },
- labelsListTitle: {
- type: String,
- required: true,
- },
- dropdownButtonText: {
- type: String,
- required: true,
- },
- footerCreateLabelTitle: {
- type: String,
- required: true,
- },
- footerManageLabelTitle: {
- type: String,
- required: true,
- },
- variant: {
- type: String,
- required: true,
- },
- isVisible: {
- type: Boolean,
- required: false,
- default: false,
- },
- fullPath: {
- type: String,
- required: true,
- },
- workspaceType: {
- type: String,
- required: true,
- },
- attrWorkspacePath: {
- type: String,
- required: true,
- },
- labelCreateType: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- showDropdownContentsCreateView: false,
- localSelectedLabels: [...this.selectedLabels],
- isDirty: false,
- searchKey: '',
- };
- },
- computed: {
- dropdownContentsView() {
- if (this.showDropdownContentsCreateView) {
- return 'dropdown-contents-create-view';
- }
- return 'dropdown-contents-labels-view';
- },
- dropdownTitle() {
- return this.showDropdownContentsCreateView ? this.labelsCreateTitle : this.labelsListTitle;
- },
- buttonText() {
- if (!this.localSelectedLabels.length) {
- return this.dropdownButtonText || __('Label');
- } else if (this.localSelectedLabels.length > 1) {
- return sprintf(s__('LabelSelect|%{firstLabelName} +%{remainingLabelCount} more'), {
- firstLabelName: this.localSelectedLabels[0].title,
- remainingLabelCount: this.localSelectedLabels.length - 1,
- });
- }
- return this.localSelectedLabels[0].title;
- },
- showDropdownFooter() {
- return !this.showDropdownContentsCreateView && !this.isStandalone;
- },
- isStandalone() {
- return isDropdownVariantStandalone(this.variant);
- },
- },
- watch: {
- localSelectedLabels: {
- handler() {
- this.isDirty = true;
- },
- deep: true,
- },
- isVisible(newVal) {
- if (newVal) {
- this.$refs.dropdown.show();
- this.isDirty = false;
- this.localSelectedLabels = this.selectedLabels;
- } else {
- this.$refs.dropdown.hide();
- this.setLabels();
- }
- },
- selectedLabels(newVal) {
- if (!this.isDirty) {
- this.localSelectedLabels = newVal;
- }
- },
- },
- created() {
- this.debouncedSearchKeyUpdate = debounce(this.setSearchKey, DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
- },
- beforeDestroy() {
- this.debouncedSearchKeyUpdate.cancel();
- },
- methods: {
- toggleDropdownContentsCreateView() {
- this.showDropdownContentsCreateView = !this.showDropdownContentsCreateView;
- },
- toggleDropdownContent() {
- this.toggleDropdownContentsCreateView();
- // Required to recalculate dropdown position as its size changes
- if (this.$refs.dropdown?.$refs.dropdown) {
- this.$refs.dropdown.$refs.dropdown.$_popper.scheduleUpdate();
- }
- },
- setLabels() {
- if (!this.isDirty) {
- return;
- }
- this.$emit('setLabels', this.localSelectedLabels);
- },
- handleDropdownHide() {
- this.$emit('closeDropdown');
- if (!isDropdownVariantSidebar(this.variant)) {
- this.setLabels();
- }
- },
- setSearchKey(value) {
- this.searchKey = value;
- },
- setFocus() {
- this.$refs.header.focusInput();
- },
- hideDropdown() {
- this.$refs.dropdown.hide();
- },
- showDropdown() {
- this.$refs.dropdown.show();
- },
- clearSearch() {
- if (!this.allowMultiselect || this.isStandalone) {
- return;
- }
- this.searchKey = '';
- this.setFocus();
- },
- selectFirstItem() {
- this.$refs.dropdownContentsView.selectFirstItem();
- },
- },
-};
-</script>
-
-<template>
- <gl-dropdown
- ref="dropdown"
- :text="buttonText"
- class="gl-w-full"
- block
- data-testid="labels-select-dropdown-contents"
- data-qa-selector="labels_dropdown_content"
- @hide="handleDropdownHide"
- @shown="setFocus"
- >
- <template #header>
- <dropdown-header
- ref="header"
- :search-key="searchKey"
- :labels-create-title="labelsCreateTitle"
- :labels-list-title="labelsListTitle"
- :show-dropdown-contents-create-view="showDropdownContentsCreateView"
- :is-standalone="isStandalone"
- @toggleDropdownContentsCreateView="toggleDropdownContent"
- @closeDropdown="hideDropdown"
- @input="debouncedSearchKeyUpdate"
- @searchEnter="selectFirstItem"
- />
- </template>
- <template #default>
- <component
- :is="dropdownContentsView"
- ref="dropdownContentsView"
- v-model="localSelectedLabels"
- :search-key="searchKey"
- :allow-multiselect="allowMultiselect"
- :full-path="fullPath"
- :workspace-type="workspaceType"
- :attr-workspace-path="attrWorkspacePath"
- :label-create-type="labelCreateType"
- @hideCreateView="toggleDropdownContent"
- @input="clearSearch"
- />
- </template>
- <template #footer>
- <dropdown-footer
- v-if="showDropdownFooter"
- :footer-create-label-title="footerCreateLabelTitle"
- :footer-manage-label-title="footerManageLabelTitle"
- @toggleDropdownContentsCreateView="toggleDropdownContent"
- />
- </template>
- </gl-dropdown>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue
deleted file mode 100644
index ce93ad216ec..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue
+++ /dev/null
@@ -1,200 +0,0 @@
-<script>
-import {
- GlAlert,
- GlTooltipDirective,
- GlButton,
- GlFormInput,
- GlLink,
- GlLoadingIcon,
-} from '@gitlab/ui';
-import produce from 'immer';
-import { createAlert } from '~/flash';
-import { __ } from '~/locale';
-import { workspaceLabelsQueries } from '~/sidebar/constants';
-import createLabelMutation from './graphql/create_label.mutation.graphql';
-import { LabelType } from './constants';
-
-const errorMessage = __('Error creating label.');
-
-export default {
- components: {
- GlAlert,
- GlButton,
- GlFormInput,
- GlLink,
- GlLoadingIcon,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- fullPath: {
- type: String,
- required: true,
- },
- attrWorkspacePath: {
- type: String,
- required: true,
- },
- labelCreateType: {
- type: String,
- required: true,
- },
- workspaceType: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- labelTitle: '',
- selectedColor: '',
- labelCreateInProgress: false,
- error: undefined,
- };
- },
- computed: {
- disableCreate() {
- return !this.labelTitle.length || !this.selectedColor.length || this.labelCreateInProgress;
- },
- suggestedColors() {
- const colorsMap = gon.suggested_label_colors;
- return Object.keys(colorsMap).map((color) => ({ [color]: colorsMap[color] }));
- },
- mutationVariables() {
- const attributePath = this.labelCreateType === LabelType.group ? 'groupPath' : 'projectPath';
-
- return {
- title: this.labelTitle,
- color: this.selectedColor,
- [attributePath]: this.attrWorkspacePath,
- };
- },
- },
- methods: {
- getColorCode(color) {
- return Object.keys(color).pop();
- },
- getColorName(color) {
- return Object.values(color).pop();
- },
- handleColorClick(color) {
- this.selectedColor = this.getColorCode(color);
- },
- updateLabelsInCache(store, label) {
- const { query } = workspaceLabelsQueries[this.workspaceType];
-
- const sourceData = store.readQuery({
- query,
- variables: { fullPath: this.fullPath, searchTerm: '' },
- });
-
- const collator = new Intl.Collator('en');
- const data = produce(sourceData, (draftData) => {
- const { nodes } = draftData.workspace.labels;
- nodes.push(label);
- nodes.sort((a, b) => collator.compare(a.title, b.title));
- });
-
- store.writeQuery({
- query,
- variables: { fullPath: this.fullPath, searchTerm: '' },
- data,
- });
- },
- async createLabel() {
- this.labelCreateInProgress = true;
- try {
- const {
- data: { labelCreate },
- } = await this.$apollo.mutate({
- mutation: createLabelMutation,
- variables: this.mutationVariables,
- update: (
- store,
- {
- data: {
- labelCreate: { label },
- },
- },
- ) => {
- if (label) {
- this.updateLabelsInCache(store, label);
- }
- },
- });
- if (labelCreate.errors.length) {
- [this.error] = labelCreate.errors;
- } else {
- this.$emit('hideCreateView');
- }
- } catch {
- createAlert({ message: errorMessage });
- }
- this.labelCreateInProgress = false;
- },
- },
-};
-</script>
-
-<template>
- <div class="labels-select-contents-create js-labels-create">
- <div class="dropdown-input">
- <gl-alert v-if="error" variant="danger" :dismissible="false" class="gl-mt-3">
- {{ error }}
- </gl-alert>
- <gl-form-input
- v-model.trim="labelTitle"
- class="gl-mt-3"
- :placeholder="__('Name new label')"
- :autofocus="true"
- data-testid="label-title-input"
- />
- </div>
- <div class="dropdown-content gl-px-3">
- <div class="suggest-colors suggest-colors-dropdown gl-mt-0! gl-mb-3! gl-mb-0">
- <gl-link
- v-for="(color, index) in suggestedColors"
- :key="index"
- v-gl-tooltip:tooltipcontainer
- :style="{ backgroundColor: getColorCode(color) }"
- :title="getColorName(color)"
- @click.prevent="handleColorClick(color)"
- />
- </div>
- <div class="color-input-container gl-display-flex">
- <span
- class="dropdown-label-color-preview gl-relative gl-display-inline-block"
- data-testid="selected-color"
- :style="{ backgroundColor: selectedColor }"
- ></span>
- <gl-form-input
- v-model.trim="selectedColor"
- class="gl-rounded-top-left-none gl-rounded-bottom-left-none gl-mb-2"
- :placeholder="__('Use custom color #FF0000')"
- data-testid="selected-color-text"
- />
- </div>
- </div>
- <div class="dropdown-actions gl-display-flex gl-justify-content-space-between gl-pt-3 gl-px-3">
- <gl-button
- :disabled="disableCreate"
- category="primary"
- variant="confirm"
- class="gl-display-flex gl-align-items-center"
- data-testid="create-button"
- @click="createLabel"
- >
- <gl-loading-icon v-if="labelCreateInProgress" size="sm" :inline="true" class="mr-1" />
- {{ __('Create') }}
- </gl-button>
- <gl-button
- class="js-btn-cancel-create"
- data-testid="cancel-button"
- @click.stop="$emit('hideCreateView')"
- >
- {{ __('Cancel') }}
- </gl-button>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue
deleted file mode 100644
index 1d854505d11..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue
+++ /dev/null
@@ -1,177 +0,0 @@
-<script>
-import { GlDropdownForm, GlDropdownItem, GlLoadingIcon, GlIntersectionObserver } from '@gitlab/ui';
-import fuzzaldrinPlus from 'fuzzaldrin-plus';
-import { createAlert } from '~/flash';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { __ } from '~/locale';
-import { workspaceLabelsQueries } from '~/sidebar/constants';
-import LabelItem from './label_item.vue';
-
-export default {
- components: {
- GlDropdownForm,
- GlDropdownItem,
- GlLoadingIcon,
- GlIntersectionObserver,
- LabelItem,
- },
- model: {
- prop: 'localSelectedLabels',
- },
- props: {
- allowMultiselect: {
- type: Boolean,
- required: true,
- },
- localSelectedLabels: {
- type: Array,
- required: true,
- },
- fullPath: {
- type: String,
- required: true,
- },
- searchKey: {
- type: String,
- required: true,
- },
- workspaceType: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- labels: [],
- isVisible: false,
- };
- },
- apollo: {
- labels: {
- query() {
- return workspaceLabelsQueries[this.workspaceType].query;
- },
- variables() {
- return {
- fullPath: this.fullPath,
- searchTerm: this.searchKey,
- };
- },
- skip() {
- return this.searchKey.length === 1 || !this.isVisible;
- },
- update: (data) => data.workspace?.labels?.nodes || [],
- error() {
- createAlert({ message: __('Error fetching labels.') });
- },
- },
- },
- computed: {
- labelsFetchInProgress() {
- return this.$apollo.queries.labels.loading;
- },
- localSelectedLabelsIds() {
- return this.localSelectedLabels.map((label) => getIdFromGraphQLId(label.id));
- },
- visibleLabels() {
- if (this.searchKey) {
- return fuzzaldrinPlus.filter(this.labels, this.searchKey, {
- key: ['title'],
- });
- }
- return this.labels;
- },
- showNoMatchingResultsMessage() {
- return Boolean(this.searchKey) && this.visibleLabels.length === 0;
- },
- shouldHighlightFirstItem() {
- return this.searchKey !== '' && this.visibleLabels.length > 0;
- },
- },
- methods: {
- isLabelSelected(label) {
- return this.localSelectedLabelsIds.includes(getIdFromGraphQLId(label.id));
- },
- /**
- * This method scrolls item from dropdown into
- * the view if it is off the viewable area of the
- * container.
- */
- scrollIntoViewIfNeeded() {
- const highlightedLabel = this.$refs.labelsListContainer.querySelector('.is-focused');
-
- if (highlightedLabel) {
- const container = this.$refs.labelsListContainer.getBoundingClientRect();
- const label = highlightedLabel.getBoundingClientRect();
-
- if (label.bottom > container.bottom) {
- this.$refs.labelsListContainer.scrollTop += label.bottom - container.bottom;
- } else if (label.top < container.top) {
- this.$refs.labelsListContainer.scrollTop -= container.top - label.top;
- }
- }
- },
- updateSelectedLabels(label) {
- let labels;
- if (this.isLabelSelected(label)) {
- labels = this.localSelectedLabels.filter(
- ({ id }) => id !== getIdFromGraphQLId(label.id) && id !== label.id,
- );
- } else {
- labels = [...this.localSelectedLabels, label];
- }
- this.$emit('input', labels);
- },
- handleLabelClick(label) {
- this.updateSelectedLabels(label);
- if (!this.allowMultiselect) {
- this.$emit('closeDropdown', this.localSelectedLabels);
- }
- },
- onDropdownAppear() {
- this.isVisible = true;
- },
- selectFirstItem() {
- if (this.shouldHighlightFirstItem) {
- this.handleLabelClick(this.visibleLabels[0]);
- }
- },
- },
-};
-</script>
-
-<template>
- <gl-intersection-observer @appear="onDropdownAppear">
- <gl-dropdown-form class="labels-select-contents-list js-labels-list">
- <div ref="labelsListContainer" data-testid="dropdown-content">
- <gl-loading-icon
- v-if="labelsFetchInProgress"
- class="labels-fetch-loading gl-align-items-center gl-w-full gl-h-full gl-mb-3"
- size="lg"
- />
- <template v-else>
- <gl-dropdown-item
- v-for="(label, index) in visibleLabels"
- :key="label.id"
- :is-checked="isLabelSelected(label)"
- is-check-centered
- is-check-item
- :active="shouldHighlightFirstItem && index === 0"
- active-class="is-focused"
- data-testid="labels-list"
- @click.native.capture.stop="handleLabelClick(label)"
- >
- <label-item :label="label" />
- </gl-dropdown-item>
- <gl-dropdown-item
- v-show="showNoMatchingResultsMessage"
- class="gl-p-3 gl-text-center"
- data-testid="no-results"
- >
- {{ __('No matching results') }}
- </gl-dropdown-item>
- </template>
- </div>
- </gl-dropdown-form>
- </gl-intersection-observer>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue
deleted file mode 100644
index 2c27a69d587..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue
+++ /dev/null
@@ -1,406 +0,0 @@
-<script>
-import { debounce } from 'lodash';
-import issuableLabelsSubscription from 'ee_else_ce/sidebar/queries/issuable_labels.subscription.graphql';
-import { MutationOperationMode, getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { createAlert } from '~/flash';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import { IssuableType } from '~/issues/constants';
-
-import { __ } from '~/locale';
-import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
-import { issuableLabelsQueries } from '~/sidebar/constants';
-import { DEBOUNCE_DROPDOWN_DELAY, DropdownVariant } from './constants';
-import DropdownContents from './dropdown_contents.vue';
-import DropdownValue from './dropdown_value.vue';
-import {
- isDropdownVariantSidebar,
- isDropdownVariantStandalone,
- isDropdownVariantEmbedded,
-} from './utils';
-
-export default {
- components: {
- DropdownValue,
- DropdownContents,
- SidebarEditableItem,
- },
- mixins: [glFeatureFlagsMixin()],
- inject: {
- allowLabelEdit: {
- default: false,
- },
- },
- props: {
- iid: {
- type: String,
- required: false,
- default: '',
- },
- fullPath: {
- type: String,
- required: true,
- },
- allowLabelRemove: {
- type: Boolean,
- required: false,
- default: false,
- },
- allowMultiselect: {
- type: Boolean,
- required: false,
- default: false,
- },
- variant: {
- type: String,
- required: false,
- default: DropdownVariant.Sidebar,
- },
- labelsFilterBasePath: {
- type: String,
- required: false,
- default: '',
- },
- labelsFilterParam: {
- type: String,
- required: false,
- default: 'label_name',
- },
- dropdownButtonText: {
- type: String,
- required: false,
- default: __('Label'),
- },
- labelsListTitle: {
- type: String,
- required: false,
- default: __('Assign labels'),
- },
- labelsCreateTitle: {
- type: String,
- required: false,
- default: __('Create group label'),
- },
- footerCreateLabelTitle: {
- type: String,
- required: false,
- default: __('Create group label'),
- },
- footerManageLabelTitle: {
- type: String,
- required: false,
- default: __('Manage group labels'),
- },
- issuableType: {
- type: String,
- required: true,
- },
- workspaceType: {
- type: String,
- required: true,
- },
- attrWorkspacePath: {
- type: String,
- required: true,
- },
- labelCreateType: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- contentIsOnViewport: true,
- issuable: null,
- labelsSelectInProgress: false,
- oldIid: null,
- sidebarExpandedOnClick: false,
- };
- },
- computed: {
- isLoading() {
- return this.labelsSelectInProgress || this.$apollo.queries.issuable.loading;
- },
- issuableLabelIds() {
- return this.issuableLabels.map((label) => label.id);
- },
- issuableLabels() {
- return this.issuable?.labels.nodes || [];
- },
- issuableId() {
- return this.issuable?.id;
- },
- },
- apollo: {
- issuable: {
- query() {
- return issuableLabelsQueries[this.issuableType].issuableQuery;
- },
- skip() {
- return !isDropdownVariantSidebar(this.variant);
- },
- variables() {
- return {
- iid: this.iid,
- fullPath: this.fullPath,
- };
- },
- update(data) {
- return data.workspace?.issuable;
- },
- error() {
- createAlert({ message: __('Error fetching labels.') });
- },
- subscribeToMore: {
- document() {
- return issuableLabelsSubscription;
- },
- variables() {
- return {
- issuableId: this.issuableId,
- };
- },
- skip() {
- return !this.issuableId || !this.isDropdownVariantSidebar;
- },
- updateQuery(
- _,
- {
- subscriptionData: {
- data: { issuableLabelsUpdated },
- },
- },
- ) {
- if (issuableLabelsUpdated) {
- const {
- id,
- labels: { nodes },
- } = issuableLabelsUpdated;
- this.$emit('updateSelectedLabels', { id, labels: nodes });
- }
- },
- },
- },
- },
- watch: {
- iid(_, oldVal) {
- this.oldIid = oldVal;
- },
- },
- mounted() {
- document.addEventListener('toggleSidebarRevealLabelsDropdown', this.handleCollapsedValueClick);
- },
- beforeDestroy() {
- document.removeEventListener(
- 'toggleSidebarRevealLabelsDropdown',
- this.handleCollapsedValueClick,
- );
- },
- methods: {
- handleDropdownClose(labels) {
- if (this.iid !== '') {
- this.updateSelectedLabels(this.getUpdateVariables(labels));
- } else {
- this.$emit('updateSelectedLabels', { labels });
- }
-
- this.collapseEditableItem();
- },
- collapseEditableItem() {
- this.$refs.editable?.collapse();
- if (this.sidebarExpandedOnClick) {
- this.sidebarExpandedOnClick = false;
- this.$emit('toggleCollapse');
- }
- },
- handleCollapsedValueClick() {
- this.sidebarExpandedOnClick = true;
- this.$emit('toggleCollapse');
- debounce(() => {
- this.$refs.editable.toggle();
- this.$refs.dropdownContents.showDropdown();
- }, DEBOUNCE_DROPDOWN_DELAY)();
- },
- getUpdateVariables(labels) {
- let labelIds = [];
-
- labelIds = labels.map(({ id }) => id);
- const currentIid = this.oldIid || this.iid;
-
- const updateVariables = {
- iid: currentIid,
- projectPath: this.fullPath,
- labelIds,
- };
-
- switch (this.issuableType) {
- case IssuableType.Issue:
- return updateVariables;
- case IssuableType.MergeRequest:
- return {
- ...updateVariables,
- operationMode: MutationOperationMode.Replace,
- };
- case IssuableType.Epic:
- return {
- iid: currentIid,
- groupPath: this.fullPath,
- addLabelIds: labelIds.map((id) => getIdFromGraphQLId(id)),
- removeLabelIds: this.issuableLabelIds
- .filter((id) => !labelIds.includes(id))
- .map((id) => getIdFromGraphQLId(id)),
- };
- default:
- return {};
- }
- },
- updateSelectedLabels(inputVariables) {
- this.labelsSelectInProgress = true;
-
- this.$apollo
- .mutate({
- mutation: issuableLabelsQueries[this.issuableType].mutation,
- variables: { input: inputVariables },
- })
- .then(({ data }) => {
- if (data.updateIssuableLabels?.errors?.length) {
- throw new Error();
- }
-
- this.$emit('updateSelectedLabels', {
- id: data.updateIssuableLabels?.issuable?.id,
- labels: data.updateIssuableLabels?.issuable?.labels?.nodes,
- });
- })
- .catch((error) =>
- createAlert({
- message: __('An error occurred while updating labels.'),
- captureError: true,
- error,
- }),
- )
- .finally(() => {
- this.labelsSelectInProgress = false;
- });
- },
- getRemoveVariables(labelId) {
- const removeVariables = {
- iid: this.iid,
- projectPath: this.fullPath,
- };
-
- switch (this.issuableType) {
- case IssuableType.Issue:
- return {
- ...removeVariables,
- removeLabelIds: [labelId],
- };
- case IssuableType.MergeRequest:
- return {
- ...removeVariables,
- labelIds: [labelId],
- operationMode: MutationOperationMode.Remove,
- };
- case IssuableType.Epic:
- return {
- iid: this.iid,
- removeLabelIds: [getIdFromGraphQLId(labelId)],
- groupPath: this.fullPath,
- };
- default:
- return {};
- }
- },
- handleLabelRemove(labelId) {
- this.updateSelectedLabels(this.getRemoveVariables(labelId));
- this.$emit('onLabelRemove', labelId);
- },
- isDropdownVariantSidebar,
- isDropdownVariantStandalone,
- isDropdownVariantEmbedded,
- },
-};
-</script>
-
-<template>
- <div
- class="labels-select-wrapper gl-relative"
- :class="{
- 'is-standalone': isDropdownVariantStandalone(variant),
- 'is-embedded': isDropdownVariantEmbedded(variant),
- }"
- data-testid="sidebar-labels"
- data-qa-selector="labels_block"
- >
- <template v-if="isDropdownVariantSidebar(variant)">
- <sidebar-editable-item
- ref="editable"
- :title="__('Labels')"
- :loading="isLoading"
- :can-edit="allowLabelEdit"
- @open="oldIid = null"
- >
- <template #collapsed>
- <dropdown-value
- :disable-labels="labelsSelectInProgress"
- :selected-labels="issuableLabels"
- :allow-label-remove="allowLabelRemove"
- :labels-filter-base-path="labelsFilterBasePath"
- :labels-filter-param="labelsFilterParam"
- @onLabelRemove="handleLabelRemove"
- @onCollapsedValueClick="handleCollapsedValueClick"
- >
- <slot></slot>
- </dropdown-value>
- </template>
- <template #default="{ edit }">
- <dropdown-value
- :disable-labels="labelsSelectInProgress"
- :selected-labels="issuableLabels"
- :allow-label-remove="allowLabelRemove"
- :labels-filter-base-path="labelsFilterBasePath"
- :labels-filter-param="labelsFilterParam"
- class="gl-mb-2"
- @onLabelRemove="handleLabelRemove"
- >
- <slot></slot>
- </dropdown-value>
- <dropdown-contents
- ref="dropdownContents"
- :dropdown-button-text="dropdownButtonText"
- :allow-multiselect="allowMultiselect"
- :labels-list-title="labelsListTitle"
- :footer-create-label-title="footerCreateLabelTitle"
- :footer-manage-label-title="footerManageLabelTitle"
- :labels-create-title="labelsCreateTitle"
- :selected-labels="issuableLabels"
- :variant="variant"
- :is-visible="edit"
- :full-path="fullPath"
- :workspace-type="workspaceType"
- :attr-workspace-path="attrWorkspacePath"
- :label-create-type="labelCreateType"
- @setLabels="handleDropdownClose"
- @closeDropdown="collapseEditableItem"
- />
- </template>
- </sidebar-editable-item>
- </template>
- <dropdown-contents
- v-else
- ref="dropdownContents"
- :allow-multiselect="allowMultiselect"
- :dropdown-button-text="dropdownButtonText"
- :labels-list-title="labelsListTitle"
- :footer-create-label-title="footerCreateLabelTitle"
- :footer-manage-label-title="footerManageLabelTitle"
- :labels-create-title="labelsCreateTitle"
- :selected-labels="issuableLabels"
- :variant="variant"
- :full-path="fullPath"
- :workspace-type="workspaceType"
- :attr-workspace-path="attrWorkspacePath"
- :label-create-type="labelCreateType"
- @setLabels="handleDropdownClose"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql
deleted file mode 100644
index bb6c7181e5c..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql
+++ /dev/null
@@ -1,21 +0,0 @@
-#import "~/graphql_shared/fragments/user.fragment.graphql"
-#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
-
-query alertAssignees(
- $domain: AlertManagementDomainFilter = threat_monitoring
- $fullPath: ID!
- $iid: String!
-) {
- workspace: project(fullPath: $fullPath) {
- id
- issuable: alertManagementAlert(domain: $domain, iid: $iid) {
- iid
- assignees {
- nodes {
- ...User
- ...UserAvailability
- }
- }
- }
- }
-}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js
deleted file mode 100644
index 465ee9aa0d4..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import TodoButton from './todo_button.vue';
-
-export default {
- component: TodoButton,
- title: 'vue_shared/sidebar/todo_toggle/todo_button',
-};
-
-const Template = (args, { argTypes }) => ({
- components: { TodoButton },
- props: Object.keys(argTypes),
- template: '<todo-button v-bind="$props" v-on="$props" />',
-});
-
-export const Default = Template.bind({});
-Default.argTypes = {
- isTodo: {
- description: 'True if to-do is unresolved (i.e. not "done")',
- control: { type: 'boolean' },
- },
- click: { action: 'clicked' },
-};
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue
deleted file mode 100644
index cdc7422c7df..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue
+++ /dev/null
@@ -1,44 +0,0 @@
-<script>
-import { GlButton } from '@gitlab/ui';
-import { todoLabel, updateGlobalTodoCount } from './utils';
-
-export default {
- components: {
- GlButton,
- },
- props: {
- isTodo: {
- type: Boolean,
- required: false,
- default: true,
- },
- },
- computed: {
- buttonLabel() {
- return todoLabel(this.isTodo);
- },
- },
- methods: {
- incrementGlobalTodoCount() {
- updateGlobalTodoCount(1);
- },
- decrementGlobalTodoCount() {
- updateGlobalTodoCount(-1);
- },
- onToggle(event) {
- if (this.isTodo) {
- this.decrementGlobalTodoCount();
- } else {
- this.incrementGlobalTodoCount();
- }
- this.$emit('click', event);
- },
- },
-};
-</script>
-
-<template>
- <gl-button v-bind="$attrs" :aria-label="buttonLabel" @click="onToggle($event)">
- {{ buttonLabel }}
- </gl-button>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js
deleted file mode 100644
index 098ab72dfb5..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { __ } from '~/locale';
-
-export const todoLabel = (hasTodo) => {
- return hasTodo ? __('Mark as done') : __('Add a to do');
-};
-
-export const updateGlobalTodoCount = (additionalTodoCount) => {
- const countContainer = document.querySelector('.js-todos-count');
-
- if (countContainer === null) return;
-
- const currentCount = parseInt(countContainer.innerText, 10);
-
- const todoToggleEvent = new CustomEvent('todo:toggle', {
- detail: {
- count: Math.max(currentCount + additionalTodoCount, 0),
- },
- });
-
- document.dispatchEvent(todoToggleEvent);
-};
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue
index a2d8b7cbd15..28a16cd846a 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue
@@ -1,6 +1,6 @@
<script>
-import { GlIntersectionObserver, GlSafeHtmlDirective } from '@gitlab/ui';
-import { scrollToElement } from '~/lib/utils/common_utils';
+import { GlIntersectionObserver } from '@gitlab/ui';
+import LineHighlighter from '~/blob/line_highlighter';
import ChunkLine from './chunk_line.vue';
/*
@@ -20,9 +20,6 @@ export default {
ChunkLine,
GlIntersectionObserver,
},
- directives: {
- SafeHtml: GlSafeHtmlDirective,
- },
props: {
isFirstChunk: {
type: Boolean,
@@ -84,12 +81,14 @@ export default {
return;
}
- window.requestIdleCallback(() => {
+ window.requestIdleCallback(async () => {
this.isLoading = false;
const { hash } = this.$route;
if (hash && this.totalChunks > 0 && this.totalChunks === this.chunkIndex + 1) {
// when the last chunk is loaded scroll to the hash
- scrollToElement(hash, { behavior: 'auto' });
+ await this.$nextTick();
+ const lineHighlighter = new LineHighlighter({ scrollBehavior: 'auto' });
+ lineHighlighter.highlightHash(hash);
}
});
},
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue
index 0bf19f83d86..ce6741f33b1 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue
@@ -1,11 +1,11 @@
<script>
-import { GlSafeHtmlDirective } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { getPageParamValue, getPageSearchString } from '~/blob/utils';
export default {
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [glFeatureFlagMixin()],
props: {
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/plugins/link_dependencies.js b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/link_dependencies.js
index fca2616f069..cd15916851c 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/plugins/link_dependencies.js
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/link_dependencies.js
@@ -4,6 +4,7 @@ import godepsJsonLinker from './utils/godeps_json_linker';
import gemfileLinker from './utils/gemfile_linker';
import podspecJsonLinker from './utils/podspec_json_linker';
import composerJsonLinker from './utils/composer_json_linker';
+import goSumLinker from './utils/go_sum_linker';
const DEPENDENCY_LINKERS = {
package_json: packageJsonLinker,
@@ -12,6 +13,7 @@ const DEPENDENCY_LINKERS = {
gemfile: gemfileLinker,
podspec_json: podspecJsonLinker,
composer_json: composerJsonLinker,
+ go_sum: goSumLinker,
};
/**
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/plugins/utils/go_sum_linker.js b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/utils/go_sum_linker.js
new file mode 100644
index 00000000000..b290dfa78b9
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/utils/go_sum_linker.js
@@ -0,0 +1,34 @@
+import { createLink } from './dependency_linker_util';
+
+const openTag = '<span class="">';
+const closeTag = '</span>';
+const TAG_URL = 'https://sum.golang.org/lookup/';
+const GO_PACKAGE_URL = 'https://pkg.go.dev/';
+
+const DEPENDENCY_REGEX = new RegExp(
+ /*
+ * Detects dependencies inside of content that is highlighted by Highlight.js
+ * Example: '<span class="">cloud.google.com/Go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=</span>'
+ * Group 1 (packagePath): 'cloud.google.com/Go/bigquery'
+ * Group 2 (version): 'v1.0.1/go.mod'
+ * Group 3 (base64url): 'i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o='
+ */
+ `${openTag}(.*) (v.*) h1:(.*)${closeTag}`,
+ 'gm',
+);
+
+const handleReplace = (packagePath, version, tag) => {
+ const lowercasePath = packagePath.toLowerCase();
+ const packageHref = `${GO_PACKAGE_URL}${lowercasePath}`;
+ const packageLink = createLink(packageHref, packagePath);
+ const tagHref = `${TAG_URL}${lowercasePath}@${version.split('/go.mod')[0]}`;
+ const tagLink = createLink(tagHref, tag);
+
+ return `${openTag}${packageLink} ${version} h1:${tagLink}${closeTag}`;
+};
+
+export default (result) => {
+ return result.value.replace(DEPENDENCY_REGEX, (_, packagePath, version, tag) =>
+ handleReplace(packagePath, version, tag),
+ );
+};
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue
index f621a23734a..0cfee93ce5d 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSafeHtmlDirective, GlLoadingIcon } from '@gitlab/ui';
+import { GlLoadingIcon } from '@gitlab/ui';
import LineHighlighter from '~/blob/line_highlighter';
import eventHub from '~/notes/event_hub';
import languageLoader from '~/content_editor/services/highlight_js_language_loader';
@@ -28,9 +28,6 @@ export default {
GlLoadingIcon,
Chunk,
},
- directives: {
- SafeHtml: GlSafeHtmlDirective,
- },
mixins: [Tracking.mixin()],
props: {
blob: {
diff --git a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
index 80c1fcbacfa..d06bc7b8f98 100644
--- a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
+++ b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
@@ -4,11 +4,11 @@ import {
GlLink,
GlSkeletonLoader,
GlIcon,
- GlSafeHtmlDirective,
GlSprintf,
GlButton,
GlAvatarLabeled,
} from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { glEmojiTag } from '~/emoji';
import { createAlert } from '~/flash';
import { followUser, unfollowUser } from '~/rest_api';
@@ -44,7 +44,7 @@ export default {
GlAvatarLabeled,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [Tracking.mixin()],
props: {
diff --git a/app/assets/javascripts/vue_shared/components/web_ide_link.vue b/app/assets/javascripts/vue_shared/components/web_ide_link.vue
index 6d179b3dc92..383dc27ea5e 100644
--- a/app/assets/javascripts/vue_shared/components/web_ide_link.vue
+++ b/app/assets/javascripts/vue_shared/components/web_ide_link.vue
@@ -1,14 +1,16 @@
<script>
-import { GlModal, GlSprintf, GlLink } from '@gitlab/ui';
+import { GlModal, GlSprintf, GlLink, GlPopover } from '@gitlab/ui';
import { s__, __ } from '~/locale';
+import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
import ActionsButton from '~/vue_shared/components/actions_button.vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import ConfirmForkModal from '~/vue_shared/components/confirm_fork_modal.vue';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-const KEY_EDIT = 'edit';
-const KEY_WEB_IDE = 'webide';
-const KEY_GITPOD = 'gitpod';
-const KEY_PIPELINE_EDITOR = 'pipeline_editor';
+export const KEY_EDIT = 'edit';
+export const KEY_WEB_IDE = 'webide';
+export const KEY_GITPOD = 'gitpod';
+export const KEY_PIPELINE_EDITOR = 'pipeline_editor';
export const i18n = {
modal: {
@@ -25,6 +27,9 @@ export const i18n = {
),
};
+export const PREFERRED_EDITOR_KEY = 'gl-web-ide-button-selected';
+export const PREFERRED_EDITOR_RESET_KEY = 'gl-web-ide-button-selected-reset';
+
export default {
components: {
ActionsButton,
@@ -32,9 +37,12 @@ export default {
GlModal,
GlSprintf,
GlLink,
+ GlPopover,
ConfirmForkModal,
+ UserCalloutDismisser,
},
i18n,
+ mixins: [glFeatureFlagsMixin()],
props: {
isFork: {
type: Boolean,
@@ -131,6 +139,11 @@ export default {
required: false,
default: '',
},
+ webIdePromoPopoverImg: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
data() {
return {
@@ -296,6 +309,12 @@ export default {
},
};
},
+ displayVscodeWebIdeCallout() {
+ return this.glFeatures.vscodeWebIde && !this.showEditButton;
+ },
+ },
+ mounted() {
+ this.resetPreferredEditor();
},
methods: {
select(key) {
@@ -304,41 +323,109 @@ export default {
showModal(dataKey) {
this[dataKey] = true;
},
+ resetPreferredEditor() {
+ if (!this.glFeatures.vscodeWebIde || this.showEditButton) {
+ return;
+ }
+
+ if (localStorage.getItem(PREFERRED_EDITOR_RESET_KEY) === 'true') {
+ return;
+ }
+
+ localStorage.setItem(PREFERRED_EDITOR_KEY, KEY_WEB_IDE);
+ localStorage.setItem(PREFERRED_EDITOR_RESET_KEY, true);
+
+ this.select(KEY_WEB_IDE);
+ },
+ dismissCalloutOnActionClicked(dismiss) {
+ if (this.displayVscodeWebIdeCallout) {
+ dismiss();
+ }
+ },
},
+ webIdeButtonId: 'web-ide-link',
+ PREFERRED_EDITOR_KEY,
};
</script>
<template>
- <div class="gl-sm-ml-3">
- <actions-button
- :actions="actions"
- :selected-key="selection"
- :variant="isBlob ? 'confirm' : 'default'"
- :category="isBlob ? 'primary' : 'secondary'"
- @select="select"
- />
- <local-storage-sync
- storage-key="gl-web-ide-button-selected"
- :value="selection"
- as-string
- @input="select"
- />
- <gl-modal
- v-if="computedShowGitpodButton && !gitpodEnabled"
- v-model="showEnableGitpodModal"
- v-bind="enableGitpodModalProps"
- >
- <gl-sprintf :message="$options.i18n.modal.content">
- <template #link="{ content }">
- <gl-link :href="userPreferencesGitpodPath">{{ content }}</gl-link>
- </template>
- </gl-sprintf>
- </gl-modal>
- <confirm-fork-modal
- v-if="showWebIdeButton || showEditButton"
- v-model="showForkModal"
- :modal-id="forkModalId"
- :fork-path="forkPath"
- />
- </div>
+ <user-callout-dismisser
+ :skip-query="!displayVscodeWebIdeCallout"
+ feature-name="vscode_web_ide_callout"
+ >
+ <template #default="{ dismiss, shouldShowCallout }">
+ <div class="gl-sm-ml-3">
+ <actions-button
+ :id="$options.webIdeButtonId"
+ :actions="actions"
+ :selected-key="selection"
+ :variant="isBlob ? 'confirm' : 'default'"
+ :category="isBlob ? 'primary' : 'secondary'"
+ :show-action-tooltip="!displayVscodeWebIdeCallout || !shouldShowCallout"
+ @select="select"
+ @actionClicked="dismissCalloutOnActionClicked(dismiss)"
+ />
+ <local-storage-sync
+ :storage-key="$options.PREFERRED_EDITOR_KEY"
+ :value="selection"
+ as-string
+ @input="select"
+ />
+ <gl-modal
+ v-if="computedShowGitpodButton && !gitpodEnabled"
+ v-model="showEnableGitpodModal"
+ v-bind="enableGitpodModalProps"
+ >
+ <gl-sprintf :message="$options.i18n.modal.content">
+ <template #link="{ content }">
+ <gl-link :href="userPreferencesGitpodPath">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </gl-modal>
+ <confirm-fork-modal
+ v-if="showWebIdeButton || showEditButton"
+ v-model="showForkModal"
+ :modal-id="forkModalId"
+ :fork-path="forkPath"
+ />
+ <gl-popover
+ v-if="displayVscodeWebIdeCallout"
+ :target="$options.webIdeButtonId"
+ :show="shouldShowCallout"
+ :css-classes="['web-ide-promo-popover']"
+ :boundary-padding="80"
+ show-close-button
+ triggers="manual"
+ @close-button-clicked="dismiss"
+ >
+ <img
+ :src="webIdePromoPopoverImg"
+ class="web-ide-promo-popover-illustration"
+ width="280"
+ height="140"
+ />
+ <div class="gl-mx-2">
+ <h5 class="gl-mt-3 gl-mb-3">{{ __('The new Web IDE') }}</h5>
+ <p>
+ {{
+ __(
+ 'VS Code in your browser. View code and make changes from the same UI as in your local IDE.',
+ )
+ }}
+ </p>
+ <gl-link
+ class="gl-button btn btn-confirm block gl-mb-4 gl-mt-5"
+ variant="confirm"
+ category="primary"
+ target="_blank"
+ :href="webIdeUrl"
+ block
+ >
+ {{ __('Try it out now') }}
+ </gl-link>
+ </div>
+ </gl-popover>
+ </div>
+ </template>
+ </user-callout-dismisser>
</template>
diff --git a/app/assets/javascripts/vue_shared/constants.js b/app/assets/javascripts/vue_shared/constants.js
index a851f84ed2f..2f85a29fb84 100644
--- a/app/assets/javascripts/vue_shared/constants.js
+++ b/app/assets/javascripts/vue_shared/constants.js
@@ -13,7 +13,9 @@ export const SHORT_DATE_FORMAT = 'd mmm, yyyy';
export const ISO_SHORT_FORMAT = 'yyyy-mm-dd';
-export const DATE_FORMATS = [SHORT_DATE_FORMAT, ISO_SHORT_FORMAT];
+export const LONG_DATE_FORMAT_WITH_TZ = 'yyyy-mm-dd HH:MM:ss Z';
+
+export const DATE_FORMATS = [SHORT_DATE_FORMAT, ISO_SHORT_FORMAT, LONG_DATE_FORMAT_WITH_TZ];
const getTimeLabel = (days) => n__('1 day', '%d days', days);
diff --git a/app/assets/javascripts/vue_shared/issuable/create/components/issuable_form.vue b/app/assets/javascripts/vue_shared/issuable/create/components/issuable_form.vue
index 25799171905..2644befc902 100644
--- a/app/assets/javascripts/vue_shared/issuable/create/components/issuable_form.vue
+++ b/app/assets/javascripts/vue_shared/issuable/create/components/issuable_form.vue
@@ -1,8 +1,8 @@
<script>
import { GlForm, GlFormInput, GlFormGroup } from '@gitlab/ui';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
-import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_vue/constants';
-import LabelsSelect from '~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue';
+import { DropdownVariant } from '~/sidebar/components/labels/labels_select_vue/constants';
+import LabelsSelect from '~/sidebar/components/labels/labels_select_vue/labels_select_root.vue';
export default {
LabelSelectVariant: DropdownVariant,
diff --git a/app/assets/javascripts/vue_shared/issuable/create/components/issuable_label_selector.vue b/app/assets/javascripts/vue_shared/issuable/create/components/issuable_label_selector.vue
new file mode 100644
index 00000000000..b3f9c8d9fcd
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/issuable/create/components/issuable_label_selector.vue
@@ -0,0 +1,92 @@
+<script>
+import { GlFormGroup, GlIcon } from '@gitlab/ui';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import LabelsSelect from '~/sidebar/components/labels/labels_select_widget/labels_select_root.vue';
+import { __ } from '~/locale';
+
+export default {
+ components: {
+ GlFormGroup,
+ GlIcon,
+ LabelsSelect,
+ },
+ inject: [
+ 'allowLabelRemove',
+ 'attrWorkspacePath',
+ 'fieldName',
+ 'fullPath',
+ 'labelsFilterBasePath',
+ 'initialLabels',
+ 'issuableType',
+ 'labelType',
+ 'variant',
+ 'workspaceType',
+ ],
+ data() {
+ return {
+ selectedLabels: this.initialLabels || [],
+ };
+ },
+ methods: {
+ handleUpdateSelectedLabels({ labels }) {
+ this.selectedLabels = labels.map((label) => ({ ...label, id: getIdFromGraphQLId(label.id) }));
+ },
+ handleLabelRemove(labelId) {
+ this.selectedLabels = this.selectedLabels.filter((label) => label.id !== labelId);
+ },
+ },
+ i18n: {
+ fieldLabel: __('Labels'),
+ dropdownButtonText: __('Select label'),
+ listTitle: __('Select label'),
+ createTitle: __('Create project label'),
+ manageTitle: __('Manage project labels'),
+ emptySelection: __('None'),
+ },
+};
+</script>
+
+<template>
+ <gl-form-group class="row" label-class="gl-display-none">
+ <label class="col-12 gl-display-flex gl-align-center gl-mb-1">
+ {{ $options.i18n.fieldLabel }}
+ <div class="gl-ml-3">
+ <gl-icon name="labels" />
+ <span class="gl-font-base gl-line-height-24">{{ selectedLabels.length }}</span>
+ </div>
+ </label>
+ <div class="col-12">
+ <div class="issuable-form-select-holder">
+ <input
+ v-for="selectedLabel in selectedLabels"
+ :key="selectedLabel.id"
+ :value="selectedLabel.id"
+ :name="fieldName"
+ type="hidden"
+ />
+ <labels-select
+ class="block labels"
+ :allow-label-remove="allowLabelRemove"
+ :allow-multiselect="true"
+ :show-embedded-labels-list="true"
+ :full-path="fullPath"
+ :attr-workspace-path="attrWorkspacePath"
+ :labels-filter-base-path="labelsFilterBasePath"
+ :dropdown-button-text="$options.i18n.dropdownButtonText"
+ :labels-list-title="$options.i18n.listTitle"
+ :footer-create-label-title="$options.i18n.createTitle"
+ :footer-manage-label-title="$options.i18n.manageTitle"
+ :variant="variant"
+ :workspace-type="workspaceType"
+ :issuable-type="issuableType"
+ :label-create-type="labelType"
+ :selected-labels="selectedLabels"
+ @updateSelectedLabels="handleUpdateSelectedLabels"
+ @onLabelRemove="handleLabelRemove"
+ >
+ {{ $options.i18n.emptySelection }}
+ </labels-select>
+ </div>
+ </div>
+ </gl-form-group>
+</template>
diff --git a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue
index 30b7b073ac3..5b303b9a314 100644
--- a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue
+++ b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue
@@ -318,8 +318,8 @@ export default {
<slot name="statistics"></slot>
<li
v-if="showDiscussions"
- data-testid="issuable-discussions"
- class="issuable-comments gl-display-none gl-sm-display-block"
+ class="gl-display-none gl-sm-display-block"
+ data-testid="issuable-comments"
>
<gl-link
v-gl-tooltip.top
diff --git a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue
index dd3d7c8f4d6..5b6c5bf6e03 100644
--- a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue
+++ b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue
@@ -331,6 +331,7 @@ export default {
<slot name="sidebar-items" :checked-issuables="bulkEditIssuables"></slot>
</template>
</issuable-bulk-edit-sidebar>
+ <slot name="list-body"></slot>
<ul v-if="issuablesLoading" class="content-list">
<li v-for="n in skeletonItemCount" :key="n" class="issue gl-px-5! gl-py-5!">
<gl-skeleton-loader />
diff --git a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_description.vue b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_description.vue
index d4e9120ff17..ce1851ab873 100644
--- a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_description.vue
+++ b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_description.vue
@@ -1,7 +1,6 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
-import $ from 'jquery';
-import '~/behaviors/markdown/render_gfm';
+import SafeHtml from '~/vue_shared/directives/safe_html';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
export default {
directives: {
@@ -26,12 +25,7 @@ export default {
},
},
mounted() {
- this.renderGFM();
- },
- methods: {
- renderGFM() {
- $(this.$refs.gfmContainer).renderGFM();
- },
+ renderGFM(this.$refs.gfmContainer);
},
};
</script>
diff --git a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_title.vue b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_title.vue
index 35124bd15d2..fd94245b7c9 100644
--- a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_title.vue
+++ b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_title.vue
@@ -1,12 +1,6 @@
<script>
-import {
- GlIcon,
- GlBadge,
- GlButton,
- GlIntersectionObserver,
- GlTooltipDirective,
- GlSafeHtmlDirective as SafeHtml,
-} from '@gitlab/ui';
+import { GlIcon, GlBadge, GlButton, GlIntersectionObserver, GlTooltipDirective } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { __ } from '~/locale';
import { IssuableStates } from '~/vue_shared/issuable/list/constants';
diff --git a/app/assets/javascripts/vue_shared/new_namespace/components/legacy_container.vue b/app/assets/javascripts/vue_shared/new_namespace/components/legacy_container.vue
index e42720bf1db..ae40076ca96 100644
--- a/app/assets/javascripts/vue_shared/new_namespace/components/legacy_container.vue
+++ b/app/assets/javascripts/vue_shared/new_namespace/components/legacy_container.vue
@@ -1,4 +1,6 @@
<script>
+import projectNew from '~/projects/project_new';
+
export default {
inheritAttrs: false,
props: {
@@ -16,6 +18,7 @@ export default {
this.source = legacyEntry.parentNode;
this.$el.appendChild(legacyEntry);
legacyEntry.classList.add('active');
+ projectNew.bindEvents();
}
},
diff --git a/app/assets/javascripts/vue_shared/new_namespace/components/welcome.vue b/app/assets/javascripts/vue_shared/new_namespace/components/welcome.vue
index 5cd2018bb8c..b6a459f21e0 100644
--- a/app/assets/javascripts/vue_shared/new_namespace/components/welcome.vue
+++ b/app/assets/javascripts/vue_shared/new_namespace/components/welcome.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import Tracking from '~/tracking';
export default {
diff --git a/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue b/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue
index 624ae7027d5..318adec2319 100644
--- a/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue
+++ b/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue
@@ -1,5 +1,6 @@
<script>
-import { GlBreadcrumb, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlBreadcrumb, GlIcon } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import NewTopLevelGroupAlert from '~/groups/components/new_top_level_group_alert.vue';
import LegacyContainer from './components/legacy_container.vue';
diff --git a/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue b/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue
index 0e1975e1c09..b739baad5d7 100644
--- a/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue
+++ b/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue
@@ -2,8 +2,8 @@
import { mapActions, mapGetters } from 'vuex';
import { createAlert } from '~/flash';
import { s__ } from '~/locale';
-import ReportSection from '~/reports/components/report_section.vue';
-import { ERROR, SLOT_SUCCESS, SLOT_LOADING, SLOT_ERROR } from '~/reports/constants';
+import ReportSection from '~/ci/reports/components/report_section.vue';
+import { ERROR, SLOT_SUCCESS, SLOT_LOADING, SLOT_ERROR } from '~/ci/reports/constants';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import HelpIcon from './components/help_icon.vue';
import SecurityReportDownloadDropdown from './components/security_report_download_dropdown.vue';
diff --git a/app/assets/javascripts/vue_shared/security_reports/store/getters.js b/app/assets/javascripts/vue_shared/security_reports/store/getters.js
index 08f6bcca15b..c274f531139 100644
--- a/app/assets/javascripts/vue_shared/security_reports/store/getters.js
+++ b/app/assets/javascripts/vue_shared/security_reports/store/getters.js
@@ -1,5 +1,5 @@
import { s__, sprintf } from '~/locale';
-import { LOADING, ERROR, SUCCESS } from '~/reports/constants';
+import { LOADING, ERROR, SUCCESS } from '~/ci/reports/constants';
import { TRANSLATION_IS_LOADING } from './messages';
import { countVulnerabilities, groupedTextBuilder } from './utils';
diff --git a/app/assets/javascripts/vue_shared/security_reports/store/utils.js b/app/assets/javascripts/vue_shared/security_reports/store/utils.js
index a6628fa0f9f..f3cb5fc16f0 100644
--- a/app/assets/javascripts/vue_shared/security_reports/store/utils.js
+++ b/app/assets/javascripts/vue_shared/security_reports/store/utils.js
@@ -29,7 +29,13 @@ export const fetchDiffData = (state, endpoint, category) => {
*/
export const enrichVulnerabilityWithFeedback = (vulnerability, feedback = []) =>
feedback
- .filter((fb) => fb.project_fingerprint === vulnerability.project_fingerprint)
+ .filter((fb) =>
+ // Some records still have a `finding_uuid` with null, we need to fallback to using `project_fingerprint` in those cases. Once all entries have been fixed, we can remove the fallback.
+ // related epic: https://gitlab.com/groups/gitlab-org/-/epics/2791
+ fb.finding_uuid !== null
+ ? fb.finding_uuid === vulnerability.finding_uuid
+ : fb.project_fingerprint === vulnerability.project_fingerprint,
+ )
.reduce((vuln, fb) => {
if (fb.feedback_type === FEEDBACK_TYPE_DISMISSAL) {
return {
diff --git a/app/assets/javascripts/webhooks/components/push_events.vue b/app/assets/javascripts/webhooks/components/push_events.vue
index 677f06314e0..91d7e21500a 100644
--- a/app/assets/javascripts/webhooks/components/push_events.vue
+++ b/app/assets/javascripts/webhooks/components/push_events.vue
@@ -33,7 +33,7 @@ export default {
<template>
<div>
- <gl-form-checkbox v-model="pushEventsData">{{ s__('Webhooks|Push events') }}</gl-form-checkbox>
+ <gl-form-checkbox v-model="pushEventsData">{{ __('Push events') }}</gl-form-checkbox>
<input type="hidden" :value="pushEventsData" name="hook[push_events]" />
<div v-if="pushEventsData" class="gl-pl-6">
diff --git a/app/assets/javascripts/webhooks/constants.js b/app/assets/javascripts/webhooks/constants.js
index 6710a418117..96632b47e6b 100644
--- a/app/assets/javascripts/webhooks/constants.js
+++ b/app/assets/javascripts/webhooks/constants.js
@@ -7,13 +7,13 @@ export const BRANCH_FILTER_REGEX = 'regex';
export const WILDCARD_CODE_STABLE = '*-stable';
export const WILDCARD_CODE_PRODUCTION = 'production/*';
-export const REGEX_CODE = '(feature|hotfix)/*';
+export const REGEX_CODE = '^(feature|hotfix)/';
export const descriptionText = {
[BRANCH_FILTER_WILDCARD]: s__(
'Webhooks|Wildcards such as %{WILDCARD_CODE_STABLE} or %{WILDCARD_CODE_PRODUCTION} are supported.',
),
- [BRANCH_FILTER_REGEX]: s__('Webhooks|Regex such as %{REGEX_CODE} is supported.'),
+ [BRANCH_FILTER_REGEX]: s__('Webhooks|Regular expressions such as %{REGEX_CODE} are supported.'),
};
export const MASK_ITEM_VALUE_HIDDEN = '************';
diff --git a/app/assets/javascripts/whats_new/components/feature.vue b/app/assets/javascripts/whats_new/components/feature.vue
index c954a86e593..044a6db6d93 100644
--- a/app/assets/javascripts/whats_new/components/feature.vue
+++ b/app/assets/javascripts/whats_new/components/feature.vue
@@ -1,5 +1,6 @@
<script>
-import { GlBadge, GlIcon, GlLink, GlSafeHtmlDirective, GlButton } from '@gitlab/ui';
+import { GlBadge, GlIcon, GlLink, GlButton } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { dateInWords, isValidDate } from '~/lib/utils/datetime_utility';
export default {
@@ -10,7 +11,7 @@ export default {
GlButton,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
feature: {
diff --git a/app/assets/javascripts/work_items/components/notes/system_note.vue b/app/assets/javascripts/work_items/components/notes/system_note.vue
new file mode 100644
index 00000000000..92a2fcaf1df
--- /dev/null
+++ b/app/assets/javascripts/work_items/components/notes/system_note.vue
@@ -0,0 +1,229 @@
+<script>
+/**
+ * Common component to render a system note, icon and user information.
+ *
+ * This component need not be used with any store neither has any vuex dependency
+ *
+ * @example
+ * <system-note
+ * :note="{
+ * id: String,
+ * author: Object,
+ * createdAt: String,
+ * bodyHtml: String,
+ * systemNoteIconName: String
+ * }"
+ * />
+ */
+import { GlButton, GlSkeletonLoader, GlTooltipDirective, GlIcon } from '@gitlab/ui';
+import $ from 'jquery';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
+import SafeHtml from '~/vue_shared/directives/safe_html';
+import descriptionVersionHistoryMixin from 'ee_else_ce/notes/mixins/description_version_history';
+import axios from '~/lib/utils/axios_utils';
+import { getLocationHash } from '~/lib/utils/url_utility';
+import { __ } from '~/locale';
+import NoteHeader from '~/notes/components/note_header.vue';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
+
+const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
+
+export default {
+ i18n: {
+ deleteButtonLabel: __('Remove description history'),
+ },
+ name: 'SystemNote',
+ components: {
+ GlIcon,
+ NoteHeader,
+ TimelineEntryItem,
+ GlButton,
+ GlSkeletonLoader,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ SafeHtml,
+ },
+ mixins: [descriptionVersionHistoryMixin, glFeatureFlagsMixin()],
+ props: {
+ note: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ expanded: false,
+ lines: [],
+ showLines: false,
+ loadingDiff: false,
+ isLoadingDescriptionVersion: false,
+ };
+ },
+ computed: {
+ targetNoteHash() {
+ return getLocationHash();
+ },
+ descriptionVersions() {
+ return [];
+ },
+ noteAnchorId() {
+ return `note_${this.note.id}`;
+ },
+ isTargetNote() {
+ return this.targetNoteHash === this.noteAnchorId;
+ },
+ toggleIcon() {
+ return this.expanded ? 'chevron-up' : 'chevron-down';
+ },
+ // following 2 methods taken from code in `collapseLongCommitList` of notes.js:
+ actionTextHtml() {
+ return $(this.note.bodyHtml).unwrap().html();
+ },
+ hasMoreCommits() {
+ return $(this.note.bodyHtml).filter('ul').children().length > MAX_VISIBLE_COMMIT_LIST_COUNT;
+ },
+ descriptionVersion() {
+ return this.descriptionVersions[this.note.description_version_id];
+ },
+ },
+ mounted() {
+ renderGFM(this.$refs['gfm-content']);
+ },
+ methods: {
+ fetchDescriptionVersion() {},
+ softDeleteDescriptionVersion() {},
+
+ async toggleDiff() {
+ this.showLines = !this.showLines;
+
+ if (!this.lines.length) {
+ this.loadingDiff = true;
+ const { data } = await axios.get(this.note.outdated_line_change_path);
+
+ this.lines = data.map((l) => ({
+ ...l,
+ rich_text: l.rich_text.replace(/^[+ -]/, ''),
+ }));
+ this.loadingDiff = false;
+ }
+ },
+ },
+ safeHtmlConfig: {
+ ADD_TAGS: ['use'], // to support icon SVGs
+ },
+ userColorSchemeClass: window.gon.user_color_scheme,
+};
+</script>
+
+<template>
+ <timeline-entry-item
+ :id="noteAnchorId"
+ :class="{ target: isTargetNote, 'pr-0': shouldShowDescriptionVersion }"
+ class="note system-note note-wrapper"
+ >
+ <div class="timeline-icon"><gl-icon :name="note.systemNoteIconName" /></div>
+ <div class="timeline-content">
+ <div class="note-header">
+ <note-header
+ :author="note.author"
+ :created-at="note.createdAt"
+ :note-id="note.id"
+ :is-system-note="true"
+ >
+ <span ref="gfm-content" v-safe-html="actionTextHtml"></span>
+ <template
+ v-if="canSeeDescriptionVersion || note.outdated_line_change_path"
+ #extra-controls
+ >
+ &middot;
+ <gl-button
+ v-if="canSeeDescriptionVersion"
+ variant="link"
+ :icon="descriptionVersionToggleIcon"
+ data-testid="compare-btn"
+ class="gl-vertical-align-text-bottom gl-font-sm!"
+ @click="toggleDescriptionVersion"
+ >{{ __('Compare with previous version') }}</gl-button
+ >
+ <gl-button
+ v-if="note.outdated_line_change_path"
+ :icon="showLines ? 'chevron-up' : 'chevron-down'"
+ variant="link"
+ data-testid="outdated-lines-change-btn"
+ class="gl-vertical-align-text-bottom gl-font-sm!"
+ @click="toggleDiff"
+ >
+ {{ __('Compare changes') }}
+ </gl-button>
+ </template>
+ </note-header>
+ </div>
+ <div class="note-body">
+ <div
+ v-safe-html="note.bodyHtml"
+ :class="{ 'system-note-commit-list': hasMoreCommits, 'hide-shade': expanded }"
+ class="note-text md"
+ ></div>
+ <div v-if="hasMoreCommits" class="flex-list">
+ <div class="system-note-commit-list-toggler flex-row" @click="expanded = !expanded">
+ <gl-icon :name="toggleIcon" :size="8" class="gl-mr-2" />
+ <span>{{ __('Toggle commit list') }}</span>
+ </div>
+ </div>
+ <div v-if="shouldShowDescriptionVersion" class="description-version pt-2">
+ <pre v-if="isLoadingDescriptionVersion" class="loading-state">
+ <gl-skeleton-loader />
+ </pre>
+ <pre v-else v-safe-html="descriptionVersion" class="wrapper mt-2"></pre>
+ <gl-button
+ v-if="displayDeleteButton"
+ v-gl-tooltip
+ :title="$options.i18n.deleteButtonLabel"
+ :aria-label="$options.i18n.deleteButtonLabel"
+ variant="default"
+ category="tertiary"
+ icon="remove"
+ class="delete-description-history"
+ data-testid="delete-description-version-button"
+ @click="deleteDescriptionVersion"
+ />
+ </div>
+ <div
+ v-if="lines.length && showLines"
+ class="diff-content outdated-lines-wrapper gl-border-solid gl-border-1 gl-border-gray-200 gl-mt-4 gl-rounded-small gl-overflow-hidden"
+ >
+ <table
+ :class="$options.userColorSchemeClass"
+ class="code js-syntax-highlight"
+ data-testid="outdated-lines"
+ >
+ <tr v-for="line in lines" v-once :key="line.line_code" class="line_holder">
+ <td
+ :class="line.type"
+ class="diff-line-num old_line gl-border-bottom-0! gl-border-top-0! gl-border-0! gl-rounded-0!"
+ >
+ {{ line.old_line }}
+ </td>
+ <td
+ :class="line.type"
+ class="diff-line-num new_line gl-border-bottom-0! gl-border-top-0!"
+ >
+ {{ line.new_line }}
+ </td>
+ <td
+ :class="line.type"
+ class="line_content gl-display-table-cell! gl-border-0! gl-rounded-0!"
+ v-html="line.rich_text /* eslint-disable-line vue/no-v-html */"
+ ></td>
+ </tr>
+ </table>
+ </div>
+ <div v-else-if="showLines" class="mt-4">
+ <gl-skeleton-loader />
+ </div>
+ </div>
+ </div>
+ </timeline-entry-item>
+</template>
diff --git a/app/assets/javascripts/work_items/components/work_item_assignees.vue b/app/assets/javascripts/work_items/components/work_item_assignees.vue
index 4d6a27f61ac..c2980405a19 100644
--- a/app/assets/javascripts/work_items/components/work_item_assignees.vue
+++ b/app/assets/javascripts/work_items/components/work_item_assignees.vue
@@ -202,6 +202,7 @@ export default {
if (!this.allowsMultipleAssignees) {
this.localAssignees = assignees.length > 0 ? [assignees[assignees.length - 1]] : [];
this.isEditing = false;
+ this.setAssignees(this.assigneeIds);
return;
}
this.localAssignees = assignees;
diff --git a/app/assets/javascripts/work_items/components/work_item_description.vue b/app/assets/javascripts/work_items/components/work_item_description.vue
index 57930951856..07da0279b41 100644
--- a/app/assets/javascripts/work_items/components/work_item_description.vue
+++ b/app/assets/javascripts/work_items/components/work_item_description.vue
@@ -1,5 +1,5 @@
<script>
-import { GlButton, GlFormGroup } from '@gitlab/ui';
+import { GlAlert, GlButton, GlFormGroup } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { helpPagePath } from '~/helpers/help_page_helper';
import { getDraft, clearDraft, updateDraft } from '~/lib/utils/autosave';
@@ -19,6 +19,7 @@ import WorkItemDescriptionRendered from './work_item_description_rendered.vue';
export default {
components: {
EditedAt,
+ GlAlert,
GlButton,
GlFormGroup,
MarkdownEditor,
@@ -54,6 +55,7 @@ export default {
isSubmittingWithKeydown: false,
descriptionText: '',
descriptionHtml: '',
+ conflictedDescription: '',
};
},
apollo: {
@@ -68,11 +70,17 @@ export default {
return this.fetchByIid ? data.workspace.workItems.nodes[0] : data.workItem;
},
skip() {
- return !this.workItemId;
+ return !this.queryVariables.id && !this.queryVariables.iid;
},
result() {
- this.descriptionText = this.workItemDescription?.description;
- this.descriptionHtml = this.workItemDescription?.descriptionHtml;
+ if (this.isEditing) {
+ if (this.descriptionText !== this.workItemDescription?.description) {
+ this.conflictedDescription = this.workItemDescription?.description;
+ }
+ } else {
+ this.descriptionText = this.workItemDescription?.description;
+ this.descriptionHtml = this.workItemDescription?.descriptionHtml;
+ }
},
error() {
this.$emit('error', i18n.fetchError);
@@ -94,6 +102,9 @@ export default {
canEdit() {
return this.workItem?.userPermissions?.updateWorkItem || false;
},
+ hasConflicts() {
+ return Boolean(this.conflictedDescription);
+ },
tracking() {
return {
category: TRACKING_CATEGORY_SHOW,
@@ -196,6 +207,7 @@ export default {
this.isEditing = false;
clearDraft(this.autosaveKey);
+ this.conflictedDescription = '';
} catch (error) {
this.$emit('error', error.message);
Sentry.captureException(error);
@@ -224,7 +236,7 @@ export default {
label-for="work-item-description"
>
<markdown-editor
- v-if="glFeatures.workItemsMvc2"
+ v-if="glFeatures.workItemsMvc"
class="gl-my-3 common-note-form"
:value="descriptionText"
:render-markdown-path="markdownPreviewPath"
@@ -235,6 +247,7 @@ export default {
form-field-name="work-item-description"
enable-autocomplete
init-on-autofocus
+ use-bottom-toolbar
@input="setDescriptionText"
@keydown.meta.enter="updateWorkItem"
@keydown.ctrl.enter="updateWorkItem"
@@ -246,7 +259,7 @@ export default {
:is-submitting="isSubmitting"
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="$options.markdownDocsPath"
- class="gl-p-3 bordered-box gl-mt-5"
+ class="gl-px-3 bordered-box gl-mt-5"
>
<template #textarea>
<textarea
@@ -267,17 +280,59 @@ export default {
</template>
</markdown-field>
<div class="gl-display-flex">
- <gl-button
- category="primary"
- variant="confirm"
- :loading="isSubmitting"
- data-testid="save-description"
- @click="updateWorkItem"
- >{{ __('Save') }}
- </gl-button>
- <gl-button category="tertiary" class="gl-ml-3" data-testid="cancel" @click="cancelEditing"
- >{{ __('Cancel') }}
- </gl-button>
+ <gl-alert
+ v-if="hasConflicts"
+ :dismissible="false"
+ variant="danger"
+ class="gl-w-full"
+ data-testid="work-item-description-conflicts"
+ >
+ <p>
+ {{
+ s__(
+ "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits.",
+ )
+ }}
+ </p>
+ <details class="gl-mb-5">
+ <summary class="gl-text-blue-500">{{ s__('WorkItem|View current version') }}</summary>
+ <textarea
+ class="note-textarea js-gfm-input js-autosize markdown-area gl-p-3"
+ readonly
+ :value="conflictedDescription"
+ ></textarea>
+ </details>
+ <template #actions>
+ <gl-button
+ category="primary"
+ variant="confirm"
+ :loading="isSubmitting"
+ data-testid="save-description"
+ @click="updateWorkItem"
+ >{{ s__('WorkItem|Save and overwrite') }}
+ </gl-button>
+ <gl-button
+ category="secondary"
+ class="gl-ml-3"
+ data-testid="cancel"
+ @click="cancelEditing"
+ >{{ s__('WorkItem|Discard changes') }}
+ </gl-button>
+ </template>
+ </gl-alert>
+ <template v-else>
+ <gl-button
+ category="primary"
+ variant="confirm"
+ :loading="isSubmitting"
+ data-testid="save-description"
+ @click="updateWorkItem"
+ >{{ __('Save') }}
+ </gl-button>
+ <gl-button category="tertiary" class="gl-ml-3" data-testid="cancel" @click="cancelEditing"
+ >{{ __('Cancel') }}
+ </gl-button>
+ </template>
</div>
</gl-form-group>
<work-item-description-rendered
diff --git a/app/assets/javascripts/work_items/components/work_item_description_rendered.vue b/app/assets/javascripts/work_items/components/work_item_description_rendered.vue
index e6f8a301c5e..d58983c013d 100644
--- a/app/assets/javascripts/work_items/components/work_item_description_rendered.vue
+++ b/app/assets/javascripts/work_items/components/work_item_description_rendered.vue
@@ -1,13 +1,14 @@
<script>
-import { GlButton, GlSafeHtmlDirective } from '@gitlab/ui';
-import $ from 'jquery';
-import '~/behaviors/markdown/render_gfm';
+import { GlButton, GlTooltipDirective } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
const isCheckbox = (target) => target?.classList.contains('task-list-item-checkbox');
export default {
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
+ GlTooltip: GlTooltipDirective,
},
components: {
GlButton,
@@ -45,7 +46,7 @@ export default {
async renderGFM() {
await this.$nextTick();
- $(this.$refs['gfm-content']).renderGFM();
+ renderGFM(this.$refs['gfm-content']);
if (this.canEdit) {
this.checkboxes = this.$el.querySelectorAll('.task-list-item-checkbox');
@@ -93,14 +94,16 @@ export default {
<template>
<div class="gl-mb-5 gl-border-t gl-pt-5">
- <div class="gl-display-inline-flex gl-align-items-center gl-mb-5">
+ <div class="gl-display-inline-flex gl-align-items-center gl-mb-3">
<label class="d-block col-form-label gl-mr-5">{{ __('Description') }}</label>
<gl-button
v-if="canEdit"
+ v-gl-tooltip
class="gl-ml-auto"
icon="pencil"
data-testid="edit-description"
:aria-label="__('Edit description')"
+ :title="__('Edit description')"
@click="$emit('startEditing')"
/>
</div>
@@ -111,6 +114,7 @@ export default {
ref="gfm-content"
v-safe-html="descriptionHtml"
class="md gl-mb-5 gl-min-h-8"
+ data-testid="work-item-description"
@change="toggleCheckboxes"
></div>
</div>
diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue
index 7e9fa24e3f5..cb45a05de89 100644
--- a/app/assets/javascripts/work_items/components/work_item_detail.vue
+++ b/app/assets/javascripts/work_items/components/work_item_detail.vue
@@ -1,5 +1,6 @@
<script>
import { isEmpty } from 'lodash';
+import { produce } from 'immer';
import {
GlAlert,
GlSkeletonLoader,
@@ -11,10 +12,11 @@ import {
GlEmptyState,
} from '@gitlab/ui';
import noAccessSvg from '@gitlab/svgs/dist/illustrations/analytics/no-access.svg';
+import * as Sentry from '@sentry/browser';
import { s__ } from '~/locale';
import { parseBoolean } from '~/lib/utils/common_utils';
+import { getParameterByName } from '~/lib/utils/url_utility';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import WorkItemTypeIcon from '~/work_items/components/work_item_type_icon.vue';
import {
i18n,
@@ -23,10 +25,14 @@ import {
WIDGET_TYPE_DESCRIPTION,
WIDGET_TYPE_START_AND_DUE_DATE,
WIDGET_TYPE_WEIGHT,
+ WIDGET_TYPE_PROGRESS,
WIDGET_TYPE_HIERARCHY,
- WORK_ITEM_VIEWED_STORAGE_KEY,
WIDGET_TYPE_MILESTONE,
WIDGET_TYPE_ITERATION,
+ WIDGET_TYPE_HEALTH_STATUS,
+ WORK_ITEM_TYPE_VALUE_ISSUE,
+ WORK_ITEM_TYPE_VALUE_OBJECTIVE,
+ WIDGET_TYPE_NOTES,
} from '../constants';
import workItemDatesSubscription from '../graphql/work_item_dates.subscription.graphql';
@@ -37,6 +43,7 @@ import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql
import updateWorkItemTaskMutation from '../graphql/update_work_item_task.mutation.graphql';
import { getWorkItemQuery } from '../utils';
+import WorkItemTree from './work_item_links/work_item_tree.vue';
import WorkItemActions from './work_item_actions.vue';
import WorkItemState from './work_item_state.vue';
import WorkItemTitle from './work_item_title.vue';
@@ -45,7 +52,7 @@ import WorkItemDueDate from './work_item_due_date.vue';
import WorkItemAssignees from './work_item_assignees.vue';
import WorkItemLabels from './work_item_labels.vue';
import WorkItemMilestone from './work_item_milestone.vue';
-import WorkItemInformation from './work_item_information.vue';
+import WorkItemNotes from './work_item_notes.vue';
export default {
i18n,
@@ -68,11 +75,14 @@ export default {
WorkItemTitle,
WorkItemState,
WorkItemWeight: () => import('ee_component/work_items/components/work_item_weight.vue'),
- WorkItemInformation,
- LocalStorageSync,
+ WorkItemProgress: () => import('ee_component/work_items/components/work_item_progress.vue'),
WorkItemTypeIcon,
WorkItemIteration: () => import('ee_component/work_items/components/work_item_iteration.vue'),
+ WorkItemHealthStatus: () =>
+ import('ee_component/work_items/components/work_item_health_status.vue'),
WorkItemMilestone,
+ WorkItemTree,
+ WorkItemNotes,
},
mixins: [glFeatureFlagMixin()],
inject: ['fullPath'],
@@ -87,7 +97,7 @@ export default {
required: false,
default: null,
},
- iid: {
+ workItemIid: {
type: String,
required: false,
default: null,
@@ -103,7 +113,6 @@ export default {
error: undefined,
updateError: undefined,
workItem: {},
- showInfoBanner: true,
updateInProgress: false,
};
},
@@ -201,17 +210,31 @@ export default {
fullPath() {
return this.workItem?.project.fullPath;
},
+ workItemsMvcEnabled() {
+ return this.glFeatures.workItemsMvc;
+ },
workItemsMvc2Enabled() {
return this.glFeatures.workItemsMvc2;
},
parentWorkItem() {
return this.isWidgetPresent(WIDGET_TYPE_HIERARCHY)?.parent;
},
+ parentWorkItemType() {
+ return this.parentWorkItem?.workItemType?.name;
+ },
+ parentWorkItemIconName() {
+ return this.parentWorkItem?.workItemType?.iconName;
+ },
parentWorkItemConfidentiality() {
return this.parentWorkItem?.confidential;
},
parentUrl() {
- return `../../issues/${this.parentWorkItem?.iid}`;
+ // Once more types are moved to have Work Items involved
+ // we need to handle this properly.
+ if (this.parentWorkItemType === WORK_ITEM_TYPE_VALUE_ISSUE) {
+ return `../../issues/${this.parentWorkItem?.iid}`;
+ }
+ return this.parentWorkItem?.webUrl;
},
workItemIconName() {
return this.workItem?.workItemType?.iconName;
@@ -234,41 +257,48 @@ export default {
workItemWeight() {
return this.isWidgetPresent(WIDGET_TYPE_WEIGHT);
},
+ workItemProgress() {
+ return this.isWidgetPresent(WIDGET_TYPE_PROGRESS);
+ },
workItemHierarchy() {
return this.isWidgetPresent(WIDGET_TYPE_HIERARCHY);
},
workItemIteration() {
return this.isWidgetPresent(WIDGET_TYPE_ITERATION);
},
+ workItemHealthStatus() {
+ return this.isWidgetPresent(WIDGET_TYPE_HEALTH_STATUS);
+ },
workItemMilestone() {
return this.isWidgetPresent(WIDGET_TYPE_MILESTONE);
},
+ workItemNotes() {
+ return this.isWidgetPresent(WIDGET_TYPE_NOTES);
+ },
fetchByIid() {
- return this.glFeatures.useIidInWorkItemsPath && parseBoolean(this.$route.query.iid_path);
+ return this.glFeatures.useIidInWorkItemsPath && parseBoolean(getParameterByName('iid_path'));
},
queryVariables() {
return this.fetchByIid
? {
fullPath: this.fullPath,
- iid: this.iid,
+ iid: this.workItemIid,
}
: {
id: this.workItemId,
};
},
- },
- beforeDestroy() {
- /** make sure that if the user has not even dismissed the alert ,
- * should no be able to see the information next time and update the local storage * */
- this.dismissBanner();
+ children() {
+ const widgetHierarchy = this.workItem.widgets.find(
+ (widget) => widget.type === WIDGET_TYPE_HIERARCHY,
+ );
+ return widgetHierarchy.children.nodes;
+ },
},
methods: {
isWidgetPresent(type) {
return this.workItem?.widgets?.find((widget) => widget.type === type);
},
- dismissBanner() {
- this.showInfoBanner = false;
- },
toggleConfidentiality(confidentialStatus) {
this.updateInProgress = true;
let updateMutation = updateWorkItemMutation;
@@ -321,8 +351,76 @@ export default {
this.error = this.$options.i18n.fetchError;
document.title = s__('404|Not found');
},
+ addChild(child) {
+ const { defaultClient: client } = this.$apollo.provider.clients;
+ this.toggleChildFromCache(child, child.id, client);
+ },
+ toggleChildFromCache(workItem, childId, store) {
+ const sourceData = store.readQuery({
+ query: getWorkItemQuery(this.fetchByIid),
+ variables: this.queryVariables,
+ });
+
+ const newData = produce(sourceData, (draftState) => {
+ const widgetHierarchy = draftState.workItem.widgets.find(
+ (widget) => widget.type === WIDGET_TYPE_HIERARCHY,
+ );
+
+ const index = widgetHierarchy.children.nodes.findIndex((child) => child.id === childId);
+
+ if (index >= 0) {
+ widgetHierarchy.children.nodes.splice(index, 1);
+ } else {
+ widgetHierarchy.children.nodes.unshift(workItem);
+ }
+ });
+
+ store.writeQuery({
+ query: getWorkItemQuery(this.fetchByIid),
+ variables: this.queryVariables,
+ data: newData,
+ });
+ },
+ async updateWorkItem(workItem, childId, parentId) {
+ return this.$apollo.mutate({
+ mutation: updateWorkItemMutation,
+ variables: { input: { id: childId, hierarchyWidget: { parentId } } },
+ update: (store) => this.toggleChildFromCache(workItem, childId, store),
+ });
+ },
+ async undoChildRemoval(workItem, childId) {
+ try {
+ const { data } = await this.updateWorkItem(workItem, childId, this.workItem.id);
+
+ if (data.workItemUpdate.errors.length === 0) {
+ this.activeToast?.hide();
+ }
+ } catch (error) {
+ this.updateError = s__('WorkItem|Something went wrong while undoing child removal.');
+ Sentry.captureException(error);
+ } finally {
+ this.activeToast?.hide();
+ }
+ },
+ async removeChild(childId) {
+ try {
+ const { data } = await this.updateWorkItem(null, childId, null);
+
+ if (data.workItemUpdate.errors.length === 0) {
+ this.activeToast = this.$toast.show(s__('WorkItem|Child removed'), {
+ action: {
+ text: s__('WorkItem|Undo'),
+ onClick: this.undoChildRemoval.bind(this, data.workItemUpdate.workItem, childId),
+ },
+ });
+ }
+ } catch (error) {
+ this.updateError = s__('WorkItem|Something went wrong while removing child.');
+ Sentry.captureException(error);
+ }
+ },
},
- WORK_ITEM_VIEWED_STORAGE_KEY,
+ WORK_ITEM_TYPE_VALUE_OBJECTIVE,
};
</script>
@@ -347,14 +445,14 @@ export default {
<div class="gl-display-flex gl-align-items-center" data-testid="work-item-body">
<ul
v-if="parentWorkItem"
- class="list-unstyled gl-display-flex gl-mr-auto gl-max-w-26 gl-md-max-w-50p gl-min-w-0 gl-mb-0"
+ class="list-unstyled gl-display-flex gl-mr-auto gl-max-w-26 gl-md-max-w-50p gl-min-w-0 gl-mb-0 gl-z-index-0"
data-testid="work-item-parent"
>
<li class="gl-ml-n4 gl-display-flex gl-align-items-center gl-overflow-hidden">
<gl-button
v-gl-tooltip.hover
class="gl-text-truncate gl-max-w-full"
- icon="issues"
+ :icon="parentWorkItemIconName"
category="tertiary"
:href="parentUrl"
:title="parentWorkItem.title"
@@ -411,16 +509,6 @@ export default {
@click="$emit('close')"
/>
</div>
- <local-storage-sync
- v-model="showInfoBanner"
- :storage-key="$options.WORK_ITEM_VIEWED_STORAGE_KEY"
- >
- <work-item-information
- v-if="showInfoBanner && !error"
- :show-info-banner="showInfoBanner"
- @work-item-banner-dismissed="dismissBanner"
- />
- </local-storage-sync>
<work-item-title
v-if="workItem.title"
:work-item-id="workItem.id"
@@ -465,19 +553,17 @@ export default {
:work-item-type="workItemType"
@error="updateError = $event"
/>
- <template v-if="workItemsMvc2Enabled">
- <work-item-milestone
- v-if="workItemMilestone"
- :work-item-id="workItem.id"
- :work-item-milestone="workItemMilestone.milestone"
- :work-item-type="workItemType"
- :fetch-by-iid="fetchByIid"
- :query-variables="queryVariables"
- :can-update="canUpdate"
- :full-path="fullPath"
- @error="updateError = $event"
- />
- </template>
+ <work-item-milestone
+ v-if="workItemMilestone"
+ :work-item-id="workItem.id"
+ :work-item-milestone="workItemMilestone.milestone"
+ :work-item-type="workItemType"
+ :fetch-by-iid="fetchByIid"
+ :query-variables="queryVariables"
+ :can-update="canUpdate"
+ :full-path="fullPath"
+ @error="updateError = $event"
+ />
<work-item-weight
v-if="workItemWeight"
class="gl-mb-5"
@@ -489,20 +575,38 @@ export default {
:query-variables="queryVariables"
@error="updateError = $event"
/>
- <template v-if="workItemsMvc2Enabled">
- <work-item-iteration
- v-if="workItemIteration"
- class="gl-mb-5"
- :iteration="workItemIteration.iteration"
- :can-update="canUpdate"
- :work-item-id="workItem.id"
- :work-item-type="workItemType"
- :fetch-by-iid="fetchByIid"
- :query-variables="queryVariables"
- :full-path="fullPath"
- @error="updateError = $event"
- />
- </template>
+ <work-item-progress
+ v-if="workItemProgress"
+ class="gl-mb-5"
+ :can-update="canUpdate"
+ :progress="workItemProgress.progress"
+ :work-item-id="workItem.id"
+ :work-item-type="workItemType"
+ :fetch-by-iid="fetchByIid"
+ :query-variables="queryVariables"
+ @error="updateError = $event"
+ />
+ <work-item-iteration
+ v-if="workItemIteration"
+ class="gl-mb-5"
+ :iteration="workItemIteration.iteration"
+ :can-update="canUpdate"
+ :work-item-id="workItem.id"
+ :work-item-type="workItemType"
+ :fetch-by-iid="fetchByIid"
+ :query-variables="queryVariables"
+ :full-path="fullPath"
+ @error="updateError = $event"
+ />
+ <work-item-health-status
+ v-if="workItemHealthStatus"
+ class="gl-mb-5"
+ :health-status="workItemHealthStatus.healthStatus"
+ :can-update="canUpdate"
+ :work-item-id="workItem.id"
+ :work-item-type="workItemType"
+ @error="updateError = $event"
+ />
<work-item-description
v-if="hasDescriptionWidget"
:work-item-id="workItem.id"
@@ -512,6 +616,27 @@ export default {
class="gl-pt-5"
@error="updateError = $event"
/>
+ <work-item-tree
+ v-if="workItemType === $options.WORK_ITEM_TYPE_VALUE_OBJECTIVE"
+ :work-item-type="workItemType"
+ :work-item-id="workItem.id"
+ :children="children"
+ :can-update="canUpdate"
+ :project-path="fullPath"
+ @addWorkItemChild="addChild"
+ @removeChild="removeChild"
+ />
+ <template v-if="workItemsMvc2Enabled">
+ <work-item-notes
+ v-if="workItemNotes"
+ :work-item-id="workItem.id"
+ :query-variables="queryVariables"
+ :full-path="fullPath"
+ :fetch-by-iid="fetchByIid"
+ class="gl-pt-5"
+ @error="updateError = $event"
+ />
+ </template>
<gl-empty-state
v-if="error"
:title="$options.i18n.fetchErrorTitle"
diff --git a/app/assets/javascripts/work_items/components/work_item_detail_modal.vue b/app/assets/javascripts/work_items/components/work_item_detail_modal.vue
index 39a662a6c54..e8726814aaf 100644
--- a/app/assets/javascripts/work_items/components/work_item_detail_modal.vue
+++ b/app/assets/javascripts/work_items/components/work_item_detail_modal.vue
@@ -20,6 +20,11 @@ export default {
required: false,
default: null,
},
+ workItemIid: {
+ type: String,
+ required: false,
+ default: null,
+ },
issueGid: {
type: String,
required: false,
@@ -134,6 +139,7 @@ export default {
size="lg"
modal-id="work-item-detail-modal"
header-class="gl-p-0 gl-pb-2!"
+ scrollable
@hide="closeModal"
>
<gl-alert v-if="error" variant="danger" @dismiss="error = false">
@@ -144,6 +150,7 @@ export default {
is-modal
:work-item-parent-id="issueGid"
:work-item-id="workItemId"
+ :work-item-iid="workItemIid"
class="gl-p-5 gl-mt-n3"
@close="hide"
@deleteWorkItem="deleteWorkItem"
diff --git a/app/assets/javascripts/work_items/components/work_item_information.vue b/app/assets/javascripts/work_items/components/work_item_information.vue
deleted file mode 100644
index ce75cc98a75..00000000000
--- a/app/assets/javascripts/work_items/components/work_item_information.vue
+++ /dev/null
@@ -1,53 +0,0 @@
-<script>
-import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
-import { s__ } from '~/locale';
-import { helpPagePath } from '~/helpers/help_page_helper';
-
-export default {
- i18n: {
- learnTasksLinkText: s__('WorkItem|Learn about tasks.'),
- tasksInformationTitle: s__('WorkItem|Introducing tasks'),
- tasksInformationBody: s__(
- 'WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}',
- ),
- },
- helpPageLinks: {
- tasksDocLinkPath: helpPagePath('user/tasks'),
- },
- components: {
- GlAlert,
- GlSprintf,
- GlLink,
- },
- props: {
- showInfoBanner: {
- type: Boolean,
- required: false,
- default: true,
- },
- },
- emits: ['work-item-banner-dismissed'],
-};
-</script>
-
-<template>
- <section class="gl-display-block gl-mb-2">
- <gl-alert
- v-if="showInfoBanner"
- variant="tip"
- :title="$options.i18n.tasksInformationTitle"
- data-testid="work-item-information"
- class="gl-mt-3"
- @dismiss="$emit('work-item-banner-dismissed')"
- >
- <gl-sprintf :message="$options.i18n.tasksInformationBody">
- <template #learnMoreLink>
- <gl-link :href="$options.helpPageLinks.tasksDocLinkPath">{{
- $options.i18n.learnTasksLinkText
- }}</gl-link>
- </template>
- ></gl-sprintf
- >
- </gl-alert>
- </section>
-</template>
diff --git a/app/assets/javascripts/work_items/components/work_item_labels.vue b/app/assets/javascripts/work_items/components/work_item_labels.vue
index 22af3c653e9..45fb0f7f21a 100644
--- a/app/assets/javascripts/work_items/components/work_item_labels.vue
+++ b/app/assets/javascripts/work_items/components/work_item_labels.vue
@@ -3,8 +3,8 @@ import { GlTokenSelector, GlLabel, GlSkeletonLoader } from '@gitlab/ui';
import { debounce, uniqueId, without } from 'lodash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import Tracking from '~/tracking';
-import labelSearchQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql';
-import LabelItem from '~/vue_shared/components/sidebar/labels_select_widget/label_item.vue';
+import labelSearchQuery from '~/sidebar/components/labels/labels_select_widget/graphql/project_labels.query.graphql';
+import LabelItem from '~/sidebar/components/labels/labels_select_widget/label_item.vue';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import { isScopedLabel } from '~/lib/utils/common_utils';
import workItemLabelsSubscription from 'ee_else_ce/work_items/graphql/work_item_labels.subscription.graphql';
@@ -83,7 +83,7 @@ export default {
return this.fetchByIid ? data.workspace.workItems.nodes[0] : data.workItem;
},
skip() {
- return !this.workItemId;
+ return !this.queryVariables.id && !this.queryVariables.iid;
},
error() {
this.$emit('error', i18n.fetchError);
diff --git a/app/assets/javascripts/work_items/components/work_item_links/index.js b/app/assets/javascripts/work_items/components/work_item_links/index.js
index 0251dcc33fa..edad0e9b616 100644
--- a/app/assets/javascripts/work_items/components/work_item_links/index.js
+++ b/app/assets/javascripts/work_items/components/work_item_links/index.js
@@ -17,6 +17,7 @@ export default function initWorkItemLinks() {
wiHasIssueWeightsFeature,
iid,
wiHasIterationsFeature,
+ wiHasIssuableHealthStatusFeature,
} = workItemLinksRoot.dataset;
// eslint-disable-next-line no-new
@@ -33,6 +34,7 @@ export default function initWorkItemLinks() {
fullPath: projectPath,
hasIssueWeightsFeature: wiHasIssueWeightsFeature,
hasIterationsFeature: wiHasIterationsFeature,
+ hasIssuableHealthStatusFeature: wiHasIssuableHealthStatusFeature,
},
render: (createElement) =>
createElement('work-item-links', {
diff --git a/app/assets/javascripts/work_items/components/work_item_links/okr_actions_split_button.vue b/app/assets/javascripts/work_items/components/work_item_links/okr_actions_split_button.vue
new file mode 100644
index 00000000000..dc5bcdc3dcc
--- /dev/null
+++ b/app/assets/javascripts/work_items/components/work_item_links/okr_actions_split_button.vue
@@ -0,0 +1,66 @@
+<script>
+import { GlDropdown, GlDropdownSectionHeader, GlDropdownItem, GlDropdownDivider } from '@gitlab/ui';
+
+import { s__ } from '~/locale';
+
+const objectiveActionItems = [
+ {
+ title: s__('OKR|New objective'),
+ eventName: 'showCreateObjectiveForm',
+ },
+ {
+ title: s__('OKR|Existing objective'),
+ eventName: 'showAddObjectiveForm',
+ },
+];
+
+const keyResultActionItems = [
+ {
+ title: s__('OKR|New key result'),
+ eventName: 'showCreateKeyResultForm',
+ },
+ {
+ title: s__('OKR|Existing key result'),
+ eventName: 'showAddKeyResultForm',
+ },
+];
+
+export default {
+ keyResultActionItems,
+ objectiveActionItems,
+ components: {
+ GlDropdown,
+ GlDropdownSectionHeader,
+ GlDropdownItem,
+ GlDropdownDivider,
+ },
+ methods: {
+ change({ eventName }) {
+ this.$emit(eventName);
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-dropdown :text="__('Add')" size="small" right>
+ <gl-dropdown-section-header>{{ __('Objective') }}</gl-dropdown-section-header>
+ <gl-dropdown-item
+ v-for="item in $options.objectiveActionItems"
+ :key="item.eventName"
+ @click="change(item)"
+ >
+ {{ item.title }}
+ </gl-dropdown-item>
+
+ <gl-dropdown-divider />
+ <gl-dropdown-section-header>{{ __('Key result') }}</gl-dropdown-section-header>
+ <gl-dropdown-item
+ v-for="item in $options.keyResultActionItems"
+ :key="item.eventName"
+ @click="change(item)"
+ >
+ {{ item.title }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+</template>
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue
index 34874908f9b..763f2f338a3 100644
--- a/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue
@@ -1,19 +1,35 @@
<script>
-import { GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui';
+import { GlButton, GlLink, GlIcon, GlTooltipDirective } from '@gitlab/ui';
-import { __ } from '~/locale';
+import { __, s__ } from '~/locale';
+import { createAlert } from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import RichTimestampTooltip from '~/vue_shared/components/rich_timestamp_tooltip.vue';
-import { STATE_OPEN } from '../../constants';
+import {
+ STATE_OPEN,
+ TASK_TYPE_NAME,
+ WORK_ITEM_TYPE_VALUE_OBJECTIVE,
+ WIDGET_TYPE_MILESTONE,
+ WIDGET_TYPE_HIERARCHY,
+ WIDGET_TYPE_ASSIGNEES,
+ WIDGET_TYPE_LABELS,
+ WORK_ITEM_NAME_TO_ICON_MAP,
+} from '../../constants';
+import getWorkItemTreeQuery from '../../graphql/work_item_tree.query.graphql';
+import WorkItemLinkChildMetadata from './work_item_link_child_metadata.vue';
import WorkItemLinksMenu from './work_item_links_menu.vue';
+import WorkItemTreeChildren from './work_item_tree_children.vue';
export default {
components: {
+ GlLink,
GlButton,
GlIcon,
RichTimestampTooltip,
+ WorkItemLinkChildMetadata,
WorkItemLinksMenu,
+ WorkItemTreeChildren,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -35,16 +51,48 @@ export default {
type: Object,
required: true,
},
+ hasIndirectChildren: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ workItemType: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ data() {
+ return {
+ isExpanded: false,
+ children: [],
+ isLoadingChildren: false,
+ };
},
computed: {
+ canHaveChildren() {
+ return this.workItemType === WORK_ITEM_TYPE_VALUE_OBJECTIVE;
+ },
+ allowsScopedLabels() {
+ return this.getWidgetByType(this.childItem, WIDGET_TYPE_LABELS)?.allowsScopedLabels;
+ },
isItemOpen() {
return this.childItem.state === STATE_OPEN;
},
- iconClass() {
- return this.isItemOpen ? 'gl-text-green-500' : 'gl-text-blue-500';
+ childItemType() {
+ return this.childItem.workItemType.name;
},
iconName() {
- return this.isItemOpen ? 'issue-open-m' : 'issue-close';
+ if (this.childItemType === TASK_TYPE_NAME) {
+ return this.isItemOpen ? 'issue-open-m' : 'issue-close';
+ }
+ return WORK_ITEM_NAME_TO_ICON_MAP[this.childItemType];
+ },
+ iconClass() {
+ if (this.childItemType === TASK_TYPE_NAME) {
+ return this.isItemOpen ? 'gl-text-green-500' : 'gl-text-blue-500';
+ }
+ return '';
},
stateTimestamp() {
return this.isItemOpen ? this.childItem.createdAt : this.childItem.closedAt;
@@ -55,55 +103,161 @@ export default {
childPath() {
return `/${this.projectPath}/-/work_items/${getIdFromGraphQLId(this.childItem.id)}`;
},
+ hasChildren() {
+ return this.getWidgetByType(this.childItem, WIDGET_TYPE_HIERARCHY)?.hasChildren;
+ },
+ chevronType() {
+ return this.isExpanded ? 'chevron-down' : 'chevron-right';
+ },
+ chevronTooltip() {
+ return this.isExpanded ? __('Collapse') : __('Expand');
+ },
+ hasMetadata() {
+ return this.milestone || this.assignees.length > 0 || this.labels.length > 0;
+ },
+ milestone() {
+ return this.getWidgetByType(this.childItem, WIDGET_TYPE_MILESTONE)?.milestone;
+ },
+ assignees() {
+ return this.getWidgetByType(this.childItem, WIDGET_TYPE_ASSIGNEES)?.assignees?.nodes || [];
+ },
+ labels() {
+ return this.getWidgetByType(this.childItem, WIDGET_TYPE_LABELS)?.labels?.nodes || [];
+ },
+ },
+ methods: {
+ toggleItem() {
+ this.isExpanded = !this.isExpanded;
+ if (this.children.length === 0 && this.hasChildren) {
+ this.fetchChildren();
+ }
+ },
+ getWidgetByType(workItem, widgetType) {
+ return workItem?.widgets?.find((widget) => widget.type === widgetType);
+ },
+ async fetchChildren() {
+ this.isLoadingChildren = true;
+ try {
+ const { data } = await this.$apollo.query({
+ query: getWorkItemTreeQuery,
+ variables: {
+ id: this.childItem.id,
+ },
+ });
+ this.children = this.getWidgetByType(data?.workItem, WIDGET_TYPE_HIERARCHY).children.nodes;
+ } catch (error) {
+ this.isExpanded = !this.isExpanded;
+ createAlert({
+ message: s__('Hierarchy|Something went wrong while fetching children.'),
+ captureError: true,
+ error,
+ });
+ } finally {
+ this.isLoadingChildren = false;
+ }
+ },
},
};
</script>
<template>
- <div
- class="gl-relative gl-display-flex gl-overflow-break-word gl-min-w-0 gl-bg-white gl-mb-3 gl-py-3 gl-px-4 gl-border gl-border-gray-100 gl-rounded-base gl-line-height-32"
- data-testid="links-child"
- >
- <div class="gl-overflow-hidden gl-display-flex gl-align-items-center gl-flex-grow-1">
- <span :id="`stateIcon-${childItem.id}`" class="gl-mr-3" data-testid="item-status-icon">
- <gl-icon :name="iconName" :class="iconClass" :aria-label="stateTimestampTypeText" />
- </span>
- <rich-timestamp-tooltip
- :target="`stateIcon-${childItem.id}`"
- :raw-timestamp="stateTimestamp"
- :timestamp-type-text="stateTimestampTypeText"
- />
- <gl-icon
- v-if="childItem.confidential"
- v-gl-tooltip.top
- name="eye-slash"
- class="gl-mr-2 gl-text-orange-500"
- data-testid="confidential-icon"
- :aria-label="__('Confidential')"
- :title="__('Confidential')"
- />
- <gl-button
- :href="childPath"
- category="tertiary"
- variant="link"
- class="gl-text-truncate gl-max-w-80 gl-text-black-normal!"
- @click="$emit('click', childItem.id, $event)"
- @mouseover="$emit('mouseover', childItem.id, $event)"
- @mouseout="$emit('mouseout', childItem.id, $event)"
- >
- {{ childItem.title }}
- </gl-button>
- </div>
+ <div>
<div
- v-if="canUpdate"
- class="gl-ml-0 gl-sm-ml-auto! gl-display-inline-flex gl-align-items-center"
+ class="gl-display-flex gl-align-items-flex-start gl-mb-3"
+ :class="{ 'gl-ml-6': canHaveChildren && !hasChildren && hasIndirectChildren }"
>
- <work-item-links-menu
- :work-item-id="childItem.id"
- :parent-work-item-id="issuableGid"
- data-testid="links-menu"
- @removeChild="$emit('remove', childItem.id)"
+ <gl-button
+ v-if="hasChildren"
+ v-gl-tooltip.viewport
+ :title="chevronTooltip"
+ :aria-label="chevronTooltip"
+ :icon="chevronType"
+ category="tertiary"
+ :loading="isLoadingChildren"
+ class="gl-px-0! gl-py-3! gl-mr-3"
+ data-testid="expand-child"
+ @click="toggleItem"
/>
+ <div
+ class="gl-relative gl-display-flex gl-flex-grow-1 gl-overflow-break-word gl-min-w-0 gl-bg-white gl-py-3 gl-px-4 gl-border gl-border-gray-100 gl-rounded-base gl-line-height-32"
+ data-testid="links-child"
+ >
+ <span
+ :id="`stateIcon-${childItem.id}`"
+ class="gl-mr-3"
+ :class="{ 'gl-display-flex': hasMetadata }"
+ data-testid="item-status-icon"
+ >
+ <gl-icon
+ class="gl-text-secondary"
+ :class="iconClass"
+ :name="iconName"
+ :aria-label="stateTimestampTypeText"
+ />
+ </span>
+ <div
+ class="gl-display-flex gl-flex-grow-1"
+ :class="{
+ 'gl-flex-direction-column gl-align-items-flex-start': hasMetadata,
+ 'gl-align-items-center': !hasMetadata,
+ }"
+ >
+ <div class="gl-display-flex">
+ <rich-timestamp-tooltip
+ :target="`stateIcon-${childItem.id}`"
+ :raw-timestamp="stateTimestamp"
+ :timestamp-type-text="stateTimestampTypeText"
+ />
+ <gl-icon
+ v-if="childItem.confidential"
+ v-gl-tooltip.top
+ name="eye-slash"
+ class="gl-mr-2 gl-text-orange-500"
+ data-testid="confidential-icon"
+ :aria-label="__('Confidential')"
+ :title="__('Confidential')"
+ />
+ <gl-link
+ :href="childPath"
+ class="gl-overflow-wrap-break gl-line-height-normal gl-text-black-normal! gl-font-weight-bold"
+ data-testid="item-title"
+ @click="$emit('click', $event)"
+ @mouseover="$emit('mouseover')"
+ @mouseout="$emit('mouseout')"
+ >
+ {{ childItem.title }}
+ </gl-link>
+ </div>
+ <work-item-link-child-metadata
+ v-if="hasMetadata"
+ :allows-scoped-labels="allowsScopedLabels"
+ :milestone="milestone"
+ :assignees="assignees"
+ :labels="labels"
+ class="gl-mt-3"
+ />
+ </div>
+ <div
+ v-if="canUpdate"
+ class="gl-ml-0 gl-sm-ml-auto! gl-display-inline-flex gl-align-items-center"
+ >
+ <work-item-links-menu
+ :work-item-id="childItem.id"
+ :parent-work-item-id="issuableGid"
+ data-testid="links-menu"
+ @removeChild="$emit('removeChild', childItem.id)"
+ />
+ </div>
+ </div>
</div>
+ <work-item-tree-children
+ v-if="isExpanded"
+ :project-path="projectPath"
+ :can-update="canUpdate"
+ :work-item-id="issuableGid"
+ :work-item-type="workItemType"
+ :children="children"
+ @removeChild="fetchChildren"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child_metadata.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child_metadata.vue
new file mode 100644
index 00000000000..7be7e1f3496
--- /dev/null
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child_metadata.vue
@@ -0,0 +1,123 @@
+<script>
+import { GlLabel, GlAvatar, GlAvatarLink, GlAvatarsInline, GlTooltipDirective } from '@gitlab/ui';
+
+import { s__, sprintf } from '~/locale';
+import { isScopedLabel } from '~/lib/utils/common_utils';
+
+import ItemMilestone from '~/issuable/components/issue_milestone.vue';
+
+export default {
+ components: {
+ GlLabel,
+ GlAvatar,
+ GlAvatarLink,
+ GlAvatarsInline,
+ ItemMilestone,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ allowsScopedLabels: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ milestone: {
+ type: Object,
+ required: false,
+ default: null,
+ },
+ assignees: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ labels: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ },
+ computed: {
+ assigneesCollapsedTooltip() {
+ if (this.assignees.length > 2) {
+ return sprintf(s__('WorkItem|%{count} more assignees'), {
+ count: this.assignees.length - 2,
+ });
+ }
+ return '';
+ },
+ assigneesContainerClass() {
+ if (this.assignees.length === 2) {
+ return 'fixed-width-avatars-2';
+ } else if (this.assignees.length > 2) {
+ return 'fixed-width-avatars-3';
+ }
+ return '';
+ },
+ labelsContainerClass() {
+ if (this.milestone || this.assignees.length) {
+ return 'gl-sm-ml-5';
+ }
+ return '';
+ },
+ },
+ methods: {
+ showScopedLabel(label) {
+ return isScopedLabel(label) && this.allowsScopedLabels;
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="gl-display-flex gl-flex-wrap gl-align-items-center">
+ <item-milestone
+ v-if="milestone"
+ :milestone="milestone"
+ class="gl-display-flex gl-align-items-center gl-mr-5 gl-max-w-15 gl-text-secondary! gl-cursor-help! gl-text-decoration-none!"
+ />
+ <gl-avatars-inline
+ v-if="assignees.length"
+ :avatars="assignees"
+ :collapsed="true"
+ :max-visible="2"
+ :avatar-size="24"
+ badge-tooltip-prop="name"
+ :badge-sr-only-text="assigneesCollapsedTooltip"
+ :class="assigneesContainerClass"
+ >
+ <template #avatar="{ avatar }">
+ <gl-avatar-link v-gl-tooltip target="blank" :href="avatar.webUrl" :title="avatar.name">
+ <gl-avatar :src="avatar.avatarUrl" :size="24" />
+ </gl-avatar-link>
+ </template>
+ </gl-avatars-inline>
+ <div v-if="labels.length" class="gl-display-flex gl-flex-wrap" :class="labelsContainerClass">
+ <gl-label
+ v-for="label in labels"
+ :key="label.id"
+ :title="label.title"
+ :background-color="label.color"
+ :description="label.description"
+ :scoped="showScopedLabel(label)"
+ class="gl-mt-2 gl-sm-mt-0 gl-mr-2 gl-mb-auto gl-label-sm"
+ tooltip-placement="top"
+ />
+ </div>
+ </div>
+</template>
+
+<style scoped>
+/**
+ * These overrides are needed to address https://gitlab.com/gitlab-org/gitlab-ui/-/issues/865
+ */
+.fixed-width-avatars-2 {
+ width: 42px !important;
+}
+
+.fixed-width-avatars-3 {
+ width: 67px !important;
+}
+</style>
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue
index 3d469b790a1..faadb5fa6fa 100644
--- a/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue
@@ -9,13 +9,15 @@ import {
GlTooltipDirective,
} from '@gitlab/ui';
import { produce } from 'immer';
+import { isEmpty } from 'lodash';
import { s__ } from '~/locale';
import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import { TYPE_WORK_ITEM } from '~/graphql_shared/constants';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import getIssueDetailsQuery from 'ee_else_ce/work_items/graphql/get_issue_details.query.graphql';
-import { isMetaKey } from '~/lib/utils/common_utils';
-import { setUrlParams, updateHistory } from '~/lib/utils/url_utility';
+import { isMetaKey, parseBoolean } from '~/lib/utils/common_utils';
+import { setUrlParams, updateHistory, getParameterByName } from '~/lib/utils/url_utility';
import {
FORM_TYPES,
@@ -26,6 +28,7 @@ import {
import getWorkItemLinksQuery from '../../graphql/work_item_links.query.graphql';
import updateWorkItemMutation from '../../graphql/update_work_item.mutation.graphql';
import workItemQuery from '../../graphql/work_item.query.graphql';
+import workItemByIidQuery from '../../graphql/work_item_by_iid.query.graphql';
import WorkItemDetailModal from '../work_item_detail_modal.vue';
import WorkItemLinkChild from './work_item_link_child.vue';
import WorkItemLinksForm from './work_item_links_form.vue';
@@ -45,6 +48,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
+ mixins: [glFeatureFlagMixin()],
inject: ['projectPath', 'iid'],
props: {
workItemId: {
@@ -72,6 +76,18 @@ export default {
error(e) {
this.error = e.message || this.$options.i18n.fetchError;
},
+ async result() {
+ const { id, iid } = this.childUrlParams;
+ this.activeChild = this.fetchByIid
+ ? this.children.find((child) => child.iid === iid) ?? {}
+ : this.children.find((child) => child.id === id) ?? {};
+ await this.$nextTick();
+ if (!isEmpty(this.activeChild)) {
+ this.$refs.modal.show();
+ return;
+ }
+ this.updateWorkItemIdUrlQuery();
+ },
},
parentIssue: {
query: getIssueDetailsQuery,
@@ -90,7 +106,7 @@ export default {
return {
isShownAddForm: false,
isOpen: true,
- activeChildId: null,
+ activeChild: {},
activeToast: null,
prefetchedWorkItem: null,
error: undefined,
@@ -139,6 +155,29 @@ export default {
childrenCountLabel() {
return this.isLoading && this.children.length === 0 ? '...' : this.children.length;
},
+ fetchByIid() {
+ return this.glFeatures.useIidInWorkItemsPath && parseBoolean(getParameterByName('iid_path'));
+ },
+ childUrlParams() {
+ const params = {};
+ if (this.fetchByIid) {
+ const iid = getParameterByName('work_item_iid');
+ if (iid) {
+ params.iid = iid;
+ }
+ } else {
+ const workItemId = getParameterByName('work_item_id');
+ if (workItemId) {
+ params.id = convertToGraphQLId(TYPE_WORK_ITEM, workItemId);
+ }
+ }
+ return params;
+ },
+ },
+ mounted() {
+ if (!isEmpty(this.childUrlParams)) {
+ this.addWorkItemQuery(this.childUrlParams);
+ }
},
methods: {
toggle() {
@@ -159,29 +198,29 @@ export default {
const { defaultClient: client } = this.$apollo.provider.clients;
this.toggleChildFromCache(child, child.id, client);
},
- openChild(childItemId, e) {
+ openChild(child, e) {
if (isMetaKey(e)) {
return;
}
e.preventDefault();
- this.activeChildId = childItemId;
+ this.activeChild = child;
this.$refs.modal.show();
- this.updateWorkItemIdUrlQuery(childItemId);
+ this.updateWorkItemIdUrlQuery(child);
},
- closeModal() {
- this.activeChildId = null;
- this.updateWorkItemIdUrlQuery(undefined);
+ async closeModal() {
+ this.activeChild = {};
+ this.updateWorkItemIdUrlQuery();
},
handleWorkItemDeleted(childId) {
const { defaultClient: client } = this.$apollo.provider.clients;
this.toggleChildFromCache(null, childId, client);
this.activeToast = this.$toast.show(s__('WorkItem|Task deleted'));
},
- updateWorkItemIdUrlQuery(childItemId) {
- updateHistory({
- url: setUrlParams({ work_item_id: getIdFromGraphQLId(childItemId) }),
- replace: true,
- });
+ updateWorkItemIdUrlQuery({ id, iid } = {}) {
+ const params = this.fetchByIid
+ ? { work_item_iid: iid }
+ : { work_item_id: getIdFromGraphQLId(id) };
+ updateHistory({ url: setUrlParams(params), replace: true });
},
toggleChildFromCache(workItem, childId, store) {
const sourceData = store.readQuery({
@@ -235,16 +274,31 @@ export default {
});
}
},
- prefetchWorkItem(id) {
+ addWorkItemQuery({ id, iid }) {
+ const variables = this.fetchByIid
+ ? {
+ fullPath: this.projectPath,
+ iid,
+ }
+ : {
+ id,
+ };
+ this.$apollo.addSmartQuery('prefetchedWorkItem', {
+ query() {
+ return this.fetchByIid ? workItemByIidQuery : workItemQuery;
+ },
+ variables,
+ update(data) {
+ return this.fetchByIid ? data.workspace.workItems.nodes[0] : data.workItem;
+ },
+ context: {
+ isSingleRequest: true,
+ },
+ });
+ },
+ prefetchWorkItem({ id, iid }) {
this.prefetch = setTimeout(
- () =>
- this.$apollo.addSmartQuery('prefetchedWorkItem', {
- query: workItemQuery,
- variables: {
- id,
- },
- update: (data) => data.workItem,
- }),
+ () => this.addWorkItemQuery({ id, iid }),
DEFAULT_DEBOUNCE_AND_THROTTLE_MS,
);
},
@@ -355,16 +409,17 @@ export default {
:can-update="canUpdate"
:issuable-gid="issuableGid"
:child-item="child"
- @click="openChild"
- @mouseover="prefetchWorkItem"
+ @click="openChild(child, $event)"
+ @mouseover="prefetchWorkItem(child)"
@mouseout="clearPrefetching"
- @remove="removeChild"
+ @removeChild="removeChild"
/>
<work-item-detail-modal
ref="modal"
- :work-item-id="activeChildId"
+ :work-item-id="activeChild.id"
+ :work-item-iid="activeChild.iid"
@close="closeModal"
- @workItemDeleted="handleWorkItemDeleted(activeChildId)"
+ @workItemDeleted="handleWorkItemDeleted(activeChild.id)"
/>
</template>
</div>
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue
index 095ea86e0d8..5cf0c4154bb 100644
--- a/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue
@@ -9,7 +9,16 @@ import projectWorkItemTypesQuery from '~/work_items/graphql/project_work_item_ty
import projectWorkItemsQuery from '../../graphql/project_work_items.query.graphql';
import updateWorkItemMutation from '../../graphql/update_work_item.mutation.graphql';
import createWorkItemMutation from '../../graphql/create_work_item.mutation.graphql';
-import { FORM_TYPES, TASK_TYPE_NAME } from '../../constants';
+import {
+ FORM_TYPES,
+ WORK_ITEMS_TYPE_MAP,
+ WORK_ITEM_TYPE_ENUM_TASK,
+ I18N_WORK_ITEM_CREATE_BUTTON_LABEL,
+ I18N_WORK_ITEM_SEARCH_INPUT_PLACEHOLDER,
+ I18N_WORK_ITEM_ADD_BUTTON_LABEL,
+ I18N_WORK_ITEM_ADD_MULTIPLE_BUTTON_LABEL,
+ sprintfWorkItem,
+} from '../../constants';
export default {
components: {
@@ -52,6 +61,11 @@ export default {
type: String,
required: true,
},
+ childrenType: {
+ type: String,
+ required: false,
+ default: WORK_ITEM_TYPE_ENUM_TASK,
+ },
},
apollo: {
workItemTypes: {
@@ -71,7 +85,7 @@ export default {
return {
projectPath: this.projectPath,
searchTerm: this.search?.title || this.search,
- types: ['TASK'],
+ types: [this.childrenType],
in: this.search ? 'TITLE' : undefined,
};
},
@@ -79,7 +93,9 @@ export default {
return !this.searchStarted;
},
update(data) {
- return data.workspace.workItems.nodes.filter((wi) => !this.childrenIds.includes(wi.id));
+ return data.workspace.workItems.nodes.filter(
+ (wi) => !this.childrenIds.includes(wi.id) && this.issuableGid !== wi.id,
+ );
},
},
},
@@ -99,14 +115,14 @@ export default {
let workItemInput = {
title: this.search?.title || this.search,
projectPath: this.projectPath,
- workItemTypeId: this.taskWorkItemType,
+ workItemTypeId: this.childWorkItemType,
hierarchyWidget: {
parentId: this.issuableGid,
},
confidential: this.parentConfidential,
};
- if (this.associateMilestone) {
+ if (this.parentMilestoneId) {
workItemInput = {
...workItemInput,
milestoneWidget: {
@@ -114,46 +130,62 @@ export default {
},
};
}
+
+ if (this.associateIteration) {
+ workItemInput = {
+ ...workItemInput,
+ iterationWidget: {
+ iterationId: this.parentIterationId,
+ },
+ };
+ }
+
return workItemInput;
},
+ workItemsMvcEnabled() {
+ return this.glFeatures.workItemsMvc;
+ },
workItemsMvc2Enabled() {
return this.glFeatures.workItemsMvc2;
},
isCreateForm() {
return this.formType === FORM_TYPES.create;
},
+ childrenTypeName() {
+ return WORK_ITEMS_TYPE_MAP[this.childrenType]?.name;
+ },
addOrCreateButtonLabel() {
if (this.isCreateForm) {
- return this.$options.i18n.createChildOptionLabel;
+ return sprintfWorkItem(I18N_WORK_ITEM_CREATE_BUTTON_LABEL, this.childrenTypeName);
} else if (this.workItemsToAdd.length > 1) {
- return this.$options.i18n.addTasksButtonLabel;
+ return sprintfWorkItem(I18N_WORK_ITEM_ADD_MULTIPLE_BUTTON_LABEL, this.childrenTypeName);
}
- return this.$options.i18n.addTaskButtonLabel;
+ return sprintfWorkItem(I18N_WORK_ITEM_ADD_BUTTON_LABEL, this.childrenTypeName);
},
addOrCreateMethod() {
return this.isCreateForm ? this.createChild : this.addChild;
},
- taskWorkItemType() {
- return this.workItemTypes.find((type) => type.name === TASK_TYPE_NAME)?.id;
+ childWorkItemType() {
+ return this.workItemTypes.find((type) => type.name === this.childrenTypeName)?.id;
},
parentIterationId() {
return this.parentIteration?.id;
},
associateIteration() {
- return this.parentIterationId && this.hasIterationsFeature && this.workItemsMvc2Enabled;
+ return this.parentIterationId && this.hasIterationsFeature;
},
parentMilestoneId() {
return this.parentMilestone?.id;
},
- associateMilestone() {
- return this.parentMilestoneId && this.workItemsMvc2Enabled;
- },
isSubmitButtonDisabled() {
return this.isCreateForm ? this.search.length === 0 : this.workItemsToAdd.length === 0;
},
isLoading() {
return this.$apollo.queries.availableWorkItems.loading;
},
+ addInputPlaceholder() {
+ return sprintfWorkItem(I18N_WORK_ITEM_SEARCH_INPUT_PLACEHOLDER, this.childrenTypeName);
+ },
},
created() {
this.debouncedSearchKeyUpdate = debounce(this.setSearchKey, DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
@@ -206,13 +238,6 @@ export default {
} else {
this.unsetError();
this.$emit('addWorkItemChild', data.workItemCreate.workItem);
- /**
- * call update mutation only when there is an iteration associated with the issue
- */
- // TODO: setting the iteration should be moved to the creation mutation once the backend is done
- if (this.associateIteration) {
- this.addIterationToWorkItem(data.workItemCreate.workItem.id);
- }
}
})
.catch(() => {
@@ -223,19 +248,6 @@ export default {
this.childToCreateTitle = null;
});
},
- async addIterationToWorkItem(workItemId) {
- await this.$apollo.mutate({
- mutation: updateWorkItemMutation,
- variables: {
- input: {
- id: workItemId,
- iterationWidget: {
- iterationId: this.parentIterationId,
- },
- },
- },
- });
- },
setSearchKey(value) {
this.search = value;
},
@@ -253,17 +265,13 @@ export default {
},
i18n: {
inputLabel: __('Title'),
- addTaskButtonLabel: s__('WorkItem|Add task'),
- addTasksButtonLabel: s__('WorkItem|Add tasks'),
addChildErrorMessage: s__(
'WorkItem|Something went wrong when trying to add a child. Please try again.',
),
- createChildOptionLabel: s__('WorkItem|Create task'),
createChildErrorMessage: s__(
'WorkItem|Something went wrong when trying to create a child. Please try again.',
),
createPlaceholder: s__('WorkItem|Add a title'),
- addPlaceholder: s__('WorkItem|Search existing tasks'),
fieldValidationMessage: __('Maximum of 255 characters'),
},
};
@@ -296,7 +304,7 @@ export default {
v-model="workItemsToAdd"
:dropdown-items="availableWorkItems"
:loading="isLoading"
- :placeholder="$options.i18n.addPlaceholder"
+ :placeholder="addInputPlaceholder"
menu-class="gl-dropdown-menu-wide dropdown-reduced-height gl-min-h-7!"
class="gl-mb-4"
data-testid="work-item-token-select-input"
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue
new file mode 100644
index 00000000000..f06de2ca048
--- /dev/null
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue
@@ -0,0 +1,244 @@
+<script>
+import { GlButton } from '@gitlab/ui';
+import { isEmpty } from 'lodash';
+import { __ } from '~/locale';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+import { TYPE_WORK_ITEM } from '~/graphql_shared/constants';
+import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import { getParameterByName } from '~/lib/utils/url_utility';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+
+import {
+ FORM_TYPES,
+ WIDGET_TYPE_HIERARCHY,
+ WORK_ITEMS_TREE_TEXT_MAP,
+ WORK_ITEM_TYPE_ENUM_OBJECTIVE,
+ WORK_ITEM_TYPE_ENUM_KEY_RESULT,
+ WORK_ITEM_TYPE_VALUE_OBJECTIVE,
+} from '../../constants';
+import workItemQuery from '../../graphql/work_item.query.graphql';
+import workItemByIidQuery from '../../graphql/work_item_by_iid.query.graphql';
+import OkrActionsSplitButton from './okr_actions_split_button.vue';
+import WorkItemLinksForm from './work_item_links_form.vue';
+import WorkItemLinkChild from './work_item_link_child.vue';
+
+export default {
+ FORM_TYPES,
+ WORK_ITEMS_TREE_TEXT_MAP,
+ WORK_ITEM_TYPE_ENUM_OBJECTIVE,
+ WORK_ITEM_TYPE_ENUM_KEY_RESULT,
+ components: {
+ GlButton,
+ OkrActionsSplitButton,
+ WorkItemLinksForm,
+ WorkItemLinkChild,
+ },
+ mixins: [glFeatureFlagMixin()],
+ props: {
+ workItemType: {
+ type: String,
+ required: true,
+ },
+ workItemId: {
+ type: String,
+ required: true,
+ },
+ children: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ canUpdate: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ projectPath: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ isShownAddForm: false,
+ isOpen: true,
+ error: null,
+ formType: null,
+ childType: null,
+ prefetchedWorkItem: null,
+ };
+ },
+ computed: {
+ toggleIcon() {
+ return this.isOpen ? 'chevron-lg-up' : 'chevron-lg-down';
+ },
+ toggleLabel() {
+ return this.isOpen ? __('Collapse') : __('Expand');
+ },
+ fetchByIid() {
+ return this.glFeatures.useIidInWorkItemsPath && parseBoolean(getParameterByName('iid_path'));
+ },
+ childrenIds() {
+ return this.children.map((c) => c.id);
+ },
+ hasIndirectChildren() {
+ return this.children
+ .map(
+ (child) => child.widgets?.find((widget) => widget.type === WIDGET_TYPE_HIERARCHY) || {},
+ )
+ .some((hierarchy) => hierarchy.hasChildren);
+ },
+ childUrlParams() {
+ const params = {};
+ if (this.fetchByIid) {
+ const iid = getParameterByName('work_item_iid');
+ if (iid) {
+ params.iid = iid;
+ }
+ } else {
+ const workItemId = getParameterByName('work_item_id');
+ if (workItemId) {
+ params.id = convertToGraphQLId(TYPE_WORK_ITEM, workItemId);
+ }
+ }
+ return params;
+ },
+ },
+ mounted() {
+ if (!isEmpty(this.childUrlParams)) {
+ this.addWorkItemQuery(this.childUrlParams);
+ }
+ },
+ methods: {
+ toggle() {
+ this.isOpen = !this.isOpen;
+ },
+ showAddForm(formType, childType) {
+ this.isOpen = true;
+ this.isShownAddForm = true;
+ this.formType = formType;
+ this.childType = childType;
+ this.$nextTick(() => {
+ this.$refs.wiLinksForm.$refs.wiTitleInput?.$el.focus();
+ });
+ },
+ hideAddForm() {
+ this.isShownAddForm = false;
+ },
+ addWorkItemQuery({ id, iid }) {
+ const variables = this.fetchByIid
+ ? {
+ fullPath: this.projectPath,
+ iid,
+ }
+ : {
+ id,
+ };
+ this.$apollo.addSmartQuery('prefetchedWorkItem', {
+ query() {
+ return this.fetchByIid ? workItemByIidQuery : workItemQuery;
+ },
+ variables,
+ update(data) {
+ return this.fetchByIid ? data.workspace.workItems.nodes[0] : data.workItem;
+ },
+ context: {
+ isSingleRequest: true,
+ },
+ });
+ },
+ prefetchWorkItem({ id, iid }) {
+ if (this.workItemType !== WORK_ITEM_TYPE_VALUE_OBJECTIVE) {
+ this.prefetch = setTimeout(
+ () => this.addWorkItemQuery({ id, iid }),
+ DEFAULT_DEBOUNCE_AND_THROTTLE_MS,
+ );
+ }
+ },
+ clearPrefetching() {
+ if (this.prefetch) {
+ clearTimeout(this.prefetch);
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <div
+ class="gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100 gl-bg-gray-10 gl-mt-4"
+ data-testid="work-item-tree"
+ >
+ <div
+ class="gl-px-5 gl-py-3 gl-display-flex gl-justify-content-space-between"
+ :class="{ 'gl-border-b-1 gl-border-b-solid gl-border-b-gray-100': isOpen }"
+ >
+ <div class="gl-display-flex gl-flex-grow-1">
+ <h5 class="gl-m-0 gl-line-height-24">
+ {{ $options.WORK_ITEMS_TREE_TEXT_MAP[workItemType].title }}
+ </h5>
+ </div>
+ <okr-actions-split-button
+ @showCreateObjectiveForm="
+ showAddForm($options.FORM_TYPES.create, $options.WORK_ITEM_TYPE_ENUM_OBJECTIVE)
+ "
+ @showAddObjectiveForm="
+ showAddForm($options.FORM_TYPES.add, $options.WORK_ITEM_TYPE_ENUM_OBJECTIVE)
+ "
+ @showCreateKeyResultForm="
+ showAddForm($options.FORM_TYPES.create, $options.WORK_ITEM_TYPE_ENUM_KEY_RESULT)
+ "
+ @showAddKeyResultForm="
+ showAddForm($options.FORM_TYPES.add, $options.WORK_ITEM_TYPE_ENUM_KEY_RESULT)
+ "
+ />
+ <div class="gl-border-l-1 gl-border-l-solid gl-border-l-gray-100 gl-pl-3 gl-ml-3">
+ <gl-button
+ category="tertiary"
+ size="small"
+ :icon="toggleIcon"
+ :aria-label="toggleLabel"
+ data-testid="toggle-tree"
+ @click="toggle"
+ />
+ </div>
+ </div>
+ <div
+ v-if="isOpen"
+ class="gl-bg-gray-10 gl-rounded-bottom-left-base gl-rounded-bottom-right-base"
+ :class="{ 'gl-p-5 gl-pb-3': !error }"
+ data-testid="tree-body"
+ >
+ <div v-if="!isShownAddForm && !error && children.length === 0" data-testid="tree-empty">
+ <p class="gl-mb-3">
+ {{ $options.WORK_ITEMS_TREE_TEXT_MAP[workItemType].empty }}
+ </p>
+ </div>
+ <work-item-links-form
+ v-if="isShownAddForm"
+ ref="wiLinksForm"
+ data-testid="add-tree-form"
+ :issuable-gid="workItemId"
+ :form-type="formType"
+ :children-type="childType"
+ :children-ids="childrenIds"
+ @addWorkItemChild="$emit('addWorkItemChild', $event)"
+ @cancel="hideAddForm"
+ />
+ <work-item-link-child
+ v-for="child in children"
+ :key="child.id"
+ :project-path="projectPath"
+ :can-update="canUpdate"
+ :issuable-gid="workItemId"
+ :child-item="child"
+ :work-item-type="workItemType"
+ :has-indirect-children="hasIndirectChildren"
+ @mouseover="prefetchWorkItem(child)"
+ @mouseout="clearPrefetching"
+ @removeChild="$emit('removeChild', $event)"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_tree_children.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_tree_children.vue
new file mode 100644
index 00000000000..911cac4de88
--- /dev/null
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_tree_children.vue
@@ -0,0 +1,68 @@
+<script>
+import { createAlert } from '~/flash';
+import { s__ } from '~/locale';
+
+import updateWorkItemMutation from '../../graphql/update_work_item.mutation.graphql';
+
+export default {
+ components: {
+ WorkItemLinkChild: () => import('./work_item_link_child.vue'),
+ },
+ props: {
+ workItemType: {
+ type: String,
+ required: true,
+ },
+ workItemId: {
+ type: String,
+ required: true,
+ },
+ children: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ canUpdate: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ projectPath: {
+ type: String,
+ required: true,
+ },
+ },
+ methods: {
+ async updateWorkItem(childId) {
+ try {
+ await this.$apollo.mutate({
+ mutation: updateWorkItemMutation,
+ variables: { input: { id: childId, hierarchyWidget: { parentId: null } } },
+ });
+ this.$emit('removeChild');
+ } catch (error) {
+ createAlert({
+ message: s__('Hierarchy|Something went wrong while removing a child item.'),
+ captureError: true,
+ error,
+ });
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="gl-ml-6">
+ <work-item-link-child
+ v-for="child in children"
+ :key="child.id"
+ :project-path="projectPath"
+ :can-update="canUpdate"
+ :issuable-gid="workItemId"
+ :child-item="child"
+ :work-item-type="workItemType"
+ @removeChild="updateWorkItem"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/work_items/components/work_item_milestone.vue b/app/assets/javascripts/work_items/components/work_item_milestone.vue
index a8d3b57aae0..6ed230b8ad4 100644
--- a/app/assets/javascripts/work_items/components/work_item_milestone.vue
+++ b/app/assets/javascripts/work_items/components/work_item_milestone.vue
@@ -13,6 +13,7 @@ import { debounce } from 'lodash';
import Tracking from '~/tracking';
import { s__, __ } from '~/locale';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
+import { MILESTONE_STATE } from '~/sidebar/constants';
import projectMilestonesQuery from '~/sidebar/queries/project_milestones.query.graphql';
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
import {
@@ -118,6 +119,7 @@ export default {
return {
'gl-text-gray-500!': this.canUpdate && this.isNoMilestone,
'is-not-focused': !this.isFocused,
+ 'gl-min-w-20': true,
};
},
},
@@ -139,6 +141,7 @@ export default {
return {
fullPath: this.fullPath,
title: this.searchTerm,
+ state: MILESTONE_STATE.ACTIVE,
first: 20,
};
},
@@ -214,9 +217,10 @@ export default {
<template>
<gl-form-group
- class="work-item-dropdown"
+ class="work-item-dropdown gl-flex-nowrap"
:label="$options.i18n.MILESTONE"
- label-class="gl-pb-0! gl-overflow-wrap-break gl-mt-3"
+ label-for="milestone-value"
+ label-class="gl-pb-0! gl-mt-3 gl-overflow-wrap-break"
label-cols="3"
label-cols-lg="2"
>
@@ -229,6 +233,8 @@ export default {
</span>
<gl-dropdown
v-else
+ id="milestone-value"
+ class="gl-pl-0 gl-max-w-full"
:toggle-class="dropdownClasses"
:text="dropdownText"
:loading="updateInProgress"
diff --git a/app/assets/javascripts/work_items/components/work_item_notes.vue b/app/assets/javascripts/work_items/components/work_item_notes.vue
new file mode 100644
index 00000000000..91e90589a93
--- /dev/null
+++ b/app/assets/javascripts/work_items/components/work_item_notes.vue
@@ -0,0 +1,109 @@
+<script>
+import { GlSkeletonLoader } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import SystemNote from '~/work_items/components/notes/system_note.vue';
+import { i18n, DEFAULT_PAGE_SIZE_NOTES } from '~/work_items/constants';
+import { getWorkItemNotesQuery } from '~/work_items/utils';
+
+export default {
+ i18n: {
+ ACTIVITY_LABEL: s__('WorkItem|Activity'),
+ },
+ loader: {
+ repeat: 10,
+ width: 1000,
+ height: 40,
+ },
+ components: {
+ SystemNote,
+ GlSkeletonLoader,
+ },
+ props: {
+ workItemId: {
+ type: String,
+ required: true,
+ },
+ queryVariables: {
+ type: Object,
+ required: true,
+ },
+ fullPath: {
+ type: String,
+ required: true,
+ },
+ fetchByIid: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ areNotesLoading() {
+ return this.$apollo.queries.workItemNotes.loading;
+ },
+ notes() {
+ return this.workItemNotes?.nodes;
+ },
+ pageInfo() {
+ return this.workItemNotes?.pageInfo;
+ },
+ },
+ apollo: {
+ workItemNotes: {
+ query() {
+ return getWorkItemNotesQuery(this.fetchByIid);
+ },
+ context: {
+ isSingleRequest: true,
+ },
+ variables() {
+ return {
+ ...this.queryVariables,
+ pageSize: DEFAULT_PAGE_SIZE_NOTES,
+ };
+ },
+ update(data) {
+ const workItemWidgets = this.fetchByIid
+ ? data.workspace?.workItems?.nodes[0]?.widgets
+ : data.workItem?.widgets;
+ return workItemWidgets.find((widget) => widget.type === 'NOTES').discussions || [];
+ },
+ skip() {
+ return !this.queryVariables.id && !this.queryVariables.iid;
+ },
+ error() {
+ this.$emit('error', i18n.fetchError);
+ },
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="gl-border-t gl-mt-5">
+ <label class="gl-mb-0">{{ $options.i18n.ACTIVITY_LABEL }}</label>
+ <div v-if="areNotesLoading" class="gl-mt-5">
+ <gl-skeleton-loader
+ v-for="index in $options.loader.repeat"
+ :key="index"
+ :width="$options.loader.width"
+ :height="$options.loader.height"
+ preserve-aspect-ratio="xMinYMax meet"
+ >
+ <circle cx="20" cy="20" r="16" />
+ <rect width="500" x="45" y="15" height="10" rx="4" />
+ </gl-skeleton-loader>
+ </div>
+ <div v-else class="issuable-discussion gl-mb-5 work-item-notes">
+ <template v-if="notes && notes.length">
+ <ul class="notes main-notes-list timeline">
+ <system-note
+ v-for="note in notes"
+ :key="note.notes.nodes[0].id"
+ :note="note.notes.nodes[0]"
+ />
+ </ul>
+ </template>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/work_items/components/work_item_type_icon.vue b/app/assets/javascripts/work_items/components/work_item_type_icon.vue
index 96a6493357c..32678e29fa4 100644
--- a/app/assets/javascripts/work_items/components/work_item_type_icon.vue
+++ b/app/assets/javascripts/work_items/components/work_item_type_icon.vue
@@ -33,6 +33,11 @@ export default {
},
computed: {
iconName() {
+ // TODO: Remove this once https://gitlab.com/gitlab-org/gitlab-svgs/-/merge_requests/865
+ // is merged and updated in GitLab repo.
+ if (this.workItemIconName === 'issue-type-keyresult') {
+ return 'issue-type-key-result';
+ }
return (
this.workItemIconName || WORK_ITEMS_TYPE_MAP[this.workItemType]?.icon || 'issue-type-issue'
);
diff --git a/app/assets/javascripts/work_items/constants.js b/app/assets/javascripts/work_items/constants.js
index 8b47c24de7d..3cd17f4d360 100644
--- a/app/assets/javascripts/work_items/constants.js
+++ b/app/assets/javascripts/work_items/constants.js
@@ -16,17 +16,23 @@ export const WIDGET_TYPE_DESCRIPTION = 'DESCRIPTION';
export const WIDGET_TYPE_LABELS = 'LABELS';
export const WIDGET_TYPE_START_AND_DUE_DATE = 'START_AND_DUE_DATE';
export const WIDGET_TYPE_WEIGHT = 'WEIGHT';
+export const WIDGET_TYPE_PROGRESS = 'PROGRESS';
export const WIDGET_TYPE_HIERARCHY = 'HIERARCHY';
export const WIDGET_TYPE_MILESTONE = 'MILESTONE';
export const WIDGET_TYPE_ITERATION = 'ITERATION';
-
-export const WORK_ITEM_VIEWED_STORAGE_KEY = 'gl-show-work-item-banner';
+export const WIDGET_TYPE_NOTES = 'NOTES';
+export const WIDGET_TYPE_HEALTH_STATUS = 'HEALTH_STATUS';
export const WORK_ITEM_TYPE_ENUM_INCIDENT = 'INCIDENT';
export const WORK_ITEM_TYPE_ENUM_ISSUE = 'ISSUE';
export const WORK_ITEM_TYPE_ENUM_TASK = 'TASK';
export const WORK_ITEM_TYPE_ENUM_TEST_CASE = 'TEST_CASE';
export const WORK_ITEM_TYPE_ENUM_REQUIREMENTS = 'REQUIREMENTS';
+export const WORK_ITEM_TYPE_ENUM_OBJECTIVE = 'OBJECTIVE';
+export const WORK_ITEM_TYPE_ENUM_KEY_RESULT = 'KEY_RESULT';
+
+export const WORK_ITEM_TYPE_VALUE_ISSUE = 'Issue';
+export const WORK_ITEM_TYPE_VALUE_OBJECTIVE = 'Objective';
export const i18n = {
fetchErrorTitle: s__('WorkItem|Work item not found'),
@@ -61,6 +67,13 @@ export const I18N_WORK_ITEM_FETCH_ITERATIONS_ERROR = s__(
'WorkItem|Something went wrong when fetching iterations. Please try again.',
);
+export const I18N_WORK_ITEM_CREATE_BUTTON_LABEL = s__('WorkItem|Create %{workItemType}');
+export const I18N_WORK_ITEM_ADD_BUTTON_LABEL = s__('WorkItem|Add %{workItemType}');
+export const I18N_WORK_ITEM_ADD_MULTIPLE_BUTTON_LABEL = s__('WorkItem|Add %{workItemType}s');
+export const I18N_WORK_ITEM_SEARCH_INPUT_PLACEHOLDER = s__(
+ 'WorkItem|Search existing %{workItemType}s',
+);
+
export const sprintfWorkItem = (msg, workItemTypeArg) => {
const workItemType = workItemTypeArg || s__('WorkItem|Work item');
return capitalizeFirstCharacter(
@@ -100,11 +113,45 @@ export const WORK_ITEMS_TYPE_MAP = {
icon: `issue-type-requirements`,
name: s__('WorkItem|Requirements'),
},
+ [WORK_ITEM_TYPE_ENUM_OBJECTIVE]: {
+ icon: `issue-type-objective`,
+ name: s__('WorkItem|Objective'),
+ },
+ [WORK_ITEM_TYPE_ENUM_KEY_RESULT]: {
+ icon: `issue-type-issue`,
+ name: s__('WorkItem|Key Result'),
+ },
+};
+
+export const WORK_ITEMS_TREE_TEXT_MAP = {
+ [WORK_ITEM_TYPE_VALUE_OBJECTIVE]: {
+ title: s__('WorkItem|Child objectives and key results'),
+ empty: s__('WorkItem|No objectives or key results are currently assigned.'),
+ },
+ [WORK_ITEM_TYPE_VALUE_ISSUE]: {
+ title: s__('WorkItem|Tasks'),
+ empty: s__(
+ 'WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts.',
+ ),
+ },
+};
+
+export const WORK_ITEM_NAME_TO_ICON_MAP = {
+ Issue: 'issue-type-issue',
+ Task: 'issue-type-task',
+ Objective: 'issue-type-objective',
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ 'Key Result': 'issue-type-key-result',
};
export const FORM_TYPES = {
create: 'create',
add: 'add',
+ [WORK_ITEM_TYPE_ENUM_OBJECTIVE]: {
+ icon: `issue-type-issue`,
+ name: s__('WorkItem|Objective'),
+ },
};
export const DEFAULT_PAGE_SIZE_ASSIGNEES = 10;
+export const DEFAULT_PAGE_SIZE_NOTES = 100;
diff --git a/app/assets/javascripts/work_items/graphql/discussion.fragment.graphql b/app/assets/javascripts/work_items/graphql/discussion.fragment.graphql
new file mode 100644
index 00000000000..62ced6bdfea
--- /dev/null
+++ b/app/assets/javascripts/work_items/graphql/discussion.fragment.graphql
@@ -0,0 +1,12 @@
+#import "~/graphql_shared/fragments/user.fragment.graphql"
+
+fragment Discussion on Note {
+ id
+ body
+ bodyHtml
+ systemNoteIconName
+ createdAt
+ author {
+ ...User
+ }
+}
diff --git a/app/assets/javascripts/work_items/graphql/milestone.fragment.graphql b/app/assets/javascripts/work_items/graphql/milestone.fragment.graphql
index 58140aff89e..5c93370aac9 100644
--- a/app/assets/javascripts/work_items/graphql/milestone.fragment.graphql
+++ b/app/assets/javascripts/work_items/graphql/milestone.fragment.graphql
@@ -2,4 +2,7 @@ fragment MilestoneFragment on Milestone {
expired
id
title
+ state
+ startDate
+ dueDate
}
diff --git a/app/assets/javascripts/work_items/graphql/work_item_links.query.graphql b/app/assets/javascripts/work_items/graphql/work_item_links.query.graphql
index 7b63d9c7ca3..7fcf622cdb2 100644
--- a/app/assets/javascripts/work_items/graphql/work_item_links.query.graphql
+++ b/app/assets/javascripts/work_items/graphql/work_item_links.query.graphql
@@ -20,9 +20,12 @@ query workItemLinksQuery($id: WorkItemID!) {
children {
nodes {
id
+ iid
confidential
workItemType {
id
+ name
+ iconName
}
title
state
diff --git a/app/assets/javascripts/work_items/graphql/work_item_metadata_widgets.fragment.graphql b/app/assets/javascripts/work_items/graphql/work_item_metadata_widgets.fragment.graphql
new file mode 100644
index 00000000000..baefcdaea93
--- /dev/null
+++ b/app/assets/javascripts/work_items/graphql/work_item_metadata_widgets.fragment.graphql
@@ -0,0 +1,29 @@
+#import "~/graphql_shared/fragments/label.fragment.graphql"
+#import "~/graphql_shared/fragments/user.fragment.graphql"
+#import "~/work_items/graphql/milestone.fragment.graphql"
+
+fragment WorkItemMetadataWidgets on WorkItemWidget {
+ ... on WorkItemWidgetMilestone {
+ type
+ milestone {
+ ...MilestoneFragment
+ }
+ }
+ ... on WorkItemWidgetAssignees {
+ type
+ assignees {
+ nodes {
+ ...User
+ }
+ }
+ }
+ ... on WorkItemWidgetLabels {
+ type
+ allowsScopedLabels
+ labels {
+ nodes {
+ ...Label
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/work_items/graphql/work_item_notes.query.graphql b/app/assets/javascripts/work_items/graphql/work_item_notes.query.graphql
new file mode 100644
index 00000000000..9439f22f955
--- /dev/null
+++ b/app/assets/javascripts/work_items/graphql/work_item_notes.query.graphql
@@ -0,0 +1,27 @@
+#import "~/graphql_shared/fragments/page_info.fragment.graphql"
+#import "~/work_items/graphql/discussion.fragment.graphql"
+
+query workItemNotes($id: WorkItemID!, $after: String, $pageSize: Int) {
+ workItem(id: $id) {
+ id
+ iid
+ widgets {
+ ... on WorkItemWidgetNotes {
+ type
+ discussions(first: $pageSize, after: $after, filter: ONLY_ACTIVITY) {
+ pageInfo {
+ ...PageInfo
+ }
+ nodes {
+ id
+ notes {
+ nodes {
+ ...Discussion
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/work_items/graphql/work_item_notes_by_iid.query.graphql b/app/assets/javascripts/work_items/graphql/work_item_notes_by_iid.query.graphql
new file mode 100644
index 00000000000..3e0960f3f54
--- /dev/null
+++ b/app/assets/javascripts/work_items/graphql/work_item_notes_by_iid.query.graphql
@@ -0,0 +1,32 @@
+#import "~/graphql_shared/fragments/page_info.fragment.graphql"
+#import "~/work_items/graphql/discussion.fragment.graphql"
+
+query workItemNotesByIid($fullPath: ID!, $iid: String, $after: String, $pageSize: Int) {
+ workspace: project(fullPath: $fullPath) {
+ id
+ workItems(iid: $iid) {
+ nodes {
+ id
+ iid
+ widgets {
+ ... on WorkItemWidgetNotes {
+ type
+ discussions(first: $pageSize, after: $after, filter: ONLY_ACTIVITY) {
+ pageInfo {
+ ...PageInfo
+ }
+ nodes {
+ id
+ notes {
+ nodes {
+ ...Discussion
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/work_items/graphql/work_item_tree.query.graphql b/app/assets/javascripts/work_items/graphql/work_item_tree.query.graphql
new file mode 100644
index 00000000000..006ca29e01c
--- /dev/null
+++ b/app/assets/javascripts/work_items/graphql/work_item_tree.query.graphql
@@ -0,0 +1,53 @@
+#import "~/graphql_shared/fragments/label.fragment.graphql"
+#import "~/graphql_shared/fragments/user.fragment.graphql"
+#import "./work_item_metadata_widgets.fragment.graphql"
+
+query workItemTreeQuery($id: WorkItemID!) {
+ workItem(id: $id) {
+ id
+ workItemType {
+ id
+ name
+ iconName
+ }
+ title
+ userPermissions {
+ deleteWorkItem
+ updateWorkItem
+ }
+ confidential
+ widgets {
+ type
+ ... on WorkItemWidgetHierarchy {
+ type
+ parent {
+ id
+ }
+ children {
+ nodes {
+ id
+ iid
+ confidential
+ workItemType {
+ id
+ name
+ iconName
+ }
+ title
+ state
+ createdAt
+ closedAt
+ widgets {
+ ... on WorkItemWidgetHierarchy {
+ type
+ hasChildren
+ }
+ ...WorkItemMetadataWidgets
+ }
+ }
+ }
+ }
+ ...WorkItemMetadataWidgets
+ }
+ }
+}
diff --git a/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql b/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql
index b9715c21c27..cf3374e1737 100644
--- a/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql
+++ b/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql
@@ -1,6 +1,7 @@
#import "~/graphql_shared/fragments/label.fragment.graphql"
#import "~/graphql_shared/fragments/user.fragment.graphql"
#import "~/work_items/graphql/milestone.fragment.graphql"
+#import "./work_item_metadata_widgets.fragment.graphql"
fragment WorkItemWidgets on WorkItemWidget {
... on WorkItemWidgetDescription {
@@ -38,15 +39,39 @@ fragment WorkItemWidgets on WorkItemWidget {
}
... on WorkItemWidgetHierarchy {
type
+ hasChildren
parent {
id
iid
title
confidential
+ webUrl
+ workItemType {
+ id
+ name
+ iconName
+ }
}
children {
nodes {
id
+ confidential
+ workItemType {
+ id
+ name
+ iconName
+ }
+ title
+ state
+ createdAt
+ closedAt
+ widgets {
+ ... on WorkItemWidgetHierarchy {
+ type
+ hasChildren
+ }
+ ...WorkItemMetadataWidgets
+ }
}
}
}
@@ -56,4 +81,7 @@ fragment WorkItemWidgets on WorkItemWidget {
...MilestoneFragment
}
}
+ ... on WorkItemWidgetNotes {
+ type
+ }
}
diff --git a/app/assets/javascripts/work_items/index.js b/app/assets/javascripts/work_items/index.js
index 4fbcdfe2b96..a056fde6928 100644
--- a/app/assets/javascripts/work_items/index.js
+++ b/app/assets/javascripts/work_items/index.js
@@ -6,7 +6,14 @@ import { createRouter } from './router';
export const initWorkItemsRoot = () => {
const el = document.querySelector('#js-work-items');
- const { fullPath, hasIssueWeightsFeature, issuesListPath, hasIterationsFeature } = el.dataset;
+ const {
+ fullPath,
+ hasIssueWeightsFeature,
+ issuesListPath,
+ hasIterationsFeature,
+ hasOkrsFeature,
+ hasIssuableHealthStatusFeature,
+ } = el.dataset;
return new Vue({
el,
@@ -15,9 +22,12 @@ export const initWorkItemsRoot = () => {
apolloProvider,
provide: {
fullPath,
+ projectPath: fullPath,
hasIssueWeightsFeature: parseBoolean(hasIssueWeightsFeature),
+ hasOkrsFeature: parseBoolean(hasOkrsFeature),
issuesListPath,
hasIterationsFeature: parseBoolean(hasIterationsFeature),
+ hasIssuableHealthStatusFeature: parseBoolean(hasIssuableHealthStatusFeature),
},
render(createElement) {
return createElement(App);
diff --git a/app/assets/javascripts/work_items/pages/work_item_root.vue b/app/assets/javascripts/work_items/pages/work_item_root.vue
index 1c00bd16263..d04d4942253 100644
--- a/app/assets/javascripts/work_items/pages/work_item_root.vue
+++ b/app/assets/javascripts/work_items/pages/work_item_root.vue
@@ -70,6 +70,10 @@ export default {
<template>
<div>
<gl-alert v-if="error" variant="danger" @dismiss="error = ''">{{ error }}</gl-alert>
- <work-item-detail :work-item-id="gid" :iid="id" @deleteWorkItem="deleteWorkItem($event)" />
+ <work-item-detail
+ :work-item-id="gid"
+ :work-item-iid="id"
+ @deleteWorkItem="deleteWorkItem($event)"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/work_items/utils.js b/app/assets/javascripts/work_items/utils.js
index 17f9c882c2d..e58fd19ea31 100644
--- a/app/assets/javascripts/work_items/utils.js
+++ b/app/assets/javascripts/work_items/utils.js
@@ -1,6 +1,12 @@
import workItemQuery from './graphql/work_item.query.graphql';
import workItemByIidQuery from './graphql/work_item_by_iid.query.graphql';
+import workItemNotesIdQuery from './graphql/work_item_notes.query.graphql';
+import workItemNotesByIidQuery from './graphql/work_item_notes_by_iid.query.graphql';
export function getWorkItemQuery(isFetchedByIid) {
return isFetchedByIid ? workItemByIidQuery : workItemQuery;
}
+
+export function getWorkItemNotesQuery(isFetchedByIid) {
+ return isFetchedByIid ? workItemNotesByIidQuery : workItemNotesIdQuery;
+}
diff --git a/app/assets/stylesheets/_page_specific_files.scss b/app/assets/stylesheets/_page_specific_files.scss
index 6878e9a10d7..fa5d2bf7972 100644
--- a/app/assets/stylesheets/_page_specific_files.scss
+++ b/app/assets/stylesheets/_page_specific_files.scss
@@ -4,20 +4,16 @@
@import './pages/events';
@import './pages/groups';
@import './pages/hierarchy';
-@import './pages/issuable';
@import './pages/issues';
@import './pages/labels';
@import './pages/login';
@import './pages/ml_experiment_tracking';
@import './pages/merge_requests';
-@import './pages/monitor';
@import './pages/note_form';
@import './pages/notes';
@import './pages/pipelines';
@import './pages/profile';
@import './pages/projects';
@import './pages/registry';
-@import './pages/search';
@import './pages/settings';
@import './pages/storage_quota';
-@import './pages/users';
diff --git a/app/assets/stylesheets/components/content_editor.scss b/app/assets/stylesheets/components/content_editor.scss
index 1b6a0208ca7..44b06c0ff12 100644
--- a/app/assets/stylesheets/components/content_editor.scss
+++ b/app/assets/stylesheets/components/content_editor.scss
@@ -130,6 +130,18 @@
background-color: var(--gl-color-chip-color);
}
+.content-editor-comment {
+ &::before {
+ content: '<!--';
+ }
+
+ &::after {
+ content: '-->';
+ }
+}
+
+
+
.bubble-menu-form {
width: 320px;
}
diff --git a/app/assets/stylesheets/components/ref_selector.scss b/app/assets/stylesheets/components/ref_selector.scss
index ded911c2492..f7a9367499e 100644
--- a/app/assets/stylesheets/components/ref_selector.scss
+++ b/app/assets/stylesheets/components/ref_selector.scss
@@ -6,7 +6,7 @@
width: 20rem;
&,
- .gl-new-dropdown-inner {
+ .gl-dropdown-inner {
max-height: $dropdown-max-height-lg;
}
}
diff --git a/app/assets/stylesheets/fonts.scss b/app/assets/stylesheets/fonts.scss
new file mode 100644
index 00000000000..a6ecca88bd4
--- /dev/null
+++ b/app/assets/stylesheets/fonts.scss
@@ -0,0 +1,32 @@
+/* -------------------------------------------------------
+Inter variable font.
+
+Usage:
+ html { font-family: 'GitLab Sans', sans-serif; }
+*/
+@font-face {
+ font-family: 'GitLab Sans';
+ font-weight: 100 900;
+ font-display: optional;
+ font-style: normal;
+ font-named-instance: 'Regular'; /* stylelint-disable property-no-unknown */
+ src: font-url('gitlab-sans/GitLabSans.woff2') format('woff2');
+}
+
+/* -------------------------------------------------------
+Monospaced font: JetBrains Mono.
+
+Usage:
+ html { font-family: 'JetBrains Mono', sans-serif; }
+*/
+@font-face {
+ font-family: 'JetBrains Mono';
+ font-display: optional;
+ font-style: normal;
+ src: font-url('jetbrains-mono/JetBrainsMono.woff2') format('woff2');
+}
+
+:root {
+ --default-mono-font: 'JetBrains Mono', 'Menlo';
+ --default-regular-font: 'GitLab Sans', -apple-system;
+}
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index be8a890320f..14e756a5c21 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -1,9 +1,9 @@
/** COLORS **/
.cgray { color: $gl-text-color; }
-.clgray { color: $common-gray-light; }
+.clgray { color: $gray-200; }
.cred { color: $red-500; }
.cgreen { color: $green-600; }
-.cdark { color: $common-gray-dark; }
+.cdark { color: $gray-800; }
.fwhite { fill: $white; }
.fgray { fill: $gray-500; }
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index c5a34ca4b31..0acda85f527 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -5,7 +5,7 @@
// for Snippets is introduced and Clone button is relocated, we won't
// need this style.
// Issue for the refactoring: https://gitlab.com/gitlab-org/gitlab/-/issues/213327
- &.gl-new-dropdown button.dropdown-toggle {
+ &.gl-dropdown button.dropdown-toggle {
@include gl-display-inline-flex;
}
@@ -41,7 +41,7 @@
max-height: $extended-max-height;
// See comment below for explanation
- .gl-new-dropdown-inner {
+ .gl-dropdown-inner {
max-height: $extended-max-height - 2px;
}
}
@@ -54,12 +54,12 @@
width: 100%;
}
- // `GlDropdown` specifies the `max-height` of `.gl-new-dropdown-inner`
+ // `GlDropdown` specifies the `max-height` of `.gl-dropdown-inner`
// as `$dropdown-max-height`, but the `max-height` rule above forces
// the parent `.dropdown-menu` to be _slightly_ too small because of
// the 1px borders. The workaround below overrides the @gitlab/ui style
// to avoid a double scroll bar.
- .gl-new-dropdown-inner {
+ .gl-dropdown-inner {
max-height: $dropdown-max-height - 2px;
}
}
@@ -285,7 +285,7 @@
list-style: none;
> a,
- button,
+ > button,
.gl-button.btn-link,
.menu-item {
@include dropdown-link;
@@ -1027,7 +1027,7 @@
// This class won't be needed once we can add a prop for this in the GitLab UI component
// https://gitlab.com/gitlab-org/gitlab-ui/-/issues/966
-.gl-new-dropdown {
+.gl-dropdown {
.gl-dropdown-menu-wide {
width: $gl-dropdown-width-wide;
}
@@ -1035,7 +1035,7 @@
// This class won't be needed once we can add a prop for this in the GitLab UI component
// https://gitlab.com/gitlab-org/gitlab-ui/-/issues/966
-.gl-new-dropdown.gl-dropdown-menu-full-width {
+.gl-dropdown.gl-dropdown-menu-full-width {
.dropdown-menu {
width: 100%;
}
diff --git a/app/assets/stylesheets/framework/emojis.scss b/app/assets/stylesheets/framework/emojis.scss
index 68a3493670d..16ad6f62c64 100644
--- a/app/assets/stylesheets/framework/emojis.scss
+++ b/app/assets/stylesheets/framework/emojis.scss
@@ -36,7 +36,7 @@ gl-emoji {
}
}
-.emoji-picker .gl-new-dropdown .dropdown-menu {
+.emoji-picker .gl-dropdown .dropdown-menu {
width: 350px;
}
@@ -48,6 +48,6 @@ gl-emoji {
border-bottom-color: var(--gl-theme-accent, $theme-indigo-500);
}
-.emoji-picker .gl-new-dropdown-inner > :last-child {
+.emoji-picker .gl-dropdown-inner > :last-child {
padding-bottom: 0;
}
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index 37b61d36911..b35175f4ef6 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -252,7 +252,7 @@
z-index: 1;
&:hover .clear-search-icon {
- color: $common-gray-dark;
+ color: $gray-800;
}
}
}
@@ -433,8 +433,7 @@
.search-token-target-branch {
.value {
- font-family: $monospace-font;
- font-size: $gl-font-size-monospace;
+ @include gl-font-monospace;
}
}
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index bba995a6de3..e86edff3f13 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -14,6 +14,28 @@ input[type='text'].danger {
text-shadow: 0 1px 1px $white;
}
+/**
+ * When form input type is number, Firefox & Safari show the up/down arrows
+ * on the right side of the input persistently, while Chrome shows it only
+ * on hover or focus, this fix allows us to hide the arrows in all browsers.
+ * You can conditionally add/remove `hide-spinners` class to have consistent
+ * behaviour across browsers.
+ */
+
+/* stylelint-disable property-no-vendor-prefix */
+input[type='number'].hide-spinners {
+ -moz-appearance: textfield;
+ appearance: textfield;
+
+ &::-webkit-inner-spin-button,
+ &::-webkit-outer-spin-button {
+ -webkit-appearance: none;
+ appearance: none;
+ margin: 0;
+ }
+}
+/* stylelint-enable property-no-vendor-prefix */
+
.datetime-controls {
select {
width: 100px;
@@ -204,6 +226,22 @@ label {
}
}
+.show-password-complexity-errors {
+ .form-control:not(textarea) {
+ height: 34px;
+ }
+
+ .password-complexity-error-outline {
+ border: 1px solid $red-500;
+
+ &:focus {
+ box-shadow: 0 0 0 1px $red-500 inset, 0 1px 1px $gl-field-focus-shadow inset,
+ 0 0 4px 0 $gl-field-focus-shadow-error;
+ border: 0 none;
+ }
+ }
+}
+
.input-icon-wrapper,
.select-wrapper {
position: relative;
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index ed41d10f3b2..4b1efcc1e9a 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -1,3 +1,7 @@
+$search-input-field-min-width: 320px;
+$search-input-field-max-width: 640px;
+$search-input-field-x-min-width: 200px;
+
.navbar-gitlab {
padding: 0 16px;
z-index: $header-zindex;
@@ -76,6 +80,57 @@
.navbar-collapse > ul.nav > li:not(.d-none) {
margin: 0 2px;
}
+
+ .header-search-new {
+ max-width: $search-input-field-max-width;
+ }
+
+ &.header-search-is-active {
+ .global-search-container {
+ flex-grow: 1;
+ }
+ }
+ }
+
+ .header-search {
+ min-width: $search-input-field-min-width;
+
+ // This is a temporary workaround!
+ // the button in GitLab UI Search components need to be updated to not be the small size
+ // see in Figma: https://www.figma.com/file/qEddyqCrI7kPSBjGmwkZzQ/Component-library?node-id=43905%3A45540
+ .gl-search-box-by-type-clear.btn-sm {
+ padding: 0.5rem !important;
+ }
+
+ @include media-breakpoint-between(md, lg) {
+ min-width: $search-input-field-x-min-width;
+ }
+
+ &.is-searching {
+ .in-search-scope-help {
+ position: absolute;
+ top: $gl-spacing-scale-2;
+ right: 2.125rem;
+ z-index: 2;
+ }
+ }
+
+ &.is-not-focused {
+ .gl-search-box-by-type-clear {
+ display: none;
+ }
+ }
+
+ .keyboard-shortcut-helper {
+ transform: translateY(calc(50% - 2px));
+ box-shadow: none;
+ border-color: transparent;
+ }
+ }
+
+ .header-search-dropdown-menu {
+ max-height: $dropdown-max-height;
+ top: 100%;
}
.navbar-collapse {
@@ -555,7 +610,7 @@
}
.top-nav-container-view {
- .gl-new-dropdown & .gl-search-box-by-type {
+ .gl-dropdown & .gl-search-box-by-type {
@include gl-m-0;
}
diff --git a/app/assets/stylesheets/framework/kbd.scss b/app/assets/stylesheets/framework/kbd.scss
index 7dd0ae47834..16e0214c703 100644
--- a/app/assets/stylesheets/framework/kbd.scss
+++ b/app/assets/stylesheets/framework/kbd.scss
@@ -1,10 +1,10 @@
kbd {
display: inline-block;
padding: 3px 5px;
- font-size: $gl-font-size-monospace-sm;
+ @include gl-font-sm;
line-height: 10px;
color: var(--gray-700, $gray-700);
- vertical-align: middle;
+ vertical-align: unset;
background-color: var(--gray-10, $gray-10);
border-width: 1px;
border-style: solid;
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 47856f1a0d3..628406d5889 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -450,7 +450,7 @@
}
@mixin avatar-counter($border-radius: 1em) {
- background-color: $gray-darkest;
+ background-color: $gray-400;
color: $white;
border: 1px solid $gray-normal;
border-radius: $border-radius;
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 7878e08e549..eb34d91476b 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -198,17 +198,17 @@
}
}
+ $line-height: map-get($spacers, 4) + px-to-rem(2px);
+
&-icon {
/**
* 2px extra is to give a little more height than needed
* to hide timeline line before/after the element starts/ends
*/
- height: map-get($spacers, 4) + px-to-rem(2px);
+ height: $line-height;
z-index: 1;
position: relative;
- top: -3px;
padding: $gl-padding-4 0;
- background-color: $body-bg;
&.opened {
color: $green-500;
@@ -220,7 +220,7 @@
}
&-content {
- line-height: initial;
+ line-height: $line-height;
margin-left: $gl-padding-8;
}
}
@@ -280,3 +280,639 @@
grid-area: user;
}
}
+
+@mixin right-sidebar {
+ position: fixed;
+ top: $header-height;
+ // Default value for CSS var must contain a unit
+ // stylelint-disable-next-line length-zero-no-unit
+ bottom: var(--review-bar-height, 0px);
+ right: 0;
+ transition: width $gl-transition-duration-medium;
+ background-color: $white;
+ z-index: 200;
+ overflow: hidden;
+
+}
+
+.right-sidebar {
+ &:not(.right-sidebar-merge-requests) {
+ @include right-sidebar;
+ }
+
+ &.right-sidebar-merge-requests {
+ @include media-breakpoint-down(md) {
+ @include right-sidebar;
+ z-index: 251;
+ }
+ }
+
+ @include media-breakpoint-down(sm) {
+ z-index: 251;
+ }
+
+ a:not(.btn) {
+ color: inherit;
+
+ &:hover {
+ color: $blue-800;
+ }
+ }
+
+ .gl-label .gl-label-link:hover {
+ color: inherit;
+ }
+
+ .btn-link {
+ color: inherit;
+ }
+
+ .issuable-header-text {
+ margin-top: 7px;
+ }
+
+ .gutter-toggle {
+ display: flex;
+ align-items: center;
+ margin-left: 20px;
+ padding: 4px;
+ border-radius: 4px;
+ height: 24px;
+
+ &:hover {
+ color: $gl-text-color;
+ background: $gray-50;
+ }
+
+ &:hover,
+ &:focus {
+ text-decoration: none;
+ }
+ }
+
+ &.right-sidebar-merge-requests {
+ .block,
+ .sidebar-contained-width,
+ .issuable-sidebar-header {
+ width: 100%;
+ }
+
+ .block {
+ @include media-breakpoint-up(lg) {
+ padding: $gl-spacing-scale-4 0 $gl-spacing-scale-5;
+ }
+
+ &.participants {
+ border-bottom: 0;
+ }
+ }
+ }
+
+ .block,
+ .sidebar-contained-width,
+ .issuable-sidebar-header {
+ @include clearfix;
+ padding: $gl-spacing-scale-4 0 $gl-spacing-scale-5;
+ border-bottom: 1px solid $border-gray-normal;
+ // This prevents the mess when resizing the sidebar
+ // of elements repositioning themselves..
+ width: $gutter-inner-width;
+ // --
+
+ &:last-child {
+ border: 0;
+ }
+
+ &.assignee {
+ .author-link {
+ display: block;
+ position: relative;
+
+ &:hover {
+ .author {
+ text-decoration: underline;
+ }
+ }
+ }
+ }
+
+ &.time-tracking,
+ &.participants,
+ &.subscriptions,
+ &.with-sub-blocks {
+ padding-top: $gl-spacing-scale-5;
+ }
+ }
+
+ .block-first {
+ padding-top: 0;
+ }
+
+ .title {
+ color: $gl-text-color;
+ line-height: $gl-line-height-20;
+
+ .avatar {
+ margin-left: 0;
+ }
+ }
+
+ .selectbox {
+ display: none;
+
+ &.show {
+ display: block;
+ }
+ }
+
+ .btn-clipboard:hover {
+ color: $gl-text-color;
+ }
+
+ .issuable-sidebar {
+ height: 100%;
+
+ &:not(.is-merge-request) {
+ overflow-y: scroll;
+ overflow-x: hidden;
+ -webkit-overflow-scrolling: touch;
+ }
+
+ &.is-merge-request {
+ @include media-breakpoint-down(sm) {
+ overflow-y: scroll;
+ overflow-x: hidden;
+ -webkit-overflow-scrolling: touch;
+ }
+ }
+ }
+
+ &.right-sidebar-expanded {
+ &:not(.right-sidebar-merge-requests) {
+ width: $gutter-width;
+ }
+
+ .value {
+ line-height: 1;
+ }
+
+ .issuable-sidebar {
+ padding: 0 20px;
+
+ &.is-merge-request {
+ @include media-breakpoint-up(lg) {
+ padding: 0;
+
+ .issuable-context-form {
+ --initial-top: calc(#{$header-height} + 76px);
+ --top: var(--initial-top);
+
+ @include gl-sticky;
+ @include gl-overflow-auto;
+
+ top: var(--top);
+ height: calc(100vh - var(--top));
+ padding: 0 15px;
+ margin-bottom: calc(var(--top) * -1);
+
+ .with-performance-bar & {
+ --top: calc(var(--initial-top) + #{$performance-bar-height});
+ }
+
+ .with-system-header & {
+ --top: calc(var(--initial-top) + #{$system-header-height});
+ }
+
+ .with-performance-bar.with-system-header & {
+ --top: calc(var(--initial-top) + #{$system-header-height} + #{$performance-bar-height});
+ }
+ }
+ }
+ }
+ }
+
+ &:not(.boards-sidebar):not([data-signed-in]):not([data-always-show-toggle]) {
+ .issuable-sidebar-header {
+ display: none;
+ }
+ }
+
+ .light {
+ font-weight: $gl-font-weight-normal;
+ }
+
+ .no-value {
+ color: $gl-text-color-secondary;
+ }
+
+ .sidebar-collapsed-icon {
+ display: none;
+ }
+
+ .gutter-toggle {
+ text-align: center;
+ }
+
+ .title .gutter-toggle {
+ margin-top: 0;
+ }
+
+ .assignee .user-list .avatar {
+ margin: 0;
+ }
+
+ .hide-expanded {
+ display: none;
+ }
+ }
+
+ &.right-sidebar-collapsed {
+ /* Extra small devices (phones, less than 768px) */
+ display: none;
+ /* Small devices (tablets, 768px and up) */
+
+ &:not(.right-sidebar-merge-requests) {
+ @include media-breakpoint-up(sm) {
+ display: block;
+ }
+ }
+
+ &.right-sidebar-merge-requests {
+ @include media-breakpoint-up(lg) {
+ display: block;
+ }
+ }
+
+ width: $gutter-collapsed-width;
+ padding: 0;
+
+ .block,
+ .sidebar-contained-width,
+ .issuable-sidebar-header {
+ width: $gutter-collapsed-width - 2px;
+ padding: 0;
+ border-bottom: 0;
+ overflow: hidden;
+ }
+
+ .block,
+ .gutter-toggle,
+ .sidebar-collapsed-container {
+ &.with-sub-blocks .sub-block:hover,
+ &:not(.with-sub-blocks):hover {
+ background-color: $gray-100;
+ }
+ }
+
+ .participants {
+ border-bottom: 1px solid $border-gray-normal;
+ }
+
+ .hide-collapsed {
+ display: none;
+ }
+
+ .gutter-toggle {
+ width: 100%;
+ height: $sidebar-toggle-height;
+ margin-left: 0;
+ border-bottom: 1px solid $border-white-normal;
+ border-radius: 0;
+ }
+
+ a.gutter-toggle {
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ text-align: center;
+ }
+
+ .merge-icon {
+ height: 12px;
+ width: 12px;
+ bottom: -5px;
+ right: 4px;
+ }
+
+ .sidebar-collapsed-icon {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: $sidebar-toggle-height;
+ text-align: center;
+ color: $gl-text-color-secondary;
+
+ > svg {
+ fill: $gl-text-color-secondary;
+ }
+
+ &:hover:not(.disabled),
+ &:hover .todo-undone {
+ color: $gl-text-color;
+
+ > svg {
+ fill: $gl-text-color;
+ }
+ }
+
+ .todo-undone {
+ color: $blue-600;
+ fill: $blue-600;
+ }
+
+ .author {
+ display: none;
+ }
+
+ .btn-clipboard {
+ /*
+ This change should be temporary, because the DOM currently gets
+ generated from a ruby definition in `app/helpers/button_helper.rb`.
+ As soon as the `copy to clipboard` button will be transferred to
+ Vue this should be adjusted as well.
+ */
+ flex: 1;
+ align-self: stretch;
+ padding: 0;
+
+ border: 0;
+ background: transparent;
+ color: $gl-text-color-secondary;
+
+ &:hover {
+ color: $gl-text-color;
+ }
+ }
+
+ &.multiple-users {
+ display: flex;
+ justify-content: center;
+ }
+ }
+
+ .sidebar-avatar-counter {
+ width: 24px;
+ height: 24px;
+ border-radius: 12px;
+
+ ~.merge-icon {
+ bottom: 0;
+ }
+ }
+
+ .sidebar-collapsed-user {
+ padding-bottom: 0;
+
+ .author-link {
+ padding-left: 0;
+
+ .avatar {
+ position: static;
+ margin: 0;
+ }
+ }
+ }
+
+ .issuable-header-btn {
+ display: none;
+ }
+
+ .multiple-users {
+ .btn-link {
+ padding: 0;
+ border: 0;
+
+ .avatar {
+ margin: 0;
+ }
+ }
+
+ .btn-link:first-child {
+ position: absolute;
+ left: 10px;
+ z-index: 1;
+ }
+
+ .btn-link:last-child {
+ position: absolute;
+ right: 10px;
+
+ &:hover {
+ text-decoration: none;
+ }
+ }
+ }
+
+ .milestone-title span,
+ .collapse-truncated-title {
+ @include str-truncated(100%);
+ display: block;
+ margin: 0 4px;
+ }
+ }
+
+ .dropdown-menu-toggle {
+ width: 100%;
+ padding-top: 6px;
+ }
+
+ .dropdown-menu {
+ width: 100%;
+
+ /*
+ * Overwrite hover style for dropdown items, so that they are not blue
+ * This should be removed during dev of https://gitlab.com/gitlab-org/gitlab-foss/issues/44040
+ */
+ li a {
+ &:hover,
+ &:active,
+ &:focus,
+ &.is-focused {
+ @include dropdown-item-hover;
+ }
+ }
+
+ }
+}
+
+.with-performance-bar .right-sidebar {
+ top: calc(#{$header-height} + #{$performance-bar-height});
+}
+
+.sidebar-move-issue-confirmation-button {
+ width: 100%;
+
+ &.is-loading {
+ .sidebar-move-issue-confirmation-loading-icon {
+ display: inline-block;
+ }
+ }
+}
+
+.sidebar-move-issue-confirmation-loading-icon {
+ display: none;
+}
+
+.issuable-show-labels {
+ .gl-label {
+ margin-bottom: 5px;
+ margin-right: 5px;
+ }
+
+ a {
+ display: inline-block;
+
+ .color-label {
+ padding: 4px $grid-size;
+ border-radius: $label-border-radius;
+ margin-right: 4px;
+ margin-bottom: 4px;
+ }
+
+ &:hover .color-label {
+ text-decoration: underline;
+ }
+ }
+
+ &.has-labels {
+ // this font size is a fix to
+ // prevent unintended spacing between labels
+ // which shows up when rendering markup has white-space
+ // characters present.
+ // see: https://css-tricks.com/fighting-the-space-between-inline-block-elements/#article-header-id-3
+ font-size: 0;
+ margin-bottom: -5px;
+ }
+}
+
+.assignee,
+.reviewer {
+ .merge-icon {
+ color: $orange-400;
+ position: absolute;
+ bottom: -3px;
+ right: -3px;
+ filter: drop-shadow(0 0 0.5px $white) drop-shadow(0 0 1px $white) drop-shadow(0 0 2px $white);
+ }
+}
+
+.participants-author {
+ &:nth-of-type(8n) {
+ padding-right: 0;
+ }
+
+ .avatar.avatar-inline {
+ margin: 0;
+ }
+}
+
+.participants-more,
+.user-list-more {
+ margin-left: 5px;
+
+ a,
+ .btn-link {
+ color: $gl-text-color-secondary;
+ }
+
+ .btn-link {
+ padding: 0;
+ }
+
+ .btn-link:hover {
+ color: $blue-800;
+ text-decoration: none;
+ }
+
+ .btn-link:focus {
+ text-decoration: none;
+ }
+}
+
+.sidebar-help-wrap {
+ .sidebar-help-state {
+ margin: 16px -20px -20px;
+ padding: 16px 20px;
+ }
+
+ .help-state-toggle-enter-active {
+ transition: all 0.8s ease;
+ }
+
+ .help-state-toggle-leave-active {
+ transition: all 0.5s ease;
+ }
+
+ .help-state-toggle-enter,
+ .help-state-toggle-leave-active {
+ opacity: 0;
+ }
+}
+
+.time-tracker {
+ .sidebar-collapsed-icon {
+ > .stopwatch-svg {
+ display: inline-block;
+ }
+
+ svg {
+ width: 16px;
+ height: 16px;
+ fill: $gl-text-color-secondary;
+ }
+
+ &:hover svg {
+ fill: $gl-text-color;
+ }
+ }
+
+ .compare-meter {
+ &.over_estimate {
+ .time-remaining,
+ .compare-value.spent {
+ color: $red-500;
+ }
+ }
+ }
+
+ .compare-display-container {
+ font-size: 13px;
+ }
+}
+
+/*
+ * Following overrides are done to prevent
+ * legacy dropdown styles from influencing
+ * GitLab UI components used within GlDropdown
+ */
+.right-sidebar-collapsed {
+ .sidebar-grouped-item {
+ .sidebar-collapsed-icon {
+ margin-bottom: 0;
+ }
+
+ .sidebar-collapsed-divider {
+ line-height: 5px;
+ font-size: 12px;
+ color: $gray-500;
+
+ + .sidebar-collapsed-icon {
+ padding-top: 0;
+ }
+ }
+ }
+}
+
+@include media-breakpoint-down(sm) {
+ // Overriding the following rule with the negative margin
+ // https://gitlab.com/gitlab-org/gitlab/-/blob/146c43c931c3743a140529307aea616e4aa9ff21/app/assets/stylesheets/framework/sidebar.scss#L1-5
+ .container-fluid {
+ .issuable-list,
+ .issues-filters,
+ .epics-filters {
+ margin: 0 (-$gl-padding);
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 2c2d8a2b592..0a475845fd3 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -81,6 +81,7 @@
word-wrap: break-word;
overflow-wrap: break-word;
word-break: keep-all;
+ @include gl-font-base;
}
h1 {
@@ -322,7 +323,6 @@
pre {
margin-bottom: 16px;
- font-size: 13px;
line-height: 1.6em;
overflow-x: auto;
border-radius: $border-radius-default;
@@ -587,7 +587,7 @@
}
}
- .gl-new-dropdown-item {
+ .gl-dropdown-item {
margin: 0;
padding: 0;
line-height: 1rem;
@@ -658,7 +658,7 @@ pre {
display: block;
padding: $gl-padding-8 $input-horizontal-padding;
margin: 0 0 $gl-padding-8;
- font-size: $gl-font-size-monospace;
+ @include gl-font-base;
word-break: break-all;
word-wrap: break-word;
color: $gl-text-color;
@@ -680,7 +680,7 @@ code {
}
.monospace {
- font-family: $monospace-font;
+ @include gl-font-monospace;
}
.weight-normal {
@@ -706,7 +706,7 @@ code {
*/
textarea.js-gfm-input {
font-family: $monospace-font;
- font-size: $gl-font-size-monospace;
+ @include gl-font-base;
}
h1,
@@ -772,3 +772,8 @@ textarea {
wbr {
display: inline-block;
}
+
+// The font used in Monaco editor - Web IDE, Snippets, single file editor
+:root {
+ --code-editor-font: #{$monospace-font};
+}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 99284ea0a64..ec8ffaf8c53 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -83,18 +83,9 @@ $darken-dark-factor: 10% !default;
$darken-border-factor: 5% !default;
$darken-border-dashed-factor: 25% !default;
-$white: #fff !default;
-$white-normal: #f0f0f0 !default;
-$white-dark: #eaeaea !default;
-$white-transparent: rgba($white, 0.8) !default;
-
$purple: #6d49cb !default;
$purple-light: #ede8fb !default;
-$black: #000 !default;
-$black-transparent: rgba(0, 0, 0, 0.3) !default;
-$almost-black: #242424 !default;
-
$green-50: #ecf4ee !default;
$green-100: #c3e6cd !default;
$green-200: #91d4a8 !default;
@@ -183,6 +174,15 @@ $t-gray-a-08: rgba($gray-950, 0.08) !default;
$t-gray-a-16: rgba($gray-950, 0.16) !default;
$t-gray-a-24: rgba($gray-950, 0.24) !default;
+$white: #fff !default;
+$white-normal: $gray-50 !default;
+$white-dark: darken($gray-50, 2) !default;
+$white-transparent: rgba($white, 0.8) !default;
+
+$black: #000 !default;
+$black-transparent: $t-gray-a-24 !default;
+$almost-black: $gray-950 !default;
+
$greens: (
'50': $green-50,
'100': $green-100,
@@ -350,17 +350,17 @@ $theme-light-red-700: #a62e21;
// Data visualization color palette
-$data-viz-blue-50: #e9ebff;
-$data-viz-blue-100: #d4dcfa;
-$data-viz-blue-200: #b7c6ff;
-$data-viz-blue-300: #97acff;
-$data-viz-blue-400: #748eff;
-$data-viz-blue-500: #5772ff;
-$data-viz-blue-600: #445cf2;
-$data-viz-blue-700: #3547de;
-$data-viz-blue-800: #232fcf;
-$data-viz-blue-900: #1e23a8;
-$data-viz-blue-950: #11118a;
+$data-viz-blue-50: #e9ebff !default;
+$data-viz-blue-100: #d2dcff !default;
+$data-viz-blue-200: #b7c6ff !default;
+$data-viz-blue-300: #97acff !default;
+$data-viz-blue-400: #7992f5 !default;
+$data-viz-blue-500: #617ae2 !default;
+$data-viz-blue-600: #4e65cd !default;
+$data-viz-blue-700: #3f51ae !default;
+$data-viz-blue-800: #374291 !default;
+$data-viz-blue-900: #303470 !default;
+$data-viz-blue-950: #2a2b59 !default;
$border-white-light: darken($white, $darken-border-factor) !default;
$border-white-normal: darken($white-normal, $darken-border-factor) !default;
@@ -380,7 +380,7 @@ $well-expand-item: #e8f2f7 !default;
$well-inner-border: #eef0f2 !default;
$well-light-border: #f1f1f1;
$well-light-text-color: #5b6169;
-$nav-active-bg: rgba($black, 0.08);
+$nav-active-bg: $t-gray-a-08;
/*
* Text
@@ -555,11 +555,13 @@ $diff-jagged-border-gradient-color: darken($white-normal, 8%);
/*
* Fonts
*/
-$monospace-font: 'Menlo', 'DejaVu Sans Mono', 'Liberation Mono', 'Consolas', 'Ubuntu Mono',
- 'Courier New', 'andale mono', 'lucida console', monospace;
-$regular-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Noto Sans', Ubuntu, Cantarell,
- 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
- 'Noto Color Emoji';
+$monospace-font: var(--default-mono-font, 'Menlo'), 'DejaVu Sans Mono', 'Liberation Mono', 'Consolas',
+ 'Ubuntu Mono', 'Courier New', 'andale mono', 'lucida console', monospace;
+$regular-font: var(--default-regular-font, -apple-system), BlinkMacSystemFont, 'Segoe UI', Roboto, 'Noto Sans',
+ Ubuntu, Cantarell, 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
+ 'Segoe UI Symbol', 'Noto Color Emoji';
+$gl-monospace-font: $monospace-font;
+$gl-regular-font: $regular-font;
/*
* Dropdowns
@@ -730,12 +732,6 @@ $commit-message-text-area-bg: rgba(0, 0, 0, 0);
$commit-stat-summary-height: 36px;
/*
-* Common
-*/
-$common-gray-light: #bbb;
-$common-gray-dark: #444;
-
-/*
* Files
*/
$logs-li-color: #888;
@@ -784,16 +780,6 @@ $fade-mask-transition-curve: ease-in-out;
$login-brand-holder-color: #888;
/*
-* Projects
-*/
-$project-option-descr-color: #54565b;
-
-/*
- * Monitor Charts
- */
-$chart-tooltip-max-width: 512px;
-
-/*
Stat Graph
*/
$stat-graph-common-bg: #f3f3f3;
@@ -822,7 +808,6 @@ Pipeline Graph
$ci-action-icon-size: 22px;
$ci-action-icon-size-lg: 24px;
$pipeline-dropdown-line-height: 20px;
-$pipeline-dropdown-status-icon-size: 18px;
$ci-action-dropdown-button-size: 24px;
$ci-action-dropdown-svg-size: 12px;
diff --git a/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss b/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
index 613d27a2f39..ed15e352b7d 100644
--- a/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
+++ b/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
@@ -33,8 +33,8 @@
.ci-action-icon-container {
position: absolute;
- right: 8px;
- top: 8px;
+ right: 11px;
+ top: 7px;
&.ci-action-icon-wrapper {
height: $ci-action-dropdown-button-size;
@@ -84,25 +84,6 @@
&.non-details-job-component {
padding: $gl-padding-8 $gl-btn-horz-padding;
}
-
- .ci-job-name-component {
- align-items: center;
- display: flex;
- flex: 1;
- }
-
- .ci-status-icon {
- position: relative;
-
- > svg {
- width: $pipeline-dropdown-status-icon-size;
- height: $pipeline-dropdown-status-icon-size;
- margin: 3px 0;
- position: relative;
- overflow: visible;
- display: block;
- }
- }
}
// ensure .mini-pipeline-graph-dropdown-item has hover style when action-icon is hovered
diff --git a/app/assets/stylesheets/page_bundles/alert_management_details.scss b/app/assets/stylesheets/page_bundles/alert_management_details.scss
index 2eaf4517710..d67dadafa9e 100644
--- a/app/assets/stylesheets/page_bundles/alert_management_details.scss
+++ b/app/assets/stylesheets/page_bundles/alert_management_details.scss
@@ -28,7 +28,7 @@
@include gl-pt-8;
}
- .gl-new-dropdown-item-text-wrapper {
+ .gl-dropdown-item-text-wrapper {
@include gl-py-0;
}
}
diff --git a/app/assets/stylesheets/page_bundles/boards.scss b/app/assets/stylesheets/page_bundles/boards.scss
index 0cc1fb40e4a..bdbcf7ab58c 100644
--- a/app/assets/stylesheets/page_bundles/boards.scss
+++ b/app/assets/stylesheets/page_bundles/boards.scss
@@ -185,13 +185,15 @@
}
.issue-boards-content.is-focused {
+ $focus-mode-z-index: 9000;
+
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: var(--white, $white);
- z-index: 9000;
+ z-index: $focus-mode-z-index;
@include media-breakpoint-down(sm) {
padding-top: 10px;
@@ -201,13 +203,24 @@
height: calc(100vh - #{$issue-boards-filter-height});
}
- .boards-sidebar {
- height: 100%;
- top: 0;
+ // Use !important for these as top and z-index are set on style attribute
+ // in gitlab-ui; https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1805
+ ~ #js-right-sidebar-portal .boards-sidebar {
+ top: 0 !important;
+ z-index: calc(#{$focus-mode-z-index} + 1) !important;
}
}
.boards-sidebar {
+ top: $header-height !important;
+ height: auto;
+ bottom: 0;
+ padding-bottom: 0.5rem;
+
+ .with-performance-bar & {
+ top: calc(#{$header-height} + #{$performance-bar-height}) !important;
+ }
+
.sidebar-collapsed-icon {
@include gl-display-none;
}
diff --git a/app/assets/stylesheets/page_bundles/clusters.scss b/app/assets/stylesheets/page_bundles/clusters.scss
index 4f29ff4b1ad..4d75159e87a 100644
--- a/app/assets/stylesheets/page_bundles/clusters.scss
+++ b/app/assets/stylesheets/page_bundles/clusters.scss
@@ -6,7 +6,7 @@
@include gl-w-full;
order: -1;
- .gl-new-dropdown,
+ .gl-dropdown,
.split-content-button {
@include gl-w-full;
}
@@ -24,3 +24,9 @@
.cluster-button-container:focus-within {
@include gl-focus;
}
+
+.select-agent-dropdown {
+ .gl-button-text {
+ @include gl-flex-grow-1;
+ }
+}
diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss
index ec75c53d026..c3688f4a138 100644
--- a/app/assets/stylesheets/page_bundles/ide.scss
+++ b/app/assets/stylesheets/page_bundles/ide.scss
@@ -27,13 +27,6 @@ $ide-commit-header-height: 48px;
@include str-truncated(250px);
}
-.ide-layout {
- // Fix for iOS 13+, the height of the page is actually less than
- // 100vh because of the presence of the bottom bar
- max-height: 100%;
- position: fixed;
-}
-
.ide-view {
position: relative;
margin-top: 0;
@@ -522,13 +515,6 @@ $ide-commit-header-height: 48px;
}
}
-.ide-loading {
- display: flex;
- height: 100%;
- align-items: center;
- justify-content: center;
-}
-
.ide-empty-state {
display: flex;
height: 100vh;
diff --git a/app/assets/stylesheets/page_bundles/incidents.scss b/app/assets/stylesheets/page_bundles/incidents.scss
index de246fa14b9..e807c4c0bbb 100644
--- a/app/assets/stylesheets/page_bundles/incidents.scss
+++ b/app/assets/stylesheets/page_bundles/incidents.scss
@@ -4,13 +4,10 @@
.main-notes-list::before {
content: none;
}
+}
- .timeline-event-note {
- p {
- margin-bottom: 0;
- font-size: 0.875rem;
- }
- }
+.timeline-event {
+ grid-template-columns: #{$gl-spacing-scale-9} minmax(0, 1fr) #{$gl-spacing-scale-7};
}
/**
diff --git a/app/assets/stylesheets/page_bundles/issuable.scss b/app/assets/stylesheets/page_bundles/issuable.scss
new file mode 100644
index 00000000000..f364170c99f
--- /dev/null
+++ b/app/assets/stylesheets/page_bundles/issuable.scss
@@ -0,0 +1,183 @@
+@import 'mixins_and_variables_and_functions';
+
+.status-box {
+ padding: 0 $gl-btn-padding;
+ border-radius: $border-radius-default;
+ display: block;
+ float: left;
+ margin-right: $gl-padding-8;
+ color: var(--white, $white);
+ font-size: $gl-font-size;
+ line-height: $gl-line-height-24;
+}
+
+.issuable-warning-icon {
+ background-color: var(--orange-50, $orange-50);
+ border-radius: $border-radius-default;
+ color: var(--orange-600, $orange-600);
+ width: $issuable-warning-size;
+ height: $issuable-warning-size;
+ text-align: center;
+ margin-right: $issuable-warning-icon-margin;
+ line-height: $gl-line-height-24;
+ flex: 0 0 auto;
+}
+
+.limit-container-width {
+ .flash-container,
+ .detail-page-header,
+ .page-content-header,
+ .commit-box,
+ .info-well,
+ .commit-ci-menu,
+ .files-changed-inner,
+ .limited-header-width,
+ .limited-width-notes {
+ @include fixed-width-container;
+ }
+
+ .issuable-details {
+ .detail-page-description,
+ .mr-source-target,
+ .mr-state-widget,
+ .merge-manually {
+ @include fixed-width-container;
+ }
+ }
+
+ .merge-request-details {
+ .emoji-list-container {
+ @include fixed-width-container;
+ }
+ }
+}
+
+.issuable-details {
+ section {
+ .issuable-discussion {
+ margin-right: 1px;
+ }
+ }
+
+ .title-container {
+ display: flex;
+ align-items: flex-start;
+ }
+
+ .title {
+ padding: 0;
+ margin-bottom: $gl-padding;
+ border-bottom: 0;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ min-width: 0;
+ width: 100%;
+ text-align: initial;
+ }
+
+ .btn-edit {
+ margin-left: auto;
+ }
+}
+
+.detail-page-description {
+ padding: 16px 0;
+
+ small {
+ color: var(--gray-500, $gray-500);
+ }
+}
+
+.edited-text {
+ color: var(--gray-500, $gray-500);
+ display: block;
+ margin: 16px 0 0;
+ font-size: 85%;
+
+ .author-link {
+ color: var(--gray-500, $gray-500);
+ }
+}
+
+.user-item {
+ padding: 5px;
+ flex-basis: 20%;
+
+ .user-link {
+ display: inline-block;
+ }
+}
+
+.issuable-gutter-toggle {
+ @include media-breakpoint-down(sm) {
+ margin-left: $btn-side-margin;
+ }
+}
+
+.issuable-meta {
+ flex: 1;
+ display: inline-block;
+ font-size: 14px;
+ align-self: center;
+ overflow: hidden;
+ text-overflow: ellipsis;
+
+ .user-status-emoji {
+ margin-left: $gl-padding-4;
+ margin-right: 0;
+ }
+}
+
+.js-issuable-selector-wrap {
+ .js-issuable-selector {
+ width: 100%;
+ }
+
+ @include media-breakpoint-down(sm) {
+ margin-bottom: $gl-padding;
+ }
+}
+
+.add-issuable-form-input-wrapper {
+ &.focus {
+ border-color: var(--gray-700, $gray-700);
+ @include gl-focus;
+
+ input {
+ @include gl-shadow-none;
+ }
+ }
+
+ .gl-show-field-errors &.form-control:not(textarea) {
+ height: auto;
+ }
+}
+
+/*
+ * Following overrides are done to prevent
+ * legacy dropdown styles from influencing
+ * GitLab UI components used within GlDropdown
+ */
+.issuable-move-dropdown {
+ .b-dropdown-form {
+ @include gl-p-0;
+ }
+
+ .gl-search-box-by-type button.gl-clear-icon-button:hover {
+ @include gl-bg-transparent;
+ }
+
+ .issuable-move-button:not(.disabled):hover {
+ @include gl-text-white;
+ }
+}
+
+.suggestion-footer {
+ font-size: 12px;
+ line-height: 15px;
+
+ .avatar {
+ margin-top: -3px;
+ border: 0;
+ }
+}
diff --git a/app/assets/stylesheets/page_bundles/issuable_list.scss b/app/assets/stylesheets/page_bundles/issuable_list.scss
new file mode 100644
index 00000000000..b08e129a805
--- /dev/null
+++ b/app/assets/stylesheets/page_bundles/issuable_list.scss
@@ -0,0 +1,96 @@
+@import 'mixins_and_variables_and_functions';
+
+.issuable-list {
+ li {
+ .issuable-info-container {
+ flex: 1;
+ display: flex;
+ }
+
+ .issuable-main-info {
+ flex: 1 auto;
+ margin-right: 10px;
+ min-width: 0;
+
+ .issue-weight-icon,
+ .issue-estimate-icon {
+ vertical-align: sub;
+ }
+ }
+
+ .issuable-meta {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ flex: 1 0 auto;
+
+ .controls {
+ margin-bottom: 2px;
+ line-height: 20px;
+ padding: 0;
+ }
+ }
+
+ @include media-breakpoint-down(xs) {
+ .issuable-meta {
+ .controls li {
+ margin-right: 0;
+ }
+ }
+ }
+
+ .issue-check {
+ min-width: 15px;
+ }
+
+ .issuable-milestone,
+ .issuable-info,
+ .task-status,
+ .issuable-timestamp {
+ font-weight: $gl-font-weight-normal;
+ color: var(--gray-500, $gl-text-color-secondary);
+
+ a {
+ color: var(--gl-text-color, $gl-text-color);
+ }
+
+ .gl-label-link {
+ color: inherit;
+
+ &:hover {
+ text-decoration: none;
+
+ .gl-label-text:last-of-type {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ .milestone {
+ color: var(--gray-700, $gray-700);
+ }
+ }
+
+ @media(max-width: map-get($grid-breakpoints, lg)-1) {
+ .task-status,
+ .issuable-due-date,
+ .issuable-weight,
+ .project-ref-path {
+ display: none;
+ }
+ }
+ }
+}
+
+.issuable-list li,
+.issuable-info-container .controls {
+ .avatar-counter {
+ display: inline-block;
+ vertical-align: middle;
+ min-width: 16px;
+ line-height: 14px;
+ height: 16px;
+ padding-left: 2px;
+ padding-right: 2px;
+ }
+}
diff --git a/app/assets/stylesheets/page_bundles/merge_requests.scss b/app/assets/stylesheets/page_bundles/merge_requests.scss
index 771428b49e0..4950561bcb7 100644
--- a/app/assets/stylesheets/page_bundles/merge_requests.scss
+++ b/app/assets/stylesheets/page_bundles/merge_requests.scss
@@ -329,6 +329,8 @@ $tabs-holder-z-index: 250;
top: 0;
// !important is required to override inline styles of resizable sidebar
width: 100% !important;
+ // avoid sticky elements overlap header and other elements
+ z-index: 1;
}
.tree-list-holder {
@@ -339,11 +341,13 @@ $tabs-holder-z-index: 250;
}
.ci-widget-container {
+ align-items: center;
justify-content: space-between;
flex: 1;
flex-direction: row;
@include media-breakpoint-down(sm) {
+ align-items: initial;
flex-direction: column;
.dropdown .mini-pipeline-graph-dropdown-menu.dropdown-menu {
@@ -632,13 +636,6 @@ $tabs-holder-z-index: 250;
margin: 3px 0;
}
- .ci-status-icon svg {
- margin: 3px 0;
- position: relative;
- overflow: visible;
- display: block;
- }
-
.normal {
flex: 1;
flex-basis: auto;
@@ -673,10 +670,6 @@ $tabs-holder-z-index: 250;
}
.mr-widget-body {
- &:not(.mr-widget-body-line-height-1) {
- line-height: 24px;
- }
-
@include clearfix;
.approve-btn {
@@ -1003,7 +996,7 @@ $tabs-holder-z-index: 250;
max-width: 650px;
max-height: calc(100vh - 50px);
- .gl-new-dropdown-inner {
+ .gl-dropdown-inner {
max-height: none !important;
}
@@ -1038,7 +1031,7 @@ $tabs-holder-z-index: 250;
}
}
- .gl-new-dropdown-contents {
+ .gl-dropdown-contents {
padding: $gl-spacing-scale-4 !important;
}
@@ -1048,20 +1041,28 @@ $tabs-holder-z-index: 250;
}
.mr-widget-merge-details {
+ *,
+ & {
+ @include gl-font-sm;
+ }
+
+ p {
+ @include gl-font-base;
+ }
+
li:not(:last-child) {
- @include gl-mb-3;
+ @include gl-mb-2;
}
}
-.mr-ready-merge-related-links,
-.mr-widget-merge-details {
- a {
- @include gl-text-decoration-underline;
+.mr-ready-merge-related-links a,
+.mr-widget-merge-details a,
+.mr-widget-author {
+ @include gl-text-decoration-underline;
- &:hover,
- &:focus {
- @include gl-text-decoration-none;
- }
+ &:hover,
+ &:focus {
+ @include gl-text-decoration-none;
}
}
@@ -1075,36 +1076,20 @@ $tabs-holder-z-index: 250;
}
}
-.detail-page-header-actions {
- .gl-toggle {
- @include gl-ml-auto;
- @include gl-rounded-pill;
- @include gl-w-9;
-
- &.is-checked:hover {
- background-color: $blue-500;
- }
- }
-}
-
.page-with-icon-sidebar .issue-sticky-header {
--width: calc(100% - #{$contextual-sidebar-collapsed-width});
}
.merge-request-notification-toggle {
+ .gl-toggle {
+ @include gl-ml-auto;
+ }
+
.gl-toggle-label {
@include gl-font-weight-normal;
}
}
-.dropdown-menu li button.gl-toggle:not(.is-checked) {
- background: $gray-400;
-}
-
-.mr-widget-content-row:first-child {
- border-top: 0;
-}
-
.mr-widget-status-icon-level-1::before {
@include gl-content-empty;
@include gl-absolute;
diff --git a/app/assets/stylesheets/page_bundles/milestone.scss b/app/assets/stylesheets/page_bundles/milestone.scss
index 63bcb83e747..9ee6d17cb50 100644
--- a/app/assets/stylesheets/page_bundles/milestone.scss
+++ b/app/assets/stylesheets/page_bundles/milestone.scss
@@ -1,17 +1,7 @@
@import 'page_bundles/mixins_and_variables_and_functions';
-$status-box-line-height: 26px;
-
-.issues-sortable-list .str-truncated {
- max-width: 90%;
-}
-
.milestones {
.milestone {
- h4 {
- font-weight: $gl-font-weight-bold;
- }
-
.progress {
width: 100%;
height: 6px;
diff --git a/app/assets/stylesheets/page_bundles/oncall_schedules.scss b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
index 91fd2d42657..b995724ec7c 100644
--- a/app/assets/stylesheets/page_bundles/oncall_schedules.scss
+++ b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
@@ -9,7 +9,7 @@
@include gl-w-full;
}
- .gl-new-dropdown-item-text-primary {
+ .gl-dropdown-item-text-primary {
@include gl-overflow-hidden;
@include gl-text-overflow-ellipsis;
}
diff --git a/app/assets/stylesheets/page_bundles/pipelines.scss b/app/assets/stylesheets/page_bundles/pipelines.scss
index 4946bbbebe5..f9c49b0e6ca 100644
--- a/app/assets/stylesheets/page_bundles/pipelines.scss
+++ b/app/assets/stylesheets/page_bundles/pipelines.scss
@@ -70,33 +70,20 @@
}
}
-// Mini Pipelines
-
-.stage-cell {
- .stage-container {
- &:last-child {
- margin-right: 0;
- }
-
- // Hack to show a button tooltip inline
- button.has-tooltip + .tooltip {
- min-width: 105px;
- }
-
- // Bootstrap way of showing the content inline for anchors.
- a.has-tooltip {
- white-space: nowrap;
- }
+// Pipeline mini graph
+.pipeline-mini-graph-stage-container {
+ &:last-child {
+ margin-right: 0;
+ }
- &:not(:last-child) {
- &::after {
- content: '';
- border-bottom: 2px solid $gray-200;
- position: absolute;
- right: -4px;
- top: 11px;
- width: 4px;
- }
+ &:not(:last-child) {
+ &::after {
+ content: '';
+ border-bottom: 2px solid $gray-200;
+ position: absolute;
+ right: -4px;
+ top: 11px;
+ width: 4px;
}
}
}
diff --git a/app/assets/stylesheets/page_bundles/search.scss b/app/assets/stylesheets/page_bundles/search.scss
new file mode 100644
index 00000000000..10da541ed8d
--- /dev/null
+++ b/app/assets/stylesheets/page_bundles/search.scss
@@ -0,0 +1,336 @@
+@import 'mixins_and_variables_and_functions';
+
+$search-dropdown-max-height: 400px;
+$search-avatar-size: 16px;
+$search-sidebar-min-width: 240px;
+$search-sidebar-max-width: 300px;
+$search-keyboard-shortcut: '/';
+
+$border-radius-medium: 3px;
+
+.search-results {
+ .search-result-row {
+ border-bottom: 1px solid var(--border-color, $border-color);
+ padding-bottom: $gl-padding;
+ margin-bottom: $gl-padding;
+
+ &:last-child {
+ border-bottom: 0;
+ }
+ }
+}
+
+.search-sidebar {
+ @include media-breakpoint-up(md) {
+ min-width: $search-sidebar-min-width;
+ max-width: $search-sidebar-max-width;
+ }
+}
+
+.search {
+ margin: 0 8px;
+
+ form {
+ display: block;
+ margin: 0;
+ padding: 4px;
+ width: $search-input-width;
+ line-height: 24px;
+ height: 32px;
+ border: 0;
+ border-radius: $border-radius-default;
+ transition: border-color ease-in-out $default-transition-duration,
+ background-color ease-in-out $default-transition-duration;
+
+ @include media-breakpoint-up(xl) {
+ width: $search-input-xl-width;
+ }
+
+ &:hover {
+ box-shadow: none;
+ }
+ }
+
+ .search-input {
+ border: 0;
+ font-size: 14px;
+ padding: 0 20px 0 0;
+ margin-left: 5px;
+ line-height: 25px;
+ width: 98%;
+ color: $white;
+ background: none;
+ transition: color ease-in-out $default-transition-duration;
+ }
+
+ .search-input::placeholder {
+ transition: color ease-in-out $default-transition-duration;
+ }
+
+ .search-input-container {
+ display: flex;
+ position: relative;
+ }
+
+ .search-input-wrap {
+ width: 100%;
+
+ .search-icon,
+ .clear-icon {
+ position: absolute;
+ right: 5px;
+ top: 4px;
+ }
+
+ .search-icon {
+ transition: color $default-transition-duration;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ }
+
+ .clear-icon {
+ display: none;
+ }
+
+ // Rewrite position. Dropdown menu should be relative to .search-input-container
+ .dropdown {
+ position: static;
+ }
+
+ .dropdown-header {
+ // Necessary because deprecatedJQueryDropdown doesn't support a second style of headers
+ font-weight: $gl-font-weight-bold;
+ color: var(--gl-text-color, $gl-text-color);
+ font-size: $gl-font-size;
+ line-height: 16px;
+ }
+
+ // Custom dropdown positioning
+ .dropdown-menu {
+ left: -5px;
+ max-height: $search-dropdown-max-height;
+ overflow: auto;
+
+ @include media-breakpoint-up(xl) {
+ width: $search-input-xl-width;
+ }
+ }
+
+ .dropdown-content {
+ max-height: $search-dropdown-max-height - 18px;
+ }
+ }
+
+ &.search-active {
+ form {
+ border-color: var(--blue-300, $blue-300);
+ box-shadow: none;
+
+ .search-input-wrap {
+ .search-icon,
+ .clear-icon {
+ color: var(--gray-400, $gl-text-color-tertiary);
+ transition: color ease-in-out $default-transition-duration;
+ }
+ }
+
+ .search-input {
+ color: var(--gl-text-color, $gl-text-color);
+ transition: color ease-in-out $default-transition-duration;
+ }
+
+ .search-input::placeholder {
+ color: var(--gray-400, $gl-text-color-tertiary);
+ }
+ }
+ }
+
+ &.has-value {
+ .search-icon {
+ display: none;
+ }
+
+ .clear-icon {
+ cursor: pointer;
+ display: block;
+ }
+ }
+
+ .inline-search-icon {
+ position: relative;
+ margin-right: 4px;
+ color: var(--gray-500, $gl-text-color-secondary);
+ }
+
+ .identicon,
+ .search-item-avatar {
+ flex-basis: $search-avatar-size;
+ flex-shrink: 0;
+ margin-right: 4px;
+ }
+
+ .search-item-avatar {
+ width: $search-avatar-size;
+ height: $search-avatar-size;
+ border-radius: 50%;
+ border: 1px solid var(--gray-50, $gray-normal);
+ }
+}
+
+.search-field-holder,
+.project-filter-form {
+ flex: 1 0 auto;
+ position: relative;
+
+ .search-holder & {
+ margin-right: 0;
+
+ @include media-breakpoint-up(sm) {
+ margin-right: 5px;
+ }
+ }
+
+ .search-icon {
+ position: absolute;
+ left: 10px;
+ top: 9px;
+ color: var(--gray-700, $gray-darkest);
+ pointer-events: none;
+ }
+
+ .search-text-input {
+ padding-left: $gl-padding + 15px;
+ padding-right: $gl-padding + 15px;
+ }
+}
+
+.search-holder {
+ @include media-breakpoint-up(sm) {
+ display: flex;
+ }
+
+ .btn-search,
+ .btn-success,
+ .dropdown-menu-toggle,
+ .gl-dropdown {
+ width: 100%;
+ margin-top: 5px;
+
+ @include media-breakpoint-up(sm) {
+ width: auto;
+ margin-top: 0;
+ margin-left: 5px;
+ }
+ }
+
+ .dropdown {
+ @include media-breakpoint-up(sm) {
+ margin-left: 5px;
+ margin-right: 5px;
+ }
+ }
+
+ .dropdown-menu-toggle,
+ .gl-dropdown {
+ @include media-breakpoint-up(sm) {
+ width: 180px;
+ margin-top: 0;
+ }
+ }
+}
+
+.search-page-form {
+ .dropdown-menu-toggle,
+ .btn-search {
+ width: 100%;
+ }
+
+ .dropdown-menu-toggle,
+ .gl-dropdown {
+ @include media-breakpoint-up(lg) {
+ width: 240px;
+ }
+ }
+
+ .btn-search {
+ @include media-breakpoint-up(lg) {
+ width: auto;
+ }
+ }
+}
+
+.ref-truncated {
+ @include str-truncated(10em);
+}
+
+.global-search-dropdown-menu {
+ width: 100% !important;
+ max-width: 400px;
+
+ @include media-breakpoint-up(md) {
+ // This is larger than the container so width: 100% doesn't work.
+ width: 400px !important;
+ }
+}
+
+// This overrides parts of the Project File View CSS
+// We leverage most of the styling but broke off
+// from how we were doing it in `shared/file_highlight`
+#search-blob-content {
+ .line_holder {
+ pre {
+ padding: 0; // This overrides the existing style that will add space between each line.
+ .line {
+ @include gl-word-break-word;
+ white-space: break-spaces;
+ }
+ }
+
+ svg {
+ float: none; // We have more than one icon on this implementation and don't want to float them.
+ margin: 0; // We will manage the margin with GitLab UI utility classes
+ }
+
+ .line-numbers {
+ padding: 0; // This overrides the existing style that will add space between each line.
+ min-width: 6.5rem; // Ensure our numbers fit
+
+ .diff-line-num {
+ a {
+ transition: none; // There will be a hover transition from theme, blue, darkened
+ }
+ }
+ }
+
+ &:hover {
+ svg {
+ visibility: visible; // We want to show the icons when the any part of the line is hovered
+ }
+ }
+
+ // The icons only appear on hover
+ // So on mobile we can hide them and retake the space for the code blob
+ @include media-breakpoint-down(sm) {
+ svg {
+ display: none;
+ }
+
+ .line-numbers {
+ min-width: 4rem;
+ }
+ }
+ }
+}
+
+// Disable Webkit's search input styles
+input[type='search'] {
+ /* stylelint-disable-next-line property-no-vendor-prefix */
+ -webkit-appearance: textfield;
+
+ &::-webkit-search-cancel-button,
+ &::-webkit-search-results-button {
+ @include gl-display-none;
+ }
+}
diff --git a/app/assets/stylesheets/page_bundles/settings.scss b/app/assets/stylesheets/page_bundles/settings.scss
new file mode 100644
index 00000000000..9037eb7ae62
--- /dev/null
+++ b/app/assets/stylesheets/page_bundles/settings.scss
@@ -0,0 +1,209 @@
+@import 'mixins_and_variables_and_functions';
+
+@keyframes expandMaxHeight {
+ 0% {
+ max-height: 0;
+ }
+
+ 99% {
+ max-height: 100vh;
+ }
+
+ 100% {
+ max-height: none;
+ }
+}
+
+@keyframes collapseMaxHeight {
+ 0% {
+ max-height: 100vh;
+ }
+
+ 100% {
+ max-height: 0;
+ }
+}
+
+.settings {
+ // border-top for each item except the top one
+ border-top: 1px solid var(--border-color, $border-color);
+
+ &:first-of-type {
+ margin-top: 10px;
+ padding-top: 0;
+ border: 0;
+ }
+
+ + div .settings:first-of-type {
+ margin-top: 0;
+ border-top: 1px solid var(--border-color, $border-color);
+ }
+
+ &.animating {
+ overflow: hidden;
+ }
+}
+
+.settings-header {
+ position: relative;
+ padding: $gl-padding-24 110px 0 0;
+
+ h4 {
+ margin-top: 0;
+ }
+
+ .settings-title {
+ cursor: pointer;
+ }
+
+ button {
+ position: absolute;
+ top: 20px;
+ right: 6px;
+ min-width: 80px;
+ }
+}
+
+.settings-content {
+ max-height: 1px;
+ overflow-y: hidden;
+ padding-right: 110px;
+ animation: collapseMaxHeight 300ms ease-out;
+ // Keep the section from expanding when we scroll over it
+ pointer-events: none;
+
+ .settings.expanded & {
+ max-height: none;
+ overflow-y: visible;
+ animation: expandMaxHeight 300ms ease-in;
+ // Reset and allow clicks again when expanded
+ pointer-events: auto;
+ }
+
+ .settings.no-animate & {
+ animation: none;
+ }
+
+ @media(max-width: map-get($grid-breakpoints, md)-1) {
+ padding-right: 20px;
+ }
+
+ &::before {
+ content: ' ';
+ display: block;
+ height: 1px;
+ overflow: hidden;
+ margin-bottom: 4px;
+ }
+
+ &::after {
+ content: ' ';
+ display: block;
+ height: 1px;
+ overflow: hidden;
+ margin-top: 20px;
+ }
+
+ .sub-section {
+ margin-bottom: 32px;
+ padding: 16px;
+ border: 1px solid var(--border-color, $border-color);
+ background-color: var(--gray-light, $gray-light);
+ }
+
+ .bs-callout,
+ .form-check:first-child,
+ .form-check .form-text.text-muted,
+ .form-check + .form-text.text-muted {
+ margin-top: 0;
+ }
+
+ .form-check .form-text.text-muted {
+ margin-bottom: $grid-size;
+ }
+}
+
+.settings-list-icon {
+ color: var(--gray-500, $gl-text-color-secondary);
+ font-size: $default-icon-size;
+ line-height: 42px;
+}
+
+.settings-message {
+ padding: 5px;
+ line-height: 1.3;
+ color: var(--gray-900, $gray-900);
+ background-color: var(--orange-50, $orange-50);
+ border: 1px solid var(--orange-200, $orange-200);
+ border-radius: $gl-border-radius-base;
+}
+
+.prometheus-metrics-monitoring {
+ .card {
+ .card-toggle {
+ width: 14px;
+ }
+
+ .badge.badge-pill {
+ font-size: 12px;
+ line-height: 12px;
+ }
+
+ .card-header .label-count {
+ color: var(--white, $white);
+ background: var(--gray-800, $gray-800);
+ }
+
+ .card-body {
+ padding: 0;
+ }
+
+ .flash-container {
+ margin-bottom: 0;
+ cursor: default;
+
+ .flash-notice {
+ border-radius: 0;
+ }
+ }
+ }
+
+ .custom-monitored-metrics {
+ .card-header {
+ display: flex;
+ align-items: center;
+ }
+
+ .custom-metric {
+ display: flex;
+ align-items: center;
+ }
+
+ .custom-metric-link-bold {
+ font-weight: $gl-font-weight-bold;
+ text-decoration: none;
+ }
+ }
+
+ .loading-metrics .metrics-load-spinner {
+ color: var(--gray-700, $gray-700);
+ }
+
+ .metrics-list {
+ margin-bottom: 0;
+
+ li {
+ padding: $gl-padding;
+
+ .badge.badge-pill {
+ margin-left: 5px;
+ background: $badge-bg;
+ }
+
+ /* Ensure we don't add border if there's only single li */
+ + li {
+ border-top: 1px solid var(--border-color, $border-color);
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/page_bundles/todos.scss b/app/assets/stylesheets/page_bundles/todos.scss
index 3eacf98688e..b35f5b38740 100644
--- a/app/assets/stylesheets/page_bundles/todos.scss
+++ b/app/assets/stylesheets/page_bundles/todos.scss
@@ -7,8 +7,16 @@
.todos-list > .todo {
// workaround because we cannot use border-collapse
+ padding: 6px 12px !important;
border-top: 1px solid transparent;
+ span:not(.todo-label),
+ button,
+ a:not(.todo-target-link),
+ time {
+ @include gl-relative;
+ }
+
// overwrite border style of .content-list
&:last-child {
border-bottom: 1px solid transparent;
@@ -38,25 +46,66 @@
.todo-item {
@include transition(opacity);
- .todo-label,
- .todo-project {
- a {
- color: var(--blue-600, $blue-600);
- }
+ .todo-label a::before {
+ // Make area of the todo item clickable by expanding the area around the todo link
+ @include gl-content-empty;
+ @include gl-absolute;
+ @include gl-left-0;
+ @include gl-right-0;
+ @include gl-top-0;
+ @include gl-bottom-0;
+ z-index: 9;
}
+}
- .todo-body {
- p {
- color: var(--gl-text-color, $gl-text-color);
- }
+.todo-title {
+ margin-right: 2.5rem;
- .gl-label-scoped {
- --label-inset-border: inset 0 0 0 1px currentColor;
- }
+ @include media-breakpoint-up(sm) {
+ @include gl-mr-0;
+ @include gl-text-overflow-ellipsis;
+ @include gl-white-space-nowrap;
+ @include gl-overflow-hidden;
+ }
+}
- @include media-breakpoint-down(sm) {
- border-left: 2px solid var(--border-color, $border-color);
- padding-left: 10px;
- }
+.todo-body {
+ p {
+ @include gl-display-inline;
+ color: var(--gl-text-color, $gl-text-color);
+ }
+
+ pre.code.highlight {
+ @include gl-py-0;
+ @include gl-px-1;
+ @include gl-m-0;
+ @include gl-bg-gray-50;
+ @include gl-border-0;
+ @include gl-rounded-base;
+ @include gl-display-inline-flex;
+ @include gl-text-body;
+ }
+
+ .gl-label-scoped {
+ --label-inset-border: inset 0 0 0 1px currentColor;
+ }
+
+ .avatar {
+ @include gl-mb-0;
+ }
+}
+
+.todo-actions,
+.todo-body .todo-avatar,
+.todos-list > .todo a:not(.todo-target-link) {
+ z-index: 11 !important;
+}
+
+.todo-actions {
+ @include gl-absolute;
+ @include gl-right-0;
+
+ @include media-breakpoint-up(sm) {
+ @include gl-relative;
}
}
diff --git a/app/assets/stylesheets/page_bundles/tree.scss b/app/assets/stylesheets/page_bundles/tree.scss
index 58e55e11f7e..50d9684c7d2 100644
--- a/app/assets/stylesheets/page_bundles/tree.scss
+++ b/app/assets/stylesheets/page_bundles/tree.scss
@@ -205,3 +205,18 @@
.blob-content-holder {
margin-top: $gl-padding;
}
+
+
+.web-ide-promo-popover {
+ box-shadow: 0 0 18px -1.9px rgba(119, 89, 194, 0.16),
+ 0 0 12.9px -1.7px rgba(119, 89, 194, 0.16), 0 0 9.2px -1.4px rgba(119, 89, 194, 0.16),
+ 0 0 6.4px -1.1px rgba(119, 89, 194, 0.16), 0 0 4.5px -0.8px rgba(119, 89, 194, 0.16),
+ 0 0 3px -0.6px rgba(119, 89, 194, 0.16), 0 0 1.8px -0.3px rgba(119, 89, 194, 0.16),
+ 0 0 0.6px rgba(119, 89, 194, 0.16);
+ z-index: 999;
+}
+
+.web-ide-promo-popover-illustration {
+ width: calc(100% + 24px);
+ margin: -28px -12px 0;
+}
diff --git a/app/assets/stylesheets/page_bundles/users.scss b/app/assets/stylesheets/page_bundles/users.scss
new file mode 100644
index 00000000000..d4cd28504fc
--- /dev/null
+++ b/app/assets/stylesheets/page_bundles/users.scss
@@ -0,0 +1,70 @@
+@import 'mixins_and_variables_and_functions';
+
+.user-search-form {
+ position: relative;
+
+ @include media-breakpoint-up(sm) {
+ float: right;
+ }
+
+ .dropdown {
+ width: 100%;
+ margin-top: 5px;
+
+ .dropdown-menu-toggle {
+ vertical-align: middle;
+ width: 100%;
+ }
+
+ @include media-breakpoint-up(sm) {
+ margin-top: 0;
+ width: 155px;
+ }
+ }
+
+ .form-control {
+ padding-right: 35px;
+ }
+
+ .search-control-wrap,
+ .form-control {
+ width: 100%;
+
+ @include media-breakpoint-up(sm) {
+ width: 250px;
+ }
+ }
+}
+
+.user-search-btn {
+ position: absolute;
+ right: 4px;
+ top: 0;
+ height: 35px;
+ padding-left: 10px;
+ padding-right: 10px;
+ color: $gray-darkest;
+ background: transparent;
+ border: 0;
+ outline: 0;
+}
+
+.content-list.members-list li {
+ display: flex;
+ justify-content: space-between;
+
+ .list-item-name {
+ float: none;
+ display: flex;
+ flex: 1;
+ }
+}
+
+.card-body .user-info {
+ float: left;
+
+ .user {
+ color: $gl-text-color;
+ font-weight: $gl-font-weight-bold;
+ }
+}
diff --git a/app/assets/stylesheets/page_bundles/work_items.scss b/app/assets/stylesheets/page_bundles/work_items.scss
index 820a1a0b53e..4766f124e5b 100644
--- a/app/assets/stylesheets/page_bundles/work_items.scss
+++ b/app/assets/stylesheets/page_bundles/work_items.scss
@@ -81,4 +81,8 @@
}
}
}
+
+ > .col {
+ min-width: 0;
+ }
}
diff --git a/app/assets/stylesheets/pages/colors.scss b/app/assets/stylesheets/pages/colors.scss
index 20e072b9903..d1917948c88 100644
--- a/app/assets/stylesheets/pages/colors.scss
+++ b/app/assets/stylesheets/pages/colors.scss
@@ -22,3 +22,11 @@
display: none;
}
}
+
+.warning-title {
+ color: var(--gray-900, $gray-900);
+}
+
+.danger-title {
+ color: var(--red-500, $red-500);
+}
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 19318d87731..dd24e3fcb5d 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -338,11 +338,6 @@
color: $gl-text-color;
}
-.commit .gpg-popover-help-link {
- display: block;
- color: $link-color;
-}
-
.add-review-item {
.gl-tab-nav-item {
height: 100%;
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index ce8dd6684f2..f6c79a4eca2 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -4,7 +4,7 @@
*/
.event-item {
font-size: $gl-font-size;
- padding: $gl-padding 0 $gl-padding 56px;
+ padding: $gl-padding 0 $gl-padding $gl-spacing-scale-8;
border-bottom: 1px solid $white-normal;
color: $gl-text-color-secondary;
position: relative;
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
deleted file mode 100644
index 1b6e7954366..00000000000
--- a/app/assets/stylesheets/pages/issuable.scss
+++ /dev/null
@@ -1,912 +0,0 @@
-.status-box {
- padding: 0 $gl-btn-padding;
- border-radius: $border-radius-default;
- display: block;
- float: left;
- margin-right: $gl-padding-8;
- color: $white;
- font-size: $gl-font-size;
- line-height: $gl-line-height-24;
-}
-
-.issuable-warning-icon {
- background-color: $orange-50;
- border-radius: $border-radius-default;
- color: $orange-600;
- width: $issuable-warning-size;
- height: $issuable-warning-size;
- text-align: center;
- margin-right: $issuable-warning-icon-margin;
- line-height: $gl-line-height-24;
- flex: 0 0 auto;
-}
-
-.limit-container-width {
- .flash-container,
- .detail-page-header,
- .page-content-header,
- .commit-box,
- .info-well,
- .commit-ci-menu,
- .files-changed-inner,
- .limited-header-width,
- .limited-width-notes {
- @include fixed-width-container;
- }
-
- .issuable-details {
- .detail-page-description,
- .mr-source-target,
- .mr-state-widget,
- .merge-manually {
- @include fixed-width-container;
- }
- }
-
- .merge-request-details {
- .emoji-list-container {
- @include fixed-width-container;
- }
- }
-}
-
-.issuable-details {
- section {
- .issuable-discussion {
- margin-right: 1px;
- }
- }
-
- .title-container {
- display: flex;
- align-items: flex-start;
- }
-
- .title {
- padding: 0;
- margin-bottom: $gl-padding;
- border-bottom: 0;
- word-wrap: break-word;
- overflow-wrap: break-word;
- min-width: 0;
- width: 100%;
- text-align: initial;
- }
-
- .btn-edit {
- margin-left: auto;
- }
-}
-
-.issuable-show-labels {
- .gl-label {
- margin-bottom: 5px;
- margin-right: 5px;
- }
-
- a {
- display: inline-block;
-
- .color-label {
- padding: 4px $grid-size;
- border-radius: $label-border-radius;
- margin-right: 4px;
- margin-bottom: 4px;
- }
-
- &:hover .color-label {
- text-decoration: underline;
- }
- }
-
- &.has-labels {
- // this font size is a fix to
- // prevent unintended spacing between labels
- // which shows up when rendering markup has white-space
- // characters present.
- // see: https://css-tricks.com/fighting-the-space-between-inline-block-elements/#article-header-id-3
- font-size: 0;
- margin-bottom: -5px;
- }
-}
-
-.assignee,
-.reviewer {
- .merge-icon {
- color: $orange-400;
- position: absolute;
- bottom: -3px;
- right: -3px;
- filter: drop-shadow(0 0 0.5px $white) drop-shadow(0 0 1px $white) drop-shadow(0 0 2px $white);
- }
-}
-
-@mixin right-sidebar {
- position: fixed;
- top: $header-height;
- // Default value for CSS var must contain a unit
- // stylelint-disable-next-line length-zero-no-unit
- bottom: var(--review-bar-height, 0px);
- right: 0;
- transition: width $gl-transition-duration-medium;
- background-color: $white;
- z-index: 200;
- overflow: hidden;
-
-}
-
-.right-sidebar {
- &:not(.right-sidebar-merge-requests) {
- @include right-sidebar;
- }
-
- &.right-sidebar-merge-requests {
- @include media-breakpoint-down(md) {
- @include right-sidebar;
- z-index: 251;
- }
- }
-
- @include media-breakpoint-down(sm) {
- z-index: 251;
- }
-
- a:not(.btn) {
- color: inherit;
-
- &:hover {
- color: $blue-800;
- }
- }
-
- .gl-label .gl-label-link:hover {
- color: inherit;
- }
-
- .btn-link {
- color: inherit;
- }
-
- .issuable-header-text {
- margin-top: 7px;
- }
-
- .gutter-toggle {
- display: flex;
- align-items: center;
- margin-left: 20px;
- padding: 4px;
- border-radius: 4px;
- height: 24px;
-
- &:hover {
- color: $gl-text-color;
- background: $gray-50;
- }
-
- &:hover,
- &:focus {
- text-decoration: none;
- }
- }
-
- &.right-sidebar-merge-requests {
- .block,
- .sidebar-contained-width,
- .issuable-sidebar-header {
- width: 100%;
- }
-
- .block {
- @include media-breakpoint-up(lg) {
- padding: $gl-spacing-scale-4 0 $gl-spacing-scale-5;
- }
-
- &.participants {
- border-bottom: 0;
- }
- }
- }
-
- .block,
- .sidebar-contained-width,
- .issuable-sidebar-header {
- @include clearfix;
- padding: $gl-spacing-scale-4 0 $gl-spacing-scale-5;
- border-bottom: 1px solid $border-gray-normal;
- // This prevents the mess when resizing the sidebar
- // of elements repositioning themselves..
- width: $gutter-inner-width;
- // --
-
- &:last-child {
- border: 0;
- }
-
- &.assignee {
- .author-link {
- display: block;
- position: relative;
-
- &:hover {
- .author {
- text-decoration: underline;
- }
- }
- }
- }
-
- &.time-tracking,
- &.participants,
- &.subscriptions,
- &.with-sub-blocks {
- padding-top: $gl-spacing-scale-5;
- }
- }
-
- .block-first {
- padding-top: 0;
- }
-
- .title {
- color: $gl-text-color;
- line-height: $gl-line-height-20;
-
- .avatar {
- margin-left: 0;
- }
- }
-
- .selectbox {
- display: none;
-
- &.show {
- display: block;
- }
- }
-
- .btn-clipboard:hover {
- color: $gl-text-color;
- }
-
- .issuable-sidebar {
- height: 100%;
-
- &:not(.is-merge-request) {
- overflow-y: scroll;
- overflow-x: hidden;
- -webkit-overflow-scrolling: touch;
- }
-
- &.is-merge-request {
- @include media-breakpoint-down(sm) {
- overflow-y: scroll;
- overflow-x: hidden;
- -webkit-overflow-scrolling: touch;
- }
- }
- }
-
- &.right-sidebar-expanded {
- &:not(.right-sidebar-merge-requests) {
- width: $gutter-width;
- }
-
- .value {
- line-height: 1;
- }
-
- .issuable-sidebar {
- padding: 0 20px;
-
- &.is-merge-request {
- @include media-breakpoint-up(lg) {
- padding: 0;
-
- .issuable-context-form {
- --initial-top: calc(#{$header-height} + 76px);
- --top: var(--initial-top);
-
- @include gl-sticky;
- @include gl-overflow-auto;
-
- top: var(--top);
- height: calc(100vh - var(--top));
- padding: 0 15px;
- margin-bottom: calc(var(--top) * -1);
-
- .with-performance-bar & {
- --top: calc(var(--initial-top) + #{$performance-bar-height});
- }
-
- .with-system-header & {
- --top: calc(var(--initial-top) + #{$system-header-height});
- }
-
- .with-performance-bar.with-system-header & {
- --top: calc(var(--initial-top) + #{$system-header-height} + #{$performance-bar-height});
- }
- }
- }
- }
- }
-
- &:not(.boards-sidebar):not([data-signed-in]):not([data-always-show-toggle]) {
- .issuable-sidebar-header {
- display: none;
- }
- }
-
- .light {
- font-weight: $gl-font-weight-normal;
- }
-
- .no-value {
- color: $gl-text-color-secondary;
- }
-
- .sidebar-collapsed-icon {
- display: none;
- }
-
- .gutter-toggle {
- text-align: center;
- }
-
- .title .gutter-toggle {
- margin-top: 0;
- }
-
- .assignee .user-list .avatar {
- margin: 0;
- }
-
- .hide-expanded {
- display: none;
- }
- }
-
- &.right-sidebar-collapsed {
- /* Extra small devices (phones, less than 768px) */
- display: none;
- /* Small devices (tablets, 768px and up) */
-
- &:not(.right-sidebar-merge-requests) {
- @include media-breakpoint-up(sm) {
- display: block;
- }
- }
-
- &.right-sidebar-merge-requests {
- @include media-breakpoint-up(lg) {
- display: block;
- }
- }
-
- width: $gutter-collapsed-width;
- padding: 0;
-
- .block,
- .sidebar-contained-width,
- .issuable-sidebar-header {
- width: $gutter-collapsed-width - 2px;
- padding: 0;
- border-bottom: 0;
- overflow: hidden;
- }
-
- .block,
- .gutter-toggle,
- .sidebar-collapsed-container {
- &.with-sub-blocks .sub-block:hover,
- &:not(.with-sub-blocks):hover {
- background-color: $gray-100;
- }
- }
-
- .participants {
- border-bottom: 1px solid $border-gray-normal;
- }
-
- .hide-collapsed {
- display: none;
- }
-
- .gutter-toggle {
- width: 100%;
- height: $sidebar-toggle-height;
- margin-left: 0;
- border-bottom: 1px solid $border-white-normal;
- border-radius: 0;
- }
-
- a.gutter-toggle {
- display: flex;
- justify-content: center;
- flex-direction: column;
- text-align: center;
- }
-
- .merge-icon {
- height: 12px;
- width: 12px;
- bottom: -5px;
- right: 4px;
- }
-
- .sidebar-collapsed-icon {
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- width: 100%;
- height: $sidebar-toggle-height;
- text-align: center;
- color: $gl-text-color-secondary;
-
- > svg {
- fill: $gl-text-color-secondary;
- }
-
- &:hover:not(.disabled),
- &:hover .todo-undone {
- color: $gl-text-color;
-
- > svg {
- fill: $gl-text-color;
- }
- }
-
- .todo-undone {
- color: $blue-600;
- fill: $blue-600;
- }
-
- .author {
- display: none;
- }
-
- .avatar-counter:hover {
- color: $gl-text-color-secondary;
- border-color: $gl-text-color-secondary;
- }
-
- .btn-clipboard {
- /*
- This change should be temporary, because the DOM currently gets
- generated from a ruby definition in `app/helpers/button_helper.rb`.
- As soon as the `copy to clipboard` button will be transferred to
- Vue this should be adjusted as well.
- */
- flex: 1;
- align-self: stretch;
- padding: 0;
-
- border: 0;
- background: transparent;
- color: $gl-text-color-secondary;
-
- &:hover {
- color: $gl-text-color;
- }
- }
-
- &.multiple-users {
- display: flex;
- justify-content: center;
- }
- }
-
- .sidebar-avatar-counter {
- width: 24px;
- height: 24px;
- border-radius: 12px;
-
- ~.merge-icon {
- bottom: 0;
- }
- }
-
- .sidebar-collapsed-user {
- padding-bottom: 0;
-
- .author-link {
- padding-left: 0;
-
- .avatar {
- position: static;
- margin: 0;
- }
- }
- }
-
- .issuable-header-btn {
- display: none;
- }
-
- .multiple-users {
- .btn-link {
- padding: 0;
- border: 0;
-
- .avatar {
- margin: 0;
- }
- }
-
- .btn-link:first-child {
- position: absolute;
- left: 10px;
- z-index: 1;
- }
-
- .btn-link:last-child {
- position: absolute;
- right: 10px;
-
- &:hover {
- text-decoration: none;
- }
- }
- }
-
- .milestone-title span,
- .collapse-truncated-title {
- @include str-truncated(100%);
- display: block;
- margin: 0 4px;
- }
- }
-
- .dropdown-menu-toggle {
- width: 100%;
- padding-top: 6px;
- }
-
- .dropdown-menu {
- width: 100%;
-
- /*
- * Overwrite hover style for dropdown items, so that they are not blue
- * This should be removed during dev of https://gitlab.com/gitlab-org/gitlab-foss/issues/44040
- */
- li a {
- &:hover,
- &:active,
- &:focus,
- &.is-focused {
- @include dropdown-item-hover;
- }
- }
-
- }
-}
-
-.with-performance-bar .right-sidebar {
- top: calc(#{$header-height} + #{$performance-bar-height});
-}
-
-.sidebar-move-issue-confirmation-button {
- width: 100%;
-
- &.is-loading {
- .sidebar-move-issue-confirmation-loading-icon {
- display: inline-block;
- }
- }
-}
-
-.sidebar-move-issue-confirmation-loading-icon {
- display: none;
-}
-
-.detail-page-description {
- padding: 16px 0;
-
- small {
- color: $gray-500;
- }
-}
-
-.edited-text {
- color: $gray-500;
- display: block;
- margin: 16px 0 0;
- font-size: 85%;
-
- .author-link {
- color: $gray-500;
- }
-}
-
-.participants-author {
- &:nth-of-type(8n) {
- padding-right: 0;
- }
-
- .avatar.avatar-inline {
- margin: 0;
- }
-}
-
-.user-item {
- padding: 5px;
- flex-basis: 20%;
-
- .user-link {
- display: inline-block;
- }
-}
-
-.participants-more,
-.user-list-more {
- margin-left: 5px;
-
- a,
- .btn-link {
- color: $gl-text-color-secondary;
- }
-
- .btn-link {
- padding: 0;
- }
-
- .btn-link:hover {
- color: $blue-800;
- text-decoration: none;
- }
-
- .btn-link:focus {
- text-decoration: none;
- }
-}
-
-.issuable-gutter-toggle {
- @include media-breakpoint-down(sm) {
- margin-left: $btn-side-margin;
- }
-}
-
-.issuable-meta {
- flex: 1;
- display: inline-block;
- font-size: 14px;
- align-self: center;
- overflow: hidden;
- text-overflow: ellipsis;
-
- .user-status-emoji {
- margin-left: $gl-padding-4;
- margin-right: 0;
- }
-}
-
-.js-issuable-selector-wrap {
- .js-issuable-selector {
- width: 100%;
- }
-
- @include media-breakpoint-down(sm) {
- margin-bottom: $gl-padding;
- }
-}
-
-.issuable-list {
- li {
- .issuable-info-container {
- flex: 1;
- display: flex;
- }
-
- .issuable-main-info {
- flex: 1 auto;
- margin-right: 10px;
- min-width: 0;
-
- .issue-weight-icon,
- .issue-estimate-icon {
- vertical-align: sub;
- }
- }
-
- .issuable-meta {
- display: flex;
- flex-direction: column;
- align-items: flex-end;
- flex: 1 0 auto;
-
- .controls {
- margin-bottom: 2px;
- line-height: 20px;
- padding: 0;
- }
- }
-
- @include media-breakpoint-down(xs) {
- .issuable-meta {
- .controls li {
- margin-right: 0;
- }
- }
- }
-
- .issue-check {
- min-width: 15px;
- }
-
- .issuable-milestone,
- .issuable-info,
- .task-status,
- .issuable-timestamp {
- font-weight: $gl-font-weight-normal;
- color: $gl-text-color-secondary;
-
- a {
- color: $gl-text-color;
- }
-
- .gl-label-link {
- color: inherit;
-
- &:hover {
- text-decoration: none;
-
- .gl-label-text:last-of-type {
- text-decoration: underline;
- }
- }
- }
-
- .milestone {
- color: $gray-700;
- }
- }
-
- @media(max-width: map-get($grid-breakpoints, lg)-1) {
- .task-status,
- .issuable-due-date,
- .issuable-weight,
- .project-ref-path {
- display: none;
- }
- }
- }
-}
-
-.issuable-list li,
-.issuable-info-container .controls {
- .avatar-counter {
- display: inline-block;
- vertical-align: middle;
- min-width: 16px;
- line-height: 14px;
- height: 16px;
- padding-left: 2px;
- padding-right: 2px;
- }
-}
-
-.add-issuable-form-input-wrapper {
- &.focus {
- border-color: $gray-700;
- @include gl-focus;
-
- input {
- @include gl-shadow-none;
- }
- }
-
- .gl-show-field-errors &.form-control:not(textarea) {
- height: auto;
- }
-}
-
-.sidebar-help-wrap {
- .sidebar-help-state {
- margin: 16px -20px -20px;
- padding: 16px 20px;
- }
-
- .help-state-toggle-enter-active {
- transition: all 0.8s ease;
- }
-
- .help-state-toggle-leave-active {
- transition: all 0.5s ease;
- }
-
- .help-state-toggle-enter,
- .help-state-toggle-leave-active {
- opacity: 0;
- }
-}
-
-.time-tracker {
- .sidebar-collapsed-icon {
- > .stopwatch-svg {
- display: inline-block;
- }
-
- svg {
- width: 16px;
- height: 16px;
- fill: $gl-text-color-secondary;
- }
-
- &:hover svg {
- fill: $gl-text-color;
- }
- }
-
- .compare-meter {
- &.over_estimate {
- .time-remaining,
- .compare-value.spent {
- color: $red-500;
- }
- }
- }
-
- .compare-display-container {
- font-size: 13px;
- }
-}
-
-/*
- * Following overrides are done to prevent
- * legacy dropdown styles from influencing
- * GitLab UI components used within GlDropdown
- */
-.issuable-move-dropdown {
- .b-dropdown-form {
- @include gl-p-0;
- }
-
- .gl-search-box-by-type button.gl-clear-icon-button:hover {
- @include gl-bg-transparent;
- }
-
- .issuable-move-button:not(.disabled):hover {
- @include gl-text-white;
- }
-}
-
-.right-sidebar-collapsed {
- .sidebar-grouped-item {
- .sidebar-collapsed-icon {
- margin-bottom: 0;
- }
-
- .sidebar-collapsed-divider {
- line-height: 5px;
- font-size: 12px;
- color: $gray-500;
-
- + .sidebar-collapsed-icon {
- padding-top: 0;
- }
- }
- }
-}
-
-.suggestion-footer {
- font-size: 12px;
- line-height: 15px;
-
- .avatar {
- margin-top: -3px;
- border: 0;
- }
-}
-
-@include media-breakpoint-down(sm) {
- // Overriding the following rule with the negative margin
- // https://gitlab.com/gitlab-org/gitlab/-/blob/146c43c931c3743a140529307aea616e4aa9ff21/app/assets/stylesheets/framework/sidebar.scss#L1-5
- .container-fluid {
- .issuable-list,
- .issues-filters,
- .epics-filters {
- margin: 0 (-$gl-padding);
- }
- }
-}
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index d4ad6da7f4d..360ea20733d 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -262,7 +262,7 @@
.footer-container,
hr.footer-fixed {
- position: absolute;
+ position: fixed;
bottom: 0;
left: 0;
right: 0;
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index b016d0a1068..6b662359a67 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -382,3 +382,9 @@ $comparison-empty-state-height: 62px;
.survey-slide-up-enter-active {
@include gl-transition-slow;
}
+
+.mr-compare-dropdown {
+ .gl-button-text {
+ @include gl-w-full;
+ }
+}
diff --git a/app/assets/stylesheets/pages/ml_experiment_tracking.scss b/app/assets/stylesheets/pages/ml_experiment_tracking.scss
index 2dff51cff92..c1582f2090b 100644
--- a/app/assets/stylesheets/pages/ml_experiment_tracking.scss
+++ b/app/assets/stylesheets/pages/ml_experiment_tracking.scss
@@ -14,3 +14,9 @@
color: $gl-text-color;
}
}
+
+table.candidate-details {
+ td {
+ padding: $gl-spacing-scale-3;
+ }
+}
diff --git a/app/assets/stylesheets/pages/monitor.scss b/app/assets/stylesheets/pages/monitor.scss
deleted file mode 100644
index 25ff5abd774..00000000000
--- a/app/assets/stylesheets/pages/monitor.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-.chart-tooltip > .popover {
- min-width: 0;
- width: max-content;
- max-width: $chart-tooltip-max-width;
-}
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index cb77c31d59a..adeab227670 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -287,8 +287,7 @@ table {
.discussion-reply-holder {
.reply-placeholder-text-field {
- font-family: $monospace-font;
- font-size: $gl-font-size-monospace;
+ @include gl-font-monospace;
border-radius: $gl-border-radius-base;
width: 100%;
resize: none;
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index fa3c87490f1..75d52424fd9 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -4,7 +4,7 @@ $system-note-svg-size: 1rem;
@mixin vertical-line($left) {
&::before {
content: '';
- border-left: 2px solid var(--gray-10, $gray-10);
+ border-left: 2px solid var(--gray-50, $gray-50);
position: absolute;
top: $gl-padding-6;
bottom: 0;
@@ -60,6 +60,10 @@ $system-note-svg-size: 1rem;
padding: $gl-padding-4 $gl-padding-8;
}
+ &.draft-note .timeline-content:not(.flash-container) {
+ border: 0;
+ }
+
.note-header-info {
min-height: 2rem;
display: flex;
@@ -81,7 +85,7 @@ $system-note-svg-size: 1rem;
margin-top: 5px;
}
- .timeline-content {
+ .timeline-content:not(.flash-container) {
margin-left: 2.5rem;
border-left: 1px solid $border-color;
border-right: 1px solid $border-color;
@@ -93,16 +97,26 @@ $system-note-svg-size: 1rem;
}
}
+ &.draft-note .timeline-content:not(.flash-container) {
+ margin-left: 0;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ }
+
&:not(:first-of-type) .timeline-entry-inner {
margin-left: 2.5rem;
border-left: 1px solid $border-color;
border-right: 1px solid $border-color;
background-color: $white;
- .timeline-content {
+ .timeline-content:not(.flash-container) {
padding: $gl-padding-8 $gl-padding-8 $gl-padding-8 $gl-padding;
}
+ .timeline-discussion-body-footer {
+ padding: 0 $gl-padding-8 0;
+ }
+
.timeline-avatar {
margin: $gl-padding-8 0 0 $gl-padding;
}
@@ -111,6 +125,12 @@ $system-note-svg-size: 1rem;
margin-left: 2rem;
}
}
+
+ &:last-of-type .timeline-entry-inner {
+ border-bottom: 1px solid $border-color;
+ border-bottom-left-radius: $gl-border-radius-base;
+ border-bottom-right-radius: $gl-border-radius-base;
+ }
}
.diff-content {
@@ -416,17 +436,17 @@ $system-note-svg-size: 1rem;
.timeline-icon {
display: flex;
align-items: center;
- background-color: $gray-10;
+ background-color: $gray-50;
width: $system-note-icon-size;
height: $system-note-icon-size;
- border: 1px solid $gray-10;
+ border: 1px solid $gray-50;
border-radius: $system-note-icon-size;
margin: -6px 0 0;
svg {
width: $system-note-svg-size;
height: $system-note-svg-size;
- fill: $gray-400;
+ fill: $gray-600;
display: block;
margin: 0 auto;
}
@@ -1050,7 +1070,7 @@ $system-note-svg-size: 1rem;
padding-left: 0;
ul.notes li.note-wrapper {
- .timeline-content {
+ .timeline-content:not(.flash-container) {
padding: $gl-padding-8 $gl-padding-8 $gl-padding-8 $gl-padding;
}
@@ -1066,7 +1086,7 @@ $system-note-svg-size: 1rem;
border-right: 0;
}
- div.discussion-reply-holder {
+ .discussion-reply-holder {
margin-left: 0;
}
}
@@ -1097,7 +1117,7 @@ $system-note-svg-size: 1rem;
}
}
- .draft-note-component .draft-note.timeline-entry {
+ .draft-note-component.draft-note.timeline-entry {
.timeline-content:not(.flash-container) {
padding: $gl-padding-8 $gl-padding-8 $gl-padding-8 $gl-padding;
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index bf20204cfd9..15a32ea8ad3 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -247,7 +247,7 @@
.repository-languages-bar {
height: 8px;
- margin-bottom: $gl-padding-8;
+ margin-bottom: $gl-padding;
background-color: $white;
border-radius: $border-radius-default;
@@ -562,7 +562,7 @@
// Remove once gitlab/ui solution is implemented
// https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1158
// https://gitlab.com/gitlab-org/gitlab/-/issues/300405
- .gl-new-dropdown-button-text {
+ .gl-dropdown-button-text {
@include str-truncated;
}
}
@@ -654,86 +654,3 @@
}
}
}
-
-.project-filters {
- .btn svg {
- color: $gray-700;
- }
-
- .button-filter-group {
- .btn {
- width: 96px;
- }
-
- a {
- color: $black;
- }
-
- .active {
- background: $btn-active-gray;
- }
- }
-
- .filtered-search-dropdown-label {
- min-width: 68px;
-
- @include media-breakpoint-down(xs) {
- min-width: 60px;
- }
- }
-
- .filtered-search {
- min-width: 30%;
- flex-basis: 0;
-
- .project-filter-form .project-filter-form-field {
- padding-right: $gl-padding-8;
- }
-
- .filtered-search,
- .filtered-search-nav,
- .filtered-search-dropdown {
- flex-basis: 0;
- }
-
- @include media-breakpoint-down(lg) {
- min-width: 15%;
-
- .project-filter-form-field {
- min-width: 150px;
- }
- }
-
- @include media-breakpoint-down(md) {
- min-width: 30%;
- }
- }
-
- .filtered-search-box {
- border-radius: 3px 0 0 3px;
- }
-
- .dropdown-menu-toggle {
- margin-left: $gl-padding-8;
- }
-
- @include media-breakpoint-down(md) {
- .extended-filtered-search-box {
- min-width: 55%;
- }
-
- .filtered-search-dropdown {
- width: 50%;
-
- .dropdown-menu-toggle {
- width: 100%;
- }
- }
- }
-
- @include media-breakpoint-down(xs) {
- .filtered-search-dropdown {
- width: 100%;
- }
- }
-}
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
deleted file mode 100644
index 1bca04e5eb1..00000000000
--- a/app/assets/stylesheets/pages/search.scss
+++ /dev/null
@@ -1,405 +0,0 @@
-$search-dropdown-max-height: 400px;
-$search-avatar-size: 16px;
-$search-sidebar-min-width: 240px;
-$search-sidebar-max-width: 300px;
-$search-input-field-x-min-width: 200px;
-$search-input-field-min-width: 320px;
-$search-input-field-max-width: 640px;
-$search-keyboard-shortcut: '/';
-
-$border-radius-medium: 3px;
-
-.search-results {
- .search-result-row {
- border-bottom: 1px solid $border-color;
- padding-bottom: $gl-padding;
- margin-bottom: $gl-padding;
-
- &:last-child {
- border-bottom: 0;
- }
- }
-}
-
-.search-sidebar {
- @include media-breakpoint-up(md) {
- min-width: $search-sidebar-min-width;
- max-width: $search-sidebar-max-width;
- }
-}
-
-.search form:hover,
-.file-finder-input:hover,
-.issuable-search-form:hover,
-.search-text-input:hover,
-.form-control:hover,
-:not[readonly] {
- border-color: lighten($blue-300, 20%);
- box-shadow: 0 0 4px lighten($dropdown-input-focus-shadow, 20%);
-}
-
-input[type='checkbox']:hover {
- box-shadow: 0 0 2px 2px lighten($dropdown-input-focus-shadow, 20%),
- 0 0 0 1px lighten($dropdown-input-focus-shadow, 20%);
-}
-
-.header-content {
- .header-search-new {
- max-width: $search-input-field-max-width;
- }
-
- &.header-search-is-active {
- .global-search-container {
- flex-grow: 1;
- }
- }
-}
-
-.header-search {
- min-width: $search-input-field-min-width;
-
- // This is a temporary workaround!
- // the button in GitLab UI Search components need to be updated to not be the small size
- // see in Figma: https://www.figma.com/file/qEddyqCrI7kPSBjGmwkZzQ/Component-library?node-id=43905%3A45540
- .gl-search-box-by-type-clear.btn-sm {
- padding: 0.5rem !important;
- }
-
- @include media-breakpoint-between(md, lg) {
- min-width: $search-input-field-x-min-width;
- }
-
- &.is-searching {
- .in-search-scope-help {
- position: absolute;
- top: $gl-spacing-scale-2;
- right: 2.125rem;
- z-index: 2;
- }
- }
-
- &.is-not-focused {
- .gl-search-box-by-type-clear {
- display: none;
- }
- }
-
- .keyboard-shortcut-helper {
- transform: translateY(calc(50% - 2px));
- box-shadow: none;
- border-color: transparent;
- }
-}
-
-.header-search-dropdown-menu {
- max-height: $dropdown-max-height;
- top: 100%;
-}
-
-.search {
- margin: 0 8px;
-
- form {
- display: block;
- margin: 0;
- padding: 4px;
- width: $search-input-width;
- line-height: 24px;
- height: 32px;
- border: 0;
- border-radius: $border-radius-default;
- transition: border-color ease-in-out $default-transition-duration,
- background-color ease-in-out $default-transition-duration;
-
- @include media-breakpoint-up(xl) {
- width: $search-input-xl-width;
- }
-
- &:hover {
- box-shadow: none;
- }
- }
-
- .search-input {
- border: 0;
- font-size: 14px;
- padding: 0 20px 0 0;
- margin-left: 5px;
- line-height: 25px;
- width: 98%;
- color: $white;
- background: none;
- transition: color ease-in-out $default-transition-duration;
- }
-
- .search-input::placeholder {
- transition: color ease-in-out $default-transition-duration;
- }
-
- .search-input-container {
- display: flex;
- position: relative;
- }
-
- .search-input-wrap {
- width: 100%;
-
- .search-icon,
- .clear-icon {
- position: absolute;
- right: 5px;
- top: 4px;
- }
-
- .search-icon {
- transition: color $default-transition-duration;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- }
-
- .clear-icon {
- display: none;
- }
-
- // Rewrite position. Dropdown menu should be relative to .search-input-container
- .dropdown {
- position: static;
- }
-
- .dropdown-header {
- // Necessary because deprecatedJQueryDropdown doesn't support a second style of headers
- font-weight: $gl-font-weight-bold;
- color: $gl-text-color;
- font-size: $gl-font-size;
- line-height: 16px;
- }
-
- // Custom dropdown positioning
- .dropdown-menu {
- left: -5px;
- max-height: $search-dropdown-max-height;
- overflow: auto;
-
- @include media-breakpoint-up(xl) {
- width: $search-input-xl-width;
- }
- }
-
- .dropdown-content {
- max-height: $search-dropdown-max-height - 18px;
- }
- }
-
- &.search-active {
- form {
- border-color: $blue-300;
- box-shadow: none;
-
- .search-input-wrap {
- .search-icon,
- .clear-icon {
- color: $gl-text-color-tertiary;
- transition: color ease-in-out $default-transition-duration;
- }
- }
-
- .search-input {
- color: $gl-text-color;
- transition: color ease-in-out $default-transition-duration;
- }
-
- .search-input::placeholder {
- color: $gl-text-color-tertiary;
- }
- }
- }
-
- &.has-value {
- .search-icon {
- display: none;
- }
-
- .clear-icon {
- cursor: pointer;
- display: block;
- }
- }
-
- .inline-search-icon {
- position: relative;
- margin-right: 4px;
- color: $gl-text-color-secondary;
- }
-
- .identicon,
- .search-item-avatar {
- flex-basis: $search-avatar-size;
- flex-shrink: 0;
- margin-right: 4px;
- }
-
- .search-item-avatar {
- width: $search-avatar-size;
- height: $search-avatar-size;
- border-radius: 50%;
- border: 1px solid $gray-normal;
- }
-}
-
-.search-field-holder,
-.project-filter-form {
- flex: 1 0 auto;
- position: relative;
-
- .search-holder & {
- margin-right: 0;
-
- @include media-breakpoint-up(sm) {
- margin-right: 5px;
- }
- }
-
- .search-icon {
- position: absolute;
- left: 10px;
- top: 9px;
- color: $gray-darkest;
- pointer-events: none;
- }
-
- .search-text-input {
- padding-left: $gl-padding + 15px;
- padding-right: $gl-padding + 15px;
- }
-}
-
-.search-holder {
- @include media-breakpoint-up(sm) {
- display: flex;
- }
-
- .btn-search,
- .btn-success,
- .dropdown-menu-toggle,
- .gl-new-dropdown {
- width: 100%;
- margin-top: 5px;
-
- @include media-breakpoint-up(sm) {
- width: auto;
- margin-top: 0;
- margin-left: 5px;
- }
- }
-
- .dropdown {
- @include media-breakpoint-up(sm) {
- margin-left: 5px;
- margin-right: 5px;
- }
- }
-
- .dropdown-menu-toggle,
- .gl-new-dropdown {
- @include media-breakpoint-up(sm) {
- width: 180px;
- margin-top: 0;
- }
- }
-}
-
-.search-page-form {
- .dropdown-menu-toggle,
- .btn-search {
- width: 100%;
- }
-
- .dropdown-menu-toggle,
- .gl-new-dropdown {
- @include media-breakpoint-up(lg) {
- width: 240px;
- }
- }
-
- .btn-search {
- @include media-breakpoint-up(lg) {
- width: auto;
- }
- }
-}
-
-.ref-truncated {
- @include str-truncated(10em);
-}
-
-.global-search-dropdown-menu {
- width: 100% !important;
- max-width: 400px;
-
- @include media-breakpoint-up(md) {
- // This is larger than the container so width: 100% doesn't work.
- width: 400px !important;
- }
-}
-
-// This overrides parts of the Project File View CSS
-// We leverage most of the styling but broke off
-// from how we were doing it in `shared/file_highlight`
-#search-blob-content {
- .line_holder {
- pre {
- padding: 0; // This overrides the existing style that will add space between each line.
- .line {
- @include gl-word-break-word;
- white-space: break-spaces;
- }
- }
-
- svg {
- float: none; // We have more than one icon on this implementation and don't want to float them.
- margin: 0; // We will manage the margin with GitLab UI utility classes
- }
-
- .line-numbers {
- padding: 0; // This overrides the existing style that will add space between each line.
- min-width: 6.5rem; // Ensure our numbers fit
-
- .diff-line-num {
- a {
- transition: none; // There will be a hover transition from theme, blue, darkened
- }
- }
- }
-
- &:hover {
- svg {
- visibility: visible; // We want to show the icons when the any part of the line is hovered
- }
- }
-
- // The icons only appear on hover
- // So on mobile we can hide them and retake the space for the code blob
- @include media-breakpoint-down(sm) {
- svg {
- display: none;
- }
-
- .line-numbers {
- min-width: 4rem;
- }
- }
- }
-}
-
-// Disable Webkit's search input styles
-input[type='search'] {
- /* stylelint-disable-next-line property-no-vendor-prefix */
- -webkit-appearance: textfield;
-
- &::-webkit-search-cancel-button,
- &::-webkit-search-results-button {
- @include gl-display-none;
- }
-}
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index c364b233803..2d78ab82b7d 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -1,149 +1,3 @@
-@keyframes expandMaxHeight {
- 0% {
- max-height: 0;
- }
-
- 99% {
- max-height: 100vh;
- }
-
- 100% {
- max-height: none;
- }
-}
-
-@keyframes collapseMaxHeight {
- 0% {
- max-height: 100vh;
- }
-
- 100% {
- max-height: 0;
- }
-}
-
-.settings {
- // border-top for each item except the top one
- border-top: 1px solid $border-color;
-
- &:first-of-type {
- margin-top: 10px;
- padding-top: 0;
- border: 0;
- }
-
- + div .settings:first-of-type {
- margin-top: 0;
- border-top: 1px solid $border-color;
- }
-
- &.animating {
- overflow: hidden;
- }
-}
-
-.settings-header {
- position: relative;
- padding: 24px 110px 0 0;
-
- h4 {
- margin-top: 0;
- }
-
- .settings-title {
- cursor: pointer;
- }
-
- button {
- position: absolute;
- top: 20px;
- right: 6px;
- min-width: 80px;
- }
-}
-
-.settings-content {
- max-height: 1px;
- overflow-y: hidden;
- padding-right: 110px;
- animation: collapseMaxHeight 300ms ease-out;
- // Keep the section from expanding when we scroll over it
- pointer-events: none;
-
- .settings.expanded & {
- max-height: none;
- overflow-y: visible;
- animation: expandMaxHeight 300ms ease-in;
- // Reset and allow clicks again when expanded
- pointer-events: auto;
- }
-
- .settings.no-animate & {
- animation: none;
- }
-
- @media(max-width: map-get($grid-breakpoints, md)-1) {
- padding-right: 20px;
- }
-
- &::before {
- content: ' ';
- display: block;
- height: 1px;
- overflow: hidden;
- margin-bottom: 4px;
- }
-
- &::after {
- content: ' ';
- display: block;
- height: 1px;
- overflow: hidden;
- margin-top: 20px;
- }
-
- .sub-section {
- margin-bottom: 32px;
- padding: 16px;
- border: 1px solid $border-color;
- background-color: $gray-light;
- }
-
- .bs-callout,
- .form-check:first-child,
- .form-check .form-text.text-muted,
- .form-check + .form-text.text-muted {
- margin-top: 0;
- }
-
- .form-check .form-text.text-muted {
- margin-bottom: $grid-size;
- }
-}
-
-.settings-list-icon {
- color: $gl-text-color-secondary;
- font-size: $default-icon-size;
- line-height: 42px;
-}
-
-.settings-message {
- padding: 5px;
- line-height: 1.3;
- color: $gray-900;
- background-color: $orange-50;
- border: 1px solid $orange-200;
- border-radius: $border-radius-base;
-}
-
-.warning-title {
- color: $gray-900;
-}
-
-.danger-title {
- color: $red-500;
-}
-
.integration-settings-form {
.card.card-body,
.info-well {
@@ -160,13 +14,13 @@
.option-title {
font-weight: $gl-font-weight-normal;
display: inline-block;
- color: $gl-text-color;
+ color: var(--gl-text-color, $gl-text-color);
vertical-align: top;
}
.option-description,
.option-disabled-reason {
- color: $project-option-descr-color;
+ color: var(--gray-700, $gray-700);
}
.option-disabled-reason {
@@ -188,79 +42,9 @@
}
}
-.prometheus-metrics-monitoring {
- .card {
- .card-toggle {
- width: 14px;
- }
-
- .badge.badge-pill {
- font-size: 12px;
- line-height: 12px;
- }
-
- .card-header .label-count {
- color: $white;
- background: $common-gray-dark;
- }
-
- .card-body {
- padding: 0;
- }
-
- .flash-container {
- margin-bottom: 0;
- cursor: default;
-
- .flash-notice {
- border-radius: 0;
- }
- }
- }
-
- .custom-monitored-metrics {
- .card-header {
- display: flex;
- align-items: center;
- }
-
- .custom-metric {
- display: flex;
- align-items: center;
- }
-
- .custom-metric-link-bold {
- font-weight: $gl-font-weight-bold;
- text-decoration: none;
- }
- }
-
- .loading-metrics .metrics-load-spinner {
- color: $gray-700;
- }
-
- .metrics-list {
- margin-bottom: 0;
-
- li {
- padding: $gl-padding;
-
- .badge.badge-pill {
- margin-left: 5px;
- background: $badge-bg;
- }
-
- /* Ensure we don't add border if there's only single li */
- + li {
- border-top: 1px solid $border-color;
- }
- }
- }
-}
-
.saml-settings.info-well {
.form-control[readonly] {
- background: $white;
+ background: var(--white, $white);
}
}
@@ -275,8 +59,8 @@
}
.btn-clipboard {
- background-color: $white;
- border: 1px solid $gray-100;
+ background-color: var(--white, $white);
+ border: 1px solid var(--gray-100, $gray-100);
}
.deploy-token-help-block {
@@ -294,7 +78,7 @@
.ci-secure-files-table {
table {
thead {
- border-bottom: 1px solid $white-normal;
+ border-bottom: 1px solid var(--gray-50, $gray-50);
}
tr {
diff --git a/app/assets/stylesheets/pages/users.scss b/app/assets/stylesheets/pages/users.scss
deleted file mode 100644
index 3dcc17df61a..00000000000
--- a/app/assets/stylesheets/pages/users.scss
+++ /dev/null
@@ -1,68 +0,0 @@
-.user-search-form {
- position: relative;
-
- @include media-breakpoint-up(sm) {
- float: right;
- }
-
- .dropdown {
- width: 100%;
- margin-top: 5px;
-
- .dropdown-menu-toggle {
- vertical-align: middle;
- width: 100%;
- }
-
- @include media-breakpoint-up(sm) {
- margin-top: 0;
- width: 155px;
- }
- }
-
- .form-control {
- padding-right: 35px;
- }
-
- .search-control-wrap,
- .form-control {
- width: 100%;
-
- @include media-breakpoint-up(sm) {
- width: 250px;
- }
- }
-}
-
-.user-search-btn {
- position: absolute;
- right: 4px;
- top: 0;
- height: 35px;
- padding-left: 10px;
- padding-right: 10px;
- color: $gray-darkest;
- background: transparent;
- border: 0;
- outline: 0;
-}
-
-.content-list.members-list li {
- display: flex;
- justify-content: space-between;
-
- .list-item-name {
- float: none;
- display: flex;
- flex: 1;
- }
-}
-
-.card-body .user-info {
- float: left;
-
- .user {
- color: $gl-text-color;
- font-weight: $gl-font-weight-bold;
- }
-}
diff --git a/app/assets/stylesheets/startup/startup-dark.scss b/app/assets/stylesheets/startup/startup-dark.scss
index 11131cc1a4b..c7e55289b11 100644
--- a/app/assets/stylesheets/startup/startup-dark.scss
+++ b/app/assets/stylesheets/startup/startup-dark.scss
@@ -2,25 +2,6 @@
// Please see the feedback issue for more details and help:
// https://gitlab.com/gitlab-org/gitlab/-/issues/331812
@charset "UTF-8";
-:root {
- color-scheme: dark;
-}
-body.gl-dark {
- --gray-10: #1f1e24;
- --gray-50: #333238;
- --gray-100: #434248;
- --gray-200: #535158;
- --gray-700: #bfbfc3;
- --gray-900: #ececef;
- --green-100: #0d532a;
- --green-700: #91d4a8;
- --gl-text-color: #ececef;
- --border-color: #4f4f4f;
- --black: #fff;
-}
-:root {
- --white: #333;
-}
*,
*::before,
*::after {
@@ -36,9 +17,10 @@ header {
}
body {
margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
- "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue", sans-serif,
- "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ font-family: var(--default-regular-font, -apple-system), BlinkMacSystemFont,
+ "Segoe UI", Roboto, "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue",
+ sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
+ "Noto Color Emoji";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
@@ -66,8 +48,9 @@ a:not([href]):not([class]) {
text-decoration: none;
}
kbd {
- font-family: "Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas",
- "Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace;
+ font-family: var(--default-mono-font, "Menlo"), "DejaVu Sans Mono",
+ "Liberation Mono", "Consolas", "Ubuntu Mono", "Courier New", "andale mono",
+ "lucida console", monospace;
font-size: 1em;
}
img {
@@ -117,7 +100,7 @@ button::-moz-focus-inner,
kbd {
padding: 0.2rem 0.4rem;
font-size: 90%;
- color: #333;
+ color: #333238;
background-color: #ececef;
border-radius: 0.2rem;
}
@@ -142,7 +125,7 @@ kbd kbd {
font-weight: 400;
line-height: 1.5;
color: #ececef;
- background-color: #333;
+ background-color: #333238;
background-clip: padding-box;
border: 1px solid #737278;
border-radius: 0.25rem;
@@ -158,7 +141,7 @@ kbd kbd {
opacity: 1;
}
.form-control:disabled {
- background-color: #333238;
+ background-color: #24232a;
opacity: 1;
}
.form-inline {
@@ -215,7 +198,7 @@ kbd kbd {
color: #ececef;
text-align: left;
list-style: none;
- background-color: #333;
+ background-color: #333238;
background-clip: padding-box;
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 0.25rem;
@@ -410,7 +393,7 @@ a.gl-badge.badge-info:active {
background-color: #0b5cad;
}
a.gl-badge.badge-info:active {
- box-shadow: 0 0 0 1px #333, 0 0 0 3px #1f75cb;
+ box-shadow: 0 0 0 1px #333238, 0 0 0 3px #1f75cb;
outline: none;
}
.gl-badge.badge-success {
@@ -423,7 +406,7 @@ a.gl-badge.badge-success:active {
background-color: #24663b;
}
a.gl-badge.badge-success:active {
- box-shadow: 0 0 0 1px #333, 0 0 0 3px #1f75cb;
+ box-shadow: 0 0 0 1px #333238, 0 0 0 3px #1f75cb;
outline: none;
}
.gl-badge.badge-warning {
@@ -436,7 +419,7 @@ a.gl-badge.badge-warning:active {
background-color: #8f4700;
}
a.gl-badge.badge-warning:active {
- box-shadow: 0 0 0 1px #333, 0 0 0 3px #1f75cb;
+ box-shadow: 0 0 0 1px #333238, 0 0 0 3px #1f75cb;
outline: none;
}
.gl-button .gl-badge {
@@ -444,10 +427,11 @@ a.gl-badge.badge-warning:active {
}
.gl-form-input,
.gl-form-input.form-control {
- background-color: #333;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
- "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue", sans-serif,
- "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ background-color: #333238;
+ font-family: var(--default-regular-font, -apple-system), BlinkMacSystemFont,
+ "Segoe UI", Roboto, "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue",
+ sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
+ "Noto Color Emoji";
font-size: 0.875rem;
line-height: 1rem;
padding-top: 0.5rem;
@@ -526,12 +510,21 @@ a.gl-badge.badge-warning:active {
font-size: 0.875rem;
border-radius: 0.25rem;
}
+.gl-button.gl-button .gl-button-text {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ margin-top: -1px;
+ margin-bottom: -1px;
+}
.gl-button.gl-button.btn-default {
- background-color: #333;
+ background-color: #333238;
}
.gl-button.gl-button.btn-default:active,
.gl-button.gl-button.btn-default.active {
- box-shadow: inset 0 0 0 1px #a4a3a8, 0 0 0 1px #333, 0 0 0 3px #1f75cb;
+ box-shadow: inset 0 0 0 1px #a4a3a8, 0 0 0 1px #333238, 0 0 0 3px #1f75cb;
outline: none;
background-color: #434248;
}
@@ -613,7 +606,7 @@ html {
font-size: 0.875rem;
font-weight: 400;
padding: 6px 10px;
- background-color: #333;
+ background-color: #333238;
border-color: #434248;
color: #ececef;
color: #ececef;
@@ -625,7 +618,7 @@ html {
}
.btn:active,
.btn.active {
- background-color: #444;
+ background-color: #434248;
border-color: #4f4f4f;
color: #ececef;
}
@@ -649,7 +642,7 @@ html {
position: relative;
}
.dropdown-menu-toggle:active {
- box-shadow: 0 0 0 1px #333, 0 0 0 3px #1f75cb;
+ box-shadow: 0 0 0 1px #333238, 0 0 0 3px #1f75cb;
outline: none;
}
.search-input-container .dropdown-menu {
@@ -657,7 +650,7 @@ html {
}
.dropdown-menu-toggle {
padding: 6px 8px 6px 10px;
- background-color: #333;
+ background-color: #333238;
color: #ececef;
font-size: 14px;
text-align: left;
@@ -689,7 +682,7 @@ html {
font-size: 0.875rem;
font-weight: 400;
padding: 8px 0;
- background-color: #333;
+ background-color: #333238;
border: 1px solid #434248;
border-radius: 0.25rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
@@ -704,7 +697,7 @@ html {
list-style: none;
}
.dropdown-menu li > a,
-.dropdown-menu li button {
+.dropdown-menu li > button {
background: transparent;
border: 0;
border-radius: 0;
@@ -721,16 +714,16 @@ html {
width: 100%;
}
.dropdown-menu li > a:active,
-.dropdown-menu li button:active {
- background-color: #4f4f4f;
+.dropdown-menu li > button:active {
+ background-color: #4e4c53;
color: #ececef;
outline: 0;
text-decoration: none;
}
.dropdown-menu li > a:active,
-.dropdown-menu li button:active {
- box-shadow: inset 0 0 0 2px #1f75cb, inset 0 0 0 3px #333,
- inset 0 0 0 1px #333;
+.dropdown-menu li > button:active {
+ box-shadow: inset 0 0 0 2px #1f75cb, inset 0 0 0 3px #333238,
+ inset 0 0 0 1px #333238;
outline: none;
}
.dropdown-menu .divider {
@@ -765,7 +758,7 @@ html {
input {
border-radius: 0.25rem;
color: #ececef;
- background-color: #333;
+ background-color: #333238;
}
.form-control {
border-radius: 4px;
@@ -777,10 +770,10 @@ input {
kbd {
display: inline-block;
padding: 3px 5px;
- font-size: 0.6875rem;
+ font-size: 0.75rem;
line-height: 10px;
color: var(--gray-700, #bfbfc3);
- vertical-align: middle;
+ vertical-align: unset;
background-color: var(--gray-10, #1f1e24);
border-width: 1px;
border-style: solid;
@@ -840,6 +833,22 @@ kbd {
.navbar-gitlab .header-content .navbar-collapse > ul.nav > li:not(.d-none) {
margin: 0 2px;
}
+.navbar-gitlab .header-content .header-search-new {
+ max-width: 640px;
+}
+.navbar-gitlab .header-search {
+ min-width: 320px;
+}
+@media (min-width: 768px) and (max-width: 1199.98px) {
+ .navbar-gitlab .header-search {
+ min-width: 200px;
+ }
+}
+.navbar-gitlab .header-search .keyboard-shortcut-helper {
+ transform: translateY(calc(50% - 2px));
+ box-shadow: none;
+ border-color: transparent;
+}
.navbar-gitlab .navbar-collapse {
flex: 0 0 auto;
border-top: 0;
@@ -1010,7 +1019,7 @@ kbd {
float: left;
margin-right: 5px;
border-radius: 50%;
- border: 1px solid #333;
+ border: 1px solid #333238;
}
.notification-dot {
background-color: #9e5400;
@@ -1049,7 +1058,7 @@ kbd {
}
.context-header .avatar-container {
flex: 0 0 32px;
- background-color: #333;
+ background-color: #333238;
}
.context-header .sidebar-context-title {
overflow: hidden;
@@ -1142,7 +1151,7 @@ kbd {
font-weight: 600;
}
.nav-sidebar li.active:not(.fly-out-top-item) > a:not(.has-sub-items) {
- background-color: rgba(255, 255, 255, 0.08);
+ background-color: rgba(251, 250, 253, 0.08);
}
.nav-sidebar ul {
padding-left: 0;
@@ -1189,7 +1198,7 @@ kbd {
margin-bottom: -0.25rem;
margin-top: 0;
position: relative;
- color: #333;
+ color: #333238;
background: var(--black, #fff);
}
.nav-sidebar
@@ -1417,7 +1426,7 @@ kbd {
.close-nav-button {
height: 48px;
padding: 0 16px;
- background-color: #333238;
+ background-color: #24232a;
border: 0;
color: #89888d;
display: flex;
@@ -1523,87 +1532,6 @@ svg.s12 {
svg.s16 {
vertical-align: -3px;
}
-.header-content .header-search-new {
- max-width: 640px;
-}
-.header-search {
- min-width: 320px;
-}
-@media (min-width: 768px) and (max-width: 1199.98px) {
- .header-search {
- min-width: 200px;
- }
-}
-.header-search .keyboard-shortcut-helper {
- transform: translateY(calc(50% - 2px));
- box-shadow: none;
- border-color: transparent;
-}
-.search {
- margin: 0 8px;
-}
-.search form {
- display: block;
- margin: 0;
- padding: 4px;
- width: 200px;
- line-height: 24px;
- height: 32px;
- border: 0;
- border-radius: 4px;
-}
-@media (min-width: 1200px) {
- .search form {
- width: 320px;
- }
-}
-.search .search-input {
- border: 0;
- font-size: 14px;
- padding: 0 20px 0 0;
- margin-left: 5px;
- line-height: 25px;
- width: 98%;
- color: #333;
- background: none;
-}
-.search .search-input-container {
- display: flex;
- position: relative;
-}
-.search .search-input-wrap {
- width: 100%;
-}
-.search .search-input-wrap .search-icon,
-.search .search-input-wrap .clear-icon {
- position: absolute;
- right: 5px;
- top: 4px;
-}
-.search .search-input-wrap .search-icon {
- user-select: none;
-}
-.search .search-input-wrap .clear-icon {
- display: none;
-}
-.search .search-input-wrap .dropdown {
- position: static;
-}
-.search .search-input-wrap .dropdown-menu {
- left: -5px;
- max-height: 400px;
- overflow: auto;
-}
-@media (min-width: 1200px) {
- .search .search-input-wrap .dropdown-menu {
- width: 320px;
- }
-}
-.search .identicon {
- flex-basis: 16px;
- flex-shrink: 0;
- margin-right: 4px;
-}
.avatar,
.avatar-container {
float: left;
@@ -1627,7 +1555,7 @@ svg.s16 {
width: 40px;
height: 40px;
padding: 0;
- background: #222;
+ background: #212027;
overflow: hidden;
box-shadow: inset 0 0 0 1px rgba(251, 250, 253, 0.1);
}
@@ -1705,97 +1633,25 @@ svg.s16 {
}
:root {
color-scheme: dark;
-}
-body.gl-dark {
--gray-10: #1f1e24;
--gray-50: #333238;
--gray-100: #434248;
--gray-200: #535158;
- --gray-300: #626168;
- --gray-400: #737278;
- --gray-500: #89888d;
- --gray-600: #a4a3a8;
--gray-700: #bfbfc3;
- --gray-800: #dcdcde;
--gray-900: #ececef;
- --gray-950: #fbfafd;
- --green-50: #0a4020;
--green-100: #0d532a;
- --green-200: #24663b;
- --green-300: #217645;
- --green-400: #108548;
- --green-500: #2da160;
- --green-600: #52b87a;
--green-700: #91d4a8;
- --green-800: #c3e6cd;
- --green-900: #ecf4ee;
- --green-950: #f1fdf6;
- --blue-50: #033464;
- --blue-100: #064787;
- --blue-200: #0b5cad;
- --blue-300: #1068bf;
- --blue-400: #1f75cb;
- --blue-500: #428fdc;
- --blue-600: #63a6e9;
- --blue-700: #9dc7f1;
- --blue-800: #cbe2f9;
- --blue-900: #e9f3fc;
- --blue-950: #f2f9ff;
- --orange-50: #5c2900;
- --orange-100: #703800;
- --orange-200: #8f4700;
- --orange-300: #9e5400;
- --orange-400: #ab6100;
- --orange-500: #c17d10;
- --orange-600: #d99530;
- --orange-700: #e9be74;
- --orange-800: #f5d9a8;
- --orange-900: #fdf1dd;
- --orange-950: #fff4e1;
- --red-50: #660e00;
- --red-100: #8d1300;
- --red-200: #ae1800;
- --red-300: #c91c00;
- --red-400: #dd2b0e;
- --red-500: #ec5941;
- --red-600: #f57f6c;
- --red-700: #fcb5aa;
- --red-800: #fdd4cd;
- --red-900: #fcf1ef;
- --red-950: #fff4f3;
- --indigo-50: #1a1a40;
- --indigo-100: #292961;
- --indigo-200: #393982;
- --indigo-300: #4b4ba3;
- --indigo-400: #5b5bbd;
- --indigo-500: #6666c4;
- --indigo-600: #7c7ccc;
- --indigo-700: #a6a6de;
- --indigo-800: #d1d1f0;
- --indigo-900: #ebebfa;
- --indigo-950: #f7f7ff;
- --purple-50: #232150;
- --purple-100: #2f2a6b;
- --purple-200: #453894;
- --purple-300: #5943b6;
- --purple-400: #694cc0;
- --purple-500: #7b58cf;
- --purple-600: #9475db;
- --purple-700: #ac93e6;
- --purple-800: #cbbbf2;
- --purple-900: #e1d8f9;
- --purple-950: #f4f0ff;
- --dark-icon-color-purple-1: #524a68;
- --dark-icon-color-purple-2: #715bae;
- --dark-icon-color-purple-3: #9a79f7;
- --dark-icon-color-orange-1: #665349;
- --dark-icon-color-orange-2: #b37a5d;
--gl-text-color: #ececef;
- --border-color: #4f4f4f;
- --white: #333;
+ --border-color: #434248;
+ --white: #333238;
+ --black: #fff;
+}
+body.gl-dark {
+ color-scheme: dark;
+ --gray-10: #1f1e24;
+ --border-color: #434248;
+ --white: #333238;
--black: #fff;
- --gray-light: #333238;
- --svg-status-bg: #333;
}
.nav-sidebar,
.toggle-sidebar-button,
@@ -1830,7 +1686,7 @@ body.gl-dark .navbar-gitlab .navbar-sub-nav > li.active > button,
body.gl-dark .navbar-gitlab .navbar-nav > li.active > a,
body.gl-dark .navbar-gitlab .navbar-nav > li.active > button {
color: #ececef;
- background-color: #333;
+ background-color: #333238;
}
body.gl-dark .navbar-gitlab .navbar-sub-nav {
color: #ececef;
@@ -1862,10 +1718,10 @@ body.gl-dark
}
body.gl-dark .navbar-gitlab .nav > li.active > a {
color: #ececef;
- background-color: #333;
+ background-color: #333238;
}
body.gl-dark .navbar-gitlab .nav > li.active > a .notification-dot {
- border-color: #333;
+ border-color: #333238;
}
body.gl-dark
.navbar-gitlab
@@ -1947,100 +1803,6 @@ body.gl-dark .navbar-gitlab .search form .search-input {
color: var(--gl-text-color);
}
-:root {
- color-scheme: dark;
-}
-body.gl-dark {
- --gray-10: #1f1e24;
- --gray-50: #333238;
- --gray-100: #434248;
- --gray-200: #535158;
- --gray-300: #626168;
- --gray-400: #737278;
- --gray-500: #89888d;
- --gray-600: #a4a3a8;
- --gray-700: #bfbfc3;
- --gray-800: #dcdcde;
- --gray-900: #ececef;
- --gray-950: #fbfafd;
- --green-50: #0a4020;
- --green-100: #0d532a;
- --green-200: #24663b;
- --green-300: #217645;
- --green-400: #108548;
- --green-500: #2da160;
- --green-600: #52b87a;
- --green-700: #91d4a8;
- --green-800: #c3e6cd;
- --green-900: #ecf4ee;
- --green-950: #f1fdf6;
- --blue-50: #033464;
- --blue-100: #064787;
- --blue-200: #0b5cad;
- --blue-300: #1068bf;
- --blue-400: #1f75cb;
- --blue-500: #428fdc;
- --blue-600: #63a6e9;
- --blue-700: #9dc7f1;
- --blue-800: #cbe2f9;
- --blue-900: #e9f3fc;
- --blue-950: #f2f9ff;
- --orange-50: #5c2900;
- --orange-100: #703800;
- --orange-200: #8f4700;
- --orange-300: #9e5400;
- --orange-400: #ab6100;
- --orange-500: #c17d10;
- --orange-600: #d99530;
- --orange-700: #e9be74;
- --orange-800: #f5d9a8;
- --orange-900: #fdf1dd;
- --orange-950: #fff4e1;
- --red-50: #660e00;
- --red-100: #8d1300;
- --red-200: #ae1800;
- --red-300: #c91c00;
- --red-400: #dd2b0e;
- --red-500: #ec5941;
- --red-600: #f57f6c;
- --red-700: #fcb5aa;
- --red-800: #fdd4cd;
- --red-900: #fcf1ef;
- --red-950: #fff4f3;
- --indigo-50: #1a1a40;
- --indigo-100: #292961;
- --indigo-200: #393982;
- --indigo-300: #4b4ba3;
- --indigo-400: #5b5bbd;
- --indigo-500: #6666c4;
- --indigo-600: #7c7ccc;
- --indigo-700: #a6a6de;
- --indigo-800: #d1d1f0;
- --indigo-900: #ebebfa;
- --indigo-950: #f7f7ff;
- --purple-50: #232150;
- --purple-100: #2f2a6b;
- --purple-200: #453894;
- --purple-300: #5943b6;
- --purple-400: #694cc0;
- --purple-500: #7b58cf;
- --purple-600: #9475db;
- --purple-700: #ac93e6;
- --purple-800: #cbbbf2;
- --purple-900: #e1d8f9;
- --purple-950: #f4f0ff;
- --dark-icon-color-purple-1: #524a68;
- --dark-icon-color-purple-2: #715bae;
- --dark-icon-color-purple-3: #9a79f7;
- --dark-icon-color-orange-1: #665349;
- --dark-icon-color-orange-2: #b37a5d;
- --gl-text-color: #ececef;
- --border-color: #4f4f4f;
- --white: #333;
- --black: #fff;
- --gray-light: #333238;
- --svg-status-bg: #333;
-}
.tab-width-8 {
tab-size: 8;
}
diff --git a/app/assets/stylesheets/startup/startup-general.scss b/app/assets/stylesheets/startup/startup-general.scss
index 7fb373bb6f4..f24b6fb9e81 100644
--- a/app/assets/stylesheets/startup/startup-general.scss
+++ b/app/assets/stylesheets/startup/startup-general.scss
@@ -17,9 +17,10 @@ header {
}
body {
margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
- "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue", sans-serif,
- "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ font-family: var(--default-regular-font, -apple-system), BlinkMacSystemFont,
+ "Segoe UI", Roboto, "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue",
+ sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
+ "Noto Color Emoji";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
@@ -47,8 +48,9 @@ a:not([href]):not([class]) {
text-decoration: none;
}
kbd {
- font-family: "Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas",
- "Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace;
+ font-family: var(--default-mono-font, "Menlo"), "DejaVu Sans Mono",
+ "Liberation Mono", "Consolas", "Ubuntu Mono", "Courier New", "andale mono",
+ "lucida console", monospace;
font-size: 1em;
}
img {
@@ -426,9 +428,10 @@ a.gl-badge.badge-warning:active {
.gl-form-input,
.gl-form-input.form-control {
background-color: #fff;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
- "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue", sans-serif,
- "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ font-family: var(--default-regular-font, -apple-system), BlinkMacSystemFont,
+ "Segoe UI", Roboto, "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue",
+ sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
+ "Noto Color Emoji";
font-size: 0.875rem;
line-height: 1rem;
padding-top: 0.5rem;
@@ -507,6 +510,15 @@ a.gl-badge.badge-warning:active {
font-size: 0.875rem;
border-radius: 0.25rem;
}
+.gl-button.gl-button .gl-button-text {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ margin-top: -1px;
+ margin-bottom: -1px;
+}
.gl-button.gl-button.btn-default {
background-color: #fff;
}
@@ -606,8 +618,8 @@ html {
}
.btn:active,
.btn.active {
- background-color: #eaeaea;
- border-color: #e3e3e3;
+ background-color: #e6e6ea;
+ border-color: #dedee3;
color: #333238;
}
.btn svg {
@@ -685,7 +697,7 @@ html {
list-style: none;
}
.dropdown-menu li > a,
-.dropdown-menu li button {
+.dropdown-menu li > button {
background: transparent;
border: 0;
border-radius: 0;
@@ -702,14 +714,14 @@ html {
width: 100%;
}
.dropdown-menu li > a:active,
-.dropdown-menu li button:active {
+.dropdown-menu li > button:active {
background-color: #ececef;
color: #333238;
outline: 0;
text-decoration: none;
}
.dropdown-menu li > a:active,
-.dropdown-menu li button:active {
+.dropdown-menu li > button:active {
box-shadow: inset 0 0 0 2px #428fdc, inset 0 0 0 3px #fff,
inset 0 0 0 1px #fff;
outline: none;
@@ -758,10 +770,10 @@ input {
kbd {
display: inline-block;
padding: 3px 5px;
- font-size: 0.6875rem;
+ font-size: 0.75rem;
line-height: 10px;
color: var(--gray-700, #535158);
- vertical-align: middle;
+ vertical-align: unset;
background-color: var(--gray-10, #fbfafd);
border-width: 1px;
border-style: solid;
@@ -821,6 +833,22 @@ kbd {
.navbar-gitlab .header-content .navbar-collapse > ul.nav > li:not(.d-none) {
margin: 0 2px;
}
+.navbar-gitlab .header-content .header-search-new {
+ max-width: 640px;
+}
+.navbar-gitlab .header-search {
+ min-width: 320px;
+}
+@media (min-width: 768px) and (max-width: 1199.98px) {
+ .navbar-gitlab .header-search {
+ min-width: 200px;
+ }
+}
+.navbar-gitlab .header-search .keyboard-shortcut-helper {
+ transform: translateY(calc(50% - 2px));
+ box-shadow: none;
+ border-color: transparent;
+}
.navbar-gitlab .navbar-collapse {
flex: 0 0 auto;
border-top: 0;
@@ -1123,7 +1151,7 @@ kbd {
font-weight: 600;
}
.nav-sidebar li.active:not(.fly-out-top-item) > a:not(.has-sub-items) {
- background-color: rgba(0, 0, 0, 0.08);
+ background-color: rgba(31, 30, 36, 0.08);
}
.nav-sidebar ul {
padding-left: 0;
@@ -1504,87 +1532,6 @@ svg.s12 {
svg.s16 {
vertical-align: -3px;
}
-.header-content .header-search-new {
- max-width: 640px;
-}
-.header-search {
- min-width: 320px;
-}
-@media (min-width: 768px) and (max-width: 1199.98px) {
- .header-search {
- min-width: 200px;
- }
-}
-.header-search .keyboard-shortcut-helper {
- transform: translateY(calc(50% - 2px));
- box-shadow: none;
- border-color: transparent;
-}
-.search {
- margin: 0 8px;
-}
-.search form {
- display: block;
- margin: 0;
- padding: 4px;
- width: 200px;
- line-height: 24px;
- height: 32px;
- border: 0;
- border-radius: 4px;
-}
-@media (min-width: 1200px) {
- .search form {
- width: 320px;
- }
-}
-.search .search-input {
- border: 0;
- font-size: 14px;
- padding: 0 20px 0 0;
- margin-left: 5px;
- line-height: 25px;
- width: 98%;
- color: #fff;
- background: none;
-}
-.search .search-input-container {
- display: flex;
- position: relative;
-}
-.search .search-input-wrap {
- width: 100%;
-}
-.search .search-input-wrap .search-icon,
-.search .search-input-wrap .clear-icon {
- position: absolute;
- right: 5px;
- top: 4px;
-}
-.search .search-input-wrap .search-icon {
- user-select: none;
-}
-.search .search-input-wrap .clear-icon {
- display: none;
-}
-.search .search-input-wrap .dropdown {
- position: static;
-}
-.search .search-input-wrap .dropdown-menu {
- left: -5px;
- max-height: 400px;
- overflow: auto;
-}
-@media (min-width: 1200px) {
- .search .search-input-wrap .dropdown-menu {
- width: 320px;
- }
-}
-.search .identicon {
- flex-basis: 16px;
- flex-shrink: 0;
- margin-right: 4px;
-}
.avatar,
.avatar-container {
float: left;
diff --git a/app/assets/stylesheets/startup/startup-signin.scss b/app/assets/stylesheets/startup/startup-signin.scss
index 7ae158b3930..d8afff1a200 100644
--- a/app/assets/stylesheets/startup/startup-signin.scss
+++ b/app/assets/stylesheets/startup/startup-signin.scss
@@ -16,9 +16,10 @@ header {
}
body {
margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
- "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue", sans-serif,
- "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ font-family: var(--default-regular-font, -apple-system), BlinkMacSystemFont,
+ "Segoe UI", Roboto, "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue",
+ sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
+ "Noto Color Emoji";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
@@ -390,9 +391,10 @@ input.btn-block[type="button"] {
.gl-form-input,
.gl-form-input.form-control {
background-color: #fff;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
- "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue", sans-serif,
- "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ font-family: var(--default-regular-font, -apple-system), BlinkMacSystemFont,
+ "Segoe UI", Roboto, "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue",
+ sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
+ "Noto Color Emoji";
font-size: 0.875rem;
line-height: 1rem;
padding-top: 0.5rem;
@@ -647,8 +649,8 @@ body.navless {
}
.btn:active,
.btn.active {
- background-color: #eaeaea;
- border-color: #e3e3e3;
+ background-color: #e6e6ea;
+ border-color: #dedee3;
color: #333238;
}
.btn svg {
@@ -954,7 +956,7 @@ svg {
}
.devise-layout-html body .footer-container,
.devise-layout-html body hr.footer-fixed {
- position: absolute;
+ position: fixed;
bottom: 0;
left: 0;
right: 0;
@@ -988,6 +990,9 @@ svg {
.gl-justify-content-center {
justify-content: center;
}
+.gl-justify-content-space-between {
+ justify-content: space-between;
+}
.gl-float-right {
float: right;
}
diff --git a/app/assets/stylesheets/themes/_dark.scss b/app/assets/stylesheets/themes/_dark.scss
index a3474d2ed50..8db91fd9908 100644
--- a/app/assets/stylesheets/themes/_dark.scss
+++ b/app/assets/stylesheets/themes/_dark.scss
@@ -11,6 +11,20 @@ $gray-800: #dcdcde;
$gray-900: #ececef;
$gray-950: #fbfafd;
+$gray-lightest: lighten($gray-10, 1);
+$gray-light: lighten($gray-10, 2);
+$gray-lighter: darken($gray-50, 4);
+$gray-normal: $gray-50;
+$gray-dark: darken($gray-100, 2);
+$gray-darker: darken($gray-200, 2);
+$gray-darkest: $gray-700;
+
+$black: #fff;
+$black-normal: $gray-900;
+$white: $gray-50;
+$white-normal: $gray-50;
+$white-dark: $gray-100;
+
$green-50: #0a4020;
$green-100: #0d532a;
$green-200: #24663b;
@@ -83,159 +97,21 @@ $purple-800: #cbbbf2;
$purple-900: #e1d8f9;
$purple-950: #f4f0ff;
-$gray-lightest: #222;
-$gray-light: $gray-50;
-$gray-lighter: #303030;
-$gray-normal: #333;
-$gray-dark: $gray-100;
-$gray-darker: #4f4f4f;
-$gray-darkest: #c4c4c4;
-
-$black: #fff;
-$black-normal: $gray-900;
-$white: #333;
-$white-light: #2b2b2b;
-$white-normal: #333;
-$white-dark: #444;
-
$theme-indigo-50: #1a1a40;
$border-color: #4f4f4f;
-:root {
- color-scheme: dark;
-}
-
-body.gl-dark {
- --gray-10: #{$gray-10};
- --gray-50: #{$gray-50};
- --gray-100: #{$gray-100};
- --gray-200: #{$gray-200};
- --gray-300: #{$gray-300};
- --gray-400: #{$gray-400};
- --gray-500: #{$gray-500};
- --gray-600: #{$gray-600};
- --gray-700: #{$gray-700};
- --gray-800: #{$gray-800};
- --gray-900: #{$gray-900};
- --gray-950: #{$gray-950};
-
- --green-50: #{$green-50};
- --green-100: #{$green-100};
- --green-200: #{$green-200};
- --green-300: #{$green-300};
- --green-400: #{$green-400};
- --green-500: #{$green-500};
- --green-600: #{$green-600};
- --green-700: #{$green-700};
- --green-800: #{$green-800};
- --green-900: #{$green-900};
- --green-950: #{$green-950};
-
- --blue-50: #{$blue-50};
- --blue-100: #{$blue-100};
- --blue-200: #{$blue-200};
- --blue-300: #{$blue-300};
- --blue-400: #{$blue-400};
- --blue-500: #{$blue-500};
- --blue-600: #{$blue-600};
- --blue-700: #{$blue-700};
- --blue-800: #{$blue-800};
- --blue-900: #{$blue-900};
- --blue-950: #{$blue-950};
-
- --orange-50: #{$orange-50};
- --orange-100: #{$orange-100};
- --orange-200: #{$orange-200};
- --orange-300: #{$orange-300};
- --orange-400: #{$orange-400};
- --orange-500: #{$orange-500};
- --orange-600: #{$orange-600};
- --orange-700: #{$orange-700};
- --orange-800: #{$orange-800};
- --orange-900: #{$orange-900};
- --orange-950: #{$orange-950};
-
- --red-50: #{$red-50};
- --red-100: #{$red-100};
- --red-200: #{$red-200};
- --red-300: #{$red-300};
- --red-400: #{$red-400};
- --red-500: #{$red-500};
- --red-600: #{$red-600};
- --red-700: #{$red-700};
- --red-800: #{$red-800};
- --red-900: #{$red-900};
- --red-950: #{$red-950};
-
- --indigo-50: #{$indigo-50};
- --indigo-100: #{$indigo-100};
- --indigo-200: #{$indigo-200};
- --indigo-300: #{$indigo-300};
- --indigo-400: #{$indigo-400};
- --indigo-500: #{$indigo-500};
- --indigo-600: #{$indigo-600};
- --indigo-700: #{$indigo-700};
- --indigo-800: #{$indigo-800};
- --indigo-900: #{$indigo-900};
- --indigo-950: #{$indigo-950};
-
- --purple-50: #{$purple-50};
- --purple-100: #{$purple-100};
- --purple-200: #{$purple-200};
- --purple-300: #{$purple-300};
- --purple-400: #{$purple-400};
- --purple-500: #{$purple-500};
- --purple-600: #{$purple-600};
- --purple-700: #{$purple-700};
- --purple-800: #{$purple-800};
- --purple-900: #{$purple-900};
- --purple-950: #{$purple-950};
-
- --dark-icon-color-purple-1: #524a68;
- --dark-icon-color-purple-2: #715bae;
- --dark-icon-color-purple-3: #9a79f7;
- --dark-icon-color-orange-1: #665349;
- --dark-icon-color-orange-2: #b37a5d;
-
- --gl-text-color: #{$gray-900};
- --border-color: #{$border-color};
-
- --white: #{$white};
- --black: #{$black};
- --gray-light: #{$gray-50};
-
- --svg-status-bg: #{$white};
-
- .gl-button.gl-button,
- .gl-button.gl-button.btn-block {
- &.btn-default,
- &.btn-dashed,
- &.btn-info,
- &.btn-success,
- &.btn-danger,
- &.btn-confirm {
- &-tertiary {
- mix-blend-mode: screen;
- }
- }
- }
-
- .gl-datepicker-theme {
- .pika-prev,
- .pika-next {
- filter: invert(0.9);
- }
-
- .is-selected > .pika-button {
- color: $gray-900;
- }
-
- :not(.is-selected) > .pika-button:hover {
- background-color: $gray-200;
- }
- }
-}
+$data-viz-blue-50: #2a2b59;
+$data-viz-blue-100: #303470;
+$data-viz-blue-200: #374291;
+$data-viz-blue-300: #3f51ae;
+$data-viz-blue-400: #4e65cd;
+$data-viz-blue-500: #617ae2;
+$data-viz-blue-600: #7992f5;
+$data-viz-blue-700: #97acff;
+$data-viz-blue-800: #b7c6ff;
+$data-viz-blue-900: #d2dcff;
+$data-viz-blue-950: #e9ebff;
$border-white-normal: $border-color;
@@ -265,11 +141,3 @@ $line-removed-dark: $red-200;
$well-expand-item: $gray-200;
$well-inner-border: $gray-200;
-
-$calendar-activity-colors: (
- #404040,
- #1e23a8,
- #445cf2,
- #97acff,
- #e9ebff
-);
diff --git a/app/assets/stylesheets/themes/dark_mode_overrides.scss b/app/assets/stylesheets/themes/dark_mode_overrides.scss
index a0d19c3de2a..bb97261a1ca 100644
--- a/app/assets/stylesheets/themes/dark_mode_overrides.scss
+++ b/app/assets/stylesheets/themes/dark_mode_overrides.scss
@@ -2,6 +2,149 @@
@import 'page_bundles/mixins_and_variables_and_functions';
@import './themes/theme_helper';
+:root {
+ color-scheme: dark;
+ --gray-10: #{$gray-10};
+ --gray-50: #{$gray-50};
+ --gray-100: #{$gray-100};
+ --gray-200: #{$gray-200};
+ --gray-300: #{$gray-300};
+ --gray-400: #{$gray-400};
+ --gray-500: #{$gray-500};
+ --gray-600: #{$gray-600};
+ --gray-700: #{$gray-700};
+ --gray-800: #{$gray-800};
+ --gray-900: #{$gray-900};
+ --gray-950: #{$gray-950};
+
+ --green-50: #{$green-50};
+ --green-100: #{$green-100};
+ --green-200: #{$green-200};
+ --green-300: #{$green-300};
+ --green-400: #{$green-400};
+ --green-500: #{$green-500};
+ --green-600: #{$green-600};
+ --green-700: #{$green-700};
+ --green-800: #{$green-800};
+ --green-900: #{$green-900};
+ --green-950: #{$green-950};
+
+ --blue-50: #{$blue-50};
+ --blue-100: #{$blue-100};
+ --blue-200: #{$blue-200};
+ --blue-300: #{$blue-300};
+ --blue-400: #{$blue-400};
+ --blue-500: #{$blue-500};
+ --blue-600: #{$blue-600};
+ --blue-700: #{$blue-700};
+ --blue-800: #{$blue-800};
+ --blue-900: #{$blue-900};
+ --blue-950: #{$blue-950};
+
+ --orange-50: #{$orange-50};
+ --orange-100: #{$orange-100};
+ --orange-200: #{$orange-200};
+ --orange-300: #{$orange-300};
+ --orange-400: #{$orange-400};
+ --orange-500: #{$orange-500};
+ --orange-600: #{$orange-600};
+ --orange-700: #{$orange-700};
+ --orange-800: #{$orange-800};
+ --orange-900: #{$orange-900};
+ --orange-950: #{$orange-950};
+
+ --red-50: #{$red-50};
+ --red-100: #{$red-100};
+ --red-200: #{$red-200};
+ --red-300: #{$red-300};
+ --red-400: #{$red-400};
+ --red-500: #{$red-500};
+ --red-600: #{$red-600};
+ --red-700: #{$red-700};
+ --red-800: #{$red-800};
+ --red-900: #{$red-900};
+ --red-950: #{$red-950};
+
+ --indigo-50: #{$indigo-50};
+ --indigo-100: #{$indigo-100};
+ --indigo-200: #{$indigo-200};
+ --indigo-300: #{$indigo-300};
+ --indigo-400: #{$indigo-400};
+ --indigo-500: #{$indigo-500};
+ --indigo-600: #{$indigo-600};
+ --indigo-700: #{$indigo-700};
+ --indigo-800: #{$indigo-800};
+ --indigo-900: #{$indigo-900};
+ --indigo-950: #{$indigo-950};
+
+ --purple-50: #{$purple-50};
+ --purple-100: #{$purple-100};
+ --purple-200: #{$purple-200};
+ --purple-300: #{$purple-300};
+ --purple-400: #{$purple-400};
+ --purple-500: #{$purple-500};
+ --purple-600: #{$purple-600};
+ --purple-700: #{$purple-700};
+ --purple-800: #{$purple-800};
+ --purple-900: #{$purple-900};
+ --purple-950: #{$purple-950};
+
+ --dark-icon-color-purple-1: #524a68;
+ --dark-icon-color-purple-2: #715bae;
+ --dark-icon-color-purple-3: #9a79f7;
+ --dark-icon-color-orange-1: #665349;
+ --dark-icon-color-orange-2: #b37a5d;
+
+ --gl-text-color: #{$gray-900};
+ --border-color: #{$border-color};
+
+ --white: #{$white};
+ --black: #{$black};
+ --gray-light: #{$gray-50};
+
+ --svg-status-bg: #{$white};
+}
+
+body.gl-dark {
+ // redefine some colors and values to prevent sourcegraph conflicts
+ color-scheme: dark;
+ --gray-10: #{$gray-10};
+ --border-color: #{$border-color};
+ --white: #{$white};
+ --black: #{$black};
+}
+
+.gl-dark {
+ .gl-button.gl-button,
+ .gl-button.gl-button.btn-block {
+ &.btn-default,
+ &.btn-dashed,
+ &.btn-info,
+ &.btn-success,
+ &.btn-danger,
+ &.btn-confirm {
+ &-tertiary {
+ mix-blend-mode: screen;
+ }
+ }
+ }
+
+ .gl-datepicker-theme {
+ .pika-prev,
+ .pika-next {
+ filter: invert(0.9);
+ }
+
+ .is-selected > .pika-button {
+ color: $gray-900;
+ }
+
+ :not(.is-selected) > .pika-button:hover {
+ background-color: $gray-200;
+ }
+ }
+}
+
// Some hacks and overrides for things that don't properly support dark mode
.gl-label {
filter: brightness(0.9) contrast(1.1);
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index 4be4fc82d04..714dd932147 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -236,6 +236,13 @@ to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1709
}
}
+// TODO: Remove once https: //gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/3198 is merged
+.gl-sm-ml-5 {
+ @include gl-media-breakpoint-up(sm) {
+ @include gl-ml-5;
+ }
+}
+
/* End gitlab-ui#1709 */
/*
@@ -251,3 +258,8 @@ to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1709
.gl-flex-flow-row-wrap {
flex-flow: row wrap;
}
+
+// Will be moved to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/2098
+.gl-max-w-0 {
+ max-width: 0;
+}
diff --git a/app/channels/graphql_channel.rb b/app/channels/graphql_channel.rb
index d364cc2b64b..ae37be85da8 100644
--- a/app/channels/graphql_channel.rb
+++ b/app/channels/graphql_channel.rb
@@ -25,9 +25,7 @@ class GraphqlChannel < ApplicationCable::Channel # rubocop:disable Gitlab/Namesp
# Track the subscription here so we can remove it
# on unsubscribe.
- if result.context[:subscription_id]
- @subscription_ids << result.context[:subscription_id]
- end
+ @subscription_ids << result.context[:subscription_id] if result.context[:subscription_id]
transmit(payload)
end
diff --git a/app/components/diffs/stats_component.rb b/app/components/diffs/stats_component.rb
index 74788133aa2..407c3ca4e58 100644
--- a/app/components/diffs/stats_component.rb
+++ b/app/components/diffs/stats_component.rb
@@ -14,14 +14,14 @@ module Diffs
def diff_files_data
diffs_map = @diff_files.map do |f|
{
- href: "##{helpers.hexdigest(f.file_path)}",
- title: f.new_path,
- name: f.file_path,
- path: diff_file_path_text(f),
- icon: diff_file_changed_icon(f),
- iconColor: "#{diff_file_changed_icon_color(f)}",
- added: f.added_lines,
- removed: f.removed_lines
+ href: "##{helpers.hexdigest(f.file_path)}",
+ title: f.new_path,
+ name: f.file_path,
+ path: diff_file_path_text(f),
+ icon: diff_file_changed_icon(f),
+ iconColor: diff_file_changed_icon_color(f).to_s,
+ added: f.added_lines,
+ removed: f.removed_lines
}
end
diff --git a/app/components/pajamas/button_component.rb b/app/components/pajamas/button_component.rb
index b2dd798b718..cdfd201bfb8 100644
--- a/app/components/pajamas/button_component.rb
+++ b/app/components/pajamas/button_component.rb
@@ -65,7 +65,7 @@ module Pajamas
classes.push(VARIANT_CLASSES[@variant])
unless NON_CATEGORY_VARIANTS.include?(@variant) || @category == :primary
- classes.push(VARIANT_CLASSES[@variant] + '-' + CATEGORY_CLASSES[@category])
+ classes.push("#{VARIANT_CLASSES[@variant]}-#{CATEGORY_CLASSES[@category]}")
end
classes.push(@button_options[:class])
diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb
index 0de2115d4d6..80aca7e21ce 100644
--- a/app/controllers/abuse_reports_controller.rb
+++ b/app/controllers/abuse_reports_controller.rb
@@ -3,7 +3,7 @@
class AbuseReportsController < ApplicationController
before_action :set_user, only: [:new]
- feature_category :users
+ feature_category :insider_threat
def new
@abuse_report = AbuseReport.new
diff --git a/app/controllers/admin/abuse_reports_controller.rb b/app/controllers/admin/abuse_reports_controller.rb
index 6f80ed3c172..5357558434e 100644
--- a/app/controllers/admin/abuse_reports_controller.rb
+++ b/app/controllers/admin/abuse_reports_controller.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class Admin::AbuseReportsController < Admin::ApplicationController
- feature_category :users
+ feature_category :insider_threat
def index
@abuse_reports = AbuseReportsFinder.new(params).execute
diff --git a/app/controllers/admin/application_settings/appearances_controller.rb b/app/controllers/admin/application_settings/appearances_controller.rb
index cf765c96a8f..1a8447185a7 100644
--- a/app/controllers/admin/application_settings/appearances_controller.rb
+++ b/app/controllers/admin/application_settings/appearances_controller.rb
@@ -68,6 +68,7 @@ class Admin::ApplicationSettings::AppearancesController < Admin::ApplicationCont
def allowed_appearance_params
%i[
title
+ short_title
description
logo
logo_cache
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index ec9441c2b9b..b8c1bc266f7 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -40,9 +40,9 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
feature_category :pages, [:lets_encrypt_terms_of_service]
feature_category :error_tracking, [:reset_error_tracking_access_token]
- VALID_SETTING_PANELS = %w(general repository
+ VALID_SETTING_PANELS = %w[general repository
ci_cd reporting metrics_and_profiling
- network preferences).freeze
+ network preferences].freeze
# The current size of a sidekiq job's jid is 24 characters. The size of the
# jid is an internal detail of Sidekiq, and they do not guarantee that it'll
@@ -150,9 +150,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
}
end
- if @application_setting.self_monitoring_project_id.present?
- return render status: :ok, json: self_monitoring_data
- end
+ return render status: :ok, json: self_monitoring_data if @application_setting.self_monitoring_project_id.present?
render status: :bad_request, json: {
message: _('Self-monitoring project does not exist. Please check logs ' \
@@ -236,7 +234,9 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
params[:application_setting][:restricted_visibility_levels]&.delete("")
if params[:application_setting].key?(:required_instance_ci_template)
- params[:application_setting][:required_instance_ci_template] = nil if params[:application_setting][:required_instance_ci_template].empty?
+ if params[:application_setting][:required_instance_ci_template].empty?
+ params[:application_setting][:required_instance_ci_template] = nil
+ end
end
remove_blank_params_for!(:elasticsearch_aws_secret_access_key, :eks_secret_access_key)
@@ -290,9 +290,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
.new(@application_setting, current_user, application_setting_params)
.execute
- if recheck_user_consent?
- session[:ask_for_usage_stats_consent] = current_user.requires_usage_stats_consent?
- end
+ session[:ask_for_usage_stats_consent] = current_user.requires_usage_stats_consent? if recheck_user_consent?
redirect_path = referer_path(request) || general_admin_application_settings_path
diff --git a/app/controllers/admin/background_jobs_controller.rb b/app/controllers/admin/background_jobs_controller.rb
index 4eda35d66f6..43d2c983823 100644
--- a/app/controllers/admin/background_jobs_controller.rb
+++ b/app/controllers/admin/background_jobs_controller.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
-class Admin::BackgroundJobsController < Admin::ApplicationController
- feature_category :not_owned # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned
+module Admin
+ class BackgroundJobsController < ApplicationController
+ feature_category :not_owned # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned
+ end
end
diff --git a/app/controllers/admin/background_migrations_controller.rb b/app/controllers/admin/background_migrations_controller.rb
index c6c9e0ced22..b904196c5ab 100644
--- a/app/controllers/admin/background_migrations_controller.rb
+++ b/app/controllers/admin/background_migrations_controller.rb
@@ -1,66 +1,68 @@
# frozen_string_literal: true
-class Admin::BackgroundMigrationsController < Admin::ApplicationController
- feature_category :database
- urgency :low
-
- around_action :support_multiple_databases
-
- def index
- @relations_by_tab = {
- 'queued' => batched_migration_class.queued.queue_order,
- 'failed' => batched_migration_class.with_status(:failed).queue_order,
- 'finished' => batched_migration_class.with_status(:finished).queue_order.reverse_order
- }
-
- @current_tab = @relations_by_tab.key?(params[:tab]) ? params[:tab] : 'queued'
- @migrations = @relations_by_tab[@current_tab].page(params[:page])
- @successful_rows_counts = batched_migration_class.successful_rows_counts(@migrations.map(&:id))
- @databases = Gitlab::Database.db_config_names
- end
+module Admin
+ class BackgroundMigrationsController < ApplicationController
+ feature_category :database
+ urgency :low
+
+ around_action :support_multiple_databases
+
+ def index
+ @relations_by_tab = {
+ 'queued' => batched_migration_class.queued.queue_order,
+ 'failed' => batched_migration_class.with_status(:failed).queue_order,
+ 'finished' => batched_migration_class.with_status(:finished).queue_order.reverse_order
+ }
+
+ @current_tab = @relations_by_tab.key?(params[:tab]) ? params[:tab] : 'queued'
+ @migrations = @relations_by_tab[@current_tab].page(params[:page])
+ @successful_rows_counts = batched_migration_class.successful_rows_counts(@migrations.map(&:id))
+ @databases = Gitlab::Database.db_config_names
+ end
- def show
- @migration = batched_migration_class.find(params[:id])
+ def show
+ @migration = batched_migration_class.find(params[:id])
- @failed_jobs = @migration.batched_jobs.with_status(:failed).page(params[:page])
- end
+ @failed_jobs = @migration.batched_jobs.with_status(:failed).page(params[:page])
+ end
- def pause
- migration = batched_migration_class.find(params[:id])
- migration.pause!
+ def pause
+ migration = batched_migration_class.find(params[:id])
+ migration.pause!
- redirect_back fallback_location: { action: 'index' }
- end
+ redirect_back fallback_location: { action: 'index' }
+ end
- def resume
- migration = batched_migration_class.find(params[:id])
- migration.execute!
+ def resume
+ migration = batched_migration_class.find(params[:id])
+ migration.execute!
- redirect_back fallback_location: { action: 'index' }
- end
+ redirect_back fallback_location: { action: 'index' }
+ end
- def retry
- migration = batched_migration_class.find(params[:id])
- migration.retry_failed_jobs! if migration.failed?
+ def retry
+ migration = batched_migration_class.find(params[:id])
+ migration.retry_failed_jobs! if migration.failed?
- redirect_back fallback_location: { action: 'index' }
- end
+ redirect_back fallback_location: { action: 'index' }
+ end
- private
+ private
- def support_multiple_databases
- Gitlab::Database::SharedModel.using_connection(base_model.connection) do
- yield
+ def support_multiple_databases
+ Gitlab::Database::SharedModel.using_connection(base_model.connection) do
+ yield
+ end
end
- end
- def base_model
- @selected_database = params[:database] || Gitlab::Database::MAIN_DATABASE_NAME
+ def base_model
+ @selected_database = params[:database] || Gitlab::Database::MAIN_DATABASE_NAME
- Gitlab::Database.database_base_models[@selected_database]
- end
+ Gitlab::Database.database_base_models[@selected_database]
+ end
- def batched_migration_class
- @batched_migration_class ||= Gitlab::Database::BackgroundMigration::BatchedMigration
+ def batched_migration_class
+ @batched_migration_class ||= Gitlab::Database::BackgroundMigration::BatchedMigration
+ end
end
end
diff --git a/app/controllers/admin/batched_jobs_controller.rb b/app/controllers/admin/batched_jobs_controller.rb
index 0a00ba13dc8..10b5f68d630 100644
--- a/app/controllers/admin/batched_jobs_controller.rb
+++ b/app/controllers/admin/batched_jobs_controller.rb
@@ -1,28 +1,30 @@
# frozen_string_literal: true
-class Admin::BatchedJobsController < Admin::ApplicationController
- feature_category :database
- urgency :low
+module Admin
+ class BatchedJobsController < ApplicationController
+ feature_category :database
+ urgency :low
- around_action :support_multiple_databases
+ around_action :support_multiple_databases
- def show
- @job = Gitlab::Database::BackgroundMigration::BatchedJob.find(params[:id])
+ def show
+ @job = Gitlab::Database::BackgroundMigration::BatchedJob.find(params[:id])
- @transition_logs = @job.batched_job_transition_logs
- end
+ @transition_logs = @job.batched_job_transition_logs
+ end
- private
+ private
- def support_multiple_databases
- Gitlab::Database::SharedModel.using_connection(base_model.connection) do
- yield
+ def support_multiple_databases
+ Gitlab::Database::SharedModel.using_connection(base_model.connection) do
+ yield
+ end
end
- end
- def base_model
- @selected_database = params[:database] || Gitlab::Database::MAIN_DATABASE_NAME
+ def base_model
+ @selected_database = params[:database] || Gitlab::Database::MAIN_DATABASE_NAME
- Gitlab::Database.database_base_models[@selected_database]
+ Gitlab::Database.database_base_models[@selected_database]
+ end
end
end
diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb
index bdf0c6aedb9..093c5667a24 100644
--- a/app/controllers/admin/broadcast_messages_controller.rb
+++ b/app/controllers/admin/broadcast_messages_controller.rb
@@ -1,104 +1,106 @@
# frozen_string_literal: true
-class Admin::BroadcastMessagesController < Admin::ApplicationController
- include BroadcastMessagesHelper
+module Admin
+ class BroadcastMessagesController < ApplicationController
+ include BroadcastMessagesHelper
- before_action :find_broadcast_message, only: [:edit, :update, :destroy]
- before_action :find_broadcast_messages, only: [:index, :create]
- before_action :push_features, only: [:index, :edit]
+ before_action :find_broadcast_message, only: [:edit, :update, :destroy]
+ before_action :find_broadcast_messages, only: [:index, :create]
+ before_action :push_features, only: [:index, :edit]
- feature_category :onboarding
- urgency :low
+ feature_category :onboarding
+ urgency :low
- def index
- @broadcast_message = BroadcastMessage.new
- end
-
- def edit
- end
+ def index
+ @broadcast_message = BroadcastMessage.new
+ end
- def create
- @broadcast_message = BroadcastMessage.new(broadcast_message_params)
- success = @broadcast_message.save
+ def edit
+ end
- respond_to do |format|
- format.json do
- if success
- render json: @broadcast_message, status: :ok
- else
- render json: { errors: @broadcast_message.errors.full_messages }, status: :bad_request
+ def create
+ @broadcast_message = BroadcastMessage.new(broadcast_message_params)
+ success = @broadcast_message.save
+
+ respond_to do |format|
+ format.json do
+ if success
+ render json: @broadcast_message, status: :ok
+ else
+ render json: { errors: @broadcast_message.errors.full_messages }, status: :bad_request
+ end
end
- end
- format.html do
- if success
- redirect_to admin_broadcast_messages_path, notice: _('Broadcast Message was successfully created.')
- else
- render :index
+ format.html do
+ if success
+ redirect_to admin_broadcast_messages_path, notice: _('Broadcast Message was successfully created.')
+ else
+ render :index
+ end
end
end
end
- end
- def update
- success = @broadcast_message.update(broadcast_message_params)
+ def update
+ success = @broadcast_message.update(broadcast_message_params)
- respond_to do |format|
- format.json do
- if success
- render json: @broadcast_message, status: :ok
- else
- render json: { errors: @broadcast_message.errors.full_messages }, status: :bad_request
+ respond_to do |format|
+ format.json do
+ if success
+ render json: @broadcast_message, status: :ok
+ else
+ render json: { errors: @broadcast_message.errors.full_messages }, status: :bad_request
+ end
end
- end
- format.html do
- if success
- redirect_to admin_broadcast_messages_path, notice: _('Broadcast Message was successfully updated.')
- else
- render :edit
+ format.html do
+ if success
+ redirect_to admin_broadcast_messages_path, notice: _('Broadcast Message was successfully updated.')
+ else
+ render :edit
+ end
end
end
end
- end
- def destroy
- @broadcast_message.destroy
+ def destroy
+ @broadcast_message.destroy
- respond_to do |format|
- format.html { redirect_back_or_default(default: { action: 'index' }) }
- format.js { head :ok }
+ respond_to do |format|
+ format.html { redirect_back_or_default(default: { action: 'index' }) }
+ format.js { head :ok }
+ end
end
- end
- def preview
- @broadcast_message = BroadcastMessage.new(broadcast_message_params)
- render partial: 'admin/broadcast_messages/preview'
- end
+ def preview
+ @broadcast_message = BroadcastMessage.new(broadcast_message_params)
+ render partial: 'admin/broadcast_messages/preview'
+ end
- protected
+ protected
- def find_broadcast_message
- @broadcast_message = BroadcastMessage.find(params[:id])
- end
+ def find_broadcast_message
+ @broadcast_message = BroadcastMessage.find(params[:id])
+ end
- def find_broadcast_messages
- @broadcast_messages = BroadcastMessage.order(ends_at: :desc).page(params[:page]) # rubocop: disable CodeReuse/ActiveRecord
- end
+ def find_broadcast_messages
+ @broadcast_messages = BroadcastMessage.order(ends_at: :desc).page(params[:page]) # rubocop: disable CodeReuse/ActiveRecord
+ end
- def broadcast_message_params
- params.require(:broadcast_message)
- .permit(%i(
- theme
- ends_at
- message
- starts_at
- target_path
- broadcast_type
- dismissable
- ), target_access_levels: []).reverse_merge!(target_access_levels: [])
- end
+ def broadcast_message_params
+ params.require(:broadcast_message)
+ .permit(%i[
+ theme
+ ends_at
+ message
+ starts_at
+ target_path
+ broadcast_type
+ dismissable
+ ], target_access_levels: []).reverse_merge!(target_access_levels: [])
+ end
- def push_features
- push_frontend_feature_flag(:vue_broadcast_messages, current_user)
- push_frontend_feature_flag(:role_targeted_broadcast_messages, current_user)
+ def push_features
+ push_frontend_feature_flag(:vue_broadcast_messages, current_user)
+ push_frontend_feature_flag(:role_targeted_broadcast_messages, current_user)
+ end
end
end
diff --git a/app/controllers/admin/ci/variables_controller.rb b/app/controllers/admin/ci/variables_controller.rb
index 7d643435ddb..ef50d7362c4 100644
--- a/app/controllers/admin/ci/variables_controller.rb
+++ b/app/controllers/admin/ci/variables_controller.rb
@@ -1,50 +1,54 @@
# frozen_string_literal: true
-class Admin::Ci::VariablesController < Admin::ApplicationController
- feature_category :pipeline_authoring
-
- def show
- respond_to do |format|
- format.json { render_instance_variables }
- end
- end
-
- def update
- service = Ci::UpdateInstanceVariablesService.new(variables_params)
-
- if service.execute
- respond_to do |format|
- format.json { render_instance_variables }
+module Admin
+ module Ci
+ class VariablesController < ApplicationController
+ feature_category :pipeline_authoring
+
+ def show
+ respond_to do |format|
+ format.json { render_instance_variables }
+ end
end
- else
- respond_to do |format|
- format.json { render_error(service.errors) }
+
+ def update
+ service = ::Ci::UpdateInstanceVariablesService.new(variables_params)
+
+ if service.execute
+ respond_to do |format|
+ format.json { render_instance_variables }
+ end
+ else
+ respond_to do |format|
+ format.json { render_error(service.errors) }
+ end
+ end
end
- end
- end
- private
+ private
- def variables
- @variables ||= Ci::InstanceVariable.all
- end
+ def variables
+ @variables ||= ::Ci::InstanceVariable.all
+ end
- def render_instance_variables
- render status: :ok,
- json: {
- variables: Ci::InstanceVariableSerializer.new.represent(variables)
- }
- end
+ def render_instance_variables
+ render status: :ok,
+ json: {
+ variables: ::Ci::InstanceVariableSerializer.new.represent(variables)
+ }
+ end
- def render_error(errors)
- render status: :bad_request, json: errors
- end
+ def render_error(errors)
+ render status: :bad_request, json: errors
+ end
- def variables_params
- params.permit(variables_attributes: Array(variable_params_attributes))
- end
+ def variables_params
+ params.permit(variables_attributes: Array(variable_params_attributes))
+ end
- def variable_params_attributes
- %i[id variable_type key secret_value protected masked _destroy]
+ def variable_params_attributes
+ %i[id variable_type key secret_value protected masked raw _destroy]
+ end
+ end
end
end
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 1395d4bb3b7..8005babe19e 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -51,6 +51,10 @@ class Admin::GroupsController < Admin::ApplicationController
@group.build_admin_note unless @group.admin_note
if @group.update(group_params)
+ unless Gitlab::Utils.to_boolean(group_params['runner_registration_enabled'])
+ Ci::Runners::ResetRegistrationTokenService.new(@group, current_user).execute
+ end
+
redirect_to [:admin, @group], notice: _('Group was successfully updated.')
else
render "edit"
@@ -91,6 +95,7 @@ class Admin::GroupsController < Admin::ApplicationController
:name,
:path,
:request_access_enabled,
+ :runner_registration_enabled,
:visibility_level,
:require_two_factor_authentication,
:two_factor_grace_period,
diff --git a/app/controllers/admin/plan_limits_controller.rb b/app/controllers/admin/plan_limits_controller.rb
index 2cebc059830..ea52198432c 100644
--- a/app/controllers/admin/plan_limits_controller.rb
+++ b/app/controllers/admin/plan_limits_controller.rb
@@ -47,6 +47,7 @@ class Admin::PlanLimitsController < Admin::ApplicationController
ci_needs_size_limit
ci_registered_group_runners
ci_registered_project_runners
+ pipeline_hierarchy_size
])
end
end
diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb
index 3f3c3581555..9e841487508 100644
--- a/app/controllers/admin/projects_controller.rb
+++ b/app/controllers/admin/projects_controller.rb
@@ -13,9 +13,7 @@ class Admin::ProjectsController < Admin::ApplicationController
params[:sort] ||= 'latest_activity_desc'
@sort = params[:sort]
- if params[:last_repository_check_failed].present? && params[:archived].nil?
- params[:archived] = true
- end
+ params[:archived] = true if params[:last_repository_check_failed].present? && params[:archived].nil?
@projects = Admin::ProjectsFinder.new(params: params, current_user: current_user).execute
@@ -57,9 +55,7 @@ class Admin::ProjectsController < Admin::ApplicationController
namespace = Namespace.find_by(id: params[:new_namespace_id])
::Projects::TransferService.new(@project, current_user, params.dup).execute(namespace)
- if @project.errors[:new_namespace].present?
- flash[:alert] = @project.errors[:new_namespace].first
- end
+ flash[:alert] = @project.errors[:new_namespace].first if @project.errors[:new_namespace].present?
@project.reset
redirect_to admin_project_path(@project)
diff --git a/app/controllers/admin/spam_logs_controller.rb b/app/controllers/admin/spam_logs_controller.rb
index 3a55fc4b951..180f4634136 100644
--- a/app/controllers/admin/spam_logs_controller.rb
+++ b/app/controllers/admin/spam_logs_controller.rb
@@ -5,7 +5,7 @@ class Admin::SpamLogsController < Admin::ApplicationController
# rubocop: disable CodeReuse/ActiveRecord
def index
- @spam_logs = SpamLog.order(id: :desc).page(params[:page])
+ @spam_logs = SpamLog.includes(:user).order(id: :desc).page(params[:page])
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/controllers/admin/system_info_controller.rb b/app/controllers/admin/system_info_controller.rb
index 41f95addc66..96fb73cedfe 100644
--- a/app/controllers/admin/system_info_controller.rb
+++ b/app/controllers/admin/system_info_controller.rb
@@ -59,11 +59,11 @@ class Admin::SystemInfoController < Admin::ApplicationController
begin
disk = Sys::Filesystem.stat(mount.mount_point)
@disks.push({
- bytes_total: disk.bytes_total,
- bytes_used: disk.bytes_used,
- disk_name: mount.name,
- mount_path: disk.path
- })
+ bytes_total: disk.bytes_total,
+ bytes_used: disk.bytes_used,
+ disk_name: mount.name,
+ mount_path: disk.path
+ })
rescue Sys::Filesystem::Error
end
end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 2c8b4888d5d..5f6e3f0062f 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -88,17 +88,25 @@ class Admin::UsersController < Admin::ApplicationController
end
def activate
- return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user must be unblocked to be activated")) if user.blocked?
+ if user.blocked?
+ return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user must be unblocked to be activated"))
+ end
user.activate
redirect_back_or_admin_user(notice: _("Successfully activated"))
end
def deactivate
- return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user cannot be deactivated")) if user.blocked?
+ if user.blocked?
+ return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user cannot be deactivated"))
+ end
+
return redirect_back_or_admin_user(notice: _("Successfully deactivated")) if user.deactivated?
return redirect_back_or_admin_user(notice: _("Internal users cannot be deactivated")) if user.internal?
- return redirect_back_or_admin_user(notice: _("The user you are trying to deactivate has been active in the past %{minimum_inactive_days} days and cannot be deactivated") % { minimum_inactive_days: Gitlab::CurrentSettings.deactivate_dormant_users_period }) unless user.can_be_deactivated?
+
+ unless user.can_be_deactivated?
+ return redirect_back_or_admin_user(notice: _("The user you are trying to deactivate has been active in the past %{minimum_inactive_days} days and cannot be deactivated") % { minimum_inactive_days: Gitlab::CurrentSettings.deactivate_dormant_users_period })
+ end
user.deactivate
redirect_back_or_admin_user(notice: _("Successfully deactivated"))
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 4de6b5de42a..e64d3110c3a 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -158,7 +158,7 @@ class ApplicationController < ActionController::Base
protected
def workhorse_excluded_content_types
- @workhorse_excluded_content_types ||= %w(text/html application/json)
+ @workhorse_excluded_content_types ||= %w[text/html application/json]
end
def append_info_to_payload(payload)
@@ -179,9 +179,7 @@ class ApplicationController < ActionController::Base
payload[:queue_duration_s] = request.env[::Gitlab::Middleware::RailsQueueDuration::GITLAB_RAILS_QUEUE_DURATION_KEY]
- if Feature.enabled?(:log_response_length)
- payload[:response_bytes] = response.body_parts.sum(&:bytesize)
- end
+ payload[:response_bytes] = response.body_parts.sum(&:bytesize) if Feature.enabled?(:log_response_length)
store_cloudflare_headers!(payload, request)
end
@@ -349,9 +347,7 @@ class ApplicationController < ActionController::Base
def check_password_expiration
return if session[:impersonator_id] || !current_user&.allow_password_authentication?
- if current_user&.password_expired?
- redirect_to new_profile_password_path
- end
+ redirect_to new_profile_password_path if current_user&.password_expired?
end
def active_user_check
@@ -426,8 +422,8 @@ class ApplicationController < ActionController::Base
# accepting the terms.
redirect_path = if request.get?
request.fullpath
- else
- URI(request.referer).path if request.referer
+ elsif request.referer
+ URI(request.referer).path
end
flash[:notice] = message
@@ -529,7 +525,7 @@ class ApplicationController < ActionController::Base
end
def set_page_title_header
- # Per https://tools.ietf.org/html/rfc5987, headers need to be ISO-8859-1, not UTF-8
+ # Per https://www.rfc-editor.org/rfc/rfc5987, headers need to be ISO-8859-1, not UTF-8
response.headers['Page-Title'] = Addressable::URI.encode_component(page_title('GitLab'))
end
@@ -565,9 +561,7 @@ class ApplicationController < ActionController::Base
session[:ask_for_usage_stats_consent] = current_user.requires_usage_stats_consent?
- if session[:ask_for_usage_stats_consent]
- disable_usage_stats
- end
+ disable_usage_stats if session[:ask_for_usage_stats_consent]
end
def disable_usage_stats
diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb
index 817f82085e6..b4a36b7db22 100644
--- a/app/controllers/concerns/authenticates_with_two_factor.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor.rb
@@ -23,6 +23,8 @@ module AuthenticatesWithTwoFactor
session[:otp_user_id] = user.id
session[:user_password_hash] = Digest::SHA256.hexdigest(user.encrypted_password)
+
+ add_gon_variables
push_frontend_feature_flag(:webauthn)
if Feature.enabled?(:webauthn)
diff --git a/app/controllers/concerns/controller_with_cross_project_access_check.rb b/app/controllers/concerns/controller_with_cross_project_access_check.rb
index 3f72f092683..eace8e9464b 100644
--- a/app/controllers/concerns/controller_with_cross_project_access_check.rb
+++ b/app/controllers/concerns/controller_with_cross_project_access_check.rb
@@ -9,9 +9,7 @@ module ControllerWithCrossProjectAccessCheck
end
def cross_project_check
- if Gitlab::CrossProjectAccess.find_check(self)&.should_run?(self)
- authorize_cross_project_page!
- end
+ authorize_cross_project_page! if Gitlab::CrossProjectAccess.find_check(self)&.should_run?(self)
end
def authorize_cross_project_page!
diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb
index b6ba1b13cc3..53bb11090c8 100644
--- a/app/controllers/concerns/creates_commit.rb
+++ b/app/controllers/concerns/creates_commit.rb
@@ -78,7 +78,7 @@ module CreatesCommit
_("You can now submit a merge request to get this change into the original branch.")
end
- flash[:notice] += " " + mr_message
+ flash[:notice] += " #{mr_message}"
end
end
end
diff --git a/app/controllers/concerns/cycle_analytics_params.rb b/app/controllers/concerns/cycle_analytics_params.rb
index 70bcefe339c..5199d879595 100644
--- a/app/controllers/concerns/cycle_analytics_params.rb
+++ b/app/controllers/concerns/cycle_analytics_params.rb
@@ -23,7 +23,10 @@ module CycleAnalyticsParams
opts[:from] = params[:from] || start_date(params)
opts[:to] = params[:to] if params[:to]
opts[:end_event_filter] = params[:end_event_filter] if params[:end_event_filter]
- opts[:use_aggregated_data_collector] = params[:use_aggregated_data_collector] if params[:use_aggregated_data_collector]
+ if params[:use_aggregated_data_collector]
+ opts[:use_aggregated_data_collector] = params[:use_aggregated_data_collector]
+ end
+
opts.merge!(params.slice(*::Gitlab::Analytics::CycleAnalytics::RequestParams::FINDER_PARAM_NAMES))
opts.merge!(date_range(params))
end
diff --git a/app/controllers/concerns/enforces_two_factor_authentication.rb b/app/controllers/concerns/enforces_two_factor_authentication.rb
index b1b6e21644e..c8de041d5bd 100644
--- a/app/controllers/concerns/enforces_two_factor_authentication.rb
+++ b/app/controllers/concerns/enforces_two_factor_authentication.rb
@@ -10,19 +10,12 @@
module EnforcesTwoFactorAuthentication
extend ActiveSupport::Concern
- MFA_HELP_PAGE = Rails.application.routes.url_helpers.help_page_url(
- 'user/profile/account/two_factor_authentication.html',
- anchor: 'enable-two-factor-authentication'
- )
-
included do
before_action :check_two_factor_requirement, except: [:route_not_found]
# to include this in controllers inheriting from `ActionController::Metal`
# we need to add this block
- if respond_to?(:helper_method)
- helper_method :two_factor_grace_period_expired?, :two_factor_skippable?
- end
+ helper_method :two_factor_grace_period_expired?, :two_factor_skippable? if respond_to?(:helper_method)
end
def check_two_factor_requirement
@@ -33,7 +26,7 @@ module EnforcesTwoFactorAuthentication
when GraphqlController
render_error(
_("Authentication error: enable 2FA in your profile settings to continue using GitLab: %{mfa_help_page}") %
- { mfa_help_page: MFA_HELP_PAGE },
+ { mfa_help_page: mfa_help_page_url },
status: :unauthorized
)
else
@@ -84,6 +77,13 @@ module EnforcesTwoFactorAuthentication
def two_factor_verifier
@two_factor_verifier ||= Gitlab::Auth::TwoFactorAuthVerifier.new(current_user) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
+
+ def mfa_help_page_url
+ Rails.application.routes.url_helpers.help_page_url(
+ 'user/profile/account/two_factor_authentication.html',
+ anchor: 'enable-two-factor-authentication'
+ )
+ end
end
EnforcesTwoFactorAuthentication.prepend_mod_with('EnforcesTwoFactorAuthentication')
diff --git a/app/controllers/concerns/impersonation.rb b/app/controllers/concerns/impersonation.rb
index 539dd9ad69d..e562cf5dbe4 100644
--- a/app/controllers/concerns/impersonation.rb
+++ b/app/controllers/concerns/impersonation.rb
@@ -3,11 +3,11 @@
module Impersonation
include Gitlab::Utils::StrongMemoize
- SESSION_KEYS_TO_DELETE = %w(
+ SESSION_KEYS_TO_DELETE = %w[
github_access_token gitea_access_token gitlab_access_token
bitbucket_token bitbucket_refresh_token bitbucket_server_personal_access_token
bulk_import_gitlab_access_token fogbugz_token
- ).freeze
+ ].freeze
def current_user
user = super
diff --git a/app/controllers/concerns/import/github_oauth.rb b/app/controllers/concerns/import/github_oauth.rb
index d53022aabf2..c233f5d09fa 100644
--- a/app/controllers/concerns/import/github_oauth.rb
+++ b/app/controllers/concerns/import/github_oauth.rb
@@ -53,6 +53,7 @@ module Import
def authorize_url
state = SecureRandom.base64(64)
session[auth_state_key] = state
+ session[:auth_on_failure_path] = "#{new_project_path}#import_project"
if Feature.enabled?(:remove_legacy_github_client)
oauth_client.auth_code.authorize_url(
redirect_uri: callback_import_url,
diff --git a/app/controllers/concerns/integrations/params.rb b/app/controllers/concerns/integrations/params.rb
index 30de4a86bec..74d998503b7 100644
--- a/app/controllers/concerns/integrations/params.rb
+++ b/app/controllers/concerns/integrations/params.rb
@@ -88,7 +88,9 @@ module Integrations
param_values = return_value[:integration]
if param_values.is_a?(ActionController::Parameters)
- if action_name == 'update' && integration.chat? && param_values['webhook'] == BaseChatNotification::SECRET_MASK
+ if %w[update test].include?(action_name) && integration.chat? &&
+ param_values['webhook'] == BaseChatNotification::SECRET_MASK
+
param_values.delete('webhook')
end
diff --git a/app/controllers/concerns/invisible_captcha_on_signup.rb b/app/controllers/concerns/invisible_captcha_on_signup.rb
index c7fd6d08744..b78869e02d0 100644
--- a/app/controllers/concerns/invisible_captcha_on_signup.rb
+++ b/app/controllers/concerns/invisible_captcha_on_signup.rb
@@ -13,7 +13,7 @@ module InvisibleCaptchaOnSignup
invisible_captcha_honeypot_counter.increment
log_request('Invisible_Captcha_Honeypot_Request')
- head(200)
+ head(:ok)
end
def on_timestamp_spam_callback
diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb
index bea184e44b9..0669f051457 100644
--- a/app/controllers/concerns/issuable_actions.rb
+++ b/app/controllers/concerns/issuable_actions.rb
@@ -146,13 +146,17 @@ module IssuableActions
finder = Issuable::DiscussionsListService.new(current_user, issuable, finder_params_for_issuable)
discussion_notes = finder.execute
- response.headers['X-Next-Page-Cursor'] = finder.paginator.cursor_for_next_page if finder.paginator.present? && finder.paginator.has_next_page?
+ if finder.paginator.present? && finder.paginator.has_next_page?
+ response.headers['X-Next-Page-Cursor'] = finder.paginator.cursor_for_next_page
+ end
case issuable
when MergeRequest
render_mr_discussions(discussion_notes, discussion_serializer, discussion_cache_context)
when Issue
- render json: discussion_serializer.represent(discussion_notes, context: self) if stale?(etag: [discussion_cache_context, discussion_notes])
+ if stale?(etag: [discussion_cache_context, discussion_notes])
+ render json: discussion_serializer.represent(discussion_notes, context: self)
+ end
else
render json: discussion_serializer.represent(discussion_notes, context: self)
end
@@ -173,7 +177,7 @@ module IssuableActions
def render_cached_discussions(discussions, serializer, cache_context)
render_cached(discussions,
with: serializer,
- cache_context: -> (_) { cache_context },
+ cache_context: ->(_) { cache_context },
context: self)
end
@@ -230,15 +234,11 @@ module IssuableActions
end
def authorize_destroy_issuable!
- unless can?(current_user, :"destroy_#{issuable.to_ability_name}", issuable)
- access_denied!
- end
+ access_denied! unless can?(current_user, :"destroy_#{issuable.to_ability_name}", issuable)
end
def authorize_admin_issuable!
- unless can?(current_user, :"admin_#{resource_name}", parent)
- access_denied!
- end
+ access_denied! unless can?(current_user, :"admin_#{resource_name}", parent)
end
def authorize_update_issuable!
diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb
index de38d26e3fe..7b0d8cf8dcb 100644
--- a/app/controllers/concerns/issuable_collections.rb
+++ b/app/controllers/concerns/issuable_collections.rb
@@ -14,7 +14,9 @@ module IssuableCollections
private
def show_alert_if_search_is_disabled
- return if current_user || params[:search].blank? || !html_request? || Feature.disabled?(:disable_anonymous_search, type: :ops)
+ if current_user || params[:search].blank? || !html_request? || Feature.disabled?(:disable_anonymous_search, type: :ops)
+ return
+ end
flash.now[:notice] = _('You must sign in to search for specific terms.')
end
diff --git a/app/controllers/concerns/issues_calendar.rb b/app/controllers/concerns/issues_calendar.rb
index 51d6d3cf05a..692ac5e700b 100644
--- a/app/controllers/concerns/issues_calendar.rb
+++ b/app/controllers/concerns/issues_calendar.rb
@@ -16,9 +16,7 @@ module IssuesCalendar
# the content as a file (even ignoring the Content-Disposition
# header). We want to display the content inline when accessed
# from GitLab, similarly to the RSS feed.
- if request.referer&.start_with?(::Settings.gitlab.base_url)
- response.headers['Content-Type'] = 'text/plain'
- end
+ response.headers['Content-Type'] = 'text/plain' if request.referer&.start_with?(::Settings.gitlab.base_url)
end
end
end
diff --git a/app/controllers/concerns/labels_as_hash.rb b/app/controllers/concerns/labels_as_hash.rb
index e428520f709..601d3bf50eb 100644
--- a/app/controllers/concerns/labels_as_hash.rb
+++ b/app/controllers/concerns/labels_as_hash.rb
@@ -16,9 +16,7 @@ module LabelsAsHash
if already_set_labels.present?
titles = already_set_labels.map(&:title)
label_hashes.each do |hash|
- if titles.include?(hash['title'])
- hash[:set] = true
- end
+ hash[:set] = true if titles.include?(hash['title'])
end
end
end
diff --git a/app/controllers/concerns/lfs_request.rb b/app/controllers/concerns/lfs_request.rb
index 97df3c7caea..1653b40bad5 100644
--- a/app/controllers/concerns/lfs_request.rb
+++ b/app/controllers/concerns/lfs_request.rb
@@ -78,25 +78,27 @@ module LfsRequest
end
def lfs_download_access?
- strong_memoize(:lfs_download_access) do
- ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code? || deploy_token_can_download_code?
- end
+ ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code? || deploy_token_can_download_code?
end
+ strong_memoize_attr :lfs_download_access?, :lfs_download_access
def deploy_token_can_download_code?
deploy_token.present? &&
- deploy_token.project == project &&
- deploy_token.active? &&
+ deploy_token.has_access_to?(project) &&
deploy_token.read_repository?
end
def lfs_upload_access?
- strong_memoize(:lfs_upload_access) do
- next false unless has_authentication_ability?(:push_code)
- next false if limit_exceeded?
+ return false unless has_authentication_ability?(:push_code)
+ return false if limit_exceeded?
- lfs_deploy_token? || can?(user, :push_code, project) || can?(deploy_token, :push_code, project)
- end
+ lfs_deploy_token? || can?(user, :push_code,
+project) || can?(deploy_token, :push_code, project) || any_branch_allows_collaboration?
+ end
+ strong_memoize_attr :lfs_upload_access?, :lfs_upload_access
+
+ def any_branch_allows_collaboration?
+ project.merge_requests_allowing_push_to_user(user).any?
end
def lfs_deploy_token?
diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb
index 8a67b62f28b..28d0af7a118 100644
--- a/app/controllers/concerns/membership_actions.rb
+++ b/app/controllers/concerns/membership_actions.rb
@@ -40,17 +40,15 @@ module MembershipActions
respond_to do |format|
format.html do
message =
- begin
- case membershipable
- when Namespace
- if skip_subresources
- _("User was successfully removed from group.")
- else
- _("User was successfully removed from group and any subgroups and projects.")
- end
+ case membershipable
+ when Namespace
+ if skip_subresources
+ _("User was successfully removed from group.")
else
- _("User was successfully removed from project.")
+ _("User was successfully removed from group and any subgroups and projects.")
end
+ else
+ _("User was successfully removed from project.")
end
redirect_to members_page_url, notice: message
diff --git a/app/controllers/concerns/metrics/dashboard/prometheus_api_proxy.rb b/app/controllers/concerns/metrics/dashboard/prometheus_api_proxy.rb
index 65237b552ca..ea9fd2de961 100644
--- a/app/controllers/concerns/metrics/dashboard/prometheus_api_proxy.rb
+++ b/app/controllers/concerns/metrics/dashboard/prometheus_api_proxy.rb
@@ -12,9 +12,7 @@ module Metrics::Dashboard::PrometheusApiProxy
variable_substitution_result =
proxy_variable_substitution_service.new(proxyable, permit_params).execute
- if variable_substitution_result[:status] == :error
- return error_response(variable_substitution_result)
- end
+ return error_response(variable_substitution_result) if variable_substitution_result[:status] == :error
prometheus_result = ::Prometheus::ProxyService.new(
proxyable,
diff --git a/app/controllers/concerns/metrics_dashboard.rb b/app/controllers/concerns/metrics_dashboard.rb
index 28d0692d748..d4e8e95e016 100644
--- a/app/controllers/concerns/metrics_dashboard.rb
+++ b/app/controllers/concerns/metrics_dashboard.rb
@@ -118,9 +118,7 @@ module MetricsDashboard
def decoded_params
params = metrics_dashboard_params
- if params[:dashboard_path]
- params[:dashboard_path] = CGI.unescape(params[:dashboard_path])
- end
+ params[:dashboard_path] = CGI.unescape(params[:dashboard_path]) if params[:dashboard_path]
params
end
diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb
index 0a859bd3af9..e1967c50d70 100644
--- a/app/controllers/concerns/milestone_actions.rb
+++ b/app/controllers/concerns/milestone_actions.rb
@@ -8,9 +8,9 @@ module MilestoneActions
format.html { redirect_to milestone_redirect_path }
format.json do
render json: tabs_json("shared/milestones/_issues_tab", {
- issues: @milestone.sorted_issues(current_user), # rubocop:disable Gitlab/ModuleWithInstanceVariables
- show_project_name: Gitlab::Utils.to_boolean(params[:show_project_name])
- })
+ issues: @milestone.sorted_issues(current_user), # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ show_project_name: Gitlab::Utils.to_boolean(params[:show_project_name])
+ })
end
end
end
@@ -20,9 +20,9 @@ module MilestoneActions
format.html { redirect_to milestone_redirect_path }
format.json do
render json: tabs_json("shared/milestones/_merge_requests_tab", {
- merge_requests: @milestone.sorted_merge_requests(current_user).preload_milestoneish_associations, # rubocop:disable Gitlab/ModuleWithInstanceVariables
- show_project_name: Gitlab::Utils.to_boolean(params[:show_project_name])
- })
+ merge_requests: @milestone.sorted_merge_requests(current_user).preload_milestoneish_associations, # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ show_project_name: Gitlab::Utils.to_boolean(params[:show_project_name])
+ })
end
end
end
@@ -32,8 +32,8 @@ module MilestoneActions
format.html { redirect_to milestone_redirect_path }
format.json do
render json: tabs_json("shared/milestones/_participants_tab", {
- users: @milestone.issue_participants_visible_by_user(current_user) # rubocop:disable Gitlab/ModuleWithInstanceVariables
- })
+ users: @milestone.issue_participants_visible_by_user(current_user) # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ })
end
end
end
@@ -46,10 +46,10 @@ module MilestoneActions
milestone_labels = @milestone.issue_labels_visible_by_user(current_user)
render json: tabs_json("shared/milestones/_labels_tab", {
- labels: milestone_labels.map do |label|
- label.present(issuable_subject: @milestone.resource_parent)
- end
- })
+ labels: milestone_labels.map do |label|
+ label.present(issuable_subject: @milestone.resource_parent)
+ end
+ })
end
end
end
diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb
index b595c3c6790..a41e2d840ac 100644
--- a/app/controllers/concerns/notes_actions.rb
+++ b/app/controllers/concerns/notes_actions.rb
@@ -89,9 +89,7 @@ module NotesActions
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def destroy
- if note.editable?
- Notes::DestroyService.new(project, current_user).execute(note)
- end
+ Notes::DestroyService.new(project, current_user).execute(note) if note.editable?
respond_to do |format|
format.js { head :ok }
@@ -258,15 +256,14 @@ module NotesActions
end
def last_fetched_at
- strong_memoize(:last_fetched_at) do
- microseconds = request.headers['X-Last-Fetched-At'].to_i
+ microseconds = request.headers['X-Last-Fetched-At'].to_i
- seconds = microseconds / MICROSECOND
- frac = microseconds % MICROSECOND
+ seconds = microseconds / MICROSECOND
+ frac = microseconds % MICROSECOND
- Time.zone.at(seconds, frac)
- end
+ Time.zone.at(seconds, frac)
end
+ strong_memoize_attr :last_fetched_at
def notes_filter
current_user&.notes_filter_for(params[:target_type])
@@ -285,23 +282,22 @@ module NotesActions
end
def note_project
- strong_memoize(:note_project) do
- next nil unless project
+ return unless project
- note_project_id = params[:note_project_id]
+ note_project_id = params[:note_project_id]
- the_project =
- if note_project_id.present?
- Project.find(note_project_id)
- else
- project
- end
+ the_project =
+ if note_project_id.present?
+ Project.find(note_project_id)
+ else
+ project
+ end
- next access_denied! unless can?(current_user, :create_note, the_project)
+ return access_denied! unless can?(current_user, :create_note, the_project)
- the_project
- end
+ the_project
end
+ strong_memoize_attr :note_project
def return_discussion?
Gitlab::Utils.to_boolean(params[:return_discussion])
diff --git a/app/controllers/concerns/oauth_applications.rb b/app/controllers/concerns/oauth_applications.rb
index 8e63cc391ff..5b6fe933fda 100644
--- a/app/controllers/concerns/oauth_applications.rb
+++ b/app/controllers/concerns/oauth_applications.rb
@@ -12,9 +12,7 @@ module OauthApplications
def prepare_scopes
scopes = params.fetch(:doorkeeper_application, {}).fetch(:scopes, nil)
- if scopes
- params[:doorkeeper_application][:scopes] = scopes.join(' ')
- end
+ params[:doorkeeper_application][:scopes] = scopes.join(' ') if scopes
end
def set_created_session
@@ -30,7 +28,7 @@ module OauthApplications
end
def permitted_params
- %i{name redirect_uri scopes confidential}
+ %i[name redirect_uri scopes confidential]
end
def application_params
diff --git a/app/controllers/concerns/observability/content_security_policy.rb b/app/controllers/concerns/observability/content_security_policy.rb
new file mode 100644
index 00000000000..eccd1e1e3ef
--- /dev/null
+++ b/app/controllers/concerns/observability/content_security_policy.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Observability
+ module ContentSecurityPolicy
+ extend ActiveSupport::Concern
+
+ included do
+ content_security_policy do |p|
+ next if p.directives.blank? || Gitlab::Observability.observability_url.blank?
+
+ default_frame_src = p.directives['frame-src'] || p.directives['default-src']
+
+ # When ObservabilityUI is not authenticated, it needs to be able
+ # to redirect to the GL sign-in page, hence '/users/sign_in' and '/oauth/authorize'
+ frame_src_values = Array.wrap(default_frame_src) | [Gitlab::Observability.observability_url,
+ Gitlab::Utils.append_path(Gitlab.config.gitlab.url,
+'/users/sign_in'),
+ Gitlab::Utils.append_path(Gitlab.config.gitlab.url,
+'/oauth/authorize')]
+
+ p.frame_src(*frame_src_values)
+ end
+ end
+ end
+end
diff --git a/app/controllers/concerns/page_limiter.rb b/app/controllers/concerns/page_limiter.rb
index 362b02e5856..1d044a41899 100644
--- a/app/controllers/concerns/page_limiter.rb
+++ b/app/controllers/concerns/page_limiter.rb
@@ -44,10 +44,11 @@ module PageLimiter
raise PageLimitNotANumberError unless max_page_number.is_a?(Integer)
raise PageLimitNotSensibleError unless max_page_number > 0
- if params[:page].present? && params[:page].to_i > max_page_number
- record_page_limit_interception
- raise PageOutOfBoundsError, max_page_number
- end
+ return if params[:page].blank?
+ return if params[:page].to_i <= max_page_number
+
+ record_page_limit_interception
+ raise PageOutOfBoundsError, max_page_number
end
# By default just return a HTTP status code and an empty response
diff --git a/app/controllers/concerns/paginated_collection.rb b/app/controllers/concerns/paginated_collection.rb
index fcee4493314..94a52dd0f89 100644
--- a/app/controllers/concerns/paginated_collection.rb
+++ b/app/controllers/concerns/paginated_collection.rb
@@ -10,9 +10,7 @@ module PaginatedCollection
out_of_range = collection.current_page > total_pages
- if out_of_range
- redirect_to(url_for(safe_params.merge(page: total_pages, only_path: true)))
- end
+ redirect_to(url_for(safe_params.merge(page: total_pages, only_path: true))) if out_of_range
out_of_range
end
diff --git a/app/controllers/concerns/preferred_language_switcher.rb b/app/controllers/concerns/preferred_language_switcher.rb
index 9711e57cf7a..00cd0f9d1d5 100644
--- a/app/controllers/concerns/preferred_language_switcher.rb
+++ b/app/controllers/concerns/preferred_language_switcher.rb
@@ -16,3 +16,5 @@ module PreferredLanguageSwitcher
Gitlab::CurrentSettings.default_preferred_language
end
end
+
+PreferredLanguageSwitcher.prepend_mod
diff --git a/app/controllers/concerns/preview_markdown.rb b/app/controllers/concerns/preview_markdown.rb
index 7af114313a1..a7655efe7a9 100644
--- a/app/controllers/concerns/preview_markdown.rb
+++ b/app/controllers/concerns/preview_markdown.rb
@@ -45,7 +45,13 @@ module PreviewMarkdown
when 'projects' then projects_filter_params
when 'timeline_events' then timeline_events_filter_params
else {}
- end.merge(requested_path: params[:path], ref: params[:ref])
+ end.merge(
+ requested_path: params[:path],
+ ref: params[:ref],
+ # Disable comments in markdown for IE browsers because comments in IE
+ # could allow script execution.
+ allow_comments: !browser.ie?
+ )
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
diff --git a/app/controllers/concerns/product_analytics_tracking.rb b/app/controllers/concerns/product_analytics_tracking.rb
index dfa159ccfd7..b01320ce3ec 100644
--- a/app/controllers/concerns/product_analytics_tracking.rb
+++ b/app/controllers/concerns/product_analytics_tracking.rb
@@ -16,7 +16,7 @@ module ProductAnalyticsTracking
end
end
- def track_custom_event(*controller_actions, name:, conditions: nil, action:, label:, destinations: [:redis_hll], &block)
+ def track_custom_event(*controller_actions, name:, action:, label:, conditions: nil, destinations: [:redis_hll], &block)
custom_conditions = [:trackable_html_request?, *conditions]
after_action only: controller_actions, if: custom_conditions do
@@ -30,15 +30,15 @@ module ProductAnalyticsTracking
def route_events_to(destinations, name, &block)
track_unique_redis_hll_event(name, &block) if destinations.include?(:redis_hll)
- if destinations.include?(:snowplow) && event_enabled?(name)
- Gitlab::Tracking.event(
- self.class.to_s,
- name,
- namespace: tracking_namespace_source,
- user: current_user,
- context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: name).to_context]
- )
- end
+ return unless destinations.include?(:snowplow) && event_enabled?(name)
+
+ Gitlab::Tracking.event(
+ self.class.to_s,
+ name,
+ namespace: tracking_namespace_source,
+ user: current_user,
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: name).to_context]
+ )
end
def route_custom_events_to(destinations, name, action, label, &block)
@@ -64,30 +64,32 @@ module ProductAnalyticsTracking
def event_enabled?(event)
events_to_ff = {
- g_analytics_valuestream: :route_hll_to_snowplow,
-
- i_search_paid: :route_hll_to_snowplow_phase2,
- i_search_total: :route_hll_to_snowplow_phase2,
- i_search_advanced: :route_hll_to_snowplow_phase2,
- i_ecosystem_jira_service_list_issues: :route_hll_to_snowplow_phase2,
- users_viewing_analytics_group_devops_adoption: :route_hll_to_snowplow_phase2,
- i_analytics_dev_ops_adoption: :route_hll_to_snowplow_phase2,
- i_analytics_dev_ops_score: :route_hll_to_snowplow_phase2,
- p_analytics_merge_request: :route_hll_to_snowplow_phase2,
- i_analytics_instance_statistics: :route_hll_to_snowplow_phase2,
- g_analytics_contribution: :route_hll_to_snowplow_phase2,
- p_analytics_pipelines: :route_hll_to_snowplow_phase2,
- p_analytics_code_reviews: :route_hll_to_snowplow_phase2,
- p_analytics_valuestream: :route_hll_to_snowplow_phase2,
- p_analytics_insights: :route_hll_to_snowplow_phase2,
- p_analytics_issues: :route_hll_to_snowplow_phase2,
- p_analytics_repo: :route_hll_to_snowplow_phase2,
- g_analytics_insights: :route_hll_to_snowplow_phase2,
- g_analytics_issues: :route_hll_to_snowplow_phase2,
- g_analytics_productivity: :route_hll_to_snowplow_phase2,
- i_analytics_cohorts: :route_hll_to_snowplow_phase2
+ g_analytics_valuestream: '',
+
+ i_search_paid: :_phase2,
+ i_search_total: :_phase2,
+ i_search_advanced: :_phase2,
+ i_ecosystem_jira_service_list_issues: :_phase2,
+ users_viewing_analytics_group_devops_adoption: :_phase2,
+ i_analytics_dev_ops_adoption: :_phase2,
+ i_analytics_dev_ops_score: :_phase2,
+ p_analytics_merge_request: :_phase2,
+ i_analytics_instance_statistics: :_phase2,
+ g_analytics_contribution: :_phase2,
+ p_analytics_pipelines: :_phase2,
+ p_analytics_code_reviews: :_phase2,
+ p_analytics_valuestream: :_phase2,
+ p_analytics_insights: :_phase2,
+ p_analytics_issues: :_phase2,
+ p_analytics_repo: :_phase2,
+ g_analytics_insights: :_phase2,
+ g_analytics_issues: :_phase2,
+ g_analytics_productivity: :_phase2,
+ i_analytics_cohorts: :_phase2,
+
+ g_compliance_dashboard: :_phase4
}
- Feature.enabled?(events_to_ff[event.to_sym], tracking_namespace_source)
+ Feature.enabled?("route_hll_to_snowplow#{events_to_ff[event.to_sym]}", tracking_namespace_source)
end
end
diff --git a/app/controllers/concerns/record_user_last_activity.rb b/app/controllers/concerns/record_user_last_activity.rb
index 29164df4516..6ac87d8f27b 100644
--- a/app/controllers/concerns/record_user_last_activity.rb
+++ b/app/controllers/concerns/record_user_last_activity.rb
@@ -18,9 +18,8 @@ module RecordUserLastActivity
def set_user_last_activity
return unless request.get?
return if Gitlab::Database.read_only?
+ return unless current_user && current_user.last_activity_on != Date.today
- if current_user && current_user.last_activity_on != Date.today
- Users::ActivityService.new(current_user).execute
- end
+ Users::ActivityService.new(current_user).execute
end
end
diff --git a/app/controllers/concerns/render_service_results.rb b/app/controllers/concerns/render_service_results.rb
index 0149a71d9f5..83b880096be 100644
--- a/app/controllers/concerns/render_service_results.rb
+++ b/app/controllers/concerns/render_service_results.rb
@@ -5,25 +5,25 @@ module RenderServiceResults
def success_response(result)
render({
- status: result[:http_status],
- json: result[:body]
- })
+ status: result[:http_status],
+ json: result[:body]
+ })
end
def continue_polling_response
render({
- status: :no_content,
- json: {
- status: _('processing'),
- message: _('Not ready yet. Try again later.')
- }
- })
+ status: :no_content,
+ json: {
+ status: _('processing'),
+ message: _('Not ready yet. Try again later.')
+ }
+ })
end
def error_response(result)
render({
- status: result[:http_status] || :bad_request,
- json: { status: result[:status], message: result[:message] }
- })
+ status: result[:http_status] || :bad_request,
+ json: { status: result[:status], message: result[:message] }
+ })
end
end
diff --git a/app/controllers/concerns/renders_ldap_servers.rb b/app/controllers/concerns/renders_ldap_servers.rb
index cc83ff47048..8c3d9fd4d5c 100644
--- a/app/controllers/concerns/renders_ldap_servers.rb
+++ b/app/controllers/concerns/renders_ldap_servers.rb
@@ -8,12 +8,10 @@ module RendersLdapServers
end
def ldap_servers
- @ldap_servers ||= begin
- if Gitlab::Auth::Ldap::Config.sign_in_enabled?
- Gitlab::Auth::Ldap::Config.available_servers
- else
- []
- end
- end
+ @ldap_servers ||= if Gitlab::Auth::Ldap::Config.sign_in_enabled?
+ Gitlab::Auth::Ldap::Config.available_servers
+ else
+ []
+ end
end
end
diff --git a/app/controllers/concerns/routable_actions.rb b/app/controllers/concerns/routable_actions.rb
index e34d6b09c24..28e1fa473b3 100644
--- a/app/controllers/concerns/routable_actions.rb
+++ b/app/controllers/concerns/routable_actions.rb
@@ -46,13 +46,13 @@ module RoutableActions
return unless request.get?
canonical_path = routable.full_path
- if canonical_path != routable_full_path
- if !request.xhr? && request.format.html? && canonical_path.casecmp(routable_full_path) != 0
- flash[:notice] = "#{routable.class.to_s.titleize} '#{routable_full_path}' was moved to '#{canonical_path}'. Please update any links and bookmarks that may still have the old path."
- end
+ return unless canonical_path != routable_full_path
- redirect_to build_canonical_path(routable), status: :moved_permanently
+ if !request.xhr? && request.format.html? && canonical_path.casecmp(routable_full_path) != 0
+ flash[:notice] = "#{routable.class.to_s.titleize} '#{routable_full_path}' was moved to '#{canonical_path}'. Please update any links and bookmarks that may still have the old path."
end
+
+ redirect_to build_canonical_path(routable), status: :moved_permanently
end
end
diff --git a/app/controllers/concerns/snippets/blobs_actions.rb b/app/controllers/concerns/snippets/blobs_actions.rb
index b510594ad63..2a0491b4df8 100644
--- a/app/controllers/concerns/snippets/blobs_actions.rb
+++ b/app/controllers/concerns/snippets/blobs_actions.rb
@@ -25,14 +25,13 @@ module Snippets::BlobsActions
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def blob
- strong_memoize(:blob) do
- assign_ref_vars
+ assign_ref_vars
- next unless @commit
+ return unless @commit
- @repo.blob_at(@commit.id, @path)
- end
+ @repo.blob_at(@commit.id, @path)
end
+ strong_memoize_attr :blob
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def ensure_blob
@@ -40,11 +39,11 @@ module Snippets::BlobsActions
end
def ensure_repository
- unless snippet.repo_exists?
- Gitlab::AppLogger.error(message: "Snippet raw blob attempt with no repo", snippet: snippet.id)
+ return if snippet.repo_exists?
- respond_422
- end
+ Gitlab::AppLogger.error(message: "Snippet raw blob attempt with no repo", snippet: snippet.id)
+
+ respond_422
end
def snippet_id
diff --git a/app/controllers/concerns/sorting_preference.rb b/app/controllers/concerns/sorting_preference.rb
index 6278b489028..300c1d6d779 100644
--- a/app/controllers/concerns/sorting_preference.rb
+++ b/app/controllers/concerns/sorting_preference.rb
@@ -45,9 +45,7 @@ module SortingPreference
return sort_param if Gitlab::Database.read_only?
- if user_preference[field] != sort_param
- user_preference.update(field => sort_param)
- end
+ user_preference.update(field => sort_param) if user_preference[field] != sort_param
sort_param
end
diff --git a/app/controllers/concerns/sourcegraph_decorator.rb b/app/controllers/concerns/sourcegraph_decorator.rb
index 061990a4361..4aeace1ca67 100644
--- a/app/controllers/concerns/sourcegraph_decorator.rb
+++ b/app/controllers/concerns/sourcegraph_decorator.rb
@@ -22,8 +22,8 @@ module SourcegraphDecorator
return unless sourcegraph_enabled?
gon.push({
- sourcegraph: { url: Gitlab::CurrentSettings.sourcegraph_url }
- })
+ sourcegraph: { url: Gitlab::CurrentSettings.sourcegraph_url }
+ })
end
def sourcegraph_enabled?
diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb
index e98d36854f1..0ba13896631 100644
--- a/app/controllers/concerns/uploads_actions.rb
+++ b/app/controllers/concerns/uploads_actions.rb
@@ -5,7 +5,7 @@ module UploadsActions
include Gitlab::Utils::StrongMemoize
include SendFileUpload
- UPLOAD_MOUNTS = %w(avatar attachment file logo header_logo favicon).freeze
+ UPLOAD_MOUNTS = %w[avatar attachment file logo header_logo favicon].freeze
included do
prepend_before_action :set_request_format_from_path_extension
@@ -73,11 +73,11 @@ module UploadsActions
def set_request_format_from_path_extension
path = request.headers['action_dispatch.original_path'] || request.headers['PATH_INFO']
- if match = path&.match(/\.(\w+)\z/)
- format = Mime[match.captures.first]
+ return unless match = path&.match(/\.(\w+)\z/)
- request.format = format.symbol if format
- end
+ format = Mime[match.captures.first]
+
+ request.format = format.symbol if format
end
def content_disposition
@@ -102,14 +102,13 @@ module UploadsActions
end
def uploader
- strong_memoize(:uploader) do
- if uploader_mounted?
- model.public_send(upload_mount) # rubocop:disable GitlabSecurity/PublicSend
- else
- build_uploader_from_upload || build_uploader_from_params
- end
+ if uploader_mounted?
+ model.public_send(upload_mount) # rubocop:disable GitlabSecurity/PublicSend
+ else
+ build_uploader_from_upload || build_uploader_from_params
end
end
+ strong_memoize_attr :uploader
# rubocop: disable CodeReuse/ActiveRecord
def build_uploader_from_upload
@@ -163,8 +162,9 @@ module UploadsActions
end
def model
- strong_memoize(:model) { find_model }
+ find_model
end
+ strong_memoize_attr :model
def workhorse_authorize_request?
action_name == 'authorize'
diff --git a/app/controllers/concerns/verifies_with_email.rb b/app/controllers/concerns/verifies_with_email.rb
index ac1475597ff..3cada24a81a 100644
--- a/app/controllers/concerns/verifies_with_email.rb
+++ b/app/controllers/concerns/verifies_with_email.rb
@@ -28,7 +28,7 @@ module VerifiesWithEmail
if user.unlock_token
# Prompt for the token if it already has been set
prompt_for_email_verification(user)
- elsif user.access_locked? || !AuthenticationEvent.initial_login_or_known_ip_address?(user, request.ip)
+ elsif user.access_locked? || !trusted_ip_address?(user)
# require email verification if:
# - their account has been locked because of too many failed login attempts, or
# - they have logged in before, but never from the current ip address
@@ -68,7 +68,7 @@ module VerifiesWithEmail
# After successful verification and calling sign_in, devise redirects the
# user to this path. Override it to show the successful verified page.
def after_sign_in_path_for(resource)
- if action_name == 'create' && session[:verification_user_id]
+ if action_name == 'create' && session[:verification_user_id] == resource.id
return users_successful_verification_path
end
@@ -133,6 +133,12 @@ module VerifiesWithEmail
sign_in(user)
end
+ def trusted_ip_address?(user)
+ return true if Feature.disabled?(:check_ip_address_for_email_verification)
+
+ AuthenticationEvent.initial_login_or_known_ip_address?(user, request.ip)
+ end
+
def prompt_for_email_verification(user)
session[:verification_user_id] = user.id
self.resource = user
diff --git a/app/controllers/concerns/vscode_cdn_csp.rb b/app/controllers/concerns/vscode_cdn_csp.rb
new file mode 100644
index 00000000000..dc8cea966e5
--- /dev/null
+++ b/app/controllers/concerns/vscode_cdn_csp.rb
@@ -0,0 +1,17 @@
+# rubocop:disable Naming/FileName
+# frozen_string_literal: true
+
+module VSCodeCDNCSP
+ extend ActiveSupport::Concern
+
+ included do
+ content_security_policy do |policy|
+ next if policy.directives.blank?
+
+ default_src = Array(policy.directives['default-src'] || [])
+ policy.directives['frame-src'] ||= default_src
+ policy.directives['frame-src'].concat(['https://*.vscode-cdn.net/'])
+ end
+ end
+end
+# rubocop:enable Naming/FileName
diff --git a/app/controllers/concerns/web_hooks/hook_actions.rb b/app/controllers/concerns/web_hooks/hook_actions.rb
index 75065ef9d24..f61600af951 100644
--- a/app/controllers/concerns/web_hooks/hook_actions.rb
+++ b/app/controllers/concerns/web_hooks/hook_actions.rb
@@ -18,7 +18,9 @@ module WebHooks
self.hook = relation.new(hook_params)
hook.save
- unless hook.valid?
+ if hook.valid?
+ flash[:notice] = _('Webhook was created')
+ else
self.hooks = relation.select(&:persisted?)
flash[:alert] = hook.errors.full_messages.to_sentence.html_safe
end
@@ -28,8 +30,8 @@ module WebHooks
def update
if hook.update(hook_params)
- flash[:notice] = _('Hook was successfully updated.')
- redirect_to action: :index
+ flash[:notice] = _('Webhook was updated')
+ redirect_to action: :edit
else
render 'edit'
end
@@ -66,21 +68,14 @@ module WebHooks
end
def hook_param_names
- param_names = %i[enable_ssl_verification token url push_events_branch_filter]
- param_names.push(:branch_filter_strategy) if Feature.enabled?(:enhanced_webhook_support_regex)
- param_names
+ %i[enable_ssl_verification token url push_events_branch_filter branch_filter_strategy]
end
def destroy_hook(hook)
result = WebHooks::DestroyService.new(current_user).execute(hook)
if result[:status] == :success
- flash[:notice] =
- if result[:async]
- format(_("%{hook_type} was scheduled for deletion"), hook_type: hook.model_name.human)
- else
- format(_("%{hook_type} was deleted"), hook_type: hook.model_name.human)
- end
+ flash[:notice] = result[:async] ? _('Webhook was scheduled for deletion') : _('Webhook was deleted')
else
flash[:alert] = result[:message]
end
diff --git a/app/controllers/dashboard/snippets_controller.rb b/app/controllers/dashboard/snippets_controller.rb
index 5a885349467..d72a5e44b9f 100644
--- a/app/controllers/dashboard/snippets_controller.rb
+++ b/app/controllers/dashboard/snippets_controller.rb
@@ -7,7 +7,7 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController
skip_cross_project_access_check :index
- feature_category :snippets
+ feature_category :source_code_management
def index
@snippet_counts = Snippets::CountService
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index d2434d4b0ba..3005d19f8ed 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -3,6 +3,7 @@
class Dashboard::TodosController < Dashboard::ApplicationController
include ActionView::Helpers::NumberHelper
include PaginatedCollection
+ include Gitlab::Utils::StrongMemoize
before_action :authorize_read_project!, only: :index
before_action :authorize_read_group!, only: :index
@@ -64,19 +65,19 @@ class Dashboard::TodosController < Dashboard::ApplicationController
def authorize_read_project!
project_id = params[:project_id]
- if project_id.present?
- project = Project.find(project_id)
- render_404 unless can?(current_user, :read_project, project)
- end
+ return unless project_id.present?
+
+ project = Project.find(project_id)
+ render_404 unless can?(current_user, :read_project, project)
end
def authorize_read_group!
group_id = params[:group_id]
- if group_id.present?
- group = Group.find(group_id)
- render_404 unless can?(current_user, :read_group, group)
- end
+ return unless group_id.present?
+
+ group = Group.find(group_id)
+ render_404 unless can?(current_user, :read_group, group)
end
def find_todos
@@ -99,14 +100,28 @@ class Dashboard::TodosController < Dashboard::ApplicationController
end
def todo_params
- aliased_action_id(
+ aliased_params(
params.permit(:action_id, :author_id, :project_id, :type, :sort, :state, :group_id)
)
end
+ strong_memoize_attr :todo_params
+
+ def aliased_params(original_params)
+ alias_issue_type(original_params)
+ alias_action_id(original_params)
+
+ original_params
+ end
+
+ def alias_issue_type(original_params)
+ return unless original_params[:type] == Issue.name
+
+ original_params[:type] = [Issue.name, WorkItem.name]
+ end
- def aliased_action_id(original_params)
- return original_params unless original_params[:action_id].to_i == ::Todo::MENTIONED
+ def alias_action_id(original_params)
+ return unless original_params[:action_id].to_i == ::Todo::MENTIONED
- original_params.merge(action_id: [::Todo::MENTIONED, ::Todo::DIRECTLY_ADDRESSED])
+ original_params[:action_id] = [::Todo::MENTIONED, ::Todo::DIRECTLY_ADDRESSED]
end
end
diff --git a/app/controllers/explore/snippets_controller.rb b/app/controllers/explore/snippets_controller.rb
index 617cc2e7f3d..dee94b53cc1 100644
--- a/app/controllers/explore/snippets_controller.rb
+++ b/app/controllers/explore/snippets_controller.rb
@@ -3,7 +3,7 @@
class Explore::SnippetsController < Explore::ApplicationController
include Gitlab::NoteableMetadata
- feature_category :snippets
+ feature_category :source_code_management
def index
@snippets = SnippetsFinder.new(current_user, explore: true)
diff --git a/app/controllers/google_api/authorizations_controller.rb b/app/controllers/google_api/authorizations_controller.rb
index 5080ee5fbbe..536c5e347e7 100644
--- a/app/controllers/google_api/authorizations_controller.rb
+++ b/app/controllers/google_api/authorizations_controller.rb
@@ -48,14 +48,13 @@ module GoogleApi
end
def redirect_uri_from_session
- strong_memoize(:redirect_uri_from_session) do
- if params[:state].present?
- session[session_key_for_redirect_uri(params[:state])]
- else
- nil
- end
+ if params[:state].present?
+ session[session_key_for_redirect_uri(params[:state])]
+ else
+ nil
end
end
+ strong_memoize_attr :redirect_uri_from_session
def session_key_for_redirect_uri(state)
GoogleApi::CloudPlatform::Client.session_key_for_redirect_uri(state)
diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb
index 5ffd525c170..942cb9beed4 100644
--- a/app/controllers/graphql_controller.rb
+++ b/app/controllers/graphql_controller.rb
@@ -70,6 +70,12 @@ class GraphqlController < ApplicationController
end
end
+ rescue_from Gitlab::Auth::TooManyIps do |exception|
+ log_exception(exception)
+
+ render_error(exception.message, status: :forbidden)
+ end
+
rescue_from Gitlab::Graphql::Variables::Invalid do |exception|
render_error(exception.message, status: :unprocessable_entity)
end
diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb
index f8cfa996447..5440908aee7 100644
--- a/app/controllers/groups/application_controller.rb
+++ b/app/controllers/groups/application_controller.rb
@@ -96,6 +96,28 @@ class Groups::ApplicationController < ApplicationController
def validate_root_group!
render_404 unless group.root?
end
+
+ def authorize_action!(action)
+ access_denied! unless can?(current_user, action, group)
+ end
+
+ def respond_to_missing?(method, *args)
+ case method.to_s
+ when /\Aauthorize_(.*)!\z/
+ true
+ else
+ super
+ end
+ end
+
+ def method_missing(method_sym, *arguments, &block)
+ case method_sym.to_s
+ when /\Aauthorize_(.*)!\z/
+ authorize_action!(Regexp.last_match(1).to_sym)
+ else
+ super
+ end
+ end
end
Groups::ApplicationController.prepend_mod_with('Groups::ApplicationController')
diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb
index e1ba86220c7..6bb807be1c4 100644
--- a/app/controllers/groups/boards_controller.rb
+++ b/app/controllers/groups/boards_controller.rb
@@ -20,16 +20,14 @@ class Groups::BoardsController < Groups::ApplicationController
private
def board_finder
- strong_memoize :board_finder do
- Boards::BoardsFinder.new(parent, current_user, board_id: params[:id])
- end
+ Boards::BoardsFinder.new(parent, current_user, board_id: params[:id])
end
+ strong_memoize_attr :board_finder
def board_create_service
- strong_memoize :board_create_service do
- Boards::CreateService.new(parent, current_user)
- end
+ Boards::CreateService.new(parent, current_user)
end
+ strong_memoize_attr :board_create_service
def authorize_read_board!
access_denied! unless can?(current_user, :read_issue_board, group)
diff --git a/app/controllers/groups/dependency_proxy_for_containers_controller.rb b/app/controllers/groups/dependency_proxy_for_containers_controller.rb
index 2e9e0b12d2f..427df9a7129 100644
--- a/app/controllers/groups/dependency_proxy_for_containers_controller.rb
+++ b/app/controllers/groups/dependency_proxy_for_containers_controller.rb
@@ -117,7 +117,7 @@ class Groups::DependencyProxyForContainersController < ::Groups::DependencyProxy
end
def blob_file_name
- @blob_file_name ||= params[:sha].sub('sha256:', '') + '.gz'
+ @blob_file_name ||= "#{params[:sha].sub('sha256:', '')}.gz"
end
def manifest_file_name
diff --git a/app/controllers/groups/observability_controller.rb b/app/controllers/groups/observability_controller.rb
index 4b1f2b582ce..3baa5e830ff 100644
--- a/app/controllers/groups/observability_controller.rb
+++ b/app/controllers/groups/observability_controller.rb
@@ -1,18 +1,9 @@
# frozen_string_literal: true
module Groups
class ObservabilityController < Groups::ApplicationController
- feature_category :tracing
-
- content_security_policy do |p|
- next if p.directives.blank?
-
- default_frame_src = p.directives['frame-src'] || p.directives['default-src']
+ include ::Observability::ContentSecurityPolicy
- # When ObservabilityUI is not authenticated, it needs to be able to redirect to the GL sign-in page, hence 'self'
- frame_src_values = Array.wrap(default_frame_src) | [observability_url, "'self'"]
-
- p.frame_src(*frame_src_values)
- end
+ feature_category :tracing
before_action :check_observability_allowed
@@ -34,16 +25,8 @@ module Groups
render 'observability', layout: 'group', locals: { base_layout: 'layouts/fullscreen' }
end
- def self.observability_url
- Gitlab::Observability.observability_url
- end
-
- def observability_url
- self.class.observability_url
- end
-
def check_observability_allowed
- return render_404 unless observability_url.present?
+ return render_404 unless Gitlab::Observability.observability_url.present?
render_404 unless can?(current_user, :read_observability, @group)
end
diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb
index b1afac1f1c7..1dfa8cdf133 100644
--- a/app/controllers/groups/settings/ci_cd_controller.rb
+++ b/app/controllers/groups/settings/ci_cd_controller.rb
@@ -15,6 +15,8 @@ module Groups
urgency :low
def show
+ @entity = :group
+ @variable_limit = ::Plan.default.actual_limits.group_ci_variables
end
def update
diff --git a/app/controllers/groups/usage_quotas_controller.rb b/app/controllers/groups/usage_quotas_controller.rb
new file mode 100644
index 00000000000..29878f0001d
--- /dev/null
+++ b/app/controllers/groups/usage_quotas_controller.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Groups
+ class UsageQuotasController < Groups::ApplicationController
+ before_action :authorize_read_usage_quotas!
+ before_action :verify_usage_quotas_enabled!
+
+ feature_category :subscription_cost_management
+ urgency :low
+
+ def index
+ # To be used in ee/app/controllers/ee/groups/usage_quotas_controller.rb
+ @seat_count_data = seat_count_data
+ end
+
+ private
+
+ def verify_usage_quotas_enabled!
+ render_404 unless Feature.enabled?(:usage_quotas_for_all_editions, group)
+ render_404 if group.has_parent?
+ end
+
+ # To be overriden in ee/app/controllers/ee/groups/usage_quotas_controller.rb
+ def seat_count_data; end
+ end
+end
+
+Groups::UsageQuotasController.prepend_mod
diff --git a/app/controllers/groups/variables_controller.rb b/app/controllers/groups/variables_controller.rb
index 220b0b4509c..9ddf6c80c70 100644
--- a/app/controllers/groups/variables_controller.rb
+++ b/app/controllers/groups/variables_controller.rb
@@ -50,7 +50,7 @@ module Groups
end
def variable_params_attributes
- %i[id variable_type key secret_value protected masked _destroy]
+ %i[id variable_type key secret_value protected masked raw _destroy]
end
def authorize_admin_build!
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 3f516c24a69..0a487bb2508 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -386,7 +386,7 @@ class GroupsController < Groups::ApplicationController
override :has_project_list?
def has_project_list?
- %w(details show index).include?(action_name)
+ %w[details show index].include?(action_name)
end
def captcha_enabled?
diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb
index fcf6871d137..8a8c41e65b9 100644
--- a/app/controllers/ide_controller.rb
+++ b/app/controllers/ide_controller.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
class IdeController < ApplicationController
+ include VSCodeCDNCSP
include ClientsidePreviewCSP
include StaticObjectExternalStorageCSP
include Gitlab::Utils::StrongMemoize
diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb
index 75193309a4e..1d05cee02d4 100644
--- a/app/controllers/import/bitbucket_controller.rb
+++ b/app/controllers/import/bitbucket_controller.rb
@@ -49,6 +49,14 @@ class Import::BitbucketController < Import::BaseController
namespace_path = params[:new_namespace].presence || repo_owner
target_namespace = find_or_create_namespace(namespace_path, current_user)
+ Gitlab::Tracking.event(
+ self.class.name,
+ 'create',
+ label: 'import_access_level',
+ user: current_user,
+ extra: { user_role: user_role(current_user, target_namespace), import_type: 'bitbucket' }
+ )
+
if current_user.can?(:create_projects, target_namespace)
# The token in a session can be expired, we need to get most recent one because
# Bitbucket::Connection class refreshes it.
@@ -89,6 +97,21 @@ class Import::BitbucketController < Import::BaseController
private
+ def user_role(user, namespace)
+ if current_user.id == namespace&.owner_id
+ Gitlab::Access.options_with_owner.key(Gitlab::Access::OWNER)
+ else
+ access_level = current_user&.group_members&.find_by(source_id: namespace&.id)&.access_level
+
+ case access_level
+ when nil
+ 'Not a member'
+ else
+ Gitlab::Access.human_access(access_level)
+ end
+ end
+ end
+
def oauth_client
@oauth_client ||= OAuth2::Client.new(provider.app_id, provider.app_secret, options)
end
diff --git a/app/controllers/import/bulk_imports_controller.rb b/app/controllers/import/bulk_imports_controller.rb
index 655fc7854fe..9a7118ce498 100644
--- a/app/controllers/import/bulk_imports_controller.rb
+++ b/app/controllers/import/bulk_imports_controller.rb
@@ -135,7 +135,7 @@ class Import::BulkImportsController < ApplicationController
session[url_key],
allow_localhost: allow_local_requests?,
allow_local_network: allow_local_requests?,
- schemes: %w(http https)
+ schemes: %w[http https]
)
rescue Gitlab::UrlBlocker::BlockedUrlError => e
clear_session_data
diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb
index 7b580234227..77043e174b4 100644
--- a/app/controllers/import/fogbugz_controller.rb
+++ b/app/controllers/import/fogbugz_controller.rb
@@ -114,7 +114,7 @@ class Import::FogbugzController < Import::BaseController
end
def user_map_params
- params.permit(users: %w(name email gitlab_user))
+ params.permit(users: %w[name email gitlab_user])
end
def verify_fogbugz_import_enabled
@@ -126,7 +126,7 @@ class Import::FogbugzController < Import::BaseController
params[:uri],
allow_localhost: allow_local_requests?,
allow_local_network: allow_local_requests?,
- schemes: %w(http https)
+ schemes: %w[http https]
)
rescue Gitlab::UrlBlocker::BlockedUrlError => e
redirect_to new_import_fogbugz_url, alert: _('Specified URL cannot be used: "%{reason}"') % { reason: e.message }
diff --git a/app/controllers/import/gitea_controller.rb b/app/controllers/import/gitea_controller.rb
index 4b4ac07b389..61e32650db3 100644
--- a/app/controllers/import/gitea_controller.rb
+++ b/app/controllers/import/gitea_controller.rb
@@ -16,12 +16,27 @@ class Import::GiteaController < Import::GithubController
super
end
- # We need to re-expose controller's internal method 'status' as action.
- # rubocop:disable Lint/UselessMethodDefinition
def status
- super
+ # Request repos to display error page if provider token is invalid
+ # Improving in https://gitlab.com/gitlab-org/gitlab/-/issues/25859
+ client_repos
+
+ respond_to do |format|
+ format.json do
+ render json: { imported_projects: serialized_imported_projects,
+ provider_repos: serialized_provider_repos,
+ incompatible_repos: serialized_incompatible_repos }
+ end
+
+ format.html do
+ if params[:namespace_id].present?
+ @namespace = Namespace.find_by_id(params[:namespace_id])
+
+ render_404 unless current_user.can?(:create_projects, @namespace)
+ end
+ end
+ end
end
- # rubocop:enable Lint/UselessMethodDefinition
protected
@@ -61,7 +76,6 @@ class Import::GiteaController < Import::GithubController
@client_repos ||= filtered(client.repos)
end
- override :client
def client
@client ||= Gitlab::LegacyGithubImport::Client.new(session[access_token_key], **client_options)
end
@@ -78,7 +92,7 @@ class Import::GiteaController < Import::GithubController
provider_url,
allow_localhost: allow_local_requests?,
allow_local_network: allow_local_requests?,
- schemes: %w(http https)
+ schemes: %w[http https]
)
rescue Gitlab::UrlBlocker::BlockedUrlError => e
session[access_token_key] = nil
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
index 92763e09ba3..cb58b5974ca 100644
--- a/app/controllers/import/github_controller.rb
+++ b/app/controllers/import/github_controller.rb
@@ -15,6 +15,8 @@ class Import::GithubController < Import::BaseController
rescue_from Octokit::TooManyRequests, with: :provider_rate_limit
rescue_from Gitlab::GithubImport::RateLimitError, with: :rate_limit_threshold_exceeded
+ delegate :client, to: :client_proxy, private: true
+
PAGE_LENGTH = 25
def new
@@ -46,7 +48,22 @@ class Import::GithubController < Import::BaseController
# Improving in https://gitlab.com/gitlab-org/gitlab-foss/issues/55585
client_repos
- super
+ respond_to do |format|
+ format.json do
+ render json: { imported_projects: serialized_imported_projects,
+ provider_repos: serialized_provider_repos,
+ incompatible_repos: serialized_incompatible_repos,
+ page_info: client_repos_response[:page_info] }
+ end
+
+ format.html do
+ if params[:namespace_id].present?
+ @namespace = Namespace.find_by_id(params[:namespace_id])
+
+ render_404 unless current_user.can?(:create_projects, @namespace)
+ end
+ end
+ end
end
def create
@@ -126,24 +143,18 @@ class Import::GithubController < Import::BaseController
end
end
- def client
- @client ||= if Feature.enabled?(:remove_legacy_github_client)
- Gitlab::GithubImport::Client.new(session[access_token_key])
- else
- Gitlab::LegacyGithubImport::Client.new(session[access_token_key], **client_options)
- end
+ def client_proxy
+ @client_proxy ||= Gitlab::GithubImport::Clients::Proxy.new(
+ session[access_token_key], client_options
+ )
+ end
+
+ def client_repos_response
+ @client_repos_response ||= client_proxy.repos(sanitized_filter_param, pagination_options)
end
def client_repos
- @client_repos ||= if Feature.enabled?(:remove_legacy_github_client)
- if sanitized_filter_param
- client.search_repos_by_name(sanitized_filter_param, pagination_options)[:items]
- else
- client.repos(pagination_options)
- end
- else
- filtered(client.repos)
- end
+ client_repos_response[:repos]
end
def sanitized_filter_param
@@ -213,6 +224,11 @@ class Import::GithubController < Import::BaseController
def pagination_options
{
+ before: params[:before].presence,
+ after: params[:after].presence,
+ first: PAGE_LENGTH,
+ # TODO: remove after rollout FF github_client_fetch_repos_via_graphql
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/385649
page: [1, params[:page].to_i].max,
per_page: PAGE_LENGTH
}
diff --git a/app/controllers/jira_connect/app_descriptor_controller.rb b/app/controllers/jira_connect/app_descriptor_controller.rb
index 16bd73f5ab6..3c50d54fa10 100644
--- a/app/controllers/jira_connect/app_descriptor_controller.rb
+++ b/app/controllers/jira_connect/app_descriptor_controller.rb
@@ -28,7 +28,7 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController
type: 'jwt'
},
modules: modules,
- scopes: %w(READ WRITE DELETE),
+ scopes: %w[READ WRITE DELETE],
apiVersion: 1,
apiMigrations: {
'context-qsh': true,
@@ -76,7 +76,7 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController
jiraDevelopmentTool: {
actions: {
createBranch: {
- templateUrl: new_jira_connect_branch_url + '?issue_key={issue.key}&issue_summary={issue.summary}'
+ templateUrl: "#{new_jira_connect_branch_url}?issue_key={issue.key}&issue_summary={issue.summary}"
}
},
key: 'gitlab-development-tool',
@@ -84,7 +84,7 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController
name: { value: 'GitLab' },
url: HOME_URL,
logoUrl: logo_url,
- capabilities: %w(branch commit pull_request)
+ capabilities: %w[branch commit pull_request]
}
}
end
diff --git a/app/controllers/jira_connect/application_controller.rb b/app/controllers/jira_connect/application_controller.rb
index b9f0ea795e1..e26d69314cd 100644
--- a/app/controllers/jira_connect/application_controller.rb
+++ b/app/controllers/jira_connect/application_controller.rb
@@ -3,11 +3,6 @@
class JiraConnect::ApplicationController < ApplicationController
include Gitlab::Utils::StrongMemoize
- CORS_ALLOWED_METHODS = {
- '/-/jira_connect/oauth_application_id' => %i[GET OPTIONS],
- '/-/jira_connect/subscriptions/*' => %i[DELETE OPTIONS]
- }.freeze
-
skip_before_action :authenticate_user!
skip_before_action :verify_authenticity_token
before_action :verify_atlassian_jwt!
@@ -65,25 +60,4 @@ class JiraConnect::ApplicationController < ApplicationController
def auth_token
params[:jwt] || request.headers['Authorization']&.split(' ', 2)&.last
end
-
- def cors_allowed_methods
- CORS_ALLOWED_METHODS[resource]
- end
-
- def resource
- request.path.gsub(%r{/\d+$}, '/*')
- end
-
- def set_cors_headers
- return unless allow_cors_request?
-
- response.set_header('Access-Control-Allow-Origin', Gitlab::CurrentSettings.jira_connect_proxy_url)
- response.set_header('Access-Control-Allow-Methods', cors_allowed_methods.join(', '))
- end
-
- def allow_cors_request?
- return false if cors_allowed_methods.nil?
-
- !Gitlab.com? && Gitlab::CurrentSettings.jira_connect_proxy_url.present?
- end
end
diff --git a/app/controllers/jira_connect/cors_preflight_checks_controller.rb b/app/controllers/jira_connect/cors_preflight_checks_controller.rb
deleted file mode 100644
index 3f30c1e04df..00000000000
--- a/app/controllers/jira_connect/cors_preflight_checks_controller.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-module JiraConnect
- class CorsPreflightChecksController < ApplicationController
- feature_category :integrations
-
- skip_before_action :verify_atlassian_jwt!
- before_action :set_cors_headers
-
- def index
- return render_404 unless allow_cors_request?
-
- render plain: '', content_type: 'text/plain'
- end
- end
-end
diff --git a/app/controllers/jira_connect/events_controller.rb b/app/controllers/jira_connect/events_controller.rb
index 394fdc9b2f6..fa1e1f505eb 100644
--- a/app/controllers/jira_connect/events_controller.rb
+++ b/app/controllers/jira_connect/events_controller.rb
@@ -31,7 +31,10 @@ class JiraConnect::EventsController < JiraConnect::ApplicationController
end
def update_installation
- current_jira_installation.update(update_params)
+ JiraConnectInstallations::UpdateService.execute(
+ current_jira_installation,
+ update_params
+ ).success?
end
def create_params
@@ -56,7 +59,7 @@ class JiraConnect::EventsController < JiraConnect::ApplicationController
def jwt_verification_claims
{
- aud: jira_connect_base_url(protocol: 'https'),
+ aud: Gitlab.config.jira_connect.enforce_jira_base_url_https ? jira_connect_base_url(protocol: 'https') : jira_connect_base_url,
iss: transformed_params[:client_key],
qsh: Atlassian::Jwt.create_query_string_hash(request.url, request.method, jira_connect_base_url)
}
diff --git a/app/controllers/jira_connect/installations_controller.rb b/app/controllers/jira_connect/installations_controller.rb
index 401bc4f9c87..44dbf90f5fb 100644
--- a/app/controllers/jira_connect/installations_controller.rb
+++ b/app/controllers/jira_connect/installations_controller.rb
@@ -6,11 +6,12 @@ class JiraConnect::InstallationsController < JiraConnect::ApplicationController
end
def update
- if current_jira_installation.update(installation_params)
+ result = update_installation
+ if result.success?
render json: installation_json(current_jira_installation)
else
render(
- json: { errors: current_jira_installation.errors },
+ json: { errors: result.message },
status: :unprocessable_entity
)
end
@@ -18,6 +19,13 @@ class JiraConnect::InstallationsController < JiraConnect::ApplicationController
private
+ def update_installation
+ JiraConnectInstallations::UpdateService.execute(
+ current_jira_installation,
+ installation_params
+ )
+ end
+
def installation_json(installation)
{
gitlab_com: installation.instance_url.blank?,
diff --git a/app/controllers/jira_connect/oauth_application_ids_controller.rb b/app/controllers/jira_connect/oauth_application_ids_controller.rb
index 3e788e2282e..de520337af3 100644
--- a/app/controllers/jira_connect/oauth_application_ids_controller.rb
+++ b/app/controllers/jira_connect/oauth_application_ids_controller.rb
@@ -5,7 +5,6 @@ module JiraConnect
feature_category :integrations
skip_before_action :verify_atlassian_jwt!
- before_action :set_cors_headers
def show
if show_application_id?
@@ -20,7 +19,7 @@ module JiraConnect
def show_application_id?
return if Gitlab.com?
- Feature.enabled?(:jira_connect_oauth_self_managed) && jira_connect_application_key.present?
+ jira_connect_application_key.present?
end
def jira_connect_application_key
diff --git a/app/controllers/jira_connect/public_keys_controller.rb b/app/controllers/jira_connect/public_keys_controller.rb
index b3144993edb..09003f8478f 100644
--- a/app/controllers/jira_connect/public_keys_controller.rb
+++ b/app/controllers/jira_connect/public_keys_controller.rb
@@ -10,7 +10,9 @@ module JiraConnect
skip_before_action :authenticate_user!
def show
- return render_404 if Feature.disabled?(:jira_connect_oauth_self_managed) || !Gitlab.com?
+ if Feature.disabled?(:jira_connect_oauth_self_managed) || !Gitlab.config.jira_connect.enable_public_keys_storage
+ return render_404
+ end
render plain: public_key.key
end
diff --git a/app/controllers/jira_connect/subscriptions_controller.rb b/app/controllers/jira_connect/subscriptions_controller.rb
index 9a732cadd94..ff7477a94d6 100644
--- a/app/controllers/jira_connect/subscriptions_controller.rb
+++ b/app/controllers/jira_connect/subscriptions_controller.rb
@@ -1,19 +1,20 @@
# frozen_string_literal: true
class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController
+ ALLOWED_IFRAME_ANCESTORS = [:self, 'https://*.atlassian.net', 'https://*.jira.com'].freeze
layout 'jira_connect'
content_security_policy do |p|
next if p.directives.blank?
# rubocop: disable Lint/PercentStringArray
- script_src_values = Array.wrap(p.directives['script-src']) | %w('self' https://connect-cdn.atl-paas.net)
- style_src_values = Array.wrap(p.directives['style-src']) | %w('self' 'unsafe-inline')
+ script_src_values = Array.wrap(p.directives['script-src']) | %w['self' https://connect-cdn.atl-paas.net]
+ style_src_values = Array.wrap(p.directives['style-src']) | %w['self' 'unsafe-inline']
# rubocop: enable Lint/PercentStringArray
# *.jira.com is needed for some legacy Jira Cloud instances, new ones will use *.atlassian.net
# https://support.atlassian.com/organization-administration/docs/ip-addresses-and-domains-for-atlassian-cloud-products/
- p.frame_ancestors :self, 'https://*.atlassian.net', 'https://*.jira.com'
+ p.frame_ancestors(*(ALLOWED_IFRAME_ANCESTORS + Gitlab.config.jira_connect.additional_iframe_ancestors))
p.script_src(*script_src_values)
p.style_src(*style_src_values)
end
@@ -27,7 +28,6 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController
before_action :verify_qsh_claim!, only: :index
before_action :allow_self_managed_content_security_policy, only: :index
before_action :authenticate_user!, only: :create
- before_action :set_cors_headers
def index
@subscriptions = current_jira_installation.subscriptions.preload_namespace_route
@@ -65,8 +65,6 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController
private
def allow_self_managed_content_security_policy
- return unless Feature.enabled?(:jira_connect_oauth_self_managed_setting)
-
return unless current_jira_installation.instance_url?
request.content_security_policy.directives['connect-src'] ||= []
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index f3f0ddd968a..8650b6cbc6f 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -119,6 +119,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
identity_linker ||= auth_module::IdentityLinker.new(current_user, oauth, session)
link_identity(identity_linker)
+ set_remember_me(current_user)
if identity_linker.changed?
redirect_identity_linked
@@ -169,6 +170,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# available in the logs for this request.
Gitlab::ApplicationContext.push(user: user)
log_audit_event(user, with: oauth['provider'])
+ Gitlab::Tracking.event(self.class.name, "#{oauth['provider']}_sso", user: user) if new_user
set_remember_me(user)
diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb
index 1216353be36..38cdb16c350 100644
--- a/app/controllers/passwords_controller.rb
+++ b/app/controllers/passwords_controller.rb
@@ -58,8 +58,8 @@ class PasswordsController < Devise::PasswordsController
def check_password_authentication_available
if resource
return if resource.allow_password_authentication?
- else
- return if Gitlab::CurrentSettings.password_authentication_enabled?
+ elsif Gitlab::CurrentSettings.password_authentication_enabled?
+ return
end
redirect_to after_sending_reset_password_instructions_path_for(resource_name),
diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb
index 90d5f945d78..39e8f6c500d 100644
--- a/app/controllers/profiles/keys_controller.rb
+++ b/app/controllers/profiles/keys_controller.rb
@@ -37,6 +37,6 @@ class Profiles::KeysController < Profiles::ApplicationController
private
def key_params
- params.require(:key).permit(:title, :key, :expires_at)
+ params.require(:key).permit(:title, :key, :usage_type, :expires_at)
end
end
diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb
index a57c87bf691..974e7104c07 100644
--- a/app/controllers/profiles/preferences_controller.rb
+++ b/app/controllers/profiles/preferences_controller.rb
@@ -57,7 +57,8 @@ class Profiles::PreferencesController < Profiles::ApplicationController
:render_whitespace_in_code,
:markdown_surround_selection,
:markdown_automatic_lists,
- :use_legacy_web_ide
+ :use_legacy_web_ide,
+ :use_new_navigation
]
end
end
diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb
index 0933f2bb7ea..03b7cc9f892 100644
--- a/app/controllers/profiles/two_factor_auths_controller.rb
+++ b/app/controllers/profiles/two_factor_auths_controller.rb
@@ -97,7 +97,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def skip
if two_factor_grace_period_expired?
- redirect_to new_profile_two_factor_auth_path, alert: s_('Cannot skip two factor authentication setup')
+ redirect_to new_profile_two_factor_auth_path, alert: _('Cannot skip two factor authentication setup')
else
session[:skip_two_factor] = current_user.otp_grace_period_started_at + two_factor_grace_period.hours
redirect_to root_path
@@ -186,9 +186,9 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def u2f_registrations
current_user.u2f_registrations.map do |u2f_registration|
{
- name: u2f_registration.name,
- created_at: u2f_registration.created_at,
- delete_path: profile_u2f_registration_path(u2f_registration)
+ name: u2f_registration.name,
+ created_at: u2f_registration.created_at,
+ delete_path: profile_u2f_registration_path(u2f_registration)
}
end
end
@@ -196,9 +196,9 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def webauthn_registrations
current_user.webauthn_registrations.map do |webauthn_registration|
{
- name: webauthn_registration.name,
- created_at: webauthn_registration.created_at,
- delete_path: profile_webauthn_registration_path(webauthn_registration)
+ name: webauthn_registration.name,
+ created_at: webauthn_registration.created_at,
+ delete_path: profile_webauthn_registration_path(webauthn_registration)
}
end
end
@@ -216,7 +216,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
group_links = groups.map { |group| view_context.link_to group.full_name, group_path(group) }.to_sentence
leave_group_links = groups.map { |group| view_context.link_to (s_("leave %{group_name}") % { group_name: group.full_name }), leave_group_members_path(group), remote: false, method: :delete }.to_sentence
- s_(%{The group settings for %{group_links} require you to enable Two-Factor Authentication for your account. You can %{leave_group_links}.})
+ s_(%(The group settings for %{group_links} require you to enable Two-Factor Authentication for your account. You can %{leave_group_links}.))
.html_safe % { group_links: group_links.html_safe, leave_group_links: leave_group_links.html_safe }
end
diff --git a/app/controllers/projects/autocomplete_sources_controller.rb b/app/controllers/projects/autocomplete_sources_controller.rb
index 7755effe1da..ef20c71cd77 100644
--- a/app/controllers/projects/autocomplete_sources_controller.rb
+++ b/app/controllers/projects/autocomplete_sources_controller.rb
@@ -7,7 +7,7 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController
feature_category :team_planning, [:issues, :labels, :milestones, :commands, :contacts]
feature_category :code_review, [:merge_requests]
feature_category :users, [:members]
- feature_category :snippets, [:snippets]
+ feature_category :source_code_management, [:snippets]
urgency :low, [:merge_requests, :members]
urgency :low, [:issues, :labels, :milestones, :commands, :contacts]
diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb
index 42bd87e1c01..dbbffc4c283 100644
--- a/app/controllers/projects/badges_controller.rb
+++ b/app/controllers/projects/badges_controller.rb
@@ -13,10 +13,10 @@ class Projects::BadgesController < Projects::ApplicationController
def pipeline
pipeline_status = Gitlab::Ci::Badge::Pipeline::Status
.new(project, params[:ref], opts: {
- ignore_skipped: params[:ignore_skipped],
- key_text: params[:key_text],
- key_width: params[:key_width]
- })
+ ignore_skipped: params[:ignore_skipped],
+ key_text: params[:key_text],
+ key_width: params[:key_width]
+ })
render_badge pipeline_status
end
@@ -24,13 +24,13 @@ class Projects::BadgesController < Projects::ApplicationController
def coverage
coverage_report = Gitlab::Ci::Badge::Coverage::Report
.new(project, params[:ref], opts: {
- job: params[:job],
- key_text: params[:key_text],
- key_width: params[:key_width],
- min_good: params[:min_good],
- min_acceptable: params[:min_acceptable],
- min_medium: params[:min_medium]
- })
+ job: params[:job],
+ key_text: params[:key_text],
+ key_width: params[:key_width],
+ min_good: params[:min_good],
+ min_acceptable: params[:min_acceptable],
+ min_medium: params[:min_medium]
+ })
render_badge coverage_report
end
@@ -38,10 +38,10 @@ class Projects::BadgesController < Projects::ApplicationController
def release
latest_release = Gitlab::Ci::Badge::Release::LatestRelease
.new(project, current_user, opts: {
- key_text: params[:key_text],
- key_width: params[:key_width],
- order_by: params[:order_by]
- })
+ key_text: params[:key_text],
+ key_width: params[:key_width],
+ order_by: params[:order_by]
+ })
render_badge latest_release
end
diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb
index 01ed5473b41..cfff281604e 100644
--- a/app/controllers/projects/blame_controller.rb
+++ b/app/controllers/projects/blame_controller.rb
@@ -7,7 +7,7 @@ class Projects::BlameController < Projects::ApplicationController
before_action :require_non_empty_project
before_action :assign_ref_vars
- before_action :authorize_download_code!
+ before_action :authorize_read_code!
feature_category :source_code_management
urgency :low, [:show]
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index f5188e28b81..4eda76f4f21 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -18,7 +18,8 @@ class Projects::BlobController < Projects::ApplicationController
around_action :allow_gitaly_ref_name_caching, only: [:show]
before_action :require_non_empty_project, except: [:new, :create]
- before_action :authorize_download_code!
+ before_action :authorize_download_code!, except: [:show]
+ before_action :authorize_read_code!, only: [:show]
# We need to assign the blob vars before `authorize_edit_tree!` so we can
# validate access to a specific ref.
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index 27969cb1a75..7b01e4db42a 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -6,7 +6,7 @@ class Projects::BranchesController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project, except: :create
- before_action :authorize_download_code!
+ before_action :authorize_read_code!
before_action :authorize_push_code!, only: [:new, :create, :destroy, :destroy_all_merged]
# Support legacy URLs
diff --git a/app/controllers/projects/ci/daily_build_group_report_results_controller.rb b/app/controllers/projects/ci/daily_build_group_report_results_controller.rb
index b2b5e096105..37138afc719 100644
--- a/app/controllers/projects/ci/daily_build_group_report_results_controller.rb
+++ b/app/controllers/projects/ci/daily_build_group_report_results_controller.rb
@@ -25,7 +25,7 @@ class Projects::Ci::DailyBuildGroupReportResultsController < Projects::Applicati
{
date: 'date',
group_name: 'group_name',
- param_type => -> (record) { record.data[param_type] }
+ param_type => ->(record) { record.data[param_type] }
}
).render
end
diff --git a/app/controllers/projects/clusters_controller.rb b/app/controllers/projects/clusters_controller.rb
index 30d001d0ac5..b781365b3c3 100644
--- a/app/controllers/projects/clusters_controller.rb
+++ b/app/controllers/projects/clusters_controller.rb
@@ -5,7 +5,6 @@ class Projects::ClustersController < Clusters::ClustersController
before_action :repository
before_action do
- push_frontend_feature_flag(:prometheus_computed_alerts)
push_frontend_feature_flag(:show_gitlab_agent_feedback, type: :ops)
end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 870320a79d9..583b572d4b1 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -12,7 +12,7 @@ class Projects::CommitController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
- before_action :authorize_download_code!
+ before_action :authorize_read_code!
before_action :authorize_read_pipeline!, only: [:pipelines]
before_action :commit
before_action :define_commit_vars, only: [:show, :diff_for_path, :diff_files, :pipelines, :merge_requests]
diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb
index f4125fd0a15..c006d56ae81 100644
--- a/app/controllers/projects/commits_controller.rb
+++ b/app/controllers/projects/commits_controller.rb
@@ -12,7 +12,7 @@ class Projects::CommitsController < Projects::ApplicationController
around_action :allow_gitaly_ref_name_caching
before_action :require_non_empty_project
before_action :assign_ref_vars, except: :commits_root
- before_action :authorize_download_code!
+ before_action :authorize_read_code!
before_action :validate_ref!, except: :commits_root
before_action :set_commits, except: :commits_root
@@ -28,6 +28,8 @@ class Projects::CommitsController < Projects::ApplicationController
@merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened
.find_by(source_project: @project, source_branch: @ref, target_branch: @repository.root_ref)
+ @ref_type = ref_type
+
respond_to do |format|
format.html
format.atom { render layout: 'xml' }
@@ -73,18 +75,20 @@ class Projects::CommitsController < Projects::ApplicationController
search = permitted_params[:search]
author = permitted_params[:author]
+ # fully_qualified_ref is available in some situations when the use_ref_type_parameter FF is enabled
+ ref = @fully_qualified_ref || @ref
@commits =
if search.present?
- @repository.find_commits_by_message(search, @ref, @path, @limit, @offset)
+ @repository.find_commits_by_message(search, ref, @path, @limit, @offset)
elsif author.present?
- @repository.commits(@ref, author: author, path: @path, limit: @limit, offset: @offset)
+ @repository.commits(ref, author: author, path: @path, limit: @limit, offset: @offset)
else
- @repository.commits(@ref, path: @path, limit: @limit, offset: @offset)
+ @repository.commits(ref, path: @path, limit: @limit, offset: @offset)
end
@commits.each(&:lazy_author) # preload authors
- @commits = @commits.with_markdown_cache.with_latest_pipeline(@ref)
+ @commits = @commits.with_markdown_cache.with_latest_pipeline(ref)
@commits = set_commits_for_rendering(@commits)
end
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index 61308f24412..266edd506d5 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -10,7 +10,7 @@ class Projects::CompareController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
- before_action :authorize_download_code!
+ before_action :authorize_read_code!
# Defining ivars
before_action :define_diffs, only: [:show, :diff_for_path]
before_action :define_environment, only: [:show]
@@ -95,7 +95,7 @@ class Projects::CompareController < Projects::ApplicationController
target_project = target_projects(source_project).find_by_id(compare_params[:from_project_id])
# Just ignore the field if it points at a non-existent or hidden project
- next source_project unless target_project && can?(current_user, :download_code, target_project)
+ next source_project unless target_project && can?(current_user, :read_code, target_project)
target_project
end
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 67f2f85ce65..537fd3854c4 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -14,11 +14,11 @@ class Projects::EnvironmentsController < Projects::ApplicationController
before_action only: [:metrics, :additional_metrics, :metrics_dashboard] do
authorize_metrics_dashboard!
-
- push_frontend_feature_flag(:prometheus_computed_alerts)
- push_frontend_feature_flag(:disable_metric_dashboard_refresh_rate)
end
+ before_action only: [:show] do
+ push_frontend_feature_flag(:environment_details_vue, @project)
+ end
before_action :authorize_read_environment!, except: [:metrics, :additional_metrics, :metrics_dashboard, :metrics_redirect]
before_action :authorize_create_environment!, only: [:new, :create]
before_action :authorize_stop_environment!, only: [:stop]
diff --git a/app/controllers/projects/find_file_controller.rb b/app/controllers/projects/find_file_controller.rb
index c6bc115e737..b5099d555ae 100644
--- a/app/controllers/projects/find_file_controller.rb
+++ b/app/controllers/projects/find_file_controller.rb
@@ -8,7 +8,7 @@ class Projects::FindFileController < Projects::ApplicationController
before_action :require_non_empty_project
before_action :assign_ref_vars
- before_action :authorize_download_code!
+ before_action :authorize_read_code!
feature_category :source_code_management
urgency :low, [:show, :list]
diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb
index 3208a5076e7..ff3dc71b6cc 100644
--- a/app/controllers/projects/forks_controller.rb
+++ b/app/controllers/projects/forks_controller.rb
@@ -9,9 +9,9 @@ class Projects::ForksController < Projects::ApplicationController
# Authorize
before_action :disable_query_limiting, only: [:create]
before_action :require_non_empty_project
- before_action :authorize_download_code!
+ before_action :authorize_read_code!
before_action :authenticate_user!, only: [:new, :create]
- before_action :authorize_fork_project!, only: [:new, :create]
+ before_action :authorize_fork_project!, except: [:index]
before_action :authorize_fork_namespace!, only: [:create]
feature_category :source_code_management
diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb
index 6da70b5e157..d072381933a 100644
--- a/app/controllers/projects/graphs_controller.rb
+++ b/app/controllers/projects/graphs_controller.rb
@@ -21,11 +21,24 @@ class Projects::GraphsController < Projects::ApplicationController
feature_category :continuous_integration, [:ci]
urgency :low, [:ci]
+ MAX_COMMITS = 6000
+
def show
+ @ref_type = ref_type
+
respond_to do |format|
format.html
format.json do
- fetch_graph
+ commits = @project.repository.commits(ref, limit: MAX_COMMITS, skip_merges: true)
+ log = commits.map do |commit|
+ {
+ author_name: commit.author_name,
+ author_email: commit.author_email,
+ date: commit.committed_date.strftime("%Y-%m-%d")
+ }
+ end
+
+ render json: Gitlab::Json.dump(log)
end
end
end
@@ -50,9 +63,13 @@ class Projects::GraphsController < Projects::ApplicationController
private
+ def ref
+ @fully_qualified_ref || @ref
+ end
+
def get_commits
@commits_limit = 2000
- @commits = @project.repository.commits(@ref, limit: @commits_limit, skip_merges: true)
+ @commits = @project.repository.commits(ref, limit: @commits_limit, skip_merges: true)
@commits_graph = Gitlab::Graphs::Commits.new(@commits)
@commits_per_week_days = @commits_graph.commits_per_week_days
@commits_per_time = @commits_graph.commits_per_time
@@ -76,7 +93,7 @@ class Projects::GraphsController < Projects::ApplicationController
base_params: {
start_date: date_today - report_window,
end_date: date_today,
- ref_path: @project.repository.expand_ref(@ref),
+ ref_path: @project.repository.expand_ref(ref),
param_type: 'coverage'
},
download_path: namespace_project_ci_daily_build_group_report_results_path(
@@ -92,21 +109,6 @@ class Projects::GraphsController < Projects::ApplicationController
}
end
- def fetch_graph
- @commits = @project.repository.commits(@ref, limit: 6000, skip_merges: true)
- @log = []
-
- @commits.each do |commit|
- @log << {
- author_name: commit.author_name,
- author_email: commit.author_email,
- date: commit.committed_date.strftime("%Y-%m-%d")
- }
- end
-
- render json: Gitlab::Json.dump(@log)
- end
-
def tracking_namespace_source
project.namespace
end
diff --git a/app/controllers/projects/incidents_controller.rb b/app/controllers/projects/incidents_controller.rb
index 599505dcb6d..3842a88d15b 100644
--- a/app/controllers/projects/incidents_controller.rb
+++ b/app/controllers/projects/incidents_controller.rb
@@ -8,6 +8,7 @@ class Projects::IncidentsController < Projects::ApplicationController
before_action :load_incident, only: [:show]
before_action do
push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?)
+ push_force_frontend_feature_flag(:work_items_mvc, @project&.work_items_mvc_feature_flag_enabled?)
push_force_frontend_feature_flag(:work_items_mvc_2, @project&.work_items_mvc_2_feature_flag_enabled?)
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index ee845cd001e..631e697dd2f 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -7,6 +7,7 @@ class Projects::IssuesController < Projects::ApplicationController
include IssuableCollections
include IssuesCalendar
include RecordUserLastActivity
+ include ::Observability::ContentSecurityPolicy
ISSUES_EXCEPT_ACTIONS = %i[index calendar new create bulk_update import_csv export_csv service_desk].freeze
SET_ISSUABLES_INDEX_ONLY_ACTIONS = %i[index calendar service_desk].freeze
@@ -19,7 +20,7 @@ class Projects::IssuesController < Projects::ApplicationController
before_action :disable_query_limiting, only: [:create_merge_request, :move, :bulk_update]
before_action :check_issues_available!
before_action :issue, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) }
- before_action :redirect_if_task, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) }
+ before_action :redirect_if_work_item, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) }
after_action :log_issue_show, only: :show
@@ -37,7 +38,7 @@ class Projects::IssuesController < Projects::ApplicationController
before_action :authorize_create_merge_request_from!, only: [:create_merge_request]
before_action :authorize_import_issues!, only: [:import_csv]
- before_action :authorize_download_code!, only: [:related_branches]
+ before_action :authorize_read_code!, only: [:related_branches]
before_action do
push_frontend_feature_flag(:preserve_unchanged_markdown, project)
@@ -55,8 +56,10 @@ class Projects::IssuesController < Projects::ApplicationController
before_action only: :show do
push_frontend_feature_flag(:issue_assignees_widget, project)
push_frontend_feature_flag(:work_items_mvc, project&.group)
+ push_force_frontend_feature_flag(:work_items_mvc, project&.work_items_mvc_feature_flag_enabled?)
push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?)
push_frontend_feature_flag(:epic_widget_edit_confirmation, project)
+ push_frontend_feature_flag(:use_iid_in_work_items_path, project)
push_force_frontend_feature_flag(:work_items_create_from_markdown, project&.work_items_create_from_markdown_feature_flag_enabled?)
end
@@ -432,8 +435,8 @@ class Projects::IssuesController < Projects::ApplicationController
# Overridden in EE
def create_vulnerability_issue_feedback(issue); end
- def redirect_if_task
- return unless issue.task?
+ def redirect_if_work_item
+ return unless allowed_work_item?
if Feature.enabled?(:use_iid_in_work_items_path, project.group)
redirect_to project_work_items_path(project, issue.iid, params: request.query_parameters.merge(iid_path: true))
@@ -441,6 +444,10 @@ class Projects::IssuesController < Projects::ApplicationController
redirect_to project_work_items_path(project, issue.id, params: request.query_parameters)
end
end
+
+ def allowed_work_item?
+ issue.task?
+ end
end
Projects::IssuesController.prepend_mod_with('Projects::IssuesController')
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index 557ac566733..c6d442a6f27 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -20,9 +20,6 @@ class Projects::JobsController < Projects::ApplicationController
before_action :verify_proxy_request!, only: :proxy_websocket_authorize
before_action :push_job_log_jump_to_failures, only: [:show]
before_action :reject_if_build_artifacts_size_refreshing!, only: [:erase]
- before_action do
- push_frontend_feature_flag(:graphql_job_app, project, type: :development)
- end
layout 'project'
diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb
index 93e2298ca98..cba0056ccd5 100644
--- a/app/controllers/projects/merge_requests/creations_controller.rb
+++ b/app/controllers/projects/merge_requests/creations_controller.rb
@@ -4,6 +4,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
include DiffForPath
include DiffHelper
include RendersCommits
+ include ::Observability::ContentSecurityPolicy
skip_before_action :merge_request
before_action :authorize_create_merge_request_from!
@@ -19,6 +20,10 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
:branch_to
]
+ before_action do
+ push_frontend_feature_flag(:mr_compare_dropdowns, project)
+ end
+
def new
define_new_vars
end
@@ -89,6 +94,14 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
render layout: false
end
+ def target_projects
+ projects = MergeRequestTargetProjectFinder
+ .new(current_user: current_user, source_project: @project, project_feature: :repository)
+ .execute(include_routes: true).limit(20).search(params[:search])
+
+ render json: ProjectSerializer.new.represent(projects)
+ end
+
private
def build_merge_request
diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb
index c88dbc70ed5..83377f67723 100644
--- a/app/controllers/projects/merge_requests/diffs_controller.rb
+++ b/app/controllers/projects/merge_requests/diffs_controller.rb
@@ -60,17 +60,11 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
options[:merge_conflicts_in_diff]
]
- if Feature.enabled?(:check_etags_diffs_batch_before_write_cache, merge_request.project) && !stale?(etag: [cache_context + diff_options_hash.fetch(:paths, []), diffs])
- return
- end
+ return unless stale?(etag: [cache_context + diff_options_hash.fetch(:paths, []), diffs])
diffs.unfold_diff_files(unfoldable_positions)
diffs.write_cache
- if Feature.disabled?(:check_etags_diffs_batch_before_write_cache, merge_request.project) && !stale?(etag: [cache_context + diff_options_hash.fetch(:paths, []), diffs])
- return
- end
-
render json: PaginatedDiffSerializer.new(current_user: current_user).represent(diffs, options)
end
# rubocop: enable Metrics/AbcSize
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 4ba79d43f27..3ab1f7d1d32 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -11,10 +11,11 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
include SourcegraphDecorator
include DiffHelper
include Gitlab::Cache::Helpers
+ include ::Observability::ContentSecurityPolicy
prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) }
skip_before_action :merge_request, only: [:index, :bulk_update, :export_csv]
- before_action :apply_diff_view_cookie!, only: [:show]
+ before_action :apply_diff_view_cookie!, only: [:show, :diffs]
before_action :disable_query_limiting, only: [:assign_related_issues, :update]
before_action :authorize_update_issuable!, only: [:close, :edit, :update, :remove_wip, :sort]
before_action :authorize_read_actual_head_pipeline!, only: [
@@ -30,7 +31,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
before_action :authenticate_user!, only: [:assign_related_issues]
before_action :check_user_can_push_to_source_branch!, only: [:rebase]
- before_action only: [:show] do
+ before_action only: [:show, :diffs] do
push_frontend_feature_flag(:core_security_mr_widget_counts, project)
push_frontend_feature_flag(:issue_assignees_widget, @project)
push_frontend_feature_flag(:refactor_security_extension, @project)
@@ -40,21 +41,22 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:mr_review_submit_comment, project)
push_frontend_feature_flag(:mr_experience_survey, project)
push_frontend_feature_flag(:realtime_reviewers, project)
+ push_frontend_feature_flag(:realtime_mr_status_change, project)
end
before_action do
push_frontend_feature_flag(:permit_all_shared_groups_for_approval, @project)
end
- around_action :allow_gitaly_ref_name_caching, only: [:index, :show, :discussions]
+ around_action :allow_gitaly_ref_name_caching, only: [:index, :show, :diffs, :discussions]
- after_action :log_merge_request_show, only: [:show]
+ after_action :log_merge_request_show, only: [:show, :diffs]
feature_category :code_review, [
:assign_related_issues, :bulk_update, :cancel_auto_merge,
:commit_change_content, :commits, :context_commits, :destroy,
:discussions, :edit, :index, :merge, :rebase, :remove_wip,
- :show, :toggle_award_emoji, :toggle_subscription, :update
+ :show, :diffs, :toggle_award_emoji, :toggle_subscription, :update
]
feature_category :code_testing, [:test_reports, :coverage_reports]
@@ -67,6 +69,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
urgency :low, [
:index,
:show,
+ :diffs,
:commits,
:bulk_update,
:edit,
@@ -100,74 +103,13 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
end
- # rubocop:disable Metrics/AbcSize
def show
- close_merge_request_if_no_source_project
- @merge_request.check_mergeability(async: true)
-
- respond_to do |format|
- format.html do
- # use next to appease Rubocop
- next render('invalid') if target_branch_missing?
-
- preload_assignees_for_render(@merge_request)
-
- # Build a note object for comment form
- @note = @project.notes.new(noteable: @merge_request)
-
- @noteable = @merge_request
- @commits_count = @merge_request.commits_count + @merge_request.context_commits_count
- @diffs_count = get_diffs_count
- @issuable_sidebar = serializer.represent(@merge_request, serializer: 'sidebar')
- @current_user_data = Gitlab::Json.dump(UserSerializer.new(project: @project).represent(current_user, {}, MergeRequestCurrentUserEntity))
- @show_whitespace_default = current_user.nil? || current_user.show_whitespace_in_diffs
- @file_by_file_default = current_user&.view_diffs_file_by_file
- @coverage_path = coverage_reports_project_merge_request_path(@project, @merge_request, format: :json) if @merge_request.has_coverage_reports?
- @update_current_user_path = expose_path(api_v4_user_preferences_path)
- @endpoint_metadata_url = endpoint_metadata_url(@project, @merge_request)
- @endpoint_diff_batch_url = endpoint_diff_batch_url(@project, @merge_request)
-
- set_pipeline_variables
-
- @number_of_pipelines = @pipelines.size
-
- render
- end
-
- format.json do
- Gitlab::PollingInterval.set_header(response, interval: 10_000)
-
- if params[:serializer] == 'sidebar_extras'
- cache_context = [
- params[:serializer],
- current_user&.cache_key,
- @merge_request.merge_request_assignees.map(&:cache_key),
- @merge_request.merge_request_reviewers.map(&:cache_key)
- ]
-
- render_cached(@merge_request,
- with: serializer,
- cache_context: -> (_) { [Digest::SHA256.hexdigest(cache_context.to_s)] },
- serializer: params[:serializer])
- else
- render json: serializer.represent(@merge_request, serializer: params[:serializer])
- end
- end
-
- format.patch do
- break render_404 unless @merge_request.diff_refs
-
- send_git_patch @project.repository, @merge_request.diff_refs
- end
-
- format.diff do
- break render_404 unless @merge_request.diff_refs
+ show_merge_request
+ end
- send_git_diff @project.repository, @merge_request.diff_refs
- end
- end
+ def diffs
+ show_merge_request
end
- # rubocop:enable Metrics/AbcSize
def commits
# Get context commits from repository
@@ -412,6 +354,77 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
private
+ def show_merge_request
+ close_merge_request_if_no_source_project
+ @merge_request.check_mergeability(async: true)
+
+ respond_to do |format|
+ format.html do
+ # use next to appease Rubocop
+ next render('invalid') if target_branch_missing?
+
+ render_html_page
+ end
+
+ format.json do
+ Gitlab::PollingInterval.set_header(response, interval: 10_000)
+
+ if params[:serializer] == 'sidebar_extras'
+ cache_context = [
+ params[:serializer],
+ current_user&.cache_key,
+ @merge_request.merge_request_assignees.map(&:cache_key),
+ @merge_request.merge_request_reviewers.map(&:cache_key)
+ ]
+
+ render_cached(@merge_request,
+ with: serializer,
+ cache_context: ->(_) { [Digest::SHA256.hexdigest(cache_context.to_s)] },
+ serializer: params[:serializer])
+ else
+ render json: serializer.represent(@merge_request, serializer: params[:serializer])
+ end
+ end
+
+ format.patch do
+ break render_404 unless @merge_request.diff_refs
+
+ send_git_patch @project.repository, @merge_request.diff_refs
+ end
+
+ format.diff do
+ break render_404 unless @merge_request.diff_refs
+
+ send_git_diff @project.repository, @merge_request.diff_refs
+ end
+ end
+ end
+
+ def render_html_page
+ preload_assignees_for_render(@merge_request)
+
+ # Build a note object for comment form
+ @note = @project.notes.new(noteable: @merge_request)
+
+ @noteable = @merge_request
+ @commits_count = @merge_request.commits_count + @merge_request.context_commits_count
+ @diffs_count = get_diffs_count
+ @issuable_sidebar = serializer.represent(@merge_request, serializer: 'sidebar')
+ @current_user_data = Gitlab::Json.dump(UserSerializer.new(project: @project).represent(current_user, {}, MergeRequestCurrentUserEntity))
+ @show_whitespace_default = current_user.nil? || current_user.show_whitespace_in_diffs
+ @file_by_file_default = current_user&.view_diffs_file_by_file
+ @coverage_path = coverage_reports_project_merge_request_path(@project, @merge_request, format: :json) if @merge_request.has_coverage_reports?
+ @update_current_user_path = expose_path(api_v4_user_preferences_path)
+ @endpoint_metadata_url = endpoint_metadata_url(@project, @merge_request)
+ @endpoint_diff_batch_url = endpoint_diff_batch_url(@project, @merge_request)
+
+ set_pipeline_variables
+
+ @number_of_pipelines = @pipelines.size
+
+ render
+ end
+
def get_diffs_count
if show_only_context_commits?
@merge_request.context_commits_diff.raw_diffs.size
diff --git a/app/controllers/projects/metrics_dashboard_controller.rb b/app/controllers/projects/metrics_dashboard_controller.rb
index b78ee6ca917..08757d11912 100644
--- a/app/controllers/projects/metrics_dashboard_controller.rb
+++ b/app/controllers/projects/metrics_dashboard_controller.rb
@@ -9,10 +9,6 @@ module Projects
include Gitlab::Utils::StrongMemoize
before_action :authorize_metrics_dashboard!
- before_action do
- push_frontend_feature_flag(:prometheus_computed_alerts)
- push_frontend_feature_flag(:disable_metric_dashboard_refresh_rate)
- end
feature_category :metrics
urgency :low
diff --git a/app/controllers/projects/ml/candidates_controller.rb b/app/controllers/projects/ml/candidates_controller.rb
new file mode 100644
index 00000000000..b702edb858e
--- /dev/null
+++ b/app/controllers/projects/ml/candidates_controller.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Projects
+ module Ml
+ class CandidatesController < ApplicationController
+ before_action :check_feature_flag
+
+ feature_category :mlops
+
+ def show
+ @candidate = ::Ml::Candidate.with_project_id_and_iid(@project.id, params['iid'])
+
+ render_404 unless @candidate.present?
+ end
+
+ private
+
+ def check_feature_flag
+ render_404 unless Feature.enabled?(:ml_experiment_tracking, @project)
+ end
+ end
+ end
+end
diff --git a/app/controllers/projects/ml/experiments_controller.rb b/app/controllers/projects/ml/experiments_controller.rb
index 749586791ac..c82a959d612 100644
--- a/app/controllers/projects/ml/experiments_controller.rb
+++ b/app/controllers/projects/ml/experiments_controller.rb
@@ -3,7 +3,6 @@
module Projects
module Ml
class ExperimentsController < ::Projects::ApplicationController
- include Projects::Ml::ExperimentsHelper
before_action :check_feature_flag
feature_category :mlops
diff --git a/app/controllers/projects/network_controller.rb b/app/controllers/projects/network_controller.rb
index 84ac9fb01fd..aa0838752e2 100644
--- a/app/controllers/projects/network_controller.rb
+++ b/app/controllers/projects/network_controller.rb
@@ -6,7 +6,7 @@ class Projects::NetworkController < Projects::ApplicationController
before_action :require_non_empty_project
before_action :assign_ref_vars
- before_action :authorize_download_code!
+ before_action :authorize_read_code!
before_action :assign_options
before_action :assign_commit
@@ -14,7 +14,13 @@ class Projects::NetworkController < Projects::ApplicationController
urgency :low, [:show]
def show
- @url = project_network_path(@project, @ref, @options.merge(format: :json))
+ @url = if Feature.enabled?(:use_ref_type_parameter, @project)
+ project_network_path(@project, @ref, @options.merge(format: :json, ref_type: ref_type))
+ else
+ project_network_path(@project, @ref, @options.merge(format: :json))
+ end
+
+ @ref_type = ref_type
@commit_url = project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s")
respond_to do |format|
diff --git a/app/controllers/projects/performance_monitoring/dashboards_controller.rb b/app/controllers/projects/performance_monitoring/dashboards_controller.rb
index 8acbc17aef3..d043f8d0b9f 100644
--- a/app/controllers/projects/performance_monitoring/dashboards_controller.rb
+++ b/app/controllers/projects/performance_monitoring/dashboards_controller.rb
@@ -70,7 +70,7 @@ module Projects
end
def validate_required_params!
- params.require(%i(branch file_name dashboard commit_message))
+ params.require(%i[branch file_name dashboard commit_message])
end
def redirect_safe_branch_name
@@ -78,7 +78,7 @@ module Projects
end
def dashboard_params
- params.permit(%i(branch file_name dashboard commit_message)).to_h
+ params.permit(%i[branch file_name dashboard commit_message]).to_h
end
def file_content_params
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index 7d1a75ae449..db77127cb0a 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -24,11 +24,6 @@ class Projects::PipelinesController < Projects::ApplicationController
before_action :ensure_pipeline, only: [:show, :downloadable_artifacts]
before_action :reject_if_build_artifacts_size_refreshing!, only: [:destroy]
- before_action do
- push_frontend_feature_flag(:pipeline_tabs_vue, @project)
- push_frontend_feature_flag(:run_pipeline_graphql, @project)
- end
-
# Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/225596
before_action :redirect_for_legacy_scope_filter, only: [:index], if: -> { request.format.html? }
diff --git a/app/controllers/projects/protected_branches_controller.rb b/app/controllers/projects/protected_branches_controller.rb
index 8c70ef446a2..baa4607dcb6 100644
--- a/app/controllers/projects/protected_branches_controller.rb
+++ b/app/controllers/projects/protected_branches_controller.rb
@@ -1,6 +1,12 @@
# frozen_string_literal: true
class Projects::ProtectedBranchesController < Projects::ProtectedRefsController
+ def show
+ super
+
+ render 'protected_branches/show'
+ end
+
protected
def project_refs
diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb
index 9707b70f26f..924de0ee7ea 100644
--- a/app/controllers/projects/raw_controller.rb
+++ b/app/controllers/projects/raw_controller.rb
@@ -12,7 +12,7 @@ class Projects::RawController < Projects::ApplicationController
before_action :set_ref_and_path
before_action :require_non_empty_project
- before_action :authorize_download_code!
+ before_action :authorize_read_code!
before_action :check_show_rate_limit!, only: [:show], unless: :external_storage_request?
before_action :redirect_to_external_storage, only: :show, if: :static_objects_external_storage_enabled?
@@ -21,7 +21,7 @@ class Projects::RawController < Projects::ApplicationController
def show
@blob = @repository.blob_at(@ref, @path, limit: Gitlab::Git::Blob::LFS_POINTER_MAX_SIZE)
- send_blob(@repository, @blob, inline: (params[:inline] != 'false'), allow_caching: Guest.can?(:download_code, @project))
+ send_blob(@repository, @blob, inline: (params[:inline] != 'false'), allow_caching: Guest.can?(:read_code, @project))
end
private
diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb
index 72af3280a39..8ac6d872aae 100644
--- a/app/controllers/projects/refs_controller.rb
+++ b/app/controllers/projects/refs_controller.rb
@@ -9,7 +9,7 @@ class Projects::RefsController < Projects::ApplicationController
before_action :require_non_empty_project
before_action :validate_ref_id
before_action :assign_ref_vars
- before_action :authorize_download_code!
+ before_action :authorize_read_code!
feature_category :source_code_management
urgency :low, [:switch, :logs_tree]
@@ -24,9 +24,17 @@ class Projects::RefsController < Projects::ApplicationController
when "blob"
project_blob_path(@project, @id)
when "graph"
- project_network_path(@project, @id, @options)
+ if Feature.enabled?(:use_ref_type_parameter, @project)
+ project_network_path(@project, @id, ref_type: ref_type)
+ else
+ project_network_path(@project, @id, @options)
+ end
when "graphs"
- project_graph_path(@project, @id)
+ if Feature.enabled?(:use_ref_type_parameter, @project)
+ project_graph_path(@project, @id, ref_type: ref_type)
+ else
+ project_graph_path(@project, @id)
+ end
when "find_file"
project_find_file_path(@project, @id)
when "graphs_commits"
@@ -34,7 +42,11 @@ class Projects::RefsController < Projects::ApplicationController
when "badges"
project_settings_ci_cd_path(@project, ref: @id)
else
- project_commits_path(@project, @id)
+ if Feature.enabled?(:use_ref_type_parameter, @project)
+ project_commits_path(@project, @id, ref_type: ref_type)
+ else
+ project_commits_path(@project, @id)
+ end
end
redirect_to new_path
diff --git a/app/controllers/projects/registry/repositories_controller.rb b/app/controllers/projects/registry/repositories_controller.rb
index ffe95bf4fee..6c663c4694a 100644
--- a/app/controllers/projects/registry/repositories_controller.rb
+++ b/app/controllers/projects/registry/repositories_controller.rb
@@ -23,10 +23,6 @@ module Projects
def destroy
image.delete_scheduled!
- unless Feature.enabled?(:container_registry_delete_repository_with_cron_worker)
- DeleteContainerRepositoryWorker.perform_async(current_user.id, image.id) # rubocop:disable CodeReuse/Worker
- end
-
track_package_event(:delete_repository, :container)
respond_to do |format|
diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb
index 5946c43b134..6f896244acb 100644
--- a/app/controllers/projects/runner_projects_controller.rb
+++ b/app/controllers/projects/runner_projects_controller.rb
@@ -11,7 +11,7 @@ class Projects::RunnerProjectsController < Projects::ApplicationController
def create
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
- return head(403) unless can?(current_user, :assign_runner, @runner)
+ return head(:forbidden) unless can?(current_user, :assign_runner, @runner)
path = project_runners_path(project)
diff --git a/app/controllers/projects/service_desk_controller.rb b/app/controllers/projects/service_desk_controller.rb
index aa0e70121df..8f576b8d72b 100644
--- a/app/controllers/projects/service_desk_controller.rb
+++ b/app/controllers/projects/service_desk_controller.rb
@@ -29,7 +29,7 @@ class Projects::ServiceDeskController < Projects::ApplicationController
end
def allowed_update_attributes
- %i(issue_template_key outgoing_name project_key)
+ %i[issue_template_key outgoing_name project_key]
end
def service_desk_attributes
diff --git a/app/controllers/projects/service_ping_controller.rb b/app/controllers/projects/service_ping_controller.rb
index 43c249afd8e..cfc322b47e7 100644
--- a/app/controllers/projects/service_ping_controller.rb
+++ b/app/controllers/projects/service_ping_controller.rb
@@ -10,7 +10,7 @@ class Projects::ServicePingController < Projects::ApplicationController
Gitlab::UsageDataCounters::WebIdeCounter.increment_previews_count
- head(200)
+ head(:ok)
end
def web_ide_clientside_preview_success
@@ -20,12 +20,12 @@ class Projects::ServicePingController < Projects::ApplicationController
Gitlab::UsageDataCounters::EditorUniqueCounter.track_live_preview_edit_action(author: current_user,
project: project)
- head(200)
+ head(:ok)
end
def web_ide_pipelines_count
Gitlab::UsageDataCounters::WebIdeCounter.increment_pipelines_count
- head(200)
+ head(:ok)
end
end
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index 8aef1c3d24d..cf07de4dc29 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -18,6 +18,9 @@ module Projects
urgency :low
def show
+ @entity = :project
+ @variable_limit = ::Plan.default.actual_limits.project_ci_variables
+
if Feature.enabled?(:ci_pipeline_triggers_settings_vue_ui, @project)
triggers = ::Ci::TriggerSerializer.new.represent(
@project.triggers, current_user: current_user, project: @project
@@ -122,11 +125,13 @@ module Projects
.page(params[:specific_page]).per(NUMBER_OF_RUNNERS_PER_PAGE)
.with_tags
- @shared_runners = ::Ci::Runner.instance_type.active.with_tags
-
- @shared_runners_count = @shared_runners.count(:all)
+ active_shared_runners = ::Ci::Runner.instance_type.active
+ @shared_runners_count = active_shared_runners.count
+ @shared_runners = active_shared_runners.page(params[:shared_runners_page]).per(NUMBER_OF_RUNNERS_PER_PAGE).with_tags
- @group_runners = ::Ci::Runner.belonging_to_parent_group_of_project(@project.id).with_tags
+ parent_group_runners = ::Ci::Runner.belonging_to_parent_group_of_project(@project.id)
+ @group_runners_count = parent_group_runners.count
+ @group_runners = parent_group_runners.page(params[:group_runners_page]).per(NUMBER_OF_RUNNERS_PER_PAGE).with_tags
end
def define_ci_variables
diff --git a/app/controllers/projects/settings/integrations_controller.rb b/app/controllers/projects/settings/integrations_controller.rb
index 2bbcd9fe20c..16c1373df2b 100644
--- a/app/controllers/projects/settings/integrations_controller.rb
+++ b/app/controllers/projects/settings/integrations_controller.rb
@@ -79,7 +79,7 @@ module Projects
return {
error: true,
message: _('Validations failed.'),
- service_response: integration.errors.full_messages.join(','),
+ service_response: integration.errors.full_messages.join(', '),
test_failed: false
}
end
@@ -90,7 +90,7 @@ module Projects
return {
error: true,
message: s_('Integrations|Connection failed. Check your integration settings.'),
- service_response: result[:message].to_s,
+ service_response: result[:result].to_s,
test_failed: true
}
end
diff --git a/app/controllers/projects/settings/repository_controller.rb b/app/controllers/projects/settings/repository_controller.rb
index 90988645d3a..6d099aa8b3d 100644
--- a/app/controllers/projects/settings/repository_controller.rb
+++ b/app/controllers/projects/settings/repository_controller.rb
@@ -95,6 +95,14 @@ module Projects
@protected_tags_count = @protected_tags.reduce(0) { |sum, tag| sum + tag.matching(@project.repository.tag_names).size }
+ if Feature.enabled?(:group_protected_branches)
+ @protected_group_branches = if @project.root_namespace.is_a?(Group)
+ @project.root_namespace.protected_branches.order(:name).page(params[:page])
+ else
+ []
+ end
+ end
+
load_gon_index
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/controllers/projects/snippets/application_controller.rb b/app/controllers/projects/snippets/application_controller.rb
index 8ee12bf3795..b8faf464531 100644
--- a/app/controllers/projects/snippets/application_controller.rb
+++ b/app/controllers/projects/snippets/application_controller.rb
@@ -4,7 +4,7 @@ class Projects::Snippets::ApplicationController < Projects::ApplicationControlle
include FindSnippet
include SnippetAuthorizations
- feature_category :snippets
+ feature_category :source_code_management
private
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index 847b1baca10..3c1735c728c 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -7,7 +7,7 @@ class Projects::TagsController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
- before_action :authorize_download_code!
+ before_action :authorize_read_code!
before_action :authorize_admin_tag!, only: [:new, :create, :destroy]
feature_category :source_code_management
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index fea2689db14..737a6290431 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -13,11 +13,10 @@ class Projects::TreeController < Projects::ApplicationController
before_action :require_non_empty_project, except: [:new, :create]
before_action :assign_ref_vars
before_action :assign_dir_vars, only: [:create_dir]
- before_action :authorize_download_code!
+ before_action :authorize_read_code!
before_action :authorize_edit_tree!, only: [:create_dir]
before_action do
- push_frontend_feature_flag(:lazy_load_commits, @project)
push_frontend_feature_flag(:highlight_js, @project)
push_frontend_feature_flag(:file_line_blame, @project)
push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks)
diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb
index a8f062bd7c1..a83ccccbeae 100644
--- a/app/controllers/projects/variables_controller.rb
+++ b/app/controllers/projects/variables_controller.rb
@@ -47,6 +47,6 @@ class Projects::VariablesController < Projects::ApplicationController
end
def variable_params_attributes
- %i[id variable_type key secret_value protected masked environment_scope _destroy]
+ %i[id variable_type key secret_value protected masked raw environment_scope _destroy]
end
end
diff --git a/app/controllers/projects/work_items_controller.rb b/app/controllers/projects/work_items_controller.rb
index a7e59a28fb7..a118c6986f7 100644
--- a/app/controllers/projects/work_items_controller.rb
+++ b/app/controllers/projects/work_items_controller.rb
@@ -3,6 +3,7 @@
class Projects::WorkItemsController < Projects::ApplicationController
before_action do
push_force_frontend_feature_flag(:work_items, project&.work_items_feature_flag_enabled?)
+ push_force_frontend_feature_flag(:work_items_mvc, project&.work_items_mvc_feature_flag_enabled?)
push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?)
push_frontend_feature_flag(:use_iid_in_work_items_path, project)
end
@@ -10,3 +11,5 @@ class Projects::WorkItemsController < Projects::ApplicationController
feature_category :team_planning
urgency :low
end
+
+Projects::WorkItemsController.prepend_mod
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index a5dacbf7f2f..886819fe778 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -26,7 +26,7 @@ class ProjectsController < Projects::ApplicationController
before_action :verify_git_import_enabled, only: [:create]
before_action :project_export_enabled, only: [:export, :download_export, :remove_export, :generate_new_export]
before_action :present_project, only: [:edit]
- before_action :authorize_download_code!, only: [:refs]
+ before_action :authorize_read_code!, only: [:refs]
# Authorize
before_action :authorize_admin_project!, only: [:edit, :update, :housekeeping, :download_export, :export, :remove_export, :generate_new_export]
@@ -37,21 +37,17 @@ class ProjectsController < Projects::ApplicationController
before_action :check_export_rate_limit!, only: [:export, :download_export, :generate_new_export]
before_action do
- push_frontend_feature_flag(:lazy_load_commits, @project)
push_frontend_feature_flag(:highlight_js, @project)
push_frontend_feature_flag(:file_line_blame, @project)
push_frontend_feature_flag(:increase_page_size_exponentially, @project)
push_licensed_feature(:file_locks) if @project.present? && @project.licensed_feature_available?(:file_locks)
push_licensed_feature(:security_orchestration_policies) if @project.present? && @project.licensed_feature_available?(:security_orchestration_policies)
push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?)
+ push_force_frontend_feature_flag(:work_items_mvc, @project&.work_items_mvc_feature_flag_enabled?)
push_force_frontend_feature_flag(:work_items_mvc_2, @project&.work_items_mvc_2_feature_flag_enabled?)
push_frontend_feature_flag(:package_registry_access_level)
end
- before_action only: :edit do
- push_frontend_feature_flag(:split_operations_visibility_permissions, @project)
- end
-
layout :determine_layout
feature_category :projects, [
@@ -369,7 +365,7 @@ class ProjectsController < Projects::ApplicationController
def render_landing_page
Gitlab::Tracking.event('project_overview', 'render', user: current_user, project: @project.project)
- if can?(current_user, :download_code, @project)
+ if can?(current_user, :read_code, @project)
return render 'projects/no_repo' unless @project.repository_exists?
render 'projects/empty' if @project.empty_repo?
@@ -433,17 +429,11 @@ class ProjectsController < Projects::ApplicationController
security_and_compliance_access_level
container_registry_access_level
releases_access_level
- ] + operations_feature_attributes
- end
-
- def operations_feature_attributes
- if Feature.enabled?(:split_operations_visibility_permissions, project)
- %i[
- environments_access_level feature_flags_access_level monitor_access_level infrastructure_access_level
- ]
- else
- %i[operations_access_level]
- end
+ environments_access_level
+ feature_flags_access_level
+ monitor_access_level
+ infrastructure_access_level
+ ]
end
def project_setting_attributes
@@ -520,14 +510,6 @@ class ProjectsController < Projects::ApplicationController
false
end
- def project_view_files?
- if current_user
- current_user.project_view == 'files'
- else
- project_view_files_allowed?
- end
- end
-
# Override extract_ref from ExtractsPath, which returns the branch and file path
# for the blob/tree, which in this case is just the root of the default branch.
# This way we avoid to access the repository.ref_names.
@@ -540,10 +522,6 @@ class ProjectsController < Projects::ApplicationController
project.repository.root_ref
end
- def project_view_files_allowed?
- !project.empty_repo? && can?(current_user, :download_code, project)
- end
-
def build_canonical_path(project)
params[:namespace_id] = project.namespace.to_param
params[:id] = project.to_param
diff --git a/app/controllers/registrations/welcome_controller.rb b/app/controllers/registrations/welcome_controller.rb
index a49b82319da..4a42632a980 100644
--- a/app/controllers/registrations/welcome_controller.rb
+++ b/app/controllers/registrations/welcome_controller.rb
@@ -14,12 +14,16 @@ module Registrations
def show
return redirect_to path_for_signed_in_user(current_user) if completed_welcome_step?
+
+ track_event('render')
end
def update
result = ::Users::SignupService.new(current_user, update_params).execute
if result[:status] == :success
+ track_event('successfully_submitted_form')
+
return redirect_to issues_dashboard_path(assignee_username: current_user.username) if show_tasks_to_be_done?
return redirect_to update_success_path if show_signup_onboarding?
@@ -86,6 +90,10 @@ module Registrations
# overridden in EE
def update_success_path
end
+
+ # overridden in EE
+ def track_event(category)
+ end
end
end
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index 995303a631a..11f9f1cf0c6 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -15,10 +15,10 @@ class RegistrationsController < Devise::RegistrationsController
layout 'devise'
prepend_before_action :check_captcha, only: :create
+ before_action :ensure_first_name_and_last_name_not_empty, only: :create
before_action :ensure_destroy_prerequisites_met, only: [:destroy]
before_action :init_preferred_language, only: :new
before_action :load_recaptcha, only: :new
- before_action :set_invite_params, only: :new
before_action only: [:create] do
check_rate_limit!(:user_sign_up, scope: request.ip)
end
@@ -32,11 +32,11 @@ class RegistrationsController < Devise::RegistrationsController
def new
@resource = build_resource
+ set_invite_params
end
def create
- set_user_state
- set_custom_confirmation_token
+ set_resource_fields
super do |new_user|
accept_pending_invitations if new_user.persisted?
@@ -111,8 +111,11 @@ class RegistrationsController < Devise::RegistrationsController
super
end
+ # overridden by EE module
def after_request_hook(user)
- # overridden by EE module
+ return unless user.persisted?
+
+ Gitlab::Tracking.event(self.class.name, 'successfully_submitted_form', user: user)
end
def after_sign_up_path_for(user)
@@ -132,6 +135,7 @@ class RegistrationsController < Devise::RegistrationsController
return identity_verification_redirect_path if custom_confirmation_enabled?
+ Gitlab::Tracking.event(self.class.name, 'render', user: resource)
users_almost_there_path(email: resource.email)
end
@@ -172,6 +176,21 @@ class RegistrationsController < Devise::RegistrationsController
render action: 'new'
end
+ def ensure_first_name_and_last_name_not_empty
+ # The key here will be affected by feature flag 'arkose_labs_signup_challenge'
+ # When flag is disabled, the key will be 'user' because #check_captcha will remove 'new_' prefix
+ # When flag is enabled, #check_captcha will be skipped, so the key will have 'new_' prefix
+ first_name = params.dig(resource_name, :first_name) || params.dig("new_#{resource_name}", :first_name)
+ last_name = params.dig(resource_name, :last_name) || params.dig("new_#{resource_name}", :last_name)
+
+ return if first_name.present? && last_name.present?
+
+ resource.errors.add(_('First name'), _("cannot be blank")) if first_name.blank?
+ resource.errors.add(_('Last name'), _("cannot be blank")) if last_name.blank?
+
+ render action: 'new'
+ end
+
def pending_approval?
return false unless Gitlab::CurrentSettings.require_admin_approval_after_user_signup
@@ -211,18 +230,22 @@ class RegistrationsController < Devise::RegistrationsController
Gitlab::Recaptcha.load_configurations!
end
- def set_user_state
+ # overridden by EE module
+ def set_resource_fields
return unless set_blocked_pending_approval?
resource.state = User::BLOCKED_PENDING_APPROVAL_STATE
end
+ # overridden by EE module
def set_blocked_pending_approval?
Gitlab::CurrentSettings.require_admin_approval_after_user_signup
end
def set_invite_params
- @invite_email = ActionController::Base.helpers.sanitize(params[:invite_email])
+ if resource.email.blank? && params[:invite_email].present?
+ resource.email = @invite_email = ActionController::Base.helpers.sanitize(params[:invite_email])
+ end
end
def after_pending_invitations_hook
@@ -251,10 +274,6 @@ class RegistrationsController < Devise::RegistrationsController
# overridden by EE module
end
- def set_custom_confirmation_token
- # overridden by EE module
- end
-
def send_custom_confirmation_instructions
# overridden by EE module
end
diff --git a/app/controllers/repositories/lfs_locks_api_controller.rb b/app/controllers/repositories/lfs_locks_api_controller.rb
index f36126d67ff..ea858d63236 100644
--- a/app/controllers/repositories/lfs_locks_api_controller.rb
+++ b/app/controllers/repositories/lfs_locks_api_controller.rb
@@ -54,9 +54,9 @@ module Repositories
def error_payload(message, custom_attrs = {})
custom_attrs.merge({
- message: message,
- documentation_url: help_url
- })
+ message: message,
+ documentation_url: help_url
+ })
end
def split_by_owner(locks)
@@ -72,7 +72,7 @@ module Repositories
end
def upload_request?
- %w(create unlock verify).include?(params[:action])
+ %w[create unlock verify].include?(params[:action])
end
def lfs_params
diff --git a/app/controllers/repositories/lfs_storage_controller.rb b/app/controllers/repositories/lfs_storage_controller.rb
index d54b51b463a..22f1a81b95b 100644
--- a/app/controllers/repositories/lfs_storage_controller.rb
+++ b/app/controllers/repositories/lfs_storage_controller.rb
@@ -49,7 +49,7 @@ module Repositories
validate_uploaded_file!
if store_file!(oid, size)
- head 200, content_type: LfsRequest::CONTENT_TYPE
+ head :ok, content_type: LfsRequest::CONTENT_TYPE
else
render plain: 'Unprocessable entity', status: :unprocessable_entity
end
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 5351e3e9e77..66968b34380 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -8,6 +8,7 @@ class SearchController < ApplicationController
include SearchRateLimitable
RESCUE_FROM_TIMEOUT_ACTIONS = [:count, :show, :autocomplete, :aggregations].freeze
+ CODE_SEARCH_LITERALS = %w[blob: extension: path: filename:].freeze
track_custom_event :show,
name: 'i_search_total',
@@ -32,7 +33,10 @@ class SearchController < ApplicationController
before_action only: :show do
push_frontend_feature_flag(:search_page_vertical_nav, current_user)
end
-
+ before_action only: :show do
+ update_scope_for_code_search
+ end
+ before_action :elasticsearch_in_use, only: :show
rescue_from ActiveRecord::QueryCanceled, with: :render_timeout
layout 'search'
@@ -43,6 +47,7 @@ class SearchController < ApplicationController
def show
@project = search_service.project
@group = search_service.group
+ @search_service = Gitlab::View::Presenter::Factory.new(search_service, current_user: current_user).fabricate!
return unless search_term_valid?
@@ -51,15 +56,11 @@ class SearchController < ApplicationController
@search_term = params[:search]
@sort = params[:sort] || default_sort
- @search_service = Gitlab::View::Presenter::Factory.new(search_service, current_user: current_user).fabricate!
-
@search_level = @search_service.level
@search_type = search_type
@global_search_duration_s = Benchmark.realtime do
@scope = @search_service.scope
- @without_count = @search_service.without_count?
- @show_snippets = @search_service.show_snippets?
@search_results = @search_service.search_results
@search_objects = @search_service.search_objects
@search_highlight = @search_service.search_highlight
@@ -118,8 +119,22 @@ class SearchController < ApplicationController
def opensearch
end
+ def elasticsearch_in_use
+ search_service.respond_to?(:use_elasticsearch?) && search_service.use_elasticsearch?
+ end
+ strong_memoize_attr :elasticsearch_in_use
+
private
+ def update_scope_for_code_search
+ return if params[:scope] == 'blobs'
+ return unless params[:search].present?
+
+ if CODE_SEARCH_LITERALS.any? { |literal| literal.in? params[:search] }
+ redirect_to search_path(safe_params.except(:controller, :action).merge(scope: 'blobs'))
+ end
+ end
+
# overridden in EE
def default_sort
'created_desc'
diff --git a/app/controllers/snippets/application_controller.rb b/app/controllers/snippets/application_controller.rb
index f259f4569ef..64adc4e6611 100644
--- a/app/controllers/snippets/application_controller.rb
+++ b/app/controllers/snippets/application_controller.rb
@@ -4,7 +4,7 @@ class Snippets::ApplicationController < ApplicationController
include FindSnippet
include SnippetAuthorizations
- feature_category :snippets
+ feature_category :source_code_management
private
diff --git a/app/controllers/snippets/notes_controller.rb b/app/controllers/snippets/notes_controller.rb
index 8a4e8edbf3c..9e23eef4178 100644
--- a/app/controllers/snippets/notes_controller.rb
+++ b/app/controllers/snippets/notes_controller.rb
@@ -8,7 +8,7 @@ class Snippets::NotesController < ApplicationController
before_action :authorize_read_snippet!, only: [:show, :index]
before_action :authorize_create_note!, only: [:create]
- feature_category :snippets
+ feature_category :source_code_management
private
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 0f03333d793..f23e513e419 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -31,8 +31,7 @@ class UsersController < ApplicationController
:followers, :following, :calendar, :calendar_activities,
:exists, :activity, :follow, :unfollow, :ssh_keys]
- feature_category :snippets, [:snippets]
- feature_category :source_code_management, [:gpg_keys]
+ feature_category :source_code_management, [:snippets, :gpg_keys]
# TODO: Set higher urgency after resolving https://gitlab.com/gitlab-org/gitlab/-/issues/357914
urgency :low, [:show, :calendar_activities, :contributed, :activity, :projects, :groups, :calendar, :snippets]
diff --git a/app/controllers/web_ide/remote_ide_controller.rb b/app/controllers/web_ide/remote_ide_controller.rb
new file mode 100644
index 00000000000..fe70e78b1e5
--- /dev/null
+++ b/app/controllers/web_ide/remote_ide_controller.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'uri'
+
+module WebIde
+ class RemoteIdeController < ApplicationController
+ include VSCodeCDNCSP
+
+ rescue_from URI::InvalidComponentError, with: :render_404
+
+ before_action :allow_remote_ide_content_security_policy
+
+ feature_category :remote_development
+
+ urgency :low
+
+ def index
+ return render_404 unless Feature.enabled?(:vscode_web_ide, current_user)
+
+ render layout: 'fullscreen', locals: { minimal: true, data: root_element_data }
+ end
+
+ private
+
+ def allow_remote_ide_content_security_policy
+ return if request.content_security_policy.directives.blank?
+
+ default_src = Array(request.content_security_policy.directives['default-src'] || [])
+
+ request.content_security_policy.directives['connect-src'] ||= default_src
+ request.content_security_policy.directives['connect-src'].concat(connect_src_urls)
+ end
+
+ def connect_src_urls
+ # It's okay if "port" is null
+ host, port = params.require(:remote_host).split(':')
+
+ # This could throw URI::InvalidComponentError. We go ahead and let it throw
+ # and let the controller recover with a bad_request response
+ %w[ws wss http https].map { |scheme| URI::Generic.build(scheme: scheme, host: host, port: port).to_s }
+ end
+
+ def root_element_data
+ {
+ connection_token: params.fetch(:connection_token, ''),
+ remote_host: params.require(:remote_host),
+ remote_path: params.fetch(:remote_path, ''),
+ return_url: params.fetch(:return_url, ''),
+ csp_nonce: content_security_policy_nonce
+ }
+ end
+ end
+end
diff --git a/app/events/gitlab_subscriptions/renewed_event.rb b/app/events/gitlab_subscriptions/renewed_event.rb
new file mode 100644
index 00000000000..02bf8ec7095
--- /dev/null
+++ b/app/events/gitlab_subscriptions/renewed_event.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module GitlabSubscriptions
+ class RenewedEvent < Gitlab::EventStore::Event
+ def schema
+ {
+ 'type' => 'object',
+ 'required' => %w[
+ namespace_id
+ ],
+ 'properties' => {
+ 'namespace_id' => { 'type' => 'integer' }
+ }
+ }
+ end
+ end
+end
diff --git a/app/experiments/concerns/project_commit_count.rb b/app/experiments/concerns/project_commit_count.rb
index 706a1a24640..3f08538c21f 100644
--- a/app/experiments/concerns/project_commit_count.rb
+++ b/app/experiments/concerns/project_commit_count.rb
@@ -10,9 +10,9 @@ module ProjectCommitCount
return default_count unless root_ref
Gitlab::GitalyClient::CommitService.new(raw_repo).commit_count(root_ref, {
- all: true, # include all branches
- max_count: max_count # limit as an optimization
- })
+ all: true, # include all branches
+ max_count: max_count # limit as an optimization
+ })
rescue StandardError => e
Gitlab::ErrorTracking.track_exception(e, exception_details)
diff --git a/app/finders/autocomplete/routes_finder.rb b/app/finders/autocomplete/routes_finder.rb
index 858a4b69376..ecede0c1c1c 100644
--- a/app/finders/autocomplete/routes_finder.rb
+++ b/app/finders/autocomplete/routes_finder.rb
@@ -13,7 +13,7 @@ module Autocomplete
end
def execute
- return [] if @search.blank?
+ return Route.none if @search.blank?
Route
.for_routable(routables)
@@ -30,7 +30,7 @@ module Autocomplete
class NamespacesOnly < self
def routables
- return Namespace.without_project_namespaces if current_user.admin?
+ return Namespace.without_project_namespaces if current_user.can_admin_all_resources?
current_user.namespaces
end
@@ -38,7 +38,7 @@ module Autocomplete
class ProjectsOnly < self
def routables
- return Project.all if current_user.admin?
+ return Project.all if current_user.can_admin_all_resources?
current_user.projects
end
diff --git a/app/finders/ci/freeze_periods_finder.rb b/app/finders/ci/freeze_periods_finder.rb
new file mode 100644
index 00000000000..91df776abe6
--- /dev/null
+++ b/app/finders/ci/freeze_periods_finder.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Ci
+ class FreezePeriodsFinder
+ def initialize(project, current_user = nil)
+ @project = project
+ @current_user = current_user
+ end
+
+ def execute
+ return Ci::FreezePeriod.none unless Ability.allowed?(@current_user, :read_freeze_period, @project)
+
+ @project.freeze_periods
+ end
+ end
+end
diff --git a/app/finders/ci/jobs_finder.rb b/app/finders/ci/jobs_finder.rb
index 152eb271694..1627e41a02d 100644
--- a/app/finders/ci/jobs_finder.rb
+++ b/app/finders/ci/jobs_finder.rb
@@ -16,6 +16,7 @@ module Ci
def execute
builds = init_collection.order_id_desc
+ builds = filter_by_with_artifacts(builds)
filter_by_scope(builds)
rescue Gitlab::Access::AccessDeniedError
type.none
@@ -30,7 +31,7 @@ module Ci
end
def all_jobs
- raise Gitlab::Access::AccessDeniedError unless current_user&.admin?
+ raise Gitlab::Access::AccessDeniedError unless current_user&.can_admin_all_resources?
type.all
end
@@ -72,6 +73,14 @@ module Ci
end
end
+ def filter_by_with_artifacts(builds)
+ if params[:with_artifacts]
+ builds.with_erasable_artifacts
+ else
+ builds
+ end
+ end
+
def filter_by_statuses!(builds)
unknown_statuses = params[:scope] - ::CommitStatus::AVAILABLE_STATUSES
raise ArgumentError, 'Scope contains invalid value(s)' unless unknown_statuses.empty?
diff --git a/app/finders/ci/pipelines_finder.rb b/app/finders/ci/pipelines_finder.rb
index 712d5f8c6fb..4c47517299a 100644
--- a/app/finders/ci/pipelines_finder.rb
+++ b/app/finders/ci/pipelines_finder.rb
@@ -36,6 +36,7 @@ module Ci
items = by_yaml_errors(items)
items = by_updated_at(items)
items = by_source(items)
+ items = by_name(items)
sort_items(items)
end
@@ -152,6 +153,15 @@ module Ci
items
end
+ def by_name(items)
+ return items unless
+ Feature.enabled?(:pipeline_name, project) &&
+ Feature.enabled?(:pipeline_name_search, project) &&
+ params[:name].present?
+
+ items.for_name(params[:name])
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def sort_items(items)
order_by = if ALLOWED_INDEXED_COLUMNS.include?(params[:order_by])
diff --git a/app/finders/ci/runners_finder.rb b/app/finders/ci/runners_finder.rb
index d0d98a59677..136d23939e2 100644
--- a/app/finders/ci/runners_finder.rb
+++ b/app/finders/ci/runners_finder.rb
@@ -10,6 +10,7 @@ module Ci
def initialize(current_user:, params:)
@params = params
@group = params.delete(:group)
+ @project = params.delete(:project)
@current_user = current_user
end
@@ -36,13 +37,19 @@ module Ci
private
def search!
- @group ? group_runners : all_runners
+ if @project && Feature.enabled?(:on_demand_scans_runner_tags, @project)
+ project_runners
+ elsif @group
+ group_runners
+ else
+ all_runners
+ end
@runners = @runners.search(@params[:search]) if @params[:search].present?
end
def all_runners
- raise Gitlab::Access::AccessDeniedError unless @current_user&.admin?
+ raise Gitlab::Access::AccessDeniedError unless @current_user&.can_admin_all_resources?
@runners = Ci::Runner.all
end
@@ -66,6 +73,12 @@ module Ci
end
end
+ def project_runners
+ raise Gitlab::Access::AccessDeniedError unless can?(@current_user, :admin_project, @project)
+
+ @runners = ::Ci::Runner.owned_or_instance_wide(@project.id)
+ end
+
def filter_by_active!
@runners = @runners.active(@params[:active]) if @params.include?(:active)
end
diff --git a/app/finders/clusters/agent_tokens_finder.rb b/app/finders/clusters/agent_tokens_finder.rb
index e241836e1dc..72692777bc6 100644
--- a/app/finders/clusters/agent_tokens_finder.rb
+++ b/app/finders/clusters/agent_tokens_finder.rb
@@ -2,24 +2,30 @@
module Clusters
class AgentTokensFinder
- def initialize(object, current_user, agent_id)
- @object = object
+ include FinderMethods
+
+ def initialize(agent, current_user, params = {})
+ @agent = agent
@current_user = current_user
- @agent_id = agent_id
+ @params = params
end
def execute
- raise_not_found_unless_can_read_cluster
+ return ::Clusters::AgentToken.none unless can_read_cluster_agents?
- object.cluster_agents.find(agent_id).agent_tokens
+ agent.agent_tokens.then { |agent_tokens| by_status(agent_tokens) }
end
private
- attr_reader :object, :current_user, :agent_id
+ attr_reader :agent, :current_user, :params
+
+ def by_status(agent_tokens)
+ params[:status].present? ? agent_tokens.with_status(params[:status]) : agent_tokens
+ end
- def raise_not_found_unless_can_read_cluster
- raise ActiveRecord::RecordNotFound unless current_user&.can?(:read_cluster, object)
+ def can_read_cluster_agents?
+ current_user&.can?(:read_cluster, agent&.project)
end
end
end
diff --git a/app/finders/deployments_finder.rb b/app/finders/deployments_finder.rb
index 5b2139cb941..21869f6f31d 100644
--- a/app/finders/deployments_finder.rb
+++ b/app/finders/deployments_finder.rb
@@ -212,6 +212,7 @@ class DeploymentsFinder
deployable: {
job_artifacts: [],
user: [],
+ metadata: [],
pipeline: {
project: {
route: [],
diff --git a/app/finders/environments/environments_finder.rb b/app/finders/environments/environments_finder.rb
index f2dcba04349..85cd37c267e 100644
--- a/app/finders/environments/environments_finder.rb
+++ b/app/finders/environments/environments_finder.rb
@@ -41,7 +41,13 @@ module Environments
def by_search(environments)
if params[:search].present?
- environments.for_name_like(params[:search], limit: nil)
+ if Feature.enabled?(:enable_environments_search_within_folder, project)
+ Environment.from_union(
+ environments.for_name_like(params[:search], limit: nil),
+ environments.for_name_like_within_folder(params[:search], limit: nil))
+ else
+ environments.for_name_like(params[:search], limit: nil)
+ end
else
environments
end
@@ -57,7 +63,7 @@ module Environments
def by_ids(environments)
if params[:environment_ids].present?
- environments.for_id(params[:environment_ids])
+ environments.id_in(params[:environment_ids])
else
environments
end
diff --git a/app/finders/freeze_periods_finder.rb b/app/finders/freeze_periods_finder.rb
deleted file mode 100644
index 2a9bfbe12ba..00000000000
--- a/app/finders/freeze_periods_finder.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-class FreezePeriodsFinder
- def initialize(project, current_user = nil)
- @project = project
- @current_user = current_user
- end
-
- def execute
- return Ci::FreezePeriod.none unless Ability.allowed?(@current_user, :read_freeze_period, @project)
-
- @project.freeze_periods
- end
-end
diff --git a/app/finders/git_refs_finder.rb b/app/finders/git_refs_finder.rb
index dbe0060d8ae..0492dd9934f 100644
--- a/app/finders/git_refs_finder.rb
+++ b/app/finders/git_refs_finder.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class GitRefsFinder
+ include Gitlab::Utils::StrongMemoize
+
def initialize(repository, params = {})
@repository = repository
@params = params
@@ -10,44 +12,28 @@ class GitRefsFinder
attr_reader :repository, :params
- def search
- @params[:search].to_s.presence
- end
-
- def sort
- @params[:sort].to_s.presence || 'name'
- end
-
def by_search(refs)
return refs unless search
- case search
- when ->(v) { v.starts_with?('^') }
- filter_refs_with_prefix(refs, search.slice(1..-1))
- when ->(v) { v.ends_with?('$') }
- filter_refs_with_suffix(refs, search.chop)
- else
- matches = filter_refs_by_name(refs, search)
- set_exact_match_as_first_result(matches, search)
- end
- end
-
- def filter_refs_with_prefix(refs, prefix)
- prefix = prefix.downcase
+ matches = filter_refs(refs, search)
+ return matches if regex_search?
- refs.select { |ref| ref.name.downcase.starts_with?(prefix) }
+ set_exact_match_as_first_result(matches, search)
end
- def filter_refs_with_suffix(refs, suffix)
- suffix = suffix.downcase
-
- refs.select { |ref| ref.name.downcase.ends_with?(suffix) }
+ def search
+ @params[:search].to_s.presence
end
+ strong_memoize_attr :search
- def filter_refs_by_name(refs, term)
- term = term.downcase
+ def sort
+ @params[:sort].to_s.presence || 'name'
+ end
- refs.select { |ref| ref.name.downcase.include?(term) }
+ def filter_refs(refs, term)
+ regex_string = Regexp.quote(term.downcase)
+ regex_string = unescape_regex_operators(regex_string) if regex_search?
+ refs.select { |ref| /#{regex_string}/ === ref.name.downcase }
end
def set_exact_match_as_first_result(matches, term)
@@ -59,4 +45,13 @@ class GitRefsFinder
def find_exact_match_index(matches, term)
matches.index { |ref| ref.name.casecmp(term) == 0 }
end
+
+ def regex_search?
+ Regexp.union('^', '$', '*') === search
+ end
+ strong_memoize_attr :regex_search?, :regex_search
+
+ def unescape_regex_operators(regex_string)
+ regex_string.sub('\^', '^').gsub('\*', '.*?').sub('\$', '$')
+ end
end
diff --git a/app/finders/group_descendants_finder.rb b/app/finders/group_descendants_finder.rb
index 42cd06c8066..033af0f42a6 100644
--- a/app/finders/group_descendants_finder.rb
+++ b/app/finders/group_descendants_finder.rb
@@ -22,7 +22,7 @@
class GroupDescendantsFinder
attr_reader :current_user, :parent_group, :params
- def initialize(current_user: nil, parent_group:, params: {})
+ def initialize(parent_group:, current_user: nil, params: {})
@current_user = current_user
@parent_group = parent_group
@params = params.reverse_merge(non_archived: params[:archived].blank?, not_aimed_for_deletion: true)
diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb
index 4688d561897..47ed623b252 100644
--- a/app/finders/group_members_finder.rb
+++ b/app/finders/group_members_finder.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
class GroupMembersFinder < UnionFinder
- RELATIONS = %i(direct inherited descendants shared_from_groups).freeze
- DEFAULT_RELATIONS = %i(direct inherited).freeze
+ RELATIONS = %i[direct inherited descendants shared_from_groups].freeze
+ DEFAULT_RELATIONS = %i[direct inherited].freeze
INVALID_RELATION_TYPE_ERROR_MSG = "is not a valid relation type. Valid relation types are #{RELATIONS.join(', ')}."
RELATIONS_DESCRIPTIONS = {
diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb
index e68a0c8fca9..de6eacbb1e0 100644
--- a/app/finders/members_finder.rb
+++ b/app/finders/members_finder.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
class MembersFinder
- RELATIONS = %i(direct inherited descendants invited_groups).freeze
- DEFAULT_RELATIONS = %i(direct inherited).freeze
+ RELATIONS = %i[direct inherited descendants invited_groups].freeze
+ DEFAULT_RELATIONS = %i[direct inherited].freeze
# Params can be any of the following:
# sort: string
diff --git a/app/finders/merge_request_target_project_finder.rb b/app/finders/merge_request_target_project_finder.rb
index dc9b28ab0a0..fdb3bac8935 100644
--- a/app/finders/merge_request_target_project_finder.rb
+++ b/app/finders/merge_request_target_project_finder.rb
@@ -5,7 +5,7 @@ class MergeRequestTargetProjectFinder
attr_reader :current_user, :source_project
- def initialize(current_user: nil, source_project:, project_feature: :merge_requests)
+ def initialize(source_project:, current_user: nil, project_feature: :merge_requests)
@current_user = current_user
@source_project = source_project
@project_feature = project_feature
diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb
index 42bd7a24888..7890502cf0e 100644
--- a/app/finders/notes_finder.rb
+++ b/app/finders/notes_finder.rb
@@ -65,7 +65,7 @@ class NotesFinder
@target =
if target_type == "commit"
- if Ability.allowed?(@current_user, :download_code, @project)
+ if Ability.allowed?(@current_user, :read_code, @project)
@project.commit(target_id)
end
else
@@ -101,7 +101,7 @@ class NotesFinder
# rubocop: disable CodeReuse/ActiveRecord
def notes_of_any_type
- types = %w(commit issue merge_request snippet)
+ types = %w[commit issue merge_request snippet]
note_relations = types.map { |t| notes_for_type(t) }
note_relations.map! { |notes| search(notes) }
UnionFinder.new.find_union(note_relations, Note.includes(:author)) # rubocop: disable CodeReuse/Finder
@@ -126,7 +126,7 @@ class NotesFinder
# rubocop: disable CodeReuse/ActiveRecord
def notes_for_type(noteable_type)
if noteable_type == "commit"
- if Ability.allowed?(@current_user, :download_code, @project)
+ if Ability.allowed?(@current_user, :read_code, @project)
@project.notes.where(noteable_type: 'Commit')
else
Note.none
diff --git a/app/finders/personal_access_tokens_finder.rb b/app/finders/personal_access_tokens_finder.rb
index 8403c531945..5af08cf0660 100644
--- a/app/finders/personal_access_tokens_finder.rb
+++ b/app/finders/personal_access_tokens_finder.rb
@@ -33,7 +33,7 @@ class PersonalAccessTokensFinder
attr_reader :current_user
def by_current_user(tokens)
- return tokens if current_user.nil? || current_user.admin?
+ return tokens if current_user.nil? || current_user.can_admin_all_resources?
return PersonalAccessToken.none unless Ability.allowed?(current_user, :read_user_personal_access_tokens, params[:user])
tokens
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index 126687ae41f..1afd5adeada 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -89,6 +89,7 @@ class ProjectsFinder < UnionFinder
collection = by_not_aimed_for_deletion(collection)
collection = by_last_activity_after(collection)
collection = by_last_activity_before(collection)
+ collection = by_language(collection)
by_repository_storage(collection)
end
@@ -97,12 +98,10 @@ class ProjectsFinder < UnionFinder
current_user.owned_projects
elsif min_access_level?
current_user.authorized_projects(params[:min_access_level])
+ elsif private_only? || impossible_visibility_level?
+ current_user.authorized_projects
else
- if private_only? || impossible_visibility_level?
- current_user.authorized_projects
- else
- Project.public_or_visible_to_user(current_user)
- end
+ Project.public_or_visible_to_user(current_user)
end
end
@@ -239,6 +238,14 @@ class ProjectsFinder < UnionFinder
end
end
+ def by_language(items)
+ if Feature.enabled?(:project_language_search, current_user) && params[:language].present?
+ items.with_programming_language_id(params[:language])
+ else
+ items
+ end
+ end
+
def sort(items)
if params[:sort].present?
items.sort_by_attribute(params[:sort])
diff --git a/app/finders/releases/group_releases_finder.rb b/app/finders/releases/group_releases_finder.rb
index 08530f63ea6..67784d6579c 100644
--- a/app/finders/releases/group_releases_finder.rb
+++ b/app/finders/releases/group_releases_finder.rb
@@ -33,8 +33,8 @@ module Releases
Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder.new(
scope: releases_scope,
array_scope: Project.for_group_and_its_subgroups(parent).select(:id),
- array_mapping_scope: -> (project_id_expression) { Release.where(Release.arel_table[:project_id].eq(project_id_expression)) },
- finder_query: -> (order_by, id_expression) { Release.where(Release.arel_table[:id].eq(id_expression)) }
+ array_mapping_scope: ->(project_id_expression) { Release.where(Release.arel_table[:project_id].eq(project_id_expression)) },
+ finder_query: ->(order_by, id_expression) { Release.where(Release.arel_table[:id].eq(id_expression)) }
)
.execute
end
diff --git a/app/finders/repositories/tree_finder.rb b/app/finders/repositories/tree_finder.rb
index 2ea5a8856ec..231c1de1513 100644
--- a/app/finders/repositories/tree_finder.rb
+++ b/app/finders/repositories/tree_finder.rb
@@ -1,15 +1,13 @@
# frozen_string_literal: true
module Repositories
- class TreeFinder < GitRefsFinder
- attr_reader :user_project
-
+ class TreeFinder
CommitMissingError = Class.new(StandardError)
- def initialize(user_project, params = {})
- super(user_project.repository, params)
-
- @user_project = user_project
+ def initialize(project, params = {})
+ @project = project
+ @repository = project.repository
+ @params = params
end
def execute(gitaly_pagination: false)
@@ -17,15 +15,15 @@ module Repositories
request_params = { recursive: recursive }
request_params[:pagination_params] = pagination_params if gitaly_pagination
- tree = user_project.repository.tree(commit.id, path, **request_params)
- tree.sorted_entries
+ repository.tree(commit.id, path, **request_params).sorted_entries
end
def total
# This is inefficient and we'll look at replacing this implementation
- Gitlab::Cache.fetch_once([user_project, repository.commit, :tree_size, commit.id, path, recursive]) do
- user_project.repository.tree(commit.id, path, recursive: recursive).entries.size
+ cache_key = [project, repository.commit, :tree_size, commit.id, path, recursive]
+ Gitlab::Cache.fetch_once(cache_key) do
+ repository.tree(commit.id, path, recursive: recursive).entries.size
end
end
@@ -35,12 +33,14 @@ module Repositories
private
+ attr_reader :project, :repository, :params
+
def commit
- @commit ||= user_project.commit(ref)
+ @commit ||= project.commit(ref)
end
def ref
- params[:ref] || user_project.default_branch
+ params[:ref] || project.default_branch
end
def path
diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb
index e83018ed24c..0bf31ea33dd 100644
--- a/app/finders/todos_finder.rb
+++ b/app/finders/todos_finder.rb
@@ -24,7 +24,7 @@ class TodosFinder
NONE = '0'
- TODO_TYPES = Set.new(%w(Issue MergeRequest DesignManagement::Design AlertManagement::Alert)).freeze
+ TODO_TYPES = Set.new(%w[Issue WorkItem MergeRequest DesignManagement::Design AlertManagement::Alert]).freeze
attr_accessor :current_user, :params
diff --git a/app/finders/users_finder.rb b/app/finders/users_finder.rb
index 9c2462b42a6..11e3c341c1f 100644
--- a/app/finders/users_finder.rb
+++ b/app/finders/users_finder.rb
@@ -55,7 +55,7 @@ class UsersFinder
private
def base_scope
- scope = current_user&.admin? ? User.all : User.without_forbidden_states
+ scope = current_user&.can_admin_all_resources? ? User.all : User.without_forbidden_states
scope.order_id_desc
end
@@ -80,7 +80,7 @@ class UsersFinder
def by_search(users)
return users unless params[:search].present?
- users.search(params[:search], with_private_emails: current_user&.admin?)
+ users.search(params[:search], with_private_emails: current_user&.can_admin_all_resources?)
end
def by_blocked(users)
@@ -97,7 +97,7 @@ class UsersFinder
# rubocop: disable CodeReuse/ActiveRecord
def by_external_identity(users)
- return users unless current_user&.admin? && params[:extern_uid] && params[:provider]
+ return users unless current_user&.can_admin_all_resources? && params[:extern_uid] && params[:provider]
users.joins(:identities).merge(Identity.with_extern_uid(params[:provider], params[:extern_uid]))
end
diff --git a/app/graphql/graphql_triggers.rb b/app/graphql/graphql_triggers.rb
index 710e7fe110c..7f83b62a2ff 100644
--- a/app/graphql/graphql_triggers.rb
+++ b/app/graphql/graphql_triggers.rb
@@ -44,6 +44,14 @@ module GraphqlTriggers
merge_request
)
end
+
+ def self.merge_request_approval_state_updated(merge_request)
+ GitlabSchema.subscriptions.trigger(
+ 'mergeRequestApprovalStateUpdated',
+ { issuable_id: merge_request.to_gid },
+ merge_request
+ )
+ end
end
GraphqlTriggers.prepend_mod
diff --git a/app/graphql/mutations/alert_management/alerts/set_assignees.rb b/app/graphql/mutations/alert_management/alerts/set_assignees.rb
index c986111d290..500e2b868b1 100644
--- a/app/graphql/mutations/alert_management/alerts/set_assignees.rb
+++ b/app/graphql/mutations/alert_management/alerts/set_assignees.rb
@@ -20,7 +20,7 @@ module Mutations
alert = authorized_find!(project_path: args[:project_path], iid: args[:iid])
result = set_assignees(alert, args[:assignee_usernames], args[:operation_mode])
- track_usage_event(:incident_management_alert_assigned, current_user.id)
+ track_alert_events('incident_management_alert_assigned', alert)
prepare_response(result)
end
diff --git a/app/graphql/mutations/alert_management/alerts/todo/create.rb b/app/graphql/mutations/alert_management/alerts/todo/create.rb
index 2a1056e8f64..999c0bec5af 100644
--- a/app/graphql/mutations/alert_management/alerts/todo/create.rb
+++ b/app/graphql/mutations/alert_management/alerts/todo/create.rb
@@ -11,7 +11,7 @@ module Mutations
alert = authorized_find!(project_path: args[:project_path], iid: args[:iid])
result = ::AlertManagement::Alerts::Todo::CreateService.new(alert, current_user).execute
- track_usage_event(:incident_management_alert_todo, current_user.id)
+ track_alert_events('incident_management_alert_todo', alert)
prepare_response(result)
end
diff --git a/app/graphql/mutations/alert_management/base.rb b/app/graphql/mutations/alert_management/base.rb
index d01f200107c..2eef6bb9db7 100644
--- a/app/graphql/mutations/alert_management/base.rb
+++ b/app/graphql/mutations/alert_management/base.rb
@@ -39,6 +39,24 @@ module Mutations
::AlertManagement::AlertsFinder.new(current_user, project, args).execute.first
end
+
+ def track_alert_events(event, alert)
+ project = alert.project
+ namespace = project.namespace
+ track_usage_event(event, current_user.id)
+
+ return unless Feature.enabled?(:route_hll_to_snowplow_phase2, namespace)
+
+ Gitlab::Tracking.event(
+ self.class.to_s,
+ event,
+ project: project,
+ namespace: namespace,
+ user: current_user,
+ label: 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly',
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event).to_context]
+ )
+ end
end
end
end
diff --git a/app/graphql/mutations/alert_management/create_alert_issue.rb b/app/graphql/mutations/alert_management/create_alert_issue.rb
index 77a7d7a4147..7c8de6365e7 100644
--- a/app/graphql/mutations/alert_management/create_alert_issue.rb
+++ b/app/graphql/mutations/alert_management/create_alert_issue.rb
@@ -9,7 +9,7 @@ module Mutations
alert = authorized_find!(project_path: args[:project_path], iid: args[:iid])
result = create_alert_issue(alert, current_user)
- track_usage_event(:incident_management_incident_created, current_user.id)
+ track_alert_events('incident_management_incident_created', alert)
track_usage_event(:incident_management_alert_create_incident, current_user.id)
prepare_response(alert, result)
diff --git a/app/graphql/mutations/alert_management/update_alert_status.rb b/app/graphql/mutations/alert_management/update_alert_status.rb
index 21566c7d66f..be271a7d795 100644
--- a/app/graphql/mutations/alert_management/update_alert_status.rb
+++ b/app/graphql/mutations/alert_management/update_alert_status.rb
@@ -13,7 +13,7 @@ module Mutations
alert = authorized_find!(project_path: project_path, iid: iid)
result = update_status(alert, status)
- track_usage_event(:incident_management_alert_status_changed, current_user.id)
+ track_alert_events('incident_management_alert_status_changed', alert)
prepare_response(result)
end
diff --git a/app/graphql/mutations/ci/pipeline_schedule/create.rb b/app/graphql/mutations/ci/pipeline_schedule/create.rb
new file mode 100644
index 00000000000..65b355cd80f
--- /dev/null
+++ b/app/graphql/mutations/ci/pipeline_schedule/create.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Ci
+ module PipelineSchedule
+ class Create < BaseMutation
+ graphql_name 'PipelineScheduleCreate'
+
+ include FindsProject
+
+ authorize :create_pipeline_schedule
+
+ argument :project_path, GraphQL::Types::ID,
+ required: true,
+ description: 'Full path of the project the pipeline schedule is associated with.'
+
+ argument :description, GraphQL::Types::String,
+ required: true,
+ description: 'Description of the pipeline schedule.'
+
+ argument :cron, GraphQL::Types::String,
+ required: true,
+ description: 'Cron expression of the pipeline schedule.'
+
+ argument :cron_timezone, GraphQL::Types::String,
+ required: false,
+ description:
+ <<-STR
+ Cron time zone supported by ActiveSupport::TimeZone.
+ For example: "Pacific Time (US & Canada)" (default: "UTC").
+ STR
+
+ argument :ref, GraphQL::Types::String,
+ required: true,
+ description: 'Ref of the pipeline schedule.'
+
+ argument :active, GraphQL::Types::Boolean,
+ required: false,
+ description: 'Indicates if the pipeline schedule should be active or not.'
+
+ argument :variables, [Mutations::Ci::PipelineSchedule::VariableInputType],
+ required: false,
+ description: 'Variables for the pipeline schedule.'
+
+ field :pipeline_schedule,
+ Types::Ci::PipelineScheduleType,
+ description: 'Created pipeline schedule.'
+
+ def resolve(project_path:, variables: [], **pipeline_schedule_attrs)
+ project = authorized_find!(project_path)
+
+ params = pipeline_schedule_attrs.merge(variables_attributes: variables.map(&:to_h))
+
+ schedule = ::Ci::CreatePipelineScheduleService
+ .new(project, current_user, params)
+ .execute
+
+ unless schedule.persisted?
+ return {
+ pipeline_schedule: nil, errors: schedule.errors.full_messages
+ }
+ end
+
+ {
+ pipeline_schedule: schedule,
+ errors: []
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/ci/pipeline_schedule/play.rb b/app/graphql/mutations/ci/pipeline_schedule/play.rb
new file mode 100644
index 00000000000..056890852c9
--- /dev/null
+++ b/app/graphql/mutations/ci/pipeline_schedule/play.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Ci
+ module PipelineSchedule
+ class Play < Base
+ graphql_name 'PipelineSchedulePlay'
+
+ authorize :play_pipeline_schedule
+
+ field :pipeline_schedule,
+ Types::Ci::PipelineScheduleType,
+ null: true,
+ description: 'Pipeline schedule after mutation.'
+
+ def resolve(id:)
+ schedule = authorized_find!(id: id)
+
+ job_id = ::Ci::PipelineScheduleService
+ .new(schedule.project, current_user)
+ .execute(schedule)
+
+ if job_id
+ { pipeline_schedule: schedule, errors: [] }
+ else
+ { pipeline_schedule: nil, errors: ['Unable to schedule a pipeline to run immediately.'] }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/ci/pipeline_schedule/variable_input_type.rb b/app/graphql/mutations/ci/pipeline_schedule/variable_input_type.rb
new file mode 100644
index 00000000000..54a6ad92448
--- /dev/null
+++ b/app/graphql/mutations/ci/pipeline_schedule/variable_input_type.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Ci
+ module PipelineSchedule
+ class VariableInputType < Types::BaseInputObject
+ graphql_name 'PipelineScheduleVariableInput'
+
+ description 'Attributes for the pipeline schedule variable.'
+
+ argument :key, GraphQL::Types::String, required: true, description: 'Name of the variable.'
+
+ argument :value, GraphQL::Types::String, required: true, description: 'Value of the variable.'
+
+ argument :variable_type, Types::Ci::VariableTypeEnum, required: true, description: 'Type of the variable.'
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/ci/runner/update.rb b/app/graphql/mutations/ci/runner/update.rb
index 3c99cde60a4..4f0bf19f09c 100644
--- a/app/graphql/mutations/ci/runner/update.rb
+++ b/app/graphql/mutations/ci/runner/update.rb
@@ -54,7 +54,7 @@ module Mutations
argument :associated_projects, [::Types::GlobalIDType[::Project]],
required: false,
description: 'Projects associated with the runner. Available only for project runners.',
- prepare: -> (global_ids, ctx) { global_ids&.filter_map { |gid| gid.model_id.to_i } }
+ prepare: ->(global_ids, ctx) { global_ids&.filter_map { |gid| gid.model_id.to_i } }
field :runner,
Types::Ci::RunnerType,
diff --git a/app/graphql/mutations/clusters/agent_tokens/create.rb b/app/graphql/mutations/clusters/agent_tokens/create.rb
index a99a54fa5ed..c10e1633350 100644
--- a/app/graphql/mutations/clusters/agent_tokens/create.rb
+++ b/app/graphql/mutations/clusters/agent_tokens/create.rb
@@ -49,9 +49,9 @@ module Mutations
payload = result.payload
{
- secret: payload[:secret],
- token: payload[:token],
- errors: Array.wrap(result.message)
+ secret: payload[:secret],
+ token: payload[:token],
+ errors: Array.wrap(result.message)
}
end
diff --git a/app/graphql/mutations/container_repositories/destroy.rb b/app/graphql/mutations/container_repositories/destroy.rb
index fe1c3fe4e61..c3bd7acf444 100644
--- a/app/graphql/mutations/container_repositories/destroy.rb
+++ b/app/graphql/mutations/container_repositories/destroy.rb
@@ -22,10 +22,6 @@ module Mutations
container_repository.delete_scheduled!
- unless Feature.enabled?(:container_registry_delete_repository_with_cron_worker)
- DeleteContainerRepositoryWorker.perform_async(current_user.id, container_repository.id) # rubocop:disable CodeReuse/Worker
- end
-
track_event(:delete_repository, :container)
{
diff --git a/app/graphql/mutations/incident_management/timeline_event/update.rb b/app/graphql/mutations/incident_management/timeline_event/update.rb
index 1f53bdc19cb..b35feed3082 100644
--- a/app/graphql/mutations/incident_management/timeline_event/update.rb
+++ b/app/graphql/mutations/incident_management/timeline_event/update.rb
@@ -18,6 +18,10 @@ module Mutations
required: false,
description: 'Timestamp when the event occurred.'
+ argument :timeline_event_tag_names, [GraphQL::Types::String],
+ required: false,
+ description: copy_field_description(Types::IncidentManagement::TimelineEventType, :timeline_event_tags)
+
def resolve(id:, **args)
timeline_event = authorized_find!(id: id)
diff --git a/app/graphql/mutations/issues/link_alerts.rb b/app/graphql/mutations/issues/link_alerts.rb
new file mode 100644
index 00000000000..c45e90c598f
--- /dev/null
+++ b/app/graphql/mutations/issues/link_alerts.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Issues
+ class LinkAlerts < Base
+ graphql_name 'IssueLinkAlerts'
+
+ argument :alert_references, [GraphQL::Types::String],
+ required: true,
+ description: 'Alerts references to be linked to the incident.'
+
+ authorize :admin_issue
+
+ def resolve(project_path:, iid:, alert_references:)
+ issue = authorized_find!(project_path: project_path, iid: iid)
+
+ ::IncidentManagement::LinkAlerts::CreateService.new(issue, current_user, alert_references).execute
+
+ {
+ issue: issue,
+ errors: errors_on_object(issue)
+ }
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/issues/unlink_alert.rb b/app/graphql/mutations/issues/unlink_alert.rb
new file mode 100644
index 00000000000..a11af4133cf
--- /dev/null
+++ b/app/graphql/mutations/issues/unlink_alert.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Issues
+ class UnlinkAlert < Base
+ graphql_name 'IssueUnlinkAlert'
+
+ argument :alert_id, ::Types::GlobalIDType[::AlertManagement::Alert],
+ required: true,
+ description: 'Global ID of the alert to unlink from the incident.'
+
+ authorize :admin_issue
+
+ def resolve(project_path:, iid:, alert_id:)
+ issue = authorized_find!(project_path: project_path, iid: iid)
+ alert = find_alert_by_gid(alert_id)
+
+ result = ::IncidentManagement::LinkAlerts::DestroyService.new(issue, current_user, alert).execute
+
+ {
+ issue: issue,
+ errors: result.errors
+ }
+ end
+
+ private
+
+ def find_alert_by_gid(alert_id)
+ ::Gitlab::Graphql::Lazy.force(GitlabSchema.object_from_id(alert_id, expected_type: ::AlertManagement::Alert))
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/notes/create/diff_note.rb b/app/graphql/mutations/notes/create/diff_note.rb
index 7b8c06fd104..df2bd55106e 100644
--- a/app/graphql/mutations/notes/create/diff_note.rb
+++ b/app/graphql/mutations/notes/create/diff_note.rb
@@ -31,10 +31,10 @@ module Mutations
def create_note_params(noteable, args)
super(noteable, args).merge({
- type: 'DiffNote',
- position: position(noteable, args),
- merge_request_diff_head_sha: args[:position][:head_sha]
- })
+ type: 'DiffNote',
+ position: position(noteable, args),
+ merge_request_diff_head_sha: args[:position][:head_sha]
+ })
end
def position(noteable, args)
diff --git a/app/graphql/mutations/notes/create/image_diff_note.rb b/app/graphql/mutations/notes/create/image_diff_note.rb
index d94fd4d6ff8..3de93e4f5c1 100644
--- a/app/graphql/mutations/notes/create/image_diff_note.rb
+++ b/app/graphql/mutations/notes/create/image_diff_note.rb
@@ -15,9 +15,9 @@ module Mutations
def create_note_params(noteable, args)
super(noteable, args).merge({
- type: 'DiffNote',
- position: position(noteable, args)
- })
+ type: 'DiffNote',
+ position: position(noteable, args)
+ })
end
def position(noteable, args)
diff --git a/app/graphql/mutations/notes/create/note.rb b/app/graphql/mutations/notes/create/note.rb
index 4d6f056de09..9b105b7fe1c 100644
--- a/app/graphql/mutations/notes/create/note.rb
+++ b/app/graphql/mutations/notes/create/note.rb
@@ -31,9 +31,9 @@ module Mutations
end
super(noteable, args).merge({
- in_reply_to_discussion_id: discussion_id,
- merge_request_diff_head_sha: args[:merge_request_diff_head_sha]
- })
+ in_reply_to_discussion_id: discussion_id,
+ merge_request_diff_head_sha: args[:merge_request_diff_head_sha]
+ })
end
def authorize_discussion!(discussion)
diff --git a/app/graphql/mutations/timelogs/create.rb b/app/graphql/mutations/timelogs/create.rb
index bab7508454e..1be023eed8a 100644
--- a/app/graphql/mutations/timelogs/create.rb
+++ b/app/graphql/mutations/timelogs/create.rb
@@ -11,7 +11,7 @@ module Mutations
description: 'Amount of time spent.'
argument :spent_at,
- Types::DateType,
+ Types::TimeType,
required: true,
description: 'When the time was spent.'
@@ -28,8 +28,12 @@ module Mutations
authorize :create_timelog
def resolve(issuable_id:, time_spent:, spent_at:, summary:, **args)
- issuable = authorized_find!(id: issuable_id)
parsed_time_spent = Gitlab::TimeTrackingFormatter.parse(time_spent)
+ if parsed_time_spent.nil?
+ return { timelog: nil, errors: [_('Time spent must be formatted correctly. For example: 1h 30m.')] }
+ end
+
+ issuable = authorized_find!(id: issuable_id)
result = ::Timelogs::CreateService.new(
issuable, parsed_time_spent, spent_at, summary, current_user
diff --git a/app/graphql/mutations/todos/restore_many.rb b/app/graphql/mutations/todos/restore_many.rb
index 20913a9e7da..f2f944860c2 100644
--- a/app/graphql/mutations/todos/restore_many.rb
+++ b/app/graphql/mutations/todos/restore_many.rb
@@ -23,9 +23,9 @@ module Mutations
updated_ids = restore(todos)
{
- updated_ids: updated_ids,
- todos: Todo.id_in(updated_ids),
- errors: errors_on_objects(todos)
+ updated_ids: updated_ids,
+ todos: Todo.id_in(updated_ids),
+ errors: errors_on_objects(todos)
}
end
diff --git a/app/graphql/mutations/work_items/create.rb b/app/graphql/mutations/work_items/create.rb
index 793e5d3caf8..a4efffb69c1 100644
--- a/app/graphql/mutations/work_items/create.rb
+++ b/app/graphql/mutations/work_items/create.rb
@@ -73,3 +73,5 @@ module Mutations
end
end
end
+
+Mutations::WorkItems::Create.prepend_mod
diff --git a/app/graphql/resolvers/base_resolver.rb b/app/graphql/resolvers/base_resolver.rb
index 2b54a3fdd55..6f847221f1b 100644
--- a/app/graphql/resolvers/base_resolver.rb
+++ b/app/graphql/resolvers/base_resolver.rb
@@ -15,6 +15,13 @@ module Resolvers
@calls_gitaly = true
end
+ # This is a flag to allow us to use `complexity_multiplier` to compute complexity for connection
+ # fields(see BaseField#connection_complexity_multiplier) in resolvers that do external connection pagination,
+ # thus disabling the default `connection` option(see self.field_options method above).
+ def self.calculate_ext_conn_complexity
+ false
+ end
+
def self.field_options
extra_options = {
requires_argument: @requires_argument,
@@ -116,7 +123,7 @@ module Resolvers
# When fetching many items, additional complexity is added to the field
# depending on how many items is fetched. For each item we add 1% of the
# original complexity - this means that loading 100 items (our default
- # maxp_age_size limit) doubles the original complexity.
+ # max_page_size limit) doubles the original complexity.
#
# Complexity is not increased when searching by specific ID(s), because
# complexity difference is minimal in this case.
diff --git a/app/graphql/resolvers/ci/project_runners_resolver.rb b/app/graphql/resolvers/ci/project_runners_resolver.rb
new file mode 100644
index 00000000000..378fa73c065
--- /dev/null
+++ b/app/graphql/resolvers/ci/project_runners_resolver.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Ci
+ class ProjectRunnersResolver < RunnersResolver
+ type Types::Ci::RunnerType.connection_type, null: true
+
+ def parent_param
+ raise 'Expected project missing' unless parent.is_a?(Project)
+
+ { project: parent }
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/ci/runner_groups_resolver.rb b/app/graphql/resolvers/ci/runner_groups_resolver.rb
new file mode 100644
index 00000000000..3360e820bd2
--- /dev/null
+++ b/app/graphql/resolvers/ci/runner_groups_resolver.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Ci
+ class RunnerGroupsResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+ include ResolvesGroups
+
+ type Types::GroupConnection, null: true
+
+ authorize :read_runner
+ authorizes_object!
+
+ alias_method :runner, :object
+
+ def resolve_with_lookahead(**args)
+ return unless runner.group_type?
+
+ BatchLoader::GraphQL.for(runner.id).batch(key: :runner_namespaces) do |runner_ids, loader|
+ plucked_runner_and_namespace_ids =
+ ::Ci::RunnerNamespace
+ .for_runner(runner_ids)
+ .select(:runner_id, :namespace_id)
+ .pluck(:runner_id, :namespace_id) # rubocop: disable CodeReuse/ActiveRecord)
+
+ namespace_ids = plucked_runner_and_namespace_ids.collect(&:last).uniq
+ groups = apply_lookahead(::Group.id_in(namespace_ids))
+ Preloaders::GroupPolicyPreloader.new(groups, current_user).execute
+ groups_by_id = groups.index_by(&:id)
+
+ runner_group_ids_by_runner_id =
+ plucked_runner_and_namespace_ids
+ .group_by { |runner_id, _namespace_id| runner_id }
+ .transform_values { |values| values.filter_map { |_runner_id, namespace_id| groups_by_id[namespace_id] } }
+
+ runner_ids.each do |runner_id|
+ runner_namespaces = runner_group_ids_by_runner_id[runner_id] || []
+
+ loader.call(runner_id, runner_namespaces)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/ci/runner_jobs_resolver.rb b/app/graphql/resolvers/ci/runner_jobs_resolver.rb
index de00aadaea8..b818be3f018 100644
--- a/app/graphql/resolvers/ci/runner_jobs_resolver.rb
+++ b/app/graphql/resolvers/ci/runner_jobs_resolver.rb
@@ -27,9 +27,17 @@ module Resolvers
def preloads
{
- previous_stage_jobs_and_needs: [:needs, :pipeline],
+ previous_stage_jobs_or_needs: [:needs, :pipeline],
artifacts: [:job_artifacts],
- pipeline: [:user]
+ pipeline: [:user],
+ detailed_status: [
+ :metadata,
+ { pipeline: [:merge_request] },
+ { project: [:route, { namespace: :route }] }
+ ],
+ commit_path: [:pipeline, { project: [:route, { namespace: [:route] }] }],
+ short_sha: [:pipeline],
+ tags: [:tags]
}
end
end
diff --git a/app/graphql/resolvers/ci/runner_owner_project_resolver.rb b/app/graphql/resolvers/ci/runner_owner_project_resolver.rb
index da8fab93619..f4e044b81c9 100644
--- a/app/graphql/resolvers/ci/runner_owner_project_resolver.rb
+++ b/app/graphql/resolvers/ci/runner_owner_project_resolver.rb
@@ -13,20 +13,22 @@ module Resolvers
resolve_owner
end
- def preloads
- {
- full_path: [:route]
- }
- end
-
private
- def filtered_preloads
- selection = lookahead
+ def node_selection(selection = lookahead)
+ # There are no nodes or edges selections in RunnerOwnerProjectResolver, but rather a project directly
+ selection
+ end
+
+ def unconditional_includes
+ [:project_feature]
+ end
- preloads.each.flat_map do |name, requirements|
- selection&.selects?(name) ? requirements : []
- end
+ def preloads
+ {
+ full_path: [:route, { namespace: [:route] }],
+ web_url: [:route, { namespace: [:route] }]
+ }
end
def resolve_owner
@@ -48,7 +50,7 @@ module Resolvers
.transform_values { |runner_projects| runner_projects.first.project_id }
project_ids = owner_project_id_by_runner_id.values.uniq
- projects = Project.where(id: project_ids)
+ projects = apply_lookahead(Project.id_in(project_ids))
Preloaders::ProjectPolicyPreloader.new(projects, current_user).execute
projects_by_id = projects.index_by(&:id)
diff --git a/app/graphql/resolvers/ci/runner_projects_resolver.rb b/app/graphql/resolvers/ci/runner_projects_resolver.rb
index af9a67acfda..2a2d63f85de 100644
--- a/app/graphql/resolvers/ci/runner_projects_resolver.rb
+++ b/app/graphql/resolvers/ci/runner_projects_resolver.rb
@@ -40,6 +40,7 @@ module Resolvers
params: project_finder_params(args),
project_ids_relation: project_ids)
.execute
+ projects = apply_lookahead(projects)
Preloaders::ProjectPolicyPreloader.new(projects, current_user).execute
projects_by_id = projects.index_by(&:id)
@@ -58,6 +59,19 @@ module Resolvers
end
# rubocop:enable CodeReuse/ActiveRecord
end
+
+ private
+
+ def unconditional_includes
+ [:project_feature]
+ end
+
+ def preloads
+ super.merge({
+ full_path: [:route, { namespace: [:route] }],
+ web_url: [:route, { namespace: [:route] }]
+ })
+ end
end
end
end
diff --git a/app/graphql/resolvers/clusters/agent_tokens_resolver.rb b/app/graphql/resolvers/clusters/agent_tokens_resolver.rb
index 9740bc6bb6a..b7355a1752e 100644
--- a/app/graphql/resolvers/clusters/agent_tokens_resolver.rb
+++ b/app/graphql/resolvers/clusters/agent_tokens_resolver.rb
@@ -14,18 +14,7 @@ module Resolvers
description: 'Status of the token.'
def resolve(**args)
- return ::Clusters::AgentToken.none unless can_read_agent_tokens?
-
- tokens = agent.agent_tokens
- tokens = tokens.with_status(args[:status]) if args[:status].present?
-
- tokens
- end
-
- private
-
- def can_read_agent_tokens?
- current_user.can?(:read_cluster, project)
+ ::Clusters::AgentTokensFinder.new(agent, current_user, args).execute
end
end
end
diff --git a/app/graphql/resolvers/concerns/looks_ahead.rb b/app/graphql/resolvers/concerns/looks_ahead.rb
index 81099c04e9f..1d532eb2486 100644
--- a/app/graphql/resolvers/concerns/looks_ahead.rb
+++ b/app/graphql/resolvers/concerns/looks_ahead.rb
@@ -39,7 +39,7 @@ module LooksAhead
def filtered_preloads
nodes = node_selection
- return [] unless nodes
+ return [] unless nodes&.selected?
selected_fields = nodes.selections.map(&:name)
root_level_preloads = preloads_from_node_selection(selected_fields, preloads)
@@ -65,13 +65,13 @@ module LooksAhead
end.flatten
end
- def node_selection
- return unless lookahead
+ def node_selection(selection = lookahead)
+ return selection unless selection&.selected?
+ return selection.selection(:edges).selection(:node) if selection.selects?(:edges)
- if lookahead.selects?(:nodes)
- lookahead.selection(:nodes)
- elsif lookahead.selects?(:edges)
- lookahead.selection(:edges).selection(:node)
- end
+ # Will return a NullSelection object if :nodes is not a selection. This
+ # is better than returning nil as we can continue chaining selections on
+ # without raising errors.
+ selection.selection(:nodes)
end
end
diff --git a/app/graphql/resolvers/concerns/resolves_groups.rb b/app/graphql/resolvers/concerns/resolves_groups.rb
index 2a3dce80057..1268e74fd58 100644
--- a/app/graphql/resolvers/concerns/resolves_groups.rb
+++ b/app/graphql/resolvers/concerns/resolves_groups.rb
@@ -22,6 +22,7 @@ module ResolvesGroups
custom_emoji: [:custom_emoji],
full_path: [:route],
path: [:route],
+ web_url: [:route],
dependency_proxy_blob_count: [:dependency_proxy_blobs],
dependency_proxy_blobs: [:dependency_proxy_blobs],
dependency_proxy_image_count: [:dependency_proxy_manifests],
diff --git a/app/graphql/resolvers/environments/nested_environments_resolver.rb b/app/graphql/resolvers/environments/nested_environments_resolver.rb
new file mode 100644
index 00000000000..f043270beca
--- /dev/null
+++ b/app/graphql/resolvers/environments/nested_environments_resolver.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Environments
+ class NestedEnvironmentsResolver < EnvironmentsResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ type Types::NestedEnvironmentType, null: true
+
+ authorizes_object!
+ authorize :read_environment
+
+ def resolve(**args)
+ offset_pagination(super(**args).nested)
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/environments_resolver.rb b/app/graphql/resolvers/environments_resolver.rb
index f265e2183d0..aca1a36f0f5 100644
--- a/app/graphql/resolvers/environments_resolver.rb
+++ b/app/graphql/resolvers/environments_resolver.rb
@@ -14,6 +14,10 @@ module Resolvers
required: false,
description: 'States of environments that should be included in result.'
+ argument :type, GraphQL::Types::String,
+ required: false,
+ description: 'Search query for environment type.'
+
type Types::EnvironmentType, null: true
alias_method :project, :object
diff --git a/app/graphql/resolvers/group_packages_resolver.rb b/app/graphql/resolvers/group_packages_resolver.rb
index e6a6abb39dd..ae578390fd5 100644
--- a/app/graphql/resolvers/group_packages_resolver.rb
+++ b/app/graphql/resolvers/group_packages_resolver.rb
@@ -13,9 +13,9 @@ module Resolvers
default_value: :created_desc
GROUP_SORT_TO_PARAMS_MAP = SORT_TO_PARAMS_MAP.merge({
- project_path_desc: { order_by: 'project_path', sort: 'desc' },
- project_path_asc: { order_by: 'project_path', sort: 'asc' }
- }).freeze
+ project_path_desc: { order_by: 'project_path', sort: 'desc' },
+ project_path_asc: { order_by: 'project_path', sort: 'asc' }
+ }).freeze
def resolve(sort:, **filters)
return unless packages_available?
diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb
index e3102a7d32a..3e61ba755d8 100644
--- a/app/graphql/resolvers/issues_resolver.rb
+++ b/app/graphql/resolvers/issues_resolver.rb
@@ -12,6 +12,11 @@ module Resolvers
# see app/graphql/types/issue_connection.rb
type 'Types::IssueConnection', null: true
+ before_connection_authorization do |nodes, current_user|
+ projects = nodes.map(&:project)
+ ::Preloaders::UserMaxAccessLevelInProjectsPreloader.new(projects, current_user).execute
+ end
+
def resolve_with_lookahead(**args)
return unless Feature.enabled?(:root_level_issues_query)
diff --git a/app/graphql/resolvers/package_details_resolver.rb b/app/graphql/resolvers/package_details_resolver.rb
index b77c6b1112b..c565fcb70e3 100644
--- a/app/graphql/resolvers/package_details_resolver.rb
+++ b/app/graphql/resolvers/package_details_resolver.rb
@@ -11,6 +11,14 @@ module Resolvers
description: 'Global ID of the package.'
def resolve(id:)
+ Gitlab::Graphql::Lazy.with_value(find_object(id: id)) do |package|
+ package if package.default?
+ end
+ end
+
+ private
+
+ def find_object(id:)
GitlabSchema.find_by_gid(id)
end
end
diff --git a/app/graphql/resolvers/package_pipelines_resolver.rb b/app/graphql/resolvers/package_pipelines_resolver.rb
index 9ff77f02547..7f610915489 100644
--- a/app/graphql/resolvers/package_pipelines_resolver.rb
+++ b/app/graphql/resolvers/package_pipelines_resolver.rb
@@ -18,7 +18,7 @@ module Resolvers
# This returns a promise for a connection of promises for pipelines:
# Lazy[Connection[Lazy[Pipeline]]] structure
- def resolve(first: nil, last: nil, after: nil, before: nil, lookahead:)
+ def resolve(lookahead:, first: nil, last: nil, after: nil, before: nil)
default_value = default_value_for(first: first, last: last, after: after, before: before)
BatchLoader::GraphQL.for(package.id)
.batch(default_value: default_value) do |package_ids, loader|
diff --git a/app/graphql/resolvers/paginated_tree_resolver.rb b/app/graphql/resolvers/paginated_tree_resolver.rb
index c7e9e522c25..6c4e978125e 100644
--- a/app/graphql/resolvers/paginated_tree_resolver.rb
+++ b/app/graphql/resolvers/paginated_tree_resolver.rb
@@ -41,7 +41,10 @@ module Resolvers
next_cursor = tree.cursor&.next_cursor
Gitlab::Graphql::ExternallyPaginatedArray.new(cursor, next_cursor, *tree)
rescue Gitlab::Git::CommandError => e
- raise Gitlab::Graphql::Errors::ArgumentError, e
+ raise Gitlab::Graphql::Errors::BaseError.new(
+ e,
+ extensions: { code: e.code, gitaly_code: e.status, service: e.service }
+ )
end
def self.field_options
diff --git a/app/graphql/resolvers/project_jobs_resolver.rb b/app/graphql/resolvers/project_jobs_resolver.rb
index 4d13a4a3fae..2bf71679dbf 100644
--- a/app/graphql/resolvers/project_jobs_resolver.rb
+++ b/app/graphql/resolvers/project_jobs_resolver.rb
@@ -14,10 +14,18 @@ module Resolvers
required: false,
description: 'Filter jobs by status.'
+ argument :with_artifacts, ::GraphQL::Types::Boolean,
+ required: false,
+ description: 'Filter by artifacts presence.'
+
alias_method :project, :object
- def resolve_with_lookahead(statuses: nil)
- jobs = ::Ci::JobsFinder.new(current_user: current_user, project: project, params: { scope: statuses }).execute
+ def resolve_with_lookahead(statuses: nil, with_artifacts: nil)
+ jobs = ::Ci::JobsFinder.new(
+ current_user: current_user, project: project, params: {
+ scope: statuses, with_artifacts: with_artifacts
+ }
+ ).execute
apply_lookahead(jobs)
end
@@ -26,7 +34,7 @@ module Resolvers
def preloads
{
- previous_stage_jobs_and_needs: [:needs, :pipeline],
+ previous_stage_jobs_or_needs: [:needs, :pipeline],
artifacts: [:job_artifacts],
pipeline: [:user]
}
diff --git a/app/graphql/resolvers/projects/fork_details_resolver.rb b/app/graphql/resolvers/projects/fork_details_resolver.rb
new file mode 100644
index 00000000000..fcc13a1bc1e
--- /dev/null
+++ b/app/graphql/resolvers/projects/fork_details_resolver.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Projects
+ class ForkDetailsResolver < BaseResolver
+ type Types::Projects::ForkDetailsType, null: true
+
+ argument :ref, GraphQL::Types::String,
+ required: false,
+ description: 'Ref of the fork. Default value is HEAD.'
+
+ alias_method :project, :object
+
+ def resolve(**args)
+ return unless project.forked?
+
+ ::Projects::Forks::DivergenceCounts.new(project, args[:ref]).counts
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/work_items/work_item_discussions_resolver.rb b/app/graphql/resolvers/work_items/work_item_discussions_resolver.rb
new file mode 100644
index 00000000000..b40d85e8003
--- /dev/null
+++ b/app/graphql/resolvers/work_items/work_item_discussions_resolver.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module WorkItems
+ class WorkItemDiscussionsResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+ extension Gitlab::Graphql::Extensions::ForwardOnlyExternallyPaginatedArrayExtension
+
+ authorize :read_work_item
+ authorizes_object!
+
+ # this resolver may be calling gitaly as part of parsing notes that contain commit references
+ calls_gitaly!
+
+ alias_method :notes_widget, :object
+
+ argument :filter, Types::WorkItems::NotesFilterTypeEnum,
+ required: false,
+ default_value: Types::WorkItems::NotesFilterTypeEnum.default_value,
+ description: 'Type of notes collection: ALL_NOTES, ONLY_COMMENTS, ONLY_ACTIVITY.'
+
+ type Types::Notes::DiscussionType.connection_type, null: true
+
+ def resolve(**args)
+ finder = Issuable::DiscussionsListService.new(current_user, work_item, params(args))
+
+ Gitlab::Graphql::ExternallyPaginatedArray.new(
+ finder.paginator.cursor_for_previous_page,
+ finder.paginator.cursor_for_next_page,
+ *finder.execute
+ )
+ end
+
+ def self.field_options
+ # we manage the pagination manually through external array, so opt out of the connection field extension
+ super.merge(connection: false)
+ end
+
+ def self.calculate_ext_conn_complexity
+ true
+ end
+
+ def self.complexity_multiplier(args)
+ 0.05
+ end
+
+ private
+
+ def work_item
+ notes_widget.work_item
+ end
+ strong_memoize_attr :work_item
+
+ def params(args)
+ {
+ notes_filter: args[:filter],
+ cursor: args[:after],
+ per_page: self.class.nodes_limit(args, @field, context: context)
+ }
+ end
+
+ def self.nodes_limit(args, field, **kwargs)
+ page_size = field&.max_page_size || kwargs[:context]&.schema&.default_max_page_size
+ [args[:first], page_size].compact.min
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/work_items_resolver.rb b/app/graphql/resolvers/work_items_resolver.rb
index 42f4f99d4a9..a3de875c196 100644
--- a/app/graphql/resolvers/work_items_resolver.rb
+++ b/app/graphql/resolvers/work_items_resolver.rb
@@ -55,6 +55,7 @@ module Resolvers
last_edited_by: :last_edited_by,
assignees: :assignees,
parent: :work_item_parent,
+ children: { work_item_children: [:author, { project: :project_feature }] },
labels: :labels,
milestone: :milestone
}
diff --git a/app/graphql/types/alert_management/alert_type.rb b/app/graphql/types/alert_management/alert_type.rb
index a0d19229d3d..a13453f9194 100644
--- a/app/graphql/types/alert_management/alert_type.rb
+++ b/app/graphql/types/alert_management/alert_type.rb
@@ -13,6 +13,11 @@ module Types
authorize :read_alert_management_alert
+ field :id,
+ GraphQL::Types::ID,
+ null: false,
+ description: 'ID of the alert.'
+
field :iid,
GraphQL::Types::ID,
null: false,
@@ -116,7 +121,10 @@ module Types
null: true,
description: 'Runbook for the alert as defined in alert details.'
- field :todos, description: 'To-do items of the current user for the alert.', resolver: Resolvers::TodosResolver
+ field :todos,
+ Types::TodoType.connection_type,
+ description: 'To-do items of the current user for the alert.',
+ resolver: Resolvers::TodosResolver
field :details_url,
GraphQL::Types::String,
diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb
index 36ba3399754..615c143a0b9 100644
--- a/app/graphql/types/base_field.rb
+++ b/app/graphql/types/base_field.rb
@@ -135,15 +135,16 @@ module Types
:resolver_complexity, args, child_complexity: child_complexity
).to_i
complexity += 1 if calls_gitaly?
- complexity += complexity * connection_complexity_multiplier(ctx, args)
+ ext_conn = resolver&.try(:calculate_ext_conn_complexity)
+ complexity += complexity * connection_complexity_multiplier(ctx, args, calculate_ext_conn_complexity: ext_conn)
complexity.to_i
end
end
- def connection_complexity_multiplier(ctx, args)
+ def connection_complexity_multiplier(ctx, args, calculate_ext_conn_complexity:)
# Resolvers may add extra complexity depending on number of items being loaded.
- return 0 unless connection?
+ return 0 if !connection? && !calculate_ext_conn_complexity
page_size = max_page_size || ctx.schema.default_max_page_size
limit_value = [args[:first], args[:last], page_size].compact.min
diff --git a/app/graphql/types/ci/config_variable_type.rb b/app/graphql/types/ci/config_variable_type.rb
index 5b5890fd5a5..020af5b2444 100644
--- a/app/graphql/types/ci/config_variable_type.rb
+++ b/app/graphql/types/ci/config_variable_type.rb
@@ -19,6 +19,7 @@ module Types
description: 'Value of the variable.'
field :value_options, [GraphQL::Types::String],
+ hash_key: :options,
null: true,
description: 'Value options for the variable.'
end
diff --git a/app/graphql/types/ci/freeze_period_status_enum.rb b/app/graphql/types/ci/freeze_period_status_enum.rb
new file mode 100644
index 00000000000..aebd0f537e9
--- /dev/null
+++ b/app/graphql/types/ci/freeze_period_status_enum.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class FreezePeriodStatusEnum < BaseEnum
+ graphql_name 'CiFreezePeriodStatus'
+ description 'Deploy freeze period status'
+
+ value 'ACTIVE', value: :active, description: 'Freeze period is active.'
+ value 'INACTIVE', value: :inactive, description: 'Freeze period is inactive.'
+ end
+ end
+end
diff --git a/app/graphql/types/ci/freeze_period_type.rb b/app/graphql/types/ci/freeze_period_type.rb
new file mode 100644
index 00000000000..6a3f2ed8fa4
--- /dev/null
+++ b/app/graphql/types/ci/freeze_period_type.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class FreezePeriodType < BaseObject
+ graphql_name 'CiFreezePeriod'
+ description 'Represents a deployment freeze window of a project'
+
+ authorize :read_freeze_period
+
+ present_using ::Ci::FreezePeriodPresenter
+
+ field :status, Types::Ci::FreezePeriodStatusEnum,
+ description: 'Freeze period status.',
+ null: false
+
+ field :start_cron, GraphQL::Types::String,
+ description: 'Start of the freeze period in cron format.',
+ null: false,
+ method: :freeze_start
+
+ field :end_cron, GraphQL::Types::String,
+ description: 'End of the freeze period in cron format.',
+ null: false,
+ method: :freeze_end
+
+ field :cron_timezone, GraphQL::Types::String,
+ description: 'Time zone for the cron fields, defaults to UTC if not provided.',
+ null: true
+
+ field :start_time, Types::TimeType,
+ description: 'Timestamp (UTC) of when the current/next active period starts.',
+ null: true
+
+ field :end_time, Types::TimeType,
+ description: 'Timestamp (UTC) of when the current/next active period ends.',
+ null: true,
+ method: :time_end_from_now
+ end
+ end
+end
diff --git a/app/graphql/types/ci/pipeline_schedule_type.rb b/app/graphql/types/ci/pipeline_schedule_type.rb
index 04f9fc78a92..904fa3f1c72 100644
--- a/app/graphql/types/ci/pipeline_schedule_type.rb
+++ b/app/graphql/types/ci/pipeline_schedule_type.rb
@@ -5,6 +5,8 @@ module Types
class PipelineScheduleType < BaseObject
graphql_name 'PipelineSchedule'
+ description 'Represents a pipeline schedule'
+
connection_type_class(Types::CountableConnectionType)
expose_permissions Types::PermissionTypes::Ci::PipelineSchedules
@@ -17,7 +19,9 @@ module Types
field :owner, ::Types::UserType, null: false, description: 'Owner of the pipeline schedule.'
- field :active, GraphQL::Types::Boolean, null: false, description: 'Indicates if a pipeline schedule is active.'
+ field :active, GraphQL::Types::Boolean, null: false, description: 'Indicates if the pipeline schedule is active.'
+
+ field :project, ::Types::ProjectType, null: true, description: 'Project of the pipeline schedule.'
field :next_run_at, Types::TimeType, null: false, description: 'Time when the next pipeline will run.'
@@ -26,20 +30,50 @@ module Types
field :last_pipeline, PipelineType, null: true, description: 'Last pipeline object.'
field :ref_for_display, GraphQL::Types::String,
- null: true, description: 'Git ref for the pipeline schedule.', method: :ref_for_display
-
- field :ref_path, GraphQL::Types::String, null: true, description: 'Path to the ref that triggered the pipeline.'
+ null: true, description: 'Git ref for the pipeline schedule.'
field :for_tag, GraphQL::Types::Boolean,
null: false, description: 'Indicates if a pipelines schedule belongs to a tag.', method: :for_tag?
- field :cron, GraphQL::Types::String, null: false, description: 'Cron notation for the schedule.'
+ field :edit_path, GraphQL::Types::String,
+ null: true,
+ description: 'Edit path of the pipeline schedule.',
+ authorize: :update_pipeline_schedule
+
+ field :variables,
+ Types::Ci::PipelineScheduleVariableType.connection_type,
+ null: true,
+ description: 'Pipeline schedule variables.',
+ authorize: :read_pipeline_schedule_variables
+
+ field :ref, GraphQL::Types::String,
+ null: true, description: 'Ref of the pipeline schedule.', method: :ref_for_display
+
+ field :ref_path, GraphQL::Types::String,
+ null: true,
+ description: 'Path to the ref that triggered the pipeline.'
- field :cron_timezone, GraphQL::Types::String, null: false, description: 'Timezone for the pipeline schedule.'
+ field :cron, GraphQL::Types::String,
+ null: false,
+ description: 'Cron notation for the schedule.'
+
+ field :cron_timezone, GraphQL::Types::String,
+ null: false,
+ description: 'Timezone for the pipeline schedule.'
+
+ field :created_at, Types::TimeType,
+ null: false, description: 'Timestamp of when the pipeline schedule was created.'
+
+ field :updated_at, Types::TimeType,
+ null: false, description: 'Timestamp of when the pipeline schedule was last updated.'
def ref_path
::Gitlab::Routing.url_helpers.project_commits_path(object.project, object.ref_for_display)
end
+
+ def edit_path
+ ::Gitlab::Routing.url_helpers.edit_project_pipeline_schedule_path(object.project, object)
+ end
end
end
end
diff --git a/app/graphql/types/ci/pipeline_schedule_variable_type.rb b/app/graphql/types/ci/pipeline_schedule_variable_type.rb
new file mode 100644
index 00000000000..1cb407bc2e4
--- /dev/null
+++ b/app/graphql/types/ci/pipeline_schedule_variable_type.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class PipelineScheduleVariableType < BaseObject
+ graphql_name 'PipelineScheduleVariable'
+
+ authorize :read_pipeline_schedule_variables
+
+ implements(VariableInterface)
+ end
+ end
+end
diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb
index 4a523f2edd9..cb561f48b3b 100644
--- a/app/graphql/types/ci/pipeline_type.rb
+++ b/app/graphql/types/ci/pipeline_type.rb
@@ -78,7 +78,7 @@ module Types
resolver: Resolvers::Ci::PipelineStagesResolver
field :user,
- type: Types::UserType,
+ type: 'Types::UserType',
null: true,
description: 'Pipeline user.'
diff --git a/app/graphql/types/ci/runner_job_execution_status_enum.rb b/app/graphql/types/ci/runner_job_execution_status_enum.rb
new file mode 100644
index 00000000000..686ea085199
--- /dev/null
+++ b/app/graphql/types/ci/runner_job_execution_status_enum.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class RunnerJobExecutionStatusEnum < BaseEnum
+ graphql_name 'CiRunnerJobExecutionStatus'
+
+ value 'IDLE',
+ description: "Runner is idle.",
+ value: :idle,
+ deprecated: { milestone: '15.7', reason: :alpha }
+
+ value 'RUNNING',
+ description: 'Runner is executing jobs.',
+ value: :running,
+ deprecated: { milestone: '15.7', reason: :alpha }
+ end
+ end
+end
diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb
index a9c76974850..5d34906f7b8 100644
--- a/app/graphql/types/ci/runner_type.rb
+++ b/app/graphql/types/ci/runner_type.rb
@@ -23,6 +23,9 @@ module Types
deprecated: { reason: 'Use paused', milestone: '14.8' }
field :admin_url, GraphQL::Types::String, null: true,
description: 'Admin URL of the runner. Only available for administrators.'
+ field :architecture_name, GraphQL::Types::String, null: true,
+ description: 'Architecture provided by the the runner.',
+ method: :architecture
field :contacted_at, Types::TimeType, null: true,
description: 'Timestamp of last contact from this runner.',
method: :contacted_at
@@ -35,32 +38,39 @@ module Types
field :executor_name, GraphQL::Types::String, null: true,
description: 'Executor last advertised by the runner.',
method: :executor_name
- field :platform_name, GraphQL::Types::String, null: true,
- description: 'Platform provided by the runner.',
- method: :platform
- field :architecture_name, GraphQL::Types::String, null: true,
- description: 'Architecture provided by the the runner.',
- method: :architecture
- field :maintenance_note, GraphQL::Types::String, null: true,
- description: 'Runner\'s maintenance notes.'
- field :groups, ::Types::GroupType.connection_type, null: true,
- description: 'Groups the runner is associated with. For group runners only.'
+ field :groups, 'Types::GroupConnection',
+ null: true,
+ resolver: ::Resolvers::Ci::RunnerGroupsResolver,
+ description: 'Groups the runner is associated with. For group runners only.'
field :id, ::Types::GlobalIDType[::Ci::Runner], null: false,
description: 'ID of the runner.'
field :ip_address, GraphQL::Types::String, null: true,
description: 'IP address of the runner.'
field :job_count, GraphQL::Types::Int, null: true,
description: "Number of jobs processed by the runner (limited to #{JOB_COUNT_LIMIT}, plus one to indicate that more items exist)."
+ field :job_execution_status,
+ Types::Ci::RunnerJobExecutionStatusEnum,
+ null: true,
+ description: 'Job execution status of the runner.',
+ deprecated: { milestone: '15.7', reason: :alpha }
field :jobs, ::Types::Ci::JobType.connection_type, null: true,
description: 'Jobs assigned to the runner. This field can only be resolved for one runner in any single request.',
authorize: :read_builds,
resolver: ::Resolvers::Ci::RunnerJobsResolver
field :locked, GraphQL::Types::Boolean, null: true,
description: 'Indicates the runner is locked.'
+ field :maintenance_note, GraphQL::Types::String, null: true,
+ description: 'Runner\'s maintenance notes.'
field :maximum_timeout, GraphQL::Types::Int, null: true,
description: 'Maximum timeout (in seconds) for jobs processed by the runner.'
+ field :owner_project, ::Types::ProjectType, null: true,
+ description: 'Project that owns the runner. For project runners only.',
+ resolver: ::Resolvers::Ci::RunnerOwnerProjectResolver
field :paused, GraphQL::Types::Boolean, null: false,
description: 'Indicates the runner is paused and not available to run jobs.'
+ field :platform_name, GraphQL::Types::String, null: true,
+ description: 'Platform provided by the runner.',
+ method: :platform
field :project_count, GraphQL::Types::Int, null: true,
description: 'Number of projects that the runner is associated with.'
field :projects,
@@ -88,9 +98,6 @@ module Types
method: :token_expires_at
field :version, GraphQL::Types::String, null: true,
description: 'Version of the runner.'
- field :owner_project, ::Types::ProjectType, null: true,
- description: 'Project that owns the runner. For project runners only.',
- resolver: ::Resolvers::Ci::RunnerOwnerProjectResolver
markdown_field :maintenance_note_html, null: true
@@ -99,8 +106,25 @@ module Types
end
def job_count
- # We limit to 1 above the JOB_COUNT_LIMIT to indicate that more items exist after JOB_COUNT_LIMIT
- runner.builds.limit(JOB_COUNT_LIMIT + 1).count
+ BatchLoader::GraphQL.for(runner.id).batch(key: :job_count) do |runner_ids, loader, _args|
+ # rubocop: disable CodeReuse/ActiveRecord
+ # We limit to 1 above the JOB_COUNT_LIMIT to indicate that more items exist after JOB_COUNT_LIMIT
+ builds_tbl = ::Ci::Build.arel_table
+ runners_tbl = ::Ci::Runner.arel_table
+ lateral_query = ::Ci::Build.select(1)
+ .where(builds_tbl['runner_id'].eq(runners_tbl['id']))
+ .limit(JOB_COUNT_LIMIT + 1)
+ counts = ::Ci::Runner.joins("JOIN LATERAL (#{lateral_query.to_sql}) builds_with_limit ON true")
+ .id_in(runner_ids)
+ .select(:id, Arel.star.count.as('count'))
+ .group(:id)
+ .index_by(&:id)
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ runner_ids.each do |runner_id|
+ loader.call(runner_id, counts[runner_id]&.count || 0)
+ end
+ end
end
def admin_url
@@ -111,14 +135,13 @@ module Types
Gitlab::Routing.url_helpers.edit_admin_runner_url(runner) if can_admin_runners?
end
- # rubocop: disable CodeReuse/ActiveRecord
def project_count
BatchLoader::GraphQL.for(runner.id).batch(key: :runner_project_count) do |ids, loader, args|
counts = ::Ci::Runner.project_type
.select(:id, 'COUNT(ci_runner_projects.id) as count')
.left_outer_joins(:runner_projects)
- .where(id: ids)
- .group(:id)
+ .id_in(ids)
+ .group(:id) # rubocop: disable CodeReuse/ActiveRecord
.index_by(&:id)
ids.each do |id|
@@ -126,12 +149,15 @@ module Types
end
end
end
- # rubocop: enable CodeReuse/ActiveRecord
- def groups
- return unless runner.group_type?
+ def job_execution_status
+ BatchLoader::GraphQL.for(runner.id).batch(key: :running_builds_exist) do |runner_ids, loader|
+ statuses = ::Ci::Runner.id_in(runner_ids).with_running_builds.index_by(&:id)
- batched_owners(::Ci::RunnerNamespace, Group, :runner_groups, :namespace_id)
+ runner_ids.each do |runner_id|
+ loader.call(runner_id, statuses[runner_id] ? :running : :idle)
+ end
+ end
end
private
@@ -139,29 +165,6 @@ module Types
def can_admin_runners?
context[:current_user]&.can_admin_all_resources?
end
-
- # rubocop: disable CodeReuse/ActiveRecord
- def batched_owners(runner_assoc_type, assoc_type, key, column_name)
- BatchLoader::GraphQL.for(runner.id).batch(key: key) do |runner_ids, loader|
- plucked_runner_and_owner_ids = runner_assoc_type
- .select(:runner_id, column_name)
- .where(runner_id: runner_ids)
- .pluck(:runner_id, column_name)
- # In plucked_runner_and_owner_ids, first() represents the runner ID, and second() the owner ID,
- # so let's group the owner IDs by runner ID
- runner_owner_ids_by_runner_id = plucked_runner_and_owner_ids
- .group_by(&:first)
- .transform_values { |runner_and_owner_id| runner_and_owner_id.map(&:second) }
-
- owner_ids = runner_owner_ids_by_runner_id.values.flatten.uniq
- owners = assoc_type.where(id: owner_ids).index_by(&:id)
-
- runner_ids.each do |runner_id|
- loader.call(runner_id, runner_owner_ids_by_runner_id[runner_id]&.map { |owner_id| owners[owner_id] } || [])
- end
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/app/graphql/types/commit_signature_interface.rb b/app/graphql/types/commit_signature_interface.rb
index 6b0c16e538a..0449a0634ef 100644
--- a/app/graphql/types/commit_signature_interface.rb
+++ b/app/graphql/types/commit_signature_interface.rb
@@ -21,7 +21,8 @@ module Types
description: 'Project of the associated commit.'
orphan_types Types::CommitSignatures::GpgSignatureType,
- Types::CommitSignatures::X509SignatureType
+ Types::CommitSignatures::X509SignatureType,
+ Types::CommitSignatures::SshSignatureType
def self.resolve_type(object, context)
case object
@@ -29,6 +30,8 @@ module Types
Types::CommitSignatures::GpgSignatureType
when ::CommitSignatures::X509CommitSignature
Types::CommitSignatures::X509SignatureType
+ when ::CommitSignatures::SshSignature
+ Types::CommitSignatures::SshSignatureType
else
raise 'Unsupported commit signature type'
end
diff --git a/app/graphql/types/commit_signatures/gpg_signature_type.rb b/app/graphql/types/commit_signatures/gpg_signature_type.rb
index 2a845fff3e2..3baf2d9d21d 100644
--- a/app/graphql/types/commit_signatures/gpg_signature_type.rb
+++ b/app/graphql/types/commit_signatures/gpg_signature_type.rb
@@ -11,6 +11,7 @@ module Types
authorize :download_code
field :user, Types::UserType, null: true,
+ method: :signed_by_user,
description: 'User associated with the key.'
field :gpg_key_user_name, GraphQL::Types::String,
diff --git a/app/graphql/types/commit_signatures/ssh_signature_type.rb b/app/graphql/types/commit_signatures/ssh_signature_type.rb
new file mode 100644
index 00000000000..92eb4f7949a
--- /dev/null
+++ b/app/graphql/types/commit_signatures/ssh_signature_type.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Types
+ module CommitSignatures
+ class SshSignatureType < Types::BaseObject
+ graphql_name 'SshSignature'
+ description 'SSH signature for a signed commit'
+
+ implements Types::CommitSignatureInterface
+
+ authorize :download_code
+
+ field :user, Types::UserType, null: true,
+ method: :signed_by_user,
+ calls_gitaly: true,
+ description: 'User associated with the key.'
+
+ field :key, Types::KeyType,
+ null: true,
+ description: 'SSH key used for the signature.'
+ end
+ end
+end
diff --git a/app/graphql/types/commit_signatures/x509_signature_type.rb b/app/graphql/types/commit_signatures/x509_signature_type.rb
index 9ac96dbc015..2d58c3d5b5d 100644
--- a/app/graphql/types/commit_signatures/x509_signature_type.rb
+++ b/app/graphql/types/commit_signatures/x509_signature_type.rb
@@ -11,6 +11,7 @@ module Types
authorize :download_code
field :user, Types::UserType, null: true,
+ method: :signed_by_user,
calls_gitaly: true,
description: 'User associated with the key.'
diff --git a/app/graphql/types/container_repository_type.rb b/app/graphql/types/container_repository_type.rb
index cb818ac5e92..dfa599e798c 100644
--- a/app/graphql/types/container_repository_type.rb
+++ b/app/graphql/types/container_repository_type.rb
@@ -13,6 +13,7 @@ module Types
field :expiration_policy_cleanup_status, Types::ContainerRepositoryCleanupStatusEnum, null: true, description: 'Tags cleanup status for the container repository.'
field :expiration_policy_started_at, Types::TimeType, null: true, description: 'Timestamp when the cleanup done by the expiration policy was started on the container repository.'
field :id, GraphQL::Types::ID, null: false, description: 'ID of the container repository.'
+ field :last_cleanup_deleted_tags_count, GraphQL::Types::Int, null: true, description: 'Number of deleted tags from the last cleanup.'
field :location, GraphQL::Types::String, null: false, description: 'URL of the container repository.'
field :migration_state, GraphQL::Types::String, null: false, description: 'Migration state of the container repository.'
field :name, GraphQL::Types::String, null: false, description: 'Name of the container repository.'
@@ -21,7 +22,6 @@ module Types
field :status, Types::ContainerRepositoryStatusEnum, null: true, description: 'Status of the container repository.'
field :tags_count, GraphQL::Types::Int, null: false, description: 'Number of tags associated with this image.'
field :updated_at, Types::TimeType, null: false, description: 'Timestamp when the container repository was updated.'
- field :last_cleanup_deleted_tags_count, GraphQL::Types::Int, null: true, description: 'Number of deleted tags from the last cleanup.'
def can_delete
Ability.allowed?(current_user, :update_container_image, object)
diff --git a/app/graphql/types/dependency_proxy/manifest_type.rb b/app/graphql/types/dependency_proxy/manifest_type.rb
index f7e751e30d3..53b7610e490 100644
--- a/app/graphql/types/dependency_proxy/manifest_type.rb
+++ b/app/graphql/types/dependency_proxy/manifest_type.rb
@@ -14,11 +14,11 @@ module Types
field :id, ::Types::GlobalIDType[::DependencyProxy::Manifest], null: false, description: 'ID of the manifest.'
field :image_name, GraphQL::Types::String, null: false, description: 'Name of the image.'
field :size, GraphQL::Types::String, null: false, description: 'Size of the manifest file.'
- field :updated_at, Types::TimeType, null: false, description: 'Date of most recent update.'
field :status,
Types::DependencyProxy::ManifestTypeEnum,
null: false,
description: "Status of the manifest (#{::DependencyProxy::Manifest.statuses.keys.join(', ')})"
+ field :updated_at, Types::TimeType, null: false, description: 'Date of most recent update.'
def image_name
object.file_name.chomp(File.extname(object.file_name))
diff --git a/app/graphql/types/deployment_details_type.rb b/app/graphql/types/deployment_details_type.rb
deleted file mode 100644
index bbb5cc8e3f1..00000000000
--- a/app/graphql/types/deployment_details_type.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- class DeploymentDetailsType < DeploymentType
- graphql_name 'DeploymentDetails'
- description 'The details of the deployment'
- authorize :read_deployment
- present_using ::Deployments::DeploymentPresenter
-
- field :tags,
- [Types::DeploymentTagType],
- description: 'Git tags that contain this deployment.',
- calls_gitaly: true
- end
-end
-
-Types::DeploymentDetailsType.prepend_mod_with('Types::DeploymentDetailsType')
diff --git a/app/graphql/types/deployment_type.rb b/app/graphql/types/deployment_type.rb
index 59b59dc4e1d..1c23fd44ea1 100644
--- a/app/graphql/types/deployment_type.rb
+++ b/app/graphql/types/deployment_type.rb
@@ -1,12 +1,6 @@
# frozen_string_literal: true
module Types
- # If you're considering to add a new field in DeploymentType, please follow this guideline:
- # - If the field is preloadable in batch, define it in DeploymentType.
- # In this case, you should extend DeploymentsResolver logic to preload the field. Also, add a new test that
- # fetching the specific field for multiple deployments doesn't cause N+1 query problem.
- # - If the field is NOT preloadable in batch, define it in DeploymentDetailsType.
- # This type can be only fetched for a single deployment, so you don't need to take care of the preloading.
class DeploymentType < BaseObject
graphql_name 'Deployment'
description 'The deployment of an environment'
@@ -15,6 +9,8 @@ module Types
authorize :read_deployment
+ expose_permissions Types::PermissionTypes::Deployment
+
field :id,
GraphQL::Types::ID,
description: 'Global ID of the deployment.'
@@ -65,5 +61,15 @@ module Types
Types::UserType,
description: 'User who executed the deployment.',
method: :deployed_by
+
+ field :tags,
+ [Types::DeploymentTagType],
+ description: 'Git tags that contain this deployment. ' \
+ 'This field can only be resolved for one deployment in any single request.',
+ calls_gitaly: true do
+ extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
+ end
end
end
+
+Types::DeploymentType.prepend_mod_with('Types::DeploymentType')
diff --git a/app/graphql/types/environment_type.rb b/app/graphql/types/environment_type.rb
index dd2286d333d..5f58fc38540 100644
--- a/app/graphql/types/environment_type.rb
+++ b/app/graphql/types/environment_type.rb
@@ -9,6 +9,12 @@ module Types
authorize :read_environment
+ expose_permissions Types::PermissionTypes::Environment,
+ description: 'Permissions for the current user on the resource. '\
+ 'This field can only be resolved for one environment in any single request.' do
+ extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
+ end
+
field :name, GraphQL::Types::String, null: false,
description: 'Human-readable name of the environment.'
@@ -67,6 +73,11 @@ module Types
description: 'Last deployment of the environment.',
resolver: Resolvers::Environments::LastDeploymentResolver
+ field :deploy_freezes,
+ [Types::Ci::FreezePeriodType],
+ null: true,
+ description: 'Deployment freeze periods of the environment.'
+
def tier
object.tier.to_sym
end
diff --git a/app/graphql/types/global_id_type.rb b/app/graphql/types/global_id_type.rb
index a71c2fb0e6c..7ebd98ff2e7 100644
--- a/app/graphql/types/global_id_type.rb
+++ b/app/graphql/types/global_id_type.rb
@@ -49,9 +49,7 @@ module Types
An example `#{graphql_name}` is: `"#{::Gitlab::GlobalId.build(model_name: model_name, id: 1)}"`.
#{
if deprecation = Gitlab::GlobalId::Deprecations.deprecation_by(model_name)
- 'The older format `"' +
- ::Gitlab::GlobalId.build(model_name: deprecation.old_name, id: 1).to_s +
- '"` was deprecated in ' + deprecation.milestone + '.'
+ "The older format `\"#{::Gitlab::GlobalId.build(model_name: deprecation.old_name, id: 1)}\"` was deprecated in #{deprecation.milestone}."
end}
MD
diff --git a/app/graphql/types/group_connection.rb b/app/graphql/types/group_connection.rb
new file mode 100644
index 00000000000..e4332e24302
--- /dev/null
+++ b/app/graphql/types/group_connection.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+# Normally this wouldn't be needed and we could use
+#
+# type Types::GroupType.connection_type, null: true
+#
+# in a resolver. However we can end up with cyclic definitions.
+# Running the spec locally can result in errors like
+#
+# NameError: uninitialized constant Types::GroupType
+#
+# or other errors. To fix this, we created this file and use
+#
+# type "Types::GroupConnection", null: true
+#
+# which gives a delayed resolution, and the proper connection type.
+#
+# See gitlab/app/graphql/types/ci/runner_type.rb
+# Reference: https://github.com/rmosolgo/graphql-ruby/issues/3974#issuecomment-1084444214
+# and https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#testing-tips-and-tricks
+#
+Types::GroupConnection = Types::GroupType.connection_type
diff --git a/app/graphql/types/issue_type_enum.rb b/app/graphql/types/issue_type_enum.rb
index 78cd27f60c3..d7f587ff03d 100644
--- a/app/graphql/types/issue_type_enum.rb
+++ b/app/graphql/types/issue_type_enum.rb
@@ -16,5 +16,9 @@ module Types
value 'OBJECTIVE', value: 'objective',
description: 'Objective issue type. Available only when feature flag `okrs_mvc` is enabled.',
alpha: { milestone: '15.6' }
+
+ value 'KEY_RESULT', value: 'key_result',
+ description: 'Key Result issue type. Available only when feature flag `okrs_mvc` is enabled.',
+ alpha: { milestone: '15.7' }
end
end
diff --git a/app/graphql/types/key_type.rb b/app/graphql/types/key_type.rb
new file mode 100644
index 00000000000..30699793045
--- /dev/null
+++ b/app/graphql/types/key_type.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Types
+ class KeyType < Types::BaseObject # rubocop:disable Graphql/AuthorizeTypes
+ graphql_name 'Key'
+ description 'Represents an SSH key.'
+
+ field :created_at, Types::TimeType, null: false,
+ description: 'Timestamp of when the key was created.'
+ field :expires_at, Types::TimeType, null: false,
+ description: "Timestamp of when the key expires. It's null if it never expires."
+ field :id, GraphQL::Types::ID, null: false, description: 'ID of the key.'
+ field :key, GraphQL::Types::String, null: false, method: :publishable_key,
+ description: 'Public key of the key pair.'
+ field :title, GraphQL::Types::String, null: false, description: 'Title of the key.'
+ end
+end
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index 49bf7aa638c..abf7b3ad530 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -200,10 +200,10 @@ module Types
description: 'Array of available auto merge strategies.'
field :commits, Types::CommitType.connection_type, null: true,
calls_gitaly: true, description: 'Merge request commits.'
- field :committers, Types::UserType.connection_type, null: true, complexity: 5,
- calls_gitaly: true, description: 'Users who have added commits to the merge request.'
field :commits_without_merge_commits, Types::CommitType.connection_type, null: true,
calls_gitaly: true, description: 'Merge request commits excluding merge commits.'
+ field :committers, Types::UserType.connection_type, null: true, complexity: 5,
+ calls_gitaly: true, description: 'Users who have added commits to the merge request.'
field :has_ci, GraphQL::Types::Boolean, null: false, method: :has_ci?,
description: 'Indicates if the merge request has CI.'
field :merge_user, Types::UserType, null: true,
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 1cbb2ede544..b342e57804b 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -63,6 +63,8 @@ module Types
mount_mutation Mutations::Issues::SetEscalationStatus
mount_mutation Mutations::Issues::Update
mount_mutation Mutations::Issues::Move
+ mount_mutation Mutations::Issues::LinkAlerts
+ mount_mutation Mutations::Issues::UnlinkAlert
mount_mutation Mutations::Labels::Create
mount_mutation Mutations::MergeRequests::Accept
mount_mutation Mutations::MergeRequests::Create
@@ -117,6 +119,8 @@ module Types
mount_mutation Mutations::Ci::Pipeline::Retry
mount_mutation Mutations::Ci::PipelineSchedule::Delete
mount_mutation Mutations::Ci::PipelineSchedule::TakeOwnership
+ mount_mutation Mutations::Ci::PipelineSchedule::Play
+ mount_mutation Mutations::Ci::PipelineSchedule::Create
mount_mutation Mutations::Ci::CiCdSettingsUpdate, deprecated: {
reason: :renamed,
replacement: 'ProjectCiCdSettingsUpdate',
diff --git a/app/graphql/types/nested_environment_type.rb b/app/graphql/types/nested_environment_type.rb
new file mode 100644
index 00000000000..b835af2bf45
--- /dev/null
+++ b/app/graphql/types/nested_environment_type.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Types
+ # rubocop: disable Graphql/AuthorizeTypes
+ class NestedEnvironmentType < BaseObject
+ graphql_name 'NestedEnvironment'
+ description 'Describes where code is deployed for a project organized by folder.'
+
+ field :name, GraphQL::Types::String,
+ null: false, description: 'Human-readable name of the environment.'
+
+ field :size, GraphQL::Types::Int,
+ null: false, description: 'Number of environments nested in the folder.'
+
+ field :environment,
+ Types::EnvironmentType,
+ null: true, description: 'Latest environment in the folder.'
+
+ def environment
+ BatchLoader::GraphQL.for(object.last_id).batch do |environment_ids, loader|
+ Environment.id_in(environment_ids).each do |environment|
+ loader.call(environment.id, environment)
+ end
+ end
+ end
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+end
diff --git a/app/graphql/types/notes/note_type.rb b/app/graphql/types/notes/note_type.rb
index eef5ce40bde..05629ea9223 100644
--- a/app/graphql/types/notes/note_type.rb
+++ b/app/graphql/types/notes/note_type.rb
@@ -77,6 +77,14 @@ module Types
def author
Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find
end
+
+ # We now support also SyntheticNote notes as a NoteType, but SyntheticNote does not have a real note ID,
+ # as SyntheticNote is generated dynamically from a ResourceEvent instance.
+ def id
+ return super unless object.is_a?(SyntheticNote)
+
+ ::Gitlab::GlobalId.build(object, model_name: object.class.to_s, id: object.discussion_id)
+ end
end
end
end
diff --git a/app/graphql/types/packages/package_links_type.rb b/app/graphql/types/packages/package_links_type.rb
index f16937530b9..eb29fb655bd 100644
--- a/app/graphql/types/packages/package_links_type.rb
+++ b/app/graphql/types/packages/package_links_type.rb
@@ -12,6 +12,8 @@ module Types
field :web_path, GraphQL::Types::String, null: true, description: 'Path to the package details page.'
def web_path
+ return unless object.default?
+
package_path(object)
end
end
diff --git a/app/graphql/types/permission_types/base_permission_type.rb b/app/graphql/types/permission_types/base_permission_type.rb
index 07e6e7a55d6..0192af25d0f 100644
--- a/app/graphql/types/permission_types/base_permission_type.rb
+++ b/app/graphql/types/permission_types/base_permission_type.rb
@@ -11,20 +11,20 @@ module Types
abilities.each { |ability| ability_field(ability) }
end
- def self.ability_field(ability, **kword_args)
+ def self.ability_field(ability, **kword_args, &block)
define_field_resolver_method(ability) unless resolving_keywords?(kword_args)
- permission_field(ability, **kword_args)
+ permission_field(ability, **kword_args, &block)
end
- def self.permission_field(name, **kword_args)
+ def self.permission_field(name, **kword_args, &block)
kword_args = kword_args.reverse_merge(
name: name,
type: GraphQL::Types::Boolean,
description: "Indicates the user can perform `#{name}` on this resource",
null: false)
- field(**kword_args) # rubocop:disable Graphql/Descriptions
+ field(**kword_args, &block) # rubocop:disable Graphql/Descriptions
end
def self.define_field_resolver_method(ability)
diff --git a/app/graphql/types/permission_types/deployment.rb b/app/graphql/types/permission_types/deployment.rb
new file mode 100644
index 00000000000..fce376552b1
--- /dev/null
+++ b/app/graphql/types/permission_types/deployment.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Types
+ module PermissionTypes
+ class Deployment < BasePermissionType
+ graphql_name 'DeploymentPermissions'
+
+ abilities :destroy_deployment
+ ability_field :update_deployment, calls_gitaly: true
+ end
+ end
+end
+
+Types::PermissionTypes::Deployment.prepend_mod_with('Types::PermissionTypes::Deployment')
diff --git a/app/graphql/types/permission_types/environment.rb b/app/graphql/types/permission_types/environment.rb
new file mode 100644
index 00000000000..59c9fce64e5
--- /dev/null
+++ b/app/graphql/types/permission_types/environment.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Types
+ module PermissionTypes
+ class Environment < BasePermissionType
+ graphql_name 'EnvironmentPermissions'
+
+ abilities :update_environment, :destroy_environment, :stop_environment
+ end
+ end
+end
diff --git a/app/graphql/types/permission_types/project.rb b/app/graphql/types/permission_types/project.rb
index f6a5563d367..c833b512222 100644
--- a/app/graphql/types/permission_types/project.rb
+++ b/app/graphql/types/permission_types/project.rb
@@ -17,7 +17,8 @@ module Types
:admin_wiki, :admin_project, :update_pages,
:admin_remote_mirror, :create_label, :update_wiki, :destroy_wiki,
:create_pages, :destroy_pages, :read_pages_content, :admin_operations,
- :read_merge_request, :read_design, :create_design, :destroy_design
+ :read_merge_request, :read_design, :create_design, :destroy_design,
+ :read_environment
permission_field :create_snippet
diff --git a/app/graphql/types/project_statistics_type.rb b/app/graphql/types/project_statistics_type.rb
index c43baf1280b..a1d721856a9 100644
--- a/app/graphql/types/project_statistics_type.rb
+++ b/app/graphql/types/project_statistics_type.rb
@@ -11,6 +11,10 @@ module Types
field :build_artifacts_size, GraphQL::Types::Float, null: false,
description: 'Build artifacts size of the project in bytes.'
+ field :container_registry_size,
+ GraphQL::Types::Float,
+ null: true,
+ description: 'Container Registry size of the project in bytes.'
field :lfs_objects_size,
GraphQL::Types::Float,
null: false,
@@ -29,9 +33,5 @@ module Types
description: 'Uploads size of the project in bytes.'
field :wiki_size, GraphQL::Types::Float, null: true,
description: 'Wiki size of the project in bytes.'
- field :container_registry_size,
- GraphQL::Types::Float,
- null: true,
- description: 'Container Registry size of the project in bytes.'
end
end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 771dad00fb3..fe13ee7ef3c 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -258,8 +258,11 @@ module Types
field :environments,
Types::EnvironmentType.connection_type,
null: true,
- description: 'Environments of the project.',
- resolver: Resolvers::EnvironmentsResolver
+ description: 'Environments of the project. ' \
+ 'This field can only be resolved for one project in any single request.',
+ resolver: Resolvers::EnvironmentsResolver do
+ extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
+ end
field :environment,
Types::EnvironmentType,
@@ -267,8 +270,18 @@ module Types
description: 'A single environment of the project.',
resolver: Resolvers::EnvironmentsResolver.single
+ field :nested_environments,
+ Types::NestedEnvironmentType.connection_type,
+ null: true,
+ calls_gitaly: true,
+ description: 'Environments for this project with nested folders, ' \
+ 'can only be resolved for one project in any single request',
+ resolver: Resolvers::Environments::NestedEnvironmentsResolver do
+ extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
+ end
+
field :deployment,
- Types::DeploymentDetailsType,
+ Types::DeploymentType,
null: true,
description: 'Details of the deployment of the project.',
resolver: Resolvers::DeploymentResolver.single
@@ -526,6 +539,13 @@ module Types
resolver: Resolvers::Projects::ForkTargetsResolver,
description: 'Namespaces in which the current user can fork the project into.'
+ field :fork_details, Types::Projects::ForkDetailsType,
+ calls_gitaly: true,
+ alpha: { milestone: '15.7' },
+ authorize: :read_code,
+ resolver: Resolvers::Projects::ForkDetailsResolver,
+ description: 'Details of the fork project compared to its upstream project.'
+
field :branch_rules,
Types::Projects::BranchRuleType.connection_type,
null: true,
@@ -537,6 +557,11 @@ module Types
description: "Programming languages used in the project.",
calls_gitaly: true
+ field :runners, Types::Ci::RunnerType.connection_type,
+ null: true,
+ resolver: ::Resolvers::Ci::ProjectRunnersResolver,
+ description: "Find runners visible to the current user."
+
def timelog_categories
object.project_namespace.timelog_categories if Feature.enabled?(:timelog_categories)
end
diff --git a/app/graphql/types/projects/fork_details_type.rb b/app/graphql/types/projects/fork_details_type.rb
new file mode 100644
index 00000000000..88c17d89620
--- /dev/null
+++ b/app/graphql/types/projects/fork_details_type.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Types
+ module Projects
+ # rubocop: disable Graphql/AuthorizeTypes
+ class ForkDetailsType < BaseObject
+ graphql_name 'ForkDetails'
+ description 'Details of the fork project compared to its upstream project.'
+
+ field :ahead, GraphQL::Types::Int,
+ null: true,
+ description: 'Number of commits ahead of upstream.'
+
+ field :behind, GraphQL::Types::Int,
+ null: true,
+ description: 'Number of commits behind upstream.'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 21cb3f9e06c..7263f792bae 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -67,7 +67,7 @@ module Types
end
field :package,
- description: 'Find a package. This field can only be resolved for one query in any single request.',
+ description: 'Find a package. This field can only be resolved for one query in any single request. Returns `null` if a package has no `default` status.',
resolver: Resolvers::PackageDetailsResolver
field :user, Types::UserType,
diff --git a/app/graphql/types/release_type.rb b/app/graphql/types/release_type.rb
index a20e53ad1bd..8516256b433 100644
--- a/app/graphql/types/release_type.rb
+++ b/app/graphql/types/release_type.rb
@@ -13,9 +13,6 @@ module Types
present_using ReleasePresenter
- field :id, ::Types::GlobalIDType[Release],
- null: false,
- description: 'Global ID of the release.'
field :assets, Types::ReleaseAssetsType, null: true, method: :itself,
description: 'Assets of the release.'
field :created_at, Types::TimeType, null: true,
@@ -26,6 +23,11 @@ module Types
description: 'Description (also known as "release notes") of the release.'
field :evidences, Types::EvidenceType.connection_type, null: true,
description: 'Evidence for the release.'
+ field :historical_release, GraphQL::Types::Boolean, null: true, method: :historical_release?,
+ description: 'Indicates the release is an historical release.'
+ field :id, ::Types::GlobalIDType[Release],
+ null: false,
+ description: 'Global ID of the release.'
field :links, Types::ReleaseLinksType, null: true, method: :itself,
description: 'Links of the release.'
field :milestones, Types::MilestoneType.connection_type, null: true,
@@ -42,8 +44,6 @@ module Types
authorize: :read_code
field :upcoming_release, GraphQL::Types::Boolean, null: true, method: :upcoming_release?,
description: 'Indicates the release is an upcoming release.'
- field :historical_release, GraphQL::Types::Boolean, null: true, method: :historical_release?,
- description: 'Indicates the release is an historical release.'
field :author, Types::UserType, null: true,
description: 'User that created the release.'
diff --git a/app/graphql/types/root_storage_statistics_type.rb b/app/graphql/types/root_storage_statistics_type.rb
index b1b712aab38..64aaf3e73a0 100644
--- a/app/graphql/types/root_storage_statistics_type.rb
+++ b/app/graphql/types/root_storage_statistics_type.rb
@@ -7,6 +7,7 @@ module Types
authorize :read_statistics
field :build_artifacts_size, GraphQL::Types::Float, null: false, description: 'CI artifacts size in bytes.'
+ field :container_registry_size, GraphQL::Types::Float, null: false, description: 'Container Registry size in bytes.'
field :dependency_proxy_size, GraphQL::Types::Float, null: false, description: 'Dependency Proxy sizes in bytes.'
field :lfs_objects_size, GraphQL::Types::Float, null: false, description: 'LFS objects size in bytes.'
field :packages_size, GraphQL::Types::Float, null: false, description: 'Packages size in bytes.'
@@ -16,6 +17,5 @@ module Types
field :storage_size, GraphQL::Types::Float, null: false, description: 'Total storage in bytes.'
field :uploads_size, GraphQL::Types::Float, null: false, description: 'Uploads size in bytes.'
field :wiki_size, GraphQL::Types::Float, null: false, description: 'Wiki size in bytes.'
- field :container_registry_size, GraphQL::Types::Float, null: false, description: 'Container Registry size in bytes.'
end
end
diff --git a/app/graphql/types/subscription_type.rb b/app/graphql/types/subscription_type.rb
index 9d5edec82b2..f7f26ba4c5a 100644
--- a/app/graphql/types/subscription_type.rb
+++ b/app/graphql/types/subscription_type.rb
@@ -34,6 +34,11 @@ module Types
subscription: Subscriptions::IssuableUpdated,
null: true,
description: 'Triggered when the merge status of a merge request is updated.'
+
+ field :merge_request_approval_state_updated,
+ subscription: Subscriptions::IssuableUpdated,
+ null: true,
+ description: 'Triggered when approval state of a merge request is updated.'
end
end
diff --git a/app/graphql/types/todo_action_enum.rb b/app/graphql/types/todo_action_enum.rb
index ef43b6eb464..33e1c4e98a4 100644
--- a/app/graphql/types/todo_action_enum.rb
+++ b/app/graphql/types/todo_action_enum.rb
@@ -5,11 +5,12 @@ module Types
value 'assigned', value: 1, description: 'User was assigned.'
value 'mentioned', value: 2, description: 'User was mentioned.'
value 'build_failed', value: 3, description: 'Build triggered by the user failed.'
- value 'marked', value: 4, description: 'User added a TODO.'
+ value 'marked', value: 4, description: 'User added a to-do item.'
value 'approval_required', value: 5, description: 'User was set as an approver.'
value 'unmergeable', value: 6, description: 'Merge request authored by the user could not be merged.'
value 'directly_addressed', value: 7, description: 'User was directly addressed.'
value 'merge_train_removed', value: 8, description: 'Merge request authored by the user was removed from the merge train.'
value 'review_requested', value: 9, description: 'Review was requested from the user.'
+ value 'member_access_requested', value: 10, description: 'Group access requested from the user.'
end
end
diff --git a/app/graphql/types/todo_type.rb b/app/graphql/types/todo_type.rb
index 0de6b1d6f8a..6e5ce35033b 100644
--- a/app/graphql/types/todo_type.rb
+++ b/app/graphql/types/todo_type.rb
@@ -15,13 +15,11 @@ module Types
field :project, Types::ProjectType,
description: 'Project this to-do item is associated with.',
- null: true,
- authorize: :read_project
+ null: true
field :group, 'Types::GroupType',
description: 'Group this to-do item is associated with.',
- null: true,
- authorize: :read_group
+ null: true
field :author, Types::UserType,
description: 'Author of this to-do item.',
diff --git a/app/graphql/types/user_interface.rb b/app/graphql/types/user_interface.rb
index f49b3eee4f5..51046d09f90 100644
--- a/app/graphql/types/user_interface.rb
+++ b/app/graphql/types/user_interface.rb
@@ -88,7 +88,10 @@ module Types
null: true,
description: 'Personal namespace of the user.'
- field :todos, resolver: Resolvers::TodosResolver, description: 'To-do items of the user.'
+ field :todos,
+ Types::TodoType.connection_type,
+ description: 'To-do items of the user.',
+ resolver: Resolvers::TodosResolver
# Merge request field: MRs can be authored, assigned, or assigned-for-review:
field :authored_merge_requests,
diff --git a/app/graphql/types/work_items/notes_filter_type_enum.rb b/app/graphql/types/work_items/notes_filter_type_enum.rb
new file mode 100644
index 00000000000..93fb4689f0b
--- /dev/null
+++ b/app/graphql/types/work_items/notes_filter_type_enum.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Types
+ module WorkItems
+ class NotesFilterTypeEnum < BaseEnum
+ graphql_name 'NotesFilterType'
+ description 'Work item notes collection type.'
+
+ ::UserPreference::NOTES_FILTERS.each_pair do |key, value|
+ value key.upcase,
+ value: value,
+ description: UserPreference.notes_filters.invert[::UserPreference::NOTES_FILTERS[key]]
+ end
+
+ def self.default_value
+ ::UserPreference::NOTES_FILTERS[:all_notes]
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/work_items/widget_interface.rb b/app/graphql/types/work_items/widget_interface.rb
index b85d0a23535..672a78f12e1 100644
--- a/app/graphql/types/work_items/widget_interface.rb
+++ b/app/graphql/types/work_items/widget_interface.rb
@@ -17,7 +17,8 @@ module Types
::Types::WorkItems::Widgets::LabelsType,
::Types::WorkItems::Widgets::AssigneesType,
::Types::WorkItems::Widgets::StartAndDueDateType,
- ::Types::WorkItems::Widgets::MilestoneType
+ ::Types::WorkItems::Widgets::MilestoneType,
+ ::Types::WorkItems::Widgets::NotesType
].freeze
def self.ce_orphan_types
@@ -41,6 +42,8 @@ module Types
::Types::WorkItems::Widgets::StartAndDueDateType
when ::WorkItems::Widgets::Milestone
::Types::WorkItems::Widgets::MilestoneType
+ when ::WorkItems::Widgets::Notes
+ ::Types::WorkItems::Widgets::NotesType
else
raise "Unknown GraphQL type for widget #{object}"
end
diff --git a/app/graphql/types/work_items/widgets/hierarchy_type.rb b/app/graphql/types/work_items/widgets/hierarchy_type.rb
index 0ccd8af7dc8..4ec8ec84779 100644
--- a/app/graphql/types/work_items/widgets/hierarchy_type.rb
+++ b/app/graphql/types/work_items/widgets/hierarchy_type.rb
@@ -20,8 +20,29 @@ module Types
null: true, complexity: 5,
description: 'Child work items.'
+ field :has_children, GraphQL::Types::Boolean,
+ null: false, description: 'Indicates if the work item has children.'
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def has_children?
+ BatchLoader::GraphQL.for(object.work_item.id).batch(default_value: false) do |ids, loader|
+ links_for_parents = ::WorkItems::ParentLink.for_parents(ids)
+ .select(:work_item_parent_id)
+ .group(:work_item_parent_id)
+ .reorder(nil)
+
+ links_for_parents.each { |link| loader.call(link.work_item_parent_id, true) }
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ alias_method :has_children, :has_children?
+
def children
- object.children.inc_relations_for_permission_check
+ relation = object.children
+ relation = relation.inc_relations_for_permission_check unless object.children.loaded?
+
+ relation
end
end
# rubocop:enable Graphql/AuthorizeTypes
diff --git a/app/graphql/types/work_items/widgets/notes_type.rb b/app/graphql/types/work_items/widgets/notes_type.rb
new file mode 100644
index 00000000000..7da2777beee
--- /dev/null
+++ b/app/graphql/types/work_items/widgets/notes_type.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Types
+ module WorkItems
+ module Widgets
+ # Disabling widget level authorization as it might be too granular
+ # and we already authorize the parent work item
+ # rubocop:disable Graphql/AuthorizeTypes
+ class NotesType < BaseObject
+ graphql_name 'WorkItemWidgetNotes'
+ description 'Represents a notes widget'
+
+ implements Types::WorkItems::WidgetInterface
+
+ # This field loads user comments, system notes and resource events as a discussion for an work item,
+ # raising the complexity considerably. In order to discourage fetching this field as part of fetching
+ # a list of issues we raise the complexity
+ field :discussions, Types::Notes::DiscussionType.connection_type,
+ null: true,
+ description: "Notes on this work item.",
+ resolver: Resolvers::WorkItems::WorkItemDiscussionsResolver
+ end
+ # rubocop:enable Graphql/AuthorizeTypes
+ end
+ end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 340f3d45365..c78563a9a5f 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -124,7 +124,7 @@ module ApplicationHelper
end
def simple_sanitize(str)
- sanitize(str, tags: %w(a span))
+ sanitize(str, tags: %w[a span])
end
def body_data
@@ -187,14 +187,14 @@ module ApplicationHelper
css_classes << html_class unless html_class.blank?
content_tag :time, l(time, format: "%b %d, %Y"),
- class: css_classes.join(' '),
- title: l(time.to_time.in_time_zone, format: :timeago_tooltip),
- datetime: time.to_time.getutc.iso8601,
- data: {
- toggle: 'tooltip',
- placement: placement,
- container: 'body'
- }
+ class: css_classes.join(' '),
+ title: l(time.to_time.in_time_zone, format: :timeago_tooltip),
+ datetime: time.to_time.getutc.iso8601,
+ data: {
+ toggle: 'tooltip',
+ placement: placement,
+ container: 'body'
+ }
end
def edited_time_ago_with_tooltip(object, placement: 'top', html_class: 'time_ago', exclude_author: false)
@@ -234,11 +234,11 @@ module ApplicationHelper
end
def promo_url
- 'https://' + promo_host
+ "https://#{promo_host}"
end
def support_url
- Gitlab::CurrentSettings.current_application_settings.help_page_support_url.presence || promo_url + '/getting-help/'
+ Gitlab::CurrentSettings.current_application_settings.help_page_support_url.presence || "#{promo_url}/getting-help/"
end
def instance_review_permitted?
@@ -279,7 +279,19 @@ module ApplicationHelper
end
def stylesheet_link_tag_defer(path)
- stylesheet_link_tag(path, media: "print", crossorigin: ActionController::Base.asset_host ? 'anonymous' : nil)
+ if startup_css_enabled?
+ stylesheet_link_tag(path, media: "print", crossorigin: ActionController::Base.asset_host ? 'anonymous' : nil)
+ else
+ stylesheet_link_tag(path, crossorigin: ActionController::Base.asset_host ? 'anonymous' : nil)
+ end
+ end
+
+ def startup_css_enabled?
+ !params.has_key?(:no_startup_css)
+ end
+
+ def use_new_fonts?
+ Feature.enabled?(:new_fonts, current_user) || request.params.has_key?(:new_fonts)
end
def outdated_browser?
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 7f13f609353..2b2ac262848 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -216,6 +216,7 @@ module ApplicationSettingsHelper
:default_branch_protection,
:default_ci_config_path,
:default_group_visibility,
+ :default_preferred_language,
:default_project_creation,
:default_project_visibility,
:default_projects_limit,
@@ -287,6 +288,7 @@ module ApplicationSettingsHelper
:max_import_size,
:max_pages_size,
:max_pages_custom_domains_per_project,
+ :max_terraform_state_size_bytes,
:max_yaml_size_bytes,
:max_yaml_depth,
:metrics_method_call_threshold,
@@ -318,7 +320,6 @@ module ApplicationSettingsHelper
:require_two_factor_authentication,
:restricted_visibility_levels,
:rsa_key_restriction,
- :send_user_confirmation_email,
:session_expire_delay,
:shared_runners_enabled,
:shared_runners_text,
@@ -445,7 +446,8 @@ module ApplicationSettingsHelper
:project_runner_token_expiration_interval,
:pipeline_limit_per_project_user_sha,
:invitation_flow_enforcement,
- :can_create_group
+ :can_create_group,
+ :bulk_import_enabled
].tap do |settings|
next if Gitlab.com?
@@ -545,7 +547,6 @@ module ApplicationSettingsHelper
settings_path: general_admin_application_settings_path(anchor: 'js-signup-settings'),
signup_enabled: @application_setting[:signup_enabled].to_s,
require_admin_approval_after_user_signup: @application_setting[:require_admin_approval_after_user_signup].to_s,
- send_user_confirmation_email: @application_setting[:send_user_confirmation_email].to_s,
email_confirmation_setting: @application_setting[:email_confirmation_setting].to_s,
minimum_password_length: @application_setting[:minimum_password_length],
minimum_password_length_min: ApplicationSetting::DEFAULT_MINIMUM_PASSWORD_LENGTH,
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index 07152133402..c41b5923d13 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -70,11 +70,9 @@ module AuthHelper
end
def form_based_provider_with_highest_priority
- @form_based_provider_with_highest_priority ||= begin
- form_based_provider_priority.each do |provider_regexp|
- highest_priority = form_based_providers.find { |provider| provider.match?(provider_regexp) }
- break highest_priority unless highest_priority.nil?
- end
+ @form_based_provider_with_highest_priority ||= form_based_provider_priority.each do |provider_regexp|
+ highest_priority = form_based_providers.find { |provider| provider.match?(provider_regexp) }
+ break highest_priority unless highest_priority.nil?
end
end
diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb
index 798bb7b64a4..0fac2cb5fc5 100644
--- a/app/helpers/avatars_helper.rb
+++ b/app/helpers/avatars_helper.rb
@@ -91,7 +91,7 @@ module AvatarsHelper
title: user_name
}
- tag(:img, image_options)
+ tag.img(**image_options)
end
def user_avatar(options = {})
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index f08c1a2ff0a..281d5c923d0 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -113,7 +113,7 @@ module BlobHelper
end
def parent_dir_raw_path
- blob_raw_path.rpartition("/").first + "/"
+ "#{blob_raw_path.rpartition('/').first}/"
end
# SVGs can contain malicious JavaScript; only include whitelisted
@@ -295,7 +295,7 @@ module BlobHelper
end
def edit_link_tag(link_text, edit_path, common_classes)
- link_to link_text, edit_path, class: "#{common_classes}"
+ link_to link_text, edit_path, class: common_classes
end
def edit_button_tag(blob, common_classes, text, edit_path, project, ref)
diff --git a/app/helpers/ci/jobs_helper.rb b/app/helpers/ci/jobs_helper.rb
index 7b8290ac9ef..424e5920fed 100644
--- a/app/helpers/ci/jobs_helper.rb
+++ b/app/helpers/ci/jobs_helper.rb
@@ -39,7 +39,7 @@ module Ci
def job_statuses
statuses = Ci::HasStatus::AVAILABLE_STATUSES
- statuses.to_h { |status| [status, status.upcase] }
+ statuses.index_with(&:upcase)
end
end
end
diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb
index 0de84c0d61f..8df30ee1f0d 100644
--- a/app/helpers/ci/runners_helper.rb
+++ b/app/helpers/ci/runners_helper.rb
@@ -96,8 +96,8 @@ module Ci
def toggle_shared_runners_settings_data(project)
{
- is_enabled: "#{project.shared_runners_enabled?}",
- is_disabled_and_unoverridable: "#{project.group&.shared_runners_setting == Namespace::SR_DISABLED_AND_UNOVERRIDABLE}",
+ is_enabled: project.shared_runners_enabled?.to_s,
+ is_disabled_and_unoverridable: (project.group&.shared_runners_setting == Namespace::SR_DISABLED_AND_UNOVERRIDABLE).to_s,
update_path: toggle_shared_runners_project_runners_path(project)
}
end
diff --git a/app/helpers/ci/secure_files_helper.rb b/app/helpers/ci/secure_files_helper.rb
index 30b2e12ac3b..fca89ddab1e 100644
--- a/app/helpers/ci/secure_files_helper.rb
+++ b/app/helpers/ci/secure_files_helper.rb
@@ -4,7 +4,7 @@ module Ci
def show_secure_files_setting(project, user)
return false if user.nil?
- Feature.enabled?(:ci_secure_files, project) && user.can?(:read_secure_files, project)
+ user.can?(:read_secure_files, project)
end
end
end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index 4493bc2bc6d..53781364af7 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -124,7 +124,7 @@ module CommitsHelper
new_project_tag_path: new_project_tag_path(project, ref: commit),
email_patches_path: project_commit_path(project, commit, format: :patch),
plain_diff_path: project_commit_path(project, commit, format: :diff),
- can_revert: "#{can_collaborate && !commit.has_been_reverted?(current_user)}",
+ can_revert: (can_collaborate && !commit.has_been_reverted?(current_user)).to_s,
can_cherry_pick: can_collaborate.to_s,
can_tag: can?(current_user, :push_code, project).to_s,
can_email_patches: (commit.parents.length < 2).to_s
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index e05adc5cd0e..e0a1697cfa9 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -76,13 +76,11 @@ module DiffHelper
def diff_line_content(line)
if line.blank?
"&nbsp;".html_safe
- else
+ elsif line.start_with?('+', '-', ' ')
# `sub` and substring-ing would destroy HTML-safeness of `line`
- if line.start_with?('+', '-', ' ')
- line[1, line.length]
- else
- line
- end
+ line[1, line.length]
+ else
+ line
end
end
@@ -227,7 +225,7 @@ module DiffHelper
end
def conflicts(allow_tree_conflicts: false)
- return unless merge_request.cannot_be_merged?
+ return unless merge_request.cannot_be_merged? && merge_request.source_branch_exists? && merge_request.target_branch_exists?
conflicts_service = MergeRequests::Conflicts::ListService.new(merge_request, allow_tree_conflicts: allow_tree_conflicts) # rubocop:disable CodeReuse/ServiceClass
diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb
index 62e66b9a3ea..427cbe18fbf 100644
--- a/app/helpers/dropdowns_helper.rb
+++ b/app/helpers/dropdowns_helper.rb
@@ -17,8 +17,8 @@ module DropdownsHelper
end
content_tag_options = { class: "dropdown-menu dropdown-select #{options[:dropdown_class] if options.key?(:dropdown_class)}" }
- content_tag_options[:data] = options[:dropdown_qa_selector] ? { qa_selector: "#{options[:dropdown_qa_selector]}" } : {}
- content_tag_options[:data][:testid] = "#{options[:dropdown_testid]}" if options[:dropdown_testid]
+ content_tag_options[:data] = options[:dropdown_qa_selector] ? { qa_selector: (options[:dropdown_qa_selector]).to_s } : {}
+ content_tag_options[:data][:testid] = (options[:dropdown_testid]).to_s if options[:dropdown_testid]
dropdown_output << content_tag(:div, content_tag_options) do
output = []
@@ -86,7 +86,7 @@ module DropdownsHelper
title_output = []
if has_back
- title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-back " + margin_class, aria: { label: "Go back" }, type: "button") do
+ title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-back #{margin_class}", aria: { label: "Go back" }, type: "button") do
sprite_icon('arrow-left')
end
end
@@ -94,7 +94,7 @@ module DropdownsHelper
title_output << content_tag(:span, title, class: margin_class)
if has_close
- title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-close " + margin_class, aria: { label: "Close" }, type: "button") do
+ title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-close #{margin_class}", aria: { label: "Close" }, type: "button") do
sprite_icon('close', size: 16, css_class: 'dropdown-menu-close-icon')
end
end
diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb
index 54733fa9101..cad39854c0e 100644
--- a/app/helpers/emails_helper.rb
+++ b/app/helpers/emails_helper.rb
@@ -139,7 +139,7 @@ module EmailsHelper
max_domain_length = list_id_max_length - Gitlab.config.gitlab.host.length - project.id.to_s.length - 2
if max_domain_length < 3
- return project.id.to_s + "..." + Gitlab.config.gitlab.host
+ return "#{project.id}...#{Gitlab.config.gitlab.host}"
end
if project_path_as_domain.length > max_domain_length
@@ -151,7 +151,7 @@ module EmailsHelper
project_path_as_domain = project_path_as_domain.slice(0, last_dot_index).concat("..")
end
- project.id.to_s + "." + project_path_as_domain + "." + Gitlab.config.gitlab.host
+ "#{project.id}.#{project_path_as_domain}.#{Gitlab.config.gitlab.host}"
end
def html_header_message
diff --git a/app/helpers/environment_helper.rb b/app/helpers/environment_helper.rb
index b6997b6fb70..0e64a98c9da 100644
--- a/app/helpers/environment_helper.rb
+++ b/app/helpers/environment_helper.rb
@@ -72,6 +72,7 @@ module EnvironmentHelper
{
name: environment.name,
id: environment.id,
+ project_full_path: project.full_path,
external_url: environment.external_url,
can_update_environment: can?(current_user, :update_environment, environment),
can_destroy_environment: can_destroy_environment?(environment),
diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb
index 333237db6a4..5bf4fa2ffcc 100644
--- a/app/helpers/environments_helper.rb
+++ b/app/helpers/environments_helper.rb
@@ -67,7 +67,7 @@ module EnvironmentsHelper
'external_dashboard_url' => project.metrics_setting_external_dashboard_url,
'custom_metrics_path' => project_prometheus_metrics_path(project),
'validate_query_path' => validate_query_project_prometheus_metrics_path(project),
- 'custom_metrics_available' => "#{custom_metrics_available?(project)}",
+ 'custom_metrics_available' => custom_metrics_available?(project).to_s,
'dashboard_timezone' => project.metrics_setting_dashboard_timezone.to_s.upcase
}
end
@@ -78,8 +78,8 @@ module EnvironmentsHelper
{
'metrics_dashboard_base_path' => metrics_dashboard_base_path(environment, project),
'current_environment_name' => environment.name,
- 'has_metrics' => "#{environment.has_metrics?}",
- 'environment_state' => "#{environment.state}"
+ 'has_metrics' => environment.has_metrics?.to_s,
+ 'environment_state' => environment.state.to_s
}
end
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index 087e4838ed9..bef2da495b0 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -92,7 +92,7 @@ module EventsHelper
content_tag :li, class: active do
link_to request.path, link_opts do
- content_tag(:span, ' ' + text)
+ content_tag(:span, " #{text}")
end
end
end
diff --git a/app/helpers/groups/observability_helper.rb b/app/helpers/groups/observability_helper.rb
index 6fb6acce386..26caac4ce7f 100644
--- a/app/helpers/groups/observability_helper.rb
+++ b/app/helpers/groups/observability_helper.rb
@@ -5,15 +5,15 @@ module Groups
ACTION_TO_PATH = {
'dashboards' => {
path: '/',
- title: -> { s_('Dashboards') }
+ title: -> { _('Dashboards') }
},
'manage' => {
path: '/dashboards',
- title: -> { s_('Manage Dashboards') }
+ title: -> { _('Manage Dashboards') }
},
'explore' => {
path: '/explore',
- title: -> { s_('Explore') }
+ title: -> { _('Explore') }
}
}.freeze
@@ -22,7 +22,7 @@ module Groups
# When running Observability UI in standalone mode (i.e. not backed by Observability Backend)
# the group-id is not required. This is mostly used for local dev
- base_url = ENV['STANDALONE_OBSERVABILITY_UI'] == 'true' ? observability_url : "#{observability_url}/#{group.id}"
+ base_url = ENV['STANDALONE_OBSERVABILITY_UI'] == 'true' ? observability_url : "#{observability_url}/-/#{group.id}"
sanitized_path = if params[:observability_path] && sanitize(params[:observability_path]) != ''
CGI.unescapeHTML(sanitize(params[:observability_path]))
diff --git a/app/helpers/groups/settings_helper.rb b/app/helpers/groups/settings_helper.rb
index 1b391680996..38300043dd7 100644
--- a/app/helpers/groups/settings_helper.rb
+++ b/app/helpers/groups/settings_helper.rb
@@ -9,7 +9,7 @@ module Groups
remove_form_id: remove_form_id,
button_text: _('Remove group'),
button_testid: 'remove-group-button',
- disabled: group.paid?.to_s,
+ disabled: group.prevent_delete?.to_s,
confirm_danger_message: remove_group_message(group),
phrase: group.full_path
}
diff --git a/app/helpers/hooks_helper.rb b/app/helpers/hooks_helper.rb
index 921e30edbaa..63544e28a0e 100644
--- a/app/helpers/hooks_helper.rb
+++ b/app/helpers/hooks_helper.rb
@@ -10,7 +10,7 @@ module HooksHelper
def link_to_test_hook(hook, trigger)
path = test_hook_path(hook, trigger)
- trigger_human_name = trigger.to_s.tr('_', ' ').camelize
+ trigger_human_name = integration_webhook_event_human_name(trigger)
link_to path, rel: 'nofollow', method: :post do
content_tag(:span, trigger_human_name)
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index c81041c2d9c..021b47ceab2 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -38,7 +38,7 @@ module IconsHelper
css_classes = []
css_classes << "s#{size}" if size
- css_classes << "#{css_class}" unless css_class.blank?
+ css_classes << css_class.to_s unless css_class.blank?
content_tag(
:svg,
diff --git a/app/helpers/ide_helper.rb b/app/helpers/ide_helper.rb
index 34f4749c42a..0e81cea8ac7 100644
--- a/app/helpers/ide_helper.rb
+++ b/app/helpers/ide_helper.rb
@@ -7,7 +7,10 @@ module IdeHelper
'use-new-web-ide' => use_new_web_ide?.to_s,
'new-web-ide-help-page-path' => help_page_path('user/project/web_ide/index.md', anchor: 'vscode-reimplementation'),
'user-preferences-path' => profile_preferences_path,
- 'branch-name' => @branch
+ 'branch-name' => @branch,
+ 'file-path' => @path,
+ 'fork-info' => @fork_info&.to_json,
+ 'merge-request' => @merge_request
}.merge(use_new_web_ide? ? new_ide_data : legacy_ide_data)
end
@@ -24,7 +27,9 @@ module IdeHelper
def new_ide_data
{
'project-path' => @project&.path_with_namespace,
- 'csp-nonce' => content_security_policy_nonce
+ 'csp-nonce' => content_security_policy_nonce,
+ # We will replace these placeholders in the FE
+ 'ide-remote-path' => ide_remote_path(remote_host: ':remote_host', remote_path: ':remote_path')
}
end
@@ -42,9 +47,6 @@ module IdeHelper
'render-whitespace-in-code': current_user.render_whitespace_in_code.to_s,
'codesandbox-bundler-url': Gitlab::CurrentSettings.web_ide_clientside_preview_bundler_url,
'default-branch' => @project && @project.default_branch,
- 'file-path' => @path,
- 'merge-request' => @merge_request,
- 'fork-info' => @fork_info&.to_json,
'project' => convert_to_project_entity_json(@project),
'enable-environments-guidance' => enable_environments_guidance?.to_s,
'preview-markdown-path' => @project && preview_markdown_path(@project),
diff --git a/app/helpers/integrations_helper.rb b/app/helpers/integrations_helper.rb
index abfa55cff24..0650af33e37 100644
--- a/app/helpers/integrations_helper.rb
+++ b/app/helpers/integrations_helper.rb
@@ -185,6 +185,29 @@ module IntegrationsHelper
target_type_i18n_map[target_type] || target_type
end
+ def integration_webhook_event_human_name(event)
+ event_i18n_map = {
+ repository_update_events: _('Repository update events'),
+ push_events: _('Push events'),
+ tag_push_events: s_('Webhooks|Tag push events'),
+ note_events: _('Comments'),
+ confidential_note_events: s_('Webhooks|Confidential comments'),
+ issues_events: s_('Webhooks|Issues events'),
+ confidential_issues_events: s_('Webhooks|Confidential issues events'),
+ subgroup_events: s_('Webhooks|Subgroup events'),
+ member_events: s_('Webhooks|Member events'),
+ merge_requests_events: s_('Webhooks|Merge request events'),
+ job_events: s_('Webhooks|Job events'),
+ pipeline_events: s_('Webhooks|Pipeline events'),
+ wiki_page_events: s_('Webhooks|Wiki page events'),
+ deployment_events: s_('Webhooks|Deployment events'),
+ feature_flag_events: s_('Webhooks|Feature flag events'),
+ releases_events: s_('Webhooks|Releases events')
+ }
+
+ event_i18n_map[event] || event.to_s.humanize
+ end
+
extend self
private
diff --git a/app/helpers/invite_members_helper.rb b/app/helpers/invite_members_helper.rb
index 5d537767eaf..6fad1346426 100644
--- a/app/helpers/invite_members_helper.rb
+++ b/app/helpers/invite_members_helper.rb
@@ -20,6 +20,7 @@ module InviteMembersHelper
end
end
+ # Overridden in EE
def common_invite_group_modal_data(source, member_class, is_project)
{
id: source.id,
@@ -29,7 +30,8 @@ module InviteMembersHelper
invalid_groups: source.related_group_ids,
help_link: help_page_url('user/permissions'),
is_project: is_project,
- access_levels: member_class.permissible_access_level_roles(current_user, source).to_json
+ access_levels: member_class.permissible_access_level_roles(current_user, source).to_json,
+ full_path: source.full_path
}.merge(group_select_data(source))
end
@@ -39,7 +41,8 @@ module InviteMembersHelper
id: source.id,
root_id: source.root_ancestor&.id,
name: source.name,
- default_access_level: Gitlab::Access::GUEST
+ default_access_level: Gitlab::Access::GUEST,
+ full_path: source.full_path
}
if show_invite_members_for_task?(source)
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index fd181109a94..2b21d8c51e6 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -138,15 +138,15 @@ module IssuablesHelper
def issuable_meta_author_status(author)
return "" unless author&.status&.customized? && status = user_status(author)
- "#{status}".html_safe
+ status.to_s.html_safe
end
def issuable_meta(issuable, project)
output = []
if issuable.respond_to?(:work_item_type) && WorkItems::Type::WI_TYPES_WITH_CREATED_HEADER.include?(issuable.work_item_type.base_type)
- output << content_tag(:span, sprite_icon("#{issuable.work_item_type.icon_name}", css_class: 'gl-icon gl-vertical-align-middle gl-text-gray-500'), class: 'gl-mr-2', aria: { hidden: 'true' })
- output << content_tag(:span, s_('IssuableStatus|%{wi_type} created %{created_at} by ').html_safe % { wi_type: issuable.issue_type.capitalize, created_at: time_ago_with_tooltip(issuable.created_at) }, class: 'gl-mr-2')
+ output << content_tag(:span, sprite_icon(issuable.work_item_type.icon_name.to_s, css_class: 'gl-icon gl-vertical-align-middle gl-text-gray-500'), class: 'gl-mr-2', aria: { hidden: 'true' })
+ output << content_tag(:span, s_('IssuableStatus|%{wi_type} created %{created_at} by ').html_safe % { wi_type: IntegrationsHelper.integration_issue_type(issuable.issue_type), created_at: time_ago_with_tooltip(issuable.created_at) }, class: 'gl-mr-2')
else
output << content_tag(:span, s_('IssuableStatus|Created %{created_at} by').html_safe % { created_at: time_ago_with_tooltip(issuable.created_at) }, class: 'gl-mr-2')
end
@@ -207,7 +207,14 @@ module IssuablesHelper
def assigned_issuables_count(issuable_type)
case issuable_type
when :issues
- current_user.assigned_open_issues_count
+ if Feature.enabled?(:limit_assigned_issues_count)
+ ::Users::AssignedIssuesCountService.new(
+ current_user: current_user,
+ max_limit: User::MAX_LIMIT_FOR_ASSIGNEED_ISSUES_COUNT
+ ).count
+ else
+ current_user.assigned_open_issues_count
+ end
when :merge_requests
current_user.assigned_open_merge_requests_count
else
@@ -215,6 +222,16 @@ module IssuablesHelper
end
end
+ def assigned_open_issues_count_text
+ count = assigned_issuables_count(:issues)
+
+ if Feature.enabled?(:limit_assigned_issues_count) && count > User::MAX_LIMIT_FOR_ASSIGNEED_ISSUES_COUNT - 1
+ "#{count - 1}+"
+ else
+ count.to_s
+ end
+ end
+
def issuable_reference(issuable)
@show_full_reference ? issuable.to_reference(full: true) : issuable.to_reference(@group || @project)
end
@@ -348,12 +365,10 @@ module IssuablesHelper
else
[_("Closed"), "merge-request-close"]
end
+ elsif issuable.open?
+ [_("Open"), "issues"]
else
- if issuable.open?
- [_("Open"), "issues"]
- else
- [_("Closed"), "issue-closed"]
- end
+ [_("Closed"), "issue-closed"]
end
end
@@ -414,6 +429,7 @@ module IssuablesHelper
id: issuable[:id],
severity: issuable[:severity],
timeTrackingLimitToHours: Gitlab::CurrentSettings.time_tracking_limit_to_hours,
+ canCreateTimelogs: issuable.dig(:current_user, :can_create_timelogs),
createNoteEmail: issuable[:create_note_email],
issuableType: issuable[:type]
}
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 932a50d9451..1d68dccc741 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -256,6 +256,18 @@ module IssuesHelper
)
end
+ def dashboard_issues_list_data(current_user)
+ {
+ calendar_path: url_for(safe_params.merge(calendar_url_options)),
+ empty_state_svg_path: image_path('illustrations/issue-dashboard_results-without-filter.svg'),
+ initial_sort: current_user&.user_preference&.issues_sort,
+ is_public_visibility_restricted:
+ Gitlab::CurrentSettings.restricted_visibility_levels&.include?(Gitlab::VisibilityLevel::PUBLIC).to_s,
+ is_signed_in: current_user.present?.to_s,
+ rss_path: url_for(safe_params.merge(rss_url_options))
+ }
+ end
+
def issues_form_data(project)
{
new_issue_path: new_project_issue_path(project)
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index 0123eb68c9a..8c069bc828b 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -159,7 +159,7 @@ module LabelsHelper
end
def label_subscription_toggle_button_text(label, project = nil)
- label.subscribed?(current_user, project) ? 'Unsubscribe' : 'Subscribe'
+ label.subscribed?(current_user, project) ? _('Unsubscribe') : _('Subscribe')
end
def create_label_title(subject)
@@ -219,8 +219,8 @@ module LabelsHelper
}.merge(opts)
end
- def issuable_types
- ['issues', 'merge requests']
+ def labels_function_introduction
+ _('Labels can be applied to issues and merge requests. Group labels are available for any project within the group.')
end
def show_labels_full_path?(project, group)
diff --git a/app/helpers/listbox_helper.rb b/app/helpers/listbox_helper.rb
index 16caf862c7b..0aaeb39c82d 100644
--- a/app/helpers/listbox_helper.rb
+++ b/app/helpers/listbox_helper.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
module ListboxHelper
- DROPDOWN_CONTAINER_CLASSES = %w[dropdown b-dropdown gl-new-dropdown btn-group js-redirect-listbox].freeze
+ DROPDOWN_CONTAINER_CLASSES = %w[dropdown b-dropdown gl-dropdown btn-group js-redirect-listbox].freeze
DROPDOWN_BUTTON_CLASSES = %w[btn dropdown-toggle btn-default btn-md gl-button gl-dropdown-toggle].freeze
- DROPDOWN_INNER_CLASS = 'gl-new-dropdown-button-text'
+ DROPDOWN_INNER_CLASS = 'gl-dropdown-button-text'
DROPDOWN_ICON_CLASS = 'gl-button-icon dropdown-chevron gl-icon'
# Creates a listbox component with redirect behavior.
diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb
index 9baea43b77d..ed9129ff78b 100644
--- a/app/helpers/markup_helper.rb
+++ b/app/helpers/markup_helper.rb
@@ -59,12 +59,20 @@ module MarkupHelper
# as Markdown. HTML tags in the parsed output are not counted toward the
# +max_chars+ limit. If the length limit falls within a tag's contents, then
# the tag contents are truncated without removing the closing tag.
- def first_line_in_markdown(object, attribute, max_chars = nil, options = {})
+ def first_line_in_markdown(object, attribute, max_chars = nil, is_todo: false, **options)
md = markdown_field(object, attribute, options.merge(post_process: false))
return unless md.present?
+ includes_code = false
+
tags = %w(a gl-emoji b strong i em pre code p span)
- tags << 'img' if options[:allow_images]
+
+ if is_todo
+ fragment = Nokogiri::HTML.fragment(md)
+ includes_code = fragment.css('code').any?
+
+ md = fragment
+ end
context = markdown_field_render_context(object, attribute, options)
context.reverse_merge!(truncate_visible_max_chars: max_chars || md.length)
@@ -77,12 +85,19 @@ module MarkupHelper
%w(
style data-src data-name data-unicode-version data-html
data-reference-type data-project-path data-iid data-mr-title
+ data-user
)
)
+ # Extra span with relative positioning relative due to system font being behind
+ # background color when username is first word of mention
+ if is_todo && !includes_code
+ text = "<span class=\"gl-relative\">\"</span>#{text}<span class=\"gl-relative\">\"</span>"
+ end
+
# since <img> tags are stripped, this can leave empty <a> tags hanging around
# (as our markdown wraps images in links)
- options[:allow_images] ? text : strip_empty_link_tags(text).html_safe
+ strip_empty_link_tags(text).html_safe
end
def markdown(text, context = {})
diff --git a/app/helpers/members_helper.rb b/app/helpers/members_helper.rb
index f1f5f941edd..29f94adcc78 100644
--- a/app/helpers/members_helper.rb
+++ b/app/helpers/members_helper.rb
@@ -14,19 +14,17 @@ module MembersHelper
else
"deny #{member.user.name}'s request to join"
end
+ elsif member.user
+ "remove #{member.user.name} from"
else
- if member.user
- "remove #{member.user.name} from"
- else
- e = RuntimeError.new("Data integrity error: no associated user for member ID #{member.id}")
- Gitlab::ErrorTracking.track_exception(e,
- member_id: member.id,
- invite_email: member.invite_email,
- invite_accepted_at: member.invite_accepted_at,
- source_id: member.source_id,
- source_type: member.source_type)
- "remove this orphaned member from"
- end
+ e = RuntimeError.new("Data integrity error: no associated user for member ID #{member.id}")
+ Gitlab::ErrorTracking.track_exception(e,
+ member_id: member.id,
+ invite_email: member.invite_email,
+ invite_accepted_at: member.invite_accepted_at,
+ source_id: member.source_id,
+ source_type: member.source_type)
+ "remove this orphaned member from"
end
"#{text} #{action} the #{member.source.human_name} #{source_text(member)}?"
diff --git a/app/helpers/nav/top_nav_helper.rb b/app/helpers/nav/top_nav_helper.rb
index 751900f4593..bd4d661ab49 100644
--- a/app/helpers/nav/top_nav_helper.rb
+++ b/app/helpers/nav/top_nav_helper.rb
@@ -101,8 +101,7 @@ module Nav
builder.add_primary_menu_item_with_shortcut(
header: top_nav_localized_headers[:switch_to],
active: nav == 'project' || active_nav_link?(path: %w[root#index projects#trending projects#starred dashboard/projects#index]),
- css_class: 'qa-projects-dropdown',
- data: { track_label: "projects_dropdown", track_action: "click_dropdown" },
+ data: { track_label: "projects_dropdown", track_action: "click_dropdown", qa_selector: "projects_dropdown" },
view: PROJECTS_VIEW,
shortcut_href: dashboard_projects_path,
**projects_menu_item_attrs
@@ -116,8 +115,7 @@ module Nav
builder.add_primary_menu_item_with_shortcut(
header: top_nav_localized_headers[:switch_to],
active: nav == 'group' || active_nav_link?(path: %w[dashboard/groups explore/groups]),
- css_class: 'qa-groups-dropdown',
- data: { track_label: "groups_dropdown", track_action: "click_dropdown" },
+ data: { track_label: "groups_dropdown", track_action: "click_dropdown", qa_selector: "groups_dropdown" },
view: GROUPS_VIEW,
shortcut_href: dashboard_groups_path,
**groups_menu_item_attrs
@@ -133,7 +131,7 @@ module Nav
href: dashboard_milestones_path,
active: active_nav_link?(controller: 'dashboard/milestones'),
icon: 'clock',
- data: { qa_selector: 'milestones_link', **menu_data_tracking_attrs('milestones') },
+ data: { **menu_data_tracking_attrs('milestones') },
shortcut_class: 'dashboard-shortcuts-milestones'
)
end
@@ -156,7 +154,7 @@ module Nav
href: activity_dashboard_path,
active: active_nav_link?(path: 'dashboard#activity'),
icon: 'history',
- data: { qa_selector: 'activity_link', **menu_data_tracking_attrs('activity') },
+ data: { **menu_data_tracking_attrs('activity') },
shortcut_class: 'dashboard-shortcuts-activity'
)
end
@@ -173,9 +171,8 @@ module Nav
title: title,
active: active_nav_link?(controller: 'admin/dashboard'),
icon: 'admin',
- css_class: 'qa-admin-area-link',
href: admin_root_path,
- data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
+ data: { qa_selector: 'admin_area_link', **menu_data_tracking_attrs(title) }
)
end
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index 0cf2c5cea4c..bf3b132e33a 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -21,11 +21,11 @@ module NavHelper
def page_gutter_class
moved_sidebar_enabled = current_controller?('merge_requests') && moved_mr_sidebar_enabled?
- if page_has_markdown? && !current_controller?('conflicts')
+ if (page_has_markdown? || current_path?('projects/merge_requests#diffs')) && !current_controller?('conflicts')
if cookies[:collapsed_gutter] == 'true'
- ["page-gutter", "#{'right-sidebar-collapsed' unless moved_sidebar_enabled}"]
+ ["page-gutter", ('right-sidebar-collapsed' unless moved_sidebar_enabled).to_s]
else
- ["page-gutter", "#{'right-sidebar-expanded' unless moved_sidebar_enabled}"]
+ ["page-gutter", ('right-sidebar-expanded' unless moved_sidebar_enabled).to_s]
end
elsif current_path?('jobs#show')
%w[page-gutter build-sidebar right-sidebar-expanded]
diff --git a/app/helpers/numbers_helper.rb b/app/helpers/numbers_helper.rb
index 38d3f90dd55..7184a9c075c 100644
--- a/app/helpers/numbers_helper.rb
+++ b/app/helpers/numbers_helper.rb
@@ -6,7 +6,7 @@ module NumbersHelper
count = resource.page.total_count_with_limit(:all, limit: limit)
if count > limit
- number_with_delimiter(count - 1, options) + '+'
+ "#{number_with_delimiter(count - 1, options)}+"
else
number_with_delimiter(count, options)
end
diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb
index c0665463706..6f7b2877100 100644
--- a/app/helpers/page_layout_helper.rb
+++ b/app/helpers/page_layout_helper.rb
@@ -80,8 +80,8 @@ module PageLayoutHelper
tags = []
page_card_attributes.each_with_index do |pair, i|
- tags << tag(:meta, property: "twitter:label#{i + 1}", content: pair[0])
- tags << tag(:meta, property: "twitter:data#{i + 1}", content: pair[1])
+ tags << tag.meta(property: "twitter:label#{i + 1}", content: pair[0])
+ tags << tag.meta(property: "twitter:data#{i + 1}", content: pair[1])
end
tags.join.html_safe
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index 57afe0ed0be..f2b7c0064e4 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -100,11 +100,19 @@ module PreferencesHelper
def language_choices
options_for_select(
- selectable_locales_with_translation_level.sort,
+ selectable_locales_with_translation_level(Gitlab::I18n::MINIMUM_TRANSLATION_LEVEL).sort,
current_user.preferred_language
)
end
+ def default_preferred_language_choices
+ options_for_select(
+ selectable_locales_with_translation_level(
+ PreferredLanguageSwitcherHelper::SWITCHER_MINIMUM_TRANSLATION_LEVEL).sort,
+ Gitlab::CurrentSettings.default_preferred_language
+ )
+ end
+
def integration_views
[].tap do |views|
views << { name: 'gitpod', message: gitpod_enable_description, message_url: gitpod_url_placeholder, help_link: help_page_path('integration/gitpod.md') } if Gitlab::CurrentSettings.gitpod_enabled
@@ -136,8 +144,8 @@ module PreferencesHelper
first_day_of_week_choices.rassoc(Gitlab::CurrentSettings.first_day_of_week).first
end
- def selectable_locales_with_translation_level
- Gitlab::I18n.selectable_locales.map do |code, language|
+ def selectable_locales_with_translation_level(minimum_level)
+ Gitlab::I18n.selectable_locales(minimum_level).map do |code, language|
[
s_("i18n|%{language} (%{percent_translated}%% translated)") % {
language: language,
diff --git a/app/helpers/preferred_language_switcher_helper.rb b/app/helpers/preferred_language_switcher_helper.rb
new file mode 100644
index 00000000000..1cad4f842ec
--- /dev/null
+++ b/app/helpers/preferred_language_switcher_helper.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module PreferredLanguageSwitcherHelper
+ SWITCHER_MINIMUM_TRANSLATION_LEVEL = 90
+
+ def ordered_selectable_locales
+ highly_translated_languages = Gitlab::I18n.selectable_locales(SWITCHER_MINIMUM_TRANSLATION_LEVEL)
+ # see https://docs.gitlab.com/ee/development/i18n/externalization.html#adding-a-new-language
+ # for translation standards
+ locale_list = highly_translated_languages.filter_map do |code, language|
+ percentage = Gitlab::I18n.percentage_translated_for(code)
+ {
+ value: code,
+ percentage: percentage,
+ text: language.split('-').last.strip
+ }
+ end
+
+ locale_list.sort_by { |item| item[:percentage] }.reverse
+ end
+end
diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb
index bfe39bbc211..979b979fba7 100644
--- a/app/helpers/profiles_helper.rb
+++ b/app/helpers/profiles_helper.rb
@@ -46,6 +46,14 @@ module ProfilesHelper
end
end
+ def ssh_key_usage_types
+ {
+ s_('SSHKey|Authentication & Signing') => 'auth_and_signing',
+ s_('SSHKey|Authentication') => 'auth',
+ s_('SSHKey|Signing') => 'signing'
+ }
+ end
+
# Overridden in EE::ProfilesHelper#ssh_key_expiration_tooltip
def ssh_key_expiration_tooltip(key)
return key.errors.full_messages.join(', ') if key.errors.full_messages.any?
diff --git a/app/helpers/programming_languages_helper.rb b/app/helpers/programming_languages_helper.rb
new file mode 100644
index 00000000000..c50872aec6f
--- /dev/null
+++ b/app/helpers/programming_languages_helper.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module ProgrammingLanguagesHelper
+ def search_language_placeholder
+ placeholder = _('Language')
+
+ return placeholder unless params[:language].present?
+
+ programming_languages.find { |language| language.id.to_s == params[:language] }&.name ||
+ placeholder
+ end
+
+ def programming_languages
+ @programming_languages ||= ProgrammingLanguage.most_popular
+ end
+
+ def language_state_class(language)
+ params[:language] == language.id.to_s ? 'is-active' : ''
+ end
+end
diff --git a/app/helpers/projects/ml/experiments_helper.rb b/app/helpers/projects/ml/experiments_helper.rb
index 29bd879859e..a67484e3d2f 100644
--- a/app/helpers/projects/ml/experiments_helper.rb
+++ b/app/helpers/projects/ml/experiments_helper.rb
@@ -9,7 +9,9 @@ module Projects
items = candidates.map do |candidate|
{
**candidate.params.to_h { |p| [p.name, p.value] },
- **candidate.latest_metrics.to_h { |m| [m.name, number_with_precision(m.value, precision: 4)] }
+ **candidate.latest_metrics.to_h { |m| [m.name, number_with_precision(m.value, precision: 4)] },
+ artifact: link_to_artifact(candidate),
+ details: link_to_details(candidate)
}
end
@@ -19,6 +21,42 @@ module Projects
def unique_logged_names(candidates, &selector)
Gitlab::Json.generate(candidates.flat_map(&selector).map(&:name).uniq)
end
+
+ def candidate_as_data(candidate)
+ data = {
+ params: candidate.params,
+ metrics: candidate.latest_metrics,
+ info: {
+ iid: candidate.iid,
+ path_to_artifact: link_to_artifact(candidate),
+ experiment_name: candidate.experiment.name,
+ path_to_experiment: link_to_experiment(candidate),
+ status: candidate.status
+ }
+ }
+
+ Gitlab::Json.generate(data)
+ end
+
+ private
+
+ def link_to_artifact(candidate)
+ artifact = candidate.artifact
+
+ return unless artifact.present?
+
+ project_package_path(candidate.experiment.project, artifact)
+ end
+
+ def link_to_details(candidate)
+ project_ml_candidate_path(candidate.experiment.project, candidate.iid)
+ end
+
+ def link_to_experiment(candidate)
+ experiment = candidate.experiment
+
+ project_ml_experiment_path(experiment.project, experiment.iid)
+ end
end
end
end
diff --git a/app/helpers/projects/pipeline_helper.rb b/app/helpers/projects/pipeline_helper.rb
index edbdb9d4adf..5c62920cd89 100644
--- a/app/helpers/projects/pipeline_helper.rb
+++ b/app/helpers/projects/pipeline_helper.rb
@@ -12,6 +12,7 @@ module Projects
graphql_resource_etag: graphql_etag_pipeline_path(pipeline),
metrics_path: namespace_project_ci_prometheus_metrics_histograms_path(namespace_id: project.namespace, project_id: project, format: :json),
pipeline_iid: pipeline.iid,
+ pipeline_path: pipeline_path(pipeline),
pipeline_project_path: project.full_path,
total_job_count: pipeline.total_size,
summary_endpoint: summary_project_pipeline_tests_path(project, pipeline, format: :json),
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index e41a3fa5091..682febe9dc9 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -142,6 +142,8 @@ module ProjectsHelper
end
def project_search_tabs?(tab)
+ return false unless @project.present?
+
abilities = Array(search_tab_ability_map[tab])
abilities.any? { |ability| can?(current_user, ability, @project) }
@@ -254,11 +256,8 @@ module ProjectsHelper
end
end
- # TODO: Remove this method when removing the feature flag
- # https://gitlab.com/gitlab-org/gitlab/merge_requests/11209#note_162234863
- # make sure to remove from the EE specific controller as well: ee/app/controllers/ee/dashboard/projects_controller.rb
def show_projects?(projects, params)
- Feature.enabled?(:project_list_filter_bar) || !!(params[:personal] || params[:name] || any_projects?(projects))
+ !!(params[:personal] || params[:name] || params[:language] || any_projects?(projects))
end
def push_to_create_project_command(user = current_user)
@@ -465,9 +464,9 @@ module ProjectsHelper
def project_coverage_chart_data_attributes(daily_coverage_options, ref)
{
graph_endpoint: "#{daily_coverage_options[:graph_api_path]}?#{daily_coverage_options[:base_params].to_query}",
- graph_start_date: "#{daily_coverage_options[:base_params][:start_date].strftime('%b %d')}",
- graph_end_date: "#{daily_coverage_options[:base_params][:end_date].strftime('%b %d')}",
- graph_ref: "#{ref}",
+ graph_start_date: daily_coverage_options[:base_params][:start_date].strftime('%b %d'),
+ graph_end_date: daily_coverage_options[:base_params][:end_date].strftime('%b %d'),
+ graph_ref: ref.to_s,
graph_csv_path: "#{daily_coverage_options[:download_path]}?#{daily_coverage_options[:base_params].to_query}"
}
end
@@ -480,6 +479,32 @@ module ProjectsHelper
format_cached_count(1000, number)
end
+ def fork_divergence_message(counts)
+ messages = []
+
+ if counts[:behind].nil? || counts[:ahead].nil?
+ return s_('ForksDivergence|Fork has diverged from upstream repository')
+ end
+
+ if counts[:behind] > 0
+ messages << s_("ForksDivergence|%{behind} %{commit_word} behind") % {
+ behind: counts[:behind], commit_word: n_('commit', 'commits', counts[:behind])
+ }
+ end
+
+ if counts[:ahead] > 0
+ messages << s_("ForksDivergence|%{ahead} %{commit_word} ahead of") % {
+ ahead: counts[:ahead], commit_word: n_('commit', 'commits', counts[:ahead])
+ }
+ end
+
+ if messages.blank?
+ s_('ForksDivergence|Up to date with upstream repository')
+ else
+ s_("ForksDivergence|%{messages} upstream repository") % { messages: messages.join(', ') }
+ end
+ end
+
private
def localized_access_names
@@ -531,10 +556,10 @@ module ProjectsHelper
def search_tab_ability_map
@search_tab_ability_map ||= tab_ability_map.merge(
- blobs: :download_code,
- commits: :download_code,
+ blobs: :read_code,
+ commits: :read_code,
merge_requests: :read_merge_request,
- notes: [:read_merge_request, :download_code, :read_issue, :read_snippet],
+ notes: [:read_merge_request, :read_code, :read_issue, :read_snippet],
members: :read_project_member
)
end
@@ -658,7 +683,6 @@ module ProjectsHelper
lfsEnabled: !!project.lfs_enabled,
emailsDisabled: project.emails_disabled?,
metricsDashboardAccessLevel: feature.metrics_dashboard_access_level,
- operationsAccessLevel: feature.operations_access_level,
monitorAccessLevel: feature.monitor_access_level,
showDefaultAwardEmojis: project.show_default_award_emojis?,
warnAboutPotentiallyUnwantedCharacters: project.warn_about_potentially_unwanted_characters?,
@@ -680,7 +704,7 @@ module ProjectsHelper
def find_file_path
return unless @project && !@project.empty_repo?
- return unless can?(current_user, :download_code, @project)
+ return unless can?(current_user, :read_code, @project)
ref = @ref || @project.repository.root_ref
diff --git a/app/helpers/routing/pseudonymization_helper.rb b/app/helpers/routing/pseudonymization_helper.rb
index dce0517690d..63e2b377fef 100644
--- a/app/helpers/routing/pseudonymization_helper.rb
+++ b/app/helpers/routing/pseudonymization_helper.rb
@@ -12,6 +12,7 @@ module Routing
tab
glm_source
glm_content
+ _gl
].freeze
def initialize(request_object, group, project)
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index b8ac2afa7d6..e03365ad5f1 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -40,6 +40,7 @@ module SearchHelper
[
groups_autocomplete(term),
projects_autocomplete(term),
+ users_autocomplete(term),
issue_autocomplete(term)
].flatten
end
@@ -308,8 +309,8 @@ module SearchHelper
{
category: "Groups",
id: group.id,
- value: "#{search_result_sanitize(group.name)}",
- label: "#{search_result_sanitize(group.full_name)}",
+ value: search_result_sanitize(group.name),
+ label: search_result_sanitize(group.full_name),
url: group_path(group),
avatar_url: group.avatar_url || ''
}
@@ -343,14 +344,32 @@ module SearchHelper
{
category: "Projects",
id: p.id,
- value: "#{search_result_sanitize(p.name)}",
- label: "#{search_result_sanitize(p.full_name)}",
+ value: search_result_sanitize(p.name),
+ label: search_result_sanitize(p.full_name),
url: project_path(p),
avatar_url: p.avatar_url || ''
}
end
end
+ def users_autocomplete(term, limit = 5)
+ return [] unless current_user && Ability.allowed?(current_user, :read_users_list)
+
+ SearchService
+ .new(current_user, { scope: 'users', per_page: limit, search: term })
+ .search_objects
+ .map do |user|
+ {
+ category: "Users",
+ id: user.id,
+ value: search_result_sanitize(user.name),
+ label: search_result_sanitize(user.username),
+ url: user_path(user),
+ avatar_url: user.avatar_url || ''
+ }
+ end
+ end
+
def recent_merge_requests_autocomplete(term)
return [] unless current_user
@@ -427,20 +446,38 @@ module SearchHelper
result
end
+ def code_tab_condition
+ return true if project_search_tabs?(:blobs)
+
+ @project.nil? && search_service.show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_code_tab)
+ end
+
+ def wiki_tab_condition
+ return true if project_search_tabs?(:wiki)
+
+ @project.nil? && search_service.show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_wiki_tab)
+ end
+
+ def commits_tab_condition
+ return true if project_search_tabs?(:commits)
+
+ @project.nil? && search_service.show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_commits_tab)
+ end
+
# search page scope navigation
def search_navigation
{
projects: { sort: 1, label: _("Projects"), data: { qa_selector: 'projects_tab' }, condition: @project.nil? },
- blobs: { sort: 2, label: _("Code"), data: { qa_selector: 'code_tab' }, condition: project_search_tabs?(:blobs) || (search_service.show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_code_tab)) },
+ blobs: { sort: 2, label: _("Code"), data: { qa_selector: 'code_tab' }, condition: code_tab_condition },
# sort: 3 is reserved for EE items
issues: { sort: 4, label: _("Issues"), condition: project_search_tabs?(:issues) || feature_flag_tab_enabled?(:global_search_issues_tab) },
merge_requests: { sort: 5, label: _("Merge requests"), condition: project_search_tabs?(:merge_requests) || feature_flag_tab_enabled?(:global_search_merge_requests_tab) },
- wiki_blobs: { sort: 6, label: _("Wiki"), condition: project_search_tabs?(:wiki) || search_service.show_elasticsearch_tabs? },
- commits: { sort: 7, label: _("Commits"), condition: project_search_tabs?(:commits) || (search_service.show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_commits_tab)) },
+ wiki_blobs: { sort: 6, label: _("Wiki"), condition: wiki_tab_condition },
+ commits: { sort: 7, label: _("Commits"), condition: commits_tab_condition },
notes: { sort: 8, label: _("Comments"), condition: project_search_tabs?(:notes) || search_service.show_elasticsearch_tabs? },
milestones: { sort: 9, label: _("Milestones"), condition: project_search_tabs?(:milestones) || @project.nil? },
- users: { sort: 10, label: _("Users"), condition: show_user_search_tab? },
- snippet_titles: { sort: 11, label: _("Titles and Descriptions"), search: { snippets: true, group_id: nil, project_id: nil }, condition: @show_snippets.present? && @project.nil? }
+ users: { sort: 10, label: _("Users"), condition: show_user_search_tab? },
+ snippet_titles: { sort: 11, label: _("Titles and Descriptions"), search: { snippets: true, group_id: nil, project_id: nil }, condition: search_service.show_snippets? && @project.nil? }
}
end
@@ -545,12 +582,10 @@ module SearchHelper
else
:success
end
+ elsif issuable.closed?
+ :info
else
- if issuable.closed?
- :info
- else
- :success
- end
+ :success
end
end
@@ -566,7 +601,7 @@ module SearchHelper
end
def feature_flag_tab_enabled?(flag)
- @group || Feature.enabled?(flag, current_user, type: :ops)
+ @group.present? || Feature.enabled?(flag, current_user, type: :ops)
end
def sanitized_search_params
diff --git a/app/helpers/sidebars_helper.rb b/app/helpers/sidebars_helper.rb
index 9002fdda128..cbee02a28c0 100644
--- a/app/helpers/sidebars_helper.rb
+++ b/app/helpers/sidebars_helper.rb
@@ -20,9 +20,8 @@ module SidebarsHelper
end
end
- def project_sidebar_context(project, user, current_ref)
- context_data = project_sidebar_context_data(project, user, current_ref)
-
+ def project_sidebar_context(project, user, current_ref, ref_type: nil)
+ context_data = project_sidebar_context_data(project, user, current_ref, ref_type: ref_type)
Sidebars::Projects::Context.new(**context_data)
end
@@ -83,12 +82,13 @@ module SidebarsHelper
tracking_attrs('user_side_navigation', 'render', 'user_side_navigation')
end
- def project_sidebar_context_data(project, user, current_ref)
+ def project_sidebar_context_data(project, user, current_ref, ref_type: nil)
{
current_user: user,
container: project,
learn_gitlab_enabled: learn_gitlab_enabled?(project),
current_ref: current_ref,
+ ref_type: ref_type,
jira_issues_integration: project_jira_issues_integration?,
can_view_pipeline_editor: can_view_pipeline_editor?(project),
show_cluster_hint: show_gke_cluster_integration_callout?(project)
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
index a711f36fe05..4a9596a1347 100644
--- a/app/helpers/sorting_helper.rb
+++ b/app/helpers/sorting_helper.rb
@@ -44,25 +44,18 @@ module SortingHelper
# rubocop: enable Metrics/AbcSize
def projects_sort_options_hash
- use_old_sorting = Feature.disabled?(:project_list_filter_bar) || current_controller?('admin/projects')
-
options = {
sort_value_latest_activity => sort_title_latest_activity,
sort_value_recently_created => sort_title_created_date,
sort_value_name => sort_title_name,
sort_value_name_desc => sort_title_name_desc,
- sort_value_stars_desc => sort_title_stars
+ sort_value_stars_desc => sort_title_stars,
+ sort_value_oldest_activity => sort_title_oldest_activity,
+ sort_value_oldest_created => sort_title_oldest_created,
+ sort_value_recently_created => sort_title_recently_created,
+ sort_value_stars_desc => sort_title_most_stars
}
- if use_old_sorting
- options = options.merge({
- sort_value_oldest_activity => sort_title_oldest_activity,
- sort_value_oldest_created => sort_title_oldest_created,
- sort_value_recently_created => sort_title_recently_created,
- sort_value_stars_desc => sort_title_most_stars
- })
- end
-
if current_controller?('admin/projects')
options[sort_value_largest_repo] = sort_title_largest_repo
end
@@ -79,29 +72,6 @@ module SortingHelper
}
end
- def projects_sort_option_titles
- # Only used for the project filter search bar
- projects_sort_options_hash.merge({
- sort_value_oldest_activity => sort_title_latest_activity,
- sort_value_oldest_created => sort_title_created_date,
- sort_value_name_desc => sort_title_name,
- sort_value_stars_asc => sort_title_stars
- })
- end
-
- def projects_reverse_sort_options_hash
- {
- sort_value_latest_activity => sort_value_oldest_activity,
- sort_value_recently_created => sort_value_oldest_created,
- sort_value_name => sort_value_name_desc,
- sort_value_stars_desc => sort_value_stars_asc,
- sort_value_oldest_activity => sort_value_latest_activity,
- sort_value_oldest_created => sort_value_recently_created,
- sort_value_name_desc => sort_value_name,
- sort_value_stars_asc => sort_value_stars_desc
- }
- end
-
def forks_reverse_sort_options_hash
{
sort_value_recently_created => sort_value_oldest_created,
@@ -188,13 +158,6 @@ module SortingHelper
}
end
- def runners_sort_options_hash
- {
- sort_value_created_date => sort_title_created_date,
- sort_value_contacted_date => sort_title_contacted_date
- }
- end
-
def starrers_sort_options_hash
{
sort_value_name => sort_title_name,
@@ -308,7 +271,7 @@ module SortingHelper
end
def sort_direction_button(reverse_url, reverse_sort, sort_value)
- link_class = 'gl-button btn btn-default btn-icon has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort'
+ link_class = 'gl-button btn btn-default btn-icon has-tooltip reverse-sort-btn rspec-reverse-sort'
icon = sort_direction_icon(sort_value)
url = reverse_url
@@ -329,13 +292,6 @@ module SortingHelper
sort_direction_button(url, reverse_sort, sort_value)
end
- def project_sort_direction_button(sort_value)
- reverse_sort = projects_reverse_sort_options_hash[sort_value]
- url = filter_projects_path(sort: reverse_sort)
-
- sort_direction_button(url, reverse_sort, sort_value)
- end
-
def packages_sort_options_hash
{
sort_value_recently_created => sort_title_created_date,
diff --git a/app/helpers/ssh_keys_helper.rb b/app/helpers/ssh_keys_helper.rb
index 17a9fd6146d..4cd40836335 100644
--- a/app/helpers/ssh_keys_helper.rb
+++ b/app/helpers/ssh_keys_helper.rb
@@ -2,10 +2,14 @@
module SshKeysHelper
def ssh_key_delete_modal_data(key, path)
+ title = _('Delete Key')
+
{
path: path,
method: 'delete',
qa_selector: 'delete_ssh_key_button',
+ title: title,
+ aria_label: title,
modal_attributes: {
'data-qa-selector': 'ssh_key_delete_modal',
title: _('Are you sure you want to delete this SSH key?'),
diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb
index 2942765a108..e3e2f423da3 100644
--- a/app/helpers/submodule_helper.rb
+++ b/app/helpers/submodule_helper.rb
@@ -75,7 +75,7 @@ module SubmoduleHelper
return true if url_no_dotgit == [Gitlab.config.gitlab.url, '/', namespace, '/',
project].join('')
- url_with_dotgit = url_no_dotgit + '.git'
+ url_with_dotgit = "#{url_no_dotgit}.git"
url_with_dotgit == Gitlab::RepositoryUrlBuilder.build([namespace, '/', project].join(''))
end
@@ -108,7 +108,7 @@ module SubmoduleHelper
def relative_self_links(relative_path, commit, old_commit, project)
relative_path = relative_path.rstrip
- absolute_project_path = "/" + project.full_path
+ absolute_project_path = "/#{project.full_path}"
# Resolve `relative_path` to target path
# Assuming `absolute_project_path` is `/g1/p1`:
diff --git a/app/helpers/timeboxes_helper.rb b/app/helpers/timeboxes_helper.rb
index e0e6229bc6d..307f03e0d64 100644
--- a/app/helpers/timeboxes_helper.rb
+++ b/app/helpers/timeboxes_helper.rb
@@ -36,7 +36,7 @@ module TimeboxesHelper
end
end
- def milestones_browse_issuables_path(milestone, state: nil, type:)
+ def milestones_browse_issuables_path(milestone, type:, state: nil)
opts = { milestone_title: milestone.title, state: state }
if @project
diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb
index be63d28600f..d7c4540544b 100644
--- a/app/helpers/todos_helper.rb
+++ b/app/helpers/todos_helper.rb
@@ -16,17 +16,20 @@ module TodosHelper
def todo_action_name(todo)
case todo.action
when Todo::ASSIGNED then todo.self_added? ? _('assigned') : _('assigned you')
- when Todo::REVIEW_REQUESTED then s_('Todos|requested a review of')
+ when Todo::REVIEW_REQUESTED then s_('Todos|requested a review')
when Todo::MENTIONED, Todo::DIRECTLY_ADDRESSED then format(
- s_("Todos|mentioned %{who} on"), who: todo_action_subject(todo)
+ s_("Todos|mentioned %{who}"), who: todo_action_subject(todo)
)
- when Todo::BUILD_FAILED then s_('Todos|The pipeline failed in')
- when Todo::MARKED then s_('Todos|added a todo for')
+ when Todo::BUILD_FAILED then s_('Todos|The pipeline failed')
+ when Todo::MARKED then s_('Todos|added a to-do item')
when Todo::APPROVAL_REQUIRED then format(
- s_("Todos|set %{who} as an approver for"), who: todo_action_subject(todo)
+ s_("Todos|set %{who} as an approver"), who: todo_action_subject(todo)
)
when Todo::UNMERGEABLE then s_('Todos|Could not merge')
- when Todo::MERGE_TRAIN_REMOVED then s_("Todos|Removed from Merge Train:")
+ when Todo::MERGE_TRAIN_REMOVED then s_("Todos|Removed from Merge Train")
+ when Todo::MEMBER_ACCESS_REQUESTED then format(
+ s_("Todos|has requested access to group %{which}"), which: _(todo.target.name)
+ )
end
end
@@ -37,45 +40,48 @@ module TodosHelper
end
end
- def todo_target_link(todo)
- text = raw(todo_target_type_name(todo) + ' ') +
- if todo.for_commit?
- content_tag(:span, todo.target_reference, class: 'commit-sha')
- else
- todo.target_reference
- end
+ def todo_target_name(todo)
+ return todo.target_reference unless todo.for_commit?
- link_to text, todo_target_path(todo)
+ content_tag(:span, todo.target_reference, class: 'commit-sha')
end
def todo_target_title(todo)
- # Design To Dos' filenames are displayed in `#todo_target_link` (see `Design#to_reference`),
+ # Design To Dos' filenames are displayed in `#todo_target_name` (see `Design#to_reference`),
# so to avoid displaying duplicate filenames in the To Do list for designs,
# we return an empty string here.
- return "" if todo.target.blank? || todo.for_design?
+ return "" if todo.target.blank? || todo.for_design? || todo.member_access_requested?
- "\"#{todo.target.title}\""
+ todo.target.title.to_s
end
def todo_parent_path(todo)
if todo.resource_parent.is_a?(Group)
- link_to todo.resource_parent.name, group_path(todo.resource_parent)
+ todo.resource_parent.name
else
- link_to_project(todo.project)
+ title = content_tag(:span, todo.project.name, class: 'project-name')
+ namespace = content_tag(:span, "#{todo.project.namespace.human_name} / ", class: 'namespace-name')
+
+ title.prepend(namespace) if todo.project.namespace
+
+ title
end
end
- def todo_target_type_name(todo)
- return _('design') if todo.for_design?
- return _('alert') if todo.for_alert?
-
- target_type = if todo.for_issue_or_work_item?
+ def todo_target_aria_label(todo)
+ target_type = if todo.for_design?
+ _('Design')
+ elsif todo.for_alert?
+ _('Alert')
+ elsif todo.member_access_requested?
+ _('Group')
+ elsif todo.for_issue_or_work_item?
IntegrationsHelper.integration_issue_type(todo.target.issue_type)
else
IntegrationsHelper.integration_todo_target_type(todo.target_type)
end
- target_type.titleize.downcase
+ "#{target_type} #{todo_target_name(todo)}"
end
def todo_target_path(todo)
@@ -92,6 +98,8 @@ module TodosHelper
elsif todo.for_issue_or_work_item?
path_options[:only_path] = true
Gitlab::UrlBuilder.build(todo.target, **path_options)
+ elsif todo.member_access_requested?
+ todo.access_request_url
else
path = [todo.resource_parent, todo.target]
@@ -123,18 +131,18 @@ module TodosHelper
when MergeRequest
case state
when 'closed'
- background_class = 'gl-bg-red-500'
+ variant = 'danger'
when 'merged'
- background_class = 'gl-bg-blue-500'
+ variant = 'info'
end
when Issue
- background_class = 'gl-bg-blue-500' if state == 'closed'
+ variant = 'info' if state == 'closed'
when AlertManagement::Alert
- background_class = 'gl-bg-blue-500' if state == 'resolved'
+ variant = 'info' if state == 'resolved'
end
- tag.span class: "gl-my-0 gl-px-2 status-box #{background_class}" do
- raw_state_to_i18n[state] || state.capitalize
+ content_tag(:span, class: 'todo-target-state') do
+ gl_badge_tag(raw_state_to_i18n[state] || state.capitalize, { variant: variant, size: 'sm' })
end
end
@@ -183,7 +191,8 @@ module TodosHelper
{ id: Todo::REVIEW_REQUESTED, text: s_('Todos|Review requested') },
{ id: Todo::MENTIONED, text: s_('Todos|Mentioned') },
{ id: Todo::MARKED, text: s_('Todos|Added') },
- { id: Todo::BUILD_FAILED, text: s_('Todos|Pipelines') }
+ { id: Todo::BUILD_FAILED, text: s_('Todos|Pipelines') },
+ { id: Todo::MEMBER_ACCESS_REQUESTED, text: s_('Todos|Member access requested') }
]
end
@@ -222,10 +231,15 @@ module TodosHelper
end
content = content_tag(:span, class: css_class) do
- "Due #{is_due_today ? "today" : todo.target.due_date.to_s(:medium)}"
+ format(s_("Todos|Due %{due_date}"), due_date: if is_due_today
+ _("today")
+ else
+ l(todo.target.due_date,
+ format: Date::DATE_FORMATS[:medium])
+ end)
end
- "&middot; #{content}".html_safe
+ "#{content} &middot;".html_safe
end
def todo_author_display?(todo)
diff --git a/app/helpers/tooling/visual_review_helper.rb b/app/helpers/tooling/visual_review_helper.rb
index da6eb3ec434..cd3b8be5aac 100644
--- a/app/helpers/tooling/visual_review_helper.rb
+++ b/app/helpers/tooling/visual_review_helper.rb
@@ -14,10 +14,10 @@ module Tooling
GITLAB_ORG_GITLAB_PROJECT_PATH = 'gitlab-org/gitlab'
def visual_review_toolbar_options
- { 'data-merge-request-id': "#{ENV['REVIEW_APPS_MERGE_REQUEST_IID']}",
- 'data-mr-url': "#{GITLAB_INSTANCE_URL}",
- 'data-project-id': "#{GITLAB_ORG_GITLAB_PROJECT_ID}",
- 'data-project-path': "#{GITLAB_ORG_GITLAB_PROJECT_PATH}",
+ { 'data-merge-request-id': ENV['REVIEW_APPS_MERGE_REQUEST_IID'].to_s,
+ 'data-mr-url': GITLAB_INSTANCE_URL,
+ 'data-project-id': GITLAB_ORG_GITLAB_PROJECT_ID,
+ 'data-project-path': GITLAB_ORG_GITLAB_PROJECT_PATH,
'data-require-auth': false,
'id': 'review-app-toolbar-script',
'src': 'https://gitlab.com/assets/webpack/visual_review_toolbar.js' }
diff --git a/app/helpers/version_check_helper.rb b/app/helpers/version_check_helper.rb
index 48548ae9e6a..0bb92dfd118 100644
--- a/app/helpers/version_check_helper.rb
+++ b/app/helpers/version_check_helper.rb
@@ -1,6 +1,10 @@
# frozen_string_literal: true
module VersionCheckHelper
+ include Gitlab::Utils::StrongMemoize
+
+ SECURITY_ALERT_SEVERITY = 'danger'
+
def show_version_check?
return false unless Gitlab::CurrentSettings.version_check_enabled
return false if User.single_user&.requires_usage_stats_consent?
@@ -8,6 +12,17 @@ module VersionCheckHelper
current_user&.can_read_all_resources?
end
+ def gitlab_version_check
+ VersionCheck.new.response
+ end
+ strong_memoize_attr :gitlab_version_check
+
+ def show_security_patch_upgrade_alert?
+ return false unless show_version_check? && gitlab_version_check
+
+ gitlab_version_check['severity'] === SECURITY_ALERT_SEVERITY
+ end
+
def link_to_version
if Gitlab.pre_release?
commit_link = link_to(Gitlab.revision, source_host_url + namespace_project_commits_path(source_code_group, source_code_project, Gitlab.revision))
diff --git a/app/helpers/web_hooks/web_hooks_helper.rb b/app/helpers/web_hooks/web_hooks_helper.rb
index e95b90c69ef..bda9bf58fb7 100644
--- a/app/helpers/web_hooks/web_hooks_helper.rb
+++ b/app/helpers/web_hooks/web_hooks_helper.rb
@@ -7,8 +7,6 @@ module WebHooks
def show_project_hook_failed_callout?(project:)
return false if project_hook_page?
return false unless current_user
- return false unless Feature.enabled?(:webhooks_failed_callout, project)
- return false unless Feature.enabled?(:web_hooks_disable_failed, project)
return false unless Ability.allowed?(current_user, :read_web_hooks, project)
# Assumes include of Users::CalloutsHelper
diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb
index 017a1861905..b2b8ca2a120 100644
--- a/app/helpers/wiki_helper.rb
+++ b/app/helpers/wiki_helper.rb
@@ -60,7 +60,7 @@ module WikiHelper
end
def wiki_sort_controls(wiki, direction)
- link_class = 'gl-button btn btn-default btn-icon has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort'
+ link_class = 'gl-button btn btn-default btn-icon has-tooltip reverse-sort-btn rspec-reverse-sort'
reversed_direction = direction == 'desc' ? 'asc' : 'desc'
icon_class = direction == 'desc' ? 'highest' : 'lowest'
title = direction == 'desc' ? _('Sort direction: Descending') : _('Sort direction: Ascending')
diff --git a/app/helpers/x509_helper.rb b/app/helpers/x509_helper.rb
index 1a9dbefceef..599be0b91f7 100644
--- a/app/helpers/x509_helper.rb
+++ b/app/helpers/x509_helper.rb
@@ -16,8 +16,4 @@ module X509Helper
rescue StandardError
{}
end
-
- def x509_signature?(sig)
- sig.is_a?(CommitSignatures::X509CommitSignature) || sig.is_a?(Gitlab::X509::Signature)
- end
end
diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb
index 65ea90d0b5d..ede6007e0e2 100644
--- a/app/mailers/emails/profile.rb
+++ b/app/mailers/emails/profile.rb
@@ -94,12 +94,13 @@ module Emails
end
end
- def access_token_revoked_email(user, token_name)
+ def access_token_revoked_email(user, token_name, source = nil)
return unless user&.active?
@user = user
@token_name = token_name
@target_url = profile_personal_access_tokens_url
+ @source = source
Gitlab::I18n.with_locale(@user.preferred_language) do
mail_with_locale(to: @user.notification_email_or_default, subject: subject(_("A personal access token has been revoked")))
diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb
index 7cfebf0473f..f1f22d94061 100644
--- a/app/models/abuse_report.rb
+++ b/app/models/abuse_report.rb
@@ -14,7 +14,7 @@ class AbuseReport < ApplicationRecord
validates :message, presence: true
validates :user_id, uniqueness: { message: 'has already been reported' }
- scope :by_user, -> (user) { where(user_id: user) }
+ scope :by_user, ->(user) { where(user_id: user) }
scope :with_users, -> { includes(:reporter, :user) }
# For CacheMarkdownField
diff --git a/app/models/achievements/achievement.rb b/app/models/achievements/achievement.rb
new file mode 100644
index 00000000000..904961491b5
--- /dev/null
+++ b/app/models/achievements/achievement.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Achievements
+ class Achievement < ApplicationRecord
+ include Avatarable
+ include StripAttribute
+
+ belongs_to :namespace, inverse_of: :achievements, optional: false
+
+ strip_attributes! :name, :description
+
+ validates :name,
+ presence: true,
+ length: { maximum: 255 },
+ uniqueness: { case_sensitive: false, scope: [:namespace_id] }
+ validates :description, length: { maximum: 1024 }
+ end
+end
diff --git a/app/models/alert_management/alert.rb b/app/models/alert_management/alert.rb
index 9f05c87018d..a5a539eae75 100644
--- a/app/models/alert_management/alert.rb
+++ b/app/models/alert_management/alert.rb
@@ -53,7 +53,7 @@ module AlertManagement
validates :fingerprint, allow_blank: true, uniqueness: {
scope: :project,
conditions: -> { not_resolved },
- message: -> (object, data) { _('Cannot have multiple unresolved alerts') }
+ message: ->(object, data) { _('Cannot have multiple unresolved alerts') }
}, unless: :resolved?
validate :hosts_format
@@ -74,23 +74,23 @@ module AlertManagement
delegate :iid, to: :issue, prefix: true, allow_nil: true
delegate :details_url, to: :present
- scope :for_iid, -> (iid) { where(iid: iid) }
- scope :for_fingerprint, -> (project, fingerprint) { where(project: project, fingerprint: fingerprint) }
- scope :for_environment, -> (environment) { where(environment: environment) }
- scope :for_assignee_username, -> (assignee_username) { joins(:assignees).merge(User.by_username(assignee_username)) }
- scope :search, -> (query) { fuzzy_search(query, [:title, :description, :monitoring_tool, :service]) }
+ scope :for_iid, ->(iid) { where(iid: iid) }
+ scope :for_fingerprint, ->(project, fingerprint) { where(project: project, fingerprint: fingerprint) }
+ scope :for_environment, ->(environment) { where(environment: environment) }
+ scope :for_assignee_username, ->(assignee_username) { joins(:assignees).merge(User.by_username(assignee_username)) }
+ scope :search, ->(query) { fuzzy_search(query, [:title, :description, :monitoring_tool, :service]) }
scope :not_resolved, -> { without_status(:resolved) }
scope :with_prometheus_alert, -> { includes(:prometheus_alert) }
scope :with_operations_alerts, -> { where(domain: :operations) }
- scope :order_start_time, -> (sort_order) { order(started_at: sort_order) }
- scope :order_end_time, -> (sort_order) { order(ended_at: sort_order) }
- scope :order_event_count, -> (sort_order) { order(events: sort_order) }
+ scope :order_start_time, ->(sort_order) { order(started_at: sort_order) }
+ scope :order_end_time, ->(sort_order) { order(ended_at: sort_order) }
+ scope :order_event_count, ->(sort_order) { order(events: sort_order) }
# Ascending sort order sorts severity from less critical to more critical.
# Descending sort order sorts severity from more critical to less critical.
# https://gitlab.com/gitlab-org/gitlab/-/issues/221242#what-is-the-expected-correct-behavior
- scope :order_severity, -> (sort_order) { order(severity: sort_order == :asc ? :desc : :asc) }
+ scope :order_severity, ->(sort_order) { order(severity: sort_order == :asc ? :desc : :asc) }
scope :order_severity_with_open_prometheus_alert, -> { open.with_prometheus_alert.order(severity: :asc, started_at: :desc) }
scope :counts_by_project_id, -> { group(:project_id).count }
diff --git a/app/models/alert_management/http_integration.rb b/app/models/alert_management/http_integration.rb
index b2686924363..906855d6dfc 100644
--- a/app/models/alert_management/http_integration.rb
+++ b/app/models/alert_management/http_integration.rb
@@ -28,7 +28,7 @@ module AlertManagement
before_validation :ensure_token
before_validation :ensure_payload_example_not_nil
- scope :for_endpoint_identifier, -> (endpoint_identifier) { where(endpoint_identifier: endpoint_identifier) }
+ scope :for_endpoint_identifier, ->(endpoint_identifier) { where(endpoint_identifier: endpoint_identifier) }
scope :active, -> { where(active: true) }
scope :ordered_by_id, -> { order(:id) }
diff --git a/app/models/analytics/cycle_analytics/aggregation.rb b/app/models/analytics/cycle_analytics/aggregation.rb
index 2e58d64ae95..a888422a6b4 100644
--- a/app/models/analytics/cycle_analytics/aggregation.rb
+++ b/app/models/analytics/cycle_analytics/aggregation.rb
@@ -1,24 +1,15 @@
# frozen_string_literal: true
class Analytics::CycleAnalytics::Aggregation < ApplicationRecord
- include IgnorableColumns
include FromUnion
belongs_to :group, optional: false
validates :incremental_runtimes_in_seconds, :incremental_processed_records, :full_runtimes_in_seconds, :full_processed_records, presence: true, length: { maximum: 10 }, allow_blank: true
- scope :priority_order, -> (column_to_sort = :last_incremental_run_at) { order(arel_table[column_to_sort].asc.nulls_first) }
+ scope :priority_order, ->(column_to_sort = :last_incremental_run_at) { order(arel_table[column_to_sort].asc.nulls_first) }
scope :enabled, -> { where('enabled IS TRUE') }
- # These columns were added with wrong naming convention, the columns were never used.
- ignore_column :last_full_run_processed_records, remove_with: '15.1', remove_after: '2022-05-22'
- ignore_column :last_full_run_runtimes_in_seconds, remove_with: '15.1', remove_after: '2022-05-22'
- ignore_column :last_full_run_issues_updated_at, remove_with: '15.1', remove_after: '2022-05-22'
- ignore_column :last_full_run_mrs_updated_at, remove_with: '15.1', remove_after: '2022-05-22'
- ignore_column :last_full_run_issues_id, remove_with: '15.1', remove_after: '2022-05-22'
- ignore_column :last_full_run_merge_requests_id, remove_with: '15.1', remove_after: '2022-05-22'
-
def cursor_for(mode, model)
{
updated_at: self["last_#{mode}_#{model.table_name}_updated_at"],
diff --git a/app/models/analytics/usage_trends/measurement.rb b/app/models/analytics/usage_trends/measurement.rb
index 02e239ca0ef..c1245d8dce7 100644
--- a/app/models/analytics/usage_trends/measurement.rb
+++ b/app/models/analytics/usage_trends/measurement.rb
@@ -23,9 +23,9 @@ module Analytics
validates :recorded_at, uniqueness: { scope: :identifier }
scope :order_by_latest, -> { order(recorded_at: :desc) }
- scope :with_identifier, -> (identifier) { where(identifier: identifier) }
- scope :recorded_after, -> (date) { where(self.model.arel_table[:recorded_at].gteq(date)) if date.present? }
- scope :recorded_before, -> (date) { where(self.model.arel_table[:recorded_at].lteq(date)) if date.present? }
+ scope :with_identifier, ->(identifier) { where(identifier: identifier) }
+ scope :recorded_after, ->(date) { where(self.model.arel_table[:recorded_at].gteq(date)) if date.present? }
+ scope :recorded_before, ->(date) { where(self.model.arel_table[:recorded_at].lteq(date)) if date.present? }
def self.identifier_query_mapping
{
diff --git a/app/models/appearance.rb b/app/models/appearance.rb
index bd948c2c32a..4a046b3ab20 100644
--- a/app/models/appearance.rb
+++ b/app/models/appearance.rb
@@ -3,10 +3,10 @@
class Appearance < ApplicationRecord
include CacheableAttributes
include CacheMarkdownField
- include ObjectStorage::BackgroundMove
include WithUploads
attribute :title, default: ''
+ attribute :short_title, default: ''
attribute :description, default: ''
attribute :new_project_guidelines, default: ''
attribute :profile_image_guidelines, default: ''
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index adbbddd635c..3fb1f58f3e0 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -11,6 +11,7 @@ class ApplicationSetting < ApplicationRecord
ignore_columns %i[elasticsearch_shards elasticsearch_replicas], remove_with: '14.4', remove_after: '2021-09-22'
ignore_columns %i[static_objects_external_storage_auth_token], remove_with: '14.9', remove_after: '2022-03-22'
ignore_column :user_email_lookup_limit, remove_with: '15.0', remove_after: '2022-04-18'
+ ignore_column :send_user_confirmation_email, remove_with: '15.8', remove_after: '2022-12-18'
INSTANCE_REVIEW_MIN_USERS = 50
GRAFANA_URL_ERROR_MESSAGE = 'Please check your Grafana URL setting in ' \
@@ -20,7 +21,7 @@ class ApplicationSetting < ApplicationRecord
'Admin Area > Settings > General > Kroki'
enum whats_new_variant: { all_tiers: 0, current_tier: 1, disabled: 2 }, _prefix: true
- enum email_confirmation_setting: { off: 0, soft: 1, hard: 2 }
+ enum email_confirmation_setting: { off: 0, soft: 1, hard: 2 }, _prefix: true
add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption) ? :optional : :required }
add_authentication_token_field :health_check_access_token
@@ -87,7 +88,7 @@ class ApplicationSetting < ApplicationRecord
validates :grafana_url,
system_hook_url: {
- blocked_message: "is blocked: %{exception_message}. " + GRAFANA_URL_ERROR_MESSAGE
+ blocked_message: "is blocked: %{exception_message}. #{GRAFANA_URL_ERROR_MESSAGE}"
},
if: :grafana_url_absolute?
@@ -226,6 +227,10 @@ class ApplicationSetting < ApplicationRecord
presence: true,
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
+ validates :max_terraform_state_size_bytes,
+ presence: true,
+ numericality: { only_integer: true, greater_than_or_equal_to: 0 }
+
validates :default_artifacts_expire_in, presence: true, duration: true
validates :container_expiration_policies_enable_historic_entries,
@@ -412,12 +417,10 @@ class ApplicationSetting < ApplicationRecord
allow_nil: false,
inclusion: { in: [true, false], message: N_('must be a boolean value') }
- # rubocop:disable Cop/StaticTranslationDefinition
validates :deactivate_dormant_users_period,
presence: true,
- numericality: { only_integer: true, greater_than_or_equal_to: 90, message: _("'%{value}' days of inactivity must be greater than or equal to 90") },
+ numericality: { only_integer: true, greater_than_or_equal_to: 90, message: N_("'%{value}' days of inactivity must be greater than or equal to 90") },
if: :deactivate_dormant_users?
- # rubocop:enable Cop/StaticTranslationDefinition
Gitlab::SSHPublicKey.supported_types.each do |type|
validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type }
@@ -466,7 +469,7 @@ class ApplicationSetting < ApplicationRecord
validates :external_auth_client_key,
presence: true,
- if: -> (setting) { setting.external_auth_client_cert.present? }
+ if: ->(setting) { setting.external_auth_client_cert.present? }
validates :lets_encrypt_notification_email,
devise_email: true,
@@ -488,17 +491,17 @@ class ApplicationSetting < ApplicationRecord
validates :eks_access_key_id,
length: { in: 16..128 },
- if: -> (setting) { setting.eks_integration_enabled? && setting.eks_access_key_id.present? }
+ if: ->(setting) { setting.eks_integration_enabled? && setting.eks_access_key_id.present? }
validates :eks_secret_access_key,
presence: true,
- if: -> (setting) { setting.eks_integration_enabled? && setting.eks_access_key_id.present? }
+ if: ->(setting) { setting.eks_integration_enabled? && setting.eks_access_key_id.present? }
validates_with X509CertificateCredentialsValidator,
certificate: :external_auth_client_cert,
pkey: :external_auth_client_key,
pass: :external_auth_client_key_pass,
- if: -> (setting) { setting.external_auth_client_cert.present? }
+ if: ->(setting) { setting.external_auth_client_cert.present? }
validates :default_ci_config_path,
format: { without: %r{(\.{2}|\A/)},
@@ -687,6 +690,10 @@ class ApplicationSetting < ApplicationRecord
validates :disable_admin_oauth_scopes,
inclusion: { in: [true, false], message: N_('must be a boolean value') }
+ validates :bulk_import_enabled,
+ allow_nil: false,
+ inclusion: { in: [true, false], message: N_('must be a boolean value') }
+
before_validation :ensure_uuid!
before_validation :coerce_repository_storages_weighted, if: :repository_storages_weighted_changed?
before_validation :normalize_default_branch_name
diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb
index 308c05d638c..229c4e68d79 100644
--- a/app/models/application_setting_implementation.rb
+++ b/app/models/application_setting_implementation.rb
@@ -76,6 +76,7 @@ module ApplicationSettingImplementation
eks_account_id: nil,
eks_integration_enabled: false,
eks_secret_access_key: nil,
+ email_confirmation_setting: 'off',
email_restrictions_enabled: false,
email_restrictions: nil,
external_pipeline_validation_service_timeout: nil,
@@ -113,6 +114,7 @@ module ApplicationSettingImplementation
max_attachment_size: Settings.gitlab['max_attachment_size'],
max_export_size: 0,
max_import_size: 0,
+ max_terraform_state_size_bytes: 0,
max_yaml_size_bytes: 1.megabyte,
max_yaml_depth: 100,
minimum_password_length: DEFAULT_MINIMUM_PASSWORD_LENGTH,
@@ -146,7 +148,6 @@ module ApplicationSettingImplementation
require_two_factor_authentication: false,
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
rsa_key_restriction: default_min_key_size(:rsa),
- send_user_confirmation_email: false,
session_expire_delay: Settings.gitlab['session_expire_delay'],
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
shared_runners_text: nil,
@@ -243,7 +244,8 @@ module ApplicationSettingImplementation
search_rate_limit_unauthenticated: 10,
users_get_by_id_limit: 300,
users_get_by_id_limit_allowlist: [],
- can_create_group: true
+ can_create_group: true,
+ bulk_import_enabled: false
}
end
diff --git a/app/models/audit_event.rb b/app/models/audit_event.rb
index 0ad17cd8869..5cc87be388f 100644
--- a/app/models/audit_event.rb
+++ b/app/models/audit_event.rb
@@ -28,11 +28,11 @@ class AuditEvent < ApplicationRecord
validates :entity_type, presence: true
validates :ip_address, ip_address: true
- scope :by_entity_type, -> (entity_type) { where(entity_type: entity_type) }
- scope :by_entity_id, -> (entity_id) { where(entity_id: entity_id) }
- scope :by_author_id, -> (author_id) { where(author_id: author_id) }
- scope :by_entity_username, -> (username) { where(entity_id: find_user_id(username)) }
- scope :by_author_username, -> (username) { where(author_id: find_user_id(username)) }
+ scope :by_entity_type, ->(entity_type) { where(entity_type: entity_type) }
+ scope :by_entity_id, ->(entity_id) { where(entity_id: entity_id) }
+ scope :by_author_id, ->(author_id) { where(author_id: author_id) }
+ scope :by_entity_username, ->(username) { where(entity_id: find_user_id(username)) }
+ scope :by_author_username, ->(username) { where(author_id: find_user_id(username)) }
after_initialize :initialize_details
diff --git a/app/models/award_emoji.rb b/app/models/award_emoji.rb
index e9530a80d9f..f41f0a8be84 100644
--- a/app/models/award_emoji.rb
+++ b/app/models/award_emoji.rb
@@ -23,11 +23,11 @@ class AwardEmoji < ApplicationRecord
scope :downvotes, -> { named(DOWNVOTE_NAME) }
scope :upvotes, -> { named(UPVOTE_NAME) }
- scope :named, -> (names) { where(name: names) }
- scope :awarded_by, -> (users) { where(user: users) }
+ scope :named, ->(names) { where(name: names) }
+ scope :awarded_by, ->(users) { where(user: users) }
- after_save :expire_cache
after_destroy :expire_cache
+ after_save :expire_cache
class << self
def votes_for_collection(ids, type)
diff --git a/app/models/badge.rb b/app/models/badge.rb
index 4339d419b48..0676de10d02 100644
--- a/app/models/badge.rb
+++ b/app/models/badge.rb
@@ -8,6 +8,8 @@ class Badge < ApplicationRecord
# the placeholder is found.
PLACEHOLDERS = {
'project_path' => :full_path,
+ 'project_title' => :title,
+ 'project_name' => :path,
'project_id' => :id,
'default_branch' => :default_branch,
'commit_sha' => ->(project) { project.commit&.sha }
diff --git a/app/models/blob_viewer/metrics_dashboard_yml.rb b/app/models/blob_viewer/metrics_dashboard_yml.rb
index cac6b2192d0..4b7a178566c 100644
--- a/app/models/blob_viewer/metrics_dashboard_yml.rb
+++ b/app/models/blob_viewer/metrics_dashboard_yml.rb
@@ -25,11 +25,7 @@ module BlobViewer
private
def parse_blob_data
- if Feature.enabled?(:metrics_dashboard_exhaustive_validations, project)
- exhaustive_metrics_dashboard_validation
- else
- old_metrics_dashboard_validation
- end
+ old_metrics_dashboard_validation
end
def old_metrics_dashboard_validation
@@ -41,14 +37,5 @@ module BlobViewer
rescue ActiveModel::ValidationError => e
e.model.errors.messages.map { |messages| messages.join(': ') }
end
-
- def exhaustive_metrics_dashboard_validation
- yaml = ::Gitlab::Config::Loader::Yaml.new(blob.data).load_raw!
- Gitlab::Metrics::Dashboard::Validator
- .errors(yaml, dashboard_path: blob.path, project: project)
- .map(&:message)
- rescue Gitlab::Config::Loader::FormatError => e
- [e.message]
- end
end
end
diff --git a/app/models/board_group_recent_visit.rb b/app/models/board_group_recent_visit.rb
index dc273e256a8..65299d6dd12 100644
--- a/app/models/board_group_recent_visit.rb
+++ b/app/models/board_group_recent_visit.rb
@@ -12,7 +12,7 @@ class BoardGroupRecentVisit < ApplicationRecord
validates :group, presence: true
validates :board, presence: true
- scope :by_user_parent, -> (user, group) { where(user: user, group: group) }
+ scope :by_user_parent, ->(user, group) { where(user: user, group: group) }
def self.board_parent_relation
:group
diff --git a/app/models/board_project_recent_visit.rb b/app/models/board_project_recent_visit.rb
index 723afd6feab..c5122392b91 100644
--- a/app/models/board_project_recent_visit.rb
+++ b/app/models/board_project_recent_visit.rb
@@ -12,7 +12,7 @@ class BoardProjectRecentVisit < ApplicationRecord
validates :project, presence: true
validates :board, presence: true
- scope :by_user_parent, -> (user, project) { where(user: user, project: project) }
+ scope :by_user_parent, ->(user, project) { where(user: user, project: project) }
def self.board_parent_relation
:project
diff --git a/app/models/bulk_import.rb b/app/models/bulk_import.rb
index 2200a66b3c2..2565ad5f2b8 100644
--- a/app/models/bulk_import.rb
+++ b/app/models/bulk_import.rb
@@ -17,7 +17,7 @@ class BulkImport < ApplicationRecord
enum source_type: { gitlab: 0 }
scope :stale, -> { where('created_at < ?', 8.hours.ago).where(status: [0, 1]) }
- scope :order_by_created_at, -> (direction) { order(created_at: direction) }
+ scope :order_by_created_at, ->(direction) { order(created_at: direction) }
state_machine :status, initial: :created do
state :created, value: 0
diff --git a/app/models/bulk_imports/entity.rb b/app/models/bulk_imports/entity.rb
index a2542e669e1..e49c4e09a50 100644
--- a/app/models/bulk_imports/entity.rb
+++ b/app/models/bulk_imports/entity.rb
@@ -53,7 +53,7 @@ class BulkImports::Entity < ApplicationRecord
scope :by_user_id, ->(user_id) { joins(:bulk_import).where(bulk_imports: { user_id: user_id }) }
scope :stale, -> { where('created_at < ?', 8.hours.ago).where(status: [0, 1]) }
scope :by_bulk_import_id, ->(bulk_import_id) { where(bulk_import_id: bulk_import_id) }
- scope :order_by_created_at, -> (direction) { order(created_at: direction) }
+ scope :order_by_created_at, ->(direction) { order(created_at: direction) }
alias_attribute :destination_slug, :destination_name
diff --git a/app/models/bulk_imports/export_upload.rb b/app/models/bulk_imports/export_upload.rb
index a9cba5119af..4304032b28c 100644
--- a/app/models/bulk_imports/export_upload.rb
+++ b/app/models/bulk_imports/export_upload.rb
@@ -3,7 +3,6 @@
module BulkImports
class ExportUpload < ApplicationRecord
include WithUploads
- include ObjectStorage::BackgroundMove
self.table_name = 'bulk_import_export_uploads'
diff --git a/app/models/bulk_imports/tracker.rb b/app/models/bulk_imports/tracker.rb
index 357f4629078..b04ef1cb7ae 100644
--- a/app/models/bulk_imports/tracker.rb
+++ b/app/models/bulk_imports/tracker.rb
@@ -26,7 +26,7 @@ class BulkImports::Tracker < ApplicationRecord
entity_scope = where(bulk_import_entity_id: entity_id)
next_stage_scope = entity_scope.with_status(:created).select('MIN(stage)')
- entity_scope.where(stage: next_stage_scope)
+ entity_scope.where(stage: next_stage_scope).with_status(:created)
}
def self.stage_running?(entity_id, stage)
diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb
index d6051d70503..662fb3cffa8 100644
--- a/app/models/ci/bridge.rb
+++ b/app/models/ci/bridge.rb
@@ -18,8 +18,11 @@ module Ci
belongs_to :project
belongs_to :trigger_request
+
+ # To be removed upon :ci_bridge_remove_sourced_pipelines feature flag removal
has_many :sourced_pipelines, class_name: "::Ci::Sources::Pipeline",
- foreign_key: :source_job_id
+ foreign_key: :source_job_id,
+ inverse_of: :source_bridge
has_one :downstream_pipeline, through: :sourced_pipeline, source: :pipeline
@@ -86,8 +89,20 @@ module Ci
end
end
+ def sourced_pipelines
+ if Feature.enabled?(:ci_bridge_remove_sourced_pipelines, project)
+ raise 'Ci::Bridge does not have sourced_pipelines association'
+ end
+
+ super
+ end
+
def has_downstream_pipeline?
- sourced_pipelines.exists?
+ if Feature.enabled?(:ci_bridge_remove_sourced_pipelines, project)
+ sourced_pipeline.present?
+ else
+ sourced_pipelines.exists?
+ end
end
def downstream_pipeline_params
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index f44ba124fe2..7f42b21bc87 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -7,7 +7,6 @@ module Ci
include Ci::Contextable
include TokenAuthenticatable
include AfterCommitQueue
- include ObjectStorage::BackgroundMove
include Presentable
include Importable
include Ci::HasRef
@@ -47,7 +46,7 @@ module Ci
# DELETE queries when the Ci::Build is destroyed. The next step is to remove `dependent: :destroy`.
# Details: https://gitlab.com/gitlab-org/gitlab/-/issues/24644#note_689472685
has_many :job_artifacts, class_name: 'Ci::JobArtifact', foreign_key: :job_id, dependent: :destroy, inverse_of: :job # rubocop:disable Cop/ActiveRecordDependent
- has_many :job_variables, class_name: 'Ci::JobVariable', foreign_key: :job_id
+ has_many :job_variables, class_name: 'Ci::JobVariable', foreign_key: :job_id, inverse_of: :job
has_many :sourced_pipelines, class_name: 'Ci::Sources::Pipeline', foreign_key: :source_job_id
has_many :pages_deployments, inverse_of: :ci_build
@@ -71,6 +70,7 @@ module Ci
delegate :harbor_integration, to: :project
delegate :trigger_short_token, to: :trigger_request, allow_nil: true
delegate :ensure_persistent_ref, to: :pipeline
+ delegate :enable_debug_trace!, to: :metadata
serialize :options # rubocop:disable Cop/ActiveRecordSerialize
serialize :yaml_variables, Gitlab::Serializer::Ci::Variables # rubocop:disable Cop/ActiveRecordSerialize
@@ -90,7 +90,7 @@ module Ci
scope :with_downloadable_artifacts, -> do
where('EXISTS (?)',
Ci::JobArtifact.select(1)
- .where('ci_builds.id = ci_job_artifacts.job_id')
+ .where("#{Ci::Build.quoted_table_name}.id = #{Ci::JobArtifact.quoted_table_name}.job_id")
.where(file_type: Ci::JobArtifact::DOWNLOADABLE_TYPES)
)
end
@@ -98,7 +98,7 @@ module Ci
scope :with_erasable_artifacts, -> do
where('EXISTS (?)',
Ci::JobArtifact.select(1)
- .where('ci_builds.id = ci_job_artifacts.job_id')
+ .where("#{Ci::Build.quoted_table_name}.id = #{Ci::JobArtifact.quoted_table_name}.job_id")
.where(file_type: Ci::JobArtifact.erasable_file_types)
)
end
@@ -108,11 +108,11 @@ module Ci
end
scope :with_existing_job_artifacts, ->(query) do
- where('EXISTS (?)', ::Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').merge(query))
+ where('EXISTS (?)', ::Ci::JobArtifact.select(1).where("#{Ci::Build.quoted_table_name}.id = #{Ci::JobArtifact.quoted_table_name}.job_id").merge(query))
end
scope :without_archived_trace, -> do
- where('NOT EXISTS (?)', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').trace)
+ where('NOT EXISTS (?)', Ci::JobArtifact.select(1).where("#{Ci::Build.quoted_table_name}.id = #{Ci::JobArtifact.quoted_table_name}.job_id").trace)
end
scope :with_artifacts, ->(artifact_scope) do
@@ -155,7 +155,7 @@ module Ci
scope :manual_actions, -> { where(when: :manual, status: COMPLETED_STATUSES + %i[manual]) }
scope :scheduled_actions, -> { where(when: :delayed, status: COMPLETED_STATUSES + %i[scheduled]) }
scope :ref_protected, -> { where(protected: true) }
- scope :with_live_trace, -> { where('EXISTS (?)', Ci::BuildTraceChunk.where('ci_builds.id = ci_build_trace_chunks.build_id').select(1)) }
+ scope :with_live_trace, -> { where('EXISTS (?)', Ci::BuildTraceChunk.where("#{quoted_table_name}.id = #{Ci::BuildTraceChunk.quoted_table_name}.build_id").select(1)) }
scope :with_stale_live_trace, -> { with_live_trace.finished_before(12.hours.ago) }
scope :finished_before, -> (date) { finished.where('finished_at < ?', date) }
scope :license_management_jobs, -> { where(name: %i(license_management license_scanning)) } # handle license rename https://gitlab.com/gitlab-org/gitlab/issues/8911
@@ -172,8 +172,6 @@ module Ci
add_authentication_token_field :token, encrypted: :required
- before_save :ensure_token, unless: :assign_token_on_scheduling?
-
after_save :stick_build_if_status_changed
after_create unless: :importing? do |build|
@@ -247,11 +245,8 @@ module Ci
!build.waiting_for_deployment_approval? # If false is returned, it stops the transition
end
- before_transition any => [:pending] do |build, transition|
- if build.assign_token_on_scheduling?
- build.ensure_token
- end
-
+ before_transition any => [:pending] do |build|
+ build.ensure_token
true
end
@@ -419,12 +414,12 @@ module Ci
end
def waiting_for_deployment_approval?
- manual? && starts_environment? && deployment&.blocked?
+ manual? && deployment_job? && deployment&.blocked?
end
def outdated_deployment?
strong_memoize(:outdated_deployment) do
- starts_environment? &&
+ deployment_job? &&
incomplete? &&
project.ci_forward_deployment_enabled? &&
deployment&.older_than_last_successful_deployment?
@@ -528,7 +523,7 @@ module Ci
environment.present?
end
- def starts_environment?
+ def deployment_job?
has_environment_keyword? && self.environment_action == 'start'
end
@@ -722,7 +717,7 @@ module Ci
end
def ensure_trace_metadata!
- Ci::BuildTraceMetadata.find_or_upsert_for!(id)
+ Ci::BuildTraceMetadata.find_or_upsert_for!(id, partition_id)
end
def artifacts_expose_as
@@ -866,6 +861,10 @@ module Ci
Gitlab::Ci::Build::Step.from_after_script(self)].compact
end
+ def runtime_hooks
+ Gitlab::Ci::Build::Hook.from_hooks(self)
+ end
+
def image
Gitlab::Ci::Build::Image.from_image(self)
end
@@ -995,7 +994,7 @@ module Ci
# Virtual deployment status depending on the environment status.
def deployment_status
- return unless starts_environment?
+ return unless deployment_job?
if success?
return successful_deployment_status
@@ -1136,8 +1135,15 @@ module Ci
end
end
- def assign_token_on_scheduling?
- ::Feature.enabled?(:ci_assign_job_token_on_scheduling, project)
+ def partition_id_token_prefix
+ partition_id.to_s(16) if Feature.enabled?(:ci_build_partition_id_token_prefix, project)
+ end
+
+ override :format_token
+ def format_token(token)
+ return token if partition_id_token_prefix.nil?
+
+ "#{partition_id_token_prefix}_#{token}"
end
protected
@@ -1208,11 +1214,11 @@ module Ci
if project.ci_cd_settings.opt_in_jwt?
id_tokens_variables
else
- legacy_jwt_variables.concat(id_tokens_variables)
+ predefined_jwt_variables.concat(id_tokens_variables)
end
end
- def legacy_jwt_variables
+ def predefined_jwt_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
jwt = Gitlab::Ci::Jwt.for_build(self)
jwt_v2 = Gitlab::Ci::JwtV2.for_build(self)
@@ -1229,7 +1235,7 @@ module Ci
Gitlab::Ci::Variables::Collection.new.tap do |variables|
id_tokens.each do |var_name, token_data|
- token = Gitlab::Ci::JwtV2.for_build(self, aud: token_data['id_token']['aud'])
+ token = Gitlab::Ci::JwtV2.for_build(self, aud: token_data['aud'])
variables.append(key: var_name, value: token, public: false, masked: true)
end
diff --git a/app/models/ci/build_metadata.rb b/app/models/ci/build_metadata.rb
index 2f28509f812..9b4794abb2e 100644
--- a/app/models/ci/build_metadata.rb
+++ b/app/models/ci/build_metadata.rb
@@ -5,21 +5,16 @@ module Ci
# Data that should be persisted forever, should be stored with Ci::Build model.
class BuildMetadata < Ci::ApplicationRecord
BuildTimeout = Struct.new(:value, :source)
- ROUTING_FEATURE_FLAG = :ci_partitioning_use_ci_builds_metadata_routing_table
include Ci::Partitionable
include Presentable
include ChronicDurationAttribute
include Gitlab::Utils::StrongMemoize
- self.table_name = 'ci_builds_metadata'
+ self.table_name = 'p_ci_builds_metadata'
self.primary_key = 'id'
- self.sequence_name = 'ci_builds_metadata_id_seq'
- partitionable scope: :build, through: {
- table: :p_ci_builds_metadata,
- flag: ROUTING_FEATURE_FLAG
- }
+ partitionable scope: :build
belongs_to :build, class_name: 'CommitStatus'
belongs_to :project
@@ -63,6 +58,12 @@ module Ci
runtime_runner_features[:cancel_gracefully] == true
end
+ def enable_debug_trace!
+ self.debug_trace_enabled = true
+ save! if changes.any?
+ true
+ end
+
private
def set_build_project
diff --git a/app/models/ci/build_need.rb b/app/models/ci/build_need.rb
index d4cbbfac4ab..3fa17d6d286 100644
--- a/app/models/ci/build_need.rb
+++ b/app/models/ci/build_need.rb
@@ -2,15 +2,18 @@
module Ci
class BuildNeed < Ci::ApplicationRecord
+ include Ci::Partitionable
include BulkInsertSafe
belongs_to :build, class_name: "Ci::Processable", foreign_key: :build_id, inverse_of: :needs
+ partitionable scope: :build
+
validates :build, presence: true
validates :name, presence: true, length: { maximum: 128 }
validates :optional, inclusion: { in: [true, false] }
- scope :scoped_build, -> { where('ci_builds.id=ci_build_needs.build_id') }
+ scope :scoped_build, -> { where("#{Ci::Build.quoted_table_name}.id = #{quoted_table_name}.build_id") }
scope :artifacts, -> { where(artifacts: true) }
end
end
diff --git a/app/models/ci/build_pending_state.rb b/app/models/ci/build_pending_state.rb
index 53cf0697e2e..3684dac06c7 100644
--- a/app/models/ci/build_pending_state.rb
+++ b/app/models/ci/build_pending_state.rb
@@ -1,8 +1,12 @@
# frozen_string_literal: true
class Ci::BuildPendingState < Ci::ApplicationRecord
+ include Ci::Partitionable
+
belongs_to :build, class_name: 'Ci::Build', foreign_key: :build_id
+ partitionable scope: :build
+
enum state: Ci::Stage.statuses
enum failure_reason: CommitStatus.failure_reasons
diff --git a/app/models/ci/build_report_result.rb b/app/models/ci/build_report_result.rb
index b674c1b1a0e..b2d99fab295 100644
--- a/app/models/ci/build_report_result.rb
+++ b/app/models/ci/build_report_result.rb
@@ -2,11 +2,15 @@
module Ci
class BuildReportResult < Ci::ApplicationRecord
+ include Ci::Partitionable
+
self.primary_key = :build_id
belongs_to :build, class_name: "Ci::Build", inverse_of: :report_results
belongs_to :project, class_name: "Project", inverse_of: :build_report_results
+ partitionable scope: :build
+
validates :build, :project, presence: true
validates :data, json_schema: { filename: "build_report_result_data" }
diff --git a/app/models/ci/build_runner_session.rb b/app/models/ci/build_runner_session.rb
index 0f37ce70964..20c0b04e228 100644
--- a/app/models/ci/build_runner_session.rb
+++ b/app/models/ci/build_runner_session.rb
@@ -4,6 +4,8 @@ module Ci
# The purpose of this class is to store Build related runner session.
# Data will be removed after transitioning from running to any state.
class BuildRunnerSession < Ci::ApplicationRecord
+ include Ci::Partitionable
+
TERMINAL_SUBPROTOCOL = 'terminal.gitlab.com'
DEFAULT_SERVICE_NAME = 'build'
DEFAULT_PORT_NAME = 'default_port'
@@ -12,6 +14,8 @@ module Ci
belongs_to :build, class_name: 'Ci::Build', inverse_of: :runner_session
+ partitionable scope: :build
+
validates :build, presence: true
validates :url, public_url: { schemes: %w(https) }
diff --git a/app/models/ci/build_trace_chunk.rb b/app/models/ci/build_trace_chunk.rb
index 7baa98b59f9..57d8b9ba368 100644
--- a/app/models/ci/build_trace_chunk.rb
+++ b/app/models/ci/build_trace_chunk.rb
@@ -2,6 +2,7 @@
module Ci
class BuildTraceChunk < Ci::ApplicationRecord
+ include Ci::Partitionable
include ::Comparable
include ::FastDestroyAll
include ::Checksummable
@@ -10,6 +11,8 @@ module Ci
belongs_to :build, class_name: "Ci::Build", foreign_key: :build_id
+ partitionable scope: :build
+
attribute :data_store, default: :redis_trace_chunks
after_create { metrics.increment_trace_operation(operation: :chunked) }
@@ -28,8 +31,8 @@ module Ci
redis_trace_chunks: 4
}.freeze
- STORE_TYPES = DATA_STORES.keys.to_h do |store|
- [store, "Ci::BuildTraceChunks::#{store.to_s.camelize}".constantize]
+ STORE_TYPES = DATA_STORES.keys.index_with do |store|
+ "Ci::BuildTraceChunks::#{store.to_s.camelize}".constantize
end.freeze
LIVE_STORES = %i[redis redis_trace_chunks].freeze
diff --git a/app/models/ci/build_trace_metadata.rb b/app/models/ci/build_trace_metadata.rb
index 86de90983ff..00cf1531483 100644
--- a/app/models/ci/build_trace_metadata.rb
+++ b/app/models/ci/build_trace_metadata.rb
@@ -2,6 +2,8 @@
module Ci
class BuildTraceMetadata < Ci::ApplicationRecord
+ include Ci::Partitionable
+
MAX_ATTEMPTS = 5
self.table_name = 'ci_build_trace_metadata'
self.primary_key = :build_id
@@ -9,15 +11,17 @@ module Ci
belongs_to :build, class_name: 'Ci::Build'
belongs_to :trace_artifact, class_name: 'Ci::JobArtifact'
+ partitionable scope: :build
+
validates :build, presence: true
validates :archival_attempts, presence: true
- def self.find_or_upsert_for!(build_id)
- record = find_by(build_id: build_id)
+ def self.find_or_upsert_for!(build_id, partition_id)
+ record = find_by(build_id: build_id, partition_id: partition_id)
return record if record
- upsert({ build_id: build_id }, unique_by: :build_id)
- find_by!(build_id: build_id)
+ upsert({ build_id: build_id, partition_id: partition_id }, unique_by: :build_id)
+ find_by!(build_id: build_id, partition_id: partition_id)
end
# The job is retried around 5 times during the 7 days retention period for
diff --git a/app/models/ci/freeze_period.rb b/app/models/ci/freeze_period.rb
index da0bbbacddd..1bf32e04a15 100644
--- a/app/models/ci/freeze_period.rb
+++ b/app/models/ci/freeze_period.rb
@@ -4,6 +4,10 @@ module Ci
class FreezePeriod < Ci::ApplicationRecord
include StripAttribute
include Ci::NamespacedModelName
+ include Gitlab::Utils::StrongMemoize
+
+ STATUS_ACTIVE = :active
+ STATUS_INACTIVE = :inactive
default_scope { order(created_at: :asc) } # rubocop:disable Cop/DefaultScope
@@ -14,5 +18,60 @@ module Ci
validates :freeze_start, cron: true, presence: true
validates :freeze_end, cron: true, presence: true
validates :cron_timezone, cron_freeze_period_timezone: true, presence: true
+
+ def active?
+ status == STATUS_ACTIVE
+ end
+
+ def status
+ Gitlab::SafeRequestStore.fetch("ci:freeze_period:#{id}:status") do
+ within_freeze_period? ? STATUS_ACTIVE : STATUS_INACTIVE
+ end
+ end
+
+ def time_start
+ Gitlab::SafeRequestStore.fetch("ci:freeze_period:#{id}:time_start") do
+ freeze_start_parsed_cron.previous_time_from(time_zone_now)
+ end
+ end
+
+ def next_time_start
+ Gitlab::SafeRequestStore.fetch("ci:freeze_period:#{id}:next_time_start") do
+ freeze_start_parsed_cron.next_time_from(time_zone_now)
+ end
+ end
+
+ def time_end_from_now
+ Gitlab::SafeRequestStore.fetch("ci:freeze_period:#{id}:time_end_from_now") do
+ freeze_end_parsed_cron.next_time_from(time_zone_now)
+ end
+ end
+
+ def time_end_from_start
+ Gitlab::SafeRequestStore.fetch("ci:freeze_period:#{id}:time_end_from_start") do
+ freeze_end_parsed_cron.next_time_from(time_start)
+ end
+ end
+
+ private
+
+ def within_freeze_period?
+ time_start <= time_zone_now && time_zone_now <= time_end_from_start
+ end
+
+ def freeze_start_parsed_cron
+ Gitlab::Ci::CronParser.new(freeze_start, cron_timezone)
+ end
+ strong_memoize_attr :freeze_start_parsed_cron
+
+ def freeze_end_parsed_cron
+ Gitlab::Ci::CronParser.new(freeze_end, cron_timezone)
+ end
+ strong_memoize_attr :freeze_end_parsed_cron
+
+ def time_zone_now
+ Time.zone.now
+ end
+ strong_memoize_attr :time_zone_now
end
end
diff --git a/app/models/ci/freeze_period_status.rb b/app/models/ci/freeze_period_status.rb
deleted file mode 100644
index e810bb3f229..00000000000
--- a/app/models/ci/freeze_period_status.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-# frozen_string_literal: true
-
-module Ci
- class FreezePeriodStatus
- attr_reader :project
-
- def initialize(project:)
- @project = project
- end
-
- def execute
- project.freeze_periods.any? { |period| within_freeze_period?(period) }
- end
-
- def within_freeze_period?(period)
- start_freeze_cron = Gitlab::Ci::CronParser.new(period.freeze_start, period.cron_timezone)
- end_freeze_cron = Gitlab::Ci::CronParser.new(period.freeze_end, period.cron_timezone)
-
- start_freeze = start_freeze_cron.previous_time_from(time_zone_now)
- end_freeze = end_freeze_cron.next_time_from(start_freeze)
-
- start_freeze <= time_zone_now && time_zone_now <= end_freeze
- end
-
- private
-
- def time_zone_now
- @time_zone_now ||= Time.zone.now
- end
- end
-end
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index 922806a21c3..53c358f4eba 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -5,7 +5,6 @@ module Ci
include Ci::Partitionable
include IgnorableColumns
include AfterCommitQueue
- include ObjectStorage::BackgroundMove
include UpdateProjectStatistics
include UsageStatistics
include Sortable
@@ -52,7 +51,8 @@ module Ci
cobertura: 'cobertura-coverage.xml',
terraform: 'tfplan.json',
cluster_applications: 'gl-cluster-applications.json', # DEPRECATED: https://gitlab.com/gitlab-org/gitlab/-/issues/361094
- requirements: 'requirements.json',
+ requirements: 'requirements.json', # Will be DEPRECATED soon: https://gitlab.com/groups/gitlab-org/-/epics/9203
+ requirements_v2: 'requirements_v2.json',
coverage_fuzzing: 'gl-coverage-fuzzing.json',
api_fuzzing: 'gl-api-fuzzing-report.json',
cyclonedx: 'gl-sbom.cdx.json'
@@ -95,6 +95,7 @@ module Ci
load_performance: :raw,
terraform: :raw,
requirements: :raw,
+ requirements_v2: :raw,
coverage_fuzzing: :raw,
api_fuzzing: :raw
}.freeze
@@ -119,6 +120,7 @@ module Ci
sast
secret_detection
requirements
+ requirements_v2
cluster_image_scanning
cyclonedx
].freeze
@@ -209,7 +211,8 @@ module Ci
load_performance: 25, ## EE-specific
api_fuzzing: 26, ## EE-specific
cluster_image_scanning: 27, ## EE-specific
- cyclonedx: 28 ## EE-specific
+ cyclonedx: 28, ## EE-specific
+ requirements_v2: 29 ## EE-specific
}
# `file_location` indicates where actual files are stored.
diff --git a/app/models/ci/job_token/allowlist.rb b/app/models/ci/job_token/allowlist.rb
new file mode 100644
index 00000000000..9e9a0a68ebd
--- /dev/null
+++ b/app/models/ci/job_token/allowlist.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+module Ci
+ module JobToken
+ class Allowlist
+ def initialize(source_project, direction:)
+ @source_project = source_project
+ @direction = direction
+ end
+
+ def includes?(target_project)
+ source_links
+ .with_target(target_project)
+ .exists?
+ end
+
+ def projects
+ Project.from_union(target_projects, remove_duplicates: false)
+ end
+
+ private
+
+ def source_links
+ Ci::JobToken::ProjectScopeLink
+ .with_source(@source_project)
+ .where(direction: @direction)
+ end
+
+ def target_project_ids
+ source_links
+ # pluck needed to avoid ci and main db join
+ .pluck(:target_project_id)
+ end
+
+ def target_projects
+ [
+ Project.id_in(@source_project),
+ Project.id_in(target_project_ids)
+ ]
+ end
+ end
+ end
+end
diff --git a/app/models/ci/job_token/project_scope_link.rb b/app/models/ci/job_token/project_scope_link.rb
index 3fdf07123e6..b784f93651a 100644
--- a/app/models/ci/job_token/project_scope_link.rb
+++ b/app/models/ci/job_token/project_scope_link.rb
@@ -12,8 +12,8 @@ module Ci
belongs_to :target_project, class_name: 'Project'
belongs_to :added_by, class_name: 'User'
- scope :from_project, ->(project) { where(source_project: project) }
- scope :to_project, ->(project) { where(target_project: project) }
+ scope :with_source, ->(project) { where(source_project: project) }
+ scope :with_target, ->(project) { where(target_project: project) }
validates :source_project, presence: true
validates :target_project, presence: true
diff --git a/app/models/ci/job_token/scope.rb b/app/models/ci/job_token/scope.rb
index 1aa49b95201..e320c0f92d1 100644
--- a/app/models/ci/job_token/scope.rb
+++ b/app/models/ci/job_token/scope.rb
@@ -1,49 +1,58 @@
# frozen_string_literal: true
-# This model represents the surface where a CI_JOB_TOKEN can be used.
-# A Scope is initialized with the project that the job token belongs to,
-# and indicates what are all the other projects that the token could access.
+# This model represents the scope of access for a CI_JOB_TOKEN.
#
-# By default a job token can only access its own project, which is the same
-# project that defines the scope.
-# By adding ScopeLinks to the scope we can allow other projects to be accessed
-# by the job token. This works as an allowlist of projects for a job token.
+# A scope is initialized with a project.
+#
+# Projects can be added to the scope by adding ScopeLinks to
+# create an allowlist of projects in either access direction (inbound, outbound).
+#
+# Currently, projects in the outbound allowlist can be accessed via the token
+# in the source project.
+#
+# TODO(Issue #346298) Projects in the inbound allowlist can use their token to access
+# the source project.
+#
+# CI_JOB_TOKEN should be considered untrusted without these features enabled.
#
-# If a project is not included in the scope we should not allow the job user
-# to access it since operations using CI_JOB_TOKEN should be considered untrusted.
module Ci
module JobToken
class Scope
- attr_reader :source_project
+ attr_reader :current_project
- def initialize(project)
- @source_project = project
+ def initialize(current_project)
+ @current_project = current_project
end
- def includes?(target_project)
- # if the setting is disabled any project is considered to be in scope.
- return true unless source_project.ci_outbound_job_token_scope_enabled?
+ def allows?(accessed_project)
+ self_referential?(accessed_project) || outbound_allows?(accessed_project)
+ end
- target_project.id == source_project.id ||
- Ci::JobToken::ProjectScopeLink.from_project(source_project).to_project(target_project).exists?
+ def outbound_projects
+ outbound_allowlist.projects
end
+ # Deprecated: use outbound_projects, TODO(Issue #346298) remove references to all_project
def all_projects
- Project.from_union(target_projects, remove_duplicates: false)
+ outbound_projects
end
private
- def target_project_ids
- Ci::JobToken::ProjectScopeLink.from_project(source_project).pluck(:target_project_id)
+ def outbound_allows?(accessed_project)
+ # if the setting is disabled any project is considered to be in scope.
+ return true unless @current_project.ci_outbound_job_token_scope_enabled?
+
+ outbound_allowlist.includes?(accessed_project)
+ end
+
+ def outbound_allowlist
+ Ci::JobToken::Allowlist.new(@current_project, direction: :outbound)
end
- def target_projects
- [
- Project.id_in(source_project),
- Project.id_in(target_project_ids)
- ]
+ def self_referential?(accessed_project)
+ @current_project.id == accessed_project.id
end
end
end
diff --git a/app/models/ci/job_variable.rb b/app/models/ci/job_variable.rb
index 332a78b66ae..998f0647ad5 100644
--- a/app/models/ci/job_variable.rb
+++ b/app/models/ci/job_variable.rb
@@ -2,12 +2,15 @@
module Ci
class JobVariable < Ci::ApplicationRecord
+ include Ci::Partitionable
include Ci::NewHasVariable
include Ci::RawVariable
include BulkInsertSafe
belongs_to :job, class_name: "Ci::Build", foreign_key: :job_id
+ partitionable scope: :job
+
alias_attribute :secret_value, :value
validates :key, uniqueness: { scope: :job_id }, unless: :dotenv_source?
diff --git a/app/models/ci/pending_build.rb b/app/models/ci/pending_build.rb
index 0fa6a234a3d..2b1eb67d4f2 100644
--- a/app/models/ci/pending_build.rb
+++ b/app/models/ci/pending_build.rb
@@ -3,11 +3,14 @@
module Ci
class PendingBuild < Ci::ApplicationRecord
include EachBatch
+ include Ci::Partitionable
belongs_to :project
belongs_to :build, class_name: 'Ci::Build'
belongs_to :namespace, inverse_of: :pending_builds, class_name: 'Namespace'
+ partitionable scope: :build
+
validates :namespace, presence: true
scope :ref_protected, -> { where(protected: true) }
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 020f5cf9d8e..05207fb1ca0 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -350,9 +350,13 @@ module Ci
scope :for_sha_or_source_sha, -> (sha) { for_sha(sha).or(for_source_sha(sha)) }
scope :for_ref, -> (ref) { where(ref: ref) }
scope :for_branch, -> (branch) { for_ref(branch).where(tag: false) }
- scope :for_id, -> (id) { where(id: id) }
scope :for_iid, -> (iid) { where(iid: iid) }
scope :for_project, -> (project_id) { where(project_id: project_id) }
+ scope :for_name, -> (name) do
+ name_column = Ci::PipelineMetadata.arel_table[:name]
+
+ joins(:pipeline_metadata).where(name_column.lower.eq(name.downcase))
+ end
scope :created_after, -> (time) { where(arel_table[:created_at].gt(time)) }
scope :created_before_id, -> (id) { where(arel_table[:id].lt(id)) }
scope :before_pipeline, -> (pipeline) { created_before_id(pipeline.id).outside_pipeline_family(pipeline) }
@@ -721,7 +725,7 @@ module Ci
def freeze_period?
strong_memoize(:freeze_period) do
- Ci::FreezePeriodStatus.new(project: project).execute
+ project.freeze_periods.any?(&:active?)
end
end
@@ -1341,13 +1345,14 @@ module Ci
persistent_ref.create
end
+ # For dependent bridge jobs we reset the upstream bridge recursively
+ # to reflect that a downstream pipeline is running again
def reset_source_bridge!(current_user)
# break recursion when no source_pipeline bridge (first upstream pipeline)
return unless bridge_waiting?
return unless current_user.can?(:update_pipeline, source_bridge.pipeline)
- source_bridge.pending!
- Ci::AfterRequeueJobService.new(project, current_user).execute(source_bridge) # rubocop:disable CodeReuse/ServiceClass
+ Ci::EnqueueJobService.new(source_bridge, current_user: current_user).execute(&:pending!) # rubocop:disable CodeReuse/ServiceClass
end
# EE-only
diff --git a/app/models/ci/pipeline_schedule.rb b/app/models/ci/pipeline_schedule.rb
index 96e5567e85e..20ff07e88ba 100644
--- a/app/models/ci/pipeline_schedule.rb
+++ b/app/models/ci/pipeline_schedule.rb
@@ -16,7 +16,7 @@ module Ci
belongs_to :owner, class_name: 'User'
has_one :last_pipeline, -> { order(id: :desc) }, class_name: 'Ci::Pipeline'
has_many :pipelines
- has_many :variables, class_name: 'Ci::PipelineScheduleVariable', validate: false
+ has_many :variables, class_name: 'Ci::PipelineScheduleVariable'
validates :cron, unless: :importing?, cron: true, presence: { unless: :importing? }
validates :cron_timezone, cron_timezone: true, presence: { unless: :importing? }
@@ -78,8 +78,6 @@ module Ci
ref.start_with? 'refs/tags/'
end
- private
-
def worker_cron_expression
Settings.cron_jobs['pipeline_schedule_worker']['cron']
end
diff --git a/app/models/ci/pipeline_schedule_variable.rb b/app/models/ci/pipeline_schedule_variable.rb
index 718ed14edeb..00251ea06fd 100644
--- a/app/models/ci/pipeline_schedule_variable.rb
+++ b/app/models/ci/pipeline_schedule_variable.rb
@@ -9,6 +9,6 @@ module Ci
alias_attribute :secret_value, :value
- validates :key, uniqueness: { scope: :pipeline_schedule_id }
+ validates :key, presence: true, uniqueness: { scope: :pipeline_schedule_id }
end
end
diff --git a/app/models/ci/processable.rb b/app/models/ci/processable.rb
index eb805ffae0a..37c82c125aa 100644
--- a/app/models/ci/processable.rb
+++ b/app/models/ci/processable.rb
@@ -104,8 +104,8 @@ module Ci
to: :pipeline
def clone(current_user:, new_job_variables_attributes: [])
- new_attributes = self.class.clone_accessors.to_h do |attribute|
- [attribute, public_send(attribute)] # rubocop:disable GitlabSecurity/PublicSend
+ new_attributes = self.class.clone_accessors.index_with do |attribute|
+ public_send(attribute) # rubocop:disable GitlabSecurity/PublicSend
end
if persisted_environment.present?
diff --git a/app/models/ci/resource_group.rb b/app/models/ci/resource_group.rb
index 6d25f747a9d..b788e4f58c1 100644
--- a/app/models/ci/resource_group.rb
+++ b/app/models/ci/resource_group.rb
@@ -24,11 +24,18 @@ module Ci
# NOTE: This is concurrency-safe method that the subquery in the `UPDATE`
# works as explicit locking.
def assign_resource_to(processable)
- resources.free.limit(1).update_all(build_id: processable.id) > 0
+ attrs = {
+ build_id: processable.id,
+ partition_id: processable.partition_id
+ }
+
+ resources.free.limit(1).update_all(attrs) > 0
end
def release_resource_from(processable)
- resources.retained_by(processable).update_all(build_id: nil) > 0
+ attrs = { build_id: nil, partition_id: nil }
+
+ resources.retained_by(processable).update_all(attrs) > 0
end
def upcoming_processables
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 3be627989b1..a7f3ff938c3 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -89,6 +89,9 @@ module Ci
scope :ordered, -> { order(id: :desc) }
scope :with_recent_runner_queue, -> { where('contacted_at > ?', recent_queue_deadline) }
+ scope :with_running_builds, -> do
+ where('EXISTS(?)', ::Ci::Build.running.select(1).where('ci_builds.runner_id = ci_runners.id'))
+ end
# BACKWARD COMPATIBILITY: There are needed to maintain compatibility with `AVAILABLE_SCOPES` used by `lib/api/runners.rb`
scope :deprecated_shared, -> { instance_type }
diff --git a/app/models/ci/runner_namespace.rb b/app/models/ci/runner_namespace.rb
index 82390ccc538..502ceae3675 100644
--- a/app/models/ci/runner_namespace.rb
+++ b/app/models/ci/runner_namespace.rb
@@ -15,6 +15,8 @@ module Ci
validates :runner_id, uniqueness: { scope: :namespace_id }
validate :group_runner_type
+ scope :for_runner, ->(runner_id) { where(runner_id: runner_id) }
+
def recent_runners
::Ci::Runner.belonging_to_group(namespace_id).recent
end
diff --git a/app/models/ci/running_build.rb b/app/models/ci/running_build.rb
index ae38d54862d..43214b0c336 100644
--- a/app/models/ci/running_build.rb
+++ b/app/models/ci/running_build.rb
@@ -1,7 +1,18 @@
# frozen_string_literal: true
module Ci
+ # This model represents metadata for a running build.
+ # Despite the generic RunningBuild name, in this first iteration it applies only to shared runners
+ # (see Ci::RunningBuild.upsert_shared_runner_build!).
+ # The decision to insert all of the running builds here was deferred to avoid the pressure on the database as
+ # at this time that was not necessary.
+ # We can reconsider the decision to limit this only to shared runners when there is more evidence that inserting all
+ # of the running builds there is worth the additional pressure.
class RunningBuild < Ci::ApplicationRecord
+ include Ci::Partitionable
+
+ partitionable scope: :build
+
belongs_to :project
belongs_to :build, class_name: 'Ci::Build'
belongs_to :runner, class_name: 'Ci::Runner'
diff --git a/app/models/ci/secure_file.rb b/app/models/ci/secure_file.rb
index df38398e5a9..1e6c48bbef5 100644
--- a/app/models/ci/secure_file.rb
+++ b/app/models/ci/secure_file.rb
@@ -17,20 +17,19 @@ module Ci
validates :file, presence: true, file_size: { maximum: FILE_SIZE_LIMIT }
validates :checksum, :file_store, :name, :project_id, presence: true
validates :name, uniqueness: { scope: :project }
+
+ attribute :metadata, :ind_jsonb
validates :metadata, json_schema: { filename: "ci_secure_file_metadata" }, allow_nil: true
+ attribute :file_store, default: -> { Ci::SecureFileUploader.default_store }
+ mount_file_store_uploader Ci::SecureFileUploader
+
after_initialize :generate_key_data
before_validation :assign_checksum
scope :order_by_created_at, -> { order(created_at: :desc) }
scope :project_id_in, ->(ids) { where(project_id: ids) }
- serialize :metadata, Serializers::Json # rubocop:disable Cop/ActiveRecordSerialize
-
- attribute :file_store, default: -> { Ci::SecureFileUploader.default_store }
-
- mount_file_store_uploader Ci::SecureFileUploader
-
def checksum_algorithm
CHECKSUM_ALGORITHM
end
diff --git a/app/models/ci/sources/pipeline.rb b/app/models/ci/sources/pipeline.rb
index 2df504cd3de..855e68d1db1 100644
--- a/app/models/ci/sources/pipeline.rb
+++ b/app/models/ci/sources/pipeline.rb
@@ -3,6 +3,7 @@
module Ci
module Sources
class Pipeline < Ci::ApplicationRecord
+ include Ci::Partitionable
include Ci::NamespacedModelName
self.table_name = "ci_sources_pipelines"
@@ -15,6 +16,11 @@ module Ci
belongs_to :source_bridge, class_name: "Ci::Bridge", foreign_key: :source_job_id
belongs_to :source_pipeline, class_name: "Ci::Pipeline", foreign_key: :source_pipeline_id
+ partitionable scope: :pipeline
+
+ before_validation :set_source_partition_id, on: :create
+ validates :source_partition_id, presence: true
+
validates :project, presence: true
validates :pipeline, presence: true
@@ -23,6 +29,15 @@ module Ci
validates :source_pipeline, presence: true
scope :same_project, -> { where(arel_table[:source_project_id].eq(arel_table[:project_id])) }
+
+ private
+
+ def set_source_partition_id
+ return if source_partition_id_changed? && source_partition_id.present?
+ return unless source_job
+
+ self.source_partition_id = source_job.partition_id
+ end
end
end
end
diff --git a/app/models/ci/unit_test_failure.rb b/app/models/ci/unit_test_failure.rb
index a5aa3b70e37..cfef1249164 100644
--- a/app/models/ci/unit_test_failure.rb
+++ b/app/models/ci/unit_test_failure.rb
@@ -2,6 +2,8 @@
module Ci
class UnitTestFailure < Ci::ApplicationRecord
+ include Ci::Partitionable
+
REPORT_WINDOW = 14.days
validates :unit_test, :build, :failed_at, presence: true
@@ -9,6 +11,8 @@ module Ci
belongs_to :unit_test, class_name: "Ci::UnitTest", foreign_key: :unit_test_id
belongs_to :build, class_name: "Ci::Build", foreign_key: :build_id
+ partitionable scope: :build
+
scope :deletable, -> { where('failed_at < ?', REPORT_WINDOW.ago) }
def self.recent_failures_count(project:, unit_test_keys:, date_range: REPORT_WINDOW.ago..Time.current)
diff --git a/app/models/clusters/agent_token.rb b/app/models/clusters/agent_token.rb
index 1607d0b6d19..e2dcff13a69 100644
--- a/app/models/clusters/agent_token.rb
+++ b/app/models/clusters/agent_token.rb
@@ -25,5 +25,9 @@ module Clusters
active: 0,
revoked: 1
}
+
+ def to_ability_name
+ :cluster
+ end
end
end
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 54de45ebba7..5175842e5de 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -359,6 +359,10 @@ class Commit
end
def has_signature?
+ if signature_type == :SSH && !ssh_signatures_enabled?
+ return false
+ end
+
signature_type && signature_type != :NONE
end
@@ -378,6 +382,10 @@ class Commit
@signature_type ||= raw_signature_type || :NONE
end
+ def ssh_signatures_enabled?
+ Feature.enabled?(:ssh_commit_signatures, project)
+ end
+
def signature
strong_memoize(:signature) do
case signature_type
@@ -385,6 +393,8 @@ class Commit
gpg_commit.signature
when :X509
Gitlab::X509::Commit.new(self).signature
+ when :SSH
+ Gitlab::Ssh::Commit.new(self).signature if ssh_signatures_enabled?
else
nil
end
diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb
index e2f0de52bc9..87029cb2033 100644
--- a/app/models/commit_range.rb
+++ b/app/models/commit_range.rb
@@ -148,7 +148,7 @@ class CommitRange
def sha_start
return unless sha_from
- exclude_start? ? sha_from + '^' : sha_from
+ exclude_start? ? "#{sha_from}^" : sha_from
end
def commit_start
diff --git a/app/models/commit_signatures/gpg_signature.rb b/app/models/commit_signatures/gpg_signature.rb
index 2ae59853520..a9e8ca2dd33 100644
--- a/app/models/commit_signatures/gpg_signature.rb
+++ b/app/models/commit_signatures/gpg_signature.rb
@@ -2,6 +2,7 @@
module CommitSignatures
class GpgSignature < ApplicationRecord
include CommitSignature
+ include SignatureType
sha_attribute :gpg_key_primary_keyid
@@ -10,6 +11,14 @@ module CommitSignatures
validates :gpg_key_primary_keyid, presence: true
+ def signed_by_user
+ gpg_key&.user
+ end
+
+ def type
+ :gpg
+ end
+
def self.with_key_and_subkeys(gpg_key)
subkey_ids = gpg_key.subkeys.pluck(:id)
diff --git a/app/models/commit_signatures/ssh_signature.rb b/app/models/commit_signatures/ssh_signature.rb
index 7a8d0653fcd..1e64e2b2978 100644
--- a/app/models/commit_signatures/ssh_signature.rb
+++ b/app/models/commit_signatures/ssh_signature.rb
@@ -3,7 +3,16 @@
module CommitSignatures
class SshSignature < ApplicationRecord
include CommitSignature
+ include SignatureType
belongs_to :key, optional: true
+
+ def type
+ :ssh
+ end
+
+ def signed_by_user
+ key&.user
+ end
end
end
diff --git a/app/models/commit_signatures/x509_commit_signature.rb b/app/models/commit_signatures/x509_commit_signature.rb
index 2cbb331dd7e..4edbc147502 100644
--- a/app/models/commit_signatures/x509_commit_signature.rb
+++ b/app/models/commit_signatures/x509_commit_signature.rb
@@ -2,15 +2,24 @@
module CommitSignatures
class X509CommitSignature < ApplicationRecord
include CommitSignature
+ include SignatureType
belongs_to :x509_certificate, class_name: 'X509Certificate', foreign_key: 'x509_certificate_id', optional: false
validates :x509_certificate_id, presence: true
+ def type
+ :x509
+ end
+
def x509_commit
return unless commit
Gitlab::X509::Commit.new(commit)
end
+
+ def signed_by_user
+ commit&.committer
+ end
end
end
diff --git a/app/models/concerns/avatarable.rb b/app/models/concerns/avatarable.rb
index b32502c3ee2..f419fa8518e 100644
--- a/app/models/concerns/avatarable.rb
+++ b/app/models/concerns/avatarable.rb
@@ -16,7 +16,6 @@ module Avatarable
included do
prepend ShadowMethods
- include ObjectStorage::BackgroundMove
include Gitlab::Utils::StrongMemoize
include ApplicationHelper
diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb
index ec0cf36d875..6a855198697 100644
--- a/app/models/concerns/cache_markdown_field.rb
+++ b/app/models/concerns/cache_markdown_field.rb
@@ -40,7 +40,7 @@ module CacheMarkdownField
# Banzai is less strict about authors, so don't always have an author key
context[:author] = self.author if self.respond_to?(:author)
- context[:markdown_engine] = :common_mark
+ context[:markdown_engine] = Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE
if Feature.enabled?(:personal_snippet_reference_filters, context[:author])
context[:user] = self.parent_user
diff --git a/app/models/concerns/cached_commit.rb b/app/models/concerns/cached_commit.rb
index 183d5728743..0fb72552dd5 100644
--- a/app/models/concerns/cached_commit.rb
+++ b/app/models/concerns/cached_commit.rb
@@ -4,8 +4,8 @@ module CachedCommit
extend ActiveSupport::Concern
def to_hash
- Gitlab::Git::Commit::SERIALIZE_KEYS.each_with_object({}) do |key, hash|
- hash[key] = public_send(key) # rubocop:disable GitlabSecurity/PublicSend
+ Gitlab::Git::Commit::SERIALIZE_KEYS.index_with do |key|
+ public_send(key) # rubocop:disable GitlabSecurity/PublicSend
end
end
diff --git a/app/models/concerns/ci/partitionable.rb b/app/models/concerns/ci/partitionable.rb
index 68a6714c892..d6ba0f4488f 100644
--- a/app/models/concerns/ci/partitionable.rb
+++ b/app/models/concerns/ci/partitionable.rb
@@ -25,10 +25,21 @@ module Ci
PARTITIONABLE_MODELS = %w[
CommitStatus
Ci::BuildMetadata
- Ci::Stage
+ Ci::BuildNeed
+ Ci::BuildReportResult
+ Ci::BuildRunnerSession
+ Ci::BuildTraceChunk
+ Ci::BuildTraceMetadata
+ Ci::BuildPendingState
Ci::JobArtifact
- Ci::PipelineVariable
+ Ci::JobVariable
Ci::Pipeline
+ Ci::PendingBuild
+ Ci::RunningBuild
+ Ci::PipelineVariable
+ Ci::Sources::Pipeline
+ Ci::Stage
+ Ci::UnitTestFailure
].freeze
def self.check_inclusion(klass)
@@ -57,14 +68,31 @@ module Ci
end
class_methods do
- def partitionable(scope:, through: nil)
- if through
- define_singleton_method(:routing_table_name) { through[:table] }
- define_singleton_method(:routing_table_name_flag) { through[:flag] }
+ def partitionable(scope:, through: nil, partitioned: false)
+ handle_partitionable_through(through)
+ handle_partitionable_dml(partitioned)
+ handle_partitionable_scope(scope)
+ end
- include Partitionable::Switch
- end
+ private
+
+ def handle_partitionable_through(options)
+ return unless options
+
+ define_singleton_method(:routing_table_name) { options[:table] }
+ define_singleton_method(:routing_table_name_flag) { options[:flag] }
+
+ include Partitionable::Switch
+ end
+
+ def handle_partitionable_dml(partitioned)
+ define_singleton_method(:partitioned?) { partitioned }
+ return unless partitioned
+
+ include Partitionable::PartitionedFilter
+ end
+ def handle_partitionable_scope(scope)
define_method(:partition_scope_value) do
strong_memoize(:partition_scope_value) do
next Ci::Pipeline.current_partition_value if respond_to?(:importing?) && importing?
diff --git a/app/models/concerns/ci/partitionable/partitioned_filter.rb b/app/models/concerns/ci/partitionable/partitioned_filter.rb
new file mode 100644
index 00000000000..4adae3be26a
--- /dev/null
+++ b/app/models/concerns/ci/partitionable/partitioned_filter.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Ci
+ module Partitionable
+ # Used to patch the save, update, delete, destroy methods to use the
+ # partition_id attributes for their SQL queries.
+ module PartitionedFilter
+ extend ActiveSupport::Concern
+
+ if Rails::VERSION::MAJOR >= 7
+ # These methods are updated in Rails 7 to use `_primary_key_constraints_hash`
+ # by default, so this patch will no longer be required.
+ #
+ # rubocop:disable Gitlab/NoCodeCoverageComment
+ # :nocov:
+ raise "`#{__FILE__}` should be double checked" if Rails.env.test?
+
+ warn "Update `#{__FILE__}`. Patches Rails internals for partitioning"
+ # :nocov:
+ # rubocop:enable Gitlab/NoCodeCoverageComment
+ else
+ def _update_row(attribute_names, attempted_action = "update")
+ self.class._update_record(
+ attributes_with_values(attribute_names),
+ _primary_key_constraints_hash
+ )
+ end
+
+ def _delete_row
+ self.class._delete_record(_primary_key_constraints_hash)
+ end
+ end
+
+ # Introduced in Rails 7, but updated to include `partition_id` filter.
+ # https://github.com/rails/rails/blob/a4dbb153fd390ac31bb9808809e7ac4d3a2c5116/activerecord/lib/active_record/persistence.rb#L1031-L1033
+ def _primary_key_constraints_hash
+ { @primary_key => id_in_database, partition_id: partition_id } # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/commit_signature.rb b/app/models/concerns/commit_signature.rb
index 5bdfa9a2966..7f1fbbefd94 100644
--- a/app/models/concerns/commit_signature.rb
+++ b/app/models/concerns/commit_signature.rb
@@ -44,7 +44,7 @@ module CommitSignature
project.commit(commit_sha)
end
- def user
- commit.committer
+ def signed_by_user
+ raise NoMethodError, 'must implement `signed_by_user` method'
end
end
diff --git a/app/models/concerns/counter_attribute.rb b/app/models/concerns/counter_attribute.rb
index 03e062a9855..f1efbba67e1 100644
--- a/app/models/concerns/counter_attribute.rb
+++ b/app/models/concerns/counter_attribute.rb
@@ -17,14 +17,29 @@
# counter_attribute :storage_size
# end
#
+# It's possible to define a conditional counter attribute. You need to pass a proc
+# that must accept a single argument, the object instance on which this concern is
+# included.
+#
+# @example:
+#
+# class ProjectStatistics
+# include CounterAttribute
+#
+# counter_attribute :conditional_one, if: -> { |object| object.use_counter_attribute? }
+# end
+#
# To increment the counter we can use the method:
-# delayed_increment_counter(:commit_count, 3)
+# increment_counter(:commit_count, 3)
+#
+# This method would determine whether it would increment the counter using Redis,
+# or fallback to legacy increment on ActiveRecord counters.
#
# It is possible to register callbacks to be executed after increments have
# been flushed to the database. Callbacks are not executed if there are no increments
# to flush.
#
-# counter_attribute_after_flush do |statistic|
+# counter_attribute_after_commit do |statistic|
# Namespaces::ScheduleAggregationWorker.perform_async(statistic.namespace_id)
# end
#
@@ -32,99 +47,51 @@ module CounterAttribute
extend ActiveSupport::Concern
extend AfterCommitQueue
include Gitlab::ExclusiveLeaseHelpers
-
- LUA_STEAL_INCREMENT_SCRIPT = <<~EOS
- local increment_key, flushed_key = KEYS[1], KEYS[2]
- local increment_value = redis.call("get", increment_key) or 0
- local flushed_value = redis.call("incrby", flushed_key, increment_value)
- if flushed_value == 0 then
- redis.call("del", increment_key, flushed_key)
- else
- redis.call("del", increment_key)
- end
- return flushed_value
- EOS
-
- WORKER_DELAY = 10.minutes
- WORKER_LOCK_TTL = 10.minutes
+ include Gitlab::Utils::StrongMemoize
class_methods do
- def counter_attribute(attribute)
- counter_attributes << attribute
+ def counter_attribute(attribute, if: nil)
+ counter_attributes << {
+ attribute: attribute,
+ if_proc: binding.local_variable_get(:if) # can't read `if` directly
+ }
end
def counter_attributes
- @counter_attributes ||= Set.new
+ @counter_attributes ||= []
end
- def after_flush_callbacks
- @after_flush_callbacks ||= []
+ def after_commit_callbacks
+ @after_commit_callbacks ||= []
end
- # perform registered callbacks after increments have been flushed to the database
- def counter_attribute_after_flush(&callback)
- after_flush_callbacks << callback
- end
-
- def counter_attribute_enabled?(attribute)
- counter_attributes.include?(attribute)
+ # perform registered callbacks after increments have been committed to the database
+ def counter_attribute_after_commit(&callback)
+ after_commit_callbacks << callback
end
end
- # This method must only be called by FlushCounterIncrementsWorker
- # because it should run asynchronously and with exclusive lease.
- # This will
- # 1. temporarily move the pending increment for a given attribute
- # to a relative "flushed" Redis key, delete the increment key and return
- # the value. If new increments are performed at this point, the increment
- # key is recreated as part of `delayed_increment_counter`.
- # The "flushed" key is used to ensure that we can keep incrementing
- # counters in Redis while flushing existing values.
- # 2. then the value is used to update the counter in the database.
- # 3. finally the "flushed" key is deleted.
- def flush_increments_to_database!(attribute)
- lock_key = counter_lock_key(attribute)
-
- with_exclusive_lease(lock_key) do
- previous_db_value = read_attribute(attribute)
- increment_key = counter_key(attribute)
- flushed_key = counter_flushed_key(attribute)
- increment_value = steal_increments(increment_key, flushed_key)
- new_db_value = nil
-
- next if increment_value == 0
-
- transaction do
- update_counters_with_lease({ attribute => increment_value })
- redis_state { |redis| redis.del(flushed_key) }
- new_db_value = reset.read_attribute(attribute)
- end
+ def counter_attribute_enabled?(attribute)
+ counter_attribute = self.class.counter_attributes.find { |registered| registered[:attribute] == attribute }
+ return false unless counter_attribute
+ return true unless counter_attribute[:if_proc]
- execute_after_flush_callbacks
+ counter_attribute[:if_proc].call(self)
+ end
- log_flush_counter(attribute, increment_value, previous_db_value, new_db_value)
+ def counter(attribute)
+ strong_memoize_with(:counter, attribute) do
+ # This needs #to_sym because attribute could come from a Sidekiq param,
+ # which would be a string.
+ build_counter_for(attribute.to_sym)
end
end
- def delayed_increment_counter(attribute, increment)
- raise ArgumentError, "#{attribute} is not a counter attribute" unless counter_attribute_enabled?(attribute)
-
+ def increment_counter(attribute, increment)
return if increment == 0
run_after_commit_or_now do
- increment_counter(attribute, increment)
-
- FlushCounterIncrementsWorker.perform_in(WORKER_DELAY, self.class.name, self.id, attribute)
- end
-
- true
- end
-
- def increment_counter(attribute, increment)
- if counter_attribute_enabled?(attribute)
- new_value = redis_state do |redis|
- redis.incrby(counter_key(attribute), increment)
- end
+ new_value = counter(attribute).increment(increment)
log_increment_counter(attribute, increment, new_value)
end
@@ -137,74 +104,33 @@ module CounterAttribute
end
def reset_counter!(attribute)
- if counter_attribute_enabled?(attribute)
- detect_race_on_record(log_fields: { caller: __method__, attributes: attribute }) do
- update!(attribute => 0)
- clear_counter!(attribute)
- end
-
- log_clear_counter(attribute)
+ detect_race_on_record(log_fields: { caller: __method__, attributes: attribute }) do
+ counter(attribute).reset!
end
- end
- def get_counter_value(attribute)
- if counter_attribute_enabled?(attribute)
- redis_state do |redis|
- redis.get(counter_key(attribute)).to_i
- end
- end
+ log_clear_counter(attribute)
end
- def counter_key(attribute)
- "project:{#{project_id}}:counters:#{self.class}:#{id}:#{attribute}"
- end
-
- def counter_flushed_key(attribute)
- counter_key(attribute) + ':flushed'
- end
-
- def counter_lock_key(attribute)
- counter_key(attribute) + ':lock'
- end
-
- def counter_attribute_enabled?(attribute)
- self.class.counter_attribute_enabled?(attribute)
+ def execute_after_commit_callbacks
+ self.class.after_commit_callbacks.each do |callback|
+ callback.call(self.reset)
+ end
end
private
- def database_lock_key
- "project:{#{project_id}}:#{self.class}:#{id}"
- end
-
- def steal_increments(increment_key, flushed_key)
- redis_state do |redis|
- redis.eval(LUA_STEAL_INCREMENT_SCRIPT, keys: [increment_key, flushed_key])
- end
- end
+ def build_counter_for(attribute)
+ raise ArgumentError, %(attribute "#{attribute}" does not exist) unless has_attribute?(attribute)
- def clear_counter!(attribute)
- redis_state do |redis|
- redis.del(counter_key(attribute))
- end
- end
-
- def execute_after_flush_callbacks
- self.class.after_flush_callbacks.each do |callback|
- callback.call(self)
+ if counter_attribute_enabled?(attribute)
+ Gitlab::Counters::BufferedCounter.new(self, attribute)
+ else
+ Gitlab::Counters::LegacyCounter.new(self, attribute)
end
end
- def redis_state(&block)
- Gitlab::Redis::SharedState.with(&block)
- end
-
- def with_exclusive_lease(lock_key)
- in_lock(lock_key, ttl: WORKER_LOCK_TTL) do
- yield
- end
- rescue Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError
- # a worker is already updating the counters
+ def database_lock_key
+ "project:{#{project_id}}:#{self.class}:#{id}"
end
# detect_race_on_record uses a lease to monitor access
@@ -258,19 +184,6 @@ module CounterAttribute
Gitlab::AppLogger.info(payload)
end
- def log_flush_counter(attribute, increment, previous_db_value, new_db_value)
- payload = Gitlab::ApplicationContext.current.merge(
- message: 'Flush counter attribute to database',
- attribute: attribute,
- project_id: project_id,
- increment: increment,
- previous_db_value: previous_db_value,
- new_db_value: new_db_value
- )
-
- Gitlab::AppLogger.info(payload)
- end
-
def log_clear_counter(attribute)
payload = Gitlab::ApplicationContext.current.merge(
message: 'Clear counter attribute',
diff --git a/app/models/concerns/has_user_type.rb b/app/models/concerns/has_user_type.rb
index ad070090dd5..1af655277b8 100644
--- a/app/models/concerns/has_user_type.rb
+++ b/app/models/concerns/has_user_type.rb
@@ -13,10 +13,11 @@ module HasUserType
project_bot: 6,
migration_bot: 7,
security_bot: 8,
- automation_bot: 9
+ automation_bot: 9,
+ admin_bot: 11
}.with_indifferent_access.freeze
- BOT_USER_TYPES = %w[alert_bot project_bot support_bot visual_review_bot migration_bot security_bot automation_bot].freeze
+ BOT_USER_TYPES = %w[alert_bot project_bot support_bot visual_review_bot migration_bot security_bot automation_bot admin_bot].freeze
NON_INTERNAL_USER_TYPES = %w[human project_bot service_user].freeze
INTERNAL_USER_TYPES = (USER_TYPES.keys - NON_INTERNAL_USER_TYPES).freeze
@@ -24,7 +25,6 @@ module HasUserType
scope :humans, -> { where(user_type: :human) }
scope :bots, -> { where(user_type: BOT_USER_TYPES) }
scope :without_bots, -> { humans.or(where.not(user_type: BOT_USER_TYPES)) }
- scope :bots_without_project_bot, -> { where(user_type: BOT_USER_TYPES - ['project_bot']) }
scope :non_internal, -> { humans.or(where(user_type: NON_INTERNAL_USER_TYPES)) }
scope :without_ghosts, -> { humans.or(where.not(user_type: :ghost)) }
scope :without_project_bot, -> { humans.or(where.not(user_type: :project_bot)) }
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 31b2a8d7cc1..9f0cd96a8f8 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -366,7 +366,7 @@ module Issuable
select(issuable_columns)
.select(extra_select_columns)
- .from("#{table_name}")
+ .from(table_name.to_s)
.joins("JOIN LATERAL(#{highest_priority}) as highest_priorities ON TRUE")
.group(group_columns)
.reorder(highest_priority_arel_with_direction.nulls_last)
diff --git a/app/models/concerns/milestoneable.rb b/app/models/concerns/milestoneable.rb
index a95bed7ad42..e95a8a42aa6 100644
--- a/app/models/concerns/milestoneable.rb
+++ b/app/models/concerns/milestoneable.rb
@@ -9,6 +9,12 @@
module Milestoneable
extend ActiveSupport::Concern
+ class_methods do
+ def milestone_releases_subquery
+ Milestone.joins(:releases).where("#{table_name}.milestone_id = milestones.id")
+ end
+ end
+
included do
belongs_to :milestone
@@ -17,9 +23,15 @@ module Milestoneable
scope :any_milestone, -> { where.not(milestone_id: nil) }
scope :with_milestone, ->(title) { left_joins_milestones.where(milestones: { title: title }) }
scope :without_particular_milestones, ->(titles) { left_outer_joins(:milestone).where("milestones.title NOT IN (?) OR milestone_id IS NULL", titles) }
- scope :any_release, -> { joins_milestone_releases }
- scope :with_release, -> (tag, project_id) { joins_milestone_releases.where(milestones: { releases: { tag: tag, project_id: project_id } }) }
- scope :without_particular_release, -> (tag, project_id) { joins_milestone_releases.where.not(milestones: { releases: { tag: tag, project_id: project_id } }) }
+ scope :any_release, -> do
+ where("EXISTS (?)", milestone_releases_subquery)
+ end
+ scope :with_release, -> (tag, project_id) do
+ where("EXISTS (?)", milestone_releases_subquery.where(releases: { tag: tag, project_id: project_id }))
+ end
+ scope :without_particular_release, -> (tag, project_id) do
+ where("EXISTS (?)", milestone_releases_subquery.where.not(releases: { tag: tag, project_id: project_id }))
+ end
scope :left_joins_milestones, -> { joins("LEFT OUTER JOIN milestones ON #{table_name}.milestone_id = milestones.id") }
scope :order_milestone_due_desc, -> { left_joins_milestones.reorder(Arel.sql('milestones.due_date IS NULL, milestones.id IS NULL, milestones.due_date DESC')) }
@@ -30,11 +42,6 @@ module Milestoneable
.where(milestone_releases: { release_id: nil })
end
- scope :joins_milestone_releases, -> do
- joins("JOIN milestone_releases ON #{table_name}.milestone_id = milestone_releases.milestone_id
- JOIN releases ON milestone_releases.release_id = releases.id").distinct
- end
-
private
def milestone_is_valid
diff --git a/app/models/concerns/sensitive_serializable_hash.rb b/app/models/concerns/sensitive_serializable_hash.rb
index 4ad8d16fcb9..794748483e4 100644
--- a/app/models/concerns/sensitive_serializable_hash.rb
+++ b/app/models/concerns/sensitive_serializable_hash.rb
@@ -19,8 +19,6 @@ module SensitiveSerializableHash
# In general, prefer NOT to use serializable_hash / to_json / as_json in favor
# of serializers / entities instead which has an allowlist of attributes
def serializable_hash(options = nil)
- return super if options && options[:unsafe_serialization_hash]
-
options = options.try(:dup) || {}
options[:except] = Array(options[:except]).dup
diff --git a/app/models/concerns/signature_type.rb b/app/models/concerns/signature_type.rb
new file mode 100644
index 00000000000..804f42b6f72
--- /dev/null
+++ b/app/models/concerns/signature_type.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module SignatureType
+ TYPES = %i[gpg ssh x509].freeze
+
+ def type
+ raise NoMethodError, 'must implement `type` method'
+ end
+
+ TYPES.each do |type|
+ define_method("#{type}?") { self.type == type }
+ end
+end
diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb
index eccb004b503..6532a18d1b8 100644
--- a/app/models/concerns/sortable.rb
+++ b/app/models/concerns/sortable.rb
@@ -72,7 +72,7 @@ module Sortable
private
- def highest_label_priority(target_type_column: nil, target_type: nil, target_column:, project_column:, excluded_labels: [])
+ def highest_label_priority(target_column:, project_column:, target_type_column: nil, target_type: nil, excluded_labels: [])
query = Label.select(LabelPriority.arel_table[:priority].minimum.as('label_priority'))
.left_join_priorities
.joins(:label_links)
diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb
index ee5774d4868..05addcf83d2 100644
--- a/app/models/concerns/taskable.rb
+++ b/app/models/concerns/taskable.rb
@@ -63,14 +63,15 @@ module Taskable
def task_status(short: false)
return '' if description.blank?
- prep, completed = if short
- ['/', '']
- else
- [' of ', ' completed']
- end
-
sum = tasks.summary
- "#{sum.complete_count}#{prep}#{sum.item_count} #{'checklist item'.pluralize(sum.item_count)}#{completed}"
+ checklist_item_noun = n_('checklist item', 'checklist items', sum.item_count)
+ if short
+ format(s_('Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}'),
+checklist_item_noun: checklist_item_noun, complete_count: sum.complete_count, total_count: sum.item_count)
+ else
+ format(s_('Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed'),
+checklist_item_noun: checklist_item_noun, complete_count: sum.complete_count, total_count: sum.item_count)
+ end
end
# Return a short string that describes the current state of this Taskable's
diff --git a/app/models/concerns/time_trackable.rb b/app/models/concerns/time_trackable.rb
index 54fe9eac2bc..2b7447dc700 100644
--- a/app/models/concerns/time_trackable.rb
+++ b/app/models/concerns/time_trackable.rb
@@ -15,12 +15,13 @@ module TimeTrackable
alias_method :time_spent?, :time_spent
- default_value_for :time_estimate, value: 0, allows_nil: false
+ attribute :time_estimate, default: 0
validates :time_estimate, numericality: { message: 'has an invalid format' }, allow_nil: false
validate :check_negative_time_spent
has_many :timelogs, dependent: :destroy, autosave: true # rubocop:disable Cop/ActiveRecordDependent
+ after_initialize :set_time_estimate_default_value
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
@@ -67,6 +68,13 @@ module TimeTrackable
val.is_a?(Integer) ? super([val, Gitlab::Database::MAX_INT_VALUE].min) : super(val)
end
+ def set_time_estimate_default_value
+ return if new_record?
+ return unless has_attribute?(:time_estimate)
+
+ self.time_estimate ||= self.class.column_defaults['time_estimate']
+ end
+
private
def reset_spent_time
diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb
index 7da4e31b472..db0fcd915b3 100644
--- a/app/models/container_repository.rb
+++ b/app/models/container_repository.rb
@@ -98,6 +98,8 @@ class ContainerRepository < ApplicationRecord
)
end
+ before_update :set_status_updated_at_to_now, if: :status_changed?
+
state_machine :migration_state, initial: :default, use_transactions: false do
state :pre_importing do
validates :migration_pre_import_started_at, presence: true
@@ -521,11 +523,20 @@ class ContainerRepository < ApplicationRecord
end
def set_delete_ongoing_status
- update_columns(status: :delete_ongoing, delete_started_at: Time.zone.now)
+ now = Time.zone.now
+ update_columns(
+ status: :delete_ongoing,
+ delete_started_at: now,
+ status_updated_at: now
+ )
end
def set_delete_scheduled_status
- update_columns(status: :delete_scheduled, delete_started_at: nil)
+ update_columns(
+ status: :delete_scheduled,
+ delete_started_at: nil,
+ status_updated_at: Time.zone.now
+ )
end
def migration_in_active_state?
@@ -623,6 +634,10 @@ class ContainerRepository < ApplicationRecord
tag
end
end
+
+ def set_status_updated_at_to_now
+ self.status_updated_at = Time.zone.now
+ end
end
ContainerRepository.prepend_mod_with('ContainerRepository')
diff --git a/app/models/customer_relations/organization.rb b/app/models/customer_relations/organization.rb
index 5eda9b4bf15..91656d4f846 100644
--- a/app/models/customer_relations/organization.rb
+++ b/app/models/customer_relations/organization.rb
@@ -85,8 +85,8 @@ class CustomerRelations::Organization < ApplicationRecord
private
def self.default_state_counts
- states.keys.each_with_object({}) do |key, memo|
- memo[key] = 0
+ states.keys.index_with do |key|
+ 0
end
end
diff --git a/app/models/dependency_proxy/group_setting.rb b/app/models/dependency_proxy/group_setting.rb
index 3a7ae66a263..b39ea36644a 100644
--- a/app/models/dependency_proxy/group_setting.rb
+++ b/app/models/dependency_proxy/group_setting.rb
@@ -3,7 +3,5 @@
class DependencyProxy::GroupSetting < ApplicationRecord
belongs_to :group
- attribute :enabled, default: true
-
validates :group, presence: true
end
diff --git a/app/models/deploy_token.rb b/app/models/deploy_token.rb
index 66d1ce01814..498ca9c4f30 100644
--- a/app/models/deploy_token.rb
+++ b/app/models/deploy_token.rb
@@ -37,6 +37,7 @@ class DeployToken < ApplicationRecord
message: "can contain only letters, digits, '_', '-', '+', and '.'"
}
+ validates :expires_at, iso8601_date: true, on: :create
validates :deploy_token_type, presence: true
enum deploy_token_type: {
group_type: 1,
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index ea92b978d3a..1254ce1c90a 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -363,6 +363,10 @@ class Deployment < ApplicationRecord
deployable&.user || user
end
+ def triggered_by?(user)
+ deployed_by == user
+ end
+
def link_merge_requests(relation)
# NOTE: relation.select will perform column deduplication,
# when id == environment_id it will outputs 2 columns instead of 3
@@ -441,9 +445,10 @@ class Deployment < ApplicationRecord
# default tag limit is 100, 0 means no limit
# when refs_by_oid is passed an SHA, returns refs for that commit
def tags(limit: 100)
- project.repository.refs_by_oid(oid: sha, limit: limit, ref_patterns: [Gitlab::Git::TAG_REF_PREFIX]) || []
+ strong_memoize_with(:tag, limit) do
+ project.repository.refs_by_oid(oid: sha, limit: limit, ref_patterns: [Gitlab::Git::TAG_REF_PREFIX]) || []
+ end
end
- strong_memoize_attr :tags
private
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 2d3f342953f..f1edfb3a34b 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -6,6 +6,7 @@ class Environment < ApplicationRecord
include FastDestroyAll::Helpers
include Presentable
include NullifyIfBlank
+ include FromUnion
self.reactive_cache_refresh_interval = 1.minute
self.reactive_cache_lifetime = 55.seconds
@@ -27,27 +28,29 @@ class Environment < ApplicationRecord
has_many :self_managed_prometheus_alert_events, inverse_of: :environment
has_many :alert_management_alerts, class_name: 'AlertManagement::Alert', inverse_of: :environment
- # NOTE: If you preload multiple last deployments of environments, use Preloaders::Environments::DeploymentPreloader.
- has_one :last_deployment, -> { success.ordered }, class_name: 'Deployment', inverse_of: :environment
- has_one :last_visible_deployment, -> { visible.order(id: :desc) }, inverse_of: :environment, class_name: 'Deployment'
+ # NOTE:
+ # 1) no-op arguments is to prevent accidental legacy preloading. See: https://gitlab.com/gitlab-org/gitlab/-/issues/369240
+ # 2) If you preload multiple last deployments of environments, use Preloaders::Environments::DeploymentPreloader.
+ has_one :last_deployment, -> (_env) { success.ordered }, class_name: 'Deployment', inverse_of: :environment
+ has_one :last_visible_deployment, -> (_env) { visible.order(id: :desc) }, inverse_of: :environment, class_name: 'Deployment'
+ has_one :upcoming_deployment, -> (_env) { upcoming.order(id: :desc) }, class_name: 'Deployment', inverse_of: :environment
Deployment::FINISHED_STATUSES.each do |status|
- has_one :"last_#{status}_deployment", -> { where(status: status).ordered },
+ has_one :"last_#{status}_deployment", -> (_env) { where(status: status).ordered },
class_name: 'Deployment', inverse_of: :environment
end
Deployment::UPCOMING_STATUSES.each do |status|
- has_one :"last_#{status}_deployment", -> { where(status: status).ordered_as_upcoming },
+ has_one :"last_#{status}_deployment", -> (_env) { where(status: status).ordered_as_upcoming },
class_name: 'Deployment', inverse_of: :environment
end
- has_one :upcoming_deployment, -> { upcoming.order(id: :desc) }, class_name: 'Deployment', inverse_of: :environment
has_one :latest_opened_most_severe_alert, -> { order_severity_with_open_prometheus_alert }, class_name: 'AlertManagement::Alert', inverse_of: :environment
before_validation :generate_slug, if: ->(env) { env.slug.blank? }
+ before_validation :ensure_environment_tier
before_save :set_environment_type
- before_save :ensure_environment_tier
after_save :clear_reactive_cache!
validates :name,
@@ -68,6 +71,10 @@ class Environment < ApplicationRecord
length: { maximum: 255 },
allow_nil: true
+ # Currently, the tier presence is validaed for newly created environments.
+ # After the `BackfillEnvironmentTiers` background migration has been completed, we should remove `on: :create`.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/385253.
+ validates :tier, presence: true, on: :create
validate :safe_external_url
validate :merge_request_not_changed
@@ -87,7 +94,6 @@ class Environment < ApplicationRecord
scope :in_review_folder, -> { where(environment_type: "review") }
scope :for_name, -> (name) { where(name: name) }
- scope :preload_cluster, -> { preload(last_deployment: :cluster) }
scope :preload_project, -> { preload(:project) }
scope :auto_stoppable, -> (limit) { available.where('auto_stop_at < ?', Time.zone.now).limit(limit) }
scope :auto_deletable, -> (limit) { stopped.where('auto_delete_at < ?', Time.zone.now).limit(limit) }
@@ -96,7 +102,16 @@ class Environment < ApplicationRecord
# Search environments which have names like the given query.
# Do not set a large limit unless you've confirmed that it works on gitlab.com scale.
scope :for_name_like, -> (query, limit: 5) do
- where('LOWER(environments.name) LIKE LOWER(?) || \'%\'', sanitize_sql_like(query)).limit(limit)
+ top_level = 'LOWER(environments.name) LIKE LOWER(?) || \'%\''
+
+ where(top_level, sanitize_sql_like(query)).limit(limit)
+ end
+
+ scope :for_name_like_within_folder, -> (query, limit: 5) do
+ within_folder = 'LOWER(ltrim(environments.name, environments.environment_type'\
+ ' || \'/\')) LIKE LOWER(?) || \'%\''
+
+ where(within_folder, sanitize_sql_like(query)).limit(limit)
end
scope :for_project, -> (project) { where(project_id: project) }
@@ -106,7 +121,6 @@ class Environment < ApplicationRecord
scope :with_rank, -> do
select('environments.*, rank() OVER (PARTITION BY project_id ORDER BY id DESC)')
end
- scope :for_id, -> (id) { where(id: id) }
scope :with_deployment, -> (sha, status: nil) do
deployments = Deployment.select(1).where('deployments.environment_id = environments.id').where(sha: sha)
@@ -197,12 +211,19 @@ class Environment < ApplicationRecord
update_all(auto_delete_at: at_time)
end
+ def self.nested
+ group('COALESCE(environment_type, id::text)', 'COALESCE(environment_type, name)')
+ .select('COALESCE(environment_type, id::text), COALESCE(environment_type, name) AS name',
+ 'COUNT(*) AS size', 'MAX(id) AS last_id')
+ .order('name ASC')
+ end
+
class << self
def count_by_state
environments_count_by_state = group(:state).count
- valid_states.each_with_object({}) do |state, count_hash|
- count_hash[state] = environments_count_by_state[state.to_s] || 0
+ valid_states.index_with do |state|
+ environments_count_by_state[state.to_s] || 0
end
end
end
@@ -490,6 +511,12 @@ class Environment < ApplicationRecord
environment_type.nil?
end
+ def deploy_freezes
+ Gitlab::SafeRequestStore.fetch("project:#{project_id}:freeze_periods_for_environments") do
+ project.freeze_periods
+ end
+ end
+
private
# We deliberately avoid using AddressableUrlValidator to allow users to update their environments even if they have
diff --git a/app/models/event.rb b/app/models/event.rb
index a1417db3410..ed65b367b8a 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -132,7 +132,7 @@ class Event < ApplicationRecord
where(
'action IN (?) OR (target_type IN (?) AND action IN (?))',
[actions[:pushed], actions[:commented]],
- %w(MergeRequest Issue), [actions[:created], actions[:closed], actions[:merged]]
+ %w(MergeRequest Issue WorkItem), [actions[:created], actions[:closed], actions[:merged]]
)
end
@@ -380,13 +380,11 @@ class Event < ApplicationRecord
protected
def capability
- @capability ||= begin
- capabilities.flat_map do |ability, syms|
- if syms.any? { |sym| send(sym) } # rubocop: disable GitlabSecurity/PublicSend
- [ability]
- else
- []
- end
+ @capability ||= capabilities.flat_map do |ability, syms|
+ if syms.any? { |sym| send(sym) } # rubocop: disable GitlabSecurity/PublicSend
+ [ability]
+ else
+ []
end
end
end
diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb
index 6c8bfc35334..b02074849a1 100644
--- a/app/models/generic_commit_status.rb
+++ b/app/models/generic_commit_status.rb
@@ -3,8 +3,6 @@
class GenericCommitStatus < CommitStatus
EXTERNAL_STAGE_IDX = 1_000_000
- before_validation :set_default_values
-
validates :target_url, addressable_url: true,
length: { maximum: 255 },
allow_nil: true
@@ -13,12 +11,6 @@ class GenericCommitStatus < CommitStatus
# GitHub compatible API
alias_attribute :context, :name
- def set_default_values
- self.context ||= 'default'
- self.stage ||= 'external'
- self.stage_idx ||= EXTERNAL_STAGE_IDX
- end
-
def tags
[:external]
end
diff --git a/app/models/gpg_key.rb b/app/models/gpg_key.rb
index 2db074e733e..1bf35179393 100644
--- a/app/models/gpg_key.rb
+++ b/app/models/gpg_key.rb
@@ -40,8 +40,8 @@ class GpgKey < ApplicationRecord
unless: -> { errors.has_key?(:key) }
before_validation :extract_fingerprint, :extract_primary_keyid
- after_commit :update_invalid_gpg_signatures, on: :create
after_create :generate_subkeys
+ after_commit :update_invalid_gpg_signatures, on: :create
def primary_keyid
super&.upcase
diff --git a/app/models/group.rb b/app/models/group.rb
index 098116ed800..0cdd7dd8596 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -20,6 +20,7 @@ class Group < Namespace
include BulkUsersByEmailLoad
include ChronicDurationAttribute
include RunnerTokenExpirationInterval
+ include Todoable
extend ::Gitlab::Utils::Override
@@ -119,7 +120,7 @@ class Group < Namespace
has_many :group_callouts, class_name: 'Users::GroupCallout', foreign_key: :group_id
- has_many :protected_branches, inverse_of: :group
+ has_many :protected_branches, inverse_of: :group, foreign_key: :namespace_id
has_one :group_feature, inverse_of: :group, class_name: 'Groups::FeatureSetting'
@@ -154,10 +155,10 @@ class Group < Namespace
prefix: RunnersTokenPrefixable::RUNNERS_TOKEN_PREFIX
after_create :post_create_hook
+ after_create -> { create_or_load_association(:group_feature) }
+ after_update :path_changed_hook, if: :saved_change_to_path?
after_destroy :post_destroy_hook
after_commit :update_two_factor_requirement
- after_update :path_changed_hook, if: :saved_change_to_path?
- after_create -> { create_or_load_association(:group_feature) }
scope :with_users, -> { includes(:users) }
@@ -165,7 +166,16 @@ class Group < Namespace
scope :by_id, ->(groups) { where(id: groups) }
- scope :by_ids_or_paths, -> (ids, paths) { by_id(ids).or(where(path: paths)) }
+ scope :by_ids_or_paths, -> (ids, paths) do
+ return by_id(ids) unless paths.present?
+
+ ids_by_full_path = Route
+ .for_routable_type(Namespace.name)
+ .where('LOWER(routes.path) IN (?)', paths.map(&:downcase))
+ .select(:namespace_id)
+
+ Group.from_union([by_id(ids), by_id(ids_by_full_path), where('LOWER(path) IN (?)', paths.map(&:downcase))])
+ end
scope :for_authorized_group_members, -> (user_ids) do
joins(:group_members)
@@ -550,6 +560,11 @@ class Group < Namespace
members_with_parents.pluck(Arel.sql('DISTINCT members.user_id'))
end
+ def self_and_hierarchy_intersecting_with_user_groups(user)
+ user_groups = GroupsFinder.new(user).execute.unscope(:order)
+ self_and_hierarchy.unscope(:order).where(id: user_groups)
+ end
+
def self_and_ancestors_ids
strong_memoize(:self_and_ancestors_ids) do
self_and_ancestors.pluck(:id)
@@ -831,6 +846,7 @@ class Group < Namespace
def has_project_with_service_desk_enabled?
Gitlab::ServiceDesk.supported? && all_projects.service_desk_enabled.exists?
end
+ strong_memoize_attr :has_project_with_service_desk_enabled?, :has_project_with_service_desk_enabled
def activity_path
Gitlab::Routing.url_helpers.activity_group_path(self)
@@ -887,6 +903,10 @@ class Group < Namespace
feature_flag_enabled_for_self_or_ancestor?(:work_items)
end
+ def work_items_mvc_feature_flag_enabled?
+ feature_flag_enabled_for_self_or_ancestor?(:work_items_mvc)
+ end
+
def work_items_mvc_2_feature_flag_enabled?
feature_flag_enabled_for_self_or_ancestor?(:work_items_mvc_2)
end
diff --git a/app/models/group_deploy_key.rb b/app/models/group_deploy_key.rb
index c65b00a6de0..9495df7ab6d 100644
--- a/app/models/group_deploy_key.rb
+++ b/app/models/group_deploy_key.rb
@@ -12,6 +12,11 @@ class GroupDeployKey < Key
joins(:group_deploy_keys_groups).where(group_deploy_keys_groups: { group_id: group_ids }).uniq
end
+ # Remove usage_type because it defined in Key class but doesn't have a column in group_deploy_keys table
+ def self.defined_enums
+ super.without('usage_type')
+ end
+
def type
'DeployKey'
end
diff --git a/app/models/hooks/active_hook_filter.rb b/app/models/hooks/active_hook_filter.rb
index cdcfd3f3ff5..4599ebf8717 100644
--- a/app/models/hooks/active_hook_filter.rb
+++ b/app/models/hooks/active_hook_filter.rb
@@ -18,10 +18,6 @@ class ActiveHookFilter
branch_name = Gitlab::Git.branch_name(data[:ref])
- if Feature.disabled?(:enhanced_webhook_support_regex)
- return RefMatcher.new(@hook.push_events_branch_filter).matches?(branch_name)
- end
-
case @hook.branch_filter_strategy
when 'all_branches'
true
diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb
index 27119d3a95a..94ced96bbde 100644
--- a/app/models/hooks/service_hook.rb
+++ b/app/models/hooks/service_hook.rb
@@ -13,4 +13,9 @@ class ServiceHook < WebHook
override :parent
delegate :parent, to: :integration
+
+ override :executable?
+ def executable?
+ true
+ end
end
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index 946cdda2e75..189291a38ec 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -41,12 +41,9 @@ class WebHook < ApplicationRecord
after_initialize :initialize_url_variables
before_validation :reset_token
- before_validation :set_branch_filter_nil, \
- if: -> { branch_filter_strategy_all_branches? && enhanced_webhook_support_regex? }
- validates :push_events_branch_filter, \
- untrusted_regexp: true, if: -> { branch_filter_strategy_regex? && enhanced_webhook_support_regex? }
- validates :push_events_branch_filter, \
- "web_hooks/wildcard_branch_filter": true, if: -> { branch_filter_strategy_wildcard? }
+ before_validation :set_branch_filter_nil, if: :branch_filter_strategy_all_branches?
+ validates :push_events_branch_filter, untrusted_regexp: true, if: :branch_filter_strategy_regex?
+ validates :push_events_branch_filter, "web_hooks/wildcard_branch_filter": true, if: :branch_filter_strategy_wildcard?
validates :url_variables, json_schema: { filename: 'web_hooks_url_variables' }
validate :no_missing_url_variables
@@ -59,8 +56,6 @@ class WebHook < ApplicationRecord
}, _prefix: true
scope :executable, -> do
- next all unless Feature.enabled?(:web_hooks_disable_failed)
-
where('recent_failures <= ? AND (disabled_until IS NULL OR disabled_until < ?)', FAILURE_THRESHOLD, Time.current)
end
@@ -69,23 +64,17 @@ class WebHook < ApplicationRecord
where('recent_failures > ? OR disabled_until >= ?', FAILURE_THRESHOLD, Time.current)
end
- def self.web_hooks_disable_failed?(hook)
- Feature.enabled?(:web_hooks_disable_failed, hook.parent)
- end
-
def executable?
!temporarily_disabled? && !permanently_disabled?
end
def temporarily_disabled?
- return false unless web_hooks_disable_failed?
return false if recent_failures <= FAILURE_THRESHOLD
disabled_until.present? && disabled_until >= Time.current
end
def permanently_disabled?
- return false unless web_hooks_disable_failed?
return false if disabled_until.present?
recent_failures > FAILURE_THRESHOLD
@@ -197,7 +186,7 @@ class WebHook < ApplicationRecord
end
# See app/validators/json_schemas/web_hooks_url_variables.json
- VARIABLE_REFERENCE_RE = /\{([A-Za-z_][A-Za-z0-9_]+)\}/.freeze
+ VARIABLE_REFERENCE_RE = /\{([A-Za-z]+[0-9]*(?:[._-][A-Za-z0-9]+)*)\}/.freeze
def interpolated_url
return url unless url.include?('{')
@@ -232,10 +221,6 @@ class WebHook < ApplicationRecord
backoff_count.succ.clamp(1, MAX_FAILURES)
end
- def web_hooks_disable_failed?
- self.class.web_hooks_disable_failed?(self)
- end
-
def initialize_url_variables
self.url_variables = {} if encrypted_url_variables.nil?
end
@@ -257,10 +242,6 @@ class WebHook < ApplicationRecord
errors.add(:url, "Invalid URL template. Missing keys: #{missing}")
end
- def enhanced_webhook_support_regex?
- Feature.enabled?(:enhanced_webhook_support_regex)
- end
-
def set_branch_filter_nil
self.push_events_branch_filter = nil
end
diff --git a/app/models/import_export_upload.rb b/app/models/import_export_upload.rb
index bc363cce8dd..bdb53653637 100644
--- a/app/models/import_export_upload.rb
+++ b/app/models/import_export_upload.rb
@@ -2,7 +2,6 @@
class ImportExportUpload < ApplicationRecord
include WithUploads
- include ObjectStorage::BackgroundMove
belongs_to :project
belongs_to :group
diff --git a/app/models/integration.rb b/app/models/integration.rb
index 41278dce22d..a630a6dee11 100644
--- a/app/models/integration.rb
+++ b/app/models/integration.rb
@@ -19,7 +19,7 @@ class Integration < ApplicationRecord
INTEGRATION_NAMES = %w[
asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker datadog discord
- drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat harbor irker jira
+ drone_ci emails_on_push ewm external_wiki hangouts_chat harbor irker jira
mattermost mattermost_slash_commands microsoft_teams packagist pipelines_email
pivotaltracker prometheus pumble pushover redmine slack slack_slash_commands teamcity unify_circuit webex_teams youtrack zentao
].freeze
@@ -41,7 +41,9 @@ class Integration < ApplicationRecord
Integrations::BaseCi
Integrations::BaseIssueTracker
Integrations::BaseMonitoring
+ Integrations::BaseSlackNotification
Integrations::BaseSlashCommands
+ Integrations::BaseThirdPartyWiki
].freeze
SECTION_TYPE_CONFIGURATION = 'configuration'
diff --git a/app/models/integrations/asana.rb b/app/models/integrations/asana.rb
index 2cfd71c9eb2..b8cfd718007 100644
--- a/app/models/integrations/asana.rb
+++ b/app/models/integrations/asana.rb
@@ -42,10 +42,8 @@ module Integrations
end
def client
- @_client ||= begin
- ::Asana::Client.new do |c|
- c.authentication :access_token, api_key
- end
+ @_client ||= ::Asana::Client.new do |c|
+ c.authentication :access_token, api_key
end
end
diff --git a/app/models/integrations/bamboo.rb b/app/models/integrations/bamboo.rb
index b4e97f0871e..fc5e6a88c2d 100644
--- a/app/models/integrations/bamboo.rb
+++ b/app/models/integrations/bamboo.rb
@@ -16,7 +16,7 @@ module Integrations
help: -> { s_('BambooService|Bamboo build plan key.') },
non_empty_password_title: -> { s_('BambooService|Enter new build key') },
non_empty_password_help: -> { s_('BambooService|Leave blank to use your current build key.') },
- placeholder: -> { s_('KEY') },
+ placeholder: -> { _('KEY') },
required: true
field :username,
diff --git a/app/models/integrations/base_chat_notification.rb b/app/models/integrations/base_chat_notification.rb
index 750aa60b185..f2a707c2214 100644
--- a/app/models/integrations/base_chat_notification.rb
+++ b/app/models/integrations/base_chat_notification.rb
@@ -33,7 +33,10 @@ module Integrations
boolean_accessor :notify_only_broken_pipelines, :notify_only_default_branch
- validates :webhook, presence: true, public_url: true, if: :activated?
+ validates :webhook,
+ presence: true,
+ public_url: true,
+ if: -> (integration) { integration.activated? && integration.requires_webhook? }
validates :labels_to_be_notified_behavior, inclusion: { in: LABEL_NOTIFICATION_BEHAVIOURS }, allow_blank: true
def initialize_properties
@@ -73,8 +76,6 @@ module Integrations
def default_fields
[
- { type: 'text', name: 'webhook', help: "#{webhook_help}", required: true }.freeze,
- { type: 'text', name: 'username', placeholder: 'GitLab-integration' }.freeze,
{ type: 'checkbox', name: 'notify_only_broken_pipelines', help: 'Do not send notifications for successful pipelines.' }.freeze,
{
type: 'select',
@@ -96,19 +97,24 @@ module Integrations
['Match all of the labels', MATCH_ALL_LABELS]
]
}.freeze
- ].freeze
+ ].tap do |fields|
+ next unless requires_webhook?
+
+ fields.unshift(
+ { type: 'text', name: 'webhook', help: webhook_help, required: true }.freeze,
+ { type: 'text', name: 'username', placeholder: 'GitLab-integration' }.freeze
+ )
+ end.freeze
end
def execute(data)
- return unless supported_events.include?(data[:object_kind])
-
- return unless webhook.present?
-
object_kind = data[:object_kind]
+ return false unless should_execute?(object_kind)
+
data = custom_data(data)
- return unless notify_label?(data)
+ return false unless notify_label?(data)
# WebHook events often have an 'update' event that follows a 'open' or
# 'close' action. Ignore update events for now to prevent duplicate
@@ -168,8 +174,17 @@ module Integrations
self.public_send(field_name) # rubocop:disable GitlabSecurity/PublicSend
end
+ def requires_webhook?
+ true
+ end
+
private
+ def should_execute?(object_kind)
+ supported_events.include?(object_kind) &&
+ (!requires_webhook? || webhook.present?)
+ end
+
def log_usage(_, _)
# Implement in child class
end
diff --git a/app/models/integrations/base_slack_notification.rb b/app/models/integrations/base_slack_notification.rb
index cb785afdcfe..7a2a91aa0d2 100644
--- a/app/models/integrations/base_slack_notification.rb
+++ b/app/models/integrations/base_slack_notification.rb
@@ -32,13 +32,15 @@ module Integrations
true
end
+ private
+
override :log_usage
def log_usage(event, user_id)
return unless user_id
return unless SUPPORTED_EVENTS_FOR_USAGE_LOG.include?(event)
- key = "i_ecosystem_slack_service_#{event}_notification"
+ key = "#{metrics_key_prefix}_#{event}_notification"
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(key, values: user_id)
@@ -55,8 +57,13 @@ module Integrations
label: Integration::SNOWPLOW_EVENT_LABEL,
property: key,
user: User.find(user_id),
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: key).to_context],
**optional_arguments
)
end
+
+ def metrics_key_prefix
+ raise NotImplementedError
+ end
end
end
diff --git a/app/models/integrations/base_slash_commands.rb b/app/models/integrations/base_slash_commands.rb
index 314f0a6ee5d..11ff7547325 100644
--- a/app/models/integrations/base_slash_commands.rb
+++ b/app/models/integrations/base_slash_commands.rb
@@ -60,7 +60,7 @@ module Integrations
# rubocop: disable CodeReuse/ServiceClass
def find_chat_user(params)
- ChatNames::FindUserService.new(self, params).execute
+ ChatNames::FindUserService.new(params[:team_id], params[:user_id]).execute
end
# rubocop: enable CodeReuse/ServiceClass
diff --git a/app/models/integrations/confluence.rb b/app/models/integrations/confluence.rb
index c1c43af99bf..31e9a171d1b 100644
--- a/app/models/integrations/confluence.rb
+++ b/app/models/integrations/confluence.rb
@@ -10,7 +10,7 @@ module Integrations
validate :validate_confluence_url_is_cloud, if: :activated?
field :confluence_url,
- title: -> { s_('Confluence Cloud Workspace URL') },
+ title: -> { _('Confluence Cloud Workspace URL') },
placeholder: 'https://example.atlassian.net/wiki',
required: true
diff --git a/app/models/integrations/datadog.rb b/app/models/integrations/datadog.rb
index 27bed5d3f76..80eecc14d0f 100644
--- a/app/models/integrations/datadog.rb
+++ b/app/models/integrations/datadog.rb
@@ -9,7 +9,7 @@ module Integrations
URL_API_KEYS_DOCS = "https://docs.#{DEFAULT_DOMAIN}/account_management/api-app-keys/"
SUPPORTED_EVENTS = %w[
- pipeline job archive_trace
+ pipeline build archive_trace
].freeze
TAG_KEY_VALUE_RE = %r{\A [\w-]+ : .*\S.* \z}x.freeze
@@ -48,8 +48,8 @@ module Integrations
field :archive_trace_events,
storage: :attribute,
type: 'checkbox',
- title: -> { s_('Logs') },
- checkbox_label: -> { s_('Enable logs collection') },
+ title: -> { _('Logs') },
+ checkbox_label: -> { _('Enable logs collection') },
help: -> { s_('When enabled, job logs are collected by Datadog and displayed along with pipeline execution traces.') }
field :datadog_service,
@@ -156,10 +156,10 @@ module Integrations
end
def execute(data)
+ return unless supported_events.include?(data[:object_kind])
+
object_kind = data[:object_kind]
object_kind = 'job' if object_kind == 'build'
- return unless supported_events.include?(object_kind)
-
data = hook_data(data, object_kind)
execute_web_hook!(data, "#{object_kind} hook")
end
diff --git a/app/models/integrations/flowdock.rb b/app/models/integrations/flowdock.rb
index 52efb29f2c1..d7625cfb3d2 100644
--- a/app/models/integrations/flowdock.rb
+++ b/app/models/integrations/flowdock.rb
@@ -1,28 +1,12 @@
# frozen_string_literal: true
+# This integration is scheduled for removal.
+# All records must be deleted before the class can be removed.
+# https://gitlab.com/gitlab-org/gitlab/-/issues/379197
module Integrations
class Flowdock < Integration
- validates :token, presence: true, if: :activated?
-
- field :token,
- type: 'password',
- help: -> { s_('FlowdockService|Enter your Flowdock token.') },
- non_empty_password_title: -> { s_('ProjectService|Enter new token') },
- non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
- placeholder: '1b609b52537...',
- required: true
-
- def title
- 'Flowdock'
- end
-
- def description
- s_('FlowdockService|Send event notifications from GitLab to Flowdock flows.')
- end
-
- def help
- docs_link = ActionController::Base.helpers.link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('api/services', anchor: 'flowdock'), target: '_blank', rel: 'noopener noreferrer'
- s_('FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
+ def readonly?
+ true
end
def self.to_param
@@ -30,22 +14,7 @@ module Integrations
end
def self.supported_events
- %w(push)
- end
-
- def execute(data)
- return unless supported_events.include?(data[:object_kind])
-
- ::Flowdock::Git.post(
- data[:ref],
- data[:before],
- data[:after],
- token: token,
- repo: project.repository,
- repo_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}",
- commit_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/-/commit/%s",
- diff_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/compare/%s...%s"
- )
+ %w[]
end
end
end
diff --git a/app/models/integrations/jira.rb b/app/models/integrations/jira.rb
index 65492bfd9c2..45302a0bd09 100644
--- a/app/models/integrations/jira.rb
+++ b/app/models/integrations/jira.rb
@@ -132,11 +132,9 @@ module Integrations
end
def client
- @client ||= begin
- JIRA::Client.new(options).tap do |client|
- # Replaces JIRA default http client with our implementation
- client.request_client = Gitlab::Jira::HttpClient.new(client.options)
- end
+ @client ||= JIRA::Client.new(options).tap do |client|
+ # Replaces JIRA default http client with our implementation
+ client.request_client = Gitlab::Jira::HttpClient.new(client.options)
end
end
@@ -406,6 +404,7 @@ module Integrations
label: Integration::SNOWPLOW_EVENT_LABEL,
property: key,
user: user,
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: key).to_context],
**optional_arguments
)
end
diff --git a/app/models/integrations/mattermost.rb b/app/models/integrations/mattermost.rb
index dd1c98ee06b..e3c5c22ad3a 100644
--- a/app/models/integrations/mattermost.rb
+++ b/app/models/integrations/mattermost.rb
@@ -5,7 +5,7 @@ module Integrations
include SlackMattermostNotifier
def title
- s_('Mattermost notifications')
+ _('Mattermost notifications')
end
def description
diff --git a/app/models/integrations/packagist.rb b/app/models/integrations/packagist.rb
index 7148de66aee..3973b492b6d 100644
--- a/app/models/integrations/packagist.rb
+++ b/app/models/integrations/packagist.rb
@@ -5,15 +5,15 @@ module Integrations
include HasWebHook
field :username,
- title: -> { s_('Username') },
- help: -> { s_('Enter your Packagist username.') },
+ title: -> { _('Username') },
+ help: -> { _('Enter your Packagist username.') },
placeholder: '',
required: true
field :token,
type: 'password',
- title: -> { s_('Token') },
- help: -> { s_('Enter your Packagist token.') },
+ title: -> { _('Token') },
+ help: -> { _('Enter your Packagist token.') },
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
placeholder: '',
diff --git a/app/models/integrations/pushover.rb b/app/models/integrations/pushover.rb
index 791e27c5db7..6bb6b6d60f6 100644
--- a/app/models/integrations/pushover.rb
+++ b/app/models/integrations/pushover.rb
@@ -112,7 +112,7 @@ module Integrations
user: user_key,
device: device,
priority: priority,
- title: "#{project.full_name}",
+ title: project.full_name.to_s,
message: message,
url: data[:project][:web_url],
url_title: s_("PushoverService|See project %{project_full_name}") % { project_full_name: project.full_name }
diff --git a/app/models/integrations/slack.rb b/app/models/integrations/slack.rb
index 89326b8174f..07d2d802915 100644
--- a/app/models/integrations/slack.rb
+++ b/app/models/integrations/slack.rb
@@ -20,5 +20,12 @@ module Integrations
def webhook_help
'https://hooks.slack.com/services/…'
end
+
+ private
+
+ override :metrics_key_prefix
+ def metrics_key_prefix
+ 'i_ecosystem_slack_service'
+ end
end
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index fc083002c41..1dd11ff8315 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -91,7 +91,7 @@ class Issue < ApplicationRecord
has_one :incident_management_issuable_escalation_status, class_name: 'IncidentManagement::IssuableEscalationStatus'
has_and_belongs_to_many :self_managed_prometheus_alert_events, join_table: :issues_self_managed_prometheus_alert_events # rubocop: disable Rails/HasAndBelongsToMany
has_and_belongs_to_many :prometheus_alert_events, join_table: :issues_prometheus_alert_events # rubocop: disable Rails/HasAndBelongsToMany
- has_many :alert_management_alerts, class_name: 'AlertManagement::Alert', inverse_of: :issue
+ has_many :alert_management_alerts, class_name: 'AlertManagement::Alert', inverse_of: :issue, validate: false
has_many :prometheus_alerts, through: :prometheus_alert_events
has_many :issue_customer_relations_contacts, class_name: 'CustomerRelations::IssueContact', inverse_of: :issue
has_many :customer_relations_contacts, through: :issue_customer_relations_contacts, source: :contact, class_name: 'CustomerRelations::Contact', inverse_of: :issues
@@ -105,9 +105,10 @@ class Issue < ApplicationRecord
validates :project, presence: true
validates :issue_type, presence: true
- validates :namespace, presence: true, if: -> { project.present? }
+ validates :namespace, presence: true
validates :work_item_type, presence: true
+ validate :allowed_work_item_type_change, on: :update, if: :work_item_type_id_changed?
validate :due_date_after_start_date
validate :parent_link_confidentiality
@@ -180,7 +181,7 @@ class Issue < ApplicationRecord
scope :without_hidden, -> {
if Feature.enabled?(:ban_user_feature_flag)
- where.not(author_id: Users::BannedUser.all.select(:user_id))
+ where('NOT EXISTS (?)', Users::BannedUser.select(1).where('issues.author_id = banned_users.user_id'))
else
all
end
@@ -216,8 +217,8 @@ class Issue < ApplicationRecord
before_validation :ensure_namespace_id, :ensure_work_item_type
- after_commit :expire_etag_cache, unless: :importing?
after_save :ensure_metrics, unless: :importing?
+ after_commit :expire_etag_cache, unless: :importing?
after_create_commit :record_create_action, unless: :importing?
attr_spammable :title, spam_title: true
@@ -743,6 +744,17 @@ class Issue < ApplicationRecord
self.work_item_type = WorkItems::Type.default_by_type(issue_type)
end
+
+ def allowed_work_item_type_change
+ return unless changes[:work_item_type_id]
+
+ involved_types = WorkItems::Type.where(id: changes[:work_item_type_id].compact).pluck(:base_type).uniq
+ disallowed_types = involved_types - WorkItems::Type::CHANGEABLE_BASE_TYPES
+
+ return if disallowed_types.empty?
+
+ errors.add(:work_item_type_id, format(_('can not be changed to %{new_type}'), new_type: work_item_type&.name))
+ end
end
Issue.prepend_mod_with('Issue')
diff --git a/app/models/issue_collection.rb b/app/models/issue_collection.rb
deleted file mode 100644
index 05607fc3a08..00000000000
--- a/app/models/issue_collection.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: true
-
-# IssueCollection can be used to reduce a list of issues down to a subset.
-#
-# IssueCollection is not meant to be some sort of Enumerable, instead it's meant
-# to take a list of issues and return a new list of issues based on some
-# criteria. For example, given a list of issues you may want to return a list of
-# issues that can be read or updated by a given user.
-class IssueCollection
- attr_reader :collection
-
- def initialize(collection)
- @collection = collection
- end
-
- # Returns all the issues that can be updated by the user.
- def updatable_by_user(user)
- return collection if user.admin?
-
- # Given all the issue projects we get a list of projects that the current
- # user has at least reporter access to.
- projects_with_reporter_access = user
- .projects_with_reporter_access_limited_to(project_ids)
- .pluck(:id)
-
- collection.select do |issue|
- if projects_with_reporter_access.include?(issue.project_id)
- true
- elsif issue.is_a?(Issue)
- issue.assignee_or_author?(user)
- else
- false
- end
- end
- end
-
- alias_method :visible_to, :updatable_by_user
-
- private
-
- def project_ids
- @project_ids ||= collection.map(&:project_id).uniq
- end
-end
diff --git a/app/models/issue_email_participant.rb b/app/models/issue_email_participant.rb
index 76a96151350..dd963bc9e7e 100644
--- a/app/models/issue_email_participant.rb
+++ b/app/models/issue_email_participant.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class IssueEmailParticipant < ApplicationRecord
+ include BulkInsertSafe
+
belongs_to :issue
validates :email, uniqueness: { scope: [:issue_id], case_sensitive: false }
diff --git a/app/models/iteration.rb b/app/models/iteration.rb
index c6269313d8b..ebec24731ed 100644
--- a/app/models/iteration.rb
+++ b/app/models/iteration.rb
@@ -4,9 +4,6 @@
class Iteration < ApplicationRecord
include IgnorableColumns
- # TODO https://gitlab.com/gitlab-org/gitlab/-/issues/372126
- ignore_column :project_id, remove_with: '15.7', remove_after: '2022-11-18'
-
self.table_name = 'sprints'
def self.reference_prefix
diff --git a/app/models/jira_connect_installation.rb b/app/models/jira_connect_installation.rb
index 23813fa138f..0e88d1ceae9 100644
--- a/app/models/jira_connect_installation.rb
+++ b/app/models/jira_connect_installation.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class JiraConnectInstallation < ApplicationRecord
+ include Gitlab::Routing
+
attr_encrypted :shared_secret,
mode: :per_attribute_iv,
algorithm: 'aes-256-gcm',
@@ -37,13 +39,19 @@ class JiraConnectInstallation < ApplicationRecord
def audience_url
return unless proxy?
- Gitlab::Utils.append_path(instance_url, '/-/jira_connect')
+ Gitlab::Utils.append_path(instance_url, jira_connect_base_path)
end
def audience_installed_event_url
return unless proxy?
- Gitlab::Utils.append_path(instance_url, '/-/jira_connect/events/installed')
+ Gitlab::Utils.append_path(instance_url, jira_connect_events_installed_path)
+ end
+
+ def audience_uninstalled_event_url
+ return unless proxy?
+
+ Gitlab::Utils.append_path(instance_url, jira_connect_events_uninstalled_path)
end
def proxy?
diff --git a/app/models/key.rb b/app/models/key.rb
index 78b0a38bcaa..1f2234129ed 100644
--- a/app/models/key.rb
+++ b/app/models/key.rb
@@ -32,12 +32,18 @@ class Key < ApplicationRecord
delegate :name, :email, to: :user, prefix: true
- after_commit :add_to_authorized_keys, on: :create
+ enum usage_type: {
+ auth_and_signing: 0,
+ auth: 1,
+ signing: 2
+ }
+
after_create :post_create_hook
after_create :refresh_user_cache
- after_commit :remove_from_authorized_keys, on: :destroy
after_destroy :post_destroy_hook
after_destroy :refresh_user_cache
+ after_commit :add_to_authorized_keys, on: :create
+ after_commit :remove_from_authorized_keys, on: :destroy
alias_attribute :fingerprint_md5, :fingerprint
alias_attribute :name, :title
@@ -45,6 +51,8 @@ class Key < ApplicationRecord
scope :preload_users, -> { preload(:user) }
scope :for_user, -> (user) { where(user: user) }
scope :order_last_used_at_desc, -> { reorder(arel_table[:last_used_at].desc.nulls_last) }
+ scope :auth, -> { where(usage_type: [:auth, :auth_and_signing]) }
+ scope :signing, -> { where(usage_type: [:signing, :auth_and_signing]) }
# Date is set specifically in this scope to improve query time.
scope :expired_today_and_not_notified, -> { where(["date(expires_at AT TIME ZONE 'UTC') = CURRENT_DATE AND expiry_notification_delivered_at IS NULL"]) }
diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb
index 8aa48561e60..e1f28c0e117 100644
--- a/app/models/lfs_object.rb
+++ b/app/models/lfs_object.rb
@@ -4,7 +4,6 @@ class LfsObject < ApplicationRecord
include AfterCommitQueue
include Checksummable
include EachBatch
- include ObjectStorage::BackgroundMove
include FileStoreMounter
has_many :lfs_objects_projects
diff --git a/app/models/member.rb b/app/models/member.rb
index 80c5fd7e468..107530daf51 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -61,6 +61,7 @@ class Member < ApplicationRecord
validate :access_level_inclusion
validate :validate_member_role_access_level
validate :validate_access_level_locked_for_member_role, on: :update
+ validate :validate_member_role_belongs_to_same_root_namespace
scope :with_invited_user_state, -> do
joins('LEFT JOIN users as invited_user ON invited_user.email = members.invite_email')
@@ -515,12 +516,22 @@ class Member < ApplicationRecord
end
end
+ def validate_member_role_belongs_to_same_root_namespace
+ return unless member_role_id
+
+ return if member_namespace.id == member_role.namespace_id
+ return if member_namespace.root_ancestor.id == member_role.namespace_id
+
+ errors.add(:member_namespace, _("must be in same hierarchy as custom role's namespace"))
+ end
+
def send_invite
# override in subclass
end
def send_request
notification_service.new_access_request(self)
+ todo_service.create_member_access_request(self) if source_type != 'Project'
end
def post_create_hook
@@ -579,6 +590,12 @@ class Member < ApplicationRecord
end
# rubocop: enable CodeReuse/ServiceClass
+ # rubocop: disable CodeReuse/ServiceClass
+ def todo_service
+ TodoService.new
+ end
+ # rubocop: enable CodeReuse/ServiceClass
+
def notifiable_options
{}
end
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index ad1ad1e74fe..796b05b7fff 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -55,6 +55,12 @@ class GroupMember < Member
{ group: group }
end
+ def last_owner_of_the_group?
+ return false unless access_level == Gitlab::Access::OWNER
+
+ group.member_last_owner?(self) || group.member_last_blocked_owner?(self)
+ end
+
private
override :refresh_member_authorized_projects
diff --git a/app/models/members/member_role.rb b/app/models/members/member_role.rb
index b4e3d6874ef..e9d7b1d3f80 100644
--- a/app/models/members/member_role.rb
+++ b/app/models/members/member_role.rb
@@ -1,18 +1,30 @@
# frozen_string_literal: true
class MemberRole < ApplicationRecord # rubocop:disable Gitlab/NamespacedClass
+ include IgnorableColumns
+ ignore_column :download_code, remove_with: '15.9', remove_after: '2023-01-22'
+
has_many :members
belongs_to :namespace
validates :namespace, presence: true
validates :base_access_level, presence: true
validate :belongs_to_top_level_namespace
+ validate :validate_namespace_locked, on: :update
+
+ validates_associated :members
private
def belongs_to_top_level_namespace
return if !namespace || namespace.root?
- errors.add(:namespace, s_("must be top-level namespace"))
+ errors.add(:namespace, s_("MemberRole|must be top-level namespace"))
+ end
+
+ def validate_namespace_locked
+ return unless namespace_id_changed?
+
+ errors.add(:namespace, s_("MemberRole|can't be changed"))
end
end
diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb
index 1099e0f48c0..6aa6afb595d 100644
--- a/app/models/members/project_member.rb
+++ b/app/models/members/project_member.rb
@@ -96,6 +96,10 @@ class ProjectMember < Member
{ project: project }
end
+ def holder_of_the_personal_namespace?
+ project.personal_namespace_holder?(user)
+ end
+
private
override :access_level_inclusion
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 735c0df1529..78c6d983a3d 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -121,6 +121,7 @@ class MergeRequest < ApplicationRecord
has_many :draft_notes
has_many :reviews, inverse_of: :merge_request
+ has_many :reviewed_by_users, -> { distinct }, through: :reviews, source: :author
has_many :created_environments, class_name: 'Environment', foreign_key: :merge_request_id, inverse_of: :merge_request
KNOWN_MERGE_PARAMS = [
@@ -139,6 +140,7 @@ class MergeRequest < ApplicationRecord
after_create :ensure_merge_request_diff, unless: :skip_ensure_merge_request_diff
after_update :clear_memoized_shas
after_update :reload_diff_if_branch_changed
+ after_save :keep_around_commit, unless: :importing?
after_commit :ensure_metrics, on: [:create, :update], unless: :importing?
after_commit :expire_etag_cache, unless: :importing?
@@ -246,7 +248,9 @@ class MergeRequest < ApplicationRecord
end
after_transition any => [:unchecked, :cannot_be_merged_recheck, :checking, :cannot_be_merged_rechecking, :can_be_merged, :cannot_be_merged] do |merge_request, transition|
- GraphqlTriggers.merge_request_merge_status_updated(merge_request)
+ merge_request.run_after_commit do
+ GraphqlTriggers.merge_request_merge_status_updated(merge_request)
+ end
end
# rubocop: disable CodeReuse/ServiceClass
@@ -438,8 +442,6 @@ class MergeRequest < ApplicationRecord
.pick(MergeRequest::Metrics.time_to_merge_expression)
end
- after_save :keep_around_commit, unless: :importing?
-
alias_attribute :project, :target_project
alias_attribute :project_id, :target_project_id
@@ -1270,7 +1272,7 @@ class MergeRequest < ApplicationRecord
end
def mergeable_discussions_state?
- return true unless project.only_allow_merge_if_all_discussions_are_resolved?
+ return true unless project.only_allow_merge_if_all_discussions_are_resolved?(inherit_group_setting: true)
unresolved_notes.none?(&:to_be_resolved?)
end
@@ -1382,7 +1384,7 @@ class MergeRequest < ApplicationRecord
def default_merge_commit_message(include_description: false, user: nil)
if self.target_project.merge_commit_template.present? && !include_description
- return ::Gitlab::MergeRequests::CommitMessageGenerator.new(merge_request: self, current_user: user).merge_message
+ return ::Gitlab::MergeRequests::MessageGenerator.new(merge_request: self, current_user: user).merge_commit_message
end
closes_issues_references = visible_closing_issues_for.map do |issue|
@@ -1398,7 +1400,7 @@ class MergeRequest < ApplicationRecord
message << "Closes #{closes_issues_references.to_sentence}"
end
- message << "#{description}" if include_description && description.present?
+ message << description if include_description && description.present?
message << "See merge request #{to_reference(full: true)}"
message.join("\n\n")
@@ -1406,7 +1408,7 @@ class MergeRequest < ApplicationRecord
def default_squash_commit_message(user: nil)
if self.target_project.squash_commit_template.present?
- return ::Gitlab::MergeRequests::CommitMessageGenerator.new(merge_request: self, current_user: user).squash_message
+ return ::Gitlab::MergeRequests::MessageGenerator.new(merge_request: self, current_user: user).squash_commit_message
end
title
@@ -1451,9 +1453,9 @@ class MergeRequest < ApplicationRecord
end
def mergeable_ci_state?
- return true unless project.only_allow_merge_if_pipeline_succeeds?
+ return true unless project.only_allow_merge_if_pipeline_succeeds?(inherit_group_setting: true)
return false unless actual_head_pipeline
- return true if project.allow_merge_on_skipped_pipeline? && actual_head_pipeline.skipped?
+ return true if project.allow_merge_on_skipped_pipeline?(inherit_group_setting: true) && actual_head_pipeline.skipped?
actual_head_pipeline.success?
end
diff --git a/app/models/merge_request/predictions.rb b/app/models/merge_request/predictions.rb
deleted file mode 100644
index ef9e00b5f74..00000000000
--- a/app/models/merge_request/predictions.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-class MergeRequest::Predictions < ApplicationRecord # rubocop:disable Style/ClassAndModuleChildren
- belongs_to :merge_request, inverse_of: :predictions
-
- validates :suggested_reviewers, json_schema: { filename: 'merge_request_predictions_suggested_reviewers' }
-end
diff --git a/app/models/merge_request_context_commit.rb b/app/models/merge_request_context_commit.rb
index ebbdecf8aa7..281e11c7c13 100644
--- a/app/models/merge_request_context_commit.rb
+++ b/app/models/merge_request_context_commit.rb
@@ -12,7 +12,7 @@ class MergeRequestContextCommit < ApplicationRecord
validates :sha, presence: true
validates :sha, uniqueness: { message: 'has already been added' }
- serialize :trailers, Serializers::Json # rubocop:disable Cop/ActiveRecordSerialize
+ attribute :trailers, :ind_jsonb
validates :trailers, json_schema: { filename: 'git_trailers' }
# Sort by committed date in descending order to ensure latest commits comes on the top
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 98a9ccc2040..cff8911d84b 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -6,7 +6,6 @@ class MergeRequestDiff < ApplicationRecord
include ManualInverseAssociation
include EachBatch
include Gitlab::Utils::StrongMemoize
- include ObjectStorage::BackgroundMove
include BulkInsertableAssociations
# Don't display more than 100 commits at once
@@ -267,7 +266,7 @@ class MergeRequestDiff < ApplicationRecord
end
# This method will rely on repository branch sha
- # in case start_commit_sha is nil. Its necesarry for old merge request diff
+ # in case start_commit_sha is nil. It's necessary for old merge request diff
# created before version 8.4 to work
def safe_start_commit_sha
start_commit_sha || merge_request.target_branch_sha
@@ -414,6 +413,29 @@ class MergeRequestDiff < ApplicationRecord
end
end
+ def paginated_diffs(page, per_page)
+ fetching_repository_diffs({}) do |comparison|
+ reorder_diff_files!
+
+ collection = Gitlab::Diff::FileCollection::PaginatedMergeRequestDiff.new(
+ self,
+ page,
+ per_page
+ )
+
+ if comparison
+ comparison.diffs(
+ paths: collection.diff_paths,
+ page: collection.current_page,
+ per_page: collection.limit_value,
+ count: collection.total_count
+ )
+ else
+ collection
+ end
+ end
+ end
+
def diffs(diff_options = nil)
fetching_repository_diffs(diff_options) do |comparison|
# It should fetch the repository when diffs are cleaned by the system.
diff --git a/app/models/merge_request_diff_commit.rb b/app/models/merge_request_diff_commit.rb
index 152fb195c97..7e2efa2049b 100644
--- a/app/models/merge_request_diff_commit.rb
+++ b/app/models/merge_request_diff_commit.rb
@@ -35,7 +35,7 @@ class MergeRequestDiffCommit < ApplicationRecord
sha_attribute :sha
alias_attribute :id, :sha
- serialize :trailers, Serializers::Json # rubocop:disable Cop/ActiveRecordSerialize
+ attribute :trailers, :ind_jsonb
validates :trailers, json_schema: { filename: 'git_trailers' }
scope :with_users, -> { preload(:commit_author, :committer) }
diff --git a/app/models/ml/candidate.rb b/app/models/ml/candidate.rb
index f7da4418624..f24161d598f 100644
--- a/app/models/ml/candidate.rb
+++ b/app/models/ml/candidate.rb
@@ -11,6 +11,7 @@ module Ml
belongs_to :user
has_many :metrics, class_name: 'Ml::CandidateMetric'
has_many :params, class_name: 'Ml::CandidateParam'
+ has_many :metadata, class_name: 'Ml::CandidateMetadata'
has_many :latest_metrics, -> { latest }, class_name: 'Ml::CandidateMetric', inverse_of: :candidate
attribute :iid, default: -> { SecureRandom.uuid }
@@ -18,7 +19,21 @@ module Ml
scope :including_metrics_and_params, -> { includes(:latest_metrics, :params) }
def artifact_root
- "/ml_candidate_#{iid}/-/"
+ "/#{package_name}/#{package_version}/"
+ end
+
+ def artifact
+ ::Packages::Generic::PackageFinder.new(experiment.project).execute!(package_name, package_version)
+ rescue ActiveRecord::RecordNotFound
+ nil
+ end
+
+ def package_name
+ "ml_candidate_#{iid}"
+ end
+
+ def package_version
+ '-'
end
class << self
diff --git a/app/models/ml/candidate_metadata.rb b/app/models/ml/candidate_metadata.rb
new file mode 100644
index 00000000000..06b893c211f
--- /dev/null
+++ b/app/models/ml/candidate_metadata.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Ml
+ class CandidateMetadata < ApplicationRecord
+ validates :candidate, presence: true
+ validates :name,
+ length: { maximum: 250 },
+ presence: true,
+ uniqueness: { scope: :candidate, message: ->(candidate, _) { "'#{candidate.name}' already taken" } }
+ validates :value, length: { maximum: 5000 }, presence: true
+
+ belongs_to :candidate, class_name: 'Ml::Candidate'
+ end
+end
diff --git a/app/models/ml/experiment.rb b/app/models/ml/experiment.rb
index 05b238b960d..0a326b0e005 100644
--- a/app/models/ml/experiment.rb
+++ b/app/models/ml/experiment.rb
@@ -10,6 +10,7 @@ module Ml
belongs_to :project
belongs_to :user
has_many :candidates, class_name: 'Ml::Candidate'
+ has_many :metadata, class_name: 'Ml::ExperimentMetadata'
has_internal_id :iid, scope: :project
diff --git a/app/models/ml/experiment_metadata.rb b/app/models/ml/experiment_metadata.rb
new file mode 100644
index 00000000000..93496807e1a
--- /dev/null
+++ b/app/models/ml/experiment_metadata.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Ml
+ class ExperimentMetadata < ApplicationRecord
+ validates :experiment, presence: true
+ validates :name,
+ length: { maximum: 250 },
+ presence: true,
+ uniqueness: { scope: :experiment, message: ->(exp, _) { "'#{exp.name}' already taken" } }
+ validates :value, length: { maximum: 5000 }, presence: true
+
+ belongs_to :experiment, class_name: 'Ml::Experiment'
+ end
+end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 51c39ad4ec3..d7d53956656 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -86,6 +86,7 @@ class Namespace < ApplicationRecord
has_many :issues, inverse_of: :namespace
has_many :timelog_categories, class_name: 'TimeTracking::TimelogCategory'
+ has_many :achievements, class_name: 'Achievements::Achievement'
validates :owner, presence: true, if: ->(n) { n.owner_required? }
validates :name,
@@ -131,26 +132,28 @@ class Namespace < ApplicationRecord
to: :namespace_settings, allow_nil: true
delegate :show_diff_preview_in_email, :show_diff_preview_in_email?, :show_diff_preview_in_email=,
to: :namespace_settings
+ delegate :runner_registration_enabled, :runner_registration_enabled?, :runner_registration_enabled=,
+ to: :namespace_settings
delegate :maven_package_requests_forwarding,
:pypi_package_requests_forwarding,
:npm_package_requests_forwarding,
to: :package_settings
- after_save :reload_namespace_details
-
- after_commit :refresh_access_of_projects_invited_groups, on: :update, if: -> { previous_changes.key?('share_with_group_lock') }
-
before_create :sync_share_with_group_lock_with_parent
before_update :sync_share_with_group_lock_with_parent, if: :parent_changed?
after_update :force_share_with_group_lock_on_descendants, if: -> { saved_change_to_share_with_group_lock? && share_with_group_lock? }
after_update :expire_first_auto_devops_config_cache, if: -> { saved_change_to_auto_devops_enabled? }
+ after_update :move_dir, if: :saved_change_to_path_or_parent?, unless: -> { is_a?(Namespaces::ProjectNamespace) }
+ after_destroy :rm_dir
+ after_save :reload_namespace_details
+
+ after_commit :refresh_access_of_projects_invited_groups, on: :update, if: -> { previous_changes.key?('share_with_group_lock') }
+
after_sync_traversal_ids :schedule_sync_event_worker # custom callback defined in Namespaces::Traversal::Linear
# Legacy Storage specific hooks
- after_update :move_dir, if: :saved_change_to_path_or_parent?, unless: -> { is_a?(Namespaces::ProjectNamespace) }
before_destroy(prepend: true) { prepare_for_destroy }
- after_destroy :rm_dir
after_commit :expire_child_caches, on: :update, if: -> {
Feature.enabled?(:cached_route_lookups, self, type: :ops) &&
saved_change_to_name? || saved_change_to_path? || saved_change_to_parent_id?
@@ -330,6 +333,13 @@ class Namespace < ApplicationRecord
type.nil? || type == Namespaces::UserNamespace.sti_name || !(group_namespace? || project_namespace?)
end
+ def bot_user_namespace?
+ return false unless user_namespace?
+ return false unless owner && owner.bot?
+
+ true
+ end
+
def owner_required?
user_namespace?
end
@@ -507,6 +517,10 @@ class Namespace < ApplicationRecord
root? && actual_plan.paid?
end
+ def prevent_delete?
+ paid?
+ end
+
def actual_limits
# We default to PlanLimits.new otherwise a lot of specs would fail
# On production each plan should already have associated limits record
@@ -541,12 +555,10 @@ class Namespace < ApplicationRecord
def shared_runners_setting
if shared_runners_enabled
SR_ENABLED
+ elsif allow_descendants_override_disabled_shared_runners
+ SR_DISABLED_WITH_OVERRIDE
else
- if allow_descendants_override_disabled_shared_runners
- SR_DISABLED_WITH_OVERRIDE
- else
- SR_DISABLED_AND_UNOVERRIDABLE
- end
+ SR_DISABLED_AND_UNOVERRIDABLE
end
end
@@ -597,6 +609,10 @@ class Namespace < ApplicationRecord
namespace_settings&.enabled_git_access_protocol
end
+ def all_ancestors_have_runner_registration_enabled?
+ namespace_settings&.all_ancestors_have_runner_registration_enabled?
+ end
+
private
def cluster_enabled_granted?
diff --git a/app/models/namespace_setting.rb b/app/models/namespace_setting.rb
index 3e6371b0c4d..5081d5cdafe 100644
--- a/app/models/namespace_setting.rb
+++ b/app/models/namespace_setting.rb
@@ -59,6 +59,16 @@ class NamespaceSetting < ApplicationRecord
all_ancestors_allow_diff_preview_in_email?
end
+ def runner_registration_enabled?
+ runner_registration_enabled && all_ancestors_have_runner_registration_enabled?
+ end
+
+ def all_ancestors_have_runner_registration_enabled?
+ return true unless namespace.has_parent?
+
+ !self.class.where(namespace_id: namespace.ancestors, runner_registration_enabled: false).exists?
+ end
+
private
def all_ancestors_allow_diff_preview_in_email?
diff --git a/app/models/namespace_statistics.rb b/app/models/namespace_statistics.rb
index 04ca05d85ff..a17ca2e2c1d 100644
--- a/app/models/namespace_statistics.rb
+++ b/app/models/namespace_statistics.rb
@@ -10,8 +10,8 @@ class NamespaceStatistics < ApplicationRecord # rubocop:disable Gitlab/Namespace
scope :for_namespaces, -> (namespaces) { where(namespace: namespaces) }
before_save :update_storage_size
- after_save :update_root_storage_statistics, if: :saved_change_to_storage_size?
after_destroy :update_root_storage_statistics
+ after_save :update_root_storage_statistics, if: :saved_change_to_storage_size?
delegate :group_namespace?, to: :namespace
diff --git a/app/models/note.rb b/app/models/note.rb
index 8e1f4979602..052df6142c5 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -168,10 +168,10 @@ class Note < ApplicationRecord
# Syncs `confidential` with `internal` as we rename the column.
# https://gitlab.com/gitlab-org/gitlab/-/issues/367923
before_create :set_internal_flag
+ after_destroy :expire_etag_cache
after_save :keep_around_commit, if: :for_project_noteable?, unless: -> { importing? || skip_keep_around_commits }
after_save :expire_etag_cache, unless: :importing?
after_save :touch_noteable, unless: :importing?
- after_destroy :expire_etag_cache
after_commit :notify_after_create, on: :create
after_commit :notify_after_destroy, on: :destroy
diff --git a/app/models/operations/feature_flags_client.rb b/app/models/operations/feature_flags_client.rb
index e8c237abbc5..5a05d76254d 100644
--- a/app/models/operations/feature_flags_client.rb
+++ b/app/models/operations/feature_flags_client.rb
@@ -19,11 +19,11 @@ module Operations
before_validation :ensure_token!
- def self.find_for_project_and_token(project, token)
- return unless project
+ def self.find_for_project_and_token(project_id, token)
+ return unless project_id
return unless token
- where(project_id: project).find_by_token(token)
+ where(project_id: project_id).find_by_token(token)
end
def self.update_last_feature_flag_updated_at!(project)
diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb
index 317db51f4ef..17c5415939c 100644
--- a/app/models/packages/package.rb
+++ b/app/models/packages/package.rb
@@ -149,6 +149,7 @@ class Packages::Package < ApplicationRecord
end
scope :preload_composer, -> { preload(:composer_metadatum) }
scope :preload_npm_metadatum, -> { preload(:npm_metadatum) }
+ scope :preload_pypi_metadatum, -> { preload(:pypi_metadatum) }
scope :without_nuget_temporary_name, -> { where.not(name: Packages::Nuget::TEMPORARY_PACKAGE_NAME) }
diff --git a/app/models/packages/rpm/repository_file.rb b/app/models/packages/rpm/repository_file.rb
index 4b5fa59c6ee..614ec9b3e56 100644
--- a/app/models/packages/rpm/repository_file.rb
+++ b/app/models/packages/rpm/repository_file.rb
@@ -8,6 +8,8 @@ module Packages
include Packages::Installable
INSTALLABLE_STATUSES = [:default].freeze
+ FILELISTS_FILENAME = 'filelists.xml'
+ FILELISTS_SIZE_LIMITATION = 20.megabytes
enum status: { default: 0, pending_destruction: 1, processing: 2, error: 3 }
@@ -20,6 +22,14 @@ module Packages
mount_file_store_uploader Packages::Rpm::RepositoryFileUploader
update_project_statistics project_statistics_name: :packages_size
+
+ def self.has_oversized_filelists?(project_id:)
+ where(
+ project_id: project_id,
+ file_name: FILELISTS_FILENAME,
+ size: [FILELISTS_SIZE_LIMITATION..]
+ ).exists?
+ end
end
end
end
diff --git a/app/models/pages/lookup_path.rb b/app/models/pages/lookup_path.rb
index c1056d4f6cb..cf0f0f9e92f 100644
--- a/app/models/pages/lookup_path.rb
+++ b/app/models/pages/lookup_path.rb
@@ -19,11 +19,13 @@ module Pages
def access_control
project.private_pages?
end
+ strong_memoize_attr :access_control
def https_only
domain_https = domain ? domain.https? : true
project.pages_https_only? && domain_https
end
+ strong_memoize_attr :https_only
def source
return unless deployment&.file
@@ -41,6 +43,7 @@ module Pages
file_count: deployment.file_count
}
end
+ strong_memoize_attr :source
def prefix
if project.pages_group_root?
@@ -49,6 +52,7 @@ module Pages
project.full_path.delete_prefix(trim_prefix) + '/'
end
end
+ strong_memoize_attr :prefix
private
diff --git a/app/models/pages/virtual_domain.rb b/app/models/pages/virtual_domain.rb
index 119cc7fc166..fafbe449c8c 100644
--- a/app/models/pages/virtual_domain.rb
+++ b/app/models/pages/virtual_domain.rb
@@ -28,6 +28,7 @@ module Pages
paths.sort_by(&:prefix).reverse
end
+ # cache_key is required by #present_cached in ::API::Internal::Pages
def cache_key
@cache_key ||= cache&.cache_key
end
diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb
index 328c67a0711..4e3f4b0c328 100644
--- a/app/models/pages_domain.rb
+++ b/app/models/pages_domain.rb
@@ -17,6 +17,7 @@ class PagesDomain < ApplicationRecord
has_many :acme_orders, class_name: "PagesDomainAcmeOrder"
has_many :serverless_domain_clusters, class_name: 'Serverless::DomainCluster', inverse_of: :pages_domain
+ after_initialize :set_verification_code
before_validation :clear_auto_ssl_failure, unless: :auto_ssl_enabled
validates :domain, hostname: { allow_numeric_hostname: true }
@@ -44,8 +45,6 @@ class PagesDomain < ApplicationRecord
key: Settings.attr_encrypted_db_key_base,
algorithm: 'aes-256-cbc'
- after_initialize :set_verification_code
-
scope :for_project, ->(project) { where(project: project) }
scope :enabled, -> { where('enabled_until >= ?', Time.current) }
diff --git a/app/models/performance_monitoring/prometheus_dashboard.rb b/app/models/performance_monitoring/prometheus_dashboard.rb
index 4804f620a99..37bf080ae49 100644
--- a/app/models/performance_monitoring/prometheus_dashboard.rb
+++ b/app/models/performance_monitoring/prometheus_dashboard.rb
@@ -53,8 +53,6 @@ module PerformanceMonitoring
# This method is planned to be refactored as a part of https://gitlab.com/gitlab-org/gitlab/-/issues/219398
# implementation. For new existing logic was reused to faster deliver MVC
def schema_validation_warnings
- return run_custom_validation.map(&:message) if Feature.enabled?(:metrics_dashboard_exhaustive_validations, environment&.project)
-
self.class.from_json(reload_schema)
[]
rescue Gitlab::Metrics::Dashboard::Errors::LayoutError => e
@@ -65,11 +63,6 @@ module PerformanceMonitoring
private
- def run_custom_validation
- Gitlab::Metrics::Dashboard::Validator
- .errors(reload_schema, dashboard_path: path, project: environment&.project)
- end
-
# dashboard finder methods are somehow limited, #find includes checking if
# user is authorised to view selected dashboard, but modifies schema, which in some cases may
# cause false positives returned from validation, and #find_raw does not authorise users
diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb
index 3126dba9d6d..887ef36cc17 100644
--- a/app/models/personal_access_token.rb
+++ b/app/models/personal_access_token.rb
@@ -18,6 +18,7 @@ class PersonalAccessToken < ApplicationRecord
belongs_to :user
+ after_initialize :set_default_scopes, if: :persisted?
before_save :ensure_token
scope :active, -> { not_revoked.not_expired }
@@ -41,8 +42,6 @@ class PersonalAccessToken < ApplicationRecord
validates :scopes, presence: true
validate :validate_scopes
- after_initialize :set_default_scopes, if: :persisted?
-
def revoke!
update!(revoked: true)
end
diff --git a/app/models/postgresql/detached_partition.rb b/app/models/postgresql/detached_partition.rb
index b0dd52c9657..d26778957d5 100644
--- a/app/models/postgresql/detached_partition.rb
+++ b/app/models/postgresql/detached_partition.rb
@@ -7,5 +7,9 @@ module Postgresql
def fully_qualified_table_name
"#{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}.#{table_name}"
end
+
+ def table_schema
+ Gitlab::Database::GitlabSchema.table_schema(table_name)
+ end
end
end
diff --git a/app/models/programming_language.rb b/app/models/programming_language.rb
index 06e3034e56a..4156c672518 100644
--- a/app/models/programming_language.rb
+++ b/app/models/programming_language.rb
@@ -10,4 +10,22 @@ class ProgrammingLanguage < ApplicationRecord
sanitized_names = names.map(&method(:sanitize_sql_like))
where(arel_table[:name].matches_any(sanitized_names))
end
+
+ def self.most_popular(limit = 25)
+ sql = <<~SQL
+ SELECT
+ mcv
+ FROM
+ pg_stats
+ CROSS JOIN LATERAL
+ unnest(most_common_vals::text::int[]) mt(mcv)
+ WHERE
+ tablename = 'repository_languages' and attname='programming_language_id'
+ LIMIT
+ $1
+ SQL
+ ids = connection.exec_query(sql, 'SQL', [limit]).rows.flatten
+
+ where(id: ids).order(:name)
+ end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 0c4f76fb2b9..73dbb55a07b 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -89,68 +89,56 @@ class Project < ApplicationRecord
cache_markdown_field :description, pipeline: :description
- default_value_for :packages_enabled, true
- default_value_for :archived, false
- default_value_for :resolve_outdated_diff_discussions, false
- default_value_for(:repository_storage) do
- Repository.pick_storage_shard
- end
+ attribute :packages_enabled, default: true
+ attribute :archived, default: false
+ attribute :resolve_outdated_diff_discussions, default: false
+ attribute :repository_storage, default: -> { Repository.pick_storage_shard }
+ attribute :shared_runners_enabled, default: -> { Gitlab::CurrentSettings.shared_runners_enabled }
+ attribute :only_allow_merge_if_all_discussions_are_resolved, default: false
+ attribute :remove_source_branch_after_merge, default: true
+ attribute :autoclose_referenced_issues, default: true
+ attribute :ci_config_path, default: -> { Gitlab::CurrentSettings.default_ci_config_path }
- default_value_for(:shared_runners_enabled) { Gitlab::CurrentSettings.shared_runners_enabled }
default_value_for :issues_enabled, gitlab_config_features.issues
default_value_for :merge_requests_enabled, gitlab_config_features.merge_requests
default_value_for :builds_enabled, gitlab_config_features.builds
default_value_for :wiki_enabled, gitlab_config_features.wiki
default_value_for :snippets_enabled, gitlab_config_features.snippets
- default_value_for :only_allow_merge_if_all_discussions_are_resolved, false
- default_value_for :remove_source_branch_after_merge, true
- default_value_for :autoclose_referenced_issues, true
- default_value_for(:ci_config_path) { Gitlab::CurrentSettings.default_ci_config_path }
add_authentication_token_field :runners_token,
encrypted: -> { Feature.enabled?(:projects_tokens_optional_encryption) ? :optional : :required },
prefix: RunnersTokenPrefixable::RUNNERS_TOKEN_PREFIX
+ # Storage specific hooks
+ after_initialize :use_hashed_storage
before_validation :mark_remote_mirrors_for_removal, if: -> { RemoteMirror.table_exists? }
- before_save :ensure_runners_token
before_validation :ensure_project_namespace_in_sync
-
before_validation :set_package_registry_access_level, if: :packages_enabled_changed?
before_validation :remove_leading_spaces_on_name
-
- after_save :update_project_statistics, if: :saved_change_to_namespace_id?
-
- after_save :schedule_sync_event_worker, if: -> { saved_change_to_id? || saved_change_to_namespace_id? }
-
- after_save :create_import_state, if: ->(project) { project.import? && project.import_state.nil? }
-
- after_save :save_topics
-
- after_save :reload_project_namespace_details
+ after_validation :check_pending_delete
+ before_save :ensure_runners_token
after_create -> { create_or_load_association(:project_feature) }
-
after_create -> { create_or_load_association(:ci_cd_settings) }
-
after_create -> { create_or_load_association(:container_expiration_policy) }
-
after_create -> { create_or_load_association(:pages_metadatum) }
-
after_create :set_timestamps_for_create
+ after_create :check_repository_absence!
after_update :update_forks_visibility_level
-
before_destroy :remove_private_deploy_keys
+ after_destroy :remove_exports
+ after_save :update_project_statistics, if: :saved_change_to_namespace_id?
- use_fast_destroy :build_trace_chunks
+ after_save :schedule_sync_event_worker, if: -> { saved_change_to_id? || saved_change_to_namespace_id? }
- after_destroy :remove_exports
+ after_save :create_import_state, if: ->(project) { project.import? && project.import_state.nil? }
- after_validation :check_pending_delete
+ after_save :save_topics
- # Storage specific hooks
- after_initialize :use_hashed_storage
- after_create :check_repository_absence!
+ after_save :reload_project_namespace_details
+
+ use_fast_destroy :build_trace_chunks
has_many :project_topics, -> { order(:id) }, class_name: 'Projects::ProjectTopic'
has_many :topics, through: :project_topics, class_name: 'Projects::Topic'
@@ -196,7 +184,6 @@ class Project < ApplicationRecord
has_one :emails_on_push_integration, class_name: 'Integrations::EmailsOnPush'
has_one :ewm_integration, class_name: 'Integrations::Ewm'
has_one :external_wiki_integration, class_name: 'Integrations::ExternalWiki'
- has_one :flowdock_integration, class_name: 'Integrations::Flowdock'
has_one :hangouts_chat_integration, class_name: 'Integrations::HangoutsChat'
has_one :harbor_integration, class_name: 'Integrations::Harbor'
has_one :irker_integration, class_name: 'Integrations::Irker'
@@ -231,6 +218,17 @@ class Project < ApplicationRecord
has_one :fork_network_member
has_one :fork_network, through: :fork_network_member
has_one :forked_from_project, through: :fork_network_member
+
+ # Projects with a very large number of notes may time out destroying them
+ # through the foreign key. Additionally, the deprecated attachment uploader
+ # for notes requires us to use dependent: :destroy to avoid orphaning uploaded
+ # files.
+ #
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/207222
+ # Order of this association is important for project deletion.
+ # has_many :notes` should be the first association among all `has_many` associations.
+ has_many :notes, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
+
has_many :forked_to_members, class_name: 'ForkNetworkMember', foreign_key: 'forked_from_project_id'
has_many :forks, through: :forked_to_members, source: :project, inverse_of: :forked_from_project
has_many :fork_network_projects, through: :fork_network, source: :projects
@@ -259,25 +257,30 @@ class Project < ApplicationRecord
has_one :service_desk_setting, class_name: 'ServiceDeskSetting'
# Merge requests for target project should be removed with it
- has_many :merge_requests, foreign_key: 'target_project_id', inverse_of: :target_project
+ has_many :merge_requests, foreign_key: 'target_project_id', inverse_of: :target_project, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :merge_request_metrics, foreign_key: 'target_project', class_name: 'MergeRequest::Metrics', inverse_of: :target_project
has_many :source_of_merge_requests, foreign_key: 'source_project_id', class_name: 'MergeRequest'
- has_many :issues
+ has_many :issues, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :incident_management_issuable_escalation_statuses, through: :issues, inverse_of: :project, class_name: 'IncidentManagement::IssuableEscalationStatus'
has_many :incident_management_timeline_event_tags, inverse_of: :project, class_name: 'IncidentManagement::TimelineEventTag'
- has_many :labels, class_name: 'ProjectLabel'
- has_many :integrations
+ has_many :labels, class_name: 'ProjectLabel', dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :events
has_many :milestones
- # Projects with a very large number of notes may time out destroying them
- # through the foreign key. Additionally, the deprecated attachment uploader
- # for notes requires us to use dependent: :destroy to avoid orphaning uploaded
- # files.
- #
- # https://gitlab.com/gitlab-org/gitlab/-/issues/207222
- has_many :notes, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
-
+ has_many :integrations
+ has_many :alert_hooks_integrations, -> { alert_hooks }, class_name: 'Integration'
+ has_many :archive_trace_hooks_integrations, -> { archive_trace_hooks }, class_name: 'Integration'
+ has_many :confidential_issue_hooks_integrations, -> { confidential_issue_hooks }, class_name: 'Integration'
+ has_many :confidential_note_hooks_integrations, -> { confidential_note_hooks }, class_name: 'Integration'
+ has_many :deployment_hooks_integrations, -> { deployment_hooks }, class_name: 'Integration'
+ has_many :issue_hooks_integrations, -> { issue_hooks }, class_name: 'Integration'
+ has_many :job_hooks_integrations, -> { job_hooks }, class_name: 'Integration'
+ has_many :merge_request_hooks_integrations, -> { merge_request_hooks }, class_name: 'Integration'
+ has_many :note_hooks_integrations, -> { note_hooks }, class_name: 'Integration'
+ has_many :pipeline_hooks_integrations, -> { pipeline_hooks }, class_name: 'Integration'
+ has_many :push_hooks_integrations, -> { push_hooks }, class_name: 'Integration'
+ has_many :tag_push_hooks_integrations, -> { tag_push_hooks }, class_name: 'Integration'
+ has_many :wiki_page_hooks_integrations, -> { wiki_page_hooks }, class_name: 'Integration'
has_many :snippets, class_name: 'ProjectSnippet'
has_many :hooks, class_name: 'ProjectHook'
has_many :protected_branches
@@ -380,7 +383,7 @@ class Project < ApplicationRecord
has_one :auto_devops, class_name: 'ProjectAutoDevops', inverse_of: :project, autosave: true
has_many :custom_attributes, class_name: 'ProjectCustomAttribute'
- has_many :project_badges, class_name: 'ProjectBadge'
+ has_many :project_badges, class_name: 'ProjectBadge', inverse_of: :project
has_one :ci_cd_settings, class_name: 'ProjectCiCdSetting', inverse_of: :project, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :remote_mirrors, inverse_of: :project
@@ -650,6 +653,11 @@ class Project < ApplicationRecord
.where(repository_languages: { programming_language_id: lang_id_query })
end
+ scope :with_programming_language_id, ->(language_id) do
+ joins(:repository_languages)
+ .where(repository_languages: { programming_language_id: language_id })
+ end
+
scope :service_desk_enabled, -> { where(service_desk_enabled: true) }
scope :with_builds_enabled, -> { with_feature_enabled(:builds) }
scope :with_issues_enabled, -> { with_feature_enabled(:issues) }
@@ -742,6 +750,29 @@ class Project < ApplicationRecord
end
end
+ # Defines instance methods:
+ #
+ # - only_allow_merge_if_pipeline_succeeds?(inherit_group_setting: false)
+ # - allow_merge_on_skipped_pipeline?(inherit_group_setting: false)
+ # - only_allow_merge_if_all_discussions_are_resolved?(inherit_group_setting: false)
+ # - only_allow_merge_if_pipeline_succeeds_locked?
+ # - allow_merge_on_skipped_pipeline_locked?
+ # - only_allow_merge_if_all_discussions_are_resolved_locked?
+ def self.cascading_with_parent_namespace(attribute)
+ # method overriden in EE
+ define_method("#{attribute}?") do |inherit_group_setting: false|
+ self.public_send(attribute) # rubocop:disable GitlabSecurity/PublicSend
+ end
+
+ define_method("#{attribute}_locked?") do
+ false
+ end
+ end
+
+ cascading_with_parent_namespace :only_allow_merge_if_pipeline_succeeds
+ cascading_with_parent_namespace :allow_merge_on_skipped_pipeline
+ cascading_with_parent_namespace :only_allow_merge_if_all_discussions_are_resolved
+
def self.with_feature_available_for_user(feature, user)
with_project_feature.merge(ProjectFeature.with_feature_available_for_user(feature, user))
end
@@ -1691,8 +1722,14 @@ class Project < ApplicationRecord
def execute_integrations(data, hooks_scope = :push_hooks)
# Call only service hooks that are active for this scope
run_after_commit_or_now do
- integrations.public_send(hooks_scope).each do |integration| # rubocop:disable GitlabSecurity/PublicSend
- integration.async_execute(data)
+ if use_integration_relations?
+ association("#{hooks_scope}_integrations").reader.each do |integration|
+ integration.async_execute(data)
+ end
+ else
+ integrations.public_send(hooks_scope).each do |integration| # rubocop:disable GitlabSecurity/PublicSend
+ integration.async_execute(data)
+ end
end
end
end
@@ -2301,6 +2338,7 @@ class Project < ApplicationRecord
.append(key: 'CI_PROJECT_PATH', value: full_path)
.append(key: 'CI_PROJECT_PATH_SLUG', value: full_path_slug)
.append(key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path)
+ .append(key: 'CI_PROJECT_NAMESPACE_ID', value: namespace.id.to_s)
.append(key: 'CI_PROJECT_ROOT_NAMESPACE', value: namespace.root_ancestor.path)
.append(key: 'CI_PROJECT_URL', value: web_url)
.append(key: 'CI_PROJECT_VISIBILITY', value: Gitlab::VisibilityLevel.string_level(visibility_level))
@@ -2700,7 +2738,13 @@ class Project < ApplicationRecord
def access_request_approvers_to_be_notified
access_request_approvers = members.owners_and_maintainers
- access_request_approvers.connected_to_user.order_recent_sign_in.limit(Member::ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT)
+ recipients = access_request_approvers.connected_to_user.order_recent_sign_in.limit(Member::ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT)
+
+ if recipients.blank?
+ recipients = group.access_request_approvers_to_be_notified
+ end
+
+ recipients
end
def pages_lookup_path(trim_prefix: nil, domain: nil)
@@ -2994,6 +3038,10 @@ class Project < ApplicationRecord
group&.work_items_feature_flag_enabled? || Feature.enabled?(:work_items, self)
end
+ def work_items_mvc_feature_flag_enabled?
+ group&.work_items_mvc_feature_flag_enabled? || Feature.enabled?(:work_items_mvc)
+ end
+
def work_items_mvc_2_feature_flag_enabled?
group&.work_items_mvc_2_feature_flag_enabled? || Feature.enabled?(:work_items_mvc_2)
end
@@ -3321,6 +3369,12 @@ class Project < ApplicationRecord
ProjectFeature::PRIVATE
end
end
+
+ def use_integration_relations?
+ strong_memoize(:use_integration_relations) do
+ Feature.enabled?(:cache_project_integrations, self)
+ end
+ end
end
Project.prepend_mod_with('Project')
diff --git a/app/models/project_export_job.rb b/app/models/project_export_job.rb
index decc71ee193..d26ce5465cd 100644
--- a/app/models/project_export_job.rb
+++ b/app/models/project_export_job.rb
@@ -1,11 +1,24 @@
# frozen_string_literal: true
class ProjectExportJob < ApplicationRecord
+ include EachBatch
+
+ EXPIRES_IN = 7.days
+
belongs_to :project
has_many :relation_exports, class_name: 'Projects::ImportExport::RelationExport'
validates :project, :jid, :status, presence: true
+ STATUS = {
+ queued: 0,
+ started: 1,
+ finished: 2,
+ failed: 3
+ }.freeze
+
+ scope :prunable, -> { where("updated_at < ?", EXPIRES_IN.ago) }
+
state_machine :status, initial: :queued do
event :start do
transition [:queued] => :started
@@ -19,9 +32,17 @@ class ProjectExportJob < ApplicationRecord
transition [:queued, :started] => :failed
end
- state :queued, value: 0
- state :started, value: 1
- state :finished, value: 2
- state :failed, value: 3
+ state :queued, value: STATUS[:queued]
+ state :started, value: STATUS[:started]
+ state :finished, value: STATUS[:finished]
+ state :failed, value: STATUS[:failed]
+ end
+
+ class << self
+ def prune_expired_jobs
+ prunable.each_batch do |relation| # rubocop:disable Style/SymbolProc
+ relation.delete_all
+ end
+ end
end
end
diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb
index 0570be85ad1..506f6305791 100644
--- a/app/models/project_statistics.rb
+++ b/app/models/project_statistics.rb
@@ -11,21 +11,21 @@ class ProjectStatistics < ApplicationRecord
attribute :snippets_size, default: 0
counter_attribute :build_artifacts_size
+ counter_attribute :packages_size
- counter_attribute_after_flush do |project_statistic|
- project_statistic.refresh_storage_size!
+ counter_attribute_after_commit do |project_statistics|
+ project_statistics.refresh_storage_size!
- Namespaces::ScheduleAggregationWorker.perform_async(project_statistic.namespace_id)
+ Namespaces::ScheduleAggregationWorker.perform_async(project_statistics.namespace_id)
end
before_save :update_storage_size
COLUMNS_TO_REFRESH = [:repository_size, :wiki_size, :lfs_objects_size, :commit_count, :snippets_size, :uploads_size, :container_registry_size].freeze
- INCREMENTABLE_COLUMNS = {
- packages_size: %i[storage_size],
- pipeline_artifacts_size: %i[storage_size],
- snippets_size: %i[storage_size]
- }.freeze
+ INCREMENTABLE_COLUMNS = [
+ :pipeline_artifacts_size,
+ :snippets_size
+ ].freeze
NAMESPACE_RELATABLE_COLUMNS = [:repository_size, :wiki_size, :lfs_objects_size, :uploads_size, :container_registry_size].freeze
STORAGE_SIZE_COMPONENTS = [
:repository_size,
@@ -120,35 +120,27 @@ class ProjectStatistics < ApplicationRecord
# we have to update the storage_size separately.
#
# For counter attributes, storage_size will be refreshed after the counter is flushed,
- # through counter_attribute_after_flush
+ # through counter_attribute_after_commit
#
# For non-counter attributes, storage_size is updated depending on key => [columns] in INCREMENTABLE_COLUMNS
def self.increment_statistic(project, key, amount)
- raise ArgumentError, "Cannot increment attribute: #{key}" unless incrementable_attribute?(key)
- return if amount == 0
-
project.statistics.try do |project_statistics|
- if counter_attribute_enabled?(key)
- project_statistics.delayed_increment_counter(key, amount)
- else
- project_statistics.legacy_increment_statistic(key, amount)
- end
+ project_statistics.increment_statistic(key, amount)
end
end
- def self.incrementable_attribute?(key)
- INCREMENTABLE_COLUMNS.key?(key) || counter_attribute_enabled?(key)
- end
-
- def legacy_increment_statistic(key, amount)
- increment_columns!(key, amount)
+ def increment_statistic(key, amount)
+ raise ArgumentError, "Cannot increment attribute: #{key}" unless incrementable_attribute?(key)
- Namespaces::ScheduleAggregationWorker.perform_async( # rubocop: disable CodeReuse/Worker
- project.namespace_id)
+ increment_counter(key, amount)
end
private
+ def incrementable_attribute?(key)
+ INCREMENTABLE_COLUMNS.include?(key) || counter_attribute_enabled?(key)
+ end
+
def storage_size_components
STORAGE_SIZE_COMPONENTS
end
@@ -157,16 +149,6 @@ class ProjectStatistics < ApplicationRecord
storage_size_components.map { |component| "COALESCE (#{component}, 0)" }.join(' + ').freeze
end
- def increment_columns!(key, amount)
- increments = { key => amount }
- additional = INCREMENTABLE_COLUMNS.fetch(key, [])
- additional.each do |column|
- increments[column] = amount
- end
-
- update_counters_with_lease(increments)
- end
-
def schedule_namespace_aggregation_worker
run_after_commit do
Namespaces::ScheduleAggregationWorker.perform_async(project.namespace_id)
diff --git a/app/models/projects/forks/divergence_counts.rb b/app/models/projects/forks/divergence_counts.rb
new file mode 100644
index 00000000000..7d630b00083
--- /dev/null
+++ b/app/models/projects/forks/divergence_counts.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+module Projects
+ module Forks
+ # Class for calculating the divergence of a fork with the source project
+ class DivergenceCounts
+ LATEST_COMMITS_COUNT = 10
+ EXPIRATION_TIME = 8.hours
+
+ def initialize(project, ref)
+ @project = project
+ @fork_repo = project.repository
+ @source_repo = project.fork_source.repository
+ @ref = ref
+ end
+
+ def counts
+ ahead, behind = divergence_counts
+
+ { ahead: ahead, behind: behind }
+ end
+
+ private
+
+ attr_reader :project, :fork_repo, :source_repo, :ref
+
+ def cache_key
+ @cache_key ||= ['project_forks', project.id, ref, 'divergence_counts']
+ end
+
+ def divergence_counts
+ fork_sha = fork_repo.commit(ref).sha
+ source_sha = source_repo.commit.sha
+
+ cached_source_sha, cached_fork_sha, counts = Rails.cache.read(cache_key)
+ return counts if cached_source_sha == source_sha && cached_fork_sha == fork_sha
+
+ counts = calculate_divergence_counts(fork_sha, source_sha)
+
+ Rails.cache.write(cache_key, [source_sha, fork_sha, counts], expires_in: EXPIRATION_TIME)
+
+ counts
+ end
+
+ def calculate_divergence_counts(fork_sha, source_sha)
+ # If the upstream latest commit exists in the fork repo, then
+ # it's possible to calculate divergence counts within the fork repository.
+ return fork_repo.diverging_commit_count(fork_sha, source_sha) if fork_repo.commit(source_sha)
+
+ # Otherwise, we need to find a commit that exists both in the fork and upstream
+ # in order to use this commit as a base for calculating divergence counts.
+ # Considering the fact that a user usually creates a fork to contribute to the upstream,
+ # it is expected that they have a limited number of commits ahead of upstream.
+ # Let's take the latest N commits and check their existence upstream.
+ last_commits_shas = fork_repo.commits(ref, limit: LATEST_COMMITS_COUNT).map(&:sha)
+ existence_hash = source_repo.check_objects_exist(last_commits_shas)
+ first_matched_commit_sha = last_commits_shas.find { |sha| existence_hash[sha] }
+
+ # If we can't find such a commit, we return early and tell the user that the branches
+ # have diverged and action is required.
+ return unless first_matched_commit_sha
+
+ # Otherwise, we use upstream to calculate divergence counts from the matched commit
+ ahead, behind = source_repo.diverging_commit_count(first_matched_commit_sha, source_sha)
+ # And add the number of commits a fork is ahead of the first matched commit
+ ahead += last_commits_shas.index(first_matched_commit_sha)
+
+ [ahead, behind]
+ end
+ end
+ end
+end
diff --git a/app/models/projects/import_export/relation_export_upload.rb b/app/models/projects/import_export/relation_export_upload.rb
index 965dc39d19f..12cfb3415d8 100644
--- a/app/models/projects/import_export/relation_export_upload.rb
+++ b/app/models/projects/import_export/relation_export_upload.rb
@@ -4,7 +4,6 @@ module Projects
module ImportExport
class RelationExportUpload < ApplicationRecord
include WithUploads
- include ObjectStorage::BackgroundMove
self.table_name = 'project_relation_export_uploads'
diff --git a/app/models/prometheus_alert.rb b/app/models/prometheus_alert.rb
index 9080f3d9de1..59440947d71 100644
--- a/app/models/prometheus_alert.rb
+++ b/app/models/prometheus_alert.rb
@@ -20,8 +20,8 @@ class PrometheusAlert < ApplicationRecord
has_many :related_issues, through: :prometheus_alert_events
has_many :alert_management_alerts, class_name: 'AlertManagement::Alert', inverse_of: :prometheus_alert
- after_save :clear_prometheus_adapter_cache!
after_destroy :clear_prometheus_adapter_cache!
+ after_save :clear_prometheus_adapter_cache!
validates :environment, :project, :prometheus_metric, :threshold, :operator, presence: true
validates :runbook_url, length: { maximum: 255 }, allow_blank: true,
diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb
index 80967c1b072..c59ef4cd80b 100644
--- a/app/models/protected_branch.rb
+++ b/app/models/protected_branch.rb
@@ -14,10 +14,12 @@ class ProtectedBranch < ApplicationRecord
scope :allowing_force_push,
-> { where(allow_force_push: true) }
- scope :get_ids_by_name, -> (name) { where(name: name).pluck(:id) }
-
protected_ref_access_levels :merge, :push
+ def self.get_ids_by_name(name)
+ where(name: name).pluck(:id)
+ end
+
def self.protected_ref_accessible_to?(ref, user, project:, action:, protected_refs: nil)
# Maintainers, owners and admins are allowed to create the default branch
diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb
index f8d500e106b..b830cf313af 100644
--- a/app/models/remote_mirror.rb
+++ b/app/models/remote_mirror.rb
@@ -20,12 +20,11 @@ class RemoteMirror < ApplicationRecord
belongs_to :project, inverse_of: :remote_mirrors
- validates :url, presence: true, public_url: { schemes: %w(ssh git http https), allow_blank: true, enforce_user: true }
-
- after_save :set_override_remote_mirror_available, unless: -> { Gitlab::CurrentSettings.current_application_settings.mirror_available }
- after_update :reset_fields, if: :saved_change_to_mirror_url?
+ validates :url, presence: true, public_url: { schemes: Project::VALID_MIRROR_PROTOCOLS, allow_blank: true, enforce_user: true }
before_validation :store_credentials
+ after_update :reset_fields, if: :saved_change_to_mirror_url?
+ after_save :set_override_remote_mirror_available, unless: -> { Gitlab::CurrentSettings.current_application_settings.mirror_available }
scope :enabled, -> { where(enabled: true) }
scope :started, -> { with_update_status(:started) }
diff --git a/app/models/resource_label_event.rb b/app/models/resource_label_event.rb
index a1753df9294..a1426540cf5 100644
--- a/app/models/resource_label_event.rb
+++ b/app/models/resource_label_event.rb
@@ -14,8 +14,8 @@ class ResourceLabelEvent < ResourceEvent
validates :label, presence: { unless: :importing? }, on: :create
validate :exactly_one_issuable, unless: :importing?
- after_save :expire_etag_cache
after_destroy :expire_etag_cache
+ after_save :expire_etag_cache
enum action: {
add: 1,
diff --git a/app/models/service_desk_setting.rb b/app/models/service_desk_setting.rb
index 6dd7415d928..738f18ca5e3 100644
--- a/app/models/service_desk_setting.rb
+++ b/app/models/service_desk_setting.rb
@@ -53,7 +53,7 @@ class ServiceDeskSetting < ApplicationRecord
def projects_with_same_slug_and_key_exists?
return false unless project_key
- settings = self.class.with_project_key(project_key).preload(:project)
+ settings = self.class.with_project_key(project_key).where.not(project_id: project_id).preload(:project)
project_slug = self.project.full_path_slug
settings.any? do |setting|
diff --git a/app/models/snippet_statistics.rb b/app/models/snippet_statistics.rb
index 6fb6f0ef713..44bff0e1e5b 100644
--- a/app/models/snippet_statistics.rb
+++ b/app/models/snippet_statistics.rb
@@ -12,8 +12,8 @@ class SnippetStatistics < ApplicationRecord
delegate :repository, :project, :project_id, to: :snippet
- after_save :update_author_root_storage_statistics, if: :update_author_root_storage_statistics?
after_destroy :update_author_root_storage_statistics, unless: :project_snippet?
+ after_save :update_author_root_storage_statistics, if: :update_author_root_storage_statistics?
def update_commit_count
self.commit_count = repository.commit_count
diff --git a/app/models/synthetic_note.rb b/app/models/synthetic_note.rb
index dea7165af9f..a60c0d2f3bc 100644
--- a/app/models/synthetic_note.rb
+++ b/app/models/synthetic_note.rb
@@ -10,6 +10,7 @@ class SyntheticNote < Note
system: true,
author: event.user,
created_at: event.created_at,
+ updated_at: event.created_at,
discussion_id: event.discussion_id,
noteable: resource,
event: event,
diff --git a/app/models/todo.rb b/app/models/todo.rb
index f2fa0df852a..32ec4accb4b 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -19,6 +19,7 @@ class Todo < ApplicationRecord
DIRECTLY_ADDRESSED = 7
MERGE_TRAIN_REMOVED = 8 # This is an EE-only feature
REVIEW_REQUESTED = 9
+ MEMBER_ACCESS_REQUESTED = 10
ACTION_NAMES = {
ASSIGNED => :assigned,
@@ -29,10 +30,11 @@ class Todo < ApplicationRecord
APPROVAL_REQUIRED => :approval_required,
UNMERGEABLE => :unmergeable,
DIRECTLY_ADDRESSED => :directly_addressed,
- MERGE_TRAIN_REMOVED => :merge_train_removed
+ MERGE_TRAIN_REMOVED => :merge_train_removed,
+ MEMBER_ACCESS_REQUESTED => :member_access_requested
}.freeze
- ACTIONS_MULTIPLE_ALLOWED = [Todo::MENTIONED, Todo::DIRECTLY_ADDRESSED].freeze
+ ACTIONS_MULTIPLE_ALLOWED = [Todo::MENTIONED, Todo::DIRECTLY_ADDRESSED, Todo::MEMBER_ACCESS_REQUESTED].freeze
belongs_to :author, class_name: "User"
belongs_to :note
@@ -198,6 +200,16 @@ class Todo < ApplicationRecord
action == MERGE_TRAIN_REMOVED
end
+ def member_access_requested?
+ action == MEMBER_ACCESS_REQUESTED
+ end
+
+ def access_request_url
+ return "" unless self.target_type == 'Namespace'
+
+ Gitlab::Routing.url_helpers.group_group_members_url(self.target, tab: 'access_requests')
+ end
+
def done?
state == 'done'
end
@@ -209,6 +221,8 @@ class Todo < ApplicationRecord
def body
if note.present?
note.note
+ elsif member_access_requested?
+ target.full_path
else
target.title
end
@@ -246,6 +260,8 @@ class Todo < ApplicationRecord
def target_reference
if for_commit?
target.reference_link_text
+ elsif member_access_requested?
+ target.full_path
else
target.to_reference
end
diff --git a/app/models/upload.rb b/app/models/upload.rb
index ac7ebb31abc..a4fbc703146 100644
--- a/app/models/upload.rb
+++ b/app/models/upload.rb
@@ -16,14 +16,13 @@ class Upload < ApplicationRecord
scope :with_files_stored_locally, -> { where(store: ObjectStorage::Store::LOCAL) }
scope :with_files_stored_remotely, -> { where(store: ObjectStorage::Store::REMOTE) }
- before_save :calculate_checksum!, if: :foreground_checksummable?
- after_commit :schedule_checksum, if: :needs_checksum?
-
- after_commit :update_project_statistics, on: [:create, :destroy], if: :project?
-
+ before_save :calculate_checksum!, if: :foreground_checksummable?
# as the FileUploader is not mounted, the default CarrierWave ActiveRecord
# hooks are not executed and the file will not be deleted
after_destroy :delete_file!, if: -> { uploader_class <= FileUploader }
+ after_commit :schedule_checksum, if: :needs_checksum?
+
+ after_commit :update_project_statistics, on: [:create, :destroy], if: :project?
class << self
def inner_join_local_uploads_projects
diff --git a/app/models/user.rb b/app/models/user.rb
index b4b8a7ef7ad..ba3f7922c9c 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -46,6 +46,8 @@ class User < ApplicationRecord
MAX_USERNAME_LENGTH = 255
MIN_USERNAME_LENGTH = 2
+ MAX_LIMIT_FOR_ASSIGNEED_ISSUES_COUNT = 100
+
SECONDARY_EMAIL_ATTRIBUTES = [
:commit_email,
:notification_email,
@@ -58,16 +60,16 @@ class User < ApplicationRecord
add_authentication_token_field :feed_token
add_authentication_token_field :static_object_token, encrypted: :optional
- default_value_for :admin, false
- default_value_for(:external) { Gitlab::CurrentSettings.user_default_external }
- default_value_for(:can_create_group) { Gitlab::CurrentSettings.can_create_group }
- default_value_for :can_create_team, false
- default_value_for :hide_no_ssh_key, false
- default_value_for :hide_no_password, false
- default_value_for :project_view, :files
- default_value_for :notified_of_own_activity, false
- default_value_for :preferred_language, I18n.default_locale
- default_value_for :theme_id, gitlab_config.default_theme
+ attribute :admin, default: false
+ attribute :external, default: -> { Gitlab::CurrentSettings.user_default_external }
+ attribute :can_create_group, default: -> { Gitlab::CurrentSettings.can_create_group }
+ attribute :can_create_team, default: false
+ attribute :hide_no_ssh_key, default: false
+ attribute :hide_no_password, default: false
+ attribute :project_view, default: :files
+ attribute :notified_of_own_activity, default: false
+ attribute :preferred_language, default: -> { I18n.default_locale }
+ attribute :theme_id, default: -> { gitlab_config.default_theme }
attr_encrypted :otp_secret,
key: Gitlab::Application.secrets.otp_key_base,
@@ -298,16 +300,17 @@ class User < ApplicationRecord
validates :website_url, allow_blank: true, url: true, if: :website_url_changed?
+ after_initialize :set_projects_limit
before_validation :sanitize_attrs
+ before_validation :ensure_namespace_correct
+ after_validation :set_username_errors
before_save :default_private_profile_to_false
before_save :ensure_incoming_email_token
before_save :ensure_user_rights_and_limits, if: ->(user) { user.new_record? || user.external_changed? }
before_save :skip_reconfirmation!, if: ->(user) { user.email_changed? && user.read_only_attribute?(:email) }
before_save :check_for_verified_email, if: ->(user) { user.email_changed? && !user.new_record? }
- before_validation :ensure_namespace_correct
before_save :ensure_namespace_correct # in case validation is skipped
before_save :ensure_user_detail_assigned
- after_validation :set_username_errors
after_update :username_changed_hook, if: :saved_change_to_username?
after_destroy :post_destroy_hook
after_destroy :remove_key_cache
@@ -328,8 +331,6 @@ class User < ApplicationRecord
update_invalid_gpg_signatures if previous_changes.key?('email')
end
- after_initialize :set_projects_limit
-
# User's Layout preference
enum layout: { fixed: 0, fluid: 1 }
@@ -360,6 +361,7 @@ class User < ApplicationRecord
:diffs_deletion_color, :diffs_deletion_color=,
:diffs_addition_color, :diffs_addition_color=,
:use_legacy_web_ide, :use_legacy_web_ide=,
+ :use_new_navigation, :use_new_navigation=,
to: :user_preference
delegate :path, to: :namespace, allow_nil: true, prefix: true
@@ -376,6 +378,14 @@ class User < ApplicationRecord
accepts_nested_attributes_for :credit_card_validation, update_only: true, allow_destroy: true
state_machine :state, initial: :active do
+ # state_machine uses this method at class loading time to fetch the default
+ # value for the `state` column but in doing so it also evaluates all other
+ # columns default values which could trigger the recursive generation of
+ # ApplicationSetting records. We're setting it to `nil` here because we
+ # don't have a database default for the `state` column.
+ #
+ def owner_class_attribute_default; end
+
event :block do
transition active: :blocked
transition deactivated: :blocked
@@ -811,7 +821,7 @@ class User < ApplicationRecord
# Returns a user for the given SSH key.
def find_by_ssh_key_id(key_id)
- find_by('EXISTS (?)', Key.select(1).where('keys.user_id = users.id').where(id: key_id))
+ find_by('EXISTS (?)', Key.select(1).where('keys.user_id = users.id').auth.where(id: key_id))
end
def find_by_full_path(path, follow_redirects: false)
@@ -896,6 +906,18 @@ class User < ApplicationRecord
end
end
+ def admin_bot
+ email_pattern = "admin-bot%s@#{Settings.gitlab.host}"
+
+ unique_internal(where(user_type: :admin_bot), 'GitLab-Admin-Bot', email_pattern) do |u|
+ u.bio = 'Admin bot used for tasks that require admin privileges'
+ u.name = 'GitLab Admin Bot'
+ u.avatar = bot_avatar(image: 'admin-bot.png')
+ u.admin = true
+ u.confirmed_at = Time.zone.now
+ end
+ end
+
# Return true if there is only single non-internal user in the deployment,
# ghost user is ignored.
def single_user?
@@ -1759,12 +1781,10 @@ class User < ApplicationRecord
end
def ci_owned_runners
- @ci_owned_runners ||= begin
- Ci::Runner
+ @ci_owned_runners ||= Ci::Runner
.from_union([ci_owned_project_runners_from_project_members,
ci_owned_project_runners_from_group_members,
ci_owned_group_runners])
- end
end
def owns_runner?(runner)
@@ -1773,7 +1793,11 @@ class User < ApplicationRecord
def notification_email_for(notification_group)
# Return group-specific email address if present, otherwise return global notification email address
- notification_group&.notification_email_for(self) || notification_email_or_default
+ group_email = if notification_group && notification_group.respond_to?(:notification_email_for)
+ notification_group.notification_email_for(self)
+ end
+
+ group_email || notification_email_or_default
end
def notification_settings_for(source, inherit: false)
@@ -1866,6 +1890,7 @@ class User < ApplicationRecord
def invalidate_issue_cache_counts
Rails.cache.delete(['users', id, 'assigned_open_issues_count'])
+ Rails.cache.delete(['users', id, 'max_assigned_open_issues_count']) if Feature.enabled?(:limit_assigned_issues_count)
end
def invalidate_merge_request_cache_counts
@@ -2323,9 +2348,7 @@ class User < ApplicationRecord
end
def check_password_weakness
- if Feature.enabled?(:block_weak_passwords) &&
- password.present? &&
- Security::WeakPasswords.weak_for_user?(password, self)
+ if password.present? && Security::WeakPasswords.weak_for_user?(password, self)
errors.add(:password, _('must not contain commonly used combinations of words and letters'))
end
end
diff --git a/app/models/user_detail.rb b/app/models/user_detail.rb
index 2e662faea6a..0570bc2f395 100644
--- a/app/models/user_detail.rb
+++ b/app/models/user_detail.rb
@@ -19,7 +19,7 @@ class UserDetail < ApplicationRecord
validates :skype, length: { maximum: DEFAULT_FIELD_LENGTH }, allow_blank: true
validates :location, length: { maximum: DEFAULT_FIELD_LENGTH }, allow_blank: true
validates :organization, length: { maximum: DEFAULT_FIELD_LENGTH }, allow_blank: true
- validates :website_url, length: { maximum: DEFAULT_FIELD_LENGTH }, url: true, allow_blank: true
+ validates :website_url, length: { maximum: DEFAULT_FIELD_LENGTH }, url: true, allow_blank: true, if: :website_url_changed?
before_validation :sanitize_attrs
before_save :prevent_nil_bio
diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb
index c6ebd550daf..bc2c6b526b8 100644
--- a/app/models/user_preference.rb
+++ b/app/models/user_preference.rb
@@ -26,10 +26,10 @@ class UserPreference < ApplicationRecord
ignore_columns :experience_level, remove_with: '14.10', remove_after: '2021-03-22'
- default_value_for :tab_width, value: Gitlab::TabWidth::DEFAULT, allows_nil: false
- default_value_for :time_display_relative, value: true, allows_nil: false
- default_value_for :time_format_in_24h, value: false, allows_nil: false
- default_value_for :render_whitespace_in_code, value: false, allows_nil: false
+ attribute :tab_width, default: -> { Gitlab::TabWidth::DEFAULT }
+ attribute :time_display_relative, default: true
+ attribute :time_format_in_24h, default: false
+ attribute :render_whitespace_in_code, default: false
class << self
def notes_filters
@@ -59,6 +59,67 @@ class UserPreference < ApplicationRecord
self[notes_filter_field_for(resource)]
end
+ def tab_width
+ read_attribute(:tab_width) || self.class.column_defaults['tab_width']
+ end
+
+ def tab_width=(value)
+ if value.nil?
+ default = self.class.column_defaults['tab_width']
+ super(default)
+ else
+ super(value)
+ end
+ end
+
+ def time_display_relative
+ value = read_attribute(:time_display_relative)
+ return value unless value.nil?
+
+ self.class.column_defaults['time_display_relative']
+ end
+
+ def time_display_relative=(value)
+ if value.nil?
+ default = self.class.column_defaults['time_display_relative']
+ super(default)
+ else
+ super(value)
+ end
+ end
+
+ def time_format_in_24h
+ value = read_attribute(:time_format_in_24h)
+ return value unless value.nil?
+
+ self.class.column_defaults['time_format_in_24h']
+ end
+
+ def time_format_in_24h=(value)
+ if value.nil?
+ default = self.class.column_defaults['time_format_in_24h']
+ super(default)
+ else
+ super(value)
+ end
+ end
+
+ def render_whitespace_in_code
+ value = read_attribute(:render_whitespace_in_code)
+ return value unless value.nil?
+
+ self.class.column_defaults['render_whitespace_in_code']
+ end
+
+ def render_whitespace_in_code=(value)
+ if value.nil?
+ default = self.class.column_defaults['render_whitespace_in_code']
+ super(default)
+ else
+ super(value)
+ end
+ end
+
private
def notes_filter_field_for(resource)
diff --git a/app/models/users/callout.rb b/app/models/users/callout.rb
index b037d07658d..3f9353214ee 100644
--- a/app/models/users/callout.rb
+++ b/app/models/users/callout.rb
@@ -63,7 +63,9 @@ module Users
project_quality_summary_feedback: 59, # EE-only
merge_request_settings_moved_callout: 60,
new_top_level_group_alert: 61,
- artifacts_management_page_feedback_banner: 62
+ artifacts_management_page_feedback_banner: 62,
+ vscode_web_ide: 63,
+ vscode_web_ide_callout: 64
}
validates :feature_name,
diff --git a/app/models/users/group_callout.rb b/app/models/users/group_callout.rb
index 3e3e424e9c9..2552407fa4c 100644
--- a/app/models/users/group_callout.rb
+++ b/app/models/users/group_callout.rb
@@ -23,7 +23,8 @@ module Users
namespace_storage_limit_banner_alert_threshold: 12, # EE-only
namespace_storage_limit_banner_error_threshold: 13, # EE-only
usage_quota_trial_alert: 14, # EE-only
- preview_usage_quota_free_plan_alert: 15 # EE-only
+ preview_usage_quota_free_plan_alert: 15, # EE-only
+ enforcement_at_limit_alert: 16 # EE-only
}
validates :group, presence: true
diff --git a/app/models/users/phone_number_validation.rb b/app/models/users/phone_number_validation.rb
index f6123c01fd0..b9e4e908ddd 100644
--- a/app/models/users/phone_number_validation.rb
+++ b/app/models/users/phone_number_validation.rb
@@ -31,11 +31,17 @@ module Users
validates :telesign_reference_xid,
length: { maximum: 255 }
+ scope :for_user, -> (user_id) { where(user_id: user_id) }
+
def self.related_to_banned_user?(international_dial_code, phone_number)
joins(:banned_user).where(
international_dial_code: international_dial_code,
phone_number: phone_number
).exists?
end
+
+ def validated?
+ validated_at.present?
+ end
end
end
diff --git a/app/models/work_item.rb b/app/models/work_item.rb
index ed6f9d161a6..0810c520f7e 100644
--- a/app/models/work_item.rb
+++ b/app/models/work_item.rb
@@ -38,6 +38,18 @@ class WorkItem < Issue
end
end
+ def ancestors
+ hierarchy.ancestors(hierarchy_order: :asc)
+ end
+
+ def same_type_base_and_ancestors
+ hierarchy(same_type: true).base_and_ancestors(hierarchy_order: :asc)
+ end
+
+ def same_type_descendants_depth
+ hierarchy(same_type: true).max_descendants_depth.to_i
+ end
+
private
override :parent_link_confidentiality
@@ -56,6 +68,13 @@ class WorkItem < Issue
Gitlab::UsageDataCounters::WorkItemActivityUniqueCounter.track_work_item_created_action(author: author)
end
+
+ def hierarchy(options = {})
+ base = self.class.where(id: id)
+ base = base.where(work_item_type_id: work_item_type_id) if options[:same_type]
+
+ ::Gitlab::WorkItems::WorkItemHierarchy.new(base, options: options)
+ end
end
WorkItem.prepend_mod
diff --git a/app/models/work_items/hierarchy_restriction.rb b/app/models/work_items/hierarchy_restriction.rb
new file mode 100644
index 00000000000..a253447a8db
--- /dev/null
+++ b/app/models/work_items/hierarchy_restriction.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module WorkItems
+ class HierarchyRestriction < ApplicationRecord
+ self.table_name = 'work_item_hierarchy_restrictions'
+
+ belongs_to :parent_type, class_name: 'WorkItems::Type'
+ belongs_to :child_type, class_name: 'WorkItems::Type'
+
+ validates :parent_type, presence: true
+ validates :child_type, presence: true
+ validates :child_type, uniqueness: { scope: :parent_type_id }
+ end
+end
diff --git a/app/models/work_items/parent_link.rb b/app/models/work_items/parent_link.rb
index 13d6db3e08e..33857fb08c2 100644
--- a/app/models/work_items/parent_link.rb
+++ b/app/models/work_items/parent_link.rb
@@ -12,12 +12,14 @@ module WorkItems
validates :work_item_parent, presence: true
validates :work_item, presence: true, uniqueness: true
- validate :validate_child_type
- validate :validate_parent_type
+ validate :validate_hierarchy_restrictions
+ validate :validate_cyclic_reference
validate :validate_same_project
validate :validate_max_children
validate :validate_confidentiality
+ scope :for_parents, ->(parent_ids) { where(work_item_parent_id: parent_ids) }
+
class << self
def has_public_children?(parent_id)
joins(:work_item).where(work_item_parent_id: parent_id, 'issues.confidential': false).exists?
@@ -33,27 +35,6 @@ module WorkItems
private
- def validate_child_type
- return unless work_item
-
- unless work_item.task?
- errors.add :work_item, _('only Task can be assigned as a child in hierarchy.')
- end
- end
-
- def validate_parent_type
- return unless work_item_parent
-
- base_type = work_item_parent.work_item_type.base_type.to_sym
- unless PARENT_TYPES.include?(base_type)
- parent_names = WorkItems::Type::BASE_TYPES.slice(*WorkItems::ParentLink::PARENT_TYPES)
- .values.map { |type| type[:name] }
-
- errors.add :work_item_parent, _('only %{parent_types} can be parent of Task.') %
- { parent_types: parent_names.to_sentence }
- end
- end
-
def validate_same_project
return if work_item.nil? || work_item_parent.nil?
@@ -79,5 +60,40 @@ module WorkItems
"parent. Make the work item confidential and try again.")
end
end
+
+ def validate_hierarchy_restrictions
+ return unless work_item && work_item_parent
+
+ restriction = ::WorkItems::HierarchyRestriction
+ .find_by_parent_type_id_and_child_type_id(work_item_parent.work_item_type_id, work_item.work_item_type_id)
+
+ if restriction.nil?
+ errors.add :work_item, _('is not allowed to add this type of parent')
+ return
+ end
+
+ validate_depth(restriction.maximum_depth)
+ end
+
+ def validate_depth(depth)
+ return unless depth
+ return if work_item.work_item_type_id != work_item_parent.work_item_type_id
+
+ if work_item_parent.same_type_base_and_ancestors.count + work_item.same_type_descendants_depth > depth
+ errors.add :work_item, _('reached maximum depth')
+ end
+ end
+
+ def validate_cyclic_reference
+ return unless work_item_parent&.id && work_item&.id
+
+ if work_item.id == work_item_parent.id
+ errors.add :work_item, _('is not allowed to point to itself')
+ end
+
+ if work_item_parent.ancestors.detect { |ancestor| work_item.id == ancestor.id }
+ errors.add :work_item, _('is already present in ancestors')
+ end
+ end
end
end
diff --git a/app/models/work_items/type.rb b/app/models/work_items/type.rb
index dc30899d24f..e1f6a13f7a7 100644
--- a/app/models/work_items/type.rb
+++ b/app/models/work_items/type.rb
@@ -10,31 +10,86 @@ module WorkItems
include CacheMarkdownField
+ # type name is used in restrictions DB seeder to assure restrictions for
+ # default types are pre-filled
+ TYPE_NAMES = {
+ issue: 'Issue',
+ incident: 'Incident',
+ test_case: 'Test Case',
+ requirement: 'Requirement',
+ task: 'Task',
+ objective: 'Objective',
+ key_result: 'Key Result'
+ }.freeze
+
# Base types need to exist on the DB on app startup
# This constant is used by the DB seeder
# TODO - where to add new icon names created?
BASE_TYPES = {
- issue: { name: 'Issue', icon_name: 'issue-type-issue', enum_value: 0 },
- incident: { name: 'Incident', icon_name: 'issue-type-incident', enum_value: 1 },
- test_case: { name: 'Test Case', icon_name: 'issue-type-test-case', enum_value: 2 }, ## EE-only
- requirement: { name: 'Requirement', icon_name: 'issue-type-requirements', enum_value: 3 }, ## EE-only
- task: { name: 'Task', icon_name: 'issue-type-task', enum_value: 4 },
- objective: { name: 'Objective', icon_name: 'issue-type-objective', enum_value: 5 }, ## EE-only
- key_result: { name: 'Key Result', icon_name: 'issue-type-keyresult', enum_value: 6 } ## EE-only
+ issue: { name: TYPE_NAMES[:issue], icon_name: 'issue-type-issue', enum_value: 0 },
+ incident: { name: TYPE_NAMES[:incident], icon_name: 'issue-type-incident', enum_value: 1 },
+ test_case: { name: TYPE_NAMES[:test_case], icon_name: 'issue-type-test-case', enum_value: 2 }, ## EE-only
+ requirement: { name: TYPE_NAMES[:requirement], icon_name: 'issue-type-requirements', enum_value: 3 }, ## EE-only
+ task: { name: TYPE_NAMES[:task], icon_name: 'issue-type-task', enum_value: 4 },
+ objective: { name: TYPE_NAMES[:objective], icon_name: 'issue-type-objective', enum_value: 5 }, ## EE-only
+ key_result: { name: TYPE_NAMES[:key_result], icon_name: 'issue-type-keyresult', enum_value: 6 } ## EE-only
}.freeze
WIDGETS_FOR_TYPE = {
- issue: [Widgets::Assignees, Widgets::Labels, Widgets::Description, Widgets::Hierarchy, Widgets::StartAndDueDate,
- Widgets::Milestone],
- incident: [Widgets::Description, Widgets::Hierarchy],
- test_case: [Widgets::Description],
- requirement: [Widgets::Description],
- task: [Widgets::Assignees, Widgets::Labels, Widgets::Description, Widgets::Hierarchy, Widgets::StartAndDueDate,
- Widgets::Milestone],
- objective: [Widgets::Assignees, Widgets::Labels, Widgets::Description, Widgets::Hierarchy, Widgets::Milestone],
- key_result: [Widgets::Assignees, Widgets::Labels, Widgets::Description, Widgets::StartAndDueDate]
+ issue: [
+ Widgets::Assignees,
+ Widgets::Labels,
+ Widgets::Description,
+ Widgets::Hierarchy,
+ Widgets::StartAndDueDate,
+ Widgets::Milestone,
+ Widgets::Notes
+ ],
+ incident: [
+ Widgets::Description,
+ Widgets::Hierarchy,
+ Widgets::Notes
+ ],
+ test_case: [
+ Widgets::Description,
+ Widgets::Notes
+ ],
+ requirement: [
+ Widgets::Description,
+ Widgets::Notes
+ ],
+ task: [
+ Widgets::Assignees,
+ Widgets::Labels,
+ Widgets::Description,
+ Widgets::Hierarchy,
+ Widgets::StartAndDueDate,
+ Widgets::Milestone,
+ Widgets::Notes
+ ],
+ objective: [
+ Widgets::Assignees,
+ Widgets::Labels,
+ Widgets::Description,
+ Widgets::Hierarchy,
+ Widgets::Milestone,
+ Widgets::Notes
+ ],
+ key_result: [
+ Widgets::Assignees,
+ Widgets::Labels,
+ Widgets::Description,
+ Widgets::Hierarchy,
+ Widgets::StartAndDueDate,
+ Widgets::Notes
+ ]
}.freeze
+ # A list of types user can change between - both original and new
+ # type must be included in this list. This is needed for legacy issues
+ # where it's possible to switch between issue and incident.
+ CHANGEABLE_BASE_TYPES = %w[issue incident test_case].freeze
+
WI_TYPES_WITH_CREATED_HEADER = %w[issue incident].freeze
cache_markdown_field :description, pipeline: :single_line
@@ -66,6 +121,7 @@ module WorkItems
return found_type if found_type
Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.upsert_types
+ Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
find_by(namespace_id: nil, base_type: type)
end
diff --git a/app/models/work_items/widgets/notes.rb b/app/models/work_items/widgets/notes.rb
new file mode 100644
index 00000000000..bde94ea8f43
--- /dev/null
+++ b/app/models/work_items/widgets/notes.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module WorkItems
+ module Widgets
+ class Notes < Base
+ delegate :notes, to: :work_item
+ delegate_missing_to :work_item
+
+ def declarative_policy_delegate
+ work_item
+ end
+ end
+ end
+end
diff --git a/app/policies/base_policy.rb b/app/policies/base_policy.rb
index f8e7a912896..1ce866bd910 100644
--- a/app/policies/base_policy.rb
+++ b/app/policies/base_policy.rb
@@ -19,6 +19,14 @@ class BasePolicy < DeclarativePolicy::Base
with_options scope: :user, score: 0
condition(:deactivated) { @user&.deactivated? }
+ desc "User is bot"
+ with_options scope: :user, score: 0
+ condition(:bot) { @user&.bot? }
+
+ desc "User is alert bot"
+ with_options scope: :user, score: 0
+ condition(:alert_bot) { @user&.alert_bot? }
+
desc "User is support bot"
with_options scope: :user, score: 0
condition(:support_bot) { @user&.support_bot? }
@@ -50,9 +58,6 @@ class BasePolicy < DeclarativePolicy::Base
::Gitlab::ExternalAuthorization.perform_check?
end
- with_options scope: :user, score: 0
- condition(:alert_bot) { @user&.alert_bot? }
-
rule { external_authorization_enabled & ~can?(:read_all_resources) }.policy do
prevent :read_cross_project
end
diff --git a/app/policies/ci/freeze_period_policy.rb b/app/policies/ci/freeze_period_policy.rb
index 60e53a7b2f9..9e2cca5e5a2 100644
--- a/app/policies/ci/freeze_period_policy.rb
+++ b/app/policies/ci/freeze_period_policy.rb
@@ -2,6 +2,6 @@
module Ci
class FreezePeriodPolicy < BasePolicy
- delegate { @subject.resource_parent }
+ delegate { @subject.project }
end
end
diff --git a/app/policies/ci/pipeline_schedule_variable_policy.rb b/app/policies/ci/pipeline_schedule_variable_policy.rb
new file mode 100644
index 00000000000..dbbf9221e77
--- /dev/null
+++ b/app/policies/ci/pipeline_schedule_variable_policy.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module Ci
+ class PipelineScheduleVariablePolicy < BasePolicy
+ delegate :pipeline_schedule
+ end
+end
diff --git a/app/policies/commit_signatures/ssh_signature_policy.rb b/app/policies/commit_signatures/ssh_signature_policy.rb
new file mode 100644
index 00000000000..34c8f123029
--- /dev/null
+++ b/app/policies/commit_signatures/ssh_signature_policy.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module CommitSignatures
+ class SshSignaturePolicy < BasePolicy
+ delegate { @subject.project }
+ end
+end
diff --git a/app/policies/concerns/archived_abilities.rb b/app/policies/concerns/archived_abilities.rb
new file mode 100644
index 00000000000..b4dfad599c7
--- /dev/null
+++ b/app/policies/concerns/archived_abilities.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module ArchivedAbilities
+ extend ActiveSupport::Concern
+
+ ARCHIVED_ABILITIES = %i[
+ admin_tag
+ push_code
+ push_to_delete_protected_branch
+ request_access
+ upload_file
+ resolve_note
+ create_merge_request_from
+ create_merge_request_in
+ award_emoji
+ create_incident
+ ].freeze
+
+ ARCHIVED_FEATURES = %i[
+ issue
+ issue_board_list
+ merge_request
+ label
+ milestone
+ snippet
+ wiki
+ design
+ note
+ pipeline
+ pipeline_schedule
+ build
+ trigger
+ environment
+ deployment
+ commit_status
+ container_image
+ pages
+ cluster
+ release
+ ].freeze
+
+ class_methods do
+ def archived_abilities
+ ARCHIVED_ABILITIES
+ end
+
+ def archived_features
+ ARCHIVED_FEATURES
+ end
+ end
+end
+
+ArchivedAbilities::ClassMethods.prepend_mod_with('ArchivedAbilities::ClassMethods')
diff --git a/app/policies/concerns/readonly_abilities.rb b/app/policies/concerns/readonly_abilities.rb
deleted file mode 100644
index 300f17088b7..00000000000
--- a/app/policies/concerns/readonly_abilities.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-# frozen_string_literal: true
-
-module ReadonlyAbilities
- extend ActiveSupport::Concern
-
- READONLY_ABILITIES = %i[
- admin_tag
- push_code
- push_to_delete_protected_branch
- request_access
- upload_file
- resolve_note
- create_merge_request_from
- create_merge_request_in
- award_emoji
- create_incident
- ].freeze
-
- READONLY_FEATURES = %i[
- issue
- issue_board_list
- merge_request
- label
- milestone
- snippet
- wiki
- design
- note
- pipeline
- pipeline_schedule
- build
- trigger
- environment
- deployment
- commit_status
- container_image
- pages
- cluster
- release
- ].freeze
-
- class_methods do
- def readonly_abilities
- READONLY_ABILITIES
- end
-
- def readonly_features
- READONLY_FEATURES
- end
- end
-end
-
-ReadonlyAbilities::ClassMethods.prepend_mod_with('ReadonlyAbilities::ClassMethods')
diff --git a/app/policies/group_member_policy.rb b/app/policies/group_member_policy.rb
index f61f758a8e8..78ab9fc750b 100644
--- a/app/policies/group_member_policy.rb
+++ b/app/policies/group_member_policy.rb
@@ -6,7 +6,7 @@ class GroupMemberPolicy < BasePolicy
delegate :group
with_scope :subject
- condition(:last_owner) { @subject.group.member_last_owner?(@subject) || @subject.group.member_last_blocked_owner?(@subject) }
+ condition(:last_owner) { @subject.last_owner_of_the_group? }
condition(:project_bot) { @subject.user&.project_bot? && @subject.group.member?(@subject.user) }
desc "Membership is users' own"
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index 806c57bab74..858c145de3f 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -83,8 +83,8 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
with_scope :subject
condition(:crm_enabled, score: 0, scope: :subject) { @subject.crm_enabled? }
- condition(:group_runner_registration_allowed, scope: :global) do
- Gitlab::CurrentSettings.valid_runner_registrars.include?('group')
+ condition(:group_runner_registration_allowed, scope: :subject) do
+ Gitlab::CurrentSettings.valid_runner_registrars.include?('group') && @subject.runner_registration_enabled?
end
rule { can?(:read_group) & design_management_enabled }.policy do
@@ -193,6 +193,7 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
enable :admin_group_member
enable :change_visibility_level
+ enable :read_usage_quotas
enable :read_group_runners
enable :admin_group_runners
enable :register_group_runners
diff --git a/app/policies/issue_policy.rb b/app/policies/issue_policy.rb
index 87db228a698..491eebe9daf 100644
--- a/app/policies/issue_policy.rb
+++ b/app/policies/issue_policy.rb
@@ -9,7 +9,7 @@ class IssuePolicy < IssuablePolicy
desc "User can read confidential issues"
condition(:can_read_confidential) do
- @user && IssueCollection.new([@subject]).visible_to(@user).any?
+ @user && (@user.admin? || can?(:reporter_access) || assignee_or_author?) # rubocop:disable Cop/UserAdmin
end
desc "Project belongs to a group, crm is enabled and user can read contacts in the root group"
@@ -27,6 +27,23 @@ class IssuePolicy < IssuablePolicy
desc "Issue is persisted"
condition(:persisted, scope: :subject) { @subject.persisted? }
+ # accessing notes requires the notes widget to be available for work items(or issue)
+ condition(:notes_widget_enabled, scope: :subject) do
+ @subject.work_item_type.widgets.include?(::WorkItems::Widgets::Notes)
+ end
+
+ rule { ~notes_widget_enabled }.policy do
+ prevent :create_note
+ prevent :read_note
+ prevent :read_internal_note
+ prevent :set_note_created_at
+ prevent :mark_note_as_confidential
+ # these actions on notes are not available on issues/work items yet,
+ # but preventing any action on work item notes as long as there is no notes widget seems reasonable
+ prevent :resolve_note
+ prevent :reposition_note
+ end
+
rule { confidential & ~can_read_confidential }.policy do
prevent(*create_read_update_admin_destroy(:issue))
prevent :read_issue_iid
diff --git a/app/policies/merge_request_policy.rb b/app/policies/merge_request_policy.rb
index bda327cb661..1759cf057e4 100644
--- a/app/policies/merge_request_policy.rb
+++ b/app/policies/merge_request_policy.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class MergeRequestPolicy < IssuablePolicy
+ condition(:can_approve) { can_approve? }
+
rule { locked }.policy do
prevent :reopen_merge_request
end
@@ -14,10 +16,14 @@ class MergeRequestPolicy < IssuablePolicy
prevent :accept_merge_request
end
- rule { can?(:update_merge_request) & is_project_member }.policy do
+ rule { can_approve }.policy do
enable :approve_merge_request
end
+ rule { can?(:approve_merge_request) & bot }.policy do
+ enable :reset_merge_request_approvals
+ end
+
rule { ~anonymous & can?(:read_merge_request) }.policy do
enable :create_todo
enable :update_subscription
@@ -32,6 +38,12 @@ class MergeRequestPolicy < IssuablePolicy
rule { can?(:admin_merge_request) }.policy do
enable :set_merge_request_metadata
end
+
+ private
+
+ def can_approve?
+ can?(:update_merge_request) && is_project_member?
+ end
end
MergeRequestPolicy.prepend_mod_with('MergeRequestPolicy')
diff --git a/app/policies/namespaces/user_namespace_policy.rb b/app/policies/namespaces/user_namespace_policy.rb
index 89158578ac1..1deeae8241f 100644
--- a/app/policies/namespaces/user_namespace_policy.rb
+++ b/app/policies/namespaces/user_namespace_policy.rb
@@ -5,6 +5,7 @@ module Namespaces
rule { anonymous }.prevent_all
condition(:can_create_personal_project, scope: :user) { @user.can_create_project? }
+ condition(:bot_user_namespace) { @subject.bot_user_namespace? }
condition(:owner) { @subject.owner == @user }
rule { owner | admin }.policy do
@@ -21,6 +22,8 @@ module Namespaces
rule { ~can_create_personal_project }.prevent :create_projects
+ rule { bot_user_namespace }.prevent :create_projects
+
rule { (owner | admin) & can?(:create_projects) }.enable :transfer_projects
end
end
diff --git a/app/policies/note_policy.rb b/app/policies/note_policy.rb
index 67b57595beb..9fd95bbe42d 100644
--- a/app/policies/note_policy.rb
+++ b/app/policies/note_policy.rb
@@ -20,12 +20,20 @@ class NotePolicy < BasePolicy
condition(:confidential, scope: :subject) { @subject.confidential? }
+ # if noteable is a work item it needs to check the notes widget availability
+ condition(:notes_widget_enabled, scope: :subject) do
+ !@subject.noteable.respond_to?(:work_item_type) ||
+ @subject.noteable.work_item_type.widgets.include?(::WorkItems::Widgets::Notes)
+ end
+
# Should be matched with IssuablePolicy#read_internal_note
# and EpicPolicy#read_internal_note
condition(:can_read_confidential) do
access_level >= Gitlab::Access::REPORTER || admin?
end
+ rule { ~notes_widget_enabled }.prevent_all
+
rule { ~editable }.prevent :admin_note
# If user can't read the issue/MR/etc then they should not be allowed to do anything to their own notes
diff --git a/app/policies/project_member_policy.rb b/app/policies/project_member_policy.rb
index bcfc7c87d41..ace74dca448 100644
--- a/app/policies/project_member_policy.rb
+++ b/app/policies/project_member_policy.rb
@@ -5,7 +5,7 @@ class ProjectMemberPolicy < BasePolicy
delegate { @subject.project }
condition(:target_is_holder_of_the_personal_namespace, scope: :subject) do
- @subject.project.personal_namespace_holder?(@subject.user)
+ @subject.holder_of_the_personal_namespace?
end
desc "Membership is users' own access request"
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index bfeb1a602ab..7f67e80e432 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -2,7 +2,7 @@
class ProjectPolicy < BasePolicy
include CrudPolicyHelpers
- include ReadonlyAbilities
+ include ArchivedAbilities
desc "Project has public builds enabled"
condition(:public_builds, scope: :subject, score: 0) { project.public_builds? }
@@ -121,7 +121,7 @@ class ProjectPolicy < BasePolicy
desc "If user is authenticated via CI job token then the target project should be in scope"
condition(:project_allowed_for_job_token) do
- !@user&.from_ci_job_token? || @user.ci_job_token_scope.includes?(project)
+ !@user&.from_ci_job_token? || @user.ci_job_token_scope.allows?(project)
end
with_scope :subject
@@ -369,29 +369,12 @@ class ProjectPolicy < BasePolicy
prevent(:metrics_dashboard)
end
- condition(:split_operations_visibility_permissions) do
- ::Feature.enabled?(:split_operations_visibility_permissions, @subject)
- end
-
- rule { ~split_operations_visibility_permissions & operations_disabled }.policy do
- prevent(*create_read_update_admin_destroy(:feature_flag))
- prevent(*create_read_update_admin_destroy(:environment))
- prevent(*create_read_update_admin_destroy(:sentry_issue))
- prevent(*create_read_update_admin_destroy(:alert_management_alert))
- prevent(*create_read_update_admin_destroy(:cluster))
- prevent(*create_read_update_admin_destroy(:terraform_state))
- prevent(*create_read_update_admin_destroy(:deployment))
- prevent(:metrics_dashboard)
- prevent(:read_pod_logs)
- prevent(:read_prometheus)
- end
-
- rule { split_operations_visibility_permissions & environments_disabled }.policy do
+ rule { environments_disabled }.policy do
prevent(*create_read_update_admin_destroy(:environment))
prevent(*create_read_update_admin_destroy(:deployment))
end
- rule { split_operations_visibility_permissions & feature_flags_disabled }.policy do
+ rule { feature_flags_disabled }.policy do
prevent(*create_read_update_admin_destroy(:feature_flag))
prevent(:admin_feature_flags_user_lists)
prevent(:admin_feature_flags_client)
@@ -401,13 +384,13 @@ class ProjectPolicy < BasePolicy
prevent(*create_read_update_admin_destroy(:release))
end
- rule { split_operations_visibility_permissions & monitor_disabled }.policy do
+ rule { monitor_disabled }.policy do
prevent(:metrics_dashboard)
prevent(*create_read_update_admin_destroy(:sentry_issue))
prevent(*create_read_update_admin_destroy(:alert_management_alert))
end
- rule { split_operations_visibility_permissions & infrastructure_disabled }.policy do
+ rule { infrastructure_disabled }.policy do
prevent(*create_read_update_admin_destroy(:terraform_state))
prevent(*create_read_update_admin_destroy(:cluster))
prevent(:read_pod_logs)
@@ -552,15 +535,15 @@ class ProjectPolicy < BasePolicy
rule { can?(:push_code) }.enable :admin_tag
rule { archived }.policy do
- prevent(*readonly_abilities)
+ prevent(*archived_abilities)
- readonly_features.each do |feature|
+ archived_features.each do |feature|
prevent(*create_update_admin(feature))
end
end
rule { archived & ~pending_delete }.policy do
- readonly_features.each do |feature|
+ archived_features.each do |feature|
prevent(:"destroy_#{feature}")
end
end
diff --git a/app/presenters/blob_presenter.rb b/app/presenters/blob_presenter.rb
index 92dcfeed104..f25436c54be 100644
--- a/app/presenters/blob_presenter.rb
+++ b/app/presenters/blob_presenter.rb
@@ -98,7 +98,7 @@ class BlobPresenter < Gitlab::View::Presenter::Delegated
end
def permalink_path
- url_helpers.project_blob_path(project, File.join(project.repository.commit.sha, blob.path))
+ url_helpers.project_blob_path(project, File.join(project.repository.commit(blob.commit_id).sha, blob.path))
end
def environment_formatted_external_url
diff --git a/app/presenters/ci/freeze_period_presenter.rb b/app/presenters/ci/freeze_period_presenter.rb
new file mode 100644
index 00000000000..064197f34dd
--- /dev/null
+++ b/app/presenters/ci/freeze_period_presenter.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Ci
+ class FreezePeriodPresenter < Gitlab::View::Presenter::Delegated
+ presents ::Ci::FreezePeriod, as: :freeze_period
+
+ def start_time
+ return freeze_period.time_start if freeze_period.active?
+
+ freeze_period.next_time_start
+ end
+ end
+end
diff --git a/app/presenters/group_member_presenter.rb b/app/presenters/group_member_presenter.rb
index 88facc3608d..18554df4bd9 100644
--- a/app/presenters/group_member_presenter.rb
+++ b/app/presenters/group_member_presenter.rb
@@ -3,6 +3,10 @@
class GroupMemberPresenter < MemberPresenter
presents ::GroupMember
+ def last_owner?
+ member.last_owner_of_the_group?
+ end
+
private
def admin_member_permission
diff --git a/app/presenters/member_presenter.rb b/app/presenters/member_presenter.rb
index 67d044dd01c..4cdaca3c39e 100644
--- a/app/presenters/member_presenter.rb
+++ b/app/presenters/member_presenter.rb
@@ -37,6 +37,10 @@ class MemberPresenter < Gitlab::View::Presenter::Delegated
false
end
+ def last_owner?
+ raise NotImplementedError
+ end
+
private
def admin_member_permission
diff --git a/app/presenters/packages/pypi/simple_package_versions_presenter.rb b/app/presenters/packages/pypi/simple_package_versions_presenter.rb
index 0baa0714463..2bccaf4db72 100644
--- a/app/presenters/packages/pypi/simple_package_versions_presenter.rb
+++ b/app/presenters/packages/pypi/simple_package_versions_presenter.rb
@@ -13,7 +13,10 @@ module Packages
def links
refs = []
- available_packages.each_batch do |batch|
+ available_packages.each_batch do |relation|
+ batch = relation.preload_files
+ .preload_pypi_metadatum
+
batch.each do |package|
package_files = package.installable_package_files
diff --git a/app/presenters/project_member_presenter.rb b/app/presenters/project_member_presenter.rb
index da24972775a..bb389b7a3ab 100644
--- a/app/presenters/project_member_presenter.rb
+++ b/app/presenters/project_member_presenter.rb
@@ -21,6 +21,12 @@ class ProjectMemberPresenter < MemberPresenter
super
end
+ def last_owner?
+ # all owners of a project in a group are removable.
+ # but in personal projects, the namespace holder is not removable.
+ member.holder_of_the_personal_namespace?
+ end
+
private
def admin_member_permission
diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb
index 0be13197343..4d1a9b3f589 100644
--- a/app/presenters/project_presenter.rb
+++ b/app/presenters/project_presenter.rb
@@ -68,7 +68,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
user_view = current_user.project_view
- if can?(current_user, :download_code, project)
+ if can?(current_user, :read_code, project)
user_view
elsif user_view == 'activity'
'activity'
@@ -179,7 +179,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
return if releases_count < 1
AnchorData.new(true,
- statistic_icon('rocket') +
+ statistic_icon('deployments') +
n_('%{strong_start}%{release_count}%{strong_end} Release', '%{strong_start}%{release_count}%{strong_end} Releases', releases_count).html_safe % {
release_count: number_with_delimiter(releases_count),
strong_start: '<strong class="project-stat-value">'.html_safe,
@@ -290,16 +290,10 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
'btn-default',
nil,
'license')
- else
- if can_current_user_push_to_default_branch?
- AnchorData.new(false,
+ elsif can_current_user_push_to_default_branch?
+ AnchorData.new(false,
content_tag(:span, statistic_icon + _('Add LICENSE'), class: 'add-license-link d-flex'),
empty_repo? ? add_license_ide_path : add_license_path)
- else
- AnchorData.new(false,
- icon + content_tag(:span, _('No license. All rights reserved'), class: 'project-stat-value'),
- nil)
- end
end
end
@@ -423,7 +417,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end
def anonymous_project_view
- if !project.empty_repo? && can?(current_user, :download_code, project)
+ if !project.empty_repo? && can?(current_user, :read_code, project)
'files'
elsif project.wiki_repository_exists? && can?(current_user, :read_wiki, project)
'wiki'
diff --git a/app/presenters/search_service_presenter.rb b/app/presenters/search_service_presenter.rb
index 4755b88cbea..d7d959217b0 100644
--- a/app/presenters/search_service_presenter.rb
+++ b/app/presenters/search_service_presenter.rb
@@ -25,7 +25,7 @@ class SearchServicePresenter < Gitlab::View::Presenter::Delegated
case scope
when 'users'
- objects.eager_load(:status) if objects.respond_to?(:eager_load) # rubocop:disable CodeReuse/ActiveRecord
+ objects.respond_to?(:eager_load) ? objects.eager_load(:status) : objects # rubocop:disable CodeReuse/ActiveRecord
when 'commits'
prepare_commits_for_rendering(objects)
else
@@ -45,4 +45,10 @@ class SearchServicePresenter < Gitlab::View::Presenter::Delegated
def without_count?
search_objects.is_a?(Kaminari::PaginatableWithoutCount)
end
+
+ def advanced_search_enabled?
+ false
+ end
end
+
+SearchServicePresenter.prepend_mod_with('SearchServicePresenter')
diff --git a/app/serializers/analytics/cycle_analytics/configuration_entity.rb b/app/serializers/analytics/cycle_analytics/configuration_entity.rb
index 45ea7c92758..6a9ec3f5e9e 100644
--- a/app/serializers/analytics/cycle_analytics/configuration_entity.rb
+++ b/app/serializers/analytics/cycle_analytics/configuration_entity.rb
@@ -11,11 +11,7 @@ module Analytics
private
def events
- (stage_events.events - stage_events.internal_events).sort_by(&:name)
- end
-
- def stage_events
- Gitlab::Analytics::CycleAnalytics::StageEvents
+ Gitlab::Analytics::CycleAnalytics::StageEvents.selectable_events
end
end
end
diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb
index dc7b5e95361..1caa9720c08 100644
--- a/app/serializers/build_details_entity.rb
+++ b/app/serializers/build_details_entity.rb
@@ -11,7 +11,7 @@ class BuildDetailsEntity < Ci::JobEntity
expose :metadata, using: BuildMetadataEntity
expose :pipeline, using: Ci::PipelineEntity
- expose :deployment_status, if: -> (*) { build.starts_environment? } do
+ expose :deployment_status, if: -> (*) { build.deployment_job? } do
expose :deployment_status, as: :status
expose :persisted_environment, as: :environment do |build, options|
options.merge(deployment_details: false).yield_self do |opts|
diff --git a/app/serializers/ci/basic_variable_entity.rb b/app/serializers/ci/basic_variable_entity.rb
index dad59e8735b..210c01408a6 100644
--- a/app/serializers/ci/basic_variable_entity.rb
+++ b/app/serializers/ci/basic_variable_entity.rb
@@ -9,5 +9,6 @@ module Ci
expose :protected?, as: :protected
expose :masked?, as: :masked
+ expose :raw?, as: :raw
end
end
diff --git a/app/serializers/issuable_sidebar_basic_entity.rb b/app/serializers/issuable_sidebar_basic_entity.rb
index b66aad6cc65..9039606a8e5 100644
--- a/app/serializers/issuable_sidebar_basic_entity.rb
+++ b/app/serializers/issuable_sidebar_basic_entity.rb
@@ -38,6 +38,10 @@ class IssuableSidebarBasicEntity < Grape::Entity
expose :can_admin_label do |issuable|
can?(current_user, :admin_label, issuable.project)
end
+
+ expose :can_create_timelogs do |issuable|
+ can?(current_user, :create_timelog, issuable)
+ end
end
expose :issuable_json_path do |issuable|
diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb
index 3d94d2e2e9d..397f333008c 100644
--- a/app/serializers/issue_entity.rb
+++ b/app/serializers/issue_entity.rb
@@ -7,11 +7,15 @@ class IssueEntity < IssuableEntity
item.try(:upcase)
end
+ format_with(:iso8601) do |item|
+ item.try(:iso8601)
+ end
+
expose :state
expose :milestone_id
expose :updated_by_id
- expose :created_at
- expose :updated_at
+ expose :created_at, format_with: :iso8601
+ expose :updated_at, format_with: :iso8601
expose :milestone, using: API::Entities::Milestone
expose :labels, using: LabelEntity
expose :lock_version
@@ -85,6 +89,11 @@ class IssueEntity < IssuableEntity
end
expose :issue_email_participants do |issue|
+ # TODO - This is a Temporary solution to avoid leaking participants' emails
+ # on public/internal projects when issue is not confidential.
+ # Should be removed when https://gitlab.com/gitlab-org/gitlab/-/issues/383448 is implemented.
+ next [] unless issue.confidential?
+
issue.issue_email_participants.map { |x| { email: x.email } }
end
diff --git a/app/serializers/member_entity.rb b/app/serializers/member_entity.rb
index bfb5b3eeae6..8e5d352e413 100644
--- a/app/serializers/member_entity.rb
+++ b/app/serializers/member_entity.rb
@@ -23,6 +23,8 @@ class MemberEntity < Grape::Entity
member.can_remove?
end
+ expose :last_owner?, as: :is_last_owner
+
expose :is_direct_member do |member, options|
member.source == options[:source]
end
diff --git a/app/serializers/merge_request_metrics_entity.rb b/app/serializers/merge_request_metrics_entity.rb
index 1c9db08d103..ded82a9ef45 100644
--- a/app/serializers/merge_request_metrics_entity.rb
+++ b/app/serializers/merge_request_metrics_entity.rb
@@ -1,8 +1,12 @@
# frozen_string_literal: true
class MergeRequestMetricsEntity < Grape::Entity
- expose :latest_closed_at, as: :closed_at
- expose :merged_at
+ format_with(:iso8601) do |item|
+ item.try(:iso8601)
+ end
+
+ expose :latest_closed_at, as: :closed_at, format_with: :iso8601
+ expose :merged_at, format_with: :iso8601
expose :latest_closed_by, as: :closed_by, using: UserEntity
expose :merged_by, using: UserEntity
end
diff --git a/app/serializers/merge_request_poll_widget_entity.rb b/app/serializers/merge_request_poll_widget_entity.rb
index ab180b35b29..cef3f4555df 100644
--- a/app/serializers/merge_request_poll_widget_entity.rb
+++ b/app/serializers/merge_request_poll_widget_entity.rb
@@ -31,7 +31,7 @@ class MergeRequestPollWidgetEntity < Grape::Entity
end
expose :only_allow_merge_if_pipeline_succeeds do |merge_request|
- merge_request.project.only_allow_merge_if_pipeline_succeeds?
+ merge_request.project.only_allow_merge_if_pipeline_succeeds?(inherit_group_setting: true)
end
# CI related
diff --git a/app/serializers/project_entity.rb b/app/serializers/project_entity.rb
index 77e2115fbe2..cbdc19a83ce 100644
--- a/app/serializers/project_entity.rb
+++ b/app/serializers/project_entity.rb
@@ -13,4 +13,8 @@ class ProjectEntity < Grape::Entity
expose :full_name, documentation: { type: 'string', example: 'GitLab Org / GitLab' } do |project|
project.full_name
end
+
+ expose :refs_url do |project|
+ refs_project_path(project)
+ end
end
diff --git a/app/services/admin/set_feature_flag_service.rb b/app/services/admin/set_feature_flag_service.rb
index d72a18a6a58..3378be7eddd 100644
--- a/app/services/admin/set_feature_flag_service.rb
+++ b/app/services/admin/set_feature_flag_service.rb
@@ -2,82 +2,143 @@
module Admin
class SetFeatureFlagService
+ UnknownOperationError = Class.new(StandardError)
+
def initialize(feature_flag_name:, params:)
@name = feature_flag_name
+ @target = Feature::Target.new(params)
@params = params
+ @force = params[:force]
end
def execute
- unless params[:force]
+ unless force
error = validate_feature_flag_name
return ServiceResponse.error(message: error, reason: :invalid_feature_flag) if error
end
- flag_target = Feature::Target.new(params)
- value = gate_value(params)
-
- case value
- when true
- enable!(flag_target)
- when false
- disable!(flag_target)
+ if target.gate_specified?
+ update_targets
else
- enable_partially!(value, params)
+ update_global
end
feature_flag = Feature.get(name) # rubocop:disable Gitlab/AvoidFeatureGet
ServiceResponse.success(payload: { feature_flag: feature_flag })
- rescue Feature::Target::UnknowTargetError => e
+ rescue Feature::InvalidOperation => e
+ ServiceResponse.error(message: e.message, reason: :illegal_operation)
+ rescue UnknownOperationError => e
+ ServiceResponse.error(message: e.message, reason: :illegal_operation)
+ rescue Feature::Target::UnknownTargetError => e
ServiceResponse.error(message: e.message, reason: :actor_not_found)
end
private
- attr_reader :name, :params
+ attr_reader :name, :params, :target, :force
- def enable!(flag_target)
- if flag_target.gate_specified?
- flag_target.targets.each { |target| Feature.enable(name, target) }
- else
- Feature.enable(name)
+ # Note: the if expressions in `update_targets` and `update_global` are order dependant.
+ def update_targets
+ target.targets.each do |target|
+ if enable?
+ enable(target)
+ elsif disable?
+ Feature.disable(name, target)
+ elsif opt_out?
+ Feature.opt_out(name, target)
+ elsif remove_opt_out?
+ remove_opt_out(target)
+ else
+ raise UnknownOperationError, "Cannot set '#{name}' to #{value.inspect} for #{target}"
+ end
end
end
- def disable!(flag_target)
- if flag_target.gate_specified?
- flag_target.targets.each { |target| Feature.disable(name, target) }
- else
+ def update_global
+ if enable?
+ Feature.enable(name)
+ elsif disable?
Feature.disable(name)
+ elsif percentage_of_actors?
+ Feature.enable_percentage_of_actors(name, percentage)
+ elsif percentage_of_time?
+ Feature.enable_percentage_of_time(name, percentage)
+ else
+ msg = if key.present?
+ "Cannot set '#{name}' (#{key.inspect}) to #{value.inspect}"
+ else
+ "Cannot set '#{name}' to #{value.inspect}"
+ end
+
+ raise UnknownOperationError, msg
end
end
- def enable_partially!(value, params)
- if params[:key] == 'percentage_of_actors'
- Feature.enable_percentage_of_actors(name, value)
- else
- Feature.enable_percentage_of_time(name, value)
+ def remove_opt_out(target)
+ raise Feature::InvalidOperation, "No opt-out exists for #{target}" unless Feature.opted_out?(name, target)
+
+ Feature.remove_opt_out(name, target)
+ end
+
+ def enable(target)
+ if Feature.opted_out?(name, target)
+ target_name = target.respond_to?(:to_reference) ? target.to_reference : target.to_s
+ raise Feature::InvalidOperation, "Opt-out exists for #{target_name} - remove opt-out before enabling"
end
+
+ Feature.enable(name, target)
end
- def validate_feature_flag_name
- # overridden in EE
+ def value
+ params[:value]
end
- def gate_value(params)
- case params[:value]
- when 'true'
- true
- when '0', 'false'
- false
- else
- # https://github.com/jnunemaker/flipper/blob/master/lib/flipper/typecast.rb#L47
- if params[:value].to_s.include?('.')
- params[:value].to_f
- else
- params[:value].to_i
- end
- end
+ def key
+ params[:key]
+ end
+
+ def numeric_value?
+ params[:value].match?(/^\d+(\.\d+)?$/)
+ end
+
+ def percentage
+ raise UnknownOperationError, "Not a percentage" unless numeric_value?
+
+ value.to_f
+ end
+
+ def percentage_of_actors?
+ key == 'percentage_of_actors'
+ end
+
+ def percentage_of_time?
+ return true if key == 'percentage_of_time'
+ return numeric_value? if key.nil?
+
+ false
+ end
+
+ # Note: `key` is NOT considered - setting to a percentage to 0 is the same as disabling.
+ def disable?
+ value.in?(%w[0 0.0 false])
+ end
+
+ # Note: `key` is NOT considered - setting to a percentage to 100 is the same
+ def enable?
+ value.in?(%w[100 100.0 true])
+ end
+
+ def opt_out?
+ value == 'opt_out'
+ end
+
+ def remove_opt_out?
+ value == 'remove_opt_out'
+ end
+
+ def validate_feature_flag_name
+ ## Overridden in EE
end
end
end
diff --git a/app/services/bulk_imports/create_service.rb b/app/services/bulk_imports/create_service.rb
index d3c6dcca588..124b5964232 100644
--- a/app/services/bulk_imports/create_service.rb
+++ b/app/services/bulk_imports/create_service.rb
@@ -57,11 +57,14 @@ module BulkImports
bulk_import = BulkImport.create!(
user: current_user,
source_type: 'gitlab',
- source_version: client.instance_version
+ source_version: client.instance_version,
+ source_enterprise: client.instance_enterprise
)
bulk_import.create_configuration!(credentials.slice(:url, :access_token))
Array.wrap(params).each do |entity|
+ track_access_level(entity)
+
BulkImports::Entity.create!(
bulk_import: bulk_import,
source_type: entity[:source_type],
@@ -75,6 +78,34 @@ module BulkImports
end
end
+ def track_access_level(entity)
+ Gitlab::Tracking.event(
+ self.class.name,
+ 'create',
+ label: 'import_access_level',
+ user: current_user,
+ extra: { user_role: user_role(entity[:destination_namespace]), import_type: 'bulk_import_group' }
+ )
+ end
+
+ def user_role(destination_namespace)
+ namespace = Namespace.find_by_full_path(destination_namespace)
+ # if there is no parent namespace we assume user will be group creator/owner
+ return owner_role unless destination_namespace
+ return owner_role unless namespace
+ return owner_role unless namespace.group_namespace? # user namespace
+
+ membership = current_user.group_members.find_by(source_id: namespace.id) # rubocop:disable CodeReuse/ActiveRecord
+
+ return 'Not a member' unless membership
+
+ Gitlab::Access.human_access(membership.access_level)
+ end
+
+ def owner_role
+ Gitlab::Access.human_access(Gitlab::Access::OWNER)
+ end
+
def client
@client ||= BulkImports::Clients::HTTP.new(
url: @credentials[:url],
diff --git a/app/services/bulk_imports/file_download_service.rb b/app/services/bulk_imports/file_download_service.rb
index 45f1350df92..ee499c782b4 100644
--- a/app/services/bulk_imports/file_download_service.rb
+++ b/app/services/bulk_imports/file_download_service.rb
@@ -31,14 +31,13 @@ module BulkImports
@tmpdir = tmpdir
@file_size_limit = file_size_limit
@allowed_content_types = allowed_content_types
+ @remote_content_validated = false
end
def execute
validate_tmpdir
validate_filepath
validate_url
- validate_content_type
- validate_content_length
download_file
@@ -49,7 +48,7 @@ module BulkImports
private
- attr_reader :configuration, :relative_url, :tmpdir, :file_size_limit, :allowed_content_types
+ attr_reader :configuration, :relative_url, :tmpdir, :file_size_limit, :allowed_content_types, :response_headers
def download_file
File.open(filepath, 'wb') do |file|
@@ -58,6 +57,15 @@ module BulkImports
http_client.stream(relative_url) do |chunk|
next if bytes_downloaded == 0 && [301, 302, 303, 307, 308].include?(chunk.code)
+ @response_headers ||= Gitlab::HTTP::Response::Headers.new(chunk.http_response.to_hash)
+
+ unless @remote_content_validated
+ validate_content_type
+ validate_content_length
+
+ @remote_content_validated = true
+ end
+
bytes_downloaded += chunk.size
validate_size!(bytes_downloaded)
@@ -90,10 +98,6 @@ module BulkImports
::Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services?
end
- def response_headers
- @response_headers ||= http_client.head(relative_url).headers
- end
-
def validate_tmpdir
Gitlab::Utils.check_allowed_absolute_path!(tmpdir, [Dir.tmpdir])
end
diff --git a/app/services/chat_names/find_user_service.rb b/app/services/chat_names/find_user_service.rb
index 3dd3ba7f01c..3b204d51bab 100644
--- a/app/services/chat_names/find_user_service.rb
+++ b/app/services/chat_names/find_user_service.rb
@@ -2,9 +2,9 @@
module ChatNames
class FindUserService
- def initialize(integration, params)
- @integration = integration
- @params = params
+ def initialize(team_id, user_id)
+ @team_id = team_id
+ @user_id = user_id
end
def execute
@@ -17,12 +17,13 @@ module ChatNames
private
+ attr_reader :team_id, :user_id
+
# rubocop: disable CodeReuse/ActiveRecord
def find_chat_name
ChatName.find_by(
- integration: @integration,
- team_id: @params[:team_id],
- chat_id: @params[:user_id]
+ team_id: team_id,
+ chat_id: user_id
)
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/services/ci/after_requeue_job_service.rb b/app/services/ci/after_requeue_job_service.rb
deleted file mode 100644
index 4374ccd52e0..00000000000
--- a/app/services/ci/after_requeue_job_service.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-# frozen_string_literal: true
-
-module Ci
- class AfterRequeueJobService < ::BaseService
- def execute(processable)
- @processable = processable
-
- process_subsequent_jobs
- reset_source_bridge
- end
-
- private
-
- def process_subsequent_jobs
- dependent_jobs.each do |job|
- process(job)
- end
- end
-
- def reset_source_bridge
- @processable.pipeline.reset_source_bridge!(current_user)
- end
-
- # rubocop: disable CodeReuse/ActiveRecord
- def dependent_jobs
- ordered_by_dag(
- @processable.pipeline.processables
- .from_union(needs_dependent_jobs, stage_dependent_jobs)
- .skipped
- .ordered_by_stage
- .preload(:needs)
- )
- end
-
- def process(job)
- Gitlab::OptimisticLocking.retry_lock(job, name: 'ci_requeue_job') do |job|
- job.process(current_user)
- end
- end
-
- def stage_dependent_jobs
- @processable.pipeline.processables.after_stage(@processable.stage_idx)
- end
-
- def needs_dependent_jobs
- ::Gitlab::Ci::ProcessableObjectHierarchy.new(
- ::Ci::Processable.where(id: @processable.id)
- ).descendants
- end
-
- def ordered_by_dag(jobs)
- sorted_job_names = sort_jobs(jobs).each_with_index.to_h
-
- jobs.group_by(&:stage_idx).flat_map do |_, stage_jobs|
- stage_jobs.sort_by { |job| sorted_job_names.fetch(job.name) }
- end
- end
-
- def sort_jobs(jobs)
- Gitlab::Ci::YamlProcessor::Dag.order(
- jobs.to_h do |job|
- [job.name, job.needs.map(&:name)]
- end
- )
- end
- # rubocop: enable CodeReuse/ActiveRecord
- end
-end
diff --git a/app/services/ci/append_build_trace_service.rb b/app/services/ci/append_build_trace_service.rb
index 0eef0ff0e61..4432eecc24a 100644
--- a/app/services/ci/append_build_trace_service.rb
+++ b/app/services/ci/append_build_trace_service.rb
@@ -24,6 +24,11 @@ module Ci
body_start = content_range[0].to_i
body_end = body_start + body_data.bytesize
+ if first_debug_chunk?(body_start)
+ # Update the build metadata prior to appending trace content
+ build.enable_debug_trace!
+ end
+
if trace_size_exceeded?(body_end)
build.drop(:trace_size_exceeded)
@@ -45,10 +50,18 @@ module Ci
delegate :project, to: :build
+ def first_debug_chunk?(body_start)
+ body_start == 0 && debug_trace
+ end
+
def stream_range
params.fetch(:content_range)
end
+ def debug_trace
+ params.fetch(:debug_trace, false)
+ end
+
def log_range_error(stream_size, body_end)
extra = {
build_id: build.id,
diff --git a/app/services/ci/create_downstream_pipeline_service.rb b/app/services/ci/create_downstream_pipeline_service.rb
index 25cc9045052..3d0a7fb99ea 100644
--- a/app/services/ci/create_downstream_pipeline_service.rb
+++ b/app/services/ci/create_downstream_pipeline_service.rb
@@ -11,24 +11,25 @@ module Ci
DuplicateDownstreamPipelineError = Class.new(StandardError)
MAX_NESTED_CHILDREN = 2
- MAX_HIERARCHY_SIZE = 1000
def execute(bridge)
@bridge = bridge
- if bridge.has_downstream_pipeline?
+ if @bridge.has_downstream_pipeline?
Gitlab::ErrorTracking.track_exception(
DuplicateDownstreamPipelineError.new,
bridge_id: @bridge.id, project_id: @bridge.project_id
)
- return error('Already has a downstream pipeline')
+ return ServiceResponse.error(message: 'Already has a downstream pipeline')
end
pipeline_params = @bridge.downstream_pipeline_params
target_ref = pipeline_params.dig(:target_revision, :ref)
- return error('Pre-conditions not met') unless ensure_preconditions!(target_ref)
+ return ServiceResponse.error(message: 'Pre-conditions not met') unless ensure_preconditions!(target_ref)
+
+ return ServiceResponse.error(message: 'Can not run the bridge') unless @bridge.run
service = ::Ci::CreatePipelineService.new(
pipeline_params.fetch(:project),
@@ -40,10 +41,7 @@ module Ci
.payload
log_downstream_pipeline_creation(downstream_pipeline)
-
- downstream_pipeline.tap do |pipeline|
- update_bridge_status!(@bridge, pipeline)
- end
+ update_bridge_status!(@bridge, downstream_pipeline)
end
private
@@ -54,9 +52,12 @@ module Ci
# If bridge uses `strategy:depend` we leave it running
# and update the status when the downstream pipeline completes.
subject.success! unless subject.dependent?
+ ServiceResponse.success(payload: pipeline)
else
- subject.options[:downstream_errors] = pipeline.errors.full_messages
+ message = pipeline.errors.full_messages
+ subject.options[:downstream_errors] = message
subject.drop!(:downstream_pipeline_creation_failed)
+ ServiceResponse.error(payload: pipeline, message: message)
end
end
rescue StateMachines::InvalidTransition => e
@@ -64,6 +65,7 @@ module Ci
Ci::Bridge::InvalidTransitionError.new(e.message),
bridge_id: bridge.id,
downstream_pipeline_id: pipeline.id)
+ ServiceResponse.error(payload: pipeline, message: e.message)
end
def ensure_preconditions!(target_ref)
@@ -151,7 +153,13 @@ module Ci
return false unless @bridge.triggers_downstream_pipeline?
# Applies to the entire pipeline tree across all projects
- @bridge.pipeline.complete_hierarchy_count >= MAX_HIERARCHY_SIZE
+ # A pipeline tree can be shared between multiple namespaces (customers), the limit that is used here
+ # is the limit of the namespace that has added a downstream pipeline to a pipeline tree.
+ @bridge.project.actual_limits.exceeded?(:pipeline_hierarchy_size, complete_hierarchy_count)
+ end
+
+ def complete_hierarchy_count
+ @bridge.pipeline.complete_hierarchy_count
end
def config_checksum(pipeline)
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 4106dfe0ecc..9c3cc803587 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -4,8 +4,6 @@ module Ci
class CreatePipelineService < BaseService
attr_reader :pipeline, :logger
- CreateError = Class.new(StandardError)
-
LOG_MAX_DURATION_THRESHOLD = 3.seconds
LOG_MAX_PIPELINE_SIZE = 2_000
LOG_MAX_CREATION_THRESHOLD = 20.seconds
@@ -140,25 +138,24 @@ module Ci
def build_logger
Gitlab::Ci::Pipeline::Logger.new(project: project) do |l|
l.log_when do |observations|
- observations.any? do |name, values|
- values.any? &&
+ observations.any? do |name, observation|
name.to_s.end_with?('duration_s') &&
- values.max >= LOG_MAX_DURATION_THRESHOLD
+ Array(observation).max >= LOG_MAX_DURATION_THRESHOLD
end
end
l.log_when do |observations|
- values = observations['pipeline_size_count']
- next false if values.empty?
+ count = observations['pipeline_size_count']
+ next false unless count
- values.max >= LOG_MAX_PIPELINE_SIZE
+ count >= LOG_MAX_PIPELINE_SIZE
end
l.log_when do |observations|
- values = observations['pipeline_creation_duration_s']
- next false if values.empty?
+ duration = observations['pipeline_creation_duration_s']
+ next false unless duration
- values.max >= LOG_MAX_CREATION_THRESHOLD
+ duration >= LOG_MAX_CREATION_THRESHOLD
end
end
end
diff --git a/app/services/ci/enqueue_job_service.rb b/app/services/ci/enqueue_job_service.rb
new file mode 100644
index 00000000000..9e3bea3fd28
--- /dev/null
+++ b/app/services/ci/enqueue_job_service.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Ci
+ class EnqueueJobService
+ attr_accessor :job, :current_user, :variables
+
+ def initialize(job, current_user:, variables: nil)
+ @job = job
+ @current_user = current_user
+ @variables = variables
+ end
+
+ def execute(&transition)
+ job.user = current_user
+ job.job_variables_attributes = variables if variables
+
+ transition ||= ->(job) { job.enqueue! }
+ Gitlab::OptimisticLocking.retry_lock(job, name: 'ci_enqueue_job', &transition)
+
+ ResetSkippedJobsService.new(job.project, current_user).execute(job)
+
+ job
+ end
+ end
+end
diff --git a/app/services/ci/generate_kubeconfig_service.rb b/app/services/ci/generate_kubeconfig_service.rb
index 347bc99dbf5..1c6aaa9d1ff 100644
--- a/app/services/ci/generate_kubeconfig_service.rb
+++ b/app/services/ci/generate_kubeconfig_service.rb
@@ -2,9 +2,11 @@
module Ci
class GenerateKubeconfigService
- def initialize(pipeline, token:)
+ def initialize(pipeline, token:, environment:)
@pipeline = pipeline
@token = token
+ @environment = environment
+
@template = Gitlab::Kubernetes::Kubeconfig::Template.new
end
@@ -36,10 +38,13 @@ module Ci
private
- attr_reader :pipeline, :token, :template
+ attr_reader :pipeline, :token, :environment, :template
def agent_authorizations
- pipeline.cluster_agent_authorizations
+ ::Clusters::Agents::FilterAuthorizationsService.new(
+ pipeline.cluster_agent_authorizations,
+ environment: environment
+ ).execute
end
def cluster_name
diff --git a/app/services/ci/job_artifacts/create_service.rb b/app/services/ci/job_artifacts/create_service.rb
index 3dc097a8603..ee9982cf3ab 100644
--- a/app/services/ci/job_artifacts/create_service.rb
+++ b/app/services/ci/job_artifacts/create_service.rb
@@ -16,7 +16,7 @@ module Ci
def initialize(job)
@job = job
@project = job.project
- @pipeline = job.pipeline if ::Feature.enabled?(:ci_update_unlocked_job_artifacts, @project)
+ @pipeline = job.pipeline
end
def authorize(artifact_type:, filesize: nil)
@@ -85,7 +85,7 @@ module Ci
expire_in: expire_in
}
- artifact_attributes[:locked] = pipeline.locked if ::Feature.enabled?(:ci_update_unlocked_job_artifacts, project)
+ artifact_attributes[:locked] = pipeline.locked
artifact = Ci::JobArtifact.new(
artifact_attributes.merge(
diff --git a/app/services/ci/pipeline_schedule_service.rb b/app/services/ci/pipeline_schedule_service.rb
index 536eaa56f9b..d320382d19f 100644
--- a/app/services/ci/pipeline_schedule_service.rb
+++ b/app/services/ci/pipeline_schedule_service.rb
@@ -8,7 +8,7 @@ module Ci
# Ensure `next_run_at` is set properly before creating a pipeline.
# Otherwise, multiple pipelines could be created in a short interval.
schedule.schedule_next_run!
- RunPipelineScheduleWorker.perform_async(schedule.id, schedule.owner&.id)
+ RunPipelineScheduleWorker.perform_async(schedule.id, current_user&.id)
end
end
end
diff --git a/app/services/ci/pipeline_schedules/calculate_next_run_service.rb b/app/services/ci/pipeline_schedules/calculate_next_run_service.rb
index 6c8ccb017e9..a1b9ab5f82e 100644
--- a/app/services/ci/pipeline_schedules/calculate_next_run_service.rb
+++ b/app/services/ci/pipeline_schedules/calculate_next_run_service.rb
@@ -35,7 +35,7 @@ module Ci
def worker_cron
strong_memoize(:worker_cron) do
- Gitlab::Ci::CronParser.new(worker_cron_expression, Time.zone.name)
+ Gitlab::Ci::CronParser.new(@schedule.worker_cron_expression, Time.zone.name)
end
end
@@ -50,10 +50,6 @@ module Ci
Gitlab::Ci::CronParser.parse_natural("every #{every_x_minutes} minutes", Time.zone.name)
end
end
-
- def worker_cron_expression
- Settings.cron_jobs['pipeline_schedule_worker']['cron']
- end
end
end
end
diff --git a/app/services/ci/play_bridge_service.rb b/app/services/ci/play_bridge_service.rb
index a719467253e..ffcd2b05b31 100644
--- a/app/services/ci/play_bridge_service.rb
+++ b/app/services/ci/play_bridge_service.rb
@@ -5,12 +5,7 @@ module Ci
def execute(bridge)
check_access!(bridge)
- bridge.tap do |bridge|
- bridge.user = current_user
- bridge.enqueue!
-
- AfterRequeueJobService.new(project, current_user).execute(bridge)
- end
+ Ci::EnqueueJobService.new(bridge, current_user: current_user).execute
end
private
diff --git a/app/services/ci/play_build_service.rb b/app/services/ci/play_build_service.rb
index b7aec57f3e3..8d2225aba71 100644
--- a/app/services/ci/play_build_service.rb
+++ b/app/services/ci/play_build_service.rb
@@ -5,17 +5,7 @@ module Ci
def execute(build, job_variables_attributes = nil)
check_access!(build, job_variables_attributes)
- if build.can_enqueue?
- build.user = current_user
- build.job_variables_attributes = job_variables_attributes || []
- build.enqueue!
-
- AfterRequeueJobService.new(project, current_user).execute(build)
-
- build
- else
- retry_build(build)
- end
+ Ci::EnqueueJobService.new(build, current_user: current_user, variables: job_variables_attributes || []).execute
rescue StateMachines::InvalidTransition
retry_build(build.reset)
end
diff --git a/app/services/ci/process_build_service.rb b/app/services/ci/process_build_service.rb
index cb51d918fc2..a5300cfd29f 100644
--- a/app/services/ci/process_build_service.rb
+++ b/app/services/ci/process_build_service.rb
@@ -15,7 +15,7 @@ module Ci
private
def process(build)
- return enqueue(build) if Feature.enabled?(:ci_retry_job_fix, project) && build.enqueue_immediately?
+ return enqueue(build) if build.enqueue_immediately?
if build.schedulable?
build.schedule
diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index f11577feb88..cd879e9bc07 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -108,15 +108,13 @@ module Ci
def each_build(params, &blk)
queue = ::Ci::Queue::BuildQueueService.new(runner)
- builds = begin
- if runner.instance_type?
- queue.builds_for_shared_runner
- elsif runner.group_type?
- queue.builds_for_group_runner
- else
- queue.builds_for_project_runner
- end
- end
+ builds = if runner.instance_type?
+ queue.builds_for_shared_runner
+ elsif runner.group_type?
+ queue.builds_for_group_runner
+ else
+ queue.builds_for_project_runner
+ end
if runner.ref_protected?
builds = queue.builds_for_protected_runner(builds)
diff --git a/app/services/ci/reset_skipped_jobs_service.rb b/app/services/ci/reset_skipped_jobs_service.rb
new file mode 100644
index 00000000000..eb809b0162c
--- /dev/null
+++ b/app/services/ci/reset_skipped_jobs_service.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+module Ci
+ # This service resets skipped jobs so they can be processed again.
+ # It affects the jobs that depend on the passed in job parameter.
+ class ResetSkippedJobsService < ::BaseService
+ def execute(processable)
+ @processable = processable
+
+ process_subsequent_jobs
+ reset_source_bridge
+ end
+
+ private
+
+ def process_subsequent_jobs
+ dependent_jobs.each do |job|
+ process(job)
+ end
+ end
+
+ def reset_source_bridge
+ @processable.pipeline.reset_source_bridge!(current_user)
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def dependent_jobs
+ ordered_by_dag(
+ @processable.pipeline.processables
+ .from_union(needs_dependent_jobs, stage_dependent_jobs)
+ .skipped
+ .ordered_by_stage
+ .preload(:needs)
+ )
+ end
+
+ def process(job)
+ Gitlab::OptimisticLocking.retry_lock(job, name: 'ci_requeue_job') do |job|
+ job.process(current_user)
+ end
+ end
+
+ def stage_dependent_jobs
+ @processable.pipeline.processables.after_stage(@processable.stage_idx)
+ end
+
+ def needs_dependent_jobs
+ ::Gitlab::Ci::ProcessableObjectHierarchy.new(
+ ::Ci::Processable.where(id: @processable.id)
+ ).descendants
+ end
+
+ def ordered_by_dag(jobs)
+ sorted_job_names = sort_jobs(jobs).each_with_index.to_h
+
+ jobs.group_by(&:stage_idx).flat_map do |_, stage_jobs|
+ stage_jobs.sort_by { |job| sorted_job_names.fetch(job.name) }
+ end
+ end
+
+ def sort_jobs(jobs)
+ Gitlab::Ci::YamlProcessor::Dag.order(
+ jobs.to_h do |job|
+ [job.name, job.needs.map(&:name)]
+ end
+ )
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+end
diff --git a/app/services/ci/retry_job_service.rb b/app/services/ci/retry_job_service.rb
index 74ebaef48b1..da0e80dfed7 100644
--- a/app/services/ci/retry_job_service.rb
+++ b/app/services/ci/retry_job_service.rb
@@ -28,7 +28,7 @@ module Ci
check_access!(job)
new_job = job.clone(current_user: current_user, new_job_variables_attributes: variables)
- if Feature.enabled?(:ci_retry_job_fix, project) && enqueue_if_actionable && new_job.action?
+ if enqueue_if_actionable && new_job.action?
new_job.set_enqueue_immediately!
end
@@ -64,15 +64,10 @@ module Ci
next if new_job.failed?
- Gitlab::OptimisticLocking.retry_lock(new_job, name: 'retry_build', &:enqueue) if Feature.disabled?(
- :ci_retry_job_fix, project)
+ ResetSkippedJobsService.new(project, current_user).execute(job)
- AfterRequeueJobService.new(project, current_user).execute(job)
-
- if Feature.enabled?(:ci_retry_job_fix, project)
- Ci::PipelineCreation::StartPipelineService.new(job.pipeline).execute
- new_job.reset
- end
+ Ci::PipelineCreation::StartPipelineService.new(job.pipeline).execute
+ new_job.reset
end
end
diff --git a/app/services/ci/test_failure_history_service.rb b/app/services/ci/test_failure_history_service.rb
index 5a8072b2a0d..4f794b3d671 100644
--- a/app/services/ci/test_failure_history_service.rb
+++ b/app/services/ci/test_failure_history_service.rb
@@ -103,7 +103,8 @@ module Ci
{
unit_test_id: ci_unit_test.id,
build_id: build.id,
- failed_at: build.finished_at
+ failed_at: build.finished_at,
+ partition_id: build.partition_id
}
end
end
diff --git a/app/services/ci/track_failed_build_service.rb b/app/services/ci/track_failed_build_service.rb
index caf7034234c..973c43a9445 100644
--- a/app/services/ci/track_failed_build_service.rb
+++ b/app/services/ci/track_failed_build_service.rb
@@ -6,7 +6,7 @@
# @param exit_code [Int] the resulting exit code.
module Ci
class TrackFailedBuildService
- SCHEMA_URL = 'iglu:com.gitlab/ci_build_failed/jsonschema/1-0-0'
+ SCHEMA_URL = 'iglu:com.gitlab/ci_build_failed/jsonschema/1-0-1'
def initialize(build:, exit_code:, failure_reason:)
@build = build
@@ -42,7 +42,8 @@ module Ci
build_name: @build.name,
build_artifact_types: @build.job_artifact_types,
exit_code: @exit_code,
- failure_reason: @failure_reason
+ failure_reason: @failure_reason,
+ project: @build.project_id
}
end
end
diff --git a/app/services/ci/unlock_artifacts_service.rb b/app/services/ci/unlock_artifacts_service.rb
index 574cdae6480..237f1997edb 100644
--- a/app/services/ci/unlock_artifacts_service.rb
+++ b/app/services/ci/unlock_artifacts_service.rb
@@ -11,42 +11,21 @@ module Ci
unlocked_pipeline_artifacts: 0
}
- if ::Feature.enabled?(:ci_update_unlocked_job_artifacts, ci_ref.project)
- loop do
- unlocked_pipelines = []
- unlocked_job_artifacts = []
+ loop do
+ unlocked_pipelines = []
+ unlocked_job_artifacts = []
- ::Ci::Pipeline.transaction do
- unlocked_pipelines = unlock_pipelines(ci_ref, before_pipeline)
- unlocked_job_artifacts = unlock_job_artifacts(unlocked_pipelines)
+ ::Ci::Pipeline.transaction do
+ unlocked_pipelines = unlock_pipelines(ci_ref, before_pipeline)
+ unlocked_job_artifacts = unlock_job_artifacts(unlocked_pipelines)
- results[:unlocked_pipeline_artifacts] += unlock_pipeline_artifacts(unlocked_pipelines)
- end
+ results[:unlocked_pipeline_artifacts] += unlock_pipeline_artifacts(unlocked_pipelines)
+ end
- break if unlocked_pipelines.empty?
+ break if unlocked_pipelines.empty?
- results[:unlocked_pipelines] += unlocked_pipelines.length
- results[:unlocked_job_artifacts] += unlocked_job_artifacts.length
- end
- else
- query = <<~SQL.squish
- UPDATE "ci_pipelines"
- SET "locked" = #{::Ci::Pipeline.lockeds[:unlocked]}
- WHERE "ci_pipelines"."id" in (
- #{collect_pipelines(ci_ref, before_pipeline).select(:id).to_sql}
- LIMIT #{BATCH_SIZE}
- FOR UPDATE SKIP LOCKED
- )
- RETURNING "ci_pipelines"."id";
- SQL
-
- loop do
- unlocked_pipelines = Ci::Pipeline.connection.exec_query(query)
-
- break if unlocked_pipelines.empty?
-
- results[:unlocked_pipelines] += unlocked_pipelines.length
- end
+ results[:unlocked_pipelines] += unlocked_pipelines.length
+ results[:unlocked_job_artifacts] += unlocked_job_artifacts.length
end
results
@@ -88,13 +67,6 @@ module Ci
private
- def collect_pipelines(ci_ref, before_pipeline)
- pipeline_scope = ci_ref.pipelines
- pipeline_scope = pipeline_scope.before_pipeline(before_pipeline) if before_pipeline
-
- pipeline_scope.artifacts_locked
- end
-
def unlock_job_artifacts(pipelines)
return if pipelines.empty?
diff --git a/app/services/clusters/agents/filter_authorizations_service.rb b/app/services/clusters/agents/filter_authorizations_service.rb
new file mode 100644
index 00000000000..68517ceec04
--- /dev/null
+++ b/app/services/clusters/agents/filter_authorizations_service.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Agents
+ class FilterAuthorizationsService
+ def initialize(authorizations, filter_params)
+ @authorizations = authorizations
+ @filter_params = filter_params
+
+ @environments_matcher = {}
+ end
+
+ def execute
+ filter_by_environment(authorizations)
+ end
+
+ private
+
+ attr_reader :authorizations, :filter_params
+
+ def filter_by_environment(auths)
+ return auths unless filter_by_environment?
+
+ auths.select do |auth|
+ next true if auth.config['environments'].blank?
+
+ auth.config['environments'].any? { |environment_pattern| matches_environment?(environment_pattern) }
+ end
+ end
+
+ def filter_by_environment?
+ filter_params.has_key?(:environment)
+ end
+
+ def environment_filter
+ @environment_filter ||= filter_params[:environment]
+ end
+
+ def matches_environment?(environment_pattern)
+ return false if environment_filter.nil?
+
+ environments_matcher(environment_pattern).match?(environment_filter)
+ end
+
+ def environments_matcher(environment_pattern)
+ @environments_matcher[environment_pattern] ||= ::Gitlab::Ci::EnvironmentMatcher.new(environment_pattern)
+ end
+ end
+ end
+end
diff --git a/app/services/clusters/agents/refresh_authorization_service.rb b/app/services/clusters/agents/refresh_authorization_service.rb
index 54b90a7304c..53b14ab54da 100644
--- a/app/services/clusters/agents/refresh_authorization_service.rb
+++ b/app/services/clusters/agents/refresh_authorization_service.rb
@@ -83,11 +83,7 @@ module Clusters
end
def allowed_projects
- if group_root_ancestor?
- root_ancestor.all_projects
- else
- ::Project.id_in(project.id)
- end
+ root_ancestor.all_projects
end
def allowed_groups
diff --git a/app/services/clusters/applications/base_service.rb b/app/services/clusters/applications/base_service.rb
deleted file mode 100644
index c6f22cfa04c..00000000000
--- a/app/services/clusters/applications/base_service.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-# frozen_string_literal: true
-
-module Clusters
- module Applications
- class BaseService
- InvalidApplicationError = Class.new(StandardError)
-
- attr_reader :cluster, :current_user, :params
-
- def initialize(cluster, user, params = {})
- @cluster = cluster
- @current_user = user
- @params = params.dup
- end
-
- def execute(request)
- instantiate_application.tap do |application|
- if application.has_attribute?(:hostname)
- application.hostname = params[:hostname]
- end
-
- if application.has_attribute?(:email)
- application.email = params[:email]
- end
-
- if application.has_attribute?(:stack)
- application.stack = params[:stack]
- end
-
- if application.respond_to?(:oauth_application)
- application.oauth_application = create_oauth_application(application, request)
- end
-
- if application.instance_of?(Knative)
- Serverless::AssociateDomainService
- .new(application, pages_domain_id: params[:pages_domain_id], creator: current_user)
- .execute
- end
-
- worker = worker_class(application)
-
- application.make_scheduled!
-
- worker.perform_async(application.name, application.id)
- end
- end
-
- protected
-
- def worker_class(application)
- raise NotImplementedError
- end
-
- def builder
- raise NotImplementedError
- end
-
- def project_builders
- raise NotImplementedError
- end
-
- def instantiate_application
- raise_invalid_application_error if unknown_application?
-
- builder || raise(InvalidApplicationError, "invalid application: #{application_name}")
- end
-
- def raise_invalid_application_error
- raise(InvalidApplicationError, "invalid application: #{application_name}")
- end
-
- def unknown_application?
- Clusters::Cluster::APPLICATIONS.keys.exclude?(application_name)
- end
-
- def application_name
- params[:application]
- end
-
- def application_class
- Clusters::Cluster::APPLICATIONS[application_name]
- end
-
- def create_oauth_application(application, request)
- oauth_application_params = {
- name: params[:application],
- redirect_uri: application.callback_url,
- scopes: application.oauth_scopes,
- owner: current_user
- }
-
- ::Applications::CreateService.new(current_user, oauth_application_params).execute(request)
- end
- end
- end
-end
diff --git a/app/services/clusters/applications/check_progress_service.rb b/app/services/clusters/applications/check_progress_service.rb
deleted file mode 100644
index 4a07b955f8e..00000000000
--- a/app/services/clusters/applications/check_progress_service.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-# frozen_string_literal: true
-
-module Clusters
- module Applications
- class CheckProgressService < BaseHelmService
- def execute
- return unless operation_in_progress?
-
- case pod_phase
- when Gitlab::Kubernetes::Pod::SUCCEEDED
- on_success
- when Gitlab::Kubernetes::Pod::FAILED
- on_failed
- else
- check_timeout
- end
- rescue Kubeclient::HttpError => e
- log_error(e)
-
- app.make_errored!(_('Kubernetes error: %{error_code}') % { error_code: e.error_code })
- end
-
- private
-
- def operation_in_progress?
- raise NotImplementedError
- end
-
- def on_success
- raise NotImplementedError
- end
-
- def pod_name
- raise NotImplementedError
- end
-
- def on_failed
- app.make_errored!(_('Operation failed. Check pod logs for %{pod_name} for more details.') % { pod_name: pod_name })
- end
-
- def timed_out?
- raise NotImplementedError
- end
-
- def pod_phase
- helm_api.status(pod_name)
- end
- end
- end
-end
diff --git a/app/services/clusters/applications/install_service.rb b/app/services/clusters/applications/install_service.rb
deleted file mode 100644
index dffb4ce65ab..00000000000
--- a/app/services/clusters/applications/install_service.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-module Clusters
- module Applications
- class InstallService < BaseHelmService
- def execute
- return unless app.scheduled?
-
- app.make_installing!
-
- install
- end
-
- private
-
- def install
- log_event(:begin_install)
- helm_api.install(install_command)
-
- log_event(:schedule_wait_for_installation)
- ClusterWaitForAppInstallationWorker.perform_in(
- ClusterWaitForAppInstallationWorker::INTERVAL, app.name, app.id)
- rescue Kubeclient::HttpError => e
- log_error(e)
- app.make_errored!(_('Kubernetes error: %{error_code}') % { error_code: e.error_code })
- rescue StandardError => e
- log_error(e)
- app.make_errored!(_('Failed to install.'))
- end
- end
- end
-end
diff --git a/app/services/clusters/applications/prometheus_config_service.rb b/app/services/clusters/applications/prometheus_config_service.rb
deleted file mode 100644
index d39d63c874f..00000000000
--- a/app/services/clusters/applications/prometheus_config_service.rb
+++ /dev/null
@@ -1,155 +0,0 @@
-# frozen_string_literal: true
-
-module Clusters
- module Applications
- class PrometheusConfigService
- def initialize(project, cluster, app)
- @project = project
- @cluster = cluster
- @app = app
- end
-
- def execute(config = {})
- if has_alerts?
- generate_alert_manager(config)
- else
- reset_alert_manager(config)
- end
- end
-
- private
-
- attr_reader :project, :cluster, :app
-
- def reset_alert_manager(config)
- config = set_alert_manager_enabled(config, false)
- config.delete('alertmanagerFiles')
- config['serverFiles'] ||= {}
- config['serverFiles']['alerts'] = {}
-
- config
- end
-
- def generate_alert_manager(config)
- config = set_alert_manager_enabled(config, true)
- config = set_alert_manager_files(config)
-
- set_alert_manager_groups(config)
- end
-
- def set_alert_manager_enabled(config, enabled)
- config['alertmanager'] ||= {}
- config['alertmanager']['enabled'] = enabled
-
- config
- end
-
- def set_alert_manager_files(config)
- config['alertmanagerFiles'] = {
- 'alertmanager.yml' => {
- 'receivers' => alert_manager_receivers_params,
- 'route' => alert_manager_route_params
- }
- }
-
- config
- end
-
- def set_alert_manager_groups(config)
- config['serverFiles'] ||= {}
- config['serverFiles']['alerts'] ||= {}
- config['serverFiles']['alerts']['groups'] ||= []
-
- environments_with_alerts.each do |env_name, alerts|
- index = config['serverFiles']['alerts']['groups'].find_index do |group|
- group['name'] == env_name
- end
-
- if index
- config['serverFiles']['alerts']['groups'][index]['rules'] = alerts
- else
- config['serverFiles']['alerts']['groups'] << {
- 'name' => env_name,
- 'rules' => alerts
- }
- end
- end
-
- config
- end
-
- def alert_manager_receivers_params
- [
- {
- 'name' => 'gitlab',
- 'webhook_configs' => [
- {
- 'url' => notify_url,
- 'send_resolved' => true,
- 'http_config' => {
- 'bearer_token' => alert_manager_token
- }
- }
- ]
- }
- ]
- end
-
- def alert_manager_token
- app.alert_manager_token
- end
-
- def alert_manager_route_params
- {
- 'receiver' => 'gitlab',
- 'group_wait' => '30s',
- 'group_interval' => '5m',
- 'repeat_interval' => '4h'
- }
- end
-
- def notify_url
- ::Gitlab::Routing.url_helpers
- .notify_project_prometheus_alerts_url(project, format: :json)
- end
-
- def has_alerts?
- environments_with_alerts.values.flatten(1).any?
- end
-
- def environments_with_alerts
- @environments_with_alerts ||=
- environments.each_with_object({}) do |environment, hash|
- name = rule_name(environment)
- hash[name] = alerts(environment)
- end
- end
-
- def rule_name(environment)
- "#{environment.name}.rules"
- end
-
- def alerts(environment)
- alerts = Projects::Prometheus::AlertsFinder
- .new(environment: environment)
- .execute
-
- alerts.map do |alert|
- hash = alert.to_param
- hash['expr'] = substitute_query_variables(hash['expr'], environment)
- hash
- end
- end
-
- def substitute_query_variables(query, environment)
- result = ::Prometheus::ProxyVariableSubstitutionService.new(environment, query: query).execute
-
- result[:params][:query]
- end
-
- def environments
- project.environments_for_scope(cluster.environment_scope)
- end
- end
- end
-end
diff --git a/app/services/clusters/applications/upgrade_service.rb b/app/services/clusters/applications/upgrade_service.rb
deleted file mode 100644
index ac68e64af38..00000000000
--- a/app/services/clusters/applications/upgrade_service.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-module Clusters
- module Applications
- class UpgradeService < BaseHelmService
- def execute
- return unless app.scheduled?
-
- app.make_updating!
-
- upgrade
- end
-
- private
-
- def upgrade
- # install_command works with upgrades too
- # as it basically does `helm upgrade --install`
- log_event(:begin_upgrade)
- helm_api.update(install_command)
-
- log_event(:schedule_wait_for_upgrade)
- ClusterWaitForAppInstallationWorker.perform_in(
- ClusterWaitForAppInstallationWorker::INTERVAL, app.name, app.id)
- rescue Kubeclient::HttpError => e
- log_error(e)
- app.make_errored!(_('Kubernetes error: %{error_code}') % { error_code: e.error_code })
- rescue StandardError => e
- log_error(e)
- app.make_errored!(_('Failed to upgrade.'))
- end
- end
- end
-end
diff --git a/app/services/clusters/kubernetes/create_or_update_service_account_service.rb b/app/services/clusters/kubernetes/create_or_update_service_account_service.rb
index eabc428d0d2..e87640f4c76 100644
--- a/app/services/clusters/kubernetes/create_or_update_service_account_service.rb
+++ b/app/services/clusters/kubernetes/create_or_update_service_account_service.rb
@@ -3,7 +3,7 @@
module Clusters
module Kubernetes
class CreateOrUpdateServiceAccountService
- def initialize(kubeclient, service_account_name:, service_account_namespace:, service_account_namespace_labels: nil, token_name:, rbac:, namespace_creator: false, role_binding_name: nil)
+ def initialize(kubeclient, service_account_name:, service_account_namespace:, token_name:, rbac:, service_account_namespace_labels: nil, namespace_creator: false, role_binding_name: nil)
@kubeclient = kubeclient
@service_account_name = service_account_name
@service_account_namespace = service_account_namespace
diff --git a/app/services/concerns/incident_management/usage_data.rb b/app/services/concerns/incident_management/usage_data.rb
index 27e60029ea3..40183085344 100644
--- a/app/services/concerns/incident_management/usage_data.rb
+++ b/app/services/concerns/incident_management/usage_data.rb
@@ -7,7 +7,23 @@ module IncidentManagement
def track_incident_action(current_user, target, action)
return unless target.incident?
- track_usage_event(:"incident_management_#{action}", current_user.id)
+ event = "incident_management_#{action}"
+ track_usage_event(event, current_user.id)
+
+ namespace = target.try(:namespace)
+ project = target.try(:project)
+
+ return unless Feature.enabled?(:route_hll_to_snowplow_phase2, target.try(:namespace))
+
+ Gitlab::Tracking.event(
+ self.class.to_s,
+ event,
+ project: project,
+ namespace: namespace,
+ user: current_user,
+ label: 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly',
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event).to_context]
+ )
end
end
end
diff --git a/app/services/concerns/rate_limited_service.rb b/app/services/concerns/rate_limited_service.rb
index 5d7247a5b99..fa366c1ccd0 100644
--- a/app/services/concerns/rate_limited_service.rb
+++ b/app/services/concerns/rate_limited_service.rb
@@ -49,8 +49,8 @@ module RateLimitedService
end
def evaluated_scope_for(service)
- opts[:scope].each_with_object({}) do |var, all|
- all[var] = service.public_send(var) # rubocop: disable GitlabSecurity/PublicSend
+ opts[:scope].index_with do |var|
+ service.public_send(var) # rubocop: disable GitlabSecurity/PublicSend
end
end
end
diff --git a/app/services/deployments/create_for_build_service.rb b/app/services/deployments/create_for_build_service.rb
index 7bc0ea88910..b58aa50a66f 100644
--- a/app/services/deployments/create_for_build_service.rb
+++ b/app/services/deployments/create_for_build_service.rb
@@ -28,7 +28,7 @@ module Deployments
def to_resource(build, environment)
return build.deployment if build.deployment
- return unless build.starts_environment?
+ return unless build.deployment_job?
deployment = ::Deployment.new(attributes(build, environment))
diff --git a/app/services/design_management/generate_image_versions_service.rb b/app/services/design_management/generate_image_versions_service.rb
index 3ff239b59cc..0771ada72a1 100644
--- a/app/services/design_management/generate_image_versions_service.rb
+++ b/app/services/design_management/generate_image_versions_service.rb
@@ -69,17 +69,15 @@ module DesignManagement
# The LFS pointer file data contains an "OID" that lets us retrieve `LfsObject`
# records, which have an Uploader (`LfsObjectUploader`) for the original design file.
def raw_files_by_path
- @raw_files_by_path ||= begin
- LfsObject.for_oids(blobs_by_oid.keys).each_with_object({}) do |lfs_object, h|
- blob = blobs_by_oid[lfs_object.oid]
- file = lfs_object.file.file
- # The `CarrierWave::SanitizedFile` is loaded without knowing the `content_type`
- # of the file, due to the file not having an extension.
- #
- # Set the content_type from the `Blob`.
- file.content_type = blob.content_type
- h[blob.path] = file
- end
+ @raw_files_by_path ||= LfsObject.for_oids(blobs_by_oid.keys).each_with_object({}) do |lfs_object, h|
+ blob = blobs_by_oid[lfs_object.oid]
+ file = lfs_object.file.file
+ # The `CarrierWave::SanitizedFile` is loaded without knowing the `content_type`
+ # of the file, due to the file not having an extension.
+ #
+ # Set the content_type from the `Blob`.
+ file.content_type = blob.content_type
+ h[blob.path] = file
end
end
diff --git a/app/services/environments/create_for_build_service.rb b/app/services/environments/create_for_build_service.rb
index c46b66ac5b3..ff4da212002 100644
--- a/app/services/environments/create_for_build_service.rb
+++ b/app/services/environments/create_for_build_service.rb
@@ -3,10 +3,10 @@
module Environments
# This class creates an environment record for a build (a pipeline job).
class CreateForBuildService
- def execute(build, merge_request: nil)
+ def execute(build)
return unless build.instance_of?(::Ci::Build) && build.has_environment_keyword?
- environment = to_resource(build, merge_request)
+ environment = to_resource(build)
if environment.persisted?
build.persisted_environment = environment
@@ -21,12 +21,12 @@ module Environments
private
# rubocop: disable Performance/ActiveRecordSubtransactionMethods
- def to_resource(build, merge_request)
+ def to_resource(build)
build.project.environments.safe_find_or_create_by(name: build.expanded_environment_name) do |environment|
# Initialize the attributes at creation
environment.auto_stop_in = expanded_auto_stop_in(build)
environment.tier = build.environment_tier_from_options
- environment.merge_request = merge_request
+ environment.merge_request = build.pipeline.merge_request
end
end
# rubocop: enable Performance/ActiveRecordSubtransactionMethods
diff --git a/app/services/environments/schedule_to_delete_review_apps_service.rb b/app/services/environments/schedule_to_delete_review_apps_service.rb
index 041b834f11b..8e9fe3300c4 100644
--- a/app/services/environments/schedule_to_delete_review_apps_service.rb
+++ b/app/services/environments/schedule_to_delete_review_apps_service.rb
@@ -68,7 +68,7 @@ module Environments
end
def mark_for_deletion(deletable_environments)
- Environment.for_id(deletable_environments).schedule_to_delete
+ Environment.id_in(deletable_environments).schedule_to_delete
end
class Result
diff --git a/app/services/error_tracking/list_projects_service.rb b/app/services/error_tracking/list_projects_service.rb
index 625addaf915..2f23d47029c 100644
--- a/app/services/error_tracking/list_projects_service.rb
+++ b/app/services/error_tracking/list_projects_service.rb
@@ -19,19 +19,18 @@ module ErrorTracking
end
def project_error_tracking_setting
- @project_error_tracking_setting ||= begin
- (super || project.build_error_tracking_setting).tap do |setting|
- setting.api_url = ErrorTracking::ProjectErrorTrackingSetting.build_api_url_from(
- api_host: params[:api_host],
- organization_slug: 'org',
- project_slug: 'proj'
- )
-
- setting.token = token(setting)
- setting.enabled = true
- end
+ (super || project.build_error_tracking_setting).tap do |setting|
+ setting.api_url = ErrorTracking::ProjectErrorTrackingSetting.build_api_url_from(
+ api_host: params[:api_host],
+ organization_slug: 'org',
+ project_slug: 'proj'
+ )
+
+ setting.token = token(setting)
+ setting.enabled = true
end
end
+ strong_memoize_attr :project_error_tracking_setting
def token(setting)
# Use param token if not masked, otherwise use database token
diff --git a/app/services/event_create_service.rb b/app/services/event_create_service.rb
index 662980fe506..bf4a26400e1 100644
--- a/app/services/event_create_service.rb
+++ b/app/services/event_create_service.rb
@@ -10,6 +10,10 @@
class EventCreateService
IllegalActionError = Class.new(StandardError)
+ DEGIGN_EVENT_LABEL = 'usage_activity_by_stage_monthly.create.action_monthly_active_users_design_management'
+ MR_EVENT_LABEL = 'usage_activity_by_stage_monthly.create.merge_requests_users'
+ MR_EVENT_PROPERTY = 'merge_requests_users'
+
def open_issue(issue, current_user)
create_record_event(issue, current_user, :created)
end
@@ -26,9 +30,11 @@ class EventCreateService
create_record_event(merge_request, current_user, :created).tap do
track_event(event_action: :created, event_target: MergeRequest, author_id: current_user.id)
track_snowplow_event(
- :created,
- merge_request,
- current_user
+ action: :created,
+ project: merge_request.project,
+ user: current_user,
+ label: MR_EVENT_LABEL,
+ property: MR_EVENT_PROPERTY
)
end
end
@@ -37,9 +43,11 @@ class EventCreateService
create_record_event(merge_request, current_user, :closed).tap do
track_event(event_action: :closed, event_target: MergeRequest, author_id: current_user.id)
track_snowplow_event(
- :closed,
- merge_request,
- current_user
+ action: :closed,
+ project: merge_request.project,
+ user: current_user,
+ label: MR_EVENT_LABEL,
+ property: MR_EVENT_PROPERTY
)
end
end
@@ -52,9 +60,11 @@ class EventCreateService
create_record_event(merge_request, current_user, :merged).tap do
track_event(event_action: :merged, event_target: MergeRequest, author_id: current_user.id)
track_snowplow_event(
- :merged,
- merge_request,
- current_user
+ action: :merged,
+ project: merge_request.project,
+ user: current_user,
+ label: MR_EVENT_LABEL,
+ property: MR_EVENT_PROPERTY
)
end
end
@@ -80,11 +90,12 @@ class EventCreateService
if note.is_a?(DiffNote) && note.for_merge_request?
track_event(event_action: :commented, event_target: MergeRequest, author_id: current_user.id)
track_snowplow_event(
- :commented,
- note,
- current_user
+ action: :commented,
+ project: note.project,
+ user: current_user,
+ label: MR_EVENT_LABEL,
+ property: MR_EVENT_PROPERTY
)
-
end
end
end
@@ -117,17 +128,10 @@ class EventCreateService
records = create.zip([:created].cycle) + update.zip([:updated].cycle)
return [] if records.empty?
- if create.any?
- old_track_snowplow_event(create.first, current_user,
- Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION,
- :create, 'design_users')
- end
+ event_meta = { user: current_user, label: DEGIGN_EVENT_LABEL, property: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION }
+ track_snowplow_event(action: :create, project: create.first.project, **event_meta) if create.any?
- if update.any?
- old_track_snowplow_event(update.first, current_user,
- Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION,
- :update, 'design_users')
- end
+ track_snowplow_event(action: :update, project: update.first.project, **event_meta) if update.any?
create_record_events(records, current_user)
end
@@ -135,9 +139,13 @@ class EventCreateService
def destroy_designs(designs, current_user)
return [] unless designs.present?
- old_track_snowplow_event(designs.first, current_user,
- Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION,
- :destroy, 'design_users')
+ track_snowplow_event(
+ action: :destroy,
+ project: designs.first.project,
+ user: current_user,
+ label: DEGIGN_EVENT_LABEL,
+ property: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION
+ )
create_record_events(designs.zip([:destroyed].cycle), current_user)
end
@@ -229,7 +237,8 @@ class EventCreateService
namespace: namespace,
user: current_user,
project: project,
- context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: 'action_active_users_project_repo').to_context]
+ property: 'project_action',
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: 'project_action').to_context]
)
end
@@ -270,33 +279,18 @@ class EventCreateService
Gitlab::UsageDataCounters::TrackUniqueEvents.track_event(**params)
end
- # This will be deleted as a part of
- # https://gitlab.com/groups/gitlab-org/-/epics/8641
- # once all the events are fixed
- def old_track_snowplow_event(record, current_user, category, action, label)
+ def track_snowplow_event(action:, project:, user:, label:, property:)
return unless Feature.enabled?(:route_hll_to_snowplow_phase2)
- project = record.project
- Gitlab::Tracking.event(
- category.to_s,
- action.to_s,
- label: label,
- project: project,
- namespace: project.namespace,
- user: current_user
- )
- end
-
- def track_snowplow_event(action, record, user)
- project = record.project
Gitlab::Tracking.event(
self.class.to_s,
action.to_s,
- label: 'usage_activity_by_stage_monthly.create.merge_requests_users',
+ label: label,
namespace: project.namespace,
user: user,
project: project,
- context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: 'merge_requests_users').to_context]
+ property: property.to_s,
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: property.to_s).to_context]
)
end
end
diff --git a/app/services/git/branch_hooks_service.rb b/app/services/git/branch_hooks_service.rb
index 7de56c037ed..71dd9501648 100644
--- a/app/services/git/branch_hooks_service.rb
+++ b/app/services/git/branch_hooks_service.rb
@@ -164,16 +164,27 @@ module Git
end
end
- def unsigned_x509_shas(commits)
- CommitSignatures::X509CommitSignature.unsigned_commit_shas(commits.map(&:sha))
+ def signature_types
+ types = [
+ ::CommitSignatures::GpgSignature,
+ ::CommitSignatures::X509CommitSignature
+ ]
+
+ types.push(::CommitSignatures::SshSignature) if Feature.enabled?(:ssh_commit_signatures, project)
+
+ types
end
- def unsigned_gpg_shas(commits)
- CommitSignatures::GpgSignature.unsigned_commit_shas(commits.map(&:sha))
+ def unsigned_commit_shas(commits)
+ commit_shas = commits.map(&:sha)
+
+ signature_types
+ .map { |signature| signature.unsigned_commit_shas(commit_shas) }
+ .reduce(&:&)
end
def enqueue_update_signatures
- unsigned = unsigned_x509_shas(limited_commits) & unsigned_gpg_shas(limited_commits)
+ unsigned = unsigned_commit_shas(limited_commits)
return if unsigned.empty?
signable = Gitlab::Git::Commit.shas_with_signatures(project.repository, unsigned)
diff --git a/app/services/groups/group_links/create_service.rb b/app/services/groups/group_links/create_service.rb
index 56ddf3ec0b4..52180c39972 100644
--- a/app/services/groups/group_links/create_service.rb
+++ b/app/services/groups/group_links/create_service.rb
@@ -2,7 +2,7 @@
module Groups
module GroupLinks
- class CreateService < Groups::BaseService
+ class CreateService < ::Groups::BaseService
include GroupLinkable
def initialize(group, shared_with_group, user, params)
diff --git a/app/services/groups/group_links/destroy_service.rb b/app/services/groups/group_links/destroy_service.rb
index 4d74b5f32e2..d1f16775ab3 100644
--- a/app/services/groups/group_links/destroy_service.rb
+++ b/app/services/groups/group_links/destroy_service.rb
@@ -2,7 +2,7 @@
module Groups
module GroupLinks
- class DestroyService < BaseService
+ class DestroyService < ::Groups::BaseService
def execute(one_or_more_links, skip_authorization: false)
unless skip_authorization || group && can?(current_user, :admin_group_member, group)
return error('Not Found', 404)
diff --git a/app/services/groups/group_links/update_service.rb b/app/services/groups/group_links/update_service.rb
index a1411de36d6..244ec2254a8 100644
--- a/app/services/groups/group_links/update_service.rb
+++ b/app/services/groups/group_links/update_service.rb
@@ -2,7 +2,7 @@
module Groups
module GroupLinks
- class UpdateService < BaseService
+ class UpdateService < ::Groups::BaseService
def initialize(group_link, user = nil)
super(group_link.shared_group, user)
diff --git a/app/services/groups/import_export/import_service.rb b/app/services/groups/import_export/import_service.rb
index 4092ded67bc..ac181245986 100644
--- a/app/services/groups/import_export/import_service.rb
+++ b/app/services/groups/import_export/import_service.rb
@@ -8,6 +8,7 @@ module Groups
def initialize(group:, user:)
@group = group
@current_user = user
+ @user_role = user_role
@shared = Gitlab::ImportExport::Shared.new(@group)
@logger = Gitlab::Import::Logger.build
end
@@ -31,6 +32,14 @@ module Groups
if valid_user_permissions? && import_file && restorers.all?(&:restore)
notify_success
+ Gitlab::Tracking.event(
+ self.class.name,
+ 'create',
+ label: 'import_access_level',
+ user: current_user,
+ extra: { user_role: user_role, import_type: 'import_group_from_file' }
+ )
+
group
else
notify_error!
@@ -43,6 +52,15 @@ module Groups
private
+ def user_role
+ # rubocop:disable CodeReuse/ActiveRecord, Style/MultilineTernaryOperator
+ access_level = group.parent ?
+ current_user&.group_members&.find_by(source_id: group.parent&.id)&.access_level :
+ Gitlab::Access::OWNER
+ Gitlab::Access.human_access(access_level)
+ # rubocop:enable CodeReuse/ActiveRecord, Style/MultilineTernaryOperator
+ end
+
def import_file
@import_file ||= Gitlab::ImportExport::FileImporter.import(
importable: group,
diff --git a/app/services/import/base_service.rb b/app/services/import/base_service.rb
index ab3e9c7abba..6b5adcbc39e 100644
--- a/app/services/import/base_service.rb
+++ b/app/services/import/base_service.rb
@@ -35,5 +35,30 @@ module Import
def success(project)
super().merge(project: project, status: :success)
end
+
+ def track_access_level(import_type)
+ Gitlab::Tracking.event(
+ self.class.name,
+ 'create',
+ label: 'import_access_level',
+ user: current_user,
+ extra: { user_role: user_role, import_type: import_type }
+ )
+ end
+
+ def user_role
+ if current_user.id == target_namespace.owner_id
+ 'Owner'
+ else
+ access_level = current_user&.group_members&.find_by(source_id: target_namespace.id)&.access_level
+
+ case access_level
+ when nil
+ 'Not a member'
+ else
+ Gitlab::Access.human_access(access_level)
+ end
+ end
+ end
end
end
diff --git a/app/services/import/bitbucket_server_service.rb b/app/services/import/bitbucket_server_service.rb
index 20f6c987c92..f7f17f1e53e 100644
--- a/app/services/import/bitbucket_server_service.rb
+++ b/app/services/import/bitbucket_server_service.rb
@@ -19,6 +19,8 @@ module Import
project = create_project(credentials)
+ track_access_level('bitbucket')
+
if project.persisted?
success(project)
elsif project.errors[:import_source_disabled].present?
diff --git a/app/services/import/github/gists_import_service.rb b/app/services/import/github/gists_import_service.rb
new file mode 100644
index 00000000000..df1bbe306e7
--- /dev/null
+++ b/app/services/import/github/gists_import_service.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Import
+ module Github
+ class GistsImportService < ::BaseService
+ def initialize(user, params)
+ @current_user = user
+ @params = params
+ end
+
+ def execute
+ return error('Import already in progress', 422) if import_status.started?
+
+ start_import
+ success
+ end
+
+ private
+
+ def import_status
+ @import_status ||= Gitlab::GithubGistsImport::Status.new(current_user.id)
+ end
+
+ def encrypted_token
+ Gitlab::CryptoHelper.aes256_gcm_encrypt(params[:github_access_token])
+ end
+
+ def start_import
+ Gitlab::GithubGistsImport::StartImportWorker.perform_async(current_user.id, encrypted_token)
+ import_status.start!
+ end
+ end
+ end
+end
diff --git a/app/services/import/github_service.rb b/app/services/import/github_service.rb
index a60963e28c7..2378a4b11b1 100644
--- a/app/services/import/github_service.rb
+++ b/app/services/import/github_service.rb
@@ -13,6 +13,7 @@ module Import
return context_error if context_error
project = create_project(access_params, provider)
+ track_access_level('github')
if project.persisted?
store_import_settings(project)
diff --git a/app/services/import/gitlab_projects/file_acquisition_strategies/file_upload.rb b/app/services/import/gitlab_projects/file_acquisition_strategies/file_upload.rb
index 8bee3067d6c..1652bdab5b8 100644
--- a/app/services/import/gitlab_projects/file_acquisition_strategies/file_upload.rb
+++ b/app/services/import/gitlab_projects/file_acquisition_strategies/file_upload.rb
@@ -8,7 +8,7 @@ module Import
validate :uploaded_file
- def initialize(current_user: nil, params:)
+ def initialize(params:, current_user: nil)
@params = params
end
diff --git a/app/services/import/gitlab_projects/file_acquisition_strategies/remote_file.rb b/app/services/import/gitlab_projects/file_acquisition_strategies/remote_file.rb
index ac58711a0ac..e179a14c497 100644
--- a/app/services/import/gitlab_projects/file_acquisition_strategies/remote_file.rb
+++ b/app/services/import/gitlab_projects/file_acquisition_strategies/remote_file.rb
@@ -21,7 +21,7 @@ module Import
# whole condition of this validation:
validates_with RemoteFileValidator, if: -> { validate_aws_s3? || !s3_request? }
- def initialize(current_user: nil, params:)
+ def initialize(params:, current_user: nil)
@params = params
end
diff --git a/app/services/import/gitlab_projects/file_acquisition_strategies/remote_file_s3.rb b/app/services/import/gitlab_projects/file_acquisition_strategies/remote_file_s3.rb
index 5cbca53582d..7599343d4e1 100644
--- a/app/services/import/gitlab_projects/file_acquisition_strategies/remote_file_s3.rb
+++ b/app/services/import/gitlab_projects/file_acquisition_strategies/remote_file_s3.rb
@@ -25,7 +25,7 @@ module Import
# we add an expiration a bit longer to ensure it won't expire during the import.
URL_EXPIRATION = 28.hours.seconds
- def initialize(current_user: nil, params:)
+ def initialize(params:, current_user: nil)
@params = params
end
diff --git a/app/services/incident_management/incidents/create_service.rb b/app/services/incident_management/incidents/create_service.rb
index f44842650b7..49019278871 100644
--- a/app/services/incident_management/incidents/create_service.rb
+++ b/app/services/incident_management/incidents/create_service.rb
@@ -23,7 +23,7 @@ module IncidentManagement
description: description,
issue_type: ISSUE_TYPE,
severity: severity,
- alert_management_alert: alert
+ alert_management_alerts: [alert].compact
},
spam_params: nil
).execute
diff --git a/app/services/incident_management/link_alerts/base_service.rb b/app/services/incident_management/link_alerts/base_service.rb
new file mode 100644
index 00000000000..474a63ab528
--- /dev/null
+++ b/app/services/incident_management/link_alerts/base_service.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module IncidentManagement
+ module LinkAlerts
+ class BaseService < ::BaseProjectService
+ private
+
+ attr_reader :incident
+
+ def allowed?
+ current_user&.can?(:admin_issue, project)
+ end
+
+ def success
+ ServiceResponse.success(payload: { incident: incident })
+ end
+
+ def error(message)
+ ServiceResponse.error(message: message)
+ end
+
+ def error_no_permissions
+ error(_('You have insufficient permissions to manage alerts for this project'))
+ end
+ end
+ end
+end
diff --git a/app/services/incident_management/link_alerts/create_service.rb b/app/services/incident_management/link_alerts/create_service.rb
new file mode 100644
index 00000000000..5e5a974efdd
--- /dev/null
+++ b/app/services/incident_management/link_alerts/create_service.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module IncidentManagement
+ module LinkAlerts
+ class CreateService < BaseService
+ # @param incident [Issue] an incident to link alerts
+ # @param current_user [User]
+ # @param alert_references [[String]] a list of alert references. Can be either a short reference or URL
+ # Examples:
+ # "^alert#IID"
+ # "https://gitlab.com/company/project/-/alert_management/IID/details"
+ def initialize(incident, current_user, alert_references)
+ @incident = incident
+ @current_user = current_user
+ @alert_references = alert_references
+
+ super(project: incident.project, current_user: current_user)
+ end
+
+ def execute
+ return error_no_permissions unless allowed?
+
+ references = extract_alerts_from_references
+ incident.alert_management_alerts << references if references.present?
+
+ success
+ end
+
+ private
+
+ attr_reader :alert_references
+
+ def extract_alerts_from_references
+ text = alert_references.join(' ')
+ extractor = Gitlab::ReferenceExtractor.new(project, current_user)
+ extractor.analyze(text, {})
+
+ extractor.alerts
+ end
+ end
+ end
+end
diff --git a/app/services/incident_management/link_alerts/destroy_service.rb b/app/services/incident_management/link_alerts/destroy_service.rb
new file mode 100644
index 00000000000..baeedaf74b6
--- /dev/null
+++ b/app/services/incident_management/link_alerts/destroy_service.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module IncidentManagement
+ module LinkAlerts
+ class DestroyService < BaseService
+ # @param incident [Issue] an incident to unlink alert from
+ # @param current_user [User]
+ # @param alert [AlertManagement::Alert] an alert to unlink from the incident
+ def initialize(incident, current_user, alert)
+ @incident = incident
+ @current_user = current_user
+ @alert = alert
+
+ super(project: incident.project, current_user: current_user)
+ end
+
+ def execute
+ return error_no_permissions unless allowed?
+
+ incident.alert_management_alerts.delete(alert)
+
+ success
+ end
+
+ private
+
+ attr_reader :alert
+ end
+ end
+end
diff --git a/app/services/incident_management/pager_duty/process_webhook_service.rb b/app/services/incident_management/pager_duty/process_webhook_service.rb
index a49e639ea62..3ce2674616e 100644
--- a/app/services/incident_management/pager_duty/process_webhook_service.rb
+++ b/app/services/incident_management/pager_duty/process_webhook_service.rb
@@ -9,8 +9,8 @@ module IncidentManagement
# https://developer.pagerduty.com/docs/webhooks/webhook-behavior/#size-limit
PAGER_DUTY_PAYLOAD_SIZE_LIMIT = 55.kilobytes
- # https://developer.pagerduty.com/docs/webhooks/v2-overview/#webhook-types
- PAGER_DUTY_PROCESSABLE_EVENT_TYPES = %w(incident.trigger).freeze
+ # https://developer.pagerduty.com/docs/db0fa8c8984fc-overview#event-types
+ PAGER_DUTY_PROCESSABLE_EVENT_TYPES = %w(incident.triggered).freeze
def initialize(project, payload)
super(project: project)
@@ -33,16 +33,18 @@ module IncidentManagement
attr_reader :payload
def process_incidents
- pager_duty_processable_events.each do |event|
- ::IncidentManagement::PagerDuty::ProcessIncidentWorker.perform_async(project.id, event['incident'])
- end
+ event = pager_duty_processable_event
+ return unless event
+
+ ::IncidentManagement::PagerDuty::ProcessIncidentWorker
+ .perform_async(project.id, event['incident'])
end
- def pager_duty_processable_events
- strong_memoize(:pager_duty_processable_events) do
- ::PagerDuty::WebhookPayloadParser
- .call(payload.to_h)
- .filter { |msg| msg['event'].to_s.in?(PAGER_DUTY_PROCESSABLE_EVENT_TYPES) }
+ def pager_duty_processable_event
+ strong_memoize(:pager_duty_processable_event) do
+ event = ::PagerDuty::WebhookPayloadParser.call(payload.to_h)
+
+ event if event['event'].to_s.in?(PAGER_DUTY_PROCESSABLE_EVENT_TYPES)
end
end
diff --git a/app/services/incident_management/timeline_events/base_service.rb b/app/services/incident_management/timeline_events/base_service.rb
index 7168e2fdd38..e0ca4320091 100644
--- a/app/services/incident_management/timeline_events/base_service.rb
+++ b/app/services/incident_management/timeline_events/base_service.rb
@@ -5,6 +5,8 @@ module IncidentManagement
class BaseService
include Gitlab::Utils::UsageData
+ AUTOCREATE_TAGS = [TimelineEventTag::START_TIME_TAG_NAME, TimelineEventTag::END_TIME_TAG_NAME].freeze
+
def allowed?
user&.can?(:admin_incident_management_timeline_event, incident)
end
@@ -24,6 +26,33 @@ module IncidentManagement
def error_in_save(timeline_event)
error(timeline_event.errors.full_messages.to_sentence)
end
+
+ def track_timeline_event(event, project)
+ namespace = project.namespace
+ track_usage_event(event, user.id)
+
+ return unless Feature.enabled?(:route_hll_to_snowplow_phase2, namespace)
+
+ Gitlab::Tracking.event(
+ self.class.to_s,
+ event,
+ project: project,
+ namespace: namespace,
+ user: user,
+ label: 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly',
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event).to_context]
+ )
+ end
+
+ def auto_create_predefined_tags(new_tags)
+ new_tags = new_tags.map(&:downcase)
+
+ tags_to_create = AUTOCREATE_TAGS.select { |tag| tag.downcase.in?(new_tags) }
+
+ tags_to_create.each do |name|
+ project.incident_management_timeline_event_tags.create(name: name)
+ end
+ end
end
end
end
diff --git a/app/services/incident_management/timeline_events/create_service.rb b/app/services/incident_management/timeline_events/create_service.rb
index 71ff5b64515..06e8fc32335 100644
--- a/app/services/incident_management/timeline_events/create_service.rb
+++ b/app/services/incident_management/timeline_events/create_service.rb
@@ -5,7 +5,6 @@ module IncidentManagement
DEFAULT_ACTION = 'comment'
DEFAULT_EDITABLE = false
DEFAULT_AUTO_CREATED = false
- AUTOCREATE_TAGS = [TimelineEventTag::START_TIME_TAG_NAME, TimelineEventTag::END_TIME_TAG_NAME].freeze
class CreateService < TimelineEvents::BaseService
def initialize(incident, user, params)
@@ -106,7 +105,7 @@ module IncidentManagement
create_timeline_event_tag_links(timeline_event, params[:timeline_event_tag_names])
- track_usage_event(:incident_management_timeline_event_created, user.id)
+ track_timeline_event("incident_management_timeline_event_created", project)
success(timeline_event)
else
@@ -153,16 +152,6 @@ module IncidentManagement
IncidentManagement::TimelineEventTagLink.insert_all(tag_links) if tag_links.any?
end
- def auto_create_predefined_tags(new_tags)
- new_tags = new_tags.map(&:downcase)
-
- tags_to_create = AUTOCREATE_TAGS.select { |tag| tag.downcase.in?(new_tags) }
-
- tags_to_create.each do |name|
- project.incident_management_timeline_event_tags.create(name: name)
- end
- end
-
def validate_tags(project, tag_names)
return [] unless tag_names&.any?
diff --git a/app/services/incident_management/timeline_events/destroy_service.rb b/app/services/incident_management/timeline_events/destroy_service.rb
index e1c6bbbdb85..aba46cdda27 100644
--- a/app/services/incident_management/timeline_events/destroy_service.rb
+++ b/app/services/incident_management/timeline_events/destroy_service.rb
@@ -18,7 +18,7 @@ module IncidentManagement
if timeline_event.destroy
add_system_note(incident, user)
- track_usage_event(:incident_management_timeline_event_deleted, user.id)
+ track_timeline_event('incident_management_timeline_event_deleted', project)
success(timeline_event)
else
error_in_save(timeline_event)
diff --git a/app/services/incident_management/timeline_events/update_service.rb b/app/services/incident_management/timeline_events/update_service.rb
index 8d4e29c6857..4949a5a0bd1 100644
--- a/app/services/incident_management/timeline_events/update_service.rb
+++ b/app/services/incident_management/timeline_events/update_service.rb
@@ -13,21 +13,41 @@ module IncidentManagement
def initialize(timeline_event, user, params)
@timeline_event = timeline_event
@incident = timeline_event.incident
+ @project = incident.project
@user = user
@note = params[:note]
@occurred_at = params[:occurred_at]
@validation_context = VALIDATION_CONTEXT
+ @timeline_event_tags = params[:timeline_event_tag_names]
end
def execute
return error_no_permissions unless allowed?
- timeline_event.assign_attributes(update_params)
+ unless timeline_event_tags.nil?
+ auto_create_predefined_tags(timeline_event_tags)
- if timeline_event.save(context: validation_context)
+ # Refetches the tag objects to consider predefined tags as well
+ new_tags = timeline_event
+ .project
+ .incident_management_timeline_event_tags
+ .by_names(timeline_event_tags)
+
+ non_existing_tags = validate_tags(new_tags)
+
+ return error("#{_("Following tags don't exist")}: #{non_existing_tags}") if non_existing_tags.any?
+ end
+
+ begin
+ timeline_event_saved = update_timeline_event_and_event_tags(new_tags)
+ rescue ActiveRecord::RecordInvalid
+ error_in_save(timeline_event)
+ end
+
+ if timeline_event_saved
add_system_note(timeline_event)
- track_usage_event(:incident_management_timeline_event_edited, user.id)
+ track_timeline_event('incident_management_timeline_event_edited', timeline_event.project)
success(timeline_event)
else
error_in_save(timeline_event)
@@ -36,7 +56,18 @@ module IncidentManagement
private
- attr_reader :timeline_event, :incident, :user, :note, :occurred_at, :validation_context
+ attr_reader :timeline_event, :incident, :project, :user,
+ :note, :occurred_at, :validation_context, :timeline_event_tags
+
+ def update_timeline_event_and_event_tags(new_tags)
+ ApplicationRecord.transaction do
+ timeline_event.timeline_event_tags = new_tags unless timeline_event_tags.nil?
+
+ timeline_event.assign_attributes(update_params)
+
+ timeline_event.save!(context: validation_context)
+ end
+ end
def update_params
{ updated_by_user: user, note: note, occurred_at: occurred_at }.compact
@@ -61,6 +92,10 @@ module IncidentManagement
:none
end
+ def validate_tags(new_tags)
+ timeline_event_tags.map(&:downcase) - new_tags.map(&:name).map(&:downcase)
+ end
+
def allowed?
user&.can?(:edit_incident_management_timeline_event, timeline_event)
end
diff --git a/app/services/issuable/discussions_list_service.rb b/app/services/issuable/discussions_list_service.rb
index 7aa0363af01..1e5e37e4e1b 100644
--- a/app/services/issuable/discussions_list_service.rb
+++ b/app/services/issuable/discussions_list_service.rb
@@ -16,7 +16,7 @@ module Issuable
end
def execute
- return Note.none unless can_read_issuable?
+ return Note.none unless can_read_issuable_notes?
notes = NotesFinder.new(current_user, params.merge({ target: issuable, project: issuable.project }))
.execute.with_web_entity_associations.inc_relations_for_view.fresh
@@ -39,12 +39,9 @@ module Issuable
notes = prepare_notes_for_rendering(notes)
- # TODO: optimize this permission check.
- # Given this loads notes on a single issuable and current permission system, we should not have to check
- # permission on every single note. We should be able to check permission on the given issuable or its container,
- # which should result in just one permission check. Perhaps that should also either be passed to NotesFinder or
- # should be done in NotesFinder, which would decide right away if it would need to return no notes
- # or if it should just filter out internal notes.
+ # we need to check the permission on every note, because some system notes for instance can have references to
+ # resources that some user do not have read access, so those notes are filtered out from the list of notes.
+ # see Note#all_referenced_mentionables_allowed?
notes = notes.select { |n| n.readable_by?(current_user) }
Discussion.build_collection(notes, issuable)
@@ -61,10 +58,11 @@ module Issuable
end
end
- def can_read_issuable?
+ def can_read_issuable_notes?
return Ability.allowed?(current_user, :read_security_resource, issuable) if issuable.is_a?(Vulnerability)
- Ability.allowed?(current_user, :"read_#{issuable.to_ability_name}", issuable)
+ Ability.allowed?(current_user, :"read_#{issuable.to_ability_name}", issuable) &&
+ Ability.allowed?(current_user, :read_note, issuable)
end
end
end
diff --git a/app/services/issue_links/create_service.rb b/app/services/issue_links/create_service.rb
index 7f509f3b3e0..80c6af88f21 100644
--- a/app/services/issue_links/create_service.rb
+++ b/app/services/issue_links/create_service.rb
@@ -5,9 +5,7 @@ module IssueLinks
include IncidentManagement::UsageData
def linkable_issuables(issues)
- @linkable_issuables ||= begin
- issues.select { |issue| can?(current_user, :admin_issue_link, issue) }
- end
+ @linkable_issuables ||= issues.select { |issue| can?(current_user, :admin_issue_link, issue) }
end
def previous_related_issuables
diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb
index 28ea6b0ebf8..10407e99715 100644
--- a/app/services/issues/base_service.rb
+++ b/app/services/issues/base_service.rb
@@ -114,6 +114,11 @@ module Issues
Milestones::IssuesCountService.new(milestone).delete_cache
end
+
+ override :allowed_create_params
+ def allowed_create_params(params)
+ super(params).except(:issue_type, :work_item_type_id, :work_item_type)
+ end
end
end
diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb
index da888386e0a..4f6a859e20e 100644
--- a/app/services/issues/close_service.rb
+++ b/app/services/issues/close_service.rb
@@ -56,7 +56,7 @@ module Issues
end
def perform_incident_management_actions(issue)
- resolve_alert(issue)
+ resolve_alerts(issue)
resolve_incident(issue)
end
@@ -71,12 +71,17 @@ module Issues
SystemNoteService.change_status(issue, issue.project, current_user, issue.state, current_commit)
end
- def resolve_alert(issue)
- return unless alert = issue.alert_management_alert
+ def resolve_alerts(issue)
+ issue.alert_management_alerts.each { |alert| resolve_alert(alert) }
+ end
+
+ def resolve_alert(alert)
return if alert.resolved?
+ issue = alert.issue
+
if alert.resolve
- SystemNoteService.change_alert_status(alert, current_user, " by closing incident #{issue.to_reference(project)}")
+ SystemNoteService.change_alert_status(alert, User.alert_bot, " because #{current_user.to_reference} closed incident #{issue.to_reference(project)}")
else
Gitlab::AppLogger.warn(
message: 'Cannot resolve an associated Alert Management alert',
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index 89b35bbab24..afad8d0c6bf 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -13,7 +13,7 @@ module Issues
# spam_checking is likely to be necessary. However, if there is not a request available in scope
# in the caller (for example, an issue created via email) and the required arguments to the
# SpamParams constructor are not otherwise available, spam_params: must be explicitly passed as nil.
- def initialize(project:, current_user: nil, params: {}, spam_params:, build_service: nil)
+ def initialize(project:, spam_params:, current_user: nil, params: {}, build_service: nil)
@extra_params = params.delete(:extra_params) || {}
super(project: project, current_user: current_user, params: params)
@spam_params = spam_params
diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb
index 6366ff4076b..f7f7d85611b 100644
--- a/app/services/issues/move_service.rb
+++ b/app/services/issues/move_service.rb
@@ -19,6 +19,7 @@ module Issues
# to receive service desk emails on the new moved issue.
update_service_desk_sent_notifications
+ copy_email_participants
queue_copy_designs
new_entity
@@ -49,6 +50,18 @@ module Issues
.sent_notifications.update_all(project_id: new_entity.project_id, noteable_id: new_entity.id)
end
+ def copy_email_participants
+ new_attributes = { id: nil, issue_id: new_entity.id }
+
+ new_participants = original_entity.issue_email_participants.dup
+
+ new_participants.each do |participant|
+ participant.assign_attributes(new_attributes)
+ end
+
+ IssueEmailParticipant.bulk_insert!(new_participants)
+ end
+
override :update_old_entity
def update_old_entity
super
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 0aed9e3ba40..71cc5581ae6 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -146,7 +146,7 @@ module Issues
# don't enqueue immediately to prevent todos removal in case of a mistake
TodosDestroyer::ConfidentialIssueWorker.perform_in(Todo::WAIT_FOR_DELETE, issue.id) if issue.confidential?
create_confidentiality_note(issue)
- track_usage_event(:incident_management_incident_change_confidential, current_user.id)
+ track_incident_action(current_user, issue, :incident_change_confidential)
end
end
diff --git a/app/services/jira_connect/create_asymmetric_jwt_service.rb b/app/services/jira_connect/create_asymmetric_jwt_service.rb
index 71aba6feddd..0f24128c20b 100644
--- a/app/services/jira_connect/create_asymmetric_jwt_service.rb
+++ b/app/services/jira_connect/create_asymmetric_jwt_service.rb
@@ -4,10 +4,11 @@ module JiraConnect
class CreateAsymmetricJwtService
ARGUMENT_ERROR_MESSAGE = 'jira_connect_installation is not a proxy installation'
- def initialize(jira_connect_installation)
+ def initialize(jira_connect_installation, event: :installed)
raise ArgumentError, ARGUMENT_ERROR_MESSAGE unless jira_connect_installation.proxy?
@jira_connect_installation = jira_connect_installation
+ @event = event
end
def execute
@@ -30,12 +31,18 @@ module JiraConnect
def qsh_claim
Atlassian::Jwt.create_query_string_hash(
- @jira_connect_installation.audience_installed_event_url,
+ audience_event_url,
'POST',
@jira_connect_installation.audience_url
)
end
+ def audience_event_url
+ return @jira_connect_installation.audience_uninstalled_event_url if @event == :uninstalled
+
+ @jira_connect_installation.audience_installed_event_url
+ end
+
def private_key
@private_key ||= OpenSSL::PKey::RSA.generate(3072)
end
diff --git a/app/services/jira_connect_installations/proxy_lifecycle_event_service.rb b/app/services/jira_connect_installations/proxy_lifecycle_event_service.rb
new file mode 100644
index 00000000000..d94d9e1324e
--- /dev/null
+++ b/app/services/jira_connect_installations/proxy_lifecycle_event_service.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+module JiraConnectInstallations
+ class ProxyLifecycleEventService
+ SUPPOERTED_EVENTS = %i[installed uninstalled].freeze
+
+ def self.execute(installation, event, instance_url)
+ new(installation, event, instance_url).execute
+ end
+
+ def initialize(installation, event, instance_url)
+ # To ensure the event is sent to the right instance, this makes
+ # a copy of the installation and assigns the instance_url
+ #
+ # The installation might be modified already with a new instance_url.
+ # This can be the case for an uninstalled event.
+ # The installation is updated first, and the uninstalled event has to be sent to
+ # the old instance_url.
+ @installation = installation.dup
+ @installation.instance_url = instance_url
+
+ @event = event.to_sym
+
+ raise(ArgumentError, "Unknown event '#{@event}'") unless SUPPOERTED_EVENTS.include?(@event)
+ end
+
+ def execute
+ result = send_hook
+
+ return ServiceResponse.new(status: :success) if result.code == 200
+
+ log_unsuccessful_response(result.code, result.body)
+
+ ServiceResponse.error(message: { type: :response_error, code: result.code })
+ rescue *Gitlab::HTTP::HTTP_ERRORS => error
+ ServiceResponse.error(message: { type: :network_error, message: error.message })
+ end
+
+ private
+
+ attr_reader :installation, :event
+
+ def send_hook
+ Gitlab::HTTP.post(hook_uri, body: body)
+ end
+
+ def hook_uri
+ case event
+ when :installed
+ installation.audience_installed_event_url
+ when :uninstalled
+ installation.audience_uninstalled_event_url
+ end
+ end
+
+ def body
+ return base_body unless event == :installed
+
+ base_body.merge(installed_body)
+ end
+
+ def base_body
+ {
+ clientKey: installation.client_key,
+ jwt: jwt_token,
+ eventType: event
+ }
+ end
+
+ def installed_body
+ {
+ sharedSecret: installation.shared_secret,
+ baseUrl: installation.base_url
+ }
+ end
+
+ def jwt_token
+ @jwt_token ||= JiraConnect::CreateAsymmetricJwtService.new(@installation, event: event).execute
+ end
+
+ def log_unsuccessful_response(status_code, body)
+ Gitlab::IntegrationsLogger.info(
+ integration: 'JiraConnect',
+ message: 'Proxy lifecycle event received error response',
+ event_type: event,
+ status_code: status_code,
+ body: body
+ )
+ end
+ end
+end
diff --git a/app/services/jira_connect_installations/update_service.rb b/app/services/jira_connect_installations/update_service.rb
new file mode 100644
index 00000000000..b2b6f2a91f2
--- /dev/null
+++ b/app/services/jira_connect_installations/update_service.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+module JiraConnectInstallations
+ class UpdateService
+ def self.execute(installation, update_params)
+ new(installation, update_params).execute
+ end
+
+ def initialize(installation, update_params)
+ @installation = installation
+ @update_params = update_params
+ end
+
+ def execute
+ return update_error unless @installation.update(@update_params)
+
+ if @installation.instance_url?
+ hook_result = ProxyLifecycleEventService.execute(@installation, :installed, @installation.instance_url)
+
+ if instance_url_changed? && hook_result.error?
+ @installation.update!(instance_url: @installation.instance_url_before_last_save)
+
+ return instance_installation_creation_error(hook_result.message)
+ end
+ end
+
+ send_uninstalled_hook if instance_url_changed?
+
+ ServiceResponse.new(status: :success)
+ end
+
+ private
+
+ def instance_url_changed?
+ @installation.instance_url_before_last_save != @installation.instance_url
+ end
+
+ def send_uninstalled_hook
+ return if @installation.instance_url_before_last_save.blank?
+
+ JiraConnect::SendUninstalledHookWorker.perform_async(
+ @installation.id,
+ @installation.instance_url_before_last_save
+ )
+ end
+
+ def instance_installation_creation_error(error_message)
+ message = if error_message[:type] == :response_error
+ "Could not be installed on the instance. Error response code #{error_message[:code]}"
+ else
+ 'Could not be installed on the instance. Network error'
+ end
+
+ ServiceResponse.error(message: { instance_url: [message] })
+ end
+
+ def update_error
+ ServiceResponse.error(message: @installation.errors)
+ end
+ end
+end
diff --git a/app/services/jira_import/start_import_service.rb b/app/services/jira_import/start_import_service.rb
index 9cd56cf339e..ef376e2f24a 100644
--- a/app/services/jira_import/start_import_service.rb
+++ b/app/services/jira_import/start_import_service.rb
@@ -73,7 +73,7 @@ module JiraImport
jira_imports_for_project = project.jira_imports.by_jira_project_key(jira_project_key).size + 1
title = "jira-import::#{jira_project_key}-#{jira_imports_for_project}"
description = "Label for issues that were imported from Jira on #{import_start_time.strftime('%Y-%m-%d %H:%M:%S')}"
- color = "#{::Gitlab::Color.color_for(title)}"
+ color = ::Gitlab::Color.color_for(title).to_s
{ title: title, description: description, color: color }
end
diff --git a/app/services/markup/rendering_service.rb b/app/services/markup/rendering_service.rb
index c4abbb6b5b0..cd89c170efa 100644
--- a/app/services/markup/rendering_service.rb
+++ b/app/services/markup/rendering_service.rb
@@ -2,12 +2,6 @@
module Markup
class RenderingService
- # Let's increase the render timeout
- # For a smaller one, a test that renders the blob content statically fails
- # We can consider removing this custom timeout when markup_rendering_timeout FF is removed:
- # https://gitlab.com/gitlab-org/gitlab/-/issues/365358
- RENDER_TIMEOUT = 5.seconds
-
def initialize(text, file_name: nil, context: {}, postprocess_context: {})
@text = text
@file_name = file_name
@@ -19,7 +13,7 @@ module Markup
return '' unless text.present?
return context.delete(:rendered) if context.has_key?(:rendered)
- html = file_name ? markup_unsafe : markdown_unsafe
+ html = markup_unsafe
return '' unless html.present?
@@ -29,27 +23,17 @@ module Markup
private
def markup_unsafe
- markup = proc do
- if Gitlab::MarkupHelper.gitlab_markdown?(file_name)
- markdown_unsafe
- elsif Gitlab::MarkupHelper.asciidoc?(file_name)
- asciidoc_unsafe
- elsif Gitlab::MarkupHelper.plain?(file_name)
- plain_unsafe
- else
- other_markup_unsafe
- end
- end
-
- if Feature.enabled?(:markup_rendering_timeout, context[:project])
- Gitlab::RenderTimeout.timeout(foreground: RENDER_TIMEOUT, &markup)
+ return markdown_unsafe unless file_name
+
+ if Gitlab::MarkupHelper.gitlab_markdown?(file_name)
+ markdown_unsafe
+ elsif Gitlab::MarkupHelper.asciidoc?(file_name)
+ asciidoc_unsafe
+ elsif Gitlab::MarkupHelper.plain?(file_name)
+ plain_unsafe
else
- markup.call
+ other_markup_unsafe
end
- rescue StandardError => e
- Gitlab::ErrorTracking.track_exception(e, project_id: context[:project]&.id, file_name: file_name)
-
- ActionController::Base.helpers.simple_format(text)
end
def markdown_unsafe
diff --git a/app/services/members/destroy_service.rb b/app/services/members/destroy_service.rb
index f18269454e3..5afc13701e0 100644
--- a/app/services/members/destroy_service.rb
+++ b/app/services/members/destroy_service.rb
@@ -15,15 +15,15 @@ module Members
@skip_auth = skip_authorization
last_owner = true
- in_lock("delete_members:#{member.source.class}:#{member.source.id}") do
+ in_lock("delete_members:#{member.source.class}:#{member.source.id}", sleep_sec: 0.1.seconds) do
break if member.is_a?(GroupMember) && member.source.last_owner?(member.user)
last_owner = false
member.destroy
- member.user&.invalidate_cache_counts
end
unless last_owner
+ member.user&.invalidate_cache_counts
delete_member_associations(member, skip_subresources, unassign_issuables)
end
diff --git a/app/services/merge_requests/after_create_service.rb b/app/services/merge_requests/after_create_service.rb
index 20b32dbc2a0..9e39aa94246 100644
--- a/app/services/merge_requests/after_create_service.rb
+++ b/app/services/merge_requests/after_create_service.rb
@@ -22,7 +22,7 @@ module MergeRequests
def prepare_merge_request(merge_request)
event_service.open_mr(merge_request, current_user)
- merge_request_activity_counter.track_create_mr_action(user: current_user)
+ merge_request_activity_counter.track_create_mr_action(user: current_user, merge_request: merge_request)
merge_request_activity_counter.track_mr_including_ci_config(user: current_user, merge_request: merge_request)
notification_service.new_merge_request(merge_request, current_user)
diff --git a/app/services/merge_requests/approval_service.rb b/app/services/merge_requests/approval_service.rb
index 72f398ce415..8560a15b7c4 100644
--- a/app/services/merge_requests/approval_service.rb
+++ b/app/services/merge_requests/approval_service.rb
@@ -13,6 +13,7 @@ module MergeRequests
merge_request_activity_counter.track_approve_mr_action(user: current_user, merge_request: merge_request)
trigger_merge_request_merge_status_updated(merge_request)
trigger_merge_request_reviewers_updated(merge_request)
+ trigger_merge_request_approval_state_updated(merge_request)
# Approval side effects (things not required to be done immediately but
# should happen after a successful approval) should be done asynchronously
diff --git a/app/services/merge_requests/assign_issues_service.rb b/app/services/merge_requests/assign_issues_service.rb
index f016c16e816..c107280efb1 100644
--- a/app/services/merge_requests/assign_issues_service.rb
+++ b/app/services/merge_requests/assign_issues_service.rb
@@ -3,15 +3,13 @@
module MergeRequests
class AssignIssuesService < BaseProjectService
def assignable_issues
- @assignable_issues ||= begin
- if current_user == merge_request.author
- closes_issues.select do |issue|
- !issue.is_a?(ExternalIssue) && !issue.assignees.present? && can?(current_user, :admin_issue, issue)
- end
- else
- []
- end
- end
+ @assignable_issues ||= if current_user == merge_request.author
+ closes_issues.select do |issue|
+ !issue.is_a?(ExternalIssue) && !issue.assignees.present? && can?(current_user, :admin_issue, issue)
+ end
+ else
+ []
+ end
end
def execute
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index e7ab2c062ee..468cadb03c7 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -59,6 +59,8 @@ module MergeRequests
merge_request_activity_counter.track_users_review_requested(users: new_reviewers)
merge_request_activity_counter.track_reviewers_changed_action(user: current_user)
trigger_merge_request_reviewers_updated(merge_request)
+
+ capture_suggested_reviewers_accepted(merge_request)
end
def cleanup_environments(merge_request)
@@ -137,6 +139,7 @@ module MergeRequests
end
filter_reviewer(merge_request)
+ filter_suggested_reviewers
end
def filter_reviewer(merge_request)
@@ -163,6 +166,10 @@ module MergeRequests
end
end
+ def filter_suggested_reviewers
+ # Implemented in EE
+ end
+
def merge_request_metrics_service(merge_request)
MergeRequestMetricsService.new(merge_request.metrics)
end
@@ -253,6 +260,14 @@ module MergeRequests
def trigger_merge_request_merge_status_updated(merge_request)
GraphqlTriggers.merge_request_merge_status_updated(merge_request)
end
+
+ def trigger_merge_request_approval_state_updated(merge_request)
+ GraphqlTriggers.merge_request_approval_state_updated(merge_request)
+ end
+
+ def capture_suggested_reviewers_accepted(merge_request)
+ # Implemented in EE
+ end
end
end
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index cc786ac02bd..b9a681f29db 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -224,6 +224,7 @@ module MergeRequests
#
def assign_title_and_description
assign_description_from_repository_template
+ replace_variables_in_description
assign_title_and_description_from_commits
merge_request.title ||= title_from_issue if target_project.issues_enabled? || target_project.external_issue_tracker
merge_request.title ||= source_branch.titleize.humanize
@@ -318,6 +319,15 @@ module MergeRequests
merge_request.description = repository_template.content
end
+ def replace_variables_in_description
+ return unless merge_request.description.present?
+
+ merge_request.description = ::Gitlab::MergeRequests::MessageGenerator.new(
+ merge_request: merge_request,
+ current_user: current_user
+ ).new_mr_description
+ end
+
def issue_iid
strong_memoize(:issue_iid) do
@params_issue_iid || begin
diff --git a/app/services/merge_requests/create_service.rb b/app/services/merge_requests/create_service.rb
index 04d08f257f1..8fa80dc3513 100644
--- a/app/services/merge_requests/create_service.rb
+++ b/app/services/merge_requests/create_service.rb
@@ -39,7 +39,7 @@ module MergeRequests
# open while the Gitaly RPC waits. To avoid an idle in transaction
# timeout, we do this before we attempt to save the merge request.
- if Feature.enabled?(:async_merge_request_diff_creation, current_user)
+ if Feature.enabled?(:async_merge_request_diff_creation, merge_request.target_project)
merge_request.skip_ensure_merge_request_diff = true
else
merge_request.eager_fetch_ref!
diff --git a/app/services/merge_requests/push_options_handler_service.rb b/app/services/merge_requests/push_options_handler_service.rb
index aa52349b0ee..711978dc3f7 100644
--- a/app/services/merge_requests/push_options_handler_service.rb
+++ b/app/services/merge_requests/push_options_handler_service.rb
@@ -7,7 +7,7 @@ module MergeRequests
attr_reader :errors, :changes,
:push_options, :target_project
- def initialize(project:, current_user:, params: {}, changes:, push_options:)
+ def initialize(project:, current_user:, changes:, push_options:, params: {})
super(project: project, current_user: current_user, params: params)
@target_project = @project.default_merge_request_target
diff --git a/app/services/merge_requests/remove_approval_service.rb b/app/services/merge_requests/remove_approval_service.rb
index 8387c23fe3f..c0bb257eda6 100644
--- a/app/services/merge_requests/remove_approval_service.rb
+++ b/app/services/merge_requests/remove_approval_service.rb
@@ -19,6 +19,7 @@ module MergeRequests
merge_request_activity_counter.track_unapprove_mr_action(user: current_user)
trigger_merge_request_merge_status_updated(merge_request)
trigger_merge_request_reviewers_updated(merge_request)
+ trigger_merge_request_approval_state_updated(merge_request)
end
success
diff --git a/app/services/metrics/dashboard/grafana_metric_embed_service.rb b/app/services/metrics/dashboard/grafana_metric_embed_service.rb
index e94c8d92c3a..26ccded45f8 100644
--- a/app/services/metrics/dashboard/grafana_metric_embed_service.rb
+++ b/app/services/metrics/dashboard/grafana_metric_embed_service.rb
@@ -51,8 +51,7 @@ module Metrics
# being passed to #get_dashboard (which accepts none)
::Metrics::Dashboard::BaseService
.instance_method(:get_dashboard)
- .bind(self)
- .call() # rubocop:disable Style/MethodCallWithoutArgsParentheses
+ .bind_call(self)
end
def cache_key(*args)
diff --git a/app/services/ml/experiment_tracking/candidate_repository.rb b/app/services/ml/experiment_tracking/candidate_repository.rb
index b6f87995185..1dbeb30145b 100644
--- a/app/services/ml/experiment_tracking/candidate_repository.rb
+++ b/app/services/ml/experiment_tracking/candidate_repository.rb
@@ -14,11 +14,15 @@ module Ml
::Ml::Candidate.with_project_id_and_iid(project.id, iid)
end
- def create!(experiment, start_time)
- experiment.candidates.create!(
+ def create!(experiment, start_time, tags = nil)
+ candidate = experiment.candidates.create!(
user: user,
start_time: start_time || 0
)
+
+ add_tags(candidate, tags)
+
+ candidate
end
def update(candidate, status, end_time)
@@ -41,36 +45,21 @@ module Ml
candidate.params.create!(name: name, value: value)
end
- def add_metrics(candidate, metric_definitions)
- return unless candidate.present?
-
- metrics = metric_definitions.map do |metric|
- {
- candidate_id: candidate.id,
- name: metric[:key],
- value: metric[:value],
- tracked_at: metric[:timestamp],
- step: metric[:step],
- **timestamps
- }
- end
+ def add_tag!(candidate, name, value)
+ candidate.metadata.create!(name: name, value: value)
+ end
- ::Ml::CandidateMetric.insert_all(metrics, returning: false) unless metrics.empty?
+ def add_metrics(candidate, metric_definitions)
+ extra_keys = { tracked_at: :timestamp, step: :step }
+ insert_many(candidate, metric_definitions, ::Ml::CandidateMetric, extra_keys)
end
def add_params(candidate, param_definitions)
- return unless candidate.present?
-
- parameters = param_definitions.map do |p|
- {
- candidate_id: candidate.id,
- name: p[:key],
- value: p[:value],
- **timestamps
- }
- end
+ insert_many(candidate, param_definitions, ::Ml::CandidateParam)
+ end
- ::Ml::CandidateParam.insert_all(parameters, returning: false) unless parameters.empty?
+ def add_tags(candidate, tag_definitions)
+ insert_many(candidate, tag_definitions, ::Ml::CandidateMetadata)
end
private
@@ -80,6 +69,22 @@ module Ml
{ created_at: current_time, updated_at: current_time }
end
+
+ def insert_many(candidate, definitions, entity_class, extra_keys = {})
+ return unless candidate.present? && definitions.present?
+
+ entities = definitions.map do |d|
+ {
+ candidate_id: candidate.id,
+ name: d[:key],
+ value: d[:value],
+ **extra_keys.transform_values { |old_key| d[old_key] },
+ **timestamps
+ }
+ end
+
+ entity_class.insert_all(entities, returning: false) unless entities.empty?
+ end
end
end
end
diff --git a/app/services/ml/experiment_tracking/experiment_repository.rb b/app/services/ml/experiment_tracking/experiment_repository.rb
index 891674adc2a..90f4cf1abec 100644
--- a/app/services/ml/experiment_tracking/experiment_repository.rb
+++ b/app/services/ml/experiment_tracking/experiment_repository.rb
@@ -20,10 +20,43 @@ module Ml
::Ml::Experiment.by_project_id(project.id)
end
- def create!(name)
- ::Ml::Experiment.create!(name: name,
- user: user,
- project: project)
+ def create!(name, tags = nil)
+ experiment = ::Ml::Experiment.create!(name: name,
+ user: user,
+ project: project)
+
+ add_tags(experiment, tags)
+
+ experiment
+ end
+
+ def add_tag!(experiment, key, value)
+ return unless experiment.present?
+
+ experiment.metadata.create!(name: key, value: value)
+ end
+
+ private
+
+ def timestamps
+ current_time = Time.zone.now
+
+ { created_at: current_time, updated_at: current_time }
+ end
+
+ def add_tags(experiment, tag_definitions)
+ return unless experiment.present? && tag_definitions.present?
+
+ entities = tag_definitions.map do |d|
+ {
+ experiment_id: experiment.id,
+ name: d[:key],
+ value: d[:value],
+ **timestamps
+ }
+ end
+
+ ::Ml::ExperimentMetadata.insert_all(entities, returning: false) unless entities.empty?
end
end
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 660d9891e46..550bd6d4c55 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -98,10 +98,10 @@ class NotificationService
end
# Notify the user when one of their personal access tokens is revoked
- def access_token_revoked(user, token_name)
+ def access_token_revoked(user, token_name, source = nil)
return unless user.can?(:receive_notifications)
- mailer.access_token_revoked_email(user, token_name).deliver_later
+ mailer.access_token_revoked_email(user, token_name, source).deliver_later
end
# Notify the user when at least one of their ssh key has expired today
@@ -495,13 +495,7 @@ class NotificationService
def new_access_request(member)
return true unless member.notifiable?(:subscription)
- source = member.source
-
- recipients = source.access_request_approvers_to_be_notified
-
- if fallback_to_group_access_request_approvers?(recipients, source)
- recipients = source.group.access_request_approvers_to_be_notified
- end
+ recipients = member.source.access_request_approvers_to_be_notified
return true if recipients.empty?
@@ -959,12 +953,6 @@ class NotificationService
mailer.member_access_requested_email(member.real_source_type, member.id, recipient.user.id).deliver_later
end
- def fallback_to_group_access_request_approvers?(recipients, source)
- return false if recipients.present?
-
- source.respond_to?(:group) && source.group
- end
-
def warn_skipping_notifications(user, object)
Gitlab::AppLogger.warn(message: "Skipping sending notifications", user: user.id, klass: object.class.to_s, object_id: object.id)
end
diff --git a/app/services/packages/debian/process_package_file_service.rb b/app/services/packages/debian/process_package_file_service.rb
new file mode 100644
index 00000000000..59e8ac3425b
--- /dev/null
+++ b/app/services/packages/debian/process_package_file_service.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+module Packages
+ module Debian
+ class ProcessPackageFileService
+ include ExclusiveLeaseGuard
+ include Gitlab::Utils::StrongMemoize
+
+ SOURCE_FIELD_SPLIT_REGEX = /[ ()]/.freeze
+ # used by ExclusiveLeaseGuard
+ DEFAULT_LEASE_TIMEOUT = 1.hour.to_i.freeze
+
+ def initialize(package_file, creator, distribution_name, component_name)
+ @package_file = package_file
+ @creator = creator
+ @distribution_name = distribution_name
+ @component_name = component_name
+ end
+
+ def execute
+ try_obtain_lease do
+ validate!
+
+ @package_file.transaction do
+ update_file_metadata
+ end
+
+ ::Packages::Debian::GenerateDistributionWorker.perform_async(:project, package.debian_distribution.id)
+ end
+ end
+
+ private
+
+ def validate!
+ raise ArgumentError, 'package file without Debian metadata' unless @package_file.debian_file_metadatum
+ raise ArgumentError, 'already processed package file' unless @package_file.debian_file_metadatum.unknown?
+
+ return if file_metadata[:file_type] == :deb || file_metadata[:file_type] == :udeb
+
+ raise ArgumentError, "invalid package file type: #{file_metadata[:file_type]}"
+ end
+
+ def update_file_metadata
+ ::Packages::UpdatePackageFileService.new(@package_file, package_id: package.id)
+ .execute
+
+ # Force reload from database, as package has changed
+ @package_file.reload_package
+
+ @package_file.debian_file_metadatum.update!(
+ file_type: file_metadata[:file_type],
+ component: @component_name,
+ architecture: file_metadata[:architecture],
+ fields: file_metadata[:fields]
+ )
+ end
+
+ def package
+ strong_memoize(:package) do
+ package_name = file_metadata[:fields]['Package']
+ package_version = file_metadata[:fields]['Version']
+
+ if file_metadata[:fields]['Source']
+ # "sample" or "sample (1.2.3~alpha2)"
+ source_field_parts = file_metadata[:fields]['Source'].split(SOURCE_FIELD_SPLIT_REGEX)
+ package_name = source_field_parts[0]
+ package_version = source_field_parts[2] || package_version
+ end
+
+ params = {
+ 'name': package_name,
+ 'version': package_version,
+ 'distribution_name': @distribution_name
+ }
+ response = Packages::Debian::FindOrCreatePackageService.new(project, @creator, params).execute
+ response.payload[:package]
+ end
+ end
+
+ def file_metadata
+ strong_memoize(:metadata) do
+ ::Packages::Debian::ExtractMetadataService.new(@package_file).execute
+ end
+ end
+
+ def project
+ @package_file.package.project
+ end
+
+ # used by ExclusiveLeaseGuard
+ def lease_key
+ "packages:debian:process_package_file_service:package_file:#{@package_file.id}"
+ end
+
+ # used by ExclusiveLeaseGuard
+ def lease_timeout
+ DEFAULT_LEASE_TIMEOUT
+ end
+ end
+ end
+end
diff --git a/app/services/packages/rpm/parse_package_service.rb b/app/services/packages/rpm/parse_package_service.rb
index 18b916a9d8b..d2751c77c5b 100644
--- a/app/services/packages/rpm/parse_package_service.rb
+++ b/app/services/packages/rpm/parse_package_service.rb
@@ -49,8 +49,8 @@ module Packages
end
def extract_static_attributes
- STATIC_ATTRIBUTES.each_with_object({}) do |attribute, hash|
- hash[attribute] = package_tags[attribute]
+ STATIC_ATTRIBUTES.index_with do |attribute|
+ package_tags[attribute]
end
end
diff --git a/app/services/pages_domains/obtain_lets_encrypt_certificate_service.rb b/app/services/pages_domains/obtain_lets_encrypt_certificate_service.rb
index ca5df4ce017..1733021cbb5 100644
--- a/app/services/pages_domains/obtain_lets_encrypt_certificate_service.rb
+++ b/app/services/pages_domains/obtain_lets_encrypt_certificate_service.rb
@@ -28,7 +28,7 @@ module PagesDomains
api_order = ::Gitlab::LetsEncrypt::Client.new.load_order(acme_order.url)
- # https://tools.ietf.org/html/rfc8555#section-7.1.6 - statuses diagram
+ # https://www.rfc-editor.org/rfc/rfc8555#section-7.1.6 - statuses diagram
case api_order.status
when 'ready'
api_order.request_certificate(private_key: acme_order.private_key, domain: pages_domain.domain)
diff --git a/app/services/pages_domains/retry_acme_order_service.rb b/app/services/pages_domains/retry_acme_order_service.rb
index ef3d8ce0b67..6251c9d3615 100644
--- a/app/services/pages_domains/retry_acme_order_service.rb
+++ b/app/services/pages_domains/retry_acme_order_service.rb
@@ -15,7 +15,26 @@ module PagesDomains
pages_domain.update!(auto_ssl_failed: false)
end
- PagesDomainSslRenewalWorker.perform_async(pages_domain.id) if updated
+ return unless updated
+
+ PagesDomainSslRenewalWorker.perform_async(pages_domain.id)
+
+ publish_event(pages_domain)
+ end
+
+ private
+
+ def publish_event(domain)
+ event = PagesDomainUpdatedEvent.new(
+ data: {
+ project_id: domain.project.id,
+ namespace_id: domain.project.namespace_id,
+ root_namespace_id: domain.project.root_namespace.id,
+ domain: domain.domain
+ }
+ )
+
+ Gitlab::EventStore.publish(event)
end
end
end
diff --git a/app/services/personal_access_tokens/revoke_service.rb b/app/services/personal_access_tokens/revoke_service.rb
index 5371b6c91ef..bb5edc27340 100644
--- a/app/services/personal_access_tokens/revoke_service.rb
+++ b/app/services/personal_access_tokens/revoke_service.rb
@@ -4,10 +4,13 @@ module PersonalAccessTokens
class RevokeService < BaseService
attr_reader :token, :current_user, :group
- def initialize(current_user = nil, token: nil, group: nil)
+ VALID_SOURCES = %w[secret_detection].freeze
+
+ def initialize(current_user = nil, token: nil, group: nil, source: nil)
@current_user = current_user
@token = token
@group = group
+ @source = source
end
def execute
@@ -15,7 +18,7 @@ module PersonalAccessTokens
if token.revoke!
log_event
- notification_service.access_token_revoked(token.user, token.name)
+ notification_service.access_token_revoked(token.user, token.name, @source)
ServiceResponse.success(message: success_message)
else
ServiceResponse.error(message: error_message)
@@ -33,11 +36,24 @@ module PersonalAccessTokens
end
def revocation_permitted?
- Ability.allowed?(current_user, :revoke_token, token)
+ if current_user
+ Ability.allowed?(current_user, :revoke_token, token)
+ else
+ source && VALID_SOURCES.include?(source)
+ end
+ end
+
+ def source
+ current_user&.username || @source
end
def log_event
- Gitlab::AppLogger.info("PAT REVOCATION: revoked_by: '#{current_user.username}', revoked_for: '#{token.user.username}', token_id: '#{token.id}'")
+ Gitlab::AppLogger.info(
+ class: self.class.name,
+ message: "PAT Revoked",
+ revoked_by: source,
+ revoked_for: token.user.username,
+ token_id: token.id)
end
end
end
diff --git a/app/services/projects/batch_forks_count_service.rb b/app/services/projects/batch_forks_count_service.rb
index 6467744a435..78663d8dad5 100644
--- a/app/services/projects/batch_forks_count_service.rb
+++ b/app/services/projects/batch_forks_count_service.rb
@@ -7,11 +7,9 @@ module Projects
class BatchForksCountService < Projects::BatchCountService
# rubocop: disable CodeReuse/ActiveRecord
def global_count
- @global_count ||= begin
- count_service.query(project_ids)
+ @global_count ||= count_service.query(project_ids)
.group(:forked_from_project_id)
.count
- end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/services/projects/batch_open_issues_count_service.rb b/app/services/projects/batch_open_issues_count_service.rb
index d6ff2291af8..c396d7c0cfc 100644
--- a/app/services/projects/batch_open_issues_count_service.rb
+++ b/app/services/projects/batch_open_issues_count_service.rb
@@ -7,9 +7,7 @@ module Projects
class BatchOpenIssuesCountService < Projects::BatchCountService
# rubocop: disable CodeReuse/ActiveRecord
def global_count
- @global_count ||= begin
- count_service.query(project_ids).group(:project_id).count
- end
+ @global_count ||= count_service.query(project_ids).group(:project_id).count
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/services/projects/container_repository/cleanup_tags_base_service.rb b/app/services/projects/container_repository/cleanup_tags_base_service.rb
index 5393c2c080d..45557d03502 100644
--- a/app/services/projects/container_repository/cleanup_tags_base_service.rb
+++ b/app/services/projects/container_repository/cleanup_tags_base_service.rb
@@ -6,6 +6,8 @@ module Projects
private
def filter_out_latest!(tags)
+ return unless keep_latest
+
tags.reject!(&:latest?)
end
@@ -84,6 +86,10 @@ module Projects
params['keep_n']
end
+ def keep_latest
+ params.fetch('keep_latest', true)
+ end
+
def project
container_repository.project
end
diff --git a/app/services/projects/container_repository/destroy_service.rb b/app/services/projects/container_repository/destroy_service.rb
index 83bb8624bba..6db6b449671 100644
--- a/app/services/projects/container_repository/destroy_service.rb
+++ b/app/services/projects/container_repository/destroy_service.rb
@@ -3,12 +3,46 @@
module Projects
module ContainerRepository
class DestroyService < BaseService
- def execute(container_repository)
+ CLEANUP_TAGS_SERVICE_PARAMS = {
+ 'name_regex_delete' => '.*',
+ 'container_expiration_policy' => true, # to avoid permissions checks
+ 'keep_latest' => false
+ }.freeze
+
+ def execute(container_repository, disable_timeout: true)
return false unless can?(current_user, :update_container_image, project)
# Delete tags outside of the transaction to avoid hitting an idle-in-transaction timeout
- container_repository.delete_tags!
- container_repository.delete_failed! unless container_repository.destroy
+ unless delete_tags(container_repository, disable_timeout) &&
+ destroy_container_repository(container_repository)
+ container_repository.delete_failed!
+ end
+ end
+
+ private
+
+ def delete_tags(container_repository, disable_timeout)
+ service = Projects::ContainerRepository::CleanupTagsService.new(
+ container_repository: container_repository,
+ params: CLEANUP_TAGS_SERVICE_PARAMS.merge('disable_timeout' => disable_timeout)
+ )
+ result = service.execute
+ return true if result[:status] == :success
+
+ log_error(error_message(container_repository, 'error in deleting tags'))
+ false
+ end
+
+ def destroy_container_repository(container_repository)
+ return true if container_repository.destroy
+
+ log_error(error_message(container_repository, container_repository.errors.full_messages.join('. ')))
+ false
+ end
+
+ def error_message(container_repository, message)
+ "Container repository with ID: #{container_repository.id} and path: #{container_repository.path}" \
+ " failed with message: #{message}"
end
end
end
diff --git a/app/services/projects/container_repository/gitlab/cleanup_tags_service.rb b/app/services/projects/container_repository/gitlab/cleanup_tags_service.rb
index e947e9575e2..b69a3cc1a2c 100644
--- a/app/services/projects/container_repository/gitlab/cleanup_tags_service.rb
+++ b/app/services/projects/container_repository/gitlab/cleanup_tags_service.rb
@@ -18,7 +18,7 @@ module Projects
container_repository.each_tags_page(page_size: TAGS_PAGE_SIZE) do |tags|
execute_for_tags(tags, result)
- raise TimeoutError if timeout?(start_time)
+ raise TimeoutError if !timeout_disabled? && timeout?(start_time)
end
end
end
@@ -72,6 +72,10 @@ module Projects
def pushed_at(tag)
tag.updated_at || tag.created_at
end
+
+ def timeout_disabled?
+ params['disable_timeout'] || false
+ end
end
end
end
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index c72f9b4b602..a4b473f35c6 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -317,6 +317,3 @@ module Projects
end
Projects::CreateService.prepend_mod_with('Projects::CreateService')
-
-# Measurable should be at the bottom of the ancestor chain, so it will measure execution of EE::Projects::CreateService as well
-Projects::CreateService.prepend(Measurable)
diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb
index ddbcfbb675c..a1f55f547a1 100644
--- a/app/services/projects/import_export/export_service.rb
+++ b/app/services/projects/import_export/export_service.rb
@@ -3,8 +3,6 @@
module Projects
module ImportExport
class ExportService < BaseService
- prepend Measurable
-
def initialize(*args)
super
diff --git a/app/services/projects/import_export/parallel_export_service.rb b/app/services/projects/import_export/parallel_export_service.rb
new file mode 100644
index 00000000000..7e4c0279b06
--- /dev/null
+++ b/app/services/projects/import_export/parallel_export_service.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+module Projects
+ module ImportExport
+ class ParallelExportService
+ def initialize(export_job, current_user, after_export_strategy)
+ @export_job = export_job
+ @current_user = current_user
+ @after_export_strategy = after_export_strategy
+ @shared = project.import_export_shared
+ @logger = Gitlab::Export::Logger.build
+ end
+
+ def execute
+ log_info('Parallel project export started')
+
+ if save_exporters && save_export_archive
+ log_info('Parallel project export finished successfully')
+ execute_after_export_action(after_export_strategy)
+ else
+ notify_error
+ end
+
+ ensure
+ cleanup
+ end
+
+ private
+
+ attr_reader :export_job, :current_user, :after_export_strategy, :shared, :logger
+
+ delegate :project, to: :export_job
+
+ def execute_after_export_action(after_export_strategy)
+ return if after_export_strategy.execute(current_user, project)
+
+ notify_error
+ end
+
+ def exporters
+ [version_saver, exported_relations_merger]
+ end
+
+ def save_exporters
+ exporters.all? do |exporter|
+ log_info("Parallel project export - #{exporter.class.name} saver started")
+
+ exporter.save
+ end
+ end
+
+ def save_export_archive
+ Gitlab::ImportExport::Saver.save(exportable: project, shared: shared)
+ end
+
+ def version_saver
+ @version_saver ||= Gitlab::ImportExport::VersionSaver.new(shared: shared)
+ end
+
+ def exported_relations_merger
+ @relation_saver ||= Gitlab::ImportExport::Project::ExportedRelationsMerger.new(
+ export_job: export_job,
+ shared: shared)
+ end
+
+ def cleanup
+ FileUtils.rm_rf(shared.export_path) if File.exist?(shared.export_path)
+ FileUtils.rm_rf(shared.archive_path) if File.exist?(shared.archive_path)
+ end
+
+ def log_info(message)
+ logger.info(
+ message: message,
+ **log_base_data
+ )
+ end
+
+ def notify_error
+ logger.error(
+ message: 'Parallel project export error',
+ export_errors: shared.errors.join(', '),
+ export_job_id: export_job.id,
+ **log_base_data
+ )
+
+ NotificationService.new.project_not_exported(project, current_user, shared.errors)
+ end
+
+ def log_base_data
+ {
+ project_id: project.id,
+ project_name: project.name,
+ project_path: project.full_path
+ }
+ end
+ end
+ end
+end
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index 6a13b8e38c1..967a1e990b2 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -179,6 +179,3 @@ module Projects
end
Projects::ImportService.prepend_mod_with('Projects::ImportService')
-
-# Measurable should be at the bottom of the ancestor chain, so it will measure execution of EE::Projects::ImportService as well
-Projects::ImportService.prepend(Measurable)
diff --git a/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb b/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb
index c91103f897f..f7de7f98768 100644
--- a/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-# This service lists the download link from a remote source based on the
+# This service yields operation on each download link from a remote source based on the
# oids provided
module Projects
module LfsPointers
@@ -23,29 +23,22 @@ module Projects
@remote_uri = remote_uri
end
- # This method accepts two parameters:
# - oids: hash of oids to query. The structure is { lfs_file_oid => lfs_file_size }
- #
- # Returns an array of LfsDownloadObject
- def execute(oids)
- return [] unless project&.lfs_enabled? && remote_uri && oids.present?
+ # Yields operation for each link batch-by-batch
+ def each_link(oids, &block)
+ return unless project&.lfs_enabled? && remote_uri && oids.present?
- get_download_links_in_batches(oids)
+ download_links_in_batches(oids, &block)
end
private
- def get_download_links_in_batches(oids, batch_size = REQUEST_BATCH_SIZE)
- download_links = []
-
+ def download_links_in_batches(oids, batch_size = REQUEST_BATCH_SIZE, &block)
oids.each_slice(batch_size) do |batch|
- download_links += get_download_links(batch)
+ download_links_for(batch).each(&block)
end
-
- download_links
-
rescue DownloadLinksRequestEntityTooLargeError => e
- # Log this exceptions to see how open it happens
+ # Log this exceptions to see how often it happens
Gitlab::ErrorTracking
.track_exception(e, project_id: project&.id, batch_size: batch_size, oids_count: oids.count)
@@ -57,7 +50,7 @@ module Projects
raise DownloadLinksError, 'Unable to download due to RequestEntityTooLarge errors'
end
- def get_download_links(oids)
+ def download_links_for(oids)
response = Gitlab::HTTP.post(remote_uri,
body: request_body(oids),
headers: headers)
diff --git a/app/services/projects/lfs_pointers/lfs_download_service.rb b/app/services/projects/lfs_pointers/lfs_download_service.rb
index eaf73b78c1c..26352198e5c 100644
--- a/app/services/projects/lfs_pointers/lfs_download_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_download_service.rb
@@ -92,9 +92,15 @@ module Projects
end
def fetch_file(&block)
+ attempts ||= 1
response = Gitlab::HTTP.get(lfs_sanitized_url, download_options, &block)
raise ResponseError, "Received error code #{response.code}" unless response.success?
+ rescue Net::OpenTimeout
+ raise if attempts >= 3
+
+ attempts += 1
+ retry
end
def with_tmp_file
diff --git a/app/services/projects/lfs_pointers/lfs_import_service.rb b/app/services/projects/lfs_pointers/lfs_import_service.rb
index 3fc82f2c410..c9791041088 100644
--- a/app/services/projects/lfs_pointers/lfs_import_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_import_service.rb
@@ -9,9 +9,7 @@ module Projects
def execute
return success unless project&.lfs_enabled?
- lfs_objects_to_download = LfsObjectDownloadListService.new(project).execute
-
- lfs_objects_to_download.each do |lfs_download_object|
+ LfsObjectDownloadListService.new(project).each_list_item do |lfs_download_object|
LfsDownloadService.new(project, lfs_download_object).execute
end
diff --git a/app/services/projects/lfs_pointers/lfs_object_download_list_service.rb b/app/services/projects/lfs_pointers/lfs_object_download_list_service.rb
index b4872cd9442..09fec9939b9 100644
--- a/app/services/projects/lfs_pointers/lfs_object_download_list_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_object_download_list_service.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
-# This service manages the whole worflow of discovering the Lfs files in a
-# repository, linking them to the project and downloading (and linking) the non
-# existent ones.
+# This service discovers the Lfs files that are linked in repository,
+# but not downloaded yet and yields the operation
+# on each Lfs file link (url) to remote repository.
module Projects
module LfsPointers
class LfsObjectDownloadListService < BaseService
@@ -14,30 +14,31 @@ module Projects
LfsObjectDownloadListError = Class.new(StandardError)
- def execute
- return [] unless project&.lfs_enabled?
-
- if external_lfs_endpoint?
- # If the endpoint host is different from the import_url it means
- # that the repo is using a third party service for storing the LFS files.
- # In this case, we have to disable lfs in the project
- disable_lfs!
-
- return []
- end
+ def each_list_item(&block)
+ return unless context_valid?
# Downloading the required information and gathering it inside an
# LfsDownloadObject for each oid
- #
LfsDownloadLinkListService
.new(project, remote_uri: current_endpoint_uri)
- .execute(missing_lfs_files)
+ .each_link(missing_lfs_files, &block)
rescue LfsDownloadLinkListService::DownloadLinksError => e
raise LfsObjectDownloadListError, "The LFS objects download list couldn't be imported. Error: #{e.message}"
end
private
+ def context_valid?
+ return false unless project&.lfs_enabled?
+ return true unless external_lfs_endpoint?
+
+ # If the endpoint host is different from the import_url it means
+ # that the repo is using a third party service for storing the LFS files.
+ # In this case, we have to disable lfs in the project
+ disable_lfs!
+ false
+ end
+
def external_lfs_endpoint?
lfsconfig_endpoint_uri && lfsconfig_endpoint_uri.host != import_uri.host
end
@@ -99,12 +100,10 @@ module Projects
# The import url must end with '.git' here we ensure it is
def default_endpoint_uri
- @default_endpoint_uri ||= begin
- import_uri.dup.tap do |uri|
- path = uri.path.gsub(%r(/$), '')
- path += '.git' unless path.ends_with?('.git')
- uri.path = path + LFS_BATCH_API_ENDPOINT
- end
+ @default_endpoint_uri ||= import_uri.dup.tap do |uri|
+ path = uri.path.gsub(%r(/$), '')
+ path += '.git' unless path.ends_with?('.git')
+ uri.path = path + LFS_BATCH_API_ENDPOINT
end
end
end
diff --git a/app/services/projects/refresh_build_artifacts_size_statistics_service.rb b/app/services/projects/refresh_build_artifacts_size_statistics_service.rb
index 1f86e5f4ba9..8e006dc8c34 100644
--- a/app/services/projects/refresh_build_artifacts_size_statistics_service.rb
+++ b/app/services/projects/refresh_build_artifacts_size_statistics_service.rb
@@ -18,7 +18,7 @@ module Projects
# Mark the refresh ready for another worker to pick up and process the next batch
refresh.requeue!(batch.last.id)
- refresh.project.statistics.delayed_increment_counter(:build_artifacts_size, total_artifacts_size)
+ refresh.project.statistics.increment_counter(:build_artifacts_size, total_artifacts_size)
end
else
# Remove the refresh job from the table if there are no more
diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb
index 6a963e7fcd1..0fadd75669e 100644
--- a/app/services/projects/update_pages_service.rb
+++ b/app/services/projects/update_pages_service.rb
@@ -63,16 +63,19 @@ module Projects
end
def build_commit_status
+ stage = create_stage
+
GenericCommitStatus.new(
user: build.user,
ci_stage: stage,
name: 'pages:deploy',
- stage: 'deploy'
+ stage: 'deploy',
+ stage_idx: stage.position
)
end
# rubocop: disable Performance/ActiveRecordSubtransactionMethods
- def stage
+ def create_stage
build.pipeline.stages.safe_find_or_create_by(name: 'deploy', pipeline_id: build.pipeline.id) do |stage|
stage.position = GenericCommitStatus::EXTERNAL_STAGE_IDX
stage.project = build.project
diff --git a/app/services/projects/update_remote_mirror_service.rb b/app/services/projects/update_remote_mirror_service.rb
index f686f14b5b3..aca6fa91eb1 100644
--- a/app/services/projects/update_remote_mirror_service.rb
+++ b/app/services/projects/update_remote_mirror_service.rb
@@ -10,7 +10,7 @@ module Projects
return success unless remote_mirror.enabled?
# Blocked URLs are a hard failure, no need to attempt to retry
- if Gitlab::UrlBlocker.blocked_url?(normalized_url(remote_mirror.url))
+ if Gitlab::UrlBlocker.blocked_url?(normalized_url(remote_mirror.url), schemes: Project::VALID_MIRROR_PROTOCOLS)
hard_retry_or_fail(remote_mirror, _('The remote mirror URL is invalid.'), tries)
return error(remote_mirror.last_error)
end
diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb
index f9a2c825608..301d11d841c 100644
--- a/app/services/projects/update_service.rb
+++ b/app/services/projects/update_service.rb
@@ -10,7 +10,6 @@ module Projects
def execute
build_topics
remove_unallowed_params
- mirror_operations_access_level_changes
validate!
ensure_wiki_exists if enabling_wiki?
@@ -65,16 +64,36 @@ module Projects
return unless changing_default_branch?
previous_default_branch = project.default_branch
+ new_default_branch = params[:default_branch]
- if project.change_head(params[:default_branch])
+ if project.change_head(new_default_branch)
params[:previous_default_branch] = previous_default_branch
+ if !project.root_ref?(new_default_branch) && has_custom_head_branch?
+ raise ValidationError,
+ format(
+ s_("UpdateProject|Could not set the default branch. Do you have a branch named 'HEAD' in your repository? (%{linkStart}How do I fix this?%{linkEnd})"),
+ linkStart: ambiguous_head_documentation_link, linkEnd: '</a>'
+ ).html_safe
+ end
+
after_default_branch_change(previous_default_branch)
else
raise ValidationError, s_("UpdateProject|Could not set the default branch")
end
end
+ def ambiguous_head_documentation_link
+ url = Rails.application.routes.url_helpers.help_page_path('user/project/repository/branches/index.md', anchor: 'error-ambiguous-head-branch-exists')
+
+ format('<a href="%{url}" target="_blank" rel="noopener noreferrer">', url: url)
+ end
+
+ # See issue: https://gitlab.com/gitlab-org/gitlab/-/issues/381731
+ def has_custom_head_branch?
+ project.repository.branch_names.any? { |name| name.casecmp('head') == 0 }
+ end
+
def after_default_branch_change(previous_default_branch)
# overridden by EE module
end
@@ -83,21 +102,6 @@ module Projects
params.delete(:emails_disabled) unless can?(current_user, :set_emails_disabled, project)
end
- # Temporary code to sync permissions changes as operations access setting
- # is being split into monitor_access_level, deployments_access_level, infrastructure_access_level.
- # To be removed as part of https://gitlab.com/gitlab-org/gitlab/-/issues/364240
- def mirror_operations_access_level_changes
- return if Feature.enabled?(:split_operations_visibility_permissions, project)
-
- operations_access_level = params.dig(:project_feature_attributes, :operations_access_level)
-
- return if operations_access_level.nil?
-
- [:monitor_access_level, :infrastructure_access_level, :feature_flags_access_level, :environments_access_level].each do |key|
- params[:project_feature_attributes][key] = operations_access_level
- end
- end
-
def after_update
todos_features_changes = %w(
issues_access_level
diff --git a/app/services/protected_branches/api_service.rb b/app/services/protected_branches/api_service.rb
index b8fe9bac13e..0a7777c7fed 100644
--- a/app/services/protected_branches/api_service.rb
+++ b/app/services/protected_branches/api_service.rb
@@ -3,11 +3,11 @@
module ProtectedBranches
class ApiService < ProtectedBranches::BaseService
def create
- ::ProtectedBranches::CreateService.new(@project, @current_user, protected_branch_params).execute
+ ::ProtectedBranches::CreateService.new(project_or_group, @current_user, protected_branch_params).execute
end
def update(protected_branch)
- ::ProtectedBranches::UpdateService.new(@project, @current_user,
+ ::ProtectedBranches::UpdateService.new(project_or_group, @current_user,
protected_branch_params(with_defaults: false)).execute(protected_branch)
end
@@ -36,4 +36,4 @@ protected_branch_params(with_defaults: false)).execute(protected_branch)
end
end
-ProtectedBranches::ApiService.prepend_mod_with('ProtectedBranches::ApiService')
+ProtectedBranches::ApiService.prepend_mod
diff --git a/app/services/protected_branches/base_service.rb b/app/services/protected_branches/base_service.rb
index d26c1b148bf..951017b2d01 100644
--- a/app/services/protected_branches/base_service.rb
+++ b/app/services/protected_branches/base_service.rb
@@ -2,10 +2,12 @@
module ProtectedBranches
class BaseService < ::BaseService
+ attr_reader :project_or_group
+
# current_user - The user that performs the action
# params - A hash of parameters
- def initialize(project, current_user = nil, params = {})
- @project = project
+ def initialize(project_or_group, current_user = nil, params = {})
+ @project_or_group = project_or_group
@current_user = current_user
@params = params
end
@@ -15,7 +17,7 @@ module ProtectedBranches
end
def refresh_cache
- CacheService.new(@project, @current_user, @params).refresh
+ CacheService.new(@project_or_group, @current_user, @params).refresh
end
end
end
diff --git a/app/services/protected_branches/cache_service.rb b/app/services/protected_branches/cache_service.rb
index 66ca549c508..af8c9ce74bb 100644
--- a/app/services/protected_branches/cache_service.rb
+++ b/app/services/protected_branches/cache_service.rb
@@ -66,13 +66,18 @@ module ProtectedBranches
log_error(
'class' => self.class.name,
'message' => "Cache mismatch '#{encoded_ref_name}': cached value: #{cached_value}, real value: #{real_value}",
- 'project_id' => @project.id,
- 'project_path' => @project.full_path
+ 'record_class' => project_or_group.class.name,
+ 'record_id' => project_or_group.id,
+ 'record_path' => project_or_group.full_path
)
end
def redis_key
- @redis_key ||= [CACHE_ROOT_KEY, @project.id].join(':')
+ @redis_key ||= if Feature.enabled?(:group_protected_branches)
+ [CACHE_ROOT_KEY, project_or_group.class.name, project_or_group.id].join(':')
+ else
+ [CACHE_ROOT_KEY, project_or_group.id].join(':')
+ end
end
def metrics
diff --git a/app/services/protected_branches/create_service.rb b/app/services/protected_branches/create_service.rb
index 903addf7afc..46585e0b65d 100644
--- a/app/services/protected_branches/create_service.rb
+++ b/app/services/protected_branches/create_service.rb
@@ -23,9 +23,9 @@ module ProtectedBranches
end
def protected_branch
- @protected_branch ||= project.protected_branches.new(params)
+ @protected_branch ||= project_or_group.protected_branches.new(params)
end
end
end
-ProtectedBranches::CreateService.prepend_mod_with('ProtectedBranches::CreateService')
+ProtectedBranches::CreateService.prepend_mod
diff --git a/app/services/protected_branches/destroy_service.rb b/app/services/protected_branches/destroy_service.rb
index 01d3b68314f..a32a867491e 100644
--- a/app/services/protected_branches/destroy_service.rb
+++ b/app/services/protected_branches/destroy_service.rb
@@ -10,4 +10,4 @@ module ProtectedBranches
end
end
-ProtectedBranches::DestroyService.prepend_mod_with('ProtectedBranches::DestroyService')
+ProtectedBranches::DestroyService.prepend_mod
diff --git a/app/services/protected_branches/legacy_api_create_service.rb b/app/services/protected_branches/legacy_api_create_service.rb
index aef99a860a0..f662d9d1bf0 100644
--- a/app/services/protected_branches/legacy_api_create_service.rb
+++ b/app/services/protected_branches/legacy_api_create_service.rb
@@ -24,7 +24,7 @@ module ProtectedBranches
@params.merge!(push_access_levels_attributes: [{ access_level: push_access_level }],
merge_access_levels_attributes: [{ access_level: merge_access_level }])
- service = ProtectedBranches::CreateService.new(@project, @current_user, @params)
+ service = ProtectedBranches::CreateService.new(project_or_group, @current_user, @params)
service.execute
end
end
diff --git a/app/services/protected_branches/legacy_api_update_service.rb b/app/services/protected_branches/legacy_api_update_service.rb
index 8ff6c4bd734..b144797ab6d 100644
--- a/app/services/protected_branches/legacy_api_update_service.rb
+++ b/app/services/protected_branches/legacy_api_update_service.rb
@@ -30,7 +30,7 @@ module ProtectedBranches
params[:merge_access_levels_attributes] = [{ access_level: Gitlab::Access::MAINTAINER }]
end
- service = ProtectedBranches::UpdateService.new(project, current_user, params)
+ service = ProtectedBranches::UpdateService.new(project_or_group, current_user, params)
service.execute(protected_branch)
end
end
diff --git a/app/services/protected_branches/update_service.rb b/app/services/protected_branches/update_service.rb
index c155e0022f5..4b54bf92989 100644
--- a/app/services/protected_branches/update_service.rb
+++ b/app/services/protected_branches/update_service.rb
@@ -19,4 +19,4 @@ module ProtectedBranches
end
end
-ProtectedBranches::UpdateService.prepend_mod_with('ProtectedBranches::UpdateService')
+ProtectedBranches::UpdateService.prepend_mod
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index 1d7c5d2c80a..f1e4dac8835 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -158,15 +158,15 @@ module QuickActions
end
def map_commands(commands, method)
- commands.map do |name, arg|
- definition = self.class.definition_by_name(name)
+ commands.map do |name_or_alias, arg|
+ definition = self.class.definition_by_name(name_or_alias)
next unless definition
case method
when :explain
definition.explain(self, arg)
when :execute_message
- @execution_message[name.to_sym] || definition.execute_message(self, arg)
+ @execution_message[definition.name.to_sym] || definition.execute_message(self, arg)
end
end.compact
end
diff --git a/app/services/repositories/housekeeping_service.rb b/app/services/repositories/housekeeping_service.rb
index de80390e60b..e12d69807f2 100644
--- a/app/services/repositories/housekeeping_service.rb
+++ b/app/services/repositories/housekeeping_service.rb
@@ -84,7 +84,11 @@ module Repositories
end
def period_match?
- [gc_period, full_repack_period, repack_period, PACK_REFS_PERIOD].any? { |period| pushes_since_gc % period == 0 }
+ if Feature.enabled?(:optimized_housekeeping)
+ pushes_since_gc % repack_period == 0
+ else
+ [gc_period, full_repack_period, repack_period, PACK_REFS_PERIOD].any? { |period| pushes_since_gc % period == 0 }
+ end
end
def housekeeping_enabled?
diff --git a/app/services/search_service.rb b/app/services/search_service.rb
index f38522b9764..403a2f077b0 100644
--- a/app/services/search_service.rb
+++ b/app/services/search_service.rb
@@ -45,9 +45,9 @@ class SearchService
end
def show_snippets?
- return @show_snippets if defined?(@show_snippets)
-
- @show_snippets = params[:snippets] == 'true'
+ strong_memoize(:show_snippets) do
+ params[:snippets] == 'true'
+ end
end
delegate :scope, to: :search_service
diff --git a/app/services/snippets/create_service.rb b/app/services/snippets/create_service.rb
index 5cadff42958..a62d5290271 100644
--- a/app/services/snippets/create_service.rb
+++ b/app/services/snippets/create_service.rb
@@ -4,7 +4,7 @@ module Snippets
class CreateService < Snippets::BaseService
# NOTE: For Issues::CreateService, we require the spam_params and do not default it to nil, because
# spam_checking is likely to be necessary.
- def initialize(project:, current_user: nil, params: {}, spam_params:)
+ def initialize(project:, spam_params:, current_user: nil, params: {})
super(project: project, current_user: current_user, params: params)
@spam_params = spam_params
end
diff --git a/app/services/system_notes/commit_service.rb b/app/services/system_notes/commit_service.rb
index c89998f77c7..592351079aa 100644
--- a/app/services/system_notes/commit_service.rb
+++ b/app/services/system_notes/commit_service.rb
@@ -81,12 +81,10 @@ module SystemNotes
commit_ids = if count == 1
existing_commits.first.short_id
+ elsif oldrev && !Gitlab::Git.blank_ref?(oldrev)
+ "#{Commit.truncate_sha(oldrev)}...#{existing_commits.last.short_id}"
else
- if oldrev && !Gitlab::Git.blank_ref?(oldrev)
- "#{Commit.truncate_sha(oldrev)}...#{existing_commits.last.short_id}"
- else
- "#{existing_commits.first.short_id}..#{existing_commits.last.short_id}"
- end
+ "#{existing_commits.first.short_id}..#{existing_commits.last.short_id}"
end
commits_text = "#{count} commit".pluralize(count)
diff --git a/app/services/task_list_toggle_service.rb b/app/services/task_list_toggle_service.rb
index 082fa1447fc..8e20ffc2a52 100644
--- a/app/services/task_list_toggle_service.rb
+++ b/app/services/task_list_toggle_service.rb
@@ -44,8 +44,8 @@ class TaskListToggleService
# any `[ ]` or `[x]` in the middle of the text
if currently_checked
markdown_task.sub!(Taskable::COMPLETE_PATTERN, '[ ]') unless toggle_as_checked
- else
- markdown_task.sub!(Taskable::INCOMPLETE_PATTERN, '[x]') if toggle_as_checked
+ elsif toggle_as_checked
+ markdown_task.sub!(Taskable::INCOMPLETE_PATTERN, '[x]')
end
source_lines[source_line_index] = markdown_task
diff --git a/app/services/timelogs/base_service.rb b/app/services/timelogs/base_service.rb
index e09264864fd..712a0a4f128 100644
--- a/app/services/timelogs/base_service.rb
+++ b/app/services/timelogs/base_service.rb
@@ -22,9 +22,9 @@ module Timelogs
end
def error_in_save(timelog)
- return error(_("Failed to save timelog")) if timelog.errors.empty?
+ return error(_("Failed to save timelog"), 404) if timelog.errors.empty?
- error(timelog.errors.full_messages.to_sentence)
+ error(timelog.errors.full_messages.to_sentence, 404)
end
end
end
diff --git a/app/services/timelogs/create_service.rb b/app/services/timelogs/create_service.rb
index 12181cec20a..19428864fa9 100644
--- a/app/services/timelogs/create_service.rb
+++ b/app/services/timelogs/create_service.rb
@@ -21,6 +21,9 @@ module Timelogs
}, 404)
end
+ return error(_("Spent at can't be a future date and time."), 404) if spent_at.future?
+ return error(_("Time spent can't be zero."), 404) if time_spent == 0
+
issue = issuable if issuable.is_a?(Issue)
merge_request = issuable if issuable.is_a?(MergeRequest)
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index 06352d36215..9ae31f8ac58 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -166,8 +166,9 @@ class TodoService
# When user marks a target as todo
def mark_todo(target, current_user)
- attributes = attributes_for_todo(target.project, target, current_user, Todo::MARKED)
- create_todos(current_user, attributes)
+ project = target.project
+ attributes = attributes_for_todo(project, target, current_user, Todo::MARKED)
+ create_todos(current_user, attributes, project&.namespace, project)
end
def todo_exist?(issuable, current_user)
@@ -214,13 +215,32 @@ class TodoService
end
def create_request_review_todo(target, author, reviewers)
- attributes = attributes_for_todo(target.project, target, author, Todo::REVIEW_REQUESTED)
- create_todos(reviewers, attributes)
+ project = target.project
+ attributes = attributes_for_todo(project, target, author, Todo::REVIEW_REQUESTED)
+ create_todos(reviewers, attributes, project.namespace, project)
+ end
+
+ def create_member_access_request(member)
+ source = member.source
+ attributes = attributes_for_access_request_todos(source, member.user, Todo::MEMBER_ACCESS_REQUESTED)
+
+ approvers = source.access_request_approvers_to_be_notified.map(&:user)
+ return true if approvers.empty?
+
+ if source.instance_of? Project
+ project = source
+ namespace = project.namespace
+ else
+ project = nil
+ namespace = source
+ end
+
+ create_todos(approvers, attributes, namespace, project)
end
private
- def create_todos(users, attributes)
+ def create_todos(users, attributes, namespace, project)
users = Array(users)
return if users.empty?
@@ -246,7 +266,7 @@ class TodoService
todos = users.map do |user|
issue_type = attributes.delete(:issue_type)
- track_todo_creation(user, issue_type)
+ track_todo_creation(user, issue_type, namespace, project)
Todo.create(attributes.merge(user_id: user.id))
end
@@ -286,9 +306,10 @@ class TodoService
def create_assignment_todo(target, author, old_assignees = [])
if target.assignees.any?
+ project = target.project
assignees = target.assignees - old_assignees
- attributes = attributes_for_todo(target.project, target, author, Todo::ASSIGNED)
- create_todos(assignees, attributes)
+ attributes = attributes_for_todo(project, target, author, Todo::ASSIGNED)
+ create_todos(assignees, attributes, project.namespace, project)
end
end
@@ -303,22 +324,24 @@ class TodoService
# Create Todos for directly addressed users
directly_addressed_users = filter_directly_addressed_users(parent, note || target, author, skip_users)
attributes = attributes_for_todo(parent, target, author, Todo::DIRECTLY_ADDRESSED, note)
- create_todos(directly_addressed_users, attributes)
+ create_todos(directly_addressed_users, attributes, parent&.namespace, parent)
# Create Todos for mentioned users
mentioned_users = filter_mentioned_users(parent, note || target, author, skip_users + directly_addressed_users)
attributes = attributes_for_todo(parent, target, author, Todo::MENTIONED, note)
- create_todos(mentioned_users, attributes)
+ create_todos(mentioned_users, attributes, parent&.namespace, parent)
end
def create_build_failed_todo(merge_request, todo_author)
- attributes = attributes_for_todo(merge_request.project, merge_request, todo_author, Todo::BUILD_FAILED)
- create_todos(todo_author, attributes)
+ project = merge_request.project
+ attributes = attributes_for_todo(project, merge_request, todo_author, Todo::BUILD_FAILED)
+ create_todos(todo_author, attributes, project.namespace, project)
end
def create_unmergeable_todo(merge_request, todo_author)
- attributes = attributes_for_todo(merge_request.project, merge_request, todo_author, Todo::UNMERGEABLE)
- create_todos(todo_author, attributes)
+ project = merge_request.project
+ attributes = attributes_for_todo(project, merge_request, todo_author, Todo::UNMERGEABLE)
+ create_todos(todo_author, attributes, project.namespace, project)
end
def attributes_for_target(target)
@@ -382,10 +405,37 @@ class TodoService
PendingTodosFinder.new(users, criteria).execute
end
- def track_todo_creation(user, issue_type)
+ def track_todo_creation(user, issue_type, namespace, project)
return unless issue_type == 'incident'
- track_usage_event(:incident_management_incident_todo, user.id)
+ event = "incident_management_incident_todo"
+ track_usage_event(event, user.id)
+
+ return unless Feature.enabled?(:route_hll_to_snowplow_phase2, namespace)
+
+ Gitlab::Tracking.event(
+ self.class.to_s,
+ event,
+ project: project,
+ namespace: namespace,
+ user: user,
+ label: 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly',
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event).to_context]
+ )
+ end
+
+ def attributes_for_access_request_todos(source, author, action, note = nil)
+ attributes = {
+ target_id: source.id,
+ target_type: source.class.polymorphic_name,
+ author_id: author.id,
+ action: action,
+ note: note
+ }
+
+ attributes[:group_id] = source.id unless source.instance_of? Project
+
+ attributes
end
end
diff --git a/app/services/users/approve_service.rb b/app/services/users/approve_service.rb
index 15486ddcd43..353456c545d 100644
--- a/app/services/users/approve_service.rb
+++ b/app/services/users/approve_service.rb
@@ -42,7 +42,7 @@ module Users
end
def log_event(user)
- Gitlab::AppLogger.info(message: "User instance access request approved", user: "#{user.username}", email: "#{user.email}", approved_by: "#{current_user.username}", ip_address: "#{current_user.current_sign_in_ip}")
+ Gitlab::AppLogger.info(message: "User instance access request approved", user: user.username.to_s, email: user.email.to_s, approved_by: current_user.username.to_s, ip_address: current_user.current_sign_in_ip.to_s)
end
end
end
diff --git a/app/services/users/assigned_issues_count_service.rb b/app/services/users/assigned_issues_count_service.rb
new file mode 100644
index 00000000000..6590902587d
--- /dev/null
+++ b/app/services/users/assigned_issues_count_service.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+module Users
+ class AssignedIssuesCountService < ::BaseCountService
+ def initialize(current_user:, max_limit: User::MAX_LIMIT_FOR_ASSIGNEED_ISSUES_COUNT)
+ @current_user = current_user
+ @max_limit = max_limit
+ end
+
+ def cache_key
+ ['users', @current_user.id, 'max_assigned_open_issues_count']
+ end
+
+ def cache_options
+ { force: false, expires_in: User::COUNT_CACHE_VALIDITY_PERIOD }
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def uncached_count
+ # When a user has many assigned issues, counting them all can be very slow.
+ # As a workaround, we will short-circuit the counting query once the count reaches some threshold.
+ #
+ # Concretely, given a threshold, say 100 (= max_limit),
+ # iterate through the first 100 issues, sorted by ID desc, assigned to the user using `issue_assignees` table.
+ # For each issue iterated, use IssuesFinder to check if the issue should be counted.
+ initializer = IssueAssignee
+ .select(:issue_id).joins(", LATERAL (#{finder_constraint.to_sql}) as issues")
+ .where(user_id: @current_user.id)
+ .order(issue_id: :desc)
+ .limit(1)
+ recursive_finder = initializer.where("issue_assignees.issue_id < assigned_issues.issue_id")
+
+ cte = <<~SQL
+ WITH RECURSIVE assigned_issues AS (
+ (
+ #{initializer.to_sql}
+ )
+ UNION ALL
+ (
+ SELECT next_assigned_issue.issue_id
+ FROM assigned_issues,
+ LATERAL (
+ #{recursive_finder.to_sql}
+ ) next_assigned_issue
+ )
+ ) SELECT COUNT(*) FROM (SELECT * FROM assigned_issues LIMIT #{@max_limit}) issues
+ SQL
+
+ ApplicationRecord.connection.execute(cte).first["count"]
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ private
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def finder_constraint
+ IssuesFinder.new(@current_user, assignee_id: @current_user.id, state: 'opened', non_archived: true)
+ .execute
+ .where("issues.id=issue_assignees.issue_id").limit(1)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+end
diff --git a/app/services/users/banned_user_base_service.rb b/app/services/users/banned_user_base_service.rb
index a582816283a..74c10581a6e 100644
--- a/app/services/users/banned_user_base_service.rb
+++ b/app/services/users/banned_user_base_service.rb
@@ -36,7 +36,7 @@ module Users
end
def log_event(user)
- Gitlab::AppLogger.info(message: "User #{action}", user: "#{user.username}", email: "#{user.email}", "#{action}_by": "#{current_user.username}", ip_address: "#{current_user.current_sign_in_ip}")
+ Gitlab::AppLogger.info(message: "User #{action}", user: user.username.to_s, email: user.email.to_s, "#{action}_by": current_user.username.to_s, ip_address: current_user.current_sign_in_ip.to_s)
end
end
end
diff --git a/app/services/users/build_service.rb b/app/services/users/build_service.rb
index 8ef1b3e0613..064bf132d3d 100644
--- a/app/services/users/build_service.rb
+++ b/app/services/users/build_service.rb
@@ -117,7 +117,7 @@ module Users
end
def skip_user_confirmation_email_from_setting
- !Gitlab::CurrentSettings.send_user_confirmation_email
+ Gitlab::CurrentSettings.email_confirmation_setting_off?
end
def use_fallback_name?
diff --git a/app/services/users/keys_count_service.rb b/app/services/users/keys_count_service.rb
index f82d27eded9..378093f2e1b 100644
--- a/app/services/users/keys_count_service.rb
+++ b/app/services/users/keys_count_service.rb
@@ -11,7 +11,7 @@ module Users
end
def relation_for_count
- user.keys
+ user.keys.auth
end
def raw?
diff --git a/app/services/users/migrate_records_to_ghost_user_service.rb b/app/services/users/migrate_records_to_ghost_user_service.rb
index 2d92aaed7da..5d518803315 100644
--- a/app/services/users/migrate_records_to_ghost_user_service.rb
+++ b/app/services/users/migrate_records_to_ghost_user_service.rb
@@ -42,6 +42,7 @@ module Users
migrate_award_emoji
migrate_snippets
migrate_reviews
+ migrate_releases
end
def post_migrate_records
@@ -96,6 +97,10 @@ module Users
batched_migrate(Review, :author_id)
end
+ def migrate_releases
+ batched_migrate(Release, :author_id)
+ end
+
# rubocop:disable CodeReuse/ActiveRecord
def batched_migrate(base_scope, column, batch_size: 50)
loop do
diff --git a/app/services/users/reject_service.rb b/app/services/users/reject_service.rb
index 459dd81b74d..dc22b2ec21d 100644
--- a/app/services/users/reject_service.rb
+++ b/app/services/users/reject_service.rb
@@ -34,7 +34,7 @@ module Users
end
def log_event(user)
- Gitlab::AppLogger.info(message: "User instance access request rejected", user: "#{user.username}", email: "#{user.email}", rejected_by: "#{current_user.username}", ip_address: "#{current_user.current_sign_in_ip}")
+ Gitlab::AppLogger.info(message: "User instance access request rejected", user: user.username.to_s, email: user.email.to_s, rejected_by: current_user.username.to_s, ip_address: current_user.current_sign_in_ip.to_s)
end
end
end
diff --git a/app/services/users/update_highest_member_role_service.rb b/app/services/users/update_highest_member_role_service.rb
index 90a5966265d..fff001e04d7 100644
--- a/app/services/users/update_highest_member_role_service.rb
+++ b/app/services/users/update_highest_member_role_service.rb
@@ -17,9 +17,7 @@ module Users
private
def user_highest_role
- @user_highest_role ||= begin
- @user.user_highest_role || @user.build_user_highest_role
- end
+ @user_highest_role ||= @user.user_highest_role || @user.build_user_highest_role
end
def highest_access_level
diff --git a/app/services/web_hooks/log_execution_service.rb b/app/services/web_hooks/log_execution_service.rb
index 448bb7d4097..b1da0c1642f 100644
--- a/app/services/web_hooks/log_execution_service.rb
+++ b/app/services/web_hooks/log_execution_service.rb
@@ -17,18 +17,32 @@ module WebHooks
end
def execute
- update_hook_failure_state if WebHook.web_hooks_disable_failed?(hook)
+ update_hook_failure_state
log_execution
end
private
def log_execution
+ mask_response_headers
+
log_data[:request_headers]['X-Gitlab-Token'] = _('[REDACTED]') if hook.token?
WebHookLog.create!(web_hook: hook, **log_data)
end
+ def mask_response_headers
+ return unless hook.url_variables?
+ return unless log_data.key?(:response_headers)
+
+ variables_map = hook.url_variables.invert.transform_values { "{#{_1}}" }
+ regex = Regexp.union(variables_map.keys)
+
+ log_data[:response_headers].transform_values! do |value|
+ regex === value ? value.gsub(regex, variables_map) : value
+ end
+ end
+
# Perform this operation within an `Gitlab::ExclusiveLease` lock to make it
# safe to be called concurrently from different workers.
def update_hook_failure_state
diff --git a/app/services/wiki_pages/update_service.rb b/app/services/wiki_pages/update_service.rb
index 12b2cf87d5d..cf9eddbd13f 100644
--- a/app/services/wiki_pages/update_service.rb
+++ b/app/services/wiki_pages/update_service.rb
@@ -12,7 +12,7 @@ module WikiPages
execute_hooks(page)
ServiceResponse.success(payload: { page: page })
else
- raise UpdateError, s_('Could not update wiki page')
+ raise UpdateError, _('Could not update wiki page')
end
rescue UpdateError, WikiPage::PageChangedError, WikiPage::PageRenameError => e
page.update_attributes(@params) # rubocop:disable Rails/ActiveRecordAliases
diff --git a/app/services/work_items/create_and_link_service.rb b/app/services/work_items/create_and_link_service.rb
index 5cc358c4b4f..351ebc14564 100644
--- a/app/services/work_items/create_and_link_service.rb
+++ b/app/services/work_items/create_and_link_service.rb
@@ -6,7 +6,7 @@ module WorkItems
# This class should always be run inside a transaction as we could end up with
# new work items that were never associated with other work items as expected.
class CreateAndLinkService
- def initialize(project:, current_user: nil, params: {}, spam_params:, link_params: {})
+ def initialize(project:, spam_params:, current_user: nil, params: {}, link_params: {})
@project = project
@current_user = current_user
@params = params
diff --git a/app/services/work_items/create_from_task_service.rb b/app/services/work_items/create_from_task_service.rb
index ef1d47c560d..ced5b17a21c 100644
--- a/app/services/work_items/create_from_task_service.rb
+++ b/app/services/work_items/create_from_task_service.rb
@@ -2,7 +2,7 @@
module WorkItems
class CreateFromTaskService
- def initialize(work_item:, current_user: nil, work_item_params: {}, spam_params:)
+ def initialize(work_item:, spam_params:, current_user: nil, work_item_params: {})
@work_item = work_item
@current_user = current_user
@work_item_params = work_item_params
diff --git a/app/services/work_items/create_service.rb b/app/services/work_items/create_service.rb
index 87cc690d666..c89ebc75b80 100644
--- a/app/services/work_items/create_service.rb
+++ b/app/services/work_items/create_service.rb
@@ -4,7 +4,7 @@ module WorkItems
class CreateService < Issues::CreateService
include WidgetableService
- def initialize(project:, current_user: nil, params: {}, spam_params:, widget_params: {})
+ def initialize(project:, spam_params:, current_user: nil, params: {}, widget_params: {})
super(
project: project,
current_user: current_user,
diff --git a/app/services/work_items/delete_task_service.rb b/app/services/work_items/delete_task_service.rb
index 3bb23576442..2a82a993b71 100644
--- a/app/services/work_items/delete_task_service.rb
+++ b/app/services/work_items/delete_task_service.rb
@@ -2,7 +2,7 @@
module WorkItems
class DeleteTaskService
- def initialize(work_item:, current_user: nil, task_params: {}, lock_version:)
+ def initialize(work_item:, lock_version:, current_user: nil, task_params: {})
@work_item = work_item
@current_user = current_user
@task_params = task_params
diff --git a/app/uploaders/ci/secure_file_uploader.rb b/app/uploaders/ci/secure_file_uploader.rb
index 8aa624d6b30..11cbfc6c1f2 100644
--- a/app/uploaders/ci/secure_file_uploader.rb
+++ b/app/uploaders/ci/secure_file_uploader.rb
@@ -34,10 +34,6 @@ module Ci
false
end
- def background_upload_enabled?
- false
- end
-
def default_store
object_store_enabled? ? ObjectStorage::Store::REMOTE : ObjectStorage::Store::LOCAL
end
diff --git a/app/uploaders/file_mover.rb b/app/uploaders/file_mover.rb
index 95bc2680ed6..92ab2d88b41 100644
--- a/app/uploaders/file_mover.rb
+++ b/app/uploaders/file_mover.rb
@@ -24,7 +24,6 @@ class FileMover
if update_markdown
update_upload_model
- uploader.schedule_background_upload
end
end
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index 7250ce5c0b0..f947f70985c 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -27,10 +27,6 @@ class FileUploader < GitlabUploader
after :remove, :prune_store_dir
- # FileUploader do not run in a model transaction, so we can simply
- # enqueue a job after the :store hook.
- after :store, :schedule_background_upload
-
def self.root
File.join(options.storage_path, 'uploads')
end
diff --git a/app/uploaders/gitlab_uploader.rb b/app/uploaders/gitlab_uploader.rb
index abe06bd97e1..62024bff4c0 100644
--- a/app/uploaders/gitlab_uploader.rb
+++ b/app/uploaders/gitlab_uploader.rb
@@ -101,8 +101,8 @@ class GitlabUploader < CarrierWave::Uploader::Base
stream =
if file_storage?
File.open(path, "rb") if path
- else
- ::Gitlab::HttpIO.new(url, cached_size) if url
+ elsif url
+ ::Gitlab::HttpIO.new(url, cached_size)
end
return unless stream
@@ -115,6 +115,15 @@ class GitlabUploader < CarrierWave::Uploader::Base
end
end
+ def multi_read(offsets)
+ open do |stream|
+ offsets.map do |start_offset, end_offset|
+ stream.seek(start_offset)
+ stream.read(end_offset - start_offset + 1)
+ end
+ end
+ end
+
# Used to replace an existing upload with another +file+ without modifying stored metadata
# Use this method only to repair/replace an existing upload, or to upload to a Geo secondary node
#
diff --git a/app/uploaders/object_storage.rb b/app/uploaders/object_storage.rb
index 063aca7937c..e74998ce4a8 100644
--- a/app/uploaders/object_storage.rb
+++ b/app/uploaders/object_storage.rb
@@ -67,16 +67,6 @@ module ObjectStorage
super
end
- def schedule_background_upload(*args)
- return unless schedule_background_upload?
- return unless upload
-
- ObjectStorage::BackgroundMoveWorker.perform_async(self.class.name,
- upload.class.to_s,
- mounted_as,
- upload.id)
- end
-
def exclusive_lease_key
# For FileUploaders, model may have many uploaders. In that case
# we want to use exclusive key per upload, not per model to allow
@@ -99,40 +89,6 @@ module ObjectStorage
end
end
- # Add support for automatic background uploading after the file is stored.
- #
- module BackgroundMove
- extend ActiveSupport::Concern
-
- def background_upload(mount_points = [])
- return unless mount_points.any?
-
- run_after_commit do
- mount_points.each { |mount| send(mount).schedule_background_upload } # rubocop:disable GitlabSecurity/PublicSend
- end
- end
-
- def changed_mounts
- self.class.uploaders.select do |mount, uploader_class|
- mounted_as = uploader_class.serialization_column(self.class, mount)
- uploader = send(:"#{mounted_as}") # rubocop:disable GitlabSecurity/PublicSend
-
- next unless uploader
- next unless uploader.exists?
- next unless send(:"saved_change_to_#{mounted_as}?") # rubocop:disable GitlabSecurity/PublicSend
-
- mount
- end.keys
- end
-
- included do
- include AfterCommitQueue
- after_save do
- background_upload(changed_mounts)
- end
- end
- end
-
module Concern
extend ActiveSupport::Concern
@@ -155,10 +111,6 @@ module ObjectStorage
object_store_options&.direct_upload
end
- def background_upload_enabled?
- object_store_options.background_upload
- end
-
def proxy_download_enabled?
object_store_options.proxy_download
end
@@ -311,15 +263,6 @@ module ObjectStorage
end
end
- def schedule_background_upload(*args)
- return unless schedule_background_upload?
-
- ObjectStorage::BackgroundMoveWorker.perform_async(self.class.name,
- model.class.name,
- mounted_as,
- model.id)
- end
-
def fog_directory
self.class.remote_store_path
end
@@ -405,12 +348,6 @@ module ObjectStorage
private
- def schedule_background_upload?
- self.class.object_store_enabled? &&
- self.class.background_upload_enabled? &&
- self.file_storage?
- end
-
def cache_remote_file!(remote_object_id, original_filename)
file_path = File.join(TMP_UPLOAD_PATH, remote_object_id)
file_path = Pathname.new(file_path).cleanpath.to_s
diff --git a/app/uploaders/packages/composer/cache_uploader.rb b/app/uploaders/packages/composer/cache_uploader.rb
index f8052ec4810..ad7c017c4ba 100644
--- a/app/uploaders/packages/composer/cache_uploader.rb
+++ b/app/uploaders/packages/composer/cache_uploader.rb
@@ -4,8 +4,6 @@ class Packages::Composer::CacheUploader < GitlabUploader
storage_options Gitlab.config.packages
- after :store, :schedule_background_upload
-
alias_method :upload, :model
def filename
diff --git a/app/uploaders/packages/debian/component_file_uploader.rb b/app/uploaders/packages/debian/component_file_uploader.rb
index e4d637fecac..2de4743d7f7 100644
--- a/app/uploaders/packages/debian/component_file_uploader.rb
+++ b/app/uploaders/packages/debian/component_file_uploader.rb
@@ -5,8 +5,6 @@ class Packages::Debian::ComponentFileUploader < GitlabUploader
storage_options Gitlab.config.packages
- after :store, :schedule_background_upload
-
alias_method :upload, :model
def filename
diff --git a/app/uploaders/packages/debian/distribution_release_file_uploader.rb b/app/uploaders/packages/debian/distribution_release_file_uploader.rb
index a6ff3767b22..268d42796e9 100644
--- a/app/uploaders/packages/debian/distribution_release_file_uploader.rb
+++ b/app/uploaders/packages/debian/distribution_release_file_uploader.rb
@@ -5,8 +5,6 @@ class Packages::Debian::DistributionReleaseFileUploader < GitlabUploader
storage_options Gitlab.config.packages
- after :store, :schedule_background_upload
-
alias_method :upload, :model
def filename
diff --git a/app/uploaders/packages/package_file_uploader.rb b/app/uploaders/packages/package_file_uploader.rb
index 9c0a88c9bf8..c8a09c50dc6 100644
--- a/app/uploaders/packages/package_file_uploader.rb
+++ b/app/uploaders/packages/package_file_uploader.rb
@@ -5,8 +5,6 @@ class Packages::PackageFileUploader < GitlabUploader
storage_options Gitlab.config.packages
- after :store, :schedule_background_upload
-
alias_method :upload, :model
def filename
diff --git a/app/uploaders/packages/rpm/repository_file_uploader.rb b/app/uploaders/packages/rpm/repository_file_uploader.rb
index ff7e2bc719a..f95f861585c 100644
--- a/app/uploaders/packages/rpm/repository_file_uploader.rb
+++ b/app/uploaders/packages/rpm/repository_file_uploader.rb
@@ -6,8 +6,6 @@ module Packages
storage_options Gitlab.config.packages
- after :store, :schedule_background_upload
-
alias_method :upload, :model
def filename
diff --git a/app/uploaders/pages/deployment_uploader.rb b/app/uploaders/pages/deployment_uploader.rb
index e510025fc7d..c5ba65673ab 100644
--- a/app/uploaders/pages/deployment_uploader.rb
+++ b/app/uploaders/pages/deployment_uploader.rb
@@ -36,13 +36,6 @@ module Pages
false
end
- # we don't need background uploads because we upload files
- # to the right store right away, and we already do that in
- # the background job
- def background_upload_enabled?
- false
- end
-
def default_store
object_store_enabled? ? ObjectStorage::Store::REMOTE : ObjectStorage::Store::LOCAL
end
diff --git a/app/uploaders/terraform/state_uploader.rb b/app/uploaders/terraform/state_uploader.rb
index 091b253b0ed..61e7ed7b0e6 100644
--- a/app/uploaders/terraform/state_uploader.rb
+++ b/app/uploaders/terraform/state_uploader.rb
@@ -48,10 +48,6 @@ module Terraform
false
end
- def background_upload_enabled?
- false
- end
-
def proxy_download_enabled?
true
end
diff --git a/app/validators/iso8601_date_validator.rb b/app/validators/iso8601_date_validator.rb
new file mode 100644
index 00000000000..2b4682f0572
--- /dev/null
+++ b/app/validators/iso8601_date_validator.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class Iso8601DateValidator < ActiveModel::EachValidator
+ def validate_each(record, attribute, value)
+ Date.iso8601(record.read_attribute_before_type_cast(attribute).to_s)
+ rescue ArgumentError, TypeError
+ record.errors.add(attribute, _('must be in ISO 8601 format'))
+ end
+end
diff --git a/app/validators/json_schemas/build_metadata_id_tokens.json b/app/validators/json_schemas/build_metadata_id_tokens.json
index 7f39c7274f3..d97b2241ca3 100644
--- a/app/validators/json_schemas/build_metadata_id_tokens.json
+++ b/app/validators/json_schemas/build_metadata_id_tokens.json
@@ -5,18 +5,27 @@
"patternProperties": {
".*": {
"type": "object",
- "patternProperties": {
- "^id_token$": {
- "type": "object",
- "required": ["aud"],
- "properties": {
- "aud": { "type": "string" },
- "field": { "type": "string" }
- },
- "additionalProperties": false
+ "required": [
+ "aud"
+ ],
+ "properties": {
+ "aud": {
+ "oneOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "minItems": 1,
+ "uniqueItems": true
+ }
+ ]
}
},
"additionalProperties": false
}
}
-}
+} \ No newline at end of file
diff --git a/app/validators/json_schemas/build_report_result_data.json b/app/validators/json_schemas/build_report_result_data.json
index 0a12c9c39a7..d109389a046 100644
--- a/app/validators/json_schemas/build_report_result_data.json
+++ b/app/validators/json_schemas/build_report_result_data.json
@@ -3,11 +3,16 @@
"description": "Build report result data",
"type": "object",
"properties": {
- "coverage": { "type": "float" },
+ "coverage": {
+ "type": "number",
+ "format": "float"
+ },
"tests": {
"type": "object",
- "items": { "$ref": "./build_report_result_data_tests.json" }
+ "items": {
+ "$ref": "./build_report_result_data_tests.json"
+ }
}
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/app/validators/json_schemas/build_report_result_data_tests.json b/app/validators/json_schemas/build_report_result_data_tests.json
index 610070fde5f..3b6a2688313 100644
--- a/app/validators/json_schemas/build_report_result_data_tests.json
+++ b/app/validators/json_schemas/build_report_result_data_tests.json
@@ -3,12 +3,24 @@
"description": "Build report result data tests",
"type": "object",
"properties": {
- "name": { "type": "string" },
- "duration": { "type": "string" },
- "failed": { "type": "integer" },
- "errored": { "type": "integer" },
- "skipped": { "type": "integer" },
- "success": { "type": "integer" }
+ "name": {
+ "type": "string"
+ },
+ "duration": {
+ "type": "string"
+ },
+ "failed": {
+ "type": "integer"
+ },
+ "errored": {
+ "type": "integer"
+ },
+ "skipped": {
+ "type": "integer"
+ },
+ "success": {
+ "type": "integer"
+ }
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/app/validators/json_schemas/ci_secure_file_metadata.json b/app/validators/json_schemas/ci_secure_file_metadata.json
index 46a7ff60b8f..66e778d6026 100644
--- a/app/validators/json_schemas/ci_secure_file_metadata.json
+++ b/app/validators/json_schemas/ci_secure_file_metadata.json
@@ -4,10 +4,10 @@
"properties": {
"id": { "type": "string" },
"team_name": { "type": "string" },
- "team_id": { "type": "string" },
+ "team_id": { "type": "array" },
"app_name": { "type": "string" },
"app_id": { "type": "string" },
- "app_id_prefix": { "type": "string" },
+ "app_id_prefix": { "type": "array" },
"xcode_managed": { "type": "boolean" },
"entitlements": { "type": "object" },
"devices": { "type": "array" },
diff --git a/app/validators/json_schemas/daily_build_group_report_result_data.json b/app/validators/json_schemas/daily_build_group_report_result_data.json
index 2b073506375..5b153b47b1e 100644
--- a/app/validators/json_schemas/daily_build_group_report_result_data.json
+++ b/app/validators/json_schemas/daily_build_group_report_result_data.json
@@ -3,7 +3,10 @@
"description": "Daily build group report result data",
"type": "object",
"properties": {
- "coverage": { "type": "float" }
+ "coverage": {
+ "type": "number",
+ "format": "float"
+ }
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/app/validators/json_schemas/merge_request_predictions_suggested_reviewers.json b/app/validators/json_schemas/merge_request_predictions_suggested_reviewers.json
deleted file mode 100644
index 8e80b52d9b8..00000000000
--- a/app/validators/json_schemas/merge_request_predictions_suggested_reviewers.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "description": "Merge request predictions suggested reviewers",
- "type": "object",
- "properties": {
- "top_n": { "type": "number" },
- "version": { "type": "string" },
- "reviewers": { "type": "array" }
- },
- "additionalProperties": true
-}
diff --git a/app/validators/json_schemas/web_hooks_url_variables.json b/app/validators/json_schemas/web_hooks_url_variables.json
index ea504d114e3..27b251a059f 100644
--- a/app/validators/json_schemas/web_hooks_url_variables.json
+++ b/app/validators/json_schemas/web_hooks_url_variables.json
@@ -8,7 +8,7 @@
"^[A-Za-z]+[0-9]*(?:[._-][A-Za-z0-9]+)*$": {
"type": "string",
"minLength": 1,
- "maxLength": 100
+ "maxLength": 2048
}
}
}
diff --git a/app/views/abuse_reports/new.html.haml b/app/views/abuse_reports/new.html.haml
index aaa85e81bd4..d5dfddef837 100644
--- a/app/views/abuse_reports/new.html.haml
+++ b/app/views/abuse_reports/new.html.haml
@@ -1,8 +1,8 @@
-- page_title _("Report abuse to admin")
+- page_title _("Report abuse to administrator")
%h1.page-title.gl-font-size-h-display
- = _("Report abuse to admin")
+ = _("Report abuse to administrator")
%p
- = _("Please use this form to report to the admin users who create spam issues, comments or behave inappropriately.")
+ = _("Use this form to report to the administrator users who create spam issues, comments or behave inappropriately.")
%p
= _("A member of the abuse team will review your report as soon as possible.")
%hr
diff --git a/app/views/admin/abuse_reports/_abuse_report.html.haml b/app/views/admin/abuse_reports/_abuse_report.html.haml
index 00e5650b551..eeedd58ec15 100644
--- a/app/views/admin/abuse_reports/_abuse_report.html.haml
+++ b/app/views/admin/abuse_reports/_abuse_report.html.haml
@@ -24,11 +24,13 @@
= markdown_field(abuse_report, :message)
%td
- if user
- = link_to _('Remove user & report'), admin_abuse_report_path(abuse_report, remove_user: true),
- data: { confirm: _("USER %{user} WILL BE REMOVED! Are you sure?") % { user: user.name }, confirm_btn_variant: "danger" }, aria: { label: _('Remove user & report') }, remote: true, method: :delete, class: "gl-button btn btn-block btn-danger js-remove-tr"
+ = render Pajamas::ButtonComponent.new(href: admin_abuse_report_path(abuse_report, remove_user: true), variant: :danger, block: true, button_options: { data: { confirm: _("USER %{user} WILL BE REMOVED! Are you sure?") % { user: user.name }, confirm_btn_variant: "danger", remote: true, method: :delete }, class: "js-remove-tr gl-mb-5" }) do
+ = _('Remove user & report')
- if user && !user.blocked?
- = link_to _('Block user'), block_admin_user_path(user), data: { confirm: _('USER WILL BE BLOCKED! Are you sure?') }, aria: { label: _('Block user') }, method: :put, class: "gl-button btn btn-default btn-block"
+ = render Pajamas::ButtonComponent.new(href: block_admin_user_path(user), block: true, button_options: { data: { confirm: _('USER WILL BE BLOCKED! Are you sure?'), method: :put }, class: "gl-mb-5" }) do
+ = _('Block user')
- else
- .gl-button.btn.btn-default.disabled.btn-block
+ = render Pajamas::ButtonComponent.new(href: block_admin_user_path(user), block: true, disabled: true, button_options: { data: { confirm: _('USER WILL BE BLOCKED! Are you sure?'), method: :put }, class: "gl-mb-5" }) do
= _('Already blocked')
- = link_to _('Remove report'), [:admin, abuse_report], remote: true, method: :delete, class: "gl-button btn btn-default btn-block btn-close js-remove-tr"
+ = render Pajamas::ButtonComponent.new(href: [:admin, abuse_report], block: true, button_options: { data: { remote: true, method: :delete }, class: "js-remove-tr" }) do
+ = _('Remove report')
diff --git a/app/views/admin/application_settings/_account_and_limit.html.haml b/app/views/admin/application_settings/_account_and_limit.html.haml
index 0f7b10f822d..21f69f6700f 100644
--- a/app/views/admin/application_settings/_account_and_limit.html.haml
+++ b/app/views/admin/application_settings/_account_and_limit.html.haml
@@ -46,7 +46,7 @@
= f.text_field :user_default_internal_regex, placeholder: _('Regex pattern'), class: 'form-control gl-form-input gl-mt-2'
.help-block
= _('Specify an email address regex pattern to identify default internal users.')
- = link_to _('Learn more.'), help_page_path('user/permissions', anchor: 'setting-new-users-to-external'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('user/admin_area/external_users', anchor: 'set-a-new-user-to-external'), target: '_blank', rel: 'noopener noreferrer'
- unless Gitlab.com?
.form-group
= f.label :deactivate_dormant_users, _('Dormant users'), class: 'label-bold'
diff --git a/app/views/admin/application_settings/_ci_cd.html.haml b/app/views/admin/application_settings/_ci_cd.html.haml
index f6635ad17ef..8fafa52cd4c 100644
--- a/app/views/admin/application_settings/_ci_cd.html.haml
+++ b/app/views/admin/application_settings/_ci_cd.html.haml
@@ -1,6 +1,6 @@
.settings-content
= gitlab_ui_form_for @application_setting, url: ci_cd_admin_application_settings_path(anchor: 'js-ci-cd-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting )
+ = form_errors(@application_setting)
%fieldset
.form-group
@@ -61,7 +61,7 @@
%h4
= s_('AdminSettings|CI/CD limits')
%p
- = s_('AdminSettings|Set limit to 0 to disable it.')
+ = s_('AdminSettings|By default, set a limit to 0 to have no limit.')
.scrolling-tabs-container.inner-page-scroll-tabs
- if @plans.size > 1
%ul.nav-links.scrolling-tabs.mobile-separator.nav.nav-tabs.gl-mb-5
@@ -94,10 +94,14 @@
.form-group
= f.label :ci_needs_size_limit, s_('AdminSettings|Maximum number of DAG dependencies that a job can have')
= f.number_field :ci_needs_size_limit, class: 'form-control gl-form-input'
+ .form-text.text-muted= s_('AdminSettings|This limit cannot be disabled. Set to 0 to block all DAG dependencies.')
.form-group
= f.label :ci_registered_group_runners, s_('AdminSettings|Maximum number of runners registered per group')
= f.number_field :ci_registered_group_runners, class: 'form-control gl-form-input'
.form-group
= f.label :ci_registered_project_runners, s_('AdminSettings|Maximum number of runners registered per project')
= f.number_field :ci_registered_project_runners, class: 'form-control gl-form-input'
+ .form-group
+ = f.label :pipeline_hierarchy_size, s_("AdminSettings|Maximum number of downstream pipelines in a pipeline's hierarchy tree")
+ = f.number_field :pipeline_hierarchy_size, class: 'form-control gl-form-input'
= f.submit s_('AdminSettings|Save %{name} limits').html_safe % { name: plan.name.capitalize }, pajamas_button: true
diff --git a/app/views/admin/application_settings/_default_branch.html.haml b/app/views/admin/application_settings/_default_branch.html.haml
index 7be4bac02fd..67de5ffb2b9 100644
--- a/app/views/admin/application_settings/_default_branch.html.haml
+++ b/app/views/admin/application_settings/_default_branch.html.haml
@@ -8,7 +8,7 @@
= f.label :default_branch_name, _('Initial default branch name'), class: 'label-light'
= f.text_field :default_branch_name, placeholder: Gitlab::DefaultBranch.value, class: 'form-control gl-form-input'
%span.form-text.text-muted
- = (s_("AdminSettings|If not specified at the group or instance level, the default is %{default_initial_branch_name}. Does not affect existing repositories.") % { default_initial_branch_name: fallback_branch_name } ).html_safe
+ = (s_("AdminSettings|If not specified at the group or instance level, the default is %{default_initial_branch_name}. Does not affect existing repositories.") % { default_initial_branch_name: fallback_branch_name }).html_safe
= render 'shared/default_branch_protection', f: f
diff --git a/app/views/admin/application_settings/_error_tracking.html.haml b/app/views/admin/application_settings/_error_tracking.html.haml
index 5a8aba5784e..aa42cd99e89 100644
--- a/app/views/admin/application_settings/_error_tracking.html.haml
+++ b/app/views/admin/application_settings/_error_tracking.html.haml
@@ -37,4 +37,4 @@
= f.label :error_tracking_api_url, _('Opstrace endpoint for Error Tracking integration'), class: 'label-light'
= f.text_field :error_tracking_api_url, class: 'form-control gl-form-input'
- = f.submit _('Save changes'), class: 'gl-button btn btn-confirm'
+ = f.submit _('Save changes'), pajamas_button: true
diff --git a/app/views/admin/application_settings/_git_lfs_limits.html.haml b/app/views/admin/application_settings/_git_lfs_limits.html.haml
index b8970a5bcf1..638984ae97a 100644
--- a/app/views/admin/application_settings/_git_lfs_limits.html.haml
+++ b/app/views/admin/application_settings/_git_lfs_limits.html.haml
@@ -15,4 +15,4 @@
= f.label :throttle_authenticated_git_lfs_period_in_seconds, _('Authenticated Git LFS rate limit period in seconds'), class: 'gl-font-weight-bold'
= f.number_field :throttle_authenticated_git_lfs_period_in_seconds, class: 'form-control gl-form-input'
- = f.submit _('Save changes'), class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
+ = f.submit _('Save changes'), pajamas_button: true, data: { qa_selector: 'save_changes_button' }
diff --git a/app/views/admin/application_settings/_grafana.html.haml b/app/views/admin/application_settings/_grafana.html.haml
index 7f305b9ad9c..e2a53106cec 100644
--- a/app/views/admin/application_settings/_grafana.html.haml
+++ b/app/views/admin/application_settings/_grafana.html.haml
@@ -11,4 +11,4 @@
= f.text_field :grafana_url, class: 'form-control gl-form-input', placeholder: '/-/grafana'
%span.form-text.text-muted#support_help_block= _('URL of the Grafana instance to link to from the Metrics Dashboard menu item.')
- = f.submit _('Save changes'), class: "gl-button btn btn-confirm"
+ = f.submit _('Save changes'), pajamas_button: true
diff --git a/app/views/admin/application_settings/_kroki.html.haml b/app/views/admin/application_settings/_kroki.html.haml
index 4f5a313d7b7..f1f6dd34401 100644
--- a/app/views/admin/application_settings/_kroki.html.haml
+++ b/app/views/admin/application_settings/_kroki.html.haml
@@ -24,7 +24,7 @@
- install_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: install_link_url }
= html_escape(_('Use the public cloud instance URL (%{kroki_public_url}) or %{install_link_start}install Kroki%{install_link_end} on your own infrastructure and use your own instance URL.')) % { kroki_public_url: '<code>https://kroki.io</code>'.html_safe, install_link_start: install_link_start, install_link_end: '</a>'.html_safe }
.form-group
- = f.label :kroki_formats, 'Additional diagram formats', class: 'label-bold'
+ = f.label :kroki_formats, _('Additional diagram formats'), class: 'label-bold'
.form-text.text-muted
- container_link_url = 'https://docs.kroki.io/kroki/setup/install/#images'
- container_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: container_link_url }
diff --git a/app/views/admin/application_settings/_localization.html.haml b/app/views/admin/application_settings/_localization.html.haml
index 90cb34395d8..9ec4afec484 100644
--- a/app/views/admin/application_settings/_localization.html.haml
+++ b/app/views/admin/application_settings/_localization.html.haml
@@ -15,5 +15,12 @@
- time_tracking_help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: time_tracking_help_link }
= f.gitlab_ui_checkbox_component :time_tracking_limit_to_hours, _('Limit display of time tracking units to hours.'), help_text: _('Display time tracking in issues in total hours only. %{link_start}What is time tracking?%{link_end}').html_safe % { link_start: time_tracking_help_link_start, link_end: '</a>'.html_safe }
+ .form-group
+ = f.label :default_preferred_language, class: 'label-bold' do
+ = _('Default language')
+ = f.select :default_preferred_language, default_preferred_language_choices, {}, class: 'gl-form-select custom-select'
+ .form-text.text-muted
+ = s_('Default language for users who are not logged in.')
+
= f.submit _('Save changes'), pajamas_button: true
diff --git a/app/views/admin/application_settings/_mailgun.html.haml b/app/views/admin/application_settings/_mailgun.html.haml
index 1604419869c..fb15f6e79a5 100644
--- a/app/views/admin/application_settings/_mailgun.html.haml
+++ b/app/views/admin/application_settings/_mailgun.html.haml
@@ -19,4 +19,4 @@
= f.label :mailgun_signing_key, _('Mailgun HTTP webhook signing key'), class: 'label-light'
= f.text_field :mailgun_signing_key, class: 'form-control gl-form-input'
- = f.submit _('Save changes'), class: 'gl-button btn btn-confirm'
+ = f.submit _('Save changes'), pajamas_button: true
diff --git a/app/views/admin/application_settings/_outbound.html.haml b/app/views/admin/application_settings/_outbound.html.haml
index 3505a3bf3ee..1821c8ef4bb 100644
--- a/app/views/admin/application_settings/_outbound.html.haml
+++ b/app/views/admin/application_settings/_outbound.html.haml
@@ -20,6 +20,6 @@
.form-group
= f.gitlab_ui_checkbox_component :dns_rebinding_protection_enabled,
s_('OutboundRequests|Enforce DNS rebinding attack protection'),
- help_text: _('OutboundRequests|Resolve IP addresses once and uses them to submit requests.')
+ help_text: s_('OutboundRequests|Resolve IP addresses once and uses them to submit requests.')
= f.submit _('Save changes'), pajamas_button: true, data: { qa_selector: 'save_changes_button' }
diff --git a/app/views/admin/application_settings/_performance_bar.html.haml b/app/views/admin/application_settings/_performance_bar.html.haml
index d4f6d84ea74..c09ba01b7ed 100644
--- a/app/views/admin/application_settings/_performance_bar.html.haml
+++ b/app/views/admin/application_settings/_performance_bar.html.haml
@@ -4,7 +4,7 @@
%fieldset
.form-group
= f.gitlab_ui_checkbox_component :performance_bar_enabled,
- s_("Allow non-administrators access to the performance bar"),
+ _("Allow non-administrators access to the performance bar"),
checkbox_options: { data: { qa_selector: 'enable_performance_bar_checkbox' } }
.form-group
= f.label :performance_bar_allowed_group_path, _('Allow access to members of the following group'), class: 'label-bold'
diff --git a/app/views/admin/application_settings/_plantuml.html.haml b/app/views/admin/application_settings/_plantuml.html.haml
index 5c86ce8dbfb..42f289d87b2 100644
--- a/app/views/admin/application_settings/_plantuml.html.haml
+++ b/app/views/admin/application_settings/_plantuml.html.haml
@@ -22,4 +22,4 @@
.form-text.text-muted
= _('The hostname of your PlantUML server.')
- = f.submit _('Save changes'), class: "gl-button btn btn-confirm"
+ = f.submit _('Save changes'), pajamas_button: true
diff --git a/app/views/admin/application_settings/_repository_check.html.haml b/app/views/admin/application_settings/_repository_check.html.haml
index aaf76c5ff7a..332d3a94b92 100644
--- a/app/views/admin/application_settings/_repository_check.html.haml
+++ b/app/views/admin/application_settings/_repository_check.html.haml
@@ -19,28 +19,35 @@
%h4= _("Housekeeping")
.form-group
- help_text = _("Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time.")
- - help_link = link_to s_('Learn more.'), help_page_path('administration/housekeeping.md', anchor: 'configure-push-based-maintenance'), target: '_blank', rel: 'noopener noreferrer'
+ - help_link = link_to _('Learn more.'), help_page_path('administration/housekeeping.md', anchor: 'configure-push-based-maintenance'), target: '_blank', rel: 'noopener noreferrer'
= f.gitlab_ui_checkbox_component :housekeeping_enabled,
_("Enable automatic repository housekeeping"),
help_text: '%{help_text} %{help_link}'.html_safe % { help_text: help_text, help_link: help_link }
- .form-group
- = f.label :housekeeping_incremental_repack_period, 'Incremental repack period', class: 'label-bold'
- = f.number_field :housekeeping_incremental_repack_period, class: 'form-control gl-form-input'
- .form-text.text-muted
- = html_escape(s_('Number of Git pushes after which an incremental %{code_start}git repack%{code_end} is run.')) % { code_start: '<code>'.html_safe, code_end: '</code>'.html_safe }
- .form-group
- = f.label :housekeeping_full_repack_period, 'Full repack period', class: 'label-bold'
- = f.number_field :housekeeping_full_repack_period, class: 'form-control gl-form-input'
- .form-text.text-muted
- = html_escape(s_('Number of Git pushes after which a full %{code_start}git repack%{code_end} is run.')) % { code_start: '<code>'.html_safe, code_end: '</code>'.html_safe }
- .form-group
- = f.label :housekeeping_gc_period, _('Git GC period'), class: 'label-bold'
- = f.number_field :housekeeping_gc_period, class: 'form-control gl-form-input'
- .form-text.text-muted
- = html_escape(s_('Number of Git pushes after which %{code_start}git gc%{code_end} is run.')) % { code_start: '<code>'.html_safe, code_end: '</code>'.html_safe }
+ - if Feature.enabled?(:optimized_housekeeping)
+ .form-group
+ = f.label :housekeeping_incremental_repack_period, _('Optimize repository period'), class: 'label-bold'
+ = f.number_field :housekeeping_incremental_repack_period, class: 'form-control gl-form-input'
+ .form-text.text-muted
+ = _('Number of Git pushes after which Gitaly is asked to optimize a repository.')
+ - else
+ .form-group
+ = f.label :housekeeping_incremental_repack_period, 'Incremental repack period', class: 'label-bold'
+ = f.number_field :housekeeping_incremental_repack_period, class: 'form-control gl-form-input'
+ .form-text.text-muted
+ = html_escape(s_('Number of Git pushes after which an incremental %{code_start}git repack%{code_end} is run.')) % { code_start: '<code>'.html_safe, code_end: '</code>'.html_safe }
+ .form-group
+ = f.label :housekeeping_full_repack_period, 'Full repack period', class: 'label-bold'
+ = f.number_field :housekeeping_full_repack_period, class: 'form-control gl-form-input'
+ .form-text.text-muted
+ = html_escape(s_('Number of Git pushes after which a full %{code_start}git repack%{code_end} is run.')) % { code_start: '<code>'.html_safe, code_end: '</code>'.html_safe }
+ .form-group
+ = f.label :housekeeping_gc_period, _('Git GC period'), class: 'label-bold'
+ = f.number_field :housekeeping_gc_period, class: 'form-control gl-form-input'
+ .form-text.text-muted
+ = html_escape(s_('Number of Git pushes after which %{code_start}git gc%{code_end} is run.')) % { code_start: '<code>'.html_safe, code_end: '</code>'.html_safe }
.sub-section
%h4= s_("AdminSettings|Inactive project deletion")
.js-inactive-project-deletion-form{ data: inactive_projects_deletion_data(@application_setting) }
- = f.submit _('Save changes'), class: "gl-button btn btn-confirm"
+ = f.submit _('Save changes'), pajamas_button: true
diff --git a/app/views/admin/application_settings/_repository_static_objects.html.haml b/app/views/admin/application_settings/_repository_static_objects.html.haml
index d962d050ebc..b301ec15a0e 100644
--- a/app/views/admin/application_settings/_repository_static_objects.html.haml
+++ b/app/views/admin/application_settings/_repository_static_objects.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: repository_admin_application_settings_path(anchor: 'js-repository-static-objects-settings'), html: { class: 'fieldset-form' } do |f|
+= gitlab_ui_form_for @application_setting, url: repository_admin_application_settings_path(anchor: 'js-repository-static-objects-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -15,4 +15,4 @@
%span.form-text.text-muted#static_objects_external_storage_auth_token_help_block
= _('Secure token that identifies an external storage request.')
- = f.submit _('Save changes'), class: "gl-button btn btn-confirm"
+ = f.submit _('Save changes'), pajamas_button: true
diff --git a/app/views/admin/application_settings/_repository_storage.html.haml b/app/views/admin/application_settings/_repository_storage.html.haml
index 12dd8816783..066d77c792b 100644
--- a/app/views/admin/application_settings/_repository_storage.html.haml
+++ b/app/views/admin/application_settings/_repository_storage.html.haml
@@ -20,7 +20,7 @@
- weights_link_url = help_page_path('administration/repository_storage_paths.md', anchor: 'configure-where-new-repositories-are-stored')
- weights_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: weights_link_url }
= html_escape(s_('Enter %{weights_link_start}weights%{weights_link_end} for storages for new repositories. Configured storages appear below.')) % { weights_link_start: weights_link_start, weights_link_end: '</a>'.html_safe }
- = link_to s_('Learn more.'), help_page_path('administration/repository_storage_paths.md'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('administration/repository_storage_paths.md'), target: '_blank', rel: 'noopener noreferrer'
.form-check
= f.fields_for :repository_storages_weighted, storage_weights do |storage_form|
- Gitlab.config.repositories.storages.each_key do |storage|
diff --git a/app/views/admin/application_settings/_runner_registrars_form.html.haml b/app/views/admin/application_settings/_runner_registrars_form.html.haml
index 1d6051a06ea..08486a808bf 100644
--- a/app/views/admin/application_settings/_runner_registrars_form.html.haml
+++ b/app/views/admin/application_settings/_runner_registrars_form.html.haml
@@ -5,7 +5,7 @@
.gl-form-group
%span.form-text.gl-mb-3.gl-mt-0
= _('If no options are selected, only administrators can register runners.')
- = link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'prevent-users-from-registering-runners'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'restrict-runner-registration-by-all-users-in-an-instance'), target: '_blank', rel: 'noopener noreferrer'
= hidden_field_tag "application_setting[valid_runner_registrars][]", nil
- ApplicationSetting::VALID_RUNNER_REGISTRAR_TYPES.each do |type|
= f.gitlab_ui_checkbox_component :valid_runner_registrars, s_("Runners|Members of the %{type} can register runners") % { type: type },
@@ -13,4 +13,4 @@
checked_value: type,
unchecked_value: nil
- = f.submit _('Save changes'), class: "gl-button btn btn-confirm"
+ = f.submit _('Save changes'), pajamas_button: true
diff --git a/app/views/admin/application_settings/_search_limits.html.haml b/app/views/admin/application_settings/_search_limits.html.haml
index 945c9397f0d..396c263dd5d 100644
--- a/app/views/admin/application_settings/_search_limits.html.haml
+++ b/app/views/admin/application_settings/_search_limits.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-search-limits-settings'), html: { class: 'fieldset-form' } do |f|
+= gitlab_ui_form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-search-limits-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -13,4 +13,4 @@
= f.number_field :search_rate_limit_unauthenticated, class: 'form-control gl-form-input'
- = f.submit _('Save changes'), class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
+ = f.submit _('Save changes'), data: { qa_selector: 'save_changes_button' }, pajamas_button: true
diff --git a/app/views/admin/application_settings/_spam.html.haml b/app/views/admin/application_settings/_spam.html.haml
index bb512940be2..96face44344 100644
--- a/app/views/admin/application_settings/_spam.html.haml
+++ b/app/views/admin/application_settings/_spam.html.haml
@@ -50,8 +50,7 @@
= f.label :akismet_api_key, _('Akismet API Key'), class: 'label-bold'
= f.text_field :akismet_api_key, class: 'form-control gl-form-input'
.form-text.text-muted
- Generate API key at
- %a{ href: 'http://www.akismet.com', target: 'blank', rel: 'noopener noreferrer' } http://www.akismet.com
+ = _("Generate API key at %{site}").html_safe % { site: link_to('http://www.akismet.com', 'http://www.akismet.com', target: 'blank', ref: 'noopener noreferrer') }
%h5
= _('IP address restrictions')
@@ -86,4 +85,4 @@
= f.text_field :spam_check_api_key, class: 'form-control gl-form-input'
.form-text.text-muted= _('The API key used by GitLab for accessing the Spam Check service endpoint.')
- = f.submit _('Save changes'), class: "gl-button btn btn-confirm"
+ = f.submit _('Save changes'), pajamas_button: true
diff --git a/app/views/admin/application_settings/_terminal.html.haml b/app/views/admin/application_settings/_terminal.html.haml
index c53f63e124b..b07db09d06c 100644
--- a/app/views/admin/application_settings/_terminal.html.haml
+++ b/app/views/admin/application_settings/_terminal.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-terminal-settings'), html: { class: 'fieldset-form', id: 'terminal-settings' } do |f|
+= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-terminal-settings'), html: { class: 'fieldset-form', id: 'terminal-settings' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -7,4 +7,4 @@
= f.number_field :terminal_max_session_time, class: 'form-control gl-form-input'
.form-text.text-muted
= _('Maximum time, in seconds, for a web terminal websocket connection. 0 for unlimited.')
- = f.submit _('Save changes'), class: "gl-button btn btn-confirm"
+ = f.submit _('Save changes'), pajamas_button: true
diff --git a/app/views/admin/application_settings/_terraform_limits.html.haml b/app/views/admin/application_settings/_terraform_limits.html.haml
new file mode 100644
index 00000000000..bdb0ba5cc85
--- /dev/null
+++ b/app/views/admin/application_settings/_terraform_limits.html.haml
@@ -0,0 +1,11 @@
+= gitlab_ui_form_for @application_setting, url: preferences_admin_application_settings_path(anchor: 'js-terraform-limits-settings'), html: { class: 'fieldset-form' } do |f|
+ = form_errors(@application_setting)
+
+ %fieldset
+ .form-group
+ = f.label :max_terraform_state_size_bytes, s_('TerraformLimits|Terraform state size limit (bytes)'), class: 'label-bold'
+ = f.number_field :max_terraform_state_size_bytes, class: 'form-control gl-form-input'
+ .form-text.text-muted
+ = s_("TerraformLimits|Maximum file size (in bytes) of Terraform state files. Set to 0 for no limit.")
+
+ = f.submit _('Save changes'), pajamas_button: true
diff --git a/app/views/admin/application_settings/_visibility_and_access.html.haml b/app/views/admin/application_settings/_visibility_and_access.html.haml
index 85bee72e863..9c8770b8998 100644
--- a/app/views/admin/application_settings/_visibility_and_access.html.haml
+++ b/app/views/admin/application_settings/_visibility_and_access.html.haml
@@ -36,10 +36,14 @@
= render_if_exists 'admin/application_settings/ldap_access_setting', form: f
- .form-group
+ .form-group{ data: { testid: 'project-export' } }
= f.label :project_export, s_('AdminSettings|Project export'), class: 'label-bold'
= f.gitlab_ui_checkbox_component :project_export_enabled, s_('AdminSettings|Enabled')
+ .form-group{ data: { testid: 'bulk-import' } }
+ = f.label :bulk_import, s_('AdminSettings|Enable migrating GitLab groups and projects by direct transfer'), class: 'gl-font-weight-bold'
+ = f.gitlab_ui_checkbox_component :bulk_import_enabled, s_('AdminSettings|Enabled')
+
.form-group
%label.label-bold= _('Enabled Git access protocols')
= select(:application_setting, :enabled_git_access_protocol, [['Both SSH and HTTP(S)', nil], ['Only SSH', 'ssh'], ['Only HTTP(S)', 'http']], {}, class: 'form-control')
@@ -67,4 +71,4 @@
-# This is added for Jihu edition in https://jihulab.com/gitlab-cn/gitlab/-/merge_requests/1112
= render_if_exists 'admin/application_settings/disable_download_button', f: f
- = f.submit _('Save changes'), class: "gl-button btn btn-confirm"
+ = f.submit _('Save changes'), pajamas_button: true
diff --git a/app/views/admin/application_settings/appearances/_system_header_footer_form.html.haml b/app/views/admin/application_settings/appearances/_system_header_footer_form.html.haml
index 415606c055d..d7bb3a85f3a 100644
--- a/app/views/admin/application_settings/appearances/_system_header_footer_form.html.haml
+++ b/app/views/admin/application_settings/appearances/_system_header_footer_form.html.haml
@@ -20,7 +20,7 @@
label_options: { class: 'gl-font-weight-bold!' }
.form-group.js-toggle-colors-container
- %button.btn.gl-button.btn-link.js-toggle-colors-link{ type: 'button' }
+ = render Pajamas::ButtonComponent.new(variant: :link, button_options: { class: 'js-toggle-colors-link' }) do
= _('Customize colors')
.form-group.js-toggle-colors-container.hide
= form.label :message_background_color, _('Background Color'), class: 'col-form-label label-bold'
diff --git a/app/views/admin/application_settings/appearances/show.html.haml b/app/views/admin/application_settings/appearances/show.html.haml
index 77a08913666..1e55190d53b 100644
--- a/app/views/admin/application_settings/appearances/show.html.haml
+++ b/app/views/admin/application_settings/appearances/show.html.haml
@@ -1,4 +1,5 @@
- page_title _("Appearance")
- @content_class = "limit-container-width" unless fluid_layout
+- add_page_specific_style 'page_bundles/settings'
= render 'form'
diff --git a/app/views/admin/application_settings/ci/_header.html.haml b/app/views/admin/application_settings/ci/_header.html.haml
index 0adb6cbbcf0..79c07f491fc 100644
--- a/app/views/admin/application_settings/ci/_header.html.haml
+++ b/app/views/admin/application_settings/ci/_header.html.haml
@@ -8,12 +8,13 @@
%p
= _('Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables.')
- = link_to s_('Learn more.'), help_page_path('ci/variables/index', anchor: 'add-a-cicd-variable-to-an-instance'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('ci/variables/index', anchor: 'add-a-cicd-variable-to-an-instance'), target: '_blank', rel: 'noopener noreferrer'
%p
= _('Variables can be:')
%ul
%li
= html_escape(_('%{code_open}Protected:%{code_close} Only exposed to protected branches or protected tags.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
+ = link_to _('Learn more.'), help_page_path('ci/variables/index', anchor: 'protected-cicd-variables'), target: '_blank', rel: 'noopener noreferrer'
%li
= html_escape(_('%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
= link_to _('Learn more.'), help_page_path('ci/variables/index', anchor: 'mask-a-cicd-variable'), target: '_blank', rel: 'noopener noreferrer'
diff --git a/app/views/admin/application_settings/ci_cd.html.haml b/app/views/admin/application_settings/ci_cd.html.haml
index b7244c45871..0414382a108 100644
--- a/app/views/admin/application_settings/ci_cd.html.haml
+++ b/app/views/admin/application_settings/ci_cd.html.haml
@@ -1,5 +1,6 @@
- breadcrumb_title _("CI/CD")
- page_title _("CI/CD")
+- add_page_specific_style 'page_bundles/settings'
- @content_class = "limit-container-width" unless fluid_layout
%section.settings.no-animate#js-ci-cd-variables{ class: ('expanded' if expanded_by_default?) }
diff --git a/app/views/admin/application_settings/general.html.haml b/app/views/admin/application_settings/general.html.haml
index 6d8428d1aa6..8c9d54cd5d8 100644
--- a/app/views/admin/application_settings/general.html.haml
+++ b/app/views/admin/application_settings/general.html.haml
@@ -1,5 +1,6 @@
- breadcrumb_title _("General")
- page_title _("General")
+- add_page_specific_style 'page_bundles/settings'
- @content_class = "limit-container-width" unless fluid_layout
%section.settings.as-visibility-access.no-animate#js-visibility-settings{ class: ('expanded' if expanded_by_default?) }
@@ -124,4 +125,4 @@
= render 'admin/application_settings/eks'
= render 'admin/application_settings/floc'
= render_if_exists 'admin/application_settings/add_license'
-= render 'admin/application_settings/jira_connect' if Feature.enabled?(:jira_connect_oauth_self_managed_setting, current_user)
+= render 'admin/application_settings/jira_connect'
diff --git a/app/views/admin/application_settings/integrations.html.haml b/app/views/admin/application_settings/integrations.html.haml
index d818c587b79..fd1ad5cd304 100644
--- a/app/views/admin/application_settings/integrations.html.haml
+++ b/app/views/admin/application_settings/integrations.html.haml
@@ -1,5 +1,6 @@
- breadcrumb_title s_('Integrations|Instance-level integration management')
- page_title s_('Integrations|Instance-level integration management')
+- add_page_specific_style 'page_bundles/settings'
- @content_class = 'limit-container-width' unless fluid_layout
%h3= s_('Integrations|Instance-level integration management')
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 b79b189e9cf..b5981578866 100644
--- a/app/views/admin/application_settings/metrics_and_profiling.html.haml
+++ b/app/views/admin/application_settings/metrics_and_profiling.html.haml
@@ -2,6 +2,7 @@
- breadcrumb_title _("Metrics and profiling")
- page_title _("Metrics and profiling")
+- add_page_specific_style 'page_bundles/settings'
- @content_class = "limit-container-width" unless fluid_layout
%section.settings.as-prometheus.no-animate#js-prometheus-settings{ class: ('expanded' if expanded_by_default?) }
@@ -23,7 +24,7 @@
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Link to your Grafana instance.')
- = link_to s_('Learn more.'), help_page_path('administration/monitoring/performance/grafana_configuration.md'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('administration/monitoring/performance/grafana_configuration.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= render 'grafana'
@@ -36,7 +37,7 @@
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Enable access to the performance bar for non-administrators in a given group.')
- = link_to s_('Learn more.'), help_page_path('administration/monitoring/performance/performance_bar.md', anchor: 'enable-the-performance-bar-for-non-administrators'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('administration/monitoring/performance/performance_bar.md', anchor: 'enable-the-performance-bar-for-non-administrators'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= render 'performance_bar'
diff --git a/app/views/admin/application_settings/network.html.haml b/app/views/admin/application_settings/network.html.haml
index 485b3a9828b..779263b439f 100644
--- a/app/views/admin/application_settings/network.html.haml
+++ b/app/views/admin/application_settings/network.html.haml
@@ -1,5 +1,6 @@
- breadcrumb_title _("Network")
- page_title _("Network")
+- add_page_specific_style 'page_bundles/settings'
- @content_class = "limit-container-width" unless fluid_layout
%section.settings.as-performance.no-animate#js-performance-settings{ class: ('expanded' if expanded_by_default?) }
diff --git a/app/views/admin/application_settings/preferences.html.haml b/app/views/admin/application_settings/preferences.html.haml
index bd92f7d490c..dd6666542ca 100644
--- a/app/views/admin/application_settings/preferences.html.haml
+++ b/app/views/admin/application_settings/preferences.html.haml
@@ -1,5 +1,6 @@
- breadcrumb_title _("Preferences")
- page_title _("Preferences")
+- add_page_specific_style 'page_bundles/settings'
- @content_class = "limit-container-width" unless fluid_layout
%section.settings.as-email.no-animate#js-email-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'email_content' } }
@@ -32,7 +33,7 @@
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Additional text for the sign-in and Help page.')
- = link_to s_('Learn more.'), help_page_path('user/admin_area/settings/help_page.md'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('user/admin_area/settings/help_page.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= render 'help_page'
@@ -79,7 +80,7 @@
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
- = _('Configure the default first day of the week and time tracking units.')
+ = _('Configure the default first day of the week, time tracking units, and default language.')
.settings-content
= render 'localization'
@@ -96,3 +97,15 @@
.settings-content
= render 'sidekiq_job_limits'
+
+%section.settings.as-terraform-limits.no-animate#js-terraform-limits-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
+ = s_('TerraformLimits|Terraform limits')
+ = render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = s_('TerraformLimits|Limits for Terraform features')
+ = link_to s_('TerraformLimits|Learn more about Terraform limits.'), help_page_path('user/admin_area/settings/terraform_limits.md'), target: '_blank', rel: 'noopener noreferrer'
+ .settings-content
+ = render 'terraform_limits'
diff --git a/app/views/admin/application_settings/reporting.html.haml b/app/views/admin/application_settings/reporting.html.haml
index af9145bf1e7..3d803e95cd0 100644
--- a/app/views/admin/application_settings/reporting.html.haml
+++ b/app/views/admin/application_settings/reporting.html.haml
@@ -1,5 +1,6 @@
- breadcrumb_title _("Reporting")
- page_title _("Reporting")
+- add_page_specific_style 'page_bundles/settings'
- @content_class = "limit-container-width" unless fluid_layout
%section.settings.as-spam.no-animate#js-spam-settings{ class: ('expanded' if expanded_by_default?) }
diff --git a/app/views/admin/application_settings/repository.html.haml b/app/views/admin/application_settings/repository.html.haml
index 12063ea700b..50798ad476c 100644
--- a/app/views/admin/application_settings/repository.html.haml
+++ b/app/views/admin/application_settings/repository.html.haml
@@ -1,5 +1,6 @@
- breadcrumb_title _("Repository")
- page_title _("Repository")
+- add_page_specific_style 'page_bundles/settings'
- @content_class = "limit-container-width" unless fluid_layout
%section.settings.as-default-branch-name.no-animate#js-default-branch-name{ class: ('expanded' if expanded_by_default?) }
@@ -21,7 +22,7 @@
= expanded_by_default? ? 'Collapse' : 'Expand'
%p
= _('Configure repository mirroring.')
- = link_to s_('Learn more.'), help_page_path('user/project/repository/mirror/index.md'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('user/project/repository/mirror/index.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= render partial: 'repository_mirrors_form'
@@ -33,7 +34,7 @@
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Configure repository storage.')
- = link_to s_('Learn more.'), help_page_path('administration/repository_storage_paths.md'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('administration/repository_storage_paths.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= render 'repository_storage'
@@ -60,6 +61,6 @@
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Serve repository static objects (for example, archives and blobs) from external storage.')
- = link_to s_('Learn more.'), help_page_path('administration/static_objects_external_storage.md'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('administration/static_objects_external_storage.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= render 'repository_static_objects'
diff --git a/app/views/admin/application_settings/service_usage_data.html.haml b/app/views/admin/application_settings/service_usage_data.html.haml
index 82b627e1805..d6860cc08ac 100644
--- a/app/views/admin/application_settings/service_usage_data.html.haml
+++ b/app/views/admin/application_settings/service_usage_data.html.haml
@@ -2,6 +2,7 @@
- breadcrumb_title name
- page_title name
+- add_page_specific_style 'page_bundles/settings'
- @content_class = "limit-container-width" unless fluid_layout
- payload_class = 'js-service-ping-payload'
@@ -9,10 +10,10 @@
%h3= name
- if @service_ping_data_present
- = render Pajamas::ButtonComponent.new(button_options: { class: 'js-payload-preview-trigger gl-mr-2', data: { payload_selector: ".#{payload_class}" } } ) do
+ = render Pajamas::ButtonComponent.new(button_options: { class: 'js-payload-preview-trigger gl-mr-2', data: { payload_selector: ".#{payload_class}" } }) do
= gl_loading_icon(css_class: 'js-spinner gl-display-none', inline: true)
%span.js-text.gl-display-inline= _('Preview payload')
- = render Pajamas::ButtonComponent.new(button_options: { class: 'js-payload-download-trigger gl-mr-2', data: { endpoint: usage_data_admin_application_settings_path(format: :json) } } ) do
+ = render Pajamas::ButtonComponent.new(button_options: { class: 'js-payload-download-trigger gl-mr-2', data: { endpoint: usage_data_admin_application_settings_path(format: :json) } }) do
= gl_loading_icon(css_class: 'js-spinner gl-display-none', inline: true)
%span.js-text.gl-display-inline= _('Download payload')
%pre.js-syntax-highlight.code.highlight.gl-mt-2.gl-display-none{ class: payload_class, data: { endpoint: usage_data_admin_application_settings_path(format: :html) } }
diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml
index b603c7e5f49..a92bad5e601 100644
--- a/app/views/admin/applications/index.html.haml
+++ b/app/views/admin/applications/index.html.haml
@@ -14,11 +14,13 @@
.gl-max-w-full.gl-m-auto
%h1.h4.gl-font-size-h-display= s_('AdminArea|No applications found')
- = link_to _('New application'), new_admin_application_path, class: 'btn gl-button btn-confirm'
+ = render Pajamas::ButtonComponent.new(href: new_admin_application_path, variant: :confirm) do
+ = s_('New application')
- else
%hr
- %p= link_to _('New application'), new_admin_application_path, class: 'gl-button btn btn-confirm'
+ = render Pajamas::ButtonComponent.new(href: new_admin_application_path, variant: :confirm) do
+ = s_('New application')
.table-responsive
%table.b-table.gl-table.gl-w-full{ role: 'table' }
diff --git a/app/views/admin/broadcast_messages/_form.html.haml b/app/views/admin/broadcast_messages/_form.html.haml
index dfd3b87c674..4e05eb31010 100644
--- a/app/views/admin/broadcast_messages/_form.html.haml
+++ b/app/views/admin/broadcast_messages/_form.html.haml
@@ -17,14 +17,14 @@
= f.label :broadcast_type, _('Type')
.col-sm-10
= f.select :broadcast_type, broadcast_type_options, {}, class: 'form-control js-broadcast-message-type'
- .form-group.row.js-broadcast-message-background-color-form-group{ class: ('hidden' unless @broadcast_message.banner? ) }
+ .form-group.row.js-broadcast-message-background-color-form-group{ class: ('hidden' unless @broadcast_message.banner?) }
.col-sm-2.col-form-label
= f.label :theme, _("Theme")
.col-sm-10
.input-group
= f.select :theme, broadcast_theme_options, {}, class: 'form-control js-broadcast-message-theme'
- .form-group.row.js-broadcast-message-dismissable-form-group{ class: ('hidden' unless @broadcast_message.banner? ) }
+ .form-group.row.js-broadcast-message-dismissable-form-group{ class: ('hidden' unless @broadcast_message.banner?) }
.col-sm-2.col-form-label.pt-0
= f.label :starts_at, _("Dismissable")
.col-sm-10
@@ -62,6 +62,6 @@
= f.datetime_select :ends_at, {}, class: 'form-control form-control-inline'
.form-actions
- if @broadcast_message.persisted?
- = f.submit _("Update broadcast message"), class: "btn gl-button btn-confirm"
+ = f.submit _("Update broadcast message"), pajamas_button: true
- else
- = f.submit _("Add broadcast message"), class: "btn gl-button btn-confirm"
+ = f.submit _("Add broadcast message"), pajamas_button: true
diff --git a/app/views/admin/broadcast_messages/edit.html.haml b/app/views/admin/broadcast_messages/edit.html.haml
index 569aaa29cc4..28301833f7d 100644
--- a/app/views/admin/broadcast_messages/edit.html.haml
+++ b/app/views/admin/broadcast_messages/edit.html.haml
@@ -1,4 +1,19 @@
- breadcrumb_title _("Messages")
- page_title _("Broadcast Messages")
+- vue_app_enabled = Feature.enabled?(:vue_broadcast_messages, current_user)
-= render 'form'
+- if vue_app_enabled
+ #js-broadcast-message{ data: {
+ id: @broadcast_message.id,
+ message: @broadcast_message.message,
+ broadcast_type: @broadcast_message.broadcast_type,
+ theme: @broadcast_message.theme,
+ dismissable: @broadcast_message.dismissable.to_s,
+ target_access_levels: @broadcast_message.target_access_levels,
+ target_path: @broadcast_message.target_path,
+ starts_at: @broadcast_message.starts_at,
+ ends_at: @broadcast_message.ends_at,
+ target_access_level_options: target_access_level_options.to_json,
+ } }
+- else
+ = render 'form'
diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml
index 7559365e49a..7a005f9c982 100644
--- a/app/views/admin/broadcast_messages/index.html.haml
+++ b/app/views/admin/broadcast_messages/index.html.haml
@@ -10,6 +10,7 @@
- if vue_app_enabled
#js-broadcast-messages{ data: {
page: params[:page] || 1,
+ target_access_level_options: target_access_level_options.to_json,
messages_count: @broadcast_messages.total_count,
messages: @broadcast_messages.map { |message| {
id: message.id,
diff --git a/app/views/admin/dashboard/_security_newsletter_callout.html.haml b/app/views/admin/dashboard/_security_newsletter_callout.html.haml
index 76bfa347480..7495298936d 100644
--- a/app/views/admin/dashboard/_security_newsletter_callout.html.haml
+++ b/app/views/admin/dashboard/_security_newsletter_callout.html.haml
@@ -10,5 +10,5 @@
= c.body do
= s_('AdminArea|Sign up for the GitLab Security Newsletter to get notified for security updates.')
= c.actions do
- = link_to 'https://about.gitlab.com/company/preference-center/', target: '_blank', rel: 'noreferrer noopener', class: 'deferred-link gl-alert-action btn-confirm btn-md gl-button' do
+ = render Pajamas::ButtonComponent.new(variant: :confirm, href: 'https://about.gitlab.com/company/preference-center/', target: '_blank', button_options: { class: 'deferred-link gl-alert-action', rel: 'noreferrer noopener' }) do
= s_('AdminArea|Sign up for the GitLab newsletter')
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 886402139e9..27ae7d523b9 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -32,7 +32,8 @@
= sprite_icon('project', size: 16, css_class: 'gl-text-gray-700')
%h3.gl-m-0.gl-ml-3= approximate_count_with_delimiters(@counts, Project)
.gl-mt-3.text-uppercase= s_('AdminArea|Projects')
- = link_to(s_('AdminArea|New project'), new_project_path, class: "btn gl-button btn-default")
+ = render Pajamas::ButtonComponent.new(href: new_project_path) do
+ = s_('AdminArea|New project')
= c.footer do
.d-flex.align-items-center
= link_to(s_('AdminArea|View latest projects'), admin_projects_path(sort: 'created_desc'))
@@ -55,7 +56,8 @@
.gl-mt-3.text-uppercase
= s_('AdminArea|Users')
= link_to(s_('AdminArea|Users statistics'), admin_dashboard_stats_path, class: "text-capitalize gl-ml-2")
- = link_to(s_('AdminArea|New user'), new_admin_user_path, class: "btn gl-button btn-default")
+ = render Pajamas::ButtonComponent.new(href: new_admin_user_path) do
+ = s_('AdminArea|New user')
= c.footer do
.d-flex.align-items-center
= link_to(s_('AdminArea|View latest users'), admin_users_path({ sort: 'created_desc' }))
@@ -68,7 +70,8 @@
= sprite_icon('group', size: 16, css_class: 'gl-text-gray-700')
%h3.gl-m-0.gl-ml-3= approximate_count_with_delimiters(@counts, Group)
.gl-mt-3.text-uppercase= s_('AdminArea|Groups')
- = link_to(s_('AdminArea|New group'), new_admin_group_path, class: "btn gl-button btn-default")
+ = render Pajamas::ButtonComponent.new(href: new_admin_group_path) do
+ = s_('AdminArea|New group')
= c.footer do
.d-flex.align-items-center
= link_to(s_('AdminArea|View latest groups'), admin_groups_path(sort: 'created_desc'))
@@ -122,7 +125,7 @@
= s_('AdminArea|Components')
- if show_version_check?
.float-right
- .js-gitlab-version-check-badge{ data: { "size": "lg", "actionable": "true" } }
+ .js-gitlab-version-check-badge{ data: { "size": "lg", "actionable": "true", "version": gitlab_version_check.to_json } }
= link_to(sprite_icon('question'), "https://gitlab.com/gitlab-org/gitlab/-/blob/master/CHANGELOG.md", class: 'gl-ml-2', target: '_blank', rel: 'noopener noreferrer')
%p
= link_to _('GitLab'), general_admin_application_settings_path
diff --git a/app/views/admin/deploy_keys/edit.html.haml b/app/views/admin/deploy_keys/edit.html.haml
index acdf503727d..b51ab3457d6 100644
--- a/app/views/admin/deploy_keys/edit.html.haml
+++ b/app/views/admin/deploy_keys/edit.html.haml
@@ -7,4 +7,5 @@
= render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
.form-actions
= f.submit _('Save changes'), pajamas_button: true
- = link_to _('Cancel'), admin_deploy_keys_path, class: 'btn gl-button btn-default btn-cancel'
+ = render Pajamas::ButtonComponent.new(href: admin_deploy_keys_path) do
+ = _('Cancel')
diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml
index 7adba0d023b..20ee8c9f310 100644
--- a/app/views/admin/groups/_form.html.haml
+++ b/app/views/admin/groups/_form.html.haml
@@ -36,9 +36,11 @@
= render 'shared/group_tips'
.gl-mt-5
= f.submit _('Create group'), pajamas_button: true
- = link_to _('Cancel'), admin_groups_path, class: "gl-button btn btn-default btn-cancel"
+ = render Pajamas::ButtonComponent.new(href: admin_groups_path) do
+ = _('Cancel')
- else
.gl-mt-5
= f.submit _('Save changes'), data: { qa_selector: 'save_changes_button' }, pajamas_button: true
- = link_to _('Cancel'), admin_group_path(@group), class: "gl-button btn btn-cancel"
+ = render Pajamas::ButtonComponent.new(href: admin_group_path(@group)) do
+ = _('Cancel')
diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml
index a1afb1ddbfa..f9ebda2bc21 100644
--- a/app/views/admin/groups/_group.html.haml
+++ b/app/views/admin/groups/_group.html.haml
@@ -31,5 +31,7 @@
= visibility_level_icon(group.visibility_level)
.controls.gl-flex-shrink-0.gl-ml-5
- = link_to _('Edit'), admin_group_edit_path(group), id: "edit_#{dom_id(group)}", class: 'btn gl-button btn-default'
- = link_to _('Delete'), [:admin, group], aria: { label: _('Remove') }, data: { confirm: _("Are you sure you want to remove %{group_name}?") % { group_name: group.name }, confirm_btn_variant: 'danger' }, method: :delete, class: 'gl-button btn btn-danger'
+ = render Pajamas::ButtonComponent.new(href: admin_group_edit_path(group), button_options: { id: "edit_#{dom_id(group)}" }) do
+ = _('Edit')
+ = render Pajamas::ButtonComponent.new(href: [:admin, group], variant: :danger, button_options: { data: { confirm: _("Are you sure you want to remove %{group_name}?") % { group_name: group.name }, confirm_btn_variant: 'danger', method: :delete } }) do
+ = _('Delete')
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index 2ea5890be2c..2a49b9c5ad8 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -1,4 +1,5 @@
- page_title _("Groups")
+- add_page_specific_style 'page_bundles/search'
.top-area
.gl-mt-3.gl-mb-3
@@ -9,7 +10,7 @@
= search_field_tag :name, params[:name].presence, class: "form-control search-text-input js-search-input", autofocus: true, spellcheck: false, placeholder: 'Search by name', data: { qa_selector: 'group_search_field' }
= sprite_icon('search', css_class: 'search-icon')
= render "shared/groups/dropdown", options_hash: admin_groups_sort_options_hash
- = link_to new_admin_group_path, class: "gl-button btn btn-confirm" do
+ = render Pajamas::ButtonComponent.new(variant: :confirm, href: new_admin_group_path) do
= _('New group')
%ul.content-list
= render @groups
diff --git a/app/views/admin/hook_logs/show.html.haml b/app/views/admin/hook_logs/show.html.haml
index 6fcaf2ea152..0ccde159905 100644
--- a/app/views/admin/hook_logs/show.html.haml
+++ b/app/views/admin/hook_logs/show.html.haml
@@ -5,8 +5,11 @@
%hr
- if @hook_log.oversize?
- = button_tag _("Resend Request"), class: "btn gl-button btn-default float-right gl-ml-3 has-tooltip", disabled: true, title: _("Request data is too large")
+ - tooltip = _("Request data is too large")
+ = render Pajamas::ButtonComponent.new(disabled: true, button_options: { class: 'gl-float-right gl-ml-3 has-tooltip', title: tooltip }) do
+ = _("Resend Request")
- else
- = link_to _("Resend Request"), retry_admin_hook_hook_log_path(@hook, @hook_log), method: :post, class: "btn gl-button btn-default float-right gl-ml-3"
+ = render Pajamas::ButtonComponent.new(href: retry_admin_hook_hook_log_path(@hook, @hook_log), method: :post, button_options: { class: 'gl-float-right gl-ml-3' }) do
+ = _("Resend Request")
= render partial: 'shared/hook_logs/content', locals: { hook_log: @hook_log }
diff --git a/app/views/admin/identities/_form.html.haml b/app/views/admin/identities/_form.html.haml
index ba7687db9c7..ad78c677da1 100644
--- a/app/views/admin/identities/_form.html.haml
+++ b/app/views/admin/identities/_form.html.haml
@@ -1,4 +1,4 @@
-= form_for [:admin, @user, @identity], html: { class: 'fieldset-form' } do |f|
+= gitlab_ui_form_for [:admin, @user, @identity], html: { class: 'fieldset-form' } do |f|
= form_errors(@identity)
.form-group.row
@@ -14,5 +14,5 @@
= f.text_field :extern_uid, class: 'form-control', required: true
.form-actions
- = f.submit _('Save changes'), class: "gl-button btn btn-confirm"
+ = f.submit _('Save changes'), pajamas_button: true
diff --git a/app/views/admin/identities/_identity.html.haml b/app/views/admin/identities/_identity.html.haml
index 3121cd2ae59..a24cd000464 100644
--- a/app/views/admin/identities/_identity.html.haml
+++ b/app/views/admin/identities/_identity.html.haml
@@ -14,11 +14,11 @@
icon: 'pencil',
button_options: { title: _('Edit'),
'aria-label' => _('Edit'),
- class: button_classes } )
+ class: button_classes })
= render Pajamas::ButtonComponent.new(category: :tertiary,
href: url_for([:admin, @user, identity]),
icon: 'remove',
button_options: { title: _('Delete'),
'aria-label' => _('Delete identity'),
class: button_classes,
- data: { method: :delete, confirm: _("Are you sure you want to remove this identity?") } } )
+ data: { method: :delete, confirm: _("Are you sure you want to remove this identity?") } })
diff --git a/app/views/admin/labels/index.html.haml b/app/views/admin/labels/index.html.haml
index 21b19236683..d6f2898a383 100644
--- a/app/views/admin/labels/index.html.haml
+++ b/app/views/admin/labels/index.html.haml
@@ -27,6 +27,5 @@
%p
= s_('AdminLabels|They can be used to categorize issues and merge requests.')
.gl-display-flex.gl-flex-wrap.gl-justify-content-center
- = link_to new_admin_label_path, class: "btn gl-mb-3 btn-confirm btn-md gl-button gl-mx-2" do
- %span.gl-button-text
- = _('New label')
+ = render Pajamas::ButtonComponent.new(href: new_admin_label_path) do
+ = _('New label')
diff --git a/app/views/admin/projects/_projects.html.haml b/app/views/admin/projects/_projects.html.haml
index c7c30673d74..cf1bd2a8022 100644
--- a/app/views/admin/projects/_projects.html.haml
+++ b/app/views/admin/projects/_projects.html.haml
@@ -25,8 +25,8 @@
.controls.gl-flex-shrink-0.gl-ml-5
= render Pajamas::ButtonComponent.new(href: edit_project_path(project), button_options: { id: dom_id(project, :edit) }) do
- = s_('Edit')
- = render Pajamas::ButtonComponent.new(variant: :danger, button_options: { class: 'delete-project-button', data: { delete_project_url: admin_project_path(project), project_name: project.name } } ) do
+ = _('Edit')
+ = render Pajamas::ButtonComponent.new(variant: :danger, button_options: { class: 'delete-project-button', data: { delete_project_url: admin_project_path(project), project_name: project.name } }) do
= s_('AdminProjects|Delete')
= paginate @projects, theme: 'gitlab'
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index f23a688dd48..18cd3400c60 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -1,4 +1,5 @@
- page_title _('Projects')
+- add_page_specific_style 'page_bundles/search'
- params[:visibility_level] ||= []
.top-area
@@ -20,10 +21,9 @@
- namespace = Namespace.find(params[:namespace_id])
- current_namespace = "#{namespace.kind}: #{namespace.full_path}"
%button.dropdown-menu-toggle.btn.btn-default.btn-md.gl-button.js-namespace-select{ data: { show_any: 'true', field_name: 'namespace_id', placeholder: current_namespace, update_location: 'true' }, type: 'button' }
- %span.gl-new-dropdown-button-text
+ %span.gl-dropdown-button-text
= current_namespace
- = render 'shared/projects/dropdown'
= link_to new_project_path, class: 'gl-button btn btn-confirm' do
= _('New Project')
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index a60c3996cf2..829e9f508e0 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -6,8 +6,7 @@
%h1.page-title.gl-font-size-h-display
= _('Project: %{name}') % { name: @project.full_name }
- = link_to edit_project_path(@project), class: "btn btn-default gl-button float-right" do
- = sprite_icon('pencil', css_class: 'gl-icon gl-mr-2')
+ = render Pajamas::ButtonComponent.new(href: edit_project_path(@project), icon: 'pencil', button_options: { class: 'gl-float-right' }) do
= _('Edit')
%hr
- if @project.last_repository_check_failed?
@@ -143,7 +142,7 @@
.col-sm-9
- placeholder = _('Search for Namespace')
%button.dropdown-menu-toggle.btn.btn-default.btn-md.gl-button.js-namespace-select{ data: { field_name: 'new_namespace_id', placeholder: placeholder }, type: 'button' }
- %span.gl-new-dropdown-button-text
+ %span.gl-dropdown-button-text
= placeholder
.form-group.row
diff --git a/app/views/admin/topics/_form.html.haml b/app/views/admin/topics/_form.html.haml
index 9b9d97950cc..544310e312c 100644
--- a/app/views/admin/topics/_form.html.haml
+++ b/app/views/admin/topics/_form.html.haml
@@ -38,10 +38,13 @@
- if @topic.new_record?
.form-actions
- = f.submit _('Create topic'), class: "gl-button btn btn-confirm"
- = link_to _('Cancel'), admin_topics_path, class: "gl-button btn btn-default btn-cancel"
+ = f.submit _('Create topic'), pajamas_button: true
+ = render Pajamas::ButtonComponent.new(href: admin_topics_path) do
+ = _('Cancel')
- else
.form-actions
- = f.submit _('Save changes'), class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
- = link_to _('Cancel'), admin_topics_path, class: "gl-button btn btn-cancel"
+ = f.submit _('Save changes'), pajamas_button: true, data: { qa_selector: 'save_changes_button' }
+ = render Pajamas::ButtonComponent.new(href: admin_topics_path) do
+ = _('Cancel')
+
diff --git a/app/views/admin/topics/index.html.haml b/app/views/admin/topics/index.html.haml
index 77823ed7058..2f39f27208e 100644
--- a/app/views/admin/topics/index.html.haml
+++ b/app/views/admin/topics/index.html.haml
@@ -9,7 +9,7 @@
= sprite_icon('search', css_class: 'search-icon')
.gl-flex-grow-1
.js-merge-topics{ data: { path: merge_admin_topics_path } }
- = link_to new_admin_topic_path, class: "gl-button btn btn-confirm gl-w-full gl-md-w-auto" do
+ = render Pajamas::ButtonComponent.new(href: new_admin_topic_path, variant: 'confirm') do
= _('New topic')
%ul.content-list
= render partial: 'topic', collection: @topics
diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml
index 6809f147ef8..eb151b40a65 100644
--- a/app/views/admin/users/_form.html.haml
+++ b/app/views/admin/users/_form.html.haml
@@ -76,7 +76,9 @@
%div
- if @user.new_record?
= f.submit _('Create user'), pajamas_button: true
- = link_to _('Cancel'), admin_users_path, class: "gl-button btn btn-default btn-cancel"
+ = render Pajamas::ButtonComponent.new(href: admin_users_path) do
+ = _('Cancel')
- else
= f.submit _('Save changes'), pajamas_button: true
- = link_to _('Cancel'), admin_user_path(@user), class: "gl-button btn btn-default btn-cancel"
+ = render Pajamas::ButtonComponent.new(href: admin_user_path(@user)) do
+ = _('Cancel')
diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml
index 1fa7c9c8651..6b5ec62bc77 100644
--- a/app/views/admin/users/_head.html.haml
+++ b/app/views/admin/users/_head.html.haml
@@ -40,7 +40,8 @@
= render Pajamas::ButtonComponent.new(variant: :default, button_options: { class: 'js-confirm-modal-button', data: confirm_user_data(@user) }) do
= _('Confirm user')
.gl-p-2
- = link_to _('New identity'), new_admin_user_identity_path(@user), class: "btn btn-primary gl-button"
+ = render Pajamas::ButtonComponent.new(variant: :confirm, href: new_admin_user_identity_path(@user)) do
+ = _('New identity')
= gl_tabs_nav do
= gl_tab_link_to _("Account"), admin_user_path(@user)
= gl_tab_link_to _("Groups and projects"), projects_admin_user_path(@user)
diff --git a/app/views/admin/users/_users.html.haml b/app/views/admin/users/_users.html.haml
index 6d85ff50fbe..96e6a264d8e 100644
--- a/app/views/admin/users/_users.html.haml
+++ b/app/views/admin/users/_users.html.haml
@@ -1,3 +1,5 @@
+- add_page_specific_style 'page_bundles/search'
+
- if registration_features_can_be_prompted?
= render Pajamas::AlertComponent.new(variant: :tip,
alert_options: { class: 'gl-my-5' },
diff --git a/app/views/ci/runner/_how_to_setup_runner.html.haml b/app/views/ci/runner/_how_to_setup_runner.html.haml
index 8f4cc41822b..cdf25a9348c 100644
--- a/app/views/ci/runner/_how_to_setup_runner.html.haml
+++ b/app/views/ci/runner/_how_to_setup_runner.html.haml
@@ -17,8 +17,7 @@
= clipboard_button(target: '#registration_token', title: _("Copy token"))
.gl-mt-3.gl-mb-3
-= button_to _("Reset registration token"), reset_token_url,
-method: :put, class: 'gl-button btn btn-default',
-data: { confirm: _("Are you sure you want to reset the registration token?") }
+= render Pajamas::ButtonComponent.new(variant: :default, method: :put, href: reset_token_url, button_options: { id: 'Reset registration token', data: { confirm: _("Are you sure you want to reset the registration token?") } }) do
+ = _('Reset registration token')
#js-install-runner
diff --git a/app/views/ci/variables/_content.html.haml b/app/views/ci/variables/_content.html.haml
index b597c2d442a..37043a207ff 100644
--- a/app/views/ci/variables/_content.html.haml
+++ b/app/views/ci/variables/_content.html.haml
@@ -1,10 +1,12 @@
-= _('Variables store information, like passwords and secret keys, that you can use in job scripts.')
-= link_to s_('Learn more.'), help_page_path('ci/variables/index'), target: '_blank', rel: 'noopener noreferrer'
+= format(s_('CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables.'), entity: entity, limit: variable_limit).html_safe
+= link_to _('Learn more.'), help_page_path('ci/variables/index'), target: '_blank', rel: 'noopener noreferrer'
%p
- = _('Variables can be:')
+ = _('Variables can have several attributes.')
+ = link_to _('Learn more.'), help_page_path('ci/variables/index', anchor: 'add-a-cicd-variable-to-an-instance'), target: '_blank', rel: 'noopener noreferrer'
%ul
%li
= html_escape(_('%{code_open}Protected:%{code_close} Only exposed to protected branches or protected tags.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
%li
= html_escape(_('%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
- = link_to _('Learn more.'), help_page_path('ci/variables/index', anchor: 'mask-a-cicd-variable'), target: '_blank', rel: 'noopener noreferrer'
+ %li
+ = html_escape(_('%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
diff --git a/app/views/ci/variables/_header.html.haml b/app/views/ci/variables/_header.html.haml
index d6a9ce72d03..dfcf8f39533 100644
--- a/app/views/ci/variables/_header.html.haml
+++ b/app/views/ci/variables/_header.html.haml
@@ -7,4 +7,4 @@
= expanded ? _('Collapse') : _('Expand')
%p
- = render "ci/variables/content"
+ = render "ci/variables/content", entity: @entity, variable_limit: @variable_limit
diff --git a/app/views/ci/variables/_index.html.haml b/app/views/ci/variables/_index.html.haml
index 08865abbe86..fdbf5132d40 100644
--- a/app/views/ci/variables/_index.html.haml
+++ b/app/views/ci/variables/_index.html.haml
@@ -23,7 +23,7 @@
aws_tip_deploy_link: help_page_path('ci/cloud_deployment/index.md', anchor: 'deploy-your-application-to-ecs'),
aws_tip_commands_link: help_page_path('ci/cloud_deployment/index.md', anchor: 'use-an-image-to-run-aws-commands'),
aws_tip_learn_link: help_page_path('ci/cloud_deployment/index.md'),
- contains_variable_reference_link: help_page_path('ci/variables/index', anchor: 'use-variables-in-other-variables'),
+ contains_variable_reference_link: help_page_path('ci/variables/index', anchor: 'expand-cicd-variables'),
protected_environment_variables_link: help_page_path('ci/variables/index', anchor: 'protected-cicd-variables'),
masked_environment_variables_link: help_page_path('ci/variables/index', anchor: 'mask-a-cicd-variable'),
environment_scope_link: help_page_path('ci/environments/index', anchor: 'scope-environments-with-specs') } }
diff --git a/app/views/ci/variables/_variable_row.html.haml b/app/views/ci/variables/_variable_row.html.haml
index c5e518d8526..a710655aa20 100644
--- a/app/views/ci/variables/_variable_row.html.haml
+++ b/app/views/ci/variables/_variable_row.html.haml
@@ -33,5 +33,4 @@
%p.masking-validation-error.gl-field-error.hide
= s_("CiVariables|Cannot use Masked Variable with current value")
= link_to sprite_icon('question-o'), help_page_path('ci/variables/index', anchor: 'mask-a-cicd-variable'), target: '_blank', rel: 'noopener noreferrer'
- %button.gl-button.btn.btn-default.btn-icon.js-row-remove-button.ci-variable-row-remove-button.table-section{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') }
- = sprite_icon('close')
+ = render Pajamas::ButtonComponent.new(icon: 'close', button_options: { class: 'js-row-remove-button ci-variable-row-remove-button table-section', 'aria-label': s_('CiVariables|Remove variable row') })
diff --git a/app/views/clusters/clusters/_details.html.haml b/app/views/clusters/clusters/_details.html.haml
index 3079e024369..34408a9adeb 100644
--- a/app/views/clusters/clusters/_details.html.haml
+++ b/app/views/clusters/clusters/_details.html.haml
@@ -4,8 +4,6 @@
%section.settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4= s_('ClusterIntegration|Provider details')
- %button.btn.gl-button.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
%p= s_('ClusterIntegration|See and edit the details for your Kubernetes cluster')
.settings-content
= render 'provider_details_form', cluster: @cluster, platform: @cluster.platform_kubernetes, update_cluster_url_path: clusterable.cluster_path(@cluster)
diff --git a/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml b/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml
index c3b881df98d..af4c934fd72 100644
--- a/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml
+++ b/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml
@@ -6,5 +6,5 @@
= c.body do
= s_('ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab\'s Google Kubernetes Engine Integration.').html_safe % { sign_up_link: link }
= c.actions do
- %a.gl-button.btn-confirm.text-decoration-none{ href: 'https://cloud.google.com/partners/partnercredit/?pcn_code=0014M00001h35gDQAQ#contact-form', target: '_blank', rel: 'noopener noreferrer' }
+ = render Pajamas::ButtonComponent.new(variant: :confirm, href: 'https://cloud.google.com/partners/partnercredit/?pcn_code=0014M00001h35gDQAQ#contact-form', target: '_blank', button_options: { rel: 'noopener noreferrer' }) do
= s_("ClusterIntegration|Apply for credit")
diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml
index 4edb0f324dc..8750b80ccfd 100644
--- a/app/views/dashboard/_activities.html.haml
+++ b/app/views/dashboard/_activities.html.haml
@@ -1,8 +1,7 @@
.nav-block.activities
= render 'shared/event_filter'
.controls
- = link_to dashboard_projects_path(rss_url_options), class: 'btn gl-button btn-default btn-icon d-none d-sm-inline-flex has-tooltip', title: 'Subscribe' do
- = sprite_icon('rss', css_class: 'gl-icon')
+ = render Pajamas::ButtonComponent.new(href: dashboard_projects_path(rss_url_options), icon: 'rss', button_options: { title: _('Subscribe'), aria: { label: _('Subscribe') }, class: 'gl-display-none gl-sm-display-inline-flex' })
.content_list
.loading
diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml
index 1c82b30ed8d..09e2e35c617 100644
--- a/app/views/dashboard/_groups_head.html.haml
+++ b/app/views/dashboard/_groups_head.html.haml
@@ -3,8 +3,8 @@
- if current_user.can_create_group?
.page-title-controls
- = link_to _("New group"), new_group_path, class: "gl-button btn btn-confirm", data: { qa_selector: "new_group_button", testid: "new-group-button" }
-
+ = render Pajamas::ButtonComponent.new(href: new_group_path, variant: :confirm, button_options: { data: { qa_selector: "new_group_button", testid: "new-group-button" } }) do
+ = _("New group")
.top-area
= gl_tabs_nav({ class: 'gl-flex-grow-1 gl-border-0' }) do
= gl_tab_link_to _("Your groups"), dashboard_groups_path
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index 9c492a0da34..10e653fd427 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -1,6 +1,3 @@
-- project_tab_filter = local_assigns.fetch(:project_tab_filter, "")
-- feature_project_list_filter_bar = Feature.enabled?(:project_list_filter_bar)
-
= content_for :flash_message do
= render 'shared/project_limit'
@@ -9,17 +6,13 @@
- if current_user.can_create_project?
.page-title-controls
- = link_to _("New project"), new_project_path, class: "gl-button btn btn-confirm", data: { qa_selector: 'new_project_button' }
+ = render Pajamas::ButtonComponent.new(href: new_project_path, variant: :confirm, button_options: { data: { qa_selector: 'new_project_button' } }) do
+ = _("New project")
.top-area
- .scrolling-tabs-container.inner-page-scroll-tabs.gl-flex-grow-1.gl-min-w-0.gl-w-full
+ .scrolling-tabs-container.inner-page-scroll-tabs.gl-flex-grow-1.gl-min-w-0
.fade-left= sprite_icon('chevron-lg-left', size: 12)
.fade-right= sprite_icon('chevron-lg-right', size: 12)
= render 'dashboard/projects_nav'
- - unless feature_project_list_filter_bar
- .nav-controls
- = render 'shared/projects/search_form'
- = render 'shared/projects/dropdown'
-- if feature_project_list_filter_bar
- .project-filters
- = render 'shared/projects/search_bar', project_tab_filter: project_tab_filter
+ .nav-controls
+ = render 'shared/projects/search_form'
diff --git a/app/views/dashboard/_projects_nav.html.haml b/app/views/dashboard/_projects_nav.html.haml
index 29c820ddc58..7cbd2fb14ec 100644
--- a/app/views/dashboard/_projects_nav.html.haml
+++ b/app/views/dashboard/_projects_nav.html.haml
@@ -3,11 +3,11 @@
= gl_tabs_nav({ class: 'scrolling-tabs nav-links gl-display-flex gl-flex-grow-1 gl-w-full nav gl-tabs-nav' }) do
= gl_tab_link_to dashboard_projects_path, { item_active: is_your_projects_path, class: 'shortcuts-activity', data: { placement: 'right' } } do
- = _("Your projects")
+ = s_("ProjectList|Yours")
= gl_tab_counter_badge(limited_counter_with_delimiter(@total_user_projects_count))
= gl_tab_link_to starred_dashboard_projects_path, { data: { placement: 'right' } } do
- = _("Starred projects")
+ = s_("ProjectList|Starred")
= gl_tab_counter_badge(limited_counter_with_delimiter(@total_starred_projects_count))
- = gl_tab_link_to _("Explore projects"), explore_root_path, { item_active: is_explore_projects_path, data: { placement: 'right' } }
- = gl_tab_link_to _("Explore topics"), topics_explore_projects_path, { data: { placement: 'right' } }
+ = gl_tab_link_to s_("ProjectList|Explore"), explore_root_path, { item_active: is_explore_projects_path, data: { placement: 'right' } }
+ = gl_tab_link_to s_("ProjectList|Topics"), topics_explore_projects_path, { data: { placement: 'right' } }
= render_if_exists "dashboard/removed_projects_tab"
diff --git a/app/views/dashboard/_snippets_head.html.haml b/app/views/dashboard/_snippets_head.html.haml
index be2124fdd7e..5a798c249d1 100644
--- a/app/views/dashboard/_snippets_head.html.haml
+++ b/app/views/dashboard/_snippets_head.html.haml
@@ -4,7 +4,8 @@
- if current_user && current_user.snippets.any? || @snippets.any?
.page-title-controls
- if can?(current_user, :create_snippet)
- = link_to _("New snippet"), new_snippet_path, class: "gl-button btn btn-confirm", title: _("New snippet")
+ = render Pajamas::ButtonComponent.new(href: new_snippet_path, variant: :confirm, button_options: { title: _("New snippet") }) do
+ = _("New snippet")
.top-area
= gl_tabs_nav({ class: 'gl-border-0' }) do
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index 79f6bfc866a..5293f685d06 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -1,6 +1,7 @@
- @hide_top_links = true
- page_title _("Issues")
- @breadcrumb_link = issues_dashboard_path(assignee_username: current_user.username)
+- add_page_specific_style 'page_bundles/issuable_list'
- add_page_specific_style 'page_bundles/dashboard'
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{current_user.name} issues")
@@ -15,10 +16,7 @@
= render 'shared/new_project_item_select', path: 'issues/new', label: _("issue"), with_feature_enabled: 'issues', type: :issues
- if ::Feature.enabled?(:vue_issues_dashboard)
- .js-issues-dashboard{ data: { calendar_path: url_for(safe_params.merge(calendar_url_options)),
- empty_state_svg_path: image_path('illustrations/issue-dashboard_results-without-filter.svg'),
- is_signed_in: current_user.present?.to_s,
- rss_path: url_for(safe_params.merge(rss_url_options)) } }
+ .js-issues-dashboard{ data: dashboard_issues_list_data(current_user) }
- else
.top-area
= render 'shared/issuable/nav', type: :issues, display_count: !@no_filters_set
diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml
index 8a639d08a27..c921375edd1 100644
--- a/app/views/dashboard/merge_requests.html.haml
+++ b/app/views/dashboard/merge_requests.html.haml
@@ -1,6 +1,7 @@
- @hide_top_links = true
- page_title _("Merge requests")
- @breadcrumb_link = merge_requests_dashboard_path(assignee_username: current_user.username)
+- add_page_specific_style 'page_bundles/issuable_list'
= render_dashboard_ultimate_trial(current_user)
diff --git a/app/views/dashboard/projects/_nav.html.haml b/app/views/dashboard/projects/_nav.html.haml
index 3e39872902d..45e3267813f 100644
--- a/app/views/dashboard/projects/_nav.html.haml
+++ b/app/views/dashboard/projects/_nav.html.haml
@@ -1,19 +1,4 @@
-- inactive_class = 'btn p-2'
-- active_class = 'btn p-2 active'
-- project_tab_filter = local_assigns.fetch(:project_tab_filter, "")
-- is_explore_trending = project_tab_filter == :explore_trending
-- feature_project_list_filter_bar = Feature.enabled?(:project_list_filter_bar)
-
-.nav-block{ class: ("w-100" if feature_project_list_filter_bar) }
- - if feature_project_list_filter_bar
- .btn-group.button-filter-group.d-flex.m-0.p-0
- - if project_tab_filter == :explore || is_explore_trending
- = link_to s_('DashboardProjects|Trending'), trending_explore_projects_path, class: is_explore_trending ? active_class : inactive_class
- = link_to s_('DashboardProjects|All'), explore_projects_path, class: is_explore_trending ? inactive_class : active_class
- - else
- = link_to s_('DashboardProjects|All'), dashboard_projects_path, class: params[:personal].present? ? inactive_class : active_class
- = link_to s_('DashboardProjects|Personal'), filter_projects_path(personal: true), class: params[:personal].present? ? active_class : inactive_class
- - else
- = gl_tabs_nav do
- = gl_tab_link_to s_('DashboardProjects|All'), dashboard_projects_path, { item_active: params[:personal].blank? }
- = gl_tab_link_to s_('DashboardProjects|Personal'), filter_projects_path(personal: true), { item_active: params[:personal].present? }
+.nav-block
+ = gl_tabs_nav do
+ = gl_tab_link_to s_('DashboardProjects|All'), dashboard_projects_path, { item_active: params[:personal].blank? }
+ = gl_tab_link_to s_('DashboardProjects|Personal'), filter_projects_path(personal: true), { item_active: params[:personal].present? }
diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml
index 0d9257e659a..f427c347dd3 100644
--- a/app/views/dashboard/projects/index.html.haml
+++ b/app/views/dashboard/projects/index.html.haml
@@ -12,7 +12,7 @@
= render "projects/last_push"
- if show_projects?(@projects, params)
= render 'dashboard/projects_head'
- = render 'nav' unless Feature.enabled?(:project_list_filter_bar)
+ = render 'nav'
= render 'projects'
- else
= render "zero_authorized_projects"
diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml
index 47bc8f5c95b..9dfeaa3d07d 100644
--- a/app/views/dashboard/todos/_todo.html.haml
+++ b/app/views/dashboard/todos/_todo.html.haml
@@ -1,60 +1,66 @@
-%li.todo.gl-hover-border-blue-200.gl-hover-bg-blue-50.gl-hover-cursor-pointer{ class: "todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo), data: { url: todo_target_path(todo) } }
- .gl-display-flex.gl-flex-direction-row
- .todo-avatar.gl-display-none.gl-sm-display-inline-block
- = author_avatar(todo, size: 40)
-
- .todo-item.flex-fill.gl-overflow-hidden.gl-overflow-x-auto.gl-align-self-center{ data: { qa_selector: "todo_item_container" } }
- .todo-title.gl-mb-3.gl-md-mb-0
- - if todo_author_display?(todo)
- = todo_target_state_pill(todo)
-
- %span.title-item.author-name.bold
- - if todo.author
- = link_to_author(todo, self_added: todo.self_added?)
- - else
- = _('(removed)')
-
- %span.title-item.action-name{ data: { qa_selector: "todo_action_name_content" } }
- = todo_action_name(todo)
-
- %span.title-item.todo-label.todo-target-link
+%li.todo.gl-hover-border-blue-200.gl-hover-bg-blue-50.gl-hover-cursor-pointer.gl-relative{ class: "todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo) }
+ .gl-display-flex.gl-flex-direction-column.gl-sm-flex-direction-row.gl-sm-align-items-center
+ .todo-item.gl-overflow-hidden.gl-overflow-x-auto.gl-align-self-center.gl-w-full{ data: { qa_selector: "todo_item_container" } }
+ .todo-title.gl-pt-2.gl-pb-3.gl-px-2.gl-md-mb-1.gl-font-sm.gl-text-gray-500
+
+ = todo_target_state_pill(todo)
+
+ %span.todo-target-title{ data: { qa_selector: "todo_target_title_content" }, :id => dom_id(todo) + "_describer" }
+ = todo_target_title(todo)
+
+ - if !todo.for_design? && !todo.member_access_requested?
+ &middot;
+
+ %span
+ = todo_parent_path(todo)
+
+ %span.todo-label
- if todo.target
- = todo_target_link(todo)
+ = link_to todo_target_name(todo), todo_target_path(todo), class: 'todo-target-link gl-text-gray-500! gl-text-decoration-none!', :'aria-describedby' => dom_id(todo) + "_describer", :'aria-label' => todo_target_aria_label(todo)
- else
= _("(removed)")
- %span.title-item.todo-target-title{ data: { qa_selector: "todo_target_title_content" } }
- = todo_target_title(todo)
-
- %span.title-item.todo-project.todo-label
- = s_('Todo|at %{todo_parent_path}').html_safe % { todo_parent_path: todo_parent_path(todo) }
+ .todo-body.gl-mb-2.gl-px-2.gl-display-flex.gl-align-items-flex-start.gl-lg-align-items-center
+ .todo-avatar.gl-display-none.gl-sm-display-inline-block
+ = author_avatar(todo, size: 24)
+ .todo-note
+ - if todo_author_display?(todo)
+ .author-name.bold.gl-display-inline
+ - if todo.author
+ = link_to_author(todo, self_added: todo.self_added?)
+ - else
+ = _('(removed)')
- - if todo.self_assigned?
- %span.title-item.action-name
- = todo_self_addressing(todo)
+ %span.action-name{ data: { qa_selector: "todo_action_name_content" } }<
+ = todo_action_name(todo)
+ - if todo.note.present?
+ \:
+ - unless todo.note.present? || todo.self_assigned?
+ \.
- %span.title-item
- &middot;
+ - if todo.self_assigned?
+ %span.action-name<
+ = todo_self_addressing(todo)
+ \.
+ - if todo.note.present?
+ %span.action-description.gl-font-style-italic<
+ = first_line_in_markdown(todo, :body, 100, is_todo: true, project: todo.project, group: todo.group)
- %span.title-item.todo-timestamp
- #{time_ago_with_tooltip(todo.created_at)}
- = todo_due_date(todo)
+ .todo-timestamp.gl-white-space-nowrap.gl-sm-ml-3.gl-mt-2.gl-mb-2.gl-sm-my-0.gl-px-2.gl-sm-px-0
+ %span.todo-timestamp.gl-font-sm.gl-text-gray-500
+ = todo_due_date(todo)
+ #{time_ago_with_tooltip(todo.created_at)}
- - if todo.note.present?
- .todo-body
- .todo-note.break-word
- .md
- = first_line_in_markdown(todo, :body, 150, project: todo.project, group: todo.group)
- .todo-actions.gl-ml-3
+ .todo-actions.gl-mr-4.gl-px-2.gl-sm-px-0.gl-sm-mx-0
- if todo.pending?
- = link_to dashboard_todo_path(todo), method: :delete, class: 'gl-button gl-bg-gray-50 btn btn-default btn-loading d-flex align-items-center js-done-todo', data: { href: dashboard_todo_path(todo) } do
+ = render Pajamas::ButtonComponent.new(button_options: { class: 'btn-loading btn-icon gl-display-flex js-done-todo has-tooltip', title: _('Mark as done')}, method: :delete, href: dashboard_todo_path(todo)), 'aria-label' => _('Mark as done') do
= gl_loading_icon(inline: true)
- = _('Done')
- = link_to restore_dashboard_todo_path(todo), method: :patch, class: 'gl-button gl-bg-gray-50 btn btn-default btn-loading d-flex align-items-center js-undo-todo hidden', data: { href: restore_dashboard_todo_path(todo) } do
+ = sprite_icon('check', css_class: 'js-todo-button-icon')
+ = render Pajamas::ButtonComponent.new(button_options: { class: 'btn-loading btn-icon gl-display-flex js-undo-todo hidden has-tooltip', title: _('Undo')}, method: :patch, href: restore_dashboard_todo_path(todo)), 'aria-label' => _('Undo') do
= gl_loading_icon(inline: true)
- = _('Undo')
+ = sprite_icon('redo', css_class: 'js-todo-button-icon')
- else
- = link_to restore_dashboard_todo_path(todo), method: :patch, class: 'gl-button gl-bg-gray-50 btn btn-default btn-loading d-flex align-items-center js-add-todo', data: { href: restore_dashboard_todo_path(todo) } do
+ = render Pajamas::ButtonComponent.new(button_options: { class: 'btn-loading btn-icon gl-display-flex js-add-todo has-tooltip', title: _('Add a to do')}, method: :patch, href: restore_dashboard_todo_path(todo)), 'aria-label' => _('Add a to do') do
= gl_loading_icon(inline: true)
- = _('Add a to do')
+ = sprite_icon('todo-add', css_class: 'js-todo-button-icon')
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index deb1ac9e360..c0bd3ee3f0d 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -5,6 +5,7 @@
= render_two_factor_auth_recovery_settings_check
= render_dashboard_ultimate_trial(current_user)
- add_page_specific_style 'page_bundles/todos'
+- add_page_specific_style 'page_bundles/issuable'
.page-title-holder.d-flex.align-items-center
%h1.page-title.gl-font-size-h-display= _("To-Do List")
diff --git a/app/views/devise/shared/_footer.html.haml b/app/views/devise/shared/_footer.html.haml
index 10cfc07a719..0744faa148c 100644
--- a/app/views/devise/shared/_footer.html.haml
+++ b/app/views/devise/shared/_footer.html.haml
@@ -1,9 +1,10 @@
%hr.footer-fixed
-.container.footer-container
+.container.footer-container.gl-display-flex.gl-justify-content-space-between
.footer-links
- unless public_visibility_restricted?
= link_to _("Explore"), explore_root_path
= link_to _("Help"), help_path
= link_to _("About GitLab"), "https://#{ApplicationHelper.promo_host}"
= link_to _("Community forum"), ApplicationHelper.community_forum, target: '_blank', class: 'text-nowrap', rel: 'noopener noreferrer'
+ = render 'devise/shared/language_switcher'
= footer_message
diff --git a/app/views/devise/shared/_language_switcher.html.haml b/app/views/devise/shared/_language_switcher.html.haml
new file mode 100644
index 00000000000..4c47e3efd0f
--- /dev/null
+++ b/app/views/devise/shared/_language_switcher.html.haml
@@ -0,0 +1,3 @@
+- return unless ::Feature.enabled?(:preferred_language_switcher)
+
+.js-language-switcher{ data: { locales: ordered_selectable_locales.to_json } }
diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
index b9c9c99bf1a..a3a5fe690a7 100644
--- a/app/views/devise/shared/_signup_box.html.haml
+++ b/app/views/devise/shared/_signup_box.html.haml
@@ -44,7 +44,6 @@
.form-group
= f.label :email, class: "label-bold #{'gl-mb-1' if Feature.enabled?(:restyle_login_page, @project)}"
= f.email_field :email,
- value: @invite_email,
class: 'form-control gl-form-input middle js-validate-email',
data: { qa_selector: 'new_user_email_field' },
required: true,
diff --git a/app/views/devise/unlocks/new.html.haml b/app/views/devise/unlocks/new.html.haml
index abaf169afd5..b9d50e48d05 100644
--- a/app/views/devise/unlocks/new.html.haml
+++ b/app/views/devise/unlocks/new.html.haml
@@ -1,14 +1,14 @@
= render 'devise/shared/tab_single', tab_title: _('Resend unlock instructions')
.login-box
.login-body
- = form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post, class: 'gl-show-field-errors' }) do |f|
+ = gitlab_ui_form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post, class: 'gl-show-field-errors' }) do |f|
.devise-errors
= render "devise/shared/error_messages", resource: resource
.form-group.gl-mb-6
= f.label :email
= f.email_field :email, class: 'form-control', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off', title: _('Please provide a valid email address.')
.clearfix
- = f.submit _('Resend unlock instructions'), class: 'gl-button btn btn-confirm'
+ = f.submit _('Resend unlock instructions'), pajamas_button: true, class: 'gl-w-full'
.clearfix.prepend-top-20
= render 'devise/shared/sign_in_link'
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index f02d30081b6..67a88f3d623 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -1,9 +1,7 @@
- has_label = local_assigns.fetch(:has_label, false)
-- feature_project_list_filter_bar = Feature.enabled?(:project_list_filter_bar)
-- klass = feature_project_list_filter_bar ? 'gl-ml-3 gl-display-flex gl-flex-grow-1 gl-flex-shrink-1' : 'gl-ml-3'
- selected = projects_filter_selected(params[:visibility_level])
- if current_user
- unless has_label
%span.gl-float-left= _("Visibility:")
- = gl_redirect_listbox_tag(projects_filter_items, selected, class: klass, data: { right: true })
+ = gl_redirect_listbox_tag(projects_filter_items, selected, class: 'gl-ml-3', data: { right: true })
diff --git a/app/views/explore/projects/_nav.html.haml b/app/views/explore/projects/_nav.html.haml
index 9d7a6f1ccfb..9119026320a 100644
--- a/app/views/explore/projects/_nav.html.haml
+++ b/app/views/explore/projects/_nav.html.haml
@@ -7,5 +7,4 @@
.nav-controls
- unless current_user
= render 'shared/projects/search_form'
- = render 'shared/projects/dropdown'
= render 'filter'
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index ae59d9c728b..9585eb76912 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -10,5 +10,5 @@
- else
= render 'explore/head'
-= render 'explore/projects/nav' unless Feature.enabled?(:project_list_filter_bar) && current_user
+= render 'explore/projects/nav'
= render 'projects', projects: @projects
diff --git a/app/views/explore/projects/page_out_of_bounds.html.haml b/app/views/explore/projects/page_out_of_bounds.html.haml
index c554cce3dc6..ef5ee2c679e 100644
--- a/app/views/explore/projects/page_out_of_bounds.html.haml
+++ b/app/views/explore/projects/page_out_of_bounds.html.haml
@@ -9,7 +9,7 @@
- else
= render 'explore/head'
-= render 'explore/projects/nav' unless Feature.enabled?(:project_list_filter_bar) && current_user
+= render 'explore/projects/nav'
.nothing-here-block
.svg-content
@@ -18,4 +18,5 @@
%h5= _("Maximum page reached")
%p= _("Sorry, you have exceeded the maximum browsable page number. Please use the API to explore further.")
- = link_to _("Back to page %{number}") % { number: @max_page_number }, request.params.merge(page: @max_page_number), class: 'gl-button btn btn-inverted'
+ = render Pajamas::ButtonComponent.new(href: request.params.merge(page: @max_page_number)) do
+ = _("Back to page %{number}") % { number: @max_page_number }
diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml
index a1f2fea5134..ec7eefea264 100644
--- a/app/views/explore/projects/starred.html.haml
+++ b/app/views/explore/projects/starred.html.haml
@@ -9,5 +9,5 @@
- else
= render 'explore/head'
-= render 'explore/projects/nav' unless Feature.enabled?(:project_list_filter_bar) && current_user
+= render 'explore/projects/nav'
= render 'projects', projects: @projects
diff --git a/app/views/explore/projects/topic.html.haml b/app/views/explore/projects/topic.html.haml
index 76e59a49ed1..7b2c5683482 100644
--- a/app/views/explore/projects/topic.html.haml
+++ b/app/views/explore/projects/topic.html.haml
@@ -25,7 +25,6 @@
.top-area.gl-pt-2.gl-pb-2
.nav-controls
= render 'shared/projects/search_form'
- = render 'shared/projects/dropdown'
= render 'filter'
= render 'projects', projects: @projects
diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml
index e23f63b0064..8a92ec31b22 100644
--- a/app/views/explore/projects/trending.html.haml
+++ b/app/views/explore/projects/trending.html.haml
@@ -9,5 +9,5 @@
- else
= render 'explore/head'
-= render 'explore/projects/nav' unless Feature.enabled?(:project_list_filter_bar) && current_user
+= render 'explore/projects/nav'
= render 'projects', projects: @projects
diff --git a/app/views/groups/_activities.html.haml b/app/views/groups/_activities.html.haml
index 0d6c3e74ce8..b62076c23f3 100644
--- a/app/views/groups/_activities.html.haml
+++ b/app/views/groups/_activities.html.haml
@@ -1,8 +1,7 @@
.nav-block.activities
= render 'shared/event_filter', show_group_events: @group.supports_events?
.controls
- = link_to group_path(@group, rss_url_options), class: 'btn gl-button btn-default btn-icon d-none d-sm-inline-flex has-tooltip' , title: _('Subscribe') do
- = sprite_icon('rss', css_class: 'gl-icon')
+ = render Pajamas::ButtonComponent.new(href: group_path(@group, rss_url_options), icon: 'rss', button_options: { class: 'd-none d-sm-inline-flex has-tooltip', title: _('Subscribe') })
.content_list
.loading
diff --git a/app/views/groups/_group_admin_settings.html.haml b/app/views/groups/_group_admin_settings.html.haml
index 687a1fb32bf..0b26db64ffa 100644
--- a/app/views/groups/_group_admin_settings.html.haml
+++ b/app/views/groups/_group_admin_settings.html.haml
@@ -27,3 +27,13 @@
= f.text_field :two_factor_grace_period, class: 'form-control gl-form-input gl-form-input-sm'
%small.form-text.text-gl-muted
= _("Time (in hours) that users are allowed to skip forced configuration of two-factor authentication.")
+
+- if @group.namespace_settings.present?
+ .form-group.gl-form-group
+ %legend.col-form-label.col-form-label
+ = s_('Runners|Runner Registration')
+ - parent_disabled = Gitlab::CurrentSettings.valid_runner_registrars.exclude?('group') || !@group.all_ancestors_have_runner_registration_enabled?
+ = f.gitlab_ui_checkbox_component :runner_registration_enabled,
+ s_('Runners|New group runners can be registered'),
+ checkbox_options: { checked: @group.runner_registration_enabled && !parent_disabled, disabled: parent_disabled },
+ help_text: s_('Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD.').html_safe
diff --git a/app/views/groups/_home_panel.html.haml b/app/views/groups/_home_panel.html.haml
index a82a2e41508..1494990e427 100644
--- a/app/views/groups/_home_panel.html.haml
+++ b/app/views/groups/_home_panel.html.haml
@@ -3,16 +3,15 @@
- emails_disabled = @group.emails_disabled?
.group-home-panel
- .row.my-3
- .home-panel-title-row.col-md-12.col-lg-6.d-flex
+ .gl-display-flex.gl-justify-content-space-between.gl-flex-wrap.gl-sm-flex-direction-column.gl-gap-3.gl-my-5
+ .home-panel-title-row.gl-display-flex.gl-align-items-center
.avatar-container.rect-avatar.s64.home-panel-avatar.gl-flex-shrink-0.float-none{ class: 'gl-mr-3!' }
= group_icon(@group, class: 'avatar avatar-tile s64', width: 64, height: 64, itemprop: 'logo')
- .d-flex.flex-column.flex-wrap.align-items-baseline
- .d-inline-flex.align-items-baseline
- %h1.home-panel-title.gl-font-size-h1.gl-mt-3.gl-mb-2{ itemprop: 'name' }
- = @group.name
- %span.visibility-icon.gl-text-secondary.gl-ml-2.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) }
- = visibility_level_icon(@group.visibility_level, options: {class: 'icon'})
+ %div
+ %h1.home-panel-title.gl-font-size-h1.gl-mt-3.gl-mb-2.gl-display-flex{ itemprop: 'name' }
+ = @group.name
+ %span.visibility-icon.gl-text-secondary.has-tooltip.gl-ml-2{ data: { container: 'body' }, title: visibility_icon_description(@group) }
+ = visibility_level_icon(@group.visibility_level, options: {class: 'icon'})
.home-panel-metadata.gl-text-secondary.gl-font-base.gl-font-weight-normal.gl-line-height-normal{ data: { qa_selector: 'group_id_content' }, itemprop: 'identifier' }
- if can?(current_user, :read_group, @group)
%span.gl-display-inline-block.gl-vertical-align-middle
@@ -22,24 +21,23 @@
%span.gl-ml-3.gl-mb-3
= render 'shared/members/access_request_links', source: @group
- .home-panel-buttons.col-md-12.col-lg-6
- - if current_user
- .gl-display-flex.gl-flex-wrap.gl-lg-justify-content-end.gl-mx-n2{ data: { testid: 'group-buttons' } }
- - if current_user.admin?
- = link_to [:admin, @group], class: 'btn btn-default gl-button btn-icon gl-mt-3 gl-mr-2', title: _('View group in admin area'),
- data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
- = sprite_icon('admin')
- - if @notification_setting
- .js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), group_id: @group.id, container_class: 'gl-mx-2 gl-mt-3 gl-vertical-align-top', no_flip: 'true' } }
- - if can_create_subgroups
- .gl-px-2.gl-sm-w-auto.gl-w-full
- = link_to _("New subgroup"),
- new_group_path(parent_id: @group.id, anchor: 'create-group-pane'),
- class: "btn btn-default gl-button gl-mt-3 gl-sm-w-auto gl-w-full",
- data: { qa_selector: 'new_subgroup_button' }
- - if can_create_projects
- .gl-px-2.gl-sm-w-auto.gl-w-full
- = link_to _("New project"), new_project_path(namespace_id: @group.id), class: "btn btn-confirm gl-button gl-mt-3 gl-sm-w-auto gl-w-full", data: { qa_selector: 'new_project_button' }
+ - if current_user
+ .home-panel-buttons.gl-display-flex.gl-justify-content-md-end.gl-align-items-center.gl-flex-wrap.gl-gap-3{ data: { testid: 'group-buttons' } }
+ - if current_user.admin?
+ = link_to [:admin, @group], class: 'btn btn-default gl-button btn-icon', title: _('View group in admin area'),
+ data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = sprite_icon('admin')
+ - if @notification_setting
+ .js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), group_id: @group.id, container_class: 'gl-vertical-align-top', no_flip: 'true' } }
+ - if can_create_subgroups
+ .gl-sm-w-auto.gl-w-full
+ = link_to _("New subgroup"),
+ new_group_path(parent_id: @group.id, anchor: 'create-group-pane'),
+ class: "btn btn-default gl-button gl-sm-w-auto gl-w-full",
+ data: { qa_selector: 'new_subgroup_button' }
+ - if can_create_projects
+ .gl-sm-w-auto.gl-w-full
+ = link_to _("New project"), new_project_path(namespace_id: @group.id), class: "btn btn-confirm gl-button gl-sm-w-auto gl-w-full", data: { qa_selector: 'new_project_button' }
- if @group.description.present?
.group-home-desc.mt-1
diff --git a/app/views/groups/_import_group_from_another_instance_panel.html.haml b/app/views/groups/_import_group_from_another_instance_panel.html.haml
index d48bf0173a4..4a4bdfc6714 100644
--- a/app/views/groups/_import_group_from_another_instance_panel.html.haml
+++ b/app/views/groups/_import_group_from_another_instance_panel.html.haml
@@ -1,20 +1,39 @@
+- bulk_imports_disabled = !Gitlab::CurrentSettings.bulk_import_enabled?
+
= gitlab_ui_form_with url: configure_import_bulk_imports_path(namespace_id: params[:parent_id]), class: 'gl-show-field-errors' do |f|
.gl-border-l-solid.gl-border-r-solid.gl-border-t-solid.gl-border-gray-100.gl-border-1.gl-p-5.gl-mt-4
.gl-display-flex.gl-align-items-center
%h4.gl-display-flex
= s_('GroupsNew|Import groups from another instance of GitLab')
= link_to _('History'), history_import_bulk_imports_path, class: 'gl-link gl-ml-auto'
- = render Pajamas::AlertComponent.new(dismissible: false,
- variant: :warning) do |c|
- = c.body do
- - docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/group/import/index.md') }
- - docs_link_end = '</a>'.html_safe
- = s_('GroupsNew|Not all related objects are migrated. %{docs_link_start}More info%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: docs_link_end }
+
+ - if bulk_imports_disabled
+ = render Pajamas::AlertComponent.new(dismissible: false, variant: :tip) do |c|
+ = c.body do
+ = s_('GroupsNew|Importing groups by direct transfer is currently disabled.')
+
+ - if current_user.admin?
+ - admin_link_start = '<a href="%{url}">'.html_safe % { url: general_admin_application_settings_path(anchor: 'js-visibility-settings') }
+ - admin_link_end = '</a>'.html_safe
+
+ = s_('GroupsNew|Please %{admin_link_start}enable it in the Admin settings%{admin_link_end}.').html_safe % { admin_link_start: admin_link_start, admin_link_end: admin_link_end }
+ - else
+ = s_('GroupsNew|Please ask your Administrator to enable it in the Admin settings.')
+
+ = s_('GroupsNew|Remember to enable it also on the instance you are migrating from.')
+ - else
+ = render Pajamas::AlertComponent.new(dismissible: false,
+ variant: :warning) do |c|
+ = c.body do
+ - docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/group/import/index.md') }
+ - docs_link_end = '</a>'.html_safe
+ = s_('GroupsNew|Not all related objects are migrated. %{docs_link_start}More info%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: docs_link_end }
+
%p.gl-mt-3
- = s_('GroupsNew|Provide credentials for another instance of GitLab to import your groups directly.')
+ = s_('GroupsNew|Provide credentials for another instance of GitLab to import your groups directly.')
.form-group.gl-display-flex.gl-flex-direction-column
= f.label :bulk_import_gitlab_url, s_('GroupsNew|GitLab source URL'), for: 'import_gitlab_url'
- = f.text_field :bulk_import_gitlab_url, placeholder: 'https://gitlab.example.com', class: 'gl-form-input col-xs-12 col-sm-8',
+ = f.text_field :bulk_import_gitlab_url, disabled: bulk_imports_disabled, placeholder: 'https://gitlab.example.com', class: 'gl-form-input col-xs-12 col-sm-8',
required: true,
title: s_('GroupsNew|Please fill in GitLab source URL.'),
id: 'import_gitlab_url',
@@ -24,12 +43,13 @@
.gl-font-weight-normal
- pat_link_start = '<a href="%{url}" target="_blank">'.html_safe % { url: help_page_path('user/profile/personal_access_tokens') }
- short_living_link_start = '<a href="%{url}" target="_blank">'.html_safe % { url: help_page_path('security/token_overview', anchor: 'security-considerations') }
- = s_('GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token.').html_safe % { pat_link_start: pat_link_start, pat_link_end: '</a>'.html_safe, short_living_link_start: short_living_link_start, short_living_link_end: '</a>'.html_safe }
+ = s_('GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time.').html_safe % { code_start: '<code>'.html_safe, code_end: '</code>'.html_safe, pat_link_start: pat_link_start, pat_link_end: '</a>'.html_safe, short_living_link_start: short_living_link_start, short_living_link_end: '</a>'.html_safe }
= f.text_field :bulk_import_gitlab_access_token, placeholder: s_('GroupsNew|e.g. h8d3f016698e...'), class: 'gl-form-input gl-mt-3 col-xs-12 col-sm-8',
required: true,
+ disabled: bulk_imports_disabled,
autocomplete: 'off',
title: s_('GroupsNew|Please fill in your personal access token.'),
id: 'import_gitlab_token',
data: { qa_selector: 'import_gitlab_token' }
.gl-border-gray-100.gl-border-solid.gl-border-1.gl-bg-gray-10.gl-p-5
- = f.submit s_('GroupsNew|Connect instance'), pajamas_button: true, data: { qa_selector: 'connect_instance_button' }
+ = f.submit s_('GroupsNew|Connect instance'), disabled: bulk_imports_disabled, pajamas_button: true, data: { qa_selector: 'connect_instance_button' }
diff --git a/app/views/groups/_invite_groups_modal.html.haml b/app/views/groups/_invite_groups_modal.html.haml
index 2e11f6cee4f..b8e40460a92 100644
--- a/app/views/groups/_invite_groups_modal.html.haml
+++ b/app/views/groups/_invite_groups_modal.html.haml
@@ -1,3 +1,3 @@
- return unless can_admin_group_member?(group)
-.js-invite-groups-modal{ data: common_invite_group_modal_data(group, GroupMember, 'false') }
+.js-invite-groups-modal{ data: { reload_page_on_submit: local_assigns.fetch(:reload_page_on_submit, false).to_s }.merge(common_invite_group_modal_data(group, GroupMember, 'false')) }
diff --git a/app/views/groups/_invite_members_modal.html.haml b/app/views/groups/_invite_members_modal.html.haml
index 786034fd2e7..bf0e8b627fd 100644
--- a/app/views/groups/_invite_members_modal.html.haml
+++ b/app/views/groups/_invite_members_modal.html.haml
@@ -2,4 +2,5 @@
.js-invite-members-modal{ data: { is_project: 'false',
access_levels: GroupMember.access_level_roles.to_json,
+ reload_page_on_submit: local_assigns.fetch(:reload_page_on_submit, false).to_s,
help_link: help_page_url('user/permissions') }.merge(common_invite_modal_dataset(group)).merge(users_filter_data(group)) }
diff --git a/app/views/groups/_new_group_fields.html.haml b/app/views/groups/_new_group_fields.html.haml
index 94b0b018084..95990e8937c 100644
--- a/app/views/groups/_new_group_fields.html.haml
+++ b/app/views/groups/_new_group_fields.html.haml
@@ -30,5 +30,7 @@
= recaptcha_tags nonce: content_security_policy_nonce
.row
.col-sm-12
- = f.submit submit_label, class: "btn gl-button btn-confirm", data: { qa_selector: 'create_group_button' }
- = link_to _('Cancel'), dashboard_groups_path, class: 'btn gl-button btn-default btn-cancel'
+ = f.submit submit_label, pajamas_button: true, data: { qa_selector: 'create_group_button' }
+ = render Pajamas::ButtonComponent.new(href: dashboard_groups_path) do
+ = _('Cancel')
+
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index bca1c874cc6..8763912438b 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -13,7 +13,7 @@
= _('Collapse')
%p
= _('Update your group name, description, avatar, and visibility.')
- = link_to s_('Learn more about groups.'), help_page_path('user/group/index')
+ = link_to _('Learn more about groups.'), help_page_path('user/group/index')
.settings-content
= render 'groups/settings/general'
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index 4da70c8bf5d..298ed2c0806 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -15,8 +15,8 @@
classes: 'gl-md-w-auto gl-w-full gl-md-ml-3 gl-md-mt-0 gl-mt-3',
trigger_source: 'group-members-page',
display_text: _('Invite members') } }
- = render 'groups/invite_groups_modal', group: @group
- = render 'groups/invite_members_modal', group: @group
+ = render 'groups/invite_groups_modal', group: @group, reload_page_on_submit: true
+ = render 'groups/invite_members_modal', group: @group, reload_page_on_submit: true
= render_if_exists 'groups/group_members/ldap_sync'
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index 925e7d46f14..6faa4758d66 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -1,8 +1,9 @@
- page_title _('Issues')
+- add_page_specific_style 'page_bundles/issuable_list'
- add_page_specific_style 'page_bundles/issues_list'
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{@group.name} issues")
-.js-issues-list{ data: group_issues_list_data(@group, current_user) }
+.js-issues-list-root{ data: group_issues_list_data(@group, current_user) }
- if can?(current_user, :admin_issue, @group) && @group.licensed_feature_available?(:group_bulk_edit)
= render_if_exists 'shared/issuable/group_bulk_update_sidebar', group: @group, type: :issues
diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml
index 8187dda5471..a03c406acc6 100644
--- a/app/views/groups/labels/index.html.haml
+++ b/app/views/groups/labels/index.html.haml
@@ -11,7 +11,7 @@
.labels-container.gl-mt-5
- if @labels.any?
.text-muted.gl-mb-5
- = _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuable_types.to_sentence }
+ = labels_function_introduction
.other-labels
%h4= _('Labels')
%ul.manage-labels-list.js-other-labels
diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml
index 6c4a8b53764..92f6c896e7b 100644
--- a/app/views/groups/merge_requests.html.haml
+++ b/app/views/groups/merge_requests.html.haml
@@ -1,6 +1,7 @@
- @can_bulk_update = can?(current_user, :admin_merge_request, @group) && @group.licensed_feature_available?(:group_bulk_edit) && issuables_count_for_state(:merge_requests, :all) > 0
- page_title _("Merge requests")
+- add_page_specific_style 'page_bundles/issuable_list'
.top-area
= render 'shared/issuable/nav', type: :merge_requests
diff --git a/app/views/groups/milestones/_form.html.haml b/app/views/groups/milestones/_form.html.haml
index d4b1c3c27f1..a99d76f99a7 100644
--- a/app/views/groups/milestones/_form.html.haml
+++ b/app/views/groups/milestones/_form.html.haml
@@ -22,7 +22,9 @@
.form-actions
- if @milestone.new_record?
= f.submit _('Create milestone'), data: { qa_selector: "create_milestone_button" }, pajamas_button: true
- = link_to _("Cancel"), group_milestones_path(@group), class: "btn gl-button btn-cancel"
+ = render Pajamas::ButtonComponent.new(href: group_milestones_path(@group)) do
+ = _("Cancel")
- else
= f.submit _('Update milestone'), pajamas_button: true
- = link_to _("Cancel"), group_milestone_path(@group, @milestone), class: "btn gl-button btn-cancel"
+ = render Pajamas::ButtonComponent.new(href: group_milestone_path(@group, @milestone)) do
+ = _("Cancel")
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index 50a1b474504..f49b69f821d 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -9,13 +9,14 @@
= render 'shared/milestones/search_form'
= render 'shared/milestones_sort_dropdown'
- if can?(current_user, :admin_milestone, @group)
- = link_to _('New milestone'), new_group_milestone_path(@group), class: "btn gl-button btn-confirm gl-ml-3", data: { qa_selector: "new_group_milestone_link" }
-
+ = render Pajamas::ButtonComponent.new(href: new_group_milestone_path(@group), variant: :confirm, button_options: { data: { qa_selector: "new_group_milestone_link" }, class: "gl-ml-3" }) do
+ = _('New milestone')
- if @milestones.blank?
= render 'shared/empty_states/milestones_tab', learn_more_path: help_page_path('user/project/milestones/index') do
- if can?(current_user, :admin_milestone, @group)
.text-center
- = link_to _('New milestone'), new_group_milestone_path(@group), class: "btn gl-button btn-confirm", data: { qa_selector: "new_group_milestone_link" }
+ = render Pajamas::ButtonComponent.new(href: new_group_milestone_path(@group), variant: :confirm, button_options: { data: { qa_selector: "new_group_milestone_link" }}) do
+ = _('New milestone')
- else
.milestones
%ul.content-list
@@ -29,4 +30,5 @@
= render 'shared/empty_states/milestones', learn_more_path: help_page_path('user/project/milestones/index') do
- if can?(current_user, :admin_milestone, @group)
.text-center
- = link_to _('New milestone'), new_group_milestone_path(@group), class: "btn gl-button btn-confirm", data: { qa_selector: "new_group_milestone_link" }
+ = render Pajamas::ButtonComponent.new(href: new_group_milestone_path(@group), variant: :confirm, button_options: { data: { qa_selector: "new_group_milestone_link" }}) do
+ = _('New milestone')
diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml
index e7ae54a8879..cae347630ee 100644
--- a/app/views/groups/projects.html.haml
+++ b/app/views/groups/projects.html.haml
@@ -12,19 +12,19 @@
= _("New project")
- c.body do
%ul.content-list
- - @projects.each do |project|
- %li.project-row.gl-align-items-center{ class: 'gl-display-flex!' }
+ - @projects.each_with_index do |project, idx|
+ %li.project-row.gl-align-items-center{ class: 'gl-display-flex!', data: { qa_selector: 'project_row_container', qa_index: idx } }
.avatar-container.rect-avatar.s40.gl-flex-shrink-0
= project_icon(project, alt: '', class: 'avatar project-avatar s40', width: 40, height: 40)
.gl-min-w-0.gl-flex-grow-1
.title
= link_to project_path(project), class: 'js-prefetch-document' do
- %span.project-full-name
- %span.namespace-name
+ %span.project-full-name{ data: { qa_selector: 'project_fullname_content' } }
+ %span.namespace-name{ data: { qa_selector: 'project_namespace_content' } }
- if project.namespace
= project.namespace.human_name
\/
- %span.project-name
+ %span.project-name{ data: { qa_selector: 'project_name_content', qa_project_name: project.name } }
= project.name
%span{ class: visibility_level_color(project.visibility_level) }
= visibility_level_icon(project.visibility_level)
@@ -38,9 +38,9 @@
= render 'project_badges', project: project
.controls.gl-flex-shrink-0.gl-ml-5
- = link_to _('Members'), project_project_members_path(project), id: dom_id(project, :edit), class: "btn gl-button"
- = link_to _('Edit'), edit_project_path(project), id: dom_id(project, :edit), class: "btn gl-button"
- = render 'delete_project_button', project: project
+ = link_to _('Members'), project_project_members_path(project), id: dom_id(project, :edit), class: "btn gl-button", data: { qa_selector: 'project_members_button' }
+ = link_to _('Edit'), edit_project_path(project), id: dom_id(project, :edit), class: "btn gl-button", data: { qa_selector: 'project_edit_button' }
+ = render 'delete_project_button', project: project, data: { qa_selector: 'project_delete_button' }
- if @projects.blank?
.nothing-here-block= _("This group has no projects yet")
diff --git a/app/views/groups/registry/repositories/index.html.haml b/app/views/groups/registry/repositories/index.html.haml
index 6060d697f52..efd2e53e100 100644
--- a/app/views/groups/registry/repositories/index.html.haml
+++ b/app/views/groups/registry/repositories/index.html.haml
@@ -1,6 +1,6 @@
- page_title _("Container Registry")
- @content_class = "limit-container-width" unless fluid_layout
-- add_page_startup_graphql_call('container_registry/get_container_repositories', { fullPath: @group.full_path, first: 10, name: nil, isGroupPage: true, sort: nil} )
+- add_page_startup_graphql_call('container_registry/get_container_repositories', { fullPath: @group.full_path, first: 10, name: nil, isGroupPage: true, sort: nil})
%section
#js-container-registry{ data: { endpoint: group_container_registries_path(@group),
@@ -12,7 +12,6 @@
"registry_host_url_with_port" => escape_once(registry_config.host_port),
"garbage_collection_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'container-registry-garbage-collection'),
"run_cleanup_policies_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'run-the-cleanup-policy-now'),
- "container_registry_importing_help_page_path" => help_page_path('user/packages/container_registry/index', anchor: 'tags-temporarily-cannot-be-marked-for-deletion'),
"is_admin": current_user&.admin.to_s,
is_group_page: "true",
"group_path": @group.full_path,
diff --git a/app/views/groups/runners/_settings.html.haml b/app/views/groups/runners/_settings.html.haml
index 3e5ec3c26e2..24b2469c501 100644
--- a/app/views/groups/runners/_settings.html.haml
+++ b/app/views/groups/runners/_settings.html.haml
@@ -3,10 +3,3 @@
- if @group.licensed_feature_available?(:stale_runner_cleanup_for_namespace)
.gl-mb-5
#stale-runner-cleanup-form{ data: { group_full_path: @group.full_path, stale_timeout_secs: ::Ci::Runner::STALE_TIMEOUT.to_i } }
-= render Pajamas::BannerComponent.new(button_text: s_('Runners|Take me there!'),
- button_link: group_runners_path(@group),
- svg_path: 'illustrations/rocket-launch-md.svg',
- close_options: { class: 'gl-display-none' }) do |c|
- - c.title do
- = s_('Runners|New group runners view')
- %p= s_('Runners|The new view gives you more space and better visibility into your fleet of runners.')
diff --git a/app/views/groups/runners/index.html.haml b/app/views/groups/runners/index.html.haml
index 1146063969b..9ea83397348 100644
--- a/app/views/groups/runners/index.html.haml
+++ b/app/views/groups/runners/index.html.haml
@@ -1,3 +1,3 @@
- page_title s_('Runners|Runners')
-#js-group-runners{ data: group_runners_data_attributes(@group).merge( { group_runners_limited_count: @group_runners_limited_count, registration_token: @group_runner_registration_token } ) }
+#js-group-runners{ data: group_runners_data_attributes(@group).merge({ group_runners_limited_count: @group_runners_limited_count, registration_token: @group_runner_registration_token }) }
diff --git a/app/views/groups/settings/_export.html.haml b/app/views/groups/settings/_export.html.haml
index 66c1341fb15..5d79d0f8e79 100644
--- a/app/views/groups/settings/_export.html.haml
+++ b/app/views/groups/settings/_export.html.haml
@@ -25,10 +25,10 @@
%li= _('Runner tokens')
%li= _('SAML discovery tokens')
- if group.export_file_exists?
- = link_to _('Download export'), download_export_group_path(group),
- rel: 'nofollow', method: :get, class: 'btn gl-button btn-default', data: { qa_selector: 'download_export_link' }
- = link_to _('Regenerate export'), export_group_path(group),
- method: :post, class: 'btn gl-button btn-default', data: { qa_selector: 'regenerate_export_group_link' }
+ = render Pajamas::ButtonComponent.new(href: download_export_group_path(group), button_options: { rel: 'nofollow', data: { method: :get, qa_selector: 'download_export_link' } }) do
+ = _('Download export')
+ = render Pajamas::ButtonComponent.new(href: export_group_path(group), button_options: { data: { method: :post, qa_selector: 'regenerate_export_group_link' } }) do
+ = _('Regenerate export')
- else
- = link_to _('Export group'), export_group_path(group),
- method: :post, class: 'btn gl-button btn-default', data: { qa_selector: 'export_group_link' }
+ = render Pajamas::ButtonComponent.new(href: export_group_path(group), button_options: { data: { method: :post, qa_selector: 'export_group_link' } }) do
+ = _('Export group')
diff --git a/app/views/groups/settings/_general.html.haml b/app/views/groups/settings/_general.html.haml
index be9d2c45885..658109fde64 100644
--- a/app/views/groups/settings/_general.html.haml
+++ b/app/views/groups/settings/_general.html.haml
@@ -32,4 +32,4 @@
= link_to s_('Groups|Remove avatar'), group_avatar_path(@group.to_param), aria: { label: s_('Groups|Remove avatar') }, data: { confirm: s_('Groups|Avatar will be removed. Are you sure?'), 'confirm-btn-variant': 'danger' }, method: :delete, class: 'gl-button btn btn-danger-secondary'
.form-group.gl-form-group
= render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group
- = f.submit s_('Groups|Save changes'), class: 'btn gl-button btn-confirm js-dirty-submit', data: { qa_selector: 'save_name_visibility_settings_button' }
+ = f.submit s_('Groups|Save changes'), pajamas_button: true, class: 'js-dirty-submit', data: { qa_selector: 'save_name_visibility_settings_button' }
diff --git a/app/views/groups/settings/_git_access_protocols.html.haml b/app/views/groups/settings/_git_access_protocols.html.haml
index df798db79ad..01e8536c7ad 100644
--- a/app/views/groups/settings/_git_access_protocols.html.haml
+++ b/app/views/groups/settings/_git_access_protocols.html.haml
@@ -1,6 +1,6 @@
- if group.root? && Feature.enabled?(:group_level_git_protocol_control, group)
.form-group
- = f.label s_('Enabled Git access protocols'), class: 'label-bold'
+ = f.label _('Enabled Git access protocols'), class: 'label-bold'
= f.select :enabled_git_access_protocol, options_for_select(enabled_git_access_protocol_options_for_group, group.enabled_git_access_protocol), {}, class: 'form-control', data: { qa_selector: 'enabled_git_access_protocol_dropdown' }, disabled: !::Gitlab::CurrentSettings.enabled_git_access_protocol.blank?
- if !::Gitlab::CurrentSettings.enabled_git_access_protocol.blank?
.form-text.text-muted
diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml
index e35c0341ec0..a18789b52a3 100644
--- a/app/views/groups/settings/_permissions.html.haml
+++ b/app/views/groups/settings/_permissions.html.haml
@@ -52,4 +52,4 @@
checkbox_options: { checked: @group.crm_enabled? },
help_text: s_('GroupSettings|Organizations and contacts can be created and associated with issues.')
- = f.submit _('Save changes'), class: 'btn gl-button btn-confirm gl-mt-3 js-dirty-submit', data: { qa_selector: 'save_permissions_changes_button' }
+ = f.submit _('Save changes'), pajamas_button: true, class: 'gl-mt-3 js-dirty-submit', data: { qa_selector: 'save_permissions_changes_button' }
diff --git a/app/views/groups/settings/_remove_button.html.haml b/app/views/groups/settings/_remove_button.html.haml
index df978f3cb96..cb05076b39d 100644
--- a/app/views/groups/settings/_remove_button.html.haml
+++ b/app/views/groups/settings/_remove_button.html.haml
@@ -1,8 +1,8 @@
- remove_form_id = local_assigns.fetch(:remove_form_id, nil)
-- if group.paid?
+- if group.prevent_delete?
= render Pajamas::AlertComponent.new(dismissible: false, alert_options: { class: 'gl-mb-5', data: { testid: 'group-has-linked-subscription-alert' }}) do |c|
= c.body do
- = html_escape(_("This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group.")) % { linkStart: "<a href=\"#{help_page_path('subscriptions/index', anchor: 'change-the-linked-namespace')}\">".html_safe, linkEnd: '</a>'.html_safe }
+ = html_escape(_("This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group.")) % { linkStart: "<a href=\"#{help_page_path('subscriptions/gitlab_com/index', anchor: 'change-the-linked-namespace')}\">".html_safe, linkEnd: '</a>'.html_safe }
.js-confirm-danger{ data: group_settings_confirm_modal_data(group, remove_form_id) }
diff --git a/app/views/groups/settings/_transfer.html.haml b/app/views/groups/settings/_transfer.html.haml
index e01d703206c..3c76e8a864a 100644
--- a/app/views/groups/settings/_transfer.html.haml
+++ b/app/views/groups/settings/_transfer.html.haml
@@ -1,7 +1,7 @@
- form_id = "transfer-group-form"
- initial_data = { button_text: s_('GroupSettings|Transfer group'), group_name: @group.name, group_id: @group.id, target_form_id: form_id, is_paid_group: group.paid?.to_s }
-.sub-section
+.sub-section{ data: { qa_selector: 'transfer_group_content' } }
%h4.warning-title= s_('GroupSettings|Transfer group')
%p= _('Transfer group to another parent group.')
= form_for group, url: transfer_group_path(group), method: :put, html: { id: form_id, class: 'js-group-transfer-form' } do |f|
@@ -15,5 +15,5 @@
- if group.paid?
= render Pajamas::AlertComponent.new(dismissible: false, alert_options: { class: 'gl-mb-5' }) do |c|
= c.body do
- = html_escape(_("This group can't be transferred because it is linked to a subscription. To transfer this group, %{linkStart}link the subscription%{linkEnd} with a different group.")) % { linkStart: "<a href=\"#{help_page_path('subscriptions/index', anchor: 'change-the-linked-namespace')}\">".html_safe, linkEnd: '</a>'.html_safe }
+ = html_escape(_("This group can't be transferred because it is linked to a subscription. To transfer this group, %{linkStart}link the subscription%{linkEnd} with a different group.")) % { linkStart: "<a href=\"#{help_page_path('subscriptions/gitlab_com/index', anchor: 'change-the-linked-namespace')}\">".html_safe, linkEnd: '</a>'.html_safe }
.js-transfer-group-form{ data: initial_data }
diff --git a/app/views/groups/settings/applications/index.html.haml b/app/views/groups/settings/applications/index.html.haml
index 96f834bd271..95bf2151bda 100644
--- a/app/views/groups/settings/applications/index.html.haml
+++ b/app/views/groups/settings/applications/index.html.haml
@@ -1,4 +1,5 @@
- page_title _("Group applications")
+- add_page_specific_style 'page_bundles/settings'
= render 'shared/doorkeeper/applications/index',
oauth_applications_enabled: user_oauth_applications?,
diff --git a/app/views/groups/settings/ci_cd/_auto_devops_form.html.haml b/app/views/groups/settings/ci_cd/_auto_devops_form.html.haml
index a55ccd94974..06cb9893196 100644
--- a/app/views/groups/settings/ci_cd/_auto_devops_form.html.haml
+++ b/app/views/groups/settings/ci_cd/_auto_devops_form.html.haml
@@ -13,4 +13,4 @@
help_text: '%{help_text} %{learn_more_link}'.html_safe % { help_text: help_text, learn_more_link: learn_more_link },
checkbox_options: { checked: group.auto_devops_enabled? }
- = f.submit _('Save changes'), class: 'btn gl-button btn-confirm gl-mt-5'
+ = f.submit _('Save changes'), class: 'gl-mt-5', pajamas_button: true
diff --git a/app/views/groups/settings/ci_cd/_form.html.haml b/app/views/groups/settings/ci_cd/_form.html.haml
index 89e353b94b0..d31d22c61be 100644
--- a/app/views/groups/settings/ci_cd/_form.html.haml
+++ b/app/views/groups/settings/ci_cd/_form.html.haml
@@ -7,5 +7,5 @@
= f.number_field :max_artifacts_size, class: 'form-control'
%p.form-text.text-muted
= _("The maximum file size in megabytes for individual job artifacts.")
- = link_to s_('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size'), target: '_blank', rel: 'noopener noreferrer'
= f.submit _('Save changes'), pajamas_button: true
diff --git a/app/views/groups/settings/repository/_default_branch.html.haml b/app/views/groups/settings/repository/_default_branch.html.haml
index 844a5f890a4..e8aa809a6ca 100644
--- a/app/views/groups/settings/repository/_default_branch.html.haml
+++ b/app/views/groups/settings/repository/_default_branch.html.haml
@@ -21,4 +21,4 @@
= render 'groups/settings/default_branch_protection', f: f, group: @group
= f.hidden_field :redirect_target, value: "repository_settings"
- = f.submit _('Save changes'), class: 'btn gl-button btn-confirm'
+ = f.submit _('Save changes'), pajamas_button: true
diff --git a/app/views/groups/usage_quotas/index.html.haml b/app/views/groups/usage_quotas/index.html.haml
new file mode 100644
index 00000000000..a8c1071b876
--- /dev/null
+++ b/app/views/groups/usage_quotas/index.html.haml
@@ -0,0 +1,7 @@
+- page_title s_("UsageQuota|Usage")
+
+.gl-alert.gl-alert-no-icon.gl-alert-info.gl-mt-6
+ %h2.gl-alert-title
+ Development
+ .gl-alert-content
+ Placeholder for usage quotas Vue app
diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml
index 8c74aac5ef5..b3f9d538e83 100644
--- a/app/views/help/index.html.haml
+++ b/app/views/help/index.html.haml
@@ -11,7 +11,7 @@
%span= link_to_version
- if show_version_check?
%span.gl-mt-5.gl-mb-3.gl-ml-3
- .js-gitlab-version-check-badge{ data: { "size": "lg", "actionable": "true" } }
+ .js-gitlab-version-check-badge{ data: { "size": "lg", "actionable": "true", "version": gitlab_version_check.to_json } }
%hr
- unless Gitlab::CurrentSettings.help_page_hide_commercial_content?
diff --git a/app/views/ide/_show.html.haml b/app/views/ide/_show.html.haml
index 95c15612adf..b18b5f1574b 100644
--- a/app/views/ide/_show.html.haml
+++ b/app/views/ide/_show.html.haml
@@ -1,13 +1,10 @@
-- @body_class = 'ide-layout'
- page_title _('IDE')
-- add_page_specific_style 'page_bundles/build'
-- add_page_specific_style 'page_bundles/ide'
+- unless use_new_web_ide?
+ - add_page_specific_style 'page_bundles/build'
+ - add_page_specific_style 'page_bundles/ide'
-- content_for :prefetch_asset_tags do
- - webpack_preload_asset_tag('monaco')
+ - content_for :prefetch_asset_tags do
+ - webpack_preload_asset_tag('monaco')
-#ide.ide-loading{ data: ide_data }
- .text-center
- = gl_loading_icon(size: 'md')
- %h2.clgray= _('Loading the GitLab IDE...')
+= render partial: 'shared/ide_root', locals: { data: ide_data, loading_text: _('Loading the GitLab IDE...') }
diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml
index e92db09aaf1..4d2186a1352 100644
--- a/app/views/import/_githubish_status.html.haml
+++ b/app/views/import/_githubish_status.html.haml
@@ -4,6 +4,7 @@
- filterable = local_assigns.fetch(:filterable, true)
- paginatable = local_assigns.fetch(:paginatable, false)
- default_namespace_path = (local_assigns[:default_namespace] || current_user.namespace).full_path
+- cancel_path = local_assigns.fetch(:cancel_path, nil)
- provider_title = Gitlab::ImportSources.title(local_assigns.fetch(:provider))
- optional_stages = local_assigns.fetch(:optional_stages, [])
@@ -13,11 +14,11 @@
#import-projects-mount-element{ data: { provider: provider, provider_title: provider_title,
can_select_namespace: current_user.can_select_namespace?.to_s,
ci_cd_only: has_ci_cd_only_params?.to_s,
- namespaces_path: import_available_namespaces_path,
repos_path: url_for([:status, :import, provider, { format: :json }]),
jobs_path: url_for([:realtime_changes, :import, provider, { format: :json }]),
default_target_namespace: default_namespace_path,
import_path: url_for([:import, provider, { format: :json }]),
+ cancel_path: cancel_path,
filterable: filterable.to_s,
paginatable: paginatable.to_s,
optional_stages: optional_stages.to_json }.merge(extra_data) }
diff --git a/app/views/import/bulk_imports/status.html.haml b/app/views/import/bulk_imports/status.html.haml
index 1c8de23f28f..e1547920708 100644
--- a/app/views/import/bulk_imports/status.html.haml
+++ b/app/views/import/bulk_imports/status.html.haml
@@ -3,7 +3,6 @@
- page_title _('Import groups')
#import-groups-mount-element{ data: { status_path: status_import_bulk_imports_path(format: :json),
- available_namespaces_path: import_available_namespaces_path(format: :json),
default_target_namespace: @namespace&.id,
create_bulk_import_path: import_bulk_imports_path(format: :json),
jobs_path: realtime_changes_import_bulk_imports_path(format: :json),
diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml
index 25afe9a7b1b..4a9f8be35c3 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -10,4 +10,5 @@
= render 'import/githubish_status',
provider: 'github', paginatable: paginatable,
default_namespace: @namespace,
+ cancel_path: cancel_import_github_path,
optional_stages: Gitlab::GithubImport::Settings.stages_array
diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml
index 42a9d2c3136..079123e989e 100644
--- a/app/views/import/gitlab_projects/new.html.haml
+++ b/app/views/import/gitlab_projects/new.html.haml
@@ -21,5 +21,8 @@
= file_field_tag :file, class: ''
.row
.form-actions.col-sm-12
- = submit_tag _('Import project'), class: 'gl-button btn btn-confirm', data: { qa_selector: 'import_project_button' }
- = link_to _('Cancel'), new_project_path, class: 'gl-button btn btn-default btn-cancel'
+ = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, button_options: { class: 'gl-mr-2', data: { qa_selector: 'import_project_button' }}) do
+ = _('Import project')
+ = render Pajamas::ButtonComponent.new(href: new_project_path) do
+ = _('Cancel')
+
diff --git a/app/views/import/manifest/_form.html.haml b/app/views/import/manifest/_form.html.haml
index 096d2543502..a35e9ea3fcf 100644
--- a/app/views/import/manifest/_form.html.haml
+++ b/app/views/import/manifest/_form.html.haml
@@ -2,11 +2,14 @@
.form-group
= label_tag :group_id, nil, class: 'label-bold' do
= _('Group')
- .input-group
- .input-group-prepend.has-tooltip{ title: root_url }
- .input-group-text
- = root_url
- = select_tag :group_id, namespaces_options(params[:namespace_id], display_path: true, groups_only: true), { class: 'select2 js-select-namespace' }
+ .input-group.gl-max-w-62
+ - namespace_id = namespace_id_from(params) || current_user.manageable_groups(include_groups_with_developer_maintainer_access: true)&.first&.id
+ - namespace_full_path = GroupFinder.new(current_user).execute(id: namespace_id)&.full_path
+ .js-vue-new-project-url-select{ data: { namespace_full_path: namespace_full_path,
+ namespace_id: namespace_id ,
+ input_id: 'group_id',
+ input_name: 'group_id',
+ root_url: root_url } }
.form-text.text-muted
= _('Choose the top-level group for your repository imports.')
@@ -19,5 +22,8 @@
= link_to sprite_icon('question-o'), help_page_path('user/project/import/manifest')
.gl-mb-3
- = submit_tag _('List available repositories'), class: 'gl-button btn btn-confirm'
- = link_to _('Cancel'), new_project_path, class: 'gl-button btn btn-default btn-cancel'
+ = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm) do
+ = _('List available repositories')
+
+ = render Pajamas::ButtonComponent.new(href: new_project_path) do
+ = _('Cancel')
diff --git a/app/views/import/shared/_new_project_form.html.haml b/app/views/import/shared/_new_project_form.html.haml
index 16526382f42..6000612a285 100644
--- a/app/views/import/shared/_new_project_form.html.haml
+++ b/app/views/import/shared/_new_project_form.html.haml
@@ -2,20 +2,23 @@
.form-group.project-name.col-sm-12
= label_tag :name, _('Project name'), class: 'label-bold'
= text_field_tag :name, @name, placeholder: "My awesome project", class: "js-project-name form-control gl-form-input input-lg", autofocus: true, required: true, aria: { required: true }, data: { qa_selector: 'project_name_field' }
- .form-group.col-12.col-sm-6
+ .form-group.col-12.col-sm-6.gl-pr-0
= label_tag :namespace_id, _('Project URL'), class: 'label-bold'
- .form-group
- .input-group.gl-flex-nowrap
- - if current_user.can_select_namespace?
- .input-group-prepend.flex-shrink-0.has-tooltip{ title: root_url }
- .input-group-text
- = root_url
- = select_tag :namespace_id, namespaces_options(namespace_id_from(params) || :current_user, display_path: true, extra_group: namespace_id_from(params)), class: 'select2 js-select-namespace block-truncated'
- - else
- .input-group-prepend.static-namespace.has-tooltip{ title: user_url(current_user.username) + '/' }
- .input-group-text.border-0
- #{user_url(current_user.username)}/
- = hidden_field_tag :namespace_id, current_user.namespace_id
+ .input-group.gl-flex-nowrap
+ - if current_user.can_select_namespace?
+ - namespace_id = namespace_id_from(params)
+ .js-vue-new-project-url-select{ data: { namespace_full_path: GroupFinder.new(current_user).execute(id: namespace_id)&.full_path || current_user.namespace.full_path,
+ namespace_id: namespace_id || current_user.namespace_id,
+ input_id: 'namespace_id',
+ input_name: 'namespace_id',
+ root_url: root_url,
+ user_namespace_id: current_user.namespace_id } }
+ - else
+ .input-group-prepend.static-namespace.flex-shrink-0.has-tooltip{ title: user_url(current_user.username) + '/' }
+ .input-group-text.border-0
+ #{user_url(current_user.username)}/
+ = hidden_field_tag :namespace_id, current_user.namespace_id
+ .gl-align-self-center.gl-pl-5 /
.form-group.col-12.col-sm-6.project-path
= label_tag :path, _('Project slug'), class: 'label-bold'
= text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control gl-form-input", required: true, aria: { required: true }, data: { qa_selector: 'project_slug_field' }
diff --git a/app/views/invites/show.html.haml b/app/views/invites/show.html.haml
index c1ee12bb6c8..5f65405c8bc 100644
--- a/app/views/invites/show.html.haml
+++ b/app/views/invites/show.html.haml
@@ -17,8 +17,10 @@
= html_escape(_("You have been invited by %{link_to_inviter} to join %{source_name} %{strong_open}%{link_to_source}%{strong_close} as %{role}")) % { link_to_inviter: link_to_inviter, source_name: @invite_details[:title], strong_open: '<strong>'.html_safe, link_to_source: link_to_source, strong_close: '</strong>'.html_safe, role: @member.human_access }
.actions
- = link_to _("Accept invitation"), accept_invite_url(@token), method: :post, class: "btn gl-button btn-confirm"
- = link_to _("Decline"), decline_invite_url(@token), method: :post, class: "btn gl-button btn-danger gl-ml-3"
+ = render Pajamas::ButtonComponent.new(variant: :confirm, method: :post, href: accept_invite_url(@token)) do
+ = _("Accept invitation")
+ = render Pajamas::ButtonComponent.new(variant: :danger, method: :post, href: decline_invite_url(@token), button_options: { class: 'gl-ml-3' }) do
+ = _("Decline")
- else
%p
diff --git a/app/views/jira_connect/users/show.html.haml b/app/views/jira_connect/users/show.html.haml
index 569c4587f14..5db6cb44ff6 100644
--- a/app/views/jira_connect/users/show.html.haml
+++ b/app/views/jira_connect/users/show.html.haml
@@ -11,7 +11,10 @@
= s_('JiraService|You can now close this window and%{br}return to the GitLab for Jira application.').html_safe % { br: '<br>'.html_safe }
- if @jira_app_link
- %p= link_to s_('Integrations|Return to GitLab for Jira'), @jira_app_link, class: 'gl-button btn btn-confirm'
+ %p
+ = render Pajamas::ButtonComponent.new(href: @jira_app_link, variant: :confirm) do
+ = s_('Integrations|Return to GitLab for Jira')
+
%p= link_to _('Sign out'), destroy_user_session_path, method: :post
diff --git a/app/views/layouts/_google_tag_manager_head.html.haml b/app/views/layouts/_google_tag_manager_head.html.haml
index 97e118aba93..21b9a604a35 100644
--- a/app/views/layouts/_google_tag_manager_head.html.haml
+++ b/app/views/layouts/_google_tag_manager_head.html.haml
@@ -20,6 +20,17 @@
'wait_for_update': 500
});
+ window.geofeed = (options) => {
+ dataLayer.push({
+ 'event' : 'OneTrustCountryLoad',
+ 'oneTrustCountryId': options.country.toString()
+ })
+ }
+
+ const json = document.createElement('script');
+ json.setAttribute('src', 'https://geolocation.onetrust.com/cookieconsentpub/v1/geo/location/geofeed');
+ document.head.appendChild(json);
+
- if Feature.enabled?(:gtm_nonce, type: :ops)
= javascript_tag nonce: content_security_policy_nonce do
:plain
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 2ac926a7fc3..ea2f452b9e2 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -18,7 +18,13 @@
= favicon_link_tag favicon, id: 'favicon', data: { original_href: favicon }, type: 'image/png'
- = render 'layouts/startup_css', { startup_filename: local_assigns.fetch(:startup_filename, nil) }
+ - if startup_css_enabled?
+ = render 'layouts/startup_css', { startup_filename: local_assigns.fetch(:startup_filename, nil) }
+ - else
+ - diffs_colors = user_diffs_colors
+ = stylesheet_link_tag "themes/#{user_application_theme_css_filename}" if user_application_theme_css_filename
+ = render 'layouts/diffs_colors_css', diffs_colors if diffs_colors.present? || request.path == profile_preferences_path
+
- if user_application_theme == 'gl-dark'
%meta{ name: 'color-scheme', content: 'dark light' }
= stylesheet_link_tag_defer "application_dark"
@@ -31,16 +37,22 @@
= stylesheet_link_tag "disable_animations", media: "all" if Rails.env.test? || Gitlab.config.gitlab['disable_animations']
= stylesheet_link_tag "test_environment", media: "all" if Rails.env.test?
+ = stylesheet_link_tag_defer "fonts" if use_new_fonts?
+
= stylesheet_link_tag_defer "highlight/themes/#{user_color_scheme}"
- = render 'layouts/startup_css_activation'
+ - if startup_css_enabled?
+ = render 'layouts/startup_css_activation'
= stylesheet_link_tag 'performance_bar' if performance_bar_enabled?
= Gon::Base.render_data(nonce: content_security_policy_nonce)
= render_if_exists 'layouts/header/translations'
- = webpack_bundle_tag "sentry" if Gitlab.config.sentry.enabled
+ - if Feature.enabled?(:enable_new_sentry_clientside_integration, current_user) && Gitlab::CurrentSettings.sentry_enabled
+ = webpack_bundle_tag 'sentry'
+ - elsif Gitlab.config.sentry.enabled
+ = webpack_bundle_tag 'legacy_sentry'
= webpack_bundle_tag 'performance_bar' if performance_bar_enabled?
= yield :page_specific_javascripts
diff --git a/app/views/layouts/_loading_hints.html.haml b/app/views/layouts/_loading_hints.html.haml
index b3bb474ea43..b1d1447ae2a 100644
--- a/app/views/layouts/_loading_hints.html.haml
+++ b/app/views/layouts/_loading_hints.html.haml
@@ -13,3 +13,9 @@
= preload_link_tag(path_to_stylesheet("highlight/themes/#{user_color_scheme}"), crossorigin: css_crossorigin)
- if Gitlab::Tracking.enabled? && Gitlab::Tracking.collector_hostname
%link{ rel: 'preconnect', href: "https://#{Gitlab::Tracking.collector_hostname}", crossorigin: '' }
+ - if use_new_fonts?
+ -# Do not use preload_link_tag for fonts, to work around Firefox double-fetch bug.
+ -# See https://github.com/web-platform-tests/wpt/pull/36930
+ %link{ rel: 'preload', href: font_path('gitlab-sans/GitLabSans.woff2'), as: 'font', crossorigin: css_crossorigin }
+ %link{ rel: 'preload', href: font_path('jetbrains-mono/JetBrainsMono.woff2'), as: 'font', crossorigin: css_crossorigin }
+ = preload_link_tag(path_to_stylesheet('fonts'), crossorigin: css_crossorigin)
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index d668399b408..bb1d051f71f 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -14,6 +14,7 @@
= dispensable_render "layouts/header/registration_enabled_callout"
= dispensable_render "layouts/nav/classification_level_banner"
= yield :flash_message
+ = dispensable_render "shared/gitlab_version/security_patch_upgrade_alert"
= dispensable_render "shared/service_ping_consent"
= dispensable_render_if_exists "layouts/header/ee_subscribable_banner"
= dispensable_render_if_exists "layouts/header/seat_count_alert"
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index 0350dc82e46..daf2c582de2 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -29,7 +29,7 @@
= hidden_field_tag :scope, search_context.scope
= hidden_field_tag :search_code, search_context.code_search?
- - ref = search_context.ref if can?(current_user, :download_code, search_context.project)
+ - ref = search_context.ref if can?(current_user, :read_code, search_context.project)
= hidden_field_tag :snippets, search_context.for_snippets?
= hidden_field_tag :repository_ref, ref
= hidden_field_tag :nav_source, 'navbar'
diff --git a/app/views/layouts/group_settings.html.haml b/app/views/layouts/group_settings.html.haml
index c4e5e811280..60eeb9a4602 100644
--- a/app/views/layouts/group_settings.html.haml
+++ b/app/views/layouts/group_settings.html.haml
@@ -1,5 +1,6 @@
- page_title _("Settings")
- nav "group"
+- add_page_specific_style 'page_bundles/settings'
- enable_search_settings locals: { container_class: 'gl-my-5' }
= render template: "layouts/group"
diff --git a/app/views/layouts/header/_current_user_dropdown.html.haml b/app/views/layouts/header/_current_user_dropdown.html.haml
index 00e7a0567da..8363d424c1b 100644
--- a/app/views/layouts/header/_current_user_dropdown.html.haml
+++ b/app/views/layouts/header/_current_user_dropdown.html.haml
@@ -46,6 +46,10 @@
%li.d-md-none
= link_to _("Switch to GitLab Next"), Gitlab::Saas.canary_toggle_com_url
+ - if Feature.enabled?(:super_sidebar_nav, current_user)
+ %li.divider
+ .js-new-nav-toggle{ data: { enabled: current_user.use_new_navigation.to_s, endpoint: profile_preferences_url} }
+
- if current_user_menu?(:sign_out)
%li.divider
%li
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 47d8f5a447f..558af352ae9 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -54,7 +54,7 @@
= sprite_icon('issues')
- issues_count = assigned_issuables_count(:issues)
= gl_badge_tag({ size: :sm, variant: :success }, { class: "gl-ml-n2 #{'gl-display-none' if issues_count == 0}", "aria-label": n_("%d assigned issue", "%d assigned issues", issues_count) % issues_count }) do
- = number_with_delimiter(issues_count)
+ = assigned_open_issues_count_text
- if header_link?(:merge_requests)
= nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter dropdown" }) do
- top_level_link = assigned_mrs_dashboard_path
@@ -119,12 +119,12 @@
= render 'layouts/header/current_user_dropdown'
- if has_impersonation_link
%li.nav-item.impersonation.ml-0
- = link_to admin_impersonation_path, class: 'nav-link impersonation-btn', method: :delete, title: _('Stop impersonation'), aria: { label: _('Stop impersonation') }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body', qa_selector: 'stop_impersonation_link' } do
- = sprite_icon('incognito', size: 18)
+ = render Pajamas::ButtonComponent.new(href: admin_impersonation_path, icon: 'incognito', button_options: { title: _('Stop impersonation'), class: 'impersonation-btn', aria: { label: _('Stop impersonation') }, data: { method: :delete, toggle: 'tooltip', placement: 'bottom', container: 'body', qa_selector: 'stop_impersonation_link' } })
- if header_link?(:sign_in)
- if Gitlab.com?
%li.nav-item.gl-display-none.gl-sm-display-block
- = link_to _('Sign up now'), new_user_registration_path, class: 'gl-button btn btn-default btn-sign-in'
+ = render Pajamas::ButtonComponent.new(href: new_user_registration_path) do
+ = _('Sign up now')
%li.nav-item.gl-display-none.gl-sm-display-block
= link_to _('Login'), new_session_path(:user, redirect_to_referer: 'yes')
= render 'layouts/header/sign_in_register_button', class: 'gl-sm-display-none'
diff --git a/app/views/layouts/header/_gitlab_version.html.haml b/app/views/layouts/header/_gitlab_version.html.haml
index 2315caa5fe8..581d4d498e1 100644
--- a/app/views/layouts/header/_gitlab_version.html.haml
+++ b/app/views/layouts/header/_gitlab_version.html.haml
@@ -17,4 +17,4 @@
%span.gl-font-sm.gl-text-gray-500
#{Gitlab.version_info.major}.#{Gitlab.version_info.minor}
%span.gl-ml-2
- .js-gitlab-version-check-badge{ data: { "size": "sm" } }
+ .js-gitlab-version-check-badge{ data: { "size": "sm", "version": gitlab_version_check.to_json } }
diff --git a/app/views/layouts/header/_marketing_links.html.haml b/app/views/layouts/header/_marketing_links.html.haml
index 24069de394d..c33229e4ec4 100644
--- a/app/views/layouts/header/_marketing_links.html.haml
+++ b/app/views/layouts/header/_marketing_links.html.haml
@@ -6,29 +6,29 @@
.dropdown-menu
%ul
%li
- = link_to 'https://about.gitlab.com/stages-devops-lifecycle/' do
+ = link_to Gitlab::Utils.append_path(promo_url, 'stages-devops-lifecycle') do
= s_('LoggedOutMarketingHeader|GitLab: the DevOps platform')
%li
= link_to explore_root_path do
= s_('LoggedOutMarketingHeader|Explore GitLab')
%li
- = link_to 'https://about.gitlab.com/install/' do
+ = link_to Gitlab::Utils.append_path(promo_url, 'install') do
= s_('LoggedOutMarketingHeader|Install GitLab')
%li
- = link_to 'https://about.gitlab.com/is-it-any-good/' do
+ = link_to Gitlab::Utils.append_path(promo_url, 'is-it-any-good') do
= s_('LoggedOutMarketingHeader|How GitLab compares')
%li
- = link_to 'https://about.gitlab.com/get-started/' do
+ = link_to Gitlab::Utils.append_path(promo_url, 'get-started') do
= s_('LoggedOutMarketingHeader|Get started')
%li
- = link_to 'https://docs.gitlab.com/' do
+ = link_to Gitlab::Saas::doc_url do
= s_('LoggedOutMarketingHeader|GitLab docs')
%li
- = link_to 'https://about.gitlab.com/learn/' do
+ = link_to Gitlab::Utils.append_path(promo_url, 'learn') do
= s_('LoggedOutMarketingHeader|GitLab Learn')
%li.gl-mr-3
- = link_to 'https://about.gitlab.com/pricing/' do
+ = link_to Gitlab::Utils.append_path(promo_url, 'pricing') do
= s_('LoggedOutMarketingHeader|Pricing')
%li.gl-mr-3
- = link_to 'https://about.gitlab.com/sales/' do
+ = link_to Gitlab::Utils.append_path(promo_url, 'sales') do
= s_('LoggedOutMarketingHeader|Talk to an expert')
diff --git a/app/views/layouts/header/_registration_enabled_callout.html.haml b/app/views/layouts/header/_registration_enabled_callout.html.haml
index dd3d14a5678..52c39fce961 100644
--- a/app/views/layouts/header/_registration_enabled_callout.html.haml
+++ b/app/views/layouts/header/_registration_enabled_callout.html.haml
@@ -1,17 +1,17 @@
- return unless show_registration_enabled_user_callout?
-= render Pajamas::AlertComponent.new(title: _('Anyone can register for an account.'),
+= render Pajamas::AlertComponent.new(title: _('Check your sign-up restrictions'),
variant: :warning,
alert_options: { class: 'js-registration-enabled-callout',
data: { feature_id: Users::CalloutsHelper::REGISTRATION_ENABLED_CALLOUT,
dismiss_endpoint: callouts_path }},
close_button_options: { data: { testid: 'close-registration-enabled-callout' }}) do |c|
= c.body do
- = _('Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable.')
+ = _("Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account.")
= c.actions do
= link_to general_admin_application_settings_path(anchor: 'js-signup-settings'), class: 'btn gl-alert-action btn-confirm btn-md gl-button' do
%span.gl-button-text
- = _('Turn off')
+ = _('Deactivate')
%button.btn.gl-alert-action.btn-default.btn-md.gl-button.js-close
%span.gl-button-text
= _('Acknowledge')
diff --git a/app/views/layouts/header/_sign_in_register_button.html.haml b/app/views/layouts/header/_sign_in_register_button.html.haml
index 992e8785251..cadb7cfe683 100644
--- a/app/views/layouts/header/_sign_in_register_button.html.haml
+++ b/app/views/layouts/header/_sign_in_register_button.html.haml
@@ -3,4 +3,5 @@
%li.nav-item{ class: top_class }
%div
- sign_in_text = allow_signup? ? _('Sign in / Register') : _('Sign in')
- = link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'gl-button btn btn-default btn-sign-in'
+ = render Pajamas::ButtonComponent.new(href: new_session_path(:user, redirect_to_referer: 'yes'), button_options: { class: 'btn-sign-in'}) do
+ = sign_in_text
diff --git a/app/views/layouts/jira_connect.html.haml b/app/views/layouts/jira_connect.html.haml
index 6acd7799875..80bbe578510 100644
--- a/app/views/layouts/jira_connect.html.haml
+++ b/app/views/layouts/jira_connect.html.haml
@@ -5,7 +5,7 @@
GitLab
= yield :page_specific_styles
- = javascript_include_tag 'https://connect-cdn.atl-paas.net/all.js'
+ = javascript_include_tag Gitlab.config.jira_connect.atlassian_js_url
= Gon::Base.render_data(nonce: content_security_policy_nonce)
= yield :head
%body
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index 8815dec5a6b..717175e8eb3 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -14,7 +14,7 @@
%span.nav-item-name
= _('Overview')
%ul.sidebar-sub-level-items
- = nav_link(controller: %w[dashboard admin admin/projects users groups jobs runners gitaly_servers cohorts], html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: %w[dashboard admin admin/projects users groups jobs runners gitaly_servers cohorts], html_options: { class: "fly-out-top-item" }) do
= link_to admin_root_path do
%strong.fly-out-top-item-name
= _('Overview')
@@ -82,7 +82,7 @@
= _('Monitoring')
%ul.sidebar-sub-level-items{ data: { qa_selector: 'admin_monitoring_submenu_content' } }
- = nav_link(controller: admin_monitoring_nav_links, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: admin_monitoring_nav_links, html_options: { class: "fly-out-top-item" }) do
= link_to admin_system_info_path do
%strong.fly-out-top-item-name
= _('Monitoring')
@@ -117,7 +117,7 @@
%span.nav-item-name
= _('Messages')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :broadcast_messages, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: :broadcast_messages, html_options: { class: "fly-out-top-item" }) do
= link_to admin_broadcast_messages_path do
%strong.fly-out-top-item-name
= _('Messages')
@@ -129,7 +129,7 @@
%span.nav-item-name
= _('System Hooks')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: [:hooks, :hook_logs], html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: [:hooks, :hook_logs], html_options: { class: "fly-out-top-item" }) do
= link_to admin_hooks_path do
%strong.fly-out-top-item-name
= _('System Hooks')
@@ -141,7 +141,7 @@
%span.nav-item-name
= _('Applications')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :applications, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: :applications, html_options: { class: "fly-out-top-item" }) do
= link_to admin_applications_path do
%strong.fly-out-top-item-name
= _('Applications')
@@ -154,7 +154,7 @@
= _('Abuse Reports')
= gl_badge_tag number_with_delimiter(AbuseReport.count(:all)), variant: :info, size: :sm
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :abuse_reports, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: :abuse_reports, html_options: { class: "fly-out-top-item" }) do
= link_to admin_abuse_reports_path do
%strong.fly-out-top-item-name
= _('Abuse Reports')
@@ -170,7 +170,7 @@
%span.nav-item-name
= _('Kubernetes')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :clusters, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: :clusters, html_options: { class: "fly-out-top-item" }) do
= link_to admin_clusters_path do
%strong.fly-out-top-item-name
= _('Kubernetes')
@@ -183,7 +183,7 @@
%span.nav-item-name
= _('Spam Logs')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :spam_logs, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: :spam_logs, html_options: { class: "fly-out-top-item" }) do
= link_to admin_spam_logs_path do
%strong.fly-out-top-item-name
= _('Spam Logs')
@@ -199,7 +199,7 @@
%span.nav-item-name
= _('Deploy Keys')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :deploy_keys, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: :deploy_keys, html_options: { class: "fly-out-top-item" }) do
= link_to admin_deploy_keys_path do
%strong.fly-out-top-item-name
= _('Deploy Keys')
@@ -213,7 +213,7 @@
%span.nav-item-name
= _('Labels')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :labels, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: :labels, html_options: { class: "fly-out-top-item" }) do
= link_to admin_labels_path do
%strong.fly-out-top-item-name
= _('Labels')
@@ -227,7 +227,7 @@
%ul.sidebar-sub-level-items{ data: { qa_selector: 'admin_settings_submenu_content' } }
-# This active_nav_link check is also used in `app/views/layouts/admin.html.haml`
- = nav_link(controller: [:application_settings, :integrations, :appearances], html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: [:application_settings, :integrations, :appearances], html_options: { class: "fly-out-top-item" }) do
= link_to general_admin_application_settings_path do
%strong.fly-out-top-item-name
= _('Settings')
@@ -273,7 +273,7 @@
= link_to network_admin_application_settings_path, title: _('Network'), data: { qa_selector: 'admin_settings_network_link' } do
%span
= _('Network')
- = nav_link(controller: :appearances ) do
+ = nav_link(controller: :appearances) do
= link_to admin_application_settings_appearances_path do
%span
= _('Appearance')
diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml
index 0e3327935ca..e1978009114 100644
--- a/app/views/layouts/nav/sidebar/_profile.html.haml
+++ b/app/views/layouts/nav/sidebar/_profile.html.haml
@@ -12,7 +12,7 @@
%span.nav-item-name
= _('Profile')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(path: 'profiles#show', html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(path: 'profiles#show', html_options: { class: "fly-out-top-item" }) do
= link_to profile_path do
%strong.fly-out-top-item-name
= _('Profile')
@@ -23,7 +23,7 @@
%span.nav-item-name
= _('Account')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: [:accounts, :two_factor_auths], html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: [:accounts, :two_factor_auths], html_options: { class: "fly-out-top-item" }) do
= link_to profile_account_path do
%strong.fly-out-top-item-name
= _('Account')
@@ -36,7 +36,7 @@
%span.nav-item-name
= _('Applications')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: 'oauth/applications', html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: 'oauth/applications', html_options: { class: "fly-out-top-item" }) do
= link_to applications_profile_path do
%strong.fly-out-top-item-name
= _('Applications')
@@ -47,7 +47,7 @@
%span.nav-item-name
= _('Chat')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :chat_names, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: :chat_names, html_options: { class: "fly-out-top-item" }) do
= link_to profile_chat_names_path do
%strong.fly-out-top-item-name
= _('Chat')
@@ -59,7 +59,7 @@
%span.nav-item-name
= _('Access Tokens')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :personal_access_tokens, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: :personal_access_tokens, html_options: { class: "fly-out-top-item" }) do
= link_to profile_personal_access_tokens_path do
%strong.fly-out-top-item-name
= _('Access Tokens')
@@ -70,7 +70,7 @@
%span.nav-item-name
= _('Emails')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :emails, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: :emails, html_options: { class: "fly-out-top-item" }) do
= link_to profile_emails_path do
%strong.fly-out-top-item-name
= _('Emails')
@@ -82,7 +82,7 @@
%span.nav-item-name
= _('Password')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :passwords, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: :passwords, html_options: { class: "fly-out-top-item" }) do
= link_to edit_profile_password_path do
%strong.fly-out-top-item-name
= _('Password')
@@ -93,7 +93,7 @@
%span.nav-item-name
= _('Notifications')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :notifications, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: :notifications, html_options: { class: "fly-out-top-item" }) do
= link_to profile_notifications_path do
%strong.fly-out-top-item-name
= _('Notifications')
@@ -104,7 +104,7 @@
%span.nav-item-name
= _('SSH Keys')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :keys, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: :keys, html_options: { class: "fly-out-top-item" }) do
= link_to profile_keys_path do
%strong.fly-out-top-item-name
= _('SSH Keys')
@@ -115,7 +115,7 @@
%span.nav-item-name
= _('GPG Keys')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :gpg_keys, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: :gpg_keys, html_options: { class: "fly-out-top-item" }) do
= link_to profile_gpg_keys_path do
%strong.fly-out-top-item-name
= _('GPG Keys')
@@ -126,7 +126,7 @@
%span.nav-item-name
= _('Preferences')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :preferences, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: :preferences, html_options: { class: "fly-out-top-item" }) do
= link_to profile_preferences_path do
%strong.fly-out-top-item-name
= _('Preferences')
@@ -137,7 +137,7 @@
%span.nav-item-name
= _('Active Sessions')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :active_sessions, html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: :active_sessions, html_options: { class: "fly-out-top-item" }) do
= link_to profile_active_sessions_path do
%strong.fly-out-top-item-name
= _('Active Sessions')
@@ -148,7 +148,7 @@
%span.nav-item-name
= _('Authentication log')
%ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(path: 'profiles#audit_log', html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(path: 'profiles#audit_log', html_options: { class: "fly-out-top-item" }) do
= link_to audit_log_profile_path do
%strong.fly-out-top-item-name
= _('Authentication Log')
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index a06f9f8d6ef..67c3cd9cc54 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -1 +1 @@
-= render partial: 'shared/nav/sidebar', object: Sidebars::Projects::Panel.new(project_sidebar_context(@project, current_user, current_ref))
+= render partial: 'shared/nav/sidebar', object: Sidebars::Projects::Panel.new(project_sidebar_context(@project, current_user, current_ref, ref_type: @ref_type))
diff --git a/app/views/layouts/project_settings.html.haml b/app/views/layouts/project_settings.html.haml
index 97d9f2fbc78..29e30c4434f 100644
--- a/app/views/layouts/project_settings.html.haml
+++ b/app/views/layouts/project_settings.html.haml
@@ -1,5 +1,6 @@
- page_title _("Settings")
- nav "project"
+- add_page_specific_style 'page_bundles/settings'
- enable_search_settings locals: { container_class: 'gl-my-5' }
diff --git a/app/views/layouts/search.html.haml b/app/views/layouts/search.html.haml
index dd4b9e45207..44c4b14e90d 100644
--- a/app/views/layouts/search.html.haml
+++ b/app/views/layouts/search.html.haml
@@ -1,4 +1,5 @@
- page_title _("Search")
- header_title _("Search"), search_path
+- add_page_specific_style 'page_bundles/search'
= render template: "layouts/application"
diff --git a/app/views/notify/_reassigned_issuable_email.html.haml b/app/views/notify/_reassigned_issuable_email.html.haml
index 54e51e07c86..ead8e5d0a7e 100644
--- a/app/views/notify/_reassigned_issuable_email.html.haml
+++ b/app/views/notify/_reassigned_issuable_email.html.haml
@@ -1,4 +1,4 @@
-- to_names = content_tag(:strong, issuable.assignees.any? ? sanitize_name(issuable.assignee_list) : s_('Unassigned'))
+- to_names = content_tag(:strong, issuable.assignees.any? ? sanitize_name(issuable.assignee_list) : _('Unassigned'))
%p
- if previous_assignees.any?
diff --git a/app/views/notify/access_token_revoked_email.html.haml b/app/views/notify/access_token_revoked_email.html.haml
index 4d9b9e14d14..ecd2b3e84b2 100644
--- a/app/views/notify/access_token_revoked_email.html.haml
+++ b/app/views/notify/access_token_revoked_email.html.haml
@@ -2,6 +2,8 @@
= _('Hi %{username}!') % { username: sanitize_name(@user.name) }
%p
= html_escape(_('A personal access token, named %{code_start}%{token_name}%{code_end}, has been revoked.')) % { code_start: '<code>'.html_safe, token_name: @token_name, code_end: '</code>'.html_safe }
+- if @source == 'secret_detection'
+ = _('We found your token in a public project and have automatically revoked it to protect your account.')
%p
- pat_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: @target_url }
= html_escape(_('You can check your tokens or create a new one in your %{pat_link_start}personal access tokens settings%{pat_link_end}.')) % { pat_link_start: pat_link_start, pat_link_end: '</a>'.html_safe }
diff --git a/app/views/notify/access_token_revoked_email.text.erb b/app/views/notify/access_token_revoked_email.text.erb
index 17dd628d76c..a0623f96488 100644
--- a/app/views/notify/access_token_revoked_email.text.erb
+++ b/app/views/notify/access_token_revoked_email.text.erb
@@ -1,5 +1,9 @@
<%= _('Hi %{username}!') % { username: sanitize_name(@user.name) } %>
<%= _('A personal access token, named %{token_name}, has been revoked.') % { token_name: @token_name } %>
+<% if @source == 'secret_detection' %>
+
+<%= _('We found your token in a public project and have automatically revoked it to protect your account.') %>
+<% end %>
<%= _('You can check your tokens or create a new one in your personal access tokens settings %{pat_link}.') % { pat_link: @target_url } %>
diff --git a/app/views/notify/autodevops_disabled_email.html.haml b/app/views/notify/autodevops_disabled_email.html.haml
index bdf2a1136d3..d6812821966 100644
--- a/app/views/notify/autodevops_disabled_email.html.haml
+++ b/app/views/notify/autodevops_disabled_email.html.haml
@@ -11,7 +11,7 @@
- link_style = "color: #1b69b6; text-decoration:none;"
- pipeline_link = link_to("\##{@pipeline.iid}", pipeline_url(@pipeline), style: link_style).html_safe
- project_link = link_to(@project.name, project_url(@project), style: link_style).html_safe
- - supported_langs_link = link_to(s_('Notify|currently supported languages'), 'https://docs.gitlab.com/ee/topics/autodevops/#currently-supported-languages', style: link_style ).html_safe
+ - supported_langs_link = link_to(s_('Notify|currently supported languages'), 'https://docs.gitlab.com/ee/topics/autodevops/#currently-supported-languages', style: link_style).html_safe
- settings_link = link_to(s_('Notify|CI/CD project settings'), project_settings_ci_cd_url(@project), style: link_style).html_safe
= s_('Notify|The Auto DevOps pipeline failed for pipeline %{pipeline_link} and has been disabled for %{project_link}. In order to use the Auto DevOps pipeline with your project, please review the %{supported_langs_link}, adjust your project accordingly, and turn on the Auto DevOps pipeline within your %{settings_link}.').html_safe % { pipeline_link: pipeline_link, project_link: project_link, supported_langs_link: supported_langs_link, settings_link: settings_link }
diff --git a/app/views/notify/issue_moved_email.html.haml b/app/views/notify/issue_moved_email.html.haml
index c77a863d1a4..666aa45540e 100644
--- a/app/views/notify/issue_moved_email.html.haml
+++ b/app/views/notify/issue_moved_email.html.haml
@@ -2,6 +2,6 @@
= s_('Notify|Issue was moved to another project.')
- if @can_access_project
%p
- = sprintf(s_('Notify|New issue: %{project_issue_url}'), { project_issue_url: link_to(@new_issue.title, project_issue_url(@new_project, @new_issue)) } ).html_safe
+ = sprintf(s_('Notify|New issue: %{project_issue_url}'), { project_issue_url: link_to(@new_issue.title, project_issue_url(@new_project, @new_issue)) }).html_safe
- else
= s_("Notify|You don't have access to the project.")
diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml
index ee219914513..d493f9d5d98 100644
--- a/app/views/notify/repository_push_email.html.haml
+++ b/app/views/notify/repository_push_email.html.haml
@@ -60,7 +60,7 @@
- if diff_file.deleted_file?
%strong<
= diff_file.old_path
- = s_('deleted')
+ = _('deleted')
- elsif diff_file.renamed_file?
%strong<
= diff_file.old_path
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index cdd5a9ae7a1..bc0d615bb64 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -24,10 +24,12 @@
%p
#{_('Status')}: #{current_user.two_factor_enabled? ? _('Enabled') : _('Disabled')}
- if current_user.two_factor_enabled?
- = link_to _('Manage two-factor authentication'), profile_two_factor_auth_path, class: 'gl-button btn btn-confirm'
+ = render Pajamas::ButtonComponent.new(variant: :confirm, href: profile_two_factor_auth_path) do
+ = _('Manage two-factor authentication')
- else
.gl-mb-3
- = link_to _('Enable two-factor authentication'), profile_two_factor_auth_path, class: 'gl-button btn btn-confirm', data: { qa_selector: 'enable_2fa_button' }
+ = render Pajamas::ButtonComponent.new(variant: :confirm, href: profile_two_factor_auth_path, button_options: { data: { qa_selector: 'enable_2fa_button' }}) do
+ = _('Enable two-factor authentication')
.col-lg-12
%hr
diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml
index e6d91543585..5c4ea7b2ecb 100644
--- a/app/views/profiles/keys/_form.html.haml
+++ b/app/views/profiles/keys/_form.html.haml
@@ -12,7 +12,11 @@
= f.label :title, s_('Profiles|Title'), class: 'label-bold'
= f.text_field :title, class: "form-control gl-form-input input-lg", required: true, placeholder: s_('Profiles|Example: MacBook key'), data: { qa_selector: 'key_title_field' }
%p.form-text.text-muted= s_('Profiles|Key titles are publicly visible.')
-
+ .form-row
+ .col.form-group
+ = f.label :usage_type, s_('Profiles|Usage type')
+ .gl-md-form-input-lg
+ = f.select :usage_type, options_for_select(ssh_key_usage_types, :auth_and_signing), {}, { class: 'gl-form-select custom-select' }
.form-row
.col.form-group
.js-access-tokens-expires-at{ data: {min_date: Date.tomorrow, max_date: max_date, default_date_offset: 365, description: ssh_key_expires_field_description } }
diff --git a/app/views/profiles/keys/_key.html.haml b/app/views/profiles/keys/_key.html.haml
index de4a19bdad7..219e7c4d2fe 100644
--- a/app/views/profiles/keys/_key.html.haml
+++ b/app/views/profiles/keys/_key.html.haml
@@ -25,7 +25,10 @@
%span.expires.gl-mr-3
= key.expired? ? s_('Profiles|Expired:') : s_('Profiles|Expires:')
= key.expires_at ? key.expires_at.to_date : _('Never')
+ %span.last-used-at.gl-mr-3
+ = s_('Profiles|Usage type:')
+ = ssh_key_usage_types.invert[key.usage_type]
%span.key-created-at.gl-display-flex.gl-align-items-center
- if key.can_delete?
.gl-ml-3
- = render 'shared/ssh_keys/key_delete', html_class: "btn gl-button btn-icon btn-default js-confirm-modal-button", button_data: ssh_key_delete_modal_data(key, path_to_key(key, is_admin))
+ = render 'shared/ssh_keys/key_delete', icon: true, button_data: ssh_key_delete_modal_data(key, path_to_key(key, is_admin))
diff --git a/app/views/profiles/keys/_key_details.html.haml b/app/views/profiles/keys/_key_details.html.haml
index 04fa1d96204..3c05502be57 100644
--- a/app/views/profiles/keys/_key_details.html.haml
+++ b/app/views/profiles/keys/_key_details.html.haml
@@ -10,6 +10,9 @@
%span.light= _('Title:')
%strong= @key.title
%li
+ %span.light= s_('Profiles|Usage type:')
+ %strong= ssh_key_usage_types.invert[@key.usage_type]
+ %li
%span.light= _('Created on:')
%strong= @key.created_at.to_s(:medium)
%li
@@ -39,4 +42,4 @@
.col-md-12
.float-right
- if @key.can_delete?
- = render 'shared/ssh_keys/key_delete', text: _('Delete'), html_class: "btn btn-danger gl-button delete-key js-confirm-modal-button", button_data: ssh_key_delete_modal_data(@key, path_to_key(@key, is_admin))
+ = render 'shared/ssh_keys/key_delete', button_data: ssh_key_delete_modal_data(@key, path_to_key(@key, is_admin))
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index a1d6ef3fec5..24ef9cf4dec 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -73,7 +73,7 @@
.form-group
= f.label :layout, class: 'label-bold' do
= s_('Preferences|Layout width')
- = f.select :layout, layout_choices, {}, class: 'select2'
+ = f.select :layout, layout_choices, {}, class: 'gl-form-select custom-select'
.form-text.text-muted
= s_('Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout.').html_safe % { percentage: '100%' }
.form-group
@@ -88,7 +88,7 @@
.form-group
= f.label :project_view, class: 'label-bold' do
= s_('Preferences|Project overview content')
- = f.select :project_view, project_view_choices, {}, class: 'select2'
+ = f.select :project_view, project_view_choices, {}, class: 'gl-form-select custom-select'
.form-text.text-muted
= s_('Preferences|Choose what content you want to see on a project’s overview page.')
.form-group
@@ -103,7 +103,7 @@
- supported_characters = %w(" ' ` &#40; [ { < * _).map { |char| "<code>#{char}</code>" }.join(', ')
= f.gitlab_ui_checkbox_component :markdown_surround_selection,
s_('Preferences|Surround text selection when typing quotes or brackets'),
- help_text: sprintf(s_( "Preferences|When you type in a description or comment box, selected text is surrounded by the corresponding character after typing one of the following characters: %{supported_characters}."), { supported_characters: supported_characters }).html_safe
+ help_text: sprintf(s_("Preferences|When you type in a description or comment box, selected text is surrounded by the corresponding character after typing one of the following characters: %{supported_characters}."), { supported_characters: supported_characters }).html_safe
.form-group
= f.gitlab_ui_checkbox_component :markdown_automatic_lists,
s_('Preferences|Automatically add new list items'),
@@ -144,9 +144,24 @@
.form-group
= f.label :first_day_of_week, class: 'label-bold' do
= _('First day of the week')
- = f.select :first_day_of_week, first_day_of_week_choices_with_default, {}, class: 'select2'
+ = f.select :first_day_of_week, first_day_of_week_choices_with_default, {}, class: 'gl-form-select custom-select'
.col-sm-12
%hr
+ - if Feature.enabled?(:vscode_web_ide, current_user)
+ .row.js-preferences-form.js-search-settings-section
+ .col-lg-4.profile-settings-sidebar#web-ide
+ %h4.gl-mt-0
+ = s_('Preferences|Web IDE')
+ %p
+ = s_('Preferences|The Web IDE Beta is the default Web IDE experience.')
+ = link_to _('Learn more'), help_page_path('user/project/web_ide_beta/index.md'), target: '_blank', rel: 'noopener noreferrer'
+ .col-lg-8
+ .form-group
+ = f.gitlab_ui_checkbox_component :use_legacy_web_ide,
+ s_('Preferences|Opt out of the Web IDE Beta'),
+ help_text: s_('Preferences|The Web IDE remains available alongside the Beta.')
+ .col-sm-12
+ %hr
.row.js-preferences-form.js-search-settings-section
.col-lg-4.profile-settings-sidebar#time-preferences
%h4.gl-mt-0
diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml
index 51222784847..712d6fabf82 100644
--- a/app/views/projects/_files.html.haml
+++ b/app/views/projects/_files.html.haml
@@ -1,20 +1,23 @@
+- show_auto_devops_callout = show_auto_devops_callout?(@project)
- is_project_overview = local_assigns.fetch(:is_project_overview, false)
- ref = local_assigns.fetch(:ref) { current_ref }
- project = local_assigns.fetch(:project) { @project }
-- show_auto_devops_callout = show_auto_devops_callout?(@project)
- add_page_startup_api_call logs_file_project_ref_path(@project, ref, @path, format: "json", offset: 0)
- if readme_path = @project.repository.readme_path
- add_page_startup_api_call project_blob_path(@project, tree_join(@ref, readme_path), viewer: "rich", format: "json")
#tree-holder.tree-holder.clearfix.js-per-page{ data: { blame_per_page: Projects::BlameService::PER_PAGE } }
- .nav-block.gl-display-flex.gl-xs-flex-direction-column.gl-align-items-stretch
- = render 'projects/tree/tree_header', tree: @tree, is_project_overview: is_project_overview
-
.info-well.gl-display-none.gl-sm-display-flex.project-last-commit.gl-flex-direction-column
#js-last-commit.gl-m-auto
= gl_loading_icon(size: 'md')
#js-code-owners
+ .nav-block.gl-display-flex.gl-xs-flex-direction-column.gl-align-items-stretch
+ = render 'projects/tree/tree_header', tree: @tree, is_project_overview: is_project_overview
+
+ - if project.forked? && Feature.enabled?(:fork_divergence_counts, @project.fork_source)
+ = render 'projects/fork_info'
+
- if is_project_overview
.project-buttons.gl-mb-5.js-show-on-project-root{ data: { qa_selector: 'project_buttons' } }
= render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout), project_buttons: true
diff --git a/app/views/projects/_flash_messages.html.haml b/app/views/projects/_flash_messages.html.haml
index 7395495b537..2d9f7e49ddc 100644
--- a/app/views/projects/_flash_messages.html.haml
+++ b/app/views/projects/_flash_messages.html.haml
@@ -2,11 +2,13 @@
= content_for :flash_message do
= render partial: 'deletion_failed', locals: { project: project }
- - if current_user && can?(current_user, :download_code, project)
+ - if current_user && can?(current_user, :read_code, project)
= render 'shared/no_ssh'
= render 'shared/no_password'
- unless project.empty_repo?
= render 'shared/auto_devops_implicitly_enabled_banner', project: project
+ - if show_auto_devops_callout?(@project)
+ = render 'shared/auto_devops_callout'
= render_if_exists 'projects/above_size_limit_warning', project: project
= render_if_exists 'shared/shared_runners_minutes_limit', project: project, classes: [container_class, ("limit-container-width" unless fluid_layout)]
= render_if_exists 'projects/terraform_banner', project: project
diff --git a/app/views/projects/_fork_info.html.haml b/app/views/projects/_fork_info.html.haml
new file mode 100644
index 00000000000..7fe30214e97
--- /dev/null
+++ b/app/views/projects/_fork_info.html.haml
@@ -0,0 +1,14 @@
+.info-well.gl-sm-display-flex.gl-flex-direction-column
+ .well-segment.gl-p-5.gl-w-full.gl-display-flex
+ .gl-icon.s32.gl-mt-4.gl-mr-4.gl-text-center
+ = sprite_icon('fork')
+ - source = visible_fork_source(@project)
+ - if source
+ %div
+ #{ s_('ForkedFromProjectPath|Forked from') }
+ = link_to source.full_name, project_path(source), data: { qa_selector: 'forked_from_link' }
+ .gl-text-secondary
+ = fork_divergence_message(::Projects::Forks::DivergenceCounts.new(@project, @ref).counts)
+ - else
+ .gl-py-4
+ = s_('ForkedFromProjectPath|Forked from an inaccessible project')
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index a862b841008..dc426f2f6b7 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -4,17 +4,16 @@
- cache_enabled = Feature.enabled?(:cache_home_panel, @project, type: :development)
.project-home-panel.js-show-on-project-root.gl-my-5{ class: [("empty-project" if empty_repo)] }
- .gl-display-flex.gl-justify-content-space-between.gl-flex-wrap.gl-sm-flex-direction-column.gl-mb-3
- .home-panel-title-row.gl-display-flex
+ .gl-display-flex.gl-justify-content-space-between.gl-flex-wrap.gl-sm-flex-direction-column.gl-mb-3.gl-gap-5
+ .home-panel-title-row.gl-display-flex.gl-align-items-center
%div{ class: 'avatar-container rect-avatar s64 home-panel-avatar gl-flex-shrink-0 gl-w-11 gl-h-11 gl-mr-3! float-none' }
= project_icon(@project, alt: @project.name, class: 'avatar avatar-tile s64', width: 64, height: 64, itemprop: 'image')
- .d-flex.flex-column.flex-wrap.align-items-baseline
- .d-inline-flex.align-items-baseline
- %h1.home-panel-title.gl-mt-3.gl-mb-2.gl-font-size-h1{ data: { qa_selector: 'project_name_content' }, itemprop: 'name' }
- = @project.name
- %span.visibility-icon.gl-text-secondary.gl-ml-2.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@project) }
- = visibility_level_icon(@project.visibility_level, options: { class: 'icon' })
- = render_if_exists 'compliance_management/compliance_framework/compliance_framework_badge', project: @project
+ %div
+ %h1.home-panel-title.gl-font-size-h1.gl-mt-3.gl-mb-2.gl-display-flex{ data: { qa_selector: 'project_name_content' }, itemprop: 'name' }
+ = @project.name
+ %span.visibility-icon.gl-text-secondary.has-tooltip.gl-ml-2{ data: { container: 'body' }, title: visibility_icon_description(@project) }
+ = visibility_level_icon(@project.visibility_level, options: { class: 'icon' })
+ = render_if_exists 'compliance_management/compliance_framework/compliance_framework_badge', project: @project, additional_classes: 'gl-align-self-center gl-ml-2'
.home-panel-metadata.gl-font-sm.gl-text-secondary.gl-font-base.gl-font-weight-normal.gl-line-height-normal{ data: { qa_selector: 'project_id_content' }, itemprop: 'identifier' }
- if can?(current_user, :read_project, @project)
%span.gl-display-inline-block.gl-vertical-align-middle
@@ -25,22 +24,20 @@
= render 'shared/members/access_request_links', source: @project
= cache_if(cache_enabled, [@project, @project.star_count, @project.forks_count, :buttons, current_user, @notification_setting], expires_in: 1.day) do
- .project-repo-buttons.gl-display-flex.gl-justify-content-md-end.gl-align-items-start.gl-flex-wrap.gl-mt-5
+ .project-repo-buttons.gl-display-flex.gl-justify-content-md-end.gl-align-items-center.gl-flex-wrap.gl-gap-3
- if current_user
- if current_user.admin?
- = link_to [:admin, @project], class: 'btn gl-button btn-icon gl-align-self-start gl-py-2! gl-mr-3', title: _('View project in admin area'),
+ = link_to [:admin, @project], class: 'btn btn-default gl-button btn-icon', title: _('View project in admin area'),
data: {toggle: 'tooltip', placement: 'top', container: 'body'} do
= sprite_icon('admin')
- .gl-display-flex.gl-align-items-start.gl-mr-3
- - if @notification_setting
- .js-vue-notification-dropdown{ data: { button_size: "small", disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id, no_flip: 'true' } }
+ - if @notification_setting
+ .js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id, no_flip: 'true' } }
- .count-buttons.gl-display-flex.gl-align-items-flex-start
- = render 'projects/buttons/star'
- = render 'projects/buttons/fork'
+ = render 'projects/buttons/star'
+ = render 'projects/buttons/fork'
- - if can?(current_user, :download_code, @project)
- = cache_if(cache_enabled, [@project, :download_code], expires_in: 1.minute) do
+ - if can?(current_user, :read_code, @project)
+ = cache_if(cache_enabled, [@project, :read_code], expires_in: 1.minute) do
%nav.project-stats
- if @project.empty_repo?
= render 'stat_anchor_list', anchors: @project.empty_repo_statistics_anchors
@@ -56,7 +53,7 @@
%button.btn.gl-button.btn-blank.btn-link.js-read-more-trigger.d-lg-none{ type: "button" }
= _("Read more")
- - if @project.forked?
+ - if @project.forked? && Feature.disabled?(:fork_divergence_counts, @project.fork_source)
%p
- source = visible_fork_source(@project)
- if source
diff --git a/app/views/projects/_invite_groups_modal.html.haml b/app/views/projects/_invite_groups_modal.html.haml
index 40dc0009b24..101acd9149e 100644
--- a/app/views/projects/_invite_groups_modal.html.haml
+++ b/app/views/projects/_invite_groups_modal.html.haml
@@ -1,3 +1,3 @@
- return unless can_invite_members_for_project?(project)
-.js-invite-groups-modal{ data: common_invite_group_modal_data(project, ProjectMember, 'true') }
+.js-invite-groups-modal{ data: { reload_page_on_submit: local_assigns.fetch(:reload_page_on_submit, false).to_s }.merge(common_invite_group_modal_data(project, ProjectMember, 'true')) }
diff --git a/app/views/projects/_invite_members_modal.html.haml b/app/views/projects/_invite_members_modal.html.haml
index 16288f4357a..53f74a0f270 100644
--- a/app/views/projects/_invite_members_modal.html.haml
+++ b/app/views/projects/_invite_members_modal.html.haml
@@ -2,4 +2,5 @@
.js-invite-members-modal{ data: { is_project: 'true',
access_levels: ProjectMember.permissible_access_level_roles(current_user, project).to_json,
+ reload_page_on_submit: local_assigns.fetch(:reload_page_on_submit, false).to_s,
help_link: help_page_url('user/permissions') }.merge(common_invite_modal_dataset(project)).merge(users_filter_data(project.group)) }
diff --git a/app/views/projects/_merge_request_merge_checks_settings.html.haml b/app/views/projects/_merge_request_merge_checks_settings.html.haml
index 8c12399fdbb..bb7a7731067 100644
--- a/app/views/projects/_merge_request_merge_checks_settings.html.haml
+++ b/app/views/projects/_merge_request_merge_checks_settings.html.haml
@@ -3,17 +3,6 @@
.form-group
%b= s_('ProjectSettings|Merge checks')
%p.text-secondary= s_('ProjectSettings|These checks must pass before merge requests can be merged.')
- .builds-feature
- = form.gitlab_ui_checkbox_component :only_allow_merge_if_pipeline_succeeds,
- s_('ProjectSettings|Pipelines must succeed'),
- help_text: s_("ProjectSettings|Merge requests can't be merged if the latest pipeline did not succeed or is still running.")
- .gl-pl-6
- = form.gitlab_ui_checkbox_component :allow_merge_on_skipped_pipeline,
- s_('ProjectSettings|Skipped pipelines are considered successful'),
- help_text: s_('ProjectSettings|Introduces the risk of merging changes that do not pass the pipeline.'),
- checkbox_options: { class: 'gl-pl-6' }
+ = render 'projects/merge_request_pipelines_and_threads_options', form: form, project: @project
= render_if_exists 'projects/merge_request_merge_checks_status_checks', form: form, project: @project
- = form.gitlab_ui_checkbox_component :only_allow_merge_if_all_discussions_are_resolved,
- s_('ProjectSettings|All threads must be resolved'),
- checkbox_options: { data: { qa_selector: 'allow_merge_if_all_discussions_are_resolved_checkbox' } }
= render_if_exists 'projects/merge_request_merge_checks_jira_enforcement', form: form, project: @project
diff --git a/app/views/projects/_merge_request_pipelines_and_threads_options.html.haml b/app/views/projects/_merge_request_pipelines_and_threads_options.html.haml
new file mode 100644
index 00000000000..94f8d3cc4a3
--- /dev/null
+++ b/app/views/projects/_merge_request_pipelines_and_threads_options.html.haml
@@ -0,0 +1,13 @@
+- form = local_assigns.fetch(:form)
+
+= form.gitlab_ui_checkbox_component :only_allow_merge_if_pipeline_succeeds,
+ s_('ProjectSettings|Pipelines must succeed'),
+ help_text: s_("ProjectSettings|Merge requests can't be merged if the latest pipeline did not succeed or is still running.")
+.gl-pl-6
+ = form.gitlab_ui_checkbox_component :allow_merge_on_skipped_pipeline,
+ s_('ProjectSettings|Skipped pipelines are considered successful'),
+ help_text: s_('ProjectSettings|Introduces the risk of merging changes that do not pass the pipeline.'),
+ checkbox_options: { class: 'gl-pl-6' }
+= form.gitlab_ui_checkbox_component :only_allow_merge_if_all_discussions_are_resolved,
+ s_('ProjectSettings|All threads must be resolved'),
+ checkbox_options: { data: { qa_selector: 'allow_merge_if_all_discussions_are_resolved_checkbox' } }
diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml
index 0699e39b420..ec83782985b 100644
--- a/app/views/projects/_new_project_fields.html.haml
+++ b/app/views/projects/_new_project_fields.html.haml
@@ -10,6 +10,7 @@
= f.label :name, class: 'label-bold' do
%span= _("Project name")
= f.text_field :name, placeholder: "My awesome project", class: "form-control gl-form-input input-lg", data: { qa_selector: 'project_name', track_label: "#{track_label}", track_action: "activate_form_input", track_property: "project_name", track_value: "" }, required: true, aria: { required: true }
+ #project_name_error.gl-field-error.hidden
.form-group.project-path.col-sm-6.gl-pr-0
= f.label :namespace_id, class: 'label-bold' do
%span= _('Project URL')
@@ -18,6 +19,8 @@
- namespace_id = namespace_id_from(params)
.js-vue-new-project-url-select{ data: { namespace_full_path: GroupFinder.new(current_user).execute(id: namespace_id)&.full_path || @current_user_group&.full_path,
namespace_id: namespace_id || @current_user_group&.id,
+ input_id: 'project_namespace_id',
+ input_name: 'project[namespace_id]',
root_url: root_url,
track_label: track_label,
user_namespace_id: current_user.namespace.id } }
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
index a907e175443..87a6b54d697 100644
--- a/app/views/projects/blob/_editor.html.haml
+++ b/app/views/projects/blob/_editor.html.haml
@@ -28,7 +28,8 @@
.file-buttons.gl-display-flex.gl-align-items-center.gl-justify-content-end
- if is_markdown
- = render 'shared/blob/markdown_buttons', show_fullscreen_button: false, supports_file_upload: false
+ - unless Feature.enabled?(:source_editor_toolbar, current_user)
+ = render 'shared/blob/markdown_buttons', show_fullscreen_button: false, supports_file_upload: false
%span.soft-wrap-toggle
= render Pajamas::ButtonComponent.new(icon: 'soft-unwrap', button_options: { class: 'no-wrap' }) do
= _("No wrap")
diff --git a/app/views/projects/blob/_template_selectors.html.haml b/app/views/projects/blob/_template_selectors.html.haml
index 249c474587c..4fe68c1ce1a 100644
--- a/app/views/projects/blob/_template_selectors.html.haml
+++ b/app/views/projects/blob/_template_selectors.html.haml
@@ -4,12 +4,12 @@
- toggle_text = should_suggest_gitlab_ci_yml? ? '.gitlab-ci.yml' : 'Select a template type'
= dropdown_tag(_(toggle_text), options: { toggle_class: 'js-template-type-selector', dropdown_class: 'dropdown-menu-selectable', data: { qa_selector: 'template_type_dropdown' } })
.license-selector.js-license-selector-wrap.js-template-selector-wrap.hidden
- = dropdown_tag(_("Apply a template"), options: { toggle_class: 'js-license-selector', dropdown_class: 'dropdown-menu-selectable', filter: true, placeholder: "Filter", data: { data: licenses_for_select(@project), project: @project.name, fullname: @project.namespace.human_name, qa_selector: 'license_dropdown' } } )
+ = dropdown_tag(_("Apply a template"), options: { toggle_class: 'js-license-selector', dropdown_class: 'dropdown-menu-selectable', filter: true, placeholder: "Filter", data: { data: licenses_for_select(@project), project: @project.name, fullname: @project.namespace.human_name, qa_selector: 'license_dropdown' } })
.gitignore-selector.js-gitignore-selector-wrap.js-template-selector-wrap.hidden
- = dropdown_tag(_("Apply a template"), options: { toggle_class: 'js-gitignore-selector', dropdown_class: 'dropdown-menu-selectable', filter: true, placeholder: "Filter", data: { data: gitignore_names(@project), qa_selector: 'gitignore_dropdown' } } )
+ = dropdown_tag(_("Apply a template"), options: { toggle_class: 'js-gitignore-selector', dropdown_class: 'dropdown-menu-selectable', filter: true, placeholder: "Filter", data: { data: gitignore_names(@project), qa_selector: 'gitignore_dropdown' } })
.metrics-dashboard-selector.js-metrics-dashboard-selector-wrap.js-template-selector-wrap.hidden
- = dropdown_tag(_("Apply a template"), options: { toggle_class: 'js-metrics-dashboard-selector', dropdown_class: 'dropdown-menu-selectable', filter: true, placeholder: "Filter", data: { data: metrics_dashboard_ymls(@project), qa_selector: 'metrics_dashboard_dropdown' } } )
+ = dropdown_tag(_("Apply a template"), options: { toggle_class: 'js-metrics-dashboard-selector', dropdown_class: 'dropdown-menu-selectable', filter: true, placeholder: "Filter", data: { data: metrics_dashboard_ymls(@project), qa_selector: 'metrics_dashboard_dropdown' } })
#gitlab-ci-yml-selector.gitlab-ci-yml-selector.js-gitlab-ci-yml-selector-wrap.js-template-selector-wrap.hidden
- = dropdown_tag(_("Apply a template"), options: { toggle_class: 'js-gitlab-ci-yml-selector', dropdown_class: 'dropdown-menu-selectable', filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls(@project), selected: params[:template], qa_selector: 'gitlab_ci_yml_dropdown' } } )
+ = dropdown_tag(_("Apply a template"), options: { toggle_class: 'js-gitlab-ci-yml-selector', dropdown_class: 'dropdown-menu-selectable', filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls(@project), selected: params[:template], qa_selector: 'gitlab_ci_yml_dropdown' } })
.dockerfile-selector.js-dockerfile-selector-wrap.js-template-selector-wrap.hidden
- = dropdown_tag(_("Apply a template"), options: { toggle_class: 'js-dockerfile-selector', dropdown_class: 'dropdown-menu-selectable', filter: true, placeholder: "Filter", data: { data: dockerfile_names(@project), qa_selector: 'dockerfile_dropdown' } } )
+ = dropdown_tag(_("Apply a template"), options: { toggle_class: 'js-dockerfile-selector', dropdown_class: 'dropdown-menu-selectable', filter: true, placeholder: "Filter", data: { data: dockerfile_names(@project), qa_selector: 'dockerfile_dropdown' } })
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index 63d0cf7145d..91efd5ef048 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -17,18 +17,12 @@
.form-text.text-muted.text-danger.js-branch-name-error
.form-group.row
= label_tag :ref, _('Create from'), class: 'col-form-label col-sm-2'
- .col-sm-10.create-from
- .dropdown
- = hidden_field_tag :ref, default_ref
- = button_tag type: 'button', title: default_ref, class: 'dropdown-menu-toggle wide js-branch-select monospace', required: true, data: { toggle: 'dropdown', selected: default_ref, field_name: 'ref' } do
- .text-left.dropdown-toggle-text= default_ref
- = sprite_icon('chevron-down', css_class: "dropdown-menu-toggle-icon gl-top-3")
- = render 'shared/ref_dropdown', dropdown_class: 'wide'
+ .col-sm-auto.create-from
+ .js-new-branch-ref-selector{ data: { project_id: @project.id, default_branch_name: default_ref, hidden_input_name: 'ref' } }
.form-text.text-muted
= _('Existing branch name, tag, or commit SHA')
.form-actions
= render Pajamas::ButtonComponent.new(variant: :confirm, button_options: { type: 'submit', class: 'gl-mr-3' }) do
= _('Create branch')
= link_to _('Cancel'), project_branches_path(@project), class: 'gl-button btn btn-default btn-cancel'
--# haml-lint:disable InlineJavaScript
-%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
+
diff --git a/app/views/projects/buttons/_clone.html.haml b/app/views/projects/buttons/_clone.html.haml
index 34aecd31c57..a755cb9f5b0 100644
--- a/app/views/projects/buttons/_clone.html.haml
+++ b/app/views/projects/buttons/_clone.html.haml
@@ -27,28 +27,28 @@
= render_if_exists 'projects/buttons/geo'
= render_if_exists 'projects/buttons/kerberos_clone_field'
%li.divider.mt-2
- %li.pt-2.gl-new-dropdown-item
+ %li.pt-2.gl-dropdown-item
%label.label-bold{ class: 'gl-px-4!' }
= _('Open in your IDE')
- if ssh_enabled?
- escaped_ssh_url_to_repo = CGI.escape(project.ssh_url_to_repo)
%a.dropdown-item.open-with-link{ href: 'vscode://vscode.git/clone?url=' + escaped_ssh_url_to_repo }
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Visual Studio Code (SSH)')
- if http_enabled?
- escaped_http_url_to_repo = CGI.escape(project.http_url_to_repo)
%a.dropdown-item.open-with-link{ href: 'vscode://vscode.git/clone?url=' + escaped_http_url_to_repo }
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Visual Studio Code (HTTPS)')
- if ssh_enabled?
%a.dropdown-item.open-with-link{ href: 'jetbrains://idea/checkout/git?idea.required.plugins.id=Git4Idea&checkout.repo=' + escaped_ssh_url_to_repo }
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('IntelliJ IDEA (SSH)')
- if http_enabled?
%a.dropdown-item.open-with-link{ href: 'jetbrains://idea/checkout/git?idea.required.plugins.id=Git4Idea&checkout.repo=' + escaped_http_url_to_repo }
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('IntelliJ IDEA (HTTPS)')
- if show_xcode_link?(@project)
%a.dropdown-item.open-with-link{ href: xcode_uri_to_repo(@project) }
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _("Xcode")
diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml
index 23dcb7f41e1..1fbc399c3ff 100644
--- a/app/views/projects/buttons/_download.html.haml
+++ b/app/views/projects/buttons/_download.html.haml
@@ -4,7 +4,7 @@
- if !project.empty_repo? && can?(current_user, :download_code, project)
- archive_prefix = "#{project.path}-#{ref.tr('/', '-')}"
- .project-action-button.dropdown.gl-new-dropdown.inline>
+ .project-action-button.dropdown.gl-dropdown.inline>
%button.gl-button.btn.btn-default.dropdown-toggle.gl-dropdown-toggle.dropdown-icon-only.has-tooltip{ title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download'), 'data-display' => 'static', data: { qa_selector: 'download_source_code_button' } }
= sprite_icon('download', css_class: 'gl-icon dropdown-icon')
%span.sr-only= _('Select Archive Format')
diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml
index 3621853430d..97186149a9d 100644
--- a/app/views/projects/buttons/_fork.html.haml
+++ b/app/views/projects/buttons/_fork.html.haml
@@ -2,17 +2,17 @@
- if current_user
.count-badge.btn-group
- if current_user.already_forked?(@project) && current_user.forkable_namespaces.size < 2
- = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: s_('ProjectOverview|Go to your fork'), class: 'gl-button btn btn-default btn-sm has-tooltip fork-btn' do
+ = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: s_('ProjectOverview|Go to your fork'), class: 'gl-button btn btn-default has-tooltip fork-btn' do
= sprite_icon('fork', css_class: 'icon')
%span= s_('ProjectOverview|Fork')
- else
- disabled_tooltip = fork_button_disabled_tooltip(@project)
- - count_class = 'disabled' unless can?(current_user, :download_code, @project)
+ - count_class = 'disabled' unless can?(current_user, :read_code, @project)
- button_class = 'disabled' if disabled_tooltip
%span.btn-group{ class: ('has-tooltip' if disabled_tooltip), title: disabled_tooltip }
- = link_to new_project_fork_path(@project), class: "gl-button btn btn-default btn-sm fork-btn #{button_class}" do
+ = link_to new_project_fork_path(@project), class: "gl-button btn btn-default fork-btn #{button_class}" do
= sprite_icon('fork', css_class: 'icon')
%span= s_('ProjectOverview|Fork')
- = link_to project_forks_path(@project), title: n_(s_('ProjectOverview|Forks'), s_('ProjectOverview|Forks'), @project.forks_count), class: "gl-button btn btn-default btn-sm count has-tooltip fork-count #{count_class}" do
+ = link_to project_forks_path(@project), title: n_(s_('ProjectOverview|Forks'), s_('ProjectOverview|Forks'), @project.forks_count), class: "gl-button btn btn-default count has-tooltip fork-count #{count_class}" do
= @project.forks_count
diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml
index eaf906ad89f..d4dcfbdff54 100644
--- a/app/views/projects/buttons/_star.html.haml
+++ b/app/views/projects/buttons/_star.html.haml
@@ -3,15 +3,15 @@
- icon = starred ? 'star' : 'star-o'
- button_text = starred ? s_('ProjectOverview|Unstar') : s_('ProjectOverview|Star')
- button_text_classes = starred ? 'starred' : ''
- .count-badge.d-inline-flex.align-item-stretch.gl-mr-3.btn-group
- = render Pajamas::ButtonComponent.new(size: :small, icon: icon, button_text_classes: button_text_classes, button_options: { class: 'star-btn toggle-star', data: { endpoint: toggle_star_project_path(@project, :json) } }) do
+ .count-badge.d-inline-flex.align-item-stretch.btn-group
+ = render Pajamas::ButtonComponent.new(size: :medium, icon: icon, button_text_classes: button_text_classes, button_options: { class: 'star-btn toggle-star', data: { endpoint: toggle_star_project_path(@project, :json) } }) do
- button_text
- = link_to project_starrers_path(@project), title: n_(s_('ProjectOverview|Starrer'), s_('ProjectOverview|Starrers'), @project.star_count), class: 'gl-button btn btn-default btn-sm has-tooltip star-count count' do
+ = link_to project_starrers_path(@project), title: n_(s_('ProjectOverview|Starrer'), s_('ProjectOverview|Starrers'), @project.star_count), class: 'gl-button btn btn-default has-tooltip star-count count' do
= @project.star_count
- else
- .count-badge.d-inline-flex.align-item-stretch.gl-mr-3.btn-group
- = link_to new_user_session_path, class: 'gl-button btn btn-default btn-sm has-tooltip star-btn', title: s_('ProjectOverview|You must sign in to star a project') do
+ .count-badge.d-inline-flex.align-item-stretch.btn-group
+ = link_to new_user_session_path, class: 'gl-button btn btn-default has-tooltip star-btn', title: s_('ProjectOverview|You must sign in to star a project') do
= sprite_icon('star-o', css_class: 'icon')
%span= s_('ProjectOverview|Star')
- = link_to project_starrers_path(@project), title: n_(s_('ProjectOverview|Starrer'), s_('ProjectOverview|Starrers'), @project.star_count), class: 'gl-button btn btn-default btn-sm has-tooltip star-count count' do
+ = link_to project_starrers_path(@project), title: n_(s_('ProjectOverview|Starrer'), s_('ProjectOverview|Starrers'), @project.star_count), class: 'gl-button btn btn-default has-tooltip star-count count' do
= @project.star_count
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 6e202063900..079e24c6389 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -53,9 +53,7 @@
= ci_label_for_status(@last_pipeline.status)
- if @last_pipeline.stages_count.nonzero?
#{ n_(s_('Pipeline|with stage'), s_('Pipeline|with stages'), @last_pipeline.stages_count) }
- .mr-widget-pipeline-graph
- .stage-cell
- .js-commit-pipeline-mini-graph{ data: { stages: @last_pipeline_stages.to_json.html_safe, full_path: @project.full_path, iid: @last_pipeline.iid, graphql_resource_etag: graphql_etag_pipeline_path(@last_pipeline) } }
+ .js-commit-pipeline-mini-graph{ data: { stages: @last_pipeline_stages.to_json.html_safe, full_path: @project.full_path, iid: @last_pipeline.iid, graphql_resource_etag: graphql_etag_pipeline_path(@last_pipeline) } }
- if @last_pipeline.duration
in
= time_interval_in_words @last_pipeline.duration
diff --git a/app/views/projects/commit/_signature.html.haml b/app/views/projects/commit/_signature.html.haml
index 978d83bf2b4..c6f1e51049e 100644
--- a/app/views/projects/commit/_signature.html.haml
+++ b/app/views/projects/commit/_signature.html.haml
@@ -1,3 +1,3 @@
- if signature
- - uri = "projects/commit/#{'x509/' if x509_signature?(signature)}"
+ - uri = "projects/commit/#{'x509/' if signature.x509?}"
= render partial: "#{uri}#{signature.verification_status}_signature_badge", locals: { signature: signature }
diff --git a/app/views/projects/commit/_signature_badge.html.haml b/app/views/projects/commit/_signature_badge.html.haml
index fb30bfc2953..ad6b524c01b 100644
--- a/app/views/projects/commit/_signature_badge.html.haml
+++ b/app/views/projects/commit/_signature_badge.html.haml
@@ -17,18 +17,23 @@
- content = capture do
- if show_user
.clearfix
- - uri_signature_badge_user = "projects/commit/#{'x509/' if x509_signature?(signature)}signature_badge_user"
+ - uri_signature_badge_user = "projects/commit/#{'x509/' if signature.x509?}signature_badge_user"
= render partial: "#{uri_signature_badge_user}", locals: { signature: signature }
- - if x509_signature?(signature)
+ - if signature.x509?
= render partial: "projects/commit/x509/certificate_details", locals: { signature: signature }
- = link_to(_('Learn more about X.509 signed commits'), help_page_path('user/project/repository/x509_signed_commits/index.md'), class: 'gpg-popover-help-link')
+ = link_to(_('Learn more about X.509 signed commits'), help_page_path('user/project/repository/x509_signed_commits/index.md'), class: 'gl-link gl-display-block')
+ - elsif ::Feature.enabled?(:ssh_commit_signatures, signature.project) && signature.ssh?
+ = _('SSH key fingerprint:')
+ %span.gl-font-monospace= signature.key&.fingerprint_sha256 || _('Unknown')
+
+ = link_to(_('Learn about signing commits with SSH keys.'), help_page_path('user/project/repository/ssh_signed_commits/index.md'), class: 'gl-link gl-display-block')
- else
= _('GPG Key ID:')
- %span.monospace= signature.gpg_key_primary_keyid
+ %span.gl-font-monospace= signature.gpg_key_primary_keyid
- = link_to(_('Learn more about signing commits'), help_page_path('user/project/repository/gpg_signed_commits/index.md'), class: 'gpg-popover-help-link gl-display-block')
+ = link_to(_('Learn more about signing commits'), help_page_path('user/project/repository/gpg_signed_commits/index.md'), class: 'gl-link gl-display-block')
%a{ role: 'button', tabindex: 0, class: css_classes, data: { toggle: 'popover', html: 'true', placement: 'top', title: title, content: content } }
= label
diff --git a/app/views/projects/commit/_signature_badge_user.html.haml b/app/views/projects/commit/_signature_badge_user.html.haml
index b20198e76db..656adef6a72 100644
--- a/app/views/projects/commit/_signature_badge_user.html.haml
+++ b/app/views/projects/commit/_signature_badge_user.html.haml
@@ -1,7 +1,4 @@
-- gpg_key = signature.gpg_key
-- user = gpg_key&.user
-- user_name = signature.gpg_key_user_name
-- user_email = signature.gpg_key_user_email
+- user = signature.signed_by_user
- if user
= link_to user_path(user), class: 'gpg-popover-user-link' do
@@ -11,11 +8,14 @@
%div
%strong= user.name
%div= user.to_reference
-- else
- = mail_to user_email do
- %div
- = user_avatar_without_link(user_name: user_name, user_email: user_email, size: 32)
+- elsif signature.gpg? # SSH signatures do not have an email embedded in them
+ - user_name = signature.gpg_key_user_name
+ - user_email = signature.gpg_key_user_email
+ - if user_name && user_email
+ = mail_to user_email do
+ %div
+ = user_avatar_without_link(user_name: user_name, user_email: user_email, size: 32)
- %div
- %strong= user_name
- %div= user_email
+ %div
+ %strong= user_name
+ %div= user_email
diff --git a/app/views/projects/commit/x509/_signature_badge_user.html.haml b/app/views/projects/commit/x509/_signature_badge_user.html.haml
index f3d39b21ec2..da749172369 100644
--- a/app/views/projects/commit/x509/_signature_badge_user.html.haml
+++ b/app/views/projects/commit/x509/_signature_badge_user.html.haml
@@ -1,5 +1,5 @@
- user_email = signature.x509_certificate.email
-- user = signature.user
+- user = signature.signed_by_user
- if user
= link_to user_path(user), class: 'gpg-popover-user-link' do
diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml
index b5ecc9b0193..b79f17ae7b3 100644
--- a/app/views/projects/commits/_commits.html.haml
+++ b/app/views/projects/commits/_commits.html.haml
@@ -15,7 +15,7 @@
%li.commits-row{ data: { day: day } }
%ul.content-list.commit-list.flex-list
- if Feature.enabled?(:cached_commits, project)
- = render partial: 'projects/commits/commit', collection: daily_commits, locals: { project: project, ref: ref, merge_request: merge_request }, cached: -> (commit) { commit_partial_cache_key(commit, ref: ref, merge_request: merge_request, request: request) }
+ = render partial: 'projects/commits/commit', collection: daily_commits, locals: { project: project, ref: ref, merge_request: merge_request }, cached: ->(commit) { commit_partial_cache_key(commit, ref: ref, merge_request: merge_request, request: request) }
- else
= render partial: 'projects/commits/commit', collection: daily_commits, locals: { project: project, ref: ref, merge_request: merge_request }
@@ -29,7 +29,7 @@
%li.commits-row
%ul.content-list.commit-list.flex-list
- if Feature.enabled?(:cached_commits, project)
- = render partial: 'projects/commits/commit', collection: context_commits, locals: { project: project, ref: ref, merge_request: merge_request }, cached: -> (commit) { commit_partial_cache_key(commit, ref: ref, merge_request: merge_request, request: request) }
+ = render partial: 'projects/commits/commit', collection: context_commits, locals: { project: project, ref: ref, merge_request: merge_request }, cached: ->(commit) { commit_partial_cache_key(commit, ref: ref, merge_request: merge_request, request: request) }
- else
= render partial: 'projects/commits/commit', collection: context_commits, locals: { project: project, ref: ref, merge_request: merge_request }
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index ae68a13929e..c129d978e7e 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -1,6 +1,7 @@
- breadcrumb_title _("Commits")
- add_page_specific_style 'page_bundles/tree'
- page_title _("Commits"), @ref
+
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, project_commits_path(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits")
@@ -9,7 +10,7 @@
.nav-block
.tree-ref-container
.tree-ref-holder
- = render 'shared/ref_switcher', destination: 'commits'
+ #js-project-commits-ref-switcher{ data: { "project-id" => @project.id, "ref" => @ref, "commits_path": project_commits_path(@project) } }
%ul.breadcrumb.repo-breadcrumb
= commits_breadcrumbs
@@ -24,7 +25,7 @@
= _("Create merge request")
.control
- = form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form js-signature-container', data: { 'signatures-path' => namespace_project_signatures_path }) do
+ = form_tag(project_commits_path(@project, @id, ref_type: @ref_type), method: :get, class: 'commits-search-form js-signature-container', data: { 'signatures-path' => namespace_project_signatures_path(ref_type: @ref_type)}) do
= search_field_tag :search, params[:search], { placeholder: _('Search by message'), id: 'commits-search', class: 'form-control gl-form-input input-short gl-mt-3 gl-sm-mt-0 gl-min-w-full', spellcheck: false }
.control.d-none.d-md-block
= link_to project_commits_path(@project, @id, rss_url_options), title: _("Commits feed"), class: 'btn gl-button btn-default btn-icon' do
diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml
index 11984a9d6f6..8ff6d348d95 100644
--- a/app/views/projects/diffs/_diffs.html.haml
+++ b/app/views/projects/diffs/_diffs.html.haml
@@ -34,7 +34,7 @@
- if load_diff_files_async
- url = url_for(safe_params.merge(action: 'diff_files'))
.js-diffs-batch{ data: { diff_files_path: url } }
- = gl_loading_icon( size: "md", css_class: "gl-mt-4" )
+ = gl_loading_icon(size: "md", css_class: "gl-mt-4")
- else
= render partial: 'projects/diffs/file', collection: diff_files, as: :diff_file, locals: { project: diffs.project, environment: environment, diff_page_context: diff_page_context }
diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml
index 6d60ef92d86..53b2af88511 100644
--- a/app/views/projects/environments/show.html.haml
+++ b/app/views/projects/environments/show.html.haml
@@ -8,28 +8,31 @@
#environments-detail-view{ data: { details: environments_detail_data_json(current_user, @project, @environment) } }
#environments-detail-view-header
- .environments-container
- - if @deployments.blank?
- .empty-state
- .text-content
- %h4.state-title
- = _("You don't have any deployments right now.")
- %p
- = html_escape(_("Define environments in the deploy stage(s) in %{code_open}.gitlab-ci.yml%{code_close} to track deployments here.")) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
- .text-center
- = link_to _("Read more"), help_page_path("ci/environments/index.md"), class: "gl-button btn btn-confirm"
- - else
- .table-holder.gl-overflow-visible
- .ci-table.environments{ role: 'grid' }
- .gl-responsive-table-row.table-row-header{ role: 'row' }
- .table-section.section-15{ role: 'columnheader' }= _('Status')
- .table-section.section-10{ role: 'columnheader' }= _('ID')
- .table-section.section-10{ role: 'columnheader' }= _('Triggerer')
- .table-section.section-25{ role: 'columnheader' }= _('Commit')
- .table-section.section-10{ role: 'columnheader' }= _('Job')
- .table-section.section-10{ role: 'columnheader' }= _('Created')
- .table-section.section-10{ role: 'columnheader' }= _('Deployed')
+ - if Feature.enabled?(:environment_details_vue, @project)
+ #environment_details_page
+ - else
+ .environments-container
+ - if @deployments.blank?
+ .empty-state
+ .text-content
+ %h4.state-title
+ = _("You don't have any deployments right now.")
+ %p
+ = html_escape(_("Define environments in the deploy stage(s) in %{code_open}.gitlab-ci.yml%{code_close} to track deployments here.")) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
+ .text-center
+ = link_to _("Read more"), help_page_path("ci/environments/index.md"), class: "gl-button btn btn-confirm"
+ - else
+ .table-holder.gl-overflow-visible
+ .ci-table.environments{ role: 'grid' }
+ .gl-responsive-table-row.table-row-header{ role: 'row' }
+ .table-section.section-15{ role: 'columnheader' }= _('Status')
+ .table-section.section-10{ role: 'columnheader' }= _('ID')
+ .table-section.section-10{ role: 'columnheader' }= _('Triggerer')
+ .table-section.section-25{ role: 'columnheader' }= _('Commit')
+ .table-section.section-10{ role: 'columnheader' }= _('Job')
+ .table-section.section-10{ role: 'columnheader' }= _('Created')
+ .table-section.section-10{ role: 'columnheader' }= _('Deployed')
- = render @deployments
+ = render @deployments
- = paginate @deployments, theme: 'gitlab'
+ = paginate @deployments, theme: 'gitlab'
diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml
index c7639eec75d..a27f076d5dd 100644
--- a/app/views/projects/graphs/show.html.haml
+++ b/app/views/projects/graphs/show.html.haml
@@ -1,8 +1,14 @@
- page_title _('Contributors')
+- if Feature.enabled?(:use_ref_type_parameter, @project)
+ - graph_path = project_graph_path(@project, current_ref, ref_type: @ref_type, format: :json)
+ - commits_path = project_commits_path(@project, current_ref, ref_type: @ref_type)
+- else
+ - graph_path = project_graph_path(@project, current_ref, format: :json)
+ - commits_path = project_commits_path(@project, current_ref)
.sub-header-block.gl-bg-gray-10.gl-p-5
.tree-ref-holder.gl-display-inline-block.gl-vertical-align-middle.gl-mr-3>
= render 'shared/ref_switcher', destination: 'graphs'
- = link_to s_('Commits|History'), project_commits_path(@project, current_ref), class: 'btn gl-button btn-default'
+ = link_to s_('Commits|History'), commits_path, class: 'btn gl-button btn-default'
-.js-contributors-graph{ class: container_class, data: { project_graph_path: project_graph_path(@project, current_ref, format: :json), project_branch: current_ref, default_branch: @project.default_branch } }
+.js-contributors-graph{ class: container_class, data: { project_graph_path: graph_path, project_branch: current_ref, default_branch: @project.default_branch } }
diff --git a/app/views/projects/issuable/_show.html.haml b/app/views/projects/issuable/_show.html.haml
index 0898e0ae52d..ec233bc9aff 100644
--- a/app/views/projects/issuable/_show.html.haml
+++ b/app/views/projects/issuable/_show.html.haml
@@ -3,6 +3,7 @@
- page_card_attributes issuable.card_attributes
- if issuable.relocation_target
- page_canonical_link issuable.relocation_target.present(current_user: current_user).web_url
+- add_page_specific_style 'page_bundles/issuable'
= render "projects/issues/service_desk/alert_moved_from_service_desk", issue: issuable
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index b730eb5072e..f8f57934303 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -1,13 +1,14 @@
- page_title _('Issues')
+- add_page_specific_style 'page_bundles/issuable_list'
- add_page_specific_style 'page_bundles/issues_list'
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{@project.name} issues")
-.js-jira-issues-import-status{ data: { can_edit: can?(current_user, :admin_project, @project).to_s,
+.js-jira-issues-import-status-root{ data: { can_edit: can?(current_user, :admin_project, @project).to_s,
is_jira_configured: @project.jira_integration.present?.to_s,
issues_path: project_issues_path(@project),
project_path: @project.full_path } }
-.js-issues-list{ data: project_issues_list_data(@project, current_user) }
+.js-issues-list-root{ data: project_issues_list_data(@project, current_user) }
- if can?(current_user, :admin_issue, @project)
= render 'shared/issuable/bulk_update_sidebar', type: :issues
diff --git a/app/views/projects/issues/service_desk.html.haml b/app/views/projects/issues/service_desk.html.haml
index 93cb5ddd7e2..3cc419716e5 100644
--- a/app/views/projects/issues/service_desk.html.haml
+++ b/app/views/projects/issues/service_desk.html.haml
@@ -1,7 +1,7 @@
- @can_bulk_update = false
- page_title _("Service Desk")
-- add_page_specific_style 'page_bundles/issues_list'
+- add_page_specific_style 'page_bundles/issuable_list'
- content_for :breadcrumbs_extra do
= render "projects/issues/service_desk/nav_btns", show_export_button: false, show_rss_button: false
diff --git a/app/views/projects/jobs/_table.html.haml b/app/views/projects/jobs/_table.html.haml
index cd59eae1fb7..954c77a21f3 100644
--- a/app/views/projects/jobs/_table.html.haml
+++ b/app/views/projects/jobs/_table.html.haml
@@ -20,16 +20,16 @@
%table.table.ci-table.builds-page
%thead
%tr
- %th Status
- %th Name
- %th Job
- %th Pipeline
+ %th= _('Status')
+ %th= _('Name')
+ %th= _('Job')
+ %th= _('Pipeline')
- if admin
- %th Project
- %th Runner
- %th Stage
- %th Duration
- %th Coverage
+ %th= _('Project')
+ %th= _('Runner')
+ %th= _('Stage')
+ %th= _('Duration')
+ %th= _('Coverage')
%th
= render partial: "projects/ci/builds/build", collection: builds, as: :build, locals: { commit_sha: true, ref: true, pipeline_link: true, stage: true, allow_retry: true, admin: admin }
diff --git a/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml b/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml
index 78fce3f7087..fb950611f81 100644
--- a/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml
+++ b/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml
@@ -1,53 +1,53 @@
- display_issuable_type = issuable_display_type(@merge_request)
-.btn-group.gl-md-ml-3.gl-display-flex.dropdown.gl-new-dropdown.gl-md-w-auto.gl-w-full
+.btn-group.gl-md-ml-3.gl-display-flex.dropdown.gl-dropdown.gl-md-w-auto.gl-w-full
= button_tag type: 'button', class: "btn dropdown-toggle btn-default btn-md gl-button gl-dropdown-toggle btn-default-tertiary dropdown-icon-only dropdown-toggle-no-caret has-tooltip gl-display-none! gl-md-display-inline-flex!", data: { toggle: 'dropdown', title: _('Merge request actions'), testid: 'merge-request-actions', 'aria-label': _('Merge request actions') } do
= sprite_icon "ellipsis_v", size: 16, css_class: "dropdown-icon gl-icon"
= button_tag type: 'button', class: "btn dropdown-toggle btn-default btn-md btn-block gl-button gl-dropdown-toggle gl-md-display-none!", data: { 'toggle' => 'dropdown' } do
- %span.gl-new-dropdown-button-text= _('Merge request actions')
+ %span.gl-dropdown-button-text= _('Merge request actions')
= sprite_icon "chevron-down", size: 16, css_class: "dropdown-icon gl-icon"
.dropdown-menu.dropdown-menu-right
- .gl-new-dropdown-inner
- .gl-new-dropdown-contents
+ .gl-dropdown-inner
+ .gl-dropdown-contents
%ul
- if current_user && moved_mr_sidebar_enabled?
- %li.gl-new-dropdown-item.js-sidebar-subscriptions-widget-root
- %li.gl-new-dropdown-divider
+ %li.gl-dropdown-item.js-sidebar-subscriptions-widget-root
+ %li.gl-dropdown-divider
%hr.dropdown-divider
- if can?(current_user, :update_merge_request, @merge_request)
- %li.gl-new-dropdown-item{ class: "gl-md-display-none!" }
+ %li.gl-dropdown-item{ class: "gl-md-display-none!" }
= link_to edit_project_merge_request_path(@project, @merge_request), class: 'dropdown-item' do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Edit')
- if @merge_request.open?
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
= link_to toggle_draft_merge_request_path(@merge_request), method: :put, class: 'dropdown-item js-draft-toggle-button' do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= @merge_request.draft? ? _('Mark as ready') : _('Mark as draft')
- %li.gl-new-dropdown-item.js-close-item
+ %li.gl-dropdown-item.js-close-item
= link_to close_issuable_path(@merge_request), method: :put, class: 'dropdown-item' do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Close')
= display_issuable_type
- elsif !@merge_request.source_project_missing? && @merge_request.closed?
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
= link_to reopen_issuable_path(@merge_request), method: :put, class: 'dropdown-item' do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Reopen')
= display_issuable_type
- if moved_mr_sidebar_enabled?
- %li.gl-new-dropdown-item.js-sidebar-lock-root
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item.js-sidebar-lock-root
+ %li.gl-dropdown-item
%button.dropdown-item.js-copy-reference{ type: "button", data: { 'clipboard-text': @merge_request.to_reference(full: true) } }
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Copy reference')
- unless current_controller?('conflicts')
- unless issuable_author_is_current_user(@merge_request)
- if moved_mr_sidebar_enabled?
- %li.gl-new-dropdown-divider
+ %li.gl-dropdown-divider
%hr.dropdown-divider
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
= link_to new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request)), class: 'dropdown-item' do
- .gl-new-dropdown-item-text-wrapper
- = _('Report abuse')
+ .gl-dropdown-item-text-wrapper
+ = _('Report abuse to administrator')
diff --git a/app/views/projects/merge_requests/_code_dropdown.html.haml b/app/views/projects/merge_requests/_code_dropdown.html.haml
index 5c7fe56095c..2ef89a7bf04 100644
--- a/app/views/projects/merge_requests/_code_dropdown.html.haml
+++ b/app/views/projects/merge_requests/_code_dropdown.html.haml
@@ -1,39 +1,39 @@
-.gl-md-ml-3.dropdown.gl-new-dropdown{ class: "gl-display-none! gl-md-display-flex!" }
+.gl-md-ml-3.dropdown.gl-dropdown{ class: "gl-display-none! gl-md-display-flex!" }
#js-check-out-modal{ data: how_merge_modal_data(@merge_request) }
= button_tag type: 'button', class: "btn dropdown-toggle btn-confirm gl-button gl-dropdown-toggle", data: { toggle: 'dropdown', qa_selector: 'mr_code_dropdown' } do
- %span.gl-new-dropdown-button-text= _('Code')
+ %span.gl-dropdown-button-text= _('Code')
= sprite_icon "chevron-down", size: 16, css_class: "dropdown-icon gl-icon gl-ml-2 gl-mr-0!"
.dropdown-menu.dropdown-menu-right
- .gl-new-dropdown-inner
- .gl-new-dropdown-contents
+ .gl-dropdown-inner
+ .gl-dropdown-contents
%ul
- %li.gl-new-dropdown-section-header
+ %li.gl-dropdown-section-header
%header.dropdown-header
= _('Review changes')
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
%button.dropdown-item.js-check-out-modal-trigger{ type: 'button' }
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Check out branch')
- if current_user
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
= link_to ide_merge_request_path(@merge_request), class: 'dropdown-item', target: '_blank', data: { qa_selector: 'open_in_web_ide_button' } do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Open in Web IDE')
- if Gitlab::CurrentSettings.gitpod_enabled && current_user&.gitpod_enabled
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
= link_to "#{Gitlab::CurrentSettings.gitpod_url}##{merge_request_url(@merge_request)}", target: '_blank', class: 'dropdown-item' do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Open in Gitpod')
- %li.gl-new-dropdown-divider
+ %li.gl-dropdown-divider
%hr.dropdown-divider
- %li.gl-new-dropdown-section-header
+ %li.gl-dropdown-section-header
%header.dropdown-header
= _('Download')
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
= link_to merge_request_path(@merge_request, format: :patch), class: 'dropdown-item', download: '', data: { qa_selector: 'download_email_patches_menu_item' } do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Email patches')
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
= link_to merge_request_path(@merge_request, format: :diff), class: 'dropdown-item', download: '', data: { qa_selector: 'download_plain_diff_menu_item' } do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Plain diff')
diff --git a/app/views/projects/merge_requests/_page.html.haml b/app/views/projects/merge_requests/_page.html.haml
new file mode 100644
index 00000000000..9d79352659c
--- /dev/null
+++ b/app/views/projects/merge_requests/_page.html.haml
@@ -0,0 +1,114 @@
+- @gfm_form = true
+- unless moved_mr_sidebar_enabled?
+ - @content_class = "merge-request-container#{' limit-container-width' unless fluid_layout}"
+- add_to_breadcrumbs _("Merge requests"), project_merge_requests_path(@project)
+- breadcrumb_title @merge_request.to_reference
+- page_title "#{@merge_request.title} (#{@merge_request.to_reference})", _("Merge requests")
+- page_description @merge_request.description_html
+- page_card_attributes @merge_request.card_attributes
+- suggest_changes_help_path = help_page_path('user/project/merge_requests/reviews/suggestions.md')
+- mr_action = j(params[:tab].presence || 'show')
+- add_page_specific_style 'page_bundles/issuable'
+- add_page_specific_style 'page_bundles/design_management'
+- add_page_specific_style 'page_bundles/merge_requests'
+- add_page_specific_style 'page_bundles/pipelines'
+- add_page_specific_style 'page_bundles/reports'
+- add_page_specific_style 'page_bundles/ci_status'
+
+- add_page_startup_api_call @endpoint_metadata_url
+- if mr_action == 'diffs'
+ - add_page_startup_api_call @endpoint_diff_batch_url
+
+.merge-request{ data: { mr_action: mr_action, url: merge_request_path(@merge_request, format: :json), project_path: project_path(@merge_request.project), lock_version: @merge_request.lock_version } }
+ - if moved_mr_sidebar_enabled?
+ #js-merge-sticky-header{ data: { data: sticky_header_data.to_json } }
+ = render "projects/merge_requests/mr_title"
+
+ .merge-request-details.issuable-details{ data: { id: @merge_request.project.id } }
+ = render "projects/merge_requests/mr_box"
+ .merge-request-tabs-holder{ class: "#{'js-tabs-affix' unless ENV['RAILS_ENV'] == 'test'} #{'gl-static' if moved_mr_sidebar_enabled?}" }
+ .merge-request-tabs-container.gl-display-flex.gl-justify-content-space-between{ class: "#{'is-merge-request' if Feature.enabled?(:moved_mr_sidebar, @project) && !fluid_layout}" }
+ %ul.merge-request-tabs.nav-tabs.nav.nav-links.gl-display-flex.gl-flex-nowrap.gl-m-0.gl-p-0{ class: "#{'gl-w-full gl-lg-w-auto!' if Feature.enabled?(:moved_mr_sidebar, @project)}" }
+ = render "projects/merge_requests/tabs/tab", class: "notes-tab", qa_selector: "notes_tab" do
+ = tab_link_for @merge_request, :show, force_link: @commit.present? do
+ = _("Overview")
+ = gl_badge_tag @merge_request.related_notes.user.count, { size: :sm }, { class: 'js-discussions-count' }
+ - if @merge_request.source_project
+ = render "projects/merge_requests/tabs/tab", name: "commits", class: "commits-tab", qa_selector: "commits_tab" do
+ = tab_link_for @merge_request, :commits do
+ = _("Commits")
+ = gl_badge_tag @commits_count, { size: :sm }
+ - if @project.builds_enabled?
+ = render "projects/merge_requests/tabs/tab", name: "pipelines", class: "pipelines-tab" do
+ = tab_link_for @merge_request, :pipelines do
+ = _("Pipelines")
+ = gl_badge_tag @number_of_pipelines, { size: :sm }, { class: 'js-pipelines-mr-count' }
+ = render "projects/merge_requests/tabs/tab", name: "diffs", class: "diffs-tab js-diffs-tab", id: "diffs-tab", qa_selector: "diffs_tab" do
+ = tab_link_for @merge_request, :diffs do
+ = _("Changes")
+ = gl_badge_tag @diffs_count, { size: :sm }
+ .d-flex.flex-wrap.align-items-center.justify-content-lg-end
+ #js-vue-discussion-counter{ data: { blocks_merge: @project.only_allow_merge_if_all_discussions_are_resolved?.to_s } }
+ - if moved_mr_sidebar_enabled?
+ - if !!@issuable_sidebar.dig(:current_user, :id)
+ .js-sidebar-todo-widget-root{ data: { project_path: @issuable_sidebar[:project_full_path], iid: @issuable_sidebar[:iid], id: @issuable_sidebar[:id] } }
+ .gl-ml-auto.gl-align-items-center.gl-display-none.gl-md-display-flex.gl-ml-3.js-expand-sidebar.gl-absolute.gl-right-5{ class: "gl-lg-display-none!" }
+ = render Pajamas::ButtonComponent.new(icon: 'chevron-double-lg-left',
+ button_options: { class: 'js-sidebar-toggle' }) do
+ = _('Expand')
+ .tab-content#diff-notes-app
+ #js-diff-file-finder
+ #js-code-navigation
+ = render "projects/merge_requests/tabs/pane", id: "notes", class: "notes voting_notes" do
+ %div{ class: "#{'merge-request-overview' if moved_mr_sidebar_enabled?}" }
+ %section
+ .issuable-discussion.js-vue-notes-event
+ - if @merge_request.description.present?
+ .detail-page-description.gl-pb-0
+ = render "projects/merge_requests/description"
+ = render "projects/merge_requests/awards_block"
+ = render "projects/merge_requests/widget"
+ - if mr_action === "show"
+ - add_page_startup_api_call Feature.enabled?(:paginated_mr_discussions, @project) ? discussions_path(@merge_request, per_page: 20) : discussions_path(@merge_request)
+ - add_page_startup_api_call widget_project_json_merge_request_path(@project, @merge_request, format: :json)
+ - add_page_startup_api_call cached_widget_project_json_merge_request_path(@project, @merge_request, format: :json)
+ #js-vue-mr-discussions{ data: { notes_data: notes_data(@merge_request).to_json,
+ endpoint_metadata: @endpoint_metadata_url,
+ noteable_data: serialize_issuable(@merge_request, serializer: 'noteable'),
+ noteable_type: 'MergeRequest',
+ notes_filters: UserPreference.notes_filters.to_json,
+ notes_filter_value: current_user&.notes_filter_for(@merge_request),
+ target_type: 'merge_request',
+ help_page_path: suggest_changes_help_path,
+ current_user_data: @current_user_data,
+ is_locked: @merge_request.discussion_locked.to_s } }
+ - if moved_mr_sidebar_enabled?
+ = render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, reviewers: @merge_request.reviewers, source_branch: @merge_request.source_branch
+
+ = render "projects/merge_requests/tabs/pane", name: "commits", id: "commits", class: "commits" do
+ -# This tab is always loaded via AJAX
+ = render "projects/merge_requests/tabs/pane", name: "pipelines", id: "pipelines", class: "pipelines" do
+ - if @project.builds_enabled?
+ = render 'projects/commit/pipelines_list', disable_initialization: true, endpoint: pipelines_project_merge_request_path(@project, @merge_request)
+ - params = request.query_parameters.merge(diff_head: true)
+ = render "projects/merge_requests/tabs/pane", name: "diffs", id: "js-diffs-app", class: "diffs", data: diffs_tab_pane_data(@project, @merge_request, params)
+
+ .mr-loading-status
+ .loading.hide
+ = gl_loading_icon(size: 'lg')
+
+- unless moved_mr_sidebar_enabled?
+ = render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, reviewers: @merge_request.reviewers, source_branch: @merge_request.source_branch
+
+- if @merge_request.can_be_reverted?(current_user)
+ = render "projects/commit/change", type: 'revert', commit: @merge_request.merge_commit
+- if @merge_request.can_be_cherry_picked?
+ = render "projects/commit/change", type: 'cherry-pick', commit: @merge_request.merge_commit
+
+#js-review-bar
+
+- if current_user && Feature.enabled?(:mr_experience_survey, current_user)
+ #js-mr-experience-survey{ data: { account_age: current_user.account_age_in_days } }
+
+= render 'projects/invite_members_modal', project: @project
+= render 'shared/web_ide_path'
diff --git a/app/views/projects/merge_requests/creations/_new_compare.html.haml b/app/views/projects/merge_requests/creations/_new_compare.html.haml
index 17b1e5a757c..48334023cf0 100644
--- a/app/views/projects/merge_requests/creations/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/creations/_new_compare.html.haml
@@ -1,7 +1,7 @@
%h1.page-title.gl-font-size-h-display
= _('New merge request')
-= form_for [@project, @merge_request], url: project_new_merge_request_path(@project), method: :get, html: { class: "merge-request-form js-requires-input" } do |f|
+= gitlab_ui_form_for [@project, @merge_request], url: project_new_merge_request_path(@project), method: :get, html: { class: "merge-request-form js-requires-input" } do |f|
- if params[:nav_source].present?
= hidden_field_tag(:nav_source, params[:nav_source])
.js-merge-request-new-compare.row{ 'data-source-branch-url': project_new_merge_request_branch_from_path(@source_project), 'data-target-branch-url': project_new_merge_request_branch_to_path(@source_project) }
@@ -40,17 +40,20 @@
%h2.gl-font-size-h2
= _('Target branch')
.clearfix
- - projects = target_projects(@project)
.merge-request-select.dropdown
- = f.hidden_field :target_project_id
- = dropdown_toggle f.object.target_project.full_path, { toggle: "dropdown", 'field-name': "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted?, default_text: _("Select target project") }, { toggle_class: "js-compare-dropdown js-target-project" }
- .dropdown-menu.dropdown-menu-selectable.dropdown-target-project
- = dropdown_title(_("Select target project"))
- = dropdown_filter(_("Search projects"))
- = dropdown_content do
- = render 'projects/merge_requests/dropdowns/project',
- projects: projects,
- selected: f.object.target_project_id
+ - if Feature.enabled?(:mr_compare_dropdowns, @project)
+ #js-target-project-dropdown{ data: { target_projects_path: project_new_merge_request_json_target_projects_path(@project), current_project: { value: f.object.target_project_id.to_s, text: f.object.target_project.full_path }.to_json } }
+ - else
+ - projects = target_projects(@project)
+ = f.hidden_field :target_project_id
+ = dropdown_toggle f.object.target_project.full_path, { toggle: "dropdown", 'field-name': "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted?, default_text: _("Select target project") }, { toggle_class: "js-compare-dropdown js-target-project" }
+ .dropdown-menu.dropdown-menu-selectable.dropdown-target-project
+ = dropdown_title(_("Select target project"))
+ = dropdown_filter(_("Search projects"))
+ = dropdown_content do
+ = render 'projects/merge_requests/dropdowns/project',
+ projects: projects,
+ selected: f.object.target_project_id
.merge-request-select.dropdown
= f.hidden_field :target_branch
= dropdown_toggle f.object.target_branch.presence || _("Select target branch"), { toggle: "dropdown", 'field-name': "#{f.object_name}[target_branch]", 'refs-url': refs_project_path(f.object.target_project), selected: f.object.target_branch, default_text: _("Select target branch") }, { toggle_class: "js-compare-dropdown js-target-branch monospace" }
@@ -68,4 +71,4 @@
- if @merge_request.errors.any?
= form_errors(@merge_request)
- = f.submit _('Compare branches and continue'), class: "gl-button btn btn-confirm mr-compare-btn gl-mt-4", data: { qa_selector: "compare_branches_button" }
+ = f.submit _('Compare branches and continue'), data: { qa_selector: 'compare_branches_button' }, pajamas_button: true
diff --git a/app/views/projects/merge_requests/diffs.html.haml b/app/views/projects/merge_requests/diffs.html.haml
new file mode 100644
index 00000000000..1ef212ee5ce
--- /dev/null
+++ b/app/views/projects/merge_requests/diffs.html.haml
@@ -0,0 +1 @@
+= render 'page'
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index a3f40207d20..79da09c5205 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -5,6 +5,7 @@
- page_title _("Merge requests")
- new_merge_request_email = @project.new_issuable_address(current_user, 'merge_request')
+- add_page_specific_style 'page_bundles/issuable_list'
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{@project.name} merge requests")
diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml
index 203724fc1f1..1ef212ee5ce 100644
--- a/app/views/projects/merge_requests/show.html.haml
+++ b/app/views/projects/merge_requests/show.html.haml
@@ -1,113 +1 @@
-- @gfm_form = true
-- unless moved_mr_sidebar_enabled?
- - @content_class = "merge-request-container#{' limit-container-width' unless fluid_layout}"
-- add_to_breadcrumbs _("Merge requests"), project_merge_requests_path(@project)
-- breadcrumb_title @merge_request.to_reference
-- page_title "#{@merge_request.title} (#{@merge_request.to_reference})", _("Merge requests")
-- page_description @merge_request.description_html
-- page_card_attributes @merge_request.card_attributes
-- suggest_changes_help_path = help_page_path('user/project/merge_requests/reviews/suggestions.md')
-- mr_action = j(params[:tab].presence || 'show')
-- add_page_specific_style 'page_bundles/design_management'
-- add_page_specific_style 'page_bundles/merge_requests'
-- add_page_specific_style 'page_bundles/pipelines'
-- add_page_specific_style 'page_bundles/reports'
-- add_page_specific_style 'page_bundles/ci_status'
-
-- add_page_startup_api_call @endpoint_metadata_url
-- if mr_action == 'diffs'
- - add_page_startup_api_call @endpoint_diff_batch_url
-
-.merge-request{ data: { mr_action: mr_action, url: merge_request_path(@merge_request, format: :json), project_path: project_path(@merge_request.project), lock_version: @merge_request.lock_version } }
- - if moved_mr_sidebar_enabled?
- #js-merge-sticky-header{ data: { data: sticky_header_data.to_json } }
- = render "projects/merge_requests/mr_title"
-
- .merge-request-details.issuable-details{ data: { id: @merge_request.project.id } }
- = render "projects/merge_requests/mr_box"
- .merge-request-tabs-holder{ class: "#{'js-tabs-affix' unless ENV['RAILS_ENV'] == 'test'} #{'gl-static' if moved_mr_sidebar_enabled?}" }
- .merge-request-tabs-container.gl-display-flex.gl-justify-content-space-between{ class: "#{'is-merge-request' if Feature.enabled?(:moved_mr_sidebar, @project) && !fluid_layout}" }
- %ul.merge-request-tabs.nav-tabs.nav.nav-links.gl-display-flex.gl-flex-nowrap.gl-m-0.gl-p-0{ class: "#{'gl-w-full gl-lg-w-auto!' if Feature.enabled?(:moved_mr_sidebar, @project)}" }
- = render "projects/merge_requests/tabs/tab", class: "notes-tab", qa_selector: "notes_tab" do
- = tab_link_for @merge_request, :show, force_link: @commit.present? do
- = _("Overview")
- = gl_badge_tag @merge_request.related_notes.user.count, { size: :sm }
- - if @merge_request.source_project
- = render "projects/merge_requests/tabs/tab", name: "commits", class: "commits-tab", qa_selector: "commits_tab" do
- = tab_link_for @merge_request, :commits do
- = _("Commits")
- = gl_badge_tag @commits_count, { size: :sm }
- - if @project.builds_enabled?
- = render "projects/merge_requests/tabs/tab", name: "pipelines", class: "pipelines-tab" do
- = tab_link_for @merge_request, :pipelines do
- = _("Pipelines")
- = gl_badge_tag @number_of_pipelines, { size: :sm }, { class: 'js-pipelines-mr-count' }
- = render "projects/merge_requests/tabs/tab", name: "diffs", class: "diffs-tab js-diffs-tab", id: "diffs-tab", qa_selector: "diffs_tab" do
- = tab_link_for @merge_request, :diffs do
- = _("Changes")
- = gl_badge_tag @diffs_count, { size: :sm }
- .d-flex.flex-wrap.align-items-center.justify-content-lg-end
- #js-vue-discussion-counter{ data: { blocks_merge: @project.only_allow_merge_if_all_discussions_are_resolved?.to_s } }
- - if moved_mr_sidebar_enabled?
- - if !!@issuable_sidebar.dig(:current_user, :id)
- .js-sidebar-todo-widget-root{ data: { project_path: @issuable_sidebar[:project_full_path], iid: @issuable_sidebar[:iid], id: @issuable_sidebar[:id] } }
- .gl-ml-auto.gl-align-items-center.gl-display-none.gl-md-display-flex.gl-ml-3.js-expand-sidebar{ class: "gl-lg-display-none!" }
- = render Pajamas::ButtonComponent.new(icon: 'chevron-double-lg-left',
- button_options: { class: 'js-sidebar-toggle' }) do
- = _('Expand')
- .tab-content#diff-notes-app
- #js-diff-file-finder
- #js-code-navigation
- = render "projects/merge_requests/tabs/pane", id: "notes", class: "notes voting_notes" do
- %div{ class: "#{'merge-request-overview' if moved_mr_sidebar_enabled?}" }
- %section
- .issuable-discussion.js-vue-notes-event
- - if @merge_request.description.present?
- .detail-page-description.gl-pb-0
- = render "projects/merge_requests/description"
- = render "projects/merge_requests/awards_block"
- = render "projects/merge_requests/widget"
- - if mr_action === "show"
- - add_page_startup_api_call Feature.enabled?(:paginated_mr_discussions, @project) ? discussions_path(@merge_request, per_page: 20) : discussions_path(@merge_request)
- - add_page_startup_api_call widget_project_json_merge_request_path(@project, @merge_request, format: :json)
- - add_page_startup_api_call cached_widget_project_json_merge_request_path(@project, @merge_request, format: :json)
- #js-vue-mr-discussions{ data: { notes_data: notes_data(@merge_request).to_json,
- endpoint_metadata: @endpoint_metadata_url,
- noteable_data: serialize_issuable(@merge_request, serializer: 'noteable'),
- noteable_type: 'MergeRequest',
- notes_filters: UserPreference.notes_filters.to_json,
- notes_filter_value: current_user&.notes_filter_for(@merge_request),
- target_type: 'merge_request',
- help_page_path: suggest_changes_help_path,
- current_user_data: @current_user_data,
- is_locked: @merge_request.discussion_locked.to_s } }
- - if moved_mr_sidebar_enabled?
- = render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, reviewers: @merge_request.reviewers, source_branch: @merge_request.source_branch
-
- = render "projects/merge_requests/tabs/pane", name: "commits", id: "commits", class: "commits" do
- -# This tab is always loaded via AJAX
- = render "projects/merge_requests/tabs/pane", name: "pipelines", id: "pipelines", class: "pipelines" do
- - if @project.builds_enabled?
- = render 'projects/commit/pipelines_list', disable_initialization: true, endpoint: pipelines_project_merge_request_path(@project, @merge_request)
- - params = request.query_parameters.merge(diff_head: true)
- = render "projects/merge_requests/tabs/pane", name: "diffs", id: "js-diffs-app", class: "diffs", data: diffs_tab_pane_data(@project, @merge_request, params)
-
- .mr-loading-status
- .loading.hide
- = gl_loading_icon(size: 'lg')
-
-- unless moved_mr_sidebar_enabled?
- = render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, reviewers: @merge_request.reviewers, source_branch: @merge_request.source_branch
-
-- if @merge_request.can_be_reverted?(current_user)
- = render "projects/commit/change", type: 'revert', commit: @merge_request.merge_commit
-- if @merge_request.can_be_cherry_picked?
- = render "projects/commit/change", type: 'cherry-pick', commit: @merge_request.merge_commit
-
-#js-review-bar
-
-- if current_user && Feature.enabled?(:mr_experience_survey, current_user)
- #js-mr-experience-survey{ data: { account_age: current_user.account_age_in_days } }
-
-= render 'projects/invite_members_modal', project: @project
-= render 'shared/web_ide_path'
+= render 'page'
diff --git a/app/views/projects/ml/candidates/show.html.haml b/app/views/projects/ml/candidates/show.html.haml
new file mode 100644
index 00000000000..7fa98f69edf
--- /dev/null
+++ b/app/views/projects/ml/candidates/show.html.haml
@@ -0,0 +1,7 @@
+- experiment = @candidate.experiment
+- add_to_breadcrumbs _("Experiments"), project_ml_experiments_path(@project)
+- add_to_breadcrumbs experiment.name, project_ml_experiment_path(@project, experiment.iid)
+- breadcrumb_title "Candidate #{@candidate.iid}"
+- data = candidate_as_data(@candidate)
+
+#js-show-ml-candidate{ data: { candidate: data } }
diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml
index 2a3171e9fd8..70bb97b7625 100644
--- a/app/views/projects/network/show.html.haml
+++ b/app/views/projects/network/show.html.haml
@@ -1,10 +1,11 @@
- breadcrumb_title _("Graph")
- page_title _("Graph"), @ref
+- network_path = Feature.enabled?(:use_ref_type_parameter) ? project_network_path(@project, @id, ref_type: @ref_type) : project_network_path(@project, @id)
= render "head"
.gl-mt-5
.project-network.gl-border-1.gl-border-solid.gl-border-gray-300
.controls.gl-bg-gray-50.gl-p-2.gl-font-base.gl-text-gray-400.gl-border-b-1.gl-border-b-solid.gl-border-b-gray-300
- = form_tag project_network_path(@project, @id), method: :get, class: 'form-inline network-form' do |f|
+ = form_tag network_path, method: :get, class: 'form-inline network-form' do |f|
= text_field_tag :extended_sha1, @options[:extended_sha1], placeholder: _("Git revision"), class: 'search-input form-control gl-form-input input-mx-250 search-sha gl-mr-2'
= render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, icon: 'search')
.inline.gl-ml-5
diff --git a/app/views/projects/notes/_more_actions_dropdown.html.haml b/app/views/projects/notes/_more_actions_dropdown.html.haml
index 5f70e25f802..2351bd209a7 100644
--- a/app/views/projects/notes/_more_actions_dropdown.html.haml
+++ b/app/views/projects/notes/_more_actions_dropdown.html.haml
@@ -10,7 +10,7 @@
- unless is_current_user
%li
= link_to new_abuse_report_path(user_id: note.author.id, ref_url: noteable_note_url(note)) do
- = _('Report abuse to admin')
+ = _('Report abuse to administrator')
- if note_editable
%li
= link_to note_url(note), method: :delete, data: { confirm: _('Are you sure you want to delete this comment?'), confirm_btn_variant: 'danger', qa_selector: 'delete_comment_button' }, aria: { label: _('Delete comment') }, remote: true, class: 'js-note-delete' do
diff --git a/app/views/projects/pages/_list.html.haml b/app/views/projects/pages/_list.html.haml
index 16312da1353..32e67fdadb8 100644
--- a/app/views/projects/pages/_list.html.haml
+++ b/app/views/projects/pages/_list.html.haml
@@ -12,7 +12,7 @@
- if verification_enabled
- tooltip, status = domain.unverified? ? [s_('GitLabPages|Unverified'), 'failed'] : [s_('GitLabPages|Verified'), 'success']
.domain-status.ci-status-icon.has-tooltip{ class: "gl-mr-5 ci-status-icon-#{status}", title: tooltip }
- = sprite_icon("status_#{status}" )
+ = sprite_icon("status_#{status}")
.domain-name
= external_link(domain.url, domain.url)
- if domain.certificate
diff --git a/app/views/projects/pages_domains/new.html.haml b/app/views/projects/pages_domains/new.html.haml
index 6de8117df6b..c88255e23f9 100644
--- a/app/views/projects/pages_domains/new.html.haml
+++ b/app/views/projects/pages_domains/new.html.haml
@@ -4,9 +4,8 @@
= _("New Pages Domain")
= render 'projects/pages_domains/helper_text'
%div
- = form_for [@project, domain_presenter], html: { class: 'fieldset-form' } do |f|
+ = gitlab_ui_form_for [@project, domain_presenter], html: { class: 'fieldset-form' } do |f|
= render 'form', { f: f }
- .form-actions
- = f.submit _('Create New Domain'), class: "gl-button btn btn-confirm"
- .float-right
- = link_to _('Cancel'), project_pages_path(@project), class: 'gl-button btn btn-default btn-cancel'
+ .form-actions.gl-display-flex
+ = f.submit _('Create New Domain'), class: 'gl-mr-3', pajamas_button: true
+ = link_to _('Cancel'), project_pages_path(@project), class: 'gl-button btn btn-default btn-cancel'
diff --git a/app/views/projects/pages_domains/show.html.haml b/app/views/projects/pages_domains/show.html.haml
index 0edf75c9abc..5de5188ae6a 100644
--- a/app/views/projects/pages_domains/show.html.haml
+++ b/app/views/projects/pages_domains/show.html.haml
@@ -15,8 +15,8 @@
= _('Pages Domain')
= render 'projects/pages_domains/helper_text'
%div
- = form_for [@project, domain_presenter], html: { class: 'fieldset-form' } do |f|
+ = gitlab_ui_form_for [@project, domain_presenter], html: { class: 'fieldset-form' } do |f|
= render 'form', { f: f }
- .form-actions.d-flex.justify-content-between
- = f.submit _('Save Changes'), class: "gl-button btn btn-confirm"
+ .form-actions.gl-display-flex
+ = f.submit _('Save Changes'), class: 'gl-mr-3', pajamas_button: true
= link_to _('Cancel'), project_pages_path(@project), class: 'gl-button btn btn-default btn-inverse'
diff --git a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
index 7b16564dfa2..0de31f59033 100644
--- a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
+++ b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
@@ -1,33 +1,38 @@
- if pipeline_schedule
%tr.pipeline-schedule-table-row
- %td
- = pipeline_schedule.description
- %td.branch-name-cell.gl-text-truncate
- - if pipeline_schedule.for_tag?
- = sprite_icon('tag', size: 12, css_class: 'gl-vertical-align-middle!' )
- - else
- = sprite_icon('fork', size: 12, css_class: 'gl-vertical-align-middle!')
- - if pipeline_schedule.ref.present?
- = link_to pipeline_schedule.ref_for_display, project_ref_path(@project, pipeline_schedule.ref_for_display), class: "ref-name"
- %td
- - if pipeline_schedule.last_pipeline
- .status-icon-container{ class: "ci-status-icon-#{pipeline_schedule.last_pipeline.status}" }
- = link_to project_pipeline_path(@project, pipeline_schedule.last_pipeline.id) do
- = ci_icon_for_status(pipeline_schedule.last_pipeline.status)
- %span ##{pipeline_schedule.last_pipeline.id}
- - else
- = s_("PipelineSchedules|None")
- %td.gl-text-gray-500{ 'data-testid': 'next-run-cell' }
- - if pipeline_schedule.active? && pipeline_schedule.next_run_at
- = time_ago_with_tooltip(pipeline_schedule.real_next_run)
- - else
- = s_("PipelineSchedules|Inactive")
- %td
- - if pipeline_schedule.owner
- = render Pajamas::AvatarComponent.new(pipeline_schedule.owner, size: 24, class: "gl-mr-2")
- = link_to user_path(pipeline_schedule.owner) do
- = pipeline_schedule.owner&.name
- %td
+ %td{ role: 'cell', data: { label: _('Description') } }
+ %div
+ = pipeline_schedule.description
+ %td.branch-name-cell.gl-text-truncate{ role: 'cell', data: { label: s_("PipelineSchedules|Target") } }
+ %div
+ - if pipeline_schedule.for_tag?
+ = sprite_icon('tag', size: 12, css_class: 'gl-vertical-align-middle!')
+ - else
+ = sprite_icon('fork', size: 12, css_class: 'gl-vertical-align-middle!')
+ - if pipeline_schedule.ref.present?
+ = link_to pipeline_schedule.ref_for_display, project_ref_path(@project, pipeline_schedule.ref_for_display), class: "ref-name"
+ %td{ role: 'cell', data: { label: _("Last Pipeline") } }
+ %div
+ - if pipeline_schedule.last_pipeline
+ .status-icon-container{ class: "ci-status-icon-#{pipeline_schedule.last_pipeline.status}" }
+ = link_to project_pipeline_path(@project, pipeline_schedule.last_pipeline.id) do
+ = ci_icon_for_status(pipeline_schedule.last_pipeline.status)
+ %span.gl-text-blue-500! ##{pipeline_schedule.last_pipeline.id}
+ - else
+ = s_("PipelineSchedules|None")
+ %td.gl-text-gray-500{ role: 'cell', data: { label: s_("PipelineSchedules|Next Run") }, 'data-testid': 'next-run-cell' }
+ %div
+ - if pipeline_schedule.active? && pipeline_schedule.next_run_at
+ = time_ago_with_tooltip(pipeline_schedule.real_next_run)
+ - else
+ = s_("PipelineSchedules|Inactive")
+ %td{ role: 'cell', data: { label: _("Owner") } }
+ %div
+ - if pipeline_schedule.owner
+ = render Pajamas::AvatarComponent.new(pipeline_schedule.owner, size: 24, class: "gl-mr-2")
+ = link_to user_path(pipeline_schedule.owner) do
+ = pipeline_schedule.owner&.name
+ %td{ role: 'cell', data: { label: _('Actions') } }
.float-right.btn-group
- if can?(current_user, :play_pipeline_schedule, pipeline_schedule)
= link_to play_pipeline_schedule_path(pipeline_schedule), method: :post, title: _('Play'), class: 'btn gl-button btn-default btn-icon' do
diff --git a/app/views/projects/pipeline_schedules/_table.html.haml b/app/views/projects/pipeline_schedules/_table.html.haml
index d0c7ea77263..2f96ac6a534 100644
--- a/app/views/projects/pipeline_schedules/_table.html.haml
+++ b/app/views/projects/pipeline_schedules/_table.html.haml
@@ -1,12 +1,12 @@
.table-holder
- %table.table.ci-table
- %thead
- %tr
- %th= _("Description")
- %th= s_("PipelineSchedules|Target")
- %th= _("Last Pipeline")
- %th= s_("PipelineSchedules|Next Run")
- %th= _("Owner")
- %th
-
+ %table.table.ci-table.responsive-table.b-table.gl-table.b-table-stacked-md{ role: 'table' }
+ %thead{ role: 'rowgroup' }
+ %tr{ role: 'row' }
+ %th.table-th-transparent.border-bottom{ role: 'cell', style: 'width: 34%' }= _("Description")
+ %th.table-th-transparent.border-bottom{ role: 'cell' }= s_("PipelineSchedules|Target")
+ %th.table-th-transparent.border-bottom{ role: 'cell' }= _("Last Pipeline")
+ %th.table-th-transparent.border-bottom{ role: 'cell' }= s_("PipelineSchedules|Next Run")
+ %th.table-th-transparent.border-bottom{ role: 'cell' }= _("Owner")
+ %th.table-th-transparent.border-bottom{ role: 'cell' }
+ %tbody{ role: 'rowgroup' }
= render partial: "pipeline_schedule", collection: @schedules
diff --git a/app/views/projects/pipeline_schedules/index.html.haml b/app/views/projects/pipeline_schedules/index.html.haml
index 47ad8cc826d..cb7cd631859 100644
--- a/app/views/projects/pipeline_schedules/index.html.haml
+++ b/app/views/projects/pipeline_schedules/index.html.haml
@@ -21,8 +21,7 @@
%ul.content-list
= render partial: "table"
- else
- = render Pajamas::CardComponent.new(card_options: { class: 'bg-light gl-mt-3 gl-text-center' }) do |c|
- - c.body do
- = _("No schedules")
+ .nothing-here-block
+ = _("No schedules")
#pipeline-take-ownership-modal
diff --git a/app/views/projects/pipeline_schedules/new.html.haml b/app/views/projects/pipeline_schedules/new.html.haml
index d3757d0e339..2d4ed5a9872 100644
--- a/app/views/projects/pipeline_schedules/new.html.haml
+++ b/app/views/projects/pipeline_schedules/new.html.haml
@@ -9,6 +9,6 @@
= _("Schedule a new pipeline")
- if Feature.enabled?(:pipeline_schedules_vue, @project)
- #pipeline-schedules-form-new{ data: { full_path: @project.full_path } }
+ #pipeline-schedules-form-new{ data: { full_path: @project.full_path, cron: @schedule.cron, daily_limit: @schedule.daily_limit, timezone_data: timezone_data.to_json, cron_timezone: @schedule.cron_timezone, project_id: @project.id, default_branch: @project.default_branch, settings_link: project_settings_ci_cd_path(@project), } }
- else
= render "form"
diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml
index 30cc7f94311..1a079324a0f 100644
--- a/app/views/projects/pipelines/_info.html.haml
+++ b/app/views/projects/pipelines/_info.html.haml
@@ -1,6 +1,7 @@
- if Feature.enabled?(:pipeline_name, @pipeline.project) && @pipeline.name
- %h3
- = @pipeline.name
+ .gl-border-t.gl-p-5.gl-px-0
+ %h3.gl-m-0.gl-text-body
+ = @pipeline.name
- else
.commit-box
%h3.commit-title
@@ -45,7 +46,7 @@
- popover_content_text = _('Learn more about Auto DevOps')
= gl_badge_tag s_('Pipelines|Auto DevOps'), { variant: :info, size: :sm }, { class: 'js-pipeline-url-autodevops', href: "#", tabindex: "0", role: "button", data: { container: 'body', toggle: 'popover', placement: 'top', html: 'true', triggers: 'focus', title: "<div class='gl-font-weight-normal gl-line-height-normal'>#{popover_title_text}</div>", content: "<a href='#{popover_content_url}' target='_blank' rel='noopener noreferrer nofollow'>#{popover_content_text}</a>" } }
- if @pipeline.detached_merge_request_pipeline?
- = gl_badge_tag s_('Pipelines|merge request'), { variant: :info, size: :sm }, { class: 'js-pipeline-url-mergerequest has-tooltip', title: s_("Pipelines|This pipeline ran on the contents of this merge request's source branch, not the target branch.") }
+ = gl_badge_tag s_('Pipelines|merge request'), { variant: :info, size: :sm }, { class: 'js-pipeline-url-mergerequest has-tooltip', data: { qa_selector: 'merge_request_badge_tag' }, title: s_("Pipelines|This pipeline ran on the contents of this merge request's source branch, not the target branch.") }
- if @pipeline.stuck?
= gl_badge_tag s_('Pipelines|stuck'), { variant: :warning, size: :sm }, { class: 'js-pipeline-url-stuck has-tooltip' }
diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml
deleted file mode 100644
index e83547fd8f8..00000000000
--- a/app/views/projects/pipelines/_with_tabs.html.haml
+++ /dev/null
@@ -1,48 +0,0 @@
-- return if pipeline_has_errors
-
-.tabs-holder
- %ul.pipelines-tabs.nav-links.no-top.no-bottom.mobile-separator.nav.nav-tabs
- %li.js-pipeline-tab-link
- = link_to project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-pipeline', action: 'pipelines', toggle: 'tab' }, class: 'pipeline-tab' do
- = _('Pipeline')
- %li.js-dag-tab-link
- = link_to dag_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-dag', action: 'dag', toggle: 'tab' }, class: 'dag-tab' do
- = _('Needs')
- %li.js-builds-tab-link
- = link_to builds_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do
- = _('Jobs')
- = gl_badge_tag @pipeline.total_size, { size: :sm }, { class: 'js-builds-counter' }
- - if @pipeline.failed_builds.present?
- %li.js-failures-tab-link
- = link_to failures_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do
- = _('Failed Jobs')
- = gl_badge_tag @pipeline.failed_builds.count, { size: :sm }, { class: 'js-failures-counter' }
- %li.js-tests-tab-link
- = link_to test_report_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-tests', action: 'test_report', toggle: 'tab' }, class: 'test-tab' do
- = s_('TestReports|Tests')
- = gl_badge_tag @pipeline.test_report_summary.total[:count], { size: :sm }, { class: 'js-test-report-badge-counter' }
- = render_if_exists "projects/pipelines/tabs_holder", pipeline: @pipeline, project: @project
-
-.tab-content
- #js-tab-pipeline.tab-pane.gl-w-full
- #js-pipeline-graph-vue
-
- #js-tab-builds.tab-pane
- - if stages.present?
- #js-pipeline-jobs-vue{ data: { full_path: @project.full_path, pipeline_iid: @pipeline.iid } }
-
- - if @pipeline.failed_builds.present?
- #js-tab-failures.tab-pane
- #js-pipeline-failed-jobs-vue{ data: { full_path: @project.full_path, pipeline_iid: @pipeline.iid, failed_jobs_summary_data: prepare_failed_jobs_summary_data(@pipeline.failed_builds) } }
-
- #js-tab-dag.tab-pane
- #js-pipeline-dag-vue{ data: { pipeline_project_path: @project.full_path, pipeline_iid: @pipeline.iid, empty_svg_path: image_path('illustrations/empty-state/empty-dag-md.svg'), about_dag_doc_path: help_page_path('ci/directed_acyclic_graph/index.md'), dag_doc_path: help_page_path('ci/yaml/index.md', anchor: 'needs')} }
-
- #js-tab-tests.tab-pane
- #js-pipeline-tests-detail{ data: { summary_endpoint: summary_project_pipeline_tests_path(@project, @pipeline, format: :json),
- suite_endpoint: project_pipeline_test_path(@project, @pipeline, suite_name: 'suite', format: :json),
- blob_path: project_blob_path(@project, @pipeline.sha),
- has_test_report: @pipeline.complete_and_has_reports?(Ci::JobArtifact.of_report_type(:test)).to_s,
- empty_state_image_path: image_path('illustrations/empty-state/empty-test-cases-lg.svg'),
- artifacts_expired_image_path: image_path('illustrations/pipeline.svg') } }
- = render_if_exists "projects/pipelines/tabs_content", pipeline: @pipeline, project: @project
diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml
index 4531bb2d0a9..9b0a81a2f60 100644
--- a/app/views/projects/pipelines/show.html.haml
+++ b/app/views/projects/pipelines/show.html.haml
@@ -9,7 +9,7 @@
- add_page_startup_graphql_call('pipelines/get_pipeline_details', { projectPath: @project.full_path, iid: @pipeline.iid })
.js-pipeline-container{ data: { controller_action: "#{controller.action_name}" } }
- #js-pipeline-header-vue.pipeline-header-container{ data: { full_path: @project.full_path, pipeline_iid: @pipeline.iid, pipeline_id: @pipeline.id, pipelines_path: project_pipelines_path(@project) } }
+ #js-pipeline-header-vue.pipeline-header-container{ data: { full_path: @project.full_path, graphql_resource_etag: graphql_etag_pipeline_path(@pipeline), pipeline_iid: @pipeline.iid, pipeline_id: @pipeline.id, pipelines_path: project_pipelines_path(@project) } }
= render_if_exists 'projects/pipelines/cc_validation_required_alert', pipeline: @pipeline
@@ -18,16 +18,10 @@
- if pipeline_has_errors
.bs-callout.bs-callout-danger
- %h4= _('Found errors in your %{gitlab_ci_yml}:') % { gitlab_ci_yml: '.gitlab-ci.yml' }
+ %h4= _('Unable to create pipeline')
%ul
- @pipeline.yaml_errors.split("\n").each do |error|
%li= error
- - lint_link_url = project_ci_pipeline_editor_path(@project, tab: "LINT_TAB")
- - lint_link_start = '<a href="%{url}" class="gl-text-blue-500!">'.html_safe % { url: lint_link_url }
- = s_('You can also test your %{gitlab_ci_yml} in %{lint_link_start}CI Lint%{lint_link_end}').html_safe % { gitlab_ci_yml: '.gitlab-ci.yml', lint_link_start: lint_link_start, lint_link_end: '</a>'.html_safe }
- - if Feature.enabled?(:pipeline_tabs_vue, @project)
- #js-pipeline-tabs{ data: js_pipeline_tabs_data(@project, @pipeline, @current_user) }
- else
- = render "projects/pipelines/with_tabs", pipeline: @pipeline, stages: @stages, pipeline_has_errors: pipeline_has_errors
-.js-pipeline-details-vue{ data: { metrics_path: namespace_project_ci_prometheus_metrics_histograms_path(namespace_id: @project.namespace, project_id: @project, format: :json), pipeline_project_path: @project.full_path, pipeline_iid: @pipeline.iid, graphql_resource_etag: graphql_etag_pipeline_path(@pipeline), pipeline_path: pipeline_path(@pipeline) } }
+ #js-pipeline-tabs{ data: js_pipeline_tabs_data(@project, @pipeline, @current_user) }
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index c7818602f52..4ac0e28d386 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -15,17 +15,17 @@
- invite_group_top_margin = ''
- if can_admin_project_member?(@project)
.js-import-project-members-trigger{ data: { classes: 'gl-md-w-auto gl-w-full' } }
- .js-import-project-members-modal{ data: { project_id: @project.id, project_name: @project.name } }
+ .js-import-project-members-modal{ data: { project_id: @project.id, project_name: @project.name, reload_page_on_submit: true.to_s } }
- invite_group_top_margin = 'gl-md-mt-0 gl-mt-3'
- if @project.allowed_to_share_with_group?
.js-invite-group-trigger{ data: { classes: "gl-md-w-auto gl-w-full gl-md-ml-3 #{invite_group_top_margin}", display_text: _('Invite a group') } }
- = render 'projects/invite_groups_modal', project: @project
+ = render 'projects/invite_groups_modal', project: @project, reload_page_on_submit: true
- if can_admin_project_member?(@project)
.js-invite-members-trigger{ data: { variant: 'confirm',
classes: 'gl-md-w-auto gl-w-full gl-md-ml-3 gl-md-mt-0 gl-mt-3',
trigger_source: 'project-members-page',
display_text: _('Invite members') } }
- = render 'projects/invite_members_modal', project: @project
+ = render 'projects/invite_members_modal', project: @project, reload_page_on_submit: true
- else
- if project_can_be_shared?
%h4
diff --git a/app/views/projects/protected_branches/_branches_list.html.haml b/app/views/projects/protected_branches/_branches_list.html.haml
deleted file mode 100644
index 24d2b971472..00000000000
--- a/app/views/projects/protected_branches/_branches_list.html.haml
+++ /dev/null
@@ -1,4 +0,0 @@
-- can_admin_project = can?(current_user, :admin_project, @project)
-
-= render layout: 'projects/protected_branches/shared/branches_list', locals: { can_admin_project: can_admin_project } do
- = render partial: 'projects/protected_branches/protected_branch', collection: @protected_branches
diff --git a/app/views/projects/protected_branches/_create_protected_branch.html.haml b/app/views/projects/protected_branches/_create_protected_branch.html.haml
deleted file mode 100644
index 76aadc3be28..00000000000
--- a/app/views/projects/protected_branches/_create_protected_branch.html.haml
+++ /dev/null
@@ -1,14 +0,0 @@
-- content_for :merge_access_levels do
- .merge_access_levels-container
- = dropdown_tag(_('Select'),
- options: { toggle_class: 'js-allowed-to-merge wide',
- dropdown_class: 'dropdown-menu-selectable capitalize-header', dropdown_qa_selector: 'allowed_to_merge_dropdown_content', dropdown_testid: 'allowed-to-merge-dropdown',
- data: { field_name: 'protected_branch[merge_access_levels_attributes][0][access_level]', input_id: 'merge_access_levels_attributes', qa_selector: 'allowed_to_merge_dropdown' }})
-- content_for :push_access_levels do
- .push_access_levels-container
- = dropdown_tag(_('Select'),
- options: { toggle_class: "js-allowed-to-push js-multiselect wide",
- dropdown_class: 'dropdown-menu-selectable capitalize-header', dropdown_qa_selector: 'allowed_to_push_dropdown_content' , dropdown_testid: 'allowed-to-push-dropdown',
- data: { field_name: 'protected_branch[push_access_levels_attributes][0][access_level]', input_id: 'push_access_levels_attributes', qa_selector: 'allowed_to_push_dropdown' }})
-
-= render 'projects/protected_branches/shared/create_protected_branch'
diff --git a/app/views/projects/protected_branches/_index.html.haml b/app/views/projects/protected_branches/_index.html.haml
deleted file mode 100644
index 2b0a502fe4d..00000000000
--- a/app/views/projects/protected_branches/_index.html.haml
+++ /dev/null
@@ -1,7 +0,0 @@
-- content_for :create_protected_branch do
- = render 'projects/protected_branches/create_protected_branch'
-
-- content_for :branches_list do
- = render "projects/protected_branches/branches_list"
-
-= render 'projects/protected_branches/shared/index'
diff --git a/app/views/projects/protected_branches/_protected_branch.html.haml b/app/views/projects/protected_branches/_protected_branch.html.haml
deleted file mode 100644
index 366d7a7a2eb..00000000000
--- a/app/views/projects/protected_branches/_protected_branch.html.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-= render layout: 'projects/protected_branches/shared/protected_branch', locals: { protected_branch: protected_branch } do
- = render_if_exists 'projects/protected_branches/update_protected_branch', protected_branch: protected_branch
diff --git a/app/views/projects/protected_branches/_update_protected_branch.html.haml b/app/views/projects/protected_branches/_update_protected_branch.html.haml
deleted file mode 100644
index b2ec98be056..00000000000
--- a/app/views/projects/protected_branches/_update_protected_branch.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-= render 'shared/projects/protected_branches/update_protected_branch', protected_branch: protected_branch
diff --git a/app/views/projects/protected_branches/shared/_branches_list.html.haml b/app/views/projects/protected_branches/shared/_branches_list.html.haml
deleted file mode 100644
index 64db51d5df2..00000000000
--- a/app/views/projects/protected_branches/shared/_branches_list.html.haml
+++ /dev/null
@@ -1,38 +0,0 @@
-.protected-branches-list.js-protected-branches-list{ data: { testid: 'protected-branches-list' } }
- - if @protected_branches.empty?
- .card-header.bg-white
- = s_("ProtectedBranch|Protected branch (%{protected_branches_count})") % { protected_branches_count: 0 }
- %p.settings-message.text-center
- = s_("ProtectedBranch|There are currently no protected branches, protect a branch with the form above.")
- - else
- .flash-container
- %table.table.table-bordered
- %colgroup
- %col{ width: "30%" }
- %col{ width: "20%" }
- %col{ width: "20%" }
- %col{ width: "10%" }
- %col{ width: "10%" }
- - if can_admin_project
- %col
- %thead
- %tr
- %th
- = s_("ProtectedBranch|Branch")
- %th
- = s_("ProtectedBranch|Allowed to merge")
- %th
- = s_("ProtectedBranch|Allowed to push")
- %th
- = s_("ProtectedBranch|Allowed to force push")
- %span.has-tooltip{ data: { container: 'body' }, title: s_('ProtectedBranch|Allow all users with push access to force push.'), 'aria-hidden': 'true' }
- = sprite_icon('question', size: 16, css_class: 'gl-text-gray-500')
-
- = render_if_exists 'projects/protected_branches/ee/code_owner_approval_table_head'
-
- - if can_admin_project
- %th
- %tbody
- = yield
-
- = paginate @protected_branches, theme: 'gitlab'
diff --git a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
deleted file mode 100644
index 770d79943b3..00000000000
--- a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
+++ /dev/null
@@ -1,35 +0,0 @@
-= gitlab_ui_form_for [@project, @protected_branch], html: { class: 'new-protected-branch js-new-protected-branch' } do |f|
- %input{ type: 'hidden', name: 'update_section', value: 'js-protected-branches-settings' }
- = render Pajamas::CardComponent.new(card_options: { class: "gl-mb-5" }) do |c|
- - c.header do
- = s_("ProtectedBranch|Protect a branch")
- - c.body do
- = form_errors(@protected_branch)
- .form-group.row
- = f.label :name, s_('ProtectedBranch|Branch:'), class: 'col-sm-12'
- .col-sm-12
- = render partial: "projects/protected_branches/shared/dropdown", locals: { f: f, toggle_classes: 'gl-w-full! gl-form-input-lg' }
- .form-text.text-muted
- - wildcards_url = help_page_url('user/project/protected_branches', anchor: 'configure-multiple-protected-branches-by-using-a-wildcard')
- - wildcards_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: wildcards_url }
- = (s_("ProtectedBranch|%{wildcards_link_start}Wildcards%{wildcards_link_end} such as %{code_tag_start}*-stable%{code_tag_end} or %{code_tag_start}production/*%{code_tag_end} are supported.") % { wildcards_link_start: wildcards_link_start, wildcards_link_end: '</a>', code_tag_start: '<code>', code_tag_end: '</code>' }).html_safe
- .form-group.row
- = f.label :merge_access_levels_attributes, s_("ProtectedBranch|Allowed to merge:"), class: 'col-sm-12'
- .col-sm-12
- = yield :merge_access_levels
- .form-group.row
- = f.label :push_access_levels_attributes, s_("ProtectedBranch|Allowed to push:"), class: 'col-sm-12'
- .col-sm-12
- = yield :push_access_levels
- .form-group.row
- = f.label :allow_force_push, s_("ProtectedBranch|Allowed to force push:"), class: 'col-sm-12'
- .col-sm-12
- = render Pajamas::ToggleComponent.new(classes: 'js-force-push-toggle',
- label: s_("ProtectedBranch|Allowed to force push"),
- label_position: :hidden) do
- - force_push_docs_url = help_page_url('topics/git/git_rebase', anchor: 'force-push')
- - force_push_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: force_push_docs_url }
- = (s_("ProtectedBranch|Allow all users with push access to %{tag_start}force push%{tag_end}.") % { tag_start: force_push_link_start, tag_end: '</a>' }).html_safe
- = render_if_exists 'projects/protected_branches/ee/code_owner_approval_form', f: f
- - c.footer do
- = f.submit s_('ProtectedBranch|Protect'), disabled: true, data: { qa_selector: 'protect_button' }, pajamas_button: true
diff --git a/app/views/projects/protected_branches/shared/_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_protected_branch.html.haml
deleted file mode 100644
index 098bd4a7eeb..00000000000
--- a/app/views/projects/protected_branches/shared/_protected_branch.html.haml
+++ /dev/null
@@ -1,23 +0,0 @@
-- can_admin_project = can?(current_user, :admin_project, @project)
-
-%tr.js-protected-branch-edit-form{ data: { url: namespace_project_protected_branch_path(@project.namespace, @project, protected_branch), testid: 'protected-branch' } }
- %td
- %span.ref-name= protected_branch.name
-
- - if @project.root_ref?(protected_branch.name)
- = gl_badge_tag s_('ProtectedBranch|default'), variant: :info
-
- %div
- - if protected_branch.wildcard?
- - matching_branches = protected_branch.matching(repository.branch_names)
- = link_to pluralize(matching_branches.count, "matching branch"), namespace_project_protected_branch_path(@project.namespace, @project, protected_branch)
- - elsif !protected_branch.commit
- %span.text-muted Branch was deleted.
-
- = yield
-
- = render_if_exists 'projects/protected_branches/ee/code_owner_approval_table', protected_branch: protected_branch
-
- - if can_admin_project
- %td
- = link_to s_('ProtectedBranch|Unprotect'), [@project, protected_branch, { update_section: 'js-protected-branches-settings' }], disabled: local_assigns[:disabled], aria: { label: s_('ProtectedBranch|Unprotect branch') }, data: { confirm: s_('ProtectedBranch|Branch will be writable for developers. Are you sure?'), confirm_btn_variant: 'danger' }, method: :delete, class: "btn gl-button btn-danger btn-sm"
diff --git a/app/views/projects/protected_branches/show.html.haml b/app/views/projects/protected_branches/show.html.haml
deleted file mode 100644
index c671757a603..00000000000
--- a/app/views/projects/protected_branches/show.html.haml
+++ /dev/null
@@ -1,25 +0,0 @@
-- page_title @protected_ref.name, _("Protected Branches")
-
-.row.gl-mt-3.gl-mb-3
- .col-lg-3
- %h4.gl-mt-0.ref-name
- = @protected_ref.name
-
- .col-lg-9
- %h5 Matching Branches
- - if @matching_refs.present?
- .table-responsive
- %table.table.protected-branches-list
- %colgroup
- %col{ width: "30%" }
- %col{ width: "30%" }
- %thead
- %tr
- %th Branch
- %th Last commit
- %tbody
- - @matching_refs.each do |matching_branch|
- = render partial: "projects/protected_branches/shared/matching_branch", object: matching_branch
- - else
- %p.settings-message.text-center
- Couldn't find any matching branches.
diff --git a/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml b/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml
index 9ea7f397c0a..1db1da5e428 100644
--- a/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml
+++ b/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml
@@ -1,4 +1,4 @@
-= form_for [@project, @protected_tag], html: { class: 'new-protected-tag js-new-protected-tag' } do |f|
+= gitlab_ui_form_for [@project, @protected_tag], html: { class: 'new-protected-tag js-new-protected-tag' } do |f|
%input{ type: 'hidden', name: 'update_section', value: 'js-protected-tags-settings' }
= render Pajamas::CardComponent.new(card_options: { class: 'gl-mb-5' }) do |c|
- c.header do
@@ -20,4 +20,4 @@
= yield :create_access_levels
- c.footer do
- = f.submit _('Protect'), class: 'gl-button btn btn-confirm', disabled: true, data: { qa_selector: 'protect_tag_button' }
+ = f.submit _('Protect'), pajamas_button: true, disabled: true, data: { qa_selector: 'protect_tag_button' }
diff --git a/app/views/projects/registry/repositories/index.html.haml b/app/views/projects/registry/repositories/index.html.haml
index 51f0b6319a1..910aab6da72 100644
--- a/app/views/projects/registry/repositories/index.html.haml
+++ b/app/views/projects/registry/repositories/index.html.haml
@@ -1,6 +1,6 @@
- page_title _("Container Registry")
- @content_class = "limit-container-width" unless fluid_layout
-- add_page_startup_graphql_call('container_registry/get_container_repositories', { fullPath: @project.full_path, first: 10, name: nil, isGroupPage: false, sort: nil} )
+- add_page_startup_graphql_call('container_registry/get_container_repositories', { fullPath: @project.full_path, first: 10, name: nil, isGroupPage: false, sort: nil})
%section
#js-container-registry{ data: { endpoint: project_container_registry_index_path(@project),
@@ -15,7 +15,6 @@
"expiration_policy_help_page_path" => help_page_path('user/packages/container_registry/reduce_container_registry_storage', anchor: 'cleanup-policy'),
"garbage_collection_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'container-registry-garbage-collection'),
"run_cleanup_policies_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'run-the-cleanup-policy-now'),
- "container_registry_importing_help_page_path" => help_page_path('user/packages/container_registry/index', anchor: 'tags-temporarily-cannot-be-marked-for-deletion'),
"project_path": @project.full_path,
"gid_prefix": container_repository_gid_prefix,
"is_admin": current_user&.admin.to_s,
diff --git a/app/views/projects/runners/_group_runners.html.haml b/app/views/projects/runners/_group_runners.html.haml
index 5acd6f95df4..d71bcd12e64 100644
--- a/app/views/projects/runners/_group_runners.html.haml
+++ b/app/views/projects/runners/_group_runners.html.haml
@@ -35,7 +35,9 @@
= _('Ask your group owner to set up a group runner.')
- else
- %h4.underlined-title
- = _('Available group runners: %{runners}').html_safe % { runners: @group_runners.count }
- %ul.bordered-list
- = render partial: 'projects/runners/runner', collection: @group_runners, as: :runner
+ %div{ data: { testid: 'group-runners' } }
+ %h5.gl-mt-6.gl-mb-0
+ = _('Available group runners: %{runners}') % { runners: @group_runners_count }
+ %ul.bordered-list
+ = render partial: 'projects/runners/runner', collection: @group_runners, as: :runner
+ = paginate @group_runners, theme: "gitlab", param_name: "group_runners_page", params: { expand_runners: true, anchor: 'js-runners-settings' }
diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml
index 18803bdd8f3..e517b37aae9 100644
--- a/app/views/projects/runners/_runner.html.haml
+++ b/app/views/projects/runners/_runner.html.haml
@@ -7,7 +7,7 @@
- else
%span
= "##{runner.id} (#{runner.short_sha})"
- - if runner.locked?
+ - if runner.locked? && runner.project_type?
%span.has-tooltip{ title: s_('Runners|Runner is locked and available for currently assigned projects only. Only administrators can change the assigned projects.') }
= sprite_icon('lock')
.gl-ml-2
diff --git a/app/views/projects/runners/_shared_runners.html.haml b/app/views/projects/runners/_shared_runners.html.haml
index 4689e70d907..9e7bbd6cefe 100644
--- a/app/views/projects/runners/_shared_runners.html.haml
+++ b/app/views/projects/runners/_shared_runners.html.haml
@@ -5,6 +5,9 @@
- if @shared_runners_count == 0
= _('This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area.')
- else
- %h5.gl-mt-6.gl-mb-0 #{_('Available shared runners:')} #{@shared_runners_count}
- %ul.bordered-list.available-shared-runners
- = render partial: 'projects/runners/runner', collection: @shared_runners, as: :runner
+ %div{ data: { testid: 'available-shared-runners' } }
+ %h5.gl-mt-6.gl-mb-0
+ = s_('Runners|Available shared runners: %{count}') % {count: @shared_runners_count}
+ %ul.bordered-list
+ = render partial: 'projects/runners/runner', collection: @shared_runners, as: :runner
+ = paginate @shared_runners, theme: "gitlab", param_name: "shared_runners_page", params: { expand_runners: true, anchor: 'js-runners-settings' }
diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml
index 3634bacb6ec..f3a7037bdab 100644
--- a/app/views/projects/runners/_specific_runners.html.haml
+++ b/app/views/projects/runners/_specific_runners.html.haml
@@ -17,7 +17,7 @@
group_path: '' }
- else
= _('Please contact an admin to register runners.')
- = link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'prevent-users-from-registering-runners'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'restrict-runner-registration-by-all-users-in-an-instance'), target: '_blank', rel: 'noopener noreferrer'
%hr
diff --git a/app/views/projects/settings/_general.html.haml b/app/views/projects/settings/_general.html.haml
index 3a62c6f41cc..5f1dee39e25 100644
--- a/app/views/projects/settings/_general.html.haml
+++ b/app/views/projects/settings/_general.html.haml
@@ -1,5 +1,5 @@
- hidden_topics_field_id = 'project_topic_list_field'
-= form_for [@project], html: { multipart: true, class: "edit-project js-general-settings-form" }, authenticity_token: true do |f|
+= gitlab_ui_form_for [@project], html: { multipart: true, class: "edit-project js-general-settings-form" }, authenticity_token: true do |f|
%input{ name: 'update_section', type: 'hidden', value: 'js-general-settings' }
%fieldset
@@ -39,4 +39,4 @@
%hr
= link_to _('Remove avatar'), project_avatar_path(@project), aria: { label: _('Remove avatar') }, data: { confirm: _('Avatar will be removed. Are you sure?'), 'confirm-btn-variant': 'danger' }, method: :delete, class: 'gl-button btn btn-danger-secondary'
- = f.submit _('Save changes'), class: "gl-button btn btn-confirm gl-mt-6", data: { qa_selector: 'save_naming_topics_avatar_button' }
+ = f.submit _('Save changes'), pajamas_button: true, class: "gl-mt-6", data: { qa_selector: 'save_naming_topics_avatar_button' }
diff --git a/app/views/projects/settings/branch_rules/index.html.haml b/app/views/projects/settings/branch_rules/index.html.haml
index 571a992a552..80a41bb579b 100644
--- a/app/views/projects/settings/branch_rules/index.html.haml
+++ b/app/views/projects/settings/branch_rules/index.html.haml
@@ -3,4 +3,4 @@
%h3.gl-mb-5= s_('BranchRules|Branch rules details')
-#js-branch-rules{ data: { project_path: @project.full_path, protected_branches_path: project_settings_repository_path(@project, anchor: 'js-protected-branches-settings'), approval_rules_path: project_settings_merge_requests_path(@project, anchor: 'js-merge-request-approval-settings'), status_checks_path: project_settings_merge_requests_path(@project, anchor: 'js-merge-request-settings') } }
+#js-branch-rules{ data: { project_path: @project.full_path, protected_branches_path: project_settings_repository_path(@project, anchor: 'js-protected-branches-settings'), approval_rules_path: project_settings_merge_requests_path(@project, anchor: 'js-merge-request-approval-settings'), status_checks_path: project_settings_merge_requests_path(@project, anchor: 'js-merge-request-settings'), branches_path: project_branches_path(@project) } }
diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
index 5748b4b0330..86238a41f0b 100644
--- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
@@ -10,8 +10,8 @@
- base_domain_link_start = link_start % { url: base_domain_path }
- help_link_continouos = link_to sprite_icon('question-o'), help_page_path('topics/autodevops/stages.md', anchor: 'auto-deploy'), target: '_blank', rel: 'noopener noreferrer'
-- help_link_timed = link_to sprite_icon('question-o'), help_page_path('topics/autodevops/customize.md', anchor: 'timed-incremental-rollout-to-production'), target: '_blank', rel: 'noopener noreferrer'
-- help_link_incremental = link_to sprite_icon('question-o'), help_page_path('topics/autodevops/customize.md', anchor: 'incremental-rollout-to-production'), target: '_blank', rel: 'noopener noreferrer'
+- help_link_timed = link_to sprite_icon('question-o'), help_page_path('topics/autodevops/cicd_variables.md', anchor: 'timed-incremental-rollout-to-production'), target: '_blank', rel: 'noopener noreferrer'
+- help_link_incremental = link_to sprite_icon('question-o'), help_page_path('topics/autodevops/cicd_variables.md', anchor: 'incremental-rollout-to-production'), target: '_blank', rel: 'noopener noreferrer'
.row
.col-lg-12
diff --git a/app/views/projects/settings/operations/_alert_management.html.haml b/app/views/projects/settings/operations/_alert_management.html.haml
index d80f1e4597c..7433e81c11c 100644
--- a/app/views/projects/settings/operations/_alert_management.html.haml
+++ b/app/views/projects/settings/operations/_alert_management.html.haml
@@ -3,7 +3,7 @@
- add_page_specific_style 'page_bundles/alert_management_settings'
- add_page_specific_style 'page_bundles/incident_management_list'
-%section.settings.no-animate#js-alert-management-settings{ class: ('expanded' if expanded) }
+%section.settings.no-animate#js-alert-management-settings{ class: ('expanded' if expanded), data: { qa_selector: 'alerts_settings_content' } }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('Alerts')
diff --git a/app/views/projects/settings/repository/_protected_branches.html.haml b/app/views/projects/settings/repository/_protected_branches.html.haml
index 31630828571..d2356b5df09 100644
--- a/app/views/projects/settings/repository/_protected_branches.html.haml
+++ b/app/views/projects/settings/repository/_protected_branches.html.haml
@@ -1,2 +1,2 @@
-= render "projects/protected_branches/index"
+= render "protected_branches/index"
= render "projects/protected_tags/index"
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 77c44b792ab..5fa70c3af32 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -15,7 +15,7 @@
= render "home_panel"
-- if can?(current_user, :download_code, @project) && @project.repository_languages.present?
+- if can?(current_user, :read_code, @project) && @project.repository_languages.present?
- add_page_startup_graphql_call('repository/path_last_commit', { projectPath: @project.full_path, ref: current_ref, path: current_route_path || "" })
= repository_languages_bar(@project.repository_languages)
@@ -25,8 +25,5 @@
- view_path = @project.default_view
-- if show_auto_devops_callout?(@project)
- = render 'shared/auto_devops_callout'
-
%div{ class: project_child_container_class(view_path) }
= render view_path, is_project_overview: true
diff --git a/app/views/projects/starrers/index.html.haml b/app/views/projects/starrers/index.html.haml
index fe8a6508dd7..23578652862 100644
--- a/app/views/projects/starrers/index.html.haml
+++ b/app/views/projects/starrers/index.html.haml
@@ -1,4 +1,5 @@
- page_title _("Starrers")
+- add_page_specific_style 'page_bundles/users'
.top-area.adjust
.nav-text
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index ed06c90efa8..2f8291d255f 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -2,7 +2,7 @@
- default_ref = params[:ref] || @project.default_branch
- if @error
- = render Pajamas::AlertComponent.new(variant: :danger, dismissible: true ) do |c|
+ = render Pajamas::AlertComponent.new(variant: :danger, dismissible: true) do |c|
= c.body do
= @error
@@ -20,14 +20,9 @@
= label_tag :tag_name, nil
= text_field_tag :tag_name, params[:tag_name], required: true, autofocus: true, class: 'form-control', data: { qa_selector: "tag_name_field" }
.form-group.row
- .col-sm-12.create-from
+ .col-sm-auto.create-from
= label_tag :ref, 'Create from'
- .dropdown
- = hidden_field_tag :ref, default_ref
- = button_tag type: 'button', title: default_ref, class: 'dropdown-menu-toggle wide js-branch-select monospace', required: true, data: { toggle: 'dropdown', selected: default_ref, field_name: 'ref' } do
- .text-left.dropdown-toggle-text= default_ref
- = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
- = render 'shared/ref_dropdown', dropdown_class: 'wide'
+ .js-new-tag-ref-selector{ data: { project_id: @project.id, default_branch_name: default_ref, hidden_input_name: 'ref' } }
.form-text.text-muted
= s_('TagsPage|Existing branch name, tag, or commit SHA')
.form-group.row
@@ -42,5 +37,4 @@
= s_('TagsPage|Create tag')
= render Pajamas::ButtonComponent.new(href: project_tags_path(@project)) do
= s_('TagsPage|Cancel')
--# haml-lint:disable InlineJavaScript
-%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
+
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index 29bdca1c876..fd807350245 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -2,7 +2,7 @@
.tree-ref-container.gl-display-flex.mb-2.mb-md-0
.tree-ref-holder
- = render 'shared/ref_switcher', destination: 'tree', show_create: true
+ #js-tree-ref-switcher{ data: { project_id: @project.id, project_root_path: project_path(@project) } }
#js-repo-breadcrumb{ data: breadcrumb_data_attributes }
diff --git a/app/views/projects/triggers/_form.html.haml b/app/views/projects/triggers/_form.html.haml
index b6b24a0c26a..b621f1ab3ed 100644
--- a/app/views/projects/triggers/_form.html.haml
+++ b/app/views/projects/triggers/_form.html.haml
@@ -6,6 +6,6 @@
%label.label-bold Token
%p.form-control-plaintext= @trigger.token
.form-group
- = f.label :key, "Description", class: "label-bold"
- = f.text_field :description, class: 'form-control gl-form-input', required: true, title: 'Trigger description is required.', placeholder: "Trigger description"
+ = f.label :key, s_("Trigger|Description"), class: "label-bold"
+ = f.text_field :description, class: 'form-control gl-form-input', required: true, title: 'Trigger description is required.', placeholder: s_("Trigger|Trigger description")
= f.submit btn_text, pajamas_button: true
diff --git a/app/views/protected_branches/_branches_list.html.haml b/app/views/protected_branches/_branches_list.html.haml
new file mode 100644
index 00000000000..82eac348f16
--- /dev/null
+++ b/app/views/protected_branches/_branches_list.html.haml
@@ -0,0 +1,4 @@
+- can_admin_project = can?(current_user, :admin_project, @project)
+
+= render layout: 'protected_branches/shared/branches_list', locals: { can_admin_project: can_admin_project } do
+ = render partial: 'protected_branches/protected_branch', collection: @protected_branches
diff --git a/app/views/protected_branches/_create_protected_branch.html.haml b/app/views/protected_branches/_create_protected_branch.html.haml
new file mode 100644
index 00000000000..22a49ba9c7e
--- /dev/null
+++ b/app/views/protected_branches/_create_protected_branch.html.haml
@@ -0,0 +1,14 @@
+- content_for :merge_access_levels do
+ .merge_access_levels-container
+ = dropdown_tag(_('Select'),
+ options: { toggle_class: 'js-allowed-to-merge wide',
+ dropdown_class: 'dropdown-menu-selectable capitalize-header', dropdown_qa_selector: 'allowed_to_merge_dropdown_content', dropdown_testid: 'allowed-to-merge-dropdown',
+ data: { field_name: 'protected_branch[merge_access_levels_attributes][0][access_level]', input_id: 'merge_access_levels_attributes', qa_selector: 'allowed_to_merge_dropdown' }})
+- content_for :push_access_levels do
+ .push_access_levels-container
+ = dropdown_tag(_('Select'),
+ options: { toggle_class: "js-allowed-to-push js-multiselect wide",
+ dropdown_class: 'dropdown-menu-selectable capitalize-header', dropdown_qa_selector: 'allowed_to_push_dropdown_content' , dropdown_testid: 'allowed-to-push-dropdown',
+ data: { field_name: 'protected_branch[push_access_levels_attributes][0][access_level]', input_id: 'push_access_levels_attributes', qa_selector: 'allowed_to_push_dropdown' }})
+
+= render 'protected_branches/shared/create_protected_branch'
diff --git a/app/views/protected_branches/_index.html.haml b/app/views/protected_branches/_index.html.haml
new file mode 100644
index 00000000000..4beca4845b8
--- /dev/null
+++ b/app/views/protected_branches/_index.html.haml
@@ -0,0 +1,7 @@
+- content_for :create_protected_branch do
+ = render 'protected_branches/create_protected_branch'
+
+- content_for :branches_list do
+ = render "protected_branches/branches_list"
+
+= render 'protected_branches/shared/index'
diff --git a/app/views/protected_branches/_protected_branch.html.haml b/app/views/protected_branches/_protected_branch.html.haml
new file mode 100644
index 00000000000..423d7f23eb5
--- /dev/null
+++ b/app/views/protected_branches/_protected_branch.html.haml
@@ -0,0 +1,2 @@
+= render layout: 'protected_branches/shared/protected_branch', locals: { protected_branch: protected_branch } do
+ = render_if_exists 'protected_branches/update_protected_branch', protected_branch: protected_branch
diff --git a/app/views/protected_branches/_update_protected_branch.html.haml b/app/views/protected_branches/_update_protected_branch.html.haml
new file mode 100644
index 00000000000..a9290d9e0da
--- /dev/null
+++ b/app/views/protected_branches/_update_protected_branch.html.haml
@@ -0,0 +1 @@
+= render 'protected_branches/shared/update_protected_branch', protected_branch: protected_branch
diff --git a/app/views/protected_branches/shared/_branches_list.html.haml b/app/views/protected_branches/shared/_branches_list.html.haml
new file mode 100644
index 00000000000..d041f9c5b48
--- /dev/null
+++ b/app/views/protected_branches/shared/_branches_list.html.haml
@@ -0,0 +1,38 @@
+.protected-branches-list.js-protected-branches-list{ data: { testid: 'protected-branches-list' } }
+ - if @protected_branches.empty?
+ .card-header.bg-white
+ = s_("ProtectedBranch|Protected branch (%{protected_branches_count})") % { protected_branches_count: 0 }
+ %p.settings-message.text-center
+ = s_("ProtectedBranch|There are currently no protected branches, protect a branch with the form above.")
+ - else
+ .flash-container
+ %table.table.table-bordered
+ %colgroup
+ %col{ width: "30%" }
+ %col{ width: "20%" }
+ %col{ width: "20%" }
+ %col{ width: "10%" }
+ %col{ width: "10%" }
+ - if can_admin_project
+ %col
+ %thead
+ %tr
+ %th
+ = s_("ProtectedBranch|Branch")
+ %th
+ = s_("ProtectedBranch|Allowed to merge")
+ %th
+ = s_("ProtectedBranch|Allowed to push")
+ %th
+ = s_("ProtectedBranch|Allowed to force push")
+ %span.has-tooltip{ data: { container: 'body' }, title: s_('ProtectedBranch|Allow all users with push access to force push.'), 'aria-hidden': 'true' }
+ = sprite_icon('question', size: 16, css_class: 'gl-text-gray-500')
+
+ = render_if_exists 'protected_branches/ee/code_owner_approval_table_head'
+
+ - if can_admin_project
+ %th
+ %tbody
+ = yield
+
+ = paginate @protected_branches, theme: 'gitlab'
diff --git a/app/views/protected_branches/shared/_create_protected_branch.html.haml b/app/views/protected_branches/shared/_create_protected_branch.html.haml
new file mode 100644
index 00000000000..6b4a143df69
--- /dev/null
+++ b/app/views/protected_branches/shared/_create_protected_branch.html.haml
@@ -0,0 +1,35 @@
+= gitlab_ui_form_for [@project, @protected_branch], html: { class: 'new-protected-branch js-new-protected-branch' } do |f|
+ %input{ type: 'hidden', name: 'update_section', value: 'js-protected-branches-settings' }
+ = render Pajamas::CardComponent.new(card_options: { class: "gl-mb-5" }) do |c|
+ - c.header do
+ = s_("ProtectedBranch|Protect a branch")
+ - c.body do
+ = form_errors(@protected_branch)
+ .form-group.row
+ = f.label :name, s_('ProtectedBranch|Branch:'), class: 'col-sm-12'
+ .col-sm-12
+ = render partial: "protected_branches/shared/dropdown", locals: { f: f, toggle_classes: 'gl-w-full! gl-form-input-lg' }
+ .form-text.text-muted
+ - wildcards_url = help_page_url('user/project/protected_branches', anchor: 'configure-multiple-protected-branches-by-using-a-wildcard')
+ - wildcards_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: wildcards_url }
+ = (s_("ProtectedBranch|%{wildcards_link_start}Wildcards%{wildcards_link_end} such as %{code_tag_start}*-stable%{code_tag_end} or %{code_tag_start}production/*%{code_tag_end} are supported.") % { wildcards_link_start: wildcards_link_start, wildcards_link_end: '</a>', code_tag_start: '<code>', code_tag_end: '</code>' }).html_safe
+ .form-group.row
+ = f.label :merge_access_levels_attributes, s_("ProtectedBranch|Allowed to merge:"), class: 'col-sm-12'
+ .col-sm-12
+ = yield :merge_access_levels
+ .form-group.row
+ = f.label :push_access_levels_attributes, s_("ProtectedBranch|Allowed to push:"), class: 'col-sm-12'
+ .col-sm-12
+ = yield :push_access_levels
+ .form-group.row
+ = f.label :allow_force_push, s_("ProtectedBranch|Allowed to force push:"), class: 'col-sm-12'
+ .col-sm-12
+ = render Pajamas::ToggleComponent.new(classes: 'js-force-push-toggle',
+ label: s_("ProtectedBranch|Allowed to force push"),
+ label_position: :hidden) do
+ - force_push_docs_url = help_page_url('topics/git/git_rebase', anchor: 'force-push')
+ - force_push_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: force_push_docs_url }
+ = (s_("ProtectedBranch|Allow all users with push access to %{tag_start}force push%{tag_end}.") % { tag_start: force_push_link_start, tag_end: '</a>' }).html_safe
+ = render_if_exists 'protected_branches/ee/code_owner_approval_form', f: f
+ - c.footer do
+ = f.submit s_('ProtectedBranch|Protect'), disabled: true, data: { qa_selector: 'protect_button' }, pajamas_button: true
diff --git a/app/views/projects/protected_branches/shared/_dropdown.html.haml b/app/views/protected_branches/shared/_dropdown.html.haml
index c5dbf8991cd..c5dbf8991cd 100644
--- a/app/views/projects/protected_branches/shared/_dropdown.html.haml
+++ b/app/views/protected_branches/shared/_dropdown.html.haml
diff --git a/app/views/projects/protected_branches/shared/_index.html.haml b/app/views/protected_branches/shared/_index.html.haml
index c204508d355..c204508d355 100644
--- a/app/views/projects/protected_branches/shared/_index.html.haml
+++ b/app/views/protected_branches/shared/_index.html.haml
diff --git a/app/views/projects/protected_branches/shared/_matching_branch.html.haml b/app/views/protected_branches/shared/_matching_branch.html.haml
index 1a2ec38fae9..1a2ec38fae9 100644
--- a/app/views/projects/protected_branches/shared/_matching_branch.html.haml
+++ b/app/views/protected_branches/shared/_matching_branch.html.haml
diff --git a/app/views/protected_branches/shared/_protected_branch.html.haml b/app/views/protected_branches/shared/_protected_branch.html.haml
new file mode 100644
index 00000000000..5dea85aaa41
--- /dev/null
+++ b/app/views/protected_branches/shared/_protected_branch.html.haml
@@ -0,0 +1,23 @@
+- can_admin_project = can?(current_user, :admin_project, @project)
+
+%tr.js-protected-branch-edit-form{ data: { url: namespace_project_protected_branch_path(@project.namespace, @project, protected_branch), testid: 'protected-branch' } }
+ %td
+ %span.ref-name= protected_branch.name
+
+ - if @project.root_ref?(protected_branch.name)
+ = gl_badge_tag s_('ProtectedBranch|default'), variant: :info
+
+ %div
+ - if protected_branch.wildcard?
+ - matching_branches = protected_branch.matching(repository.branch_names)
+ = link_to pluralize(matching_branches.count, "matching branch"), namespace_project_protected_branch_path(@project.namespace, @project, protected_branch)
+ - elsif !protected_branch.commit
+ %span.text-muted Branch was deleted.
+
+ = yield
+
+ = render_if_exists 'protected_branches/ee/code_owner_approval_table', protected_branch: protected_branch
+
+ - if can_admin_project
+ %td
+ = link_to s_('ProtectedBranch|Unprotect'), [@project, protected_branch, { update_section: 'js-protected-branches-settings' }], disabled: local_assigns[:disabled], aria: { label: s_('ProtectedBranch|Unprotect branch') }, data: { confirm: s_('ProtectedBranch|Branch will be writable for developers. Are you sure?'), confirm_btn_variant: 'danger' }, method: :delete, class: "btn gl-button btn-danger btn-sm"
diff --git a/app/views/protected_branches/shared/_update_protected_branch.html.haml b/app/views/protected_branches/shared/_update_protected_branch.html.haml
new file mode 100644
index 00000000000..0244f9e2158
--- /dev/null
+++ b/app/views/protected_branches/shared/_update_protected_branch.html.haml
@@ -0,0 +1,40 @@
+- merge_access_levels = protected_branch.merge_access_levels.for_role
+- push_access_levels = protected_branch.push_access_levels.for_role
+
+- user_merge_access_levels = protected_branch.merge_access_levels.for_user
+- user_push_access_levels = protected_branch.push_access_levels.for_user
+
+- group_merge_access_levels = protected_branch.merge_access_levels.for_group
+- group_push_access_levels = protected_branch.push_access_levels.for_group
+
+%td.merge_access_levels-container
+ = hidden_field_tag "allowed_to_merge_#{protected_branch.id}", merge_access_levels.first&.access_level
+ = dropdown_tag((merge_access_levels.first&.humanize || 'Select') ,
+ options: { toggle_class: 'js-allowed-to-merge', dropdown_class: 'dropdown-menu-selectable js-allowed-to-merge-container capitalize-header',
+ data: { field_name: "allowed_to_merge_#{protected_branch.id}", preselected_items: access_levels_data(merge_access_levels) }})
+ - if user_merge_access_levels.any?
+ %p.small
+ = _('The following %{user} can also merge into this branch: %{branch}') % { user: 'user'.pluralize(user_merge_access_levels.size), branch: user_merge_access_levels.map(&:humanize).to_sentence }
+
+ - if group_merge_access_levels.any?
+ %p.small
+ = _('Members of %{group} can also merge into this branch: %{branch}') % { group: (group_merge_access_levels.size > 1 ? 'these groups' : 'this group'), branch: group_merge_access_levels.map(&:humanize).to_sentence }
+
+%td.push_access_levels-container
+ = hidden_field_tag "allowed_to_push_#{protected_branch.id}", push_access_levels.first&.access_level
+ = dropdown_tag((push_access_levels.first&.humanize || 'Select') ,
+ options: { toggle_class: "js-allowed-to-push js-multiselect", dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header',
+ data: { field_name: "allowed_to_push_#{protected_branch.id}", preselected_items: access_levels_data(push_access_levels) }})
+ - if user_push_access_levels.any?
+ %p.small
+ = _('The following %{user} can also push to this branch: %{branch}') % { user: 'user'.pluralize(user_push_access_levels.size), branch: user_push_access_levels.map(&:humanize).to_sentence }
+
+ - if group_push_access_levels.any?
+ %p.small
+ = _('Members of %{group} can also push to this branch: %{branch}') % { group: (group_push_access_levels.size > 1 ? 'these groups' : 'this group'), branch: group_push_access_levels.map(&:humanize).to_sentence }
+
+%td
+ = render Pajamas::ToggleComponent.new(classes: 'js-force-push-toggle',
+ label: s_("ProtectedBranch|Toggle allowed to force push"),
+ is_checked: protected_branch.allow_force_push,
+ label_position: :hidden)
diff --git a/app/views/protected_branches/show.html.haml b/app/views/protected_branches/show.html.haml
new file mode 100644
index 00000000000..e0bd392ae93
--- /dev/null
+++ b/app/views/protected_branches/show.html.haml
@@ -0,0 +1,25 @@
+- page_title @protected_ref.name, _("Protected Branches")
+
+.row.gl-mt-3.gl-mb-3
+ .col-lg-3
+ %h4.gl-mt-0.ref-name
+ = @protected_ref.name
+
+ .col-lg-9
+ %h5 Matching Branches
+ - if @matching_refs.present?
+ .table-responsive
+ %table.table.protected-branches-list
+ %colgroup
+ %col{ width: "30%" }
+ %col{ width: "30%" }
+ %thead
+ %tr
+ %th Branch
+ %th Last commit
+ %tbody
+ - @matching_refs.each do |matching_branch|
+ = render partial: "protected_branches/shared/matching_branch", object: matching_branch
+ - else
+ %p.settings-message.text-center
+ Couldn't find any matching branches.
diff --git a/app/views/pwa/manifest.json.erb b/app/views/pwa/manifest.json.erb
index 557a39ee157..c5403caeafa 100644
--- a/app/views/pwa/manifest.json.erb
+++ b/app/views/pwa/manifest.json.erb
@@ -1,7 +1,7 @@
{
- "name": "GitLab",
- "short_name": "GitLab",
- "description": "<%= _("The complete DevOps platform. One application with endless possibilities. Organizations rely on GitLab’s source code management, CI/CD, security, and more to deliver software rapidly.") %>",
+ "name": "<%= Appearance.current&.title.presence || _('GitLab') %>",
+ "short_name": "<%= Appearance.current&.short_title.presence || _('GitLab') %>",
+ "description": "<%= Appearance.current&.description.presence || _("The complete DevOps platform. One application with endless possibilities. Organizations rely on GitLab’s source code management, CI/CD, security, and more to deliver software rapidly.") %>",
"start_url": "<%= explore_projects_path %>",
"scope": "<%= root_path %>",
"display": "browser",
diff --git a/app/views/registrations/welcome/show.html.haml b/app/views/registrations/welcome/show.html.haml
index 283659875ef..f4e9a597fe2 100644
--- a/app/views/registrations/welcome/show.html.haml
+++ b/app/views/registrations/welcome/show.html.haml
@@ -18,22 +18,24 @@
%p.gl-text-center= html_escape(_('%{gitlab_experience_text}. Don\'t worry, this information isn\'t shared outside of your self-managed GitLab instance.')) % { gitlab_experience_text: gitlab_experience_text }
= gitlab_ui_form_for(current_user,
url: users_sign_up_welcome_path(glm_tracking_params),
- html: { class: 'card gl-w-full! gl-p-5 js-users-signup-welcome',
+ html: { class: 'gl-w-full! gl-p-5 js-users-signup-welcome',
'aria-live' => 'assertive',
data: { testid: 'welcome-form' } }) do |f|
- .devise-errors
- = render 'devise/shared/error_messages', resource: current_user
- .row
- .form-group.col-sm-12
- = f.label :role, _('Role'), class: 'label-bold'
- = f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, { include_blank: _('Select a role') }, class: 'form-control js-user-role-dropdown', autofocus: true, required: true, data: { qa_selector: 'role_dropdown' }
- = render_if_exists "registrations/welcome/jobs_to_be_done", f: f
- = render_if_exists "registrations/welcome/setup_for_company", f: f
- = render_if_exists "registrations/welcome/joining_project"
- = render 'devise/shared/email_opted_in', f: f
- .row
- .form-group.col-sm-12.gl-mb-0
- - if partial_exists? "registrations/welcome/button"
- = render "registrations/welcome/button"
- - else
- = f.submit _('Get started!'), class: 'btn-confirm gl-button btn btn-block gl-mb-0 gl-p-3', data: { qa_selector: 'get_started_button' }
+ = render Pajamas::CardComponent.new do |c|
+ - c.body do
+ .devise-errors
+ = render 'devise/shared/error_messages', resource: current_user
+ .row
+ .form-group.col-sm-12
+ = f.label :role, _('Role'), class: 'label-bold'
+ = f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, { include_blank: _('Select a role') }, class: 'form-control js-user-role-dropdown', autofocus: true, required: true, data: { qa_selector: 'role_dropdown' }
+ = render_if_exists "registrations/welcome/jobs_to_be_done", f: f
+ = render_if_exists "registrations/welcome/setup_for_company", f: f
+ = render_if_exists "registrations/welcome/joining_project"
+ = render 'devise/shared/email_opted_in', f: f
+ .row
+ .form-group.col-sm-12.gl-mb-0
+ - if partial_exists? "registrations/welcome/button"
+ = render "registrations/welcome/button"
+ - else
+ = f.submit _('Get started!'), class: 'btn-confirm gl-button btn btn-block gl-mb-0 gl-p-3', data: { qa_selector: 'get_started_button' }
diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml
index c15afd7bd5b..3e483fe8cd2 100644
--- a/app/views/search/_category.html.haml
+++ b/app/views/search/_category.html.haml
@@ -23,7 +23,7 @@
= search_filter_link 'milestones', _("Milestones")
= users
- - elsif @show_snippets
+ - elsif @search_service.show_snippets?
= search_filter_link 'snippet_titles', _("Titles and Descriptions"), search: { snippets: true, group_id: nil, project_id: nil }
- else
= search_filter_link 'projects', _("Projects"), data: { qa_selector: 'projects_tab' }
diff --git a/app/views/search/results/_issuable.html.haml b/app/views/search/results/_issuable.html.haml
index 36458a909fc..188ead4008e 100644
--- a/app/views/search/results/_issuable.html.haml
+++ b/app/views/search/results/_issuable.html.haml
@@ -13,7 +13,7 @@
= highlight_and_truncate_issuable(issuable, @search_term, @search_highlight)
.col-sm-3.gl-mt-3.gl-sm-mt-0.gl-text-right
- if issuable.respond_to?(:upvotes_count) && issuable.upvotes_count > 0
- %li.issuable-upvotes.gl-list-style-none
+ %li.gl-list-style-none
%span.has-tooltip{ title: _('Upvotes') }
= sprite_icon('thumb-up', css_class: "gl-vertical-align-middle")
= issuable.upvotes_count
diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml
index 9d812e77ad4..e1efa271d57 100644
--- a/app/views/search/show.html.haml
+++ b/app/views/search/show.html.haml
@@ -9,7 +9,7 @@
- project_attributes = @project&.attributes&.slice('id', 'namespace_id', 'name')&.merge(name_with_namespace: @project&.name_with_namespace)
- if @search_results
- - if @without_count
+ - if @search_service.without_count?
- page_description(_("%{scope} results for term '%{term}'") % { scope: @scope, term: @search_term })
- else
- page_description(_("%{count} %{scope} for term '%{term}'") % { count: @search_results.formatted_count(@scope), scope: @scope, term: @search_term })
@@ -20,7 +20,7 @@
= render_if_exists 'search/form_elasticsearch', attrs: { class: 'mb-2 mb-sm-0 align-self-center' }
.gl-mt-3
- #js-search-topbar{ data: { "group-initial-data": group_attributes.to_json, "project-initial-data": project_attributes.to_json } }
+ #js-search-topbar{ data: { "group-initial-json": group_attributes.to_json, "project-initial-json": project_attributes.to_json, "elasticsearch-enabled": @elasticsearch_in_use.to_s, "default-branch-name": @project&.default_branch } }
- if @search_term
- if Feature.disabled?(:search_page_vertical_nav, current_user)
= render 'search/category'
diff --git a/app/views/shared/_auto_devops_callout.html.haml b/app/views/shared/_auto_devops_callout.html.haml
index c2b941c6106..93f919f01d9 100644
--- a/app/views/shared/_auto_devops_callout.html.haml
+++ b/app/views/shared/_auto_devops_callout.html.haml
@@ -1,13 +1,16 @@
-= render Pajamas::BannerComponent.new(button_text: s_('AutoDevOps|Enable in settings'),
- button_link: project_settings_ci_cd_path(@project, anchor: 'autodevops-settings'),
- svg_path: 'illustrations/autodevops.svg',
- banner_options: { class: 'js-autodevops-banner', data: { uid: 'auto_devops_settings_dismissed', project_path: project_path(@project) } },
- close_options: { 'aria-label' => s_('AutoDevOps|Dismiss Auto DevOps box'), class: 'js-close-callout' }) do |c|
- - c.title do
- = s_('AutoDevOps|Auto DevOps')
+- container = @no_breadcrumb_container ? 'container-fluid' : container_class
- %p= s_('AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration.')
+%div{ class: [container, @content_class, 'gl-pt-5!'] }
+ = render Pajamas::BannerComponent.new(button_text: s_('AutoDevOps|Enable in settings'),
+ button_link: project_settings_ci_cd_path(@project, anchor: 'autodevops-settings'),
+ svg_path: 'illustrations/autodevops.svg',
+ banner_options: { class: 'js-autodevops-banner auto-devops-callout', data: { uid: 'auto_devops_settings_dismissed', project_path: project_path(@project) } },
+ close_options: { 'aria-label' => s_('AutoDevOps|Dismiss Auto DevOps box'), class: 'js-close-callout' }) do |c|
+ - c.title do
+ = s_('AutoDevOps|Auto DevOps')
- %p
- - link = link_to(s_('AutoDevOps|Auto DevOps documentation'), help_page_path('topics/autodevops/index.md'), target: '_blank', rel: 'noopener noreferrer')
- = s_('AutoDevOps|Learn more in the %{link_to_documentation}').html_safe % { link_to_documentation: link }
+ %p= s_('AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration.')
+
+ %p
+ - link = link_to(s_('AutoDevOps|Auto DevOps documentation'), help_page_path('topics/autodevops/index.md'), target: '_blank', rel: 'noopener noreferrer')
+ = s_('AutoDevOps|Learn more in the %{link_to_documentation}').html_safe % { link_to_documentation: link }
diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml
index 73ace033dc6..a749d1037a1 100644
--- a/app/views/shared/_file_highlight.html.haml
+++ b/app/views/shared/_file_highlight.html.haml
@@ -1,16 +1,28 @@
+-# We're not using `link_to` in the line loop because it is too slow once we get to thousands of lines.
+
+- offset = defined?(first_line_number) ? first_line_number : 1
+- highlight = defined?(highlight_line) && highlight_line ? highlight_line - offset : nil
+- file_line_blame = Feature.enabled?(:file_line_blame)
+
+- if file_line_blame
+ - line_class = "js-line-links"
+ - blame_path = project_blame_path(@project, tree_join(@ref, blob.path))
+- else
+ - line_class = nil
+ - blame_path = nil
+
+- highlighted_blob = blob.present.highlight
+
#blob-content.file-content.code.js-syntax-highlight
- - offset = defined?(first_line_number) ? first_line_number : 1
- - if Feature.enabled?(:file_line_blame)
- - blame_path = project_blame_path(@project, tree_join(@ref, blob.path))
.line-numbers{ class: "gl-px-0!", data: { blame_path: blame_path } }
- if blob.data.present?
- - blob.data.each_line.each_with_index do |_, index|
+ - highlighted_blob.lines.count.times do |index|
- i = index + offset
- -# We're not using `link_to` because it is too slow once we get to thousands of lines.
- %a.file-line-num.diff-line-num{ class: ("js-line-links" if Feature.enabled?(:file_line_blame)), href: "#L#{i}", id: "L#{i}", 'data-line-number' => i }
+
+ %a.file-line-num.diff-line-num{ class: line_class, href: "#L#{i}", id: "L#{i}", 'data-line-number' => i }
= i
- - highlight = defined?(highlight_line) && highlight_line ? highlight_line - offset : nil
+
.blob-content{ data: { blob_id: blob.id, path: blob.path, highlight_line: highlight, qa_selector: 'file_content' } }
%pre.code.highlight
%code
- = blob.present.highlight
+ = highlighted_blob
diff --git a/app/views/shared/_ide_root.html.haml b/app/views/shared/_ide_root.html.haml
new file mode 100644
index 00000000000..848ff1e5728
--- /dev/null
+++ b/app/views/shared/_ide_root.html.haml
@@ -0,0 +1,11 @@
+- data = local_assigns.fetch(:data)
+- loading_text = local_assigns.fetch(:loading_text)
+
+-# Fix for iOS 13+, the height of the page is actually less than
+-# 100vh because of the presence of the bottom bar
+- @body_class = 'gl-max-h-full gl-fixed'
+
+#ide.gl--flex-center.gl-h-full{ data: data }
+ .gl-text-center
+ = gl_loading_icon(size: 'md')
+ %h2.clgray= loading_text
diff --git a/app/views/shared/_issuable_meta_data.html.haml b/app/views/shared/_issuable_meta_data.html.haml
index 01ab7bf9cd4..982d3b68792 100644
--- a/app/views/shared/_issuable_meta_data.html.haml
+++ b/app/views/shared/_issuable_meta_data.html.haml
@@ -6,23 +6,23 @@
- issuable_mr = @issuable_meta_data[issuable.id].merge_requests_count
- if issuable_mr > 0
- %li.issuable-mr.gl-display-none.gl-sm-display-block.has-tooltip{ title: _('Related merge requests'), data: { testid: 'merge-requests' } }
+ %li.gl-display-none.gl-sm-display-block.has-tooltip{ title: _('Related merge requests'), data: { testid: 'merge-requests' } }
= sprite_icon('merge-request', css_class: "gl-vertical-align-middle")
= issuable_mr
- if upvotes > 0
- %li.issuable-upvotes.gl-display-none.gl-sm-display-block.has-tooltip{ title: _('Upvotes') }
+ %li.gl-display-none.gl-sm-display-block.has-tooltip{ title: _('Upvotes'), data: { testid: 'issuable-upvotes' } }
= sprite_icon('thumb-up', css_class: "gl-vertical-align-middle")
= upvotes
- if downvotes > 0
- %li.issuable-downvotes.gl-display-none.gl-sm-display-block.has-tooltip{ title: _('Downvotes') }
+ %li.gl-display-none.gl-sm-display-block.has-tooltip{ title: _('Downvotes'), data: { testid: 'issuable-downvotes' } }
= sprite_icon('thumb-down', css_class: "gl-vertical-align-middle")
= downvotes
= render_if_exists 'shared/issuable/blocking_issues_count', issuable: issuable
-%li.issuable-comments.gl-display-none.gl-sm-display-block
- = link_to issuable_path, class: ['has-tooltip', ('no-comments' if note_count == 0)], title: _('Comments') do
+%li.gl-display-none.gl-sm-display-block
+ = link_to issuable_path, class: ['has-tooltip', ('no-comments' if note_count == 0)], title: _('Comments'), data: { testid: 'issuable-comments' } do
= sprite_icon('comments', css_class: 'gl-vertical-align-text-bottom')
= note_count
diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml
index 1645c2695b5..8a626f1620b 100644
--- a/app/views/shared/_label.html.haml
+++ b/app/views/shared/_label.html.haml
@@ -32,17 +32,17 @@
- if label.project_label? && label.project.group && can?(current_user, :admin_label, label.project.group)
%li
= render Pajamas::ButtonComponent.new(category: :tertiary,
- button_options: { class: 'js-promote-project-label-button', data: { url: promote_project_label_path(label.project, label), label_title: label.title, label_color: label.color, label_text_color: label.text_color, group_name: label.project.group.name } } ) do
+ button_options: { class: 'js-promote-project-label-button', data: { url: promote_project_label_path(label.project, label), label_title: label.title, label_color: label.color, label_text_color: label.text_color, group_name: label.project.group.name } }) do
= _('Promote to group label')
%li
%span
= render Pajamas::ButtonComponent.new(category: :tertiary,
- button_options: { class: 'text-danger js-delete-label-modal-button', data: { label_name: label.name, subject_name: label.subject_name, destroy_path: label.destroy_path } } ) do
+ button_options: { class: 'text-danger js-delete-label-modal-button', data: { label_name: label.name, subject_name: label.subject_name, destroy_path: label.destroy_path } }) do
= _('Delete')
- if current_user
%li.gl-display-inline-block.label-subscription.js-label-subscription.gl-ml-3
- if label.can_subscribe_to_label_in_different_levels?
- = render Pajamas::ButtonComponent.new(button_options: { class: "js-unsubscribe-button #{'hidden' if status.unsubscribed?}", data: { url: toggle_subscription_path, toggle: 'tooltip', container: 'body' }, title: tooltip_title } ) do
+ = render Pajamas::ButtonComponent.new(button_options: { class: "js-unsubscribe-button #{'hidden' if status.unsubscribed?}", data: { url: toggle_subscription_path, toggle: 'tooltip', container: 'body' }, title: tooltip_title }) do
= _('Unsubscribe')
.dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) }
= render Pajamas::ButtonComponent.new(button_options: { class: 'gl-w-full', data: { toggle: 'dropdown' } }) do
@@ -51,11 +51,11 @@
.dropdown-menu.dropdown-open-left
%ul
%li
- = render Pajamas::ButtonComponent.new(category: :tertiary, button_options: { class: "js-subscribe-button #{'hidden' unless status.unsubscribed?}", data: { status: status, url: toggle_subscription_project_label_path(@project, label) } } ) do
+ = render Pajamas::ButtonComponent.new(category: :tertiary, button_options: { class: "js-subscribe-button #{'hidden' unless status.unsubscribed?}", data: { status: status, url: toggle_subscription_project_label_path(@project, label) } }) do
= _('Subscribe at project level')
%li
- = render Pajamas::ButtonComponent.new(category: :tertiary, button_options: { class: "js-subscribe-button js-group-level #{'hidden' unless status.unsubscribed?}", data: { status: status, url: toggle_subscription_group_label_path(label.group, label) } } ) do
+ = render Pajamas::ButtonComponent.new(category: :tertiary, button_options: { class: "js-subscribe-button js-group-level #{'hidden' unless status.unsubscribed?}", data: { status: status, url: toggle_subscription_group_label_path(label.group, label) } }) do
= _('Subscribe at group level')
- else
- = render Pajamas::ButtonComponent.new(button_options: { class: 'js-subscribe-button gl-w-full', data: { status: status, url: toggle_subscription_path, toggle: 'tooltip', container: 'body' }, title: tooltip_title } ) do
+ = render Pajamas::ButtonComponent.new(button_options: { class: 'js-subscribe-button gl-w-full', data: { status: status, url: toggle_subscription_path, toggle: 'tooltip', container: 'body' }, title: tooltip_title }) do
= label_subscription_toggle_button_text(label, @project)
diff --git a/app/views/shared/_milestones_filter.html.haml b/app/views/shared/_milestones_filter.html.haml
index ef41dc9bb79..0053f2fe444 100644
--- a/app/views/shared/_milestones_filter.html.haml
+++ b/app/views/shared/_milestones_filter.html.haml
@@ -1,6 +1,6 @@
- count_badge_classes = 'gl-display-none gl-sm-display-inline-flex'
-= gl_tabs_nav( {class: 'gl-border-b-0 gl-flex-grow-1', data: { testid: 'milestones-filter' } } ) do
+= gl_tabs_nav({class: 'gl-border-b-0 gl-flex-grow-1', data: { testid: 'milestones-filter' } }) do
= gl_tab_link_to milestones_filter_path(state: 'opened'), { item_active: params[:state].blank? || params[:state] == 'opened' } do
= _('Open')
= gl_tab_counter_badge counts[:opened], { class: count_badge_classes }
diff --git a/app/views/shared/_new_project_item_select.html.haml b/app/views/shared/_new_project_item_select.html.haml
index 0bd5d1795d0..d080d8be8fe 100644
--- a/app/views/shared/_new_project_item_select.html.haml
+++ b/app/views/shared/_new_project_item_select.html.haml
@@ -1,5 +1,5 @@
- if any_projects?(@projects)
- .dropdown.b-dropdown.gl-new-dropdown.btn-group.project-item-select-holder{ class: 'gl-display-inline-flex!' }
+ .dropdown.b-dropdown.gl-dropdown.btn-group.project-item-select-holder{ class: 'gl-display-inline-flex!' }
%a.btn.gl-button.btn-confirm.split-content-button.js-new-project-item-link.block-truncated{ href: '', data: { label: local_assigns[:label], type: local_assigns[:type] } }
= gl_loading_icon(inline: true, color: 'light')
= project_select_tag :project_path, class: "project-item-select gl-absolute! gl-visibility-hidden", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at', relative_path: local_assigns[:path], with_shared: local_assigns[:with_shared], include_projects_in_subgroups: local_assigns[:include_projects_in_subgroups] }, with_feature_enabled: local_assigns[:with_feature_enabled]
diff --git a/app/views/shared/_ref_switcher.html.haml b/app/views/shared/_ref_switcher.html.haml
index 20bf2141cc3..fa718a9c907 100644
--- a/app/views/shared/_ref_switcher.html.haml
+++ b/app/views/shared/_ref_switcher.html.haml
@@ -13,7 +13,7 @@
- @options && @options.each do |key, value|
= hidden_field_tag key, value, id: nil
.dropdown
- = dropdown_toggle dropdown_toggle_text, { toggle: "dropdown", selected: dropdown_toggle_text, ref: ref, refs_url: refs_project_path(@project, sort: 'updated_desc'), field_name: field_name, submit_form_on_click: true, visit: true, qa_selector: "branches_dropdown", testid: "branches-select" }, { toggle_class: "js-project-refs-dropdown" }
+ = dropdown_toggle dropdown_toggle_text, { toggle: "dropdown", selected: dropdown_toggle_text, ref: ref, ref_type: @ref_type, refs_url: refs_project_path(@project, sort: 'updated_desc'), field_name: field_name, submit_form_on_click: true, visit: true, qa_selector: "branches_dropdown", testid: "branches-select" }, { toggle_class: "js-project-refs-dropdown" }
.dropdown-menu.dropdown-menu-selectable.git-revision-dropdown.dropdown-menu-paging{ class: ("dropdown-menu-right" if local_assigns[:align_right]), data: { qa_selector: "branches_dropdown_content" } }
.dropdown-page-one
= dropdown_title _("Switch branch/tag")
diff --git a/app/views/shared/_web_ide_button.html.haml b/app/views/shared/_web_ide_button.html.haml
index 83646a3c92e..aeaccdfa54b 100644
--- a/app/views/shared/_web_ide_button.html.haml
+++ b/app/views/shared/_web_ide_button.html.haml
@@ -2,4 +2,4 @@
- button_data = web_ide_button_data({ blob: blob })
- fork_options = fork_modal_options(@project, @ref, @path, blob)
-.gl-display-inline-block{ data: { options: button_data.merge(fork_options).to_json }, id: "js-#{type}-web-ide-link" }
+.gl-display-inline-block{ data: { options: button_data.merge(fork_options).to_json, web_ide_promo_popover_img: image_path('web-ide-promo-popover.svg') }, id: "js-#{type}-web-ide-link" }
diff --git a/app/views/shared/builds/_tabs.html.haml b/app/views/shared/builds/_tabs.html.haml
index 8e4b8d6d428..8f2b9fc06e3 100644
--- a/app/views/shared/builds/_tabs.html.haml
+++ b/app/views/shared/builds/_tabs.html.haml
@@ -1,6 +1,6 @@
- count_badge_classes = 'gl-display-none gl-sm-display-inline-flex'
-= gl_tabs_nav( {class: 'scrolling-tabs nav-links gl-display-flex gl-flex-grow-1 gl-w-full nav gl-border-b-0', data: { testid: 'jobs-tabs' } } ) do
+= gl_tabs_nav({class: 'scrolling-tabs nav-links gl-display-flex gl-flex-grow-1 gl-w-full nav gl-border-b-0', data: { testid: 'jobs-tabs' } }) do
= gl_tab_link_to build_path_proc.call(nil), { item_active: scope.nil? } do
= _('All')
= gl_tab_counter_badge(limited_counter_with_delimiter(all_builds), { class: count_badge_classes })
diff --git a/app/views/shared/empty_states/_milestones.html.haml b/app/views/shared/empty_states/_milestones.html.haml
index fb69e75370e..0d7dbd1415b 100644
--- a/app/views/shared/empty_states/_milestones.html.haml
+++ b/app/views/shared/empty_states/_milestones.html.haml
@@ -6,7 +6,7 @@
.svg-content
= image_tag 'illustrations/milestone_burndown_chart.svg'
.col-12
- .text-content
+ .text-content.text-center
%h4= s_('Milestones|Use milestones to track issues and merge requests over a fixed period of time')
%p.state-description
= s_('Milestones|Organize issues and merge requests into a cohesive group, and set optional start and due dates. %{learn_more_link}').html_safe % { learn_more_link: learn_more_link }
diff --git a/app/views/shared/empty_states/_milestones_tab.html.haml b/app/views/shared/empty_states/_milestones_tab.html.haml
index f6760b0a3f4..52df30434b4 100644
--- a/app/views/shared/empty_states/_milestones_tab.html.haml
+++ b/app/views/shared/empty_states/_milestones_tab.html.haml
@@ -12,6 +12,6 @@
%h4.text-center= s_('Milestones|There are no closed milestones')
- else
%h4.text-center= s_('Milestones|There are no open milestones')
- %p.state-description
+ %p.state-description.text-center
= s_('Milestones|Create a milestone to better track your issues and merge requests. %{learn_more_link}').html_safe % { learn_more_link: learn_more_link }
= yield
diff --git a/app/views/shared/file_hooks/_index.html.haml b/app/views/shared/file_hooks/_index.html.haml
index d48e9f3d02e..16e89463a4b 100644
--- a/app/views/shared/file_hooks/_index.html.haml
+++ b/app/views/shared/file_hooks/_index.html.haml
@@ -11,15 +11,16 @@
.col-lg-8.gl-mb-3
- if file_hooks.any?
- .card
- .card-header
+ = render Pajamas::CardComponent.new do |c|
+ - c.header do
= _('File Hooks (%{count})') % { count: file_hooks.count }
- %ul.content-list
- - file_hooks.each do |file|
- %li
- .monospace
- = File.basename(file)
-
+ - c.body do
+ %ul.content-list
+ - file_hooks.each do |file|
+ %li
+ .monospace
+ = File.basename(file)
- else
- .card.bg-light.text-center
- .nothing-here-block= _('No file hooks found.')
+ = render Pajamas::CardComponent.new do |c|
+ - c.body do
+ .nothing-here-block= _('No file hooks found.')
diff --git a/app/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml b/app/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml
new file mode 100644
index 00000000000..9fe1400e877
--- /dev/null
+++ b/app/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml
@@ -0,0 +1,4 @@
+- return unless show_security_patch_upgrade_alert?
+
+#js-security-patch-upgrade-alert{ data: { "current_version": Gitlab.version_info } }
+#js-security-patch-upgrade-alert-modal{ data: { "current_version": Gitlab.version_info, "version": gitlab_version_check.to_json } }
diff --git a/app/views/shared/integrations/prometheus/_custom_metrics.html.haml b/app/views/shared/integrations/prometheus/_custom_metrics.html.haml
index 896249c6163..dda84e0fb9e 100644
--- a/app/views/shared/integrations/prometheus/_custom_metrics.html.haml
+++ b/app/views/shared/integrations/prometheus/_custom_metrics.html.haml
@@ -6,12 +6,12 @@
= link_to s_('PrometheusService|More information'), help_page_path('operations/metrics/index.md', anchor: 'adding-custom-metrics'), target: '_blank', rel: "noopener noreferrer"
.col-lg-9
- .card.custom-monitored-metrics.js-panel-custom-monitored-metrics{ data: { qa_selector: 'custom_metrics_container', active_custom_metrics: project_prometheus_metrics_path(project), environments_data: environments_list_data, service_active: "#{integration.active}" } }
+ .card.custom-monitored-metrics.js-panel-custom-monitored-metrics{ data: { active_custom_metrics: project_prometheus_metrics_path(project), environments_data: environments_list_data, service_active: "#{integration.active}" } }
.card-header
%strong
= s_('PrometheusService|Custom metrics')
= gl_badge_tag 0, nil, class: 'js-custom-monitored-count'
- = link_to s_('PrometheusService|New metric'), new_project_prometheus_metric_path(project), class: 'btn gl-button btn-confirm gl-ml-auto js-new-metric-button hidden', data: { qa_selector: 'new_metric_button' }
+ = link_to s_('PrometheusService|New metric'), new_project_prometheus_metric_path(project), class: 'btn gl-button btn-confirm gl-ml-auto js-new-metric-button hidden'
.card-body
.flash-container.hidden
.flash-warning
diff --git a/app/views/shared/integrations/prometheus/_metrics.html.haml b/app/views/shared/integrations/prometheus/_metrics.html.haml
index 8ee0ddfa1b1..c74dbfd8b15 100644
--- a/app/views/shared/integrations/prometheus/_metrics.html.haml
+++ b/app/views/shared/integrations/prometheus/_metrics.html.haml
@@ -25,8 +25,8 @@
.card.hidden.js-panel-missing-env-vars
.card-header
- = sprite_icon('chevron-lg-right', css_class: 'panel-toggle js-panel-toggle-right' )
- = sprite_icon('chevron-lg-down', css_class: 'panel-toggle js-panel-toggle-down hidden' )
+ = sprite_icon('chevron-lg-right', css_class: 'panel-toggle js-panel-toggle-right')
+ = sprite_icon('chevron-lg-down', css_class: 'panel-toggle js-panel-toggle-down hidden')
= s_('PrometheusService|Missing environment variable')
= gl_badge_tag 0, nil, class: 'js-env-var-count'
.card-body.hidden
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index a325ad5f447..07cdbbece8c 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -62,9 +62,9 @@
= sanitize(html_escape(_('Please review the %{linkStart}contribution guidelines%{linkEnd} for this project.')) % { linkStart: contribution_guidelines_start, linkEnd: contribution_guidelines_end })
- if issuable.new_record?
- = form.submit "#{_('Create')} #{issuable.class.model_name.human.downcase}", class: 'gl-button btn btn-confirm gl-mr-2', data: { qa_selector: 'issuable_create_button', track_action: 'click_button', track_label: 'submit_mr', track_value: 0 }
+ = form.submit "#{_('Create')} #{issuable.class.model_name.human.downcase}", pajamas_button: true, class: 'gl-mr-2', data: { qa_selector: 'issuable_create_button', track_action: 'click_button', track_label: 'submit_mr', track_value: 0 }
- else
- = form.submit _('Save changes'), class: 'gl-button btn btn-confirm gl-mr-2', data: { track_action: 'click_button', track_label: 'submit_mr', track_value: 0 }
+ = form.submit _('Save changes'), pajamas_button: true, class: 'gl-mr-2', data: { track_action: 'click_button', track_label: 'submit_mr', track_value: 0 }
- if issuable.new_record?
= link_to _('Cancel'), polymorphic_path([@project, issuable.class]), class: 'btn gl-button btn-default js-reset-autosave'
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 0fd128df997..39a123f4775 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -20,7 +20,7 @@
.js-sidebar-todo-widget-root{ data: { project_path: issuable_sidebar[:project_full_path], iid: issuable_sidebar[:iid], id: issuable_sidebar[:id] } }
= form_for issuable_type, url: issuable_sidebar[:issuable_json_path], remote: true, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f|
- .block.assignee{ class: "#{'gl-mt-3' if !signed_in && moved_sidebar_enabled}", data: { qa_selector: 'assignee_block_container' } }
+ .block.assignee{ class: "#{'gl-mt-3' if !signed_in && moved_sidebar_enabled}", data: { qa_selector: 'assignee_block_container', testid: 'assignee-block-container' } }
= render "shared/issuable/sidebar_assignees", issuable_sidebar: issuable_sidebar, assignees: assignees, signed_in: signed_in
- if issuable_sidebar[:supports_severity]
@@ -101,7 +101,7 @@
.sidebar-collapsed-icon{ data: { toggle: 'tooltip', placement: 'left', container: 'body', boundary: 'viewport' }, title: _('Move issue') }
= sprite_icon('long-arrow')
.dropdown.sidebar-move-issue-dropdown.hide-collapsed
- = render Pajamas::ButtonComponent.new(block: true, button_options: { class: 'js-sidebar-dropdown-toggle js-move-issue', data: { toggle: 'dropdown', display: 'static', track_label: "right_sidebar", track_property: "move_issue", track_action: "click_button", track_value: "" } } ) do
+ = render Pajamas::ButtonComponent.new(block: true, button_options: { class: 'js-sidebar-dropdown-toggle js-move-issue', data: { toggle: 'dropdown', display: 'static', track_label: "right_sidebar", track_property: "move_issue", track_action: "click_button", track_value: "" } }) do
= _('Move issue')
.dropdown-menu.dropdown-menu-selectable.dropdown-extended-height
= dropdown_title(_('Move issue'))
diff --git a/app/views/shared/issuable/form/_title.html.haml b/app/views/shared/issuable/form/_title.html.haml
index 51f49c7ca8e..0f6ef33d532 100644
--- a/app/views/shared/issuable/form/_title.html.haml
+++ b/app/views/shared/issuable/form/_title.html.haml
@@ -4,8 +4,8 @@
- no_issuable_templates = issuable_templates(ref_project, issuable.to_ability_name).empty?
- toggle_wip_link_start = '<a href="" class="js-toggle-wip">'
- toggle_wip_link_end = '</a>'
-- add_wip_text = (_('%{link_start}Start the title with %{draft_snippet}%{link_end} to prevent a merge request draft from merging before it\'s ready.') % { link_start: toggle_wip_link_start, link_end: toggle_wip_link_end, draft_snippet: '<code>Draft:</code>'.html_safe } ).html_safe
-- remove_wip_text = (_('%{link_start}Remove the %{draft_snippet} prefix%{link_end} from the title to allow this merge request to be merged when it\'s ready.' ) % { link_start: toggle_wip_link_start, link_end: toggle_wip_link_end, draft_snippet: '<code>Draft</code>'.html_safe } ).html_safe
+- add_wip_text = (_('%{link_start}Start the title with %{draft_snippet}%{link_end} to prevent a merge request draft from merging before it\'s ready.') % { link_start: toggle_wip_link_start, link_end: toggle_wip_link_end, draft_snippet: '<code>Draft:</code>'.html_safe }).html_safe
+- remove_wip_text = (_('%{link_start}Remove the %{draft_snippet} prefix%{link_end} from the title to allow this merge request to be merged when it\'s ready.') % { link_start: toggle_wip_link_start, link_end: toggle_wip_link_end, draft_snippet: '<code>Draft</code>'.html_safe }).html_safe
%div{ data: { testid: 'issue-title-input-field' } }
= form.text_field :title, required: true, aria: { required: true }, maxlength: 255, autofocus: true,
diff --git a/app/views/shared/issue_type/_details_content.html.haml b/app/views/shared/issue_type/_details_content.html.haml
index 8a9b71fd91e..42f6f7b71a3 100644
--- a/app/views/shared/issue_type/_details_content.html.haml
+++ b/app/views/shared/issue_type/_details_content.html.haml
@@ -29,7 +29,7 @@
- if can?(current_user, :admin_feature_flags_issue_links, @project)
= render_if_exists 'projects/issues/related_feature_flags'
- - if can?(current_user, :download_code, @project)
+ - if can?(current_user, :read_code, @project)
- add_page_startup_api_call related_branches_path
#related-branches{ data: { url: related_branches_path } }
-# This element is filled in using JavaScript.
diff --git a/app/views/shared/nav/_sidebar_submenu.html.haml b/app/views/shared/nav/_sidebar_submenu.html.haml
index 344dafe7c0f..33b48470020 100644
--- a/app/views/shared/nav/_sidebar_submenu.html.haml
+++ b/app/views/shared/nav/_sidebar_submenu.html.haml
@@ -1,5 +1,5 @@
%ul.sidebar-sub-level-items{ class: ('is-fly-out-only' unless sidebar_menu.has_renderable_items?) }
- = nav_link(**sidebar_menu.all_active_routes, html_options: { class: 'fly-out-top-item' } ) do
+ = nav_link(**sidebar_menu.all_active_routes, html_options: { class: 'fly-out-top-item' }) do
%span.fly-out-top-item-container
%strong.fly-out-top-item-name
= sidebar_menu.title
diff --git a/app/views/shared/projects/_dropdown.html.haml b/app/views/shared/projects/_dropdown.html.haml
index 88ac03bf9e3..59f8bf0e875 100644
--- a/app/views/shared/projects/_dropdown.html.haml
+++ b/app/views/shared/projects/_dropdown.html.haml
@@ -1,5 +1,5 @@
- @sort ||= sort_value_latest_activity
-.dropdown.js-project-filter-dropdown-wrap
+.dropdown.js-project-filter-dropdown-wrap.gl-display-inline
= dropdown_toggle(projects_sort_options_hash[@sort], { toggle: 'dropdown', display: 'static' }, { id: 'sort-projects-dropdown' })
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
%li.dropdown-header
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index 908eb2428e8..40cd81ab3da 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -52,7 +52,7 @@
%span.user-access-role.gl-display-block.gl-m-0{ data: { qa_selector: 'user_role_content' } }= Gitlab::Access.human_access(access)
- if !explore_projects_tab?
- = render_if_exists 'compliance_management/compliance_framework/compliance_framework_badge', project: project
+ = render_if_exists 'compliance_management/compliance_framework/compliance_framework_badge', project: project, additional_classes: 'gl-ml-3!'
- if show_last_commit_as_description
.description.gl-display-none.gl-sm-display-block.gl-overflow-hidden.gl-mr-3.gl-mt-2
diff --git a/app/views/shared/projects/_search_bar.html.haml b/app/views/shared/projects/_search_bar.html.haml
deleted file mode 100644
index 5271a5fac09..00000000000
--- a/app/views/shared/projects/_search_bar.html.haml
+++ /dev/null
@@ -1,26 +0,0 @@
-- @sort ||= sort_value_latest_activity
-- project_tab_filter = local_assigns.fetch(:project_tab_filter, "")
-- flex_grow_and_shrink_xs = 'd-flex flex-xs-grow-1 flex-xs-shrink-1 flex-grow-0 flex-shrink-0'
-
-.filtered-search-block.row-content-block.bt-0
- .filtered-search-wrapper.d-flex.gl-flex-nowrap.flex-column.flex-sm-wrap.flex-sm-row.flex-xl-nowrap
- - unless project_tab_filter == :starred
- .filtered-search-nav.mb-2.mb-lg-0{ class: flex_grow_and_shrink_xs }
- = render 'dashboard/projects/nav', project_tab_filter: project_tab_filter
- .filtered-search.d-flex.flex-grow-1.flex-shrink-1.w-100.mb-2.mb-lg-0.ml-0{ class: project_tab_filter == :starred ? "extended-filtered-search-box mb-2 mb-lg-0" : "ml-sm-3" }
- .btn-group.w-100{ role: "group" }
- .btn-group.w-100{ role: "group" }
- .filtered-search-box.m-0
- .filtered-search-box-input-container.pl-2
- = render 'shared/projects/search_form', admin_view: false, search_form_placeholder: _("Search projects...")
- = render Pajamas::ButtonComponent.new(icon: 'search', icon_classes: 'search-icon', button_options: { type: 'submit', form: 'project-filter-form' })
- .filtered-search-dropdown.flex-row.align-items-center.mb-2.m-sm-0#filtered-search-visibility-dropdown{ class: flex_grow_and_shrink_xs }
- .filtered-search-dropdown-label.p-0.pl-sm-3.font-weight-bold
- %span
- = _("Visibility")
- = render 'explore/projects/filter', has_label: true
- .filtered-search-dropdown.flex-row.align-items-center.m-sm-0#filtered-search-sorting-dropdown{ class: flex_grow_and_shrink_xs }
- .filtered-search-dropdown-label.p-0.pl-sm-3.font-weight-bold
- %span
- = _("Sort by")
- = render 'shared/projects/sort_dropdown'
diff --git a/app/views/shared/projects/_search_form.html.haml b/app/views/shared/projects/_search_form.html.haml
index e598343d698..07a6d5bec78 100644
--- a/app/views/shared/projects/_search_form.html.haml
+++ b/app/views/shared/projects/_search_form.html.haml
@@ -1,10 +1,9 @@
-- form_field_classes = local_assigns[:admin_view] || !Feature.enabled?(:project_list_filter_bar) ? 'input-short js-projects-list-filter' : 'gl-w-full! gl-pl-7 '
- placeholder = local_assigns[:search_form_placeholder] ? search_form_placeholder : _('Filter by name')
= form_tag filter_projects_path, method: :get, class: 'project-filter-form', data: { qa_selector: 'project_filter_form_container' }, id: 'project-filter-form' do |f|
= search_field_tag :name, params[:name],
placeholder: placeholder,
- class: "project-filter-form-field form-control #{form_field_classes}",
+ class: "project-filter-form-field form-control input-short js-projects-list-filter",
spellcheck: false,
id: 'project-filter-form-field',
autofocus: local_assigns[:autofocus]
@@ -24,4 +23,22 @@
- if params[:visibility_level].present?
= hidden_field_tag :visibility_level, params[:visibility_level]
+ - if params[:language].present?
+ = hidden_field_tag :language, params[:language]
+
+ - if Feature.enabled?(:project_language_search, current_user)
+ .dropdown.inline
+ = dropdown_toggle(search_language_placeholder, { toggle: 'dropdown', testid: 'project-language-dropdown' })
+ %ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
+ %li
+ = link_to _('Any'), filter_projects_path(language: nil)
+ - programming_languages.each do |language|
+ %li
+ = link_to filter_projects_path(language: language.id), class: language_state_class(language) do
+ = language.name
+
+ = submit_tag nil, class: 'gl-display-none!'
+
+ = render 'shared/projects/dropdown'
+
= render_if_exists 'shared/projects/search_fields'
diff --git a/app/views/shared/projects/_sort_dropdown.html.haml b/app/views/shared/projects/_sort_dropdown.html.haml
deleted file mode 100644
index f3aeaacbdb1..00000000000
--- a/app/views/shared/projects/_sort_dropdown.html.haml
+++ /dev/null
@@ -1,39 +0,0 @@
-- @sort ||= sort_value_latest_activity
-- toggle_text = projects_sort_option_titles[@sort]
-
-.btn-group.w-100{ role: "group" }
- .btn-group.w-100.dropdown.js-project-filter-dropdown-wrap{ role: "group" }
- %button#sort-projects-dropdown.gl-button.btn.btn-default.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' } }
- = toggle_text
- = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
- %ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
- %li.dropdown-header
- = _("Sort by")
- - projects_sort_options_hash.each do |value, title|
- %li
- = link_to title, filter_projects_path(sort: value), class: ("is-active" if toggle_text == title)
-
- %li.divider
- %li
- = link_to filter_projects_path(archived: nil), class: ("is-active" unless params[:archived].present?) do
- = _("Hide archived projects")
- %li
- = link_to filter_projects_path(archived: true), class: ("is-active" if Gitlab::Utils.to_boolean(params[:archived])) do
- = _("Show archived projects")
- %li
- = link_to filter_projects_path(archived: 'only'), class: ("is-active" if params[:archived] == 'only') do
- = _("Show archived projects only")
-
- - if current_user && @group && @group.shared_projects.present?
- %li.divider
- %li
- = link_to filter_projects_path(shared: nil), class: ("is-active" unless params[:shared].present?) do
- = _("All projects")
- %li
- = link_to filter_projects_path(shared: 0), class: ("is-active" if params[:shared] == '0') do
- = _("Hide shared projects")
- %li
- = link_to filter_projects_path(shared: 1), class: ("is-active" if params[:shared] == '1') do
- = _("Hide group projects")
-
- = project_sort_direction_button(@sort)
diff --git a/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml b/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml
deleted file mode 100644
index d10196a83cc..00000000000
--- a/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml
+++ /dev/null
@@ -1,40 +0,0 @@
-- merge_access_levels = protected_branch.merge_access_levels.for_role
-- push_access_levels = protected_branch.push_access_levels.for_role
-
-- user_merge_access_levels = protected_branch.merge_access_levels.for_user
-- user_push_access_levels = protected_branch.push_access_levels.for_user
-
-- group_merge_access_levels = protected_branch.merge_access_levels.for_group
-- group_push_access_levels = protected_branch.push_access_levels.for_group
-
-%td.merge_access_levels-container
- = hidden_field_tag "allowed_to_merge_#{protected_branch.id}", merge_access_levels.first&.access_level
- = dropdown_tag( (merge_access_levels.first&.humanize || 'Select') ,
- options: { toggle_class: 'js-allowed-to-merge', dropdown_class: 'dropdown-menu-selectable js-allowed-to-merge-container capitalize-header',
- data: { field_name: "allowed_to_merge_#{protected_branch.id}", preselected_items: access_levels_data(merge_access_levels) }})
- - if user_merge_access_levels.any?
- %p.small
- = _('The following %{user} can also merge into this branch: %{branch}') % { user: 'user'.pluralize(user_merge_access_levels.size), branch: user_merge_access_levels.map(&:humanize).to_sentence }
-
- - if group_merge_access_levels.any?
- %p.small
- = _('Members of %{group} can also merge into this branch: %{branch}') % { group: (group_merge_access_levels.size > 1 ? 'these groups' : 'this group'), branch: group_merge_access_levels.map(&:humanize).to_sentence }
-
-%td.push_access_levels-container
- = hidden_field_tag "allowed_to_push_#{protected_branch.id}", push_access_levels.first&.access_level
- = dropdown_tag( (push_access_levels.first&.humanize || 'Select') ,
- options: { toggle_class: "js-allowed-to-push js-multiselect", dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header',
- data: { field_name: "allowed_to_push_#{protected_branch.id}", preselected_items: access_levels_data(push_access_levels) }})
- - if user_push_access_levels.any?
- %p.small
- = _('The following %{user} can also push to this branch: %{branch}') % { user: 'user'.pluralize(user_push_access_levels.size), branch: user_push_access_levels.map(&:humanize).to_sentence }
-
- - if group_push_access_levels.any?
- %p.small
- = _('Members of %{group} can also push to this branch: %{branch}') % { group: (group_push_access_levels.size > 1 ? 'these groups' : 'this group'), branch: group_push_access_levels.map(&:humanize).to_sentence }
-
-%td
- = render Pajamas::ToggleComponent.new(classes: 'js-force-push-toggle',
- label: s_("ProtectedBranch|Toggle allowed to force push"),
- is_checked: protected_branch.allow_force_push,
- label_position: :hidden)
diff --git a/app/views/shared/runners/_form.html.haml b/app/views/shared/runners/_form.html.haml
index 024b06fe97a..f4b6c3c3a50 100644
--- a/app/views/shared/runners/_form.html.haml
+++ b/app/views/shared/runners/_form.html.haml
@@ -51,4 +51,4 @@
.col-sm-10
= f.text_field :private_projects_minutes_cost_factor, class: 'form-control'
.form-actions
- = f.submit _('Save changes'), class: 'gl-button btn btn-confirm'
+ = f.submit _('Save changes'), pajamas_button: true
diff --git a/app/views/shared/ssh_keys/_key_delete.html.haml b/app/views/shared/ssh_keys/_key_delete.html.haml
index f8bb0e21f67..4b89b2a0cbf 100644
--- a/app/views/shared/ssh_keys/_key_delete.html.haml
+++ b/app/views/shared/ssh_keys/_key_delete.html.haml
@@ -1,9 +1,7 @@
-- title = _('Delete Key')
-- aria = { label: title }
+- icon = local_assigns[:icon]
+- category = local_assigns[:category] || :primary
-- if defined?(text)
- = button_to text, '#', class: html_class, data: button_data, title: title, aria: aria
-- else
- = button_to '#', class: html_class, data: button_data, title: title, aria: aria do
- %span.sr-only= _('Delete')
- = sprite_icon('remove')
+.gl-p-2
+ = render Pajamas::ButtonComponent.new(variant: :danger, category: category, icon: ('remove' if icon), button_options: { class: 'js-confirm-modal-button', data: button_data }) do
+ - unless icon
+ = _('Delete')
diff --git a/app/views/shared/topics/_search_form.html.haml b/app/views/shared/topics/_search_form.html.haml
index 97343983b3c..2806b2865dd 100644
--- a/app/views/shared/topics/_search_form.html.haml
+++ b/app/views/shared/topics/_search_form.html.haml
@@ -1,6 +1,6 @@
= form_tag page_filter_path, method: :get, class: "topic-filter-form js-topic-filter-form", id: 'topic-filter-form' do |f|
= search_field_tag :search, params[:search],
- placeholder: s_('Filter by name'),
+ placeholder: _('Filter by name'),
class: 'topic-filter-form-field form-control input-short',
spellcheck: false,
id: 'topic-filter-form-field',
diff --git a/app/views/shared/web_hooks/_form.html.haml b/app/views/shared/web_hooks/_form.html.haml
index ecb736dac4f..7eafd6ae092 100644
--- a/app/views/shared/web_hooks/_form.html.haml
+++ b/app/views/shared/web_hooks/_form.html.haml
@@ -1,13 +1,6 @@
= form_errors(hook)
-- if Feature.enabled?(:webhook_form_mask_url)
- .js-vue-webhook-form{ data: webhook_form_data(hook) }
-- else
- .form-group
- = form.label :url, s_('Webhooks|URL'), class: 'label-bold'
- = form.text_field :url, class: 'form-control gl-form-input', placeholder: 'http://example.com/trigger-ci.json'
- %p.form-text.text-muted
- = s_('Webhooks|URL must be percent-encoded if it contains one or more special characters.')
+.js-vue-webhook-form{ data: webhook_form_data(hook) }
.form-group
= form.label :token, s_('Webhooks|Secret token'), class: 'label-bold'
= form.password_field :token, value: hook.masked_token, autocomplete: 'new-password', class: 'form-control gl-form-input'
@@ -19,66 +12,57 @@
= form.label :url, s_('Webhooks|Trigger'), class: 'label-bold'
%ul.list-unstyled
%li.gl-pb-5
- - if Feature.enabled?(:enhanced_webhook_support_regex)
- - is_new_hook = hook.id.nil?
- .js-vue-push-events{ data: { push_events: hook.push_events.to_s, strategy: hook.branch_filter_strategy, is_new_hook: is_new_hook.to_s, push_events_branch_filter: hook.push_events_branch_filter } }
- - else
- = form.gitlab_ui_checkbox_component :push_events, s_('Webhooks|Push events')
- .gl-pl-6
- = form.text_field :push_events_branch_filter, class: 'form-control gl-form-input',
- placeholder: 'Branch name or wildcard pattern to trigger on (leave blank for all)'
- %p.form-text.text-muted.custom-control
- = s_('Webhooks|Push to the repository.')
+ .js-vue-push-events{ data: { push_events: hook.push_events.to_s, strategy: hook.branch_filter_strategy, is_new_hook: hook.new_record?.to_s, push_events_branch_filter: hook.push_events_branch_filter } }
%li.gl-pb-5
= form.gitlab_ui_checkbox_component :tag_push_events,
- s_('Webhooks|Tag push events'),
+ integration_webhook_event_human_name(:tag_push_events),
help_text: s_('Webhooks|A new tag is pushed to the repository.')
%li.gl-pb-5
= form.gitlab_ui_checkbox_component :note_events,
- s_('Webhooks|Comments'),
+ integration_webhook_event_human_name(:note_events),
help_text: s_('Webhooks|A comment is added to an issue or merge request.')
%li.gl-pb-5
= form.gitlab_ui_checkbox_component :confidential_note_events,
- s_('Webhooks|Confidential comments'),
+ integration_webhook_event_human_name(:confidential_note_events),
help_text: s_('Webhooks|A comment is added to a confidential issue.')
%li.gl-pb-5
= form.gitlab_ui_checkbox_component :issues_events,
- s_('Webhooks|Issues events'),
+ integration_webhook_event_human_name(:issues_events),
help_text: s_('Webhooks|An issue is created, updated, closed, or reopened.')
%li.gl-pb-5
= form.gitlab_ui_checkbox_component :confidential_issues_events,
- s_('Webhooks|Confidential issues events'),
+ integration_webhook_event_human_name(:confidential_issues_events),
help_text: s_('Webhooks|A confidential issue is created, updated, closed, or reopened.')
- if @group
= render_if_exists 'groups/hooks/member_events', form: form
= render_if_exists 'groups/hooks/subgroup_events', form: form
%li.gl-pb-5
= form.gitlab_ui_checkbox_component :merge_requests_events,
- s_('Webhooks|Merge request events'),
+ integration_webhook_event_human_name(:merge_requests_events),
help_text: s_('Webhooks|A merge request is created, updated, or merged.')
%li.gl-pb-5
= form.gitlab_ui_checkbox_component :job_events,
- s_('Webhooks|Job events'),
+ integration_webhook_event_human_name(:job_events),
help_text: s_("Webhooks|A job's status changes.")
%li.gl-pb-5
= form.gitlab_ui_checkbox_component :pipeline_events,
- s_('Webhooks|Pipeline events'),
+ integration_webhook_event_human_name(:pipeline_events),
help_text: s_("Webhooks|A pipeline's status changes.")
%li.gl-pb-5
= form.gitlab_ui_checkbox_component :wiki_page_events,
- s_('Webhooks|Wiki page events'),
+ integration_webhook_event_human_name(:wiki_page_events),
help_text: s_('Webhooks|A wiki page is created or updated.')
%li.gl-pb-5
= form.gitlab_ui_checkbox_component :deployment_events,
- s_('Webhooks|Deployment events'),
+ integration_webhook_event_human_name(:deployment_events),
help_text: s_('Webhooks|A deployment starts, finishes, fails, or is canceled.')
%li.gl-pb-5
= form.gitlab_ui_checkbox_component :feature_flag_events,
- s_('Webhooks|Feature flag events'),
+ integration_webhook_event_human_name(:feature_flag_events),
help_text: s_('Webhooks|A feature flag is turned on or off.')
%li.gl-pb-5
= form.gitlab_ui_checkbox_component :releases_events,
- s_('Webhooks|Releases events'),
+ integration_webhook_event_human_name(:releases_events),
help_text: s_('Webhooks|A release is created or updated.')
.form-group
= form.label :enable_ssl_verification, s_('Webhooks|SSL verification'), class: 'label-bold checkbox'
diff --git a/app/views/shared/web_hooks/_hook.html.haml b/app/views/shared/web_hooks/_hook.html.haml
index 529ef47a2cf..c19b518acd6 100644
--- a/app/views/shared/web_hooks/_hook.html.haml
+++ b/app/views/shared/web_hooks/_hook.html.haml
@@ -16,7 +16,7 @@
%div
- hook.class.triggers.each_value do |trigger|
- if hook.public_send(trigger)
- = gl_badge_tag(trigger.to_s.titleize, size: :sm)
+ = gl_badge_tag(integration_webhook_event_human_name(trigger), size: :sm)
= gl_badge_tag(sslBadgeText, size: :sm)
.col-md-4.col-lg-5.text-right-md.gl-mt-2
diff --git a/app/views/shared/web_hooks/_test_button.html.haml b/app/views/shared/web_hooks/_test_button.html.haml
index 3ffa45f01be..7a78a32fe87 100644
--- a/app/views/shared/web_hooks/_test_button.html.haml
+++ b/app/views/shared/web_hooks/_test_button.html.haml
@@ -2,12 +2,12 @@
- hook = local_assigns.fetch(:hook)
- triggers = hook.class.triggers
-.hook-test-button.dropdown.gl-new-dropdown.inline>
+.hook-test-button.dropdown.gl-dropdown.inline>
%button.btn.gl-button{ 'data-toggle' => 'dropdown', class: button_class }
= _('Test')
= sprite_icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right{ role: 'menu' }
- .gl-new-dropdown-inner
+ .gl-dropdown-inner
- triggers.each_value do |event|
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
= link_to_test_hook(hook, event)
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 7cef87ba19f..03ecf8cac22 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -25,7 +25,7 @@
- else
= render Pajamas::ButtonComponent.new(href: new_abuse_report_path(user_id: @user.id, ref_url: request.referer),
icon: 'error',
- button_options: { class: 'gl-flex-grow-1 gl-mx-1 has-tooltip', title: s_('UserProfile|Report abuse'), data: { toggle: 'tooltip', placement: 'bottom', container: 'body' }})
+ button_options: { class: 'gl-flex-grow-1 gl-mx-1 has-tooltip', title: _('Report abuse to administrator'), data: { toggle: 'tooltip', placement: 'bottom', container: 'body' }})
- verified_gpg_keys = @user.gpg_keys.select(&:verified?)
- if verified_gpg_keys.any?
= render Pajamas::ButtonComponent.new(href: user_gpg_keys_path,
diff --git a/app/views/web_ide/remote_ide/index.html.haml b/app/views/web_ide/remote_ide/index.html.haml
new file mode 100644
index 00000000000..f007794d056
--- /dev/null
+++ b/app/views/web_ide/remote_ide/index.html.haml
@@ -0,0 +1,5 @@
+- data = local_assigns.fetch(:data)
+
+- page_title _('Web IDE')
+
+= render partial: 'shared/ide_root', locals: { data: data, loading_text: _('Connecting to the remote environment...') }
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index b9168a65764..652a0021b0f 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -66,6 +66,24 @@
:weight: 3
:idempotent: false
:tags: []
+- :name: batched_background_migrations:database_batched_background_migration_ci_execution
+ :worker_name: Database::BatchedBackgroundMigration::CiExecutionWorker
+ :feature_category: :database
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: false
+ :tags: []
+- :name: batched_background_migrations:database_batched_background_migration_main_execution
+ :worker_name: Database::BatchedBackgroundMigration::MainExecutionWorker
+ :feature_category: :database
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: false
+ :tags: []
- :name: chaos:chaos_cpu_spin
:worker_name: Chaos::CpuSpinWorker
:feature_category: :not_owned
@@ -426,6 +444,15 @@
:weight: 1
:idempotent: false
:tags: []
+- :name: cronjob:export_prune_project_export_jobs
+ :worker_name: Gitlab::Export::PruneProjectExportJobsWorker
+ :feature_category: :importers
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: cronjob:gitlab_service_ping
:worker_name: GitlabServicePingWorker
:feature_category: :service_ping
@@ -1038,6 +1065,33 @@
:weight: 1
:idempotent: false
:tags: []
+- :name: github_gists_importer:github_gists_import_finish_import
+ :worker_name: Gitlab::GithubGistsImport::FinishImportWorker
+ :feature_category: :importers
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
+- :name: github_gists_importer:github_gists_import_import_gist
+ :worker_name: Gitlab::GithubGistsImport::ImportGistWorker
+ :feature_category: :importers
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: false
+ :tags: []
+- :name: github_gists_importer:github_gists_import_start_import
+ :worker_name: Gitlab::GithubGistsImport::StartImportWorker
+ :feature_category: :importers
+ :has_external_dependencies: true
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: false
+ :tags: []
- :name: github_importer:github_import_attachments_import_issue
:worker_name: Gitlab::GithubImport::Attachments::ImportIssueWorker
:feature_category: :importers
@@ -1380,6 +1434,15 @@
:weight: 1
:idempotent: false
:tags: []
+- :name: jira_connect:jira_connect_send_uninstalled_hook
+ :worker_name: JiraConnect::SendUninstalledHookWorker
+ :feature_category: :integrations
+ :has_external_dependencies: true
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: jira_connect:jira_connect_sync_branch
:worker_name: JiraConnect::SyncBranchWorker
:feature_category: :integrations
@@ -1560,15 +1623,6 @@
:weight: 1
:idempotent: false
:tags: []
-- :name: object_storage:object_storage_background_move
- :worker_name: ObjectStorage::BackgroundMoveWorker
- :feature_category: :not_owned
- :has_external_dependencies: false
- :urgency: :low
- :resource_boundary: :unknown
- :weight: 1
- :idempotent: false
- :tags: []
- :name: object_storage:object_storage_migrate_uploads
:worker_name: ObjectStorage::MigrateUploadsWorker
:feature_category: :not_owned
@@ -1623,6 +1677,15 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: package_repositories:packages_debian_process_package_file
+ :worker_name: Packages::Debian::ProcessPackageFileWorker
+ :feature_category: :package_registry
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: package_repositories:packages_go_sync_packages
:worker_name: Packages::Go::SyncPackagesWorker
:feature_category: :package_registry
@@ -1810,7 +1873,7 @@
:urgency: :low
:resource_boundary: :unknown
:weight: 4
- :idempotent: false
+ :idempotent: true
:tags: []
- :name: pipeline_default:ci_create_cross_project_pipeline
:worker_name: Ci::CreateCrossProjectPipelineWorker
@@ -2730,15 +2793,6 @@
:weight: 1
:idempotent: true
:tags: []
-- :name: merge_requests_delete_branch
- :worker_name: MergeRequests::DeleteBranchWorker
- :feature_category: :source_code_management
- :has_external_dependencies: false
- :urgency: :high
- :resource_boundary: :unknown
- :weight: 1
- :idempotent: true
- :tags: []
- :name: merge_requests_delete_source_branch
:worker_name: MergeRequests::DeleteSourceBranchWorker
:feature_category: :source_code_management
@@ -3009,6 +3063,15 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: projects_delete_branch
+ :worker_name: Projects::DeleteBranchWorker
+ :feature_category: :source_code_management
+ :has_external_dependencies: false
+ :urgency: :high
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: projects_git_garbage_collect
:worker_name: Projects::GitGarbageCollectWorker
:feature_category: :gitaly
@@ -3018,6 +3081,15 @@
:weight: 1
:idempotent: false
:tags: []
+- :name: projects_import_export_parallel_project_export
+ :worker_name: Projects::ImportExport::ParallelProjectExportWorker
+ :feature_category: :importers
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :memory
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: projects_import_export_relation_export
:worker_name: Projects::ImportExport::RelationExportWorker
:feature_category: :importers
@@ -3281,7 +3353,7 @@
:tags: []
- :name: update_highest_role
:worker_name: UpdateHighestRoleWorker
- :feature_category: :subscription_usage_reports
+ :feature_category: :subscription_cost_management
:has_external_dependencies: false
:urgency: :high
:resource_boundary: :unknown
diff --git a/app/workers/bulk_imports/entity_worker.rb b/app/workers/bulk_imports/entity_worker.rb
index d23d57c33ab..fb99d63d06e 100644
--- a/app/workers/bulk_imports/entity_worker.rb
+++ b/app/workers/bulk_imports/entity_worker.rb
@@ -74,6 +74,8 @@ module BulkImports
source_version: source_version,
importer: 'gitlab_migration'
)
+
+ entity.fail_op!
end
private
diff --git a/app/workers/bulk_imports/export_request_worker.rb b/app/workers/bulk_imports/export_request_worker.rb
index 1a5f6250429..530419dac26 100644
--- a/app/workers/bulk_imports/export_request_worker.rb
+++ b/app/workers/bulk_imports/export_request_worker.rb
@@ -4,11 +4,15 @@ module BulkImports
class ExportRequestWorker
include ApplicationWorker
- data_consistency :always
-
idempotent!
- worker_has_external_dependencies!
+ data_consistency :always
feature_category :importers
+ sidekiq_options dead: false, retry: 5
+ worker_has_external_dependencies!
+
+ sidekiq_retries_exhausted do |msg, exception|
+ new.perform_failure(exception, msg['args'].first)
+ end
def perform(entity_id)
entity = BulkImports::Entity.find(entity_id)
@@ -18,26 +22,12 @@ module BulkImports
request_export(entity)
BulkImports::EntityWorker.perform_async(entity_id)
- rescue BulkImports::NetworkError => e
- if e.retriable?(entity)
- retry_request(e, entity)
- else
- log_exception(e,
- {
- bulk_import_entity_id: entity.id,
- bulk_import_id: entity.bulk_import_id,
- bulk_import_entity_type: entity.source_type,
- source_full_path: entity.source_full_path,
- message: "Request to export #{entity.source_type} failed",
- source_version: entity.bulk_import.source_version_info.to_s,
- importer: 'gitlab_migration'
- }
- )
-
- BulkImports::Failure.create(failure_attributes(e, entity))
-
- entity.fail_op!
- end
+ end
+
+ def perform_failure(exception, entity_id)
+ entity = BulkImports::Entity.find(entity_id)
+
+ log_and_fail(exception, entity)
end
private
@@ -104,30 +94,32 @@ module BulkImports
end
end
- def retry_request(exception, entity)
+ def logger
+ @logger ||= Gitlab::Import::Logger.build
+ end
+
+ def log_exception(exception, payload)
+ Gitlab::ExceptionLogFormatter.format!(exception, payload)
+
+ logger.error(structured_payload(payload))
+ end
+
+ def log_and_fail(exception, entity)
log_exception(exception,
{
- message: 'Retrying export request',
bulk_import_entity_id: entity.id,
bulk_import_id: entity.bulk_import_id,
bulk_import_entity_type: entity.source_type,
source_full_path: entity.source_full_path,
+ message: "Request to export #{entity.source_type} failed",
source_version: entity.bulk_import.source_version_info.to_s,
importer: 'gitlab_migration'
}
)
- self.class.perform_in(2.seconds, entity.id)
- end
-
- def logger
- @logger ||= Gitlab::Import::Logger.build
- end
-
- def log_exception(exception, payload)
- Gitlab::ExceptionLogFormatter.format!(exception, payload)
+ BulkImports::Failure.create(failure_attributes(exception, entity))
- logger.error(structured_payload(payload))
+ entity.fail_op!
end
end
end
diff --git a/app/workers/bulk_imports/pipeline_worker.rb b/app/workers/bulk_imports/pipeline_worker.rb
index 5716f6e3f31..62e85d38e61 100644
--- a/app/workers/bulk_imports/pipeline_worker.rb
+++ b/app/workers/bulk_imports/pipeline_worker.rb
@@ -3,6 +3,7 @@
module BulkImports
class PipelineWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
+ include ExclusiveLeaseGuard
FILE_EXTRACTION_PIPELINE_PERFORM_DELAY = 10.seconds
@@ -10,44 +11,24 @@ module BulkImports
feature_category :importers
sidekiq_options retry: false, dead: false
worker_has_external_dependencies!
+ deduplicate :until_executing
def perform(pipeline_tracker_id, stage, entity_id)
- @pipeline_tracker = ::BulkImports::Tracker
- .with_status(:enqueued)
- .find_by_id(pipeline_tracker_id)
-
- if pipeline_tracker.present?
- @entity = @pipeline_tracker.entity
-
- logger.info(
- structured_payload(
- bulk_import_entity_id: entity.id,
- bulk_import_id: entity.bulk_import_id,
- bulk_import_entity_type: entity.source_type,
- source_full_path: entity.source_full_path,
- pipeline_name: pipeline_tracker.pipeline_name,
- message: 'Pipeline starting',
- source_version: source_version,
- importer: 'gitlab_migration'
- )
- )
-
- run
- else
- @entity = ::BulkImports::Entity.find(entity_id)
-
- logger.error(
- structured_payload(
- bulk_import_entity_id: entity_id,
- bulk_import_id: entity.bulk_import_id,
- bulk_import_entity_type: entity.source_type,
- source_full_path: entity.source_full_path,
- pipeline_tracker_id: pipeline_tracker_id,
- message: 'Unstarted pipeline not found',
- source_version: source_version,
- importer: 'gitlab_migration'
- )
- )
+ @entity = ::BulkImports::Entity.find(entity_id)
+ @pipeline_tracker = ::BulkImports::Tracker.find(pipeline_tracker_id)
+
+ try_obtain_lease do
+ if pipeline_tracker.enqueued?
+ logger.info(log_attributes(message: 'Pipeline starting'))
+
+ run
+ else
+ message = "Pipeline in #{pipeline_tracker.human_status_name} state instead of expected enqueued state"
+
+ logger.error(log_attributes(message: message))
+
+ fail_tracker(StandardError.new(message)) unless pipeline_tracker.finished? || pipeline_tracker.skipped?
+ end
end
ensure
@@ -63,6 +44,7 @@ module BulkImports
raise(Pipeline::ExpiredError, 'Pipeline timeout') if job_timeout?
raise(Pipeline::FailedError, "Export from source instance failed: #{export_status.error}") if export_failed?
+ raise(Pipeline::ExpiredError, 'Empty export status on source instance') if empty_export_timeout?
return re_enqueue if export_empty? || export_started?
@@ -82,29 +64,9 @@ module BulkImports
def fail_tracker(exception)
pipeline_tracker.update!(status_event: 'fail_op', jid: jid)
- log_exception(exception,
- {
- bulk_import_entity_id: entity.id,
- bulk_import_id: entity.bulk_import_id,
- bulk_import_entity_type: entity.source_type,
- source_full_path: entity.source_full_path,
- pipeline_name: pipeline_tracker.pipeline_name,
- message: 'Pipeline failed',
- source_version: source_version,
- importer: 'gitlab_migration'
- }
- )
+ log_exception(exception, log_attributes(message: 'Pipeline failed'))
- Gitlab::ErrorTracking.track_exception(
- exception,
- bulk_import_entity_id: entity.id,
- bulk_import_id: entity.bulk_import_id,
- bulk_import_entity_type: entity.source_type,
- source_full_path: entity.source_full_path,
- pipeline_name: pipeline_tracker.pipeline_name,
- source_version: source_version,
- importer: 'gitlab_migration'
- )
+ Gitlab::ErrorTracking.track_exception(exception, log_attributes)
BulkImports::Failure.create(
bulk_import_entity_id: entity.id,
@@ -144,7 +106,11 @@ module BulkImports
def job_timeout?
return false unless file_extraction_pipeline?
- (Time.zone.now - entity.created_at) > Pipeline::NDJSON_EXPORT_TIMEOUT
+ time_since_entity_created > Pipeline::NDJSON_EXPORT_TIMEOUT
+ end
+
+ def empty_export_timeout?
+ export_empty? && time_since_entity_created > Pipeline::EMPTY_EXPORT_STATUS_TIMEOUT
end
def export_failed?
@@ -166,18 +132,7 @@ module BulkImports
end
def retry_tracker(exception)
- log_exception(exception,
- {
- bulk_import_entity_id: entity.id,
- bulk_import_id: entity.bulk_import_id,
- bulk_import_entity_type: entity.source_type,
- source_full_path: entity.source_full_path,
- pipeline_name: pipeline_tracker.pipeline_name,
- message: "Retrying pipeline",
- source_version: source_version,
- importer: 'gitlab_migration'
- }
- )
+ log_exception(exception, log_attributes(message: "Retrying pipeline"))
pipeline_tracker.update!(status_event: 'retry', jid: jid)
@@ -185,25 +140,43 @@ module BulkImports
end
def skip_tracker
- logger.info(
- structured_payload(
+ logger.info(log_attributes(message: 'Skipping pipeline due to failed entity'))
+
+ pipeline_tracker.update!(status_event: 'skip', jid: jid)
+ end
+
+ def log_attributes(extra = {})
+ structured_payload(
+ {
bulk_import_entity_id: entity.id,
bulk_import_id: entity.bulk_import_id,
bulk_import_entity_type: entity.source_type,
source_full_path: entity.source_full_path,
+ pipeline_tracker_id: pipeline_tracker.id,
pipeline_name: pipeline_tracker.pipeline_name,
- message: 'Skipping pipeline due to failed entity',
+ pipeline_tracker_state: pipeline_tracker.human_status_name,
source_version: source_version,
importer: 'gitlab_migration'
- )
+ }.merge(extra)
)
-
- pipeline_tracker.update!(status_event: 'skip', jid: jid)
end
def log_exception(exception, payload)
Gitlab::ExceptionLogFormatter.format!(exception, payload)
+
logger.error(structured_payload(payload))
end
+
+ def time_since_entity_created
+ Time.zone.now - entity.created_at
+ end
+
+ def lease_timeout
+ 30
+ end
+
+ def lease_key
+ "gitlab:bulk_imports:pipeline_worker:#{pipeline_tracker.id}"
+ end
end
end
diff --git a/app/workers/ci/create_downstream_pipeline_worker.rb b/app/workers/ci/create_downstream_pipeline_worker.rb
index 747cb088272..9f5ff45b8a6 100644
--- a/app/workers/ci/create_downstream_pipeline_worker.rb
+++ b/app/workers/ci/create_downstream_pipeline_worker.rb
@@ -11,9 +11,15 @@ module Ci
def perform(bridge_id)
::Ci::Bridge.find_by_id(bridge_id).try do |bridge|
- ::Ci::CreateDownstreamPipelineService
+ result = ::Ci::CreateDownstreamPipelineService
.new(bridge.project, bridge.user)
.execute(bridge)
+
+ if result.success?
+ log_extra_metadata_on_done(:new_pipeline_id, result.payload.id)
+ else
+ log_extra_metadata_on_done(:create_error_message, result.message)
+ end
end
end
end
diff --git a/app/workers/concerns/gitlab/github_import/object_importer.rb b/app/workers/concerns/gitlab/github_import/object_importer.rb
index 9793278ac0c..c5c7da23892 100644
--- a/app/workers/concerns/gitlab/github_import/object_importer.rb
+++ b/app/workers/concerns/gitlab/github_import/object_importer.rb
@@ -47,6 +47,9 @@ module Gitlab
# Representation is created but the developer forgot to add a
# `:github_identifiers` field.
track_and_raise_exception(project, e, fail_import: true)
+ rescue ActiveRecord::RecordInvalid => e
+ # We do not raise exception to prevent job retry
+ track_exception(project, e)
rescue StandardError => e
track_and_raise_exception(project, e)
end
@@ -86,13 +89,17 @@ module Gitlab
)
end
- def track_and_raise_exception(project, exception, fail_import: false)
+ def track_exception(project, exception, fail_import: false)
Gitlab::Import::ImportFailureService.track(
project_id: project.id,
error_source: importer_class.name,
exception: exception,
fail_import: fail_import
)
+ end
+
+ def track_and_raise_exception(project, exception, fail_import: false)
+ track_exception(project, exception, fail_import: fail_import)
raise(exception)
end
diff --git a/app/workers/concerns/waitable_worker.rb b/app/workers/concerns/waitable_worker.rb
index 9300c2a5790..f23e3fb20c2 100644
--- a/app/workers/concerns/waitable_worker.rb
+++ b/app/workers/concerns/waitable_worker.rb
@@ -6,33 +6,8 @@ module WaitableWorker
class_methods do
# Schedules multiple jobs and waits for them to be completed.
def bulk_perform_and_wait(args_list)
- # Short-circuit: it's more efficient to do small numbers of jobs inline
- if args_list.size == 1 && !always_async_project_authorizations_refresh?
- return bulk_perform_inline(args_list)
- end
-
bulk_perform_async(args_list)
end
-
- # Performs multiple jobs directly. Failed jobs will be put into sidekiq so
- # they can benefit from retries
- def bulk_perform_inline(args_list)
- failed = []
-
- args_list.each do |args|
- worker = new
- Gitlab::AppJsonLogger.info(worker.structured_payload(message: 'running inline'))
- worker.perform(*args)
- rescue StandardError
- failed << args
- end
-
- bulk_perform_async(failed) if failed.present?
- end
-
- def always_async_project_authorizations_refresh?
- Feature.enabled?(:always_async_project_authorizations_refresh)
- end
end
def perform(*args)
diff --git a/app/workers/container_registry/cleanup_worker.rb b/app/workers/container_registry/cleanup_worker.rb
index 8350ae3431b..a838b97b35d 100644
--- a/app/workers/container_registry/cleanup_worker.rb
+++ b/app/workers/container_registry/cleanup_worker.rb
@@ -15,8 +15,6 @@ module ContainerRegistry
BATCH_SIZE = 200
def perform
- return unless Feature.enabled?(:container_registry_delete_repository_with_cron_worker)
-
log_counts
reset_stale_deletes
diff --git a/app/workers/container_registry/delete_container_repository_worker.rb b/app/workers/container_registry/delete_container_repository_worker.rb
index 1f94b1b9e71..d6ecd836ed2 100644
--- a/app/workers/container_registry/delete_container_repository_worker.rb
+++ b/app/workers/container_registry/delete_container_repository_worker.rb
@@ -17,6 +17,7 @@ module ContainerRegistry
MAX_CAPACITY = 2
CLEANUP_TAGS_SERVICE_PARAMS = {
'name_regex_delete' => '.*',
+ 'keep_latest' => false,
'container_expiration_policy' => true # to avoid permissions checks
}.freeze
diff --git a/app/workers/container_registry/migration/enqueuer_worker.rb b/app/workers/container_registry/migration/enqueuer_worker.rb
index 1dd29eff86e..a4f5ac8eb7e 100644
--- a/app/workers/container_registry/migration/enqueuer_worker.rb
+++ b/app/workers/container_registry/migration/enqueuer_worker.rb
@@ -130,13 +130,13 @@ module ContainerRegistry
# this issue.
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87733 and
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90735 for details.
- ContainerRepository.ready_for_import.limit(25)[0] # rubocop:disable CodeReuse/ActiveRecord
+ ContainerRepository.ready_for_import.ordered.limit(25)[0] # rubocop:disable CodeReuse/ActiveRecord
end
end
def next_aborted_repository
strong_memoize(:next_aborted_repository) do
- ContainerRepository.with_migration_state('import_aborted').limit(25)[0] # rubocop:disable CodeReuse/ActiveRecord
+ ContainerRepository.with_migration_state('import_aborted').ordered.limit(25)[0] # rubocop:disable CodeReuse/ActiveRecord
end
end
diff --git a/app/workers/database/batched_background_migration/ci_database_worker.rb b/app/workers/database/batched_background_migration/ci_database_worker.rb
index b04db87631a..58b0f5496f4 100644
--- a/app/workers/database/batched_background_migration/ci_database_worker.rb
+++ b/app/workers/database/batched_background_migration/ci_database_worker.rb
@@ -7,6 +7,10 @@ module Database
def self.tracking_database
@tracking_database ||= Gitlab::Database::CI_DATABASE_NAME.to_sym
end
+
+ def execution_worker_class
+ @execution_worker_class ||= Database::BatchedBackgroundMigration::CiExecutionWorker
+ end
end
end
end
diff --git a/app/workers/database/batched_background_migration/ci_execution_worker.rb b/app/workers/database/batched_background_migration/ci_execution_worker.rb
new file mode 100644
index 00000000000..89c70e29dda
--- /dev/null
+++ b/app/workers/database/batched_background_migration/ci_execution_worker.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Database
+ module BatchedBackgroundMigration
+ class CiExecutionWorker # rubocop:disable Scalability/IdempotentWorker
+ include ExecutionWorker
+ end
+ end
+end
diff --git a/app/workers/database/batched_background_migration/execution_worker.rb b/app/workers/database/batched_background_migration/execution_worker.rb
index 098153c742f..b59e4bd1f86 100644
--- a/app/workers/database/batched_background_migration/execution_worker.rb
+++ b/app/workers/database/batched_background_migration/execution_worker.rb
@@ -2,14 +2,47 @@
module Database
module BatchedBackgroundMigration
- class ExecutionWorker # rubocop:disable Scalability/IdempotentWorker
+ module ExecutionWorker
+ extend ActiveSupport::Concern
include ExclusiveLeaseGuard
include Gitlab::Utils::StrongMemoize
+ include ApplicationWorker
+ include LimitedCapacity::Worker
INTERVAL_VARIANCE = 5.seconds.freeze
LEASE_TIMEOUT_MULTIPLIER = 3
+ MAX_RUNNING_MIGRATIONS = 2
- def perform(database_name, migration_id)
+ included do
+ data_consistency :always
+ feature_category :database
+ queue_namespace :batched_background_migrations
+ end
+
+ class_methods do
+ def max_running_jobs
+ MAX_RUNNING_MIGRATIONS
+ end
+
+ # We have to overirde this one, as we want
+ # arguments passed as is, and not duplicated
+ def perform_with_capacity(args)
+ worker = new
+ worker.remove_failed_jobs
+
+ bulk_perform_async(args) # rubocop:disable Scalability/BulkPerformWithContext
+ end
+ end
+
+ def remaining_work_count(*args)
+ 0 # the cron worker is the only source of new jobs
+ end
+
+ def max_running_jobs
+ self.class.max_running_jobs
+ end
+
+ def perform_work(database_name, migration_id)
self.database_name = database_name
return unless enabled?
diff --git a/app/workers/database/batched_background_migration/main_execution_worker.rb b/app/workers/database/batched_background_migration/main_execution_worker.rb
new file mode 100644
index 00000000000..661496a86a9
--- /dev/null
+++ b/app/workers/database/batched_background_migration/main_execution_worker.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Database
+ module BatchedBackgroundMigration
+ class MainExecutionWorker # rubocop:disable Scalability/IdempotentWorker
+ include ExecutionWorker
+ end
+ end
+end
diff --git a/app/workers/database/batched_background_migration/single_database_worker.rb b/app/workers/database/batched_background_migration/single_database_worker.rb
index 0c7c51d5c0a..e772216e557 100644
--- a/app/workers/database/batched_background_migration/single_database_worker.rb
+++ b/app/workers/database/batched_background_migration/single_database_worker.rb
@@ -39,7 +39,7 @@ module Database
unless base_model
Sidekiq.logger.info(
class: self.class.name,
- database: self.class.tracking_database,
+ database: tracking_database,
message: 'skipping migration execution for unconfigured database')
return
@@ -48,34 +48,61 @@ module Database
if shares_db_config?
Sidekiq.logger.info(
class: self.class.name,
- database: self.class.tracking_database,
+ database: tracking_database,
message: 'skipping migration execution for database that shares database configuration with another database')
return
end
Gitlab::Database::SharedModel.using_connection(base_model.connection) do
- break unless self.class.enabled? && active_migration
+ break unless self.class.enabled?
- with_exclusive_lease(active_migration.interval) do
- run_active_migration
+ if parallel_execution_enabled?
+ migrations = Gitlab::Database::BackgroundMigration::BatchedMigration
+ .active_migrations_distinct_on_table(connection: base_model.connection, limit: max_running_migrations).to_a
+
+ queue_migrations_for_execution(migrations) if migrations.any?
+ else
+ break unless active_migration
+
+ with_exclusive_lease(active_migration.interval) do
+ run_active_migration
+ end
end
end
end
private
+ def parallel_execution_enabled?
+ Feature.enabled?(:batched_migrations_parallel_execution)
+ end
+
+ def max_running_migrations
+ execution_worker_class.max_running_jobs
+ end
+
def active_migration
@active_migration ||= Gitlab::Database::BackgroundMigration::BatchedMigration.active_migration(connection: base_model.connection)
end
def run_active_migration
- Database::BatchedBackgroundMigration::ExecutionWorker.new.perform(self.class.tracking_database, active_migration.id)
+ execution_worker_class.new.perform_work(tracking_database, active_migration.id)
+ end
+
+ def tracking_database
+ self.class.tracking_database
+ end
+
+ def queue_migrations_for_execution(migrations)
+ jobs_arguments = migrations.map { |migration| [tracking_database.to_s, migration.id] }
+
+ execution_worker_class.perform_with_capacity(jobs_arguments)
end
def base_model
strong_memoize(:base_model) do
- Gitlab::Database.database_base_models[self.class.tracking_database]
+ Gitlab::Database.database_base_models[tracking_database]
end
end
diff --git a/app/workers/database/batched_background_migration_worker.rb b/app/workers/database/batched_background_migration_worker.rb
index 29804be832d..1450613dd89 100644
--- a/app/workers/database/batched_background_migration_worker.rb
+++ b/app/workers/database/batched_background_migration_worker.rb
@@ -7,5 +7,9 @@ module Database
def self.tracking_database
@tracking_database ||= Gitlab::Database::MAIN_DATABASE_NAME.to_sym
end
+
+ def execution_worker_class
+ @execution_worker_class ||= Database::BatchedBackgroundMigration::MainExecutionWorker
+ end
end
end
diff --git a/app/workers/delete_container_repository_worker.rb b/app/workers/delete_container_repository_worker.rb
index 73e6843fdd0..d0552dce9fc 100644
--- a/app/workers/delete_container_repository_worker.rb
+++ b/app/workers/delete_container_repository_worker.rb
@@ -11,64 +11,5 @@ class DeleteContainerRepositoryWorker # rubocop:disable Scalability/IdempotentWo
queue_namespace :container_repository
feature_category :container_registry
- LEASE_TIMEOUT = 1.hour.freeze
- FIXED_DELAY = 10.seconds.freeze
-
- attr_reader :container_repository
-
- def perform(current_user_id, container_repository_id)
- current_user = User.find_by_id(current_user_id)
- @container_repository = ContainerRepository.find_by_id(container_repository_id)
- project = container_repository&.project
-
- return unless current_user && container_repository && project
-
- if migration.delete_container_repository_worker_support? && migrating?
- delay = migration_duration
-
- self.class.perform_in(delay.from_now)
-
- log_extra_metadata_on_done(:delete_postponed, delay)
-
- return
- end
-
- # If a user accidentally attempts to delete the same container registry in quick succession,
- # this can lead to orphaned tags.
- try_obtain_lease do
- Projects::ContainerRepository::DestroyService.new(project, current_user).execute(container_repository)
- end
- end
-
- private
-
- def migrating?
- !(container_repository.default? ||
- container_repository.import_done? ||
- container_repository.import_skipped?)
- end
-
- def migration_duration
- duration = migration.import_timeout.seconds + FIXED_DELAY
-
- if container_repository.pre_importing?
- duration += migration.dynamic_pre_import_timeout_for(container_repository)
- end
-
- duration
- end
-
- def migration
- ContainerRegistry::Migration
- end
-
- # For ExclusiveLeaseGuard concern
- def lease_key
- @lease_key ||= "container_repository:delete:#{container_repository.id}"
- end
-
- # For ExclusiveLeaseGuard concern
- def lease_timeout
- LEASE_TIMEOUT
- end
+ def perform(current_user_id, container_repository_id); end
end
diff --git a/app/workers/flush_counter_increments_worker.rb b/app/workers/flush_counter_increments_worker.rb
index 8c7e17587d0..e92e1a9b7b5 100644
--- a/app/workers/flush_counter_increments_worker.rb
+++ b/app/workers/flush_counter_increments_worker.rb
@@ -29,6 +29,6 @@ class FlushCounterIncrementsWorker
model = model_class.find_by_id(model_id)
return unless model
- model.flush_increments_to_database!(attribute)
+ Gitlab::Counters::BufferedCounter.new(model, attribute).commit_increment!
end
end
diff --git a/app/workers/gitlab/export/prune_project_export_jobs_worker.rb b/app/workers/gitlab/export/prune_project_export_jobs_worker.rb
new file mode 100644
index 00000000000..9a3c0c80f85
--- /dev/null
+++ b/app/workers/gitlab/export/prune_project_export_jobs_worker.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Export
+ class PruneProjectExportJobsWorker
+ include ApplicationWorker
+
+ # rubocop:disable Scalability/CronWorkerContext
+ # This worker updates several import states inline and does not schedule
+ # other jobs. So no context needed
+ include CronjobQueue
+ # rubocop:enable Scalability/CronWorkerContext
+
+ feature_category :importers
+ data_consistency :always
+ idempotent!
+
+ def perform
+ ProjectExportJob.prune_expired_jobs
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/github_gists_import/finish_import_worker.rb b/app/workers/gitlab/github_gists_import/finish_import_worker.rb
new file mode 100644
index 00000000000..1989b6314ea
--- /dev/null
+++ b/app/workers/gitlab/github_gists_import/finish_import_worker.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubGistsImport
+ class FinishImportWorker
+ include ApplicationWorker
+
+ data_consistency :always
+ queue_namespace :github_gists_importer
+ feature_category :importers
+ idempotent!
+
+ sidekiq_options dead: false, retry: 5
+
+ sidekiq_retries_exhausted do |msg, _|
+ Gitlab::GithubGistsImport::Status.new(msg['args'][0]).fail!
+ end
+
+ INTERVAL = 30.seconds.to_i
+ BLOCKING_WAIT_TIME = 5
+
+ def perform(user_id, waiter_key, remaining)
+ waiter = wait_for_jobs(waiter_key, remaining)
+
+ if waiter.nil?
+ Gitlab::GithubGistsImport::Status.new(user_id).finish!
+
+ Gitlab::GithubImport::Logger.info(user_id: user_id, message: 'GitHub Gists import finished')
+ else
+ self.class.perform_in(INTERVAL, user_id, waiter.key, waiter.jobs_remaining)
+ end
+ end
+
+ private
+
+ def wait_for_jobs(key, remaining)
+ waiter = JobWaiter.new(remaining, key)
+ waiter.wait(BLOCKING_WAIT_TIME)
+
+ return if waiter.jobs_remaining == 0
+
+ waiter
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/github_gists_import/import_gist_worker.rb b/app/workers/gitlab/github_gists_import/import_gist_worker.rb
new file mode 100644
index 00000000000..7e2b3709597
--- /dev/null
+++ b/app/workers/gitlab/github_gists_import/import_gist_worker.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubGistsImport
+ class ImportGistWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+ include Gitlab::NotifyUponDeath
+
+ data_consistency :always
+ queue_namespace :github_gists_importer
+ feature_category :importers
+
+ sidekiq_options dead: false, retry: 5
+
+ def perform(user_id, gist_hash, notify_key)
+ gist = ::Gitlab::GithubGistsImport::Representation::Gist.from_json_hash(gist_hash)
+
+ with_logging(user_id, gist.github_identifiers) do
+ result = importer_class.new(gist, user_id).execute
+ error(user_id, result.errors, gist.github_identifiers) unless result.success?
+
+ JobWaiter.notify(notify_key, jid)
+ end
+ rescue StandardError => e
+ log_and_track_error(user_id, e, gist.github_identifiers)
+
+ raise
+ end
+
+ private
+
+ def importer_class
+ ::Gitlab::GithubGistsImport::Importer::GistImporter
+ end
+
+ def with_logging(user_id, gist_id)
+ info(user_id, 'start importer', gist_id)
+
+ yield
+
+ info(user_id, 'importer finished', gist_id)
+ end
+
+ def log_and_track_error(user_id, exception, gist_id)
+ error(user_id, exception.message, gist_id)
+
+ Gitlab::ErrorTracking.track_exception(exception,
+ import_type: :github_gists,
+ user_id: user_id
+ )
+ end
+
+ def error(user_id, error_message, gist_id)
+ attributes = {
+ user_id: user_id,
+ github_identifiers: gist_id,
+ message: 'importer failed',
+ 'error.message': error_message
+ }
+
+ Gitlab::GithubImport::Logger.error(structured_payload(attributes))
+ end
+
+ def info(user_id, message, gist_id)
+ attributes = {
+ user_id: user_id,
+ message: message,
+ github_identifiers: gist_id
+ }
+
+ Gitlab::GithubImport::Logger.info(structured_payload(attributes))
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/github_gists_import/start_import_worker.rb b/app/workers/gitlab/github_gists_import/start_import_worker.rb
new file mode 100644
index 00000000000..33c91611719
--- /dev/null
+++ b/app/workers/gitlab/github_gists_import/start_import_worker.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubGistsImport
+ class StartImportWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+
+ data_consistency :always
+ queue_namespace :github_gists_importer
+ feature_category :importers
+
+ sidekiq_options dead: false, retry: 5
+
+ worker_has_external_dependencies!
+
+ sidekiq_retries_exhausted do |msg, _|
+ Gitlab::GithubGistsImport::Status.new(msg['args'][0]).fail!
+
+ user = User.find(msg['args'][0])
+ Gitlab::GithubImport::PageCounter.new(user, :gists, 'github-gists-importer').expire!
+ end
+
+ def perform(user_id, encrypted_token)
+ logger.info(structured_payload(user_id: user_id, message: 'starting importer'))
+
+ user = User.find(user_id)
+ decrypted_token = Gitlab::CryptoHelper.aes256_gcm_decrypt(encrypted_token)
+ result = Gitlab::GithubGistsImport::Importer::GistsImporter.new(user, decrypted_token).execute
+
+ if result.success?
+ schedule_finish_worker(user_id, result.waiter)
+ elsif result.next_attempt_in
+ schedule_next_attempt(result.next_attempt_in, user_id, encrypted_token)
+ else
+ log_error_and_raise!(user_id, result.error)
+ end
+ end
+
+ private
+
+ def schedule_finish_worker(user_id, waiter)
+ logger.info(structured_payload(user_id: user_id, message: 'importer finished'))
+
+ Gitlab::GithubGistsImport::FinishImportWorker.perform_async(user_id, waiter.key, waiter.jobs_remaining)
+ end
+
+ def schedule_next_attempt(next_attempt_in, user_id, encrypted_token)
+ logger.info(structured_payload(user_id: user_id, message: 'rate limit reached'))
+
+ self.class.perform_in(next_attempt_in, user_id, encrypted_token)
+ end
+
+ def log_error_and_raise!(user_id, error)
+ logger.error(structured_payload(user_id: user_id, message: 'import failed', 'error.message': error.message))
+
+ raise(error)
+ end
+
+ def logger
+ Gitlab::GithubImport::Logger
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab_shell_worker.rb b/app/workers/gitlab_shell_worker.rb
index 2f396dcdb86..b3c0fa79658 100644
--- a/app/workers/gitlab_shell_worker.rb
+++ b/app/workers/gitlab_shell_worker.rb
@@ -14,7 +14,7 @@ class GitlabShellWorker # rubocop:disable Scalability/IdempotentWorker
loggable_arguments 0
def perform(action, *arg)
- if ::Feature.enabled?(:verify_gitlab_shell_worker_method_names) && Gitlab::Shell::PERMITTED_ACTIONS.exclude?(action)
+ if Gitlab::Shell::PERMITTED_ACTIONS.exclude?(action)
raise(ArgumentError, "#{action} not allowed for #{self.class.name}")
end
diff --git a/app/workers/issuable_export_csv_worker.rb b/app/workers/issuable_export_csv_worker.rb
index ffa0ed68fc7..1c5fab8c4c0 100644
--- a/app/workers/issuable_export_csv_worker.rb
+++ b/app/workers/issuable_export_csv_worker.rb
@@ -24,7 +24,7 @@ class IssuableExportCsvWorker # rubocop:disable Scalability/IdempotentWorker
def export_service(type, user, project, params)
issuable_classes = issuable_classes_for(type.to_sym)
- issuables = issuable_classes[:finder].new(user, parse_params(params, project.id)).execute
+ issuables = issuable_classes[:finder].new(user, parse_params(params, project.id, type)).execute
issuable_classes[:service].new(issuables, project)
end
@@ -39,7 +39,7 @@ class IssuableExportCsvWorker # rubocop:disable Scalability/IdempotentWorker
end
end
- def parse_params(params, project_id)
+ def parse_params(params, project_id, _type)
params
.with_indifferent_access
.except(:sort)
diff --git a/app/workers/jira_connect/send_uninstalled_hook_worker.rb b/app/workers/jira_connect/send_uninstalled_hook_worker.rb
new file mode 100644
index 00000000000..530ef4a8b8a
--- /dev/null
+++ b/app/workers/jira_connect/send_uninstalled_hook_worker.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module JiraConnect
+ class SendUninstalledHookWorker
+ include ApplicationWorker
+
+ data_consistency :delayed
+ queue_namespace :jira_connect
+ feature_category :integrations
+ urgency :low
+
+ idempotent!
+
+ worker_has_external_dependencies!
+
+ def perform(installation_id, instance_url)
+ installation = JiraConnectInstallation.find_by_id(installation_id)
+
+ JiraConnectInstallations::ProxyLifecycleEventService.execute(installation, :uninstalled, instance_url)
+ end
+ end
+end
diff --git a/app/workers/mail_scheduler/notification_service_worker.rb b/app/workers/mail_scheduler/notification_service_worker.rb
index 12e8de4491e..8206a17021b 100644
--- a/app/workers/mail_scheduler/notification_service_worker.rb
+++ b/app/workers/mail_scheduler/notification_service_worker.rb
@@ -18,9 +18,7 @@ module MailScheduler
def perform(meth, *args)
check_arguments!(args)
- if ::Feature.enabled?(:verify_mail_scheduler_notification_service_worker_method_names) &&
- NotificationService.permitted_actions.exclude?(meth.to_sym)
-
+ if NotificationService.permitted_actions.exclude?(meth.to_sym)
raise(ArgumentError, "#{meth} not allowed for #{self.class.name}")
end
diff --git a/app/workers/merge_requests/delete_branch_worker.rb b/app/workers/merge_requests/delete_branch_worker.rb
deleted file mode 100644
index 6816f9a4b77..00000000000
--- a/app/workers/merge_requests/delete_branch_worker.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-module MergeRequests
- class DeleteBranchWorker
- include ApplicationWorker
-
- data_consistency :always
-
- feature_category :source_code_management
- urgency :high
- idempotent!
-
- def perform(merge_request_id, user_id, branch_name, retarget_branch)
- merge_request = MergeRequest.find_by_id(merge_request_id)
- user = User.find_by_id(user_id)
-
- return unless merge_request.present? && user.present?
-
- ::Branches::DeleteService.new(merge_request.source_project, user).execute(branch_name)
-
- return unless retarget_branch
-
- ::MergeRequests::RetargetChainService.new(project: merge_request.source_project, current_user: user)
- .execute(merge_request)
- end
- end
-end
diff --git a/app/workers/merge_requests/delete_source_branch_worker.rb b/app/workers/merge_requests/delete_source_branch_worker.rb
index 96dde413d5b..da1eca067a9 100644
--- a/app/workers/merge_requests/delete_source_branch_worker.rb
+++ b/app/workers/merge_requests/delete_source_branch_worker.rb
@@ -19,13 +19,14 @@ class MergeRequests::DeleteSourceBranchWorker
return if merge_request.source_branch_sha != source_branch_sha
if Feature.enabled?(:add_delete_branch_worker, merge_request.source_project)
- ::MergeRequests::DeleteBranchWorker.perform_async(merge_request_id, user_id, merge_request.source_branch, true)
+ ::Projects::DeleteBranchWorker.new.perform(merge_request.source_project.id, user_id,
+ merge_request.source_branch)
else
::Branches::DeleteService.new(merge_request.source_project, user).execute(merge_request.source_branch)
-
- ::MergeRequests::RetargetChainService.new(project: merge_request.source_project, current_user: user)
- .execute(merge_request)
end
+
+ ::MergeRequests::RetargetChainService.new(project: merge_request.source_project, current_user: user)
+ .execute(merge_request)
rescue ActiveRecord::RecordNotFound
end
end
diff --git a/app/workers/namespaces/root_statistics_worker.rb b/app/workers/namespaces/root_statistics_worker.rb
index e3aa8a1f779..02b3468c052 100644
--- a/app/workers/namespaces/root_statistics_worker.rb
+++ b/app/workers/namespaces/root_statistics_worker.rb
@@ -4,7 +4,7 @@ module Namespaces
class RootStatisticsWorker
include ApplicationWorker
- data_consistency :sticky, feature_flag: :root_statistics_worker_read_replica
+ data_consistency :sticky
sidekiq_options retry: 3
diff --git a/app/workers/object_storage/background_move_worker.rb b/app/workers/object_storage/background_move_worker.rb
deleted file mode 100644
index bb51f0d7e1f..00000000000
--- a/app/workers/object_storage/background_move_worker.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: true
-
-module ObjectStorage
- class BackgroundMoveWorker # rubocop:disable Scalability/IdempotentWorker
- include ApplicationWorker
-
- data_consistency :always
- include ObjectStorageQueue
-
- sidekiq_options retry: 5
- feature_category :not_owned # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned
- loggable_arguments 0, 1, 2, 3
-
- def perform(uploader_class_name, subject_class_name, file_field, subject_id)
- uploader_class = uploader_class_name.constantize
- subject_class = subject_class_name.constantize
-
- return unless uploader_class < ObjectStorage::Concern
- return unless uploader_class.object_store_enabled?
- return unless uploader_class.background_upload_enabled?
-
- subject = subject_class.find(subject_id)
- uploader = build_uploader(subject, file_field&.to_sym)
- uploader.migrate!(ObjectStorage::Store::REMOTE)
- end
-
- def build_uploader(subject, mount_point)
- case subject
- when Upload then subject.retrieve_uploader(mount_point)
- else
- subject.send(mount_point) # rubocop:disable GitlabSecurity/PublicSend
- end
- end
- end
-end
diff --git a/app/workers/packages/debian/process_package_file_worker.rb b/app/workers/packages/debian/process_package_file_worker.rb
new file mode 100644
index 00000000000..587c0b78c9c
--- /dev/null
+++ b/app/workers/packages/debian/process_package_file_worker.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module Packages
+ module Debian
+ class ProcessPackageFileWorker
+ include ApplicationWorker
+ include ::Packages::FIPS
+ include Gitlab::Utils::StrongMemoize
+
+ data_consistency :always
+
+ deduplicate :until_executed
+ idempotent!
+
+ queue_namespace :package_repositories
+ feature_category :package_registry
+
+ def perform(package_file_id, user_id, distribution_name, component_name)
+ raise DisabledError, 'Debian registry is not FIPS compliant' if Gitlab::FIPS.enabled?
+
+ @package_file_id = package_file_id
+ @user_id = user_id
+ @distribution_name = distribution_name
+ @component_name = component_name
+
+ return unless package_file && user && distribution_name && component_name
+ # return if file has already been processed
+ return unless package_file.debian_file_metadatum&.unknown?
+
+ ::Packages::Debian::ProcessPackageFileService.new(package_file, user, distribution_name, component_name).execute
+ rescue StandardError => e
+ raise if e.instance_of?(DisabledError)
+
+ Gitlab::ErrorTracking.log_exception(e, package_file_id: @package_file_id, user_id: @user_id,
+ distribution_name: @distribution_name, component_name: @component_name)
+ package_file.destroy!
+ end
+
+ private
+
+ def package_file
+ ::Packages::PackageFile.find_by_id(@package_file_id)
+ end
+ strong_memoize_attr :package_file
+
+ def user
+ ::User.find_by_id(@user_id)
+ end
+ strong_memoize_attr :user
+ end
+ end
+end
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index 329ccfc6362..f95176da252 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -142,12 +142,16 @@ class PostReceive
def emit_snowplow_event(project, user)
return unless Feature.enabled?(:route_hll_to_snowplow_phase2, project.namespace)
+ metric_path = 'counts.source_code_pushes'
Gitlab::Tracking.event(
'PostReceive',
- 'source_code_pushes',
+ :push,
project: project,
namespace: project.namespace,
- user: user
+ user: user,
+ property: 'source_code_pushes',
+ label: metric_path,
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis, key_path: metric_path).to_context]
)
end
end
diff --git a/app/workers/projects/delete_branch_worker.rb b/app/workers/projects/delete_branch_worker.rb
new file mode 100644
index 00000000000..1949fb67e83
--- /dev/null
+++ b/app/workers/projects/delete_branch_worker.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Projects
+ class DeleteBranchWorker
+ include ApplicationWorker
+
+ data_consistency :always
+
+ feature_category :source_code_management
+ urgency :high
+ idempotent!
+
+ def perform(project_id, user_id, branch_name)
+ project = Project.find_by_id(project_id)
+ user = User.find_by_id(user_id)
+
+ return unless project.present? && user.present?
+ return unless project.repository.branch_exists?(branch_name)
+
+ delete_service_result = ::Branches::DeleteService.new(project, user)
+ .execute(branch_name)
+
+ return unless Feature.enabled?(:track_and_raise_delete_source_errors, project)
+ # Only want to raise on 400 to avoid permission and non existant branch error
+ return unless delete_service_result[:http_status] == 400
+
+ delete_service_result.track_and_raise_exception
+ end
+ end
+end
diff --git a/app/workers/projects/import_export/parallel_project_export_worker.rb b/app/workers/projects/import_export/parallel_project_export_worker.rb
new file mode 100644
index 00000000000..ba4194fd4bc
--- /dev/null
+++ b/app/workers/projects/import_export/parallel_project_export_worker.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+module Projects
+ module ImportExport
+ class ParallelProjectExportWorker
+ include ApplicationWorker
+ include ExceptionBacktrace
+
+ idempotent!
+ data_consistency :always
+ deduplicate :until_executed
+ feature_category :importers
+ worker_resource_boundary :memory
+ urgency :low
+ loggable_arguments 1, 2
+ sidekiq_options retries: 3, dead: false, status_expiration: StuckExportJobsWorker::EXPORT_JOBS_EXPIRATION
+
+ sidekiq_retries_exhausted do |job, exception|
+ export_job = ProjectExportJob.find(job['args'].first)
+
+ export_job.fail_op!
+ project = export_job.project
+
+ log_payload = {
+ message: 'Parallel project export error',
+ export_error: job['error_message'],
+ project_export_job_id: export_job.id,
+ project_name: project.name,
+ project_id: project.id
+ }
+ Gitlab::ExceptionLogFormatter.format!(exception, log_payload)
+ Gitlab::Export::Logger.error(log_payload)
+ end
+
+ def perform(project_export_job_id, user_id, after_export_strategy = {})
+ export_job = ProjectExportJob.find(project_export_job_id)
+
+ return if export_job.finished?
+
+ export_job.update_attribute(:jid, jid)
+ current_user = User.find(user_id)
+ after_export = build!(after_export_strategy)
+
+ export_service = ::Projects::ImportExport::ParallelExportService.new(export_job, current_user, after_export)
+ export_service.execute
+
+ export_job.finish!
+ rescue Gitlab::ImportExport::AfterExportStrategyBuilder::StrategyNotFoundError
+ export_job.fail_op!
+ end
+
+ private
+
+ def build!(after_export_strategy)
+ strategy_klass = after_export_strategy&.delete('klass')
+
+ Gitlab::ImportExport::AfterExportStrategyBuilder.build!(strategy_klass, after_export_strategy)
+ end
+ end
+ end
+end
diff --git a/app/workers/projects/inactive_projects_deletion_cron_worker.rb b/app/workers/projects/inactive_projects_deletion_cron_worker.rb
index af62efeb089..31fdb3d9615 100644
--- a/app/workers/projects/inactive_projects_deletion_cron_worker.rb
+++ b/app/workers/projects/inactive_projects_deletion_cron_worker.rb
@@ -22,9 +22,9 @@ module Projects
return unless ::Gitlab::CurrentSettings.delete_inactive_projects?
@start_time ||= ::Gitlab::Metrics::System.monotonic_time
- admin_user = User.admins.active.first
+ admin_bot = ::User.admin_bot
- return unless admin_user
+ return unless admin_bot
notified_inactive_projects = Gitlab::InactiveProjectsDeletionWarningTracker.notified_projects
@@ -39,14 +39,14 @@ module Projects
raise TimeoutError
end
- with_context(project: project, user: admin_user) do
+ with_context(project: project, user: admin_bot) do
deletion_warning_email_sent_on = notified_inactive_projects["project:#{project.id}"]
if send_deletion_warning_email?(deletion_warning_email_sent_on, project)
- send_notification(project, admin_user)
+ send_notification(project, admin_bot)
elsif deletion_warning_email_sent_on && delete_due_to_inactivity?(deletion_warning_email_sent_on)
Gitlab::InactiveProjectsDeletionWarningTracker.new(project.id).reset
- delete_project(project, admin_user)
+ delete_project(project, admin_bot)
end
end
end
diff --git a/app/workers/run_pipeline_schedule_worker.rb b/app/workers/run_pipeline_schedule_worker.rb
index 8974ddce47b..f31f006eec1 100644
--- a/app/workers/run_pipeline_schedule_worker.rb
+++ b/app/workers/run_pipeline_schedule_worker.rb
@@ -10,6 +10,8 @@ class RunPipelineScheduleWorker # rubocop:disable Scalability/IdempotentWorker
queue_namespace :pipeline_creation
feature_category :continuous_integration
+ deduplicate :until_executed
+ idempotent!
def perform(schedule_id, user_id)
schedule = Ci::PipelineSchedule.find_by_id(schedule_id)
diff --git a/app/workers/update_highest_role_worker.rb b/app/workers/update_highest_role_worker.rb
index a05c9c7a1e7..dccf88e1b1a 100644
--- a/app/workers/update_highest_role_worker.rb
+++ b/app/workers/update_highest_role_worker.rb
@@ -7,7 +7,7 @@ class UpdateHighestRoleWorker
sidekiq_options retry: 3
- feature_category :subscription_usage_reports
+ feature_category :subscription_cost_management
urgency :high
weight 2
diff --git a/bin/audit-event-type b/bin/audit-event-type
index 8704dcfc0b0..fec34724c7c 100755
--- a/bin/audit-event-type
+++ b/bin/audit-event-type
@@ -36,7 +36,7 @@ class AuditEventTypeOptionParser
Options = Struct.new(
:name,
:description,
- :group,
+ :feature_category,
:milestone,
:saved_to_database,
:streamed,
@@ -71,9 +71,9 @@ class AuditEventTypeOptionParser
options.description = value
end
- opts.on('-g', '--group [string]', String,
-"Name of the group that introduced this audit event. For example, govern::compliance") do |value|
- options.group = value
+ opts.on('-c', '--feature-category [string]', String,
+"The feature category of this audit event. For example, compliance_management") do |value|
+ options.feature_category = value
end
opts.on('-M', '--milestone [string]', String,
@@ -145,16 +145,16 @@ class AuditEventTypeOptionParser
end
end
- def read_group
+ def read_feature_category
$stdout.puts
- $stdout.puts ">> Specify the group introducing the audit event type, like `govern::compliance`:"
+ $stdout.puts ">> Specify the feature category of this audit event, like `compliance_management`:"
loop do
- group = Readline.readline('?> ', false)&.strip
- group = nil if group.empty?
- return group unless group.nil?
+ feature_category = Readline.readline('?> ', false)&.strip
+ feature_category = nil if feature_category.empty?
+ return feature_category unless feature_category.nil?
- warn "group is a required field."
+ warn "feature_category is a required field."
end
end
@@ -231,7 +231,7 @@ class AuditEventTypeCreator
assert_existing_audit_event_type!
options.description ||= AuditEventTypeOptionParser.read_description
- options.group ||= AuditEventTypeOptionParser.read_group
+ options.feature_category ||= AuditEventTypeOptionParser.read_feature_category
options.milestone ||= AuditEventTypeOptionParser.read_milestone
options.saved_to_database = AuditEventTypeOptionParser.read_saved_to_database if options.saved_to_database.nil?
options.streamed = AuditEventTypeOptionParser.read_streamed if options.streamed.nil?
@@ -263,7 +263,7 @@ class AuditEventTypeCreator
{
'name' => options.name,
'description' => options.description,
- 'group' => options.group,
+ 'feature_category' => options.feature_category,
'milestone' => options.milestone,
'saved_to_database' => options.saved_to_database,
'streamed' => options.streamed,
diff --git a/bin/spring b/bin/spring
index 497ab099954..f3d9e81b53f 100755
--- a/bin/spring
+++ b/bin/spring
@@ -11,7 +11,11 @@ unless (defined?(Spring) || ENV['ENABLE_SPRING'] != '1') && File.basename($0) !=
spring = lockfile.specs.detect { |spec| spec.name == 'spring' }
if spring
Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
- gem 'spring', spring.version
- require 'spring/binstub'
+ begin
+ gem 'spring', spring.version
+ require 'spring/binstub'
+ rescue Gem::MissingSpecError => e
+ $stderr.puts 'INFO: Spring not available.'
+ end
end
end
diff --git a/config/application.rb b/config/application.rb
index 249db9c6a67..a3fe4935fdf 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -284,6 +284,8 @@ module Gitlab
config.assets.precompile << "page_bundles/incident_management_list.css"
config.assets.precompile << "page_bundles/incidents.css"
config.assets.precompile << "page_bundles/issues_analytics.css"
+ config.assets.precompile << "page_bundles/issuable.css"
+ config.assets.precompile << "page_bundles/issuable_list.css"
config.assets.precompile << "page_bundles/issues_list.css"
config.assets.precompile << "page_bundles/issues_show.css"
config.assets.precompile << "page_bundles/jira_connect.css"
@@ -316,13 +318,16 @@ module Gitlab
config.assets.precompile << "page_bundles/roadmap.css"
config.assets.precompile << "page_bundles/requirements.css"
config.assets.precompile << "page_bundles/runner_details.css"
+ config.assets.precompile << "page_bundles/search.css"
config.assets.precompile << "page_bundles/security_dashboard.css"
config.assets.precompile << "page_bundles/security_discover.css"
+ config.assets.precompile << "page_bundles/settings.css"
config.assets.precompile << "page_bundles/signup.css"
config.assets.precompile << "page_bundles/terminal.css"
config.assets.precompile << "page_bundles/terms.css"
config.assets.precompile << "page_bundles/todos.css"
config.assets.precompile << "page_bundles/tree.css"
+ config.assets.precompile << "page_bundles/users.css"
config.assets.precompile << "page_bundles/wiki.css"
config.assets.precompile << "page_bundles/work_items.css"
config.assets.precompile << "page_bundles/xterm.css"
@@ -333,6 +338,7 @@ module Gitlab
config.assets.precompile << "disable_animations.css"
config.assets.precompile << "test_environment.css"
config.assets.precompile << "snippets.css"
+ config.assets.precompile << "fonts.css"
config.assets.precompile << "locale/**/app.js"
config.assets.precompile << "emoji_sprites.css"
config.assets.precompile << "errors.css"
@@ -344,6 +350,11 @@ module Gitlab
config.assets.precompile << "highlight/diff_custom_colors_addition.css"
config.assets.precompile << "highlight/diff_custom_colors_deletion.css"
+ # Import woff2 for fonts
+ config.assets.paths << "#{config.root}/node_modules/@gitlab/fonts/"
+ config.assets.precompile << "gitlab-sans/*.woff2"
+ config.assets.precompile << "jetbrains-mono/*.woff2"
+
# Import gitlab-svgs directly from vendored directory
config.assets.paths << "#{config.root}/node_modules/@gitlab/svgs/dist"
config.assets.paths << "#{config.root}/node_modules/@jihulab/svgs/dist" if Gitlab.jh?
@@ -417,6 +428,21 @@ module Gitlab
expose: headers_to_expose
end
+ allow do
+ origins { |source, env| source == Gitlab::CurrentSettings.jira_connect_proxy_url }
+ resource '/-/jira_connect/oauth_application_id', headers: :any, credentials: false, methods: %i(get options)
+ end
+
+ allow do
+ origins { |source, env| source == Gitlab::CurrentSettings.jira_connect_proxy_url }
+ resource '/-/jira_connect/subscriptions.json', headers: :any, credentials: false, methods: %i(get options)
+ end
+
+ allow do
+ origins { |source, env| source == Gitlab::CurrentSettings.jira_connect_proxy_url }
+ resource '/-/jira_connect/subscriptions/*', headers: :any, credentials: false, methods: %i(delete options)
+ end
+
# Cross-origin requests must be enabled for the Authorization code with PKCE OAuth flow when used from a browser.
%w(/oauth/token /oauth/revoke).each do |oauth_path|
allow do
diff --git a/config/audit_events/types/policy_project_updated.yml b/config/audit_events/types/policy_project_updated.yml
deleted file mode 100644
index 6fffc7f6b10..00000000000
--- a/config/audit_events/types/policy_project_updated.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-name: policy_project_updated
-description: "This event is triggered whenever the security policy project is updated for a project."
-introduced_by_issue: "https://gitlab.com/gitlab-org/gitlab/-/issues/377877"
-introduced_by_mr: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102154"
-milestone: "15.6"
-group: "govern::security policies"
-saved_to_database: true
-streamed: false
diff --git a/config/audit_events/types/type_schema.json b/config/audit_events/types/type_schema.json
index 0d5d79bc4c4..3921b36ba91 100644
--- a/config/audit_events/types/type_schema.json
+++ b/config/audit_events/types/type_schema.json
@@ -28,9 +28,9 @@
"https"
]
},
- "group": {
+ "feature_category": {
"type": "string",
- "description": "Name of the group that introduced this audit event. For example, manage::compliance"
+ "description": "The feature category of this audit event. For example, compliance_management"
},
"milestone": {
"type": "string",
@@ -48,7 +48,7 @@
},
"required": [
"description",
- "group",
+ "feature_category",
"introduced_by_issue",
"introduced_by_mr",
"milestone",
diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml
index c3d9179a160..9afa2b6fca3 100644
--- a/config/dependency_decisions.yml
+++ b/config/dependency_decisions.yml
@@ -245,13 +245,13 @@
- unicode_utils
- MIT
- :who: Aishwarya Subramanain
- :why: https://github.com/hexorx/countries/blob/master/LICENSE
+ :why: https://github.com/countries/countries/blob/master/LICENSE
:versions: []
:when: 2019-09-11 13:08:28.431132000 Z
- - :permit
- "(MIT OR CC0-1.0)"
- - :who:
- :why:
+ - :who:
+ :why:
:versions: []
:when: 2019-11-08 10:03:31.787226000 Z
- - :permit
@@ -369,3 +369,15 @@
:why: https://github.com/gridstack/gridstack.js/blob/v7.0.0/LICENSE
:versions: []
:when: 2022-10-18 16:24:56.611523399 Z
+- - :approve
+ - "@gitlab/fonts"
+ - :who: Lukas Eipert
+ :why: https://gitlab.com/gitlab-com/legal-and-compliance/-/issues/1265
+ :versions: []
+ :when: 2022-12-02 08:24:56.611523399 Z
+- - :approve
+ - llhttp-ffi
+ - :who: Hunter Stewart
+ :why: https://gitlab.com/gitlab-com/legal-and-compliance/-/issues/1293
+ :versions: []
+ :when: 2022-12-09 20:28:33.507704000 Z
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 8f266f2660c..71376b74cfa 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -74,6 +74,10 @@ Rails.application.configure do
# Do not log asset requests
config.assets.quiet = true
+ # Use 'listen' gem to watch for file changes and improve performance
+ # See: https://guides.rubyonrails.org/configuring.html#config-file-watcher
+ config.file_watcher = ActiveSupport::EventedFileUpdateChecker
+
# BetterErrors live shell (REPL) on every stack frame
BetterErrors::Middleware.allow_ip!("127.0.0.1/0")
diff --git a/config/events/1651053267_event_create_service_action_active_users_project_repo.yml b/config/events/1651053267_event_create_service_action_active_users_project_repo.yml
deleted file mode 100644
index bab5dd9e7b3..00000000000
--- a/config/events/1651053267_event_create_service_action_active_users_project_repo.yml
+++ /dev/null
@@ -1,23 +0,0 @@
----
-description: Perform Git operation (read/write/push)
-category: EventCreateService
-action: action_active_users_project_repo
-label_description:
-property_description:
-value_description:
-extra_properties:
-identifiers:
-product_section: dev
-product_stage: create
-product_group: group::source code
-product_category: source_code_management
-milestone: "15.0"
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83795
-distributions:
-- ce
-- ee
-tiers:
-- free
-- premium
-- ultimate
-
diff --git a/config/events/1651053267_event_create_service_project_action.yml b/config/events/1651053267_event_create_service_project_action.yml
new file mode 100644
index 00000000000..a800c5f3fdf
--- /dev/null
+++ b/config/events/1651053267_event_create_service_project_action.yml
@@ -0,0 +1,23 @@
+---
+description: Perform Git operation (read/write/push)
+category: EventCreateService
+action: project_action
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+product_section: dev
+product_stage: create
+product_group: group::source code
+product_category: source_code_management
+milestone: "15.0"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83795
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1655726589_ide_edit_g_edit_by_web_ide.yml b/config/events/1655726589_ide_edit_g_edit_by_web_ide.yml
deleted file mode 100644
index 128bfaf6029..00000000000
--- a/config/events/1655726589_ide_edit_g_edit_by_web_ide.yml
+++ /dev/null
@@ -1,22 +0,0 @@
----
-description: Triggered from backend on editing file in web ide
-category: ide_edit
-action: g_edit_by_web_ide
-identifiers:
-- project
-- user
-- namespace
-product_section: dev
-product_stage: create
-product_group: group::editor
-product_category: web_ide
-milestone: "15.1"
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90484
-distributions:
-- ce
-- ee
-tiers:
-- free
-- premium
-- ultimate
-
diff --git a/config/events/1655726622_ide_edit_g_edit_by_live_preview.yml b/config/events/1655726622_ide_edit_g_edit_by_live_preview.yml
deleted file mode 100644
index d9d54e1c311..00000000000
--- a/config/events/1655726622_ide_edit_g_edit_by_live_preview.yml
+++ /dev/null
@@ -1,22 +0,0 @@
----
-description: Triggered from backend on showing a file in live preview
-category: ide_edit
-action: g_edit_by_live_preview
-identifiers:
- - project
- - user
- - namespace
-product_section: dev
-product_stage: create
-product_group: group::editor
-product_category: web_ide
-milestone: "15.1"
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90484
-distributions:
- - ce
- - ee
-tiers:
- - free
- - premium
- - ultimate
-
diff --git a/config/events/1655726650_ide_edit_g_edit_by_sfe.yml b/config/events/1655726650_ide_edit_g_edit_by_sfe.yml
deleted file mode 100644
index 9acdc317cf5..00000000000
--- a/config/events/1655726650_ide_edit_g_edit_by_sfe.yml
+++ /dev/null
@@ -1,22 +0,0 @@
----
-description: Triggered from backend on editing file by sfe
-category: ide_edit
-action: g_edit_by_sfe
-identifiers:
- - project
- - user
- - namespace
-product_section: dev
-product_stage: create
-product_group: group::editor
-product_category: web_ide
-milestone: "15.1"
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90484
-distributions:
- - ce
- - ee
-tiers:
- - free
- - premium
- - ultimate
-
diff --git a/config/events/1655726683_ide_edit_g_edit_by_snippet_ide.yml b/config/events/1655726683_ide_edit_g_edit_by_snippet_ide.yml
deleted file mode 100644
index 2b3ed1a5d7a..00000000000
--- a/config/events/1655726683_ide_edit_g_edit_by_snippet_ide.yml
+++ /dev/null
@@ -1,22 +0,0 @@
----
-description: Triggered from backend on editing file by ide snippet
-category: ide_edit
-action: g_edit_by_snippet_ide
-identifiers:
- - project
- - user
- - namespace
-product_section: dev
-product_stage: create
-product_group: group::editor
-product_category: web_ide
-milestone: "15.1"
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90484
-distributions:
- - ce
- - ee
-tiers:
- - free
- - premium
- - ultimate
-
diff --git a/config/events/1656510012_merge_requests_i_code_review_user_approve_mr.yml b/config/events/1656510012_merge_requests_i_code_review_user_approve_mr.yml
deleted file mode 100644
index 10ebf12253f..00000000000
--- a/config/events/1656510012_merge_requests_i_code_review_user_approve_mr.yml
+++ /dev/null
@@ -1,26 +0,0 @@
----
-description: Merge request approvals
-category: merge_requests
-action: i_code_review_user_approve_mr
-label_description:
-property_description:
-value_description:
-extra_properties:
-identifiers:
-- project
-- user
-- namespace
-product_section: 'TBD'
-product_stage: create
-product_group: code_review
-product_category: code_review
-milestone: "15.2"
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91493
-distributions:
-- ce
-- ee
-tiers:
-- free
-- premium
-- ultimate
-
diff --git a/config/events/1656690716_post_receive_source_code_pushes.yml b/config/events/1656690716_post_receive_source_code_pushes.yml
deleted file mode 100644
index d0c9fa35d09..00000000000
--- a/config/events/1656690716_post_receive_source_code_pushes.yml
+++ /dev/null
@@ -1,26 +0,0 @@
----
-description: All events of Git push operations
-category: PostReceive
-action: source_code_pushes
-label_description:
-property_description:
-value_description:
-extra_properties:
-identifiers:
-- project
-- user
-- namespace
-product_section: dev
-product_stage: create
-product_group: source_code
-product_category: source_code_management
-milestone: "15.2"
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91605
-distributions:
-- ce
-- ee
-tiers:
-- free
-- premium
-- ultimate
-
diff --git a/config/events/1669277827_API__Commits_commit.yml b/config/events/1669277827_API__Commits_commit.yml
new file mode 100644
index 00000000000..db95a997100
--- /dev/null
+++ b/config/events/1669277827_API__Commits_commit.yml
@@ -0,0 +1,26 @@
+---
+description: Mirrored Service Ping Redis metric counts.web_ide_commits - Count of commits made from the Web IDE
+category: API::Commits
+action: commit
+label_description: key_path of Service Ping metric counts.web_ide_commits
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: create
+product_group: editor
+product_category: web_ide
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104947
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1669597397_Gitlab__UsageDataCounters__EditorUniqueCounter_ide_edit.yml b/config/events/1669597397_Gitlab__UsageDataCounters__EditorUniqueCounter_ide_edit.yml
new file mode 100644
index 00000000000..67240ae2363
--- /dev/null
+++ b/config/events/1669597397_Gitlab__UsageDataCounters__EditorUniqueCounter_ide_edit.yml
@@ -0,0 +1,21 @@
+---
+description: Triggered from backend on interaction with web ide
+category: Gitlab::UsageDataCounters::EditorUniqueCounter
+action: ide_edit
+identifiers:
+ - project
+ - user
+ - namespace
+product_section: dev
+product_stage: create
+product_group: group::editor
+product_category: web_ide
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104809
+distributions:
+ - ce
+ - ee
+tiers:
+ - free
+ - premium
+ - ultimate
diff --git a/config/events/1669605315_PostReceive_push.yml b/config/events/1669605315_PostReceive_push.yml
new file mode 100644
index 00000000000..da79e7531cf
--- /dev/null
+++ b/config/events/1669605315_PostReceive_push.yml
@@ -0,0 +1,21 @@
+---
+description: Mirrored Redis source_code_pushes events sent to Snowplow
+category: PostReceive
+action: push
+identifiers:
+ - project
+ - user
+ - namespace
+product_section: dev
+product_stage: create
+product_group: source_code
+product_category: source_code_management
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104670
+distributions:
+ - ce
+ - ee
+tiers:
+ - free
+ - premium
+ - ultimate
diff --git a/config/events/1669605645_Gitlab__UsageDataCounters__MergeRequestActivityUniqueCounter_approve.yml b/config/events/1669605645_Gitlab__UsageDataCounters__MergeRequestActivityUniqueCounter_approve.yml
new file mode 100644
index 00000000000..a4960120659
--- /dev/null
+++ b/config/events/1669605645_Gitlab__UsageDataCounters__MergeRequestActivityUniqueCounter_approve.yml
@@ -0,0 +1,22 @@
+---
+description: Mirrored RedisHLL i_code_review_user_approve_mr_monthly events sent to Snowplow
+category: Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter
+action: approve
+identifiers:
+ - project
+ - user
+ - namespace
+product_stage: create
+product_group: code_review
+product_category: code_review
+product_section: 'TBD'
+milestone: "15.7"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104670"
+distributions:
+ - ce
+ - ee
+tiers:
+ - free
+ - premium
+ - ultimate
+
diff --git a/config/events/1669814629_StatusPage__PublishService_incident_management_incident_published.yml b/config/events/1669814629_StatusPage__PublishService_incident_management_incident_published.yml
new file mode 100644
index 00000000000..9e6f699786b
--- /dev/null
+++ b/config/events/1669814629_StatusPage__PublishService_incident_management_incident_published.yml
@@ -0,0 +1,24 @@
+---
+description: Mirrored Service Ping Redis metric. Count of unique users that published incidents per month
+category: StatusPage::PublishService
+action: incident_management_incident_published
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: ops
+product_stage: monitor
+product_group: monitor
+product_category:
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
+distributions:
+- ee
+tiers:
+- premium
+- ultimate
+
diff --git a/config/events/1669815074_Mutations__AlertManagement__Alerts__Todo__Create_incident_management_alert_todo.yml b/config/events/1669815074_Mutations__AlertManagement__Alerts__Todo__Create_incident_management_alert_todo.yml
new file mode 100644
index 00000000000..f94db886c51
--- /dev/null
+++ b/config/events/1669815074_Mutations__AlertManagement__Alerts__Todo__Create_incident_management_alert_todo.yml
@@ -0,0 +1,26 @@
+---
+description: Migrated Service Ping metric. Count of unique users adding alerts to the TODO list
+category: Mutations::AlertManagement::Alerts::Todo::Create
+action: incident_management_alert_todo
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: ops
+product_stage: monitor
+product_group: monitor
+product_category:
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223/diffs
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1669817378_Mutations__AlertManagement__Alerts__SetAssignees_incident_management_alert_assigned.yml b/config/events/1669817378_Mutations__AlertManagement__Alerts__SetAssignees_incident_management_alert_assigned.yml
new file mode 100644
index 00000000000..4b2c786149f
--- /dev/null
+++ b/config/events/1669817378_Mutations__AlertManagement__Alerts__SetAssignees_incident_management_alert_assigned.yml
@@ -0,0 +1,26 @@
+---
+description: Count of unique users assigning an alert per week. Migrated form Service Ping metric
+category: Mutations::AlertManagement::Alerts::SetAssignees
+action: incident_management_alert_assigned
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: ops
+product_stage: monitor
+product_group: monitor
+product_category:
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1669817630_Mutations__AlertManagement__CreateAlertIssue_incident_management_incident_created.yml b/config/events/1669817630_Mutations__AlertManagement__CreateAlertIssue_incident_management_incident_created.yml
new file mode 100644
index 00000000000..28bd7ba89c6
--- /dev/null
+++ b/config/events/1669817630_Mutations__AlertManagement__CreateAlertIssue_incident_management_incident_created.yml
@@ -0,0 +1,26 @@
+---
+description: Migrated from Service Ping metric. Count of unique users creating incidents
+category: Mutations::AlertManagement::CreateAlertIssue
+action: incident_management_incident_created
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: ops
+product_stage: monitor
+product_group: monitor
+product_category:
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1669817815_Mutations__AlertManagement__UpdateAlertStatus_incident_management_alert_status_change.yml b/config/events/1669817815_Mutations__AlertManagement__UpdateAlertStatus_incident_management_alert_status_change.yml
new file mode 100644
index 00000000000..409d1186348
--- /dev/null
+++ b/config/events/1669817815_Mutations__AlertManagement__UpdateAlertStatus_incident_management_alert_status_change.yml
@@ -0,0 +1,26 @@
+---
+description: Count of unique users changing alert's status. Migrated from Service Ping metric
+category: Mutations::AlertManagement::UpdateAlertStatus
+action: incident_management_alert_status_changed
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: ops
+product_stage: monitor
+product_group: monitor
+product_category:
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1669818009_IncidentManagement__TimelineEvents__CreateService_incident_management_timeline_event_.yml b/config/events/1669818009_IncidentManagement__TimelineEvents__CreateService_incident_management_timeline_event_.yml
new file mode 100644
index 00000000000..b3033c4aa3b
--- /dev/null
+++ b/config/events/1669818009_IncidentManagement__TimelineEvents__CreateService_incident_management_timeline_event_.yml
@@ -0,0 +1,27 @@
+---
+description: Count of unique users created timeline events
+category: IncidentManagement::TimelineEvents::CreateService
+action: incident_management_timeline_event_created
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: ops
+product_stage: monitor
+product_group: respond
+product_category: incident_management
+value_type: number
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1669902189_IncidentManagement__TimelineEvents__DestroyService_incident_management_timeline_event.yml b/config/events/1669902189_IncidentManagement__TimelineEvents__DestroyService_incident_management_timeline_event.yml
new file mode 100644
index 00000000000..a314f3c7b8e
--- /dev/null
+++ b/config/events/1669902189_IncidentManagement__TimelineEvents__DestroyService_incident_management_timeline_event.yml
@@ -0,0 +1,26 @@
+---
+category: IncidentManagement::TimelineEvents::DestroyService
+action: incident_management_timeline_event_deleted
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+description: "Event migrates from Service Ping metric. Count of unique users deleted timeline events"
+product_section: ops
+product_stage: monitor
+product_group: respond
+product_category: incident_management
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1669902383_IncidentManagement__TimelineEvents__UpdateService_incident_management_timeline_event_.yml b/config/events/1669902383_IncidentManagement__TimelineEvents__UpdateService_incident_management_timeline_event_.yml
new file mode 100644
index 00000000000..afab1a0f531
--- /dev/null
+++ b/config/events/1669902383_IncidentManagement__TimelineEvents__UpdateService_incident_management_timeline_event_.yml
@@ -0,0 +1,26 @@
+---
+category: IncidentManagement::TimelineEvents::UpdateService
+action: incident_management_timeline_event_edited
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+description: "Event migrated form Service Ping metric. Count of unique users edited timeline events"
+product_section: ops
+product_stage: monitor
+product_group: respond
+product_category: incident_management
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1669902538_IssueLinks__CreateService_incident_management_incident_relate.yml b/config/events/1669902538_IssueLinks__CreateService_incident_management_incident_relate.yml
new file mode 100644
index 00000000000..00ac7581617
--- /dev/null
+++ b/config/events/1669902538_IssueLinks__CreateService_incident_management_incident_relate.yml
@@ -0,0 +1,26 @@
+---
+category: IssueLinks::CreateService
+action: incident_management_incident_relate
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+description: "Count of unique users adding issues per that are related to an incident. Migrated from Service Ping"
+product_section: ops
+product_stage: monitor
+product_group: monitor
+product_category:
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1669902705_IssueLinks__DestroyService_incident_management_incident_unrelate.yml b/config/events/1669902705_IssueLinks__DestroyService_incident_management_incident_unrelate.yml
new file mode 100644
index 00000000000..4870e2b1f04
--- /dev/null
+++ b/config/events/1669902705_IssueLinks__DestroyService_incident_management_incident_unrelate.yml
@@ -0,0 +1,26 @@
+---
+category: IssueLinks::DestroyService
+action: incident_management_incident_unrelate
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+milestone: "15.7"
+description: "Count of unique users removing issue that are related to an incident. Migrated from Service Ping metric"
+product_section: ops
+product_stage: monitor
+product_group: monitor
+product_category:
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1669902889_Issues__CloseService_incident_management_incident_closed.yml b/config/events/1669902889_Issues__CloseService_incident_management_incident_closed.yml
new file mode 100644
index 00000000000..8e6f54876b2
--- /dev/null
+++ b/config/events/1669902889_Issues__CloseService_incident_management_incident_closed.yml
@@ -0,0 +1,26 @@
+---
+category: Issues::CloseService
+action: incident_management_incident_closed
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+description: "Count of users closing incidents. Migrated from Service Ping metric."
+product_section: ops
+product_stage: monitor
+product_group: monitor
+product_category:
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1669903092_Issues__ReopenService_incident_management_incident_reopened.yml b/config/events/1669903092_Issues__ReopenService_incident_management_incident_reopened.yml
new file mode 100644
index 00000000000..33118e11051
--- /dev/null
+++ b/config/events/1669903092_Issues__ReopenService_incident_management_incident_reopened.yml
@@ -0,0 +1,26 @@
+---
+category: Issues::ReopenService
+action: incident_management_incident_reopened
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+description: "Count of unique users reopening incidents. Migrated from Service Ping metric."
+product_section: ops
+product_stage: monitor
+product_group: monitor
+product_category:
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1669903273_Issues__UpdateService_incident_management_incident_change_confidential.yml b/config/events/1669903273_Issues__UpdateService_incident_management_incident_change_confidential.yml
new file mode 100644
index 00000000000..2992667da31
--- /dev/null
+++ b/config/events/1669903273_Issues__UpdateService_incident_management_incident_change_confidential.yml
@@ -0,0 +1,26 @@
+---
+category: Issues::UpdateService
+action: incident_management_incident_change_confidential
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+description: "Count of unique users changing incidents to confidential. Event migrated from Service Ping metric."
+product_section: ops
+product_stage: monitor
+product_group: monitor
+product_category:
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1669903414_Issues__ZoomLinkService_incident_management_incident_zoom_meeting.yml b/config/events/1669903414_Issues__ZoomLinkService_incident_management_incident_zoom_meeting.yml
new file mode 100644
index 00000000000..5dc7506bc21
--- /dev/null
+++ b/config/events/1669903414_Issues__ZoomLinkService_incident_management_incident_zoom_meeting.yml
@@ -0,0 +1,26 @@
+---
+category: Issues::ZoomLinkService
+action: incident_management_incident_zoom_meeting
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+description: "Count of unique users creating Zoom meetings about incidents. Event migrated from Service Ping metric."
+product_section: ops
+product_stage: monitor
+product_group: monitor
+product_category:
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1669903530_Notes__CreateService_incident_management_incident_comment.yml b/config/events/1669903530_Notes__CreateService_incident_management_incident_comment.yml
new file mode 100644
index 00000000000..f7b619e3277
--- /dev/null
+++ b/config/events/1669903530_Notes__CreateService_incident_management_incident_comment.yml
@@ -0,0 +1,26 @@
+---
+category: Notes::CreateService
+action: incident_management_incident_comment
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+description: "Count of unique users adding comments on incidents. Event migrated from Service Ping metric"
+product_section: ops
+product_stage: monitor
+product_group: monitor
+product_category:
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1669903650_TodoService_incident_management_incident_todo.yml b/config/events/1669903650_TodoService_incident_management_incident_todo.yml
new file mode 100644
index 00000000000..b8eee5ce23e
--- /dev/null
+++ b/config/events/1669903650_TodoService_incident_management_incident_todo.yml
@@ -0,0 +1,26 @@
+---
+category: TodoService
+action: incident_management_incident_todo
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+description: "Count of unique users adding incidents to the TODO list. Event migrated from Service Ping metric"
+product_section: ops
+product_stage: monitor
+product_group: monitor
+product_category:
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1670570965_Issues__UpdateService_incident_management_incident_assigned.yml b/config/events/1670570965_Issues__UpdateService_incident_management_incident_assigned.yml
new file mode 100644
index 00000000000..22c1a41127b
--- /dev/null
+++ b/config/events/1670570965_Issues__UpdateService_incident_management_incident_assigned.yml
@@ -0,0 +1,26 @@
+---
+description: Count of unique users assiging incidents per
+category: Issues::UpdateService
+action: incident_management_incident_assigned
+label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: ops
+product_stage: monitor
+product_group: monitor
+product_category:
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1671198983_Gitlab__UsageDataCounters__MergeRequestActivityUniqueCounter_create.yml b/config/events/1671198983_Gitlab__UsageDataCounters__MergeRequestActivityUniqueCounter_create.yml
new file mode 100644
index 00000000000..9c1b389b4b2
--- /dev/null
+++ b/config/events/1671198983_Gitlab__UsageDataCounters__MergeRequestActivityUniqueCounter_create.yml
@@ -0,0 +1,27 @@
+---
+description: Count of unique merge requests created per month
+category: Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter
+action: create
+label_description: "Mirrored RedisHLL i_code_review_user_create_mr_monthly events sent to Snowplow"
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106869
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/schema.json b/config/events/schema.json
index 3a0616e706b..6ce93e1a40e 100644
--- a/config/events/schema.json
+++ b/config/events/schema.json
@@ -12,59 +12,101 @@
"type": "string"
},
"label_description": {
- "type": ["string", "null"]
+ "type": [
+ "string",
+ "null"
+ ]
},
"property_description": {
- "type": ["string", "null"]
+ "type": [
+ "string",
+ "null"
+ ]
},
"value_description": {
- "type": ["string", "null"]
+ "type": [
+ "string",
+ "null"
+ ]
},
"extra_properties": {
- "type": ["object", "null"]
+ "type": [
+ "object",
+ "null"
+ ]
},
"identifiers": {
- "type": ["array", "null"],
+ "type": [
+ "array",
+ "null"
+ ],
"items": {
"type": "string",
- "enum": ["project", "user", "namespace"]
+ "enum": [
+ "project",
+ "user",
+ "namespace"
+ ]
}
},
"iglu_schema_url": {
- "type": ["string", "null"]
+ "type": [
+ "string",
+ "null"
+ ]
},
"product_section": {
"type": "string"
},
"product_stage": {
- "type": ["string", "null"]
+ "type": [
+ "string",
+ "null"
+ ]
},
"product_group": {
"type": "string"
},
"product_category": {
- "type": ["string", "null"]
+ "type": [
+ "string",
+ "null"
+ ]
},
"introduced_by_url": {
- "type": ["uri", "null"]
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "uri"
},
"milestone": {
- "type": ["string", "null"],
+ "type": [
+ "string",
+ "null"
+ ],
"pattern": "^[0-9]+\\.[0-9]+$"
},
"distributions": {
"type": "array",
"items": {
"type": "string",
- "enum": ["ee", "ce"]
+ "enum": [
+ "ee",
+ "ce"
+ ]
}
},
"tiers": {
"type": "array",
"items": {
"type": "string",
- "enum": ["free", "premium", "ultimate"]
+ "enum": [
+ "free",
+ "premium",
+ "ultimate"
+ ]
}
}
}
-}
+} \ No newline at end of file
diff --git a/config/feature_categories.yml b/config/feature_categories.yml
index 94a50dc416e..ae2eb043fdd 100644
--- a/config/feature_categories.yml
+++ b/config/feature_categories.yml
@@ -25,6 +25,8 @@
- cluster_cost_management
- code_quality
- code_review
+- code_search
+- code_suggestions
- code_testing
- commerce_integrations
- compliance_management
@@ -49,6 +51,7 @@
- design_system
- devops_reports
- disaster_recovery
+- dora_metrics
- dynamic_application_security_testing
- editor_extension
- environment_management
@@ -67,7 +70,6 @@
- geo_replication
- git_lfs
- gitaly
-- gitlab_docs
- global_search
- helm_chart_registry
- importers
@@ -105,6 +107,7 @@
- pubsec_services
- purchase
- quality_management
+- rate_limiting
- redis
- release_evidence
- release_orchestration
@@ -116,6 +119,7 @@
- runner_fleet
- runner_saas
- saas_provisioning
+- sbom
- scalability
- secret_detection
- secrets_management
@@ -124,13 +128,11 @@
- service_desk
- service_ping
- sm_provisioning
-- snippets
- source_code_management
- static_application_security_testing
- subgroups
- subscription_cost_management
- subscription_management
-- subscription_usage_reports
- system_access
- team_planning
- tracing
diff --git a/config/feature_flags/development/actors_aware_gitaly_calls.yml b/config/feature_flags/development/actors_aware_gitaly_calls.yml
deleted file mode 100644
index 0ae6140c579..00000000000
--- a/config/feature_flags/development/actors_aware_gitaly_calls.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: actors_aware_gitaly_calls
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101218
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381516
-milestone: '15.6'
-type: development
-group: group::gitaly
-default_enabled: false
diff --git a/config/feature_flags/development/add_refresh_pull_mirror_worker.yml b/config/feature_flags/development/add_refresh_pull_mirror_worker.yml
new file mode 100644
index 00000000000..f59f0ff3d8e
--- /dev/null
+++ b/config/feature_flags/development/add_refresh_pull_mirror_worker.yml
@@ -0,0 +1,8 @@
+---
+name: add_refresh_pull_mirror_worker
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103665
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382226
+milestone: '15.7'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/config/feature_flags/development/allow_audit_event_type_filtering.yml b/config/feature_flags/development/allow_audit_event_type_filtering.yml
deleted file mode 100644
index e5cbd2fddcf..00000000000
--- a/config/feature_flags/development/allow_audit_event_type_filtering.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: allow_audit_event_type_filtering
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102502
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373833
-milestone: '15.6'
-type: development
-group: group::compliance
-default_enabled: false
diff --git a/config/feature_flags/development/allow_dots_on_tf_state_names.yml b/config/feature_flags/development/allow_dots_on_tf_state_names.yml
new file mode 100644
index 00000000000..3bbff808eda
--- /dev/null
+++ b/config/feature_flags/development/allow_dots_on_tf_state_names.yml
@@ -0,0 +1,8 @@
+---
+name: allow_dots_on_tf_state_names
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106861
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385597
+milestone: '15.7'
+type: development
+group: group::configure
+default_enabled: false
diff --git a/config/feature_flags/development/always_async_project_authorizations_refresh.yml b/config/feature_flags/development/always_async_project_authorizations_refresh.yml
deleted file mode 100644
index f5ec2473af8..00000000000
--- a/config/feature_flags/development/always_async_project_authorizations_refresh.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: always_async_project_authorizations_refresh
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92333
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367683
-milestone: '15.3'
-type: development
-group: group::workspace
-default_enabled: true
diff --git a/config/feature_flags/development/approval_rules_pagination.yml b/config/feature_flags/development/approval_rules_pagination.yml
deleted file mode 100644
index 78d4ad37ced..00000000000
--- a/config/feature_flags/development/approval_rules_pagination.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: approval_rules_pagination
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91702
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366823
-milestone: '15.2'
-type: development
-group: group::source code
-default_enabled: true
diff --git a/config/feature_flags/development/automated_email_provision.yml b/config/feature_flags/development/automated_email_provision.yml
deleted file mode 100644
index 2734413dd8f..00000000000
--- a/config/feature_flags/development/automated_email_provision.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: automated_email_provision
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75872
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/348317
-milestone: '14.6'
-type: development
-group: group::license
-default_enabled: true
diff --git a/config/feature_flags/development/ban_user_feature_flag.yml b/config/feature_flags/development/ban_user_feature_flag.yml
index d06a0668549..74aee3f46f8 100644
--- a/config/feature_flags/development/ban_user_feature_flag.yml
+++ b/config/feature_flags/development/ban_user_feature_flag.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61292
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/330667
milestone: '13.12'
type: development
-group: group::access
+group: group::authentication and authorization
default_enabled: true
diff --git a/config/feature_flags/development/batched_migrations_parallel_execution.yml b/config/feature_flags/development/batched_migrations_parallel_execution.yml
new file mode 100644
index 00000000000..8cd030b8912
--- /dev/null
+++ b/config/feature_flags/development/batched_migrations_parallel_execution.yml
@@ -0,0 +1,8 @@
+---
+name: batched_migrations_parallel_execution
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104027
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/384883
+milestone: '15.7'
+type: development
+group: group::database
+default_enabled: false
diff --git a/config/feature_flags/development/block_weak_passwords.yml b/config/feature_flags/development/block_weak_passwords.yml
deleted file mode 100644
index aaa8c2cac38..00000000000
--- a/config/feature_flags/development/block_weak_passwords.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: block_weak_passwords
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86310
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/363445
-milestone: '15.4'
-type: development
-group: group::authentication and authorization
-default_enabled: false
diff --git a/config/feature_flags/development/cache_project_integrations.yml b/config/feature_flags/development/cache_project_integrations.yml
new file mode 100644
index 00000000000..3bb652d4b51
--- /dev/null
+++ b/config/feature_flags/development/cache_project_integrations.yml
@@ -0,0 +1,8 @@
+---
+name: cache_project_integrations
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104062
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/384004
+milestone: '15.7'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/config/feature_flags/development/check_etags_diffs_batch_before_write_cache.yml b/config/feature_flags/development/check_etags_diffs_batch_before_write_cache.yml
deleted file mode 100644
index fb03ff91d0a..00000000000
--- a/config/feature_flags/development/check_etags_diffs_batch_before_write_cache.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: check_etags_diffs_batch_before_write_cache
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101421
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/378333
-milestone: '15.6'
-type: development
-group: group::code review
-default_enabled: false
diff --git a/config/feature_flags/development/check_ip_address_for_email_verification.yml b/config/feature_flags/development/check_ip_address_for_email_verification.yml
new file mode 100644
index 00000000000..1d0c640d9a4
--- /dev/null
+++ b/config/feature_flags/development/check_ip_address_for_email_verification.yml
@@ -0,0 +1,8 @@
+---
+name: check_ip_address_for_email_verification
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106441
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385196
+milestone: "15.7"
+type: development
+group: group::anti-abuse
+default_enabled: false
diff --git a/config/feature_flags/development/ci_assign_job_token_on_scheduling.yml b/config/feature_flags/development/ci_assign_job_token_on_scheduling.yml
deleted file mode 100644
index 179fef03d5e..00000000000
--- a/config/feature_flags/development/ci_assign_job_token_on_scheduling.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_assign_job_token_on_scheduling
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103377
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382042
-milestone: '15.6'
-type: development
-group: group::pipeline execution
-default_enabled: false
diff --git a/config/feature_flags/development/ci_bridge_remove_sourced_pipelines.yml b/config/feature_flags/development/ci_bridge_remove_sourced_pipelines.yml
new file mode 100644
index 00000000000..503e676d4ab
--- /dev/null
+++ b/config/feature_flags/development/ci_bridge_remove_sourced_pipelines.yml
@@ -0,0 +1,8 @@
+---
+name: ci_bridge_remove_sourced_pipelines
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105708
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/384359
+milestone: '15.7'
+type: development
+group: group::pipeline authoring
+default_enabled: false
diff --git a/config/feature_flags/development/ci_build_partition_id_token_prefix.yml b/config/feature_flags/development/ci_build_partition_id_token_prefix.yml
new file mode 100644
index 00000000000..5b3cd22a489
--- /dev/null
+++ b/config/feature_flags/development/ci_build_partition_id_token_prefix.yml
@@ -0,0 +1,8 @@
+---
+name: ci_build_partition_id_token_prefix
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106179
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385401
+milestone: '15.7'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/config/feature_flags/development/ci_enforce_rate_limits_jobs_api.yml b/config/feature_flags/development/ci_enforce_rate_limits_jobs_api.yml
new file mode 100644
index 00000000000..14c435f294a
--- /dev/null
+++ b/config/feature_flags/development/ci_enforce_rate_limits_jobs_api.yml
@@ -0,0 +1,8 @@
+---
+name: ci_enforce_rate_limits_jobs_api
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104912
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/384186
+milestone: '15.7'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/config/feature_flags/development/ci_hooks_pre_get_sources_script.yml b/config/feature_flags/development/ci_hooks_pre_get_sources_script.yml
new file mode 100644
index 00000000000..42afd4235cc
--- /dev/null
+++ b/config/feature_flags/development/ci_hooks_pre_get_sources_script.yml
@@ -0,0 +1,8 @@
+---
+name: ci_hooks_pre_get_sources_script
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102332
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381840
+milestone: '15.6'
+type: development
+group: group::pipeline authoring
+default_enabled: false
diff --git a/config/feature_flags/development/ci_job_token_scope.yml b/config/feature_flags/development/ci_job_token_scope.yml
index 464fc77a69e..c86a3eca25d 100644
--- a/config/feature_flags/development/ci_job_token_scope.yml
+++ b/config/feature_flags/development/ci_job_token_scope.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49750
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/300821
milestone: '13.12'
type: development
-group: group::package
+group: group::package registry
default_enabled: false
diff --git a/config/feature_flags/development/ci_partitioning_use_ci_builds_metadata_routing_table.yml b/config/feature_flags/development/ci_partitioning_use_ci_builds_metadata_routing_table.yml
deleted file mode 100644
index 71c2aa735a2..00000000000
--- a/config/feature_flags/development/ci_partitioning_use_ci_builds_metadata_routing_table.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_partitioning_use_ci_builds_metadata_routing_table
-introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100935"
-rollout_issue_url: "https://gitlab.com/gitlab-org/gitlab/-/issues/378601"
-milestone: '15.6'
-type: development
-group: "group::pipeline execution"
-default_enabled: false
diff --git a/config/feature_flags/development/ci_raw_variables_in_yaml_config.yml b/config/feature_flags/development/ci_raw_variables_in_yaml_config.yml
index ab135526c0b..0b6fc6022f4 100644
--- a/config/feature_flags/development/ci_raw_variables_in_yaml_config.yml
+++ b/config/feature_flags/development/ci_raw_variables_in_yaml_config.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/375034
milestone: '15.6'
type: development
group: group::pipeline authoring
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/ci_refactoring_external_mapper.yml b/config/feature_flags/development/ci_refactoring_external_mapper.yml
new file mode 100644
index 00000000000..22933d253d4
--- /dev/null
+++ b/config/feature_flags/development/ci_refactoring_external_mapper.yml
@@ -0,0 +1,8 @@
+---
+name: ci_refactoring_external_mapper
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106408
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385179
+milestone: '15.7'
+type: development
+group: group::pipeline authoring
+default_enabled: false
diff --git a/config/feature_flags/development/ci_register_job_temporary_lock.yml b/config/feature_flags/development/ci_register_job_temporary_lock.yml
index f404df8f85b..d839669b2ea 100644
--- a/config/feature_flags/development/ci_register_job_temporary_lock.yml
+++ b/config/feature_flags/development/ci_register_job_temporary_lock.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55202
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323180
milestone: '13.10'
type: development
-group: group::memory
+group: group::pipeline excution
default_enabled: false
diff --git a/config/feature_flags/development/ci_retry_job_fix.yml b/config/feature_flags/development/ci_retry_job_fix.yml
deleted file mode 100644
index 30782e57dea..00000000000
--- a/config/feature_flags/development/ci_retry_job_fix.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_retry_job_fix
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100712
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/207988
-milestone: '15.6'
-type: development
-group: group::pipeline execution
-default_enabled: false
diff --git a/config/feature_flags/development/ci_reuse_build_in_seed_context.yml b/config/feature_flags/development/ci_reuse_build_in_seed_context.yml
new file mode 100644
index 00000000000..aa63b8d898f
--- /dev/null
+++ b/config/feature_flags/development/ci_reuse_build_in_seed_context.yml
@@ -0,0 +1,8 @@
+---
+name: ci_reuse_build_in_seed_context
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105492
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/384577
+milestone: '15.7'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/config/feature_flags/development/ci_secure_files.yml b/config/feature_flags/development/ci_secure_files.yml
deleted file mode 100644
index a1aa82fe298..00000000000
--- a/config/feature_flags/development/ci_secure_files.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_secure_files
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78227
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350748
-milestone: '14.8'
-type: development
-group: group::incubation
-default_enabled: false
diff --git a/config/feature_flags/development/ci_skip_auto_cancelation_on_child_pipelines.yml b/config/feature_flags/development/ci_skip_auto_cancelation_on_child_pipelines.yml
deleted file mode 100644
index 71d5836bee1..00000000000
--- a/config/feature_flags/development/ci_skip_auto_cancelation_on_child_pipelines.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_skip_auto_cancelation_on_child_pipelines
-introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100854"
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/377712
-milestone: '15.5'
-type: development
-group: group::pipeline execution
-default_enabled: false
diff --git a/config/feature_flags/development/ci_update_unlocked_job_artifacts.yml b/config/feature_flags/development/ci_update_unlocked_job_artifacts.yml
deleted file mode 100644
index 7cb79c05774..00000000000
--- a/config/feature_flags/development/ci_update_unlocked_job_artifacts.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_update_unlocked_job_artifacts
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70235
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343465
-milestone: '14.5'
-type: development
-group: group::pipeline insights
-default_enabled: true
diff --git a/config/feature_flags/development/collect_package_events.yml b/config/feature_flags/development/collect_package_events.yml
index 488dce6a724..68ceb596d8a 100644
--- a/config/feature_flags/development/collect_package_events.yml
+++ b/config/feature_flags/development/collect_package_events.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45802
rollout_issue_url:
milestone: '13.6'
type: development
-group: group::package
+group: group::package registry
default_enabled: false
diff --git a/config/feature_flags/development/container_registry_delete_repository_with_cron_worker.yml b/config/feature_flags/development/container_registry_delete_repository_with_cron_worker.yml
deleted file mode 100644
index ef531228398..00000000000
--- a/config/feature_flags/development/container_registry_delete_repository_with_cron_worker.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: container_registry_delete_repository_with_cron_worker
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101918
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/378818
-milestone: '15.6'
-type: development
-group: group::container registry
-default_enabled: false
diff --git a/config/feature_flags/development/container_registry_migration_limit_gitlab_org.yml b/config/feature_flags/development/container_registry_migration_limit_gitlab_org.yml
index 8b952b79f9b..3fbe5acb0d1 100644
--- a/config/feature_flags/development/container_registry_migration_limit_gitlab_org.yml
+++ b/config/feature_flags/development/container_registry_migration_limit_gitlab_org.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78613
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350543
milestone: '14.8'
type: development
-group: group::package
+group: group::container registry
default_enabled: false
diff --git a/config/feature_flags/development/container_registry_migration_phase2_all_plans.yml b/config/feature_flags/development/container_registry_migration_phase2_all_plans.yml
index 6742a006519..bcb116ea98e 100644
--- a/config/feature_flags/development/container_registry_migration_phase2_all_plans.yml
+++ b/config/feature_flags/development/container_registry_migration_phase2_all_plans.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83135
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350543
milestone: '14.10'
type: development
-group: group::package
+group: group::container registry
default_enabled: false
diff --git a/config/feature_flags/development/container_registry_migration_phase2_capacity_1.yml b/config/feature_flags/development/container_registry_migration_phase2_capacity_1.yml
index 846bc8b690b..2d47d608f2d 100644
--- a/config/feature_flags/development/container_registry_migration_phase2_capacity_1.yml
+++ b/config/feature_flags/development/container_registry_migration_phase2_capacity_1.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79061
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350543
milestone: '14.8'
type: development
-group: group::package
+group: group::container registry
default_enabled: false
diff --git a/config/feature_flags/development/container_registry_migration_phase2_capacity_10.yml b/config/feature_flags/development/container_registry_migration_phase2_capacity_10.yml
index fcbcc5bfb48..c03c063d9a2 100644
--- a/config/feature_flags/development/container_registry_migration_phase2_capacity_10.yml
+++ b/config/feature_flags/development/container_registry_migration_phase2_capacity_10.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79061
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350543
milestone: '14.8'
type: development
-group: group::package
+group: group::container registry
default_enabled: false
diff --git a/config/feature_flags/development/container_registry_migration_phase2_capacity_2.yml b/config/feature_flags/development/container_registry_migration_phase2_capacity_2.yml
index 11e66a5531e..febf7d323b4 100644
--- a/config/feature_flags/development/container_registry_migration_phase2_capacity_2.yml
+++ b/config/feature_flags/development/container_registry_migration_phase2_capacity_2.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85277
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350543
milestone: '14.10'
type: development
-group: group::package
+group: group::container registry
default_enabled: false
diff --git a/config/feature_flags/development/container_registry_migration_phase2_capacity_25.yml b/config/feature_flags/development/container_registry_migration_phase2_capacity_25.yml
index b52693e0aba..35a33ebd281 100644
--- a/config/feature_flags/development/container_registry_migration_phase2_capacity_25.yml
+++ b/config/feature_flags/development/container_registry_migration_phase2_capacity_25.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79061
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350543
milestone: '14.8'
type: development
-group: group::package
+group: group::container registry
default_enabled: false
diff --git a/config/feature_flags/development/container_registry_migration_phase2_capacity_40.yml b/config/feature_flags/development/container_registry_migration_phase2_capacity_40.yml
index f26ddd99607..2f35c3105fc 100644
--- a/config/feature_flags/development/container_registry_migration_phase2_capacity_40.yml
+++ b/config/feature_flags/development/container_registry_migration_phase2_capacity_40.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86543
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350543
milestone: '15.1'
type: development
-group: group::package
+group: group::container registry
default_enabled: false
diff --git a/config/feature_flags/development/container_registry_migration_phase2_capacity_5.yml b/config/feature_flags/development/container_registry_migration_phase2_capacity_5.yml
index 544354f1b84..80903b0a522 100644
--- a/config/feature_flags/development/container_registry_migration_phase2_capacity_5.yml
+++ b/config/feature_flags/development/container_registry_migration_phase2_capacity_5.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85908
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350543
milestone: '15.0'
type: development
-group: group::package
+group: group::container registry
default_enabled: false
diff --git a/config/feature_flags/development/container_registry_migration_phase2_delete_container_repository_worker_support.yml b/config/feature_flags/development/container_registry_migration_phase2_delete_container_repository_worker_support.yml
index f6a5ae36c07..6635151fe19 100644
--- a/config/feature_flags/development/container_registry_migration_phase2_delete_container_repository_worker_support.yml
+++ b/config/feature_flags/development/container_registry_migration_phase2_delete_container_repository_worker_support.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88997
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350543
milestone: '15.1'
type: development
-group: group::package
+group: group::container registry
default_enabled: false
diff --git a/config/feature_flags/development/container_registry_migration_phase2_enabled.yml b/config/feature_flags/development/container_registry_migration_phase2_enabled.yml
index c48cbdb435e..a4c5d7bffa0 100644
--- a/config/feature_flags/development/container_registry_migration_phase2_enabled.yml
+++ b/config/feature_flags/development/container_registry_migration_phase2_enabled.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79061
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350543
milestone: '14.8'
type: development
-group: group::package
+group: group::container registry
default_enabled: false
diff --git a/config/feature_flags/development/container_registry_migration_phase2_enqueue_speed_fast.yml b/config/feature_flags/development/container_registry_migration_phase2_enqueue_speed_fast.yml
index 9a312161824..587096b0890 100644
--- a/config/feature_flags/development/container_registry_migration_phase2_enqueue_speed_fast.yml
+++ b/config/feature_flags/development/container_registry_migration_phase2_enqueue_speed_fast.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79061
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350543
milestone: '14.8'
type: development
-group: group::package
+group: group::container registry
default_enabled: false
diff --git a/config/feature_flags/development/container_registry_migration_phase2_enqueue_speed_slow.yml b/config/feature_flags/development/container_registry_migration_phase2_enqueue_speed_slow.yml
index f02259be928..531b5b0549f 100644
--- a/config/feature_flags/development/container_registry_migration_phase2_enqueue_speed_slow.yml
+++ b/config/feature_flags/development/container_registry_migration_phase2_enqueue_speed_slow.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79061
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350543
milestone: '14.8'
type: development
-group: group::package
+group: group::container registry
default_enabled: false
diff --git a/config/feature_flags/development/dast_api_scanner.yml b/config/feature_flags/development/dast_api_scanner.yml
deleted file mode 100644
index 5cc268ee9b9..00000000000
--- a/config/feature_flags/development/dast_api_scanner.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: dast_api_scanner
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73564
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345837
-milestone: '14.7'
-type: development
-group: group::dynamic analysis
-default_enabled: true
diff --git a/config/feature_flags/development/debian_group_packages.yml b/config/feature_flags/development/debian_group_packages.yml
index bc0c2eaf8eb..63a9220369d 100644
--- a/config/feature_flags/development/debian_group_packages.yml
+++ b/config/feature_flags/development/debian_group_packages.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66188
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/336536
milestone: '14.1'
type: development
-group: group::package
+group: group::package registry
default_enabled: false
diff --git a/config/feature_flags/development/debian_packages.yml b/config/feature_flags/development/debian_packages.yml
index 98bc5d898a5..077c75b9f57 100644
--- a/config/feature_flags/development/debian_packages.yml
+++ b/config/feature_flags/development/debian_packages.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42670
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/337288
milestone: '13.5'
type: development
-group: group::package
+group: group::package registry
default_enabled: false
diff --git a/config/feature_flags/development/disable_metric_dashboard_refresh_rate.yml b/config/feature_flags/development/disable_metric_dashboard_refresh_rate.yml
deleted file mode 100644
index 6adfba96e10..00000000000
--- a/config/feature_flags/development/disable_metric_dashboard_refresh_rate.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: disable_metric_dashboard_refresh_rate
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37195
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229841
-milestone: '13.2'
-type: development
-group: group::respond
-default_enabled: false
diff --git a/config/feature_flags/development/display_merge_conflicts_in_diff.yml b/config/feature_flags/development/display_merge_conflicts_in_diff.yml
index 50c22b52d11..71146d9236b 100644
--- a/config/feature_flags/development/display_merge_conflicts_in_diff.yml
+++ b/config/feature_flags/development/display_merge_conflicts_in_diff.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/276918
milestone: '13.5'
type: development
group: group::code review
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/enable_environments_search_within_folder.yml b/config/feature_flags/development/enable_environments_search_within_folder.yml
new file mode 100644
index 00000000000..a03b31ffbfc
--- /dev/null
+++ b/config/feature_flags/development/enable_environments_search_within_folder.yml
@@ -0,0 +1,8 @@
+---
+name: enable_environments_search_within_folder
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102227/diffs
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382108
+milestone: '15.7'
+type: development
+group: group::release
+default_enabled: true
diff --git a/config/feature_flags/development/enable_minor_delay_during_project_authorizations_refresh.yml b/config/feature_flags/development/enable_minor_delay_during_project_authorizations_refresh.yml
index cacc564df73..846052dc76f 100644
--- a/config/feature_flags/development/enable_minor_delay_during_project_authorizations_refresh.yml
+++ b/config/feature_flags/development/enable_minor_delay_during_project_authorizations_refresh.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373672
milestone: '15.4'
type: development
group: group::workspace
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/enforce_scan_result_policies_for_preexisting_vulnerabilities.yml b/config/feature_flags/development/enforce_scan_result_policies_for_preexisting_vulnerabilities.yml
new file mode 100644
index 00000000000..e72dc713e02
--- /dev/null
+++ b/config/feature_flags/development/enforce_scan_result_policies_for_preexisting_vulnerabilities.yml
@@ -0,0 +1,8 @@
+---
+name: enforce_scan_result_policies_for_preexisting_vulnerabilities
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105248
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/384260
+milestone: '15.7'
+type: development
+group: group::security policies
+default_enabled: false
diff --git a/config/feature_flags/development/enhanced_webhook_support_regex.yml b/config/feature_flags/development/enhanced_webhook_support_regex.yml
deleted file mode 100644
index 2c0d2c82dbf..00000000000
--- a/config/feature_flags/development/enhanced_webhook_support_regex.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: enhanced_webhook_support_regex
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97235
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/375728
-milestone: '15.6'
-type: development
-group: group::integrations
-default_enabled: false
diff --git a/config/feature_flags/development/environment_details_vue.yml b/config/feature_flags/development/environment_details_vue.yml
new file mode 100644
index 00000000000..5a647f65a7a
--- /dev/null
+++ b/config/feature_flags/development/environment_details_vue.yml
@@ -0,0 +1,8 @@
+---
+name: environment_details_vue
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105527"
+rollout_issue_url: "https://gitlab.com/gitlab-org/gitlab/-/issues/384914"
+milestone: '15.7'
+type: development
+group: group::release
+default_enabled: false
diff --git a/config/feature_flags/development/fork_divergence_counts.yml b/config/feature_flags/development/fork_divergence_counts.yml
new file mode 100644
index 00000000000..929e9fae790
--- /dev/null
+++ b/config/feature_flags/development/fork_divergence_counts.yml
@@ -0,0 +1,8 @@
+---
+name: fork_divergence_counts
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103814
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382878
+milestone: '15.7'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/config/feature_flags/development/forti_authenticator.yml b/config/feature_flags/development/forti_authenticator.yml
index f3360d136ec..63e780ccc64 100644
--- a/config/feature_flags/development/forti_authenticator.yml
+++ b/config/feature_flags/development/forti_authenticator.yml
@@ -1,8 +1,8 @@
---
name: forti_authenticator
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45055
-rollout_issue_url:
+rollout_issue_url:
milestone: '13.5'
type: development
-group: group::access
+group: group::authentication and authorization
default_enabled: false
diff --git a/config/feature_flags/development/forti_token_cloud.yml b/config/feature_flags/development/forti_token_cloud.yml
index 10f143ca912..5bf350c9b33 100644
--- a/config/feature_flags/development/forti_token_cloud.yml
+++ b/config/feature_flags/development/forti_token_cloud.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49089
rollout_issue_url:
milestone: '13.7'
type: development
-group: group::access
+group: group::authentication and authorization
default_enabled: false
diff --git a/config/feature_flags/development/geo_container_repository_replication.yml b/config/feature_flags/development/geo_container_repository_replication.yml
deleted file mode 100644
index 94682cc63cc..00000000000
--- a/config/feature_flags/development/geo_container_repository_replication.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: geo_container_repository_replication
-introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93690"
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366662
-milestone: '15.5'
-type: development
-group: group::geo
-default_enabled: true
diff --git a/config/feature_flags/development/github_client_fetch_repos_via_graphql.yml b/config/feature_flags/development/github_client_fetch_repos_via_graphql.yml
new file mode 100644
index 00000000000..7ff87410458
--- /dev/null
+++ b/config/feature_flags/development/github_client_fetch_repos_via_graphql.yml
@@ -0,0 +1,8 @@
+---
+name: github_client_fetch_repos_via_graphql
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105824
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385649
+milestone: '15.7'
+type: development
+group: group::import
+default_enabled: false
diff --git a/config/feature_flags/development/gitlab_metrics_error_rate_sli.yml b/config/feature_flags/development/gitlab_metrics_error_rate_sli.yml
new file mode 100644
index 00000000000..30b872343ce
--- /dev/null
+++ b/config/feature_flags/development/gitlab_metrics_error_rate_sli.yml
@@ -0,0 +1,8 @@
+---
+name: gitlab_metrics_error_rate_sli
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103976
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383071
+milestone: '15.7'
+type: development
+group: group::scalability
+default_enabled: false
diff --git a/config/feature_flags/development/gitlab_pat_auto_revocation.yml b/config/feature_flags/development/gitlab_pat_auto_revocation.yml
new file mode 100644
index 00000000000..3bbbadac23f
--- /dev/null
+++ b/config/feature_flags/development/gitlab_pat_auto_revocation.yml
@@ -0,0 +1,8 @@
+---
+name: gitlab_pat_auto_revocation
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103713
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382610
+milestone: '15.6'
+type: development
+group: group::static analysis
+default_enabled: false
diff --git a/config/feature_flags/development/global_search_error_rate_sli.yml b/config/feature_flags/development/global_search_error_rate_sli.yml
deleted file mode 100644
index d1637ad692c..00000000000
--- a/config/feature_flags/development/global_search_error_rate_sli.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: global_search_error_rate_sli
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96667
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373800
-milestone: '15.4'
-type: development
-group: group::application performance
-default_enabled: false
diff --git a/config/feature_flags/development/go_proxy.yml b/config/feature_flags/development/go_proxy.yml
index 4ec5dc786fd..ead133232e4 100644
--- a/config/feature_flags/development/go_proxy.yml
+++ b/config/feature_flags/development/go_proxy.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27746
rollout_issue_url:
milestone: '13.1'
type: development
-group: group::package
+group: group::package registry
default_enabled: false
diff --git a/config/feature_flags/development/go_proxy_disable_gomod_validation.yml b/config/feature_flags/development/go_proxy_disable_gomod_validation.yml
index f361d3392ec..5cf5ff528af 100644
--- a/config/feature_flags/development/go_proxy_disable_gomod_validation.yml
+++ b/config/feature_flags/development/go_proxy_disable_gomod_validation.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34338
rollout_issue_url:
milestone: '13.1'
type: development
-group: group::package
+group: group::package registry
default_enabled: false
diff --git a/config/feature_flags/development/graphql_job_app.yml b/config/feature_flags/development/graphql_job_app.yml
deleted file mode 100644
index a0f0cb71e17..00000000000
--- a/config/feature_flags/development/graphql_job_app.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: graphql_job_app
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96703
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372276
-milestone: '15.4'
-type: development
-group: group::pipeline authoring
-default_enabled: false
diff --git a/config/feature_flags/development/graphql_keyset_pagination_without_next_page_query.yml b/config/feature_flags/development/graphql_keyset_pagination_without_next_page_query.yml
deleted file mode 100644
index 7b4c884a82f..00000000000
--- a/config/feature_flags/development/graphql_keyset_pagination_without_next_page_query.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: graphql_keyset_pagination_without_next_page_query
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97509
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373792
-milestone: '15.4'
-type: development
-group: group::optimize
-default_enabled: true
diff --git a/config/feature_flags/development/group_protected_branches.yml b/config/feature_flags/development/group_protected_branches.yml
new file mode 100644
index 00000000000..4d580734dc4
--- /dev/null
+++ b/config/feature_flags/development/group_protected_branches.yml
@@ -0,0 +1,8 @@
+---
+name: group_protected_branches
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372816
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383178
+milestone: '15.7'
+type: development
+group: group::compliance
+default_enabled: false
diff --git a/config/feature_flags/development/harbor_registry_integration.yml b/config/feature_flags/development/harbor_registry_integration.yml
index 84d9709ca30..f1786f53c19 100644
--- a/config/feature_flags/development/harbor_registry_integration.yml
+++ b/config/feature_flags/development/harbor_registry_integration.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81593
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/353595
milestone: '14.9'
type: development
-group: group::package
+group: group::container registry
default_enabled: false
diff --git a/config/feature_flags/development/hash_based_cache_for_protected_branches.yml b/config/feature_flags/development/hash_based_cache_for_protected_branches.yml
index 4e071707182..58372ff3516 100644
--- a/config/feature_flags/development/hash_based_cache_for_protected_branches.yml
+++ b/config/feature_flags/development/hash_based_cache_for_protected_branches.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/368279
milestone: '15.3'
type: development
group: group::source code
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/hide_public_email_on_profile.yml b/config/feature_flags/development/hide_public_email_on_profile.yml
index 87ed700c359..acf8e4e9ca7 100644
--- a/config/feature_flags/development/hide_public_email_on_profile.yml
+++ b/config/feature_flags/development/hide_public_email_on_profile.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79717
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351731
milestone: '14.8'
type: development
-group: group::optimize
+group: group::workspace
default_enabled: false
diff --git a/config/feature_flags/development/indifferent_wal_location_keys.yml b/config/feature_flags/development/indifferent_wal_location_keys.yml
deleted file mode 100644
index 2d89ad82085..00000000000
--- a/config/feature_flags/development/indifferent_wal_location_keys.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: indifferent_wal_location_keys
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101096
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/377989
-milestone: '15.5'
-type: development
-group: group::source code
-default_enabled: false
diff --git a/config/feature_flags/development/integrated_error_tracking.yml b/config/feature_flags/development/integrated_error_tracking.yml
index fb302daed57..33b7b20ab29 100644
--- a/config/feature_flags/development/integrated_error_tracking.yml
+++ b/config/feature_flags/development/integrated_error_tracking.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81767
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/353956
milestone: '14.9'
type: development
-group: group::respond
+group: group::observability
default_enabled: false
diff --git a/config/feature_flags/development/jira_connect_oauth_self_managed_setting.yml b/config/feature_flags/development/jira_connect_oauth_self_managed_setting.yml
deleted file mode 100644
index 05232d0f80a..00000000000
--- a/config/feature_flags/development/jira_connect_oauth_self_managed_setting.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: jira_connect_oauth_self_managed_setting
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100725
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/377679
-milestone: '15.6'
-type: development
-group: group::integrations
-default_enabled: false
diff --git a/config/feature_flags/development/lazy_load_commits.yml b/config/feature_flags/development/lazy_load_commits.yml
deleted file mode 100644
index 6140b88c3c2..00000000000
--- a/config/feature_flags/development/lazy_load_commits.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: lazy_load_commits
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71633
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342497
-milestone: '14.4'
-type: development
-group: group::source code
-default_enabled: true
diff --git a/config/feature_flags/development/limit_assigned_issues_count.yml b/config/feature_flags/development/limit_assigned_issues_count.yml
new file mode 100644
index 00000000000..7fb58220f26
--- /dev/null
+++ b/config/feature_flags/development/limit_assigned_issues_count.yml
@@ -0,0 +1,8 @@
+---
+name: limit_assigned_issues_count
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105759
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/384363
+milestone: '15.7'
+type: development
+group: group::project management
+default_enabled: false
diff --git a/config/feature_flags/development/linear_group_descendants_finder_upto.yml b/config/feature_flags/development/linear_group_descendants_finder_upto.yml
index ef045ebfa7a..db3a37191ff 100644
--- a/config/feature_flags/development/linear_group_descendants_finder_upto.yml
+++ b/config/feature_flags/development/linear_group_descendants_finder_upto.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78991
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350972
milestone: '14.8'
type: development
-group: group::authentication and authorization
+group: group::workspace
default_enabled: false
diff --git a/config/feature_flags/development/linear_project_ancestors.yml b/config/feature_flags/development/linear_project_ancestors.yml
index 28c8fbcbf59..00b04b20b30 100644
--- a/config/feature_flags/development/linear_project_ancestors.yml
+++ b/config/feature_flags/development/linear_project_ancestors.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68072
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/338403
milestone: '14.2'
type: development
-group: group::access
+group: group::authentication and authorization
default_enabled: false
diff --git a/config/feature_flags/development/linear_user_manageable_groups.yml b/config/feature_flags/development/linear_user_manageable_groups.yml
index e5822fc3d7d..8d59d689f6e 100644
--- a/config/feature_flags/development/linear_user_manageable_groups.yml
+++ b/config/feature_flags/development/linear_user_manageable_groups.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68845
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339434
milestone: '14.3'
type: development
-group: group::access
+group: group::authentication and authorization
default_enabled: false
diff --git a/config/feature_flags/development/markup_rendering_timeout.yml b/config/feature_flags/development/markup_rendering_timeout.yml
deleted file mode 100644
index 6c579ebe28a..00000000000
--- a/config/feature_flags/development/markup_rendering_timeout.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: markup_rendering_timeout
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89509
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365358
-milestone: '15.1'
-type: development
-group: group::source code
-default_enabled: false
diff --git a/config/feature_flags/development/maven_central_request_forwarding.yml b/config/feature_flags/development/maven_central_request_forwarding.yml
index 756a931b3a1..46e4cbe5aca 100644
--- a/config/feature_flags/development/maven_central_request_forwarding.yml
+++ b/config/feature_flags/development/maven_central_request_forwarding.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85299
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/359553
milestone: '15.4'
type: development
-group: group::package
+group: group::package registry
default_enabled: false
diff --git a/config/feature_flags/development/metrics_dashboard_exhaustive_validations.yml b/config/feature_flags/development/metrics_dashboard_exhaustive_validations.yml
deleted file mode 100644
index 5c3c76dd252..00000000000
--- a/config/feature_flags/development/metrics_dashboard_exhaustive_validations.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: metrics_dashboard_exhaustive_validations
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40103
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/241697
-milestone: '13.4'
-type: development
-group: group::respond
-default_enabled: false
diff --git a/config/feature_flags/development/mirror_only_branches_match_regex.yml b/config/feature_flags/development/mirror_only_branches_match_regex.yml
new file mode 100644
index 00000000000..6494569b364
--- /dev/null
+++ b/config/feature_flags/development/mirror_only_branches_match_regex.yml
@@ -0,0 +1,8 @@
+---
+name: mirror_only_branches_match_regex
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/99201
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381667
+milestone: '15.6'
+type: development
+group: "group::source code"
+default_enabled: false
diff --git a/config/feature_flags/development/mr_compare_dropdowns.yml b/config/feature_flags/development/mr_compare_dropdowns.yml
new file mode 100644
index 00000000000..bffab02389b
--- /dev/null
+++ b/config/feature_flags/development/mr_compare_dropdowns.yml
@@ -0,0 +1,8 @@
+---
+name: mr_compare_dropdowns
+introduced_by_url:
+rollout_issue_url:
+milestone: '15.7'
+type: development
+group: group::code review
+default_enabled: false
diff --git a/config/feature_flags/development/multiple_environment_approval_rules_fe.yml b/config/feature_flags/development/multiple_environment_approval_rules_fe.yml
new file mode 100644
index 00000000000..c282313f409
--- /dev/null
+++ b/config/feature_flags/development/multiple_environment_approval_rules_fe.yml
@@ -0,0 +1,8 @@
+---
+name: multiple_environment_approval_rules_fe
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105719
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/384334
+milestone: '15.7'
+type: development
+group: group::release
+default_enabled: false
diff --git a/config/feature_flags/development/new_fonts.yml b/config/feature_flags/development/new_fonts.yml
new file mode 100644
index 00000000000..20bf283e704
--- /dev/null
+++ b/config/feature_flags/development/new_fonts.yml
@@ -0,0 +1,8 @@
+---
+name: new_fonts
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102347
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/379147
+milestone: '15.7'
+type: development
+group: group::foundations
+default_enabled: false
diff --git a/config/feature_flags/development/on_demand_scans_runner_tags.yml b/config/feature_flags/development/on_demand_scans_runner_tags.yml
new file mode 100644
index 00000000000..e25c6f8e3d1
--- /dev/null
+++ b/config/feature_flags/development/on_demand_scans_runner_tags.yml
@@ -0,0 +1,8 @@
+---
+name: on_demand_scans_runner_tags
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103634
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381910
+milestone: '15.7'
+type: development
+group: group::dynamic analysis
+default_enabled: false
diff --git a/config/feature_flags/development/operational_vulnerabilities_filters.yml b/config/feature_flags/development/operational_vulnerabilities_filters.yml
deleted file mode 100644
index 93206935a94..00000000000
--- a/config/feature_flags/development/operational_vulnerabilities_filters.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: operational_vulnerabilities_filters
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90845
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365986
-milestone: '15.2'
-type: development
-group: group::container security
-default_enabled: true
diff --git a/config/feature_flags/development/optimized_housekeeping.yml b/config/feature_flags/development/optimized_housekeeping.yml
index 9a5b9244116..478d5ee22e7 100644
--- a/config/feature_flags/development/optimized_housekeeping.yml
+++ b/config/feature_flags/development/optimized_housekeeping.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/353607
milestone: '14.9'
type: development
group: group::source code
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/package_registry_access_level.yml b/config/feature_flags/development/package_registry_access_level.yml
index 093315f77bd..3434e7f4260 100644
--- a/config/feature_flags/development/package_registry_access_level.yml
+++ b/config/feature_flags/development/package_registry_access_level.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82808
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/363018
milestone: '15.0'
type: development
-group: group::package
-default_enabled: false
+group: group::package registry
+default_enabled: true
diff --git a/config/feature_flags/development/pipeline_name.yml b/config/feature_flags/development/pipeline_name.yml
index 40557a7d01e..9070e754fff 100644
--- a/config/feature_flags/development/pipeline_name.yml
+++ b/config/feature_flags/development/pipeline_name.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/376095
milestone: '15.5'
type: development
group: group::delivery
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/pipeline_name_search.yml b/config/feature_flags/development/pipeline_name_search.yml
new file mode 100644
index 00000000000..ccf4f4a6c9c
--- /dev/null
+++ b/config/feature_flags/development/pipeline_name_search.yml
@@ -0,0 +1,8 @@
+---
+name: pipeline_name_search
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/107086
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385864
+milestone: '15.7'
+type: development
+group: group::delivery
+default_enabled: false
diff --git a/config/feature_flags/development/pipeline_tabs_vue.yml b/config/feature_flags/development/pipeline_tabs_vue.yml
deleted file mode 100644
index 848166d2cc1..00000000000
--- a/config/feature_flags/development/pipeline_tabs_vue.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: pipeline_tabs_vue
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80401
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/353118
-milestone: '14.10'
-type: development
-group: group::pipeline authoring
-default_enabled: false
diff --git a/config/feature_flags/development/project_language_search.yml b/config/feature_flags/development/project_language_search.yml
new file mode 100644
index 00000000000..2d8c24650d1
--- /dev/null
+++ b/config/feature_flags/development/project_language_search.yml
@@ -0,0 +1,8 @@
+---
+name: project_language_search
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104167
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385465
+milestone: '15.7'
+type: development
+group: group::workspace
+default_enabled: false
diff --git a/config/feature_flags/development/project_list_filter_bar.yml b/config/feature_flags/development/project_list_filter_bar.yml
deleted file mode 100644
index 29d5d67af95..00000000000
--- a/config/feature_flags/development/project_list_filter_bar.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: project_list_filter_bar
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/11209
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/321404
-milestone: '11.11'
-type: development
-group: group::access
-default_enabled: false
diff --git a/config/feature_flags/development/projects_preloader_fix.yml b/config/feature_flags/development/projects_preloader_fix.yml
index 1ad578f11a4..58c56257028 100644
--- a/config/feature_flags/development/projects_preloader_fix.yml
+++ b/config/feature_flags/development/projects_preloader_fix.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/378858
milestone: '15.6'
type: development
group: group::workspace
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/prometheus_computed_alerts.yml b/config/feature_flags/development/prometheus_computed_alerts.yml
deleted file mode 100644
index 97912685fb5..00000000000
--- a/config/feature_flags/development/prometheus_computed_alerts.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: prometheus_computed_alerts
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/13443
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/255304
-milestone: '12.0'
-type: development
-group: group::respond
-default_enabled: false
diff --git a/config/feature_flags/development/rate_limit_gitlab_shell_by_ip.yml b/config/feature_flags/development/rate_limit_gitlab_shell_by_ip.yml
index 67a465ef6d6..667c0db7ee0 100644
--- a/config/feature_flags/development/rate_limit_gitlab_shell_by_ip.yml
+++ b/config/feature_flags/development/rate_limit_gitlab_shell_by_ip.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367998
milestone: '15.3'
type: development
group: group::source code
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/realtime_mr_status_change.yml b/config/feature_flags/development/realtime_mr_status_change.yml
new file mode 100644
index 00000000000..0cba2d3ca57
--- /dev/null
+++ b/config/feature_flags/development/realtime_mr_status_change.yml
@@ -0,0 +1,8 @@
+---
+name: realtime_mr_status_change
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103011
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385077
+milestone: '15.7'
+type: development
+group: group::code review
+default_enabled: false
diff --git a/config/feature_flags/development/registry_migration_guard_dynamic_pre_import_timeout.yml b/config/feature_flags/development/registry_migration_guard_dynamic_pre_import_timeout.yml
index 503eb95dd62..c4ef2e4be9d 100644
--- a/config/feature_flags/development/registry_migration_guard_dynamic_pre_import_timeout.yml
+++ b/config/feature_flags/development/registry_migration_guard_dynamic_pre_import_timeout.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88292
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350543
milestone: '15.1'
type: development
-group: group::package
+group: group::container registry
default_enabled: false
diff --git a/config/feature_flags/development/require_approval_on_scan_removal.yml b/config/feature_flags/development/require_approval_on_scan_removal.yml
deleted file mode 100644
index 2cbbe8e49f8..00000000000
--- a/config/feature_flags/development/require_approval_on_scan_removal.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: require_approval_on_scan_removal
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102631
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382079
-milestone: '15.6'
-type: development
-group: group::security policies
-default_enabled: false
diff --git a/config/feature_flags/development/root_statistics_worker_read_replica.yml b/config/feature_flags/development/root_statistics_worker_read_replica.yml
deleted file mode 100644
index 516bead1ee7..00000000000
--- a/config/feature_flags/development/root_statistics_worker_read_replica.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: root_statistics_worker_read_replica
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102516
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/379678
-milestone: '15.6'
-type: development
-group: group::utilization
-default_enabled: false
diff --git a/config/feature_flags/development/route_hll_to_snowplow_phase3.yml b/config/feature_flags/development/route_hll_to_snowplow_phase3.yml
new file mode 100644
index 00000000000..5a000a0569a
--- /dev/null
+++ b/config/feature_flags/development/route_hll_to_snowplow_phase3.yml
@@ -0,0 +1,8 @@
+---
+name: route_hll_to_snowplow_phase3
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104947
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383657
+milestone: '15.7'
+type: development
+group: 'group::product intelligence'
+default_enabled: false
diff --git a/config/feature_flags/development/route_hll_to_snowplow_phase4.yml b/config/feature_flags/development/route_hll_to_snowplow_phase4.yml
new file mode 100644
index 00000000000..a4109b15533
--- /dev/null
+++ b/config/feature_flags/development/route_hll_to_snowplow_phase4.yml
@@ -0,0 +1,8 @@
+---
+name: route_hll_to_snowplow_phase4
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103528"
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366767
+milestone: '15.7'
+type: development
+group: group::product intelligence
+default_enabled: false
diff --git a/config/feature_flags/development/rpm_packages.yml b/config/feature_flags/development/rpm_packages.yml
index a342f1203f7..2ed00b2cf11 100644
--- a/config/feature_flags/development/rpm_packages.yml
+++ b/config/feature_flags/development/rpm_packages.yml
@@ -4,5 +4,5 @@ introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96019'
rollout_issue_url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/371863'
milestone: '15.4'
type: development
-group: group::package
+group: group::package registry
default_enabled: false
diff --git a/config/feature_flags/development/rubygem_packages.yml b/config/feature_flags/development/rubygem_packages.yml
index 9f394f091bb..3a05251d9b6 100644
--- a/config/feature_flags/development/rubygem_packages.yml
+++ b/config/feature_flags/development/rubygem_packages.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52147
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/299383
milestone: '13.9'
type: development
-group: group::package
+group: group::package registry
default_enabled: false
diff --git a/config/feature_flags/development/run_pipeline_graphql.yml b/config/feature_flags/development/run_pipeline_graphql.yml
deleted file mode 100644
index 78d8afbbee5..00000000000
--- a/config/feature_flags/development/run_pipeline_graphql.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: run_pipeline_graphql
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96633
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372310
-milestone: '15.4'
-type: development
-group: group::pipeline authoring
-default_enabled: false
diff --git a/config/feature_flags/development/scan_execution_rule_mode.yml b/config/feature_flags/development/scan_execution_rule_mode.yml
deleted file mode 100644
index 6e3dd6acf92..00000000000
--- a/config/feature_flags/development/scan_execution_rule_mode.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: scan_execution_rule_mode
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90099
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/359883
-milestone: '15.2'
-type: development
-group: group::container security
-default_enabled: true
diff --git a/config/feature_flags/development/scan_execution_tags.yml b/config/feature_flags/development/scan_execution_tags.yml
new file mode 100644
index 00000000000..bd9412a4e9c
--- /dev/null
+++ b/config/feature_flags/development/scan_execution_tags.yml
@@ -0,0 +1,8 @@
+---
+name: scan_execution_tags
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104954
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385169
+milestone: '15.7'
+type: development
+group: group::security policies
+default_enabled: true
diff --git a/config/feature_flags/development/schema_linting.yml b/config/feature_flags/development/schema_linting.yml
index 6c1cbdb5248..0abca3e03dc 100644
--- a/config/feature_flags/development/schema_linting.yml
+++ b/config/feature_flags/development/schema_linting.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/255919
milestone: '13.2'
type: development
group: group::editor
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/search_page_vertical_nav.yml b/config/feature_flags/development/search_page_vertical_nav.yml
index 58088cee802..a0e9d760295 100644
--- a/config/feature_flags/development/search_page_vertical_nav.yml
+++ b/config/feature_flags/development/search_page_vertical_nav.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373613
milestone: '15.5'
type: development
group: group::global search
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/secure_files_metadata_parsers.yml b/config/feature_flags/development/secure_files_metadata_parsers.yml
deleted file mode 100644
index 2d6eed27f4b..00000000000
--- a/config/feature_flags/development/secure_files_metadata_parsers.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: secure_files_metadata_parsers
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/99046
-rollout_issue_url:
-milestone: '15.5'
-type: development
-group: group::incubation
-default_enabled: false
diff --git a/config/feature_flags/development/specialized_worker_for_group_lock_update_auth_recalculation.yml b/config/feature_flags/development/specialized_worker_for_group_lock_update_auth_recalculation.yml
index 820e6cafb41..aa8e243e89e 100644
--- a/config/feature_flags/development/specialized_worker_for_group_lock_update_auth_recalculation.yml
+++ b/config/feature_flags/development/specialized_worker_for_group_lock_update_auth_recalculation.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66525
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/336592
milestone: '14.2'
type: development
-group: group::access
+group: group::authentication and authorization
default_enabled: false
diff --git a/config/feature_flags/development/split_operations_visibility_permissions.yml b/config/feature_flags/development/split_operations_visibility_permissions.yml
deleted file mode 100644
index 56955733217..00000000000
--- a/config/feature_flags/development/split_operations_visibility_permissions.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: split_operations_visibility_permissions
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89089
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/364240
-milestone: '15.1'
-type: development
-group: group::respond
-default_enabled: true
diff --git a/config/feature_flags/development/ssh_commit_signatures.yml b/config/feature_flags/development/ssh_commit_signatures.yml
new file mode 100644
index 00000000000..a056ba1a2ba
--- /dev/null
+++ b/config/feature_flags/development/ssh_commit_signatures.yml
@@ -0,0 +1,8 @@
+---
+name: ssh_commit_signatures
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97248
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350301
+milestone: '15.7'
+type: development
+group: group::source code
+default_enabled: true
diff --git a/config/feature_flags/development/subgroups_approval_rules.yml b/config/feature_flags/development/subgroups_approval_rules.yml
deleted file mode 100644
index e7935f5e5d2..00000000000
--- a/config/feature_flags/development/subgroups_approval_rules.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: subgroups_approval_rules
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91598
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366741
-milestone: '15.2'
-type: development
-group: group::source code
-default_enabled: true
diff --git a/config/feature_flags/development/super_sidebar_nav.yml b/config/feature_flags/development/super_sidebar_nav.yml
new file mode 100644
index 00000000000..cf088956bc0
--- /dev/null
+++ b/config/feature_flags/development/super_sidebar_nav.yml
@@ -0,0 +1,8 @@
+---
+name: super_sidebar_nav
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101910
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381456
+milestone: '15.7'
+type: development
+group: group::foundations
+default_enabled: false
diff --git a/config/feature_flags/development/track_and_raise_delete_source_errors.yml b/config/feature_flags/development/track_and_raise_delete_source_errors.yml
new file mode 100644
index 00000000000..5f34ab47f19
--- /dev/null
+++ b/config/feature_flags/development/track_and_raise_delete_source_errors.yml
@@ -0,0 +1,8 @@
+---
+name: track_and_raise_delete_source_errors
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103842
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382440
+milestone: '15.6'
+type: development
+group: group::code review
+default_enabled: false
diff --git a/config/feature_flags/development/two_factor_for_cli.yml b/config/feature_flags/development/two_factor_for_cli.yml
index e442bb035cf..341f06d9ffa 100644
--- a/config/feature_flags/development/two_factor_for_cli.yml
+++ b/config/feature_flags/development/two_factor_for_cli.yml
@@ -1,8 +1,8 @@
---
name: two_factor_for_cli
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39703
-rollout_issue_url:
+rollout_issue_url:
milestone: '13.5'
type: development
-group: group::access
+group: group::authentication and authorization
default_enabled: false
diff --git a/config/feature_flags/development/usage_data_ci_i_testing_coverage_report_uploaded.yml b/config/feature_flags/development/usage_data_ci_i_testing_coverage_report_uploaded.yml
deleted file mode 100644
index f3a3772972e..00000000000
--- a/config/feature_flags/development/usage_data_ci_i_testing_coverage_report_uploaded.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: usage_data_ci_i_testing_coverage_report_uploaded
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102371
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339721
-milestone: '15.6'
-type: development
-group: group::pipeline insights
-default_enabled: false
diff --git a/config/feature_flags/development/usage_data_diff_searches.yml b/config/feature_flags/development/usage_data_diff_searches.yml
deleted file mode 100644
index 70c053b7a17..00000000000
--- a/config/feature_flags/development/usage_data_diff_searches.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-name: usage_data_diff_searches
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86603
-rollout_issue_url:
-milestone: '15.0'
-type: development
-group: group::code review
-default_enabled: true
diff --git a/config/feature_flags/development/use_ref_type_parameter.yml b/config/feature_flags/development/use_ref_type_parameter.yml
new file mode 100644
index 00000000000..5cdd3d6f198
--- /dev/null
+++ b/config/feature_flags/development/use_ref_type_parameter.yml
@@ -0,0 +1,9 @@
+---
+name: use_ref_type_parameter
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102936
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381501
+milestone: '15.6'
+type: development
+group: group::source code
+default_enabled: false
+
diff --git a/config/feature_flags/development/use_traversal_ids_for_descendants_scopes.yml b/config/feature_flags/development/use_traversal_ids_for_descendants_scopes.yml
index b2262c9707e..3eece0b906a 100644
--- a/config/feature_flags/development/use_traversal_ids_for_descendants_scopes.yml
+++ b/config/feature_flags/development/use_traversal_ids_for_descendants_scopes.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78542
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350637
milestone: '14.8'
type: development
-group: group::authentication and authorization
+group: group::workspace
default_enabled: true
diff --git a/config/feature_flags/development/user_time_settings.yml b/config/feature_flags/development/user_time_settings.yml
index 098b96e97f0..77ee79fe80a 100644
--- a/config/feature_flags/development/user_time_settings.yml
+++ b/config/feature_flags/development/user_time_settings.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/25
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/321407
milestone: '11.11'
type: development
-group: group::access
+group: group::workspace
default_enabled: false
diff --git a/config/feature_flags/development/verify_gitlab_shell_worker_method_names.yml b/config/feature_flags/development/verify_gitlab_shell_worker_method_names.yml
deleted file mode 100644
index d6b28c28600..00000000000
--- a/config/feature_flags/development/verify_gitlab_shell_worker_method_names.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: verify_gitlab_shell_worker_method_names
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103783
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371470
-milestone: '15.6'
-type: development
-group: group::scalability
-default_enabled: false
diff --git a/config/feature_flags/development/verify_mail_scheduler_notification_service_worker_method_names.yml b/config/feature_flags/development/verify_mail_scheduler_notification_service_worker_method_names.yml
deleted file mode 100644
index 0fc30f63047..00000000000
--- a/config/feature_flags/development/verify_mail_scheduler_notification_service_worker_method_names.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: verify_mail_scheduler_notification_service_worker_method_names
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103785
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371470
-milestone: '15.6'
-type: development
-group: group::scalability
-default_enabled: false
diff --git a/config/feature_flags/development/web_hooks_disable_failed.yml b/config/feature_flags/development/web_hooks_disable_failed.yml
deleted file mode 100644
index 3a7c85edafc..00000000000
--- a/config/feature_flags/development/web_hooks_disable_failed.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: web_hooks_disable_failed
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60837
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/329849
-milestone: '13.12'
-type: development
-group: group::integrations
-default_enabled: false
diff --git a/config/feature_flags/development/web_hooks_no_rate_limit.yml b/config/feature_flags/development/web_hooks_no_rate_limit.yml
deleted file mode 100644
index 387aa8bb007..00000000000
--- a/config/feature_flags/development/web_hooks_no_rate_limit.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: web_hooks_no_rate_limit
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90868
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366084
-milestone: '15.2'
-type: development
-group: group::integrations
-default_enabled: false
diff --git a/config/feature_flags/development/webauthn.yml b/config/feature_flags/development/webauthn.yml
index 135d4af2465..6bd4fc95020 100644
--- a/config/feature_flags/development/webauthn.yml
+++ b/config/feature_flags/development/webauthn.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26692
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/232671
milestone: '13.4'
type: development
-group: group::access
+group: group::authentication and authorization
default_enabled: true
diff --git a/config/feature_flags/development/webhook_form_mask_url.yml b/config/feature_flags/development/webhook_form_mask_url.yml
deleted file mode 100644
index 445fcb0b6b3..00000000000
--- a/config/feature_flags/development/webhook_form_mask_url.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: webhook_form_mask_url
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/99995
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/376106
-milestone: '15.5'
-type: development
-group: group::integrations
-default_enabled: false
diff --git a/config/feature_flags/development/webhooks_failed_callout.yml b/config/feature_flags/development/webhooks_failed_callout.yml
deleted file mode 100644
index 11de5a793f6..00000000000
--- a/config/feature_flags/development/webhooks_failed_callout.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: webhooks_failed_callout
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91092
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365535
-milestone: '15.2'
-type: development
-group: group::integrations
-default_enabled: false
diff --git a/config/feature_flags/experiment/generic_explore_groups.yml b/config/feature_flags/experiment/generic_explore_groups.yml
index 635af65f000..d928dcd4189 100644
--- a/config/feature_flags/experiment/generic_explore_groups.yml
+++ b/config/feature_flags/experiment/generic_explore_groups.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381564
milestone: '15.6'
type: experiment
group: group::source code
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/ops/advanced_user_search.yml b/config/feature_flags/ops/advanced_user_search.yml
new file mode 100644
index 00000000000..0f52e9f22f0
--- /dev/null
+++ b/config/feature_flags/ops/advanced_user_search.yml
@@ -0,0 +1,8 @@
+---
+name: advanced_user_search
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102724
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382542
+milestone: '15.7'
+type: ops
+group: group::global search
+default_enabled: false
diff --git a/config/feature_flags/ops/automatic_lock_writes_on_table.yml b/config/feature_flags/ops/automatic_lock_writes_on_table.yml
new file mode 100644
index 00000000000..ade0bcb0d19
--- /dev/null
+++ b/config/feature_flags/ops/automatic_lock_writes_on_table.yml
@@ -0,0 +1,8 @@
+---
+name: automatic_lock_writes_on_table
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/99287
+rollout_issue_url:
+milestone: '15.7'
+type: ops
+group: group::pods
+default_enabled: false
diff --git a/config/feature_flags/ops/block_password_auth_for_saml_users.yml b/config/feature_flags/ops/block_password_auth_for_saml_users.yml
index 492c00f2dd5..d84d8b5133b 100644
--- a/config/feature_flags/ops/block_password_auth_for_saml_users.yml
+++ b/config/feature_flags/ops/block_password_auth_for_saml_users.yml
@@ -4,5 +4,5 @@ introduced_by_url:
rollout_issue_url:
milestone: '13.11'
type: ops
-group: group::access
+group: group::authentication and authorization
default_enabled: false
diff --git a/config/feature_flags/ops/dynamic_image_resizing.yml b/config/feature_flags/ops/dynamic_image_resizing.yml
index f456fa8bf1e..2b83b043a22 100644
--- a/config/feature_flags/ops/dynamic_image_resizing.yml
+++ b/config/feature_flags/ops/dynamic_image_resizing.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45050
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/266986
milestone: '13.6'
type: ops
-group: group::memory
+group: group::workspace
default_enabled: true
diff --git a/config/feature_flags/ops/dynamic_nonce.yml b/config/feature_flags/ops/dynamic_nonce.yml
index 6a63eda8862..ad8c63f9fa3 100644
--- a/config/feature_flags/ops/dynamic_nonce.yml
+++ b/config/feature_flags/ops/dynamic_nonce.yml
@@ -4,5 +4,5 @@ introduced_by_url:
rollout_issue_url:
milestone: '14.0'
type: ops
-group: group::access
+group: group::authentication and authorization
default_enabled: false
diff --git a/config/feature_flags/ops/enforce_memory_watchdog.yml b/config/feature_flags/ops/enforce_memory_watchdog.yml
index 1d1f9a4eef0..fae127564c9 100644
--- a/config/feature_flags/ops/enforce_memory_watchdog.yml
+++ b/config/feature_flags/ops/enforce_memory_watchdog.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91910
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367534
milestone: '15.2'
type: ops
-group: group::memory
+group: group::application performance
default_enabled: false
diff --git a/config/feature_flags/ops/gitlab_memory_watchdog.yml b/config/feature_flags/ops/gitlab_memory_watchdog.yml
index 9b995ea607e..f4c9a489864 100644
--- a/config/feature_flags/ops/gitlab_memory_watchdog.yml
+++ b/config/feature_flags/ops/gitlab_memory_watchdog.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91910
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367534
milestone: '15.2'
type: ops
-group: group::memory
+group: group::application performance
default_enabled: false
diff --git a/config/feature_flags/ops/gitlab_service_measuring_projects_create_service.yml b/config/feature_flags/ops/gitlab_service_measuring_projects_create_service.yml
deleted file mode 100644
index 78e60987a7f..00000000000
--- a/config/feature_flags/ops/gitlab_service_measuring_projects_create_service.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: gitlab_service_measuring_projects_create_service
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30977
-rollout_issue_url:
-milestone: '13.0'
-type: ops
-group: group::memory
-default_enabled: false
diff --git a/config/feature_flags/ops/gitlab_service_measuring_projects_import_export_export_service.yml b/config/feature_flags/ops/gitlab_service_measuring_projects_import_export_export_service.yml
deleted file mode 100644
index 309492f8be9..00000000000
--- a/config/feature_flags/ops/gitlab_service_measuring_projects_import_export_export_service.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: gitlab_service_measuring_projects_import_export_export_service
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30977
-rollout_issue_url:
-milestone: '13.0'
-type: ops
-group: group::memory
-default_enabled: false
diff --git a/config/feature_flags/ops/gitlab_service_measuring_projects_import_service.yml b/config/feature_flags/ops/gitlab_service_measuring_projects_import_service.yml
deleted file mode 100644
index 03a8eca99d9..00000000000
--- a/config/feature_flags/ops/gitlab_service_measuring_projects_import_service.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: gitlab_service_measuring_projects_import_service
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30977
-rollout_issue_url:
-milestone: '13.0'
-type: ops
-group: group::memory
-default_enabled: false
diff --git a/config/feature_flags/ops/jira_raise_timeouts.yml b/config/feature_flags/ops/jira_raise_timeouts.yml
deleted file mode 100644
index ac572b172fc..00000000000
--- a/config/feature_flags/ops/jira_raise_timeouts.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: jira_raise_timeouts
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86439
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/375587
-milestone: '15.0'
-type: ops
-group: group::integrations
-default_enabled: false
diff --git a/config/feature_flags/ops/legacy_open_source_license_available.yml b/config/feature_flags/ops/legacy_open_source_license_available.yml
index 2408e31633a..36ee6c230bf 100644
--- a/config/feature_flags/ops/legacy_open_source_license_available.yml
+++ b/config/feature_flags/ops/legacy_open_source_license_available.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79855
rollout_issue_url:
milestone: '14.8'
type: ops
-group: 'group::authentication and authorization'
+group: 'group::workspace'
default_enabled: true
diff --git a/config/feature_flags/ops/purge_stale_security_findings.yml b/config/feature_flags/ops/purge_stale_security_findings.yml
deleted file mode 100644
index b540c8a1d60..00000000000
--- a/config/feature_flags/ops/purge_stale_security_findings.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: purge_stale_security_findings
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81423
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/356464
-milestone: '14.9'
-type: ops
-group: group::threat insights
-default_enabled: true
diff --git a/config/feature_flags/ops/recaptcha_on_top_level_group_creation.yml b/config/feature_flags/ops/recaptcha_on_top_level_group_creation.yml
index 3ee8538b4fd..e01dd2b566c 100644
--- a/config/feature_flags/ops/recaptcha_on_top_level_group_creation.yml
+++ b/config/feature_flags/ops/recaptcha_on_top_level_group_creation.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56707
rollout_issue_url:
milestone: '13.11'
type: ops
-group: group::access
+group: group::authentication and authorization
default_enabled: false
diff --git a/config/feature_flags/ops/report_heap_dumps.yml b/config/feature_flags/ops/report_heap_dumps.yml
new file mode 100644
index 00000000000..12b126a8f80
--- /dev/null
+++ b/config/feature_flags/ops/report_heap_dumps.yml
@@ -0,0 +1,8 @@
+---
+name: report_heap_dumps
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106406
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385175
+milestone: '15.7'
+type: ops
+group: group::application performance
+default_enabled: false
diff --git a/config/feature_flags/ops/report_jemalloc_stats.yml b/config/feature_flags/ops/report_jemalloc_stats.yml
index 9cf5fd9d14b..61fbfa26206 100644
--- a/config/feature_flags/ops/report_jemalloc_stats.yml
+++ b/config/feature_flags/ops/report_jemalloc_stats.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91283
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367845
milestone: '15.2'
type: ops
-group: group::memory
-default_enabled: true
+group: group::application performance
+default_enabled: false
diff --git a/config/feature_flags/ops/search_curation_dry_run.yml b/config/feature_flags/ops/search_curation_dry_run.yml
new file mode 100644
index 00000000000..37f5328ff13
--- /dev/null
+++ b/config/feature_flags/ops/search_curation_dry_run.yml
@@ -0,0 +1,8 @@
+---
+name: search_curation_dry_run
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104321
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383003
+milestone: '15.7'
+type: ops
+group: group::global search
+default_enabled: true
diff --git a/config/feature_flags/ops/search_index_curation_commits.yml b/config/feature_flags/ops/search_index_curation_commits.yml
new file mode 100644
index 00000000000..4a25284e130
--- /dev/null
+++ b/config/feature_flags/ops/search_index_curation_commits.yml
@@ -0,0 +1,8 @@
+---
+name: search_index_curation_commits
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104423"
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383093
+milestone: '15.7'
+type: ops
+group: group::global search
+default_enabled: false
diff --git a/config/feature_flags/ops/search_index_curation_issues.yml b/config/feature_flags/ops/search_index_curation_issues.yml
new file mode 100644
index 00000000000..0a695a21796
--- /dev/null
+++ b/config/feature_flags/ops/search_index_curation_issues.yml
@@ -0,0 +1,8 @@
+---
+name: search_index_curation_issues
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104423"
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383094
+milestone: '15.7'
+type: ops
+group: group::global search
+default_enabled: false
diff --git a/config/feature_flags/ops/search_index_curation_main_index.yml b/config/feature_flags/ops/search_index_curation_main_index.yml
new file mode 100644
index 00000000000..7d767de89cd
--- /dev/null
+++ b/config/feature_flags/ops/search_index_curation_main_index.yml
@@ -0,0 +1,8 @@
+---
+name: search_index_curation_main_index
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104423
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383106
+milestone: '15.7'
+type: ops
+group: group::global search
+default_enabled: false
diff --git a/config/feature_flags/ops/search_index_curation_merge_requests.yml b/config/feature_flags/ops/search_index_curation_merge_requests.yml
new file mode 100644
index 00000000000..79dcb8be79d
--- /dev/null
+++ b/config/feature_flags/ops/search_index_curation_merge_requests.yml
@@ -0,0 +1,8 @@
+---
+name: search_index_curation_merge_requests
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104423"
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383095
+milestone: '15.7'
+type: ops
+group: group::global search
+default_enabled: false
diff --git a/config/feature_flags/ops/search_index_curation_notes.yml b/config/feature_flags/ops/search_index_curation_notes.yml
new file mode 100644
index 00000000000..1bba280eb64
--- /dev/null
+++ b/config/feature_flags/ops/search_index_curation_notes.yml
@@ -0,0 +1,8 @@
+---
+name: search_index_curation_notes
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104423"
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383096
+milestone: '15.7'
+type: ops
+group: group::global search
+default_enabled: false
diff --git a/config/feature_flags/ops/search_index_curation_users.yml b/config/feature_flags/ops/search_index_curation_users.yml
new file mode 100644
index 00000000000..a5383df8ad7
--- /dev/null
+++ b/config/feature_flags/ops/search_index_curation_users.yml
@@ -0,0 +1,8 @@
+---
+name: search_index_curation_users
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104423"
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383097
+milestone: '15.7'
+type: ops
+group: group::global search
+default_enabled: false
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 732d46d284b..f67099fbdf0 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -482,6 +482,14 @@ production: &base
enabled: false
host: 'https://mattermost.example.com'
+ ## Jira connect
+ ## To switch to a Jira connect development environment
+ jira_connect:
+ # atlassian_js_url: 'http://localhost:9292/atlassian.js'
+ # enable_public_keys_storage: true
+ # enforce_jira_base_url_https: false
+ # additional_iframe_ancestors: ['localhost:*']
+
## Gravatar
## If using gravatar.com, there's nothing to change here. For Libravatar
## you'll need to provide the custom URLs. For more information,
@@ -847,7 +855,7 @@ production: &base
# Filter LDAP users
#
- # Format: RFC 4515 https://tools.ietf.org/search/rfc4515
+ # Format: RFC 4515 https://www.rfc-editor.org/rfc/rfc4515
# Ex. (employeeType=developer)
#
# Note: GitLab does not support omniauth-ldap's custom filter syntax.
@@ -1050,7 +1058,7 @@ production: &base
# disable_ssl_verification: false,
# login_url: '/cas/login',
# service_validate_url: '/cas/p3/serviceValidate',
- # logout_url: '/cas/logout'} }
+ # logout_url: '/cas/logout' } }
# - { name: 'authentiq',
# # for client credentials (client ID and secret), go to https://www.authentiq.com/developers
# app_id: 'YOUR_CLIENT_ID',
@@ -1559,7 +1567,7 @@ test:
disable_ssl_verification: false,
login_url: '/cas/login',
service_validate_url: '/cas/p3/serviceValidate',
- logout_url: '/cas/logout'} }
+ logout_url: '/cas/logout' } }
- { name: 'github',
app_id: 'YOUR_APP_ID',
app_secret: 'YOUR_APP_SECRET',
diff --git a/config/gitlab_loose_foreign_keys.yml b/config/gitlab_loose_foreign_keys.yml
index efb14cdea36..5212ffbfd6b 100644
--- a/config/gitlab_loose_foreign_keys.yml
+++ b/config/gitlab_loose_foreign_keys.yml
@@ -166,6 +166,10 @@ clusters_applications_runners:
- table: ci_runners
column: runner_id
on_delete: async_nullify
+dast_pre_scan_verifications:
+ - table: ci_pipelines
+ column: ci_pipeline_id
+ on_delete: async_delete
dast_profiles_pipelines:
- table: ci_pipelines
column: ci_pipeline_id
@@ -174,6 +178,10 @@ dast_scanner_profiles_builds:
- table: ci_builds
column: ci_build_id
on_delete: async_delete
+dast_scanner_profiles_tags:
+ - table: tags
+ column: tag_id
+ on_delete: async_delete
dast_site_profiles_builds:
- table: ci_builds
column: ci_build_id
diff --git a/config/initializers/1_active_record_data_types.rb b/config/initializers/1_active_record_data_types.rb
new file mode 100644
index 00000000000..ba4ca8b3b5c
--- /dev/null
+++ b/config/initializers/1_active_record_data_types.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+# ActiveRecord custom data type for storing datetimes with timezone information.
+# See https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/11229
+
+require 'active_record/connection_adapters/postgresql_adapter'
+
+ActiveRecord::Type.register(:ind_jsonb, Gitlab::Database::Type::IndifferentJsonb)
+ActiveRecord::Type.register(:sym_jsonb, Gitlab::Database::Type::SymbolizedJsonb)
+
+module ActiveRecord::ConnectionAdapters::PostgreSQL::OID # rubocop:disable Style/ClassAndModuleChildren
+ # Add the class `DateTimeWithTimeZone` so we can map `timestamptz` to it.
+ class DateTimeWithTimeZone < DateTime
+ def type
+ :datetime_with_timezone
+ end
+ end
+end
+
+module RegisterDateTimeWithTimeZone
+ # Run original `initialize_type_map` and then register `timestamptz` as a
+ # `DateTimeWithTimeZone`.
+ #
+ # Apparently it does not matter that the original `initialize_type_map`
+ # aliases `timestamptz` to `timestamp`.
+ #
+ # When schema dumping, `timestamptz` columns will be output as
+ # `t.datetime_with_timezone`.
+ def initialize_type_map(mapping = type_map)
+ super mapping
+
+ register_class_with_precision(
+ mapping,
+ 'timestamptz',
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID::DateTimeWithTimeZone
+ )
+ end
+end
+
+class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter # rubocop:disable Style/ClassAndModuleChildren
+ prepend RegisterDateTimeWithTimeZone
+
+ # Add column type `datetime_with_timezone` so we can do this in
+ # migrations:
+ #
+ # add_column(:users, :datetime_with_timezone)
+ #
+ NATIVE_DATABASE_TYPES[:datetime_with_timezone] = { name: 'timestamptz' }
+end
+
+def connection_active?
+ ActiveRecord::Base.connection.active? # rubocop:disable Database/MultipleDatabases
+rescue StandardError
+ false
+end
+
+# Ensure `datetime_with_timezone` columns are correctly written to schema.rb
+
+# rubocop:disable Database/MultipleDatabases
+ActiveRecord::Base.connection.send(:reload_type_map) if connection_active?
+
+ActiveRecord::Base.time_zone_aware_types += [:datetime_with_timezone]
+# rubocop:enable Database/MultipleDatabases
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index da4277c8146..aafca0e2f09 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -221,7 +221,7 @@ Settings.gitlab['issue_closing_pattern'] = '\b((?:[Cc]los(?:e[sd]?|ing)|\b[Ff]ix
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab['graphql_timeout'] ||= 30
-Settings.gitlab['max_attachment_size'] ||= 10
+Settings.gitlab['max_attachment_size'] ||= 100
Settings.gitlab['session_expire_delay'] ||= 10080
Settings.gitlab['unauthenticated_session_expire_delay'] ||= 2.hours.to_i
Settings.gitlab.default_projects_features['issues'] = true if Settings.gitlab.default_projects_features['issues'].nil?
@@ -440,6 +440,17 @@ Settings.mattermost['enabled'] = false if Settings.mattermost['enabled'].nil?
Settings.mattermost['host'] = nil unless Settings.mattermost.enabled
#
+# Jira Connect (GitLab.com for Jira Cloud App)
+#
+Settings['jira_connect'] ||= Settingslogic.new({})
+
+Settings.jira_connect['atlassian_js_url'] ||= 'https://connect-cdn.atl-paas.net/all.js'
+Settings.jira_connect['enable_public_keys_storage'] ||= false
+Settings.jira_connect['enable_public_keys_storage'] = true if Gitlab.com?
+Settings.jira_connect['enforce_jira_base_url_https'] = true if Settings.jira_connect['enforce_jira_base_url_https'].nil?
+Settings.jira_connect['additional_iframe_ancestors'] ||= []
+
+#
# Gravatar
#
Settings['gravatar'] ||= Settingslogic.new({})
@@ -515,6 +526,9 @@ Settings.cron_jobs['remove_unaccepted_member_invites_worker']['job_class'] = 'Re
Settings.cron_jobs['prune_old_events_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['prune_old_events_worker']['cron'] ||= '0 */6 * * *'
Settings.cron_jobs['prune_old_events_worker']['job_class'] = 'PruneOldEventsWorker'
+Settings.cron_jobs['gitlab_export_prune_project_export_jobs_worker'] ||= Settingslogic.new({})
+Settings.cron_jobs['gitlab_export_prune_project_export_jobs_worker']['cron'] ||= '30 3 * * */7'
+Settings.cron_jobs['gitlab_export_prune_project_export_jobs_worker']['job_class'] = 'Gitlab::Export::PruneProjectExportJobsWorker'
Settings.cron_jobs['trending_projects_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['trending_projects_worker']['cron'] = '0 1 * * *'
Settings.cron_jobs['trending_projects_worker']['job_class'] = 'TrendingProjectsWorker'
@@ -718,9 +732,6 @@ Gitlab.ee do
Settings.cron_jobs['geo_repository_verification_secondary_scheduler_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['geo_repository_verification_secondary_scheduler_worker']['cron'] ||= '*/1 * * * *'
Settings.cron_jobs['geo_repository_verification_secondary_scheduler_worker']['job_class'] ||= 'Geo::RepositoryVerification::Secondary::SchedulerWorker'
- Settings.cron_jobs['geo_container_repository_sync_worker'] ||= Settingslogic.new({})
- Settings.cron_jobs['geo_container_repository_sync_worker']['cron'] ||= '*/1 * * * *'
- Settings.cron_jobs['geo_container_repository_sync_worker']['job_class'] ||= 'Geo::ContainerRepositorySyncDispatchWorker'
Settings.cron_jobs['historical_data_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['historical_data_worker']['cron'] ||= '0 12 * * *'
Settings.cron_jobs['historical_data_worker']['job_class'] = 'HistoricalDataWorker'
@@ -809,6 +820,9 @@ Gitlab.ee do
Settings.cron_jobs['disable_legacy_open_source_license_for_inactive_projects'] ||= Settingslogic.new({})
Settings.cron_jobs['disable_legacy_open_source_license_for_inactive_projects']['cron'] ||= "30 5 * * 0"
Settings.cron_jobs['disable_legacy_open_source_license_for_inactive_projects']['job_class'] = 'Projects::DisableLegacyOpenSourceLicenseForInactiveProjectsWorker'
+ Settings.cron_jobs['notify_seats_exceeded_batch_worker'] ||= Settingslogic.new({})
+ Settings.cron_jobs['notify_seats_exceeded_batch_worker']['cron'] ||= '0 3 * * *'
+ Settings.cron_jobs['notify_seats_exceeded_batch_worker']['job_class'] ||= 'GitlabSubscriptions::NotifySeatsExceededBatchWorker'
end
end
@@ -1041,6 +1055,12 @@ Settings.prometheus['enabled'] ||= false
Settings.prometheus['server_address'] ||= nil
#
+# Bullet settings
+#
+Settings['bullet'] ||= Settingslogic.new({})
+Settings.bullet['enabled'] ||= Rails.env.development?
+
+#
# Shutdown settings
#
Settings['shutdown'] ||= Settingslogic.new({})
diff --git a/config/initializers/active_record_data_types.rb b/config/initializers/active_record_data_types.rb
deleted file mode 100644
index 7f4bd32c221..00000000000
--- a/config/initializers/active_record_data_types.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-# ActiveRecord custom data type for storing datetimes with timezone information.
-# See https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/11229
-
-require 'active_record/connection_adapters/postgresql_adapter'
-
-module ActiveRecord::ConnectionAdapters::PostgreSQL::OID
- # Add the class `DateTimeWithTimeZone` so we can map `timestamptz` to it.
- class DateTimeWithTimeZone < DateTime
- def type
- :datetime_with_timezone
- end
- end
-end
-
-module RegisterDateTimeWithTimeZone
- # Run original `initialize_type_map` and then register `timestamptz` as a
- # `DateTimeWithTimeZone`.
- #
- # Apparently it does not matter that the original `initialize_type_map`
- # aliases `timestamptz` to `timestamp`.
- #
- # When schema dumping, `timestamptz` columns will be output as
- # `t.datetime_with_timezone`.
- def initialize_type_map(mapping = type_map)
- super mapping
-
- register_class_with_precision(
- mapping,
- 'timestamptz',
- ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID::DateTimeWithTimeZone
- )
- end
-end
-
-class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
- prepend RegisterDateTimeWithTimeZone
-
- # Add column type `datetime_with_timezone` so we can do this in
- # migrations:
- #
- # add_column(:users, :datetime_with_timezone)
- #
- NATIVE_DATABASE_TYPES[:datetime_with_timezone] = { name: 'timestamptz' }
-end
-
-def connection_active?
- ActiveRecord::Base.connection.active? # rubocop:disable Database/MultipleDatabases
-rescue StandardError
- false
-end
-
-# Ensure `datetime_with_timezone` columns are correctly written to schema.rb
-ActiveRecord::Base.connection.send(:reload_type_map) if connection_active?
-
-ActiveRecord::Base.time_zone_aware_types += [:datetime_with_timezone]
diff --git a/config/initializers/countries.rb b/config/initializers/countries.rb
index 52537b5d885..171c126143c 100644
--- a/config/initializers/countries.rb
+++ b/config/initializers/countries.rb
@@ -8,7 +8,7 @@ end
# This overrides the display name for Ukraine to 'Ukraine (except the Crimea, Donetsk, and Luhansk regions)'
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/374946
# To be removed after https://gitlab.com/gitlab-org/gitlab/issues/14784 is implemented
-# Data fetched is based on https://github.com/hexorx/countries/blob/master/lib/countries/data/countries/UA.yaml
+# Data fetched is based on https://github.com/countries/countries/blob/master/lib/countries/data/countries/UA.yaml
ISO3166::Data.register(
continent: "Europe",
address_format: "|-
diff --git a/config/initializers/diagnostic_reports.rb b/config/initializers/diagnostic_reports.rb
index 47266f99f2d..14bac060c25 100644
--- a/config/initializers/diagnostic_reports.rb
+++ b/config/initializers/diagnostic_reports.rb
@@ -7,3 +7,9 @@ return unless Gitlab::Runtime.puma?
Gitlab::Cluster::LifecycleEvents.on_worker_start do
Gitlab::Memory::ReportsDaemon.instance.start
end
+
+Gitlab::Cluster::LifecycleEvents.on_worker_stop do
+ Gitlab::Memory::Reporter.new.run_report(
+ Gitlab::Memory::Reports::HeapDump.new
+ )
+end
diff --git a/config/initializers/rest-client-hostname_override.rb b/config/initializers/rest-client-hostname_override.rb
index bb8995a4659..2fb3b9fc27d 100644
--- a/config/initializers/rest-client-hostname_override.rb
+++ b/config/initializers/rest-client-hostname_override.rb
@@ -9,7 +9,8 @@ module RestClient
begin
ip, hostname_override = Gitlab::UrlBlocker.validate!(uri, allow_local_network: allow_settings_local_requests?,
allow_localhost: allow_settings_local_requests?,
- dns_rebind_protection: dns_rebind_protection?)
+ dns_rebind_protection: dns_rebind_protection?,
+ schemes: %w[http https])
self.hostname_override = hostname_override
rescue Gitlab::UrlBlocker::BlockedUrlError => e
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index 363438849ed..58441b83c7d 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -32,7 +32,8 @@ queues_config_hash = Gitlab::Redis::Queues.params
queues_config_hash[:namespace] = Gitlab::Redis::Queues::SIDEKIQ_NAMESPACE
enable_json_logs = Gitlab.config.sidekiq.log_format == 'json'
-enable_sidekiq_memory_killer = ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'].to_i.nonzero?
+enable_sidekiq_memory_killer = ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'].to_i.nonzero? &&
+ !Gitlab::Utils.to_boolean(ENV['GITLAB_MEMORY_WATCHDOG_ENABLED'])
Sidekiq.configure_server do |config|
config[:strict] = false
diff --git a/config/initializers/types.rb b/config/initializers/types.rb
deleted file mode 100644
index 4a20e257469..00000000000
--- a/config/initializers/types.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-# frozen_string_literal: true
-
-ActiveRecord::Type.register(:sym_jsonb, Gitlab::Database::Type::SymbolizedJsonb)
diff --git a/config/initializers/zz_metrics.rb b/config/initializers/zz_metrics.rb
index ff3ae9a2467..8ee6a055535 100644
--- a/config/initializers/zz_metrics.rb
+++ b/config/initializers/zz_metrics.rb
@@ -24,6 +24,7 @@ if Gitlab::Metrics.enabled? && !Rails.env.test? && !(Rails.env.development? && d
require_dependency 'gitlab/metrics/subscribers/action_view'
require_dependency 'gitlab/metrics/subscribers/active_record'
require_dependency 'gitlab/metrics/subscribers/rails_cache'
+ require_dependency 'gitlab/metrics/subscribers/ldap'
Gitlab::Application.configure do |config|
# We want to track certain metrics during the Load Balancing host resolving process.
diff --git a/config/initializers_before_autoloader/000_inflections.rb b/config/initializers_before_autoloader/000_inflections.rb
index 795b0f20128..2bf98a38544 100644
--- a/config/initializers_before_autoloader/000_inflections.rb
+++ b/config/initializers_before_autoloader/000_inflections.rb
@@ -19,6 +19,7 @@ ActiveSupport::Inflector.inflections do |inflect|
container_repository_registry
dependency_proxy_blob_registry
design_registry
+ dependency_proxy_manifest_registry
event_log
file_registry
group_view
diff --git a/config/metrics/counts_28d/20210216184454_code_review_total_unique_counts_monthly.yml b/config/metrics/counts_28d/20210216184454_code_review_total_unique_counts_monthly.yml
index e7eb6a01f82..90c053612cf 100644
--- a/config/metrics/counts_28d/20210216184454_code_review_total_unique_counts_monthly.yml
+++ b/config/metrics/counts_28d/20210216184454_code_review_total_unique_counts_monthly.yml
@@ -132,6 +132,12 @@ options:
- i_code_review_user_unresolve_thread
- i_code_review_user_vs_code_api_request
- i_code_review_widget_nothing_merge_click_new_file
+ - i_code_review_merge_request_widget_security_reports_expand
+ - i_code_review_merge_request_widget_security_reports_expand_failed
+ - i_code_review_merge_request_widget_security_reports_expand_success
+ - i_code_review_merge_request_widget_security_reports_expand_warning
+ - i_code_review_merge_request_widget_security_reports_full_report_clicked
+ - i_code_review_merge_request_widget_security_reports_view
distribution:
- ce
- ee
diff --git a/config/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml b/config/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml
index 554b5c57bb6..4b972a3315d 100755
--- a/config/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml
+++ b/config/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml
@@ -94,6 +94,8 @@ options:
- p_ci_templates_jobs_secret_detection_latest
- p_ci_templates_jobs_code_intelligence
- p_ci_templates_jobs_code_quality
+ - p_ci_templates_jobs_container_scanning
+ - p_ci_templates_jobs_container_scanning_latest
- p_ci_templates_jobs_dependency_scanning
- p_ci_templates_jobs_dependency_scanning_latest
- p_ci_templates_jobs_license_scanning
@@ -142,6 +144,7 @@ options:
- p_ci_templates_implicit_jobs_secret_detection
- p_ci_templates_implicit_jobs_code_intelligence
- p_ci_templates_implicit_jobs_code_quality
+ - p_ci_templates_implicit_jobs_container_scanning
- p_ci_templates_implicit_jobs_dependency_scanning
- p_ci_templates_implicit_jobs_license_scanning
- p_ci_templates_implicit_jobs_deploy_ecs
diff --git a/config/metrics/counts_28d/20210216184957_ecosystem_total_unique_counts_monthly.yml b/config/metrics/counts_28d/20210216184957_ecosystem_total_unique_counts_monthly.yml
index 7b18f83973a..93be16fa2f4 100644
--- a/config/metrics/counts_28d/20210216184957_ecosystem_total_unique_counts_monthly.yml
+++ b/config/metrics/counts_28d/20210216184957_ecosystem_total_unique_counts_monthly.yml
@@ -39,5 +39,4 @@ tier:
performance_indicator_type:
- gmau
- paid_gmau
-- smau
milestone: "<13.9"
diff --git a/config/metrics/counts_28d/20210427102618_code_review_category_monthly_active_users.yml b/config/metrics/counts_28d/20210427102618_code_review_category_monthly_active_users.yml
index 6f7b82ec793..8a0005a5c5c 100644
--- a/config/metrics/counts_28d/20210427102618_code_review_category_monthly_active_users.yml
+++ b/config/metrics/counts_28d/20210427102618_code_review_category_monthly_active_users.yml
@@ -132,3 +132,9 @@ options:
- 'i_code_review_merge_request_widget_license_compliance_expand_success'
- 'i_code_review_merge_request_widget_license_compliance_expand_warning'
- 'i_code_review_merge_request_widget_license_compliance_expand_failed'
+ - 'i_code_review_merge_request_widget_security_reports_view'
+ - 'i_code_review_merge_request_widget_security_reports_full_report_clicked'
+ - 'i_code_review_merge_request_widget_security_reports_expand'
+ - 'i_code_review_merge_request_widget_security_reports_expand_success'
+ - 'i_code_review_merge_request_widget_security_reports_expand_warning'
+ - 'i_code_review_merge_request_widget_security_reports_expand_failed'
diff --git a/config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml b/config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml
index ce14aa19f9c..7ef2725f01c 100644
--- a/config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml
+++ b/config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml
@@ -136,3 +136,9 @@ options:
- 'i_code_review_merge_request_widget_license_compliance_expand_success'
- 'i_code_review_merge_request_widget_license_compliance_expand_warning'
- 'i_code_review_merge_request_widget_license_compliance_expand_failed'
+ - 'i_code_review_merge_request_widget_security_reports_view'
+ - 'i_code_review_merge_request_widget_security_reports_full_report_clicked'
+ - 'i_code_review_merge_request_widget_security_reports_expand'
+ - 'i_code_review_merge_request_widget_security_reports_expand_success'
+ - 'i_code_review_merge_request_widget_security_reports_expand_warning'
+ - 'i_code_review_merge_request_widget_security_reports_expand_failed'
diff --git a/config/metrics/counts_28d/20221108092725_p_ci_templates_implicit_jobs_container_scanning_monthly.yml b/config/metrics/counts_28d/20221108092725_p_ci_templates_implicit_jobs_container_scanning_monthly.yml
new file mode 100644
index 00000000000..62ce68dd4e6
--- /dev/null
+++ b/config/metrics/counts_28d/20221108092725_p_ci_templates_implicit_jobs_container_scanning_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_container_scanning_monthly
+description: Monthly counts for implicit use of Container Scanning CI template (Jobs folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: container_scanning
+value_type: number
+status: active
+milestone: '15.6'
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103262"
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_implicit_jobs_container_scanning
diff --git a/config/metrics/counts_28d/20221108092725_p_ci_templates_jobs_container_scanning_latest_monthly.yml b/config/metrics/counts_28d/20221108092725_p_ci_templates_jobs_container_scanning_latest_monthly.yml
new file mode 100644
index 00000000000..df169b9eb48
--- /dev/null
+++ b/config/metrics/counts_28d/20221108092725_p_ci_templates_jobs_container_scanning_latest_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_container_scanning_latest_monthly
+description: Monthly counts for Container Scanning CI Latest template (Jobs folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: container_scanning
+value_type: number
+status: active
+milestone: '15.6'
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103262"
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_jobs_container_scanning_latest
diff --git a/config/metrics/counts_28d/20221108092725_p_ci_templates_jobs_container_scanning_monthly.yml b/config/metrics/counts_28d/20221108092725_p_ci_templates_jobs_container_scanning_monthly.yml
new file mode 100644
index 00000000000..2abbb3f9b16
--- /dev/null
+++ b/config/metrics/counts_28d/20221108092725_p_ci_templates_jobs_container_scanning_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_container_scanning_monthly
+description: Monthly counts for Container Scanning CI template (Jobs folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: container_scanning
+value_type: number
+status: active
+milestone: '15.6'
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103262"
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_jobs_container_scanning
diff --git a/config/metrics/counts_28d/20221108101211_merge_request_authors_monthly.yml b/config/metrics/counts_28d/20221108101211_merge_request_authors_monthly.yml
index 5987bde2d14..4e9d58bddad 100644
--- a/config/metrics/counts_28d/20221108101211_merge_request_authors_monthly.yml
+++ b/config/metrics/counts_28d/20221108101211_merge_request_authors_monthly.yml
@@ -6,9 +6,11 @@ product_stage: create
product_group: code_review
product_category: code_review
value_type: number
-status: active
+status: removed
milestone: "15.6"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103334
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106449
+milestone_removed: "15.7"
time_frame: 28d
data_source: database
data_category: optional
diff --git a/config/metrics/counts_28d/20221121115622_i_code_review_merge_request_widget_security_reports_view_monthly.yml b/config/metrics/counts_28d/20221121115622_i_code_review_merge_request_widget_security_reports_view_monthly.yml
new file mode 100644
index 00000000000..078da4a2768
--- /dev/null
+++ b/config/metrics/counts_28d/20221121115622_i_code_review_merge_request_widget_security_reports_view_monthly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_security_reports_view_monthly
+description: The count of unique users (monthly) who were able to see the Security Reports widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+options:
+ events:
+ - i_code_review_merge_request_widget_security_reports_view
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20221121115623_i_code_review_merge_request_widget_security_reports_expand_monthly.yml b/config/metrics/counts_28d/20221121115623_i_code_review_merge_request_widget_security_reports_expand_monthly.yml
new file mode 100644
index 00000000000..2b520e995d2
--- /dev/null
+++ b/config/metrics/counts_28d/20221121115623_i_code_review_merge_request_widget_security_reports_expand_monthly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_security_reports_expand_monthly
+description: The count of unique users (monthly) who expanded the Security Reports widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+options:
+ events:
+ - i_code_review_merge_request_widget_security_reports_expand
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20221121115623_i_code_review_merge_request_widget_security_reports_full_report_clicked_monthly.yml b/config/metrics/counts_28d/20221121115623_i_code_review_merge_request_widget_security_reports_full_report_clicked_monthly.yml
new file mode 100644
index 00000000000..e18744673e4
--- /dev/null
+++ b/config/metrics/counts_28d/20221121115623_i_code_review_merge_request_widget_security_reports_full_report_clicked_monthly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_security_reports_full_report_clicked_monthly
+description: The count of unique users (monthly) who clicked the Full Report button on the Security Reports widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+options:
+ events:
+ - i_code_review_merge_request_widget_security_reports_full_report_clicked
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20221121115624_i_code_review_merge_request_widget_security_reports_expand_success_monthly.yml b/config/metrics/counts_28d/20221121115624_i_code_review_merge_request_widget_security_reports_expand_success_monthly.yml
new file mode 100644
index 00000000000..e604a3df3e6
--- /dev/null
+++ b/config/metrics/counts_28d/20221121115624_i_code_review_merge_request_widget_security_reports_expand_success_monthly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_security_reports_expand_success_monthly
+description: The count of unique users (monthly) who expanded the Security Reports widget extension while it is in its Success state
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+options:
+ events:
+ - i_code_review_merge_request_widget_security_reports_expand_success
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20221121115625_i_code_review_merge_request_widget_security_reports_expand_failed_monthly.yml b/config/metrics/counts_28d/20221121115625_i_code_review_merge_request_widget_security_reports_expand_failed_monthly.yml
new file mode 100644
index 00000000000..f33f78961a9
--- /dev/null
+++ b/config/metrics/counts_28d/20221121115625_i_code_review_merge_request_widget_security_reports_expand_failed_monthly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_security_reports_expand_failed_monthly
+description: The count of unique users (monthly) who expanded the Security Reports widget extension while it is in its Failed state
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+options:
+ events:
+ - i_code_review_merge_request_widget_security_reports_expand_failed
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20221121115625_i_code_review_merge_request_widget_security_reports_expand_warning_monthly.yml b/config/metrics/counts_28d/20221121115625_i_code_review_merge_request_widget_security_reports_expand_warning_monthly.yml
new file mode 100644
index 00000000000..28763d81c88
--- /dev/null
+++ b/config/metrics/counts_28d/20221121115625_i_code_review_merge_request_widget_security_reports_expand_warning_monthly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_security_reports_expand_warning_monthly
+description: The count of unique users (monthly) who expanded the Security Reports widget extension while it is in its Warning state
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+options:
+ events:
+ - i_code_review_merge_request_widget_security_reports_expand_warning
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20221213182900_i_code_review_create_mr_monthly.yml b/config/metrics/counts_28d/20221213182900_i_code_review_create_mr_monthly.yml
new file mode 100644
index 00000000000..dca8545691a
--- /dev/null
+++ b/config/metrics/counts_28d/20221213182900_i_code_review_create_mr_monthly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_create_mr_monthly
+description: Count of unique merge requests created per month
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106869
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+options:
+ events:
+ - i_code_review_create_mr
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20210216184452_code_review_total_unique_counts_weekly.yml b/config/metrics/counts_7d/20210216184452_code_review_total_unique_counts_weekly.yml
index 420926c825c..07985c3e56e 100644
--- a/config/metrics/counts_7d/20210216184452_code_review_total_unique_counts_weekly.yml
+++ b/config/metrics/counts_7d/20210216184452_code_review_total_unique_counts_weekly.yml
@@ -132,6 +132,12 @@ options:
- i_code_review_user_unresolve_thread
- i_code_review_user_vs_code_api_request
- i_code_review_widget_nothing_merge_click_new_file
+ - i_code_review_merge_request_widget_security_reports_expand
+ - i_code_review_merge_request_widget_security_reports_expand_failed
+ - i_code_review_merge_request_widget_security_reports_expand_success
+ - i_code_review_merge_request_widget_security_reports_expand_warning
+ - i_code_review_merge_request_widget_security_reports_full_report_clicked
+ - i_code_review_merge_request_widget_security_reports_view
distribution:
- ce
- ee
diff --git a/config/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml b/config/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml
index f3758f5bd06..029742453d8 100755
--- a/config/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml
+++ b/config/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml
@@ -94,6 +94,8 @@ options:
- p_ci_templates_jobs_secret_detection_latest
- p_ci_templates_jobs_code_intelligence
- p_ci_templates_jobs_code_quality
+ - p_ci_templates_jobs_container_scanning
+ - p_ci_templates_jobs_container_scanning_latest
- p_ci_templates_jobs_dependency_scanning
- p_ci_templates_jobs_dependency_scanning_latest
- p_ci_templates_jobs_license_scanning
@@ -142,6 +144,7 @@ options:
- p_ci_templates_implicit_jobs_secret_detection
- p_ci_templates_implicit_jobs_code_intelligence
- p_ci_templates_implicit_jobs_code_quality
+ - p_ci_templates_implicit_jobs_container_scanning
- p_ci_templates_implicit_jobs_dependency_scanning
- p_ci_templates_implicit_jobs_license_scanning
- p_ci_templates_implicit_jobs_deploy_ecs
diff --git a/config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml b/config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml
index f29f4d0cf28..f9acb05be3e 100644
--- a/config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml
+++ b/config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml
@@ -134,3 +134,9 @@ options:
- 'i_code_review_merge_request_widget_license_compliance_expand_success'
- 'i_code_review_merge_request_widget_license_compliance_expand_warning'
- 'i_code_review_merge_request_widget_license_compliance_expand_failed'
+ - 'i_code_review_merge_request_widget_security_reports_view'
+ - 'i_code_review_merge_request_widget_security_reports_full_report_clicked'
+ - 'i_code_review_merge_request_widget_security_reports_expand'
+ - 'i_code_review_merge_request_widget_security_reports_expand_success'
+ - 'i_code_review_merge_request_widget_security_reports_expand_warning'
+ - 'i_code_review_merge_request_widget_security_reports_expand_failed'
diff --git a/config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml b/config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml
index e40e7c7f19a..6ceeef68c21 100644
--- a/config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml
+++ b/config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml
@@ -132,3 +132,9 @@ options:
- 'i_code_review_merge_request_widget_license_compliance_expand_success'
- 'i_code_review_merge_request_widget_license_compliance_expand_warning'
- 'i_code_review_merge_request_widget_license_compliance_expand_failed'
+ - 'i_code_review_merge_request_widget_security_reports_view'
+ - 'i_code_review_merge_request_widget_security_reports_full_report_clicked'
+ - 'i_code_review_merge_request_widget_security_reports_expand'
+ - 'i_code_review_merge_request_widget_security_reports_expand_success'
+ - 'i_code_review_merge_request_widget_security_reports_expand_warning'
+ - 'i_code_review_merge_request_widget_security_reports_expand_failed'
diff --git a/config/metrics/counts_7d/20221108092725_p_ci_templates_implicit_jobs_container_scanning_weekly.yml b/config/metrics/counts_7d/20221108092725_p_ci_templates_implicit_jobs_container_scanning_weekly.yml
new file mode 100644
index 00000000000..8c081dd8a77
--- /dev/null
+++ b/config/metrics/counts_7d/20221108092725_p_ci_templates_implicit_jobs_container_scanning_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_container_scanning_weekly
+description: Weekly counts for implicit use of Container Scanning CI template (Jobs folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: container_scanning
+value_type: number
+status: active
+milestone: '15.6'
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103262"
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_implicit_jobs_container_scanning
diff --git a/config/metrics/counts_7d/20221108092725_p_ci_templates_jobs_container_scanning_latest_weekly.yml b/config/metrics/counts_7d/20221108092725_p_ci_templates_jobs_container_scanning_latest_weekly.yml
new file mode 100644
index 00000000000..7535ebc57a2
--- /dev/null
+++ b/config/metrics/counts_7d/20221108092725_p_ci_templates_jobs_container_scanning_latest_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_container_scanning_latest_weekly
+description: Weekly counts for Container Scanning CI Latest template (Jobs folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: container_scanning
+value_type: number
+status: active
+milestone: '15.6'
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103262"
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_jobs_container_scanning_latest
diff --git a/config/metrics/counts_7d/20221108092725_p_ci_templates_jobs_container_scanning_weekly.yml b/config/metrics/counts_7d/20221108092725_p_ci_templates_jobs_container_scanning_weekly.yml
new file mode 100644
index 00000000000..4f79b514af5
--- /dev/null
+++ b/config/metrics/counts_7d/20221108092725_p_ci_templates_jobs_container_scanning_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_container_scanning_weekly
+description: Weekly counts for Container Scanning CI template (Jobs folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: container_scanning
+value_type: number
+status: active
+milestone: '15.6'
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103262"
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_jobs_container_scanning
diff --git a/config/metrics/counts_7d/20221121115618_i_code_review_merge_request_widget_security_reports_view_weekly.yml b/config/metrics/counts_7d/20221121115618_i_code_review_merge_request_widget_security_reports_view_weekly.yml
new file mode 100644
index 00000000000..751337d22d3
--- /dev/null
+++ b/config/metrics/counts_7d/20221121115618_i_code_review_merge_request_widget_security_reports_view_weekly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_security_reports_view_weekly
+description: The count of unique users (weekly) who were able to see the Security Reports widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+options:
+ events:
+ - i_code_review_merge_request_widget_security_reports_view
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20221121115619_i_code_review_merge_request_widget_security_reports_expand_weekly.yml b/config/metrics/counts_7d/20221121115619_i_code_review_merge_request_widget_security_reports_expand_weekly.yml
new file mode 100644
index 00000000000..1f123bb8c68
--- /dev/null
+++ b/config/metrics/counts_7d/20221121115619_i_code_review_merge_request_widget_security_reports_expand_weekly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_security_reports_expand_weekly
+description: The count of unique users (weekly) who expanded the Security Reports widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+options:
+ events:
+ - i_code_review_merge_request_widget_security_reports_expand
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20221121115619_i_code_review_merge_request_widget_security_reports_full_report_clicked_weekly.yml b/config/metrics/counts_7d/20221121115619_i_code_review_merge_request_widget_security_reports_full_report_clicked_weekly.yml
new file mode 100644
index 00000000000..925b2730c2c
--- /dev/null
+++ b/config/metrics/counts_7d/20221121115619_i_code_review_merge_request_widget_security_reports_full_report_clicked_weekly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_security_reports_full_report_clicked_weekly
+description: The count of unique users (weekly) who clicked the Full Report button on the Security Reports widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+options:
+ events:
+ - i_code_review_merge_request_widget_security_reports_full_report_clicked
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20221121115620_i_code_review_merge_request_widget_security_reports_expand_success_weekly.yml b/config/metrics/counts_7d/20221121115620_i_code_review_merge_request_widget_security_reports_expand_success_weekly.yml
new file mode 100644
index 00000000000..84163e934cb
--- /dev/null
+++ b/config/metrics/counts_7d/20221121115620_i_code_review_merge_request_widget_security_reports_expand_success_weekly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_security_reports_expand_success_weekly
+description: The count of unique users (weekly) who expanded the Security Reports widget extension while it is in its Success state
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+options:
+ events:
+ - i_code_review_merge_request_widget_security_reports_expand_success
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20221121115621_i_code_review_merge_request_widget_security_reports_expand_failed_weekly.yml b/config/metrics/counts_7d/20221121115621_i_code_review_merge_request_widget_security_reports_expand_failed_weekly.yml
new file mode 100644
index 00000000000..b5729bc1ec9
--- /dev/null
+++ b/config/metrics/counts_7d/20221121115621_i_code_review_merge_request_widget_security_reports_expand_failed_weekly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_security_reports_expand_failed_weekly
+description: The count of unique users (weekly) who expanded the Security Reports widget extension while it is in its Failed state
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+options:
+ events:
+ - i_code_review_merge_request_widget_security_reports_expand_failed
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20221121115621_i_code_review_merge_request_widget_security_reports_expand_warning_weekly.yml b/config/metrics/counts_7d/20221121115621_i_code_review_merge_request_widget_security_reports_expand_warning_weekly.yml
new file mode 100644
index 00000000000..061b3a0fb30
--- /dev/null
+++ b/config/metrics/counts_7d/20221121115621_i_code_review_merge_request_widget_security_reports_expand_warning_weekly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_security_reports_expand_warning_weekly
+description: The count of unique users (weekly) who expanded the Security Reports widget extension while it is in its Warning state
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+options:
+ events:
+ - i_code_review_merge_request_widget_security_reports_expand_warning
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20221213183300_i_code_review_create_mr_weekly.yml b/config/metrics/counts_7d/20221213183300_i_code_review_create_mr_weekly.yml
new file mode 100644
index 00000000000..43405d5bd2c
--- /dev/null
+++ b/config/metrics/counts_7d/20221213183300_i_code_review_create_mr_weekly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_create_mr_weekly
+description: Count of unique merge requests created per week
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106869
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+options:
+ events:
+ - i_code_review_create_mr
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20210204124930_servers.yml b/config/metrics/counts_all/20210204124930_servers.yml
index bd756b8bcef..7e5f41d54f5 100644
--- a/config/metrics/counts_all/20210204124930_servers.yml
+++ b/config/metrics/counts_all/20210204124930_servers.yml
@@ -9,7 +9,7 @@ product_category: gitaly
value_type: number
status: active
time_frame: all
-data_source: database
+data_source: system
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210204124932_clusters.yml b/config/metrics/counts_all/20210204124932_clusters.yml
index 6f7524c8765..0e37a6c19b5 100644
--- a/config/metrics/counts_all/20210204124932_clusters.yml
+++ b/config/metrics/counts_all/20210204124932_clusters.yml
@@ -9,7 +9,7 @@ product_category: gitaly
value_type: number
status: active
time_frame: all
-data_source: database
+data_source: system
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210216175837_projects_flowdock_active.yml b/config/metrics/counts_all/20210216175837_projects_flowdock_active.yml
index 46db9f97e85..b88351eb4dc 100644
--- a/config/metrics/counts_all/20210216175837_projects_flowdock_active.yml
+++ b/config/metrics/counts_all/20210216175837_projects_flowdock_active.yml
@@ -7,7 +7,9 @@ product_stage: manage
product_group: integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102394
+milestone_removed: '15.7'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175839_groups_flowdock_active.yml b/config/metrics/counts_all/20210216175839_groups_flowdock_active.yml
index d5da36978b6..f77fe5ec728 100644
--- a/config/metrics/counts_all/20210216175839_groups_flowdock_active.yml
+++ b/config/metrics/counts_all/20210216175839_groups_flowdock_active.yml
@@ -7,7 +7,9 @@ product_stage: manage
product_group: integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102394
+milestone_removed: '15.7'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175842_instances_flowdock_active.yml b/config/metrics/counts_all/20210216175842_instances_flowdock_active.yml
index 198af43a99d..f22e6e6bc77 100644
--- a/config/metrics/counts_all/20210216175842_instances_flowdock_active.yml
+++ b/config/metrics/counts_all/20210216175842_instances_flowdock_active.yml
@@ -7,7 +7,9 @@ product_stage: manage
product_group: integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102394
+milestone_removed: '15.7'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175844_projects_inheriting_flowdock_active.yml b/config/metrics/counts_all/20210216175844_projects_inheriting_flowdock_active.yml
index f094f894ded..a291191eaeb 100644
--- a/config/metrics/counts_all/20210216175844_projects_inheriting_flowdock_active.yml
+++ b/config/metrics/counts_all/20210216175844_projects_inheriting_flowdock_active.yml
@@ -7,7 +7,9 @@ product_stage: manage
product_group: integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102394
+milestone_removed: '15.7'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175846_groups_inheriting_flowdock_active.yml b/config/metrics/counts_all/20210216175846_groups_inheriting_flowdock_active.yml
index fb7931ddf09..c3c4a01a809 100644
--- a/config/metrics/counts_all/20210216175846_groups_inheriting_flowdock_active.yml
+++ b/config/metrics/counts_all/20210216175846_groups_inheriting_flowdock_active.yml
@@ -7,7 +7,9 @@ product_stage: manage
product_group: integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102394
+milestone_removed: '15.7'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216181038_projects_with_expiration_policy_enabled_with_older_than_set_to_7d.yml b/config/metrics/counts_all/20210216181038_projects_with_expiration_policy_enabled_with_older_than_set_to_7d.yml
index bf1a98fd290..22c6c6b7833 100644
--- a/config/metrics/counts_all/20210216181038_projects_with_expiration_policy_enabled_with_older_than_set_to_7d.yml
+++ b/config/metrics/counts_all/20210216181038_projects_with_expiration_policy_enabled_with_older_than_set_to_7d.yml
@@ -11,6 +11,10 @@ value_type: number
status: active
time_frame: all
data_source: database
+instrumentation_class: DistinctCountProjectsWithExpirationPolicyMetric
+options:
+ enabled: true
+ older_than: 7d
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216181040_projects_with_expiration_policy_enabled_with_older_than_set_to_14d.yml b/config/metrics/counts_all/20210216181040_projects_with_expiration_policy_enabled_with_older_than_set_to_14d.yml
index 31f7f6562e4..e4094c25594 100644
--- a/config/metrics/counts_all/20210216181040_projects_with_expiration_policy_enabled_with_older_than_set_to_14d.yml
+++ b/config/metrics/counts_all/20210216181040_projects_with_expiration_policy_enabled_with_older_than_set_to_14d.yml
@@ -11,6 +11,10 @@ value_type: number
status: active
time_frame: all
data_source: database
+instrumentation_class: DistinctCountProjectsWithExpirationPolicyMetric
+options:
+ enabled: true
+ older_than: 14d
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216181042_projects_with_expiration_policy_enabled_with_older_than_set_to_30d.yml b/config/metrics/counts_all/20210216181042_projects_with_expiration_policy_enabled_with_older_than_set_to_30d.yml
index 577e9e60d56..11e7b1ebef5 100644
--- a/config/metrics/counts_all/20210216181042_projects_with_expiration_policy_enabled_with_older_than_set_to_30d.yml
+++ b/config/metrics/counts_all/20210216181042_projects_with_expiration_policy_enabled_with_older_than_set_to_30d.yml
@@ -11,6 +11,10 @@ value_type: number
status: active
time_frame: all
data_source: database
+instrumentation_class: DistinctCountProjectsWithExpirationPolicyMetric
+options:
+ enabled: true
+ older_than: 30d
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216181044_projects_with_expiration_policy_enabled_with_older_than_set_to_90d.yml b/config/metrics/counts_all/20210216181044_projects_with_expiration_policy_enabled_with_older_than_set_to_90d.yml
index 3ef97b5a6f8..987bffdcef8 100644
--- a/config/metrics/counts_all/20210216181044_projects_with_expiration_policy_enabled_with_older_than_set_to_90d.yml
+++ b/config/metrics/counts_all/20210216181044_projects_with_expiration_policy_enabled_with_older_than_set_to_90d.yml
@@ -11,6 +11,10 @@ value_type: number
status: active
time_frame: all
data_source: database
+instrumentation_class: DistinctCountProjectsWithExpirationPolicyMetric
+options:
+ enabled: true
+ older_than: 90d
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216181048_projects_with_expiration_policy_enabled_with_older_than_unset.yml b/config/metrics/counts_all/20210216181048_projects_with_expiration_policy_enabled_with_older_than_unset.yml
index d2be783a12a..a4cfecc228b 100644
--- a/config/metrics/counts_all/20210216181048_projects_with_expiration_policy_enabled_with_older_than_unset.yml
+++ b/config/metrics/counts_all/20210216181048_projects_with_expiration_policy_enabled_with_older_than_unset.yml
@@ -11,6 +11,10 @@ value_type: number
status: active
time_frame: all
data_source: database
+instrumentation_class: DistinctCountProjectsWithExpirationPolicyMetric
+options:
+ enabled: true
+ older_than: null
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216181051_vendor.yml b/config/metrics/counts_all/20210216181051_vendor.yml
index 3ea154f5877..ecc9cf97b4d 100644
--- a/config/metrics/counts_all/20210216181051_vendor.yml
+++ b/config/metrics/counts_all/20210216181051_vendor.yml
@@ -10,7 +10,7 @@ product_category: container registry
value_type: number
status: active
time_frame: all
-data_source: database
+data_source: system
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210915082040_projects_with_expiration_policy_enabled_with_older_than_set_to_60d.yml b/config/metrics/counts_all/20210915082040_projects_with_expiration_policy_enabled_with_older_than_set_to_60d.yml
index 276b1c97e0c..bd00db71abb 100644
--- a/config/metrics/counts_all/20210915082040_projects_with_expiration_policy_enabled_with_older_than_set_to_60d.yml
+++ b/config/metrics/counts_all/20210915082040_projects_with_expiration_policy_enabled_with_older_than_set_to_60d.yml
@@ -11,6 +11,10 @@ value_type: number
status: active
time_frame: all
data_source: database
+instrumentation_class: DistinctCountProjectsWithExpirationPolicyMetric
+options:
+ enabled: true
+ older_than: 60d
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20221121113321_i_code_review_merge_request_widget_security_reports_count_view.yml b/config/metrics/counts_all/20221121113321_i_code_review_merge_request_widget_security_reports_count_view.yml
new file mode 100644
index 00000000000..4c17bd746b3
--- /dev/null
+++ b/config/metrics/counts_all/20221121113321_i_code_review_merge_request_widget_security_reports_count_view.yml
@@ -0,0 +1,26 @@
+---
+key_path: counts.i_code_review_merge_request_widget_security_reports_count_view
+description: Total number of times the Security Reports widget extension was viewed (rendered to the screen)
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: all
+data_source: redis
+data_category: optional
+instrumentation_class: MergeRequestWidgetExtensionMetric
+performance_indicator_type: []
+options:
+ event: view
+ widget: security_reports
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20221121113323_i_code_review_merge_request_widget_security_reports_count_expand.yml b/config/metrics/counts_all/20221121113323_i_code_review_merge_request_widget_security_reports_count_expand.yml
new file mode 100644
index 00000000000..60b6b75d064
--- /dev/null
+++ b/config/metrics/counts_all/20221121113323_i_code_review_merge_request_widget_security_reports_count_expand.yml
@@ -0,0 +1,26 @@
+---
+key_path: counts.i_code_review_merge_request_widget_security_reports_count_expand
+description: Total number of times the Security Reports widget extension was expanded (in any state)
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: all
+data_source: redis
+data_category: optional
+instrumentation_class: MergeRequestWidgetExtensionMetric
+performance_indicator_type: []
+options:
+ event: expand
+ widget: security_reports
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20221121113323_i_code_review_merge_request_widget_security_reports_count_full_report_clicked.yml b/config/metrics/counts_all/20221121113323_i_code_review_merge_request_widget_security_reports_count_full_report_clicked.yml
new file mode 100644
index 00000000000..f5fef7e7cbe
--- /dev/null
+++ b/config/metrics/counts_all/20221121113323_i_code_review_merge_request_widget_security_reports_count_full_report_clicked.yml
@@ -0,0 +1,26 @@
+---
+key_path: counts.i_code_review_merge_request_widget_security_reports_count_full_report_clicked
+description: Total number of times the Security Reports widget extension Full Report button was clicked
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: all
+data_source: redis
+data_category: optional
+instrumentation_class: MergeRequestWidgetExtensionMetric
+performance_indicator_type: []
+options:
+ event: full_report_clicked
+ widget: security_reports
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20221121113324_i_code_review_merge_request_widget_security_reports_count_expand_success.yml b/config/metrics/counts_all/20221121113324_i_code_review_merge_request_widget_security_reports_count_expand_success.yml
new file mode 100644
index 00000000000..fa0b573be84
--- /dev/null
+++ b/config/metrics/counts_all/20221121113324_i_code_review_merge_request_widget_security_reports_count_expand_success.yml
@@ -0,0 +1,26 @@
+---
+key_path: counts.i_code_review_merge_request_widget_security_reports_count_expand_success
+description: Total number of times the Security Reports widget extension was expanded (while in its Success state)
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: all
+data_source: redis
+data_category: optional
+instrumentation_class: MergeRequestWidgetExtensionMetric
+performance_indicator_type: []
+options:
+ event: expand_success
+ widget: security_reports
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20221121113325_i_code_review_merge_request_widget_security_reports_count_expand_failed.yml b/config/metrics/counts_all/20221121113325_i_code_review_merge_request_widget_security_reports_count_expand_failed.yml
new file mode 100644
index 00000000000..20e21862a9a
--- /dev/null
+++ b/config/metrics/counts_all/20221121113325_i_code_review_merge_request_widget_security_reports_count_expand_failed.yml
@@ -0,0 +1,26 @@
+---
+key_path: counts.i_code_review_merge_request_widget_security_reports_count_expand_failed
+description: Total number of times the Security Reports widget extension was expanded (while in its Failed state)
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: all
+data_source: redis
+data_category: optional
+instrumentation_class: MergeRequestWidgetExtensionMetric
+performance_indicator_type: []
+options:
+ event: expand_failed
+ widget: security_reports
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20221121113325_i_code_review_merge_request_widget_security_reports_count_expand_warning.yml b/config/metrics/counts_all/20221121113325_i_code_review_merge_request_widget_security_reports_count_expand_warning.yml
new file mode 100644
index 00000000000..7a917d85b9c
--- /dev/null
+++ b/config/metrics/counts_all/20221121113325_i_code_review_merge_request_widget_security_reports_count_expand_warning.yml
@@ -0,0 +1,26 @@
+---
+key_path: counts.i_code_review_merge_request_widget_security_reports_count_expand_warning
+description: Total number of times the Security Reports widget extension was expanded (while in its Warning state)
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.7"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104578
+time_frame: all
+data_source: redis
+data_category: optional
+instrumentation_class: MergeRequestWidgetExtensionMetric
+performance_indicator_type: []
+options:
+ event: expand_warning
+ widget: security_reports
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/settings/20210216175609_version.yml b/config/metrics/settings/20210216175609_version.yml
index 24637178a2a..493c93d3d1a 100644
--- a/config/metrics/settings/20210216175609_version.yml
+++ b/config/metrics/settings/20210216175609_version.yml
@@ -9,7 +9,7 @@ product_category: collection
value_type: string
status: active
time_frame: none
-data_source: database
+data_source: system
distribution:
- ce
- ee
diff --git a/config/metrics/settings/20210216180314_gitpod_enabled.yml b/config/metrics/settings/20210216180314_gitpod_enabled.yml
index 73ba2dd01fd..fc12cc8803f 100644
--- a/config/metrics/settings/20210216180314_gitpod_enabled.yml
+++ b/config/metrics/settings/20210216180314_gitpod_enabled.yml
@@ -9,7 +9,7 @@ product_category: integrations
value_type: boolean
status: active
time_frame: none
-data_source: database
+data_source: system
distribution:
- ce
- ee
diff --git a/config/metrics/settings/20210216180841_background_upload.yml b/config/metrics/settings/20210216180841_background_upload.yml
index e9d9d475b06..22f7e5e078d 100644
--- a/config/metrics/settings/20210216180841_background_upload.yml
+++ b/config/metrics/settings/20210216180841_background_upload.yml
@@ -7,7 +7,8 @@ product_stage: enablement
product_group: memory
product_category: memory
value_type: boolean
-status: active
+status: broken
+repair_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382900
time_frame: none
data_source: system
distribution:
diff --git a/config/metrics/settings/20210216180851_background_upload.yml b/config/metrics/settings/20210216180851_background_upload.yml
index b7a0d328b8b..21536e35303 100644
--- a/config/metrics/settings/20210216180851_background_upload.yml
+++ b/config/metrics/settings/20210216180851_background_upload.yml
@@ -8,7 +8,8 @@ product_stage: enablement
product_group: memory
product_category: memory
value_type: boolean
-status: active
+status: broken
+repair_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382900
time_frame: none
data_source: system
distribution:
diff --git a/config/metrics/settings/20210216180900_background_upload.yml b/config/metrics/settings/20210216180900_background_upload.yml
index ed6931dd443..df93b11c656 100644
--- a/config/metrics/settings/20210216180900_background_upload.yml
+++ b/config/metrics/settings/20210216180900_background_upload.yml
@@ -7,7 +7,8 @@ product_stage: enablement
product_group: memory
product_category: memory
value_type: boolean
-status: active
+status: broken
+repair_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382900
time_frame: none
data_source: system
distribution:
diff --git a/config/metrics/settings/20210216180909_background_upload.yml b/config/metrics/settings/20210216180909_background_upload.yml
index c18a22d1634..32871d5d30e 100644
--- a/config/metrics/settings/20210216180909_background_upload.yml
+++ b/config/metrics/settings/20210216180909_background_upload.yml
@@ -7,7 +7,8 @@ product_stage: enablement
product_group: memory
product_category: memory
value_type: boolean
-status: active
+status: broken
+repair_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382900
time_frame: none
data_source: system
distribution:
diff --git a/config/metrics/settings/20210216180918_background_upload.yml b/config/metrics/settings/20210216180918_background_upload.yml
index 940273ff972..186e7c8b2b9 100644
--- a/config/metrics/settings/20210216180918_background_upload.yml
+++ b/config/metrics/settings/20210216180918_background_upload.yml
@@ -7,7 +7,8 @@ product_stage: enablement
product_group: memory
product_category: memory
value_type: boolean
-status: active
+status: broken
+repair_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382900
time_frame: none
data_source: system
distribution:
diff --git a/config/object_store_settings.rb b/config/object_store_settings.rb
index 173ce5a5982..3fc24dab3d7 100644
--- a/config/object_store_settings.rb
+++ b/config/object_store_settings.rb
@@ -27,7 +27,6 @@ class ObjectStoreSettings
)
object_store['direct_upload'] = true
- object_store['background_upload'] = false
object_store['proxy_download'] = false if object_store['proxy_download'].nil?
object_store['storage_options'] ||= {}
@@ -97,7 +96,6 @@ class ObjectStoreSettings
# "server_side_encryption" => "AES256"
# },
# "direct_upload" => true,
- # "background_upload" => false,
# "proxy_download" => false,
# "remote_directory" => "artifacts"
# }
@@ -116,7 +114,6 @@ class ObjectStoreSettings
# "server_side_encryption" => "AES256"
# },
# "direct_upload" => true,
- # "background_upload" => false,
# "proxy_download" => true,
# "remote_directory" => "lfs-objects"
# }
@@ -128,7 +125,7 @@ class ObjectStoreSettings
# 2. However, a bucket has to be specified for each object
# type. Reusing buckets is not really supported, but we don't
# enforce that yet.
- # 3. direct_upload and background_upload cannot be configured anymore.
+ # 3. direct_upload cannot be configured anymore.
def parse!
return unless use_consolidated_settings?
@@ -138,7 +135,6 @@ class ObjectStoreSettings
common_config['connection']&.deep_stringify_keys!
# These are no longer configurable if common config is used
common_config['direct_upload'] = true
- common_config['background_upload'] = false
common_config['storage_options'] ||= {}
SUPPORTED_TYPES.each do |store_type|
diff --git a/config/open_api.yml b/config/open_api.yml
index 6e767f51ef8..cbf70c24ce1 100644
--- a/config/open_api.yml
+++ b/config/open_api.yml
@@ -27,10 +27,20 @@ metadata:
description: Operations related to the GitLab agent for Kubernetes
- name: clusters
description: Operations related to clusters
+ - name: composer_packages
+ description: Operations related to Composer packages
+ - name: conan_packages
+ description: Operations related to Conan packages
- name: container_registry
description: Operations related to container registry
+ - name: container_registry_event
+ description: Operations related to container registry events
- name: dashboard_annotations
description: Operations related to dashboard annotations
+ - name: debian_distribution
+ description: Operations related to Debian Linux distributions
+ - name: debian_packages
+ description: Operations related to Debian Linux packages
- name: dependency_proxy
description: Operations to manage dependency proxy for a groups
- name: deploy_keys
@@ -55,6 +65,8 @@ metadata:
description: Operations related to managing Flipper-based feature flags
- name: freeze_periods
description: Operations related to deploy freeze periods
+ - name: generic_packages
+ description: Operations related to Generic packages
- name: geo
description: Operations related to Geo
- name: geo_nodes
@@ -67,16 +79,26 @@ metadata:
description: Operations related to importing groups
- name: group_packages
description: Operations related to group packages
+ - name: helm_packages
+ description: Operations related to Helm packages
- name: integrations
description: Operations related to integrations
- name: issue_links
description: Operations related to issue links
+ - name: jira_connect_subscriptions
+ description: Operations related to JiraConnect subscriptions
+ - name: maven_packages
+ description: Operations related to Maven packages
- name: merge_requests
description: Operations related to merge requests
- name: metadata
description: Operations related to metadata of the GitLab instance
- name: metrics_user_starred_dashboards
description: Operations related to User-starred metrics dashboards
+ - name: npm_packages
+ description: Operations related to NPM packages
+ - name: nuget_packages
+ description: Operations related to Nuget packages
- name: package_files
description: Operations about package files
- name: plan_limits
@@ -91,14 +113,24 @@ metadata:
description: Operations related to importing BitBucket projects
- name: project_import_github
description: Operations related to importing GitHub projects
+ - name: project_packages
+ description: Operations related to project packages
+ - name: projects
+ description: Operations related to projects
- name: protected environments
description: Operations related to protected environments
+ - name: pypi_packages
+ description: Operations related to PyPI packages
- name: release_links
description: Operations related to release assets (links)
- name: releases
description: Operations related to releases
- name: resource_milestone_events
description: Operations about resource milestone events
+ - name: rpm_packages
+ description: Operations related to RPM packages
+ - name: rubygem_packages
+ description: Operations related to RubyGems
- name: suggestions
description: Operations related to suggestions
- name: system_hooks
diff --git a/config/routes.rb b/config/routes.rb
index 27313854233..a9cb462b326 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -55,9 +55,9 @@ InitializerConnections.with_disabled_database_connections do
match '/oauth/token' => 'oauth/tokens#create', via: :options
match '/oauth/revoke' => 'oauth/tokens#revoke', via: :options
- match '/-/jira_connect/oauth_application_id' => 'jira_connect/cors_preflight_checks#index', via: :options
- match '/-/jira_connect/subscriptions/:id' => 'jira_connect/cors_preflight_checks#index', via: :options
- match '/-/jira_connect/installations' => 'jira_connect/cors_preflight_checks#index', via: :options
+ match '/-/jira_connect/oauth_application_id' => 'jira_connect/oauth_application_ids#show', via: :options
+ match '/-/jira_connect/subscriptions(.:format)' => 'jira_connect/subscriptions#index', via: :options
+ match '/-/jira_connect/subscriptions/:id' => 'jira_connect/subscriptions#delete', via: :options
# Sign up
scope path: '/users/sign_up', module: :registrations, as: :users_sign_up do
@@ -155,6 +155,12 @@ InitializerConnections.with_disabled_database_connections do
get '/merge_requests/:merge_request_id', to: 'ide#index', constraints: { merge_request_id: /\d+/ }
get '/', to: 'ide#index'
end
+
+ # Remote host can contain "." characters so it needs a constraint
+ post 'remote/:remote_host(/*remote_path)',
+ as: :remote,
+ to: 'web_ide/remote_ide#index',
+ constraints: { remote_host: %r{[^/?]+} }
end
draw :operations
diff --git a/config/routes/group.rb b/config/routes/group.rb
index a715596580d..819db0bb6b1 100644
--- a/config/routes/group.rb
+++ b/config/routes/group.rb
@@ -61,6 +61,8 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
resource :packages_and_registries, only: [:show]
end
+ resources :usage_quotas, only: [:index]
+
resource :variables, only: [:show, :update]
resources :children, only: [:index]
diff --git a/config/routes/merge_requests.rb b/config/routes/merge_requests.rb
index b0bab1717a6..29e0d65b58c 100644
--- a/config/routes/merge_requests.rb
+++ b/config/routes/merge_requests.rb
@@ -34,7 +34,7 @@ resources :merge_requests, concerns: :awardable, except: [:new, :create, :show],
scope action: :show do
get :commits, defaults: { tab: 'commits' }
get :pipelines, defaults: { tab: 'pipelines' }
- get :diffs, defaults: { tab: 'diffs' }
+ get :diffs, to: 'merge_requests#diffs', defaults: { tab: 'diffs' }
end
get :diff_for_path, controller: 'merge_requests/diffs'
@@ -78,6 +78,7 @@ scope path: 'merge_requests', controller: 'merge_requests/creations' do
scope constraints: ->(req) { req.format == :json }, as: :json do
get :diffs
get :pipelines
+ get :target_projects
end
scope action: :new do
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 5a85a029607..798829484da 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -475,6 +475,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
namespace :ml do
resources :experiments, only: [:index, :show], controller: 'experiments'
+ resources :candidates, only: [:show], controller: 'candidates', param: :iid
end
end
# End of the /-/ scope.
diff --git a/config/routes/user.rb b/config/routes/user.rb
index 0c1bc1956a9..1c122ea0c94 100644
--- a/config/routes/user.rb
+++ b/config/routes/user.rb
@@ -55,6 +55,22 @@ devise_scope :user do
get '/users/almost_there' => 'confirmations#almost_there'
post '/users/resend_verification_code', to: 'sessions#resend_verification_code'
get '/users/successful_verification', to: 'sessions#successful_verification'
+
+ # Redirect on GitHub authorization request errors. E.g. it could happen when user:
+ # 1. cancel authorization the GitLab OAuth app via GitHub to import GitHub repos
+ # (they'll be redirected to /projects/new#import_project)
+ # 2. cancel signing in to GitLab using GitHub account
+ # (they'll be redirected to /users/sign_in)
+ # In these cases, GitHub redirects user to the GitLab OAuth app's
+ # registered callback URL - /users/auth, which is the url to the auth user's profile page
+ get '/users/auth',
+ constraints: ->(req) {
+ req.params[:error].present? && req.params[:state].present?
+ },
+ to: redirect { |_params, req|
+ redirect_path = req.session.delete(:auth_on_failure_path)
+ redirect_path || Rails.application.routes.url_helpers.new_user_session_path
+ }
end
scope '-/users', module: :users do
diff --git a/config/settings.rb b/config/settings.rb
index 51d54817646..34acb09b9ed 100644
--- a/config/settings.rb
+++ b/config/settings.rb
@@ -42,12 +42,10 @@ class Settings < Settingslogic
if gitlab_shell.ssh_port != 22
"ssh://#{user_host}:#{gitlab_shell.ssh_port}/"
+ elsif gitlab_shell.ssh_host.include? ':'
+ "[#{user_host}]:"
else
- if gitlab_shell.ssh_host.include? ':'
- "[#{user_host}]:"
- else
- "#{user_host}:"
- end
+ "#{user_host}:"
end
end
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index 4ed4dca8912..929df749422 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -71,6 +71,8 @@
- 1
- - background_migration_ci_database
- 1
+- - batched_background_migrations
+ - 1
- - bulk_import
- 1
- - bulk_imports_entity
@@ -129,6 +131,10 @@
- 1
- - delete_user
- 1
+- - dependencies_destroy_export
+ - 1
+- - dependencies_export
+ - 1
- - dependency_proxy
- 1
- - dependency_proxy_blob
@@ -193,6 +199,8 @@
- 1
- - geo
- 1
+- - github_gists_importer
+ - 1
- - github_import_advance_stage
- 1
- - github_importer
@@ -259,8 +267,6 @@
- 1
- - integrations_slack_event
- 1
-- - integrations_slack_interactivity
- - 1
- - invalid_gpg_signature_update
- 2
- - issuable_export_csv
@@ -295,14 +301,14 @@
- 1
- - merge_request_reset_approvals
- 1
+- - merge_requests_capture_suggested_reviewers_accepted
+ - 1
- - merge_requests_close_issue
- 1
- - merge_requests_create_approval_event
- 1
- - merge_requests_create_approval_note
- 1
-- - merge_requests_delete_branch
- - 1
- - merge_requests_delete_source_branch
- 1
- - merge_requests_execute_approval_hooks
@@ -401,8 +407,12 @@
- 1
- - projects_after_import
- 1
+- - projects_delete_branch
+ - 1
- - projects_git_garbage_collect
- 1
+- - projects_import_export_parallel_project_export
+ - 1
- - projects_import_export_relation_export
- 1
- - projects_inactive_projects_deletion_notification
@@ -431,6 +441,8 @@
- 1
- - propagate_integration_project
- 1
+- - pull_mirrors_reenable_configuration
+ - 1
- - reactive_caching
- 1
- - rebase
diff --git a/config/webpack.config.js b/config/webpack.config.js
index d79e6e12b39..a0c65ed4012 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -160,6 +160,7 @@ function generateEntries() {
*/
const manualEntries = {
default: defaultEntries,
+ legacy_sentry: './sentry/legacy_index.js',
sentry: './sentry/index.js',
performance_bar: './performance_bar/index.js',
jira_connect_app: './jira_connect/subscriptions/index.js',
@@ -174,6 +175,11 @@ function generateEntries() {
const alias = {
// Map Apollo client to apollo/client/core to prevent react related imports from being loaded
'@apollo/client$': '@apollo/client/core',
+ // Map Sentry calls to use local wrapper
+ '@sentry/browser$': path.join(
+ ROOT_PATH,
+ 'app/assets/javascripts/sentry/sentry_browser_wrapper.js',
+ ),
'~': path.join(ROOT_PATH, 'app/assets/javascripts'),
emojis: path.join(ROOT_PATH, 'fixtures/emojis'),
empty_states: path.join(ROOT_PATH, 'app/views/shared/empty_states'),
@@ -378,6 +384,7 @@ module.exports = {
},
{
test: /_worker\.js$/,
+ resourceQuery: /worker/,
use: [
{
loader: 'worker-loader',
@@ -429,6 +436,7 @@ module.exports = {
},
{
test: /\.(yml|yaml)$/,
+ resourceQuery: /raw/,
loader: 'raw-loader',
},
].filter(Boolean),
diff --git a/danger/plugins/stable_branch.rb b/danger/plugins/stable_branch.rb
new file mode 100644
index 00000000000..81dae35ff24
--- /dev/null
+++ b/danger/plugins/stable_branch.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require_relative '../../tooling/danger/stable_branch'
+
+module Danger
+ class StableBranch < ::Danger::Plugin
+ include Tooling::Danger::StableBranch
+ end
+end
diff --git a/danger/plugins/user_types.rb b/danger/plugins/user_types.rb
new file mode 100644
index 00000000000..4f7dd572224
--- /dev/null
+++ b/danger/plugins/user_types.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require_relative '../../tooling/danger/user_types'
+
+module Danger
+ class UserTypes < ::Danger::Plugin
+ include Tooling::Danger::UserTypes
+ end
+end
diff --git a/danger/product_intelligence/Dangerfile b/danger/product_intelligence/Dangerfile
index c38d87604cc..1be549f139f 100644
--- a/danger/product_intelligence/Dangerfile
+++ b/danger/product_intelligence/Dangerfile
@@ -1,3 +1,5 @@
# frozen_string_literal: true
product_intelligence.check!
+
+product_intelligence.check_affected_scopes!
diff --git a/danger/qa_selector/Dangerfile b/danger/qa_selector/Dangerfile
new file mode 100644
index 00000000000..a20e626c694
--- /dev/null
+++ b/danger/qa_selector/Dangerfile
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+def filter_changed_lines(files)
+ lines = []
+ files.each do |file|
+ qa_selector_changed_lines = helper.changed_lines(file).select { |line| line =~ /qa_selector|data-qa-selector/ }
+ next unless qa_selector_changed_lines.any?
+
+ lines += ["file `#{file}`:", qa_selector_changed_lines]
+ end
+ lines
+end
+
+changed_code_files = helper.changed_files(/\.(vue|haml|js|rb)$/)
+
+return if changed_code_files.empty?
+
+lines_with_qa_selectors = filter_changed_lines(changed_code_files)
+
+return if lines_with_qa_selectors.empty?
+
+markdown(<<~MARKDOWN)
+ ## QA Selectors
+
+MARKDOWN
+
+if lines_with_qa_selectors.any?
+ markdown(<<~MARKDOWN)
+ The following changed lines in this MR contain QA selectors:
+
+ * #{lines_with_qa_selectors.join("\n* ")}
+
+ Please ensure `e2e:package-and-test` job is run and the tests are passing.
+
+ For the list of known failures please refer to [the latest pipeline triage issue](https://gitlab.com/gitlab-org/quality/pipeline-triage/-/issues).
+
+ If your changes are under a feature flag, please check our [Testing with feature flags](https://docs.gitlab.com/ee/development/testing_guide/end_to_end/feature_flags.html#automatic-test-execution-when-a-feature-flag-definition-changes) documentation for instructions.
+
+ MARKDOWN
+
+ warn "This merge request contains lines with QA selectors. Please ensure `e2e:package-and-test` job is run."
+end
diff --git a/danger/specs/Dangerfile b/danger/specs/Dangerfile
index 145f7237458..d17c17bc545 100644
--- a/danger/specs/Dangerfile
+++ b/danger/specs/Dangerfile
@@ -2,6 +2,7 @@
NO_SPECS_LABELS = [
'maintenance::pipelines',
+ 'maintenance::refactor',
'maintenance::workflow',
'documentation',
'QA'
@@ -56,4 +57,5 @@ end
specs.changed_specs_files.each do |filename|
specs.add_suggestions_for_match_with_array(filename)
specs.add_suggestions_for_project_factory_usage(filename)
+ specs.add_suggestions_for_feature_category(filename)
end
diff --git a/danger/stable_branch_patch/Dangerfile b/danger/stable_branch_patch/Dangerfile
new file mode 100644
index 00000000000..b3326e82020
--- /dev/null
+++ b/danger/stable_branch_patch/Dangerfile
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+stable_branch.check!
diff --git a/danger/user_types/Dangerfile b/danger/user_types/Dangerfile
new file mode 100644
index 00000000000..4b7ab1dbe39
--- /dev/null
+++ b/danger/user_types/Dangerfile
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+user_types.bot_user_types_change_warning
diff --git a/data/deprecations/14-0-nfs-fot-git-repository-storage.yml b/data/deprecations/14-0-nfs-fot-git-repository-storage.yml
index 778744c9363..b0746502713 100644
--- a/data/deprecations/14-0-nfs-fot-git-repository-storage.yml
+++ b/data/deprecations/14-0-nfs-fot-git-repository-storage.yml
@@ -1,4 +1,4 @@
-- name: "NFS for Git repository storage" # The name of the feature to be deprecated
+- title: "NFS for Git repository storage" # The name of the feature to be deprecated
announcement_milestone: "14.0" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-06-22" # The date of the milestone release when this feature was first announced as deprecated
removal_milestone: "15.6" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-10-dependency-scanning-default-java-version.yml b/data/deprecations/14-10-dependency-scanning-default-java-version.yml
index 1717c452891..c0ef84a562e 100644
--- a/data/deprecations/14-10-dependency-scanning-default-java-version.yml
+++ b/data/deprecations/14-10-dependency-scanning-default-java-version.yml
@@ -1,4 +1,4 @@
-- name: "Dependency Scanning default Java version changed to 17"
+- title: "Dependency Scanning default Java version changed to 17"
announcement_milestone: "14.10"
announcement_date: "2022-04-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-10-deprecate-toggle-notes-confidentiality.yml b/data/deprecations/14-10-deprecate-toggle-notes-confidentiality.yml
index e5646f5123d..8a4dcd47e2c 100644
--- a/data/deprecations/14-10-deprecate-toggle-notes-confidentiality.yml
+++ b/data/deprecations/14-10-deprecate-toggle-notes-confidentiality.yml
@@ -1,4 +1,4 @@
-- name: "Toggle notes confidentiality on APIs"
+- title: "Toggle notes confidentiality on APIs"
announcement_milestone: "14.10" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-03-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-10-old-search-migration-removal.yml b/data/deprecations/14-10-old-search-migration-removal.yml
index a0074c53ffb..dd6161c2414 100644
--- a/data/deprecations/14-10-old-search-migration-removal.yml
+++ b/data/deprecations/14-10-old-search-migration-removal.yml
@@ -1,4 +1,4 @@
-- name: "Outdated indices of Advanced Search migrations"
+- title: "Outdated indices of Advanced Search migrations"
announcement_milestone: "14.10"
announcement_date: "2021-04-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-2-deprecation-release-cli.yml b/data/deprecations/14-2-deprecation-release-cli.yml
index fe53dea309a..f937728b944 100644
--- a/data/deprecations/14-2-deprecation-release-cli.yml
+++ b/data/deprecations/14-2-deprecation-release-cli.yml
@@ -1,4 +1,4 @@
-- name: "Release CLI distributed as a generic package" # The name of the feature to be deprecated
+- title: "Release CLI distributed as a generic package" # The name of the feature to be deprecated
announcement_milestone: "14.2" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-08-22" # The date of the milestone release when this feature was first announced as deprecated
removal_milestone: "14.6" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-2-deprecation-task-runner.yml b/data/deprecations/14-2-deprecation-task-runner.yml
index 1b72330c5bd..b69fb0969d5 100644
--- a/data/deprecations/14-2-deprecation-task-runner.yml
+++ b/data/deprecations/14-2-deprecation-task-runner.yml
@@ -1,4 +1,4 @@
-- name: "Rename Task Runner pod to Toolbox" # The name of the feature to be deprecated
+- title: "Rename Task Runner pod to Toolbox" # The name of the feature to be deprecated
announcement_milestone: "14.2" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-08-22" # The date of the milestone release when this feature was first announced as deprecated
removal_milestone: "14.5" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-3-database-deprecate-legacy-database-conf.yml b/data/deprecations/14-3-database-deprecate-legacy-database-conf.yml
index 3644b681da0..829e1b39b0f 100644
--- a/data/deprecations/14-3-database-deprecate-legacy-database-conf.yml
+++ b/data/deprecations/14-3-database-deprecate-legacy-database-conf.yml
@@ -1,4 +1,4 @@
-- name: "Legacy database configuration"
+- title: "Legacy database configuration"
announcement_milestone: "14.3"
announcement_date: "2021-09-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-3-deprecation_omniauth-kerberos_gem.yml b/data/deprecations/14-3-deprecation_omniauth-kerberos_gem.yml
index ba7c07eaf83..5f28497e982 100644
--- a/data/deprecations/14-3-deprecation_omniauth-kerberos_gem.yml
+++ b/data/deprecations/14-3-deprecation_omniauth-kerberos_gem.yml
@@ -1,4 +1,4 @@
-- name: "OmniAuth Kerberos gem"
+- title: "OmniAuth Kerberos gem"
announcement_milestone: "14.3"
announcement_date: "2021-09-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-3-repository-push-audit-events.yml b/data/deprecations/14-3-repository-push-audit-events.yml
index 033fa957320..bec376121da 100644
--- a/data/deprecations/14-3-repository-push-audit-events.yml
+++ b/data/deprecations/14-3-repository-push-audit-events.yml
@@ -1,4 +1,4 @@
-- name: "Audit events for repository push events"
+- title: "Audit events for repository push events"
announcement_milestone: "14.3" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-09-22" # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69024
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-3-serverless.yml b/data/deprecations/14-3-serverless.yml
index 08a54046f4a..55a702c4d27 100644
--- a/data/deprecations/14-3-serverless.yml
+++ b/data/deprecations/14-3-serverless.yml
@@ -1,4 +1,4 @@
-- name: "GitLab Serverless"
+- title: "GitLab Serverless"
announcement_milestone: "14.3"
announcement_date: "2021-09-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-5-certificate-based-integration-with-kubernetes-saas.yml b/data/deprecations/14-5-certificate-based-integration-with-kubernetes-saas.yml
index 559189d759b..32e9f61db0c 100644
--- a/data/deprecations/14-5-certificate-based-integration-with-kubernetes-saas.yml
+++ b/data/deprecations/14-5-certificate-based-integration-with-kubernetes-saas.yml
@@ -1,4 +1,4 @@
-- name: "SaaS certificate-based integration with Kubernetes"
+- title: "SaaS certificate-based integration with Kubernetes"
announcement_milestone: "14.5"
announcement_date: "2021-11-15"
removal_milestone: "15.9"
diff --git a/data/deprecations/14-5-certificate-based-integration-with-kubernetes.yml b/data/deprecations/14-5-certificate-based-integration-with-kubernetes.yml
index f76bd9f5424..44098f83c20 100644
--- a/data/deprecations/14-5-certificate-based-integration-with-kubernetes.yml
+++ b/data/deprecations/14-5-certificate-based-integration-with-kubernetes.yml
@@ -1,4 +1,4 @@
-- name: "Self-managed certificate-based integration with Kubernetes"
+- title: "Self-managed certificate-based integration with Kubernetes"
announcement_milestone: "14.5"
announcement_date: "2021-11-15"
removal_milestone: "17.0"
diff --git a/data/deprecations/14-5-deprecate-convert-instance-runner-to-project.yml b/data/deprecations/14-5-deprecate-convert-instance-runner-to-project.yml
index f6b47162a36..953af3634f7 100644
--- a/data/deprecations/14-5-deprecate-convert-instance-runner-to-project.yml
+++ b/data/deprecations/14-5-deprecate-convert-instance-runner-to-project.yml
@@ -1,4 +1,4 @@
-- name: "Changing an instance (shared) runner to a project (specific) runner"
+- title: "Changing an instance (shared) runner to a project (specific) runner"
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22"
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-5-deprecate-defaultMergeCommitMessageWithDescription-graphql.yml b/data/deprecations/14-5-deprecate-defaultMergeCommitMessageWithDescription-graphql.yml
index 17ce084ada6..447fb4e3ef5 100644
--- a/data/deprecations/14-5-deprecate-defaultMergeCommitMessageWithDescription-graphql.yml
+++ b/data/deprecations/14-5-deprecate-defaultMergeCommitMessageWithDescription-graphql.yml
@@ -1,4 +1,4 @@
-- name: "`defaultMergeCommitMessageWithDescription` GraphQL API field" # The name of the feature to be deprecated
+- title: "`defaultMergeCommitMessageWithDescription` GraphQL API field" # The name of the feature to be deprecated
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-5-deprecate-opensuse-15-2.yml b/data/deprecations/14-5-deprecate-opensuse-15-2.yml
index 877fcf7e3a6..abc30980c7f 100644
--- a/data/deprecations/14-5-deprecate-opensuse-15-2.yml
+++ b/data/deprecations/14-5-deprecate-opensuse-15-2.yml
@@ -1,4 +1,4 @@
-- name: "openSUSE Leap 15.2 packages" # The name of the feature to be deprecated
+- title: "openSUSE Leap 15.2 packages" # The name of the feature to be deprecated
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "14.8" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-5-deprecate-sles-12sp2.yml b/data/deprecations/14-5-deprecate-sles-12sp2.yml
index adfbea4ce42..cc5bef6f203 100644
--- a/data/deprecations/14-5-deprecate-sles-12sp2.yml
+++ b/data/deprecations/14-5-deprecate-sles-12sp2.yml
@@ -1,4 +1,4 @@
-- name: "Support for SLES 12 SP2" # The name of the feature to be deprecated
+- title: "Support for SLES 12 SP2" # The name of the feature to be deprecated
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22"
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-5-deprecation-versions-packagetype.yml b/data/deprecations/14-5-deprecation-versions-packagetype.yml
index da6e705b94f..110bb9f218f 100644
--- a/data/deprecations/14-5-deprecation-versions-packagetype.yml
+++ b/data/deprecations/14-5-deprecation-versions-packagetype.yml
@@ -1,4 +1,4 @@
-- name: "`Versions` on base `PackageType`"
+- title: "`Versions` on base `PackageType`"
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-5-deprecation-vsa-announce-deprecation-of-vsa-filtering-calculation.yml b/data/deprecations/14-5-deprecation-vsa-announce-deprecation-of-vsa-filtering-calculation.yml
index 0ba7b828f8d..10ed8ff8ad6 100644
--- a/data/deprecations/14-5-deprecation-vsa-announce-deprecation-of-vsa-filtering-calculation.yml
+++ b/data/deprecations/14-5-deprecation-vsa-announce-deprecation-of-vsa-filtering-calculation.yml
@@ -1,4 +1,4 @@
-- name: "Value Stream Analytics filtering calculation change" # The name of the feature to be deprecated
+- title: "Value Stream Analytics filtering calculation change" # The name of the feature to be deprecated
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-5-disable_strict_host_key_checking.yml b/data/deprecations/14-5-disable_strict_host_key_checking.yml
index b2091578ff4..c5677dd8c3c 100644
--- a/data/deprecations/14-5-disable_strict_host_key_checking.yml
+++ b/data/deprecations/14-5-disable_strict_host_key_checking.yml
@@ -1,4 +1,4 @@
-- name: "Known host required for GitLab Runner SSH executor"
+- title: "Known host required for GitLab Runner SSH executor"
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22"
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-5-geo-deprecate-promote-db.yml b/data/deprecations/14-5-geo-deprecate-promote-db.yml
index 0dbd785c1a0..4aedc7a8876 100644
--- a/data/deprecations/14-5-geo-deprecate-promote-db.yml
+++ b/data/deprecations/14-5-geo-deprecate-promote-db.yml
@@ -1,4 +1,4 @@
-- name: "`promote-db` command from `gitlab-ctl`" # The name of the feature to be deprecated
+- title: "`promote-db` command from `gitlab-ctl`" # The name of the feature to be deprecated
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-5-geo-deprecate-promote-to-primary-node.yml b/data/deprecations/14-5-geo-deprecate-promote-to-primary-node.yml
index 69034bcd33d..048a78aedc7 100644
--- a/data/deprecations/14-5-geo-deprecate-promote-to-primary-node.yml
+++ b/data/deprecations/14-5-geo-deprecate-promote-to-primary-node.yml
@@ -1,4 +1,4 @@
-- name: "`promote-to-primary-node` command from `gitlab-ctl`" # The name of the feature to be deprecated
+- title: "`promote-to-primary-node` command from `gitlab-ctl`" # The name of the feature to be deprecated
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-5-package-container-registry-api-group-update.yml b/data/deprecations/14-5-package-container-registry-api-group-update.yml
index 67e3557cae3..40174c02e50 100644
--- a/data/deprecations/14-5-package-container-registry-api-group-update.yml
+++ b/data/deprecations/14-5-package-container-registry-api-group-update.yml
@@ -1,4 +1,4 @@
-- name: "Update to the Container Registry group-level API"
+- title: "Update to the Container Registry group-level API"
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22"
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-5-remove-dependency-proxy-permissions-flag.yml b/data/deprecations/14-5-remove-dependency-proxy-permissions-flag.yml
index 2229637f527..0d9fe9c9539 100644
--- a/data/deprecations/14-5-remove-dependency-proxy-permissions-flag.yml
+++ b/data/deprecations/14-5-remove-dependency-proxy-permissions-flag.yml
@@ -1,4 +1,4 @@
-- name: "`dependency_proxy_for_private_groups` feature flag" # The name of the feature to be deprecated
+- title: "`dependency_proxy_for_private_groups` feature flag" # The name of the feature to be deprecated
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-5-remove-package-pipelines-api.yml b/data/deprecations/14-5-remove-package-pipelines-api.yml
index 91b2971297f..0cf2201d6e3 100644
--- a/data/deprecations/14-5-remove-package-pipelines-api.yml
+++ b/data/deprecations/14-5-remove-package-pipelines-api.yml
@@ -1,4 +1,4 @@
-- name: "Package pipelines in API payload is paginated" # The name of the feature to be deprecated
+- title: "Package pipelines in API payload is paginated" # The name of the feature to be deprecated
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-5-remove-pipelines-from-version-field.yml b/data/deprecations/14-5-remove-pipelines-from-version-field.yml
index 98174daa26c..0470bba35cc 100644
--- a/data/deprecations/14-5-remove-pipelines-from-version-field.yml
+++ b/data/deprecations/14-5-remove-pipelines-from-version-field.yml
@@ -1,4 +1,4 @@
-- name: "`pipelines` field from the `version` field" # The name of the feature to be deprecated
+- title: "`pipelines` field from the `version` field" # The name of the feature to be deprecated
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-5-runner-api-status-does-contain-paused.yml b/data/deprecations/14-5-runner-api-status-does-contain-paused.yml
index 41124638509..9cf1294467a 100644
--- a/data/deprecations/14-5-runner-api-status-does-contain-paused.yml
+++ b/data/deprecations/14-5-runner-api-status-does-contain-paused.yml
@@ -1,8 +1,8 @@
-- name: "GraphQL API Runner status will not return `paused`"
+- title: "GraphQL API Runner status will not return `paused`"
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22"
removal_milestone: "16.0" # the milestone when this feature is planned to be removed
- removal_date: "2023-04-22" # the date of the milestone release when this feature is planned to be removed
+ removal_date: "2023-05-22" # the date of the milestone release when this feature is planned to be removed
breaking_change: true
body: | # Do not modify this line, instead modify the lines below.
The GitLab Runner GraphQL API endpoints will not return `paused` or `active` as a status in GitLab 16.0.
diff --git a/data/deprecations/14-6-Enforce-validation-of-security-schemas.yml b/data/deprecations/14-6-Enforce-validation-of-security-schemas.yml
index b80fd45239e..614a1bc73d5 100644
--- a/data/deprecations/14-6-Enforce-validation-of-security-schemas.yml
+++ b/data/deprecations/14-6-Enforce-validation-of-security-schemas.yml
@@ -1,4 +1,4 @@
-- name: "Enforced validation of security report schemas" # The name of the feature to be deprecated
+- title: "Enforced validation of security report schemas" # The name of the feature to be deprecated
announcement_milestone: "14.7" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-01-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-6-container-scanning-schemas-below-14.yml b/data/deprecations/14-6-container-scanning-schemas-below-14.yml
index 9427c9f0e88..244c5662b25 100644
--- a/data/deprecations/14-6-container-scanning-schemas-below-14.yml
+++ b/data/deprecations/14-6-container-scanning-schemas-below-14.yml
@@ -1,4 +1,4 @@
-- name: "Container scanning schemas below 14.0.0" # The name of the feature to be deprecated
+- title: "Container scanning schemas below 14.0.0" # The name of the feature to be deprecated
announcement_milestone: "14.7" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-01-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-6-coverage-fuzzing-schemas-below-14.yml b/data/deprecations/14-6-coverage-fuzzing-schemas-below-14.yml
index 78d076294ed..dcb643d5787 100644
--- a/data/deprecations/14-6-coverage-fuzzing-schemas-below-14.yml
+++ b/data/deprecations/14-6-coverage-fuzzing-schemas-below-14.yml
@@ -1,4 +1,4 @@
-- name: "Coverage guided fuzzing schemas below 14.0.0" # The name of the feature to be deprecated
+- title: "Coverage guided fuzzing schemas below 14.0.0" # The name of the feature to be deprecated
announcement_milestone: "14.7" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-01-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-6-dast-schemas-below-14.yml b/data/deprecations/14-6-dast-schemas-below-14.yml
index 305cf9469f9..8ff3d899951 100644
--- a/data/deprecations/14-6-dast-schemas-below-14.yml
+++ b/data/deprecations/14-6-dast-schemas-below-14.yml
@@ -1,4 +1,4 @@
-- name: "DAST schemas below 14.0.0" # The name of the feature to be deprecated
+- title: "DAST schemas below 14.0.0" # The name of the feature to be deprecated
announcement_milestone: "14.7" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-01-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-6-dependency-scanning-schemas-below-14.yml b/data/deprecations/14-6-dependency-scanning-schemas-below-14.yml
index f04130bc68f..5255779f5a8 100644
--- a/data/deprecations/14-6-dependency-scanning-schemas-below-14.yml
+++ b/data/deprecations/14-6-dependency-scanning-schemas-below-14.yml
@@ -1,4 +1,4 @@
-- name: "Dependency scanning schemas below 14.0.0" # The name of the feature to be deprecated
+- title: "Dependency scanning schemas below 14.0.0" # The name of the feature to be deprecated
announcement_milestone: "14.7" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-01-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-6-deprecate-types.yml b/data/deprecations/14-6-deprecate-types.yml
index 7f6c1a4cfa5..c2afcc8226d 100644
--- a/data/deprecations/14-6-deprecate-types.yml
+++ b/data/deprecations/14-6-deprecate-types.yml
@@ -1,4 +1,4 @@
-- name: "`type` and `types` keyword in CI/CD configuration" # The name of the feature to be deprecated
+- title: "`type` and `types` keyword in CI/CD configuration" # The name of the feature to be deprecated
announcement_milestone: "14.6" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-12-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-6-deprecation-license-compliance-api-terms.yml b/data/deprecations/14-6-deprecation-license-compliance-api-terms.yml
index 344d1a04a4b..03bfd5e1a7b 100644
--- a/data/deprecations/14-6-deprecation-license-compliance-api-terms.yml
+++ b/data/deprecations/14-6-deprecation-license-compliance-api-terms.yml
@@ -1,4 +1,4 @@
-- name: "Legacy approval status names from License Compliance API" # The name of the feature to be deprecated
+- title: "Legacy approval status names from License Compliance API" # The name of the feature to be deprecated
announcement_milestone: "14.6" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-12-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-6-deprecation-secure-dependency-scanning-bundler-audit.yml b/data/deprecations/14-6-deprecation-secure-dependency-scanning-bundler-audit.yml
index 72039930348..5321c23e797 100644
--- a/data/deprecations/14-6-deprecation-secure-dependency-scanning-bundler-audit.yml
+++ b/data/deprecations/14-6-deprecation-secure-dependency-scanning-bundler-audit.yml
@@ -1,4 +1,4 @@
-- name: "bundler-audit Dependency Scanning tool" # The name of the feature to be deprecated
+- title: "bundler-audit Dependency Scanning tool" # The name of the feature to be deprecated
announcement_milestone: "14.6" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-12-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-6-job_char_limit.yml b/data/deprecations/14-6-job_char_limit.yml
index b7dff12da5f..a1422f57506 100644
--- a/data/deprecations/14-6-job_char_limit.yml
+++ b/data/deprecations/14-6-job_char_limit.yml
@@ -1,4 +1,4 @@
-- name: "CI/CD job name length limit" # The name of the feature to be deprecated
+- title: "CI/CD job name length limit" # The name of the feature to be deprecated
announcement_milestone: "14.6" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-12-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-6-remove-api-fuzzing-ci-configuration-create-mutation.yml b/data/deprecations/14-6-remove-api-fuzzing-ci-configuration-create-mutation.yml
index bdf7a64700b..639c48f7302 100644
--- a/data/deprecations/14-6-remove-api-fuzzing-ci-configuration-create-mutation.yml
+++ b/data/deprecations/14-6-remove-api-fuzzing-ci-configuration-create-mutation.yml
@@ -1,4 +1,4 @@
-- name: "apiFuzzingCiConfigurationCreate GraphQL mutation"
+- title: "apiFuzzingCiConfigurationCreate GraphQL mutation"
announcement_milestone: "14.6"
announcement_date: "2021-12-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-6-sast-schemas-below-14.yml b/data/deprecations/14-6-sast-schemas-below-14.yml
index 635eaa3624b..9afab73e316 100644
--- a/data/deprecations/14-6-sast-schemas-below-14.yml
+++ b/data/deprecations/14-6-sast-schemas-below-14.yml
@@ -1,4 +1,4 @@
-- name: "SAST schemas below 14.0.0" # The name of the feature to be deprecated
+- title: "SAST schemas below 14.0.0" # The name of the feature to be deprecated
announcement_milestone: "14.7" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-01-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-6-secret-detection-schemas-below-14.yml b/data/deprecations/14-6-secret-detection-schemas-below-14.yml
index 16af233df44..53c9cf89795 100644
--- a/data/deprecations/14-6-secret-detection-schemas-below-14.yml
+++ b/data/deprecations/14-6-secret-detection-schemas-below-14.yml
@@ -1,4 +1,4 @@
-- name: "Secret detection schemas below 14.0.0" # The name of the feature to be deprecated
+- title: "Secret detection schemas below 14.0.0" # The name of the feature to be deprecated
announcement_milestone: "14.7" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-01-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-7-deprecate-artifacts-keyword.yml b/data/deprecations/14-7-deprecate-artifacts-keyword.yml
index 26fd6b485df..e4c82ebcc24 100644
--- a/data/deprecations/14-7-deprecate-artifacts-keyword.yml
+++ b/data/deprecations/14-7-deprecate-artifacts-keyword.yml
@@ -1,4 +1,4 @@
-- name: "`artifacts:reports:cobertura` keyword"
+- title: "`artifacts:reports:cobertura` keyword"
announcement_milestone: "14.7"
announcement_date: "2022-01-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-7-deprecate-godep-support-in-license-compliance.yml b/data/deprecations/14-7-deprecate-godep-support-in-license-compliance.yml
index 9d0e2aa91dc..080853e6f1e 100644
--- a/data/deprecations/14-7-deprecate-godep-support-in-license-compliance.yml
+++ b/data/deprecations/14-7-deprecate-godep-support-in-license-compliance.yml
@@ -1,4 +1,4 @@
-- name: "Godep support in License Compliance" # The name of the feature to be deprecated
+- title: "Godep support in License Compliance" # The name of the feature to be deprecated
announcement_milestone: "14.7" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-01-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-7-deprecate-merged_by-api-field.yml b/data/deprecations/14-7-deprecate-merged_by-api-field.yml
index 561f3d5360e..623a544052a 100644
--- a/data/deprecations/14-7-deprecate-merged_by-api-field.yml
+++ b/data/deprecations/14-7-deprecate-merged_by-api-field.yml
@@ -10,7 +10,7 @@
#
# Please delete this line and above before submitting your merge request.
-- name: "merged_by API field" # The name of the feature to be deprecated
+- title: "merged_by API field" # The name of the feature to be deprecated
announcement_milestone: "14.7" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-01-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-7-deprecate-static-site-editor.yml b/data/deprecations/14-7-deprecate-static-site-editor.yml
index 7d1d324c0ca..f44f4f3f256 100644
--- a/data/deprecations/14-7-deprecate-static-site-editor.yml
+++ b/data/deprecations/14-7-deprecate-static-site-editor.yml
@@ -1,4 +1,4 @@
-- name: "Static Site Editor" # The name of the feature to be deprecated
+- title: "Static Site Editor" # The name of the feature to be deprecated
announcement_milestone: "14.7" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-01-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-7-pseudonymizer.yml b/data/deprecations/14-7-pseudonymizer.yml
index f278cd506e2..43987332210 100644
--- a/data/deprecations/14-7-pseudonymizer.yml
+++ b/data/deprecations/14-7-pseudonymizer.yml
@@ -1,4 +1,4 @@
-- name: "Pseudonymizer" # The name of the feature to be deprecated
+- title: "Pseudonymizer" # The name of the feature to be deprecated
announcement_milestone: "14.7" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-01-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-7-sidekiq-metrics-health-check-donfig.yml b/data/deprecations/14-7-sidekiq-metrics-health-check-donfig.yml
index 1faec65e9ef..324339a45ea 100644
--- a/data/deprecations/14-7-sidekiq-metrics-health-check-donfig.yml
+++ b/data/deprecations/14-7-sidekiq-metrics-health-check-donfig.yml
@@ -1,4 +1,4 @@
-- name: "Sidekiq metrics and health checks configuration"
+- title: "Sidekiq metrics and health checks configuration"
announcement_milestone: "14.7"
announcement_date: "2021-01-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-8-Elasticsearch-6-8.yml b/data/deprecations/14-8-Elasticsearch-6-8.yml
index e52e237ffbc..d9b7f607f27 100644
--- a/data/deprecations/14-8-Elasticsearch-6-8.yml
+++ b/data/deprecations/14-8-Elasticsearch-6-8.yml
@@ -1,4 +1,4 @@
-- name: "Elasticsearch 6.8"
+- title: "Elasticsearch 6.8"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-8-ci-build-variables.yml b/data/deprecations/14-8-ci-build-variables.yml
index dba841841f2..f12f6dda5a6 100644
--- a/data/deprecations/14-8-ci-build-variables.yml
+++ b/data/deprecations/14-8-ci-build-variables.yml
@@ -1,8 +1,8 @@
-- name: "`CI_BUILD_*` predefined variables"
+- title: "`CI_BUILD_*` predefined variables"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "16.0"
- removal_date: "2023-04-22"
+ removal_date: "2023-05-22"
breaking_change: true
reporter: dhershkovitch
body: |
diff --git a/data/deprecations/14-8-compliance-required-pipeline-configuration-premium.yml b/data/deprecations/14-8-compliance-required-pipeline-configuration-premium.yml
index aabd330d567..1b262c98d40 100644
--- a/data/deprecations/14-8-compliance-required-pipeline-configuration-premium.yml
+++ b/data/deprecations/14-8-compliance-required-pipeline-configuration-premium.yml
@@ -1,4 +1,4 @@
-- name: "Required pipeline configurations in Premium tier"
+- title: "Required pipeline configurations in Premium tier"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-8-compliance-status-check-api-field.yml b/data/deprecations/14-8-compliance-status-check-api-field.yml
index ba8c84343bd..6493905a6d5 100644
--- a/data/deprecations/14-8-compliance-status-check-api-field.yml
+++ b/data/deprecations/14-8-compliance-status-check-api-field.yml
@@ -1,4 +1,4 @@
-- name: "External status check API breaking changes"
+- title: "External status check API breaking changes"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-8-deprecate-projectFingerprint-from-PipelineSecurityReportFinding-GraphQL.yml b/data/deprecations/14-8-deprecate-projectFingerprint-from-PipelineSecurityReportFinding-GraphQL.yml
index 36104ad15d8..f2fd11ceb28 100644
--- a/data/deprecations/14-8-deprecate-projectFingerprint-from-PipelineSecurityReportFinding-GraphQL.yml
+++ b/data/deprecations/14-8-deprecate-projectFingerprint-from-PipelineSecurityReportFinding-GraphQL.yml
@@ -1,4 +1,4 @@
-- name: "`projectFingerprint` in `PipelineSecurityReportFinding` GraphQL" # The name of the feature to be deprecated
+- title: "`projectFingerprint` in `PipelineSecurityReportFinding` GraphQL" # The name of the feature to be deprecated
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-8-deprecation-secure-dependency-scanning-retire-js.yml b/data/deprecations/14-8-deprecation-secure-dependency-scanning-retire-js.yml
index 3713ae532b3..070262707a9 100644
--- a/data/deprecations/14-8-deprecation-secure-dependency-scanning-retire-js.yml
+++ b/data/deprecations/14-8-deprecation-secure-dependency-scanning-retire-js.yml
@@ -1,4 +1,4 @@
-- name: "Retire-JS Dependency Scanning tool" # The name of the feature to be deprecated
+- title: "Retire-JS Dependency Scanning tool" # The name of the feature to be deprecated
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-8-enforce-pat-expiration.yml b/data/deprecations/14-8-enforce-pat-expiration.yml
index bc8e35a4e2c..6f165bbdb19 100644
--- a/data/deprecations/14-8-enforce-pat-expiration.yml
+++ b/data/deprecations/14-8-enforce-pat-expiration.yml
@@ -1,4 +1,4 @@
-- name: "Optional enforcement of PAT expiration" # The name of the feature to be deprecated
+- title: "Optional enforcement of PAT expiration" # The name of the feature to be deprecated
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-8-enforce-ssh-expiration.yml b/data/deprecations/14-8-enforce-ssh-expiration.yml
index 5887e749d2c..d772adf1b4c 100644
--- a/data/deprecations/14-8-enforce-ssh-expiration.yml
+++ b/data/deprecations/14-8-enforce-ssh-expiration.yml
@@ -1,4 +1,4 @@
-- name: "Optional enforcement of SSH expiration" # The name of the feature to be deprecated
+- title: "Optional enforcement of SSH expiration" # The name of the feature to be deprecated
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-8-geo-deprecate-db-rake-tasks.yml b/data/deprecations/14-8-geo-deprecate-db-rake-tasks.yml
index d38e579e3e6..0390632968a 100644
--- a/data/deprecations/14-8-geo-deprecate-db-rake-tasks.yml
+++ b/data/deprecations/14-8-geo-deprecate-db-rake-tasks.yml
@@ -1,4 +1,4 @@
-- name: "Deprecate custom Geo:db:* Rake tasks"
+- title: "Deprecate custom Geo:db:* Rake tasks"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-8-geo-deprecate-replication-detail-routes.yml b/data/deprecations/14-8-geo-deprecate-replication-detail-routes.yml
index c28dc849f72..c4a4117f0cd 100644
--- a/data/deprecations/14-8-geo-deprecate-replication-detail-routes.yml
+++ b/data/deprecations/14-8-geo-deprecate-replication-detail-routes.yml
@@ -1,4 +1,4 @@
-- name: "Deprecate Geo Admin UI Routes"
+- title: "Deprecate Geo Admin UI Routes"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-8-gitaly-deprecate-legacy-config-options.yml b/data/deprecations/14-8-gitaly-deprecate-legacy-config-options.yml
index 861bed1e976..0273559a268 100644
--- a/data/deprecations/14-8-gitaly-deprecate-legacy-config-options.yml
+++ b/data/deprecations/14-8-gitaly-deprecate-legacy-config-options.yml
@@ -1,4 +1,4 @@
-- name: "Deprecate legacy Gitaly configuration methods" # The name of the feature to be deprecated
+- title: "Deprecate legacy Gitaly configuration methods" # The name of the feature to be deprecated
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-8-gitaly-remove-per-repository-election.yml b/data/deprecations/14-8-gitaly-remove-per-repository-election.yml
index 7edaff9821f..88412780048 100644
--- a/data/deprecations/14-8-gitaly-remove-per-repository-election.yml
+++ b/data/deprecations/14-8-gitaly-remove-per-repository-election.yml
@@ -1,4 +1,4 @@
-- name: "Configurable Gitaly `per_repository` election strategy" # The name of the feature to be deprecated
+- title: "Configurable Gitaly `per_repository` election strategy" # The name of the feature to be deprecated
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "14.9" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-8-graphql-ids.yml b/data/deprecations/14-8-graphql-ids.yml
index 899ff323792..599748652f6 100644
--- a/data/deprecations/14-8-graphql-ids.yml
+++ b/data/deprecations/14-8-graphql-ids.yml
@@ -1,8 +1,8 @@
-- name: "GraphQL ID and GlobalID compatibility"
+- title: "GraphQL ID and GlobalID compatibility"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
- removal_date: "2022-04-22"
+ removal_date: "2022-05-22"
breaking_change: true
reporter: alexkalderimis
body: | # Do not modify this line, instead modify the lines below.
diff --git a/data/deprecations/14-8-grpc-proxy.yml b/data/deprecations/14-8-grpc-proxy.yml
index 9d161dbc8f9..9f7a044d154 100644
--- a/data/deprecations/14-8-grpc-proxy.yml
+++ b/data/deprecations/14-8-grpc-proxy.yml
@@ -1,4 +1,4 @@
-- name: "Support for gRPC-aware proxy deployed between Gitaly and rest of GitLab"
+- title: "Support for gRPC-aware proxy deployed between Gitaly and rest of GitLab"
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-8-iteration-started-field.yml b/data/deprecations/14-8-iteration-started-field.yml
index e323b93752b..a4401632682 100644
--- a/data/deprecations/14-8-iteration-started-field.yml
+++ b/data/deprecations/14-8-iteration-started-field.yml
@@ -1,4 +1,4 @@
-- name: "`started` iterations API field" # The name of the feature to be deprecated
+- title: "`started` iterations API field" # The name of the feature to be deprecated
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-8-protect-cns-chs.yml b/data/deprecations/14-8-protect-cns-chs.yml
index 6fb67e98b2c..098b571f9fb 100644
--- a/data/deprecations/14-8-protect-cns-chs.yml
+++ b/data/deprecations/14-8-protect-cns-chs.yml
@@ -1,4 +1,4 @@
-- name: "Container Network and Host Security"
+- title: "Container Network and Host Security"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-8-protect-vulnerability-check.yml b/data/deprecations/14-8-protect-vulnerability-check.yml
index ddb296886b2..737756ae8ac 100644
--- a/data/deprecations/14-8-protect-vulnerability-check.yml
+++ b/data/deprecations/14-8-protect-vulnerability-check.yml
@@ -1,4 +1,4 @@
-- name: "Vulnerability Check"
+- title: "Vulnerability Check"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-8-remove_ff_push_rules_supersede_code_owners.yml b/data/deprecations/14-8-remove_ff_push_rules_supersede_code_owners.yml
index 4029443cc76..24e192e7f33 100644
--- a/data/deprecations/14-8-remove_ff_push_rules_supersede_code_owners.yml
+++ b/data/deprecations/14-8-remove_ff_push_rules_supersede_code_owners.yml
@@ -1,4 +1,4 @@
-- name: "Deprecate feature flag PUSH_RULES_SUPERSEDE_CODE_OWNERS" # The name of the feature to be deprecated
+- title: "Deprecate feature flag PUSH_RULES_SUPERSEDE_CODE_OWNERS" # The name of the feature to be deprecated
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
@@ -6,7 +6,7 @@
breaking_change: true # If this deprecation is a breaking change, set this value to true
reporter: sarahwaldner # GitLab username of the person reporting the deprecation
body: | # Do not modify this line, instead modify the lines below.
- The feature flag `PUSH_RULES_SUPERSEDE_CODE_OWNERS` is being removed in GitLab 15.0. Upon its removal, push rules will supersede CODEOWNERS. The CODEOWNERS feature will no longer be available for access control.
+ The feature flag `PUSH_RULES_SUPERSEDE_CODE_OWNERS` is being removed in GitLab 15.0. Upon its removal, push rules will supersede Code Owners. Even if Code Owner approval is required, a push rule that explicitly allows a specific user to push code supersedes the Code Owners setting.
# The following items are not published on the docs page, but may be used in the future.
stage: create # (optional - may be required in the future) String value of the stage that the feature was created in. e.g., Growth
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
diff --git a/data/deprecations/14-8-request-profiling.yml b/data/deprecations/14-8-request-profiling.yml
index e6c20abf2b1..eb7ab356d27 100644
--- a/data/deprecations/14-8-request-profiling.yml
+++ b/data/deprecations/14-8-request-profiling.yml
@@ -1,4 +1,4 @@
-- name: "Request profiling"
+- title: "Request profiling"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-8-runner-api-active-field-replaced-with-paused-breaking-change.yml b/data/deprecations/14-8-runner-api-active-field-replaced-with-paused-breaking-change.yml
index 223c1439191..a8173a80047 100644
--- a/data/deprecations/14-8-runner-api-active-field-replaced-with-paused-breaking-change.yml
+++ b/data/deprecations/14-8-runner-api-active-field-replaced-with-paused-breaking-change.yml
@@ -1,8 +1,8 @@
-- name: "REST and GraphQL API Runner usage of `active` replaced by `paused`"
+- title: "REST and GraphQL API Runner usage of `active` replaced by `paused`"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "16.0"
- removal_date: "2023-04-22"
+ removal_date: "2023-05-22"
breaking_change: true
reporter: pedropombeiro
body: |
diff --git a/data/deprecations/14-8-runner-api-status-filter-does-accept-active-or-paused.yml b/data/deprecations/14-8-runner-api-status-filter-does-accept-active-or-paused.yml
index 376a27c8863..cf0476ae768 100644
--- a/data/deprecations/14-8-runner-api-status-filter-does-accept-active-or-paused.yml
+++ b/data/deprecations/14-8-runner-api-status-filter-does-accept-active-or-paused.yml
@@ -1,8 +1,8 @@
-- name: "GraphQL API Runner will not accept `status` filter values of `active` or `paused`"
+- title: "GraphQL API Runner will not accept `status` filter values of `active` or `paused`"
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22"
removal_milestone: "16.0"
- removal_date: "2023-04-22"
+ removal_date: "2023-05-22"
breaking_change: true
body: | # Do not modify this line, instead modify the lines below.
The GitLab Runner GraphQL endpoints will stop accepting `paused` or `active` as a status value in GitLab 16.0.
diff --git a/data/deprecations/14-8-sast-analyzer-removals.yml b/data/deprecations/14-8-sast-analyzer-removals.yml
index 4c4675be808..0ad3920669a 100644
--- a/data/deprecations/14-8-sast-analyzer-removals.yml
+++ b/data/deprecations/14-8-sast-analyzer-removals.yml
@@ -1,4 +1,4 @@
-- name: "SAST analyzer consolidation and CI/CD template changes"
+- title: "SAST analyzer consolidation and CI/CD template changes"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.4"
diff --git a/data/deprecations/14-8-sast-dotnet-21.yml b/data/deprecations/14-8-sast-dotnet-21.yml
index ab1b3c16b23..1a09329244d 100644
--- a/data/deprecations/14-8-sast-dotnet-21.yml
+++ b/data/deprecations/14-8-sast-dotnet-21.yml
@@ -1,4 +1,4 @@
-- name: "SAST support for .NET 2.1"
+- title: "SAST support for .NET 2.1"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-8-sast-secret-analyzer-image.yml b/data/deprecations/14-8-sast-secret-analyzer-image.yml
index cc7e559722d..e774941ace4 100644
--- a/data/deprecations/14-8-sast-secret-analyzer-image.yml
+++ b/data/deprecations/14-8-sast-secret-analyzer-image.yml
@@ -1,4 +1,4 @@
-- name: "Secure and Protect analyzer images published in new location"
+- title: "Secure and Protect analyzer images published in new location"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-8-sast-spotbugs-java-8.yml b/data/deprecations/14-8-sast-spotbugs-java-8.yml
index 3e3672eeb63..58d1e205aad 100644
--- a/data/deprecations/14-8-sast-spotbugs-java-8.yml
+++ b/data/deprecations/14-8-sast-spotbugs-java-8.yml
@@ -1,4 +1,4 @@
-- name: "Out-of-the-box SAST support for Java 8"
+- title: "Out-of-the-box SAST support for Java 8"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-8-secret-detection-configurations.yml b/data/deprecations/14-8-secret-detection-configurations.yml
index f9103c1cea3..8078d7c6fa0 100644
--- a/data/deprecations/14-8-secret-detection-configurations.yml
+++ b/data/deprecations/14-8-secret-detection-configurations.yml
@@ -1,4 +1,4 @@
-- name: "Secret Detection configuration variables deprecated"
+- title: "Secret Detection configuration variables deprecated"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-8-secure-and-protect-analyzer-bump.yml b/data/deprecations/14-8-secure-and-protect-analyzer-bump.yml
index dafa8b4cdc7..e1616cd1f6e 100644
--- a/data/deprecations/14-8-secure-and-protect-analyzer-bump.yml
+++ b/data/deprecations/14-8-secure-and-protect-analyzer-bump.yml
@@ -1,4 +1,4 @@
-- name: "Secure and Protect analyzer major version update" # The name of the feature to be deprecated
+- title: "Secure and Protect analyzer major version update" # The name of the feature to be deprecated
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-8-secure-ca-python-deprecation.yml b/data/deprecations/14-8-secure-ca-python-deprecation.yml
index 21b3b01a590..fab7455fe1c 100644
--- a/data/deprecations/14-8-secure-ca-python-deprecation.yml
+++ b/data/deprecations/14-8-secure-ca-python-deprecation.yml
@@ -1,4 +1,4 @@
-- name: "Dependency Scanning Python 3.9 and 3.6 image deprecation" # The name of the feature to be deprecated
+- title: "Dependency Scanning Python 3.9 and 3.6 image deprecation" # The name of the feature to be deprecated
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-02-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-9-background-upload.yml b/data/deprecations/14-9-background-upload.yml
index ae9b1bd9a4a..e336cb7d39c 100644
--- a/data/deprecations/14-9-background-upload.yml
+++ b/data/deprecations/14-9-background-upload.yml
@@ -1,4 +1,4 @@
-- name: "Background upload for object storage"
+- title: "Background upload for object storage"
announcement_milestone: "14.9"
announcement_date: "2022-03-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-9-deprecate-composer-download-permissions.yml b/data/deprecations/14-9-deprecate-composer-download-permissions.yml
index e44f1c55167..47e1502c466 100644
--- a/data/deprecations/14-9-deprecate-composer-download-permissions.yml
+++ b/data/deprecations/14-9-deprecate-composer-download-permissions.yml
@@ -1,4 +1,4 @@
-- name: "Permissions change for downloading Composer dependencies"
+- title: "Permissions change for downloading Composer dependencies"
announcement_milestone: "14.9"
announcement_date: "2022-03-22"
removal_milestone: "14.10"
diff --git a/data/deprecations/14-9-deprecate-debian-9.yml b/data/deprecations/14-9-deprecate-debian-9.yml
index 3dce4892ab7..7057ceb4176 100644
--- a/data/deprecations/14-9-deprecate-debian-9.yml
+++ b/data/deprecations/14-9-deprecate-debian-9.yml
@@ -1,4 +1,4 @@
-- name: "Deprecate support for Debian 9"
+- title: "Deprecate support for Debian 9"
announcement_milestone: "14.9"
announcement_date: "2022-03-22"
removal_milestone: "15.1"
diff --git a/data/deprecations/14-9-deprecate-permissions-change-package-settings.yml b/data/deprecations/14-9-deprecate-permissions-change-package-settings.yml
index 366f0e83470..9e6da8c8776 100644
--- a/data/deprecations/14-9-deprecate-permissions-change-package-settings.yml
+++ b/data/deprecations/14-9-deprecate-permissions-change-package-settings.yml
@@ -1,4 +1,4 @@
-- name: "GraphQL permissions change for Package settings"
+- title: "GraphQL permissions change for Package settings"
announcement_milestone: "14.9"
announcement_date: "2022-03-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-9-deprecate-testcoveragesetting.yml b/data/deprecations/14-9-deprecate-testcoveragesetting.yml
index e6dfb22562b..9532a34b568 100644
--- a/data/deprecations/14-9-deprecate-testcoveragesetting.yml
+++ b/data/deprecations/14-9-deprecate-testcoveragesetting.yml
@@ -1,4 +1,4 @@
-- name: "Test coverage project CI/CD setting" # The name of the feature to be deprecated
+- title: "Test coverage project CI/CD setting" # The name of the feature to be deprecated
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-9-deprecation-htpassword-authentication-container-registry.yml b/data/deprecations/14-9-deprecation-htpassword-authentication-container-registry.yml
index 5d940b5e14d..0090a960417 100644
--- a/data/deprecations/14-9-deprecation-htpassword-authentication-container-registry.yml
+++ b/data/deprecations/14-9-deprecation-htpassword-authentication-container-registry.yml
@@ -1,4 +1,4 @@
-- name: "htpasswd Authentication for the Container Registry"
+- title: "htpasswd Authentication for the Container Registry"
announcement_milestone: "14.9"
announcement_date: "2022-03-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-9-global-search-deprecate-user-email-lookup-limit.yml b/data/deprecations/14-9-global-search-deprecate-user-email-lookup-limit.yml
index 2b986a11d47..f69ac6e9a82 100644
--- a/data/deprecations/14-9-global-search-deprecate-user-email-lookup-limit.yml
+++ b/data/deprecations/14-9-global-search-deprecate-user-email-lookup-limit.yml
@@ -1,4 +1,4 @@
-- name: "user_email_lookup_limit API field"
+- title: "user_email_lookup_limit API field"
announcement_milestone: "14.9"
announcement_date: "2022-03-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/14-9-pages-daemon.yml b/data/deprecations/14-9-pages-daemon.yml
index a8fb5924ac6..827daf18c21 100644
--- a/data/deprecations/14-9-pages-daemon.yml
+++ b/data/deprecations/14-9-pages-daemon.yml
@@ -1,4 +1,4 @@
-- name: "GitLab Pages running as daemon" # The name of the feature to be deprecated
+- title: "GitLab Pages running as daemon" # The name of the feature to be deprecated
announcement_milestone: "14.9" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-03-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/14-9-system_monitoring.yml b/data/deprecations/14-9-system_monitoring.yml
index c4b3343bee6..10d0a6db422 100644
--- a/data/deprecations/14-9-system_monitoring.yml
+++ b/data/deprecations/14-9-system_monitoring.yml
@@ -1,4 +1,4 @@
-- name: "GitLab self-monitoring project" # The name of the feature to be deprecated
+- title: "GitLab self-monitoring project" # The name of the feature to be deprecated
announcement_milestone: "14.9" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-03-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-0-ci-cd-settings-update-mutation-renamed.yml b/data/deprecations/15-0-ci-cd-settings-update-mutation-renamed.yml
index 7f426190963..4f8fc40fd73 100644
--- a/data/deprecations/15-0-ci-cd-settings-update-mutation-renamed.yml
+++ b/data/deprecations/15-0-ci-cd-settings-update-mutation-renamed.yml
@@ -1,4 +1,4 @@
-- name: "CiCdSettingsUpdate mutation renamed to ProjectCiCdSettingsUpdate"
+- title: "CiCdSettingsUpdate mutation renamed to ProjectCiCdSettingsUpdate"
announcement_milestone: "15.0"
announcement_date: "2022-05-22"
removal_milestone: "16.0"
diff --git a/data/deprecations/15-0-deprecate-monitor-logging.yml b/data/deprecations/15-0-deprecate-monitor-logging.yml
index 8dbae6f5c4c..62f889e666f 100644
--- a/data/deprecations/15-0-deprecate-monitor-logging.yml
+++ b/data/deprecations/15-0-deprecate-monitor-logging.yml
@@ -1,4 +1,4 @@
-- name: "Logging in GitLab" # The name of the feature to be deprecated
+- title: "Logging in GitLab" # The name of the feature to be deprecated
announcement_milestone: "14.7" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-01-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-0-deprecate-monitor-metrics.yml b/data/deprecations/15-0-deprecate-monitor-metrics.yml
index 9ad78d9c01d..652845f0090 100644
--- a/data/deprecations/15-0-deprecate-monitor-metrics.yml
+++ b/data/deprecations/15-0-deprecate-monitor-metrics.yml
@@ -1,4 +1,4 @@
-- name: "Monitor performance metrics through Prometheus" # The name of the feature to be deprecated
+- title: "Monitor performance metrics through Prometheus" # The name of the feature to be deprecated
announcement_milestone: "14.7" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-01-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-0-deprecate-monitor-tracing.yml b/data/deprecations/15-0-deprecate-monitor-tracing.yml
index 3c1eef177ff..3989ea2ed68 100644
--- a/data/deprecations/15-0-deprecate-monitor-tracing.yml
+++ b/data/deprecations/15-0-deprecate-monitor-tracing.yml
@@ -1,4 +1,4 @@
-- name: "Tracing in GitLab" # The name of the feature to be deprecated
+- title: "Tracing in GitLab" # The name of the feature to be deprecated
announcement_milestone: "14.7" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-01-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-0-deprecate-postgresql-12.yml b/data/deprecations/15-0-deprecate-postgresql-12.yml
index bebfba64405..1cf463d9411 100644
--- a/data/deprecations/15-0-deprecate-postgresql-12.yml
+++ b/data/deprecations/15-0-deprecate-postgresql-12.yml
@@ -1,4 +1,4 @@
-- name: "PostgreSQL 12 deprecated"
+- title: "PostgreSQL 12 deprecated"
announcement_milestone: "15.0"
announcement_date: "2022-05-22"
removal_milestone: "16.0"
diff --git a/data/deprecations/15-0-instance-statistics-graphql-node-removal.yml b/data/deprecations/15-0-instance-statistics-graphql-node-removal.yml
index 113ccdc66ea..5d2527094f9 100644
--- a/data/deprecations/15-0-instance-statistics-graphql-node-removal.yml
+++ b/data/deprecations/15-0-instance-statistics-graphql-node-removal.yml
@@ -1,4 +1,4 @@
-- name: "Querying Usage Trends via the `instanceStatisticsMeasurements` GraphQL node"
+- title: "Querying Usage Trends via the `instanceStatisticsMeasurements` GraphQL node"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/deprecations/15-0-oauth-noexpiry.yml b/data/deprecations/15-0-oauth-noexpiry.yml
index 9f2521998aa..88ccb7702b8 100644
--- a/data/deprecations/15-0-oauth-noexpiry.yml
+++ b/data/deprecations/15-0-oauth-noexpiry.yml
@@ -1,4 +1,4 @@
-- name: "OAuth tokens without expiration" # The name of the feature to be deprecated
+- title: "OAuth tokens without expiration" # The name of the feature to be deprecated
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-0-oauth.yml b/data/deprecations/15-0-oauth.yml
index 7e1c3ff51cd..b7af6b51afc 100644
--- a/data/deprecations/15-0-oauth.yml
+++ b/data/deprecations/15-0-oauth.yml
@@ -1,4 +1,4 @@
-- name: "OAuth implicit grant" # The name of the feature to be deprecated
+- title: "OAuth implicit grant" # The name of the feature to be deprecated
announcement_milestone: "14.0" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-06-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-0-runner-status-legacy-mode.yml b/data/deprecations/15-0-runner-status-legacy-mode.yml
index ee7ac1af820..dfdbf6e5f58 100644
--- a/data/deprecations/15-0-runner-status-legacy-mode.yml
+++ b/data/deprecations/15-0-runner-status-legacy-mode.yml
@@ -1,4 +1,4 @@
-- name: "GraphQL API legacyMode argument for Runner status" # The name of the feature to be deprecated
+- title: "GraphQL API legacyMode argument for Runner status" # The name of the feature to be deprecated
announcement_milestone: "15.0" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-05-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-1-deprecate-maintainer_note.yml b/data/deprecations/15-1-deprecate-maintainer_note.yml
index 9656e62f3db..fc35244827d 100644
--- a/data/deprecations/15-1-deprecate-maintainer_note.yml
+++ b/data/deprecations/15-1-deprecate-maintainer_note.yml
@@ -1,4 +1,4 @@
-- name: "REST API Runner maintainer_note" # (required) The name of the feature to be deprecated
+- title: "REST API Runner maintainer_note" # (required) The name of the feature to be deprecated
announcement_milestone: "15.1" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-06-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-1-jira-github-enterprise-dvcs.yml b/data/deprecations/15-1-jira-github-enterprise-dvcs.yml
index d35d06e006c..69606c3dd2d 100644
--- a/data/deprecations/15-1-jira-github-enterprise-dvcs.yml
+++ b/data/deprecations/15-1-jira-github-enterprise-dvcs.yml
@@ -1,4 +1,4 @@
-- name: "Jira GitHub Enterprise DVCS integration" # The name of the feature to be deprecated
+- title: "Jira GitHub Enterprise DVCS integration" # The name of the feature to be deprecated
announcement_milestone: "15.1" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-06-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-1-pipelinesecurityreportfinding-name.yml b/data/deprecations/15-1-pipelinesecurityreportfinding-name.yml
index bc2df85c1c5..aa6b9ea06db 100644
--- a/data/deprecations/15-1-pipelinesecurityreportfinding-name.yml
+++ b/data/deprecations/15-1-pipelinesecurityreportfinding-name.yml
@@ -1,4 +1,4 @@
-- name: "PipelineSecurityReportFinding name GraphQL field" # (required) The name of the feature to be deprecated
+- title: "PipelineSecurityReportFinding name GraphQL field" # (required) The name of the feature to be deprecated
announcement_milestone: "15.1" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-06-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-1-pipelinesecurityreportfinding-projectfingerprint.yml b/data/deprecations/15-1-pipelinesecurityreportfinding-projectfingerprint.yml
index 511f691f6bb..e127a258f4f 100644
--- a/data/deprecations/15-1-pipelinesecurityreportfinding-projectfingerprint.yml
+++ b/data/deprecations/15-1-pipelinesecurityreportfinding-projectfingerprint.yml
@@ -1,4 +1,4 @@
-- name: "PipelineSecurityReportFinding projectFingerprint GraphQL field" # (required) The name of the feature to be deprecated
+- title: "PipelineSecurityReportFinding projectFingerprint GraphQL field" # (required) The name of the feature to be deprecated
announcement_milestone: "15.1" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-06-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-1-project-pipeline-securityReportFindings.yml b/data/deprecations/15-1-project-pipeline-securityReportFindings.yml
index 3d93c94770b..bb46a9b90dd 100644
--- a/data/deprecations/15-1-project-pipeline-securityReportFindings.yml
+++ b/data/deprecations/15-1-project-pipeline-securityReportFindings.yml
@@ -1,4 +1,4 @@
-- name: "project.pipeline.securityReportFindings GraphQL query" # (required) The name of the feature to be deprecated
+- title: "project.pipeline.securityReportFindings GraphQL query" # (required) The name of the feature to be deprecated
announcement_milestone: "15.1" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-06-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-2-deprecation-vulnerability-report-state-sort.yml b/data/deprecations/15-2-deprecation-vulnerability-report-state-sort.yml
index 058ff44d618..2bcf1e114b4 100644
--- a/data/deprecations/15-2-deprecation-vulnerability-report-state-sort.yml
+++ b/data/deprecations/15-2-deprecation-vulnerability-report-state-sort.yml
@@ -1,4 +1,4 @@
-- name: "Vulnerability Report sort by State" # (required) The name of the feature to be deprecated
+- title: "Vulnerability Report sort by State" # (required) The name of the feature to be deprecated
announcement_milestone: "15.0" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-05-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.3" # (required) The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-2-job_age-deprecation.yml b/data/deprecations/15-2-job_age-deprecation.yml
index b395c97447c..b550f10920e 100644
--- a/data/deprecations/15-2-job_age-deprecation.yml
+++ b/data/deprecations/15-2-job_age-deprecation.yml
@@ -17,7 +17,7 @@
#
# REQUIRED FIELDS
#
-- name: "Remove `job_age` parameter from `POST /jobs/request` Runner endpoint" # (required) The name of the feature to be deprecated
+- title: "Remove `job_age` parameter from `POST /jobs/request` Runner endpoint" # (required) The name of the feature to be deprecated
announcement_milestone: "15.2" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-07-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-3-deprecate-redis-5.yml b/data/deprecations/15-3-deprecate-redis-5.yml
index d61b1a1c475..b94539061e5 100644
--- a/data/deprecations/15-3-deprecate-redis-5.yml
+++ b/data/deprecations/15-3-deprecate-redis-5.yml
@@ -1,4 +1,4 @@
-- name: "Redis 5 deprecated" # (required) The name of the feature to be deprecated
+- title: "Redis 5 deprecated" # (required) The name of the feature to be deprecated
announcement_milestone: "15.3" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-08-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-3-deprecation-vulnerability-report-tool-sort.yml b/data/deprecations/15-3-deprecation-vulnerability-report-tool-sort.yml
index 326070667f2..a3965f7d0cb 100644
--- a/data/deprecations/15-3-deprecation-vulnerability-report-tool-sort.yml
+++ b/data/deprecations/15-3-deprecation-vulnerability-report-tool-sort.yml
@@ -1,4 +1,4 @@
-- name: "Vulnerability Report sort by Tool" # (required) The name of the feature to be deprecated
+- title: "Vulnerability Report sort by Tool" # (required) The name of the feature to be deprecated
announcement_milestone: "15.1" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-06-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.3" # (required) The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-3-omniauth-cas3.yml b/data/deprecations/15-3-omniauth-cas3.yml
index 156c89ed06b..70290a3fc51 100644
--- a/data/deprecations/15-3-omniauth-cas3.yml
+++ b/data/deprecations/15-3-omniauth-cas3.yml
@@ -1,7 +1,7 @@
#
# REQUIRED FIELDS
#
-- name: "CAS OmniAuth provider" # (required) The name of the feature to be deprecated
+- title: "CAS OmniAuth provider" # (required) The name of the feature to be deprecated
announcement_milestone: "15.3" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-08-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-3-omniauth-crowd.yml b/data/deprecations/15-3-omniauth-crowd.yml
index 1f40ae73901..e14b40a1505 100644
--- a/data/deprecations/15-3-omniauth-crowd.yml
+++ b/data/deprecations/15-3-omniauth-crowd.yml
@@ -1,7 +1,7 @@
#
# REQUIRED FIELDS
#
-- name: "Atlassian Crowd OmniAuth provider" # (required) The name of the feature to be deprecated
+- title: "Atlassian Crowd OmniAuth provider" # (required) The name of the feature to be deprecated
announcement_milestone: "15.3" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-08-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-3-pipeline_activity_limit.yml b/data/deprecations/15-3-pipeline_activity_limit.yml
index 5cb620e576e..5374e2d7972 100644
--- a/data/deprecations/15-3-pipeline_activity_limit.yml
+++ b/data/deprecations/15-3-pipeline_activity_limit.yml
@@ -17,7 +17,7 @@
#
# REQUIRED FIELDS
#
-- name: "Maximum number of active pipelines per project limit (`ci_active_pipelines`)" # (required) The name of the feature to be deprecated
+- title: "Maximum number of active pipelines per project limit (`ci_active_pipelines`)" # (required) The name of the feature to be deprecated
announcement_milestone: "15.3" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-08-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-3-vulnerabilityFindingDismiss-mutation.yml b/data/deprecations/15-3-vulnerabilityFindingDismiss-mutation.yml
index 25e8a6a2488..691196a892b 100644
--- a/data/deprecations/15-3-vulnerabilityFindingDismiss-mutation.yml
+++ b/data/deprecations/15-3-vulnerabilityFindingDismiss-mutation.yml
@@ -1,7 +1,7 @@
#
# REQUIRED FIELDS
#
-- name: "Use of `id` field in vulnerabilityFindingDismiss mutation" # (required) The name of the feature to be deprecated
+- title: "Use of `id` field in vulnerabilityFindingDismiss mutation" # (required) The name of the feature to be deprecated
announcement_milestone: "15.3" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-08-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
diff --git a/data/deprecations/15-4-confidence-field-in-graphql.yml b/data/deprecations/15-4-confidence-field-in-graphql.yml
index da3287eeff9..90e9a452a43 100644
--- a/data/deprecations/15-4-confidence-field-in-graphql.yml
+++ b/data/deprecations/15-4-confidence-field-in-graphql.yml
@@ -1,4 +1,4 @@
-- name: "Vulnerability confidence field"
+- title: "Vulnerability confidence field"
announcement_milestone: "15.4"
announcement_date: "2022-09-22"
removal_milestone: "16.0"
diff --git a/data/deprecations/15-4-create-deprecation-draft-quick-action-toggle.yml b/data/deprecations/15-4-create-deprecation-draft-quick-action-toggle.yml
index 5d4fb1b5b66..8cb2a5a0b93 100644
--- a/data/deprecations/15-4-create-deprecation-draft-quick-action-toggle.yml
+++ b/data/deprecations/15-4-create-deprecation-draft-quick-action-toggle.yml
@@ -1,8 +1,8 @@
-- name: "Toggle behavior of `/draft` quick action in merge requests" # (required) The name of the feature to be deprecated
+- title: "Toggle behavior of `/draft` quick action in merge requests" # (required) The name of the feature to be deprecated
announcement_milestone: "15.4" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-09-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
- removal_date: "2022-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
reporter: phikai # (required) GitLab username of the person reporting the deprecation
stage: create # (required) String value of the stage that the feature was created in. e.g., Growth
diff --git a/data/deprecations/15-4-cs-docker-variables.yml b/data/deprecations/15-4-cs-docker-variables.yml
index 37f2552ac00..9bd0acff5b0 100644
--- a/data/deprecations/15-4-cs-docker-variables.yml
+++ b/data/deprecations/15-4-cs-docker-variables.yml
@@ -1,4 +1,4 @@
-- name: "Container Scanning variables that reference Docker"
+- title: "Container Scanning variables that reference Docker"
announcement_milestone: "15.4"
announcement_date: "2022-09-22"
removal_milestone: "16.0"
diff --git a/data/deprecations/15-4-deprecate-bundled-grafana.yml b/data/deprecations/15-4-deprecate-bundled-grafana.yml
index b6659e01c42..1a78691c475 100644
--- a/data/deprecations/15-4-deprecate-bundled-grafana.yml
+++ b/data/deprecations/15-4-deprecate-bundled-grafana.yml
@@ -1,4 +1,4 @@
-- name: "Bundled Grafana deprecated"
+- title: "Bundled Grafana deprecated"
announcement_milestone: "15.3"
announcement_date: "2022-08-22"
removal_milestone: "15.4"
diff --git a/data/deprecations/15-4-non-expiring-access-tokens.yml b/data/deprecations/15-4-non-expiring-access-tokens.yml
index c4becf9ed34..a302c39bde6 100644
--- a/data/deprecations/15-4-non-expiring-access-tokens.yml
+++ b/data/deprecations/15-4-non-expiring-access-tokens.yml
@@ -1,4 +1,4 @@
-- name: "Non-expiring access tokens"
+- title: "Non-expiring access tokens"
announcement_milestone: "15.4"
announcement_date: "2022-09-22"
removal_milestone: "16.0"
diff --git a/data/deprecations/15-4-starboard-directive.yml b/data/deprecations/15-4-starboard-directive.yml
index 07b7cdf7fd6..e83f240eef4 100644
--- a/data/deprecations/15-4-starboard-directive.yml
+++ b/data/deprecations/15-4-starboard-directive.yml
@@ -1,4 +1,4 @@
-- name: "Starboard directive in the config for the GitLab Agent for Kubernetes"
+- title: "Starboard directive in the config for the GitLab Agent for Kubernetes"
announcement_milestone: "15.4"
announcement_date: "2022-09-22"
removal_milestone: "16.0"
diff --git a/data/deprecations/15-5-confidential-field-on-notes.yml b/data/deprecations/15-5-confidential-field-on-notes.yml
index 5ec5c3adab9..ad68eecf04e 100644
--- a/data/deprecations/15-5-confidential-field-on-notes.yml
+++ b/data/deprecations/15-5-confidential-field-on-notes.yml
@@ -1,4 +1,4 @@
-- name: 'GraphQL field `confidential` changed to `internal` on notes'
+- title: 'GraphQL field `confidential` changed to `internal` on notes'
announcement_milestone: '15.5'
announcement_date: '2022-10-22'
removal_milestone: '16.0'
diff --git a/data/deprecations/15-5-disable-file-type-var-expansion-ci-pipeline.yml b/data/deprecations/15-5-disable-file-type-var-expansion-ci-pipeline.yml
index 31b0d9c2dbc..fe05cbfd62f 100644
--- a/data/deprecations/15-5-disable-file-type-var-expansion-ci-pipeline.yml
+++ b/data/deprecations/15-5-disable-file-type-var-expansion-ci-pipeline.yml
@@ -1,8 +1,8 @@
-- name: "File Type variable expansion in `.gitlab-ci.yml`" # (required) The name of the feature to be deprecated
+- title: "File Type variable expansion in `.gitlab-ci.yml`" # (required) The name of the feature to be deprecated
announcement_milestone: "15.5" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-10-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.7" # (required) The milestone when this feature is planned to be removed
- removal_date: # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_date: "2022-12-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
reporter: DarrenEastman # (required) GitLab username of the person reporting the deprecation
stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
diff --git a/data/deprecations/15-5-vulnerabilityFindingDismiss-mutation.yml b/data/deprecations/15-5-vulnerabilityFindingDismiss-mutation.yml
index 949aa40e5ce..56479933f94 100644
--- a/data/deprecations/15-5-vulnerabilityFindingDismiss-mutation.yml
+++ b/data/deprecations/15-5-vulnerabilityFindingDismiss-mutation.yml
@@ -1,4 +1,4 @@
-- name: "vulnerabilityFindingDismiss GraphQL mutation"
+- title: "vulnerabilityFindingDismiss GraphQL mutation"
announcement_milestone: "15.5"
announcement_date: "2022-10-22"
removal_milestone: "16.0"
diff --git a/data/deprecations/15-6-deprecate-config-fields-runner-helm-chart.yml b/data/deprecations/15-6-deprecate-config-fields-runner-helm-chart.yml
index 3d8d946027f..5690bc0763f 100644
--- a/data/deprecations/15-6-deprecate-config-fields-runner-helm-chart.yml
+++ b/data/deprecations/15-6-deprecate-config-fields-runner-helm-chart.yml
@@ -1,13 +1,13 @@
-- name: "Configuration fields in GitLab Runner Helm Chart" # (required) The name of the feature to be deprecated
+- title: "Configuration fields in GitLab Runner Helm Chart" # (required) The name of the feature to be deprecated
announcement_milestone: "15.6" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-11-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
- removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
reporter: ratchade # (required) GitLab username of the person reporting the deprecation
- stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
- issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/379064 # (required) Link to the deprecation issue in GitLab
+ stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/379064 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
From GitLab 13.6, users can [specify any runner configuration in the GitLab Runner Helm chart](https://docs.gitlab.com/runner/install/kubernetes.html). When we implemented this feature, we deprecated values in the GitLab Helm Chart configuration that were specific to GitLab Runner. These fields are deprecated and we plan to remove them in v1.0 of the GitLab Runner Helm chart.
end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
- end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.
+ end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.
diff --git a/data/deprecations/15-6-deprecate-merge_status-api-field.yml b/data/deprecations/15-6-deprecate-merge_status-api-field.yml
index 55a7cce0b6c..7d7b160d6bd 100644
--- a/data/deprecations/15-6-deprecate-merge_status-api-field.yml
+++ b/data/deprecations/15-6-deprecate-merge_status-api-field.yml
@@ -1,15 +1,14 @@
-- name: "merge_status API field" # The name of the feature to be deprecated
- announcement_milestone: "15.6" # The milestone when this feature was first announced as deprecated.
- announcement_date: "2022-11-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
- removal_milestone: "16.0" # The milestone when this feature is planned to be removed
- removal_date: "2023-05-22" # the date of the milestone release when this feature is planned to be removed
- breaking_change: true # If this deprecation is a breaking change, set this value to true
- body: | # Do not modify this line, instead modify the lines below.
+- title: "merge_status API field"
+ announcement_milestone: "15.6"
+ announcement_date: "2022-11-22"
+ removal_milestone: "16.0"
+ removal_date: "2023-05-22"
+ breaking_change: true
+ body: |
The `merge_status` field in the [merge request API](https://docs.gitlab.com/ee/api/merge_requests.html#merge-status) has been deprecated in favor of the `detailed_merge_status` field which more correctly identifies all of the potential statuses that a merge request can be in. API users are encouraged to use the new `detailed_merge_status` field instead. The `merge_status` field will be removed in v5 of the GitLab REST API.
-# The following items are not published on the docs page, but may be used in the future.
- stage: create # (optional - may be required in the future) String value of the stage that the feature was created in. e.g., Growth
- tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
- issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382032 # (optional) This is a link to the deprecation issue in GitLab
- documentation_url: https://docs.gitlab.com/ee/api/merge_requests.html#merge-status # (optional) This is a link to the current documentation page
- image_url: # (optional) This is a link to a thumbnail image depicting the feature
- video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
+ stage: create
+ tiers:
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382032
+ documentation_url: https://docs.gitlab.com/ee/api/merge_requests.html#merge-status
+ image_url:
+ video_url:
diff --git a/data/deprecations/15-6-deprecate-post-api-v4-runner.yml b/data/deprecations/15-6-deprecate-post-api-v4-runner.yml
index 9e308fbecce..07590296096 100644
--- a/data/deprecations/15-6-deprecate-post-api-v4-runner.yml
+++ b/data/deprecations/15-6-deprecate-post-api-v4-runner.yml
@@ -1,24 +1,24 @@
-- name: "`POST /api/v4/runners` method to register runners" # (required) The name of the feature to be deprecated
+- title: "Registration tokens and server-side runner arguments in `POST /api/v4/runners` endpoint" # (required) The name of the feature to be deprecated
announcement_milestone: "15.6" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-11-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
- removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
- removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_milestone: "17.0" # (required) The milestone when this feature is planned to be removed
+ removal_date: "2024-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
reporter: pedropombeiro # (required) GitLab username of the person reporting the deprecation
- stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
+ stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/379743 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
- The `POST` method operation on the `/api/v4/runners` endpoint is deprecated.
- This endpoint and method [registers](https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner) a runner
- with a GitLab instance at the instance, group, or project level through the API. We plan to remove this endpoint
- and method in GitLab 16.0.
+ The support for registration tokens and certain runner configuration arguments in the `POST` method operation on the `/api/v4/runners` endpoint is deprecated.
+ This endpoint [registers](https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner) a runner
+ with a GitLab instance at the instance, group, or project level through the API. We plan to remove the support for
+ registration tokens and certain configuration arguments in this endpoint in GitLab 17.0.
In GitLab 15.8, we plan to implement a new method to bind runners to a GitLab instance,
as part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/).
This new architecture introduces a new method for registering runners and will eliminate the legacy
[runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens).
- From GitLab 16.0 and later, the runner registration methods implemented by the new GitLab Runner token architecture will be the only supported methods.
+ From GitLab 17.0 and later, the runner registration methods implemented by the new GitLab Runner token architecture will be the only supported methods.
end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
- end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.
+ end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
documentation_url: https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner # (optional) This is a link to the current documentation page
diff --git a/data/deprecations/15-6-deprecate-runner-reg-token-helm.yml b/data/deprecations/15-6-deprecate-runner-reg-token-helm.yml
index 330f1b1f39e..f93ffb05014 100644
--- a/data/deprecations/15-6-deprecate-runner-reg-token-helm.yml
+++ b/data/deprecations/15-6-deprecate-runner-reg-token-helm.yml
@@ -1,19 +1,19 @@
-- name: "`runnerRegistrationToken` parameter for GitLab Runner Helm Chart" # (required) The name of the feature to be deprecated
+- title: "`runnerRegistrationToken` parameter for GitLab Runner Helm Chart" # (required) The name of the feature to be deprecated
announcement_milestone: "15.6" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-11-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
- removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
- removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_milestone: "17.0" # (required) The milestone when this feature is planned to be removed
+ removal_date: "2024-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
reporter: pedropombeiro # (required) GitLab username of the person reporting the deprecation
- stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
- issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381111 # (required) Link to the deprecation issue in GitLab
+ stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381111 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
The [`runnerRegistrationToken`](https://docs.gitlab.com/runner/install/kubernetes.html#required-configuration) parameter to use the GitLab Helm Chart to install a runner on Kubernetes is deprecated.
As part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/), in GitLab 15.8 we plan to introduce:
- - A new method to bind runners to a GitLab instance.
+ - A new method to bind runners to a GitLab instance leveraging `runnerToken`.
- A unique system ID saved to the `config.toml`, which will ensure traceability between jobs and runners.
- From GitLab 16.0 and later, the methods to register runners introduced by the new GitLab Runner token architecture will be the only supported methods.
+ From GitLab 17.0 and later, the methods to register runners introduced by the new GitLab Runner token architecture will be the only supported methods.
end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
- end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.
+ end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.
diff --git a/data/deprecations/15-6-deprecate-runner-register-command.yml b/data/deprecations/15-6-deprecate-runner-register-command.yml
index b20bc4bbeec..d2633ffacc9 100644
--- a/data/deprecations/15-6-deprecate-runner-register-command.yml
+++ b/data/deprecations/15-6-deprecate-runner-register-command.yml
@@ -1,18 +1,19 @@
-- name: "`gitlab-runner register` command" # (required) The name of the feature to be deprecated
+- title: "Registration tokens and server-side runner arguments in `gitlab-runner register` command" # (required) The name of the feature to be deprecated
announcement_milestone: "15.6" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-11-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
- removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
- removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_milestone: "17.0" # (required) The milestone when this feature is planned to be removed
+ removal_date: "2024-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
reporter: pedropombeiro # (required) GitLab username of the person reporting the deprecation
stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/380872 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
- The command to [register](https://docs.gitlab.com/runner/register/) a runner, `gitlab-runner register` is deprecated.
+ The support for registration tokens and certain configuration arguments in the command to [register](https://docs.gitlab.com/runner/register/) a runner, `gitlab-runner register` is deprecated.
GitLab plans to introduce a new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/) in GitLab 15.8,
which introduces a new method for registering runners and eliminates the legacy
[runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens).
- The new method will involve passing a [runner authentication token](https://docs.gitlab.com/ee/security/token_overview.html#runner-authentication-tokens-also-called-runner-tokens)
- to a new `gitlab-runner deploy` command.
+ The new method will involve creating the runner in the GitLab UI and passing the
+ [runner authentication token](https://docs.gitlab.com/ee/security/token_overview.html#runner-authentication-tokens-also-called-runner-tokens)
+ to the `gitlab-runner register` command.
end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.
diff --git a/data/deprecations/15-6-deprecate-runner-register-token-k8s-operator.yml b/data/deprecations/15-6-deprecate-runner-register-token-k8s-operator.yml
index 20f6ba758bb..ec128c1aad1 100644
--- a/data/deprecations/15-6-deprecate-runner-register-token-k8s-operator.yml
+++ b/data/deprecations/15-6-deprecate-runner-register-token-k8s-operator.yml
@@ -1,20 +1,16 @@
-- name: "GitLab Runner registration token in Runner Operator" # (required) The name of the feature to be deprecated
+- title: "GitLab Runner registration token in Runner Operator" # (required) The name of the feature to be deprecated
announcement_milestone: "15.6" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-11-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
- removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
- removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_milestone: "17.0" # (required) The milestone when this feature is planned to be removed
+ removal_date: "2024-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
- reporter: ratchade # (required) GitLab username of the person reporting the deprecation
- stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
- issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382077 # (required) Link to the deprecation issue in GitLab
+ reporter: ratchade # (required) GitLab username of the person reporting the deprecation
+ stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382077 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
The [`runner-registration-token`](https://docs.gitlab.com/runner/install/operator.html#install-the-kubernetes-operator) parameter that uses the OpenShift and k8s Vanilla Operator to install a runner on Kubernetes is deprecated. GitLab plans to introduce a new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/) in GitLab 15.8, which introduces a new method for registering runners and eliminates the legacy runner registration token.
end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
- end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.
-
-
-# OTHER OPTIONAL FIELDS
-#
+ end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
documentation_url: https://docs.gitlab.com/runner/install/operator.html#install-the-kubernetes-operator # (optional) This is a link to the current documentation page
image_url: # (optional) This is a link to a thumbnail image depicting the feature
diff --git a/data/deprecations/15-7-dast-api-variable-deprecation.yml b/data/deprecations/15-7-dast-api-variable-deprecation.yml
new file mode 100644
index 00000000000..724c24cf440
--- /dev/null
+++ b/data/deprecations/15-7-dast-api-variable-deprecation.yml
@@ -0,0 +1,17 @@
+- title: "DAST API variables" # (required) Actionable title. e.g., The `confidential` field for a `Note` is deprecated. Use `internal` instead.
+ announcement_milestone: "15.7" # (required) The milestone when this feature was first announced as deprecated.
+ announcement_date: "2022-12-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
+ removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
+ reporter: derekferguson # (required) GitLab username of the person reporting the deprecation
+ stage: Secure # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383467 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ With the switch to the new DAST API analyzer in GitLab 15.6, two legacy DAST API variables are being deprecated. The variables `DAST_API_HOST_OVERRIDE` and `DAST_API_SPECIFICATION` will no longer be used for DAST API scans.
+
+ `DAST_API_HOST_OVERRIDE` has been deprecated in favor of using the `DAST_API_TARGET_URL` to automatically override the host in the OpenAPI specification.
+
+ `DAST_API_SPECIFICATION` has been deprecated in favor of `DAST_API_OPENAPI`. To continue using an OpenAPI specification to guide the test, users must replace the `DAST_API_SPECIFICATION` variable with the `DAST_API_OPENAPI` variable. The value can remain the same, but the variable name must be replaced.
+
+ These two variables will be removed in GitLab 16.0.
diff --git a/data/deprecations/15-7-deprecate-api-v4-runner-registration-token-reset-endpoints.yml b/data/deprecations/15-7-deprecate-api-v4-runner-registration-token-reset-endpoints.yml
new file mode 100644
index 00000000000..17336c0d2c8
--- /dev/null
+++ b/data/deprecations/15-7-deprecate-api-v4-runner-registration-token-reset-endpoints.yml
@@ -0,0 +1,27 @@
+- title: "Support for REST API endpoints that reset runner registration tokens" # (required) The name of the feature to be deprecated
+ announcement_milestone: "15.7" # (required) The milestone when this feature was first announced as deprecated.
+ announcement_date: "2022-12-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
+ removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
+ reporter: pedropombeiro # (required) GitLab username of the person reporting the deprecation
+ stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383341 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ The support for runner registration tokens is deprecated. As a consequence, the REST API endpoints to reset a registration token are also deprecated and will
+ be removed in GitLab 16.0.
+ The deprecated endpoints are:
+
+ - `POST /runners/reset_registration_token`
+ - `POST /projects/:id/runners/reset_registration_token`
+ - `POST /groups/:id/runners/reset_registration_token`
+
+ In GitLab 15.8, we plan to implement a new method to bind runners to a GitLab instance,
+ as part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/).
+ This new architecture introduces a new method for registering runners and will eliminate the legacy
+ [runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens).
+ From GitLab 16.0 and later, the runner registration methods implemented by the new GitLab Runner token architecture will be the only supported methods.
+ end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
+ end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.
+ tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ documentation_url: https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner # (optional) This is a link to the current documentation page
diff --git a/data/deprecations/15-7-deprecate-dast-api-scan-in-dast-template.yml b/data/deprecations/15-7-deprecate-dast-api-scan-in-dast-template.yml
new file mode 100644
index 00000000000..172683dcb86
--- /dev/null
+++ b/data/deprecations/15-7-deprecate-dast-api-scan-in-dast-template.yml
@@ -0,0 +1,11 @@
+- title: "DAST API scans using DAST template is deprecated" # (required) Actionable title. e.g., The `confidential` field for a `Note` is deprecated. Use `internal` instead.
+ announcement_milestone: "15.7" # (required) The milestone when this feature was first announced as deprecated.
+ announcement_date: "2022-12-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
+ removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
+ reporter: derekferguson # (required) GitLab username of the person reporting the deprecation
+ stage: Secure # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/384198 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ With the move to the new DAST API analyzer and the `DAST-API.gitlab-ci.yml` template for DAST API scans, we will be removing the ability to scan APIs with the DAST analyzer. Use of the `DAST.gitlab-ci.yml` or `DAST-latest.gitlab-ci.yml` templates for API scans is deprecated as of GitLab 15.7 and will no longer work in GitLab 16.0. Please use `DAST-API.gitlab-ci.yml` template and refer to the [DAST API analyzer](https://docs.gitlab.com/ee/user/application_security/dast_api/#configure-dast-api-with-an-openapi-specification) documentation for configuration details.
diff --git a/data/deprecations/15-7-deprecate-dast-zap-variables.yml b/data/deprecations/15-7-deprecate-dast-zap-variables.yml
new file mode 100644
index 00000000000..764d74b00bb
--- /dev/null
+++ b/data/deprecations/15-7-deprecate-dast-zap-variables.yml
@@ -0,0 +1,13 @@
+- title: "DAST ZAP advanced configuration variables deprecation" # (required) Actionable title. e.g., The `confidential` field for a `Note` is deprecated. Use `internal` instead.
+ announcement_milestone: "15.7" # (required) The milestone when this feature was first announced as deprecated.
+ announcement_date: "2022-12-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
+ removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
+ reporter: derekferguson # (required) GitLab username of the person reporting the deprecation
+ stage: Secure # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383467 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ With the new browser-based DAST analyzer GA in GitLab 15.7, we are working towards making it the default DAST analyzer at some point in the future. In preparation for this, the following legacy DAST variables are being deprecated and scheduled for removal in GitLab 16.0: `DAST_ZAP_CLI_OPTIONS` and `DAST_ZAP_LOG_CONFIGURATION`. These variables allowed for advanced configuration of the legacy DAST analyzer, which was based on OWASP ZAP. The new browser-based analyzer will not include the same functionality, as these were specific to how ZAP worked.
+
+ These three variables will be removed in GitLab 16.0.
diff --git a/data/deprecations/15-7-deprecate-gitlab-runner-exec-cmd.yml b/data/deprecations/15-7-deprecate-gitlab-runner-exec-cmd.yml
new file mode 100644
index 00000000000..d535e58fc36
--- /dev/null
+++ b/data/deprecations/15-7-deprecate-gitlab-runner-exec-cmd.yml
@@ -0,0 +1,21 @@
+- title: "The `gitlab-runner exec` command is deprecated" # (required) The name of the feature to be deprecated
+ announcement_milestone: "15.7" # (required) The milestone when this feature was first announced as deprecated.
+ announcement_date: "2022-12-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
+ removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
+ reporter: DarrenEastman # (required) GitLab username of the person reporting the deprecation
+ stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385235 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ The [`gitlab-runner exec`](https://docs.gitlab.com/runner/commands/#gitlab-runner-exec) command is deprecated and will be fully removed from GitLab Runner in 16.0. The `gitlab-runner exec` feature was initially developed to provide the ability to validate a GitLab CI pipeline on a local system without needing to commit the updates to a GitLab instance. However, with the continued evolution of GitLab CI, replicating all GitLab CI features into `gitlab-runner exec` was no longer viable. Pipeline syntax and validation [simulation](https://docs.gitlab.com/ee/ci/pipeline_editor/#simulate-a-cicd-pipeline) are available in the GitLab pipeline editor.
+ end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
+ end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.
+
+
+# OTHER OPTIONAL FIELDS
+#
+ tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ documentation_url: https://docs.gitlab.com/runner/commands/#gitlab-runner-exec # (optional) This is a link to the current documentation page
+ image_url: # (optional) This is a link to a thumbnail image depicting the feature
+ video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
diff --git a/data/deprecations/15-7-deprecate-kas-metrics-port-in-gitlab-chart.yml b/data/deprecations/15-7-deprecate-kas-metrics-port-in-gitlab-chart.yml
new file mode 100644
index 00000000000..2b747a2fead
--- /dev/null
+++ b/data/deprecations/15-7-deprecate-kas-metrics-port-in-gitlab-chart.yml
@@ -0,0 +1,22 @@
+- title: "KAS Metrics Port in GitLab Helm Chart" # (required) The name of the feature to be deprecated
+ announcement_milestone: "15.7" # (required) The milestone when this feature was first announced as deprecated.
+ announcement_date: "2022-12-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
+ removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
+ reporter: timofurrer # (required) GitLab username of the person reporting the deprecation
+ stage: Configure # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383039 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ The `gitlab.kas.metrics.port` has been deprecated in favor of the new `gitlab.kas.observability.port` configuration field for the [GitLab Helm Chart](https://gitlab.com/gitlab-org/charts/gitlab/-/merge_requests/2839).
+ This port is used for much more than just metrics, which warranted this change to avoid confusion in configuration.
+ end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
+ end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.
+
+
+ # OTHER OPTIONAL FIELDS
+ #
+ tiers: [Core, Premium, Ultimate] # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ documentation_url: https://docs.gitlab.com/charts/charts/gitlab/kas/index.html # (optional) This is a link to the current documentation page
+ image_url: # (optional) This is a link to a thumbnail image depicting the feature
+ video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
diff --git a/data/deprecations/15-7-deprecate-phabricator-importer.yml b/data/deprecations/15-7-deprecate-phabricator-importer.yml
new file mode 100644
index 00000000000..42958a05b1d
--- /dev/null
+++ b/data/deprecations/15-7-deprecate-phabricator-importer.yml
@@ -0,0 +1,12 @@
+- title: 'The Phabricator task importer is deprecated'
+ announcement_milestone: '15.7'
+ announcement_date: '2022-12-22'
+ removal_milestone: '16.0'
+ removal_date: '2023-05-22'
+ breaking_change: true
+ body: |
+ The [Phabricator task importer](https://docs.gitlab.com/ee/user/project/import/phabricator.html) is being deprecated. Phabricator itself as a project is no longer actively maintained since June 1, 2021. We haven't observed imports using this tool. There has been no activity on the open related issues on GitLab.
+ stage: manage
+ tiers:
+ issue_url: https://gitlab.com/gitlab-com/Product/-/issues/4894
+ documentation_url: https://docs.gitlab.com/ee/user/project/import/phabricator.html
diff --git a/data/deprecations/15-7-deprecate-shimo-integration.yml b/data/deprecations/15-7-deprecate-shimo-integration.yml
new file mode 100644
index 00000000000..11ccbae41de
--- /dev/null
+++ b/data/deprecations/15-7-deprecate-shimo-integration.yml
@@ -0,0 +1,27 @@
+- title: "Shimo integration" # (required) Clearly explain the change, or planned change. For example, "The `confidential` field for a `Note` is deprecated" or "The maximum number of characters in a job name will be limited to 250."
+ announcement_milestone: "15.7" # (required) The milestone when this feature was first announced as deprecated.
+ announcement_date: "2022-12-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
+ removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
+ reporter: arturoherrero # (required) GitLab username of the person reporting the deprecation
+ stage: Manage # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/377824 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ The [Shimo Workspace integration](https://docs.gitlab.com/ee/user/project/integrations/shimo.html) has been deprecated
+ and will be moved to the JiHu GitLab codebase.
+#
+# OPTIONAL END OF SUPPORT FIELDS
+#
+# If an End of Support period applies, the announcement should be shared with GitLab Support
+# in the `#spt_managers` channel in Slack, and mention `@gitlab-com/support` in this MR.
+#
+ end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
+ end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.
+ #
+ # OTHER OPTIONAL FIELDS
+ #
+ tiers: [Core, Premium, Ultimate] # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ documentation_url: https://docs.gitlab.com/ee/user/project/integrations/shimo.html # (optional) This is a link to the current documentation page
+ image_url: # (optional) This is a link to a thumbnail image depicting the feature
+ video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
diff --git a/data/deprecations/15-7-deprecate-single-merge-request-changes-api-endpoint.yml b/data/deprecations/15-7-deprecate-single-merge-request-changes-api-endpoint.yml
new file mode 100644
index 00000000000..4dcefc617c0
--- /dev/null
+++ b/data/deprecations/15-7-deprecate-single-merge-request-changes-api-endpoint.yml
@@ -0,0 +1,14 @@
+- title: 'Single merge request changes API endpoint'
+ announcement_milestone: '15.7'
+ announcement_date: '2022-12-22'
+ removal_milestone: '16.0'
+ removal_date: '2023-05-22'
+ breaking_change: true
+ body: |
+ The endpoint to get [changes from a single merge request](https://docs.gitlab.com/ee/api/merge_requests.html#get-single-merge-request-changes) has been deprecated in favor the [list merge request diffs](https://docs.gitlab.com/ee/api/merge_requests.html#list-merge-request-diffs) endpoint. API users are encouraged to switch to the new diffs endpoint instead. The `changes from a single merge request` endpoint will be removed in v5 of the GitLab REST API.
+ stage: create
+ tiers:
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/322117
+ documentation_url: https://docs.gitlab.com/ee/api/merge_requests.html#list-merge-request-diffs
+ image_url:
+ video_url:
diff --git a/data/deprecations/15-7-deprecate-zentao-integration.yml b/data/deprecations/15-7-deprecate-zentao-integration.yml
new file mode 100644
index 00000000000..5fc7e9da5fc
--- /dev/null
+++ b/data/deprecations/15-7-deprecate-zentao-integration.yml
@@ -0,0 +1,27 @@
+- title: "ZenTao integration" # (required) Clearly explain the change, or planned change. For example, "The `confidential` field for a `Note` is deprecated" or "The maximum number of characters in a job name will be limited to 250."
+ announcement_milestone: "15.7" # (required) The milestone when this feature was first announced as deprecated.
+ announcement_date: "2022-12-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
+ removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
+ reporter: arturoherrero # (required) GitLab username of the person reporting the deprecation
+ stage: Manage # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/377825 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ The [ZenTao product integration](https://docs.gitlab.com/ee/user/project/integrations/zentao.html) has been deprecated
+ and will be moved to the JiHu GitLab codebase.
+#
+# OPTIONAL END OF SUPPORT FIELDS
+#
+# If an End of Support period applies, the announcement should be shared with GitLab Support
+# in the `#spt_managers` channel in Slack, and mention `@gitlab-com/support` in this MR.
+#
+ end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
+ end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.
+ #
+ # OTHER OPTIONAL FIELDS
+ #
+ tiers: [Premium, Ultimate] # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ documentation_url: https://docs.gitlab.com/ee/user/project/integrations/zentao.html # (optional) This is a link to the current documentation page
+ image_url: # (optional) This is a link to a thumbnail image depicting the feature
+ video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
diff --git a/data/deprecations/15-7-enable-period-in-terraform-state-name.yml b/data/deprecations/15-7-enable-period-in-terraform-state-name.yml
new file mode 100644
index 00000000000..e9db80ea34c
--- /dev/null
+++ b/data/deprecations/15-7-enable-period-in-terraform-state-name.yml
@@ -0,0 +1,25 @@
+- title: "Support for periods (`.`) in Terraform state names might break existing states"
+ announcement_milestone: "15.7"
+ announcement_date: "2022-12-22"
+ removal_milestone: "16.0"
+ removal_date: "2023-05-22"
+ breaking_change: true
+ reporter: nagyv-gitlab
+ stage: configure
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385564
+ body: |
+ Previously, Terraform state names containing periods were not supported. However, you could still use state names with periods via a workaround.
+
+ GitLab 15.7 [adds full support](https://docs.gitlab.com/ee/user/infrastructure/iac/troubleshooting.html#state-not-found-if-the-state-name-contains-a-period) for state names that contain periods. If you used a workaround to handle these state names, your jobs might fail, or it might look like you've run Terraform for the first time.
+
+ To resolve the issue:
+
+ 1. Change any references to the state file by excluding the period and any characters that follow.
+ - For example, if your state name is `state.name`, change all references to `state`.
+ 1. Run your Terraform commands.
+
+ To use the full state name, including the period, [migrate to the full state file](https://docs.gitlab.com/ee/user/infrastructure/iac/terraform_state.html#migrate-to-a-gitlab-managed-terraform-state).
+ end_of_support_milestone: 16.0
+ end_of_support_date: 2023-05-22
+ tiers: [Free, Silver, Gold, Core, Premium, Ultimate]
+ documentation_url: 'https://docs.gitlab.com/ee/user/infrastructure/iac/troubleshooting.html#troubleshooting-terraform-state'
diff --git a/data/deprecations/15-8-dast-report-variables-deprecation.yml b/data/deprecations/15-8-dast-report-variables-deprecation.yml
new file mode 100644
index 00000000000..21416e0d009
--- /dev/null
+++ b/data/deprecations/15-8-dast-report-variables-deprecation.yml
@@ -0,0 +1,13 @@
+- title: "DAST report variables deprecation" # (required) Actionable title. e.g., The `confidential` field for a `Note` is deprecated. Use `internal` instead.
+ announcement_milestone: "15.7" # (required) The milestone when this feature was first announced as deprecated.
+ announcement_date: "2022-12-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
+ removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
+ reporter: derekferguson # (required) GitLab username of the person reporting the deprecation
+ stage: Secure # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/384340 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ With the new browser-based DAST analyzer GA in GitLab 15.7, we are working towards making it the default DAST analyzer at some point in the future. In preparation for this, the following legacy DAST variables are being deprecated and scheduled for removal in GitLab 16.0: `DAST_HTML_REPORT`, `DAST_XML_REPORT`, and `DAST_MARKDOWN_REPORT`. These reports relied on the legacy DAST analyzer and we do not plan to implement them in the new browser-based analyzer. As of GitLab 16.0, these report artifacts will no longer be generated.
+
+ These three variables will be removed in GitLab 16.0.
diff --git a/data/deprecations/16-0-post-ci-lint.yml b/data/deprecations/16-0-post-ci-lint.yml
new file mode 100644
index 00000000000..3bea4201bc0
--- /dev/null
+++ b/data/deprecations/16-0-post-ci-lint.yml
@@ -0,0 +1,44 @@
+# This is a template for a feature deprecation.
+#
+# Please refer to the deprecation guidelines to confirm your understanding of the
+# definitions for "Deprecation", "End of Support", and "Removal":
+# https://docs.gitlab.com/ee/development/deprecation_guidelines/#terminology
+#
+# Deprecations must be announced at least three releases prior to removal.
+# See the OPTIONAL END OF SUPPORT FIELDS section below if an End of Support period also applies.
+#
+# Breaking changes must happen in a major release.
+#
+# For more information please refer to the handbook documentation here:
+# https://about.gitlab.com/handbook/marketing/blog/release-posts/#deprecations
+#
+# Please delete this line and above before submitting your merge request.
+#
+# REQUIRED FIELDS
+#
+- title: "`POST ci/lint` API endpoint deprecated" # (required) The name of the feature to be deprecated
+ announcement_milestone: "15.7" # (required) The milestone when this feature was first announced as deprecated.
+ announcement_date: "2022-12-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
+ removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
+ breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
+ reporter: dhershkovitch # (required) GitLab username of the person reporting the deprecation
+ stage: verify # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381669 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ The `POST ci/lint` API endpoint is deprecated in 15.7, and will be removed in 16.0. This endpoint does not validate the full range of CI/CD configuration options. Instead, use [`POST /projects/:id/ci/lint`](https://docs.gitlab.com/15.5/ee/api/lint.html#validate-a-ci-yaml-configuration-with-a-namespace), which properly validates CI/CD configuration.
+#
+# OPTIONAL END OF SUPPORT FIELDS
+#
+# If an End of Support period applies, the announcement should be shared with GitLab Support
+# in the `#spt_managers` channel in Slack, and mention `@gitlab-com/support` in this MR.
+#
+ end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
+ end_of_support_date: # (optional) The date of the milestone release when support for this feature will end.
+ #
+ # OTHER OPTIONAL FIELDS
+ #
+ tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ documentation_url: # (optional) This is a link to the current documentation page
+ image_url: # (optional) This is a link to a thumbnail image depicting the feature
+ video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
diff --git a/data/deprecations/16-0-security_report_schemas_v14-x-x.yml b/data/deprecations/16-0-security_report_schemas_v14-x-x.yml
index 46f6012a7db..5001bd490b0 100644
--- a/data/deprecations/16-0-security_report_schemas_v14-x-x.yml
+++ b/data/deprecations/16-0-security_report_schemas_v14-x-x.yml
@@ -1,7 +1,7 @@
#
# REQUIRED FIELDS
#
-- name: "Security report schemas version 14.x.x" # (required) the name of the feature being removed. Avoid the words `deprecation`, `deprecate`, `removal`, and `remove` in this field because these are implied.
+- title: "Security report schemas version 14.x.x" # (required) the name of the feature being removed. Avoid the words `deprecation`, `deprecate`, `removal`, and `remove` in this field because these are implied.
announcement_milestone: "15.3" # (required) The milestone when this feature was deprecated.
announcement_date: "2022-08-22" # (required) The date of the milestone release when this feature was deprecated. This should almost always be the 22nd of a month (YYYY-MM-DD), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is being removed.
diff --git a/data/deprecations/distribution_deprecations_14-4.yml b/data/deprecations/distribution_deprecations_14-4.yml
index e54108d2947..8dc948918c1 100644
--- a/data/deprecations/distribution_deprecations_14-4.yml
+++ b/data/deprecations/distribution_deprecations_14-4.yml
@@ -1,4 +1,4 @@
-- name: "Move `custom_hooks_dir` setting from GitLab Shell to Gitaly" # The name of the feature to be deprecated
+- title: "Move `custom_hooks_dir` setting from GitLab Shell to Gitaly" # The name of the feature to be deprecated
announcement_milestone: "14.9" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-03-22"
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
diff --git a/data/deprecations/templates/_deprecation_template.md.erb b/data/deprecations/templates/_deprecation_template.md.erb
index 4fd2ca29bc8..7712790d166 100644
--- a/data/deprecations/templates/_deprecation_template.md.erb
+++ b/data/deprecations/templates/_deprecation_template.md.erb
@@ -34,6 +34,9 @@ In each release, GitLab announces features that are deprecated and no longer rec
Each deprecated feature will be removed in a future release.
Some features cause breaking changes when they are removed.
+**{rss}** **To be notified of upcoming breaking changes**,
+add this URL to your RSS feed reader: `https://about.gitlab.com/breaking-changes.xml`
+
DISCLAIMER:
This page contains information related to upcoming products, features, and functionality.
It is important to note that the information presented is for informational purposes only.
@@ -50,7 +53,7 @@ sole discretion of GitLab Inc.
<%- entries.select{|d| d["announcement_milestone"] == milestone}.each do |deprecation| %>
<div class="deprecation removal-<%= deprecation["removal_milestone"].gsub('.', '') %><% if deprecation["breaking_change"] -%> breaking-change<% end %>">
-### <%= deprecation["name"]%>
+### <%= deprecation["title"] %>
<% if deprecation["end_of_support_milestone"] -%>
End of Support: GitLab <span class="removal-milestone"><%= deprecation["end_of_support_milestone"]%></span> (<%= deprecation["end_of_support_date"]%>)<br />
diff --git a/data/deprecations/templates/example.yml b/data/deprecations/templates/example.yml
index cc512f70b3e..619810f1e7e 100644
--- a/data/deprecations/templates/example.yml
+++ b/data/deprecations/templates/example.yml
@@ -16,7 +16,7 @@
#
# REQUIRED FIELDS
#
-- name: "Feature name" # (required) The name of the feature to be deprecated
+- title: "Feature A is deprecated" # (required) Clearly explain the change, or planned change. For example, "The `confidential` field for a `Note` is deprecated" or "The maximum number of characters in a job name will be limited to 250."
announcement_milestone: "XX.YY" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "YYYY-MM-DD" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "XX.YY" # (required) The milestone when this feature is planned to be removed
@@ -28,9 +28,18 @@
body: | # (required) Do not modify this line, instead modify the lines below.
<!-- START OF BODY COMMENT
- This area supports markdown. Delete this entire comment and replace it with your markdown content.
+ Be clear and concise. Give a brief explanation of the details or reasons for the change.
- When ready, assign to your tech writer for review. When ready, they will run `bin/rake gitlab:docs:compile_deprecations` to update the deprecations doc, then merge.
+ Additionally, deprecations and other planned changes should be actionable, so add details that explain what users need to do to address the change. For example:
+
+ - "Use the `internal` keyword instead of `confidential`."
+ - "Reduce the number of characters in all jobs to be 250 characters or less."
+ - "Give an expiration date to any access tokens that have no expiration date."
+ - "Stop using the `omniauth_crowd` gem. It will be removed and will not be replaced."
+
+ When ready, assign to your tech writer for review. They will run `bin/rake gitlab:docs:compile_deprecations` to update the deprecations doc, then merge.
+
+ This area supports markdown. Delete this entire comment and replace it with your markdown content.
END OF BODY COMMENT -->
#
diff --git a/data/removals/14_0/14_0-ds-deprecations.yml b/data/removals/14_0/14_0-ds-deprecations.yml
index 91da10097ed..ba47a9d1ed4 100644
--- a/data/removals/14_0/14_0-ds-deprecations.yml
+++ b/data/removals/14_0/14_0-ds-deprecations.yml
@@ -1,4 +1,4 @@
-- name: "Dependency Scanning"
+- title: "Dependency Scanning"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: nicoleschwartz
diff --git a/data/removals/14_0/14_0-lc-deprecations.yml b/data/removals/14_0/14_0-lc-deprecations.yml
index 6322b102ec5..d5d1b6f422b 100644
--- a/data/removals/14_0/14_0-lc-deprecations.yml
+++ b/data/removals/14_0/14_0-lc-deprecations.yml
@@ -1,4 +1,4 @@
-- name: "License Compliance"
+- title: "License Compliance"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: nicoleschwartz
diff --git a/data/removals/14_0/change_default_branch_name_to_main.yml b/data/removals/14_0/change_default_branch_name_to_main.yml
index 1c47f99870b..30bac5e5bf8 100644
--- a/data/removals/14_0/change_default_branch_name_to_main.yml
+++ b/data/removals/14_0/change_default_branch_name_to_main.yml
@@ -1,4 +1,4 @@
-- name: "Default branch name for new repositories now `main`"
+- title: "Default branch name for new repositories now `main`"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: sarahwaldner
diff --git a/data/removals/14_0/create-code-review-draft-wip.yml b/data/removals/14_0/create-code-review-draft-wip.yml
index 20eaaf3cd02..aab9f86b711 100644
--- a/data/removals/14_0/create-code-review-draft-wip.yml
+++ b/data/removals/14_0/create-code-review-draft-wip.yml
@@ -1,4 +1,4 @@
-- name: "WIP merge requests renamed 'draft merge requests'"
+- title: "WIP merge requests renamed 'draft merge requests'"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: phikai
diff --git a/data/removals/14_0/create-code-review-w-parameter-removal.yml b/data/removals/14_0/create-code-review-w-parameter-removal.yml
index 3adec30a1e7..cb10e1a583d 100644
--- a/data/removals/14_0/create-code-review-w-parameter-removal.yml
+++ b/data/removals/14_0/create-code-review-w-parameter-removal.yml
@@ -1,4 +1,4 @@
-- name: "`?w=1` URL parameter to ignore whitespace changes"
+- title: "`?w=1` URL parameter to ignore whitespace changes"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: phikai
diff --git a/data/removals/14_0/deprecation_bump_terraform_template_version.yml b/data/removals/14_0/deprecation_bump_terraform_template_version.yml
index 201c2efa8aa..3ccba65553d 100644
--- a/data/removals/14_0/deprecation_bump_terraform_template_version.yml
+++ b/data/removals/14_0/deprecation_bump_terraform_template_version.yml
@@ -1,4 +1,4 @@
-- name: "Terraform template version"
+- title: "Terraform template version"
removal_date: "2021-06-22"
removal_milestone: "14.0" # example
issue_url: ""
diff --git a/data/removals/14_0/deprecation_manage_access_14_0.yml b/data/removals/14_0/deprecation_manage_access_14_0.yml
index 30167d23d60..5de9e31c44e 100644
--- a/data/removals/14_0/deprecation_manage_access_14_0.yml
+++ b/data/removals/14_0/deprecation_manage_access_14_0.yml
@@ -1,4 +1,4 @@
-- name: Limit projects returned in `GET /groups/:id/`
+- title: Limit projects returned in `GET /groups/:id/`
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: ogolowisnki
@@ -7,7 +7,7 @@
body: |
To improve performance, we are limiting the number of projects returned from the `GET /groups/:id/` API call to 100. A complete list of projects can still be retrieved with the `GET /groups/:id/projects` API call.
-- name: "GitLab OAuth implicit grant"
+- title: "GitLab OAuth implicit grant"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: ogolowinski
diff --git a/data/removals/14_0/deprecation_update_cicd_templates_to_stop_using_hardcode_master.yml b/data/removals/14_0/deprecation_update_cicd_templates_to_stop_using_hardcode_master.yml
index 4df59321bc5..d7ac8818476 100644
--- a/data/removals/14_0/deprecation_update_cicd_templates_to_stop_using_hardcode_master.yml
+++ b/data/removals/14_0/deprecation_update_cicd_templates_to_stop_using_hardcode_master.yml
@@ -1,4 +1,4 @@
-- name: "Hardcoded `master` in CI/CD templates"
+- title: "Hardcoded `master` in CI/CD templates"
reporter: dhershkovitch
removal_date: "2021-06-22"
removal_milestone: "14.0"
diff --git a/data/removals/14_0/deuley_servicetemplates_removal.yml b/data/removals/14_0/deuley_servicetemplates_removal.yml
index bbc70d98562..4854911b8ac 100644
--- a/data/removals/14_0/deuley_servicetemplates_removal.yml
+++ b/data/removals/14_0/deuley_servicetemplates_removal.yml
@@ -1,4 +1,4 @@
-- name: "Service Templates"
+- title: "Service Templates"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: deuley
diff --git a/data/removals/14_0/release_announce_deprecation_of_release_notes_api.yml b/data/removals/14_0/release_announce_deprecation_of_release_notes_api.yml
index d7e12a89f14..7f1133f12d0 100644
--- a/data/removals/14_0/release_announce_deprecation_of_release_notes_api.yml
+++ b/data/removals/14_0/release_announce_deprecation_of_release_notes_api.yml
@@ -1,4 +1,4 @@
-- name: "Release description in the Tags API"
+- title: "Release description in the Tags API"
reporter: kbychu
removal_date: "2021-06-22"
removal_milestone: "14.0"
diff --git a/data/removals/14_0/release_deprecation_auto-deploy-image.yml b/data/removals/14_0/release_deprecation_auto-deploy-image.yml
index d6cd9ac23ca..b68388e0a9a 100644
--- a/data/removals/14_0/release_deprecation_auto-deploy-image.yml
+++ b/data/removals/14_0/release_deprecation_auto-deploy-image.yml
@@ -1,4 +1,4 @@
-- name: "Auto Deploy CI template v1"
+- title: "Auto Deploy CI template v1"
reporter: kbychu
removal_date: "2021-06-22"
removal_milestone: "14.0"
diff --git a/data/removals/14_0/release_domainsource_configuration_for_gitlab_pages_deprecation.yml b/data/removals/14_0/release_domainsource_configuration_for_gitlab_pages_deprecation.yml
index bb10ab8a2ab..902c36b8e96 100644
--- a/data/removals/14_0/release_domainsource_configuration_for_gitlab_pages_deprecation.yml
+++ b/data/removals/14_0/release_domainsource_configuration_for_gitlab_pages_deprecation.yml
@@ -1,4 +1,4 @@
-- name: "Disk source configuration for GitLab Pages"
+- title: "Disk source configuration for GitLab Pages"
reporter: kbychu
removal_date: "2021-06-22"
removal_milestone: "14.0"
diff --git a/data/removals/14_0/release_legacy_feature_flags_deprecation.yml b/data/removals/14_0/release_legacy_feature_flags_deprecation.yml
index bf0075faa1e..4bf95e4ed35 100644
--- a/data/removals/14_0/release_legacy_feature_flags_deprecation.yml
+++ b/data/removals/14_0/release_legacy_feature_flags_deprecation.yml
@@ -1,4 +1,4 @@
-- name: "Legacy feature flags"
+- title: "Legacy feature flags"
reporter: kbychu
removal_date: "2021-06-22"
removal_milestone: "14.0"
diff --git a/data/removals/14_0/release_remove_redundant_keyvalue_pair_from_the_payload_of_dora.yml b/data/removals/14_0/release_remove_redundant_keyvalue_pair_from_the_payload_of_dora.yml
index 98aef74fd03..3679242d33a 100644
--- a/data/removals/14_0/release_remove_redundant_keyvalue_pair_from_the_payload_of_dora.yml
+++ b/data/removals/14_0/release_remove_redundant_keyvalue_pair_from_the_payload_of_dora.yml
@@ -1,4 +1,4 @@
-- name: "Redundant timestamp field from DORA metrics API payload"
+- title: "Redundant timestamp field from DORA metrics API payload"
reporter: kbychu
removal_date: "2021-06-22"
removal_milestone: "14.0"
diff --git a/data/removals/14_0/removal-geo-fdw-settings.yml b/data/removals/14_0/removal-geo-fdw-settings.yml
index 5997705bf30..b2d035921c9 100644
--- a/data/removals/14_0/removal-geo-fdw-settings.yml
+++ b/data/removals/14_0/removal-geo-fdw-settings.yml
@@ -1,4 +1,4 @@
-- name: "Geo Foreign Data Wrapper settings"
+- title: "Geo Foreign Data Wrapper settings"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: fzimmer
diff --git a/data/removals/14_0/removal-graphql-fields.yml b/data/removals/14_0/removal-graphql-fields.yml
index 71d277bd6fc..bf73c427f96 100644
--- a/data/removals/14_0/removal-graphql-fields.yml
+++ b/data/removals/14_0/removal-graphql-fields.yml
@@ -1,4 +1,4 @@
-- name: "Deprecated GraphQL fields"
+- title: "Deprecated GraphQL fields"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: gweaver
diff --git a/data/removals/14_0/removal-legacy-storage.yml b/data/removals/14_0/removal-legacy-storage.yml
index 28b6ffeb0d1..4c229c9e9d9 100644
--- a/data/removals/14_0/removal-legacy-storage.yml
+++ b/data/removals/14_0/removal-legacy-storage.yml
@@ -1,4 +1,4 @@
-- name: "Legacy storage"
+- title: "Legacy storage"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: fzimmer
diff --git a/data/removals/14_0/removal-protect-features.yml b/data/removals/14_0/removal-protect-features.yml
index e8e71f7bbcd..71fcaddaa06 100644
--- a/data/removals/14_0/removal-protect-features.yml
+++ b/data/removals/14_0/removal-protect-features.yml
@@ -1,4 +1,4 @@
-- name: Container Scanning Engine Clair
+- title: Container Scanning Engine Clair
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: sam.white
@@ -6,7 +6,7 @@
body: |
Clair, the default container scanning engine, was deprecated in GitLab 13.9 and is removed from GitLab 14.0 and replaced by Trivy. We advise customers who are customizing variables for their container scanning job to [follow these instructions](https://docs.gitlab.com/ee/user/application_security/container_scanning/#change-scanners) to ensure that their container scanning jobs continue to work.
-- name: Web Application Firewall (WAF)
+- title: Web Application Firewall (WAF)
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: sam.white
diff --git a/data/removals/14_0/removal-sidekiq_experimental_queue_selector.yml b/data/removals/14_0/removal-sidekiq_experimental_queue_selector.yml
index d53e7744e3a..bbff9c56dda 100644
--- a/data/removals/14_0/removal-sidekiq_experimental_queue_selector.yml
+++ b/data/removals/14_0/removal-sidekiq_experimental_queue_selector.yml
@@ -1,4 +1,4 @@
-- name: Experimental prefix in Sidekiq queue selector options
+- title: Experimental prefix in Sidekiq queue selector options
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: smcgivern
diff --git a/data/removals/14_0/removal-unicorn.yml b/data/removals/14_0/removal-unicorn.yml
index d5062aa0eea..737c1fa53aa 100644
--- a/data/removals/14_0/removal-unicorn.yml
+++ b/data/removals/14_0/removal-unicorn.yml
@@ -1,4 +1,4 @@
-- name: "Unicorn in GitLab self-managed"
+- title: "Unicorn in GitLab self-managed"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: fzimmer
diff --git a/data/removals/14_0/removal_ci_project_config_path.yml b/data/removals/14_0/removal_ci_project_config_path.yml
index d71fbbb78f3..a54722c5923 100644
--- a/data/removals/14_0/removal_ci_project_config_path.yml
+++ b/data/removals/14_0/removal_ci_project_config_path.yml
@@ -1,4 +1,4 @@
-- name: "`CI_PROJECT_CONFIG_PATH` variable"
+- title: "`CI_PROJECT_CONFIG_PATH` variable"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: stkerr
diff --git a/data/removals/14_0/removal_enablement_helm2.yml b/data/removals/14_0/removal_enablement_helm2.yml
index 09136bead3e..57ed67851c3 100644
--- a/data/removals/14_0/removal_enablement_helm2.yml
+++ b/data/removals/14_0/removal_enablement_helm2.yml
@@ -1,4 +1,4 @@
-- name: "Helm v2 support"
+- title: "Helm v2 support"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: joshlambert
diff --git a/data/removals/14_0/removal_enablement_opensuse_15_1.yml b/data/removals/14_0/removal_enablement_opensuse_15_1.yml
index 2e0333c3b88..c9da3535f41 100644
--- a/data/removals/14_0/removal_enablement_opensuse_15_1.yml
+++ b/data/removals/14_0/removal_enablement_opensuse_15_1.yml
@@ -1,4 +1,4 @@
-- name: "OpenSUSE Leap 15.1"
+- title: "OpenSUSE Leap 15.1"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: dorrino
diff --git a/data/removals/14_0/removal_enablement_pg11.yml b/data/removals/14_0/removal_enablement_pg11.yml
index d409a1bc9d3..a7f9d3910be 100644
--- a/data/removals/14_0/removal_enablement_pg11.yml
+++ b/data/removals/14_0/removal_enablement_pg11.yml
@@ -1,4 +1,4 @@
-- name: "PostgreSQL 11 support"
+- title: "PostgreSQL 11 support"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: joshlambert
diff --git a/data/removals/14_0/removal_enablement_ubuntu_16.yml b/data/removals/14_0/removal_enablement_ubuntu_16.yml
index e67829de7ab..4c345c23e9c 100644
--- a/data/removals/14_0/removal_enablement_ubuntu_16.yml
+++ b/data/removals/14_0/removal_enablement_ubuntu_16.yml
@@ -1,4 +1,4 @@
-- name: "Ubuntu 16.04 support"
+- title: "Ubuntu 16.04 support"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: joshlambert
diff --git a/data/removals/14_0/removal_repost_static_analysis_notices.yml b/data/removals/14_0/removal_repost_static_analysis_notices.yml
index 1a2ae93a902..6ee59f72c4e 100644
--- a/data/removals/14_0/removal_repost_static_analysis_notices.yml
+++ b/data/removals/14_0/removal_repost_static_analysis_notices.yml
@@ -1,4 +1,4 @@
-- name: "Migrate from `SAST_DEFAULT_ANALYZERS` to `SAST_EXCLUDED_ANALYZERS`"
+- title: "Migrate from `SAST_DEFAULT_ANALYZERS` to `SAST_EXCLUDED_ANALYZERS`"
reporter: tmccaslin
removal_date: "2021-06-22"
removal_milestone: "14.0"
@@ -8,7 +8,7 @@
Until GitLab 13.9, if you wanted to avoid running one particular GitLab SAST analyzer, you needed to remove it from the [long string of analyzers in the `SAST.gitlab-ci.yml` file](https://gitlab.com/gitlab-org/gitlab/-/blob/390afc431e7ce1ac253b35beb39f19e49c746bff/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml#L12) and use that to set the [`SAST_DEFAULT_ANALYZERS`](https://docs.gitlab.com/ee/user/application_security/sast/#docker-images) variable in your project's CI file. If you did this, it would exclude you from future new analyzers because this string hard codes the list of analyzers to execute. We avoid this problem by inverting this variable's logic to exclude, rather than choose default analyzers.
Beginning with 13.9, [we migrated](https://gitlab.com/gitlab-org/gitlab/-/blob/14fed7a33bfdbd4663d8928e46002a5ef3e3282c/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml#L13) to `SAST_EXCLUDED_ANALYZERS` in our `SAST.gitlab-ci.yml` file. We encourage anyone who uses a [customized SAST configuration](https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings) in their project CI file to migrate to this new variable. If you have not overridden `SAST_DEFAULT_ANALYZERS`, no action is needed. The CI/CD variable `SAST_DEFAULT_ANALYZERS` has been removed in GitLab 14.0, which released on June 22, 2021.
-- name: "`secret_detection_default_branch` job"
+- title: "`secret_detection_default_branch` job"
reporter: tmccaslin
removal_date: "2021-06-22"
removal_milestone: "14.0"
@@ -18,7 +18,7 @@
To ensure Secret Detection was scanning both default branches and feature branches, we introduced two separate secret detection CI jobs (`secret_detection_default_branch` and `secret_detection`) in our managed [`Secret-Detection.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Secret-Detection.gitlab-ci.yml) template. These two CI jobs created confusion and complexity in the CI rules logic. This deprecation moves the `rule` logic into the `script` section, which then determines how the `secret_detection` job is run (historic, on a branch, commits, etc).
If you override or maintain custom versions of `SAST.gitlab-ci.yml` or `Secret-Detection.gitlab-ci.yml`, you must update your CI templates. We strongly encourage [inheriting and overriding our managed CI templates](https://docs.gitlab.com/ee/user/application_security/secret_detection/#custom-settings-example) to future-proof your CI templates. GitLab 14.0 no longer supports the old `secret_detection_default_branch` job.
-- name: "SAST analyzer `SAST_GOSEC_CONFIG` variable"
+- title: "SAST analyzer `SAST_GOSEC_CONFIG` variable"
reporter: tmccaslin
removal_date: "2021-06-22"
removal_milestone: "14.0"
@@ -28,7 +28,7 @@
With the release of [SAST Custom Rulesets](https://docs.gitlab.com/ee/user/application_security/sast/#customize-rulesets) in GitLab 13.5 we allow greater flexibility in configuration options for our Go analyzer (GoSec). As a result we no longer plan to support our less flexible [`SAST_GOSEC_CONFIG`](https://docs.gitlab.com/ee/user/application_security/sast/#analyzer-settings) analyzer setting. This variable was deprecated in GitLab 13.10.
GitLab 14.0 removes the old `SAST_GOSEC_CONFIG variable`. If you use or override `SAST_GOSEC_CONFIG` in your CI file, update your SAST CI configuration or pin to an older version of the GoSec analyzer. We strongly encourage [inheriting and overriding our managed CI templates](https://docs.gitlab.com/ee/user/application_security/sast/#overriding-sast-jobs) to future-proof your CI templates.
-- name: "Global `SAST_ANALYZER_IMAGE_TAG` in SAST CI template"
+- title: "Global `SAST_ANALYZER_IMAGE_TAG` in SAST CI template"
reporter: tmccaslin
removal_date: "2021-06-22"
removal_milestone: "14.0"
diff --git a/data/removals/14_0/removal_runner_25555.yml b/data/removals/14_0/removal_runner_25555.yml
index f775bd977bf..13c4d41989c 100644
--- a/data/removals/14_0/removal_runner_25555.yml
+++ b/data/removals/14_0/removal_runner_25555.yml
@@ -1,4 +1,4 @@
-- name: "Off peak time mode configuration for Docker Machine autoscaling"
+- title: "Off peak time mode configuration for Docker Machine autoscaling"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: deastman
diff --git a/data/removals/14_0/removal_runner_26036.yml b/data/removals/14_0/removal_runner_26036.yml
index 5f391909a2d..c806b2b54f1 100644
--- a/data/removals/14_0/removal_runner_26036.yml
+++ b/data/removals/14_0/removal_runner_26036.yml
@@ -1,4 +1,4 @@
-- name: "Ubuntu 19.10 (Eoan Ermine) package"
+- title: "Ubuntu 19.10 (Eoan Ermine) package"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: deastman
diff --git a/data/removals/14_0/removal_runner_26419.yml b/data/removals/14_0/removal_runner_26419.yml
index 85d57de763e..83690f807ba 100644
--- a/data/removals/14_0/removal_runner_26419.yml
+++ b/data/removals/14_0/removal_runner_26419.yml
@@ -1,4 +1,4 @@
-- name: "Make `pwsh` the default shell for newly-registered Windows Runners"
+- title: "Make `pwsh` the default shell for newly-registered Windows Runners"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: deastman
diff --git a/data/removals/14_0/removal_runner_4845.yml b/data/removals/14_0/removal_runner_4845.yml
index db25c0741cb..7aab67e3d41 100644
--- a/data/removals/14_0/removal_runner_4845.yml
+++ b/data/removals/14_0/removal_runner_4845.yml
@@ -1,4 +1,4 @@
-- name: "GitLab Runner installation to ignore the `skel` directory"
+- title: "GitLab Runner installation to ignore the `skel` directory"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: deastman
diff --git a/data/removals/14_0/removal_runner_6413.yml b/data/removals/14_0/removal_runner_6413.yml
index 514cb254d8d..e511b792fe7 100644
--- a/data/removals/14_0/removal_runner_6413.yml
+++ b/data/removals/14_0/removal_runner_6413.yml
@@ -1,4 +1,4 @@
-- name: "`FF_SHELL_EXECUTOR_USE_LEGACY_PROCESS_KILL` feature flag"
+- title: "`FF_SHELL_EXECUTOR_USE_LEGACY_PROCESS_KILL` feature flag"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: deastman
diff --git a/data/removals/14_0/removals-14-testing-team.yml b/data/removals/14_0/removals-14-testing-team.yml
index 8b877ece6cc..4146bfe916c 100644
--- a/data/removals/14_0/removals-14-testing-team.yml
+++ b/data/removals/14_0/removals-14-testing-team.yml
@@ -1,4 +1,4 @@
-- name: "Default Browser Performance testing job renamed in GitLab 14.0"
+- title: "Default Browser Performance testing job renamed in GitLab 14.0"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: jheimbuck_gl
@@ -7,7 +7,7 @@
Browser Performance Testing has run in a job named `performance` by default. With the introduction of [Load Performance Testing](https://docs.gitlab.com/ee/ci/testing/code_quality.html) in GitLab 13.2, this naming could be confusing. To make it clear which job is running [Browser Performance Testing](https://docs.gitlab.com/ee/ci/testing/browser_performance_testing.html), the default job name is changed from `performance` to `browser_performance` in the template in GitLab 14.0.
Relevant Issue: [Rename default Browser Performance Testing job](https://gitlab.com/gitlab-org/gitlab/-/issues/225914)
-- name: "Code Quality RuboCop support changed"
+- title: "Code Quality RuboCop support changed"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: jheimbuck_gl
@@ -16,7 +16,7 @@
By default, the Code Quality feature has not provided support for Ruby 2.6+ if you're using the Code Quality template. To better support the latest versions of Ruby, the default RuboCop version is updated to add support for Ruby 2.4 through 3.0. As a result, support for Ruby 2.1, 2.2, and 2.3 is removed. You can re-enable support for older versions by [customizing your configuration](https://docs.gitlab.com/ee/ci/testing/code_quality.html#rubocop-errors).
Relevant Issue: [Default `codeclimate-rubocop` engine does not support Ruby 2.6+](https://gitlab.com/gitlab-org/ci-cd/codequality/-/issues/28)
-- name: "Ruby version changed in `Ruby.gitlab-ci.yml`"
+- title: "Ruby version changed in `Ruby.gitlab-ci.yml`"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: jheimbuck_gl
diff --git a/data/removals/14_0/removals_runner_26651.yml b/data/removals/14_0/removals_runner_26651.yml
index 8a35996c81a..c2c0f88728a 100644
--- a/data/removals/14_0/removals_runner_26651.yml
+++ b/data/removals/14_0/removals_runner_26651.yml
@@ -1,4 +1,4 @@
-- name: "`/usr/lib/gitlab-runner` symlink from package"
+- title: "`/usr/lib/gitlab-runner` symlink from package"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: deastman
diff --git a/data/removals/14_0/removals_runner_26679.yml b/data/removals/14_0/removals_runner_26679.yml
index e99a551a293..1827992a801 100644
--- a/data/removals/14_0/removals_runner_26679.yml
+++ b/data/removals/14_0/removals_runner_26679.yml
@@ -1,4 +1,4 @@
-- name: "`FF_RESET_HELPER_IMAGE_ENTRYPOINT` feature flag"
+- title: "`FF_RESET_HELPER_IMAGE_ENTRYPOINT` feature flag"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: deastman
diff --git a/data/removals/14_0/removals_runner_26900.yml b/data/removals/14_0/removals_runner_26900.yml
index addf11c86ad..a823d797c1a 100644
--- a/data/removals/14_0/removals_runner_26900.yml
+++ b/data/removals/14_0/removals_runner_26900.yml
@@ -1,4 +1,4 @@
-- name: "Success and failure for finished build metric conversion"
+- title: "Success and failure for finished build metric conversion"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: deastman
diff --git a/data/removals/14_0/removals_runner_27175.yml b/data/removals/14_0/removals_runner_27175.yml
index a31c4e757e7..c164f50a973 100644
--- a/data/removals/14_0/removals_runner_27175.yml
+++ b/data/removals/14_0/removals_runner_27175.yml
@@ -1,4 +1,4 @@
-- name: "`FF_USE_GO_CLOUD_WITH_CACHE_ARCHIVER` feature flag"
+- title: "`FF_USE_GO_CLOUD_WITH_CACHE_ARCHIVER` feature flag"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: deastman
diff --git a/data/removals/14_0/removals_runner_27218.yml b/data/removals/14_0/removals_runner_27218.yml
index 8fb7e800b0b..8c18551d68b 100644
--- a/data/removals/14_0/removals_runner_27218.yml
+++ b/data/removals/14_0/removals_runner_27218.yml
@@ -1,4 +1,4 @@
-- name: "GitLab Runner helper image in GitLab.com Container Registry"
+- title: "GitLab Runner helper image in GitLab.com Container Registry"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: deastman
diff --git a/data/removals/14_0/removals_runner_27551.yml b/data/removals/14_0/removals_runner_27551.yml
index 43ecf3d72f6..0165a541420 100644
--- a/data/removals/14_0/removals_runner_27551.yml
+++ b/data/removals/14_0/removals_runner_27551.yml
@@ -1,4 +1,4 @@
-- name: "Windows Server 1903 image support"
+- title: "Windows Server 1903 image support"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: deastman
diff --git a/data/removals/14_0/removals_runner_27899.yml b/data/removals/14_0/removals_runner_27899.yml
index 468e04dc677..8eef3009df8 100644
--- a/data/removals/14_0/removals_runner_27899.yml
+++ b/data/removals/14_0/removals_runner_27899.yml
@@ -1,4 +1,4 @@
-- name: "Windows Server 1909 image support"
+- title: "Windows Server 1909 image support"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: deastman
diff --git a/data/removals/14_0/remove-sql-elector.yml b/data/removals/14_0/remove-sql-elector.yml
index 6a306569d9f..2628f23129c 100644
--- a/data/removals/14_0/remove-sql-elector.yml
+++ b/data/removals/14_0/remove-sql-elector.yml
@@ -1,4 +1,4 @@
-- name: "Gitaly Cluster SQL primary elector"
+- title: "Gitaly Cluster SQL primary elector"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: mjwood
diff --git a/data/removals/14_0/remove_dast_env_variables.yml b/data/removals/14_0/remove_dast_env_variables.yml
index 3a40c6518e0..8ed23aae6fa 100644
--- a/data/removals/14_0/remove_dast_env_variables.yml
+++ b/data/removals/14_0/remove_dast_env_variables.yml
@@ -1,4 +1,4 @@
-- name: "DAST environment variable renaming and removal"
+- title: "DAST environment variable renaming and removal"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: derekferguson
diff --git a/data/removals/14_0/remove_dast_legacy_domain_validation.yml b/data/removals/14_0/remove_dast_legacy_domain_validation.yml
index 39acc3e7188..ffb2ec1118e 100644
--- a/data/removals/14_0/remove_dast_legacy_domain_validation.yml
+++ b/data/removals/14_0/remove_dast_legacy_domain_validation.yml
@@ -1,4 +1,4 @@
-- name: "Legacy DAST domain validation"
+- title: "Legacy DAST domain validation"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: derekferguson
diff --git a/data/removals/14_0/remove_dast_legacy_report_fields.yml b/data/removals/14_0/remove_dast_legacy_report_fields.yml
index e5fca1fa256..cf885ca521d 100644
--- a/data/removals/14_0/remove_dast_legacy_report_fields.yml
+++ b/data/removals/14_0/remove_dast_legacy_report_fields.yml
@@ -1,4 +1,4 @@
-- name: "Legacy fields from DAST report"
+- title: "Legacy fields from DAST report"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: derekferguson
diff --git a/data/removals/14_0/remove_dast_spider_host_reset.yml b/data/removals/14_0/remove_dast_spider_host_reset.yml
index 53122f6071f..6a44a70655e 100644
--- a/data/removals/14_0/remove_dast_spider_host_reset.yml
+++ b/data/removals/14_0/remove_dast_spider_host_reset.yml
@@ -1,4 +1,4 @@
-- name: "Default DAST spider begins crawling at target URL"
+- title: "Default DAST spider begins crawling at target URL"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: derekferguson
diff --git a/data/removals/14_0/remove_dast_template_stages.yml b/data/removals/14_0/remove_dast_template_stages.yml
index 0995e09c3ed..4a7c9f13fc8 100644
--- a/data/removals/14_0/remove_dast_template_stages.yml
+++ b/data/removals/14_0/remove_dast_template_stages.yml
@@ -1,4 +1,4 @@
-- name: "DAST default template stages"
+- title: "DAST default template stages"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: derekferguson
diff --git a/data/removals/14_0/remove_optimize_api.yml b/data/removals/14_0/remove_optimize_api.yml
index a472e6a0d59..aa50a4ce7d2 100644
--- a/data/removals/14_0/remove_optimize_api.yml
+++ b/data/removals/14_0/remove_optimize_api.yml
@@ -1,4 +1,4 @@
-- name: "DevOps Adoption API Segments"
+- title: "DevOps Adoption API Segments"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: ljlane
diff --git a/data/removals/14_0/remove_terraform_template.yml b/data/removals/14_0/remove_terraform_template.yml
index 2d78eb1f59c..77fade19fc5 100644
--- a/data/removals/14_0/remove_terraform_template.yml
+++ b/data/removals/14_0/remove_terraform_template.yml
@@ -1,4 +1,4 @@
-- name: "Breaking changes to Terraform CI template"
+- title: "Breaking changes to Terraform CI template"
reporter: nagyv-gitlab
removal_date: "2021-06-22"
removal_milestone: "14.0"
diff --git a/data/removals/14_0/verify-ci-removal-parametertrace.yml b/data/removals/14_0/verify-ci-removal-parametertrace.yml
index 8822abaf357..7dd74bac53c 100644
--- a/data/removals/14_0/verify-ci-removal-parametertrace.yml
+++ b/data/removals/14_0/verify-ci-removal-parametertrace.yml
@@ -1,4 +1,4 @@
-- name: "`trace` parameter in `jobs` API"
+- title: "`trace` parameter in `jobs` API"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: jreporter
diff --git a/data/removals/14_0/verify-ci-removalpipelineservice.yml b/data/removals/14_0/verify-ci-removalpipelineservice.yml
index fe3d3f224d1..74c10f8fbbe 100644
--- a/data/removals/14_0/verify-ci-removalpipelineservice.yml
+++ b/data/removals/14_0/verify-ci-removalpipelineservice.yml
@@ -1,4 +1,4 @@
-- name: "External Pipeline Validation Service Code Changes"
+- title: "External Pipeline Validation Service Code Changes"
removal_date: "2021-06-22"
removal_milestone: "14.0"
reporter: jreporter
diff --git a/data/removals/14_1/removal-memory-prometheus-options-source.yml b/data/removals/14_1/removal-memory-prometheus-options-source.yml
index 57a5dfa5d69..a98dc74bbaf 100644
--- a/data/removals/14_1/removal-memory-prometheus-options-source.yml
+++ b/data/removals/14_1/removal-memory-prometheus-options-source.yml
@@ -1,4 +1,4 @@
-- name: "Remove support for `prometheus.listen_address` and `prometheus.enable`"
+- title: "Remove support for `prometheus.listen_address` and `prometheus.enable`"
removal_date: July 22, 2021
removal_milestone: "14.1"
reporter: fzimmer
diff --git a/data/removals/14_1/removal-outdated-browser-support.yml b/data/removals/14_1/removal-outdated-browser-support.yml
index 281b8d65686..8ef732564a8 100644
--- a/data/removals/14_1/removal-outdated-browser-support.yml
+++ b/data/removals/14_1/removal-outdated-browser-support.yml
@@ -1,4 +1,4 @@
-- name: "Remove support for older browsers"
+- title: "Remove support for older browsers"
removal_date: July 22, 2021
removal_milestone: "14.1"
reporter: leipert
diff --git a/data/removals/14_10/14-10-package-permissions-composer-change.yml b/data/removals/14_10/14-10-package-permissions-composer-change.yml
index e44f1c55167..47e1502c466 100644
--- a/data/removals/14_10/14-10-package-permissions-composer-change.yml
+++ b/data/removals/14_10/14-10-package-permissions-composer-change.yml
@@ -1,4 +1,4 @@
-- name: "Permissions change for downloading Composer dependencies"
+- title: "Permissions change for downloading Composer dependencies"
announcement_milestone: "14.9"
announcement_date: "2022-03-22"
removal_milestone: "14.10"
diff --git a/data/removals/14_2/removal-verify-build-log.yml b/data/removals/14_2/removal-verify-build-log.yml
index f80971b6f4d..8ef41231543 100644
--- a/data/removals/14_2/removal-verify-build-log.yml
+++ b/data/removals/14_2/removal-verify-build-log.yml
@@ -1,4 +1,4 @@
-- name: "Max job log file size of 100 MB"
+- title: "Max job log file size of 100 MB"
removal_date: August 22, 2021 # day the removal was released
removal_milestone: "14.2"
reporter: jreporter # GitLab username of the person reporting the removal
diff --git a/data/removals/14_3/removal-limit-tags-to-50.yml b/data/removals/14_3/removal-limit-tags-to-50.yml
index 24dcddf6955..b75fa440ff6 100644
--- a/data/removals/14_3/removal-limit-tags-to-50.yml
+++ b/data/removals/14_3/removal-limit-tags-to-50.yml
@@ -1,4 +1,4 @@
-- name: "Introduced limit of 50 tags for jobs"
+- title: "Introduced limit of 50 tags for jobs"
removal_date: September 22nd, 2021
removal_milestone: "14.3"
reporter: jreporter
diff --git a/data/removals/14_3/removal-verify-pe-pipelinefindername.yml b/data/removals/14_3/removal-verify-pe-pipelinefindername.yml
index e3ad364ce29..ad069308045 100644
--- a/data/removals/14_3/removal-verify-pe-pipelinefindername.yml
+++ b/data/removals/14_3/removal-verify-pe-pipelinefindername.yml
@@ -1,4 +1,4 @@
-- name: "List project pipelines API endpoint removes `name` support in 14.3"
+- title: "List project pipelines API endpoint removes `name` support in 14.3"
removal_date: September 22, 2021 # day the removal was released
removal_milestone: "14.3"
reporter: jreporter # GitLab username of the person reporting the removal
diff --git a/data/removals/14_3/removal_legacy_storage_setting.yml b/data/removals/14_3/removal_legacy_storage_setting.yml
index 1a635dc9b0a..ded32188e56 100644
--- a/data/removals/14_3/removal_legacy_storage_setting.yml
+++ b/data/removals/14_3/removal_legacy_storage_setting.yml
@@ -1,4 +1,4 @@
-- name: Use of legacy storage setting
+- title: Use of legacy storage setting
removal_date: September 22nd, 2021 # day the removal was released
removal_milestone: "14.3"
reporter: dorrino # GitLab username of the person reporting the removal
diff --git a/data/removals/14_6/limit_trigger_pipelines.yml b/data/removals/14_6/limit_trigger_pipelines.yml
index 668d887bb0b..f893ebf7643 100644
--- a/data/removals/14_6/limit_trigger_pipelines.yml
+++ b/data/removals/14_6/limit_trigger_pipelines.yml
@@ -1,4 +1,4 @@
-- name: "Limit the number of triggered pipeline to 25K in free tier"
+- title: "Limit the number of triggered pipeline to 25K in free tier"
removal_date: Dec 22, 2021 # day the removal was released
removal_milestone: "14.6"
reporter: dhershkovitch # GitLab username of the person reporting the removal
diff --git a/data/removals/14_6/removal-release-cli-s3.yml b/data/removals/14_6/removal-release-cli-s3.yml
index 7f37f017431..458c027c4c0 100644
--- a/data/removals/14_6/removal-release-cli-s3.yml
+++ b/data/removals/14_6/removal-release-cli-s3.yml
@@ -1,4 +1,4 @@
-- name: "Release CLI distributed as a generic package"
+- title: "Release CLI distributed as a generic package"
removal_date: Dec 22, 2021 # day the removal was released
removal_milestone: "14.6"
reporter: kbychu # GitLab username of the person reporting the removal
diff --git a/data/removals/14_9/removal_monitor_respond_integrated_error_tracking.yml b/data/removals/14_9/removal_monitor_respond_integrated_error_tracking.yml
index a0467a56f4f..93824a4e68b 100644
--- a/data/removals/14_9/removal_monitor_respond_integrated_error_tracking.yml
+++ b/data/removals/14_9/removal_monitor_respond_integrated_error_tracking.yml
@@ -1,4 +1,4 @@
-- name: "Integrated error tracking disabled by default"
+- title: "Integrated error tracking disabled by default"
announcement_milestone: "14.9"
announcement_date: "2022-02-23" # This is the date customers were notified about the change in rate limits, making integrated error tracking unusable, see https://gitlab.com/groups/gitlab-org/-/epics/7580#communication-to-rate-limit-impacted-users
removal_milestone: "14.9"
diff --git a/data/removals/15_0/15-0-Legacy-approval-status-names-from-License-Compliance-API.yml b/data/removals/15_0/15-0-Legacy-approval-status-names-from-License-Compliance-API.yml
index 4a241f913a1..81e9df8d761 100644
--- a/data/removals/15_0/15-0-Legacy-approval-status-names-from-License-Compliance-API.yml
+++ b/data/removals/15_0/15-0-Legacy-approval-status-names-from-License-Compliance-API.yml
@@ -1,4 +1,4 @@
-- name: "Legacy approval status names in License Compliance API"
+- title: "Legacy approval status names in License Compliance API"
announcement_milestone: "14.6"
announcement_date: "2021-12-13"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-Pseudonymizer.yml b/data/removals/15_0/15-0-Pseudonymizer.yml
index f3ad893e5dc..6d0bb6e358c 100644
--- a/data/removals/15_0/15-0-Pseudonymizer.yml
+++ b/data/removals/15_0/15-0-Pseudonymizer.yml
@@ -1,4 +1,4 @@
-- name: "Pseudonymizer"
+- title: "Pseudonymizer"
announcement_milestone: "14.7"
announcement_date: "2022-01-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-Retire-js-analyzer.yml b/data/removals/15_0/15-0-Retire-js-analyzer.yml
index 5b5d38d039c..fa744ca1951 100644
--- a/data/removals/15_0/15-0-Retire-js-analyzer.yml
+++ b/data/removals/15_0/15-0-Retire-js-analyzer.yml
@@ -1,4 +1,4 @@
-- name: "Retire-JS Dependency Scanning tool"
+- title: "Retire-JS Dependency Scanning tool"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-SLES-12-SP2.yml b/data/removals/15_0/15-0-SLES-12-SP2.yml
index 40c51fa5f95..b6499455574 100644
--- a/data/removals/15_0/15-0-SLES-12-SP2.yml
+++ b/data/removals/15_0/15-0-SLES-12-SP2.yml
@@ -1,4 +1,4 @@
-- name: "SUSE Linux Enterprise Server 12 SP2"
+- title: "SUSE Linux Enterprise Server 12 SP2"
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22"
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
diff --git a/data/removals/15_0/15-0-advanced-search-elasticsearch-6-8.yml b/data/removals/15_0/15-0-advanced-search-elasticsearch-6-8.yml
index e40e48ba9e9..d920c54a786 100644
--- a/data/removals/15_0/15-0-advanced-search-elasticsearch-6-8.yml
+++ b/data/removals/15_0/15-0-advanced-search-elasticsearch-6-8.yml
@@ -1,4 +1,4 @@
-- name: "Elasticsearch 6.8.x in GitLab 15.0"
+- title: "Elasticsearch 6.8.x in GitLab 15.0"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-bundler-audit.yml b/data/removals/15_0/15-0-bundler-audit.yml
index 991280585ba..397b18580c6 100644
--- a/data/removals/15_0/15-0-bundler-audit.yml
+++ b/data/removals/15_0/15-0-bundler-audit.yml
@@ -1,4 +1,4 @@
-- name: "bundler-audit Dependency Scanning tool"
+- title: "bundler-audit Dependency Scanning tool"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-configure-self-managed-cert-based-kube-feature-flag.yml b/data/removals/15_0/15-0-configure-self-managed-cert-based-kube-feature-flag.yml
index a4b8b422dd9..48152a06eb3 100644
--- a/data/removals/15_0/15-0-configure-self-managed-cert-based-kube-feature-flag.yml
+++ b/data/removals/15_0/15-0-configure-self-managed-cert-based-kube-feature-flag.yml
@@ -1,4 +1,4 @@
-- name: "Self-managed certificate-based integration with Kubernetes feature flagged"
+- title: "Self-managed certificate-based integration with Kubernetes feature flagged"
announcement_milestone: "14.5"
announcement_date: "2021-11-15"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-container-registry-htpasswd.yml b/data/removals/15_0/15-0-container-registry-htpasswd.yml
index e9a4b2e9882..78a28c4e615 100644
--- a/data/removals/15_0/15-0-container-registry-htpasswd.yml
+++ b/data/removals/15_0/15-0-container-registry-htpasswd.yml
@@ -1,4 +1,4 @@
-- name: "Container registry authentication with htpasswd"
+- title: "Container registry authentication with htpasswd"
announcement_milestone: "14.9"
announcement_date: "2022-03-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-custom_hooks_dir.yml b/data/removals/15_0/15-0-custom_hooks_dir.yml
index 2c116a55e1c..55f63c9bef7 100644
--- a/data/removals/15_0/15-0-custom_hooks_dir.yml
+++ b/data/removals/15_0/15-0-custom_hooks_dir.yml
@@ -1,4 +1,4 @@
-- name: "Move `custom_hooks_dir` setting from GitLab Shell to Gitaly"
+- title: "Move `custom_hooks_dir` setting from GitLab Shell to Gitaly"
announcement_milestone: "14.9"
announcement_date: "2022-03-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-database-deprecate-legacy-database-conf.yml b/data/removals/15_0/15-0-database-deprecate-legacy-database-conf.yml
index 37595f8808d..4b203b61649 100644
--- a/data/removals/15_0/15-0-database-deprecate-legacy-database-conf.yml
+++ b/data/removals/15_0/15-0-database-deprecate-legacy-database-conf.yml
@@ -1,4 +1,4 @@
-- name: "Support for legacy format of `config/database.yml`"
+- title: "Support for legacy format of `config/database.yml`"
announcement_milestone: "14.3"
announcement_date: "2021-09-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-dependency-scanning-default-java-version.yml b/data/removals/15_0/15-0-dependency-scanning-default-java-version.yml
index dee3b5093fc..8576b1e0deb 100644
--- a/data/removals/15_0/15-0-dependency-scanning-default-java-version.yml
+++ b/data/removals/15_0/15-0-dependency-scanning-default-java-version.yml
@@ -1,4 +1,4 @@
-- name: "Dependency Scanning default Java version changed to 17"
+- title: "Dependency Scanning default Java version changed to 17"
announcement_milestone: "14.10"
announcement_date: "2022-04-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-dependency-scanning-python-image.yml b/data/removals/15_0/15-0-dependency-scanning-python-image.yml
index cbb1e32a211..1ffd3a4b78e 100644
--- a/data/removals/15_0/15-0-dependency-scanning-python-image.yml
+++ b/data/removals/15_0/15-0-dependency-scanning-python-image.yml
@@ -1,4 +1,4 @@
-- name: "End of support for Python 3.6 in Dependency Scanning"
+- title: "End of support for Python 3.6 in Dependency Scanning"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-ds-default-analyzers.yml b/data/removals/15_0/15-0-ds-default-analyzers.yml
index 702ce854d0d..a1cbed6366b 100644
--- a/data/removals/15_0/15-0-ds-default-analyzers.yml
+++ b/data/removals/15_0/15-0-ds-default-analyzers.yml
@@ -1,4 +1,4 @@
-- name: "DS_DEFAULT_ANALYZERS environment variable"
+- title: "DS_DEFAULT_ANALYZERS environment variable"
announcement_milestone: "14.0"
announcement_date: "2021-06-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-geo-remove-db-rake-tasks.yml b/data/removals/15_0/15-0-geo-remove-db-rake-tasks.yml
index 571217f9c92..66d50a7ba67 100644
--- a/data/removals/15_0/15-0-geo-remove-db-rake-tasks.yml
+++ b/data/removals/15_0/15-0-geo-remove-db-rake-tasks.yml
@@ -1,4 +1,4 @@
-- name: "Custom `geo:db:*` Rake tasks are no longer available"
+- title: "Custom `geo:db:*` Rake tasks are no longer available"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-geo-remove-promote-db.yml b/data/removals/15_0/15-0-geo-remove-promote-db.yml
index 65fa8fdff8e..73459b04515 100644
--- a/data/removals/15_0/15-0-geo-remove-promote-db.yml
+++ b/data/removals/15_0/15-0-geo-remove-promote-db.yml
@@ -1,4 +1,4 @@
-- name: "The `promote-db` command is no longer available from `gitlab-ctl`"
+- title: "The `promote-db` command is no longer available from `gitlab-ctl`"
announcement_milestone: "14.5"
announcement_date: "2021-11-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-geo-remove-promote-to-primary-node.yml b/data/removals/15_0/15-0-geo-remove-promote-to-primary-node.yml
index 1eb0c1658a2..38e1a87104f 100644
--- a/data/removals/15_0/15-0-geo-remove-promote-to-primary-node.yml
+++ b/data/removals/15_0/15-0-geo-remove-promote-to-primary-node.yml
@@ -1,4 +1,4 @@
-- name: "`promote-to-primary-node` command from `gitlab-ctl`"
+- title: "`promote-to-primary-node` command from `gitlab-ctl`"
announcement_milestone: "14.5"
announcement_date: "2021-11-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-gitaly-internal-socket-dir.yml b/data/removals/15_0/15-0-gitaly-internal-socket-dir.yml
index eedb50d2205..509d034263c 100644
--- a/data/removals/15_0/15-0-gitaly-internal-socket-dir.yml
+++ b/data/removals/15_0/15-0-gitaly-internal-socket-dir.yml
@@ -1,4 +1,4 @@
-- name: "Support for `gitaly['internal_socket_dir']`"
+- title: "Support for `gitaly['internal_socket_dir']`"
announcement_milestone: "14.10"
announcement_date: "2022-04-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-managed-cluster-applications.yml b/data/removals/15_0/15-0-managed-cluster-applications.yml
index b0e4bae8852..74fd72d5d2d 100644
--- a/data/removals/15_0/15-0-managed-cluster-applications.yml
+++ b/data/removals/15_0/15-0-managed-cluster-applications.yml
@@ -1,4 +1,4 @@
-- name: "`Managed-Cluster-Applications.gitlab-ci.yml`"
+- title: "`Managed-Cluster-Applications.gitlab-ci.yml`"
announcement_milestone: "14.0"
announcement_date: "2021-06-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-merge-commit-message.yml b/data/removals/15_0/15-0-merge-commit-message.yml
index ff6acd62304..5e2f82f1fa0 100644
--- a/data/removals/15_0/15-0-merge-commit-message.yml
+++ b/data/removals/15_0/15-0-merge-commit-message.yml
@@ -1,4 +1,4 @@
-- name: "`defaultMergeCommitMessageWithDescription` GraphQL API field"
+- title: "`defaultMergeCommitMessageWithDescription` GraphQL API field"
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/removals/15_0/15-0-oauth-implicit-grant.yml b/data/removals/15_0/15-0-oauth-implicit-grant.yml
index 4a8cd53dc13..73e4fdae0f5 100644
--- a/data/removals/15_0/15-0-oauth-implicit-grant.yml
+++ b/data/removals/15_0/15-0-oauth-implicit-grant.yml
@@ -1,4 +1,4 @@
-- name: "OAuth implicit grant" # the name of the feature being removed. Avoid the words `deprecation`, `deprecate`, `removal`, and `remove` in this field because these are implied.
+- title: "OAuth implicit grant" # the name of the feature being removed. Avoid the words `deprecation`, `deprecate`, `removal`, and `remove` in this field because these are implied.
announcement_milestone: "14.0" # The milestone when this feature was deprecated.
announcement_date: "2021-06-22" # The date of the milestone release when this feature was deprecated. This should almost always be the 22nd of a month (YYYY-MM-DD), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is being removed.
diff --git a/data/removals/15_0/15-0-oauth-tokens-no-expiry.yml b/data/removals/15_0/15-0-oauth-tokens-no-expiry.yml
index f93b8253ec7..54f60cc64d2 100644
--- a/data/removals/15_0/15-0-oauth-tokens-no-expiry.yml
+++ b/data/removals/15_0/15-0-oauth-tokens-no-expiry.yml
@@ -1,4 +1,4 @@
-- name: "OAuth tokens without an expiration"
+- title: "OAuth tokens without an expiration"
announcement_milestone: "14.3"
announcement_date: "2021-09-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-omniauth-kerberos-gem.yml b/data/removals/15_0/15-0-omniauth-kerberos-gem.yml
index fa005ff0404..cf8e9b8ea50 100644
--- a/data/removals/15_0/15-0-omniauth-kerberos-gem.yml
+++ b/data/removals/15_0/15-0-omniauth-kerberos-gem.yml
@@ -1,4 +1,4 @@
-- name: "`omniauth-kerberos` gem" # the name of the feature being removed. Avoid the words `deprecation`, `deprecate`, `removal`, and `remove` in this field because these are implied.
+- title: "`omniauth-kerberos` gem" # the name of the feature being removed. Avoid the words `deprecation`, `deprecate`, `removal`, and `remove` in this field because these are implied.
announcement_milestone: "14.3" # The milestone when this feature was deprecated.
announcement_date: "2021-09-22" # The date of the milestone release when this feature was deprecated. This should almost always be the 22nd of a month (YYYY-MM-DD), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is being removed.
diff --git a/data/removals/15_0/15-0-package-container-registry-group-api.yml b/data/removals/15_0/15-0-package-container-registry-group-api.yml
index 4d992c6c75f..0adba1afa0b 100644
--- a/data/removals/15_0/15-0-package-container-registry-group-api.yml
+++ b/data/removals/15_0/15-0-package-container-registry-group-api.yml
@@ -1,4 +1,4 @@
-- name: "Update to the Container Registry group-level API"
+- title: "Update to the Container Registry group-level API"
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22"
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
diff --git a/data/removals/15_0/15-0-package-settings-permissions.yml b/data/removals/15_0/15-0-package-settings-permissions.yml
index 5a458b588b8..0c649439459 100644
--- a/data/removals/15_0/15-0-package-settings-permissions.yml
+++ b/data/removals/15_0/15-0-package-settings-permissions.yml
@@ -1,4 +1,4 @@
-- name: "GraphQL permissions change for Package settings"
+- title: "GraphQL permissions change for Package settings"
announcement_milestone: "14.9"
announcement_date: "2022-03-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-praefect-database-no-proxy.yml b/data/removals/15_0/15-0-praefect-database-no-proxy.yml
index b363decc68e..d25eda37612 100644
--- a/data/removals/15_0/15-0-praefect-database-no-proxy.yml
+++ b/data/removals/15_0/15-0-praefect-database-no-proxy.yml
@@ -1,4 +1,4 @@
-- name: "Move Gitaly Cluster Praefect `database_host_no_proxy` and `database_port_no_proxy configs`"
+- title: "Move Gitaly Cluster Praefect `database_host_no_proxy` and `database_port_no_proxy configs`"
announcement_milestone: "14.0"
announcement_date: "2021-05-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-praefect-virtual-storage.yml b/data/removals/15_0/15-0-praefect-virtual-storage.yml
index a1ffa20bcfd..2b37a324001 100644
--- a/data/removals/15_0/15-0-praefect-virtual-storage.yml
+++ b/data/removals/15_0/15-0-praefect-virtual-storage.yml
@@ -1,4 +1,4 @@
-- name: "Gitaly nodes in virtual storage"
+- title: "Gitaly nodes in virtual storage"
announcement_milestone: "13.12" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-04-22"
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
diff --git a/data/removals/15_0/15-0-protect-cns-chs.yml b/data/removals/15_0/15-0-protect-cns-chs.yml
index 470f809efa1..bdf509dba33 100644
--- a/data/removals/15_0/15-0-protect-cns-chs.yml
+++ b/data/removals/15_0/15-0-protect-cns-chs.yml
@@ -1,4 +1,4 @@
-- name: "Container Network and Host Security"
+- title: "Container Network and Host Security"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-protect-vulnerability-check.yml b/data/removals/15_0/15-0-protect-vulnerability-check.yml
index 91052457612..dc448f3fb54 100644
--- a/data/removals/15_0/15-0-protect-vulnerability-check.yml
+++ b/data/removals/15_0/15-0-protect-vulnerability-check.yml
@@ -1,4 +1,4 @@
-- name: "Vulnerability Check"
+- title: "Vulnerability Check"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-removal-artifacts-keyword.yml b/data/removals/15_0/15-0-removal-artifacts-keyword.yml
index 15c9a5ee27a..6f5eb4804d7 100644
--- a/data/removals/15_0/15-0-removal-artifacts-keyword.yml
+++ b/data/removals/15_0/15-0-removal-artifacts-keyword.yml
@@ -1,4 +1,4 @@
-- name: "`artifacts:reports:cobertura` keyword"
+- title: "`artifacts:reports:cobertura` keyword"
announcement_milestone: "14.7"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-removal-testcoveragesetting.yml b/data/removals/15_0/15-0-removal-testcoveragesetting.yml
index bd72a28f0db..67dd7aed8ce 100644
--- a/data/removals/15_0/15-0-removal-testcoveragesetting.yml
+++ b/data/removals/15_0/15-0-removal-testcoveragesetting.yml
@@ -1,4 +1,4 @@
-- name: "Test coverage project CI/CD setting" # The headline announcing the removal. i.e. "`CI_PROJECT_CONFIG_PATH` removed in Gitlab 14.0"
+- title: "Test coverage project CI/CD setting" # The headline announcing the removal. i.e. "`CI_PROJECT_CONFIG_PATH` removed in Gitlab 14.0"
announcement_milestone: "14.8" # The milestone when this feature was deprecated.
announcement_date: "2022-03-22" # The date of the milestone release when this feature was deprecated. This should almost always be the 22nd of a month (YYYY-MM-DD), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is being removed.
diff --git a/data/removals/15_0/15-0-remove-background-upload-object-storage.yml b/data/removals/15_0/15-0-remove-background-upload-object-storage.yml
index dac96032359..ca9fcc408ba 100644
--- a/data/removals/15_0/15-0-remove-background-upload-object-storage.yml
+++ b/data/removals/15_0/15-0-remove-background-upload-object-storage.yml
@@ -1,4 +1,4 @@
-- name: "Background upload for object storage"
+- title: "Background upload for object storage"
announcement_milestone: "14.9"
announcement_date: "2022-03-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-remove-dependency-proxy-feature-flag.yml b/data/removals/15_0/15-0-remove-dependency-proxy-feature-flag.yml
index 577789e7fd2..f8f1cd62966 100644
--- a/data/removals/15_0/15-0-remove-dependency-proxy-feature-flag.yml
+++ b/data/removals/15_0/15-0-remove-dependency-proxy-feature-flag.yml
@@ -1,4 +1,4 @@
-- name: "`dependency_proxy_for_private_groups` feature flag" # The name of the feature to be deprecated
+- title: "`dependency_proxy_for_private_groups` feature flag" # The name of the feature to be deprecated
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/removals/15_0/15-0-remove-replicaiton-detail-routes.yml b/data/removals/15_0/15-0-remove-replicaiton-detail-routes.yml
index 133a7464a91..59775a66534 100644
--- a/data/removals/15_0/15-0-remove-replicaiton-detail-routes.yml
+++ b/data/removals/15_0/15-0-remove-replicaiton-detail-routes.yml
@@ -1,4 +1,4 @@
-- name: "Legacy Geo Admin UI routes"
+- title: "Legacy Geo Admin UI routes"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-remove-versions-packagetype.yml b/data/removals/15_0/15-0-remove-versions-packagetype.yml
index de45444b64a..0e1155d3067 100644
--- a/data/removals/15_0/15-0-remove-versions-packagetype.yml
+++ b/data/removals/15_0/15-0-remove-versions-packagetype.yml
@@ -1,4 +1,4 @@
-- name: "Versions from `PackageType`"
+- title: "Versions from `PackageType`"
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/removals/15_0/15-0-remove_ff_push_rules_supersede_code_owners.yml b/data/removals/15_0/15-0-remove_ff_push_rules_supersede_code_owners.yml
index 61a482fe73f..4dd658eb195 100644
--- a/data/removals/15_0/15-0-remove_ff_push_rules_supersede_code_owners.yml
+++ b/data/removals/15_0/15-0-remove_ff_push_rules_supersede_code_owners.yml
@@ -1,4 +1,4 @@
-- name: "`push_rules_supersede_code_owners` feature flag" # The name of the feature to be deprecated
+- title: "`push_rules_supersede_code_owners` feature flag" # The name of the feature to be deprecated
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
@@ -6,7 +6,7 @@
breaking_change: true # If this deprecation is a breaking change, set this value to true
reporter: tlinz # GitLab username of the person reporting the deprecation
body: | # Do not modify this line, instead modify the lines below.
- The `push_rules_supersede_code_owners` feature flag has been removed in GitLab 15.0. From now on, push rules will supersede the `CODEOWNERS` file. The code owners feature is no longer available for access control.
+ The `push_rules_supersede_code_owners` feature flag has been removed in GitLab 15.0. From now on, push rules will supersede the `CODEOWNERS` file. Even if Code Owner approval is required, a push rule that explicitly allows a specific user to push code supersedes the Code Owners setting.
# The following items are not published on the docs page, but may be used in the future.
stage: create # (optional - may be required in the future) String value of the stage that the feature was created in. e.g., Growth
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
diff --git a/data/removals/15_0/15-0-request-profiling.yml b/data/removals/15_0/15-0-request-profiling.yml
index f5cea4e0d11..16f4f09e1c1 100644
--- a/data/removals/15_0/15-0-request-profiling.yml
+++ b/data/removals/15_0/15-0-request-profiling.yml
@@ -1,4 +1,4 @@
-- name: "Request profiling"
+- title: "Request profiling"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-runner-api-status-renames-not_connected.yml b/data/removals/15_0/15-0-runner-api-status-renames-not_connected.yml
index 9406936a2eb..32de20469bf 100644
--- a/data/removals/15_0/15-0-runner-api-status-renames-not_connected.yml
+++ b/data/removals/15_0/15-0-runner-api-status-renames-not_connected.yml
@@ -1,4 +1,4 @@
-- name: "Runner status `not_connected` API value"
+- title: "Runner status `not_connected` API value"
announcement_milestone: "14.6" # The milestone when this feature was first announced as deprecated.
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
removal_date: "2022-05-22"
diff --git a/data/removals/15_0/15-0-runner-disable-strict-host-key-check.yml b/data/removals/15_0/15-0-runner-disable-strict-host-key-check.yml
index 384d025ad66..a8ec1080766 100644
--- a/data/removals/15_0/15-0-runner-disable-strict-host-key-check.yml
+++ b/data/removals/15_0/15-0-runner-disable-strict-host-key-check.yml
@@ -1,4 +1,4 @@
-- name: "Known host required for GitLab Runner SSH executor"
+- title: "Known host required for GitLab Runner SSH executor"
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
removal_date: "2022-05-22"
diff --git a/data/removals/15_0/15-0-runner_api_new_stale_status_breaking_change.yml b/data/removals/15_0/15-0-runner_api_new_stale_status_breaking_change.yml
index 182c492f17b..1d4c79bc40e 100644
--- a/data/removals/15_0/15-0-runner_api_new_stale_status_breaking_change.yml
+++ b/data/removals/15_0/15-0-runner_api_new_stale_status_breaking_change.yml
@@ -1,4 +1,4 @@
-- name: "API: `stale` status returned instead of `offline` or `not_connected`"
+- title: "API: `stale` status returned instead of `offline` or `not_connected`"
announcement_milestone: "14.6" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-12-22"
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
diff --git a/data/removals/15_0/15-0-sast-dotnet-21.yml b/data/removals/15_0/15-0-sast-dotnet-21.yml
index 28f13949154..8c1bf5dfcae 100644
--- a/data/removals/15_0/15-0-sast-dotnet-21.yml
+++ b/data/removals/15_0/15-0-sast-dotnet-21.yml
@@ -1,4 +1,4 @@
-- name: "SAST support for .NET 2.1"
+- title: "SAST support for .NET 2.1"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-sast-spotbugs-java-8.yml b/data/removals/15_0/15-0-sast-spotbugs-java-8.yml
index c6e59bf4b0f..1a636cf15e3 100644
--- a/data/removals/15_0/15-0-sast-spotbugs-java-8.yml
+++ b/data/removals/15_0/15-0-sast-spotbugs-java-8.yml
@@ -1,4 +1,4 @@
-- name: "Out-of-the-box SAST (SpotBugs) support for Java 8"
+- title: "Out-of-the-box SAST (SpotBugs) support for Java 8"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-secret-detection-configurations.yml b/data/removals/15_0/15-0-secret-detection-configurations.yml
index 0f759fae11c..1bdecd49118 100644
--- a/data/removals/15_0/15-0-secret-detection-configurations.yml
+++ b/data/removals/15_0/15-0-secret-detection-configurations.yml
@@ -1,4 +1,4 @@
-- name: "Secret Detection configuration variables"
+- title: "Secret Detection configuration variables"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-serverless.yml b/data/removals/15_0/15-0-serverless.yml
index 9f6a94037d8..ed517e6808c 100644
--- a/data/removals/15_0/15-0-serverless.yml
+++ b/data/removals/15_0/15-0-serverless.yml
@@ -1,4 +1,4 @@
-- name: "GitLab Serverless"
+- title: "GitLab Serverless"
announcement_milestone: "14.3"
announcement_date: "2021-09-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-sidekiq-metrics-health-check-config.yml b/data/removals/15_0/15-0-sidekiq-metrics-health-check-config.yml
index cfd0d5f5b13..efe8636143f 100644
--- a/data/removals/15_0/15-0-sidekiq-metrics-health-check-config.yml
+++ b/data/removals/15_0/15-0-sidekiq-metrics-health-check-config.yml
@@ -1,4 +1,4 @@
-- name: "Sidekiq configuration for metrics and health checks"
+- title: "Sidekiq configuration for metrics and health checks"
announcement_milestone: "14.7"
announcement_date: "2021-01-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15-0-static-site-editor.yml b/data/removals/15_0/15-0-static-site-editor.yml
index bea1b14232a..5ea43041734 100644
--- a/data/removals/15_0/15-0-static-site-editor.yml
+++ b/data/removals/15_0/15-0-static-site-editor.yml
@@ -1,4 +1,4 @@
-- name: "Static Site Editor" # (required) the name of the feature being removed. Avoid the words `deprecation`, `deprecate`, `removal`, and `remove` in this field because these are implied.
+- title: "Static Site Editor" # (required) the name of the feature being removed. Avoid the words `deprecation`, `deprecate`, `removal`, and `remove` in this field because these are implied.
announcement_milestone: "14.7" # (required) The milestone when this feature was deprecated.
announcement_date: "2022-01-22" # (required) The date of the milestone release when this feature was deprecated. This should almost always be the 22nd of a month (YYYY-MM-DD), unless you did an out of band blog post.
removal_milestone: "15.0" # (required) The milestone when this feature is being removed.
diff --git a/data/removals/15_0/15-0-tracing.yml b/data/removals/15_0/15-0-tracing.yml
index 1e99bd4cb3c..fdda6d8faaf 100644
--- a/data/removals/15_0/15-0-tracing.yml
+++ b/data/removals/15_0/15-0-tracing.yml
@@ -1,4 +1,4 @@
-- name: "Jaeger integration" # The headline announcing the removal. i.e. "`CI_PROJECT_CONFIG_PATH` removed in Gitlab 14.0"
+- title: "Jaeger integration" # The headline announcing the removal. i.e. "`CI_PROJECT_CONFIG_PATH` removed in Gitlab 14.0"
announcement_milestone: "14.7" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-01-22"
removal_milestone: "15.0" # The milestone when this feature is being removed.
diff --git a/data/removals/15_0/15-0-type.yml b/data/removals/15_0/15-0-type.yml
index c80efadc008..184bd6fb2e6 100644
--- a/data/removals/15_0/15-0-type.yml
+++ b/data/removals/15_0/15-0-type.yml
@@ -1,4 +1,4 @@
-- name: "`type` and `types` keyword from CI/CD configuration"
+- title: "`type` and `types` keyword from CI/CD configuration"
announcement_milestone: "14.6"
announcement_date: "2021-12-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/15_0-logging.yml b/data/removals/15_0/15_0-logging.yml
index 984ffbb0917..5db77a90b78 100644
--- a/data/removals/15_0/15_0-logging.yml
+++ b/data/removals/15_0/15_0-logging.yml
@@ -1,4 +1,4 @@
-- name: "ELK stack logging" # The headline announcing the removal. i.e. "`CI_PROJECT_CONFIG_PATH` removed in Gitlab 14.0"
+- title: "ELK stack logging" # The headline announcing the removal. i.e. "`CI_PROJECT_CONFIG_PATH` removed in Gitlab 14.0"
announcement_milestone: "14.7" # The milestone when this feature was deprecated.
announcement_date: "2022-01-22" # The date of the milestone release when this feature was deprecated. This should almost always be the 22nd of a month (YYYY-MM-DD), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is being removed.
diff --git a/data/removals/15_0/15_0-remove-pipelines-from-version-field.yml b/data/removals/15_0/15_0-remove-pipelines-from-version-field.yml
index df0c4c74e2e..e826943f7f1 100644
--- a/data/removals/15_0/15_0-remove-pipelines-from-version-field.yml
+++ b/data/removals/15_0/15_0-remove-pipelines-from-version-field.yml
@@ -1,4 +1,4 @@
-- name: "Pipelines field from the version field" # The name of the feature to be deprecated
+- title: "Pipelines field from the version field" # The name of the feature to be deprecated
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-11-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
diff --git a/data/removals/15_0/removal-manage-premium-required-pipelines.yml b/data/removals/15_0/removal-manage-premium-required-pipelines.yml
index 1e91fe1d3fc..4a9105bf63d 100644
--- a/data/removals/15_0/removal-manage-premium-required-pipelines.yml
+++ b/data/removals/15_0/removal-manage-premium-required-pipelines.yml
@@ -1,4 +1,4 @@
-- name: "Required pipeline configurations in Premium tier"
+- title: "Required pipeline configurations in Premium tier"
announcement_milestone: "14.8"
announcement_date: "2021-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/removal_manage_optional_pat_expiration.yml b/data/removals/15_0/removal_manage_optional_pat_expiration.yml
index 21c5b99d1a8..e1e6a125186 100644
--- a/data/removals/15_0/removal_manage_optional_pat_expiration.yml
+++ b/data/removals/15_0/removal_manage_optional_pat_expiration.yml
@@ -1,4 +1,4 @@
-- name: "Optional enforcement of personal access token expiration"
+- title: "Optional enforcement of personal access token expiration"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/removal_manage_repository_push_audit_event.yml b/data/removals/15_0/removal_manage_repository_push_audit_event.yml
index 474a5c41a95..c1c47823325 100644
--- a/data/removals/15_0/removal_manage_repository_push_audit_event.yml
+++ b/data/removals/15_0/removal_manage_repository_push_audit_event.yml
@@ -1,4 +1,4 @@
-- name: "Audit events for repository push events"
+- title: "Audit events for repository push events"
announcement_milestone: "14.3"
announcement_date: "2021-09-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/removal_manage_ssh_expiration.yml b/data/removals/15_0/removal_manage_ssh_expiration.yml
index accd1d49f6f..889d79c8a29 100644
--- a/data/removals/15_0/removal_manage_ssh_expiration.yml
+++ b/data/removals/15_0/removal_manage_ssh_expiration.yml
@@ -1,4 +1,4 @@
-- name: "Optional enforcement of SSH expiration"
+- title: "Optional enforcement of SSH expiration"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_0/removal_manage_status_check_passed_status.yml b/data/removals/15_0/removal_manage_status_check_passed_status.yml
index 598cba369cb..b5e63812d7c 100644
--- a/data/removals/15_0/removal_manage_status_check_passed_status.yml
+++ b/data/removals/15_0/removal_manage_status_check_passed_status.yml
@@ -1,4 +1,4 @@
-- name: "External status check API breaking changes"
+- title: "External status check API breaking changes"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
diff --git a/data/removals/15_2/removal-outdated-browser-support.yml b/data/removals/15_2/removal-outdated-browser-support.yml
index 84b2fa32020..bbe6892a5f5 100644
--- a/data/removals/15_2/removal-outdated-browser-support.yml
+++ b/data/removals/15_2/removal-outdated-browser-support.yml
@@ -1,4 +1,4 @@
-- name: "Support for older browsers"
+- title: "Support for older browsers"
removal_date: July 22, 2022
removal_milestone: "15.2"
reporter: leipert
diff --git a/data/removals/15_3/15-3-vulnerability-report-state-sort.yml b/data/removals/15_3/15-3-vulnerability-report-state-sort.yml
index 3ba5b451718..a9c84827b7c 100644
--- a/data/removals/15_3/15-3-vulnerability-report-state-sort.yml
+++ b/data/removals/15_3/15-3-vulnerability-report-state-sort.yml
@@ -1,7 +1,7 @@
#
# REQUIRED FIELDS
#
-- name: "Vulnerability Report sort by State" # (required) the name of the feature being removed. Avoid the words `deprecation`, `deprecate`, `removal`, and `remove` in this field because these are implied.
+- title: "Vulnerability Report sort by State" # (required) the name of the feature being removed. Avoid the words `deprecation`, `deprecate`, `removal`, and `remove` in this field because these are implied.
announcement_milestone: "15.0" # (required) The milestone when this feature was deprecated.
announcement_date: "2022-05-22" # (required) The date of the milestone release when this feature was deprecated. This should almost always be the 22nd of a month (YYYY-MM-DD), unless you did an out of band blog post.
removal_milestone: "15.3" # (required) The milestone when this feature is being removed.
diff --git a/data/removals/15_3/15-3-vulnerability-report-tool-sort.yml b/data/removals/15_3/15-3-vulnerability-report-tool-sort.yml
index 9aba5ca5ee7..6743a43d4b3 100644
--- a/data/removals/15_3/15-3-vulnerability-report-tool-sort.yml
+++ b/data/removals/15_3/15-3-vulnerability-report-tool-sort.yml
@@ -1,7 +1,7 @@
#
# REQUIRED FIELDS
#
-- name: "Vulnerability Report sort by Tool" # (required) the name of the feature being removed. Avoid the words `deprecation`, `deprecate`, `removal`, and `remove` in this field because these are implied.
+- title: "Vulnerability Report sort by Tool" # (required) the name of the feature being removed. Avoid the words `deprecation`, `deprecate`, `removal`, and `remove` in this field because these are implied.
announcement_milestone: "15.1" # (required) The milestone when this feature was deprecated.
announcement_date: "2022-06-22" # (required) The date of the milestone release when this feature was deprecated. This should almost always be the 22nd of a month (YYYY-MM-DD), unless you did an out of band blog post.
removal_milestone: "15.3" # (required) The milestone when this feature is being removed.
diff --git a/data/removals/15_3/removal_debian9.yml b/data/removals/15_3/removal_debian9.yml
index 5438922173f..eca198d6b18 100644
--- a/data/removals/15_3/removal_debian9.yml
+++ b/data/removals/15_3/removal_debian9.yml
@@ -1,4 +1,4 @@
-- name: "Support for Debian 9"
+- title: "Support for Debian 9"
removal_date: Aug 22, 2022 # day the removal was released
removal_milestone: "15.3"
reporter: dorrino # GitLab username of the person reporting the removal
diff --git a/data/removals/15_4/15-4-sast-analyzer-consolidation.yml b/data/removals/15_4/15-4-sast-analyzer-consolidation.yml
index 825fb2b4bfc..61358d3653b 100644
--- a/data/removals/15_4/15-4-sast-analyzer-consolidation.yml
+++ b/data/removals/15_4/15-4-sast-analyzer-consolidation.yml
@@ -1,4 +1,4 @@
-- name: "SAST analyzer consolidation and CI/CD template changes"
+- title: "SAST analyzer consolidation and CI/CD template changes"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.4"
diff --git a/data/removals/15_6/15-6-nfs-git-repository-storage.yml b/data/removals/15_6/15-6-nfs-git-repository-storage.yml
new file mode 100644
index 00000000000..95d9298fa4f
--- /dev/null
+++ b/data/removals/15_6/15-6-nfs-git-repository-storage.yml
@@ -0,0 +1,32 @@
+- title: "NFS as Git repository storage is no longer supported"
+ announcement_milestone: "14.0"
+ announcement_date: "2021-06-22"
+ removal_milestone: "15.6"
+ removal_date: "2022-11-22"
+ breaking_change: false
+ reporter: mjwood
+ stage: create
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351243
+ body: |
+ As of November 22, 2022, we have removed support for customers using NFS for Git repository storage. This was
+ originally planned for May 22, 2022, but in an effort to allow continued maturity of Gitaly Cluster, we delayed
+ our removal of support date until now. Please see our official [Statement of Support](https://about.gitlab.com/support/statement-of-support/#gitaly-and-nfs)
+ for further information.
+
+ This change in support follows the development deprecation for NFS for Git repository storage that occurred in GitLab 14.0.
+
+ Gitaly Cluster offers tremendous benefits for our customers such as:
+
+ - [Variable replication factors](https://docs.gitlab.com/ee/administration/gitaly/index.html#replication-factor).
+ - [Strong consistency](https://docs.gitlab.com/ee/administration/gitaly/index.html#strong-consistency).
+ - [Distributed read capabilities](https://docs.gitlab.com/ee/administration/gitaly/index.html#distributed-reads).
+
+ We encourage customers currently using NFS for Git repositories to migrate as soon as possible by reviewing our documentation on
+ [migrating to Gitaly Cluster](https://docs.gitlab.com/ee/administration/gitaly/index.html#migrate-to-gitaly-cluster).
+#
+# OPTIONAL FIELDS
+#
+ tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ documentation_url: # (optional) This is a link to the current documentation page
+ image_url: # (optional) This is a link to a thumbnail image depicting the feature
+ video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
diff --git a/data/removals/15_7/15-7-remove-flowdock-integration.yml b/data/removals/15_7/15-7-remove-flowdock-integration.yml
new file mode 100644
index 00000000000..46f8ed6bdf9
--- /dev/null
+++ b/data/removals/15_7/15-7-remove-flowdock-integration.yml
@@ -0,0 +1,18 @@
+- title: "Flowdock integration" # (required) Actionable title. e.g., The `confidential` field for a `Note` is deprecated. Use `internal` instead.
+ announcement_milestone: "15.7" # (required) The milestone when this feature was deprecated.
+ announcement_date: "2022-12-22" # (required) The date of the milestone release when this feature was deprecated. This should almost always be the 22nd of a month (YYYY-MM-DD), unless you did an out of band blog post.
+ removal_milestone: "15.7" # (required) The milestone when this feature is being removed.
+ removal_date: "2022-12-22" # (required) This should almost always be the 22nd of a month (YYYY-MM-DD), the date of the milestone release when this feature will be removed.
+ breaking_change: false # (required) Change to true if this removal is a breaking change.
+ reporter: arturoherrero # (required) GitLab username of the person reporting the removal
+ stage: manage # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/379197 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ As of December 22, 2022, we are removing the Flowdock integration because the service was shut down on August 15, 2022.
+#
+# OPTIONAL FIELDS
+#
+ tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ documentation_url: # (optional) This is a link to the current documentation page
+ image_url: # (optional) This is a link to a thumbnail image depicting the feature
+ video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
diff --git a/data/removals/15_8/15-8-auto-deploy-helm-chart-cilium-policy.yml b/data/removals/15_8/15-8-auto-deploy-helm-chart-cilium-policy.yml
new file mode 100644
index 00000000000..40f7a57b6ec
--- /dev/null
+++ b/data/removals/15_8/15-8-auto-deploy-helm-chart-cilium-policy.yml
@@ -0,0 +1,16 @@
+- title: "CiliumNetworkPolicy within the auto deploy Helm chart is removed" # (required) Actionable title. e.g., The `confidential` field for a `Note` is deprecated. Use `internal` instead.
+ announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
+ announcement_date: "2022-02-22" # The date of the milestone release when this feature was first announced as deprecated
+ removal_milestone: "15.8" # The milestone when this feature is planned to be removed
+ removal_date: "2023-01-22" # (optional - may be required in the future) YYYY-MM-DD format - the date of the milestone release when this feature is planned to be removed
+ breaking_change: false # (required) Change to true if this removal is a breaking change.
+ reporter: sam.white # (required) GitLab username of the person reporting the removal
+ stage: govern # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382044
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ All functionality related to the GitLab Container Network Security and Container Host Security categories was deprecated in GitLab 14.8 and scheduled for removal in GitLab 15.0. The [CiliumNetworkPolicy definition](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/blob/master/assets/auto-deploy-app/values.yaml#L175) that exists as part of the [GitLab Auto Deploy Helm chart](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app) was not removed as scheduled in GitLab 15.0. This policy is planned to be removed in the GitLab 15.8 release.
+
+ If you want to preserve this functionality, you can follow one of these two paths:
+
+ 1. Fork the [GitLab Auto Deploy Helm chart](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app) into the `chart/` path within your project
+ 1. Set `AUTO_DEPLOY_IMAGE_VERSION` and `DAST_AUTO_DEPLOY_IMAGE_VERSION` to the most recent version of the image that included the CiliumNetworkPolicy
diff --git a/data/removals/16_0/source_code-approvals-endpoint.yml b/data/removals/16_0/source_code-approvals-endpoint.yml
index e754c1cfebf..0d683206f5a 100644
--- a/data/removals/16_0/source_code-approvals-endpoint.yml
+++ b/data/removals/16_0/source_code-approvals-endpoint.yml
@@ -1,8 +1,8 @@
-- name: "Changing merge request approvals with the `/approvals` API endpoint"
+- title: "Changing merge request approvals with the `/approvals` API endpoint"
announcement_milestone: "12.3"
announcement_date: "2019-09-22"
removal_milestone: "16.0"
- removal_date: "2023-03-22"
+ removal_date: "2023-05-22"
breaking_change: true
reporter: tlinz
stage: Create
diff --git a/data/removals/templates/_removal_template.md.erb b/data/removals/templates/_removal_template.md.erb
index ad0298a6596..89e25c76e04 100644
--- a/data/removals/templates/_removal_template.md.erb
+++ b/data/removals/templates/_removal_template.md.erb
@@ -9,6 +9,9 @@ info: "See the Technical Writers assigned to Development Guidelines: https://abo
In each release, GitLab removes features that were deprecated in an earlier release.
Some features cause breaking changes when they are removed.
+**{rss}** **To be notified of upcoming breaking changes**,
+add this URL to your RSS feed reader: `https://about.gitlab.com/breaking-changes.xml`
+
<!-- vale off -->
<!--
@@ -34,7 +37,7 @@ For removal reviewers (Technical Writers only):
<%- milestones.each do |milestone| %>
## Removed in <%= milestone %>
<%- entries.select{|entry| entry["removal_milestone"] == milestone}.each do |removal| %>
-### <%= removal["name"]%>
+### <%= removal["title"]%>
<% if removal["breaking_change"] -%>
WARNING:
diff --git a/data/removals/templates/example.yml b/data/removals/templates/example.yml
index 8d7d694be26..e7c0268c182 100644
--- a/data/removals/templates/example.yml
+++ b/data/removals/templates/example.yml
@@ -12,7 +12,7 @@
#
# REQUIRED FIELDS
#
-- name: "Feature name" # (required) the name of the feature being removed. Avoid the words `deprecation`, `deprecate`, `removal`, and `remove` in this field because these are implied.
+- title: "X is removed. Do Y instead." # (required) Actionable title. e.g., The `confidential` field for a `Note` is deprecated. Use `internal` instead.
announcement_milestone: "XX.YY" # (required) The milestone when this feature was deprecated.
announcement_date: "YYYY-MM-DD" # (required) The date of the milestone release when this feature was deprecated. This should almost always be the 22nd of a month (YYYY-MM-DD), unless you did an out of band blog post.
removal_milestone: "XX.YY" # (required) The milestone when this feature is being removed.
@@ -26,6 +26,16 @@
This area supports markdown. Delete this entire comment and replace it with your markdown content.
+ Removals must be actionable.
+
+ Use terms such as "removed in favor of" or "use X instead." If no replacement is available, the action would be, "Discontinue use of foo immediately."
+
+ For example:
+
+ - The `confidential` field for a `Note` is removed. Use `internal` instead.
+ - The `merge_status` field in the merge request API has been removed in favor of `detailed_merge_status`.
+ - The `notable` endpoint has been removed. Discontinue use of `notable` immediately.
+
When ready, assign to your tech writer for review. When ready, they will run `bin/rake gitlab:docs:compile_removals` to update the removals doc, then merge.
END OF BODY COMMENT -->
diff --git a/data/whats_new/202211220001_15_06.yml b/data/whats_new/202211220001_15_06.yml
new file mode 100644
index 00000000000..e966305abf0
--- /dev/null
+++ b/data/whats_new/202211220001_15_06.yml
@@ -0,0 +1,87 @@
+- name: "Group and subgroup-level scan result policies"
+ description: |
+ GitLab now supports managing scan result policies at both the group and subgroup levels.
+ These policies automatically flow down and apply to all projects inside the group. This
+ makes it considerably easier to enforce these policies uniformly for large organizations
+ that have large numbers of projects. To get started, have a group or subgroup Owner link
+ an associated security policy project on the **Security & Compliance > Policies** page.
+ stage: Govern
+ self-managed: true
+ gitlab-com: true
+ available_in: [Ultimate]
+ documentation_link: 'https://docs.gitlab.com/ee/user/application_security/policies/scan-result-policies.html'
+ image_url: 'https://img.youtube.com/vi/jfbNo5IE-2s/hqdefault.jpg'
+ published_at: 2022-11-22
+ release: 15.6
+- name: "Git abuse rate limiting"
+ description: |
+ In GitLab 15.6, we're introducing [Git abuse rate limiting](https://docs.gitlab.com/ee/user/group/reporting/git_abuse_rate_limit.html).
+ Enable this feature to automatically notify administrators when a user downloads
+ or clones more than a specified number of repositories in a group or any of its
+ subgroups within a given time frame.
+
+ You can also automatically ban users who exceed the rate limit.
+ Banned users cannot access the main group or any of its non-public subgroups.
+ Access to unrelated groups is unaffected. Bans are permanent by default, but
+ group administrators can always unban a banned user.
+ stage: anti-abuse
+ self-managed: true
+ gitlab-com: false
+ available_in: [Ultimate]
+ documentation_link: 'https://docs.gitlab.com/ee/user/group/reporting/git_abuse_rate_limit.html'
+ image_url: 'https://about.gitlab.com/images/15_6/git-abuse-rate-limiting.png'
+ published_at: 2022-11-22
+ release: 15.6
+- name: "DAST API analyzer for on-demand DAST API scans"
+ description: |
+ With GitLab 15.6, the DAST API analyzer is now being used for GitLab on-demand DAST API scans.
+ In previous versions of GitLab, the analyzer used in these on-demand scans was our legacy DAST
+ analyzer. Our internal benchmarking shows that our DAST API analyzer finds more vulnerabilities,
+ with a lower false-positive rate than our legacy analyzer. The DAST API analyzer also introduces
+ new functionality such as GraphQL scans, support for authentication tokens that expire, scans using
+ Postman collections, and scans using HAR files. With the switch to the DAST API analyzer, some of
+ that functionality is already available in the on-demand site profile.
+
+ In addition to using an OpenAPI specification in the site profile to define an API test, you can now
+ use a Postman collection or HAR file to make sure that your test gets the API coverage that you expect.
+ Also, we've added basic authentication as an option for on-demand API scans, adding to the previous
+ functionality of using token authentication in authorization headers.
+
+ Next, we will be working next on adding GraphQL support for on-demand API scans. Look for more improvements
+ over the next few releases as we incorporate more of the advanced functionality of the DAST API analyzer.
+ stage: secure
+ self-managed: true
+ gitlab-com: true
+ available_in: [Ultimate]
+ documentation_link: 'https://docs.gitlab.com/ee/user/application_security/dast_api/'
+ image_url: 'https://about.gitlab.com/images/15_6/on_demand_api_security.png'
+ published_at: 2022-11-22
+ release: 15.6
+- name: "Support for special characters in CI/CD variables"
+ description: |
+ Previously, it was difficult to use the $ character in a CI/CD variable because $ normally signifies the
+ start of another variable. GitLab would interpret it as a variable and try to expand it. In this release,
+ we are introducing the variable: expand: keyword which will allow you to mark a variable as “rawâ€. A raw
+ variable can contain any special characters and is not expanded when passed to the GitLab runner.
+ stage: verify
+ self-managed: true
+ gitlab-com: true
+ available_in: [Free, Premium, Ultimate]
+ documentation_link: 'https://docs.gitlab.com/ee/ci/yaml/#variablesexpand'
+ image_url: 'https://about.gitlab.com/images/15_6/special_character_support.png'
+ published_at: 2022-11-22
+ release: 15.6
+- name: "CI/CD variable support in rules:exists configuration"
+ description: |
+ The more complex your .gitlab-ci.yml configuration is, the more difficult it is to maintain and scale. By
+ adding support for CI/CD variables with the rules: exists keyword, you can now use variables for paths
+ or filenames. Having a single source of truth by storing frequently used values in variables ensures
+ consistent behavior and makes your configuration easier to manage.
+ stage: verify
+ self-managed: true
+ gitlab-com: true
+ available_in: [Free, Premium, Ultimate]
+ documentation_link: 'https://docs.gitlab.com/ee/ci/yaml/#rulesexists'
+ image_url: 'https://about.gitlab.com/images/15_6/variable_support_rule_exists.png'
+ published_at: 2022-11-22
+ release: 15.6
diff --git a/db/docs/abuse_reports.yml b/db/docs/abuse_reports.yml
index dcd081e6892..896c978f9d7 100644
--- a/db/docs/abuse_reports.yml
+++ b/db/docs/abuse_reports.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores abuse reports from other users.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/cba7f20dc8614d12e3eeda6e14f454aeb22b9b54
milestone: '7.13'
+gitlab_schema: gitlab_main
diff --git a/db/docs/achievements.yml b/db/docs/achievements.yml
new file mode 100644
index 00000000000..20f9d1616b3
--- /dev/null
+++ b/db/docs/achievements.yml
@@ -0,0 +1,10 @@
+---
+table_name: achievements
+classes:
+- Achievements::Achivement
+feature_categories:
+- users
+description: Achievements which can be created by namespaces to award them to users
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105871
+milestone: '15.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/agent_activity_events.yml b/db/docs/agent_activity_events.yml
index 3be300e1852..9fd906d5352 100644
--- a/db/docs/agent_activity_events.yml
+++ b/db/docs/agent_activity_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Historical timeline events belonging to a cluster agent
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74577
milestone: '14.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/agent_group_authorizations.yml b/db/docs/agent_group_authorizations.yml
index 3592c93ed83..61c8733383a 100644
--- a/db/docs/agent_group_authorizations.yml
+++ b/db/docs/agent_group_authorizations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Configuration for a group that is authorized to use a particular cluster agent
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68023
milestone: '14.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/agent_project_authorizations.yml b/db/docs/agent_project_authorizations.yml
index c4e101f754f..e595c84b5d5 100644
--- a/db/docs/agent_project_authorizations.yml
+++ b/db/docs/agent_project_authorizations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Configuration for a project that is authorized to use a particular cluster agent
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67295
milestone: '14.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/alert_management_alert_assignees.yml b/db/docs/alert_management_alert_assignees.yml
index 4d5a1acb0ca..cda7725e18f 100644
--- a/db/docs/alert_management_alert_assignees.yml
+++ b/db/docs/alert_management_alert_assignees.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists metadata between users and alerts to support alert assignments
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32609
milestone: '13.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/alert_management_alert_metric_images.yml b/db/docs/alert_management_alert_metric_images.yml
index bcfa2b54b1c..20117109ad4 100644
--- a/db/docs/alert_management_alert_metric_images.yml
+++ b/db/docs/alert_management_alert_metric_images.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists metadata for uploads related to alerts
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80339
milestone: '14.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/alert_management_alert_user_mentions.yml b/db/docs/alert_management_alert_user_mentions.yml
index 6a3aaf2ce83..02f5602a33f 100644
--- a/db/docs/alert_management_alert_user_mentions.yml
+++ b/db/docs/alert_management_alert_user_mentions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists metadata for system notes related to alerts
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33217
milestone: '13.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/alert_management_alerts.yml b/db/docs/alert_management_alerts.yml
index ca8b02ec346..4e3f4151165 100644
--- a/db/docs/alert_management_alerts.yml
+++ b/db/docs/alert_management_alerts.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists incoming alert data including its payload
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29864
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/alert_management_http_integrations.yml b/db/docs/alert_management_http_integrations.yml
index 8fa330f0775..4eb824f74ec 100644
--- a/db/docs/alert_management_http_integrations.yml
+++ b/db/docs/alert_management_http_integrations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists settings for alert HTTP integrations
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43634
milestone: '13.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/allowed_email_domains.yml b/db/docs/allowed_email_domains.yml
index 65a12dd712c..4dd6100e4c4 100644
--- a/db/docs/allowed_email_domains.yml
+++ b/db/docs/allowed_email_domains.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores email domains for group, only members with email from those domains can be added to the group
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/14800
milestone: '12.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/analytics_cycle_analytics_aggregations.yml b/db/docs/analytics_cycle_analytics_aggregations.yml
index ab92c5c078b..0f9bf6695c5 100644
--- a/db/docs/analytics_cycle_analytics_aggregations.yml
+++ b/db/docs/analytics_cycle_analytics_aggregations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores Value Stream Analytics aggregation related metadata for top-level groups.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79942
milestone: '14.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/analytics_cycle_analytics_group_stages.yml b/db/docs/analytics_cycle_analytics_group_stages.yml
index 67bca593a61..5dbb3eba2c0 100644
--- a/db/docs/analytics_cycle_analytics_group_stages.yml
+++ b/db/docs/analytics_cycle_analytics_group_stages.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/15061
milestone: '12.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/analytics_cycle_analytics_group_value_streams.yml b/db/docs/analytics_cycle_analytics_group_value_streams.yml
index 8942439dddd..e29d07c5a72 100644
--- a/db/docs/analytics_cycle_analytics_group_value_streams.yml
+++ b/db/docs/analytics_cycle_analytics_group_value_streams.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Store group level Value Stream objects.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36658
milestone: '13.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/analytics_cycle_analytics_issue_stage_events.yml b/db/docs/analytics_cycle_analytics_issue_stage_events.yml
index b3f6a9f4716..f31cb808b09 100644
--- a/db/docs/analytics_cycle_analytics_issue_stage_events.yml
+++ b/db/docs/analytics_cycle_analytics_issue_stage_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Contains various Issue-related timestamps for aggregating Value Stream Analytics data.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68950
milestone: '14.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/analytics_cycle_analytics_merge_request_stage_events.yml b/db/docs/analytics_cycle_analytics_merge_request_stage_events.yml
index ae3074d544b..35438cd4c0c 100644
--- a/db/docs/analytics_cycle_analytics_merge_request_stage_events.yml
+++ b/db/docs/analytics_cycle_analytics_merge_request_stage_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Contains various MergeRequest-related timestamps for aggregating Value Stream Analytics data.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68950
milestone: '14.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/analytics_cycle_analytics_project_stages.yml b/db/docs/analytics_cycle_analytics_project_stages.yml
index 1ff917faf94..dc5b1db16cd 100644
--- a/db/docs/analytics_cycle_analytics_project_stages.yml
+++ b/db/docs/analytics_cycle_analytics_project_stages.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists project level value stream analytics stages.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/15061
milestone: '12.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/analytics_cycle_analytics_project_value_streams.yml b/db/docs/analytics_cycle_analytics_project_value_streams.yml
index 9b63ea0500a..5881c048d3b 100644
--- a/db/docs/analytics_cycle_analytics_project_value_streams.yml
+++ b/db/docs/analytics_cycle_analytics_project_value_streams.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store the value stream configurations for projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60925
milestone: '13.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/analytics_cycle_analytics_stage_event_hashes.yml b/db/docs/analytics_cycle_analytics_stage_event_hashes.yml
index 3df5ee1c172..52cef9fcdcb 100644
--- a/db/docs/analytics_cycle_analytics_stage_event_hashes.yml
+++ b/db/docs/analytics_cycle_analytics_stage_event_hashes.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores hashes of Value Stream Analytics stage configurations.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67259
milestone: '14.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/analytics_devops_adoption_segments.yml b/db/docs/analytics_devops_adoption_segments.yml
index 4b22c5926c2..42f04f57608 100644
--- a/db/docs/analytics_devops_adoption_segments.yml
+++ b/db/docs/analytics_devops_adoption_segments.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores a reference to the Namespace which is enabled for the DevOps report.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45748
milestone: '13.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/analytics_devops_adoption_snapshots.yml b/db/docs/analytics_devops_adoption_snapshots.yml
index 3fcaea684d6..378b83cc22f 100644
--- a/db/docs/analytics_devops_adoption_snapshots.yml
+++ b/db/docs/analytics_devops_adoption_snapshots.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Contains periodical DevOps Adoption data points.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47388
milestone: '13.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/analytics_language_trend_repository_languages.yml b/db/docs/analytics_language_trend_repository_languages.yml
index c42328b6bc8..3425788591b 100644
--- a/db/docs/analytics_language_trend_repository_languages.yml
+++ b/db/docs/analytics_language_trend_repository_languages.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Contains snapshot data about the used programming languages over time.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16491
milestone: '12.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/analytics_usage_trends_measurements.yml b/db/docs/analytics_usage_trends_measurements.yml
index 1672c195e3d..8c492d3dda1 100644
--- a/db/docs/analytics_usage_trends_measurements.yml
+++ b/db/docs/analytics_usage_trends_measurements.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Contains periodically snapshotted database record counts.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62797
milestone: '14.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/appearances.yml b/db/docs/appearances.yml
index 8d30bc52d4d..104a72263f0 100644
--- a/db/docs/appearances.yml
+++ b/db/docs/appearances.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/40104eead753e7e8ea77951a74a3941546c35aab
milestone: '6.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/application_setting_terms.yml b/db/docs/application_setting_terms.yml
index 11f8c6536c4..046231b13a4 100644
--- a/db/docs/application_setting_terms.yml
+++ b/db/docs/application_setting_terms.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/cf37bef287d7dd5d2dce3e2276489767b8c0671f
milestone: '10.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/application_settings.yml b/db/docs/application_settings.yml
index ae28221b7ac..1015884e8a3 100644
--- a/db/docs/application_settings.yml
+++ b/db/docs/application_settings.yml
@@ -11,3 +11,4 @@ feature_categories:
description: GitLab application settings
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/8589b4e137f50293952923bb07e2814257d7784d
milestone: '7.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/approval_merge_request_rule_sources.yml b/db/docs/approval_merge_request_rule_sources.yml
index 868d694d190..9f4c8212360 100644
--- a/db/docs/approval_merge_request_rule_sources.yml
+++ b/db/docs/approval_merge_request_rule_sources.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Keeps connection between merge request and project approval rule
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/8497
milestone: '11.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/approval_merge_request_rules.yml b/db/docs/approval_merge_request_rules.yml
index ad8b3411706..517294e4ed0 100644
--- a/db/docs/approval_merge_request_rules.yml
+++ b/db/docs/approval_merge_request_rules.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Keeps approval merge request rules
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/8497
milestone: '11.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/approval_merge_request_rules_approved_approvers.yml b/db/docs/approval_merge_request_rules_approved_approvers.yml
index 9074a4dd39a..4a453da4162 100644
--- a/db/docs/approval_merge_request_rules_approved_approvers.yml
+++ b/db/docs/approval_merge_request_rules_approved_approvers.yml
@@ -6,3 +6,4 @@ feature_categories:
description: Join table for approved approvers and ApprovalMergeRequestRule
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/8497
milestone: '11.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/approval_merge_request_rules_groups.yml b/db/docs/approval_merge_request_rules_groups.yml
index 1acf9882d57..45306dc0bd1 100644
--- a/db/docs/approval_merge_request_rules_groups.yml
+++ b/db/docs/approval_merge_request_rules_groups.yml
@@ -6,3 +6,4 @@ feature_categories:
description: Keeps connection between group and a merge request approval rule
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/8497
milestone: '11.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/approval_merge_request_rules_users.yml b/db/docs/approval_merge_request_rules_users.yml
index 750e7ae1f48..746aa70ebd2 100644
--- a/db/docs/approval_merge_request_rules_users.yml
+++ b/db/docs/approval_merge_request_rules_users.yml
@@ -6,3 +6,4 @@ feature_categories:
description: Keeps connection between user and a merge request approval rule
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/8497
milestone: '11.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/approval_project_rules.yml b/db/docs/approval_project_rules.yml
index c2aff9d358f..c970b86bb18 100644
--- a/db/docs/approval_project_rules.yml
+++ b/db/docs/approval_project_rules.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Keeps approval project rules
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/8497
milestone: '11.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/approval_project_rules_groups.yml b/db/docs/approval_project_rules_groups.yml
index 83eeb52099c..5d1b96a4197 100644
--- a/db/docs/approval_project_rules_groups.yml
+++ b/db/docs/approval_project_rules_groups.yml
@@ -6,3 +6,4 @@ feature_categories:
description: Keeps connection between group and a project approval rule
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/8497
milestone: '11.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/approval_project_rules_protected_branches.yml b/db/docs/approval_project_rules_protected_branches.yml
index a41fd741af8..e2776e89f21 100644
--- a/db/docs/approval_project_rules_protected_branches.yml
+++ b/db/docs/approval_project_rules_protected_branches.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Keeps relation between approval project rules and protected branches.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22673
milestone: '12.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/approval_project_rules_users.yml b/db/docs/approval_project_rules_users.yml
index a1ff8bf7bff..ce35033356a 100644
--- a/db/docs/approval_project_rules_users.yml
+++ b/db/docs/approval_project_rules_users.yml
@@ -6,3 +6,4 @@ feature_categories:
description: Keeps connection between user and a project approval rule
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/8497
milestone: '11.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/approvals.yml b/db/docs/approvals.yml
index 82d833b9ba6..30cc53ece16 100644
--- a/db/docs/approvals.yml
+++ b/db/docs/approvals.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores merge request approvals made by users
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/73faf3c7289c4fa4535b752a12247ee74b173976
milestone: '7.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/approver_groups.yml b/db/docs/approver_groups.yml
index e078e20814c..fd892ed3f4c 100644
--- a/db/docs/approver_groups.yml
+++ b/db/docs/approver_groups.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Group approvers of given merge request
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/743
milestone: '8.13'
+gitlab_schema: gitlab_main
diff --git a/db/docs/approvers.yml b/db/docs/approvers.yml
index f0bfa47761a..ac15bbb8767 100644
--- a/db/docs/approvers.yml
+++ b/db/docs/approvers.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Approvers of given merge request
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/3cc78d89984d9c9df8372c52b7bba38e6226f9f2
milestone: '7.13'
+gitlab_schema: gitlab_main
diff --git a/db/docs/ar_internal_metadata.yml b/db/docs/ar_internal_metadata.yml
index e299e9f6317..4e5c0c2d833 100644
--- a/db/docs/ar_internal_metadata.yml
+++ b/db/docs/ar_internal_metadata.yml
@@ -7,3 +7,4 @@ description: >-
An internal table used by ActiveRecord to store information about how the database was migrated.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/9ba1224867665844b117fa037e1465bb706b3685
milestone: '0.8'
+gitlab_schema: gitlab_internal
diff --git a/db/docs/atlassian_identities.yml b/db/docs/atlassian_identities.yml
index 64e29a80817..e43c8018d5c 100644
--- a/db/docs/atlassian_identities.yml
+++ b/db/docs/atlassian_identities.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores Atlassian credentials that are used to integrate with Atlassian API
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40176
milestone: '13.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/audit_events.yml b/db/docs/audit_events.yml
index b19b26be711..ec707e41286 100644
--- a/db/docs/audit_events.yml
+++ b/db/docs/audit_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/cf6b622686eacffa46aba5c8ed6419dc877a6b58
milestone: '7.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/audit_events_external_audit_event_destinations.yml b/db/docs/audit_events_external_audit_event_destinations.yml
index 6cac68c54cb..91fb1e5a17a 100644
--- a/db/docs/audit_events_external_audit_event_destinations.yml
+++ b/db/docs/audit_events_external_audit_event_destinations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70706
milestone: '14.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/audit_events_streaming_event_type_filters.yml b/db/docs/audit_events_streaming_event_type_filters.yml
index 7119c84589e..fe36fbf5a5a 100644
--- a/db/docs/audit_events_streaming_event_type_filters.yml
+++ b/db/docs/audit_events_streaming_event_type_filters.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Represents a event type filter for audit event streaming
introduced_by_url:
milestone: '15.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/audit_events_streaming_headers.yml b/db/docs/audit_events_streaming_headers.yml
index 034ed2c6644..4f0ef9f20b5 100644
--- a/db/docs/audit_events_streaming_headers.yml
+++ b/db/docs/audit_events_streaming_headers.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Represents a HTTP header sent with streaming audit events
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88063
milestone: '15.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/authentication_events.yml b/db/docs/authentication_events.yml
index 7eec9124e81..eaede3b7cd4 100644
--- a/db/docs/authentication_events.yml
+++ b/db/docs/authentication_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39652
milestone: '13.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/award_emoji.yml b/db/docs/award_emoji.yml
index 7405e3e57e2..6cbea437117 100644
--- a/db/docs/award_emoji.yml
+++ b/db/docs/award_emoji.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Store the awarding of emoji by users on Issues, Epics, and other Awardables.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/3785
milestone: '8.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/aws_roles.yml b/db/docs/aws_roles.yml
index df308acc423..902d6a97b62 100644
--- a/db/docs/aws_roles.yml
+++ b/db/docs/aws_roles.yml
@@ -7,3 +7,4 @@ feature_categories:
description: (Deprecated) AWS IAM role for creating EKS clusters via GitLab
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17057
milestone: '12.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/background_migration_jobs.yml b/db/docs/background_migration_jobs.yml
index b0fd5b58d50..14903b74ce0 100644
--- a/db/docs/background_migration_jobs.yml
+++ b/db/docs/background_migration_jobs.yml
@@ -9,3 +9,4 @@ description: >-
See https://docs.gitlab.com/ee/development/database/background_migrations.html for more details.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35913
milestone: '13.2'
+gitlab_schema: gitlab_shared
diff --git a/db/docs/badges.yml b/db/docs/badges.yml
index 209d4d1d88a..af05638152c 100644
--- a/db/docs/badges.yml
+++ b/db/docs/badges.yml
@@ -9,3 +9,4 @@ feature_categories:
description: Stores badges records
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17082
milestone: '10.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/banned_users.yml b/db/docs/banned_users.yml
index 788684494db..d14b6d77234 100644
--- a/db/docs/banned_users.yml
+++ b/db/docs/banned_users.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64728
milestone: '14.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/batched_background_migration_job_transition_logs.yml b/db/docs/batched_background_migration_job_transition_logs.yml
index 826145d82b4..d6cca5f24d3 100644
--- a/db/docs/batched_background_migration_job_transition_logs.yml
+++ b/db/docs/batched_background_migration_job_transition_logs.yml
@@ -9,3 +9,4 @@ description: >-
Every time a batched background migrations job changes to a new state, the system records that information in this table.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75113
milestone: '14.8'
+gitlab_schema: gitlab_shared
diff --git a/db/docs/batched_background_migration_jobs.yml b/db/docs/batched_background_migration_jobs.yml
index 4a307d28781..b44cfc860d1 100644
--- a/db/docs/batched_background_migration_jobs.yml
+++ b/db/docs/batched_background_migration_jobs.yml
@@ -8,3 +8,4 @@ description: >-
The batched_background_migration_jobs table stores information about the jobs created during the execution of a batched background migration.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54628
milestone: '13.10'
+gitlab_schema: gitlab_shared
diff --git a/db/docs/batched_background_migrations.yml b/db/docs/batched_background_migrations.yml
index a8285210801..d23f1b30b1d 100644
--- a/db/docs/batched_background_migrations.yml
+++ b/db/docs/batched_background_migrations.yml
@@ -9,3 +9,4 @@ description: >-
See https://docs.gitlab.com/ee/development/batched_background_migrations.html for more details.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54628
milestone: '13.10'
+gitlab_schema: gitlab_shared
diff --git a/db/docs/board_assignees.yml b/db/docs/board_assignees.yml
index dd7235a440e..5da2c57c3f4 100644
--- a/db/docs/board_assignees.yml
+++ b/db/docs/board_assignees.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used by issue boards to filter issues by assignee as part of the default scope
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/2912
milestone: '10.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/board_group_recent_visits.yml b/db/docs/board_group_recent_visits.yml
index 3e516038737..5615620567c 100644
--- a/db/docs/board_group_recent_visits.yml
+++ b/db/docs/board_group_recent_visits.yml
@@ -7,3 +7,4 @@ feature_categories:
description: The last group issue board visited by a user, used to redirect them from the default boards page
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/7aeab58f4861144fcc1d334907cb1b465c645001
milestone: '11.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/board_labels.yml b/db/docs/board_labels.yml
index 33dfdd5df70..d682783eaf9 100644
--- a/db/docs/board_labels.yml
+++ b/db/docs/board_labels.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used by issue boards to filter issues by label as part of the default scope
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/2912
milestone: '10.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/board_project_recent_visits.yml b/db/docs/board_project_recent_visits.yml
index bd7783acfe7..336be808517 100644
--- a/db/docs/board_project_recent_visits.yml
+++ b/db/docs/board_project_recent_visits.yml
@@ -7,3 +7,4 @@ feature_categories:
description: The last project issue board visited by a user, used to redirect them from the default boards page
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/7aeab58f4861144fcc1d334907cb1b465c645001
milestone: '11.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/board_user_preferences.yml b/db/docs/board_user_preferences.yml
index 61b30c4fc66..c997acf3439 100644
--- a/db/docs/board_user_preferences.yml
+++ b/db/docs/board_user_preferences.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Preferences for issue boards stored on a per user basis, such as whether to hide or show labels
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33892
milestone: '13.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/boards.yml b/db/docs/boards.yml
index bf50cecd826..db177f7a562 100644
--- a/db/docs/boards.yml
+++ b/db/docs/boards.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Configuration information for issue boards, including default scope and visibility of open and closed lists
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/611dab2e522e5e59cf09cd459a31686e65616863
milestone: '8.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/boards_epic_board_labels.yml b/db/docs/boards_epic_board_labels.yml
index d34499f8105..021e34b653d 100644
--- a/db/docs/boards_epic_board_labels.yml
+++ b/db/docs/boards_epic_board_labels.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Information relating epic boards to labels used to scope the boards
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48658
milestone: '13.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/boards_epic_board_positions.yml b/db/docs/boards_epic_board_positions.yml
index 88d01bc9a11..ba63fe2b9a8 100644
--- a/db/docs/boards_epic_board_positions.yml
+++ b/db/docs/boards_epic_board_positions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Relates epics to epic boards by position, unique to each combination of epic and board
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48120
milestone: '13.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/boards_epic_board_recent_visits.yml b/db/docs/boards_epic_board_recent_visits.yml
index c60d1400f8c..2262241a0c0 100644
--- a/db/docs/boards_epic_board_recent_visits.yml
+++ b/db/docs/boards_epic_board_recent_visits.yml
@@ -7,3 +7,4 @@ feature_categories:
description: The epic board most recently visited by users, used to determine where to redirect them by default
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60720
milestone: '13.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/boards_epic_boards.yml b/db/docs/boards_epic_boards.yml
index 7b35decdd54..d55e99e58f0 100644
--- a/db/docs/boards_epic_boards.yml
+++ b/db/docs/boards_epic_boards.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Configuration information for epic boards, including default scope and visibility of open and closed lists
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48658
milestone: '13.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/boards_epic_list_user_preferences.yml b/db/docs/boards_epic_list_user_preferences.yml
index ea3eee5a33f..32107ac86a2 100644
--- a/db/docs/boards_epic_list_user_preferences.yml
+++ b/db/docs/boards_epic_list_user_preferences.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Epic board list preferences on a per-user basis, specifically whether the user has collapsed the list or not
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54541
milestone: '13.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/boards_epic_lists.yml b/db/docs/boards_epic_lists.yml
index 87a15e4dde0..31fb116d110 100644
--- a/db/docs/boards_epic_lists.yml
+++ b/db/docs/boards_epic_lists.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Configuration of a single list within an epic board
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49728
milestone: '13.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/boards_epic_user_preferences.yml b/db/docs/boards_epic_user_preferences.yml
index 8137d370ebe..aed2b7a47d8 100644
--- a/db/docs/boards_epic_user_preferences.yml
+++ b/db/docs/boards_epic_user_preferences.yml
@@ -7,3 +7,4 @@ feature_categories:
description: The epic_ids used to create swimlanes on issue boards; stored per user, per board
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40360
milestone: '13.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/broadcast_messages.yml b/db/docs/broadcast_messages.yml
index 1e4c181d48f..d6d6a93ad2e 100644
--- a/db/docs/broadcast_messages.yml
+++ b/db/docs/broadcast_messages.yml
@@ -7,3 +7,4 @@ feature_categories:
description: GitLab can display broadcast messages to users of a GitLab instance
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/f1ecf53c1e55fbbc66cb2d7d12fb411cbfc2ace8
milestone: '6.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/bulk_import_configurations.yml b/db/docs/bulk_import_configurations.yml
index 8456e834230..9ddb3115068 100644
--- a/db/docs/bulk_import_configurations.yml
+++ b/db/docs/bulk_import_configurations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store the configuration details of a bulk import of groups or projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42978
milestone: '13.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/bulk_import_entities.yml b/db/docs/bulk_import_entities.yml
index 250df850949..2232d68679a 100644
--- a/db/docs/bulk_import_entities.yml
+++ b/db/docs/bulk_import_entities.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store and track the status of the migration of groups or projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42978
milestone: '13.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/bulk_import_export_uploads.yml b/db/docs/bulk_import_export_uploads.yml
index 40b418b2711..494678098a1 100644
--- a/db/docs/bulk_import_export_uploads.yml
+++ b/db/docs/bulk_import_export_uploads.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store information of the exported files containing the data of groups or projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59976
milestone: '13.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/bulk_import_exports.yml b/db/docs/bulk_import_exports.yml
index 27af7fd1892..cc70f92ec8f 100644
--- a/db/docs/bulk_import_exports.yml
+++ b/db/docs/bulk_import_exports.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to track the generation status of export files for groups or projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59976
milestone: '13.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/bulk_import_failures.yml b/db/docs/bulk_import_failures.yml
index 87a127f47c4..3d15e06e2f0 100644
--- a/db/docs/bulk_import_failures.yml
+++ b/db/docs/bulk_import_failures.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store failures that occur during the migration of groups or projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47526
milestone: '13.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/bulk_import_trackers.yml b/db/docs/bulk_import_trackers.yml
index 6f31bfb7c80..66f0158e771 100644
--- a/db/docs/bulk_import_trackers.yml
+++ b/db/docs/bulk_import_trackers.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store and track the status of each pipeline associated with the migration of groups or projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47009
milestone: '13.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/bulk_imports.yml b/db/docs/bulk_imports.yml
index 354cb9c4ef7..c5f41fdc896 100644
--- a/db/docs/bulk_imports.yml
+++ b/db/docs/bulk_imports.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store and track the status of a bulk import request of groups or projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42978
milestone: '13.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/chat_names.yml b/db/docs/chat_names.yml
index c588c65dca5..fd1efaf91d7 100644
--- a/db/docs/chat_names.yml
+++ b/db/docs/chat_names.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Stores mappings from external chat accounts to GitLab users.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/7450
milestone: '8.14'
+gitlab_schema: gitlab_main
diff --git a/db/docs/chat_teams.yml b/db/docs/chat_teams.yml
index 295947d77c8..d50e596279a 100644
--- a/db/docs/chat_teams.yml
+++ b/db/docs/chat_teams.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Stores mappings from external chat teams to GitLab groups.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/8746
milestone: '9.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/ci_build_needs.yml b/db/docs/ci_build_needs.yml
index f9367124ed2..7253e83601a 100644
--- a/db/docs/ci_build_needs.yml
+++ b/db/docs/ci_build_needs.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Dependencies for a specific CI/CD job.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/31328
milestone: '12.2'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_build_pending_states.yml b/db/docs/ci_build_pending_states.yml
index aa9e07d64b5..24b640c9598 100644
--- a/db/docs/ci_build_pending_states.yml
+++ b/db/docs/ci_build_pending_states.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41585
milestone: '13.4'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_build_report_results.yml b/db/docs/ci_build_report_results.yml
index b1f112aea3c..388b4d8445d 100644
--- a/db/docs/ci_build_report_results.yml
+++ b/db/docs/ci_build_report_results.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores data related to the build that finished, including junit test data.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32991
milestone: '13.1'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_build_trace_chunks.yml b/db/docs/ci_build_trace_chunks.yml
index b44f04d3e60..3d7bd10192d 100644
--- a/db/docs/ci_build_trace_chunks.yml
+++ b/db/docs/ci_build_trace_chunks.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/9d6fe7bfdf9ff3f68ee73baa0e3d0aa7df13c351
milestone: '10.8'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_build_trace_metadata.yml b/db/docs/ci_build_trace_metadata.yml
index 54c35cf9c76..25b45fbc95c 100644
--- a/db/docs/ci_build_trace_metadata.yml
+++ b/db/docs/ci_build_trace_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68171
milestone: '14.2'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_builds.yml b/db/docs/ci_builds.yml
index fdaecfe22e8..547338b5969 100644
--- a/db/docs/ci_builds.yml
+++ b/db/docs/ci_builds.yml
@@ -11,3 +11,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/046b28312704f3131e72dcd2dbdacc5264d4aa62
milestone: '8.0'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_builds_metadata.yml b/db/docs/ci_builds_metadata.yml
index ce5c72cf0f1..71f8a0c5427 100644
--- a/db/docs/ci_builds_metadata.yml
+++ b/db/docs/ci_builds_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/1dde609ca6b130aa0a3d39e929edee7e770e62fc
milestone: '10.7'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_builds_runner_session.yml b/db/docs/ci_builds_runner_session.yml
index 0e46442da3f..282da1de91d 100644
--- a/db/docs/ci_builds_runner_session.yml
+++ b/db/docs/ci_builds_runner_session.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Store build-related runner session. Data is removed after the respective job transitions from running to any state.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6208
milestone: '11.1'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_daily_build_group_report_results.yml b/db/docs/ci_daily_build_group_report_results.yml
index 3e75950f462..1d88a4c3e40 100644
--- a/db/docs/ci_daily_build_group_report_results.yml
+++ b/db/docs/ci_daily_build_group_report_results.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores daily aggregated data related to the build group, including code coverage data.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30387
milestone: '13.0'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_deleted_objects.yml b/db/docs/ci_deleted_objects.yml
index a2e108e6c0a..bb35f63b438 100644
--- a/db/docs/ci_deleted_objects.yml
+++ b/db/docs/ci_deleted_objects.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Allows efficient batch deletion of data in object storage.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/9bf76fe03f8edf4f67023448161af27abb8fb521
milestone: '13.5'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_freeze_periods.yml b/db/docs/ci_freeze_periods.yml
index 5c6e25ecc32..0267f925149 100644
--- a/db/docs/ci_freeze_periods.yml
+++ b/db/docs/ci_freeze_periods.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/ci/environments/deployment_safety.html#prevent-deployments-during-deploy-freeze-windows
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29162
milestone: '13.0'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_group_variables.yml b/db/docs/ci_group_variables.yml
index de361da918d..c504a601c4c 100644
--- a/db/docs/ci_group_variables.yml
+++ b/db/docs/ci_group_variables.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/5b0954759cc24bdba97be89bb117c5440174f859
milestone: '9.4'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_instance_variables.yml b/db/docs/ci_instance_variables.yml
index 94d7c08a0fa..c39fe03b993 100644
--- a/db/docs/ci_instance_variables.yml
+++ b/db/docs/ci_instance_variables.yml
@@ -7,3 +7,4 @@ feature_categories:
description: CI/CD variables available to all projects and groups in an instance.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30156
milestone: '13.0'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_job_artifact_states.yml b/db/docs/ci_job_artifact_states.yml
index 6fe7443dd26..4c9116a059b 100644
--- a/db/docs/ci_job_artifact_states.yml
+++ b/db/docs/ci_job_artifact_states.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Separate table for job artifacts containing Geo verification metadata.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75264
milestone: '14.8'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_job_artifacts.yml b/db/docs/ci_job_artifacts.yml
index 492132315b6..bff81fa5ae9 100644
--- a/db/docs/ci_job_artifacts.yml
+++ b/db/docs/ci_job_artifacts.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Stores artifacts produced by a build.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/61864a5a5bb523953589c9398a431c4369fbfc76
milestone: '10.3'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_job_token_project_scope_links.yml b/db/docs/ci_job_token_project_scope_links.yml
index 993d392bb93..de6d69d1c64 100644
--- a/db/docs/ci_job_token_project_scope_links.yml
+++ b/db/docs/ci_job_token_project_scope_links.yml
@@ -4,6 +4,10 @@ classes:
- Ci::JobToken::ProjectScopeLink
feature_categories:
- continuous_integration
-description: The connection between a source project, which defines the job token scope, and a target project, which is the one allowed to be accessed by the job token.
+description: |
+ Links a source project and target project, allowing a project's job token to give access to another project.
+ Using the outbound direction, the source project's job token can access target projects.
+ Using the inbound direction, the source project can be accessed by the target project's job token.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62733
milestone: '14.0'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_job_variables.yml b/db/docs/ci_job_variables.yml
index f913ee4f800..1cbabb4fcbb 100644
--- a/db/docs/ci_job_variables.yml
+++ b/db/docs/ci_job_variables.yml
@@ -7,3 +7,4 @@ feature_categories:
description: CI/CD variables set to a job when running it manually.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/14784
milestone: '12.2'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_minutes_additional_packs.yml b/db/docs/ci_minutes_additional_packs.yml
index 37e5e9b1423..2be58f2cdde 100644
--- a/db/docs/ci_minutes_additional_packs.yml
+++ b/db/docs/ci_minutes_additional_packs.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62393
milestone: '14.0'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_namespace_mirrors.yml b/db/docs/ci_namespace_mirrors.yml
index 31349cfa94c..dd6b8eab821 100644
--- a/db/docs/ci_namespace_mirrors.yml
+++ b/db/docs/ci_namespace_mirrors.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Mirrors some data from the `main` database into the `ci` database so that we can join directly in a single query
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75621
milestone: '14.6'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_namespace_monthly_usages.yml b/db/docs/ci_namespace_monthly_usages.yml
index 601d208f1af..276cc52c7f3 100644
--- a/db/docs/ci_namespace_monthly_usages.yml
+++ b/db/docs/ci_namespace_monthly_usages.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52915
milestone: '13.9'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_partitions.yml b/db/docs/ci_partitions.yml
index 8dfa31f05f9..98144deb0c2 100644
--- a/db/docs/ci_partitions.yml
+++ b/db/docs/ci_partitions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Database partitioning metadata for CI tables
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96856
milestone: '15.4'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_pending_builds.yml b/db/docs/ci_pending_builds.yml
index 68a31db0868..5622df4feab 100644
--- a/db/docs/ci_pending_builds.yml
+++ b/db/docs/ci_pending_builds.yml
@@ -4,6 +4,7 @@ classes:
- Ci::PendingBuild
feature_categories:
- continuous_integration
-description: TODO
+description: Pending builds metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61581
milestone: '14.0'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_pipeline_artifacts.yml b/db/docs/ci_pipeline_artifacts.yml
index 124fe4de90b..6d498f6c99a 100644
--- a/db/docs/ci_pipeline_artifacts.yml
+++ b/db/docs/ci_pipeline_artifacts.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores aggregated artifacts produced by a pipeline.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37969
milestone: '13.3'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_pipeline_chat_data.yml b/db/docs/ci_pipeline_chat_data.yml
index 1edd3c923f2..eeef86bd4dd 100644
--- a/db/docs/ci_pipeline_chat_data.yml
+++ b/db/docs/ci_pipeline_chat_data.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores information about a CI pipeline created via chatops
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4466
milestone: '10.6'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_pipeline_messages.yml b/db/docs/ci_pipeline_messages.yml
index ad759f5f7e6..603b7934939 100644
--- a/db/docs/ci_pipeline_messages.yml
+++ b/db/docs/ci_pipeline_messages.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33762
milestone: '13.2'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_pipeline_metadata.yml b/db/docs/ci_pipeline_metadata.yml
index ed0bd896841..8a255916261 100644
--- a/db/docs/ci_pipeline_metadata.yml
+++ b/db/docs/ci_pipeline_metadata.yml
@@ -1,9 +1,10 @@
---
-table_name: ci_pipelines_metadata
+table_name: ci_pipeline_metadata
classes:
- Ci::PipelineMetadata
feature_categories:
- continuous_integration
-description: 'Stores additional information about CI pipelines'
+description: Stores additional information about CI pipelines
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97139
milestone: '15.5'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_pipeline_schedule_variables.yml b/db/docs/ci_pipeline_schedule_variables.yml
index 20c439c7d87..9cf48ef2fea 100644
--- a/db/docs/ci_pipeline_schedule_variables.yml
+++ b/db/docs/ci_pipeline_schedule_variables.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/d278da48f837292491aaf81649afef1da3a1eb09
milestone: '9.4'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_pipeline_schedules.yml b/db/docs/ci_pipeline_schedules.yml
index e61e3adfb82..a5a17c0941a 100644
--- a/db/docs/ci_pipeline_schedules.yml
+++ b/db/docs/ci_pipeline_schedules.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/8df3997a92bffa2d29f3c559933a336b837cdb93
milestone: '9.2'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_pipeline_variables.yml b/db/docs/ci_pipeline_variables.yml
index a98f17405bc..aaf49b17c99 100644
--- a/db/docs/ci_pipeline_variables.yml
+++ b/db/docs/ci_pipeline_variables.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/56418e85ac6b667d19495665860092ce4d74f55d
milestone: '9.5'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_pipelines.yml b/db/docs/ci_pipelines.yml
index 8931c5233f3..62ec2508ad9 100644
--- a/db/docs/ci_pipelines.yml
+++ b/db/docs/ci_pipelines.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/c6ae290cea4b88ecaa9cfe0bc9d88e8fd32070c1
milestone: '9.0'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_pipelines_config.yml b/db/docs/ci_pipelines_config.yml
index c3a5f33fda0..42a7b460bbd 100644
--- a/db/docs/ci_pipelines_config.yml
+++ b/db/docs/ci_pipelines_config.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21827
milestone: '12.7'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_platform_metrics.yml b/db/docs/ci_platform_metrics.yml
index b96f613f3ac..c3026d66783 100644
--- a/db/docs/ci_platform_metrics.yml
+++ b/db/docs/ci_platform_metrics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Instrumentation for https://docs.gitlab.com/ee/ci/cloud_deployment/ecs/quick_start_guide.html
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40036
milestone: '13.4'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_project_mirrors.yml b/db/docs/ci_project_mirrors.yml
index f04fbf80596..d6619880b20 100644
--- a/db/docs/ci_project_mirrors.yml
+++ b/db/docs/ci_project_mirrors.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Mirrors some data from the `main` database into the `ci` database so that we can join directly in a single query
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75621
milestone: '14.6'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_project_monthly_usages.yml b/db/docs/ci_project_monthly_usages.yml
index e7f0dc2b17b..03d84aa669f 100644
--- a/db/docs/ci_project_monthly_usages.yml
+++ b/db/docs/ci_project_monthly_usages.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53460
milestone: '13.9'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_refs.yml b/db/docs/ci_refs.yml
index ce2d9256a9c..1e4da221241 100644
--- a/db/docs/ci_refs.yml
+++ b/db/docs/ci_refs.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16951
milestone: '12.9'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_resource_groups.yml b/db/docs/ci_resource_groups.yml
index 716dea0b182..ea66284ccd1 100644
--- a/db/docs/ci_resource_groups.yml
+++ b/db/docs/ci_resource_groups.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/ci/resource_groups/
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20950
milestone: '12.7'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_resources.yml b/db/docs/ci_resources.yml
index 2fac94eb416..1950db54890 100644
--- a/db/docs/ci_resources.yml
+++ b/db/docs/ci_resources.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20950
milestone: '12.7'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_runner_namespaces.yml b/db/docs/ci_runner_namespaces.yml
index ea57281076d..e6d4693e58c 100644
--- a/db/docs/ci_runner_namespaces.yml
+++ b/db/docs/ci_runner_namespaces.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Relationships between runners and namespaces for group runners
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/1549239849adf31a078be7503ab2288795e337cf
milestone: '10.8'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_runner_projects.yml b/db/docs/ci_runner_projects.yml
index 120044e4a34..3819cddb8a0 100644
--- a/db/docs/ci_runner_projects.yml
+++ b/db/docs/ci_runner_projects.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Relationships between runners and projects for project runners
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/046b28312704f3131e72dcd2dbdacc5264d4aa62
milestone: '8.0'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_runner_versions.yml b/db/docs/ci_runner_versions.yml
index e0221e3956f..cb983222a6e 100644
--- a/db/docs/ci_runner_versions.yml
+++ b/db/docs/ci_runner_versions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Information about used Ci::Runner versions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90982
milestone: '15.2'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_runners.yml b/db/docs/ci_runners.yml
index 1ca8cb39738..5930adf7062 100644
--- a/db/docs/ci_runners.yml
+++ b/db/docs/ci_runners.yml
@@ -9,3 +9,4 @@ feature_categories:
description: Registered CI runners
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/046b28312704f3131e72dcd2dbdacc5264d4aa62
milestone: '8.0'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_running_builds.yml b/db/docs/ci_running_builds.yml
index 5a004ac9c61..72e941a8665 100644
--- a/db/docs/ci_running_builds.yml
+++ b/db/docs/ci_running_builds.yml
@@ -4,6 +4,13 @@ classes:
- Ci::RunningBuild
feature_categories:
- continuous_integration
-description: TODO
+description: >
+ Running builds metadata.
+ Despite the generic `RunningBuild` name, in this first iteration it applies only to shared runners.
+ The decision to insert all of the running builds here was deferred to avoid the pressure on the database as
+ at this time that was not necessary.
+ We can reconsider the decision to limit this only to shared runners when there is more evidence that inserting all
+ of the running builds there is worth the additional pressure.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62912
milestone: '14.0'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_secure_file_states.yml b/db/docs/ci_secure_file_states.yml
index 5e8a748e52a..5734f040f2f 100644
--- a/db/docs/ci_secure_file_states.yml
+++ b/db/docs/ci_secure_file_states.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores verification state for Geo replicated Project-level Secure Files.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90510
milestone: '15.2'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_secure_files.yml b/db/docs/ci_secure_files.yml
index deeb24b02a5..6124eeade28 100644
--- a/db/docs/ci_secure_files.yml
+++ b/db/docs/ci_secure_files.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77886
milestone: '14.7'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_sources_pipelines.yml b/db/docs/ci_sources_pipelines.yml
index 1fdc758a181..7e0121ab66e 100644
--- a/db/docs/ci_sources_pipelines.yml
+++ b/db/docs/ci_sources_pipelines.yml
@@ -7,3 +7,4 @@ feature_categories:
description: It stores parent-child and cross-project pipeline relationships.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/163983e0d7d1dab622846441bd0dd9086c78a69f
milestone: '9.3'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_sources_projects.yml b/db/docs/ci_sources_projects.yml
index b39e3a20f03..10aba3025d8 100644
--- a/db/docs/ci_sources_projects.yml
+++ b/db/docs/ci_sources_projects.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20063
milestone: '12.9'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_stages.yml b/db/docs/ci_stages.yml
index 9c71193dfd4..553e4a612bf 100644
--- a/db/docs/ci_stages.yml
+++ b/db/docs/ci_stages.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/acc22a8422cd1471819510aa375c455b5ea009c5
milestone: '9.3'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_subscriptions_projects.yml b/db/docs/ci_subscriptions_projects.yml
index 0e55c95d6bb..e383432eac6 100644
--- a/db/docs/ci_subscriptions_projects.yml
+++ b/db/docs/ci_subscriptions_projects.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18678
milestone: '12.5'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_trigger_requests.yml b/db/docs/ci_trigger_requests.yml
index 25360f81f50..15e15e9b7bc 100644
--- a/db/docs/ci_trigger_requests.yml
+++ b/db/docs/ci_trigger_requests.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/046b28312704f3131e72dcd2dbdacc5264d4aa62
milestone: '8.0'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_triggers.yml b/db/docs/ci_triggers.yml
index c0e556b481c..d17efc7b85a 100644
--- a/db/docs/ci_triggers.yml
+++ b/db/docs/ci_triggers.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/046b28312704f3131e72dcd2dbdacc5264d4aa62
milestone: '8.0'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_unit_test_failures.yml b/db/docs/ci_unit_test_failures.yml
index 9a1b27e8062..90f6e780e19 100644
--- a/db/docs/ci_unit_test_failures.yml
+++ b/db/docs/ci_unit_test_failures.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores unit test failure data produced from builds.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56137
milestone: '13.11'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_unit_tests.yml b/db/docs/ci_unit_tests.yml
index 46b405678f0..501795a7182 100644
--- a/db/docs/ci_unit_tests.yml
+++ b/db/docs/ci_unit_tests.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores unit test data produced from builds.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56137
milestone: '13.11'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/ci_variables.yml b/db/docs/ci_variables.yml
index 2636ff2aa30..bcb6b3f3753 100644
--- a/db/docs/ci_variables.yml
+++ b/db/docs/ci_variables.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/046b28312704f3131e72dcd2dbdacc5264d4aa62
milestone: '8.0'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/cluster_agent_tokens.yml b/db/docs/cluster_agent_tokens.yml
index a08684e4e84..24b093c948c 100644
--- a/db/docs/cluster_agent_tokens.yml
+++ b/db/docs/cluster_agent_tokens.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Tokens used by cluster agents to connect to GitLab
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33228
milestone: '13.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/cluster_agents.yml b/db/docs/cluster_agents.yml
index 59090f2ff75..14574fb82f4 100644
--- a/db/docs/cluster_agents.yml
+++ b/db/docs/cluster_agents.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Represents a GitLab Agent for Kubernetes installed in a Kubernetes cluster
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33228
milestone: '13.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/cluster_enabled_grants.yml b/db/docs/cluster_enabled_grants.yml
index 7a8faba26d6..873bbb4b7b5 100644
--- a/db/docs/cluster_enabled_grants.yml
+++ b/db/docs/cluster_enabled_grants.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists information about namespaces which got an extended life for certificate based clusters
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87149
milestone: '15.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/cluster_groups.yml b/db/docs/cluster_groups.yml
index 8a20ad3d562..ff5c3d890fd 100644
--- a/db/docs/cluster_groups.yml
+++ b/db/docs/cluster_groups.yml
@@ -4,6 +4,7 @@ classes:
- Clusters::Group
feature_categories:
- kubernetes_management
-description: (Deprecated) Join table between 'clusters' and 'namespaces'
+description: "(Deprecated) Join table between 'clusters' and 'namespaces'"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/0e15eec86d83cbdfefe17966bf5c02e4d419a34d
milestone: '11.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/cluster_platforms_kubernetes.yml b/db/docs/cluster_platforms_kubernetes.yml
index ab1b53e36b4..c92705b7c46 100644
--- a/db/docs/cluster_platforms_kubernetes.yml
+++ b/db/docs/cluster_platforms_kubernetes.yml
@@ -4,6 +4,7 @@ classes:
- Clusters::Platforms::Kubernetes
feature_categories:
- kubernetes_management
-description: (Deprecated) Kubernetes specific details for a cluster integration
+description: "(Deprecated) Kubernetes specific details for a cluster integration"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/d0cff7f5855f91b5479f9fdaa39d8d95ec691a9e
milestone: '10.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/cluster_projects.yml b/db/docs/cluster_projects.yml
index 51f996c0d5c..89bcd726e1f 100644
--- a/db/docs/cluster_projects.yml
+++ b/db/docs/cluster_projects.yml
@@ -4,6 +4,7 @@ classes:
- Clusters::Project
feature_categories:
- kubernetes_management
-description: (Deprecated) Join table between 'clusters' and 'projects'
+description: "(Deprecated) Join table between 'clusters' and 'projects'"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/d0cff7f5855f91b5479f9fdaa39d8d95ec691a9e
milestone: '10.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/cluster_providers_aws.yml b/db/docs/cluster_providers_aws.yml
index 1af9a814685..7c32b9291d1 100644
--- a/db/docs/cluster_providers_aws.yml
+++ b/db/docs/cluster_providers_aws.yml
@@ -4,6 +4,7 @@ classes:
- Clusters::Providers::Aws
feature_categories:
- kubernetes_management
-description: (Deprecated) AWS specific details for an EKS cluster integration
+description: "(Deprecated) AWS specific details for an EKS cluster integration"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17057
milestone: '12.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/cluster_providers_gcp.yml b/db/docs/cluster_providers_gcp.yml
index 3ef9ebb995c..cc89320d346 100644
--- a/db/docs/cluster_providers_gcp.yml
+++ b/db/docs/cluster_providers_gcp.yml
@@ -4,6 +4,7 @@ classes:
- Clusters::Providers::Gcp
feature_categories:
- kubernetes_management
-description: (Deprecated) GCP specific details for a GKE cluster integration
+description: "(Deprecated) GCP specific details for a GKE cluster integration"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/d0cff7f5855f91b5479f9fdaa39d8d95ec691a9e
milestone: '10.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/clusters.yml b/db/docs/clusters.yml
index cad0fd4c3b3..f791c84bfa2 100644
--- a/db/docs/clusters.yml
+++ b/db/docs/clusters.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists information about GitLab managed clusters
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/14879
milestone: '10.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/clusters_applications_cert_managers.yml b/db/docs/clusters_applications_cert_managers.yml
index bf85400c3ae..36bc15e045c 100644
--- a/db/docs/clusters_applications_cert_managers.yml
+++ b/db/docs/clusters_applications_cert_managers.yml
@@ -4,6 +4,7 @@ classes:
- Clusters::Applications::CertManager
feature_categories:
- kubernetes_management
-description: (Deprecated) A GitLab managed cert-manager installation in a Kubernetes cluster
+description: "(Deprecated) A GitLab managed cert-manager installation in a Kubernetes cluster"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/8837519445c319a699e0f3ced1c6912c839f3389
milestone: '11.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/clusters_applications_cilium.yml b/db/docs/clusters_applications_cilium.yml
index 1fa71a93911..8744ee73633 100644
--- a/db/docs/clusters_applications_cilium.yml
+++ b/db/docs/clusters_applications_cilium.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Information about installed instance of Cilium in the cluster
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34601
milestone: '13.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/clusters_applications_crossplane.yml b/db/docs/clusters_applications_crossplane.yml
index ef9bbed9415..f633f746e70 100644
--- a/db/docs/clusters_applications_crossplane.yml
+++ b/db/docs/clusters_applications_crossplane.yml
@@ -4,6 +4,7 @@ classes:
- Clusters::Applications::Crossplane
feature_categories:
- kubernetes_management
-description: (Deprecated) A GitLab managed Crossplane installation in a Kubernetes cluster
+description: "(Deprecated) A GitLab managed Crossplane installation in a Kubernetes cluster"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18797
milestone: '12.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/clusters_applications_helm.yml b/db/docs/clusters_applications_helm.yml
index 83014ab9221..bb849a21cae 100644
--- a/db/docs/clusters_applications_helm.yml
+++ b/db/docs/clusters_applications_helm.yml
@@ -4,6 +4,7 @@ classes:
- Clusters::Applications::Helm
feature_categories:
- kubernetes_management
-description: (Deprecated) A GitLab managed Helm installation in a Kubernetes cluster
+description: "(Deprecated) A GitLab managed Helm installation in a Kubernetes cluster"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/64be8d70ae20928df351e495a3442bb6036bc3e7
milestone: '10.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/clusters_applications_ingress.yml b/db/docs/clusters_applications_ingress.yml
index 73c0d8d7cb4..02dda5a4498 100644
--- a/db/docs/clusters_applications_ingress.yml
+++ b/db/docs/clusters_applications_ingress.yml
@@ -4,6 +4,7 @@ classes:
- Clusters::Applications::Ingress
feature_categories:
- kubernetes_management
-description: (Deprecated) A GitLab managed Ingress installation in a Kubernetes cluster
+description: "(Deprecated) A GitLab managed Ingress installation in a Kubernetes cluster"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/d8223468ae2ae061020cc26336c51dc93cc75571
milestone: '10.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/clusters_applications_jupyter.yml b/db/docs/clusters_applications_jupyter.yml
index faff294bf6b..b15b889779b 100644
--- a/db/docs/clusters_applications_jupyter.yml
+++ b/db/docs/clusters_applications_jupyter.yml
@@ -4,6 +4,7 @@ classes:
- Clusters::Applications::Jupyter
feature_categories:
- kubernetes_management
-description: (Deprecated) A GitLab managed Jupyter installation in a Kubernetes cluster
+description: "(Deprecated) A GitLab managed Jupyter installation in a Kubernetes cluster"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/db9f765852d9fef464e69c0bf47a382f2ab7219d
milestone: '11.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/clusters_applications_knative.yml b/db/docs/clusters_applications_knative.yml
index 4d8d3a30ad5..e17a0284a1f 100644
--- a/db/docs/clusters_applications_knative.yml
+++ b/db/docs/clusters_applications_knative.yml
@@ -4,6 +4,7 @@ classes:
- Clusters::Applications::Knative
feature_categories:
- kubernetes_management
-description: (Deprecated) A GitLab managed Knative installation in a Kubernetes cluster
+description: "(Deprecated) A GitLab managed Knative installation in a Kubernetes cluster"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/b5155b90ee233e2824c168fbb06b3ce5d3aeb194
milestone: '11.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/clusters_applications_prometheus.yml b/db/docs/clusters_applications_prometheus.yml
index 394ab63f81f..9b99ffba5db 100644
--- a/db/docs/clusters_applications_prometheus.yml
+++ b/db/docs/clusters_applications_prometheus.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Information about installed instance of Prometheus in the cluster
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/0d4548026f3060ca0a8f7aa8d8fc89838bc66130
milestone: '10.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/clusters_applications_runners.yml b/db/docs/clusters_applications_runners.yml
index 06c43337b83..c2fd4799cf9 100644
--- a/db/docs/clusters_applications_runners.yml
+++ b/db/docs/clusters_applications_runners.yml
@@ -4,6 +4,7 @@ classes:
- Clusters::Applications::Runner
feature_categories:
- kubernetes_management
-description: (Deprecated) A GitLab managed Runner installation in a Kubernetes cluster
+description: "(Deprecated) A GitLab managed Runner installation in a Kubernetes cluster"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/c607008ee55e35465e04a938a341f2f24cb6761f
milestone: '10.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/clusters_integration_prometheus.yml b/db/docs/clusters_integration_prometheus.yml
index f8702226daa..a0abd9935d8 100644
--- a/db/docs/clusters_integration_prometheus.yml
+++ b/db/docs/clusters_integration_prometheus.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists information about prometheus cluster integration
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59091
milestone: '13.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/clusters_kubernetes_namespaces.yml b/db/docs/clusters_kubernetes_namespaces.yml
index 0772f9ce877..3572cfa4f2c 100644
--- a/db/docs/clusters_kubernetes_namespaces.yml
+++ b/db/docs/clusters_kubernetes_namespaces.yml
@@ -4,6 +4,7 @@ classes:
- Clusters::KubernetesNamespace
feature_categories:
- kubernetes_management
-description: (Deprecated) A Kubernetes namespace in a GitLab managed Kubernetes cluster
+description: "(Deprecated) A Kubernetes namespace in a GitLab managed Kubernetes cluster"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/e3ca493876ab71ed29817a0af436fc563f564bbe
milestone: '11.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/commit_user_mentions.yml b/db/docs/commit_user_mentions.yml
index e04f2001d9c..6dbd0a1f1bf 100644
--- a/db/docs/commit_user_mentions.yml
+++ b/db/docs/commit_user_mentions.yml
@@ -4,6 +4,7 @@ classes:
- CommitUserMention
feature_categories:
- team_planning
-description: User mentions in commit messages
+description: User mentions in commit messages
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19009
milestone: '12.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/compliance_management_frameworks.yml b/db/docs/compliance_management_frameworks.yml
index edf89095593..9a75e43a938 100644
--- a/db/docs/compliance_management_frameworks.yml
+++ b/db/docs/compliance_management_frameworks.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44098
milestone: '13.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/container_expiration_policies.yml b/db/docs/container_expiration_policies.yml
index a7027ee6f5e..8cc8c675cf9 100644
--- a/db/docs/container_expiration_policies.yml
+++ b/db/docs/container_expiration_policies.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Project level settings for container registry cleanup policies
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20412
milestone: '12.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/container_repositories.yml b/db/docs/container_repositories.yml
index c4eb599848a..472385b8e65 100644
--- a/db/docs/container_repositories.yml
+++ b/db/docs/container_repositories.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Container registry repositories (images), originally named container_images, renamed in https://gitlab.com/gitlab-org/gitlab/-/commit/01d159b409d8b24d36204979a73de249843d71bf
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10109
milestone: '9.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/content_blocked_states.yml b/db/docs/content_blocked_states.yml
index 0abf239a98b..38349b2014a 100644
--- a/db/docs/content_blocked_states.yml
+++ b/db/docs/content_blocked_states.yml
@@ -6,3 +6,4 @@ feature_categories:
description: JiHu only. Keeps list of restricted blobs.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72124
milestone: '14.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/conversational_development_index_metrics.yml b/db/docs/conversational_development_index_metrics.yml
index 9371f9f1bfb..507b21daf53 100644
--- a/db/docs/conversational_development_index_metrics.yml
+++ b/db/docs/conversational_development_index_metrics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Contains data for calculating DevOps score.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/26dde5f55f1dac2e6bea4f7e1dfa51c72dc756cb
milestone: '9.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/coverage_fuzzing_corpuses.yml b/db/docs/coverage_fuzzing_corpuses.yml
index 3e8e55cd4e6..38410c1a72d 100644
--- a/db/docs/coverage_fuzzing_corpuses.yml
+++ b/db/docs/coverage_fuzzing_corpuses.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores additional values describing corpuses used by coverage fuzzing
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71704
milestone: '14.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/csv_issue_imports.yml b/db/docs/csv_issue_imports.yml
index 7560876c4a0..c5bdd72e6e4 100644
--- a/db/docs/csv_issue_imports.yml
+++ b/db/docs/csv_issue_imports.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to report the unique user usage of the CSV Issue Import feature
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44742
milestone: '13.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/custom_emoji.yml b/db/docs/custom_emoji.yml
index 205e652835e..2b20ea014f4 100644
--- a/db/docs/custom_emoji.yml
+++ b/db/docs/custom_emoji.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Filename and name of custom emoji created by users
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24229
milestone: '13.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/customer_relations_contacts.yml b/db/docs/customer_relations_contacts.yml
index 57fce1e0def..dcb7e3184dd 100644
--- a/db/docs/customer_relations_contacts.yml
+++ b/db/docs/customer_relations_contacts.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Contacts, against which time can be spent by users on issues using the CRM functionality
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67985
milestone: '14.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/customer_relations_organizations.yml b/db/docs/customer_relations_organizations.yml
index b25c70a4fd2..07f9ed01ca6 100644
--- a/db/docs/customer_relations_organizations.yml
+++ b/db/docs/customer_relations_organizations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Organizations, against which time can be spent by users on issues using the CRM functionality
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67551
milestone: '14.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dast_pre_scan_verification_steps.yml b/db/docs/dast_pre_scan_verification_steps.yml
new file mode 100644
index 00000000000..9b7e7a78d09
--- /dev/null
+++ b/db/docs/dast_pre_scan_verification_steps.yml
@@ -0,0 +1,10 @@
+---
+table_name: dast_pre_scan_verification_steps
+classes:
+- Dast::PreScanVerificationStep
+feature_categories:
+- dynamic_application_security_testing
+description: Verification step status for DAST Profiles
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105702
+milestone: '15.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dast_pre_scan_verifications.yml b/db/docs/dast_pre_scan_verifications.yml
new file mode 100644
index 00000000000..45d39335264
--- /dev/null
+++ b/db/docs/dast_pre_scan_verifications.yml
@@ -0,0 +1,10 @@
+---
+table_name: dast_pre_scan_verifications
+classes:
+- Dast::PreScanVerifications
+feature_categories:
+- dynamic_application_security_testing
+description: Verification status for DAST Profiles
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103063
+milestone: '15.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dast_profile_schedules.yml b/db/docs/dast_profile_schedules.yml
index 820251d2e16..b7846b6d798 100644
--- a/db/docs/dast_profile_schedules.yml
+++ b/db/docs/dast_profile_schedules.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Scheduling for scans using DAST Profiles
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65327
milestone: '14.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dast_profiles.yml b/db/docs/dast_profiles.yml
index bd909be59f1..a7999915039 100644
--- a/db/docs/dast_profiles.yml
+++ b/db/docs/dast_profiles.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Profile used to run a DAST on-demand scan
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51296
milestone: '13.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dast_profiles_pipelines.yml b/db/docs/dast_profiles_pipelines.yml
index 3b972423083..19a235d6ba4 100644
--- a/db/docs/dast_profiles_pipelines.yml
+++ b/db/docs/dast_profiles_pipelines.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Join table between DAST Profiles and CI Pipelines
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56821
milestone: '13.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dast_scanner_profiles.yml b/db/docs/dast_scanner_profiles.yml
index bb5850abbd2..8d69bef4b60 100644
--- a/db/docs/dast_scanner_profiles.yml
+++ b/db/docs/dast_scanner_profiles.yml
@@ -7,3 +7,4 @@ feature_categories:
description: A scanner profile defines the scanner settings used to run an on-demand scan
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37404
milestone: '13.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dast_scanner_profiles_builds.yml b/db/docs/dast_scanner_profiles_builds.yml
index 8beed026a0b..c4531c21fbf 100644
--- a/db/docs/dast_scanner_profiles_builds.yml
+++ b/db/docs/dast_scanner_profiles_builds.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Join table between DAST Scanner Profiles and CI Builds
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63362
milestone: '14.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dast_scanner_profiles_tags.yml b/db/docs/dast_scanner_profiles_tags.yml
new file mode 100644
index 00000000000..9766ce6c4fc
--- /dev/null
+++ b/db/docs/dast_scanner_profiles_tags.yml
@@ -0,0 +1,10 @@
+---
+table_name: dast_scanner_profiles_tags
+classes:
+ - Dast::ScannerProfileTag
+feature_categories:
+ - dynamic_application_security_testing
+description: Join Table for Runner tags and DAST Scanner Profiles
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104909
+milestone: '15.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dast_site_profile_secret_variables.yml b/db/docs/dast_site_profile_secret_variables.yml
index d1711d5f6e7..c9a51911dc9 100644
--- a/db/docs/dast_site_profile_secret_variables.yml
+++ b/db/docs/dast_site_profile_secret_variables.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Secret variables used in DAST on-demand scans
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56067
milestone: '13.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dast_site_profiles.yml b/db/docs/dast_site_profiles.yml
index a584a8eaf22..e9542426cd7 100644
--- a/db/docs/dast_site_profiles.yml
+++ b/db/docs/dast_site_profiles.yml
@@ -7,3 +7,4 @@ feature_categories:
description: A site profile describes the attributes of a web site to scan on demand with DAST
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36659
milestone: '13.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dast_site_profiles_builds.yml b/db/docs/dast_site_profiles_builds.yml
index 71bfea2e122..bbe3dbe7396 100644
--- a/db/docs/dast_site_profiles_builds.yml
+++ b/db/docs/dast_site_profiles_builds.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Join table between DAST Site Profiles and CI Builds
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63362
milestone: '14.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dast_site_profiles_pipelines.yml b/db/docs/dast_site_profiles_pipelines.yml
index 022b241934e..21f622fad6b 100644
--- a/db/docs/dast_site_profiles_pipelines.yml
+++ b/db/docs/dast_site_profiles_pipelines.yml
@@ -6,3 +6,4 @@ feature_categories:
description: Join table between DAST Site Profiles and CI Pipelines
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60090
milestone: '13.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dast_site_tokens.yml b/db/docs/dast_site_tokens.yml
index 1d92bcd6981..c96c76c9ded 100644
--- a/db/docs/dast_site_tokens.yml
+++ b/db/docs/dast_site_tokens.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Token for the site to be validated
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41639
milestone: '13.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dast_site_validations.yml b/db/docs/dast_site_validations.yml
index cb42895bc6a..7fc2e41f1ab 100644
--- a/db/docs/dast_site_validations.yml
+++ b/db/docs/dast_site_validations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: The site to be validated with a dast_site_token
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41639
milestone: '13.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dast_sites.yml b/db/docs/dast_sites.yml
index 63dcad7b35f..8e0faf2217e 100644
--- a/db/docs/dast_sites.yml
+++ b/db/docs/dast_sites.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Site to run dast scan on
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36659
milestone: '13.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/deleted_tables/alerts_service_data.yml b/db/docs/deleted_tables/alerts_service_data.yml
new file mode 100644
index 00000000000..46cef40c4c5
--- /dev/null
+++ b/db/docs/deleted_tables/alerts_service_data.yml
@@ -0,0 +1,9 @@
+---
+table_name: alerts_service_data
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16607
+milestone: '12.3'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53534
+removed_in_milestone: '13.9'
diff --git a/db/docs/deleted_tables/analytics_devops_adoption_segment_selections.yml b/db/docs/deleted_tables/analytics_devops_adoption_segment_selections.yml
new file mode 100644
index 00000000000..90008e3e68b
--- /dev/null
+++ b/db/docs/deleted_tables/analytics_devops_adoption_segment_selections.yml
@@ -0,0 +1,9 @@
+---
+table_name: analytics_devops_adoption_segment_selections
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45748
+milestone: '13.6'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62594
+removed_in_milestone: '14.0'
diff --git a/db/docs/deleted_tables/analytics_repository_file_commits.yml b/db/docs/deleted_tables/analytics_repository_file_commits.yml
new file mode 100644
index 00000000000..93a18171f02
--- /dev/null
+++ b/db/docs/deleted_tables/analytics_repository_file_commits.yml
@@ -0,0 +1,9 @@
+---
+table_name: analytics_repository_file_commits
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17277
+milestone: '12.4'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23590
+removed_in_milestone: '12.8'
diff --git a/db/docs/deleted_tables/analytics_repository_file_edits.yml b/db/docs/deleted_tables/analytics_repository_file_edits.yml
new file mode 100644
index 00000000000..58f66fb88ca
--- /dev/null
+++ b/db/docs/deleted_tables/analytics_repository_file_edits.yml
@@ -0,0 +1,9 @@
+---
+table_name: analytics_repository_file_edits
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17277
+milestone: '12.4'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24222
+removed_in_milestone: '12.8'
diff --git a/db/docs/deleted_tables/analytics_repository_files.yml b/db/docs/deleted_tables/analytics_repository_files.yml
new file mode 100644
index 00000000000..ae02c3aa29d
--- /dev/null
+++ b/db/docs/deleted_tables/analytics_repository_files.yml
@@ -0,0 +1,9 @@
+---
+table_name: analytics_repository_files
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: TODO
+milestone: TODO
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23590
+removed_in_milestone: '12.8'
diff --git a/db/docs/deleted_tables/audit_events_archived.yml b/db/docs/deleted_tables/audit_events_archived.yml
new file mode 100644
index 00000000000..6f94a8e4466
--- /dev/null
+++ b/db/docs/deleted_tables/audit_events_archived.yml
@@ -0,0 +1,9 @@
+---
+table_name: audit_events_archived
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44655
+milestone: '13.6'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53880
+removed_in_milestone: '13.11'
diff --git a/db/docs/deleted_tables/audit_events_part_5fc467ac26.yml b/db/docs/deleted_tables/audit_events_part_5fc467ac26.yml
new file mode 100644
index 00000000000..86d27af6f34
--- /dev/null
+++ b/db/docs/deleted_tables/audit_events_part_5fc467ac26.yml
@@ -0,0 +1,9 @@
+---
+table_name: audit_events_part_5fc467ac26
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36298
+milestone: '13.3'
+removed_by_url: TODO
+removed_in_milestone: TODO
diff --git a/db/docs/deleted_tables/backup_labels.yml b/db/docs/deleted_tables/backup_labels.yml
new file mode 100644
index 00000000000..52a092aca7b
--- /dev/null
+++ b/db/docs/deleted_tables/backup_labels.yml
@@ -0,0 +1,9 @@
+---
+table_name: backup_labels
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: TODO
+milestone: TODO
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54856
+removed_in_milestone: '13.10'
diff --git a/db/docs/deleted_tables/ci_build_trace_section_names.yml b/db/docs/deleted_tables/ci_build_trace_section_names.yml
new file mode 100644
index 00000000000..83092b0c12a
--- /dev/null
+++ b/db/docs/deleted_tables/ci_build_trace_section_names.yml
@@ -0,0 +1,9 @@
+---
+table_name: ci_build_trace_section_names
+gitlab_schema: gitlab_ci
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67618
+milestone: '14.2'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73841
+removed_in_milestone: '14.5'
diff --git a/db/docs/deleted_tables/ci_build_trace_sections.yml b/db/docs/deleted_tables/ci_build_trace_sections.yml
new file mode 100644
index 00000000000..764b9731f1a
--- /dev/null
+++ b/db/docs/deleted_tables/ci_build_trace_sections.yml
@@ -0,0 +1,9 @@
+---
+table_name: ci_build_trace_sections
+gitlab_schema: gitlab_ci
+feature_categories: []
+description: TODO
+introduced_by_url: TODO
+milestone: TODO
+removed_in_milestone: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73841
+removed_by_url: '14.5'
diff --git a/db/docs/deleted_tables/ci_daily_report_results.yml b/db/docs/deleted_tables/ci_daily_report_results.yml
new file mode 100644
index 00000000000..a95bf75e0c1
--- /dev/null
+++ b/db/docs/deleted_tables/ci_daily_report_results.yml
@@ -0,0 +1,9 @@
+---
+table_name: ci_daily_report_results
+gitlab_schema: gitlab_ci
+feature_categories: []
+description: TODO
+introduced_by_url: TODO
+milestone: TODO
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36102
+removed_in_milestone: '13.2'
diff --git a/db/docs/deleted_tables/ci_test_case_failures.yml b/db/docs/deleted_tables/ci_test_case_failures.yml
new file mode 100644
index 00000000000..0e62b617e56
--- /dev/null
+++ b/db/docs/deleted_tables/ci_test_case_failures.yml
@@ -0,0 +1,9 @@
+---
+table_name: ci_test_case_failures
+gitlab_schema: gitlab_ci
+feature_categories: []
+description: TODO
+introduced_by_url: TODO
+milestone: TODO
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67180
+removed_in_milestone: '14.2'
diff --git a/db/docs/deleted_tables/ci_test_cases.yml b/db/docs/deleted_tables/ci_test_cases.yml
new file mode 100644
index 00000000000..61b8d5f5472
--- /dev/null
+++ b/db/docs/deleted_tables/ci_test_cases.yml
@@ -0,0 +1,9 @@
+---
+table_name: ci_test_cases
+gitlab_schema: gitlab_ci
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45027
+milestone: '13.6'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67180
+removed_in_milestone: '14.2'
diff --git a/db/docs/deleted_tables/clusters_applications_fluentd.yml b/db/docs/deleted_tables/clusters_applications_fluentd.yml
new file mode 100644
index 00000000000..4f481290cc6
--- /dev/null
+++ b/db/docs/deleted_tables/clusters_applications_fluentd.yml
@@ -0,0 +1,9 @@
+---
+table_name: clusters_applications_fluentd
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28844
+milestone: '12.10'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63758
+removed_in_milestone: '14.1'
diff --git a/db/docs/deleted_tables/forked_project_links.yml b/db/docs/deleted_tables/forked_project_links.yml
new file mode 100644
index 00000000000..a83391f4340
--- /dev/null
+++ b/db/docs/deleted_tables/forked_project_links.yml
@@ -0,0 +1,9 @@
+---
+table_name: forked_project_links
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: TODO
+milestone: TODO
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20771
+removed_in_milestone: '12.9'
diff --git a/db/docs/deleted_tables/issue_milestones.yml b/db/docs/deleted_tables/issue_milestones.yml
new file mode 100644
index 00000000000..7e18cd12f7c
--- /dev/null
+++ b/db/docs/deleted_tables/issue_milestones.yml
@@ -0,0 +1,9 @@
+---
+table_name: issue_milestones
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: TODO
+milestone: TODO
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25198
+removed_in_milestone: '12.8'
diff --git a/db/docs/deleted_tables/merge_request_milestones.yml b/db/docs/deleted_tables/merge_request_milestones.yml
new file mode 100644
index 00000000000..3f42312c5ec
--- /dev/null
+++ b/db/docs/deleted_tables/merge_request_milestones.yml
@@ -0,0 +1,9 @@
+---
+table_name: merge_request_milestones
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22043
+milestone: '12.7'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25198
+removed_in_milestone: '12.8'
diff --git a/db/docs/deleted_tables/namespace_onboarding_actions.yml b/db/docs/deleted_tables/namespace_onboarding_actions.yml
new file mode 100644
index 00000000000..da3f42f3f2f
--- /dev/null
+++ b/db/docs/deleted_tables/namespace_onboarding_actions.yml
@@ -0,0 +1,9 @@
+---
+table_name: namespace_onboarding_actions
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48018
+milestone: '13.7'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53488
+removed_in_milestone: '13.9'
diff --git a/db/docs/deleted_tables/services.yml b/db/docs/deleted_tables/services.yml
new file mode 100644
index 00000000000..cc05b0e615b
--- /dev/null
+++ b/db/docs/deleted_tables/services.yml
@@ -0,0 +1,9 @@
+---
+table_name: services
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: TODO
+milestone: TODO
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64562
+removed_in_milestone: '14.1'
diff --git a/db/docs/deleted_tables/terraform_state_registry.yml b/db/docs/deleted_tables/terraform_state_registry.yml
new file mode 100644
index 00000000000..e8b5e1f857d
--- /dev/null
+++ b/db/docs/deleted_tables/terraform_state_registry.yml
@@ -0,0 +1,9 @@
+---
+table_name: terraform_state_registry
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36594
+milestone: '13.3'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43341
+removed_in_milestone: '13.5'
diff --git a/db/docs/deleted_tables/tmp_fingerprint_sha256_migration.yml b/db/docs/deleted_tables/tmp_fingerprint_sha256_migration.yml
new file mode 100644
index 00000000000..42b3f7d379e
--- /dev/null
+++ b/db/docs/deleted_tables/tmp_fingerprint_sha256_migration.yml
@@ -0,0 +1,9 @@
+---
+table_name: tmp_fingerprint_sha256_migration
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21579
+milestone: '12.7'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21579
+removed_in_milestone: '12.7'
diff --git a/db/docs/deleted_tables/vulnerability_export_registry.yml b/db/docs/deleted_tables/vulnerability_export_registry.yml
new file mode 100644
index 00000000000..90ae84ed128
--- /dev/null
+++ b/db/docs/deleted_tables/vulnerability_export_registry.yml
@@ -0,0 +1,9 @@
+---
+table_name: vulnerability_export_registry
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36620
+milestone: '13.3'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38299
+removed_in_milestone: '13.3'
diff --git a/db/docs/deleted_tables/vulnerability_export_verification_status.yml b/db/docs/deleted_tables/vulnerability_export_verification_status.yml
new file mode 100644
index 00000000000..ed9cced468d
--- /dev/null
+++ b/db/docs/deleted_tables/vulnerability_export_verification_status.yml
@@ -0,0 +1,9 @@
+---
+table_name: vulnerability_export_verification_status
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36620
+milestone: '13.3'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38299
+removed_in_milestone: '13.3'
diff --git a/db/docs/deleted_tables/vulnerability_finding_fingerprints.yml b/db/docs/deleted_tables/vulnerability_finding_fingerprints.yml
new file mode 100644
index 00000000000..dbcf8312d5a
--- /dev/null
+++ b/db/docs/deleted_tables/vulnerability_finding_fingerprints.yml
@@ -0,0 +1,9 @@
+---
+table_name: vulnerability_finding_fingerprints
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: TODO
+milestone: TODO
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57840
+removed_in_milestone: '13.11'
diff --git a/db/docs/deleted_tables/web_hook_logs_archived.yml b/db/docs/deleted_tables/web_hook_logs_archived.yml
new file mode 100644
index 00000000000..a55b9db157b
--- /dev/null
+++ b/db/docs/deleted_tables/web_hook_logs_archived.yml
@@ -0,0 +1,9 @@
+---
+table_name: web_hook_logs_archived
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60184
+milestone: '13.12'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63649
+removed_in_milestone: '14.0'
diff --git a/db/docs/deleted_tables/web_hook_logs_part_0c5294f417.yml b/db/docs/deleted_tables/web_hook_logs_part_0c5294f417.yml
new file mode 100644
index 00000000000..21867b7b2aa
--- /dev/null
+++ b/db/docs/deleted_tables/web_hook_logs_part_0c5294f417.yml
@@ -0,0 +1,9 @@
+---
+table_name: web_hook_logs_part_0c5294f417
+gitlab_schema: gitlab_main
+feature_categories: []
+description: TODO
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55938
+milestone: '13.10'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60184
+removed_in_milestone: '13.12'
diff --git a/db/docs/dependency_list_exports.yml b/db/docs/dependency_list_exports.yml
new file mode 100644
index 00000000000..14d222edb06
--- /dev/null
+++ b/db/docs/dependency_list_exports.yml
@@ -0,0 +1,8 @@
+---
+table_name: dependency_list_exports
+feature_categories:
+- dependency_management
+description: Dependency list exported data
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104361
+milestone: '15.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dependency_proxy_blob_states.yml b/db/docs/dependency_proxy_blob_states.yml
index ddb9414b5f8..acbd015f619 100644
--- a/db/docs/dependency_proxy_blob_states.yml
+++ b/db/docs/dependency_proxy_blob_states.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Separate table for dependency proxy blob verification states
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101429
milestone: '15.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dependency_proxy_blobs.yml b/db/docs/dependency_proxy_blobs.yml
index ad54ac8943e..78b0cc54013 100644
--- a/db/docs/dependency_proxy_blobs.yml
+++ b/db/docs/dependency_proxy_blobs.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Dependency proxy blob files
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/10676
milestone: '11.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dependency_proxy_group_settings.yml b/db/docs/dependency_proxy_group_settings.yml
index d975ca482de..53ec18594e0 100644
--- a/db/docs/dependency_proxy_group_settings.yml
+++ b/db/docs/dependency_proxy_group_settings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Group-level settings for the dependency proxy
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/10676
milestone: '11.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dependency_proxy_image_ttl_group_policies.yml b/db/docs/dependency_proxy_image_ttl_group_policies.yml
index f985c083118..6f744246b18 100644
--- a/db/docs/dependency_proxy_image_ttl_group_policies.yml
+++ b/db/docs/dependency_proxy_image_ttl_group_policies.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Group-level settings for dependency proxy cleanup policies
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68809
milestone: '14.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dependency_proxy_manifest_states.yml b/db/docs/dependency_proxy_manifest_states.yml
new file mode 100644
index 00000000000..e0ad1808da9
--- /dev/null
+++ b/db/docs/dependency_proxy_manifest_states.yml
@@ -0,0 +1,10 @@
+---
+table_name: dependency_proxy_manifest_states
+classes:
+ - Geo::DependencyProxyManifestState
+feature_categories:
+ - geo_replication
+description: Separate table for dependency proxy manifest verification states
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102908
+milestone: '15.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dependency_proxy_manifests.yml b/db/docs/dependency_proxy_manifests.yml
index 408878790a0..5ee2eeaef27 100644
--- a/db/docs/dependency_proxy_manifests.yml
+++ b/db/docs/dependency_proxy_manifests.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Dependency proxy manifest files
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48535
milestone: '13.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/deploy_keys_projects.yml b/db/docs/deploy_keys_projects.yml
index d308af56712..5d3591f1b4f 100644
--- a/db/docs/deploy_keys_projects.yml
+++ b/db/docs/deploy_keys_projects.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/user/project/deploy_keys/
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/a735ce2aa7da72242629a4452c33e7a1900fdd62
milestone: "<6.0"
+gitlab_schema: gitlab_main
diff --git a/db/docs/deploy_tokens.yml b/db/docs/deploy_tokens.yml
index 320fc9e2ba8..73b40699a58 100644
--- a/db/docs/deploy_tokens.yml
+++ b/db/docs/deploy_tokens.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/user/project/deploy_tokens/
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/db18993f652425b72c4b854e18a002e0ec44b196
milestone: '10.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/deployment_approvals.yml b/db/docs/deployment_approvals.yml
index 1defeb8dbb5..291e3f001c8 100644
--- a/db/docs/deployment_approvals.yml
+++ b/db/docs/deployment_approvals.yml
@@ -4,6 +4,9 @@ classes:
- Deployments::Approval
feature_categories:
- continuous_delivery
-description: https://docs.gitlab.com/ee/ci/environments/deployment_approvals.html
+description: >-
+ Stores the user that approved/rejected a deployment and which approval rule was used.
+ See https://docs.gitlab.com/ee/ci/environments/deployment_approvals.html for more details.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74932
milestone: '14.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/deployment_clusters.yml b/db/docs/deployment_clusters.yml
index e23278d0e00..eea538a83d2 100644
--- a/db/docs/deployment_clusters.yml
+++ b/db/docs/deployment_clusters.yml
@@ -4,6 +4,7 @@ classes:
- DeploymentCluster
feature_categories:
- kubernetes_management
-description: (Deprecated) Join table between `deployments` and `clusters`
+description: "(Deprecated) Join table between `deployments` and `clusters`"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24235
milestone: '12.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/deployment_merge_requests.yml b/db/docs/deployment_merge_requests.yml
index 9af247a03d8..7f1017fe5b0 100644
--- a/db/docs/deployment_merge_requests.yml
+++ b/db/docs/deployment_merge_requests.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/ci/environments/index.html#track-newly-included-merge-requests-per-deployment
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18755
milestone: '12.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/deployments.yml b/db/docs/deployments.yml
index 960e2c67a1e..3fc477efced 100644
--- a/db/docs/deployments.yml
+++ b/db/docs/deployments.yml
@@ -4,6 +4,9 @@ classes:
- Deployment
feature_categories:
- continuous_delivery
-description: https://docs.gitlab.com/ee/ci/environments/
+description: >-
+ Stores metadata related to a deployment CI Build, including user, environment, status, and SHA.
+ See https://docs.gitlab.com/ee/ci/environments/ for more details.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/907c0e6796b69f9577c147dd489cf55748c749ac
milestone: '8.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/description_versions.yml b/db/docs/description_versions.yml
index 8fb2d481fe9..d254aa1fa3f 100644
--- a/db/docs/description_versions.yml
+++ b/db/docs/description_versions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: The diff of the change when an edit is made to an Issue, MR or Epic description
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17147
milestone: '12.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/design_management_designs.yml b/db/docs/design_management_designs.yml
index abdd01899da..ea284c8bee4 100644
--- a/db/docs/design_management_designs.yml
+++ b/db/docs/design_management_designs.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Information about Designs, image files under management by the Design Management tool
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/9801
milestone: '11.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/design_management_designs_versions.yml b/db/docs/design_management_designs_versions.yml
index 2613cba55d9..3adff979ff5 100644
--- a/db/docs/design_management_designs_versions.yml
+++ b/db/docs/design_management_designs_versions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: The SHA referencing changes to a single design or multiple design files
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/10552
milestone: '11.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/design_management_versions.yml b/db/docs/design_management_versions.yml
index 8f411df6e93..03adc2154f4 100644
--- a/db/docs/design_management_versions.yml
+++ b/db/docs/design_management_versions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: The SHA referencing changes to individual designs made using the Design Management tool
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/9801
milestone: '11.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/design_user_mentions.yml b/db/docs/design_user_mentions.yml
index 74aa0d89c01..b13baa1bc5f 100644
--- a/db/docs/design_user_mentions.yml
+++ b/db/docs/design_user_mentions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: User mentions in content related to designs
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19009
milestone: '12.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/detached_partitions.yml b/db/docs/detached_partitions.yml
index 7563576c996..dbd77d472b3 100644
--- a/db/docs/detached_partitions.yml
+++ b/db/docs/detached_partitions.yml
@@ -15,3 +15,4 @@ description: >
Rows in this table are processed by Database::DropDetachedPartitionsWorker, which runs once a day.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67056
milestone: '14.2'
+gitlab_schema: gitlab_shared
diff --git a/db/docs/diff_note_positions.yml b/db/docs/diff_note_positions.yml
index 0c4f688b4d4..9aa292c3825 100644
--- a/db/docs/diff_note_positions.yml
+++ b/db/docs/diff_note_positions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores diff notes positions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28113
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dingtalk_tracker_data.yml b/db/docs/dingtalk_tracker_data.yml
index b7335584271..6994bd805a7 100644
--- a/db/docs/dingtalk_tracker_data.yml
+++ b/db/docs/dingtalk_tracker_data.yml
@@ -6,3 +6,4 @@ feature_categories:
- integrations
description: Data related to the Dingtalk integration (JiHu-specific, see https://jihulab.com/gitlab-cn/gitlab/-/merge_requests/417).
milestone: '15.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dora_configurations.yml b/db/docs/dora_configurations.yml
index e13cf088670..63114ba6f80 100644
--- a/db/docs/dora_configurations.yml
+++ b/db/docs/dora_configurations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores project specific configurations for DORA4 calculations.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96561
milestone: '15.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/dora_daily_metrics.yml b/db/docs/dora_daily_metrics.yml
index 09f2ad02bfe..52ffdfc7f1b 100644
--- a/db/docs/dora_daily_metrics.yml
+++ b/db/docs/dora_daily_metrics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores daily snapshots of DORA4 metrics per environment.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55473
milestone: '13.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/draft_notes.yml b/db/docs/draft_notes.yml
index 9273fb5fa61..047241a7976 100644
--- a/db/docs/draft_notes.yml
+++ b/db/docs/draft_notes.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Notes created during the review of an MR that are not yet published
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4213
milestone: '11.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/elastic_index_settings.yml b/db/docs/elastic_index_settings.yml
index 61093803d35..213f66a46f2 100644
--- a/db/docs/elastic_index_settings.yml
+++ b/db/docs/elastic_index_settings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56344
milestone: '13.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/elastic_reindexing_slices.yml b/db/docs/elastic_reindexing_slices.yml
index 0829431270d..b547c96a35d 100644
--- a/db/docs/elastic_reindexing_slices.yml
+++ b/db/docs/elastic_reindexing_slices.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55681
milestone: '13.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/elastic_reindexing_subtasks.yml b/db/docs/elastic_reindexing_subtasks.yml
index 85f540cfb41..86e2c84b069 100644
--- a/db/docs/elastic_reindexing_subtasks.yml
+++ b/db/docs/elastic_reindexing_subtasks.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48334
milestone: '13.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/elastic_reindexing_tasks.yml b/db/docs/elastic_reindexing_tasks.yml
index 049dae71d4c..f4e5fa92b46 100644
--- a/db/docs/elastic_reindexing_tasks.yml
+++ b/db/docs/elastic_reindexing_tasks.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34069
milestone: '13.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/elasticsearch_indexed_namespaces.yml b/db/docs/elasticsearch_indexed_namespaces.yml
index ca854727c12..870918da54e 100644
--- a/db/docs/elasticsearch_indexed_namespaces.yml
+++ b/db/docs/elasticsearch_indexed_namespaces.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/9861
milestone: '11.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/elasticsearch_indexed_projects.yml b/db/docs/elasticsearch_indexed_projects.yml
index 3703f7a0c6b..78d8e2e58f7 100644
--- a/db/docs/elasticsearch_indexed_projects.yml
+++ b/db/docs/elasticsearch_indexed_projects.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/9861
milestone: '11.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/emails.yml b/db/docs/emails.yml
index 229861b1d91..8b107994d7d 100644
--- a/db/docs/emails.yml
+++ b/db/docs/emails.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores users email records
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/29cfd33d949d21d67f3892473c24d4f0a127dfe6
milestone: '6.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/environments.yml b/db/docs/environments.yml
index 08165712766..d7fcce52898 100644
--- a/db/docs/environments.yml
+++ b/db/docs/environments.yml
@@ -4,6 +4,9 @@ classes:
- Environment
feature_categories:
- continuous_delivery
-description: https://docs.gitlab.com/ee/ci/environments/
+description: >-
+ Project-level deployment target and metadata.
+ See https://docs.gitlab.com/ee/ci/environments/ for more details.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/907c0e6796b69f9577c147dd489cf55748c749ac
milestone: '8.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/epic_issues.yml b/db/docs/epic_issues.yml
index 506005eee4c..5a79385e5f2 100644
--- a/db/docs/epic_issues.yml
+++ b/db/docs/epic_issues.yml
@@ -7,3 +7,4 @@ feature_categories:
description: The relationships between Epics and Issues
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3302
milestone: '10.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/epic_metrics.yml b/db/docs/epic_metrics.yml
index 4cb6ea86ba2..700a33a4956 100644
--- a/db/docs/epic_metrics.yml
+++ b/db/docs/epic_metrics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3126
milestone: '10.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/epic_user_mentions.yml b/db/docs/epic_user_mentions.yml
index 8d2803b2526..b6998cb482a 100644
--- a/db/docs/epic_user_mentions.yml
+++ b/db/docs/epic_user_mentions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: User mentions in epic descriptions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19009
milestone: '12.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/epics.yml b/db/docs/epics.yml
index a8a07c445d0..2cc82b178d6 100644
--- a/db/docs/epics.yml
+++ b/db/docs/epics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Information directly related to Epics, used by epic management features and the roadmap
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3126
milestone: '10.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/error_tracking_client_keys.yml b/db/docs/error_tracking_client_keys.yml
index c07fc282839..81fc55a197c 100644
--- a/db/docs/error_tracking_client_keys.yml
+++ b/db/docs/error_tracking_client_keys.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Model to store public keys used by Sentry SDK for Error Tracking
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66466
milestone: '14.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/error_tracking_error_events.yml b/db/docs/error_tracking_error_events.yml
index 9d938e47e3c..4e0d6b5a268 100644
--- a/db/docs/error_tracking_error_events.yml
+++ b/db/docs/error_tracking_error_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists error event data for the Error Tracking's GitLab backend
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64712
milestone: '14.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/error_tracking_errors.yml b/db/docs/error_tracking_errors.yml
index a961d759da3..f42f248dad7 100644
--- a/db/docs/error_tracking_errors.yml
+++ b/db/docs/error_tracking_errors.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists error data for the Error Tracking's GitLab backend
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64712
milestone: '14.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/events.yml b/db/docs/events.yml
index d766bc7cae3..45e3d49fd94 100644
--- a/db/docs/events.yml
+++ b/db/docs/events.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Stores user generated events.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/a847501fd2ffc1c4becc7d0d352d80168d9b3568
milestone: "<6.0"
+gitlab_schema: gitlab_main
diff --git a/db/docs/evidences.yml b/db/docs/evidences.yml
index ddfb42dd5a1..d7f36bc6e04 100644
--- a/db/docs/evidences.yml
+++ b/db/docs/evidences.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/user/project/releases/#release-evidence
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17217
milestone: '12.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/experiment_subjects.yml b/db/docs/experiment_subjects.yml
deleted file mode 100644
index 85546028cd1..00000000000
--- a/db/docs/experiment_subjects.yml
+++ /dev/null
@@ -1,9 +0,0 @@
----
-table_name: experiment_subjects
-classes:
-- ExperimentSubject
-feature_categories:
-- experimentation_conversion
-description: TODO
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47042
-milestone: '13.7'
diff --git a/db/docs/experiments.yml b/db/docs/experiments.yml
deleted file mode 100644
index ef2ccfa8d89..00000000000
--- a/db/docs/experiments.yml
+++ /dev/null
@@ -1,9 +0,0 @@
----
-table_name: experiments
-classes:
-- Experiment
-feature_categories:
-- experimentation_conversion
-description: TODO
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38397
-milestone: '13.3'
diff --git a/db/docs/external_approval_rules.yml b/db/docs/external_approval_rules.yml
index ac44d0e6c48..73e77f653b4 100644
--- a/db/docs/external_approval_rules.yml
+++ b/db/docs/external_approval_rules.yml
@@ -6,3 +6,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54002
milestone: '13.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/external_approval_rules_protected_branches.yml b/db/docs/external_approval_rules_protected_branches.yml
index de4e1af7214..f1f85f4374b 100644
--- a/db/docs/external_approval_rules_protected_branches.yml
+++ b/db/docs/external_approval_rules_protected_branches.yml
@@ -6,3 +6,4 @@ feature_categories:
description: Keeps relation between protected branches and external approval rules
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54002
milestone: '13.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/external_pull_requests.yml b/db/docs/external_pull_requests.yml
index c8864dabfdc..e3777ae67ba 100644
--- a/db/docs/external_pull_requests.yml
+++ b/db/docs/external_pull_requests.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/ca6a1f33f91a8cceadebfb9c4e9ac6afa340f71d
milestone: '12.3'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/external_status_checks.yml b/db/docs/external_status_checks.yml
index 1bb1bc03224..5f7ea9b5314 100644
--- a/db/docs/external_status_checks.yml
+++ b/db/docs/external_status_checks.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores project's external status checks
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62186
milestone: '14.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/external_status_checks_protected_branches.yml b/db/docs/external_status_checks_protected_branches.yml
index bf26689bd0b..c8f33b2b13a 100644
--- a/db/docs/external_status_checks_protected_branches.yml
+++ b/db/docs/external_status_checks_protected_branches.yml
@@ -6,3 +6,4 @@ feature_categories:
description: Keeps relation between protected branches and external status checks
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62186
milestone: '14.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/feature_gates.yml b/db/docs/feature_gates.yml
index 19d74975c6e..10060ad38ba 100644
--- a/db/docs/feature_gates.yml
+++ b/db/docs/feature_gates.yml
@@ -8,3 +8,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/development/feature_flags/
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/671284ba375109becbfa2a288032cdc7301b157b
milestone: '9.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/features.yml b/db/docs/features.yml
index f5628a17c19..9866eff2a3f 100644
--- a/db/docs/features.yml
+++ b/db/docs/features.yml
@@ -8,3 +8,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/development/feature_flags/
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/ee2d3de1a634611a1c660516c955be0d3000904b
milestone: '8.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/fork_network_members.yml b/db/docs/fork_network_members.yml
index 2077977f1b7..c3dd193b4aa 100644
--- a/db/docs/fork_network_members.yml
+++ b/db/docs/fork_network_members.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Keeps track of fork relations between projects.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62186
milestone: '10.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/fork_networks.yml b/db/docs/fork_networks.yml
index 51123405baf..ca0960dd93a 100644
--- a/db/docs/fork_networks.yml
+++ b/db/docs/fork_networks.yml
@@ -7,3 +7,4 @@ feature_categories:
description: When a project is first forked, a row is created in this table. Also referenced by the fork_network_members table. This is used to know which projects can send merge reqeusts to each other.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3098
milestone: '10.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/geo_cache_invalidation_events.yml b/db/docs/geo_cache_invalidation_events.yml
index 5695cddfb7f..5fc2e0b5a7e 100644
--- a/db/docs/geo_cache_invalidation_events.yml
+++ b/db/docs/geo_cache_invalidation_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Geo event to process feature flag toggles instantly on a secondary by invalidating the cache, belongs to geo_event_log.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7738
milestone: '11.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/geo_container_repository_updated_events.yml b/db/docs/geo_container_repository_updated_events.yml
index 8d17ded97d4..a07758da54e 100644
--- a/db/docs/geo_container_repository_updated_events.yml
+++ b/db/docs/geo_container_repository_updated_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Geo event for when a container repository (image, tag, registry) gets updated, belongs to geo_event_log.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/1902d9cc74a1dc2c87fdbb39a6cdbb67092cbb5a
milestone: '12.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/geo_event_log.yml b/db/docs/geo_event_log.yml
index 905383664e5..8aee2b6858d 100644
--- a/db/docs/geo_event_log.yml
+++ b/db/docs/geo_event_log.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Log of all events that a Geo secondary can process. Parsed/watched through streaming replication on all secondaries.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/cb6c7cbe2a9ee05cea6926e3d8c18f6aa26f4c64
milestone: '9.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/geo_events.yml b/db/docs/geo_events.yml
index 6e30da25f5a..0fc3db22dca 100644
--- a/db/docs/geo_events.yml
+++ b/db/docs/geo_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Geo events implemented generically, used by the SSF where all object types can generate an event to be processed by the secondary sites.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23447
milestone: '12.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/geo_hashed_storage_attachments_events.yml b/db/docs/geo_hashed_storage_attachments_events.yml
index 457b34af0f1..f025cc8f892 100644
--- a/db/docs/geo_hashed_storage_attachments_events.yml
+++ b/db/docs/geo_hashed_storage_attachments_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to replicate storage attachments migration paths on Geo secondaries from regular to hashed storage.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3544
milestone: '10.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/geo_hashed_storage_migrated_events.yml b/db/docs/geo_hashed_storage_migrated_events.yml
index 175015c487d..4d92998851e 100644
--- a/db/docs/geo_hashed_storage_migrated_events.yml
+++ b/db/docs/geo_hashed_storage_migrated_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to replicate repository migration paths on Geo secondaries from regular to hashed storage.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3066
milestone: '10.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/geo_node_namespace_links.yml b/db/docs/geo_node_namespace_links.yml
index cede321ed47..198b4ac0833 100644
--- a/db/docs/geo_node_namespace_links.yml
+++ b/db/docs/geo_node_namespace_links.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Passthrough table for geo_nodes many-to-many namespaces relation.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/14c6128530579ca92fa79342d4119d25bcff1f2d
milestone: '9.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/geo_node_statuses.yml b/db/docs/geo_node_statuses.yml
index ff955e79eb1..8b6dd28318e 100644
--- a/db/docs/geo_node_statuses.yml
+++ b/db/docs/geo_node_statuses.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Contains sites status and metadata for each Geo site, updated async through a scheduled worker.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3230
milestone: '10.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/geo_nodes.yml b/db/docs/geo_nodes.yml
index 956b79fe0a0..c1f0feb2deb 100644
--- a/db/docs/geo_nodes.yml
+++ b/db/docs/geo_nodes.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Contains Geo sites configuration data and settings.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/5ab12ad02ed753dd933485094ba45512890f0b50
milestone: '8.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/geo_repositories_changed_events.yml b/db/docs/geo_repositories_changed_events.yml
index 348d8331fe7..f9da7c623f1 100644
--- a/db/docs/geo_repositories_changed_events.yml
+++ b/db/docs/geo_repositories_changed_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Geo event for when the repositories for selective sync of a specific Geo secondary change, belongs to geo_event_log.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/312bc703a4619b87ba2ac4e59623e7747a24502c
milestone: '9.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/geo_repository_created_events.yml b/db/docs/geo_repository_created_events.yml
index ea7b8558bdc..14825a70e12 100644
--- a/db/docs/geo_repository_created_events.yml
+++ b/db/docs/geo_repository_created_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Geo event for when a repository gets created, belongs to geo_event_log.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/f3eacf881659b7af97b7c7ba3289237ec6cdc1cb
milestone: '10.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/geo_repository_deleted_events.yml b/db/docs/geo_repository_deleted_events.yml
index 4814994181a..43dc2ac54c6 100644
--- a/db/docs/geo_repository_deleted_events.yml
+++ b/db/docs/geo_repository_deleted_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Geo event for when a repository gets deleted, belongs to geo_event_log.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/04c3da24ac5975b140cf2e6a7e33414543f148f5
milestone: '9.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/geo_repository_renamed_events.yml b/db/docs/geo_repository_renamed_events.yml
index 2e6838f51e4..be54571864c 100644
--- a/db/docs/geo_repository_renamed_events.yml
+++ b/db/docs/geo_repository_renamed_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Geo event for when a repository gets renamed, belongs to geo_event_log.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/6e5fa040d1c689fad4e110dd10be8ddba61ea7ef
milestone: '9.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/geo_repository_updated_events.yml b/db/docs/geo_repository_updated_events.yml
index 51a0033d0a7..53186621519 100644
--- a/db/docs/geo_repository_updated_events.yml
+++ b/db/docs/geo_repository_updated_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Geo event for when a repository gets updated (content changed), belongs to geo_event_log.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/71cc57b1e4b7721c93107357517235a18f7ba8e2
milestone: '9.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/geo_reset_checksum_events.yml b/db/docs/geo_reset_checksum_events.yml
index d4aad156687..c57dda44569 100644
--- a/db/docs/geo_reset_checksum_events.yml
+++ b/db/docs/geo_reset_checksum_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Geo event for when a project gets reverified on the primary, belongs to geo_event_log.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7394
milestone: '11.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/ghost_user_migrations.yml b/db/docs/ghost_user_migrations.yml
index f4e69e71baa..d90eca9f153 100644
--- a/db/docs/ghost_user_migrations.yml
+++ b/db/docs/ghost_user_migrations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Users records awaiting for their associated records to be migrated to ghost user
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95473
milestone: '15.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/gitlab_subscription_histories.yml b/db/docs/gitlab_subscription_histories.yml
index 1b84c943a04..25cf11ff8bc 100644
--- a/db/docs/gitlab_subscription_histories.yml
+++ b/db/docs/gitlab_subscription_histories.yml
@@ -7,3 +7,4 @@ feature_categories:
description: History log for the gitlab_subscriptions table
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19694
milestone: '12.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/gitlab_subscriptions.yml b/db/docs/gitlab_subscriptions.yml
index d4a15216aa6..9b0b718c394 100644
--- a/db/docs/gitlab_subscriptions.yml
+++ b/db/docs/gitlab_subscriptions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store information related to GitLab subscriptions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7885
milestone: '11.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/gpg_key_subkeys.yml b/db/docs/gpg_key_subkeys.yml
index b3824c36e81..3c92c807566 100644
--- a/db/docs/gpg_key_subkeys.yml
+++ b/db/docs/gpg_key_subkeys.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores GPG subkeys
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/14517
milestone: '10.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/gpg_keys.yml b/db/docs/gpg_keys.yml
index 00b76959fe4..bb8fc7bc371 100644
--- a/db/docs/gpg_keys.yml
+++ b/db/docs/gpg_keys.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores GPG keys
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/9546
milestone: '9.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/gpg_signatures.yml b/db/docs/gpg_signatures.yml
index f49a0c03844..e5866ef6517 100644
--- a/db/docs/gpg_signatures.yml
+++ b/db/docs/gpg_signatures.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores GPG signatures
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/9546
milestone: '9.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/grafana_integrations.yml b/db/docs/grafana_integrations.yml
index c479b07ffb1..bcbc5b2b172 100644
--- a/db/docs/grafana_integrations.yml
+++ b/db/docs/grafana_integrations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17234
milestone: '12.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/group_crm_settings.yml b/db/docs/group_crm_settings.yml
index 3798512d05e..36ade1ba134 100644
--- a/db/docs/group_crm_settings.yml
+++ b/db/docs/group_crm_settings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Group-level settings for CRM-related features
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76983
milestone: '14.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/group_custom_attributes.yml b/db/docs/group_custom_attributes.yml
index 3bf4f6a0f92..a6984004149 100644
--- a/db/docs/group_custom_attributes.yml
+++ b/db/docs/group_custom_attributes.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores custom attributes per group
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/14593
milestone: '10.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/group_deletion_schedules.yml b/db/docs/group_deletion_schedules.yml
index b3bc6665c4c..ff271e4b1f0 100644
--- a/db/docs/group_deletion_schedules.yml
+++ b/db/docs/group_deletion_schedules.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20276
milestone: '12.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/group_deploy_keys.yml b/db/docs/group_deploy_keys.yml
index 0e85102dbb9..137d2774c90 100644
--- a/db/docs/group_deploy_keys.yml
+++ b/db/docs/group_deploy_keys.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/user/project/deploy_keys/
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30886
milestone: '13.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/group_deploy_keys_groups.yml b/db/docs/group_deploy_keys_groups.yml
index 3db288647f9..129be2af5df 100644
--- a/db/docs/group_deploy_keys_groups.yml
+++ b/db/docs/group_deploy_keys_groups.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/user/project/deploy_keys/
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32901
milestone: '13.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/group_deploy_tokens.yml b/db/docs/group_deploy_tokens.yml
index 6b497f59285..450f67c57b1 100644
--- a/db/docs/group_deploy_tokens.yml
+++ b/db/docs/group_deploy_tokens.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/user/project/deploy_tokens/
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23460
milestone: '12.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/group_features.yml b/db/docs/group_features.yml
index ca156be4117..68b84a2a08e 100644
--- a/db/docs/group_features.yml
+++ b/db/docs/group_features.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores access levels for group features, like the wiki
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82017
milestone: '14.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/group_group_links.yml b/db/docs/group_group_links.yml
index e9671034330..f1541871795 100644
--- a/db/docs/group_group_links.yml
+++ b/db/docs/group_group_links.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17117
milestone: '12.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/group_import_states.yml b/db/docs/group_import_states.yml
index fe51c414c25..6343895264b 100644
--- a/db/docs/group_import_states.yml
+++ b/db/docs/group_import_states.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store and track the group import status when using the Import/Export feature
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29588
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/group_merge_request_approval_settings.yml b/db/docs/group_merge_request_approval_settings.yml
index 33bb2370a86..c3b6bb8877c 100644
--- a/db/docs/group_merge_request_approval_settings.yml
+++ b/db/docs/group_merge_request_approval_settings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Keeps merge request approval settings per group
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50256
milestone: '13.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/group_repository_storage_moves.yml b/db/docs/group_repository_storage_moves.yml
index 439dfa381cc..68df9b30b7c 100644
--- a/db/docs/group_repository_storage_moves.yml
+++ b/db/docs/group_repository_storage_moves.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51803
milestone: '13.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/group_wiki_repositories.yml b/db/docs/group_wiki_repositories.yml
index 0c4ce444958..278c643c8e7 100644
--- a/db/docs/group_wiki_repositories.yml
+++ b/db/docs/group_wiki_repositories.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores information about group wiki repositories.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31121
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/historical_data.yml b/db/docs/historical_data.yml
index 75f3493cb6d..020cc33b25d 100644
--- a/db/docs/historical_data.yml
+++ b/db/docs/historical_data.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/85c04a8aa654d1b7f898e55a113e50521bacaaf2
milestone: '7.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/identities.yml b/db/docs/identities.yml
index 078fb1197d7..149907a419e 100644
--- a/db/docs/identities.yml
+++ b/db/docs/identities.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/1a80d13a3990937580c97e2b0ba8fb98f69bc055
milestone: '7.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/import_export_uploads.yml b/db/docs/import_export_uploads.yml
index 6cac47c8490..3c1bcf4f8fd 100644
--- a/db/docs/import_export_uploads.yml
+++ b/db/docs/import_export_uploads.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store the location of the imported or exported archives files of groups or projects when using the feature Import/Export
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/a2bf1641546a1d3eeb3e9f44734854f655c0adef
milestone: '11.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/import_failures.yml b/db/docs/import_failures.yml
index 7fb01579573..ac30148ba7a 100644
--- a/db/docs/import_failures.yml
+++ b/db/docs/import_failures.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store group or project import failures that occur when using the Import/Export feature
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20727
milestone: '12.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/in_product_marketing_emails.yml b/db/docs/in_product_marketing_emails.yml
index 443b67aaae5..be33ee824b6 100644
--- a/db/docs/in_product_marketing_emails.yml
+++ b/db/docs/in_product_marketing_emails.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55840
milestone: '13.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/incident_management_escalation_policies.yml b/db/docs/incident_management_escalation_policies.yml
index 9584f65b14d..74f58525767 100644
--- a/db/docs/incident_management_escalation_policies.yml
+++ b/db/docs/incident_management_escalation_policies.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists information about escalation policies in a project
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60685
milestone: '13.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/incident_management_escalation_rules.yml b/db/docs/incident_management_escalation_rules.yml
index 40c1f9bdcc0..8081bdcb880 100644
--- a/db/docs/incident_management_escalation_rules.yml
+++ b/db/docs/incident_management_escalation_rules.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists information about escalation rules for incident management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60685
milestone: '13.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/incident_management_issuable_escalation_statuses.yml b/db/docs/incident_management_issuable_escalation_statuses.yml
index 466bc0314b6..24e20245b41 100644
--- a/db/docs/incident_management_issuable_escalation_statuses.yml
+++ b/db/docs/incident_management_issuable_escalation_statuses.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists escalation status information for incidents
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65206
milestone: '14.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/incident_management_oncall_participants.yml b/db/docs/incident_management_oncall_participants.yml
index 9186be4824c..75e2651d234 100644
--- a/db/docs/incident_management_oncall_participants.yml
+++ b/db/docs/incident_management_oncall_participants.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Persists information about on-call rotation participants
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49058
milestone: '13.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/incident_management_oncall_rotations.yml b/db/docs/incident_management_oncall_rotations.yml
index bf9df2e2d2a..8d0a50ad4e1 100644
--- a/db/docs/incident_management_oncall_rotations.yml
+++ b/db/docs/incident_management_oncall_rotations.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Persists information about on-call rotation
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49058
milestone: '13.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/incident_management_oncall_schedules.yml b/db/docs/incident_management_oncall_schedules.yml
index 9fa0ed1bec5..b2ddd795b30 100644
--- a/db/docs/incident_management_oncall_schedules.yml
+++ b/db/docs/incident_management_oncall_schedules.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Persists on-call schedules for incident management in a project
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47407
milestone: '13.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/incident_management_oncall_shifts.yml b/db/docs/incident_management_oncall_shifts.yml
index 6ef7de5da50..2ae33b4430a 100644
--- a/db/docs/incident_management_oncall_shifts.yml
+++ b/db/docs/incident_management_oncall_shifts.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Tracks past and present on-call shifts
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49423
milestone: '13.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/incident_management_pending_alert_escalations.yml b/db/docs/incident_management_pending_alert_escalations.yml
index c39b8d74ce2..a866fbaab01 100644
--- a/db/docs/incident_management_pending_alert_escalations.yml
+++ b/db/docs/incident_management_pending_alert_escalations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists information about pending alert escalations for incidents
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64274
milestone: '14.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/incident_management_pending_issue_escalations.yml b/db/docs/incident_management_pending_issue_escalations.yml
index eb8f11fc72f..945aff4b4cd 100644
--- a/db/docs/incident_management_pending_issue_escalations.yml
+++ b/db/docs/incident_management_pending_issue_escalations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Represents when issues should be escalated according to a project's escalation policy
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65209
milestone: '14.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/incident_management_timeline_event_tag_links.yml b/db/docs/incident_management_timeline_event_tag_links.yml
index 429371aefb7..e3a2b31e093 100644
--- a/db/docs/incident_management_timeline_event_tag_links.yml
+++ b/db/docs/incident_management_timeline_event_tag_links.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists links between timeline event tags and timeline events.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100271
milestone: '15.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/incident_management_timeline_event_tags.yml b/db/docs/incident_management_timeline_event_tags.yml
index 47dedaf3de2..aba8f7db152 100644
--- a/db/docs/incident_management_timeline_event_tags.yml
+++ b/db/docs/incident_management_timeline_event_tags.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists tags for timeline events in a project.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100271
milestone: '15.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/incident_management_timeline_events.yml b/db/docs/incident_management_timeline_events.yml
index 6031f0d32e2..428d25d71cb 100644
--- a/db/docs/incident_management_timeline_events.yml
+++ b/db/docs/incident_management_timeline_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists timeline events for an incident
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74530
milestone: '14.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/index_statuses.yml b/db/docs/index_statuses.yml
index 933795a265a..5ff3b46d145 100644
--- a/db/docs/index_statuses.yml
+++ b/db/docs/index_statuses.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/03370b017c7b120af7b53682714ffc325742fc98
milestone: '8.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/insights.yml b/db/docs/insights.yml
index 2439f289340..3dce08e218d 100644
--- a/db/docs/insights.yml
+++ b/db/docs/insights.yml
@@ -7,3 +7,4 @@ feature_categories:
description: The table is used to specify a project which contains the group level insights configuration.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/9665
milestone: '11.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/integrations.yml b/db/docs/integrations.yml
index 5100ee77fa7..52d719e19da 100644
--- a/db/docs/integrations.yml
+++ b/db/docs/integrations.yml
@@ -21,7 +21,6 @@ classes:
- Integrations::EmailsOnPush
- Integrations::Ewm
- Integrations::ExternalWiki
-- Integrations::Flowdock
- Integrations::Github
- Integrations::GitlabSlackApplication
- Integrations::HangoutsChat
@@ -56,3 +55,4 @@ description: |
https://gitlab.com/gitlab-org/gitlab/-/commit/1dab19d0d7b25cb5af27b8d10c8b615b2d38c2cf
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64562
milestone: '9.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/internal_ids.yml b/db/docs/internal_ids.yml
index 100e58ad921..5109a51802c 100644
--- a/db/docs/internal_ids.yml
+++ b/db/docs/internal_ids.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Keeps track of counters scoped to a certain context, e.g. a project-wide counter for issues.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17580
milestone: '10.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/ip_restrictions.yml b/db/docs/ip_restrictions.yml
index 6437cda47f2..93f0da0505a 100644
--- a/db/docs/ip_restrictions.yml
+++ b/db/docs/ip_restrictions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/12669
milestone: '12.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/issuable_metric_images.yml b/db/docs/issuable_metric_images.yml
index d4460d5e31c..1cc39a8de12 100644
--- a/db/docs/issuable_metric_images.yml
+++ b/db/docs/issuable_metric_images.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46845
milestone: '13.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/issuable_resource_links.yml b/db/docs/issuable_resource_links.yml
index e58355cadd6..b2ad7c0c301 100644
--- a/db/docs/issuable_resource_links.yml
+++ b/db/docs/issuable_resource_links.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists resources links for an issuable, particularly incident.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88417
milestone: '15.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/issuable_severities.yml b/db/docs/issuable_severities.yml
index 5f9a8f5ba43..724561ce460 100644
--- a/db/docs/issuable_severities.yml
+++ b/db/docs/issuable_severities.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Integer representing severity applied to issues, currently used for incidents
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40272
milestone: '13.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/issuable_slas.yml b/db/docs/issuable_slas.yml
index 14e94a3aed3..6668dc7c4d9 100644
--- a/db/docs/issuable_slas.yml
+++ b/db/docs/issuable_slas.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists information about incident SLAs for incidents
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44253
milestone: '13.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/issue_assignees.yml b/db/docs/issue_assignees.yml
index f6a06e7c51d..dbfd277213c 100644
--- a/db/docs/issue_assignees.yml
+++ b/db/docs/issue_assignees.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Users assigned to an issue
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1541
milestone: '9.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/issue_customer_relations_contacts.yml b/db/docs/issue_customer_relations_contacts.yml
index 1548835f6d3..ca7c3dcdc09 100644
--- a/db/docs/issue_customer_relations_contacts.yml
+++ b/db/docs/issue_customer_relations_contacts.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Holds Customer Relations (CRM) Contacts
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71007
milestone: '14.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/issue_email_participants.yml b/db/docs/issue_email_participants.yml
index 8cbed5f9d8f..38d40f535b6 100644
--- a/db/docs/issue_email_participants.yml
+++ b/db/docs/issue_email_participants.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Email addresses for non-GitLab users added to issues as participants
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42943
milestone: '13.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/issue_emails.yml b/db/docs/issue_emails.yml
index 3f6c3ee3d72..4ae4e309338 100644
--- a/db/docs/issue_emails.yml
+++ b/db/docs/issue_emails.yml
@@ -8,3 +8,4 @@ feature_categories:
description: The Message-ID of the original email that resulted in the creation of an issue
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71749
milestone: '14.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/issue_links.yml b/db/docs/issue_links.yml
index 592a4b3873f..ed21e9c177b 100644
--- a/db/docs/issue_links.yml
+++ b/db/docs/issue_links.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Links two issues by relationship type, which can be related or blocking
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1719
milestone: '9.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/issue_metrics.yml b/db/docs/issue_metrics.yml
index 3d2055a155c..d886aede7d0 100644
--- a/db/docs/issue_metrics.yml
+++ b/db/docs/issue_metrics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Store various metrics for issues.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/516c838a1846d049814765afa85c28a3c14a5b9f
milestone: '8.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/issue_search_data.yml b/db/docs/issue_search_data.yml
index cea9a385afb..f064c15af4a 100644
--- a/db/docs/issue_search_data.yml
+++ b/db/docs/issue_search_data.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71913
milestone: '14.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/issue_tracker_data.yml b/db/docs/issue_tracker_data.yml
index 3af23de8633..ce62346c3d6 100644
--- a/db/docs/issue_tracker_data.yml
+++ b/db/docs/issue_tracker_data.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Data related to the issue tracker integrations.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/14187
milestone: '12.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/issue_user_mentions.yml b/db/docs/issue_user_mentions.yml
index 72e0d483c64..4238441ea0b 100644
--- a/db/docs/issue_user_mentions.yml
+++ b/db/docs/issue_user_mentions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: User mentions in issue descriptions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19009
milestone: '12.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/issues.yml b/db/docs/issues.yml
index db95aea01c3..369dc907d57 100644
--- a/db/docs/issues.yml
+++ b/db/docs/issues.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Information describing issues, which is also used by the WorkItem class
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/9ba1224867665844b117fa037e1465bb706b3685
milestone: "<6.0"
+gitlab_schema: gitlab_main
diff --git a/db/docs/issues_prometheus_alert_events.yml b/db/docs/issues_prometheus_alert_events.yml
index 01ff7f9b6e4..75394e7b962 100644
--- a/db/docs/issues_prometheus_alert_events.yml
+++ b/db/docs/issues_prometheus_alert_events.yml
@@ -6,3 +6,4 @@ feature_categories:
description: Adds relationship between PrometheusAlertEvent and issues created due to them
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17477
milestone: '12.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/issues_self_managed_prometheus_alert_events.yml b/db/docs/issues_self_managed_prometheus_alert_events.yml
index feb208d7c3a..7ac9433a2cb 100644
--- a/db/docs/issues_self_managed_prometheus_alert_events.yml
+++ b/db/docs/issues_self_managed_prometheus_alert_events.yml
@@ -6,3 +6,4 @@ feature_categories:
description: Adds associations between Issues table and Prometheus alerts from self-managed Prometheus instances
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18046
milestone: '12.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/iterations_cadences.yml b/db/docs/iterations_cadences.yml
index 5c6ea38b10e..972e577a788 100644
--- a/db/docs/iterations_cadences.yml
+++ b/db/docs/iterations_cadences.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Defines an iteration cadence for a group, used to define how iterations should act
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50707
milestone: '13.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/jira_connect_installations.yml b/db/docs/jira_connect_installations.yml
index 8695e0294b6..e812fd748d5 100644
--- a/db/docs/jira_connect_installations.yml
+++ b/db/docs/jira_connect_installations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: GitLab.com for Jira Cloud app installation data, formerly Jira Connect App.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/9593
milestone: '11.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/jira_connect_subscriptions.yml b/db/docs/jira_connect_subscriptions.yml
index 775ae3aa96c..6293ab3714d 100644
--- a/db/docs/jira_connect_subscriptions.yml
+++ b/db/docs/jira_connect_subscriptions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: GitLab.com for Jira Cloud app subscriptions data, formerly Jira Connect App.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/10453
milestone: '11.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/jira_imports.yml b/db/docs/jira_imports.yml
index 38cdca874e2..63ee28358ae 100644
--- a/db/docs/jira_imports.yml
+++ b/db/docs/jira_imports.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Track Jira issue import progress into GitLab issues.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28108
milestone: '12.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/jira_tracker_data.yml b/db/docs/jira_tracker_data.yml
index 6e7d6236ffa..e41ce0b5d87 100644
--- a/db/docs/jira_tracker_data.yml
+++ b/db/docs/jira_tracker_data.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Data related to the Jira integration.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/1f332ae8da994509232c7601074b25514ad23c52
milestone: '12.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/keys.yml b/db/docs/keys.yml
index d016b316c15..4e626b1465c 100644
--- a/db/docs/keys.yml
+++ b/db/docs/keys.yml
@@ -10,3 +10,4 @@ feature_categories:
description: SSH keys used by users or for deployments.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/9ba1224867665844b117fa037e1465bb706b3685
milestone: "<6.0"
+gitlab_schema: gitlab_main
diff --git a/db/docs/label_links.yml b/db/docs/label_links.yml
index 5eb10e5e968..2d5664ab431 100644
--- a/db/docs/label_links.yml
+++ b/db/docs/label_links.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Information linking labels with target objects that can be labelled; such as issues, MRs and epics
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/03654a6abf47c88b8b980a6707874ff78080d2fe
milestone: '7.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/label_priorities.yml b/db/docs/label_priorities.yml
index 608cda8590e..cdeb18d815b 100644
--- a/db/docs/label_priorities.yml
+++ b/db/docs/label_priorities.yml
@@ -7,3 +7,4 @@ feature_categories:
description: The relative priority assigned to a label within a project, if any
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/297892011330ecdd2fa7cbe47fbc6fd4f3b62171
milestone: '8.14'
+gitlab_schema: gitlab_main
diff --git a/db/docs/labels.yml b/db/docs/labels.yml
index efda4b65021..47a3bfb4417 100644
--- a/db/docs/labels.yml
+++ b/db/docs/labels.yml
@@ -9,3 +9,4 @@ feature_categories:
description: Information related to labels, which can be associated with groups or projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/03654a6abf47c88b8b980a6707874ff78080d2fe
milestone: '7.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/ldap_group_links.yml b/db/docs/ldap_group_links.yml
index 49c4e560c34..d9a1b0acca5 100644
--- a/db/docs/ldap_group_links.yml
+++ b/db/docs/ldap_group_links.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/b017947ac91655f8ae6593fb63c3423cd1b439f4
milestone: '7.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/lfs_file_locks.yml b/db/docs/lfs_file_locks.yml
index 8cd1c2fd4f9..c366c43d6d7 100644
--- a/db/docs/lfs_file_locks.yml
+++ b/db/docs/lfs_file_locks.yml
@@ -7,3 +7,4 @@ feature_categories:
description: File locks for LFS objects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4091
milestone: '10.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/lfs_object_states.yml b/db/docs/lfs_object_states.yml
index a18699e3557..02334202bdc 100644
--- a/db/docs/lfs_object_states.yml
+++ b/db/docs/lfs_object_states.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Geo verification states for LFS objects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63981
milestone: '14.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/lfs_objects.yml b/db/docs/lfs_objects.yml
index 0e26e3c7758..490bc1af0d9 100644
--- a/db/docs/lfs_objects.yml
+++ b/db/docs/lfs_objects.yml
@@ -7,3 +7,4 @@ feature_categories:
description: LFS files
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/1727
milestone: '8.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/lfs_objects_projects.yml b/db/docs/lfs_objects_projects.yml
index 85cc8f94022..7158e702312 100644
--- a/db/docs/lfs_objects_projects.yml
+++ b/db/docs/lfs_objects_projects.yml
@@ -5,6 +5,7 @@ classes:
feature_categories:
- git_lfs
- source_code_management
-description: Join table relating lfs_objects and projects
+description: Join table relating lfs_objects and projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/1727
milestone: '8.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/licenses.yml b/db/docs/licenses.yml
index 5e178996e3d..0957105695e 100644
--- a/db/docs/licenses.yml
+++ b/db/docs/licenses.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store information related to the instance's license
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/d1f2b09fadcfba210c5121bd214b910b9f9809fd
milestone: '7.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/list_user_preferences.yml b/db/docs/list_user_preferences.yml
index cd2b53fd384..95f935d69bc 100644
--- a/db/docs/list_user_preferences.yml
+++ b/db/docs/list_user_preferences.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Issue board list preferences on a per-user basis, specifically whether the user has collapsed the list or not
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/15657
milestone: '12.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/lists.yml b/db/docs/lists.yml
index 4633be6fb58..6ce93b887d7 100644
--- a/db/docs/lists.yml
+++ b/db/docs/lists.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Configuration of a single list on an issue board
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/611dab2e522e5e59cf09cd459a31686e65616863
milestone: '8.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/loose_foreign_keys_deleted_records.yml b/db/docs/loose_foreign_keys_deleted_records.yml
index df26ffaefd1..aeaf241ef4a 100644
--- a/db/docs/loose_foreign_keys_deleted_records.yml
+++ b/db/docs/loose_foreign_keys_deleted_records.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used by the loose foreign keys feature as a queue of parent records whose child records (via foreign keys) need to be deleted/nullified
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70152
milestone: '14.3'
+gitlab_schema: gitlab_shared
diff --git a/db/docs/member_roles.yml b/db/docs/member_roles.yml
index 314c65a1ef7..559a85823cc 100644
--- a/db/docs/member_roles.yml
+++ b/db/docs/member_roles.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Stores custom roles with composable permissions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92152
milestone: '15.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/member_tasks.yml b/db/docs/member_tasks.yml
index 8802d50176b..0413011c005 100644
--- a/db/docs/member_tasks.yml
+++ b/db/docs/member_tasks.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69299
milestone: '14.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/members.yml b/db/docs/members.yml
index b26d0edd6c9..94306dc5f87 100644
--- a/db/docs/members.yml
+++ b/db/docs/members.yml
@@ -11,3 +11,4 @@ feature_categories:
description: Stores members per namespace
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/3503b504eabf95487fc3fb49df953a7d694da4fe
milestone: '7.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_request_assignees.yml b/db/docs/merge_request_assignees.yml
index 6fd82ac003e..38f476ead90 100644
--- a/db/docs/merge_request_assignees.yml
+++ b/db/docs/merge_request_assignees.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Store allocated assignees for merge requests
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/26496
milestone: '11.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_request_blocks.yml b/db/docs/merge_request_blocks.yml
index 1a3452fc66c..3b7e18818c4 100644
--- a/db/docs/merge_request_blocks.yml
+++ b/db/docs/merge_request_blocks.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Keeps relation between blocked and blocking merge requests
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/27323
milestone: '11.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_request_cleanup_schedules.yml b/db/docs/merge_request_cleanup_schedules.yml
index 9a9338713a6..e25c88c99a7 100644
--- a/db/docs/merge_request_cleanup_schedules.yml
+++ b/db/docs/merge_request_cleanup_schedules.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Store refs cleanup schedules for merge requests
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46758
milestone: '13.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_request_context_commit_diff_files.yml b/db/docs/merge_request_context_commit_diff_files.yml
index 08af5c387c4..6b64ea87555 100644
--- a/db/docs/merge_request_context_commit_diff_files.yml
+++ b/db/docs/merge_request_context_commit_diff_files.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores diffs data for merge request context commits
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23701
milestone: '12.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_request_context_commits.yml b/db/docs/merge_request_context_commits.yml
index 4c52a0a6c08..1c8fc45776f 100644
--- a/db/docs/merge_request_context_commits.yml
+++ b/db/docs/merge_request_context_commits.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Store context commit related data for merge requests
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23701
milestone: '12.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_request_diff_commit_users.yml b/db/docs/merge_request_diff_commit_users.yml
index 4a07b37993e..1e6e78b37e3 100644
--- a/db/docs/merge_request_diff_commit_users.yml
+++ b/db/docs/merge_request_diff_commit_users.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Store commit user information for merge request diffs
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63669
milestone: '14.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_request_diff_commits.yml b/db/docs/merge_request_diff_commits.yml
index 155b6f82612..1b2f910c88a 100644
--- a/db/docs/merge_request_diff_commits.yml
+++ b/db/docs/merge_request_diff_commits.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Store commit related information within a merge request diff
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/12527
milestone: '9.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_request_diff_details.yml b/db/docs/merge_request_diff_details.yml
index ff0770e97b6..7036c799df9 100644
--- a/db/docs/merge_request_diff_details.yml
+++ b/db/docs/merge_request_diff_details.yml
@@ -7,3 +7,4 @@ feature_categories:
description: External MR diff replication detail
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34248
milestone: '13.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_request_diff_files.yml b/db/docs/merge_request_diff_files.yml
index 56b7b0ec6ee..5bb625231f8 100644
--- a/db/docs/merge_request_diff_files.yml
+++ b/db/docs/merge_request_diff_files.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Store file related information within a merge request diff
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/12047
milestone: '9.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_request_diffs.yml b/db/docs/merge_request_diffs.yml
index 912f972e67d..d1044ebffe8 100644
--- a/db/docs/merge_request_diffs.yml
+++ b/db/docs/merge_request_diffs.yml
@@ -4,6 +4,7 @@ classes:
- MergeRequestDiff
feature_categories:
- code_review
-description: Store information about the changes made within a git push for a merge request
+description: Store information about the changes made within a git push for a merge request
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/c983e8eb3d9cac01090b8657735544f71f891576
milestone: '6.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_request_metrics.yml b/db/docs/merge_request_metrics.yml
index 0b166eee455..31267b6bf97 100644
--- a/db/docs/merge_request_metrics.yml
+++ b/db/docs/merge_request_metrics.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Store various metrics for merge requests.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5986
milestone: '8.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_request_predictions.yml b/db/docs/merge_request_predictions.yml
index 7495f0934a4..60680a73583 100644
--- a/db/docs/merge_request_predictions.yml
+++ b/db/docs/merge_request_predictions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Includes machine learning model predictions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97622
milestone: '15.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_request_reviewers.yml b/db/docs/merge_request_reviewers.yml
index 61810bd13c9..b8afea0d217 100644
--- a/db/docs/merge_request_reviewers.yml
+++ b/db/docs/merge_request_reviewers.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Store allocated reviewers for merge requests
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40358
milestone: '13.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_request_user_mentions.yml b/db/docs/merge_request_user_mentions.yml
index 95d2117c12b..26d2b0b7a25 100644
--- a/db/docs/merge_request_user_mentions.yml
+++ b/db/docs/merge_request_user_mentions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Store user mentions for merge requests
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19009
milestone: '12.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_requests.yml b/db/docs/merge_requests.yml
index 6546d28a534..8e849a242b4 100644
--- a/db/docs/merge_requests.yml
+++ b/db/docs/merge_requests.yml
@@ -7,3 +7,4 @@ feature_categories:
description: This is the main table that stores information about project merge requests.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/6d460aa2d6b3959593c168eed181516036525393
milestone: "<6.0"
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_requests_closing_issues.yml b/db/docs/merge_requests_closing_issues.yml
index 210419bc75f..9ad45df375a 100644
--- a/db/docs/merge_requests_closing_issues.yml
+++ b/db/docs/merge_requests_closing_issues.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Store the events of merge request closing any issues
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5986
milestone: '8.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_requests_compliance_violations.yml b/db/docs/merge_requests_compliance_violations.yml
index cfa7f78c13e..f23e734de8f 100644
--- a/db/docs/merge_requests_compliance_violations.yml
+++ b/db/docs/merge_requests_compliance_violations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74290
milestone: '14.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/merge_trains.yml b/db/docs/merge_trains.yml
index e0481d923c0..3b666322d3b 100644
--- a/db/docs/merge_trains.yml
+++ b/db/docs/merge_trains.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/827fc3ccb9335aa29fba0fc532b70015ec4c5186
milestone: '11.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/metrics_dashboard_annotations.yml b/db/docs/metrics_dashboard_annotations.yml
index a874ef0dfdc..6ecf5317794 100644
--- a/db/docs/metrics_dashboard_annotations.yml
+++ b/db/docs/metrics_dashboard_annotations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27583
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/metrics_users_starred_dashboards.yml b/db/docs/metrics_users_starred_dashboards.yml
index 903b563d071..29a027de895 100644
--- a/db/docs/metrics_users_starred_dashboards.yml
+++ b/db/docs/metrics_users_starred_dashboards.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29912
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/milestone_releases.yml b/db/docs/milestone_releases.yml
index de2b6a9cfbc..e84c83891c3 100644
--- a/db/docs/milestone_releases.yml
+++ b/db/docs/milestone_releases.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/user/project/releases/#associate-milestones-with-a-release
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/a43ab8d6a430014e875deb3bff3fd8d8da256747
milestone: '12.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/milestones.yml b/db/docs/milestones.yml
index c41bb289f37..8ba4f3a4de0 100644
--- a/db/docs/milestones.yml
+++ b/db/docs/milestones.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Information related to milestones, used by team planning features to timebox work
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/23d950855d6d2524d00b1f0618c008e2529f06a4
milestone: "<6.0"
+gitlab_schema: gitlab_main
diff --git a/db/docs/ml_candidate_metadata.yml b/db/docs/ml_candidate_metadata.yml
new file mode 100644
index 00000000000..485544f0f59
--- /dev/null
+++ b/db/docs/ml_candidate_metadata.yml
@@ -0,0 +1,11 @@
+---
+table_name: ml_candidate_metadata
+classes:
+ - Ml::CandidateMetadata
+feature_categories:
+ - mlops
+ - incubation
+gitlab_schema: gitlab_main
+description: A Candidate Metadata record holds extra information about the candidate
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104267
+milestone: '15.7'
diff --git a/db/docs/ml_candidate_metrics.yml b/db/docs/ml_candidate_metrics.yml
index b0d9ed13489..24489973dd5 100644
--- a/db/docs/ml_candidate_metrics.yml
+++ b/db/docs/ml_candidate_metrics.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Metrics recorded for a Machine Learning model candidate
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95168
milestone: '15.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/ml_candidate_params.yml b/db/docs/ml_candidate_params.yml
index 01903b66108..25372de7c7f 100644
--- a/db/docs/ml_candidate_params.yml
+++ b/db/docs/ml_candidate_params.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Configuration parameters recorded for a Machine Learning model candidate
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95168
milestone: '15.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/ml_candidates.yml b/db/docs/ml_candidates.yml
index c1f7f622350..c057eb45675 100644
--- a/db/docs/ml_candidates.yml
+++ b/db/docs/ml_candidates.yml
@@ -8,3 +8,4 @@ feature_categories:
description: A Model Candidate is a record of the results on training a model on some configuration
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95168
milestone: '15.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/ml_experiment_metadata.yml b/db/docs/ml_experiment_metadata.yml
new file mode 100644
index 00000000000..a77781cb601
--- /dev/null
+++ b/db/docs/ml_experiment_metadata.yml
@@ -0,0 +1,11 @@
+---
+table_name: ml_experiment_metadata
+classes:
+ - Ml::ExperimentMetadata
+feature_categories:
+ - mlops
+ - incubation
+gitlab_schema: gitlab_main
+description: An Experiment Metadata record holds extra information about the experiment
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104267
+milestone: '15.7'
diff --git a/db/docs/ml_experiments.yml b/db/docs/ml_experiments.yml
index ea5edc9569c..bada30749d3 100644
--- a/db/docs/ml_experiments.yml
+++ b/db/docs/ml_experiments.yml
@@ -8,3 +8,4 @@ feature_categories:
description: A Machine Learning Experiments groups many Model Candidates
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95168
milestone: '15.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/namespace_admin_notes.yml b/db/docs/namespace_admin_notes.yml
index f46d8f8846b..6d6710f7ee4 100644
--- a/db/docs/namespace_admin_notes.yml
+++ b/db/docs/namespace_admin_notes.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Contains notes about groups that are visible to server administrators.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47825
milestone: '13.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/namespace_aggregation_schedules.yml b/db/docs/namespace_aggregation_schedules.yml
index 07c80396302..c961c33f2ee 100644
--- a/db/docs/namespace_aggregation_schedules.yml
+++ b/db/docs/namespace_aggregation_schedules.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Keeps update schedules for namespace_root_storage_statistics
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/29570
milestone: '12.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/namespace_bans.yml b/db/docs/namespace_bans.yml
index 7e11738ab81..af68cf0b48b 100644
--- a/db/docs/namespace_bans.yml
+++ b/db/docs/namespace_bans.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Contains users banned from namespaces
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91271
milestone: "15.2"
+gitlab_schema: gitlab_main
diff --git a/db/docs/namespace_ci_cd_settings.yml b/db/docs/namespace_ci_cd_settings.yml
index 8159f721c98..ddfa390694c 100644
--- a/db/docs/namespace_ci_cd_settings.yml
+++ b/db/docs/namespace_ci_cd_settings.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Namespace-scoped settings related to the CI/CD domain
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86477
milestone: '15.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/namespace_commit_emails.yml b/db/docs/namespace_commit_emails.yml
index d7e192f97f4..c19ff1c577b 100644
--- a/db/docs/namespace_commit_emails.yml
+++ b/db/docs/namespace_commit_emails.yml
@@ -7,3 +7,4 @@ feature_categories:
description: User default email for commits from the GitLab UI
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101832
milestone: '15.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/namespace_details.yml b/db/docs/namespace_details.yml
index 00053d39396..d256085bf00 100644
--- a/db/docs/namespace_details.yml
+++ b/db/docs/namespace_details.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store details for namespaces
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82958
milestone: '15.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/namespace_limits.yml b/db/docs/namespace_limits.yml
index 55b174f9e6f..8601d163d9d 100644
--- a/db/docs/namespace_limits.yml
+++ b/db/docs/namespace_limits.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Contains limits for namespace features like storage and ci
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34746
milestone: '13.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/namespace_package_settings.yml b/db/docs/namespace_package_settings.yml
index 518458dd02f..7247b1187f5 100644
--- a/db/docs/namespace_package_settings.yml
+++ b/db/docs/namespace_package_settings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Namespace and group-level settings for the package registry
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50104
milestone: '13.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/namespace_root_storage_statistics.yml b/db/docs/namespace_root_storage_statistics.yml
index ce8620db1e5..2a3e19ab108 100644
--- a/db/docs/namespace_root_storage_statistics.yml
+++ b/db/docs/namespace_root_storage_statistics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/bde41ee866d0fe0b1bb5ece1130fb6e24d95ad17
milestone: '12.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/namespace_settings.yml b/db/docs/namespace_settings.yml
index ef2f96eb46e..85df86074dc 100644
--- a/db/docs/namespace_settings.yml
+++ b/db/docs/namespace_settings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores settings per namespace
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36321
milestone: '13.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/namespace_statistics.yml b/db/docs/namespace_statistics.yml
index eb8d7f6a5ca..fd7dcb4b62e 100644
--- a/db/docs/namespace_statistics.yml
+++ b/db/docs/namespace_statistics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/48d8bdca0493056a717cd7d9fee2e8b51d6b0502
milestone: '9.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/namespaces.yml b/db/docs/namespaces.yml
index 0518b055e52..e608e996d51 100644
--- a/db/docs/namespaces.yml
+++ b/db/docs/namespaces.yml
@@ -10,3 +10,4 @@ feature_categories:
description: Storing namespaces records for groups, users and projects
introduced_by_url: https://github.com/gitlabhq/gitlabhq/pull/2051
milestone: "<6.0"
+gitlab_schema: gitlab_main
diff --git a/db/docs/namespaces_sync_events.yml b/db/docs/namespaces_sync_events.yml
index f674bfcf622..f143ac29804 100644
--- a/db/docs/namespaces_sync_events.yml
+++ b/db/docs/namespaces_sync_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used as a queue of data that needs to be synchronized between the `ci` and `main` database
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75517
milestone: '14.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/note_diff_files.yml b/db/docs/note_diff_files.yml
index 33921af7f6d..5e78644fe52 100644
--- a/db/docs/note_diff_files.yml
+++ b/db/docs/note_diff_files.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persisted truncated note diffs
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/18991
milestone: '11.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/notes.yml b/db/docs/notes.yml
index f97bfc5bb5c..a6bc81f093f 100644
--- a/db/docs/notes.yml
+++ b/db/docs/notes.yml
@@ -20,3 +20,4 @@ feature_categories:
description: The object at the core of comments, discussions and system notes shown on issues, MRs and epics
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/9ba1224867665844b117fa037e1465bb706b3685
milestone: "<6.0"
+gitlab_schema: gitlab_main
diff --git a/db/docs/notification_settings.yml b/db/docs/notification_settings.yml
index 214db1ca14a..c048163a790 100644
--- a/db/docs/notification_settings.yml
+++ b/db/docs/notification_settings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: User preferences for receiving notifications related to various actions within the application
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/31b0e53015e38e51d9c02cca85c9279600b1bf85
milestone: '8.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/oauth_access_grants.yml b/db/docs/oauth_access_grants.yml
index e36f1bcc36c..197d4fc59bd 100644
--- a/db/docs/oauth_access_grants.yml
+++ b/db/docs/oauth_access_grants.yml
@@ -8,3 +8,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/e41dadcb33fda44ee274daa673bd933e13aa90eb
milestone: '7.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/oauth_access_tokens.yml b/db/docs/oauth_access_tokens.yml
index 0f9165f1fca..f409762f483 100644
--- a/db/docs/oauth_access_tokens.yml
+++ b/db/docs/oauth_access_tokens.yml
@@ -8,3 +8,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/e41dadcb33fda44ee274daa673bd933e13aa90eb
milestone: '7.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/oauth_applications.yml b/db/docs/oauth_applications.yml
index 307a436373e..ac13ab3319a 100644
--- a/db/docs/oauth_applications.yml
+++ b/db/docs/oauth_applications.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/e41dadcb33fda44ee274daa673bd933e13aa90eb
milestone: '7.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/oauth_openid_requests.yml b/db/docs/oauth_openid_requests.yml
index 321811e1722..011b91a758a 100644
--- a/db/docs/oauth_openid_requests.yml
+++ b/db/docs/oauth_openid_requests.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/c4982890489d254da2fe998aab30bf257767ed5e
milestone: '9.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/onboarding_progresses.yml b/db/docs/onboarding_progresses.yml
index 80b70fe0b1f..805b674d44b 100644
--- a/db/docs/onboarding_progresses.yml
+++ b/db/docs/onboarding_progresses.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50711
milestone: '13.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/operations_feature_flag_scopes.yml b/db/docs/operations_feature_flag_scopes.yml
index ac1665fb3a6..98c5c09982c 100644
--- a/db/docs/operations_feature_flag_scopes.yml
+++ b/db/docs/operations_feature_flag_scopes.yml
@@ -6,3 +6,4 @@ feature_categories:
description: Deprecated in favor of `operations_scopes`. To be dropped.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/9110
milestone: '11.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/operations_feature_flags.yml b/db/docs/operations_feature_flags.yml
index c84ed55d0fb..9207ab20b3a 100644
--- a/db/docs/operations_feature_flags.yml
+++ b/db/docs/operations_feature_flags.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7433
milestone: '11.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/operations_feature_flags_clients.yml b/db/docs/operations_feature_flags_clients.yml
index f8f04cadbb7..d97309f0202 100644
--- a/db/docs/operations_feature_flags_clients.yml
+++ b/db/docs/operations_feature_flags_clients.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/operations/feature_flags.html
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7433
milestone: '11.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/operations_feature_flags_issues.yml b/db/docs/operations_feature_flags_issues.yml
index 6b62629a38d..ad361f2e659 100644
--- a/db/docs/operations_feature_flags_issues.yml
+++ b/db/docs/operations_feature_flags_issues.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/operations/feature_flags.html#feature-flag-related-issues
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32876
milestone: '13.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/operations_scopes.yml b/db/docs/operations_scopes.yml
index 781b0a459ab..0275bec0579 100644
--- a/db/docs/operations_scopes.yml
+++ b/db/docs/operations_scopes.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/operations/feature_flags.html#feature-flag-strategies
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24819
milestone: '12.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/operations_strategies.yml b/db/docs/operations_strategies.yml
index c21859e2de6..f92e6c5b057 100644
--- a/db/docs/operations_strategies.yml
+++ b/db/docs/operations_strategies.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/operations/feature_flags.html#feature-flag-strategies
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24819
milestone: '12.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/operations_strategies_user_lists.yml b/db/docs/operations_strategies_user_lists.yml
index ec8062ab57c..c4f0c42c5c5 100644
--- a/db/docs/operations_strategies_user_lists.yml
+++ b/db/docs/operations_strategies_user_lists.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/operations/feature_flags.html#user-list
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30243
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/operations_user_lists.yml b/db/docs/operations_user_lists.yml
index af1e091ee45..37b6949ea15 100644
--- a/db/docs/operations_user_lists.yml
+++ b/db/docs/operations_user_lists.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/operations/feature_flags.html#user-list
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28822
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/p_ci_builds_metadata.yml b/db/docs/p_ci_builds_metadata.yml
index 676cb3bfb1c..d984c68541d 100644
--- a/db/docs/p_ci_builds_metadata.yml
+++ b/db/docs/p_ci_builds_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Routing table that holds information for job execution
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100115
milestone: '15.5'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/packages_build_infos.yml b/db/docs/packages_build_infos.yml
index 5eae65c0e0e..aafb24ad265 100644
--- a/db/docs/packages_build_infos.yml
+++ b/db/docs/packages_build_infos.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Join table relating packages_packages with ci_pipelines
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19796
milestone: '12.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_cleanup_policies.yml b/db/docs/packages_cleanup_policies.yml
index 1221c7952a0..0353f08fc8e 100644
--- a/db/docs/packages_cleanup_policies.yml
+++ b/db/docs/packages_cleanup_policies.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Cleanup policy parameters for packages.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85918
milestone: '15.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_composer_cache_files.yml b/db/docs/packages_composer_cache_files.yml
index e6e81eb149b..76dc9d75245 100644
--- a/db/docs/packages_composer_cache_files.yml
+++ b/db/docs/packages_composer_cache_files.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Composer packages cached SHA files (deprecated)
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51509
milestone: '13.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_composer_metadata.yml b/db/docs/packages_composer_metadata.yml
index 19d51711d42..e6ab8fcb71e 100644
--- a/db/docs/packages_composer_metadata.yml
+++ b/db/docs/packages_composer_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Composer package metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30448
milestone: '13.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_conan_file_metadata.yml b/db/docs/packages_conan_file_metadata.yml
index 7d4d86ee4ed..9d8888d73eb 100644
--- a/db/docs/packages_conan_file_metadata.yml
+++ b/db/docs/packages_conan_file_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Conan package file metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16418
milestone: '12.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_conan_metadata.yml b/db/docs/packages_conan_metadata.yml
index 82b590af698..84bf02b9aeb 100644
--- a/db/docs/packages_conan_metadata.yml
+++ b/db/docs/packages_conan_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Conan package metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16418
milestone: '12.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_debian_file_metadata.yml b/db/docs/packages_debian_file_metadata.yml
index 6a86b7ec285..f24ddd0efbf 100644
--- a/db/docs/packages_debian_file_metadata.yml
+++ b/db/docs/packages_debian_file_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Debian package file metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49692
milestone: '13.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_debian_group_architectures.yml b/db/docs/packages_debian_group_architectures.yml
index 4ffee154fa3..d9d6ea4c714 100644
--- a/db/docs/packages_debian_group_architectures.yml
+++ b/db/docs/packages_debian_group_architectures.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Debian registry group-level architectures
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51265
milestone: '13.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_debian_group_component_files.yml b/db/docs/packages_debian_group_component_files.yml
index dc68328e4b5..134400b42b4 100644
--- a/db/docs/packages_debian_group_component_files.yml
+++ b/db/docs/packages_debian_group_component_files.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Debian group-level component files
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52885
milestone: '13.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_debian_group_components.yml b/db/docs/packages_debian_group_components.yml
index 316e46b90b7..d57fbf8d8b7 100644
--- a/db/docs/packages_debian_group_components.yml
+++ b/db/docs/packages_debian_group_components.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Debian package group-level distribution components
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51732
milestone: '13.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_debian_group_distribution_keys.yml b/db/docs/packages_debian_group_distribution_keys.yml
index 19d55536e37..522eeb8afb1 100644
--- a/db/docs/packages_debian_group_distribution_keys.yml
+++ b/db/docs/packages_debian_group_distribution_keys.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Debian group-level distribution keys
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60993
milestone: '14.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_debian_group_distributions.yml b/db/docs/packages_debian_group_distributions.yml
index cb4b3bcd469..e324e73a2bb 100644
--- a/db/docs/packages_debian_group_distributions.yml
+++ b/db/docs/packages_debian_group_distributions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Debian registry group level distributions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49405
milestone: '13.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_debian_project_architectures.yml b/db/docs/packages_debian_project_architectures.yml
index becdee8cfa6..1aba40a3549 100644
--- a/db/docs/packages_debian_project_architectures.yml
+++ b/db/docs/packages_debian_project_architectures.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Debian registry group-level architectures
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51265
milestone: '13.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_debian_project_component_files.yml b/db/docs/packages_debian_project_component_files.yml
index 5b6da936ebc..a90860ba8b9 100644
--- a/db/docs/packages_debian_project_component_files.yml
+++ b/db/docs/packages_debian_project_component_files.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Debian project-level component files
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52885
milestone: '13.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_debian_project_components.yml b/db/docs/packages_debian_project_components.yml
index 44eb9a489a0..36d0caf29a0 100644
--- a/db/docs/packages_debian_project_components.yml
+++ b/db/docs/packages_debian_project_components.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Debian package project-level distribution components
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51732
milestone: '13.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_debian_project_distribution_keys.yml b/db/docs/packages_debian_project_distribution_keys.yml
index 17863f45e88..947c487312e 100644
--- a/db/docs/packages_debian_project_distribution_keys.yml
+++ b/db/docs/packages_debian_project_distribution_keys.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Debian project-level distribution keys
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60993
milestone: '14.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_debian_project_distributions.yml b/db/docs/packages_debian_project_distributions.yml
index 4689b0b7534..316849a6dd7 100644
--- a/db/docs/packages_debian_project_distributions.yml
+++ b/db/docs/packages_debian_project_distributions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Debian package registry project level distributions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49405
milestone: '13.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_debian_publications.yml b/db/docs/packages_debian_publications.yml
index 181338308a3..1ba9e7b3736 100644
--- a/db/docs/packages_debian_publications.yml
+++ b/db/docs/packages_debian_publications.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Debian package publications relating distributions to packages
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52916
milestone: '13.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_dependencies.yml b/db/docs/packages_dependencies.yml
index 27b0b2cbd9b..ef1be227ce3 100644
--- a/db/docs/packages_dependencies.yml
+++ b/db/docs/packages_dependencies.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Information about package dependencies for a set of supported package types
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20549
milestone: '12.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_dependency_links.yml b/db/docs/packages_dependency_links.yml
index 5a6731a9e82..2c4548b5685 100644
--- a/db/docs/packages_dependency_links.yml
+++ b/db/docs/packages_dependency_links.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Join table between packages_packages and packages_dependencies
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20549
milestone: '12.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_events.yml b/db/docs/packages_events.yml
index 1063e26a749..38c47c53721 100644
--- a/db/docs/packages_events.yml
+++ b/db/docs/packages_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Package tracking events (deprecated)
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41846
milestone: '13.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_helm_file_metadata.yml b/db/docs/packages_helm_file_metadata.yml
index 13b23fd88cf..dbd39544482 100644
--- a/db/docs/packages_helm_file_metadata.yml
+++ b/db/docs/packages_helm_file_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Helm package file metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57017
milestone: '13.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_maven_metadata.yml b/db/docs/packages_maven_metadata.yml
index 6f99f79ffc2..01ebfb1c597 100644
--- a/db/docs/packages_maven_metadata.yml
+++ b/db/docs/packages_maven_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Maven package metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6607
milestone: '11.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_npm_metadata.yml b/db/docs/packages_npm_metadata.yml
index af8c20bf1c0..677e9b55b0d 100644
--- a/db/docs/packages_npm_metadata.yml
+++ b/db/docs/packages_npm_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Npm package metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73639
milestone: '14.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_nuget_dependency_link_metadata.yml b/db/docs/packages_nuget_dependency_link_metadata.yml
index 92c00306eb3..7d93e6aaf77 100644
--- a/db/docs/packages_nuget_dependency_link_metadata.yml
+++ b/db/docs/packages_nuget_dependency_link_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Join table between nuget target frameworks and packages_dependency_links
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30618
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_nuget_metadata.yml b/db/docs/packages_nuget_metadata.yml
index 8179666c148..83a43f24af0 100644
--- a/db/docs/packages_nuget_metadata.yml
+++ b/db/docs/packages_nuget_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Nuget package metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30994
milestone: '13.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_package_file_build_infos.yml b/db/docs/packages_package_file_build_infos.yml
index 16996a07def..2477117add9 100644
--- a/db/docs/packages_package_file_build_infos.yml
+++ b/db/docs/packages_package_file_build_infos.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Join table relating packages_package_files and ci_pipelines
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44348
milestone: '13.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_package_files.yml b/db/docs/packages_package_files.yml
index c9e23f1003e..30f28f5e4ab 100644
--- a/db/docs/packages_package_files.yml
+++ b/db/docs/packages_package_files.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Package registry file links and file metadata for all package types
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6607
milestone: '11.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_packages.yml b/db/docs/packages_packages.yml
index 6378aeaa565..d2e08350ab7 100644
--- a/db/docs/packages_packages.yml
+++ b/db/docs/packages_packages.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Information for individual packages in the package registry
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6607
milestone: '11.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_pypi_metadata.yml b/db/docs/packages_pypi_metadata.yml
index 7d2d8d7be05..01dce318658 100644
--- a/db/docs/packages_pypi_metadata.yml
+++ b/db/docs/packages_pypi_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: PyPI package metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27632
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_rpm_metadata.yml b/db/docs/packages_rpm_metadata.yml
index cd34529ff0c..193dc46427e 100644
--- a/db/docs/packages_rpm_metadata.yml
+++ b/db/docs/packages_rpm_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Rpm package metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96019
milestone: '15.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_rpm_repository_files.yml b/db/docs/packages_rpm_repository_files.yml
index 3aac984265c..7044b58e22a 100644
--- a/db/docs/packages_rpm_repository_files.yml
+++ b/db/docs/packages_rpm_repository_files.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Package registry file links and file metadata for RPM packages
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97484
milestone: '15.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_rubygems_metadata.yml b/db/docs/packages_rubygems_metadata.yml
index ba521f99d77..d4cb5cbbeb8 100644
--- a/db/docs/packages_rubygems_metadata.yml
+++ b/db/docs/packages_rubygems_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Ruby gems metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52639
milestone: '13.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/packages_tags.yml b/db/docs/packages_tags.yml
index 41aad5590f5..dcd1eccd35c 100644
--- a/db/docs/packages_tags.yml
+++ b/db/docs/packages_tags.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Package identifier tags for supported package types. See https://docs.gitlab.com/ee/user/packages/npm_registry/#add-npm-distribution-tags for an example.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20636
milestone: '12.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/pages_deployment_states.yml b/db/docs/pages_deployment_states.yml
index dd0ff12abbe..f1907c93454 100644
--- a/db/docs/pages_deployment_states.yml
+++ b/db/docs/pages_deployment_states.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores verification state for Geo replicated Pages deployments.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74905
milestone: '14.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/pages_deployments.yml b/db/docs/pages_deployments.yml
index 606147f9887..f8aae63c191 100644
--- a/db/docs/pages_deployments.yml
+++ b/db/docs/pages_deployments.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores ZIP archives for GitLab Pages websites.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41785
milestone: '13.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/pages_domain_acme_orders.yml b/db/docs/pages_domain_acme_orders.yml
index 1bc97c3e47e..c285f638f3f 100644
--- a/db/docs/pages_domain_acme_orders.yml
+++ b/db/docs/pages_domain_acme_orders.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores ACME order information used in obtaining Let's Encrypt certificates for GitLab Pages domains.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/14014
milestone: '12.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/pages_domains.yml b/db/docs/pages_domains.yml
index e0fc084c4c2..ca4bde86f88 100644
--- a/db/docs/pages_domains.yml
+++ b/db/docs/pages_domains.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Store Pages domain, certificate and encryption meta data.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/173
milestone: '8.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/path_locks.yml b/db/docs/path_locks.yml
index 27548f44c39..f27856d5dee 100644
--- a/db/docs/path_locks.yml
+++ b/db/docs/path_locks.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores paths to repository blobs locked by users
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/684e9d1b5979e11d2edae11a3028a696bfcdedf8
milestone: '8.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/personal_access_tokens.yml b/db/docs/personal_access_tokens.yml
index 70ad9a93247..8241f4234d8 100644
--- a/db/docs/personal_access_tokens.yml
+++ b/db/docs/personal_access_tokens.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/3a609038748055a27c7e01cf4b55d8249709c9cc
milestone: '8.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/plan_limits.yml b/db/docs/plan_limits.yml
index f5ddcb11f3d..7baa9a5b447 100644
--- a/db/docs/plan_limits.yml
+++ b/db/docs/plan_limits.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Contains Plan specific limits (CI minute quantities for example)
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19438
milestone: '12.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/plans.yml b/db/docs/plans.yml
index df227bcb6e9..71053051cb8 100644
--- a/db/docs/plans.yml
+++ b/db/docs/plans.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Contains information about purchasable Plans for GitLab namespaces
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/39ca951a0f28d147d4689379bbe48a9c14d55d9f
milestone: '9.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/pm_licenses.yml b/db/docs/pm_licenses.yml
new file mode 100644
index 00000000000..55ef2719cbc
--- /dev/null
+++ b/db/docs/pm_licenses.yml
@@ -0,0 +1,10 @@
+---
+table_name: pm_licenses
+classes:
+- PackageMetadata::License
+feature_categories:
+ - license_compliance
+description: Tracks licenses referenced by public package registries.
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102794
+milestone: '15.6'
+gitlab_schema: gitlab_pm
diff --git a/db/docs/pm_package_version_licenses.yml b/db/docs/pm_package_version_licenses.yml
new file mode 100644
index 00000000000..439162ecf9d
--- /dev/null
+++ b/db/docs/pm_package_version_licenses.yml
@@ -0,0 +1,10 @@
+---
+table_name: pm_package_version_licenses
+classes:
+- PackageMetadata::PackageVersionLicense
+feature_categories:
+ - license_compliance
+description: Tracks licenses under which a given package version has been published.
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102794
+milestone: '15.6'
+gitlab_schema: gitlab_pm
diff --git a/db/docs/pm_package_versions.yml b/db/docs/pm_package_versions.yml
new file mode 100644
index 00000000000..7b015ddc174
--- /dev/null
+++ b/db/docs/pm_package_versions.yml
@@ -0,0 +1,10 @@
+---
+table_name: pm_package_versions
+classes:
+- PackageMetadata::PackageVersion
+feature_categories:
+- license_compliance
+description: Tracks package versions served by public package registries.
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102794
+milestone: '15.6'
+gitlab_schema: gitlab_pm
diff --git a/db/docs/pm_packages.yml b/db/docs/pm_packages.yml
new file mode 100644
index 00000000000..35932b37990
--- /dev/null
+++ b/db/docs/pm_packages.yml
@@ -0,0 +1,10 @@
+---
+table_name: pm_packages
+classes:
+- PackageMetadata::Package
+feature_categories:
+- license_compliance
+description: Tracks packages served by public package registries.
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102794
+milestone: '15.6'
+gitlab_schema: gitlab_pm
diff --git a/db/docs/pool_repositories.yml b/db/docs/pool_repositories.yml
index 96ca1dcf7d9..190b2127f58 100644
--- a/db/docs/pool_repositories.yml
+++ b/db/docs/pool_repositories.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/fff7754186202cfcdeaa0962c28e5d43ddd705b7
milestone: '11.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/postgres_async_indexes.yml b/db/docs/postgres_async_indexes.yml
index b23b72de808..9e54fe11b89 100644
--- a/db/docs/postgres_async_indexes.yml
+++ b/db/docs/postgres_async_indexes.yml
@@ -9,3 +9,4 @@ description: >-
See https://docs.gitlab.com/ee/development/adding_database_indexes.html#create-indexes-asynchronously for more details.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66478
milestone: '14.2'
+gitlab_schema: gitlab_shared
diff --git a/db/docs/postgres_reindex_actions.yml b/db/docs/postgres_reindex_actions.yml
index d7297454d6a..d36917bd707 100644
--- a/db/docs/postgres_reindex_actions.yml
+++ b/db/docs/postgres_reindex_actions.yml
@@ -10,3 +10,4 @@ description: >-
for details about reindexing.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43156
milestone: '13.5'
+gitlab_schema: gitlab_shared
diff --git a/db/docs/postgres_reindex_queued_actions.yml b/db/docs/postgres_reindex_queued_actions.yml
index 7955d000f8c..d6eef0eb326 100644
--- a/db/docs/postgres_reindex_queued_actions.yml
+++ b/db/docs/postgres_reindex_queued_actions.yml
@@ -9,3 +9,4 @@ description: >-
Actions in this queue will be prioritized over regular reindexing actions.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73480
milestone: '14.5'
+gitlab_schema: gitlab_shared
diff --git a/db/docs/product_analytics_events_experimental.yml b/db/docs/product_analytics_events_experimental.yml
index c295074b706..347a3ef88ac 100644
--- a/db/docs/product_analytics_events_experimental.yml
+++ b/db/docs/product_analytics_events_experimental.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Product analytic events, experimental feature.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/fc6c53e6f7b47dc22c8619a5a6fe491d29778d3f
milestone: '13.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/programming_languages.yml b/db/docs/programming_languages.yml
index 5da5720a116..176ab502c74 100644
--- a/db/docs/programming_languages.yml
+++ b/db/docs/programming_languages.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Contains known programming languages and their assigned colors
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/19480
milestone: '11.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_access_tokens.yml b/db/docs/project_access_tokens.yml
index 8c53c854b64..ddaca744571 100644
--- a/db/docs/project_access_tokens.yml
+++ b/db/docs/project_access_tokens.yml
@@ -6,3 +6,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33272
milestone: '13.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_alerting_settings.yml b/db/docs/project_alerting_settings.yml
index 0737c65faaf..629ba0ba834 100644
--- a/db/docs/project_alerting_settings.yml
+++ b/db/docs/project_alerting_settings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists project-level tokens for manual Prometheus installations
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/9334
milestone: '11.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_aliases.yml b/db/docs/project_aliases.yml
index f79c81d2afe..799dff34e7e 100644
--- a/db/docs/project_aliases.yml
+++ b/db/docs/project_aliases.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores aliases of projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/14108
milestone: '12.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_authorizations.yml b/db/docs/project_authorizations.yml
index 890997d7d33..b37634047f0 100644
--- a/db/docs/project_authorizations.yml
+++ b/db/docs/project_authorizations.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Stores maximal access to the project per user
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/6839
milestone: '8.14'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_auto_devops.yml b/db/docs/project_auto_devops.yml
index dd960ecc4eb..ff4e92c74f5 100644
--- a/db/docs/project_auto_devops.yml
+++ b/db/docs/project_auto_devops.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Auto DevOps settings for a project
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/6ed490401f49a8941dc7a9e3757ec4012f14ef0b
milestone: '10.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_build_artifacts_size_refreshes.yml b/db/docs/project_build_artifacts_size_refreshes.yml
index 56bad0e4df6..2e3a6a12b14 100644
--- a/db/docs/project_build_artifacts_size_refreshes.yml
+++ b/db/docs/project_build_artifacts_size_refreshes.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Temporary table to accurately recompute artifacts size.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81306
milestone: '14.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_ci_cd_settings.yml b/db/docs/project_ci_cd_settings.yml
index a736cf6a8dc..265ec896247 100644
--- a/db/docs/project_ci_cd_settings.yml
+++ b/db/docs/project_ci_cd_settings.yml
@@ -9,3 +9,4 @@ feature_categories:
description: Project-scoped settings related to the CI/CD domain
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/392c411bdc16386ef42c86afaf8c4d8e4cddb955
milestone: '10.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_ci_feature_usages.yml b/db/docs/project_ci_feature_usages.yml
index e7e354c6cc7..a3f7be26027 100644
--- a/db/docs/project_ci_feature_usages.yml
+++ b/db/docs/project_ci_feature_usages.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Project CI feature usage information used to access CI data from the main database.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68186
milestone: '14.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_compliance_framework_settings.yml b/db/docs/project_compliance_framework_settings.yml
index bc5555926c1..ab68259e87e 100644
--- a/db/docs/project_compliance_framework_settings.yml
+++ b/db/docs/project_compliance_framework_settings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28182
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_custom_attributes.yml b/db/docs/project_custom_attributes.yml
index 90f13b4f593..3a4b76c5b02 100644
--- a/db/docs/project_custom_attributes.yml
+++ b/db/docs/project_custom_attributes.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores custom attributes per project
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/14593
milestone: '10.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_daily_statistics.yml b/db/docs/project_daily_statistics.yml
index 5de94c2845b..862a63b1909 100644
--- a/db/docs/project_daily_statistics.yml
+++ b/db/docs/project_daily_statistics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores repository fetch statistics per day
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/23596
milestone: '11.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_deploy_tokens.yml b/db/docs/project_deploy_tokens.yml
index 12e565bf4de..80bca84bf4c 100644
--- a/db/docs/project_deploy_tokens.yml
+++ b/db/docs/project_deploy_tokens.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/user/project/deploy_tokens/
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/8315861c9a50675b4f4f4ca536f0da90f27994f3
milestone: '10.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_error_tracking_settings.yml b/db/docs/project_error_tracking_settings.yml
index d10982fe712..5bfc278a206 100644
--- a/db/docs/project_error_tracking_settings.yml
+++ b/db/docs/project_error_tracking_settings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Project settings related to Error Tracking
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/24047
milestone: '11.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_export_jobs.yml b/db/docs/project_export_jobs.yml
index 991cdbeb12a..2eb6aa51202 100644
--- a/db/docs/project_export_jobs.yml
+++ b/db/docs/project_export_jobs.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to track and control project export status
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23664
milestone: '12.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_feature_usages.yml b/db/docs/project_feature_usages.yml
index b3182de243b..c209abf25eb 100644
--- a/db/docs/project_feature_usages.yml
+++ b/db/docs/project_feature_usages.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Track Jira DVCS usage
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/21db9a55e200b23a5a47251e9df46fd548c74559
milestone: '11.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_features.yml b/db/docs/project_features.yml
index 7b94db88e70..a0a1cce3a7d 100644
--- a/db/docs/project_features.yml
+++ b/db/docs/project_features.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores access levels for project features like wikis, issues, repositories, containers, ect.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5606
milestone: '8.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_group_links.yml b/db/docs/project_group_links.yml
index ca1aedf25b8..c03141058b6 100644
--- a/db/docs/project_group_links.yml
+++ b/db/docs/project_group_links.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/3ac5a759e93e632539438d4564582c645a9f6799
milestone: "<6.0"
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_import_data.yml b/db/docs/project_import_data.yml
index 22c0f036b63..283657a1dd3 100644
--- a/db/docs/project_import_data.yml
+++ b/db/docs/project_import_data.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store credentials and configuration of external projects when using the Import/Export feature
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/7d98c8842d6bc9b14fb410f028db7ab651961b42
milestone: '7.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_incident_management_settings.yml b/db/docs/project_incident_management_settings.yml
index b1ef6824fe2..2e9812e9bf0 100644
--- a/db/docs/project_incident_management_settings.yml
+++ b/db/docs/project_incident_management_settings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists project settings for incident management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/9744
milestone: '11.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_metrics_settings.yml b/db/docs/project_metrics_settings.yml
index 9090f15278c..6ff8902b24e 100644
--- a/db/docs/project_metrics_settings.yml
+++ b/db/docs/project_metrics_settings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/bb13ae974c295718eb80c14a179b721ba192a089
milestone: '11.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_mirror_data.yml b/db/docs/project_mirror_data.yml
index 3ea755c04a2..5ac43215b26 100644
--- a/db/docs/project_mirror_data.yml
+++ b/db/docs/project_mirror_data.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store and track the project import status when using the Import/Export feature
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/0ca479d1ce0eadfcdc0e29d0e18136f5790d5b2f
milestone: '9.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_pages_metadata.yml b/db/docs/project_pages_metadata.yml
index aa792520bc3..d9b609d7784 100644
--- a/db/docs/project_pages_metadata.yml
+++ b/db/docs/project_pages_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Store GitLab Pages metadata for projects.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17197
milestone: '12.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_relation_export_uploads.yml b/db/docs/project_relation_export_uploads.yml
index 369f6d281ee..43c6d33f5a5 100644
--- a/db/docs/project_relation_export_uploads.yml
+++ b/db/docs/project_relation_export_uploads.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store relation export files location
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90624
milestone: '15.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_relation_exports.yml b/db/docs/project_relation_exports.yml
index 7014d4cae0d..f25fe8280ae 100644
--- a/db/docs/project_relation_exports.yml
+++ b/db/docs/project_relation_exports.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to track the generation of relation export files for projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90624
milestone: '15.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_repositories.yml b/db/docs/project_repositories.yml
index ed90a0d1595..2a3e37098c7 100644
--- a/db/docs/project_repositories.yml
+++ b/db/docs/project_repositories.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Keeps disk path to repositories and link to the shard
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/8614
milestone: '11.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_repository_states.yml b/db/docs/project_repository_states.yml
index fa762a646f4..6a8f33e7fa5 100644
--- a/db/docs/project_repository_states.yml
+++ b/db/docs/project_repository_states.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Keeps checksums of repositories
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4428
milestone: '10.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_repository_storage_moves.yml b/db/docs/project_repository_storage_moves.yml
index 4255a0d4a8a..d48dc700bae 100644
--- a/db/docs/project_repository_storage_moves.yml
+++ b/db/docs/project_repository_storage_moves.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores status of project repository moves
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29095
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_security_settings.yml b/db/docs/project_security_settings.yml
index 79ebdcae8c2..99a767978fb 100644
--- a/db/docs/project_security_settings.yml
+++ b/db/docs/project_security_settings.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Project settings related to security features.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32577
milestone: '13.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_settings.yml b/db/docs/project_settings.yml
index cc084590c89..7113aedf1f8 100644
--- a/db/docs/project_settings.yml
+++ b/db/docs/project_settings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores settings per project
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/a2a7ad291f64a5db74c1bc21fb556e6e8862d0f3
milestone: '10.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_statistics.yml b/db/docs/project_statistics.yml
index 323ba1d60d4..a3afb678877 100644
--- a/db/docs/project_statistics.yml
+++ b/db/docs/project_statistics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Records statistics about the usage of various product features
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/7754
milestone: '8.16'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_topics.yml b/db/docs/project_topics.yml
index cbe0d482586..3d8ec0c342a 100644
--- a/db/docs/project_topics.yml
+++ b/db/docs/project_topics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores topics per project relationship
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67574
milestone: '14.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_wiki_repositories.yml b/db/docs/project_wiki_repositories.yml
index 9f01fd2db3f..7da09b7fffe 100644
--- a/db/docs/project_wiki_repositories.yml
+++ b/db/docs/project_wiki_repositories.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores information about project wiki repositories.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103399
milestone: '15.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/project_wiki_repository_states.yml b/db/docs/project_wiki_repository_states.yml
index b074eca3c89..c12e904b339 100644
--- a/db/docs/project_wiki_repository_states.yml
+++ b/db/docs/project_wiki_repository_states.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Separate table for project wikis containing Geo verification metadata.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/99168
milestone: '15.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/projects.yml b/db/docs/projects.yml
index 9a845a21751..0b7dbbe2127 100644
--- a/db/docs/projects.yml
+++ b/db/docs/projects.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores project records
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/9ba1224867665844b117fa037e1465bb706b3685
milestone: "<6.0"
+gitlab_schema: gitlab_main
diff --git a/db/docs/projects_sync_events.yml b/db/docs/projects_sync_events.yml
index cdc27423778..7e03e5abe63 100644
--- a/db/docs/projects_sync_events.yml
+++ b/db/docs/projects_sync_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used as a queue of data that needs to be synchronized between the `ci` and `main` database
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75517
milestone: '14.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/prometheus_alert_events.yml b/db/docs/prometheus_alert_events.yml
index 9fed9d9d73b..91820cffa16 100644
--- a/db/docs/prometheus_alert_events.yml
+++ b/db/docs/prometheus_alert_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7493
milestone: '11.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/prometheus_alerts.yml b/db/docs/prometheus_alerts.yml
index 3d3a2e45650..cfab6c1c094 100644
--- a/db/docs/prometheus_alerts.yml
+++ b/db/docs/prometheus_alerts.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists information about prometheus alerts from an environment
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6590
milestone: '11.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/prometheus_metrics.yml b/db/docs/prometheus_metrics.yml
index 315aaf7f9bd..69c74dd1629 100644
--- a/db/docs/prometheus_metrics.yml
+++ b/db/docs/prometheus_metrics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3799
milestone: '9.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/protected_branch_merge_access_levels.yml b/db/docs/protected_branch_merge_access_levels.yml
index a07303975ad..3a348825dce 100644
--- a/db/docs/protected_branch_merge_access_levels.yml
+++ b/db/docs/protected_branch_merge_access_levels.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores merge access settings for protected branches
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5081
milestone: '8.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/protected_branch_push_access_levels.yml b/db/docs/protected_branch_push_access_levels.yml
index fff94bceace..24865372ad0 100644
--- a/db/docs/protected_branch_push_access_levels.yml
+++ b/db/docs/protected_branch_push_access_levels.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores push access settings for protected branches
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5081
milestone: '8.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/protected_branch_unprotect_access_levels.yml b/db/docs/protected_branch_unprotect_access_levels.yml
index 8727d77e8ec..aafc7282a5e 100644
--- a/db/docs/protected_branch_unprotect_access_levels.yml
+++ b/db/docs/protected_branch_unprotect_access_levels.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores access settings for protected branch unprotection
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/5103
milestone: '10.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/protected_branches.yml b/db/docs/protected_branches.yml
index a94c7d7681c..7c3132336e2 100644
--- a/db/docs/protected_branches.yml
+++ b/db/docs/protected_branches.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Keeps a list of protected branches by project
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/37224dc9c1ee80ba9030b616e2bc87bd96919e09
milestone: "<6.0"
+gitlab_schema: gitlab_main
diff --git a/db/docs/protected_environment_approval_rules.yml b/db/docs/protected_environment_approval_rules.yml
index fe3d9d7ad08..5bfecbf767d 100644
--- a/db/docs/protected_environment_approval_rules.yml
+++ b/db/docs/protected_environment_approval_rules.yml
@@ -4,6 +4,9 @@ classes:
- ProtectedEnvironments::ApprovalRule
feature_categories:
- continuous_delivery
-description: https://docs.gitlab.com/ee/ci/environments/deployment_approvals.html#multiple-approval-rules
+description: >-
+ A rule associated to a protected environment that allows a user, group, or role to approve a deployment.
+ See https://docs.gitlab.com/ee/ci/environments/deployment_approvals.html#multiple-approval-rules for more details.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82800
milestone: '14.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/protected_environment_deploy_access_levels.yml b/db/docs/protected_environment_deploy_access_levels.yml
index c25044dc7e2..123d58f4c1a 100644
--- a/db/docs/protected_environment_deploy_access_levels.yml
+++ b/db/docs/protected_environment_deploy_access_levels.yml
@@ -4,6 +4,9 @@ classes:
- ProtectedEnvironments::DeployAccessLevel
feature_categories:
- continuous_delivery
-description: https://docs.gitlab.com/ee/ci/environments/protected_environments.html
+description: >-
+ A rule associated to a protected environment that allows a user, group, or role to deploy.
+ See https://docs.gitlab.com/ee/ci/environments/protected_environments.html for more details.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6672
milestone: '11.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/protected_environments.yml b/db/docs/protected_environments.yml
index 6a0d18ee4b5..b4fcbf130a1 100644
--- a/db/docs/protected_environments.yml
+++ b/db/docs/protected_environments.yml
@@ -4,6 +4,9 @@ classes:
- ProtectedEnvironment
feature_categories:
- continuous_delivery
-description: https://docs.gitlab.com/ee/ci/environments/protected_environments.html
+description: >-
+ Project or group-level record associated to one or more environments by name/pattern.
+ See https://docs.gitlab.com/ee/ci/environments/protected_environments.html for more details.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6672
milestone: '11.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/protected_tag_create_access_levels.yml b/db/docs/protected_tag_create_access_levels.yml
index 0c1ae808e67..2644868a76e 100644
--- a/db/docs/protected_tag_create_access_levels.yml
+++ b/db/docs/protected_tag_create_access_levels.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores create access settings for protected tags
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/07d7d8e65905a39164b63f55eccdcea8f10f5d14
milestone: '9.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/protected_tags.yml b/db/docs/protected_tags.yml
index 79b0b51de5f..4bd43f4a617 100644
--- a/db/docs/protected_tags.yml
+++ b/db/docs/protected_tags.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Keeps a list of protected tags by project
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10356
milestone: '9.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/push_event_payloads.yml b/db/docs/push_event_payloads.yml
index 68cd4ae4bb8..d0f1bdeb060 100644
--- a/db/docs/push_event_payloads.yml
+++ b/db/docs/push_event_payloads.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores log of push events
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/12463
milestone: '9.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/push_rules.yml b/db/docs/push_rules.yml
index 6a51fc79b33..85c609719b6 100644
--- a/db/docs/push_rules.yml
+++ b/db/docs/push_rules.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/1b98b5ab97ce3e9997df542059cbf3c6ce0bf0e1
milestone: '8.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/raw_usage_data.yml b/db/docs/raw_usage_data.yml
index c7e194d6417..5266b10e370 100644
--- a/db/docs/raw_usage_data.yml
+++ b/db/docs/raw_usage_data.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38457
milestone: '13.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/redirect_routes.yml b/db/docs/redirect_routes.yml
index a9b93a9b8f9..7997aae2952 100644
--- a/db/docs/redirect_routes.yml
+++ b/db/docs/redirect_routes.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Stores routes for redirect after changing the path to group or project
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11136
milestone: '9.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/related_epic_links.yml b/db/docs/related_epic_links.yml
index f498353c04b..43799a99101 100644
--- a/db/docs/related_epic_links.yml
+++ b/db/docs/related_epic_links.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Information on the relationship between two epics
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80499
milestone: '14.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/release_links.yml b/db/docs/release_links.yml
index 03fa9e2bbbb..bc194c949a3 100644
--- a/db/docs/release_links.yml
+++ b/db/docs/release_links.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/user/project/releases/#links
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/66755c9ed506af9f51022a678ed26e5d31ee87ac
milestone: '11.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/releases.yml b/db/docs/releases.yml
index da4fbfe830f..99ec9a5bed7 100644
--- a/db/docs/releases.yml
+++ b/db/docs/releases.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/user/project/releases
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/1c4d1c3bd69a6f9ec43cce4ab59de4ba47f73229
milestone: '8.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/remote_mirrors.yml b/db/docs/remote_mirrors.yml
index 5d38c9cc3ec..2ae633eb023 100644
--- a/db/docs/remote_mirrors.yml
+++ b/db/docs/remote_mirrors.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores push mirrors and their update statuses
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/249
milestone: '8.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/repository_languages.yml b/db/docs/repository_languages.yml
index ceee8670a68..506c607cf54 100644
--- a/db/docs/repository_languages.yml
+++ b/db/docs/repository_languages.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Keeps relation between projects and repository languages
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/19480
milestone: '11.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/required_code_owners_sections.yml b/db/docs/required_code_owners_sections.yml
index 059078cce27..dbbc5e77af4 100644
--- a/db/docs/required_code_owners_sections.yml
+++ b/db/docs/required_code_owners_sections.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Keeps required code owners sections
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43573
milestone: '13.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/requirements.yml b/db/docs/requirements.yml
index 29340196b32..9fad8b634cf 100644
--- a/db/docs/requirements.yml
+++ b/db/docs/requirements.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Information relating to Requirements as implemented by the Requirements Management tool
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26097
milestone: '12.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/requirements_management_test_reports.yml b/db/docs/requirements_management_test_reports.yml
index b7fc5fa54f8..69f40f9592b 100644
--- a/db/docs/requirements_management_test_reports.yml
+++ b/db/docs/requirements_management_test_reports.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Information related to Test Reports, which relate historical test outcomes to Requirements
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31643
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/resource_iteration_events.yml b/db/docs/resource_iteration_events.yml
index 8e61c68a6f6..46a9e88fd9a 100644
--- a/db/docs/resource_iteration_events.yml
+++ b/db/docs/resource_iteration_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Records the addition and removal of issues to iterations
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37617
milestone: '13.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/resource_label_events.yml b/db/docs/resource_label_events.yml
index b770b642452..9de636ea874 100644
--- a/db/docs/resource_label_events.yml
+++ b/db/docs/resource_label_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Records the addition and removal of labels from resources that can be labelled; such as issues, MRs and epics
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6697
milestone: '11.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/resource_milestone_events.yml b/db/docs/resource_milestone_events.yml
index 8792bb269b6..02962bc0056 100644
--- a/db/docs/resource_milestone_events.yml
+++ b/db/docs/resource_milestone_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Records the addition and removal of issues to milestones
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23965
milestone: '12.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/resource_state_events.yml b/db/docs/resource_state_events.yml
index 12887a5a6c4..2390cd26bac 100644
--- a/db/docs/resource_state_events.yml
+++ b/db/docs/resource_state_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Records the change of state of issues between opened and closed
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28926
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/resource_weight_events.yml b/db/docs/resource_weight_events.yml
index 3f17b312fea..12cf9b27d19 100644
--- a/db/docs/resource_weight_events.yml
+++ b/db/docs/resource_weight_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Records the change of weight on issues along with timestamps
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21515
milestone: '12.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/reviews.yml b/db/docs/reviews.yml
index 5a9f4c03bfb..81253d0c3c1 100644
--- a/db/docs/reviews.yml
+++ b/db/docs/reviews.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/8442
milestone: '11.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/routes.yml b/db/docs/routes.yml
index 9184309dabf..c54f976d91a 100644
--- a/db/docs/routes.yml
+++ b/db/docs/routes.yml
@@ -10,3 +10,4 @@ feature_categories:
description: Stores routes per namespaces and projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/7121
milestone: '9.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/saml_group_links.yml b/db/docs/saml_group_links.yml
index 109fd62a16b..5fd2372a22d 100644
--- a/db/docs/saml_group_links.yml
+++ b/db/docs/saml_group_links.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45061
milestone: '13.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/saml_providers.yml b/db/docs/saml_providers.yml
index 42f2fa956b6..6fcc0e0e370 100644
--- a/db/docs/saml_providers.yml
+++ b/db/docs/saml_providers.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4549
milestone: '10.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/saved_replies.yml b/db/docs/saved_replies.yml
index bc667bddf4b..c1d4a51ce0d 100644
--- a/db/docs/saved_replies.yml
+++ b/db/docs/saved_replies.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Text templates used to populate comments using a quick action
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80807
milestone: '14.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/sbom_component_versions.yml b/db/docs/sbom_component_versions.yml
index 1bee0ddb3fb..25ef9e3fb9c 100644
--- a/db/docs/sbom_component_versions.yml
+++ b/db/docs/sbom_component_versions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores version information for software components produced by a Software Bill of Materials (SBoM)
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90809
milestone: '15.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/sbom_components.yml b/db/docs/sbom_components.yml
index 0bb1a4d7b30..b735d7f29c2 100644
--- a/db/docs/sbom_components.yml
+++ b/db/docs/sbom_components.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores information about software components produced by a Software Bill of Materials (SBoM)
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90809
milestone: '15.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/sbom_occurrences.yml b/db/docs/sbom_occurrences.yml
index b30bac79698..094199225e0 100644
--- a/db/docs/sbom_occurrences.yml
+++ b/db/docs/sbom_occurrences.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Tracks each occurrence of an SBoM component
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90814
milestone: '15.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/sbom_sources.yml b/db/docs/sbom_sources.yml
index dd17b02a3dd..b9cc8611b45 100644
--- a/db/docs/sbom_sources.yml
+++ b/db/docs/sbom_sources.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores information about where an SBoM component originated from
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90812
milestone: '15.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/sbom_vulnerable_component_versions.yml b/db/docs/sbom_vulnerable_component_versions.yml
index bb67c6e4f68..8747b6c6588 100644
--- a/db/docs/sbom_vulnerable_component_versions.yml
+++ b/db/docs/sbom_vulnerable_component_versions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores information about vulnerable SBoM components
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95622
milestone: '15.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/schema_migrations.yml b/db/docs/schema_migrations.yml
index a8df9b8a767..f5b52bc4db2 100644
--- a/db/docs/schema_migrations.yml
+++ b/db/docs/schema_migrations.yml
@@ -9,3 +9,4 @@ description: >-
An internal table used by ActiveRecord to keep track of which migrations have been applied to the database.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/9ba1224867665844b117fa037e1465bb706b3685
milestone: '0.8'
+gitlab_schema: gitlab_internal
diff --git a/db/docs/scim_identities.yml b/db/docs/scim_identities.yml
index 1aa56dc2740..6ad69d9b4cc 100644
--- a/db/docs/scim_identities.yml
+++ b/db/docs/scim_identities.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26124
milestone: '12.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/scim_oauth_access_tokens.yml b/db/docs/scim_oauth_access_tokens.yml
index bc258eda41a..e26cd94f4cd 100644
--- a/db/docs/scim_oauth_access_tokens.yml
+++ b/db/docs/scim_oauth_access_tokens.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/e9b2253fe3538234d1c4d173c4549a955233d836
milestone: '11.10'
+gitlab_schema: gitlab_main
diff --git a/db/docs/security_findings.yml b/db/docs/security_findings.yml
index b4607d56dca..724881d4d17 100644
--- a/db/docs/security_findings.yml
+++ b/db/docs/security_findings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores a subset of the Finding data which is used to optimize the pipeline security tab
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40368
milestone: '13.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/security_orchestration_policy_configurations.yml b/db/docs/security_orchestration_policy_configurations.yml
index 0f91d180dc3..7d23d30de82 100644
--- a/db/docs/security_orchestration_policy_configurations.yml
+++ b/db/docs/security_orchestration_policy_configurations.yml
@@ -9,3 +9,4 @@ description: |
Policies are stored in the repository as a YAML file.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53743
milestone: '13.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/security_orchestration_policy_rule_schedules.yml b/db/docs/security_orchestration_policy_rule_schedules.yml
index 160e8657f7c..8d1067d8f58 100644
--- a/db/docs/security_orchestration_policy_rule_schedules.yml
+++ b/db/docs/security_orchestration_policy_rule_schedules.yml
@@ -8,3 +8,4 @@ description: |
Security policies scheduled to run based on cadence defined in the policy
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59842
milestone: '13.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/security_scans.yml b/db/docs/security_scans.yml
index fc6732bc80f..b89faf6584d 100644
--- a/db/docs/security_scans.yml
+++ b/db/docs/security_scans.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores information about the security scans that are a part of Ci::Build
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23669
milestone: '12.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/security_training_providers.yml b/db/docs/security_training_providers.yml
index b8c6bc7e01f..69c42a39d1a 100644
--- a/db/docs/security_training_providers.yml
+++ b/db/docs/security_training_providers.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores information about the available security training providers
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78195
milestone: '14.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/security_trainings.yml b/db/docs/security_trainings.yml
index 2e84f8a5f65..6c55b6912e8 100644
--- a/db/docs/security_trainings.yml
+++ b/db/docs/security_trainings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores information about the primary security training provider for a given project
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78195
milestone: '14.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/self_managed_prometheus_alert_events.yml b/db/docs/self_managed_prometheus_alert_events.yml
index 83eb24475c9..08cdc639518 100644
--- a/db/docs/self_managed_prometheus_alert_events.yml
+++ b/db/docs/self_managed_prometheus_alert_events.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18046
milestone: '12.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/sent_notifications.yml b/db/docs/sent_notifications.yml
index 9bc7158b067..4e5b93804e9 100644
--- a/db/docs/sent_notifications.yml
+++ b/db/docs/sent_notifications.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Information related to sent email notifications that supports reply-by-email functionality
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/1173
milestone: '8.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/sentry_issues.yml b/db/docs/sentry_issues.yml
index af96751fc7d..25ff1ff9b8f 100644
--- a/db/docs/sentry_issues.yml
+++ b/db/docs/sentry_issues.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists issue data for the Error Tracking's Sentry backend
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20629
milestone: '12.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/serverless_domain_cluster.yml b/db/docs/serverless_domain_cluster.yml
index 2692eb22096..23c77b2c043 100644
--- a/db/docs/serverless_domain_cluster.yml
+++ b/db/docs/serverless_domain_cluster.yml
@@ -4,6 +4,7 @@ classes:
- Serverless::DomainCluster
feature_categories:
- kubernetes_management
-description: (Deprecated) A custom domain for a GitLab managed Knative installation
+description: "(Deprecated) A custom domain for a GitLab managed Knative installation"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19835
milestone: '12.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/service_desk_settings.yml b/db/docs/service_desk_settings.yml
index 1e924ecd01b..90c304c480c 100644
--- a/db/docs/service_desk_settings.yml
+++ b/db/docs/service_desk_settings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Settings related to Service Desk such as templates to use for email notifications
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19515
milestone: '12.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/shards.yml b/db/docs/shards.yml
index ffd4be0b12d..bf6c7dd3959 100644
--- a/db/docs/shards.yml
+++ b/db/docs/shards.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/f760c1cd17881c8aef3a33a3b43db54673db8111
milestone: '10.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/slack_api_scopes.yml b/db/docs/slack_api_scopes.yml
new file mode 100644
index 00000000000..467880dccf5
--- /dev/null
+++ b/db/docs/slack_api_scopes.yml
@@ -0,0 +1,10 @@
+---
+table_name: slack_api_scopes
+classes:
+- Integrations::KnownSlackApiScope
+feature_categories:
+- integrations
+description: Data related to the Slack application integration.
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105439
+milestone: '15.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/slack_integrations.yml b/db/docs/slack_integrations.yml
index 031bd77ada6..2c997cd1087 100644
--- a/db/docs/slack_integrations.yml
+++ b/db/docs/slack_integrations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Data related to the Slack application integration.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/f50ef96b87d8c785662e82843c22a2ef6093132e
milestone: '9.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/slack_integrations_scopes.yml b/db/docs/slack_integrations_scopes.yml
new file mode 100644
index 00000000000..b09c1b4a51e
--- /dev/null
+++ b/db/docs/slack_integrations_scopes.yml
@@ -0,0 +1,10 @@
+---
+table_name: slack_integrations_scopes
+classes:
+- Integrations::SlackIntegrationsKnownApiScope
+feature_categories:
+- integrations
+description: Data related to the Slack application integration.
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105439
+milestone: '15.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/smartcard_identities.yml b/db/docs/smartcard_identities.yml
index ad798c0728d..76b8d1a1368 100644
--- a/db/docs/smartcard_identities.yml
+++ b/db/docs/smartcard_identities.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/b6316689fdc2d142af85b17d511d39e50712b420
milestone: '11.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/snippet_repositories.yml b/db/docs/snippet_repositories.yml
index 52a6b96c42c..f33c3828c46 100644
--- a/db/docs/snippet_repositories.yml
+++ b/db/docs/snippet_repositories.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores repository information used to version control snippets.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23796
milestone: '12.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/snippet_repository_storage_moves.yml b/db/docs/snippet_repository_storage_moves.yml
index e8fea9995c7..8d6b7cfc668 100644
--- a/db/docs/snippet_repository_storage_moves.yml
+++ b/db/docs/snippet_repository_storage_moves.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45990
milestone: '13.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/snippet_statistics.yml b/db/docs/snippet_statistics.yml
index 390d096d1d3..74a0d29bedc 100644
--- a/db/docs/snippet_statistics.yml
+++ b/db/docs/snippet_statistics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores the repository size, commit count, and file count regarding the snippet repository.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35026
milestone: '13.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/snippet_user_mentions.yml b/db/docs/snippet_user_mentions.yml
index aee265b202a..8f141bd0263 100644
--- a/db/docs/snippet_user_mentions.yml
+++ b/db/docs/snippet_user_mentions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: For storing mentioned users, groups, projects referenced in a snippet description.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19009
milestone: '12.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/snippets.yml b/db/docs/snippets.yml
index 1d8f7ca6b15..4d92b59c924 100644
--- a/db/docs/snippets.yml
+++ b/db/docs/snippets.yml
@@ -9,3 +9,4 @@ feature_categories:
description: GitLab snippets allow you to store and share bits of code and text with other users.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/9265de3d25715aeafd38a4ef41596dca058dc18c
milestone: "1.0.1"
+gitlab_schema: gitlab_main
diff --git a/db/docs/software_license_policies.yml b/db/docs/software_license_policies.yml
index 615ae644985..b533ecfee01 100644
--- a/db/docs/software_license_policies.yml
+++ b/db/docs/software_license_policies.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Allows user to approve or deny the use certain software licenses in their project.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6246
milestone: '11.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/software_licenses.yml b/db/docs/software_licenses.yml
index 67ebd697fa8..c3cf32cc288 100644
--- a/db/docs/software_licenses.yml
+++ b/db/docs/software_licenses.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Normalized software licenses to use in conjunction with License Compliance features (like software license policies)
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6246
milestone: '11.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/spam_logs.yml b/db/docs/spam_logs.yml
index 6e16b3600c8..299d4cf9b1b 100644
--- a/db/docs/spam_logs.yml
+++ b/db/docs/spam_logs.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Logs users flagged by the Akismet anti-spam integration.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/2266
milestone: '8.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/sprints.yml b/db/docs/sprints.yml
index 7193c225dae..cee20a3bcb5 100644
--- a/db/docs/sprints.yml
+++ b/db/docs/sprints.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Also called iterations, timeboxes to which issues can be assigned. Used to plan and track work.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30125
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/ssh_signatures.yml b/db/docs/ssh_signatures.yml
index 7907f335585..583485d9bac 100644
--- a/db/docs/ssh_signatures.yml
+++ b/db/docs/ssh_signatures.yml
@@ -9,3 +9,4 @@ description: >
is part of the commit body and is stored in Gitaly.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87962
milestone: '15.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/status_check_responses.yml b/db/docs/status_check_responses.yml
index 0959a44106b..bcb063400e1 100644
--- a/db/docs/status_check_responses.yml
+++ b/db/docs/status_check_responses.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61135
milestone: '13.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/status_page_published_incidents.yml b/db/docs/status_page_published_incidents.yml
index 4a21ed156f2..92489209d1b 100644
--- a/db/docs/status_page_published_incidents.yml
+++ b/db/docs/status_page_published_incidents.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Corresponds to an issue which has been published to the Status Page
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29994
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/status_page_settings.yml b/db/docs/status_page_settings.yml
index a5cefe70300..0e948b6da5c 100644
--- a/db/docs/status_page_settings.yml
+++ b/db/docs/status_page_settings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Project settings related to Status Page
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25863
milestone: '12.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/subscriptions.yml b/db/docs/subscriptions.yml
index 0f20343bb5e..d129bc40401 100644
--- a/db/docs/subscriptions.yml
+++ b/db/docs/subscriptions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Subscriptions between users and subscribable objects; such as issues, epics and MRs.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/410d25c8ca8afabb25e5f89b36e3cfd09ffe6f87
milestone: '7.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/suggestions.yml b/db/docs/suggestions.yml
index 837fb58af04..7667a693b32 100644
--- a/db/docs/suggestions.yml
+++ b/db/docs/suggestions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Storing code suggestions within notes
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/8656
milestone: '11.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/system_note_metadata.yml b/db/docs/system_note_metadata.yml
index 40b193a4b91..371f90e2bc6 100644
--- a/db/docs/system_note_metadata.yml
+++ b/db/docs/system_note_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to store notes metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/1c3c7fb25d972fc19d5b4bb371cb21094d81e478
milestone: '9.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/taggings.yml b/db/docs/taggings.yml
index 71078ab9c19..a981907486f 100644
--- a/db/docs/taggings.yml
+++ b/db/docs/taggings.yml
@@ -7,4 +7,5 @@ feature_categories:
- runner
description: Taggings applied to arbitrary models based on entries in the 'tags' table
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/b946da44695c9c8fe8867bb87bcdf801c52177d3
-milestone: "1.2"
+milestone: '1.2'
+gitlab_schema: gitlab_ci
diff --git a/db/docs/tags.yml b/db/docs/tags.yml
index 9ae2a4361ff..28337af9291 100644
--- a/db/docs/tags.yml
+++ b/db/docs/tags.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Tags applied to arbitrary models through the 'taggings' table
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/b946da44695c9c8fe8867bb87bcdf801c52177d3
milestone: "<6.0"
+gitlab_schema: gitlab_ci
diff --git a/db/docs/term_agreements.yml b/db/docs/term_agreements.yml
index dd0bd829b19..502adad8ac0 100644
--- a/db/docs/term_agreements.yml
+++ b/db/docs/term_agreements.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/82eeb72c8c03727540b902d40e7e657d0a5ecb4c
milestone: '10.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/terraform_state_versions.yml b/db/docs/terraform_state_versions.yml
index 1d98b049f45..ba5578ebe9f 100644
--- a/db/docs/terraform_state_versions.yml
+++ b/db/docs/terraform_state_versions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Represents a Terraform state file at a point in time, with a corresponding file stored in object storage
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35211
milestone: '13.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/terraform_states.yml b/db/docs/terraform_states.yml
index dc2bc799582..eca77b164a8 100644
--- a/db/docs/terraform_states.yml
+++ b/db/docs/terraform_states.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Represents a Terraform state backend
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26619
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/timelog_categories.yml b/db/docs/timelog_categories.yml
index 7be6c588f0e..8448e3dd59f 100644
--- a/db/docs/timelog_categories.yml
+++ b/db/docs/timelog_categories.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Categories that can be associated to a timelog to categorize them
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87529
milestone: '15.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/timelogs.yml b/db/docs/timelogs.yml
index 0aebf8ec5db..3b2b5982897 100644
--- a/db/docs/timelogs.yml
+++ b/db/docs/timelogs.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Time spend data recorded by users on issues
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/870
milestone: '8.14'
+gitlab_schema: gitlab_main
diff --git a/db/docs/todos.yml b/db/docs/todos.yml
index 1a146e29d2f..d7328af05b9 100644
--- a/db/docs/todos.yml
+++ b/db/docs/todos.yml
@@ -7,3 +7,4 @@ feature_categories:
description: An action required or notification of action taken for a user on a target object, generated by various actions within the GitLab application
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/3d52e139b13ad077286f2f9f46b7e98f43ad9564
milestone: '8.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/token_with_ivs.yml b/db/docs/token_with_ivs.yml
index 1df428afdaa..2acdff0dad1 100644
--- a/db/docs/token_with_ivs.yml
+++ b/db/docs/token_with_ivs.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/37b80b4048190c2e1a35ec399e4aeb35d511090e
milestone: '13.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/topics.yml b/db/docs/topics.yml
index 25a448e3361..4411566e7ec 100644
--- a/db/docs/topics.yml
+++ b/db/docs/topics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores topics that can be assigned to projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67574
milestone: '14.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/trending_projects.yml b/db/docs/trending_projects.yml
index 1ee72f2d244..58a8421cbdf 100644
--- a/db/docs/trending_projects.yml
+++ b/db/docs/trending_projects.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores the list of trending projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/6749
milestone: '8.13'
+gitlab_schema: gitlab_main
diff --git a/db/docs/u2f_registrations.yml b/db/docs/u2f_registrations.yml
index c6e1b65eea4..27b0ca3f2f5 100644
--- a/db/docs/u2f_registrations.yml
+++ b/db/docs/u2f_registrations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/791cc9138be6ea1783e3c3853370cf0290f4d41e
milestone: '8.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/upcoming_reconciliations.yml b/db/docs/upcoming_reconciliations.yml
index 722fa2aee70..9e89bb1a57f 100644
--- a/db/docs/upcoming_reconciliations.yml
+++ b/db/docs/upcoming_reconciliations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores the data needed to notify a user of an upcoming reconciliation
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63054
milestone: '14.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/upload_states.yml b/db/docs/upload_states.yml
index 29df41c07dd..e6e86cb4bb1 100644
--- a/db/docs/upload_states.yml
+++ b/db/docs/upload_states.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Separate table for uploads containing Geo verification metadata.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65921
milestone: '14.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/uploads.yml b/db/docs/uploads.yml
index 53cfd49839a..b22f8b1d1ad 100644
--- a/db/docs/uploads.yml
+++ b/db/docs/uploads.yml
@@ -13,3 +13,4 @@ feature_categories:
description: For tracking blob metadata. Single table inheritance is used to relate this table to many other tables.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/4c622b71fd284058deee483bf0009f8179b792bc
milestone: '9.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_agent_details.yml b/db/docs/user_agent_details.yml
index 53292bf93f1..08cd811b60b 100644
--- a/db/docs/user_agent_details.yml
+++ b/db/docs/user_agent_details.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores user agent details for submission to Akismet spam detection.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5538
milestone: '8.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_callouts.yml b/db/docs/user_callouts.yml
index 63ee837eb2c..b77f2e538e4 100644
--- a/db/docs/user_callouts.yml
+++ b/db/docs/user_callouts.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/16735
milestone: '10.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_canonical_emails.yml b/db/docs/user_canonical_emails.yml
index 4d9c3ba4797..aeb1c3d830f 100644
--- a/db/docs/user_canonical_emails.yml
+++ b/db/docs/user_canonical_emails.yml
@@ -7,3 +7,4 @@ feature_categories:
description: stores the canonical version of user's primary email address
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27722
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_credit_card_validations.yml b/db/docs/user_credit_card_validations.yml
index 1ba8bf40460..4c8a851ade7 100644
--- a/db/docs/user_credit_card_validations.yml
+++ b/db/docs/user_credit_card_validations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores whether the user has completed a first time validation to run CI pipelines
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60626
milestone: '13.12'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_custom_attributes.yml b/db/docs/user_custom_attributes.yml
index 956450acb68..992f790cf24 100644
--- a/db/docs/user_custom_attributes.yml
+++ b/db/docs/user_custom_attributes.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Storing custom attributes per user
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/13038
milestone: '10.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_details.yml b/db/docs/user_details.yml
index 636074214b0..7001c22289d 100644
--- a/db/docs/user_details.yml
+++ b/db/docs/user_details.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores user details
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25483
milestone: '12.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_follow_users.yml b/db/docs/user_follow_users.yml
index 56243b97546..12d64b01a1d 100644
--- a/db/docs/user_follow_users.yml
+++ b/db/docs/user_follow_users.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores which users follow each other
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45451
milestone: '13.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_group_callouts.yml b/db/docs/user_group_callouts.yml
index 910752339ed..41028319708 100644
--- a/db/docs/user_group_callouts.yml
+++ b/db/docs/user_group_callouts.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68785
milestone: '14.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_highest_roles.yml b/db/docs/user_highest_roles.yml
index 23754f7c8d3..8713aff5f3f 100644
--- a/db/docs/user_highest_roles.yml
+++ b/db/docs/user_highest_roles.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26987
milestone: '12.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_interacted_projects.yml b/db/docs/user_interacted_projects.yml
index e62e863b0a9..206cb76c496 100644
--- a/db/docs/user_interacted_projects.yml
+++ b/db/docs/user_interacted_projects.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Tracks which projects a given user has actively interacted with
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17327
milestone: '10.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_namespace_callouts.yml b/db/docs/user_namespace_callouts.yml
index 5038ecce3bc..3f057e21aee 100644
--- a/db/docs/user_namespace_callouts.yml
+++ b/db/docs/user_namespace_callouts.yml
@@ -1,4 +1,3 @@
-
---
table_name: user_namespace_callouts
classes:
@@ -8,3 +7,4 @@ feature_categories:
description: Contains records of which users have dismissed a callout, grouped by namespace.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91092
milestone: '15.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_permission_export_uploads.yml b/db/docs/user_permission_export_uploads.yml
index 93f7e360ec2..217ede5bad2 100644
--- a/db/docs/user_permission_export_uploads.yml
+++ b/db/docs/user_permission_export_uploads.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47846
milestone: '13.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_phone_number_validations.yml b/db/docs/user_phone_number_validations.yml
index 9feacd76c5a..5d0100cdb95 100644
--- a/db/docs/user_phone_number_validations.yml
+++ b/db/docs/user_phone_number_validations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores whether the user has verified their phone number
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97762
milestone: '15.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_preferences.yml b/db/docs/user_preferences.yml
index b9afb679503..7bc1ab27ccb 100644
--- a/db/docs/user_preferences.yml
+++ b/db/docs/user_preferences.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores users' preferences
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7816
milestone: '11.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_project_callouts.yml b/db/docs/user_project_callouts.yml
index 308c3048aa7..40ccfa243ac 100644
--- a/db/docs/user_project_callouts.yml
+++ b/db/docs/user_project_callouts.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Adds the ability to track a user callout being dismissed by project
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94144
milestone: '15.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_statuses.yml b/db/docs/user_statuses.yml
index 94aaff70d85..e5f4d4ee72f 100644
--- a/db/docs/user_statuses.yml
+++ b/db/docs/user_statuses.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores users' statuses
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/20614
milestone: '11.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/user_synced_attributes_metadata.yml b/db/docs/user_synced_attributes_metadata.yml
index 73f3e6166ed..efc0ad1ec95 100644
--- a/db/docs/user_synced_attributes_metadata.yml
+++ b/db/docs/user_synced_attributes_metadata.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/4df54f260751a832ebf0b8c18524020d6604994b
milestone: '10.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/users.yml b/db/docs/users.yml
index 7c6a7fbdcb8..324d81850a1 100644
--- a/db/docs/users.yml
+++ b/db/docs/users.yml
@@ -9,3 +9,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/9ba1224867665844b117fa037e1465bb706b3685
milestone: "<6.0"
+gitlab_schema: gitlab_main
diff --git a/db/docs/users_ops_dashboard_projects.yml b/db/docs/users_ops_dashboard_projects.yml
index d8854d1db45..d09d3196e19 100644
--- a/db/docs/users_ops_dashboard_projects.yml
+++ b/db/docs/users_ops_dashboard_projects.yml
@@ -7,3 +7,4 @@ feature_categories:
description: https://docs.gitlab.com/ee/user/operations_dashboard/
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7341
milestone: '11.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/users_security_dashboard_projects.yml b/db/docs/users_security_dashboard_projects.yml
index 4c379e5739d..9d5d8cfbe2b 100644
--- a/db/docs/users_security_dashboard_projects.yml
+++ b/db/docs/users_security_dashboard_projects.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores projects which users select to appear in their Security Dashboard
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18708
milestone: '12.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/users_star_projects.yml b/db/docs/users_star_projects.yml
index 0199a0e3433..df03f721d57 100644
--- a/db/docs/users_star_projects.yml
+++ b/db/docs/users_star_projects.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores conection between users and project through staring action
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/714f7201d3362793d11f33793e5ef6dc83bdd2f0
milestone: '7.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/users_statistics.yml b/db/docs/users_statistics.yml
index f2c44cc456e..9110ee6063d 100644
--- a/db/docs/users_statistics.yml
+++ b/db/docs/users_statistics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: User statistics
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26261
milestone: '12.9'
+gitlab_schema: gitlab_main
diff --git a/db/docs/verification_codes.yml b/db/docs/verification_codes.yml
index 24c4d0991f9..9d0e3f53830 100644
--- a/db/docs/verification_codes.yml
+++ b/db/docs/verification_codes.yml
@@ -6,3 +6,4 @@ feature_categories:
description: Used by the JiHu edition for user verification
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71139
milestone: '14.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/views/postgres_autovacuum_activity.yml b/db/docs/views/postgres_autovacuum_activity.yml
new file mode 100644
index 00000000000..68878ceaaef
--- /dev/null
+++ b/db/docs/views/postgres_autovacuum_activity.yml
@@ -0,0 +1,10 @@
+---
+view_name: postgres_autovacuum_activity
+description: TODO
+classes:
+- Gitlab::Database::PostgresAutovacuumActivity
+feature_categories:
+- database
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85103
+milestone: '15.0'
+gitlab_schema: gitlab_shared
diff --git a/db/docs/views/postgres_constraints.yml b/db/docs/views/postgres_constraints.yml
new file mode 100644
index 00000000000..133b4430ef2
--- /dev/null
+++ b/db/docs/views/postgres_constraints.yml
@@ -0,0 +1,10 @@
+---
+view_name: postgres_constraints
+description: TODO
+classes:
+- Gitlab::Database::PostgresConstraint
+feature_categories:
+- database
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96815
+milestone: '15.4'
+gitlab_schema: gitlab_shared
diff --git a/db/docs/views/postgres_foreign_keys.yml b/db/docs/views/postgres_foreign_keys.yml
new file mode 100644
index 00000000000..0124d854681
--- /dev/null
+++ b/db/docs/views/postgres_foreign_keys.yml
@@ -0,0 +1,10 @@
+---
+view_name: postgres_foreign_keys
+description: TODO
+classes:
+- Gitlab::Database::PostgresForeignKey
+feature_categories:
+- database
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66473
+milestone: '14.2'
+gitlab_schema: gitlab_shared
diff --git a/db/docs/views/postgres_index_bloat_estimates.yml b/db/docs/views/postgres_index_bloat_estimates.yml
new file mode 100644
index 00000000000..ac3fc462b58
--- /dev/null
+++ b/db/docs/views/postgres_index_bloat_estimates.yml
@@ -0,0 +1,10 @@
+---
+view_name: postgres_index_bloat_estimates
+description: TODO
+classes:
+- Gitlab::Database::PostgresIndexBloatEstimate
+feature_categories:
+- database
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48698
+milestone: '13.7'
+gitlab_schema: gitlab_shared
diff --git a/db/docs/views/postgres_indexes.yml b/db/docs/views/postgres_indexes.yml
new file mode 100644
index 00000000000..b6c7a399216
--- /dev/null
+++ b/db/docs/views/postgres_indexes.yml
@@ -0,0 +1,10 @@
+---
+view_name: postgres_indexes
+description: TODO
+classes:
+- Gitlab::Database::PostgresIndex
+feature_categories:
+- database
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42967
+milestone: '13.5'
+gitlab_schema: gitlab_shared
diff --git a/db/docs/views/postgres_partitioned_tables.yml b/db/docs/views/postgres_partitioned_tables.yml
new file mode 100644
index 00000000000..ddec7550e80
--- /dev/null
+++ b/db/docs/views/postgres_partitioned_tables.yml
@@ -0,0 +1,10 @@
+---
+view_name: postgres_partitioned_tables
+description: TODO
+classes:
+- Gitlab::Database::PostgresPartitionedTables
+feature_categories:
+- database
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45591
+milestone: '13.6'
+gitlab_schema: gitlab_shared
diff --git a/db/docs/views/postgres_partitions.yml b/db/docs/views/postgres_partitions.yml
new file mode 100644
index 00000000000..4cb72f71956
--- /dev/null
+++ b/db/docs/views/postgres_partitions.yml
@@ -0,0 +1,10 @@
+---
+view_name: postgres_partitions
+description: TODO
+classes:
+- Gitlab::Database::PostgresPartition
+feature_categories:
+- database
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45592
+milestone: '13.6'
+gitlab_schema: gitlab_shared
diff --git a/db/docs/vulnerabilities.yml b/db/docs/vulnerabilities.yml
index 72f080a075f..11e7885f7cc 100644
--- a/db/docs/vulnerabilities.yml
+++ b/db/docs/vulnerabilities.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores information about vulnerabilites present in the project's source code
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16181
milestone: '12.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_advisories.yml b/db/docs/vulnerability_advisories.yml
index 2c88be94a11..18029e784b5 100644
--- a/db/docs/vulnerability_advisories.yml
+++ b/db/docs/vulnerability_advisories.yml
@@ -9,3 +9,4 @@ feature_categories:
description: Stores vulnerability advisories
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95622
milestone: '15.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_exports.yml b/db/docs/vulnerability_exports.yml
index 4254a4426d6..823ec9ac252 100644
--- a/db/docs/vulnerability_exports.yml
+++ b/db/docs/vulnerability_exports.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores metadata about exported Vulnerabilities CSV files
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27196
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_external_issue_links.yml b/db/docs/vulnerability_external_issue_links.yml
index 2adf90e6838..4c2dcd8d8d6 100644
--- a/db/docs/vulnerability_external_issue_links.yml
+++ b/db/docs/vulnerability_external_issue_links.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores information about connections between external issue trackers and vulnerabilities
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48465
milestone: '13.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_feedback.yml b/db/docs/vulnerability_feedback.yml
index bcd8c3935e6..473ae398531 100644
--- a/db/docs/vulnerability_feedback.yml
+++ b/db/docs/vulnerability_feedback.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores information about the confirm, dismiss, or create issue to investigate actions taken on vulnerabilities
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/5452
milestone: '10.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_finding_evidences.yml b/db/docs/vulnerability_finding_evidences.yml
index 0c7fc7c7fdd..35ecfd57fe3 100644
--- a/db/docs/vulnerability_finding_evidences.yml
+++ b/db/docs/vulnerability_finding_evidences.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores evidence used to identify presence of a vulnerability
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56790
milestone: '13.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_finding_links.yml b/db/docs/vulnerability_finding_links.yml
index c259778cb09..267355dd873 100644
--- a/db/docs/vulnerability_finding_links.yml
+++ b/db/docs/vulnerability_finding_links.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores URLs relevant to the vulnerability findings
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46555
milestone: '13.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_finding_signatures.yml b/db/docs/vulnerability_finding_signatures.yml
index a9faf1e8a1b..9a1e59697cf 100644
--- a/db/docs/vulnerability_finding_signatures.yml
+++ b/db/docs/vulnerability_finding_signatures.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores signatures of vulnerability locations which are used to improve tracking
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57840
milestone: '13.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_findings_remediations.yml b/db/docs/vulnerability_findings_remediations.yml
index f59b2360f42..ffadb160b76 100644
--- a/db/docs/vulnerability_findings_remediations.yml
+++ b/db/docs/vulnerability_findings_remediations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Join table between Remediations and Findings
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47166
milestone: '13.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_flags.yml b/db/docs/vulnerability_flags.yml
index 5d26faf66e0..fabc8a48200 100644
--- a/db/docs/vulnerability_flags.yml
+++ b/db/docs/vulnerability_flags.yml
@@ -4,6 +4,7 @@ classes:
- Vulnerabilities::Flag
feature_categories:
- vulnerability_management
-description: Stores additional information for vulnerabilities, for example if a vulnerability is identified as a false positive
+description: Stores additional information for vulnerabilities, for example if a vulnerability is identified as a false positive
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65573
milestone: '14.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_historical_statistics.yml b/db/docs/vulnerability_historical_statistics.yml
index 22622f2494d..5efa7a51e0f 100644
--- a/db/docs/vulnerability_historical_statistics.yml
+++ b/db/docs/vulnerability_historical_statistics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores aggregate vulnerability statistics which are used in the Security Dashboard
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36955
milestone: '13.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_identifiers.yml b/db/docs/vulnerability_identifiers.yml
index 9be03505671..fa8f63507e1 100644
--- a/db/docs/vulnerability_identifiers.yml
+++ b/db/docs/vulnerability_identifiers.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores identifiers (like CVE or CWE) for vulnerabilities that have been found
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6896
milestone: '11.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_issue_links.yml b/db/docs/vulnerability_issue_links.yml
index 8503af34831..4bbc587707e 100644
--- a/db/docs/vulnerability_issue_links.yml
+++ b/db/docs/vulnerability_issue_links.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Join table between Vulnerabilities and Issues
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19852
milestone: '12.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_merge_request_links.yml b/db/docs/vulnerability_merge_request_links.yml
index 7c9d958303f..8cc71b2a76a 100644
--- a/db/docs/vulnerability_merge_request_links.yml
+++ b/db/docs/vulnerability_merge_request_links.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Join table between Vulnerabilities and Merge Requests
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92096
milestone: '15.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_occurrence_identifiers.yml b/db/docs/vulnerability_occurrence_identifiers.yml
index 77b985e3e02..cd2236631aa 100644
--- a/db/docs/vulnerability_occurrence_identifiers.yml
+++ b/db/docs/vulnerability_occurrence_identifiers.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Join table between Findings and Identifiers
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6896
milestone: '11.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_occurrence_pipelines.yml b/db/docs/vulnerability_occurrence_pipelines.yml
index 5c798e7a2ac..542d4026824 100644
--- a/db/docs/vulnerability_occurrence_pipelines.yml
+++ b/db/docs/vulnerability_occurrence_pipelines.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Join table between Findings and Pipelines
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7578
milestone: '11.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_occurrences.yml b/db/docs/vulnerability_occurrences.yml
index 919a0ae19e7..90795b94c45 100644
--- a/db/docs/vulnerability_occurrences.yml
+++ b/db/docs/vulnerability_occurrences.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores information about findings for a given vulnerability
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6896
milestone: '11.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_reads.yml b/db/docs/vulnerability_reads.yml
index 29727da2e69..a7f589bf74e 100644
--- a/db/docs/vulnerability_reads.yml
+++ b/db/docs/vulnerability_reads.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Denormalized version of the vulnerabilites table used for faster reads
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74733
milestone: '14.6'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_remediations.yml b/db/docs/vulnerability_remediations.yml
index 3f4e93ba0d8..d522a2147c0 100644
--- a/db/docs/vulnerability_remediations.yml
+++ b/db/docs/vulnerability_remediations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores remediation information, such as diffs, for a given vulnerability
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47166
milestone: '13.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_scanners.yml b/db/docs/vulnerability_scanners.yml
index 2ea7a3763d6..90e8808e929 100644
--- a/db/docs/vulnerability_scanners.yml
+++ b/db/docs/vulnerability_scanners.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores information about the vulnerability scanners used by projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6896
milestone: '11.4'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_state_transitions.yml b/db/docs/vulnerability_state_transitions.yml
index 908b4120b47..0f168f7a0ee 100644
--- a/db/docs/vulnerability_state_transitions.yml
+++ b/db/docs/vulnerability_state_transitions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores state transitions of a Vulnerability
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87957
milestone: '15.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_statistics.yml b/db/docs/vulnerability_statistics.yml
index c94145e24f1..fbe1f07fbcc 100644
--- a/db/docs/vulnerability_statistics.yml
+++ b/db/docs/vulnerability_statistics.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores pre-calculated vulnerability statistics for projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34289
milestone: '13.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_user_mentions.yml b/db/docs/vulnerability_user_mentions.yml
index 4e4a07e97ac..9a95c834726 100644
--- a/db/docs/vulnerability_user_mentions.yml
+++ b/db/docs/vulnerability_user_mentions.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores notes for a given vulnerability
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27515
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/web_hook_logs.yml b/db/docs/web_hook_logs.yml
index e7ed77112bb..d342c9a9ed0 100644
--- a/db/docs/web_hook_logs.yml
+++ b/db/docs/web_hook_logs.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Webhooks logs data.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/330789c23c777d8ca646eba7c25f39cb7342cdee
milestone: '9.3'
+gitlab_schema: gitlab_main
diff --git a/db/docs/web_hooks.yml b/db/docs/web_hooks.yml
index 3c43dd837b5..6300a2f7c32 100644
--- a/db/docs/web_hooks.yml
+++ b/db/docs/web_hooks.yml
@@ -11,3 +11,4 @@ feature_categories:
description: Webhooks data with the custom HTTP callbacks that a user defines.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/edab46e9fa5f568b1423c0021e81d30453d7dc1e
milestone: "<6.0"
+gitlab_schema: gitlab_main
diff --git a/db/docs/webauthn_registrations.yml b/db/docs/webauthn_registrations.yml
index 13c4c28e24b..fc983ea60ca 100644
--- a/db/docs/webauthn_registrations.yml
+++ b/db/docs/webauthn_registrations.yml
@@ -7,3 +7,4 @@ feature_categories:
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35797
milestone: '13.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/wiki_page_meta.yml b/db/docs/wiki_page_meta.yml
index bed636eed08..2af6c3dc587 100644
--- a/db/docs/wiki_page_meta.yml
+++ b/db/docs/wiki_page_meta.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Used to record the metadata for wiki pages when we create events.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26529
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/wiki_page_slugs.yml b/db/docs/wiki_page_slugs.yml
index a6997322f73..89c9da260d8 100644
--- a/db/docs/wiki_page_slugs.yml
+++ b/db/docs/wiki_page_slugs.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores wiki page slug metadata for when we create events.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26529
milestone: '13.0'
+gitlab_schema: gitlab_main
diff --git a/db/docs/work_item_hierarchy_restrictions.yml b/db/docs/work_item_hierarchy_restrictions.yml
new file mode 100644
index 00000000000..8c8b85a9df9
--- /dev/null
+++ b/db/docs/work_item_hierarchy_restrictions.yml
@@ -0,0 +1,10 @@
+---
+table_name: work_item_hierarchy_restrictions
+classes:
+- WorkItems::HierarchyRestriction
+feature_categories:
+- team_planning
+description: Restrictions applied to parent/child relationships. Currently one of a predefined set but in future will support custom types.
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103796
+milestone: '15.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/work_item_parent_links.yml b/db/docs/work_item_parent_links.yml
index f4b5cd20abb..3966154a17e 100644
--- a/db/docs/work_item_parent_links.yml
+++ b/db/docs/work_item_parent_links.yml
@@ -8,3 +8,4 @@ feature_categories:
description: Persists link between work item and its parent.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87283
milestone: '15.1'
+gitlab_schema: gitlab_main
diff --git a/db/docs/work_item_progresses.yml b/db/docs/work_item_progresses.yml
new file mode 100644
index 00000000000..881e5d879e3
--- /dev/null
+++ b/db/docs/work_item_progresses.yml
@@ -0,0 +1,10 @@
+---
+table_name: work_item_progresses
+classes:
+- WorkItems::Progress
+feature_categories:
+- team_planning
+description: The progress of a Work Item.
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104591/
+milestone: '15.7'
+gitlab_schema: gitlab_main
diff --git a/db/docs/work_item_types.yml b/db/docs/work_item_types.yml
index afb694bca79..21ec69da152 100644
--- a/db/docs/work_item_types.yml
+++ b/db/docs/work_item_types.yml
@@ -7,3 +7,4 @@ feature_categories:
description: The work item type related to an issue. Currently one of a predefined set but in future will support custom types.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55705
milestone: '14.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/x509_certificates.yml b/db/docs/x509_certificates.yml
index bcf976155f4..364bd3615bb 100644
--- a/db/docs/x509_certificates.yml
+++ b/db/docs/x509_certificates.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores data about X.509 certificate
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17773
milestone: '12.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/x509_commit_signatures.yml b/db/docs/x509_commit_signatures.yml
index 170294c8d56..2d95eaa609e 100644
--- a/db/docs/x509_commit_signatures.yml
+++ b/db/docs/x509_commit_signatures.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores X.509 verification status of the commit
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17773
milestone: '12.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/x509_issuers.yml b/db/docs/x509_issuers.yml
index 30bbe8e4b12..04253f903be 100644
--- a/db/docs/x509_issuers.yml
+++ b/db/docs/x509_issuers.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Stores data about issuer of X.509 certificate
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17773
milestone: '12.8'
+gitlab_schema: gitlab_main
diff --git a/db/docs/zentao_tracker_data.yml b/db/docs/zentao_tracker_data.yml
index c99aebd70e2..c02e08bb5e8 100644
--- a/db/docs/zentao_tracker_data.yml
+++ b/db/docs/zentao_tracker_data.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Data related to the ZenTao integration.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67938
milestone: '14.2'
+gitlab_schema: gitlab_main
diff --git a/db/docs/zoom_meetings.yml b/db/docs/zoom_meetings.yml
index 620df953ad5..95bb98bf896 100644
--- a/db/docs/zoom_meetings.yml
+++ b/db/docs/zoom_meetings.yml
@@ -7,3 +7,4 @@ feature_categories:
description: Persists Zoom meetings, its associations and its metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17890
milestone: '12.5'
+gitlab_schema: gitlab_main
diff --git a/db/fixtures/development/14_pipelines.rb b/db/fixtures/development/14_pipelines.rb
index 8f63ce3dbfe..9120d95ca5d 100644
--- a/db/fixtures/development/14_pipelines.rb
+++ b/db/fixtures/development/14_pipelines.rb
@@ -1,51 +1,69 @@
require './spec/support/sidekiq_middleware'
class Gitlab::Seeder::Pipelines
- STAGES = %w[build test deploy notify]
- BUILDS = [
- # build stage
- { name: 'build:linux', stage: 'build', status: :success,
- queued_at: 10.hour.ago, started_at: 9.hour.ago, finished_at: 8.hour.ago },
- { name: 'build:osx', stage: 'build', status: :success,
- queued_at: 10.hour.ago, started_at: 10.hour.ago, finished_at: 9.hour.ago },
-
- # test stage
- { name: 'rspec:linux 0 3', stage: 'test', status: :success,
- queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
- { name: 'rspec:linux 1 3', stage: 'test', status: :success,
- queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
- { name: 'rspec:linux 2 3', stage: 'test', status: :success,
- queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
- { name: 'rspec:windows 0 3', stage: 'test', status: :success,
- queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
- { name: 'rspec:windows 1 3', stage: 'test', status: :success,
- queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
- { name: 'rspec:windows 2 3', stage: 'test', status: :success,
- queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
- { name: 'rspec:windows 2 3', stage: 'test', status: :success,
- queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
- { name: 'rspec:osx', stage: 'test', status_event: :success,
- queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
- { name: 'spinach:linux', stage: 'test', status: :success,
- queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
- { name: 'spinach:osx', stage: 'test', status: :failed, allow_failure: true,
- queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
-
- # deploy stage
- { name: 'staging', stage: 'deploy', environment: 'staging', status_event: :success,
- options: { environment: { action: 'start', on_stop: 'stop staging' } },
- queued_at: 7.hour.ago, started_at: 6.hour.ago, finished_at: 4.hour.ago },
- { name: 'stop staging', stage: 'deploy', environment: 'staging',
- when: 'manual', status: :skipped },
- { name: 'production', stage: 'deploy', environment: 'production',
- when: 'manual', status: :skipped },
-
- # notify stage
- { name: 'slack', stage: 'notify', when: 'manual', status: :success },
- ]
- EXTERNAL_JOBS = [
- { name: 'jenkins', stage: 'test', status: :success,
- queued_at: 7.hour.ago, started_at: 6.hour.ago, finished_at: 4.hour.ago },
+ PIPELINE_STAGES = [
+ {
+ name: 'build',
+ position: 0,
+ builds: [
+ { name: 'build:linux', status: :success,
+ queued_at: 10.hour.ago, started_at: 9.hour.ago, finished_at: 8.hour.ago },
+ { name: 'build:osx', status: :success,
+ queued_at: 10.hour.ago, started_at: 10.hour.ago, finished_at: 9.hour.ago },
+ ]
+ },
+ {
+ name: 'test',
+ position: 1,
+ builds: [
+ { name: 'rspec:linux 0 3', status: :success,
+ queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
+ { name: 'rspec:linux 1 3', status: :success,
+ queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
+ { name: 'rspec:linux 2 3', status: :success,
+ queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
+ { name: 'rspec:windows 0 3', status: :success,
+ queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
+ { name: 'rspec:windows 1 3', status: :success,
+ queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
+ { name: 'rspec:windows 2 3', status: :success,
+ queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
+ { name: 'rspec:windows 2 3', status: :success,
+ queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
+ { name: 'rspec:osx', status_event: :success,
+ queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
+ { name: 'spinach:linux', status: :success,
+ queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago },
+ { name: 'spinach:osx', status: :failed, allow_failure: true,
+ queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }
+ ]
+ },
+ {
+ name: 'deploy',
+ position: 2,
+ builds: [
+ { name: 'staging', environment: 'staging', status_event: :success,
+ options: { environment: { action: 'start', on_stop: 'stop staging' } },
+ queued_at: 7.hour.ago, started_at: 6.hour.ago, finished_at: 4.hour.ago },
+ { name: 'stop staging', environment: 'staging', when: 'manual', status: :skipped },
+ { name: 'production', environment: 'production', when: 'manual', status: :skipped },
+ ]
+ },
+ {
+ name: 'notify',
+ position: 3,
+ builds: [
+ { name: 'slack', when: 'manual', status: :success },
+ ]
+ },
+ {
+ name: 'external',
+ position: 4,
+ builds: [
+ { name: 'jenkins', status: :success,
+ queued_at: 7.hour.ago, started_at: 6.hour.ago, finished_at: 4.hour.ago }
+ ]
+ }
]
def initialize(project)
@@ -54,9 +72,20 @@ class Gitlab::Seeder::Pipelines
def seed!
pipelines.each do |pipeline|
- BUILDS.each { |opts| build_create!(pipeline, opts) }
- EXTERNAL_JOBS.each { |opts| commit_status_create!(pipeline, opts) }
+ PIPELINE_STAGES.each do |stage_attrs|
+ stage = stage_create!(pipeline, stage_attrs[:name], stage_attrs[:position])
+
+ stage_attrs[:builds].each do |build_attrs|
+ if stage_attrs[:name] == 'external'
+ generic_commit_status_create!(pipeline, stage, build_attrs)
+ else
+ build_create!(pipeline, stage, build_attrs)
+ end
+ end
+ end
+
pipeline.update_duration
+
::Ci::ProcessPipelineService.new(pipeline).execute
end
@@ -65,6 +94,10 @@ class Gitlab::Seeder::Pipelines
private
+ def stage_create!(pipeline, name, position)
+ Ci::Stage.create!(pipeline: pipeline, project: pipeline.project, name: name, position: position)
+ end
+
def pipelines
create_master_pipelines + create_merge_request_pipelines
end
@@ -106,8 +139,8 @@ class Gitlab::Seeder::Pipelines
project.ci_pipelines.create!(sha: commit.id, ref: ref, source: :push)
end
- def build_create!(pipeline, opts = {})
- attributes = job_attributes(pipeline, opts)
+ def build_create!(pipeline, stage, opts = {})
+ attributes = job_attributes(pipeline, stage, opts)
attributes[:options] ||= {}
attributes[:options][:script] = 'build command'
@@ -129,7 +162,7 @@ class Gitlab::Seeder::Pipelines
end
def setup_artifacts(build)
- return unless build.stage_name == "build"
+ return unless build.ci_stage.name == 'build'
artifacts_cache_file(artifacts_archive_path) do |file|
build.job_artifacts.build(project: build.project, file_type: :archive, file_format: :zip, file: file)
@@ -141,7 +174,7 @@ class Gitlab::Seeder::Pipelines
end
def setup_test_reports(build)
- return unless build.stage_name == "test" && build.name == "rspec:osx"
+ return unless build.ci_stage.name == 'test' && build.name == "rspec:osx"
if build.ref == build.project.default_branch
artifacts_cache_file(test_reports_pass_path) do |file|
@@ -160,15 +193,15 @@ class Gitlab::Seeder::Pipelines
end
end
- def commit_status_create!(pipeline, opts = {})
- attributes = job_attributes(pipeline, opts)
+ def generic_commit_status_create!(pipeline, stage, opts = {})
+ attributes = job_attributes(pipeline, stage, opts)
GenericCommitStatus.create!(attributes)
end
- def job_attributes(pipeline, opts)
+ def job_attributes(pipeline, stage, opts)
{
- name: 'test build', stage: 'test', stage_idx: stage_index(opts[:stage]),
+ name: 'test build', ci_stage: stage, stage_idx: stage.position,
ref: pipeline.ref, tag: false, user: build_user, project: @project, pipeline: pipeline,
scheduling_type: :stage, created_at: Time.now, updated_at: Time.now
}.merge(opts)
@@ -186,10 +219,6 @@ class Gitlab::Seeder::Pipelines
Ci::Build::AVAILABLE_STATUSES.sample
end
- def stage_index(stage)
- STAGES.index(stage) || 0
- end
-
def artifacts_archive_path
Rails.root + 'spec/fixtures/ci_build_artifacts.zip'
end
diff --git a/db/fixtures/development/50_create_work_item_hierarchy_restrictions.rb b/db/fixtures/development/50_create_work_item_hierarchy_restrictions.rb
new file mode 100644
index 00000000000..b5c5d0cacdd
--- /dev/null
+++ b/db/fixtures/development/50_create_work_item_hierarchy_restrictions.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+Gitlab::Seeder.quiet do
+ Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
+end
diff --git a/db/fixtures/production/020_create_work_item_hierarchy_restrictions.rb b/db/fixtures/production/020_create_work_item_hierarchy_restrictions.rb
new file mode 100644
index 00000000000..b5c5d0cacdd
--- /dev/null
+++ b/db/fixtures/production/020_create_work_item_hierarchy_restrictions.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+Gitlab::Seeder.quiet do
+ Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
+end
diff --git a/db/migrate/20220824082427_remove_tmp_idx_vulnerability_occurrences_on_id_and_scanner_id.rb b/db/migrate/20220824082427_remove_tmp_idx_vulnerability_occurrences_on_id_and_scanner_id.rb
new file mode 100644
index 00000000000..ebb90df569b
--- /dev/null
+++ b/db/migrate/20220824082427_remove_tmp_idx_vulnerability_occurrences_on_id_and_scanner_id.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class RemoveTmpIdxVulnerabilityOccurrencesOnIdAndScannerId < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = 'tmp_index_vulnerability_occurrences_on_id_and_scanner_id'
+ REPORT_TYPES = { cluster_image_scanning: 7, generic: 99 }.freeze
+ CLAUSE = "report_type IN (#{REPORT_TYPES.values.join(',')})"
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name :vulnerability_occurrences, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :vulnerability_occurrences,
+ [:id, :scanner_id],
+ where: CLAUSE,
+ name: INDEX_NAME
+ end
+end
diff --git a/db/migrate/20220908150054_add_runner_registration_enabled_to_namespace_settings.rb b/db/migrate/20220908150054_add_runner_registration_enabled_to_namespace_settings.rb
new file mode 100644
index 00000000000..7721d0d592c
--- /dev/null
+++ b/db/migrate/20220908150054_add_runner_registration_enabled_to_namespace_settings.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddRunnerRegistrationEnabledToNamespaceSettings < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :namespace_settings, :runner_registration_enabled, :boolean, default: true
+ end
+end
diff --git a/db/migrate/20221026095133_add_status_updated_at_to_container_repository.rb b/db/migrate/20221026095133_add_status_updated_at_to_container_repository.rb
new file mode 100644
index 00000000000..caf838e8307
--- /dev/null
+++ b/db/migrate/20221026095133_add_status_updated_at_to_container_repository.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddStatusUpdatedAtToContainerRepository < Gitlab::Database::Migration[2.0]
+ def change
+ add_column :container_repositories, :status_updated_at, :datetime_with_timezone
+ end
+end
diff --git a/db/migrate/20221101174816_create_package_metadata.rb b/db/migrate/20221101174816_create_package_metadata.rb
new file mode 100644
index 00000000000..f1456689641
--- /dev/null
+++ b/db/migrate/20221101174816_create_package_metadata.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class CreatePackageMetadata < Gitlab::Database::Migration[2.0]
+ def change
+ create_table :pm_packages do |t|
+ t.integer :purl_type, null: false, limit: 2
+ t.text :name, null: false, limit: 255
+ t.index [:purl_type, :name], unique: true, name: 'i_pm_packages_purl_type_and_name'
+ end
+ end
+end
diff --git a/db/migrate/20221101194416_create_package_metadata_versions.rb b/db/migrate/20221101194416_create_package_metadata_versions.rb
new file mode 100644
index 00000000000..19b86d86de1
--- /dev/null
+++ b/db/migrate/20221101194416_create_package_metadata_versions.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class CreatePackageMetadataVersions < Gitlab::Database::Migration[2.0]
+ def change
+ create_table :pm_package_versions do |t|
+ t.references :pm_package, foreign_key: { to_table: :pm_packages, on_delete: :cascade }
+ t.text :version, null: false, limit: 255
+ t.index [:pm_package_id, :version], unique: true, name: 'i_pm_package_versions_on_package_id_and_version'
+ end
+ end
+end
diff --git a/db/migrate/20221101195309_create_package_metadata_licenses.rb b/db/migrate/20221101195309_create_package_metadata_licenses.rb
new file mode 100644
index 00000000000..1fbb0c147bd
--- /dev/null
+++ b/db/migrate/20221101195309_create_package_metadata_licenses.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class CreatePackageMetadataLicenses < Gitlab::Database::Migration[2.0]
+ def change
+ create_table :pm_licenses do |t|
+ t.text :spdx_identifier, null: false, limit: 50
+ t.index [:spdx_identifier], unique: true, name: 'i_pm_licenses_on_spdx_identifier'
+ end
+ end
+end
diff --git a/db/migrate/20221101195543_create_package_metadata_package_version_licenses.rb b/db/migrate/20221101195543_create_package_metadata_package_version_licenses.rb
new file mode 100644
index 00000000000..9007f869e1f
--- /dev/null
+++ b/db/migrate/20221101195543_create_package_metadata_package_version_licenses.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+class CreatePackageMetadataPackageVersionLicenses < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = 'i_pm_package_version_licenses_on_version_and_license_ids'
+
+ def change
+ create_table :pm_package_version_licenses, primary_key: [:pm_package_version_id, :pm_license_id] do |t|
+ t.references :pm_package_version, foreign_key: { on_delete: :cascade }, null: false
+ t.references :pm_license, foreign_key: { on_delete: :cascade }, null: false
+ end
+ end
+end
diff --git a/db/migrate/20221102150737_index_environments_for_name_search_within_folder.rb b/db/migrate/20221102150737_index_environments_for_name_search_within_folder.rb
new file mode 100644
index 00000000000..10fd8b33c45
--- /dev/null
+++ b/db/migrate/20221102150737_index_environments_for_name_search_within_folder.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class IndexEnvironmentsForNameSearchWithinFolder < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_environments_for_name_search_within_folder'
+
+ def up
+ add_concurrent_index :environments,
+ "project_id, lower(ltrim(name, environment_type || '/')) varchar_pattern_ops, state", name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :environments, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20221102195642_create_dependency_proxy_manifest_states.rb b/db/migrate/20221102195642_create_dependency_proxy_manifest_states.rb
new file mode 100644
index 00000000000..e0da92c6c94
--- /dev/null
+++ b/db/migrate/20221102195642_create_dependency_proxy_manifest_states.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+class CreateDependencyProxyManifestStates < Gitlab::Database::Migration[2.0]
+ DEPENDENCY_PROXY_MANIFEST_INDEX_NAME = "index_manifest_states_on_dependency_proxy_manifest_id"
+ VERIFICATION_STATE_INDEX_NAME = "index_manifest_states_on_verification_state"
+ PENDING_VERIFICATION_INDEX_NAME = "index_manifest_states_pending_verification"
+ FAILED_VERIFICATION_INDEX_NAME = "index_manifest_states_failed_verification"
+ NEEDS_VERIFICATION_INDEX_NAME = "index_manifest_states_needs_verification"
+
+ enable_lock_retries!
+
+ def up
+ create_table :dependency_proxy_manifest_states, id: false do |t|
+ t.datetime_with_timezone :verification_started_at
+ t.datetime_with_timezone :verification_retry_at
+ t.datetime_with_timezone :verified_at
+ t.references :dependency_proxy_manifest,
+ primary_key: true,
+ index: { name: DEPENDENCY_PROXY_MANIFEST_INDEX_NAME },
+ default: nil,
+ foreign_key: { on_delete: :cascade }
+ t.integer :verification_state, default: 0, limit: 2, null: false
+ t.integer :verification_retry_count, limit: 2, default: 0, null: false
+ t.binary :verification_checksum, using: 'verification_checksum::bytea'
+ t.text :verification_failure, limit: 255
+
+ t.index :verification_state, name: VERIFICATION_STATE_INDEX_NAME
+ t.index :verified_at,
+ where: "(verification_state = 0)",
+ order: { verified_at: 'ASC NULLS FIRST' },
+ name: PENDING_VERIFICATION_INDEX_NAME
+ t.index :verification_retry_at,
+ where: "(verification_state = 3)",
+ order: { verification_retry_at: 'ASC NULLS FIRST' },
+ name: FAILED_VERIFICATION_INDEX_NAME
+ t.index :verification_state,
+ where: "(verification_state = 0 OR verification_state = 3)",
+ name: NEEDS_VERIFICATION_INDEX_NAME
+ end
+ end
+
+ def down
+ drop_table :dependency_proxy_manifest_states
+ end
+end
diff --git a/db/migrate/20221103205317_create_dast_pre_scan_verification.rb b/db/migrate/20221103205317_create_dast_pre_scan_verification.rb
new file mode 100644
index 00000000000..85375be53b5
--- /dev/null
+++ b/db/migrate/20221103205317_create_dast_pre_scan_verification.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class CreateDastPreScanVerification < Gitlab::Database::Migration[2.0]
+ def up
+ create_table :dast_pre_scan_verifications do |t|
+ t.references :dast_profile, null: false, foreign_key: { on_delete: :cascade },
+ index: { name: 'index_dast_pre_scan_verifications_on_dast_profile_id' }
+
+ t.bigint :ci_pipeline_id, null: false
+
+ t.timestamps_with_timezone
+
+ t.integer :status, default: 0, limit: 2, null: false
+
+ t.index :ci_pipeline_id, unique: true, name: :index_dast_pre_scan_verifications_on_ci_pipeline_id
+ end
+ end
+
+ def down
+ drop_table :dast_pre_scan_verifications
+ end
+end
diff --git a/db/migrate/20221107013943_add_accepted_reviewers_to_merge_request_predictions.rb b/db/migrate/20221107013943_add_accepted_reviewers_to_merge_request_predictions.rb
new file mode 100644
index 00000000000..9876ac3127a
--- /dev/null
+++ b/db/migrate/20221107013943_add_accepted_reviewers_to_merge_request_predictions.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddAcceptedReviewersToMergeRequestPredictions < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :merge_request_predictions, :accepted_reviewers, :jsonb, null: false, default: {}
+ end
+end
diff --git a/db/migrate/20221110080508_add_partition_id_to_ci_unit_test_failures.rb b/db/migrate/20221110080508_add_partition_id_to_ci_unit_test_failures.rb
new file mode 100644
index 00000000000..2217048965c
--- /dev/null
+++ b/db/migrate/20221110080508_add_partition_id_to_ci_unit_test_failures.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiUnitTestFailures < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_unit_test_failures, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20221110080636_add_partition_id_to_ci_sources_pipelines.rb b/db/migrate/20221110080636_add_partition_id_to_ci_sources_pipelines.rb
new file mode 100644
index 00000000000..a76955d8db1
--- /dev/null
+++ b/db/migrate/20221110080636_add_partition_id_to_ci_sources_pipelines.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiSourcesPipelines < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_sources_pipelines, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20221110080748_add_partition_id_to_ci_build_pending_states.rb b/db/migrate/20221110080748_add_partition_id_to_ci_build_pending_states.rb
new file mode 100644
index 00000000000..abf14f4ceca
--- /dev/null
+++ b/db/migrate/20221110080748_add_partition_id_to_ci_build_pending_states.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiBuildPendingStates < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_build_pending_states, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20221110080822_add_partition_id_to_ci_build_trace_chunks.rb b/db/migrate/20221110080822_add_partition_id_to_ci_build_trace_chunks.rb
new file mode 100644
index 00000000000..27a9bbdb896
--- /dev/null
+++ b/db/migrate/20221110080822_add_partition_id_to_ci_build_trace_chunks.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiBuildTraceChunks < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_build_trace_chunks, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20221110080913_add_partition_id_to_ci_build_report_results.rb b/db/migrate/20221110080913_add_partition_id_to_ci_build_report_results.rb
new file mode 100644
index 00000000000..b915ac9f907
--- /dev/null
+++ b/db/migrate/20221110080913_add_partition_id_to_ci_build_report_results.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiBuildReportResults < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_build_report_results, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20221110080956_add_partition_id_to_ci_build_needs.rb b/db/migrate/20221110080956_add_partition_id_to_ci_build_needs.rb
new file mode 100644
index 00000000000..faa0309f2c1
--- /dev/null
+++ b/db/migrate/20221110080956_add_partition_id_to_ci_build_needs.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiBuildNeeds < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_build_needs, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20221110081037_add_partition_id_to_ci_builds_runner_session.rb b/db/migrate/20221110081037_add_partition_id_to_ci_builds_runner_session.rb
new file mode 100644
index 00000000000..e6f5d65c7b1
--- /dev/null
+++ b/db/migrate/20221110081037_add_partition_id_to_ci_builds_runner_session.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiBuildsRunnerSession < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_builds_runner_session, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20221110081115_add_partition_id_to_ci_pending_builds.rb b/db/migrate/20221110081115_add_partition_id_to_ci_pending_builds.rb
new file mode 100644
index 00000000000..24435fd7aa9
--- /dev/null
+++ b/db/migrate/20221110081115_add_partition_id_to_ci_pending_builds.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiPendingBuilds < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_pending_builds, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20221110081207_add_partition_id_to_ci_build_trace_metadata.rb b/db/migrate/20221110081207_add_partition_id_to_ci_build_trace_metadata.rb
new file mode 100644
index 00000000000..0c4baa2af1e
--- /dev/null
+++ b/db/migrate/20221110081207_add_partition_id_to_ci_build_trace_metadata.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiBuildTraceMetadata < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_build_trace_metadata, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20221110081348_add_partition_id_to_ci_running_builds.rb b/db/migrate/20221110081348_add_partition_id_to_ci_running_builds.rb
new file mode 100644
index 00000000000..f4d61a93e3e
--- /dev/null
+++ b/db/migrate/20221110081348_add_partition_id_to_ci_running_builds.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiRunningBuilds < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_running_builds, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20221110081448_add_partition_id_to_ci_job_variables.rb b/db/migrate/20221110081448_add_partition_id_to_ci_job_variables.rb
new file mode 100644
index 00000000000..0e0eadd8f72
--- /dev/null
+++ b/db/migrate/20221110081448_add_partition_id_to_ci_job_variables.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiJobVariables < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_job_variables, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20221110183103_add_dashboard_fields_to_namespace_details.rb b/db/migrate/20221110183103_add_dashboard_fields_to_namespace_details.rb
new file mode 100644
index 00000000000..73e8ccbcb51
--- /dev/null
+++ b/db/migrate/20221110183103_add_dashboard_fields_to_namespace_details.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class AddDashboardFieldsToNamespaceDetails < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :namespace_details, :dashboard_notification_at, :datetime_with_timezone
+ add_column :namespace_details, :dashboard_enforcement_at, :datetime_with_timezone
+ end
+end
diff --git a/db/migrate/20221111123146_add_onboarding_in_progress_to_users.rb b/db/migrate/20221111123146_add_onboarding_in_progress_to_users.rb
new file mode 100644
index 00000000000..665760347bf
--- /dev/null
+++ b/db/migrate/20221111123146_add_onboarding_in_progress_to_users.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddOnboardingInProgressToUsers < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ # rubocop:disable Migration/AddColumnsToWideTables
+ def up
+ add_column :users, :onboarding_in_progress, :boolean, default: false, null: false
+ end
+
+ def down
+ remove_column :users, :onboarding_in_progress
+ end
+ # rubocop:enable Migration/AddColumnsToWideTables
+end
diff --git a/db/migrate/20221111123147_add_onboarding_step_url_to_user_details.rb b/db/migrate/20221111123147_add_onboarding_step_url_to_user_details.rb
new file mode 100644
index 00000000000..7b6c035e05a
--- /dev/null
+++ b/db/migrate/20221111123147_add_onboarding_step_url_to_user_details.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddOnboardingStepUrlToUserDetails < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ # rubocop:disable Migration/AddLimitToTextColumns
+ # limit is added in 20221111123148_add_text_limit_to_onboarding_step_url.rb
+ def up
+ add_column :user_details, :onboarding_step_url, :text
+ end
+
+ def down
+ remove_column :user_details, :onboarding_step_url
+ end
+ # rubocop:enable Migration/AddLimitToTextColumns
+end
diff --git a/db/migrate/20221111123148_add_text_limit_to_onboarding_step_url.rb b/db/migrate/20221111123148_add_text_limit_to_onboarding_step_url.rb
new file mode 100644
index 00000000000..cfd9f004131
--- /dev/null
+++ b/db/migrate/20221111123148_add_text_limit_to_onboarding_step_url.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class AddTextLimitToOnboardingStepUrl < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ add_text_limit :user_details, :onboarding_step_url, 2000
+ end
+
+ def down
+ remove_text_limit :user_details, :onboarding_step_url
+ end
+end
diff --git a/db/migrate/20221111135238_create_dependency_list_exports_table.rb b/db/migrate/20221111135238_create_dependency_list_exports_table.rb
new file mode 100644
index 00000000000..d47bd93fff7
--- /dev/null
+++ b/db/migrate/20221111135238_create_dependency_list_exports_table.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class CreateDependencyListExportsTable < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ create_table :dependency_list_exports do |t|
+ t.timestamps_with_timezone null: false
+ t.bigint :project_id, null: false
+ t.bigint :user_id
+ t.integer :file_store
+ t.integer :status, default: 0, null: false, limit: 2
+ t.text :file, limit: 255
+
+ t.index :user_id
+ t.index :project_id
+ end
+ end
+
+ def down
+ drop_table :dependency_list_exports
+ end
+end
diff --git a/db/migrate/20221111142921_add_hierarchy_restrictions.rb b/db/migrate/20221111142921_add_hierarchy_restrictions.rb
new file mode 100644
index 00000000000..dd80de04969
--- /dev/null
+++ b/db/migrate/20221111142921_add_hierarchy_restrictions.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class AddHierarchyRestrictions < Gitlab::Database::Migration[2.0]
+ UNIQUE_INDEX_NAME = 'index_work_item_hierarchy_restrictions_on_parent_and_child'
+
+ def up
+ create_table :work_item_hierarchy_restrictions do |t|
+ t.references :parent_type, index: true, null: false,
+ foreign_key: { on_delete: :cascade, to_table: :work_item_types }
+ t.references :child_type, index: true, null: false,
+ foreign_key: { on_delete: :cascade, to_table: :work_item_types }
+ t.integer :maximum_depth, limit: 2
+
+ t.index [:parent_type_id, :child_type_id], unique: true, name: UNIQUE_INDEX_NAME
+ end
+ end
+
+ def down
+ drop_table :work_item_hierarchy_restrictions
+ end
+end
diff --git a/db/migrate/20221114131943_add_short_title_to_appearances.rb b/db/migrate/20221114131943_add_short_title_to_appearances.rb
new file mode 100644
index 00000000000..6bf5d32f441
--- /dev/null
+++ b/db/migrate/20221114131943_add_short_title_to_appearances.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class AddShortTitleToAppearances < Gitlab::Database::Migration[2.0]
+ # rubocop:disable Migration/AddLimitToTextColumns
+ # limit is added in 20221115085813_add_limit_to_appereances_short_title.rb
+ def change
+ add_column :appearances, :short_title, :text
+ end
+ # rubocop:enable Migration/AddLimitToTextColumns
+end
diff --git a/db/migrate/20221114145103_add_last_seat_refresh_at_to_gitlab_subscriptions.rb b/db/migrate/20221114145103_add_last_seat_refresh_at_to_gitlab_subscriptions.rb
new file mode 100644
index 00000000000..77d6bb42f02
--- /dev/null
+++ b/db/migrate/20221114145103_add_last_seat_refresh_at_to_gitlab_subscriptions.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddLastSeatRefreshAtToGitlabSubscriptions < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ TABLE_NAME = 'gitlab_subscriptions'
+ COLUMN_NAME = 'last_seat_refresh_at'
+
+ def up
+ add_column(TABLE_NAME, COLUMN_NAME, :datetime_with_timezone)
+ end
+
+ def down
+ remove_column(TABLE_NAME, COLUMN_NAME)
+ end
+end
diff --git a/db/migrate/20221114212908_add_debug_trace_to_ci_builds_metadata.rb b/db/migrate/20221114212908_add_debug_trace_to_ci_builds_metadata.rb
new file mode 100644
index 00000000000..aee479dfcee
--- /dev/null
+++ b/db/migrate/20221114212908_add_debug_trace_to_ci_builds_metadata.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddDebugTraceToCiBuildsMetadata < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :p_ci_builds_metadata, :debug_trace_enabled, :boolean, null: false, default: false
+ end
+end
diff --git a/db/migrate/20221115085813_add_limit_to_appereances_short_title.rb b/db/migrate/20221115085813_add_limit_to_appereances_short_title.rb
new file mode 100644
index 00000000000..d75895216f7
--- /dev/null
+++ b/db/migrate/20221115085813_add_limit_to_appereances_short_title.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class AddLimitToAppereancesShortTitle < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ add_text_limit :appearances, :short_title, 255
+ end
+
+ def down
+ remove_text_limit :appearances, :short_title
+ end
+end
diff --git a/db/migrate/20221116100056_add_foreign_key_to_dependency_list_exports.rb b/db/migrate/20221116100056_add_foreign_key_to_dependency_list_exports.rb
new file mode 100644
index 00000000000..1c7df66bd04
--- /dev/null
+++ b/db/migrate/20221116100056_add_foreign_key_to_dependency_list_exports.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class AddForeignKeyToDependencyListExports < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key(
+ :dependency_list_exports,
+ :users,
+ column: :user_id,
+ on_delete: :nullify)
+ add_concurrent_foreign_key(
+ :dependency_list_exports,
+ :projects,
+ column: :project_id,
+ on_delete: :cascade)
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key_if_exists :dependency_list_exports, column: :user_id
+ end
+ with_lock_retries do
+ remove_foreign_key_if_exists :dependency_list_exports, column: :project_id
+ end
+ end
+end
diff --git a/db/migrate/20221116113323_add_index_on_team_id_and_chat_id.rb b/db/migrate/20221116113323_add_index_on_team_id_and_chat_id.rb
new file mode 100644
index 00000000000..7a7bcf48d80
--- /dev/null
+++ b/db/migrate/20221116113323_add_index_on_team_id_and_chat_id.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddIndexOnTeamIdAndChatId < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_chat_names_on_team_id_and_chat_id'
+
+ def up
+ add_concurrent_index(:chat_names, [:team_id, :chat_id], name: INDEX_NAME)
+ end
+
+ def down
+ remove_concurrent_index_by_name :chat_names, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20221116124821_add_enterprise_boolean_to_bulk_imports.rb b/db/migrate/20221116124821_add_enterprise_boolean_to_bulk_imports.rb
new file mode 100644
index 00000000000..69cc39b72f6
--- /dev/null
+++ b/db/migrate/20221116124821_add_enterprise_boolean_to_bulk_imports.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddEnterpriseBooleanToBulkImports < Gitlab::Database::Migration[2.0]
+ def change
+ add_column :bulk_imports, :source_enterprise, :boolean, default: true, null: false
+ end
+end
diff --git a/db/migrate/20221116160204_create_ml_experiment_metadata_and_ml_candidate_metadata.rb b/db/migrate/20221116160204_create_ml_experiment_metadata_and_ml_candidate_metadata.rb
new file mode 100644
index 00000000000..288d1e4be98
--- /dev/null
+++ b/db/migrate/20221116160204_create_ml_experiment_metadata_and_ml_candidate_metadata.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class CreateMlExperimentMetadataAndMlCandidateMetadata < Gitlab::Database::Migration[2.0]
+ def change
+ create_table :ml_experiment_metadata do |t|
+ t.timestamps_with_timezone null: false
+ t.references :experiment,
+ foreign_key: { to_table: :ml_experiments, on_delete: :cascade },
+ index: false,
+ null: false
+ t.text :name, limit: 255, null: false
+ t.text :value, limit: 5000, null: false
+
+ t.index [:experiment_id, :name], unique: true
+ end
+
+ create_table :ml_candidate_metadata do |t|
+ t.timestamps_with_timezone null: false
+ t.references :candidate,
+ foreign_key: { to_table: :ml_candidates, on_delete: :cascade },
+ index: false,
+ null: false
+ t.text :name, limit: 255, null: false, index: true
+ t.text :value, limit: 5000, null: false
+
+ t.index [:candidate_id, :name], unique: true
+ end
+ end
+end
diff --git a/db/migrate/20221116161126_add_auth_signing_type_to_keys.rb b/db/migrate/20221116161126_add_auth_signing_type_to_keys.rb
new file mode 100644
index 00000000000..795074fa0ca
--- /dev/null
+++ b/db/migrate/20221116161126_add_auth_signing_type_to_keys.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddAuthSigningTypeToKeys < Gitlab::Database::Migration[2.0]
+ def change
+ add_column :keys, :usage_type, :integer, limit: 2, null: false, default: 0
+ end
+end
diff --git a/db/migrate/20221121091238_add_work_item_progress.rb b/db/migrate/20221121091238_add_work_item_progress.rb
new file mode 100644
index 00000000000..0e306bf7332
--- /dev/null
+++ b/db/migrate/20221121091238_add_work_item_progress.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddWorkItemProgress < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def up
+ create_table :work_item_progresses, id: false do |t|
+ t.timestamps_with_timezone null: false
+ t.references :issue, primary_key: true, index: false, default: nil,
+ foreign_key: { on_delete: :cascade, to_table: :issues }
+ t.integer :progress, default: 0, limit: 2, null: false
+ end
+ end
+
+ def down
+ drop_table :work_item_progresses
+ end
+end
diff --git a/db/migrate/20221121100431_add_partition_id_to_ci_resources.rb b/db/migrate/20221121100431_add_partition_id_to_ci_resources.rb
new file mode 100644
index 00000000000..5b783057b3a
--- /dev/null
+++ b/db/migrate/20221121100431_add_partition_id_to_ci_resources.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiResources < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column(:ci_resources, :partition_id, :bigint, default: 100, null: false)
+ end
+end
diff --git a/db/migrate/20221122141046_add_allow_pipeline_trigger_approve_deployment_to_project_settings.rb b/db/migrate/20221122141046_add_allow_pipeline_trigger_approve_deployment_to_project_settings.rb
new file mode 100644
index 00000000000..b9397d80211
--- /dev/null
+++ b/db/migrate/20221122141046_add_allow_pipeline_trigger_approve_deployment_to_project_settings.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddAllowPipelineTriggerApproveDeploymentToProjectSettings < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ def change
+ add_column :project_settings, :allow_pipeline_trigger_approve_deployment, :boolean, default: false, null: false
+ end
+end
diff --git a/db/migrate/20221122225925_set_email_confirmation_setting_before_removing_send_user_confirmation_email_column.rb b/db/migrate/20221122225925_set_email_confirmation_setting_before_removing_send_user_confirmation_email_column.rb
new file mode 100644
index 00000000000..f92704ac212
--- /dev/null
+++ b/db/migrate/20221122225925_set_email_confirmation_setting_before_removing_send_user_confirmation_email_column.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class SetEmailConfirmationSettingBeforeRemovingSendUserConfirmationEmailColumn < Gitlab::Database::Migration[2.0]
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ class ApplicationSetting < MigrationRecord
+ self.table_name = 'application_settings'
+ end
+
+ def up
+ return unless ApplicationSetting.exists?
+ return unless ApplicationSetting.last.send_user_confirmation_email
+
+ ApplicationSetting.last.update(email_confirmation_setting: 2)
+ end
+
+ def down
+ return unless ApplicationSetting.exists?
+
+ ApplicationSetting.last.update(email_confirmation_setting: 0)
+ end
+end
diff --git a/db/migrate/20221124113925_add_pipeline_hierarchy_size_to_plan_limits.rb b/db/migrate/20221124113925_add_pipeline_hierarchy_size_to_plan_limits.rb
new file mode 100644
index 00000000000..f96097febe5
--- /dev/null
+++ b/db/migrate/20221124113925_add_pipeline_hierarchy_size_to_plan_limits.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddPipelineHierarchySizeToPlanLimits < Gitlab::Database::Migration[2.1]
+ def change
+ add_column(:plan_limits, :pipeline_hierarchy_size, :integer, default: 1000, null: false)
+ end
+end
diff --git a/db/migrate/20221128123514_add_source_partition_id_to_ci_sources_pipeline.rb b/db/migrate/20221128123514_add_source_partition_id_to_ci_sources_pipeline.rb
new file mode 100644
index 00000000000..a98cdbf88de
--- /dev/null
+++ b/db/migrate/20221128123514_add_source_partition_id_to_ci_sources_pipeline.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddSourcePartitionIdToCiSourcesPipeline < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_sources_pipelines, :source_partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20221129192619_increase_self_hosted_attachment_size_limit.rb b/db/migrate/20221129192619_increase_self_hosted_attachment_size_limit.rb
new file mode 100644
index 00000000000..ec938afc7ba
--- /dev/null
+++ b/db/migrate/20221129192619_increase_self_hosted_attachment_size_limit.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class IncreaseSelfHostedAttachmentSizeLimit < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ def up
+ change_column_default :application_settings, :max_attachment_size, from: 10, to: 100
+ end
+
+ def down
+ change_column_default :application_settings, :max_attachment_size, from: 100, to: 10
+ end
+end
diff --git a/db/migrate/20221130170433_create_dast_pre_scan_verification_step.rb b/db/migrate/20221130170433_create_dast_pre_scan_verification_step.rb
new file mode 100644
index 00000000000..f0b88ab1c4d
--- /dev/null
+++ b/db/migrate/20221130170433_create_dast_pre_scan_verification_step.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class CreateDastPreScanVerificationStep < Gitlab::Database::Migration[2.1]
+ def up
+ create_table :dast_pre_scan_verification_steps do |t|
+ t.references :dast_pre_scan_verification,
+ null: false, foreign_key: { on_delete: :cascade },
+ index: { name: 'i_dast_pre_scan_verification_steps_on_pre_scan_verification_id' }
+ t.timestamps_with_timezone
+ t.text :name, limit: 255
+ t.text :verification_errors, array: true, default: [], null: false
+ end
+ end
+
+ def down
+ drop_table :dast_pre_scan_verification_steps
+ end
+end
diff --git a/db/migrate/20221130182056_add_plan_limits_max_size_to_requirements_v2_artifact.rb b/db/migrate/20221130182056_add_plan_limits_max_size_to_requirements_v2_artifact.rb
new file mode 100644
index 00000000000..48d53d69898
--- /dev/null
+++ b/db/migrate/20221130182056_add_plan_limits_max_size_to_requirements_v2_artifact.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddPlanLimitsMaxSizeToRequirementsV2Artifact < Gitlab::Database::Migration[2.0]
+ def change
+ add_column :plan_limits, :ci_max_artifact_size_requirements_v2, :integer, null: false, default: 0
+ end
+end
diff --git a/db/migrate/20221202144210_create_achievements.rb b/db/migrate/20221202144210_create_achievements.rb
new file mode 100644
index 00000000000..30b2fd528ee
--- /dev/null
+++ b/db/migrate/20221202144210_create_achievements.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class CreateAchievements < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ def up
+ create_table :achievements do |t|
+ t.references :namespace,
+ null: false,
+ index: false,
+ foreign_key: { on_delete: :cascade }
+ t.timestamps_with_timezone null: false
+ t.text :name, null: false, limit: 255
+ t.text :avatar, limit: 255
+ t.text :description, limit: 1024
+ t.boolean :revokeable, default: false, null: false
+ t.index 'namespace_id, LOWER(name)', unique: true
+ end
+ end
+
+ def down
+ drop_table :achievements
+ end
+end
diff --git a/db/migrate/20221202202351_remove_index_i_ci_job_token_project_scope_links_on_source_and_target_project.rb b/db/migrate/20221202202351_remove_index_i_ci_job_token_project_scope_links_on_source_and_target_project.rb
new file mode 100644
index 00000000000..81a636739b4
--- /dev/null
+++ b/db/migrate/20221202202351_remove_index_i_ci_job_token_project_scope_links_on_source_and_target_project.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+class RemoveIndexICiJobTokenProjectScopeLinksOnSourceAndTargetProject < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ TABLE_NAME = 'ci_job_token_project_scope_links'
+ OLD_INDEX_NAME = 'i_ci_job_token_project_scope_links_on_source_and_target_project'
+ NEW_INDEX_NAME = 'ci_job_token_scope_links_source_and_target_project_direction'
+ NEW_INDEX_COL = %w[source_project_id target_project_id direction]
+
+ def up
+ add_concurrent_index(
+ TABLE_NAME,
+ NEW_INDEX_COL,
+ name: NEW_INDEX_NAME,
+ unique: true
+ )
+ remove_concurrent_index_by_name(TABLE_NAME, OLD_INDEX_NAME)
+ end
+
+ def down
+ # noop: as we can have duplicate records once the unique index is removed
+ end
+end
diff --git a/db/migrate/20221205061134_add_disable_pats_to_application_settings.rb b/db/migrate/20221205061134_add_disable_pats_to_application_settings.rb
new file mode 100644
index 00000000000..0f2438f3e78
--- /dev/null
+++ b/db/migrate/20221205061134_add_disable_pats_to_application_settings.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddDisablePatsToApplicationSettings < Gitlab::Database::Migration[2.0]
+ def change
+ add_column(:application_settings, :disable_personal_access_tokens, :boolean, default: false, null: false)
+ end
+end
diff --git a/db/migrate/20221206163420_add_use_new_navigation_to_user_preferences.rb b/db/migrate/20221206163420_add_use_new_navigation_to_user_preferences.rb
new file mode 100644
index 00000000000..72531e8c067
--- /dev/null
+++ b/db/migrate/20221206163420_add_use_new_navigation_to_user_preferences.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddUseNewNavigationToUserPreferences < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :user_preferences, :use_new_navigation, :boolean, default: nil, null: true
+ end
+end
diff --git a/db/migrate/20221206211814_add_authorized_scopes_to_slack_integration.rb b/db/migrate/20221206211814_add_authorized_scopes_to_slack_integration.rb
new file mode 100644
index 00000000000..40abf087dfe
--- /dev/null
+++ b/db/migrate/20221206211814_add_authorized_scopes_to_slack_integration.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+class AddAuthorizedScopesToSlackIntegration < Gitlab::Database::Migration[2.1]
+ def up
+ create_table :slack_api_scopes do |t|
+ t.text :name, null: false, limit: 100
+
+ t.index :name, name: 'index_slack_api_scopes_on_name', unique: true
+ end
+
+ create_table :slack_integrations_scopes do |t|
+ references :slack_api_scope,
+ null: false,
+ index: false, # See composite index
+ foreign_key: {
+ to_table: :slack_api_scopes,
+ on_delete: :cascade
+ }
+
+ references :slack_integration,
+ null: false,
+ index: false, # see composite index
+ foreign_key: {
+ to_table: :slack_integrations,
+ on_delete: :cascade
+ }
+
+ t.index [:slack_integration_id, :slack_api_scope_id],
+ unique: true,
+ name: 'index_slack_api_scopes_on_name_and_integration'
+ end
+ end
+
+ def down
+ drop_table :slack_integrations_scopes, if_exists: true
+ drop_table :slack_api_scopes, if_exists: true
+ end
+end
diff --git a/db/migrate/20221206222032_add_read_code_to_member_roles.rb b/db/migrate/20221206222032_add_read_code_to_member_roles.rb
new file mode 100644
index 00000000000..dc62672ccd0
--- /dev/null
+++ b/db/migrate/20221206222032_add_read_code_to_member_roles.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddReadCodeToMemberRoles < Gitlab::Database::Migration[2.1]
+ def change
+ add_column :member_roles, :read_code, :boolean, default: false
+ end
+end
diff --git a/db/migrate/20221206235208_add_max_terraform_state_size_bytes_to_application_settings.rb b/db/migrate/20221206235208_add_max_terraform_state_size_bytes_to_application_settings.rb
new file mode 100644
index 00000000000..28bfce8ac0b
--- /dev/null
+++ b/db/migrate/20221206235208_add_max_terraform_state_size_bytes_to_application_settings.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class AddMaxTerraformStateSizeBytesToApplicationSettings < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ CONSTRAINT_NAME = "app_settings_max_terraform_state_size_bytes_check"
+
+ def up
+ add_column(
+ :application_settings,
+ :max_terraform_state_size_bytes,
+ :integer,
+ null: false,
+ default: 0,
+ if_not_exists: true
+ )
+
+ add_check_constraint :application_settings, "max_terraform_state_size_bytes >= 0", CONSTRAINT_NAME
+ end
+
+ def down
+ remove_column :application_settings, :max_terraform_state_size_bytes, if_exists: true
+ end
+end
diff --git a/db/migrate/20221207140259_add_bulk_import_enabled_to_application_settings.rb b/db/migrate/20221207140259_add_bulk_import_enabled_to_application_settings.rb
new file mode 100644
index 00000000000..c6702559da1
--- /dev/null
+++ b/db/migrate/20221207140259_add_bulk_import_enabled_to_application_settings.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddBulkImportEnabledToApplicationSettings < Gitlab::Database::Migration[2.1]
+ def change
+ add_column :application_settings, :bulk_import_enabled, :boolean, default: false, null: false
+ end
+end
diff --git a/db/migrate/20221207220120_create_dast_scanner_profiles_runner_tags.rb b/db/migrate/20221207220120_create_dast_scanner_profiles_runner_tags.rb
new file mode 100644
index 00000000000..f07cebb6b2c
--- /dev/null
+++ b/db/migrate/20221207220120_create_dast_scanner_profiles_runner_tags.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class CreateDastScannerProfilesRunnerTags < Gitlab::Database::Migration[2.1]
+ def up
+ create_table :dast_scanner_profiles_tags do |t|
+ t.references :dast_scanner_profile, null: false, foreign_key: { on_delete: :cascade },
+ index: { name: 'i_dast_scanner_profiles_tags_on_scanner_profiles_id' }
+
+ t.bigint :tag_id, null: false
+
+ t.index :tag_id, name: :index_dast_scanner_profiles_tags_on_tag_id
+ end
+ end
+
+ def down
+ drop_table :dast_scanner_profiles_tags
+ end
+end
diff --git a/db/migrate/20221208122921_remove_constraints_from_ci_resources_for_partition_id.rb b/db/migrate/20221208122921_remove_constraints_from_ci_resources_for_partition_id.rb
new file mode 100644
index 00000000000..ffdd744b05c
--- /dev/null
+++ b/db/migrate/20221208122921_remove_constraints_from_ci_resources_for_partition_id.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class RemoveConstraintsFromCiResourcesForPartitionId < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ def up
+ change_column_null :ci_resources, :partition_id, true
+ end
+
+ def down
+ # no-op
+ # Adding back the not null constraint requires a long exclusive lock.
+ # Also depending on when it gets called, it might not even be possible to
+ # execute because the application could have inserted null values.
+ end
+end
diff --git a/db/migrate/20221209110934_update_import_sources_on_application_settings.rb b/db/migrate/20221209110934_update_import_sources_on_application_settings.rb
new file mode 100644
index 00000000000..59955d30544
--- /dev/null
+++ b/db/migrate/20221209110934_update_import_sources_on_application_settings.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class UpdateImportSourcesOnApplicationSettings < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ class ApplicationSetting < MigrationRecord
+ end
+
+ def up
+ return if import_sources.empty?
+
+ new_sources = import_sources - ['google_code']
+ ApplicationSetting.update_all(import_sources: new_sources.to_yaml)
+ end
+
+ def down
+ ## a reversion is not needed as google_code is no longer a supported import source
+ # and attempting to save it as one will result in a ActiveRecord error.
+ end
+
+ def import_sources
+ ## the last ApplicationSetting record is used to determine application settings
+ import_sources = ApplicationSetting.last&.import_sources
+ import_sources.nil? ? [] : YAML.safe_load(import_sources)
+ end
+end
diff --git a/db/migrate/20221209110935_fix_update_import_sources_on_application_settings.rb b/db/migrate/20221209110935_fix_update_import_sources_on_application_settings.rb
new file mode 100644
index 00000000000..d3123113e82
--- /dev/null
+++ b/db/migrate/20221209110935_fix_update_import_sources_on_application_settings.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+# This fixes 20221209110934_update_import_sources_on_application_settings.rb, which
+# previously serialized a YAML column into a string.
+class FixUpdateImportSourcesOnApplicationSettings < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ class ApplicationSetting < MigrationRecord
+ end
+
+ def up
+ sources = ApplicationSetting.last&.import_sources
+
+ return unless sources.is_a?(String)
+ return if sources.start_with?('---')
+
+ sources = YAML.safe_load(sources)
+
+ ApplicationSetting.update_all(import_sources: sources.to_yaml)
+ end
+
+ def down; end
+end
diff --git a/db/migrate/20221213184314_change_enabled_default_in_dependency_proxy_group_settings.rb b/db/migrate/20221213184314_change_enabled_default_in_dependency_proxy_group_settings.rb
new file mode 100644
index 00000000000..6cddcb80949
--- /dev/null
+++ b/db/migrate/20221213184314_change_enabled_default_in_dependency_proxy_group_settings.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class ChangeEnabledDefaultInDependencyProxyGroupSettings < Gitlab::Database::Migration[2.0]
+ def change
+ change_column_default :dependency_proxy_group_settings, :enabled, from: false, to: true
+ end
+end
diff --git a/db/post_migrate/20210731132939_backfill_stage_event_hash.rb b/db/post_migrate/20210731132939_backfill_stage_event_hash.rb
index 2c4dc904387..e4966cc0e6d 100644
--- a/db/post_migrate/20210731132939_backfill_stage_event_hash.rb
+++ b/db/post_migrate/20210731132939_backfill_stage_event_hash.rb
@@ -75,7 +75,7 @@ class BackfillStageEventHash < ActiveRecord::Migration[6.1]
records = delete_invalid_records(records)
next if records.empty?
- hashes_by_stage = records.to_h { |stage| [stage, calculate_stage_events_hash(stage)] }
+ hashes_by_stage = records.index_with { |stage| calculate_stage_events_hash(stage) }
hashes = hashes_by_stage.values.uniq
StageEventHash.insert_all(hashes.map { |hash| { hash_sha256: hash } })
diff --git a/db/post_migrate/20220202105733_delete_service_template_records.rb b/db/post_migrate/20220202105733_delete_service_template_records.rb
index b70bacc83aa..e1697f23588 100644
--- a/db/post_migrate/20220202105733_delete_service_template_records.rb
+++ b/db/post_migrate/20220202105733_delete_service_template_records.rb
@@ -5,6 +5,7 @@ class DeleteServiceTemplateRecords < Gitlab::Database::Migration[1.0]
# Disable single-table inheritance
self.inheritance_column = :_type_disabled
end
+
def up
Integration.where(template: true).delete_all
end
diff --git a/db/post_migrate/20220420214703_schedule_backfill_draft_status_on_merge_requests_corrected_regex.rb b/db/post_migrate/20220420214703_schedule_backfill_draft_status_on_merge_requests_corrected_regex.rb
index 1001aca583b..9640d5d2516 100644
--- a/db/post_migrate/20220420214703_schedule_backfill_draft_status_on_merge_requests_corrected_regex.rb
+++ b/db/post_migrate/20220420214703_schedule_backfill_draft_status_on_merge_requests_corrected_regex.rb
@@ -16,7 +16,7 @@ class ScheduleBackfillDraftStatusOnMergeRequestsCorrectedRegex < Gitlab::Databas
eligible_mrs = MergeRequest.where(state_id: 1)
.where(draft: false)
- .where("title ~* ?", "#{CORRECTED_REGEXP_STR}")
+ .where("title ~* ?", CORRECTED_REGEXP_STR)
queue_background_migration_jobs_by_range_at_intervals(
eligible_mrs,
diff --git a/db/post_migrate/20220920180451_schedule_vulnerabilities_feedback_migration.rb b/db/post_migrate/20220920180451_schedule_vulnerabilities_feedback_migration.rb
new file mode 100644
index 00000000000..35f7a5dcdb6
--- /dev/null
+++ b/db/post_migrate/20220920180451_schedule_vulnerabilities_feedback_migration.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+class ScheduleVulnerabilitiesFeedbackMigration < Gitlab::Database::Migration[2.0]
+ MIGRATION = 'MigrateVulnerabilitiesFeedbackToVulnerabilitiesStateTransition'
+ TABLE_NAME = :vulnerability_feedback
+ BATCH_COLUMN = :id
+ DELAY_INTERVAL = 5.minutes
+ BATCH_SIZE = 250
+ MAX_BATCH_SIZE = 250
+ SUB_BATCH_SIZE = 50
+
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ queue_batched_background_migration(
+ MIGRATION,
+ TABLE_NAME,
+ BATCH_COLUMN,
+ job_interval: DELAY_INTERVAL,
+ batch_size: BATCH_SIZE,
+ max_batch_size: MAX_BATCH_SIZE,
+ sub_batch_size: SUB_BATCH_SIZE
+ )
+ end
+
+ def down
+ delete_batched_background_migration(
+ MIGRATION,
+ TABLE_NAME,
+ BATCH_COLUMN,
+ []
+ )
+ end
+end
diff --git a/db/post_migrate/20221018095434_schedule_disable_legacy_open_source_license_for_projects_less_than_five_mb.rb b/db/post_migrate/20221018095434_schedule_disable_legacy_open_source_license_for_projects_less_than_five_mb.rb
new file mode 100644
index 00000000000..8b3f0668200
--- /dev/null
+++ b/db/post_migrate/20221018095434_schedule_disable_legacy_open_source_license_for_projects_less_than_five_mb.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+class ScheduleDisableLegacyOpenSourceLicenseForProjectsLessThanFiveMb < Gitlab::Database::Migration[2.0]
+ MIGRATION = 'DisableLegacyOpenSourceLicenseForProjectsLessThanFiveMb'
+ INTERVAL = 2.minutes
+ BATCH_SIZE = 4_000
+ MAX_BATCH_SIZE = 50_000
+ SUB_BATCH_SIZE = 250
+
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ return unless Gitlab.com?
+
+ queue_batched_background_migration(
+ MIGRATION,
+ :project_settings,
+ :project_id,
+ job_interval: INTERVAL,
+ batch_size: BATCH_SIZE,
+ max_batch_size: MAX_BATCH_SIZE,
+ sub_batch_size: SUB_BATCH_SIZE
+ )
+ end
+
+ def down
+ return unless Gitlab.com?
+
+ delete_batched_background_migration(MIGRATION, :project_settings, :project_id, [])
+ end
+end
diff --git a/db/post_migrate/20221104115712_backfill_project_statistics_storage_size_without_uploads_size.rb b/db/post_migrate/20221104115712_backfill_project_statistics_storage_size_without_uploads_size.rb
new file mode 100644
index 00000000000..9dd64a3e7b5
--- /dev/null
+++ b/db/post_migrate/20221104115712_backfill_project_statistics_storage_size_without_uploads_size.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class BackfillProjectStatisticsStorageSizeWithoutUploadsSize < Gitlab::Database::Migration[2.0]
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ DELAY_INTERVAL = 2.minutes.to_i
+ BATCH_SIZE = 500
+ MIGRATION_CLASS = 'BackfillProjectStatisticsStorageSizeWithoutUploadsSize'
+ SUB_BATCH_SIZE = 100
+
+ disable_ddl_transaction!
+
+ def up
+ return unless Gitlab.dev_or_test_env? || Gitlab.org_or_com?
+
+ queue_batched_background_migration(
+ MIGRATION_CLASS,
+ :project_statistics,
+ :project_id,
+ job_interval: DELAY_INTERVAL,
+ batch_size: BATCH_SIZE,
+ sub_batch_size: SUB_BATCH_SIZE
+ )
+ end
+
+ def down
+ return unless Gitlab.dev_or_test_env? || Gitlab.org_or_com?
+
+ delete_batched_background_migration(MIGRATION_CLASS, :project_statistics, :project_id, [])
+ end
+end
diff --git a/db/post_migrate/20221104141647_add_index_for_non_public_top_level_groups_to_namespaces.rb b/db/post_migrate/20221104141647_add_index_for_non_public_top_level_groups_to_namespaces.rb
new file mode 100644
index 00000000000..ade35a39737
--- /dev/null
+++ b/db/post_migrate/20221104141647_add_index_for_non_public_top_level_groups_to_namespaces.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexForNonPublicTopLevelGroupsToNamespaces < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ TABLE_NAME = 'namespaces'
+ INDEX_NAME = 'index_namespaces_on_type_and_visibility_and_parent_id'
+ CONDITIONS = "(type = 'Group' AND parent_id IS NULL AND visibility_level != 20)"
+
+ def up
+ add_concurrent_index TABLE_NAME, :id, name: INDEX_NAME, where: CONDITIONS
+ end
+
+ def down
+ remove_concurrent_index_by_name TABLE_NAME, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221109160052_add_default_for_approval_project_rules_scanners.rb b/db/post_migrate/20221109160052_add_default_for_approval_project_rules_scanners.rb
new file mode 100644
index 00000000000..a527bf4b2ef
--- /dev/null
+++ b/db/post_migrate/20221109160052_add_default_for_approval_project_rules_scanners.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddDefaultForApprovalProjectRulesScanners < Gitlab::Database::Migration[2.0]
+ def up
+ change_column_default :approval_project_rules, :scanners, from: nil, to: []
+ end
+
+ def down
+ change_column_default :approval_project_rules, :scanners, from: [], to: nil
+ end
+end
diff --git a/db/post_migrate/20221110152133_delete_orphans_approval_rules.rb b/db/post_migrate/20221110152133_delete_orphans_approval_rules.rb
new file mode 100644
index 00000000000..55b6a10d786
--- /dev/null
+++ b/db/post_migrate/20221110152133_delete_orphans_approval_rules.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+class DeleteOrphansApprovalRules < Gitlab::Database::Migration[2.0]
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ BATCH_SIZE = 1000
+ SUB_BATCH_SIZE = 500
+ MERGE_REQUEST_MIGRATION = 'DeleteOrphansApprovalMergeRequestRules'
+ PROJECT_MIGRATION = 'DeleteOrphansApprovalProjectRules'
+ INTERVAL = 2.minutes
+
+ def up
+ queue_batched_background_migration(
+ PROJECT_MIGRATION,
+ :approval_project_rules,
+ :id,
+ job_interval: INTERVAL,
+ batch_size: BATCH_SIZE,
+ sub_batch_size: SUB_BATCH_SIZE
+ )
+
+ queue_batched_background_migration(
+ MERGE_REQUEST_MIGRATION,
+ :approval_merge_request_rules,
+ :id,
+ job_interval: INTERVAL,
+ batch_size: BATCH_SIZE,
+ sub_batch_size: SUB_BATCH_SIZE
+ )
+ end
+
+ def down
+ delete_batched_background_migration(PROJECT_MIGRATION, :approval_project_rules, :id, [])
+ delete_batched_background_migration(MERGE_REQUEST_MIGRATION, :approval_merge_request_rules, :id, [])
+ end
+end
diff --git a/db/post_migrate/20221110190340_add_partial_legacy_open_source_license_available_project_id_index.rb b/db/post_migrate/20221110190340_add_partial_legacy_open_source_license_available_project_id_index.rb
new file mode 100644
index 00000000000..9931b25eb3c
--- /dev/null
+++ b/db/post_migrate/20221110190340_add_partial_legacy_open_source_license_available_project_id_index.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddPartialLegacyOpenSourceLicenseAvailableProjectIdIndex < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_project_settings_on_legacy_os_license_project_id'
+
+ def up
+ add_concurrent_index :project_settings,
+ :project_id,
+ where: "legacy_open_source_license_available = TRUE",
+ name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name(:project_settings, INDEX_NAME)
+ end
+end
diff --git a/db/post_migrate/20221114142044_delete_experiments_foreign_keys.rb b/db/post_migrate/20221114142044_delete_experiments_foreign_keys.rb
new file mode 100644
index 00000000000..d44b7a6eb51
--- /dev/null
+++ b/db/post_migrate/20221114142044_delete_experiments_foreign_keys.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+class DeleteExperimentsForeignKeys < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ with_lock_retries do
+ remove_foreign_key_if_exists :experiment_subjects, :users, name: 'fk_dfc3e211d4'
+ end
+
+ with_lock_retries do
+ remove_foreign_key_if_exists :experiment_subjects, :experiments, name: 'fk_rails_ede5754774'
+ end
+
+ with_lock_retries do
+ remove_foreign_key_if_exists :experiment_subjects, :projects, name: 'fk_ccc28f8ceb'
+ end
+
+ with_lock_retries do
+ remove_foreign_key_if_exists :experiment_subjects, :namespaces, name: 'fk_842649f2f5'
+ end
+ end
+
+ def down
+ add_concurrent_foreign_key :experiment_subjects,
+ :users, column: :user_id, name: 'fk_dfc3e211d4', on_delete: :cascade
+ add_concurrent_foreign_key :experiment_subjects,
+ :experiments, column: :experiment_id, name: 'fk_rails_ede5754774', on_delete: :cascade
+ add_concurrent_foreign_key :experiment_subjects,
+ :projects, column: :project_id, name: 'fk_ccc28f8ceb', on_delete: :cascade
+ add_concurrent_foreign_key :experiment_subjects,
+ :namespaces, column: :namespace_id, name: 'fk_842649f2f5', on_delete: :cascade
+ end
+end
diff --git a/db/post_migrate/20221114142602_drop_experiment_subjects_table.rb b/db/post_migrate/20221114142602_drop_experiment_subjects_table.rb
new file mode 100644
index 00000000000..371f214de6d
--- /dev/null
+++ b/db/post_migrate/20221114142602_drop_experiment_subjects_table.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+class DropExperimentSubjectsTable < Gitlab::Database::Migration[2.0]
+ def up
+ drop_table :experiment_subjects, if_exists: true
+ end
+
+ def down
+ unless table_exists?(:experiment_subjects)
+ create_table :experiment_subjects do |t| # rubocop:disable Migration/SchemaAdditionMethodsNoPost
+ t.bigint :experiment_id, null: false
+ t.bigint :user_id
+ t.bigint :project_id
+ t.integer :variant, limit: 2, null: false, default: 0
+ t.timestamps_with_timezone null: false
+ t.datetime_with_timezone :converted_at
+ t.jsonb :context, null: false, default: {}
+ t.bigint :namespace_id
+
+ t.index :experiment_id
+ t.index :namespace_id
+ t.index :project_id
+ t.index :user_id
+ end
+ end
+
+ # Require exactly one of user_id, group_id, or project_id to be NOT NULL
+ execute <<-SQL
+ ALTER TABLE experiment_subjects ADD CONSTRAINT check_f6411bc4b5 CHECK (num_nonnulls(user_id, namespace_id, project_id) = 1);
+ SQL
+ end
+end
diff --git a/db/post_migrate/20221114142616_drop_experiments_table.rb b/db/post_migrate/20221114142616_drop_experiments_table.rb
new file mode 100644
index 00000000000..da6c1122494
--- /dev/null
+++ b/db/post_migrate/20221114142616_drop_experiments_table.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class DropExperimentsTable < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ drop_table :experiments, if_exists: true
+ end
+
+ def down
+ unless table_exists?(:experiments)
+ create_table :experiments do |t| # rubocop:disable Migration/SchemaAdditionMethodsNoPost
+ t.text :name, null: false
+
+ t.index :name, unique: true
+ end
+ end
+
+ add_text_limit :experiments, :name, 255
+ end
+end
diff --git a/db/post_migrate/20221115120602_add_index_for_issues_health_status_ordering.rb b/db/post_migrate/20221115120602_add_index_for_issues_health_status_ordering.rb
new file mode 100644
index 00000000000..d7d861387fd
--- /dev/null
+++ b/db/post_migrate/20221115120602_add_index_for_issues_health_status_ordering.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class AddIndexForIssuesHealthStatusOrdering < Gitlab::Database::Migration[2.0]
+ INDEX_NAME_DESC = 'index_on_issues_health_status_desc_order'
+ INDEX_NAME_ASC = 'index_on_issues_health_status_asc_order'
+
+ def up
+ prepare_async_index :issues,
+ [:project_id, :health_status, :id, :state_id, :issue_type],
+ order: { health_status: 'DESC NULLS LAST', id: :desc },
+ name: INDEX_NAME_DESC
+
+ prepare_async_index :issues,
+ [:project_id, :health_status, :id, :state_id, :issue_type],
+ order: { health_status: 'ASC NULLS LAST', id: :desc },
+ name: INDEX_NAME_ASC
+ end
+
+ def down
+ unprepare_async_index :issues, INDEX_NAME_DESC
+ unprepare_async_index :issues, INDEX_NAME_ASC
+ end
+end
diff --git a/db/post_migrate/20221115173607_ensure_work_item_type_backfill_migration_finished.rb b/db/post_migrate/20221115173607_ensure_work_item_type_backfill_migration_finished.rb
new file mode 100644
index 00000000000..2cec1919e82
--- /dev/null
+++ b/db/post_migrate/20221115173607_ensure_work_item_type_backfill_migration_finished.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+class EnsureWorkItemTypeBackfillMigrationFinished < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ MIGRATION = 'BackfillWorkItemTypeIdForIssues'
+
+ class MigrationWorkItemType < MigrationRecord
+ self.table_name = 'work_item_types'
+
+ def self.id_by_type(types)
+ where(namespace_id: nil, base_type: types).pluck(:base_type, :id).to_h
+ end
+ end
+
+ def up
+ # more types were added to the types table after the backfill run
+ # so we cannot fetch all from the DB but only those that were backfilled
+ relevant_types = {
+ issue: 0,
+ incident: 1,
+ test_case: 2,
+ requirement: 3,
+ task: 4
+ }
+
+ MigrationWorkItemType.id_by_type(relevant_types.values).each do |base_type, type_id|
+ ensure_batched_background_migration_is_finished(
+ job_class_name: MIGRATION,
+ table_name: :issues,
+ column_name: :id,
+ job_arguments: [base_type, type_id]
+ )
+ end
+ end
+
+ def down
+ # noop
+ end
+end
diff --git a/db/post_migrate/20221115184525_remove_namespaces_tmp_project_id_column.rb b/db/post_migrate/20221115184525_remove_namespaces_tmp_project_id_column.rb
new file mode 100644
index 00000000000..01424f8113f
--- /dev/null
+++ b/db/post_migrate/20221115184525_remove_namespaces_tmp_project_id_column.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class RemoveNamespacesTmpProjectIdColumn < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'tmp_index_on_tmp_project_id_on_namespaces'
+
+ def up
+ with_lock_retries do
+ remove_column :namespaces, :tmp_project_id if column_exists?(:namespaces, :tmp_project_id)
+ end
+ end
+
+ def down
+ unless column_exists?(:namespaces, :tmp_project_id)
+ with_lock_retries do
+ # rubocop:disable Migration/SchemaAdditionMethodsNoPost, Migration/AddColumnsToWideTables
+ add_column :namespaces, :tmp_project_id, :integer
+ # rubocop:enable Migration/SchemaAdditionMethodsNoPost, Migration/AddColumnsToWideTables
+ end
+ end
+
+ add_concurrent_foreign_key :namespaces, :projects, column: :tmp_project_id
+
+ add_concurrent_index :namespaces, :tmp_project_id, name: INDEX_NAME, unique: true
+ end
+end
diff --git a/db/post_migrate/20221116105434_remove_index_project_settings_on_legacy_open_source_license_available.rb b/db/post_migrate/20221116105434_remove_index_project_settings_on_legacy_open_source_license_available.rb
new file mode 100644
index 00000000000..363c9b6e572
--- /dev/null
+++ b/db/post_migrate/20221116105434_remove_index_project_settings_on_legacy_open_source_license_available.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class RemoveIndexProjectSettingsOnLegacyOpenSourceLicenseAvailable < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_project_settings_on_legacy_open_source_license_available'
+
+ def up
+ remove_concurrent_index_by_name(:project_settings, INDEX_NAME)
+ end
+
+ def down
+ add_concurrent_index :project_settings,
+ %i[legacy_open_source_license_available],
+ where: "legacy_open_source_license_available = TRUE",
+ name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221116143854_add_okr_hierarchy_restrictions.rb b/db/post_migrate/20221116143854_add_okr_hierarchy_restrictions.rb
new file mode 100644
index 00000000000..658ce0287f8
--- /dev/null
+++ b/db/post_migrate/20221116143854_add_okr_hierarchy_restrictions.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+class AddOkrHierarchyRestrictions < Gitlab::Database::Migration[2.0]
+ class WorkItemType < MigrationRecord
+ self.table_name = 'work_item_types'
+ end
+
+ class HierarchyRestriction < MigrationRecord
+ self.table_name = 'work_item_hierarchy_restrictions'
+ end
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+ disable_ddl_transaction!
+
+ def up
+ objective = WorkItemType.find_by_name_and_namespace_id('Objective', nil)
+ key_result = WorkItemType.find_by_name_and_namespace_id('Key Result', nil)
+ issue = WorkItemType.find_by_name_and_namespace_id('Issue', nil)
+ task = WorkItemType.find_by_name_and_namespace_id('Task', nil)
+ incident = WorkItemType.find_by_name_and_namespace_id('Incident', nil)
+
+ # work item default types should be filled, if this is not the case
+ # then restrictions will be created together with work item types
+ unless objective && key_result && issue && task && incident
+ Gitlab::AppLogger.warn('default types are missing, not adding restrictions')
+
+ return
+ end
+
+ restrictions = [
+ { parent_type_id: objective.id, child_type_id: objective.id, maximum_depth: 9 },
+ { parent_type_id: objective.id, child_type_id: key_result.id, maximum_depth: 1 },
+ { parent_type_id: issue.id, child_type_id: task.id, maximum_depth: 1 },
+ { parent_type_id: incident.id, child_type_id: task.id, maximum_depth: 1 }
+ ]
+
+ HierarchyRestriction.upsert_all(
+ restrictions,
+ unique_by: :index_work_item_hierarchy_restrictions_on_parent_and_child
+ )
+ end
+
+ def down
+ # so far restrictions table was empty so we can delete all records when
+ # migrating down
+ HierarchyRestriction.delete_all
+ end
+end
diff --git a/db/post_migrate/20221117103015_add_async_index_author_id_created_at_on_merge_requests.rb b/db/post_migrate/20221117103015_add_async_index_author_id_created_at_on_merge_requests.rb
new file mode 100644
index 00000000000..a45fdcccc27
--- /dev/null
+++ b/db/post_migrate/20221117103015_add_async_index_author_id_created_at_on_merge_requests.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class AddAsyncIndexAuthorIdCreatedAtOnMergeRequests < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = 'index_merge_requests_on_author_id_and_created_at'
+
+ def up
+ prepare_async_index :merge_requests, %i[author_id created_at], name: INDEX_NAME
+ end
+
+ def down
+ unprepare_async_index_by_name :merge_requests, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221117135032_remove_clusters_applications_job_instances.rb b/db/post_migrate/20221117135032_remove_clusters_applications_job_instances.rb
new file mode 100644
index 00000000000..1bc80271ff7
--- /dev/null
+++ b/db/post_migrate/20221117135032_remove_clusters_applications_job_instances.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+class RemoveClustersApplicationsJobInstances < Gitlab::Database::Migration[2.0]
+ DEPRECATED_JOB_CLASSES = %w[
+ ClusterConfigureIstioWorker
+ ClusterInstallAppWorker
+ ClusterPatchAppWorker
+ ClusterUpdateAppWorker
+ ClusterUpgradeAppWorker
+ ClusterWaitForAppInstallationWorker
+ ClusterWaitForAppUpdateWorker
+ ClusterWaitForIngressIpAddressWorker
+ ]
+
+ def up
+ sidekiq_remove_jobs(job_klasses: DEPRECATED_JOB_CLASSES)
+ end
+
+ def down
+ # no-op Why: This migration removes any instances of deprecated job classes
+ # from expected queues via the sidekiq_queue_length method. Once the job
+ # class instances are removed, they cannot be added back. These job classes
+ # are deprecated and previous MRs have already no-op'd their perform
+ # methods to further increase confidence that removal is OK.
+ end
+end
diff --git a/db/post_migrate/20221117153015_add_index_merge_request_id_created_at_on_scan_finding_approval_merge_request_rules.rb b/db/post_migrate/20221117153015_add_index_merge_request_id_created_at_on_scan_finding_approval_merge_request_rules.rb
new file mode 100644
index 00000000000..ecd3a8be02e
--- /dev/null
+++ b/db/post_migrate/20221117153015_add_index_merge_request_id_created_at_on_scan_finding_approval_merge_request_rules.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexMergeRequestIdCreatedAtOnScanFindingApprovalMergeRequestRules < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = 'scan_finding_approval_mr_rule_index_mr_id_and_created_at'
+ SCAN_FINDING_REPORT_TYPE = 4
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :approval_merge_request_rules, %i[merge_request_id created_at],
+ where: "report_type = #{SCAN_FINDING_REPORT_TYPE}", name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :approval_merge_request_rules, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221118103152_finalize_issues_namespace_id_backfilling.rb b/db/post_migrate/20221118103152_finalize_issues_namespace_id_backfilling.rb
new file mode 100644
index 00000000000..c0a95b3e348
--- /dev/null
+++ b/db/post_migrate/20221118103152_finalize_issues_namespace_id_backfilling.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class FinalizeIssuesNamespaceIdBackfilling < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ MIGRATION = 'BackfillProjectNamespaceOnIssues'
+
+ def up
+ ensure_batched_background_migration_is_finished(
+ job_class_name: MIGRATION,
+ table_name: :issues,
+ column_name: :id,
+ job_arguments: []
+ )
+ end
+
+ def down
+ # noop
+ end
+end
diff --git a/db/post_migrate/20221118103352_add_cascade_delete_fk_on_issues_namespace_id.rb b/db/post_migrate/20221118103352_add_cascade_delete_fk_on_issues_namespace_id.rb
new file mode 100644
index 00000000000..094ac3abe0f
--- /dev/null
+++ b/db/post_migrate/20221118103352_add_cascade_delete_fk_on_issues_namespace_id.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+class AddCascadeDeleteFkOnIssuesNamespaceId < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ TARGET_COLUMN = :namespace_id
+
+ def up
+ # add the new FK before removing the old one
+ add_concurrent_foreign_key(
+ :issues,
+ :namespaces,
+ column: TARGET_COLUMN,
+ name: fk_name("#{TARGET_COLUMN}_new"),
+ on_delete: :cascade
+ )
+
+ with_lock_retries do
+ remove_foreign_key_if_exists(:issues, column: TARGET_COLUMN, name: fk_name(TARGET_COLUMN))
+ end
+ end
+
+ def down
+ add_concurrent_foreign_key(
+ :issues,
+ :namespaces,
+ column: TARGET_COLUMN,
+ name: fk_name(TARGET_COLUMN),
+ on_delete: :nullify
+ )
+
+ with_lock_retries do
+ remove_foreign_key_if_exists(:issues, column: TARGET_COLUMN, name: fk_name("#{TARGET_COLUMN}_new"))
+ end
+ end
+
+ def fk_name(column_name)
+ # generate a FK name
+ concurrent_foreign_key_name(:issues, column_name)
+ end
+end
diff --git a/db/post_migrate/20221118103752_add_not_null_contraint_to_issues_namespace_id.rb b/db/post_migrate/20221118103752_add_not_null_contraint_to_issues_namespace_id.rb
new file mode 100644
index 00000000000..22a69c2bac0
--- /dev/null
+++ b/db/post_migrate/20221118103752_add_not_null_contraint_to_issues_namespace_id.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class AddNotNullContraintToIssuesNamespaceId < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ add_not_null_constraint :issues, :namespace_id, validate: false
+ end
+
+ def down
+ remove_not_null_constraint :issues, :namespace_id
+ end
+end
diff --git a/db/post_migrate/20221118104752_validate_not_null_contraint_to_issues_namespace_id.rb b/db/post_migrate/20221118104752_validate_not_null_contraint_to_issues_namespace_id.rb
new file mode 100644
index 00000000000..f7aad9d3606
--- /dev/null
+++ b/db/post_migrate/20221118104752_validate_not_null_contraint_to_issues_namespace_id.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class ValidateNotNullContraintToIssuesNamespaceId < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ validate_not_null_constraint :issues, :namespace_id
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20221121000127_index_security_scans_on_created_at_and_id_for_non_purged_records.rb b/db/post_migrate/20221121000127_index_security_scans_on_created_at_and_id_for_non_purged_records.rb
new file mode 100644
index 00000000000..0085005166f
--- /dev/null
+++ b/db/post_migrate/20221121000127_index_security_scans_on_created_at_and_id_for_non_purged_records.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class IndexSecurityScansOnCreatedAtAndIdForNonPurgedRecords < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = :index_security_scans_for_non_purged_records
+ PURGED_STATE = 6
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :security_scans, %w[created_at id], where: "status != #{PURGED_STATE}", name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :security_scans, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221121000451_drop_index_security_scans_on_id_for_non_purged_records.rb b/db/post_migrate/20221121000451_drop_index_security_scans_on_id_for_non_purged_records.rb
new file mode 100644
index 00000000000..06e614101e0
--- /dev/null
+++ b/db/post_migrate/20221121000451_drop_index_security_scans_on_id_for_non_purged_records.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class DropIndexSecurityScansOnIdForNonPurgedRecords < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = :index_security_scans_on_id_for_non_purged_records
+ PURGED_STATE = 6
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name :security_scans, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :security_scans, :id, where: "status != #{PURGED_STATE}", name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221121152048_remove_unused_feedback_migration_index.rb b/db/post_migrate/20221121152048_remove_unused_feedback_migration_index.rb
new file mode 100644
index 00000000000..b5dbafccd3a
--- /dev/null
+++ b/db/post_migrate/20221121152048_remove_unused_feedback_migration_index.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class RemoveUnusedFeedbackMigrationIndex < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = "tmp_idx_for_vulnerability_feedback_migration"
+ WHERE_CLAUSE = "migrated_to_state_transition = false AND feedback_type = 0"
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name(
+ :vulnerability_feedback,
+ INDEX_NAME
+ )
+ end
+
+ def down
+ add_concurrent_index(
+ :vulnerability_feedback,
+ %i[migrated_to_state_transition feedback_type],
+ where: WHERE_CLAUSE,
+ name: INDEX_NAME
+ )
+ end
+end
diff --git a/db/post_migrate/20221121152515_add_supporting_index_for_vulnerabilities_feedback_migration2.rb b/db/post_migrate/20221121152515_add_supporting_index_for_vulnerabilities_feedback_migration2.rb
new file mode 100644
index 00000000000..8c55f2da957
--- /dev/null
+++ b/db/post_migrate/20221121152515_add_supporting_index_for_vulnerabilities_feedback_migration2.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class AddSupportingIndexForVulnerabilitiesFeedbackMigration2 < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = "tmp_idx_for_vulnerability_feedback_migration"
+ WHERE_CLAUSE = "migrated_to_state_transition = false AND feedback_type = 0"
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index(
+ :vulnerability_feedback,
+ :id,
+ where: WHERE_CLAUSE,
+ name: INDEX_NAME
+ )
+ end
+
+ def down
+ remove_concurrent_index_by_name(
+ :vulnerability_feedback,
+ INDEX_NAME
+ )
+ end
+end
diff --git a/db/post_migrate/20221121155850_change_vulnerabilities_state_transitions_comment_limit.rb b/db/post_migrate/20221121155850_change_vulnerabilities_state_transitions_comment_limit.rb
new file mode 100644
index 00000000000..b75216ee413
--- /dev/null
+++ b/db/post_migrate/20221121155850_change_vulnerabilities_state_transitions_comment_limit.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class ChangeVulnerabilitiesStateTransitionsCommentLimit < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ add_text_limit(
+ :vulnerability_state_transitions,
+ :comment,
+ 50_000,
+ constraint_name: check_constraint_name(:vulnerability_state_transitions, :comment, 'max_length_50000')
+ )
+ remove_text_limit(
+ :vulnerability_state_transitions,
+ :comment,
+ constraint_name: 'check_fca4a7ca39'
+ )
+ end
+
+ def down
+ # no-op: this can fail if records with length > 255 (previous limit) show up
+ end
+end
diff --git a/db/post_migrate/20221121180138_drop_index_on_vulnerabilities_state_case_id.rb b/db/post_migrate/20221121180138_drop_index_on_vulnerabilities_state_case_id.rb
new file mode 100644
index 00000000000..87f1e5d3ba2
--- /dev/null
+++ b/db/post_migrate/20221121180138_drop_index_on_vulnerabilities_state_case_id.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class DropIndexOnVulnerabilitiesStateCaseId < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = "index_vulnerabilities_on_state_case_id"
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name(
+ :vulnerabilities,
+ INDEX_NAME
+ )
+ end
+
+ def down
+ execute <<~SQL
+ CREATE INDEX CONCURRENTLY index_vulnerabilities_on_state_case_id ON vulnerabilities
+ USING btree (array_position(ARRAY[(1)::smallint, (4)::smallint, (3)::smallint, (2)::smallint], state), id DESC);
+ SQL
+ end
+end
diff --git a/db/post_migrate/20221121181627_drop_index_on_vulnerabilities_state_case_id_desc.rb b/db/post_migrate/20221121181627_drop_index_on_vulnerabilities_state_case_id_desc.rb
new file mode 100644
index 00000000000..712343bc7b0
--- /dev/null
+++ b/db/post_migrate/20221121181627_drop_index_on_vulnerabilities_state_case_id_desc.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class DropIndexOnVulnerabilitiesStateCaseIdDesc < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = "index_vulnerabilities_on_state_case_id_desc"
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name(
+ :vulnerabilities,
+ INDEX_NAME
+ )
+ end
+
+ def down
+ execute <<~SQL
+ CREATE INDEX CONCURRENTLY index_vulnerabilities_on_state_case_id_desc ON vulnerabilities
+ USING btree (array_position(ARRAY[(1)::smallint, (4)::smallint, (3)::smallint, (2)::smallint], state) DESC, id DESC);
+ SQL
+ end
+end
diff --git a/db/post_migrate/20221121184931_validate_not_null_contraint_on_issues_work_item_type_id.rb b/db/post_migrate/20221121184931_validate_not_null_contraint_on_issues_work_item_type_id.rb
new file mode 100644
index 00000000000..be09f2ebe3a
--- /dev/null
+++ b/db/post_migrate/20221121184931_validate_not_null_contraint_on_issues_work_item_type_id.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class ValidateNotNullContraintOnIssuesWorkItemTypeId < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ add_not_null_constraint :issues, :work_item_type_id, validate: true
+ end
+
+ def down
+ remove_not_null_constraint :issues, :work_item_type_id
+ end
+end
diff --git a/db/post_migrate/20221122063922_remove_issue_title_trigram_index.rb b/db/post_migrate/20221122063922_remove_issue_title_trigram_index.rb
new file mode 100644
index 00000000000..80a2020a364
--- /dev/null
+++ b/db/post_migrate/20221122063922_remove_issue_title_trigram_index.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class RemoveIssueTitleTrigramIndex < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_issues_on_title_trigram'
+
+ def up
+ remove_concurrent_index_by_name :issues, name: INDEX_NAME
+ end
+
+ def down
+ disable_statement_timeout do
+ execute <<-SQL
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS #{INDEX_NAME} ON issues
+ USING gin (title gin_trgm_ops) WITH (fastupdate='false')
+ SQL
+ end
+ end
+end
diff --git a/db/post_migrate/20221122064537_remove_issue_description_trigram_index.rb b/db/post_migrate/20221122064537_remove_issue_description_trigram_index.rb
new file mode 100644
index 00000000000..c6d4f62de3e
--- /dev/null
+++ b/db/post_migrate/20221122064537_remove_issue_description_trigram_index.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class RemoveIssueDescriptionTrigramIndex < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_issues_on_description_trigram'
+
+ def up
+ remove_concurrent_index_by_name :issues, name: INDEX_NAME
+ end
+
+ def down
+ disable_statement_timeout do
+ execute <<-SQL
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS #{INDEX_NAME} ON issues
+ USING gin (description gin_trgm_ops) WITH (fastupdate='false')
+ SQL
+ end
+ end
+end
diff --git a/db/post_migrate/20221122132812_schedule_prune_stale_project_export_jobs.rb b/db/post_migrate/20221122132812_schedule_prune_stale_project_export_jobs.rb
new file mode 100644
index 00000000000..871224ea18e
--- /dev/null
+++ b/db/post_migrate/20221122132812_schedule_prune_stale_project_export_jobs.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class SchedulePruneStaleProjectExportJobs < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ MIGRATION = 'PruneStaleProjectExportJobs'
+ DELAY_INTERVAL = 2.minutes
+
+ def up
+ queue_batched_background_migration(
+ MIGRATION,
+ :project_export_jobs,
+ :id,
+ job_interval: DELAY_INTERVAL
+ )
+ end
+
+ def down
+ delete_batched_background_migration(MIGRATION, :project_export_jobs, :id, [])
+ end
+end
diff --git a/db/post_migrate/20221122155149_add_index_for_paths_on_non_projects.rb b/db/post_migrate/20221122155149_add_index_for_paths_on_non_projects.rb
new file mode 100644
index 00000000000..e9a90844550
--- /dev/null
+++ b/db/post_migrate/20221122155149_add_index_for_paths_on_non_projects.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddIndexForPathsOnNonProjects < Gitlab::Database::Migration[2.0]
+ TABLE_NAME = 'namespaces'
+ INDEX_NAME = 'index_namespaces_on_path_for_top_level_non_projects'
+ COLUMN = "(lower(path::text))"
+ CONDITIONS = "(parent_id IS NULL AND type::text <> 'Project'::text)"
+
+ def up
+ prepare_async_index TABLE_NAME, COLUMN, name: INDEX_NAME, where: CONDITIONS
+ end
+
+ def down
+ unprepare_async_index TABLE_NAME, COLUMN, name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221123133054_queue_reset_status_on_container_repositories.rb b/db/post_migrate/20221123133054_queue_reset_status_on_container_repositories.rb
new file mode 100644
index 00000000000..2d482e0b83c
--- /dev/null
+++ b/db/post_migrate/20221123133054_queue_reset_status_on_container_repositories.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class QueueResetStatusOnContainerRepositories < Gitlab::Database::Migration[2.0]
+ MIGRATION = 'ResetStatusOnContainerRepositories'
+ DELAY_INTERVAL = 2.minutes
+ BATCH_SIZE = 50
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ return unless ::Gitlab.config.registry.enabled
+
+ queue_batched_background_migration(
+ MIGRATION,
+ :container_repositories,
+ :id,
+ job_interval: DELAY_INTERVAL,
+ sub_batch_size: BATCH_SIZE
+ )
+ end
+
+ def down
+ delete_batched_background_migration(MIGRATION, :container_repositories, :id, [])
+ end
+end
diff --git a/db/post_migrate/20221124153602_add_supporting_index_for_vulnerabilities_feedback_comment_proccessing.rb b/db/post_migrate/20221124153602_add_supporting_index_for_vulnerabilities_feedback_comment_proccessing.rb
new file mode 100644
index 00000000000..71f48e22b52
--- /dev/null
+++ b/db/post_migrate/20221124153602_add_supporting_index_for_vulnerabilities_feedback_comment_proccessing.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class AddSupportingIndexForVulnerabilitiesFeedbackCommentProccessing < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = "tmp_idx_for_feedback_comment_processing"
+ WHERE_CLAUSE = "char_length(comment) > 50000"
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index(
+ :vulnerability_feedback,
+ :id,
+ where: WHERE_CLAUSE,
+ name: INDEX_NAME
+ )
+ end
+
+ def down
+ remove_concurrent_index_by_name(
+ :vulnerability_feedback,
+ INDEX_NAME
+ )
+ end
+end
diff --git a/db/post_migrate/20221125222221_add_metrics_index_to_authentication_events.rb b/db/post_migrate/20221125222221_add_metrics_index_to_authentication_events.rb
new file mode 100644
index 00000000000..2d3181dea67
--- /dev/null
+++ b/db/post_migrate/20221125222221_add_metrics_index_to_authentication_events.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddMetricsIndexToAuthenticationEvents < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = 'index_successful_authentication_events_for_metrics'
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :authentication_events,
+ %i[user_id provider created_at],
+ where: "result = 1",
+ name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :authentication_events, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221125222341_remove_result_index_from_authentication_events.rb b/db/post_migrate/20221125222341_remove_result_index_from_authentication_events.rb
new file mode 100644
index 00000000000..97fb4b320d1
--- /dev/null
+++ b/db/post_migrate/20221125222341_remove_result_index_from_authentication_events.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class RemoveResultIndexFromAuthenticationEvents < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_authentication_events_on_provider_user_id_created_at'
+
+ def up
+ remove_concurrent_index_by_name :authentication_events, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :authentication_events,
+ [:provider, :user_id, :created_at],
+ where: 'result = 1',
+ name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221128120634_schedule_fixing_security_scan_statuses.rb b/db/post_migrate/20221128120634_schedule_fixing_security_scan_statuses.rb
new file mode 100644
index 00000000000..1cf4a33e09f
--- /dev/null
+++ b/db/post_migrate/20221128120634_schedule_fixing_security_scan_statuses.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+class ScheduleFixingSecurityScanStatuses < Gitlab::Database::Migration[2.0]
+ MIGRATION = 'FixSecurityScanStatuses'
+ TABLE_NAME = :security_scans
+ BATCH_COLUMN = :id
+ DELAY_INTERVAL = 2.minutes
+ BATCH_SIZE = 10_000
+ MAX_BATCH_SIZE = 50_000
+ SUB_BATCH_SIZE = 100
+
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ class SecurityScan < MigrationRecord
+ def self.start_migration_from
+ sort_order = Arel::Nodes::SqlLiteral.new("date(timezone('UTC'::text, created_at)) ASC, id ASC")
+
+ where("date(timezone('UTC'::text, created_at)) > ?", 90.days.ago).order(sort_order).first&.id
+ end
+ end
+
+ def up
+ # Only the SaaS application is affected
+ return unless Gitlab.dev_or_test_env? || Gitlab.com?
+
+ batch_min_value = SecurityScan.start_migration_from
+
+ return unless batch_min_value # It is possible that some users don't have corrupted records
+
+ queue_batched_background_migration(
+ MIGRATION,
+ TABLE_NAME,
+ BATCH_COLUMN,
+ job_interval: DELAY_INTERVAL,
+ batch_size: BATCH_SIZE,
+ max_batch_size: MAX_BATCH_SIZE,
+ sub_batch_size: SUB_BATCH_SIZE,
+ batch_min_value: batch_min_value
+ )
+ end
+
+ def down
+ delete_batched_background_migration(
+ MIGRATION,
+ TABLE_NAME,
+ BATCH_COLUMN,
+ []
+ )
+ end
+end
diff --git a/db/post_migrate/20221128220043_drop_temp_work_item_type_id_backfill_index.rb b/db/post_migrate/20221128220043_drop_temp_work_item_type_id_backfill_index.rb
new file mode 100644
index 00000000000..97fb1202e6e
--- /dev/null
+++ b/db/post_migrate/20221128220043_drop_temp_work_item_type_id_backfill_index.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class DropTempWorkItemTypeIdBackfillIndex < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'tmp_index_issues_on_issue_type_and_id'
+
+ def up
+ remove_concurrent_index_by_name :issues, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :issues, [:issue_type, :id], name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221128222417_add_back_issues_work_item_type_id_index.rb b/db/post_migrate/20221128222417_add_back_issues_work_item_type_id_index.rb
new file mode 100644
index 00000000000..3ee6f0a6179
--- /dev/null
+++ b/db/post_migrate/20221128222417_add_back_issues_work_item_type_id_index.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddBackIssuesWorkItemTypeIdIndex < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_issues_on_work_item_type_id'
+
+ def up
+ prepare_async_index :issues, :work_item_type_id, name: INDEX_NAME
+ end
+
+ def down
+ unprepare_async_index :issues, :work_item_type_id, name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221129124240_remove_flowdock_integration_records.rb b/db/post_migrate/20221129124240_remove_flowdock_integration_records.rb
new file mode 100644
index 00000000000..6390ed0d53b
--- /dev/null
+++ b/db/post_migrate/20221129124240_remove_flowdock_integration_records.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class RemoveFlowdockIntegrationRecords < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ class Integration < MigrationRecord
+ include EachBatch
+
+ self.table_name = 'integrations'
+ end
+
+ def up
+ Integration.each_batch(of: 1000, column: :id) do |relation|
+ relation.delete_by(type_new: 'Integrations::Flowdock')
+ end
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20221130192239_fix_approval_project_rules_without_protected_branches.rb b/db/post_migrate/20221130192239_fix_approval_project_rules_without_protected_branches.rb
new file mode 100644
index 00000000000..9cfe958ff60
--- /dev/null
+++ b/db/post_migrate/20221130192239_fix_approval_project_rules_without_protected_branches.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class FixApprovalProjectRulesWithoutProtectedBranches < Gitlab::Database::Migration[2.0]
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ BATCH_SIZE = 1000
+ SUB_BATCH_SIZE = 500
+ MIGRATION = 'FixApprovalProjectRulesWithoutProtectedBranches'
+ INTERVAL = 2.minutes
+
+ def up
+ return unless Gitlab.ee?
+
+ queue_batched_background_migration(
+ MIGRATION,
+ :approval_project_rules,
+ :id,
+ job_interval: INTERVAL,
+ batch_size: BATCH_SIZE,
+ sub_batch_size: SUB_BATCH_SIZE
+ )
+ end
+
+ def down
+ delete_batched_background_migration(MIGRATION, :approval_project_rules, :id, [])
+ end
+end
diff --git a/db/post_migrate/20221202031332_add_index_to_issue_assignees_on_user_id_and_issue_id.rb b/db/post_migrate/20221202031332_add_index_to_issue_assignees_on_user_id_and_issue_id.rb
new file mode 100644
index 00000000000..5c008008218
--- /dev/null
+++ b/db/post_migrate/20221202031332_add_index_to_issue_assignees_on_user_id_and_issue_id.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddIndexToIssueAssigneesOnUserIdAndIssueId < Gitlab::Database::Migration[2.1]
+ INDEX_NAME = "index_issue_assignees_on_user_id_and_issue_id"
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :issue_assignees, [:user_id, :issue_id], name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :issue_assignees, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221202031417_remove_index_to_issue_assignees_on_user_id.rb b/db/post_migrate/20221202031417_remove_index_to_issue_assignees_on_user_id.rb
new file mode 100644
index 00000000000..abdda680098
--- /dev/null
+++ b/db/post_migrate/20221202031417_remove_index_to_issue_assignees_on_user_id.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class RemoveIndexToIssueAssigneesOnUserId < Gitlab::Database::Migration[2.1]
+ INDEX_NAME = "index_issue_assignees_on_user_id"
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name :issue_assignees, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :issue_assignees, [:user_id], name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221202154128_add_pipeline_metadata_name_index.rb b/db/post_migrate/20221202154128_add_pipeline_metadata_name_index.rb
new file mode 100644
index 00000000000..3d1d7fa7e5a
--- /dev/null
+++ b/db/post_migrate/20221202154128_add_pipeline_metadata_name_index.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddPipelineMetadataNameIndex < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_pipeline_metadata_on_pipeline_id_name_lower_text_pattern'
+
+ def up
+ add_concurrent_index :ci_pipeline_metadata, 'pipeline_id, lower(name) text_pattern_ops', name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :ci_pipeline_metadata, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221202154151_remove_pipeline_metadata_pipeline_id_index.rb b/db/post_migrate/20221202154151_remove_pipeline_metadata_pipeline_id_index.rb
new file mode 100644
index 00000000000..1c551f49fa6
--- /dev/null
+++ b/db/post_migrate/20221202154151_remove_pipeline_metadata_pipeline_id_index.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class RemovePipelineMetadataPipelineIdIndex < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_ci_pipeline_metadata_on_pipeline_id_name'
+
+ def up
+ remove_concurrent_index_by_name :ci_pipeline_metadata, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :ci_pipeline_metadata, [:pipeline_id, :name], name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221205134448_set_index_for_issues_health_status_ordering.rb b/db/post_migrate/20221205134448_set_index_for_issues_health_status_ordering.rb
new file mode 100644
index 00000000000..38426c3ba15
--- /dev/null
+++ b/db/post_migrate/20221205134448_set_index_for_issues_health_status_ordering.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class SetIndexForIssuesHealthStatusOrdering < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ INDEX_NAME_DESC = 'index_on_issues_health_status_desc_order'
+ INDEX_NAME_ASC = 'index_on_issues_health_status_asc_order'
+
+ def up
+ add_concurrent_index :issues,
+ [:project_id, :health_status, :id, :state_id, :issue_type],
+ order: { health_status: 'DESC NULLS LAST', id: :desc },
+ name: INDEX_NAME_DESC
+
+ add_concurrent_index :issues,
+ [:project_id, :health_status, :id, :state_id, :issue_type],
+ order: { health_status: 'ASC NULLS LAST', id: :desc },
+ name: INDEX_NAME_ASC
+ end
+
+ def down
+ remove_concurrent_index_by_name :issues, INDEX_NAME_DESC
+ remove_concurrent_index_by_name :issues, INDEX_NAME_ASC
+ end
+end
diff --git a/db/post_migrate/20221205151917_schedule_backfill_environment_tier.rb b/db/post_migrate/20221205151917_schedule_backfill_environment_tier.rb
new file mode 100644
index 00000000000..eb62e50ea65
--- /dev/null
+++ b/db/post_migrate/20221205151917_schedule_backfill_environment_tier.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class ScheduleBackfillEnvironmentTier < Gitlab::Database::Migration[2.0]
+ MIGRATION = 'BackfillEnvironmentTiers'
+ DELAY_INTERVAL = 2.minutes
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ queue_batched_background_migration(
+ MIGRATION,
+ :environments,
+ :id,
+ job_interval: DELAY_INTERVAL
+ )
+ end
+
+ def down
+ delete_batched_background_migration(MIGRATION, :environments, :id, [])
+ end
+end
diff --git a/db/post_migrate/20221205170310_add_index_for_active_members.rb b/db/post_migrate/20221205170310_add_index_for_active_members.rb
new file mode 100644
index 00000000000..c52b8ccfd04
--- /dev/null
+++ b/db/post_migrate/20221205170310_add_index_for_active_members.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddIndexForActiveMembers < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = 'index_members_on_source_state_type_access_level_and_user_id'
+
+ disable_ddl_transaction!
+
+ def up
+ where_clause = 'requested_at is null and invite_token is null'
+
+ add_concurrent_index :members, [:source_id, :source_type, :state, :type, :access_level, :user_id],
+ name: INDEX_NAME, where: where_clause
+ end
+
+ def down
+ remove_concurrent_index_by_name :members, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221206012013_add_index_author_id_created_at_on_merge_requests.rb b/db/post_migrate/20221206012013_add_index_author_id_created_at_on_merge_requests.rb
new file mode 100644
index 00000000000..886d8aed71a
--- /dev/null
+++ b/db/post_migrate/20221206012013_add_index_author_id_created_at_on_merge_requests.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddIndexAuthorIdCreatedAtOnMergeRequests < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = 'index_merge_requests_on_author_id_and_created_at'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :merge_requests, %i[author_id created_at], name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :merge_requests, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221206075631_add_unique_id_partition_id_index_to_ci_build.rb b/db/post_migrate/20221206075631_add_unique_id_partition_id_index_to_ci_build.rb
new file mode 100644
index 00000000000..c3e7a5799d0
--- /dev/null
+++ b/db/post_migrate/20221206075631_add_unique_id_partition_id_index_to_ci_build.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddUniqueIdPartitionIdIndexToCiBuild < Gitlab::Database::Migration[2.1]
+ TABLE_NAME = :ci_builds
+ INDEX_NAME = :index_ci_builds_on_id_partition_id_unique
+ COLUMNS = %i[id partition_id].freeze
+
+ def up
+ prepare_async_index(TABLE_NAME, COLUMNS, unique: true, name: INDEX_NAME)
+ end
+
+ def down
+ unprepare_async_index(TABLE_NAME, COLUMNS, name: INDEX_NAME)
+ end
+end
diff --git a/db/post_migrate/20221206132610_add_unique_token_encrypted_partition_id_index_to_ci_build.rb b/db/post_migrate/20221206132610_add_unique_token_encrypted_partition_id_index_to_ci_build.rb
new file mode 100644
index 00000000000..c330ece1a60
--- /dev/null
+++ b/db/post_migrate/20221206132610_add_unique_token_encrypted_partition_id_index_to_ci_build.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class AddUniqueTokenEncryptedPartitionIdIndexToCiBuild < Gitlab::Database::Migration[2.1]
+ TABLE_NAME = :ci_builds
+ INDEX_NAME = :index_ci_builds_on_token_encrypted_partition_id_unique
+ COLUMNS = %i[token_encrypted partition_id].freeze
+
+ def up
+ prepare_async_index(
+ TABLE_NAME,
+ COLUMNS,
+ where: 'token_encrypted IS NOT NULL',
+ unique: true,
+ name: INDEX_NAME
+ )
+ end
+
+ def down
+ unprepare_async_index(TABLE_NAME, COLUMNS, name: INDEX_NAME)
+ end
+end
diff --git a/db/post_migrate/20221206173132_add_issues_work_item_type_id_index.rb b/db/post_migrate/20221206173132_add_issues_work_item_type_id_index.rb
new file mode 100644
index 00000000000..b50da0e4644
--- /dev/null
+++ b/db/post_migrate/20221206173132_add_issues_work_item_type_id_index.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddIssuesWorkItemTypeIdIndex < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_issues_on_work_item_type_id'
+
+ def up
+ add_concurrent_index :issues, :work_item_type_id, name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :issues, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20221210154044_update_active_billable_users_index.rb b/db/post_migrate/20221210154044_update_active_billable_users_index.rb
new file mode 100644
index 00000000000..9d306eff16b
--- /dev/null
+++ b/db/post_migrate/20221210154044_update_active_billable_users_index.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+class UpdateActiveBillableUsersIndex < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ OLD_INDEX_NAME = 'active_billable_users'
+ NEW_INDEX_NAME = 'index_users_for_active_billable'
+ TABLE_NAME = 'users'
+ COLUMNS = %i[id]
+ OLD_INDEX_FILTER_CONDITION = <<~QUERY
+ ((state)::text = 'active'::text) AND ((user_type IS NULL)
+ OR (user_type = ANY (ARRAY[NULL::integer, 6, 4]))) AND ((user_type IS NULL)
+ OR (user_type <> ALL ('{2,6,1,3,7,8}'::smallint[])))
+ QUERY
+ NEW_INDEX_FILTER_CONDITION = <<~QUERY
+ ((state)::text = 'active'::text) AND ((user_type IS NULL)
+ OR (user_type = ANY (ARRAY[NULL::integer, 6, 4]))) AND ((user_type IS NULL)
+ OR (user_type <> ALL ('{1,2,3,4,5,6,7,8,9,11}'::smallint[])))
+ QUERY
+
+ def up
+ add_concurrent_index(TABLE_NAME, COLUMNS, where: NEW_INDEX_FILTER_CONDITION, name: NEW_INDEX_NAME)
+ remove_concurrent_index_by_name(TABLE_NAME, OLD_INDEX_NAME)
+ end
+
+ def down
+ add_concurrent_index(TABLE_NAME, COLUMNS, where: OLD_INDEX_FILTER_CONDITION, name: OLD_INDEX_NAME)
+ remove_concurrent_index_by_name(TABLE_NAME, NEW_INDEX_NAME)
+ end
+end
diff --git a/db/post_migrate/20221212103743_add_index_id_partition_id_to_ci_build.rb b/db/post_migrate/20221212103743_add_index_id_partition_id_to_ci_build.rb
new file mode 100644
index 00000000000..aab67272a77
--- /dev/null
+++ b/db/post_migrate/20221212103743_add_index_id_partition_id_to_ci_build.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexIdPartitionIdToCiBuild < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ TABLE_NAME = :ci_builds
+ INDEX_NAME = :index_ci_builds_on_id_partition_id_unique
+ COLUMNS = %i[id partition_id].freeze
+
+ def up
+ add_concurrent_index(TABLE_NAME, COLUMNS, unique: true, name: INDEX_NAME)
+ end
+
+ def down
+ remove_concurrent_index_by_name(TABLE_NAME, INDEX_NAME)
+ end
+end
diff --git a/db/post_migrate/20221213064717_change_default_partition_id_on_ci_resources.rb b/db/post_migrate/20221213064717_change_default_partition_id_on_ci_resources.rb
new file mode 100644
index 00000000000..889659cdc2c
--- /dev/null
+++ b/db/post_migrate/20221213064717_change_default_partition_id_on_ci_resources.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class ChangeDefaultPartitionIdOnCiResources < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ def change
+ change_column_default :ci_resources, :partition_id, from: 100, to: nil
+ end
+end
diff --git a/db/schema_migrations/20220824082427 b/db/schema_migrations/20220824082427
new file mode 100644
index 00000000000..1805c956991
--- /dev/null
+++ b/db/schema_migrations/20220824082427
@@ -0,0 +1 @@
+80d083f58cc9b225542c1198c7d9d80fd8202c959c70957400d63d22435999aa \ No newline at end of file
diff --git a/db/schema_migrations/20220908150054 b/db/schema_migrations/20220908150054
new file mode 100644
index 00000000000..125498c3e63
--- /dev/null
+++ b/db/schema_migrations/20220908150054
@@ -0,0 +1 @@
+820ab42535cf8291960e41f26395d4f820c0a464b6b1bbf51955d79a16e900ab \ No newline at end of file
diff --git a/db/schema_migrations/20220920180451 b/db/schema_migrations/20220920180451
new file mode 100644
index 00000000000..a1121731e9d
--- /dev/null
+++ b/db/schema_migrations/20220920180451
@@ -0,0 +1 @@
+3e15307d975890ba62f9617aee32009eac5b66eb02fdd129bb0fcfa8ff4c1ac6 \ No newline at end of file
diff --git a/db/schema_migrations/20221018095434 b/db/schema_migrations/20221018095434
new file mode 100644
index 00000000000..f924caf4553
--- /dev/null
+++ b/db/schema_migrations/20221018095434
@@ -0,0 +1 @@
+0cb708a3cb823676e473bf961aa6920d156598c3c5455b87c89cb1833dfb509c \ No newline at end of file
diff --git a/db/schema_migrations/20221026095133 b/db/schema_migrations/20221026095133
new file mode 100644
index 00000000000..bd5893ed226
--- /dev/null
+++ b/db/schema_migrations/20221026095133
@@ -0,0 +1 @@
+a2a0bc78f8f9012d294d42e90e144a209aa48c0160eedae09a748c1835132ab4 \ No newline at end of file
diff --git a/db/schema_migrations/20221101174816 b/db/schema_migrations/20221101174816
new file mode 100644
index 00000000000..ce1368622a4
--- /dev/null
+++ b/db/schema_migrations/20221101174816
@@ -0,0 +1 @@
+2bd5e8cadc82207b0540d8418e6312cc76868318e1e040fb64fa69dfe3e01cb8 \ No newline at end of file
diff --git a/db/schema_migrations/20221101194416 b/db/schema_migrations/20221101194416
new file mode 100644
index 00000000000..0051072650c
--- /dev/null
+++ b/db/schema_migrations/20221101194416
@@ -0,0 +1 @@
+c4fc7b3ca831b670c504a824cbe07d6c94ddaa5c432d37cb353807c5387ee4e8 \ No newline at end of file
diff --git a/db/schema_migrations/20221101195309 b/db/schema_migrations/20221101195309
new file mode 100644
index 00000000000..54fd0d397b6
--- /dev/null
+++ b/db/schema_migrations/20221101195309
@@ -0,0 +1 @@
+9033c025820c306db295ac4acacc8fa2d99aa78f3883e134829beb8c756eacb1 \ No newline at end of file
diff --git a/db/schema_migrations/20221101195543 b/db/schema_migrations/20221101195543
new file mode 100644
index 00000000000..a1b20ac6274
--- /dev/null
+++ b/db/schema_migrations/20221101195543
@@ -0,0 +1 @@
+18e1da4447efd3c77c6a4baf194eb0cfa787d5ce60e544c6fd8d4ed0818f9082 \ No newline at end of file
diff --git a/db/schema_migrations/20221102150737 b/db/schema_migrations/20221102150737
new file mode 100644
index 00000000000..83e3c1e77ed
--- /dev/null
+++ b/db/schema_migrations/20221102150737
@@ -0,0 +1 @@
+600e0c6bd79850846c38de38f175889cee731b5619dfbd084e1bd4438d13d387 \ No newline at end of file
diff --git a/db/schema_migrations/20221102195642 b/db/schema_migrations/20221102195642
new file mode 100644
index 00000000000..746c62e7f7e
--- /dev/null
+++ b/db/schema_migrations/20221102195642
@@ -0,0 +1 @@
+66a97a441e7be47db9d4dfd49bfe5b600cc2977e581ade98daa923778a142b85 \ No newline at end of file
diff --git a/db/schema_migrations/20221103205317 b/db/schema_migrations/20221103205317
new file mode 100644
index 00000000000..f205ff2db21
--- /dev/null
+++ b/db/schema_migrations/20221103205317
@@ -0,0 +1 @@
+d1d3c4281b79318902e3e26d9104971a4537fd6380ce5f53282073330ab173e6 \ No newline at end of file
diff --git a/db/schema_migrations/20221104115712 b/db/schema_migrations/20221104115712
new file mode 100644
index 00000000000..c3b87b343d9
--- /dev/null
+++ b/db/schema_migrations/20221104115712
@@ -0,0 +1 @@
+61322973112a2450036fbbd6e329105ff16d5dc5efac72c75c152bcf87a84aeb \ No newline at end of file
diff --git a/db/schema_migrations/20221104141647 b/db/schema_migrations/20221104141647
new file mode 100644
index 00000000000..e449f19b8ab
--- /dev/null
+++ b/db/schema_migrations/20221104141647
@@ -0,0 +1 @@
+913154d7bf3d7e663e680d08076da681e963758f0bb2c5b7419914a88df55701 \ No newline at end of file
diff --git a/db/schema_migrations/20221107013943 b/db/schema_migrations/20221107013943
new file mode 100644
index 00000000000..a447e6590c1
--- /dev/null
+++ b/db/schema_migrations/20221107013943
@@ -0,0 +1 @@
+5d3efc9c623a22768e2cbf7aa6282ff2f3fead17cad8537154e4e50485748fbd \ No newline at end of file
diff --git a/db/schema_migrations/20221109160052 b/db/schema_migrations/20221109160052
new file mode 100644
index 00000000000..33d71913115
--- /dev/null
+++ b/db/schema_migrations/20221109160052
@@ -0,0 +1 @@
+4567d064918a67787a8d08a18b2747f2e6c1ef43a4bc1471a4bb2a881c66f49d \ No newline at end of file
diff --git a/db/schema_migrations/20221110080508 b/db/schema_migrations/20221110080508
new file mode 100644
index 00000000000..b1c213d19c8
--- /dev/null
+++ b/db/schema_migrations/20221110080508
@@ -0,0 +1 @@
+9000b90fa232989de2210efd289d0e435d72c955c1a8ae055879a84b42e0bf2a \ No newline at end of file
diff --git a/db/schema_migrations/20221110080636 b/db/schema_migrations/20221110080636
new file mode 100644
index 00000000000..0e93b46f183
--- /dev/null
+++ b/db/schema_migrations/20221110080636
@@ -0,0 +1 @@
+0e3578ebbde1c61610e3bf043b6b841cbbc2e5ac555723d510956bc677d18830 \ No newline at end of file
diff --git a/db/schema_migrations/20221110080748 b/db/schema_migrations/20221110080748
new file mode 100644
index 00000000000..7dca70ba2d8
--- /dev/null
+++ b/db/schema_migrations/20221110080748
@@ -0,0 +1 @@
+9b6d530507f560cfcd4e6f7c9862fe337f83a2b11209c293c35930ae3a2a9337 \ No newline at end of file
diff --git a/db/schema_migrations/20221110080822 b/db/schema_migrations/20221110080822
new file mode 100644
index 00000000000..08cd9263a3a
--- /dev/null
+++ b/db/schema_migrations/20221110080822
@@ -0,0 +1 @@
+30445b0bb4c7e5a92607c94e783c375aa701ab7021f6cdd9a4e586f36f1c8c53 \ No newline at end of file
diff --git a/db/schema_migrations/20221110080913 b/db/schema_migrations/20221110080913
new file mode 100644
index 00000000000..71bb2246107
--- /dev/null
+++ b/db/schema_migrations/20221110080913
@@ -0,0 +1 @@
+1b35ffef693ef3fc468ea993080fcc1090fec43032cde06fd0560d47c806b86c \ No newline at end of file
diff --git a/db/schema_migrations/20221110080956 b/db/schema_migrations/20221110080956
new file mode 100644
index 00000000000..be455f79d03
--- /dev/null
+++ b/db/schema_migrations/20221110080956
@@ -0,0 +1 @@
+014856a7b2e713e5181c79ddc1e0dcaa4696c867224338768274ea0987bc8b7a \ No newline at end of file
diff --git a/db/schema_migrations/20221110081037 b/db/schema_migrations/20221110081037
new file mode 100644
index 00000000000..74b21d07ec0
--- /dev/null
+++ b/db/schema_migrations/20221110081037
@@ -0,0 +1 @@
+d22d5d4288369c275c193a6863174a67b16872fb41d17be63d7698302ee6cdb5 \ No newline at end of file
diff --git a/db/schema_migrations/20221110081115 b/db/schema_migrations/20221110081115
new file mode 100644
index 00000000000..9cf89be9ab7
--- /dev/null
+++ b/db/schema_migrations/20221110081115
@@ -0,0 +1 @@
+d189936c73331c868f31e7d332230b25b2f26aa2983f6b4e5b348816dc5e7050 \ No newline at end of file
diff --git a/db/schema_migrations/20221110081207 b/db/schema_migrations/20221110081207
new file mode 100644
index 00000000000..7347f2ad446
--- /dev/null
+++ b/db/schema_migrations/20221110081207
@@ -0,0 +1 @@
+9c4a57679d2bff30da1b88cca6e81b8d5aaa16adbbb748ae0bfdaba1ba2bbc9e \ No newline at end of file
diff --git a/db/schema_migrations/20221110081348 b/db/schema_migrations/20221110081348
new file mode 100644
index 00000000000..b15c561ea95
--- /dev/null
+++ b/db/schema_migrations/20221110081348
@@ -0,0 +1 @@
+0edc396d8a2c39abb4e49709359a2917703f967ae33258aa2f9dd59dec06b562 \ No newline at end of file
diff --git a/db/schema_migrations/20221110081448 b/db/schema_migrations/20221110081448
new file mode 100644
index 00000000000..5473ccd2098
--- /dev/null
+++ b/db/schema_migrations/20221110081448
@@ -0,0 +1 @@
+77b11688ef2d41d3aba6d7e730b6a50f8baf12eaf6e8d950f239d8c70f413196 \ No newline at end of file
diff --git a/db/schema_migrations/20221110152133 b/db/schema_migrations/20221110152133
new file mode 100644
index 00000000000..d50f57be2b6
--- /dev/null
+++ b/db/schema_migrations/20221110152133
@@ -0,0 +1 @@
+2fcf1b1e5395b89ebf2d9757f0d9f005cc2014946a957127545969ad8472f99b \ No newline at end of file
diff --git a/db/schema_migrations/20221110183103 b/db/schema_migrations/20221110183103
new file mode 100644
index 00000000000..08b3a8823df
--- /dev/null
+++ b/db/schema_migrations/20221110183103
@@ -0,0 +1 @@
+3a8b69f61d48ed02d1015cf63b1dd89fb7206a3d5ce9668126cfdc52048f1e61 \ No newline at end of file
diff --git a/db/schema_migrations/20221110190340 b/db/schema_migrations/20221110190340
new file mode 100644
index 00000000000..253cd2c23a8
--- /dev/null
+++ b/db/schema_migrations/20221110190340
@@ -0,0 +1 @@
+7cc74ddc58ed05ebc2fb2dcbf2a3f1b2c0327bb2e6109666167bcc89683bcd98 \ No newline at end of file
diff --git a/db/schema_migrations/20221111123146 b/db/schema_migrations/20221111123146
new file mode 100644
index 00000000000..176a6ac6bba
--- /dev/null
+++ b/db/schema_migrations/20221111123146
@@ -0,0 +1 @@
+9679ef7921014d7b6123bf33a3df6276ca3a187641487c11d1dad86aa58b59a6 \ No newline at end of file
diff --git a/db/schema_migrations/20221111123147 b/db/schema_migrations/20221111123147
new file mode 100644
index 00000000000..f5c17b7e08f
--- /dev/null
+++ b/db/schema_migrations/20221111123147
@@ -0,0 +1 @@
+43c86e9b4c78f5335a3288c4bd40fbcd5559cc175f3619f5e62e779f8aafa126 \ No newline at end of file
diff --git a/db/schema_migrations/20221111123148 b/db/schema_migrations/20221111123148
new file mode 100644
index 00000000000..d32b1627453
--- /dev/null
+++ b/db/schema_migrations/20221111123148
@@ -0,0 +1 @@
+777d3e757eeec38ee9a29ed2e9f72631d3928d9d449db4327781ad8240ab7922 \ No newline at end of file
diff --git a/db/schema_migrations/20221111135238 b/db/schema_migrations/20221111135238
new file mode 100644
index 00000000000..5a01cca7871
--- /dev/null
+++ b/db/schema_migrations/20221111135238
@@ -0,0 +1 @@
+baf4a11c802eccd1d45bc210c981f0ee2552d7347252caa5558e223271f8a92f \ No newline at end of file
diff --git a/db/schema_migrations/20221111142921 b/db/schema_migrations/20221111142921
new file mode 100644
index 00000000000..269979c1f86
--- /dev/null
+++ b/db/schema_migrations/20221111142921
@@ -0,0 +1 @@
+5d31ed73f99f6f36cba7466ccb999337206a0eca9c29e2ad2f6c28f2154572b4 \ No newline at end of file
diff --git a/db/schema_migrations/20221114131943 b/db/schema_migrations/20221114131943
new file mode 100644
index 00000000000..ce5aa4208b4
--- /dev/null
+++ b/db/schema_migrations/20221114131943
@@ -0,0 +1 @@
+f101fcfae81e560c141c571f1494d63821b28271bda74cc2697cea9895872f8f \ No newline at end of file
diff --git a/db/schema_migrations/20221114142044 b/db/schema_migrations/20221114142044
new file mode 100644
index 00000000000..cd0e0bd8abb
--- /dev/null
+++ b/db/schema_migrations/20221114142044
@@ -0,0 +1 @@
+5df7ccad8b87eb93db804886d93d5cab40ef988f8706bfe9b54c044ea49b78f9 \ No newline at end of file
diff --git a/db/schema_migrations/20221114142602 b/db/schema_migrations/20221114142602
new file mode 100644
index 00000000000..d59022a5ba7
--- /dev/null
+++ b/db/schema_migrations/20221114142602
@@ -0,0 +1 @@
+0ce9bc1eb8164103c13be54e5b94b5d78e64c9a61a0d4c29ea7cbac6655c3d02 \ No newline at end of file
diff --git a/db/schema_migrations/20221114142616 b/db/schema_migrations/20221114142616
new file mode 100644
index 00000000000..ad8e685cb85
--- /dev/null
+++ b/db/schema_migrations/20221114142616
@@ -0,0 +1 @@
+6532501a17c42d41dab53d277624992e8be1dd8597b8c9eda08e3d62d6e1c33a \ No newline at end of file
diff --git a/db/schema_migrations/20221114145103 b/db/schema_migrations/20221114145103
new file mode 100644
index 00000000000..da49d8f76b1
--- /dev/null
+++ b/db/schema_migrations/20221114145103
@@ -0,0 +1 @@
+1621f0ac141f24c15beef34f5f411158c1eb8a89f5022dd426533d705aa859fe \ No newline at end of file
diff --git a/db/schema_migrations/20221114212908 b/db/schema_migrations/20221114212908
new file mode 100644
index 00000000000..cbd453b2cc9
--- /dev/null
+++ b/db/schema_migrations/20221114212908
@@ -0,0 +1 @@
+0a939e4568d4edcdee322a9a4f69dac51e7604a30e79d2eced9e131a7e06937a \ No newline at end of file
diff --git a/db/schema_migrations/20221115085813 b/db/schema_migrations/20221115085813
new file mode 100644
index 00000000000..c2f7c8a7c51
--- /dev/null
+++ b/db/schema_migrations/20221115085813
@@ -0,0 +1 @@
+b6538475a9c8a48e640ae367523b9843573e271e508e3f8fe575abef0a4b64f3 \ No newline at end of file
diff --git a/db/schema_migrations/20221115120602 b/db/schema_migrations/20221115120602
new file mode 100644
index 00000000000..e7d0bfac37b
--- /dev/null
+++ b/db/schema_migrations/20221115120602
@@ -0,0 +1 @@
+793a1e1c80385cf7fe8f2d27af9acc64f46298790c6dc353f5355047500eebb9 \ No newline at end of file
diff --git a/db/schema_migrations/20221115173607 b/db/schema_migrations/20221115173607
new file mode 100644
index 00000000000..1de7aaf5da6
--- /dev/null
+++ b/db/schema_migrations/20221115173607
@@ -0,0 +1 @@
+c3e763e7c801b308cf44cd494104e8c3b37e61fa00b30d777ef97ca310f4823b \ No newline at end of file
diff --git a/db/schema_migrations/20221115184525 b/db/schema_migrations/20221115184525
new file mode 100644
index 00000000000..da6620d281c
--- /dev/null
+++ b/db/schema_migrations/20221115184525
@@ -0,0 +1 @@
+7e181636d6fd40eb13a40f596ab442ea7b795748546b58a33b1475b2d5fcd264 \ No newline at end of file
diff --git a/db/schema_migrations/20221116100056 b/db/schema_migrations/20221116100056
new file mode 100644
index 00000000000..8909a1d9bf2
--- /dev/null
+++ b/db/schema_migrations/20221116100056
@@ -0,0 +1 @@
+20fd10e525180e73a642809143c4e3caf3a58defc1c475389bc2a9b386bad253 \ No newline at end of file
diff --git a/db/schema_migrations/20221116105434 b/db/schema_migrations/20221116105434
new file mode 100644
index 00000000000..105f7e99618
--- /dev/null
+++ b/db/schema_migrations/20221116105434
@@ -0,0 +1 @@
+c354ac7501cc534a0deff46dc9f0bce27f05d9c08cf5fc0883a906c3dbd7c736 \ No newline at end of file
diff --git a/db/schema_migrations/20221116113323 b/db/schema_migrations/20221116113323
new file mode 100644
index 00000000000..1c852aa2ac9
--- /dev/null
+++ b/db/schema_migrations/20221116113323
@@ -0,0 +1 @@
+d8a541a683d7957ddc8446703bfa781609c4180a2c3cbb6098e748e71b6cc9d0 \ No newline at end of file
diff --git a/db/schema_migrations/20221116124821 b/db/schema_migrations/20221116124821
new file mode 100644
index 00000000000..8bea2d3564f
--- /dev/null
+++ b/db/schema_migrations/20221116124821
@@ -0,0 +1 @@
+775519b2a0881608fd6e446b4c4aaff65ed45889b87a6522d0ea314980e5f66c \ No newline at end of file
diff --git a/db/schema_migrations/20221116143854 b/db/schema_migrations/20221116143854
new file mode 100644
index 00000000000..9f0b0815c79
--- /dev/null
+++ b/db/schema_migrations/20221116143854
@@ -0,0 +1 @@
+a6caf06dd18f096219d5ce0752c956ef099a92df71899c1b9164d3a16f6ef0ba \ No newline at end of file
diff --git a/db/schema_migrations/20221116160204 b/db/schema_migrations/20221116160204
new file mode 100644
index 00000000000..3b697bb9108
--- /dev/null
+++ b/db/schema_migrations/20221116160204
@@ -0,0 +1 @@
+09e6935b54925d65dfe11c5aaf7c2b711fee204b817cdaddd6fb4066206721d6 \ No newline at end of file
diff --git a/db/schema_migrations/20221116161126 b/db/schema_migrations/20221116161126
new file mode 100644
index 00000000000..5d65ed55915
--- /dev/null
+++ b/db/schema_migrations/20221116161126
@@ -0,0 +1 @@
+93286f75aec167041985c2cde8ef1fc32447eae4f520c87131b89c28c402675c \ No newline at end of file
diff --git a/db/schema_migrations/20221117103015 b/db/schema_migrations/20221117103015
new file mode 100644
index 00000000000..27244eb22e7
--- /dev/null
+++ b/db/schema_migrations/20221117103015
@@ -0,0 +1 @@
+3e8cb08fd67a748a5ba77b495fb9700ff968af6e45d6fd024a4d2b09ba0c4d39 \ No newline at end of file
diff --git a/db/schema_migrations/20221117135032 b/db/schema_migrations/20221117135032
new file mode 100644
index 00000000000..d9dd4606e4a
--- /dev/null
+++ b/db/schema_migrations/20221117135032
@@ -0,0 +1 @@
+bb15453aa03df0d579ed2c2d38806cc30362e642c5cd8f58ccca29c70f97ea8d \ No newline at end of file
diff --git a/db/schema_migrations/20221117153015 b/db/schema_migrations/20221117153015
new file mode 100644
index 00000000000..438ddfdcfbf
--- /dev/null
+++ b/db/schema_migrations/20221117153015
@@ -0,0 +1 @@
+ce905f8497f63b909fee18cb20f2bfc95c33f09d01df09798ca30cdcd72280dc \ No newline at end of file
diff --git a/db/schema_migrations/20221118103152 b/db/schema_migrations/20221118103152
new file mode 100644
index 00000000000..2a5ae81a7e2
--- /dev/null
+++ b/db/schema_migrations/20221118103152
@@ -0,0 +1 @@
+e7aa8bf64fde9ebc2c027c3aac7ea2b317b9a96fcb3514481b0f27070d335d74 \ No newline at end of file
diff --git a/db/schema_migrations/20221118103352 b/db/schema_migrations/20221118103352
new file mode 100644
index 00000000000..d50fb4354ad
--- /dev/null
+++ b/db/schema_migrations/20221118103352
@@ -0,0 +1 @@
+577d345895cde08e41512266a72aadea953386e0fa57773ef428b03d052c0f63 \ No newline at end of file
diff --git a/db/schema_migrations/20221118103752 b/db/schema_migrations/20221118103752
new file mode 100644
index 00000000000..a6de9bf7a55
--- /dev/null
+++ b/db/schema_migrations/20221118103752
@@ -0,0 +1 @@
+2caac7002aa56d0cd8fb157171a0dd5e0630d06334370154aa574433446220a4 \ No newline at end of file
diff --git a/db/schema_migrations/20221118104752 b/db/schema_migrations/20221118104752
new file mode 100644
index 00000000000..f507d02477a
--- /dev/null
+++ b/db/schema_migrations/20221118104752
@@ -0,0 +1 @@
+2e8e796f4477a027c53ad97b5a582222541f7919bf2ed2f5179fa5bc6a65ec60 \ No newline at end of file
diff --git a/db/schema_migrations/20221121000127 b/db/schema_migrations/20221121000127
new file mode 100644
index 00000000000..de209f0e721
--- /dev/null
+++ b/db/schema_migrations/20221121000127
@@ -0,0 +1 @@
+2b45437b2ec1ed0f1481808fcc9dfb6827d6b46122a8b4b120318399d4e622c0 \ No newline at end of file
diff --git a/db/schema_migrations/20221121000451 b/db/schema_migrations/20221121000451
new file mode 100644
index 00000000000..08a9ebf2248
--- /dev/null
+++ b/db/schema_migrations/20221121000451
@@ -0,0 +1 @@
+93da760ac1c16403bb83255a20f4ff1ff68e0279b4c4aa53a1705e792369f543 \ No newline at end of file
diff --git a/db/schema_migrations/20221121091238 b/db/schema_migrations/20221121091238
new file mode 100644
index 00000000000..d042656a9c2
--- /dev/null
+++ b/db/schema_migrations/20221121091238
@@ -0,0 +1 @@
+065c3eb12275fda5806d5c5674ae95ef99a78752c0417dd97534b6f4e2337c06 \ No newline at end of file
diff --git a/db/schema_migrations/20221121100431 b/db/schema_migrations/20221121100431
new file mode 100644
index 00000000000..c89e74a5c09
--- /dev/null
+++ b/db/schema_migrations/20221121100431
@@ -0,0 +1 @@
+e4d54fe2976b8f38053126a7e25fc26d8c84aca36f219435a7cdf57948d36b94 \ No newline at end of file
diff --git a/db/schema_migrations/20221121152048 b/db/schema_migrations/20221121152048
new file mode 100644
index 00000000000..8d19b1ff54e
--- /dev/null
+++ b/db/schema_migrations/20221121152048
@@ -0,0 +1 @@
+daf3e3b4d3b7b6487542f5cc418b0308bc22da13c0ac6f189ab3fb9352e23898 \ No newline at end of file
diff --git a/db/schema_migrations/20221121152515 b/db/schema_migrations/20221121152515
new file mode 100644
index 00000000000..cb105448807
--- /dev/null
+++ b/db/schema_migrations/20221121152515
@@ -0,0 +1 @@
+23e3d67029b004c63e4c0843ca58556e259c5795075a772043418181335e3349 \ No newline at end of file
diff --git a/db/schema_migrations/20221121155850 b/db/schema_migrations/20221121155850
new file mode 100644
index 00000000000..33c41d1b35e
--- /dev/null
+++ b/db/schema_migrations/20221121155850
@@ -0,0 +1 @@
+f73bd76a9ad54932b1f4b880af225a49089fc6ea782d213a9fc608b3029cddab \ No newline at end of file
diff --git a/db/schema_migrations/20221121180138 b/db/schema_migrations/20221121180138
new file mode 100644
index 00000000000..be8f86feea6
--- /dev/null
+++ b/db/schema_migrations/20221121180138
@@ -0,0 +1 @@
+85eb5cd27485934054a5ee8449b58282b1602e8054941ebebf5c33a169809389 \ No newline at end of file
diff --git a/db/schema_migrations/20221121181627 b/db/schema_migrations/20221121181627
new file mode 100644
index 00000000000..2be29d4fcec
--- /dev/null
+++ b/db/schema_migrations/20221121181627
@@ -0,0 +1 @@
+91c8b8327b502611b47400f229204f3093b87f6dd555c7471a2a827c0ee2d7fe \ No newline at end of file
diff --git a/db/schema_migrations/20221121184931 b/db/schema_migrations/20221121184931
new file mode 100644
index 00000000000..3d90e696941
--- /dev/null
+++ b/db/schema_migrations/20221121184931
@@ -0,0 +1 @@
+1700ebce94f46e086d2f5f4ec3d00d5bf2f212009c8115f1a7851471912c829a \ No newline at end of file
diff --git a/db/schema_migrations/20221122063922 b/db/schema_migrations/20221122063922
new file mode 100644
index 00000000000..1ea4f70aa99
--- /dev/null
+++ b/db/schema_migrations/20221122063922
@@ -0,0 +1 @@
+2c9002eb1c43ecaa8a6d023e1637f061c308d623f2c541e02e1d979f7ff2b5ba \ No newline at end of file
diff --git a/db/schema_migrations/20221122064537 b/db/schema_migrations/20221122064537
new file mode 100644
index 00000000000..40e1b0c0cfb
--- /dev/null
+++ b/db/schema_migrations/20221122064537
@@ -0,0 +1 @@
+735a85cf6adcf9de1949d0c3d4edbc9415ce62649a078c254d2e5699ce69864a \ No newline at end of file
diff --git a/db/schema_migrations/20221122132812 b/db/schema_migrations/20221122132812
new file mode 100644
index 00000000000..d7534c717fd
--- /dev/null
+++ b/db/schema_migrations/20221122132812
@@ -0,0 +1 @@
+dfc5d0f35c41cde817bdb747bbbceaddc9fa77bec0219f7027ffc66191895f1b \ No newline at end of file
diff --git a/db/schema_migrations/20221122141046 b/db/schema_migrations/20221122141046
new file mode 100644
index 00000000000..4baebb0dda6
--- /dev/null
+++ b/db/schema_migrations/20221122141046
@@ -0,0 +1 @@
+64b59128c42f55725a268b051c2f9fc656b2a49a2e721af995e3e25fc7c7e85d \ No newline at end of file
diff --git a/db/schema_migrations/20221122155149 b/db/schema_migrations/20221122155149
new file mode 100644
index 00000000000..46a4270e5ed
--- /dev/null
+++ b/db/schema_migrations/20221122155149
@@ -0,0 +1 @@
+3c9b8f6191297e95c47a0ae2e3da7725ce33daa2a702407e0256393774935b0b \ No newline at end of file
diff --git a/db/schema_migrations/20221122225925 b/db/schema_migrations/20221122225925
new file mode 100644
index 00000000000..81da88065a2
--- /dev/null
+++ b/db/schema_migrations/20221122225925
@@ -0,0 +1 @@
+223aa6d68c159847c8a50889a270c32b10c4efbf6c1445870f156896d0a34559 \ No newline at end of file
diff --git a/db/schema_migrations/20221123133054 b/db/schema_migrations/20221123133054
new file mode 100644
index 00000000000..3a7a382ee74
--- /dev/null
+++ b/db/schema_migrations/20221123133054
@@ -0,0 +1 @@
+1a0a090433dd422b1bd9efdb56f82c02af8bab45b1a651b51a6ed224d823964c \ No newline at end of file
diff --git a/db/schema_migrations/20221124113925 b/db/schema_migrations/20221124113925
new file mode 100644
index 00000000000..60ae3f4c551
--- /dev/null
+++ b/db/schema_migrations/20221124113925
@@ -0,0 +1 @@
+72063c052e88d9351dbf7aedc373dadedb685f63cfbbadc992ddf322c546579b \ No newline at end of file
diff --git a/db/schema_migrations/20221124153602 b/db/schema_migrations/20221124153602
new file mode 100644
index 00000000000..0c6055b4561
--- /dev/null
+++ b/db/schema_migrations/20221124153602
@@ -0,0 +1 @@
+688db679fa547cd07e8c9ac11cb80afd475bb529a462eba1b899391ca721a611 \ No newline at end of file
diff --git a/db/schema_migrations/20221125222221 b/db/schema_migrations/20221125222221
new file mode 100644
index 00000000000..9235ef557b7
--- /dev/null
+++ b/db/schema_migrations/20221125222221
@@ -0,0 +1 @@
+c1974d6763a85469f3d12fe4e51b1bc3b986cc335b7fe79b3875332d34a1b548 \ No newline at end of file
diff --git a/db/schema_migrations/20221125222341 b/db/schema_migrations/20221125222341
new file mode 100644
index 00000000000..5f4a29202e1
--- /dev/null
+++ b/db/schema_migrations/20221125222341
@@ -0,0 +1 @@
+401b563cf9f92627082bbc9850ab2fbe1d9806ced094fda99783c5d51e00fe1c \ No newline at end of file
diff --git a/db/schema_migrations/20221128120634 b/db/schema_migrations/20221128120634
new file mode 100644
index 00000000000..4a2fa52d675
--- /dev/null
+++ b/db/schema_migrations/20221128120634
@@ -0,0 +1 @@
+011a7add2949c39e642da2f9d7908f6e2a118c91f2e334e0eee623711576c3cb \ No newline at end of file
diff --git a/db/schema_migrations/20221128123514 b/db/schema_migrations/20221128123514
new file mode 100644
index 00000000000..a10dff5ab42
--- /dev/null
+++ b/db/schema_migrations/20221128123514
@@ -0,0 +1 @@
+2b763fd1fe9aee5631f9a8f3bdf699a19003e56f5c857efe4410ec21e5dad8f7 \ No newline at end of file
diff --git a/db/schema_migrations/20221128220043 b/db/schema_migrations/20221128220043
new file mode 100644
index 00000000000..0768b098ba1
--- /dev/null
+++ b/db/schema_migrations/20221128220043
@@ -0,0 +1 @@
+6c2e0ae4cbfabd7d303103a75b76746265e273dc0154618c1dc182621a44ae5a \ No newline at end of file
diff --git a/db/schema_migrations/20221128222417 b/db/schema_migrations/20221128222417
new file mode 100644
index 00000000000..a71c9c84fd1
--- /dev/null
+++ b/db/schema_migrations/20221128222417
@@ -0,0 +1 @@
+fc5a253aabc821a371b6755183cc1d785a37881213dd019b522f074fc884bb73 \ No newline at end of file
diff --git a/db/schema_migrations/20221129124240 b/db/schema_migrations/20221129124240
new file mode 100644
index 00000000000..9b0199dc748
--- /dev/null
+++ b/db/schema_migrations/20221129124240
@@ -0,0 +1 @@
+ae20537326115d37db8beb3432ffd3ace447b39a75906535d319da4db1fcb1b2 \ No newline at end of file
diff --git a/db/schema_migrations/20221129192619 b/db/schema_migrations/20221129192619
new file mode 100644
index 00000000000..4097014aca7
--- /dev/null
+++ b/db/schema_migrations/20221129192619
@@ -0,0 +1 @@
+b452251587b4b1f738300cc2c729642c91470b9233ac2db9f3f0061aeff1dd4a \ No newline at end of file
diff --git a/db/schema_migrations/20221130170433 b/db/schema_migrations/20221130170433
new file mode 100644
index 00000000000..e4c75f719e0
--- /dev/null
+++ b/db/schema_migrations/20221130170433
@@ -0,0 +1 @@
+2c744b68accac8be53240dfa46f09e5dae90e8b14541d57ae8e4c1823eded397 \ No newline at end of file
diff --git a/db/schema_migrations/20221130182056 b/db/schema_migrations/20221130182056
new file mode 100644
index 00000000000..5cadd5084e5
--- /dev/null
+++ b/db/schema_migrations/20221130182056
@@ -0,0 +1 @@
+8a6a12d28ddca01863d39e21461daace89aa9d0940bc13a1747712f699c07600 \ No newline at end of file
diff --git a/db/schema_migrations/20221130192239 b/db/schema_migrations/20221130192239
new file mode 100644
index 00000000000..02edafb2710
--- /dev/null
+++ b/db/schema_migrations/20221130192239
@@ -0,0 +1 @@
+dba113bc64ddabf2059a7aea8ac0a830a237957f1ce50e1a3662c7ed18a645eb \ No newline at end of file
diff --git a/db/schema_migrations/20221202031332 b/db/schema_migrations/20221202031332
new file mode 100644
index 00000000000..10dd1ac1b36
--- /dev/null
+++ b/db/schema_migrations/20221202031332
@@ -0,0 +1 @@
+b07f7fdc85af4cdf85ea3f4add62896fea2fc1fa6fcc973ba615f8a0ed84746e \ No newline at end of file
diff --git a/db/schema_migrations/20221202031417 b/db/schema_migrations/20221202031417
new file mode 100644
index 00000000000..324807b8421
--- /dev/null
+++ b/db/schema_migrations/20221202031417
@@ -0,0 +1 @@
+0c422bc0ef354437302cda84dae77d883ab28775e6008c669075b447828ba914 \ No newline at end of file
diff --git a/db/schema_migrations/20221202144210 b/db/schema_migrations/20221202144210
new file mode 100644
index 00000000000..3b37793b1a9
--- /dev/null
+++ b/db/schema_migrations/20221202144210
@@ -0,0 +1 @@
+5e29c2ebe99ef811cac0f894b3a77d2d158ba43070fb924c663db4622b8e79d7 \ No newline at end of file
diff --git a/db/schema_migrations/20221202154128 b/db/schema_migrations/20221202154128
new file mode 100644
index 00000000000..328f13d3d96
--- /dev/null
+++ b/db/schema_migrations/20221202154128
@@ -0,0 +1 @@
+99f7f14d12f9d213016e0c183cfe6a0e8384db04952128299299a5f83d144a41 \ No newline at end of file
diff --git a/db/schema_migrations/20221202154151 b/db/schema_migrations/20221202154151
new file mode 100644
index 00000000000..23f7349048a
--- /dev/null
+++ b/db/schema_migrations/20221202154151
@@ -0,0 +1 @@
+5c64c1072e6b7c05a7cd23a7c50b5cfce9aba7eca6f29169590be9e115acc87d \ No newline at end of file
diff --git a/db/schema_migrations/20221202202351 b/db/schema_migrations/20221202202351
new file mode 100644
index 00000000000..942bc0577d6
--- /dev/null
+++ b/db/schema_migrations/20221202202351
@@ -0,0 +1 @@
+8e9641a603bd9540d0004fb76b407a5cb4392c8cc41c084cd746f354b9a8d417 \ No newline at end of file
diff --git a/db/schema_migrations/20221205061134 b/db/schema_migrations/20221205061134
new file mode 100644
index 00000000000..e6b4b38a902
--- /dev/null
+++ b/db/schema_migrations/20221205061134
@@ -0,0 +1 @@
+dbb03459b96d7b5165ce093d31bbeb253a972e7d54345f2e171fb487447cdb0b \ No newline at end of file
diff --git a/db/schema_migrations/20221205134448 b/db/schema_migrations/20221205134448
new file mode 100644
index 00000000000..13a6fd7c8fd
--- /dev/null
+++ b/db/schema_migrations/20221205134448
@@ -0,0 +1 @@
+95adff6092ae61752ee817560e2ba98bf697660a38ae78e07317e1634436c778 \ No newline at end of file
diff --git a/db/schema_migrations/20221205151917 b/db/schema_migrations/20221205151917
new file mode 100644
index 00000000000..6ae6787c2a9
--- /dev/null
+++ b/db/schema_migrations/20221205151917
@@ -0,0 +1 @@
+64c4d48759a28b2dbac035d290724ee82b8af4af2bef75e1d7e07eefe10e6d17 \ No newline at end of file
diff --git a/db/schema_migrations/20221205170310 b/db/schema_migrations/20221205170310
new file mode 100644
index 00000000000..08365068b43
--- /dev/null
+++ b/db/schema_migrations/20221205170310
@@ -0,0 +1 @@
+6501b62569b96e2be3afc2c79fe438fffc6b45485b04f6b2989ae35f0cf9a4b9 \ No newline at end of file
diff --git a/db/schema_migrations/20221206012013 b/db/schema_migrations/20221206012013
new file mode 100644
index 00000000000..a7cfcc43f7c
--- /dev/null
+++ b/db/schema_migrations/20221206012013
@@ -0,0 +1 @@
+52294ac9fd807fdff49d918d6b49c071b53e683a479af6beef9449302080e44f \ No newline at end of file
diff --git a/db/schema_migrations/20221206075631 b/db/schema_migrations/20221206075631
new file mode 100644
index 00000000000..d8c7332528c
--- /dev/null
+++ b/db/schema_migrations/20221206075631
@@ -0,0 +1 @@
+35879808e9dd8f4436a1fd09c68e28114f8cd448f039eaff000075226d0a80f9 \ No newline at end of file
diff --git a/db/schema_migrations/20221206132610 b/db/schema_migrations/20221206132610
new file mode 100644
index 00000000000..1df21d68a0c
--- /dev/null
+++ b/db/schema_migrations/20221206132610
@@ -0,0 +1 @@
+52d5d6dd9d762da8885fae101ff8014ffaac7ade5c6aa8b306595320997d4e58 \ No newline at end of file
diff --git a/db/schema_migrations/20221206163420 b/db/schema_migrations/20221206163420
new file mode 100644
index 00000000000..f61bb680fd2
--- /dev/null
+++ b/db/schema_migrations/20221206163420
@@ -0,0 +1 @@
+8f22266821405e2931ca80baf4b6760cb340f6207b00e2ffbedb9c62419a3af4 \ No newline at end of file
diff --git a/db/schema_migrations/20221206173132 b/db/schema_migrations/20221206173132
new file mode 100644
index 00000000000..7f34421d270
--- /dev/null
+++ b/db/schema_migrations/20221206173132
@@ -0,0 +1 @@
+c2e7a2c25e281419e2e401e3bff661c706386900faffc784efcfbf7aca169ed8 \ No newline at end of file
diff --git a/db/schema_migrations/20221206211814 b/db/schema_migrations/20221206211814
new file mode 100644
index 00000000000..fb9c93fce39
--- /dev/null
+++ b/db/schema_migrations/20221206211814
@@ -0,0 +1 @@
+9294a13506b4e7f50ffa49674aff45f9223830f6247509029d533cdfea4d9d6f \ No newline at end of file
diff --git a/db/schema_migrations/20221206222032 b/db/schema_migrations/20221206222032
new file mode 100644
index 00000000000..16c8b6ea72b
--- /dev/null
+++ b/db/schema_migrations/20221206222032
@@ -0,0 +1 @@
+9e3f3c09100e3c26de7280bf30dc836a66d9fefb0894c86c80a3c5ee8e36235b \ No newline at end of file
diff --git a/db/schema_migrations/20221206235208 b/db/schema_migrations/20221206235208
new file mode 100644
index 00000000000..7a1a82545ae
--- /dev/null
+++ b/db/schema_migrations/20221206235208
@@ -0,0 +1 @@
+4af9700e48540da55c783900a9156eca04a5e580ca106d94b6e51fa15f3a6547 \ No newline at end of file
diff --git a/db/schema_migrations/20221207140259 b/db/schema_migrations/20221207140259
new file mode 100644
index 00000000000..d0f4a627129
--- /dev/null
+++ b/db/schema_migrations/20221207140259
@@ -0,0 +1 @@
+4ec6d2cd2a497c7416c08fa31618f34474c868fdf0060692b8815492bace3a0d \ No newline at end of file
diff --git a/db/schema_migrations/20221207220120 b/db/schema_migrations/20221207220120
new file mode 100644
index 00000000000..3ff084a13ac
--- /dev/null
+++ b/db/schema_migrations/20221207220120
@@ -0,0 +1 @@
+caa6f87b639b62ea25c9f7adc81bd64bba4084b8987bfc5df84f507b63faab4a \ No newline at end of file
diff --git a/db/schema_migrations/20221208122921 b/db/schema_migrations/20221208122921
new file mode 100644
index 00000000000..1245bbc2bc3
--- /dev/null
+++ b/db/schema_migrations/20221208122921
@@ -0,0 +1 @@
+e205d116057a4e6770b8e8b7e49a87a180fb470087a4394d1a4e529ff1dba631 \ No newline at end of file
diff --git a/db/schema_migrations/20221209110934 b/db/schema_migrations/20221209110934
new file mode 100644
index 00000000000..43951756ce2
--- /dev/null
+++ b/db/schema_migrations/20221209110934
@@ -0,0 +1 @@
+73b704bee30bf90476bcb913d56949cdd59f9c5f4b19a38025c9b02af9cb26a7 \ No newline at end of file
diff --git a/db/schema_migrations/20221209110935 b/db/schema_migrations/20221209110935
new file mode 100644
index 00000000000..6e39531220d
--- /dev/null
+++ b/db/schema_migrations/20221209110935
@@ -0,0 +1 @@
+6a25429104daf2b735f0a22e48dc631ded1aebe7d6f5f9d61520af184f6b5075 \ No newline at end of file
diff --git a/db/schema_migrations/20221210154044 b/db/schema_migrations/20221210154044
new file mode 100644
index 00000000000..44dd561a449
--- /dev/null
+++ b/db/schema_migrations/20221210154044
@@ -0,0 +1 @@
+6349918b178fb0b110f16f4cff6f64c862b3763c5a401238732f6ac507b7c79d \ No newline at end of file
diff --git a/db/schema_migrations/20221212103743 b/db/schema_migrations/20221212103743
new file mode 100644
index 00000000000..7f225c9516f
--- /dev/null
+++ b/db/schema_migrations/20221212103743
@@ -0,0 +1 @@
+a961cf4e53556fe7899fbabc7bc686d5edaf061abe5a008eb7a6304f64f2f22f \ No newline at end of file
diff --git a/db/schema_migrations/20221213064717 b/db/schema_migrations/20221213064717
new file mode 100644
index 00000000000..1da000d50c5
--- /dev/null
+++ b/db/schema_migrations/20221213064717
@@ -0,0 +1 @@
+0677f23100c5a4b010c2601d64c29116150b51735c7b920fa2c87a95de293176 \ No newline at end of file
diff --git a/db/schema_migrations/20221213184314 b/db/schema_migrations/20221213184314
new file mode 100644
index 00000000000..2d297ebe7ee
--- /dev/null
+++ b/db/schema_migrations/20221213184314
@@ -0,0 +1 @@
+b14a060e05fc73c9d76d7c8bec3f9e1fa99b33eae6ec0057b4a398b28414a02a \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 350ac2ad454..72d9c94be49 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -10614,6 +10614,29 @@ CREATE SEQUENCE abuse_reports_id_seq
ALTER SEQUENCE abuse_reports_id_seq OWNED BY abuse_reports.id;
+CREATE TABLE achievements (
+ id bigint NOT NULL,
+ namespace_id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ name text NOT NULL,
+ avatar text,
+ description text,
+ revokeable boolean DEFAULT false NOT NULL,
+ CONSTRAINT check_5171b03f22 CHECK ((char_length(name) <= 255)),
+ CONSTRAINT check_a7a7b84a80 CHECK ((char_length(description) <= 1024)),
+ CONSTRAINT check_e174e93a9e CHECK ((char_length(avatar) <= 255))
+);
+
+CREATE SEQUENCE achievements_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE achievements_id_seq OWNED BY achievements.id;
+
CREATE TABLE agent_activity_events (
id bigint NOT NULL,
agent_id bigint NOT NULL,
@@ -11040,7 +11063,9 @@ CREATE TABLE appearances (
email_header_and_footer_enabled boolean DEFAULT false NOT NULL,
profile_image_guidelines text,
profile_image_guidelines_html text,
- CONSTRAINT appearances_profile_image_guidelines CHECK ((char_length(profile_image_guidelines) <= 4096))
+ short_title text,
+ CONSTRAINT appearances_profile_image_guidelines CHECK ((char_length(profile_image_guidelines) <= 4096)),
+ CONSTRAINT check_fdf3064682 CHECK ((char_length(short_title) <= 255))
);
CREATE SEQUENCE appearances_id_seq
@@ -11081,7 +11106,7 @@ CREATE TABLE application_settings (
help_text text,
restricted_visibility_levels text,
version_check_enabled boolean DEFAULT true,
- max_attachment_size integer DEFAULT 10 NOT NULL,
+ max_attachment_size integer DEFAULT 100 NOT NULL,
default_project_visibility integer DEFAULT 0 NOT NULL,
default_snippet_visibility integer DEFAULT 0 NOT NULL,
user_oauth_applications boolean DEFAULT true,
@@ -11529,12 +11554,16 @@ CREATE TABLE application_settings (
encrypted_telesign_customer_xid_iv bytea,
encrypted_telesign_api_key bytea,
encrypted_telesign_api_key_iv bytea,
+ disable_personal_access_tokens boolean DEFAULT false NOT NULL,
+ max_terraform_state_size_bytes integer DEFAULT 0 NOT NULL,
+ bulk_import_enabled boolean DEFAULT false NOT NULL,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_container_registry_pre_import_tags_rate_positive CHECK ((container_registry_pre_import_tags_rate >= (0)::numeric)),
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)),
CONSTRAINT app_settings_git_rate_limit_users_allowlist_max_usernames CHECK ((cardinality(git_rate_limit_users_allowlist) <= 100)),
CONSTRAINT app_settings_max_pages_custom_domains_per_project_check CHECK ((max_pages_custom_domains_per_project >= 0)),
+ CONSTRAINT app_settings_max_terraform_state_size_bytes_check CHECK ((max_terraform_state_size_bytes >= 0)),
CONSTRAINT app_settings_p_cleanup_package_file_worker_capacity_positive CHECK ((packages_cleanup_package_file_worker_capacity >= 0)),
CONSTRAINT app_settings_pkg_registry_cleanup_pol_worker_capacity_gte_zero CHECK ((package_registry_cleanup_policies_worker_capacity >= 0)),
CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)),
@@ -11727,7 +11756,7 @@ CREATE TABLE approval_project_rules (
approvals_required smallint DEFAULT 0 NOT NULL,
name character varying NOT NULL,
rule_type smallint DEFAULT 0 NOT NULL,
- scanners text[],
+ scanners text[] DEFAULT '{}'::text[],
vulnerabilities_allowed smallint DEFAULT 0 NOT NULL,
severity_levels text[] DEFAULT '{}'::text[] NOT NULL,
report_type smallint,
@@ -12524,6 +12553,7 @@ CREATE TABLE bulk_imports (
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
source_version text,
+ source_enterprise boolean DEFAULT true NOT NULL,
CONSTRAINT check_ea4e58775a CHECK ((char_length(source_version) <= 63))
);
@@ -12582,7 +12612,8 @@ CREATE TABLE ci_build_needs (
name text NOT NULL,
artifacts boolean DEFAULT true NOT NULL,
optional boolean DEFAULT false NOT NULL,
- build_id bigint NOT NULL
+ build_id bigint NOT NULL,
+ partition_id bigint DEFAULT 100 NOT NULL
);
CREATE SEQUENCE ci_build_needs_id_seq
@@ -12602,7 +12633,8 @@ CREATE TABLE ci_build_pending_states (
state smallint,
failure_reason smallint,
trace_checksum bytea,
- trace_bytesize bigint
+ trace_bytesize bigint,
+ partition_id bigint DEFAULT 100 NOT NULL
);
CREATE SEQUENCE ci_build_pending_states_id_seq
@@ -12617,7 +12649,8 @@ ALTER SEQUENCE ci_build_pending_states_id_seq OWNED BY ci_build_pending_states.i
CREATE TABLE ci_build_report_results (
build_id bigint NOT NULL,
project_id bigint NOT NULL,
- data jsonb DEFAULT '{}'::jsonb NOT NULL
+ data jsonb DEFAULT '{}'::jsonb NOT NULL,
+ partition_id bigint DEFAULT 100 NOT NULL
);
CREATE TABLE ci_build_trace_chunks (
@@ -12627,7 +12660,8 @@ CREATE TABLE ci_build_trace_chunks (
raw_data bytea,
checksum bytea,
lock_version integer DEFAULT 0 NOT NULL,
- build_id bigint NOT NULL
+ build_id bigint NOT NULL,
+ partition_id bigint DEFAULT 100 NOT NULL
);
CREATE SEQUENCE ci_build_trace_chunks_id_seq
@@ -12646,7 +12680,8 @@ CREATE TABLE ci_build_trace_metadata (
checksum bytea,
remote_checksum bytea,
last_archival_attempt_at timestamp with time zone,
- archived_at timestamp with time zone
+ archived_at timestamp with time zone,
+ partition_id bigint DEFAULT 100 NOT NULL
);
CREATE TABLE ci_builds (
@@ -12721,7 +12756,8 @@ CREATE TABLE p_ci_builds_metadata (
id bigint NOT NULL,
runtime_runner_features jsonb DEFAULT '{}'::jsonb NOT NULL,
id_tokens jsonb DEFAULT '{}'::jsonb NOT NULL,
- partition_id bigint DEFAULT 100 NOT NULL
+ partition_id bigint DEFAULT 100 NOT NULL,
+ debug_trace_enabled boolean DEFAULT false NOT NULL
)
PARTITION BY LIST (partition_id);
@@ -12749,7 +12785,8 @@ CREATE TABLE ci_builds_metadata (
id bigint DEFAULT nextval('ci_builds_metadata_id_seq'::regclass) NOT NULL,
runtime_runner_features jsonb DEFAULT '{}'::jsonb NOT NULL,
id_tokens jsonb DEFAULT '{}'::jsonb NOT NULL,
- partition_id bigint DEFAULT 100 NOT NULL
+ partition_id bigint DEFAULT 100 NOT NULL,
+ debug_trace_enabled boolean DEFAULT false NOT NULL
);
ALTER TABLE ONLY p_ci_builds_metadata ATTACH PARTITION ci_builds_metadata FOR VALUES IN ('100');
@@ -12758,7 +12795,8 @@ CREATE TABLE ci_builds_runner_session (
url character varying NOT NULL,
certificate character varying,
"authorization" character varying,
- build_id bigint NOT NULL
+ build_id bigint NOT NULL,
+ partition_id bigint DEFAULT 100 NOT NULL
);
CREATE SEQUENCE ci_builds_runner_session_id_seq
@@ -12946,7 +12984,8 @@ CREATE TABLE ci_job_variables (
job_id bigint NOT NULL,
variable_type smallint DEFAULT 1 NOT NULL,
source smallint DEFAULT 0 NOT NULL,
- raw boolean DEFAULT false NOT NULL
+ raw boolean DEFAULT false NOT NULL,
+ partition_id bigint DEFAULT 100 NOT NULL
);
CREATE SEQUENCE ci_job_variables_id_seq
@@ -13039,7 +13078,8 @@ CREATE TABLE ci_pending_builds (
namespace_id bigint,
minutes_exceeded boolean DEFAULT false NOT NULL,
tag_ids integer[] DEFAULT '{}'::integer[],
- namespace_traversal_ids integer[] DEFAULT '{}'::integer[]
+ namespace_traversal_ids integer[] DEFAULT '{}'::integer[],
+ partition_id bigint DEFAULT 100 NOT NULL
);
CREATE SEQUENCE ci_pending_builds_id_seq
@@ -13335,7 +13375,8 @@ CREATE TABLE ci_resources (
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
resource_group_id bigint NOT NULL,
- build_id bigint
+ build_id bigint,
+ partition_id bigint
);
CREATE SEQUENCE ci_resources_id_seq
@@ -13430,7 +13471,8 @@ CREATE TABLE ci_running_builds (
project_id bigint NOT NULL,
runner_id bigint NOT NULL,
created_at timestamp with time zone DEFAULT now() NOT NULL,
- runner_type smallint NOT NULL
+ runner_type smallint NOT NULL,
+ partition_id bigint DEFAULT 100 NOT NULL
);
CREATE SEQUENCE ci_running_builds_id_seq
@@ -13495,7 +13537,9 @@ CREATE TABLE ci_sources_pipelines (
pipeline_id integer,
source_project_id integer,
source_pipeline_id integer,
- source_job_id bigint
+ source_job_id bigint,
+ partition_id bigint DEFAULT 100 NOT NULL,
+ source_partition_id bigint DEFAULT 100 NOT NULL
);
CREATE SEQUENCE ci_sources_pipelines_id_seq
@@ -13603,7 +13647,8 @@ CREATE TABLE ci_unit_test_failures (
id bigint NOT NULL,
failed_at timestamp with time zone NOT NULL,
unit_test_id bigint NOT NULL,
- build_id bigint NOT NULL
+ build_id bigint NOT NULL,
+ partition_id bigint DEFAULT 100 NOT NULL
);
CREATE SEQUENCE ci_unit_test_failures_id_seq
@@ -14168,6 +14213,7 @@ CREATE TABLE container_repositories (
migration_plan text,
last_cleanup_deleted_tags_count integer,
delete_started_at timestamp with time zone,
+ status_updated_at timestamp with time zone,
CONSTRAINT check_05e9012f36 CHECK ((char_length(migration_plan) <= 255)),
CONSTRAINT check_13c58fe73a CHECK ((char_length(migration_state) <= 255)),
CONSTRAINT check_97f0249439 CHECK ((char_length(migration_aborted_in_state) <= 255))
@@ -14358,6 +14404,43 @@ CREATE SEQUENCE customer_relations_organizations_id_seq
ALTER SEQUENCE customer_relations_organizations_id_seq OWNED BY customer_relations_organizations.id;
+CREATE TABLE dast_pre_scan_verification_steps (
+ id bigint NOT NULL,
+ dast_pre_scan_verification_id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ name text,
+ verification_errors text[] DEFAULT '{}'::text[] NOT NULL,
+ CONSTRAINT check_cd216b95e4 CHECK ((char_length(name) <= 255))
+);
+
+CREATE SEQUENCE dast_pre_scan_verification_steps_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE dast_pre_scan_verification_steps_id_seq OWNED BY dast_pre_scan_verification_steps.id;
+
+CREATE TABLE dast_pre_scan_verifications (
+ id bigint NOT NULL,
+ dast_profile_id bigint NOT NULL,
+ ci_pipeline_id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ status smallint DEFAULT 0 NOT NULL
+);
+
+CREATE SEQUENCE dast_pre_scan_verifications_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE dast_pre_scan_verifications_id_seq OWNED BY dast_pre_scan_verifications.id;
+
CREATE TABLE dast_profile_schedules (
id bigint NOT NULL,
project_id bigint NOT NULL,
@@ -14449,6 +14532,21 @@ CREATE SEQUENCE dast_scanner_profiles_id_seq
ALTER SEQUENCE dast_scanner_profiles_id_seq OWNED BY dast_scanner_profiles.id;
+CREATE TABLE dast_scanner_profiles_tags (
+ id bigint NOT NULL,
+ dast_scanner_profile_id bigint NOT NULL,
+ tag_id bigint NOT NULL
+);
+
+CREATE SEQUENCE dast_scanner_profiles_tags_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE dast_scanner_profiles_tags_id_seq OWNED BY dast_scanner_profiles_tags.id;
+
CREATE TABLE dast_site_profile_secret_variables (
id bigint NOT NULL,
dast_site_profile_id bigint NOT NULL,
@@ -14590,6 +14688,27 @@ CREATE SEQUENCE dast_sites_id_seq
ALTER SEQUENCE dast_sites_id_seq OWNED BY dast_sites.id;
+CREATE TABLE dependency_list_exports (
+ id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ project_id bigint NOT NULL,
+ user_id bigint,
+ file_store integer,
+ status smallint DEFAULT 0 NOT NULL,
+ file text,
+ CONSTRAINT check_fff6fc9b2f CHECK ((char_length(file) <= 255))
+);
+
+CREATE SEQUENCE dependency_list_exports_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE dependency_list_exports_id_seq OWNED BY dependency_list_exports.id;
+
CREATE TABLE dependency_proxy_blob_states (
verification_started_at timestamp with time zone,
verification_retry_at timestamp with time zone,
@@ -14631,7 +14750,7 @@ CREATE TABLE dependency_proxy_group_settings (
group_id integer NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
- enabled boolean DEFAULT false NOT NULL
+ enabled boolean DEFAULT true NOT NULL
);
CREATE SEQUENCE dependency_proxy_group_settings_id_seq
@@ -14651,6 +14770,18 @@ CREATE TABLE dependency_proxy_image_ttl_group_policies (
enabled boolean DEFAULT false NOT NULL
);
+CREATE TABLE dependency_proxy_manifest_states (
+ verification_started_at timestamp with time zone,
+ verification_retry_at timestamp with time zone,
+ verified_at timestamp with time zone,
+ dependency_proxy_manifest_id bigint NOT NULL,
+ verification_state smallint DEFAULT 0 NOT NULL,
+ verification_retry_count smallint DEFAULT 0 NOT NULL,
+ verification_checksum bytea,
+ verification_failure text,
+ CONSTRAINT check_fdd5d9791b CHECK ((char_length(verification_failure) <= 255))
+);
+
CREATE TABLE dependency_proxy_manifests (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
@@ -15363,44 +15494,6 @@ CREATE SEQUENCE evidences_id_seq
ALTER SEQUENCE evidences_id_seq OWNED BY evidences.id;
-CREATE TABLE experiment_subjects (
- id bigint NOT NULL,
- experiment_id bigint NOT NULL,
- user_id bigint,
- project_id bigint,
- variant smallint DEFAULT 0 NOT NULL,
- created_at timestamp with time zone NOT NULL,
- updated_at timestamp with time zone NOT NULL,
- converted_at timestamp with time zone,
- context jsonb DEFAULT '{}'::jsonb NOT NULL,
- namespace_id bigint,
- CONSTRAINT check_f6411bc4b5 CHECK ((num_nonnulls(user_id, namespace_id, project_id) = 1))
-);
-
-CREATE SEQUENCE experiment_subjects_id_seq
- START WITH 1
- INCREMENT BY 1
- NO MINVALUE
- NO MAXVALUE
- CACHE 1;
-
-ALTER SEQUENCE experiment_subjects_id_seq OWNED BY experiment_subjects.id;
-
-CREATE TABLE experiments (
- id bigint NOT NULL,
- name text NOT NULL,
- CONSTRAINT check_e2dda25ed0 CHECK ((char_length(name) <= 255))
-);
-
-CREATE SEQUENCE experiments_id_seq
- START WITH 1
- INCREMENT BY 1
- NO MINVALUE
- NO MAXVALUE
- CACHE 1;
-
-ALTER SEQUENCE experiments_id_seq OWNED BY experiments.id;
-
CREATE TABLE external_approval_rules (
id bigint NOT NULL,
project_id bigint NOT NULL,
@@ -15953,6 +16046,7 @@ CREATE TABLE gitlab_subscriptions (
seats_owed integer DEFAULT 0 NOT NULL,
trial_extension_type smallint,
max_seats_used_changed_at timestamp with time zone,
+ last_seat_refresh_at timestamp with time zone,
CONSTRAINT check_77fea3f0e7 CHECK ((namespace_id IS NOT NULL))
);
@@ -16913,6 +17007,8 @@ CREATE TABLE issues (
work_item_type_id bigint,
namespace_id bigint,
start_date date,
+ CONSTRAINT check_2addf801cd CHECK ((work_item_type_id IS NOT NULL)),
+ CONSTRAINT check_c33362cd43 CHECK ((namespace_id IS NOT NULL)),
CONSTRAINT check_fba63f706d CHECK ((lock_version IS NOT NULL))
);
@@ -17080,7 +17176,8 @@ CREATE TABLE keys (
fingerprint_sha256 bytea,
expires_at timestamp with time zone,
expiry_notification_delivered_at timestamp with time zone,
- before_expiry_notification_delivered_at timestamp with time zone
+ before_expiry_notification_delivered_at timestamp with time zone,
+ usage_type smallint DEFAULT 0 NOT NULL
);
CREATE SEQUENCE keys_id_seq
@@ -17324,7 +17421,8 @@ CREATE TABLE member_roles (
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
base_access_level integer NOT NULL,
- download_code boolean DEFAULT false
+ download_code boolean DEFAULT false,
+ read_code boolean DEFAULT false
);
CREATE SEQUENCE member_roles_id_seq
@@ -17617,7 +17715,8 @@ CREATE TABLE merge_request_predictions (
merge_request_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
- suggested_reviewers jsonb DEFAULT '{}'::jsonb NOT NULL
+ suggested_reviewers jsonb DEFAULT '{}'::jsonb NOT NULL,
+ accepted_reviewers jsonb DEFAULT '{}'::jsonb NOT NULL
);
CREATE SEQUENCE merge_request_predictions_merge_request_id_seq
@@ -17846,6 +17945,26 @@ CREATE SEQUENCE milestones_id_seq
ALTER SEQUENCE milestones_id_seq OWNED BY milestones.id;
+CREATE TABLE ml_candidate_metadata (
+ id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ candidate_id bigint NOT NULL,
+ name text NOT NULL,
+ value text NOT NULL,
+ CONSTRAINT check_6b38a286a5 CHECK ((char_length(name) <= 255)),
+ CONSTRAINT check_9453f4a8e9 CHECK ((char_length(value) <= 5000))
+);
+
+CREATE SEQUENCE ml_candidate_metadata_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE ml_candidate_metadata_id_seq OWNED BY ml_candidate_metadata.id;
+
CREATE TABLE ml_candidate_metrics (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
@@ -17909,6 +18028,26 @@ CREATE SEQUENCE ml_candidates_id_seq
ALTER SEQUENCE ml_candidates_id_seq OWNED BY ml_candidates.id;
+CREATE TABLE ml_experiment_metadata (
+ id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ experiment_id bigint NOT NULL,
+ name text NOT NULL,
+ value text NOT NULL,
+ CONSTRAINT check_112fe5002d CHECK ((char_length(name) <= 255)),
+ CONSTRAINT check_a91c633d68 CHECK ((char_length(value) <= 5000))
+);
+
+CREATE SEQUENCE ml_experiment_metadata_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE ml_experiment_metadata_id_seq OWNED BY ml_experiment_metadata.id;
+
CREATE TABLE ml_experiments (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
@@ -18000,7 +18139,9 @@ CREATE TABLE namespace_details (
description text,
description_html text,
free_user_cap_over_limt_notified_at timestamp with time zone,
- free_user_cap_over_limit_notified_at timestamp with time zone
+ free_user_cap_over_limit_notified_at timestamp with time zone,
+ dashboard_notification_at timestamp with time zone,
+ dashboard_enforcement_at timestamp with time zone
);
CREATE TABLE namespace_limits (
@@ -18072,6 +18213,7 @@ CREATE TABLE namespace_settings (
allow_merge_on_skipped_pipeline boolean DEFAULT false NOT NULL,
only_allow_merge_if_all_discussions_are_resolved boolean DEFAULT false NOT NULL,
default_compliance_framework_id bigint,
+ runner_registration_enabled boolean DEFAULT true,
CONSTRAINT check_0ba93c78c7 CHECK ((char_length(default_branch_name) <= 255)),
CONSTRAINT namespace_settings_unique_project_download_limit_allowlist_size CHECK ((cardinality(unique_project_download_limit_allowlist) <= 100))
);
@@ -18143,8 +18285,7 @@ CREATE TABLE namespaces (
push_rule_id bigint,
shared_runners_enabled boolean DEFAULT true NOT NULL,
allow_descendants_override_disabled_shared_runners boolean DEFAULT false NOT NULL,
- traversal_ids integer[] DEFAULT '{}'::integer[] NOT NULL,
- tmp_project_id integer
+ traversal_ids integer[] DEFAULT '{}'::integer[] NOT NULL
);
CREATE SEQUENCE namespaces_id_seq
@@ -19408,7 +19549,9 @@ CREATE TABLE plan_limits (
project_ci_variables integer DEFAULT 200 NOT NULL,
group_ci_variables integer DEFAULT 200 NOT NULL,
ci_max_artifact_size_cyclonedx integer DEFAULT 1 NOT NULL,
- rpm_max_file_size bigint DEFAULT '5368709120'::bigint NOT NULL
+ rpm_max_file_size bigint DEFAULT '5368709120'::bigint NOT NULL,
+ ci_max_artifact_size_requirements_v2 integer DEFAULT 0 NOT NULL,
+ pipeline_hierarchy_size integer DEFAULT 1000 NOT NULL
);
CREATE SEQUENCE plan_limits_id_seq
@@ -19437,6 +19580,58 @@ CREATE SEQUENCE plans_id_seq
ALTER SEQUENCE plans_id_seq OWNED BY plans.id;
+CREATE TABLE pm_licenses (
+ id bigint NOT NULL,
+ spdx_identifier text NOT NULL,
+ CONSTRAINT check_c1eb81d1ba CHECK ((char_length(spdx_identifier) <= 50))
+);
+
+CREATE SEQUENCE pm_licenses_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE pm_licenses_id_seq OWNED BY pm_licenses.id;
+
+CREATE TABLE pm_package_version_licenses (
+ pm_package_version_id bigint NOT NULL,
+ pm_license_id bigint NOT NULL
+);
+
+CREATE TABLE pm_package_versions (
+ id bigint NOT NULL,
+ pm_package_id bigint,
+ version text NOT NULL,
+ CONSTRAINT check_2d8a88cfcc CHECK ((char_length(version) <= 255))
+);
+
+CREATE SEQUENCE pm_package_versions_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE pm_package_versions_id_seq OWNED BY pm_package_versions.id;
+
+CREATE TABLE pm_packages (
+ id bigint NOT NULL,
+ purl_type smallint NOT NULL,
+ name text NOT NULL,
+ CONSTRAINT check_3a3aedb8ba CHECK ((char_length(name) <= 255))
+);
+
+CREATE SEQUENCE pm_packages_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE pm_packages_id_seq OWNED BY pm_packages.id;
+
CREATE TABLE pool_repositories (
id bigint NOT NULL,
shard_id integer NOT NULL,
@@ -20251,6 +20446,7 @@ CREATE TABLE project_settings (
suggested_reviewers_enabled boolean DEFAULT false NOT NULL,
only_allow_merge_if_all_status_checks_passed boolean DEFAULT false NOT NULL,
mirror_branch_regex text,
+ allow_pipeline_trigger_approve_deployment boolean DEFAULT false NOT NULL,
CONSTRAINT check_2981f15877 CHECK ((char_length(jitsu_key) <= 100)),
CONSTRAINT check_3a03e7557a CHECK ((char_length(previous_default_branch) <= 4096)),
CONSTRAINT check_3ca5cbffe6 CHECK ((char_length(issue_branch_template) <= 255)),
@@ -21479,6 +21675,21 @@ CREATE SEQUENCE shards_id_seq
ALTER SEQUENCE shards_id_seq OWNED BY shards.id;
+CREATE TABLE slack_api_scopes (
+ id bigint NOT NULL,
+ name text NOT NULL,
+ CONSTRAINT check_738678187a CHECK ((char_length(name) <= 100))
+);
+
+CREATE SEQUENCE slack_api_scopes_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE slack_api_scopes_id_seq OWNED BY slack_api_scopes.id;
+
CREATE TABLE slack_integrations (
id integer NOT NULL,
team_id character varying NOT NULL,
@@ -21504,6 +21715,21 @@ CREATE SEQUENCE slack_integrations_id_seq
ALTER SEQUENCE slack_integrations_id_seq OWNED BY slack_integrations.id;
+CREATE TABLE slack_integrations_scopes (
+ id bigint NOT NULL,
+ slack_api_scope_id bigint NOT NULL,
+ slack_integration_id bigint NOT NULL
+);
+
+CREATE SEQUENCE slack_integrations_scopes_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE slack_integrations_scopes_id_seq OWNED BY slack_integrations_scopes.id;
+
CREATE TABLE smartcard_identities (
id bigint NOT NULL,
user_id integer NOT NULL,
@@ -22255,9 +22481,11 @@ CREATE TABLE user_details (
location text DEFAULT ''::text NOT NULL,
organization text DEFAULT ''::text NOT NULL,
password_last_changed_at timestamp with time zone DEFAULT now() NOT NULL,
+ onboarding_step_url text,
CONSTRAINT check_245664af82 CHECK ((char_length(webauthn_xid) <= 100)),
CONSTRAINT check_444573ee52 CHECK ((char_length(skype) <= 500)),
CONSTRAINT check_466a25be35 CHECK ((char_length(twitter) <= 500)),
+ CONSTRAINT check_4f51129940 CHECK ((char_length(onboarding_step_url) <= 2000)),
CONSTRAINT check_7b246dad73 CHECK ((char_length(organization) <= 500)),
CONSTRAINT check_7d6489f8f3 CHECK ((char_length(linkedin) <= 500)),
CONSTRAINT check_7fe2044093 CHECK ((char_length(website_url) <= 500)),
@@ -22396,6 +22624,7 @@ CREATE TABLE user_preferences (
diffs_addition_color text,
markdown_automatic_lists boolean DEFAULT true NOT NULL,
use_legacy_web_ide boolean DEFAULT false NOT NULL,
+ use_new_navigation boolean,
CONSTRAINT check_89bf269f41 CHECK ((char_length(diffs_deletion_color) <= 7)),
CONSTRAINT check_d07ccd35f7 CHECK ((char_length(diffs_addition_color) <= 7))
);
@@ -22548,6 +22777,7 @@ CREATE TABLE users (
user_type smallint,
static_object_token_encrypted text,
otp_secret_expires_at timestamp with time zone,
+ onboarding_in_progress boolean DEFAULT false NOT NULL,
CONSTRAINT check_7bde697e8e CHECK ((char_length(static_object_token_encrypted) <= 255))
);
@@ -23113,7 +23343,7 @@ CREATE TABLE vulnerability_state_transitions (
comment text,
dismissal_reason smallint,
CONSTRAINT check_d1ca8ec043 CHECK ((from_state <> to_state)),
- CONSTRAINT check_fca4a7ca39 CHECK ((char_length(comment) <= 255))
+ CONSTRAINT check_fe2eb6a0f3 CHECK ((char_length(comment) <= 50000))
);
CREATE SEQUENCE vulnerability_state_transitions_id_seq
@@ -23282,6 +23512,22 @@ CREATE SEQUENCE wiki_page_slugs_id_seq
ALTER SEQUENCE wiki_page_slugs_id_seq OWNED BY wiki_page_slugs.id;
+CREATE TABLE work_item_hierarchy_restrictions (
+ id bigint NOT NULL,
+ parent_type_id bigint NOT NULL,
+ child_type_id bigint NOT NULL,
+ maximum_depth smallint
+);
+
+CREATE SEQUENCE work_item_hierarchy_restrictions_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE work_item_hierarchy_restrictions_id_seq OWNED BY work_item_hierarchy_restrictions.id;
+
CREATE TABLE work_item_parent_links (
id bigint NOT NULL,
work_item_id bigint NOT NULL,
@@ -23300,6 +23546,13 @@ CREATE SEQUENCE work_item_parent_links_id_seq
ALTER SEQUENCE work_item_parent_links_id_seq OWNED BY work_item_parent_links.id;
+CREATE TABLE work_item_progresses (
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ issue_id bigint NOT NULL,
+ progress smallint DEFAULT 0 NOT NULL
+);
+
CREATE TABLE work_item_types (
id bigint NOT NULL,
base_type smallint DEFAULT 0 NOT NULL,
@@ -23427,6 +23680,8 @@ ALTER SEQUENCE zoom_meetings_id_seq OWNED BY zoom_meetings.id;
ALTER TABLE ONLY abuse_reports ALTER COLUMN id SET DEFAULT nextval('abuse_reports_id_seq'::regclass);
+ALTER TABLE ONLY achievements ALTER COLUMN id SET DEFAULT nextval('achievements_id_seq'::regclass);
+
ALTER TABLE ONLY agent_activity_events ALTER COLUMN id SET DEFAULT nextval('agent_activity_events_id_seq'::regclass);
ALTER TABLE ONLY agent_group_authorizations ALTER COLUMN id SET DEFAULT nextval('agent_group_authorizations_id_seq'::regclass);
@@ -23709,12 +23964,18 @@ ALTER TABLE ONLY customer_relations_contacts ALTER COLUMN id SET DEFAULT nextval
ALTER TABLE ONLY customer_relations_organizations ALTER COLUMN id SET DEFAULT nextval('customer_relations_organizations_id_seq'::regclass);
+ALTER TABLE ONLY dast_pre_scan_verification_steps ALTER COLUMN id SET DEFAULT nextval('dast_pre_scan_verification_steps_id_seq'::regclass);
+
+ALTER TABLE ONLY dast_pre_scan_verifications ALTER COLUMN id SET DEFAULT nextval('dast_pre_scan_verifications_id_seq'::regclass);
+
ALTER TABLE ONLY dast_profile_schedules ALTER COLUMN id SET DEFAULT nextval('dast_profile_schedules_id_seq'::regclass);
ALTER TABLE ONLY dast_profiles ALTER COLUMN id SET DEFAULT nextval('dast_profiles_id_seq'::regclass);
ALTER TABLE ONLY dast_scanner_profiles ALTER COLUMN id SET DEFAULT nextval('dast_scanner_profiles_id_seq'::regclass);
+ALTER TABLE ONLY dast_scanner_profiles_tags ALTER COLUMN id SET DEFAULT nextval('dast_scanner_profiles_tags_id_seq'::regclass);
+
ALTER TABLE ONLY dast_site_profile_secret_variables ALTER COLUMN id SET DEFAULT nextval('dast_site_profile_secret_variables_id_seq'::regclass);
ALTER TABLE ONLY dast_site_profiles ALTER COLUMN id SET DEFAULT nextval('dast_site_profiles_id_seq'::regclass);
@@ -23725,6 +23986,8 @@ ALTER TABLE ONLY dast_site_validations ALTER COLUMN id SET DEFAULT nextval('dast
ALTER TABLE ONLY dast_sites ALTER COLUMN id SET DEFAULT nextval('dast_sites_id_seq'::regclass);
+ALTER TABLE ONLY dependency_list_exports ALTER COLUMN id SET DEFAULT nextval('dependency_list_exports_id_seq'::regclass);
+
ALTER TABLE ONLY dependency_proxy_blobs ALTER COLUMN id SET DEFAULT nextval('dependency_proxy_blobs_id_seq'::regclass);
ALTER TABLE ONLY dependency_proxy_group_settings ALTER COLUMN id SET DEFAULT nextval('dependency_proxy_group_settings_id_seq'::regclass);
@@ -23791,10 +24054,6 @@ ALTER TABLE ONLY events ALTER COLUMN id SET DEFAULT nextval('events_id_seq'::reg
ALTER TABLE ONLY evidences ALTER COLUMN id SET DEFAULT nextval('evidences_id_seq'::regclass);
-ALTER TABLE ONLY experiment_subjects ALTER COLUMN id SET DEFAULT nextval('experiment_subjects_id_seq'::regclass);
-
-ALTER TABLE ONLY experiments ALTER COLUMN id SET DEFAULT nextval('experiments_id_seq'::regclass);
-
ALTER TABLE ONLY external_approval_rules ALTER COLUMN id SET DEFAULT nextval('external_approval_rules_id_seq'::regclass);
ALTER TABLE ONLY external_approval_rules_protected_branches ALTER COLUMN id SET DEFAULT nextval('external_approval_rules_protected_branches_id_seq'::regclass);
@@ -24019,12 +24278,16 @@ ALTER TABLE ONLY metrics_users_starred_dashboards ALTER COLUMN id SET DEFAULT ne
ALTER TABLE ONLY milestones ALTER COLUMN id SET DEFAULT nextval('milestones_id_seq'::regclass);
+ALTER TABLE ONLY ml_candidate_metadata ALTER COLUMN id SET DEFAULT nextval('ml_candidate_metadata_id_seq'::regclass);
+
ALTER TABLE ONLY ml_candidate_metrics ALTER COLUMN id SET DEFAULT nextval('ml_candidate_metrics_id_seq'::regclass);
ALTER TABLE ONLY ml_candidate_params ALTER COLUMN id SET DEFAULT nextval('ml_candidate_params_id_seq'::regclass);
ALTER TABLE ONLY ml_candidates ALTER COLUMN id SET DEFAULT nextval('ml_candidates_id_seq'::regclass);
+ALTER TABLE ONLY ml_experiment_metadata ALTER COLUMN id SET DEFAULT nextval('ml_experiment_metadata_id_seq'::regclass);
+
ALTER TABLE ONLY ml_experiments ALTER COLUMN id SET DEFAULT nextval('ml_experiments_id_seq'::regclass);
ALTER TABLE ONLY namespace_admin_notes ALTER COLUMN id SET DEFAULT nextval('namespace_admin_notes_id_seq'::regclass);
@@ -24137,6 +24400,12 @@ ALTER TABLE ONLY plan_limits ALTER COLUMN id SET DEFAULT nextval('plan_limits_id
ALTER TABLE ONLY plans ALTER COLUMN id SET DEFAULT nextval('plans_id_seq'::regclass);
+ALTER TABLE ONLY pm_licenses ALTER COLUMN id SET DEFAULT nextval('pm_licenses_id_seq'::regclass);
+
+ALTER TABLE ONLY pm_package_versions ALTER COLUMN id SET DEFAULT nextval('pm_package_versions_id_seq'::regclass);
+
+ALTER TABLE ONLY pm_packages ALTER COLUMN id SET DEFAULT nextval('pm_packages_id_seq'::regclass);
+
ALTER TABLE ONLY pool_repositories ALTER COLUMN id SET DEFAULT nextval('pool_repositories_id_seq'::regclass);
ALTER TABLE ONLY postgres_async_indexes ALTER COLUMN id SET DEFAULT nextval('postgres_async_indexes_id_seq'::regclass);
@@ -24299,8 +24568,12 @@ ALTER TABLE ONLY sentry_issues ALTER COLUMN id SET DEFAULT nextval('sentry_issue
ALTER TABLE ONLY shards ALTER COLUMN id SET DEFAULT nextval('shards_id_seq'::regclass);
+ALTER TABLE ONLY slack_api_scopes ALTER COLUMN id SET DEFAULT nextval('slack_api_scopes_id_seq'::regclass);
+
ALTER TABLE ONLY slack_integrations ALTER COLUMN id SET DEFAULT nextval('slack_integrations_id_seq'::regclass);
+ALTER TABLE ONLY slack_integrations_scopes ALTER COLUMN id SET DEFAULT nextval('slack_integrations_scopes_id_seq'::regclass);
+
ALTER TABLE ONLY smartcard_identities ALTER COLUMN id SET DEFAULT nextval('smartcard_identities_id_seq'::regclass);
ALTER TABLE ONLY snippet_repository_storage_moves ALTER COLUMN id SET DEFAULT nextval('snippet_repository_storage_moves_id_seq'::regclass);
@@ -24449,6 +24722,8 @@ ALTER TABLE ONLY wiki_page_meta ALTER COLUMN id SET DEFAULT nextval('wiki_page_m
ALTER TABLE ONLY wiki_page_slugs ALTER COLUMN id SET DEFAULT nextval('wiki_page_slugs_id_seq'::regclass);
+ALTER TABLE ONLY work_item_hierarchy_restrictions ALTER COLUMN id SET DEFAULT nextval('work_item_hierarchy_restrictions_id_seq'::regclass);
+
ALTER TABLE ONLY work_item_parent_links ALTER COLUMN id SET DEFAULT nextval('work_item_parent_links_id_seq'::regclass);
ALTER TABLE ONLY work_item_types ALTER COLUMN id SET DEFAULT nextval('work_item_types_id_seq'::regclass);
@@ -25054,6 +25329,9 @@ ALTER TABLE ONLY gitlab_partitions_static.product_analytics_events_experimental_
ALTER TABLE ONLY abuse_reports
ADD CONSTRAINT abuse_reports_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY achievements
+ ADD CONSTRAINT achievements_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY agent_activity_events
ADD CONSTRAINT agent_activity_events_pkey PRIMARY KEY (id);
@@ -25540,6 +25818,12 @@ ALTER TABLE ONLY customer_relations_contacts
ALTER TABLE ONLY customer_relations_organizations
ADD CONSTRAINT customer_relations_organizations_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY dast_pre_scan_verification_steps
+ ADD CONSTRAINT dast_pre_scan_verification_steps_pkey PRIMARY KEY (id);
+
+ALTER TABLE ONLY dast_pre_scan_verifications
+ ADD CONSTRAINT dast_pre_scan_verifications_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY dast_profile_schedules
ADD CONSTRAINT dast_profile_schedules_pkey PRIMARY KEY (id);
@@ -25555,6 +25839,9 @@ ALTER TABLE ONLY dast_scanner_profiles_builds
ALTER TABLE ONLY dast_scanner_profiles
ADD CONSTRAINT dast_scanner_profiles_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY dast_scanner_profiles_tags
+ ADD CONSTRAINT dast_scanner_profiles_tags_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY dast_site_profile_secret_variables
ADD CONSTRAINT dast_site_profile_secret_variables_pkey PRIMARY KEY (id);
@@ -25576,6 +25863,9 @@ ALTER TABLE ONLY dast_site_validations
ALTER TABLE ONLY dast_sites
ADD CONSTRAINT dast_sites_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY dependency_list_exports
+ ADD CONSTRAINT dependency_list_exports_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY dependency_proxy_blob_states
ADD CONSTRAINT dependency_proxy_blob_states_pkey PRIMARY KEY (dependency_proxy_blob_id);
@@ -25588,6 +25878,9 @@ ALTER TABLE ONLY dependency_proxy_group_settings
ALTER TABLE ONLY dependency_proxy_image_ttl_group_policies
ADD CONSTRAINT dependency_proxy_image_ttl_group_policies_pkey PRIMARY KEY (group_id);
+ALTER TABLE ONLY dependency_proxy_manifest_states
+ ADD CONSTRAINT dependency_proxy_manifest_states_pkey PRIMARY KEY (dependency_proxy_manifest_id);
+
ALTER TABLE ONLY dependency_proxy_manifests
ADD CONSTRAINT dependency_proxy_manifests_pkey PRIMARY KEY (id);
@@ -25693,12 +25986,6 @@ ALTER TABLE ONLY events
ALTER TABLE ONLY evidences
ADD CONSTRAINT evidences_pkey PRIMARY KEY (id);
-ALTER TABLE ONLY experiment_subjects
- ADD CONSTRAINT experiment_subjects_pkey PRIMARY KEY (id);
-
-ALTER TABLE ONLY experiments
- ADD CONSTRAINT experiments_pkey PRIMARY KEY (id);
-
ALTER TABLE ONLY external_approval_rules
ADD CONSTRAINT external_approval_rules_pkey PRIMARY KEY (id);
@@ -26074,6 +26361,9 @@ ALTER TABLE ONLY milestone_releases
ALTER TABLE ONLY milestones
ADD CONSTRAINT milestones_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY ml_candidate_metadata
+ ADD CONSTRAINT ml_candidate_metadata_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY ml_candidate_metrics
ADD CONSTRAINT ml_candidate_metrics_pkey PRIMARY KEY (id);
@@ -26083,6 +26373,9 @@ ALTER TABLE ONLY ml_candidate_params
ALTER TABLE ONLY ml_candidates
ADD CONSTRAINT ml_candidates_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY ml_experiment_metadata
+ ADD CONSTRAINT ml_experiment_metadata_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY ml_experiments
ADD CONSTRAINT ml_experiments_pkey PRIMARY KEY (id);
@@ -26299,6 +26592,18 @@ ALTER TABLE ONLY plan_limits
ALTER TABLE ONLY plans
ADD CONSTRAINT plans_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY pm_licenses
+ ADD CONSTRAINT pm_licenses_pkey PRIMARY KEY (id);
+
+ALTER TABLE ONLY pm_package_version_licenses
+ ADD CONSTRAINT pm_package_version_licenses_pkey PRIMARY KEY (pm_package_version_id, pm_license_id);
+
+ALTER TABLE ONLY pm_package_versions
+ ADD CONSTRAINT pm_package_versions_pkey PRIMARY KEY (id);
+
+ALTER TABLE ONLY pm_packages
+ ADD CONSTRAINT pm_packages_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY pool_repositories
ADD CONSTRAINT pool_repositories_pkey PRIMARY KEY (id);
@@ -26587,9 +26892,15 @@ ALTER TABLE ONLY service_desk_settings
ALTER TABLE ONLY shards
ADD CONSTRAINT shards_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY slack_api_scopes
+ ADD CONSTRAINT slack_api_scopes_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY slack_integrations
ADD CONSTRAINT slack_integrations_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY slack_integrations_scopes
+ ADD CONSTRAINT slack_integrations_scopes_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY smartcard_identities
ADD CONSTRAINT smartcard_identities_pkey PRIMARY KEY (id);
@@ -26839,9 +27150,15 @@ ALTER TABLE ONLY wiki_page_meta
ALTER TABLE ONLY wiki_page_slugs
ADD CONSTRAINT wiki_page_slugs_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY work_item_hierarchy_restrictions
+ ADD CONSTRAINT work_item_hierarchy_restrictions_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY work_item_parent_links
ADD CONSTRAINT work_item_parent_links_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY work_item_progresses
+ ADD CONSTRAINT work_item_progresses_pkey PRIMARY KEY (issue_id);
+
ALTER TABLE ONLY work_item_types
ADD CONSTRAINT work_item_types_pkey PRIMARY KEY (id);
@@ -27778,8 +28095,6 @@ CREATE INDEX product_analytics_events_exper_project_id_collector_tstamp_idx9 ON
CREATE INDEX product_analytics_events_experi_project_id_collector_tstamp_idx ON gitlab_partitions_static.product_analytics_events_experimental_00 USING btree (project_id, collector_tstamp);
-CREATE INDEX active_billable_users ON users USING btree (id) WHERE (((state)::text = 'active'::text) AND ((user_type IS NULL) OR (user_type = ANY (ARRAY[NULL::integer, 6, 4]))) AND ((user_type IS NULL) OR (user_type <> ALL ('{2,6,1,3,7,8}'::smallint[]))));
-
CREATE INDEX analytics_index_audit_events_part_on_created_at_and_author_id ON ONLY audit_events USING btree (created_at, author_id);
CREATE INDEX analytics_index_events_on_created_at_and_author_id ON events USING btree (created_at, author_id);
@@ -27802,6 +28117,8 @@ CREATE INDEX ca_aggregations_last_incremental_run_at ON analytics_cycle_analytic
CREATE INDEX ci_builds_gitlab_monitor_metrics ON ci_builds USING btree (status, created_at, project_id) WHERE ((type)::text = 'Ci::Build'::text);
+CREATE UNIQUE INDEX ci_job_token_scope_links_source_and_target_project_direction ON ci_job_token_project_scope_links USING btree (source_project_id, target_project_id, direction);
+
CREATE INDEX ci_pipeline_artifacts_on_expire_at_for_removal ON ci_pipeline_artifacts USING btree (expire_at) WHERE ((locked = 0) AND (expire_at IS NOT NULL));
CREATE INDEX code_owner_approval_required ON protected_branches USING btree (project_id, code_owner_approval_required) WHERE (code_owner_approval_required = true);
@@ -27832,10 +28149,18 @@ CREATE INDEX finding_links_on_vulnerability_occurrence_id ON vulnerability_findi
CREATE INDEX i_batched_background_migration_job_transition_logs_on_job_id ON ONLY batched_background_migration_job_transition_logs USING btree (batched_background_migration_job_id);
-CREATE UNIQUE INDEX i_ci_job_token_project_scope_links_on_source_and_target_project ON ci_job_token_project_scope_links USING btree (source_project_id, target_project_id);
-
CREATE INDEX i_compliance_frameworks_on_id_and_created_at ON compliance_management_frameworks USING btree (id, created_at, pipeline_configuration_full_path);
+CREATE INDEX i_dast_pre_scan_verification_steps_on_pre_scan_verification_id ON dast_pre_scan_verification_steps USING btree (dast_pre_scan_verification_id);
+
+CREATE INDEX i_dast_scanner_profiles_tags_on_scanner_profiles_id ON dast_scanner_profiles_tags USING btree (dast_scanner_profile_id);
+
+CREATE UNIQUE INDEX i_pm_licenses_on_spdx_identifier ON pm_licenses USING btree (spdx_identifier);
+
+CREATE UNIQUE INDEX i_pm_package_versions_on_package_id_and_version ON pm_package_versions USING btree (pm_package_id, version);
+
+CREATE UNIQUE INDEX i_pm_packages_purl_type_and_name ON pm_packages USING btree (purl_type, name);
+
CREATE INDEX idx_analytics_devops_adoption_segments_on_namespace_id ON analytics_devops_adoption_segments USING btree (namespace_id);
CREATE INDEX idx_analytics_devops_adoption_snapshots_finalized ON analytics_devops_adoption_snapshots USING btree (namespace_id, end_time) WHERE (recorded_at >= end_time);
@@ -28030,6 +28355,8 @@ CREATE UNIQUE INDEX idx_work_item_types_on_namespace_id_and_name_null_namespace
CREATE INDEX index_abuse_reports_on_user_id ON abuse_reports USING btree (user_id);
+CREATE UNIQUE INDEX "index_achievements_on_namespace_id_LOWER_name" ON achievements USING btree (namespace_id, lower(name));
+
CREATE INDEX index_agent_activity_events_on_agent_id_and_recorded_at_and_id ON agent_activity_events USING btree (agent_id, recorded_at, id);
CREATE INDEX index_agent_activity_events_on_agent_token_id ON agent_activity_events USING btree (agent_token_id) WHERE (agent_token_id IS NOT NULL);
@@ -28178,8 +28505,6 @@ CREATE UNIQUE INDEX index_audit_events_external_audit_on_verification_token ON a
CREATE INDEX index_authentication_events_on_provider ON authentication_events USING btree (provider);
-CREATE INDEX index_authentication_events_on_provider_user_id_created_at ON authentication_events USING btree (provider, user_id, created_at) WHERE (result = 1);
-
CREATE INDEX index_authentication_events_on_user_and_ip_address_and_result ON authentication_events USING btree (user_id, ip_address, result);
CREATE INDEX index_award_emoji_on_awardable_type_and_awardable_id ON award_emoji USING btree (awardable_type, awardable_id);
@@ -28308,6 +28633,8 @@ CREATE INDEX index_bulk_imports_on_user_id ON bulk_imports USING btree (user_id)
CREATE UNIQUE INDEX index_chat_names_on_integration_id_and_team_id_and_chat_id ON chat_names USING btree (integration_id, team_id, chat_id);
+CREATE INDEX index_chat_names_on_team_id_and_chat_id ON chat_names USING btree (team_id, chat_id);
+
CREATE UNIQUE INDEX index_chat_names_on_user_id_and_integration_id ON chat_names USING btree (user_id, integration_id);
CREATE UNIQUE INDEX index_chat_teams_on_namespace_id ON chat_teams USING btree (namespace_id);
@@ -28354,6 +28681,8 @@ CREATE INDEX index_ci_builds_on_commit_id_and_type_and_ref ON ci_builds USING bt
CREATE INDEX index_ci_builds_on_commit_id_artifacts_expired_at_and_id ON ci_builds USING btree (commit_id, artifacts_expire_at, id) WHERE (((type)::text = 'Ci::Build'::text) AND ((retried = false) OR (retried IS NULL)) AND ((name)::text = ANY (ARRAY[('sast'::character varying)::text, ('secret_detection'::character varying)::text, ('dependency_scanning'::character varying)::text, ('container_scanning'::character varying)::text, ('dast'::character varying)::text])));
+CREATE UNIQUE INDEX index_ci_builds_on_id_partition_id_unique ON ci_builds USING btree (id, partition_id);
+
CREATE INDEX index_ci_builds_on_project_id_and_id ON ci_builds USING btree (project_id, id);
CREATE INDEX index_ci_builds_on_project_id_and_name_and_ref ON ci_builds USING btree (project_id, name, ref) WHERE (((type)::text = 'Ci::Build'::text) AND ((status)::text = 'success'::text) AND ((retried = false) OR (retried IS NULL)));
@@ -28470,8 +28799,6 @@ CREATE UNIQUE INDEX index_ci_pipeline_chat_data_on_pipeline_id ON ci_pipeline_ch
CREATE INDEX index_ci_pipeline_messages_on_pipeline_id ON ci_pipeline_messages USING btree (pipeline_id);
-CREATE INDEX index_ci_pipeline_metadata_on_pipeline_id_name ON ci_pipeline_metadata USING btree (pipeline_id, name);
-
CREATE INDEX index_ci_pipeline_metadata_on_project_id ON ci_pipeline_metadata USING btree (project_id);
CREATE UNIQUE INDEX index_ci_pipeline_schedule_variables_on_schedule_id_and_key ON ci_pipeline_schedule_variables USING btree (pipeline_schedule_id, key);
@@ -28766,6 +29093,10 @@ CREATE UNIQUE INDEX index_cycle_analytics_stage_event_hashes_on_hash_sha_256 ON
CREATE UNIQUE INDEX index_daily_build_group_report_results_unique_columns ON ci_daily_build_group_report_results USING btree (project_id, ref_path, date, group_name);
+CREATE UNIQUE INDEX index_dast_pre_scan_verifications_on_ci_pipeline_id ON dast_pre_scan_verifications USING btree (ci_pipeline_id);
+
+CREATE INDEX index_dast_pre_scan_verifications_on_dast_profile_id ON dast_pre_scan_verifications USING btree (dast_profile_id);
+
CREATE INDEX index_dast_profile_schedules_active_next_run_at ON dast_profile_schedules USING btree (active, next_run_at);
CREATE UNIQUE INDEX index_dast_profile_schedules_on_dast_profile_id ON dast_profile_schedules USING btree (dast_profile_id);
@@ -28784,6 +29115,8 @@ CREATE UNIQUE INDEX index_dast_profiles_pipelines_on_ci_pipeline_id ON dast_prof
CREATE UNIQUE INDEX index_dast_scanner_profiles_on_project_id_and_name ON dast_scanner_profiles USING btree (project_id, name);
+CREATE INDEX index_dast_scanner_profiles_tags_on_tag_id ON dast_scanner_profiles_tags USING btree (tag_id);
+
CREATE INDEX index_dast_site_profiles_on_dast_site_id ON dast_site_profiles USING btree (dast_site_id);
CREATE UNIQUE INDEX index_dast_site_profiles_on_project_id_and_name ON dast_site_profiles USING btree (project_id, name);
@@ -28806,6 +29139,10 @@ CREATE UNIQUE INDEX index_dast_sites_on_project_id_and_url ON dast_sites USING b
CREATE UNIQUE INDEX index_dep_prox_manifests_on_group_id_file_name_and_status ON dependency_proxy_manifests USING btree (group_id, file_name, status);
+CREATE INDEX index_dependency_list_exports_on_project_id ON dependency_list_exports USING btree (project_id);
+
+CREATE INDEX index_dependency_list_exports_on_user_id ON dependency_list_exports USING btree (user_id);
+
CREATE INDEX index_dependency_proxy_blob_states_failed_verification ON dependency_proxy_blob_states USING btree (verification_retry_at NULLS FIRST) WHERE (verification_state = 3);
CREATE INDEX index_dependency_proxy_blob_states_needs_verification ON dependency_proxy_blob_states USING btree (verification_state) WHERE ((verification_state = 0) OR (verification_state = 3));
@@ -28956,6 +29293,8 @@ CREATE INDEX index_emails_on_user_id ON emails USING btree (user_id);
CREATE INDEX index_enabled_clusters_on_id ON clusters USING btree (id) WHERE (enabled = true);
+CREATE INDEX index_environments_for_name_search_within_folder ON environments USING btree (project_id, lower(ltrim((name)::text, ((environment_type)::text || '/'::text))) varchar_pattern_ops, state);
+
CREATE INDEX index_environments_on_merge_request_id ON environments USING btree (merge_request_id);
CREATE INDEX index_environments_on_name_varchar_pattern_ops ON environments USING btree (name varchar_pattern_ops);
@@ -29066,16 +29405,6 @@ CREATE UNIQUE INDEX index_events_on_target_type_and_target_id_and_fingerprint ON
CREATE INDEX index_evidences_on_release_id ON evidences USING btree (release_id);
-CREATE INDEX index_experiment_subjects_on_experiment_id ON experiment_subjects USING btree (experiment_id);
-
-CREATE INDEX index_experiment_subjects_on_namespace_id ON experiment_subjects USING btree (namespace_id);
-
-CREATE INDEX index_experiment_subjects_on_project_id ON experiment_subjects USING btree (project_id);
-
-CREATE INDEX index_experiment_subjects_on_user_id ON experiment_subjects USING btree (user_id);
-
-CREATE UNIQUE INDEX index_experiments_on_name ON experiments USING btree (name);
-
CREATE INDEX index_expired_and_not_notified_personal_access_tokens ON personal_access_tokens USING btree (id, expires_at) WHERE ((impersonation = false) AND (revoked = false) AND (expire_notification_delivered = false));
CREATE UNIQUE INDEX index_external_audit_event_destinations_on_namespace_id ON audit_events_external_audit_event_destinations USING btree (namespace_id, destination_url);
@@ -29364,7 +29693,7 @@ CREATE INDEX index_issuable_slas_on_due_at_id_label_applied_issuable_closed ON i
CREATE UNIQUE INDEX index_issuable_slas_on_issue_id ON issuable_slas USING btree (issue_id);
-CREATE INDEX index_issue_assignees_on_user_id ON issue_assignees USING btree (user_id);
+CREATE INDEX index_issue_assignees_on_user_id_and_issue_id ON issue_assignees USING btree (user_id, issue_id);
CREATE UNIQUE INDEX index_issue_crm_contacts_on_issue_id_and_contact_id ON issue_customer_relations_contacts USING btree (issue_id, contact_id);
@@ -29398,8 +29727,6 @@ CREATE INDEX index_issues_on_closed_by_id ON issues USING btree (closed_by_id);
CREATE INDEX index_issues_on_confidential ON issues USING btree (confidential);
-CREATE INDEX index_issues_on_description_trigram ON issues USING gin (description gin_trgm_ops) WITH (fastupdate='false');
-
CREATE INDEX index_issues_on_description_trigram_non_latin ON issues USING gin (description gin_trgm_ops) WHERE (((title)::text !~ similar_escape('[\u0000-\u02FF\u1E00-\u1EFF\u2070-\u218F]*'::text, NULL::text)) OR (description !~ similar_escape('[\u0000-\u02FF\u1E00-\u1EFF\u2070-\u218F]*'::text, NULL::text)));
CREATE INDEX index_issues_on_duplicated_to_id ON issues USING btree (duplicated_to_id) WHERE (duplicated_to_id IS NOT NULL);
@@ -29436,14 +29763,14 @@ CREATE INDEX index_issues_on_promoted_to_epic_id ON issues USING btree (promoted
CREATE INDEX index_issues_on_sprint_id ON issues USING btree (sprint_id);
-CREATE INDEX index_issues_on_title_trigram ON issues USING gin (title gin_trgm_ops) WITH (fastupdate='false');
-
CREATE INDEX index_issues_on_title_trigram_non_latin ON issues USING gin (title gin_trgm_ops) WHERE (((title)::text !~ similar_escape('[\u0000-\u02FF\u1E00-\u1EFF\u2070-\u218F]*'::text, NULL::text)) OR (description !~ similar_escape('[\u0000-\u02FF\u1E00-\u1EFF\u2070-\u218F]*'::text, NULL::text)));
CREATE INDEX index_issues_on_updated_at ON issues USING btree (updated_at);
CREATE INDEX index_issues_on_updated_by_id ON issues USING btree (updated_by_id) WHERE (updated_by_id IS NOT NULL);
+CREATE INDEX index_issues_on_work_item_type_id ON issues USING btree (work_item_type_id);
+
CREATE INDEX index_iterations_cadences_on_group_id ON iterations_cadences USING btree (group_id);
CREATE UNIQUE INDEX index_jira_connect_installations_on_client_key ON jira_connect_installations USING btree (client_key);
@@ -29550,6 +29877,16 @@ CREATE INDEX index_lists_on_user_id ON lists USING btree (user_id);
CREATE INDEX index_loose_foreign_keys_deleted_records_for_partitioned_query ON ONLY loose_foreign_keys_deleted_records USING btree (partition, fully_qualified_table_name, consume_after, id) WHERE (status = 1);
+CREATE INDEX index_manifest_states_failed_verification ON dependency_proxy_manifest_states USING btree (verification_retry_at NULLS FIRST) WHERE (verification_state = 3);
+
+CREATE INDEX index_manifest_states_needs_verification ON dependency_proxy_manifest_states USING btree (verification_state) WHERE ((verification_state = 0) OR (verification_state = 3));
+
+CREATE INDEX index_manifest_states_on_dependency_proxy_manifest_id ON dependency_proxy_manifest_states USING btree (dependency_proxy_manifest_id);
+
+CREATE INDEX index_manifest_states_on_verification_state ON dependency_proxy_manifest_states USING btree (verification_state);
+
+CREATE INDEX index_manifest_states_pending_verification ON dependency_proxy_manifest_states USING btree (verified_at NULLS FIRST) WHERE (verification_state = 0);
+
CREATE INDEX index_member_roles_on_namespace_id ON member_roles USING btree (namespace_id);
CREATE INDEX index_member_tasks_on_member_id ON member_tasks USING btree (member_id);
@@ -29574,6 +29911,8 @@ CREATE INDEX index_members_on_requested_at ON members USING btree (requested_at)
CREATE INDEX index_members_on_source_id_and_source_type ON members USING btree (source_id, source_type);
+CREATE INDEX index_members_on_source_state_type_access_level_and_user_id ON members USING btree (source_id, source_type, state, type, access_level, user_id) WHERE ((requested_at IS NULL) AND (invite_token IS NULL));
+
CREATE INDEX index_members_on_user_id_and_access_level_requested_at_is_null ON members USING btree (user_id, access_level) WHERE (requested_at IS NULL);
CREATE INDEX index_members_on_user_id_created_at ON members USING btree (user_id, created_at) WHERE ((ldap = true) AND ((type)::text = 'GroupMember'::text) AND ((source_type)::text = 'Namespace'::text));
@@ -29648,6 +29987,8 @@ CREATE INDEX index_merge_requests_on_assignee_id ON merge_requests USING btree (
CREATE INDEX index_merge_requests_on_author_id ON merge_requests USING btree (author_id);
+CREATE INDEX index_merge_requests_on_author_id_and_created_at ON merge_requests USING btree (author_id, created_at);
+
CREATE INDEX index_merge_requests_on_author_id_and_id ON merge_requests USING btree (author_id, id);
CREATE INDEX index_merge_requests_on_author_id_and_target_project_id ON merge_requests USING btree (author_id, target_project_id);
@@ -29730,6 +30071,10 @@ CREATE INDEX index_milestones_on_title_trigram ON milestones USING gin (title gi
CREATE INDEX index_mirror_data_non_scheduled_or_started ON project_mirror_data USING btree (next_execution_timestamp, retry_count) WHERE ((status)::text <> ALL ('{scheduled,started}'::text[]));
+CREATE UNIQUE INDEX index_ml_candidate_metadata_on_candidate_id_and_name ON ml_candidate_metadata USING btree (candidate_id, name);
+
+CREATE INDEX index_ml_candidate_metadata_on_name ON ml_candidate_metadata USING btree (name);
+
CREATE INDEX index_ml_candidate_metrics_on_candidate_id ON ml_candidate_metrics USING btree (candidate_id);
CREATE INDEX index_ml_candidate_params_on_candidate_id ON ml_candidate_params USING btree (candidate_id);
@@ -29740,6 +30085,8 @@ CREATE UNIQUE INDEX index_ml_candidates_on_experiment_id_and_iid ON ml_candidate
CREATE INDEX index_ml_candidates_on_user_id ON ml_candidates USING btree (user_id);
+CREATE UNIQUE INDEX index_ml_experiment_metadata_on_experiment_id_and_name ON ml_experiment_metadata USING btree (experiment_id, name);
+
CREATE UNIQUE INDEX index_ml_experiments_on_project_id_and_iid ON ml_experiments USING btree (project_id, iid);
CREATE UNIQUE INDEX index_ml_experiments_on_project_id_and_name ON ml_experiments USING btree (project_id, name);
@@ -29814,6 +30161,8 @@ CREATE INDEX index_namespaces_on_traversal_ids_for_groups_btree ON namespaces US
CREATE INDEX index_namespaces_on_type_and_id ON namespaces USING btree (type, id);
+CREATE INDEX index_namespaces_on_type_and_visibility_and_parent_id ON namespaces USING btree (id) WHERE (((type)::text = 'Group'::text) AND (parent_id IS NULL) AND (visibility_level <> 20));
+
CREATE INDEX index_namespaces_public_groups_name_id ON namespaces USING btree (name, id) WHERE (((type)::text = 'Group'::text) AND (visibility_level = 20));
CREATE INDEX index_namespaces_sync_events_on_namespace_id ON namespaces_sync_events USING btree (namespace_id);
@@ -29884,6 +30233,10 @@ CREATE UNIQUE INDEX index_on_instance_statistics_recorded_at_and_identifier ON a
CREATE INDEX index_on_issues_closed_incidents_by_project_id_and_closed_at ON issues USING btree (project_id, closed_at) WHERE ((issue_type = 1) AND (state_id = 2));
+CREATE INDEX index_on_issues_health_status_asc_order ON issues USING btree (project_id, health_status, id DESC, state_id, issue_type);
+
+CREATE INDEX index_on_issues_health_status_desc_order ON issues USING btree (project_id, health_status DESC NULLS LAST, id DESC, state_id, issue_type);
+
CREATE INDEX index_on_label_links_all_columns ON label_links USING btree (target_id, label_id, target_type);
CREATE INDEX index_on_merge_request_reviewers_user_id_and_state ON merge_request_reviewers USING btree (user_id, state) WHERE (state = 2);
@@ -30102,10 +30455,18 @@ CREATE UNIQUE INDEX index_personal_access_tokens_on_token_digest ON personal_acc
CREATE INDEX index_personal_access_tokens_on_user_id ON personal_access_tokens USING btree (user_id);
+CREATE INDEX index_pipeline_metadata_on_pipeline_id_name_lower_text_pattern ON ci_pipeline_metadata USING btree (pipeline_id, lower(name) text_pattern_ops);
+
CREATE UNIQUE INDEX index_plan_limits_on_plan_id ON plan_limits USING btree (plan_id);
CREATE UNIQUE INDEX index_plans_on_name ON plans USING btree (name);
+CREATE INDEX index_pm_package_version_licenses_on_pm_license_id ON pm_package_version_licenses USING btree (pm_license_id);
+
+CREATE INDEX index_pm_package_version_licenses_on_pm_package_version_id ON pm_package_version_licenses USING btree (pm_package_version_id);
+
+CREATE INDEX index_pm_package_versions_on_pm_package_id ON pm_package_versions USING btree (pm_package_id);
+
CREATE UNIQUE INDEX index_pool_repositories_on_disk_path ON pool_repositories USING btree (disk_path);
CREATE INDEX index_pool_repositories_on_shard_id ON pool_repositories USING btree (shard_id);
@@ -30208,7 +30569,7 @@ CREATE UNIQUE INDEX index_project_repository_states_on_project_id ON project_rep
CREATE INDEX index_project_repository_storage_moves_on_project_id ON project_repository_storage_moves USING btree (project_id);
-CREATE INDEX index_project_settings_on_legacy_open_source_license_available ON project_settings USING btree (legacy_open_source_license_available) WHERE (legacy_open_source_license_available = true);
+CREATE INDEX index_project_settings_on_legacy_os_license_project_id ON project_settings USING btree (project_id) WHERE (legacy_open_source_license_available = true);
CREATE INDEX index_project_settings_on_project_id_partially ON project_settings USING btree (project_id) WHERE (has_vulnerabilities IS TRUE);
@@ -30568,12 +30929,12 @@ CREATE INDEX index_secure_ci_builds_on_user_id_name_created_at ON ci_builds USIN
CREATE INDEX index_security_ci_builds_on_name_and_id_parser_features ON ci_builds USING btree (name, id) WHERE (((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('sast'::character varying)::text, ('secret_detection'::character varying)::text, ('coverage_fuzzing'::character varying)::text, ('license_scanning'::character varying)::text, ('apifuzzer_fuzz'::character varying)::text, ('apifuzzer_fuzz_dnd'::character varying)::text])) AND ((type)::text = 'Ci::Build'::text));
+CREATE INDEX index_security_scans_for_non_purged_records ON security_scans USING btree (created_at, id) WHERE (status <> 6);
+
CREATE INDEX index_security_scans_on_created_at ON security_scans USING btree (created_at);
CREATE INDEX index_security_scans_on_date_created_at_and_id ON security_scans USING btree (date(timezone('UTC'::text, created_at)), id);
-CREATE INDEX index_security_scans_on_id_for_non_purged_records ON security_scans USING btree (id) WHERE (status <> 6);
-
CREATE INDEX index_security_scans_on_length_of_errors ON security_scans USING btree (pipeline_id, jsonb_array_length(COALESCE((info -> 'errors'::text), '[]'::jsonb)));
CREATE INDEX index_security_scans_on_length_of_warnings ON security_scans USING btree (pipeline_id, jsonb_array_length(COALESCE((info -> 'warnings'::text), '[]'::jsonb)));
@@ -30612,6 +30973,10 @@ CREATE UNIQUE INDEX index_shards_on_name ON shards USING btree (name);
CREATE UNIQUE INDEX index_site_profile_secret_variables_on_site_profile_id_and_key ON dast_site_profile_secret_variables USING btree (dast_site_profile_id, key);
+CREATE UNIQUE INDEX index_slack_api_scopes_on_name ON slack_api_scopes USING btree (name);
+
+CREATE UNIQUE INDEX index_slack_api_scopes_on_name_and_integration ON slack_integrations_scopes USING btree (slack_integration_id, slack_api_scope_id);
+
CREATE INDEX index_slack_integrations_on_integration_id ON slack_integrations USING btree (integration_id);
CREATE UNIQUE INDEX index_slack_integrations_on_team_id_and_alias ON slack_integrations USING btree (team_id, alias);
@@ -30710,6 +31075,8 @@ CREATE INDEX index_subscriptions_on_project_id ON subscriptions USING btree (pro
CREATE UNIQUE INDEX index_subscriptions_on_subscribable_and_user_id_and_project_id ON subscriptions USING btree (subscribable_id, subscribable_type, user_id, project_id);
+CREATE INDEX index_successful_authentication_events_for_metrics ON authentication_events USING btree (user_id, provider, created_at) WHERE (result = 1);
+
CREATE INDEX index_successful_deployments_on_cluster_id_and_environment_id ON deployments USING btree (cluster_id, environment_id) WHERE (status = 2);
CREATE UNIQUE INDEX index_suggestions_on_note_id_and_relative_order ON suggestions USING btree (note_id, relative_order);
@@ -30900,6 +31267,8 @@ CREATE INDEX index_user_statuses_on_user_id ON user_statuses USING btree (user_i
CREATE UNIQUE INDEX index_user_synced_attributes_metadata_on_user_id ON user_synced_attributes_metadata USING btree (user_id);
+CREATE INDEX index_users_for_active_billable ON users USING btree (id) WHERE (((state)::text = 'active'::text) AND ((user_type IS NULL) OR (user_type = ANY (ARRAY[NULL::integer, 6, 4]))) AND ((user_type IS NULL) OR (user_type <> ALL ('{1,2,3,4,5,6,7,8,9,11}'::smallint[]))));
+
CREATE INDEX index_users_on_accepted_term_id ON users USING btree (accepted_term_id);
CREATE INDEX index_users_on_admin ON users USING btree (admin);
@@ -30998,10 +31367,6 @@ CREATE INDEX index_vulnerabilities_on_resolved_by_id ON vulnerabilities USING bt
CREATE INDEX index_vulnerabilities_on_start_date_sourcing_milestone_id ON vulnerabilities USING btree (start_date_sourcing_milestone_id);
-CREATE INDEX index_vulnerabilities_on_state_case_id ON vulnerabilities USING btree (array_position(ARRAY[(1)::smallint, (4)::smallint, (3)::smallint, (2)::smallint], state), id DESC);
-
-CREATE INDEX index_vulnerabilities_on_state_case_id_desc ON vulnerabilities USING btree (array_position(ARRAY[(1)::smallint, (4)::smallint, (3)::smallint, (2)::smallint], state) DESC, id DESC);
-
CREATE INDEX index_vulnerabilities_on_updated_by_id ON vulnerabilities USING btree (updated_by_id);
CREATE INDEX index_vulnerabilities_project_id_and_id_on_default_branch ON vulnerabilities USING btree (project_id, id) WHERE (present_on_default_branch IS TRUE);
@@ -31152,6 +31517,12 @@ CREATE UNIQUE INDEX index_wiki_page_slugs_on_slug_and_wiki_page_meta_id ON wiki_
CREATE INDEX index_wiki_page_slugs_on_wiki_page_meta_id ON wiki_page_slugs USING btree (wiki_page_meta_id);
+CREATE INDEX index_work_item_hierarchy_restrictions_on_child_type_id ON work_item_hierarchy_restrictions USING btree (child_type_id);
+
+CREATE UNIQUE INDEX index_work_item_hierarchy_restrictions_on_parent_and_child ON work_item_hierarchy_restrictions USING btree (parent_type_id, child_type_id);
+
+CREATE INDEX index_work_item_hierarchy_restrictions_on_parent_type_id ON work_item_hierarchy_restrictions USING btree (parent_type_id);
+
CREATE UNIQUE INDEX index_work_item_parent_links_on_work_item_id ON work_item_parent_links USING btree (work_item_id);
CREATE INDEX index_work_item_parent_links_on_work_item_parent_id ON work_item_parent_links USING btree (work_item_parent_id);
@@ -31230,6 +31601,8 @@ CREATE INDEX scan_finding_approval_mr_rule_index_id ON approval_merge_request_ru
CREATE INDEX scan_finding_approval_mr_rule_index_merge_request_id ON approval_merge_request_rules USING btree (merge_request_id) WHERE (report_type = 4);
+CREATE INDEX scan_finding_approval_mr_rule_index_mr_id_and_created_at ON approval_merge_request_rules USING btree (merge_request_id, created_at) WHERE (report_type = 4);
+
CREATE INDEX scan_finding_approval_project_rule_index_created_at_project_id ON approval_project_rules USING btree (created_at, project_id) WHERE (report_type = 4);
CREATE INDEX scan_finding_approval_project_rule_index_project_id ON approval_project_rules USING btree (project_id) WHERE (report_type = 4);
@@ -31256,7 +31629,9 @@ CREATE UNIQUE INDEX taggings_idx ON taggings USING btree (tag_id, taggable_id, t
CREATE UNIQUE INDEX term_agreements_unique_index ON term_agreements USING btree (user_id, term_id);
-CREATE INDEX tmp_idx_for_vulnerability_feedback_migration ON vulnerability_feedback USING btree (migrated_to_state_transition, feedback_type) WHERE ((migrated_to_state_transition = false) AND (feedback_type = 0));
+CREATE INDEX tmp_idx_for_feedback_comment_processing ON vulnerability_feedback USING btree (id) WHERE (char_length(comment) > 50000);
+
+CREATE INDEX tmp_idx_for_vulnerability_feedback_migration ON vulnerability_feedback USING btree (id) WHERE ((migrated_to_state_transition = false) AND (feedback_type = 0));
CREATE INDEX tmp_idx_vulnerabilities_on_id_where_report_type_7_99 ON vulnerabilities USING btree (id) WHERE (report_type = ANY (ARRAY[7, 99]));
@@ -31276,22 +31651,16 @@ CREATE INDEX tmp_index_for_null_member_namespace_id ON members USING btree (memb
CREATE INDEX tmp_index_for_project_namespace_id_migration_on_routes ON routes USING btree (id) WHERE ((namespace_id IS NULL) AND ((source_type)::text = 'Project'::text));
-CREATE INDEX tmp_index_issues_on_issue_type_and_id ON issues USING btree (issue_type, id);
-
CREATE INDEX tmp_index_members_on_state ON members USING btree (state) WHERE (state = 2);
CREATE INDEX tmp_index_migrated_container_registries ON container_repositories USING btree (project_id) WHERE ((migration_state = 'import_done'::text) OR (created_at >= '2022-01-23 00:00:00'::timestamp without time zone));
-CREATE UNIQUE INDEX tmp_index_on_tmp_project_id_on_namespaces ON namespaces USING btree (tmp_project_id);
-
CREATE INDEX tmp_index_on_vulnerabilities_non_dismissed ON vulnerabilities USING btree (id) WHERE (state <> 2);
CREATE INDEX tmp_index_project_statistics_cont_registry_size ON project_statistics USING btree (project_id) WHERE (container_registry_size = 0);
CREATE INDEX tmp_index_project_statistics_uploads_size ON project_statistics USING btree (project_id) WHERE (uploads_size <> 0);
-CREATE INDEX tmp_index_vulnerability_occurrences_on_id_and_scanner_id ON vulnerability_occurrences USING btree (id, scanner_id) WHERE (report_type = ANY (ARRAY[7, 99]));
-
CREATE UNIQUE INDEX uniq_pkgs_deb_grp_architectures_on_distribution_id_and_name ON packages_debian_group_architectures USING btree (distribution_id, name);
CREATE UNIQUE INDEX uniq_pkgs_deb_grp_components_on_distribution_id_and_name ON packages_debian_group_components USING btree (distribution_id, name);
@@ -32990,6 +33359,9 @@ ALTER TABLE ONLY approval_merge_request_rules
ALTER TABLE ONLY deploy_keys_projects
ADD CONSTRAINT fk_58a901ca7e FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY dependency_list_exports
+ ADD CONSTRAINT fk_5b3d11e1ef FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;
+
ALTER TABLE ONLY dast_scanner_profiles_builds
ADD CONSTRAINT fk_5d46286ad3 FOREIGN KEY (dast_scanner_profile_id) REFERENCES dast_scanner_profiles(id) ON DELETE CASCADE;
@@ -33035,9 +33407,6 @@ ALTER TABLE ONLY project_wiki_repository_states
ALTER TABLE ONLY merge_requests
ADD CONSTRAINT fk_6a5165a692 FOREIGN KEY (milestone_id) REFERENCES milestones(id) ON DELETE SET NULL;
-ALTER TABLE ONLY namespaces
- ADD CONSTRAINT fk_6a77f66919 FOREIGN KEY (tmp_project_id) REFERENCES projects(id) ON DELETE CASCADE;
-
ALTER TABLE ONLY geo_event_log
ADD CONSTRAINT fk_6ada82d42a FOREIGN KEY (container_repository_updated_event_id) REFERENCES geo_container_repository_updated_events(id) ON DELETE CASCADE;
@@ -33050,9 +33419,6 @@ ALTER TABLE ONLY dast_profile_schedules
ALTER TABLE ONLY vulnerability_merge_request_links
ADD CONSTRAINT fk_6d7aa8796e FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
-ALTER TABLE ONLY issues
- ADD CONSTRAINT fk_6e10d4d38a FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE SET NULL;
-
ALTER TABLE ONLY projects
ADD CONSTRAINT fk_6e5c14658a FOREIGN KEY (pool_repository_id) REFERENCES pool_repositories(id) ON DELETE SET NULL;
@@ -33140,9 +33506,6 @@ ALTER TABLE ONLY import_export_uploads
ALTER TABLE ONLY push_rules
ADD CONSTRAINT fk_83b29894de FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
-ALTER TABLE ONLY experiment_subjects
- ADD CONSTRAINT fk_842649f2f5 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
-
ALTER TABLE ONLY merge_request_diffs
ADD CONSTRAINT fk_8483f3258f FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
@@ -33401,6 +33764,9 @@ ALTER TABLE ONLY geo_event_log
ALTER TABLE ONLY issues
ADD CONSTRAINT fk_c63cbf6c25 FOREIGN KEY (closed_by_id) REFERENCES users(id) ON DELETE SET NULL;
+ALTER TABLE ONLY issues
+ ADD CONSTRAINT fk_c78fbacd64 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY agent_activity_events
ADD CONSTRAINT fk_c815368376 FOREIGN KEY (agent_id) REFERENCES cluster_agents(id) ON DELETE CASCADE;
@@ -33428,9 +33794,6 @@ ALTER TABLE ONLY external_status_checks_protected_branches
ALTER TABLE ONLY dast_profiles_pipelines
ADD CONSTRAINT fk_cc206a8c13 FOREIGN KEY (dast_profile_id) REFERENCES dast_profiles(id) ON DELETE CASCADE;
-ALTER TABLE ONLY experiment_subjects
- ADD CONSTRAINT fk_ccc28f8ceb FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
-
ALTER TABLE ONLY todos
ADD CONSTRAINT fk_ccf0373936 FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE CASCADE;
@@ -33482,6 +33845,9 @@ ALTER TABLE ONLY system_note_metadata
ALTER TABLE ONLY sbom_occurrences
ADD CONSTRAINT fk_d857c6edc1 FOREIGN KEY (component_id) REFERENCES sbom_components(id) ON DELETE CASCADE;
+ALTER TABLE ONLY dependency_list_exports
+ ADD CONSTRAINT fk_d871d74675 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY todos
ADD CONSTRAINT fk_d94154aa95 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
@@ -33509,9 +33875,6 @@ ALTER TABLE ONLY protected_branches
ALTER TABLE ONLY issues
ADD CONSTRAINT fk_df75a7c8b8 FOREIGN KEY (promoted_to_epic_id) REFERENCES epics(id) ON DELETE SET NULL;
-ALTER TABLE ONLY experiment_subjects
- ADD CONSTRAINT fk_dfc3e211d4 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
-
ALTER TABLE ONLY ci_resources
ADD CONSTRAINT fk_e169a8e3d5 FOREIGN KEY (build_id) REFERENCES ci_builds(id) ON DELETE SET NULL;
@@ -33674,6 +34037,9 @@ ALTER TABLE ONLY ip_restrictions
ALTER TABLE ONLY terraform_state_versions
ADD CONSTRAINT fk_rails_04f176e239 FOREIGN KEY (terraform_state_id) REFERENCES terraform_states(id) ON DELETE CASCADE;
+ALTER TABLE ONLY work_item_hierarchy_restrictions
+ ADD CONSTRAINT fk_rails_08cd7fef58 FOREIGN KEY (child_type_id) REFERENCES work_item_types(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY trending_projects
ADD CONSTRAINT fk_rails_09feecd872 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -33899,6 +34265,9 @@ ALTER TABLE ONLY lfs_file_locks
ALTER TABLE ONLY project_alerting_settings
ADD CONSTRAINT fk_rails_27a84b407d FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY work_item_hierarchy_restrictions
+ ADD CONSTRAINT fk_rails_27bb3a10ba FOREIGN KEY (parent_type_id) REFERENCES work_item_types(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY user_credit_card_validations
ADD CONSTRAINT fk_rails_27ebc03cbf FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
@@ -33950,6 +34319,9 @@ ALTER TABLE ONLY issuable_severities
ALTER TABLE ONLY saml_providers
ADD CONSTRAINT fk_rails_306d459be7 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+ALTER TABLE ONLY pm_package_version_licenses
+ ADD CONSTRAINT fk_rails_30ddb7f837 FOREIGN KEY (pm_package_version_id) REFERENCES pm_package_versions(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY resource_state_events
ADD CONSTRAINT fk_rails_3112bba7dc FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
@@ -34166,6 +34538,9 @@ ALTER TABLE ONLY ci_pipeline_metadata
ALTER TABLE ONLY project_repository_storage_moves
ADD CONSTRAINT fk_rails_5106dbd44a FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY ml_candidate_metadata
+ ADD CONSTRAINT fk_rails_5117dddf22 FOREIGN KEY (candidate_id) REFERENCES ml_candidates(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY bulk_import_configurations
ADD CONSTRAINT fk_rails_536b96bff1 FOREIGN KEY (bulk_import_id) REFERENCES bulk_imports(id) ON DELETE CASCADE;
@@ -34343,6 +34718,9 @@ ALTER TABLE ONLY plan_limits
ALTER TABLE ONLY operations_feature_flags_issues
ADD CONSTRAINT fk_rails_6a8856ca4f FOREIGN KEY (feature_flag_id) REFERENCES operations_feature_flags(id) ON DELETE CASCADE;
+ALTER TABLE ONLY ml_experiment_metadata
+ ADD CONSTRAINT fk_rails_6b39844d44 FOREIGN KEY (experiment_id) REFERENCES ml_experiments(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY error_tracking_errors
ADD CONSTRAINT fk_rails_6b41f837ba FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -34397,6 +34775,9 @@ ALTER TABLE ONLY merge_request_context_commit_diff_files
ALTER TABLE ONLY group_crm_settings
ADD CONSTRAINT fk_rails_74fdf2f13d FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+ALTER TABLE ONLY pm_package_version_licenses
+ ADD CONSTRAINT fk_rails_7520ea026d FOREIGN KEY (pm_license_id) REFERENCES pm_licenses(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY clusters_applications_ingress
ADD CONSTRAINT fk_rails_753a7b41c1 FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE;
@@ -34457,6 +34838,9 @@ ALTER TABLE ONLY application_settings
ALTER TABLE ONLY clusters_kubernetes_namespaces
ADD CONSTRAINT fk_rails_7e7688ecaf FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE;
+ALTER TABLE ONLY dependency_proxy_manifest_states
+ ADD CONSTRAINT fk_rails_806cf07a3c FOREIGN KEY (dependency_proxy_manifest_id) REFERENCES dependency_proxy_manifests(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY ci_job_artifact_states
ADD CONSTRAINT fk_rails_80a9cba3b2 FOREIGN KEY (job_artifact_id) REFERENCES ci_job_artifacts(id) ON DELETE CASCADE;
@@ -34505,6 +34889,9 @@ ALTER TABLE ONLY ci_runner_namespaces
ALTER TABLE ONLY software_license_policies
ADD CONSTRAINT fk_rails_87b2247ce5 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY achievements
+ ADD CONSTRAINT fk_rails_87e990f752 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY protected_environment_deploy_access_levels
ADD CONSTRAINT fk_rails_898a13b650 FOREIGN KEY (protected_environment_id) REFERENCES protected_environments(id) ON DELETE CASCADE;
@@ -34526,6 +34913,9 @@ ALTER TABLE ONLY cluster_projects
ALTER TABLE ONLY project_pages_metadata
ADD CONSTRAINT fk_rails_8c28a61485 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY work_item_progresses
+ ADD CONSTRAINT fk_rails_8c584bfb37 FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY packages_conan_metadata
ADD CONSTRAINT fk_rails_8c68cfec8b FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE;
@@ -34622,6 +35012,9 @@ ALTER TABLE ONLY error_tracking_client_keys
ALTER TABLE ONLY pages_deployments
ADD CONSTRAINT fk_rails_993b88f59a FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY dast_pre_scan_verification_steps
+ ADD CONSTRAINT fk_rails_9990fc2adf FOREIGN KEY (dast_pre_scan_verification_id) REFERENCES dast_pre_scan_verifications(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY vulnerability_exports
ADD CONSTRAINT fk_rails_9aff2c3b45 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -34868,6 +35261,9 @@ ALTER TABLE ONLY atlassian_identities
ALTER TABLE ONLY serverless_domain_cluster
ADD CONSTRAINT fk_rails_c09009dee1 FOREIGN KEY (pages_domain_id) REFERENCES pages_domains(id) ON DELETE CASCADE;
+ALTER TABLE ONLY slack_integrations_scopes
+ ADD CONSTRAINT fk_rails_c0e018a6fe FOREIGN KEY (slack_api_scope_id) REFERENCES slack_api_scopes(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY packages_npm_metadata
ADD CONSTRAINT fk_rails_c0e5fce6f3 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE;
@@ -34961,6 +35357,9 @@ ALTER TABLE ONLY resource_iteration_events
ALTER TABLE ONLY member_roles
ADD CONSTRAINT fk_rails_cf0ee35814 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+ALTER TABLE ONLY pm_package_versions
+ ADD CONSTRAINT fk_rails_cf94c3e601 FOREIGN KEY (pm_package_id) REFERENCES pm_packages(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY upload_states
ADD CONSTRAINT fk_rails_d00f153613 FOREIGN KEY (upload_id) REFERENCES uploads(id) ON DELETE CASCADE;
@@ -35039,6 +35438,9 @@ ALTER TABLE ONLY incident_management_timeline_event_tags
ALTER TABLE ONLY user_callouts
ADD CONSTRAINT fk_rails_ddfdd80f3d FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+ALTER TABLE ONLY dast_scanner_profiles_tags
+ ADD CONSTRAINT fk_rails_deb79b7f19 FOREIGN KEY (dast_scanner_profile_id) REFERENCES dast_scanner_profiles(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY vulnerability_feedback
ADD CONSTRAINT fk_rails_debd54e456 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -35135,6 +35537,9 @@ ALTER TABLE ONLY alert_management_alert_user_mentions
ALTER TABLE ONLY snippet_statistics
ADD CONSTRAINT fk_rails_ebc283ccf1 FOREIGN KEY (snippet_id) REFERENCES snippets(id) ON DELETE CASCADE;
+ALTER TABLE ONLY slack_integrations_scopes
+ ADD CONSTRAINT fk_rails_ece1eb6772 FOREIGN KEY (slack_integration_id) REFERENCES slack_integrations(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY iterations_cadences
ADD CONSTRAINT fk_rails_ece400c55a FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
@@ -35147,9 +35552,6 @@ ALTER TABLE ONLY project_security_settings
ALTER TABLE ONLY packages_debian_group_distributions
ADD CONSTRAINT fk_rails_ede0bb937f FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE SET NULL;
-ALTER TABLE ONLY experiment_subjects
- ADD CONSTRAINT fk_rails_ede5754774 FOREIGN KEY (experiment_id) REFERENCES experiments(id) ON DELETE CASCADE;
-
ALTER TABLE ONLY ci_daily_build_group_report_results
ADD CONSTRAINT fk_rails_ee072d13b3 FOREIGN KEY (last_pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
@@ -35171,6 +35573,9 @@ ALTER TABLE ONLY fork_network_members
ALTER TABLE ONLY security_orchestration_policy_rule_schedules
ADD CONSTRAINT fk_rails_efe1d9b133 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+ALTER TABLE ONLY dast_pre_scan_verifications
+ ADD CONSTRAINT fk_rails_f08d9312a8 FOREIGN KEY (dast_profile_id) REFERENCES dast_profiles(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY prometheus_alerts
ADD CONSTRAINT fk_rails_f0e8db86aa FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
diff --git a/doc/.vale/gitlab/CurrentStatus.yml b/doc/.vale/gitlab/CurrentStatus.yml
index 57b95dcf4ac..9972573b406 100644
--- a/doc/.vale/gitlab/CurrentStatus.yml
+++ b/doc/.vale/gitlab/CurrentStatus.yml
@@ -1,14 +1,13 @@
---
-# Suggestion: gitlab.CurrentStatus
+# Warning: gitlab.CurrentStatus
#
# Checks for words that indicate a product or feature may change in the future.
#
# For a list of all options, see https://vale.sh/docs/topics/styles/
extends: existence
message: "Remove '%s'. The documentation reflects the current state of the product."
-level: suggestion
+level: warning
ignorecase: true
link: https://docs.gitlab.com/ee/development/documentation/versions.html#promising-features-in-future-versions
tokens:
- currently
- - yet
diff --git a/doc/.vale/gitlab/ElementDescriptors.yml b/doc/.vale/gitlab/ElementDescriptors.yml
index f3573f5ce65..f806f5878e1 100644
--- a/doc/.vale/gitlab/ElementDescriptors.yml
+++ b/doc/.vale/gitlab/ElementDescriptors.yml
@@ -1,13 +1,14 @@
---
-# Suggestion: gitlab.ElementDescriptors
+# Warning: gitlab.ElementDescriptors
#
-# Suggests the correct way to describe elements in a form.
+# Suggests the correct way to describe a button.
#
# For a list of all options, see https://vale.sh/docs/topics/styles/
-extends: substitution
-message: "When describing elements, %s '%s'."
-link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#language
-level: suggestion
+extends: existence
+message: "If possible, rewrite to remove 'button'."
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/word_list.html#button
+level: warning
ignorecase: true
-swap:
- button: 'if possible, rewrite to remove'
+scope: raw
+raw:
+ - \*\*.+?\*\* button
diff --git a/doc/.vale/gitlab/InclusionAbleism.yml b/doc/.vale/gitlab/InclusionAbleism.yml
index 7419430c8a2..3ee356155cd 100644
--- a/doc/.vale/gitlab/InclusionAbleism.yml
+++ b/doc/.vale/gitlab/InclusionAbleism.yml
@@ -1,5 +1,5 @@
---
-# Suggestion: gitlab.InclusionAbleism
+# Warning: gitlab.InclusionAbleism
#
# Suggests alternatives for words that foster ableism.
#
@@ -7,7 +7,7 @@
extends: substitution
message: "Use inclusive language. Consider '%s' instead of '%s'."
link: https://docs.gitlab.com/ee/development/documentation/styleguide/word_list.html
-level: suggestion
+level: warning
ignorecase: true
swap:
sanity (?:check|test): check for completeness
diff --git a/doc/.vale/gitlab/InclusionGender.yml b/doc/.vale/gitlab/InclusionGender.yml
index ce8861b6a09..dcf8c1930ec 100644
--- a/doc/.vale/gitlab/InclusionGender.yml
+++ b/doc/.vale/gitlab/InclusionGender.yml
@@ -1,5 +1,5 @@
---
-# Suggestion: gitlab.InclusionGender
+# Warning: gitlab.InclusionGender
#
# Suggests alternatives for words that are gender-specific.
#
@@ -7,7 +7,7 @@
extends: substitution
message: "Use inclusive language. Consider '%s' instead of '%s'."
link: https://docs.gitlab.com/ee/development/documentation/styleguide/word_list.html
-level: suggestion
+level: warning
ignorecase: true
swap:
mankind: humanity, people
diff --git a/doc/.vale/gitlab/Normal.yml b/doc/.vale/gitlab/Normal.yml
new file mode 100644
index 00000000000..0ced9f3318e
--- /dev/null
+++ b/doc/.vale/gitlab/Normal.yml
@@ -0,0 +1,14 @@
+---
+# Warning: gitlab.Normal
+#
+# Suggests alternatives for 'normal'.
+#
+# For a list of all options, see https://vale.sh/docs/topics/styles/
+extends: substitution
+message: "Use '%s' instead of '%s'."
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/word_list.html
+level: warning
+ignorecase: true
+swap:
+ normally: "usually' or 'typically"
+ normal: "typical' or 'standard"
diff --git a/doc/.vale/gitlab/Simplicity.yml b/doc/.vale/gitlab/Simplicity.yml
index 89169c1aa46..fd9b1c5e5a6 100644
--- a/doc/.vale/gitlab/Simplicity.yml
+++ b/doc/.vale/gitlab/Simplicity.yml
@@ -1,12 +1,12 @@
---
-# Suggestion: gitlab.Simplicity
+# Warning: gitlab.Simplicity
#
# Checks for words implying ease of use, to avoid cognitive dissonance for frustrated users.
#
# For a list of all options, see https://vale.sh/docs/topics/styles/
extends: existence
message: "Remove '%s'. Be precise instead of subjective."
-level: suggestion
+level: warning
ignorecase: true
link: https://docs.gitlab.com/ee/development/documentation/styleguide/word_list.html
tokens:
diff --git a/doc/.vale/gitlab/SubstitutionSuggestions.yml b/doc/.vale/gitlab/SubstitutionSuggestions.yml
deleted file mode 100644
index 21cabf1e0a7..00000000000
--- a/doc/.vale/gitlab/SubstitutionSuggestions.yml
+++ /dev/null
@@ -1,29 +0,0 @@
----
-# Suggestion: gitlab.SubstitutionSuggestions
-#
-# Suggests better options for frequently misused terms that are often - but not always - incorrect.
-# SubstitutionWarning.yml and Substitutions.yml also exist.
-#
-# For a list of all options, see https://vale.sh/docs/topics/styles/
-extends: substitution
-message: "Consider '%s' instead of '%s'."
-link: https://docs.gitlab.com/ee/development/documentation/styleguide/word_list.html
-level: suggestion
-ignorecase: true
-swap:
- active user: "billable user"
- active users: "billable users"
- docs: "documentation"
- e-mail: "email"
- GLFM: "GitLab Flavored Markdown"
- it is recommended: "you should"
- we recommend: "you should"
- navigate: go
- OAuth2: "OAuth 2.0"
- once that: "after that"
- once the: "after the"
- once you: "after you"
- since: "because' or 'after"
- sub-group: "subgroup"
- sub-groups: "subgroups"
- within: "in"
diff --git a/doc/.vale/gitlab/SubstitutionWarning.yml b/doc/.vale/gitlab/SubstitutionWarning.yml
index 8d6c18c1520..383ae38da16 100644
--- a/doc/.vale/gitlab/SubstitutionWarning.yml
+++ b/doc/.vale/gitlab/SubstitutionWarning.yml
@@ -1,16 +1,18 @@
---
# Warning: gitlab.SubstitutionWarning
#
-# Checks for misused terms or common shorthand that should never be used at GitLab, but can't be flagged as errors.
-# Substitutions.yml and SubstitionSuggestions.yml also exist.
+# Checks for misused terms or common shorthand that should not be used at GitLab, but can't be flagged as errors.
+# Substitutions.yml also exists.
#
# For a list of all options, see https://vale.sh/docs/topics/styles/
extends: substitution
-message: "If possible, use %s instead of '%s'."
-link: https://about.gitlab.com/handbook/communication/#top-misused-terms
+message: "Use '%s' instead of '%s' when possible."
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/word_list.html
level: warning
ignorecase: true
swap:
+ active user: "billable user"
+ active users: "billable users"
air(?:-| )?gapped: "offline environment"
bullet: "list item"
click: "select"
@@ -19,11 +21,26 @@ swap:
deselect: "clear"
deselected: "cleared"
distro: "distribution"
+ docs: "documentation"
+ e-mail: "email"
+ ex: "for example"
file name: "filename"
filesystem: "file system"
- GFM: "'GitLab Flavored Markdown' or 'GitHub Flavored Markdown'"
+ GLFM: "GitLab Flavored Markdown"
+ GFM: "GitLab Flavored Markdown' or 'GitHub Flavored Markdown"
info: "information"
+ it is recommended: "you should"
n/a: "not applicable"
+ navigate to: "go to"
+ OAuth2: "OAuth 2.0"
+ once that: "after that"
+ once the: "after the"
+ once you: "after you"
repo: "repository"
+ since: "because' or 'after"
+ sub-group: "subgroup"
+ sub-groups: "subgroups"
timezone: "time zone"
utilize: "use"
+ we recommend: "you should"
+ within: "in"
diff --git a/doc/.vale/gitlab/Substitutions.yml b/doc/.vale/gitlab/Substitutions.yml
index 92791486491..675abc6ef6d 100644
--- a/doc/.vale/gitlab/Substitutions.yml
+++ b/doc/.vale/gitlab/Substitutions.yml
@@ -2,7 +2,7 @@
# Error: gitlab.Substitutions
#
# Checks for misused terms that should never be used at GitLab.
-# SubstitutionWarning.yml and SubstitionSuggestions.yml also exist.
+# SubstitutionWarning.yml also exists.
#
# For a list of all options, see https://vale.sh/docs/topics/styles/
extends: substitution
@@ -60,3 +60,4 @@ swap:
reporter access: the Reporter role
reporter permission: the Reporter role
reporter permissions: the Reporter role
+ at least the Owner role: the Owner role
diff --git a/doc/.vale/gitlab/Units.yml b/doc/.vale/gitlab/Units.yml
index 4211fdee38b..e6263ad66b4 100644
--- a/doc/.vale/gitlab/Units.yml
+++ b/doc/.vale/gitlab/Units.yml
@@ -1,5 +1,5 @@
---
-# Suggestion: gitlab.Units
+# Warning: gitlab.Units
#
# Recommends a space between a number and a unit of measure.
#
@@ -8,7 +8,7 @@ extends: existence
message: "Add a space between the number and the unit in '%s'."
link: 'https://docs.gitlab.com/ee/development/documentation/styleguide/'
nonword: true
-level: suggestion
+level: warning
ignorecase: true
tokens:
- \d+(?:B|kB|KiB|MB|MiB|GB|GiB|TB|TiB)
diff --git a/doc/.vale/gitlab/Uppercase.yml b/doc/.vale/gitlab/Uppercase.yml
index f53e1c72dcb..039ad7c5f03 100644
--- a/doc/.vale/gitlab/Uppercase.yml
+++ b/doc/.vale/gitlab/Uppercase.yml
@@ -39,6 +39,7 @@ exceptions:
- CORE
- CORS
- CPU
+ - CRAN
- CRIME
- CRM
- CRUD
@@ -188,6 +189,7 @@ exceptions:
- SAST
- SATA
- SBOM
+ - SBT
- SCIM
- SCM
- SCP
diff --git a/doc/.vale/gitlab/spelling-exceptions.txt b/doc/.vale/gitlab/spelling-exceptions.txt
index 0fc7c9703ac..81b21f026f4 100644
--- a/doc/.vale/gitlab/spelling-exceptions.txt
+++ b/doc/.vale/gitlab/spelling-exceptions.txt
@@ -1,35 +1,53 @@
accessor
accessors
+ACLs
+Adafruit
+Airbnb
+Airtable
Akismet
Alertmanager
Algolia
Alibaba
+aliuid
Aliyun
allowlist
allowlisted
allowlisting
allowlists
AlmaLinux
+AMIs
anonymization
anonymized
Ansible
Anthos
-Apdex
+Anycast
+apdex
+API
+APIs
Apparmor
+Appetize
approvers
+Appsec
architected
architecting
archiver
Arel
arity
+Arkose
+armhf
+ARNs
Artifactory
Asana
Asciidoctor
asdf
Assembla
+Astro
+async
Atlassian
auditability
+auditable
Auth0
+authenticator
Authentiq
Authy
autocomplete
@@ -40,18 +58,22 @@ autogenerated
autoloaded
autoloader
autoloading
+automatable
autoscale
autoscaled
autoscaler
+autoscalers
autoscales
autoscaling
awardable
awardables
Axios
Ayoa
+AZs
Azure
B-tree
backfilling
+backfills
backport
backported
backporting
@@ -61,11 +83,16 @@ backtraced
backtraces
backtracing
badging
+balancer
+balancer's
Bamboo
Bazel
+bcrypt
+Beamer
Bhyve
Bitbucket
Bitnami
+Bittrex
blockquote
blockquoted
blockquotes
@@ -73,6 +100,8 @@ blockquoting
boolean
booleans
Bootsnap
+bot
+bot's
Bottlerocket
browsable
bugfix
@@ -88,15 +117,24 @@ bundlers
burndown
burnup
burstable
+CA
cacheable
Caddy
+callout
+callouts
callstack
callstacks
+camelCase
+camelCased
Camo
canonicalization
canonicalized
captcha
+CAPTCHAs
+Capybara
Casdoor
+CDNs
+CE
CentOS
Ceph
Certbot
@@ -110,28 +148,57 @@ ChaosKube
chatbot
chatbots
ChatOps
+checksummable
checksummed
checksumming
Chemlab
+chipset
+chipsets
+CIDRs
Citrix
Citus
+Civo
+Cleartext
+Clickhouse
+CLIs
+Clojars
clonable
Cloudwatch
+clusterized
+CMake
+CMK
+CMKs
+CNAs
+CNs
Cobertura
Codeception
+Codecov
+codenames
Codepen
+CodeSandbox
Cognito
+Coinbase
+colocate
colocated
colocating
+commit's
+CommonMark
compilable
composable
composables
Conda
+config
+Configs
Consul
Contentful
Corosync
+corpuses
Coursier
+CPU
+CPUs
+CRAN
cron
+crond
cronjob
cronjobs
crons
@@ -142,31 +209,53 @@ crosslinking
crosslinks
Crossplane
Crowdin
+crypto
+CSSComb
CSV
+CSVs
+CTAs
+CTEs
+CUnit
customappsso
+CVEs
+CWEs
cybersecurity
+CycloneDX
Dangerfile
+DAST
+Databricks
Datadog
datasource
datasources
+datastore
+datastores
+datestamp
datetime
+DBeaver
Debian
+debloating
+decodable
Decompressor
decryptable
+dedupe
deduplicate
deduplicated
deduplicates
deduplicating
deduplication
+delegators
deliverables
+denormalization
denormalize
denormalized
denormalizes
denormalizing
+dentry
denylist
denylisted
denylisting
denylists
+Depesz
deployer
deployers
deprovision
@@ -180,14 +269,22 @@ deserialization
deserialize
deserializers
deserializes
+desugar
+desugars
+desynchronized
+Dev
DevOps
Dhall
+dialogs
disambiguates
discoverability
dismissable
Disqus
Distroless
Divio
+DLE
+DNs
+Docker
Dockerfile
Dockerfiles
Dockerize
@@ -196,20 +293,37 @@ Dockerizing
dogfood
dogfooding
dogfoods
+DOMPurify
dotenv
+doublestar
downvoted
downvotes
Dpl
+dput
Dreamweaver
+DRIs
+DSLs
+Dynatrace
Ecto
+eden
+EGit
ElastiCache
Elasticsearch
+Eleventy
enablement
+Encrypt
enqueued
enqueues
+enricher
+enrichers
enum
enums
+Enviroments
+ESLint
+ESXi
ETag
+ETags
+Etsy
Excon
exfiltration
ExifTool
@@ -220,35 +334,49 @@ failovers
failsafe
Falco
falsy
+Fanout
Fargate
fastlane
+Fastly
Fastzip
favicon
favorited
+ffaker
Figma
Filebeat
+Filestore
+Finicity
+Finnhub
Fio
firewalled
firewalling
fixup
+flamegraph
+flamegraphs
Flawfinder
-Flowdock
+Flickr
Fluentd
+Flutterwave
Flycheck
+focusable
Forgerock
formatters
Fortinet
+FQDNs
+FreshBooks
+frontend
Fugit
fuzzer
+fuzzing
Gantt
Gbps
Gemfile
Gemnasium
Gemojione
Getter
-getters
Getters
gettext
+GIDs
Git
Gitaly
Gitea
@@ -258,25 +386,32 @@ gitlabsos
Gitleaks
Gitpod
Gitter
+GLab
globals
+globbing
Gmail
Godep
+Golang
Gollum
Google
goroutine
goroutines
Gosec
+GPUs
Gradle
Grafana
Grafonnet
gravatar
Grype
+GUIs
Gzip
Hackathon
Haml
+HAProxy
hardcode
hardcoded
hardcodes
+HashiCorp
Haswell
heatmap
heatmaps
@@ -284,6 +419,8 @@ Helm
Helmfile
Heroku
Herokuish
+heuristical
+hexdigest
Hexo
HipChat
hostname
@@ -292,21 +429,34 @@ hotfix
hotfixed
hotfixes
hotfixing
+hotspots
http
https
+hyperparameter
+hyperparameters
+iCalendar
+iCloud
idempotence
idmapper
Iglu
+IIFEs
Immer
inclusivity
+inflector
+inflectors
Ingress
initializer
initializers
+injective
innersource
innersourcing
+inodes
interdependencies
interdependency
interruptible
+inviter
+IPs
+IPython
irker
issuables
Istio
@@ -317,16 +467,23 @@ JavaScript
Jenkins
Jenkinsfile
Jira
+Jitsu
jq
jQuery
+JRuby
+JSDoc
jsdom
Jsonnet
+JUnit
JupyterHub
+JWT
+JWTs
Kaminari
kanban
kanbans
kaniko
Karma
+KCachegrind
Kerberos
Keycloak
keyset
@@ -339,18 +496,24 @@ Klar
Knative
Kramdown
Kroki
+kubeconfig
Kubecost
kubectl
Kubernetes
Kubesec
+Kucoin
Kustomize
+kwargs
Laravel
+LaunchDarkly
ldapsearch
Lefthook
Leiningen
libFuzzer
+Libgcrypt
Libravatar
liveness
+lockfile
lockfiles
Lodash
Lograge
@@ -361,14 +524,19 @@ lookahead
lookaheads
lookbehind
lookbehinds
+Lookbook
lookups
loopback
+Lua
Lucene
+macOS
+Mailchimp
Maildir
Mailgun
Mailroom
Makefile
Makefiles
+malloc
Markdown
markdownlint
Marketo
@@ -379,15 +547,19 @@ Mattermost
mbox
memoization
memoize
+memoizes
memoized
memoizing
Memorystore
mergeability
mergeable
metaprogramming
+metric's
+microformat
Microsoft
middleware
middlewares
+migratable
migratus
minikube
MinIO
@@ -401,6 +573,8 @@ mitigations
mitmproxy
mixin
mixins
+MLFlow
+Mmap
mockup
mockups
ModSecurity
@@ -408,52 +582,82 @@ Monokai
monorepo
monorepos
monospace
+MRs
+MSBuild
multiline
mutex
nameserver
nameservers
namespace
namespaced
+namespace's
namespaces
namespacing
namespacings
Nanoc
+NAT
+navigations
negatable
Netlify
+NGINX
+ngrok
+njsscan
Nokogiri
nosniff
noteable
noteables
npm
+NuGet
nullability
nullable
Nurtch
+NVMe
nyc
OAuth
Octokit
offboarded
offboarding
offboards
+OIDs
+OKRs
Okta
OmniAuth
onboarding
OpenID
OpenShift
Opsgenie
+Opstrace
+ORMs
+OS
+OSs
outdent
Overcommit
Packagist
+packfile
+packfiles
+Packwerk
+paginator
parallelization
parallelizations
+PascalCase
+PascalCased
+passthrough
+passthroughs
passwordless
+parsable
Patroni
+PDFs
performant
PgBouncer
+pgFormatter
pgLoader
+pgMustard
Phabricator
phaser
phasers
phpenv
+PHPUnit
+PIDs
pipenv
Pipfile
Pipfiles
@@ -464,25 +668,34 @@ polyfill
polyfills
pooler
postfixed
+Postgres
postgres.ai
PostgreSQL
+Praefect's
+prebuild
+prebuilds
precompile
precompiled
preconfigure
preconfigured
preconfigures
+prefetch
+prefetching
prefill
prefilled
prefilling
prefills
preload
+preloaded
preloading
preloads
prepend
prepended
prepending
prepends
+prepopulate
prepopulated
+presentationals
Prettifier
Pritaly
Priyanka
@@ -499,15 +712,20 @@ pseudocode
pseudonymization
pseudonymized
pseudonymizer
+Pulumi
Puma
+Pumble
+PyPI
pytest
Python
Qualys
queryable
Quicktime
Rackspace
+railties
Raspbian
rbenv
+rbspy
rbtrace
Rclone
Rdoc
@@ -523,7 +741,9 @@ rebase
rebased
rebases
rebasing
+rebinding
reCAPTCHA
+recoverability
Redcarpet
redirection
redirections
@@ -534,8 +754,13 @@ referer
referers
reflog
reflogs
+refname
refspec
refspecs
+regexes
+Rego
+reimplementation
+reimplemented
reindex
reindexed
reindexes
@@ -545,15 +770,19 @@ reinitializing
relicensing
remediations
renderers
+renderless
replicables
-rpcs
repmgr
repmgrd
+reposts
repurposing
+requestee
+requesters
requeue
requeued
requeues
requeuing
+resolver
Restlet
resync
resynced
@@ -567,10 +796,15 @@ reusability
reverified
reverifies
reverify
+reviewee
+RIs
roadmap
roadmaps
+rock
rollout
rollouts
+routable
+RPCs
RSpec
rsync
rsynced
@@ -580,6 +814,8 @@ Rubinius
Rubix
RuboCop
Rubular
+RubyGems
+Rugged
ruleset
rulesets
runbook
@@ -590,13 +826,22 @@ runtimes
Salesforce
sandboxing
sanitization
+SBOMs
+SBT
sbt
scalers
scatterplot
scatterplots
+schedulable
Schemastore
+scriptable
scrollable
+SDKs
+segmentations
+SELinux
Semgrep
+Sendbird
+Sendinblue
Sendmail
Sentry
serializer
@@ -605,10 +850,13 @@ serializing
serverless
setuptools
severities
+SFCs
sharded
sharding
+SHAs
shfmt
Shimo
+Shippo
Shopify
Sidekiq
Silverlight
@@ -618,10 +866,16 @@ skippable
skopeo
Slack
Slackbot
+SLAs
+SLIs
Slony
+SLOs
smartcard
smartcards
+snake_case
+snake_cased
snapshotting
+Snowplow
Snyk
Sobelow
Solargraph
@@ -631,9 +885,14 @@ Spamcheck
spammable
sparkline
sparklines
+Speedscope
spidering
Splunk
SpotBugs
+Squarespace
+SREs
+SSDs
+SSGs
Stackdriver
Stackprof
starrer
@@ -649,6 +908,7 @@ subchart
subcharts
subcommand
subcommands
+subcomponent
subfolder
subfolders
subgraph
@@ -661,6 +921,8 @@ sublicense
sublicensed
sublicenses
sublicensing
+submodule
+submodule's
subnet
subnets
subnetting
@@ -677,15 +939,20 @@ subtask
subtasks
subtest
subtests
+subtransaction
+subtransactions
subtree
subtrees
sudo
+sunsetting
supercookie
supercookies
+supergroup
superset
supersets
supertype
supertypes
+SVGs
swappiness
swimlane
swimlanes
@@ -695,8 +962,12 @@ syscall
syscalls
syslog
systemd
+tablespace
+tablespaces
tanuki
+taskscaler
tcpdump
+teardown
templated
Thanos
Thoughtbot
@@ -707,7 +978,9 @@ timeboxed
timeboxes
timeboxing
timecop
-tiptap
+timelog
+timelogs
+Tiptap
todos
tokenizer
Tokenizers
@@ -717,6 +990,7 @@ toolchain
toolchains
toolkit
toolkits
+toolset
tooltip
tooltips
transactionally
@@ -725,6 +999,7 @@ transpiled
transpiles
transpiling
Trello
+Trendline
triaged
triages
triaging
@@ -733,9 +1008,13 @@ Truststore
truthy
Twilio
Twitter
+Typeform
TypeScript
+TZInfo
Ubuntu
Udemy
+UI
+UIDs
unapplied
unapprove
unapproved
@@ -749,6 +1028,8 @@ unassign
unassigning
unassigns
unban
+unbans
+uncached
uncheck
unchecked
unchecking
@@ -757,6 +1038,7 @@ uncomment
uncommented
uncommenting
uncordon
+underperforming
unencode
unencoded
unencoder
@@ -782,6 +1064,7 @@ unoptimized
unoptimizes
unoptimizing
unpatched
+unpause
unprioritized
unprotect
unprotected
@@ -794,6 +1077,7 @@ unpublish
unpublished
unpublishes
unpublishing
+unpullable
unpushed
unreferenced
unregister
@@ -804,6 +1088,7 @@ unresolve
unresolved
unresolving
unreviewed
+unrevoke
unsanitized
unschedule
unscoped
@@ -835,10 +1120,14 @@ upstreams
upvote
upvoted
upvotes
+urgencies
URIs
+URL
+UUIDs
Vagrantfile
validator
validators
+vCPUs
vendored
vendoring
versionless
@@ -846,13 +1135,18 @@ viewport
viewports
virtualized
virtualizing
+Vite
+VMs
+VPCs
Vue
Vuex
+waitlist
walkthrough
walkthroughs
WebdriverIO
Webex
webpack
+WEBrick
webserver
Webservice
websocket
@@ -866,17 +1160,22 @@ wireframing
Wireshark
Wordpress
Workato
+workstream
worktree
worktrees
Worldline
Xcode
Xeon
+XPath
+Yandex
YouTrack
ytt
Yubico
Zabbix
+ZAProxy
Zeitwerk
Zendesk
ZenTao
zsh
Zstandard
+Zuora
diff --git a/doc/administration/application_settings_cache.md b/doc/administration/application_settings_cache.md
index d04056beb96..30019df44aa 100644
--- a/doc/administration/application_settings_cache.md
+++ b/doc/administration/application_settings_cache.md
@@ -20,7 +20,7 @@ To change the expiry value:
::Tabs
-:::TabTitle Omnibus package
+:::TabTitle Linux package (Omnibus)
1. Edit `/etc/gitlab/gitlab.rb`:
@@ -36,7 +36,7 @@ To change the expiry value:
gitlab-ctl restart
```
-:::TabTitle Source
+:::TabTitle Self-compiled (Source)
1. Edit `config/gitlab.yml`:
diff --git a/doc/administration/audit_event_streaming.md b/doc/administration/audit_event_streaming.md
index 0af1af12a60..4d0e6518ebb 100644
--- a/doc/administration/audit_event_streaming.md
+++ b/doc/administration/audit_event_streaming.md
@@ -18,6 +18,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - Custom HTTP headers UI [made generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/365259) in GitLab 15.3. [Feature flag `custom_headers_streaming_audit_events_ui`](https://gitlab.com/gitlab-org/gitlab/-/issues/365259) removed.
> - [Improved user experience](https://gitlab.com/gitlab-org/gitlab/-/issues/367963) in GitLab 15.3.
> - User-specified verification token API support [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/360813) in GitLab 15.4.
+> - Event type filters API [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/344845) in GitLab 15.7.
Users can set a streaming destination for a top-level group to receive all audit events about the group, its subgroups, and
projects as structured JSON.
@@ -143,6 +144,7 @@ query {
id
}
}
+ eventTypeFilters
}
}
}
@@ -284,6 +286,63 @@ Users with the Owner role for a group can list streaming destinations and see th
1. On the main area, select the **Streams**.
1. View the verification token on the right side of each item.
+## Event type filters
+
+> Event type filters API [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/344845) in GitLab 15.7.
+
+When this feature is enabled for a group, you can use an API to permit users to filter streamed audit events per destination.
+If the feature is enabled with no filters, the destination receives all audit events.
+
+A streaming destination that has an event type filter set has a **filtered** (**{filter}**) label.
+
+### Use the API to add an event type filter
+
+Prerequisites:
+
+- You must have the Owner role for the group.
+
+You can add a list of event type filters using the `auditEventsStreamingDestinationEventsAdd` query type:
+
+```graphql
+mutation {
+ auditEventsStreamingDestinationEventsAdd(input: {
+ destinationId: "gid://gitlab/AuditEvents::ExternalAuditEventDestination/1",
+ eventTypeFilters: ["list of event type filters"]}){
+ errors
+ eventTypeFilters
+ }
+}
+```
+
+Event type filters are added if:
+
+- The returned `errors` object is empty.
+- The API responds with `200 OK`.
+
+### Use the API to remove an event type filter
+
+Prerequisites:
+
+- You must have the Owner role for the group.
+
+You can remove a list of event type filters using the `auditEventsStreamingDestinationEventsRemove` query type:
+
+```graphql
+mutation {
+ auditEventsStreamingDestinationEventsRemove(input: {
+ destinationId: "gid://gitlab/AuditEvents::ExternalAuditEventDestination/1",
+ eventTypeFilters: ["list of event type filters"]
+ }){
+ errors
+ }
+}
+```
+
+Event type filters are removed if:
+
+- The returned `errors` object is empty.
+- The API responds with `200 OK`.
+
## Payload schema
> Documentation for an audit event streaming schema was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/358149) in GitLab 15.3.
diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md
index 0aa0d163972..1951ab5e2c7 100644
--- a/doc/administration/audit_events.md
+++ b/doc/administration/audit_events.md
@@ -12,6 +12,8 @@ You can use audit events to track, for example:
- Who changed the permission level of a particular user for a GitLab project, and when.
- Who added a new user or removed a user, and when.
+Audit events are similar to the [log system](logs/index.md).
+
The GitLab API, database, and `audit_json.log` record many audit events. Some audit events are only available through
[streaming audit events](audit_event_streaming.md).
@@ -21,56 +23,148 @@ NOTE:
You can't configure a retention policy for audit events, but epic
[7917](https://gitlab.com/groups/gitlab-org/-/epics/7917) proposes to change this.
-## List of events
+## Time zones
-There are two kinds of events logged:
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/242014) in GitLab 15.7, GitLab UI shows dates and times in the user's local time zone instead of UTC.
-- Events scoped to the group or project, used by group and project managers
- to look up who made a change.
-- Instance events scoped to the whole GitLab instance, used by your Compliance team to
- perform formal audits.
+The time zone used for audit events depends on where you view them:
-NOTE:
-Some events are recorded and available only as [streaming audit events](audit_event_streaming.md).
+- In GitLab UI, your local time zone (GitLab 15.7 and later) or UTC (GitLab 15.6 and earlier) is used.
+- The [Audit Events API](../api/audit_events.md) returns dates and times in UTC by default, or the
+ [configured time zone](timezone.md) on a self-managed GitLab instance.
+- In `audit_json.log`, UTC is used.
+- In CSV exports, UTC is used.
-### Impersonation data
+## View audit events
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/536) in GitLab 13.0.
+Depending on the events you want to view, at a minimum you must have:
-When a user is being [impersonated](../user/admin_area/index.md#user-impersonation), their actions are logged as audit events as usual, with two additional details:
+- For group audit events of all users in the group, the Owner role for the group.
+- For project audit events of all users in the project, the Maintainer role for the project.
+- For group and project audit events based on your own actions, the Developer role for the group or project.
+- [Auditor users](auditor_users.md) can see group and project events for all users.
-1. Usual audit events include information about the impersonating administrator. These audit events are visible in their
- respective audit event pages depending on their type (group, project, or user).
-1. Extra audit events are recorded for the start and stop of the administrator's impersonation session. These audit events
- are visible in the:
- - Instance audit events.
- - Group audit events for all groups the user belongs to (GitLab 14.8 and later). For performance reasons, group audit
- events are limited to the oldest 20 groups to which you belong.
+You can view audit events scoped to a group or project.
-![audit events](img/impersonated_audit_events_v13_8.png)
+To view a group's audit events:
-### Group events
+1. Go to the group.
+1. On the left sidebar, select **Security & Compliance > Audit Events**.
-A user with:
+Group events do not include project audit events. Group events can also be accessed using the
+[Group Audit Events API](../api/audit_events.md#group-audit-events). Group event queries are limited to a maximum of 30
+days.
-- Owner role (or above) can retrieve group audit events of all users.
-- Developer or Maintainer role is limited to group audit events based on their individual actions.
+To view a project's audit events:
-Group events do not include project audit events.
+1. Go to the project.
+1. On the left sidebar, select **Security & Compliance > Audit Events**.
-To view a group's audit events:
+Project events can also be accessed using the [Project Audit Events API](../api/audit_events.md#project-audit-events).
+Project event queries are limited to a maximum of 30 days.
-1. Go to the group.
-1. On the left sidebar, select **Security & Compliance > Audit Events**.
+## View instance audit events **(PREMIUM SELF)**
-From there, you can see the following actions:
+You can view audit events from user actions across an entire GitLab instance.
+
+To view instance audit events:
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Monitoring > Audit Events**.
+
+### Export to CSV
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1449) in GitLab 13.4.
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/285441) in GitLab 13.7.
+
+You can export the current view (including filters) of your instance audit events as a CSV file. To export the instance
+audit events to CSV:
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Monitoring > Audit Events**.
+1. Select the available search [filters](#filter-audit-events).
+1. Select **Export as CSV**.
+
+The exported file:
+
+- Is sorted by `created_at` in ascending order.
+- Is limited to a maximum of 100 000 events. The remaining records are truncated when this limit is reached.
+
+Data is encoded with:
+
+- Comma as the column delimiter.
+- `"` to quote fields if necessary.
+- New lines separate rows.
+
+The first row contains the headers, which are listed in the following table along with a description of the values:
+
+| Column | Description |
+|:---------------------|:---------------------------------------------------|
+| **ID** | Audit event `id`. |
+| **Author ID** | ID of the author. |
+| **Author Name** | Full name of the author. |
+| **Entity ID** | ID of the scope. |
+| **Entity Type** | Type of the scope (`Project`, `Group`, or `User`). |
+| **Entity Path** | Path of the scope. |
+| **Target ID** | ID of the target. |
+| **Target Type** | Type of the target. |
+| **Target Details** | Details of the target. |
+| **Action** | Description of the action. |
+| **IP Address** | IP address of the author who performed the action. |
+| **Created At (UTC)** | Formatted as `YYYY-MM-DD HH:MM:SS`. |
+
+## View sign-in events **(FREE)**
+
+Successful sign-in events are the only audit events available at all tiers. To see successful sign-in events:
+
+1. Select your avatar.
+1. Select **Edit profile > Authentication log**.
+
+After upgrading to a paid tier, you can see also see successful sign-in events on audit event pages.
+
+## Filter audit events
+
+From audit events pages, different filters are available depending on the page you're on.
+
+| Audit event page | Available filter |
+|:-----------------|:-----------------------------------------------------------------------------------------------------------------------|
+| Project | User (member of the project) who performed the action. |
+| Group | User (member of the group) who performed the action. |
+| Instance | Group, project, or user. |
+| All | Date range buttons and pickers (maximum range of 31 days). Default is from the first day of the month to today's date. |
+
+## User impersonation
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/536) in GitLab 13.0.
+> - Impersonation session events included in group audit events in GitLab 14.8.
+
+When a user is [impersonated](../user/admin_area/index.md#user-impersonation), their actions are logged as audit events
+with additional details:
+
+- Audit events include information about the impersonating administrator. These audit events are visible in audit event
+ pages depending on the audit event type (group, project, or user).
+- Extra audit events are recorded for the start and end of the administrator's impersonation session. These audit events
+ are visible as:
+ - Instance audit events.
+ - Group audit events for all groups the user belongs to. For performance reasons, group audit events are limited to
+ the oldest 20 groups you belong to.
+
+![Audit event with impersonated user](img/impersonated_audit_events_v15_7.png)
+
+## Available audit events
+
+You can view different events depending on the version of GitLab you have.
+
+### Group events
+
+The following actions on groups generate group audit events:
- Group name or path changed.
- Group repository size limit changed.
- Group created or deleted.
- Group changed visibility.
- User was added to group and with which [permissions](../user/permissions.md).
-- User sign-in via [Group SAML](../user/group/saml_sso/index.md).
+- User sign-in using [Group SAML](../user/group/saml_sso/index.md).
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8071) in GitLab 14.5, changes to the following
[group SAML](../user/group/saml_sso/index.md) configuration:
- Enabled status.
@@ -85,8 +179,8 @@ From there, you can see the following actions:
- Permissions changes of a user assigned to a group.
- Removed user from group.
- Project repository imported into group.
-- [Project shared with group](../user/project/members/share_project_with_groups.md)
- and with which [permissions](../user/permissions.md).
+- [Project shared with group](../user/project/members/share_project_with_groups.md) and with which
+ [permissions](../user/permissions.md).
- Removal of a previously shared group with a project.
- LFS enabled or disabled.
- Shared runners minutes limit changed.
@@ -94,36 +188,36 @@ From there, you can see the following actions:
- Request access enabled or disabled.
- 2FA enforcement or grace period changed.
- Roles allowed to create project changed.
-- Group CI/CD variable added, removed, or protected status changed. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30857) in GitLab 13.3.
-- Compliance framework created, updated, or deleted. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/340649) in GitLab 14.5.
-- Event streaming destination created, updated, or deleted. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/344664) in GitLab 14.6.
-- Instance administrator started or stopped impersonation of a group member. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/300961) in GitLab 14.8.
-- Group deploy token was successfully created, revoked, or deleted. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353452) in GitLab 14.9.
-- Failed attempt to create a group deploy token. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353452) in GitLab 14.9.
-- [IP restrictions](../user/group/access_and_permissions.md#restrict-access-to-groups-by-ip-address) changed. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/358986) in GitLab 15.0.
+- Group CI/CD variable added, removed, or protected status changed.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30857) in GitLab 13.3.
+- Compliance framework created, updated, or deleted.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/340649) in GitLab 14.5.
+- Event streaming destination created, updated, or deleted.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/344664) in GitLab 14.6.
+- Instance administrator started or stopped impersonation of a group member.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/300961) in GitLab 14.8.
+- Group deploy token was successfully created, revoked, or deleted.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353452) in GitLab 14.9.
+- Failed attempt to create a group deploy token. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353452)
+ in GitLab 14.9.
+- [IP restrictions](../user/group/access_and_permissions.md#restrict-access-to-groups-by-ip-address) changed.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/358986) in GitLab 15.0.
- Changes to push rules. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227629) in GitLab 15.0.
-- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356152) in GitLab 15.1, changes to the following merge request approvals settings:
+- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356152) in GitLab 15.1, changes to the following merge
+ request approvals settings:
- Prevent approval by author.
- Prevent approvals by users who add commits.
- Prevent editing approval rules in projects and merge requests.
- Require user password to approve.
- Remove all approvals when commits are added to the source branch.
-- Changes to streaming audit destination custom HTTP headers. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/366350) in GitLab 15.3.
-- Group had a security policy project linked, changed, or unlinked. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/377877) in GitLab 15.6)
-
-Group events can also be accessed via the [Group Audit Events API](../api/audit_events.md#group-audit-events)
+- Changes to streaming audit destination custom HTTP headers.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/366350) in GitLab 15.3.
+- Group had a security policy project linked, changed, or unlinked.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/377877) in GitLab 15.6.
### Project events
-A user with a Maintainer role (or above) can retrieve project audit events of all users.
-A user with a Developer role is limited to project audit events based on their individual actions.
-
-To view a project's audit events:
-
-1. Go to the project.
-1. On the left sidebar, select **Security & Compliance > Audit Events**.
-
-From there, you can see the following actions:
+The following actions on projects generate project audit events:
- Added or removed deploy keys
- Project created, deleted, renamed, moved (transferred), changed path
@@ -138,68 +232,87 @@ From there, you can see the following actions:
- Added, removed, or updated protected branches
- Release was added to a project
- Release was updated
-- Release was deleted ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94793/) in GitLab 15.3)
+- Release was deleted. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94793/) in GitLab 15.3.
- Release milestone associations changed
-- Permission to approve merge requests by committers was updated ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7531) in GitLab 12.9)
+- Permission to approve merge requests by committers was updated.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7531) in GitLab 12.9.
- Permission to approve merge requests by committers was updated.
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7531) in GitLab 12.9.
- Message for event [changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72623/diffs) in GitLab 14.6.
-
-- Permission to approve merge requests by authors was updated ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7531) in GitLab 12.9)
-- Number of required approvals was updated ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7531) in GitLab 12.9)
-- Added or removed users and groups from project approval groups ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213603) in GitLab 13.2)
-- Project CI/CD variable added, removed, or protected status changed ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30857) in GitLab 13.4)
-- Project access token was successfully created or revoked ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230007) in GitLab 13.9)
-- Failed attempt to create or revoke a project access token ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230007) in GitLab 13.9)
-- When default branch changes for a project ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/52339) in GitLab 13.9)
-- Created, updated, or deleted DAST profiles, DAST scanner profiles, and DAST site profiles
- ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217872) in GitLab 14.1)
-- Changed a project's compliance framework ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/329362) in GitLab 14.1)
-- User password required for approvals was updated ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336211) in GitLab 14.2)
-- Permission to modify merge requests approval rules in merge requests was updated ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336211) in GitLab 14.2)
-- New approvals requirement when new commits are added to an MR was updated ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336211) in GitLab 14.2)
-- When [strategies for feature flags](../operations/feature_flags.md#feature-flag-strategies) are changed ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68408) in GitLab 14.3)
-- Allowing force push to protected branch changed ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/338873) in GitLab 14.3)
-- Code owner approval requirement on merge requests targeting protected branch changed ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/338873) in GitLab 14.3)
-- Users and groups allowed to merge and push to protected branch added or removed ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/338873) in GitLab 14.3)
-- Project deploy token was successfully created, revoked or deleted ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353451) in GitLab 14.9)
-- Failed attempt to create a project deploy token ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353451) in GitLab 14.9)
-- When merge method is updated ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9)
-- Merged results pipelines enabled or disabled ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9)
-- Merge trains enabled or disabled ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9)
-- Automatically resolve merge request diff discussions enabled or disabled ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9)
-- Show link to create or view a merge request when pushing from the command line enabled or disabled ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9)
-- Delete source branch option by default enabled or disabled ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9)
-- Squash commits when merging is updated ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9)
-- Pipelines must succeed enabled or disabled ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9)
-- Skipped pipelines are considered successful enabled or disabled ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9)
-- All discussions must be resolved enabled or disabled ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9)
-- Commit message suggestion is updated ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9)
-- Status check is added, edited, or deleted ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/355805) in GitLab 15.0)
-- Merge commit message template is updated ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/355805) in GitLab 15.0)
-- Squash commit message template is updated ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/355805) in GitLab 15.0)
-- Default description template for merge requests is updated ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/355805) in GitLab 15.0)
-- Project was scheduled for deletion due to inactivity ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0)
-- Project had a security policy project linked, changed, or unlinked ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/377877) in GitLab 15.6)
-
-Project events can also be accessed via the [Project Audit Events API](../api/audit_events.md#project-audit-events).
-
-Project event queries are limited to a maximum of 30 days.
+- Permission to approve merge requests by authors was updated.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7531) in GitLab 12.9.
+- Number of required approvals was updated.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7531) in GitLab 12.9.
+- Added or removed users and groups from project approval groups.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213603) in GitLab 13.2.
+- Project CI/CD variable added, removed, or protected status changed.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30857) in GitLab 13.4.
+- Project access token was successfully created or revoked.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230007) in GitLab 13.9.
+- Failed attempt to create or revoke a project access token.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230007) in GitLab 13.9.
+- When default branch changes for a project.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/52339) in GitLab 13.9.
+- Created, updated, or deleted DAST profiles, DAST scanner profiles, and DAST site profiles.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217872) in GitLab 14.1.
+- Changed a project's compliance framework.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/329362) in GitLab 14.1.
+- User password required for approvals was updated.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336211) in GitLab 14.2.
+- Permission to modify merge requests approval rules in merge requests was updated.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336211) in GitLab 14.2.
+- New approvals requirement when new commits are added to an MR was updated.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336211) in GitLab 14.2.
+- When [strategies for feature flags](../operations/feature_flags.md#feature-flag-strategies) are changed.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68408) in GitLab 14.3.
+- Allowing force push to protected branch changed.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/338873) in GitLab 14.3.
+- Code owner approval requirement on merge requests targeting protected branch changed.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/338873) in GitLab 14.3.
+- Users and groups allowed to merge and push to protected branch added or removed.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/338873) in GitLab 14.3.
+- Project deploy token was successfully created, revoked or deleted.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353451) in GitLab 14.9.
+- Failed attempt to create a project deploy token.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353451) in GitLab 14.9.
+- When merge method is updated.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9.
+- Merged results pipelines enabled or disabled.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9.
+- Merge trains enabled or disabled.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9.
+- Automatically resolve merge request diff discussions enabled or disabled.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9.
+- Show link to create or view a merge request when pushing from the command line enabled or disabled.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9.
+- Delete source branch option by default enabled or disabled.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9.
+- Squash commits when merging is updated.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9.
+- Pipelines must succeed enabled or disabled.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9.
+- Skipped pipelines are considered successful enabled or disabled.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9.
+- All discussions must be resolved enabled or disabled.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9.
+- Commit message suggestion is updated.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) in GitLab 14.9.
+- Status check is added, edited, or deleted.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/355805) in GitLab 15.0.
+- Merge commit message template is updated.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/355805) in GitLab 15.0.
+- Squash commit message template is updated.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/355805) in GitLab 15.0.
+- Default description template for merge requests is updated.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/355805) in GitLab 15.0.
+- Project was scheduled for deletion due to inactivity.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0.
+- Project had a security policy project linked, changed, or unlinked.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/377877) in GitLab 15.6.
### Instance events **(PREMIUM SELF)**
-Server-wide audit events introduce the ability to observe user actions across
-the entire instance of your GitLab server, making it easy to understand who
-changed what and when for audit purposes.
-
-Instance events do not include group or project audit events.
-
-To view the server-wide audit events:
-
-1. On the top bar, select **Main menu > Admin**.
-1. On the left sidebar, select **Monitoring > Audit Events**.
-
-The following user actions are recorded:
+The following user actions on a GitLab instance generate instance audit events:
- Sign-in events and the authentication type (such as standard, LDAP, or OmniAuth)
- Failed sign-ins
@@ -209,123 +322,47 @@ The following user actions are recorded:
- Ask for password reset
- Grant OAuth access
- Started or stopped user impersonation
-- Changed username ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7797) in GitLab 12.8)
-- User was deleted ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/251) in GitLab 12.8)
-- User was added ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/251) in GitLab 12.8)
-- User requests access to an instance ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/298783) in GitLab 13.9)
-- User was approved via Admin Area ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276250) in GitLab 13.6)
-- User was rejected via Admin Area ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/298783) in GitLab 13.9)
-- User was blocked via Admin Area ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/251) in GitLab 12.8)
-- User was blocked via API ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25872) in GitLab 12.9)
-- Failed second-factor authentication attempt ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16826) in GitLab 13.5)
-- A user's personal access token was successfully created or revoked ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276921) in GitLab 13.6)
-- A failed attempt to create or revoke a user's personal access token ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276921) in GitLab 13.6)
-- Administrator added or removed ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/323905) in GitLab 14.1)
-- Removed SSH key ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220127) in GitLab 14.1)
-- Added or removed GPG key ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220127) in GitLab 14.1)
-- A user's two-factor authentication was disabled ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/238177) in GitLab 15.1)
-
-Instance events can also be accessed via the [Instance Audit Events API](../api/audit_events.md#instance-audit-events).
-
-### Sign-in events **(FREE)**
-
-Successful sign-in events are the only Audit Events available at all tiers. To see
-successful sign-in events:
-
-1. Select your avatar.
-1. Select **Edit profile > Authentication log**.
-
-After upgrading from GitLab Free to a paid tier, successful sign-in events are the only Audit
-Events visible in Audit Events views until more events are logged.
-
-### "Deleted User" events
-
-Audit events can be created for a user after the user is deleted. The user name associated with the event is set to
-"Deleted User" because the actual user name is unknowable. For example, if a deleted user's access to a project is
-removed automatically due to expiration, the audit event is created for "Deleted User". We are [investigating](https://gitlab.com/gitlab-org/gitlab/-/issues/343933)
-whether this is avoidable.
-
-### Missing events
-
-Some events are not tracked in audit events. See the following
-epics for more detail on which events are not being tracked, and our progress
-on adding these events into GitLab:
-
-- [Project settings and activity](https://gitlab.com/groups/gitlab-org/-/epics/474)
-- [Group settings and activity](https://gitlab.com/groups/gitlab-org/-/epics/475)
-- [Instance-level settings and activity](https://gitlab.com/groups/gitlab-org/-/epics/476)
-
-Don't see the event you want in any of the epics linked above? You can either:
+- Changed username. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7797) in GitLab 12.8.
+- User was deleted. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/251) in GitLab 12.8.
+- User was added. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/251) in GitLab 12.8.
+- User requests access to an instance. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/298783) in GitLab 13.9.
+- User was approved using the Admin Area. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276250) in GitLab 13.6.
+- User was rejected using the Admin Area. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/298783) in GitLab 13.9.
+- User was blocked using the Admin Area. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/251) in GitLab 12.8.
+- User was blocked using the API. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25872) in GitLab 12.9.
+- Failed second-factor authentication attempt. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16826) in
+ GitLab 13.5.
+- A user's personal access token was successfully created or revoked.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276921) in GitLab 13.6.
+- A failed attempt to create or revoke a user's personal access token.
+ [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276921) in GitLab 13.6.
+- Administrator added or removed. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/323905) in GitLab 14.1.
+- Removed SSH key. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220127) in GitLab 14.1.
+- Added or removed GPG key. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220127) in GitLab 14.1.
+- A user's two-factor authentication was disabled. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/238177) in
+ GitLab 15.1.
+- Enabled Admin Mode. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/362101) in GitLab 15.7.
+
+Instance events can also be accessed using the [Instance Audit Events API](../api/audit_events.md#instance-audit-events).
+
+## "Deleted User" events
+
+Audit events created after users are deleted are created for "Deleted User". For example, if a deleted user's access to
+a project is removed automatically due to expiration.
+
+Issue [343933](https://gitlab.com/gitlab-org/gitlab/-/issues/343933) proposes to change this behavior.
+
+## Unsupported events
+
+Some events are not tracked in audit events. The following epics propose support for more events:
+
+- [Project settings and activity](https://gitlab.com/groups/gitlab-org/-/epics/474).
+- [Group settings and activity](https://gitlab.com/groups/gitlab-org/-/epics/475).
+- [Instance-level settings and activity](https://gitlab.com/groups/gitlab-org/-/epics/476).
+
+If you don't see the event you want in any of the epics, you can either:
- Use the **Audit Event Proposal** issue template to
[create an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Audit%20Event%20Proposal) to
request it.
- [Add it yourself](../development/audit_event_guide/index.md).
-
-### Removed events
-
-> - Repositories push events was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/337993) in GitLab 14.3.
-> - Repositories push events was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/337993) in GitLab 15.0.
-
-The repositories push events feature was:
-
-- [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/337993) in GitLab 14.3.
-- [Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/337993) in GitLab 15.0.
-
-## Search
-
-The search filters you can see depends on which audit level you are at.
-
-| Filter | Available options |
-| ------ | ----------------- |
-| Scope (Project level) | A specific user who performed the action. |
-| Scope (Group level) | A specific user (in a group) who performed the action. |
-| Scope (Instance level) | A specific group, project, or user that the action was scoped to. |
-| Date range | Either via the date range buttons or pickers (maximum range of 31 days). Default is from the first day of the month to today's date. |
-
-![audit events](img/audit_events_v14_5.png)
-
-## Export to CSV **(PREMIUM SELF)**
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1449) in GitLab 13.4.
-> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/285441) in GitLab 13.7.
-
-Export to CSV allows customers to export the current filter view of your audit events as a
-CSV file, which stores tabular data in plain text. The data provides a comprehensive view with respect to
-audit events.
-
-To export the audit events to CSV:
-
-1. On the top bar, select **Main menu > Admin**.
-1. On the left sidebar, select **Monitoring > Audit Events**.
-1. Select the available search [filters](#search).
-1. Select **Export as CSV**.
-
-### Sort
-
-Exported events are always sorted by `created_at` in ascending order.
-
-### Format
-
-Data is encoded with a comma as the column delimiter, with `"` used to quote fields if needed, and newlines to separate rows.
-The first row contains the headers, which are listed in the following table along with a description of the values:
-
-| Column | Description |
-|---------|-------------|
-| ID | Audit event `id` |
-| Author ID | ID of the author |
-| Author Name | Full name of the author |
-| Entity ID | ID of the scope |
-| Entity Type | Type of the scope (`Project`/`Group`/`User`) |
-| Entity Path | Path of the scope |
-| Target ID | ID of the target |
-| Target Type | Type of the target |
-| Target Details | Details of the target |
-| Action | Description of the action |
-| IP Address | IP address of the author who performed the action |
-| Created At (UTC) | Formatted as `YYYY-MM-DD HH:MM:SS` |
-
-### Limitation
-
-The audit events CSV file is limited to a maximum of `100,000` events.
-The remaining records are truncated when this limit is reached.
diff --git a/doc/administration/auth/ldap/index.md b/doc/administration/auth/ldap/index.md
index 6243f3da2d2..2cb9bac7af9 100644
--- a/doc/administration/auth/ldap/index.md
+++ b/doc/administration/auth/ldap/index.md
@@ -87,9 +87,10 @@ with `start_tls` and `ssl` was replaced with `simple_tls`.
LDAP users must have a set email address, regardless of whether or not it's used
to sign in.
-### Example Omnibus GitLab configuration
+### Example Linux package (Omnibus) configuration
-This example shows configuration for Omnibus GitLab instances:
+This example shows a sample configuration for a GitLab instance that
+was installed by using the Linux package (Omnibus):
```ruby
gitlab_rails['ldap_enabled'] = true
@@ -135,9 +136,14 @@ gitlab_rails['ldap_servers'] = {
}
```
-### Example source install configuration
+### Example Helm chart (Kubernetes) configuration
-This example shows configuration for source install instances:
+View [how to configure LDAP for a GitLab instance that was installed by using the Helm chart](https://docs.gitlab.com/charts/charts/globals.html#ldap).
+
+### Example self-compiled (source) configuration
+
+This example shows a sample configuration for a GitLab instance that
+was installed by using the self-compiled source:
```yaml
production:
@@ -358,7 +364,9 @@ This can lead to several confusing issues such as creating links or namespaces w
GitLab can automatically lowercase usernames provided by the LDAP server by enabling
the configuration option `lowercase_usernames`. By default, this configuration option is `false`.
-**Omnibus configuration**
+::Tabs
+
+:::TabTitle Linux package (Omnibus)
1. Edit `/etc/gitlab/gitlab.rb`:
@@ -373,7 +381,7 @@ the configuration option `lowercase_usernames`. By default, this configuration o
1. [Reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-**Source configuration**
+:::TabTitle Self-compiled (source)
1. Edit `config/gitlab.yaml`:
@@ -388,6 +396,8 @@ the configuration option `lowercase_usernames`. By default, this configuration o
1. [Restart GitLab](../../restart_gitlab.md#installations-from-source) for the changes to take effect.
+::EndTabs
+
### Disable LDAP web sign in
It can be useful to prevent using LDAP credentials through the web UI when
@@ -398,7 +408,9 @@ checks like custom 2FA.
When LDAP web sign in is disabled, users don't see an **LDAP** tab on the sign-in page.
This does not disable using LDAP credentials for Git access.
-**Omnibus configuration**
+::Tabs
+
+:::TabTitle Linux package (Omnibus)
1. Edit `/etc/gitlab/gitlab.rb`:
@@ -408,7 +420,30 @@ This does not disable using LDAP credentials for Git access.
1. [Reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-**Source configuration**
+:::TabTitle Helm chart (Kubernetes)
+
+1. Export the Helm values:
+
+ ```shell
+ helm get values gitlab > gitlab_values.yaml
+ ```
+
+1. Edit `gitlab_values.yaml`:
+
+ ```yaml
+ global:
+ appConfig:
+ ldap:
+ preventSignin: true
+ ```
+
+1. Save the file and apply the new values:
+
+ ```shell
+ helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab
+ ```
+
+:::TabTitle Self-compiled (source)
1. Edit `config/gitlab.yaml`:
@@ -420,6 +455,8 @@ This does not disable using LDAP credentials for Git access.
1. [Restart GitLab](../../restart_gitlab.md#installations-from-source) for the changes to take effect.
+::EndTabs
+
### Use encrypted credentials
Instead of having the LDAP integration credentials stored in plaintext in the configuration files, you can optionally
@@ -439,7 +476,9 @@ The supported configuration items for the encrypted file are:
The encrypted contents can be configured with the [LDAP secret edit Rake command](../../raketasks/ldap.md#edit-secret).
-**Omnibus configuration**
+::Tabs
+
+:::TabTitle Linux package (Omnibus)
If initially your LDAP configuration looked like:
@@ -473,7 +512,7 @@ If initially your LDAP configuration looked like:
1. [Reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-**Source configuration**
+:::TabTitle Self-compiled (source)
If initially your LDAP configuration looked like:
@@ -507,6 +546,24 @@ If initially your LDAP configuration looked like:
1. [Restart GitLab](../../restart_gitlab.md#installations-from-source) for the changes to take effect.
+::EndTabs
+
+## Updating LDAP DN and email
+
+When an LDAP server creates a user in GitLab, the user's LDAP distinguished name (DN) is linked to their GitLab account
+as an identifier.
+
+When a user tries to sign in with LDAP, GitLab tries to find the user using the DN saved on that user's account.
+
+- If GitLab finds the user by the DN and the user's email address:
+ - Matches the GitLab account's email address, GitLab does not take any further action.
+ - Has changed, GitLab updates its record of the user's email to match the one in LDAP.
+- If GitLab cannot find a user by their DN, it tries to find the user by their email. If GitLab:
+ - Finds the user by their email, GitLab updates the DN stored in the user's GitLab account. Both values now
+ match the information stored in LDAP.
+ - Cannot find the user by their email address (both the DN **and** the email address have changed), see
+ [User DN and email have changed](ldap-troubleshooting.md#user-dn-and-email-have-changed).
+
## Disable anonymous LDAP authentication
GitLab doesn't support TLS client authentication. Complete these steps on your LDAP server.
@@ -543,7 +600,7 @@ Updating user email addresses must be done on the LDAP server that manages the u
The updated user's previous email address becomes the secondary email address to preserve that user's commit history.
-You can find more details on the expected behavior of user updates in our [LDAP troubleshooting section](ldap-troubleshooting.md#user-dn-orand-email-have-changed).
+You can find more details on the expected behavior of user updates in our [LDAP troubleshooting section](ldap-troubleshooting.md#user-dn-and-email-have-changed).
## Google Secure LDAP
diff --git a/doc/administration/auth/ldap/ldap-troubleshooting.md b/doc/administration/auth/ldap/ldap-troubleshooting.md
index 499c3c64af7..21ec4b293d4 100644
--- a/doc/administration/auth/ldap/ldap-troubleshooting.md
+++ b/doc/administration/auth/ldap/ldap-troubleshooting.md
@@ -541,7 +541,7 @@ Usually this is not a cause for concern.
If you think a particular user should already exist in GitLab, but you're seeing
this entry, it could be due to a mismatched DN stored in GitLab. See
-[User DN and/or email have changed](#user-dn-orand-email-have-changed) to update the user's LDAP identity.
+[User DN and email have changed](#user-dn-and-email-have-changed) to update the user's LDAP identity.
```shell
User with DN `uid=john0,ou=people,dc=example,dc=com` should have access
@@ -624,26 +624,16 @@ does not do this:
1. Wait until LDAP group synchronization has finished running.
1. Remove the user from the LDAP group.
-### User DN or/and email have changed
+### User DN and email have changed
-When an LDAP user is created in GitLab, their LDAP DN is stored for later reference.
+If both the primary email **and** the DN change in LDAP, GitLab cannot identify the correct LDAP record of a user. As a
+result, GitLab blocks that user. So that GitLab can find the LDAP record, update the user's existing GitLab profile with
+at least either:
-If GitLab cannot find a user by their DN, it falls back
-to finding the user by their email. If the lookup is successful, GitLab
-updates the stored DN to the new value so both values now match what's in
-LDAP.
+- The new primary email.
+- DN values.
-If the email has changed and the DN has not, GitLab finds the user with
-the DN and updates its own record of the user's email to match the one in LDAP.
-
-However, if the primary email _and_ the DN change in LDAP, then GitLab
-has no way of identifying the correct LDAP record of the user and, as a
-result, the user is blocked. To rectify this, the user's existing
-profile must be updated with at least one of the new values (primary
-email or DN) so the LDAP record can be found.
-
-The following script updates the emails for all provided users so they
-aren't blocked or unable to access their accounts.
+The following script updates the emails for all provided users so they aren't blocked or unable to access their accounts.
NOTE:
The following script requires that any new accounts with the new
@@ -669,7 +659,7 @@ end
You can then [run a UserSync](#sync-all-users) **(PREMIUM SELF)** to sync the latest DN
for each of these users.
-## Could not authenticate you from ldapmain because "Unknown provider"
+## `Could not authenticate you from Ldapmain because "Unknown provider"`
You can receive the following error when authenticating with an LDAP server:
@@ -831,6 +821,38 @@ ldapsearch -D "cn=admin,dc=ldap-testing,dc=example,dc=com" \
The `bind_dn`, `password`, `port`, `host`, and `base` are all
identical to what's configured in the `gitlab.rb`.
+#### Use ldapsearch with `start_tls` encryption
+
+The previous example performs an LDAP test in plaintext to port 389. If you are using [`start_tls` encryption](index.md#basic-configuration-settings), in
+the `ldapsearch` command include:
+
+- The `-Z` flag.
+- The FQDN of the LDAP server.
+
+You must include these because, during TLS negotiation, the FQDN of the LDAP server is evaluated against its certificate:
+
+```shell
+ldapsearch -D "cn=admin,dc=ldap-testing,dc=example,dc=com" \
+ -w Password1 \
+ -p 389 \
+ -h "testing.ldap.com" \
+ -b "dc=ldap-testing,dc=example,dc=com" -Z
+```
+
+#### Use ldapsearch with `simple_tls` encryption
+
+If you are using [`simple_tls` encryption](index.md#basic-configuration-settings) (usually on port 636), include the following in the `ldapsearch` command:
+
+- The LDAP server FQDN with the `-H` flag and the port.
+- The full constructed URI.
+
+```shell
+ldapsearch -D "cn=admin,dc=ldap-testing,dc=example,dc=com" \
+ -w Password1 \
+ -H "ldaps://testing.ldap.com:636" \
+ -b "dc=ldap-testing,dc=example,dc=com"
+```
+
For more information, see the [official `ldapsearch` documentation](https://linux.die.net/man/1/ldapsearch).
### Using **AdFind** (Windows)
diff --git a/doc/administration/auth/ldap/ldap_synchronization.md b/doc/administration/auth/ldap/ldap_synchronization.md
index 02b04861844..5c8c7f7baf1 100644
--- a/doc/administration/auth/ldap/ldap_synchronization.md
+++ b/doc/administration/auth/ldap/ldap_synchronization.md
@@ -55,7 +55,9 @@ use a [crontab generator](http://www.crontabgenerator.com).
The example below shows how to set LDAP user
sync to run once every 12 hours at the top of the hour.
-**Omnibus installations**
+::Tabs
+
+:::TabTitle Linux package (Omnibus)
1. Edit `/etc/gitlab/gitlab.rb`:
@@ -63,19 +65,77 @@ sync to run once every 12 hours at the top of the hour.
gitlab_rails['ldap_sync_worker_cron'] = "0 */12 * * *"
```
-1. [Reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+1. Save the file and reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+:::TabTitle Helm chart (Kubernetes)
+
+1. Export the Helm values:
+
+ ```shell
+ helm get values gitlab > gitlab_values.yaml
+ ```
+
+1. Edit `gitlab_values.yaml`:
+
+ ```yaml
+ global:
+ appConfig:
+ cron_jobs:
+ ldap_sync_worker:
+ cron: "0 */12 * * *"
+ ```
+
+1. Save the file and apply the new values:
+
+ ```shell
+ helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab
+ ```
+
+:::TabTitle Docker
+
+1. Edit `docker-compose.yml`:
+
+ ```yaml
+ version: "3.6"
+ services:
+ gitlab:
+ environment:
+ GITLAB_OMNIBUS_CONFIG: |
+ gitlab_rails['ldap_sync_worker_cron'] = "0 */12 * * *"
+ ```
+
+1. Save the file and restart GitLab:
-**Source installations**
+ ```shell
+ docker compose up -d
+ ```
-1. Edit `config/gitlab.yaml`:
+:::TabTitle Self-compiled (source)
+
+1. Edit `/home/git/gitlab/config/gitlab.yml`:
```yaml
- cron_jobs:
- ldap_sync_worker_cron:
- "0 */12 * * *"
+ production: &base
+ ee_cron_jobs:
+ ldap_sync_worker:
+ cron: "0 */12 * * *"
```
-1. [Restart GitLab](../../restart_gitlab.md#installations-from-source) for the changes to take effect.
+1. Save the file and restart GitLab:
+
+ ```shell
+ # For systems running systemd
+ sudo systemctl restart gitlab.target
+
+ # For systems running SysV init
+ sudo service gitlab restart
+ ```
+
+::EndTabs
## Group sync
@@ -91,41 +151,103 @@ GitLab group membership to be automatically updated based on LDAP group members.
The `group_base` configuration should be a base LDAP 'container', such as an
'organization' or 'organizational unit', that contains LDAP groups that should
be available to GitLab. For example, `group_base` could be
-`ou=groups,dc=example,dc=com`. In the configuration file it looks like the
+`ou=groups,dc=example,dc=com`. In the configuration file, it looks like the
following.
-**Omnibus configuration**
+::Tabs
+
+:::TabTitle Linux package (Omnibus)
1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
gitlab_rails['ldap_servers'] = {
- 'main' => {
- # snip...
- 'group_base' => 'ou=groups,dc=example,dc=com',
- }
+ 'main' => {
+ 'group_base' => 'ou=groups,dc=example,dc=com',
+ }
}
```
-1. [Apply your changes to GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure).
+1. Save the file and reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+:::TabTitle Helm chart (Kubernetes)
+
+1. Export the Helm values:
+
+ ```shell
+ helm get values gitlab > gitlab_values.yaml
+ ```
-**Source configuration**
+1. Edit `gitlab_values.yaml`:
+
+ ```yaml
+ global:
+ appConfig:
+ ldap:
+ servers:
+ main:
+ group_base: ou=groups,dc=example,dc=com
+ ```
+
+1. Save the file and apply the new values:
+
+ ```shell
+ helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab
+ ```
+
+:::TabTitle Docker
+
+1. Edit `docker-compose.yml`:
+
+ ```yaml
+ version: "3.6"
+ services:
+ gitlab:
+ environment:
+ GITLAB_OMNIBUS_CONFIG: |
+ gitlab_rails['ldap_servers'] = {
+ 'main' => {
+ 'group_base' => 'ou=groups,dc=example,dc=com',
+ }
+ }
+ ```
+
+1. Save the file and restart GitLab:
+
+ ```shell
+ docker compose up -d
+ ```
+
+:::TabTitle Self-compiled (source)
1. Edit `/home/git/gitlab/config/gitlab.yml`:
```yaml
- production:
+ production: &base
ldap:
servers:
main:
- # snip...
group_base: ou=groups,dc=example,dc=com
```
-1. [Restart GitLab](../../restart_gitlab.md#installations-from-source) for the changes to take effect.
+1. Save the file and restart GitLab:
+
+ ```shell
+ # For systems running systemd
+ sudo systemctl restart gitlab.target
+
+ # For systems running SysV init
+ sudo service gitlab restart
+ ```
+
+::EndTabs
To take advantage of group sync, group Owners or users with the [Maintainer role](../../../user/permissions.md) must
-[create one or more LDAP group links](#add-group-links).
+[create one or more LDAP group links](../../../user/group/access_and_permissions.md#manage-group-memberships-via-ldap).
### Add group links
@@ -146,37 +268,101 @@ as opposed to the full DN.
Additionally, if an LDAP user has an `admin` role, but is not a member of the `admin_group`
group, GitLab revokes their `admin` role when syncing.
-**Omnibus configuration**
+::Tabs
+
+:::TabTitle Linux package (Omnibus)
1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
gitlab_rails['ldap_servers'] = {
- 'main' => {
- # snip...
- 'group_base' => 'ou=groups,dc=example,dc=com',
- 'admin_group' => 'my_admin_group',
- }
+ 'main' => {
+ 'group_base' => 'ou=groups,dc=example,dc=com',
+ 'admin_group' => 'my_admin_group',
+ }
}
```
-1. [Apply your changes to GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure).
+1. Save the file and reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+:::TabTitle Helm chart (Kubernetes)
+
+1. Export the Helm values:
+
+ ```shell
+ helm get values gitlab > gitlab_values.yaml
+ ```
+
+1. Edit `gitlab_values.yaml`:
+
+ ```yaml
+ global:
+ appConfig:
+ ldap:
+ servers:
+ main:
+ group_base: ou=groups,dc=example,dc=com
+ admin_group: my_admin_group
+ ```
+
+1. Save the file and apply the new values:
+
+ ```shell
+ helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab
+ ```
-**Source configuration**
+:::TabTitle Docker
+
+1. Edit `docker-compose.yml`:
+
+ ```yaml
+ version: "3.6"
+ services:
+ gitlab:
+ environment:
+ GITLAB_OMNIBUS_CONFIG: |
+ gitlab_rails['ldap_servers'] = {
+ 'main' => {
+ 'group_base' => 'ou=groups,dc=example,dc=com',
+ 'admin_group' => 'my_admin_group',
+ }
+ }
+ ```
+
+1. Save the file and restart GitLab:
+
+ ```shell
+ docker compose up -d
+ ```
+
+:::TabTitle Self-compiled (source)
1. Edit `/home/git/gitlab/config/gitlab.yml`:
```yaml
- production:
+ production: &base
ldap:
servers:
main:
- # snip...
group_base: ou=groups,dc=example,dc=com
admin_group: my_admin_group
```
-1. [Restart GitLab](../../restart_gitlab.md#installations-from-source) for the changes to take effect.
+1. Save the file and restart GitLab:
+
+ ```shell
+ # For systems running systemd
+ sudo systemctl restart gitlab.target
+
+ # For systems running SysV init
+ sudo service gitlab restart
+ ```
+
+::EndTabs
### Global group memberships lock
@@ -218,7 +404,9 @@ You can manually configure LDAP group sync times by setting the
following configuration values. The example below shows how to set group
sync to run once every two hours at the top of the hour.
-**Omnibus installations**
+::Tabs
+
+:::TabTitle Linux package (Omnibus)
1. Edit `/etc/gitlab/gitlab.rb`:
@@ -226,56 +414,176 @@ sync to run once every two hours at the top of the hour.
gitlab_rails['ldap_group_sync_worker_cron'] = "0 */2 * * * *"
```
-1. [Reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+1. Save the file and reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+:::TabTitle Helm chart (Kubernetes)
+
+1. Export the Helm values:
+
+ ```shell
+ helm get values gitlab > gitlab_values.yaml
+ ```
+
+1. Edit `gitlab_values.yaml`:
+
+ ```yaml
+ global:
+ appConfig:
+ cron_jobs:
+ ldap_group_sync_worker:
+ cron: "*/30 * * * *"
+ ```
+
+1. Save the file and apply the new values:
+
+ ```shell
+ helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab
+ ```
+
+:::TabTitle Docker
+
+1. Edit `docker-compose.yml`:
+
+ ```yaml
+ version: "3.6"
+ services:
+ gitlab:
+ environment:
+ GITLAB_OMNIBUS_CONFIG: |
+ gitlab_rails['ldap_group_sync_worker_cron'] = "0 */2 * * * *"
+ ```
+
+1. Save the file and restart GitLab:
+
+ ```shell
+ docker compose up -d
+ ```
-**Source installations**
+:::TabTitle Self-compiled (source)
-1. Edit `config/gitlab.yaml`:
+1. Edit `/home/git/gitlab/config/gitlab.yml`:
```yaml
- cron_jobs:
- ldap_group_sync_worker_cron:
- "*/30 * * * *"
+ production: &base
+ ee_cron_jobs:
+ ldap_group_sync_worker:
+ cron: "*/30 * * * *"
```
-1. [Restart GitLab](../../restart_gitlab.md#installations-from-source) for the changes to take effect.
+1. Save the file and restart GitLab:
+
+ ```shell
+ # For systems running systemd
+ sudo systemctl restart gitlab.target
+
+ # For systems running SysV init
+ sudo service gitlab restart
+ ```
+
+::EndTabs
### External groups
Using the `external_groups` setting allows you to mark all users belonging
-to these groups as [external users](../../../user/permissions.md#external-users).
+to these groups as [external users](../../../user/admin_area/external_users.md).
Group membership is checked periodically through the `LdapGroupSync` background
task.
-**Omnibus configuration**
+::Tabs
+
+:::TabTitle Linux package (Omnibus)
1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
gitlab_rails['ldap_servers'] = {
- 'main' => {
- # snip...
- 'external_groups' => ['interns', 'contractors'],
- }
+ 'main' => {
+ 'external_groups' => ['interns', 'contractors'],
+ }
}
```
-1. [Apply your changes to GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure).
+1. Save the file and reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
-**Source configuration**
+:::TabTitle Helm chart (Kubernetes)
-1. Edit `config/gitlab.yaml`:
+1. Export the Helm values:
+
+ ```shell
+ helm get values gitlab > gitlab_values.yaml
+ ```
+
+1. Edit `gitlab_values.yaml`:
```yaml
- production:
+ global:
+ appConfig:
+ ldap:
+ servers:
+ main:
+ external_groups: ['interns', 'contractors']
+ ```
+
+1. Save the file and apply the new values:
+
+ ```shell
+ helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab
+ ```
+
+:::TabTitle Docker
+
+1. Edit `docker-compose.yml`:
+
+ ```yaml
+ version: "3.6"
+ services:
+ gitlab:
+ environment:
+ GITLAB_OMNIBUS_CONFIG: |
+ gitlab_rails['ldap_servers'] = {
+ 'main' => {
+ 'external_groups' => ['interns', 'contractors'],
+ }
+ }
+ ```
+
+1. Save the file and restart GitLab:
+
+ ```shell
+ docker compose up -d
+ ```
+
+:::TabTitle Self-compiled (source)
+
+1. Edit `/home/git/gitlab/config/gitlab.yml`:
+
+ ```yaml
+ production: &base
ldap:
servers:
main:
- # snip...
external_groups: ['interns', 'contractors']
```
-1. [Restart GitLab](../../restart_gitlab.md#installations-from-source) for the changes to take effect.
+1. Save the file and restart GitLab:
+
+ ```shell
+ # For systems running systemd
+ sudo systemctl restart gitlab.target
+
+ # For systems running SysV init
+ sudo service gitlab restart
+ ```
+
+::EndTabs
### Group sync technical details
diff --git a/doc/administration/clusters/kas.md b/doc/administration/clusters/kas.md
index d7e1c9af1de..79dd69183a6 100644
--- a/doc/administration/clusters/kas.md
+++ b/doc/administration/clusters/kas.md
@@ -69,7 +69,7 @@ To enable the agent server on multiple nodes:
- `gitlab_kas['api_secret_key']` is the shared secret used for authentication between KAS and GitLab. This value must be Base64-encoded and exactly 32 bytes long.
- `gitlab_kas['private_api_secret_key']` is the shared secret used for authentication between different KAS instances. This value must be Base64-encoded and exactly 32 bytes long.
-1. For each application node, follow the steps in: [Use an external installation](../clusters/kas.md#use-an-external-installation).
+1. For each application node, follow the steps in [Use an external installation](../clusters/kas.md#use-an-external-installation). If the agent server is enabled on the application node, do not include `gitlab_kas['enable'] = false` in the configuration for that node.
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
### For GitLab Helm Chart
diff --git a/doc/administration/configure.md b/doc/administration/configure.md
index 91fec753f7e..bf618345a94 100644
--- a/doc/administration/configure.md
+++ b/doc/administration/configure.md
@@ -16,7 +16,7 @@ Customize and configure your self-managed GitLab installation. Here are some qui
- [Packages](packages/index.md)
The following tables are intended to guide you to choose the right combination of capabilities based on your requirements. It is common to want the most
-available, quickly recoverable, highly performant and fully data resilient solution. However, there are tradeoffs.
+available, quickly recoverable, highly performant, and fully data resilient solution. However, there are tradeoffs.
The tables lists features on the left and provides their capabilities to the right along with known trade-offs.
@@ -24,9 +24,9 @@ The tables lists features on the left and provides their capabilities to the rig
| | Availability | Recoverability | Data Resiliency | Performance | Risks/Trade-offs|
|-|--------------|----------------|-----------------|-------------|-----------------|
-|Gitaly Cluster | Very high - tolerant of node failures | RTO for a single node of 10s with no manual intervention | Data is stored on multiple nodes | Good - While writes may take slightly longer due to voting, read distribution improves read speeds | **Trade-off** - Slight decrease in write speed for redundant, strongly-consistent storage solution. **Risks** - [Does not currently support snapshot backups](gitaly/index.md#snapshot-backup-and-recovery-limitations), GitLab backup task can be slow for large data sets |
+|Gitaly Cluster | Very high - tolerant of node failures | RTO for a single node of 10 s with no manual intervention | Data is stored on multiple nodes | Good - While writes may take slightly longer due to voting, read distribution improves read speeds | **Trade-off** - Slight decrease in write speed for redundant, strongly-consistent storage solution. **Risks** - [Does not support snapshot backups](gitaly/index.md#snapshot-backup-and-recovery-limitations), GitLab backup task can be slow for large data sets |
|Gitaly Shards | Single storage location is a single point of failure | Would need to restore only shards which failed | Single point of failure | Good - can allocate repositories to shards to spread load | **Trade-off** - Need to manually configure repositories into different shards to balance loads / storage space **Risks** - Single point of failure relies on recovery process when single-node failure occurs |
-|Gitaly + NFS | Single storage location is a single point of failure | Single node failure requires restoration from backup | Single point of failure | Average - NFS is not ideally suited to large quantities of small reads / writes which can have a detrimental impact on performance | **Trade-off** - Easy and familiar administration though NFS is not ideally suited to Git demands **Risks** - Many instances of NFS compatibility issues which provide very poor customer experiences |
+|Gitaly + NFS | Single storage location is a single point of failure | Single node failure requires restoration from backup | Single point of failure | Average - NFS is not ideally suited to large quantities of small reads / writes which can have a detrimental impact on performance | **Trade-off** - Familiar administration though NFS is not ideally suited to Git demands **Risks** - Many instances of NFS compatibility issues which provide very poor customer experiences |
## Geo Capabilities
@@ -34,7 +34,7 @@ If your availability needs to span multiple zones or multiple locations, read ab
| | Availability | Recoverability | Data Resiliency | Performance | Risks/Trade-offs|
|-|--------------|----------------|-----------------|-------------|-----------------|
-|Geo| Depends on the architecture of the Geo site. It is possible to deploy secondaries in single and multiple node configurations. | Eventually consistent. Recovery point depends on replication lag, which depends on a number of factors such as network speeds. Geo supports failover from a primary to secondary site using manual commands that are scriptable. | Geo currently replicates 100% of planned data types and verifies 50%. See [limitations table](geo/replication/datatypes.md#limitations-on-replicationverification) for more detail. | Improves read/clone times for users of a secondary. | Geo is not intended to replace other backup/restore solutions. Because of replication lag and the possibility of replicating bad data from a primary, we recommend that customers also take regular backups of their primary site and test the restore process. |
+|Geo| Depends on the architecture of the Geo site. It is possible to deploy secondaries in single and multiple node configurations. | Eventually consistent. Recovery point depends on replication lag, which depends on a number of factors such as network speeds. Geo supports failover from a primary to secondary site using manual commands that are scriptable. | Geo replicates 100% of planned data types and verifies 50%. See [limitations table](geo/replication/datatypes.md#limitations-on-replicationverification) for more detail. | Improves read/clone times for users of a secondary. | Geo is not intended to replace other backup/restore solutions. Because of replication lag and the possibility of replicating bad data from a primary, customers should also take regular backups of their primary site and test the restore process. |
## Scenarios for failure modes and available mitigation paths
diff --git a/doc/administration/external_pipeline_validation.md b/doc/administration/external_pipeline_validation.md
index e6f825f7cb8..2dec3857f75 100644
--- a/doc/administration/external_pipeline_validation.md
+++ b/doc/administration/external_pipeline_validation.md
@@ -9,9 +9,6 @@ type: reference, howto
You can use an external service to validate a pipeline before it's created.
-WARNING:
-This is an experimental feature and subject to change without notice.
-
GitLab sends a POST request to the external service URL with the pipeline
data as payload. The response code from the external service determines if GitLab
should accept or reject the pipeline. If the response is:
diff --git a/doc/administration/feature_flags.md b/doc/administration/feature_flags.md
index f2a40b60536..f7237b167e5 100644
--- a/doc/administration/feature_flags.md
+++ b/doc/administration/feature_flags.md
@@ -45,8 +45,7 @@ Features that are disabled by default may change or be removed without notice in
Data corruption, stability degradation, performance degradation, or security issues might occur if
you enable a feature that's disabled by default. Problems caused by using a default
-disabled feature aren't covered by GitLab support, unless you were directed by GitLab
-to enable the feature.
+disabled feature aren't covered by GitLab Support.
Security issues found in features that are disabled by default are patched in regular releases
and do not follow our regular [maintenance policy](../policy/maintenance.md#security-releases)
diff --git a/doc/administration/file_hooks.md b/doc/administration/file_hooks.md
index f8b55a7c680..b74ee22d584 100644
--- a/doc/administration/file_hooks.md
+++ b/doc/administration/file_hooks.md
@@ -1,6 +1,6 @@
---
-stage: Create
-group: Source Code
+stage: Manage
+group: Integrations
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments"
type: reference
---
diff --git a/doc/administration/geo/disaster_recovery/index.md b/doc/administration/geo/disaster_recovery/index.md
index dfa8d09e6ef..a78350d9dba 100644
--- a/doc/administration/geo/disaster_recovery/index.md
+++ b/doc/administration/geo/disaster_recovery/index.md
@@ -174,7 +174,7 @@ Use `gitlab-ctl geo promote` instead.
#### Promoting a **secondary** site with multiple nodes running GitLab 14.5 and later
-1. SSH to every Sidekiq, PostgresSQL, and Gitaly node in the **secondary** site and run one of the following commands:
+1. SSH to every Sidekiq, PostgreSQL, and Gitaly node in the **secondary** site and run one of the following commands:
- To promote the node on the secondary site to primary:
@@ -252,7 +252,7 @@ do this manually.
#### Promoting a **secondary** site with a Patroni standby cluster running GitLab 14.5 and later
-1. SSH to every Sidekiq, PostgresSQL, and Gitaly node in the **secondary** site and run one of the following commands:
+1. SSH to every Sidekiq, PostgreSQL, and Gitaly node in the **secondary** site and run one of the following commands:
- To promote the secondary site to primary:
@@ -364,7 +364,7 @@ with the **secondary** site:
sudo -u $PG_SUPERUSER $PG_CTL_BINARY -D $PG_DATA_DIRECTORY promote
```
-1. SSH to every Sidekiq, PostgresSQL, and Gitaly node in the **secondary** site and run one of the following commands:
+1. SSH to every Sidekiq, PostgreSQL, and Gitaly node in the **secondary** site and run one of the following commands:
- To promote the secondary site to primary:
diff --git a/doc/administration/geo/disaster_recovery/planned_failover.md b/doc/administration/geo/disaster_recovery/planned_failover.md
index 80707afacca..e9eb154d398 100644
--- a/doc/administration/geo/disaster_recovery/planned_failover.md
+++ b/doc/administration/geo/disaster_recovery/planned_failover.md
@@ -217,7 +217,7 @@ GitLab 13.9 through GitLab 14.3 are affected by a bug in which the Geo secondary
- All replication meters reach 100% replicated, 0% failures.
- All verification meters reach 100% verified, 0% failures.
- - Database replication lag is 0ms.
+ - Database replication lag is 0 ms.
- The Geo log cursor is up to date (0 events behind).
1. On the **secondary** site:
diff --git a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md
index 8c18aaa944d..cef0101dd40 100644
--- a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md
+++ b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md
@@ -72,7 +72,7 @@ On the **secondary** site:
1. On the left sidebar, select **Geo > Sites** to see its status.
Replicated objects (shown in green) should be close to 100%,
and there should be no failures (shown in red). If a large proportion of
- objects aren't yet replicated (shown in gray), consider giving the site more
+ objects aren't replicated (shown in gray), consider giving the site more
time to complete.
![Replication status](../../replication/img/geo_dashboard_v14_0.png)
@@ -162,7 +162,7 @@ follow these steps to avoid unnecessary data loss:
- All replication meters reach 100% replicated, 0% failures.
- All verification meters reach 100% verified, 0% failures.
- - Database replication lag is 0ms.
+ - Database replication lag is 0 ms.
- The Geo log cursor is up to date (0 events behind).
1. On the **secondary** site:
@@ -215,7 +215,7 @@ follow these steps to avoid unnecessary data loss:
`initctl stop gitlab-runsvvdir && echo 'manual' > /etc/init/gitlab-runsvdir.override && initctl reload-configuration`.
- If you do not have SSH access to the **primary** site, take the machine offline and
- prevent it from rebooting. Since there are many ways you may prefer to accomplish
+ prevent it from rebooting. As there are many ways you may prefer to accomplish
this, we avoid a single recommendation. You may have to:
- Reconfigure the load balancers.
@@ -228,7 +228,7 @@ follow these steps to avoid unnecessary data loss:
### Promoting the **secondary** site running GitLab 14.5 and later
-1. SSH to every Sidekiq, PostgresSQL, and Gitaly node in the **secondary** site and run one of the following commands:
+1. SSH to every Sidekiq, PostgreSQL, and Gitaly node in the **secondary** site and run one of the following commands:
- To promote the secondary site to primary:
@@ -276,7 +276,7 @@ WARNING:
If you encounter an `ActiveRecord::RecordInvalid: Validation failed: Name has already been taken` error during this process, read
[the troubleshooting advice](../../replication/troubleshooting.md#fixing-errors-during-a-failover-or-when-promoting-a-secondary-to-a-primary-site).
-The `gitlab-ctl promote-to-primary-node` command cannot be used yet in
+The `gitlab-ctl promote-to-primary-node` command cannot be used in
conjunction with multiple servers, as it can only
perform changes on a **secondary** with only a single machine. Instead, you must
do this manually.
diff --git a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md
index f9d99095951..085765ec51e 100644
--- a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md
+++ b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md
@@ -147,7 +147,7 @@ follow these steps to avoid unnecessary data loss:
- All replication meters reach 100% replicated, 0% failures.
- All verification meters reach 100% verified, 0% failures.
- - Database replication lag is 0ms.
+ - Database replication lag is 0 ms.
- The Geo log cursor is up to date (0 events behind).
1. On the **secondary** site:
diff --git a/doc/administration/geo/replication/configuration.md b/doc/administration/geo/replication/configuration.md
index 55c5d3784c2..d625b2a0324 100644
--- a/doc/administration/geo/replication/configuration.md
+++ b/doc/administration/geo/replication/configuration.md
@@ -264,7 +264,7 @@ Install the correct certificate based on your certificate type:
- **Multi-domain certificate** that includes both primary and secondary site domains: Install the certificate at `/etc/gitlab/ssl` on all **Rails, Sidekiq, and Gitaly** nodes in the **secondary** site.
- **Single-domain certificate** where the certificates are specific to each Geo site domain: Generate a valid certificate for your **secondary** site's domain and install it at `/etc/gitlab/ssl` per [these instructions](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates) on all **Rails, Sidekiq, and Gitaly** nodes in the **secondary** site.
-#### Connecting to external services that use customer certificates
+#### Connecting to external services that use custom certificates
A copy of the self-signed certificate for the external service needs to be added to the trust store on all the **primary** site's nodes that require access to the service.
diff --git a/doc/administration/geo/replication/datatypes.md b/doc/administration/geo/replication/datatypes.md
index 0198d2a63e8..022fe114a33 100644
--- a/doc/administration/geo/replication/datatypes.md
+++ b/doc/administration/geo/replication/datatypes.md
@@ -201,7 +201,7 @@ successfully, you must replicate their data using some other means.
|[CI job artifacts](../../../ci/pipelines/job_artifacts.md) | **Yes** (10.4) | **Yes** (14.10) | [**Yes** (15.1)](https://gitlab.com/groups/gitlab-org/-/epics/5551) | [No](object_storage.md#verification-of-files-in-object-storage) | Verification is behind the feature flag `geo_job_artifact_replication`, enabled by default in 14.10. |
|[CI Pipeline Artifacts](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/ci/pipeline_artifact.rb) | [**Yes** (13.11)](https://gitlab.com/gitlab-org/gitlab/-/issues/238464) | [**Yes** (13.11)](https://gitlab.com/gitlab-org/gitlab/-/issues/238464) | [**Yes** (15.1)](https://gitlab.com/groups/gitlab-org/-/epics/5551) | [No](object_storage.md#verification-of-files-in-object-storage) | Persists additional artifacts after a pipeline completes. |
|[CI Secure Files](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/ci/secure_file.rb) | [**Yes** (15.3)](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91430) | [**Yes** (15.3)](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91430) | [**Yes** (15.3)](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91430) | [No](object_storage.md#verification-of-files-in-object-storage) | Verification is behind the feature flag `geo_ci_secure_file_replication`, enabled by default in 15.3. |
-|[Container Registry](../../packages/container_registry.md) | **Yes** (12.3)* | No | No | No | Replication is behind feature flag `geo_container_repository_replication`, enabled by default. Requires additional configuration. See [instructions](container_registry.md) to set up the Container Registry replication. |
+|[Container Registry](../../packages/container_registry.md) | **Yes** (12.3)* | No | No | No | See [instructions](container_registry.md) to set up the Container Registry replication. |
|[Infrastructure Registry](../../../user/packages/infrastructure_registry/index.md) | **Yes** (14.0) | **Yes** (14.0) | [**Yes** (15.1)](https://gitlab.com/groups/gitlab-org/-/epics/5551) | [No](object_storage.md#verification-of-files-in-object-storage) | Behind feature flag `geo_package_file_replication`, enabled by default. |
|[Project designs repository](../../../user/project/issues/design_management.md) | **Yes** (12.7) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/32467) | N/A | N/A | Designs also require replication of LFS objects and Uploads. |
|[Package Registry](../../../user/packages/package_registry/index.md) | **Yes** (13.2) | **Yes** (13.10) | [**Yes** (15.1)](https://gitlab.com/groups/gitlab-org/-/epics/5551) | [No](object_storage.md#verification-of-files-in-object-storage) | Behind feature flag `geo_package_file_replication`, enabled by default. |
diff --git a/doc/administration/geo/replication/geo_validation_tests.md b/doc/administration/geo/replication/geo_validation_tests.md
index f09422d1e26..a12dd8d9d68 100644
--- a/doc/administration/geo/replication/geo_validation_tests.md
+++ b/doc/administration/geo/replication/geo_validation_tests.md
@@ -198,7 +198,7 @@ The following are additional validation tests we performed.
[Validate Object storage replication using Azure based object storage](https://gitlab.com/gitlab-org/gitlab/-/issues/348804#note_821294631):
-- Description: Tested the average time it takes for a single image to replicate from the primary object storage location to the secondary when using Azure based object storage replication and [GitLab based object storage replication](object_storage.md#enabling-gitlab-managed-object-storage-replication). This was tested by uploading a 1mb image to a project on the primary site every second for 60 seconds. The time was then measured until a image was available on the secondary site. This was achieved using a [Ruby Script](https://gitlab.com/gitlab-org/quality/geo-replication-tester).
+- Description: Tested the average time it takes for a single image to replicate from the primary object storage location to the secondary when using Azure based object storage replication and [GitLab based object storage replication](object_storage.md#enabling-gitlab-managed-object-storage-replication). This was tested by uploading a 1 MB image to a project on the primary site every second for 60 seconds. The time was then measured until a image was available on the secondary site. This was achieved using a [Ruby Script](https://gitlab.com/gitlab-org/quality/geo-replication-tester).
- Outcome: When using Azure based replication the average time for an image to replicate from the primary object storage to the secondary was recorded as 40 seconds, the longest replication time was 70 seconds and the quickest was 11 seconds. When using GitLab based replication the average time for replication to complete was 5 seconds, the longest replication time was 10 seconds and the quickest was 3 seconds.
- Follow up issue:
- [Validate Cross Region Object storage replication using Azure based object storage](https://gitlab.com/gitlab-org/gitlab/-/issues/358154)
@@ -207,12 +207,12 @@ The following are additional validation tests we performed.
[Validate Object storage replication using AWS based object storage](https://gitlab.com/gitlab-org/gitlab/-/issues/351463):
-- Description: Tested the average time it takes for a single image to replicate from the primary object storage location to the secondary when using AWS based object storage replication and [GitLab based object storage replication](object_storage.md#enabling-gitlab-managed-object-storage-replication). This was tested by uploading a 1mb image to a project on the primary site every second for 60 seconds. The time was then measured until a image was available on the secondary site. This was achieved using a [Ruby Script](https://gitlab.com/gitlab-org/quality/geo-replication-tester).
-- Outcome: When using AWS managed replication the average time for an image to replicate between sites is about 49 seconds, this is true for when sites are located within the same region and when they are further apart (Europe to America). When using Geo managed replication within the same region the average time for replication took just 5 seconds, however when replicating cross region the average time rose to 33 seconds.
+- Description: Tested the average time it takes for a single image to replicate from the primary object storage location to the secondary when using AWS based object storage replication and [GitLab based object storage replication](object_storage.md#enabling-gitlab-managed-object-storage-replication). This was tested by uploading a 1 MB image to a project on the primary site every second for 60 seconds. The time was then measured until a image was available on the secondary site. This was achieved using a [Ruby Script](https://gitlab.com/gitlab-org/quality/geo-replication-tester).
+- Outcome: When using AWS managed replication the average time for an image to replicate between sites is about 49 seconds, this is true for when sites are located in the same region and when they are further apart (Europe to America). When using Geo managed replication in the same region the average time for replication took just 5 seconds, however when replicating cross region the average time rose to 33 seconds.
[Validate Object storage replication using GCP based object storage](https://gitlab.com/gitlab-org/gitlab/-/issues/351464):
-- Description: Tested the average time it takes for a single image to replicate from the primary object storage location to the secondary when using GCP based object storage replication and [GitLab based object storage replication](object_storage.md#enabling-gitlab-managed-object-storage-replication). This was tested by uploading a 1mb image to a project on the primary site every second for 60 seconds. The time was then measured until a image was available on the secondary site. This was achieved using a [Ruby Script](https://gitlab.com/gitlab-org/quality/geo-replication-tester).
+- Description: Tested the average time it takes for a single image to replicate from the primary object storage location to the secondary when using GCP based object storage replication and [GitLab based object storage replication](object_storage.md#enabling-gitlab-managed-object-storage-replication). This was tested by uploading a 1 MB image to a project on the primary site every second for 60 seconds. The time was then measured until a image was available on the secondary site. This was achieved using a [Ruby Script](https://gitlab.com/gitlab-org/quality/geo-replication-tester).
- Outcome: GCP handles replication differently than other Cloud Providers. In GCP, the process is to a create single bucket that is either multi, dual, or single region based. This means that the bucket automatically stores replicas in a region based on the option chosen. Even when using multi region, this only replicates in a single continent, the options being America, Europe, or Asia. At current there doesn't seem to be any way to replicate objects between continents using GCP based replication. For Geo managed replication the average time when replicating in the same region was 6 seconds, and when replicating cross region this rose to just 9 seconds.
## Other tests
diff --git a/doc/administration/geo/replication/location_aware_git_url.md b/doc/administration/geo/replication/location_aware_git_url.md
index dbe543f5a62..460de5f3232 100644
--- a/doc/administration/geo/replication/location_aware_git_url.md
+++ b/doc/administration/geo/replication/location_aware_git_url.md
@@ -104,7 +104,7 @@ on the external URL of the current host. For example:
You can customize the:
-- SSH remote URL to use the location-aware `git.example.com`. To do so, change the SSH remote URL's
+- SSH remote URL to use the location-aware `git.example.com`. To do so, change the SSH remote URL
host by setting `gitlab_rails['gitlab_ssh_host']` in `gitlab.rb` of web nodes.
- HTTP remote URL as shown in
[Custom Git clone URL for HTTP(S)](../../../user/admin_area/settings/visibility_and_access_controls.md#customize-git-clone-url-for-https).
diff --git a/doc/administration/geo/replication/troubleshooting.md b/doc/administration/geo/replication/troubleshooting.md
index fa668091c90..1dcced781ce 100644
--- a/doc/administration/geo/replication/troubleshooting.md
+++ b/doc/administration/geo/replication/troubleshooting.md
@@ -49,6 +49,8 @@ health check manually to get this information and a few more details.
#### Health check Rake task
+> The use of a custom NTP server was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105514) in GitLab 15.7.
+
This Rake task can be run on a **Rails** node in the **primary** or **secondary**
Geo sites:
@@ -80,6 +82,22 @@ All projects are in hashed storage? ... yes
Checking Geo ... Finished
```
+You can also specify a custom NTP server using environment variables. For example:
+
+```shell
+export NTP_HOST="ntp.ubuntu.com"
+export NTP_TIMEOUT="30"
+sudo gitlab-rake gitlab:geo:check
+```
+
+The following environment variables are supported.
+
+| Variable | Description | Default value |
+| ----------- | ----------- | ------------- |
+|`NTP_HOST` | The NTP host. | `pool.ntp.org` |
+|`NTP_PORT` | The NTP port the host listens on. |`ntp`|
+|`NTP_TIMEOUT`| The NTP timeout in seconds. | The value defined in the `net-ntp` Ruby library ([60 seconds](https://github.com/zencoder/net-ntp/blob/3d0990214f439a5127782e0f50faeaf2c8ca7023/lib/net/ntp/ntp.rb#L6)). |
+
#### Sync status Rake task
Current sync information can be found manually by running this Rake task on any
@@ -204,7 +222,7 @@ Commands that change data can cause damage if not run correctly or under the rig
```
1. This will cause the primary to start checksumming all Uploads.
-1. When a primary successfully checksums a record, then all secondaries rechecksum as well, and they compare the values.
+1. When a primary successfully checksums a record, then all secondaries recalculate the checksum as well, and they compare the values.
A similar thing can be done for all Models handled by the [Geo Self-Service Framework](../../../development/geo/framework.md) which have implemented verification:
@@ -1335,7 +1353,7 @@ status
```
1. This will cause the primary to start checksumming all Uploads.
-1. When a primary successfully checksums a record, then all secondaries rechecksum as well, and they compare the values.
+1. When a primary successfully checksums a record, then all secondaries recalculate the checksum as well, and they compare the values.
For other SSF data types replace `Upload` in the command above with the desired model class.
@@ -1476,7 +1494,7 @@ Failed to contact primary https://primary.domain.com/namespace/push_test.git\\nE
The partial failover to a secondary Geo *site* may be the result of a temporary/transient issue. Therefore, first attempt to run the promote command again.
-1. SSH into every Sidekiq, PostgresSQL, Gitaly, and Rails node in the **secondary** site and run one of the following commands:
+1. SSH into every Sidekiq, PostgreSQL, Gitaly, and Rails node in the **secondary** site and run one of the following commands:
- To promote the secondary site to primary:
@@ -1495,7 +1513,7 @@ The partial failover to a secondary Geo *site* may be the result of a temporary/
If the above steps are **not successful**, proceed through the next steps:
-1. SSH to every Sidekiq, PostgresSQL, Gitaly and Rails node in the **secondary** site and perform the following operations:
+1. SSH to every Sidekiq, PostgreSQL, Gitaly and Rails node in the **secondary** site and perform the following operations:
- Create a `/etc/gitlab/gitlab-cluster.json` file with the following content:
diff --git a/doc/administration/geo/replication/tuning.md b/doc/administration/geo/replication/tuning.md
index ab9263ad344..4dc3ba93d66 100644
--- a/doc/administration/geo/replication/tuning.md
+++ b/doc/administration/geo/replication/tuning.md
@@ -30,7 +30,7 @@ However, this may not lead to more downloads in parallel unless the number of
available Sidekiq threads is also increased. For example, if repository synchronization
concurrency is increased from 25 to 50, you may also want to increase the number
of Sidekiq threads from 25 to 50. See the
-[Sidekiq concurrency documentation](../../sidekiq/extra_sidekiq_processes.md#number-of-threads)
+[Sidekiq concurrency documentation](../../sidekiq/extra_sidekiq_processes.md#concurrency)
for more details.
## Repository re-verification
diff --git a/doc/administration/geo/replication/version_specific_upgrades.md b/doc/administration/geo/replication/version_specific_upgrades.md
index 9aad5cdeaa7..d981656f748 100644
--- a/doc/administration/geo/replication/version_specific_upgrades.md
+++ b/doc/administration/geo/replication/version_specific_upgrades.md
@@ -39,10 +39,6 @@ results in a loop that consistently fails for all objects stored in object stora
For information on how to fix this, see
[Troubleshooting - Failed syncs with GitLab-managed object storage replication](troubleshooting.md#failed-syncs-with-gitlab-managed-object-storage-replication).
-## Upgrading to 14.6
-
-[Geo proxying](../secondary_proxy/index.md) was [enabled by default for unified URLs](https://gitlab.com/gitlab-org/gitlab/-/issues/325732) in 14.6. This may be a breaking change. If needed, you may [disable Geo proxying](../secondary_proxy/index.md#disable-geo-proxying).
-
## Upgrading to 14.4
There is [an issue in GitLab 14.4.0 through 14.4.2](../../../update/index.md#1440) that can affect Geo and other features that rely on cronjobs. We recommend upgrading to GitLab 14.4.3 or later.
@@ -144,7 +140,7 @@ GitLab 13.9 through GitLab 14.3 are affected by a bug in which enabling [GitLab
## Upgrading to GitLab 13.9
-### Error during zero-downtime upgrade: "cannot drop column asset_proxy_whitelist"
+### Error during zero-downtime upgrade: `cannot drop column asset_proxy_whitelist`
We've detected an issue [with a column rename](https://gitlab.com/gitlab-org/gitlab/-/issues/324160)
that prevents upgrades to GitLab 13.9.0, 13.9.1, 13.9.2 and 13.9.3 when following the zero-downtime steps. It is necessary
diff --git a/doc/administration/geo/secondary_proxy/index.md b/doc/administration/geo/secondary_proxy/index.md
index 2786982bb51..ac8b88a91d5 100644
--- a/doc/administration/geo/secondary_proxy/index.md
+++ b/doc/administration/geo/secondary_proxy/index.md
@@ -70,39 +70,6 @@ a single URL used by all Geo sites, including the primary.
In Kubernetes, you can use the same domain under `global.hosts.domain` as for the primary site.
-## Disable Geo proxying
-
-You can disable the secondary proxying on each Geo site, separately, by following these steps with Omnibus-based packages:
-
-1. SSH into each application node (serving user traffic directly) on your secondary Geo site
- and add the following environment variable:
-
- ```shell
- sudo editor /etc/gitlab/gitlab.rb
- ```
-
- ```ruby
- gitlab_workhorse['env'] = {
- "GEO_SECONDARY_PROXY" => "0"
- }
- ```
-
-1. Reconfigure the updated nodes for the change to take effect:
-
- ```shell
- gitlab-ctl reconfigure
- ```
-
-In Kubernetes, you can use `--set gitlab.webservice.extraEnv.GEO_SECONDARY_PROXY="0"`,
-or specify the following in your values file:
-
-```yaml
-gitlab:
- webservice:
- extraEnv:
- GEO_SECONDARY_PROXY: "0"
-```
-
## Geo proxying with Separate URLs
> Geo secondary proxying for separate URLs is [enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/346112) in GitLab 15.1.
@@ -116,11 +83,18 @@ You can also add feedback in the epic about any use-cases that
are not possible anymore with proxying enabled.
If you run into issues, to disable this feature, disable the `geo_secondary_proxy_separate_urls` feature flag.
-SSH into one node running Rails on your primary Geo site and run:
-```shell
-sudo gitlab-rails runner "Feature.disable(:geo_secondary_proxy_separate_urls)"
-```
+1. SSH into one node running Rails on your primary Geo site and run:
+
+ ```shell
+ sudo gitlab-rails runner "Feature.disable(:geo_secondary_proxy_separate_urls)"
+ ```
+
+1. Restart Puma on all of the nodes running Rails on your secondary Geo site:
+
+ ```shell
+ sudo gitlab-ctl restart puma
+ ```
In Kubernetes, you can run the same command in the toolbox pod. Refer to the
[Kubernetes cheat sheet](https://docs.gitlab.com/charts/troubleshooting/kubernetes_cheat_sheet.html#gitlab-specific-kubernetes-information)
@@ -192,3 +166,42 @@ It does not cover all data types, more will be added in the future as they are t
1. Git reads are served from the local secondary while pushes get proxied to the primary.
Selective sync or cases where repositories don't exist locally on the Geo secondary throw a "not found" error.
1. Pages can use the same URL (without access control), but must be configured separately and are not proxied.
+
+## Disable Geo proxying
+
+Secondary proxying is enabled by default on a secondary site when it uses a unified URL, meaning, the same `external_url` as the primary site. Disabling proxying in this case tends to not be helpful due to completely different behavior being served at the same URL, depending on routing.
+
+Secondary proxying is enabled by default in GitLab 15.1 on a secondary site even without a unified URL. If proxying needs to be disabled on a secondary site, it is much easier to disable the feature flag in [Geo proxying with Separate URLs](#geo-proxying-with-separate-urls). However, if there are multiple secondary sites, then the instructions in this section can be used to disable secondary proxying per site.
+
+Additionally, the `gitlab-workhorse` service polls `/api/v4/geo/proxy` every 10 seconds. In GitLab 15.2 and later, it is only polled once, if Geo is not enabled. Prior to GitLab 15.2, you can stop this polling by disabling secondary proxying.
+
+You can disable the secondary proxying on each Geo site, separately, by following these steps with Omnibus-based packages:
+
+1. SSH into each application node (serving user traffic directly) on your secondary Geo site
+ and add the following environment variable:
+
+ ```shell
+ sudo editor /etc/gitlab/gitlab.rb
+ ```
+
+ ```ruby
+ gitlab_workhorse['env'] = {
+ "GEO_SECONDARY_PROXY" => "0"
+ }
+ ```
+
+1. Reconfigure the updated nodes for the change to take effect:
+
+ ```shell
+ gitlab-ctl reconfigure
+ ```
+
+In Kubernetes, you can use `--set gitlab.webservice.extraEnv.GEO_SECONDARY_PROXY="0"`,
+or specify the following in your values file:
+
+```yaml
+gitlab:
+ webservice:
+ extraEnv:
+ GEO_SECONDARY_PROXY: "0"
+```
diff --git a/doc/administration/geo/setup/database.md b/doc/administration/geo/setup/database.md
index 86caf5306b5..99f7b32be59 100644
--- a/doc/administration/geo/setup/database.md
+++ b/doc/administration/geo/setup/database.md
@@ -35,23 +35,23 @@ or trying to evaluate Geo for a future clusterized installation.
A single instance can be expanded to a clusterized version using Patroni, which is recommended for a
highly available architecture.
-Follow below the instructions on how to set up PostgreSQL replication as a single instance database.
+Follow the instructions below on how to set up PostgreSQL replication as a single instance database.
Alternatively, you can look at the [Multi-node database replication](#multi-node-database-replication)
instructions on setting up replication with a Patroni cluster.
### PostgreSQL replication
The GitLab **primary** site where the write operations happen connects to
-the **primary** database server, and **secondary** sites
+the **primary** database server. **Secondary** sites
connect to their own database servers (which are read-only).
-We recommend using [PostgreSQL replication slots](https://medium.com/@tk512/replication-slots-in-postgresql-b4b03d277c75)
+You should use [PostgreSQL's replication slots](https://medium.com/@tk512/replication-slots-in-postgresql-b4b03d277c75)
to ensure that the **primary** site retains all the data necessary for the **secondary** sites to
recover. See below for more details.
The following guide assumes that:
-- You are using Omnibus and therefore you are using PostgreSQL 12 or later
+- You are using Omnibus and therefore you are using PostgreSQL 12 or later,
which includes the [`pg_basebackup` tool](https://www.postgresql.org/docs/12/app-pgbasebackup.html).
- You have a **primary** site already set up (the GitLab server you are
replicating from), running Omnibus' PostgreSQL (or equivalent version), and
@@ -120,8 +120,8 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
1. Define a password for the database [replication user](https://wiki.postgresql.org/wiki/Streaming_Replication).
- We will use the username defined in `/etc/gitlab/gitlab.rb` under the `postgresql['sql_replication_user']`
- setting. The default value is `gitlab_replicator`, but if you changed it to something else, adapt
+ Use the username defined in `/etc/gitlab/gitlab.rb` under the `postgresql['sql_replication_user']`
+ setting. The default value is `gitlab_replicator`. If you changed the username to something else, adapt
the instructions below.
Generate a MD5 hash of the desired password:
@@ -141,7 +141,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
```
If you are using an external database not managed by Omnibus GitLab, you need
- to create the replicator user and define a password to it manually:
+ to create the `gitlab_replicator` user and define a password for that user manually:
```sql
--- Create a new user 'replicator'
@@ -155,16 +155,16 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
For security reasons, PostgreSQL does not listen on any network interfaces
by default. However, Geo requires the **secondary** site to be able to
- connect to the **primary** site's database. For this reason, we need the IP address of
+ connect to the **primary** site's database. For this reason, you need the IP address of
each site.
NOTE:
For external PostgreSQL instances, see [additional instructions](external_database.md).
- If you are using a cloud provider, you can lookup the addresses for each
+ If you are using a cloud provider, you can look up the addresses for each
Geo site through your cloud provider's management console.
- To lookup the address of a Geo site, SSH in to the Geo site and execute:
+ To look up the address of a Geo site, SSH into the Geo site and execute:
```shell
##
@@ -187,7 +187,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
| `postgresql['md5_auth_cidr_addresses']` | **Primary** and **Secondary** sites' public or VPC private addresses. |
If you are using Google Cloud Platform, SoftLayer, or any other vendor that
- provides a virtual private cloud (VPC) you can use the **primary** and **secondary** sites
+ provides a virtual private cloud (VPC), you can use the **primary** and **secondary** sites'
private addresses (corresponds to "internal address" for Google Cloud Platform) for
`postgresql['md5_auth_cidr_addresses']` and `postgresql['listen_address']`.
@@ -196,14 +196,14 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
for more details.
NOTE:
- If you need to use `0.0.0.0` or `*` as the listen_address, you also must add
+ If you need to use `0.0.0.0` or `*` as the `listen_address`, you also must add
`127.0.0.1/32` to the `postgresql['md5_auth_cidr_addresses']` setting, to allow Rails to connect through
`127.0.0.1`. For more information, see [omnibus-5258](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5258).
- Depending on your network configuration, the suggested addresses may not
- be correct. If your **primary** site and **secondary** sites connect over a local
+ Depending on your network configuration, the suggested addresses may
+ be incorrect. If your **primary** site and **secondary** sites connect over a local
area network, or a virtual network connecting availability zones like
- [Amazon's VPC](https://aws.amazon.com/vpc/) or [Google's VPC](https://cloud.google.com/vpc/)
+ [Amazon's VPC](https://aws.amazon.com/vpc/) or [Google's VPC](https://cloud.google.com/vpc/),
you should use the **secondary** site's private address for `postgresql['md5_auth_cidr_addresses']`.
Edit `/etc/gitlab/gitlab.rb` and add the following, replacing the IP
@@ -286,12 +286,12 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
```
1. Now that the PostgreSQL server is set up to accept remote connections, run
- `netstat -plnt | grep 5432` to make sure that PostgreSQL is listening on port
+ `netstat -plnt | grep 5432` to ensure that PostgreSQL is listening on port
`5432` to the **primary** site's private address.
1. A certificate was automatically generated when GitLab was reconfigured. This
is used automatically to protect your PostgreSQL traffic from
- eavesdroppers, but to protect against active ("man-in-the-middle") attackers,
+ eavesdroppers. To protect against active ("man-in-the-middle") attackers,
the **secondary** site needs a copy of the certificate. Make a copy of the PostgreSQL
`server.crt` file on the **primary** site by running this command:
@@ -299,26 +299,26 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
cat ~gitlab-psql/data/server.crt
```
- Copy the output into a clipboard or into a local file. You
+ Copy the output to the clipboard or into a local file. You
need it when setting up the **secondary** site! The certificate is not sensitive
data.
However, this certificate is created with a generic `PostgreSQL` Common Name. For this,
you must use the `verify-ca` mode when replicating the database, otherwise,
- the hostname mismatch will cause errors.
+ the hostname mismatch causes errors.
1. Optional. Generate your own SSL certificate and manually
[configure SSL for PostgreSQL](https://docs.gitlab.com/omnibus/settings/database.html#configuring-ssl),
instead of using the generated certificate.
- You will need at least the SSL certificate and key, and set the `postgresql['ssl_cert_file']` and
+ You need at least the SSL certificate and key. Set the `postgresql['ssl_cert_file']` and
`postgresql['ssl_key_file']` values to their full paths, as per the Database SSL docs.
This allows you to use the `verify-full` SSL mode when replicating the database
and get the extra benefit of verifying the full hostname in the CN.
You can use this certificate (that you have also set in `postgresql['ssl_cert_file']`) instead
- of the certificate from the point above going forward. This will allow you to use `verify-full`
+ of the certificate from the point above going forward. This allows you to use `verify-full`
without replication errors if the CN matches.
#### Step 2. Configure the **secondary** server
@@ -337,7 +337,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
```
NOTE:
- This step is important so we don't try to execute anything before the site is fully configured.
+ This step is important so you don't try to execute anything before the site is fully configured.
1. [Check TCP connectivity](../../raketasks/maintenance.md) to the **primary** site's PostgreSQL server:
@@ -348,7 +348,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
NOTE:
If this step fails, you may be using the wrong IP address, or a firewall may
be preventing access to the site. Check the IP address, paying close
- attention to the difference between public and private addresses and ensure
+ attention to the difference between public and private addresses. Ensure
that, if a firewall is present, the **secondary** site is permitted to connect to the
**primary** site on port 5432.
@@ -389,14 +389,14 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
```
NOTE:
- If you are using manually generated certificates and plan on using
- `sslmode=verify-full` to benefit of the full hostname verification,
- make sure to replace `verify-ca` to `verify-full` when
+ If you are using manually generated certificates and want to use
+ `sslmode=verify-full` to benefit from the full hostname verification,
+ replace `verify-ca` with `verify-full` when
running the command.
- When prompted enter the _plaintext_ password you set in the first step for the
+ When prompted, enter the _plaintext_ password you set in the first step for the
`gitlab_replicator` user. If all worked correctly, you should see
- the list of **primary** site's databases.
+ the list of the **primary** site's databases.
A failure to connect here indicates that the TLS configuration is incorrect.
Ensure that the contents of `~gitlab-psql/data/server.crt` on the **primary** site
@@ -404,8 +404,8 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
1. Configure PostgreSQL:
- This step is similar to how we configured the **primary** instance.
- We must enable this, even if using a single node.
+ This step is similar to how you configured the **primary** instance.
+ You must enable this, even if using a single node.
Edit `/etc/gitlab/gitlab.rb` and add the following, replacing the IP
addresses with addresses appropriate to your network configuration:
@@ -450,12 +450,12 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
#### Step 3. Initiate the replication process
-Below we provide a script that connects the database on the **secondary** site to
-the database on the **primary** site, replicates the database, and creates the
+Below is a script that connects the database on the **secondary** site to
+the database on the **primary** site. This script replicates the database and creates the
needed files for streaming replication.
The directories used are the defaults that are set up in Omnibus. If you have
-changed any defaults, configure it as you see fit replacing the directories and paths.
+changed any defaults, configure the script accordingly, replacing any directories and paths.
WARNING:
Make sure to run this on the **secondary** site as it removes all PostgreSQL's
@@ -469,7 +469,7 @@ data before running `pg_basebackup`.
1. Choose a database-friendly name to use for your **secondary** site to
use as the replication slot name. For example, if your domain is
- `secondary.geo.example.com`, you may use `secondary_example` as the slot
+ `secondary.geo.example.com`, use `secondary_example` as the slot
name as shown in the commands below.
1. Execute the command below to start a backup/restore and begin the replication
@@ -492,33 +492,36 @@ data before running `pg_basebackup`.
```
NOTE:
- If you have generated custom PostgreSQL certificates, you will want to use
+ If you have generated custom PostgreSQL certificates, you need to use
`--sslmode=verify-full` (or omit the `sslmode` line entirely), to benefit from the extra
validation of the full host name in the certificate CN / SAN for additional security.
- Otherwise, using the automatically created certificate with `verify-full` will fail,
- as it has a generic `PostgreSQL` CN which will not match the `--host` value in this command.
+ Otherwise, using the automatically created certificate with `verify-full` fails,
+ as it has a generic `PostgreSQL` CN which doesn't match the `--host` value in this command.
This command also takes a number of additional options. You can use `--help`
- to list them all, but here are a couple of tips:
+ to list them all, but here are some tips:
- - If PostgreSQL is listening on a non-standard port, add `--port=` as well.
+ - If PostgreSQL is listening on a non-standard port, add `--port=`.
- If your database is too large to be transferred in 30 minutes, you need
- to increase the timeout, for example, `--backup-timeout=3600` if you expect the
+ to increase the timeout. For example, use `--backup-timeout=3600` if you expect the
initial replication to take under an hour.
- Pass `--sslmode=disable` to skip PostgreSQL TLS authentication altogether
(for example, you know the network path is secure, or you are using a site-to-site
VPN). It is **not** safe over the public Internet!
- You can read more details about each `sslmode` in the
- [PostgreSQL documentation](https://www.postgresql.org/docs/12/libpq-ssl.html#LIBPQ-SSL-PROTECTION);
- the instructions above are carefully written to ensure protection against
+ [PostgreSQL documentation](https://www.postgresql.org/docs/12/libpq-ssl.html#LIBPQ-SSL-PROTECTION).
+ The instructions above are carefully written to ensure protection against
both passive eavesdroppers and active "man-in-the-middle" attackers.
- Change the `--slot-name` to the name of the replication slot
to be used on the **primary** database. The script attempts to create the
replication slot automatically if it does not exist.
- If you're repurposing an old site into a Geo **secondary** site, you must
add `--force` to the command line.
- - When not in a production machine you can disable backup step if you
- really sure this is what you want by adding `--skip-backup`
+ - When not in a production machine, you can disable the backup step (if you
+ are certain this is what you want) by adding `--skip-backup`.
+ - If you are using PgBouncer, you need to target the database host directly.
+ - If you are using Patroni on your primary site, you must target the current leader host.
+ - If you are using a load balancer proxy (for example HAProxy) and it is targeting the Patroni leader for the primary, you should target the load balancer proxy instead.
The replication process is now complete.
@@ -528,9 +531,9 @@ The replication process is now complete.
PostgreSQL connections, which can improve performance even when using in a
single instance installation.
-We recommend using PgBouncer if you use GitLab in a highly available
+You should use PgBouncer if you use GitLab in a highly available
configuration with a cluster of nodes supporting a Geo **primary** site and
-two other clusters of nodes supporting a Geo **secondary** site. One for the
+two other clusters of nodes supporting a Geo **secondary** site. You need two PgBouncer nodes: one for the
main database and the other for the tracking database. For more information,
see [High Availability with Omnibus GitLab](../../postgresql/replication_and_failover.md).
@@ -542,7 +545,7 @@ when using Omnibus-managed PostgreSQL instances:
On the GitLab Geo **primary** site:
1. The default value for the replication user is `gitlab_replicator`, but if you've set a custom replication
- user in your `/etc/gitlab/gitlab.rb` under the `postgresql['sql_replication_user']` setting, make sure to
+ user in your `/etc/gitlab/gitlab.rb` under the `postgresql['sql_replication_user']` setting, ensure you
adapt the following instructions for your own user.
Generate an MD5 hash of the desired password:
@@ -574,7 +577,7 @@ On the GitLab Geo **primary** site:
```
Until the password is updated on any **secondary** sites, the [PostgreSQL log](../../logs/index.md#postgresql-logs) on
-the secondaries will report the following error message:
+the secondaries report the following error message:
```console
FATAL: could not connect to the primary server: FATAL: password authentication failed for user "gitlab_replicator"
@@ -616,16 +619,16 @@ If you still haven't [migrated from repmgr to Patroni](#migrating-from-repmgr-to
### Migrating from repmgr to Patroni
-1. Before migrating, we recommend that there is no replication lag between the **primary** and **secondary** sites and that replication is paused. In GitLab 13.2 and later, you can pause and resume replication with `gitlab-ctl geo-replication-pause` and `gitlab-ctl geo-replication-resume` on a Geo secondary database node.
+1. Before migrating, you should ensure there is no replication lag between the **primary** and **secondary** sites and that replication is paused. In GitLab 13.2 and later, you can pause and resume replication with `gitlab-ctl geo-replication-pause` and `gitlab-ctl geo-replication-resume` on a Geo secondary database node.
1. Follow the [instructions to migrate repmgr to Patroni](../../postgresql/replication_and_failover.md#switching-from-repmgr-to-patroni). When configuring Patroni on each **primary** site database node, add `patroni['replication_slots'] = { '<slot_name>' => 'physical' }`
-to `gitlab.rb` where `<slot_name>` is the name of the replication slot for your **secondary** site. This ensures that Patroni recognizes the replication slot as permanent and not drop it upon restarting.
-1. If database replication to the **secondary** site was paused before migration, resume replication after Patroni is confirmed working on the **primary** site.
+to `gitlab.rb` where `<slot_name>` is the name of the replication slot for your **secondary** site. This ensures that Patroni recognizes the replication slot as permanent and doesn't drop it upon restarting.
+1. If database replication to the **secondary** site was paused before migration, resume replication after Patroni is confirmed as working on the **primary** site.
### Migrating a single PostgreSQL node to Patroni
Before the introduction of Patroni, Geo had no Omnibus support for HA setups on the **secondary** site.
-With Patroni it's now possible to support that. To migrate the existing PostgreSQL to Patroni:
+With Patroni, this support is now possible. To migrate the existing PostgreSQL to Patroni:
1. Make sure you have a Consul cluster setup on the secondary (similar to how you set it up on the **primary** site).
1. [Configure a permanent replication slot](#step-1-configure-patroni-permanent-replication-slot-on-the-primary-site).
@@ -634,23 +637,23 @@ With Patroni it's now possible to support that. To migrate the existing PostgreS
1. [Configure a Standby Cluster](#step-4-configure-a-standby-cluster-on-the-secondary-site)
on that single node machine.
-You end up with a “Standby Cluster†with a single node. That allows you to later on add additional Patroni nodes by following the same instructions above.
+You end up with a “Standby Cluster†with a single node. That allows you to add additional Patroni nodes by following the same instructions above.
### Patroni support
-Patroni is the official replication management solution for Geo. It
+Patroni is the official replication management solution for Geo. Patroni
can be used to build a highly available cluster on the **primary** and a **secondary** Geo site.
-Using Patroni on a **secondary** site is optional and you don't have to use the same amount of
+Using Patroni on a **secondary** site is optional and you don't have to use the same number of
nodes on each Geo site.
-For instructions about how to set up Patroni on the primary site, see the
+For instructions on how to set up Patroni on the primary site, see the
[PostgreSQL replication and failover with Omnibus GitLab](../../postgresql/replication_and_failover.md#patroni) page.
#### Configuring Patroni cluster for a Geo secondary site
In a Geo secondary site, the main PostgreSQL database is a read-only replica of the primary site's PostgreSQL database.
-If you are currently using `repmgr` on your Geo primary site, see [these instructions](#migrating-from-repmgr-to-patroni)
+If you are using `repmgr` on your Geo primary site, see [these instructions](#migrating-from-repmgr-to-patroni)
for migrating from `repmgr` to Patroni.
A production-ready and secure setup requires at least:
@@ -661,14 +664,14 @@ A production-ready and secure setup requires at least:
- 1 internal load-balancer _(primary site only)_
The internal load balancer provides a single endpoint for connecting to the Patroni cluster's leader whenever a new leader is
-elected, and it is required for enabling cascading replication from the secondary sites.
+elected. The load balancer is required for enabling cascading replication from the secondary sites.
Be sure to use [password credentials](../../postgresql/replication_and_failover.md#database-authorization-for-patroni)
and other database best practices.
##### Step 1. Configure Patroni permanent replication slot on the primary site
-To set up database replication with Patroni on a secondary site, we must
+To set up database replication with Patroni on a secondary site, you must
configure a _permanent replication slot_ on the primary site's Patroni cluster,
and ensure password authentication is used.
@@ -734,8 +737,8 @@ Leader instance**:
##### Step 2. Configure the internal load balancer on the primary site
To avoid reconfiguring the Standby Leader on the secondary site whenever a new
-Leader is elected on the primary site, we must set up a TCP internal load
-balancer which gives a single endpoint for connecting to the Patroni
+Leader is elected on the primary site, you should set up a TCP internal load
+balancer. This load balancer provides a single endpoint for connecting to the Patroni
cluster's Leader.
The Omnibus GitLab packages do not include a Load Balancer. Here's how you
@@ -773,14 +776,14 @@ backend postgresql
server patroni3.internal 10.6.0.23:5432 maxconn 100 check port 8008
```
-Refer to your preferred Load Balancer's documentation for further guidance.
+For further guidance, refer to the documentation for your preferred load balancer.
##### Step 3. Configure PgBouncer nodes on the secondary site
A production-ready and highly available configuration requires at least
-three Consul nodes, a minimum of one PgBouncer node, but it's recommended to have
-one per database node. An internal load balancer (TCP) is required when there is
-more than one PgBouncer service nodes. The internal load balancer provides a single
+three Consul nodes and a minimum of one PgBouncer node. However, it is recommended to have
+one PgBouncer node per database node. An internal load balancer (TCP) is required when there is
+more than one PgBouncer service node. The internal load balancer provides a single
endpoint for connecting to the PgBouncer cluster. For more information,
see [High Availability with Omnibus GitLab](../../postgresql/replication_and_failover.md).
@@ -841,7 +844,7 @@ On each node running a PgBouncer instance on the **secondary** site:
NOTE:
If you are converting a secondary site with a single PostgreSQL instance to a Patroni Cluster, you must start on the PostgreSQL instance. It becomes the Patroni Standby Leader instance,
-and then you can switch over to another replica if you need.
+and then you can switch over to another replica if you need to.
For each node running a Patroni instance on the secondary site:
@@ -895,7 +898,7 @@ For each node running a Patroni instance on the secondary site:
```
1. Reconfigure GitLab for the changes to take effect.
- This is required to bootstrap PostgreSQL users and settings.
+ This step is required to bootstrap PostgreSQL users and settings.
- If this is a fresh installation of Patroni:
@@ -915,13 +918,12 @@ For each node running a Patroni instance on the secondary site:
### Migrating a single tracking database node to Patroni
-Before the introduction of Patroni, Geo had no Omnibus support for HA setups on
+Before the introduction of Patroni, Geo provided no Omnibus support for HA setups on
the secondary site.
-With Patroni, it's now possible to support that. Due to some restrictions on the
-Patroni implementation on Omnibus that do not allow us to manage two different
-clusters on the same machine, we recommend setting up a new Patroni cluster for
-the tracking database by following the same instructions above.
+With Patroni, it's now possible to support HA setups. However, some restrictions in Patroni
+prevent the management of two different clusters on the same machine. You should set up a new
+Patroni cluster for the tracking database by following the same instructions above.
The secondary nodes backfill the new tracking database, and no data
synchronization is required.
@@ -935,8 +937,8 @@ Omnibus automatically configures a tracking database when `roles(['geo_secondary
If you want to run this database in a highly available configuration, don't use the `geo_secondary_role` above.
Instead, follow the instructions below.
-A production-ready and secure setup for the tracking PostgreSQL DB requires at least three Consul nodes, two
-Patroni nodes and one PgBouncer node on the secondary site.
+A production-ready and secure setup for the tracking PostgreSQL DB requires at least three Consul nodes: two
+Patroni nodes, and one PgBouncer node on the secondary site.
Because of [omnibus-6587](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6587), Consul can't track multiple
services, so these must be different than the nodes used for the Standby Cluster database.
@@ -1066,7 +1068,7 @@ On each node running a Patroni instance on the secondary site for the PostgreSQL
```
1. Reconfigure GitLab for the changes to take effect.
- This is required to bootstrap PostgreSQL users and settings:
+ This step is required to bootstrap PostgreSQL users and settings:
```shell
gitlab-ctl reconfigure
diff --git a/doc/administration/gitaly/configure_gitaly.md b/doc/administration/gitaly/configure_gitaly.md
index e7a6fc35ced..d2f5282a69a 100644
--- a/doc/administration/gitaly/configure_gitaly.md
+++ b/doc/administration/gitaly/configure_gitaly.md
@@ -49,6 +49,9 @@ NOTE:
When configured to run on their own servers, Gitaly servers must be
[upgraded](../../update/package/index.md) before Gitaly clients in your cluster.
+NOTE:
+[Disk requirements](index.md#disk-requirements) apply to Gitaly nodes.
+
The process for setting up Gitaly on its own server is:
1. [Install Gitaly](#install-gitaly).
@@ -750,14 +753,25 @@ settings:
## Limit RPC concurrency
-Clone traffic can put a large strain on your Gitaly service. The bulk of the work gets done in the
-either of the following RPCs:
+WARNING:
+Enabling limits on your environment should be done with caution and only
+in select circumstances, such as to protect against unexpected traffic.
+When reached, limits _do_ result in disconnects that negatively impact users.
+For consistent and stable performance, you should first explore other options such as
+adjusting node specifications, and [reviewing large repositories](../../user/project/repository/managing_large_repositories.md) or workloads.
+
+When cloning or pulling repositories, various RPCs run in the background. In particular, the Git pack RPCs:
- `SSHUploadPackWithSidechannel` (for Git SSH).
- `PostUploadPackWithSidechannel` (for Git HTTP).
-To prevent such workloads from overwhelming your Gitaly server, you can set concurrency limits in
-Gitaly's configuration file. For example:
+These RPCs can consume a large amount of resources, which can have a significant impact in situations such as:
+
+- Unexpectedly high traffic.
+- Running against [large repositories](../../user/project/repository/managing_large_repositories.md) that don't follow best practices.
+
+You can limit these processes from overwhelming your Gitaly server in these scenarios using the concurrency limits in Gitaly's configuration file. For
+example:
```ruby
# in /etc/gitlab/gitlab.rb
@@ -795,30 +809,40 @@ repository. In the example above:
- If a request waits in the queue for more than 1 second, it is rejected with an error.
- If the queue grows beyond 10, subsequent requests are rejected with an error.
+NOTE:
+When these limits are reached, users are disconnected.
+
You can observe the behavior of this queue using the Gitaly logs and Prometheus. For more
information, see the [relevant documentation](monitoring.md#monitor-gitaly-concurrency-limiting).
## Control groups
+WARNING:
+Enabling limits on your environment should be done with caution and only
+in select circumstances, such as to protect against unexpected traffic.
+When reached, limits _do_ result in disconnects that negatively impact users.
+For consistent and stable performance, you should first explore other options such as
+adjusting node specifications, and [reviewing large repositories](../../user/project/repository/managing_large_repositories.md) or workloads.
+
FLAG:
On self-managed GitLab, by default repository cgroups are not available. To make it available, ask an administrator to
[enable the feature flag](../feature_flags.md) named `gitaly_run_cmds_in_cgroup`.
-Control groups (cgroups) in Linux allow limits to be imposed on how much memory and CPU can be consumed.
+When enabling cgroups for memory, you should ensure that no swap is configured on the Gitaly nodes as
+processes may switch to using that instead of being terminated. This situation could lead to notably compromised
+performance.
+
+You can use control groups (cgroups) in Linux to impose limits on how much memory and CPU can be consumed by Gitaly processes.
See the [`cgroups` Linux man page](https://man7.org/linux/man-pages/man7/cgroups.7.html) for more information.
-cgroups can be useful for protecting the system against resource exhaustion because of over consumption of memory and CPU.
+cgroups can be useful for protecting the system against unexpected resource exhaustion because of over consumption of memory and CPU.
-Some Git operations are expensive by nature. `git clone`, for instance,
-spawns a `git-upload-pack` process on the server that can consume a lot of memory
-for large repositories. For example, a client that keeps on cloning a
-large repository over and over again. This situation could potentially use up all of the
-memory on a server, causing other operations to fail for other users.
+Some Git operations can consume notable resources up to the point of exhaustion in situations such as:
-A repository can consume large amounts of memory for many reasons when cloned or downloaded.
-Using cgroups allows the kernel to kill these operations before they hog up all system resources.
+- Unexpectedly high traffic.
+- Operations running against large repositories that don't follow best practices.
-Gitaly shells out to Git for many of its operations. Git can consume a lot of resources for certain operations,
-especially for large repositories.
+As a hard protection, it's possible to use cgroups that configure the kernel to terminate these operations before they hog up all system resources
+and cause instability.
Gitaly has built-in cgroups control. When configured, Gitaly assigns Git processes to a cgroup based on the repository
the Git command is operating in. These cgroups are called repository cgroups. Each repository cgroup:
@@ -832,8 +856,8 @@ When a repository cgroup reaches its:
- Memory limit, the kernel looks through the processes for a candidate to kill.
- CPU limit, processes are not killed, but the processes are prevented from consuming more CPU than allowed.
-You configure repository cgroups for your GitLab installation to protect against system resource starvation from a few
-large repositories or bad actors.
+NOTE:
+When these limits are reached, performance may be reduced and users may be disconnected.
### Configure repository cgroups (new method)
@@ -913,8 +937,8 @@ gitaly['cgroups_cpu_enabled'] = true
In the previous example using the new configuration method:
-- The top level memory limit is capped at 60gb.
-- Each of the 1000 cgroups in the repositories pool is capped at 20gb.
+- The top level memory limit is capped at 60 GB.
+- Each of the 1000 cgroups in the repositories pool is capped at 20 GB.
This configuration leads to "oversubscription". Each cgroup in the pool has a much larger capacity than 1/1000th
of the top-level memory limit.
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index 1b7e1f0f7c6..32b44552b1a 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -54,9 +54,8 @@ Before deploying Gitaly Cluster, review:
If you have:
-- Not yet migrated to Gitaly Cluster and want to continue using NFS, remain on the service you are using. NFS is
- supported in 14.x releases but is [deprecated](../../update/deprecations.md#nfs-for-git-repository-storage).
- Support for storing Git repository data on NFS is scheduled to end for all versions of GitLab on November 22, 2022.
+- Not yet migrated to Gitaly Cluster and want to continue using NFS, remain on the service you are using. However, NFS
+ is [no longer supported](../../update/removals.md#nfs-as-git-repository-storage-is-no-longer-supported).
- Not yet migrated to Gitaly Cluster but want to migrate away from NFS, you have two options:
- A sharded Gitaly instance.
- Gitaly Cluster.
@@ -90,6 +89,23 @@ If you are unable to use either method, contact customer support for restoration
Contact customer support for immediate help in restoration or recovery.
+## Disk requirements
+
+Gitaly and Gitaly Cluster require fast local storage to perform effectively because they are heavy I/O-based processes. Therefore,
+we strongly recommend that all Gitaly nodes use solid-state drives (SSDs).
+
+These SSDs should have a throughput of at least:
+
+- 8,000 input/output operations per second (IOPS) for read operations.
+- 2,000 IOPS for write operations.
+
+These IOPS values are initial recommendations, and may be adjusted to greater or lesser values
+depending on the scale of your environment's workload. If you’re running the environment on a
+cloud provider, refer to their documentation about how to configure IOPS correctly.
+
+For repository data, only local storage is supported for Gitaly and Gitaly Cluster for performance and consistency reasons.
+Alternatives such as [NFS](../nfs.md) or [cloud-based file systems](../nfs.md#avoid-using-cloud-based-file-systems) are not supported.
+
## Directly accessing repositories
GitLab doesn't advise directly accessing Gitaly repositories stored on disk with a Git client or any other tool,
@@ -405,33 +421,6 @@ The leftover state is eventually cleaned up.
Unlike Gitaly, Gitaly Cluster doesn't move the repositories in the storages but only virtually moves the repository by updating the
relative path of the repository in the metadata store.
-### Moving beyond NFS
-
-Engineering support for NFS for Git repositories is deprecated. Technical support is planned to be unavailable starting
-November 22, 2022. See our [statement of support](https://about.gitlab.com/support/statement-of-support/#gitaly-and-nfs)
-for more details.
-
-[Network File System (NFS)](https://en.wikipedia.org/wiki/Network_File_System)
-is not well suited to Git workloads which are CPU and IOPS sensitive.
-Specifically:
-
-- Git is sensitive to file system latency. Some operations require many
- read operations. Operations that are fast on block storage can become an order of
- magnitude slower. This significantly impacts GitLab application performance.
-- NFS performance optimizations that prevent the performance gap between
- block storage and NFS being even wider are vulnerable to race conditions. We have observed
- [data inconsistencies](https://gitlab.com/gitlab-org/gitaly/-/issues/2589)
- in production environments caused by simultaneous writes to different NFS
- clients. Data corruption is not an acceptable risk.
-
-Gitaly Cluster is purpose built to provide reliable, high performance, fault
-tolerant Git storage.
-
-Further reading:
-
-- Blog post: [The road to Gitaly v1.0 (aka, why GitLab doesn't require NFS for storing Git data anymore)](https://about.gitlab.com/blog/2018/09/12/the-road-to-gitaly-1-0/)
-- Blog post: [How we spent two weeks hunting an NFS bug in the Linux kernel](https://about.gitlab.com/blog/2018/11/14/how-we-spent-two-weeks-hunting-an-nfs-bug/)
-
### Components
Gitaly Cluster consists of multiple components:
@@ -672,7 +661,7 @@ To see if GitLab can access the repository file system directly, we use the foll
- Gitaly ensures that the file system has a metadata file in its root with a UUID in it.
- Gitaly reports this UUID to GitLab by using the `ServerInfo` RPC.
-- GitLab Rails tries to read the metadata file directly. If it exists, and if the UUID's match,
+- GitLab Rails tries to read the metadata file directly. If it exists, and if the UUIDs match,
assume we have direct access.
Direct Git access is:
@@ -696,8 +685,3 @@ There are two facets to our efforts to remove direct Git access in GitLab:
The second facet presents the only real solution. For this, we developed
[Gitaly Cluster](#gitaly-cluster).
-
-## NFS deprecation notice
-
-Engineering support for NFS for Git repositories is deprecated. Technical support is planned to be
-unavailable beginning November 22, 2022. For further information, see our [NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation) documentation.
diff --git a/doc/administration/gitaly/monitoring.md b/doc/administration/gitaly/monitoring.md
index 8d4f30c7c20..9024da269ca 100644
--- a/doc/administration/gitaly/monitoring.md
+++ b/doc/administration/gitaly/monitoring.md
@@ -49,7 +49,7 @@ the Gitaly logs and Prometheus:
You can observe the status of [control groups (cgroups)](configure_gitaly.md#control-groups) using Prometheus:
- `gitaly_cgroups_reclaim_attempts_total`, a gauge for the total number of times
- there has been a memory relcaim attempt. This number resets each time a server is
+ there has been a memory reclaim attempt. This number resets each time a server is
restarted.
- `gitaly_cgroups_cpu_usage`, a gauge that measures CPU usage per cgroup.
- `gitaly_cgroup_procs_total`, a gauge that measures the total number of
diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md
index 8cf32a6aaa3..9cc93b21ae9 100644
--- a/doc/administration/gitaly/praefect.md
+++ b/doc/administration/gitaly/praefect.md
@@ -28,6 +28,9 @@ The minimum recommended configuration for a Gitaly Cluster requires:
- 3 Praefect nodes
- 3 Gitaly nodes (1 primary, 2 secondary)
+NOTE:
+[Disk requirements](index.md#disk-requirements) apply to Gitaly nodes.
+
You should configure an odd number of Gitaly nodes so that transactions have a tie-breaker in case one of the
Gitaly nodes fails in a mutating RPC call.
@@ -43,15 +46,15 @@ default value. The default value depends on the GitLab version.
Network latency for Gitaly Cluster should ideally be measurable in single-digit milliseconds. Latency is particularly
important for:
-- Gitaly node health checks. Nodes must be able to respond within 1 second.
+- Gitaly node health checks. Nodes must be able to respond 1 second or faster.
- Reference transactions that enforce [strong consistency](index.md#strong-consistency). Lower latencies mean Gitaly
nodes can agree on changes faster.
Achieving acceptable latency between Gitaly nodes:
- On physical networks generally means high bandwidth, single location connections.
-- On the cloud generally means within the same region, including allowing cross availability zone replication. These links
- are designed for this type of synchronization. Latency of less than 2ms should be sufficient for Gitaly Cluster.
+- On the cloud generally means in the same region, including allowing cross availability zone replication. These links
+ are designed for this type of synchronization. Latency of less than 2 ms should be sufficient for Gitaly Cluster.
If you can't provide low network latencies for replication (for example, between distant locations), consider Geo. For
more information, see [Comparison to Geo](index.md#comparison-to-geo).
@@ -82,7 +85,7 @@ The requirements are relatively low because the database contains only metadata
- Where repositories are located.
- Some queued work.
-It depends on the number of repositories, but a useful minimum is 5-10 GB, similar to the main
+It depends on the number of repositories, but a good minimum is 5-10 GB, similar to the main
GitLab application database.
## Setup Instructions
@@ -103,7 +106,7 @@ If you [installed](https://about.gitlab.com/install/) GitLab using the Omnibus G
Before beginning, you should already have a working GitLab instance.
[Learn how to install GitLab](https://about.gitlab.com/install/).
-Provision a PostgreSQL server. We recommend using the PostgreSQL that is shipped
+Provision a PostgreSQL server. You should use the PostgreSQL that is shipped
with Omnibus GitLab and use it to configure the PostgreSQL database. You can use an
external PostgreSQL server (version 11 or newer) but you must set it up [manually](#manual-database-setup).
@@ -1453,7 +1456,7 @@ To migrate existing clusters:
1. On the Praefect nodes, configure the election strategy in `/etc/gitlab/gitlab.rb` with `praefect['failover_election_strategy'] = 'per_repository'`.
- 1. Run `gitlab-ctl reconfigure && gitlab-ctl start` to reconfigure and start the Praefects.
+ 1. Run `gitlab-ctl reconfigure && gitlab-ctl start` to reconfigure and start the Praefect nodes.
- If downtime is unacceptable:
diff --git a/doc/administration/gitaly/reference.md b/doc/administration/gitaly/reference.md
index 8f7dc688e56..cec18960dfd 100644
--- a/doc/administration/gitaly/reference.md
+++ b/doc/administration/gitaly/reference.md
@@ -57,7 +57,7 @@ an empty string.
It is possible to temporarily disable authentication with the `transitioning`
setting. This allows you to monitor if all clients are
authenticating correctly without causing a service outage for clients
-that are not configured correctly yet:
+that are still to be configured correctly:
```toml
[auth]
@@ -159,7 +159,7 @@ sum(rate(gitaly_catfile_cache_total{type="hit"}[5m])) / sum(rate(gitaly_catfile_
### `gitaly-ruby`
A Gitaly process uses one or more `gitaly-ruby` helper processes to
-execute RPC's implemented in Ruby instead of Go. The `[gitaly-ruby]`
+execute RPCs implemented in Ruby instead of Go. The `[gitaly-ruby]`
section of the configuration file contains settings for these helper processes.
These processes are known to occasionally suffer from memory leaks.
@@ -169,7 +169,7 @@ Gitaly restarts its `gitaly-ruby` helpers when their memory exceeds the
| Name | Type | Required | Description |
| ---- | ---- | -------- | ----------- |
| `dir` | string | yes | Path to where `gitaly-ruby` is installed (needed to boot the process).|
-| `max_rss` | integer | no | Resident set size limit that triggers a `gitaly-ruby` restart, in bytes. Default is `200000000` (200MB). |
+| `max_rss` | integer | no | Resident set size limit that triggers a `gitaly-ruby` restart, in bytes. Default is `200000000` (200 MB). |
| `graceful_restart_timeout` | string | no | Grace period before a `gitaly-ruby` process is forcibly terminated after exceeding `max_rss`. Default is `10m` (10 minutes).|
| `restart_delay` | string | no |Time that `gitaly-ruby` memory must remain high before a restart. Default is `5m` (5 minutes).|
| `num_workers` | integer | no |Number of `gitaly-ruby` worker processes. Try increasing this number in case of `ResourceExhausted` errors. Default is `2`, minimum is `2`.|
diff --git a/doc/administration/gitaly/troubleshooting.md b/doc/administration/gitaly/troubleshooting.md
index 7edce840396..7f5d4b9e443 100644
--- a/doc/administration/gitaly/troubleshooting.md
+++ b/doc/administration/gitaly/troubleshooting.md
@@ -245,6 +245,28 @@ the application might be fetching this secret from a different file. Your Gitaly
If that setting is missing, GitLab defaults to using `.gitlab_shell_secret` under
`/opt/gitlab/embedded/service/gitlab-rails/.gitlab_shell_secret`.
+### Repository pushes fail
+
+When attempting `git push`, you can see:
+
+- `401 Unauthorized` errors.
+- The following in server logs:
+
+ ```json
+ {
+ ...
+ "exception.class":"JWT::VerificationError",
+ "exception.message":"Signature verification raised",
+ ...
+ }
+ ```
+
+This error occurs when the GitLab server has been upgraded to GitLab 15.5 or later but Gitaly has not yet been upgraded.
+
+From GitLab 15.5, GitLab [authenticates with GitLab Shell using a JWT token instead of a shared secret](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86148).
+You should follow the [recommendations on upgrading external Gitaly](../../update/plan_your_upgrade.md#external-gitaly) and upgrade Gitaly before the GitLab
+server.
+
### Repository pushes fail with a `deny updating a hidden ref` error
Due to [a change](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/3426)
@@ -456,6 +478,20 @@ in sync so the token check succeeds.
This check helps identify the root cause of `permission denied`
[errors being logged by Praefect](#permission-denied-errors-appearing-in-gitaly-or-praefect-logs-when-accessing-repositories).
+For offline environments where access to public [`pool.ntp.org`](https://pool.ntp.org) servers is not possible, the Praefect `check` sub-command fails this
+check with an error message similar to:
+
+```plaintext
+checking with NTP service at and allowed clock drift 60000ms [correlation_id: <XXX>]
+Failed (fatal) error: gitaly node at tcp://[gitlab.example-instance.com]:8075: rpc error: code = DeadlineExceeded desc = context deadline exceeded
+```
+
+To resolve this issue, set an environment variable on all Praefect servers to point to an accessible internal NTP server. For example:
+
+```shell
+export NTP_HOST=ntp.example.com
+```
+
### Praefect errors in logs
If you receive an error, check `/var/log/gitlab/gitlab-rails/production.log`.
@@ -608,7 +644,7 @@ Is [some cases](index.md#known-issues) the Praefect database can get out of sync
a given repository is fully synced on all nodes, run the [`gitlab:praefect:replicas` Rake task](../raketasks/praefect.md#replica-checksums)
that checksums the repository on all Gitaly nodes.
-The [Praefect dataloss](recovery.md#check-for-data-loss) command only checks the state of the repository in the Praefect database, and cannot
+The [Praefect `dataloss`](recovery.md#check-for-data-loss) command only checks the state of the repository in the Praefect database, and cannot
be relied to detect sync problems in this scenario.
### Relation does not exist errors
diff --git a/doc/administration/housekeeping.md b/doc/administration/housekeeping.md
index 2d3e937e047..584f06ef537 100644
--- a/doc/administration/housekeeping.md
+++ b/doc/administration/housekeeping.md
@@ -45,11 +45,12 @@ be slow.
### Heuristical housekeeping
-> - [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/2634) in GitLab 14.9 for the [manual trigger](#manual-trigger) and the [push-based trigger](#push-based-trigger) [with a flag](feature_flags.md) named `optimized_housekeeping`. Disabled by default.
+> - [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/2634) in GitLab 14.9 for the [manual trigger](#manual-trigger) and the [push-based trigger](#push-based-trigger) [with a flag](feature_flags.md) named `optimized_housekeeping`. Enabled by default.
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/353607) in GitLab 14.10.
FLAG:
-On self-managed GitLab, by default this feature is not available for the [manual trigger](#manual-trigger) and the [push-based trigger](#push-based-trigger).
+On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to [disable the feature flag](feature_flags.md) named `optimize_repository`.
+
To make it available, ask an administrator to [enable the feature flag](feature_flags.md) named `optimized_housekeeping`.
The heuristical (or "opportunistic") housekeeping strategy analyzes the
@@ -76,9 +77,22 @@ based on the size of the repository:
Gitaly does this to offset the fact that optimizing those data structures takes
more time the bigger they get. It is especially important in large
-monorepositories (which receive a lot of traffic) to avoid optimizing them too
+monorepos (which receive a lot of traffic) to avoid optimizing them too
frequently.
+You can change how often Gitaly is asked to optimize a repository.
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Settings > Repository**.
+1. Expand **Repository maintenance**.
+1. In the **Housekeeping** section, configure the housekeeping options.
+1. Select **Save changes**.
+
+- **Enable automatic repository housekeeping**: Regularly ask Gitaly to run repository optimization. If you
+ keep this setting disabled for a long time, Git repository access on your GitLab server becomes
+ slower and your repositories use more disk space.
+- **Optimize repository period**: Number of Git pushes after which Gitaly is asked to optimize a repository.
+
## Running housekeeping tasks
There are different ways in which GitLab runs housekeeping tasks:
@@ -108,8 +122,13 @@ To trigger housekeeping tasks manually:
This starts an asynchronous background worker for the project's repository. The
background worker executes `git gc`, which performs a number of optimizations.
+<!--- start_remove The following content will be removed on remove_date: '2023-04-22' -->
+
### Push-based trigger
+FLAG:
+On self-managed GitLab, by default this feature is not available and superseded by [heuristical housekeeping](#heuristical-housekeeping). It is planned to be removed in 15.8. To enable the feature, ask an administrator to [disable the feature flag](feature_flags.md) named `optimize_repository`.
+
GitLab automatically runs repository housekeeping tasks after a configured
number of pushes:
@@ -208,7 +227,7 @@ of a repository. When creating the first fork, we:
1. Create an object pool repository that contains all objects of the repository
that is about to be forked.
-1. Link the repository to this new object pool via Git's altenates mechanism.
+1. Link the repository to this new object pool via Git's alternates mechanism.
1. Repack the repository so that it uses objects from the object pool. It thus
can drop its own copy of the objects.
diff --git a/doc/administration/img/audit_events_v14_5.png b/doc/administration/img/audit_events_v14_5.png
deleted file mode 100644
index 57190463d05..00000000000
--- a/doc/administration/img/audit_events_v14_5.png
+++ /dev/null
Binary files differ
diff --git a/doc/administration/img/impersonated_audit_events_v13_8.png b/doc/administration/img/impersonated_audit_events_v13_8.png
deleted file mode 100644
index 0a8548d515d..00000000000
--- a/doc/administration/img/impersonated_audit_events_v13_8.png
+++ /dev/null
Binary files differ
diff --git a/doc/administration/img/impersonated_audit_events_v15_7.png b/doc/administration/img/impersonated_audit_events_v15_7.png
new file mode 100644
index 00000000000..ef7decd984d
--- /dev/null
+++ b/doc/administration/img/impersonated_audit_events_v15_7.png
Binary files differ
diff --git a/doc/administration/inactive_project_deletion.md b/doc/administration/inactive_project_deletion.md
index 88d8e3fc648..ea5658bef84 100644
--- a/doc/administration/inactive_project_deletion.md
+++ b/doc/administration/inactive_project_deletion.md
@@ -12,7 +12,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Administrators of large GitLab instances can find that over time, projects become inactive and are no longer used.
These projects take up unnecessary disk space. With inactive project deletion, you can identify these projects, warn
the maintainers ahead of time, and then delete the projects if they remain inactive. When an inactive project is
-deleted, the action generates an audit event that it was performed by the first active administrator.
+deleted, the action generates an audit event that it was performed by the @GitLab-Admin-Bot.
For the default setting on GitLab.com, see the [GitLab.com settings page](../user/gitlab_com/index.md#inactive-project-deletion).
diff --git a/doc/administration/incoming_email.md b/doc/administration/incoming_email.md
index 433956bb066..826340ad967 100644
--- a/doc/administration/incoming_email.md
+++ b/doc/administration/incoming_email.md
@@ -10,7 +10,7 @@ GitLab has several features based on receiving incoming email messages:
- [Reply by Email](reply_by_email.md): allow GitLab users to comment on issues
and merge requests by replying to notification email.
-- [New issue by email](../user/project/issues/managing_issues.md#by-sending-an-email):
+- [New issue by email](../user/project/issues/create_issues.md#by-sending-an-email):
allow GitLab users to create a new issue by sending an email to a
user-specific email address.
- [New merge request by email](../user/project/merge_requests/creating_merge_requests.md#by-sending-an-email):
diff --git a/doc/administration/index.md b/doc/administration/index.md
index 95db2b7a2e0..1059424da27 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -121,6 +121,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Creating users](../user/profile/account/create_accounts.md): Create users manually or through authentication integrations.
- [Libravatar](libravatar.md): Use Libravatar instead of Gravatar for user avatars.
- [Sign-up restrictions](../user/admin_area/settings/sign_up_restrictions.md): block email addresses of specific domains, or allow only specific domains.
+- [Admin mode](../user/admin_area/settings/sign_in_restrictions.md#admin-mode): require that administrators authenticate separately to use administrative access, like `sudo`.
- [Access restrictions](../user/admin_area/settings/visibility_and_access_controls.md#configure-enabled-git-access-protocols): Define which Git access protocols can be used to talk to GitLab (SSH, HTTP, HTTPS).
- [Authentication and Authorization](auth/index.md): Configure external authentication with LDAP, SAML, CAS, and additional providers.
- [Sync LDAP](auth/ldap/index.md)
@@ -133,7 +134,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- Instances.
- [Auditor users](auditor_users.md): Users with read-only access to all projects, groups, and other resources on the GitLab instance.
- [Incoming email](incoming_email.md): Configure incoming emails to allow
- users to [reply by email](reply_by_email.md), create [issues by email](../user/project/issues/managing_issues.md#by-sending-an-email) and
+ users to [reply by email](reply_by_email.md), create [issues by email](../user/project/issues/create_issues.md#by-sending-an-email) and
[merge requests by email](../user/project/merge_requests/creating_merge_requests.md#by-sending-an-email), and to enable [Service Desk](../user/project/service_desk.md).
- [Postfix for incoming email](reply_by_email_postfix_setup.md): Set up a
basic Postfix mail server with IMAP authentication on Ubuntu for incoming
@@ -208,7 +209,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
## Troubleshooting
- [Log system](logs/index.md): Where to look for logs.
-- [Sidekiq Troubleshooting](troubleshooting/sidekiq.md): Debug when Sidekiq appears hung and is not processing jobs.
+- [Sidekiq Troubleshooting](sidekiq/sidekiq_troubleshooting.md): Debug when Sidekiq appears hung and is not processing jobs.
- [Navigating GitLab via Rails console](operations/rails_console.md)
- [GitLab application limits](instance_limits.md)
- [Responding to security incidents](../security/responding_to_security_incidents.md)
diff --git a/doc/administration/instance_limits.md b/doc/administration/instance_limits.md
index 546c4667220..59746fc0f07 100644
--- a/doc/administration/instance_limits.md
+++ b/doc/administration/instance_limits.md
@@ -378,7 +378,7 @@ and to limit memory consumption.
When using offset-based pagination in the REST API, there is a limit to the maximum
requested offset into the set of results. This limit is only applied to endpoints that
-support keyset-based pagination. More information about pagination options can be
+also support keyset-based pagination. More information about pagination options can be
found in the [API documentation section on pagination](../api/index.md#pagination).
To set this limit for a self-managed installation, run the following in the
@@ -581,7 +581,8 @@ limit value. For example, for a maximum frequency of:
- Once per 10 minutes, the limit must be `144`.
- Once per 60 minutes, the limit must be `24`
-There is no limit for self-managed instances by default.
+The minimum value is `24`, or one pipeline per 60 minutes.
+There is no maximum value.
To set this limit to `1440` on a self-managed installation, run the following in the
[GitLab Rails console](operations/rails_console.md#starting-a-rails-console-session):
@@ -672,6 +673,7 @@ setting is used:
| `ci_max_artifact_size_network_referee` | 0 |
| `ci_max_artifact_size_performance` | 0 |
| `ci_max_artifact_size_requirements` | 0 |
+| `ci_max_artifact_size_requirements_v2` | 0 |
| `ci_max_artifact_size_sast` | 0 |
| `ci_max_artifact_size_secret_detection` | 0 |
| `ci_max_artifact_size_terraform` | 5 MB ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37018) in GitLab 13.3) |
@@ -818,9 +820,9 @@ This limit is [enabled on GitLab.com](../user/gitlab_com/index.md#gitlab-cicd).
You can set a limit on the maximum size of a dotenv artifact. This limit is checked
every time a dotenv file is exported as an artifact.
-Set the limit to `0` to disable it. Defaults to 5KB.
+Set the limit to `0` to disable it. Defaults to 5 KB.
-To set this limit to 5KB on a self-managed installation, run the following in the
+To set this limit to 5 KB on a self-managed installation, run the following in the
[GitLab Rails console](operations/rails_console.md#starting-a-rails-console-session):
```ruby
diff --git a/doc/administration/integration/terminal.md b/doc/administration/integration/terminal.md
index 14da70f6efb..a1522ccac31 100644
--- a/doc/administration/integration/terminal.md
+++ b/doc/administration/integration/terminal.md
@@ -41,7 +41,7 @@ In brief:
- The WebSocket is handled in [Workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse),
rather than the Rails application server.
- Workhorse queries Rails for connection details and user permissions. Rails
- queries Kubernetes for them in the background using [Sidekiq](../troubleshooting/sidekiq.md).
+ queries Kubernetes for them in the background using [Sidekiq](../sidekiq/sidekiq_troubleshooting.md).
- Workhorse acts as a proxy server between the user's browser and the Kubernetes
API, passing WebSocket frames between the two.
- Workhorse regularly polls Rails, terminating the WebSocket connection if the
@@ -64,8 +64,8 @@ detail below.
## Enabling and disabling terminal support
NOTE:
-AWS Classic Load Balancers (CLBs) do not support web sockets.
-If you want web terminals to work, use AWS Network Load Balancers (NLBs).
+AWS Classic Load Balancers do not support web sockets.
+If you want web terminals to work, use AWS Network Load Balancers.
Read [AWS Elastic Load Balancing Product Comparison](https://aws.amazon.com/elasticloadbalancing/features/#compare)
for more information.
diff --git a/doc/administration/issue_closing_pattern.md b/doc/administration/issue_closing_pattern.md
index e9150ae0650..bdbcdd0093c 100644
--- a/doc/administration/issue_closing_pattern.md
+++ b/doc/administration/issue_closing_pattern.md
@@ -17,36 +17,93 @@ in the project's default branch.
## Change the issue closing pattern
-To change the pattern, you must have access to the server that GitLab
-is installed on.
-
-The default pattern can be located in [`gitlab.yml.example`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/gitlab.yml.example)
-under the "Automatic issue closing" section.
+The [default issue closing pattern](../user/project/issues/managing_issues.md#default-closing-pattern)
+covers a wide range of words. You can change the pattern to suit your needs.
NOTE:
You are advised to use <https://rubular.com> to test the issue closing pattern.
-Because Rubular doesn't understand `%{issue_ref}`, you can replace this by
+However, since Rubular doesn't understand `%{issue_ref}`, you can replace this by
`#\d+` when testing your patterns, which matches only local issue references like `#123`.
-**For Omnibus installations**
+To change the default issue closing pattern:
+
+::Tabs
-1. Open `/etc/gitlab/gitlab.rb` with your editor.
-1. Change the value of `gitlab_rails['gitlab_issue_closing_pattern']` to a regular
- expression of your liking:
+:::TabTitle Linux package (Omnibus)
+
+1. Edit `/etc/gitlab/gitlab.rb` and change the `gitlab_rails['gitlab_issue_closing_pattern']`
+ value:
```ruby
- gitlab_rails['gitlab_issue_closing_pattern'] = /\b((?:[Cc]los(?:e[sd]?|ing)|\b[Ff]ix(?:e[sd]|ing)?|\b[Rr]esolv(?:e[sd]?|ing)|\b[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *,? *)?)|([A-Z][A-Z0-9_]+-\d+))+)/.source
+ gitlab_rails['gitlab_issue_closing_pattern'] = /<regular_expression>/.source
+ ```
+
+1. Save the file and reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+:::TabTitle Helm chart (Kubernetes)
+
+1. Export the Helm values:
+
+ ```shell
+ helm get values gitlab > gitlab_values.yaml
+ ```
+
+1. Edit `gitlab_values.yaml` and change the `issueClosingPattern` value:
+
+ ```yaml
+ global:
+ appConfig:
+ issueClosingPattern: "<regular_expression>"
```
-1. [Reconfigure](restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab for the changes to take effect.
+1. Save the file and apply the new values:
-**For installations from source**
+ ```shell
+ helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab
+ ```
+
+:::TabTitle Docker
+
+1. Edit `docker-compose.yml` and change the `gitlab_rails['gitlab_issue_closing_pattern']`
+ value:
+
+ ```yaml
+ version: "3.6"
+ services:
+ gitlab:
+ environment:
+ GITLAB_OMNIBUS_CONFIG: |
+ gitlab_rails['gitlab_issue_closing_pattern'] = /<regular_expression>/.source
+ ```
+
+1. Save the file and restart GitLab:
-1. Open `gitlab.yml` with your editor.
-1. Change the value of `issue_closing_pattern`:
+ ```shell
+ docker compose up -d
+ ```
+
+:::TabTitle Self-compiled (source)
+
+1. Edit `/home/git/gitlab/config/gitlab.yml` and change the `issue_closing_pattern` value:
```yaml
- issue_closing_pattern: "\b((?:[Cc]los(?:e[sd]?|ing)|\b[Ff]ix(?:e[sd]|ing)?|\b[Rr]esolv(?:e[sd]?|ing)|\b[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *,? *)?)|([A-Z][A-Z0-9_]+-\d+))+)"
+ production: &base
+ gitlab:
+ issue_closing_pattern: "<regular_expression>"
+ ```
+
+1. Save the file and restart GitLab:
+
+ ```shell
+ # For systems running systemd
+ sudo systemctl restart gitlab.target
+
+ # For systems running SysV init
+ sudo service gitlab restart
```
-1. [Restart](restart_gitlab.md#installations-from-source) GitLab for the changes to take effect.
+::EndTabs
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index d79f322c3f5..fb2f73876e8 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -100,8 +100,7 @@ In a multi-server setup you must use one of the options to
#### Object Storage Settings
-NOTE:
-In GitLab 13.2 and later, we recommend using the
+In GitLab 13.2 and later, you should use the
[consolidated object storage settings](object_storage.md#consolidated-object-storage-configuration).
This section describes the earlier configuration format.
@@ -235,7 +234,7 @@ by the `gitlab:artifacts:migrate` Rake task.
To migrate back to local storage:
1. Run `gitlab-rake gitlab:artifacts:migrate_to_local`.
-1. Disable object_storage for artifacts in `gitlab.rb`:
+1. Disable object storage for artifacts in `gitlab.rb`:
- Set `gitlab_rails['artifacts_object_store_enabled'] = false`.
- Comment out all other `artifacts_object_store` settings, including the entire
`artifacts_object_store_connection` section, including the closing `}`.
@@ -636,24 +635,6 @@ If you need to manually remove **all** job artifacts associated with multiple jo
- `3.months.ago`
- `1.year.ago`
-### Error `Downloading artifacts from coordinator... not found`
-
-When a job attempts to download artifacts from an earlier job, you might receive an error message similar to:
-
-```plaintext
-Downloading artifacts from coordinator... not found id=12345678 responseStatus=404 Not Found
-```
-
-This can be caused by a `gitlab.rb` file with the following configuration:
-
-```ruby
-gitlab_rails['artifacts_object_store_background_upload'] = false
-gitlab_rails['artifacts_object_store_direct_upload'] = true
-```
-
-To prevent this, comment out or remove those lines, or switch to their [default values](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template), and
-then run `sudo gitlab-ctl reconfigure`.
-
### Job artifact upload fails with error 500
If you are using object storage for artifacts and a job artifact fails to upload,
diff --git a/doc/administration/job_logs.md b/doc/administration/job_logs.md
index 5aa0e8f3948..9b25c70716b 100644
--- a/doc/administration/job_logs.md
+++ b/doc/administration/job_logs.md
@@ -158,7 +158,7 @@ To eliminate both file system requirements:
The data flow is the same as described in the [data flow section](#data-flow)
with one change: _the stored path of the first two phases is different_. This incremental
log architecture stores chunks of logs in Redis and a persistent store (object storage or database) instead of
-file storage. Redis is used as first-class storage, and it stores up-to 128KB
+file storage. Redis is used as first-class storage, and it stores up-to 128 KB
of data. After the full chunk is sent, it is flushed to a persistent store, either object storage (temporary directory) or database.
After a while, the data in Redis and a persistent store is archived to [object storage](#uploading-logs-to-object-storage).
@@ -169,7 +169,7 @@ Here is the detailed data flow:
1. The runner picks a job from GitLab
1. The runner sends a piece of log to GitLab
1. GitLab appends the data to Redis
-1. After the data in Redis reaches 128KB, the data is flushed to a persistent store (object storage or the database).
+1. After the data in Redis reaches 128 KB, the data is flushed to a persistent store (object storage or the database).
1. The above steps are repeated until the job is finished.
1. After the job is finished, GitLab schedules a Sidekiq worker to archive the log.
1. The Sidekiq worker archives the log to object storage and cleans up the log
diff --git a/doc/administration/lfs/index.md b/doc/administration/lfs/index.md
index 8fdc98bd12a..cf80b05a5e0 100644
--- a/doc/administration/lfs/index.md
+++ b/doc/administration/lfs/index.md
@@ -7,7 +7,8 @@ disqus_identifier: 'https://docs.gitlab.com/ee/workflow/lfs/lfs_administration.h
# GitLab Git Large File Storage (LFS) Administration **(FREE SELF)**
-Documentation about how to use Git LFS are under [Managing large binary files with Git LFS doc](../../topics/git/lfs/index.md).
+This page contains information about configuring Git LFS in self-managed GitLab instances.
+For user documentation about Git LFS, see [Git Large File Storage](../../topics/git/lfs/index.md).
LFS is enabled in GitLab self-managed instances by default.
@@ -62,27 +63,17 @@ to check which storage services can be integrated with GitLab.
You can also use external object storage in a private local network. For example,
[MinIO](https://min.io/) is a standalone object storage service that works with GitLab instances.
-GitLab provides two different options for the uploading mechanism: "Direct upload" and "Background upload".
-
[Read more about using object storage with GitLab](../object_storage.md).
NOTE:
-In GitLab 13.2 and later, we recommend using the
+In GitLab 13.2 and later, you should use the
[consolidated object storage settings](../object_storage.md#consolidated-object-storage-configuration).
-This section describes the earlier configuration format.
-
-**Option 1. Direct upload**
+This section describes the earlier configuration format. [Migration steps still apply](#migrating-to-object-storage).
1. User pushes an `lfs` file to the GitLab instance.
1. GitLab-workhorse uploads the file directly to the external object storage.
1. GitLab-workhorse notifies GitLab-rails that the upload process is complete.
-**Option 2. Background upload**
-
-1. User pushes an `lfs` file to the GitLab instance.
-1. GitLab-rails stores the file in the local file storage.
-1. GitLab-rails then uploads the file to the external object storage asynchronously.
-
The following general settings are supported.
| Setting | Description | Default |
@@ -119,9 +110,7 @@ On Omnibus GitLab installations, the settings are prefixed by `lfs_object_store_
1. Save the file, and then [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
1. [Migrate any existing local LFS objects to the object storage](#migrating-to-object-storage).
- New LFS objects
- are forwarded to object storage unless
- `gitlab_rails['lfs_object_store_background_upload']` and `gitlab_rails['lfs_object_store_direct_upload']` is set to `false`.
+ New LFS objects are forwarded to object storage.
### S3 for installations from source
@@ -150,9 +139,7 @@ For source installations the settings are nested under `lfs:` and then
1. Save the file, and then [restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect.
1. [Migrate any existing local LFS objects to the object storage](#migrating-to-object-storage).
- New LFS objects
- are forwarded to object storage unless
- `background_upload` and `direct_upload` is set to `false`.
+ New LFS objects are forwarded to object storage.
### Migrating to object storage
@@ -231,6 +218,12 @@ You can see the total storage used for LFS objects on groups and projects:
- In the administration area.
- In the [groups](../../api/groups.md) and [projects APIs](../../api/projects.md).
+## Related topics
+
+- Blog post: [Getting started with Git LFS](https://about.gitlab.com/blog/2017/01/30/getting-started-with-git-lfs-tutorial/)
+- User documentation: [Git Large File Storage (LFS)](../../topics/git/lfs/index.md)
+- [Git LFS developer information](../../development/lfs.md)
+
## Troubleshooting
### Missing LFS objects
@@ -276,39 +269,6 @@ To delete these references:
lfs_object.destroy
```
-### `Google::Apis::TransmissionError: execution expired`
-
-If LFS integration is configured with Google Cloud Storage and background uploads (`background_upload: true` and `direct_upload: false`),
-Sidekiq workers may encounter this error. This is because the uploading timed out with very large files.
-LFS files up to 6 GB can be uploaded without any extra steps, otherwise you need to use the following workaround.
-
-Sign in to Rails console:
-
-```shell
-sudo gitlab-rails console
-```
-
-Set up timeouts:
-
-- These settings are only in effect for the same session. For example, they are not effective for Sidekiq workers.
-- 20 minutes (1200 sec) is enough to upload 30GB LFS files:
-
-```ruby
-::Google::Apis::ClientOptions.default.open_timeout_sec = 1200
-::Google::Apis::ClientOptions.default.read_timeout_sec = 1200
-::Google::Apis::ClientOptions.default.send_timeout_sec = 1200
-```
-
-Upload LFS files manually (this process does not use Sidekiq at all):
-
-```ruby
-LfsObject.where(file_store: [nil, 1]).find_each do |lfs_object|
- lfs_object.file.migrate!(ObjectStorage::Store::REMOTE) if lfs_object.file.file.exists?
-end
-```
-
-See more information in [!19581](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/19581)
-
### LFS commands fail on TLS v1.3 server
If you configure GitLab to [disable TLS v1.2](https://docs.gitlab.com/omnibus/settings/nginx.html)
@@ -332,6 +292,28 @@ To check an installed Git LFS client's version, run this command:
git lfs version
```
+## Error viewing a PDF file
+
+When LFS has been configured with object storage and `proxy_download` set to
+`false`, [you may see an error when previewing a PDF file from the Web browser](https://gitlab.com/gitlab-org/gitlab/-/issues/248100):
+
+```plaintext
+An error occurred while loading the file. Please try again later.
+```
+
+This occurs due to Cross-Origin Resource Sharing (CORS) restrictions:
+the browser attempts to load the PDF from object storage, but the object
+storage provider rejects the request because the GitLab domain differs
+from the object storage domain.
+
+To fix this issue, configure your object storage provider's CORS
+settings to allow the GitLab domain. See the following documentation
+for more details:
+
+1. [AWS S3](https://aws.amazon.com/premiumsupport/knowledge-center/s3-configure-cors/)
+1. [Google Cloud Storage](https://cloud.google.com/storage/docs/configuring-cors)
+1. [Azure Storage](https://learn.microsoft.com/en-us/rest/api/storageservices/cross-origin-resource-sharing--cors--support-for-the-azure-storage-services).
+
## Known limitations
- Only compatible with the Git LFS client versions 1.1.0 and later, or 1.0.2.
diff --git a/doc/administration/logs/index.md b/doc/administration/logs/index.md
index b0631c52a47..9893c6935a3 100644
--- a/doc/administration/logs/index.md
+++ b/doc/administration/logs/index.md
@@ -6,9 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Log system **(FREE SELF)**
-GitLab has an advanced log system where everything is logged, so you
-can analyze your instance using various system log files. In addition to
-system log files, GitLab Enterprise Edition provides [Audit Events](../audit_events.md).
+GitLab has an advanced log system where everything is logged, so you can analyze your instance using various system log
+files. The log system is similar to [audit events](../audit_events.md).
System log files are typically plain text in a standard log file format.
This guide talks about how to read and use these system log files.
@@ -86,24 +85,24 @@ except those captured by `runit`.
| Log type | Managed by logrotate | Managed by svlogd/runit |
|:------------------------------------------------|:------------------------|:------------------------|
-| [Alertmanager Logs](#alertmanager-logs) | **{dotted-circle}** No | **{check-circle}** Yes |
-| [Crond Logs](#crond-logs) | **{dotted-circle}** No | **{check-circle}** Yes |
+| [Alertmanager logs](#alertmanager-logs) | **{dotted-circle}** No | **{check-circle}** Yes |
+| [crond logs](#crond-logs) | **{dotted-circle}** No | **{check-circle}** Yes |
| [Gitaly](#gitaly-logs) | **{check-circle}** Yes | **{check-circle}** Yes |
| [GitLab Exporter for Omnibus](#gitlab-exporter) | **{dotted-circle}** No | **{check-circle}** Yes |
-| [GitLab Pages Logs](#pages-logs) | **{check-circle}** Yes | **{check-circle}** Yes |
+| [GitLab Pages logs](#pages-logs) | **{check-circle}** Yes | **{check-circle}** Yes |
| GitLab Rails | **{check-circle}** Yes | **{dotted-circle}** No |
-| [GitLab Shell Logs](#gitlab-shelllog) | **{check-circle}** Yes | **{dotted-circle}** No |
-| [Grafana Logs](#grafana-logs) | **{dotted-circle}** No | **{check-circle}** Yes |
-| [LogRotate Logs](#logrotate-logs) | **{dotted-circle}** No | **{check-circle}** Yes |
+| [GitLab Shell logs](#gitlab-shelllog) | **{check-circle}** Yes | **{dotted-circle}** No |
+| [Grafana logs](#grafana-logs) | **{dotted-circle}** No | **{check-circle}** Yes |
+| [LogRotate logs](#logrotate-logs) | **{dotted-circle}** No | **{check-circle}** Yes |
| [Mailroom](#mail_room_jsonlog-default) | **{check-circle}** Yes | **{check-circle}** Yes |
| [NGINX](#nginx-logs) | **{check-circle}** Yes | **{check-circle}** Yes |
-| [PostgreSQL Logs](#postgresql-logs) | **{dotted-circle}** No | **{check-circle}** Yes |
-| [Praefect Logs](#praefect-logs) | **{dotted-circle}** Yes | **{check-circle}** Yes |
-| [Prometheus Logs](#prometheus-logs) | **{dotted-circle}** No | **{check-circle}** Yes |
+| [PostgreSQL logs](#postgresql-logs) | **{dotted-circle}** No | **{check-circle}** Yes |
+| [Praefect logs](#praefect-logs) | **{dotted-circle}** Yes | **{check-circle}** Yes |
+| [Prometheus logs](#prometheus-logs) | **{dotted-circle}** No | **{check-circle}** Yes |
| [Puma](#puma-logs) | **{check-circle}** Yes | **{check-circle}** Yes |
-| [Redis Logs](#redis-logs) | **{dotted-circle}** No | **{check-circle}** Yes |
-| [Registry Logs](#registry-logs) | **{dotted-circle}** No | **{check-circle}** Yes |
-| [Workhorse Logs](#workhorse-logs) | **{check-circle}** Yes | **{check-circle}** Yes |
+| [Redis logs](#redis-logs) | **{dotted-circle}** No | **{check-circle}** Yes |
+| [Registry logs](#registry-logs) | **{dotted-circle}** No | **{check-circle}** Yes |
+| [Workhorse logs](#workhorse-logs) | **{check-circle}** Yes | **{check-circle}** Yes |
## `production_json.log`
@@ -161,10 +160,14 @@ seconds:
- `gitaly_duration_s`: Total time by Gitaly calls
- `gitaly_calls`: Total number of calls made to Gitaly
- `redis_calls`: Total number of calls made to Redis
+- `redis_cross_slot_calls`: Total number of cross-slot calls made to Redis
+- `redis_allowed_cross_slot_calls`: Total number of allowed cross-slot calls made to Redis
- `redis_duration_s`: Total time to retrieve data from Redis
- `redis_read_bytes`: Total bytes read from Redis
- `redis_write_bytes`: Total bytes written to Redis
- `redis_<instance>_calls`: Total number of calls made to a Redis instance
+- `redis_<instance>_cross_slot_calls`: Total number of cross-slot calls made to a Redis instance
+- `redis_<instance>_allowed_cross_slot_calls`: Total number of allowed cross-slot calls made to a Redis instance
- `redis_<instance>_duration_s`: Total time to retrieve data from a Redis instance
- `redis_<instance>_read_bytes`: Total bytes read from a Redis instance
- `redis_<instance>_write_bytes`: Total bytes written to a Redis instance
@@ -490,7 +493,7 @@ are logged to this file. For example:
}
```
-## Sidekiq Logs
+## Sidekiq logs
NOTE:
In Omnibus GitLab `12.10` or earlier, the Sidekiq log is at `/var/log/gitlab/gitlab-rails/sidekiq.log`.
@@ -670,7 +673,7 @@ I, [2015-02-13T06:17:00.679433 #9291] INFO -- : Moving existing hooks directory
User clone/fetch activity using SSH transport appears in this log as
`executing git command <gitaly-upload-pack...`.
-## Gitaly Logs
+## Gitaly logs
This file is in `/var/log/gitlab/gitaly/current` and is produced by [runit](http://smarden.org/runit/).
`runit` is packaged with Omnibus GitLab and a brief explanation of its purpose
@@ -697,7 +700,7 @@ This file is at `/var/log/gitlab/gitaly/gitaly_hooks.log` and is
produced by `gitaly-hooks` command. It also contains records about
failures received during processing of the responses from GitLab API.
-## Puma Logs
+## Puma logs
### `puma_stdout.log`
@@ -980,11 +983,11 @@ can be used.
}
```
-## Registry Logs
+## Registry logs
For Omnibus GitLab installations, Container Registry logs are in `/var/log/gitlab/registry/current`.
-## NGINX Logs
+## NGINX logs
For Omnibus GitLab installations, NGINX logs are in:
@@ -1003,7 +1006,7 @@ Below is the default GitLab NGINX access log format:
$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"
```
-## Pages Logs
+## Pages logs
For Omnibus GitLab installations, Pages logs are in `/var/log/gitlab/gitlab-pages/current`.
@@ -1032,50 +1035,50 @@ For example:
}
```
-## Mattermost Logs
+## Mattermost logs
For Omnibus GitLab installations, Mattermost logs are in these locations:
- `/var/log/gitlab/mattermost/mattermost.log`
- `/var/log/gitlab/mattermost/current`
-## Workhorse Logs
+## Workhorse logs
For Omnibus GitLab installations, Workhorse logs are in `/var/log/gitlab/gitlab-workhorse/current`.
-## PostgreSQL Logs
+## PostgreSQL logs
For Omnibus GitLab installations, PostgreSQL logs are in `/var/log/gitlab/postgresql/current`.
-## Prometheus Logs
+## Prometheus logs
For Omnibus GitLab installations, Prometheus logs are in `/var/log/gitlab/prometheus/current`.
-## Redis Logs
+## Redis logs
For Omnibus GitLab installations, Redis logs are in `/var/log/gitlab/redis/current`.
-## Alertmanager Logs
+## Alertmanager logs
For Omnibus GitLab installations, Alertmanager logs are in `/var/log/gitlab/alertmanager/current`.
<!-- vale gitlab.Spelling = NO -->
-## Crond Logs
+## crond logs
For Omnibus GitLab installations, crond logs are in `/var/log/gitlab/crond/`.
<!-- vale gitlab.Spelling = YES -->
-## Grafana Logs
+## Grafana logs
For Omnibus GitLab installations, Grafana logs are in `/var/log/gitlab/grafana/current`.
-## LogRotate Logs
+## LogRotate logs
For Omnibus GitLab installations, `logrotate` logs are in `/var/log/gitlab/logrotate/current`.
-## GitLab Monitor Logs
+## GitLab Monitor logs
For Omnibus GitLab installations, GitLab Monitor logs are in `/var/log/gitlab/gitlab-monitor/`.
@@ -1088,7 +1091,7 @@ For Omnibus GitLab installations, GitLab Exporter logs are in `/var/log/gitlab/g
For Omnibus GitLab installations, GitLab agent server logs are
in `/var/log/gitlab/gitlab-kas/current`.
-## Praefect Logs
+## Praefect logs
For Omnibus GitLab installations, Praefect logs are in `/var/log/gitlab/praefect/`.
diff --git a/doc/administration/maintenance_mode/index.md b/doc/administration/maintenance_mode/index.md
index 9adb8ce2cd9..c30465b4df9 100644
--- a/doc/administration/maintenance_mode/index.md
+++ b/doc/administration/maintenance_mode/index.md
@@ -84,7 +84,7 @@ them to disable Maintenance Mode after it's been enabled.
All users can sign in and out of the GitLab instance but no new users can be created.
-If there are [LDAP syncs](../auth/ldap/index.md) scheduled for that time, they fail since user creation is disabled. Similarly, [user creations based on SAML](../../integration/saml.md#general-setup) fail.
+If there are [LDAP syncs](../auth/ldap/index.md) scheduled for that time, they fail since user creation is disabled. Similarly, [user creations based on SAML](../../integration/saml.md#configure-saml-support-in-gitlab) fail.
### Git actions
@@ -178,7 +178,7 @@ To monitor queues and disable jobs:
### Incident management
-[Incident management](../../operations/incident_management/index.md) functions are limited. The creation of [alerts](../../operations/incident_management/alerts.md) and [incidents](../../operations/incident_management/incidents.md#incident-creation) are paused entirely. Notifications and paging on alerts and incidents are therefore disabled.
+[Incident management](../../operations/incident_management/index.md) functions are limited. The creation of [alerts](../../operations/incident_management/alerts.md) and [incidents](../../operations/incident_management/manage_incidents.md#create-an-incident) are paused entirely. Notifications and paging on alerts and incidents are therefore disabled.
### Feature flags
diff --git a/doc/administration/merge_request_diffs.md b/doc/administration/merge_request_diffs.md
index 4dec9e52c19..25dba00beb9 100644
--- a/doc/administration/merge_request_diffs.md
+++ b/doc/administration/merge_request_diffs.md
@@ -104,8 +104,7 @@ be configured already.
### Object Storage Settings
-NOTE:
-In GitLab 13.2 and later, we recommend using the
+In GitLab 13.2 and later, you should use the
[consolidated object storage settings](object_storage.md#consolidated-object-storage-configuration).
This section describes the earlier configuration format.
diff --git a/doc/administration/monitoring/gitlab_self_monitoring_project/index.md b/doc/administration/monitoring/gitlab_self_monitoring_project/index.md
index 35dc64a0594..566bc070347 100644
--- a/doc/administration/monitoring/gitlab_self_monitoring_project/index.md
+++ b/doc/administration/monitoring/gitlab_self_monitoring_project/index.md
@@ -101,7 +101,7 @@ You can add custom metrics in the self-monitoring project by:
A [bug](https://gitlab.com/gitlab-org/gitlab/-/issues/208676) causes project creation to fail with
the following error in the log file when the first administrator user is an
-[external user](../../../user/permissions.md#external-users):
+[external user](../../../user/admin_area/external_users.md):
```plaintext
Could not create instance administrators group. Errors: ["You don't have permission to create groups."]
@@ -116,6 +116,6 @@ User.admins.active.first.external?
If this returns true, the first administrator user is an external user.
If you face this issue, you can temporarily
-[make the administrator user a non-external user](../../../user/permissions.md#external-users)
+[make the administrator user a non-external user](../../../user/admin_area/external_users.md)
and then try to create the project.
After the project is created, the administrator user can be changed back to an external user.
diff --git a/doc/administration/monitoring/performance/performance_bar.md b/doc/administration/monitoring/performance/performance_bar.md
index de6178a3151..171a441eaec 100644
--- a/doc/administration/monitoring/performance/performance_bar.md
+++ b/doc/administration/monitoring/performance/performance_bar.md
@@ -1,6 +1,6 @@
---
-stage: Monitor
-group: Respond
+stage: Data Stores
+group: Application Performance
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
diff --git a/doc/administration/monitoring/prometheus/gitlab_exporter.md b/doc/administration/monitoring/prometheus/gitlab_exporter.md
index bce35306505..1f4156349c5 100644
--- a/doc/administration/monitoring/prometheus/gitlab_exporter.md
+++ b/doc/administration/monitoring/prometheus/gitlab_exporter.md
@@ -1,6 +1,6 @@
---
-stage: Monitor
-group: Respond
+stage: Data Stores
+group: Application Performance
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index d3b28fd15bc..53ee028bc32 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -39,8 +39,9 @@ The following metrics are available:
| `gitlab_cache_misses_total` | Counter | 10.2 | Cache read miss | `controller`, `action` |
| `gitlab_cache_operation_duration_seconds` | Histogram | 10.2 | Cache access time | |
| `gitlab_cache_operations_total` | Counter | 12.2 | Cache operations by controller or action | `controller`, `action`, `operation` |
+| `gitlab_cache_read_multikey_count` | Histogram | 15.7 | Count of keys in multi-key cache read operations | `controller`, `action` |
| `gitlab_ci_pipeline_builder_scoped_variables_duration` | Histogram | 14.5 | Time in seconds it takes to create the scoped variables for a CI/CD job
-| `gitlab_ci_pipeline_creation_duration_seconds` | Histogram | 13.0 | Time in seconds it takes to create a CI/CD pipeline | |
+| `gitlab_ci_pipeline_creation_duration_seconds` | Histogram | 13.0 | Time in seconds it takes to create a CI/CD pipeline | `gitlab` |
| `gitlab_ci_pipeline_size_builds` | Histogram | 13.1 | Total number of builds within a pipeline grouped by a pipeline source | `source` |
| `gitlab_ci_runner_authentication_success_total` | Counter | 15.2 | Total number of times that runner authentication has succeeded | `type` |
| `gitlab_ci_runner_authentication_failure_total` | Counter | 15.2 | Total number of times that runner authentication has failed
@@ -331,6 +332,16 @@ configuration option in `gitlab.yml`. These metrics are served from the
| `geo_dependency_proxy_blob_verification_total` | Gauge | 15.6 | Number of dependency proxy blobs verifications tried on secondary | |
| `geo_dependency_proxy_blob_verified` | Gauge | 15.6 | Number of dependency proxy blobs verified on secondary | |
| `geo_dependency_proxy_blob_verification_failed` | Gauge | 15.6 | Number of dependency proxy blobs verifications failed on secondary | |
+| `geo_dependency_proxy_manifests` | Gauge | 15.6 | Number of dependency proxy manifests on primary | `url` |
+| `geo_dependency_proxy_manifests_checksum_total` | Gauge | 15.6 | Number of dependency proxy manifests tried to checksum on primary | `url` |
+| `geo_dependency_proxy_manifests_checksummed` | Gauge | 15.6 | Number of dependency proxy manifests successfully checksummed on primary | `url` |
+| `geo_dependency_proxy_manifests_checksum_failed` | Gauge | 15.6 | Number of dependency proxy manifests failed to calculate the checksum on primary | `url` |
+| `geo_dependency_proxy_manifests_synced` | Gauge | 15.6 | Number of syncable dependency proxy manifests synced on secondary | `url` |
+| `geo_dependency_proxy_manifests_failed` | Gauge | 15.6 | Number of syncable dependency proxy manifests failed to sync on secondary | `url` |
+| `geo_dependency_proxy_manifests_registry` | Gauge | 15.6 | Number of dependency proxy manifests in the registry | `url` |
+| `geo_dependency_proxy_manifests_verification_total` | Gauge | 15.6 | Number of dependency proxy manifests verifications tried on secondary | `url` |
+| `geo_dependency_proxy_manifests_verified` | Gauge | 15.6 | Number of dependency proxy manifests verified on secondary | `url` |
+| `geo_dependency_proxy_manifests_verification_failed` | Gauge | 15.6 | Number of dependency proxy manifests verifications failed on secondary | `url` |
## Database load balancing metrics **(PREMIUM SELF)**
diff --git a/doc/administration/nfs.md b/doc/administration/nfs.md
index 85f35a1b188..972db315268 100644
--- a/doc/administration/nfs.md
+++ b/doc/administration/nfs.md
@@ -20,47 +20,14 @@ actions that read or write to Git repositories. For steps you can use to test
file system performance, see
[File System Performance Benchmarking](operations/filesystem_benchmarking.md).
-## Gitaly and NFS deprecation
+## Gitaly with NFS not supported
-Starting with GitLab version 14.0, support for NFS to store Git repository data is deprecated. Technical customer support and engineering support is available for the 14.x releases. Engineering is fixing bugs and security vulnerabilities consistent with our [release and maintenance policy](../policy/maintenance.md#security-releases).
+Technical and engineering support for using NFS to store Git repository data is officially at end-of-life. No product
+changes or troubleshooting is provided through engineering, security or paid support channels.
-Upon the release of GitLab 15.6 technical and engineering support for using NFS to store Git repository data is officially at end-of-life. There are no product changes or troubleshooting provided via Engineering, Security or Paid Support channels after the release date of 15.6, regardless of your GitLab version.
-
-Until the release of 15.6, for customers running 14.x releases, we continue to help with Git related tickets from customers running one or more Gitaly servers with its data stored on NFS. Examples may include:
-
-- Performance issues or timeouts accessing Git data
-- Commits or branches vanish
-- GitLab intermittently returns the wrong Git data (such as reporting that a repository has no branches)
-
-Assistance is limited to activities like:
-
-- Verifying developers' workflow uses features like protected branches
-- Reviewing GitLab event data from the database to advise if it looks like a force push over-wrote branches
-- Verifying that NFS client mount options match our [documented recommendations](#mount-options)
-- Analyzing the GitLab Workhorse and Rails logs, and determining that `500` errors being seen in the environment are caused by slow responses from Gitaly
-
-GitLab support is unable to continue with the investigation if both:
-
-- The date of the request is on or after the release of GitLab version 15.6.
-- Support Engineers and Management determine that all reasonable non-NFS root causes have been exhausted.
-
-If the issue is reproducible, or if it happens intermittently but regularly, GitLab Support can investigate providing the issue reproduces without the use of NFS. To reproduce without NFS, the affected repositories should be migrated to a different Gitaly shard, such as Gitaly cluster or a standalone Gitaly VM, backed with block storage.
-
-### Why remove NFS for Git repository data
-
-{:.no-toc}
-
-NFS is not well-suited to a workload consisting of many small files, like Git repositories. NFS does provide a number of configuration options designed to improve performance. However, over time, a number of these mount options have proven to result in inconsistencies across multiple nodes mounting the NFS volume, up to and including data loss. Addressing these inconsistencies consume extraordinary development and support engineer time that hamper our ability to develop [Gitaly Cluster](gitaly/praefect.md), our purpose-built solution to addressing the deficiencies of NFS in this environment.
-
-Gitaly Cluster provides highly-available Git repository storage. If this is not a requirement, single-node Gitaly backed by block storage is a suitable substitute.
-
-Engineering support for NFS for Git repositories is deprecated. Technical support is planned to be
-unavailable from GitLab 15.0. No further enhancements are planned for this feature.
-
-Read:
-
-- [Moving beyond NFS](gitaly/index.md#moving-beyond-nfs).
-- About the [correct mount options to use](#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
+If an issue is reproducible, or if it happens intermittently but regularly, GitLab Support can investigate providing the
+issue can be reproduced without NFS. To reproduce without NFS, migrate the affected repositories to a different Gitaly
+shard. For example, a Gitaly Cluster or a standalone Gitaly VM, backed with block storage.
## Known kernel version incompatibilities
@@ -397,8 +364,8 @@ sudo ufw allow from <client_ip_address> to any port nfs
### Upgrade to Gitaly Cluster or disable caching if experiencing data loss
WARNING:
-Engineering support for NFS for Git repositories is deprecated. Read about
-[moving beyond NFS](gitaly/index.md#moving-beyond-nfs).
+Engineering support for NFS for Git repositories
+[is unavailable](../update/removals.md#nfs-as-git-repository-storage-is-no-longer-supported).
Customers and users have reported data loss on high-traffic repositories when using NFS for Git repositories.
For example, we have seen:
diff --git a/doc/administration/object_storage.md b/doc/administration/object_storage.md
index d2c9c35148c..0cae46faf28 100644
--- a/doc/administration/object_storage.md
+++ b/doc/administration/object_storage.md
@@ -70,7 +70,7 @@ must be enabled, only the following providers can be used:
- [Azure Blob storage](#azure-blob-storage)
When consolidated object storage is used, direct upload is enabled
-automatically. Background upload is not supported. For storage-specific
+automatically. For storage-specific
configuration, [direct upload may become the default](https://gitlab.com/gitlab-org/gitlab/-/issues/27331)
because it does not require a shared folder.
@@ -482,7 +482,7 @@ To migrate existing local data to object storage see the following guides:
- [LFS objects](lfs/index.md#migrating-to-object-storage)
- [Uploads](raketasks/uploads/migrate.md#migrate-to-object-storage)
- [Merge request diffs](merge_request_diffs.md#using-object-storage)
-- [Packages](packages/index.md#migrating-local-packages-to-object-storage) (optional feature)
+- [Packages](packages/index.md#migrate-local-packages-to-object-storage) (optional feature)
- [Dependency Proxy](packages/dependency_proxy.md#migrate-local-dependency-proxy-blobs-and-manifests-to-object-storage)
- [Terraform state files](terraform_state.md#migrate-to-object-storage)
- [Pages content](pages/index.md#migrate-pages-deployments-to-object-storage)
@@ -536,8 +536,8 @@ supported by consolidated configuration form, refer to the following guides:
| [Uploads](uploads.md#using-object-storage) | **{check-circle}** Yes |
| [Container Registry](packages/container_registry.md#use-object-storage) (optional feature) | **{dotted-circle}** No |
| [Merge request diffs](merge_request_diffs.md#using-object-storage) | **{check-circle}** Yes |
-| [Mattermost](https://docs.mattermost.com/administration/config-settings.html#file-storage)| **{dotted-circle}** No |
-| [Packages](packages/index.md#using-object-storage) (optional feature) | **{check-circle}** Yes |
+| [Mattermost](https://docs.mattermost.com/configure/file-storage-configuration-settings.html)| **{dotted-circle}** No |
+| [Packages](packages/index.md#use-object-storage) (optional feature) | **{check-circle}** Yes |
| [Dependency Proxy](packages/dependency_proxy.md#using-object-storage) (optional feature) | **{check-circle}** Yes |
| [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | **{dotted-circle}** No |
| [Terraform state files](terraform_state.md#using-object-storage) | **{check-circle}** Yes |
@@ -638,9 +638,15 @@ if this access is not in place include:
Received status code 403 from server: Forbidden
```
-Getting a `403 Forbidden` response is specifically called out on the
-[package repository documentation](packages/index.md#using-object-storage)
-as a side effect of how some build tools work.
+- Object storage buckets need to allow Cross-Origin Resource Sharing
+ (CORS) access from the URL of the GitLab instance. Attempting to load
+ a PDF in the repository page may show the following error:
+
+ ```plaintext
+ An error occurred while loading the file. Please try again later.
+ ```
+
+ See [the LFS documentation](lfs/index.md#error-viewing-a-pdf-file) for more details.
Additionally for a short time period users could share pre-signed, time-limited object storage URLs
with others without authentication. Also bandwidth charges may be incurred
diff --git a/doc/administration/operations/extra_sidekiq_processes.md b/doc/administration/operations/extra_sidekiq_processes.md
deleted file mode 100644
index 58858c54843..00000000000
--- a/doc/administration/operations/extra_sidekiq_processes.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../sidekiq/extra_sidekiq_processes.md'
-remove_date: '2022-11-11'
----
-
-This document was moved to [another location](../sidekiq/extra_sidekiq_processes.md).
-
-<!-- This redirect file can be deleted after <2022-11-11>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/administration/operations/extra_sidekiq_routing.md b/doc/administration/operations/extra_sidekiq_routing.md
deleted file mode 100644
index 072b6f63537..00000000000
--- a/doc/administration/operations/extra_sidekiq_routing.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../sidekiq/extra_sidekiq_routing.md'
-remove_date: '2022-11-11'
----
-
-This document was moved to [another location](../sidekiq/extra_sidekiq_routing.md).
-
-<!-- This redirect file can be deleted after <2022-11-11>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/administration/operations/filesystem_benchmarking.md b/doc/administration/operations/filesystem_benchmarking.md
index ec2975baf52..cd4ab1a9cf8 100644
--- a/doc/administration/operations/filesystem_benchmarking.md
+++ b/doc/administration/operations/filesystem_benchmarking.md
@@ -19,7 +19,7 @@ I/O. The information on this page can be used for either scenario.
### Benchmarking with `fio`
-We recommend using
+You should use
[Fio](https://fio.readthedocs.io/en/latest/fio_doc.html) to test I/O
performance. This test should be run both on the NFS server and on the
application nodes that talk to the NFS server.
@@ -35,8 +35,8 @@ Then run the following:
fio --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 --name=test --bs=4k --iodepth=64 --readwrite=randrw --rwmixread=75 --size=4G --filename=/path/to/git-data/testfile
```
-This creates a 4GB file in `/path/to/git-data/testfile`. It performs
-4KB reads and writes using a 75%/25% split in the file, with 64
+This creates a 4 GB file in `/path/to/git-data/testfile`. It performs
+4 KB reads and writes using a 75%/25% split in the file, with 64
operations running at a time. Be sure to delete the file after the test
completes.
diff --git a/doc/administration/operations/index.md b/doc/administration/operations/index.md
index d18f41becd5..527a72c5933 100644
--- a/doc/administration/operations/index.md
+++ b/doc/administration/operations/index.md
@@ -13,10 +13,9 @@ Keep your GitLab instance up and running smoothly.
and more.
- [Moving repositories](moving_repositories.md): Moving all repositories managed
by GitLab to another file system or another server.
-- [Sidekiq MemoryKiller](sidekiq_memory_killer.md): Configure Sidekiq MemoryKiller
+- [Sidekiq MemoryKiller](../sidekiq/sidekiq_memory_killer.md): Configure Sidekiq MemoryKiller
to restart Sidekiq.
-- [Multiple Sidekiq processes](extra_sidekiq_processes.md): Configure multiple Sidekiq processes to ensure certain queues always have dedicated workers, no matter the number of jobs that must be processed. **(FREE SELF)**
-- [Sidekiq routing rules](extra_sidekiq_routing.md): Configure the routing rules to route a job from a worker to a desirable queue. **(FREE SELF)**
+- [Multiple Sidekiq processes](../sidekiq/extra_sidekiq_processes.md): Configure multiple Sidekiq processes to ensure certain queues always have dedicated workers, no matter the number of jobs that must be processed. **(FREE SELF)**
- [Puma](puma.md): Understand Puma and puma-worker-killer.
- Speed up SSH operations by
[Authorizing SSH users via a fast, indexed lookup to the GitLab database](fast_ssh_key_lookup.md), and/or
diff --git a/doc/administration/operations/puma.md b/doc/administration/operations/puma.md
index eb326c06e6a..af595cdf297 100644
--- a/doc/administration/operations/puma.md
+++ b/doc/administration/operations/puma.md
@@ -66,9 +66,9 @@ From this output:
- The formula that calculates the maximum memory value results in workers
being killed before they reach the `per_worker_max_memory_mb` value.
-- In GitLab 13.4 and earlier, the default values for the formula were 550MB for the primary
- and 850MB for each worker.
-- In GitLab 13.5 and later, the values are primary: 800MB, worker: 1024MB.
+- In GitLab 13.4 and earlier, the default values for the formula were 550 MB for the primary
+ and 850 MB for each worker.
+- In GitLab 13.5 and later, the values are primary: 800 MB, worker: 1024 MB.
- The threshold for workers to be killed is set at 98% of the limit:
```plaintext
@@ -110,11 +110,11 @@ To change the worker timeout to 600 seconds:
WARNING:
This is an experimental [Alpha feature](../../policy/alpha-beta-support.md#alpha-features) and subject to change without notice. The feature
-is not ready for production use. If you want to use this feature, we recommend testing
+is not ready for production use. If you want to use this feature, you should test
outside of production first. See the [known issues](#puma-single-mode-known-issues)
for additional details.
-In a memory-constrained environment with less than 4GB of RAM available, consider disabling Puma
+In a memory-constrained environment with less than 4 GB of RAM available, consider disabling Puma
[clustered mode](https://github.com/puma/puma#clustered-mode).
Set the number of `workers` to `0` to reduce memory usage by hundreds of MB:
diff --git a/doc/administration/operations/sidekiq_memory_killer.md b/doc/administration/operations/sidekiq_memory_killer.md
deleted file mode 100644
index b1977589fae..00000000000
--- a/doc/administration/operations/sidekiq_memory_killer.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../sidekiq/sidekiq_memory_killer.md'
-remove_date: '2022-11-11'
----
-
-This document was moved to [another location](../sidekiq/sidekiq_memory_killer.md).
-
-<!-- This redirect file can be deleted after <2022-11-11>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/administration/package_information/index.md b/doc/administration/package_information/index.md
index 4758b453135..bfa751a051f 100644
--- a/doc/administration/package_information/index.md
+++ b/doc/administration/package_information/index.md
@@ -16,9 +16,9 @@ The released package versions are in the format `MAJOR.MINOR.PATCH-EDITION.OMNIB
| Component | Meaning | Example |
|---------------------|---------|---------|
-| `MAJOR.MINOR.PATCH` | The GitLab version this corresponds to. | 13.3.0 |
-| `EDITION` | The edition of GitLab this corresponds to. | ee |
-| `OMNIBUS_RELEASE` | The Omnibus GitLab release. Usually, this is 0. This is incremented if we need to build a new package without changing the GitLab version. | 0 |
+| `MAJOR.MINOR.PATCH` | The GitLab version this corresponds to. | `13.3.0` |
+| `EDITION` | The edition of GitLab this corresponds to. | `ee` |
+| `OMNIBUS_RELEASE` | The Omnibus GitLab release. Usually, this is 0. This is incremented if we need to build a new package without changing the GitLab version. | `0` |
## Licenses
diff --git a/doc/administration/package_information/licensing.md b/doc/administration/package_information/licensing.md
index 0e46289a308..ece1597c521 100644
--- a/doc/administration/package_information/licensing.md
+++ b/doc/administration/package_information/licensing.md
@@ -25,8 +25,7 @@ Starting with version 9.2, the Omnibus GitLab package ships a
`dependency_licenses.json` file containing version and license information of
all bundled software, including software libraries, Ruby gems that the rails
application uses, and JavaScript libraries that is required for the frontend
-components. This file, being in JSON format, is easily machine parseable and
-can be used for automated checks or validations. The file may be found at
+components. Because it's in JSON format, GitLab can parse this file easily and use it for automated checks or validations. The file may be found at
`/opt/gitlab/dependency_licenses.json`.
Starting with version 11.3, we have also made the license information available
diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md
index 2623f2afd8d..d76c728fb78 100644
--- a/doc/administration/packages/container_registry.md
+++ b/doc/administration/packages/container_registry.md
@@ -343,6 +343,9 @@ these deleted temporary upload artifacts are kept as non-current versions, there
storage bucket size. To ensure that non-current versions are deleted after a given amount of time,
you should configure an object lifecycle policy with your storage provider.
+WARNING:
+Do not directly modify the files or objects stored by the container registry. Anything other than the registry writing or deleting these entries can lead to instance-wide data consistency and instability issues from which recovery may not be possible.
+
You can configure the Container Registry to use various storage backends by
configuring a storage driver. By default the GitLab Container Registry
is configured to use the [file system driver](#use-file-system)
@@ -585,6 +588,38 @@ you can pull from the Container Registry, but you cannot push.
1. Configure your registry to [use the S3 bucket for storage](#use-object-storage).
1. For the changes to take effect, set the Registry back to `read-write` mode and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
+#### Moving to Azure Object Storage
+
+When moving from an existing file system or another object storage provider to Azure Object Storage, you must configure the registry to use the standard root directory.
+This configuration is done by setting [`trimlegacyrootprefix: true]`](https://gitlab.com/gitlab-org/container-registry/-/blob/a3f64464c3ec1c5a599c0a2daa99ebcbc0100b9a/docs-gitlab/README.md#azure-storage-driver) in the Azure storage driver section of the registry configuration.
+Without this configuration, the Azure storage driver uses `//` instead of `/` as the first section of the root path, rendering the migrated images inaccessible.
+
+**Omnibus GitLab installations**
+
+```ruby
+registry['storage'] = {
+ 'azure' => {
+ 'accountname' => 'accountname',
+ 'accesskey' => 'base64encodedaccountkey',
+ 'container' => 'containername',
+ 'rootdirectory' => '/azure/virtual/container',
+ 'trimlegacyrootprefix' => 'true'
+ }
+}
+```
+
+**Installations from source**
+
+```yaml
+storage:
+ azure:
+ accountname: accountname
+ accountkey: base64encodedaccountkey
+ container: containername
+ rootdirectory: /azure/virtual/container
+ trimlegacyrootprefix: true
+```
+
### Disable redirect for storage driver
By default, users accessing a registry configured with a remote backend are redirected to the default backend for the storage driver. For example, registries can be configured using the `s3` storage driver, which redirects requests to a remote S3 bucket to alleviate load on the GitLab server.
@@ -882,7 +917,7 @@ WARNING:
If you're using a distributed architecture and Sidekiq is running on a different node, the cleanup
policies don't work. To fix this, you must configure the `gitlab.rb` file on the Sidekiq nodes to
point to the correct registry URL and copy the `registry.key` file to each Sidekiq node. For more
-information, see the [Sidekiq configuration](../sidekiq.md)
+information, see the [Sidekiq configuration](../sidekiq/index.md)
page.
To reduce the amount of [Container Registry disk space used by a given project](#registry-disk-space-usage-by-project),
@@ -1091,6 +1126,9 @@ To enable the read-only mode:
1. Next, trigger one of the garbage collect commands:
+ WARNING:
+ You must use `/opt/gitlab/embedded/bin/registry` to recycle unused tags. If you use `gitlab-ctl registry-garbage-collect`, you **will bring the container registry down**.
+
```shell
# Recycling unused tags
sudo /opt/gitlab/embedded/bin/registry garbage-collect /var/opt/gitlab/registry/config.yml
@@ -1348,7 +1386,7 @@ level=error msg="response completed with error" err.code=unknown err.detail="une
```
To resolve the error specify a `chunksize` value in the Registry configuration.
-Start with a value between `25000000` (25MB) and `50000000` (50MB).
+Start with a value between `25000000` (25 MB) and `50000000` (50 MB).
**For Omnibus installations**
diff --git a/doc/administration/packages/dependency_proxy.md b/doc/administration/packages/dependency_proxy.md
index f6eb6f85274..78efd75bbe3 100644
--- a/doc/administration/packages/dependency_proxy.md
+++ b/doc/administration/packages/dependency_proxy.md
@@ -121,15 +121,12 @@ To change the local storage path:
### Using object storage
Instead of relying on the local storage, you can use an object storage to
-store the blobs of the Dependency Proxy.
+store the blobs of the Dependency Proxy. In GitLab 13.2 and later, you should use the
+[consolidated object storage settings](../object_storage.md#consolidated-object-storage-configuration).
+This section describes the earlier configuration format. [Migration steps still apply](#migrate-local-dependency-proxy-blobs-and-manifests-to-object-storage).
[Read more about using object storage with GitLab](../object_storage.md).
-NOTE:
-In GitLab 13.2 and later, we recommend using the
-[consolidated object storage settings](../object_storage.md#consolidated-object-storage-configuration).
-This section describes the earlier configuration format.
-
**Omnibus GitLab installations**
1. Edit `/etc/gitlab/gitlab.rb` and add the following lines (uncomment where
diff --git a/doc/administration/packages/index.md b/doc/administration/packages/index.md
index 74d835eb744..6e53d77109f 100644
--- a/doc/administration/packages/index.md
+++ b/doc/administration/packages/index.md
@@ -6,11 +6,13 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# GitLab Package Registry administration **(FREE SELF)**
-GitLab Packages allows organizations to use GitLab as a private repository
-for a variety of common package managers. Users are able to build and publish
-packages, which can be easily consumed as a dependency in downstream projects.
+To use GitLab as a private repository for a variety of common package managers, use the Package Registry.
+You can build and publish
+packages, which can be consumed as dependencies in downstream projects.
-The Packages feature allows GitLab to act as a repository and supports the following formats:
+## Supported formats
+
+The Package Registry supports the following formats:
| Package type | GitLab version |
|-------------------------------------------------------------------|----------------|
@@ -49,204 +51,155 @@ guides you through the process.
<!-- vale gitlab.Spelling = YES -->
-## Enabling the Packages feature
+## Rate limits
-NOTE:
-After the Packages feature is enabled, the repositories are available
-for all new projects by default. To enable it for existing projects, users
-explicitly do so in the project's settings.
+When downloading packages as dependencies in downstream projects, many requests are made through the
+Packages API. You may therefore reach enforced user and IP rate limits. To address this issue, you
+can define specific rate limits for the Packages API. For more details, see [Package Registry Rate Limits](../../user/admin_area/settings/package_registry_rate_limits.md).
-To enable the Packages feature:
+## Change the storage path
-**Omnibus GitLab installations**
+By default, the packages are stored locally, but you can change the default
+local location or even use object storage.
-1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
+### Change the local storage path
- ```ruby
- gitlab_rails['packages_enabled'] = true
- ```
+By default, the packages are stored in a local path, relative to the GitLab
+installation:
-1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+- Linux package (Omnibus): `/var/opt/gitlab/gitlab-rails/shared/packages/`
+- Self-compiled (source): `/home/git/gitlab/shared/packages/`
-**Installations from source**
+To change the local storage path:
-1. After the installation is complete, you configure the `packages`
- section in `config/gitlab.yml`. Set to `true` to enable it:
+::Tabs
- ```yaml
- packages:
- enabled: true
+:::TabTitle Linux package (Omnibus)
+
+1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
+
+ ```ruby
+ gitlab_rails['packages_storage_path'] = "/mnt/packages"
```
-1. [Restart GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+1. Save the file and reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
-**Helm Chart installations**
+:::TabTitle Self-compiled (source)
-1. After the installation is complete, you configure the `packages`
- section in `global.appConfig.packages`. Set to `true` to enable it:
+1. Edit `/home/git/gitlab/config/gitlab.yml`:
```yaml
- packages:
- enabled: true
+ production: &base
+ packages:
+ enabled: true
+ storage_path: /mnt/packages
```
-1. [Restart GitLab](../restart_gitlab.md#helm-chart-installations) for the changes to take effect.
+1. Save the file and restart GitLab:
-## Rate limits
+ ```shell
+ # For systems running systemd
+ sudo systemctl restart gitlab.target
-When downloading packages as dependencies in downstream projects, many requests are made through the
-Packages API. You may therefore reach enforced user and IP rate limits. To address this issue, you
-can define specific rate limits for the Packages API. For more details, see [Package Registry Rate Limits](../../user/admin_area/settings/package_registry_rate_limits.md).
+ # For systems running SysV init
+ sudo service gitlab restart
+ ```
-## Changing the storage path
+::EndTabs
-By default, the packages are stored locally, but you can change the default
-local location or even use object storage.
+Docker and Kubernetes do not use local storage.
-### Changing the local storage path
+- For the Helm chart (Kubernetes): Use object storage instead.
+- For Docker: The `/var/opt/gitlab/` directory is already
+ mounted in a directory on the host. There's no need to change the local
+ storage path inside the container.
-The packages for Omnibus GitLab installations are stored under
-`/var/opt/gitlab/gitlab-rails/shared/packages/` and for source
-installations under `shared/packages/` (relative to the Git home directory).
-To change the local storage path:
+### Use object storage
-**Omnibus GitLab installations**
+Instead of relying on the local storage, you can use an object storage to store
+packages.
-1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
+For more information, see how to use the
+[consolidated object storage settings](../object_storage.md#consolidated-object-storage-configuration).
- ```ruby
- gitlab_rails['packages_storage_path'] = "/mnt/packages"
- ```
+### Migrate local packages to object storage
+
+After [configuring the object storage](#use-object-storage), use the following task to
+migrate existing packages from the local storage to the remote storage.
+The processing is done in a background worker and requires **no downtime**.
-1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure)
- for the changes to take effect.
+1. Migrate the packages.
-**Installations from source**
+ ::Tabs
-1. Edit the `packages` section in `config/gitlab.yml`:
+ :::TabTitle Linux package (Omnibus)
- ```yaml
- packages:
- enabled: true
- storage_path: shared/packages
+ ```shell
+ sudo gitlab-rake "gitlab:packages:migrate"
```
-1. Save the file and [restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect.
-
-### Using object storage
+ :::TabTitle Self-compiled (source)
-Instead of relying on the local storage, you can use an object storage to
-store packages.
+ ```shell
+ RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:packages:migrate
+ ```
-[Read more about using object storage with GitLab](../object_storage.md).
+ ::EndTabs
-NOTE:
-We recommend using the [consolidated object storage settings](../object_storage.md#consolidated-object-storage-configuration). The following instructions apply to the original configuration format.
+1. Track the progress and verify that all packages migrated successfully using
+ the PostgreSQL console.
-**Omnibus GitLab installations**
+ ::Tabs
-1. Edit `/etc/gitlab/gitlab.rb` and add the following lines (uncomment where
- necessary):
+ :::TabTitle Linux package (Omnibus) 14.1 and earlier
- ```ruby
- gitlab_rails['packages_enabled'] = true
- gitlab_rails['packages_object_store_enabled'] = true
- gitlab_rails['packages_object_store_remote_directory'] = "packages" # The bucket name.
- gitlab_rails['packages_object_store_proxy_download'] = false # Passthrough all downloads via GitLab instead of using Redirects to Object Storage.
- gitlab_rails['packages_object_store_connection'] = {
- ##
- ## If the provider is AWS S3, uncomment the following
- ##
- #'provider' => 'AWS',
- #'region' => 'eu-west-1',
- #'aws_access_key_id' => 'AWS_ACCESS_KEY_ID',
- #'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY',
- ## If an IAM profile is being used with AWS, omit the aws_access_key_id and aws_secret_access_key and uncomment
- #'use_iam_profile' => true,
- ##
- ## If the provider is other than AWS (an S3-compatible one), uncomment the following
- ##
- #'host' => 's3.amazonaws.com',
- #'aws_signature_version' => 4 # For creation of signed URLs. Set to 2 if provider does not support v4.
- #'endpoint' => 'https://s3.amazonaws.com' # Useful for S3-compliant services such as DigitalOcean Spaces.
- #'path_style' => false # If true, use 'host/bucket_name/object' instead of 'bucket_name.host/object'.
- }
+ ```shell
+ sudo gitlab-rails dbconsole
```
-1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure)
- for the changes to take effect.
-
-**Installations from source**
+ :::TabTitle Linux package (Omnibus) 14.2 and later
-1. Edit the `packages` section in `config/gitlab.yml` (uncomment where necessary):
-
- ```yaml
- packages:
- enabled: true
- ##
- ## The location where build packages are stored (default: shared/packages).
- ##
- # storage_path: shared/packages
- object_store:
- enabled: false
- remote_directory: packages # The bucket name.
- # proxy_download: false # Passthrough all downloads via GitLab instead of using Redirects to Object Storage.
- connection:
- ##
- ## If the provider is AWS S3, use the following:
- ##
- provider: AWS
- region: us-east-1
- aws_access_key_id: AWS_ACCESS_KEY_ID
- aws_secret_access_key: AWS_SECRET_ACCESS_KEY
- ##
- ## If the provider is other than AWS (an S3-compatible one), comment out the previous 4 lines and use the following instead:
- ##
- # host: 's3.amazonaws.com' # default: s3.amazonaws.com.
- # aws_signature_version: 4 # For creation of signed URLs. Set to 2 if provider does not support v4.
- # endpoint: 'https://s3.amazonaws.com' # Useful for S3-compliant services such as DigitalOcean Spaces.
- # path_style: false # If true, use 'host/bucket_name/object' instead of 'bucket_name.host/object'.
+ ```shell
+ sudo gitlab-rails dbconsole --database main
```
-1. Save the file and [restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect.
-
-### Migrating local packages to object storage
+ :::TabTitle Self-compiled (source)
-After [configuring the object storage](#using-object-storage), use the following task to
-migrate existing packages from the local storage to the remote storage.
-The processing is done in a background worker and requires **no downtime**.
+ ```shell
+ RAILS_ENV=production sudo -u git -H psql -d gitlabhq_production
+ ```
-For Omnibus GitLab:
+ ::EndTabs
-```shell
-sudo gitlab-rake "gitlab:packages:migrate"
-```
+1. Verify that all packages migrated to object storage with the following SQL
+ query. The number of `objectstg` should be the same as `total`:
-For installations from source:
+ ```shell
+ gitlabhq_production=# SELECT count(*) AS total, sum(case when file_store = '1' then 1 else 0 end) AS filesystem, sum(case when file_store = '2' then 1 else 0 end) AS objectstg FROM packages_package_files;
-```shell
-RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:packages:migrate
-```
+ total | filesystem | objectstg
+ ------+------------+-----------
+ 34 | 0 | 34
+ ```
-You can optionally track progress and verify that all packages migrated successfully using the
-[PostgreSQL console](https://docs.gitlab.com/omnibus/settings/database.html#connecting-to-the-bundled-postgresql-database):
+1. Finally, verify that there are no files on disk in the `packages` directory:
-- `sudo gitlab-rails dbconsole` for Omnibus GitLab 14.1 and earlier.
-- `sudo gitlab-rails dbconsole --database main` for Omnibus GitLab 14.2 and later.
-- `sudo -u git -H psql -d gitlabhq_production` for source-installed instances.
+ ::Tabs
-Verify `objectstg` below (where `file_store = '2'`) has count of all packages:
+ :::TabTitle Linux package (Omnibus)
-```shell
-gitlabhq_production=# SELECT count(*) AS total, sum(case when file_store = '1' then 1 else 0 end) AS filesystem, sum(case when file_store = '2' then 1 else 0 end) AS objectstg FROM packages_package_files;
+ ```shell
+ sudo find /var/opt/gitlab/gitlab-rails/shared/packages -type f | grep -v tmp | wc -l
+ ```
-total | filesystem | objectstg
-------+------------+-----------
- 34 | 0 | 34
-```
+ :::TabTitle Self-compiled (source)
-Verify that there are no files on disk in the `packages` folder:
+ ```shell
+ sudo -u git find /home/git/gitlab/shared/packages -type f | grep -v tmp | wc -l
+ ```
-```shell
-sudo find /var/opt/gitlab/gitlab-rails/shared/packages -type f | grep -v tmp | wc -l
-```
+ ::EndTabs
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index 3d31491a9d2..2a28df96ef4 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -247,20 +247,20 @@ control over how the Pages daemon runs and serves content in your environment.
| `enable` | Enable or disable GitLab Pages on the current system. |
| `external_http` | Configure Pages to bind to one or more secondary IP addresses, serving HTTP requests. Multiple addresses can be given as an array, along with exact ports, for example `['1.2.3.4', '1.2.3.5:8063']`. Sets value for `listen_http`. |
| `external_https` | Configure Pages to bind to one or more secondary IP addresses, serving HTTPS requests. Multiple addresses can be given as an array, along with exact ports, for example `['1.2.3.4', '1.2.3.5:8063']`. Sets value for `listen_https`. |
-| `server_shutdown_timeout` | GitLab Pages server shutdown timeout in seconds (default: 30s). |
-| `gitlab_client_http_timeout` | GitLab API HTTP client connection timeout in seconds (default: 10s). |
-| `gitlab_client_jwt_expiry` | JWT Token expiry time in seconds (default: 30s). |
-| `gitlab_cache_expiry` | The maximum time a domain's configuration is stored in the cache (default: 600s). |
-| `gitlab_cache_refresh` | The interval at which a domain's configuration is set to be due to refresh (default: 60s). |
-| `gitlab_cache_cleanup` | The interval at which expired items are removed from the cache (default: 60s). |
-| `gitlab_retrieval_timeout` | The maximum time to wait for a response from the GitLab API per request (default: 30s). |
-| `gitlab_retrieval_interval` | The interval to wait before retrying to resolve a domain's configuration via the GitLab API (default: 1s). |
+| `server_shutdown_timeout` | GitLab Pages server shutdown timeout in seconds (default: 30 s). |
+| `gitlab_client_http_timeout` | GitLab API HTTP client connection timeout in seconds (default: 10 s). |
+| `gitlab_client_jwt_expiry` | JWT Token expiry time in seconds (default: 30 s). |
+| `gitlab_cache_expiry` | The maximum time a domain's configuration is stored in the cache (default: 600 s). |
+| `gitlab_cache_refresh` | The interval at which a domain's configuration is set to be due to refresh (default: 60 s). |
+| `gitlab_cache_cleanup` | The interval at which expired items are removed from the cache (default: 60 s). |
+| `gitlab_retrieval_timeout` | The maximum time to wait for a response from the GitLab API per request (default: 30 s). |
+| `gitlab_retrieval_interval` | The interval to wait before retrying to resolve a domain's configuration via the GitLab API (default: 1 s). |
| `gitlab_retrieval_retries` | The maximum number of times to retry to resolve a domain's configuration via the API (default: 3). |
| `domain_config_source` | This parameter was removed in 14.0, on earlier versions it can be used to enable and test API domain configuration source |
| `gitlab_id` | The OAuth application public ID. Leave blank to automatically fill when Pages authenticates with GitLab. |
| `gitlab_secret` | The OAuth application secret. Leave blank to automatically fill when Pages authenticates with GitLab. |
| `auth_scope` | The OAuth application scope to use for authentication. Must match GitLab Pages OAuth application settings. Leave blank to use `api` scope by default. |
-| `auth_cookie_session_timeout` | Authentication cookie session timeout in seconds (default: 600s). A value of `0` means the cookie is deleted after the browser session ends. |
+| `auth_cookie_session_timeout` | Authentication cookie session timeout in seconds (default: 600 s). A value of `0` means the cookie is deleted after the browser session ends. |
| `gitlab_server` | Server to use for authentication when access control is enabled; defaults to GitLab `external_url`. |
| `headers` | Specify any additional http headers that should be sent to the client with each response. Multiple headers can be given as an array, header and value as one string, for example `['my-header: myvalue', 'my-other-header: my-other-value']` |
| `enable_disk` | Allows the GitLab Pages daemon to serve content from disk. Shall be disabled if shared disk storage isn't available. |
@@ -275,9 +275,9 @@ control over how the Pages daemon runs and serves content in your environment.
| `max_uri_length` | The maximum length of URIs accepted by GitLab Pages. Set to 0 for unlimited length. [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/659) in GitLab 14.5.
| `metrics_address` | The address to listen on for metrics requests. |
| `redirect_http` | Redirect pages from HTTP to HTTPS, true/false. |
-| `redirects_max_config_size` | The maximum size of the _redirects file, in bytes (default: 65536). |
-| `redirects_max_path_segments` | The maximum number of path segments allowed in _redirects rules URLs (default: 25). |
-| `redirects_max_rule_count` | The maximum number of rules allowed in _redirects (default: 1000). |
+| `redirects_max_config_size` | The maximum size of the `_redirects` file, in bytes (default: 65536). |
+| `redirects_max_path_segments` | The maximum number of path segments allowed in `_redirects` rules URLs (default: 25). |
+| `redirects_max_rule_count` | The maximum number of rules allowed in `_redirects` (default: 1000). |
| `sentry_dsn` | The address for sending Sentry crash reporting to. |
| `sentry_enabled` | Enable reporting and logging with Sentry, true/false. |
| `sentry_environment` | The environment for Sentry crash reporting. |
@@ -294,7 +294,7 @@ control over how the Pages daemon runs and serves content in your environment.
| `pages_path` | The directory on disk where pages are stored, defaults to `GITLAB-RAILS/shared/pages`. |
| **`pages_nginx[]`** | |
| `enable` | Include a virtual host `server{}` block for Pages inside NGINX. Needed for NGINX to proxy traffic back to the Pages daemon. Set to `false` if the Pages daemon should directly receive all requests, for example, when using [custom domains](index.md#custom-domains). |
-| `FF_ENABLE_PLACEHOLDERS` | Feature flag to enable/disable rewrites (disabled by default). Read the [redirects documentation](../../user/project/pages/redirects.md#feature-flag-for-rewrites) for more information. |
+| `FF_ENABLE_PLACEHOLDERS` | Feature flag for rewrites (enabled by default). See [Rewrites](../../user/project/pages/redirects.md#rewrites) for more information. |
| `use_legacy_storage` | Temporarily-introduced parameter allowing to use legacy domain configuration source and storage. [Removed in 14.3](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6166). |
| `rate_limit_source_ip` | Rate limit per source IP in number of requests per second. Set to `0` to disable this feature. |
| `rate_limit_source_ip_burst` | Rate limit per source IP maximum burst allowed per second. |
@@ -540,22 +540,22 @@ archive. You can modify the cache behavior by changing the following configurati
| Setting | Description |
| ------- | ----------- |
-| `zip_cache_expiration` | The cache expiration interval of ZIP archives. Must be greater than zero to avoid serving stale content. Default is 60s. |
-| `zip_cache_cleanup` | The interval at which archives are cleaned from memory if they have already expired. Default is 30s. |
-| `zip_cache_refresh` | The time interval in which an archive is extended in memory if accessed before `zip_cache_expiration`. This works together with `zip_cache_expiration` to determine if an archive is extended in memory. See the [example below](#zip-cache-refresh-example) for important details. Default is 30s. |
-| `zip_open_timeout` | The maximum time allowed to open a ZIP archive. Increase this time for big archives or slow network connections, as doing so may affect the latency of serving Pages. Default is 30s. |
-| `zip_http_client_timeout` | The maximum time for the ZIP HTTP client. Default is 30m. |
+| `zip_cache_expiration` | The cache expiration interval of ZIP archives. Must be greater than zero to avoid serving stale content. Default is 60 s. |
+| `zip_cache_cleanup` | The interval at which archives are cleaned from memory if they have already expired. Default is 30 s. |
+| `zip_cache_refresh` | The time interval in which an archive is extended in memory if accessed before `zip_cache_expiration`. This works together with `zip_cache_expiration` to determine if an archive is extended in memory. See the [example below](#zip-cache-refresh-example) for important details. Default is 30 s. |
+| `zip_open_timeout` | The maximum time allowed to open a ZIP archive. Increase this time for big archives or slow network connections, as doing so may affect the latency of serving Pages. Default is 30 s. |
+| `zip_http_client_timeout` | The maximum time for the ZIP HTTP client. Default is 30 m. |
#### ZIP cache refresh example
Archives are refreshed in the cache (extending the time they are held in memory) if they're accessed
before `zip_cache_expiration`, and the time left before expiring is less than or equal to
-`zip_cache_refresh`. For example, if `archive.zip` is accessed at time 0s, it expires in 60s (the
-default for `zip_cache_expiration`). In the example below, if the archive is opened again after 15s
-it is **not** refreshed because the time left for expiry (45s) is greater than `zip_cache_refresh`
-(default 30s). However, if the archive is accessed again after 45s (from the first time it was
+`zip_cache_refresh`. For example, if `archive.zip` is accessed at time 0 s, it expires in 60 s (the
+default for `zip_cache_expiration`). In the example below, if the archive is opened again after 15 s
+it is **not** refreshed because the time left for expiry (45 s) is greater than `zip_cache_refresh`
+(default 30 s). However, if the archive is accessed again after 45 s (from the first time it was
opened) it's refreshed. This extends the time the archive remains in memory from
-`45s + zip_cache_expiration (60s)`, for a total of 105s.
+`45s + zip_cache_expiration (60s)`, for a total of 105 s.
After an archive reaches `zip_cache_expiration`, it's marked as expired and removed on the next
`zip_cache_cleanup` interval.
@@ -941,6 +941,10 @@ If you want to stop using and disconnect the NFS server, you need to
#### S3-compatible connection settings
+In GitLab 13.2 and later, you should use the
+[consolidated object storage settings](../object_storage.md#consolidated-object-storage-configuration).
+This section describes the earlier configuration format.
+
See [the available connection settings for different providers](../object_storage.md#connection-settings).
In Omnibus installations:
diff --git a/doc/administration/postgresql/database_load_balancing.md b/doc/administration/postgresql/database_load_balancing.md
index 805c4b2023f..15129770888 100644
--- a/doc/administration/postgresql/database_load_balancing.md
+++ b/doc/administration/postgresql/database_load_balancing.md
@@ -76,18 +76,20 @@ Database Load Balancing can be configured in one of two ways:
### Hosts
-To configure a list of hosts, add the `gitlab_rails['db_load_balancing']` setting into the
-`gitlab.rb` file in the GitLab Rails / Sidekiq nodes for each environment you want to balance.
+To configure a list of hosts, perform these steps on all GitLab Rails (Sidekiq)
+nodes for each environment you want to balance:
-For example, on an environment that has PostgreSQL running on the hosts `host1.example.com`,
-`host2.example.com` and `host3.example.com` and reachable on the same port configured with
-`gitlab_rails['db_port']`:
+1. Edit the `/etc/gitlab/gitlab.rb` file.
+1. In `gitlab_rails['db_load_balancing']`, create an array of the read-only
+ replicas you want to balance. Do not add the primary host. For example, on
+ an environment with PostgreSQL running on the hosts `primary.example.com`,
+ `host1.example.com`, `host2.example.com`, and `host3.example.com`:
-1. On each GitLab Rails / Sidekiq node, edit `/etc/gitlab/gitlab.rb` and add the following line:
+ ```ruby
+ gitlab_rails['db_load_balancing'] = { 'hosts' => ['host1.example.com', 'host2.example.com', `host3.example.com`] }
+ ```
- ```ruby
- gitlab_rails['db_load_balancing'] = { 'hosts' => ['host1.example.com', 'host2.example.com', `host3.example.com`] }
- ```
+ These replicas must be reachable on the same port configured with `gitlab_rails['db_port']`.
1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
diff --git a/doc/administration/postgresql/multiple_databases.md b/doc/administration/postgresql/multiple_databases.md
new file mode 100644
index 00000000000..ffccbf861e7
--- /dev/null
+++ b/doc/administration/postgresql/multiple_databases.md
@@ -0,0 +1,141 @@
+---
+stage: Data Stores
+group: Pods
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Multiple Databases **(FREE SELF)**
+
+> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6168) in GitLab 15.7.
+
+WARNING:
+This feature is not ready for production use
+
+By default, GitLab uses a single application database, referred to as the `main` database.
+
+To scale GitLab, you can configure GitLab to use multiple application databases.
+
+Due to [known issues](#known-issues), configuring GitLab with multiple databases is in [**Alpha**](../../policy/alpha-beta-support.md#alpha-features).
+
+## Known issues
+
+- Migrating data from the `main` database to the `ci` database is not supported or documented yet.
+- Once data is migrated to the `ci` database, you cannot migrate it back.
+
+## Set up multiple databases
+
+Use the following content to set up multiple databases with a new GitLab installation.
+
+There is no documentation for existing GitLab installations yet.
+
+After you have set up multiple databases, GitLab uses a second application database for
+[CI/CD features](../../ci/index.md), referred to as the `ci` database. For
+example, GitLab reads and writes to the `ci_pipelines` table in the `ci`
+database.
+
+WARNING:
+You must stop GitLab before setting up multiple databases. This prevents
+split-brain situations, where `main` data is written to the `ci` database, and
+the other way around.
+
+### Installations from source
+
+1. [Back up GitLab](../../raketasks/backup_restore.md)
+ in case of unforeseen issues.
+
+1. Stop GitLab:
+
+ ```shell
+ sudo service gitlab stop
+ ```
+
+1. Open `config/database.yml`, and add a `ci:` section under
+ `production:`. See `config/database.yml.decomposed-postgresql` for possible
+ values for this new `ci:` section. Once modified, the `config/database.yml` should
+ look like:
+
+ ```yaml
+ production:
+ main:
+ # ...
+ ci:
+ adapter: postgresql
+ encoding: unicode
+ database: gitlabhq_production_ci
+ # ...
+ ```
+
+1. Save the `config/database.yml` file.
+
+1. Create the `gitlabhq_production_ci` database:
+
+ ```shell
+ sudo -u postgres psql -d template1 -c "CREATE DATABASE gitlabhq_production OWNER git;"
+ sudo -u git -H bundle exec rake db:schema:load:ci
+ ```
+
+1. Lock writes for `ci` tables in `main` database, and the other way around:
+
+ ```shell
+ sudo -u git -H bundle exec rake gitlab:db:lock_writes
+ ```
+
+1. Restart GitLab:
+
+ ```shell
+ sudo service gitlab restart
+ ```
+
+### Omnibus GitLab installations
+
+1. [Back up GitLab](../../raketasks/backup_restore.md)
+ in case of unforeseen issues.
+
+1. Stop GitLab:
+
+ ```shell
+ sudo gitlab-ctl stop
+ ```
+
+1. Edit `/etc/gitlab/gitlab.rb` and add the following lines:
+
+ ```ruby
+ gitlab_rails['databases']['ci']['enable'] = true
+ gitlab_rails['databases']['ci']['db_database'] = 'gitlabhq_production_ci'
+ ```
+
+1. Save the `/etc/gitlab/gitlab.rb` file.
+
+1. Reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+1. Optional. Reconfiguring GitLab should create the `gitlabhq_production_ci`. If it did not, manually create the `gitlabhq_production_ci`:
+
+ ```shell
+ sudo gitlab-ctl start postgresql
+ sudo -u gitlab-psql /opt/gitlab/embedded/bin/psql -h /var/opt/gitlab/postgresql -d template1 -c "CREATE DATABASE gitlabhq_production_ci OWNER gitlab;"
+ sudo gitlab-rake db:schema:load:ci
+
+1. Lock writes for `ci` tables in `main` database, and the other way around:
+
+ ```shell
+ sudo gitlab-ctl start postgresql
+ sudo gitlab-rake gitlab:db:lock_writes
+ ```
+
+1. Restart GitLab:
+
+ ```shell
+ sudo gitlab-ctl restart
+ ```
+
+## Further information
+
+For more information on multiple databases, see [issue 6168](https://gitlab.com/groups/gitlab-org/-/epics/6168).
+
+For more information on how multiple databases work in GitLab, see the [development guide for multiple databases](../../development/database/multiple_databases.md).
+
+Since 2022-07-02, GitLab.com has been running with two separate databases. For more information, see this [blog post](https://about.gitlab.com/blog/2022/06/02/splitting-database-into-main-and-ci/).
diff --git a/doc/administration/postgresql/pgbouncer.md b/doc/administration/postgresql/pgbouncer.md
index 91c689fadea..25c4c940b97 100644
--- a/doc/administration/postgresql/pgbouncer.md
+++ b/doc/administration/postgresql/pgbouncer.md
@@ -220,8 +220,8 @@ the database. Each of the listed services below use the following formula to def
- `puma` : `max_threads + headroom` (default `14`)
- `max_threads` is configured via: `gitlab['puma']['max_threads']` (default: `4`)
- `headroom` can be configured via `DB_POOL_HEADROOM` environment variable (default to `10`)
-- `sidekiq` : `max_concurrency + 1 + headroom` (default: `61`)
- - `max_concurrency` is configured via: `sidekiq['max_concurrency']` (default: `50`)
+- `sidekiq` : `max_concurrency + 1 + headroom` (default: `31`)
+ - `max_concurrency` is configured via: `sidekiq['max_concurrency']` (default: `20`)
- `headroom` can be configured via `DB_POOL_HEADROOM` environment variable (default to `10`)
- `geo-logcursor`: `1+headroom` (default: `11`)
- `headroom` can be configured via `DB_POOL_HEADROOM` environment variable (default to `10`)
diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md
index ee90b120d05..ececa12f3ad 100644
--- a/doc/administration/postgresql/replication_and_failover.md
+++ b/doc/administration/postgresql/replication_and_failover.md
@@ -608,7 +608,7 @@ Here is a list and description of each machine and the assigned IP:
All passwords are set to `toomanysecrets`. Do not use this password or derived hashes and the `external_url` for GitLab is `http://gitlab.example.com`.
-After the initial configuration, if a failover occurs, the PostgresSQL leader node changes to one of the available secondaries until it is failed back.
+After the initial configuration, if a failover occurs, the PostgreSQL leader node changes to one of the available secondaries until it is failed back.
#### Example recommended setup for Consul servers
@@ -1081,6 +1081,190 @@ Reverting the PostgreSQL upgrade with `gitlab-ctl revert-pg-upgrade` has the sam
`gitlab-ctl pg-upgrade`. You should follow the same procedure by first stopping the replicas,
then reverting the leader, and finally reverting the replicas.
+### Near zero downtime upgrade of PostgreSQL in a Patroni cluster (Experimental)
+
+Patroni enables you to run a major PostgreSQL upgrade without shutting down the cluster. However, this
+requires additional resources to host the new Patroni nodes with the upgraded PostgreSQL. In practice, with this
+procedure, you are:
+
+- Creating a new Patroni cluster with a new version of PostgreSQL.
+- Migrating the data from the existing cluster.
+
+This procedure is non-invasive, and does not impact your existing cluster before switching it off.
+However, it can be both time- and resource-consuming. Consider their trade-offs with availability.
+
+The steps, in order:
+
+1. [Provision resources for the new cluster](#provision-resources-for-the-new-cluster).
+1. [Preflight check](#preflight-check).
+1. [Configure the leader of the new cluster](#configure-the-leader-of-the-new-cluster).
+1. [Start publisher on the existing leader](#start-publisher-on-the-existing-leader).
+1. [Copy the data from the existing cluster](#copy-the-data-from-the-existing-cluster).
+1. [Replicate data from the existing cluster](#replicate-data-from-the-existing-cluster).
+1. [Grow the new cluster](#grow-the-new-cluster).
+1. [Switch the application to use the new cluster](#switch-the-application-to-use-the-new-cluster).
+1. [Clean up](#clean-up).
+
+#### Provision resources for the new cluster
+
+You need a new set of resources for Patroni nodes. The new Patroni cluster does not require exactly the same number
+of nodes as the existing cluster. You may choose a different number of nodes based on your requirements. The new
+cluster uses the existing Consul cluster (with a different `patroni['scope']`) and PgBouncer nodes.
+
+Make sure that at least the leader node of the existing cluster is accessible from the nodes of the new
+cluster.
+
+#### Preflight check
+
+We rely on PostgreSQL [logical replication](https://www.postgresql.org/docs/current/logical-replication.html)
+to support near-zero downtime upgrades of Patroni clusters. The of
+[logical replication requirements](https://www.postgresql.org/docs/current/logical-replication-restrictions.html)
+must be met. In particular, `wal_level` must be `logical`. To check the `wal_level`,
+run the following command with `gitlab-psql` on any node of the existing cluster:
+
+```sql
+SHOW wal_level;
+```
+
+By default, Patroni sets `wal_level` to `replica`. You must increase it to `logical`.
+Changing `wal_level` requires restarting PostgreSQL, so this step leads to a short
+downtime (hence near-zero downtime). To do this on the Patroni **leader** node:
+
+1. Edit `gitlab.rb` by setting:
+
+ ```ruby
+ patroni['postgresql']['wal_level'] = 'logical'
+ ```
+
+1. Run `gitlab-ctl reconfigure`. This writes the configuration but does not restart PostgreSQL service.
+1. Run `gitlab-ctl patroni restart` to restart PostgreSQL and apply the new `wal_level` without triggering
+ failover. For the duration of restart cycle, the cluster leader is unavailable.
+1. Verify the change by running `SHOW wal_level` with `gitlab-psql`.
+
+#### Configure the leader of the new cluster
+
+Configure the first node of the new cluster. It becomes the leader of the new cluster.
+You can use the configuration of the existing cluster, if it is compatible with the new
+PostgreSQL version. Refer to the documentation on [configuring Patroni clusters](#configuring-patroni-cluster).
+
+In addition to the common configuration, you must apply the following in `gitlab.rb` to:
+
+1. Make sure that the new Patroni cluster uses a different scope. The scope is used to namespace the Patroni settings
+ in Consul, making it possible to use the same Consul cluster for the existing and the new clusters.
+
+ ```ruby
+ patroni['scope'] = 'postgresql_new-ha'
+ ```
+
+1. Make sure that Consul agents don't mix PostgreSQL services offered by the existing and the new Patroni
+ clusters. For this purpose, you must use an internal attribute that is currently undocumented:
+
+ ```ruby
+ consul['internal']['postgresql_service_name'] = 'postgresql_new'
+ ```
+
+#### Start publisher on the existing leader
+
+On the existing leader, run this SQL statement with `gitlab-psql` to start a logical replication publisher:
+
+```sql
+CREATE PUBLICATION patroni_upgrade FOR ALL TABLES;
+```
+
+#### Copy the data from the existing cluster
+
+To dump the current database from the existing cluster, run these commands on the
+**leader** of the new cluster:
+
+1. Optional. Copy global database objects:
+
+ ```shell
+ pg_dumpall -h ${EXISTING_CLUSTER_LEADER} -U gitlab-psql -g | gitlab-psql
+ ```
+
+ You can ignore the errors about existing database objects, such as roles. They are
+ created when the node is configured for the first time.
+
+1. Copy the current database:
+
+ ```shell
+ pg_dump -h ${EXISTING_CLUSTER_LEADER} -U gitlab-psql -d gitlabhq_production -s | gitlab-psql
+ ```
+
+ Depending on the size of your database, this command may take a while to complete.
+
+The `pg_dump` and `pg_dumpall` commands are in `/opt/gitlab/embedded/bin`. In these commands,
+`EXISTING_CLUSTER_LEADER` is the host address of the leader node of the existing cluster.
+
+NOTE:
+The `gitlab-psql` user must be able to authenticate the existing leader from the new leader node.
+
+#### Replicate data from the existing cluster
+
+After taking the initial data dump, you must keep the new leader in sync with the
+latest changes of your existing cluster. On the new leader, run this SQL statement
+with `gitlab-psql` to subscribe to publication of the existing leader:
+
+```sql
+CREATE SUBSCRIPTION patroni_upgrade
+ CONNECTION 'host=EXISTING_CLUSTER_LEADER dbname=gitlabhq_production user=gitlab-psql'
+ PUBLICATION patroni_upgrade;
+```
+
+In this statement, `EXISTING_CLUSTER_LEADER` is the host address of the leader node
+of the existing cluster. You can also use
+[other parameters](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS)
+to change the connection string. For example, you can pass the authentication password.
+
+To check the status of replication, run these queries:
+
+- `SELECT * FROM pg_replication_slots WHERE slot_name = 'patroni_upgrade'` on the existing leader (the publisher).
+- `SELECT * FROM pg_stat_subscription` on the new leader (the subscriber).
+
+#### Grow the new cluster
+
+Configure other nodes of the new cluster in the way you
+[configured the leader](#configure-the-leader-of-the-new-cluster).
+Make sure that you use the same `patroni['scope']` and
+`consul['internal']['postgresql_service_name']`.
+
+What happens here:
+
+- The application still uses the existing leader as its database backend.
+- The logical replication ensures that the new leader keeps in sync.
+- When other nodes are added to the new cluster, Patroni handles
+ the replication to the these nodes.
+
+It is a good idea to wait until the replica nodes of the new cluster are initialized and caught up on the replication
+lag.
+
+#### Switch the application to use the new cluster
+
+Up to this point, you can stop the upgrade procedure without losing data on the
+existing cluster. When you switch the database backend of the application and point
+it to the new cluster, the old cluster does not receive new updates. It falls behind
+the new cluster. After this point, any recovery must be done from the nodes of the new cluster.
+
+To do the switch on **all** PgBouncer nodes:
+
+1. Edit `gitlab.rb` by setting:
+
+ ```ruby
+ consul['watchers'] = %w(postgresql_new)
+ consul['internal']['postgresql_service_name'] = 'postgresql_new'
+ ```
+
+1. Run `gitlab-ctl reconfigure`.
+1. You must also run `rm /var/opt/gitlab/consul/config.d/watcher_postgresql.json`.
+ This is a [known issue](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/7293).
+
+#### Clean up
+
+After completing these steps, then you can clean up the resources of the old Patroni cluster.
+They are no longer needed. However, before removing the resources, remove the
+logical replication subscription on the new leader by running `DROP SUBSCRIPTION patroni_upgrade`
+with `gitlab-psql`.
+
## Troubleshooting
### Consul and PostgreSQL changes not taking effect
diff --git a/doc/administration/raketasks/geo.md b/doc/administration/raketasks/geo.md
index a4d027101dd..03b09e00f1c 100644
--- a/doc/administration/raketasks/geo.md
+++ b/doc/administration/raketasks/geo.md
@@ -4,7 +4,7 @@ group: Geo
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Geo Rake Tasks **(PREMIUM SELF)**
+# Geo Rake tasks **(PREMIUM SELF)**
The following Rake tasks are for [Geo installations](../geo/index.md).
See also [troubleshooting Geo](../geo/replication/troubleshooting.md) for additional Geo Rake tasks.
diff --git a/doc/administration/raketasks/github_import.md b/doc/administration/raketasks/github_import.md
index 224ed63d3e6..61f6137e1ed 100644
--- a/doc/administration/raketasks/github_import.md
+++ b/doc/administration/raketasks/github_import.md
@@ -4,7 +4,7 @@ group: Distribution
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# GitHub import **(FREE SELF)**
+# GitHub import Rake task **(FREE SELF)**
To retrieve and import GitHub repositories, you need a [GitHub personal access token](https://github.com/settings/tokens).
A username should be passed as the second argument to the Rake task,
diff --git a/doc/administration/raketasks/maintenance.md b/doc/administration/raketasks/maintenance.md
index 293efb1b7ae..ba095b33bf5 100644
--- a/doc/administration/raketasks/maintenance.md
+++ b/doc/administration/raketasks/maintenance.md
@@ -301,7 +301,7 @@ sudo gitlab-rake gitlab:exclusive_lease:clear[project_housekeeping:4]
## Display status of database migrations
-See the [upgrade documentation](../../update/index.md#checking-for-background-migrations-before-upgrading)
+See the [background migrations documentation](../../update/background_migrations.md)
for how to check that migrations are complete when upgrading GitLab.
To check the status of specific migrations, you can use the following Rake task:
diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md
index a2463c6ff88..88913eb1f7f 100644
--- a/doc/administration/reference_architectures/10k_users.md
+++ b/doc/administration/reference_architectures/10k_users.md
@@ -35,13 +35,13 @@ full list of reference architectures, see
| GitLab Rails | 3 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` |
| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
| Object storage<sup>4</sup> | - | - | - | - |
-| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix -->
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
- [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work.
- - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB clusters](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB cluster](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - Note that [Amazon RDS Multi-AZ DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZSingleStandby.html) is a separate product and is supported.
- [Amazon Aurora](https://aws.amazon.com/rds/aurora/) is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440).
- Consul is primarily used for Omnibus PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However, Consul is also used optionally by Prometheus for Omnibus auto host discovery.
2. Can be optionally run on reputable third-party external PaaS Redis solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
@@ -189,7 +189,7 @@ CI pipelines alike.
As such, large repositories come with notable cost and typically will require more resources to handle,
significantly so in some cases. It's therefore **strongly** recommended then to review large repositories
-to ensure they maintain good repo health and reduce their size wherever possible.
+to ensure they maintain good health and reduce their size wherever possible.
NOTE:
If best practices aren't followed and large repositories are present on the environment,
@@ -227,9 +227,6 @@ To set up GitLab and its components to accommodate up to 10,000 users:
environment.
1. [Configure the object storage](#configure-the-object-storage)
used for shared data objects.
-1. [Configure NFS](#configure-nfs-optional) (optional, and not recommended)
- to have shared disk storage service as an alternative to Gitaly or object
- storage.
1. [Configure Advanced Search](#configure-advanced-search) (optional) for faster,
more advanced code search across your entire GitLab instance.
@@ -1275,7 +1272,7 @@ in the second step, do not supply the `EXTERNAL_URL` value.
# PostgreSQL configuration
postgresql['listen_address'] = '0.0.0.0'
- postgresql['max_connections'] = 200
+ postgresql['max_connections'] = 500
# Prevent database migrations from running on upgrade automatically
gitlab_rails['auto_migrate'] = false
@@ -1759,9 +1756,8 @@ To configure Praefect with TLS:
Sidekiq requires connection to the [Redis](#configure-redis),
[PostgreSQL](#configure-postgresql) and [Gitaly](#configure-gitaly) instances.
-Since it's recommended to use [Object storage](#configure-the-object-storage)
-over [NFS](#configure-nfs-optional) for data objects, the following examples
-include the Object storage configuration.
+Because you must use [Object storage](#configure-the-object-storage) instead of NFS for data objects, the following
+examples include the Object storage configuration.
- `10.6.0.101`: Sidekiq 1
- `10.6.0.102`: Sidekiq 2
@@ -1859,8 +1855,8 @@ Updates to example must be made at:
# Set number of Sidekiq queue processes to the same number as available CPUs
sidekiq['queue_groups'] = ['*'] * 4
- # Set number of Sidekiq threads per queue process to the recommend number of 10
- sidekiq['max_concurrency'] = 10
+ # Set number of Sidekiq threads per queue process to the recommend number of 20
+ sidekiq['max_concurrency'] = 20
# Monitoring
consul['enable'] = true
@@ -1918,7 +1914,7 @@ Updates to example must be made at:
NOTE:
If you find that the environment's Sidekiq job processing is slow with long queues,
more nodes can be added as required. You can also tune your Sidekiq nodes to
-run [multiple Sidekiq processes](../operations/extra_sidekiq_processes.md).
+run [multiple Sidekiq processes](../sidekiq/extra_sidekiq_processes.md).
<div align="right">
<a type="button" class="btn btn-default" href="#setup-components">
@@ -1929,9 +1925,8 @@ run [multiple Sidekiq processes](../operations/extra_sidekiq_processes.md).
## Configure GitLab Rails
This section describes how to configure the GitLab application (Rails) component.
-Since it's recommended to use [Object storage](#configure-the-object-storage)
-over [NFS](#configure-nfs-optional) for data objects, the following examples
-include the Object storage configuration.
+Because you must use [Object storage](#configure-the-object-storage) instead of NFS for data objects, the following
+examples include the Object storage configuration.
The following IPs will be used as an example:
@@ -2070,7 +2065,6 @@ On each node perform the following:
1. Copy the `/etc/gitlab/gitlab-secrets.json` file from the first Omnibus node you configured and add or replace
the file of the same name on this server. If this is the first Omnibus node you are configuring then you can skip this step.
-
1. To ensure database migrations are only run during reconfigure and not automatically on upgrade, run:
```shell
@@ -2081,9 +2075,7 @@ On each node perform the following:
[GitLab Rails post-configuration](#gitlab-rails-post-configuration) section.
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-
-1. [Enable incremental logging](#enable-incremental-logging), unless you are using [NFS](#configure-nfs-optional).
-
+1. [Enable incremental logging](#enable-incremental-logging).
1. Confirm the node can connect to Gitaly:
```shell
@@ -2209,9 +2201,6 @@ To configure the Monitoring node:
## Configure the object storage
GitLab supports using an object storage service for holding numerous types of data.
-It's recommended over [NFS](#configure-nfs-optional) and in general it's better
-in larger setups as object storage is typically much more performant, reliable,
-and scalable.
GitLab has been tested on a number of object storage providers:
@@ -2235,7 +2224,7 @@ NOTE:
When using the [storage-specific form](../object_storage.md#storage-specific-configuration)
in GitLab 14.x and earlier, you should enable [direct upload mode](../../development/uploads/index.md#direct-upload).
The previous [background upload](../../development/uploads/index.md#direct-upload) mode,
-which was deprecated in 14.9, requires shared storage such as [NFS](#configure-nfs-optional).
+which was deprecated in 14.9, requires shared storage such as NFS.
Using separate buckets for each data type is the recommended approach for GitLab.
This ensures there are no collisions across the various types of data GitLab stores.
@@ -2254,22 +2243,6 @@ GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily
While sharing the job logs through NFS is supported, it's recommended to avoid the need to use NFS by enabling [incremental logging](../job_logs.md#incremental-logging-architecture) (required when no NFS node has been deployed). Incremental logging uses Redis instead of disk space for temporary caching of job logs.
-## Configure NFS (optional)
-
-[Object storage](#configure-the-object-storage), along with [Gitaly](#configure-gitaly)
-are recommended over NFS wherever possible for improved performance.
-
-See how to [configure NFS](../nfs.md).
-
-WARNING:
-Engineering support for NFS for Git repositories is deprecated, and [technical support is scheduled to be unavailable](../nfs.md#gitaly-and-nfs-deprecation)
-after the release of GitLab 15.6. No further enhancements are planned for this feature.
-
-Read:
-
-- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
-- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
-
## Configure Advanced Search
You can leverage Elasticsearch and [enable Advanced Search](../../integration/advanced_search/elasticsearch.md)
@@ -2319,12 +2292,10 @@ Refer to [epic 6127](https://gitlab.com/groups/gitlab-org/-/epics/6127) for more
The following tables and diagram detail the hybrid environment using the same formats
as the normal environment above.
-First are the components that run in Kubernetes. The recommendation at this time is to
-use Google Cloud's Kubernetes Engine (GKE) or AWS Elastic Kubernetes Service (EKS) and associated machine types, but the memory
-and CPU requirements should translate to most other providers. We hope to update this in the
-future with further specific cloud provider details.
+First are the components that run in Kubernetes. These run across several node groups, although you can change
+the overall makeup as desired as long as the minimum CPU and Memory requirements are observed.
-| Service | Nodes | Configuration | GCP | AWS | Min Allocatable CPUs and Memory |
+| Service Node Group | Nodes | Configuration | GCP | AWS | Min Allocatable CPUs and Memory |
|---------------------|-------|-------------------------|-----------------|--------------|---------------------------------|
| Webservice | 4 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` | 127.5 vCPU, 118 GB memory |
| Sidekiq | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | 15.5 vCPU, 50 GB memory |
@@ -2333,7 +2304,7 @@ future with further specific cloud provider details.
- For this setup, we **recommend** and regularly [test](index.md#validation-and-test-results)
[Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine) and [Amazon Elastic Kubernetes Service (EKS)](https://aws.amazon.com/eks/). Other Kubernetes services may also work, but your mileage may vary.
- Nodes configuration is shown as it is forced to ensure pod vCPU / memory ratios and avoid scaling during **performance testing**.
- - In production deployments, there is no need to assign pods to nodes. A minimum of three nodes in three different availability zones is strongly recommended to align with resilient cloud architecture practices.
+ - In production deployments, there is no need to assign pods to specific nodes. A minimum of three nodes per node group in three different availability zones is strongly recommended to align with resilient cloud architecture practices.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
services where applicable):
@@ -2355,7 +2326,8 @@ services where applicable):
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
- [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work.
- - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB clusters](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB cluster](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - Note that [Amazon RDS Multi-AZ DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZSingleStandby.html) is a separate product and is supported.
- [Amazon Aurora](https://aws.amazon.com/rds/aurora/) is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440).
- Consul is primarily used for Omnibus PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However, Consul is also used optionally by Prometheus for Omnibus auto host discovery.
2. Can be optionally run on reputable third-party external PaaS Redis solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md
index 84eba01fe11..02739904f5e 100644
--- a/doc/administration/reference_architectures/25k_users.md
+++ b/doc/administration/reference_architectures/25k_users.md
@@ -35,13 +35,13 @@ full list of reference architectures, see
| GitLab Rails | 5 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` |
| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
| Object storage<sup>4</sup> | - | - | - | - |
-| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix -->
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
- [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work.
- - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB clusters](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB cluster](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - Note that [Amazon RDS Multi-AZ DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZSingleStandby.html) is a separate product and is supported.
- [Amazon Aurora](https://aws.amazon.com/rds/aurora/) is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440).
- Consul is primarily used for Omnibus PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However, Consul is also used optionally by Prometheus for Omnibus auto host discovery.
2. Can be optionally run on reputable third-party external PaaS Redis solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
@@ -189,7 +189,7 @@ CI pipelines alike.
As such, large repositories come with notable cost and typically will require more resources to handle,
significantly so in some cases. It's therefore **strongly** recommended then to review large repositories
-to ensure they maintain good repo health and reduce their size wherever possible.
+to ensure they maintain good health and reduce their size wherever possible.
NOTE:
If best practices aren't followed and large repositories are present on the environment,
@@ -227,9 +227,6 @@ To set up GitLab and its components to accommodate up to 25,000 users:
environment.
1. [Configure the object storage](#configure-the-object-storage)
used for shared data objects.
-1. [Configure NFS](#configure-nfs-optional) (optional, and not recommended)
- to have shared disk storage service as an alternative to Gitaly or object
- storage.
1. [Configure Advanced Search](#configure-advanced-search) (optional) for faster,
more advanced code search across your entire GitLab instance.
@@ -1295,7 +1292,7 @@ in the second step, do not supply the `EXTERNAL_URL` value.
# PostgreSQL configuration
postgresql['listen_address'] = '0.0.0.0'
- postgresql['max_connections'] = 200
+ postgresql['max_connections'] = 500
# Prevent database migrations from running on upgrade automatically
gitlab_rails['auto_migrate'] = false
@@ -1777,9 +1774,8 @@ To configure Praefect with TLS:
Sidekiq requires connection to the [Redis](#configure-redis),
[PostgreSQL](#configure-postgresql) and [Gitaly](#configure-gitaly) instances.
-Since it's recommended to use [Object storage](#configure-the-object-storage)
-over [NFS](#configure-nfs-optional) for data objects, the following examples
-include the Object storage configuration.
+Because you must use [Object storage](#configure-the-object-storage) instead of NFS for data objects, the following
+examples include the Object storage configuration.
- `10.6.0.101`: Sidekiq 1
- `10.6.0.102`: Sidekiq 2
@@ -1877,8 +1873,8 @@ Updates to example must be made at:
# Set number of Sidekiq queue processes to the same number as available CPUs
sidekiq['queue_groups'] = ['*'] * 4
- # Set number of Sidekiq threads per queue process to the recommend number of 10
- sidekiq['max_concurrency'] = 10
+ # Set number of Sidekiq threads per queue process to the recommend number of 20
+ sidekiq['max_concurrency'] = 20
# Monitoring
consul['enable'] = true
@@ -1936,7 +1932,7 @@ Updates to example must be made at:
NOTE:
If you find that the environment's Sidekiq job processing is slow with long queues,
more nodes can be added as required. You can also tune your Sidekiq nodes to
-run [multiple Sidekiq processes](../operations/extra_sidekiq_processes.md).
+run [multiple Sidekiq processes](../sidekiq/extra_sidekiq_processes.md).
<div align="right">
<a type="button" class="btn btn-default" href="#setup-components">
@@ -1947,9 +1943,8 @@ run [multiple Sidekiq processes](../operations/extra_sidekiq_processes.md).
## Configure GitLab Rails
This section describes how to configure the GitLab application (Rails) component.
-Since it's recommended to use [Object storage](#configure-the-object-storage)
-over [NFS](#configure-nfs-optional) for data objects, the following examples
-include the Object storage configuration.
+Because you must use [Object storage](#configure-the-object-storage) instead of NFS for data objects, the following
+examples include the Object storage configuration.
The following IPs will be used as an example:
@@ -2090,7 +2085,6 @@ On each node perform the following:
1. Copy the `/etc/gitlab/gitlab-secrets.json` file from the first Omnibus node you configured and add or replace
the file of the same name on this server. If this is the first Omnibus node you are configuring then you can skip this step.
-
1. To ensure database migrations are only run during reconfigure and not automatically on upgrade, run:
```shell
@@ -2101,9 +2095,7 @@ On each node perform the following:
[GitLab Rails post-configuration](#gitlab-rails-post-configuration) section.
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-
-1. [Enable incremental logging](#enable-incremental-logging), unless you are using [NFS](#configure-nfs-optional).
-
+1. [Enable incremental logging](#enable-incremental-logging).
1. Confirm the node can connect to Gitaly:
```shell
@@ -2228,9 +2220,6 @@ To configure the Monitoring node:
## Configure the object storage
GitLab supports using an object storage service for holding numerous types of data.
-It's recommended over [NFS](#configure-nfs-optional) and in general it's better
-in larger setups as object storage is typically much more performant, reliable,
-and scalable.
GitLab has been tested on a number of object storage providers:
@@ -2254,7 +2243,7 @@ NOTE:
When using the [storage-specific form](../object_storage.md#storage-specific-configuration)
in GitLab 14.x and earlier, you should enable [direct upload mode](../../development/uploads/index.md#direct-upload).
The previous [background upload](../../development/uploads/index.md#direct-upload) mode,
-which was deprecated in 14.9, requires shared storage such as [NFS](#configure-nfs-optional).
+which was deprecated in 14.9, requires shared storage such as NFS.
Using separate buckets for each data type is the recommended approach for GitLab.
This ensures there are no collisions across the various types of data GitLab stores.
@@ -2273,22 +2262,6 @@ GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily
While sharing the job logs through NFS is supported, it's recommended to avoid the need to use NFS by enabling [incremental logging](../job_logs.md#incremental-logging-architecture) (required when no NFS node has been deployed). Incremental logging uses Redis instead of disk space for temporary caching of job logs.
-## Configure NFS (optional)
-
-[Object storage](#configure-the-object-storage), along with [Gitaly](#configure-gitaly)
-are recommended over NFS wherever possible for improved performance.
-
-See how to [configure NFS](../nfs.md).
-
-WARNING:
-Engineering support for NFS for Git repositories is deprecated, and [technical support is scheduled to be unavailable](../nfs.md#gitaly-and-nfs-deprecation)
-after the release of GitLab 15.6. No further enhancements are planned for this feature.
-
-Read:
-
-- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
-- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
-
## Configure Advanced Search
You can leverage Elasticsearch and [enable Advanced Search](../../integration/advanced_search/elasticsearch.md)
@@ -2338,12 +2311,10 @@ Refer to [epic 6127](https://gitlab.com/groups/gitlab-org/-/epics/6127) for more
The following tables and diagram detail the hybrid environment using the same formats
as the normal environment above.
-First are the components that run in Kubernetes. The recommendation at this time is to
-use Google Cloud's Kubernetes Engine (GKE) or AWS Elastic Kubernetes Service (EKS) and associated machine types, but the memory
-and CPU requirements should translate to most other providers. We hope to update this in the
-future with further specific cloud provider details.
+First are the components that run in Kubernetes. These run across several node groups, although you can change
+the overall makeup as desired as long as the minimum CPU and Memory requirements are observed.
-| Service | Nodes | Configuration | GCP | AWS | Min Allocatable CPUs and Memory |
+| Service Node Group | Nodes | Configuration | GCP | AWS | Min Allocatable CPUs and Memory |
|---------------------|-------|-------------------------|-----------------|--------------|---------------------------------|
| Webservice | 7 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` | 223 vCPU, 206.5 GB memory |
| Sidekiq | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | 15.5 vCPU, 50 GB memory |
@@ -2352,7 +2323,7 @@ future with further specific cloud provider details.
- For this setup, we **recommend** and regularly [test](index.md#validation-and-test-results)
[Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine) and [Amazon Elastic Kubernetes Service (EKS)](https://aws.amazon.com/eks/). Other Kubernetes services may also work, but your mileage may vary.
- Nodes configuration is shown as it is forced to ensure pod vCPU / memory ratios and avoid scaling during **performance testing**.
- - In production deployments, there is no need to assign pods to nodes. A minimum of three nodes in three different availability zones is strongly recommended to align with resilient cloud architecture practices.
+ - In production deployments, there is no need to assign pods to specific nodes. A minimum of three nodes per node group in three different availability zones is strongly recommended to align with resilient cloud architecture practices.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
services where applicable):
@@ -2362,7 +2333,7 @@ services where applicable):
| Consul<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
| PostgreSQL<sup>1</sup> | 3 | 16 vCPU, 60 GB memory | `n1-standard-16` | `m5.4xlarge` |
| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Internal load balancing node<sup>3</sup> | 1 | 4 vCPU, 3.6GB memory | `n1-highcpu-4` | `c5.xlarge` |
+| Internal load balancing node<sup>3</sup> | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
| Redis/Sentinel - Cache<sup>2</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` |
| Redis/Sentinel - Persistent<sup>2</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` |
| Gitaly<sup>5 6</sup> | 3 | 32 vCPU, 120 GB memory | `n1-standard-32` | `m5.8xlarge` |
@@ -2374,7 +2345,8 @@ services where applicable):
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
- [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work.
- - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB clusters](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB cluster](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - Note that [Amazon RDS Multi-AZ DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZSingleStandby.html) is a separate product and is supported.
- [Amazon Aurora](https://aws.amazon.com/rds/aurora/) is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440).
- Consul is primarily used for Omnibus PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However, Consul is also used optionally by Prometheus for Omnibus auto host discovery.
2. Can be optionally run on reputable third-party external PaaS Redis solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md
index 1acae93f764..f41c8e9cb24 100644
--- a/doc/administration/reference_architectures/2k_users.md
+++ b/doc/administration/reference_architectures/2k_users.md
@@ -29,12 +29,12 @@ For a full list of reference architectures, see
| GitLab Rails | 2 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` | `F8s v2` |
| Monitoring node | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
| Object storage<sup>4</sup> | - | - | - | - | - |
-| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
- [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work.
- - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB clusters](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB cluster](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - Note that [Amazon RDS Multi-AZ DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZSingleStandby.html) is a separate product and is supported.
- [Amazon Aurora](https://aws.amazon.com/rds/aurora/) is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440), and [Azure Database for PostgreSQL](https://azure.microsoft.com/en-gb/products/postgresql/#overview) is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61).
- Consul is primarily used for Omnibus PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However, Consul is also used optionally by Prometheus for Omnibus auto host discovery.
2. Can be optionally run on reputable third-party external PaaS Redis solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
@@ -125,7 +125,7 @@ CI pipelines alike.
As such, large repositories come with notable cost and typically will require more resources to handle,
significantly so in some cases. It's therefore **strongly** recommended then to review large repositories
-to ensure they maintain good repo health and reduce their size wherever possible.
+to ensure they maintain good health and reduce their size wherever possible.
NOTE:
If best practices aren't followed and large repositories are present on the environment,
@@ -151,9 +151,6 @@ To set up GitLab and its components to accommodate up to 2,000 users:
environment.
1. [Configure the object storage](#configure-the-object-storage) used for
shared data objects.
-1. [Configure NFS](#configure-nfs-optional) (optional, and not recommended)
- to have shared disk storage service as an alternative to Gitaly or object
- storage.
1. [Configure Advanced Search](#configure-advanced-search) (optional) for faster,
more advanced code search across your entire GitLab instance.
@@ -700,8 +697,8 @@ On each node perform the following:
puma['listen'] = '0.0.0.0'
sidekiq['listen_address'] = "0.0.0.0"
- # Configure Sidekiq with 2 workers and 10 max concurrency
- sidekiq['max_concurrency'] = 10
+ # Configure Sidekiq with 2 workers and 20 max concurrency
+ sidekiq['max_concurrency'] = 20
sidekiq['queue_groups'] = ['*'] * 2
# Add the monitoring node's IP address to the monitoring whitelist and allow it to
@@ -780,9 +777,7 @@ On each node perform the following:
[GitLab Rails post-configuration](#gitlab-rails-post-configuration) section.
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-
-1. [Enable incremental logging](#enable-incremental-logging), unless you are using [NFS](#configure-nfs-optional).
-
+1. [Enable incremental logging](#enable-incremental-logging).
1. Run `sudo gitlab-rake gitlab:gitaly:check` to confirm the node can connect to Gitaly.
1. Tail the logs to see the requests:
@@ -930,9 +925,6 @@ running [Prometheus](../monitoring/prometheus/index.md) and
## Configure the object storage
GitLab supports using an object storage service for holding numerous types of data.
-It's recommended over [NFS](#configure-nfs-optional) and in general it's better
-in larger setups as object storage is typically much more performant, reliable,
-and scalable.
GitLab has been tested on a number of object storage providers:
@@ -957,7 +949,7 @@ NOTE:
When using the [storage-specific form](../object_storage.md#storage-specific-configuration)
in GitLab 14.x and earlier, you should enable [direct upload mode](../../development/uploads/index.md#direct-upload).
The previous [background upload](../../development/uploads/index.md#direct-upload) mode,
-which was deprecated in 14.9, requires shared storage such as [NFS](#configure-nfs-optional).
+which was deprecated in 14.9, requires shared storage such as NFS.
Using separate buckets for each data type is the recommended approach for GitLab.
This ensures there are no collisions across the various types of data GitLab stores.
@@ -976,23 +968,6 @@ GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily
While sharing the job logs through NFS is supported, it's recommended to avoid the need to use NFS by enabling [incremental logging](../job_logs.md#incremental-logging-architecture) (required when no NFS node has been deployed). Incremental logging uses Redis instead of disk space for temporary caching of job logs.
-## Configure NFS (optional)
-
-For improved performance, [object storage](#configure-the-object-storage),
-along with [Gitaly](#configure-gitaly), are recommended over using NFS whenever
-possible.
-
-See how to [configure NFS](../nfs.md).
-
-WARNING:
-Engineering support for NFS for Git repositories is deprecated, and [technical support is scheduled to be unavailable](../nfs.md#gitaly-and-nfs-deprecation)
-after the release of GitLab 15.6. No further enhancements are planned for this feature.
-
-Read:
-
-- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
-- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
-
## Configure Advanced Search **(PREMIUM SELF)**
You can leverage Elasticsearch and [enable Advanced Search](../../integration/advanced_search/elasticsearch.md)
@@ -1046,12 +1021,10 @@ Refer to [epic 6127](https://gitlab.com/groups/gitlab-org/-/epics/6127) for more
The following tables and diagram detail the hybrid environment using the same formats
as the normal environment above.
-First are the components that run in Kubernetes. The recommendation at this time is to
-use Google Cloud's Kubernetes Engine (GKE) or AWS Elastic Kubernetes Service (EKS) and associated machine types, but the memory
-and CPU requirements should translate to most other providers. We hope to update this in the
-future with further specific cloud provider details.
+First are the components that run in Kubernetes. These run across several node groups, although you can change
+the overall makeup as desired as long as the minimum CPU and Memory requirements are observed.
-| Service | Nodes | Configuration | GCP | AWS | Min Allocatable CPUs and Memory |
+| Service Node Group | Nodes | Configuration | GCP | AWS | Min Allocatable CPUs and Memory |
|---------------------|-------|------------------------|-----------------|--------------|---------------------------------|
| Webservice | 3 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` | 23.7 vCPU, 16.9 GB memory |
| Sidekiq | 2 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | 7.8 vCPU, 25.9 GB memory |
@@ -1060,7 +1033,7 @@ future with further specific cloud provider details.
- For this setup, we **recommend** and regularly [test](index.md#validation-and-test-results)
[Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine) and [Amazon Elastic Kubernetes Service (EKS)](https://aws.amazon.com/eks/). Other Kubernetes services may also work, but your mileage may vary.
- Nodes configuration is shown as it is forced to ensure pod vCPU / memory ratios and avoid scaling during **performance testing**.
- - In production deployments, there is no need to assign pods to nodes. A minimum of three nodes in three different availability zones is strongly recommended to align with resilient cloud architecture practices.
+ - In production deployments, there is no need to assign pods to specific nodes. A minimum of three nodes per node group in three different availability zones is strongly recommended to align with resilient cloud architecture practices.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
services where applicable):
@@ -1076,7 +1049,8 @@ services where applicable):
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
- [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work.
- - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB clusters](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB cluster](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - Note that [Amazon RDS Multi-AZ DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZSingleStandby.html) is a separate product and is supported.
- [Amazon Aurora](https://aws.amazon.com/rds/aurora/) is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440), and [Azure Database for PostgreSQL](https://azure.microsoft.com/en-gb/products/postgresql/#overview) is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61).
- Consul is primarily used for Omnibus PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However, Consul is also used optionally by Prometheus for Omnibus auto host discovery.
2. Can be optionally run on reputable third-party external PaaS Redis solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md
index 4fc6af3f72e..008b5ffcc0e 100644
--- a/doc/administration/reference_architectures/3k_users.md
+++ b/doc/administration/reference_architectures/3k_users.md
@@ -44,13 +44,13 @@ For a full list of reference architectures, see
| GitLab Rails | 3 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` |
| Monitoring node | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
| Object storage<sup>4</sup> | - | - | - | - |
-| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix -->
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
- [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work.
- - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB clusters](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB cluster](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - Note that [Amazon RDS Multi-AZ DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZSingleStandby.html) is a separate product and is supported.
- [Amazon Aurora](https://aws.amazon.com/rds/aurora/) is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440).
- Consul is primarily used for Omnibus PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However, Consul is also used optionally by Prometheus for Omnibus auto host discovery.
2. Can be optionally run on reputable third-party external PaaS Redis solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
@@ -195,7 +195,7 @@ CI pipelines alike.
As such, large repositories come with notable cost and typically will require more resources to handle,
significantly so in some cases. It's therefore **strongly** recommended then to review large repositories
-to ensure they maintain good repo health and reduce their size wherever possible.
+to ensure they maintain good health and reduce their size wherever possible.
NOTE:
If best practices aren't followed and large repositories are present on the environment,
@@ -233,9 +233,6 @@ To set up GitLab and its components to accommodate up to 3,000 users:
environment.
1. [Configure the object storage](#configure-the-object-storage)
used for shared data objects.
-1. [Configure NFS](#configure-nfs-optional) (optional, and not recommended)
- to have shared disk storage service as an alternative to Gitaly or object
- storage.
1. [Configure Advanced Search](#configure-advanced-search) (optional) for faster,
more advanced code search across your entire GitLab instance.
@@ -1230,7 +1227,7 @@ in the second step, do not supply the `EXTERNAL_URL` value.
# PostgreSQL configuration
postgresql['listen_address'] = '0.0.0.0'
- postgresql['max_connections'] = 200
+ postgresql['max_connections'] = 500
# Prevent database migrations from running on upgrade automatically
gitlab_rails['auto_migrate'] = false
@@ -1711,9 +1708,8 @@ To configure Praefect with TLS:
Sidekiq requires connection to the [Redis](#configure-redis),
[PostgreSQL](#configure-postgresql) and [Gitaly](#configure-gitaly) instances.
-Since it's recommended to use [Object storage](#configure-the-object-storage)
-over [NFS](#configure-nfs-optional) for data objects, the following examples
-include the Object storage configuration.
+Because you must use [Object storage](#configure-the-object-storage) instead of NFS for data objects, the following
+examples include the Object storage configuration.
The following IPs will be used as an example:
@@ -1794,8 +1790,8 @@ Updates to example must be made at:
## Set number of Sidekiq queue processes to the same number as available CPUs
sidekiq['queue_groups'] = ['*'] * 2
- ## Set number of Sidekiq threads per queue process to the recommend number of 10
- sidekiq['max_concurrency'] = 10
+ ## Set number of Sidekiq threads per queue process to the recommend number of 20
+ sidekiq['max_concurrency'] = 20
# Monitoring
consul['enable'] = true
@@ -1869,7 +1865,7 @@ Updates to example must be made at:
NOTE:
If you find that the environment's Sidekiq job processing is slow with long queues,
more nodes can be added as required. You can also tune your Sidekiq nodes to
-run [multiple Sidekiq processes](../operations/extra_sidekiq_processes.md).
+run [multiple Sidekiq processes](../sidekiq/extra_sidekiq_processes.md).
<div align="right">
<a type="button" class="btn btn-default" href="#setup-components">
@@ -1880,9 +1876,8 @@ run [multiple Sidekiq processes](../operations/extra_sidekiq_processes.md).
## Configure GitLab Rails
This section describes how to configure the GitLab application (Rails) component.
-Since it's recommended to use [Object storage](#configure-the-object-storage)
-over [NFS](#configure-nfs-optional) for data objects, the following examples
-include the Object storage configuration.
+Because you must use [Object storage](#configure-the-object-storage) instead of NFS for data objects, the following
+examples include the Object storage configuration.
On each node perform the following:
@@ -2021,7 +2016,6 @@ On each node perform the following:
1. Copy the `/etc/gitlab/gitlab-secrets.json` file from the first Omnibus node you configured and add or replace
the file of the same name on this server. If this is the first Omnibus node you are configuring then you can skip this step.
-
1. To ensure database migrations are only run during reconfigure and not automatically on upgrade, run:
```shell
@@ -2032,11 +2026,8 @@ On each node perform the following:
[GitLab Rails post-configuration](#gitlab-rails-post-configuration) section.
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-
-1. [Enable incremental logging](#enable-incremental-logging), unless you are using [NFS](#configure-nfs-optional).
-
+1. [Enable incremental logging](#enable-incremental-logging).
1. Run `sudo gitlab-rake gitlab:gitaly:check` to confirm the node can connect to Gitaly.
-
1. Tail the logs to see the requests:
```shell
@@ -2175,9 +2166,6 @@ running [Prometheus](../monitoring/prometheus/index.md) and
## Configure the object storage
GitLab supports using an object storage service for holding numerous types of data.
-It's recommended over [NFS](#configure-nfs-optional) and in general it's better
-in larger setups as object storage is typically much more performant, reliable,
-and scalable.
GitLab has been tested on a number of object storage providers:
@@ -2201,7 +2189,7 @@ NOTE:
When using the [storage-specific form](../object_storage.md#storage-specific-configuration)
in GitLab 14.x and earlier, you should enable [direct upload mode](../../development/uploads/index.md#direct-upload).
The previous [background upload](../../development/uploads/index.md#direct-upload) mode,
-which was deprecated in 14.9, requires shared storage such as [NFS](#configure-nfs-optional).
+which was deprecated in 14.9, requires shared storage such as NFS.
Using separate buckets for each data type is the recommended approach for GitLab.
This ensures there are no collisions across the various types of data GitLab stores.
@@ -2220,22 +2208,6 @@ GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily
While sharing the job logs through NFS is supported, it's recommended to avoid the need to use NFS by enabling [incremental logging](../job_logs.md#incremental-logging-architecture) (required when no NFS node has been deployed). Incremental logging uses Redis instead of disk space for temporary caching of job logs.
-## Configure NFS (optional)
-
-[Object storage](#configure-the-object-storage), along with [Gitaly](#configure-gitaly)
-are recommended over NFS wherever possible for improved performance.
-
-See how to [configure NFS](../nfs.md).
-
-WARNING:
-Engineering support for NFS for Git repositories is deprecated, and [technical support is scheduled to be unavailable](../nfs.md#gitaly-and-nfs-deprecation)
-after the release of GitLab 15.6. No further enhancements are planned for this feature.
-
-Read:
-
-- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
-- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
-
## Configure Advanced Search
You can leverage Elasticsearch and [enable Advanced Search](../../integration/advanced_search/elasticsearch.md)
@@ -2309,12 +2281,10 @@ Refer to [epic 6127](https://gitlab.com/groups/gitlab-org/-/epics/6127) for more
The following tables and diagram detail the hybrid environment using the same formats
as the normal environment above.
-First are the components that run in Kubernetes. The recommendation at this time is to
-use Google Cloud's Kubernetes Engine (GKE) or AWS Elastic Kubernetes Service (EKS) and associated machine types, but the memory
-and CPU requirements should translate to most other providers. We hope to update this in the
-future with further specific cloud provider details.
+First are the components that run in Kubernetes. These run across several node groups, although you can change
+the overall makeup as desired as long as the minimum CPU and Memory requirements are observed.
-| Service | Nodes | Configuration | GCP | AWS | Min Allocatable CPUs and Memory |
+| Service Node Group | Nodes | Configuration | GCP | AWS | Min Allocatable CPUs and Memory |
|---------------------|-------|-------------------------|-----------------|--------------|---------------------------------|
| Webservice | 2 | 16 vCPU, 14.4 GB memory | `n1-highcpu-16` | `c5.4xlarge` | 31.8 vCPU, 24.8 GB memory |
| Sidekiq | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | 11.8 vCPU, 38.9 GB memory |
@@ -2323,7 +2293,7 @@ future with further specific cloud provider details.
- For this setup, we **recommend** and regularly [test](index.md#validation-and-test-results)
[Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine) and [Amazon Elastic Kubernetes Service (EKS)](https://aws.amazon.com/eks/). Other Kubernetes services may also work, but your mileage may vary.
- Nodes configuration is shown as it is forced to ensure pod vCPU / memory ratios and avoid scaling during **performance testing**.
- - In production deployments, there is no need to assign pods to nodes. A minimum of three nodes in three different availability zones is strongly recommended to align with resilient cloud architecture practices.
+ - In production deployments, there is no need to assign pods to specific nodes. A minimum of three nodes per node group in three different availability zones is strongly recommended to align with resilient cloud architecture practices.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
services where applicable):
@@ -2344,7 +2314,8 @@ services where applicable):
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
- [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work.
- - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB clusters](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB cluster](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - Note that [Amazon RDS Multi-AZ DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZSingleStandby.html) is a separate product and is supported.
- [Amazon Aurora](https://aws.amazon.com/rds/aurora/) is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440).
- Consul is primarily used for Omnibus PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However, Consul is also used optionally by Prometheus for Omnibus auto host discovery.
2. Can be optionally run on reputable third-party external PaaS Redis solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md
index ca159d62f1f..87d1408b568 100644
--- a/doc/administration/reference_architectures/50k_users.md
+++ b/doc/administration/reference_architectures/50k_users.md
@@ -35,13 +35,13 @@ full list of reference architectures, see
| GitLab Rails | 12 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` |
| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
| Object storage<sup>4</sup> | - | - | - | - |
-| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix -->
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
- [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work.
- - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB clusters](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB cluster](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - Note that [Amazon RDS Multi-AZ DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZSingleStandby.html) is a separate product and is supported.
- [Amazon Aurora](https://aws.amazon.com/rds/aurora/) is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440).
- Consul is primarily used for Omnibus PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However, Consul is also used optionally by Prometheus for Omnibus auto host discovery.
2. Can be optionally run on reputable third-party external PaaS Redis solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
@@ -189,7 +189,7 @@ CI pipelines alike.
As such, large repositories come with notable cost and typically will require more resources to handle,
significantly so in some cases. It's therefore **strongly** recommended then to review large repositories
-to ensure they maintain good repo health and reduce their size wherever possible.
+to ensure they maintain good health and reduce their size wherever possible.
NOTE:
If best practices aren't followed and large repositories are present on the environment,
@@ -227,9 +227,6 @@ To set up GitLab and its components to accommodate up to 50,000 users:
environment.
1. [Configure the object storage](#configure-the-object-storage)
used for shared data objects.
-1. [Configure NFS](#configure-nfs-optional) (optional, and not recommended)
- to have shared disk storage service as an alternative to Gitaly or object
- storage.
1. [Configure Advanced Search](#configure-advanced-search) (optional) for faster,
more advanced code search across your entire GitLab instance.
@@ -296,8 +293,8 @@ could also be used, those load balancers have not been validated.
### Balancing algorithm
-We recommend that a least-connection load balancing algorithm or equivalent
-is used wherever possible to ensure equal spread of calls to the nodes and good performance.
+You should use a least-connection load balancing algorithm or equivalent
+wherever possible to ensure equal spread of calls to the nodes and good performance.
We don't recommend the use of round-robin algorithms as they are known to not
spread connections equally in practice.
@@ -1288,7 +1285,7 @@ in the second step, do not supply the `EXTERNAL_URL` value.
# PostgreSQL configuration
postgresql['listen_address'] = '0.0.0.0'
- postgresql['max_connections'] = 200
+ postgresql['max_connections'] = 500
# Prevent database migrations from running on upgrade automatically
gitlab_rails['auto_migrate'] = false
@@ -1772,9 +1769,8 @@ To configure Praefect with TLS:
Sidekiq requires connection to the [Redis](#configure-redis),
[PostgreSQL](#configure-postgresql) and [Gitaly](#configure-gitaly) instances.
-Since it's recommended to use [Object storage](#configure-the-object-storage)
-over [NFS](#configure-nfs-optional) for data objects, the following examples
-include the Object storage configuration.
+Because you must use [Object storage](#configure-the-object-storage) instead of NFS for data objects, the following
+examples include the Object storage configuration.
- `10.6.0.101`: Sidekiq 1
- `10.6.0.102`: Sidekiq 2
@@ -1872,8 +1868,8 @@ Updates to example must be made at:
## Set number of Sidekiq queue processes to the same number as available CPUs
sidekiq['queue_groups'] = ['*'] * 4
- ## Set number of Sidekiq threads per queue process to the recommend number of 10
- sidekiq['max_concurrency'] = 10
+ ## Set number of Sidekiq threads per queue process to the recommend number of 20
+ sidekiq['max_concurrency'] = 20
# Monitoring
consul['enable'] = true
@@ -1931,7 +1927,7 @@ Updates to example must be made at:
NOTE:
If you find that the environment's Sidekiq job processing is slow with long queues,
more nodes can be added as required. You can also tune your Sidekiq nodes to
-run [multiple Sidekiq processes](../operations/extra_sidekiq_processes.md).
+run [multiple Sidekiq processes](../sidekiq/extra_sidekiq_processes.md).
<div align="right">
<a type="button" class="btn btn-default" href="#setup-components">
@@ -1942,9 +1938,8 @@ run [multiple Sidekiq processes](../operations/extra_sidekiq_processes.md).
## Configure GitLab Rails
This section describes how to configure the GitLab application (Rails) component.
-Since it's recommended to use [Object storage](#configure-the-object-storage)
-over [NFS](#configure-nfs-optional) for data objects, the following examples
-include the Object storage configuration.
+Because you must use [Object storage](#configure-the-object-storage) instead of NFS for data objects, the following
+examples include the Object storage configuration.
The following IPs will be used as an example:
@@ -2092,7 +2087,6 @@ On each node perform the following:
1. Copy the `/etc/gitlab/gitlab-secrets.json` file from the first Omnibus node you configured and add or replace
the file of the same name on this server. If this is the first Omnibus node you are configuring then you can skip this step.
-
1. To ensure database migrations are only run during reconfigure and not automatically on upgrade, run:
```shell
@@ -2103,9 +2097,7 @@ On each node perform the following:
[GitLab Rails post-configuration](#gitlab-rails-post-configuration) section.
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-
-1. [Enable incremental logging](#enable-incremental-logging), unless you are using [NFS](#configure-nfs-optional).
-
+1. [Enable incremental logging](#enable-incremental-logging).
1. Confirm the node can connect to Gitaly:
```shell
@@ -2230,9 +2222,6 @@ To configure the Monitoring node:
## Configure the object storage
GitLab supports using an object storage service for holding numerous types of data.
-It's recommended over [NFS](#configure-nfs-optional) and in general it's better
-in larger setups as object storage is typically much more performant, reliable,
-and scalable.
GitLab has been tested on a number of object storage providers:
@@ -2256,7 +2245,7 @@ NOTE:
When using the [storage-specific form](../object_storage.md#storage-specific-configuration)
in GitLab 14.x and earlier, you should enable [direct upload mode](../../development/uploads/index.md#direct-upload).
The previous [background upload](../../development/uploads/index.md#direct-upload) mode,
-which was deprecated in 14.9, requires shared storage such as [NFS](#configure-nfs-optional).
+which was deprecated in 14.9, requires shared storage such as NFS.
Using separate buckets for each data type is the recommended approach for GitLab.
This ensures there are no collisions across the various types of data GitLab stores.
@@ -2275,22 +2264,6 @@ GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily
While sharing the job logs through NFS is supported, it's recommended to avoid the need to use NFS by enabling [incremental logging](../job_logs.md#incremental-logging-architecture) (required when no NFS node has been deployed). Incremental logging uses Redis instead of disk space for temporary caching of job logs.
-## Configure NFS (optional)
-
-[Object storage](#configure-the-object-storage), along with [Gitaly](#configure-gitaly)
-are recommended over NFS wherever possible for improved performance.
-
-See how to [configure NFS](../nfs.md).
-
-WARNING:
-Engineering support for NFS for Git repositories is deprecated, and [technical support is scheduled to be unavailable](../nfs.md#gitaly-and-nfs-deprecation)
-after the release of GitLab 15.6. No further enhancements are planned for this feature.
-
-Read:
-
-- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
-- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
-
## Configure Advanced Search
You can leverage Elasticsearch and [enable Advanced Search](../../integration/advanced_search/elasticsearch.md)
@@ -2340,12 +2313,10 @@ Refer to [epic 6127](https://gitlab.com/groups/gitlab-org/-/epics/6127) for more
The following tables and diagram detail the hybrid environment using the same formats
as the normal environment above.
-First are the components that run in Kubernetes. The recommendation at this time is to
-use Google Cloud's Kubernetes Engine (GKE) or AWS Elastic Kubernetes Service (EKS) and associated machine types, but the memory
-and CPU requirements should translate to most other providers. We hope to update this in the
-future with further specific cloud provider details.
+First are the components that run in Kubernetes. These run across several node groups, although you can change
+the overall makeup as desired as long as the minimum CPU and Memory requirements are observed.
-| Service | Nodes | Configuration | GCP | AWS | Min Allocatable CPUs and Memory |
+| Service Node Group | Nodes | Configuration | GCP | AWS | Min Allocatable CPUs and Memory |
|---------------------|-------|-------------------------|-----------------|--------------|---------------------------------|
| Webservice | 16 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `m5.8xlarge` | 510 vCPU, 472 GB memory |
| Sidekiq | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | 15.5 vCPU, 50 GB memory |
@@ -2354,7 +2325,7 @@ future with further specific cloud provider details.
- For this setup, we **recommend** and regularly [test](index.md#validation-and-test-results)
[Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine) and [Amazon Elastic Kubernetes Service (EKS)](https://aws.amazon.com/eks/). Other Kubernetes services may also work, but your mileage may vary.
- Nodes configuration is shown as it is forced to ensure pod vCPU / memory ratios and avoid scaling during **performance testing**.
- - In production deployments, there is no need to assign pods to nodes. A minimum of three nodes in three different availability zones is strongly recommended to align with resilient cloud architecture practices.
+ - In production deployments, there is no need to assign pods to specific nodes. A minimum of three nodes per node group in three different availability zones is strongly recommended to align with resilient cloud architecture practices.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
services where applicable):
@@ -2376,7 +2347,8 @@ services where applicable):
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
- [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work.
- - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB clusters](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB cluster](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - Note that [Amazon RDS Multi-AZ DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZSingleStandby.html) is a separate product and is supported.
- [Amazon Aurora](https://aws.amazon.com/rds/aurora/) is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440).
- Consul is primarily used for Omnibus PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However, Consul is also used optionally by Prometheus for Omnibus auto host discovery.
2. Can be optionally run on reputable third-party external PaaS Redis solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md
index a2b92f9c300..182edb82b5f 100644
--- a/doc/administration/reference_architectures/5k_users.md
+++ b/doc/administration/reference_architectures/5k_users.md
@@ -41,13 +41,13 @@ costly-to-operate environment by using the
| GitLab Rails | 3 | 16 vCPU, 14.4 GB memory | `n1-highcpu-16` | `c5.4xlarge` |
| Monitoring node | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
| Object storage<sup>4</sup> | - | - | - | - |
-| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix -->
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
- [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work.
- - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB clusters](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB cluster](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - Note that [Amazon RDS Multi-AZ DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZSingleStandby.html) is a separate product and is supported.
- [Amazon Aurora](https://aws.amazon.com/rds/aurora/) is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440).
- Consul is primarily used for Omnibus PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However, Consul is also used optionally by Prometheus for Omnibus auto host discovery.
2. Can be optionally run on reputable third-party external PaaS Redis solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
@@ -192,7 +192,7 @@ CI pipelines alike.
As such, large repositories come with notable cost and typically will require more resources to handle,
significantly so in some cases. It's therefore **strongly** recommended then to review large repositories
-to ensure they maintain good repo health and reduce their size wherever possible.
+to ensure they maintain good health and reduce their size wherever possible.
NOTE:
If best practices aren't followed and large repositories are present on the environment,
@@ -230,9 +230,6 @@ To set up GitLab and its components to accommodate up to 5,000 users:
environment.
1. [Configure the object storage](#configure-the-object-storage)
used for shared data objects.
-1. [Configure NFS](#configure-nfs-optional) (optional, and not recommended)
- to have shared disk storage service as an alternative to Gitaly or object
- storage.
1. [Configure Advanced Search](#configure-advanced-search) (optional) for faster,
more advanced code search across your entire GitLab instance.
@@ -1226,7 +1223,7 @@ in the second step, do not supply the `EXTERNAL_URL` value.
# PostgreSQL configuration
postgresql['listen_address'] = '0.0.0.0'
- postgresql['max_connections'] = 200
+ postgresql['max_connections'] = 500
# Prevent database migrations from running on upgrade automatically
gitlab_rails['auto_migrate'] = false
@@ -1708,9 +1705,8 @@ To configure Praefect with TLS:
Sidekiq requires connection to the [Redis](#configure-redis),
[PostgreSQL](#configure-postgresql) and [Gitaly](#configure-gitaly) instances.
-Since it's recommended to use [Object storage](#configure-the-object-storage)
-over [NFS](#configure-nfs-optional) for data objects, the following examples
-include the Object storage configuration.
+Because you must use [Object storage](#configure-the-object-storage) instead of NFS for data objects, the following
+examples include the Object storage configuration.
- `10.6.0.71`: Sidekiq 1
- `10.6.0.72`: Sidekiq 2
@@ -1790,8 +1786,8 @@ Updates to example must be made at:
## Set number of Sidekiq queue processes to the same number as available CPUs
sidekiq['queue_groups'] = ['*'] * 4
- ## Set number of Sidekiq threads per queue process to the recommend number of 10
- sidekiq['max_concurrency'] = 10
+ ## Set number of Sidekiq threads per queue process to the recommend number of 20
+ sidekiq['max_concurrency'] = 20
# Monitoring
consul['enable'] = true
@@ -1865,7 +1861,7 @@ Updates to example must be made at:
NOTE:
If you find that the environment's Sidekiq job processing is slow with long queues,
more nodes can be added as required. You can also tune your Sidekiq nodes to
-run [multiple Sidekiq processes](../operations/extra_sidekiq_processes.md).
+run [multiple Sidekiq processes](../sidekiq/extra_sidekiq_processes.md).
<div align="right">
<a type="button" class="btn btn-default" href="#setup-components">
@@ -1876,9 +1872,8 @@ run [multiple Sidekiq processes](../operations/extra_sidekiq_processes.md).
## Configure GitLab Rails
This section describes how to configure the GitLab application (Rails) component.
-Since it's recommended to use [Object storage](#configure-the-object-storage)
-over [NFS](#configure-nfs-optional) for data objects, the following examples
-include the Object storage configuration.
+Because you must use [Object storage](#configure-the-object-storage) instead of NFS for data objects, the following
+examples include the Object storage configuration.
On each node perform the following:
@@ -2020,7 +2015,6 @@ On each node perform the following:
1. Copy the `/etc/gitlab/gitlab-secrets.json` file from the first Omnibus node you configured and add or replace
the file of the same name on this server. If this is the first Omnibus node you are configuring then you can skip this step.
-
1. To ensure database migrations are only run during reconfigure and not automatically on upgrade, run:
```shell
@@ -2031,11 +2025,8 @@ On each node perform the following:
[GitLab Rails post-configuration](#gitlab-rails-post-configuration) section.
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-
-1. [Enable incremental logging](#enable-incremental-logging), unless you are using [NFS](#configure-nfs-optional).
-
+1. [Enable incremental logging](#enable-incremental-logging).
1. Run `sudo gitlab-rake gitlab:gitaly:check` to confirm the node can connect to Gitaly.
-
1. Tail the logs to see the requests:
```shell
@@ -2174,9 +2165,6 @@ running [Prometheus](../monitoring/prometheus/index.md) and
## Configure the object storage
GitLab supports using an object storage service for holding numerous types of data.
-It's recommended over [NFS](#configure-nfs-optional) and in general it's better
-in larger setups as object storage is typically much more performant, reliable,
-and scalable.
GitLab has been tested on a number of object storage providers:
@@ -2200,7 +2188,7 @@ NOTE:
When using the [storage-specific form](../object_storage.md#storage-specific-configuration)
in GitLab 14.x and earlier, you should enable [direct upload mode](../../development/uploads/index.md#direct-upload).
The previous [background upload](../../development/uploads/index.md#direct-upload) mode,
-which was deprecated in 14.9, requires shared storage such as [NFS](#configure-nfs-optional).
+which was deprecated in 14.9, requires shared storage such as NFS.
Using separate buckets for each data type is the recommended approach for GitLab.
This ensures there are no collisions across the various types of data GitLab stores.
@@ -2219,22 +2207,6 @@ GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily
While sharing the job logs through NFS is supported, it's recommended to avoid the need to use NFS by enabling [incremental logging](../job_logs.md#incremental-logging-architecture) (required when no NFS node has been deployed). Incremental logging uses Redis instead of disk space for temporary caching of job logs.
-## Configure NFS (optional)
-
-[Object storage](#configure-the-object-storage), along with [Gitaly](#configure-gitaly)
-are recommended over NFS wherever possible for improved performance.
-
-See how to [configure NFS](../nfs.md).
-
-WARNING:
-Engineering support for NFS for Git repositories is deprecated, and [technical support is scheduled to be unavailable](../nfs.md#gitaly-and-nfs-deprecation)
-after the release of GitLab 15.6. No further enhancements are planned for this feature.
-
-Read:
-
-- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
-- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
-
## Configure Advanced Search
You can leverage Elasticsearch and [enable Advanced Search](../../integration/advanced_search/elasticsearch.md)
@@ -2284,16 +2256,14 @@ Refer to [epic 6127](https://gitlab.com/groups/gitlab-org/-/epics/6127) for more
The following tables and diagram detail the hybrid environment using the same formats
as the normal environment above.
-First are the components that run in Kubernetes. The recommendation at this time is to
-use Google Cloud's Kubernetes Engine (GKE) or AWS Elastic Kubernetes Service (EKS) and associated machine types, but the memory
-and CPU requirements should translate to most other providers. We hope to update this in the
-future with further specific cloud provider details.
+First are the components that run in Kubernetes. These run across several node groups, although you can change
+the overall makeup as desired as long as the minimum CPU and Memory requirements are observed.
-| Service | Nodes | Configuration | GCP | AWS | Min Allocatable CPUs and Memory |
-|-----------------------------------------------|-------|-------------------------|-----------------|--------------|---------------------------------|
-| Webservice | 5 | 16 vCPU, 14.4 GB memory | `n1-highcpu-16` | `c5.4xlarge` | 79.5 vCPU, 62 GB memory |
-| Sidekiq | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | 11.8 vCPU, 38.9 GB memory |
-| Supporting services such as NGINX, Prometheus | 2 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | 3.9 vCPU, 11.8 GB memory |
+| Service Node Group | Nodes | Configuration | GCP | AWS | Min Allocatable CPUs and Memory |
+|-------------------- |-------|-------------------------|-----------------|--------------|---------------------------------|
+| Webservice | 5 | 16 vCPU, 14.4 GB memory | `n1-highcpu-16` | `c5.4xlarge` | 79.5 vCPU, 62 GB memory |
+| Sidekiq | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | 11.8 vCPU, 38.9 GB memory |
+| Supporting services | 2 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | 3.9 vCPU, 11.8 GB memory |
- For this setup, we **recommend** and regularly [test](index.md#validation-and-test-results)
[Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine) and [Amazon Elastic Kubernetes Service (EKS)](https://aws.amazon.com/eks/). Other Kubernetes services may also work, but your mileage may vary.
@@ -2319,7 +2289,8 @@ services where applicable):
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
- [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work.
- - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB clusters](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB cluster](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - Note that [Amazon RDS Multi-AZ DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZSingleStandby.html) is a separate product and is supported.
- [Amazon Aurora](https://aws.amazon.com/rds/aurora/) is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440).
- Consul is primarily used for Omnibus PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However, Consul is also used optionally by Prometheus for Omnibus auto host discovery.
2. Can be optionally run on reputable third-party external PaaS Redis solutions. See [Recommended cloud providers and services](index.md#recommended-cloud-providers-and-services) for more information.
diff --git a/doc/administration/reference_architectures/index.md b/doc/administration/reference_architectures/index.md
index 467cc332e25..60258fb5a09 100644
--- a/doc/administration/reference_architectures/index.md
+++ b/doc/administration/reference_architectures/index.md
@@ -207,7 +207,8 @@ Several cloud provider services are known not to support the above or have been
- [Amazon Aurora](https://aws.amazon.com/rds/aurora/) is incompatible and not supported. See [14.4.0](../../update/index.md#1440) for more details.
- [Azure Database for PostgreSQL Single Server](https://azure.microsoft.com/en-gb/products/postgresql/#overview) (Single / Flexible) is **strongly not recommended** for use due to notable performance / stability issues or missing functionality. See [Recommendation Notes for Azure](#recommendation-notes-for-azure) for more details.
-- [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB clusters](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+- [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB cluster](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
+ - Note that [Amazon RDS Multi-AZ DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZSingleStandby.html) is a separate product and is supported.
### Recommendation notes for Azure
@@ -216,7 +217,7 @@ Due to performance issues that we found with several key Azure services, we only
In addition to the above, you should be aware of the additional specific guidance for Azure:
- **We outright strongly do not recommend [Azure Database for PostgreSQL Single Server](https://learn.microsoft.com/en-us/azure/postgresql/single-server/overview-single-server)** specifically due to significant performance and stability issues found. **For GitLab 14.0 and higher the service is not supported** due to it only supporting up to PostgreSQL 11.
- - A new service, [Azure Database for Postgres Flexible Server](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/) has been released but due to it missing some functionality we don't recommend it at this time.
+ - A new service, [Azure Database for PostgreSQL Flexible Server](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/) has been released but due to it missing some functionality we don't recommend it at this time.
- [Azure Blob Storage](https://azure.microsoft.com/en-gb/products/storage/blobs/) has been found to have performance limits that can impact production use at certain times. However, this has only been seen in larger architectures.
## Validation and test results
@@ -240,11 +241,11 @@ Testing occurs against all reference architectures and cloud providers in an aut
- The [GitLab Environment Toolkit](https://gitlab.com/gitlab-org/gitlab-environment-toolkit) for building the environments.
- The [GitLab Performance Tool](https://gitlab.com/gitlab-org/quality/performance) for performance testing.
-Network latency on the test environments between components on all Cloud Providers were measured at <5ms. Note that this is shared as an observation and not as an implicit recommendation.
+Network latency on the test environments between components on all Cloud Providers were measured at <5 ms. Note that this is shared as an observation and not as an implicit recommendation.
We aim to have a "test smart" approach where architectures tested have a good range that can also apply to others. Testing focuses on 10k Omnibus on GCP as the testing has shown this is a good bellwether for the other architectures and cloud providers as well as Cloud Native Hybrids.
-The Standard Reference Architectures are designed to be platform-agnostic, with everything being run on VMs via [Omnibus GitLab](https://docs.gitlab.com/omnibus/). While testing occurs primarily on GCP, ad-hoc testing has shown that they perform similarly on equivalently specced hardware on other Cloud Providers or if run on premises (bare-metal).
+The Standard Reference Architectures are designed to be platform-agnostic, with everything being run on VMs via [Omnibus GitLab](https://docs.gitlab.com/omnibus/). While testing occurs primarily on GCP, ad-hoc testing has shown that they perform similarly on hardware with equivalent specs on other Cloud Providers or if run on premises (bare-metal).
Testing on these reference architectures is performed with the
[GitLab Performance Tool](https://gitlab.com/gitlab-org/quality/performance)
diff --git a/doc/administration/restart_gitlab.md b/doc/administration/restart_gitlab.md
index 1a1194e16a9..7996db3d1e1 100644
--- a/doc/administration/restart_gitlab.md
+++ b/doc/administration/restart_gitlab.md
@@ -14,7 +14,7 @@ A short downtime is expected for all methods.
## Omnibus installations
-If you have used the [Omnibus packages](https://about.gitlab.com/install/) to install GitLab, then
+If you have used the [Omnibus packages](https://about.gitlab.com/install/) to install GitLab,
you should already have `gitlab-ctl` in your `PATH`.
`gitlab-ctl` interacts with the Omnibus packages and can be used to restart the
@@ -88,16 +88,14 @@ sudo gitlab-ctl reconfigure
Reconfiguring GitLab should occur in the event that something in its
configuration (`/etc/gitlab/gitlab.rb`) has changed.
-When you run this command, [Chef](https://www.chef.io/products/chef-infra), the underlying configuration management
-application that powers Omnibus GitLab, makes sure that all things like directories,
-permissions, and services are in place and in the same shape that they were
-initially shipped.
+When you run `gitlab-ctl reconfigure`, [Chef](https://www.chef.io/products/chef-infra),
+the underlying configuration management application that powers Omnibus GitLab, runs some checks.
+Chef ensures directories, permissions, and services are in place and working.
-It also [restarts GitLab components](#how-to-restart-gitlab)
-where needed, if any of their configuration files have changed.
+Chef also [restarts GitLab components](#how-to-restart-gitlab) if any of their configuration files have changed.
If you manually edit any files in `/var/opt/gitlab` that are managed by Chef,
-running reconfigure reverts the changes and restarts the services that
+running `reconfigure` reverts the changes and restarts the services that
depend on those files.
## Installations from source
@@ -118,7 +116,7 @@ This should restart Puma, Sidekiq, GitLab Workhorse, and [Mailroom](reply_by_ema
## Helm chart installations
-There is no single command to restart the entire GitLab application installed via
+There is no single command to restart the entire GitLab application installed through
the [cloud-native Helm chart](https://docs.gitlab.com/charts/). Usually, it should be
enough to restart a specific component separately (for example, `gitaly`, `puma`,
`workhorse`, or `gitlab-shell`) by deleting all the pods related to it:
diff --git a/doc/administration/server_hooks.md b/doc/administration/server_hooks.md
index 448becb32dc..3d4f39b5ff0 100644
--- a/doc/administration/server_hooks.md
+++ b/doc/administration/server_hooks.md
@@ -47,15 +47,30 @@ To create server hooks for a repository:
`pre-receive` server hook, the filename should be `pre-receive` with no extension.
- To create many server hooks, create a directory for the hooks that matches the hook type. For example, for a
`pre-receive` server hook, the directory name should be `pre-receive.d`. Put the files for the hook in that directory.
-1. Make the server hook files executable and ensure that they are owned by the Git user.
+1. **Make the server hook files executable** and ensure that they are owned by the Git user.
1. Write the code to make the server hook function as expected. Git server hooks can be in any programming language. Ensure
the [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) at the top reflects the language type. For
example, if the script is in Ruby the shebang is probably `#!/usr/bin/env ruby`.
-1. Make the hook file executable, ensure that it's owned by the Git user, and ensure it does not match the backup file
+1. Ensure the hook file does not match the backup file
pattern (`*~`).
If the server hook code is properly implemented, it should execute when the Git hook is next triggered.
+### Gitaly Cluster
+
+If you use [Gitaly Cluster](gitaly/index.md), the scripts must be copied to every Gitaly node that has a replica of the repository. Every Gitaly node
+needs a copy because any node can be made a primary at any time. Server hooks only run on primary nodes.
+
+The location to copy the scripts to depends on where repositories are stored:
+
+- In GitLab 15.2 and earlier, Gitaly Cluster uses the [hashed storage path](repository_storage_types.md#hashed-storage)
+ reported by the GitLab application.
+- In GitLab 15.3 and later, new repositories are created using
+ [Praefect-generated replica paths](gitaly/index.md#praefect-generated-replica-paths-gitlab-150-and-later),
+ which are not the hashed storage path. The replica path can be identified by
+ [querying the Praefect repository metadata](../administration/gitaly/troubleshooting.md#view-repository-metadata)
+ using `-relative-path` to specify the expected GitLab hashed storage path.
+
## Create global server hooks for all repositories
To create a Git hook that applies to all repositories, set a global server hook. Global server hooks also apply to:
diff --git a/doc/administration/sidekiq.md b/doc/administration/sidekiq.md
deleted file mode 100644
index 01f83f98607..00000000000
--- a/doc/administration/sidekiq.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'sidekiq/index.md'
-remove_date: '2022-11-11'
----
-
-This document was moved to [another location](sidekiq/index.md).
-
-<!-- This redirect file can be deleted after <2022-11-11>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/administration/sidekiq/extra_sidekiq_processes.md b/doc/administration/sidekiq/extra_sidekiq_processes.md
index feaaa55aa59..d5007e9a3e9 100644
--- a/doc/administration/sidekiq/extra_sidekiq_processes.md
+++ b/doc/administration/sidekiq/extra_sidekiq_processes.md
@@ -6,91 +6,41 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Run multiple Sidekiq processes **(FREE SELF)**
-GitLab allows you to start multiple Sidekiq processes.
-These processes can be used to consume a dedicated set
-of queues. This can be used to ensure certain queues always have dedicated
-workers, no matter the number of jobs to be processed.
+GitLab allows you to start multiple Sidekiq processes to process background jobs
+at a higher rate on a single instance. By default, Sidekiq starts one worker
+process and only uses a single core.
NOTE:
The information in this page applies only to Omnibus GitLab.
-## Available Sidekiq queues
-
-For a list of the existing Sidekiq queues, check the following files:
-
-- [Queues for both GitLab Community and Enterprise Editions](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/workers/all_queues.yml)
-- [Queues for GitLab Enterprise Editions only](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/workers/all_queues.yml)
-
-Each entry in the above files represents a queue on which Sidekiq processes
-can be started.
-
## Start multiple processes
> - [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/4006) in GitLab 12.10, starting multiple processes with Sidekiq cluster.
> - [Sidekiq cluster moved](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/181) to GitLab Free in 12.10.
> - [Sidekiq cluster became default](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/4140) in GitLab 13.0.
-When starting multiple processes, the number of processes should
-equal (and **not** exceed) the number of CPU cores you want to
-dedicate to Sidekiq. Each Sidekiq process can use only 1 CPU
-core, subject to the available workload and concurrency settings.
+When starting multiple processes, the number of processes should at most equal
+(and **not** exceed) the number of CPU cores you want to dedicate to Sidekiq.
+The Sidekiq worker process uses no more than one CPU core.
-To start multiple processes:
+To start multiple processes, use the `sidekiq['queue_groups']` array setting to
+specify how many processes to create using `sidekiq-cluster` and which queues
+they should handle. Each item in the array equates to one additional Sidekiq
+process, and values in each item determine the queues it works on. In the vast
+majority of cases, all processes should listen to all queues (see
+[processing specific job classes](processing_specific_job_classes.md) for more
+details).
-1. Using the `sidekiq['queue_groups']` array setting, specify how many processes to
- create using `sidekiq-cluster` and which queue they should handle.
- Each item in the array equates to one additional Sidekiq
- process, and values in each item determine the queues it works on.
+For example, to create four Sidekiq processes, each listening
+to all available queues:
- For example, the following setting creates three Sidekiq processes, one to run on
- `elastic_commit_indexer`, one to run on `mailers`, and one process running on all queues:
+1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
- sidekiq['queue_groups'] = [
- "elastic_commit_indexer",
- "mailers",
- "*"
- ]
+ sidekiq['queue_groups'] = ['*'] * 4
```
- To have an additional Sidekiq process handle multiple queues, add multiple
- queue names to its item delimited by commas. For example:
-
- ```ruby
- sidekiq['queue_groups'] = [
- "elastic_commit_indexer, elastic_association_indexer",
- "mailers",
- "*"
- ]
- ```
-
- [In GitLab 12.9](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26594) and
- later, the special queue name `*` means all queues. This starts two
- processes, each handling all queues:
-
- ```ruby
- sidekiq['queue_groups'] = [
- "*",
- "*"
- ]
- ```
-
- `*` which matches all workers.
- As a result, the wildcard query must stay at the end of the list or the rules after it are ignored.
-
- `*` cannot be combined with concrete queue names - `*, mailers`
- just handles the `mailers` queue.
-
- When `sidekiq-cluster` is only running on a single node, make sure that at least
- one process is running on all queues using `*`. This ensures a process
- automatically picks up jobs in queues created in the future,
- including queues that have dedicated processes.
-
- If `sidekiq-cluster` is running on more than one node, you can also use
- [`--negate`](#negate-settings) and list all the queues that are already being
- processed.
-
-1. Save the file and reconfigure GitLab for the changes to take effect:
+1. Save the file and reconfigure GitLab:
```shell
sudo gitlab-ctl reconfigure
@@ -101,125 +51,38 @@ To view the Sidekiq processes in GitLab:
1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
-## Negate settings
+## Concurrency
-To have the Sidekiq process work on every queue **except** the ones
-you list. In this example, we exclude all import-related jobs from a Sidekiq node:
+By default each process defined under `sidekiq` starts with a number of threads
+that equals the number of queues, plus one spare thread, up to a maximum of 50.
+For example, a process that handles all queues will use 50 threads by default.
-1. Edit `/etc/gitlab/gitlab.rb` and add:
-
- ```ruby
- sidekiq['negate'] = true
- sidekiq['queue_selector'] = true
- sidekiq['queue_groups'] = [
- "feature_category=importers"
- ]
- ```
-
-1. Save the file and reconfigure GitLab for the changes to take effect:
-
- ```shell
- sudo gitlab-ctl reconfigure
- ```
-
-## Queue selector
-
-> - [Introduced](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/45) in GitLab 12.8.
-> - [Sidekiq cluster, including queue selector, moved](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/181) to GitLab Free in 12.10.
-> - [Renamed from `experimental_queue_selector` to `queue_selector`](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/147) in GitLab 13.6.
-
-In addition to selecting queues by name, as above, the `queue_selector` option
-allows queue groups to be selected in a more general way using a
-[worker matching query](extra_sidekiq_routing.md#worker-matching-query). After `queue_selector`
-is set, all `queue_groups` must follow the aforementioned syntax.
-
-In `/etc/gitlab/gitlab.rb`:
-
-```ruby
-sidekiq['enable'] = true
-sidekiq['queue_selector'] = true
-sidekiq['queue_groups'] = [
- # Run all non-CPU-bound queues that are high urgency
- 'resource_boundary!=cpu&urgency=high',
- # Run all continuous integration and pages queues that are not high urgency
- 'feature_category=continuous_integration,pages&urgency!=high',
- # Run all queues
- '*'
-]
-```
-
-## Ignore all import queues
-
-When [importing from GitHub](../../user/project/import/github.md) or
-other sources, Sidekiq might use all of its resources to perform those
-operations. To set up two separate `sidekiq-cluster` processes, where
-one only processes imports and the other processes all other queues:
-
-1. Edit `/etc/gitlab/gitlab.rb` and add:
-
- ```ruby
- sidekiq['enable'] = true
- sidekiq['queue_selector'] = true
- sidekiq['queue_groups'] = [
- "feature_category=importers",
- "feature_category!=importers"
- ]
- ```
-
-1. Save the file and reconfigure GitLab for the changes to take effect:
-
- ```shell
- sudo gitlab-ctl reconfigure
- ```
-
-## Number of threads
-
-By default each process defined under `sidekiq` starts with a
-number of threads that equals the number of queues, plus one spare thread.
-For example, a process that handles the `process_commit` and `post_receive`
-queues uses three threads in total.
-
-These thread run inside a single Ruby process, and each process
-can only use a single CPU core. The usefulness of threading depends
-on the work having some external dependencies to wait on, like database queries or
-HTTP requests. Most Sidekiq deployments benefit from this threading, and when
-running fewer queues in a process, increasing the thread count might be
-even more desirable to make the most effective use of CPU resources.
+These threads run inside a single Ruby process, and each process can only use a
+single CPU core. The usefulness of threading depends on the work having some
+external dependencies to wait on, like database queries or HTTP requests. Most
+Sidekiq deployments benefit from this threading.
### Manage thread counts explicitly
-The correct maximum thread count (also called concurrency) depends on the workload.
-Typical values range from `1` for highly CPU-bound tasks to `15` or higher for mixed
-low-priority work. A reasonable starting range is `15` to `25` for a non-specialized
-deployment.
+The correct maximum thread count (also called concurrency) depends on the
+workload. Typical values range from `5` for highly CPU-bound tasks to `15` or
+higher for mixed low-priority work. A reasonable starting range is `15` to `25`
+for a non-specialized deployment.
-You can find example values used by GitLab.com by searching for `concurrency:` in
-[the Helm charts](https://gitlab.com/gitlab-com/gl-infra/k8s-workloads/gitlab-com/-/blob/master/releases/gitlab/values/gprd.yaml.gotmpl).
-The values vary according to the work each specific deployment of Sidekiq does.
-Any other specialized deployments with processes dedicated to specific queues should
-have the concurrency tuned according to:
-have the concurrency tuned according to:
+We only recommend setting explicit concurrency by setting `min_concurrency` and
+`max_concurrency` to the same value. The two values are kept for backwards
+compatibility reasons, but for more predictable results, use the same value.
-- The CPU usage of each type of process.
-- The throughput achieved.
-
-Each thread requires a Redis connection, so adding threads may increase Redis
-latency and potentially cause client timeouts. See the
-[Sidekiq documentation about Redis](https://github.com/mperham/sidekiq/wiki/Using-Redis) for more
-details.
-
-#### When running Sidekiq cluster (default)
+For example, to set the concurrency to `20`:
-Running Sidekiq cluster is the default in GitLab 13.0 and later.
-
-1. Edit `/etc/gitlab/gitlab.rb` and add:
+1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
- sidekiq['min_concurrency'] = 15
- sidekiq['max_concurrency'] = 25
+ sidekiq['min_concurrency'] = 20
+ sidekiq['max_concurrency'] = 20
```
-1. Save the file and reconfigure GitLab for the changes to take effect:
+1. Save the file and reconfigure GitLab:
```shell
sudo gitlab-ctl reconfigure
@@ -231,50 +94,45 @@ the other. Setting `min_concurrency` to `0` disables the limit.
For each queue group, let `N` be one more than the number of queues. The
concurrency is set to:
+1. `min_concurrency`, if it's equal to `max_concurrency`.
1. `N`, if it's between `min_concurrency` and `max_concurrency`.
1. `max_concurrency`, if `N` exceeds this value.
1. `min_concurrency`, if `N` is less than this value.
-If `min_concurrency` is equal to `max_concurrency`, then this value is used
-regardless of the number of queues.
-
When `min_concurrency` is greater than `max_concurrency`, it is treated as
being equal to `max_concurrency`.
-#### When running a single Sidekiq process
-
-Running a single Sidekiq process is the default in GitLab 12.10 and earlier.
-
-WARNING:
-Running Sidekiq directly was removed in GitLab
-[14.0](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/240).
-
-1. Edit `/etc/gitlab/gitlab.rb` and add:
-
- ```ruby
- sidekiq['cluster'] = false
- sidekiq['concurrency'] = 25
- ```
-
-1. Save the file and reconfigure GitLab for the changes to take effect:
+You can find example values used by GitLab.com by searching for `concurrency:`
+in [the Helm charts](https://gitlab.com/gitlab-com/gl-infra/k8s-workloads/gitlab-com/-/blob/master/releases/gitlab/values/gprd.yaml.gotmpl).
+The values vary according to the work each specific deployment of Sidekiq does.
+Any other specialized deployments with processes dedicated to specific queues
+should have the concurrency tuned according to:
- ```shell
- sudo gitlab-ctl reconfigure
- ```
+- The CPU usage of each type of process.
+- The throughput achieved.
-This sets the concurrency (number of threads) for the Sidekiq process.
+Each thread requires a Redis connection, so adding threads may increase Redis
+latency and potentially cause client timeouts. See the [Sidekiq documentation about Redis](https://github.com/mperham/sidekiq/wiki/Using-Redis)
+for more details.
## Modify the check interval
-To modify `sidekiq-cluster`'s health check interval for the additional Sidekiq processes:
+To modify Sidekiq's health check interval for the additional Sidekiq
+processes:
-1. Edit `/etc/gitlab/gitlab.rb` and add (the value can be any integer number of seconds):
+1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
sidekiq['interval'] = 5
```
-1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+ The value can be any integer number of seconds.
+
+1. Save the file and reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
## Troubleshoot using the CLI
@@ -291,6 +149,9 @@ takes arguments using the following syntax:
/opt/gitlab/embedded/service/gitlab-rails/bin/sidekiq-cluster [QUEUE,QUEUE,...] [QUEUE, ...]
```
+The `--dryrun` argument allows viewing the command to be executed without
+actually starting it.
+
Each separate argument denotes a group of queues that have to be processed by a
Sidekiq process. Multiple queues can be processed by the same process by
separating them with a comma instead of a space.
@@ -301,29 +162,6 @@ explicitly list all the queue names. For more information about queue namespaces
see the relevant section in the
[Sidekiq development documentation](../../development/sidekiq/index.md#queue-namespaces).
-For example, say you want to start 2 extra processes: one to process the
-`process_commit` queue, and one to process the `post_receive` queue. This can be
-done as follows:
-
-```shell
-/opt/gitlab/embedded/service/gitlab-rails/bin/sidekiq-cluster process_commit post_receive
-```
-
-If you instead want to start one process processing both queues, you'd use the
-following syntax:
-
-```shell
-/opt/gitlab/embedded/service/gitlab-rails/bin/sidekiq-cluster process_commit,post_receive
-```
-
-If you want to have one Sidekiq process dealing with the `process_commit` and
-`post_receive` queues, and one process to process the `gitlab_shell` queue,
-you'd use the following:
-
-```shell
-/opt/gitlab/embedded/service/gitlab-rails/bin/sidekiq-cluster process_commit,post_receive gitlab_shell
-```
-
### Monitor the `sidekiq-cluster` command
The `sidekiq-cluster` command does not terminate once it has started the desired
diff --git a/doc/administration/sidekiq/extra_sidekiq_routing.md b/doc/administration/sidekiq/extra_sidekiq_routing.md
index 56c51beb758..d1d65498fcc 100644
--- a/doc/administration/sidekiq/extra_sidekiq_routing.md
+++ b/doc/administration/sidekiq/extra_sidekiq_routing.md
@@ -1,163 +1,11 @@
---
-stage: Systems
-group: Distribution
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+redirect_to: 'processing_specific_job_classes.md#routing-rules'
+remove_date: '2023-02-01'
---
-# Queue routing rules **(FREE SELF)**
+This document was moved to [another location](processing_specific_job_classes.md#routing-rules).
-When the number of Sidekiq jobs increases to a certain scale, the system faces
-some scalability issues. One of them is that the length of the queue tends to get
-longer. High-urgency jobs have to wait longer until other less urgent jobs
-finish. This head-of-line blocking situation may eventually affect the
-responsiveness of the system, especially critical actions. In another scenario,
-the performance of some jobs is degraded due to other long running or CPU-intensive jobs
-(computing or rendering ones) in the same machine.
-
-To counter the aforementioned issues, one effective solution is to split
-Sidekiq jobs into different queues and assign machines handling each queue
-exclusively. For example, all CPU-intensive jobs could be routed to the
-`cpu-bound` queue and handled by a fleet of CPU optimized instances. The queue
-topology differs between companies depending on the workloads and usage
-patterns. Therefore, GitLab supports a flexible mechanism for the
-administrator to route the jobs based on their characteristics.
-
-As an alternative to [Queue selector](extra_sidekiq_processes.md#queue-selector), which
-configures Sidekiq cluster to listen to a specific set of workers or queues,
-GitLab also supports routing a job from a worker to the desired queue when it
-is scheduled. Sidekiq clients try to match a job against a configured list of
-routing rules. Rules are evaluated from first to last, and as soon as we find a
-match for a given worker we stop processing for that worker (first match wins).
-If the worker doesn't match any rule, it falls back to the queue name generated
-from the worker name.
-
-By default, if the routing rules are not configured (or denoted with an empty
-array), all the jobs are routed to the queue generated from the worker name.
-
-## Example configuration
-
-In `/etc/gitlab/gitlab.rb`:
-
-```ruby
-sidekiq['routing_rules'] = [
- # Do not re-route workers that require their own queue
- ['tags=needs_own_queue', nil],
- # Route all non-CPU-bound workers that are high urgency to `high-urgency` queue
- ['resource_boundary!=cpu&urgency=high', 'high-urgency'],
- # Route all database, gitaly and global search workers that are throttled to `throttled` queue
- ['feature_category=database,gitaly,global_search&urgency=throttled', 'throttled'],
- # Route all workers having contact with outside work to a `network-intenstive` queue
- ['has_external_dependencies=true|feature_category=hooks|tags=network', 'network-intensive'],
- # Route all import workers to the queues generated by the worker name, for
- # example, JiraImportWorker to `jira_import`, SVNWorker to `svn_worker`
- ['feature_category=import', nil],
- # Wildcard matching, route the rest to `default` queue
- ['*', 'default']
-]
-```
-
-The routing rules list is an order-matter array of tuples of query and
-corresponding queue:
-
-- The query is following a [worker matching query](#worker-matching-query) syntax.
-- The `<queue_name>` must be a valid Sidekiq queue name. If the queue name
- is `nil`, or an empty string, the worker is routed to the queue generated
- by the name of the worker instead.
-
-The query supports wildcard matching `*`, which matches all workers. As a
-result, the wildcard query must stay at the end of the list or the rules after it
-are ignored.
-
-NOTE:
-Mixing queue routing rules and queue selectors requires care to
-ensure all jobs that are scheduled and picked up by appropriate Sidekiq
-workers.
-
-## Worker matching query
-
-GitLab provides a query syntax to match a worker based on its
-attributes. This query syntax is employed by both
-[Queue routing rules](#queue-routing-rules) and
-[Queue selector](extra_sidekiq_processes.md#queue-selector). A query includes two
-components:
-
-- Attributes that can be selected.
-- Operators used to construct a query.
-
-### Available attributes
-
-> [Introduced](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/261) in GitLab 13.1 (`tags`).
-
-Queue matching query works upon the worker attributes, described in
-[Sidekiq style guide](../../development/sidekiq/index.md). We support querying
-based on a subset of worker attributes:
-
-- `feature_category` - the
- [GitLab feature category](https://about.gitlab.com/direction/maturity/#category-maturity) the
- queue belongs to. For example, the `merge` queue belongs to the
- `source_code_management` category.
-- `has_external_dependencies` - whether or not the queue connects to external
- services. For example, all importers have this set to `true`.
-- `urgency` - how important it is that this queue's jobs run
- quickly. Can be `high`, `low`, or `throttled`. For example, the
- `authorized_projects` queue is used to refresh user permissions, and
- is `high` urgency.
-- `worker_name` - the worker name. Use this attribute to select a specific worker.
-- `name` - the queue name generated from the worker name. Use this attribute to select a specific queue. Because this is generated from
- the worker name, it does not change based on the result of other routing
- rules.
-- `resource_boundary` - if the queue is bound by `cpu`, `memory`, or
- `unknown`. For example, the `ProjectExportWorker` is memory bound as it has
- to load data in memory before saving it for export.
-- `tags` - short-lived annotations for queues. These are expected to frequently
- change from release to release, and may be removed entirely.
-
-`has_external_dependencies` is a boolean attribute: only the exact
-string `true` is considered true, and everything else is considered
-false.
-
-`tags` is a set, which means that `=` checks for intersecting sets, and
-`!=` checks for disjoint sets. For example, `tags=a,b` selects queues
-that have tags `a`, `b`, or both. `tags!=a,b` selects queues that have
-neither of those tags.
-
-The attributes of each worker are hard-coded in the source code. For
-convenience, we generate a
-[list of all available attributes in GitLab Community Edition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/workers/all_queues.yml)
-and a
-[list of all available attributes in GitLab Enterprise Edition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/workers/all_queues.yml).
-
-### Available operators
-
-`queue_selector` supports the following operators, listed from highest
-to lowest precedence:
-
-- `|` - the logical `OR` operator. For example, `query_a|query_b` (where `query_a`
- and `query_b` are queries made up of the other operators here) includes
- queues that match either query.
-- `&` - the logical `AND` operator. For example, `query_a&query_b` (where
- `query_a` and `query_b` are queries made up of the other operators here) will
- only include queues that match both queries.
-- `!=` - the `NOT IN` operator. For example, `feature_category!=issue_tracking`
- excludes all queues from the `issue_tracking` feature category.
-- `=` - the `IN` operator. For example, `resource_boundary=cpu` includes all
- queues that are CPU bound.
-- `,` - the concatenate set operator. For example,
- `feature_category=continuous_integration,pages` includes all queues from
- either the `continuous_integration` category or the `pages` category. This
- example is also possible using the OR operator, but allows greater brevity, as
- well as being lower precedence.
-
-The operator precedence for this syntax is fixed: it's not possible to make `AND`
-have higher precedence than `OR`.
-
-[In GitLab 12.9](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26594) and
-later, as with the standard queue group syntax above, a single `*` as the
-entire queue group selects all queues.
-
-### Migration
-
-After the Sidekiq routing rules are changed, administrators must take care
-with the migration to avoid losing jobs entirely, especially in a system with
-long queues of jobs. The migration can be done by following the migration steps
-mentioned in [Sidekiq job migration](sidekiq_job_migration.md)
+<!-- This redirect file can be deleted after <2023-02-01>. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/administration/sidekiq/index.md b/doc/administration/sidekiq/index.md
index f17c248e60e..7b3ecdd0890 100644
--- a/doc/administration/sidekiq/index.md
+++ b/doc/administration/sidekiq/index.md
@@ -379,11 +379,11 @@ To enable LDAP with the synchronization worker for Sidekiq:
## Configure SAML Groups for SAML Group Sync
-If you use [SAML Group Sync](../../user/group/saml_sso/group_sync.md), you must configure [SAML Groups](../../integration/saml.md#saml-groups) on all your Sidekiq nodes.
+If you use [SAML Group Sync](../../user/group/saml_sso/group_sync.md), you must configure [SAML Groups](../../integration/saml.md#configure-users-based-on-saml-group-membership) on all your Sidekiq nodes.
## Disable Rugged
-Calls into Rugged, Ruby bindings for `libgit2`, [lock the Sidekiq processes's GVL](https://silverhammermba.github.io/emberb/c/#c-in-ruby-threads),
+Calls into Rugged, Ruby bindings for `libgit2`, [lock the Sidekiq processes (GVL)](https://silverhammermba.github.io/emberb/c/#c-in-ruby-threads),
blocking all jobs on that worker from proceeding. If Rugged calls performed by Sidekiq are slow, this can cause significant delays in
background task processing.
@@ -398,7 +398,7 @@ sudo gitlab-rake gitlab:features:disable_rugged
## Related topics
- [Extra Sidekiq processes](extra_sidekiq_processes.md)
-- [Extra Sidekiq routing](extra_sidekiq_routing.md)
+- [Processing specific job classes](processing_specific_job_classes.md)
- [Sidekiq health checks](sidekiq_health_check.md)
- [Using the GitLab-Sidekiq chart](https://docs.gitlab.com/charts/charts/gitlab/sidekiq/)
diff --git a/doc/administration/sidekiq/processing_specific_job_classes.md b/doc/administration/sidekiq/processing_specific_job_classes.md
new file mode 100644
index 00000000000..080ad7d4eae
--- /dev/null
+++ b/doc/administration/sidekiq/processing_specific_job_classes.md
@@ -0,0 +1,337 @@
+---
+stage: Systems
+group: Distribution
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Processing specific job classes
+
+WARNING:
+These are advanced settings. While they are used on GitLab.com, most GitLab
+instances should add more processes that all listen to all queues. This is the
+same approach we take in our [Reference Architectures](../reference_architectures/index.md).
+
+GitLab has two options for creating Sidekiq processes that only handle specific
+job classes:
+
+1. [Routing rules](#routing-rules) are used on GitLab.com. They direct jobs
+ inside the application to queue names configured by administrators. This
+ lowers the load on Redis, which is important on very large-scale deployments.
+1. [Queue selectors](#queue-selectors) perform the job selection outside the
+ application, when starting the Sidekiq process. This was used on GitLab.com
+ until September 2021, and is retained for compatibility reasons.
+
+Both of these use the same [worker matching query](#worker-matching-query)
+syntax. While they can technically be used together, most deployments should
+choose one or the other; there is no particular benefit in combining them.
+
+Routing rules must be the same across all GitLab nodes as they are part of the
+application configuration. Queue selectors can be different across GitLab nodes
+because they only change the arguments to the launched Sidekiq process.
+
+## Routing rules
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59604) in GitLab 13.12.
+> - [Default routing rule value](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97908) added in GitLab 15.4.
+
+NOTE:
+Mailer jobs cannot be routed by routing rules, and always go to the
+`mailers` queue. When using routing rules, ensure that at least one process is
+listening to the `mailers` queue. Typically this can be placed alongside the
+`default` queue.
+
+We recommend most GitLab instances using routing rules to manage their Sidekiq
+queues. This allows administrators to choose single queue names for groups of
+job classes based on their attributes. The syntax is an ordered array of pairs of `[query, queue]`:
+
+1. The query is a [worker matching query](#worker-matching-query).
+1. The queue name must be a valid Sidekiq queue name. If the queue name
+ is `nil`, or an empty string, the worker is routed to the queue generated
+ by the name of the worker instead. (See [list of available job classes](#list-of-available-job-classes)
+ for more information).
+ The queue name does not have to match any existing queue name in the
+ list of available job classes.
+1. The first query matching a worker is chosen for that worker; later rules are
+ ignored.
+
+### Routing rules migration
+
+After the Sidekiq routing rules are changed, administrators must take care with
+the migration to avoid losing jobs entirely, especially in a system with long
+queues of jobs. The migration can be done by following the migration steps
+mentioned in [Sidekiq job migration](sidekiq_job_migration.md).
+
+### Detailed example
+
+This is a comprehensive example intended to show different possibilities. It is
+not a recommendation.
+
+1. Edit `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ sidekiq['routing_rules'] = [
+ # Route all non-CPU-bound workers that are high urgency to `high-urgency` queue
+ ['resource_boundary!=cpu&urgency=high', 'high-urgency'],
+ # Route all database, gitaly and global search workers that are throttled to `throttled` queue
+ ['feature_category=database,gitaly,global_search&urgency=throttled', 'throttled'],
+ # Route all workers having contact with outside world to a `network-intenstive` queue
+ ['has_external_dependencies=true|feature_category=hooks|tags=network', 'network-intensive'],
+ # Route all import workers to the queues generated by the worker name, for
+ # example, JiraImportWorker to `jira_import`, SVNWorker to `svn_worker`
+ ['feature_category=import', 'import'],
+ # Wildcard matching, route the rest to `default` queue
+ ['*', 'default']
+ ]
+ ```
+
+ The `queue_groups` can then be set to match these generated queue names. For
+ instance:
+
+ ```ruby
+ sidekiq['queue_selector'] = false
+ sidekiq['queue_groups'] = [
+ # Run two high-urgency processes
+ 'high-urgency',
+ 'high-urgency',
+ # Run one process for throttled, network-intensive, import
+ 'throttled,network-intensive,import',
+ # Run one 'catchall' process on the default and mailers queues
+ 'default,mailers'
+ ]
+ ```
+
+1. Save the file and reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+## Queue selectors
+
+> - [Introduced](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/45) in GitLab 12.8.
+> - [Sidekiq cluster, including queue selector, moved](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/181) to GitLab Free in 12.10.
+> - [Renamed from `experimental_queue_selector` to `queue_selector`](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/147) in GitLab 13.6.
+
+The `queue_selector` option allows queue groups to be selected in a more general
+way using a [worker matching query](#worker-matching-query). After
+`queue_selector` is set, all `queue_groups` must follow the aforementioned
+syntax.
+
+### Using queue selectors
+
+1. Edit `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ sidekiq['enable'] = true
+ sidekiq['routing_rules'] = [['*', nil]]
+ sidekiq['queue_selector'] = true
+ sidekiq['queue_groups'] = [
+ # Run all non-CPU-bound queues that are high urgency
+ 'resource_boundary!=cpu&urgency=high',
+ # Run all continuous integration and pages queues that are not high urgency
+ 'feature_category=continuous_integration,pages&urgency!=high',
+ # Run all queues
+ '*'
+ ]
+ ```
+
+1. Save the file and reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+### Negate settings
+
+This allows you to have the Sidekiq process work on every queue **except** the
+ones you list. This is generally only used when there are multiple Sidekiq
+nodes. In this example, we exclude all import-related jobs from a Sidekiq node.
+
+1. Edit `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ sidekiq['routing_rules'] = [['*', nil]]
+ sidekiq['negate'] = true
+ sidekiq['queue_selector'] = true
+ sidekiq['queue_groups'] = [
+ "feature_category=importers"
+ ]
+ ```
+
+1. Save the file and reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+### Migrating from queue selectors to routing rules
+
+We recommend GitLab deployments add more Sidekiq processes listening to all queues, as in the
+[Reference Architectures](../reference_architectures/index.md). For very large-scale deployments, we recommend
+[routing rules](#routing-rules) instead of [queue selectors](#queue-selectors). We use routing rules on GitLab.com as
+it helps to lower the load on Redis.
+
+To migrate from queue selectors to routing rules:
+
+1. Open `/etc/gitlab/gitlab.rb`.
+1. Set `sidekiq['queue_selector']` to `false`.
+1. Take all queue `selector`s in the `sidekiq['queue_groups']`.
+1. Give each `selector` a `queue_name` and put them in `[selector, queue_name]` format.
+1. Replace `sidekiq['routing_rules']` with an array of `[selector, queue_name]` entries.
+1. Add a wildcard match of `['*', 'default']` as the last entry in `sidekiq['routing_rules']`. This "catchall" queue has
+ to be named as `default`.
+1. Replace `sidekiq['queue_groups']` with `queue_name`s.
+1. Add at least one `default` queue and at least one `mailers` queue to the `sidekiq['queue_groups']`.
+1. Save the file and reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+1. Run the Rake task to [migrate existing jobs](sidekiq_job_migration.md):
+
+ ```shell
+ sudo gitlab-rake gitlab:sidekiq:migrate_jobs:retry gitlab:sidekiq:migrate_jobs:schedule gitlab:sidekiq:migrate_jobs:queued
+ ```
+
+NOTE:
+It is important to run the Rake task immediately after reconfiguring GitLab.
+After reconfiguring GitLab, existing jobs are not processed until the Rake task starts to migrate the jobs.
+
+The following example better illustrates the migration process above:
+
+1. Check the following content of `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ sidekiq['routing_rules'] = []
+ sidekiq['queue_selector'] = true
+ sidekiq['queue_groups'] = [
+ 'urgency=high',
+ 'urgency=low',
+ 'urgency=throttled',
+ '*'
+ ]
+ ```
+
+1. Update `/etc/gitlab/gitlab.rb` to use routing rules:
+
+ ```ruby
+ sidekiq['min_concurrency'] = 20
+ sidekiq['max_concurrency'] = 20
+
+ sidekiq['routing_rules'] = [
+ ['urgency=high', 'high_urgency'],
+ ['urgency=low', 'low_urgency'],
+ ['urgency=throttled', 'throttled_urgency'],
+ # Wildcard matching, route the rest to `default` queue
+ ['*', 'default']
+ ]
+
+ sidekiq['queue_selector'] = false
+ sidekiq['queue_groups'] = [
+ 'high_urgency',
+ 'low_urgency',
+ 'throttled_urgency',
+ 'default,mailers'
+ ]
+ ```
+
+1. Save the file and reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+1. Run the Rake task to [migrate existing jobs](sidekiq_job_migration.md):
+
+ ```shell
+ sudo gitlab-rake gitlab:sidekiq:migrate_jobs:retry gitlab:sidekiq:migrate_jobs:schedule gitlab:sidekiq:migrate_jobs:queued
+ ```
+
+WARNING:
+As described in [the concurrency section](extra_sidekiq_processes.md#manage-thread-counts-explicitly), we
+recommend setting `min_concurrency` and `max_concurrency` to the same value. For example, if the number of queues
+in a queue group entry is 1, while `min_concurrency` is set to `0`, and `max_concurrency` is set to `20`, the resulting
+concurrency will be set to `2` instead. A concurrency of `2` might be too low in most cases, except for very highly-CPU
+bound tasks.
+
+## Worker matching query
+
+GitLab provides a query syntax to match a worker based on its attributes. This
+query syntax is employed by both [routing rules](#routing-rules) and
+[queue selectors](#queue-selectors). A query includes two components:
+
+- Attributes that can be selected.
+- Operators used to construct a query.
+
+### Available attributes
+
+> [Introduced](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/261) in GitLab 13.1 (`tags`).
+
+Queue matching query works upon the worker attributes, described in
+[Sidekiq style guide](../../development/sidekiq/index.md). We support querying
+based on a subset of worker attributes:
+
+- `feature_category` - the
+ [GitLab feature category](https://about.gitlab.com/direction/maturity/#category-maturity) the
+ queue belongs to. For example, the `merge` queue belongs to the
+ `source_code_management` category.
+- `has_external_dependencies` - whether or not the queue connects to external
+ services. For example, all importers have this set to `true`.
+- `urgency` - how important it is that this queue's jobs run
+ quickly. Can be `high`, `low`, or `throttled`. For example, the
+ `authorized_projects` queue is used to refresh user permissions, and
+ is `high` urgency.
+- `worker_name` - the worker name. Use this attribute to select a specific worker. Find all available names in [the job classes lists](#list-of-available-job-classes) below.
+- `name` - the queue name generated from the worker name. Use this attribute to select a specific queue. Because this is generated from
+ the worker name, it does not change based on the result of other routing
+ rules.
+- `resource_boundary` - if the queue is bound by `cpu`, `memory`, or
+ `unknown`. For example, the `ProjectExportWorker` is memory bound as it has
+ to load data in memory before saving it for export.
+- `tags` - short-lived annotations for queues. These are expected to frequently
+ change from release to release, and may be removed entirely.
+
+`has_external_dependencies` is a boolean attribute: only the exact
+string `true` is considered true, and everything else is considered
+false.
+
+`tags` is a set, which means that `=` checks for intersecting sets, and
+`!=` checks for disjoint sets. For example, `tags=a,b` selects queues
+that have tags `a`, `b`, or both. `tags!=a,b` selects queues that have
+neither of those tags.
+
+### Available operators
+
+Routing rules and queue selectors support the following operators, listed from
+highest to lowest precedence:
+
+- `|` - the logical `OR` operator. For example, `query_a|query_b` (where `query_a`
+ and `query_b` are queries made up of the other operators here) includes
+ queues that match either query.
+- `&` - the logical `AND` operator. For example, `query_a&query_b` (where
+ `query_a` and `query_b` are queries made up of the other operators here) will
+ only include queues that match both queries.
+- `!=` - the `NOT IN` operator. For example, `feature_category!=issue_tracking`
+ excludes all queues from the `issue_tracking` feature category.
+- `=` - the `IN` operator. For example, `resource_boundary=cpu` includes all
+ queues that are CPU bound.
+- `,` - the concatenate set operator. For example,
+ `feature_category=continuous_integration,pages` includes all queues from
+ either the `continuous_integration` category or the `pages` category. This
+ example is also possible using the OR operator, but allows greater brevity, as
+ well as being lower precedence.
+
+The operator precedence for this syntax is fixed: it's not possible to make `AND`
+have higher precedence than `OR`.
+
+As with the standard queue group syntax above, a single `*` as the
+entire queue group selects all queues.
+
+### List of available job classes
+
+For a list of the existing Sidekiq job classes and queues, check the following
+files:
+
+- [Queues for all GitLab editions](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/workers/all_queues.yml)
+- [Queues for GitLab Enterprise Editions only](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/workers/all_queues.yml)
diff --git a/doc/administration/sidekiq/sidekiq_job_migration.md b/doc/administration/sidekiq/sidekiq_job_migration.md
index f61021ad4e7..b93d86d4c86 100644
--- a/doc/administration/sidekiq/sidekiq_job_migration.md
+++ b/doc/administration/sidekiq/sidekiq_job_migration.md
@@ -4,7 +4,7 @@ group: unassigned
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Sidekiq job migration **(FREE SELF)**
+# Sidekiq job migration Rake tasks **(FREE SELF)**
WARNING:
This operation should be very uncommon. We do not recommend it for the vast majority of GitLab instances.
@@ -17,24 +17,27 @@ If the Sidekiq routing rules are changed, administrators need to take care with
1. Listen to both the old and new queues.
1. Update the routing rules.
-1. Wait until there are no publishers dispatching jobs to the old queues.
-1. Run the [Rake tasks for future jobs](#future-jobs).
-1. Wait for the old queues to be empty.
+1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+1. Run the [Rake tasks for migrating queued and future jobs](#migrate-queued-and-future-jobs).
1. Stop listening to the old queues.
-## Future jobs
+## Migrate queued and future jobs
Step 4 involves rewriting some Sidekiq job data for jobs that are already stored in Redis, but due to run in future. There are two sets of jobs to run in future: scheduled jobs and jobs to be retried. We provide a separate Rake task to migrate each set:
- `gitlab:sidekiq:migrate_jobs:retry` for jobs to be retried.
- `gitlab:sidekiq:migrate_jobs:scheduled` for scheduled jobs.
-Most of the time, running both at the same time is the correct choice. There are two separate tasks to allow for more fine-grained control where needed. To run both at once:
+Queued jobs that are yet to be run can also be migrated with a Rake task:
+
+- `gitlab:sidekiq:migrate_jobs:queued` for queued jobs to be performed asynchronously.
+
+Most of the time, running all three at the same time is the correct choice. There are three separate tasks to allow for more fine-grained control where needed. To run all three at once:
```shell
# omnibus-gitlab
-sudo gitlab-rake gitlab:sidekiq:migrate_jobs:retry gitlab:sidekiq:migrate_jobs:schedule
+sudo gitlab-rake gitlab:sidekiq:migrate_jobs:retry gitlab:sidekiq:migrate_jobs:schedule gitlab:sidekiq:migrate_jobs:queued
# source installations
-bundle exec rake gitlab:sidekiq:migrate_jobs:retry gitlab:sidekiq:migrate_jobs:schedule RAILS_ENV=production
+bundle exec rake gitlab:sidekiq:migrate_jobs:retry gitlab:sidekiq:migrate_jobs:schedule gitlab:sidekiq:migrate_jobs:queued RAILS_ENV=production
```
diff --git a/doc/administration/sidekiq/sidekiq_troubleshooting.md b/doc/administration/sidekiq/sidekiq_troubleshooting.md
index d2afe171e9c..b261e385949 100644
--- a/doc/administration/sidekiq/sidekiq_troubleshooting.md
+++ b/doc/administration/sidekiq/sidekiq_troubleshooting.md
@@ -56,6 +56,120 @@ gitlab_rails['env'] = {"SIDEKIQ_LOG_ARGUMENTS" => "0"}
In GitLab 13.5 and earlier, set `SIDEKIQ_LOG_ARGUMENTS` to `1` to start logging arguments passed to Sidekiq.
+## Investigating Sidekiq queue backlogs or slow performance
+
+Symptoms of slow Sidekiq performance include problems with merge request status updates,
+and delays before CI pipelines start running.
+
+Potential causes include:
+
+- The GitLab instance may need more Sidekiq workers. By default, a single-node Omnibus GitLab
+ runs one worker, restricting the execution of Sidekiq jobs to a maximum of one CPU core.
+ [Read more about running multiple Sidekiq workers](extra_sidekiq_processes.md).
+
+- The instance is configured with more Sidekiq workers, but most of the extra workers are
+ not configured to run any job that is queued. This can result in a backlog of jobs
+ when the instance is busy, if the workload has changed in the months or years since
+ the workers were configured, or as a result of GitLab product changes.
+
+Gather data on the state of the Sidekiq workers with the following Ruby script.
+
+1. Create the script:
+
+ ```ruby
+ cat > /var/opt/gitlab/sidekiqcheck.rb <<EOF
+ require 'sidekiq/monitor'
+ Sidekiq::Monitor::Status.new.display('overview')
+ Sidekiq::Monitor::Status.new.display('processes'); nil
+ Sidekiq::Monitor::Status.new.display('queues'); nil
+ puts "----------- workers ----------- "
+ workers = Sidekiq::Workers.new
+ workers.each do |_process_id, _thread_id, work|
+ pp work
+ end
+ puts "----------- Queued Jobs ----------- "
+ Sidekiq::Queue.all.each do |queue|
+ queue.each do |job|
+ pp job
+ end
+ end ;nil
+ puts "----------- done! ----------- "
+ EOF
+ ```
+
+1. Execute and capture the output:
+
+ ```shell
+ sudo gitlab-rails runner /var/opt/gitlab/sidekiqcheck.rb > /tmp/sidekiqcheck_$(date '+%Y%m%d-%H:%M').out
+ ```
+
+ If the performance issue is intermittent:
+
+ - Run this in a cron job every five minutes. Write the files to a location with enough space: allow for 500KB per file.
+ - Refer back to the data to see what went wrong.
+
+1. Analyze the output. The following commands assume that you have a directory of output files.
+
+ 1. `grep 'Busy: ' *` shows how many jobs were being run. `grep 'Enqueued: ' *`
+ shows the backlog of work at that time.
+
+ 1. Look at the number of busy threads across the workers in samples where Sidekiq is under load:
+
+ ```shell
+ ls | while read f ; do if grep -q 'Enqueued: 0' $f; then :
+ else echo $f; egrep 'Busy:|Enqueued:|---- Processes' $f
+ grep 'Threads:' $f ; fi
+ done | more
+ ```
+
+ Example output:
+
+ ```plaintext
+ sidekiqcheck_20221024-14:00.out
+ Busy: 47
+ Enqueued: 363
+ ---- Processes (13) ----
+ Threads: 30 (0 busy)
+ Threads: 30 (0 busy)
+ Threads: 30 (0 busy)
+ Threads: 30 (0 busy)
+ Threads: 23 (0 busy)
+ Threads: 30 (0 busy)
+ Threads: 30 (0 busy)
+ Threads: 30 (0 busy)
+ Threads: 30 (0 busy)
+ Threads: 30 (0 busy)
+ Threads: 30 (0 busy)
+ Threads: 30 (24 busy)
+ Threads: 30 (23 busy)
+ ```
+
+ - In this output file, 47 threads were busy, and there was a backlog of 363 jobs.
+ - Of the 13 worker processes, only two were busy.
+ - This indicates that the other workers are configured too specifically.
+ - Look at the full output to work out which workers were busy.
+ Correlate with your `sidekiq_queues` configuration in `gitlab.rb`.
+ - An overloaded single-worker environment might look like this:
+
+ ```plaintext
+ sidekiqcheck_20221024-14:00.out
+ Busy: 25
+ Enqueued: 363
+ ---- Processes (1) ----
+ Threads: 25 (25 busy)
+ ```
+
+ 1. Look at the `---- Queues (xxx) ----` section of the output file to
+ determine what jobs were queued up at the time.
+
+ 1. The files also include low level details about the state of Sidekiq at the time.
+ This could be useful for identifying where spikes in workload are coming from.
+
+ - The `----------- workers -----------` section details the jobs that make up the
+ `Busy` count in the summary.
+ - The `----------- Queued Jobs -----------` section provides details on
+ jobs that are `Enqueued`.
+
## Thread dump
Send the Sidekiq process ID the `TTIN` signal to output thread
@@ -379,3 +493,60 @@ has number of drawbacks, as mentioned in [Why Ruby's Timeout is dangerous (and T
> - in any of your code, regardless of whether it could have possibly raised an exception before
>
> Nobody writes code to defend against an exception being raised on literally any line. That's not even possible. So Thread.raise is basically like a sneak attack on your code that could result in almost anything. It would probably be okay if it were pure-functional code that did not modify any state. But this is Ruby, so that's unlikely :)
+
+## Omnibus GitLab 14.0 and later: remove the `sidekiq-cluster` service
+
+Omnibus GitLab instances that were configured to run `sidekiq-cluster` prior to GitLab 14.0
+might still have this service running along side `sidekiq` in later releases.
+
+The code to manage `sidekiq-cluster` was removed in GitLab 14.0.
+The configuration files remain on disk so the `sidekiq-cluster` process continues
+to be started by the GitLab systemd service .
+
+The extra service can be identified as running by:
+
+- `gitlab-ctl status` showing both services:
+
+ ```plaintext
+ run: sidekiq: (pid 1386) 445s; run: log: (pid 1385) 445s
+ run: sidekiq-cluster: (pid 1388) 445s; run: log: (pid 1381) 445s
+ ```
+
+- `ps -ef | grep 'runsv sidekiq'` showing two processes:
+
+ ```plaintext
+ root 31047 31045 0 13:54 ? 00:00:00 runsv sidekiq-cluster
+ root 31054 31045 0 13:54 ? 00:00:00 runsv sidekiq
+ ```
+
+To remove the `sidekiq-cluster` service from servers running GitLab 14.0 and later:
+
+1. Stop GitLab and the systemd service:
+
+ ```shell
+ sudo gitlab-ctl stop
+ sudo systemctl stop gitlab-runsvdir.service
+ ```
+
+1. Remove the `runsv` service definition:
+
+ ```shell
+ sudo rm -rf /opt/gitlab/sv/sidekiq-cluster
+ ```
+
+1. Restart GitLab:
+
+ ```shell
+ sudo systemctl start gitlab-runsvdir.service
+ ```
+
+1. Check that all services are up, and the `sidekiq-cluster` service is not listed:
+
+ ```shell
+ sudo gitlab-ctl status
+ ```
+
+This change might reduce the amount of work Sidekiq can do. Symptoms like delays creating pipelines
+indicate that additional Sidekiq processes would be beneficial.
+Consider [adding additional Sidekiq processes](extra_sidekiq_processes.md)
+to compensate for removing the `sidekiq-cluster` service.
diff --git a/doc/administration/sidekiq_health_check.md b/doc/administration/sidekiq_health_check.md
deleted file mode 100644
index 3294eb663f2..00000000000
--- a/doc/administration/sidekiq_health_check.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'sidekiq/sidekiq_health_check.md'
-remove_date: '2022-11-11'
----
-
-This document was moved to [another location](sidekiq/sidekiq_health_check.md).
-
-<!-- This redirect file can be deleted after <2022-11-11>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/administration/snippets/index.md b/doc/administration/snippets/index.md
index 7bf828afedd..4bd03aeb8c8 100644
--- a/doc/administration/snippets/index.md
+++ b/doc/administration/snippets/index.md
@@ -72,3 +72,7 @@ You can also use the API to [retrieve the current value](../../api/settings.md#g
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/application/settings"
```
+
+## Related topics
+
+- [User snippets](../../user/snippets.md)
diff --git a/doc/administration/terraform_state.md b/doc/administration/terraform_state.md
index 02a95e28747..d3b941bd129 100644
--- a/doc/administration/terraform_state.md
+++ b/doc/administration/terraform_state.md
@@ -160,6 +160,10 @@ sudo find /var/opt/gitlab/gitlab-rails/shared/terraform_state -type f | grep -v
### S3-compatible connection settings
+In GitLab 13.2 and later, you should use the
+[consolidated object storage settings](object_storage.md#consolidated-object-storage-configuration).
+This section describes the earlier configuration format.
+
See [the available connection settings for different providers](object_storage.md#connection-settings).
**In Omnibus installations:**
diff --git a/doc/administration/troubleshooting/elasticsearch.md b/doc/administration/troubleshooting/elasticsearch.md
deleted file mode 100644
index 7390f4bc816..00000000000
--- a/doc/administration/troubleshooting/elasticsearch.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../../integration/advanced_search/elasticsearch_troubleshooting.md'
-remove_date: '2022-11-02'
----
-
-This document was moved to [another location](../../integration/advanced_search/elasticsearch_troubleshooting.md).
-
-<!-- This redirect file can be deleted after <2022-11-02>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
index 6993d48b450..7c5feb24e15 100644
--- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
+++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
@@ -1,11 +1,95 @@
---
-redirect_to: 'index.md'
-remove_date: '2023-02-01'
+stage: Systems
+group: Distribution
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-This document was moved to [another location](index.md).
+# GitLab Rails Console Cheat Sheet **(FREE SELF)**
-<!-- This redirect file can be deleted after 2023-02-01. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
+This was the GitLab Support Team's collection of information regarding the GitLab Rails
+console, for use while troubleshooting. It is listed here for posterity,
+as most content has been moved to feature-specific troubleshooting pages and sections,
+see epic [&8147](https://gitlab.com/groups/gitlab-org/-/epics/8147#tree).
+Please update your bookmarks accordingly.
+
+If you are currently having an issue with GitLab,
+it is highly recommended that you first check
+our guide on [the Rails console](../operations/rails_console.md),
+and your [support options](https://about.gitlab.com/support/),
+before attempting the information pointed to from here.
+
+WARNING:
+Some of these scripts could be damaging if not run correctly,
+or under the right conditions. We highly recommend running them under the
+guidance of a Support Engineer, or running them in a test environment with a
+backup of the instance ready to be restored, just in case.
+
+WARNING:
+As GitLab changes, changes to the code are inevitable,
+and so some scripts may not work as they once used to. These are not kept
+up-to-date as these scripts/commands were added as they were found/needed. As
+mentioned above, we recommend running these scripts under the supervision of a
+Support Engineer, who can also verify that they continue to work as they
+should and, if needed, update the script for the latest version of GitLab.
+
+## Mirrors
+
+### Find mirrors with "bad decrypt" errors
+
+This content has been converted to a Rake task, see [verify database values can be decrypted using the current secrets](../raketasks/check.md#verify-database-values-can-be-decrypted-using-the-current-secrets).
+
+### Transfer mirror users and tokens to a single service account
+
+This content has been moved to [Troubleshooting Repository mirroring](../../user/project/repository/mirror/index.md#transfer-mirror-users-and-tokens-to-a-single-service-account-in-rails-console).
+
+## Merge requests
+
+## CI
+
+This content has been moved to [Troubleshooting CI/CD](../../ci/troubleshooting.md).
+
+## License
+
+This content has been moved to [Activate GitLab EE with a license file or key](../../user/admin_area/license_file.md).
+
+## Registry
+
+### Registry Disk Space Usage by Project
+
+Find this content in the [Container Registry troubleshooting documentation](../packages/container_registry.md#registry-disk-space-usage-by-project).
+
+### Run the Cleanup policy now
+
+Find this content in the [Container Registry troubleshooting documentation](../packages/container_registry.md#run-the-cleanup-policy-now).
+
+## Sidekiq
+
+This content has been moved to [Troubleshooting Sidekiq](../sidekiq/sidekiq_troubleshooting.md).
+
+## Geo
+
+### Reverify all uploads (or any SSF data type which is verified)
+
+Moved to [Geo replication troubleshooting](../geo/replication/troubleshooting.md#reverify-all-uploads-or-any-ssf-data-type-which-is-verified).
+
+### Artifacts
+
+Moved to [Geo replication troubleshooting](../geo/replication/troubleshooting.md#find-failed-artifacts).
+
+### Repository verification failures
+
+Moved to [Geo replication troubleshooting](../geo/replication/troubleshooting.md#find-repository-verification-failures).
+
+### Resync repositories
+
+Moved to [Geo replication troubleshooting - Resync repository types except for project or project wiki repositories](../geo/replication/troubleshooting.md#repository-types-except-for-project-or-project-wiki-repositories).
+
+Moved to [Geo replication troubleshooting - Resync project and project wiki repositories](../geo/replication/troubleshooting.md#resync-project-and-project-wiki-repositories).
+
+### Blob types
+
+Moved to [Geo replication troubleshooting](../geo/replication/troubleshooting.md#blob-types).
+
+## Generate Service Ping
+
+This content has been moved to [Service Ping Troubleshooting](../../development/service_ping/troubleshooting.md).
diff --git a/doc/administration/troubleshooting/linux_cheat_sheet.md b/doc/administration/troubleshooting/linux_cheat_sheet.md
index 90cd1e24c79..ae0ef44f0b1 100644
--- a/doc/administration/troubleshooting/linux_cheat_sheet.md
+++ b/doc/administration/troubleshooting/linux_cheat_sheet.md
@@ -294,8 +294,8 @@ small differences should not be considered significant.
|Setup | access times |
|:--------------|:--------------|
-| EFS | 10 - 30ms |
-| Local Storage | 0.01 - 1ms |
+| EFS | 10 - 30 ms |
+| Local Storage | 0.01 - 1 ms |
## Networking
diff --git a/doc/administration/troubleshooting/postgresql.md b/doc/administration/troubleshooting/postgresql.md
index 829fed38060..d5288bfead8 100644
--- a/doc/administration/troubleshooting/postgresql.md
+++ b/doc/administration/troubleshooting/postgresql.md
@@ -44,10 +44,6 @@ This section is for links to information elsewhere in the GitLab documentation.
- Consuming PostgreSQL from [within CI runners](../../ci/services/postgres.md).
-- [Using Slony to update PostgreSQL](../../update/upgrading_postgresql_using_slony.md).
- - Uses replication to handle PostgreSQL upgrades if the schemas are the same.
- - Reduces downtime to a short window for switching to the newer version.
-
- Managing Omnibus PostgreSQL versions [from the development docs](https://docs.gitlab.com/omnibus/development/managing-postgresql-versions.html).
- [PostgreSQL scaling](../postgresql/replication_and_failover.md)
@@ -104,14 +100,14 @@ or `statement_timeout`, but to leave the third setting at 60 seconds. Setting
`idle_in_transaction` protects the database from sessions potentially hanging for
days. There's more discussion in [the issue relating to introducing this timeout on GitLab.com](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/1053).
-PostgresSQL defaults:
+PostgreSQL defaults:
- `statement_timeout = 0` (never)
- `idle_in_transaction_session_timeout = 0` (never)
Comments in issue [#30528](https://gitlab.com/gitlab-org/gitlab/-/issues/30528)
indicate that these should both be set to at least a number of minutes for all
-Omnibus GitLab installations (so they don't hang indefinitely). However, 15s
+Omnibus GitLab installations (so they don't hang indefinitely). However, 15 s
for `statement_timeout` is very short, and is only effective if the
underlying infrastructure is very performant.
@@ -188,7 +184,7 @@ To temporarily change the statement timeout:
### Database is not accepting commands to avoid wraparound data loss
-This error likely means that AUTOVACUUM is failing to complete its run:
+This error likely means that `autovacuum` is failing to complete its run:
```plaintext
ERROR: database is not accepting commands to avoid wraparound data loss in database "gitlabhq_production"
@@ -211,7 +207,7 @@ To resolve the error, run `VACUUM` manually:
The [database requirements](../../install/requirements.md#database) for GitLab include:
-- Support for MySQL was removed in GitLab 12.1; [migrate to PostgreSQL](../../update/mysql_to_postgresql.md).
+- Support for MySQL was removed in [GitLab 12.1](../../update/index.md#1210).
- Review and install the [required extension list](../../install/postgresql_extensions.md).
### Serialization errors in the `production/sidekiq` log
diff --git a/doc/administration/troubleshooting/sidekiq.md b/doc/administration/troubleshooting/sidekiq.md
deleted file mode 100644
index e49e0ed4f1c..00000000000
--- a/doc/administration/troubleshooting/sidekiq.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../sidekiq/sidekiq_troubleshooting.md'
-remove_date: '2022-11-11'
----
-
-This document was moved to [another location](../sidekiq/sidekiq_troubleshooting.md).
-
-<!-- This redirect file can be deleted after <2022-11-11>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/administration/troubleshooting/tracing_correlation_id.md b/doc/administration/troubleshooting/tracing_correlation_id.md
deleted file mode 100644
index 917e27bab70..00000000000
--- a/doc/administration/troubleshooting/tracing_correlation_id.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../logs/tracing_correlation_id.md'
-remove_date: '2022-11-12'
----
-
-This document was moved to [another location](../logs/tracing_correlation_id.md).
-
-<!-- This redirect file can be deleted after 2022-11-12. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> \ No newline at end of file
diff --git a/doc/administration/uploads.md b/doc/administration/uploads.md
index f0def7320cc..ff0b8ecf178 100644
--- a/doc/administration/uploads.md
+++ b/doc/administration/uploads.md
@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Uploads administration **(FREE SELF)**
-Uploads represent all user data that may be sent to GitLab as a single file. As an example, avatars and notes' attachments are uploads. Uploads are integral to GitLab functionality, and therefore cannot be disabled.
+Uploads represent all user data that may be sent to GitLab as a single file. For example, avatars and note attachments are uploads. Uploads are integral to GitLab functionality and therefore cannot be disabled.
## Using local storage
@@ -14,15 +14,15 @@ This is the default configuration. To change the location where the uploads are
stored locally, use the steps in this section based on your installation method:
NOTE:
-For historical reasons, instance level uploads (for example the [favicon](../user/admin_area/appearance.md#favicon)) are stored into a base directory,
-which by default is `uploads/-/system`. It is strongly discouraged to change the base
-directory on an existing GitLab installation.
+For historical reasons, uploads for the whole instance (for example the [favicon](../user/admin_area/appearance.md#favicon)) are stored in a base directory,
+which by default is `uploads/-/system`. Changing the base
+directory on an existing GitLab installation is strongly discouraged.
**In Omnibus GitLab installations:**
_The uploads are stored by default in `/var/opt/gitlab/gitlab-rails/uploads`._
-1. To change the storage path for example to `/mnt/storage/uploads`, edit
+1. To change the storage path, for example to `/mnt/storage/uploads`, edit
`/etc/gitlab/gitlab.rb` and add the following line:
```ruby
@@ -38,7 +38,7 @@ _The uploads are stored by default in `/var/opt/gitlab/gitlab-rails/uploads`._
_The uploads are stored by default in
`/home/git/gitlab/public/uploads`._
-1. To change the storage path for example to `/mnt/storage/uploads`, edit
+1. To change the storage path, for example to `/mnt/storage/uploads`, edit
`/home/git/gitlab/config/gitlab.yml` and add or amend the following lines:
```yaml
@@ -57,10 +57,12 @@ This configuration relies on valid AWS credentials to be configured already.
[Read more about using object storage with GitLab](object_storage.md).
-We recommend using the [consolidated object storage settings](object_storage.md#consolidated-object-storage-configuration). The following instructions apply to the original configuration format.
-
### Object Storage Settings
+In GitLab 13.2 and later, you should use the
+[consolidated object storage settings](object_storage.md#consolidated-object-storage-configuration).
+This section describes the earlier configuration format.
+
For source installations the following settings are nested under `uploads:` and then `object_store:`. On Omnibus GitLab installs they are prefixed by `uploads_object_store_`.
| Setting | Description | Default |
@@ -104,7 +106,7 @@ _The uploads are stored by default in
```
1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-1. Migrate any existing local uploads to the object storage using [`gitlab:uploads:migrate:all` Rake task](raketasks/uploads/migrate.md).
+1. Migrate any existing local uploads to the object storage with the [`gitlab:uploads:migrate:all` Rake task](raketasks/uploads/migrate.md).
**In installations from source:**
@@ -127,4 +129,4 @@ _The uploads are stored by default in
```
1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect.
-1. Migrate any existing local uploads to the object storage using [`gitlab:uploads:migrate:all` Rake task](raketasks/uploads/migrate.md).
+1. Migrate any existing local uploads to the object storage with the [`gitlab:uploads:migrate:all` Rake task](raketasks/uploads/migrate.md).
diff --git a/doc/administration/user_settings.md b/doc/administration/user_settings.md
index c96a6311208..b02e1c7244b 100644
--- a/doc/administration/user_settings.md
+++ b/doc/administration/user_settings.md
@@ -18,7 +18,7 @@ ability to create top-level groups (does not affect existing users' setting), Gi
- The [application setting API](../api/settings.md#change-application-settings).
- In GitLab 15.4 and earlier, in a configuration file by following the steps in this section.
-To disable new users' ability to create top-level groups using the configuation file:
+To disable new users' ability to create top-level groups using the configuration file:
**Omnibus GitLab installations**
diff --git a/doc/api/alert_management_alerts.md b/doc/api/alert_management_alerts.md
index bf3d6287341..702d453e140 100644
--- a/doc/api/alert_management_alerts.md
+++ b/doc/api/alert_management_alerts.md
@@ -33,7 +33,7 @@ Example response:
"created_at": "2020-11-12T20:07:58.156Z",
"filename": "sample_2054",
"file_path": "/uploads/-/system/alert_metric_image/file/17/sample_2054.png",
- "url": "example.com/metric",
+ "url": "https://example.com/metric",
"url_text": "An example metric"
}
```
@@ -62,7 +62,7 @@ Example response:
"created_at": "2020-11-12T20:07:58.156Z",
"filename": "sample_2054",
"file_path": "/uploads/-/system/alert_metric_image/file/17/sample_2054.png",
- "url": "example.com/metric",
+ "url": "https://example.com/metric",
"url_text": "An example metric"
},
{
@@ -70,7 +70,7 @@ Example response:
"created_at": "2020-11-12T20:14:26.441Z",
"filename": "sample_2054",
"file_path": "/uploads/-/system/alert_metric_image/file/18/sample_2054.png",
- "url": "example.com/metric",
+ "url": "https://example.com/metric",
"url_text": "An example metric"
}
]
@@ -102,8 +102,8 @@ Example response:
"created_at": "2020-11-13T00:06:18.084Z",
"filename": "file.png",
"file_path": "/uploads/-/system/alert_metric_image/file/23/file.png",
- "url": "http://example.com",
- "url_text": "Example website"
+ "url": "https://example.com/metric",
+ "url_text": "An example metric"
}
```
diff --git a/doc/api/appearance.md b/doc/api/appearance.md
index f8926f8a91d..622239e7283 100644
--- a/doc/api/appearance.md
+++ b/doc/api/appearance.md
@@ -29,6 +29,7 @@ Example response:
```json
{
"title": "GitLab Test Instance",
+ "short_title": "GitLab",
"description": "gitlab-test.example.com",
"logo": "/uploads/-/system/appearance/logo/1/logo.png",
"header_logo": "/uploads/-/system/appearance/header_logo/1/header.png",
@@ -54,6 +55,7 @@ PUT /application/appearance
| Attribute | Type | Required | Description |
| --------------------------------- | ------- | -------- | ----------- |
| `title` | string | no | Instance title on the sign in / sign up page
+| `short_title` | string | no | Short title for progressive web app
| `description` | string | no | Markdown text shown on the sign in / sign up page
| `logo` | mixed | no | Instance image used on the sign in / sign up page. See [Change logo](#change-logo)
| `header_logo` | mixed | no | Instance image used for the main navigation bar
@@ -75,6 +77,7 @@ Example response:
```json
{
"title": "GitLab Test Instance",
+ "short_title": "GitLab",
"description": "gitlab-test.example.com",
"logo": "/uploads/-/system/appearance/logo/1/logo.png",
"header_logo": "/uploads/-/system/appearance/header_logo/1/header.png",
diff --git a/doc/api/applications.md b/doc/api/applications.md
index f7b646d2351..53ea2f51d1a 100644
--- a/doc/api/applications.md
+++ b/doc/api/applications.md
@@ -103,7 +103,7 @@ Parameters:
| Attribute | Type | Required | Description |
|:----------|:--------|:---------|:----------------------------------------------------|
-| `id` | integer | yes | The ID of the application (not the application_id). |
+| `id` | integer | yes | The ID of the application (not the `application_id`). |
Example request:
diff --git a/doc/api/bulk_imports.md b/doc/api/bulk_imports.md
index e18a77df6df..a438bc13818 100644
--- a/doc/api/bulk_imports.md
+++ b/doc/api/bulk_imports.md
@@ -29,10 +29,11 @@ POST /bulk_imports
| `entities[source_full_path]` | String | yes | Source full path of the entity to import. |
| `entities[destination_name]` | String | yes | Deprecated: Use :destination_slug instead. Destination slug for the entity. |
| `entities[destination_slug]` | String | yes | Destination slug for the entity. |
-| `entities[destination_namespace]` | String | no | Destination namespace for the entity. |
+| `entities[destination_namespace]` | String | yes | Destination namespace for the entity. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/bulk_imports" \
+ --header "Content-Type: application/json" \
--data '{
"configuration": {
"url": "http://gitlab.example/",
diff --git a/doc/api/commits.md b/doc/api/commits.md
index 3fe77dd5f43..83fa54231ee 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -100,15 +100,15 @@ POST /projects/:id/repository/commits
| `stats` | boolean | no | Include commit stats. Default is true |
| `force` | boolean | no | When `true` overwrites the target branch with a new commit based on the `start_branch` or `start_sha` |
-| `actions[]` Attribute | Type | Required | Description |
-| --------------------- | ---- | -------- | ----------- |
-| `action` | string | yes | The action to perform, `create`, `delete`, `move`, `update`, `chmod`|
-| `file_path` | string | yes | Full path to the file. Ex. `lib/class.rb` |
-| `previous_path` | string | no | Original full path to the file being moved. Ex. `lib/class1.rb`. Only considered for `move` action. |
-| `content` | string | no | File content, required for all except `delete`, `chmod`, and `move`. Move actions that do not specify `content` preserve the existing file content, and any other value of `content` overwrites the file content. |
-| `encoding` | string | no | `text` or `base64`. `text` is default. |
-| `last_commit_id` | string | no | Last known file commit ID. Only considered in update, move, and delete actions. |
-| `execute_filemode` | boolean | no | When `true/false` enables/disables the execute flag on the file. Only considered for `chmod` action. |
+| `actions[]` Attribute | Type | Required | Description |
+|-----------------------|---------|----------|-------------|
+| `action` | string | yes | The action to perform: `create`, `delete`, `move`, `update`, or `chmod`. |
+| `file_path` | string | yes | Full path to the file. For example: `lib/class.rb`. |
+| `previous_path` | string | no | Original full path to the file being moved. For example `lib/class1.rb`. Only considered for `move` action. |
+| `content` | string | no | File content, required for all except `delete`, `chmod`, and `move`. Move actions that do not specify `content` preserve the existing file content, and any other value of `content` overwrites the file content. |
+| `encoding` | string | no | `text` or `base64`. `text` is default. |
+| `last_commit_id` | string | no | Last known file commit ID. Only considered in update, move, and delete actions. |
+| `execute_filemode` | boolean | no | When `true/false` enables/disables the execute flag on the file. Only considered for `chmod` action. |
```shell
PAYLOAD=$(cat << 'JSON'
@@ -695,9 +695,10 @@ Example response:
]
```
-### Post the build status to a commit
+### Set the pipeline status of a commit
-Adds or updates a build status of a commit.
+Add or update the pipeline status of a commit. If the commit is associated with a merge request,
+the API call must target the commit in the merge request's source branch.
```plaintext
POST /projects/:id/statuses/:sha
diff --git a/doc/api/container_registry.md b/doc/api/container_registry.md
index 5b11c324802..a2e4d9f37f5 100644
--- a/doc/api/container_registry.md
+++ b/doc/api/container_registry.md
@@ -419,18 +419,51 @@ To query those, follow the Registry's built-in mechanism to obtain and use an
NOTE:
These are different from project or personal access tokens in the GitLab application.
+### Obtain token from GitLab
+
+```plaintext
+GET ${CI_SERVER_URL}/jwt/auth?service=container_registry&scope=*
+```
+
+You must specify the correct [scopes and actions](https://docs.docker.com/registry/spec/auth/scope/) to retrieve a valid token:
+
+```shell
+$ SCOPE="repository:${CI_REGISTRY_IMAGE}:delete" #or push,pull
+
+$ curl --request GET --user "${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD}" \
+ "https://gitlab.example.com/jwt/auth?service=container_registry&scope=${SCOPE}"
+{"token":" ... "}
+```
+
+### Delete image tags by reference
+
+```plaintext
+DELETE http(s)://${CI_REGISTRY}/v2/${CI_REGISTRY_IMAGE}/tags/reference/${CI_COMMIT_SHORT_SHA}
+```
+
+You can use the token retrieved with the predefined `CI_REGISTRY_USER` and `CI_REGISTRY_PASSWORD` variables to delete container image tags by reference on your GitLab instance.
+The `tag_delete` [Container-Registry-Feature](https://gitlab.com/gitlab-org/container-registry/-/tree/v3.61.0-gitlab/docs-gitlab#api) must be enabled.
+
+```shell
+$ curl --request DELETE --header "Authorization: Bearer <token_from_above>" \
+ --header "Accept: application/vnd.docker.distribution.manifest.v2+json" \
+ "https://gitlab.example.com:5050/v2/${CI_REGISTRY_IMAGE}/tags/reference/${CI_COMMIT_SHORT_SHA}"
+```
+
### Listing all container repositories
```plaintext
-GET /v2/_catalog
+GET http(s)://${CI_REGISTRY}/v2/_catalog
```
To list all container repositories on your GitLab instance, administrator credentials are required:
```shell
-$ curl --request GET --user "<admin-username>:<admin-password>" "https://gitlab.example.com/jwt/auth?service=container_registry&scope=registry:catalog:*"
+$ SCOPE="registry:catalog:*"
+
+$ curl --request GET --user "<admin-username>:<admin-password>" \
+ "https://gitlab.example.com/jwt/auth?service=container_registry&scope=${SCOPE}"
{"token":" ... "}
$ curl --header "Authorization: Bearer <token_from_above>" https://gitlab.example.com:5050/v2/_catalog
-{"repositories":["user/project1", "group/subgroup/project2", ... ]}
```
diff --git a/doc/api/deployments.md b/doc/api/deployments.md
index daf2b635855..688806e9b22 100644
--- a/doc/api/deployments.md
+++ b/doc/api/deployments.md
@@ -15,20 +15,25 @@ Get a list of deployments in a project.
GET /projects/:id/deployments
```
-| Attribute | Type | Required | Description |
-|------------------|----------------|----------|-----------------------------------------------------------------------------------------------------------------|
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
-| `order_by` | string | no | Return deployments ordered by either one of `id`, `iid`, `created_at`, `updated_at` or `ref` fields. Default is `id`. |
-| `sort` | string | no | Return deployments sorted in `asc` or `desc` order. Default is `asc`. |
-| `updated_after` | datetime | no | Return deployments updated after the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `updated_before` | datetime | no | Return deployments updated before the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `environment` | string | no | The [name of the environment](../ci/environments/index.md) to filter deployments by. |
-| `status` | string | no | The status to filter deployments by. One of `created`, `running`, `success`, `failed`, `canceled`, or `blocked`.
+| Attribute | Type | Required | Description |
+|-------------------|----------------|----------|-----------------------------------------------------------------------------------------------------------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `order_by` | string | no | Return deployments ordered by either one of `id`, `iid`, `created_at`, `updated_at`, `finished_at` or `ref` fields. Default is `id`. |
+| `sort` | string | no | Return deployments sorted in `asc` or `desc` order. Default is `asc`. |
+| `updated_after` | datetime | no | Return deployments updated after the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `updated_before` | datetime | no | Return deployments updated before the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `finished_after` | datetime | no | Return deployments finished after the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `finished_before` | datetime | no | Return deployments finished before the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `environment` | string | no | The [name of the environment](../ci/environments/index.md) to filter deployments by. |
+| `status` | string | no | The status to filter deployments by. One of `created`, `running`, `success`, `failed`, `canceled`, or `blocked`.
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/deployments"
```
+NOTE:
+When using `finished_before` or `finished_after`, you should specify the `order_by` to be `finished_at` and `status` should be `success`.
+
Example response:
```json
diff --git a/doc/api/discussions.md b/doc/api/discussions.md
index e82f6927e0b..67fca84e449 100644
--- a/doc/api/discussions.md
+++ b/doc/api/discussions.md
@@ -911,7 +911,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>"\
To create a new thread:
-1. [Get the latest merge request version](merge_requests.md#get-mr-diff-versions):
+1. [Get the latest merge request version](merge_requests.md#get-merge-request-diff-versions):
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>"\
diff --git a/doc/api/dora/metrics.md b/doc/api/dora/metrics.md
index d902f5eb91f..4374d1dde95 100644
--- a/doc/api/dora/metrics.md
+++ b/doc/api/dora/metrics.md
@@ -103,7 +103,7 @@ parameter:
|:---------------------------|:-----------------------------------|
| `change_failure_rate` | The number of incidents divided by the number of deployments during the time period. Available only for production environment. |
| `deployment_frequency` | The number of successful deployments during the time period. |
-| `lead_time_for_changes` | The median number of seconds between the merge of the merge request (MR) and the deployment of the MR's commits for all MRs deployed during the time period. |
+| `lead_time_for_changes` | The median number of seconds between the merge of the merge request (MR) and the deployment of the MR commits for all MRs deployed during the time period. |
| `time_to_restore_service` | The median number of seconds an incident was open during the time period. Available only for production environment. |
NOTE:
diff --git a/doc/api/epic_issues.md b/doc/api/epic_issues.md
index 4bbd0b21136..28c74a0a418 100644
--- a/doc/api/epic_issues.md
+++ b/doc/api/epic_issues.md
@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Epic Issues API **(PREMIUM)**
-Every API call to epic_issues must be authenticated.
+Every API call to the epic issues API endpoint must be authenticated.
If a user is not a member of a group and the group is private, a `GET` request on that group
results in a `404` status code.
diff --git a/doc/api/geo_nodes.md b/doc/api/geo_nodes.md
index 00380e1624b..6b62e82f54d 100644
--- a/doc/api/geo_nodes.md
+++ b/doc/api/geo_nodes.md
@@ -523,6 +523,18 @@ Example response:
"container_repositories_registry_count": 5,
"container_repositories_synced_in_percentage": "100.00%",
"container_repositories_synced_missing_on_primary_count": 0,
+ "dependency_proxy_manifests_count": 5,
+ "dependency_proxy_manifests_checksum_total_count": 5,
+ "dependency_proxy_manifests_checksummed_count": 5,
+ "dependency_proxy_manifests_checksum_failed_count": 5,
+ "dependency_proxy_manifests_synced_count": 5,
+ "dependency_proxy_manifests_failed_count": 0,
+ "dependency_proxy_manifests_registry_count": 5,
+ "dependency_proxy_manifests_verification_total_count": 5,
+ "dependency_proxy_manifests_verified_count": 5,
+ "dependency_proxy_manifests_verification_failed_count": 5,
+ "dependency_proxy_manifests_synced_in_percentage": "100.00%",
+ "dependency_proxy_manifests_verified_in_percentage": "100.00%"
},
{
"geo_node_id": 2,
@@ -707,6 +719,18 @@ Example response:
"container_repositories_registry_count": 5,
"container_repositories_synced_in_percentage": "100.00%",
"container_repositories_synced_missing_on_primary_count": 0,
+ "dependency_proxy_manifests_count": 5,
+ "dependency_proxy_manifests_checksum_total_count": 5,
+ "dependency_proxy_manifests_checksummed_count": 5,
+ "dependency_proxy_manifests_checksum_failed_count": 5,
+ "dependency_proxy_manifests_synced_count": 5,
+ "dependency_proxy_manifests_failed_count": 0,
+ "dependency_proxy_manifests_registry_count": 5,
+ "dependency_proxy_manifests_verification_total_count": 5,
+ "dependency_proxy_manifests_verified_count": 5,
+ "dependency_proxy_manifests_verification_failed_count": 5,
+ "dependency_proxy_manifests_synced_in_percentage": "100.00%",
+ "dependency_proxy_manifests_verified_in_percentage": "100.00%"
}
]
```
@@ -901,6 +925,18 @@ Example response:
"container_repositories_registry_count": 5,
"container_repositories_synced_in_percentage": "100.00%",
"container_repositories_synced_missing_on_primary_count": 0,
+ "dependency_proxy_manifests_count": 5,
+ "dependency_proxy_manifests_checksum_total_count": 5,
+ "dependency_proxy_manifests_checksummed_count": 5,
+ "dependency_proxy_manifests_checksum_failed_count": 5,
+ "dependency_proxy_manifests_synced_count": 5,
+ "dependency_proxy_manifests_failed_count": 0,
+ "dependency_proxy_manifests_registry_count": 5,
+ "dependency_proxy_manifests_verification_total_count": 5,
+ "dependency_proxy_manifests_verified_count": 5,
+ "dependency_proxy_manifests_verification_failed_count": 5,
+ "dependency_proxy_manifests_synced_in_percentage": "100.00%",
+ "dependency_proxy_manifests_verified_in_percentage": "100.00%"
}
```
diff --git a/doc/api/graphql/audit_report.md b/doc/api/graphql/audit_report.md
index 2f96bc5ecdd..5529f0b872a 100644
--- a/doc/api/graphql/audit_report.md
+++ b/doc/api/graphql/audit_report.md
@@ -20,13 +20,13 @@ The query includes:
- [`pageInfo`](#pageinfo)
- [`nodes`](#nodes)
-## pageInfo
+## `pageInfo`
This contains the data needed to implement pagination. GitLab uses cursor-based
[pagination](getting_started.md#pagination). For more information, see
[Pagination](https://graphql.org/learn/pagination/) in the GraphQL documentation.
-## nodes
+## `nodes`
In a GraphQL query, `nodes` is used to represent a collection of [`nodes` on a graph](https://en.wikipedia.org/wiki/Vertex_(graph_theory)).
In this case, the collection of nodes is a collection of `User` objects. For each one,
diff --git a/doc/api/graphql/index.md b/doc/api/graphql/index.md
index 4cf296ac1f3..0ae6013df80 100644
--- a/doc/api/graphql/index.md
+++ b/doc/api/graphql/index.md
@@ -172,6 +172,7 @@ Limit | Default
Max page size | 100 records (nodes) per page. Applies to most connections in the API. Particular connections may have different max page size limits that are higher or lower.
[Max query complexity](#max-query-complexity) | `200` for unauthenticated requests and `250` for authenticated requests.
Request timeout | 30 seconds.
+Max query size | 10,000 characters per query. If this limit is reached, use [variables](https://graphql.org/learn/queries/#variables) and [fragments](https://graphql.org/learn/queries/#fragments) to reduce the query size. Remove white spaces as last resort.
### Max query complexity
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index b1f9d6ceae1..a3d4458bb6c 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -347,7 +347,7 @@ Returns [`Namespace`](#namespace).
### `Query.package`
-Find a package. This field can only be resolved for one query in any single request.
+Find a package. This field can only be resolved for one query in any single request. Returns `null` if a package has no `default` status.
Returns [`PackageDetailsType`](#packagedetailstype).
@@ -850,6 +850,25 @@ Input type: `AuditEventsStreamingDestinationEventsAddInput`
| <a id="mutationauditeventsstreamingdestinationeventsadderrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationauditeventsstreamingdestinationeventsaddeventtypefilters"></a>`eventTypeFilters` | [`[String!]`](#string) | Event type filters present. |
+### `Mutation.auditEventsStreamingDestinationEventsRemove`
+
+Input type: `AuditEventsStreamingDestinationEventsRemoveInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationauditeventsstreamingdestinationeventsremoveclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationauditeventsstreamingdestinationeventsremovedestinationid"></a>`destinationId` | [`AuditEventsExternalAuditEventDestinationID!`](#auditeventsexternalauditeventdestinationid) | Destination URL. |
+| <a id="mutationauditeventsstreamingdestinationeventsremoveeventtypefilters"></a>`eventTypeFilters` | [`[String!]!`](#string) | List of event type filters to remove from streaming. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationauditeventsstreamingdestinationeventsremoveclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationauditeventsstreamingdestinationeventsremoveerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+
### `Mutation.auditEventsStreamingHeadersCreate`
Input type: `AuditEventsStreamingHeadersCreateInput`
@@ -2079,8 +2098,8 @@ Input type: `DastSiteProfileCreateInput`
| <a id="mutationdastsiteprofilecreatefullpath"></a>`fullPath` | [`ID!`](#id) | Project the site profile belongs to. |
| <a id="mutationdastsiteprofilecreateprofilename"></a>`profileName` | [`String!`](#string) | Name of the site profile. |
| <a id="mutationdastsiteprofilecreaterequestheaders"></a>`requestHeaders` | [`String`](#string) | Comma-separated list of request header names and values to be added to every request made by DAST. |
-| <a id="mutationdastsiteprofilecreatescanfilepath"></a>`scanFilePath` | [`String`](#string) | File Path or URL used as input for the scan method. Will not be saved or updated if `dast_api_scanner` feature flag is disabled. |
-| <a id="mutationdastsiteprofilecreatescanmethod"></a>`scanMethod` | [`DastScanMethodType`](#dastscanmethodtype) | Scan method by the scanner. Is not saved or updated if `dast_api_scanner` feature flag is disabled. |
+| <a id="mutationdastsiteprofilecreatescanfilepath"></a>`scanFilePath` | [`String`](#string) | File Path or URL used as input for the scan method. |
+| <a id="mutationdastsiteprofilecreatescanmethod"></a>`scanMethod` | [`DastScanMethodType`](#dastscanmethodtype) | Scan method by the scanner. |
| <a id="mutationdastsiteprofilecreatetargettype"></a>`targetType` | [`DastTargetTypeEnum`](#dasttargettypeenum) | Type of target to be scanned. |
| <a id="mutationdastsiteprofilecreatetargeturl"></a>`targetUrl` | [`String`](#string) | URL of the target to be scanned. |
@@ -2127,8 +2146,8 @@ Input type: `DastSiteProfileUpdateInput`
| <a id="mutationdastsiteprofileupdateid"></a>`id` | [`DastSiteProfileID!`](#dastsiteprofileid) | ID of the site profile to be updated. |
| <a id="mutationdastsiteprofileupdateprofilename"></a>`profileName` | [`String!`](#string) | Name of the site profile. |
| <a id="mutationdastsiteprofileupdaterequestheaders"></a>`requestHeaders` | [`String`](#string) | Comma-separated list of request header names and values to be added to every request made by DAST. |
-| <a id="mutationdastsiteprofileupdatescanfilepath"></a>`scanFilePath` | [`String`](#string) | File Path or URL used as input for the scan method. Will not be saved or updated if `dast_api_scanner` feature flag is disabled. |
-| <a id="mutationdastsiteprofileupdatescanmethod"></a>`scanMethod` | [`DastScanMethodType`](#dastscanmethodtype) | Scan method by the scanner. Is not saved or updated if `dast_api_scanner` feature flag is disabled. |
+| <a id="mutationdastsiteprofileupdatescanfilepath"></a>`scanFilePath` | [`String`](#string) | File Path or URL used as input for the scan method. |
+| <a id="mutationdastsiteprofileupdatescanmethod"></a>`scanMethod` | [`DastScanMethodType`](#dastscanmethodtype) | Scan method by the scanner. |
| <a id="mutationdastsiteprofileupdatetargettype"></a>`targetType` | [`DastTargetTypeEnum`](#dasttargettypeenum) | Type of target to be scanned. |
| <a id="mutationdastsiteprofileupdatetargeturl"></a>`targetUrl` | [`String`](#string) | URL of the target to be scanned. |
@@ -3132,6 +3151,27 @@ Input type: `IssuableResourceLinkDestroyInput`
| <a id="mutationissuableresourcelinkdestroyerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationissuableresourcelinkdestroyissuableresourcelink"></a>`issuableResourceLink` | [`IssuableResourceLink`](#issuableresourcelink) | Issuable resource link. |
+### `Mutation.issueLinkAlerts`
+
+Input type: `IssueLinkAlertsInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationissuelinkalertsalertreferences"></a>`alertReferences` | [`[String!]!`](#string) | Alerts references to be linked to the incident. |
+| <a id="mutationissuelinkalertsclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationissuelinkalertsiid"></a>`iid` | [`String!`](#string) | IID of the issue to mutate. |
+| <a id="mutationissuelinkalertsprojectpath"></a>`projectPath` | [`ID!`](#id) | Project the issue to mutate is in. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationissuelinkalertsclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationissuelinkalertserrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| <a id="mutationissuelinkalertsissue"></a>`issue` | [`Issue`](#issue) | Issue after mutation. |
+
### `Mutation.issueMove`
Input type: `IssueMoveInput`
@@ -3434,6 +3474,27 @@ Input type: `IssueSetWeightInput`
| <a id="mutationissuesetweighterrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationissuesetweightissue"></a>`issue` | [`Issue`](#issue) | Issue after mutation. |
+### `Mutation.issueUnlinkAlert`
+
+Input type: `IssueUnlinkAlertInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationissueunlinkalertalertid"></a>`alertId` | [`AlertManagementAlertID!`](#alertmanagementalertid) | Global ID of the alert to unlink from the incident. |
+| <a id="mutationissueunlinkalertclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationissueunlinkalertiid"></a>`iid` | [`String!`](#string) | IID of the issue to mutate. |
+| <a id="mutationissueunlinkalertprojectpath"></a>`projectPath` | [`ID!`](#id) | Project the issue to mutate is in. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationissueunlinkalertclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationissueunlinkalerterrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| <a id="mutationissueunlinkalertissue"></a>`issue` | [`Issue`](#issue) | Issue after mutation. |
+
### `Mutation.iterationCadenceCreate`
Input type: `IterationCadenceCreateInput`
@@ -4252,6 +4313,31 @@ Input type: `PipelineRetryInput`
| <a id="mutationpipelineretryerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationpipelineretrypipeline"></a>`pipeline` | [`Pipeline`](#pipeline) | Pipeline after mutation. |
+### `Mutation.pipelineScheduleCreate`
+
+Input type: `PipelineScheduleCreateInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationpipelineschedulecreateactive"></a>`active` | [`Boolean`](#boolean) | Indicates if the pipeline schedule should be active or not. |
+| <a id="mutationpipelineschedulecreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationpipelineschedulecreatecron"></a>`cron` | [`String!`](#string) | Cron expression of the pipeline schedule. |
+| <a id="mutationpipelineschedulecreatecrontimezone"></a>`cronTimezone` | [`String`](#string) | Cron time zone supported by ActiveSupport::TimeZone. For example: "Pacific Time (US & Canada)" (default: "UTC"). |
+| <a id="mutationpipelineschedulecreatedescription"></a>`description` | [`String!`](#string) | Description of the pipeline schedule. |
+| <a id="mutationpipelineschedulecreateprojectpath"></a>`projectPath` | [`ID!`](#id) | Full path of the project the pipeline schedule is associated with. |
+| <a id="mutationpipelineschedulecreateref"></a>`ref` | [`String!`](#string) | Ref of the pipeline schedule. |
+| <a id="mutationpipelineschedulecreatevariables"></a>`variables` | [`[PipelineScheduleVariableInput!]`](#pipelineschedulevariableinput) | Variables for the pipeline schedule. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationpipelineschedulecreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationpipelineschedulecreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| <a id="mutationpipelineschedulecreatepipelineschedule"></a>`pipelineSchedule` | [`PipelineSchedule`](#pipelineschedule) | Created pipeline schedule. |
+
### `Mutation.pipelineScheduleDelete`
Input type: `PipelineScheduleDeleteInput`
@@ -4270,6 +4356,25 @@ Input type: `PipelineScheduleDeleteInput`
| <a id="mutationpipelinescheduledeleteclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationpipelinescheduledeleteerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+### `Mutation.pipelineSchedulePlay`
+
+Input type: `PipelineSchedulePlayInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationpipelinescheduleplayclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationpipelinescheduleplayid"></a>`id` | [`CiPipelineScheduleID!`](#cipipelinescheduleid) | ID of the pipeline schedule to mutate. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationpipelinescheduleplayclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationpipelinescheduleplayerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| <a id="mutationpipelinescheduleplaypipelineschedule"></a>`pipelineSchedule` | [`PipelineSchedule`](#pipelineschedule) | Pipeline schedule after mutation. |
+
### `Mutation.pipelineScheduleTakeOwnership`
Input type: `PipelineScheduleTakeOwnershipInput`
@@ -4313,6 +4418,25 @@ Input type: `ProjectCiCdSettingsUpdateInput`
| <a id="mutationprojectcicdsettingsupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationprojectcicdsettingsupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+### `Mutation.projectInitializeProductAnalytics`
+
+Input type: `ProjectInitializeProductAnalyticsInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationprojectinitializeproductanalyticsclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationprojectinitializeproductanalyticsprojectpath"></a>`projectPath` | [`ID!`](#id) | Full path of the project to initialize. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationprojectinitializeproductanalyticsclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationprojectinitializeproductanalyticserrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| <a id="mutationprojectinitializeproductanalyticsproject"></a>`project` | [`Project`](#project) | Project on which the initialization took place. |
+
### `Mutation.projectSetComplianceFramework`
Assign (or unset) a compliance framework to a project.
@@ -5048,6 +5172,7 @@ Input type: `TimelineEventUpdateInput`
| <a id="mutationtimelineeventupdateid"></a>`id` | [`IncidentManagementTimelineEventID!`](#incidentmanagementtimelineeventid) | ID of the timeline event to update. |
| <a id="mutationtimelineeventupdatenote"></a>`note` | [`String`](#string) | Text note of the timeline event. |
| <a id="mutationtimelineeventupdateoccurredat"></a>`occurredAt` | [`Time`](#time) | Timestamp when the event occurred. |
+| <a id="mutationtimelineeventupdatetimelineeventtagnames"></a>`timelineEventTagNames` | [`[String!]`](#string) | Tags for the incident timeline event. |
#### Fields
@@ -5067,7 +5192,7 @@ Input type: `TimelogCreateInput`
| ---- | ---- | ----------- |
| <a id="mutationtimelogcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationtimelogcreateissuableid"></a>`issuableId` | [`IssuableID!`](#issuableid) | Global ID of the issuable (Issue, WorkItem or MergeRequest). |
-| <a id="mutationtimelogcreatespentat"></a>`spentAt` | [`Date!`](#date) | When the time was spent. |
+| <a id="mutationtimelogcreatespentat"></a>`spentAt` | [`Time!`](#time) | When the time was spent. |
| <a id="mutationtimelogcreatesummary"></a>`summary` | [`String!`](#string) | Summary of time spent. |
| <a id="mutationtimelogcreatetimespent"></a>`timeSpent` | [`String!`](#string) | Amount of time spent. |
@@ -5757,7 +5882,7 @@ Input type: `VulnerabilityDismissInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationvulnerabilitydismissclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
-| <a id="mutationvulnerabilitydismisscomment"></a>`comment` | [`String`](#string) | Comment why vulnerability should be dismissed. |
+| <a id="mutationvulnerabilitydismisscomment"></a>`comment` | [`String`](#string) | Comment why vulnerability should be dismissed (max. 50 000 characters). |
| <a id="mutationvulnerabilitydismissdismissalreason"></a>`dismissalReason` | [`VulnerabilityDismissalReason`](#vulnerabilitydismissalreason) | Reason why vulnerability should be dismissed. |
| <a id="mutationvulnerabilitydismissid"></a>`id` | [`VulnerabilityID!`](#vulnerabilityid) | ID of the vulnerability to be dismissed. |
@@ -5890,6 +6015,7 @@ Input type: `WorkItemCreateInput`
| <a id="mutationworkitemcreateconfidential"></a>`confidential` | [`Boolean`](#boolean) | Sets the work item confidentiality. |
| <a id="mutationworkitemcreatedescription"></a>`description` | [`String`](#string) | Description of the work item. |
| <a id="mutationworkitemcreatehierarchywidget"></a>`hierarchyWidget` | [`WorkItemWidgetHierarchyCreateInput`](#workitemwidgethierarchycreateinput) | Input for hierarchy widget. |
+| <a id="mutationworkitemcreateiterationwidget"></a>`iterationWidget` | [`WorkItemWidgetIterationInput`](#workitemwidgetiterationinput) | Iteration widget of the work item. |
| <a id="mutationworkitemcreatemilestonewidget"></a>`milestoneWidget` | [`WorkItemWidgetMilestoneInput`](#workitemwidgetmilestoneinput) | Input for milestone widget. |
| <a id="mutationworkitemcreateprojectpath"></a>`projectPath` | [`ID!`](#id) | Full path of the project the work item is associated with. |
| <a id="mutationworkitemcreatetitle"></a>`title` | [`String!`](#string) | Title of the work item. |
@@ -6000,11 +6126,13 @@ Input type: `WorkItemUpdateInput`
| <a id="mutationworkitemupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationworkitemupdateconfidential"></a>`confidential` | [`Boolean`](#boolean) | Sets the work item confidentiality. |
| <a id="mutationworkitemupdatedescriptionwidget"></a>`descriptionWidget` | [`WorkItemWidgetDescriptionInput`](#workitemwidgetdescriptioninput) | Input for description widget. |
+| <a id="mutationworkitemupdatehealthstatuswidget"></a>`healthStatusWidget` | [`WorkItemWidgetHealthStatusInput`](#workitemwidgethealthstatusinput) | Input for health status widget. |
| <a id="mutationworkitemupdatehierarchywidget"></a>`hierarchyWidget` | [`WorkItemWidgetHierarchyUpdateInput`](#workitemwidgethierarchyupdateinput) | Input for hierarchy widget. |
| <a id="mutationworkitemupdateid"></a>`id` | [`WorkItemID!`](#workitemid) | Global ID of the work item. |
| <a id="mutationworkitemupdateiterationwidget"></a>`iterationWidget` | [`WorkItemWidgetIterationInput`](#workitemwidgetiterationinput) | Input for iteration widget. |
| <a id="mutationworkitemupdatelabelswidget"></a>`labelsWidget` | [`WorkItemWidgetLabelsUpdateInput`](#workitemwidgetlabelsupdateinput) | Input for labels widget. |
| <a id="mutationworkitemupdatemilestonewidget"></a>`milestoneWidget` | [`WorkItemWidgetMilestoneInput`](#workitemwidgetmilestoneinput) | Input for milestone widget. |
+| <a id="mutationworkitemupdateprogresswidget"></a>`progressWidget` | [`WorkItemWidgetProgressInput`](#workitemwidgetprogressinput) | Input for progress widget. |
| <a id="mutationworkitemupdatestartandduedatewidget"></a>`startAndDueDateWidget` | [`WorkItemWidgetStartAndDueDateUpdateInput`](#workitemwidgetstartandduedateupdateinput) | Input for start and due date widget. |
| <a id="mutationworkitemupdatestateevent"></a>`stateEvent` | [`WorkItemStateEvent`](#workitemstateevent) | Close or reopen a work item. |
| <a id="mutationworkitemupdatestatuswidget"></a>`statusWidget` | [`StatusInput`](#statusinput) | Input for status widget. |
@@ -7329,6 +7457,29 @@ The edge type for [`DependencyProxyManifest`](#dependencyproxymanifest).
| <a id="dependencyproxymanifestedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="dependencyproxymanifestedgenode"></a>`node` | [`DependencyProxyManifest`](#dependencyproxymanifest) | The item at the end of the edge. |
+#### `DependencyProxyManifestRegistryConnection`
+
+The connection type for [`DependencyProxyManifestRegistry`](#dependencyproxymanifestregistry).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="dependencyproxymanifestregistryconnectionedges"></a>`edges` | [`[DependencyProxyManifestRegistryEdge]`](#dependencyproxymanifestregistryedge) | A list of edges. |
+| <a id="dependencyproxymanifestregistryconnectionnodes"></a>`nodes` | [`[DependencyProxyManifestRegistry]`](#dependencyproxymanifestregistry) | A list of nodes. |
+| <a id="dependencyproxymanifestregistryconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `DependencyProxyManifestRegistryEdge`
+
+The edge type for [`DependencyProxyManifestRegistry`](#dependencyproxymanifestregistry).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="dependencyproxymanifestregistryedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| <a id="dependencyproxymanifestregistryedgenode"></a>`node` | [`DependencyProxyManifestRegistry`](#dependencyproxymanifestregistry) | The item at the end of the edge. |
+
#### `DeploymentConnection`
The connection type for [`Deployment`](#deployment).
@@ -8326,6 +8477,29 @@ The edge type for [`Namespace`](#namespace).
| <a id="namespaceedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="namespaceedgenode"></a>`node` | [`Namespace`](#namespace) | The item at the end of the edge. |
+#### `NestedEnvironmentConnection`
+
+The connection type for [`NestedEnvironment`](#nestedenvironment).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="nestedenvironmentconnectionedges"></a>`edges` | [`[NestedEnvironmentEdge]`](#nestedenvironmentedge) | A list of edges. |
+| <a id="nestedenvironmentconnectionnodes"></a>`nodes` | [`[NestedEnvironment]`](#nestedenvironment) | A list of nodes. |
+| <a id="nestedenvironmentconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `NestedEnvironmentEdge`
+
+The edge type for [`NestedEnvironment`](#nestedenvironment).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="nestedenvironmentedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| <a id="nestedenvironmentedgenode"></a>`node` | [`NestedEnvironment`](#nestedenvironment) | The item at the end of the edge. |
+
#### `NetworkPolicyConnection`
The connection type for [`NetworkPolicy`](#networkpolicy).
@@ -8652,6 +8826,29 @@ The edge type for [`PipelineSchedule`](#pipelineschedule).
| <a id="pipelinescheduleedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="pipelinescheduleedgenode"></a>`node` | [`PipelineSchedule`](#pipelineschedule) | The item at the end of the edge. |
+#### `PipelineScheduleVariableConnection`
+
+The connection type for [`PipelineScheduleVariable`](#pipelineschedulevariable).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="pipelineschedulevariableconnectionedges"></a>`edges` | [`[PipelineScheduleVariableEdge]`](#pipelineschedulevariableedge) | A list of edges. |
+| <a id="pipelineschedulevariableconnectionnodes"></a>`nodes` | [`[PipelineScheduleVariable]`](#pipelineschedulevariable) | A list of nodes. |
+| <a id="pipelineschedulevariableconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `PipelineScheduleVariableEdge`
+
+The edge type for [`PipelineScheduleVariable`](#pipelineschedulevariable).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="pipelineschedulevariableedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| <a id="pipelineschedulevariableedgenode"></a>`node` | [`PipelineScheduleVariable`](#pipelineschedulevariable) | The item at the end of the edge. |
+
#### `PipelineSecurityReportFindingConnection`
The connection type for [`PipelineSecurityReportFinding`](#pipelinesecurityreportfinding).
@@ -10087,6 +10284,7 @@ Describes an alert from the project's Alert Management.
| <a id="alertmanagementalertenvironment"></a>`environment` | [`Environment`](#environment) | Environment for the alert. |
| <a id="alertmanagementalerteventcount"></a>`eventCount` | [`Int`](#int) | Number of events of this alert. |
| <a id="alertmanagementalerthosts"></a>`hosts` | [`[String!]`](#string) | List of hosts the alert came from. |
+| <a id="alertmanagementalertid"></a>`id` | [`ID!`](#id) | ID of the alert. |
| <a id="alertmanagementalertiid"></a>`iid` | [`ID!`](#id) | Internal ID of the alert. |
| <a id="alertmanagementalertissue"></a>`issue` | [`Issue`](#issue) | Issue attached to the alert. |
| <a id="alertmanagementalertissueiid"></a>`issueIid` **{warning-solid}** | [`ID`](#id) | **Deprecated** in 13.10. Use issue field. |
@@ -10787,6 +10985,21 @@ CI/CD config variables.
| <a id="ciconfigvariablevalue"></a>`value` | [`String`](#string) | Value of the variable. |
| <a id="ciconfigvariablevalueoptions"></a>`valueOptions` | [`[String!]`](#string) | Value options for the variable. |
+### `CiFreezePeriod`
+
+Represents a deployment freeze window of a project.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="cifreezeperiodcrontimezone"></a>`cronTimezone` | [`String`](#string) | Time zone for the cron fields, defaults to UTC if not provided. |
+| <a id="cifreezeperiodendcron"></a>`endCron` | [`String!`](#string) | End of the freeze period in cron format. |
+| <a id="cifreezeperiodendtime"></a>`endTime` | [`Time`](#time) | Timestamp (UTC) of when the current/next active period ends. |
+| <a id="cifreezeperiodstartcron"></a>`startCron` | [`String!`](#string) | Start of the freeze period in cron format. |
+| <a id="cifreezeperiodstarttime"></a>`startTime` | [`Time`](#time) | Timestamp (UTC) of when the current/next active period starts. |
+| <a id="cifreezeperiodstatus"></a>`status` | [`CiFreezePeriodStatus!`](#cifreezeperiodstatus) | Freeze period status. |
+
### `CiGroup`
#### Fields
@@ -10970,10 +11183,11 @@ CI/CD variables for a project.
| <a id="cirunnerdescription"></a>`description` | [`String`](#string) | Description of the runner. |
| <a id="cirunnereditadminurl"></a>`editAdminUrl` | [`String`](#string) | Admin form URL of the runner. Only available for administrators. |
| <a id="cirunnerexecutorname"></a>`executorName` | [`String`](#string) | Executor last advertised by the runner. |
-| <a id="cirunnergroups"></a>`groups` | [`GroupConnection`](#groupconnection) | Groups the runner is associated with. For group runners only. (see [Connections](#connections)) |
+| <a id="cirunnergroups"></a>`groups` | [`GroupConnection`](#groupconnection) | Types::GroupConnection. (see [Connections](#connections)) |
| <a id="cirunnerid"></a>`id` | [`CiRunnerID!`](#cirunnerid) | ID of the runner. |
| <a id="cirunneripaddress"></a>`ipAddress` | [`String`](#string) | IP address of the runner. |
| <a id="cirunnerjobcount"></a>`jobCount` | [`Int`](#int) | Number of jobs processed by the runner (limited to 1000, plus one to indicate that more items exist). |
+| <a id="cirunnerjobexecutionstatus"></a>`jobExecutionStatus` **{warning-solid}** | [`CiRunnerJobExecutionStatus`](#cirunnerjobexecutionstatus) | **Introduced** in 15.7. This feature is in Alpha. It can be changed or removed at any time. Job execution status of the runner. |
| <a id="cirunnerlocked"></a>`locked` | [`Boolean`](#boolean) | Indicates the runner is locked. |
| <a id="cirunnermaintenancenote"></a>`maintenanceNote` | [`String`](#string) | Runner's maintenance notes. |
| <a id="cirunnermaintenancenotehtml"></a>`maintenanceNoteHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `maintenance_note`. |
@@ -11644,8 +11858,8 @@ Represents a DAST Site Profile.
| <a id="dastsiteprofileprofilename"></a>`profileName` | [`String`](#string) | Name of the site profile. |
| <a id="dastsiteprofilereferencedinsecuritypolicies"></a>`referencedInSecurityPolicies` | [`[String!]`](#string) | List of security policy names that are referencing given project. |
| <a id="dastsiteprofilerequestheaders"></a>`requestHeaders` | [`String`](#string) | Comma-separated list of request header names and values to be added to every request made by DAST. |
-| <a id="dastsiteprofilescanfilepath"></a>`scanFilePath` | [`String`](#string) | Scan File Path used as input for the scanner. Will always return `null` if `dast_api_scanner` feature flag is disabled. |
-| <a id="dastsiteprofilescanmethod"></a>`scanMethod` | [`DastScanMethodType`](#dastscanmethodtype) | Scan method used by the scanner. Always returns `null` if `dast_api_scanner` feature flag is disabled. |
+| <a id="dastsiteprofilescanfilepath"></a>`scanFilePath` | [`String`](#string) | Scan File Path used as input for the scanner. |
+| <a id="dastsiteprofilescanmethod"></a>`scanMethod` | [`DastScanMethodType`](#dastscanmethodtype) | Scan method used by the scanner. |
| <a id="dastsiteprofiletargettype"></a>`targetType` | [`DastTargetTypeEnum`](#dasttargettypeenum) | Type of target to be scanned. |
| <a id="dastsiteprofiletargeturl"></a>`targetUrl` | [`String`](#string) | URL of the target to be scanned. |
| <a id="dastsiteprofileuserpermissions"></a>`userPermissions` | [`DastSiteProfilePermissions!`](#dastsiteprofilepermissions) | Permissions for the current user on the resource. |
@@ -11765,6 +11979,25 @@ Dependency proxy manifest.
| <a id="dependencyproxymanifeststatus"></a>`status` | [`DependencyProxyManifestStatus!`](#dependencyproxymanifeststatus) | Status of the manifest (default, pending_destruction, processing, error). |
| <a id="dependencyproxymanifestupdatedat"></a>`updatedAt` | [`Time!`](#time) | Date of most recent update. |
+### `DependencyProxyManifestRegistry`
+
+Represents the Geo replication and verification state of a dependency_proxy_manifest.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="dependencyproxymanifestregistrycreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp when the DependencyProxyManifestRegistry was created. |
+| <a id="dependencyproxymanifestregistrydependencyproxymanifestid"></a>`dependencyProxyManifestId` | [`ID!`](#id) | ID of the Dependency Proxy Manifest. |
+| <a id="dependencyproxymanifestregistryid"></a>`id` | [`ID!`](#id) | ID of the DependencyProxyManifestRegistry. |
+| <a id="dependencyproxymanifestregistrylastsyncfailure"></a>`lastSyncFailure` | [`String`](#string) | Error message during sync of the DependencyProxyManifestRegistry. |
+| <a id="dependencyproxymanifestregistrylastsyncedat"></a>`lastSyncedAt` | [`Time`](#time) | Timestamp of the most recent successful sync of the DependencyProxyManifestRegistry. |
+| <a id="dependencyproxymanifestregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the DependencyProxyManifestRegistry is resynced. |
+| <a id="dependencyproxymanifestregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the DependencyProxyManifestRegistry. |
+| <a id="dependencyproxymanifestregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the DependencyProxyManifestRegistry. |
+| <a id="dependencyproxymanifestregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the DependencyProxyManifestRegistry is reverified. |
+| <a id="dependencyproxymanifestregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the DependencyProxyManifestRegistry. |
+
### `DependencyProxySetting`
Group-level Dependency Proxy settings.
@@ -11783,6 +12016,7 @@ The deployment of an environment.
| Name | Type | Description |
| ---- | ---- | ----------- |
+| <a id="deploymentapprovalsummary"></a>`approvalSummary` | [`DeploymentApprovalSummary`](#deploymentapprovalsummary) | Approval summary of the deployment.This field can only be resolved for one deployment in any single request. |
| <a id="deploymentcommit"></a>`commit` | [`Commit`](#commit) | Commit details of the deployment. |
| <a id="deploymentcreatedat"></a>`createdAt` | [`Time`](#time) | When the deployment record was created. |
| <a id="deploymentfinishedat"></a>`finishedAt` | [`Time`](#time) | When the deployment finished. |
@@ -11793,8 +12027,10 @@ The deployment of an environment.
| <a id="deploymentsha"></a>`sha` | [`String`](#string) | Git-SHA that the deployment ran on. |
| <a id="deploymentstatus"></a>`status` | [`DeploymentStatus`](#deploymentstatus) | Status of the deployment. |
| <a id="deploymenttag"></a>`tag` | [`Boolean`](#boolean) | True or false if the deployment ran on a Git-tag. |
+| <a id="deploymenttags"></a>`tags` | [`[DeploymentTag!]`](#deploymenttag) | Git tags that contain this deployment. This field can only be resolved for one deployment in any single request. |
| <a id="deploymenttriggerer"></a>`triggerer` | [`UserCore`](#usercore) | User who executed the deployment. |
| <a id="deploymentupdatedat"></a>`updatedAt` | [`Time`](#time) | When the deployment record was updated. |
+| <a id="deploymentuserpermissions"></a>`userPermissions` | [`DeploymentPermissions!`](#deploymentpermissions) | Permissions for the current user on the resource. |
### `DeploymentApproval`
@@ -11823,28 +12059,15 @@ Approval summary of the deployment.
| <a id="deploymentapprovalsummarytotalpendingapprovalcount"></a>`totalPendingApprovalCount` | [`Int`](#int) | Total pending approval count. |
| <a id="deploymentapprovalsummarytotalrequiredapprovals"></a>`totalRequiredApprovals` | [`Int`](#int) | Total number of required approvals. |
-### `DeploymentDetails`
-
-The details of the deployment.
+### `DeploymentPermissions`
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
-| <a id="deploymentdetailsapprovalsummary"></a>`approvalSummary` | [`DeploymentApprovalSummary`](#deploymentapprovalsummary) | Approval summary of the deployment. |
-| <a id="deploymentdetailscommit"></a>`commit` | [`Commit`](#commit) | Commit details of the deployment. |
-| <a id="deploymentdetailscreatedat"></a>`createdAt` | [`Time`](#time) | When the deployment record was created. |
-| <a id="deploymentdetailsfinishedat"></a>`finishedAt` | [`Time`](#time) | When the deployment finished. |
-| <a id="deploymentdetailsid"></a>`id` | [`ID`](#id) | Global ID of the deployment. |
-| <a id="deploymentdetailsiid"></a>`iid` | [`ID`](#id) | Project-level internal ID of the deployment. |
-| <a id="deploymentdetailsjob"></a>`job` | [`CiJob`](#cijob) | Pipeline job of the deployment. |
-| <a id="deploymentdetailsref"></a>`ref` | [`String`](#string) | Git-Ref that the deployment ran on. |
-| <a id="deploymentdetailssha"></a>`sha` | [`String`](#string) | Git-SHA that the deployment ran on. |
-| <a id="deploymentdetailsstatus"></a>`status` | [`DeploymentStatus`](#deploymentstatus) | Status of the deployment. |
-| <a id="deploymentdetailstag"></a>`tag` | [`Boolean`](#boolean) | True or false if the deployment ran on a Git-tag. |
-| <a id="deploymentdetailstags"></a>`tags` | [`[DeploymentTag!]`](#deploymenttag) | Git tags that contain this deployment. |
-| <a id="deploymentdetailstriggerer"></a>`triggerer` | [`UserCore`](#usercore) | User who executed the deployment. |
-| <a id="deploymentdetailsupdatedat"></a>`updatedAt` | [`Time`](#time) | When the deployment record was updated. |
+| <a id="deploymentpermissionsapprovedeployment"></a>`approveDeployment` | [`Boolean!`](#boolean) | Indicates the user can perform `approve_deployment` on this resource. This field can only be resolved for one environment in any single request. |
+| <a id="deploymentpermissionsdestroydeployment"></a>`destroyDeployment` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_deployment` on this resource. |
+| <a id="deploymentpermissionsupdatedeployment"></a>`updateDeployment` | [`Boolean!`](#boolean) | Indicates the user can perform `update_deployment` on this resource. |
### `DeploymentTag`
@@ -12283,6 +12506,7 @@ Describes where code is deployed for a project.
| <a id="environmentautodeleteat"></a>`autoDeleteAt` | [`Time`](#time) | When the environment is going to be deleted automatically. |
| <a id="environmentautostopat"></a>`autoStopAt` | [`Time`](#time) | When the environment is going to be stopped automatically. |
| <a id="environmentcreatedat"></a>`createdAt` | [`Time`](#time) | When the environment was created. |
+| <a id="environmentdeployfreezes"></a>`deployFreezes` | [`[CiFreezePeriod!]`](#cifreezeperiod) | Deployment freeze periods of the environment. |
| <a id="environmentenvironmenttype"></a>`environmentType` | [`String`](#string) | Folder name of the environment. |
| <a id="environmentexternalurl"></a>`externalUrl` | [`String`](#string) | External URL of the environment. |
| <a id="environmentid"></a>`id` | [`ID!`](#id) | ID of the environment. |
@@ -12294,6 +12518,7 @@ Describes where code is deployed for a project.
| <a id="environmentstate"></a>`state` | [`String!`](#string) | State of the environment, for example: available/stopped. |
| <a id="environmenttier"></a>`tier` | [`DeploymentTier`](#deploymenttier) | Deployment tier of the environment. |
| <a id="environmentupdatedat"></a>`updatedAt` | [`Time`](#time) | When the environment was updated. |
+| <a id="environmentuserpermissions"></a>`userPermissions` | [`EnvironmentPermissions!`](#environmentpermissions) | Permissions for the current user on the resource. This field can only be resolved for one environment in any single request. |
#### Fields with arguments
@@ -12338,6 +12563,16 @@ Returns [`MetricsDashboard`](#metricsdashboard).
| ---- | ---- | ----------- |
| <a id="environmentmetricsdashboardpath"></a>`path` | [`String!`](#string) | Path to a file which defines a metrics dashboard eg: `"config/prometheus/common_metrics.yml"`. |
+### `EnvironmentPermissions`
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="environmentpermissionsdestroyenvironment"></a>`destroyEnvironment` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_environment` on this resource. |
+| <a id="environmentpermissionsstopenvironment"></a>`stopEnvironment` | [`Boolean!`](#boolean) | Indicates the user can perform `stop_environment` on this resource. |
+| <a id="environmentpermissionsupdateenvironment"></a>`updateEnvironment` | [`Boolean!`](#boolean) | Indicates the user can perform `update_environment` on this resource. |
+
### `Epic`
Represents an epic.
@@ -12628,6 +12863,7 @@ Relationship between an epic and an issue.
| <a id="epicissuenotes"></a>`notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) |
| <a id="epicissueparticipants"></a>`participants` | [`UserCoreConnection`](#usercoreconnection) | List of participants in the issue. (see [Connections](#connections)) |
| <a id="epicissueprojectid"></a>`projectId` | [`Int!`](#int) | ID of the issue project. |
+| <a id="epicissuerelatedvulnerabilities"></a>`relatedVulnerabilities` | [`VulnerabilityConnection`](#vulnerabilityconnection) | Related vulnerabilities of the issue. (see [Connections](#connections)) |
| <a id="epicissuerelationpath"></a>`relationPath` | [`String`](#string) | URI path of the epic-issue relation. |
| <a id="epicissuerelativeposition"></a>`relativePosition` | [`Int`](#int) | Relative position of the issue (used for positioning in epic tree and issue boards). |
| <a id="epicissueseverity"></a>`severity` | [`IssuableSeverity`](#issuableseverity) | Severity level of the incident. |
@@ -12876,6 +13112,17 @@ Describes an external status check.
| <a id="fileuploadpath"></a>`path` | [`String!`](#string) | Path of the upload. |
| <a id="fileuploadsize"></a>`size` | [`Int!`](#int) | Size of the upload in bytes. |
+### `ForkDetails`
+
+Details of the fork project compared to its upstream project.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="forkdetailsahead"></a>`ahead` | [`Int`](#int) | Number of commits ahead of upstream. |
+| <a id="forkdetailsbehind"></a>`behind` | [`Int`](#int) | Number of commits behind upstream. |
+
### `GeoNode`
#### Fields
@@ -12920,11 +13167,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
##### `GeoNode.containerRepositoryRegistries`
-Find Container Repository registries on this Geo node. Ignored if `geo_container_repository_replication` feature flag is disabled.
-
-WARNING:
-**Introduced** in 15.5.
-This feature is in Alpha. It can be changed or removed at any time.
+Find Container Repository registries on this Geo node.
Returns [`ContainerRepositoryRegistryConnection`](#containerrepositoryregistryconnection).
@@ -12962,6 +13205,28 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="geonodedependencyproxyblobregistriesreplicationstate"></a>`replicationState` | [`ReplicationStateEnum`](#replicationstateenum) | Filters registries by their replication state. |
| <a id="geonodedependencyproxyblobregistriesverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Filters registries by their verification state. |
+##### `GeoNode.dependencyProxyManifestRegistries`
+
+Find Dependency Proxy Manifest registries on this Geo node. Ignored if `geo_dependency_proxy_manifest_replication` feature flag is disabled.
+
+WARNING:
+**Introduced** in 15.6.
+This feature is in Alpha. It can be changed or removed at any time.
+
+Returns [`DependencyProxyManifestRegistryConnection`](#dependencyproxymanifestregistryconnection).
+
+This field returns a [connection](#connections). It accepts the
+four standard [pagination arguments](#connection-pagination-arguments):
+`before: String`, `after: String`, `first: Int`, `last: Int`.
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="geonodedependencyproxymanifestregistriesids"></a>`ids` | [`[ID!]`](#id) | Filters registries by their ID. |
+| <a id="geonodedependencyproxymanifestregistriesreplicationstate"></a>`replicationState` | [`ReplicationStateEnum`](#replicationstateenum) | Filters registries by their replication state. |
+| <a id="geonodedependencyproxymanifestregistriesverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Filters registries by their verification state. |
+
##### `GeoNode.groupWikiRepositoryRegistries`
Find group wiki repository registries on this Geo node.
@@ -13938,6 +14203,7 @@ Returns [`[VulnerableProjectsByGrade!]!`](#vulnerableprojectsbygrade).
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="groupvulnerabilitygradesincludesubgroups"></a>`includeSubgroups` | [`Boolean`](#boolean) | Include grades belonging to subgroups. |
+| <a id="groupvulnerabilitygradeslettergrade"></a>`letterGrade` | [`VulnerabilityGrade`](#vulnerabilitygrade) | Filter the response by given letter grade. |
##### `Group.vulnerabilitySeveritiesCount`
@@ -14165,7 +14431,6 @@ A block of time for which a participant is on-call.
| Name | Type | Description |
| ---- | ---- | ----------- |
-| <a id="instancesecuritydashboardvulnerabilitygrades"></a>`vulnerabilityGrades` | [`[VulnerableProjectsByGrade!]!`](#vulnerableprojectsbygrade) | Represents vulnerable project counts for each grade. |
| <a id="instancesecuritydashboardvulnerabilityscanners"></a>`vulnerabilityScanners` | [`VulnerabilityScannerConnection`](#vulnerabilityscannerconnection) | Vulnerability scanners reported on the vulnerabilities from projects selected in Instance Security Dashboard. (see [Connections](#connections)) |
#### Fields with arguments
@@ -14202,6 +14467,18 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="instancesecuritydashboardprojectssearch"></a>`search` | [`String`](#string) | Search query, which can be for the project name, a path, or a description. |
+##### `InstanceSecurityDashboard.vulnerabilityGrades`
+
+Represents vulnerable project counts for each grade.
+
+Returns [`[VulnerableProjectsByGrade!]!`](#vulnerableprojectsbygrade).
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="instancesecuritydashboardvulnerabilitygradeslettergrade"></a>`letterGrade` | [`VulnerabilityGrade`](#vulnerabilitygrade) | Filter the response by given letter grade. |
+
##### `InstanceSecurityDashboard.vulnerabilitySeveritiesCount`
Counts for each vulnerability severity from projects selected in Instance Security Dashboard.
@@ -14284,6 +14561,7 @@ Describes an issuable resource link for incident issues.
| <a id="issuenotes"></a>`notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) |
| <a id="issueparticipants"></a>`participants` | [`UserCoreConnection`](#usercoreconnection) | List of participants in the issue. (see [Connections](#connections)) |
| <a id="issueprojectid"></a>`projectId` | [`Int!`](#int) | ID of the issue project. |
+| <a id="issuerelatedvulnerabilities"></a>`relatedVulnerabilities` | [`VulnerabilityConnection`](#vulnerabilityconnection) | Related vulnerabilities of the issue. (see [Connections](#connections)) |
| <a id="issuerelativeposition"></a>`relativePosition` | [`Int`](#int) | Relative position of the issue (used for positioning in epic tree and issue boards). |
| <a id="issueseverity"></a>`severity` | [`IssuableSeverity`](#issuableseverity) | Severity level of the incident. |
| <a id="issuesladueat"></a>`slaDueAt` | [`Time`](#time) | Timestamp of when the issue SLA expires. |
@@ -14564,6 +14842,20 @@ Represents the Geo replication and verification state of a job_artifact.
| <a id="kasexternalurl"></a>`externalUrl` | [`String`](#string) | URL used by the Agents to communicate with KAS. |
| <a id="kasversion"></a>`version` | [`String`](#string) | KAS version. |
+### `Key`
+
+Represents an SSH key.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="keycreatedat"></a>`createdAt` | [`Time!`](#time) | Timestamp of when the key was created. |
+| <a id="keyexpiresat"></a>`expiresAt` | [`Time!`](#time) | Timestamp of when the key expires. It's null if it never expires. |
+| <a id="keyid"></a>`id` | [`ID!`](#id) | ID of the key. |
+| <a id="keykey"></a>`key` | [`String!`](#string) | Public key of the key pair. |
+| <a id="keytitle"></a>`title` | [`String!`](#string) | Title of the key. |
+
### `Label`
#### Fields
@@ -16014,6 +16306,18 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="namespacecicdsettingallowstalerunnerpruning"></a>`allowStaleRunnerPruning` | [`Boolean`](#boolean) | Indicates if stale runners directly belonging to this namespace should be periodically pruned. |
| <a id="namespacecicdsettingnamespace"></a>`namespace` | [`Namespace`](#namespace) | Namespace the CI/CD settings belong to. |
+### `NestedEnvironment`
+
+Describes where code is deployed for a project organized by folder.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="nestedenvironmentenvironment"></a>`environment` | [`Environment`](#environment) | Latest environment in the folder. |
+| <a id="nestedenvironmentname"></a>`name` | [`String!`](#string) | Human-readable name of the environment. |
+| <a id="nestedenvironmentsize"></a>`size` | [`Int!`](#int) | Number of environments nested in the folder. |
+
### `NetworkPolicy`
Represents the network policy.
@@ -16643,23 +16947,31 @@ Represents pipeline counts for the project.
### `PipelineSchedule`
+Represents a pipeline schedule.
+
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
-| <a id="pipelinescheduleactive"></a>`active` | [`Boolean!`](#boolean) | Indicates if a pipeline schedule is active. |
+| <a id="pipelinescheduleactive"></a>`active` | [`Boolean!`](#boolean) | Indicates if the pipeline schedule is active. |
+| <a id="pipelineschedulecreatedat"></a>`createdAt` | [`Time!`](#time) | Timestamp of when the pipeline schedule was created. |
| <a id="pipelineschedulecron"></a>`cron` | [`String!`](#string) | Cron notation for the schedule. |
| <a id="pipelineschedulecrontimezone"></a>`cronTimezone` | [`String!`](#string) | Timezone for the pipeline schedule. |
| <a id="pipelinescheduledescription"></a>`description` | [`String`](#string) | Description of the pipeline schedule. |
+| <a id="pipelinescheduleeditpath"></a>`editPath` | [`String`](#string) | Edit path of the pipeline schedule. |
| <a id="pipelineschedulefortag"></a>`forTag` | [`Boolean!`](#boolean) | Indicates if a pipelines schedule belongs to a tag. |
| <a id="pipelinescheduleid"></a>`id` | [`ID!`](#id) | ID of the pipeline schedule. |
| <a id="pipelineschedulelastpipeline"></a>`lastPipeline` | [`Pipeline`](#pipeline) | Last pipeline object. |
| <a id="pipelineschedulenextrunat"></a>`nextRunAt` | [`Time!`](#time) | Time when the next pipeline will run. |
| <a id="pipelinescheduleowner"></a>`owner` | [`UserCore!`](#usercore) | Owner of the pipeline schedule. |
+| <a id="pipelinescheduleproject"></a>`project` | [`Project`](#project) | Project of the pipeline schedule. |
| <a id="pipelineschedulerealnextrun"></a>`realNextRun` | [`Time!`](#time) | Time when the next pipeline will run. |
+| <a id="pipelinescheduleref"></a>`ref` | [`String`](#string) | Ref of the pipeline schedule. |
| <a id="pipelineschedulereffordisplay"></a>`refForDisplay` | [`String`](#string) | Git ref for the pipeline schedule. |
| <a id="pipelineschedulerefpath"></a>`refPath` | [`String`](#string) | Path to the ref that triggered the pipeline. |
+| <a id="pipelinescheduleupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of when the pipeline schedule was last updated. |
| <a id="pipelinescheduleuserpermissions"></a>`userPermissions` | [`PipelineSchedulePermissions!`](#pipelineschedulepermissions) | Permissions for the current user on the resource. |
+| <a id="pipelineschedulevariables"></a>`variables` | [`PipelineScheduleVariableConnection`](#pipelineschedulevariableconnection) | Pipeline schedule variables. (see [Connections](#connections)) |
### `PipelineSchedulePermissions`
@@ -16672,6 +16984,18 @@ Represents pipeline counts for the project.
| <a id="pipelineschedulepermissionstakeownershippipelineschedule"></a>`takeOwnershipPipelineSchedule` | [`Boolean!`](#boolean) | Indicates the user can perform `take_ownership_pipeline_schedule` on this resource. |
| <a id="pipelineschedulepermissionsupdatepipelineschedule"></a>`updatePipelineSchedule` | [`Boolean!`](#boolean) | Indicates the user can perform `update_pipeline_schedule` on this resource. |
+### `PipelineScheduleVariable`
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="pipelineschedulevariableid"></a>`id` | [`ID!`](#id) | ID of the variable. |
+| <a id="pipelineschedulevariablekey"></a>`key` | [`String`](#string) | Name of the variable. |
+| <a id="pipelineschedulevariableraw"></a>`raw` | [`Boolean`](#boolean) | Indicates whether the variable is raw. |
+| <a id="pipelineschedulevariablevalue"></a>`value` | [`String`](#string) | Value of the variable. |
+| <a id="pipelineschedulevariablevariabletype"></a>`variableType` | [`CiVariableType`](#civariabletype) | Type of the variable. |
+
### `PipelineSecurityReportFinding`
Represents vulnerability finding of a security report on the pipeline.
@@ -16696,10 +17020,11 @@ Represents vulnerability finding of a security report on the pipeline.
| <a id="pipelinesecurityreportfindingreporttype"></a>`reportType` | [`VulnerabilityReportType`](#vulnerabilityreporttype) | Type of the security report that found the vulnerability finding. |
| <a id="pipelinesecurityreportfindingscanner"></a>`scanner` | [`VulnerabilityScanner`](#vulnerabilityscanner) | Scanner metadata for the vulnerability. |
| <a id="pipelinesecurityreportfindingseverity"></a>`severity` | [`VulnerabilitySeverity`](#vulnerabilityseverity) | Severity of the vulnerability finding. |
-| <a id="pipelinesecurityreportfindingsolution"></a>`solution` | [`String`](#string) | URL to the vulnerability's details page. |
+| <a id="pipelinesecurityreportfindingsolution"></a>`solution` | [`String`](#string) | Solution for resolving the security report finding. |
| <a id="pipelinesecurityreportfindingstate"></a>`state` | [`VulnerabilityState`](#vulnerabilitystate) | Finding status. |
| <a id="pipelinesecurityreportfindingtitle"></a>`title` | [`String`](#string) | Title of the vulnerability finding. |
-| <a id="pipelinesecurityreportfindinguuid"></a>`uuid` | [`String`](#string) | Name of the vulnerability finding. |
+| <a id="pipelinesecurityreportfindinguuid"></a>`uuid` | [`String`](#string) | UUIDv5 digest based on the vulnerability's report type, primary identifier, location, fingerprint, project identifier. |
+| <a id="pipelinesecurityreportfindingvulnerability"></a>`vulnerability` | [`Vulnerability`](#vulnerability) | Vulnerability related to the security report finding. |
### `PreviewBillableUserChange`
@@ -16707,9 +17032,9 @@ Represents vulnerability finding of a security report on the pipeline.
| Name | Type | Description |
| ---- | ---- | ----------- |
-| <a id="previewbillableuserchangehasoverage"></a>`hasOverage` | [`Boolean`](#boolean) | If the group has an overage after change. |
| <a id="previewbillableuserchangenewbillableusercount"></a>`newBillableUserCount` | [`Int`](#int) | Total number of billable users after change. |
| <a id="previewbillableuserchangeseatsinsubscription"></a>`seatsInSubscription` | [`Int`](#int) | Number of seats in subscription. |
+| <a id="previewbillableuserchangewillincreaseoverage"></a>`willIncreaseOverage` | [`Boolean`](#boolean) | If the group will have an increased overage after change. |
### `ProductAnalyticsDashboard`
@@ -16723,6 +17048,18 @@ Represents a product analytics dashboard.
| <a id="productanalyticsdashboardtitle"></a>`title` | [`String!`](#string) | Title of the dashboard. |
| <a id="productanalyticsdashboardwidgets"></a>`widgets` | [`ProductAnalyticsDashboardWidgetConnection!`](#productanalyticsdashboardwidgetconnection) | Widgets shown on the dashboard. (see [Connections](#connections)) |
+### `ProductAnalyticsDashboardVisualization`
+
+Represents a product analytics dashboard visualization.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="productanalyticsdashboardvisualizationdata"></a>`data` | [`JSON!`](#json) | Data of the visualization. |
+| <a id="productanalyticsdashboardvisualizationoptions"></a>`options` | [`JSON!`](#json) | Options of the visualization. |
+| <a id="productanalyticsdashboardvisualizationtype"></a>`type` | [`String!`](#string) | Type of the visualization. |
+
### `ProductAnalyticsDashboardWidget`
Represents a product analytics dashboard widget.
@@ -16733,6 +17070,7 @@ Represents a product analytics dashboard widget.
| ---- | ---- | ----------- |
| <a id="productanalyticsdashboardwidgetgridattributes"></a>`gridAttributes` | [`JSON`](#json) | Description of the position and size of the widget. |
| <a id="productanalyticsdashboardwidgettitle"></a>`title` | [`String!`](#string) | Title of the widget. |
+| <a id="productanalyticsdashboardwidgetvisualization"></a>`visualization` | [`ProductAnalyticsDashboardVisualization!`](#productanalyticsdashboardvisualization) | Visualization of the widget. |
### `Project`
@@ -16775,6 +17113,7 @@ Represents a product analytics dashboard widget.
| <a id="projectissuesenabled"></a>`issuesEnabled` | [`Boolean`](#boolean) | Indicates if Issues are enabled for the current user. |
| <a id="projectjiraimportstatus"></a>`jiraImportStatus` | [`String`](#string) | Status of Jira import background job of the project. |
| <a id="projectjiraimports"></a>`jiraImports` | [`JiraImportConnection`](#jiraimportconnection) | Jira imports into the project. (see [Connections](#connections)) |
+| <a id="projectjitsukey"></a>`jitsuKey` **{warning-solid}** | [`String`](#string) | **Introduced** in 15.7. This feature is in Alpha. It can be changed or removed at any time. Jitsu key assigned to the project. |
| <a id="projectjobsenabled"></a>`jobsEnabled` | [`Boolean`](#boolean) | Indicates if CI/CD pipeline jobs are enabled for the current user. |
| <a id="projectlanguages"></a>`languages` | [`[RepositoryLanguage!]`](#repositorylanguage) | Programming languages used in the project. |
| <a id="projectlastactivityat"></a>`lastActivityAt` | [`Time`](#time) | Timestamp of the project last activity. |
@@ -17088,7 +17427,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
Details of the deployment of the project.
-Returns [`DeploymentDetails`](#deploymentdetails).
+Returns [`Deployment`](#deployment).
###### Arguments
@@ -17109,10 +17448,11 @@ Returns [`Environment`](#environment).
| <a id="projectenvironmentname"></a>`name` | [`String`](#string) | Name of the environment. |
| <a id="projectenvironmentsearch"></a>`search` | [`String`](#string) | Search query for environment name. |
| <a id="projectenvironmentstates"></a>`states` | [`[String!]`](#string) | States of environments that should be included in result. |
+| <a id="projectenvironmenttype"></a>`type` | [`String`](#string) | Search query for environment type. |
##### `Project.environments`
-Environments of the project.
+Environments of the project. This field can only be resolved for one project in any single request.
Returns [`EnvironmentConnection`](#environmentconnection).
@@ -17127,6 +17467,23 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="projectenvironmentsname"></a>`name` | [`String`](#string) | Name of the environment. |
| <a id="projectenvironmentssearch"></a>`search` | [`String`](#string) | Search query for environment name. |
| <a id="projectenvironmentsstates"></a>`states` | [`[String!]`](#string) | States of environments that should be included in result. |
+| <a id="projectenvironmentstype"></a>`type` | [`String`](#string) | Search query for environment type. |
+
+##### `Project.forkDetails`
+
+Details of the fork project compared to its upstream project.
+
+WARNING:
+**Introduced** in 15.7.
+This feature is in Alpha. It can be changed or removed at any time.
+
+Returns [`ForkDetails`](#forkdetails).
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="projectforkdetailsref"></a>`ref` | [`String`](#string) | Ref of the fork. Default value is HEAD. |
##### `Project.forkTargets`
@@ -17447,6 +17804,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectjobsstatuses"></a>`statuses` | [`[CiJobStatus!]`](#cijobstatus) | Filter jobs by status. |
+| <a id="projectjobswithartifacts"></a>`withArtifacts` | [`Boolean`](#boolean) | Filter by artifacts presence. |
##### `Project.label`
@@ -17547,6 +17905,25 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="projectmilestonestimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
| <a id="projectmilestonestitle"></a>`title` | [`String`](#string) | Title of the milestone. |
+##### `Project.nestedEnvironments`
+
+Environments for this project with nested folders, can only be resolved for one project in any single request.
+
+Returns [`NestedEnvironmentConnection`](#nestedenvironmentconnection).
+
+This field returns a [connection](#connections). It accepts the
+four standard [pagination arguments](#connection-pagination-arguments):
+`before: String`, `after: String`, `first: Int`, `last: Int`.
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="projectnestedenvironmentsname"></a>`name` | [`String`](#string) | Name of the environment. |
+| <a id="projectnestedenvironmentssearch"></a>`search` | [`String`](#string) | Search query for environment name. |
+| <a id="projectnestedenvironmentsstates"></a>`states` | [`[String!]`](#string) | States of environments that should be included in result. |
+| <a id="projectnestedenvironmentstype"></a>`type` | [`String`](#string) | Search query for environment type. |
+
##### `Project.networkPolicies`
Network Policies of the project.
@@ -17730,12 +18107,14 @@ Returns [`Requirement`](#requirement).
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectrequirementauthorusername"></a>`authorUsername` | [`[String!]`](#string) | Filter requirements by author username. |
-| <a id="projectrequirementiid"></a>`iid` | [`ID`](#id) | IID of the requirement, e.g., "1". |
-| <a id="projectrequirementiids"></a>`iids` | [`[ID!]`](#id) | List of IIDs of requirements, e.g., `[1, 2]`. |
+| <a id="projectrequirementiid"></a>`iid` | [`ID`](#id) | IID of the requirement, for example, "1". |
+| <a id="projectrequirementiids"></a>`iids` | [`[ID!]`](#id) | List of IIDs of requirements, for example, `[1, 2]`. |
| <a id="projectrequirementlasttestreportstate"></a>`lastTestReportState` | [`RequirementStatusFilter`](#requirementstatusfilter) | State of latest requirement test report. |
| <a id="projectrequirementsearch"></a>`search` | [`String`](#string) | Search query for requirement title. |
| <a id="projectrequirementsort"></a>`sort` | [`Sort`](#sort) | List requirements by sort order. |
| <a id="projectrequirementstate"></a>`state` | [`RequirementState`](#requirementstate) | Filter requirements by state. |
+| <a id="projectrequirementworkitemiid"></a>`workItemIid` | [`ID`](#id) | IID of the requirement work item, for example, "1". |
+| <a id="projectrequirementworkitemiids"></a>`workItemIids` | [`[ID!]`](#id) | List of IIDs of requirement work items, for example, `[1, 2]`. |
##### `Project.requirements`
@@ -17752,12 +18131,37 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectrequirementsauthorusername"></a>`authorUsername` | [`[String!]`](#string) | Filter requirements by author username. |
-| <a id="projectrequirementsiid"></a>`iid` | [`ID`](#id) | IID of the requirement, e.g., "1". |
-| <a id="projectrequirementsiids"></a>`iids` | [`[ID!]`](#id) | List of IIDs of requirements, e.g., `[1, 2]`. |
+| <a id="projectrequirementsiid"></a>`iid` | [`ID`](#id) | IID of the requirement, for example, "1". |
+| <a id="projectrequirementsiids"></a>`iids` | [`[ID!]`](#id) | List of IIDs of requirements, for example, `[1, 2]`. |
| <a id="projectrequirementslasttestreportstate"></a>`lastTestReportState` | [`RequirementStatusFilter`](#requirementstatusfilter) | State of latest requirement test report. |
| <a id="projectrequirementssearch"></a>`search` | [`String`](#string) | Search query for requirement title. |
| <a id="projectrequirementssort"></a>`sort` | [`Sort`](#sort) | List requirements by sort order. |
| <a id="projectrequirementsstate"></a>`state` | [`RequirementState`](#requirementstate) | Filter requirements by state. |
+| <a id="projectrequirementsworkitemiid"></a>`workItemIid` | [`ID`](#id) | IID of the requirement work item, for example, "1". |
+| <a id="projectrequirementsworkitemiids"></a>`workItemIids` | [`[ID!]`](#id) | List of IIDs of requirement work items, for example, `[1, 2]`. |
+
+##### `Project.runners`
+
+Find runners visible to the current user.
+
+Returns [`CiRunnerConnection`](#cirunnerconnection).
+
+This field returns a [connection](#connections). It accepts the
+four standard [pagination arguments](#connection-pagination-arguments):
+`before: String`, `after: String`, `first: Int`, `last: Int`.
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="projectrunnersactive"></a>`active` **{warning-solid}** | [`Boolean`](#boolean) | **Deprecated** in 14.8. This was renamed. Use: `paused`. |
+| <a id="projectrunnerspaused"></a>`paused` | [`Boolean`](#boolean) | Filter runners by `paused` (true) or `active` (false) status. |
+| <a id="projectrunnerssearch"></a>`search` | [`String`](#string) | Filter by full token or partial text in description field. |
+| <a id="projectrunnerssort"></a>`sort` | [`CiRunnerSort`](#cirunnersort) | Sort order of results. |
+| <a id="projectrunnersstatus"></a>`status` | [`CiRunnerStatus`](#cirunnerstatus) | Filter runners by status. |
+| <a id="projectrunnerstaglist"></a>`tagList` | [`[String!]`](#string) | Filter by tags associated with the runner (comma-separated or array). |
+| <a id="projectrunnerstype"></a>`type` | [`CiRunnerType`](#cirunnertype) | Filter runners by type. |
+| <a id="projectrunnersupgradestatus"></a>`upgradeStatus` | [`CiRunnerUpgradeStatus`](#cirunnerupgradestatus) | Filter by upgrade status. |
##### `Project.scanExecutionPolicies`
@@ -18086,6 +18490,7 @@ Returns [`UserMergeRequestInteraction`](#usermergerequestinteraction).
| <a id="projectpermissionsreadcommitstatus"></a>`readCommitStatus` | [`Boolean!`](#boolean) | Indicates the user can perform `read_commit_status` on this resource. |
| <a id="projectpermissionsreadcycleanalytics"></a>`readCycleAnalytics` | [`Boolean!`](#boolean) | Indicates the user can perform `read_cycle_analytics` on this resource. |
| <a id="projectpermissionsreaddesign"></a>`readDesign` | [`Boolean!`](#boolean) | Indicates the user can perform `read_design` on this resource. |
+| <a id="projectpermissionsreadenvironment"></a>`readEnvironment` | [`Boolean!`](#boolean) | Indicates the user can perform `read_environment` on this resource. |
| <a id="projectpermissionsreadmergerequest"></a>`readMergeRequest` | [`Boolean!`](#boolean) | Indicates the user can perform `read_merge_request` on this resource. |
| <a id="projectpermissionsreadpagescontent"></a>`readPagesContent` | [`Boolean!`](#boolean) | Indicates the user can perform `read_pages_content` on this resource. |
| <a id="projectpermissionsreadproject"></a>`readProject` | [`Boolean!`](#boolean) | Indicates the user can perform `read_project` on this resource. |
@@ -18509,6 +18914,7 @@ Represents a requirement.
| <a id="requirementtitlehtml"></a>`titleHtml` | [`String`](#string) | GitLab Flavored Markdown rendering of `title`. |
| <a id="requirementupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of when the requirement was last updated. |
| <a id="requirementuserpermissions"></a>`userPermissions` | [`RequirementPermissions!`](#requirementpermissions) | Permissions for the current user on the resource. |
+| <a id="requirementworkitemiid"></a>`workItemIid` | [`ID!`](#id) | Work item IID of the requirement, will replace current IID as identifier soon. |
#### Fields with arguments
@@ -19075,6 +19481,20 @@ Represents the Geo sync and verification state of a snippet repository.
| <a id="snippetrepositoryregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the SnippetRepositoryRegistry is reverified. |
| <a id="snippetrepositoryregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the SnippetRepositoryRegistry. |
+### `SshSignature`
+
+SSH signature for a signed commit.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="sshsignaturecommitsha"></a>`commitSha` | [`String`](#string) | SHA of the associated commit. |
+| <a id="sshsignaturekey"></a>`key` | [`Key`](#key) | SSH key used for the signature. |
+| <a id="sshsignatureproject"></a>`project` | [`Project`](#project) | Project of the associated commit. |
+| <a id="sshsignatureuser"></a>`user` | [`UserCore`](#usercore) | User associated with the key. |
+| <a id="sshsignatureverificationstatus"></a>`verificationStatus` | [`VerificationStatus`](#verificationstatus) | Indicates verification status of the associated key or certificate. |
+
### `StatusAction`
#### Fields
@@ -20454,6 +20874,17 @@ Represents a description widget.
| <a id="workitemwidgetdescriptionlasteditedby"></a>`lastEditedBy` | [`UserCore`](#usercore) | User that made the last edit to the work item's description. |
| <a id="workitemwidgetdescriptiontype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. |
+### `WorkItemWidgetHealthStatus`
+
+Represents a health status widget.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="workitemwidgethealthstatushealthstatus"></a>`healthStatus` | [`HealthStatus`](#healthstatus) | Health status of the work item. |
+| <a id="workitemwidgethealthstatustype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. |
+
### `WorkItemWidgetHierarchy`
Represents a hierarchy widget.
@@ -20463,6 +20894,7 @@ Represents a hierarchy widget.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="workitemwidgethierarchychildren"></a>`children` | [`WorkItemConnection`](#workitemconnection) | Child work items. (see [Connections](#connections)) |
+| <a id="workitemwidgethierarchyhaschildren"></a>`hasChildren` | [`Boolean!`](#boolean) | Indicates if the work item has children. |
| <a id="workitemwidgethierarchyparent"></a>`parent` | [`WorkItem`](#workitem) | Parent work item. |
| <a id="workitemwidgethierarchytype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. |
@@ -20500,6 +20932,45 @@ Represents a milestone widget.
| <a id="workitemwidgetmilestonemilestone"></a>`milestone` | [`Milestone`](#milestone) | Milestone of the work item. |
| <a id="workitemwidgetmilestonetype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. |
+### `WorkItemWidgetNotes`
+
+Represents a notes widget.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="workitemwidgetnotestype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. |
+
+#### Fields with arguments
+
+##### `WorkItemWidgetNotes.discussions`
+
+Notes on this work item.
+
+Returns [`DiscussionConnection`](#discussionconnection).
+
+This field returns a [connection](#connections). It accepts the
+four standard [pagination arguments](#connection-pagination-arguments):
+`before: String`, `after: String`, `first: Int`, `last: Int`.
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="workitemwidgetnotesdiscussionsfilter"></a>`filter` | [`NotesFilterType`](#notesfiltertype) | Type of notes collection: ALL_NOTES, ONLY_COMMENTS, ONLY_ACTIVITY. |
+
+### `WorkItemWidgetProgress`
+
+Represents a progress widget.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="workitemwidgetprogressprogress"></a>`progress` | [`Int`](#int) | Progress of the work item. |
+| <a id="workitemwidgetprogresstype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. |
+
### `WorkItemWidgetStartAndDueDate`
Represents a start and due date widget.
@@ -20780,6 +21251,15 @@ Values for YAML processor result.
| <a id="ciconfigstatusinvalid"></a>`INVALID` | Configuration file is not valid. |
| <a id="ciconfigstatusvalid"></a>`VALID` | Configuration file is valid. |
+### `CiFreezePeriodStatus`
+
+Deploy freeze period status.
+
+| Value | Description |
+| ----- | ----------- |
+| <a id="cifreezeperiodstatusactive"></a>`ACTIVE` | Freeze period is active. |
+| <a id="cifreezeperiodstatusinactive"></a>`INACTIVE` | Freeze period is inactive. |
+
### `CiJobKind`
| Value | Description |
@@ -20810,6 +21290,13 @@ Values for YAML processor result.
| <a id="cirunneraccesslevelnot_protected"></a>`NOT_PROTECTED` | A runner that is not protected. |
| <a id="cirunneraccesslevelref_protected"></a>`REF_PROTECTED` | A runner that is ref protected. |
+### `CiRunnerJobExecutionStatus`
+
+| Value | Description |
+| ----- | ----------- |
+| <a id="cirunnerjobexecutionstatusidle"></a>`IDLE` **{warning-solid}** | **Introduced** in 15.7. This feature is in Alpha. It can be changed or removed at any time. Runner is idle. |
+| <a id="cirunnerjobexecutionstatusrunning"></a>`RUNNING` **{warning-solid}** | **Introduced** in 15.7. This feature is in Alpha. It can be changed or removed at any time. Runner is executing jobs. |
+
### `CiRunnerMembershipFilter`
Values for filtering runners in namespaces. The previous type name `RunnerMembershipFilter` was deprecated in 15.4.
@@ -21082,6 +21569,7 @@ Scan method to be used by the scanner.
| Value | Description |
| ----- | ----------- |
+| <a id="dastscanmethodtypegraphql"></a>`GRAPHQL` | GraphQL scan method. |
| <a id="dastscanmethodtypehar"></a>`HAR` | HAR scan method. |
| <a id="dastscanmethodtypeopenapi"></a>`OPENAPI` | OpenAPI scan method. |
| <a id="dastscanmethodtypepostman_collection"></a>`POSTMAN_COLLECTION` | Postman scan method. |
@@ -21557,6 +22045,7 @@ Issue type.
| ----- | ----------- |
| <a id="issuetypeincident"></a>`INCIDENT` | Incident issue type. |
| <a id="issuetypeissue"></a>`ISSUE` | Issue issue type. |
+| <a id="issuetypekey_result"></a>`KEY_RESULT` **{warning-solid}** | **Introduced** in 15.7. This feature is in Alpha. It can be changed or removed at any time. Key Result issue type. Available only when feature flag `okrs_mvc` is enabled. |
| <a id="issuetypeobjective"></a>`OBJECTIVE` **{warning-solid}** | **Introduced** in 15.6. This feature is in Alpha. It can be changed or removed at any time. Objective issue type. Available only when feature flag `okrs_mvc` is enabled. |
| <a id="issuetyperequirement"></a>`REQUIREMENT` | Requirement issue type. |
| <a id="issuetypetask"></a>`TASK` **{warning-solid}** | **Introduced** in 15.2. This feature is in Alpha. It can be changed or removed at any time. Task issue type. |
@@ -21631,6 +22120,7 @@ Iteration ID wildcard values.
| <a id="jobartifactfiletypenetwork_referee"></a>`NETWORK_REFEREE` | NETWORK REFEREE job artifact file type. |
| <a id="jobartifactfiletypeperformance"></a>`PERFORMANCE` | PERFORMANCE job artifact file type. |
| <a id="jobartifactfiletyperequirements"></a>`REQUIREMENTS` | REQUIREMENTS job artifact file type. |
+| <a id="jobartifactfiletyperequirements_v2"></a>`REQUIREMENTS_V2` | REQUIREMENTS V2 job artifact file type. |
| <a id="jobartifactfiletypesast"></a>`SAST` | SAST job artifact file type. |
| <a id="jobartifactfiletypesecret_detection"></a>`SECRET_DETECTION` | SECRET DETECTION job artifact file type. |
| <a id="jobartifactfiletypeterraform"></a>`TERRAFORM` | TERRAFORM job artifact file type. |
@@ -21852,6 +22342,16 @@ Kind of the network policy.
| <a id="networkpolicykindciliumnetworkpolicy"></a>`CiliumNetworkPolicy` | Policy kind of Cilium Network Policy. |
| <a id="networkpolicykindnetworkpolicy"></a>`NetworkPolicy` | Policy kind of Network Policy. |
+### `NotesFilterType`
+
+Work item notes collection type.
+
+| Value | Description |
+| ----- | ----------- |
+| <a id="notesfiltertypeall_notes"></a>`ALL_NOTES` | Show all activity. |
+| <a id="notesfiltertypeonly_activity"></a>`ONLY_ACTIVITY` | Show history only. |
+| <a id="notesfiltertypeonly_comments"></a>`ONLY_COMMENTS` | Show comments only. |
+
### `OncallRotationUnitEnum`
Rotation length unit of an on-call rotation.
@@ -22193,7 +22693,6 @@ State of a Sentry error.
| <a id="servicetypeemails_on_push_service"></a>`EMAILS_ON_PUSH_SERVICE` | EmailsOnPushService type. |
| <a id="servicetypeewm_service"></a>`EWM_SERVICE` | EwmService type. |
| <a id="servicetypeexternal_wiki_service"></a>`EXTERNAL_WIKI_SERVICE` | ExternalWikiService type. |
-| <a id="servicetypeflowdock_service"></a>`FLOWDOCK_SERVICE` | FlowdockService type. |
| <a id="servicetypegithub_service"></a>`GITHUB_SERVICE` | GithubService type. |
| <a id="servicetypegitlab_slack_application_service"></a>`GITLAB_SLACK_APPLICATION_SERVICE` | GitlabSlackApplicationService type (Gitlab.com only). |
| <a id="servicetypehangouts_chat_service"></a>`HANGOUTS_CHAT_SERVICE` | HangoutsChatService type. |
@@ -22322,7 +22821,8 @@ Category of error.
| <a id="todoactionenumassigned"></a>`assigned` | User was assigned. |
| <a id="todoactionenumbuild_failed"></a>`build_failed` | Build triggered by the user failed. |
| <a id="todoactionenumdirectly_addressed"></a>`directly_addressed` | User was directly addressed. |
-| <a id="todoactionenummarked"></a>`marked` | User added a TODO. |
+| <a id="todoactionenummarked"></a>`marked` | User added a to-do item. |
+| <a id="todoactionenummember_access_requested"></a>`member_access_requested` | Group access requested from the user. |
| <a id="todoactionenummentioned"></a>`mentioned` | User was mentioned. |
| <a id="todoactionenummerge_train_removed"></a>`merge_train_removed` | Merge request authored by the user was removed from the merge train. |
| <a id="todoactionenumreview_requested"></a>`review_requested` | Review was requested from the user. |
@@ -22420,6 +22920,8 @@ Name of the feature that the callout is for.
| <a id="usercalloutfeaturenameenumunfinished_tag_cleanup_callout"></a>`UNFINISHED_TAG_CLEANUP_CALLOUT` | Callout feature name for unfinished_tag_cleanup_callout. |
| <a id="usercalloutfeaturenameenumuser_reached_limit_free_plan_alert"></a>`USER_REACHED_LIMIT_FREE_PLAN_ALERT` | Callout feature name for user_reached_limit_free_plan_alert. |
| <a id="usercalloutfeaturenameenumverification_reminder"></a>`VERIFICATION_REMINDER` | Callout feature name for verification_reminder. |
+| <a id="usercalloutfeaturenameenumvscode_web_ide"></a>`VSCODE_WEB_IDE` | Callout feature name for vscode_web_ide. |
+| <a id="usercalloutfeaturenameenumvscode_web_ide_callout"></a>`VSCODE_WEB_IDE_CALLOUT` | Callout feature name for vscode_web_ide_callout. |
| <a id="usercalloutfeaturenameenumweb_ide_alert_dismissed"></a>`WEB_IDE_ALERT_DISMISSED` | Callout feature name for web_ide_alert_dismissed. |
| <a id="usercalloutfeaturenameenumweb_ide_ci_environments_guidance"></a>`WEB_IDE_CI_ENVIRONMENTS_GUIDANCE` | Callout feature name for web_ide_ci_environments_guidance. |
@@ -22639,10 +23141,13 @@ Type of a work item widget.
| ----- | ----------- |
| <a id="workitemwidgettypeassignees"></a>`ASSIGNEES` | Assignees widget. |
| <a id="workitemwidgettypedescription"></a>`DESCRIPTION` | Description widget. |
+| <a id="workitemwidgettypehealth_status"></a>`HEALTH_STATUS` | Health Status widget. |
| <a id="workitemwidgettypehierarchy"></a>`HIERARCHY` | Hierarchy widget. |
| <a id="workitemwidgettypeiteration"></a>`ITERATION` | Iteration widget. |
| <a id="workitemwidgettypelabels"></a>`LABELS` | Labels widget. |
| <a id="workitemwidgettypemilestone"></a>`MILESTONE` | Milestone widget. |
+| <a id="workitemwidgettypenotes"></a>`NOTES` | Notes widget. |
+| <a id="workitemwidgettypeprogress"></a>`PROGRESS` | Progress widget. |
| <a id="workitemwidgettypestart_and_due_date"></a>`START_AND_DUE_DATE` | Start And Due Date widget. |
| <a id="workitemwidgettypestatus"></a>`STATUS` | Status widget. |
| <a id="workitemwidgettypeweight"></a>`WEIGHT` | Weight widget. |
@@ -22658,6 +23163,12 @@ each kind of object.
For more information, read about [Scalar Types](https://graphql.org/learn/schema/#scalar-types) on `graphql.org`.
+### `AlertManagementAlertID`
+
+A `AlertManagementAlertID` is a global ID. It is encoded as a string.
+
+An example `AlertManagementAlertID` is: `"gid://gitlab/AlertManagement::Alert/1"`.
+
### `AlertManagementHttpIntegrationID`
A `AlertManagementHttpIntegrationID` is a global ID. It is encoded as a string.
@@ -23420,6 +23931,7 @@ Implementations:
- [`CiInstanceVariable`](#ciinstancevariable)
- [`CiManualVariable`](#cimanualvariable)
- [`CiProjectVariable`](#ciprojectvariable)
+- [`PipelineScheduleVariable`](#pipelineschedulevariable)
##### Fields
@@ -23438,6 +23950,7 @@ Represents signing information for a commit.
Implementations:
- [`GpgSignature`](#gpgsignature)
+- [`SshSignature`](#sshsignature)
- [`X509Signature`](#x509signature)
##### Fields
@@ -23933,10 +24446,13 @@ Implementations:
- [`WorkItemWidgetAssignees`](#workitemwidgetassignees)
- [`WorkItemWidgetDescription`](#workitemwidgetdescription)
+- [`WorkItemWidgetHealthStatus`](#workitemwidgethealthstatus)
- [`WorkItemWidgetHierarchy`](#workitemwidgethierarchy)
- [`WorkItemWidgetIteration`](#workitemwidgetiteration)
- [`WorkItemWidgetLabels`](#workitemwidgetlabels)
- [`WorkItemWidgetMilestone`](#workitemwidgetmilestone)
+- [`WorkItemWidgetNotes`](#workitemwidgetnotes)
+- [`WorkItemWidgetProgress`](#workitemwidgetprogress)
- [`WorkItemWidgetStartAndDueDate`](#workitemwidgetstartandduedate)
- [`WorkItemWidgetStatus`](#workitemwidgetstatus)
- [`WorkItemWidgetWeight`](#workitemwidgetweight)
@@ -24199,6 +24715,7 @@ Represents an escalation rule.
| <a id="negatedboardissueinputassigneeusername"></a>`assigneeUsername` | [`[String]`](#string) | Filter by assignee username. |
| <a id="negatedboardissueinputauthorusername"></a>`authorUsername` | [`String`](#string) | Filter by author username. |
| <a id="negatedboardissueinputepicid"></a>`epicId` | [`EpicID`](#epicid) | Filter by epic ID. Incompatible with epicWildcardId. |
+| <a id="negatedboardissueinputhealthstatusfilter"></a>`healthStatusFilter` | [`HealthStatus`](#healthstatus) | Health status not applied to the issue. Includes issues where health status is not set. |
| <a id="negatedboardissueinputiids"></a>`iids` | [`[String!]`](#string) | List of IIDs of issues. For example `["1", "2"]`. |
| <a id="negatedboardissueinputiterationid"></a>`iterationId` | [`[IterationID!]`](#iterationid) | Filter by a list of iteration IDs. Incompatible with iterationWildcardId. |
| <a id="negatedboardissueinputiterationtitle"></a>`iterationTitle` | [`String`](#string) | Filter by iteration title. |
@@ -24241,6 +24758,7 @@ Represents an escalation rule.
| <a id="negatedissuefilterinputassigneeusernames"></a>`assigneeUsernames` | [`[String!]`](#string) | Usernames of users not assigned to the issue. |
| <a id="negatedissuefilterinputauthorusername"></a>`authorUsername` | [`String`](#string) | Username of a user who didn't author the issue. |
| <a id="negatedissuefilterinputepicid"></a>`epicId` | [`String`](#string) | ID of an epic not associated with the issues. |
+| <a id="negatedissuefilterinputhealthstatusfilter"></a>`healthStatusFilter` | [`HealthStatus`](#healthstatus) | Health status not applied to the issue. Includes issues where health status is not set. |
| <a id="negatedissuefilterinputiids"></a>`iids` | [`[String!]`](#string) | List of IIDs of issues to exclude. For example, `[1, 2]`. |
| <a id="negatedissuefilterinputiterationid"></a>`iterationId` | [`[ID!]`](#id) | List of iteration Global IDs not applied to the issue. |
| <a id="negatedissuefilterinputiterationwildcardid"></a>`iterationWildcardId` | [`IterationWildcardId`](#iterationwildcardid) | Filter by negated iteration ID wildcard. |
@@ -24297,6 +24815,18 @@ The rotation user and color palette.
| <a id="oncalluserinputtypecolorweight"></a>`colorWeight` | [`DataVisualizationWeightEnum`](#datavisualizationweightenum) | Color weight to assign to for the on-call user. To view on-call schedules in GitLab, do not provide a value below 500. A value between 500 and 950 ensures sufficient contrast. |
| <a id="oncalluserinputtypeusername"></a>`username` | [`String!`](#string) | Username of the user to participate in the on-call rotation. For example, `"user_one"`. |
+### `PipelineScheduleVariableInput`
+
+Attributes for the pipeline schedule variable.
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="pipelineschedulevariableinputkey"></a>`key` | [`String!`](#string) | Name of the variable. |
+| <a id="pipelineschedulevariableinputvalue"></a>`value` | [`String!`](#string) | Value of the variable. |
+| <a id="pipelineschedulevariableinputvariabletype"></a>`variableType` | [`CiVariableType!`](#civariabletype) | Type of the variable. |
+
### `ReleaseAssetLinkInput`
Fields that are available when modifying a release asset link.
@@ -24502,6 +25032,14 @@ A time-frame defined as a closed inclusive range of two dates.
| ---- | ---- | ----------- |
| <a id="workitemwidgetdescriptioninputdescription"></a>`description` | [`String!`](#string) | Description of the work item. |
+### `WorkItemWidgetHealthStatusInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="workitemwidgethealthstatusinputhealthstatus"></a>`healthStatus` | [`HealthStatus`](#healthstatus) | Health status to be assigned to the work item. |
+
### `WorkItemWidgetHierarchyCreateInput`
#### Arguments
@@ -24544,6 +25082,14 @@ A time-frame defined as a closed inclusive range of two dates.
| ---- | ---- | ----------- |
| <a id="workitemwidgetmilestoneinputmilestoneid"></a>`milestoneId` | [`MilestoneID`](#milestoneid) | Milestone to assign to the work item. |
+### `WorkItemWidgetProgressInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="workitemwidgetprogressinputprogress"></a>`progress` | [`Int!`](#int) | Progress of the work item. |
+
### `WorkItemWidgetStartAndDueDateUpdateInput`
#### Arguments
diff --git a/doc/api/graphql/users_example.md b/doc/api/graphql/users_example.md
index 96bbeeb2d06..9d223f9e618 100644
--- a/doc/api/graphql/users_example.md
+++ b/doc/api/graphql/users_example.md
@@ -20,13 +20,13 @@ The query includes:
- [`pageInfo`](#pageinfo)
- [`nodes`](#nodes)
-## pageInfo
+## `pageInfo`
This contains the data needed to implement pagination. GitLab uses cursor-based
[pagination](getting_started.md#pagination). For more information, see
[Pagination](https://graphql.org/learn/pagination/) in the GraphQL documentation.
-## nodes
+## `nodes`
In a GraphQL query, `nodes` is used to represent a collection of [`nodes` on a graph](https://en.wikipedia.org/wiki/Vertex_(graph_theory)).
In this case, the collection of nodes is a collection of `User` objects. For each one,
diff --git a/doc/api/group_badges.md b/doc/api/group_badges.md
index cc99c137a47..14146745d86 100644
--- a/doc/api/group_badges.md
+++ b/doc/api/group_badges.md
@@ -15,6 +15,8 @@ Badges support placeholders that are replaced in real time in both the link and
<!-- vale gitlab.Spelling = NO -->
- **%{project_path}**: replaced by the project path.
+- **%{project_title}**: replaced by the project title.
+- **%{project_name}**: replaced by the project name.
- **%{project_id}**: replaced by the project ID.
- **%{default_branch}**: replaced by the project default branch.
- **%{commit_sha}**: replaced by the last project's commit SHA.
diff --git a/doc/api/group_import_export.md b/doc/api/group_import_export.md
index 1efed80699b..989b7a66285 100644
--- a/doc/api/group_import_export.md
+++ b/doc/api/group_import_export.md
@@ -102,7 +102,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
NOTE:
The maximum import file size can be set by the Administrator, default is `0` (unlimited).
-As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the [Application settings API](settings.md#change-application-settings) or the [Admin Area](../user/admin_area/settings/account_and_limit_settings.md). Default [modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50MB to 0 in GitLab 13.8.
+As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the [Application settings API](settings.md#change-application-settings) or the [Admin Area](../user/admin_area/settings/account_and_limit_settings.md). Default [modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50 MB to 0 in GitLab 13.8.
## Important notes
@@ -111,6 +111,6 @@ Note the following:
- To preserve group-level relationships from imported projects, run Group Import/Export first,
to allow project imports into the desired group structure.
- Imported groups are given a `private` visibility level, unless imported into a parent group.
-- If imported into a parent group, subgroups will inherit a similar level of visibility, unless otherwise restricted.
+- If imported into a parent group, subgroups inherit a similar level of visibility, unless otherwise restricted.
- To preserve the member list and their respective permissions on imported groups,
review the users in these groups. Make sure these users exist before importing the desired groups.
diff --git a/doc/api/group_wikis.md b/doc/api/group_wikis.md
index cba64269942..d5fe7825dc6 100644
--- a/doc/api/group_wikis.md
+++ b/doc/api/group_wikis.md
@@ -131,13 +131,13 @@ Update an existing wiki page. At least one parameter is required to update the w
PUT /groups/:id/wikis/:slug
```
-| Attribute | Type | Required | Description |
-| --------------- | ------- | --------------------------------- | ------------------------------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
-| `content` | string | yes if `title` is not provided | The content of the wiki page |
-| `title` | string | yes if `content` is not provided | The title of the wiki page |
-| `format` | string | no | The format of the wiki page. Available formats are: `markdown` (default), `rdoc`, `asciidoc` and `org` |
-| `slug` | string | yes | URL encoded slug (a unique string) of the wiki page. Ex. dir%2Fpage_name |
+| Attribute | Type | Required | Description |
+|-----------|----------------|----------------------------------|-------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding). |
+| `content` | string | yes if `title` is not provided | The content of the wiki page. |
+| `title` | string | yes if `content` is not provided | The title of the wiki page. |
+| `format` | string | no | The format of the wiki page. Available formats are `markdown` (default), `rdoc`, `asciidoc`, and `org`. |
+| `slug` | string | yes | URL encoded slug (a unique string) of the wiki page. For example: `dir%2Fpage_name`. |
```shell
curl --request PUT --data "format=rdoc&content=documentation&title=Docs" \
diff --git a/doc/api/groups.md b/doc/api/groups.md
index cba54648705..d017876b9c2 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -30,7 +30,7 @@ Parameters:
| `statistics` | boolean | no | Include group statistics (administrators only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (administrators only) |
| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
-| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md#valid-access-levels) |
+| `min_access_level` | integer | no | Limit to groups where current user has at least this [role (`access_level`)](members.md#roles) |
| `top_level_only` | boolean | no | Limit to top level groups, excluding all subgroups |
```plaintext
@@ -148,7 +148,7 @@ Parameters:
| `statistics` | boolean | no | Include group statistics (administrators only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (administrators only) |
| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
-| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md#valid-access-levels) |
+| `min_access_level` | integer | no | Limit to groups where current user has at least this [role (`access_level`)](members.md#roles) |
```plaintext
GET /groups/:id/subgroups
@@ -206,7 +206,7 @@ Parameters:
| `statistics` | boolean | no | Include group statistics (administrators only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (administrators only) |
| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
-| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md#valid-access-levels) |
+| `min_access_level` | integer | no | Limit to groups where current user has at least this [role (`access_level`)](members.md#roles) |
```plaintext
GET /groups/:id/descendant_groups
@@ -294,7 +294,7 @@ Parameters:
| `with_merge_requests_enabled` | boolean | no | Limit by projects with merge requests feature enabled. Default is `false` |
| `with_shared` | boolean | no | Include projects shared to this group. Default is `true` |
| `include_subgroups` | boolean | no | Include projects in subgroups of this group. Default is `false` |
-| `min_access_level` | integer | no | Limit to projects where current user has at least this [access level](members.md#valid-access-levels) |
+| `min_access_level` | integer | no | Limit to projects where current user has at least this [role (`access_level`)](members.md#roles) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (administrators only) |
| `with_security_reports` **(ULTIMATE)** | boolean | no | Return only projects that have security reports artifacts present in any of their builds. This means "projects with security reports enabled". Default is `false` |
@@ -374,7 +374,7 @@ Parameters:
| `starred` | boolean | no | Limit by projects starred by the current user |
| `with_issues_enabled` | boolean | no | Limit by projects with issues feature enabled. Default is `false` |
| `with_merge_requests_enabled` | boolean | no | Limit by projects with merge requests feature enabled. Default is `false` |
-| `min_access_level` | integer | no | Limit to projects where current user has at least this [access level](members.md#valid-access-levels) |
+| `min_access_level` | integer | no | Limit to projects where current user has at least this [role (`access_level`)](members.md#roles) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (administrators only) |
Example response:
@@ -487,7 +487,7 @@ Example response:
## Details of a group
-> The `membership_lock` field was [added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82271) in GitLab 14.10.
+> The `membership_lock` field was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82271) in GitLab 14.10.
Get all details of a group. This endpoint can be accessed without authentication
if the group is publicly accessible. In case the user that requests is an administrator
@@ -1158,7 +1158,7 @@ Parameters:
The response is `202 Accepted` if the user has authorization.
NOTE:
-A GitLab.com group can't be removed if it is linked to a subscription. To remove such a group, first [link the subscription](../subscriptions/index.md#change-the-linked-namespace) with a different group.
+A GitLab.com group can't be removed if it is linked to a subscription. To remove such a group, first [link the subscription](../subscriptions/gitlab_com/index.md#change-the-linked-namespace) with a different group.
## Restore group marked for deletion **(PREMIUM)**
@@ -1361,25 +1361,25 @@ PUT /groups/:id/hooks/:hook_id
| Attribute | Type | Required | Description |
| ---------------------------- | -------------- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
-| `hook_id` | integer | yes | The ID of the group hook |
-| `url` | string | yes | The hook URL |
-| `push_events` | boolean | no | Trigger hook on push events |
+| `id` | integer or string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding). |
+| `hook_id` | integer | yes | The ID of the group hook. |
+| `url` | string | yes | The hook URL. |
+| `push_events` | boolean | no | Trigger hook on push events. |
| `push_events_branch_filter` | string | No | Trigger hook on push events for matching branches only. |
-| `issues_events` | boolean | no | Trigger hook on issues events |
-| `confidential_issues_events` | boolean | no | Trigger hook on confidential issues events |
-| `merge_requests_events` | boolean | no | Trigger hook on merge requests events |
-| `tag_push_events` | boolean | no | Trigger hook on tag push events |
-| `note_events` | boolean | no | Trigger hook on note events |
-| `confidential_note_events` | boolean | no | Trigger hook on confidential note events |
-| `job_events` | boolean | no | Trigger hook on job events |
-| `pipeline_events` | boolean | no | Trigger hook on pipeline events |
-| `wiki_page_events` | boolean | no | Trigger hook on wiki page events |
-| `deployment_events` | boolean | no | Trigger hook on deployment events |
-| `releases_events` | boolean | no | Trigger hook on release events |
-| `subgroup_events` | boolean | no | Trigger hook on subgroup events |
-| `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook |
-| `token` | string | no | Secret token to validate received payloads; not returned in the response |
+| `issues_events` | boolean | no | Trigger hook on issues events. |
+| `confidential_issues_events` | boolean | no | Trigger hook on confidential issues events. |
+| `merge_requests_events` | boolean | no | Trigger hook on merge requests events. |
+| `tag_push_events` | boolean | no | Trigger hook on tag push events. |
+| `note_events` | boolean | no | Trigger hook on note events. |
+| `confidential_note_events` | boolean | no | Trigger hook on confidential note events. |
+| `job_events` | boolean | no | Trigger hook on job events. |
+| `pipeline_events` | boolean | no | Trigger hook on pipeline events. |
+| `wiki_page_events` | boolean | no | Trigger hook on wiki page events. |
+| `deployment_events` | boolean | no | Trigger hook on deployment events. |
+| `releases_events` | boolean | no | Trigger hook on release events. |
+| `subgroup_events` | boolean | no | Trigger hook on subgroup events. |
+| `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook. |
+| `token` | string | no | Secret token to validate received payloads. Not returned in the response. When you change the webhook URL, the secret token is reset and not retained. |
### Delete group hook
@@ -1444,7 +1444,7 @@ POST /groups/:id/ldap_group_links
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
| `cn` | string | no | The CN of an LDAP group |
| `filter` | string | no | The LDAP filter for the group |
-| `group_access` | integer | yes | [Access level](members.md#valid-access-levels) for members of the LDAP group |
+| `group_access` | integer | yes | [Role (`access_level`)](members.md#roles) for members of the LDAP group |
| `provider` | string | yes | LDAP provider for the LDAP group link |
NOTE:
@@ -1519,7 +1519,7 @@ If successful, returns [`200`](index.md#status-codes) and the following response
| Attribute | Type | Description |
|:-------------------|:--------|:-----------------------------------------------------------------------------|
| `[].name` | string | Name of the SAML group |
-| `[].access_level` | integer | [Access level](members.md#valid-access-levels) for members of the SAML group. The attribute had a string type from GitLab 15.3.0 to GitLab 15.3.3 |
+| `[].access_level` | integer | [Role (`access_level`)](members.md#roles) for members of the SAML group. The attribute had a string type from GitLab 15.3.0 to GitLab 15.3.3 |
Example request:
@@ -1562,7 +1562,7 @@ If successful, returns [`200`](index.md#status-codes) and the following response
| Attribute | Type | Description |
|:---------------|:--------|:-----------------------------------------------------------------------------|
| `name` | string | Name of the SAML group |
-| `access_level` | integer | [Access level](members.md#valid-access-levels) for members of the SAML group. The attribute had a string type from GitLab 15.3.0 to GitLab 15.3.3 |
+| `access_level` | integer | [Role (`access_level`)](members.md#roles) for members of the SAML group. The attribute had a string type from GitLab 15.3.0 to GitLab 15.3.3 |
Example request:
@@ -1593,14 +1593,14 @@ Supported attributes:
|:-------------------|:---------------|:---------|:-----------------------------------------------------------------------------|
| `id` | integer or string | yes | ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
| `saml_group_name` | string | yes | Name of a SAML group |
-| `access_level` | integer | yes | [Access level](members.md#valid-access-levels) for members of the SAML group |
+| `access_level` | integer | yes | [Role (`access_level`)](members.md#roles) for members of the SAML group |
If successful, returns [`201`](index.md#status-codes) and the following response attributes:
| Attribute | Type | Description |
|:---------------|:--------|:-----------------------------------------------------------------------------|
| `name` | string | Name of the SAML group |
-| `access_level` | integer | [Access level](members.md#valid-access-levels) for members of the for members of the SAML group. The attribute had a string type from GitLab 15.3.0 to GitLab 15.3.3 |
+| `access_level` | integer | [Role (`access_level`)](members.md#roles) for members of the for members of the SAML group. The attribute had a string type from GitLab 15.3.0 to GitLab 15.3.3 |
Example request:
@@ -1682,7 +1682,7 @@ POST /groups/:id/share
| --------- | -------------- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
| `group_id` | integer | yes | The ID of the group to share with |
-| `group_access` | integer | yes | The [access level](members.md#valid-access-levels) to grant the group |
+| `group_access` | integer | yes | The [role (`access_level`)](members.md#roles) to grant the group |
| `expires_at` | string | no | Share expiration date in ISO 8601 format: 2016-09-26 |
### Delete link sharing group with another group
diff --git a/doc/api/import.md b/doc/api/import.md
index 78b9beb1815..7a1eb4fe8b3 100644
--- a/doc/api/import.md
+++ b/doc/api/import.md
@@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Import repository from GitHub
-Import your projects from GitHub to GitLab via the API.
+Import your projects from GitHub to GitLab using the API.
```plaintext
POST /import/github
@@ -61,6 +61,16 @@ Example response:
}
```
+### Import a public project through the API using a group access token
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/362683) in GitLab 15.7, projects are not imported into a [bot user's](../user/group/settings/group_access_tokens.md#bot-users-for-groups) namespace in any circumstances. Projects imported into a bot user's namespace could not be deleted by users with valid tokens, which represented a security risk.
+
+When you import a project from GitHub to GitLab through the API using a group access
+token:
+
+- The GitLab project inherits the original project's visibility settings. As a result, the project is publicly accessible if the original project is public.
+- If the `path` or `target_namespace` does not exist, the project import fails.
+
## Cancel GitHub project import
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/364783) in GitLab 15.5.
diff --git a/doc/api/index.md b/doc/api/index.md
index cc54731de81..ef054318c5c 100644
--- a/doc/api/index.md
+++ b/doc/api/index.md
@@ -26,12 +26,6 @@ Contributions are welcome.
For an introduction and basic steps, see
[How to make GitLab API calls](https://www.youtube.com/watch?v=0LsMC3ZiXkA).
-## SCIM API **(PREMIUM SAAS)**
-
-GitLab provides a [SCIM API](scim.md) that both implements
-[the RFC7644 protocol](https://www.rfc-editor.org/rfc/rfc7644) and provides the
-`/Users` endpoint. The base URL is `/api/scim/v2/groups/:group_path/Users/`.
-
## GraphQL API
A GraphQL API is available in GitLab.
@@ -177,7 +171,7 @@ Read more about [GitLab as an OAuth2 provider](oauth2.md).
NOTE:
We recommend OAuth access tokens have an expiration. You can use the `refresh_token` parameter
to refresh tokens. Integrations may need to be updated to use refresh tokens prior to
-expiration, which is based on the [expires_in](https://datatracker.ietf.org/doc/html/rfc6749#appendix-A.14)
+expiration, which is based on the [`expires_in`](https://datatracker.ietf.org/doc/html/rfc6749#appendix-A.14)
property in the token endpoint response. See [OAuth2 token](oauth2.md) documentation
for examples requesting a new access token using a refresh token.
diff --git a/doc/api/integrations.md b/doc/api/integrations.md
index d64c67e1402..f6ad095aad6 100644
--- a/doc/api/integrations.md
+++ b/doc/api/integrations.md
@@ -354,7 +354,7 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `webhook` | string | true | The Unify Circuit webhook. For example, `https://circuit.com/rest/v2/webhooks/incoming/...`. |
| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are `all`, `default`, `protected`, and `default_and_protected`. The default value is "default" |
| `push_events` | boolean | false | Enable notifications for push events |
| `issues_events` | boolean | false | Enable notifications for issue events |
| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
@@ -444,7 +444,7 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `webhook` | string | true | The Webex Teams webhook. For example, `https://api.ciscospark.com/v1/webhooks/incoming/...`. |
| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are `all`, `default`, `protected`, and `default_and_protected`. The default value is "default" |
| `push_events` | boolean | false | Enable notifications for push events |
| `issues_events` | boolean | false | Enable notifications for issue events |
| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
@@ -601,7 +601,7 @@ Parameters:
| `send_from_committer_email` | boolean | false | Send from committer |
| `push_events` | boolean | false | Enable notifications for push events |
| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". Notifications are always fired for tag pushes. The default value is "all" |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are `all`, `default`, `protected`, and `default_and_protected`. Notifications are always fired for tag pushes. The default value is "all" |
### Disable Emails on Push integration
@@ -755,42 +755,6 @@ Get External wiki integration settings for a project.
GET /projects/:id/integrations/external-wiki
```
-## Flowdock
-
-Flowdock is a ChatOps application for collaboration in software engineering
-companies. You can send notifications from GitLab events to Flowdock flows.
-For integration instructions, see the [Flowdock documentation](https://www.flowdock.com/help/gitlab).
-
-### Create/Edit Flowdock integration
-
-Set Flowdock integration for a project.
-
-```plaintext
-PUT /projects/:id/integrations/flowdock
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | true | Flowdock Git source token |
-
-### Disable Flowdock integration
-
-Disable the Flowdock integration for a project. Integration settings are preserved.
-
-```plaintext
-DELETE /projects/:id/integrations/flowdock
-```
-
-### Get Flowdock integration settings
-
-Get Flowdock integration settings for a project.
-
-```plaintext
-GET /projects/:id/integrations/flowdock
-```
-
## GitHub **(PREMIUM)**
Code collaboration software.
@@ -846,7 +810,7 @@ Parameters:
| `webhook` | string | true | The Hangouts Chat webhook. For example, `https://chat.googleapis.com/v1/spaces...`. |
| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are `all`, `default`, `protected`, and `default_and_protected`. The default value is "default" |
| `push_events` | boolean | false | Enable notifications for push events |
| `issues_events` | boolean | false | Enable notifications for issue events |
| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
@@ -1106,7 +1070,7 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `recipients` | string | yes | Comma-separated list of recipient email addresses |
| `notify_only_broken_pipelines` | boolean | no | Notify only broken pipelines |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected. The default value is "default" |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are `all`, `default`, `protected`, and `default_and_protected`. The default value is "default" |
| `notify_only_default_branch` | boolean | no | Send notifications only for the default branch ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/28271)) |
| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
@@ -1181,7 +1145,7 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `api_url` | string | true | Prometheus API Base URL. For example, `http://prometheus.example.com/`. |
| `google_iap_audience_client_id` | string | false | Client ID of the IAP secured resource (looks like IAP_CLIENT_ID.apps.googleusercontent.com) |
-| `google_iap_service_account_json` | string | false | `credentials.json` file for your service account, like { "type": "service_account", "project_id": ... } |
+| `google_iap_service_account_json` | string | false | `credentials.json` file for your service account, like { `"type": "service_account", "project_id": ... }` |
### Disable Prometheus integration
@@ -1294,7 +1258,7 @@ Parameters:
| `channel` | string | false | Default channel to use if others are not configured |
| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are `all`, `default`, `protected`, and `default_and_protected`. The default value is "default" |
| `commit_events` | boolean | false | Enable notifications for commit events |
| `confidential_issue_channel` | string | false | The name of the channel to receive confidential issues events notifications |
| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
@@ -1353,7 +1317,7 @@ Parameters:
| `webhook` | string | true | The Microsoft Teams webhook. For example, `https://outlook.office.com/webhook/...` |
| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are `all`, `default`, `protected`, and `default_and_protected`. The default value is "default" |
| `push_events` | boolean | false | Enable notifications for push events |
| `issues_events` | boolean | false | Enable notifications for issue events |
| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
@@ -1401,7 +1365,7 @@ Parameters:
| `channel` | string | false | Default channel to use if others are not configured |
| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are `all`, `default`, `protected`, and `default_and_protected`. The default value is "default" |
| `push_events` | boolean | false | Enable notifications for push events |
| `issues_events` | boolean | false | Enable notifications for issue events |
| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
diff --git a/doc/api/invitations.md b/doc/api/invitations.md
index 94362b097af..908fa0ce890 100644
--- a/doc/api/invitations.md
+++ b/doc/api/invitations.md
@@ -126,7 +126,7 @@ PUT /projects/:id/invitations/:email
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project or group](index.md#namespaced-path-encoding) owned by the authenticated user. |
-| `email` | string | yes | The email address to which the invitation was previously sent. |
+| `email` | string | yes | The email address the invitation was previously sent to. |
| `access_level` | integer | no | A valid access level (defaults: `30`, the Developer role). |
| `expires_at` | string | no | A date string in ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`). |
diff --git a/doc/api/issue_links.md b/doc/api/issue_links.md
index 253be9109c7..ce3d26f1c08 100644
--- a/doc/api/issue_links.md
+++ b/doc/api/issue_links.md
@@ -178,7 +178,7 @@ POST /projects/:id/issues/:issue_iid/links
| `issue_iid` | integer | yes | The internal ID of a project's issue |
| `target_project_id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) of a target project |
| `target_issue_iid` | integer/string | yes | The internal ID of a target project's issue |
-| `link_type` | string | no | The type of the relation ("relates_to", "blocks", "is_blocked_by"), defaults to "relates_to"). |
+| `link_type` | string | no | The type of the relation (`relates_to`, `blocks`, `is_blocked_by`), defaults to `relates_to`). |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/4/issues/1/links?target_project_id=5&target_issue_iid=1"
diff --git a/doc/api/issues.md b/doc/api/issues.md
index dd5a1354a3a..94547d69064 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -2010,7 +2010,7 @@ POST /projects/:id/issues/:issue_iid/time_estimate
| Attribute | Type | Required | Description |
|-------------|---------|----------|------------------------------------------|
-| `duration` | string | yes | The duration in human format. e.g: 3h30m |
+| `duration` | string | yes | The duration in human format. e.g: `3h30m` |
| `id` | integer/string | yes | The global ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of a project's issue |
@@ -2067,7 +2067,7 @@ POST /projects/:id/issues/:issue_iid/add_spent_time
| Attribute | Type | Required | Description |
|-------------|---------|----------|------------------------------------------|
-| `duration` | string | yes | The duration in human format. e.g: 3h30m |
+| `duration` | string | yes | The duration in human format. e.g: `3h30m` |
| `id` | integer/string | yes | The global ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of a project's issue |
| `summary` | string | no | A summary of how the time was spent |
diff --git a/doc/api/jobs.md b/doc/api/jobs.md
index fc2de00c3d2..992cb70c45d 100644
--- a/doc/api/jobs.md
+++ b/doc/api/jobs.md
@@ -520,46 +520,39 @@ Example response:
```json
{
- "allowed_agents":
- [
- {
+ "allowed_agents": [
+ {
+ "id": 1,
+ "config_project": {
"id": 1,
- "config_project": {
- "id": 1,
- "description": null,
- "name": "project1",
- "name_with_namespace": "John Doe2 / project1",
- "path": "project1",
- "path_with_namespace": "namespace1/project1",
- "created_at": "2021-03-26T14:51:50.579Z"
- }
+ "description": null,
+ "name": "project1",
+ "name_with_namespace": "John Doe2 / project1",
+ "path": "project1",
+ "path_with_namespace": "namespace1/project1",
+ "created_at": "2022-11-16T14:51:50.579Z"
}
- ],
+ }
+ ],
"job": {
- "id": 1,
- "name": "test",
- "stage": "test",
- "project_id": 1,
- "project_name": "project1"
+ "id": 1
},
"pipeline": {
- "id": 1,
- "project_id": 1,
- "sha": "b83d6e391c22777fca1ed3012fce84f633d7fed0",
- "ref": "main",
- "status": "pending",
- "created_at": "2021-03-26T14:51:51.107Z",
- "updated_at": "2021-03-26T14:51:51.107Z",
- "web_url": "http://localhost/namespace1/project1/-/pipelines/1"
+ "id": 2
},
"project": {
"id": 1,
- "description": null,
- "name": "project1",
- "name_with_namespace": "John Doe2 / project1",
- "path": "project1",
- "path_with_namespace": "namespace1/project1",
- "created_at": "2021-03-26T14:51:50.579Z"
+ "groups": [
+ {
+ "id": 1
+ },
+ {
+ "id": 2
+ },
+ {
+ "id": 3
+ }
+ ]
},
"user": {
"id": 2,
diff --git a/doc/api/keys.md b/doc/api/keys.md
index 74d8bc4383f..e7bdc70017c 100644
--- a/doc/api/keys.md
+++ b/doc/api/keys.md
@@ -32,6 +32,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1256k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"created_at": "2015-09-03T07:24:44.627Z",
"expires_at": "2020-05-05T00:00:00.000Z",
+ "usage_type": "auth",
"user": {
"name": "John Smith",
"username": "john_smith",
@@ -101,6 +102,7 @@ Example response:
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1016k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"created_at": "2019-11-14T15:11:13.222Z",
"expires_at": "2020-05-05T00:00:00.000Z",
+ "usage_type": "auth",
"user": {
"id": 1,
"name": "Administrator",
@@ -159,6 +161,7 @@ Example response:
"title": "Sample key 1",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1016k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"created_at": "2019-11-14T15:11:13.222Z",
+ "usage_type": "auth",
"user": {
"id": 1,
"name": "Administrator",
diff --git a/doc/api/license.md b/doc/api/license.md
index 72589710590..ca9d9cf386d 100644
--- a/doc/api/license.md
+++ b/doc/api/license.md
@@ -97,6 +97,55 @@ Returns:
- `200 OK` with response containing the licenses in JSON format. This is an empty JSON array if there are no licenses.
- `403 Forbidden` if the current user in not permitted to read the licenses.
+## Retrieve information about a single license
+
+```plaintext
+GET /license/:id
+```
+
+Supported attributes:
+
+| Attribute | Type | Required | Description |
+|-----------|---------|----------|---------------------------|
+| `id` | integer | yes | ID of the GitLab license. |
+
+Returns the following status codes:
+
+- `200 OK`: Response contains the licenses in JSON format.
+- `404 Not Found`: The requested license doesn't exist.
+- `403 Forbidden`: The current user is not permitted to read the licenses.
+
+Example request:
+
+```shell
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/license/:id"
+```
+
+Example response:
+
+```json
+{
+ "id": 1,
+ "plan": "premium",
+ "created_at": "2018-02-27T23:21:58.674Z",
+ "starts_at": "2018-01-27",
+ "expires_at": "2022-01-27",
+ "historical_max": 300,
+ "maximum_user_count": 300,
+ "expired": false,
+ "overage": 200,
+ "user_limit": 100,
+ "active_users": 50,
+ "licensee": {
+ "Name": "John Doe1"
+ },
+ "add_ons": {
+ "GitLab_FileLocks": 1,
+ "GitLab_Auditor_User": 1
+ }
+}
+```
+
## Add a new license
```plaintext
diff --git a/doc/api/members.md b/doc/api/members.md
index 66729abcad8..0d6fd6aabc4 100644
--- a/doc/api/members.md
+++ b/doc/api/members.md
@@ -8,9 +8,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> `created_by` field [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/28789) in GitLab 14.10.
-## Valid access levels
+## Roles
-The access levels are defined in the `Gitlab::Access` module. Currently, these levels are recognized:
+The [role](../user/permissions.md) assigned to a user or group is defined
+in the `Gitlab::Access` module as `access_level`.
- No access (`0`)
- Minimal access (`5`) ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220203) in GitLab 13.5.)
@@ -28,7 +29,8 @@ In GitLab 14.8 and earlier, projects in personal namespaces have an `access_leve
The `group_saml_identity` attribute is only visible to a group owner for [SSO enabled groups](../user/group/saml_sso/index.md).
-The `email` attribute is only visible for users with public emails.
+The `email` attribute is only visible to group owners when the user was provisioned by the group.
+Users are provisioned by the group when the account was created via [SCIM](../user/group/saml_sso/scim_setup.md) or by first sign-in with [SAML SSO for GitLab.com groups](../user/group/saml_sso/index.md).
## List all members of a group or project
diff --git a/doc/api/merge_request_approvals.md b/doc/api/merge_request_approvals.md
index 0476035784a..d9777b87ff2 100644
--- a/doc/api/merge_request_approvals.md
+++ b/doc/api/merge_request_approvals.md
@@ -84,6 +84,8 @@ Supported attributes:
> - Moved to GitLab Premium in 13.9.
> - Pagination support [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/31011) in GitLab 15.3 [with a flag](../administration/feature_flags.md) named `approval_rules_pagination`. Enabled by default.
> - `applies_to_all_protected_branches` property was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335316) in GitLab 15.3.
+> - Pagination support [generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/366823) in GitLab 15.7. Feature flag `approval_rules_pagination` removed.
+> - `usernames` property was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/) in GitLab 15.8.
You can request information about a project's approval rules using the following endpoint:
@@ -187,6 +189,7 @@ Supported attributes:
> - Introduced in GitLab 13.7.
> - `applies_to_all_protected_branches` property was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335316) in GitLab 15.3.
+> - `usernames` property was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/) in GitLab 1x.x.
You can request information about a single project approval rules using the following endpoint:
@@ -288,6 +291,7 @@ Supported attributes:
> - Moved to GitLab Premium in 13.9.
> - [Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/357300) the Vulnerability-Check feature in GitLab 15.0.
> - `applies_to_all_protected_branches` property was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335316) in GitLab 15.3.
+> - `usernames` property was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/) in GitLab 1x.x.
You can create project approval rules using the following endpoint:
@@ -308,6 +312,7 @@ Supported attributes:
| `report_type` | string | **{dotted-circle}** No | The report type required when the rule type is `report_approver`. The supported report types are `license_scanning` and `code_coverage`. |
| `rule_type` | string | **{dotted-circle}** No | The type of rule. `any_approver` is a pre-configured default rule with `approvals_required` at `0`. Other rules are `regular`. |
| `user_ids` | Array | **{dotted-circle}** No | The IDs of users as approvers. |
+| `usernames` | string array | **{dotted-circle}** No | The usernames for this rule. |
```json
{
@@ -413,6 +418,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
> - Moved to GitLab Premium in 13.9.
> - [Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/357300) the Vulnerability-Check feature in GitLab 15.0.
> - `applies_to_all_protected_branches` property was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335316) in GitLab 15.3.
+> - `usernames` property was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/) in GitLab 1x.x.
You can update project approval rules using the following endpoint:
@@ -433,7 +439,9 @@ Supported attributes:
| `applies_to_all_protected_branches` | boolean | **{dotted-circle}** No | Whether the rule is applied to all protected branches. If set to `true`, the value of `protected_branch_ids` is ignored. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335316) in GitLab 15.3. |
| `group_ids` | Array | **{dotted-circle}** No | The IDs of groups as approvers. |
| `protected_branch_ids` | Array | **{dotted-circle}** No | The IDs of protected branches to scope the rule by. To identify the ID, [use the API](protected_branches.md#list-protected-branches). |
+| `remove_hidden_groups` | boolean | **{dotted-circle}** No | Whether hidden groups should be removed. |
| `user_ids` | Array | **{dotted-circle}** No | The IDs of users as approvers. |
+| `usernames` | string array | **{dotted-circle}** No | The usernames for this rule. |
```json
{
@@ -703,6 +711,7 @@ Supported attributes:
> - Moved to GitLab Premium in 13.9.
> - Pagination support [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/31011) in GitLab 15.3 [with a flag](../administration/feature_flags.md) named `approval_rules_pagination`. Enabled by default.
+> - Pagination support [generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/366823) in GitLab 15.7. Feature flag `approval_rules_pagination` removed.
You can request information about a merge request's approval rules using the following endpoint:
@@ -876,6 +885,7 @@ Supported attributes:
| `approval_project_rule_id` | integer | **{dotted-circle}** No | The ID of a project-level approval rule. |
| `group_ids` | Array | **{dotted-circle}** No | The IDs of groups as approvers. |
| `user_ids` | Array | **{dotted-circle}** No | The IDs of users as approvers. |
+| `usernames` | string array | **{dotted-circle}** No | The usernames for this rule. |
**Important:** When `approval_project_rule_id` is set, the `name`, `users` and
`groups` of project-level rule are copied. The `approvals_required` specified
@@ -964,7 +974,9 @@ Supported attributes:
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of a merge request. |
| `name` | string | **{check-circle}** Yes | The name of the approval rule. |
| `group_ids` | Array | **{dotted-circle}** No | The IDs of groups as approvers. |
+| `remove_hidden_groups` | boolean | **{dotted-circle}** No | Whether hidden groups should be removed. |
| `user_ids` | Array | **{dotted-circle}** No | The IDs of users as approvers. |
+| `usernames` | string array | **{dotted-circle}** No | The usernames for this rule. |
```json
{
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 4b4d36ec23e..5843a10ca59 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -6,8 +6,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Merge requests API **(FREE)**
+> - `reference` was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20354) in GitLab 12.7.
> - `draft` was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63473) as a replacement for `work_in_progress` in GitLab 14.0.
+> - `merged_by` was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/350534) in GitLab 14.7.
> - `merge_user` was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/349031) as an eventual replacement for `merged_by` in GitLab 14.7.
+> - `merge_status` was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/3169#note_1162532204) in favor of `detailed_merge_status` in GitLab 15.6.
Every API call to merge requests must be authenticated.
@@ -45,27 +48,27 @@ Supported attributes:
| `assignee_id` | integer | **{dotted-circle}** No | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
| `author_id` | integer | **{dotted-circle}** No | Returns merge requests created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`. |
| `author_username` | string | **{dotted-circle}** No | Returns merge requests created by the given `username`. Mutually exclusive with `author_id`. |
-| `created_after` | datetime | **{dotted-circle}** No | Return merge requests created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `created_before` | datetime | **{dotted-circle}** No | Return merge requests created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `deployed_after` | datetime | **{dotted-circle}** No | Return merge requests deployed after the given date/time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `deployed_before` | datetime | **{dotted-circle}** No | Return merge requests deployed before the given date/time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `created_after` | datetime | **{dotted-circle}** No | Returns merge requests created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `created_before` | datetime | **{dotted-circle}** No | Returns merge requests created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `deployed_after` | datetime | **{dotted-circle}** No | Returns merge requests deployed after the given date/time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `deployed_before` | datetime | **{dotted-circle}** No | Returns merge requests deployed before the given date/time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `environment` | string | **{dotted-circle}** No | Returns merge requests deployed to the given environment. |
| `in` | string | **{dotted-circle}** No | Modify the scope of the `search` attribute. `title`, `description`, or a string joining them with comma. Default is `title,description`. |
-| `labels` | string | **{dotted-circle}** No | Return merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. |
-| `milestone` | string | **{dotted-circle}** No | Return merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
-| `my_reaction_emoji` | string | **{dotted-circle}** No | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
-| `not` | Hash | **{dotted-circle}** No | Return merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. |
-| `order_by` | string | **{dotted-circle}** No | Return requests ordered by `created_at`, `title`, or `updated_at` fields. Default is `created_at`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331625) in GitLab 14.8.|
+| `labels` | string | **{dotted-circle}** No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. |
+| `milestone` | string | **{dotted-circle}** No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
+| `my_reaction_emoji` | string | **{dotted-circle}** No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
+| `not` | Hash | **{dotted-circle}** No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. |
+| `order_by` | string | **{dotted-circle}** No | Returns requests ordered by `created_at`, `title`, or `updated_at` fields. Default is `created_at`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331625) in GitLab 14.8.|
| `reviewer_id` | integer | **{dotted-circle}** No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. |
| `reviewer_username` | string | **{dotted-circle}** No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49341) in GitLab 13.8. |
-| `scope` | string | **{dotted-circle}** No | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`. |
+| `scope` | string | **{dotted-circle}** No | Returns merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`. |
| `search` | string | **{dotted-circle}** No | Search merge requests against their `title` and `description`. |
-| `sort` | string | **{dotted-circle}** No | Return requests sorted in `asc` or `desc` order. Default is `desc`. |
-| `source_branch` | string | **{dotted-circle}** No | Return merge requests with the given source branch. |
-| `state` | string | **{dotted-circle}** No | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged`. |
-| `target_branch` | string | **{dotted-circle}** No | Return merge requests with the given target branch. |
-| `updated_after` | datetime | **{dotted-circle}** No | Return merge requests updated on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `updated_before` | datetime | **{dotted-circle}** No | Return merge requests updated on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `sort` | string | **{dotted-circle}** No | Returns requests sorted in `asc` or `desc` order. Default is `desc`. |
+| `source_branch` | string | **{dotted-circle}** No | Returns merge requests with the given source branch. |
+| `state` | string | **{dotted-circle}** No | Returns all merge requests or just those that are `opened`, `closed`, `locked`, or `merged`. |
+| `target_branch` | string | **{dotted-circle}** No | Returns merge requests with the given target branch. |
+| `updated_after` | datetime | **{dotted-circle}** No | Returns merge requests updated on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `updated_before` | datetime | **{dotted-circle}** No | Returns merge requests updated on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `view` | string | **{dotted-circle}** No | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request. |
| `with_labels_details` | boolean | **{dotted-circle}** No | If `true`, response returns more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. |
| `with_merge_status_recheck` | boolean | **{dotted-circle}** No | If `true`, this projection requests (but does not guarantee) that the `merge_status` field be recalculated asynchronously. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31890) in GitLab 13.0. |
@@ -229,7 +232,7 @@ GET /projects/:id/merge_requests?labels=bug,reproduced
GET /projects/:id/merge_requests?my_reaction_emoji=star
```
-`project_id` represents the ID of the project where the MR resides.
+`project_id` represents the ID of the project where the merge request resides.
`project_id` always equals `target_project_id`.
In the case of a merge request from the same project,
@@ -248,25 +251,25 @@ Supported attributes:
| `assignee_id` | integer | **{dotted-circle}** No | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
| `author_id` | integer | **{dotted-circle}** No | Returns merge requests created by the given user `id`. Mutually exclusive with `author_username`. |
| `author_username` | string | **{dotted-circle}** No | Returns merge requests created by the given `username`. Mutually exclusive with `author_id`. |
-| `created_after` | datetime | **{dotted-circle}** No | Return merge requests created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `created_before` | datetime | **{dotted-circle}** No | Return merge requests created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `created_after` | datetime | **{dotted-circle}** No | Returns merge requests created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `created_before` | datetime | **{dotted-circle}** No | Returns merge requests created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `environment` | string | **{dotted-circle}** No | Returns merge requests deployed to the given environment. |
-| `iids[]` | integer array | **{dotted-circle}** No | Return the request having the given `iid`. |
-| `labels` | string | **{dotted-circle}** No | Return merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. |
-| `milestone` | string | **{dotted-circle}** No | Return merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
-| `my_reaction_emoji` | string | **{dotted-circle}** No | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
-| `not` | Hash | **{dotted-circle}** No | Return merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. |
-| `order_by` | string | **{dotted-circle}** No | Return requests ordered by `created_at`, `title` or `updated_at` fields. Default is `created_at`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331625) in GitLab 14.8. |
+| `iids[]` | integer array | **{dotted-circle}** No | Returns the request having the given `iid`. |
+| `labels` | string | **{dotted-circle}** No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. |
+| `milestone` | string | **{dotted-circle}** No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
+| `my_reaction_emoji` | string | **{dotted-circle}** No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
+| `not` | Hash | **{dotted-circle}** No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. |
+| `order_by` | string | **{dotted-circle}** No | Returns requests ordered by `created_at`, `title` or `updated_at` fields. Default is `created_at`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331625) in GitLab 14.8. |
| `reviewer_id` | integer | **{dotted-circle}** No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. |
| `reviewer_username` | string | **{dotted-circle}** No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49341) in GitLab 13.8. |
-| `scope` | string | **{dotted-circle}** No | Return merge requests for the given scope: `created_by_me`, `assigned_to_me`, or `all`. |
+| `scope` | string | **{dotted-circle}** No | Returns merge requests for the given scope: `created_by_me`, `assigned_to_me`, or `all`. |
| `search` | string | **{dotted-circle}** No | Search merge requests against their `title` and `description`. |
-| `sort` | string | **{dotted-circle}** No | Return requests sorted in `asc` or `desc` order. Default is `desc`. |
-| `source_branch` | string | **{dotted-circle}** No | Return merge requests with the given source branch. |
-| `state` | string | **{dotted-circle}** No | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged`. |
-| `target_branch` | string | **{dotted-circle}** No | Return merge requests with the given target branch. |
-| `updated_after` | datetime | **{dotted-circle}** No | Return merge requests updated on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `updated_before` | datetime | **{dotted-circle}** No | Return merge requests updated on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `sort` | string | **{dotted-circle}** No | Returns requests sorted in `asc` or `desc` order. Default is `desc`. |
+| `source_branch` | string | **{dotted-circle}** No | Returns merge requests with the given source branch. |
+| `state` | string | **{dotted-circle}** No | Returns all merge requests or just those that are `opened`, `closed`, `locked`, or `merged`. |
+| `target_branch` | string | **{dotted-circle}** No | Returns merge requests with the given target branch. |
+| `updated_after` | datetime | **{dotted-circle}** No | Returns merge requests updated on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `updated_before` | datetime | **{dotted-circle}** No | Returns merge requests updated on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `view` | string | **{dotted-circle}** No | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request. |
| `wip` | string | **{dotted-circle}** No | Filter merge requests against their `wip` status. `yes` to return *only* draft merge requests, `no` to return *non-draft* merge requests. |
| `with_labels_details` | boolean | **{dotted-circle}** No | If `true`, response returns more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. |
@@ -425,7 +428,7 @@ GET /groups/:id/merge_requests?labels=bug,reproduced
GET /groups/:id/merge_requests?my_reaction_emoji=star
```
-`group_id` represents the ID of the group which contains the project where the MR resides.
+`group_id` represents the ID of the group which contains the project where the merge request resides.
Supported attributes:
@@ -438,24 +441,24 @@ Supported attributes:
| `assignee_id` | integer | **{dotted-circle}** No | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
| `author_id` | integer | **{dotted-circle}** No | Returns merge requests created by the given user `id`. Mutually exclusive with `author_username`. |
| `author_username` | string | **{dotted-circle}** No | Returns merge requests created by the given `username`. Mutually exclusive with `author_id`. |
-| `created_after` | datetime | **{dotted-circle}** No | Return merge requests created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `created_before` | datetime | **{dotted-circle}** No | Return merge requests created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `labels` | string | **{dotted-circle}** No | Return merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. |
-| `milestone` | string | **{dotted-circle}** No | Return merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
-| `my_reaction_emoji` | string | **{dotted-circle}** No | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
-| `non_archived` | boolean | **{dotted-circle}** No | Return merge requests from non archived projects only. Default is `true`. |
-| `not` | Hash | **{dotted-circle}** No | Return merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. |
-| `order_by` | string | **{dotted-circle}** No | Return merge requests ordered by `created_at`, `title` or `updated_at` fields. Default is `created_at`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331625) in GitLab 14.8. |
+| `created_after` | datetime | **{dotted-circle}** No | Returns merge requests created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `created_before` | datetime | **{dotted-circle}** No | Returns merge requests created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `labels` | string | **{dotted-circle}** No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. |
+| `milestone` | string | **{dotted-circle}** No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
+| `my_reaction_emoji` | string | **{dotted-circle}** No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
+| `non_archived` | boolean | **{dotted-circle}** No | Returns merge requests from non archived projects only. Default is `true`. |
+| `not` | Hash | **{dotted-circle}** No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. |
+| `order_by` | string | **{dotted-circle}** No | Returns merge requests ordered by `created_at`, `title` or `updated_at` fields. Default is `created_at`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331625) in GitLab 14.8. |
| `reviewer_id` | integer | **{dotted-circle}** No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. |
| `reviewer_username` | string | **{dotted-circle}** No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49341) in GitLab 13.8. |
-| `scope` | string | **{dotted-circle}** No | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`. |
+| `scope` | string | **{dotted-circle}** No | Returns merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`. |
| `search` | string | **{dotted-circle}** No | Search merge requests against their `title` and `description`. |
-| `source_branch` | string | **{dotted-circle}** No | Return merge requests with the given source branch. |
-| `sort` | string | **{dotted-circle}** No | Return merge requests sorted in `asc` or `desc` order. Default is `desc`. |
-| `state` | string | **{dotted-circle}** No | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged`. |
-| `target_branch` | string | **{dotted-circle}** No | Return merge requests with the given target branch. |
-| `updated_after` | datetime | **{dotted-circle}** No | Return merge requests updated on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `updated_before` | datetime | **{dotted-circle}** No | Return merge requests updated on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `source_branch` | string | **{dotted-circle}** No | Returns merge requests with the given source branch. |
+| `sort` | string | **{dotted-circle}** No | Returns merge requests sorted in `asc` or `desc` order. Default is `desc`. |
+| `state` | string | **{dotted-circle}** No | Returns all merge requests or just those that are `opened`, `closed`, `locked`, or `merged`. |
+| `target_branch` | string | **{dotted-circle}** No | Returns merge requests with the given target branch. |
+| `updated_after` | datetime | **{dotted-circle}** No | Returns merge requests updated on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `updated_before` | datetime | **{dotted-circle}** No | Returns merge requests updated on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `view` | string | **{dotted-circle}** No | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request. |
| `with_labels_details` | boolean | **{dotted-circle}** No | If `true`, response returns more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. |
| `with_merge_status_recheck` | boolean | **{dotted-circle}** No | If `true`, this projection requests (but does not guarantee) that the `merge_status` field be recalculated asynchronously. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31890) in GitLab 13.0. |
@@ -601,7 +604,7 @@ For important notes on response data, read [Merge requests list response notes](
Shows information about a single merge request.
**Note**: the `changes_count` value in the response is a string, not an
-integer. This is because when an MR has too many changes to display and store,
+integer. This is because when an merge request has too many changes to display and store,
it is capped at 1,000. In that case, the API returns the string
`"1000+"` for the changes count.
@@ -633,7 +636,7 @@ Supported attributes:
| `closed_by` | object | User who closed this merge request. |
| `created_at` | datetime | Timestamp of when the merge request was created. |
| `description` | string | Description of the merge request. Contains Markdown rendered as HTML for caching. |
-| `detailed_merge_status` | string | Detailed merge status of the merge request. |
+| `detailed_merge_status` | string | Detailed merge status of the merge request. Read [merge status](#merge-status) for a list of potential values. |
| `diff_refs` | object | References of the base SHA, the head SHA, and the start SHA for this merge request. Corresponds to the latest diff version of the merge request. |
| `discussion_locked` | boolean | Indicates if comments on the merge request are locked to members only. |
| `downvotes` | integer | Number of downvotes for the merge request. |
@@ -651,7 +654,7 @@ Supported attributes:
| `merge_commit_sha` | string | SHA of the merge request commit. Returns `null` until merged. |
| `merge_error` | string | Error message due to a merge error. |
| `merge_user` | object | The user who merged this merge request, the user who set it to merge when pipeline succeeds, or `null`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/349031) in GitLab 14.7. |
-| `merge_status` | string | Status of the merge request. Can be `unchecked`, `checking`, `can_be_merged`, `cannot_be_merged`, or `cannot_be_merged_recheck`. Affects the `has_conflicts` property. [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/3169#note_1162532204) in GitLab 15.6. Use `detailed_merge_status` instead. |
+| `merge_status` | string | Status of the merge request. Can be `unchecked`, `checking`, `can_be_merged`, `cannot_be_merged`, or `cannot_be_merged_recheck`. Affects the `has_conflicts` property. For important notes on response data, read [Single merge request response notes](#single-merge-request-response-notes). [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/3169#note_1162532204) in GitLab 15.6. Use `detailed_merge_status` instead. |
| `merge_when_pipeline_succeeds` | boolean | Indicates if the merge has been set to be merged when its pipeline succeeds. |
| `merged_at` | datetime | Timestamp of when the merge request was merged. |
| `merged_by` | object | User who merged this merge request or set it to merge when pipeline succeeds. [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/350534) in GitLab 14.7, and scheduled for removal in [API version 5](https://gitlab.com/groups/gitlab-org/-/epics/8115). Use `merge_user` instead. |
@@ -831,27 +834,27 @@ Supported attributes:
### Merge status
-> - The `detailed_merge_status` field was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101724) in GitLab 15.6.
> - The `merge_status` field was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/3169#note_1162532204) in GitLab 15.6.
+> - The `detailed_merge_status` field was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101724) in GitLab 15.6.
Use `detailed_merge_status` instead of `merge_status` to account for all potential statuses.
-- The `detailed_merge_status` field may hold one of the following values:
- - `blocked_status`: Merge request is blocked by another merge request.
- - `broken_status`: Can not merge the source into the target branch, potential conflict.
- - `checking`: currently checking for mergeability.
- - `ci_must_pass`: Pipeline must succeed before merging.
- - `ci_still_running`: Pipeline is still running.
- - `discussions_not_resolved`: Discussions must be resolved before merging.
- - `draft_status`: Merge request must not be draft before merging.
- - `external_status_checks`: Status checks must pass.
- - `mergeable`: branch can be merged.
- - `not_approved`: Merge request must be approved before merging.
- - `not_open`: merge request must be open before merging.
- - `policies_denied`: There are denied policies for the merge request.
- - `unchecked`: merge status has not been checked.
-
-## Get single MR participants
+- The `detailed_merge_status` field can contain one of the following values related to the merge request:
+ - `blocked_status`: Blocked by another merge request.
+ - `broken_status`: Can't merge into the target branch due to a potential conflict.
+ - `checking`: Mergeability checks are still in progress.
+ - `ci_must_pass`: A CI/CD pipeline must succeed before merge.
+ - `ci_still_running`: A CI/CD pipeline is still running.
+ - `discussions_not_resolved`: All discussions must be resolved before merge.
+ - `draft_status`: Can't merge because the merge request is a draft.
+ - `external_status_checks`: All status checks must pass before merge.
+ - `mergeable`: The branch can merge cleanly into the target branch.
+ - `not_approved`: Approval is required before merge.
+ - `not_open`: The merge request must be open before merge.
+ - `policies_denied`: The merge request contains denied policies.
+ - `unchecked`: The merge status has not been checked.
+
+## Get single merge request participants
Get a list of merge request participants.
@@ -887,7 +890,7 @@ Supported attributes:
]
```
-## Get single MR reviewers
+## Get single merge request reviewers
Get a list of merge request reviewers.
@@ -931,7 +934,7 @@ Supported attributes:
]
```
-## Get single MR commits
+## Get single merge request commits
Get a list of merge request commits.
@@ -969,7 +972,11 @@ Supported attributes:
]
```
-## Get single MR changes
+## Get single merge request changes
+
+WARNING:
+This endpoint was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/322117) in GitLab 15.7
+and will be removed in API v5. Use the [List merge request diffs](#list-merge-request-diffs) endpoint instead.
Shows information about the merge request including its files and changes.
@@ -1101,7 +1108,71 @@ Supported attributes:
}
```
-## List MR pipelines
+## List merge request diffs
+
+List diffs of the files changed in a merge request.
+
+```plaintext
+GET /projects/:id/merge_requests/:merge_request_iid/diffs
+```
+
+Supported attributes:
+
+| Attribute | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `merge_request_iid` | integer | **{check-circle}** Yes | The internal ID of the merge request. |
+| `page` | integer | **{dotted-circle}** no | The page of results to return. Defaults to 1. |
+| `per_page` | integer | **{dotted-circle}** no | The number of results per page. Defaults to 20. |
+
+If successful, returns [`200 OK`](index.md#status-codes) and the
+following response attributes:
+
+| Attribute | Type | Description |
+|:----------|:-----|:------------|
+| `old_path` | string | Old path of the file. |
+| `new_path` | string | New path of the file. |
+| `a_mode` | string | Old file mode of the file. |
+| `b_mode` | string | New file mode of the file. |
+| `diff` | string | Diff representation of the changes made on the file. |
+| `new_file` | boolean | Indicates if the file has just been added. |
+| `renamed_file` | boolean | Indicates if the file has been renamed. |
+| `deleted_file` | boolean | Indicates if the file has been removed. |
+
+Example request:
+
+```shell
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/merge_requests/1/diffs?page=1&per_page=2"
+```
+
+Example response:
+
+```json
+[
+ {
+ "old_path": "README",
+ "new_path": "README",
+ "a_mode": "100644",
+ "b_mode": "100644",
+ "diff": "--- a/README\ +++ b/README\ @@ -1 +1 @@\ -Title\ +README",
+ "new_file": false,
+ "renamed_file": false,
+ "deleted_file": false
+ },
+ {
+ "old_path": "VERSION",
+ "new_path": "VERSION",
+ "a_mode": "100644",
+ "b_mode": "100644",
+ "diff": "--- a/VERSION\ +++ b/VERSION\ @@ -1 +1 @@\ -1.9.7\ +1.9.8",
+ "new_file": false,
+ "renamed_file": false,
+ "deleted_file": false
+ }
+]
+```
+
+## List merge request pipelines
Get a list of merge request pipelines.
@@ -1127,7 +1198,7 @@ Supported attributes:
]
```
-## Create MR Pipeline
+## Create merge request pipeline
Create a new [pipeline for a merge request](../ci/pipelines/merge_request_pipelines.md).
A pipeline created via this endpoint doesn't run a regular branch/tag pipeline.
@@ -1207,7 +1278,7 @@ POST /projects/:id/merge_requests
| `allow_maintainer_to_push` | boolean | **{dotted-circle}** No | Alias of `allow_collaboration`. |
| `approvals_before_merge` **(PREMIUM)** | integer | **{dotted-circle}** No | Number of approvals required before this can be merged (see below). |
| `assignee_id` | integer | **{dotted-circle}** No | Assignee user ID. |
-| `assignee_ids` | integer array | **{dotted-circle}** No | The ID of the users to assign the MR to. Set to `0` or provide an empty value to unassign all assignees. |
+| `assignee_ids` | integer array | **{dotted-circle}** No | The ID of the users to assign the merge request to. Set to `0` or provide an empty value to unassign all assignees. |
| `description` | string | **{dotted-circle}** No | Description of the merge request. Limited to 1,048,576 characters. |
| `labels` | string | **{dotted-circle}** No | Labels for the merge request, as a comma-separated list. |
| `milestone_id` | integer | **{dotted-circle}** No | The global ID of a milestone. |
@@ -1559,7 +1630,7 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git
## Merge a merge request
-Accept and merge changes submitted with MR using this API.
+Accept and merge changes submitted with merge request using this API.
```plaintext
PUT /projects/:id/merge_requests/:merge_request_iid/merge
@@ -2574,7 +2645,7 @@ Example response:
}
```
-## Get MR diff versions
+## Get merge request diff versions
Get a list of merge request diff versions. For an explanation of the SHAs in the response,
read [SHAs in the API response](#shas-in-the-api-response).
@@ -2624,7 +2695,7 @@ Example response:
| `head_commit_sha` | The HEAD commit of the source branch. |
| `start_commit_sha` | The HEAD commit SHA of the target branch when this version of the diff was created. |
-## Get a single MR diff version
+## Get a single merge request diff version
Get a single merge request diff version. For an explanation of the SHAs in the response,
read [SHAs in the API response](#shas-in-the-api-response).
diff --git a/doc/api/merge_trains.md b/doc/api/merge_trains.md
index 111cf5255d6..e8912aac759 100644
--- a/doc/api/merge_trains.md
+++ b/doc/api/merge_trains.md
@@ -154,3 +154,69 @@ Example response:
}
]
```
+
+## Get the status of a merge request on a merge train
+
+Get merge train information for the requested merge request.
+
+```plaintext
+GET /projects/:id/merge_trains/merge_requests/:merge_request_iid
+```
+
+Supported attributes:
+
+| Attribute | Type | Required | Description |
+| ------------------- | -------------- | -------- | ------------------------------------------------------------------------------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
+| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
+
+Example request:
+
+```shell
+curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/597/merge_trains/merge_requests/1"
+```
+
+Example response:
+
+```json
+{
+ "id": 267,
+ "merge_request": {
+ "id": 273,
+ "iid": 1,
+ "project_id": 597,
+ "title": "My title 9",
+ "description": null,
+ "state": "opened",
+ "created_at": "2022-10-31T19:06:05.725Z",
+ "updated_at": "2022-10-31T19:06:05.725Z",
+ "web_url": "http://localhost/namespace18/project21/-/merge_requests/1"
+ },
+ "user": {
+ "id": 933,
+ "username": "user12",
+ "name": "Sidney Jones31",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/6c8365de387cb3db10ecc7b1880203c4?s=80\u0026d=identicon",
+ "web_url": "http://localhost/user12"
+ },
+ "pipeline": {
+ "id": 273,
+ "iid": 1,
+ "project_id": 598,
+ "sha": "b83d6e391c22777fca1ed3012fce84f633d7fed0",
+ "ref": "main",
+ "status": "pending",
+ "source": "push",
+ "created_at": "2022-10-31T19:06:06.231Z",
+ "updated_at": "2022-10-31T19:06:06.231Z",
+ "web_url": "http://localhost/namespace19/project22/-/pipelines/273"
+ },
+ "created_at": "2022-10-31T19:06:06.237Z",
+ "updated_at":"2022-10-31T19:06:06.237Z",
+ "target_branch":"main",
+ "status":"idle",
+ "merged_at":null,
+ "duration":null
+}
+```
diff --git a/doc/api/packages.md b/doc/api/packages.md
index ffdd9fe7d11..6c5cf6bee1e 100644
--- a/doc/api/packages.md
+++ b/doc/api/packages.md
@@ -180,7 +180,7 @@ can result in malformed data or broken packages.
## Get a project package
-Get a single project package.
+Get a single project package. Only packages with status `default` are returned.
```plaintext
GET /projects/:id/packages/:package_id
@@ -256,7 +256,7 @@ Example response:
The `_links` object contains the following properties:
-- `web_path`: The path which you can visit in GitLab and see the details of the package.
+- `web_path`: The path which you can visit in GitLab and see the details of the package. Only available if the package has status `default`.
- `delete_api_path`: The API path to delete the package. Only available if the request user has permission to do so.
## List package files
diff --git a/doc/api/packages/pypi.md b/doc/api/packages/pypi.md
index 4e4c060d99b..e9546f50e36 100644
--- a/doc/api/packages/pypi.md
+++ b/doc/api/packages/pypi.md
@@ -32,7 +32,7 @@ Download a PyPI package file. The [simple API](#group-level-simple-api-entry-poi
normally supplies this URL.
```plaintext
-GET groups/:id/packages/pypi/files/:sha256/:file_identifier
+GET groups/:id/-/packages/pypi/files/:sha256/:file_identifier
```
| Attribute | Type | Required | Description |
@@ -42,13 +42,13 @@ GET groups/:id/packages/pypi/files/:sha256/:file_identifier
| `file_identifier` | string | yes | The PyPI package file's name. |
```shell
-curl --user <username>:<personal_access_token> "https://gitlab.example.com/api/v4/groups/1/packages/pypi/files/5y57017232013c8ac80647f4ca153k3726f6cba62d055cd747844ed95b3c65ff/my.pypi.package-0.0.1.tar.gz"
+curl --user <username>:<personal_access_token> "https://gitlab.example.com/api/v4/groups/1/-/packages/pypi/files/5y57017232013c8ac80647f4ca153k3726f6cba62d055cd747844ed95b3c65ff/my.pypi.package-0.0.1.tar.gz"
```
To write the output to a file:
```shell
-curl --user <username>:<personal_access_token> "https://gitlab.example.com/api/v4/groups/1/packages/pypi/files/5y57017232013c8ac80647f4ca153k3726f6cba62d055cd747844ed95b3c65ff/my.pypi.package-0.0.1.tar.gz" >> my.pypi.package-0.0.1.tar.gz
+curl --user <username>:<personal_access_token> "https://gitlab.example.com/api/v4/groups/1/-/packages/pypi/files/5y57017232013c8ac80647f4ca153k3726f6cba62d055cd747844ed95b3c65ff/my.pypi.package-0.0.1.tar.gz" >> my.pypi.package-0.0.1.tar.gz
```
This writes the downloaded file to `my.pypi.package-0.0.1.tar.gz` in the current
diff --git a/doc/api/packages/terraform-modules.md b/doc/api/packages/terraform-modules.md
index bb5b39b5161..6b3d6b477b2 100644
--- a/doc/api/packages/terraform-modules.md
+++ b/doc/api/packages/terraform-modules.md
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
This is the API documentation for [Terraform Modules](../../user/packages/terraform_module_registry/index.md).
WARNING:
-This API is used by the [terraform cli](https://www.terraform.io/)
+This API is used by the [Terraform CLI](https://www.terraform.io/)
and is generally not meant for manual consumption.
For instructions on how to upload and install Terraform modules from the GitLab
diff --git a/doc/api/pipelines.md b/doc/api/pipelines.md
index 8242f8cff00..669161049d5 100644
--- a/doc/api/pipelines.md
+++ b/doc/api/pipelines.md
@@ -274,7 +274,7 @@ Sample response:
Get the latest pipeline for a specific ref in a project.
```plaintext
-POST /projects/:id/pipeline/latest
+GET /projects/:id/pipelines/latest
```
| Attribute | Type | Required | Description |
diff --git a/doc/api/product_analytics.md b/doc/api/product_analytics.md
index e10327bc59b..90df1090f62 100644
--- a/doc/api/product_analytics.md
+++ b/doc/api/product_analytics.md
@@ -16,12 +16,13 @@ This feature is not ready for production use.
NOTE:
Make sure to define the `cube_api_base_url` and `cube_api_key` application settings first using [the API](settings.md).
-## Send request to Cube
+## Send query request to Cube
Generate an access token that can be used to query the Cube API. For example:
```plaintext
POST /projects/:id/product_analytics/request/load
+POST /projects/:id/product_analytics/request/dry-run
```
| Attribute | Type | Required | Description |
@@ -30,7 +31,7 @@ POST /projects/:id/product_analytics/request/load
### Request body
-The body of the request should be a valid Cube query.
+The body of the load request must be a valid Cube query.
```json
{
@@ -66,3 +67,15 @@ The body of the request should be a valid Cube query.
"queryType": "multi"
}
```
+
+## Send metadata request to Cube
+
+Return Cube Metadata for the Analytics data. For example:
+
+```plaintext
+GET /projects/:id/product_analytics/request/meta
+```
+
+| Attribute | Type | Required | Description |
+| --------- |------------------| -------- |---------------------------------------------------------------|
+| `id` | integer | yes | The ID of a project that the current user has read access to. |
diff --git a/doc/api/project_badges.md b/doc/api/project_badges.md
index d83aa370808..52d32ab17b9 100644
--- a/doc/api/project_badges.md
+++ b/doc/api/project_badges.md
@@ -13,6 +13,8 @@ Badges support placeholders that are replaced in real-time in both the link and
<!-- vale gitlab.Spelling = NO -->
- **%{project_path}**: Replaced by the project path.
+- **%{project_title}**: Replaced by the project title.
+- **%{project_name}**: Replaced by the project name.
- **%{project_id}**: Replaced by the project ID.
- **%{default_branch}**: Replaced by the project default branch.
- **%{commit_sha}**: Replaced by the last project's commit SHA.
diff --git a/doc/api/project_import_export.md b/doc/api/project_import_export.md
index 83d8746e1d0..a14f385cdb5 100644
--- a/doc/api/project_import_export.md
+++ b/doc/api/project_import_export.md
@@ -30,6 +30,9 @@ project to a web server or to any S3-compatible platform. For exports, GitLab:
The `upload[url]` parameter is required if the `upload` parameter is present.
+For uploads to Amazon S3, refer to [Generating a pre-signed URL for uploading objects](https://docs.aws.amazon.com/AmazonS3/latest/userguide/PresignedUrlUploadObject.html)
+documentation scripts to generate the `upload[url]`.
+
```plaintext
POST /projects/:id/export
```
@@ -187,7 +190,7 @@ requests.post(url, headers=headers, data=data, files=files)
NOTE:
The maximum import file size can be set by the Administrator, default is `0` (unlimited)..
-As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the [Application settings API](settings.md#change-application-settings) or the [Admin Area](../user/admin_area/settings/account_and_limit_settings.md). Default [modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50MB to 0 in GitLab 13.8.
+As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the [Application settings API](settings.md#change-application-settings) or the [Admin Area](../user/admin_area/settings/account_and_limit_settings.md). Default [modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50 MB to 0 in GitLab 13.8.
## Import a file from a remote object storage
diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md
index 29d3b38f977..afb7519d5f3 100644
--- a/doc/api/project_snippets.md
+++ b/doc/api/project_snippets.md
@@ -16,7 +16,7 @@ Constants for snippet visibility levels are:
| visibility | Description |
| ---------- | ----------- |
| `private` | The snippet is visible only to project members |
-| `internal` | The snippet is visible for any logged in user except [external users](../user/permissions.md#external-users) |
+| `internal` | The snippet is visible for any logged in user except [external users](../user/admin_area/external_users.md) |
| `public` | The snippet can be accessed without any authentication |
NOTE:
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 638af168f22..9ddb58b1436 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -16,7 +16,7 @@ The visibility level is determined by the `visibility` field in the project.
Values for the project visibility level are:
- `private`: project access must be granted explicitly to each user.
-- `internal`: the project can be cloned by any signed-in user except [external users](../user/permissions.md#external-users).
+- `internal`: the project can be cloned by any signed-in user except [external users](../user/admin_area/external_users.md).
- `public`: the project can be accessed without any authentication.
For more, read [Project visibility](../user/public_access.md).
@@ -51,10 +51,10 @@ GET /projects
| `id_after` | integer | **{dotted-circle}** No | Limit results to projects with IDs greater than the specified ID. |
| `id_before` | integer | **{dotted-circle}** No | Limit results to projects with IDs less than the specified ID. |
| `imported` | boolean | **{dotted-circle}** No | Limit results to projects which were imported from external systems by current user. |
-| `last_activity_after` | datetime | **{dotted-circle}** No | Limit results to projects with last_activity after specified time. Format: ISO 8601 (`YYYY-MM-DDTHH:MM:SSZ`) |
-| `last_activity_before` | datetime | **{dotted-circle}** No | Limit results to projects with last_activity before specified time. Format: ISO 8601 (`YYYY-MM-DDTHH:MM:SSZ`) |
+| `last_activity_after` | datetime | **{dotted-circle}** No | Limit results to projects with last activity after specified time. Format: ISO 8601 (`YYYY-MM-DDTHH:MM:SSZ`) |
+| `last_activity_before` | datetime | **{dotted-circle}** No | Limit results to projects with last activity before specified time. Format: ISO 8601 (`YYYY-MM-DDTHH:MM:SSZ`) |
| `membership` | boolean | **{dotted-circle}** No | Limit by projects that the current user is a member of. |
-| `min_access_level` | integer | **{dotted-circle}** No | Limit by current user minimal [access level](members.md#valid-access-levels). |
+| `min_access_level` | integer | **{dotted-circle}** No | Limit by current user minimal [role (`access_level`)](members.md#roles). |
| `order_by` | string | **{dotted-circle}** No | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, `last_activity_at`, or `similarity` fields. `repository_size`, `storage_size`, `packages_size` or `wiki_size` fields are only allowed for administrators. `similarity` ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/332890) in GitLab 14.1) is only available when searching and is limited to projects that the current user is a member of. Default is `created_at`. |
| `owned` | boolean | **{dotted-circle}** No | Limit by projects explicitly owned by the current user. |
| `repository_checksum_failed` **(PREMIUM)** | boolean | **{dotted-circle}** No | Limit projects where the repository checksum calculation has failed. |
@@ -333,7 +333,7 @@ GET /users/:user_id/projects
| `id_after` | integer | **{dotted-circle}** No | Limit results to projects with IDs greater than the specified ID. |
| `id_before` | integer | **{dotted-circle}** No | Limit results to projects with IDs less than the specified ID. |
| `membership` | boolean | **{dotted-circle}** No | Limit by projects that the current user is a member of. |
-| `min_access_level` | integer | **{dotted-circle}** No | Limit by current user minimal [access level](members.md#valid-access-levels). |
+| `min_access_level` | integer | **{dotted-circle}** No | Limit by current user minimal [role (`access_level`)](members.md#roles). |
| `order_by` | string | **{dotted-circle}** No | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at`. |
| `owned` | boolean | **{dotted-circle}** No | Limit by projects explicitly owned by the current user. |
| `search` | string | **{dotted-circle}** No | Return list of projects matching the search criteria. |
@@ -590,7 +590,7 @@ GET /users/:user_id/starred_projects
| `user_id` | string | **{check-circle}** Yes | The ID or username of the user. |
| `archived` | boolean | **{dotted-circle}** No | Limit by archived status. |
| `membership` | boolean | **{dotted-circle}** No | Limit by projects that the current user is a member of. |
-| `min_access_level` | integer | **{dotted-circle}** No | Limit by current user minimal [access level](members.md#valid-access-levels). |
+| `min_access_level` | integer | **{dotted-circle}** No | Limit by current user minimal [role (`access_level`)](members.md#roles). |
| `order_by` | string | **{dotted-circle}** No | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at`. |
| `owned` | boolean | **{dotted-circle}** No | Limit by projects explicitly owned by the current user. |
| `search` | string | **{dotted-circle}** No | Return list of projects matching the search criteria. |
@@ -1150,7 +1150,7 @@ GET /projects/:id/groups
|-----------------------------|-------------------|------------------------|-------------|
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
| `search` | string | **{dotted-circle}** No | Search for specific groups. |
-| `shared_min_access_level` | integer | **{dotted-circle}** No | Limit to shared groups with at least this [access level](members.md#valid-access-levels). |
+| `shared_min_access_level` | integer | **{dotted-circle}** No | Limit to shared groups with at least this [role (`access_level`)](members.md#roles). |
| `shared_visible_only` | boolean | **{dotted-circle}** No | Limit to shared groups user has access to. |
| `skip_groups` | array of integers | **{dotted-circle}** No | Skip the group IDs passed. |
| `with_shared` | boolean | **{dotted-circle}** No | Include projects shared with this group. Default is `false`. |
@@ -1250,6 +1250,10 @@ curl --request POST --header "PRIVATE-TOKEN: <your-token>" \
| `printing_merge_request_link_enabled` | boolean | **{dotted-circle}** No | Show link to create/view merge request when pushing from the command line. |
| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project members. |
| `releases_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
+| `environments_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
+| `feature_flags_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
+| `infrastructure_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
+| `monitor_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `remove_source_branch_after_merge` | boolean | **{dotted-circle}** No | Enable `Delete source branch` option by default for all new merge requests. |
| `repository_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `repository_storage` | string | **{dotted-circle}** No | Which storage shard the repository is on. _(administrator only)_ |
@@ -1330,6 +1334,10 @@ POST /projects/user/:user_id
| `printing_merge_request_link_enabled` | boolean | **{dotted-circle}** No | Show link to create/view merge request when pushing from the command line. |
| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project-members. |
| `releases_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
+| `environments_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
+| `feature_flags_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
+| `infrastructure_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
+| `monitor_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `remove_source_branch_after_merge` | boolean | **{dotted-circle}** No | Enable `Delete source branch` option by default for all new merge requests. |
| `repository_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `repository_storage` | string | **{dotted-circle}** No | Which storage shard the repository is on. _(administrators only)_ |
@@ -1434,6 +1442,10 @@ Supported attributes:
| `printing_merge_request_link_enabled` | boolean | **{dotted-circle}** No | Show link to create/view merge request when pushing from the command line. |
| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project members. |
| `releases_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
+| `environments_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
+| `feature_flags_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
+| `infrastructure_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
+| `monitor_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `remove_source_branch_after_merge` | boolean | **{dotted-circle}** No | Enable `Delete source branch` option by default for all new merge requests. |
| `repository_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `repository_storage` | string | **{dotted-circle}** No | Which storage shard the repository is on. _(administrators only)_ |
@@ -1496,7 +1508,7 @@ GET /projects/:id/forks
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
| `archived` | boolean | **{dotted-circle}** No | Limit by archived status. |
| `membership` | boolean | **{dotted-circle}** No | Limit by projects that the current user is a member of. |
-| `min_access_level` | integer | **{dotted-circle}** No | Limit by current user minimal [access level](members.md#valid-access-levels). |
+| `min_access_level` | integer | **{dotted-circle}** No | Limit by current user minimal [role (`access_level`)](members.md#roles). |
| `order_by` | string | **{dotted-circle}** No | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at`. |
| `owned` | boolean | **{dotted-circle}** No | Limit by projects explicitly owned by the current user. |
| `search` | string | **{dotted-circle}** No | Return list of projects matching the search criteria. |
@@ -2310,7 +2322,7 @@ POST /projects/:id/share
| Attribute | Type | Required | Description |
|----------------|----------------|------------------------|-------------|
-| `group_access` | integer | **{check-circle}** Yes | The [access level](members.md#valid-access-levels) to grant the group. |
+| `group_access` | integer | **{check-circle}** Yes | The [role (`access_level`)](members.md#roles) to grant the group. |
| `group_id` | integer | **{check-circle}** Yes | The ID of the group to share with. |
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
| `expires_at` | string | **{dotted-circle}** No | Share expiration date in ISO 8601 format: 2016-09-26 |
@@ -2462,7 +2474,7 @@ PUT /projects/:id/hooks/:hook_id
| `push_events` | boolean | **{dotted-circle}** No | Trigger hook on push events. |
| `releases_events` | boolean | **{dotted-circle}** No | Trigger hook on release events. |
| `tag_push_events` | boolean | **{dotted-circle}** No | Trigger hook on tag push events. |
-| `token` | string | **{dotted-circle}** No | Secret token to validate received payloads; this isn't returned in the response. |
+| `token` | string | **{dotted-circle}** No | Secret token to validate received payloads. Not returned in the response. When you change the webhook URL, the secret token is reset and not retained. |
| `wiki_page_events` | boolean | **{dotted-circle}** No | Trigger hook on wiki page events. |
### Delete project hook
diff --git a/doc/api/protected_branches.md b/doc/api/protected_branches.md
index 96bd5c15f13..96d4240b3ef 100644
--- a/doc/api/protected_branches.md
+++ b/doc/api/protected_branches.md
@@ -464,7 +464,7 @@ To delete:
```shell
curl --header 'Content-Type: application/json' --request PATCH \
- --data '{"push_access_levels": [{"group_id": 9899829, access_level: 40}]}' \
+ --data '{"allowed_to_push": [{"access_level": 40}]}' \
--header "PRIVATE-TOKEN: <your_access_token>" \
"https://gitlab.example.com/api/v4/projects/22034114/protected_branches/master"
```
@@ -474,13 +474,13 @@ Example response:
```json
{
"name": "master",
- "allowed_to_push": [
+ "push_access_levels": [
{
"id": 12,
"access_level": 40,
- "access_level_description": "Administrator",
+ "access_level_description": "Maintainers",
"user_id": null,
- "group_id": 9899829
+ "group_id": null
}
]
}
@@ -489,21 +489,23 @@ Example response:
### Example: update a `push_access_level` record
```shell
-curl --header 'Content-Type: application/json' --request PUT \
- --data '{"push_access_levels": [{"id": 12, "group_id": 22034120}]' \
+curl --header 'Content-Type: application/json' --request PATCH \
+ --data '{"allowed_to_push": [{"id": 12, "access_level": 0}]' \
--header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/22034114/protected_branches/master"
```
+Example response:
+
```json
{
"name": "master",
- "deploy_access_levels": [
+ "push_access_levels": [
{
"id": 12,
- "access_level": 40,
- "access_level_description": "Administrator",
+ "access_level": 0,
+ "access_level_description": "No One",
"user_id": null,
- "group_id": 22034120
+ "group_id": null
}
]
}
@@ -512,8 +514,8 @@ curl --header 'Content-Type: application/json' --request PUT \
### Example: delete a `push_access_level` record
```shell
-curl --header 'Content-Type: application/json' --request PUT \
- --data '{"push_access_levels": [{"id": 12, "_destroy": true}]' \
+curl --header 'Content-Type: application/json' --request PATCH \
+ --data '{"allowed_to_push": [{"id": 12, "_destroy": true}]}' \
--header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/22034114/protected_branches/master"
```
diff --git a/doc/api/repository_files.md b/doc/api/repository_files.md
index 1db614fce36..e105f5ee5e3 100644
--- a/doc/api/repository_files.md
+++ b/doc/api/repository_files.md
@@ -214,6 +214,7 @@ GET /projects/:id/repository/files/:file_path/raw
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
| `file_path` | string | yes | URL-encoded full path to new file, such as `lib%2Fclass%2Erb`. |
| `ref` | string | yes | The name of branch, tag or commit. Default is the `HEAD` of the project. |
+| `lfs` | boolean | no | Determines if the response should be Git LFS file contents, rather than the pointer. If the file is not tracked by Git LFS, ignored. Defaults to `false`. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fmodels%2Fkey%2Erb/raw?ref=master"
diff --git a/doc/api/runners.md b/doc/api/runners.md
index f690e0cb9c1..62d5e41c877 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -14,8 +14,8 @@ There are two tokens to take into account when connecting a runner with GitLab.
| Token | Description |
| ----- | ----------- |
-| Registration token | Token used to [register the runner](https://docs.gitlab.com/runner/register/). It can be [obtained through GitLab](../ci/runners/index.md). |
-| Authentication token | Token used to authenticate the runner with the GitLab instance. It is obtained automatically when you [register a runner](https://docs.gitlab.com/runner/register/) or by the Runner API when you manually [register a runner](#register-a-new-runner-deprecated) or [reset the authentication token](#reset-runners-authentication-token-by-using-the-runner-id). |
+| Registration token | Token used to [register the runner](https://docs.gitlab.com/runner/register/). It can be [obtained through GitLab](../ci/runners/index.md). |
+| Authentication token | Token used to authenticate the runner with the GitLab instance. It is obtained automatically when you [register a runner](https://docs.gitlab.com/runner/register/) or by the Runner API when you manually [register a runner](#register-a-new-runner) or [reset the authentication token](#reset-runners-authentication-token-by-using-the-runner-id). |
Here's an example of how the two tokens are used in runner registration:
@@ -640,12 +640,7 @@ Example response:
]
```
-## Register a new runner (deprecated)
-
-> [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102579) in GitLab 15.6.
-
-WARNING:
-This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102579) in GitLab 15.6 and is planned for removal in 16.0. This change is a breaking change.
+## Register a new runner
Register a new runner for the instance.
@@ -655,18 +650,18 @@ POST /runners
| Attribute | Type | Required | Description |
|--------------------|--------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `token` | string | yes | [Registration token](#registration-and-authentication-tokens) |
-| `description` | string | no | Runner's description |
+| `token` | string | yes | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): [Registration token](#registration-and-authentication-tokens). The registration token will be replaced by an authentication token in GitLab 16.0 |
+| `description` | string | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): Runner's description |
| `info` | hash | no | Runner's metadata. You can include `name`, `version`, `revision`, `platform`, and `architecture`, but only `version`, `platform`, and `architecture` are displayed in the Admin Area of the UI |
-| `active` | boolean | no | Deprecated: Use `paused` instead. Specifies whether the runner is allowed to receive new jobs |
-| `paused` | boolean | no | Specifies whether the runner should ignore new jobs |
-| `locked` | boolean | no | Specifies whether the runner should be locked for the current project |
-| `run_untagged` | boolean | no | Specifies whether the runner should handle untagged jobs |
-| `tag_list` | string array | no | A list of runner tags |
-| `access_level` | string | no | The access level of the runner; `not_protected` or `ref_protected` |
-| `maximum_timeout` | integer | no | Maximum timeout that limits the amount of time (in seconds) that runners can run jobs |
-| `maintainer_note` | string | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/350730), see `maintenance_note` |
-| `maintenance_note` | string | no | Free-form maintenance notes for the runner (1024 characters) |
+| `active` | boolean | no | Deprecated: Use `paused` instead. Specifies whether the runner is allowed to receive new jobs |
+| `paused` | boolean | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): Specifies whether the runner should ignore new jobs |
+| `locked` | boolean | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): Specifies whether the runner should be locked for the current project |
+| `run_untagged` | boolean | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): Specifies whether the runner should handle untagged jobs |
+| `tag_list` | string array | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): A list of runner tags |
+| `access_level` | string | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): The access level of the runner; `not_protected` or `ref_protected` |
+| `maximum_timeout` | integer | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): Maximum timeout that limits the amount of time (in seconds) that runners can run jobs |
+| `maintainer_note` | string | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/350730), see `maintenance_note` |
+| `maintenance_note` | string | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): Free-form maintenance notes for the runner (1024 characters) |
```shell
curl --request POST "https://gitlab.example.com/api/v4/runners" \
@@ -762,7 +757,11 @@ Response:
## Reset instance's runner registration token
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30942) in GitLab 14.3.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30942) in GitLab 14.3.
+> - [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104691) in GitLab 15.7.
+
+WARNING:
+This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/383341) in GitLab 15.7 and is planned for removal in 16.0. This change is a breaking change.
Reset the runner registration token for the GitLab instance.
@@ -777,7 +776,11 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
## Reset project's runner registration token
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30942) in GitLab 14.3.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30942) in GitLab 14.3.
+> - [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104691) in GitLab 15.7.
+
+WARNING:
+This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/383341) in GitLab 15.7 and is planned for removal in 16.0. This change is a breaking change.
Reset the runner registration token for a project.
@@ -792,7 +795,11 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
## Reset group's runner registration token
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30942) in GitLab 14.3.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30942) in GitLab 14.3.
+> - [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104691) in GitLab 15.7.
+
+WARNING:
+This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/383341) in GitLab 15.7 and is planned for removal in 16.0. This change is a breaking change.
Reset the runner registration token for a group.
diff --git a/doc/api/saml.md b/doc/api/saml.md
index 810ed382d49..4b0e57111cc 100644
--- a/doc/api/saml.md
+++ b/doc/api/saml.md
@@ -35,9 +35,7 @@ response attributes:
Example request:
```shell
-curl --location --request GET "https://gdk.test:3443/api/v4/groups/33/saml/identities" \
---header "<PRIVATE-TOKEN>" \
---form "extern_uid=<ID_TO_BE_UPDATED>" \
+curl --location --request GET "https://gitlab.example.com/api/v4/groups/33/saml/identities" --header "<PRIVATE-TOKEN>"
```
Example response:
@@ -53,7 +51,7 @@ Example response:
## Update `extern_uid` field for a SAML identity
-Update `extern_uid` field for a SAML identity. Field that can be updated are:
+Update `extern_uid` field for a SAML identity:
| SAML IdP attribute | GitLab field |
| ------------------ | ------------ |
@@ -72,7 +70,7 @@ Parameters:
Example request:
```shell
-curl --location --request PATCH "https://gdk.test:3443/api/v4/groups/33/saml/sydney_jones" \
+curl --location --request PATCH "https://gitlab.example.com/api/v4/groups/33/saml/sydney_jones" \
--header "<PRIVATE TOKEN>" \
--form "extern_uid=sydney_jones_new" \
```
diff --git a/doc/api/scim.md b/doc/api/scim.md
index 53897cadf90..0ee9779ccbd 100644
--- a/doc/api/scim.md
+++ b/doc/api/scim.md
@@ -8,6 +8,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98354) in GitLab 15.5.
+GitLab provides an SCIM API that both implements [the RFC7644 protocol](https://tools.ietf.org/html/rfc7644)
+and provides the `/Users` endpoint. The base URL is `/api/scim/v2/groups/:group_path/Users/`.
+
To use this API, [Group SSO](../user/group/saml_sso/index.md) must be enabled for the group.
This API is only in use where [SCIM for Group SSO](../user/group/saml_sso/scim_setup.md) is enabled. It's a prerequisite to the creation of SCIM identities.
@@ -50,11 +53,10 @@ Example request:
```shell
curl --location --request GET "https://gitlab.example.com/api/v4/groups/33/scim/identities" \
---header "PRIVATE-TOKEN: <PRIVATE-TOKEN>" \
---form "extern_uid=<ID_TO_BE_UPDATED>" \
+--header "PRIVATE-TOKEN: <PRIVATE-TOKEN>"
```
-## Update extern_uid field for a SCIM identity
+## Update `extern_uid` field for a SCIM identity
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227841) in GitLab 15.5.
@@ -79,5 +81,5 @@ Example request:
```shell
curl --location --request PATCH "https://gitlab.example.com/api/v4/groups/33/scim/sydney_jones" \
--header "PRIVATE-TOKEN: <PRIVATE TOKEN>" \
---form "extern_uid=sydney_jones_new" \
+--form "extern_uid=sydney_jones_new"
```
diff --git a/doc/api/settings.md b/doc/api/settings.md
index 78dc81c4f84..7d55180db94 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -36,6 +36,7 @@ Example response:
"signup_enabled" : true,
"id" : 1,
"default_branch_protection" : 2,
+ "default_preferred_language" : "en",
"restricted_visibility_levels" : [],
"password_authentication_enabled_for_web" : true,
"after_sign_out_path" : null,
@@ -104,12 +105,15 @@ Example response:
"floc_enabled": false,
"external_pipeline_validation_service_timeout": null,
"external_pipeline_validation_service_token": null,
- "external_pipeline_validation_service_url": null
+ "external_pipeline_validation_service_url": null,
+ "jira_connect_application_key": null,
+ "jira_connect_proxy_url": null
}
```
Users on [GitLab Premium or Ultimate](https://about.gitlab.com/pricing/) may also see
-the `group_owners_can_manage_default_branch_protection`, `file_template_project_id`, `delayed_project_deletion`, `delayed_group_deletion`, `deletion_adjourned_period`, or the `geo_node_allowed_ips` parameters:
+the `group_owners_can_manage_default_branch_protection`, `file_template_project_id`, `delayed_project_deletion`,
+`delayed_group_deletion`, `deletion_adjourned_period`, `disable_personal_access_tokens`, or the `geo_node_allowed_ips` parameters:
```json
{
@@ -121,6 +125,7 @@ the `group_owners_can_manage_default_branch_protection`, `file_template_project_
"delayed_project_deletion": false,
"delayed_group_deletion": false,
"deletion_adjourned_period": 7,
+ "disable_personal_access_tokens": false,
...
}
```
@@ -144,6 +149,7 @@ Example response:
{
"id": 1,
"default_projects_limit": 100000,
+ "default_preferred_language": "en",
"signup_enabled": false,
"password_authentication_enabled_for_web": true,
"gravatar_enabled": true,
@@ -218,7 +224,9 @@ Example response:
"external_pipeline_validation_service_timeout": null,
"external_pipeline_validation_service_token": null,
"external_pipeline_validation_service_url": null,
- "can_create_group": false
+ "can_create_group": false,
+ "jira_connect_application_key": "123",
+ "jira_connect_proxy_url": "http://gitlab.example.com"
}
```
@@ -232,6 +240,7 @@ these parameters:
- `delayed_project_deletion`
- `delayed_group_deletion`
- `deletion_adjourned_period`
+- `disable_personal_access_tokens`
Example responses: **(PREMIUM SELF)**
@@ -286,6 +295,7 @@ listed in the descriptions of the relevant settings.
| `default_branch_protection` | integer | no | Determine if developers can push to the default branch. Can take: `0` _(not protected, both users with the Developer role or Maintainer role can push new commits and force push)_, `1` _(partially protected, users with the Developer role or Maintainer role can push new commits, but cannot force push)_ or `2` _(fully protected, users with the Developer or Maintainer role cannot push new commits, but users with the Developer or Maintainer role can; no one can force push)_ as a parameter. Default is `2`. |
| `default_ci_config_path` | string | no | Default CI/CD configuration file and path for new projects (`.gitlab-ci.yml` if not set). |
| `default_group_visibility` | string | no | What visibility level new groups receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. |
+| `default_preferred_language` | string | no | Default preferred language for users who are not logged in. |
| `default_project_creation` | integer | no | Default project creation protection. Can take: `0` _(No one)_, `1` _(Maintainers)_ or `2` _(Developers + Maintainers)_|
| `default_project_visibility` | string | no | What visibility level new projects receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. |
| `default_projects_limit` | integer | no | Project limit per user. Default is `100000`. |
@@ -299,10 +309,11 @@ listed in the descriptions of the relevant settings.
| `diff_max_lines` | integer | no | Maximum [lines in a diff](../user/admin_area/diff_limits.md). |
| `disable_admin_oauth_scopes` | boolean | no | Stops administrators from connecting their GitLab accounts to non-trusted OAuth 2.0 applications that have the `api`, `read_api`, `read_repository`, `write_repository`, `read_registry`, `write_registry`, or `sudo` scopes. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/375043) in GitLab 15.6. |
| `disable_feed_token` | boolean | no | Disable display of RSS/Atom and calendar feed tokens. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/231493) in GitLab 13.7. |
+| `disable_personal_access_token` **(PREMIUM SELF)** | boolean | no | Disable personal access tokens. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/384201) in GitLab 15.7. |
| `disabled_oauth_sign_in_sources` | array of strings | no | Disabled OAuth sign-in sources. |
| `dns_rebinding_protection_enabled` | boolean | no | Enforce DNS rebinding attack protection. |
| `domain_denylist_enabled` | boolean | no | (**If enabled, requires:** `domain_denylist`) Allows blocking sign-ups from emails from specific domains. |
-| `domain_denylist` | array of strings | no | Users with email addresses that match these domains **cannot** sign up. Wildcards allowed. Use separate lines for multiple entries. Ex: `domain.com`, `*.domain.com`. |
+| `domain_denylist` | array of strings | no | Users with email addresses that match these domains **cannot** sign up. Wildcards allowed. Use separate lines for multiple entries. For example: `domain.com`, `*.domain.com`. |
| `domain_allowlist` | array of strings | no | Force people to use only corporate emails for sign-up. Default is `null`, meaning there is no restriction. |
| `dsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded DSA key. Default is `0` (no restriction). `-1` disables DSA keys. |
| `ecdsa_key_restriction` | integer | no | The minimum allowed curve size (in bits) of an uploaded ECDSA key. Default is `0` (no restriction). `-1` disables ECDSA keys. |
@@ -331,6 +342,7 @@ listed in the descriptions of the relevant settings.
| `elasticsearch_password` **(PREMIUM)** | string | no | The password of your Elasticsearch instance. |
| `email_additional_text` **(PREMIUM)** | string | no | Additional text added to the bottom of every email for legal/auditing/compliance reasons. |
| `email_author_in_body` | boolean | no | Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead. |
+| `email_confirmation_setting` | string | no | Specifies whether users must confirm their email before sign in. Possible values are `off`, `soft`, and `hard`. |
| `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. |
| `enforce_namespace_storage_limit` | boolean | no | Enabling this permits enforcement of namespace storage limits. |
| `enforce_terms` | boolean | no | (**If enabled, requires:** `terms`) Enforce application ToS to all users. |
@@ -389,6 +401,7 @@ listed in the descriptions of the relevant settings.
| `max_pages_size` | integer | no | Maximum size of pages repositories in MB. |
| `max_personal_access_token_lifetime` **(ULTIMATE SELF)** | integer | no | Maximum allowable lifetime for access tokens in days. |
| `max_ssh_key_lifetime` **(ULTIMATE SELF)** | integer | no | Maximum allowable lifetime for SSH keys in days. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1007) in GitLab 14.6. |
+| `max_terraform_state_size_bytes` | integer | no | Maximum size in bytes of the [Terraform state](../administration/terraform_state.md) files. Set this to 0 for unlimited file size. |
| `metrics_method_call_threshold` | integer | no | A method call is only tracked when it takes longer than the given amount of milliseconds. |
| `max_number_of_repository_downloads` **(ULTIMATE SELF)** | integer | no | Maximum number of unique repositories a user can download in the specified time period before they are banned. Default: 0, Maximum: 10,000 repositories. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87980) in GitLab 15.1. |
| `max_number_of_repository_downloads_within_time_period` **(ULTIMATE SELF)** | integer | no | Reporting time period (in seconds). Default: 0, Maximum: 864000 seconds (10 days). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87980) in GitLab 15.1. |
@@ -439,13 +452,12 @@ listed in the descriptions of the relevant settings.
| `require_two_factor_authentication` | boolean | no | (**If enabled, requires:** `two_factor_grace_period`) Require all users to set up Two-factor authentication. |
| `restricted_visibility_levels` | array of strings | no | Selected levels cannot be used by non-Administrator users for groups, projects or snippets. Can take `private`, `internal` and `public` as a parameter. Default is `null` which means there is no restriction. |
| `rsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded RSA key. Default is `0` (no restriction). `-1` disables RSA keys. |
-| `send_user_confirmation_email` | boolean | no | Send confirmation email on sign-up. |
| `session_expire_delay` | integer | no | Session duration in minutes. GitLab restart is required to apply changes. |
| `shared_runners_enabled` | boolean | no | (**If enabled, requires:** `shared_runners_text` and `shared_runners_minutes`) Enable shared runners for new projects. |
| `shared_runners_minutes` **(PREMIUM)** | integer | required by: `shared_runners_enabled` | Set the maximum number of CI/CD minutes that a group can use on shared runners per month. |
| `shared_runners_text` | string | required by: `shared_runners_enabled` | Shared runners text. |
| `sidekiq_job_limiter_mode` | string | no | `track` or `compress`. Sets the behavior for [Sidekiq job size limits](../user/admin_area/settings/sidekiq_job_limits.md). Default: 'compress'. |
-| `sidekiq_job_limiter_compression_threshold_bytes` | integer | no | The threshold in bytes at which Sidekiq jobs are compressed before being stored in Redis. Default: 100 000 bytes (100KB). |
+| `sidekiq_job_limiter_compression_threshold_bytes` | integer | no | The threshold in bytes at which Sidekiq jobs are compressed before being stored in Redis. Default: 100,000 bytes (100 KB). |
| `sidekiq_job_limiter_limit_bytes` | integer | no | The threshold in bytes at which Sidekiq jobs are rejected. Default: 0 bytes (doesn't reject any job). |
| `sign_in_text` | string | no | Text on the login page. |
| `signin_enabled` | string | no | (Deprecated: Use `password_authentication_enabled_for_web` instead) Flag indicating if password authentication is enabled for the web interface. |
@@ -455,7 +467,7 @@ listed in the descriptions of the relevant settings.
| `slack_app_secret` **(PREMIUM)** | string | required by: `slack_app_enabled` | The app secret of the Slack-app. |
| `slack_app_signing_secret` **(PREMIUM)** | string | no | The signing secret of the Slack-app. |
| `slack_app_verification_token` **(PREMIUM)** | string | required by: `slack_app_enabled` | The verification token of the Slack-app. |
-| `snippet_size_limit` | integer | no | Max snippet content size in **bytes**. Default: 52428800 Bytes (50MB).|
+| `snippet_size_limit` | integer | no | Max snippet content size in **bytes**. Default: 52428800 Bytes (50 MB).|
| `snowplow_app_id` | string | no | The Snowplow site name / application ID. (for example, `gitlab`) |
| `snowplow_collector_hostname` | string | required by: `snowplow_enabled` | The Snowplow collector hostname. (for example, `snowplow.trx.gitlab.net`) |
| `snowplow_cookie_domain` | string | no | The Snowplow cookie domain. (for example, `.gitlab.com`) |
@@ -505,6 +517,8 @@ listed in the descriptions of the relevant settings.
| `whats_new_variant` | string | no | What's new variant, possible values: `all_tiers`, `current_tier`, and `disabled`. |
| `web_ide_clientside_preview_enabled` | boolean | no | Live Preview (allow live previews of JavaScript projects in the Web IDE using CodeSandbox Live Preview). |
| `wiki_page_max_content_bytes` | integer | no | Maximum wiki page content size in **bytes**. Default: 52428800 Bytes (50 MB). The minimum value is 1024 bytes. |
+| `jira_connect_application_key` | String | no | Application ID of the OAuth application that should be used to authenticate with the GitLab.com for Jira Cloud app |
+| `jira_connect_proxy_url` | String | no | URL of the GitLab instance that should be used as a proxy for the GitLab.com for Jira Cloud app |
### Package Registry: Package file size limits
diff --git a/doc/api/snippets.md b/doc/api/snippets.md
index 593985b5d5f..c312642a450 100644
--- a/doc/api/snippets.md
+++ b/doc/api/snippets.md
@@ -20,7 +20,7 @@ Valid values for snippet visibility levels are:
| Visibility | Description |
|:-----------|:----------------------------------------------------|
| `private` | Snippet is visible only to the snippet creator. |
-| `internal` | Snippet is visible for any logged in user except [external users](../user/permissions.md#external-users). |
+| `internal` | Snippet is visible for any logged in user except [external users](../user/admin_area/external_users.md). |
| `public` | Snippet can be accessed without any authentication. |
## List all snippets for a user
diff --git a/doc/api/status_checks.md b/doc/api/status_checks.md
index e6a9c633418..7299e529bda 100644
--- a/doc/api/status_checks.md
+++ b/doc/api/status_checks.md
@@ -71,6 +71,186 @@ POST /projects/:id/merge_requests/:merge_request_iid/status_check_responses
NOTE:
`sha` must be the SHA at the `HEAD` of the merge request's source branch.
+## Retry failed status check for a merge request
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/383200) in GitLab 15.7.
+
+For a single merge request, retry the specified failed external status check. Even
+though the merge request hasn't changed, this endpoint resends the current state of
+merge request to the defined external service.
+
+```plaintext
+POST /projects/:id/merge_requests/:merge_request_iid/status_checks/:external_status_check_id/retry
+```
+
+**Parameters:**
+
+| Attribute | Type | Required | Description |
+| -------------------------- | ------- | -------- | ------------------------------------- |
+| `id` | integer | yes | ID of a project |
+| `merge_request_iid` | integer | yes | IID of a merge request |
+| `external_status_check_id` | integer | yes | ID of a failed external status check |
+
+## Response
+
+In case of success status code is 202.
+
+```json
+{
+ "message": "202 Accepted"
+}
+```
+
+In case status check is already passed status code is 422
+
+```json
+{
+ "message": "External status check must be failed"
+}
+```
+
+## Example payload sent to external service
+
+```json
+{
+ "object_kind": "merge_request",
+ "event_type": "merge_request",
+ "user": {
+ "id": 1,
+ "name": "Administrator",
+ "username": "root",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "email": "[REDACTED]"
+ },
+ "project": {
+ "id": 6,
+ "name": "Flight",
+ "description": "Ipsa minima est consequuntur quisquam.",
+ "web_url": "http://example.com/flightjs/Flight",
+ "avatar_url": null,
+ "git_ssh_url": "ssh://example.com/flightjs/Flight.git",
+ "git_http_url": "http://example.com/flightjs/Flight.git",
+ "namespace": "Flightjs",
+ "visibility_level": 20,
+ "path_with_namespace": "flightjs/Flight",
+ "default_branch": "master",
+ "ci_config_path": null,
+ "homepage": "http://example.com/flightjs/Flight",
+ "url": "ssh://example.com/flightjs/Flight.git",
+ "ssh_url": "ssh://example.com/flightjs/Flight.git",
+ "http_url": "http://example.com/flightjs/Flight.git"
+ },
+ "object_attributes": {
+ "assignee_id": null,
+ "author_id": 1,
+ "created_at": "2022-12-07 07:53:43 UTC",
+ "description": "",
+ "head_pipeline_id": 558,
+ "id": 144,
+ "iid": 4,
+ "last_edited_at": null,
+ "last_edited_by_id": null,
+ "merge_commit_sha": null,
+ "merge_error": null,
+ "merge_params": {
+ "force_remove_source_branch": "1"
+ },
+ "merge_status": "can_be_merged",
+ "merge_user_id": null,
+ "merge_when_pipeline_succeeds": false,
+ "milestone_id": null,
+ "source_branch": "root-master-patch-30152",
+ "source_project_id": 6,
+ "state_id": 1,
+ "target_branch": "master",
+ "target_project_id": 6,
+ "time_estimate": 0,
+ "title": "Update README.md",
+ "updated_at": "2022-12-07 07:53:43 UTC",
+ "updated_by_id": null,
+ "url": "http://example.com/flightjs/Flight/-/merge_requests/4",
+ "source": {
+ "id": 6,
+ "name": "Flight",
+ "description": "Ipsa minima est consequuntur quisquam.",
+ "web_url": "http://example.com/flightjs/Flight",
+ "avatar_url": null,
+ "git_ssh_url": "ssh://example.com/flightjs/Flight.git",
+ "git_http_url": "http://example.com/flightjs/Flight.git",
+ "namespace": "Flightjs",
+ "visibility_level": 20,
+ "path_with_namespace": "flightjs/Flight",
+ "default_branch": "master",
+ "ci_config_path": null,
+ "homepage": "http://example.com/flightjs/Flight",
+ "url": "ssh://example.com/flightjs/Flight.git",
+ "ssh_url": "ssh://example.com/flightjs/Flight.git",
+ "http_url": "http://example.com/flightjs/Flight.git"
+ },
+ "target": {
+ "id": 6,
+ "name": "Flight",
+ "description": "Ipsa minima est consequuntur quisquam.",
+ "web_url": "http://example.com/flightjs/Flight",
+ "avatar_url": null,
+ "git_ssh_url": "ssh://example.com/flightjs/Flight.git",
+ "git_http_url": "http://example.com/flightjs/Flight.git",
+ "namespace": "Flightjs",
+ "visibility_level": 20,
+ "path_with_namespace": "flightjs/Flight",
+ "default_branch": "master",
+ "ci_config_path": null,
+ "homepage": "http://example.com/flightjs/Flight",
+ "url": "ssh://example.com/flightjs/Flight.git",
+ "ssh_url": "ssh://example.com/flightjs/Flight.git",
+ "http_url": "http://example.com/flightjs/Flight.git"
+ },
+ "last_commit": {
+ "id": "141be9714669a4c1ccaa013c6a7f3e462ff2a40f",
+ "message": "Update README.md",
+ "title": "Update README.md",
+ "timestamp": "2022-12-07T07:52:11+00:00",
+ "url": "http://example.com/flightjs/Flight/-/commit/141be9714669a4c1ccaa013c6a7f3e462ff2a40f",
+ "author": {
+ "name": "Administrator",
+ "email": "admin@example.com"
+ }
+ },
+ "work_in_progress": false,
+ "total_time_spent": 0,
+ "time_change": 0,
+ "human_total_time_spent": null,
+ "human_time_change": null,
+ "human_time_estimate": null,
+ "assignee_ids": [
+ ],
+ "reviewer_ids": [
+ ],
+ "labels": [
+ ],
+ "state": "opened",
+ "blocking_discussions_resolved": true,
+ "first_contribution": false,
+ "detailed_merge_status": "mergeable"
+ },
+ "labels": [
+ ],
+ "changes": {
+ },
+ "repository": {
+ "name": "Flight",
+ "url": "ssh://example.com/flightjs/Flight.git",
+ "description": "Ipsa minima est consequuntur quisquam.",
+ "homepage": "http://example.com/flightjs/Flight"
+ },
+ "external_approval_rule": {
+ "id": 1,
+ "name": "QA",
+ "external_url": "https://example.com/"
+ }
+}
+```
+
## Get project external status checks
You can request information about a project's external status checks using the following endpoint:
diff --git a/doc/api/tags.md b/doc/api/tags.md
index 35085baf93f..099448d5609 100644
--- a/doc/api/tags.md
+++ b/doc/api/tags.md
@@ -176,3 +176,56 @@ Parameters:
| ---------- | -------------- | -------- | --------------------------------------------------------------------------------------------------------------- |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `tag_name` | string | yes | The name of a tag |
+
+## Get X.509 signature of a tag
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106578) in GitLab 15.7.
+
+Get the [X.509 signature from a tag](../user/project/repository/x509_signed_commits/index.md#sign-commits-and-tags-with-x509-certificates),
+if it is signed. Unsigned tags return a `404 Not Found` response.
+
+```plaintext
+GET /projects/:id/repository/tags/:tag_name/signature
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `tag_name` | string | yes | The name of a tag. |
+
+```shell
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/repository/tags/v1.1.1/signature"
+```
+
+Example response if tag is X.509 signed:
+
+```json
+{
+ "signature_type": "X509",
+ "verification_status": "unverified",
+ "x509_certificate": {
+ "id": 1,
+ "subject": "CN=gitlab@example.org,OU=Example,O=World",
+ "subject_key_identifier": "BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC",
+ "email": "gitlab@example.org",
+ "serial_number": 278969561018901340486471282831158785578,
+ "certificate_status": "good",
+ "x509_issuer": {
+ "id": 1,
+ "subject": "CN=PKI,OU=Example,O=World",
+ "subject_key_identifier": "AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB",
+ "crl_url": "http://example.com/pki.crl"
+ }
+ }
+}
+```
+
+Example response if tag is unsigned:
+
+```json
+{
+ "message": "404 GPG Signature Not Found"
+}
+```
diff --git a/doc/api/templates/gitlab_ci_ymls.md b/doc/api/templates/gitlab_ci_ymls.md
index b7048795313..29ee93c4e45 100644
--- a/doc/api/templates/gitlab_ci_ymls.md
+++ b/doc/api/templates/gitlab_ci_ymls.md
@@ -5,9 +5,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
type: reference
---
-# GitLab CI YAMLs API **(FREE)**
+# GitLab CI YAML API **(FREE)**
-In GitLab, there is an API endpoint available to work with GitLab CI/CD YAMLs. For more
+In GitLab, there is an API endpoint available to work with GitLab CI/CD YAML. For more
information on CI/CD pipeline configuration in GitLab, see the
[configuration reference documentation](../../ci/yaml/index.md).
diff --git a/doc/api/todos.md b/doc/api/todos.md
index 8fd76864f1c..2bfae1972b7 100644
--- a/doc/api/todos.md
+++ b/doc/api/todos.md
@@ -20,14 +20,14 @@ GET /todos
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `action` | string | no | The action to be filtered. Can be `assigned`, `mentioned`, `build_failed`, `marked`, `approval_required`, `unmergeable`, `directly_addressed` or `merge_train_removed`. |
-| `author_id` | integer | no | The ID of an author |
-| `project_id` | integer | no | The ID of a project |
-| `group_id` | integer | no | The ID of a group |
-| `state` | string | no | The state of the to-do item. Can be either `pending` or `done` |
-| `type` | string | no | The type of to-do item. Can be either `Issue`, `MergeRequest`, `Commit`, `Epic`, `DesignManagement::Design` or `AlertManagement::Alert` |
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `action` | string | no | The action to be filtered. Can be `assigned`, `mentioned`, `build_failed`, `marked`, `approval_required`, `unmergeable`, `directly_addressed`, `merge_train_removed` or `member_access_requested`. |
+| `author_id` | integer | no | The ID of an author |
+| `project_id` | integer | no | The ID of a project |
+| `group_id` | integer | no | The ID of a group |
+| `state` | string | no | The state of the to-do item. Can be either `pending` or `done` |
+| `type` | string | no | The type of to-do item. Can be either `Issue`, `MergeRequest`, `Commit`, `Epic`, `DesignManagement::Design`, `AlertManagement::Alert` or `Namespace` |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/todos"
diff --git a/doc/api/users.md b/doc/api/users.md
index 19c25236f55..382d5fe03c1 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -39,7 +39,10 @@ GET /users
]
```
-You can also search for users by name, username, or public email by using `?search=`. For example. `/users?search=John`.
+You can also use `?search=` to search for users by name, username, or public email. For example, `/users?search=John`. When you search for a:
+
+- Public email, you must use the full email address to get an exact match.
+- Name or username, you do not have to get an exact match because this is a fuzzy search.
In addition, you can lookup users by username:
@@ -240,6 +243,11 @@ the `group_saml` provider option and `provisioned_by_group_id` parameter:
]
```
+You can also use `?search=` to search for users by name, username, or email. For example, `/users?search=John`. When you search for a:
+
+- Email, you must use the full email address to get an exact match. As an administrator, you can search for both public and private email addresses.
+- Name or username, you do not have to get an exact match because this is a fuzzy search.
+
You can lookup users by external UID and provider:
```plaintext
@@ -1087,6 +1095,8 @@ Parameters:
## Add SSH key
+> The `usage_type` parameter was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105551) in GitLab 15.7.
+
Creates a new key owned by the currently authenticated user.
```plaintext
@@ -1100,12 +1110,14 @@ Parameters:
| `title` | string | yes | New SSH key's title |
| `key` | string | yes | New SSH key |
| `expires_at` | string | no | Expiration date of the SSH key in ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`) |
+| `usage_type` | string | no | Scope of usage for the SSH key: `auth`, `signing` or `auth_and_signing`. Default: `auth_and_signing` |
```json
{
"title": "ABC",
"key": "ssh-dss AAAAB3NzaC1kc3MAAACBAMLrhYgI3atfrSD6KDas1b/3n6R/HP+bLaHHX6oh+L1vg31mdUqK0Ac/NjZoQunavoyzqdPYhFz9zzOezCrZKjuJDS3NRK9rspvjgM0xYR4d47oNZbdZbwkI4cTv/gcMlquRy0OvpfIvJtjtaJWMwTLtM5VhRusRuUlpH99UUVeXAAAAFQCVyX+92hBEjInEKL0v13c/egDCTQAAAIEAvFdWGq0ccOPbw4f/F8LpZqvWDydAcpXHV3thwb7WkFfppvm4SZte0zds1FJ+Hr8Xzzc5zMHe6J4Nlay/rP4ewmIW7iFKNBEYb/yWa+ceLrs+TfR672TaAgO6o7iSRofEq5YLdwgrwkMmIawa21FrZ2D9SPao/IwvENzk/xcHu7YAAACAQFXQH6HQnxOrw4dqf0NqeKy1tfIPxYYUZhPJfo9O0AmBW2S36pD2l14kS89fvz6Y1g8gN/FwFnRncMzlLY/hX70FSc/3hKBSbH6C6j8hwlgFKfizav21eS358JJz93leOakJZnGb8XlWvz1UJbwCsnR2VEY8Dz90uIk1l/UqHkA= loic@call",
- "expires_at": "2016-01-21T00:00:00.000Z"
+ "expires_at": "2016-01-21T00:00:00.000Z",
+ "usage_type": "auth"
}
```
@@ -1127,6 +1139,8 @@ error occurs a `400 Bad Request` is returned with a message explaining the error
## Add SSH key for user **(FREE SELF)**
+> The `usage_type` parameter was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105551) in GitLab 15.7.
+
Create new key owned by specified user. Available only for administrator.
```plaintext
@@ -1141,6 +1155,32 @@ Parameters:
| `title` | string | yes | New SSH key's title |
| `key` | string | yes | New SSH key |
| `expires_at` | string | no | Expiration date of the SSH key in ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`) |
+| `usage_type` | string | no | Scope of usage for the SSH key: `auth`, `signing` or `auth_and_signing`. Default: `auth_and_signing` |
+
+```json
+{
+ "title": "ABC",
+ "key": "ssh-dss AAAAB3NzaC1kc3MAAACBAMLrhYgI3atfrSD6KDas1b/3n6R/HP+bLaHHX6oh+L1vg31mdUqK0Ac/NjZoQunavoyzqdPYhFz9zzOezCrZKjuJDS3NRK9rspvjgM0xYR4d47oNZbdZbwkI4cTv/gcMlquRy0OvpfIvJtjtaJWMwTLtM5VhRusRuUlpH99UUVeXAAAAFQCVyX+92hBEjInEKL0v13c/egDCTQAAAIEAvFdWGq0ccOPbw4f/F8LpZqvWDydAcpXHV3thwb7WkFfppvm4SZte0zds1FJ+Hr8Xzzc5zMHe6J4Nlay/rP4ewmIW7iFKNBEYb/yWa+ceLrs+TfR672TaAgO6o7iSRofEq5YLdwgrwkMmIawa21FrZ2D9SPao/IwvENzk/xcHu7YAAACAQFXQH6HQnxOrw4dqf0NqeKy1tfIPxYYUZhPJfo9O0AmBW2S36pD2l14kS89fvz6Y1g8gN/FwFnRncMzlLY/hX70FSc/3hKBSbH6C6j8hwlgFKfizav21eS358JJz93leOakJZnGb8XlWvz1UJbwCsnR2VEY8Dz90uIk1l/UqHkA= loic@call",
+ "expires_at": "2016-01-21T00:00:00.000Z",
+ "usage_type": "auth"
+}
+```
+
+Returns a created key with status `201 Created` on success. If an
+error occurs a `400 Bad Request` is returned with a message explaining the error:
+
+```json
+{
+ "message": {
+ "fingerprint": [
+ "has already been taken"
+ ],
+ "key": [
+ "has already been taken"
+ ]
+ }
+}
+```
NOTE:
This also adds an audit event, as described in [audit instance events](../administration/audit_events.md#instance-events). **(PREMIUM)**
diff --git a/doc/api/vulnerabilities.md b/doc/api/vulnerabilities.md
index b82e2b6cbdd..6ee2bbf9811 100644
--- a/doc/api/vulnerabilities.md
+++ b/doc/api/vulnerabilities.md
@@ -23,9 +23,9 @@ instead. See the [GraphQL examples](#replace-vulnerability-rest-api-with-graphql
Every API call to vulnerabilities must be [authenticated](index.md#authentication).
-Vulnerability permissions inherit permissions from their project. If a project is
-private, and a user isn't a member of the project to which the vulnerability
-belongs, requests to that project returns a `404 Not Found` status code.
+If an authenticated user does not have permission to
+[view vulnerabilities](../user/permissions.md#project-members-permissions),
+this request returns a `403 Forbidden` status code.
## Single vulnerability
diff --git a/doc/api/vulnerability_exports.md b/doc/api/vulnerability_exports.md
index 8c166c2ba2a..94f373c1c0a 100644
--- a/doc/api/vulnerability_exports.md
+++ b/doc/api/vulnerability_exports.md
@@ -19,14 +19,11 @@ Every API call to vulnerability exports must be [authenticated](index.md#authent
Creates a new vulnerability export for a project.
-Vulnerability export permissions inherit permissions from their project. If a project is
-private and a user isn't a member of the project to which the vulnerability
-belongs, requests to that project return a `404 Not Found` status code.
-Vulnerability exports can be only accessed by the export's author.
-
If an authenticated user doesn't have permission to
[create a new vulnerability](../user/permissions.md#project-members-permissions),
-this request results in a `403` status code.
+this request returns a `403 Forbidden` status code.
+
+Vulnerability exports can be only accessed by the export's author.
```plaintext
POST /security/projects/:id/vulnerability_exports
@@ -65,14 +62,11 @@ Example response:
Creates a new vulnerability export for a group.
-Vulnerability export permissions inherit permissions from their group. If a group is
-private and a user isn't a member of the group to which the vulnerability
-belongs, requests to that group return a `404 Not Found` status code.
-Vulnerability exports can be only accessed by the export's author.
-
If an authenticated user doesn't have permission to
[create a new vulnerability](../user/permissions.md#group-members-permissions),
-this request results in a `403` status code.
+this request returns a `403 Forbidden` status code.
+
+Vulnerability exports can be only accessed by the export's author.
```plaintext
POST /security/groups/:id/vulnerability_exports
diff --git a/doc/api/vulnerability_findings.md b/doc/api/vulnerability_findings.md
index a236960bc68..89acbdee98a 100644
--- a/doc/api/vulnerability_findings.md
+++ b/doc/api/vulnerability_findings.md
@@ -16,13 +16,9 @@ To fix any broken integrations with the former Vulnerabilities API, change the `
Every API call to vulnerability findings must be [authenticated](index.md#authentication).
-Vulnerability findings are project-bound entities. If a user is not
-a member of a project and the project is private, a request on
-that project results in a `404` status code.
-
-If a user is able to access the project but does not have permission to
+If a user does not have permission to
[use the Project Security Dashboard](../user/permissions.md#project-members-permissions),
-any request for vulnerability findings of this project results in a `403` status code.
+any request for vulnerability findings of this project returns a `403 Forbidden` status code.
WARNING:
This API is in the process of being deprecated and considered unstable.
diff --git a/doc/architecture/blueprints/_template.md b/doc/architecture/blueprints/_template.md
index 798d51a97ad..e39c2b51a5b 100644
--- a/doc/architecture/blueprints/_template.md
+++ b/doc/architecture/blueprints/_template.md
@@ -1,6 +1,6 @@
---
status: proposed
-creation-date: yyyy-mm-dd
+creation-date: "yyyy-mm-dd"
authors: [ "@username" ]
coach: "@username"
approvers: [ "@product-manager", "@engineering-manager" ]
@@ -10,7 +10,8 @@ participating-stages: []
<!--
**Note:** Please remove comment blocks for sections you've filled in.
-When your blueprint is complete, all of these comment blocks should be removed.
+When your blueprint ready for review, all of these comment blocks should be
+removed.
To get started with a blueprint you can use this template to inform you about
what you may want to document in it at the beginning. This content will change
diff --git a/doc/architecture/blueprints/ci_data_decay/index.md b/doc/architecture/blueprints/ci_data_decay/index.md
index b7c3bdde2f8..6df37e28992 100644
--- a/doc/architecture/blueprints/ci_data_decay/index.md
+++ b/doc/architecture/blueprints/ci_data_decay/index.md
@@ -23,7 +23,7 @@ builds [continues to grow exponentially](../ci_scale/index.md).
GitLab CI/CD has come a long way since the initial release, but the design of
the data storage for pipeline builds remains almost the same since 2012. In
2021 we started working on database decomposition and extracting CI/CD data to
-ia separate database. Now we want to improve the architecture of GitLab CI/CD
+a separate database. Now we want to improve the architecture of GitLab CI/CD
product to enable further scaling.
## Goals
@@ -78,7 +78,7 @@ pipeline processing in such pipeline. It means that all the metadata, we store
in PostgreSQL, that is needed to efficiently and reliably process builds can be
safely moved to a different data store.
-Currently, storing pipeline processing data is expensive as this kind of CI/CD
+Storing pipeline processing data is expensive as this kind of CI/CD
data represents a significant portion of data stored in CI/CD tables. Once we
restrict access to processing archived pipelines, we can move this metadata to
a different place - preferably object storage - and make it accessible on
diff --git a/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md b/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
index d61412ae1ed..261390d1d14 100644
--- a/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
+++ b/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
@@ -75,7 +75,7 @@ incidents, over the last couple of months, for example:
- S2: 2022-04-12 [Transactions detected that have been running for more than 10m](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/6821)
- S2: 2022-04-06 [Database contention plausibly caused by excessive `ci_builds` reads](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/6773)
- S2: 2022-03-18 [Unable to remove a foreign key on `ci_builds`](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/6642)
-- S2: 2022-10-10 [The queuing_queries_duration SLI apdex violating SLO](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/7852#note_1130123525)
+- S2: 2022-10-10 [The `queuing_queries_duration` SLI apdex violating SLO](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/7852#note_1130123525)
We have approximately 50 `ci_*` prefixed database tables, and some of them
would benefit from partitioning.
@@ -180,6 +180,49 @@ respective database tables. Using `RANGE` partitioning works similarly to using
of `partition_id` values, using `RANGE` partitioning might be a better
strategy.
+### Multi-project pipelines
+
+Parent-child pipeline will always be part of the same partition because child
+pipelines are considered a resource of the parent pipeline. They can't be
+viewed individually in the project pipeline list page.
+
+On the other hand, multi-project pipelines can be viewed in the pipeline list page.
+They can also be accessed from the pipeline graph as downstream/upstream links
+when created via the `trigger` token or the API using a job token.
+They can also be created from other pipelines by using trigger tokens, but in this
+case we don't store the source pipeline.
+
+While partitioning `ci_builds` we need to update the foreign keys to the
+`ci_sources_pipelines` table:
+
+```plain
+Foreign-key constraints:
+ "fk_be5624bf37" FOREIGN KEY (source_job_id) REFERENCES ci_builds(id) ON DELETE CASCADE
+ "fk_d4e29af7d7" FOREIGN KEY (source_pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE
+ "fk_e1bad85861" FOREIGN KEY (pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE
+```
+
+A `ci_sources_pipelines` record references two `ci_pipelines` rows (parent and
+the child). Our usual strategy has been to add a `partition_id` to the
+table, but if we do it here we will force all multi-project
+pipelines to be part of the same partition.
+
+We should add two `partition_id` columns for this table, a
+`partition_id` and a `source_partition_id`:
+
+```plain
+Foreign-key constraints:
+ "fk_be5624bf37" FOREIGN KEY (source_job_id, source_partition_id) REFERENCES ci_builds(id, source_partition_id) ON DELETE CASCADE
+ "fk_d4e29af7d7" FOREIGN KEY (source_pipeline_id, source_partition_id) REFERENCES ci_pipelines(id, source_partition_id) ON DELETE CASCADE
+ "fk_e1bad85861" FOREIGN KEY (pipeline_id, partition_id) REFERENCES ci_pipelines(id, partition_id) ON DELETE CASCADE
+```
+
+This solution is the closest to a two way door decision because:
+
+- We retain the ability to reference pipelines in different partitions.
+- If we later decide that we want to force multi-project pipelines in the same partition
+ we could add a constraint to validate that both columns have the same value.
+
## Why do we want to use explicit logical partition ids?
Partitioning CI/CD data using a logical `partition_id` has several benefits. We
@@ -248,10 +291,9 @@ smart enough to move rows between partitions on its own.
A partitioned table is called a **routing** table and it will use the `p_`
prefix which should help us with building automated tooling for query analysis.
-A table partition will be called **partition** and it can use the a
-physical partition ID as suffix, leaded by a `p` letter, for example
-`ci_builds_p101`. Existing CI tables will become **zero partitions** of the
-new routing tables. Depending on the chosen
+A table partition will be called **partition** and it can use the a physical
+partition ID as suffix, for example `ci_builds_101`. Existing CI tables will
+become **zero partitions** of the new routing tables. Depending on the chosen
[partitioning strategy](#how-do-we-want-to-partition-cicd-data) for a given
table, it is possible to have many logical partitions per one physical partition.
@@ -273,6 +315,37 @@ during a low traffic period([after `00:00 UTC`](https://dashboards.gitlab.net/d/
See an example of this strategy in our [partition tooling](../../../development/database/table_partitioning.md#step-6---create-parent-table-and-attach-existing-table-as-the-initial-partition)).
+### Partitioning steps
+
+The database [partition tooling](../../../development/database/table_partitioning.md#partitioning-a-table-list)
+docs contain a list of steps to partition a table, but the steps are not enough
+for our iterative strategy. As our dataset continues to grow we want to take
+advantage of partitioning performance right away and not wait until all tables
+are partitioned. For example, after partitioning the `ci_builds_metadata` table
+we want to start writing and reading data to/from a new partition. This means
+that we will increase the `partition_id` value from `100`, the default value,
+to `101`. Now all of the new resources for the pipeline hierarchy will be
+persisted with `partition_id = 101`. We can continue following the database
+tooling instructions for the next table that will be partitioned, but we require
+a few extra steps:
+
+- add `partition_id` column for the FK references with default value of `100`
+ since the majority of records should have that value.
+- change application logic to cascade the `partition_id` value
+- correct `partition_id` values for recent records with a post deploy/background
+ migration, similar to this:
+
+ ```sql
+ UPDATE ci_pipeline_metadata
+ SET partition_id = ci_pipelines.partition_id
+ FROM ci_pipelines
+ WHERE ci_pipelines.id = ci_pipeline_metadata.pipeline_id
+ AND ci_pipelines.partition_id in (101, 102);
+ ```
+
+- change the foreign key definitions
+- ...
+
## Storing partitions metadata in the database
To build an efficient mechanism that will be responsible for creating
@@ -297,7 +370,7 @@ system - any letter from `g` to `z` in Latin alphabet, for example `x`. In that
case an example of an URI would look like `1e240x5ba0`. If we decide to update
the primary identifier of a partitioned resource (today it is just a big
integer) it is important to design a system that is resilient to migrating data
-between partitions, to avoid changing idenfiers when rebalancing happens.
+between partitions, to avoid changing identifiers when rebalancing happens.
`ci_partitions` table will store information about a partition identifier,
pipeline ids range it is valid for and whether the partitions have been
diff --git a/doc/architecture/blueprints/ci_pipeline_components/index.md b/doc/architecture/blueprints/ci_pipeline_components/index.md
index a3c72227f3e..100c9e67fda 100644
--- a/doc/architecture/blueprints/ci_pipeline_components/index.md
+++ b/doc/architecture/blueprints/ci_pipeline_components/index.md
@@ -3,7 +3,7 @@ status: proposed
creation-date: "2022-09-14"
authors: [ "@fabio", "@grzesiek" ]
coach: "@kamil"
-approvers: [ "@dov" ]
+approvers: [ "@dhershkovitch", "@marknuzzo" ]
owning-stage: "~devops::verify"
participating-stages: []
---
@@ -110,7 +110,8 @@ identifying abstract concepts and are subject to changes as we refine the design
## Definition of pipeline component
-A pipeline component is a reusable single-purpose building block that abstracts away a single pipeline configuration unit. Components are used to compose a part or entire pipeline configuration.
+A pipeline component is a reusable single-purpose building block that abstracts away a single pipeline configuration unit.
+Components are used to compose a part or entire pipeline configuration.
It can optionally take input parameters and set output data to be adaptable and reusable in different pipeline contexts,
while encapsulating and isolating implementation details.
@@ -133,27 +134,319 @@ For best experience with any systems made of components it's fundamental that co
The version identifies the exact interface and behavior of the component.
- **Resolvable**: when a component depends on another component, this dependency must be explicit and trackable.
-## Proposal
+## Structure of a component
-Prerequisites to create a component:
+A pipeline component is identified by the path to a repository or directory that defines it
+and a specific version: `<component-path>@<version>`.
-- Create a project. Description and avatar are highly recommended to improve discoverability.
-- Add a `README.md` in the top level directory that documents the component.
- What it does, how to use it, how to contribute, etc.
- This file is mandatory.
-- Add a `.gitlab-ci.yml` in the top level directory to test that the components works as expected.
- This file is highly recommended.
+For example: `gitlab-org/dast@1.0`.
-Characteristics of a component:
+### The component path
-- It must have a **name** to be referenced to and **description** for extra details.
-- It must specify its **type** which defines how it can be used (raw configuration to be `include`d, child pipeline workflow, job step).
-- It must define its **content** based on the type.
-- It must specify **input parameters** that it accepts. Components should depend on input parameters for dynamic values and not environment variables.
-- It can optionally define **output data** that it returns.
-- Its YAML specification should be **validated statically** (for example: using JSON schema validators).
-- It should be possible to use specific **versions** of a component by referencing official releases and SHA.
-- It should be possible to use components defined locally in the same repository.
+A component path must contain at least the metadata YAML and optionally a related `README.md` documentation file.
+
+The component path can be:
+
+- A path to a project: `gitlab-org/dast`. In this case the 2 files are defined in the root directory of the repository.
+- A path to a project subdirectory: `gitlab-org/dast/api-scan`. In this case the 2 files are defined in the `api-scan` directory.
+- A path to a local directory: `/path/to/component`. This path must contain the metadata YAML that defines the component.
+ The path must start with `/` to indicate a full path in the repository.
+
+The metadata YAML file follows the filename convention `gitlab-<component-type>.yml` where component type is one of:
+
+| Component type | Context |
+| -------------- | ------- |
+| `template` | For components used under `include:` keyword |
+| `step` | For components used under `steps:` keyword |
+| `workflow` | For components used under `trigger:` keyword |
+
+Based on the context where the component is used we fetch the correct YAML file.
+For example, if we are including a component `gitlab-org/dast@1.0` we expect a YAML file named `gitlab-template.yml` in the
+top level directory of `gitlab-org/dast` repository.
+
+A `gitlab-<component-type>.yml` file:
+
+- Must have a **name** to be referenced to and **description** for extra details.
+- Must specify its **type** in the filename, which defines how it can be used (raw configuration to be `include`d, child pipeline workflow, job step).
+- Must define its **content** based on the type.
+- Must specify **input parameters** that it accepts. Components should depend on input parameters for dynamic values and not environment variables.
+- Can optionally define **output data** that it returns.
+- Should be **validated statically** (for example: using JSON schema validators).
+
+```yaml
+spec:
+ inputs:
+ website:
+ environment:
+ default: test
+ test_run:
+ options:
+ - unit
+ - integration
+ - system
+content: { ... }
+```
+
+Components that are released in the catalog must have a `README.md` file in the same directory as the
+metadata YAML file. The `README.md` represents the documentation for the specific component, hence it's recommended
+even when not releasing versions in the catalog.
+
+### The component version
+
+The version of the component can be (in order of highest priority first):
+
+1. A commit SHA - For example: `gitlab-org/dast@e3262fdd0914fa823210cdb79a8c421e2cef79d8`
+1. A released tag - For example: `gitlab-org/dast@1.0`
+1. A special moving target version that points to the most recent released tag - For example: `gitlab-org/dast@~latest`
+1. An unreleased tag - For example: `gitlab-org/dast@rc-1.0`
+1. A branch name - For example: `gitlab-org/dast@master`
+
+If a tag and branch exist with the same name, the tag takes precedence over the branch.
+Similarly, if a tag is named `e3262fdd0914fa823210cdb79a8c421e2cef79d8`, a commit SHA (if exists)
+takes precedence over the tag.
+
+As we want to be able to reference any revisions (even those not released), a component must be defined in a Git repository.
+
+NOTE:
+When referencing a component by local path (for example `./path/to/component`), its version is implicit and matches
+the commit SHA of the current pipeline context.
+
+## Components project
+
+A components project is a GitLab project/repository that exclusively hosts one or more pipeline components.
+
+For components projects it's highly recommended to set an appropriate avatar and project description
+to improve discoverability in the catalog.
+
+### Structure of a components project
+
+A project can host one or more components depending on whether the author wants to define a single component
+per project or include multiple cohesive components under the same project.
+
+Let's imagine we are developing a component that runs RSpec tests for a Rails app. We create a component project
+called `myorg/rails-rspec`.
+
+The following directory structure would support 1 component per project:
+
+```plaintext
+.
+├── gitlab-<type>.yml
+├── README.md
+└── .gitlab-ci.yml
+```
+
+The `.gitlab-ci.yml` is recommended for the project to ensure changes are verified accordingly.
+
+The component is now identified by the path `myorg/rails-rspec`. In other words, this means that
+the `gitlab-<type>.yml` and `README.md` are located in the root directory of the repository.
+
+The following directory structure would support multiple components per project:
+
+```plaintext
+.
+├── .gitlab-ci.yml
+├── unit/
+│ ├── gitlab-workflow.yml
+│ └── README.md
+├── integration/
+│ ├── gitlab-workflow.yml
+│ └── README.md
+└── feature/
+ ├── gitlab-workflow.yml
+ └── README.md
+```
+
+In this example we are defining multiple test profiles that are executed with RSpec.
+The user could choose to use one or more of these.
+
+Each of these components are identified by their path `myorg/rails-rspec/unit`, `myorg/rails-rspec/integration`
+and `myorg/rails-rspec/feature`.
+
+This directory structure could also support both strategies:
+
+```plaintext
+.
+├── gitlab-template.yml # myorg/rails-rspec
+├── README.md
+├── .gitlab-ci.yml
+├── unit/
+│ ├── gitlab-workflow.yml # myorg/rails-rspec/unit
+│ └── README.md
+├── integration/
+│ ├── gitlab-workflow.yml # myorg/rails-rspec/integration
+│ └── README.md
+└── feature/
+ ├── gitlab-workflow.yml # myorg/rails-rspec/feature
+ └── README.md
+```
+
+With the above structure we could have a top-level component that can be used as the
+default component. For example, `myorg/rails-rspec` could run all the test profiles together.
+However, more specific test profiles could be used separately (for example `myorg/rails-rspec/integration`).
+
+NOTE:
+Any nesting more than 1 level is initially not permitted.
+This limitation encourages cohesion at project level and keeps complexity low.
+
+## Input parameters `spec:inputs:` parameters
+
+If the component takes any input parameters they must be specified according to the following schema:
+
+```yaml
+spec:
+ inputs:
+ website: # by default all declared inputs are mandatory.
+ environment:
+ default: test # apply default if not provided. This makes the input optional.
+ test_run:
+ options: # a choice must be made from the list since there is no default value.
+ - unit
+ - integration
+ - system
+```
+
+When using the component we pass the input parameters as follows:
+
+```yaml
+include:
+ - component: org/my-component@1.0
+ with:
+ website: ${MY_WEBSITE} # variables expansion
+ test_run: system
+ environment: $[[ inputs.environment ]] # interpolation of upstream inputs
+```
+
+Variables expansion must be supported for `with:` syntax as well as interpolation of
+possible [inputs provided upstream](#input-parameters-for-pipelines).
+
+Input parameters are validated as soon as possible:
+
+1. Read the file `gitlab-template.yml` inside `org/my-component`.
+1. Parse `spec:inputs` and validate the parameters against this schema.
+1. If successfully validated, proceed with parsing `content:`. Return an error otherwise.
+1. Interpolate input parameters inside the component's `content:`.
+
+```yaml
+spec:
+ inputs:
+ environment:
+ options: [test, staging, production]
+content:
+ "run-tests-$[[ inputs.environment ]]":
+ script: ./run-test
+
+ scan-website:
+ script: ./scan-website $[[ inputs.environment ]]
+ rules:
+ - if: $[[ inputs.environment ]] == 'staging'
+ - if: $[[ inputs.environment ]] == 'production'
+```
+
+With `$[[ inputs.XXX ]]` inputs are interpolated immediately after parsing the `content:`.
+
+### Why input parameters and not environment variables?
+
+Until today we have been leveraging environment variables to pass information around.
+For example, we use environment variables to pass information from an upstream pipeline to a
+downstream pipeline.
+
+Using environment variables for passing information to a component is like declaring global
+variables in programming languages. The more variables we declare the more we risk variable
+conflicts and increase variables scope.
+
+Input parameters are like variables passed to the component which exist inside a specific
+scope and they don't leak to the outside.
+Inputs are not inherited from upstream `include`s. They must be passed explicitly.
+
+This paradigm allows to build more robust and isolated components as well as declare and
+enforce contracts.
+
+### Input parameters for existing `include:` syntax
+
+Because we are adding input parameters to components used via `include:component` we have an opportunity to
+extend it to other `include:` types support inputs via `with:` syntax:
+
+```yaml
+include:
+ - component: org/my-component@1.0
+ with:
+ foo: bar
+ - local: path/to/file.yml
+ with:
+ foo: bar
+ - project: org/another
+ file: .gitlab-ci.yml
+ with:
+ foo: bar
+ - remote: http://example.com/ci/config
+ with:
+ foo: bar
+ - template: Auto-DevOps.gitlab-ci.yml
+ with:
+ foo: bar
+```
+
+Then the configuration being included must specify the inputs:
+
+```yaml
+spec:
+ inputs:
+ foo:
+
+# rest of the configuration
+```
+
+If a YAML includes content using `with:` but the including YAML doesn't specify `inputs:`, an error should be raised.
+
+|`with:`| `inputs:` | result |
+| --- | --- | --- |
+| specified | | raise error |
+| specified | specified | validate inputs |
+| | specified | use defaults |
+| | | legacy `include:` without input passing |
+
+### Input parameters for pipelines
+
+Inputs can also be used to pass parameters to a pipeline when triggered and benefit from immediate validation.
+
+Today we have different use cases where using explicit input parameters would be beneficial:
+
+1. `Run Pipeline` UI form.
+ - **Problem today**: We are using top-level variables with `variables:*:description` to surface environment variables to the UI.
+ The problem with this is the mix of responsibilities as well as the jump in [precedence](../../../ci/variables/index.md#cicd-variable-precedence)
+ that a variable gets (from a YAML variable to a pipeline variable).
+ Building validation and features on top of this solution is challenging and complex.
+1. Trigger a pipeline via API. For example `POST /projects/:id/pipelines/trigger` with `{ inputs: { provider: 'aws' } }`
+1. Trigger a pipeline via `trigger:` syntax.
+
+```yaml
+deploy-app:
+ trigger:
+ project: org/deployer
+ with:
+ provider: aws
+ deploy_environment: staging
+```
+
+To solve the problem of `Run Pipeline` UI form we could fully leverage the `spec:inputs` schema:
+
+```yaml
+spec:
+ inputs:
+ concurrency:
+ default: 10 # displayed as default value in the input box
+ provider: # can enforce `required` in the form validation
+ description: Deployment provider # optional: render as input label.
+ deploy_environment:
+ options: # render a selectbox with options in order of how they are defined below
+ - staging # 1st option
+ - canary # 2nd option
+ - production # 3rd option
+ default: staging # selected by default in the UI.
+ # if `default:` is not specified, the user must explicitly select
+ # an option.
+ description: Deployment environment # optional: render as input label.
+```
## Limits
@@ -188,3 +481,34 @@ Some limits we could consider adding:
- Allow self-managed administrators to populate their self-managed catalog by importing/updating
components from GitLab.com or from repository exports.
- Iterate on feedback.
+
+## Who
+
+Proposal:
+
+<!-- vale gitlab.Spelling = NO -->
+
+| Role | Who
+|--------------------------------|-------------------------|
+| Author | Fabio Pitino |
+| Engineering Leaders | Cheryl Li, Mark Nuzzo |
+| Product Manager | Dov Hershkovitch |
+| Architecture Evolution Coaches | Kamil Trzciński, Grzegorz Bizon |
+
+DRIs:
+
+| Role | Who
+|------------------------------|------------------------|
+| Leadership | Mark Nuzzo |
+| Product | Dov Hershkovitch |
+| Engineering | Fabio Pitino |
+| UX | Kevin Comoli (interim), Sunjung Park |
+
+Domain experts:
+
+| Area | Who
+|------------------------------|------------------------|
+| Verify / Pipeline authoring | Avielle Wolfe |
+| Verify / Pipeline authoring | Laura Montemayor-Rodriguez |
+
+<!-- vale gitlab.Spelling = YES -->
diff --git a/doc/architecture/blueprints/ci_scale/index.md b/doc/architecture/blueprints/ci_scale/index.md
index c02fb35974b..574a79c86a5 100644
--- a/doc/architecture/blueprints/ci_scale/index.md
+++ b/doc/architecture/blueprints/ci_scale/index.md
@@ -1,8 +1,10 @@
---
-stage: none
-group: unassigned
-comments: false
-description: 'Improve scalability of GitLab CI/CD'
+status: in progress
+creation-date: "2021-01-21"
+authors: [ "@grzesiek" ]
+coach: "@grzesiek"
+approvers: [ "@cheryl.li", "@jreporter" ]
+owning-stage: "~devops::verify"
---
# CI/CD Scaling
diff --git a/doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md b/doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md
index 53f38fa85fd..3ef98c33035 100644
--- a/doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md
+++ b/doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md
@@ -74,7 +74,7 @@ This blueprint explicitly talks about **horizontal** split and **Application Lay
The Bounded Contexts is a topic that was discussed extensively number of times for a couple of years.
Reflected in number of issues:
-- [Create new models / classes within a module / namespace](https://gitlab.com/gitlab-org/gitlab/-/issues/212156)
+- [Create new models / classes in a module / namespace](https://gitlab.com/gitlab-org/gitlab/-/issues/212156)
- [Make teams to be maintainers of their code](https://gitlab.com/gitlab-org/gitlab/-/issues/25872)
- [Use nested structure to organize CI classes](https://gitlab.com/gitlab-org/gitlab/-/issues/209745)
- [WIP: Make it simple to build and use "Decoupled Services"](https://gitlab.com/gitlab-org/gitlab/-/issues/31121)
@@ -86,7 +86,7 @@ We are partially executing a **Bounded Contexts** idea:
- Since we use namespaces, individual contributor or reviewer can know who to reach from domain experts about help with
the given context
-The module namespaces are actively being used today to model codebase around team boundaries. Currently, the most
+The module namespaces are actively being used today to model codebase around team boundaries. The most
prominent namespaces being used today are `Ci::` and `Packages::`. They provide a good way to contain the code owned
by a group in a well-defined structure.
@@ -125,7 +125,7 @@ application layers. This list is not exhaustive, but shows a general list of the
- Web Packages API: provide a REST API compatible with the packaging tools: Debian, Maven, Container Registry Proxy, etc.
- Git nodes: all code required to authorize `git pull/push` over `SSH` or `HTTPS`
- Sidekiq: run background jobs
-- Services/Models/DB: all code required to maintain our database structure, data validation, business logic and policies models that needs to be shared with other components
+- Services/Models/DB: all code required to maintain our database structure, data validation, business logic, and policies models that needs to be shared with other components
The best way to likely describe how the actual GitLab Rails split would look like. It is a satellite model.
Where we have a single core, that is shared across all satellite components. The design of that implies
@@ -301,7 +301,7 @@ All work can be found in these merge requests:
- [Draft: PoC - Move GraphQL to the WebEngine](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50180)
- [Draft: PoC - Move Controllers and Grape API:API to the WebEngine](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53720)
- [Draft: PoC - Move only Grape API:API to the WebEngine](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53982)
-- [Measure performance impact for proposed web_engine](https://gitlab.com/gitlab-org/gitlab/-/issues/300548)
+- [Measure performance impact for proposed `web_engine`](https://gitlab.com/gitlab-org/gitlab/-/issues/300548)
What was done?
@@ -328,8 +328,8 @@ What was done?
1. Move gems to the `engines/web_engine/`
- - We moved all GraphQL gems to the actual web_engine Gemfile
- - We moved Grape API gem to the actual web_engine Gemfile
+ - We moved all GraphQL gems to the actual `web_engine` Gemfile
+ - We moved Grape API gem to the actual `web_engine` Gemfile
```ruby
Gem::Specification.new do |spec|
@@ -344,9 +344,9 @@ What was done?
1. Move routes to the `engines/web_engine/config/routes.rb` file
- - We moved GraphQL routes to the web_engine routes.
- - We moved API routes to the web_engine routes.
- - We moved most of the controller routes to the web_engine routes.
+ - We moved GraphQL routes to the `web_engine` routes.
+ - We moved API routes to the `web_engine` routes.
+ - We moved most of the controller routes to the `web_engine` routes.
```ruby
Rails.application.routes.draw do
@@ -367,7 +367,7 @@ What was done?
1. Connect GitLab application with the WebEngine
- In GitLab Gemfile.rb, add web_engine to the engines group
+ In GitLab Gemfile.rb, add `web_engine` to the engines group
```ruby
# Gemfile
@@ -376,7 +376,7 @@ What was done?
end
```
- Since the gem is inside :engines group, it will not be automatically required by default.
+ Since the gem is inside :engines group, it is not automatically required by default.
1. Configure GitLab when to load the engine.
@@ -432,7 +432,7 @@ What was done?
- We control specs from main application using environment variable `TEST_WEB_ENGINE`
- We added new CI job that will run `engines/web_engine/spec` tests separately using `TEST_WEB_ENGINE` environment variable.
- We added new CI job that will run `engines/web_engine/ee/spec` tests separately using `TEST_WEB_ENGINE` environment variable.
- - We are running all whitebox frontend tests with `TEST_WEB_ENGINE=true`
+ - We are running all white box frontend tests with `TEST_WEB_ENGINE=true`
#### Results
@@ -451,7 +451,7 @@ Savings on Sidekiq `start-up` event, for a single Sidekiq cluster without GraphQ
- Boot-up time was reduced from 45.31 to 21.80 seconds. It was 23.51 seconds faster (51.89%)
- We have 805,772 less live objects, 4,587,535 less allocated objects, 2,866 less allocated pages and 3.65 MB less allocated space for objects outside of the heap
- We loaded 2,326 less code files (15.64%)
-- We reduced the duration of a single full GC cycle from 0.80s to 0.70 (12.64%)
+- We reduced the duration of a single full GC cycle from 0.80 seconds to 0.70 seconds (12.64%)
Puma single, showed very little difference as expected.
@@ -461,20 +461,20 @@ More details can be found in the [issue](https://gitlab.com/gitlab-org/gitlab/-/
Estimating the results for the scale of running GitLab.com, today we use:
-- Currently individual GC cycle takes around [130ms for Web](https://thanos-query.ops.gitlab.net/graph?g0.range_input=1h&g0.max_source_resolution=0s&g0.expr=avg(rate(ruby_gc_duration_seconds_sum%7Bstage%3D%22main%22%2Ctype%3D%22web%22%7D%5B5m%5D)%2Frate(ruby_gc_duration_seconds_count%5B5m%5D))&g0.tab=0)
- and [200ms for Sidekiq](https://thanos-query.ops.gitlab.net/graph?g0.range_input=1h&g0.max_source_resolution=0s&g0.expr=avg(rate(ruby_gc_duration_seconds_sum%7Bstage%3D%22main%22%2Ctype%3D%22sidekiq%22%7D%5B5m%5D)%2Frate(ruby_gc_duration_seconds_count%5B5m%5D))&g0.tab=0) on GitLab.com
+- Individual GC cycle takes around [130 ms for Web](https://thanos-query.ops.gitlab.net/graph?g0.range_input=1h&g0.max_source_resolution=0s&g0.expr=avg(rate(ruby_gc_duration_seconds_sum%7Bstage%3D%22main%22%2Ctype%3D%22web%22%7D%5B5m%5D)%2Frate(ruby_gc_duration_seconds_count%5B5m%5D))&g0.tab=0)
+ and [200 ms for Sidekiq](https://thanos-query.ops.gitlab.net/graph?g0.range_input=1h&g0.max_source_resolution=0s&g0.expr=avg(rate(ruby_gc_duration_seconds_sum%7Bstage%3D%22main%22%2Ctype%3D%22sidekiq%22%7D%5B5m%5D)%2Frate(ruby_gc_duration_seconds_count%5B5m%5D))&g0.tab=0) on GitLab.com
- On average we do around [2 GC cycles per-second](https://thanos-query.ops.gitlab.net/graph?g0.range_input=1h&g0.end_input=2021-02-17%2017%3A56&g0.max_source_resolution=0s&g0.expr=avg(rate(ruby_gc_duration_seconds_count%7Bstage%3D%22main%22%2Ctype%3D%22web%22%7D%5B5m%5D))&g0.tab=0)
or [0.12 cycles per second for Sidekiq](https://thanos-query.ops.gitlab.net/graph?g0.range_input=1h&g0.end_input=2021-02-17%2017%3A56&g0.max_source_resolution=0s&g0.expr=avg(rate(ruby_gc_duration_seconds_count%7Bstage%3D%22main%22%2Ctype%3D%22sidekiq%22%7D%5B5m%5D))&g0.tab=0)
- This translates to using [around 9.5 vCPUs per-second for Web](https://thanos-query.ops.gitlab.net/graph?g0.range_input=1h&g0.max_source_resolution=0s&g0.expr=sum(rate(ruby_gc_duration_seconds_sum%7Bstage%3D%22main%22%2Ctype%3D%22web%22%7D%5B5m%5D))&g0.tab=0)
and [around 8 vCPUs per-second for Sidekiq](https://thanos-query.ops.gitlab.net/graph?g0.range_input=1h&g0.max_source_resolution=0s&g0.expr=sum(rate(ruby_gc_duration_seconds_sum%7Bstage%3D%22main%22%2Ctype%3D%22sidekiq%22%7D%5B5m%5D))&g0.tab=0) of spend on GC alone
-- Sidekiq [uses 2.1GB on average](https://thanos-query.ops.gitlab.net/graph?g0.range_input=1h&g0.max_source_resolution=0s&g0.expr=max(ruby_process_unique_memory_bytes%7Btype%3D%22sidekiq%22%7D)%2F1024%2F1024%2F1024&g0.tab=1)
- or [550GB in total](https://thanos-query.ops.gitlab.net/graph?g0.range_input=1h&g0.max_source_resolution=0s&g0.expr=sum(ruby_process_unique_memory_bytes%7Btype%3D%22sidekiq%22%7D)%2F1024%2F1024%2F1024&g0.tab=0) of memory on GitLab.com
+- Sidekiq [uses 2.1 GB on average](https://thanos-query.ops.gitlab.net/graph?g0.range_input=1h&g0.max_source_resolution=0s&g0.expr=max(ruby_process_unique_memory_bytes%7Btype%3D%22sidekiq%22%7D)%2F1024%2F1024%2F1024&g0.tab=1)
+ or [550 GB in total](https://thanos-query.ops.gitlab.net/graph?g0.range_input=1h&g0.max_source_resolution=0s&g0.expr=sum(ruby_process_unique_memory_bytes%7Btype%3D%22sidekiq%22%7D)%2F1024%2F1024%2F1024&g0.tab=0) of memory on GitLab.com
We estimate the possible maximum savings for introducing `web_engine`:
-- Reduce a GC cycle time by 20%, from to 200ms to 160ms
+- Reduce a GC cycle time by 20%, from to 200 ms to 160 ms
- The amount of GC cycles per-second would stay the same, but due to GC cycle time reduction we would use around 6 vCPUs instead of 8 vCPUs
-- In the best case we would be looking at Sidekiq alone we would be estimating to save up-to 137GB of memory on GitLab.com
+- In the best case we would be looking at Sidekiq alone we would be estimating to save up-to 137 GB of memory on GitLab.com
This model could be extended to introduce `sidekiq_engine` giving a similar benefits
(even more important due to visible impact on users) for Web nodes.
@@ -498,14 +498,14 @@ Cons:
- It is harder to implement GraphQL subscriptions as in case of Sidekiq as we need another way to pass subscriptions
- `api_v4` paths can be used in some services that are used by Sidekiq (for example `api_v4_projects_path`)
-- url_helpers paths are used in models and services, that could be used by Sidekiq (for example `Gitlab::Routing.url_helpers.project_pipelines_path` is used by [ExpirePipelineCacheService](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/ci/expire_pipeline_cache_service.rb#L20) in [ExpirePipelineCacheWorker](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/workers/expire_pipeline_cache_worker.rb#L18))
+- `url_helpers` paths are used in models and services, that could be used by Sidekiq (for example `Gitlab::Routing.url_helpers.project_pipelines_path` is used by [ExpirePipelineCacheService](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/ci/expire_pipeline_cache_service.rb#L20) in [ExpirePipelineCacheWorker](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/workers/expire_pipeline_cache_worker.rb#L18))
#### Example: GraphQL
[Draft: PoC - Move GraphQL to the WebEngine](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50180)
- The [99% of changes](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50180/diffs?commit_id=49c9881c6696eb620dccac71532a3173f5702ea8) as visible in the above MRs is moving files as-is.
-- The [actual work](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50180/diffs?commit_id=1d9a9edfa29ea6638e7d8a6712ddf09f5be77a44) on fixing cross-dependencies, specs, and configuring web_engine
+- The [actual work](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50180/diffs?commit_id=1d9a9edfa29ea6638e7d8a6712ddf09f5be77a44) on fixing cross-dependencies, specs, and configuring `web_engine`
- We [adapted](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50180/diffs?commit_id=d7f862cc209ce242000b2aec88ff7f4485acdd92) CI to test `engines/web_engine/` as a self-sufficient component of stack
Today, loading GraphQL requires a bunch of [dependencies](https://gitlab.com/gitlab-org/gitlab/-/issues/288044):
@@ -581,7 +581,7 @@ to be created to ensure that we do not have explosion of engines.
- [Draft: PoC - Move GraphQL to the WebEngine](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50180)
- [Draft: PoC - Move Controllers and Grape API:API to the WebEngine](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53720)
- [Draft: PoC - Move only Grape API:API to the WebEngine](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53982)
-- [Measure performance impact for proposed web_engine](https://gitlab.com/gitlab-org/gitlab/-/issues/300548)
+- [Measure performance impact for proposed `web_engine`](https://gitlab.com/gitlab-org/gitlab/-/issues/300548)
- [Create new models / classes within a module / namespace](https://gitlab.com/gitlab-org/gitlab/-/issues/212156)
- [Make teams to be maintainers of their code](https://gitlab.com/gitlab-org/gitlab/-/issues/25872)
- [Use nested structure to organize CI classes](https://gitlab.com/gitlab-org/gitlab/-/issues/209745)
diff --git a/doc/architecture/blueprints/container_registry_metadata_database/index.md b/doc/architecture/blueprints/container_registry_metadata_database/index.md
index 63e27286756..f3bcf1e4e59 100644
--- a/doc/architecture/blueprints/container_registry_metadata_database/index.md
+++ b/doc/architecture/blueprints/container_registry_metadata_database/index.md
@@ -258,7 +258,7 @@ The expected registry behavior will be covered with integration tests, using a p
If unable to pull a connection from the pool to serve a given request, the registry will timeout and return an HTTP `500 Internal Server Error` error to the client and report the error to Sentry. These issues should trigger a development escalation to investigate why the pool is being exhausted. There might be too much load for the preconfigured pool size, or there might be transactions holding on to connections for too long.
-Prometheus metrics should be used to create alerts to act on a possible saturation before the application starts erroring. Special attention will be paid to these scenarios during the gradual migration of the GitLab.com registry, where we will have limited, gradual, and controlled exposure on the new registry nodes. During that process, we can identify usage patterns, observe metrics, and fine tune both infrastructure and application settings accordingly as the load increases. If needed, a rate limiting algorithm may be applied to limit impact. Decisions will be based on real data to avoid overly restrictive measures and premature optimizations.
+Prometheus metrics should be used to create alerts to act on a possible saturation before the application starts returning errors. Special attention will be paid to these scenarios during the gradual migration of the GitLab.com registry, where we will have limited, gradual, and controlled exposure on the new registry nodes. During that process, we can identify usage patterns, observe metrics, and fine tune both infrastructure and application settings accordingly as the load increases. If needed, a rate limiting algorithm may be applied to limit impact. Decisions will be based on real data to avoid overly restrictive measures and premature optimizations.
The expected registry behavior will be covered with integration tests by manipulating the pool size and spawning multiple concurrent requests against the API, putting pressure on the pool and eventually exhausting its capacity.
@@ -294,7 +294,7 @@ Together, these resources should provide an adequate level of insight into the r
GitLab ships with the GitLab Container Registry by default, but it's also compatible with third-party registries, as long as they comply with the [Docker Distribution V2 Specification](https://docs.docker.com/registry/spec/api/), now superseded by the [Open Container Initiative (OCI) Image Specification](https://github.com/opencontainers/image-spec/blob/master/spec.md).
-So far, we strived to maintain full compatibility with third-party registries when adding new features. For example, in 12.8, we introduced a new [tag delete feature](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23325) to delete a single tag without deleting the underlying manifest. Because this feature is not part of the Docker or OCI specifications, we have kept the previous behavior as a fallback option to maintain compatibility with third-party registries.
+So far, we have tried to maintain full compatibility with third-party registries when adding new features. For example, in 12.8, we introduced a new [tag delete feature](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23325) to delete a single tag without deleting the underlying manifest. Because this feature is not part of the Docker or OCI specifications, we have kept the previous behavior as a fallback option to maintain compatibility with third-party registries.
However, this will likely change in the future. Apart from online garbage collection, and as described in [challenges](#challenges), the metadata database will unblock the implementation of many requested features for the GitLab Container Registry in the mid/long term. Most of these features will only be available for instances using the GitLab Container Registry. They are not part of the Docker Distribution or OCI specifications, neither we will be able to provide a compatible fallback option.
@@ -316,7 +316,7 @@ All GitLab Rails features dependent on a specific version of the registry should
This is already done to determine whether a tag should be deleted using the new tag delete feature (only available in the GitLab Container Registry v2.8.1+) or the old method. In this case, GitLab Rails sends an `OPTIONS` request to the registry tag route to determine whether the `DELETE` method is supported or not.
-Alternatively, and as the universal long-term solution, we need to determine the registry vendor, version, and supported features (the last two are only applicable if the vendor is GitLab) and persist it in the GitLab Rails database. This information can then be used in realtime to toggle features or fallback to alternative methods, if possible. The initial implementation of this approach was introduced as part of [#204839](https://gitlab.com/gitlab-org/gitlab/-/issues/204839). Currently, it's only used for metrics purposes. Further improvements are required to guarantee that the version information is kept up to date in self-managed instances, where the registry may be hot swapped.
+Alternatively, and as the universal long-term solution, we need to determine the registry vendor, version, and supported features (the last two are only applicable if the vendor is GitLab) and persist it in the GitLab Rails database. This information can then be used in real time to toggle features or fallback to alternative methods, if possible. The initial implementation of this approach was introduced as part of [#204839](https://gitlab.com/gitlab-org/gitlab/-/issues/204839). Currently, it's only used for metrics purposes. Further improvements are required to guarantee that the version information is kept up to date in self-managed instances, where the registry may be hot swapped.
##### Release and Deployment
diff --git a/doc/architecture/blueprints/database/scalability/patterns/time_decay.md b/doc/architecture/blueprints/database/scalability/patterns/time_decay.md
index 93f5dffd3f5..2b36a43a6db 100644
--- a/doc/architecture/blueprints/database/scalability/patterns/time_decay.md
+++ b/doc/architecture/blueprints/database/scalability/patterns/time_decay.md
@@ -154,7 +154,7 @@ factors:
The perfect partitioning scheme keeps **all queries over a dataset almost always over a single partition**,
with some cases going over two partitions and seldom over multiple partitions being
an acceptable balance. We should also target for **partitions that are as small as possible**, below
-5-10M records and/or 10GB each maximum.
+5-10M records and/or 10 GB each maximum.
Partitioning can be combined with other strategies to either prune (drop) old partitions, move them
to cheaper storage inside the database or move them outside of the database (archive or use of other
@@ -241,7 +241,7 @@ Related epic: [Partitioning: `web_hook_logs` table](https://gitlab.com/groups/gi
The important characteristics of `web_hook_logs` are the following:
1. Size of the dataset: it is a really large table. At the moment we decided to
- partition it (`2021-03-01`), it had roughly 527M records and a total size of roughly 1TB
+ partition it (`2021-03-01`), it had roughly 527M records and a total size of roughly 1 TB
- Table: `web_hook_logs`
- Rows: approximately 527M
@@ -261,7 +261,7 @@ As a result, on March 2021 there were still not deleted records since July 2020
increasing in size by more than 2 million records per day instead of staying at a more or less
stable size.
-Finally, the rate of inserts has grown to more than 170GB of data per month by March 2021 and keeps
+Finally, the rate of inserts has grown to more than 170 GB of data per month by March 2021 and keeps
on growing, so the only viable solution to pruning old data was through partitioning.
Our approach was to partition the table per month as it aligned with the 90 days retention policy.
diff --git a/doc/architecture/blueprints/gitlab_observability_backend/metrics/index.md b/doc/architecture/blueprints/gitlab_observability_backend/metrics/index.md
new file mode 100644
index 00000000000..a6efe68310e
--- /dev/null
+++ b/doc/architecture/blueprints/gitlab_observability_backend/metrics/index.md
@@ -0,0 +1,688 @@
+---
+status: proposed
+creation-date: "2022-11-09"
+authors: [ "@ankitbhatnagar" ]
+coach: "@mappelman"
+approvers: [ "@sebastienpahl", "@nicholasklick" ]
+owning-stage: "~monitor::observability"
+participating-stages: []
+---
+
+# GitLab Observability Backend - Metrics
+
+## Summary
+
+Developing a multi-user system to store & query observability data typically formatted in widely accepted, industry-standard formats using Clickhouse as underlying storage, with support for long-term data retention and aggregation.
+
+## Motivation
+
+From the six pillars of Observability, commonly abbreviated as `TEMPLE` - Traces, Events, Metrics, Profiles, Logs & Errors, Metrics constitute one of the most important pillars of observability data for modern day systems, helping their users gather insights about their operational posture.
+
+Metrics which are commonly structured as timeseries data have the following characteristics:
+
+- indexed by their corresponding timestamps;
+- continuously expanding in size;
+- usually aggregated, down-sampled, and queried in ranges; and
+- have very write-intensive requirements.
+
+Within GitLab Observability Backend, we aim to add the support for our customers to ingest and query observability data around their systems & applications, helping them improve the operational health of their systems.
+
+### Goals
+
+With the development of the proposed system, we have the following goals:
+
+- Scalable, low latency & cost-effective monitoring system backed by Clickhouse whose performance has been proven via repeatable benchmarks.
+
+- Support for long-term storage for Prometheus/OpenTelemetry formatted metrics, ingested via Prometheus remote_write API and queried via Prometheus remote_read API, PromQL or SQL with support for metadata and exemplars.
+
+The aformentioned goals can further be broken down into the following four sub-goals:
+
+#### Ingesting data
+
+- For the system to be capable of ingesting large volumes of writes and reads, we aim to ensure that it must be horizontally scalable & provide durability guarantees to ensure no writes are dropped once ingested.
+
+#### Persisting data
+
+- We aim to support ingesting telemetry/data sent using Prometheus `remote_write` protocol. Any persistence we design for our dataset must be multi-tenant by default, ensuring we can store observability data for multiple tenants/groups/projects within the same storage backend.
+
+- We aim to develop a test suite for data correctness, seeking inspiration from how Prometheus compliance test suite checks the correctness of a given Metrics implementation and running it as a part of our CI setup.
+
+NOTE:
+Although remote_write_sender does not test the correctness of a remote write receiver itself as is our case, it does bring some inspiration to implement/develop one within the scope of this project.
+
+- We aim to also ensure compatibility for special Prometheus data types, e.g. Prometheus histogram(s), summary(s).
+
+#### Reading data
+
+- We aim to support querying data using PromQL which means translating PromQL queries into Clickhouse SQL. To do this, [PromQL](https://github.com/prometheus/prometheus/tree/main/promql/parser) or [MetricsQL](https://github.com/VictoriaMetrics/metricsql) parsers are good alternatives.
+
+- We aim to provide additional value by exposing all ingested data via the native Clickhouse SQL interface subject to the following reliability characteristics:
+ - query validation, sanitation
+ - rate limiting
+ - resource limiting - memory, cpu, network bandwidth
+
+- We aim to pass Prometheus test suits for correctness via the [Prometheus Compliance test suite](https://github.com/prometheus/compliance/tree/main/promql) with a target goal of 100% success rate.
+
+#### Deleting data
+
+- We aim to support being able to delete any ingested data should such a need arise. This is also in addition to us naturally deleting data when a configured TTL expires and/or respective retention policies are enforced. We must, within our schemas, build a way to delete data by labels OR their content, also add to our offering the necessary tooling to do so.
+
+### Non-Goals
+
+With the goals established above, we also want to establish what specific things are non-goals with the current proposal. They are:
+
+- We do not aim to support ingestion using OpenTelemetry/OpenMetrics formats with our first iteration, though our users can still use the Opentelemetry exporters(s) internally consuming the standard Prometheus `remote_write` protocol. More information [here](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/prometheusremotewriteexporter).
+
+- We do not aim to support ingesting Prometheus exemplars in our first iteration, though we do aim to account for them in our design from the beginning.
+
+NOTE:
+Worth noting that we intend to model exemplars the same way we’re modeling metric-labels, so building on top of the same data structure should help implementt support for metadata/exemplars rather easily.
+
+## Proposal
+
+We intend to use GitLab Observability Backend as a framework for the Metrics implementation so that its lifecycle is also managed via already existing Kubernetes controllers e.g. scheduler, tenant-operator.
+
+![Architecture](supported-deployments.png)
+
+From a development perspective, what’s been marked as our “Application Server†above needs to be developed as a part of this proposal while the remaining peripheral components either already exist or can be provisioned via existing code in `scheduler`/`tenant-operator`.
+
+**On the write path**, we expect to receive incoming data via `HTTP`/`gRPC` `Ingress` similar to what we do for our existing services, e.g. errortracking, tracing.
+
+NOTE:
+Additionally, since we intend to ingest data via Prometheus `remote_write` API, the received data will be Protobuf-encoded, Snappy-compressed. All received data therefore needs to be decompressed & decoded to turn it into a set of `prompb.TimeSeries` objects, which the rest of our components interact with.
+
+We also need to make sure to avoid writing a lot of small writes into Clickhouse, therefore it’d be prudent to batch data before writing it into Clickhouse.
+
+We must also make sure ingestion remains decoupled with `Storage` so as to reduce undue dependence on a given storage implementation. While we do intend to use Clickhouse as our backing storage for any foreseeable future, this ensures we do not tie ourselves in into Clickhouse too much should future business requirements warrant the usage of a different backend/technology. A good way to implement this in Golang would be our implementations adhering to a standard interface, the following for example:
+
+```golang
+type Storage interface {
+ Read(
+ ctx context.Context,
+ request *prompb.ReadRequest
+ ) (*prompb.ReadResponse, error)
+ Write(
+ ctx context.Context,
+ request *prompb.WriteRequest
+ ) error
+}
+```
+
+NOTE:
+We understand this couples the implementation with Prometheus data format/request types, but adding methods to the interface to support more data formats should be trivial looking forward with minimal changes to code.
+
+**On the read path**, we aim to allow our users to use the Prometheus `remote_read` API and be able to query ingested data via PromQL & SQL. Support for `remote_read` API should be trivial to implement, while supporting PromQL would need translating it into SQL. We can however employ the usage of already existing [PromQL](https://github.com/prometheus/prometheus/tree/main/promql/parser) parsing libraries.
+
+We aim to focus on implementing query validation & sanitation, rate-limiting and regulating resource-consumption to ensure underlying systems, esp. storage, remain in good operational health at all times.
+
+### Supported deployments
+
+In this first iteration of the metrics backend, we intend to support a generic deployment model that makes sure we can capture as much usage as possible and begin dogfooding the product as soon as possible. This is well illustrated in the [aforementioned architecture diagram](#proposal).
+
+In its most vanilla form, metrics support in GitLab Observability Backend can be used via the Prometheus remote read & write APIs. If a user already uses Prometheus as their monitoring abstraction, it can be configured to use this backend directly.
+
+- remote_write: [configuration](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_write)
+- remote_read: [configuration](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_read)
+
+For users of the system that do not use a Prometheus instance for scraping their telemetry data, they can export their metrics via a multitude of collectors/agents such as the OpenTelemetry collector or the Prometheus Agent for example, all of which can be configured to use our remote_write endpoint. For reads however, we intend to run a Prometheus within GOB (alongside the application server) itself, then hook it up automatically with the GitLab Observability UI (GOUI) preconfigured to consume our remote_read endpoint.
+
+Notably, the ability to use a GOB-run Prometheus instance is applicable while we can only support remote_read API for running queries. Looking forward towards our next iteration, we should be able to get rid of this additional component altogether when we have full support for executing PromQL and/or SQL queries directly from GOUI.
+
+**Per-group deployments**: From a scalability perspective, we deploy an instance of Ingress, a Prometheus instance & the application server per group to make sure we can scale them subject to traffic volumes of the respective tenant. It also helps isolate resource consumption across tenants in an otherwise multi-tenant system.
+
+### Metric collection and storage
+
+It is important to separate metric collection on the client side with the storage we provision at our end.
+
+### State of the art for storage
+
+Existing long-term Prometheus compatible metrics vendors provide APIs that are compatible with Prometheus remote_write.
+
+### State of the art for Prometheus clients
+
+Metric collection clients such as Prometheus itself, Grafana Cloud Agent, Datadog Agent, etc. will scrape metrics endpoints typically from within a firewalled environment, store locally scraped metrics in a [Write Ahead Log (WAL)](https://en.wikipedia.org/wiki/Write-ahead_logging) and then batch send them to an external environment (i.e. the vendor or an internally managed system like Thanos) via the Prometheus `remote_write` protocol.
+
+- A client-side collector is an important part of the overall architecture, though it's owned by the customer/user since it needs to run in their environment. This gives the end user full control over their data because they control how it is collected and to where it is delivered.
+
+- It's **not** feasible to provide an external vendor with credentials to access and scrape endpoints within a user's firewalled environment.
+
+- It's also critically important that our `remote_write` APIs respond correctly with the appropriate rate-limiting status codes so that Prometheus Clients can respect them.
+
+[Here](https://grafana.com/blog/2021/05/26/the-future-of-prometheus-remote-write/) is a good background/history on Prometheus `remote_write` and its importance in Prometheus based observability.
+
+## Design and implementation details
+
+Following are details of how we aim to design & implement the proposed solution. To that end, a reference implementation was also developed to understand the scope of the problem and provide early data to ensure our proposal was drafted around informed decisions and/or results of our experimentation.
+
+## Reference implementation(s)
+
+- [Application server](https://gitlab.com/gitlab-org/opstrace/opstrace/-/merge_requests/1823)
+- [Metrics generator](https://gitlab.com/ankitbhatnagar/metrics-gen/-/blob/main/main.go)
+
+## Target environments
+
+Keeping inline with our current operational structure, we intend to deploy the metrics offering as a part of GitLab Observability Backend, deployed on the following two target environments:
+
+- kind cluster (for local development)
+- GKE cluster (for staging/production environments)
+
+## Schema Design
+
+### **Proposed solution**: Fully normalized tables for decreased redundancy & increased read performance
+
+### primary, denormalized data table
+
+```sql
+CREATE TABLE IF NOT EXISTS samples ON CLUSTER '{cluster}' (
+ series_id UUID,
+ timestamp DateTime64(3, ‘UTC’) CODEC(Delta(4), ZSTD),
+ value Float64 CODEC(Gorilla, ZSTD)
+) ENGINE = ReplicatedMergeTree()
+PARTITION BY toYYYYMMDD(timestamp)
+ORDER BY (series_id, timestamp)
+```
+
+### metadata table to support timeseries metadata/exemplars
+
+```sql
+CREATE TABLE IF NOT EXISTS samples_metadata ON CLUSTER '{cluster}' (
+ series_id UUID,
+ timestamp DateTime64(3, ‘UTC’) CODEC(Delta(4), ZSTD),
+ metadata Map(String, String) CODEC(ZSTD),
+) ENGINE = ReplicatedMergeTree()
+PARTITION BY toYYYYMMDD(timestamp)
+ORDER BY (series_id, timestamp)
+```
+
+### lookup table(s)
+
+```sql
+CREATE TABLE IF NOT EXISTS labels_to_series ON CLUSTER '{cluster}' (
+ labels Map(String, String) CODEC(ZSTD)
+ series_id UUID
+) engine=ReplicatedMergeTree
+PRIMARY KEY (labels, series_id)
+```
+
+```sql
+CREATE TABLE IF NOT EXISTS group_to_series ON CLUSTER ‘{cluster}’ (
+ group_id Uint64,
+ series_id UUID,
+) ORDER BY (group_id, series_id)
+```
+
+### Refinements
+
+- sharding considerations for a given tenant when ingesting/persisting data if we intend to co-locate data specific to multiple tenants within the same database tables. To simplify things, segregating tenant-specific data to their own dedicated set of tables would make a lot of sense.
+
+- structural considerations for “timestamps†when ingesting data across tenants.
+
+- creation_time vs ingestion_time
+
+- No support for transactions in the native client yet, to be able to effectively manage writes across multiple tables.
+
+NOTE:
+Slightly non-trivial but we can potentially investigate the possibility of using ClickHouse/ch-go directly, it supposedly promises a better performance profile too.
+
+### Pros - multiple tables
+
+- Normalised data structuring allows for efficient storage of data, removing any redundancy across multiple samples for a given timeseries. Evidently, for the “samples†schema, we expect to store 32 bytes of data per metric point.
+
+- Better search complexity when filtering timeseries by labels/metadata, via the use of better indexed columns.
+
+- All data is identifiable via a unique identifier, which can be used to maintain data consistency across tables.
+
+### Cons - multiple tables
+
+- Writes are trivially expensive considering writes across multiple tables.
+
+- Writes across tables also need to be implemented as a transaction to guarantee consistency when ingesting data.
+
+### Operational characteristics - multiple tables
+
+### Storage - multiple tables
+
+A major portion of our writes are made into the `samples` schema which contains a tuple containing three data points per metric point written:
+|column|data type|byte size|
+|---|---|---|
+|series_id|UUID|16 bytes|
+|timestamp|DateTime64|8 bytes|
+|value|Float64|8 bytes|
+
+Therefore, we estimate to use 32 bytes per sample ingested.
+
+### Compression - multiple tables
+
+Inspecting the amount of compression we’re able to get with the given design on our major schemas, we see it as a good starting point. Following measurements for both primary tables:
+
+**Schema**: `labels_to_series` containing close to 12k unique `series_id`, each mapping to a set of 10-12 label string pairs
+
+```sql
+SELECT
+ table,
+ column,
+ formatReadableSize(sum(data_compressed_bytes) AS x) AS compressedsize,
+ formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed
+FROM system.parts_columns
+WHERE table LIKE 'labels_to_series_1'
+GROUP BY
+ database,
+ table,
+ column
+ORDER BY x ASC
+
+Query id: 723b4145-14f7-4e74-9ada-01c17c2f1fd5
+
+┌─table──────────────┬─column────┬─compressedsize─┬─uncompressed─â”
+│ labels_to_series_1 │ labels │ 586.66 KiB │ 2.42 MiB │
+│ labels_to_series_1 │ series_id │ 586.66 KiB │ 2.42 MiB │
+└────────────────────┴───────────┴────────────────┴──────────────┘
+```
+
+**Schema**: `samples` containing about 20k metric samples each containing a tuple comprising `series_id` (16 bytes), `timestamp` (8 bytes) and `value` (8 bytes).
+
+```sql
+SELECT
+ table,
+ column,
+ formatReadableSize(sum(data_compressed_bytes) AS x) AS compressedsize,
+ formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed
+FROM system.parts_columns
+WHERE table LIKE 'samples_1'
+GROUP BY
+ database,
+ table,
+ column
+ORDER BY x ASC
+
+Query id: 04219cea-06ea-4c5f-9287-23cb23c023d2
+
+┌─table─────┬─column────┬─compressedsize─┬─uncompressed─â”
+│ samples_1 │ value │ 373.21 KiB │ 709.78 KiB │
+│ samples_1 │ timestamp │ 373.21 KiB │ 709.78 KiB │
+│ samples_1 │ series_id │ 373.21 KiB │ 709.78 KiB │
+└───────────┴───────────┴────────────────┴──────────────┘
+```
+
+### Performance - multiple tables
+
+From profiling our reference implementation, it can also be noted that most of our time right now is spent in the application writing data to Clickhouse and/or its related operations. A “top†pprof profile sampled from the implementation looked like:
+
+```shell
+(pprof) top
+Showing nodes accounting for 42253.20kB, 100% of 42253.20kB total
+Showing top 10 nodes out of 58
+ flat flat% sum% cum cum%
+13630.30kB 32.26% 32.26% 13630.30kB 32.26% github.com/ClickHouse/clickhouse-go/v2/lib/compress.NewWriter (inline)
+11880.92kB 28.12% 60.38% 11880.92kB 28.12% github.com/ClickHouse/clickhouse-go/v2/lib/compress.NewReader (inline)
+ 5921.37kB 14.01% 74.39% 5921.37kB 14.01% bufio.NewReaderSize (inline)
+ 5921.37kB 14.01% 88.41% 5921.37kB 14.01% bufio.NewWriterSize (inline)
+ 1537.69kB 3.64% 92.04% 1537.69kB 3.64% runtime.allocm
+ 1040.73kB 2.46% 94.51% 1040.73kB 2.46% github.com/aws/aws-sdk-go/aws/endpoints.init
+ 1024.41kB 2.42% 96.93% 1024.41kB 2.42% runtime.malg
+ 768.26kB 1.82% 98.75% 768.26kB 1.82% go.uber.org/zap/zapcore.newCounters
+ 528.17kB 1.25% 100% 528.17kB 1.25% regexp.(*bitState).reset
+ 0 0% 100% 5927.73kB 14.03% github.com/ClickHouse/clickhouse-go/v2.(*clickhouse).Ping
+```
+
+As is evident above from our preliminary analysis, writing data into Clickhouse can be a potential bottleneck. Therefore, on the write path, it'd be prudent to batch our writes into Clickhouse so as to reduce the amount of work the application server ends up doing making the ingestion path more efficient.
+
+On the read path, it’s also possible to parallelize reads for the samples table either by series_id(s) OR by blocks of time between the queried start and end timestamps.
+
+### Caveats
+
+- When dropping labels from already existing metrics, we treat their new counterparts as completely new series and hence attribute them to a new series_id. This avoids having to merge series data and/or values. The old series, if not actively written into, should eventually fall off their retention and get deleted.
+
+- We have not yet accounted for any data aggregation. Our assumption is that the backing store (in Clickhouse) should allow us to keep a “sufficient†amount of data in its raw form and that we should be able to query against it within our query latency SLOs.
+
+### **Rejected alternative**: Single, centralized table
+
+### single, centralized data table
+
+```sql
+CREATE TABLE IF NOT EXISTS metrics ON CLUSTER ‘{cluster}’ (
+ group_id UInt64,
+ name LowCardinality(String) CODEC(ZSTD),
+ labels Map(String, String) CODEC(ZSTD),
+ metadata Map(String, String) CODEC(ZSTD),
+ value Float64 CODEC (Gorilla, ZSTD),
+ timestamp DateTime64(3, ‘UTC’) CODEC(Delta(4),ZSTD)
+) ENGINE = ReplicatedMergeTree()
+PARTITION BY toYYYYMMDD(timestamp)
+ORDER BY (group_id, name, timestamp);
+```
+
+### Pros - single table
+
+- Single source of truth, so all metrics data lives in one big table.
+
+- Querying data is easier to express in terms of writing SQL queries without having to query data across multiple tables.
+
+### Cons - single table
+
+- Huge redundancy built into the data structure since attributes such as name, labels, metadata are stored repeatedly for each sample collected.
+
+- Non-trivial complexity to search timeseries with values for labels/metadata given how they’re stored when backed by Maps/Arrays.
+
+- High query latencies by virtue of having to scan large amounts of data per query made.
+
+### Operational Characteristics - single table
+
+### Storage - single table
+
+|column|data type|byte size|
+|---|---|---|
+|group_id|UUID|16 bytes|
+|name|String|-|
+|labels|Map(String, String)|-|
+|metadata|Map(String, String)|-|
+|value|Float64|8 bytes|
+|timestamp|DateTime64|8 bytes|
+
+NOTE:
+Strings are of an arbitrary length, the length is not limited. Their value can contain an arbitrary set of bytes, including null bytes. We will need to regulate what we write into these columns application side.
+
+### Compression - single table
+
+**Schema**: `metrics` containing about 20k metric samples each consisting of a `group_id`, `metric name`, `labels`, `metadata`, `timestamp` & corresponding `value`.
+
+```sql
+SELECT count(*)
+FROM metrics_1
+
+Query id: e580f20b-b422-4d93-bb1f-eb1435761604
+
+┌─count()─â”
+│ 12144 │
+
+
+SELECT
+ table,
+ column,
+ formatReadableSize(sum(data_compressed_bytes) AS x) AS compressedsize,
+ formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed
+FROM system.parts_columns
+WHERE table LIKE 'metrics_1'
+GROUP BY
+ database,
+ table,
+ column
+ORDER BY x ASC
+
+Query id: b2677493-3fbc-46c1-a9a7-4524a7a86cb4
+
+┌─table─────┬─column────┬─compressedsize─┬─uncompressed─â”
+│ metrics_1 │ labels │ 283.02 MiB │ 1.66 GiB │
+│ metrics_1 │ metadata │ 283.02 MiB │ 1.66 GiB │
+│ metrics_1 │ group_id │ 283.02 MiB │ 1.66 GiB │
+│ metrics_1 │ value │ 283.02 MiB │ 1.66 GiB │
+│ metrics_1 │ name │ 283.02 MiB │ 1.66 GiB │
+│ metrics_1 │ timestamp │ 283.02 MiB │ 1.66 GiB │
+└───────────┴───────────┴────────────────┴──────────────┘
+```
+
+Though we see a good compression factor for the aforementioned schema, the amount of storage needed to store the corresponding dataset is approximately 300MiB. We also expect to see this footprint increase linearly given the redundancy baked into the schema design itself, also one of the reasons we intend **not** to proceed with this design further.
+
+### Performance - single table
+
+```shell
+(pprof) top
+Showing nodes accounting for 12844.95kB, 100% of 12844.95kB total
+Showing top 10 nodes out of 40
+ flat flat% sum% cum cum%
+ 2562.81kB 19.95% 19.95% 2562.81kB 19.95% runtime.allocm
+ 2561.90kB 19.94% 39.90% 2561.90kB 19.94% github.com/aws/aws-sdk-go/aws/endpoints.init
+ 2374.91kB 18.49% 58.39% 2374.91kB 18.49% github.com/ClickHouse/clickhouse-go/v2/lib/compress.NewReader (inline)
+ 1696.32kB 13.21% 71.59% 1696.32kB 13.21% bufio.NewWriterSize (inline)
+ 1184.27kB 9.22% 80.81% 1184.27kB 9.22% bufio.NewReaderSize (inline)
+ 1184.27kB 9.22% 90.03% 1184.27kB 9.22% github.com/ClickHouse/clickhouse-go/v2/lib/compress.NewWriter (inline)
+ 768.26kB 5.98% 96.01% 768.26kB 5.98% go.uber.org/zap/zapcore.newCounters
+ 512.20kB 3.99% 100% 512.20kB 3.99% runtime.malg
+ 0 0% 100% 6439.78kB 50.13% github.com/ClickHouse/clickhouse-go/v2.(*clickhouse).Ping
+ 0 0% 100% 6439.78kB 50.13% github.com/ClickHouse/clickhouse-go/v2.(*clickhouse).acquire
+```
+
+Writes against this schema perform much better in terms of compute, given it's concentrated on one table and does not need looking up `series_id` from a side table.
+
+### General storage considerations - Clickhouse
+
+The following sections intend to deep-dive into specific characteristics of our schema design and/or their interaction with Clickhouse - the database system.
+
+- table engines
+
+ - [MergeTree](https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/mergetree/)
+ - [S3 Table Engine](https://clickhouse.com/docs/en/engines/table-engines/integrations/s3/)
+
+- efficient partitioning and/or sharding
+
+ - Configuring our schemas with the right partitioning keys so as to have the least amount of blocks scanned when reading back the data.
+ - Sharding here would refer to how we design our data placement strategy to make sure the cluster remains optimally balanced at all times.
+
+- data compression
+
+As is visible from the aforementioned preliminary results, we see good compression results with dictionary and delta encoding for strings and floats respectively. When storing labels with a `Map` of `LowCardinality(String)`s, we were able to pack data efficiently.
+
+- materialized views
+
+Can be updated dynamically as the need be, help make read paths performant
+
+- async inserts
+
+- batch inserts
+
+- retention/TTLs
+
+We should only store data for a predetermined period of time, post which we either delete data, aggregate it or ship it to an archival store to reduce operational costs of having to store data for longer periods of time.
+
+- data aggregation/rollups
+
+- index granularity
+
+- skip indexes
+
+- `max_server_memory_usage_to_ram_ratio`
+
+### Data access via SQL
+
+While our corpus of data is PromQL-queryable, it would be prudent to make sure we make the SQL interface “generally available†as well. This capability opens up multiple possibilities to query resident data and allows our users to slice and dice their datasets whichever way they prefer to and/or need to.
+
+#### Challenges
+
+- Resource/cost profiling.
+- Query validation and sanitation.
+
+### Illustrative example(s) of data access
+
+### Writes
+
+On the write path, we first ensure registering a given set labels to a unique `series_id` and/or re-using one should we have seen the timeseries already in the past. For example:
+
+```plaintext
+redis{region="us-east-1",'os':'Ubuntu15.10',...} <TIMESTAMP> <VALUE>
+```
+
+**Schema**: labels_to_series
+
+```sql
+SELECT *
+FROM labels_to_series_1
+WHERE series_id = '6d926ae8-c3c3-420e-a9e2-d91aff3ac125'
+FORMAT Vertical
+
+Query id: dcbc4bd8-0bdb-4c35-823a-3874096aab6e
+
+Row 1:
+──────
+labels: {'arch':'x64','service':'1','__name__':'redis','region':'us-east-1','os':'Ubuntu15.10','team':'LON','service_environment':'production','rack':'36','service_version':'0','measurement':'pubsub_patterns','hostname':'host_32','datacenter':'us-east-1a'}
+series_id: 6d926ae8-c3c3-420e-a9e2-d91aff3ac125
+
+1 row in set. Elapsed: 0.612 sec.
+```
+
+Post which, we register each metric point in the `samples` table attributing it to the corresponding `series_id`.
+
+**Schema**: samples
+
+```sql
+SELECT *
+FROM samples_1
+WHERE series_id = '6d926ae8-c3c3-420e-a9e2-d91aff3ac125'
+LIMIT 1
+FORMAT Vertical
+
+Query id: f3b410af-d831-4859-8828-31c89c0385b5
+
+Row 1:
+──────
+series_id: 6d926ae8-c3c3-420e-a9e2-d91aff3ac125
+timestamp: 2022-11-10 12:59:14.939
+value: 0
+```
+
+### Reads
+
+On the read path, we first query all timeseries identifiers by searching for the labels under consideration. Once we have all the `series_id`(s), we then look up all corresponding samples between the query start timestamp and end timestamp.
+
+For e.g.
+
+```plaintext
+kernel{service_environment=~"prod.*", measurement="boot_time"}
+```
+
+which gets translated into first looking for all related timeseries:
+
+```sql
+SELECT *
+FROM labels_to_series
+WHERE
+((labels['__name__']) = 'kernel') AND
+match(labels['service_environment'], 'prod.*') AND
+((labels['measurement']) = 'boot_time');
+```
+
+yielding a bunch of `series_id`(s) corresponding to the labels just looked up.
+
+**Sidenote**, this mostly-static dataset can also be cached and built up in-memory gradually to reduce paying the latency cost the second time, which should reduce the number of lookups considerably.
+
+To account for newer writes when maintaining this cache:
+
+- Have an out-of-band process/goroutine maintain this cache, so even if a few queries miss the most recent data, subsequent ones eventually catch up.
+
+- Have TTLs on the keys, jittered per key so as to rebuild them frequently enough to account for new writes.
+
+Once we know which timeseries we’re querying for, from there, we can easily look up all samples via the following query:
+
+```sql
+SELECT *
+FROM samples
+WHERE series_id IN (
+ 'a12544be-0a3a-4693-86b0-c61a4553aea3',
+ 'abd42fc4-74c7-4d80-9b6c-12f673db375d',
+ …
+)
+AND timestamp >= '1667546789'
+AND timestamp <= '1667633189'
+ORDER BY timestamp;
+```
+
+yielding all timeseries samples we were interested in.
+
+We then render these into an array of `prometheus.QueryResult` object(s) and return back to the caller as a `prometheus.ReadResponse` object.
+
+NOTE:
+The queries have been broken down into multiple queries only during our early experimentation/iteration, it’d be prudent to use subqueries within the same roundtrip to the database going forward into production/benchmarking.
+
+## Production Readiness
+
+### Batching
+
+Considering we’ll need to batch data before ingesting large volumes of small writes into Clickhouse, the design must account for app-local persistence to allow it to locally batch incoming data before landing it into Clickhouse in batches of a predetermined size in order to increase performance and allow the table engine to continue to persist data successfully.
+
+We have considered the following alternatives to implement app-local batching:
+
+- In-memory - non durable
+- BadgerDB - durable, embedded, performant
+- Redis - trivial, external dependency
+- Kafka - non-trivial, external dependency but it can augment multiple other use-cases and help other problem domains at GitLab.
+
+**Note**: Similar challenges have also surfaced with the CH interactions `errortracking` - the subsystem has in its current implementation. There have been multiple attempts to solve this problem domain in the past - [this MR](https://gitlab.com/gitlab-org/opstrace/opstrace/-/merge_requests/1660) implemented an in-memory alternative while [this one](https://gitlab.com/gitlab-org/opstrace/opstrace/-/merge_requests/1767) attempted an on-disk alternative.
+
+Any work done in this area of concern would also benefit other subsystems such as errortracking, logging, etc.
+
+### Scalability
+
+We intend to start testing the proposed implementation with 10K metric-points per second to test/establish our initial hypothesis, though ideally, we must design the underlying backend for 1M points ingested per second.
+
+### Benchmarking
+
+We propose the following three dimensions be tested while benchmarking the proposed implementation:
+
+- Data ingest performance
+- On-disk storage requirements (accounting for replication if applicable)
+- Mean query response times
+
+For understanding performance, we’ll need to first compile a list of such queries given the data we ingest for our tests. Clickhouse query logging is super helpful while doing this.
+
+NOTE:
+Ideally, we aim to benchmark the system to be able to ingest >1M metric points/sec while consistently serving most queries under <1 sec.
+
+### Past work & references
+
+- [Benchmark ClickHouse for metrics](https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/1666)
+- [Incubation:APM ClickHouse evaluation](https://gitlab.com/gitlab-org/incubation-engineering/apm/apm/-/issues/4)
+- [Incubation:APM ClickHouse metrics schema](https://gitlab.com/gitlab-org/incubation-engineering/apm/apm/-/issues/10)
+- [Our research around TimescaleDB](https://gitlab.com/gitlab-com/gl-infra/reliability/-/issues/14137)
+- [Current Workload on our Thanos-based setup](https://gitlab.com/gitlab-com/gl-infra/reliability/-/issues/15420#current-workload)
+- [Scaling-200m-series](https://opstrace.com/blog/scaling-200m-series)
+
+### Cost-estimation
+
+- We aim to make sure the system's not too expensive, especially given our biggest footprint is on Clickhouse and the underlying storage.
+
+- We must consider the usage of multiple storage medium(s), especially:
+ - Tiered storage
+ - Object storage
+
+### Tooling
+
+- We aim to building visibility into high cardinality metrics to be able to assist with keeping our databases healthy by pruning/dropping unused metrics.
+
+- Similarly, we aim to develop the ability to see unused metrics for the end-user, which can be easily & dynamically built into the system by parsing all read requests and building usage statistics.
+
+- We aim to add monitoring for per-metric scrape frequencies to make sure the end-user is not ingesting data at a volume they do not need and/or find useful.
+
+## Looking forward
+
+### Linkage across telemetry pillars, exemplars
+
+We must build the metrics system in a way to be able cross-reference ingested data with other telemetry pillars, such as traces, logs and errors, so as to provide a more holistic view of all instrumentation a system sends our way.
+
+### User-defined SQL queries to aggregate data and/or generate materialized views
+
+We should allow users of the system to be able to run user-defined, ad-hoc queries similar to how Prometheus recording rules help generate custom metrics from existing ones.
+
+### Write Ahead Logs (WALs)
+
+We believe that should we feel the need to start buffering data local to the ingestion application and/or move away from Clickhouse for persisting data, on-disk WALs would be a good direction to proceed into given their prevelant usage among other monitoring system.
+
+### Custom DSLs or query builders
+
+Using PromQL directly could be a steep learning curve for users. It would be really nice to have a query builder (as is common in Grafana) to allow building of the typical queries you'd expect to run and to allow exploration of the available metrics. It also serves as a way to learn the DSL, so more complex queries can be created later.
+
+## Roadmap & Next Steps
+
+The following section enlists how we intend to implement the aforementioned proposal around building Metrics support into GitLab Observability Service. Each corresponding document and/or issue contains further details of how each next step is planned to be executed.
+
+- **[DONE]** [Research & draft design proposal and/or requirements](https://docs.google.com/document/d/1kHyIoWEcs14sh3CGfKGiI8QbCsdfIHeYkzVstenpsdE/edit?usp=sharing)
+- **[IN-PROGRESS]** [Submit system/schema designs (proposal) & gather feedback](https://docs.google.com/document/d/1kHyIoWEcs14sh3CGfKGiI8QbCsdfIHeYkzVstenpsdE/edit?usp=sharing)
+- **[IN-PROGRESS]** [Develop table definitions and/or storage interfaces](https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/1666)
+- **[IN-PROGRESS]** [Prototype reference implementation, instrument key metrics](https://gitlab.com/gitlab-org/opstrace/opstrace/-/merge_requests/1823)
+- [Benchmark Clickhouse and/or proposed schemas, gather expert advice from Clickhouse Inc.](https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/1666)
+- Develop write path(s) - `remote_write` API
+- Develop read path(s) - `remote_read` API, `PromQL`-based querier.
+- Setup testbed(s) for repeatable benchmarking/testing
+- Schema design and/or application server improvements if needed
+- Production Readiness v1.0-alpha/beta
+- Implement vanguarded/staged rollouts
+- Run extended alpha/beta testing
+- Release v1.0
diff --git a/doc/architecture/blueprints/gitlab_observability_backend/metrics/supported-deployments.png b/doc/architecture/blueprints/gitlab_observability_backend/metrics/supported-deployments.png
new file mode 100644
index 00000000000..54c4ed3b48f
--- /dev/null
+++ b/doc/architecture/blueprints/gitlab_observability_backend/metrics/supported-deployments.png
Binary files differ
diff --git a/doc/architecture/blueprints/image_resizing/index.md b/doc/architecture/blueprints/image_resizing/index.md
index 948378d8834..99a7c4ae144 100644
--- a/doc/architecture/blueprints/image_resizing/index.md
+++ b/doc/architecture/blueprints/image_resizing/index.md
@@ -14,7 +14,7 @@ Currently, we are showing all uploaded images 1:1, which is of course not ideal.
## MVC Avatar Resizing
-When implementing a dynamic image resizing solution, images should be resized and optimized on the fly so that if we define new targeted sizes later we can add them dynamically. This would mean a huge improvement in performance as some of the measurements suggest that we can save up to 95% of our current load size. Our initial investigations indicate that we have uploaded approximately 1.65 million avatars totaling approximately 80GB in size and averaging approximately 48kb each. Early measurements indicate we can reduce the most common avatar dimensions to between 1-3kb in size, netting us a greater than 90% size reduction. For the MVC we don't consider application level caching and rely purely on HTTP based caches as implemented in CDNs and browsers, but might revisit this decision later on. To easily mitigate performance issues with avatar resizing, especially in the case of self managed, an operations feature flag is implemented to disable dynamic image resizing.
+When implementing a dynamic image resizing solution, images should be resized and optimized on the fly so that if we define new targeted sizes later we can add them dynamically. This would mean a huge improvement in performance as some of the measurements suggest that we can save up to 95% of our current load size. Our initial investigations indicate that we have uploaded approximately 1.65 million avatars totaling approximately 80 GB in size and averaging approximately 48 KB each. Early measurements indicate we can reduce the most common avatar dimensions to between 1-3 KB in size, netting us a greater than 90% size reduction. For the MVC we don't consider application level caching and rely purely on HTTP based caches as implemented in CDNs and browsers, but might revisit this decision later on. To mitigate performance issues with avatar resizing, especially in the case of self managed, an operations feature flag is implemented to disable dynamic image resizing.
```mermaid
sequenceDiagram
@@ -38,10 +38,10 @@ sequenceDiagram
Content image resizing is a more complex problem to tackle. There are no set size restrictions and there are additional features or requirements to consider.
- Dynamic WebP support - the WebP format typically achieves an average of 30% more compression than JPEG without the loss of image quality. More details are in [this Google Comparative Study](https://developers.google.com/speed/webp/docs/c_study)
-- Extract first image of GIF's so we can prevent from loading 10MB pixels
+- Extract first GIF image so we can prevent from loading 10 MB pixels
- Check Device Pixel Ratio to deliver nice images on High DPI screens
- Progressive image loading, similar to what is described in [this article about how to build a progressive image loader](https://www.sitepoint.com/how-to-build-your-own-progressive-image-loader/)
-- Resizing recommendations (size, clarity, and so on)
+- Resizing recommendations (for example, size and clarity)
- Storage
The MVC Avatar resizing implementation is integrated into Workhorse. With the extra requirements for content image resizing, this may require further use of GraphicsMagik (GM) or a similar library and breaking it out of Workhorse.
diff --git a/doc/architecture/blueprints/object_storage/index.md b/doc/architecture/blueprints/object_storage/index.md
index 61dc37d7706..950a5f13c38 100644
--- a/doc/architecture/blueprints/object_storage/index.md
+++ b/doc/architecture/blueprints/object_storage/index.md
@@ -62,7 +62,7 @@ This has led to increased complexity across the board, from development
that would normally be "free".
- In many cases, we copy around object storage files needlessly
(for example, [issue #285597](https://gitlab.com/gitlab-org/gitlab/-/issues/285597)).
- Large files (LFS, packages, and so on) are slow to finalize or don't work
+ Large files (for example, LFS and packages) are slow to finalize or don't work
at all as a result.
## Improvements over the current situation
@@ -113,7 +113,7 @@ Because every group of features requires its own bucket, we don't have
direct upload enabled everywhere. Contributing a new upload requires
coding it in both Ruby on Rails and Go.
-Implementing a new feature that does not yet have a dedicated bucket
+Implementing a new feature that does not have a dedicated bucket
requires the developer to also create a merge request in Omnibus
and CNG, as well as coordinate with SREs to configure the new bucket
for our own environments.
@@ -138,7 +138,7 @@ access to new features without infrastructure chores.
Our implementation is built on top of a 3rd-party framework where
every object storage client is a 3rd-party library. Unfortunately some
of them are unmaintained.
-[We have customers who cannot push 5GB Git LFS objects](https://gitlab.com/gitlab-org/gitlab/-/issues/216442),
+[We have customers who cannot push 5 GB Git LFS objects](https://gitlab.com/gitlab-org/gitlab/-/issues/216442),
but with such a vital feature implemented in 3rd-party libraries we
are slowed down in fixing it, and we also rely on external maintainers
to merge and release fixes.
diff --git a/doc/architecture/blueprints/pods/images/pods-and-fulfillment.png b/doc/architecture/blueprints/pods/images/pods-and-fulfillment.png
index aab8556a5d3..fea32d1800e 100644
--- a/doc/architecture/blueprints/pods/images/pods-and-fulfillment.png
+++ b/doc/architecture/blueprints/pods/images/pods-and-fulfillment.png
Binary files differ
diff --git a/doc/architecture/blueprints/pods/index.md b/doc/architecture/blueprints/pods/index.md
index 3ba319d169b..0a36de5790f 100644
--- a/doc/architecture/blueprints/pods/index.md
+++ b/doc/architecture/blueprints/pods/index.md
@@ -324,6 +324,18 @@ This is the list of known affected features with the proposed solutions.
- [Pods: GraphQL](pods-feature-graphql.md)
- [Pods: Organizations](pods-feature-organizations.md)
- [Pods: Router Endpoints Classification](pods-feature-router-endpoints-classification.md)
+- [Pods: Schema changes (Postgres and Elasticsearch migrations)](pods-feature-schema-changes.md)
+- [Pods: Global Search](pods-feature-global-search.md)
+- [Pods: CI Runners](pods-feature-ci-runners.md)
+- [Pods: Admin Area](pods-feature-admin-area.md)
+- [Pods: Container Registry](pods-feature-container-registry.md)
+- [Pods: Contributions: Forks](pods-feature-contributions-forks.md)
+- [Pods: Personal Namespaces](pods-feature-personal-namespaces.md)
+- [Pods: Dashboard: Projects, Todos, Issues, Merge Requests, Activity, ...](pods-feature-dashboard.md)
+- [Pods: Snippets](pods-feature-snippets.md)
+- [Pods: Uploads](pods-feature-uploads.md)
+- [Pods: GitLab Pages](pods-feature-gitlab-pages.md)
+- [Pods: Agent for Kubernetes](pods-feature-agent-for-kubernetes.md)
## Links
diff --git a/doc/architecture/blueprints/pods/pods-feature-admin-area.md b/doc/architecture/blueprints/pods/pods-feature-admin-area.md
new file mode 100644
index 00000000000..7efaa383510
--- /dev/null
+++ b/doc/architecture/blueprints/pods/pods-feature-admin-area.md
@@ -0,0 +1,58 @@
+---
+stage: enablement
+group: pods
+comments: false
+description: 'Pods: Admin Area'
+---
+
+This document is a work-in-progress and represents a very early state of the
+Pods design. Significant aspects are not documented, though we expect to add
+them in the future. This is one possible architecture for Pods, and we intend to
+contrast this with alternatives before deciding which approach to implement.
+This documentation will be kept even if we decide not to implement this so that
+we can document the reasons for not choosing this approach.
+
+# Pods: Admin Area
+
+In our Pods architecture proposal we plan to share all admin related tables in
+GitLab. This allows simpler management of all Pods in one interface and reduces
+the risk of settings diverging in different Pods. This introduces challenges
+with admin pages that allow you to manage data that will be spread across all
+Pods.
+
+## 1. Definition
+
+There are consequences for admin pages that contain data that spans "the whole
+instance" as the Admin pages may be served by any Pod or possibly just 1 pod.
+There are already many parts of the Admin interface that will have data that
+spans many pods. For example lists of all Groups, Projects, Topics, Jobs,
+Analytics, Applications and more. There are also administrative monitoring
+capabilities in the Admin page that will span many pods such as the "Background
+Jobs" and "Background Migrations" pages.
+
+## 2. Data flow
+
+## 3. Proposal
+
+We will need to decide how to handle these exceptions with a few possible
+options:
+
+1. Move all these pages out into a dedicated per-pod Admin section. Probably
+ the URL will need to be routable to a single Pod like `/pods/<pod_id>/admin`,
+ then we can display this data per Pod. These pages will be distinct from
+ other Admin pages which control settings that are shared across all Pods. We
+ will also need to consider how this impacts self-managed customers and
+ whether, or not, this should be visible for single-pod instances of GitLab.
+1. Build some aggregation interfaces for this data so that it can be fetched
+ from all Pods and presented in a single UI. This may be beneficial to an
+ administrator that needs to see and filter all data at a glance, especially
+ when they don't know which Pod the data is on. The downside, however, is
+ that building this kind of aggregation is very tricky when all the Pods are
+ designed to be totally independent, and it does also enforce more strict
+ requirements on compatibility between Pods.
+
+## 4. Evaluation
+
+## 4.1. Pros
+
+## 4.2. Cons
diff --git a/doc/architecture/blueprints/pods/pods-feature-agent-for-kubernetes.md b/doc/architecture/blueprints/pods/pods-feature-agent-for-kubernetes.md
new file mode 100644
index 00000000000..f390c751b8b
--- /dev/null
+++ b/doc/architecture/blueprints/pods/pods-feature-agent-for-kubernetes.md
@@ -0,0 +1,29 @@
+---
+stage: enablement
+group: pods
+comments: false
+description: 'Pods: Agent for Kubernetes'
+---
+
+This document is a work-in-progress and represents a very early state of the
+Pods design. Significant aspects are not documented, though we expect to add
+them in the future. This is one possible architecture for Pods, and we intend to
+contrast this with alternatives before deciding which approach to implement.
+This documentation will be kept even if we decide not to implement this so that
+we can document the reasons for not choosing this approach.
+
+# Pods: Agent for Kubernetes
+
+> TL;DR
+
+## 1. Definition
+
+## 2. Data flow
+
+## 3. Proposal
+
+## 4. Evaluation
+
+## 4.1. Pros
+
+## 4.2. Cons
diff --git a/doc/architecture/blueprints/pods/pods-feature-ci-runners.md b/doc/architecture/blueprints/pods/pods-feature-ci-runners.md
new file mode 100644
index 00000000000..b75515a916f
--- /dev/null
+++ b/doc/architecture/blueprints/pods/pods-feature-ci-runners.md
@@ -0,0 +1,169 @@
+---
+stage: enablement
+group: pods
+comments: false
+description: 'Pods: CI Runners'
+---
+
+This document is a work-in-progress and represents a very early state of the
+Pods design. Significant aspects are not documented, though we expect to add
+them in the future. This is one possible architecture for Pods, and we intend to
+contrast this with alternatives before deciding which approach to implement.
+This documentation will be kept even if we decide not to implement this so that
+we can document the reasons for not choosing this approach.
+
+# Pods: CI Runners
+
+GitLab in order to execute CI jobs [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-runner/),
+very often managed by customer in their infrastructure.
+
+All CI jobs created as part of CI pipeline are run in a context of project
+it poses a challenge how to manage GitLab Runners.
+
+## 1. Definition
+
+There are 3 different types of runners:
+
+- instance-wide: runners that are registered globally with specific tags (selection criteria)
+- group runners: runners that execute jobs from a given top-level group or subprojects of that group
+- project runners: runners that execute jobs from projects or many projects: some runners might
+ have projects assigned from projects in different top-level groups.
+
+This alongside with existing data structure where `ci_runners` is a table describing
+all types of runners poses a challenge how the `ci_runners` should be managed in a Pods environment.
+
+## 2. Data flow
+
+GitLab Runners use a set of globally scoped endpoints to:
+
+- registration of a new runner via registration token `https://gitlab.com/api/v4/runners`
+ ([subject for removal](../runner_tokens/index.md)) (`registration token`)
+- requests jobs via an authenticated `https://gitlab.com/api/v4/jobs/request` endpoint (`runner token`)
+- upload job status via `https://gitlab.com/api/v4/jobs/:job_id` (`build token`)
+- upload trace via `https://gitlab.com/api/v4/jobs/:job_id/trace` (`build token`)
+- download and upload artifacts via `https://gitlab.com/api/v4/jobs/:job_id/artifacts` (`build token`)
+
+Currently three types of authentication tokens are used:
+
+- runner registration token ([subject for removal](../runner_tokens/index.md))
+- runner token representing an registered runner in a system with specific configuration (`tags`, `locked`, etc.)
+- build token representing an ephemeral token giving a limited access to updating a specific
+ job, uploading artifacts, downloading dependent artifacts, downloading and uploading
+ container registry images
+
+Each of those endpoints do receive an authentication token via header (`JOB-TOKEN` for `/trace`)
+or body parameter (`token` all other endpoints).
+
+Since the CI pipeline would be created in a context of a specific Pod it would be required
+that pick of a build would have to be processed by that particular Pod. This requires
+that build picking depending on a solution would have to be either:
+
+- routed to correct Pod for a first time
+- be made to be two phase: request build from global pool, claim build on a specific Pod using a Pod specific URL
+
+## 3. Proposal
+
+This section describes various proposals. Reader should consider that those
+proposals do describe solutions for different problems. Many or some aspects
+of those proposals might be the solution to the stated problem.
+
+### 3.1. Authentication tokens
+
+Even though the paths for CI Runners are not routable they can be made routable with
+those two possible solutions:
+
+- The `https://gitlab.com/api/v4/jobs/request` uses a long polling mechanism with
+ a ticketing mechanism (based on `X-GitLab-Last-Update` header). Runner when first
+ starts sends a request to GitLab to which GitLab responds with either a build to pick
+ by runner. This value is completely controlled by GitLab. This allows GitLab
+ to use JWT or any other means to encode `pod` identifier that could be easily
+ decodable by Router.
+- The majority of communication (in terms of volume) is using `build token` making it
+ the easiest target to change since GitLab is sole owner of the token that Runner later
+ uses for specific job. There were prior discussions about not storing `build token`
+ but rather using `JWT` token with defined scopes. Such token could encode the `pod`
+ to which router could easily route all requests.
+
+### 3.2. Request body
+
+- The most of used endpoints pass authentication token in request body. It might be desired
+ to use HTTP Headers as an easier way to access this information by Router without
+ a need to proxy requests.
+
+### 3.3. Instance-wide are Pod local
+
+We can pick a design where all runners are always registered and local to a given Pod:
+
+- Each Pod has it's own set of instance-wide runners that are updated at it's own pace
+- The project runners can only be linked to projects from the same organization
+ creating strong isolation.
+- In this model the `ci_runners` table is local to the Pod.
+- In this model we would require the above endpoints to be scoped to a Pod in some way
+ or made routable. It might be via prefixing them, adding additional Pod parameter,
+ or providing much more robust way to decode runner token and match it to Pod.
+- If routable token is used, we could move away from cryptographic random stored in
+ database to rather prefer to use JWT tokens that would encode
+- The Admin Area showing registered Runners would have to be scoped to a Pod
+
+This model might be desired since it provides strong isolation guarantees.
+This model does significantly increase maintenance overhead since each Pod is managed
+separately.
+
+This model may require adjustments to runner tags feature so that projects have consistent runner experience across pods.
+
+### 3.4. Instance-wide are cluster-wide
+
+Contrary to proposal where all runners are Pod local, we can consider that runners
+are global, or just instance-wide runners are global.
+
+However, this requires significant overhaul of system and to change the following aspects:
+
+- `ci_runners` table would likely have to be split decomposed into `ci_instance_runners`, ...
+- all interfaces would have to be adopted to use correct table
+- build queuing would have to be reworked to be two phase where each Pod would know of all pending
+ and running builds, but the actual claim of a build would happen against a Pod containing data
+- likely `ci_pending_builds` and `ci_running_builds` would have to be made `cluster-wide` tables
+ increasing likelihood of creating hotspots in a system related to CI queueing
+
+This model makes it complex to implement from engineering side. Does make some data being shared
+between Pods. Creates hotspots / scalability issues in a system (ex. during abuse) that
+might impact experience of organizations on other Pods.
+
+### 3.5. GitLab CI Daemon
+
+Another potential solution to explore is to have a dedicated service responsible for builds queueing
+owning it's database and working in a model of either sharded or podded service. There were prior
+discussions about [CI/CD Daemon](https://gitlab.com/gitlab-org/gitlab/-/issues/19435).
+
+If the service would be sharded:
+
+- depending on a model if runners are cluster-wide or pod-local this service would have to fetch
+ data from all Pods
+- if the sharded service would be used we could adapt a model of either sharing database containing
+ `ci_pending_builds/ci_running_builds` with the service
+- if the sharded service would be used we could consider a push model where each Pod pushes to CI/CD Daemon
+ builds that should be picked by Runner
+- the sharded service would be aware which Pod is responsible for processing the given build and could
+ route processing requests to designated Pod
+
+If the service would be podded:
+
+- all expectations of routable endpoints are still valid
+
+In general usage of CI Daemon does not help significantly with the stated problem. However, this offers
+a few upsides related to more efficient processing and decoupling model: push model and it opens a way
+to offer stateful communication with GitLab Runners (ex. gRPC or Websockets).
+
+## 4. Evaluation
+
+Considering all solutions it appears that solution giving the most promise is:
+
+- use "instance-wide are Pod local"
+- refine endpoints to have routable identities (either via specific paths, or better tokens)
+
+Other potential upsides is to get rid of `ci_builds.token` and rather use a `JWT token`
+that can much better and easier encode wider set of scopes allowed by CI runner.
+
+## 4.1. Pros
+
+## 4.2. Cons
diff --git a/doc/architecture/blueprints/pods/pods-feature-container-registry.md b/doc/architecture/blueprints/pods/pods-feature-container-registry.md
new file mode 100644
index 00000000000..d47913fbc2a
--- /dev/null
+++ b/doc/architecture/blueprints/pods/pods-feature-container-registry.md
@@ -0,0 +1,131 @@
+---
+stage: enablement
+group: pods
+comments: false
+description: 'Pods: Container Registry'
+---
+
+This document is a work-in-progress and represents a very early state of the
+Pods design. Significant aspects are not documented, though we expect to add
+them in the future. This is one possible architecture for Pods, and we intend to
+contrast this with alternatives before deciding which approach to implement.
+This documentation will be kept even if we decide not to implement this so that
+we can document the reasons for not choosing this approach.
+
+# Pods: Container Registry
+
+GitLab Container Registry is a feature allowing to store Docker Container Images
+in GitLab. You can read about GitLab integration [here](../../../user/packages/container_registry/index.md).
+
+## 1. Definition
+
+GitLab Container Registry is a complex service requiring usage of PostgreSQL, Redis
+and Object Storage dependencies. Right now there's undergoing work to introduce
+[Container Registry Metadata](../container_registry_metadata_database/index.md)
+to optimize data storage and image retention policies of Container Registry.
+
+GitLab Container Registry is serving as a container for stored data,
+but on it's own does not authenticate `docker login`. The `docker login`
+is executed with user credentials (can be `personal access token`)
+or CI build credentials (ephemeral `ci_builds.token`).
+
+Container Registry uses data deduplication. It means that the same blob
+(image layer) that is shared between many projects is stored only once.
+Each layer is hashed by `sha256`.
+
+The `docker login` does request JWT time-limited authentication token that
+is signed by GitLab, but validated by Container Registry service. The JWT
+token does store all authorized scopes (`container repository images`)
+and operation types (`push` or `pull`). A single JWT authentication token
+can be have many authorized scopes. This allows container registry and client
+to mount existing blobs from another scopes. GitLab responds only with
+authorized scopes. Then it is up to GitLab Container Registry to validate
+if the given operation can be performed.
+
+The GitLab.com pages are always scoped to project. Each project can have many
+container registry images attached.
+
+Currently in case of GitLab.com the actual registry service is served
+via `https://registry.gitlab.com`.
+
+The main identifiable problems are:
+
+- the authentication request (`https://gitlab.com/jwt/auth`) that is processed by GitLab.com
+- the `https://registry.gitlab.com` that is run by external service and uses it's own data store
+- the data deduplication, the Pods architecture with registry run in a Pod would reduce
+ efficiency of data storage
+
+## 2. Data flow
+
+### 2.1. Authorization request that is send by `docker login`
+
+```shell
+curl \
+ --user "username:password" \
+ "https://gitlab/jwt/auth?client_id=docker&offline_token=true&service=container_registry&scope=repository:gitlab-org/gitlab-build-images:push,pull"
+```
+
+Result is encoded and signed JWT token. Second base64 encoded string (split by `.`) contains JSON with authorized scopes.
+
+```json
+{"auth_type":"none","access":[{"type":"repository","name":"gitlab-org/gitlab-build-images","actions":["pull"]}],"jti":"61ca2459-091c-4496-a3cf-01bac51d4dc8","aud":"container_registry","iss":"omnibus-gitlab-issuer","iat":1669309469,"nbf":166}
+```
+
+### 2.2. Docker client fetching tags
+
+```shell
+curl \
+ -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
+ -H "Authorization: Bearer token" \
+ https://registry.gitlab.com/v2/gitlab-org/gitlab-build-images/tags/list
+
+curl \
+ -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
+ -H "Authorization: Bearer token" \
+ https://registry.gitlab.com/v2/gitlab-org/gitlab-build-images/manifests/danger-ruby-2.6.6
+```
+
+### 2.3. Docker client fetching blobs and manifests
+
+```shell
+curl \
+ -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
+ -H "Authorization: Bearer token" \
+ https://registry.gitlab.com/v2/gitlab-org/gitlab-build-images/blobs/sha256:a3f2e1afa377d20897e08a85cae089393daa0ec019feab3851d592248674b416
+```
+
+## 3. Proposal
+
+### 3.1. Shard Container Registry separately to Pods architecture
+
+Due to it's architecture it extensive architecture and in general highly scalable
+horizontal architecture it should be evaluated if the GitLab Container Registry
+should be run not in Pod, but in a Cluster and be scaled independently.
+
+This might be easier, but would definitely not offer the same amount of data isolation.
+
+### 3.2. Run Container Registry within a Pod
+
+It appears that except `/jwt/auth` which would likely have to be processed by Router
+(to decode `scope`) the container registry could be run as a local service of a Pod.
+
+The actual data at least in case of GitLab.com is not forwarded via registry,
+but rather served directly from Object Storage / CDN.
+
+Its design encodes container repository image in a URL that is easily routable.
+It appears that we could re-use the same stateless Router service in front of Container Registry
+to serve manifests and blobs redirect.
+
+The only downside is increased complexity of managing standalone registry for each Pod,
+but this might be desired approach.
+
+## 4. Evaluation
+
+There do not seem any theoretical problems with running GitLab Container Registry in a Pod.
+Service seems that can be easily made routable to work well.
+
+The practical complexities are around managing complex service from infrastructure side.
+
+## 4.1. Pros
+
+## 4.2. Cons
diff --git a/doc/architecture/blueprints/pods/pods-feature-contributions-forks.md b/doc/architecture/blueprints/pods/pods-feature-contributions-forks.md
new file mode 100644
index 00000000000..566ae50ec49
--- /dev/null
+++ b/doc/architecture/blueprints/pods/pods-feature-contributions-forks.md
@@ -0,0 +1,120 @@
+---
+stage: enablement
+group: pods
+comments: false
+description: 'Pods: Contributions: Forks'
+---
+
+This document is a work-in-progress and represents a very early state of the
+Pods design. Significant aspects are not documented, though we expect to add
+them in the future. This is one possible architecture for Pods, and we intend to
+contrast this with alternatives before deciding which approach to implement.
+This documentation will be kept even if we decide not to implement this so that
+we can document the reasons for not choosing this approach.
+
+# Pods: Contributions: Forks
+
+[Forking workflow](../../../user/project/repository/forking_workflow.md) allows users
+to copy existing project sources into their own namespace of choice (personal or group).
+
+## 1. Definition
+
+[Forking workflow](../../../user/project/repository/forking_workflow.md) is common workflow
+with various usage patterns:
+
+- allows users to contribute back to upstream project
+- persist repositories into their personal namespace
+- copy to make changes and release as modified project
+
+Forks allow users not having write access to parent project to make changes. The forking workflow
+is especially important for the Open Source community which is able to contribute back
+to public projects. However, it is equally important in some companies which prefer the strong split
+of responsibilites and tighter access control. The access to project is restricted
+to designated list of developers.
+
+Forks enable:
+
+- tigther control of who can modify the upstream project
+- split of the responsibilites: parent project might use CI configuration connecting to production systems
+- run CI pipelines in context of fork in much more restrictive environment
+- consider all forks to be unveted which reduces risks of leaking secrets, or any other information
+ tied with the project
+
+The forking model is problematic in Pods architecture for following reasons:
+
+- Forks are clones of existing repositories, forks could be created across different organizations, Pods and Gitaly shards.
+- User can create merge request and contribute back to upstream project, this upstream project might in a different organization and Pod.
+- The merge request CI pipeline is to executed in a context of source project, but presented in a context of target project.
+
+## 2. Data flow
+
+## 3. Proposals
+
+### 3.1. Intra-Cluster forks
+
+This proposal makes us to implement forks as a intra-ClusterPod forks where communication is done via API
+between all trusted Pods of a cluster:
+
+- Forks when created, they are created always in context of user choice of group.
+- Forks are isolated from Organization.
+- Organization or group owner could disable forking across organizations or forking in general.
+- When a Merge Request is created it is created in context of target project, referencing
+ external project on another Pod.
+- To target project the merge reference is transfered that is used for presenting information
+ in context of target project.
+- CI pipeline is fetched in context of source project as it-is today, the result is fetched into
+ Merge Request of target project.
+- The Pod holding target project internally uses GraphQL to fetch status of source project
+ and include in context of the information for merge request.
+
+Upsides:
+
+- All existing forks continue to work as-is, as they are treated as intra-Cluster forks.
+
+Downsides:
+
+- The purpose of Organizations is to provide strong isolation between organizations
+ allowing to fork across does break security boundaries.
+- However, this is no different to ability of users today to clone repository to local computer
+ and push it to any repository of choice.
+- Access control of source project can be lower than those of target project. System today
+ requires that in order to contribute back the access level needs to be the same for fork and upstream.
+
+### 3.2. Forks are created in a personal namespace of the current organization
+
+Instead of creating projects across organizations, the forks are created in a user personal namespace
+tied with the organization. Example:
+
+- Each user that is part of organization receives their personal namespace. For example for `GitLab Inc.`
+ it could be `gitlab.com/organization/gitlab-inc/@ayufan`.
+- The user has to fork into it's own personal namespace of the organization.
+- The user has that many personal namespaces as many organizations it belongs to.
+- The personal namespace behaves similar to currently offered personal namespace.
+- The user can manage and create projects within a personal namespace.
+- The organization can prevent or disable usage of personal namespaces disallowing forks.
+- All current forks are migrated into personal namespace of user in Organization.
+- All forks are part of to the organization.
+- The forks are not federated features.
+- The personal namespace and forked project do not share configuration with parent project.
+
+### 3.3. Forks are created as internal projects under current project
+
+Instead of creating projects across organizations, the forks are attachments to existing projects.
+Each user forking a project receives their unique project. Example:
+
+- For project: `gitlab.com/gitlab-org/gitlab`, forks would be created in `gitlab.com/gitlab-org/gitlab/@kamil-gitlab`.
+- Forks are created in a context of current organization, they do not cross organization boundaries
+ and are managed by the organization.
+- Tied to the user (or any other user-provided name of the fork).
+- The forks are not federated features.
+
+Downsides:
+
+- Does not answer how to handle and migrate all exisiting forks.
+- Might share current group / project settings - breaking some security boundaries.
+
+## 4. Evaluation
+
+## 4.1. Pros
+
+## 4.2. Cons
diff --git a/doc/architecture/blueprints/pods/pods-feature-dashboard.md b/doc/architecture/blueprints/pods/pods-feature-dashboard.md
new file mode 100644
index 00000000000..e63d912b4c9
--- /dev/null
+++ b/doc/architecture/blueprints/pods/pods-feature-dashboard.md
@@ -0,0 +1,29 @@
+---
+stage: enablement
+group: pods
+comments: false
+description: 'Pods: Dashboard'
+---
+
+This document is a work-in-progress and represents a very early state of the
+Pods design. Significant aspects are not documented, though we expect to add
+them in the future. This is one possible architecture for Pods, and we intend to
+contrast this with alternatives before deciding which approach to implement.
+This documentation will be kept even if we decide not to implement this so that
+we can document the reasons for not choosing this approach.
+
+# Pods: Dashboard
+
+> TL;DR
+
+## 1. Definition
+
+## 2. Data flow
+
+## 3. Proposal
+
+## 4. Evaluation
+
+## 4.1. Pros
+
+## 4.2. Cons
diff --git a/doc/architecture/blueprints/pods/pods-feature-data-migration.md b/doc/architecture/blueprints/pods/pods-feature-data-migration.md
index fad6bca45fa..fbe97316dcc 100644
--- a/doc/architecture/blueprints/pods/pods-feature-data-migration.md
+++ b/doc/architecture/blueprints/pods/pods-feature-data-migration.md
@@ -26,6 +26,24 @@ we can document the reasons for not choosing this approach.
It is essential for Pods architecture to provide a way to migrate data out of big Pods
into smaller ones. This describes various approaches to provide this type of split.
+We also need to handle for cases where data is already violating the expected
+isolation constraints of Pods (ie. references cannot span multiple
+organizations). We know that existing features like linked issues allowed users
+to link issues across any projects regardless of their hierarchy. There are many
+similar features. All of this data will need to be migrated in some way before
+it can be split across different pods. This may mean some data needs to be
+deleted, or the feature changed and modelled slightly differently before we can
+properly split or migrate the organizations between pods.
+
+Having schema deviations across different Pods, which is a necessary
+consequence of different databases, will also impact our ability to migrate
+data between pods. Different schemas impact our ability to reliably replicate
+data across pods and especially impact our ability to validate that the data is
+correctly replicated. It might force us to only be able to move data between
+pods when the schemas are all in sync (slowing down deployments and the
+rebalancing process) or possibly only migrate from newer to older schemas which
+would be complex.
+
## 1. Definition
## 2. Data flow
@@ -48,13 +66,35 @@ physical replication, etc.
1. The data of Pod 0 is live replicated to as many Pods it needs to be split.
1. Once consensus is achieved between Pod 0 and N-Pods the organizations to be migrated away
are marked as read-only cluster-wide.
-1. The `routes` is updated on for all organizations to be split to indicate an authorative
+1. The `routes` is updated on for all organizations to be split to indicate an authoritative
Pod holding the most recent data, like `gitlab-org` on `pod-100`.
1. The data for `gitlab-org` on Pod 0, and on other non-authoritative N-Pods are dormant
and will be removed in the future.
1. All accesses to `gitlab-org` on a given Pod are validated about `pod_id` of `routes`
to ensure that given Pod is authoritative to handle the data.
+#### More challenges of this proposal
+
+1. There is no streaming replication capability for Elasticsearch, but you could
+ snapshot the whole Elasticsearch index and recreate, but this takes hours.
+ It could be handled by pausing Elasticsearch indexing on the initial pod during
+ the migration as indexing downtime is not a big issue, but this still needs
+ to be coordinated with the migration process
+1. Syncing Redis, Gitaly, CI Postgres, Main Postgres, registry Postgres, other
+ new data stores snapshots in an online system would likely lead to gaps
+ without a long downtime. You need to choose a sync point and at the sync
+ point you need to stop writes to perform the migration. The more data stores
+ there are to migrate at the same time the longer the write downtime for the
+ failover. We would also need to find a reliable place in the application to
+ actually block updates to all these systems with a high degree of
+ confidence. In the past we've only been confident by shutting down all rails
+ services because any rails process could write directly to any of these at
+ any time due to async workloads or other surprising code paths.
+1. How to efficiently delete all the orphaned data. Locating all `ci_builds`
+ associated with half the organizations would be very expensive if we have to
+ do joins. We haven't yet determined if we'd want to store an `organization_id`
+ column on every table, but this is the kind of thing it would be helpful for.
+
### 3.2. Migrate organization from an existing Pod
This is different to split, as we intend to perform logical and selective replication
@@ -75,6 +115,14 @@ which Pod is authoritative for this organization.
1. It likely will require a full database structure analysis (more robust than project import/export)
to perform selective PostgreSQL logical replication.
+#### More challenges of this proposal
+
+1. Logical replication is still not performant enough to keep up with our
+ scale. Even if we could use logical replication we still don't have an
+ efficient way to filter data related to a single organization without
+ joining all the way to the `organizations` table which will slow down
+ logical replication dramatically.
+
## 4. Evaluation
## 4.1. Pros
diff --git a/doc/architecture/blueprints/pods/pods-feature-git-access.md b/doc/architecture/blueprints/pods/pods-feature-git-access.md
index ae996281d46..9bda2d1de9c 100644
--- a/doc/architecture/blueprints/pods/pods-feature-git-access.md
+++ b/doc/architecture/blueprints/pods/pods-feature-git-access.md
@@ -15,7 +15,7 @@ we can document the reasons for not choosing this approach.
# Pods: Git Access
This document describes impact of Pods architecture on all Git access (over HTTPS and SSH)
-patterns providing explanantion of how potentially those features should be changed
+patterns providing explanation of how potentially those features should be changed
to work well with Pods.
## 1. Definition
@@ -130,7 +130,7 @@ sequenceDiagram
## 3. Proposal
-The Pods stateless router proposal requires that any ambigious path (that is not routable)
+The Pods stateless router proposal requires that any ambiguous path (that is not routable)
will be made to be routable. It means that at least the following paths will have to be updated
do introduce a routable entity (project, group, or organization).
diff --git a/doc/architecture/blueprints/pods/pods-feature-gitlab-pages.md b/doc/architecture/blueprints/pods/pods-feature-gitlab-pages.md
new file mode 100644
index 00000000000..932f996d8ba
--- /dev/null
+++ b/doc/architecture/blueprints/pods/pods-feature-gitlab-pages.md
@@ -0,0 +1,29 @@
+---
+stage: enablement
+group: pods
+comments: false
+description: 'Pods: GitLab Pages'
+---
+
+This document is a work-in-progress and represents a very early state of the
+Pods design. Significant aspects are not documented, though we expect to add
+them in the future. This is one possible architecture for Pods, and we intend to
+contrast this with alternatives before deciding which approach to implement.
+This documentation will be kept even if we decide not to implement this so that
+we can document the reasons for not choosing this approach.
+
+# Pods: GitLab Pages
+
+> TL;DR
+
+## 1. Definition
+
+## 2. Data flow
+
+## 3. Proposal
+
+## 4. Evaluation
+
+## 4.1. Pros
+
+## 4.2. Cons
diff --git a/doc/architecture/blueprints/pods/pods-feature-global-search.md b/doc/architecture/blueprints/pods/pods-feature-global-search.md
new file mode 100644
index 00000000000..5ea863ac646
--- /dev/null
+++ b/doc/architecture/blueprints/pods/pods-feature-global-search.md
@@ -0,0 +1,47 @@
+---
+stage: enablement
+group: pods
+comments: false
+description: 'Pods: Global search'
+---
+
+DISCLAIMER:
+This page may contain information related to upcoming products, features and
+functionality. It is important to note that the information presented is for
+informational purposes only, so please do not rely on the information for
+purchasing or planning purposes. Just like with all projects, the items
+mentioned on the page are subject to change or delay, and the development,
+release, and timing of any products, features, or functionality remain at the
+sole discretion of GitLab Inc.
+
+This document is a work-in-progress and represents a very early state of the
+Pods design. Significant aspects are not documented, though we expect to add
+them in the future. This is one possible architecture for Pods, and we intend to
+contrast this with alternatives before deciding which approach to implement.
+This documentation will be kept even if we decide not to implement this so that
+we can document the reasons for not choosing this approach.
+
+# Pods: Global search
+
+When we introduce multiple Pods we intend to isolate all services related to
+those Pods. This will include Elasticsearch which means our current global
+search functionality will not work. It may be possible to implement aggregated
+search across all pods, but it is unlikely to be performant to do fan-out
+searches across all pods especially once you start to do pagination which
+requires setting the correct offset and page number for each search.
+
+## 1. Definition
+
+## 2. Data flow
+
+## 3. Proposal
+
+Likely first versions of Pods will simply not support global searches and then
+we may later consider if building global searches to support popular use cases
+is worthwhile.
+
+## 4. Evaluation
+
+## 4.1. Pros
+
+## 4.2. Cons
diff --git a/doc/architecture/blueprints/pods/pods-feature-graphql.md b/doc/architecture/blueprints/pods/pods-feature-graphql.md
index 5f8a39c0b3f..87c8391fbb3 100644
--- a/doc/architecture/blueprints/pods/pods-feature-graphql.md
+++ b/doc/architecture/blueprints/pods/pods-feature-graphql.md
@@ -23,7 +23,7 @@ we can document the reasons for not choosing this approach.
# Pods: GraphQL
-GitLab exensively uses GraphQL to perform efficient data query operations.
+GitLab extensively uses GraphQL to perform efficient data query operations.
GraphQL due to it's nature is not directly routable. The way how GitLab uses
it calls the `/api/graphql` endpoint, and only query or mutation of body request
might define where the data can be accessed.
diff --git a/doc/architecture/blueprints/pods/pods-feature-personal-namespaces.md b/doc/architecture/blueprints/pods/pods-feature-personal-namespaces.md
new file mode 100644
index 00000000000..f78044bb551
--- /dev/null
+++ b/doc/architecture/blueprints/pods/pods-feature-personal-namespaces.md
@@ -0,0 +1,29 @@
+---
+stage: enablement
+group: pods
+comments: false
+description: 'Pods: Personal Namespaces'
+---
+
+This document is a work-in-progress and represents a very early state of the
+Pods design. Significant aspects are not documented, though we expect to add
+them in the future. This is one possible architecture for Pods, and we intend to
+contrast this with alternatives before deciding which approach to implement.
+This documentation will be kept even if we decide not to implement this so that
+we can document the reasons for not choosing this approach.
+
+# Pods: Personal Namespaces
+
+> TL;DR
+
+## 1. Definition
+
+## 2. Data flow
+
+## 3. Proposal
+
+## 4. Evaluation
+
+## 4.1. Pros
+
+## 4.2. Cons
diff --git a/doc/architecture/blueprints/pods/pods-feature-router-endpoints-classification.md b/doc/architecture/blueprints/pods/pods-feature-router-endpoints-classification.md
index c672342fff9..bf0969fcb38 100644
--- a/doc/architecture/blueprints/pods/pods-feature-router-endpoints-classification.md
+++ b/doc/architecture/blueprints/pods/pods-feature-router-endpoints-classification.md
@@ -29,7 +29,7 @@ hitting load balancer of a GitLab installation to a Pod that can serve it.
Each Pod should be able to decode each request and classify for which Pod
it belongs to.
-GitLab currently implements houndreds of endpoints. This document tries
+GitLab currently implements hundreds of endpoints. This document tries
to describe various techniques that can be implemented to allow the Rails
to provide this information efficiently.
diff --git a/doc/architecture/blueprints/pods/pods-feature-schema-changes.md b/doc/architecture/blueprints/pods/pods-feature-schema-changes.md
new file mode 100644
index 00000000000..ae7c288028d
--- /dev/null
+++ b/doc/architecture/blueprints/pods/pods-feature-schema-changes.md
@@ -0,0 +1,55 @@
+---
+stage: enablement
+group: pods
+comments: false
+description: 'Pods: Schema changes'
+---
+
+DISCLAIMER:
+This page may contain information related to upcoming products, features and
+functionality. It is important to note that the information presented is for
+informational purposes only, so please do not rely on the information for
+purchasing or planning purposes. Just like with all projects, the items
+mentioned on the page are subject to change or delay, and the development,
+release, and timing of any products, features, or functionality remain at the
+sole discretion of GitLab Inc.
+
+This document is a work-in-progress and represents a very early state of the
+Pods design. Significant aspects are not documented, though we expect to add
+them in the future. This is one possible architecture for Pods, and we intend to
+contrast this with alternatives before deciding which approach to implement.
+This documentation will be kept even if we decide not to implement this so that
+we can document the reasons for not choosing this approach.
+
+# Pods: Schema changes
+
+When we introduce multiple Pods that own their own databases this will
+complicate the process of making schema changes to Postgres and Elasticsearch.
+Today we already need to be careful to make changes comply with our zero
+downtime deployments. For example,
+[when removing a column we need to make changes over 3 separate deployments](../../../development/database/avoiding_downtime_in_migrations.md#dropping-columns).
+We have tooling like `post_migrate` that helps with these kinds of changes to
+reduce the number of merge requests needed, but these will be complicated when
+we are dealing with deploying multiple rails applications that will be at
+different versions at any one time. This problem will be particularly tricky to
+solve for shared databases like our plan to share the `users` related tables
+among all Pods.
+
+A key benefit of Pods may be that it allows us to run different
+customers on different versions of GitLab. We may choose to update our own pod
+before all our customers giving us even more flexibility than our current
+canary architecture. But doing this means that schema changes need to have even
+more versions of backward compatibility support which could slow down
+development as we need extra steps to make schema changes.
+
+## 1. Definition
+
+## 2. Data flow
+
+## 3. Proposal
+
+## 4. Evaluation
+
+## 4.1. Pros
+
+## 4.2. Cons
diff --git a/doc/architecture/blueprints/pods/pods-feature-snippets.md b/doc/architecture/blueprints/pods/pods-feature-snippets.md
new file mode 100644
index 00000000000..1bb866ca958
--- /dev/null
+++ b/doc/architecture/blueprints/pods/pods-feature-snippets.md
@@ -0,0 +1,29 @@
+---
+stage: enablement
+group: pods
+comments: false
+description: 'Pods: Snippets'
+---
+
+This document is a work-in-progress and represents a very early state of the
+Pods design. Significant aspects are not documented, though we expect to add
+them in the future. This is one possible architecture for Pods, and we intend to
+contrast this with alternatives before deciding which approach to implement.
+This documentation will be kept even if we decide not to implement this so that
+we can document the reasons for not choosing this approach.
+
+# Pods: Snippets
+
+> TL;DR
+
+## 1. Definition
+
+## 2. Data flow
+
+## 3. Proposal
+
+## 4. Evaluation
+
+## 4.1. Pros
+
+## 4.2. Cons
diff --git a/doc/architecture/blueprints/pods/pods-feature-uploads.md b/doc/architecture/blueprints/pods/pods-feature-uploads.md
new file mode 100644
index 00000000000..634f3ef9560
--- /dev/null
+++ b/doc/architecture/blueprints/pods/pods-feature-uploads.md
@@ -0,0 +1,29 @@
+---
+stage: enablement
+group: pods
+comments: false
+description: 'Pods: Uploads'
+---
+
+This document is a work-in-progress and represents a very early state of the
+Pods design. Significant aspects are not documented, though we expect to add
+them in the future. This is one possible architecture for Pods, and we intend to
+contrast this with alternatives before deciding which approach to implement.
+This documentation will be kept even if we decide not to implement this so that
+we can document the reasons for not choosing this approach.
+
+# Pods: Uploads
+
+> TL;DR
+
+## 1. Definition
+
+## 2. Data flow
+
+## 3. Proposal
+
+## 4. Evaluation
+
+## 4.1. Pros
+
+## 4.2. Cons
diff --git a/doc/architecture/blueprints/pods/proposal-stateless-router-with-buffering-requests.md b/doc/architecture/blueprints/pods/proposal-stateless-router-with-buffering-requests.md
index 21aa72273fe..ab19b652f93 100644
--- a/doc/architecture/blueprints/pods/proposal-stateless-router-with-buffering-requests.md
+++ b/doc/architecture/blueprints/pods/proposal-stateless-router-with-buffering-requests.md
@@ -26,7 +26,7 @@ monolith. This architecture also supports regions by allowing for low traffic
databases to be replicated across regions.
Users are not directly exposed to the concept of Pods but instead they see
-different data dependent on their currently chosen "organization".
+different data dependent on their chosen "organization".
[Organizations](index.md#organizations) will be a new model introduced to enforce isolation in the
application and allow us to decide which request route to which pod, since an
organization can only be on a single pod.
@@ -166,7 +166,7 @@ graph TD;
1. A new column `routes.pod_id` is added to `routes` table
1. A new Router service exists to choose which pod to route a request to.
1. A new concept will be introduced in GitLab called an organization and a user can select a "default organization" and this will be a user level setting. The default organization is used to redirect users away from ambiguous routes like `/dashboard` to organization scoped routes like `/organizations/my-organization/-/dashboard`. Legacy users will have a special default organization that allows them to keep using global resources on `Pod US0`. All existing namespaces will initially move to this public organization.
-1. If a pod receives a request for a `routes.pod_id` that it does not own it returns a `302` with `X-Gitlab-Pod-Redirect` header so that the router can send the request to the correct pod. The correct pod can also set a header `X-Gitlab-Pod-Cache` which contains information about how this request should be cached to remember the pod. For example if the request was `/gitlab-org/gitlab` then the header would encode `/gitlab-org/* => Pod US0` (ie. any requests starting with `/gitlab-org/` can always be routed to `Pod US0`
+1. If a pod receives a request for a `routes.pod_id` that it does not own it returns a `302` with `X-Gitlab-Pod-Redirect` header so that the router can send the request to the correct pod. The correct pod can also set a header `X-Gitlab-Pod-Cache` which contains information about how this request should be cached to remember the pod. For example if the request was `/gitlab-org/gitlab` then the header would encode `/gitlab-org/* => Pod US0` (for example, any requests starting with `/gitlab-org/` can always be routed to `Pod US0`
1. When the pod does not know (from the cache) which pod to send a request to it just picks a random pod within it's region
1. Writes to `gitlab_users` and `gitlab_routes` are sent to a primary PostgreSQL server in our `US` region but reads can come from replicas in the same region. This will add latency for these writes but we expect they are infrequent relative to the rest of GitLab.
@@ -176,7 +176,7 @@ All users will get a new column `users.default_organization` which they can
control in user settings. We will introduce a concept of the
`GitLab.com Public` organization. This will be set as the default organization for all existing
users. This organization will allow the user to see data from all namespaces in
-`Pod US0` (ie. our original GitLab.com instance). This behavior can be invisible to
+`Pod US0` (for example, our original GitLab.com instance). This behavior can be invisible to
existing users such that they don't even get told when they are viewing a
global page like `/dashboard` that it's even scoped to an organization.
@@ -195,7 +195,7 @@ frustrating and painful so to avoid this we will decompose and share all Admin A
settings in the `gitlab_admin` schema. This should be safe (similar to other
shared schemas) because these receive very little write traffic.
-In cases where different pods need different settings (eg. the
+In cases where different pods need different settings (for example, the
Elasticsearch URL), we will either decide to use a templated
format in the relevant `application_settings` row which allows it to be dynamic
per pod. Alternatively if that proves difficult we'll introduce a new table
@@ -241,7 +241,7 @@ keeping settings in sync for all pods.
1. Data in `gitlab_users` and `gitlab_routes` databases must be replicated in
all regions which may be an issue for certain types of compliance.
1. The router cache may need to be very large if we get a wide variety of URLs
- (ie. long tail). In such a case we may need to implement a 2nd level of
+ (for example, long tail). In such a case we may need to implement a 2nd level of
caching in user cookies so their frequently accessed pages always go to the
right pod the first time.
1. Having shared database access for `gitlab_users` and `gitlab_routes`
@@ -363,7 +363,7 @@ sequenceDiagram
1. User is in Europe so DNS resolves to the router in Europe
1. The router does not have `/my-company/*` cached yet so it chooses randomly `Pod EU1`
1. `Pod EU1` redirects them through a login flow
-1. Stil they request `/my-company/my-project` without the router cache, so the router chooses a random pod `Pod EU1`
+1. Still they request `/my-company/my-project` without the router cache, so the router chooses a random pod `Pod EU1`
1. `Pod EU1` does not have `/my-company`, but it knows that it lives in `Pod EU0` so it redirects the router to `Pod EU0`
1. `Pod EU0` returns the correct response as well as setting the cache headers for the router `/my-company/* => Pod EU0`
1. The router now caches and remembers any request paths matching `/my-company/*` should go to `Pod EU0`
@@ -499,7 +499,7 @@ allowed to use legacy global functionality like `/dashboard` to see data across
namespaces located on `Pod US0`. The rails backend also knows that the default pod to render any ambiguous
routes like `/dashboard` is `Pod US0`. Lastly the user will be allowed to
navigate to organizations on another pod like `/my-organization` but when they do the
-user will see a message indicating that some data may be missing (eg. the
+user will see a message indicating that some data may be missing (for example, the
MRs/Issues/Todos) counts.
#### Navigates to `/gitlab-org/gitlab` while not logged in
@@ -615,9 +615,9 @@ Migrating data between pods will need to factor all data stores:
### Is it still possible to leak the existence of private groups via a timing attack?
If you have router in EU, and you know that EU router by default redirects
-to EU located Pods, you know their latency (lets assume 10ms). Now, if your
+to EU located Pods, you know their latency (lets assume 10 ms). Now, if your
request is bounced back and redirected to US which has different latency
-(lets assume that roundtrip will be around 60ms) you can deduce that 404 was
+(lets assume that roundtrip will be around 60 ms) you can deduce that 404 was
returned by US Pod and know that your 404 is in fact 403.
We may defer this until we actually implement a pod in a different region. Such timing attacks are already theoretically possible with the way we do permission checks today but the timing difference is probably too small to be able to detect.
diff --git a/doc/architecture/blueprints/pods/proposal-stateless-router-with-routes-learning.md b/doc/architecture/blueprints/pods/proposal-stateless-router-with-routes-learning.md
index e7520f3d6a8..c99b02a35e9 100644
--- a/doc/architecture/blueprints/pods/proposal-stateless-router-with-routes-learning.md
+++ b/doc/architecture/blueprints/pods/proposal-stateless-router-with-routes-learning.md
@@ -26,7 +26,7 @@ monolith. This architecture also supports regions by allowing for low traffic
databases to be replicated across regions.
Users are not directly exposed to the concept of Pods but instead they see
-different data dependent on their currently chosen "organization".
+different data dependent on their chosen "organization".
[Organizations](index.md#organizations) will be a new model introduced to enforce isolation in the
application and allow us to decide which request route to which pod, since an
organization can only be on a single pod.
@@ -639,9 +639,9 @@ Migrating data between pods will need to factor all data stores:
### Is it still possible to leak the existence of private groups via a timing attack?
If you have router in EU, and you know that EU router by default redirects
-to EU located Pods, you know their latency (lets assume 10ms). Now, if your
+to EU located Pods, you know their latency (lets assume 10 ms). Now, if your
request is bounced back and redirected to US which has different latency
-(lets assume that roundtrip will be around 60ms) you can deduce that 404 was
+(lets assume that roundtrip will be around 60 ms) you can deduce that 404 was
returned by US Pod and know that your 404 is in fact 403.
We may defer this until we actually implement a pod in a different region. Such timing attacks are already theoretically possible with the way we do permission checks today but the timing difference is probably too small to be able to detect.
diff --git a/doc/architecture/blueprints/rate_limiting/index.md b/doc/architecture/blueprints/rate_limiting/index.md
index ffe0712d69b..22709a90cee 100644
--- a/doc/architecture/blueprints/rate_limiting/index.md
+++ b/doc/architecture/blueprints/rate_limiting/index.md
@@ -50,7 +50,7 @@ vision of our next rate limiting and policies enforcement architecture.
- Finding what limits are defined requires performing a codebase audit.
- We don't have a good way to expose limits to satellite services like Registry.
- We enforce a number of different policies via opaque external systems
- (Pipeline Validation Service, Bouncer, Watchtower, Cloudflare, Haproxy).
+ (Pipeline Validation Service, Bouncer, Watchtower, Cloudflare, HAProxy).
- There is not standardized way to define policies in a way consistent with defining limits.
- It is difficult to understand when a user is approaching a limit threshold.
- There is no way to automatically notify a user when they are approaching thresholds.
@@ -103,16 +103,16 @@ quota and by a policy.
risks to performance, stability, and security.
- _Example:_ API calls per second for a given IP address
- _Example:_ `git clone` events per minute for a given user
- - _Example:_ maximum artifact upload size of 1GB
+ - _Example:_ maximum artifact upload size of 1 GB
- **Quota:** A global constraint in application usage that is aggregated across an
entire namespace over the duration of their billing cycle.
- _Example:_ 400 CI/CD minutes per namespace per month
- - _Example:_ 10GB transfer per namespace per month
+ - _Example:_ 10 GB transfer per namespace per month
- **Policy:** A representation of business logic that is decoupled from application
code. Decoupled policy definitions allow logic to be shared across multiple services
and/or "hot-loaded" at runtime without releasing a new version of the application.
- _Example:_ decode and verify a JWT, determine whether the user has access to the
- given resource based on the JWT's scopes and claims
+ given resource based on the JWT scopes and claims
- _Example:_ deny access based on group-level constraints
(such as IP allowlist, SSO, and 2FA) across all services
@@ -286,7 +286,7 @@ The GitLab Policy Service might be used in two different ways:
1. The policy service feature will be used as a backend to store policies defined by users.
These are two slightly different use-cases: first one is about using
-internally-defined policies to ensure the stability / availably of a GitLab
+internally-defined policies to ensure the stability / availability of a GitLab
instance (GitLab.com or self-managed instance). The second use-case is about
making GitLab Policy Service a feature that users will be able to build on top
of.
@@ -303,7 +303,7 @@ the sections of this document above.
It is possible that GitLab Policy Service and Decoupled Limits Service can
actually be the same thing. It, however, depends on the implementation details
that we can't predict yet, and the decision about merging these services
-together will need to be informed by subsequent interations' feedback.
+together will need to be informed by subsequent iterations' feedback.
## Hierarchical limits
@@ -362,7 +362,7 @@ hierarchy. Choosing a proper solution will require a thoughtful research.
b. Develop YAML model for limits.
c. Build Rails SDK.
d. Create examples showcasing usage of the new rate limits SDK.
-**Phase 3**: Team Fanout of Rails SDK - Stage Groups
+**Phase 3**: Team fan out of Rails SDK - Stage Groups
a. Individual stage groups begin using the SDK built in Phase 2 for new limit and policies.
b. Stage groups begin replacing historical adhoc limit implementations with the SDK.
c. Provides means to monitor and observe the progress of the replacement effort. Ideally this is broken down to the `feature_category` level to drive group-level buy-in -- Owning Team.
@@ -373,7 +373,7 @@ hierarchy. Choosing a proper solution will require a thoughtful research.
**Phase 5**: SDK for Satellite Services - Owning Team
a. Build Golang SDK.
c. Create examples showcasing usage of the new rate limits SDK.
-**Phase 6**: Team Fanout for Satellite Services - Stage Groups
+**Phase 6**: Team fan out for Satellite Services - Stage Groups
a. Individual stage groups being using the SDK built in Phase 5 for new limit and policies.
b. Stage groups begin replacing historical adhoc limit implementations with the SDK.
diff --git a/doc/architecture/blueprints/remote_development/img/remote_dev_15_7.png b/doc/architecture/blueprints/remote_development/img/remote_dev_15_7.png
new file mode 100644
index 00000000000..d0849ded94f
--- /dev/null
+++ b/doc/architecture/blueprints/remote_development/img/remote_dev_15_7.png
Binary files differ
diff --git a/doc/architecture/blueprints/remote_development/img/remote_dev_15_7_1.png b/doc/architecture/blueprints/remote_development/img/remote_dev_15_7_1.png
new file mode 100644
index 00000000000..330873380d4
--- /dev/null
+++ b/doc/architecture/blueprints/remote_development/img/remote_dev_15_7_1.png
Binary files differ
diff --git a/doc/architecture/blueprints/remote_development/index.md b/doc/architecture/blueprints/remote_development/index.md
new file mode 100644
index 00000000000..39ea2fb2948
--- /dev/null
+++ b/doc/architecture/blueprints/remote_development/index.md
@@ -0,0 +1,315 @@
+---
+status: proposed
+creation-date: "2022-11-15"
+authors: [ "@vtak" ]
+coach: "@grzesiek"
+approvers: [ "@ericschurter", "@oregand" ]
+owning-stage: "~devops::create"
+participating-stages: []
+---
+
+# Remote Development
+
+## Summary
+
+Remote Development is a new architecture for our software-as-a-service platform that provides a more consistent user experience writing code hosted in GitLab. It may also provide additional features in the future, such as a purely browser-based workspace and the ability to connect to an already running VM/Container or to use a GitLab-hosted VM/Container.
+
+## Web IDE and Remote Development
+
+It is important to note that `Remote Development !== Web IDE`, and this is something we want to be explicit about in this document as the terms can become conflated when they shouldn't. Our new Web IDE is a separate ongoing effort that is running in parallel to Remote Development.
+
+These two separate categories do have some overlap as it is a goal to allow a user to connect a running workspace to the Web IDE, **but** this does not mean the two are dependent on one another.
+
+You can use the [Web IDE](../../../user/project/web_ide/index.md) to commit changes to a project directly from your web browser without installing any dependencies or cloning any repositories. The Web IDE, however, lacks a native runtime environment on which you would compile code, run tests, or generate real-time feedback in the IDE. For a more complete IDE experience, you can pair the Web IDE with a Remote Development workspace that has been properly configured to run as a host.
+
+![WebIDERD](img/remote_dev_15_7_1.png)
+
+## Long-term vision
+
+As a [new Software Developer to a team such as Sasha](https://about.gitlab.com/handbook/product/personas/#sasha-software-developer) with no local development environment, I should be able to:
+
+- Navigate to a repository on GitLab.com or self-managed.
+- Click a button that will provide a list of current workspaces for this repository.
+- Click a button that will create a new workspace or select an existing workspace from a list.
+- Go through a configuration wizard that will let me select various options for my workspace (memory/CPU).
+- Start up a workspace from the Web IDE and within a minute have a fully interactive terminal panel at my disposal.
+- Make code changes, run tests, troubleshoot based on the terminal output, and commit new changes.
+- Submit MRs of any kind without having to clone the repository locally or to manually update a local development environment.
+
+## User Flow Diagram
+
+![User Flow](img/remote_dev_15_7.png)
+
+## Terminology
+
+We use the following terms to describe components and properties of the Remote Development architecture.
+
+### Remote Development
+
+Remote Development allows you to use a secure development environment in the cloud that you can connect to from your local machine through a web browser or a client-based solution with the purpose of developing a software product there.
+
+#### Remote Development properties
+
+- Separate your development environment to avoid impacting your local machine configuration.
+- Make it easy for new contributors to get started and keep everyone on a consistent environment.
+- Use tools or runtimes not available on your local OS or manage multiple versions of them.
+- Access an existing development environment from multiple machines or locations.
+
+Discouraged synonyms: VS Code for web, Remote Development Extension, browser-only WebIDE, Client only WebIDE
+
+### Workspace
+
+Container/VM-based developer machines providing all the tools and dependencies needed to code, build, test, run, and debug applications.
+
+#### Workspace properties
+
+- Workspaces should be isolated from each other by default and are responsible for managing the lifecycle of their components. This isolation can be multi-layered: namespace isolation, network isolation, resources isolation, node isolation, sandboxing containers, etc. ([reference](https://kubernetes.io/docs/concepts/security/multi-tenancy/)).
+- A workspace should contain project components as well as editor components.
+- A workspace should be a combination of resources that support cloud-based development environment.
+- Workspaces are constrained by the amount of resources provided to them.
+
+### Legacy Web IDE
+
+The current production [Web IDE](../../../user/project/web_ide/index.md).
+
+#### Legacy Web IDE properties
+
+An advanced editor with commit staging that currently supports:
+
+- [Live Preview](../../../user/project/web_ide/index.md#live-preview)
+- [Interactive Web Terminals](../../../user/project/web_ide/index.md#interactive-web-terminals-for-the-web-ide)
+
+### Web IDE
+
+VS Code for web - replacement of our current legacy Web IDE.
+
+#### Web IDE properties
+
+A package for bootstrapping GitLab context-aware Web IDE that:
+
+- Is built on top of Microsoft's VS Code. We customize and add VS Code features in the [GitLab fork of the VS Code project](https://gitlab.com/gitlab-org/gitlab-web-ide-vscode-fork).
+- Can be configured in a way that it connects to the workspace rather than only using the browser. When connected to a workspace, a user should be able to do the following from the Web IDE:
+ - Edit, build, or debug on a different OS than they are running locally.
+ - Make use of larger or more specialized hardware than their local machine for development.
+ - Separate developer environments to avoid conflicts, improve security, and speed up onboarding.
+
+### Remote Development Extension for Desktop
+
+Something that plugs into the desktop IDE and connects you to the workspace.
+
+#### Remote Development Extension for Desktop properties
+
+- Allows you to open any folder in a workspace.
+- Should be desktop IDE agnostic.
+- Should have access to local files or APIs.
+
+## Goals
+
+### A consistent experience
+
+Organizations should have the same user experience on our SaaS platform as they do on a self-managed GitLab instance. We want to abstract away the user's development environment to avoid impacting their local machine configuration. We also want to provide support for developing on the same operating system you deploy to or use larger or more specialized hardware.
+
+A major goal is that each member of a development team should have the same development experience minus any specialized local configuration. This will also make it easy for new contributors to get started and keep everyone on a consistent environment.
+
+### Increased availability
+
+A workspace should allow access to an existing development environment from multiple machines and locations across a single or multiple teams. It should also allow a user to make use of tools or runtimes not available on their local OS or manage multiple versions of them.
+
+Additionally, Remote Development workspaces could provide a way to implement disaster recovery if we are able to leverage the capabilities of [Pods](../../../architecture/blueprints/pods/index.md).
+
+### Scalability
+
+As an organization begins to scale, they quickly realize the need to support additional types of projects that might require extensive workflows. Remote Development workspaces aim to solve that issue by abstracting away the burden of complex machine configuration, dependency management, and possible data-seeding issues.
+
+To facilitate working on different features across different projects, Remote Development should allow each user to provision multiple workspaces to enable quick context switching.
+
+Eventually, we should be able to allow users to vertically scale their workspaces with more compute cores, memory, and other resources. If a user is currently working against a 2 CPU and 4 GB RAM workspace but comes to find they need more CPU, they should be able to upgrade their compute layer to something more suitable with a click or CLI command within the workspace.
+
+### Provide built-in security and enterprise readiness
+
+As Remote Development becomes a viable replacement for Virtual Desktop Infrastructure solutions, they must be secure and support enterprise requirements, such as role-based access control and the ability to remove all source code from developer machines.
+
+### Accelerate project and developer onboarding
+
+As a zero-install development environment that runs in your browser, Remote Development makes it easy for anyone to join your team and contribute to a project.
+
+### Regions
+
+GitLab.com is only hosted within the United States of America. Organizations located in other regions have voiced demand for local SaaS offerings. BYO infrastructure helps work in conjunction with [GitLab Regions](https://gitlab.com/groups/gitlab-org/-/epics/6037) because a user's workspace may be deployed within different geographies. The ability to deploy workspaces to different geographies might also help to solve data residency and compliance problems.
+
+## High-level architecture problems to solve
+
+A number of technical issues need to be resolved to implement a stable Remote Development offering. This section will be expanded.
+
+- Who is our main persona for BYO infrastructure?
+- How do users authenticate?
+- How do we support more than one IDE?
+- How are workspaces provisioned?
+- How can workspaces implement disaster recovery capabilities?
+- If we cannot use SSH, what are the viable alternatives for establishing a secure WebSocket connection?
+- Are we running into any limitations in functionality with the Web IDE by not having it running in the container itself? For example, are we going to get code completion, linting, and language server type features to work with our approach?
+- How will our environments be provisioned, managed, created, destroyed, etc.?
+- To what extent do we need to provide the user with a UI to interact with the provisioned environments?
+- How will the files inside the workspace get live updated based on changes in the Web IDE? Are we going to use a [CRDT](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)-like setup to patch files in a container? Are we going to generate a diff and send it though a WebSocket connection?
+
+## Iteration plan
+
+We can't ship the entire Remote Development architecture in one go - it is too large. Instead, we are adopting an iteration plan that provides value along the way.
+
+- Use GitLab Agent for Kubernetes Remote Development Module.
+- Integrate Remote Development with the UI and Web IDE.
+- Improve security and usability.
+
+### High-level approach
+
+The nuts and bolts are being worked out at [Remote Development GA4K Architecture](https://gitlab.com/gitlab-org/remote-development/gitlab-remote-development-docs/-/blob/main/doc/architecture.md) to keep a SSoT. Once we have hammered out the details, we'll replace this section with the diagram in the above repository.
+
+### Iteration 0: [GitLab Agent for Kubernetes Remote Development Module (plumbing)](https://gitlab.com/groups/gitlab-org/-/epics/9138)
+
+#### Goals
+
+- Use the [GitLab Agent](../../../user/clusters/agent/index.md) integration.
+- Create a workspace in a Kubernetes cluster based on a `devfile` in a public repository.
+- Install the IDE and dependencies as defined.
+- Report the status of the environment (via the terminal or through an endpoint).
+- Connect to an IDE in the workspace.
+
+#### Requirements
+
+- Remote environment running on a Kubernetes cluster based on a `devfile` in a repo.
+
+These are **not** part of Iteration 0:
+
+- Authentication/authorization with GitLab and a user.
+- Integration of Remote Development with the GitLab UI and Web IDE.
+- Using GA4K instead of an Ingress controller.
+
+#### Assumptions
+
+- We will use [`devworkspace-operator` v0.17.0 (latest version)](https://github.com/devfile/devworkspace-operator/releases/tag/v0.17.0). A prerequisite is [`cert-manager`](https://github.com/devfile/devworkspace-operator#with-yaml-resources).
+- We have an Ingress controller ([Ingress-NGINX](https://github.com/kubernetes/ingress-nginx)), which is accessible over the network.
+- The initial server is stubbed.
+
+#### Success criteria
+
+- Using GA4K to communicate with the Kubernetes API from the `remote_dev` agent module.
+- All calls to the Kubernetes API are done through GA4K.
+- A workspace in a Kubernetes cluster created using DevWorkspace Operator.
+
+### Iteration 1: [Rails endpoints, authentication, and authorization](https://gitlab.com/groups/gitlab-org/-/epics/9323)
+
+#### Goals
+
+- Add endpoints in Rails to accept work from a user.
+- Poll Rails for work from KAS.
+- Add authentication and authorization to the workspaces created in the Kubernetes cluster.
+- Extend the GA4K `remote_dev` agent module to accept more types of work (get details of a workspace, list workspaces for a user, etc).
+- Build an editor injector for the GitLab fork of VS Code.
+
+#### Requirements
+
+- [GitLab Agent for Kubernetes Remote Development Module (plumbing)](https://gitlab.com/groups/gitlab-org/-/epics/9138) is complete.
+
+These are **not** part of Iteration 1:
+
+- Integration of Remote Development with the GitLab UI and Web IDE.
+- Using GA4K instead of an Ingress controller.
+
+#### Assumptions
+
+- TBA
+
+#### Success criteria
+
+- Poll Rails for work from KAS.
+- Rails endpoints to create/delete/get/list workspaces.
+- All requests are correctly authenticated and authorized except where the user has requested the traffic to be public (for example, opening a server while developing and making it public).
+- A user can create a workspace, start a server on that workspace, and have that traffic become private/internal/public.
+- We are using the GitLab fork of VS Code as an editor.
+
+### Iteration 2: [Integrate Remote Development with the UI and Web IDE](https://gitlab.com/groups/gitlab-org/-/epics/9169)
+
+#### Goals
+
+- Allow users full control of their workspaces via the GitLab UI.
+
+#### Requirements
+
+- [GitLab Agent for Kubernetes Remote Development Module](https://gitlab.com/groups/gitlab-org/-/epics/9138).
+
+These are **not** part of Iteration 2:
+
+- Usability improvements
+- Security improvements
+
+#### Success criteria
+
+- Be able to list/create/delete/stop/start/restart workspaces from the UI.
+- Be able to create workspaces for the user in the Web IDE.
+- Allow the Web IDE terminal to connect to different containers in the workspace.
+- Configure DevWorkspace Operator for user-expected configuration (30-minute workspace timeout, a separate persistent volume for each workspace that is deleted when the workspace is deleted, etc.).
+
+### Iteration 3: [Improve security and usability](https://gitlab.com/groups/gitlab-org/-/epics/9170)
+
+#### Goals
+
+- Improve security and usability of our Remote Development solution.
+
+#### Requirements
+
+- [Integrate Remote Development with the UI and Web IDE](https://gitlab.com/groups/gitlab-org/-/epics/9169) is complete.
+
+#### Assumptions
+
+- We are allowing for internal feedback and closed/early customer feedback that can be iterated on.
+- We have explored or are exploring the feasibility of using GA4K with Ingresses in [Solving Ingress problems for Remote Development](https://gitlab.com/gitlab-org/gitlab/-/issues/378998).
+- We have explored or are exploring Kata containers for providing root access to workspace users in [Investigate Kata Containers / Firecracker / gVisor](https://gitlab.com/gitlab-org/gitlab/-/issues/367043).
+- We have explored or are exploring how Ingress/Egress requests cannot be misused from [resources within or outside the cluster](https://gitlab.com/gitlab-org/remote-development/gitlab-remote-development-docs/-/blob/main/doc/securing-the-workspace.md) (security hardening).
+
+#### Success criteria
+
+Add options to:
+
+- Create different classes of workspaces (1gb-2cpu, 4gb-8cpu, etc.).
+- Vertically scale up workspace resources.
+- Inject secrets from a GitLab user/group/repository.
+- Configure timeouts of workspaces at multiple levels.
+- Allow users to expose endpoints in their workspace (for example, not allow anyone in the organization to expose any endpoint publicly).
+
+## Market analysis
+
+We have conducted a market analysis to understand the broader market and what others can offer us by way of open-source libraries, integrations, or partnership opportunities. We have broken down the effort into a set of issues where we investigate each potential competitor/pathway/partnership as a spike.
+
+- [Market analysis](https://gitlab.com/groups/gitlab-org/-/epics/8131)
+- [YouTube results](https://www.youtube.com/playlist?list=PL05JrBw4t0KrRQhnSYRNh1s1mEUypx67-)
+
+### Next Steps
+
+While our spike proved fruitful, we have paused this investigation until we reach our goals in [Viable Maturity](https://gitlab.com/groups/gitlab-org/-/epics/9190).
+
+## Che versus a custom-built solution
+
+After an investigation into using [Che](https://gitlab.com/gitlab-org/gitlab/-/issues/366052) as our backend to accelerate Remote Development, we ultimately opted to [write our own custom-built solution](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97449#note_1131215629).
+
+Some advantages of us opting to write our own custom-built solution are:
+
+- We can still use the core DevWorkspace Operator and build on top of it.
+- It is easier to add support for other configurations apart from `devfile` in the future if the need arises.
+- We have the ability to choose which tech stack to use (for example, instead of using Traefik which is used in Che, explore NGINX itself or use GitLab Agent for Kubernetes).
+
+## Links
+
+- [Remote Development presentation](https://docs.google.com/presentation/d/1XHH_ZilZPufQoWVWViv3evipI-BnAvRQrdvzlhBuumw/edit#slide=id.g131f2bb72e4_0_8)
+- [Category Strategy epic](https://gitlab.com/groups/gitlab-org/-/epics/7419)
+- [Minimal Maturity epic](https://gitlab.com/groups/gitlab-org/-/epics/9189)
+- [Viable Maturity epic](https://gitlab.com/groups/gitlab-org/-/epics/9190)
+- [Complete Maturity epic](https://gitlab.com/groups/gitlab-org/-/epics/9191)
+- [Bi-weekly sync](https://docs.google.com/document/d/1hWVvksIc7VzZjG-0iSlzBnLpyr-OjwBVCYMxsBB3h_E/edit#)
+- [Market analysis and architecture](https://gitlab.com/groups/gitlab-org/-/epics/8131)
+- [GA4K Architecture](https://gitlab.com/gitlab-org/remote-development/gitlab-remote-development-docs/-/blob/main/doc/architecture.md)
+- [BYO infrastructure](https://gitlab.com/groups/gitlab-org/-/epics/8290)
+- [Browser runtime](https://gitlab.com/groups/gitlab-org/-/epics/8291)
+- [GitLab-hosted infrastructure](https://gitlab.com/groups/gitlab-org/-/epics/8292)
+- [Browser runtime spike](https://gitlab.com/gitlab-org/gitlab-web-ide/-/merge_requests/58).
+- [Remote Development direction](https://about.gitlab.com/direction/create/editor/remote_development)
+- [Ideal user journey](https://about.gitlab.com/direction/create/editor/remote_development/#ideal-user-journey)
diff --git a/doc/architecture/blueprints/runner_scaling/index.md b/doc/architecture/blueprints/runner_scaling/index.md
index 24c6820f94a..8eb6bfd2551 100644
--- a/doc/architecture/blueprints/runner_scaling/index.md
+++ b/doc/architecture/blueprints/runner_scaling/index.md
@@ -234,7 +234,7 @@ them each separately.
etc... This information is very provider specific.
- **VM lifecycle management**. Multiple machines will be created and a
system must keep track of which machines belong to this executor. Typically
- a cloud provider will have a way to manage a set of homogenous machines.
+ a cloud provider will have a way to manage a set of homogeneous machines.
E.g. GCE Instance Group. The basic operations are increase, decrease and
usually delete a specific machine.
- **VM autoscaling**. In addition to low-level lifecycle management,
@@ -273,7 +273,7 @@ interfaces.
Within the `docker+autoscaling` executor the [`machineExecutor`](https://gitlab.com/gitlab-org/gitlab-runner/-/blob/267f40d871cd260dd063f7fbd36a921fedc62241/executors/docker/machine/machine.go#L19)
type has a [`Machine`](https://gitlab.com/gitlab-org/gitlab-runner/-/blob/267f40d871cd260dd063f7fbd36a921fedc62241/helpers/docker/machine.go#L7)
-interface which it uses to aquire a VM during the common [`Prepare`](https://gitlab.com/gitlab-org/gitlab-runner/-/blob/267f40d871cd260dd063f7fbd36a921fedc62241/executors/docker/machine/machine.go#L71)
+interface which it uses to acquire a VM during the common [`Prepare`](https://gitlab.com/gitlab-org/gitlab-runner/-/blob/267f40d871cd260dd063f7fbd36a921fedc62241/executors/docker/machine/machine.go#L71)
phase. This abstraction primarily creates, accesses and deletes VMs.
There is no current abstraction for the VM autoscaling logic. It is tightly
@@ -372,7 +372,7 @@ provide a context and an environment in which a build will be executed by one
of the Custom Executors.
There are multiple solutions to implementing a custom provider abstraction. We
-can use raw Go plugins, Hashcorp's Go Plugin, HTTP interface or gRPC based
+can use raw Go plugins, HashiCorp's Go Plugin, HTTP interface or gRPC based
facade service. There are many solutions, and we want to choose the most
optimal one. In order to do that, we will describe the solutions in a separate
document, define requirements and score the solution accordingly. This will
@@ -390,18 +390,18 @@ Rationale: [Description of the Custom Executor Provider proposal](https://gitlab
We can introduce a more simple version of the `Machine` abstraction in the
form of a "Fleeting" interface. Fleeting provides a low-level interface to
-a homogenous VM group which allows increasing and decreasing the set size
+a homogeneous VM group which allows increasing and decreasing the set size
as well as consuming a VM from within the set.
Plugins for cloud providers and other VM sources are implemented via the
-Hashicorp go-plugin library. This is in practice gRPC over STDIN/STDOUT
+HashiCorp go-plugin library. This is in practice gRPC over STDIN/STDOUT
but other wire protocols can be used also.
In order to make use of the new interface, the autoscaling logic is pulled
out of the Docker Executor and placed into a new Taskscaler library.
This places the concerns of VM lifecycle, VM shape and job routing within
-the plugin. It also places the conern of VM autoscaling into a separate
+the plugin. It also places the concern of VM autoscaling into a separate
component so it can be used by multiple Runner Executors (not just `docker+autoscaling`).
Rationale: [Description of the InstanceGroup / Fleeting proposal](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/28848#note_823430883)
diff --git a/doc/architecture/blueprints/runner_tokens/index.md b/doc/architecture/blueprints/runner_tokens/index.md
index 3f8a27e503d..7a282649c5c 100644
--- a/doc/architecture/blueprints/runner_tokens/index.md
+++ b/doc/architecture/blueprints/runner_tokens/index.md
@@ -14,7 +14,7 @@ CI/CD jobs in a reliable and concurrent environment. Ever since the beginnings
of the service as a Ruby program, runners are registered in a GitLab instance with
a registration token - a randomly generated string of text. The registration token is unique for its given scope
(instance, group, or project). The registration token proves that the party that registers the runner has
-administrator access to the instance, group, or project to which the runner is registered.
+administrative access to the instance, group, or project to which the runner is registered.
This approach has worked well in the initial years, but some major known issues started to
become apparent as the target audience grew:
@@ -37,49 +37,122 @@ We call this new mechanism the "next GitLab Runner Token architecture".
The proposal addresses the issues of a _single token per scope_ and _token storage_
by eliminating the need for a registration token. Runner creation happens
-in the GitLab Runners settings page for the given scope, in the context of the logged-in user
-, which provides traceability. The page provides instructions to configure the newly-created
-runner in supported environments.
+in the GitLab Runners settings page for the given scope, in the context of the logged-in user,
+which provides traceability. The page provides instructions to configure the newly-created
+runner in supported environments using the existing `gitlab-runner register` command.
-The runner configuration will be generated through a new `deploy` command, which will leverage
-the `/runners/verify` REST endpoint to ensure the validity of the authentication token.
The remaining concerns become non-issues due to the elimination of the registration token.
-The configuration can be applied across many machines by reusing the same instructions.
-A unique system identifier will be generated automatically if a value is missing from
-the runner entry in the `config.toml` file. This allows differentiating systems sharing the same
-runner token (for example, in auto-scaling scenarios), and is crucial for the proper functioning of our
-long-polling mechanism when the same authentication token is shared across two or more runner managers.
+### Using the authentication token in place of the registration token
+
+<!-- vale gitlab.Spelling = NO -->
+In this proposal, runners created in the GitLab UI are assigned authentication tokens prefixed with
+`glrt-` (**G**it**L**ab **R**unner **T**oken).
+<!-- vale gitlab.Spelling = YES -->
+The prefix allows the existing `register` command to use the authentication token _in lieu_
+of the current registration token (`--registration-token`), requiring minimal adjustments in
+existing workflows.
+The authentication token is shown to the user only once - after completing the creation flow - to
+discourage unintended reuse.
+
+Given that the runner is pre-created through the GitLab UI, the `register` command fails if
+provided with arguments that are exposed in the runner creation form.
+Some examples are `--tag-list`, `--run-untagged`, `--locked`, or `--access-level` as these are
+sensitive parameters that should be decided at creation time by an administrator/owner.
+The runner configuration is generated through the existing `register` command, which can behave in
+two different ways depending on whether it is supplied a registration token or an authentication
+token in the `--registration-token` argument:
+
+| Token type | Behavior |
+| ---------- | -------- |
+| Registration token | Leverages the `POST /api/v4/runners` REST endpoint to create a new runner, creating a new entry in `config.toml`. |
+| Authentication token | Leverages the `POST /api/v4/runners/verify` REST endpoint to ensure the validity of the authentication token. Creates an entry in `config.toml` file and a `system_id` value in a sidecar file if missing (`.runner_system_id`). |
+
+### Transition period
+
+During a transition period, legacy tokens ("registration tokens") continue to be shown on the
+GitLab Runners settings page and to be accepted by the `gitlab-runner register` command.
+The legacy workflow is nevertheless discouraged in the UI.
+Users are steered towards the new flow consisting of creating the runner in the UI and using the
+resulting authentication token with the `gitlab-runner register` command as they do today.
+This approach reduces disruption to users responsible for deploying runners.
+
+### Reusing the runner authentication token across many machines
+
+In the existing model, a new runner is created whenever a new worker is required. This
+has led to many situations where runners are left behind and become stale.
+
+In the proposed model, a `ci_runners` table entry describes a configuration that the user can reuse
+across multiple machines.
+A unique system identifier is [generated automatically](#generating-a-system_id-value) whenever the
+runner application starts up or the configuration is saved.
+This allows differentiating the context in which the runner is being used.
+
+The `system_id` value complements the short runner token that is currently used to identify a
+runner in command line output, CI job logs, and GitLab UI.
Given that the creation of runners involves user interaction, it should be possible
to eventually lower the per-plan limit of CI runners that can be registered per scope.
-### Auto-scaling scenarios (for example Helm chart)
+#### Generating a `system_id` value
-In the existing model, a new runner is created whenever a new worker is required. This
-has led to many situations where runners are left behind and become stale.
+We ensure that a unique system identifier is assigned at all times to a `gitlab-runner`
+installation.
+The ID is derived from an existing machine identifier such as `/etc/machine-id` (on Linux) and
+hashed for privacy, in which case it is prefixed with `s_`.
+If an ID is not available, a random string is used instead, in which case it is prefixed with `r_`.
-In the proposed model, a `ci_runners` table entry describes a configuration,
-which the runner could reuse across multiple machines. This allows differentiating the context in
-which the runner is being used. In situations where we must differentiate between runners
-that reuse the same configuration, we can use the unique system identifier to track all
-unique "runners" that are executed in context of a single `ci_runners` model. This unique
-system identifier would be present in the Runner's `config.toml` configuration file and
-initially set when generating the new `[[runners]]` configuration by means of the `deploy` command.
-Legacy files that miss values for unique system identifiers will get rewritten automatically with new values.
+This unique ID identifies the `gitlab-runner` process and is sent
+on `POST /api/v4/jobs` requests for all runners in the `config.toml` file.
+
+The ID is generated and saved both at `gitlab-runner` startup and whenever the configuration is
+saved to disk.
+Instead of saving the ID at the root of `config.toml` though, we save it to a new file that lives
+next to it - `.runner_system_id`. The goal for this new file is to make it less likely that IDs
+get reused due to manual copying of the `config.toml` file
+
+```plain
+s_cpwhDr7zFz4xBJujFeEM
+```
### Runner identification in CI jobs
-For users to identify the machine where the job was executed, the unique identifier will need to be visible in CI job contexts.
+For users to identify the machine where the job was executed, the unique identifier needs to be
+visible in CI job contexts.
As a first iteration, GitLab Runner will include the unique system identifier in the build logs,
wherever it publishes the short token SHA.
-Given that the runner will potentially be reused with different unique system identifiers,
-we can store the unique system ID. This ensures the unique system ID maps to a GitLab Runner's `config.toml` entry with
-the runner token. The `ci_runner_machines` would hold information about each unique runner machine,
-with information when runner last connected, and what type of runner it was. The relevant fields
-will be moved from the `ci_runners`.
-The `ci_builds_runner_session` (or `ci_builds` or `ci_builds_metadata`) will reference
+Given that the runner can potentially be reused with different unique system identifiers,
+we should store the unique system ID in the database.
+This ensures the unique system ID maps to a GitLab Runner's `system_id` value with the runner token.
+A new `ci_runner_machines` table holds information about each unique runner machine,
+with information regarding when the runner last connected, and what type of runner it was.
+
+In the long term, the relevant fields are to be moved from the `ci_runners` into
+`ci_runner_machines`.
+Until the removal milestone though, they should be kept in the `ci_runners` as a fallback when a
+matching `ci_runner_machines` record does not exist.
+An expected scenario is the case when the table is created but the runner hasn't pinged the GitLab
+instance (for example if the runner is offline).
+
+In addition, we should add the following columns to `ci_runners`:
+
+- a `user_id` column to keep track of who created a runner;
+- a `registration_type` enum column to `ci_runners` to signal whether a runner has been created
+ using the legacy `register` method, or the new UI-based method.
+ Possible values are `:registration_token` and `:authenticated_user`.
+ This allows the stale runner cleanup service to determine which runners to clean up, and allows
+ future uses that may not be apparent.
+
+```sql
+CREATE TABLE ci_runner (
+ ...
+ user_id bigint
+ registration_type int8
+)
+```
+
+The `ci_builds_runner_session` (or `ci_builds` or `ci_builds_metadata`) shall reference
`ci_runner_machines`.
We might consider a more efficient way to store `contacted_at` than updating the existing record.
@@ -110,7 +183,8 @@ CREATE TABLE ci_runner_machines (
- Runners can always be traced back to the user who created it, using the audit log;
- The claims of a CI runner are known at creation time, and cannot be changed from the runner
(for example, changing the `access_level`/`protected` flag). Authenticated users
- may however still edit these settings through the GitLab UI.
+ may however still edit these settings through the GitLab UI;
+- Easier cleanup of stale runners, which doesn't touch the `ci_runner` table.
## Details
@@ -121,53 +195,95 @@ token to register new runners.
The new workflow looks as follows:
- 1. The user opens the Runners settings page;
+ 1. The user opens the Runners settings page (instance, group, or project level);
1. The user fills in the details regarding the new desired runner, namely description,
tags, protected, locked, etc.;
1. The user clicks `Create`. That results in the following:
- 1. Creates a new runner in the `ci_runners` table (and corresponding authentication token);
+ 1. Creates a new runner in the `ci_runners` table (and corresponding `glrt-` prefixed authentication token);
1. Presents the user with instructions on how to configure this new runner on a machine,
with possibilities for different supported deployment scenarios (e.g. shell, `docker-compose`, Helm chart, etc.)
- This information contains a token which will only be available to the user once, and the UI
- will make it clear to the user that the value will not be shown again, as registering the same runner multiple times
+ This information contains a token which is available to the user only once, and the UI
+ makes it clear to the user that the value shall not be shown again, as registering the same runner multiple times
is discouraged (though not impossible).
- 1. The user copies and pastes the instructions for the intended deployment scenario (a `deploy` command), leading to the following actions:
+ 1. The user copies and pastes the instructions for the intended deployment scenario (a `register` command), leading to the following actions:
- 1. Upon executing the new `gitlab-runner deploy` command in the instructions, `gitlab-runner` will perform
- a call to the `POST /runners/verify` with the given runner token;
- 1. If the `POST /runners/verify` GitLab endpoint validates the token, the `config.toml` file will be populated with the configuration.
+ 1. Upon executing the new `gitlab-runner register` command in the instructions, `gitlab-runner` performs
+ a call to the `POST /api/v4/runners/verify` with the given runner token;
+ 1. If the `POST /api/v4/runners/verify` GitLab endpoint validates the token, the `config.toml`
+ file is populated with the configuration;
+ 1. Whenever a runner pings for a job, the respective `ci_runner_machines` record is
+ ["upserted"](https://en.wiktionary.org/wiki/upsert) with the latest information about the
+ runner (with Redis cache in front of it like we do for Runner heartbeats).
- The `gitlab-runner deploy` will also accept executor-specific arguments
- currently present in the `register` command.
+As part of the transition period, we provide admins and top-level group owners with an
+instance/group-level setting (`allow_runner_registration_token`) to disable the legacy registration
+token functionality and enforce using only the new workflow.
+Any attempt by a `gitlab-runner register` command to hit the `POST /api/v4/runners` endpoint
+to register a new runner with a registration token results in a `HTTP 410 Gone` status code.
-As part of the transition period, we will provide admins and top-level group owners with a instance/group-level setting to disable
-the legacy registration token functionality and enforce using only the new workflow.
-Any attempt by a `gitlab-runner register` command to hit the `POST /runners` endpoint to register a new runner
-will result in a `HTTP 410 - Gone` status code. The instance setting is inherited by the groups
-, which means that if the legacy registration method is disabled at the instance method, the descendant groups/projects will also mandatorily
-prevent the legacy registration method.
+The instance setting is inherited by the groups. This means that if the legacy registration method
+is disabled at the instance method, the descendant groups/projects mandatorily prevents the legacy
+registration method.
The registration token workflow is to be deprecated (with a deprecation notice printed by the `gitlab-runner register` command)
and removed at a future major release after the concept is proven stable and customers have migrated to the new workflow.
### Handling of legacy runners
-Legacy versions of GitLab Runner will not send the unique system identifier in its requests, and we
+Legacy versions of GitLab Runner do not send the unique system identifier in its requests, and we
will not change logic in Workhorse to handle unique system IDs. This can be improved upon in the
-future once the legacy registration system is removed, and runners have been upgraded to newer
+future after the legacy registration system is removed, and runners have been upgraded to newer
versions.
-Not using the unique system ID means that all connected runners with the same token will be
+Job pings from such legacy runners results in a `ci_runner_machines` record containing a
+`<legacy>` `machine_id` field value.
+
+Not using the unique system ID means that all connected runners with the same token are
notified, instead of just the runner matching the exact system identifier. While not ideal, this is
not an issue per-se.
-### Helm chart
+### `ci_runner_machines` record lifetime
+
+New records are created when the runner pings the GitLab instance for new jobs, if a record matching
+the `token`+`system_id` does not already exist.
+
+Due to the time-decaying nature of the `ci_runner_machines` records, they are automatically
+cleaned after 7 days after the last contact from the respective runner.
+
+### Required adaptations
-The `runnerRegistrationToken` entry in the [`values.yaml` file](https://gitlab.com/gitlab-org/charts/gitlab-runner/-/blob/a70bc29a903b79d5675bb0c45d981adf8b7a8659/values.yaml#L52)
-will be retired. The `runnerRegistrationToken` entry will be replaced by the existing `runnerToken` value, which will be passed
-to the new `gitlab-runner deploy` command in [`configmap.yaml`](https://gitlab.com/gitlab-org/charts/gitlab-runner/-/blob/a70bc29a903b79d5675bb0c45d981adf8b7a8659/templates/configmap.yaml#L116).
+#### Migration to `ci_runner_machines` table
+
+When details from `ci_runner_machines` are needed, we need to fall back to the existing fields in
+`ci_runner` if a match is not found in `ci_runner_machines`.
+
+#### REST API
+
+API endpoints receiving runner tokens should be changed to also take an optional
+`system_id` parameter, sent alongside with the runner token (most often as a JSON parameter on the
+request body).
+
+#### GraphQL `CiRunner` type
+
+The [`CiRunner` type](../../../api/graphql/reference/index.md#cirunner) closely reflects the
+`ci_runners` model. This means that machine information such as `ipAddress`, `architectureName`,
+and `executorName` among others are no longer singular values in the proposed approach.
+We can live with that fact for the time being and start returning lists of unique values, separated
+by commas.
+The respective `CiRunner` fields must return the values for the `ci_runner_machines` entries
+(falling back to `ci_runner` record if non-existent).
+
+#### Stale runner cleanup
+
+The functionality to
+[clean up stale runners](../../../ci/runners/configure_runners.md#clean-up-stale-runners) needs
+to be adapted to clean up `ci_runner_machines` records instead of `ci_runners` records.
+
+At some point after the removal of the registration token support, we'll want to create a background
+migration to clean up stale runners that have been created with a registration token (leveraging the
+enum column created in the `ci_runners` table.
### Runner creation through API
@@ -176,21 +292,66 @@ using PAT tokens for example - such that every runner is associated with an owne
## Implementation plan
+### Stage 1 - Deprecations
+
+| Component | Milestone | Changes |
+|------------------|----------:|---------|
+| GitLab Rails app | `15.6` | Deprecate `POST /api/v4/runners` endpoint for `17.0`. This hinges on a [proposal](https://gitlab.com/gitlab-org/gitlab/-/issues/373774) to allow deprecating REST API endpoints for security reasons. |
+| GitLab Runner | `15.6` | Add deprecation notice for `register` command for `17.0`. |
+| GitLab Runner Helm Chart | `15.6` | Add deprecation notice for `runnerRegistrationToken` command for `17.0`. |
+| GitLab Runner Operator | `15.6` | Add deprecation notice for `runner-registration-token` command for `17.0`. |
+| GitLab Runner / GitLab Rails app | `15.7` | Add deprecation notice for registration token reset for `17.0`. |
+
+### Stage 2 - Prepare `gitlab-runner` for `system_id`
+
+| Component | Milestone | Changes |
+|------------------|----------:|---------|
+| GitLab Runner | `15.x` | Ensure a sidecar TOML file exists with a `system_id` value.<br/>Log new system ID values with `INFO` level as they get assigned. |
+| GitLab Runner | `15.x` | Log unique system ID in the build logs. |
+| GitLab Runner | `15.x` | Label Prometheus metrics with unique system ID. |
+| GitLab Runner | `15.x` | Prepare `register` command to fail if runner server-side configuration options are passed together with a new `glrt-` token. |
+
+### Stage 3 - Database changes
+
+| Component | Milestone | Changes |
+|------------------|----------:|---------|
+| GitLab Rails app | | Create database migration to add columns to `ci_runners` table. |
+| GitLab Rails app | | Create database migration to add `ci_runner_machines` table. |
+| GitLab Rails app | | Create database migration to add `ci_runner_machines.machine_id` foreign key to `ci_builds_runner_session` table. |
+| GitLab Rails app | | Create database migrations to add `allow_runner_registration_token` setting to `application_settings` and `namespace_settings` tables (default: `true`). |
+| GitLab Runner | | Use runner token + `system_id` JSON parameters in `POST /jobs/request` request in the [heartbeat request](https://gitlab.com/gitlab-org/gitlab/blob/c73c96a8ffd515295842d72a3635a8ae873d688c/lib/api/ci/helpers/runner.rb#L14-20) to update the `ci_runner_machines` cache/table. |
+| GitLab Runner | | Start sending `system_id` value in `POST /jobs/request` request and other follow-up requests that require identifying the unique system. |
+| GitLab Rails app | | Create service similar to `StaleGroupRunnersPruneCronWorker` service to clean up `ci_runner_machines` records instead of `ci_runners` records.<br/>Existing service continues to exist but focuses only on legacy runners. |
+
+### Stage 4 - New UI
+
+| Component | Milestone | Changes |
+|------------------|----------:|---------|
+| GitLab Runner | | Implement new GraphQL user-authenticated API to create a new runner. |
+| GitLab Runner | | [Add prefix to newly generated runner authentication tokens](https://gitlab.com/gitlab-org/gitlab/-/issues/383198). |
+| GitLab Rails app | | Implement UI to create new runner. |
+| GitLab Rails app | | GraphQL changes to `CiRunner` type. |
+| GitLab Rails app | | UI changes to runner details view (listing of platform, architecture, IP address, etc.) (?) |
+
+### Stage 5 - Optional disabling of registration token
+
+| Component | Milestone | Changes |
+|------------------|----------:|---------|
+| GitLab Rails app | | Add UI to allow disabling use of registration tokens at project or group level. |
+| GitLab Rails app | `16.0` | Introduce `:disable_runner_registration_tokens` feature flag (enabled by default) to control whether use of registration tokens is allowed. |
+| GitLab Rails app | | Make [`POST /api/v4/runners` endpoint](../../../api/runners.md#register-a-new-runner) permanently return `HTTP 410 Gone` if either `allow_runner_registration_token` setting or `:disable_runner_registration_tokens` feature flag disables registration tokens.<br/>A future v5 version of the API should return `HTTP 404 Not Found`. |
+| GitLab Rails app | | Start refusing job requests that don't include a unique ID, if either `allow_runner_registration_token` setting or `:disable_runner_registration_tokens` feature flag disables registration tokens. |
+| GitLab Rails app | | Hide legacy UI showing registration with a registration token, if `:disable_runner_registration_tokens` feature flag disables registration tokens. |
+
+### Stage 6 - Removals
+
| Component | Milestone | Changes |
-|------------------|-----------|---------|
-| GitLab Rails app | `15.x` (latest at `15.6`) | Deprecate `POST /api/v4/runners` endpoint for `16.0`. This hinges on a [proposal](https://gitlab.com/gitlab-org/gitlab/-/issues/373774) to allow deprecating REST API endpoints for security reasons. |
-| GitLab Runner | `15.x` (latest at `15.8`) | Add deprecation notice for `register` command for `16.0`. |
-| GitLab Runner | `15.x` | Ensure all runner entries in `config.toml` have unique system identifier values assigned. Log new system ID values with `INFO` level as they get created. |
-| GitLab Runner | `15.x` | Start additionally logging unique system ID anywhere we log the runner short SHA. |
-| GitLab Rails app | `15.x` | Create database migrations to add settings from `application_settings` and `namaspace_settings` tables. |
-| GitLab Runner | `15.x` | Start sending `unique_id` value in `POST /jobs/request` request and other follow-up requests that require identifying the unique system. |
-| GitLab Runner | `15.x` | Implement new user-authenticated API (REST and GraphQL) to create a new runner. |
-| GitLab Rails app | `15.x` | Implement UI to create new runner. |
-| GitLab Runner | `16.0` | Remove `register` command and support for `POST /runners` endpoint. |
-| GitLab Rails app | `16.0` | Remove legacy UI showing registration with a registration token. |
-| GitLab Rails app | `16.0` | Create database migrations to remove settings from `application_settings` and `namaspace_settings` tables. |
-| GitLab Rails app | `16.0` | Make [`POST /api/v4/runners` endpoint](../../../api/runners.md#register-a-new-runner-deprecated) permanently return `410 Gone`. A future v5 version of the API would return `404 Not Found`. |
-| GitLab Rails app | `16.0` | Start refusing job requests that don't include a unique ID. |
+|------------------|----------:|---------|
+| GitLab Rails app | `17.0` | Remove legacy UI showing registration with a registration token. |
+| GitLab Runner | `17.0` | Remove runner model arguments from `register` command (for example `--run-untagged`, `--tag-list`, etc.) |
+| GitLab Rails app | `17.0` | Create database migrations to drop `allow_runner_registration_token` setting columns from `application_settings` and `namespace_settings` tables. |
+| GitLab Rails app | `17.0` | Create database migrations to drop:<br/>- `runners_registration_token`/`runners_registration_token_encrypted` columns from `application_settings`;<br/>- `runners_token`/`runners_token_encrypted` from `namespaces` table;<br/>- `runners_token`/`runners_token_encrypted` from `projects` table. |
+| GitLab Rails app | `17.0` | Remove `:disable_runner_registration_tokens` feature flag. |
## Status
diff --git a/doc/architecture/blueprints/work_items/index.md b/doc/architecture/blueprints/work_items/index.md
index 75a9d8d76ad..101fdbf4c2d 100644
--- a/doc/architecture/blueprints/work_items/index.md
+++ b/doc/architecture/blueprints/work_items/index.md
@@ -60,8 +60,8 @@ All Work Item types share the same pool of predefined widgets and are customized
| assignees | |
| description | |
| hierarchy | |
-| [iteration](https://gitlab.com/gitlab-org/gitlab/-/issues/367456) | work_items_mvc_2 |
-| [milestone](https://gitlab.com/gitlab-org/gitlab/-/issues/367463) | work_items_mvc_2 |
+| [iteration](https://gitlab.com/gitlab-org/gitlab/-/issues/367456) | |
+| [milestone](https://gitlab.com/gitlab-org/gitlab/-/issues/367463) | |
| labels | |
| start and due date | |
| status\* | |
diff --git a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
index a0665e1c054..ceb56b01dcd 100644
--- a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
+++ b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
@@ -122,7 +122,7 @@ To use GitLab CI/CD with a Bitbucket Cloud repository:
1. In Bitbucket, create a `.gitlab-ci.yml` file to use the script to push
pipeline success and failures to Bitbucket. Similar to the script added above,
- this file is copied to the GitLab repo as part of the mirroring process.
+ this file is copied to the GitLab repository as part of the mirroring process.
```yaml
stages:
diff --git a/doc/ci/cloud_services/azure/index.md b/doc/ci/cloud_services/azure/index.md
index b2f78648be9..b846ee4b792 100644
--- a/doc/ci/cloud_services/azure/index.md
+++ b/doc/ci/cloud_services/azure/index.md
@@ -17,7 +17,7 @@ Prerequisites:
- Access to an existing Azure Subscription with `Owner` access level.
- Access to the corresponding Azure Active Directory Tenant with at least the `Application Developer` access level.
- A local installation of the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli).
- Alternatively, you can follow all the steps below with the [Azure Cloud Shell](https://shell.azure.com/).
+ Alternatively, you can follow all the steps below with the [Azure Cloud Shell](https://portal.azure.com/#cloudshell/).
- A GitLab project.
To complete this tutorial:
diff --git a/doc/ci/directed_acyclic_graph/index.md b/doc/ci/directed_acyclic_graph/index.md
index 39f45471021..49bce75e183 100644
--- a/doc/ci/directed_acyclic_graph/index.md
+++ b/doc/ci/directed_acyclic_graph/index.md
@@ -10,7 +10,7 @@ type: reference
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/47063) in GitLab 12.2.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/206902) in GitLab 12.10.
-A [directed acyclic graph](https://www.techopedia.com/definition/5739/directed-acyclic-graph-dag) can be
+A [directed acyclic graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph) can be
used in the context of a CI/CD pipeline to build relationships between jobs such that
execution is performed in the quickest possible manner, regardless how stages may
be set up.
@@ -38,10 +38,10 @@ It has a pipeline that looks like the following:
| build | test | deploy |
| ----- | ---- | ------ |
-| build_a | test_a | deploy_a |
-| build_b | test_b | deploy_b |
-| build_c | test_c | deploy_c |
-| build_d | test_d | deploy_d |
+| `build_a` | `test_a` | `deploy_a` |
+| `build_b` | `test_b` | `deploy_b` |
+| `build_c` | `test_c` | `deploy_c` |
+| `build_d` | `test_d` | `deploy_d` |
Using a DAG, you can relate the `_a` jobs to each other separately from the `_b` jobs,
and even if service `a` takes a very long time to build, service `b` doesn't
diff --git a/doc/ci/environments/deployment_approvals.md b/doc/ci/environments/deployment_approvals.md
index d7fa31b583b..a4815a85bc1 100644
--- a/doc/ci/environments/deployment_approvals.md
+++ b/doc/ci/environments/deployment_approvals.md
@@ -134,6 +134,8 @@ To approve or reject a deployment to a protected environment using the UI:
1. On the left sidebar, select **Deployments > Environments**.
1. Select the environment's name.
1. In the deployment's row, select **Approval options** (**{thumb-up}**).
+ Before approving or rejecting the deployment, you can view the number of approvals granted and
+ remaining, also who has approved or rejected it.
1. Optional. Add a comment which describes your reason for approving or rejecting the deployment.
1. Select **Approve** or **Reject**.
@@ -154,6 +156,29 @@ curl --data "status=approved&comment=Looks good to me" \
--header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/deployments/1/approval"
```
+### View the approval details of a deployment
+
+Prerequisites:
+
+- Permission to deploy to the protected environment.
+
+A deployment to a protected environment can only proceed after all required approvals have been
+granted.
+
+To view the approval details of a deployment:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Deployments > Environments**.
+1. Select the environment's name.
+1. In the deployment's row, select **Approval options** (**{thumb-up}**).
+
+The approval status details are shown:
+
+- Eligible approvers
+- Number of approvals granted, and number of approvals required
+- Users who have granted approval
+- History of approvals or rejections
+
## How to see blocked deployments
### Using the UI
diff --git a/doc/ci/environments/external_deployment_tools.md b/doc/ci/environments/external_deployment_tools.md
new file mode 100644
index 00000000000..ff3172f0e02
--- /dev/null
+++ b/doc/ci/environments/external_deployment_tools.md
@@ -0,0 +1,88 @@
+---
+stage: Release
+group: Release
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+type: reference
+---
+
+# Track deployments of an external deployment tool **(FREE)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22513) in GitLab 12.5.
+
+While GitLab offers a [built-in deployment solution](index.md), you might prefer to use an external deployment tool, such as Heroku or ArgoCD.
+GitLab can receive deployment events from these external tools and allows you to track the deployments within GitLab.
+For example, the following features are available by setting up tracking:
+
+- [See when an merge request has been deployed, and to which environment](../../user/project/merge_requests/widgets.md#post-merge-pipeline-status).
+- [Filter merge requests by environment or deployment date](../../user/project/merge_requests/index.md#filter-merge-requests-by-environment-or-deployment-date).
+- [DevOps Research and Assessment (DORA) metrics](../../user/analytics/dora_metrics.md).
+- [View environments and deployments](index.md#view-environments-and-deployments).
+- [Track newly included merge requests per deployment](index.md#track-newly-included-merge-requests-per-deployment).
+
+NOTE:
+Some of the features are not available because GitLab can't authorize and leverage those external deployments, including
+[Protected Environments](protected_environments.md), [Deployment Approvals](deployment_approvals.md), [Deployment safety](deployment_safety.md), and [Environment rollback](index.md#environment-rollback).
+
+## How to set up deployment tracking
+
+External deployment tools usually offer a [webhook](https://en.wikipedia.org/wiki/Webhook) to execute an additional API request when deployment state is changed.
+You can configure your tool to make a request to the GitLab [Deployment API](../../api/deployments.md). Here is an overview of the event and API request flow:
+
+- When a deployment starts running, [create a deployment with `running` status](../../api/deployments.md#create-a-deployment).
+- When a deployment succeeds, [update the deployment status to `success`](../../api/deployments.md#update-a-deployment).
+- When a deployment fails, [update the deployment status to `failed`](../../api/deployments.md#update-a-deployment).
+
+NOTE:
+You can create a [project access token](../../user/project/settings/project_access_tokens.md) for the GitLab API authentication.
+
+### Example: Track deployments of ArgoCD
+
+You can use [ArgoCD webhook](https://argocd-notifications.readthedocs.io/en/stable/services/webhook/) to send deployment events to GitLab Deployment API.
+Here is an example setup that creates a `success` deployment record in GitLab when ArgoCD successfully deploys a new revision:
+
+1. Create a new webhook. You can save the following manifest file and apply it by `kubectl apply -n argocd -f <manifiest-file-path>`:
+
+ ```yaml
+ apiVersion: v1
+ kind: ConfigMap
+ metadata:
+ name: argocd-notifications-cm
+ data:
+ trigger.on-deployed: |
+ - description: Application is synced and healthy. Triggered once per commit.
+ oncePer: app.status.sync.revision
+ send:
+ - gitlab-deployment-status
+ when: app.status.operationState.phase in ['Succeeded'] and app.status.health.status == 'Healthy'
+ template.gitlab-deployment-status: |
+ webhook:
+ gitlab:
+ method: POST
+ path: /projects/<your-project-id>/deployments
+ body: |
+ {
+ "status": "success",
+ "environment": "production",
+ "sha": "{{.app.status.operationState.operation.sync.revision}}",
+ "ref": "main",
+ "tag": "false"
+ }
+ service.webhook.gitlab: |
+ url: https://gitlab.com/api/v4
+ headers:
+ - name: PRIVATE-TOKEN
+ value: <your-access-token>
+ - name: Content-type
+ value: application/json
+ ```
+
+1. Create a new subscription in your application:
+
+ ```shell
+ kubectl patch app <your-app-name> -n argocd -p '{"metadata": {"annotations": {"notifications.argoproj.io/subscribe.on-deployed.gitlab":""}}}' --type merge
+ ```
+
+NOTE:
+If a deployment wasn't created as expected, you can troubleshoot with [`argocd-notifications` tool](https://argocd-notifications.readthedocs.io/en/stable/troubleshooting/).
+For example, `argocd-notifications template notify gitlab-deployment-status <your-app-name> --recipient gitlab:argocd-notifications`
+triggers API request immediately and renders an error message from GitLab API server if any.
diff --git a/doc/ci/environments/index.md b/doc/ci/environments/index.md
index c4672b9dc7e..0c412c85e47 100644
--- a/doc/ci/environments/index.md
+++ b/doc/ci/environments/index.md
@@ -45,15 +45,20 @@ Deployments show up in this list only after a deployment job has created them.
## Search environments
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10754) in GitLab 15.5.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10754) in GitLab 15.5.
+> - [Searching environments within a folder](https://gitlab.com/gitlab-org/gitlab/-/issues/373850) was introduced in GitLab 15.7 with [Feature flag `enable_environments_search_within_folder`](https://gitlab.com/gitlab-org/gitlab/-/issues/382108). Enabled by default.
To search environments by name:
1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Environments**.
-1. In the search bar, enter your search term. Matching applies from the
- beginning of the environment name. For example, `devel` matches the
- environment name `development`, but `elop` does not.
+1. In the search bar, enter your search term.
+ - The length of your **search term should be 3 or more characters**.
+ - Matching applies from the beginning of the environment name.
+ - For example, `devel` matches the environment name `development`, but `elop` does not.
+ - For environments with a folder name format, matching applies after the base folder name.
+ - For example when the name is `review/test-app`, search term `test` matches `review/test-app`.
+ - Also searching with the folder name prefixed like `review/test` matches `review/test-app`.
## Types of environments
@@ -340,7 +345,7 @@ and displayed at a post-merge pipeline in [merge request pages](../../user/proje
To activate this tracking, your environment must be configured in the following:
-- [Environment name](../yaml/index.md#environmentname) is not foldered with `/` (that is, top-level/long-lived environments), _OR_
+- [Environment name](../yaml/index.md#environmentname) is not using folders with `/` (that is, top-level/long-lived environments), _OR_
- [Environment tier](#deployment-tier-of-environments) is either `production` or `staging`.
Here are the example setups of [`environment` keyword](../yaml/index.md#environment) in `.gitlab-ci.yml`:
@@ -609,6 +614,20 @@ Because `stop_review_app` is set to `auto_stop_in: 1 week`,
if a merge request is inactive for more than a week,
GitLab automatically triggers the `stop_review_app` job to stop the environment.
+#### Stop an environment through the UI
+
+NOTE:
+To trigger an `on_stop` action and manually stop an environment from the
+Environments view, the stop and deploy jobs must be in the same
+[`resource_group`](../yaml/index.md#resource_group).
+
+To stop an environment in the GitLab UI:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Deployments > Environments**.
+1. Next to the environment you want to stop, select **Stop**.
+1. On the confirmation dialog box, select **Stop environment**.
+
#### Multiple stop actions for an environment
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22456) in GitLab 14.10 [with a flag](../../administration/feature_flags.md) named `environment_multiple_stop_actions`. Disabled by default.
@@ -977,6 +996,7 @@ Instead, you need to delete the old environment and create a new one:
- [Environments Dashboard](../environments/environments_dashboard.md): View a summary of each
environment's operational health. **(PREMIUM)**
- [Deployment safety](deployment_safety.md#restrict-write-access-to-a-critical-environment): Secure your deployments.
+- [Track deployments of an external deployment tool](external_deployment_tools.md): Use an external deployment tool instead of built-in deployment solution.
## Troubleshooting
diff --git a/doc/ci/environments/protected_environments.md b/doc/ci/environments/protected_environments.md
index 5c120da32a0..638bcf77967 100644
--- a/doc/ci/environments/protected_environments.md
+++ b/doc/ci/environments/protected_environments.md
@@ -132,13 +132,13 @@ they have the following privileges:
Users granted access to a protected environment, but not push or merge access
to the branch deployed to it, are only granted access to deploy the environment.
-[Invited groups](../../user/project/members/share_project_with_groups.md#share-a-project-with-a-group-of-users) added
+[Invited groups](../../user/project/members/share_project_with_groups.md#share-a-project-with-a-group) added
to the project with [Reporter role](../../user/permissions.md#project-members-permissions), appear in the dropdown list for deployment-only access.
To add deployment-only access:
1. Create a group with members who are granted to access to the protected environment, if it doesn't exist yet.
-1. [Invite the group](../../user/project/members/share_project_with_groups.md#share-a-project-with-a-group-of-users) to the project with the Reporter role.
+1. [Invite the group](../../user/project/members/share_project_with_groups.md#share-a-project-with-a-group) to the project with the Reporter role.
1. Follow the steps in [Protecting Environments](#protecting-environments).
## Modifying and unprotecting environments
@@ -200,7 +200,7 @@ To maximize the effectiveness of group-level protected environments,
[group-level memberships](../../user/group/index.md) must be correctly
configured:
-- Operators should be given at least the Owner role
+- Operators should be given the Owner role
for the top-level group. They can maintain CI/CD configurations for
the higher environments (such as production) in the group-level settings page,
which includes group-level protected environments,
diff --git a/doc/ci/examples/authenticating-with-hashicorp-vault/index.md b/doc/ci/examples/authenticating-with-hashicorp-vault/index.md
index 7208caaccae..125ae3650c9 100644
--- a/doc/ci/examples/authenticating-with-hashicorp-vault/index.md
+++ b/doc/ci/examples/authenticating-with-hashicorp-vault/index.md
@@ -90,7 +90,7 @@ The JWT is encoded by using RS256 and signed with a dedicated private key. The e
You can use this JWT and your instance's JWKS endpoint (`https://gitlab.example.com/-/jwks`) to authenticate with a Vault server that is configured to allow the JWT Authentication method for authentication.
-When configuring roles in Vault, you can use [bound_claims](https://developer.hashicorp.com/vault/docs/auth/jwt#bound-claims) to match against the JWT's claims and restrict which secrets each CI job has access to.
+When configuring roles in Vault, you can use [bound claims](https://developer.hashicorp.com/vault/docs/auth/jwt#bound-claims) to match against the JWT claims and restrict which secrets each CI/CD job has access to.
To communicate with Vault, you can use either its CLI client or perform API requests (using `curl` or another client).
@@ -180,10 +180,35 @@ $ vault write auth/jwt/role/myproject-production - <<EOF
EOF
```
-This example uses [bound_claims](https://developer.hashicorp.com/vault/api-docs/auth/jwt#bound_claims) to specify that only a JWT with matching values for the specified claims is allowed to authenticate.
+This example uses [bound claims](https://developer.hashicorp.com/vault/api-docs/auth/jwt#bound_claims) to specify that only a JWT with matching values for the specified claims is allowed to authenticate.
Combined with [protected branches](../../../user/project/protected_branches.md), you can restrict who is able to authenticate and read the secrets.
+To use the same policy for a list of projects, use `namespace_id`:
+
+```json
+"bound_claims": {
+ "namespace_id": ["12", "22", "37"]
+}
+```
+
+Any of the claims [included in the JWT](#how-it-works) can be matched against a list of values
+in the bound claims. For example:
+
+```json
+"bound_claims": {
+ "user_login": ["alice", "bob", "mallory"]
+}
+
+"bound_claims": {
+ "ref": ["main", "develop", "test"]
+}
+
+"bound_claims": {
+ "project_id": ["12", "22", "37"]
+}
+```
+
[`token_explicit_max_ttl`](https://developer.hashicorp.com/vault/api-docs/auth/jwt#token_explicit_max_ttl) specifies that the token issued by Vault, upon successful authentication, has a hard lifetime limit of 60 seconds.
[`user_claim`](https://developer.hashicorp.com/vault/api-docs/auth/jwt#user_claim) specifies the name for the Identity alias created by Vault upon a successful login.
@@ -225,7 +250,7 @@ $ vault write auth/jwt/config \
bound_issuer="gitlab.example.com"
```
-[bound_issuer](https://developer.hashicorp.com/vault/api-docs/auth/jwt#bound_issuer) specifies that only a JWT with the issuer (that is, the `iss` claim) set to `gitlab.example.com` can use this method to authenticate, and that the JWKS endpoint (`https://gitlab.example.com/-/jwks`) should be used to validate the token.
+[`bound_issuer`](https://developer.hashicorp.com/vault/api-docs/auth/jwt#bound_issuer) specifies that only a JWT with the issuer (that is, the `iss` claim) set to `gitlab.example.com` can use this method to authenticate, and that the JWKS endpoint (`https://gitlab.example.com/-/jwks`) should be used to validate the token.
For the full list of available configuration options, see Vault's [API documentation](https://developer.hashicorp.com/vault/api-docs/auth/jwt#configure).
@@ -256,7 +281,7 @@ NOTE:
If you're using a Vault instance provided by HashiCorp Cloud Platform,
you need to export the `VAULT_NAMESPACE` variable. Its default value is `admin`.
-![read_secrets staging](img/vault-read-secrets-staging.png)
+![read secrets staging example](img/vault-read-secrets-staging.png)
The following job is able to authenticate using the `myproject-production` role and read secrets under `/secret/myproject/production/`:
@@ -279,14 +304,14 @@ read_secrets:
- echo $PASSWORD
```
-![read_secrets production](img/vault-read-secrets-production.png)
+![read secrets production example](img/vault-read-secrets-production.png)
### Limit token access to Vault secrets
You can control `CI_JOB_JWT` access to Vault secrets by using Vault protections
and GitLab features. For example, restrict the token by:
-- Using Vault [bound_claims](https://developer.hashicorp.com/vault/docs/auth/jwt#bound-claims)
+- Using Vault [bound claims](https://developer.hashicorp.com/vault/docs/auth/jwt#bound-claims)
for specific groups using `group_claim`.
- Hard coding values for Vault bound claims based on the `user_login` and `user_email`
of specific users.
diff --git a/doc/ci/examples/deployment/composer-npm-deploy.md b/doc/ci/examples/deployment/composer-npm-deploy.md
index a603207aef7..533b6519d9a 100644
--- a/doc/ci/examples/deployment/composer-npm-deploy.md
+++ b/doc/ci/examples/deployment/composer-npm-deploy.md
@@ -120,7 +120,7 @@ Therefore, for a production environment we use additional steps to ensure that a
## Where to go next
-Since this was a WordPress project, I gave real life code snippets. Some further ideas you can pursue:
+Since this was a WordPress project, it includes real code snippets. Some further ideas you can pursue:
- Having a slightly different script for the default branch allows you to deploy to a production server from that branch and to a stage server from any other branches.
- Instead of pushing it live, you can push it to WordPress official repository.
diff --git a/doc/ci/examples/index.md b/doc/ci/examples/index.md
index 361061d0d75..c8ad653e41f 100644
--- a/doc/ci/examples/index.md
+++ b/doc/ci/examples/index.md
@@ -33,7 +33,7 @@ The following table lists examples with step-by-step tutorials that are containe
| npm with semantic-release | [Publish npm packages to the GitLab Package Registry using semantic-release](semantic-release.md). |
| PHP with Laravel, Envoy | [Test and deploy Laravel applications with GitLab CI/CD and Envoy](laravel_with_gitlab_and_envoy/index.md). |
| PHP with npm, SCP | [Running Composer and npm scripts with deployment via SCP in GitLab CI/CD](deployment/composer-npm-deploy.md). |
-| PHP with PHPunit, `atoum` | [Testing PHP projects](php.md). |
+| PHP with PHPUnit, `atoum` | [Testing PHP projects](php.md). |
| Secrets management with Vault | [Authenticating and Reading Secrets With HashiCorp Vault](authenticating-with-hashicorp-vault/index.md). |
### Contributed examples
diff --git a/doc/ci/examples/php.md b/doc/ci/examples/php.md
index 2d0c6382dd8..a83bcf69491 100644
--- a/doc/ci/examples/php.md
+++ b/doc/ci/examples/php.md
@@ -246,7 +246,7 @@ the [SSH keys](../ssh_keys/index.md) to be able to clone it.
## Use databases or other services
Most of the time, you need a running database for your tests to be able to
-run. If you're using the Docker executor, you can leverage Docker's ability to
+run. If you're using the Docker executor, you can leverage Docker to
link to other containers. With GitLab Runner, this can be achieved by defining
a `service`.
diff --git a/doc/ci/examples/semantic-release.md b/doc/ci/examples/semantic-release.md
index 8f0321517ab..1fa526e787a 100644
--- a/doc/ci/examples/semantic-release.md
+++ b/doc/ci/examples/semantic-release.md
@@ -13,7 +13,7 @@ You can also view or fork the complete [example source](https://gitlab.com/gitla
## Initialize the module
1. Open a terminal and navigate to the project's repository.
-1. Run `npm init`. Name the module according to [the Package Registry's naming conventions](../../user/packages/npm_registry/index.md#package-naming-convention). For example, if the project's path is `gitlab-examples/semantic-release-npm`, name the module `@gitlab-examples/semantic-release-npm`.
+1. Run `npm init`. Name the module according to [the Package Registry's naming conventions](../../user/packages/npm_registry/index.md#naming-convention). For example, if the project's path is `gitlab-examples/semantic-release-npm`, name the module `@gitlab-examples/semantic-release-npm`.
1. Install the following npm packages:
@@ -89,6 +89,7 @@ The default `before_script` generates a temporary `.npmrc` that is used to authe
As part of publishing a package, semantic-release increases the version number in `package.json`. For semantic-release to commit this change and push it back to GitLab, the pipeline requires a custom CI/CD variable named `GITLAB_TOKEN`. To create this variable:
<!-- markdownlint-disable MD044 -->
+
1. On the top bar, on the top right, select your avatar.
1. On the left sidebar, select **Access Tokens**.
1. In the **Token name** box, enter a token name.
diff --git a/doc/ci/git_submodules.md b/doc/ci/git_submodules.md
index 68cbff21fae..0f206b3fceb 100644
--- a/doc/ci/git_submodules.md
+++ b/doc/ci/git_submodules.md
@@ -68,7 +68,7 @@ To make submodules work correctly in CI/CD jobs:
```
1. You can filter or exclude specific submodules to control which submodules will be synced using
- [`GIT_SUBMODULE_PATHS`](runners/configure_runners.md#git-submodule-paths).
+ [`GIT_SUBMODULE_PATHS`](runners/configure_runners.md#sync-or-exclude-specific-submodules-from-ci-jobs).
```yaml
variables:
@@ -83,14 +83,6 @@ To make submodules work correctly in CI/CD jobs:
GIT_SUBMODULE_STRATEGY: recursive
GIT_SUBMODULE_UPDATE_FLAGS: --jobs 4
```
-
-1. You can set the [GIT_SUBMODULE_PATHS](runners/configure_runners.md#sync-or-exclude-specific-submodules-from-ci-jobs) to explicitly ignore submodules during cloning:
-
- ```yaml
- variables:
- GIT_SUBMODULE_STRATEGY: recursive
- GIT_SUBMODULE_PATHS: ':(exclude)submodule'
- ```
If you use the [`CI_JOB_TOKEN`](jobs/ci_job_token.md) to clone a submodule in a
pipeline job, the user executing the job must be assigned to a role that has
diff --git a/doc/ci/jobs/ci_job_token.md b/doc/ci/jobs/ci_job_token.md
index 1e7b389c84a..d95451a67dc 100644
--- a/doc/ci/jobs/ci_job_token.md
+++ b/doc/ci/jobs/ci_job_token.md
@@ -54,7 +54,7 @@ To make sure that this token doesn't leak, GitLab:
To make sure that this token doesn't leak, you should also configure
your [runners](../runners/index.md) to be secure. Avoid:
-- Using Docker's `privileged` mode if the machines are re-used.
+- Using Docker `privileged` mode if the machines are re-used.
- Using the [`shell` executor](https://docs.gitlab.com/runner/executors/shell.html) when jobs
run on the same machine.
diff --git a/doc/ci/jobs/index.md b/doc/ci/jobs/index.md
index 4eb8952dd73..15ec92a896e 100644
--- a/doc/ci/jobs/index.md
+++ b/doc/ci/jobs/index.md
@@ -110,6 +110,7 @@ You can't use these keywords as job names:
- `true`
- `false`
- `nil`
+- `pages:deploy` configured for a `deploy` stage
Job names must be 255 characters or fewer.
diff --git a/doc/ci/large_repositories/index.md b/doc/ci/large_repositories/index.md
index c7ecc25dc44..5a553228ba1 100644
--- a/doc/ci/large_repositories/index.md
+++ b/doc/ci/large_repositories/index.md
@@ -196,18 +196,14 @@ Our pipeline is most performant if we use the following `.gitlab-ci.yml`:
```yaml
variables:
- GIT_DEPTH: 10
GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_NAME
build:
script: ls -al
```
-The above configures a:
-
-- Shallow clone of 10, to speed up subsequent `git fetch` commands.
-- Custom clone path to make it possible to re-use worktrees between parent project and all forks
- because we use the same clone path for all forks.
+This YAML setting configures a custom clone path. This path makes it possible to re-use worktrees
+between the parent project and forks because we use the same clone path for all forks.
Why use `$CI_CONCURRENT_ID`? The main reason is to ensure that worktrees used are not conflicting
between projects. The `$CI_CONCURRENT_ID` represents a unique identifier within the given executor.
@@ -264,4 +260,4 @@ For very active repositories with a large number of references and files, you ca
seeding the repository data also helps avoid
`429 Too many requests` errors from Cloudflare.
This error can occur if you have many runners behind a single,
- NAT'ed IP address that pulls from GitLab.com.
+ IP address using NAT, that pulls from GitLab.com.
diff --git a/doc/ci/migration/jenkins.md b/doc/ci/migration/jenkins.md
index 4ba59e14811..235dd0e80ca 100644
--- a/doc/ci/migration/jenkins.md
+++ b/doc/ci/migration/jenkins.md
@@ -8,9 +8,9 @@ type: index, howto
# Migrating from Jenkins **(FREE)**
-A lot of GitLab users have successfully migrated to GitLab CI/CD from Jenkins. To make this
-easier if you're just getting started, we've collected several resources here that you might find useful
-before diving in. Think of this page as a "GitLab CI/CD for Jenkins Users" guide.
+A lot of GitLab users have successfully migrated to GitLab CI/CD from Jenkins.
+We've collected several resources here that you might find informative if you're just getting started.
+Think of this page as a "GitLab CI/CD for Jenkins Users" guide.
The following list of recommended steps was created after observing organizations
that were able to quickly complete this migration:
@@ -29,9 +29,10 @@ that were able to quickly complete this migration:
1. Check the [pipeline efficiency documentation](../pipelines/pipeline_efficiency.md)
to learn how to make your GitLab CI/CD pipelines faster and more efficient.
-For an example of how to convert a Jenkins pipeline into a GitLab CI/CD pipeline,
-or how to use Auto DevOps to test your code automatically, watch the
-[Migrating from Jenkins to GitLab](https://www.youtube.com/watch?v=RlEVGOpYF5Y) video.
+Watch the [Migrating from Jenkins to GitLab](https://www.youtube.com/watch?v=RlEVGOpYF5Y) video for examples of how to:
+
+- Convert a Jenkins pipeline into a GitLab CI/CD pipeline.
+- Use Auto DevOps to test your code automatically.
Otherwise, read on for important information that helps you get the ball rolling. Welcome
to GitLab!
@@ -42,8 +43,8 @@ can be a great resource.
## Manage organizational transition
An important part of transitioning from Jenkins to GitLab is the cultural and organizational
-changes that come with the move, and successfully managing them. There are a few
-things we have found that help this:
+changes that come with the move, and successfully managing them. A few
+things we have found that help this are:
- Setting and communicating a clear vision of what your migration goals are helps
your users understand why the effort is worth it. The value is clear when
@@ -67,11 +68,11 @@ of transition, by letting you delay the migration of less urgent pipelines for a
If you are interested in helping GitLab test the wrapper, join our [public testing issue](https://gitlab.com/gitlab-org/gitlab/-/issues/215675) for instructions and to provide your feedback.
NOTE:
-If you have a paid GitLab subscription, note that the JenkinsFile Wrapper is not packaged as part of GitLab, and falls outside of the scope of support. For more information, see the [Statement of Support](https://about.gitlab.com/support/statement-of-support/).
+If you have a paid GitLab subscription, the JenkinsFile Wrapper is not packaged with GitLab and falls outside of the scope of support. For more information, see the [Statement of Support](https://about.gitlab.com/support/statement-of-support/).
## Important product differences
-There are some high level differences between the products worth mentioning:
+Some high level differences between the products worth mentioning are:
- With GitLab you don't need a root `pipeline` keyword to wrap everything.
- The way pipelines are triggered and [trigger other pipelines](../yaml/index.md#trigger)
@@ -93,14 +94,13 @@ There are some high level differences between the products worth mentioning:
contain scripts or other reusable code.
- You can also use the [`extends` keyword](../yaml/index.md#extends) to reuse configuration
in a single pipeline configuration.
-- All jobs in a single stage always run in parallel, and all stages run in sequence. We are planning
- to allow certain jobs to break this sequencing as needed with our [directed acyclic graph](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/47063)
+- All jobs in a single stage always run in parallel, and all stages run in sequence.
+ Certain jobs might break this sequencing as needed with our [directed acyclic graph](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/47063)
feature.
- The [`parallel`](../yaml/index.md#parallel) keyword can automatically parallelize tasks,
like tests that support parallelization.
- Normally all jobs in a single stage run in parallel, and all stages run in sequence.
- There are different [pipeline architectures](../pipelines/pipeline_architectures.md)
- that allow you to change this behavior.
+ Different [pipeline architectures](../pipelines/pipeline_architectures.md) allow you to change this behavior.
- The new [`rules` syntax](../yaml/index.md#rules) is the recommended method of
controlling when different jobs run. It is more powerful than the `only/except` syntax.
- One important difference is that jobs run independently of each other and have a
@@ -114,10 +114,10 @@ There are some high level differences between the products worth mentioning:
- Manual approvals or gates can be set up as [`when:manual` jobs](../jobs/job_control.md#create-a-job-that-must-be-run-manually). These can
also leverage [`protected environments`](../jobs/job_control.md#run-a-job-after-a-delay)
to control who is able to approve them.
-- GitLab comes with a [container registry](../../user/packages/container_registry/index.md), and we recommend using
+- GitLab comes with a [container registry](../../user/packages/container_registry/index.md), so you can use
container images to set up your build environment. For example, set up one pipeline that builds your build environment
itself and publish that to the container registry. Then, have your pipelines use this instead of each building their
- own environment, which is slower and may be less consistent. We have extensive docs on [how to use the Container Registry](../../user/packages/container_registry/index.md).
+ own environment, which is slower and may be less consistent. We have extensive documentation on [how to use the Container Registry](../../user/packages/container_registry/index.md).
- A central utilities repository can be a great place to put assorted scheduled jobs
or other manual jobs that function like utilities. Jenkins installations tend to
have a few of these.
@@ -129,7 +129,7 @@ Jenkins agent, uninstall it and then [install and register the runner](../runner
Runners do not require much overhead, so you can size them similarly to the Jenkins
agents you were using.
-There are some important differences in the way runners work in comparison to agents:
+Some important differences in the way runners work in comparison to agents are:
- Runners can be set up as [shared across an instance, be added at the group level, or set up at the project level](../runners/runners_scope.md).
They self-select jobs from the scopes you've defined automatically.
@@ -137,8 +137,8 @@ There are some important differences in the way runners work in comparison to ag
associate runners with specific jobs. For example, you can use a tag for jobs that
require dedicated, more powerful, or specific hardware.
- GitLab has [autoscaling for runners](https://docs.gitlab.com/runner/configuration/autoscale.html).
- Use autoscaling to provision runners only when needed, and scale down when not needed.
- This is similar to ephemeral agents in Jenkins.
+ Use autoscaling to provision runners only when needed and scale down when not needed,
+ similar to ephemeral agents in Jenkins.
If you are using `gitlab.com`, you can take advantage of our [shared runner fleet](../runners/index.md)
to run jobs without provisioning your own runners. We are investigating making them
@@ -148,14 +148,14 @@ as well.
## Groovy vs. YAML
Jenkins Pipelines are based on [Groovy](https://groovy-lang.org/), so the pipeline specification is written as code.
-GitLab works a bit differently, we use the more highly structured [YAML](https://yaml.org/) format, which
-places scripting elements inside of `script` blocks separate from the pipeline specification itself.
+GitLab works a bit differently, using the more highly structured [YAML](https://yaml.org/) format.
+The scripting elements are in `script` blocks separate from the pipeline specification itself.
-This is a strength of GitLab, in that it helps keep the learning curve much simpler to get up and running
-and avoids some of the problem of unconstrained complexity which can make your Jenkinsfile hard to understand
+Using YAML is a strength of GitLab, in that it helps keep the learning curve much simpler to get up and running.
+It also avoids some of the problem of unconstrained complexity which can make your Jenkinsfile hard to understand
and manage.
-That said, we do of course still value DRY (don't repeat yourself) principles and want to ensure that
+We do of course still value DRY (don't repeat yourself) principles. We want to ensure that
behaviors of your jobs can be codified once and applied as needed. You can use the `extends` syntax to
[reuse configuration in your jobs](../yaml/index.md#extends), and `include` can
be used to [reuse pipeline configurations](../yaml/index.md#include) in pipelines
@@ -197,13 +197,13 @@ can leverage. You can see the complete list of packaging features in the
## Integrated features
-Where you may have used plugins to get things like code quality, unit tests, and security scanning working in Jenkins,
+You may have used plugins to get things like code quality, unit tests, and security scanning working in Jenkins.
GitLab takes advantage of our connected ecosystem to automatically pull these kinds of results into
your merge requests, pipeline details pages, and other locations. You may find that you actually don't
need to configure anything to have these appear.
-If they aren't working as expected, or if you'd like to see what's available, our [CI/CD feature index](../index.md#features) has the full list
-of bundled features and links to the documentation for each.
+Our [CI/CD feature index](../index.md#features) has the full list of bundled features and links to the documentation for each.
+Refer to this index if these features aren't working as expected, or if you'd like to see what's available.
### Templates
@@ -213,7 +213,7 @@ as well as encourage inner sourcing.
In self-managed GitLab instances, you can build an [Instance Template Repository](../../user/admin_area/settings/instance_template_repository.md).
Development teams across the whole organization can select templates from a dropdown list.
A group maintainer or a group owner is able to set a group to use as the source for the
-[custom project templates](../../user/admin_area/custom_project_templates.md), which can
+[custom project templates](../../user/admin_area/custom_project_templates.md). This can
be used by all projects in the group. An instance administrator can set a group as
the source for [instance project templates](../../user/group/custom_project_templates.md),
which can be used by projects in that instance.
@@ -221,7 +221,7 @@ which can be used by projects in that instance.
## Convert a declarative Jenkinsfile
A declarative Jenkinsfile contains "Sections" and "Directives" which are used to control the behavior of your
-pipelines. There are equivalents for all of these in GitLab, which we've documented below.
+pipelines. Equivalents for all of these exist in GitLab, which we've documented below.
This section is based on the [Jenkinsfile syntax documentation](https://www.jenkins.io/doc/book/pipeline/syntax/)
and is meant to be a mapping of concepts there to concepts in GitLab.
@@ -231,8 +231,8 @@ and is meant to be a mapping of concepts there to concepts in GitLab.
#### `agent`
The agent section is used to define how a pipeline executes. For GitLab, we use [runners](../runners/index.md)
-to provide this capability. You can configure your own runners in Kubernetes or on any host, or take advantage
-of our shared runner fleet (note that the shared runner fleet is only available for GitLab.com users).
+to provide this capability. You can configure your own runners in Kubernetes or on any host. You can also take advantage
+of our shared runner fleet (the shared runner fleet is only available for GitLab.com users).
We also support using [tags](../runners/configure_runners.md#use-tags-to-control-which-jobs-a-runner-can-run) to direct different jobs
to different runners (execution agents).
@@ -272,11 +272,11 @@ default:
#### `stages`
GitLab CI/CD also lets you define stages, but is a little bit more free-form to configure. The GitLab [`stages` keyword](../yaml/index.md#stages)
-is a top level setting that enumerates the list of stages, but you are not required to nest individual jobs underneath
+is a top level setting that enumerates the list of stages. You are not required to nest individual jobs underneath
the `stages` section. Any job defined in the `.gitlab-ci.yml` can be made a part of any stage through use of the
[`stage` keyword](../yaml/index.md#stage).
-Note that, unless otherwise specified, every pipeline is instantiated with a `build`, `test`, and `deploy` stage
+Unless otherwise specified, every pipeline is instantiated with a `build`, `test`, and `deploy` stage
which are run in that order. Jobs that have no `stage` defined are placed by default in the `test` stage.
Of course, each job that refers to a stage must refer to a stage that exists in the pipeline configuration.
@@ -292,8 +292,8 @@ my_job:
#### `steps`
-The `steps` section is equivalent to the [`script` section](../yaml/index.md#script) of an individual job. This is
-a simple YAML array with each line representing an individual command to be run:
+The `steps` section is equivalent to the [`script` section](../yaml/index.md#script) of an individual job. The `steps` section is a YAML array
+with each line representing an individual command to be run:
```yaml
my_job:
@@ -308,7 +308,7 @@ my_job:
In GitLab, we use the [`variables` keyword](../yaml/index.md#variables) to define different variables at runtime.
These can also be set up through the GitLab UI, under CI/CD settings. See also our [general documentation on variables](../variables/index.md),
-including the section on [protected variables](../variables/index.md#protected-cicd-variables) which can be used
+including the section on [protected variables](../variables/index.md#protected-cicd-variables). This can be used
to limit access to certain variables to certain environments or runners:
```yaml
@@ -330,18 +330,18 @@ can provide any variables they like.
#### `triggers` / `cron`
-Because GitLab is integrated tightly with Git, SCM polling options for triggers are not needed. We support an easy to use
+Because GitLab is integrated tightly with Git, SCM polling options for triggers are not needed. We support a
[syntax for scheduling pipelines](../pipelines/schedules.md).
#### `tools`
GitLab does not support a separate `tools` directive. Our best-practice recommendation is to use pre-built
-container images, which can be cached, and can be built to already contain the tools you need for your pipelines. Pipelines can
+container images. These images can be cached and can be built to already contain the tools you need for your pipelines. Pipelines can
be set up to automatically build these images as needed and deploy them to the [container registry](../../user/packages/container_registry/index.md).
-If you're not using container images with Docker/Kubernetes, for example on Mac or FreeBSD, then the `shell` executor does require you to
-set up your environment either in advance or as part of the jobs. You could create a `before_script`
-action that handles this for you.
+If you don't use container images with Docker or Kubernetes, but use the `shell` executor on your own system,
+you must set up your environment. You can set up the environment in advance, or as part of the jobs
+with a `before_script` action that handles this for you.
#### `input`
@@ -351,7 +351,7 @@ variable entry.
#### `when`
GitLab does support a [`when` keyword](../yaml/index.md#when) which is used to indicate when a job should be
-run in case of (or despite) failure, but most of the logic for controlling pipelines can be found in
+run in case of (or despite) failure. Most of the logic for controlling pipelines can be found in
our very powerful [`rules` system](../yaml/index.md#rules):
```yaml
diff --git a/doc/ci/pipelines/cicd_minutes.md b/doc/ci/pipelines/cicd_minutes.md
index 772a06980af..d9ad224ab95 100644
--- a/doc/ci/pipelines/cicd_minutes.md
+++ b/doc/ci/pipelines/cicd_minutes.md
@@ -126,7 +126,7 @@ These additional CI/CD minutes:
- Are used only after the monthly quota included in your subscription runs out.
- Are carried over to the next month, if any remain at the end of the month.
-- Are valid for 12 months from date of purchase or until all minutes are consumed, whichever comes first. Expiry of minutes is not currently enforced.
+- Are valid for 12 months from date of purchase or until all minutes are consumed, whichever comes first. Expiry of minutes is not enforced.
For example, with a GitLab SaaS Premium license:
@@ -137,7 +137,7 @@ For example, with a GitLab SaaS Premium license:
If you use `13,000` minutes during the month, the next month your additional minutes become
`2,000`. If you use `9,000` minutes during the month, your additional minutes remain the same.
-If you bought additional CI/CD minutes while on a trial subscription those minutes will be available after the trial ends or you upgrade to a paid plan.
+If you bought additional CI/CD minutes while on a trial subscription, those minutes are available after the trial ends or you upgrade to a paid plan.
You can find pricing for additional CI/CD minutes on the
[GitLab Pricing page](https://about.gitlab.com/pricing/).
diff --git a/doc/ci/pipelines/downstream_pipelines.md b/doc/ci/pipelines/downstream_pipelines.md
index c8fcd06da07..6c17c23552d 100644
--- a/doc/ci/pipelines/downstream_pipelines.md
+++ b/doc/ci/pipelines/downstream_pipelines.md
@@ -183,7 +183,7 @@ You can trigger a child pipeline from a YAML file generated in a job, instead of
static file saved in your project. This technique can be very powerful for generating pipelines
targeting content that changed or to build a matrix of targets and architectures.
-The artifact containing the generated YAML file must not be [larger than 5MB](https://gitlab.com/gitlab-org/gitlab/-/issues/249140).
+The artifact containing the generated YAML file must not be [larger than 5 MB](https://gitlab.com/gitlab-org/gitlab/-/issues/249140).
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [Create child pipelines using dynamically generated configurations](https://youtu.be/nMdfus2JWHM).
diff --git a/doc/ci/pipelines/index.md b/doc/ci/pipelines/index.md
index f1ca8afa62c..ab98bab022e 100644
--- a/doc/ci/pipelines/index.md
+++ b/doc/ci/pipelines/index.md
@@ -188,6 +188,30 @@ In this example:
- `DEPLOY_ENVIRONMENT` is listed in the **Run pipeline** page, but with no value set.
The user is expected to define the value each time the pipeline is run manually.
+##### Configure a list of selectable values for a prefilled variable
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/363660) in GitLab 15.5 [with a flag](../../administration/feature_flags.md) named `run_pipeline_graphql`. Disabled by default.
+> - The `options` keyword was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105502) in GitLab 15.7.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106038) in GitLab 15.7. Feature flag `run_pipeline_graphql` removed.
+
+You can define an array of CI/CD variable values the user can select from when running a pipeline manually.
+These values are in a dropdown list in the **Run pipeline** page. Add the list of
+value options to `options` and set the default value with `value`. The string in `value`
+must also be included in the `options` list.
+
+For example:
+
+```yaml
+variables:
+ DEPLOY_ENVIRONMENT:
+ value: "staging"
+ options:
+ - "production"
+ - "staging"
+ - "canary"
+ description: "The deployment target. Set to 'staging' by default."
+```
+
### Run a pipeline by using a URL query string
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24146) in GitLab 12.5.
diff --git a/doc/ci/pipelines/multi_project_pipelines.md b/doc/ci/pipelines/multi_project_pipelines.md
deleted file mode 100644
index 25ac9e13185..00000000000
--- a/doc/ci/pipelines/multi_project_pipelines.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'downstream_pipelines.md'
-remove_date: '2022-11-30'
----
-
-This document was moved to [another location](downstream_pipelines.md).
-
-<!-- This redirect file can be deleted after <2022-11-30>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/ci/pipelines/parent_child_pipelines.md b/doc/ci/pipelines/parent_child_pipelines.md
deleted file mode 100644
index be8ed8ba6d7..00000000000
--- a/doc/ci/pipelines/parent_child_pipelines.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'downstream_pipelines.md'
-remove_date: '2022-12-05'
----
-
-This document was moved to [another location](downstream_pipelines.md).
-
-<!-- This redirect file can be deleted after <2022-12-05>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/ci/pipelines/pipeline_efficiency.md b/doc/ci/pipelines/pipeline_efficiency.md
index 276a03cb480..0952fdf1f8f 100644
--- a/doc/ci/pipelines/pipeline_efficiency.md
+++ b/doc/ci/pipelines/pipeline_efficiency.md
@@ -219,7 +219,7 @@ that download and run faster.
Try to use custom Docker images with the software pre-installed. It's usually much
faster to download a larger pre-configured image than to use a common image and install
-software on it each time. Docker's [Best practices for writing Dockerfiles](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)
+software on it each time. The Docker [Best practices for writing Dockerfiles article](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)
has more information about building efficient Docker images.
Methods to reduce Docker image size:
diff --git a/doc/ci/pipelines/settings.md b/doc/ci/pipelines/settings.md
index a7f3cfb59d3..423ee31dec4 100644
--- a/doc/ci/pipelines/settings.md
+++ b/doc/ci/pipelines/settings.md
@@ -32,7 +32,7 @@ To change the visibility of your pipelines and related features:
When it is selected, pipelines and related features are visible:
- For [**Public**](../../user/public_access.md) projects, to everyone.
- - For **Internal** projects, to all logged-in users except [external users](../../user/permissions.md#external-users).
+ - For **Internal** projects, to all logged-in users except [external users](../../user/admin_area/external_users.md).
- For **Private** projects, to all project members (Guest or higher).
When it is cleared:
@@ -41,7 +41,7 @@ To change the visibility of your pipelines and related features:
and the **CI/CD** menu items are visible only to project members (Reporter or higher).
Other users, including guest users, can only view the status of pipelines and jobs, and only
when viewing merge requests or commits.
- - For **Internal** projects, pipelines are visible to all logged in users except [external users](../../user/permissions.md#external-users).
+ - For **Internal** projects, pipelines are visible to all logged in users except [external users](../../user/admin_area/external_users.md).
Related features are visible only to project members (Reporter or higher).
- For **Private** projects, pipelines and related features are visible to project members (Reporter or higher) only.
@@ -284,9 +284,8 @@ when merging a merge request would cause the project's test coverage to decline.
Follow these steps to enable the `Coverage-Check` MR approval rule:
1. Set up a [`coverage`](../yaml/index.md#coverage) regular expression for all jobs you want to include in the overall coverage value.
-1. Go to your project and select **Settings > General**.
-1. Expand **Merge request approvals**.
-1. Select **Enable** next to the `Coverage-Check` approval rule.
+1. Go to your project and select **Settings > Merge requests**.
+1. Under **Merge request approvals**, select **Enable** next to the `Coverage-Check` approval rule.
1. Select the **Target branch**.
1. Set the number of **Approvals required** to greater than zero.
1. Select the users or groups to provide approval.
diff --git a/doc/ci/runners/configure_runners.md b/doc/ci/runners/configure_runners.md
index c675f7204ec..b61e11dd8bc 100644
--- a/doc/ci/runners/configure_runners.md
+++ b/doc/ci/runners/configure_runners.md
@@ -62,7 +62,7 @@ How this feature works:
With some [runner executors](https://docs.gitlab.com/runner/executors/),
if you can run a job on the runner, you can get full access to the file system,
and thus any code it runs as well as the token of the runner. With shared runners, this means that anyone
-that runs jobs on the runner, can access anyone else's code that runs on the
+that runs jobs on the runner, can access another user's code that runs on the
runner.
In addition, because you can get access to the runner token, it is possible
@@ -292,12 +292,12 @@ variables:
A runner can have one of the following statuses.
-| Status | Description |
-|--------|-------------|
-| **online** | The runner has contacted GitLab within the last 2 hours and is available to run jobs. |
-| **offline** | The runner has not contacted GitLab in more than 2 hours and is not available to run jobs. Check the runner to see if you can bring it online. |
-| **stale** | The runner has not contacted GitLab in more than 3 months. If the runner was created more than 3 months ago, but it never contacted the instance, it is also considered **stale**. |
-| **never_contacted** | The runner has never contacted GitLab. To make the runner contact GitLab, run `gitlab-runner run`. |
+| Status | Description |
+|---------|-------------|
+| `online` | The runner has contacted GitLab within the last 2 hours and is available to run jobs. |
+| `offline` | The runner has not contacted GitLab in more than 2 hours and is not available to run jobs. Check the runner to see if you can bring it online. |
+| `stale` | The runner has not contacted GitLab in more than 3 months. If the runner was created more than 3 months ago, but it never contacted the instance, it is also considered **stale**. |
+| `never_contacted` | The runner has never contacted GitLab. To make the runner contact GitLab, run `gitlab-runner run`. |
## Configure runner behavior with variables
@@ -306,11 +306,9 @@ globally or for individual jobs:
- [`GIT_STRATEGY`](#git-strategy)
- [`GIT_SUBMODULE_STRATEGY`](#git-submodule-strategy)
-- [`GIT_SUBMODULE_PATHS`](#sync-or-exclude-specific-submodules-from-ci-jobs)
- [`GIT_CHECKOUT`](#git-checkout)
- [`GIT_CLEAN_FLAGS`](#git-clean-flags)
- [`GIT_FETCH_EXTRA_FLAGS`](#git-fetch-extra-flags)
-- [`GIT_SUBMODULE_PATHS`](#git-submodule-paths)
- [`GIT_SUBMODULE_UPDATE_FLAGS`](#git-submodule-update-flags)
- [`GIT_DEPTH`](#shallow-cloning) (shallow cloning)
- [`GIT_SUBMODULE_DEPTH`](#git-submodule-depth)
@@ -498,16 +496,13 @@ git fetch origin $REFSPECS --depth 50 --prune
Where `$REFSPECS` is a value provided to the runner internally by GitLab.
-### Git submodule paths
+### Sync or exclude specific submodules from CI jobs
> [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/2249) in GitLab Runner 14.0.
Use the `GIT_SUBMODULE_PATHS` variable to control which submodules have to be synced or updated.
You can set it globally or per-job in the [`variables`](../yaml/index.md#variables) section.
-This variable can be very useful for projects which have a large number of submodules which not all of them
-need to be synced or updated in all CI jobs.
-
The path syntax is the same as [`git submodule`](https://git-scm.com/docs/git-submodule#Documentation/git-submodule.txt-ltpathgt82308203):
- To sync and update specific paths:
@@ -524,6 +519,12 @@ The path syntax is the same as [`git submodule`](https://git-scm.com/docs/git-su
GIT_SUBMODULE_PATHS: :(exclude)submoduleA :(exclude)submoduleB
```
+WARNING:
+Git ignores nested paths. To ignore a nested submodule, exclude
+the parent submodule and then manually clone it in the job's scripts. For example,
+ `git clone <repo> --recurse-submodules=':(exclude)nested-submodule'`. Make sure
+to wrap the string in single quotes so the YAML can be parsed successfully.
+
### Git submodule update flags
> [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/3192) in GitLab Runner 14.8.
@@ -565,34 +566,6 @@ You should be aware of the implications for the security, stability, and reprodu
your builds when using the `--remote` flag. In most cases, it is better to explicitly track
submodule commits as designed, and update them using an auto-remediation/dependency bot.
-### Sync or exclude specific submodules from CI jobs
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/26495) in GitLab Runner 14.0.
-
-Some projects have a large number of submodules, and not all of them need to be
-synced or updated in all CI jobs. Use the `GIT_SUBMODULE_PATHS` variable to control this behavior.
-The path syntax is the same as [`git submodule`](https://git-scm.com/docs/git-submodule#Documentation/git-submodule.txt-ltpathgt82308203):
-
-- To sync and update specific paths:
-
- ```yaml
- variables:
- GIT_SUBMODULE_PATHS: 'submoduleA'
- ```
-
-- To exclude specific paths:
-
- ```yaml
- variables:
- GIT_SUBMODULE_PATHS: ':(exclude)submoduleA'
- ```
-
-WARNING:
-Git ignores nested and multiple submodule paths. To ignore a nested submodule, exclude
-the parent submodule and then manually clone it in the job's scripts. For example,
- `git clone <repo> --recurse-submodules=':(exclude)nested-submodule'`. Make sure
-to wrap the string in single quotes so the YAML can be parsed successfully.
-
### Shallow cloning
> Introduced in GitLab 8.9 as an experimental feature.
@@ -930,7 +903,7 @@ The default is the number of CPUs available, but given the memory ramifications,
setting.
`FASTZIP_EXTRACTOR_CONCURRENCY` controls how many files are decompressed at once. Files from a zip archive can natively
-be read from concurrency, so no additional memory is allocated in additional to what the decompressor requires. This
+be read from concurrency, so no additional memory is allocated in addition to what the decompressor requires. This
defaults to the number of CPUs available.
## Clean up stale runners **(ULTIMATE)**
diff --git a/doc/ci/runners/saas/linux_saas_runner.md b/doc/ci/runners/saas/linux_saas_runner.md
index df7d5570953..61421f86ff5 100644
--- a/doc/ci/runners/saas/linux_saas_runner.md
+++ b/doc/ci/runners/saas/linux_saas_runner.md
@@ -17,7 +17,7 @@ For Free, Premium, and Ultimate plan customers, jobs on these instances consume
| | Small | Medium | Large |
|-------------------|---------------------------|---------------------------|--------------------------|
-| Specs | 1 vCPU, 3.75GB RAM | 2 vCPUs, 8GB RAM | 4 vCPUs, 16GB RAM |
+| Specs | 1 vCPU, 3.75 GB RAM | 2 vCPUs, 8 GB RAM | 4 vCPUs, 16 GB RAM |
| GitLab CI/CD tags | `saas-linux-small-amd64` | `saas-linux-medium-amd64` | `saas-linux-large-amd64` |
| Subscription | Free, Premium, Ultimate | Free, Premium, Ultimate | Premium, Ultimate |
@@ -73,12 +73,12 @@ Below are the settings for SaaS runners on Linux.
| Setting | GitLab.com | Default |
|-------------------------------------------------------------------------|------------------|---------|
| Executor | `docker+machine` | - |
-| Default Docker image | `ruby:2.5` | - |
+| Default Docker image | `ruby:3.1` | - |
| `privileged` (run [Docker in Docker](https://hub.docker.com/_/docker/)) | `true` | `false` |
- **Cache**: These runners share a
[distributed cache](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching)
- that's stored in a Google Cloud Storage (GCS) bucket. Cache contents not updated within
+ that's stored in a Google Cloud Storage (GCS) bucket. Cache contents not updated in
the last 14 days are automatically removed, based on the
[object lifecycle management policy](https://cloud.google.com/storage/docs/lifecycle).
@@ -88,8 +88,8 @@ Below are the settings for SaaS runners on Linux.
and [#4070](https://gitlab.com/gitlab-com/gl-infra/reliability/-/issues/4070).
NOTE:
-The final disk space your jobs can use will be less than 25GB. Some disk space
-allocated to the instance will be occupied by the operating system, the Docker image,
+The final disk space your jobs can use is less than 25 GB. Some disk space
+allocated to the instance is occupied by the operating system, the Docker image,
and a copy of your cloned repository.
## Pre-clone script
@@ -198,7 +198,7 @@ sentry_dsn = "X"
]
limit = X
[runners.docker]
- image = "ruby:2.5"
+ image = "ruby:3.1"
privileged = true
volumes = [
"/certs/client",
diff --git a/doc/ci/runners/saas/macos_saas_runner.md b/doc/ci/runners/saas/macos_saas_runner.md
index cd40aac25bc..270f85e65e2 100644
--- a/doc/ci/runners/saas/macos_saas_runner.md
+++ b/doc/ci/runners/saas/macos_saas_runner.md
@@ -18,9 +18,11 @@ Jobs handled by macOS shared runners on GitLab.com **time out after 3 hours**, r
## Access request process
-While in beta, to run CI jobs on the macOS runners, GitLab SaaS customer namespaces must be explicitly added to the macOS `allow-list`.
+While in beta, to run CI jobs on the macOS runners, you must specify the GitLab SaaS customer personal or group [namespaces](../../../user/namespace/index.md) in the macOS `allow-list`. These are the namespaces that use the macOS runners.
-After you have been added, you can use the macOS runners for any projects in your namespace.
+When you specify a personal or group namespace, the top level group is not added unless you specify it.
+
+After you add your namespace, you can use the macOS runners for any projects under the namespace you included.
To request access, open an [access request](https://gitlab.com/gitlab-com/runner-saas-macos-limited-availability/-/issues/new).
The expected turnaround for activation is two business days.
diff --git a/doc/ci/secrets/index.md b/doc/ci/secrets/index.md
index a5082af89bc..bfc53210348 100644
--- a/doc/ci/secrets/index.md
+++ b/doc/ci/secrets/index.md
@@ -143,7 +143,7 @@ different policies together. If authentication is successful, these policies are
attached to the resulting Vault token.
[Bound claims](https://developer.hashicorp.com/vault/docs/auth/jwt#bound-claims) are predefined
-values that are matched to the JWT's claims. With bounded claims, you can restrict access
+values that are matched to the JWT claims. With bounded claims, you can restrict access
to specific GitLab users, specific projects, or even jobs running for specific Git
references. You can have as many bounded claims you need, but they must *all* match
for authentication to be successful.
diff --git a/doc/ci/secure_files/index.md b/doc/ci/secure_files/index.md
index bba8a3e4c27..e63b671ad2b 100644
--- a/doc/ci/secure_files/index.md
+++ b/doc/ci/secure_files/index.md
@@ -29,7 +29,7 @@ plain text and binary file types.
You can manage secure files in the project settings, or with the [secure files API](../../api/secure_files.md).
Secure files can be [downloaded and used by CI/CD jobs](#use-secure-files-in-cicd-jobs)
-by using the [load-secure-files](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/load-secure-files)
+by using the [download-secure-files](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/download-secure-files)
tool.
NOTE:
@@ -49,11 +49,11 @@ To add a secure file to a project:
## Use secure files in CI/CD jobs
-To use your secure files in a CI/CD job, you must use the [`load-secure-files`](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/load-secure-files)
+To use your secure files in a CI/CD job, you must use the [`download-secure-files`](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/download-secure-files)
tool to download the files in the job. After they are downloaded, you can use them
with your other script commands.
-Add a command in the `script` section of your job to download the `load-secure-files` tool
+Add a command in the `script` section of your job to download the `download-secure-files` tool
and execute it. The files download into a `.secure_files` directory in the root of the project.
To change the download location for the secure files, set the path in the `SECURE_FILES_DOWNLOAD_PATH`
[CI/CD variable](../variables/index.md).
@@ -65,5 +65,5 @@ test:
variables:
SECURE_FILES_DOWNLOAD_PATH: './where/files/should/go/'
script:
- - curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/load-secure-files/-/raw/main/installer" | bash
+ - curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/download-secure-files/-/raw/main/installer" | bash
```
diff --git a/doc/ci/services/index.md b/doc/ci/services/index.md
index 830b9406c6e..beebaa6d03f 100644
--- a/doc/ci/services/index.md
+++ b/doc/ci/services/index.md
@@ -260,7 +260,7 @@ test:
| Setting | Required | GitLab version | Description |
|------------|----------|----------------| ----------- |
| `name` | yes, when used with any other option | 9.4 | Full name of the image to use. If the full image name includes a registry hostname, use the `alias` option to define a shorter service access name. For more information, see [Accessing the services](#accessing-the-services). |
-| `entrypoint` | no | 9.4 |Command or script to execute as the container's entrypoint. It's translated to Docker's `--entrypoint` option while creating the container. The syntax is similar to [`Dockerfile`'s `ENTRYPOINT`](https://docs.docker.com/engine/reference/builder/#entrypoint) directive, where each shell token is a separate string in the array. |
+| `entrypoint` | no | 9.4 |Command or script to execute as the container's entrypoint. It's translated to the Docker `--entrypoint` option while creating the container. The syntax is similar to [`Dockerfile`'s `ENTRYPOINT`](https://docs.docker.com/engine/reference/builder/#entrypoint) directive, where each shell token is a separate string in the array. |
| `command` | no | 9.4 |Command or script that should be used as the container's command. It's translated to arguments passed to Docker after the image's name. The syntax is similar to [`Dockerfile`'s `CMD`](https://docs.docker.com/engine/reference/builder/#cmd) directive, where each shell token is a separate string in the array. |
| `alias` (1) | no | 9.4 | Additional alias that can be used to access the service from the job's container. Read [Accessing the services](#accessing-the-services) for more information. |
| `variables` (2) | no | 14.5 | Additional environment variables that are passed exclusively to the service. The syntax is the same as [Job Variables](../variables/index.md). |
@@ -394,6 +394,8 @@ time.
## Capturing service container logs
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/3680) in GitLab Runner 15.6.
+
Logs generated by applications running in service containers can be captured for subsequent examination and debugging.
You might want to look at service container's logs when the service container has started successfully, but is not
behaving es expected, leading to job failures. The logs can indicate missing or incorrect configuration of the service
diff --git a/doc/ci/testing/code_quality.md b/doc/ci/testing/code_quality.md
index d1ed28b79c0..8e1c3d72d3d 100644
--- a/doc/ci/testing/code_quality.md
+++ b/doc/ci/testing/code_quality.md
@@ -71,11 +71,12 @@ See also the Code Climate list of [Supported Languages for Maintainability](http
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/284140) in GitLab 13.12.
> - [Disabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/2526) in GitLab 14.0 due to [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/334116).
> - [Inline annotation added](https://gitlab.com/gitlab-org/gitlab/-/issues/2526) and [feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/284140) in GitLab 14.1.
+> - [Updated](https://gitlab.com/groups/gitlab-org/-/epics/8071) to handle multiple findings better in GitLab 15.7, disabled by default behind the `refactor_code_quality_inline_findings` [feature flag](../../administration/feature_flags.md).
Changes to files in merge requests can cause Code Quality to fall if merged. In these cases,
the merge request's diff view displays an indicator next to lines with new Code Quality violations. For example:
-![Code Quality MR diff report](img/code_quality_mr_diff_report_v14_2.png)
+![Code Quality MR diff report](img/code_quality_mr_diff_report_v15_7.png)
## Example configuration
diff --git a/doc/ci/testing/fail_fast_testing.md b/doc/ci/testing/fail_fast_testing.md
index 58471a626da..b1bc44a0a37 100644
--- a/doc/ci/testing/fail_fast_testing.md
+++ b/doc/ci/testing/fail_fast_testing.md
@@ -12,7 +12,7 @@ For applications that use RSpec for running tests, we've introduced the `Verify/
[template to run subsets of your test suite](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates/Verify/FailFast.gitlab-ci.yml),
based on the changes in your merge request.
-The template uses the [test_file_finder (`tff`) gem](https://gitlab.com/gitlab-org/ci-cd/test_file_finder/)
+The template uses the [`test_file_finder` (`tff`) gem](https://gitlab.com/gitlab-org/ci-cd/test_file_finder/)
that accepts a list of files as input, and returns a list of spec (test) files
that it believes to be relevant to the input files.
diff --git a/doc/ci/testing/img/code_quality_mr_diff_report_v14_2.png b/doc/ci/testing/img/code_quality_mr_diff_report_v14_2.png
deleted file mode 100644
index c1e9aad24ac..00000000000
--- a/doc/ci/testing/img/code_quality_mr_diff_report_v14_2.png
+++ /dev/null
Binary files differ
diff --git a/doc/ci/testing/img/code_quality_mr_diff_report_v15_7.png b/doc/ci/testing/img/code_quality_mr_diff_report_v15_7.png
new file mode 100644
index 00000000000..b45124e0e5d
--- /dev/null
+++ b/doc/ci/testing/img/code_quality_mr_diff_report_v15_7.png
Binary files differ
diff --git a/doc/ci/testing/unit_test_report_examples.md b/doc/ci/testing/unit_test_report_examples.md
index 5d4cfa88d88..87426fc8496 100644
--- a/doc/ci/testing/unit_test_report_examples.md
+++ b/doc/ci/testing/unit_test_report_examples.md
@@ -42,7 +42,7 @@ Use the following job in `.gitlab-ci.yml`:
golang:
stage: test
script:
- - go get gotest.tools/gotestsum
+ - go install gotest.tools/gotestsum@latest
- gotestsum --junitfile report.xml --format testname
artifacts:
when: always
diff --git a/doc/ci/troubleshooting.md b/doc/ci/troubleshooting.md
index 87ebff74600..81e13192cef 100644
--- a/doc/ci/troubleshooting.md
+++ b/doc/ci/troubleshooting.md
@@ -40,29 +40,8 @@ is at:
https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/editor/schema/ci.json.
```
-The schema rules for custom YAML tags like `!reference` are treated as invalid by your editor until the custom tags are
-set in your editor settings. For example:
-
-- In VS Code, you can set `vscode-yaml` to parse `customTags` in your `settings.json` file:
-
- ```json
- "yaml.customTags": [
- "!reference sequence"
- ]
- ```
-
-- In Sublime Text, if you are using the `LSP-yaml` package, you can set `customTags` in your `LSP-yaml` user settings:
-
- ```json
- {
- "settings": {
- "yaml.customTags": ["!reference sequence"]
- }
- }
- ```
-
To see the full list of custom tags covered by the CI/CD schema, check the
-latest version of the schema, linked above.
+latest version of the schema.
### Verify syntax with CI Lint tool
@@ -116,7 +95,7 @@ if you are using that type:
Troubleshooting guides are available for some CI/CD features and related topics:
-- [Container Registry](../user/packages/container_registry/index.md#troubleshooting-the-gitlab-container-registry)
+- [Container Registry](../user/packages/container_registry/troubleshoot_container_registry.md)
- [GitLab Runner](https://docs.gitlab.com/runner/faq/)
- [Merge Trains](pipelines/merge_trains.md#troubleshooting)
- [Docker Build](docker/using_docker_build.md#troubleshooting)
@@ -362,7 +341,7 @@ To [prevent duplicate pipelines](jobs/job_control.md#avoid-duplicate-pipelines),
[`workflow: rules`](yaml/index.md#workflow) or rewrite your rules to control
which pipelines can run.
-### Console workaround if job using resource_group gets stuck **(FREE SELF)**
+### Console workaround if job using `resource_group` gets stuck **(FREE SELF)**
```ruby
# find resource group by name
diff --git a/doc/ci/variables/index.md b/doc/ci/variables/index.md
index 97707c603bd..10dfa0174a0 100644
--- a/doc/ci/variables/index.md
+++ b/doc/ci/variables/index.md
@@ -153,6 +153,8 @@ job:
### Add a CI/CD variable to a project
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/362227) in GitLab 15.7, projects can define a maximum of 200 CI/CD variables.
+
You can add CI/CD variables to a project's settings. Only project members with the
Maintainer role
can add or update project CI/CD variables. To keep a CI/CD variable secret, put it
@@ -191,7 +193,8 @@ The output is:
### Add a CI/CD variable to a group
-> Support for environment scopes [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/2874) in GitLab Premium 13.11
+> - Support for environment scopes [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/2874) in GitLab Premium 13.11
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/362227) in GitLab 15.7, groups can define a maximum of 200 CI/CD variables.
To make a CI/CD variable available to all projects in a group, define a group CI/CD variable. Only group owners can add or update group-level CI/CD variables.
@@ -391,6 +394,21 @@ To mark a variable as protected:
The variable is available for all subsequent pipelines.
+### Expand CI/CD variables
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217309) in GitLab 13.7.
+
+Expanded variables treat values with the `$` character as a reference to another variable. CI/CD variables are expanded
+by default.
+
+To treat variables with a `$` character as raw strings, disable variable expansion for the variable:
+
+1. In the project or group, go to **Settings > CI/CD**.
+1. Expand the **Variables** section.
+1. Next to the variable you want to do not want expanded, select **Edit**.
+1. Clear the **Expand variable** checkbox.
+1. Select **Update variable**.
+
### CI/CD variable security
Malicious code pushed to your `.gitlab-ci.yml` file could compromise your variables
@@ -715,7 +733,7 @@ You can override the value of a CI/CD variable when you
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/295234) in GitLab 13.8.
-You can grant permission to override variables to [maintainers](../../user/permissions.md#project-features) only. When other users try to run a pipeline
+You can grant permission to override variables to users with the Maintainer role only. When other users try to run a pipeline
with overridden variables, they receive the `Insufficient permissions to set pipeline variables`
error message.
@@ -757,7 +775,7 @@ You can configure [Auto DevOps](../../topics/autodevops/index.md) to pass CI/CD
to a running application.
To make a CI/CD variable available as an environment variable in the running application's container,
-[prefix the variable key](../../topics/autodevops/customize.md#application-secret-variables)
+[prefix the variable key](../../topics/autodevops/cicd_variables.md#configure-application-secret-variables)
with `K8S_SECRET_`.
CI/CD variables with multi-line values are not supported.
@@ -776,7 +794,7 @@ or job scripts. Debug logging exposes job execution details that are usually hid
by the runner and makes job logs more verbose. It also exposes all variables and secrets
available to the job.
-Before you enable debug logging, make sure only [team members](../../user/permissions.md#project-features)
+Before you enable debug logging, make sure only team members
can view job logs. You should also [delete job logs](../jobs/index.md#view-jobs-in-a-pipeline)
with debug output before you make logs public again.
diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md
index 852110a1415..d7f67b79416 100644
--- a/doc/ci/variables/predefined_variables.md
+++ b/doc/ci/variables/predefined_variables.md
@@ -40,7 +40,7 @@ as it can cause the pipeline to behave unexpectedly.
| `CI_COMMIT_SHORT_SHA` | 11.7 | all | The first eight characters of `CI_COMMIT_SHA`. |
| `CI_COMMIT_TAG` | 9.0 | 0.5 | The commit tag name. Available only in pipelines for tags. |
| `CI_COMMIT_TAG_MESSAGE` | 15.5 | all | The commit tag message. Available only in pipelines for tags. |
-| `CI_COMMIT_TIMESTAMP` | 13.4 | all | The timestamp of the commit in the ISO 8601 format. |
+| `CI_COMMIT_TIMESTAMP` | 13.4 | all | The timestamp of the commit in the [ISO 8601](https://www.rfc-editor.org/rfc/rfc3339#appendix-A) format. |
| `CI_COMMIT_TITLE` | 10.8 | all | The title of the commit. The full first line of the message. |
| `CI_CONCURRENT_ID` | all | 11.10 | The unique ID of build execution in a single executor. |
| `CI_CONCURRENT_PROJECT_ID` | all | 11.10 | The unique ID of build execution in a single executor and project. |
@@ -75,6 +75,7 @@ as it can cause the pipeline to behave unexpectedly.
| `CI_JOB_NAME_SLUG` | 15.4 | all | `CI_JOB_NAME_SLUG` in lowercase, shortened to 63 bytes, and with everything except `0-9` and `a-z` replaced with `-`. No leading / trailing `-`. Use in paths. |
| `CI_JOB_STAGE` | 9.0 | 0.5 | The name of the job's stage. |
| `CI_JOB_STATUS` | all | 13.5 | The status of the job as each runner stage is executed. Use with [`after_script`](../yaml/index.md#after_script). Can be `success`, `failed`, or `canceled`. |
+| `CI_JOB_TIMEOUT` | 15.7 | 15.7 | The job timeout value. |
| `CI_JOB_TOKEN` | 9.0 | 1.2 | A token to authenticate with [certain API endpoints](../jobs/ci_job_token.md). The token is valid as long as the job is running. |
| `CI_JOB_URL` | 11.1 | 0.5 | The job details URL. |
| `CI_JOB_STARTED_AT` | 13.10 | all | The UTC datetime when a job started, in [ISO 8601](https://www.rfc-editor.org/rfc/rfc3339#appendix-A) format. |
@@ -94,6 +95,7 @@ as it can cause the pipeline to behave unexpectedly.
| `CI_PROJECT_ID` | all | all | The ID of the current project. This ID is unique across all projects on the GitLab instance. |
| `CI_PROJECT_NAME` | 8.10 | 0.5 | The name of the directory for the project. For example if the project URL is `gitlab.example.com/group-name/project-1`, `CI_PROJECT_NAME` is `project-1`. |
| `CI_PROJECT_NAMESPACE` | 8.10 | 0.5 | The project namespace (username or group name) of the job. |
+| `CI_PROJECT_NAMESPACE_ID` | 15.7 | 0.5 | The project namespace ID of the job. |
| `CI_PROJECT_PATH_SLUG` | 9.3 | all | `$CI_PROJECT_PATH` in lowercase with characters that are not `a-z` or `0-9` replaced with `-` and shortened to 63 bytes. Use in URLs and domain names. |
| `CI_PROJECT_PATH` | 8.10 | 0.5 | The project namespace with the project name included. |
| `CI_PROJECT_REPOSITORY_LANGUAGES` | 12.3 | all | A comma-separated, lowercase list of the languages used in the repository. For example `ruby,javascript,html,css`. The maximum number of languages is limited to 5. An issue [proposes to increase the limit](https://gitlab.com/gitlab-org/gitlab/-/issues/368925). |
diff --git a/doc/ci/variables/where_variables_can_be_used.md b/doc/ci/variables/where_variables_can_be_used.md
index c8436fc044d..88d0c9a2454 100644
--- a/doc/ci/variables/where_variables_can_be_used.md
+++ b/doc/ci/variables/where_variables_can_be_used.md
@@ -28,16 +28,16 @@ There are two places defined variables can be used. On the:
| [`artifacts:name`](../yaml/index.md#artifactsname) | yes | Runner | The variable expansion is made by GitLab Runner's shell environment. |
| [`before_script`](../yaml/index.md#before_script) | yes | Script execution shell | The variable expansion is made by the [execution shell environment](#execution-shell-environment) |
| [`cache:key`](../yaml/index.md#cachekey) | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism). |
-| [`environment:name`](../yaml/index.md#environmentname) | yes | GitLab | Similar to `environment:url`, but the variables expansion doesn't support the following:<br/><br/>- Variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`).<br/>- Any other variables related to environment (currently only `CI_ENVIRONMENT_URL`).<br/>- [Persisted variables](#persisted-variables). |
+| [`environment:name`](../yaml/index.md#environmentname) | yes | GitLab | Similar to `environment:url`, but the variables expansion doesn't support the following:<br/><br/>- `CI_ENVIRONMENT_*` variables.<br/>- [Persisted variables](#persisted-variables). |
| [`environment:url`](../yaml/index.md#environmenturl) | yes | GitLab | The variable expansion is made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab.<br/><br/>Supported are all variables defined for a job (project/group variables, variables from `.gitlab-ci.yml`, variables from triggers, variables from pipeline schedules).<br/><br/>Not supported are variables defined in the GitLab Runner `config.toml` and variables created in the job's `script`. |
| [`environment:auto_stop_in`](../yaml/index.md#environmentauto_stop_in)| yes | GitLab | The variable expansion is made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab.<br/><br/> The value of the variable being substituted should be a period of time in a human readable natural language form. See [possible inputs](../yaml/index.md#environmentauto_stop_in) for more information.|
-| [`except:variables`](../yaml/index.md#onlyvariables--exceptvariables) | no | Not applicable | The variable must be in the form of `$variable`. Not supported are the following:<br/><br/>- Variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`).<br/>- Any other variables related to environment (currently only `CI_ENVIRONMENT_URL`).<br/>- [Persisted variables](#persisted-variables). |
+| [`except:variables`](../yaml/index.md#onlyvariables--exceptvariables) | no | Not applicable | The variable must be in the form of `$variable`. Not supported are the following:<br/><br/>- `CI_ENVIRONMENT_*` variables, except `CI_ENVIRONMENT_NAME` which is supported.<br/>- [Persisted variables](#persisted-variables). |
| [`image`](../yaml/index.md#image) | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism). |
| [`include`](../yaml/index.md#include) | yes | GitLab | The variable expansion is made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab. <br/><br/>See [Use variables with include](../yaml/includes.md#use-variables-with-include) for more information on supported variables. |
-| [`only:variables`](../yaml/index.md#onlyvariables--exceptvariables) | no | Not applicable | The variable must be in the form of `$variable`. Not supported are the following:<br/><br/>- Variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`).<br/>- Any other variables related to environment (currently only `CI_ENVIRONMENT_URL`).<br/>- [Persisted variables](#persisted-variables). |
+| [`only:variables`](../yaml/index.md#onlyvariables--exceptvariables) | no | Not applicable | The variable must be in the form of `$variable`. Not supported are the following:<br/><br/>- `CI_ENVIRONMENT_*` variables, except `CI_ENVIRONMENT_NAME` which is supported.<br/>- [Persisted variables](#persisted-variables). |
| [`resource_group`](../yaml/index.md#resource_group) | yes | GitLab | Similar to `environment:url`, but the variables expansion doesn't support the following:<br/>- `CI_ENVIRONMENT_URL`<br/>- [Persisted variables](#persisted-variables). |
| [`rules:exists`](../yaml/index.md#rulesexists) | yes | GitLab | The variable expansion is made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab. |
-| [`rules:if`](../yaml/index.md#rulesif) | no | Not applicable | The variable must be in the form of `$variable`. Not supported are the following:<br/><br/>- Variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`).<br/>- Any other variables related to environment (currently only `CI_ENVIRONMENT_URL`).<br/>- [Persisted variables](#persisted-variables). |
+| [`rules:if`](../yaml/index.md#rulesif) | no | Not applicable | The variable must be in the form of `$variable`. Not supported are the following:<br/><br/>- `CI_ENVIRONMENT_*` variables, except `CI_ENVIRONMENT_NAME` which is supported.<br/>- [Persisted variables](#persisted-variables). |
| [`script`](../yaml/index.md#script) | yes | Script execution shell | The variable expansion is made by the [execution shell environment](#execution-shell-environment). |
| [`services:name`](../yaml/index.md#services) | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism). |
| [`services`](../yaml/index.md#services) | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism). |
diff --git a/doc/ci/yaml/artifacts_reports.md b/doc/ci/yaml/artifacts_reports.md
index 9f5e4e919b0..e12786f06ce 100644
--- a/doc/ci/yaml/artifacts_reports.md
+++ b/doc/ci/yaml/artifacts_reports.md
@@ -107,19 +107,17 @@ GitLab can display the results of coverage report in the merge request
## `artifacts:reports:codequality`
-> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212499) to GitLab Free in 13.2.
+> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212499) to GitLab Free in 13.2.
+> - [Added support for multiple reports in diff annotations and full pipeline report](https://gitlab.com/gitlab-org/gitlab/-/issues/9014) in 15.7.
The `codequality` report collects [code quality issues](../testing/code_quality.md). The
collected code quality report uploads to GitLab as an artifact.
-GitLab can display the results of:
+GitLab can display the results of one or more reports in:
-- One or more reports in the merge request [code quality widget](../testing/code_quality.md#code-quality-widget).
-- Only one report in:
- - The merge request [diff annotations](../testing/code_quality.md#code-quality-in-diff-view).
- Track progress on adding support for multiple reports in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/328257).
- - The [full report](../testing/metrics_reports.md). Track progress on adding support for multiple reports in
- [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/9014).
+- The merge request [code quality widget](../testing/code_quality.md#code-quality-widget).
+- The merge request [diff annotations](../testing/code_quality.md#code-quality-in-diff-view).
+- The [full report](../testing/metrics_reports.md).
## `artifacts:reports:container_scanning` **(ULTIMATE)**
@@ -152,16 +150,16 @@ GitLab can display the results of one or more reports in:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/360766) in GitLab 15.3
This report is a Software Bill of Materials describing the components of a project
-following the [cyclonedx](https://cyclonedx.org/docs/1.4) protocol format.
+following the [CycloneDX](https://cyclonedx.org/docs/1.4) protocol format.
-You can specify multiple cyclonedx reports per job. These can be either supplied
+You can specify multiple CycloneDX reports per job. These can be either supplied
as a list of filenames, a filename pattern, or both:
- List of filenames: `cyclonedx: [gl-sbom-npm-npm.cdx.json, gl-sbom-bundler-gem.cdx.json]`.
- A filename pattern: `cyclonedx: gl-sbom-*.json`.
- Combination of both of the above: `cyclonedx: [gl-sbom-*.json, my-cyclonedx.json]`.
-Below is an example of a job exposing cyclonedx artifacts:
+Below is an example of a job exposing CycloneDX artifacts:
```yaml
artifacts:
diff --git a/doc/ci/yaml/includes.md b/doc/ci/yaml/includes.md
index 2b9f42e1c75..daf2e653250 100644
--- a/doc/ci/yaml/includes.md
+++ b/doc/ci/yaml/includes.md
@@ -13,8 +13,8 @@ You can use [`include`](index.md#include) to include external YAML files in your
To include a single configuration file, use either of these syntax options:
-- `include` by itself with a single file, which is the same as
- [`include:local`](index.md#includelocal):
+- `include` by itself with a single file. If this is a local file, it is the same as [`include:local`](index.md#includelocal).
+ If this is a remote file, it is the same as [`include:remote`](index.md#includeremote).
```yaml
include: '/templates/.after-script-template.yml'
@@ -31,7 +31,8 @@ To include a single configuration file, use either of these syntax options:
You can include an array of configuration files:
-- If you do not specify an `include` type, the type defaults to [`include:local`](index.md#includelocal):
+- If you do not specify an `include` type, each array item defaults to [`include:local`](index.md#includelocal)
+ or [`include:remote`](index.md#includeremote), as needed:
```yaml
include:
diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md
index 3392304775a..dffe409b193 100644
--- a/doc/ci/yaml/index.md
+++ b/doc/ci/yaml/index.md
@@ -86,6 +86,7 @@ of the listed keywords use the value defined in the `default` section.
- [`artifacts`](#artifacts)
- [`before_script`](#before_script)
- [`cache`](#cache)
+- [`hooks`](#hooks)
- [`image`](#image)
- [`interruptible`](#interruptible)
- [`retry`](#retry)
@@ -394,12 +395,12 @@ Use [`workflow`](workflow.md) to control pipeline behavior.
#### `workflow:name`
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/372538) in GitLab 15.5 [with a flag](../../administration/feature_flags.md) named `pipeline_name`. Disabled by default.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/372538) in GitLab 15.5 [with a flag](../../administration/feature_flags.md) named `pipeline_name`. Disabled by default.
+> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/376095) in GitLab 15.7.
FLAG:
-On self-managed GitLab, by default this feature is not available. To make it available,
-ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `pipeline_name`.
-The feature is not ready for production use.
+On self-managed GitLab, by default this feature is available. To hide the feature,
+ask an administrator to [disable the feature flag](../../administration/feature_flags.md) named `pipeline_name`.
You can use `name` in `workflow:` to define a name for pipelines.
@@ -424,7 +425,7 @@ A configuration with different pipeline names depending on the pipeline conditio
```yaml
variables:
- PIPELINE_NAME: 'Default pipeline name'
+ PIPELINE_NAME: 'Default pipeline name' # A default is not required.
workflow:
name: '$PIPELINE_NAME'
@@ -437,6 +438,11 @@ workflow:
PIPELINE_NAME: 'Ruby 3 pipeline'
```
+**Additional details**:
+
+- If the name is an empty string, the pipeline is not assigned a name. A name consisting
+ of only CI/CD variables could evaluate to an empty string if all the variables are also empty.
+
#### `workflow:rules`
The `rules` keyword in `workflow` is similar to [`rules` defined in jobs](#rules),
@@ -1766,8 +1772,8 @@ deploy:
**Additional details**:
-- Enviroments created from this job definition are assigned a [tier](../environments/index.md#deployment-tier-of-environments) based on this value.
-- Existing environments don't have their tier updated if this value is added later. Existing enviroments must have their tier updated via the [Environments API](../../api/environments.md#update-an-existing-environment).
+- Environments created from this job definition are assigned a [tier](../environments/index.md#deployment-tier-of-environments) based on this value.
+- Existing environments don't have their tier updated if this value is added later. Existing environments must have their tier updated via the [Environments API](../../api/environments.md#update-an-existing-environment).
**Related topics**:
@@ -1862,6 +1868,74 @@ rspec:
- [Reuse configuration sections by using `extends`](yaml_optimization.md#use-extends-to-reuse-configuration-sections).
- Use `extends` to reuse configuration from [included configuration files](yaml_optimization.md#use-extends-and-include-together).
+### `hooks`
+
+> Introduced in GitLab 15.6 [with a flag](../../administration/feature_flags.md) named `ci_hooks_pre_get_sources_script`. Disabled by default.
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available,
+ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `ci_hooks_pre_get_sources_script`.
+The feature is not ready for production use.
+
+Use `hooks` to specify lists of commands to execute on the runner
+at certain stages of job execution, like before retrieving the Git repository.
+
+**Keyword type**: Job keyword. You can use it only as part of a job or in the
+[`default` section](#default).
+
+**Possible inputs**:
+
+- A hash of hooks and their commands. Available hooks: `pre_get_sources_script`.
+
+#### `hooks:pre_get_sources_script`
+
+> Introduced in GitLab 15.6 [with a flag](../../administration/feature_flags.md) named `ci_hooks_pre_get_sources_script`. Disabled by default.
+
+Use `hooks:pre_get_sources_script` to specify a list of commands to execute on the runner
+before retrieving the Git repository and any submodules. You can use it
+to adjust the Git client configuration first, for example.
+
+**Related topics**:
+
+- [GitLab Runner configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section)
+
+**Example of `hooks:pre_get_sources_script`**:
+
+```yaml
+job1:
+ hooks:
+ pre_get_sources_script:
+ - echo 'hello job1 pre_get_sources_script'
+ script: echo 'hello job1 script'
+```
+
+### `id_tokens`
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356986) in GitLab 15.7.
+
+Use `id_tokens` to create [JSON web tokens (JWT)](https://www.rfc-editor.org/rfc/rfc7519) to authenticate with third party services. All
+JWTs created this way support OIDC authentication. The required `aud` sub-keyword is used to configure the `aud` claim for the JWT.
+
+**Possible inputs**:
+
+- Token names with their `aud` claims. `aud` can be a single string or as an array of strings.
+
+**Example of `id_tokens`**:
+
+```yaml
+job_with_id_tokens:
+ id_tokens:
+ ID_TOKEN_1:
+ aud: https://gitlab.com
+ ID_TOKEN_2:
+ aud:
+ - https://gcp.com
+ - https://aws.com
+ script:
+ - command_to_authenticate_with_gitlab $ID_TOKEN_1
+ - command_to_authenticate_with_aws $ID_TOKEN_2
+```
+
### `image`
Use `image` to specify a Docker image that the job runs in.
@@ -2210,6 +2284,9 @@ This example creates four paths of execution:
in a job's `needs` section.
- In GitLab 13.9 and older, if `needs` refers to a job that might not be added to
a pipeline because of `only`, `except`, or `rules`, the pipeline might fail to create. In GitLab 13.10 and later, use the [`needs:optional`](#needsoptional) keyword to resolve a failed pipeline creation.
+- If a pipeline has jobs with `needs: []` and jobs in the [`.pre`](#stage-pre) stage, they will
+ all start as soon as the pipeline is created. Jobs with `needs: []` start immediately,
+ and jobs in the `.pre` stage also start immediately.
#### `needs:artifacts`
@@ -2268,12 +2345,12 @@ In this example:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/14311) in GitLab 12.7.
Use `needs:project` to download artifacts from up to five jobs in other pipelines.
-The artifacts are downloaded from the latest successful pipeline for the specified ref.
+The artifacts are downloaded from the latest successful specified job for the specified ref.
To specify multiple jobs, add each as separate array items under the `needs` keyword.
-If there is a pipeline running for the specified ref, a job with `needs:project`
-does not wait for the pipeline to complete. Instead, the job downloads the artifact
-from the latest pipeline that completed successfully.
+If there is a pipeline running for the ref, a job with `needs:project`
+does not wait for the pipeline to complete. Instead, the artifacts are downloaded
+from the latest successful run of the specified job.
`needs:project` must be used with `job`, `ref`, and `artifacts`.
@@ -2963,7 +3040,7 @@ job:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/363024) in GitLab 15.3. Supported by `release-cli` v0.12.0 or later.
-If the tag does not exist, the newly created tag is annotated with the message specifed by `tag_message`.
+If the tag does not exist, the newly created tag is annotated with the message specified by `tag_message`.
If omitted, a lightweight tag is created.
**Keyword type**: Job keyword. You can use it only as part of a job.
@@ -3670,7 +3747,7 @@ job:
### `services`
-Use `services` to specify an additional Docker image to run scripts in. The [`services` image](../services/index.md) is linked
+Use `services` to specify any additional Docker images that your scripts require to run successfully. The [`services` image](../services/index.md) is linked
to the image specified in the [`image`](#image) keyword.
**Keyword type**: Job keyword. You can use it only as part of a job or in the
@@ -3706,9 +3783,11 @@ test:
- bundle exec rake spec
```
-In this example, the job launches a Ruby container. Then, from that container, the job launches
-another container that's running PostgreSQL. Then the job then runs scripts
-in that container.
+In this example, GitLab launches two containers for the job:
+
+- A Ruby container that runs the `script` commands.
+- A PostgreSQL container. The `script` commands in the Ruby container can connect to
+ the PostgreSQL database at the `db-postgrest` hostname.
**Related topics**:
@@ -3882,6 +3961,12 @@ job2:
- echo "This job runs in the test stage."
```
+**Additional details:**
+
+- If a pipeline has jobs with [`needs: []`](#needs) and jobs in the `.pre` stage, they will
+ all start as soon as the pipeline is created. Jobs with `needs: []` start immediately,
+ ignoring any stage configuration.
+
### `tags`
> - A limit of 50 tags per job [enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/338929) in GitLab 14.3.
@@ -4228,7 +4313,8 @@ deploy_review_job:
#### `variables:description`
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30101) in GitLab 13.7.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30101) in GitLab 13.7.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/363660) in GitLab 15.5, `variables:value` can contain an array of values.
Use the `description` keyword to define a [pipeline-level (global) variable that is prefilled](../pipelines/index.md#prefill-variables-in-manual-pipelines)
when [running a pipeline manually](../pipelines/index.md#run-a-pipeline-manually).
@@ -4240,6 +4326,7 @@ If used with `value`, the variable value is also prefilled when running a pipeli
**Possible inputs**:
- A string.
+- An array of strings.
**Example of `variables:description`**:
@@ -4254,10 +4341,17 @@ variables:
- A global variable defined with `value` but no `description` behaves the same as
[`variables`](#variables).
+- `variables:value` can [contain an array of selectable values](../pipelines/index.md#configure-a-list-of-selectable-values-for-a-prefilled-variable).
#### `variables:expand`
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353991) in GitLab 15.6 [with a flag](../../administration/feature_flags.md) named `ci_raw_variables_in_yaml_config`. Disabled by default.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353991) in GitLab 15.6 [with a flag](../../administration/feature_flags.md) named `ci_raw_variables_in_yaml_config`. Disabled by default.
+> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/375034) in GitLab 15.6.
+> - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/375034) in GitLab 15.7.
+
+FLAG:
+On self-managed GitLab, by default this feature is available. To hide the feature per project,
+ask an administrator to [disable the feature flag](../../administration/feature_flags.md) named `ci_raw_variables_in_yaml_config`.
Use the `expand` keyword to configure a variable to be expandable or not.
diff --git a/doc/ci/yaml/yaml_optimization.md b/doc/ci/yaml/yaml_optimization.md
index f4774619713..5344a999b95 100644
--- a/doc/ci/yaml/yaml_optimization.md
+++ b/doc/ci/yaml/yaml_optimization.md
@@ -473,3 +473,27 @@ nested-references:
```
In this example, the `nested-references` job runs all three `echo` commands.
+
+### Configure your IDE to support `!reference` tags
+
+The [pipeline editor](../pipeline_editor/index.md) supports `!reference` tags. However, the schema rules for custom YAML
+tags like `!reference` might be treated as invalid by your editor by default.
+You can configure some editors to accept `!reference` tags. For example:
+
+- In VS Code, you can set `vscode-yaml` to parse `customTags` in your `settings.json` file:
+
+ ```json
+ "yaml.customTags": [
+ "!reference sequence"
+ ]
+ ```
+
+- In Sublime Text, if you are using the `LSP-yaml` package, you can set `customTags` in your `LSP-yaml` user settings:
+
+ ```json
+ {
+ "settings": {
+ "yaml.customTags": ["!reference sequence"]
+ }
+ }
+ ```
diff --git a/doc/development/adding_database_indexes.md b/doc/development/adding_database_indexes.md
deleted file mode 100644
index 7ab846cce3e..00000000000
--- a/doc/development/adding_database_indexes.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/adding_database_indexes.md'
-remove_date: '2022-11-05'
----
-
-This document was moved to [another location](database/adding_database_indexes.md).
-
-<!-- This redirect file can be deleted after <2022-11-05>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md
index c19341a1404..e0db0d7e34d 100644
--- a/doc/development/api_graphql_styleguide.md
+++ b/doc/development/api_graphql_styleguide.md
@@ -2115,7 +2115,7 @@ end
Authenticating a user with the `current_user:` argument for `post_graphql`
generates more queries on the first request than on subsequent requests on that
same user. If you are testing for N+1 queries using
- [QueryRecorder](query_recorder.md), use a **different** user for each request.
+ [QueryRecorder](database/query_recorder.md), use a **different** user for each request.
The below example shows how a test for avoiding N+1 queries should look:
@@ -2223,7 +2223,7 @@ end
[Adding field with resolver on a Type causes "Can't determine the return type " error on a different Type](https://github.com/rmosolgo/graphql-ruby/issues/3974).
Unfortunately, the errors generated don't really indicate what the problem is. For example,
- remove the quotes from the `Rspec.descrbe` in
+ remove the quotes from the `Rspec.describe` in
[ee/spec/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver_spec.rb](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/spec/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver_spec.rb).
Then run `rspec ee/spec/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver_spec.rb`.
@@ -2263,7 +2263,7 @@ end
1. 95% of the resolver specs use arguments that are Ruby objects, as opposed to when using the GraphQL API
only strings and integers are used. This works fine in most cases.
- 1. If your resolver takes arguments that use a `prepare` proc, such as a resolver that accepts timeframe
+ 1. If your resolver takes arguments that use a `prepare` proc, such as a resolver that accepts time frame
arguments (`TimeFrameArguments`), you must pass the `arg_style: :internal_prepared` parameter into
the `resolve` method. This tells the code to convert the arguments into strings and integers and pass
them through regular argument handling, ensuring that the `prepare` proc is called correctly.
diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md
index f74ed8db50f..006d0a01abb 100644
--- a/doc/development/api_styleguide.md
+++ b/doc/development/api_styleguide.md
@@ -152,6 +152,34 @@ For non-200 HTTP responses, use the provided helpers in `lib/api/helpers.rb` to
For `DELETE` requests, you should also generally use the `destroy_conditionally!` helper which by default returns a `204 No Content` response on success, or a `412 Precondition Failed` response if the given `If-Unmodified-Since` header is out of range. This helper calls `#destroy` on the passed resource, but you can also implement a custom deletion method by passing a block.
+## Choosing HTTP verbs
+
+When defining a new [API route](https://github.com/ruby-grape/grape#routes), use
+the correct [HTTP request method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods).
+
+### Deciding between `PATCH` and `PUT`
+
+In a Rails application, both the `PATCH` and `PUT` request methods are routed to
+the `update` method in controllers. With Grape, the framework we use to write
+the GitLab API, you must explicitly set the `PATCH` or `PUT` HTTP verb for an
+endpoint that does updates.
+
+If the endpoint updates *all* attributes of a given resource, use the
+[`PUT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) request
+method. If the endpoint updates *some* attributes of a given resource, use the
+[`PATCH`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH)
+request method.
+
+Here is a good example for `PATCH`: [`PATCH /projects/:id/protected_branches/:name`](../api/protected_branches.md#update-a-protected-branch)
+Here is a good example for `PUT`: [`PUT /projects/:id/merge_requests/:merge_request_iid/approve`](../api/merge_request_approvals.md#approve-merge-request)
+
+Often, a good `PUT` endpoint only has ids and a verb (in the example above, "approve").
+Or, they only have a single value and represent a key/value pair.
+
+The [Rails blog](https://rubyonrails.org/2012/2/26/edge-rails-patch-is-the-new-primary-http-method-for-updates)
+has a detailed explanation of why `PATCH` is usually the most apt verb for web
+API endpoints that perform an update.
+
## Using API path helpers in GitLab Rails codebase
Because we support [installing GitLab under a relative URL](../install/relative_url.md), one must take this
@@ -271,7 +299,7 @@ which introduced the scope.
When an API endpoint returns collections, always add a test to verify
that the API endpoint does not have an N+1 problem, now and in the future.
-We can do this using [`ActiveRecord::QueryRecorder`](query_recorder.md).
+We can do this using [`ActiveRecord::QueryRecorder`](database/query_recorder.md).
Example:
diff --git a/doc/development/application_limits.md b/doc/development/application_limits.md
index b64d25ccf64..b1efc11db62 100644
--- a/doc/development/application_limits.md
+++ b/doc/development/application_limits.md
@@ -38,7 +38,7 @@ It's recommended to create two separate migration script files.
desired limit using `create_or_update_plan_limit` migration helper, such as:
```ruby
- class InsertProjectHooksPlanLimits < Gitlab::Database::Migration[2.0]
+ class InsertProjectHooksPlanLimits < Gitlab::Database::Migration[2.1]
def up
create_or_update_plan_limit('project_hooks', 'default', 0)
create_or_update_plan_limit('project_hooks', 'free', 10)
diff --git a/doc/development/application_slis/rails_request_apdex.md b/doc/development/application_slis/rails_request_apdex.md
index 2fa9f5f4869..8fcd725f74d 100644
--- a/doc/development/application_slis/rails_request_apdex.md
+++ b/doc/development/application_slis/rails_request_apdex.md
@@ -108,7 +108,7 @@ a case-by-case basis. Take the following into account:
should try to keep as short as possible.
1. Traffic characteristics should also be taken into account. If the
- traffic to the endpoint is bursty, like CI traffic spinning up a
+ traffic to the endpoint sometimes bursts, like CI traffic spinning up a
big batch of jobs hitting the same endpoint, then having these
endpoints take five seconds is unacceptable from an infrastructure point of
view. We cannot scale up the fleet fast enough to accommodate for
diff --git a/doc/development/approval_rules.md b/doc/development/approval_rules.md
index ce774b1e8f9..312bf2b1bb7 100644
--- a/doc/development/approval_rules.md
+++ b/doc/development/approval_rules.md
@@ -1,6 +1,6 @@
---
stage: Create
-group: Source Code
+group: Code Review
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
@@ -221,7 +221,7 @@ It is responsible for parsing `approval_rules_attributes` parameter to:
- Filter the group IDs whether they are visible to user.
- Identify the `any_approver` rule.
- Append hidden groups to it when specified.
-- Append user defined inapplicable (rules that does not apply to MR's target
+- Append user defined inapplicable (rules that do not apply to the merge request's target
branch) approval rules.
## Flow
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index a731e661a80..5eb1dcc3208 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -592,8 +592,6 @@ Grafana is an open source, feature rich metrics dashboard and graph editor for G
Jaeger, inspired by Dapper and OpenZipkin, is a distributed tracing system.
It can be used for monitoring microservices-based distributed systems.
-For monitoring deployed apps, see [Jaeger tracing documentation](../operations/tracing.md)
-
#### Logrotate
- [Project page](https://github.com/logrotate/logrotate/blob/master/README.md)
@@ -626,7 +624,7 @@ Mattermost is an open source, private cloud, Slack-alternative from <https://mat
- Layer: Core Service (Data)
- GitLab.com: [Storage Architecture](https://about.gitlab.com/handbook/engineering/infrastructure/production/architecture/#storage-architecture)
-MinIO is an object storage server released under the GNU AGPL v3.0. It is compatible with Amazon S3 cloud storage service. It is best suited for storing unstructured data such as photos, videos, log files, backups, and container / VM images. Size of an object can range from a few KBs to a maximum of 5TB.
+MinIO is an object storage server released under the GNU AGPL v3.0. It is compatible with Amazon S3 cloud storage service. It is best suited for storing unstructured data such as photos, videos, log files, backups, and container / VM images. Size of an object can range from a few KB to a maximum of 5 TB.
#### NGINX
diff --git a/doc/development/caching.md b/doc/development/caching.md
index 36fbfc7010e..58ec7a77591 100644
--- a/doc/development/caching.md
+++ b/doc/development/caching.md
@@ -80,7 +80,7 @@ indicates we have plenty of headroom.
- Generic data can be cached for everyone.
- You must keep this in mind when building new features.
1. Try to preserve cache data as much as possible:
- - Use nested caches to maintain as much cached data as possible across expiries.
+ - Use nested caches to maintain as much cached data as possible across expires.
1. Perform as few requests to the cache as possible:
- This reduces variable latency caused by network issues.
- Lower overhead for each read on the cache.
@@ -166,7 +166,7 @@ Is the cache being added "worthy"? This can be hard to measure, but you can cons
- Calling the same method multiple times but only calculating the value once.
- Stored in Ruby memory.
- `@article ||= Article.find(params[:id])`
- - `strong_memoize { Article.find(params[:id]) }`
+ - `strong_memoize_attr :method_name`
1. Request caching:
- Return the same value for a key for the duration of a web request.
- `Gitlab::SafeRequestStore.fetch`
@@ -252,7 +252,7 @@ All the time!
### When to use method caching
-- Using instance variables, or [strong_memoize](utilities.md#strongmemoize) is something we all tend to do anyway.
+- Use instance variables, or [`StrongMemoize`](utilities.md#strongmemoize).
- Useful when the same value is needed multiple times in a request.
- Can be used to prevent multiple cache calls for the same key.
- Can cause issues with ActiveRecord objects where a value doesn't change until you call
diff --git a/doc/development/cascading_settings.md b/doc/development/cascading_settings.md
index 1a0f0ec5b5f..389623e68d8 100644
--- a/doc/development/cascading_settings.md
+++ b/doc/development/cascading_settings.md
@@ -38,7 +38,7 @@ Settings are not cascading by default. To define a cascading setting, take the f
`application_settings`.
```ruby
- class AddDelayedProjectRemovalCascadingSetting < Gitlab::Database::Migration[2.0]
+ class AddDelayedProjectRemovalCascadingSetting < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::CascadingNamespaceSettings
enable_lock_retries!
@@ -153,7 +153,7 @@ Renders the label for a checkbox setting.
[`_setting_label_fieldset.html.haml`](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/views/shared/namespaces/cascading_settings/_setting_label_fieldset.html.haml)
-Renders the label for a fieldset setting.
+Renders the label for a `fieldset` setting.
| Local | Description | Type | Required (default value) |
|:-----------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------|:-------------------------|
diff --git a/doc/development/cicd/pipeline_wizard.md b/doc/development/cicd/pipeline_wizard.md
index eb6d1c60bb1..213a9fe1762 100644
--- a/doc/development/cicd/pipeline_wizard.md
+++ b/doc/development/cicd/pipeline_wizard.md
@@ -127,7 +127,7 @@ is planned to add the ability to create a MR from here.
### Props
-- `template` (required): The template content as an unparsed String. See
+- `template` (required): The template content as an un-parsed string. See
[Template file location](#template-file-location) for more information.
- `project-path` (required): The full path of the project the final file
should be committed to
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 90f33319365..30d9d671038 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -28,7 +28,7 @@ The reviewer can:
- Give you a second opinion on the chosen solution and implementation.
- Help look for bugs, logic problems, or uncovered edge cases.
-If the merge request is trivial (for example, fixing a typo or a tiny refactor that doesn't change the behavior or any data),
+If the merge request is trivial to review (for example, fixing a typo or a tiny refactor that doesn't change the behavior or any data),
you can skip the reviewer step and directly ask a [maintainer](https://about.gitlab.com/handbook/engineering/workflow/code-review/#maintainer).
Otherwise, a merge request should always be first reviewed by a reviewer in each
[category (e.g. backend, database)](#approval-guidelines)
@@ -124,8 +124,10 @@ page, with these behaviors:
branch name (unless their out-of-office (`OOO`) status changes, as in point 1). It
removes leading `ce-` and `ee-`, and trailing `-ce` and `-ee`, so
that it can be stable for backport branches.
+- People whose Slack or [GitLab status](../user/profile/index.md#set-your-current-status) emoji
+ is â“‚ `:m:`are only suggested as reviewers on projects they are a maintainer of.
-The [Roulette dashboard](https://gitlab-org.gitlab.io/gitlab-roulette) contains:
+The [Roulette dashboard](https://gitlab-org.gitlab.io/gitlab-roulette/) contains:
- Assignment events in the last 7 and 30 days.
- Currently assigned merge requests per person.
@@ -155,7 +157,7 @@ with [domain expertise](#domain-experts).
| `~frontend` changes (*1*) | [Frontend maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_maintainers_frontend). |
| `~UX` user-facing changes (*3*) | [Product Designer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_reviewers_UX). Refer to the [design and user interface guidelines](contributing/design.md) for details. |
| Adding a new JavaScript library (*1*) | - [Frontend foundations member](https://about.gitlab.com/direction/manage/foundations/) if the library significantly increases the [bundle size](https://gitlab.com/gitlab-org/frontend/playground/webpack-memory-metrics/-/blob/master/doc/report.md).<br/>- A [legal department member](https://about.gitlab.com/handbook/legal/) if the license used by the new library hasn't been approved for use in GitLab.<br/><br/>More information about license compatibility can be found in our [GitLab Licensing and Compatibility documentation](licensing.md). |
-| A new dependency or a file system change | - [Distribution team member](https://about.gitlab.com/company/team/). See how to work with the [Distribution team](https://about.gitlab.com/handbook/engineering/development/enablement/systems/distribution/#how-to-work-with-distribution) for more details.<br/>- For Rubygems, request an [AppSec review](gemfile.md#request-an-appsec-review). |
+| A new dependency or a file system change | - [Distribution team member](https://about.gitlab.com/company/team/). See how to work with the [Distribution team](https://about.gitlab.com/handbook/engineering/development/enablement/systems/distribution/#how-to-work-with-distribution) for more details.<br/>- For RubyGems, request an [AppSec review](gemfile.md#request-an-appsec-review). |
| `~documentation` or `~UI text` changes | [Technical writer](https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments) based on assignments in the appropriate [DevOps stage group](https://about.gitlab.com/handbook/product/categories/#devops-stages). |
| Changes to development guidelines | Follow the [review process](development_processes.md#development-guidelines-review) and get the approvals accordingly. |
| End-to-end **and** non-end-to-end changes (*4*) | [Software Engineer in Test](https://about.gitlab.com/handbook/engineering/quality/#individual-contributors). |
@@ -185,49 +187,52 @@ Using checklists improves quality in software engineering. This checklist is a s
See the [test engineering process](https://about.gitlab.com/handbook/engineering/quality/quality-engineering/test-engineering/) for further quality guidelines.
-1. I have self-reviewed this MR per [code review guidelines](code_review.md).
-1. For the code that this change impacts, I believe that the automated tests ([Testing Guide](testing_guide/index.md)) validate functionality that is highly important to users (including consideration of [all test levels](testing_guide/testing_levels.md)).
-1. If the existing automated tests do not cover the above functionality, I have added the necessary additional tests or added an issue to describe the automation testing gap and linked it to this MR.
-1. I have considered the technical aspects of this change's impact on GitLab.com hosted customers and self-managed customers.
-1. I have considered the impact of this change on the frontend, backend, and database portions of the system where appropriate and applied the `~ux`, `~frontend`, `~backend`, and `~database` labels accordingly.
-1. I have tested this MR in [all supported browsers](../install/requirements.md#supported-web-browsers), or determined that this testing is not needed.
-1. I have confirmed that this change is [backwards compatible across updates](multi_version_compatibility.md), or I have decided that this does not apply.
-1. I have properly separated EE content from FOSS, or this MR is FOSS only.
+1. You have self-reviewed this MR per [code review guidelines](code_review.md).
+1. For the code that this change impacts, you believe that the automated tests ([Testing Guide](testing_guide/index.md)) validate functionality that is highly important to users (including consideration of [all test levels](testing_guide/testing_levels.md)).
+1. If the existing automated tests do not cover the above functionality, you have added the necessary additional tests or added an issue to describe the automation testing gap and linked it to this MR.
+1. You have considered the technical aspects of this change's impact on GitLab.com hosted customers and self-managed customers.
+1. You have considered the impact of this change on the frontend, backend, and database portions of the system where appropriate and applied the `~ux`, `~frontend`, `~backend`, and `~database` labels accordingly.
+1. You have tested this MR in [all supported browsers](../install/requirements.md#supported-web-browsers), or determined that this testing is not needed.
+1. You have confirmed that this change is [backwards compatible across updates](multi_version_compatibility.md), or you have decided that this does not apply.
+1. You have properly separated EE content from FOSS, or this MR is FOSS only.
- [Where should EE code go?](ee_features.md)
-1. I have considered that existing data may be surprisingly varied. For example, a new model validation can break existing records. Consider making validation on existing data optional rather than required if you haven't confirmed that existing data will pass validation.
+1. You have considered that existing data may be surprisingly varied. For example, a new model validation can break existing records. Consider making validation on existing data optional rather than required if you haven't confirmed that existing data will pass validation.
##### Performance, reliability, and availability
-1. I am confident that this MR does not harm performance, or I have asked a reviewer to help assess the performance impact. ([Merge request performance guidelines](merge_request_performance_guidelines.md))
-1. I have added [information for database reviewers in the MR description](database_review.md#required), or I have decided that it is unnecessary.
+1. You are confident that this MR does not harm performance, or you have asked a reviewer to help assess the performance impact. ([Merge request performance guidelines](merge_request_performance_guidelines.md))
+1. You have added [information for database reviewers in the MR description](database_review.md#required), or you have decided that it is unnecessary.
- [Does this MR have database-related changes?](database_review.md)
-1. I have considered the availability and reliability risks of this change.
-1. I have considered the scalability risk based on future predicted growth.
-1. I have considered the performance, reliability, and availability impacts of this change on large customers who may have significantly more data than the average customer.
+1. You have considered the availability and reliability risks of this change.
+1. You have considered the scalability risk based on future predicted growth.
+1. You have considered the performance, reliability, and availability impacts of this change on large customers who may have significantly more data than the average customer.
##### Observability instrumentation
-1. I have included enough instrumentation to facilitate debugging and proactive performance improvements through observability.
+1. You have included enough instrumentation to facilitate debugging and proactive performance improvements through observability.
See [example](https://gitlab.com/gitlab-org/gitlab/-/issues/346124#expectations) of adding feature flags, logging, and instrumentation.
##### Documentation
-1. I have included changelog trailers, or I have decided that they are not needed.
+1. You have included changelog trailers, or you have decided that they are not needed.
- [Does this MR need a changelog?](changelog.md#what-warrants-a-changelog-entry)
-1. I have added/updated documentation or decided that documentation changes are unnecessary for this MR.
+1. You have added/updated documentation or decided that documentation changes are unnecessary for this MR.
- [Is documentation required?](https://about.gitlab.com/handbook/product/ux/technical-writing/workflow/#when-documentation-is-required)
##### Security
-1. I have confirmed that if this MR contains changes to processing or storing of credentials or tokens, authorization, and authentication methods, or other items described in [the security review guidelines](https://about.gitlab.com/handbook/security/#when-to-request-a-security-review), I have added the `~security` label and I have `@`-mentioned `@gitlab-com/gl-security/appsec`.
-1. I have reviewed the documentation regarding [internal application security reviews](https://about.gitlab.com/handbook/security/#internal-application-security-reviews) for **when** and **how** to request a security review and requested a security review if this is warranted for this change.
+1. You have confirmed that if this MR contains changes to processing or storing of credentials or tokens, authorization, and authentication methods, or other items described in [the security review guidelines](https://about.gitlab.com/handbook/security/#when-to-request-a-security-review), you have added the `~security` label and you have `@`-mentioned `@gitlab-com/gl-security/appsec`.
+1. You have reviewed the documentation regarding [internal application security reviews](https://about.gitlab.com/handbook/security/#internal-application-security-reviews) for **when** and **how** to request a security review and requested a security review if this is warranted for this change.
+1. If there are security scan results that are blocking the MR (due to the [scan result policies](https://gitlab.com/gitlab-com/gl-security/security-policies)):
+ - For true positive findings, they should be corrected before the merge request is merged. This will remove the AppSec approval required by the scan result policy.
+ - For false positive findings, something that should be discussed for risk acceptance, or anything questionable, please ping `@gitlab-com/gl-security/appsec`.
##### Deployment
-1. I have considered using a feature flag for this change because the change may be high risk.
-1. If I am using a feature flag, I plan to test the change in staging before I test it in production, and I have considered rolling it out to a subset of production customers before rolling it out to all customers.
+1. You have considered using a feature flag for this change because the change may be high risk.
+1. If you are using a feature flag, you plan to test the change in staging before you test it in production, and you have considered rolling it out to a subset of production customers before rolling it out to all customers.
- [When to use a feature flag](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle/#when-to-use-feature-flags)
-1. I have informed the Infrastructure department of a default setting or new setting change per [definition of done](contributing/merge_request_workflow.md#definition-of-done), or decided that this is unnecessary.
+1. You have informed the Infrastructure department of a default setting or new setting change per [definition of done](contributing/merge_request_workflow.md#definition-of-done), or decided that this is unnecessary.
### The responsibility of the merge request author
diff --git a/doc/development/contributing/design.md b/doc/development/contributing/design.md
index e6b6b56cf73..b8d7a8eef39 100644
--- a/doc/development/contributing/design.md
+++ b/doc/development/contributing/design.md
@@ -24,8 +24,17 @@ screenshots (or videos) of your changes in the description, as explained in our
[MR workflow](merge_request_workflow.md). These screenshots/videos are very helpful
for all reviewers and can speed up the review process, especially if the changes
are small.
-- Attach the ~UX label to any merge request that impacts the user experience. This will enable Product Designers to [review](https://about.gitlab.com/handbook/product/ux/product-designer/mr-reviews/#stage-group-mrs/) any user facing changes.
-- Assign the Product Designer suggested by Reviewer Roulette as the reviewer of your merge request. The reviewer does not have to be the domain expert unless this is a community contribution.
+- Attach the ~UX label to any merge request that has any user facing changes. This will trigger our
+Reviewer Roulette to suggest a UX [reviewer](https://about.gitlab.com/handbook/product/ux/product-designer/mr-reviews/#stage-group-mrs).
+
+If you are a **team member**: We recommend assigning the Product Designer suggested by the
+[Reviewer Roulette](../code_review.md#reviewer-roulette) as reviewer. [This helps us](https://about.gitlab.com/handbook/product/ux/product-designer/mr-reviews/#benefits) spread work evenly, improve communication, and make our UI more
+consistent. If you have a reason to choose a different reviewer, add a comment to mention you assigned
+it to a Product Designer of your choice.
+
+If you are a **community contributor**: We favor choosing the Product Designer that is a
+[domain expert](../code_review.md#domain-experts) in the area you are contributing, to regardless
+of the Reviewer Roulette.
## Checklist
diff --git a/doc/development/contributing/index.md b/doc/development/contributing/index.md
index 8c0d18f877b..1a5b801a95a 100644
--- a/doc/development/contributing/index.md
+++ b/doc/development/contributing/index.md
@@ -100,7 +100,7 @@ If you have any questions or need help, visit [Getting Help](https://about.gitla
communicate with the GitLab community. GitLab prefers [asynchronous communication](https://about.gitlab.com/handbook/communication/#internal-communication) over real-time communication.
We do encourage you to connect and hang out with us. GitLab has a Gitter room dedicated for [contributors](https://gitter.im/gitlab/contributors), which is bridged with our
-internal Slack. We actively monitor this channel. There is also a community-run [Discord server](http://discord.gg/gitlab) where you can
+internal Slack. We actively monitor this channel. There is also a community-run [Discord server](https://discord.gg/gitlab) where you can
find other contributors in the `#contributors` channel.
Thanks for your contribution!
@@ -229,4 +229,4 @@ For information on how to contribute documentation, see GitLab
## Getting an Enterprise Edition License
If you need a license for contributing to an EE-feature, see
-[relevant information](https://about.gitlab.com/handbook/marketing/community-relations/code-contributor-program/#contributing-to-the-gitlab-enterprise-edition-ee).
+[relevant information](https://about.gitlab.com/handbook/marketing/community-relations/code-contributor-program/operations/#contributing-to-the-gitlab-enterprise-edition-ee).
diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md
index f2c06e289c9..f06e8825660 100644
--- a/doc/development/contributing/merge_request_workflow.md
+++ b/doc/development/contributing/merge_request_workflow.md
@@ -213,6 +213,7 @@ To reach the definition of done, the merge request must create no regressions an
- Verified as working in production on GitLab.com.
- Verified as working for self-managed instances.
+- Verified as supporting [Geo](../../administration/geo/index.md) via the [self-service framework](../geo/framework.md). To learn more see [here](../geo/framework.md#geo-is-a-requirement-in-the-definition-of-done).
If a regression occurs, we prefer you revert the change.
Your contribution is *incomplete* until you have made sure it meets all of these
@@ -313,7 +314,7 @@ The following items are checked after the merge request has been merged:
1. Confirmed to be working in staging before implementing the change in production, where possible.
1. Confirmed to be working in the production with no new [Sentry](https://about.gitlab.com/handbook/engineering/monitoring/#sentry) errors after the contribution is deployed.
1. Confirmed that the [rollout plan](https://about.gitlab.com/handbook/engineering/development/processes/rollout-plans/) has been completed.
-1. If there is a performance risk in the change, I have analyzed the performance of the system before and after the change.
+1. If there is a performance risk in the change, you have analyzed the performance of the system before and after the change.
1. *If the merge request uses feature flags, per-project or per-group enablement, and a staged rollout:*
- Confirmed to be working on GitLab projects.
- Confirmed to be working at each stage for all projects added.
diff --git a/doc/development/contributing/style_guides.md b/doc/development/contributing/style_guides.md
index fe1aa8449c2..0a030d1f3bc 100644
--- a/doc/development/contributing/style_guides.md
+++ b/doc/development/contributing/style_guides.md
@@ -47,7 +47,7 @@ We were using Overcommit prior to Lefthook, so you may want to uninstall it firs
bundle exec lefthook run pre-push
```
-This should return the lefthook version and the list of executable commands with output.
+This should return the Lefthook version and the list of executable commands with output.
### Lefthook configuration
diff --git a/doc/development/creating_enums.md b/doc/development/creating_enums.md
deleted file mode 100644
index d3892c4c44e..00000000000
--- a/doc/development/creating_enums.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/creating_enums.md'
-remove_date: '2022-11-06'
----
-
-This document was moved to [another location](database/creating_enums.md).
-
-<!-- This redirect file can be deleted after <2022-11-06>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/dangerbot.md b/doc/development/dangerbot.md
index 52503f2d9c8..b568809ea4e 100644
--- a/doc/development/dangerbot.md
+++ b/doc/development/dangerbot.md
@@ -132,7 +132,7 @@ have had the chance to add to `helper.labels_to_add`.
#### Shared rules and plugins
If the rule or plugin you implement can be useful for other projects, think about
-upstreaming them to the [`gitlab-dangerfiles`](https://gitlab.com/gitlab-org/ruby/gems/gitlab-dangerfiles) project.
+adding them upstream to the [`gitlab-dangerfiles`](https://gitlab.com/gitlab-org/ruby/gems/gitlab-dangerfiles) project.
#### Enable Danger on a project
diff --git a/doc/development/database/add_foreign_key_to_existing_column.md b/doc/development/database/add_foreign_key_to_existing_column.md
index 1609e00531e..07fa8133496 100644
--- a/doc/development/database/add_foreign_key_to_existing_column.md
+++ b/doc/development/database/add_foreign_key_to_existing_column.md
@@ -69,7 +69,7 @@ In the example above, you'd be still able to update records in the `emails` tabl
Migration file for adding `NOT VALID` foreign key:
```ruby
-class AddNotValidForeignKeyToEmailsUser < Gitlab::Database::Migration[2.0]
+class AddNotValidForeignKeyToEmailsUser < Gitlab::Database::Migration[2.1]
def up
add_concurrent_foreign_key :emails, :users, column: :user_id, on_delete: :cascade, validate: false
end
@@ -100,7 +100,7 @@ In case the data volume is higher (>1000 records), it's better to create a backg
Example for cleaning up records in the `emails` table in a database migration:
```ruby
-class RemoveRecordsWithoutUserFromEmailsTable < Gitlab::Database::Migration[2.0]
+class RemoveRecordsWithoutUserFromEmailsTable < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
class Email < ActiveRecord::Base
@@ -133,7 +133,7 @@ Migration file for validating the foreign key:
```ruby
# frozen_string_literal: true
-class ValidateForeignKeyOnEmailUsers < Gitlab::Database::Migration[2.0]
+class ValidateForeignKeyOnEmailUsers < Gitlab::Database::Migration[2.1]
def up
validate_foreign_key :emails, :user_id
end
diff --git a/doc/development/database/adding_database_indexes.md b/doc/development/database/adding_database_indexes.md
index d4cd807ef22..6a401c804f5 100644
--- a/doc/development/database/adding_database_indexes.md
+++ b/doc/development/database/adding_database_indexes.md
@@ -107,11 +107,10 @@ determining whether existing indexes are still required. More information on
the meaning of the various columns can be found at
<https://www.postgresql.org/docs/current/monitoring-stats.html>.
-To determine if an index is still being used on production, use the following
-Thanos query with your index name:
+To determine if an index is still being used on production, use [Thanos](https://thanos-query.ops.gitlab.net/graph?g0.expr=sum%20by%20(type)(rate(pg_stat_user_indexes_idx_scan%7Benv%3D%22gprd%22%2C%20indexrelname%3D%22INSERT%20INDEX%20NAME%20HERE%22%7D%5B30d%5D))&g0.tab=1&g0.stacked=0&g0.range_input=1h&g0.max_source_resolution=0s&g0.deduplicate=1&g0.partial_response=0&g0.store_matches=%5B%5D):
```sql
-sum(rate(pg_stat_user_indexes_idx_tup_read{env="gprd", indexrelname="index_ci_name", type="patroni-ci"}[5m]))
+sum by (type)(rate(pg_stat_user_indexes_idx_scan{env="gprd", indexrelname="INSERT INDEX NAME HERE"}[30d]))
```
Because the query output relies on the actual usage of your database, it
@@ -232,7 +231,7 @@ A Rails migration example:
```ruby
# in db/post_migrate/
-class AddIndexToPartitionedTable < Gitlab::Database::Migration[2.0]
+class AddIndexToPartitionedTable < Gitlab::Database::Migration[2.1]
include Gitlab::Database::PartitioningMigrationHelpers
disable_ddl_transaction!
@@ -355,7 +354,7 @@ Use the asynchronous index helpers on your local environment to test changes for
For very large tables, index destruction can be a challenge to manage.
While `remove_concurrent_index` removes indexes in a way that does not block
normal traffic, it can still be problematic if index destruction runs for
-during autovacuum. Necessary database operations like `autovacuum` cannot run, and
+during `autovacuum`. Necessary database operations like `autovacuum` cannot run, and
the deployment process on GitLab.com is blocked while waiting for index
destruction to finish.
diff --git a/doc/development/database/avoiding_downtime_in_migrations.md b/doc/development/database/avoiding_downtime_in_migrations.md
index 57f5a66a9ee..b34c0bbf728 100644
--- a/doc/development/database/avoiding_downtime_in_migrations.md
+++ b/doc/development/database/avoiding_downtime_in_migrations.md
@@ -82,7 +82,7 @@ to write a migration that removes a column:
In this case, a **transactional migration** can be used. Something as simple as:
```ruby
-class RemoveUsersUpdatedAtColumn < Gitlab::Database::Migration[2.0]
+class RemoveUsersUpdatedAtColumn < Gitlab::Database::Migration[2.1]
def up
remove_column :users, :updated_at
end
@@ -103,7 +103,7 @@ If the `down` method requires adding back any dropped indexes or constraints, th
be done within a transactional migration, then the migration would look like this:
```ruby
-class RemoveUsersUpdatedAtColumn < Gitlab::Database::Migration[2.0]
+class RemoveUsersUpdatedAtColumn < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
@@ -158,7 +158,7 @@ renaming. For example
```ruby
# A regular migration in db/migrate
-class RenameUsersUpdatedAtToUpdatedAtTimestamp < Gitlab::Database::Migration[2.0]
+class RenameUsersUpdatedAtToUpdatedAtTimestamp < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
@@ -186,7 +186,7 @@ We can perform this cleanup using
```ruby
# A post-deployment migration in db/post_migrate
-class CleanupUsersUpdatedAtRename < Gitlab::Database::Migration[2.0]
+class CleanupUsersUpdatedAtRename < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
@@ -233,7 +233,7 @@ as follows:
```ruby
# A regular migration in db/migrate
-class ChangeUsersUsernameStringToText < Gitlab::Database::Migration[2.0]
+class ChangeUsersUsernameStringToText < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
@@ -252,7 +252,7 @@ Next we need to clean up our changes using a post-deployment migration:
```ruby
# A post-deployment migration in db/post_migrate
-class ChangeUsersUsernameStringToTextCleanup < Gitlab::Database::Migration[2.0]
+class ChangeUsersUsernameStringToTextCleanup < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
@@ -319,6 +319,11 @@ This operation is safe as there's no code using the table just yet.
Dropping tables can be done safely using a post-deployment migration, but only
if the application no longer uses the table.
+Add the table to `DELETED_TABLES` in
+[gitlab_schema.rb](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/database/gitlab_schema.rb),
+along with its `gitlab_schema`. Even though the table is deleted, it is still
+referenced in database migrations.
+
## Renaming Tables
Renaming tables requires downtime as an application may continue
@@ -418,7 +423,7 @@ Check how the migration is performing while it's running. Multiple ways to do th
#### 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).
+See how to [check the status of batched background migrations](../../update/background_migrations.md).
#### Query the database
@@ -478,7 +483,7 @@ for batched background migration:
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 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
@@ -499,8 +504,8 @@ If the migration has not completed, the subsequent steps fail anyway. By checkin
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
+1. Create foreign keys (FK) using the `bigint` columns that match the existing FK using the
+`integer` column. Do this both for FK referencing other tables, and FK 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)).
@@ -509,7 +514,7 @@ that is being migrated ([see an example](https://gitlab.com/gitlab-org/gitlab/-/
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)).
+ 1. Remove old foreign keys (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).
diff --git a/doc/development/database/background_migrations.md b/doc/development/database/background_migrations.md
index fe62bbc6b14..457694c7abd 100644
--- a/doc/development/database/background_migrations.md
+++ b/doc/development/database/background_migrations.md
@@ -236,7 +236,7 @@ Next we need a post-deployment migration that schedules the migration for
existing data.
```ruby
-class ScheduleExtractIntegrationsUrl < Gitlab::Database::Migration[2.0]
+class ScheduleExtractIntegrationsUrl < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
MIGRATION = 'ExtractIntegrationsUrl'
@@ -263,7 +263,7 @@ jobs and manually run on any un-migrated rows. Such a migration would look like
this:
```ruby
-class ConsumeRemainingExtractIntegrationsUrlJobs < Gitlab::Database::Migration[2.0]
+class ConsumeRemainingExtractIntegrationsUrlJobs < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
@@ -412,7 +412,7 @@ When looking at the batch execution time versus the delay time, the execution ti
should fit comfortably within the delay time for a few reasons:
- To allow for a variance in query times.
-- To allow autovacuum to catch up after periods of high churn.
+- To allow `autovacuum` to catch up after periods of high churn.
Never try to optimize by fully filling the delay window even if you are confident
the queries themselves have no timing variance.
diff --git a/doc/development/database/batched_background_migrations.md b/doc/development/database/batched_background_migrations.md
index ca11e9c8dd3..71df4da59c3 100644
--- a/doc/development/database/batched_background_migrations.md
+++ b/doc/development/database/batched_background_migrations.md
@@ -285,7 +285,7 @@ In the second (filtered) example, we know exactly 100 will be updated with each
1. In the post-deployment migration, enqueue the batched background migration:
```ruby
- class BackfillNamespaceType < Gitlab::Database::Migration[2.0]
+ class BackfillNamespaceType < Gitlab::Database::Migration[2.1]
MIGRATION = 'BackfillNamespaceType'
DELAY_INTERVAL = 2.minutes
@@ -308,7 +308,7 @@ In the second (filtered) example, we know exactly 100 will be updated with each
NOTE:
When applying additional filters, it is important to ensure they are properly covered by an index to optimize `EachBatch` performance.
-In the example above we need an index on `(type, id)` to support the filters. See [the `EachBatch` docs for more information](../iterating_tables_in_batches.md).
+In the example above we need an index on `(type, id)` to support the filters. See [the `EachBatch` docs for more information](iterating_tables_in_batches.md).
## Example
@@ -366,7 +366,7 @@ background migration.
1. Create a post-deployment migration that queues the migration for existing data:
```ruby
- class QueueBackfillRoutesNamespaceId < Gitlab::Database::Migration[2.0]
+ class QueueBackfillRoutesNamespaceId < Gitlab::Database::Migration[2.1]
MIGRATION = 'BackfillRouteNamespaceId'
DELAY_INTERVAL = 2.minutes
@@ -403,7 +403,7 @@ background migration.
that checks that the batched background migration is completed. For example:
```ruby
- class FinalizeBackfillRouteNamespaceId < Gitlab::Database::Migration[2.0]
+ class FinalizeBackfillRouteNamespaceId < Gitlab::Database::Migration[2.1]
MIGRATION = 'BackfillRouteNamespaceId'
disable_ddl_transaction!
@@ -452,7 +452,7 @@ the batching column.
Database post-migration:
```ruby
-class ProjectsWithIssuesMigration < Gitlab::Database::Migration[2.0]
+class ProjectsWithIssuesMigration < Gitlab::Database::Migration[2.1]
MIGRATION = 'BatchProjectsWithIssues'
INTERVAL = 2.minutes
BATCH_SIZE = 5000
diff --git a/doc/development/database/creating_enums.md b/doc/development/database/creating_enums.md
index 73c3f546728..e2ae36f7481 100644
--- a/doc/development/database/creating_enums.md
+++ b/doc/development/database/creating_enums.md
@@ -79,7 +79,7 @@ This works as-is, however, it has a couple of downside that:
- When it happens, we have to ship a database migration to fix the data integrity,
which might be impossible if you cannot recover the original value.
-Also, you might observe a workaround for this concern by setting an offset in EE's values.
+Also, you might observe a workaround for this concern by setting an offset in the `EE` module's values.
For example, this example sets `1000` as the offset:
```ruby
diff --git a/doc/development/database/database_debugging.md b/doc/development/database/database_debugging.md
index 0d6e9955a19..edc35dd95e8 100644
--- a/doc/development/database/database_debugging.md
+++ b/doc/development/database/database_debugging.md
@@ -53,7 +53,7 @@ bundle exec rake db:reset RAILS_ENV=test
- `bundle exec rake db:migrate:up:main VERSION=20170926203418 RAILS_ENV=development`: Set up a migration
- `bundle exec rake db:migrate:redo:main VERSION=20170926203418 RAILS_ENV=development`: Re-run a specific migration
-Replace `main` in the above commands to execute agains the `ci` database instead of `main`.
+Replace `main` in the above commands to execute against the `ci` database instead of `main`.
## Manually access the database
@@ -107,7 +107,7 @@ The new connection should be working now.
Use these instructions for exploring the GitLab database while developing with the GDK:
1. Install or open [Visual Studio Code](https://code.visualstudio.com/download).
-1. Install the [PostgreSQL VSCode Extension](https://marketplace.visualstudio.com/items?itemName=ckolkman.vscode-postgres).
+1. Install the [PostgreSQL VS Code Extension](https://marketplace.visualstudio.com/items?itemName=ckolkman.vscode-postgres).
1. In Visual Studio Code select **PostgreSQL Explorer** in the left toolbar.
1. In the top bar of the new window, select `+` to **Add Database Connection**, and follow the prompts to fill in the details:
1. **Hostname**: the path to the PostgreSQL folder in your GDK directory (for example `/dev/gitlab-development-kit/postgresql`).
diff --git a/doc/development/database/database_dictionary.md b/doc/development/database/database_dictionary.md
index bd6dbc54316..d74d7e77edb 100644
--- a/doc/development/database/database_dictionary.md
+++ b/doc/development/database/database_dictionary.md
@@ -11,7 +11,8 @@ locate the feature categories responsible for specific database tables.
## Location
-Database dictionary metadata files are stored in the `gitlab` project under `db/docs/`.
+Database dictionary metadata files are stored in the `gitlab` project under `db/docs/` for the `main` and `ci` databases.
+For the `geo` database, the dictionary files are stored under `ee/db/docs/`.
## Example dictionary file
@@ -29,23 +30,43 @@ milestone: '13.0'
## Schema
-| Attribute | Type | Required | Description |
-|----------------------|---------------|----------|--------------------------------------------------------------------------|
-| `table_name` | String | yes | Database table name |
-| `classes` | Array(String) | no | List of classes that respond to `.table_name` with the `table_name` |
-| `feature_categories` | Array(String) | yes | List of feature categories using this table |
-| `description` | String | no | Text description of the information stored in the table and it's purpose |
-| `introduced_by_url` | URL | no | URL to the merge request or commit which introduced this table |
-| `milestone` | String | no | The milestone that introduced this table |
+| Attribute | Type | Required | Description |
+|----------------------------|---------------|----------|-----------------------------------------------------------------------------------|
+| `table_name` / `view_name` | String | yes | Database table name or view name |
+| `classes` | Array(String) | no | List of classes that are associated to this table or view. |
+| `feature_categories` | Array(String) | yes | List of feature categories using this table or view. |
+| `description` | String | no | Text description of the information stored in the table or view, and its purpose. |
+| `introduced_by_url` | URL | no | URL to the merge request or commit which introduced this table or view. |
+| `milestone` | String | no | The milestone that introduced this table or view. |
+| `gitlab_schema` | String | yes | GitLab schema name. |
## Adding tables
-When adding a new table, create a new file under `db/docs/` named
-`<table_name>.yml` containing as much information as you know about the table.
+When adding a new table, create a new file under `db/docs/` for the `main` and `ci` databases.
+For the `geo` database use `ee/db/docs/`.
+Name the file as `<table_name>.yml`, containing as much information as you know about the table.
Include this file in the commit with the migration that creates the table.
## Dropping tables
-When dropping a table, you must remove the metadata file from `db/docs/`
-in the same commit with the migration that drops the table.
+When dropping a table, you must remove the metadata file from `db/docs/` for `main` and `ci` databases.
+For the `geo` database, you must remove the file from `ee/db/docs/`.
+Use the same commit with the migration that drops the table.
+
+## Adding views
+
+When adding a new view, you should:
+
+1. Create a new file for this view in the appropriate directory:
+ - `main` database: `db/docs/views/`
+ - `ci` database: `db/docs/views/`
+ - `geo` database: `ee/db/docs/views/`
+1. Name the file `<view_name>.yml`, and include as much information as you know about the view.
+1. Include this file in the commit with the migration that creates the view.
+
+## Dropping views
+
+When dropping a view, you must remove the metadata file from `db/docs/views/`.
+For the `geo` database, you must remove the file from `ee/db/docs/views/`.
+Use the same commit with the migration that drops the view.
diff --git a/doc/development/database/efficient_in_operator_queries.md b/doc/development/database/efficient_in_operator_queries.md
index fb7ff3c1cc2..78b310ae708 100644
--- a/doc/development/database/efficient_in_operator_queries.md
+++ b/doc/development/database/efficient_in_operator_queries.md
@@ -126,8 +126,7 @@ For very large groups the database queries can easily time out, causing HTTP 500
## Optimizing ordered `IN` queries
-In the talk
-["How to teach an elephant to dance rock'n'roll"](https://www.youtube.com/watch?v=Ha38lcjVyhQ),
+In the talk ["How to teach an elephant to dance rock and roll"](https://www.youtube.com/watch?v=Ha38lcjVyhQ),
Maxim Boguk demonstrated a technique to optimize a special class of ordered `IN` queries,
such as our ordered group-level queries.
@@ -160,7 +159,7 @@ The technique can only optimize `IN` queries that satisfy the following requirem
in the following order: `column_for_the_in_query`, `order by column 1`, and
`order by column 2`.
- The columns in the `ORDER BY` clause are distinct
- (the combination of the columns uniquely identifies one particular column in the table).
+ (the combination of the columns uniquely identifies one particular row in the table).
WARNING:
This technique does not improve the performance of the `COUNT(*)` queries.
@@ -918,11 +917,11 @@ the `LIMIT` is reached or no more data can be found.
Here's an outline of the steps we take in the recursive CTE query
(expressing the steps in SQL is non-trivial but is explained next):
-1. Sort the initial resultset according to the `ORDER BY` clause.
+1. Sort the initial `resultset` according to the `ORDER BY` clause.
1. Pick the top cursor to fetch the record, this is our first record. In the example,
this cursor would be (`2020-01-05`, `3`) for `project_id=9`.
1. We can use (`2020-01-05`, `3`) to fetch the next issue respecting the `ORDER BY` clause
-`project_id=9` filter. This produces an updated resultset.
+`project_id=9` filter. This produces an updated `resultset`.
| `project_ids` | `created_at_values` | `id_values` |
| ------------- | ------------------- | ----------- |
@@ -931,7 +930,7 @@ this cursor would be (`2020-01-05`, `3`) for `project_id=9`.
| 10 | 2020-01-15 | 7 |
| **9** | **2020-01-06** | **6** |
-1. Repeat 1 to 3 with the updated resultset until we have fetched `N=20` records.
+1. Repeat 1 to 3 with the updated `resultset` until we have fetched `N=20` records.
### Initializing the recursive CTE query
diff --git a/doc/development/database/foreign_keys.md b/doc/development/database/foreign_keys.md
index d9506ae614a..25b3d815d7a 100644
--- a/doc/development/database/foreign_keys.md
+++ b/doc/development/database/foreign_keys.md
@@ -49,7 +49,7 @@ To replace a foreign key:
foreign key before removing the old one.
```ruby
- class ReplaceFkOnPackagesPackagesProjectId < Gitlab::Database::Migration[2.0]
+ class ReplaceFkOnPackagesPackagesProjectId < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
NEW_CONSTRAINT_NAME = 'fk_new'
@@ -69,7 +69,7 @@ To replace a foreign key:
1. [Validate the new foreign key](add_foreign_key_to_existing_column.md#validate-the-foreign-key)
```ruby
- class ValidateFkNew < Gitlab::Database::Migration[2.0]
+ class ValidateFkNew < Gitlab::Database::Migration[2.1]
NEW_CONSTRAINT_NAME = 'fk_new'
# foreign key added in <link to MR or path to migration adding new FK>
@@ -86,7 +86,7 @@ To replace a foreign key:
1. Remove the old foreign key:
```ruby
- class RemoveFkOld < Gitlab::Database::Migration[2.0]
+ class RemoveFkOld < Gitlab::Database::Migration[2.1]
OLD_CONSTRAINT_NAME = 'fk_old'
# new foreign key added in <link to MR or path to migration adding new FK>
diff --git a/doc/development/database/index.md b/doc/development/database/index.md
index 87b1b4a9d87..c244d784422 100644
--- a/doc/development/database/index.md
+++ b/doc/development/database/index.md
@@ -75,12 +75,24 @@ info: To determine the technical writer assigned to the Stage/Group associated w
- [Pagination performance guidelines](pagination_performance_guidelines.md)
- [Efficient `IN` operator queries](efficient_in_operator_queries.md)
- [Data layout and access patterns](layout_and_access_patterns.md)
+- [Check for background migrations before upgrading](../../update/background_migrations.md)
## Case studies
- [Database case study: Filtering by label](filtering_by_label.md)
- [Database case study: Namespaces storage statistics](namespaces_storage_statistics.md)
+## PostgreSQL information for GitLab administrators
+
+- [Configure GitLab using an external PostgreSQL service](../../administration/postgresql/external.md)
+- [Configuring PostgreSQL for scaling](../../administration/postgresql/index.md)
+- [Database Load Balancing](../../administration/postgresql/database_load_balancing.md)
+- [Moving GitLab databases to a different PostgreSQL instance](../../administration/postgresql/moving.md)
+- [Replication and failover with Omnibus GitLab](../../administration/postgresql/replication_and_failover.md)
+- [Standalone PostgreSQL using Omnibus GitLab](../../administration/postgresql/standalone.md)
+- [Troubleshooting PostgreSQL](../../administration/troubleshooting/postgresql.md)
+- [Working with the bundled PgBouncer service](../../administration/postgresql/pgbouncer.md)
+
## Miscellaneous
- [Maintenance operations](maintenance_operations.md)
diff --git a/doc/development/database/keyset_pagination.md b/doc/development/database/keyset_pagination.md
index 21bce41012e..42d7458b45a 100644
--- a/doc/development/database/keyset_pagination.md
+++ b/doc/development/database/keyset_pagination.md
@@ -159,7 +159,7 @@ configuration is necessary:
- Function-based ordering.
- Ordering with a custom tie-breaker column, like `iid`.
-These order objects can be defined in the model classes as normal ActiveRecord scopes, there is no special behavior that prevents using these scopes elsewhere (kaminari, background jobs).
+These order objects can be defined in the model classes as normal ActiveRecord scopes, there is no special behavior that prevents using these scopes elsewhere (Kaminari, background jobs).
### `NULLS LAST` ordering
diff --git a/doc/development/database/loose_foreign_keys.md b/doc/development/database/loose_foreign_keys.md
index 962cd2602bc..daa022a3de2 100644
--- a/doc/development/database/loose_foreign_keys.md
+++ b/doc/development/database/loose_foreign_keys.md
@@ -134,7 +134,7 @@ scripts/decomposition/generate-loose-foreign-key -c ci_job_token_project_scope_l
```
To swap all the foreign keys (all having `_id` appended), but not create a new branch (only commit
-the changes) and not create rspecs, run:
+the changes) and not create RSpec tests, run:
```shell
scripts/decomposition/generate-loose-foreign-key -c --no-branch --no-rspec _id
@@ -192,7 +192,7 @@ trigger needs to be configured only once. If the model already has at least one
`loose_foreign_key` definition, then this step can be skipped:
```ruby
-class TrackProjectRecordChanges < Gitlab::Database::Migration[2.0]
+class TrackProjectRecordChanges < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::LooseForeignKeyHelpers
enable_lock_retries!
@@ -227,7 +227,7 @@ trigger. If the foreign key is deleted earlier, there is a good chance of
introducing data inconsistency which needs manual cleanup:
```ruby
-class RemoveProjectsCiPipelineFk < Gitlab::Database::Migration[2.0]
+class RemoveProjectsCiPipelineFk < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
@@ -258,7 +258,7 @@ records in the database.
Migration for removing the trigger:
```ruby
-class UnTrackProjectRecordChanges < Gitlab::Database::Migration[2.0]
+class UnTrackProjectRecordChanges < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::LooseForeignKeyHelpers
enable_lock_retries!
@@ -278,7 +278,7 @@ table however, there is still a chance for having leftover pending records in th
must be removed with an inline data migration.
```ruby
-class RemoveLeftoverProjectDeletions < Gitlab::Database::Migration[2.0]
+class RemoveLeftoverProjectDeletions < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
diff --git a/doc/development/database/migrations_for_multiple_databases.md b/doc/development/database/migrations_for_multiple_databases.md
index b4d2656121b..bc0ef654336 100644
--- a/doc/development/database/migrations_for_multiple_databases.md
+++ b/doc/development/database/migrations_for_multiple_databases.md
@@ -58,7 +58,7 @@ Example migration adding a concurrent index that is treated as change of the str
that is executed on all configured databases.
```ruby
-class AddUserIdAndStateIndexToMergeRequestReviewers < Gitlab::Database::Migration[2.0]
+class AddUserIdAndStateIndexToMergeRequestReviewers < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
INDEX_NAME = 'index_on_merge_request_reviewers_user_id_and_state'
@@ -75,16 +75,24 @@ end
#### Example: Add a new table to store in a single database
-1. Define the [GitLab Schema](multiple_databases.md#gitlab-schema) of the table in [`lib/gitlab/database/gitlab_schemas.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/database/gitlab_schemas.yml):
+1. Add the table to the [database dictionary](database_dictionary.md) in [`db/docs/`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/db/docs):
```yaml
- ssh_signatures: :gitlab_main
+ table_name: ssh_signatures
+ description: Description example
+ introduced_by_url: Merge request link
+ milestone: Milestone example
+ feature_categories:
+ - Feature category example
+ classes:
+ - Class example
+ gitlab_schema: gitlab_main
```
1. Create the table in a schema migration:
```ruby
- class CreateSshSignatures < Gitlab::Database::Migration[2.0]
+ class CreateSshSignatures < Gitlab::Database::Migration[2.1]
def change
create_table :ssh_signatures do |t|
t.timestamps_with_timezone null: false
@@ -125,7 +133,7 @@ Example migration updating `archived` column of `projects` that is executed
only for the database containing `gitlab_main` schema.
```ruby
-class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.0]
+class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_main
@@ -157,7 +165,7 @@ databases. For example, running migration in context of `ci:` and reading featur
from `main:`, as no established connection to another database is present.
```ruby
-class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.0]
+class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_main
@@ -195,7 +203,7 @@ that is marked in `lib/gitlab/database/gitlab_schemas.yml` as `gitlab_shared`.
This migration is executed across all configured databases.
```ruby
-class DeleteAllLooseForeignKeyRecords < Gitlab::Database::Migration[2.0]
+class DeleteAllLooseForeignKeyRecords < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
@@ -211,13 +219,13 @@ end
#### Example: run DML `gitlab_shared` only on the database containing the given `gitlab_schema`
Example migration updating `loose_foreign_keys_deleted_records` table
-that is marked in `lib/gitlab/database/gitlab_schemas.yml` as `gitlab_shared`.
+that is marked in `db/docs/loose_foreign_keys_deleted_records.yml` as `gitlab_shared`.
This migration since it configures restriction on `gitlab_ci` is executed only
in context of database containing `gitlab_ci` schema.
```ruby
-class DeleteCiBuildsLooseForeignKeyRecords < Gitlab::Database::Migration[2.0]
+class DeleteCiBuildsLooseForeignKeyRecords < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_ci
@@ -261,7 +269,7 @@ the `database_tasks: false` set. `gitlab:db:validate_config` always runs before
## Validation
Validation in a nutshell uses [`pg_query`](https://github.com/pganalyze/pg_query) to analyze
-each query and classify tables with information from [`gitlab_schema.yml`](multiple_databases.md#gitlab-schema).
+each query and classify tables with information from [`db/docs/`](database_dictionary.md).
The migration is skipped if the specified `gitlab_schema` is outside of a list of schemas
managed by a given database connection (`Gitlab::Database::gitlab_schemas_for_connection`).
@@ -279,7 +287,7 @@ as part of the migration run and prevent the migration from being completed.
### Exception 1: migration running in DDL mode does DML select
```ruby
-class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.0]
+class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
# Missing:
@@ -310,7 +318,7 @@ running in **DDL** mode, but the executed payload appears to be reading data fro
### Exception 2: migration running in DML mode changes the structure
```ruby
-class AddUserIdAndStateIndexToMergeRequestReviewers < Gitlab::Database::Migration[2.0]
+class AddUserIdAndStateIndexToMergeRequestReviewers < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
# restrict_gitlab_migration if defined indicates DML, it should be removed
@@ -341,7 +349,7 @@ but the executed payload appears to be doing structure changes (DDL).
### Exception 3: migration running in DML mode accesses data from a table in another schema
```ruby
-class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.0]
+class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
# Since it modifies `projects` it should use `gitlab_main`
@@ -372,7 +380,7 @@ data in `gitlab_main`.
### Exception 4: mixing DDL and DML mode
```ruby
-class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.0]
+class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
# This migration is invalid regardless of specification
@@ -435,4 +443,4 @@ tables in any database, just like any ordinary Sidekiq worker can.
## How to determine `gitlab_schema` for a given table
-See [GitLab Schema](multiple_databases.md#gitlab-schema).
+See [database dictionary](database_dictionary.md).
diff --git a/doc/development/database/multiple_databases.md b/doc/development/database/multiple_databases.md
index e5b6cfb8866..d22e3209096 100644
--- a/doc/development/database/multiple_databases.md
+++ b/doc/development/database/multiple_databases.md
@@ -14,9 +14,9 @@ On GitLab.com we are using two separate databases.
## GitLab Schema
For properly discovering allowed patterns between different databases
-the GitLab application implements the `lib/gitlab/database/gitlab_schemas.yml` YAML file.
+the GitLab application implements the [database dictionary](database_dictionary.md).
-This file provides a virtual classification of tables into a `gitlab_schema`
+The database dictionary provides a virtual classification of tables into a `gitlab_schema`
which conceptually is similar to [PostgreSQL Schema](https://www.postgresql.org/docs/current/ddl-schemas.html).
We decided as part of [using database schemas to better isolated CI decomposed features](https://gitlab.com/gitlab-org/gitlab/-/issues/333415)
that we cannot use PostgreSQL schema due to complex migration procedures. Instead we implemented
diff --git a/doc/development/database/not_null_constraints.md b/doc/development/database/not_null_constraints.md
index 53ab9a83d60..77fa23bbb19 100644
--- a/doc/development/database/not_null_constraints.md
+++ b/doc/development/database/not_null_constraints.md
@@ -25,7 +25,7 @@ For example, consider a migration that creates a table with two `NOT NULL` colum
`db/migrate/20200401000001_create_db_guides.rb`:
```ruby
-class CreateDbGuides < Gitlab::Database::Migration[2.0]
+class CreateDbGuides < Gitlab::Database::Migration[2.1]
def change
create_table :db_guides do |t|
t.bigint :stars, default: 0, null: false
@@ -44,7 +44,7 @@ For example, consider a migration that adds a new `NOT NULL` column `active` to
`db/migrate/20200501000001_add_active_to_db_guides.rb`:
```ruby
-class AddExtendedTitleToSprints < Gitlab::Database::Migration[2.0]
+class AddExtendedTitleToSprints < Gitlab::Database::Migration[2.1]
def change
add_column :db_guides, :active, :boolean, default: true, null: false
end
@@ -116,7 +116,7 @@ with `validate: false` in a post-deployment migration,
`db/post_migrate/20200501000001_add_not_null_constraint_to_epics_description.rb`:
```ruby
-class AddNotNullConstraintToEpicsDescription < Gitlab::Database::Migration[2.0]
+class AddNotNullConstraintToEpicsDescription < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
@@ -147,7 +147,7 @@ so we add a post-deployment migration for the 13.0 milestone (current),
`db/post_migrate/20200501000002_cleanup_epics_with_null_description.rb`:
```ruby
-class CleanupEpicsWithNullDescription < Gitlab::Database::Migration[2.0]
+class CleanupEpicsWithNullDescription < Gitlab::Database::Migration[2.1]
# With BATCH_SIZE=1000 and epics.count=29500 on GitLab.com
# - 30 iterations will be run
# - each requires on average ~150ms
@@ -185,7 +185,7 @@ migration helper in a final post-deployment migration,
`db/post_migrate/20200601000001_validate_not_null_constraint_on_epics_description.rb`:
```ruby
-class ValidateNotNullConstraintOnEpicsDescription < Gitlab::Database::Migration[2.0]
+class ValidateNotNullConstraintOnEpicsDescription < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
diff --git a/doc/development/database/pagination_performance_guidelines.md b/doc/development/database/pagination_performance_guidelines.md
index 0f98b50d95c..b06839979da 100644
--- a/doc/development/database/pagination_performance_guidelines.md
+++ b/doc/development/database/pagination_performance_guidelines.md
@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Pagination performance guidelines
-The following document gives a few ideas for improving the pagination (sorting) performance. These apply both on [offset](pagination_guidelines.md#offset-pagination) and [keyset](pagination_guidelines.md#keyset-pagination) paginations.
+The following document gives a few ideas for improving the pagination (sorting) performance. These apply both on [offset](pagination_guidelines.md#offset-pagination) and [keyset](pagination_guidelines.md#keyset-pagination) pagination.
## Tie-breaker column
diff --git a/doc/development/database/polymorphic_associations.md b/doc/development/database/polymorphic_associations.md
index f3c9bf1276f..bb7eb46b448 100644
--- a/doc/development/database/polymorphic_associations.md
+++ b/doc/development/database/polymorphic_associations.md
@@ -98,10 +98,10 @@ AND source_id = 4
Instead such a table should be broken up into separate tables. For example, you
may end up with 4 tables in this case:
-- project_members
-- group_members
-- pending_project_members
-- pending_group_members
+- `project_members`
+- `group_members`
+- `pending_project_members`
+- `pending_group_members`
This makes querying data trivial. For example, to get the members of a group
you'd run:
diff --git a/doc/development/database/query_performance.md b/doc/development/database/query_performance.md
index 61fd80338fe..73a6a40f801 100644
--- a/doc/development/database/query_performance.md
+++ b/doc/development/database/query_performance.md
@@ -15,13 +15,14 @@ When you are optimizing your SQL queries, there are two dimensions to pay attent
## Timing guidelines for queries
-| Query Type | Maximum Query Time | Notes |
-|----|----|---|
-| General queries | `100ms` | This is not a hard limit, but if a query is getting above it, it is important to spend time understanding why it can or cannot be optimized. |
-| Queries in a migration | `100ms` | This is different than the total [migration time](../migration_style_guide.md#how-long-a-migration-should-take). |
-| Concurrent operations in a migration | `5min` | Concurrent operations do not block the database, but they block the GitLab update. This includes operations such as `add_concurrent_index` and `add_concurrent_foreign_key`. |
-| Background migrations | `1s` | |
-| Service Ping | `1s` | See the [Service Ping docs](../service_ping/implement.md) for more details. |
+| Query Type | Maximum Query Time | Notes |
+|-------------------------------------------|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| General queries | `100ms` | This is not a hard limit, but if a query is getting above it, it is important to spend time understanding why it can or cannot be optimized. |
+| Queries in a migration | `100ms` | This is different than the total [migration time](../migration_style_guide.md#how-long-a-migration-should-take). |
+| Concurrent operations in a migration | `5min` | Concurrent operations do not block the database, but they block the GitLab update. This includes operations such as `add_concurrent_index` and `add_concurrent_foreign_key`. |
+| Concurrent operations in a post migration | `20min` | Concurrent operations do not block the database, but they block the GitLab post update process. This includes operations such as `add_concurrent_index` and `add_concurrent_foreign_key`. If index creation exceeds 20 minutes, consider [async index creation](adding_database_indexes.md#create-indexes-asynchronously). |
+| Background migrations | `1s` | |
+| Service Ping | `1s` | See the [Service Ping docs](../service_ping/implement.md) for more details. |
- When analyzing your query's performance, pay attention to if the time you are seeing is on a [cold or warm cache](#cold-and-warm-cache). These guidelines apply for both cache types.
- When working with batched queries, change the range and batch size to see how it effects the query timing and caching.
diff --git a/doc/development/database/query_recorder.md b/doc/development/database/query_recorder.md
index f1540e7e2ae..84bd0fc938f 100644
--- a/doc/development/database/query_recorder.md
+++ b/doc/development/database/query_recorder.md
@@ -47,7 +47,7 @@ the longest common prefix, grouping similar queries together.
In some cases, N+1 specs have been written to include three requests: first one to
warm the cache, second one to establish a control, third one to validate that
-ther are no N+1 queries. Rather than make an extra request to warm the cache, prefer two requests
+there are no N+1 queries. Rather than make an extra request to warm the cache, prefer two requests
(control and test) and configure your test to ignore [cached queries](#cached-queries) in N+1 specs.
## Cached queries
diff --git a/doc/development/database/single_table_inheritance.md b/doc/development/database/single_table_inheritance.md
index 32de1fdea35..dcf696b85bc 100644
--- a/doc/development/database/single_table_inheritance.md
+++ b/doc/development/database/single_table_inheritance.md
@@ -31,7 +31,7 @@ could result in loading unexpected code or associations which may cause unintend
side effects or failures during upgrades.
```ruby
-class SomeMigration < Gitlab::Database::Migration[2.0]
+class SomeMigration < Gitlab::Database::Migration[2.1]
class Services < MigrationRecord
self.table_name = 'services'
self.inheritance_column = :_type_disabled
@@ -47,7 +47,7 @@ This ensures that the migration loads the columns for the migration in isolation
and the helper disables STI by default.
```ruby
-class EnqueueSomeBackgroundMigration < Gitlab::Database::Migration[2.0]
+class EnqueueSomeBackgroundMigration < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
diff --git a/doc/development/database/strings_and_the_text_data_type.md b/doc/development/database/strings_and_the_text_data_type.md
index fb005e51902..47e89c1ce0f 100644
--- a/doc/development/database/strings_and_the_text_data_type.md
+++ b/doc/development/database/strings_and_the_text_data_type.md
@@ -50,7 +50,7 @@ For example, consider a migration that creates a table with two text columns,
`db/migrate/20200401000001_create_db_guides.rb`:
```ruby
-class CreateDbGuides < Gitlab::Database::Migration[2.0]
+class CreateDbGuides < Gitlab::Database::Migration[2.1]
def change
create_table :db_guides do |t|
t.bigint :stars, default: 0, null: false
@@ -74,7 +74,7 @@ For example, consider a migration that adds a new text column `extended_title` t
`db/migrate/20200501000001_add_extended_title_to_sprints.rb`:
```ruby
-class AddExtendedTitleToSprints < Gitlab::Database::Migration[2.0]
+class AddExtendedTitleToSprints < Gitlab::Database::Migration[2.1]
# rubocop:disable Migration/AddLimitToTextColumns
# limit is added in 20200501000002_add_text_limit_to_sprints_extended_title
@@ -89,7 +89,7 @@ A second migration should follow the first one with a limit added to `extended_t
`db/migrate/20200501000002_add_text_limit_to_sprints_extended_title.rb`:
```ruby
-class AddTextLimitToSprintsExtendedTitle < Gitlab::Database::Migration[2.0]
+class AddTextLimitToSprintsExtendedTitle < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
@@ -165,7 +165,7 @@ in a post-deployment migration,
`db/post_migrate/20200501000001_add_text_limit_migration.rb`:
```ruby
-class AddTextLimitMigration < Gitlab::Database::Migration[2.0]
+class AddTextLimitMigration < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
@@ -196,7 +196,7 @@ to add a background migration for the 13.0 milestone (current),
`db/post_migrate/20200501000002_schedule_cap_title_length_on_issues.rb`:
```ruby
-class ScheduleCapTitleLengthOnIssues < Gitlab::Database::Migration[2.0]
+class ScheduleCapTitleLengthOnIssues < Gitlab::Database::Migration[2.1]
# Info on how many records will be affected on GitLab.com
# time each batch needs to run on average, etc ...
BATCH_SIZE = 5000
@@ -236,7 +236,7 @@ helper in a final post-deployment migration,
`db/post_migrate/20200601000001_validate_text_limit_migration.rb`:
```ruby
-class ValidateTextLimitMigration < Gitlab::Database::Migration[2.0]
+class ValidateTextLimitMigration < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
@@ -255,7 +255,7 @@ Increasing text limits on existing database columns can be safely achieved by fi
and then dropping the previous limit:
```ruby
-class ChangeMaintainerNoteLimitInCiRunner < Gitlab::Database::Migration[2.0]
+class ChangeMaintainerNoteLimitInCiRunner < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
diff --git a/doc/development/database/table_partitioning.md b/doc/development/database/table_partitioning.md
index ac715b871da..5f1deb77b6c 100644
--- a/doc/development/database/table_partitioning.md
+++ b/doc/development/database/table_partitioning.md
@@ -173,7 +173,7 @@ An example migration of partitioning the `audit_events` table by its
`created_at` column would look like:
```ruby
-class PartitionAuditEvents < Gitlab::Database::Migration[2.0]
+class PartitionAuditEvents < Gitlab::Database::Migration[2.1]
include Gitlab::Database::PartitioningMigrationHelpers
def up
@@ -200,7 +200,7 @@ into the partitioned copy.
Continuing the above example, the migration would look like:
```ruby
-class BackfillPartitionAuditEvents < Gitlab::Database::Migration[2.0]
+class BackfillPartitionAuditEvents < Gitlab::Database::Migration[2.1]
include Gitlab::Database::PartitioningMigrationHelpers
def up
@@ -233,7 +233,7 @@ failed jobs.
Once again, continuing the example, this migration would look like:
```ruby
-class CleanupPartitionedAuditEventsBackfill < Gitlab::Database::Migration[2.0]
+class CleanupPartitionedAuditEventsBackfill < Gitlab::Database::Migration[2.1]
include Gitlab::Database::PartitioningMigrationHelpers
def up
@@ -273,7 +273,7 @@ Include the partitioning key in the following constraints:
Add the partitioning key column. For example, in a rails migration:
```ruby
-class AddPartitionNumberForPartitioning < Gitlab::Database::Migration[2.0]
+class AddPartitionNumberForPartitioning < Gitlab::Database::Migration[2.1]
enable_lock_retries!
TABLE_NAME = :table_name
@@ -291,7 +291,7 @@ end
Add indexes including the partitioning key column. For example, in a rails migration:
```ruby
-class PrepareIndexesForPartitioning < Gitlab::Database::Migration[2.0]
+class PrepareIndexesForPartitioning < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
TABLE_NAME = :table_name
@@ -312,7 +312,7 @@ end
Swap the primary key including the partitioning key column. For example, in a rails migration:
```ruby
-class PreparePrimaryKeyForPartitioning < Gitlab::Database::Migration[2.0]
+class PreparePrimaryKeyForPartitioning < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
TABLE_NAME = :table_name
@@ -347,7 +347,7 @@ end
Enforce unique indexes including the partitioning key column. For example, in a rails migration:
```ruby
-class PrepareUniqueContraintForPartitioning < Gitlab::Database::Migration[2.0]
+class PrepareUniqueContraintForPartitioning < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
TABLE_NAME = :table_name
@@ -373,7 +373,7 @@ end
Enforce foreign keys including the partitioning key column. For example, in a rails migration:
```ruby
-class PrepareForeignKeyForPartitioning < Gitlab::Database::Migration[2.0]
+class PrepareForeignKeyForPartitioning < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
SOURCE_TABLE_NAME = :source_table_name
@@ -410,7 +410,7 @@ partition by using the following helpers provided by the database team.
For example, using list partitioning in Rails post migrations:
```ruby
-class PrepareTableConstraintsForListPartitioning < Gitlab::Database::Migration[2.0]
+class PrepareTableConstraintsForListPartitioning < Gitlab::Database::Migration[2.1]
include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers
disable_ddl_transaction!
@@ -441,7 +441,7 @@ end
```
```ruby
-class ConvertTableToListPartitioning < Gitlab::Database::Migration[2.0]
+class ConvertTableToListPartitioning < Gitlab::Database::Migration[2.1]
include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers
disable_ddl_transaction!
diff --git a/doc/development/database/understanding_explain_plans.md b/doc/development/database/understanding_explain_plans.md
index fff9d755e9a..094bd6b346f 100644
--- a/doc/development/database/understanding_explain_plans.md
+++ b/doc/development/database/understanding_explain_plans.md
@@ -826,4 +826,4 @@ A more extensive guide on understanding query plans can be found in
the [presentation](https://public.dalibo.com/exports/conferences/_archives/_2012/201211_explain/understanding_explain.pdf)
from [Dalibo.org](https://www.dalibo.com/en/).
-Depesz's blog also has a good [section](https://www.depesz.com/tag/unexplainable/) dedicated to query plans.
+The Depesz blog also has a good [section](https://www.depesz.com/tag/unexplainable/) dedicated to query plans.
diff --git a/doc/development/database_debugging.md b/doc/development/database_debugging.md
deleted file mode 100644
index f18830ee7ca..00000000000
--- a/doc/development/database_debugging.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/database_debugging.md'
-remove_date: '2022-11-06'
----
-
-This document was moved to [another location](database/database_debugging.md).
-
-<!-- This redirect file can be deleted after <2022-11-06>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/database_query_comments.md b/doc/development/database_query_comments.md
deleted file mode 100644
index 7f9def7e567..00000000000
--- a/doc/development/database_query_comments.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/database_query_comments.md'
-remove_date: '2022-11-05'
----
-
-This document was moved to [another location](database/database_query_comments.md).
-
-<!-- This redirect file can be deleted after <2022-11-05>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/database_review.md b/doc/development/database_review.md
index 7fbc48af91c..e66be062986 100644
--- a/doc/development/database_review.md
+++ b/doc/development/database_review.md
@@ -150,7 +150,7 @@ Include in the MR description:
- Write the raw SQL in the MR description. Preferably formatted
nicely with [pgFormatter](https://sqlformat.darold.net) or
- [paste.depesz.com](https://paste.depesz.com) and using regular quotes
+ <https://paste.depesz.com> and using regular quotes
<!-- vale gitlab.NonStandardQuotes = NO -->
(for example, `"projects"."id"`) and avoiding smart quotes (for example, `“projectsâ€.“idâ€`).
<!-- vale gitlab.NonStandardQuotes = YES -->
diff --git a/doc/development/db_dump.md b/doc/development/db_dump.md
deleted file mode 100644
index c632302329a..00000000000
--- a/doc/development/db_dump.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/db_dump.md'
-remove_date: '2022-11-06'
----
-
-This document was moved to [another location](database/db_dump.md).
-
-<!-- This redirect file can be deleted after <2022-11-06>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/directory_structure.md b/doc/development/directory_structure.md
index 27384fa559f..34ee86d9ee5 100644
--- a/doc/development/directory_structure.md
+++ b/doc/development/directory_structure.md
@@ -1,94 +1,11 @@
---
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+redirect_to: 'software_design.md'
+remove_date: '2023-01-24'
---
-# Backend directory structure
+This document was moved to [another location](software_design.md)
-## Use namespaces to define bounded contexts
-
-A healthy application is divided into macro and sub components that represent the contexts at play,
-whether they are related to business domain or infrastructure code.
-
-As GitLab code has so many features and components it's hard to see what contexts are involved.
-We should expect any class to be defined inside a module/namespace that represents the contexts where it operates.
-
-When we namespace classes inside their domain:
-
-- Similar terminology becomes unambiguous as the domain clarifies the meaning:
- For example, `MergeRequests::Diff` and `Notes::Diff`.
-- Top-level namespaces could be associated to one or more groups identified as domain experts.
-- We can better identify the interactions and coupling between components.
- For example, several classes inside `MergeRequests::` domain interact more with `Ci::`
- domain and less with `ImportExport::`.
-
-```ruby
-# bad
-class MyClass
-end
-
-# good
-module MyDomain
- class MyClass
- end
-end
-```
-
-### About namespace naming
-
-A good guideline for naming a top-level namespace (bounded context) is to use the related
-[feature category](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/data/categories.yml). For example, `Continuous Integration` feature category maps to `Ci::` namespace.
-
-Alternatively a new class could be added to `Projects::` or `Groups::` if it's either:
-
-- Strictly related to one of these domains. For example `Projects::Alias`.
-- A new component that does not have yet a more specific domain. In this case, when
- a more explicit domain does emerge we would need to move the class to a more specific
- namespace.
-
-Do not use the [stage or group name](https://about.gitlab.com/handbook/product/categories/#devops-stages)
-since a feature category could be reassigned to a different group in the future.
-
-```ruby
-# bad
-module Create
- class Commit
- end
-end
-
-# good
-module Repositories
- class Commit
- end
-end
-```
-
-On the other hand, a feature category may sometimes be too granular. Features tend to be
-treated differently according to Product and Marketing, while they may share a lot of
-domain models and behavior under the hood. In this case, having too many bounded contexts
-could make them shallow and more coupled with other contexts.
-
-Bounded contexts (or top-level namespaces) can be seen as macro-components in the overall app.
-Good bounded contexts should be [deep](https://medium.com/@nakabonne/depth-of-module-f62dac3c2fdb)
-so consider having nested namespaces to further break down complex parts of the domain.
-For example, `Ci::Config::`.
-
-For example, instead of having separate and granular bounded contexts like: `ContainerScanning::`,
-`ContainerHostSecurity::`, `ContainerNetworkSecurity::`, we could have:
-
-```ruby
-module ContainerSecurity
- module HostSecurity
- end
-
- module NetworkSecurity
- end
-
- module Scanning
- end
-end
-```
-
-If classes that are defined into a namespace have a lot in common with classes in other namespaces,
-chances are that these two namespaces are part of the same bounded context.
+<!-- This redirect file can be deleted after <2023-01-24>. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index d52db71b633..a9f2726ea93 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -12,7 +12,7 @@ The GitLab documentation is [intended as the single source of truth (SSOT)](http
In addition to this page, the following resources can help you craft and contribute to documentation:
- [Style Guide](styleguide/index.md) - What belongs in the docs, language guidelines, Markdown standards to follow, links, and more.
-- [Topic type template](structure.md) - Learn about the different types of topics.
+- [Topic types](topic_types/index.md) - Learn about the different types of topics.
- [Documentation process](workflow.md).
- [Markdown Guide](../../user/markdown.md) - A reference for all Markdown syntax supported by GitLab.
- [Site architecture](site_architecture/index.md) - How <https://docs.gitlab.com> is built.
@@ -159,26 +159,17 @@ You can use a Rake task to update the `CODEOWNERS` file.
To update the `CODEOWNERS` file:
-1. Open a merge request to update
- [the Rake task](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/tasks/gitlab/tw/codeowners.rake)
- with the latest [TW team assignments](https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments).
-1. Assign the merge request to a backend maintainer for review and merge.
-1. After the MR is merged, go to the root of the `gitlab` repository.
-1. Run the Rake task and save the output in a file:
-
- ```shell
- bundle exec rake tw:codeowners > ~/Desktop/updates.md
- ```
-
-1. Open the file (for example, `~/Desktop/updates.md`) and copy everything
- except the errors at the bottom of the file.
-1. Open the [`CODEOWNERS`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/CODEOWNERS)
- file and paste the lines into the `^[Documentation Pages]` section.
-
- WARNING:
- The documentation section is not the last section of the `CODEOWNERS` file. Don't
- delete data that isn't ours!
-
+1. Review the [TW team assignments](https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments)
+ in the [`codeowners.rake`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/tasks/gitlab/tw/codeowners.rake)
+ file. If any assignments have changed:
+ 1. Update the `codeowners.rake` file with the changes.
+ 1. Assign the merge request to a technical writing manager for review and merge.
+1. After the changes to `codeowners.rake` are merged, go to the root of the `gitlab` repository.
+1. Run the Rake task with this command: `bundle exec rake tw:codeowners`
+1. Review the command output for any pages that need attention to
+ their metadata. Handle any needed changes in a separate merge request.
+1. Add the changes to the CODEOWNERS file to Git: `git add .gitlab/CODEOWNERS`
+1. Commit your changes to your branch, and push your branch up to `origin`.
1. Create a merge request and assign it to a technical writing manager for review.
## Move, rename, or delete a page
diff --git a/doc/development/documentation/site_architecture/deployment_process.md b/doc/development/documentation/site_architecture/deployment_process.md
index 18cc27adaaa..2ba69ca0987 100644
--- a/doc/development/documentation/site_architecture/deployment_process.md
+++ b/doc/development/documentation/site_architecture/deployment_process.md
@@ -167,30 +167,5 @@ If you do not have the Maintainer role to perform this task, ask for help in the
## Docker files
-The [`dockerfiles` directory](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/dockerfiles/) contains all needed
-Dockerfiles to build and deploy <https://docs.gitlab.com>. It is heavily inspired by Docker's
-[Dockerfile](https://github.com/docker/docker.github.io/blob/06ed03db13895bfe867761b6fc2ad40acf6026dd/Dockerfile).
-
-| Dockerfile | Docker image | Description |
-|:---------------------------------------------------------------------------------------------------------------------------|:------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| [`bootstrap.Dockerfile`](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/dockerfiles/bootstrap.Dockerfile) | `gitlab-docs:bootstrap` | Contains all the dependencies that are needed to build the website. If the gems are updated and `Gemfile{,.lock}` changes, the image must be rebuilt. |
-| [`builder.onbuild.Dockerfile`](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/dockerfiles/builder.onbuild.Dockerfile) | `gitlab-docs:builder-onbuild` | Base image to build the docs website. It uses `ONBUILD` to perform all steps and depends on `gitlab-docs:bootstrap`. |
-| [`nginx.onbuild.Dockerfile`](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/dockerfiles/nginx.onbuild.Dockerfile) | `gitlab-docs:nginx-onbuild` | Base image to use for building documentation archives. It uses `ONBUILD` to perform all required steps to copy the archive, and relies upon its parent `Dockerfile.builder.onbuild` that is invoked when building single documentation archives (see the `Dockerfile` of each branch) |
-| [`archives.Dockerfile`](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/dockerfiles/archives.Dockerfile) | `gitlab-docs:archives` | Contains all the versions of the website in one archive. It copies all generated HTML files from every version in one location. |
-
-### How to build the images
-
-Although build images are built automatically via GitLab CI/CD, you can build and tag all tooling images locally:
-
-1. Make sure you have [Docker installed](https://docs.docker.com/get-docker/).
-1. Make sure you're in the `dockerfiles/` directory of the `gitlab-docs` repository.
-1. Build the images:
-
- ```shell
- docker build -t registry.gitlab.com/gitlab-org/gitlab-docs:bootstrap -f Dockerfile.bootstrap ../
- docker build -t registry.gitlab.com/gitlab-org/gitlab-docs:builder-onbuild -f Dockerfile.builder.onbuild ../
- docker build -t registry.gitlab.com/gitlab-org/gitlab-docs:nginx-onbuild -f Dockerfile.nginx.onbuild ../
- ```
-
-For each image, there's a manual job under the `images` stage in
-[`.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/.gitlab-ci.yml) which can be invoked at any time.
+The [`dockerfiles` directory](https://gitlab.com/gitlab-org/gitlab-docs/-/tree/main/dockerfiles) contains Dockerfiles needed
+to build, test, and deploy <https://docs.gitlab.com>.
diff --git a/doc/development/documentation/site_architecture/global_nav.md b/doc/development/documentation/site_architecture/global_nav.md
index d95ca720119..ef6f3c0ae18 100644
--- a/doc/development/documentation/site_architecture/global_nav.md
+++ b/doc/development/documentation/site_architecture/global_nav.md
@@ -18,12 +18,14 @@ Global navigation is the left-most pane in the documentation. You can use the
Research shows that people use Google to search for GitLab product documentation. When they land on a result,
we want them to find topics nearby that are related to the content they're reading. The global nav provides this information.
-At the highest level, our global nav is workflow-based. Navigation needs to help users build a mental model of how to use GitLab.
+At the highest level, our global nav is **workflow-based**. Navigation needs to help users build a mental model of how to use GitLab.
The levels under each of the higher workflow-based topics are the names of features.
For example:
**Use GitLab** (_workflow_) **> Build your application** (_workflow_) **> CI/CD** (_feature_) **> Pipelines** (_feature_)
+While some older sections of the nav are alphabetical, the nav should primarily be workflow-based.
+
## Choose the right words for your navigation entry
Before you add an item to the left nav, choose the parts of speech you want to use.
diff --git a/doc/development/documentation/structure.md b/doc/development/documentation/structure.md
deleted file mode 100644
index 35a93f08f66..00000000000
--- a/doc/development/documentation/structure.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'topic_types/index.md'
-remove_date: '2022-11-16'
----
-
-This document was moved to [another location](topic_types/index.md).
-
-<!-- This redirect file can be deleted after <2022-11-16>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> \ No newline at end of file
diff --git a/doc/development/documentation/styleguide/img/tier_badge.png b/doc/development/documentation/styleguide/img/tier_badge.png
deleted file mode 100644
index 5fc38e08172..00000000000
--- a/doc/development/documentation/styleguide/img/tier_badge.png
+++ /dev/null
Binary files differ
diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md
index ef934186981..3e55b334992 100644
--- a/doc/development/documentation/styleguide/index.md
+++ b/doc/development/documentation/styleguide/index.md
@@ -97,7 +97,7 @@ move in this direction, so we can address these issues:
information into a format that is geared toward helping others, rather than
documenting how a feature was implemented.
-GitLab uses these [topic types](../structure.md).
+GitLab uses these [topic types](../topic_types/index.md).
### Link instead of repeating text
@@ -147,7 +147,7 @@ help benefit translation. For example, we:
- [since and because](word_list.md#since)
- [once and after](word_list.md#once)
- [it](word_list.md#it)
-- Avoid [ing](word_list.md#-ing-words) words.
+- Avoid [-ing](word_list.md#-ing-words) words.
[The GitLab voice](#the-gitlab-voice) dictates that we write clearly and directly,
and with translation in mind. [The word list](word_list.md) and our Vale rules
@@ -158,7 +158,7 @@ also aid in consistency, which is important for localization.
All GitLab documentation is written using [Markdown](https://en.wikipedia.org/wiki/Markdown).
The [documentation website](https://docs.gitlab.com) uses [GitLab Kramdown](https://gitlab.com/gitlab-org/gitlab_kramdown),
-a "flavored" Kramdown engine to render pages from Markdown to HTML. The use of Kramdown's
+a "flavored" Kramdown engine to render pages from Markdown to HTML. The use of Kramdown
features is limited by our linters, so, use regular Markdown and follow the rules in the
linked style guide. You can't use Kramdown-specific markup (for example, `{:.class}`).
@@ -345,10 +345,36 @@ Some contractions, however, should be avoided:
<!-- vale gitlab.Possessive = YES -->
+### Possessives
+
+Try to avoid using possessives (`'s`) for proper nouns, like organization or product names.
+
+For example, instead of `Docker's CLI`, use `the Docker CLI`.
+
+For details, see [the Google documentation style guide](https://developers.google.com/style/possessives#product,-feature,-and-company-names).
+
+### Prepositions
+
+Use prepositions at the end of the sentence when needed.
+Dangling or stranded prepositions are fine. For example:
+
+- You can leave the group you're a member of.
+- Share the credentials with users you want to give access to.
+
+These constructions are more casual than the alternatives:
+
+- You can leave the group of which you're a member.
+- Share the credentials with users to which you want to give access.
+
### Acronyms
If you use an acronym, spell it out on first use on a page. You do not need to spell it out more than once on a page.
-When possible, try to avoid acronyms in topic titles.
+
+- **Titles:** Try to avoid acronyms in topic titles, especially if the acronym is not widely used.
+- **Plurals:** Try not to make acronyms plural. For example, use `YAML files`, not `YAMLs`. If you must make an acronym plural, do not use an apostrophe. For example, use `APIs`, not `API's`.
+- **Possessives:** Use caution when making an acronym possessive. If possible,
+ write the sentence to avoid making the acronym possessive. If you must make the
+ acronym possessive, consider spelling out the words.
### Numbers
@@ -383,8 +409,12 @@ when published. Example:
### Emphasis
+<!-- vale gitlab.Spelling = NO -->
+
Use **bold** rather than italic to provide emphasis. GitLab uses a sans-serif font and italic text does not stand out as much as it would in a serif font. For details, see [Butterick's Practical Typography guide on bold or italic](https://practicaltypography.com/bold-or-italic.html).
+<!-- vale gitlab.Spelling = YES -->
+
You can use italics when you are introducing a term for the first time. Otherwise, use bold.
- Use double asterisks (`**`) to mark a word or text in bold (`**bold**`).
@@ -700,7 +730,7 @@ Put the entire link on a single line so that [linters](../testing.md) can find i
### Links in separate repositories
To link to a page in a different repository, use an absolute URL.
-For example, to link from a page in the GitLab repo to the Charts repo,
+For example, to link from a page in the GitLab repository to the Charts repository,
use a URL like `https://docs.gitlab.com/charts/`.
### Anchor links
@@ -737,6 +767,28 @@ in your merge request fails.
### Text for links
+Follow these guidelines for link text.
+
+#### Standard text
+
+As much as possible, use text that follows one of these patterns:
+
+- `For more information, see [LINK TEXT](LINK)`.
+- `To [DO THIS THING], see [LINK TEXT](LINK)`
+
+For example:
+
+- `For more information, see [merge requests](../../../user/project/merge_requests/index.md).`
+- `To create a review app, see [review apps](../../../ci/review_apps/index.md).`
+
+You can expand on this text by using phrases like
+`For more information about this feature, see...`
+
+Do not to use alternate phrases, like `Learn more about...` or
+`To read more...`.
+
+#### Descriptive text rather than `here`
+
Use descriptive text for links, rather than words like `here` or `this page.`
For example, instead of:
@@ -748,6 +800,14 @@ Use:
- `For more information, see [merge requests](LINK)`.
+#### Links to issues
+
+When linking to an issue, include the issue number in the link. For example:
+
+- `For more information, see [issue 12345](LINK).`
+
+Do not use the pound sign (`issue #12345`).
+
### Links to external documentation
When possible, avoid links to external documentation. These links can easily become outdated, and are difficult to maintain.
@@ -1382,10 +1442,12 @@ Here's some other content in tab two.
For tab titles, be brief and consistent. Ensure they are parallel, and start each with a capital letter.
For example:
-- `Omnibus package`, `Helm chart`, `Source`
+- `Linux package (Omnibus)`, `Helm chart (Kubernetes)` (when documenting configuration edits, follow the
+ [configuration edits guide](#configuration-documentation-for-different-installation-methods))
- `15.1 and earlier`, `15.2 and later`
-See [Pajamas](https://design.gitlab.com/components/tabs/#guidelines) for details.
+See [Pajamas](https://design.gitlab.com/components/tabs/#guidelines) for more
+details on tabs.
## Terms
@@ -1414,9 +1476,7 @@ When names change, it is more complicated to search or grep text that has line b
### Product tier badges
Tier badges are displayed as orange text next to a topic title. These badges link to the GitLab
-pricing page. For example:
-
-![Tier badge](img/tier_badge.png)
+pricing page.
You must assign a tier badge:
@@ -1436,17 +1496,17 @@ functionality is described.
#### Available product tier badges
-| Tier in which feature is available | Tier badge |
-|:------------------------------------------------------------------------|:----------------------|
-| GitLab Free self-managed and SaaS, and higher tiers | `**(FREE)**` |
-| GitLab Premium self-managed and SaaS, and their higher tiers | `**(PREMIUM)**` |
-| GitLab Ultimate self-managed and SaaS | `**(ULTIMATE)**` |
-| Only GitLab Free self-managed and higher tiers (no SaaS-based tiers) | `**(FREE SELF)**` |
-| Only GitLab Premium self-managed and higher tiers (no SaaS-based tiers) | `**(PREMIUM SELF)**` |
-| Only GitLab Ultimate self-managed (no SaaS-based tiers) | `**(ULTIMATE SELF)**` |
-| Only GitLab Free SaaS and higher tiers (no self-managed instances) | `**(FREE SAAS)**` |
-| Only GitLab Premium SaaS and higher tiers (no self-managed instances) | `**(PREMIUM SAAS)**` |
-| Only GitLab Ultimate SaaS (no self-managed instances) | `**(ULTIMATE SAAS)**` |
+| Where feature is available | Tier badge |
+|:-----------------------------------------------------------------------------------------|:----------------------|
+| On GitLab self-managed and GitLab SaaS, available in all tiers. | `**(FREE)**` |
+| On GitLab self-managed and GitLab SaaS, available in Premium and Ultimate. | `**(PREMIUM)**` |
+| On GitLab self-managed and GitLab SaaS, available in Ultimate. | `**(ULTIMATE)**` |
+| On GitLab self-managed, available in all tiers. Not available on GitLab SaaS. | `**(FREE SELF)**` |
+| On GitLab self-managed, available in Premium and Ultimate. Not available on GitLab SaaS. | `**(PREMIUM SELF)**` |
+| On GitLab self-managed, available in Ultimate. Not available on GitLab SaaS. | `**(ULTIMATE SELF)**` |
+| On GitLab SaaS, available in all tiers. Not available on self-managed. | `**(FREE SAAS)**` |
+| On GitLab SaaS, available in Premium and Ultimate. Not available on self-managed. | `**(PREMIUM SAAS)**` |
+| On GitLab SaaS, available in Ultimate. Not available on self-managed. | `**(ULTIMATE SAAS)**` |
Topics that are only for instance administrators should be badged `<TIER> SELF`. Instance
administrator documentation often includes sections that mention:
@@ -1487,21 +1547,35 @@ we install Ruby from source. To update the guide for a new Ruby version:
- Replace the sha256sum. It's available on the
[downloads page](https://www.ruby-lang.org/en/downloads/) of the Ruby website.
-### Configuration documentation for source and Omnibus installations
+### Configuration documentation for different installation methods
-GitLab supports two installation methods: installations from source, and Omnibus
-packages. Possible configuration settings include:
+GitLab supports four installation methods:
-- Settings that touch configuration files in `config/`.
-- NGINX settings.
-- Other settings in `lib/support/`.
+- Linux package (Omnibus)
+- Helm chart (Kubernetes)
+- Docker
+- Self-compiled (source)
Configuration procedures can require users to edit configuration files, reconfigure
-GitLab, or restart GitLab. Use these styles to document these steps, replacing
-`PATH/TO` with the appropriate path:
+GitLab, or restart GitLab. In this case:
+
+- Use [tabs](#tabs) to differentiate among the various installation methods.
+- Use the installation methods names exactly as described in the previous list.
+- Use them in the order described below.
+- Indent the code blocks to line up with the list item they belong to.
+- Use the appropriate syntax highlighting for each code block (`ruby`, `shell`, or `yaml`).
+- For the YAML files, always include the parent settings.
+- The final step to reconfigure or restart GitLab can be used verbatim since it's
+ the same every time.
+
+You can copy and paste the following snippet when describing a configuration
+edit:
+<!-- markdownlint-disable tabs-blank-lines -->
````markdown
-**For Omnibus installations**
+::Tabs
+
+:::TabTitle Linux package (Omnibus)
1. Edit `/etc/gitlab/gitlab.rb`:
@@ -1509,32 +1583,159 @@ GitLab, or restart GitLab. Use these styles to document these steps, replacing
external_url "https://gitlab.example.com"
```
-1. Save the file and [reconfigure](PATH/TO/administration/restart_gitlab.md#omnibus-gitlab-reconfigure)
- GitLab for the changes to take effect.
+1. Save the file and reconfigure GitLab:
----
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+:::TabTitle Helm chart (Kubernetes)
+
+1. Export the Helm values:
+
+ ```shell
+ helm get values gitlab > gitlab_values.yaml
+ ```
+
+1. Edit `gitlab_values.yaml`:
+
+ ```yaml
+ global:
+ hosts:
+ gitlab:
+ name: gitlab.example.com
+ ```
+
+1. Save the file and apply the new values:
+
+ ```shell
+ helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab
+ ```
+
+:::TabTitle Docker
+
+1. Edit `docker-compose.yml`:
+
+ ```yaml
+ version: "3.6"
+ services:
+ gitlab:
+ environment:
+ GITLAB_OMNIBUS_CONFIG: |
+ external_url "https://gitlab.example.com"
+ ```
-**For installations from source**
+1. Save the file and restart GitLab:
-1. Edit `config/gitlab.yml`:
+ ```shell
+ docker compose up -d
+ ```
+
+:::TabTitle Self-compiled (source)
+
+1. Edit `/home/git/gitlab/config/gitlab.yml`:
```yaml
- gitlab:
- host: "gitlab.example.com"
+ production: &base
+ gitlab:
+ host: "gitlab.example.com"
+ ```
+
+1. Save the file and restart GitLab:
+
+ ```shell
+ # For systems running systemd
+ sudo systemctl restart gitlab.target
+
+ # For systems running SysV init
+ sudo service gitlab restart
```
-1. Save the file and [restart](PATH/TO/administration/restart_gitlab.md#installations-from-source)
- GitLab for the changes to take effect.
+::EndTabs
````
+<!-- markdownlint-enable tabs-blank-lines -->
+
+It renders as:
+
+::Tabs
+
+:::TabTitle Linux package (Omnibus)
+
+1. Edit `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ external_url "https://gitlab.example.com"
+ ```
+
+1. Save the file and reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+:::TabTitle Helm chart (Kubernetes)
+
+1. Export the Helm values:
+
+ ```shell
+ helm get values gitlab > gitlab_values.yaml
+ ```
+
+1. Edit `gitlab_values.yaml`:
+
+ ```yaml
+ global:
+ hosts:
+ gitlab:
+ name: gitlab.example.com
+ ```
+
+1. Save the file and apply the new values:
+
+ ```shell
+ helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab
+ ```
+
+:::TabTitle Docker
+
+1. Edit `docker-compose.yml`:
+
+ ```yaml
+ version: "3.6"
+ services:
+ gitlab:
+ environment:
+ GITLAB_OMNIBUS_CONFIG: |
+ external_url "https://gitlab.example.com"
+ ```
-In this case:
+1. Save the file and restart GitLab:
-- Bold the installation method's name.
-- Separate the methods with three dashes (`---`) to create a horizontal line.
-- Indent the code blocks to line up with the list item they belong to..
-- Use the appropriate syntax highlighting for each code block.
-- Use the [GitLab Restart](#gitlab-restart) section to explain any required
- restart or reconfigure of GitLab.
+ ```shell
+ docker compose up -d
+ ```
+
+:::TabTitle Self-compiled (source)
+
+1. Edit `/home/git/gitlab/config/gitlab.yml`:
+
+ ```yaml
+ production: &base
+ gitlab:
+ host: "gitlab.example.com"
+ ```
+
+1. Save the file and restart GitLab:
+
+ ```shell
+ # For systems running systemd
+ sudo systemctl restart gitlab.target
+
+ # For systems running SysV init
+ sudo service gitlab restart
+ ```
+
+::EndTabs
## Feature flags
diff --git a/doc/development/documentation/styleguide/word_list.md b/doc/development/documentation/styleguide/word_list.md
index d28972a644b..333a5521536 100644
--- a/doc/development/documentation/styleguide/word_list.md
+++ b/doc/development/documentation/styleguide/word_list.md
@@ -745,6 +745,11 @@ For **MB** and **GB**, follow the [Microsoft guidance](https://learn.microsoft.c
Do not use first-person singular. Use **you**, **we**, or **us** instead. ([Vale](../testing.md#vale) rule: [`FirstPerson.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/FirstPerson.yml))
+## member
+
+When you add a [user account](#user-account) to a group or project,
+the user account becomes a **member**.
+
## merge requests
Use lowercase for **merge requests**. If you use **MR** as the acronym, spell it out on first use.
@@ -767,20 +772,41 @@ Do not use **navigate**. Use **go** instead. For example:
([Vale](../testing.md#vale) rule: [`SubstitutionSuggestions.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/SubstitutionSuggestions.yml))
-## need to, should
+## need to
+
+Try to avoid **need to**, because it's wordy.
+
+For example, when a variable is **required**,
+instead of **You need to set the variable**, use:
+
+- Set the variable.
+- You must set the variable.
+
+When the variable is **recommended**:
+
+- You should set the variable.
-Try to avoid **needs to**, because it's wordy. If something is recommended, use **should** instead. If something is required, use **must**.
+When the variable is **optional**:
+
+- You can set the variable.
+
+## normal, normally
+
+Don't use **normal** to mean the usual, typical, or standard way of doing something.
+Use those terms instead.
Use:
-- You should set the variable. (recommended)
-- You must set the variable. (required)
-- Set the variable. (required)
+- Typically, you specify a certificate.
+- Usually, you specify a certificate.
+- Follow the standard Git workflow.
Instead of:
-- You need to set the variable.
-- We recommend that you set the variable.
+- Normally, you specify a certificate.
+- Follow the normal Git workflow.
+
+([Vale](../testing.md#vale) rule: [`Normal.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/Normal.yml))
## note that
@@ -898,6 +924,15 @@ For example, you might write something like:
Use lowercase for **push rules**.
+## recommend, we recommend
+
+Instead of **we recommend**, use **you should**. We want to talk to the user the way
+we would talk to a colleague, and to avoid differentiation between `we` and `them`.
+
+- You should set the variable. (It's recommended.)
+- Set the variable. (It's required.)
+- You can set the variable. (It's optional.)
+
## register
Use **register** instead of **sign up** when talking about creating an account.
@@ -1068,6 +1103,21 @@ Do not use **slave**. Another option is **secondary**. ([Vale](../testing.md#val
Use **subgroup** (no hyphen) instead of **sub-group**. ([Vale](../testing.md#vale) rule: [`SubstitutionSuggestions.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/SubstitutionSuggestions.yml))
+## subscription tier
+
+Do not confuse **subscription** or **subscription tier** with **[license](#license)**.
+A user purchases a **subscription**. That subscription has a **tier**.
+
+To describe tiers:
+
+| Instead of | Use |
+|---------------------------------|----------------------------------------|
+| In the Free tier or greater | In all tiers |
+| In the Free tier or higher | In all tiers |
+| In the Premium tier or greater | In the Premium and Ultimate tier |
+| In the Premium tier or higher | In the Premium and Ultimate tier |
+| In the Premium tier or lower | In the Free and Premium tier |
+
## that
Do not use **that** when describing a noun. For example:
@@ -1127,6 +1177,11 @@ Always follow these words with a noun. For example:
- Use: **Those settings** need to be configured. (Or even better, **Configure those settings.**)
- Instead of: **Those** need to be configured.
+## to which, of which
+
+Try to avoid **to which** and **of which**, and let the preposition dangle at the end of the sentence instead.
+For examples, see [Prepositions](index.md#prepositions).
+
## to-do item
Use lowercase and hyphenate **to-do** item. ([Vale](../testing.md#vale) rule: [`ToDo.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/ToDo.yml))
@@ -1198,18 +1253,10 @@ See also [downgrade](#downgrade) and [roll back](#roll-back).
Do not use **useful**. If the user doesn't find the process to be useful, we lose their trust. ([Vale](../testing.md#vale) rule: [`Simplicity.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/Simplicity.yml))
-## user, users
-
-When possible, address the reader directly, instead of calling them **users**.
-Use the [second person](#you-your-yours), **you**, instead.
+## user account
-Use:
-
-- You can configure a pipeline.
-
-Instead of:
-
-- Users can configure a pipeline.
+You create a **user account**. The user account has an [access level](#access-level).
+When you add a **user account** to a group or project, the user account becomes a **member**.
## utilize
@@ -1270,7 +1317,7 @@ in present tense, active voice.
## you, your, yours
-Use **you**, **your**, and **yours** instead of [**the user** and **the user's**](#user-users).
+Use **you**, **your**, and **yours** instead of **the user** and **the user's**.
Documentation should be from the [point of view](https://design.gitlab.com/content/voice-tone/#point-of-view) of the reader.
Use:
diff --git a/doc/development/documentation/topic_types/concept.md b/doc/development/documentation/topic_types/concept.md
index 7be6bef4fad..e01b06c2c07 100644
--- a/doc/development/documentation/topic_types/concept.md
+++ b/doc/development/documentation/topic_types/concept.md
@@ -11,7 +11,7 @@ A concept introduces a single feature or concept.
A concept should answer the questions:
- What is this?
-- Why would I use it?
+- Why would you use it?
Think of everything someone might want to know if they've never heard of this concept before.
diff --git a/doc/development/documentation/topic_types/index.md b/doc/development/documentation/topic_types/index.md
index 8e8c474ce3c..964b41303cb 100644
--- a/doc/development/documentation/topic_types/index.md
+++ b/doc/development/documentation/topic_types/index.md
@@ -6,20 +6,19 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Documentation topic types (CTRT)
-At GitLab, we have not traditionally used types for our content. However, we are starting to
-move in this direction, and we now use four primary topic types:
+Each topic on a page should be one of the following topic types:
- [Concept](concept.md)
- [Task](task.md)
- [Reference](reference.md)
- [Troubleshooting](troubleshooting.md)
+Even if a page is short, the page usually starts with a concept and then
+includes a task or reference topic.
+
The tech writing team sometimes uses the acronym `CTRT` to refer to our topic types.
The acronym refers to the first letter of each topic type.
-In general, each page in the GitLab documentation contains multiple topics.
-Each topic on a page should be recognizable as a specific topic type.
-
In addition to the four primary topic types, we also have a page type for
[Tutorials](tutorial.md) and [Get started](#get-started).
@@ -66,9 +65,9 @@ Some pages are solely a list of links to other documentation.
We do not encourage this page type. Lists of links can get out-of-date quickly
and offer little value to users, who prefer to search to find information.
-## Topic text guidelines
+## Topic title guidelines
-In general, for topic text:
+In general, for topic titles:
- Be clear and direct. Make every word count.
- Use articles and prepositions.
diff --git a/doc/development/documentation/topic_types/task.md b/doc/development/documentation/topic_types/task.md
index 78d670a16d6..0dba3e079b6 100644
--- a/doc/development/documentation/topic_types/task.md
+++ b/doc/development/documentation/topic_types/task.md
@@ -69,6 +69,12 @@ For example, `Create an issue when you want to track bugs or future work`.
To start the task steps, use a succinct action followed by a colon.
For example, `To create an issue:`
+## Task prerequisites
+
+As a best practice, if the task requires the user to have a role other than Guest,
+put the minimum role in the prerequisites. See [the Word list](../styleguide/word_list.md) for
+how to write the phrase for each role.
+
## Related topics
- [View the format for writing task steps](../styleguide/index.md#navigation).
diff --git a/doc/development/documentation/versions.md b/doc/development/documentation/versions.md
index 030bdec0361..334dcd73ea5 100644
--- a/doc/development/documentation/versions.md
+++ b/doc/development/documentation/versions.md
@@ -46,7 +46,7 @@ The item text must include these words in order. Capitalization doesn't matter.
- `introduced`, `enabled`, `deprecated`, `changed`, `moved`, `recommended`, `removed`, or `renamed`
- `in` or `to`
-- `GitLab`
+- `GitLab` (or, for external projects, the name of the project)
If possible, include a link to the related issue, merge request, or epic.
Do not link to the pricing page. Do not include the subscription tier.
@@ -203,8 +203,8 @@ We cannot guarantee future feature work, and promises
like these can raise legal issues. Instead, say that an issue exists.
For example:
-- Support for improvements is proposed in issue `[issue-number](LINK-TO-ISSUE)`.
-- You cannot do this thing, but issue `[issue-number](LINK-TO-ISSUE)` proposes to change this behavior.
+- Support for improvements is proposed in `[issue <issue_number>](LINK-TO-ISSUE)`.
+- You cannot do this thing, but `[issue 12345](LINK-TO-ISSUE)` proposes to change this behavior.
You can say that we plan to remove a feature.
diff --git a/doc/development/documentation/workflow.md b/doc/development/documentation/workflow.md
index 9d8d25607c8..2effa21b266 100644
--- a/doc/development/documentation/workflow.md
+++ b/doc/development/documentation/workflow.md
@@ -13,29 +13,32 @@ Anyone can contribute to the GitLab documentation! You can create a merge reques
accomplish their work with GitLab.
If you are working on a feature or enhancement, use the
-[feature workflow process described in the GitLab Handbook](https://about.gitlab.com/handbook/product/ux/technical-writing/workflow/#for-a-product-change).
+[feature workflow process described in the GitLab Handbook](https://about.gitlab.com/handbook/product/ux/technical-writing/workflow/#documentation-for-a-product-change).
## How to update the docs
If you are not a GitLab team member, or do not have the Developer role for the GitLab repository, to update GitLab documentation:
-1. Select an issue you'd like to work on.
+1. Select an [issue](https://about.gitlab.com/handbook/product/ux/technical-writing/#community-contribution-opportunities) you'd like to work on.
- You don't need an issue to open a merge request.
- For a Hackathon, in the issue, in a comment, mention the person who opened the issue and ask for the issue to be assigned to you.
To be fair to other contributors, if you see someone has already asked to work on the issue, choose another issue.
If you are looking for issues to work on and don't see any that suit you, you can always fix [Vale](testing.md#vale) issues.
1. Go to the [GitLab repository](https://gitlab.com/gitlab-org/gitlab).
-1. In the top-right, select **Fork**. Forking makes a copy of the repository on GitLab.com.
-1. In your fork, find the documentation page by going to the `\doc` directory.
+1. In the top right, select **Fork**. Forking makes a copy of the repository on GitLab.com.
+1. In your fork, find the documentation page in the `\doc` directory.
1. If you know Git, make your changes and open a merge request.
If not, follow these steps:
- 1. In the top right, select **Edit**, make the changes, and **Save**.
- 1. From the left menu, select **Merge requests**.
+ 1. On the top right, select **Edit** if it is visible. If it is not, select the down arrow (**{chevron-lg-down}**) next to **Open in Web IDE** or **Gitpod**, and select **Edit**.
+ 1. In the **Commit message** text box, enter a commit message. Use 3-5 words, start with a capital letter, and do not end with a period.
+ 1. Select **Commit changes**.
+ 1. On the left sidebar, select **Merge requests**.
+ 1. Select **New merge request**.
1. For the source branch, select your fork and branch. If you did not create a branch, select `master`.
For the target branch, select the [GitLab repository](https://gitlab.com/gitlab-org/gitlab) `master` branch.
- 1. For the commit message, use 3-5 words, start with a capital letter, and do not end with a period.
- 1. Select **Commit changes**. A merge request opens.
+ 1. Select **Compare branches and continue**. A new merge request opens.
1. Select the **Documentation** template. In the description, write a brief summary of the changes and link to the related issue, if there is one.
+ 1. Select **Create merge request**.
If you need help while working on the page, view:
@@ -65,7 +68,7 @@ If you are a member of the GitLab Slack workspace, you can request help in `#doc
When you author an issue or merge request, you must add these labels:
-- A [type label](../contributing/issue_workflow.md#type-labels).
+- A [type label](../contributing/issue_workflow.md#type-labels), either `~"type::feature"` or `~"type::maintenance"`.
- A [stage label](../contributing/issue_workflow.md#stage-labels) and [group label](../contributing/issue_workflow.md#group-labels).
For example, `~devops::create` and `~group::source code`.
- A `~documentation` [specialization label](../contributing/issue_workflow.md#specialization-labels).
@@ -75,9 +78,8 @@ A member of the Technical Writing team adds these labels:
- A [documentation scoped label](../../user/project/labels.md#scoped-labels) with the
`docs::` prefix. For example, `~docs::improvement`.
- The [`~Technical Writing` team label](../contributing/issue_workflow.md#team-labels).
-- A type label: either `~"type::feature"` or `~"type::maintenance"`.
-### Reviewing and merging
+## Reviewing and merging
Anyone with the Maintainer role to the relevant GitLab project can
merge documentation changes. Maintainers must make a good-faith effort to ensure that the content:
@@ -111,13 +113,24 @@ The process involves the following:
The process is reflected in the **Documentation**
[merge request template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/merge_request_templates/Documentation.md).
-## Other ways to help
+### Before merging
-If you have ideas for further documentation resources please
-[create an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Documentation)
-using the Documentation template.
+Ensure the following if skipping an initial Technical Writer review:
+
+- [Product badges](styleguide/index.md#product-tier-badges) are applied.
+- The GitLab [version](versions.md) that
+ introduced the feature is included.
+- Changes to topic titles don't affect in-app hyperlinks.
+- Specific [user permissions](../../user/permissions.md) are documented.
+- New documents are linked from higher-level indexes, for discoverability.
+- The style guide is followed:
+ - For [directories and files](site_architecture/folder_structure.md).
+ - For [images](styleguide/index.md#images).
+
+Merge requests that change the location of documentation must always be reviewed by a Technical
+Writer before merging.
-## Post-merge reviews
+### Post-merge reviews
If not assigned to a Technical Writer for review prior to merging, a review must be scheduled
immediately after merge by the developer or maintainer. For this,
@@ -146,19 +159,8 @@ Remember:
- The Technical Writer can also help decide that documentation can be merged without Technical
writer review, with the review to occur soon after merge.
-### Before merging
-
-Ensure the following if skipping an initial Technical Writer review:
-
-- [Product badges](styleguide/index.md#product-tier-badges) are applied.
-- The GitLab [version](versions.md) that
- introduced the feature is included.
-- Changes to topic titles don't affect in-app hyperlinks.
-- Specific [user permissions](../../user/permissions.md) are documented.
-- New documents are linked from higher-level indexes, for discoverability.
-- The style guide is followed:
- - For [directories and files](site_architecture/folder_structure.md).
- - For [images](styleguide/index.md#images).
+## Other ways to help
-Merge requests that change the location of documentation must always be reviewed by a Technical
-Writer before merging.
+If you have ideas for further documentation resources please
+[create an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Documentation)
+using the Documentation template.
diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md
index 14df73b8779..5e236c3e322 100644
--- a/doc/development/ee_features.md
+++ b/doc/development/ee_features.md
@@ -94,6 +94,25 @@ setting the [`FOSS_ONLY` environment variable](https://gitlab.com/gitlab-org/git
to something that evaluates as `true`. The same works for running tests
(for example `FOSS_ONLY=1 yarn jest`).
+### Simulate a CE instance with a licensed GDK
+
+To simulate a CE instance without deleting the license in a GDK:
+
+1. Create an `env.runit` file in the root of your GDK with the line:
+
+ ```shell
+ export FOSS_ONLY=1
+ ```
+
+1. Then restart the GDK:
+
+ ```shell
+ gdk restart rails && gdk restart webpack
+ ```
+
+Remove the line in `env.runit` if you want to revert back to an EE
+installation, and repeat step 2.
+
#### Run feature specs as CE
When running [feature specs](testing_guide/best_practices.md#system--feature-tests)
diff --git a/doc/development/elasticsearch.md b/doc/development/elasticsearch.md
index ab2d241a781..88a417b4745 100644
--- a/doc/development/elasticsearch.md
+++ b/doc/development/elasticsearch.md
@@ -69,7 +69,7 @@ The `whitespace` tokenizer was selected to have more control over how tokens are
Please see the `code` filter for an explanation on how tokens are split.
NOTE:
-The [Elasticsearch code_analyzer doesn't account for all code cases](../integration/advanced_search/elasticsearch_troubleshooting.md#elasticsearch-code_analyzer-doesnt-account-for-all-code-cases).
+The [Elasticsearch `code_analyzer` doesn't account for all code cases](../integration/advanced_search/elasticsearch_troubleshooting.md#elasticsearch-code_analyzer-doesnt-account-for-all-code-cases).
#### `code_search_analyzer`
@@ -113,9 +113,9 @@ Uses a [Pattern Capture token filter](https://www.elastic.co/guide/en/elasticsea
Patterns:
-- `"(\\p{Ll}+|\\p{Lu}\\p{Ll}+|\\p{Lu}+)"`: captures CamelCased and lowedCameCased strings as separate tokens
+- `"(\\p{Ll}+|\\p{Lu}\\p{Ll}+|\\p{Lu}+)"`: captures CamelCase and lowerCamelCase strings as separate tokens
- `"(\\d+)"`: extracts digits
-- `"(?=([\\p{Lu}]+[\\p{L}]+))"`: captures CamelCased strings recursively. Ex: `ThisIsATest` => `[ThisIsATest, IsATest, ATest, Test]`
+- `"(?=([\\p{Lu}]+[\\p{L}]+))"`: captures CamelCase strings recursively. For example: `ThisIsATest` => `[ThisIsATest, IsATest, ATest, Test]`
- `'"((?:\\"|[^"]|\\")*)"'`: captures terms inside quotes, removing the quotes
- `"'((?:\\'|[^']|\\')*)'"`: same as above, for single-quotes
- `'\.([^.]+)(?=\.|\s|\Z)'`: separate terms with periods in-between
diff --git a/doc/development/fe_guide/accessibility.md b/doc/development/fe_guide/accessibility.md
index 67166a93cb4..af45603782f 100644
--- a/doc/development/fe_guide/accessibility.md
+++ b/doc/development/fe_guide/accessibility.md
@@ -109,15 +109,15 @@ Text input examples:
</gl-form-group>
```
-Textarea examples:
+`textarea` examples:
```html
-<!-- Textarea with label -->
+<!-- textarea with label -->
<gl-form-group :label="__('Issue description')" label-for="issue-description">
<gl-form-textarea id="issue-description" v-model="description" />
</gl-form-group>
-<!-- Textarea with hidden label -->
+<!-- textarea with hidden label -->
<gl-form-group :label="__('Issue description')" label-for="issue-description" label-sr-only>
<gl-form-textarea id="issue-description" v-model="description" />
</gl-form-group>
@@ -347,7 +347,7 @@ Keep in mind that:
See the [Pajamas Keyboard-only page](https://design.gitlab.com/accessibility-audits/keyboard-only/) for more detail.
-## Tabindex
+## `tabindex`
Prefer **no** `tabindex` to using `tabindex`, since:
diff --git a/doc/development/fe_guide/content_editor.md b/doc/development/fe_guide/content_editor.md
index 8cc274c732e..982033cf2ad 100644
--- a/doc/development/fe_guide/content_editor.md
+++ b/doc/development/fe_guide/content_editor.md
@@ -11,7 +11,7 @@ experience for [GitLab Flavored Markdown](../../user/markdown.md) in the GitLab
It also serves as the foundation for implementing Markdown-focused editors
that target other engines, like static site generators.
-We use [tiptap 2.0](https://tiptap.dev/) and [ProseMirror](https://prosemirror.net/)
+We use [Tiptap 2.0](https://tiptap.dev/) and [ProseMirror](https://prosemirror.net/)
to build the Content Editor. These frameworks provide a level of abstraction on top of
the native
[`contenteditable`](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Editable_content) web technology.
@@ -209,7 +209,7 @@ the following events:
- `blur`
- `error`.
-Learn more about these events in [Tiptap's event guide](https://tiptap.dev/api/events/).
+Learn more about these events in [the Tiptap event guide](https://tiptap.dev/api/events/).
```html
<script>
@@ -255,13 +255,13 @@ provides all the necessary extensions to support
#### Implement new extensions
Extensions are the building blocks of the Content Editor. You can learn how to implement
-new ones by reading [Tiptap's guide](https://tiptap.dev/guide/custom-extensions).
+new ones by reading [the Tiptap guide](https://tiptap.dev/guide/custom-extensions).
We recommend checking the list of built-in [nodes](https://tiptap.dev/api/nodes) and
[marks](https://tiptap.dev/api/marks) before implementing a new extension
from scratch.
Store the Content Editor extensions in the `~/content_editor/extensions` directory.
-When using a Tiptap's built-in extension, wrap it in a ES6 module inside this directory:
+When using a Tiptap built-in extension, wrap it in a ES6 module inside this directory:
```javascript
export { Bold as default } from '@tiptap/extension-bold';
@@ -326,10 +326,10 @@ sequenceDiagram
A->>E: setContent(document)
```
-Deserializers live in the extension modules. Read Tiptap's
-[parseHTML](https://tiptap.dev/guide/custom-extensions#parse-html) and
-[addAttributes](https://tiptap.dev/guide/custom-extensions#attributes) documentation to
-learn how to implement them. Titap's API is a wrapper around ProseMirror's
+Deserializers live in the extension modules. Read Tiptap documentation about
+[`parseHTML`](https://tiptap.dev/guide/custom-extensions#parse-html) and
+[`addAttributes`](https://tiptap.dev/guide/custom-extensions#attributes) to
+learn how to implement them. The Tiptap API is a wrapper around ProseMirror's
[schema spec API](https://prosemirror.net/docs/ref/#model.SchemaSpec).
#### Serialization
diff --git a/doc/development/fe_guide/customizable_dashboards.md b/doc/development/fe_guide/customizable_dashboards.md
index 38ee750d421..807f83f5bec 100644
--- a/doc/development/fe_guide/customizable_dashboards.md
+++ b/doc/development/fe_guide/customizable_dashboards.md
@@ -41,16 +41,12 @@ export default {
// All values are grid row/column numbers up to 12.
// We use the default 12 column grid https://github.com/gridstack/gridstack.js#change-grid-columns.
gridAttributes: {
- size: {
- height: 4,
- width: 6,
- minHeight: 4,
- minWidth: 6,
- },
- position: {
- xPos: 0,
- yPos: 0,
- },
+ height: 4,
+ width: 6,
+ minHeight: 4,
+ minWidth: 6,
+ xPos: 0,
+ yPos: 0,
},
// Options that are used to set bespoke values for each widget.
// Available customizations are determined by the widget itself.
diff --git a/doc/development/fe_guide/dark_mode.md b/doc/development/fe_guide/dark_mode.md
index d687d9740c9..55181edd64c 100644
--- a/doc/development/fe_guide/dark_mode.md
+++ b/doc/development/fe_guide/dark_mode.md
@@ -19,9 +19,9 @@ Note the following:
- We define two types of variables in `_dark.scss`:
- SCSS variables are used in framework, components, and utility classes.
- CSS variables are used for any colors within the `app/assets/stylesheets/page_bundles` directory.
-- `app/views/layouts/_head.html.haml` then loads application or application_dark based on the user's theme preference.
+- `app/views/layouts/_head.html.haml` then loads `application` or `application_dark` based on the user's theme preference.
-As we do not want to generate separate `_dark.css` variants of every page_bundle file,
+As we do not want to generate separate `_dark.css` variants of every `page_bundle` file,
we use CSS variables with SCSS variables as fallbacks. This is because when we generate the `page_bundles`
CSS, we get the variable values from `_variables.scss`, so any SCSS variables have light mode values.
diff --git a/doc/development/fe_guide/development_process.md b/doc/development/fe_guide/development_process.md
index fc91ff55b24..232689080ea 100644
--- a/doc/development/fe_guide/development_process.md
+++ b/doc/development/fe_guide/development_process.md
@@ -45,7 +45,7 @@ Use your best judgment when to use it and contribute new points through merge re
- [ ] Check off tasks on your created task list to keep everyone updated on the progress
- [ ] [Share your work early with reviewers/maintainers](#share-your-work-early)
-- [ ] Share your work with UXer and Product Manager with Screenshots and/or [GIF's](https://about.gitlab.com/handbook/product/making-gifs/). They are easy to create for you and keep them up to date.
+- [ ] Share your work with UXer and Product Manager with Screenshots and/or [GIF images](https://about.gitlab.com/handbook/product/making-gifs/). They are easy to create for you and keep them up to date.
- [ ] If you are blocked on something let everyone on the issue know through a comment.
- [ ] Are you unable to work on this issue for a longer period of time, also let everyone know.
- [ ] **Documentation** Update/add docs for the new feature, see `docs/`. Ping one of the documentation experts/reviewers
@@ -58,7 +58,7 @@ Use your best judgment when to use it and contribute new points through merge re
- [ ] Did you check the mobile view?
- [ ] Check the built webpack bundle (For the report run `WEBPACK_REPORT=true gdk start`, then open `webpack-report/index.html`) if we have unnecessary bloat due to wrong references, including libraries multiple times, etc.. If you need help contact the webpack [domain expert](https://about.gitlab.com/handbook/engineering/frontend/#frontend-domain-experts)
- [ ] **Tests** Not only greenfield tests - Test also all bad cases that come to your mind.
-- [ ] If you have multiple MR's then also smoke test against the final merge.
+- [ ] If you have multiple MRs then also smoke test against the final merge.
- [ ] Are there any big changes on how and especially how frequently we use the API then let production know about it
- [ ] Smoke test of the RC on dev., staging., canary deployments and .com
- [ ] Follow up on issues that came out of the review. Create issues for discovered edge cases that should be covered in future iterations.
diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md
index 28ea84301a6..0fec38b1200 100644
--- a/doc/development/fe_guide/graphql.md
+++ b/doc/development/fe_guide/graphql.md
@@ -109,7 +109,7 @@ Default client accepts two parameters: `resolvers` and `config`.
If you are making multiple queries to the same Apollo client object you might encounter the following error: `Cache data may be lost when replacing the someProperty field of a Query object. To address this problem, either ensure all objects of SomeEntityhave an id or a custom merge function`. We are already checking `ID` presence for every GraphQL type that has an `ID`, so this shouldn't be the case. Most likely, the `SomeEntity` type doesn't have an `ID` property, and to fix this warning we need to define a custom merge function.
-We have some client-wide types with `merge: true` defined in the default client as [typePolicies](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/lib/graphql.js) (this means that Apollo will merge existing and incoming responses in the case of subsequent queries). Consider adding `SomeEntity` there or defining a custom merge function for it.
+We have some client-wide types with `merge: true` defined in the default client as [`typePolicies`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/lib/graphql.js) (this means that Apollo will merge existing and incoming responses in the case of subsequent queries). Consider adding `SomeEntity` there or defining a custom merge function for it.
## GraphQL Queries
@@ -1138,7 +1138,7 @@ query getPipelineEtag {
```javascript
/* pipeline_editor/components/header/pipeline_status.vue */
-import getPipelineEtag from '~/pipeline_editor/graphql/queries/client/pipeline_etag.query.graphql';
+import getPipelineEtag from '~/ci/pipeline_editor/graphql/queries/client/pipeline_etag.query.graphql';
apollo: {
pipelineEtag: {
diff --git a/doc/development/fe_guide/haml.md b/doc/development/fe_guide/haml.md
index d76d718e8e5..9dc5b265783 100644
--- a/doc/development/fe_guide/haml.md
+++ b/doc/development/fe_guide/haml.md
@@ -81,11 +81,7 @@ When using the GitLab UI form builder, the following components are available fo
NOTE:
Currently only the listed components are available but more components are planned.
-<!-- vale gitlab.Spelling = NO -->
-
-#### gitlab_ui_checkbox_component
-
-<!-- vale gitlab.Spelling = YES -->
+#### `gitlab_ui_checkbox_component`
[GitLab UI Docs](https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/base-form-form-checkbox--default)
@@ -110,11 +106,7 @@ This component supports [ViewComponent slots](https://viewcomponent.org/guide/sl
| `label` | Checkbox label content. This slot can be used instead of the `label` argument. |
| `help_text` | Help text content displayed below the checkbox. This slot can be used instead of the `help_text` argument. |
-<!-- vale gitlab.Spelling = NO -->
-
-#### gitlab_ui_radio_component
-
-<!-- vale gitlab.Spelling = YES -->
+#### `gitlab_ui_radio_component`
[GitLab UI Docs](https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/base-form-form-radio--default)
diff --git a/doc/development/fe_guide/merge_request_widget_extensions.md b/doc/development/fe_guide/merge_request_widget_extensions.md
index 61d3e79a080..49c6664c6d6 100644
--- a/doc/development/fe_guide/merge_request_widget_extensions.md
+++ b/doc/development/fe_guide/merge_request_widget_extensions.md
@@ -355,7 +355,12 @@ To generate these known events for a single widget:
1. `redis_slot` = `code_review`
1. `category` = `code_review`
1. `aggregation` = `weekly`
-1. Add each event to the appropriate aggregates in `config/metrics/aggregates/code_review.yml`
+1. Add each event (those listed in the command in step 7, replacing `test_reports`
+ with the appropriate name slug) to the aggregate files:
+ 1. `config/metrics/counts_7d/{timestamp}_code_review_category_monthly_active_users.yml`
+ 1. `config/metrics/counts_7d/{timestamp}_code_review_group_monthly_active_users.yml`
+ 1. `config/metrics/counts_28d/{timestamp}_code_review_category_monthly_active_users.yml`
+ 1. `config/metrics/counts_28d/{timestamp}_code_review_group_monthly_active_users.yml`
### Add new events
diff --git a/doc/development/fe_guide/security.md b/doc/development/fe_guide/security.md
index 4b7ce6d11e3..d578449e578 100644
--- a/doc/development/fe_guide/security.md
+++ b/doc/development/fe_guide/security.md
@@ -88,7 +88,7 @@ readability.
If you need to output raw HTML, you should sanitize it.
-If you are using Vue, you can use the[`v-safe-html` directive](https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/directives-safe-html-directive--default) from GitLab UI.
+If you are using Vue, you can use the[`v-safe-html` directive](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/vue_shared/directives/safe_html.js).
For other use cases, wrap a preconfigured version of [`dompurify`](https://www.npmjs.com/package/dompurify)
that also allows the icons to be rendered:
diff --git a/doc/development/fe_guide/source_editor.md b/doc/development/fe_guide/source_editor.md
index b7cd903485e..4cfc68553e0 100644
--- a/doc/development/fe_guide/source_editor.md
+++ b/doc/development/fe_guide/source_editor.md
@@ -66,9 +66,9 @@ with additional functions on the instance level:
| Function | Arguments | Description
| --------------------- | ----- | ----- |
-| `updateModelLanguage` | `path`: String | Updates the instance's syntax highlighting to follow the extension of the passed `path`. Available only on the instance level.|
-| `use` | Array of objects | Array of extensions to apply to the instance. Accepts only the array of _objects_. You must fetch the extensions' ES6 modules must be fetched and resolved in your views or components before they are passed to `use`. This property is available on _instance_ (applies extension to this particular instance) and _global editor_ (applies the same extension to all instances) levels. |
-| Monaco Editor options | See [documentation](https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IStandaloneCodeEditor.html) | Default Monaco editor options |
+| `updateModelLanguage` | `path`: String | Updates the instance's syntax highlighting to follow the extension of the passed `path`. Available only on the instance level. |
+| `use` | Array of objects | Array of extensions to apply to the instance. Accepts only an array of **objects**. The extensions' ES6 modules must be fetched and resolved in your views or components before they're passed to `use`. Available on the instance and global editor (all instances) levels. |
+| Monaco Editor options | See [documentation](https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IStandaloneCodeEditor.html) | Default Monaco editor options. |
## Tips
diff --git a/doc/development/fe_guide/style/javascript.md b/doc/development/fe_guide/style/javascript.md
index 38fb926197b..0536d1c5c77 100644
--- a/doc/development/fe_guide/style/javascript.md
+++ b/doc/development/fe_guide/style/javascript.md
@@ -7,7 +7,7 @@ disqus_identifier: 'https://docs.gitlab.com/ee/development/fe_guide/style_guide_
# JavaScript style guide
-We use [Airbnb's JavaScript Style Guide](https://github.com/airbnb/javascript) and its accompanying
+We use [the Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) and its accompanying
linter to manage most of our JavaScript style guidelines.
In addition to the style guidelines set by Airbnb, we also have a few specific rules
@@ -16,11 +16,11 @@ listed below.
NOTE:
You can run ESLint locally by running `yarn run lint:eslint:all` or `yarn run lint:eslint $PATH_TO_FILE`.
-## Avoid forEach
+## Avoid `forEach`
-Avoid forEach when mutating data. Use `map`, `reduce` or `filter` instead of `forEach`
+Avoid `forEach` when mutating data. Use `map`, `reduce` or `filter` instead of `forEach`
when mutating data. This minimizes mutations in functions,
-which aligns with [Airbnb's style guide](https://github.com/airbnb/javascript#testing--for-real).
+which aligns with [the Airbnb style guide](https://github.com/airbnb/javascript#testing--for-real).
```javascript
// bad
diff --git a/doc/development/fe_guide/style/scss.md b/doc/development/fe_guide/style/scss.md
index 98f74813231..7a5c955db93 100644
--- a/doc/development/fe_guide/style/scss.md
+++ b/doc/development/fe_guide/style/scss.md
@@ -41,13 +41,13 @@ GitLab differs from the scale used in the Bootstrap library. For a Bootstrap pad
utility, you may need to double the size of the applied utility to achieve the same visual
result (such as `ml-1` becoming `gl-ml-2`).
-#### Where should I put new utility classes?
+#### Where should you put new utility classes?
-If a class you need has not been added to GitLab UI, you get to add it! Follow the naming patterns documented in the [utility files](https://gitlab.com/gitlab-org/gitlab-ui/-/tree/main/src/scss/utility-mixins) and refer to [GitLab UI's CSS documentation](https://gitlab.com/gitlab-org/gitlab-ui/-/blob/main/doc/contributing/adding_css.md#adding-utility-mixins) for more details, especially about adding responsive and stateful rules.
+If a class you need has not been added to GitLab UI, you get to add it! Follow the naming patterns documented in the [utility files](https://gitlab.com/gitlab-org/gitlab-ui/-/tree/main/src/scss/utility-mixins) and refer to the [GitLab UI CSS documentation](https://gitlab.com/gitlab-org/gitlab-ui/-/blob/main/doc/contributing/adding_css.md#adding-utility-mixins) for more details, especially about adding responsive and stateful rules.
If it is not possible to wait for a GitLab UI update (generally one day), add the class to [`utilities.scss`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/stylesheets/utilities.scss) following the same naming conventions documented in GitLab UI. A follow-up issue to backport the class to GitLab UI and delete it from GitLab should be opened.
-#### When should I create component classes?
+#### When should you create component classes?
We recommend a "utility-first" approach.
diff --git a/doc/development/fe_guide/tooling.md b/doc/development/fe_guide/tooling.md
index c9efb8e939d..762ef852d74 100644
--- a/doc/development/fe_guide/tooling.md
+++ b/doc/development/fe_guide/tooling.md
@@ -212,7 +212,7 @@ yarn run lint:prettier:fix
Formats all files in the repository with Prettier.
-### VSCode Settings
+### VS Code Settings
#### Select Prettier as default formatter
diff --git a/doc/development/fe_guide/troubleshooting.md b/doc/development/fe_guide/troubleshooting.md
index 873a14b8f14..3e4c5007b64 100644
--- a/doc/development/fe_guide/troubleshooting.md
+++ b/doc/development/fe_guide/troubleshooting.md
@@ -10,13 +10,13 @@ Running into a problem? Maybe this will help ¯\_(ツ)_/¯.
## Troubleshooting issues
-### This guide doesn't contain the issue I ran into
+### This guide doesn't contain the issue you ran into
If you run into a Frontend development issue that is not in this guide, consider updating this guide with your issue and possible remedies. This way future adventurers can face these dragons with more success, being armed with your experience and knowledge.
## Testing issues
-### ``Property or method `nodeType` is not defined`` but I'm not using `nodeType` anywhere
+### ``Property or method `nodeType` is not defined`` but you're not using `nodeType` anywhere
This issue can happen in Vue component tests, when an expectation fails, but there is an error thrown when
Jest tries to pretty print the diff in the console. It's been noted that using `toEqual` with an array as a
diff --git a/doc/development/fe_guide/view_component.md b/doc/development/fe_guide/view_component.md
index b61c23cadef..90bb75514d8 100644
--- a/doc/development/fe_guide/view_component.md
+++ b/doc/development/fe_guide/view_component.md
@@ -180,7 +180,7 @@ The `Pajamas::CheckboxComponent` follows the [Pajamas Checkbox](https://design.g
NOTE:
`Pajamas::CheckboxComponent` is used internally by the [GitLab UI form builder](haml.md#use-the-gitlab-ui-form-builder) and requires an instance of [ActionView::Helpers::FormBuilder](https://api.rubyonrails.org/v6.1.0/classes/ActionView/Helpers/FormBuilder.html) to be passed as the `form` argument.
-It is preferred to use the [gitlab_ui_checkbox_component](haml.md#gitlab_ui_checkbox_component) method to render this ViewComponent.
+It is preferred to use the [`gitlab_ui_checkbox_component`](haml.md#gitlab_ui_checkbox_component) method to render this ViewComponent.
To use a checkbox without an instance of [ActionView::Helpers::FormBuilder](https://api.rubyonrails.org/v6.1.0/classes/ActionView/Helpers/FormBuilder.html) use [CheckboxTagComponent](#checkbox-tag).
For the full list of options, see its
diff --git a/doc/development/fe_guide/vue3_migration.md b/doc/development/fe_guide/vue3_migration.md
index aae7674d190..b9e819a95bd 100644
--- a/doc/development/fe_guide/vue3_migration.md
+++ b/doc/development/fe_guide/vue3_migration.md
@@ -182,13 +182,13 @@ In the step-by-step guide, we will be migrating [VueApollo Demo](https://gitlab.
#### Initial state
-Right after cloning, you could run [VueApollo Demo](https://gitlab.com/gitlab-org/frontend/vue3-migration-vue-apollo/-/tree/main/src/vue3compat) with Vue.js 2 using `yarn serve` or with Vue.js 3 (compat build) using `yarn serve:vue3`. However latter immediately crashes:
+Right after cloning, you could run [VueApollo Demo](https://gitlab.com/gitlab-org/frontend/vue3-migration-vue-apollo/-/tree/main/src/vue3compat) with Vue.js 2 using `yarn serve` or with Vue.js 3 (`compat` build) using `yarn serve:vue3`. However latter immediately crashes:
```javascript
Uncaught TypeError: Cannot read properties of undefined (reading 'loading')
```
-VueApollo v3 (used for Vue.js 2) fails to initialize in Vue.js compat
+VueApollo v3 (used for Vue.js 2) fails to initialize in Vue.js `compat`
NOTE:
While stubbing `Vue.version` will solve VueApollo-related issues in the demo project, it will still lose reactivity on specific scenarios, so an upgrade is still needed
diff --git a/doc/development/fe_guide/widgets.md b/doc/development/fe_guide/widgets.md
index 3364c778d76..edb8559da48 100644
--- a/doc/development/fe_guide/widgets.md
+++ b/doc/development/fe_guide/widgets.md
@@ -44,7 +44,7 @@ All editable sidebar widgets should use [`SidebarEditableItem`](https://gitlab.c
We aim to make widgets as reusable as possible. That's why we should avoid adding any external state
bindings to widgets or to their child components. This includes Vuex mappings and mediator stores.
-## Widget's responsibility
+## Widget responsibility
A widget is responsible for fetching and updating an entity it's designed for (assignees, iterations, and so on).
This means a widget should **always** fetch data (if it's not in Apollo cache already).
@@ -78,7 +78,7 @@ To handle the same logic for query updates, we **alias** query fields. For examp
- `group` or `project` become `workspace`
- `issue`, `epic`, or `mergeRequest` become `issuable`
-Unfortunately, Apollo assigns aliased fields a typename of `undefined`, so we need to fetch `__typename` explicitly:
+Unfortunately, Apollo assigns aliased fields a `typename` of `undefined`, so we need to fetch `__typename` explicitly:
```plaintext
query issueConfidential($fullPath: ID!, $iid: String) {
diff --git a/doc/development/feature_categorization/index.md b/doc/development/feature_categorization/index.md
index 0bf506b53ba..47663915ea7 100644
--- a/doc/development/feature_categorization/index.md
+++ b/doc/development/feature_categorization/index.md
@@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/269) in GitLab 13.2.
-Each Sidekiq worker, controller action, or API endpoint
+Each Sidekiq worker, controller action, [test example](../testing_guide/best_practices.md#feature-category-metadata) or API endpoint
must declare a `feature_category` attribute. This attribute maps each
of these to a [feature category](https://about.gitlab.com/handbook/product/categories/). This
is done for error budgeting, alert routing, and team attribution.
@@ -166,3 +166,40 @@ end
As with Rails controllers, an API class must specify the category for
every single action unless the same category is used for every action
within that class.
+
+## RSpec Examples
+
+You must set feature category metadata for each RSpec example. This information is used for flaky test
+issues to identify the group that owns the feature.
+
+The `feature_category` should be a value from [`categories.json`](https://about.gitlab.com/categories.json).
+
+The `feature_category` metadata can be set:
+
+- [In the top-level `RSpec.describe` blocks](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104274/diffs#6bd01173381e873f3e1b6c55d33cdaa3d897156b_5_5).
+- [In `describe` blocks](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104274/diffs#a520db2677a30e7f1f5593584f69c49031b894b9_12_12).
+
+Consider splitting the file in the case there are multiple feature categories identified in the same file.
+
+Example:
+
+ ```ruby
+ RSpec.describe Admin::Geo::SettingsController, :geo, feature_category: :geo_replication do
+ ```
+
+For examples that don't have a `feature_category` set we add a warning when running them in local environment.
+
+In order to disable the warning use `RSPEC_WARN_MISSING_FEATURE_CATEGORY=false` when running RSpec tests:
+
+```shell
+RSPEC_WARN_MISSING_FEATURE_CATEGORY=false bin/rspec spec/<test_file>
+```
+
+### Excluding specs from feature categorization
+
+In the rare case an action cannot be tied to a feature category this
+can be done using the `not_owned` feature category.
+
+```ruby
+RSpec.describe Utils, feature_category: :not_owned do
+```
diff --git a/doc/development/feature_development.md b/doc/development/feature_development.md
index 76447124177..b9e093b9479 100644
--- a/doc/development/feature_development.md
+++ b/doc/development/feature_development.md
@@ -19,7 +19,7 @@ Consult these topics for information on contributing to specific GitLab features
### General
-- [Directory structure](directory_structure.md)
+- [Software design guides](software_design.md)
- [GitLab EventStore](event_store.md) to publish/subscribe to domain events
- [GitLab utilities](utilities.md)
- [Newlines style guide](newlines_styleguide.md)
@@ -54,7 +54,7 @@ Consult these topics for information on contributing to specific GitLab features
### Debugging
- [Pry debugging](pry_debugging.md)
-- [Sidekiq debugging](../administration/troubleshooting/sidekiq.md)
+- [Sidekiq debugging](../administration/sidekiq/sidekiq_troubleshooting.md)
### Git specifics
@@ -127,7 +127,7 @@ See [database guidelines](database/index.md).
- [Security Scanners](integrations/secure.md)
- [Secure Partner Integration](integrations/secure_partner_integration.md)
- [How to run Jenkins in development environment](integrations/jenkins.md)
-- [How to run local `Codesandbox` integration for Web IDE Live Preview](integrations/codesandbox.md)
+- [How to run local CodeSandbox integration for Web IDE Live Preview](integrations/codesandbox.md)
The following integration guides are internal. Some integrations require access to administrative accounts of third-party services and are available only for GitLab team members to contribute to:
@@ -204,3 +204,4 @@ The following integration guides are internal. Some integrations require access
- [Run full Auto DevOps cycle in a GDK instance](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/main/doc/howto/auto_devops.md)
- [Using GitLab Runner with the GDK](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/main/doc/howto/runner.md)
- [Using the Web IDE terminal with the GDK](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/web_ide_terminal_gdk_setup.md)
+- [Gitpod configuration internals page](gitpod_internals.md)
diff --git a/doc/development/feature_flags/controls.md b/doc/development/feature_flags/controls.md
index e804a888e98..3e6491a92b5 100644
--- a/doc/development/feature_flags/controls.md
+++ b/doc/development/feature_flags/controls.md
@@ -67,10 +67,8 @@ a (very) rough estimate of how your feature will look and behave on GitLab.com.
Both of these instances are connected to Sentry so make sure you check the projects
there for any exceptions while testing your feature after enabling the feature flag.
-For these pre-production environments, the commands should be run in a
-Slack channel for the stage the feature is relevant to. For example, use the
-`#s_monitor` channel for features developed by the Monitor stage, Health
-group.
+For these pre-production environments, it's strongly encouraged to run the command in
+`#staging`, `#production`, or `#chatops-ops-test`, for improved visibility.
To enable a feature for 25% of the time, run the following in Slack:
@@ -289,10 +287,47 @@ To disable a feature flag that has been enabled for a specific project you can r
/chatops run feature set --project=gitlab-org/gitlab some_feature false
```
-You cannot selectively disable feature flags for a specific project/group/user without applying a [specific method of implementing](index.md#selectively-disable-by-actor) the feature flags.
+You cannot selectively disable feature flags for a specific project/group/user without applying a [specific method of implementing](controls.md#selectively-disable-by-actor) the feature flags.
If a feature flag is disabled via ChatOps, that will take precedence over the `default_enabled` value in the YAML. In other words, you could have a feature enabled for on-premise installations but not for GitLab.com.
+#### Selectively disable by actor
+
+By default you cannot selectively disable a feature flag by actor.
+
+```shell
+# This will not work how you would expect.
+/chatops run feature set some_feature true
+/chatops run feature set --project=gitlab-org/gitlab some_feature false
+```
+
+However, if you add two feature flags, you can write your conditional statement in such a way that the equivalent selective disable is possible.
+
+```ruby
+Feature.enabled?(:a_feature, project) && Feature.disabled?(:a_feature_override, project)
+```
+
+```shell
+# This will enable a feature flag globally, except for gitlab-org/gitlab
+/chatops run feature set a_feature true
+/chatops run feature set --project=gitlab-org/gitlab a_feature_override true
+```
+
+#### Percentage-based actor selection
+
+When using the percentage rollout of actors on multiple feature flags, the actors for each feature flag are selected separately.
+
+For example, the following feature flags are enabled for a certain percentage of actors:
+
+```plaintext
+/chatops run feature set feature-set-1 25 --actors
+/chatops run feature set feature-set-2 25 --actors
+```
+
+If a project A has `:feature-set-1` enabled, there is no guarantee that project A also has `:feature-set-2` enabled.
+
+For more detail, see [This is how percentages work in Flipper](https://www.hackwithpassion.com/this-is-how-percentages-work-in-flipper/).
+
### Feature flag change logging
#### ChatOps level
diff --git a/doc/development/feature_flags/index.md b/doc/development/feature_flags/index.md
index 500afa8ba1d..5ff4292dfb6 100644
--- a/doc/development/feature_flags/index.md
+++ b/doc/development/feature_flags/index.md
@@ -187,7 +187,7 @@ Only feature flags that have a YAML definition file can be used when running the
```shell
$ bin/feature-flag my_feature_flag
>> Specify the group introducing the feature flag, like `group::apm`:
-?> group::memory
+?> group::application performance
>> URL of the MR introducing the feature flag (enter to skip):
?> https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38602
@@ -202,7 +202,7 @@ create config/feature_flags/development/my_feature_flag.yml
name: my_feature_flag
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38602
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/232533
-group: group::memory
+group: group::application performance
type: development
default_enabled: false
```
@@ -398,44 +398,8 @@ Feature.enabled?(:feature_flag, group)
Feature.enabled?(:feature_flag, user)
```
-Please see [Feature flag controls](controls.md#process) for more details on working with feature flags.
-
-#### Selectively disable by actor
-
-By default you cannot selectively disable a feature flag by actor.
-
-```shell
-# This will not work how you would expect.
-/chatops run feature set some_feature true
-/chatops run feature set --project=gitlab-org/gitlab some_feature false
-```
-
-However, if you add two feature flags, you can write your conditional statement in such a way that the equivalent selective disable is possible.
-
-```ruby
-Feature.enabled?(:a_feature, project) && Feature.disabled?(:a_feature_override, project)
-```
-
-```shell
-# This will enable a feature flag globally, except for gitlab-org/gitlab
-/chatops run feature set a_feature true
-/chatops run feature set --project=gitlab-org/gitlab a_feature_override true
-```
-
-#### Percentage-based actor selection
-
-When using the percentage rollout of actors on multiple feature flags, the actors for each feature flag are selected separately.
-
-For example, the following feature flags are enabled for a certain percentage of actors:
-
-```plaintext
-/chatops run feature set feature-set-1 25 --actors
-/chatops run feature set feature-set-2 25 --actors
-```
-
-If a project A has `:feature-set-1` enabled, there is no guarantee that project A also has `:feature-set-2` enabled.
-
-For more detail, see [This is how percentages work in Flipper](https://www.hackwithpassion.com/this-is-how-percentages-work-in-flipper/).
+See [Feature flags in the development of GitLab](controls.md#process) for details on how to use ChatOps
+to selectively enable or disable feature flags in GitLab-provided environments, like staging and production.
#### Use actors for verifying in production
diff --git a/doc/development/file_storage.md b/doc/development/file_storage.md
index 11acb8a2161..c346d55f639 100644
--- a/doc/development/file_storage.md
+++ b/doc/development/file_storage.md
@@ -36,7 +36,7 @@ There are many places where file uploading is used, according to contexts:
GitLab started saving everything on local disk. While directory location changed from previous versions,
they are still not 100% standardized. You can see them below:
-| Description | In DB? | Relative path (from CarrierWave.root) | Uploader class | model_type |
+| Description | In DB? | Relative path (from CarrierWave.root) | Uploader class | Model type |
| ------------------------------------- | ------ | ----------------------------------------------------------- | ---------------------- | ---------- |
| Instance logo | yes | `uploads/-/system/appearance/logo/:id/:filename` | `AttachmentUploader` | Appearance |
| Header logo | yes | `uploads/-/system/appearance/header_logo/:id/:filename` | `AttachmentUploader` | Appearance |
diff --git a/doc/development/filtering_by_label.md b/doc/development/filtering_by_label.md
deleted file mode 100644
index 675fe004c22..00000000000
--- a/doc/development/filtering_by_label.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/filtering_by_label.md'
-remove_date: '2022-11-05'
----
-
-This document was moved to [another location](database/filtering_by_label.md).
-
-<!-- This redirect file can be deleted after <2022-11-05>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/fips_compliance.md b/doc/development/fips_compliance.md
index c6208d45c77..187a9b0cc93 100644
--- a/doc/development/fips_compliance.md
+++ b/doc/development/fips_compliance.md
@@ -69,6 +69,7 @@ listed here that also do not work properly in FIPS mode:
when operating in FIPS-compliant mode.
- Advanced Search is currently not included in FIPS mode. It must not be enabled to be FIPS-compliant.
- [Gravatar or Libravatar-based profile images](../administration/libravatar.md) are not FIPS-compliant.
+- [Personal Access Tokens](../user/profile/personal_access_tokens.md) are not available for use or creation.
Additionally, these package repositories are disabled in FIPS mode:
@@ -441,13 +442,27 @@ def default_min_key_size(name)
end
```
-## Nightly Omnibus FIPS builds
+## Omnibus FIPS packages
-The Distribution team has created [nightly FIPS Omnibus builds](https://packages.gitlab.com/gitlab/nightly-fips-builds). These
-GitLab builds are compiled to use the system OpenSSL instead of the Omnibus-embedded version of OpenSSL.
+GitLab has a dedicated repository
+([`gitlab/gitlab-fips`](https://packages.gitlab.com/gitlab/gitlab-fips))
+for builds of the Omnibus GitLab which are built with FIPS compliance.
+These GitLab builds are compiled to use the system OpenSSL, instead of
+the Omnibus-embedded version of OpenSSL. These packages are built for:
+
+- RHEL 8 (and compatible)
+- AmazonLinux 2
+- Ubuntu
+
+These are [consumed by the GitLab Environment Toolkit](#install-gitlab-with-fips-compliance) (GET).
See [the section on how FIPS builds are created](#how-fips-builds-are-created).
+### Nightly Omnibus FIPS builds
+
+The Distribution team has created [nightly FIPS Omnibus builds](https://packages.gitlab.com/gitlab/nightly-fips-builds),
+which can be used for *testing* purposes. These should never be used for production environments.
+
## Runner
See the [documentation on installing a FIPS-compliant GitLab Runner](https://docs.gitlab.com/runner/install/#fips-compliant-gitlab-runner).
diff --git a/doc/development/foreign_keys.md b/doc/development/foreign_keys.md
deleted file mode 100644
index cdf655bf0bf..00000000000
--- a/doc/development/foreign_keys.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/foreign_keys.md'
-remove_date: '2022-11-05'
----
-
-This document was moved to [another location](database/foreign_keys.md).
-
-<!-- This redirect file can be deleted after <2022-11-05>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/gemfile.md b/doc/development/gemfile.md
index 3c7dc19da8e..36ef1bcd834 100644
--- a/doc/development/gemfile.md
+++ b/doc/development/gemfile.md
@@ -47,7 +47,8 @@ This needs to be done for any new, or updated gems.
We do not allow gems that are fetched from Git repositories. All gems have
to be available in the RubyGems index. We want to minimize external build
-dependencies and build times.
+dependencies and build times. It's enforced by the RuboCop rule
+[`Cop/GemFetcher`](https://gitlab.com/gitlab-org/ruby/gems/gitlab-styles/-/blob/master/lib/rubocop/cop/gem_fetcher.rb).
## Review the new dependency for quality
@@ -56,7 +57,7 @@ This means that new dependencies should, at a minimum, meet the following criter
- They have an active developer community. At the minimum a maintainer should still be active
to merge change requests in case of emergencies.
-- There are no issues open that we know may impact the availablity or performance of GitLab.
+- There are no issues open that we know may impact the availability or performance of GitLab.
- The project is tested using some form of test automation. The test suite must be passing
using the Ruby version currently used by GitLab.
- If the project uses a C extension, consider requesting an additional review from a C or MRI
diff --git a/doc/development/geo.md b/doc/development/geo.md
index 884c09cc174..76c75cb1c6a 100644
--- a/doc/development/geo.md
+++ b/doc/development/geo.md
@@ -200,7 +200,7 @@ sequenceDiagram
S->>TDB: Insert to `job_artifact_registry`
```
-- [Sidekiq-cron](https://github.com/ondrejbartas/sidekiq-cron) enqueues a `Geo::Secondary::RegistryConsistencyWorker` job every minute. As long as it is actively doing work (creating and deleting rows), this job immediately reenqueues itself. This job uses an exclusive lease to prevent multiple instances of itself from running simultaneously.
+- [Sidekiq-cron](https://github.com/ondrejbartas/sidekiq-cron) enqueues a `Geo::Secondary::RegistryConsistencyWorker` job every minute. As long as it is actively doing work (creating and deleting rows), this job immediately re-enqueues itself. This job uses an exclusive lease to prevent multiple instances of itself from running simultaneously.
- [Sidekiq](architecture.md#sidekiq) picks up `Geo::Secondary::RegistryConsistencyWorker` job
- Sidekiq queries `ci_job_artifacts` table for up to 10000 rows
- Sidekiq queries `job_artifact_registry` table for up to 10000 rows
@@ -308,7 +308,7 @@ sequenceDiagram
- Sidekiq queries `job_artifact_registry` in the [PostgreSQL Geo Tracking Database](#tracking-database) for the number of rows marked "pending verification" or "failed verification and ready to retry"
- Sidekiq enqueues one or more `Geo::VerificationBatchWorker` jobs, limited by the "maximum verification concurrency" setting
- Sidekiq picks up `Geo::VerificationBatchWorker` job
- - Sidekiq queries `job_artifact_registry` in the PostgreSQL Geo Tracking Databasef for rows marked "pending verification"
+ - Sidekiq queries `job_artifact_registry` in the PostgreSQL Geo Tracking Database for rows marked "pending verification"
- If the previous step yielded less than 10 rows, then Sidekiq queries `job_artifact_registry` for rows marked "failed verification and ready to retry"
- For each row
- Sidekiq marks it "started verification"
@@ -588,23 +588,45 @@ When some write actions are not allowed because the site is a
The database itself will already be read-only in a replicated setup,
so we don't need to take any extra step for that.
-## Steps needed to replicate a new data type
-
-As GitLab evolves, we constantly need to add new resources to the Geo replication system.
-The implementation depends on resource specifics, but there are several things
-that need to be taken care of:
-
-- Event generation on the primary site. Whenever a new resource is changed/updated, we need to
- create a task for the Log Cursor.
-- Event handling. The Log Cursor needs to have a handler for every event type generated by the primary site.
-- Dispatch worker (cron job). Make sure the backfill condition works well.
-- Sync worker.
-- Registry with all possible states.
-- Verification.
-- Cleaner. When sync settings are changed for the secondary site, some resources need to be cleaned up.
-- Geo Node Status. We need to provide API endpoints as well as some presentation in the GitLab Admin Area.
-- Health Check. If we can perform some pre-cheÑks and make site unhealthy if something is wrong, we should do that.
- The `rake gitlab:geo:check` command has to be updated too.
+## Ensuring a new feature has Geo support
+
+Geo depends on PostgreSQL replication of the main and CI databases, so if you add a new table or field, it should already work on secondary Geo sites.
+
+However, if you introduce a new kind of data which is stored outside of the main and CI PostgreSQL databases, then you need to ensure that this data is replicated and verified by Geo. This is necessary for customers to be able to rely on their secondary sites for [disaster recovery](../administration/geo/disaster_recovery/index.md).
+
+The following subsections describe how to determine whether work is needed, and if so, how to proceed. If you have any questions, [contact the Geo team](https://about.gitlab.com/handbook/product/categories/#geo-group).
+
+For comparison with your own features, see [Supported Geo data types](../administration/geo/replication/datatypes.md). It has a detailed, up-to-date list of the types of data that Geo replicates and verifies.
+
+### Git repositories
+
+If you add a feature that is backed by Git repositories, then you must add Geo support. See [the repository replicator strategy of the Geo self-service framework](geo/framework.md#repository-replicator-strategy).
+
+### Blobs
+
+If you add a subclass of `CarrierWave::Uploader::Base`, then you are adding what Geo calls a blob. If you specifically subclass [`AttachmentUploader` as generally recommended](uploads/working_with_uploads.md#recommendations), then the data has Geo support with no work needed. This is because `AttachmentUploader` tracks blobs with the `Upload` model using the `uploads` table, and Geo support is already implemented for that model.
+
+If your blobs are tracked in a new table, perhaps because you expect millions of rows at GitLab.com scale, then you must add Geo support. See [the blob replicator strategy of the Geo self-service framework](geo/framework.md#blob-replicator-strategy).
+
+[Geo detects new blobs with a spec](https://gitlab.com/gitlab-org/gitlab/-/blob/eeba0e4d231ae39012a5bbaeac43a72c2bd8affb/ee/spec/uploaders/every_gitlab_uploader_spec.rb) that fails when an `Uploader` does not have a corresponding `Replicator`.
+
+### Features with more than one kind of data
+
+If a new complex feature is backed by multiple kinds of data, for example, a Git repository and a blob, then you can likely consider each kind of data separately.
+
+Taking [Designs](../user/project/issues/design_management.md) as an example, each issue has a Git repository which can have many LFS objects, and each LFS object may have an automatically generated thumbnail.
+
+- LFS objects were already supported by Geo, so no Geo-specific work was needed.
+- The implementation of thumbnails reused the `Upload` model, so no Geo-specific work was needed.
+- Design Git repositories were not inherently supported by Geo, so work was needed.
+
+As another example, [Dependency Proxy](../administration/packages/dependency_proxy.md) is backed by two kinds of blobs, `DependencyProxy::Blob` and `DependencyProxy::Manifest`. We can use [the blob replicator strategy of the Geo self-service framework](geo/framework.md#blob-replicator-strategy) on each type, independent of each other.
+
+### Other kinds of data
+
+If a new feature introduces a new kind of data which is not a Git repository, or a blob, or a combination of the two, then contact the Geo team to discuss how to handle it.
+
+As an example, Container Registry data does not easily fit into the above categories. It is backed by a registry service which owns the data, and GitLab interacts with the registry service's API. So a one off approach is required for Geo support of Container Registry. Still, we are able to reuse much of the glue code of [the Geo self-service framework](geo/framework.md#repository-replicator-strategy).
## History of communication channel
diff --git a/doc/development/geo/framework.md b/doc/development/geo/framework.md
index 3624d280f86..60529db5ce6 100644
--- a/doc/development/geo/framework.md
+++ b/doc/development/geo/framework.md
@@ -18,6 +18,14 @@ across Geo sites. This API is presented as a Ruby Domain-Specific
Language (DSL) and aims to make it possible to replicate data with
minimal effort of the engineer who created a data type.
+## Geo is a requirement in the definition of done
+
+Geo is the GitLab solution for [disaster recovery](https://about.gitlab.com/direction/geo/disaster_recovery/). A robust disaster recovery solution must replicate **all GitLab data** such that all GitLab services can be successfully restored in their entirety with minimal data loss in the event of a disaster.
+
+For this reason, Geo replication and verification support for GitLab generated data is part of the [definition of done](../contributing/merge_request_workflow.md#definition-of-done). This ensures that new features ship with Geo support and our customers are not exposed to data loss.
+
+Adding Geo support with the Self Service Framework (SSF) is easy and outlined in detail on this page for various types of data. However, for a more general guide that can help you decide if and how you need to add Geo support for a new GitLab feature, [you may start here](../geo.md#ensuring-a-new-feature-has-geo-support).
+
## Nomenclature
Before digging into the API, developers need to know some Geo-specific
diff --git a/doc/development/geo/proxying.md b/doc/development/geo/proxying.md
index 67d4129a9d0..f3136890788 100644
--- a/doc/development/geo/proxying.md
+++ b/doc/development/geo/proxying.md
@@ -260,12 +260,7 @@ S-->>C: return Git response from primary
## Git push
-### Unified URLs
-
-With unified URLs, a push will redirect to a local path formatted as `/-/push_from_secondary/$SECONDARY_ID/*`. Further
-requests through this path will be proxied to the primary, which will handle the push.
-
-#### Git push over SSH
+### Git push over SSH
As SSH operations go through GitLab Shell instead of Workhorse, they are not proxied through the mechanism used for
Workhorse requests. With SSH operations, they are proxied as Git HTTP requests to the primary site by the secondary
@@ -293,7 +288,12 @@ I-->>S: <response>
S-->>C: return Git response from primary
```
-#### Git push over HTTP(s)
+### Git push over HTTP(S)
+
+#### Git push over HTTP(S) unified URLs
+
+With unified URLs, a push redirects to a local path formatted as `/-/push_from_secondary/$SECONDARY_ID/*`. Further
+requests through this path are proxied to the primary, which will handle the push.
```mermaid
sequenceDiagram
@@ -326,7 +326,7 @@ W-->>Wsec: Pipe messages to the Git client
Wsec-->>C: Return piped messages from Git
```
-### Git push over HTTP with Separate URLs
+#### Git push over HTTP(S) with separate URLs
With separate URLs, the secondary will redirect to a URL formatted like `$PRIMARY/-/push_from_secondary/$SECONDARY_ID/*`.
diff --git a/doc/development/git_object_deduplication.md b/doc/development/git_object_deduplication.md
index dcfcd2e864a..6014ccbfb39 100644
--- a/doc/development/git_object_deduplication.md
+++ b/doc/development/git_object_deduplication.md
@@ -171,7 +171,7 @@ There are three different things that can go wrong here.
#### 1. SQL says repository A belongs to pool P but Gitaly says A has no alternate objects
-In this case, we miss out on disk space savings but all RPC's on A
+In this case, we miss out on disk space savings but all RPCs on A
itself function fine. The next time garbage collection runs on A,
the alternates connection gets established in Gitaly. This is done by
`Projects::GitDeduplicationService` in GitLab Rails.
diff --git a/doc/development/gitaly.md b/doc/development/gitaly.md
index a570b5dd7eb..a23f1dd2d80 100644
--- a/doc/development/gitaly.md
+++ b/doc/development/gitaly.md
@@ -56,7 +56,7 @@ they are merged.
### `gitaly-ruby`
-It is possible to implement and test RPC's in Gitaly using Ruby code,
+It is possible to implement and test RPCs in Gitaly using Ruby code,
in
[`gitaly-ruby`](https://gitlab.com/gitlab-org/gitaly/tree/master/ruby).
This should make it easier to contribute for developers who are less
@@ -324,7 +324,7 @@ the integration by using GDK:
1. The state of the flag must be observable. To check it, you must enable it
by fetching the Prometheus metrics:
- 1. Navigate to GDK's root directory.
+ 1. Navigate to the GDK root directory.
1. Make sure you have the proper branch checked out for Gitaly.
1. Recompile it with `make gitaly-setup` and restart the service with `gdk restart gitaly`.
1. Make sure your setup is running: `gdk status | grep praefect`.
@@ -342,7 +342,7 @@ the integration by using GDK:
1. After you observe the metrics for the new feature flag and it increments, you
can enable the new feature:
- 1. Navigate to GDK's root directory.
+ 1. Navigate to the GDK root directory.
1. Start a Rails console:
```shell
diff --git a/doc/development/github_importer.md b/doc/development/github_importer.md
index edf78d5f83d..17b4a28f57d 100644
--- a/doc/development/github_importer.md
+++ b/doc/development/github_importer.md
@@ -192,7 +192,7 @@ briefly waits for jobs to complete before deciding what the next action should
be. For small projects, this may slow down the import process a bit, but it
also reduces pressure on the system as a whole.
-## Refreshing import JIDs
+## Refreshing import job IDs
GitLab includes a worker called `Gitlab::Import::StuckProjectImportJobsWorker`
that periodically runs and marks project imports as failed if they have been
@@ -203,7 +203,7 @@ often we hit the GitHub rate limit (more on this below), but we don't want
To prevent this from happening we periodically refresh the expiration time of
the import process. This works by storing the JID of the import job in the
-database, then refreshing this JID's TTL at various stages throughout the import
+database, then refreshing this JID TTL at various stages throughout the import
process. This is done by calling `ProjectImportState#refresh_jid_expiration`. By
refreshing this TTL we can ensure our import does not get marked as failed so
long we're still performing work.
diff --git a/doc/development/gitlab_flavored_markdown/specification_guide/index.md b/doc/development/gitlab_flavored_markdown/specification_guide/index.md
index 17afebcf6ee..85c6653180b 100644
--- a/doc/development/gitlab_flavored_markdown/specification_guide/index.md
+++ b/doc/development/gitlab_flavored_markdown/specification_guide/index.md
@@ -157,7 +157,7 @@ official specifications, but are part of the GitHub and GitLab internal Markdown
implementations. These internal extensions are often dependent upon the GitHub or GitLab
implementations or environments, and may depend upon metadata which is only available via
interacting with those environments. For example,
-[GitHub supports GitHub-specific autolinked references](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/autolinked-references-and-urls),
+[GitHub supports GitHub-specific automatically linked references](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/autolinked-references-and-urls),
and
[GitLab also supports GitLab-specific references](../../../user/markdown.md#gitlab-specific-references).
These may also be implemented by third-party Markdown rendering engines which integrate with
@@ -458,7 +458,7 @@ You can see the RSpec shared context containing these fixtures in
In some cases, fixtures may not be usable, because they do not provide control over the varying
values. In these cases, we can introduce support for a environment variable into the production
-code, which allows us to override the randommness in our test environment when we are
+code, which allows us to override the randomness in our test environment when we are
generating the HTML for footnote examples. Even though it is in the production code path, it has
no effect unless it is explicitly set, therefore it is innocuous. It allows us to avoid
the more-complex regex-based normalization described below.
@@ -1056,7 +1056,7 @@ allows control over other aspects of the snapshot example generation process.
the example will only be run by `ee/spec/requests/api/markdown_snapshot_spec.rb`, not by
`spec/requests/api/markdown_snapshot_spec.rb`.
- The `api_request_override_path` field overrides the API endpoint path which is used to
- generate the `static` HTML for the specifed example. Different endpoints can generate different
+ generate the `static` HTML for the specified example. Different endpoints can generate different
HTML in some cases, so we want to be able to exercise different API endpoints for the same
Markdown. By default, the `/markdown` endpoint is used.
@@ -1149,7 +1149,7 @@ These files are Markdown specification files containing examples generated based
similar to the `output_spec/spec.txt` and `output_spec/spec.html`, with the following differences:
1. They contain a superset of _all_ examples from
- the Commonmark, GitHub Flavored Markdown, and GitLab Flavored Markdown specifications, whereas
+ the CommonMark, GitHub Flavored Markdown, and GitLab Flavored Markdown specifications, whereas
`spec.*` only contains the GLFM specification. This is to provide a single place to refer to
all examples when working with [snapshot testing](#markdown-snapshot-testing).
1. They contain _only_ header sections which contain examples. They do not contain any prose-only
diff --git a/doc/development/gitpod_internals.md b/doc/development/gitpod_internals.md
new file mode 100644
index 00000000000..a4674df758d
--- /dev/null
+++ b/doc/development/gitpod_internals.md
@@ -0,0 +1,30 @@
+---
+stage: none
+group: Engineering Productivity
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Gitpod internal configuration
+
+## Settings
+
+The settings for `gitlab-org/gitlab` are defined under a [project's settings in a Gitpod dashboard](https://gitpod.io/t/gitlab-org/gitlab/settings). To view the settings, you must first join the `gitlab-org` team on [Gitpod.io](https://gitpod.io/). You can join the team using the bookmark link at the top of `#gitpod-gdk` internal Slack channel.
+
+The current settings are:
+
+- `Enable Incremental Prebuilds`: Uses an earlier successful prebuild to create new prebuilds faster.
+- `Use Last Successful Prebuild`: Skips waiting for prebuilds currently in progress and uses the last successful prebuild from previous commits on the same branch.
+
+## Webhooks
+
+A webhook that starts with `https://gitpod.io/` is created to enable prebuilds (see [Enabling Prebuilds](https://www.gitpod.io/docs/configure/authentication/gitlab#enabling-prebuilds) for more details). The webhook is maintained by an [Engineering Productivity team](https://about.gitlab.com/handbook/engineering/quality/engineering-productivity/).
+
+You can find this webhook in [Webhook Settings in `gitlab-org/gitlab`](https://gitlab.com/gitlab-org/gitlab/-/hooks). If you cannot access this setting, please chat to the [Engineering Productivity team](https://about.gitlab.com/handbook/engineering/quality/engineering-productivity/).
+
+### Troubleshooting a failed webhook
+
+If a webhook failed to connect for a long time, then it may have been disabled in the project.
+
+To re-enable a failing or failed webhook, send a test request in [Webhook Settings](https://gitlab.com/gitlab-org/gitlab/-/hooks). See [Re-enable disabled webhooks page](https://docs.gitlab.com/15.4/ee/user/project/integrations/webhooks.html#re-enable-disabled-webhooks) for more details.
+
+After re-enabling, check the prebuilds' health in a [project's prebuilds](https://gitpod.io/t/gitlab-org/gitlab/prebuilds) and confirm that prebuilds start without any errors.
diff --git a/doc/development/go_guide/index.md b/doc/development/go_guide/index.md
index b561ebc4285..e7e628f5293 100644
--- a/doc/development/go_guide/index.md
+++ b/doc/development/go_guide/index.md
@@ -391,7 +391,7 @@ functionality:
This gives us a thin abstraction over underlying implementations that is
consistent across Workhorse, Gitaly, and, in future, other Go servers. For
example, in the case of `gitlab.com/gitlab-org/labkit/tracing` we can switch
-from using `Opentracing` directly to using `Zipkin` or Gokit's own tracing wrapper
+from using `Opentracing` directly to using `Zipkin` or the Go kit's own tracing wrapper
without changes to the application code, while still keeping the same
consistent configuration mechanism (that is, the `GITLAB_TRACING` environment
variable).
diff --git a/doc/development/graphql_guide/batchloader.md b/doc/development/graphql_guide/batchloader.md
index ef0b97f4f62..6a716de61b8 100644
--- a/doc/development/graphql_guide/batchloader.md
+++ b/doc/development/graphql_guide/batchloader.md
@@ -180,7 +180,7 @@ def resolve(args = {}, context = { current_user: current_user })
end
```
-We can also use [QueryRecorder](../query_recorder.md) to make sure we are performing only **one SQL query** per call.
+We can also use [QueryRecorder](../database/query_recorder.md) to make sure we are performing only **one SQL query** per call.
```ruby
it 'executes only 1 SQL query' do
diff --git a/doc/development/hash_indexes.md b/doc/development/hash_indexes.md
deleted file mode 100644
index 2a9f7e5a25d..00000000000
--- a/doc/development/hash_indexes.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/hash_indexes.md'
-remove_date: '2022-11-06'
----
-
-This document was moved to [another location](database/hash_indexes.md).
-
-<!-- This redirect file can be deleted after <2022-11-06>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/i18n/externalization.md b/doc/development/i18n/externalization.md
index 91e2efcb2a3..2269a28e496 100644
--- a/doc/development/i18n/externalization.md
+++ b/doc/development/i18n/externalization.md
@@ -284,9 +284,7 @@ expect(wrapper.text()).toEqual(MSG_ALERT_SETTINGS_FORM_ERROR);
### Dynamic translations
-Sometimes there are dynamic translations that the parser can't find when running
-`bin/rake gettext:find`. For these scenarios you can use the [`N_` method](https://github.com/grosser/gettext_i18n_rails/blob/c09e38d481e0899ca7d3fc01786834fa8e7aab97/Readme.md#unfound-translations-with-rake-gettextfind).
-There's also an alternative method to [translate messages from validation errors](https://github.com/grosser/gettext_i18n_rails/blob/c09e38d481e0899ca7d3fc01786834fa8e7aab97/Readme.md#option-a).
+For more details you can see how we [keep translations dynamic](#keep-translations-dynamic).
## Working with special content
@@ -764,6 +762,10 @@ class MyPresenter
end
```
+Sometimes there are dynamic translations that the parser can't find when running
+`bin/rake gettext:find`. For these scenarios you can use the [`N_` method](https://github.com/grosser/gettext_i18n_rails/blob/c09e38d481e0899ca7d3fc01786834fa8e7aab97/Readme.md#unfound-translations-with-rake-gettextfind).
+There's also an alternative method to [translate messages from validation errors](https://github.com/grosser/gettext_i18n_rails/blob/c09e38d481e0899ca7d3fc01786834fa8e7aab97/Readme.md#option-a).
+
### Splitting sentences
Never split a sentence, as it assumes the sentence's grammar and structure is the same in all
diff --git a/doc/development/image_scaling.md b/doc/development/image_scaling.md
index 4b19c21a457..502a18fecd7 100644
--- a/doc/development/image_scaling.md
+++ b/doc/development/image_scaling.md
@@ -1,6 +1,6 @@
---
-stage: Data Stores
-group: Application Performance
+stage: Manage
+group: Workspace
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
@@ -26,7 +26,7 @@ Whether we allow an image to be rescaled or not is decided by combination of har
The hard-coded rules only permit:
- [Project, group and user avatars](https://gitlab.com/gitlab-org/gitlab/-/blob/fd08748862a5fe5c25b919079858146ea85843ae/app/controllers/concerns/send_file_upload.rb#L65-67)
-- [PNGs or JPEGs](https://gitlab.com/gitlab-org/gitlab/-/blob/5dff8fa3814f2a683d8884f468cba1ec06a60972/lib/gitlab/file_type_detection.rb#L23)
+- [PNG or JPEG images](https://gitlab.com/gitlab-org/gitlab/-/blob/5dff8fa3814f2a683d8884f468cba1ec06a60972/lib/gitlab/file_type_detection.rb#L23)
- [Specific dimensions](https://gitlab.com/gitlab-org/gitlab/-/blob/5dff8fa3814f2a683d8884f468cba1ec06a60972/app/models/concerns/avatarable.rb#L6)
Furthermore, configuration in Workhorse can lead to the image scaler rejecting a request if:
diff --git a/doc/development/import_project.md b/doc/development/import_project.md
index b879d635350..7f5a0faf8fb 100644
--- a/doc/development/import_project.md
+++ b/doc/development/import_project.md
@@ -57,7 +57,7 @@ This method takes longer to import than the other methods and depends on several
This script was introduced in GitLab 12.6 for importing large GitLab project exports.
-As part of this script we also disable direct and background upload to avoid situations where a huge archive is being uploaded to GCS (while being inside a transaction, which can cause idle transaction timeouts).
+As part of this script we also disable direct upload to avoid situations where a huge archive is being uploaded to GCS (while being inside a transaction, which can cause idle transaction timeouts).
We can run this script from the terminal:
diff --git a/doc/development/insert_into_tables_in_batches.md b/doc/development/insert_into_tables_in_batches.md
deleted file mode 100644
index ced5332e880..00000000000
--- a/doc/development/insert_into_tables_in_batches.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/insert_into_tables_in_batches.md'
-remove_date: '2022-11-05'
----
-
-This document was moved to [another location](database/insert_into_tables_in_batches.md).
-
-<!-- This redirect file can be deleted after <2022-11-05>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/integrations/codesandbox.md b/doc/development/integrations/codesandbox.md
index d7d0ea48db0..b7fe3fbd1c4 100644
--- a/doc/development/integrations/codesandbox.md
+++ b/doc/development/integrations/codesandbox.md
@@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Set up local CodeSandbox development environment
This guide walks through setting up a local [CodeSandbox repository](https://github.com/codesandbox/codesandbox-client) and integrating it with a local GitLab instance. CodeSandbox
-is used to power the Web IDE's [Live Preview feature](../../user/project/web_ide/index.md#live-preview). Having a local CodeSandbox setup is useful for debugging upstream issues or
+is used to power the Web IDE [Live Preview feature](../../user/project/web_ide/index.md#live-preview). Having a local CodeSandbox setup is useful for debugging upstream issues or
creating upstream contributions like [this one](https://github.com/codesandbox/codesandbox-client/pull/5137).
## Initial setup
@@ -43,7 +43,7 @@ Before using CodeSandbox with your local GitLab instance, you must:
GitLab integrates with two parts of CodeSandbox:
- An npm package called `smooshpack` (called `sandpack` in the `codesandbox-client` project).
- This exposes an entrypoint for us to kick off Codesandbox's bundler.
+ This exposes an entrypoint for us to kick off CodeSandbox's bundler.
- A server that houses CodeSandbox assets for bundling and previewing. This is hosted
on a separate server for security.
diff --git a/doc/development/integrations/index.md b/doc/development/integrations/index.md
index 2639da818c6..1c9144a1163 100644
--- a/doc/development/integrations/index.md
+++ b/doc/development/integrations/index.md
@@ -58,7 +58,7 @@ end
`Integration.prop_accessor` installs accessor methods on the class. Here we would have `#url`, `#url=` and `#url_changed?`, to manage the `url` field. Fields stored in `Integration#properties` should be accessed by these accessors directly on the model, just like other ActiveRecord attributes.
-You should always access the properties through their getters, and not interact with the `properties` hash directly.
+You should always access the properties through their `getters`, and not interact with the `properties` hash directly.
You **must not** write to the `properties` hash, you **must** use the generated setter method instead. Direct writes to this
hash are not persisted.
@@ -124,6 +124,39 @@ module Integrations
end
```
+## Define configuration test
+
+Optionally, you can define a configuration test of an integration's settings. The test is executed from the integration form's **Test** button, and results are returned to the user.
+
+A good configuration test:
+
+- Does not change data on the service. For example, it should not trigger a CI build. Sending a message is okay.
+- Is meaningful and as thorough as possible.
+
+If it's not possible to follow the above guidelines, consider not adding a configuration test.
+
+To add a configuration test, define a `#test` method for the integration model.
+
+The method receives `data`, which is a test push event payload.
+It should return a hash, containing the keys:
+
+- `success` (required): a boolean to indicate if the configuration test has passed.
+- `result` (optional): a message returned to the user if the configuration test has failed.
+
+For example:
+
+```ruby
+module Integrations
+ class FooBar < Integration
+ def test(data)
+ success = test_api_key(data)
+
+ { success: success, result: 'API key is invalid' }
+ end
+ end
+end
+```
+
### Customize the frontend form
The frontend form is generated dynamically based on metadata defined in the model.
@@ -282,6 +315,8 @@ You can also refer to our general [documentation guidelines](../documentation/in
## Testing
+Testing should not be confused with [defining configuration tests](#define-configuration-test).
+
It is often sufficient to add tests for the integration model in `spec/models/integrations`,
and a factory with example settings in `spec/factories/integrations.rb`.
diff --git a/doc/development/integrations/jira_connect.md b/doc/development/integrations/jira_connect.md
index d4215662db4..c7bb77a6a5d 100644
--- a/doc/development/integrations/jira_connect.md
+++ b/doc/development/integrations/jira_connect.md
@@ -59,6 +59,19 @@ To install the app in Jira:
_Note that any changes to the app descriptor requires you to uninstall then reinstall the app._
+## Simple setup
+
+To avoid external dependencies like Gitpod and a Jira Cloud instance, use the [Jira connect test tool](https://gitlab.com/gitlab-org/manage/integrations/jira-connect-test-tool) and your local GDK:
+
+1. Clone the [**Jira-connect-test-tool**](https://gitlab.com/gitlab-org/manage/integrations/jira-connect-test-tool) `git clone git@gitlab.com:gitlab-org/manage/integrations/jira-connect-test-tool.git`.
+1. Start the app `bundle exec rackup`. (The app requires your GDK GitLab to be available on `http://127.0.0.1:3000`.).
+1. Open `config/gitlab.yml` and uncomment the `jira_connect` config.
+1. Restart GDK.
+1. Go to `http://127.0.0.1:3000/-/profile/personal_access_tokens`.
+1. Create a new token with the `api` scope and copy the token.
+1. Go to `http://localhost:9292`.
+1. Paste the token and select **Install GitLab.com Jira Cloud app**.
+
### Troubleshooting
If the app install failed, you might need to delete `jira_connect_installations` from your database.
@@ -68,7 +81,7 @@ If the app install failed, you might need to delete `jira_connect_installations`
#### Not authorized to access the file
-If you use Gitpod and you get an error about Jira not being able to access the descriptor file, you might need to make the GDK's port public by following these steps:
+If you use Gitpod and you get an error about Jira not being able to access the descriptor file, you might need to make the GDK port public by following these steps:
1. Open your GitLab workspace in Gitpod.
1. When the GDK is running, select **Ports** in the bottom-right corner.
diff --git a/doc/development/integrations/secure.md b/doc/development/integrations/secure.md
index 787b46133ad..190a6f6eda2 100644
--- a/doc/development/integrations/secure.md
+++ b/doc/development/integrations/secure.md
@@ -102,7 +102,7 @@ it's declared under the `reports:sast` key in the job definition, not because of
### Policies
-Certain GitLab workflows, such as [AutoDevOps](../../topics/autodevops/customize.md#disable-jobs),
+Certain GitLab workflows, such as [AutoDevOps](../../topics/autodevops/cicd_variables.md#job-disabling-variables),
define CI/CD variables to indicate that given scans should be disabled. You can check for this by looking
for variables such as:
@@ -328,21 +328,6 @@ You can find the schemas for these scanners here:
- [SAST](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/master/dist/sast-report-format.json)
- [Secret Detection](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/master/dist/secret-detection-report-format.json)
-### Retention period for vulnerabilities
-
-GitLab has the following retention policies for vulnerabilities on non-default branches. Vulnerabilities are no longer available:
-
-- When the related CI job artifact expires.
-- 90 days after the pipeline is created, even if the related CI job artifacts are locked.
-
-To view vulnerabilities, either:
-
-- Run a new pipeline.
-- Download the related CI job artifacts if they are available.
-
-NOTE:
-This does not apply for the vulnerabilities existing on the default branch.
-
### Report validation
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/351000) in GitLab 15.0.
diff --git a/doc/development/integrations/secure_partner_integration.md b/doc/development/integrations/secure_partner_integration.md
index bcbc02d4827..853541144fb 100644
--- a/doc/development/integrations/secure_partner_integration.md
+++ b/doc/development/integrations/secure_partner_integration.md
@@ -90,7 +90,7 @@ and complete an integration with the Secure stage.
- Documentation for [SAST reports](../../user/application_security/sast/index.md#reports-json-format).
- Documentation for [Dependency Scanning reports](../../user/application_security/dependency_scanning/index.md#reports-json-format).
- Documentation for [Container Scanning reports](../../user/application_security/container_scanning/index.md#reports-json-format).
- - See this [example secure job definition that also defines the artifact created](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml).
+ - See this [example secure job definition that also defines the artifact created](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Container-Scanning.gitlab-ci.yml).
- If you need a new kind of scan or report, [create an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new#)
and add the label `devops::secure`.
- Once the job is completed, the data can be seen:
diff --git a/doc/development/iterating_tables_in_batches.md b/doc/development/iterating_tables_in_batches.md
deleted file mode 100644
index 589e38a5cb0..00000000000
--- a/doc/development/iterating_tables_in_batches.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/iterating_tables_in_batches.md'
-remove_date: '2022-11-06'
----
-
-This document was moved to [another location](database/iterating_tables_in_batches.md).
-
-<!-- This redirect file can be deleted after <2022-11-06>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/lfs.md b/doc/development/lfs.md
index 4d3371af1d4..dd7687cd28b 100644
--- a/doc/development/lfs.md
+++ b/doc/development/lfs.md
@@ -4,7 +4,10 @@ group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Git LFS **(FREE)**
+# Git LFS developer information **(FREE)**
+
+This page contains developer-centric information for GitLab team members. For the
+user documentation, see [Git Large File Storage](../topics/git/lfs/index.md).
## Deep Dive
@@ -86,3 +89,9 @@ request is not preserved for the internal API requests made by Gitaly
correlation IDs for those API requests are random values until
[this Workhorse issue](https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/309) is
resolved.
+
+## Related topics
+
+- Blog post: [Getting started with Git LFS](https://about.gitlab.com/blog/2017/01/30/getting-started-with-git-lfs-tutorial/)
+- User documentation: [Git Large File Storage (LFS)](../topics/git/lfs/index.md)
+- [GitLab Git Large File Storage (LFS) Administration](../administration/lfs/index.md) for self-managed instances
diff --git a/doc/development/licensing.md b/doc/development/licensing.md
index d7dfdb7357d..cb703e98264 100644
--- a/doc/development/licensing.md
+++ b/doc/development/licensing.md
@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Automated Testing
-In order to comply with the terms the libraries we use are licensed under, we have to make sure to check new gems for compatible licenses whenever they're added. To automate this process, we use the [license_finder](https://github.com/pivotal/LicenseFinder) gem by Pivotal. It runs every time a new commit is pushed and verifies that all gems and node modules in the bundle use a license that doesn't conflict with the licensing of either GitLab Community Edition or GitLab Enterprise Edition.
+To comply with the terms the libraries we use are licensed under, we have to make sure to check new gems for compatible licenses whenever they're added. To automate this process, we use the [License Finder](https://github.com/pivotal/LicenseFinder) gem by Pivotal. It runs every time a new commit is pushed and verifies that all gems and node modules in the bundle use a license that doesn't conflict with the licensing of either GitLab Community Edition or GitLab Enterprise Edition.
There are some limitations with the automated testing, however. CSS, JavaScript, or Ruby libraries which are not included by way of Bundler, npm, or Yarn (for instance those manually copied into our source tree in the `vendor` directory), must be verified manually and independently. Take care whenever one such library is used, as automated tests don't catch problematic licenses from them.
diff --git a/doc/development/merge_request_performance_guidelines.md b/doc/development/merge_request_performance_guidelines.md
index 9d1489836fb..fd78d02202f 100644
--- a/doc/development/merge_request_performance_guidelines.md
+++ b/doc/development/merge_request_performance_guidelines.md
@@ -145,7 +145,7 @@ end
This means running one query for every object to update. This code can
easily overload a database given enough rows to update or many instances of this
code running in parallel. This particular problem is known as the
-["N+1 query problem"](https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations). You can write a test with [QueryRecorder](query_recorder.md) to detect this and prevent regressions.
+["N+1 query problem"](https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations). You can write a test with [QueryRecorder](database/query_recorder.md) to detect this and prevent regressions.
In this particular case the workaround is fairly easy:
@@ -224,7 +224,7 @@ The total number of the queries (including cached ones) executed by the code mod
should not increase unless absolutely necessary.
The number of executed queries (including cached queries) should not depend on
collection size.
-You can write a test by passing the `skip_cached` variable to [QueryRecorder](query_recorder.md) to detect this and prevent regressions.
+You can write a test by passing the `skip_cached` variable to [QueryRecorder](database/query_recorder.md) to detect this and prevent regressions.
As an example, say you have a CI pipeline. All pipeline builds belong to the same pipeline,
thus they also belong to the same project (`pipeline.project`):
@@ -351,7 +351,7 @@ Post.all.includes(:author).each do |post|
end
```
-Also consider using [QueryRecoder tests](query_recorder.md) to prevent a regression when eager loading.
+Also consider using [QueryRecoder tests](database/query_recorder.md) to prevent a regression when eager loading.
## Memory Usage
@@ -463,7 +463,7 @@ Read more about when and how feature flags should be used in
We can consider the following types of storages:
-- **Local temporary storage** (very-very short-term storage) This type of storage is system-provided storage, ex. `/tmp` folder.
+- **Local temporary storage** (very-very short-term storage) This type of storage is system-provided storage, like a `/tmp` folder.
This is the type of storage that you should ideally use for all your temporary tasks.
The fact that each node has its own temporary storage makes scaling significantly easier.
This storage is also very often SSD-based, thus is significantly faster.
@@ -480,7 +480,7 @@ We can consider the following types of storages:
Be respectful of that.
- **Shared persistent storage** (long-term storage) This type of storage uses
- shared network-based storage (ex. NFS). This solution is mostly used by customers running small
+ shared network-based storage (for example, NFS). This solution is mostly used by customers running small
installations consisting of a few nodes. The files on shared storage are easily accessible,
but any job that is uploading or downloading data can create a serious contention to all other jobs.
This is also an approach by default used by Omnibus.
@@ -525,13 +525,13 @@ end
The usage of shared temporary storage is required if your intent
is to persistent file for a disk-based storage, and not Object Storage.
-[Workhorse direct_upload](uploads/index.md#direct-upload) when accepting file
+[Workhorse direct upload](uploads/index.md#direct-upload) when accepting file
can write it to shared storage, and later GitLab Rails can perform a move operation.
The move operation on the same destination is instantaneous.
The system instead of performing `copy` operation just re-attaches file into a new place.
Since this introduces extra complexity into application, you should only try
-to re-use well established patterns (ex.: `ObjectStorage` concern) instead of re-implementing it.
+to re-use well established patterns (for example, `ObjectStorage` concern) instead of re-implementing it.
The usage of shared temporary storage is otherwise deprecated for all other usages.
@@ -549,7 +549,7 @@ that implements a seamless support for Shared and Object Storage-based persisten
#### Data access
Each feature that accepts data uploads or allows to download them needs to use
-[Workhorse direct_upload](uploads/index.md#direct-upload). It means that uploads needs to be
+[Workhorse direct upload](uploads/index.md#direct-upload). It means that uploads needs to be
saved directly to Object Storage by Workhorse, and all downloads needs to be served
by Workhorse.
@@ -561,5 +561,5 @@ can time out, which is especially problematic for slow clients. If clients take
to upload/download the processing slot might be killed due to request processing
timeout (usually between 30s-60s).
-For the above reasons it is required that [Workhorse direct_upload](uploads/index.md#direct-upload) is implemented
+For the above reasons it is required that [Workhorse direct upload](uploads/index.md#direct-upload) is implemented
for all file uploads and downloads.
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index 5764c876e4d..8f035d4aa13 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -184,13 +184,21 @@ git checkout origin/master db/structure.sql
VERSION=<migration ID> bundle exec rails db:migrate:main
```
-### Adding new tables to GitLab Schema
-
-GitLab connects to two different Postgres databases: `main` and `ci`. New tables should be defined in [`lib/gitlab/database/gitlab_schemas.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/database/gitlab_schemas.yml) with the databases they need to be added to.
-
- ```yaml
- <TABLE_NAME>: :gitlab_main
- ```
+### Adding new tables to the database dictionary
+
+GitLab connects to two different Postgres databases: `main` and `ci`. New tables should be defined in [`db/docs/`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/db/docs):
+
+```yaml
+table_name: table name exmaple
+description: Description example
+introduced_by_url: Merge request link
+milestone: Milestone example
+feature_categories:
+- Feature category example
+classes:
+- Class example
+gitlab_schema: gitlab_main
+```
## Avoiding downtime
@@ -305,7 +313,7 @@ of migration helpers.
In this example, we use version 2.0 of the migration class:
```ruby
-class TestMigration < Gitlab::Database::Migration[2.0]
+class TestMigration < Gitlab::Database::Migration[2.1]
def change
end
end
@@ -338,7 +346,7 @@ is concurrently accessed and modified by other processes, acquiring the lock may
a while. The lock request is waiting in a queue and it may also block other queries
on the `users` table once it has been enqueued.
-More information about PostgresSQL locks: [Explicit Locking](https://www.postgresql.org/docs/current/explicit-locking.html)
+More information about PostgreSQL locks: [Explicit Locking](https://www.postgresql.org/docs/current/explicit-locking.html)
For stability reasons, GitLab.com has a specific [`statement_timeout`](../user/gitlab_com/index.md#postgresql)
set. When the migration is invoked, any database query has
@@ -376,7 +384,7 @@ Occasionally a migration may need to acquire multiple locks on different objects
To prevent catalog bloat, ask for all those locks explicitly before performing any DDL.
A better strategy is to split the migration, so that we only need to acquire one lock at the time.
-**Removing a column:**
+#### Removing a column
```ruby
enable_lock_retries!
@@ -386,7 +394,7 @@ def change
end
```
-**Multiple changes on the same table:**
+#### Multiple changes on the same table
With the lock-retry methodology enabled, all operations wrap into a single transaction. When you have the lock,
you should do as much as possible inside the transaction rather than trying to get another lock later.
@@ -406,7 +414,7 @@ def down
end
```
-**Removing a foreign key:**
+#### Removing a foreign key
```ruby
enable_lock_retries!
@@ -420,7 +428,7 @@ def down
end
```
-**Changing default value for a column:**
+#### Changing default value for a column
```ruby
enable_lock_retries!
@@ -434,7 +442,7 @@ def down
end
```
-**Creating a new table with a foreign key:**
+#### Creating a new table with a foreign key
We can wrap the `create_table` method with `with_lock_retries`:
@@ -453,7 +461,7 @@ def down
end
```
-**Creating a new table when we have two foreign keys:**
+#### Creating a new table when we have two foreign keys
Only one foreign key should be created per transaction. This is because [the addition of a foreign key constraint requires a `SHARE ROW EXCLUSIVE` lock on the referenced table](https://www.postgresql.org/docs/12/sql-createtable.html#:~:text=The%20addition%20of%20a%20foreign%20key%20constraint%20requires%20a%20SHARE%20ROW%20EXCLUSIVE%20lock%20on%20the%20referenced%20table), and locking multiple tables in the same transaction should be avoided.
@@ -600,7 +608,7 @@ by calling the method `disable_ddl_transaction!` in the body of your migration
class like so:
```ruby
-class MyMigration < Gitlab::Database::Migration[2.0]
+class MyMigration < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
INDEX_NAME = 'index_name'
@@ -611,10 +619,10 @@ class MyMigration < Gitlab::Database::Migration[2.0]
end
```
-Verify the index is not being used anymore with this Thanos query:
+You can verify that the index is not being used with [Thanos](https://thanos-query.ops.gitlab.net/graph?g0.expr=sum%20by%20(type)(rate(pg_stat_user_indexes_idx_scan%7Benv%3D%22gprd%22%2C%20indexrelname%3D%22INSERT%20INDEX%20NAME%20HERE%22%7D%5B30d%5D))&g0.tab=1&g0.stacked=0&g0.range_input=1h&g0.max_source_resolution=0s&g0.deduplicate=1&g0.partial_response=0&g0.store_matches=%5B%5D):
```sql
-sum by (type)(rate(pg_stat_user_indexes_idx_scan{env="gprd", indexrelname="index_groups_on_parent_id_id"}[5m]))
+sum by (type)(rate(pg_stat_user_indexes_idx_scan{env="gprd", indexrelname="INSERT INDEX NAME HERE"}[30d]))
```
Note that it is not necessary to check if the index exists prior to
@@ -657,7 +665,7 @@ by calling the method `disable_ddl_transaction!` in the body of your migration
class like so:
```ruby
-class MyMigration < Gitlab::Database::Migration[2.0]
+class MyMigration < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
INDEX_NAME = 'index_name'
@@ -700,7 +708,7 @@ The easiest way to test for existence of an index by name is to use the
be used with a name option. For example:
```ruby
-class MyMigration < Gitlab::Database::Migration[2.0]
+class MyMigration < Gitlab::Database::Migration[2.1]
INDEX_NAME = 'index_name'
def up
@@ -735,7 +743,7 @@ Here's an example where we add a new column with a foreign key
constraint. Note it includes `index: true` to create an index for it.
```ruby
-class Migration < Gitlab::Database::Migration[2.0]
+class Migration < Gitlab::Database::Migration[2.1]
def change
add_reference :model, :other_model, index: true, foreign_key: { on_delete: :cascade }
@@ -766,12 +774,7 @@ With PostgreSQL 11 being the minimum version in GitLab 13.0 and later, adding co
the standard `add_column` helper should be used in all cases.
Before PostgreSQL 11, adding a column with a default was problematic as it would
-have caused a full table rewrite. The corresponding helper `add_column_with_default`
-has been deprecated and is scheduled to be removed in a later release.
-
-If a backport adding a column with a default value is needed for %12.9 or earlier versions,
-it should use `add_column_with_default` helper. If a [large table](https://gitlab.com/gitlab-org/gitlab/-/blob/master/rubocop/rubocop-migrations.yml#L3)
-is involved, backporting to %12.9 is contraindicated.
+have caused a full table rewrite.
## Removing the column default for non-nullable columns
@@ -796,7 +799,7 @@ expensive and disruptive operation for larger tables, but in reality it's not.
Take the following migration as an example:
```ruby
-class DefaultRequestAccessGroups < Gitlab::Database::Migration[2.0]
+class DefaultRequestAccessGroups < Gitlab::Database::Migration[2.1]
def change
change_column_default(:namespaces, :request_access_enabled, from: false, to: true)
end
@@ -818,7 +821,7 @@ in the `namespaces` table. Only when creating a new column with a default, all t
NOTE:
A faster [ALTER TABLE ADD COLUMN with a non-null default](https://www.depesz.com/2018/04/04/waiting-for-postgresql-11-fast-alter-table-add-column-with-a-non-null-default/)
-was introduced on PostgresSQL 11.0, removing the need of rewriting the table when a new column with a default value is added.
+was introduced on PostgreSQL 11.0, removing the need of rewriting the table when a new column with a default value is added.
For the reasons mentioned above, it's safe to use `change_column_default` in a single-transaction migration
without requiring `disable_ddl_transaction!`.
@@ -852,8 +855,7 @@ update_column_in_batches(:projects, :foo, update_value) do |table, query|
end
```
-Like `add_column_with_default`, there is a RuboCop cop to detect usage of this
-on large tables. In the case of `update_column_in_batches`, it may be acceptable
+In the case of `update_column_in_batches`, it may be acceptable
to run on a large table, as long as it is only updating a small subset of the
rows in the table, but do not ignore that without validating on the GitLab.com
staging environment - or asking someone else to do so for you - beforehand.
@@ -991,7 +993,7 @@ Re-add a sequence:
A Rails migration example:
```ruby
-class DropSequenceTest < Gitlab::Database::Migration[2.0]
+class DropSequenceTest < Gitlab::Database::Migration[2.1]
def up
drop_sequence(:ci_pipelines_config, :pipeline_id, :ci_pipelines_config_pipeline_id_seq)
end
@@ -1022,7 +1024,7 @@ Under the hood, it works like this:
- Add the primary key using the index defined beforehand.
```ruby
-class SwapPrimaryKey < Gitlab::Database::Migration[2.0]
+class SwapPrimaryKey < Gitlab::Database::Migration[2.1]
TABLE_NAME = :table_name
PRIMARY_KEY = :table_name_pkey
OLD_INDEX_NAME = :old_index_name
@@ -1113,18 +1115,18 @@ The Rails 5 natively supports `JSONB` (binary JSON) column type.
Example migration adding this column:
```ruby
-class AddOptionsToBuildMetadata < Gitlab::Database::Migration[2.0]
+class AddOptionsToBuildMetadata < Gitlab::Database::Migration[2.1]
def change
add_column :ci_builds_metadata, :config_options, :jsonb
end
end
```
-You have to use a serializer to provide a translation layer:
+By default hash keys will be strings. Optionally you can add a custom data type to provide different access to keys.
```ruby
class BuildMetadata
- serialize :config_options, Serializers::Json # rubocop:disable Cop/ActiveRecordSerialize
+ attribute :config_options, :ind_jsonb # for indifferent accesss or :sym_jsonb if you need symbols only as keys.
end
```
@@ -1145,7 +1147,7 @@ Do not store `attr_encrypted` attributes as `:text` in the database; use
efficient:
```ruby
-class AddSecretToSomething < Gitlab::Database::Migration[2.0]
+class AddSecretToSomething < Gitlab::Database::Migration[2.1]
def change
add_column :something, :encrypted_secret, :binary
add_column :something, :encrypted_secret_iv, :binary
@@ -1203,7 +1205,7 @@ If you need more complex logic, you can define and use models local to a
migration. For example:
```ruby
-class MyMigration < Gitlab::Database::Migration[2.0]
+class MyMigration < Gitlab::Database::Migration[2.1]
class Project < MigrationRecord
self.table_name = 'projects'
end
@@ -1227,7 +1229,7 @@ Be aware of the limitations [when using models in migrations](#using-models-in-m
In most circumstances, prefer migrating data in **batches** when modifying data in the database.
-We introduced a new helper [each_batch_range](https://gitlab.com/gitlab-org/gitlab/-/blob/cd3e0a5cddcb464cb9b8c6e3275839cf57dfa6e2/lib/gitlab/database/dynamic_model_helpers.rb#L28-32) which facilitates the process of iterating over a collection in a performant way. The default size of the batch is defined in the `BATCH_SIZE` constant.
+We introduced a new helper [`each_batch_range`](https://gitlab.com/gitlab-org/gitlab/-/blob/cd3e0a5cddcb464cb9b8c6e3275839cf57dfa6e2/lib/gitlab/database/dynamic_model_helpers.rb#L28-32) which facilitates the process of iterating over a collection in a performant way. The default size of the batch is defined in the `BATCH_SIZE` constant.
See the following example to get an idea.
@@ -1302,7 +1304,7 @@ in a previous migration.
It is important not to leave out the `User.reset_column_information` command, to ensure that the old schema is dropped from the cache and ActiveRecord loads the updated schema information.
```ruby
-class AddAndSeedMyColumn < Gitlab::Database::Migration[2.0]
+class AddAndSeedMyColumn < Gitlab::Database::Migration[2.1]
class User < MigrationRecord
self.table_name = 'users'
end
diff --git a/doc/development/multi_version_compatibility.md b/doc/development/multi_version_compatibility.md
index 8e2103b2f3e..36b392a0037 100644
--- a/doc/development/multi_version_compatibility.md
+++ b/doc/development/multi_version_compatibility.md
@@ -112,7 +112,7 @@ During an update, there will be [two different versions of GitLab running in dif
Yes! We have specific instructions for [zero-downtime updates](../update/index.md#upgrading-without-downtime) because it allows us to ignore some permutations of compatibility. This is why we don't worry about Rails code making DB calls to an old PostgreSQL database schema.
-## I've identified a potential backwards compatibility problem, what can I do about it?
+## You've identified a potential backwards compatibility problem, what can you do about it?
### Coordinate
diff --git a/doc/development/namespaces_storage_statistics.md b/doc/development/namespaces_storage_statistics.md
deleted file mode 100644
index 75e79d1f693..00000000000
--- a/doc/development/namespaces_storage_statistics.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/namespaces_storage_statistics.md'
-remove_date: '2022-11-05'
----
-
-This document was moved to [another location](database/namespaces_storage_statistics.md).
-
-<!-- This redirect file can be deleted after <2022-11-05>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/new_fe_guide/development/accessibility.md b/doc/development/new_fe_guide/development/accessibility.md
deleted file mode 100644
index 9575acd20c7..00000000000
--- a/doc/development/new_fe_guide/development/accessibility.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../../fe_guide/accessibility.md'
-remove_date: '2022-11-15'
----
-
-This document was moved to [another location](../../fe_guide/accessibility.md).
-
-<!-- This redirect file can be deleted after <2022-11-15>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/new_fe_guide/development/components.md b/doc/development/new_fe_guide/development/components.md
deleted file mode 100644
index 9ad742272d1..00000000000
--- a/doc/development/new_fe_guide/development/components.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../../fe_guide/index.md'
-remove_date: '2022-11-15'
----
-
-This document was moved to [another location](../../fe_guide/index.md).
-
-<!-- This redirect file can be deleted after <2022-11-15>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/new_fe_guide/development/index.md b/doc/development/new_fe_guide/development/index.md
deleted file mode 100644
index 9ad742272d1..00000000000
--- a/doc/development/new_fe_guide/development/index.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../../fe_guide/index.md'
-remove_date: '2022-11-15'
----
-
-This document was moved to [another location](../../fe_guide/index.md).
-
-<!-- This redirect file can be deleted after <2022-11-15>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/new_fe_guide/development/performance.md b/doc/development/new_fe_guide/development/performance.md
deleted file mode 100644
index c72f3ded896..00000000000
--- a/doc/development/new_fe_guide/development/performance.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../../fe_guide/performance.md'
-remove_date: '2022-11-15'
----
-
-This document was moved to [another location](../../fe_guide/performance.md).
-
-<!-- This redirect file can be deleted after <2022-11-15>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/new_fe_guide/index.md b/doc/development/new_fe_guide/index.md
deleted file mode 100644
index 83c1db696b4..00000000000
--- a/doc/development/new_fe_guide/index.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../fe_guide/index.md'
-remove_date: '2022-11-15'
----
-
-This document was moved to [another location](../fe_guide/index.md).
-
-<!-- This redirect file can be deleted after <2022-11-15>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/new_fe_guide/modules/dirty_submit.md b/doc/development/new_fe_guide/modules/dirty_submit.md
deleted file mode 100644
index 9ad742272d1..00000000000
--- a/doc/development/new_fe_guide/modules/dirty_submit.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../../fe_guide/index.md'
-remove_date: '2022-11-15'
----
-
-This document was moved to [another location](../../fe_guide/index.md).
-
-<!-- This redirect file can be deleted after <2022-11-15>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/new_fe_guide/modules/index.md b/doc/development/new_fe_guide/modules/index.md
deleted file mode 100644
index 9ad742272d1..00000000000
--- a/doc/development/new_fe_guide/modules/index.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../../fe_guide/index.md'
-remove_date: '2022-11-15'
----
-
-This document was moved to [another location](../../fe_guide/index.md).
-
-<!-- This redirect file can be deleted after <2022-11-15>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/new_fe_guide/modules/widget_extensions.md b/doc/development/new_fe_guide/modules/widget_extensions.md
deleted file mode 100644
index 3741ee8c38a..00000000000
--- a/doc/development/new_fe_guide/modules/widget_extensions.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../../fe_guide/merge_request_widget_extensions.md'
-remove_date: '2022-11-15'
----
-
-This document was moved to [another location](../../fe_guide/merge_request_widget_extensions.md).
-
-<!-- This redirect file can be deleted after <2022-11-15>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/new_fe_guide/tips.md b/doc/development/new_fe_guide/tips.md
deleted file mode 100644
index 83c1db696b4..00000000000
--- a/doc/development/new_fe_guide/tips.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../fe_guide/index.md'
-remove_date: '2022-11-15'
----
-
-This document was moved to [another location](../fe_guide/index.md).
-
-<!-- This redirect file can be deleted after <2022-11-15>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/ordering_table_columns.md b/doc/development/ordering_table_columns.md
deleted file mode 100644
index b665cb0d4c7..00000000000
--- a/doc/development/ordering_table_columns.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/ordering_table_columns.md'
-remove_date: '2022-11-04'
----
-
-This document was moved to [another location](database/ordering_table_columns.md).
-
-<!-- This redirect file can be deleted after <2022-11-04>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/packages/dependency_proxy.md b/doc/development/packages/dependency_proxy.md
index d5cc219cba0..cc8b202e556 100644
--- a/doc/development/packages/dependency_proxy.md
+++ b/doc/development/packages/dependency_proxy.md
@@ -117,7 +117,7 @@ Manifests are more complicated, partially due to [rate limiting on DockerHub](ht
A manifest is essentially a recipe for creating an image. It has a list of blobs to create a certain image. So
`alpine:latest` has a manifest associated with it that specifies the blobs needed to create the `alpine:latest`
image. The interesting part is that `alpine:latest` can change over time, so we can't just cache the manifest and
-assume it is OK to use forever. Instead, we must check the digest of the manifest, which is an Etag. This gets
+assume it is OK to use forever. Instead, we must check the digest of the manifest, which is an ETag. This gets
interesting because the requests for manifests often don't include the digest. So how do we know if the manifest
we have cached is still the most up-to-date `alpine:latest`? DockerHub allows free HEAD requests that don't count
toward their rate limit. The HEAD request returns the manifest digest so we can tell whether or not the one we
@@ -143,7 +143,7 @@ Management of file uploads and caching happens in [Workhorse](../workhorse/index
[`POST` routes](https://gitlab.com/gitlab-org/gitlab/-/blob/3f76455ac9cf90a927767e55c837d6b07af818df/config/routes/group.rb#L170-173)
that we have for the Dependency Proxy.
-The [send_dependency](https://gitlab.com/gitlab-org/gitlab/-/blob/7359d23f4e078479969c872924150219c6f1665f/app/helpers/workhorse_helper.rb#L46-53)
+The [`send_dependency`](https://gitlab.com/gitlab-org/gitlab/-/blob/7359d23f4e078479969c872924150219c6f1665f/app/helpers/workhorse_helper.rb#L46-53)
method makes a request to Workhorse including the previously fetched JWT from the external registry. Workhorse then
can use that token to request the manifest or blob the user originally requested. The Workhorse code lives in
[`workhorse/internal/dependencyproxy/dependencyproxy.go`](https://gitlab.com/gitlab-org/gitlab/-/blob/b8f44a8f3c26efe9932c2ada2df75ef7acb8417b/workhorse/internal/dependencyproxy/dependencyproxy.go#L4).
diff --git a/doc/development/pages/index.md b/doc/development/pages/index.md
index 6ee8a9ac433..5e56264330a 100644
--- a/doc/development/pages/index.md
+++ b/doc/development/pages/index.md
@@ -178,7 +178,7 @@ The `redirect-uri` must not contain any GitLab Pages site domain.
auth-redirect-uri=http://pages.gdk.test:3010/auth # the authentication callback url for GitLab Pages
```
-1. If running Pages inside the GDK, you can use GDK's `protected_config_files` section under `gdk` in
+1. If running Pages inside the GDK, you can use GDK `protected_config_files` section under `gdk` in
your `gdk.yml` to avoid getting `gitlab-pages.conf` configuration rewritten:
```yaml
diff --git a/doc/development/performance.md b/doc/development/performance.md
index 3881fad0528..127cade9fee 100644
--- a/doc/development/performance.md
+++ b/doc/development/performance.md
@@ -37,7 +37,7 @@ consistent performance of GitLab. Refer to the [Index](#performance-documentatio
- [Service measurement](../development/service_measurement.md)
- Self-managed administration and customer-focused:
- [File system performance benchmarking](../administration/operations/filesystem_benchmarking.md)
- - [Sidekiq performance troubleshooting](../administration/troubleshooting/sidekiq.md)
+ - [Sidekiq performance troubleshooting](../administration/sidekiq/sidekiq_troubleshooting.md)
## Workflow
@@ -74,7 +74,7 @@ GitLab provides built-in tools to help improve performance and availability:
- [Profiling](profiling.md).
- [Distributed Tracing](distributed_tracing.md)
- [GitLab Performance Monitoring](../administration/monitoring/performance/index.md).
-- [QueryRecoder](query_recorder.md) for preventing `N+1` regressions.
+- [QueryRecoder](database/query_recorder.md) for preventing `N+1` regressions.
- [Chaos endpoints](chaos_endpoints.md) for testing failure scenarios. Intended mainly for testing availability.
- [Service measurement](service_measurement.md) for measuring and logging service execution.
@@ -312,7 +312,7 @@ To export the flame graph to an SVG file, use [Brendan Gregg's FlameGraph tool](
stackprof --stackcollapse /tmp/group_member_policy_spec.rb.dump | flamegraph.pl > flamegraph.svg
```
-It's also possible to view flame graphs through [speedscope](https://github.com/jlfwong/speedscope).
+It's also possible to view flame graphs through [Speedscope](https://github.com/jlfwong/speedscope).
You can do this when using the [performance bar](profiling.md#speedscope-flamegraphs)
and when [profiling code blocks](https://github.com/jlfwong/speedscope/wiki/Importing-from-stackprof-(ruby)).
This option isn't supported by `bin/rspec-stackprof`.
diff --git a/doc/development/pipelines/index.md b/doc/development/pipelines/index.md
index 01bb813e794..a7b8c99bd13 100644
--- a/doc/development/pipelines/index.md
+++ b/doc/development/pipelines/index.md
@@ -68,8 +68,8 @@ Later on in [the `rspec fail-fast` job](#fail-fast-job-in-merge-request-pipeline
In addition, there are a few circumstances where we would always run the full RSpec tests:
-- when the `pipeline:run-all-rspec` label is set on the merge request
-- when the `pipeline:run-full-rspec` label is set on the merge request, this label is assigned by triage automation when the merge request is approved by any reviewer
+- when the `pipeline:run-all-rspec` label is set on the merge request. This label will trigger all RSpec tests including those run in the `as-if-foss` jobs.
+- when the `pipeline:mr-approved` label is set on the merge request, and if the code changes satisfy the `backend-patterns` rule. Note that this label is assigned by triage automation when the merge request is approved by any reviewer. It is not recommended to apply this label manually.
- when the merge request is created by an automation (for example, Gitaly update or MR targeting a stable branch)
- when the merge request is created in a security mirror
- when any CI configuration file is changed (for example, `.gitlab-ci.yml` or `.gitlab/ci/**/*`)
@@ -88,10 +88,12 @@ In addition, there are a few circumstances where we would always run the full Je
- when the `pipeline:run-all-jest` label is set on the merge request
- when the merge request is created by an automation (for example, Gitaly update or MR targeting a stable branch)
- when the merge request is created in a security mirror
-- when any CI configuration file is changed (for example, `.gitlab-ci.yml` or `.gitlab/ci/**/*`)
-- when any frontend "core" file is changed (for example, `package.json`, `yarn.lock`, `babel.config.js`, `jest.config.*.js`, `config/helpers/**/*.js`)
+- when relevant CI configuration file is changed (`.gitlab/ci/rules.gitlab-ci.yml`, `.gitlab/ci/frontend.gitlab-ci.yml`)
+- when any frontend dependency file is changed (for example, `package.json`, `yarn.lock`, `config/webpack.config.js`, `config/helpers/**/*.js`)
- when any vendored JavaScript file is changed (for example, `vendor/assets/javascripts/**/*`)
-- when any backend file is changed ([see the patterns list for details](https://gitlab.com/gitlab-org/gitlab/-/blob/3616946936c1adbd9e754c1bd06f86ba670796d8/.gitlab/ci/rules.gitlab-ci.yml#L205-216))
+
+The `rules` definitions for full Jest tests are defined at `.frontend:rules:jest` in
+[`rules.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/42321b18b946c64d2f6f788c38844499a5ae9141/.gitlab/ci/rules.gitlab-ci.yml#L938-955).
### Fork pipelines
@@ -146,9 +148,37 @@ merge request. This prevents `rspec fail-fast` duration from exceeding the avera
This number can be overridden by setting a CI/CD variable named `RSPEC_FAIL_FAST_TEST_FILE_COUNT_THRESHOLD`.
+## Re-run previously failed tests in merge request pipelines
+
+In order to reduce the feedback time after resolving failed tests for a merge request, the `rspec rspec-pg12-rerun-previous-failed-tests`
+and `rspec rspec-ee-pg12-rerun-previous-failed-tests` jobs run the failed tests from the previous MR pipeline.
+
+This was introduced on August 25th 2021, with <https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69053>.
+
+### How it works?
+
+1. The `detect-previous-failed-tests` job (`prepare` stage) detects the test files associated with failed RSpec
+ jobs from the previous MR pipeline.
+1. The `rspec rspec-pg12-rerun-previous-failed-tests` and `rspec rspec-ee-pg12-rerun-previous-failed-tests` jobs
+ will run the test files gathered by the `detect-previous-failed-tests` job.
+
+```mermaid
+graph LR
+ subgraph "prepare stage";
+ A["detect-previous-failed-tests"]
+ end
+
+ subgraph "test stage";
+ B["rspec rspec-pg12-rerun-previous-failed-tests"];
+ C["rspec rspec-ee-pg12-rerun-previous-failed-tests"];
+ end
+
+ A --"artifact: list of test files"--> B & C
+```
+
## Faster feedback for merge requests that fix a broken `master`
-When you need to [fix a broken `master`](https://about.gitlab.com/handbook/engineering/workflow/#resolution-of-broken-master), you can add the `pipeline:expedite-master-fixing` label to expedite the pipelines that run on the merge request.
+When you need to [fix a broken `master`](https://about.gitlab.com/handbook/engineering/workflow/#resolution-of-broken-master), you can add the `pipeline:expedite` label to expedite the pipelines that run on the merge request.
When this label is assigned, the following steps of the CI/CD pipeline are skipped:
diff --git a/doc/development/pipelines/internals.md b/doc/development/pipelines/internals.md
index 2861e2f266b..71492ed9f5d 100644
--- a/doc/development/pipelines/internals.md
+++ b/doc/development/pipelines/internals.md
@@ -107,7 +107,7 @@ automatically updates the Gitaly version used in the main project),
[the Dependency proxy isn't accessible](https://gitlab.com/gitlab-org/gitlab/-/issues/332411#note_1130388163)
and the job fails at the `Preparing the "docker+machine" executor` step.
To work around that, we have a special workflow rule, that overrides the
-`${GITLAB_DEPENDENCY_PROXY_ADDRESS}` variable so that Depdendency proxy isn't used in that case:
+`${GITLAB_DEPENDENCY_PROXY_ADDRESS}` variable so that Dependency proxy isn't used in that case:
```yaml
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $GITLAB_USER_LOGIN =~ /project_\d+_bot\d*/'
@@ -165,9 +165,9 @@ and included in `rules` definitions via [YAML anchors](../../ci/yaml/yaml_optimi
| `if:` conditions | Description | Notes |
|------------------|-------------|-------|
-| `if-not-canonical-namespace` | Matches if the project isn't in the canonical (`gitlab-org/`) or security (`gitlab-org/security`) namespace. | Use to create a job for forks (by using `when: on_success|manual`), or **not** create a job for forks (by using `when: never`). |
-| `if-not-ee` | Matches if the project isn't EE (that is, project name isn't `gitlab` or `gitlab-ee`). | Use to create a job only in the FOSS project (by using `when: on_success|manual`), or **not** create a job if the project is EE (by using `when: never`). |
-| `if-not-foss` | Matches if the project isn't FOSS (that is, project name isn't `gitlab-foss`, `gitlab-ce`, or `gitlabhq`). | Use to create a job only in the EE project (by using `when: on_success|manual`), or **not** create a job if the project is FOSS (by using `when: never`). |
+| `if-not-canonical-namespace` | Matches if the project isn't in the canonical (`gitlab-org/`) or security (`gitlab-org/security`) namespace. | Use to create a job for forks (by using `when: on_success` or `when: manual`), or **not** create a job for forks (by using `when: never`). |
+| `if-not-ee` | Matches if the project isn't EE (that is, project name isn't `gitlab` or `gitlab-ee`). | Use to create a job only in the FOSS project (by using `when: on_success` or `when: manual`), or **not** create a job if the project is EE (by using `when: never`). |
+| `if-not-foss` | Matches if the project isn't FOSS (that is, project name isn't `gitlab-foss`, `gitlab-ce`, or `gitlabhq`). | Use to create a job only in the EE project (by using `when: on_success` or `when: manual`), or **not** create a job if the project is FOSS (by using `when: never`). |
| `if-default-refs` | Matches if the pipeline is for `master`, `main`, `/^[\d-]+-stable(-ee)?$/` (stable branches), `/^\d+-\d+-auto-deploy-\d+$/` (auto-deploy branches), `/^security\//` (security branches), merge requests, and tags. | Note that jobs aren't created for branches with this default configuration. |
| `if-master-refs` | Matches if the current branch is `master` or `main`. | |
| `if-master-push` | Matches if the current branch is `master` or `main` and pipeline source is `push`. | |
@@ -203,7 +203,7 @@ and included in `rules` definitions via [YAML anchors](../../ci/yaml/yaml_optimi
| `ci-qa-patterns` | Only create job for CI configuration-related changes related to the `qa` stage. |
| `yaml-lint-patterns` | Only create job for YAML-related changes. |
| `docs-patterns` | Only create job for docs-related changes. |
-| `frontend-dependency-patterns` | Only create job when frontend dependencies are updated (that is, `package.json`, and `yarn.lock`). changes. |
+| `frontend-dependency-patterns` | Only create job when frontend dependencies are updated (for example, `package.json`, and `yarn.lock`) changes. |
| `frontend-patterns-for-as-if-foss` | Only create job for frontend-related changes that have impact on FOSS. |
| `backend-patterns` | Only create job for backend-related changes. |
| `db-patterns` | Only create job for DB-related changes. |
diff --git a/doc/development/pipelines/performance.md b/doc/development/pipelines/performance.md
index 1c6f9d78879..5f2df91edf3 100644
--- a/doc/development/pipelines/performance.md
+++ b/doc/development/pipelines/performance.md
@@ -54,7 +54,6 @@ This works well for the following reasons:
- `update-qa-cache`, defined in [`.gitlab/ci/qa.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/qa.gitlab-ci.yml).
- `update-assets-compile-production-cache`, defined in [`.gitlab/ci/frontend.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml).
- `update-assets-compile-test-cache`, defined in [`.gitlab/ci/frontend.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml).
- - `update-yarn-cache`, defined in [`.gitlab/ci/frontend.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml).
- `update-storybook-yarn-cache`, defined in [`.gitlab/ci/frontend.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml).
1. These jobs can also be forced to run in merge requests with the `pipeline:update-cache` label (this can be useful to warm the caches in a MR that updates the cache keys).
diff --git a/doc/development/polymorphic_associations.md b/doc/development/polymorphic_associations.md
deleted file mode 100644
index 6b9158b8408..00000000000
--- a/doc/development/polymorphic_associations.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/polymorphic_associations.md'
-remove_date: '2022-11-04'
----
-
-This document was moved to [another location](database/polymorphic_associations.md).
-
-<!-- This redirect file can be deleted after <2022-11-04>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/product_qualified_lead_guide/index.md b/doc/development/product_qualified_lead_guide/index.md
index 89a05d59fc9..1ad4f622829 100644
--- a/doc/development/product_qualified_lead_guide/index.md
+++ b/doc/development/product_qualified_lead_guide/index.md
@@ -17,7 +17,7 @@ A hand-raise PQL is a user who requests to speak to sales from within the produc
1. Set up CustomersDot using the [normal install instructions](https://gitlab.com/gitlab-org/customers-gitlab-com/-/blob/staging/doc/setup/installation_steps.md).
1. Set the `CUSTOMER_PORTAL_URL` environment variable to your local (or ngrok) URL of your CustomersDot instance.
-1. Place `export CUSTOMER_PORTAL_URL='https://XXX.ngrok.io/'` in your shell rc script (`~/.zshrc` or `~/.bash_profile` or `~/.bashrc`) and restart GDK.
+1. Place `export CUSTOMER_PORTAL_URL='https://XXX.ngrok.io/'` in your shell `rc` script (`~/.zshrc` or `~/.bash_profile` or `~/.bashrc`) and restart GDK.
1. Enter the credentials on CustomersDot development to Platypus in your `/config/secrets.yml` and restart. Credentials for the Platypus Staging are in the 1Password Growth vault. The URL for staging is `https://staging.ci.nexus.gitlabenvironment.cloud`.
```yaml
@@ -95,16 +95,16 @@ The `ctaTracking` parameters follow [the `data-track` attributes](../snowplow/im
When embedding a new hand raise form, use a unique `glmContent` or `glm_content` field that is different to any existing values.
-We currently use the following `glm content` values:
+We currently use the following `glm_content` values:
-| glm_content value | Notes |
-| ------ | ------ |
-| discover-group-security | This value is used in the group security feature discovery page. |
-| discover-group-security-pqltest | This value is used in the group security feature discovery page [experiment with 3 CTAs](https://gitlab.com/gitlab-org/gitlab/-/issues/349799). |
-| discover-project-security | This value is used in the project security feature discovery page. |
-| discover-project-security-pqltest | This value is used in the project security feature discovery page [experiment with 3 CTAs](https://gitlab.com/gitlab-org/gitlab/-/issues/349799). |
-| group-billing | This value is used in the group billing page. |
-| trial-status-show-group | This value is used in the top left nav when a namespace has an active trial. |
+| `glm_content` value | Notes |
+|-------------------------------------|-------|
+| `discover-group-security` | This value is used in the group security feature discovery page. |
+| `discover-group-security-pqltest` | This value is used in the group security feature discovery page [experiment with 3 CTAs](https://gitlab.com/gitlab-org/gitlab/-/issues/349799). |
+| `discover-project-security` | This value is used in the project security feature discovery page. |
+| `discover-project-security-pqltest` | This value is used in the project security feature discovery page [experiment with 3 CTAs](https://gitlab.com/gitlab-org/gitlab/-/issues/349799). |
+| `group-billing` | This value is used in the group billing page. |
+| `trial-status-show-group` | This value is used in the top left nav when a namespace has an active trial. |
### Test the component
@@ -144,7 +144,7 @@ sequenceDiagram
```
-#### Trial lead flow on CustomersDot (sync_to_gl)
+#### Trial lead flow on CustomersDot (`sync_to_gl`)
```mermaid
sequenceDiagram
diff --git a/doc/development/profiling.md b/doc/development/profiling.md
index 3eb2c7c9144..efee6ff3cd5 100644
--- a/doc/development/profiling.md
+++ b/doc/development/profiling.md
@@ -21,7 +21,7 @@ The first argument to the profiler is either a full URL
leading slash.
By default the report dump will be stored in a temporary file, which can be
-interacted with using the [stackprof API](#reading-a-gitlabprofiler-report).
+interacted with using the [Stackprof API](#reading-a-gitlabprofiler-report).
When using the script, command-line documentation is available by passing no
arguments.
@@ -62,7 +62,7 @@ Pass in a `profiler_options` hash to configure the output file (`out`) of the sa
Gitlab::Profiler.profile('/gitlab-org/gitlab-test', user: User.first, profiler_options: { out: 'tmp/profile.dump' })
```
-## Reading a GitLab::Profiler report
+## Reading a `GitLab::Profiler` report
You can get a summary of where time was spent by running Stackprof against the sampling data. For example:
@@ -131,25 +131,39 @@ Find more information about different sampling modes in the [Stackprof docs](htt
This is enabled for all users that can access the performance bar.
+<!-- vale gitlab.SubstitutionWarning = NO -->
+<!-- Here, "bullet" is a false positive -->
+
## Bullet
-Bullet is a Gem that can be used to track down N+1 query problems. Bullet section is
-displayed on the [performance-bar](../administration/monitoring/performance/performance_bar.md).
+Bullet is a Gem that can be used to track down N+1 query problems. It logs
+query problems to the Rails log and the browser console. The **Bullet** section is
+displayed on the [performance bar](../administration/monitoring/performance/performance_bar.md).
![Bullet](img/bullet_v13_0.png)
-Because Bullet adds quite a bit of logging noise the logging is disabled by default.
-To enable the logging, set the environment variable `ENABLE_BULLET` to a non-empty value before
-starting GitLab. For example:
+Bullet is enabled only in development mode by default. However, logging is disabled,
+because Bullet logging is noisy. To configure Bullet and its logging:
-```shell
-ENABLE_BULLET=true bundle exec rails s
-```
+- To manually enable or disable Bullet on an environment, add these lines to
+ `config/gitlab.yml`, changing the `enabled` value as needed:
+
+ ```yaml
+ bullet:
+ enabled: false
+ ```
+
+- To enable Bullet logging, set the `ENABLE_BULLET` environment variable to a
+ non-empty value before starting GitLab:
+
+ ```shell
+ ENABLE_BULLET=true bundle exec rails s
+ ```
-Bullet logs query problems to both the Rails log as well as the browser
-console.
+As a follow-up to finding `N+1` queries with Bullet, consider writing a
+[QueryRecoder test](database/query_recorder.md) to prevent a regression.
-As a follow up to finding `N+1` queries with Bullet, consider writing a [QueryRecoder test](query_recorder.md) to prevent a regression.
+<!-- vale gitlab.SubstitutionWarning = YES -->
## System stats
diff --git a/doc/development/project_templates.md b/doc/development/project_templates.md
index 2f1ded23e38..269724c0a7f 100644
--- a/doc/development/project_templates.md
+++ b/doc/development/project_templates.md
@@ -4,7 +4,9 @@ group: Workspace
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments"
---
-# Contribute a built-in project template
+# Contribute to built-in project templates
+
+## Adding a new built-in project template
This page provides instructions about how to contribute a
[built-in project template](../user/project/working_with_projects.md#create-a-project-from-a-built-in-template).
@@ -20,7 +22,7 @@ You can contribute the following types of project templates:
- Enterprise: For users with GitLab Premium and above.
- Non-enterprise: For users with GitLab Free and above.
-## Prerequisites
+### Prerequisites
To add or update an existing template, you must have the following tools
installed:
@@ -28,22 +30,22 @@ installed:
- `wget`
- `tar`
-## Create a project template for review
+### Create a project template for review
1. In your selected namespace, create a public project.
1. Add the project content you want to use in the template. Do not include unnecessary assets or dependencies. For an example,
[see this project](https://gitlab.com/gitlab-org/project-templates/dotnetcore).
1. When the project is ready for review, [create an issue](https://gitlab.com/gitlab-org/gitlab/issues) with a link to your project.
- In your issue, mention the relevant [Backend Engineering Manager and Product Manager](https://about.gitlab.com/handbook/product/categories/#source-code-group)
+ In your issue, mention the Create:Source Code [Backend Engineering Manager and Product Manager](https://about.gitlab.com/handbook/product/categories/#source-code-group)
for the Templates feature.
-## Add the template SVG icon to GitLab SVGs
+### Add the template SVG icon to GitLab SVGs
If the project template has an SVG icon, you must add it to the
[GitLab SVGs project](https://gitlab.com/gitlab-org/gitlab-svgs/-/blob/main/README.md#adding-icons-or-illustrations)
before you can create a merge request with vendor details.
-## Create a merge request with vendor details
+### Create a merge request with vendor details
Before GitLab can implement the project template, you must [create a merge request](../user/project/merge_requests/creating_merge_requests.md) in [`gitlab-org/gitlab`](https://gitlab.com/gitlab-org/gitlab) that includes vendor details about the project.
@@ -111,7 +113,7 @@ Before GitLab can implement the project template, you must [create a merge reque
1. After you run the scripts, there is one new file in `vendor/project_templates/` and four changed files. Commit all changes and push your branch to update the merge request. For an example, see this [merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25318).
-## Test your built-in project with the GitLab Development Kit
+### Test your built-in project with the GitLab Development Kit
Complete the following steps to test the project template in your own GitLab Development Kit instance:
@@ -124,9 +126,24 @@ Complete the following steps to test the project template in your own GitLab Dev
## Contribute an improvement to an existing template
-To update an existing built-in project template:
+To update an existing built-in project template, changes are usually made to the existing template, found in the [project-templates](https://gitlab.com/gitlab-org/project-templates) group. A merge request is made directly against the template and the Create:Source Code [Backend Engineering Manager and Product Manager](https://about.gitlab.com/handbook/product/categories/#source-code-group) pinged for review.
+
+Sometimes it is necessary to completely replace the template files. In this case the process would be:
1. Create a merge request in the relevant project of the `project-templates` and `pages` group and mention `@gitlab-org/manage/import/backend` when you are ready for a review.
1. If your merge request is accepted, either:
- [Create an issue](https://gitlab.com/gitlab-org/gitlab/-/issues) to ask for the template to get updated.
- [Create a merge request with vendor details](#create-a-merge-request-with-vendor-details) to update the template.
+
+## For GitLab team members
+
+Please ensure the merge request has been reviewed by the Security Counterpart before merging.
+
+To review a merge request which changes a vendored project template, run the `check-template-changes` script:
+
+```shell
+scripts/check-template-changes vendor/project_templates/<template_name>.tar.gz
+```
+
+This script outputs a diff of the file changes against the default branch and also verifies that
+the template repository matches the source template project.
diff --git a/doc/development/projections.md b/doc/development/projections.md
index e45a16c267d..7c727fc0901 100644
--- a/doc/development/projections.md
+++ b/doc/development/projections.md
@@ -20,11 +20,11 @@ Projections are a way to define relations between files. Every file can have a
You can find a basic list of projection options in
[projectionist.txt](https://github.com/tpope/vim-projectionist/blob/master/doc/projectionist.txt)
-## Which plugins can I use
+## Which plugins can you use
- vim
- [vim-projectionist](https://github.com/tpope/vim-projectionist)
-- VSCode
+- VS Code
- [Alternate File](https://marketplace.visualstudio.com/items?itemName=will-wow.vscode-alternate-file)
- [projectionist](https://github.com/jarsen/projectionist)
- [`jumpto`](https://github.com/gmdayley/jumpto)
diff --git a/doc/development/prometheus_metrics.md b/doc/development/prometheus_metrics.md
index d3d809c5386..456f2eb50aa 100644
--- a/doc/development/prometheus_metrics.md
+++ b/doc/development/prometheus_metrics.md
@@ -36,7 +36,7 @@ After you add or change an existing common metric, you must [re-run the import s
Or, you can create a database migration:
```ruby
-class ImportCommonMetrics < Gitlab::Database::Migration[2.0]
+class ImportCommonMetrics < Gitlab::Database::Migration[2.1]
def up
::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute
end
diff --git a/doc/development/query_count_limits.md b/doc/development/query_count_limits.md
deleted file mode 100644
index f16c8cfc6cd..00000000000
--- a/doc/development/query_count_limits.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/query_count_limits.md'
-remove_date: '2022-11-06'
----
-
-This document was moved to [another location](database/query_count_limits.md).
-
-<!-- This redirect file can be deleted after <2022-11-06>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/query_performance.md b/doc/development/query_performance.md
deleted file mode 100644
index 618d007f766..00000000000
--- a/doc/development/query_performance.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/query_performance.md'
-remove_date: '2022-11-06'
----
-
-This document was moved to [another location](database/query_performance.md).
-
-<!-- This redirect file can be deleted after <2022-11-06>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/query_recorder.md b/doc/development/query_recorder.md
deleted file mode 100644
index cb05bc604af..00000000000
--- a/doc/development/query_recorder.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/query_recorder.md'
-remove_date: '2022-11-06'
----
-
-This document was moved to [another location](database/query_recorder.md).
-
-<!-- This redirect file can be deleted after <2022-11-06>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/reactive_caching.md b/doc/development/reactive_caching.md
index 3239748193f..9e3b1f58abe 100644
--- a/doc/development/reactive_caching.md
+++ b/doc/development/reactive_caching.md
@@ -208,7 +208,7 @@ There are some `class_attribute` options which can be tweaked.
self.reactive_cache_key = ->(integration) { [integration.class.model_name.singular, integration.project_id] }
```
- If your reactive_cache_key is exactly like the above, you can use the existing
+ If your `reactive_cache_key` is exactly like the above, you can use the existing
`Integrations::ReactivelyCached` concern instead.
#### `self.reactive_cache_lease_timeout`
diff --git a/doc/development/redis/new_redis_instance.md b/doc/development/redis/new_redis_instance.md
index d9d4bb6a9f8..0a030a0877f 100644
--- a/doc/development/redis/new_redis_instance.md
+++ b/doc/development/redis/new_redis_instance.md
@@ -181,7 +181,7 @@ and the [old (fallback-instance)](#fallback-instance).
If we fail to fetch data from the new instance, we will fallback and read from the old Redis instance.
We can monitor logs for `Gitlab::Redis::MultiStore::ReadFromPrimaryError`, and also the Prometheus counter `gitlab_redis_multi_store_read_fallback_total`.
-For pipelined commands (`pipelined` and `multi`), we execute the entire operation in both stores and then compare the results. If they differ, we emit a
+For `pipelined` commands (`pipelined` and `multi`), we execute the entire operation in both stores and then compare the results. If they differ, we emit a
`Gitlab::Redis::MultiStore:PipelinedDiffError` error, and track it in the `gitlab_redis_multi_store_pipelined_diff_error_total` Prometheus counter.
Once we stop seeing those errors, this means that we are no longer relying on the data stored on the old Redis store.
@@ -218,7 +218,7 @@ MultiStore implements read and write Redis commands separately.
- `flushdb`
- `rpush`
-##### Pipelined commands
+##### `pipelined` commands
**NOTE:** The Ruby block passed to these commands will be executed twice, once per each store.
Thus, excluding the Redis operations performed, the block should be idempotent.
@@ -238,16 +238,16 @@ a developer will need to add an implementation for missing Redis commands before
| error | message |
|---------------------------------------------------|---------------------------------------------------------------------------------------------|
| `Gitlab::Redis::MultiStore::ReadFromPrimaryError` | Value not found on the Redis primary store. Read from the Redis secondary store successful. |
-| `Gitlab::Redis::MultiStore::PipelinedDiffError` | Pipelined command executed on both stores successfully but results differ between them. |
+| `Gitlab::Redis::MultiStore::PipelinedDiffError` | `pipelined` command executed on both stores successfully but results differ between them. |
| `Gitlab::Redis::MultiStore::MethodMissingError` | Method missing. Falling back to execute method on the Redis secondary store. |
##### Metrics
-| metrics name | type | labels | description |
-|-------------------------------------------------------|--------------------|------------------------|--------------------------------------------------------|
-| `gitlab_redis_multi_store_read_fallback_total` | Prometheus Counter | command, instance_name | Client side Redis MultiStore reading fallback total |
-| `gitlab_redis_multi_store_pipelined_diff_error_total` | Prometheus Counter | command, instance_name | Redis MultiStore pipelined command diff between stores |
-| `gitlab_redis_multi_store_method_missing_total` | Prometheus Counter | command, instance_name | Client side Redis MultiStore method missing total |
+| Metrics name | Type | Labels | Description |
+|-------------------------------------------------------|--------------------|----------------------------|----------------------------------------------------------|
+| `gitlab_redis_multi_store_read_fallback_total` | Prometheus Counter | `command`, `instance_name` | Client side Redis MultiStore reading fallback total |
+| `gitlab_redis_multi_store_pipelined_diff_error_total` | Prometheus Counter | `command`, `instance_name` | Redis MultiStore `pipelined` command diff between stores |
+| `gitlab_redis_multi_store_method_missing_total` | Prometheus Counter | `command`, `instance_name` | Client side Redis MultiStore method missing total |
## Step 4: clean up after the migration
diff --git a/doc/development/reusing_abstractions.md b/doc/development/reusing_abstractions.md
index 58d1e20394c..e3f523fc6a7 100644
--- a/doc/development/reusing_abstractions.md
+++ b/doc/development/reusing_abstractions.md
@@ -198,7 +198,7 @@ Several base classes implement the service classes convention. You may consider
- `BaseGroupService` for services scoped to groups.
Classes that are not service objects should be
-[created elsewhere](directory_structure.md#use-namespaces-to-define-bounded-contexts),
+[created elsewhere](software_design.md#use-namespaces-to-define-bounded-contexts),
such as in `lib`.
#### ServiceResponse
diff --git a/doc/development/scalability.md b/doc/development/scalability.md
index 671086f33b2..de9c57c2f2a 100644
--- a/doc/development/scalability.md
+++ b/doc/development/scalability.md
@@ -267,7 +267,7 @@ However, there are a number of strategies to ensure queues get drained
in a timely manner:
- Add more processing capacity. This can be done by spinning up more
- instances of Sidekiq or [Sidekiq Cluster](../administration/operations/extra_sidekiq_processes.md).
+ instances of Sidekiq or [Sidekiq Cluster](../administration/sidekiq/extra_sidekiq_processes.md).
- Split jobs into smaller units of work. For example, `PostReceive`
used to process each commit message in the push, but now it farms out
this to `ProcessCommitWorker`.
diff --git a/doc/development/sec/analyzer_development_guide.md b/doc/development/sec/analyzer_development_guide.md
index af3a6f2b7d7..4fb32785b9f 100644
--- a/doc/development/sec/analyzer_development_guide.md
+++ b/doc/development/sec/analyzer_development_guide.md
@@ -21,7 +21,7 @@ There are a number of shared Go modules shared across analyzers for common behav
## How to use the analyzers
Analyzers are shipped as Docker images. For example, to run the
-[semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) Docker image to scan the working directory:
+[Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) Docker image to scan the working directory:
1. `cd` into the directory of the source code you want to scan.
1. Run `docker login registry.gitlab.com` and provide username plus
diff --git a/doc/development/sec/index.md b/doc/development/sec/index.md
index fc13c960451..3f52020701f 100644
--- a/doc/development/sec/index.md
+++ b/doc/development/sec/index.md
@@ -102,15 +102,15 @@ After being [merged](../integrations/secure.md#tracking-and-merging-vulnerabilit
### Analyzer vulnerability translation
-In the case of SAST's semgrep analyzer, there is a secondary identifier of particular importance: the identifier linking the report’s vulnerability
-to the legacy analyzer (that is, bandit or eslint).
+In the case of the SAST Semgrep analyzer, there is a secondary identifier of particular importance: the identifier linking the report’s vulnerability
+to the legacy analyzer (that is, bandit or ESLint).
To [enable vulnerability translation](../../user/application_security/sast/analyzers.md#vulnerability-translation)
-the semgrep analyzer relies on a secondary identifier exactly matching the primary identifier of the legacy analyzer.
+the Semgrep analyzer relies on a secondary identifier exactly matching the primary identifier of the legacy analyzer.
For example, when [`eslint`](https://gitlab.com/gitlab-org/security-products/analyzers/eslint) was previously used to generate vulnerability records,
the [`semgrep`](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) analyzer must produce an identifier collection containing the
-original eslint primary identifier.
+original ESLint primary identifier.
Given the original `eslint` report:
@@ -131,7 +131,7 @@ Given the original `eslint` report:
}
```
-The corresponding semgrep report must contain the `eslint_rule_id`:
+The corresponding Semgrep report must contain the `eslint_rule_id`:
```json
{
diff --git a/doc/development/secure_coding_guidelines.md b/doc/development/secure_coding_guidelines.md
index c102e99720f..bccdda9ca04 100644
--- a/doc/development/secure_coding_guidelines.md
+++ b/doc/development/secure_coding_guidelines.md
@@ -410,7 +410,7 @@ References:
#### XSS mitigation and prevention in JavaScript and Vue
- When updating the content of an HTML element using JavaScript, mark user-controlled values as `textContent` or `nodeValue` instead of `innerHTML`.
-- Avoid using `v-html` with user-controlled data, use [`v-safe-html`](https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/directives-safe-html-directive--default) instead.
+- Avoid using `v-html` with user-controlled data, use [`v-safe-html`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/vue_shared/directives/safe_html.js) instead.
- Render unsafe or unsanitized content using [`dompurify`](fe_guide/security.md#sanitize-html-output).
- Consider using [`gl-sprintf`](../../ee/development/i18n/externalization.md#interpolation) to interpolate translated strings securely.
- Avoid `__()` with translations that contain user-controlled values.
@@ -422,7 +422,7 @@ References:
##### Vue
- [isSafeURL](https://gitlab.com/gitlab-org/gitlab/-/blob/v12.7.5-ee/app/assets/javascripts/lib/utils/url_utility.js#L190-207)
-- [GlSprintf](https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/utilities-sprintf--default)
+- [GlSprintf](https://gitlab-org.gitlab.io/gitlab-ui/?path=/docs/utilities-sprintf--sentence-with-link)
#### Content Security Policy
@@ -718,13 +718,12 @@ There are some cases where `users` passed in the code is actually referring to a
```ruby
def find_user_from_sources
- strong_memoize(:find_user_from_sources) do
- deploy_token_from_request ||
- find_user_from_bearer_token ||
- find_user_from_job_token ||
- user_from_warden
- end
+ deploy_token_from_request ||
+ find_user_from_bearer_token ||
+ find_user_from_job_token ||
+ user_from_warden
end
+ strong_memoize_attr :find_user_from_sources
```
### Past Vulnerable Code
@@ -1210,7 +1209,7 @@ These types of bugs are often seen in environments which allow multi-threading a
### Examples
-**Example 1:** you have a model which accepts a URL as input. When the model is created you verify that the URL's host resolves to a public IP address, to prevent attackers making internal network calls. But DNS records can change ([DNS rebinding](#server-side-request-forgery-ssrf)]). An attacker updates the DNS record to `127.0.0.1`, and when your code resolves those URL's host it results in sending a potentially malicious request to a server on the internal network. The property was valid at the "time of check", but invalid and malicious at "time of use".
+**Example 1:** you have a model which accepts a URL as input. When the model is created you verify that the URL host resolves to a public IP address, to prevent attackers making internal network calls. But DNS records can change ([DNS rebinding](#server-side-request-forgery-ssrf)]). An attacker updates the DNS record to `127.0.0.1`, and when your code resolves those URL host it results in sending a potentially malicious request to a server on the internal network. The property was valid at the "time of check", but invalid and malicious at "time of use".
GitLab-specific example can be found in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/214401) where, although `Gitlab::UrlBlocker.validate!` was called, the returned value was not used. This made it vulnerable to TOCTOU bug and SSRF protection bypass through [DNS rebinding](#server-side-request-forgery-ssrf). The fix was to [use the validated IP address](https://gitlab.com/gitlab-org/gitlab/-/commit/7af8abd4df9a98f7a1ae7c4ec9840d0a7a8c684d).
diff --git a/doc/development/serializing_data.md b/doc/development/serializing_data.md
deleted file mode 100644
index aa8b20eded7..00000000000
--- a/doc/development/serializing_data.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/serializing_data.md'
-remove_date: '2022-11-06'
----
-
-This document was moved to [another location](database/serializing_data.md).
-
-<!-- This redirect file can be deleted after <2022-11-06>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/service_ping/implement.md b/doc/development/service_ping/implement.md
index 5a564b2d83e..70da97502bb 100644
--- a/doc/development/service_ping/implement.md
+++ b/doc/development/service_ping/implement.md
@@ -181,7 +181,7 @@ The highest encountered error rate is 4.9%.
When correctly used, the `estimate_batch_distinct_count` method enables efficient counting over
columns that contain non-unique values, which cannot be assured by other counters.
-##### estimate_batch_distinct_count method
+##### `estimate_batch_distinct_count` method
Method:
@@ -316,7 +316,7 @@ HyperLogLog (HLL) is a probabilistic algorithm and its **results always includes
used HLL implementation is "approximated with a standard error of 0.81%".
NOTE:
- A user's consent for usage_stats (`User.single_user&.requires_usage_stats_consent?`) is not checked during the data tracking stage due to performance reasons. Keys corresponding to those counters are present in Redis even if `usage_stats_consent` is still required. However, no metric is collected from Redis and reported back to GitLab as long as `usage_stats_consent` is required.
+ A user's consent for `usage_stats` (`User.single_user&.requires_usage_stats_consent?`) is not checked during the data tracking stage due to performance reasons. Keys corresponding to those counters are present in Redis even if `usage_stats_consent` is still required. However, no metric is collected from Redis and reported back to GitLab as long as `usage_stats_consent` is required.
With `Gitlab::UsageDataCounters::HLLRedisCounter` we have available data structures used to count unique values.
@@ -509,7 +509,7 @@ We have the following recommendations for [adding new events](#add-new-events):
Events are tracked behind optional [feature flags](../feature_flags/index.md) due to concerns for Redis performance and scalability.
-For a full list of events and corresponding feature flags see, [known_events](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters/known_events/) files.
+For a full list of events and corresponding feature flags, see the [`known_events/`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters/known_events/) files.
To enable or disable tracking for specific event in <https://gitlab.com> or <https://about.staging.gitlab.com>, run commands such as the following to
[enable or disable the corresponding feature](../feature_flags/index.md).
@@ -870,7 +870,7 @@ these steps:
Only metrics calculated with [Estimated Batch Counters](#estimated-batch-counters)
can be persisted for database sourced aggregated metrics. To persist a metric,
inject a Ruby block into the
-[estimate_batch_distinct_count](#estimate_batch_distinct_count-method) method.
+[`estimate_batch_distinct_count`](#estimate_batch_distinct_count-method) method.
This block should invoke the
`Gitlab::Usage::Metrics::Aggregates::Sources::PostgresHll.save_aggregated_metrics`
[method](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage/metrics/aggregates/sources/postgres_hll.rb#L21),
diff --git a/doc/development/service_ping/metrics_dictionary.md b/doc/development/service_ping/metrics_dictionary.md
index 3439f581e7f..49f8a5ac465 100644
--- a/doc/development/service_ping/metrics_dictionary.md
+++ b/doc/development/service_ping/metrics_dictionary.md
@@ -41,7 +41,7 @@ Each metric is defined in a separate YAML file consisting of a number of fields:
| `value_type` | yes | `string`; one of [`string`, `number`, `boolean`, `object`](https://json-schema.org/understanding-json-schema/reference/type.html). |
| `status` | yes | `string`; [status](#metric-statuses) of the metric, may be set to `active`, `removed`, `broken`. |
| `time_frame` | yes | `string`; may be set to a value like `7d`, `28d`, `all`, `none`. |
-| `data_source` | yes | `string`; may be set to a value like `database`, `redis`, `redis_hll`, `prometheus`, `system`. |
+| `data_source` | yes | `string`; may be set to a value like `database`, `redis`, `redis_hll`, `prometheus`, `system`, `license`. |
| `data_category` | yes | `string`; [categories](#data-category) of the metric, may be set to `operational`, `optional`, `subscription`, `standard`. The default value is `optional`.|
| `instrumentation_class` | yes | `string`; [the class that implements the metric](metrics_instrumentation.md). |
| `distribution` | yes | `array`; may be set to one of `ce, ee` or `ee`. The [distribution](https://about.gitlab.com/handbook/marketing/strategic-marketing/tiers/#definitions) where the tracked feature is available. |
@@ -55,7 +55,7 @@ Each metric is defined in a separate YAML file consisting of a number of fields:
| `options` | no | `object`: options information needed to calculate the metric value. |
| `skip_validation` | no | This should **not** be set. [Used for imported metrics until we review, update and make them valid](https://gitlab.com/groups/gitlab-org/-/epics/5425). |
-### Metric key_path
+### Metric `key_path`
The `key_path` of the metric is the location in the JSON Service Ping payload.
@@ -108,7 +108,7 @@ Metric definitions can have one of the following statuses:
- `broken`: Metric reports broken data (for example, -1 fallback), or does not report data at all. A metric marked as `broken` must also have the `repair_issue_url` attribute.
- `removed`: Metric was removed, but it may appear in Service Ping payloads sent from instances running on older versions of GitLab.
-### Metric value_type
+### Metric `value_type`
Metric definitions can have one of the following values for `value_type`:
@@ -120,12 +120,23 @@ In general, we avoid complex objects and prefer one of the `boolean`, `number`,
An example of a metric that uses `value_type: object` is `topology` (`/config/metrics/settings/20210323120839_topology.yml`),
which has a related schema in `/config/metrics/objects_schemas/topology_schema.json`.
-### Metric time_frame
-
-- `7d`: The metric data applies to the most recent 7-day interval. For example, the following metric counts the number of users that create epics over a 7-day interval: `ee/config/metrics/counts_7d/20210305145820_g_product_planning_epic_created_weekly.yml`.
-- `28d`: The metric data applies to the most recent 28-day interval. For example, the following metric counts the number of unique users that create issues over a 28-day interval: `config/metrics/counts_28d/20210216181139_issues.yml`.
-- `all`: The metric data applies for the whole time the metric has been active (all-time interval). For example, the following metric counts all users that create issues: `/config/metrics/counts_all/20210216181115_issues.yml`.
-- `none`: The metric collects a type of data that's not tracked over time, such as settings and configuration information. Therefore, a time interval is not applicable. For example, `uuid` has no time interval applicable: `config/metrics/license/20210201124933_uuid.yml`.
+### Metric `time_frame`
+
+A metric's time frame is calculated based on the `time_frame` field and the `data_source` of the metric.
+For `redis_hll` metrics, the type of aggregation is also taken into consideration. In this context, the term "aggregation" refers to [chosen events data storage interval](implement.md#add-new-events), and is **NOT** related to the Aggregated Metrics feature.
+For more information about the aggregation type of each feature, see the [`common.yml` file](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters/known_events/common.yml). Weeks run from Monday to Sunday.
+
+| data_source | time_frame | aggregation | Description |
+|------------------------|------------|----------------|-------------------------------------------------|
+| any | `none` | not applicable | A type of data that’s not tracked over time, such as settings and configuration information |
+| `database` | `all` | not applicable | The whole time the metric has been active (all-time interval) |
+| `database` | `7d` | not applicable | 9 days ago to 2 days ago |
+| `database` | `28d` | not applicable | 30 days ago to 2 days ago |
+| `redis` | `all` | not applicable | The whole time the metric has been active (all-time interval) |
+| `redis_hll` | `7d` | `daily` | Most recent 7 complete days |
+| `redis_hll` | `7d` | `weekly` | Most recent complete week |
+| `redis_hll` | `28d` | `daily` | Most recent 28 complete days |
+| `redis_hll` | `28d` | `weekly` | Most recent 4 complete weeks |
### Data category
diff --git a/doc/development/service_ping/metrics_instrumentation.md b/doc/development/service_ping/metrics_instrumentation.md
index a9f236819fe..5cc8a7811d7 100644
--- a/doc/development/service_ping/metrics_instrumentation.md
+++ b/doc/development/service_ping/metrics_instrumentation.md
@@ -464,3 +464,15 @@ This guide describes how to migrate a Service Ping metric from [`lib/gitlab/usag
1. Remove the code from [`lib/gitlab/usage_data.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data.rb) or [`ee/lib/ee/gitlab/usage_data.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/ee/gitlab/usage_data.rb).
1. Remove the tests from [`spec/lib/gitlab/usage_data.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/lib/gitlab/usage_data_spec.rb) or [`ee/spec/lib/ee/gitlab/usage_data.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/spec/lib/ee/gitlab/usage_data_spec.rb).
+
+## Troubleshoot metrics
+
+Sometimes metrics fail for reasons that are not immediately clear. The failures can be related to performance issues or other problems.
+The following pairing session video gives you an example of an investigation in to a real-world failing metric.
+
+<div class="video-fallback">
+ See the video from: <a href="https://www.youtube.com/watch?v=y_6m2POx2ug">Product Intelligence Office Hours Oct 27th</a> to learn more about the metrics troubleshooting process.
+</div>
+<figure class="video-container">
+ <iframe src="https://www.youtube.com/embed/y_6m2POx2ug" frameborder="0" allowfullscreen="true"> </iframe>
+</figure>
diff --git a/doc/development/service_ping/troubleshooting.md b/doc/development/service_ping/troubleshooting.md
index f8fd45e6062..3b7cd092d97 100644
--- a/doc/development/service_ping/troubleshooting.md
+++ b/doc/development/service_ping/troubleshooting.md
@@ -30,7 +30,7 @@ For results about an investigation conducted into an unexpected drop in Service
Check if the [export jobs](https://gitlab.com/gitlab-services/version-gitlab-com#data-export-using-pipeline-schedules) are successful.
-Check [Service Ping errors](https://app.periscopedata.com/app/gitlab/968489/Product-Intelligence---Service-Ping-Health?widget=14609989&udv=0) in the [Service Ping Health Dahsboard](https://app.periscopedata.com/app/gitlab/968489/Product-Intelligence---Service-Ping-Health).
+Check [Service Ping errors](https://app.periscopedata.com/app/gitlab/968489/Product-Intelligence---Service-Ping-Health?widget=14609989&udv=0) in the [Service Ping Health Dashboard](https://app.periscopedata.com/app/gitlab/968489/Product-Intelligence---Service-Ping-Health).
### Troubleshoot Google Storage layer
diff --git a/doc/development/sha1_as_binary.md b/doc/development/sha1_as_binary.md
deleted file mode 100644
index 7f928d09470..00000000000
--- a/doc/development/sha1_as_binary.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/sha1_as_binary.md'
-remove_date: '2022-11-06'
----
-
-This document was moved to [another location](database/sha1_as_binary.md).
-
-<!-- This redirect file can be deleted after <2022-11-06>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/sidekiq/compatibility_across_updates.md b/doc/development/sidekiq/compatibility_across_updates.md
index cfb709d9110..b417a099228 100644
--- a/doc/development/sidekiq/compatibility_across_updates.md
+++ b/doc/development/sidekiq/compatibility_across_updates.md
@@ -123,14 +123,62 @@ uses a parameter hash.
end
```
-## Removing workers
+## Removing worker classes
-Try to avoid removing workers and their queues in minor and patch
-releases.
+To remove a worker class, follow these steps over two minor releases:
-During online update instance can have pending jobs and removing the queue can
-lead to those jobs being stuck forever. If you can't write migration for those
-Sidekiq jobs, please consider removing the worker in a major release only.
+### In the first minor release
+
+1. Remove any code that enqueues the jobs.
+
+ For example, if there is a UI component or an API endpoint that a user can interact with that results in the worker instance getting enqueued, make sure those surface areas are either removed or updated in a way that the worker instance is no longer enqueued.
+
+ This ensures that instances related to the worker class are no longer being enqueued.
+
+1. Ensure both the frontend and backend code no longer relies on any of the work that used to be done by the worker.
+1. In the relevant worker classes, replace the contents of the `perform` method with a no-op, while keeping any arguments in tact.
+
+ For example, if you're working with the following `ExampleWorker`:
+
+ ```ruby
+ class ExampleWorker
+ def perform(object_id)
+ SomeService.run!(object_id)
+ end
+ end
+ ```
+
+ Implementing the no-op might look like this:
+
+ ```ruby
+ class ExampleWorker
+ def perform(object_id); end
+ end
+ ```
+
+ By implementing this no-op, you can avoid unnecessary cycles once any deprecated jobs that are still enqueued eventually get processed.
+
+### In a subsequent, separate minor release
+
+1. Delete the worker class file and follow the guidance in our [Sidekiq queues documentation](../sidekiq/index.md#sidekiq-queues) around running Rake tasks to regenerate/update related files.
+1. Add a migration (not a post-deployment migration) that uses `sidekiq_remove_jobs`:
+
+ ```ruby
+ class RemoveMyDeprecatedWorkersJobInstances < Gitlab::Database::Migration[2.0]
+ DEPRECATED_JOB_CLASSES = %w[
+ MyDeprecatedWorkerOne
+ MyDeprecatedWorkerTwo
+ ]
+
+ def up
+ sidekiq_remove_jobs(job_klasses: DEPRECATED_JOB_CLASSES)
+ end
+
+ def down
+ # This migration removes any instances of deprecated workers and cannot be undone.
+ end
+ end
+ ```
## Renaming queues
@@ -141,7 +189,7 @@ When renaming queues, use the `sidekiq_queue_migrate` helper migration method
in a **post-deployment migration**:
```ruby
-class MigrateTheRenamedSidekiqQueue < Gitlab::Database::Migration[2.0]
+class MigrateTheRenamedSidekiqQueue < Gitlab::Database::Migration[2.1]
restrict_gitlab_migration gitlab_schema: :gitlab_main
disable_ddl_transaction!
diff --git a/doc/development/sidekiq/idempotent_jobs.md b/doc/development/sidekiq/idempotent_jobs.md
index 80c6c403549..1c4f4ba44a8 100644
--- a/doc/development/sidekiq/idempotent_jobs.md
+++ b/doc/development/sidekiq/idempotent_jobs.md
@@ -156,7 +156,7 @@ end
## Setting the deduplication time-to-live (TTL)
-Deduplication depends on an idempotency key that is stored in Redis. This is normally
+Deduplication depends on an idempotent key that is stored in Redis. This is normally
cleared by the configured deduplication strategy.
However, the key can remain until its TTL in certain cases like:
@@ -189,7 +189,7 @@ that can tolerate some duplication.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69372) in GitLab 14.3.
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/338350) in GitLab 14.4.
> - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/338350) in GitLab 14.6.
-> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/346598) in GitLab 14.9. [Feature flag preserve_latest_wal_locations_for_idempotent_jobs](https://gitlab.com/gitlab-org/gitlab/-/issues/346598) removed.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/346598) in GitLab 14.9. [Feature flag `preserve_latest_wal_locations_for_idempotent_jobs`](https://gitlab.com/gitlab-org/gitlab/-/issues/346598) removed.
The deduplication always take into account the latest binary replication pointer, not the first one.
This happens because we drop the same job scheduled for the second time and the Write-Ahead Log (WAL) is lost.
diff --git a/doc/development/sidekiq/index.md b/doc/development/sidekiq/index.md
index a95e94cdd34..c9d783377bd 100644
--- a/doc/development/sidekiq/index.md
+++ b/doc/development/sidekiq/index.md
@@ -14,7 +14,7 @@ information on administering GitLab, see [configuring Sidekiq](../../administrat
There are pages with additional detail on the following topics:
1. [Compatibility across updates](compatibility_across_updates.md)
-1. [Job idempotency and job deduplication](idempotent_jobs.md)
+1. [Job idempotence and job deduplication](idempotent_jobs.md)
1. [Limited capacity worker: continuously performing work with a specified concurrency](limited_capacity_worker.md)
1. [Logging](logging.md)
1. [Worker attributes](worker_attributes.md)
@@ -27,7 +27,7 @@ There are pages with additional detail on the following topics:
All workers should include `ApplicationWorker` instead of `Sidekiq::Worker`,
which adds some convenience methods and automatically sets the queue based on
-the [routing rules](../../administration/sidekiq/extra_sidekiq_routing.md#queue-routing-rules).
+the [routing rules](../../administration/sidekiq/processing_specific_job_classes.md#routing-rules).
## Retries
@@ -88,7 +88,7 @@ error rate.
Previously, each worker had its own queue, which was automatically set based on the
worker class name. For a worker named `ProcessSomethingWorker`, the queue name
would be `process_something`. You can now route workers to a specific queue using
-[queue routing rules](../../administration/sidekiq/extra_sidekiq_routing.md#queue-routing-rules).
+[queue routing rules](../../administration/sidekiq/processing_specific_job_classes.md#routing-rules).
In GDK, new workers are routed to a queue named `default`.
If you're not sure what queue a worker uses,
diff --git a/doc/development/single_table_inheritance.md b/doc/development/single_table_inheritance.md
deleted file mode 100644
index da8d48f2a42..00000000000
--- a/doc/development/single_table_inheritance.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/single_table_inheritance.md'
-remove_date: '2022-11-06'
----
-
-This document was moved to [another location](database/single_table_inheritance.md).
-
-<!-- This redirect file can be deleted after <2022-11-06>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/snowplow/index.md b/doc/development/snowplow/index.md
index 8d05e05e592..32628744a23 100644
--- a/doc/development/snowplow/index.md
+++ b/doc/development/snowplow/index.md
@@ -17,7 +17,7 @@ Snowplow is an enterprise-grade marketing and Product Intelligence platform that
- **Data modeling** joins event-level data with other data sets, aggregates them into smaller data sets, and applies business logic. This produces a clean set of tables for data analysis. We use data models for Redshift and Looker.
- **Analytics** are performed on Snowplow events or on aggregate tables.
-![snowplow_flow](../img/snowplow_flow.png)
+![Snowplow flow](../img/snowplow_flow.png)
## Enable Snowplow tracking
diff --git a/doc/development/snowplow/schemas.md b/doc/development/snowplow/schemas.md
index 5a6cdea9fee..da58cd5f2e5 100644
--- a/doc/development/snowplow/schemas.md
+++ b/doc/development/snowplow/schemas.md
@@ -68,8 +68,8 @@ Page titles are hardcoded as `GitLab` for the same reason.
| `doc_charset` | **{dotted-circle}** | string | Web page's character encoding |
| `doc_height` | **{dotted-circle}** | string | Web page height |
| `doc_width` | **{dotted-circle}** | string | Web page width |
-| `domain_sessionid` | **{dotted-circle}** | string | Unique identifier (UUID) for this visit of this user_id to this domain |
-| `domain_sessionidx` | **{dotted-circle}** | integer | Index of number of visits that this user_id has made to this domain (The first visit is `1`) |
+| `domain_sessionid` | **{dotted-circle}** | string | Unique identifier (UUID) for this visit of this `user_id` to this domain |
+| `domain_sessionidx` | **{dotted-circle}** | integer | Index of number of visits that this `user_id` has made to this domain (The first visit is `1`) |
| `domain_userid` | **{dotted-circle}** | string | Unique identifier for a user, based on a first party cookie (so domain specific) |
| `dvce_created_tstamp` | **{dotted-circle}** | timestamp | Timestamp when event occurred, as recorded by client device |
| `dvce_ismobile` | **{dotted-circle}** | boolean | Indicates whether device is mobile |
diff --git a/doc/development/snowplow/troubleshooting.md b/doc/development/snowplow/troubleshooting.md
index 306040f8c9c..47df3e43d57 100644
--- a/doc/development/snowplow/troubleshooting.md
+++ b/doc/development/snowplow/troubleshooting.md
@@ -35,7 +35,7 @@ Drop occurring at application layer can be symptom of some issue, but it might b
or even a result of a public holiday in some regions of the world with a larger user-base. To verify if there is an underlying problem to solve, you can check following things:
1. Check `about.gitlab.com` website traffic on [Google Analytics](https://analytics.google.com/analytics/web/) to verify if some public holiday might impact overall use of GitLab system
- 1. You may require to open an access request for Google Analytics access first eg: [access request internal issue](https://gitlab.com/gitlab-com/team-member-epics/access-requests/-/issues/1772)
+ 1. You may require to open an access request for Google Analytics access first, for example: [access request internal issue](https://gitlab.com/gitlab-com/team-member-epics/access-requests/-/issues/1772)
1. Plot `select date(dvce_created_tstamp) , event , count(*) from legacy.snowplow_unnested_events_90 where dvce_created_tstamp > '2021-06-15' and dvce_created_tstamp < '2021-07-10' group by 1 , 2 order by 1 , 2` in SiSense to see what type of events was responsible for drop
1. Plot `select date(dvce_created_tstamp) ,se_category , count(*) from legacy.snowplow_unnested_events_90 where dvce_created_tstamp > '2021-06-15' and dvce_created_tstamp < '2021-07-31' and event = 'struct' group by 1 , 2 order by 1, 2` what events recorded the biggest drops in suspected category
1. Check if there was any MR merged that might cause reduction in reported events, pay an attention to ~"product intelligence" and ~"growth experiment" labeled MRs
diff --git a/doc/development/software_design.md b/doc/development/software_design.md
new file mode 100644
index 00000000000..03cbbb13d9f
--- /dev/null
+++ b/doc/development/software_design.md
@@ -0,0 +1,141 @@
+---
+stage: none
++group: Engineering Productivity
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Software design guides
+
+## Use ubiquitous language instead of CRUD terminology
+
+The code should use the same [ubiquitous language](https://about.gitlab.com/handbook/communication/#ubiquitous-language)
+as used in the product and user documentation. Failure to use ubiquitous language correctly
+can be a major cause of confusion for contributors and customers when there is constant translation
+or use of multiple terms.
+This also goes against our [communication strategy](https://about.gitlab.com/handbook/communication/#mecefu-terms).
+
+In the example below, [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete)
+terminology introduces ambiguity. The name says we are creating an `epic_issues`
+association record, but we are adding an existing issue to an epic. The name `epic_issues`,
+used from Rails convention, leaks to higher abstractions such as service objects.
+The code speaks the framework jargon rather than ubiquitous language.
+
+```ruby
+# Bad
+EpicIssues::CreateService
+```
+
+Using ubiquitous language makes the code clear and doesn't introduce any
+cognitive load to a reader trying to translate the framework jargon.
+
+```ruby
+# Good
+Epic::AddExistingIssueService
+```
+
+You can use CRUD when representing simple concepts that are not ambiguous,
+like creating a project, and when matching the existing ubiquitous language.
+
+```ruby
+# OK: Matches the product language.
+Projects::CreateService
+```
+
+New classes and database tables should use ubiquitous language. In this case the model name
+and table name follow the Rails convention.
+
+Existing classes that don't follow ubiquitous language should be renamed, when possible.
+Some low level abstractions such as the database tables don't need to be renamed.
+For example, use `self.table_name=` when the model name diverges from the table name.
+
+We can allow exceptions only when renaming is challenging. For example, when the naming is used
+for STI, exposed to the user, or if it would be a breaking change.
+
+## Use namespaces to define bounded contexts
+
+A healthy application is divided into macro and sub components that represent the contexts at play,
+whether they are related to business domain or infrastructure code.
+
+As GitLab code has so many features and components it's hard to see what contexts are involved.
+We should expect any class to be defined inside a module/namespace that represents the contexts where it operates.
+
+When we namespace classes inside their domain:
+
+- Similar terminology becomes unambiguous as the domain clarifies the meaning:
+ For example, `MergeRequests::Diff` and `Notes::Diff`.
+- Top-level namespaces could be associated to one or more groups identified as domain experts.
+- We can better identify the interactions and coupling between components.
+ For example, several classes inside `MergeRequests::` domain interact more with `Ci::`
+ domain and less with `ImportExport::`.
+
+A good guideline for naming a top-level namespace (bounded context) is to use the related
+[feature category](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/data/categories.yml).
+For example, `Continuous Integration` feature category maps to `Ci::` namespace.
+
+```ruby
+# bad
+class JobArtifact
+end
+
+# good
+module Ci
+ class JobArtifact
+ end
+end
+```
+
+Projects and Groups are generally container concepts because they identify tenants.
+They allow features to exist at the project or group level, like repositories or runners,
+but do not nest such features under `Projects::` or `Groups::`.
+
+`Projects::` and `Groups::` namespaces should be used only for concepts that are strictly related to them:
+for example `Project::CreateService` or `Groups::TransferService`.
+
+For controllers we allow `app/controllers/projects` and `app/controllers/groups` to be exceptions.
+We use this convention to indicate the scope of a given web endpoint.
+
+Do not use the [stage or group name](https://about.gitlab.com/handbook/product/categories/#devops-stages)
+because a feature category could be reassigned to a different group in the future.
+
+```ruby
+# bad
+module Create
+ class Commit
+ end
+end
+
+# good
+module Repositories
+ class Commit
+ end
+end
+```
+
+On the other hand, a feature category may sometimes be too granular. Features tend to be
+treated differently according to Product and Marketing, while they may share a lot of
+domain models and behavior under the hood. In this case, having too many bounded contexts
+could make them shallow and more coupled with other contexts.
+
+Bounded contexts (or top-level namespaces) can be seen as macro-components in the overall app.
+Good bounded contexts should be [deep](https://medium.com/@nakabonne/depth-of-module-f62dac3c2fdb)
+so consider having nested namespaces to further break down complex parts of the domain.
+For example, `Ci::Config::`.
+
+For example, instead of having separate and granular bounded contexts like: `ContainerScanning::`,
+`ContainerHostSecurity::`, `ContainerNetworkSecurity::`, we could have:
+
+```ruby
+module ContainerSecurity
+ module HostSecurity
+ end
+
+ module NetworkSecurity
+ end
+
+ module Scanning
+ end
+end
+```
+
+If classes that are defined into a namespace have a lot in common with classes in other namespaces,
+chances are that these two namespaces are part of the same bounded context.
diff --git a/doc/development/spam_protection_and_captcha/exploratory_testing.md b/doc/development/spam_protection_and_captcha/exploratory_testing.md
index f6e3e6814a8..1bcd336ce93 100644
--- a/doc/development/spam_protection_and_captcha/exploratory_testing.md
+++ b/doc/development/spam_protection_and_captcha/exploratory_testing.md
@@ -353,7 +353,7 @@ GraphQL response:
}
```
-### Scenario: allow_possible_spam feature flag enabled
+### Scenario: `allow_possible_spam` feature flag enabled
With the `allow_possible_spam` feature flag enabled, the API returns a 200 response. Any
valid request is successful and no CAPTCHA is presented, even if the request is considered
diff --git a/doc/development/sql.md b/doc/development/sql.md
index cdc952eb08b..5dbfd8f3ddb 100644
--- a/doc/development/sql.md
+++ b/doc/development/sql.md
@@ -103,7 +103,7 @@ transaction. Transactions for migrations can be disabled using the following
pattern:
```ruby
-class MigrationName < Gitlab::Database::Migration[2.0]
+class MigrationName < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
end
```
@@ -111,7 +111,7 @@ end
For example:
```ruby
-class AddUsersLowerUsernameEmailIndexes < Gitlab::Database::Migration[2.0]
+class AddUsersLowerUsernameEmailIndexes < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
diff --git a/doc/development/swapping_tables.md b/doc/development/swapping_tables.md
deleted file mode 100644
index eaa6568dc36..00000000000
--- a/doc/development/swapping_tables.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/swapping_tables.md'
-remove_date: '2022-11-04'
----
-
-This document was moved to [another location](database/swapping_tables.md).
-
-<!-- This redirect file can be deleted after <2022-11-04>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md
index b6bf3c7805a..aee3e2871c2 100644
--- a/doc/development/testing_guide/best_practices.md
+++ b/doc/development/testing_guide/best_practices.md
@@ -425,11 +425,10 @@ results are available, and not just the first failure.
when you need an ID/IID/access level that doesn't actually exists. Using 123, 1234,
or even 999 is brittle as these IDs could actually exist in the database in the
context of a CI run.
-- All top-level `RSpec.describe` blocks should have [`feature_category`](https://about.gitlab.com/categories.json) metadata set.
- Consider splitting the file in the case there are identified multiple feature categories in same file.
- If no `feature_category` is identified then use `not_owned`. This information is used in flaky test
- issues created in order to identify the group owning the feature.
- Eg: `RSpec.describe Admin::Geo::SettingsController, :geo, feature_category: :geo_replication do`.
+
+### Feature category metadata
+
+You must [set feature category metadata for each RSpec example](../feature_categorization/index.md#rspec-examples).
### Coverage
@@ -845,6 +844,8 @@ it 'is overdue' do
travel_to(3.days.from_now) do
expect(issue).to be_overdue
end
+
+ travel_back # Returns the current time back to its original state
end
```
@@ -923,6 +924,21 @@ sequence-generated column. To avoid accidental conflicts, specs should also
avoid manually specifying any values in these kinds of columns. Instead, leave
them unspecified, and look up the value after the row is created.
+##### TestProf in migration specs
+
+Because of what is described above, migration specs can't be run inside
+a database transaction. Our test suite uses
+[TestProf](https://github.com/test-prof/test-prof) to improve the runtime of the
+test suite, but `TestProf` uses database transactions to perform these optimizations.
+For this reason, we can't use `TestProf` methods in our migration specs.
+These are the methods that should not be used and should be replaced with
+default RSpec methods instead:
+
+- `let_it_be`: use `let` or `let!` instead.
+- `let_it_be_with_reload`: use `let` or `let!` instead.
+- `let_it_be_with_refind`: use `let` or `let!` instead.
+- `before_all`: use `before` or `before(:all)` instead.
+
#### Redis
GitLab stores two main categories of data in Redis: cached items, and Sidekiq
@@ -1327,7 +1343,7 @@ Testing query performance allows us to:
`QueryRecorder` allows profiling and testing of the number of database queries
performed in a given block of code.
-See the [`QueryRecorder`](../query_recorder.md) section for more details.
+See the [`QueryRecorder`](../database/query_recorder.md) section for more details.
#### GitalyClient
@@ -1439,7 +1455,7 @@ or [cause the universe to implode](../contributing/verify/index.md#do-not-cause-
### Factories
-GitLab uses [factory_bot](https://github.com/thoughtbot/factory_bot) as a test fixture replacement.
+GitLab uses [`factory_bot`](https://github.com/thoughtbot/factory_bot) as a test fixture replacement.
- Factory definitions live in `spec/factories/`, named using the pluralization
of their corresponding model (`User` factories are defined in `users.rb`).
@@ -1451,11 +1467,51 @@ GitLab uses [factory_bot](https://github.com/thoughtbot/factory_bot) as a test f
resulting record to pass validation.
- When instantiating from a factory, don't supply attributes that aren't
required by the test.
-- Prefer [implicit](https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#implicit-definition),
+- Use [implicit](https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#implicit-definition),
[explicit](https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#explicit-definition), or
[inline](https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#inline-definition) associations
- over `create` / `build` for association setup in callbacks.
+ instead of `create` / `build` for association setup in callbacks.
See [issue #262624](https://gitlab.com/gitlab-org/gitlab/-/issues/262624) for further context.
+
+ When creating factories with a [`has_many`](https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#has_many-associations) and `belongs_to` association, use the `instance` method to refer to the object being built.
+ This prevents [creation of unnecessary records](https://gitlab.com/gitlab-org/gitlab/-/issues/378183) by using [interconnected associations](https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#interconnected-associations).
+
+ For example, if we have the following classes:
+
+ ```ruby
+ class Car < ApplicationRecord
+ has_many :wheels, inverse_of: :car, foreign_key: :car_id
+ end
+
+ class Wheel < ApplicationRecord
+ belongs_to :car, foreign_key: :car_id, inverse_of: :wheel, optional: false
+ end
+ ```
+
+ We can create the following factories:
+
+ ```ruby
+ FactoryBot.define do
+ factory :car do
+ transient do
+ wheels_count { 2 }
+ end
+
+ wheels do
+ Array.new(wheels_count) do
+ association(:wheel, car: instance)
+ end
+ end
+ end
+ end
+
+ FactoryBot.define do
+ factory :wheel do
+ car { association :car }
+ end
+ end
+ ```
+
- Factories don't have to be limited to `ActiveRecord` objects.
[See example](https://gitlab.com/gitlab-org/gitlab-foss/commit/0b8cefd3b2385a21cfed779bd659978c0402766d).
- Factories and their traits should produce valid objects that are [verified by specs](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/models/factories_spec.rb).
diff --git a/doc/development/testing_guide/contract/index.md b/doc/development/testing_guide/contract/index.md
index 8412a260c7d..08a21e58a52 100644
--- a/doc/development/testing_guide/contract/index.md
+++ b/doc/development/testing_guide/contract/index.md
@@ -26,6 +26,8 @@ The contracts themselves are stored in [`/spec/contracts/contracts`](https://git
Before running the consumer tests, go to `spec/contracts/consumer` and run `npm install`. To run all the consumer tests, you just need to run `npm test -- /specs`. Otherwise, to run a specific spec file, replace `/specs` with the specific spec filename.
+You can also run tests from the root directory of the project, using the command `yarn jest:contract`.
+
### Run the provider tests
Before running the provider tests, make sure your GDK (GitLab Development Kit) is fully set up and running. You can follow the setup instructions detailed in the [GDK repository](https://gitlab.com/gitlab-org/gitlab-development-kit/-/tree/main). To run the provider tests, you use Rake tasks that can be found in [`./lib/tasks/contracts`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/tasks/contracts). To get a list of all the Rake tasks related to the provider tests, run `bundle exec rake -T contracts`. For example:
diff --git a/doc/development/testing_guide/end_to_end/feature_flags.md b/doc/development/testing_guide/end_to_end/feature_flags.md
index 6d826e170f6..e473b158087 100644
--- a/doc/development/testing_guide/end_to_end/feature_flags.md
+++ b/doc/development/testing_guide/end_to_end/feature_flags.md
@@ -30,7 +30,7 @@ feature flag is under test.
- Format: `feature_flag: { name: 'feature_flag_name', scope: :project }`
- When `scope` is set to `:global`, the test will be **skipped on all live .com environments**. This is to avoid issues with feature flag changes affecting other tests or users on that environment.
-- When `scope` is set to any other value (such as `:project`, `:group` or `:user`), or if no `scope` is specified, the test will only be **skipped on canary, production, and preprod**.
+- When `scope` is set to any other value (such as `:project`, `:group` or `:user`), or if no `scope` is specified, the test will only be **skipped on canary, production, and pre-production**.
This is due to the fact that administrator access is not available there.
**WARNING:** You are strongly advised to first try and [enable feature flags only for a group, project, user](../../feature_flags/index.md#feature-actors),
@@ -42,7 +42,7 @@ or [feature group](../../feature_flags/index.md#feature-groups).
with administrator access, such as staging.
**Note on `requires_admin`:** This tag should still be applied if there are other actions within the test that require administrator access that are unrelated to updating a
-feature flag (ex: creating a user via the API).
+feature flag (like creating a user via the API).
The code below would enable a feature flag named `:feature_flag_name` for the project
created by the test:
diff --git a/doc/development/testing_guide/end_to_end/index.md b/doc/development/testing_guide/end_to_end/index.md
index 8ffe044c4d8..55d725ba4ae 100644
--- a/doc/development/testing_guide/end_to_end/index.md
+++ b/doc/development/testing_guide/end_to_end/index.md
@@ -238,7 +238,7 @@ Each type of scheduled pipeline generates a static link for the latest test repo
- [`production`](https://storage.googleapis.com/gitlab-qa-allure-reports/production-full/master/index.html)
- [`production-sanity`](https://storage.googleapis.com/gitlab-qa-allure-reports/production-sanity/master/index.html)
-## How do I run the tests?
+## How do you run the tests?
If you are not [testing code in a merge request](#testing-code-in-merge-requests),
there are two main options for running the tests. If you want to run
@@ -255,12 +255,12 @@ and the section below.
Learn how to perform [tests that require special setup or consideration to run on your local environment](running_tests_that_require_special_setup.md).
-## How do I write tests?
+## How do you write tests?
In order to write new tests, you first need to learn more about GitLab QA
architecture. See the [documentation about it](https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/architecture.md).
-Once you decided where to put [test environment orchestration scenarios](https://gitlab.com/gitlab-org/gitlab-qa/tree/master/lib/gitlab/qa/scenario) and
+After you've decided where to put [test environment orchestration scenarios](https://gitlab.com/gitlab-org/gitlab-qa/tree/master/lib/gitlab/qa/scenario) and
[instance-level scenarios](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/qa/qa/specs/features), take a look at the [GitLab QA README](https://gitlab.com/gitlab-org/gitlab/-/tree/master/qa/README.md),
the [GitLab QA orchestrator README](https://gitlab.com/gitlab-org/gitlab-qa/tree/master/README.md),
and [the already existing instance-level scenarios](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/qa/qa/specs/features).
@@ -283,7 +283,7 @@ Continued reading:
- [Execution context selection](execution_context_selection.md)
- [Troubleshooting](troubleshooting.md)
-## Where can I ask for help?
+## Where can you ask for help?
You can ask question in the `#quality` channel on Slack (GitLab internal) or
you can find an issue you would like to work on in
diff --git a/doc/development/testing_guide/end_to_end/page_objects.md b/doc/development/testing_guide/end_to_end/page_objects.md
index 5fbf2ffbcde..6a599ce9a50 100644
--- a/doc/development/testing_guide/end_to_end/page_objects.md
+++ b/doc/development/testing_guide/end_to_end/page_objects.md
@@ -155,7 +155,7 @@ In our case, `data-qa-selector="login_field"`, `data-qa-selector="password_field
Things to note:
-- The name of the element and the `qa_selector` must match and be snake_cased
+- The name of the element and the `qa_selector` must match and be snake cased
- If the element appears on the page unconditionally, add `required: true` to the element. See
[Dynamic element validation](dynamic_element_validation.md)
- You may see `.qa-selector` classes in existing Page Objects. We should prefer the [`data-qa-selector`](#data-qa-selector-vs-qa-selector)
diff --git a/doc/development/testing_guide/end_to_end/rspec_metadata_tests.md b/doc/development/testing_guide/end_to_end/rspec_metadata_tests.md
index a7d1ece77b2..c1389b3ac0e 100644
--- a/doc/development/testing_guide/end_to_end/rspec_metadata_tests.md
+++ b/doc/development/testing_guide/end_to_end/rspec_metadata_tests.md
@@ -15,7 +15,8 @@ This is a partial list of the [RSpec metadata](https://relishapp.com/rspec/rspec
|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `:elasticsearch` | The test requires an Elasticsearch service. It is used by the [instance-level scenario](https://gitlab.com/gitlab-org/gitlab-qa#definitions) [`Test::Integration::Elasticsearch`](https://gitlab.com/gitlab-org/gitlab/-/blob/72b62b51bdf513e2936301cb6c7c91ec27c35b4d/qa/qa/ee/scenario/test/integration/elasticsearch.rb) to include only tests that require Elasticsearch. |
| `:except` | The test is to be run in their typical execution contexts _except_ as specified. See [test execution context selection](execution_context_selection.md) for more information. |
-| `:feature_flag` | The test uses a feature flag and therefore requires an administrator account to run. When `scope` is set to `:global`, the test will be skipped on all live .com environments. Otherwise, it will be skipped only on Canary, Production, and Preprod. See [testing with feature flags](../../../development/testing_guide/end_to_end/feature_flags.md) for more details. |
+| `:feature_flag` | The test uses a feature flag and therefore requires an administrator account to run. When `scope` is set to `:global`, the test will be skipped on all live .com environments. Otherwise, it will be skipped only on Canary, Production, and Pre-production. See [testing with feature flags](../../../development/testing_guide/end_to_end/feature_flags.md) for more details. |
+| `:framework` | The test makes sanity assertions around the QA framework itself |
| `:geo` | The test requires two GitLab Geo instances - a primary and a secondary - to be spun up. |
| `:gitaly_cluster` | The test runs against a GitLab instance where repositories are stored on redundant Gitaly nodes behind a Praefect node. All nodes are [separate containers](../../../administration/gitaly/praefect.md#requirements). Tests that use this tag have a longer setup time since there are three additional containers that need to be started. |
| `:github` | The test requires a GitHub personal access token. |
diff --git a/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md b/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md
index 81e1c7d5dc0..4a947e59d5f 100644
--- a/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md
+++ b/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md
@@ -15,8 +15,8 @@ The project also has instructions for forking and building the images automatica
Some extra environment variables for the location of the forked repository are also needed.
-- `QA_THIRD_PARTY_DOCKER_REGISTRY` (the container registry where the repository/images are hosted, eg `registry.gitlab.com`)
-- `QA_THIRD_PARTY_DOCKER_REPOSITORY` (the base repository path where the images are hosted, eg `registry.gitlab.com/<project path>`)
+- `QA_THIRD_PARTY_DOCKER_REGISTRY` (the container registry where the repository/images are hosted, for example `registry.gitlab.com`)
+- `QA_THIRD_PARTY_DOCKER_REPOSITORY` (the base repository path where the images are hosted, for example `registry.gitlab.com/<project path>`)
- `QA_THIRD_PARTY_DOCKER_USER` (a username that has access to the container registry for this repository)
- `QA_THIRD_PARTY_DOCKER_PASSWORD` (a password/token for the username to authenticate with)
diff --git a/doc/development/testing_guide/end_to_end/troubleshooting.md b/doc/development/testing_guide/end_to_end/troubleshooting.md
index e0925cb71f4..b4c47b90f0b 100644
--- a/doc/development/testing_guide/end_to_end/troubleshooting.md
+++ b/doc/development/testing_guide/end_to_end/troubleshooting.md
@@ -59,7 +59,7 @@ Net::ReadTimeout: Net::ReadTimeout with #<TCPSocket:(closed)>
```
This error can happen if GitLab runs on an address that does not resolve from
-`localhost`. For example, if you set GDK's `hostname`
+`localhost`. For example, if you set the GDK `hostname`
[to a specific local IP address](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/docs/run_qa_against_gdk.md#run-qa-tests-against-your-gdk-setup),
you must use that IP address instead of `localhost` in the command.
For example, if your IP is `192.168.0.12`:
diff --git a/doc/development/testing_guide/flaky_tests.md b/doc/development/testing_guide/flaky_tests.md
index cc62a0ebf03..ef5b75d166f 100644
--- a/doc/development/testing_guide/flaky_tests.md
+++ b/doc/development/testing_guide/flaky_tests.md
@@ -31,7 +31,7 @@ it's reset to a pristine test after each test.
inconsistent state, so that following tests might not know about certain columns.
- [Example 2](https://gitlab.com/gitlab-org/gitlab/-/issues/368500): A test modifies data that is
used by a following test.
-- [Example 3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103434#note_1172316521): A test for a database query passes in a fresh database, but in a
+- [Example 3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103434#note_1172316521): A test for a database query passes in a fresh database, but in a
CI/CD pipeline where the database is used to process previous test sequences, the test fails. This likely
means that the query itself needs to be updated to work in a non-clean database.
@@ -56,6 +56,7 @@ the problem.
- [Example 1](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10148/diffs): Without
specifying `ORDER BY`, database will not give deterministic ordering, or data race happening
in the tests.
+- [Example 2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106936/diffs).
### Dataset-specific
@@ -75,7 +76,7 @@ difficult to achieve locally.
any table has more than 500 columns. It could pass in the merge request, but fail later in
`master` if the order of tests changes.
- [Example 2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91016/diffs): A test asserts
- that trying to find a record with an unexisting ID retuns an error message. The test uses an
+ that trying to find a record with an nonexistent ID returns an error message. The test uses an
hardcoded ID that's supposed to not exist (e.g. `42`). If the test is run early in the test
suite, it might pass as not enough records were created before it, but as soon as it would run
later in the suite, there could be a record that actually has the ID `42`, hence the test would
@@ -104,6 +105,7 @@ or the app.
**Description:** The DOM selector used in the test is unreliable.
**Difficulty to reproduce:** Moderate to difficult. Depending on whether the DOM selector is duplicated, or appears after a delay etc.
+Adding a delay in API or controller could help reproducing the issue.
**Resolution:** It really depends on the problem here. It could be to wait for requests to finish, to scroll down the page etc.
@@ -207,10 +209,10 @@ The `rspec/flaky/report-suite.json` report is:
- [Sporadic RSpec failures due to `PG::UniqueViolation`](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/28307#note_24958837): <https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/9846>
- Follow-up: <https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10688>
- [Capybara.reset_session! should be called before requests are blocked](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/33779): <https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/12224>
-- FFaker generates funky data that tests are not ready to handle (and tests should be predictable so that's bad!):
+- ffaker generates funky data that tests are not ready to handle (and tests should be predictable so that's bad!):
- [Make `spec/mailers/notify_spec.rb` more robust](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/20121): <https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10015>
- [Transient failure in `spec/requests/api/commits_spec.rb`](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/27988#note_25342521): <https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/9944>
- - [Replace FFaker factory data with sequences](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/29643): <https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10184>
+ - [Replace ffaker factory data with sequences](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/29643): <https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10184>
- [Transient failure in spec/finders/issues_finder_spec.rb](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/30211#note_26707685): <https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10404>
### Order-dependent flaky tests
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index 041b0f0a4f4..2fa5fdeab7d 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -90,7 +90,7 @@ function getFahrenheit(celsius) {
}
```
-It does not make sense to test our `getFahrenheit` function because underneath it does nothing else but invoking the library function, and we can expect that one is working as intended. (Simplified, I know)
+It does not make sense to test our `getFahrenheit` function because underneath it does nothing else but invoking the library function, and we can expect that one is working as intended.
Let's take a short look into Vue land. Vue is a critical part of the GitLab JavaScript codebase. When writing specs for Vue components, a common gotcha is to actually end up testing Vue provided functionality, because it appears to be the easiest thing to test. Here's an example taken from our codebase.
@@ -522,7 +522,7 @@ it('waits for an event', () => {
### Ensuring that tests are isolated
-Tests are normally architected in a pattern which requires a recurring setup and breakdown of the component under test. This is done by making use of the `beforeEach` and `afterEach` hooks.
+Tests are normally architected in a pattern which requires a recurring setup of the component under test. This is often achieved by making use of the `beforeEach` hook.
Example
@@ -532,16 +532,22 @@ Example
beforeEach(() => {
wrapper = mount(Component);
});
+```
+
+With [enableAutoDestroy](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100389), it is no longer necessary to manually call `wrapper.destroy()`.
+However, some mocks, spies, and fixtures do need to be torn down, and we can leverage the `afterEach` hook.
+
+Example
+
+```javascript
+ let wrapper;
afterEach(() => {
- wrapper.destroy();
+ fakeApollo = null;
+ store = null;
});
```
-When looking at this initially you'd suspect that the component is setup before each test and then broken down afterwards, providing isolation between tests.
-
-This is however not entirely true as the `destroy` method does not remove everything which has been mutated on the `wrapper` object. For functional components, destroy only removes the rendered DOM elements from the document.
-
### Jest best practices
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34209) in GitLab 13.2.
@@ -713,7 +719,7 @@ unit testing by mocking out modules that cannot be easily consumed in our test e
> Instead, consider using [`jest.mock(..)`](https://jestjs.io/docs/jest-object#jestmockmodulename-factory-options)
> (or a similar mocking function) in the relevant spec file.
-#### Where should I put manual mocks?
+#### Where should you put manual mocks?
Jest supports [manual module mocks](https://jestjs.io/docs/manual-mocks) by placing a mock in a `__mocks__/` directory next to the source module
(for example, `app/assets/javascripts/ide/__mocks__`). **Don't do this.** We want to keep all of our test-related code in one place (the `spec/` folder).
@@ -1159,7 +1165,7 @@ By now you've probably heard of [Jest snapshot tests](https://jestjs.io/docs/sna
To use them within GitLab, there are a few guidelines that should be highlighted:
- Treat snapshots as code
-- Don't think of a snapshot file as a Blackbox
+- Don't think of a snapshot file as a black box
- Care for the output of the snapshot, otherwise, it's not providing any real value. This will usually involve reading the generated snapshot file as you would read any other piece of code
Think of a snapshot test as a simple way to store a raw `String` representation of what you've put into the item being tested. This can be used to evaluate changes in a component, a store, a complex piece of generated output, etc. You can see more in the list below for some recommended `Do's and Don'ts`.
@@ -1169,7 +1175,7 @@ Jest provides a great set of docs on [best practices](https://jestjs.io/docs/sna
### How does a snapshot work?
-A snapshot is purely a stringified version of what you ask to be tested on the lefthand side of the function call. This means any kind of changes you make to the formatting of the string has an impact on the outcome. This process is done by leveraging serializers for an automatic transform step. For Vue this is already taken care of by leveraging the `vue-jest` package, which offers the proper serializer.
+A snapshot is purely a stringified version of what you ask to be tested on the left-hand side of the function call. This means any kind of changes you make to the formatting of the string has an impact on the outcome. This process is done by leveraging serializers for an automatic transform step. For Vue this is already taken care of by leveraging the `vue-jest` package, which offers the proper serializer.
Should the outcome of your spec be different from what is in the generated snapshot file, you'll be notified about it by a failing test in your test suite.
@@ -1448,13 +1454,10 @@ Before executing any page interaction when navigating or making asynchronous cal
#### Elements interaction
-There are a lot of different ways to find and interact with elements. For example, you could use the basic `find` method with the `selector` and `text` parameter and then use the `.click` method
-
-```ruby
- find('.gl-tab-nav-item', text: 'Tests').click
-```
+There are a lot of different ways to find and interact with elements.
+For best practises, refer to the [UI testing](best_practices.md#ui-testing) section.
-Alternatively, you could use `click_button` with a string of text that is found within the button, which is a more semantically meaningful way of clicking the element.
+To click a button, use `click_button` with the string of text found in the button:
```ruby
click_button 'Text inside the button element'
@@ -1474,20 +1477,20 @@ You can use `fill_in` to fill input / form elements. The first argument is the s
Alternatively, you can use the `find` selector paired with `send_keys` to add keys in a field without removing previous text, or `set` which completely replaces the value of the input element.
-All of these are valid selectors and methods. Pick whichever suits your needs and look around as there are many more useful ones!
+You can find a more comprehensive list of actions in the [feature tests actions](best_practices.md#actions) documentation.
#### Assertions
To assert anything in a page, you can always access `page` variable, which is automatically defines and actually means the page document. This means you can expect the `page` to have certain components like selectors or content. Here are a few examples:
```ruby
- # Finding an element by ID
- expect(page).to have_selector('#js-pipeline-graph')
+ # Finding a button
+ expect(page).to have_button('Submit review')
```
```ruby
# Finding by text
- expect(page).to have_content('build')
+ expect(page).to have_text('build')
```
```ruby
@@ -1496,20 +1499,20 @@ To assert anything in a page, you can always access `page` variable, which is au
```
```ruby
- # Finding by CSS selector. This is a last resort.
- # For example, when you cannot add attributes on the desired element.
- expect(page).to have_css('.js-icon-retry')
+ # Find by data-testid
+ # Like CSS selector, this is acceptable when there isn't a specific matcher available.
+ expect(page).to have_css('[data-testid="pipeline-multi-actions-dropdown"]')
```
```ruby
- # Find by data-testid
- # Like CSS selector, this is acceptable when there isn't a specific matcher available.
- expect(page).to have_selector('[data-testid="pipeline-multi-actions-dropdown"]')
+ # Finding by CSS selector. This is a last resort.
+ # For example, when you cannot add attributes on the desired element.
+ expect(page).to have_css('.js-icon-retry')
```
```ruby
# You can combine any of these selectors with `not_to` instead
- expect(page).not_to have_selector('#js-pipeline-graph')
+ expect(page).not_to have_button('Submit review')
```
```ruby
@@ -1529,11 +1532,13 @@ You can also create a sub-block to look into, to:
- Make sure an element is found within the right boundaries.
```ruby
- page.within('#js-pipeline-graph') do
+ page.within('[data-testid="pipeline-multi-actions-dropdown"]') do
...
end
```
+You can find a more comprehensive list of matchers in the [feature tests matchers](best_practices.md#matchers) documentation.
+
#### Feature flags
By default, every feature flag is enabled **regardless of the YAML definition or the flags you've set manually in your GDK**. To test when a feature flag is disabled, you must manually stub the flag, ideally in a `before do` block.
diff --git a/doc/development/testing_guide/img/testing_triangle.png b/doc/development/testing_guide/img/testing_triangle.png
index 7a9a848c2ee..3ac4955eaff 100644
--- a/doc/development/testing_guide/img/testing_triangle.png
+++ b/doc/development/testing_guide/img/testing_triangle.png
Binary files differ
diff --git a/doc/development/understanding_explain_plans.md b/doc/development/understanding_explain_plans.md
deleted file mode 100644
index 72c3df11a96..00000000000
--- a/doc/development/understanding_explain_plans.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/understanding_explain_plans.md'
-remove_date: '2022-11-04'
----
-
-This document was moved to [another location](database/understanding_explain_plans.md).
-
-<!-- This redirect file can be deleted after <2022-11-04>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/uploads/index.md b/doc/development/uploads/index.md
index 41ec71451fb..b5509f5934e 100644
--- a/doc/development/uploads/index.md
+++ b/doc/development/uploads/index.md
@@ -40,21 +40,9 @@ When using object storage, administrators can control how those files are moved
This move can happen in one of these ways:
- [Rails controller upload](#rails-controller-upload).
-- [Background upload](#background-upload).
- [Direct upload](#direct-upload).
-These strategies activate as per the following `<feature>.object_store.*` settings:
-
-| | `background_upload` = `false` | `background_upload` = `true` |
-| ------------------------- | ----------------------------- | ------------------------------- |
-| `direct_upload` = `false` | Controller upload | Background upload |
-| `direct_upload` = `true` | Direct upload | Direct upload (takes precedence)|
-
Individual Sidekiq workers might also store files in object storage, which is not something we cover here.
-More importantly, `background_upload` does not imply _all files are uploaded by Sidekiq._
-Sidekiq workers that store files in object storage could still exist when this setting is `false`.
-Those cases are never user-initiated uploads, but they might occur in response to another user-initiated
-action, such as exporting a GitLab repository.
Finally, Workhorse assists most user-initiated uploads using an upload buffering mechanism to keep slow work out of Rails controllers.
This mechanism is explained in [Workhorse assisted uploads](#workhorse-assisted-uploads),
@@ -98,12 +86,12 @@ GitLab to the object store provider. As mentioned above, there are three differe
this HTTP request is sent.
- [Rails controller upload](#rails-controller-upload).
-- [Background upload](#background-upload).
- [Direct upload](#direct-upload).
+- [Workhorse assisted uploads](#workhorse-assisted-uploads).
### Rails controller upload
-When neither background upload nor direct upload are available, Rails uploads the file to object storage
+When direct upload is not available, Rails uploads the file to object storage
as part of the controller `create` action. Which controller is responsible depends on the kind of file uploaded.
A Rails controller upload is very similar to uploading to local storage. The main difference: Rails must
@@ -115,25 +103,6 @@ keep some of the costly I/O work out of Ruby and Rails. Direct upload does a bet
This strategy is only suitable for small file uploads, as it is subject to Puma's 60 second request timeout.
-### Background upload
-
-WARNING:
-This strategy is deprecated in GitLab 14.9 and later, and is scheduled to [be removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/26600).
-
-With background uploads enabled:
-
-1. Files are uploaded as if they were to reside in local storage.
-1. When Rails saves the upload metadata and the transaction completes, a Sidekiq job is scheduled.
-1. The Sidekiq job transfers the file to the object store bucket.
- - If the job completes, the upload record is updated to reflect the file's new location.
- - If the job fails or gets lost, the upload stays in local storage and has the lifecycle of a normal local storage upload.
-
-As Rails and Sidekiq must cooperate to move the file to its final destination, it requires shared
-storage and as such is unsuitable for CNG installations. We do not use background upload in GitLab SaaS.
-
-As background upload is an extension of local storage, it benefits from the same [Workhorse assistance](#workhorse-assisted-uploads) to
-keep costly I/O work out of Ruby and Rails.
-
### Direct upload
Direct upload is the recommended way to move large files into object storage in CNG installations like GitLab SaaS.
diff --git a/doc/development/uploads/working_with_uploads.md b/doc/development/uploads/working_with_uploads.md
index c88762e6bd5..a3951fb4c7e 100644
--- a/doc/development/uploads/working_with_uploads.md
+++ b/doc/development/uploads/working_with_uploads.md
@@ -8,9 +8,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Recommendations
-- When creating an uploader, [make it a subclass](#where-should-i-store-my-files) of `AttachmentUploader`
+- When creating an uploader, [make it a subclass](#where-should-you-store-your-files) of `AttachmentUploader`
- Add your uploader to the [tables](#tables) in this document
-- Do not add [new object storage buckets](#where-should-i-store-my-files)
+- Do not add [new object storage buckets](#where-should-you-store-your-files)
- Implement [direct upload](#implementing-direct-upload-support)
- If you need to process your uploads, decide [where to do that](#processing-uploads)
@@ -19,14 +19,14 @@ info: To determine the technical writer assigned to the Stage/Group associated w
- [CarrierWave Uploaders](#carrierwave-uploaders)
- [GitLab modifications to CarrierWave](#gitlab-modifications-to-carrierwave)
-## Where should I store my files?
+## Where should you store your files?
CarrierWave Uploaders determine where files get
stored. When you create a new Uploader class you are deciding where to store the files of your new
feature.
First of all, ask yourself if you need a new Uploader class. It is OK
-to use the same Uploader class for different mountpoints or different
+to use the same Uploader class for different mount points or different
models.
If you do want or need your own Uploader class then you should make it
@@ -160,8 +160,8 @@ we modified it.
The central concept of CarrierWave is the **Uploader** class. The
Uploader defines where files get stored, and optionally contains
validation and processing logic. To use an Uploader you must associate
-it with a text column on an ActiveRecord model. This called "mounting"
-and the column is called the "mountpoint". For example:
+it with a text column on an ActiveRecord model. This is called "mounting"
+and the column is called `mountpoint`. For example:
```ruby
class Project < ApplicationRecord
@@ -169,8 +169,8 @@ class Project < ApplicationRecord
end
```
-Now if I upload an avatar called `tanuki.png` the idea is that in the
-`projects.avatar` column for my project, CarrierWave stores the string
+Now if you upload an avatar called `tanuki.png` the idea is that in the
+`projects.avatar` column for your project, CarrierWave stores the string
`tanuki.png`, and that the AttachmentUploader class contains the
configuration data and directory schema. For example if the project ID
is 123, the actual file may be in
@@ -179,12 +179,12 @@ The directory
`/var/opt/gitlab/gitlab-rails/uploads/-/system/project/avatar/123/`
was chosen by the Uploader using among others configuration
(`/var/opt/gitlab/gitlab-rails/uploads`), the model name (`project`),
-the model ID (`123`) and the mountpoint (`avatar`).
+the model ID (`123`) and the mount point (`avatar`).
> The Uploader determines the individual storage directory of your
-> upload. The mountpoint column in your model contains the filename.
+> upload. The `mountpoint` column in your model contains the filename.
-You never access the mountpoint column directly because CarrierWave
+You never access the `mountpoint` column directly because CarrierWave
defines a getter and setter on your model that operates on file handle
objects.
@@ -213,7 +213,7 @@ CarrierWave has 2 storage engines:
|CarrierWave class|GitLab name|Description|
|---|---|---|
-|`CarrierWave::Storage::File`|`ObjectStorage::Store::LOCAL` |Local files, accessed through the Ruby stdlib|
+|`CarrierWave::Storage::File`|`ObjectStorage::Store::LOCAL` |Local files, accessed through the Ruby `stdlib` |
| `CarrierWave::Storage::Fog`|`ObjectStorage::Store::REMOTE`|Cloud files, accessed through the [Fog gem](https://github.com/fog/fog)|
GitLab uses both of these engines, depending on configuration.
@@ -227,8 +227,8 @@ storage engine file by file.
An Uploader is associated with two storage areas: regular storage and
cache storage. Each has its own storage engine. If you assign a file
-to a mountpoint setter (`project.avatar =
-File.open('/tmp/tanuki.png')`) you will copy/move the file to cache
+to a mount point setter (`project.avatar = File.open('/tmp/tanuki.png')`)
+you will copy/move the file to cache
storage as a side effect via the `cache!` method. To persist the file
you must somehow call the `store!` method. This either happens via
[ActiveRecord callbacks](https://github.com/carrierwaveuploader/carrierwave/blob/v1.3.2/lib/carrierwave/orm/activerecord.rb#L55)
diff --git a/doc/development/utilities.md b/doc/development/utilities.md
index 551834670b3..58954101890 100644
--- a/doc/development/utilities.md
+++ b/doc/development/utilities.md
@@ -181,20 +181,6 @@ Refer to [`strong_memoize.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/maste
include Gitlab::Utils::StrongMemoize
def result
- strong_memoize(:result) do
- search
- end
- end
- end
- ```
-
- Alternatively, use the `strong_memoize_attr` helper to memoize the method for you:
-
- ```ruby
- class Find
- include Gitlab::Utils::StrongMemoize
-
- def result
search
end
strong_memoize_attr :result
@@ -206,6 +192,26 @@ Refer to [`strong_memoize.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/maste
end
```
+ Using `strong_memoize_attr` on methods with parameters is not supported.
+ It does not work when combined with [`override`](#override) and might memoize wrong results.
+
+ Use `strong_memoize_with` instead.
+
+ ```ruby
+ # bad
+ def expensive_method(arg)
+ # ...
+ end
+ strong_memoize_attr :expensive_method
+
+ # good
+ def expensive_method(arg)
+ strong_memoize_with(:expensive_method, arg)
+ # ...
+ end
+ end
+ ```
+
There's also `strong_memoize_with` to help memoize methods that take arguments.
This should be used for methods that have a low number of possible values
as arguments or with consistent repeating arguments in a loop.
diff --git a/doc/development/value_stream_analytics.md b/doc/development/value_stream_analytics.md
index 0d321133705..2d5f33b5dae 100644
--- a/doc/development/value_stream_analytics.md
+++ b/doc/development/value_stream_analytics.md
@@ -71,7 +71,7 @@ which are always available to the end-users regardless of the subscription.
### Value streams
Value streams are container objects for the stages. There can be multiple value streams per group
-focusing on different aspects of the Dev Ops lifecycle.
+focusing on different aspects of the DevOps lifecycle.
### Events
@@ -89,7 +89,8 @@ They're responsible for defining a timestamp expression that is used in the calc
#### Implementing an `Event` class
-There are a few methods that are required to be implemented, the `StageEvent` base class describes them in great detail. The most important ones are:
+You must implement a few methods, as described in the `StageEvent` base class.
+The most important methods are:
- `object_type`
- `timestamp_projection`
diff --git a/doc/development/value_stream_analytics/value_stream_analytics_aggregated_backend.md b/doc/development/value_stream_analytics/value_stream_analytics_aggregated_backend.md
index a07998550bf..5bcadc6f39b 100644
--- a/doc/development/value_stream_analytics/value_stream_analytics_aggregated_backend.md
+++ b/doc/development/value_stream_analytics/value_stream_analytics_aggregated_backend.md
@@ -54,9 +54,9 @@ database with a minimal development effort.
### Example configuration
-![vsa object hierarchy example](img/object_hierarchy_example_V14_10.png)
+![VSA object hierarchy example](img/object_hierarchy_example_V14_10.png)
-In this example, there are two independent value streams set up for two teams that are using
+In this example, two independent value streams are set up for two teams that are using
different development workflows within the `Test Group` (top-level namespace).
The first value stream uses standard timestamp-based events for defining the stages. The second
@@ -102,7 +102,7 @@ High-level overview for each top-level namespace with Premium or Ultimate licens
1. `INSERT` or `UPDATE` the data into the VSA database tables.
The data loading is implemented within the [`Analytics::CycleAnalytics::DataLoaderService`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/services/analytics/cycle_analytics/data_loader_service.rb)
-class. There are groups containing a lot of data, so to avoid overloading the primary database,
+class. Some groups contain a lot of data, so to avoid overloading the primary database,
the service performs operations in batches and enforces strict application limits:
- Load records in batches.
diff --git a/doc/development/verifying_database_capabilities.md b/doc/development/verifying_database_capabilities.md
deleted file mode 100644
index 0217eb96e5a..00000000000
--- a/doc/development/verifying_database_capabilities.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'database/verifying_database_capabilities.md'
-remove_date: '2022-11-06'
----
-
-This document was moved to [another location](database/verifying_database_capabilities.md).
-
-<!-- This redirect file can be deleted after <2022-11-06>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/development/work_items.md b/doc/development/work_items.md
index a417e1d1349..90e454bec85 100644
--- a/doc/development/work_items.md
+++ b/doc/development/work_items.md
@@ -54,7 +54,7 @@ To avoid confusion and ensure communication is efficient, we will use the follow
| work item view | The new frontend view that renders work items of any type | | |
| legacy issue view | The existing view used to render issues and incidents | | |
| issue | The existing issue model | | |
-| issuable | Any model currently using the issueable module (issues, epics and MRs) | _Incidents are an **issuable**_ | _Incidents are a **work item type**_ |
+| issuable | Any model currently using the issuable module (issues, epics and MRs) | _Incidents are an **issuable**_ | _Incidents are a **work item type**_ |
| widget | A UI element to present or allow interaction with specific work item data | | |
Some terms have been used in the past but have since become confusing and are now discouraged.
@@ -92,7 +92,7 @@ NOTE:
At first, defining a WIT will only be possible at the root-level group, which would then be inherited by subgroups.
We will investigate the possibility of defining new WITs at subgroup levels at a later iteration.
-### Introducing work_item_types table
+### Introducing `work_item_types` table
For example, suppose there are three root-level groups with IDs: `11`, `12`, and `13`. Also,
assume the following base types: `issue: 0`, `incident: 1`, `test_case: 2`.
@@ -228,7 +228,7 @@ So, migrating epics to a work item type requires providing feature parity betwee
The main missing features are:
-- Get WIs to the group level. This is dependent on [Consolidate Groups and Projects](https://gitlab.com/gitlab-org/architecture/tasks/-/issues/7)
+- Get work items to the group level. This is dependent on [Consolidate Groups and Projects](https://gitlab.com/gitlab-org/architecture/tasks/-/issues/7)
initiative.
- A hierarchy widget: the ability to structure work items into hierarchies.
- Inherited date widget.
diff --git a/doc/development/work_items_widgets.md b/doc/development/work_items_widgets.md
index 89602d969e6..ba15a3f0163 100644
--- a/doc/development/work_items_widgets.md
+++ b/doc/development/work_items_widgets.md
@@ -80,7 +80,7 @@ mutation {
```
-### Widget's responsibility and structure
+### Widget responsibility and structure
A widget is responsible for displaying and updating a single attribute, such as
title, description, or labels. Widgets must support any type of work item.
diff --git a/doc/development/workhorse/configuration.md b/doc/development/workhorse/configuration.md
index 82e44a6f995..9fc106b8f04 100644
--- a/doc/development/workhorse/configuration.md
+++ b/doc/development/workhorse/configuration.md
@@ -162,7 +162,7 @@ addr = "localhost:9229"
max_version = "tls1.3"
```
-## Interaction of authBackend and authSocket
+## Interaction of `authBackend` and `authSocket`
The interaction between `authBackend` and `authSocket` can be confusing.
If `authSocket` is set, it overrides the host portion of `authBackend`, but not
@@ -170,7 +170,7 @@ the relative path.
In table form:
-| authBackend | authSocket | Workhorse connects to | Rails relative URL |
+| `authBackend` | `authSocket` | Workhorse connects to | Rails relative URL |
|--------------------------------|-------------------|-----------------------|--------------------|
| unset | unset | `localhost:8080` | `/` |
| `http://localhost:3000` | unset | `localhost:3000` | `/` |
diff --git a/doc/gitlab-basics/start-using-git.md b/doc/gitlab-basics/start-using-git.md
index 25ef094b2a7..61b06fb106a 100644
--- a/doc/gitlab-basics/start-using-git.md
+++ b/doc/gitlab-basics/start-using-git.md
@@ -172,7 +172,7 @@ add your namespace (username or group) to the path:
Clone with HTTPS using a token if:
- You want to use 2FA.
-- You want to have a revokable set of credentials scoped to one or more repositories.
+- You want to have a revocable set of credentials scoped to one or more repositories.
You can use any of these tokens to authenticate when cloning over HTTPS:
diff --git a/doc/index.md b/doc/index.md
index c359ec7b639..88b8c653aae 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -20,23 +20,16 @@ description: 'Learn how to use and administer GitLab, the most scalable Git-base
# GitLab Docs
-Welcome to [GitLab](https://about.gitlab.com/) documentation.
+Welcome to the GitLab documentation!
-Here you can access the complete documentation for GitLab, the single application for the
-[entire DevOps lifecycle](#the-entire-devops-lifecycle).
-
-## Overview
-
-No matter how you use GitLab, we have documentation for you.
-
-| Essential documentation | Essential documentation |
+| | |
|:------------------------|:------------------------|
-| [**User documentation**](user/index.md)<br>Discover features and concepts for GitLab users. | [**Administrator documentation**](administration/index.md)<br/>Everything GitLab self-managed administrators need to know. |
-| [**Contributing to GitLab**](#contributing-to-gitlab)<br/>At GitLab, everyone can contribute! | [**New to Git and GitLab?**](tutorials/index.md)<br/>We have the resources to get you started. |
-| [**Build an integration with GitLab**](#build-an-integration-with-gitlab)<br/>Consult our integration documentation. | [**Coming to GitLab from another platform?**](#coming-to-gitlab-from-another-platform)<br/>Consult our guides. |
-| [**Install GitLab**](https://about.gitlab.com/install/)<br/>Installation options for different platforms. | [**Customers**](subscriptions/index.md)<br/>Information for new and existing customers. |
-| [**Update GitLab**](update/index.md)<br/>Update your GitLab self-managed instance to the latest version. | [**Reference Architectures**](administration/reference_architectures/index.md)<br/>GitLab reference architectures. |
-| [**GitLab releases**](https://about.gitlab.com/releases/)<br/>What's new in GitLab. | |
+| [**Use GitLab**](user/index.md)<br>Get started with GitLab features and functionality. | [**Administer GitLab**](administration/index.md)<br/>Administer a self-managed GitLab instance. |
+| [**New to Git and GitLab?**](tutorials/index.md)<br/>Start learning about Git and GitLab. | [**Contribute to GitLab development**](#contributing-to-gitlab)<br/>Create new GitLab functionality and documentation. |
+| [**Coming to GitLab from another platform?**](#coming-to-gitlab-from-another-platform)<br/>Learn how to move to GitLab. | [**Build an integration with GitLab**](#build-an-integration-with-gitlab)<br/>Integrate with Jira and other common applications. |
+| [**Choose a subscription**](subscriptions/index.md)<br/>Determine which subscription tier makes sense for you. | [**Install GitLab**](https://about.gitlab.com/install/)<br/>Install GitLab on different platforms. |
+| [**Reference architectures**](administration/reference_architectures/index.md)<br/>View recommended deployments at scale. | [**Update GitLab**](update/index.md)<br/>Update your GitLab self-managed instance to the latest version. |
+| [**GitLab releases**](https://about.gitlab.com/releases/)<br/>See what's new in GitLab. | |
## Popular topics
diff --git a/doc/install/aws/eks_clusters_aws.md b/doc/install/aws/eks_clusters_aws.md
index 03f7cd19ed5..191d0f93382 100644
--- a/doc/install/aws/eks_clusters_aws.md
+++ b/doc/install/aws/eks_clusters_aws.md
@@ -33,7 +33,7 @@ Using `eksctl` enables the following when building an EKS Cluster:
Read more about Amazon EKS architecture quick start guide:
-- [Landing page](https://aws.amazon.com/quickstart/architecture/amazon-eks/)
+- [Landing page](https://aws.amazon.com/solutions/implementations/amazon-eks/)
- [Reference guide](https://aws-quickstart.github.io/quickstart-amazon-eks/)
- [Reference guide deployment steps](https://aws-quickstart.github.io/quickstart-amazon-eks/#_deployment_steps)
- [Reference guide parameter reference](https://aws-quickstart.github.io/quickstart-amazon-eks/#_parameter_reference)
diff --git a/doc/install/aws/gitlab_hybrid_on_aws.md b/doc/install/aws/gitlab_hybrid_on_aws.md
index 7ae4391dde0..ef7d4ac0f69 100644
--- a/doc/install/aws/gitlab_hybrid_on_aws.md
+++ b/doc/install/aws/gitlab_hybrid_on_aws.md
@@ -14,8 +14,8 @@ Amazon provides a managed Kubernetes service offering known as [Amazon Elastic K
## Tested AWS Bill of Materials by reference architecture size
-| GitLab Cloud Native Hybrid Ref Arch | GitLab Baseline Perf Test Results Omnibus on Instances | AWS Bill of Materials (BOM) for CNH | AWS Build Performance Testing Results for [CNH](https://gitlab.com/guided-explorations/aws/implementation-patterns/gitlab-cloud-native-hybrid-on-eks/-/blob/master/gitlab-alliances-testing/5K/5k-QuickStart-ARM-RDS-Redis_v13-12-3-ee_2021-07-23_140128/5k-QuickStart-ARM-RDS-Redis_v13-12-3-ee_2021-07-23_140128_results.txt) | CNH Cost Estimate 3 AZs* |
-| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
+| GitLab Cloud Native Hybrid Ref Arch | GitLab Baseline Performance Test Results Omnibus on Instances | AWS Bill of Materials (BOM) for CNH | AWS Build Performance Testing Results for [CNH](https://gitlab.com/guided-explorations/aws/implementation-patterns/gitlab-cloud-native-hybrid-on-eks/-/blob/master/gitlab-alliances-testing/5K/5k-QuickStart-ARM-RDS-Redis_v13-12-3-ee_2021-07-23_140128/5k-QuickStart-ARM-RDS-Redis_v13-12-3-ee_2021-07-23_140128_results.txt) | CNH Cost Estimate 3 AZs* |
+| ------------------------------------------------------------ | ------------------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| [2K Omnibus](../../administration/reference_architectures/2k_users.md) | [2K Baseline](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/2k) | [2K Cloud Native Hybrid on EKS](#2k-cloud-native-hybrid-on-eks) | GPT Test Results | [1 YR Ec2 Compute Savings + 1 YR RDS & ElastiCache RIs](https://calculator.aws/#/estimate?id=544bcf1162beae6b8130ad257d081cdf9d4504e3)<br />(2 AZ Cost Estimate is in BOM Below) |
| [3K](../../administration/reference_architectures/3k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) | [3k Baseline](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/3k) | [3K Cloud Native Hybrid on EKS](#3k-cloud-native-hybrid-on-eks) | [3K Full Fixed Scale GPT Test Results](https://gitlab.com/guided-explorations/aws/implementation-patterns/gitlab-cloud-native-hybrid-on-eks/-/blob/master/gitlab-alliances-testing/3K/3k-QuickStart-ARM-RDS-Cache_v13-12-3-ee_2021-07-23_124216/3k-QuickStart-ARM-RDS-Cache_v13-12-3-ee_2021-07-23_124216_results.txt)<br /><br />[3K Elastic Auto Scale GPT Test Results](https://gitlab.com/guided-explorations/aws/implementation-patterns/gitlab-cloud-native-hybrid-on-eks/-/blob/master/gitlab-alliances-testing/3K/3k-QuickStart-AutoScale-ARM-RDS-Cache_v13-12-3-ee_2021-07-23_194200/3k-QuickStart-AutoScale-ARM-RDS-Cache_v13-12-3-ee_2021-07-23_194200_results.txt) | [1 YR Ec2 Compute Savings + 1 YR RDS & ElastiCache RIs](https://calculator.aws/#/estimate?id=f1294fec554e21be999711cddcdab9c5e7f83f14)<br />(2 AZ Cost Estimate is in BOM Below) |
| [5K](../../administration/reference_architectures/5k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) | [5k Baseline](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/5k) | [5K Cloud Native Hybrid on EKS](#5k-cloud-native-hybrid-on-eks) | [5K Full Fixed Scale GPT Test Results](https://gitlab.com/guided-explorations/aws/implementation-patterns/gitlab-cloud-native-hybrid-on-eks/-/blob/master/gitlab-alliances-testing/5K/5k-QuickStart-ARM-RDS-Redis_v13-12-3-ee_2021-07-23_140128/5k-QuickStart-ARM-RDS-Redis_v13-12-3-ee_2021-07-23_140128_results.txt)<br /><br />[5K AutoScale from 25% GPT Test Results](https://gitlab.com/guided-explorations/aws/implementation-patterns/gitlab-cloud-native-hybrid-on-eks/-/blob/master/gitlab-alliances-testing/5K/5k-QuickStart-AutoScale-From-25Percent-ARM-RDS-Redis_v13-12-3-ee_2021-07-24_102717/5k-QuickStart-AutoScale-From-25Percent-ARM-RDS-Redis_v13-12-3-ee_2021-07-24_102717_results.txt) | [1 YR Ec2 Compute Savings + 1 YR RDS & ElastiCache RIs](https://calculator.aws/#/estimate?id=330ee43c5b14662db5df6e52b34898d181a09e16) |
@@ -46,7 +46,7 @@ The Beta version deploys Aurora PostgreSQL, but the release version will deploy
| | [AWS Quick Start for GitLab Cloud Native Hybrid on EKS](https://aws-quickstart.github.io/quickstart-eks-gitlab/) | [GitLab Environment Toolkit (GET)](https://gitlab.com/gitlab-org/gitlab-environment-toolkit) |
| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
-| Overview and Vision | [AWS Quick Start](https://aws.amazon.com/quickstart/architecture/amazon-eks/) | [GitLab Environment Toolkit](https://gitlab.com/gitlab-org/gitlab-environment-toolkit/-/blob/main/README.md) |
+| Overview and Vision | [AWS Quick Start](https://aws.amazon.com/solutions/implementations/amazon-eks/) | [GitLab Environment Toolkit](https://gitlab.com/gitlab-org/gitlab-environment-toolkit/-/blob/main/README.md) |
| Licensing | [Open Source (Apache 2.0)](https://github.com/aws-quickstart/quickstart-eks-gitlab/blob/main/LICENSE.txt) | [GitLab Enterprise Edition license](https://gitlab.com/gitlab-org/gitlab-environment-toolkit/-/blob/main/LICENSE) ([GitLab Premium tier](https://gitlab.com/gitlab-org/gitlab-environment-toolkit/-/blob/main/README.md)) |
| GitLab Support | [GitLab Beta Support](../../policy/alpha-beta-support.md#beta-features) | [GitLab GA Support](../../policy/alpha-beta-support.md#generally-available-ga) |
| GitLab Reference Architecture Compliant | Yes | Yes |
@@ -166,12 +166,12 @@ If EKS node autoscaling is employed, it is likely that your average loading will
| Non-Kubernetes Compute | Ref Arch Raw Total | AWS BOM<br />(Directly Usable in AWS Quick Start) | Example Cost<br />US East, 3 AZ | Example Cost<br />US East, 2 AZ |
| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------- | ------------------------------- | ------------------------------- |
-| **Bastion Host (Quick Start)** | 1 HA instance in ASG | **t2.micro** for prod, **m4.2xlarge** for perf. testing | | |
+| **Bastion Host (Quick Start)** | 1 HA instance in ASG | **t2.micro** for prod, **m4.2xlarge** for performance testing | | |
| **PostgreSQL**<br />AWS Amazon RDS PostgreSQL Nodes Configuration (GPT tested) | 2vCPU, 7.5 GB<br />Tested with Graviton ARM | **db.r6g.large** x 3 nodes <br />(6vCPU, 48 GB) | 3 nodes x $0.26 = $0.78/hr | 3 nodes x $0.26 = $0.78/hr |
| **Redis** | 1vCPU, 3.75GB<br />(across 12 nodes for Redis Cache, Redis Queues/Shared State, Sentinel Cache, Sentinel Queues/Shared State) | **cache.m6g.large** x 3 nodes<br />(6vCPU, 19GB) | 3 nodes x $0.15 = $0.45/hr | 2 nodes x $0.15 = $0.30/hr |
| **<u>Gitaly Cluster</u>** [Details](gitlab_sre_for_aws.md#gitaly-sre-considerations) | [Gitaly & Praefect Must Have an Uneven Node Count for HA](gitlab_sre_for_aws.md#gitaly-and-praefect-elections) | | | |
| Gitaly Instances (in ASG) | 12 vCPU, 45GB<br />(across 3 nodes) | **m5.xlarge** x 3 nodes<br />(48 vCPU, 180 GB) | $0.192 x 3 = $0.58/hr | $0.192 x 3 = $0.58/hr |
-| | The GitLab Reference architecture for 2K is not Highly Available and therefore has a single Gitaly no Praefect. AWS Quick Starts MUST be HA, so it implements Prafect from the 3K Ref Architecture to meet that requirement | | | |
+| | The GitLab Reference architecture for 2K is not Highly Available and therefore has a single Gitaly no Praefect. AWS Quick Starts MUST be HA, so it implements Praefect from the 3K Ref Architecture to meet that requirement | | | |
| Praefect (Instances in ASG with load balancer) | 6 vCPU, 10 GB<br />([across 3 nodes](gitlab_sre_for_aws.md#gitaly-and-praefect-elections)) | **c5.large** x 3 nodes<br />(6 vCPU, 12 GB) | $0.09 x 3 = $0.21/hr | $0.09 x 3 = $0.21/hr |
| Praefect PostgreSQL(1) (AWS RDS) | 6 vCPU, 5.4 GB<br />([across 3 nodes](gitlab_sre_for_aws.md#gitaly-and-praefect-elections)) | Not applicable; reuses GitLab PostgreSQL | $0 | $0 |
| Internal Load Balancing Node | 2 vCPU, 1.8 GB | AWS ELB | $0.10/hr | $0.10/hr |
@@ -221,7 +221,7 @@ If EKS node autoscaling is employed, it is likely that your average loading will
| Non-Kubernetes Compute | Ref Arch Raw Total | AWS BOM<br />(Directly Usable in AWS Quick Start) | Example Cost<br />US East, 3 AZ | Example Cost<br />US East, 2 AZ |
| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------- | ------------------------------- | ------------------------------------------------------------ |
-| **Bastion Host (Quick Start)** | 1 HA instance in ASG | **t2.micro** for prod, **m4.2xlarge** for perf. testing | | |
+| **Bastion Host (Quick Start)** | 1 HA instance in ASG | **t2.micro** for prod, **m4.2xlarge** for performance testing | | |
| **PostgreSQL**<br />Amazon RDS PostgreSQL Nodes Configuration (GPT tested) | 18vCPU, 36 GB <br />(across 9 nodes for PostgreSQL, PgBouncer, Consul)<br />Tested with Graviton ARM | **db.r6g.xlarge** x 3 nodes <br />(12vCPU, 96 GB) | 3 nodes x $0.52 = $1.56/hr | 3 nodes x $0.52 = $1.56/hr |
| **Redis** | 6vCPU, 18GB<br />(across 6 nodes for Redis Cache, Sentinel) | **cache.m6g.large** x 3 nodes<br />(6vCPU, 19GB) | 3 nodes x $0.15 = $0.45/hr | 2 nodes x $0.15 = $0.30/hr |
| **<u>Gitaly Cluster</u>** [Details](gitlab_sre_for_aws.md#gitaly-sre-considerations) | | | | |
@@ -275,7 +275,7 @@ If EKS node autoscaling is employed, it is likely that your average loading will
| Non-Kubernetes Compute | Ref Arch Raw Total | AWS BOM<br />(Directly Usable in AWS Quick Start) | Example Cost<br />US East, 3 AZ | Example Cost<br />US East, 2 AZ |
| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------- | ------------------------------- | ------------------------------------------------------------ |
-| **Bastion Host (Quick Start)** | 1 HA instance in ASG | **t2.micro** for prod, **m4.2xlarge** for perf. testing | | |
+| **Bastion Host (Quick Start)** | 1 HA instance in ASG | **t2.micro** for prod, **m4.2xlarge** for performance testing | | |
| **PostgreSQL**<br />Amazon RDS PostgreSQL Nodes Configuration (GPT tested) | 21vCPU, 51 GB <br />(across 9 nodes for PostgreSQL, PgBouncer, Consul)<br />Tested with Graviton ARM | **db.r6g.2xlarge** x 3 nodes <br />(24vCPU, 192 GB) | 3 nodes x $1.04 = $3.12/hr | 3 nodes x $1.04 = $3.12/hr |
| **Redis** | 9vCPU, 27GB<br />(across 6 nodes for Redis, Sentinel) | **cache.m6g.xlarge** x 3 nodes<br />(12vCPU, 39GB) | 3 nodes x $0.30 = $0.90/hr | 2 nodes x $0.30 = $0.60/hr |
| **<u>Gitaly Cluster</u>** [Details](gitlab_sre_for_aws.md#gitaly-sre-considerations) | | | | |
@@ -328,7 +328,7 @@ If EKS node autoscaling is employed, it is likely that your average loading will
| Non-Kubernetes Compute | Ref Arch Raw Total | AWS BOM | Example Cost<br />US East, 3 AZ | Example Cost<br />US East, 2 AZ |
| ------------------------------------------------------------ | ------------------------------ | ------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
-| **Bastion Host (Quick Start)** | 1 HA instance in ASG | **t2.micro** for prod, **m4.2xlarge** for perf. testing | | |
+| **Bastion Host (Quick Start)** | 1 HA instance in ASG | **t2.micro** for prod, **m4.2xlarge** for performance testing | | |
| **PostgreSQL**<br />Amazon RDS PostgreSQL Nodes Configuration (GPT tested) | 36vCPU, 102 GB <br />(across 9 nodes for PostgreSQL, PgBouncer, Consul) | **db.r6g.2xlarge** x 3 nodes <br />(24vCPU, 192 GB) | 3 nodes x $1.04 = $3.12/hr | 3 nodes x $1.04 = $3.12/hr |
| **Redis** | 30vCPU, 114GB<br />(across 12 nodes for Redis Cache, Redis Queues/Shared State, Sentinel Cache, Sentinel Queues/Shared State) | **cache.m5.2xlarge** x 3 nodes<br />(24vCPU, 78GB) | 3 nodes x $0.62 = $1.86/hr | 2 nodes x $0.62 = $1.24/hr |
| **<u>Gitaly Cluster</u>** [Details](gitlab_sre_for_aws.md#gitaly-sre-considerations) | | | | |
@@ -381,7 +381,7 @@ If EKS node autoscaling is employed, it is likely that your average loading will
| Non-Kubernetes Compute | Ref Arch Raw Total | AWS BOM | Example Cost<br />US East, 3 AZ | Example Cost<br />US East, 2 AZ |
| ------------------------------------------------------------ | ------------------------------------------------------------ | --------------------------------------------------------- | ------------------------------- | ------------------------------------------------------------ |
-| **Bastion Host (Quick Start)** | 1 HA instance in ASG | **t2.micro** for prod, **m4.2xlarge** for perf. testing | | |
+| **Bastion Host (Quick Start)** | 1 HA instance in ASG | **t2.micro** for prod, **m4.2xlarge** for performance testing | | |
| **PostgreSQL**<br />Amazon RDS PostgreSQL Nodes Configuration (GPT tested) | 96vCPU, 360 GB <br />(across 3 nodes) | **db.r6g.8xlarge** x 3 nodes <br />(96vCPU, 768 GB total) | 3 nodes x $4.15 = $12.45/hr | 3 nodes x $4.15 = $12.45/hr |
| **Redis** | 30vCPU, 114GB<br />(across 12 nodes for Redis Cache, Redis Queues/Shared State, Sentinel Cache, Sentinel Queues/Shared State) | **cache.m6g.2xlarge** x 3 nodes<br />(24vCPU, 78GB total) | 3 nodes x $0.60 = $1.80/hr | 2 nodes x $0.60 = $1.20/hr |
| **<u>Gitaly Cluster</u>** [Details](gitlab_sre_for_aws.md#gitaly-sre-considerations) | | | | |
diff --git a/doc/install/aws/gitlab_sre_for_aws.md b/doc/install/aws/gitlab_sre_for_aws.md
index c55ce993cfc..e68aea00b36 100644
--- a/doc/install/aws/gitlab_sre_for_aws.md
+++ b/doc/install/aws/gitlab_sre_for_aws.md
@@ -56,7 +56,7 @@ All recommendations are for production configurations, including performance tes
- Use only SSD storage and the [class of Elastic Block Store (EBS) storage](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html) that suites your durability and speed requirements.
- When not using provisioned EBS IO, EBS volume size determines the I/O level, so provisioning volumes that are much larger than needed can be the least expensive way to improve EBS IO.
-- If Gitaly performance monitoring shows signs of disk stress then one of the provisioned IOPs levels can be chosen. EBS IOPs levels also have enhanced durability which may be appealing for some implementations aside from performance considerations.
+- If Gitaly performance monitoring shows signs of disk stress then one of the provisioned IOPS levels can be chosen. EBS IOPS levels also have enhanced durability which may be appealing for some implementations aside from performance considerations.
**To accommodate:**
diff --git a/doc/install/aws/index.md b/doc/install/aws/index.md
index 93a966b69fb..05db439ffcd 100644
--- a/doc/install/aws/index.md
+++ b/doc/install/aws/index.md
@@ -70,7 +70,7 @@ Because any given GitLab upgrade might involve data disk updates or database sch
- [GitLab Enterprise Edition](https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#Images:visibility=public-images;ownerAlias=782774275127;search=GitLab%20EE;sort=desc:name): If you want to unlock the enterprise features, a license is needed.
- [GitLab Community Edition](https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#Images:visibility=public-images;ownerAlias=782774275127;search=GitLab%20CE;sort=desc:name): The open source version of GitLab.
- - [GitLab Premium or Ultimate Marketplace (Prelicensed)](https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#Images:visibility=public-images;source=Marketplace;search=GitLab%20EE;sort=desc:name): 5 user license built into per-minute billing.
+ - [GitLab Premium or Ultimate Marketplace (pre-licensed)](https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#Images:visibility=public-images;source=Marketplace;search=GitLab%20EE;sort=desc:name): 5 user license built into per-minute billing.
1. AMI IDs are unique per region, so after you've loaded one of the above, select the desired target region in the upper right of the console to see the appropriate AMIs.
1. After the console is loaded, you can add additional search criteria to narrow further. For instance, type `13.` to find only 13.x versions.
@@ -104,7 +104,7 @@ Implementation patterns enable platform-specific terminology, best practice arch
### Platform as a Service (PaaS) specification and usage
-Platform as a Service options are a huge portion of the value provided by Cloud Platforms as they simplify operational complexity and reduce the SRE and security skilling required to operate advanced, highly available technology services. Implementation patterns can be prequalified against the partner PaaS options.
+Platform as a Service options are a huge portion of the value provided by Cloud Platforms as they simplify operational complexity and reduce the SRE and security skilling required to operate advanced, highly available technology services. Implementation patterns can be pre-qualified against the partner PaaS options.
- Implementation patterns help implementers understand what PaaS options are known to work and how to choose between PaaS solutions when a single platform has more than one PaaS option for the same GitLab role.
- For instance, where reference architectures do not have a specific recommendation on what technology is leveraged for GitLab outbound email services or what the sizing should be - a Reference Implementation may advise using a cloud providers Email as a Service (PaaS) and possibly even with specific settings.
diff --git a/doc/install/docker.md b/doc/install/docker.md
index 11525842c6e..2464f43dc23 100644
--- a/doc/install/docker.md
+++ b/doc/install/docker.md
@@ -263,7 +263,7 @@ Here's an example that deploys GitLab with four runners as a [stack](https://doc
1. Create a `root_password.txt` file:
```plaintext
- MySuperSecretAndSecurePass0rd!
+ MySuperSecretAndSecurePassw0rd!
```
1. Make sure you are in the same directory as `docker-compose.yml` and run:
@@ -407,8 +407,8 @@ port `2289`:
```
NOTE:
- The format for publishing ports is `hostPort:containerPort`. Read more in
- Docker's documentation about
+ The format for publishing ports is `hostPort:containerPort`. Read more in the
+ Docker documentation about
[exposing incoming ports](https://docs.docker.com/engine/reference/run/#/expose-incoming-ports).
1. Enter the running container:
@@ -720,7 +720,7 @@ purpose.
### Docker containers exhausts space due to the `json-file`
-Docker's [default logging driver is `json-file`](https://docs.docker.com/config/containers/logging/configure/#configure-the-default-logging-driver), which performs no log rotation by default. As a result of this lack of rotation, log files stored by the `json-file` driver can consume a significant amount of disk space for containers that generate a lot of output. This can lead to disk space exhaustion. To address this, use [`journald`](https://docs.docker.com/config/containers/logging/journald/) as the logging driver when available, or [another supported driver](https://docs.docker.com/config/containers/logging/configure/#supported-logging-drivers) with native rotation support.
+Docker uses the [`json-file` default logging driver](https://docs.docker.com/config/containers/logging/configure/#configure-the-default-logging-driver), which performs no log rotation by default. As a result of this lack of rotation, log files stored by the `json-file` driver can consume a significant amount of disk space for containers that generate a lot of output. This can lead to disk space exhaustion. To address this, use [`journald`](https://docs.docker.com/config/containers/logging/journald/) as the logging driver when available, or [another supported driver](https://docs.docker.com/config/containers/logging/configure/#supported-logging-drivers) with native rotation support.
### Buffer overflow error when starting Docker
diff --git a/doc/install/installation.md b/doc/install/installation.md
index c00a959e037..1191b32181e 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -32,7 +32,7 @@ Because an installation from source is a lot of work and error prone we strongly
One reason the Omnibus package is more reliable is its use of runit to restart any of the GitLab processes in case one crashes.
On heavily used GitLab instances the memory usage of the Sidekiq background worker grows over time.
-Omnibus packages solve this by [letting the Sidekiq terminate gracefully](../administration/operations/sidekiq_memory_killer.md) if it uses too much memory.
+Omnibus packages solve this by [letting the Sidekiq terminate gracefully](../administration/sidekiq/sidekiq_memory_killer.md) if it uses too much memory.
After this termination runit detects Sidekiq is not running and starts it.
Because installations from source don't use runit for process supervision, Sidekiq
can't be terminated and its memory usage grows over time.
@@ -47,11 +47,11 @@ If the highest number stable branch is unclear, check the [GitLab blog](https://
## Software requirements
| Software | Minimum version | Notes |
-| ------------------ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| ------------------ | --------------- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Ruby](#2-ruby) | `2.7` | From GitLab 13.6, Ruby 2.7 is required. Ruby 3.0 is not supported yet (see [the relevant epic](https://gitlab.com/groups/gitlab-org/-/epics/5149) for the current status). You must use the standard MRI implementation of Ruby. We love [JRuby](https://www.jruby.org/) and [Rubinius](https://github.com/rubinius/rubinius#the-rubinius-language-platform), but GitLab needs several Gems that have native extensions. |
| [Go](#3-go) | `1.18` | From GitLab 15.6, Go 1.18 or later is required. |
| [Git](#git) | `2.37.x` | From GitLab 15.6, Git 2.37.x and later is required. It's highly recommended that you use the [Git version provided by Gitaly](#git). |
-| [Node.js](#4-node) | `14.15.0` | GitLab uses [webpack](https://webpack.js.org/) to compile frontend assets. Node.js 16.x is recommended, as it's faster. You can check which version you're running with `node -v`. You must update it to a newer version if needed. |
+| [Node.js](#4-node) | `16.15.0` | From GitLab 15.7, Node.js 16.15.0 or later is required. |
## GitLab directory structure
@@ -263,7 +263,8 @@ GitLab requires the use of Node to compile JavaScript
assets, and Yarn to manage JavaScript dependencies. The current minimum
requirements for these are:
-- `node` >= v14.15.0. (We recommend node 16.x as it is faster)
+- `node` 16.x releases (v16.15.0 or later).
+ [Other LTS versions of Node.js](https://github.com/nodejs/release#release-schedule) might be able to build assets, but we only guarantee Node.js 16.x.
- `yarn` = v1.22.x (Yarn 2 is not supported yet)
In many distributions,
@@ -616,6 +617,7 @@ Install the gems (if you want to use Kerberos for user authentication, omit
```shell
sudo -u git -H bundle config set --local deployment 'true'
sudo -u git -H bundle config set --local without 'development test mysql aws kerberos'
+sudo -u git -H bundle config path /home/git/gitlab/vendor/bundle
sudo -u git -H bundle install
```
diff --git a/doc/install/openshift_and_gitlab/index.md b/doc/install/openshift_and_gitlab/index.md
index a620540369f..6086716be1c 100644
--- a/doc/install/openshift_and_gitlab/index.md
+++ b/doc/install/openshift_and_gitlab/index.md
@@ -29,11 +29,11 @@ The GitLab Operator does not include the GitLab Runner. To install and manage a
## Unsupported GitLab features
-### Secure and Protect
+### Secure
-- License Compliance
-- Code Quality scanning
-- Cluster Image Scanning
+- [License Compliance](../../user/compliance/license_compliance/index.md)
+- [Code Quality scanning](../../ci/testing/code_quality.md)
+- [Operational Container Scanning](../../user/clusters/agent/vulnerabilities.md) (Note: Pipeline [Container Scanning](../../user/application_security/container_scanning/index.md) is supported)
### Docker-in-Docker
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index f581a1c50f9..7fcb371c178 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -77,8 +77,7 @@ process, such as PostgreSQL, which can have disastrous consequences.
PostgreSQL is the only supported database, which is bundled with the Omnibus GitLab package.
You can also use an [external PostgreSQL database](https://docs.gitlab.com/omnibus/settings/database.html#using-a-non-packaged-postgresql-database-management-server).
-Support for MySQL was removed in GitLab 12.1. Existing users using GitLab with
-MySQL/MariaDB are advised to [migrate to PostgreSQL](../update/mysql_to_postgresql.md) before upgrading.
+Support for MySQL was removed in [GitLab 12.1](../update/index.md#1210).
### PostgreSQL Requirements
diff --git a/doc/integration/advanced_search/elasticsearch.md b/doc/integration/advanced_search/elasticsearch.md
index 94493aa6958..a55a56b30d6 100644
--- a/doc/integration/advanced_search/elasticsearch.md
+++ b/doc/integration/advanced_search/elasticsearch.md
@@ -46,13 +46,7 @@ If you are using a compatible version and after connecting to OpenSearch, you ge
Elasticsearch requires additional resources to those documented in the
[GitLab system requirements](../../install/requirements.md).
-Memory, CPU, and storage resource amounts vary depending on the amount of data you index into the Elasticsearch cluster. Heavily used Elasticsearch clusters may require more resources. According to
-[Elasticsearch official guidelines](https://www.elastic.co/guide/en/elasticsearch/guide/current/hardware.html#_memory),
-each node should have:
-
-- [Memory](https://www.elastic.co/guide/en/elasticsearch/guide/current/hardware.html#_memory): 8 GiB (minimum).
-- [CPU](https://www.elastic.co/guide/en/elasticsearch/guide/current/hardware.html#_cpus): Modern processor with multiple cores. GitLab has minimal CPU requirements for Elasticsearch. Multiple cores provide extra concurrency, which is more beneficial than faster CPUs.
-- [Storage](https://www.elastic.co/guide/en/elasticsearch/guide/current/hardware.html#_disks): Use SSD storage. The total storage size of all Elasticsearch nodes is about 50% of the total size of your Git repositories. It includes one primary and one replica. The [`estimate_cluster_size`](#gitlab-advanced-search-rake-tasks) Rake task ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/221177) in GitLab 13.10) uses total repository size to estimate the Advanced Search storage requirements.
+Memory, CPU, and storage resource amounts vary depending on the amount of data you index into the Elasticsearch cluster. Heavily used Elasticsearch clusters may require more resources. The [`estimate_cluster_size`](#gitlab-advanced-search-rake-tasks) Rake task ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/221177) in GitLab 13.10) uses the total repository size to estimate the Advanced Search storage requirements.
## Install Elasticsearch
@@ -239,6 +233,85 @@ Sidekiq performance. Return them to their default values if you see increased `s
in your Sidekiq logs. For more information, see
[issue 322147](https://gitlab.com/gitlab-org/gitlab/-/issues/322147).
+### Access requirements for self-managed AWS OpenSearch Service using fine-grained access control
+
+To use the self-managed AWS OpenSearch Service with GitLab using fine-grained access control, try one of the
+[recommended configurations](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/fgac.html#fgac-recommendations).
+
+Configure your instance's domain access policies to allow `es:ESHttp*` actions. You can customize
+the following example configuration to limit principals or resources.
+See [Identity and Access Management in Amazon OpenSearch Service](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/ac.html) for details.
+
+```json
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": [
+ "*"
+ ]
+ },
+ "Action": [
+ "es:ESHttp*"
+ ],
+ "Resource": "domain-arn/*"
+ }
+ ]
+}
+```
+
+#### Connecting with a master user in the internal database
+
+When using fine-grained access control with a user in the internal database, you should use HTTP basic
+authentication to connect to OpenSearch. You can provide the master username and password as part of the
+OpenSearch URL or in the **Username** and **Password** text boxes in the Advanced Search settings. See
+[Tutorial: Internal user database and HTTP basic authentication](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/fgac-walkthrough-basic.html) for details.
+
+#### Connecting with an IAM user
+
+When using fine-grained access control with IAM credentials, you can provide the credentials in the **AWS OpenSearch IAM credentials** section in the Advanced Search settings.
+
+#### Permissions for fine-grained access control
+
+The following permissions are required for Advanced Search. See [Creating roles](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/fgac.html#fgac-roles) for details.
+
+```json
+{
+ "cluster_permissions": [
+ "cluster_composite_ops",
+ "cluster_monitor"
+ ],
+ "index_permissions": [
+ {
+ "index_patterns": [
+ "gitlab*"
+ ],
+ "allowed_actions": [
+ "data_access",
+ "manage_aliases",
+ "search",
+ "create_index",
+ "delete",
+ "manage"
+ ]
+ },
+ {
+ "index_patterns": [
+ "*"
+ ],
+ "allowed_actions": [
+ "indices:admin/aliases/get",
+ "indices:monitor/stats"
+ ]
+ }
+ ]
+}
+```
+
+The index pattern `*` requires a few permissions for Advanced Search to work.
+
### Limit the number of namespaces and projects that can be indexed
If you check checkbox `Limit the number of namespaces and projects that can be indexed`
@@ -486,7 +559,7 @@ The following are some available Rake tasks:
| Task | Description |
|:--------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| [`sudo gitlab-rake gitlab:elastic:info`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Outputs debugging information for the Advanced Search intergation. |
+| [`sudo gitlab-rake gitlab:elastic:info`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Outputs debugging information for the Advanced Search integration. |
| [`sudo gitlab-rake gitlab:elastic:index`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Enables Elasticsearch indexing and run `gitlab:elastic:create_empty_index`, `gitlab:elastic:clear_index_status`, `gitlab:elastic:index_projects`, and `gitlab:elastic:index_snippets`. |
| [`sudo gitlab-rake gitlab:elastic:pause_indexing`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Pauses Elasticsearch indexing. Changes are still tracked. Useful for cluster/index migrations. |
| [`sudo gitlab-rake gitlab:elastic:resume_indexing`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Resumes Elasticsearch indexing. |
@@ -783,8 +856,8 @@ additional process dedicated to indexing a set of queues (or queue group). This
ensure that indexing queues always have a dedicated worker, while the rest of the queues have
another dedicated worker to avoid contention.
-For this purpose, use the [queue selector](../../administration/sidekiq/extra_sidekiq_processes.md#queue-selector)
-option that allows a more general selection of queue groups using a [worker matching query](../../administration/sidekiq/extra_sidekiq_routing.md#worker-matching-query).
+For this purpose, use the [queue selectors](../../administration/sidekiq/processing_specific_job_classes.md#queue-selectors)
+option that allows a more general selection of queue groups using a [worker matching query](../../administration/sidekiq/processing_specific_job_classes.md#worker-matching-query).
To handle these two queue groups, we generally recommend one of the following two options. You can either:
@@ -804,8 +877,8 @@ To create both an indexing and a non-indexing Sidekiq process in one node:
```ruby
sidekiq['enable'] = true
- sidekiq['queue_selector'] = true
- sidekiq['queue_groups'] = [
+ sidekiq['queue_selector'] = true
+ sidekiq['queue_groups'] = [
"feature_category=global_search",
"feature_category!=global_search"
]
diff --git a/doc/integration/advanced_search/elasticsearch_troubleshooting.md b/doc/integration/advanced_search/elasticsearch_troubleshooting.md
index aa6613d6f1a..7e2edf10c90 100644
--- a/doc/integration/advanced_search/elasticsearch_troubleshooting.md
+++ b/doc/integration/advanced_search/elasticsearch_troubleshooting.md
@@ -96,7 +96,7 @@ Here are some common pitfalls and how to overcome them.
a Lucene index.
- **Replicas**: Failover mechanisms that duplicate indices.
-## How can I verify that my GitLab instance is using Elasticsearch?
+## How can you verify that your GitLab instance is using Elasticsearch?
There are a couple of ways to achieve that:
@@ -184,13 +184,13 @@ If reindexing the project shows:
- Elasticsearch errors or doesn't present any errors at all, reach out to your
Elasticsearch administrator to check the instance.
-### I updated GitLab and now I can't find anything
+### You updated GitLab and now you can't find anything
We continuously make updates to our indexing strategies and aim to support
newer versions of Elasticsearch. When indexing changes are made, it may
be necessary for you to [reindex](elasticsearch.md#zero-downtime-reindexing) after updating GitLab.
-### I indexed all the repositories but I can't get any hits for my search term in the UI
+### You indexed all the repositories but you can't get any hits for your search term in the UI
Make sure you [indexed all the database data](elasticsearch.md#enable-advanced-search).
@@ -220,7 +220,7 @@ The above instructions are not to be used for scenarios that only index a [subse
See [Elasticsearch Index Scopes](elasticsearch.md#advanced-search-index-scopes) for more information on searching for specific types of data.
-### I indexed all the repositories but then switched Elasticsearch servers and now I can't find anything
+### You indexed all the repositories but then switched Elasticsearch servers and now you can't find anything
You must re-run all the Rake tasks to reindex the database, repositories, and wikis.
@@ -228,11 +228,11 @@ You must re-run all the Rake tasks to reindex the database, repositories, and wi
The more data present in your GitLab instance, the longer the indexing process takes.
-### There are some projects that weren't indexed, but I don't know which ones
+### There are some projects that weren't indexed, but you don't know which ones
You can run `sudo gitlab-rake gitlab:elastic:projects_not_indexed` to display projects that aren't indexed.
-### No new data is added to the Elasticsearch index when I push code
+### No new data is added to the Elasticsearch index when you push code
NOTE:
This was [fixed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35936) in GitLab 13.2 and the Rake task is not available for versions greater than that.
@@ -248,7 +248,7 @@ sudo gitlab-rake gitlab:elastic:clear_locked_projects
If `ElasticCommitIndexerWorker` Sidekiq workers are failing with this error during indexing, it usually means that Elasticsearch is unable to keep up with the concurrency of indexing request. To address change the following settings:
- To decrease the indexing throughput you can decrease `Bulk request concurrency` (see [Advanced Search settings](elasticsearch.md#advanced-search-configuration)). This is set to `10` by default, but you change it to as low as 1 to reduce the number of concurrent indexing operations.
-- If changing `Bulk request concurrency` didn't help, you can use the [queue selector](../../administration/sidekiq/extra_sidekiq_processes.md#queue-selector) option to [limit indexing jobs only to specific Sidekiq nodes](elasticsearch.md#index-large-instances-with-dedicated-sidekiq-nodes-or-processes), which should reduce the number of indexing requests.
+- If changing `Bulk request concurrency` didn't help, you can use the [queue selector](../../administration/sidekiq/processing_specific_job_classes.md#queue-selectors) option to [limit indexing jobs only to specific Sidekiq nodes](elasticsearch.md#index-large-instances-with-dedicated-sidekiq-nodes-or-processes), which should reduce the number of indexing requests.
### Indexing is very slow or fails with `rejected execution of coordinating operation` messages
@@ -447,24 +447,9 @@ however, searches only surface results that can be viewed by the user.
Advanced Search honors all permission checks in the application by
filtering out projects that a user does not have access to at search time.
-## Access requirements for the self-managed AWS OpenSearch Service
+### Role mapping when using fine-grained access control with AWS Elasticsearch or OpenSearch
-To use the self-managed AWS OpenSearch Service with GitLab, configure your instance's domain access policies
-to contain the actions below.
-See [Identity and Access Management in Amazon OpenSearch Service](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/ac.html) for details.
-
-```plaintext
-es:ESHttpDelete
-es:ESHttpGet
-es:ESHttpHead
-es:ESHttpPost
-es:ESHttpPut
-es:ESHttpPatch
-```
-
-## Role-mapping when using AWS Elasticsearch or AWS OpenSearch fine-grained access control
-
-When using fine-grained access control with an IAM role, you might encounter the following error:
+When using fine-grained access control with an IAM role or a role created using OpenSearch Dashboards, you might encounter the following error:
```plaintext
{"error":{"root_cause":[{"type":"security_exception","reason":"no permissions for [indices:data/write/bulk] and User [name=arn:aws:iam::xxx:role/INSERT_ROLE_NAME_HERE, backend_roles=[arn:aws:iam::xxx:role/INSERT_ROLE_NAME_HERE], requestedTenant=null]"}],"type":"security_exception","reason":"no permissions for [indices:data/write/bulk] and User [name=arn:aws:iam::xxx:role/INSERT_ROLE_NAME_HERE, backend_roles=[arn:aws:iam::xxx:role/INSERT_ROLE_NAME_HERE], requestedTenant=null]"},"status":403}
diff --git a/doc/integration/arkose.md b/doc/integration/arkose.md
index aa27e3ba4a4..09a7defcff8 100644
--- a/doc/integration/arkose.md
+++ b/doc/integration/arkose.md
@@ -35,8 +35,8 @@ improve their risk prediction model.
NOTE:
Enabling the `arkose_labs_prevent_login` feature flag results in sessions with a `High` risk
-score being denied access. So far, we have kept this feature flag disabled to evaluate Arkose
-Protect's predictions and to make sure we are not preventing legitimate users from signing in.
+score being denied access. So far, we have kept this feature flag disabled to evaluate Arkose Protect
+predictions and to make sure we are not preventing legitimate users from signing in.
That said, we have seen that interactive challenges are effective in preventing some malicious
sign-in attempts as not completing them prevents attackers from moving on to the next sign-in step.
diff --git a/doc/integration/datadog.md b/doc/integration/datadog.md
index 31e254658c1..1f20bccf083 100644
--- a/doc/integration/datadog.md
+++ b/doc/integration/datadog.md
@@ -38,9 +38,11 @@ project, group, or instance level:
Used only in advanced scenarios.
1. Optional. If you use more than one GitLab instance, provide a unique **Service** name
to differentiate between your GitLab instances.
+<!-- vale gitlab.Spelling = NO -->
1. Optional. If you use groups of GitLab instances (such as staging and production
environments), provide an **Env** name. This value is attached to each span
the integration generates.
+<!-- vale gitlab.Spelling = YES -->
1. Optional. To define any custom tags for all spans at which the integration is being configured,
enter one tag per line in **Tags**. Each line must be in the format `key:value`. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79665) in GitLab 14.8.)
1. Optional. Select **Test settings** to test your integration.
@@ -51,4 +53,4 @@ section of your Datadog account.
## Related topics
-- [Datadog's CI Visibility](https://docs.datadoghq.com/continuous_integration/) documentation.
+- [Datadog CI Visibility](https://docs.datadoghq.com/continuous_integration/) documentation.
diff --git a/doc/integration/glab/img/glabgettingstarted.gif b/doc/integration/glab/img/glabgettingstarted.gif
new file mode 100644
index 00000000000..cf335294e41
--- /dev/null
+++ b/doc/integration/glab/img/glabgettingstarted.gif
Binary files differ
diff --git a/doc/integration/glab/index.md b/doc/integration/glab/index.md
new file mode 100644
index 00000000000..3951f38dfab
--- /dev/null
+++ b/doc/integration/glab/index.md
@@ -0,0 +1,80 @@
+---
+stage: Create
+group: Code Review
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# GitLab CLI - `glab`
+
+GLab is an open source GitLab CLI tool. It brings GitLab to your terminal:
+next to where you are already working with Git and your code, without
+switching between windows and browser tabs.
+
+- Work with issues.
+- Work with merge requests.
+- Watch running pipelines directly from your CLI.
+
+![command example](img/glabgettingstarted.gif)
+
+The GitLab CLI uses commands structured like `glab <command> <subcommand> [flags]`
+to perform many of the actions you normally do from the GitLab user interface:
+
+```shell
+# Sign in
+glab auth login --stdin < token.txt
+
+# View a list of issues
+glab issue list
+
+# Create merge request for issue 123
+glab mr for 123
+
+# Check out the branch for merge request 243
+glab mr checkout 243
+
+# Watch the pipeline in progress
+glab pipeline ci view
+
+# View, approve, and merge the merge request
+glab mr view
+glab mr approve
+glab mr merge
+```
+
+## Core commands
+
+- `glab alias`
+- `glab api`
+- `glab auth`
+- `glab ci`
+- `glab issue`
+- `glab label`
+- `glab mr`
+- `glab project`
+- `glab release`
+- `glab snippet`
+- `glab ssh-key`
+- `glab user`
+- `glab variable`
+
+## Install the CLI
+
+Installation instructions are available in the GLab
+[`README`](https://gitlab.com/gitlab-org/cli/#installation).
+
+## Authenticate with GitLab
+
+To authenticate with your GitLab account, run `glab auth login`.
+`glab` respects tokens set using `GITLAB_TOKEN`.
+
+## Report issues
+
+Open an issue in the [`gitlab-org/cli` repository](https://gitlab.com/gitlab-org/cli/issues/new)
+to send us feedback.
+
+## Related topics
+
+- [Install the CLI](https://gitlab.com/gitlab-org/cli/-/blob/main/README.md#installation)
+- [Documentation](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source)
+- The extension source code is available in the
+ [`cli`](https://gitlab.com/gitlab-org/cli/) project.
diff --git a/doc/integration/index.md b/doc/integration/index.md
index b2a4201e88c..bdf6475b6d2 100644
--- a/doc/integration/index.md
+++ b/doc/integration/index.md
@@ -11,7 +11,7 @@ You can integrate GitLab with external services for enhanced functionality.
## Services
-Services such as Campfire, Flowdock, Jira, Pivotal Tracker, and Slack
+Services such as Campfire, Jira, Pivotal Tracker, and Slack
are available as [integrations](../user/project/integrations/index.md).
## Issue trackers
diff --git a/doc/integration/jenkins.md b/doc/integration/jenkins.md
index 8a438dde52e..53a2fb8bbdd 100644
--- a/doc/integration/jenkins.md
+++ b/doc/integration/jenkins.md
@@ -179,7 +179,7 @@ If you get this error message while configuring GitLab, the following are possib
- The Jenkins instance is at a local address and is not included in the
[GitLab installation's allowlist](../security/webhooks.md#create-an-allowlist-for-local-requests).
- The credentials for the Jenkins instance do not have sufficient access or are invalid.
-- The **Enable authentication for `/project` end-point** checkbox is not selected in your [Jenkin's plugin configuration](#configure-the-jenkins-server).
+- The **Enable authentication for `/project` end-point** checkbox is not selected in your [Jenkins plugin configuration](#configure-the-jenkins-server).
### Error in merge requests - "Could not connect to the CI server"
diff --git a/doc/integration/jira/connect-app.md b/doc/integration/jira/connect-app.md
index 171c1cbe484..513877a7b71 100644
--- a/doc/integration/jira/connect-app.md
+++ b/doc/integration/jira/connect-app.md
@@ -72,6 +72,55 @@ for details.
If the app requires additional permissions, [the update must first be manually approved in Jira](https://developer.atlassian.com/platform/marketplace/upgrading-and-versioning-cloud-apps/#changes-that-require-manual-customer-approval).
+## Connect the GitLab.com for Jira Cloud app for self-managed instances **(FREE SELF)**
+
+> - Introduced in GitLab 15.6 [with flags](../../administration/feature_flags.md) named [`jira_connect_oauth_self_managed_setting`](https://gitlab.com/gitlab-org/gitlab/-/issues/377679), [`jira_connect_oauth`](https://gitlab.com/gitlab-org/gitlab/-/issues/355048), and [`jira_connect_oauth_self_managed`](https://gitlab.com/gitlab-org/gitlab/-/issues/359940). Disabled by default.
+> - Feature flag `jira_connect_oauth_self_managed_setting` [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105070) in GitLab 15.7.
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available,
+ask an administrator to [enable the feature flags](../../administration/feature_flags.md) named
+`jira_connect_oauth` and `jira_connect_oauth_self_managed`. On GitLab.com, this feature
+is not available. The feature is not ready for production use.
+
+Prerequisites:
+
+- GitLab.com must serve as a proxy for the instance.
+- The instance must be publicly available.
+- The instance must be on version 15.7 or later.
+
+You can link self-managed instances after installing the GitLab.com for Jira Cloud app from the marketplace.
+Jira apps can only link to one URL per marketplace listing. The official listing links to GitLab.com.
+
+### Set up your instance
+
+To set up your self-managed instance for the GitLab.com for Jira Cloud app in GitLab 15.7 or later:
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Applications** (`/admin/applications`).
+1. Select **New application**.
+1. In **Redirect URI**, enter `https://gitlab.com/-/jira_connect/oauth_callbacks`.
+1. Ensure the **Trusted** and **Confidential** checkboxes are cleared.
+<!-- markdownlint-disable MD044 -->
+1. In **Scopes**, select the **api** checkbox only.
+<!-- markdownlint-enable MD044 -->
+1. Select **Save application**.
+1. Copy the **Application ID** value.
+1. On the left sidebar, select **Settings > General** (`/admin/application_settings/general`).
+1. Expand the **GitLab for Jira App** section.
+1. Paste the **Application ID** value into **Jira Connect Application ID**.
+1. In **Jira Connect Proxy URL**, enter `https://gitlab.com`.
+1. Select **Save changes**.
+
+### Link your instance
+
+To link your self-managed instance to the GitLab.com for Jira Cloud app:
+
+1. Install the [GitLab.com for Jira Cloud app](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud?tab=overview&hosting=cloud).
+1. Select **GitLab (self-managed)**.
+1. Enter your GitLab instance URL.
+1. Select **Save**.
+
## Install the GitLab.com for Jira Cloud app for self-managed instances **(FREE SELF)**
If your GitLab instance is self-managed, you must follow some
diff --git a/doc/integration/jira/dvcs.md b/doc/integration/jira/dvcs.md
index f33536b7b91..982c8203904 100644
--- a/doc/integration/jira/dvcs.md
+++ b/doc/integration/jira/dvcs.md
@@ -19,6 +19,9 @@ are accessible.
- **Jira Server**: Your network must allow access to your instance.
- **Jira Cloud**: Your instance must be accessible through the internet.
+NOTE:
+When using GitLab 15.0 and later (including GitLab.com) with Jira Server, you might experience a [session token bug in Jira](https://jira.atlassian.com/browse/JSWSERVER-21389). As a workaround, ensure Jira Server is version 9.1.0 and later or 8.20.11 and later.
+
## Smart Commits
When connecting GitLab with Jira with DVCS, you can process your Jira issues using
diff --git a/doc/integration/kerberos.md b/doc/integration/kerberos.md
index c7cbc4389f5..a0441b79490 100644
--- a/doc/integration/kerberos.md
+++ b/doc/integration/kerberos.md
@@ -357,6 +357,38 @@ to a larger value in [the NGINX configuration](https://nginx.org/en/docs/http/ng
## Troubleshooting
+### Test connectivity between the GitLab and Kerberos servers
+
+You can use utilities like [`kinit`](https://web.mit.edu/kerberos/krb5-1.12/doc/user/user_commands/kinit.html) and [`klist`](https://web.mit.edu/kerberos/krb5-1.12/doc/user/user_commands/klist.html) to test connectivity between the GitLab server
+and the Kerberos server. How you install these depends on your specific OS.
+
+Use `klist` to see the service principal names (SPN) available in your `keytab` file and the encryption type for each SPN:
+
+```shell
+klist -ke /etc/http.keytab
+```
+
+On an Ubuntu server, the output would look similar to the following:
+
+```shell
+Keytab name: FILE:/etc/http.keytab
+KVNO Principal
+---- --------------------------------------------------------------------------
+ 3 HTTP/my.gitlab.domain@MY.REALM (des-cbc-crc)
+ 3 HTTP/my.gitlab.domain@MY.REALM (des-cbc-md5)
+ 3 HTTP/my.gitlab.domain@MY.REALM (arcfour-hmac)
+ 3 HTTP/my.gitlab.domain@MY.REALM (aes256-cts-hmac-sha1-96)
+ 3 HTTP/my.gitlab.domain@MY.REALM (aes128-cts-hmac-sha1-96)
+```
+
+Use `kinit` in verbose mode to test whether GitLab can use the keytab file to connect to the Kerberos server:
+
+```shell
+KRB5_TRACE=/dev/stdout kinit -kt /etc/http.keytab HTTP/my.gitlab.domain@MY.REALM
+```
+
+This command shows a detailed output of the authentication process.
+
### Unsupported GSSAPI mechanism
With Kerberos SPNEGO authentication, the browser is expected to send a list of
diff --git a/doc/integration/mattermost/index.md b/doc/integration/mattermost/index.md
index 04b0157b737..df6130a7540 100644
--- a/doc/integration/mattermost/index.md
+++ b/doc/integration/mattermost/index.md
@@ -123,7 +123,7 @@ http://mattermost.example.com/signup/gitlab/complete
http://mattermost.example.com/login/gitlab/complete
```
-Note that you do not need to select any options under **Scopes**. Choose **Save application**.
+Make sure to select the **Trusted** and **Confidential** settings. Under **Scopes**, select `read_user`. Then, choose **Save application**.
Once the application is created you are provided with an `Application ID` and `Secret`. One other piece of information needed is the URL of GitLab instance.
Return to the server running GitLab Mattermost and edit the `/etc/gitlab/gitlab.rb` configuration file as follows using the values you received above:
@@ -132,7 +132,7 @@ Return to the server running GitLab Mattermost and edit the `/etc/gitlab/gitlab.
mattermost['gitlab_enable'] = true
mattermost['gitlab_id'] = "12345656"
mattermost['gitlab_secret'] = "123456789"
-mattermost['gitlab_scope'] = ""
+mattermost['gitlab_scope'] = "read_user"
mattermost['gitlab_auth_endpoint'] = "http://gitlab.example.com/oauth/authorize"
mattermost['gitlab_token_endpoint'] = "http://gitlab.example.com/oauth/token"
mattermost['gitlab_user_api_endpoint'] = "http://gitlab.example.com/api/v4/user"
diff --git a/doc/integration/oauth2_generic.md b/doc/integration/oauth2_generic.md
index a337873a67e..c51400113d4 100644
--- a/doc/integration/oauth2_generic.md
+++ b/doc/integration/oauth2_generic.md
@@ -6,8 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Generic OAuth 2.0 provider **(FREE SELF)**
-The `omniauth-oauth2-generic` gem allows single sign-on (SSO) between GitLab
-and your OAuth 2.0 provider, or any OAuth 2.0 provider compatible with this gem).
+The [`omniauth-oauth2-generic` gem](https://gitlab.com/satorix/omniauth-oauth2-generic) allows single sign-on (SSO) between GitLab
+and your OAuth 2.0 provider, or any OAuth 2.0 provider compatible with this gem.
This strategy allows for the configuration of this OmniAuth SSO process:
@@ -48,62 +48,149 @@ To configure the provider:
appear is different for each provider. This may also be called application ID
and application secret.
-1. On your GitLab server, open the appropriate configuration file.
-
- For Omnibus GitLab:
-
- ```shell
- sudo editor /etc/gitlab/gitlab.rb
- ```
-
- For installations from source:
-
- ```shell
- cd /home/git/gitlab
- sudo -u git -H editor config/gitlab.yml
- ```
-
-1. See [Configure initial settings](omniauth.md#configure-initial-settings) for
- initial settings.
-
-1. Add the provider-specific configuration for your provider. For example:
-
- ```ruby
- gitlab_rails['omniauth_providers'] = [
- {
- name: "oauth2_generic",
- label: "Provider name", # optional label for login button, defaults to "Oauth2 Generic"
- app_id: "<your_app_client_id>",
- app_secret: "<your_app_client_secret>",
- args: {
- client_options: {
- site: "<your_auth_server_url>",
- user_info_url: "/oauth2/v1/userinfo",
- authorize_url: "/oauth2/v1/authorize",
- token_url: "/oauth2/v1/token"
- },
- user_response_structure: {
- root_path: [],
- id_path: ["sub"],
- attributes: {
- email: "email",
- name: "name"
- }
- },
- authorize_params: {
- scope: "openid profile email"
- },
- strategy_class: "OmniAuth::Strategies::OAuth2Generic"
- }
- }
- ]
- ```
-
- For more information about these settings, see the [gem's README](https://gitlab.com/satorix/omniauth-oauth2-generic#gitlab-config-example).
-
-1. Save the configuration file.
-
-1. For the changes to take effect, [restart GitLab](../administration/restart_gitlab.md#installations-from-source).
+1. On your GitLab server, complete the following steps.
+
+ ::Tabs
+
+ :::TabTitle Linux package (Omnibus)
+
+ 1. [Configure the initial settings](omniauth.md#configure-initial-settings).
+ 1. Edit `/etc/gitlab/gitlab.rb` to add the configuration for your provider. For example:
+
+ ```ruby
+ gitlab_rails['omniauth_providers'] = [
+ {
+ name: "oauth2_generic",
+ label: "Provider name", # optional label for login button, defaults to "Oauth2 Generic"
+ app_id: "<your_app_client_id>",
+ app_secret: "<your_app_client_secret>",
+ args: {
+ client_options: {
+ site: "<your_auth_server_url>",
+ user_info_url: "/oauth2/v1/userinfo",
+ authorize_url: "/oauth2/v1/authorize",
+ token_url: "/oauth2/v1/token"
+ },
+ user_response_structure: {
+ root_path: [],
+ id_path: ["sub"],
+ attributes: {
+ email: "email",
+ name: "name"
+ }
+ },
+ authorize_params: {
+ scope: "openid profile email"
+ },
+ strategy_class: "OmniAuth::Strategies::OAuth2Generic"
+ }
+ }
+ ]
+ ```
+
+ 1. Save the file and reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+ :::TabTitle Helm chart (Kubernetes)
+
+ 1. [Configure the initial settings](omniauth.md#configure-initial-settings).
+ 1. Export the Helm values:
+
+ ```shell
+ helm get values gitlab > gitlab_values.yaml
+ ```
+
+ 1. Edit `gitlab_values.yaml`.
+
+ NOTE:
+ The following example exposes the `app_secret` value in the main YAML file.
+ You're strongly advised to use
+ [Helm secrets](https://docs.gitlab.com/charts/installation/secrets.html)
+ instead.
+
+ ```yaml
+ global:
+ appConfig:
+ omniauth:
+ enabled: true
+ providers:
+ - name: "oauth2_generic"
+ label: "Provider name" # optional label for login button defaults to "Oauth2 Generic"
+ app_id: "<your_app_client_id>"
+ app_secret: "<your_app_client_secret>"
+ args:
+ client_options:
+ site: "<your_auth_server_url>"
+ user_info_url: "/oauth2/v1/userinfo"
+ authorize_url: "/oauth2/v1/authorize"
+ token_url: "/oauth2/v1/token"
+ user_response_structure:
+ root_path: []
+ id_path: ["sub"]
+ attributes:
+ email: "email"
+ name: "name"
+ authorize_params:
+ scope: "openid profile email"
+ strategy_class: "OmniAuth::Strategies::OAuth2Generic"
+ ```
+
+ 1. Save the file and apply the new values:
+
+ ```shell
+ helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab
+ ```
+
+ :::TabTitle Self-compiled (source)
+
+ 1. [Configure the initial settings](omniauth.md#configure-initial-settings).
+ 1. Edit `/home/git/gitlab/config/gitlab.yml`:
+
+ ```yaml
+ production: &base
+ omniauth:
+ providers:
+ - { name: "oauth2_generic",
+ label: "Provider name", # optional label for login button, defaults to "Oauth2 Generic"
+ app_id: "<your_app_client_id>",
+ app_secret: "<your_app_client_secret>",
+ args: {
+ client_options: {
+ site: "<your_auth_server_url>",
+ user_info_url: "/oauth2/v1/userinfo",
+ authorize_url: "/oauth2/v1/authorize",
+ token_url: "/oauth2/v1/token"
+ },
+ user_response_structure: {
+ root_path: [],
+ id_path: ["sub"],
+ attributes: {
+ email: "email",
+ name: "name"
+ }
+ },
+ authorize_params: {
+ scope: "openid profile email"
+ },
+ strategy_class: "OmniAuth::Strategies::OAuth2Generic"
+ }
+ }
+ ```
+
+ 1. Save the file and restart GitLab:
+
+ ```shell
+ # For systems running systemd
+ sudo systemctl restart gitlab.target
+
+ # For systems running SysV init
+ sudo service gitlab restart
+ ```
+
+ ::EndTabs
On the sign-in page there should now be a new icon below the regular sign-in
form. Select that icon to begin your provider's authentication process. This
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index af039c8a009..2dd8505b558 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -38,7 +38,7 @@ GitLab supports the following OmniAuth providers.
| [SAML](saml.md) | `saml` |
| [Twitter](twitter.md) | `twitter` |
-## Configure initial settings
+## Initial settings
Before you configure the OmniAuth provider,
configure the settings that are common for all providers.
@@ -49,13 +49,15 @@ Omnibus, Docker, and source | Helm chart | Description | Default value
`auto_link_ldap_user` | `autoLinkLdapUser` | Creates an LDAP identity in GitLab for users that are created through an OmniAuth provider. You can enable this setting if you have [LDAP integration](../administration/auth/ldap/index.md) enabled. Requires the `uid` of the user to be the same in both LDAP and the OmniAuth provider. | `false`
`block_auto_created_users` | `blockAutoCreatedUsers` | Blocks users that are automatically created from signing in until they are approved by an administrator. | `true`. If you set the value to `false`, make sure you define providers that you can control, like SAML or Google. Otherwise, any user on the internet can sign in to GitLab without an administrator's approval.
-To change these settings:
+### Configure initial settings
+
+To change the OmniAuth settings:
::Tabs
- :::TabTitle Omnibus
+ :::TabTitle Linux package (Omnibus)
- 1. Edit `/etc/gitlab/gitlab.rb` and update the following section:
+ 1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
# CAUTION!
@@ -67,13 +69,13 @@ To change these settings:
gitlab_rails['omniauth_block_auto_created_users'] = true
```
- 1. Reconfigure GitLab:
+ 1. Save the file and reconfigure GitLab:
```shell
sudo gitlab-ctl reconfigure
```
- :::TabTitle Helm chart
+ :::TabTitle Helm chart (Kubernetes)
1. Export the Helm values:
@@ -96,22 +98,15 @@ To change these settings:
For more details, see the
[globals documentation](https://docs.gitlab.com/charts/charts/globals.html#omniauth).
- 1. Apply the new values:
+ 1. Save the file and apply the new values:
```shell
helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab
```
- :::TabTitle Source
-
- 1. Open the configuration file:
-
- ```shell
- cd /home/git/gitlab
- sudo -u git -H editor config/gitlab.yml
- ```
+ :::TabTitle Self-compiled (source)
- 1. Update the following section:
+ 1. Edit `/home/git/gitlab/config/gitlab.yml`:
```yaml
## OmniAuth settings
@@ -132,9 +127,13 @@ To change these settings:
block_auto_created_users: true
```
- 1. Restart GitLab:
+ 1. Save the file and restart GitLab:
```shell
+ # For systems running systemd
+ sudo systemctl restart gitlab.target
+
+ # For systems running SysV init
sudo service gitlab restart
```
@@ -283,7 +282,7 @@ for the OpenID Connect provider and the Twitter OAuth provider.
This method of enabling automatic linking works for all providers
[except SAML](https://gitlab.com/gitlab-org/gitlab/-/issues/338293).
-To enable automatic linking for SAML, see the [SAML setup instructions](saml.md#general-setup).
+To enable automatic linking for SAML, see the [SAML setup instructions](saml.md#configure-saml-support-in-gitlab).
## Create an external providers list
diff --git a/doc/integration/saml.md b/doc/integration/saml.md
index fd01e9e0e56..84879b7c4c7 100644
--- a/doc/integration/saml.md
+++ b/doc/integration/saml.md
@@ -5,41 +5,29 @@ info: To determine the technical writer assigned to the Stage/Group associated w
type: reference
---
-# SAML OmniAuth Provider **(FREE SELF)**
+# SAML SSO for self-managed GitLab instances **(FREE SELF)**
-This page describes instance-wide SAML for self-managed GitLab instances. For
-SAML on GitLab.com, see [SAML SSO for GitLab.com groups](../user/group/saml_sso/index.md).
+This page describes how to set up instance-wide SAML single sign on (SSO) for
+self-managed GitLab instances.
-You should also reference the [OmniAuth documentation](omniauth.md) for general
-settings that apply to all OmniAuth providers.
+You can configure GitLab to act as a SAML service provider (SP). This allows
+GitLab to consume assertions from a SAML identity provider (IdP), such as
+Okta, to authenticate users.
-## Glossary of common terms
-
-| Term | Description |
-|--------------------------------|-------------|
-| Identity provider (IdP) | The service which manages your user identities, such as Okta or OneLogin. |
-| Service provider (SP) | GitLab can be configured as a SAML 2.0 SP. |
-| Assertion | A piece of information about a user's identity, such as their name or role. Also known as claims or attributes. |
-| Single Sign-On (SSO) | Name of authentication scheme. |
-| Assertion consumer service URL | The callback on GitLab where users are redirected after successfully authenticating with the identity provider. |
-| Issuer | How GitLab identifies itself to the identity provider. Also known as a "Relying party trust identifier". |
-| Certificate fingerprint | Used to confirm that communications over SAML are secure by checking that the server is signing communications with the correct certificate. Also known as a certificate thumbprint. |
+To set up SAML on GitLab.com, see [SAML SSO for GitLab.com groups](../user/group/saml_sso/index.md).
-## General Setup
+For more information on:
-GitLab can be configured to act as a SAML 2.0 Service Provider (SP). This allows
-GitLab to consume assertions from a SAML 2.0 Identity Provider (IdP), such as
-Okta to authenticate users.
+- OmniAuth provider settings, see the [OmniAuth documentation](omniauth.md).
+- Commonly-used terms, see the [glossary of common terms](#glossary-of-common-terms).
-First configure SAML 2.0 support in GitLab, then register the GitLab application
-in your SAML IdP:
+## Configure SAML support in GitLab
-1. Make sure GitLab is configured with HTTPS.
- See [Using HTTPS](../install/installation.md#using-https) for instructions.
+1. Make sure GitLab is [configured with HTTPS](../install/installation.md#using-https).
1. On your GitLab server, open the configuration file.
- For Omnibus package:
+ For Omnibus installations:
```shell
sudo editor /etc/gitlab/gitlab.rb
@@ -53,11 +41,12 @@ in your SAML IdP:
sudo -u git -H editor config/gitlab.yml
```
-1. See [Configure initial settings](omniauth.md#configure-initial-settings) for initial settings.
+1. Edit the initial [configuration settings](omniauth.md#configure-initial-settings).
+
1. To allow your users to use SAML to sign up without having to manually create
- an account first, add the following values to your configuration:
+ an account first, add the following values to your configuration.
- For Omnibus package:
+ For Omnibus installations:
```ruby
gitlab_rails['omniauth_allow_single_sign_on'] = ['saml']
@@ -73,10 +62,10 @@ in your SAML IdP:
block_auto_created_users: false
```
-1. You can also automatically link SAML users with existing GitLab users if their
- email addresses match by adding the following setting:
+1. Optional. You can automatically link SAML users with existing GitLab users if their
+ email addresses match by adding the following setting.
- For Omnibus package:
+ For Omnibus installations:
```ruby
gitlab_rails['omniauth_auto_link_saml_user'] = true
@@ -88,12 +77,21 @@ in your SAML IdP:
auto_link_saml_user: true
```
-1. Ensure that the SAML [`NameID`](../user/group/saml_sso/index.md#nameid) and email address are fixed for each user,
-as described in the section on [Security](#security). Otherwise, your users are able to sign in as other authorized users.
+ Alternatively, a user can manually link their SAML identity to an existing GitLab
+ account by [enabling OmniAuth for an existing user](omniauth.md#enable-omniauth-for-an-existing-user).
-1. Add the provider configuration:
+1. Configure the following attributes so your SAML users cannot change them:
- For Omnibus package:
+ - [`NameID`](../user/group/saml_sso/index.md#nameid).
+ - `Email` when used with `omniauth_auto_link_saml_user`.
+
+ If users can change these attributes, they can sign in as other authorized users.
+ See your SAML IdP documentation for information on how to make these attributes
+ unchangeable.
+
+1. Add the provider configuration.
+
+ For Omnibus installations:
```ruby
gitlab_rails['omniauth_providers'] = [
@@ -129,63 +127,118 @@ as described in the section on [Security](#security). Otherwise, your users are
}
```
-1. Change the value for `assertion_consumer_service_url` to match the HTTPS endpoint
- of GitLab (append `users/auth/saml/callback` to the HTTPS URL of your GitLab
- installation to generate the correct value).
+1. Match the value for `assertion_consumer_service_url` to the HTTPS endpoint
+ of GitLab. To generate the correct value, append `users/auth/saml/callback` to the
+ HTTPS URL of your GitLab installation.
-1. Change the values of `idp_cert_fingerprint`, `idp_sso_target_url`,
- `name_identifier_format` to match your IdP. If a fingerprint is used it must
- be a SHA1 fingerprint; check
- [the OmniAuth SAML documentation](https://github.com/omniauth/omniauth-saml)
- for more details on these options.
- See the [notes on configuring your identity provider](#notes-on-configuring-your-identity-provider) for more information.
+1. Change the following values to match your IdP:
+ - `idp_cert_fingerprint`.
+ - `idp_sso_target_url`.
+ - `name_identifier_format`.
+ If you use a `idp_cert_fingerprint`, it must be a SHA1 fingerprint. For more
+ information on these values, see the
+ [OmniAuth SAML documentation](https://github.com/omniauth/omniauth-saml).
+ For more information on other configuration settings, see
+ [configuring SAML on your IdP](#configure-saml-on-your-idp).
1. Change the value of `issuer` to a unique name, which identifies the application
to the IdP.
-1. For the changes to take effect:
- - If you installed via Omnibus, [reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure).
- - If you installed from source, [restart GitLab](../administration/restart_gitlab.md#installations-from-source).
+1. For the changes to take effect, if you installed:
+ - Using Omnibus, [reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure).
+ - From source, [restart GitLab](../administration/restart_gitlab.md#installations-from-source).
-1. Register the GitLab SP in your SAML 2.0 IdP, using the application name specified
- in `issuer`.
+### Register GitLab in your SAML IdP
-To ease configuration, most IdP accept a metadata URL for the application to provide
-configuration information to the IdP. To build the metadata URL for GitLab, append
-`users/auth/saml/metadata` to the HTTPS URL of your GitLab installation, for instance:
+1. Register the GitLab SP in your SAML IdP, using the application name specified in `issuer`.
-```plaintext
-https://gitlab.example.com/users/auth/saml/metadata
-```
+1. To provide configuration information to the IdP, build a metadata URL for the
+ application. To build the metadata URL for GitLab, append `users/auth/saml/metadata`
+ to the HTTPS URL of your GitLab installation. For example:
-At a minimum the IdP *must* provide a claim containing the user's email address using `email` or `mail`.
-See [the assertions list](#assertions) for other available claims.
+ ```plaintext
+ https://gitlab.example.com/users/auth/saml/metadata
+ ```
+
+ At a minimum the IdP **must** provide a claim containing the user's email address
+ using `email` or `mail`. For more information on other available claims, see
+ [configuring assertions](#configure-assertions).
+
+1. On the sign in page there should now be a SAML icon below the regular sign in form.
+ Select the icon to begin the authentication process. If authentication is successful,
+ you are returned to GitLab and signed in.
+
+### Configure SAML on your IdP
-On the sign in page there should now be a SAML button below the regular sign in form.
-Select the icon to begin the authentication process. If everything goes well the user
-is returned to GitLab and signed in.
+To configure a SAML application on your IdP, you need at least the following information:
-### Use multiple SAML identity providers
+- Assertion consumer service URL.
+- Issuer.
+- [`NameID`](../user/group/saml_sso/index.md#nameid).
+- [Email address claim](#configure-assertions).
+
+For an example configuration, see [set up identity providers](#set-up-identity-providers).
+
+Your IdP may need additional configuration. For more information, see
+[additional configuration for SAML apps on your IdP](#additional-configuration-for-saml-apps-on-your-idp).
+
+### Configure GitLab to use multiple SAML IdPs
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/14361) in GitLab 14.6.
-You can configure GitLab to use multiple SAML identity providers if:
+You can configure GitLab to use multiple SAML IdPs if:
-- Each provider has a unique name set that matches a name set in `args`. At least one provider **must** have the name `saml` to mitigate a
- [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/366450) in GitLab 14.6 and newer.
-- The providers' names are:
- - Used in OmniAuth configuration for properties based on the provider name. For example, `allowBypassTwoFactor`, `allowSingleSignOn`, and
- `syncProfileFromProvider`.
- - Used for association to each existing user as an additional identity.
+- Each provider has a unique name set that matches a name set in `args`. At least
+ one provider must have the name `saml` to mitigate a
+ [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/366450) in GitLab
+ 14.6 and newer.
+- The providers' names are used:
+ - In OmniAuth configuration for properties based on the provider name. For example,
+ `allowBypassTwoFactor`, `allowSingleSignOn`, and `syncProfileFromProvider`.
+ - For association to each existing user as an additional identity.
- The `assertion_consumer_service_url` matches the provider name.
-- The `strategy_class` is explicitly set because it cannot be inferred from provider name.
+- The `strategy_class` is explicitly set because it cannot be inferred from provider
+ name.
-Example multiple providers configuration for Omnibus GitLab:
+Example provider's configuration for installations from source:
+
+```yaml
+omniauth:
+ providers:
+ - {
+ name: 'saml', # This must match the following name configuration parameter
+ args: {
+ name: 'saml', # This is mandatory and must match the provider name
+ strategy_class: 'OmniAuth::Strategies::SAML',
+ assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml_1/callback', # URL must match the name of the provider
+ ... # Put here all the required arguments similar to a single provider
+ },
+ label: 'Provider 1' # Differentiate the two buttons and providers in the UI
+ }
+ - {
+ name: 'saml1', # This must match the following name configuration parameter
+ args: {
+ name: 'saml1', # This is mandatory and must match the provider name
+ strategy_class: 'OmniAuth::Strategies::SAML',
+ assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml_2/callback', # URL must match the name of the provider
+ ... # Put here all the required arguments similar to a single provider
+ },
+ label: 'Provider 2' # Differentiate the two buttons and providers in the UI
+ }
+```
+
+Example provider's configuration for Omnibus GitLab installations:
+
+To allow your users to use SAML to sign up without having to manually create an account from either of the providers, add the following values to your configuration.
+
+```ruby
+gitlab_rails['omniauth_allow_single_sign_on'] = ['saml', 'saml1']
+```
```ruby
gitlab_rails['omniauth_providers'] = [
{
- name: 'saml',
+ name: 'saml', # This must match the following name configuration parameter
args: {
name: 'saml', # This is mandatory and must match the provider name
strategy_class: 'OmniAuth::Strategies::SAML',
@@ -195,7 +248,7 @@ gitlab_rails['omniauth_providers'] = [
label: 'Provider 1' # Differentiate the two buttons and providers in the UI
},
{
- name: 'saml1',
+ name: 'saml1', # This must match the following name configuration parameter
args: {
name: 'saml1', # This is mandatory and must match the provider name
strategy_class: 'OmniAuth::Strategies::SAML',
@@ -207,87 +260,126 @@ gitlab_rails['omniauth_providers'] = [
]
```
-Example providers configuration for installations from source:
+To allow your users to use SAML to sign up without having to manually create an
+account from either of the providers, add the following values to your configuration.
-```yaml
-omniauth:
- providers:
- - {
- name: 'saml',
- args: {
- name: 'saml', # This is mandatory and must match the provider name
- strategy_class: 'OmniAuth::Strategies::SAML',
- assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml_1/callback', # URL must match the name of the provider
- ... # Put here all the required arguments similar to a single provider
- },
- label: 'Provider 1' # Differentiate the two buttons and providers in the UI
- }
- - {
- name: 'saml1',
- args: {
- name: 'saml1', # This is mandatory and must match the provider name
- strategy_class: 'OmniAuth::Strategies::SAML',
- assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml_2/callback', # URL must match the name of the provider
- ... # Put here all the required arguments similar to a single provider
- },
- label: 'Provider 2' # Differentiate the two buttons and providers in the UI
- }
+```ruby
+gitlab_rails['omniauth_allow_single_sign_on'] = ['saml', 'saml1']
```
-### Notes on configuring your identity provider
+## Set up identity providers
-When configuring a SAML app on the IdP, you need at least:
+GitLab support of SAML means you can sign in to GitLab through a wide range
+of IdPs.
-- Assertion consumer service URL
-- Issuer
-- [`NameID`](../user/group/saml_sso/index.md#nameid)
-- [Email address claim](#assertions)
+GitLab provides the following content on setting up the Okta and Google Workspace
+IdPs for guidance only. If you have any questions on configuring either of these
+IdPs, contact your provider's support.
-Your identity provider may require additional configuration, such as the following:
+### Set up Okta
-| Field | Value | Notes |
-|-------|-------|-------|
-| SAML profile | Web browser SSO profile | GitLab uses SAML to sign users in through their browser. No requests are made directly to the identity provider. |
-| SAML request binding | HTTP Redirect | GitLab (the service provider) redirects users to your identity provider with a base64 encoded `SAMLRequest` HTTP parameter. |
-| SAML response binding | HTTP POST | Specifies how the SAML token is sent by your identity provider. Includes the `SAMLResponse`, which a user's browser submits back to GitLab. |
-| Sign SAML response | Required | Prevents tampering. |
-| X.509 certificate in response | Required | Signs the response and checks against the provided fingerprint. |
-| Fingerprint algorithm | SHA-1 | GitLab uses a SHA-1 hash of the certificate to sign the SAML Response. |
-| Signature algorithm | SHA-1/SHA-256/SHA-384/SHA-512 | Determines how a response is signed. Also known as the digest method, this can be specified in the SAML response. |
-| Encrypt SAML assertion | Optional | Uses TLS between your identity provider, the user's browser, and GitLab. |
-| Sign SAML assertion | Optional | Validates the integrity of a SAML assertion. When active, signs the whole response. |
-| Check SAML request signature | Optional | Checks the signature on the SAML response. |
-| Default RelayState | Optional | Specifies the URL users should end up on after successfully signing in through SAML at your identity provider. |
-| NameID format | Persistent | See [NameID format details](../user/group/saml_sso/index.md#nameid-format). |
-| Additional URLs | Optional | May include the issuer (or identifier) or the assertion consumer service URL in other fields on some providers. |
+1. In the Okta administrator section choose **Applications**.
+1. On the app screen, select **Create App Integration** and then select
+ **SAML 2.0** on the next screen.
+1. Optional. Choose and add a logo from [GitLab Press](https://about.gitlab.com/press/).
+ You must crop and resize the logo.
+1. Complete the SAML general configuration. Enter:
+ - `"Single sign-on URL"`: Use the assertion consumer service URL.
+ - `"Audience URI"`: Use the issuer.
+ - [`NameID`](../user/group/saml_sso/index.md#nameid).
+ - [Assertions](#configure-assertions).
+1. In the feedback section, enter that you're a customer and creating an
+ app for internal use.
+1. At the top of your new app's profile, select **SAML 2.0 configuration instructions**.
+1. Note the **Identity Provider Single Sign-On URL**. Use this URL for the
+ `idp_sso_target_url` on your GitLab configuration file.
+1. Before you sign out of Okta, make sure you add your user and groups if any.
+
+### Set up Google Workspace
+
+Prerequisites:
-For example configurations, see the [notes on specific providers](#providers).
+- Make sure you have access to a
+[Google Workspace Super Admin account](https://support.google.com/a/answer/2405986#super_admin).
-### Assertions
+1. Use the following information, and follow the instructions in
+[Set up your own custom SAML application in Google Workspace](https://support.google.com/a/answer/6087519?hl=en).
-| Field | Supported keys |
-|-----------------|----------------|
-| Email (required)| `email`, `mail` |
-| Full Name | `name` |
+ | | Typical value | Description |
+ |------------------|--------------------------------------------------|----------------------------------------------------------|
+ | Name of SAML App | GitLab | Other names OK. |
+ | ACS URL | `https://<GITLAB_DOMAIN>/users/auth/saml/callback` | Assertion Consumer Service URL. |
+ | GITLAB_DOMAIN | `gitlab.example.com` | Your GitLab instance domain. |
+ | Entity ID | `https://gitlab.example.com` | A value unique to your SAML application. Set it to the `issuer` in your GitLab configuration. |
+ | Name ID format | EMAIL | Required value. Also known as `name_identifier_format`. |
+ | Name ID | Primary email address | Your email address. Make sure someone receives content sent to that address. |
+ | First name | `first_name` | First name. Required value to communicate with GitLab. |
+ | Last name | `last_name` | Last name. Required value to communicate with GitLab. |
+
+1. Set up the following SAML attribute mappings:
+
+ | Google Directory attributes | App attributes |
+ |-----------------------------------|----------------|
+ | Basic information > Email | `email` |
+ | Basic Information > First name | `first_name` |
+ | Basic Information > Last name | `last_name` |
+
+ You might use some of this information when you
+ [configure SAML support in GitLab](#configure-saml-support-in-gitlab).
+
+When configuring the Google Workspace SAML application, record the following information:
+
+| | Value | Description |
+|-------------|--------------|-----------------------------------------------------------------------------------|
+| SSO URL | Depends | Google Identity Provider details. Set to the GitLab `idp_sso_target_url` setting. |
+| Certificate | Downloadable | Run `openssl x509 -in <your_certificate.crt> -noout -fingerprint` to generate the SHA1 fingerprint that can be used in the `idp_cert_fingerprint` setting. |
+
+Google Workspace Administrator also provides the IdP metadata, Entity ID, and SHA-256
+fingerprint. However, GitLab does not need this information to connect to the
+Google Workspace SAML application.
+
+### Set up other IdPs
+
+Some IdPs have documentation on how to use them as the IdP in SAML configurations.
+For example:
+
+- [Active Directory Federation Services (ADFS)](https://learn.microsoft.com/en-us/windows-server/identity/ad-fs/operations/create-a-relying-party-trust)
+- [Auth0](https://auth0.com/docs/authenticate/protocols/saml/saml-sso-integrations/configure-auth0-saml-identity-provider)
+
+If you have any questions on configuring your IdP in a SAML configuration, contact
+your provider's support.
+
+### Configure assertions
+
+| Field | Supported default keys |
+|-----------------|------------------------|
+| Email (required)| `email`, `mail` |
+| Full Name | `name` |
| First Name | `first_name`, `firstname`, `firstName` |
-| Last Name | `last_name`, `lastname`, `lastName` |
+| Last Name | `last_name`, `lastname`, `lastName` |
+
+See [`attribute_statements`](#map-saml-response-attribute-names) for:
-See [`attribute_statements`](#attribute_statements) for examples on how custom
-assertions are configured. This section also describes how to configure custom
-username attributes.
+- Custom assertion configuration examples.
+- How to configure custom username attributes.
-Please refer to [the OmniAuth SAML gem](https://github.com/omniauth/omniauth-saml/blob/master/lib/omniauth/strategies/saml.rb)
-for a full list of supported assertions.
+For a full list of supported assertions, see the [OmniAuth SAML gem](https://github.com/omniauth/omniauth-saml/blob/master/lib/omniauth/strategies/saml.rb)
-## SAML Groups
+## Configure users based on SAML group membership
-You can require users to be members of a certain group, or assign users [external](../user/permissions.md#external-users), administrator or [auditor](../user/permissions.md#auditor-users) roles based on group membership.
-These groups are checked on each SAML login and user attributes updated as necessary.
-This feature **does not** allow you to
-automatically add users to GitLab [Groups](../user/group/index.md).
+You can:
-Support for these groups depends on your [subscription](https://about.gitlab.com/pricing/)
-and whether you've installed [GitLab Enterprise Edition (EE)](https://about.gitlab.com/install/).
+- Require users to be members of a certain group.
+- Assign users [external](../user/admin_area/external_users.md), administrator or [auditor](../administration/auditor_users.md) roles based on group membership.
+
+GitLab checks these groups on each SAML sign in and updates user attributes as necessary.
+This feature **does not** allow you to automatically add users to GitLab
+[Groups](../user/group/index.md).
+
+Support for these groups depends on:
+
+- Your [subscription](https://about.gitlab.com/pricing/).
+- Whether you've installed [GitLab Enterprise Edition (EE)](https://about.gitlab.com/install/).
| Group | Tier | GitLab Enterprise Edition (EE) Only? |
|------------------------------|--------------------|--------------------------------------|
@@ -296,11 +388,11 @@ and whether you've installed [GitLab Enterprise Edition (EE)](https://about.gitl
| [Admin](#administrator-groups) | **(FREE SELF)** | Yes |
| [Auditor](#auditor-groups) | **(PREMIUM SELF)** | Yes |
-### Requirements
+### Prerequisites
-First tell GitLab where to look for group information. For this, you
-must make sure that your IdP server sends a specific `AttributeStatement` along
-with the regular SAML response. Here is an example:
+You must tell GitLab where to look for group information. To do this, make sure
+that your IdP server sends a specific `AttributeStatement` along with the regular
+SAML response. For example:
```xml
<saml:AttributeStatement>
@@ -313,22 +405,25 @@ with the regular SAML response. Here is an example:
</saml:AttributeStatement>
```
-The name of the attribute can be anything you like, but it must contain the groups
-to which a user belongs. To tell GitLab where to find these groups, you need
-to add a `groups_attribute:` element to your SAML settings.
+The name of the attribute must contain the groups that a user belongs to.
+To tell GitLab where to find these groups, add a `groups_attribute:`
+element to your SAML settings.
### Required groups
-Your IdP passes Group information to the SP (GitLab) in the SAML Response.
-To use this response, configure GitLab to identify:
+Your IdP passes group information to GitLab in the SAML response. To use this
+response, configure GitLab to identify:
+
+- Where to look for the groups in the SAML response, using the `groups_attribute` setting.
+- Information about a group or user, using a group setting.
-- Where to look for the groups in the SAML response via the `groups_attribute` setting
-- Which group membership is requisite to sign in via the `required_groups` setting
+Use the `required_groups` setting to configure GitLab to identify which group
+membership is required to sign in.
-When `required_groups` is empty or not set, anyone with proper authentication
-is able to use the service.
+If you do not set `required_groups` or leave the setting empty, anyone with proper
+authentication can use the service.
-Example:
+Example configuration:
```yaml
{ name: 'saml',
@@ -346,9 +441,17 @@ Example:
### External groups
-SAML login supports the automatic identification of a user as an
-[external user](../user/permissions.md#external-users). This is based on the user's group
-membership in the SAML identity provider.
+Your IdP passes group information to GitLab in the SAML response. To use this
+response, configure GitLab to identify:
+
+- Where to look for the groups in the SAML response, using the `groups_attribute` setting.
+- Information about a group or user, using a group setting.
+
+SAML can automatically identify a user as an
+[external user](../user/admin_area/external_users.md), based on the `external_groups`
+setting.
+
+Example configuration:
```yaml
{ name: 'saml',
@@ -366,11 +469,16 @@ membership in the SAML identity provider.
### Administrator groups
-The requirements are the same as the previous settings:
+Your IdP passes group information to GitLab in the SAML response. To use this
+response, configure GitLab to identify:
+
+- Where to look for the groups in the SAML response, using the `groups_attribute` setting.
+- Information about a group or user, using a group setting.
-- The IdP must pass Group information to GitLab.
-- GitLab must know where to look for the groups in the SAML response, as well as
- which groups grant the user administrator access.
+Use the `admin_groups` setting to configure GitLab to identify which groups grant
+the user administrator access.
+
+Example configuration:
```yaml
{ name: 'saml',
@@ -390,11 +498,16 @@ The requirements are the same as the previous settings:
> Introduced in GitLab 11.4.
-The requirements are the same as the previous settings:
+Your IdP passes group information to GitLab in the SAML response. To use this
+response, configure GitLab to identify:
+
+- Where to look for the groups in the SAML response, using the `groups_attribute` setting.
+- Information about a group or user, using a group setting.
+
+Use the `auditor_groups` setting to configure GitLab to identify which groups include
+users with [auditor access](../administration/auditor_users.md).
-- The IdP must pass Group information to GitLab.
-- GitLab should know where to look for the groups in the SAML response, as well as which
- groups include users with the [Auditor role](../user/permissions.md#auditor-users).
+Example configuration:
```yaml
{ name: 'saml',
@@ -410,17 +523,17 @@ The requirements are the same as the previous settings:
} }
```
-## Group Sync
+## Automatically manage SAML Group Sync
For information on automatically managing GitLab group membership, see [SAML Group Sync](../user/group/saml_sso/group_sync.md).
-## Bypass two factor authentication
+## Bypass two-factor authentication
-If you want some SAML authentication methods to count as 2FA on a per session
-basis, you can register them in the `upstream_two_factor_authn_contexts` list.
+To configure a SAML authentication method to count as two-factor authentication
+(2FA) on a per session basis, register that method in the `upstream_two_factor_authn_contexts`
+list.
-In addition to the changes in GitLab, make sure that your IdP is returning the
-`AuthnContext`. For example:
+1. Make sure that your IdP is returning the `AuthnContext`. For example:
```xml
<saml:AuthnStatement>
@@ -430,7 +543,11 @@ In addition to the changes in GitLab, make sure that your IdP is returning the
</saml:AuthnStatement>
```
-**For Omnibus installations:**
+1. Edit your installation configuration to register the SAML authentication method
+ in the `upstream_two_factor_authn_contexts` list. How you edit your configuration
+ will differ depending on your installation type.
+
+### Omnibus GitLab installations
1. Edit `/etc/gitlab/gitlab.rb`:
@@ -456,11 +573,10 @@ In addition to the changes in GitLab, make sure that your IdP is returning the
]
```
-1. Save the file and [reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab for the changes to take effect.
-
----
+1. Save the file and [reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure)
+ GitLab for the changes to take effect.
-**For installations from source:**
+### Installations from source
1. Edit `config/gitlab.yml`:
@@ -486,17 +602,62 @@ In addition to the changes in GitLab, make sure that your IdP is returning the
}
```
-1. Save the file and [restart GitLab](../administration/restart_gitlab.md#installations-from-source) for the changes to take effect
+1. Save the file and [restart GitLab](../administration/restart_gitlab.md#installations-from-source)
+ for the changes to take effect.
+
+## Validate response signatures
-## Customization
+IdPs must sign SAML responses to ensure that the assertions are not tampered with.
-### `auto_sign_in_with_provider`
+This prevents user impersonation and privilege escalation when specific group
+membership is required.
+
+You configure the response signature validation using `idp_cert_fingerprint`.
+An example configuration:
+
+```yaml
+args: {
+ assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
+ idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
+ idp_sso_target_url: 'https://login.example.com/idp',
+ issuer: 'https://gitlab.example.com',
+ name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
+}
+```
-You can add this setting to your GitLab configuration to automatically redirect you
-to your SAML server for authentication. This removes the requirement to select a button
-before actually signing in.
+If your IdP does not support configuring this using `idp_cert_fingerprint`, you
+can instead configure GitLab directly using `idp_cert`. An example configuration:
-For Omnibus package:
+```yaml
+args: {
+ assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
+ idp_cert: '-----BEGIN CERTIFICATE-----
+ <redacted>
+ -----END CERTIFICATE-----',
+ idp_sso_target_url: 'https://login.example.com/idp',
+ issuer: 'https://gitlab.example.com',
+ name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
+}
+```
+
+If you have configured the response signature validation incorrectly, you might see
+error messages such as:
+
+- A key validation error.
+- Digest mismatch.
+- Fingerprint mismatch.
+
+For more information on solving these errors, see the [troubleshooting SAML guide](../user/group/saml_sso/troubleshooting.md).
+
+## Customize SAML settings
+
+### Redirect users to SAML server for authentication
+
+You can add the `auto_sign_in_with_provider` setting to your GitLab configuration
+to automatically redirect you to your SAML server for authentication. This removes
+the requirement to select an element before actually signing in.
+
+For Omnibus GitLab installations:
```ruby
gitlab_rails['omniauth_auto_sign_in_with_provider'] = 'saml'
@@ -509,31 +670,28 @@ omniauth:
auto_sign_in_with_provider: saml
```
-Keep in mind that every sign in attempt redirects to the SAML server;
-you cannot sign in using local credentials. Ensure at least one of the
-SAML users has administrator access.
+Every sign in attempt redirects to the SAML server, so you cannot sign in using
+local credentials. Make sure at least one of the SAML users has administrator access.
-You may also bypass the auto sign-in feature by browsing to
+You can also bypass the auto sign-in feature by
`https://gitlab.example.com/users/sign_in?auto_sign_in=false`.
-### `attribute_statements` **(FREE SELF)**
-
-NOTE:
-This setting should be used only to map attributes that are part of the OmniAuth
-`info` hash schema.
+### Map SAML response attribute names **(FREE SELF)**
-`attribute_statements` is used to map Attribute Names in a SAMLResponse to entries
+You can use `attribute_statements` to map attribute names in a SAML response to entries
in the OmniAuth [`info` hash](https://github.com/omniauth/omniauth/wiki/Auth-Hash-Schema#schema-10-and-later).
-For example, if your SAMLResponse contains an Attribute called `EmailAddress`,
+NOTE:
+Only use this setting to map attributes that are part of the OmniAuth `info` hash schema.
+
+For example, if your `SAMLResponse` contains an Attribute called `EmailAddress`,
specify `{ email: ['EmailAddress'] }` to map the Attribute to the
corresponding key in the `info` hash. URI-named Attributes are also supported, for example,
`{ email: ['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'] }`.
-This setting allows you tell GitLab where to look for certain attributes required
-to create an account. Like mentioned above, if your IdP sends the user's email
-address as `EmailAddress` instead of `email`, let GitLab know by setting it on
-your configuration:
+Use this setting to tell GitLab where to look for certain attributes required
+to create an account. If your IdP sends the user's email address as `EmailAddress`
+instead of `email`, let GitLab know by setting it on your configuration:
```yaml
args: {
@@ -566,7 +724,7 @@ args: {
This also sets the `username` attribute in your SAML Response to the username in GitLab.
-### `allowed_clock_drift`
+### Allow for clock drift
The clock of the Identity Provider may drift slightly ahead of your system clocks.
To allow for a small amount of clock drift, you can use `allowed_clock_drift` in
@@ -585,9 +743,11 @@ args: {
}
```
-### `uid_attribute`
+### Designate a unique attribute for the `uid`
-By default, the `uid` is set as the `name_id` in the SAML response. If you'd like to designate a unique attribute for the `uid`, you can set the `uid_attribute`. In the example below, the value of `uid` attribute in the SAML response is set as the `uid_attribute`.
+By default, the `uid` is set as the `name_id` in the SAML response. To designate
+a unique attribute for the `uid`, you can set the `uid_attribute`. In the following
+example, the value of `uid` attribute in the SAML response is set as the `uid_attribute`.
```yaml
args: {
@@ -600,59 +760,19 @@ args: {
}
```
-Make sure you read the [Security](#security) section before changing this value.
-
-## Response signature validation (required)
-
-We require Identity Providers to sign SAML responses to ensure that the assertions are
-not tampered with.
-
-This prevents user impersonation and prevents privilege escalation when specific group
-membership is required. Typically this:
+Before setting the `uid` to a unique attribute, make sure that you have configured
+the following attributes so your SAML users cannot change them:
-- Is configured using `idp_cert_fingerprint`.
-- Includes the full certificate in the response, although if your Identity Provider
- doesn't support this, you can directly configure GitLab using the `idp_cert` option.
+- [`NameID`](../user/group/saml_sso/index.md#nameid).
+- `Email` when used with `omniauth_auto_link_saml_user`.
-Example configuration with `idp_cert_fingerprint`:
-
-```yaml
-args: {
- assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
- idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
- idp_sso_target_url: 'https://login.example.com/idp',
- issuer: 'https://gitlab.example.com',
- name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
-}
-```
+If users can change these attributes, they can sign in as other authorized users.
+See your SAML IdP documentation for information on how to make these attributes
+unchangeable.
-Example configuration with `idp_cert`:
+## Assertion encryption (optional)
-```yaml
-args: {
- assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
- idp_cert: '-----BEGIN CERTIFICATE-----
- <redacted>
- -----END CERTIFICATE-----',
- idp_sso_target_url: 'https://login.example.com/idp',
- issuer: 'https://gitlab.example.com',
- name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
-}
-```
-
-If the response signature validation is configured incorrectly, you can see error messages
-such as:
-
-- A key validation error.
-- Digest mismatch.
-- Fingerprint mismatch.
-
-Refer to the [troubleshooting section](#troubleshooting) for more information on
-debugging these errors.
-
-## Assertion Encryption (optional)
-
-GitLab requires the use of TLS encryption with SAML, but in some cases there can be a
+GitLab requires the use of TLS encryption with SAML 2.0, but in some cases there can be a
need for additional encryption of the assertions.
This may be the case, for example, if you terminate TLS encryption early at a load
@@ -679,7 +799,7 @@ Your Identity Provider encrypts the assertion with the public certificate of Git
NOTE:
This integration uses the `certificate` and `private_key` settings for both assertion encryption and request signing.
-## Request signing (optional)
+## Sign SAML authentication requests (optional)
Another optional configuration is to sign SAML authentication requests. GitLab
SAML Requests use the SAML redirect binding, so this isn't necessary (unlike the
@@ -713,27 +833,21 @@ args: {
GitLab signs the request with the provided private key. GitLab includes the configured public x500 certificate in the metadata for your Identity Provider to validate the signature of the received request with. For more information on this option, see the [Ruby SAML gem documentation](https://github.com/onelogin/ruby-saml/tree/v1.7.0). The Ruby SAML gem is used by the [OmniAuth SAML gem](https://github.com/omniauth/omniauth-saml) to implement the client side of the SAML authentication.
-## Security
-
-Avoid user control of the following attributes:
+## Password generation for users created through SAML
-- [`NameID`](../user/group/saml_sso/index.md#nameid)
-- `Email` when used with `omniauth_auto_link_saml_user`
-
-These attributes define the SAML user. If users can change these attributes, they can impersonate others.
-
-Refer to the documentation for your SAML Identity Provider for information on how to fix these attributes.
+The [Generated passwords for users created through integrated authentication](../security/passwords_for_integrated_authentication_methods.md) guide provides an overview of how GitLab generates and sets passwords for users created via SAML.
-## Passwords for users created via SAML
+Users authenticated with SSO or SAML must not use a password for Git operations over HTTPS. These users can do one of the following instead:
-The [Generated passwords for users created through integrated authentication](../security/passwords_for_integrated_authentication_methods.md) guide provides an overview of how GitLab generates and sets passwords for users created via SAML.
+- Set up a [personal access token](../user/profile/personal_access_tokens.md).
+- Use the [Git Credential Manager](../user/profile/account/two_factor_authentication.md#git-credential-manager) which securely authenticates using OAuth.
## Link SAML identity for an existing user
A user can manually link their SAML identity to an existing GitLab account by following the steps in
[Enable OmniAuth for an existing user](omniauth.md#enable-omniauth-for-an-existing-user).
-## Configuring Group SAML on a self-managed GitLab instance **(PREMIUM SELF)**
+## Group SAML on a self-managed GitLab instance **(PREMIUM SELF)**
For information on the GitLab.com implementation, please see the [SAML SSO for GitLab.com groups page](../user/group/saml_sso).
@@ -741,10 +855,10 @@ Group SAML SSO helps if you have to allow access via multiple SAML identity prov
To proceed with configuring Group SAML SSO instead, enable the `group_saml` OmniAuth provider. This can be done from:
-- `gitlab.rb` for [Omnibus GitLab installations](#omnibus-installations).
-- `gitlab/config/gitlab.yml` for [source installations](#source-installations).
+- `gitlab.rb` for Omnibus GitLab installations.
+- `gitlab/config/gitlab.yml` for source installations.
-### Limitations
+### Self-managed instance group SAML limitations
Group SAML on a self-managed instance is limited when compared to the recommended
[instance-wide SAML](../user/group/saml_sso/index.md). The recommended solution allows you to take advantage of:
@@ -755,7 +869,7 @@ Group SAML on a self-managed instance is limited when compared to the recommende
- [Administrator groups](#administrator-groups).
- [Auditor groups](#auditor-groups).
-### Omnibus installations
+For Omnibus installations:
1. Make sure GitLab is
[configured with HTTPS](../install/installation.md#using-https).
@@ -766,7 +880,7 @@ Group SAML on a self-managed instance is limited when compared to the recommende
gitlab_rails['omniauth_providers'] = [{ name: 'group_saml' }]
```
-### Source installations
+For installations from source:
1. Make sure GitLab is
[configured with HTTPS](../install/installation.md#using-https).
@@ -779,78 +893,39 @@ Group SAML on a self-managed instance is limited when compared to the recommende
- { name: 'group_saml' }
```
-## Providers
-
-GitLab support of SAML means that you can sign in to GitLab with a wide range of identity providers.
-Your identity provider may have additional documentation. Some identity providers include
-documentation on how to use SAML to sign in to GitLab.
-
-Examples:
-
-- [ADFS (Active Directory Federation Services)](https://learn.microsoft.com/en-us/windows-server/identity/ad-fs/operations/create-a-relying-party-trust)
-- [Auth0](https://auth0.com/docs/authenticate/protocols/saml/saml-sso-integrations/configure-auth0-saml-identity-provider)
-
-GitLab provides the following setup notes for guidance only.
-If you have any questions on configuring the SAML app, please contact your provider's support.
-
-### Okta setup notes
-
-1. In the Okta administrator section choose **Applications**.
-1. When the app screen comes up you see another button to **Create App Integration** and
- choose SAML 2.0 on the next screen.
-1. Optionally, you can add a logo
- (you can choose it from <https://about.gitlab.com/press/>). You must
- crop and resize it.
-1. Next, fill in the SAML general configuration with
- the assertion consumer service URL as "Single sign-on URL" and
- the issuer as "Audience URI" along with the [NameID](../user/group/saml_sso/index.md#nameid) and [assertions](#assertions).
-1. The last part of the configuration is the feedback section where you can
- just say you're a customer and creating an app for internal use.
-1. When you have your app you can see a few tabs on the top of the app's
- profile. Select the SAML 2.0 configuration instructions button.
-1. On the screen that comes up take note of the
- **Identity Provider Single Sign-On URL** which you can use for the
- `idp_sso_target_url` on your GitLab configuration file.
-1. **Before you leave Okta, make sure you add your user and groups if any.**
-
-### Google workspace setup notes
-
-The following guidance is based on this Google Workspace article, on how to [Set up your own custom SAML application](https://support.google.com/a/answer/6087519?hl=en):
-
-Make sure you have access to a Google Workspace [Super Admin](https://support.google.com/a/answer/2405986#super_admin) account.
- Use the information below and follow the instructions in the linked Google Workspace article.
-
-| | Typical value | Description |
-|------------------|--------------------------------------------------|----------------------------------------------------------|
-| Name of SAML App | GitLab | Other names OK. |
-| ACS URL | `https://<GITLAB_DOMAIN>/users/auth/saml/callback` | ACS is short for Assertion Consumer Service. |
-| GITLAB_DOMAIN | `gitlab.example.com` | Set to the domain of your GitLab instance. |
-| Entity ID | `https://gitlab.example.com` | A value unique to your SAML app, set it to the `issuer` in your GitLab configuration. |
-| Name ID format | EMAIL | Required value. Also known as `name_identifier_format` |
-| Name ID | Primary email address | Make sure someone receives content sent to that address |
-| First name | `first_name` | Required value to communicate with GitLab. |
-| Last name | `last_name` | Required value to communicate with GitLab. |
+## Additional configuration for SAML apps on your IdP
-You also must setup the following SAML attribute mappings:
+When configuring a SAML app on the IdP, your identity provider may require additional configuration, such as the following:
-| Google Directory attributes | App attributes |
-|-----------------------------------|----------------|
-| Basic information > Email | `email` |
-| Basic Information > First name | `first_name` |
-| Basic Information > Last name | `last_name` |
-
-You may also use some of this information when you [configure GitLab](#general-setup).
+| Field | Value | Notes |
+|-------|-------|-------|
+| SAML profile | Web browser SSO profile | GitLab uses SAML to sign users in through their browser. No requests are made directly to the identity provider. |
+| SAML request binding | HTTP Redirect | GitLab (the service provider) redirects users to your identity provider with a base64 encoded `SAMLRequest` HTTP parameter. |
+| SAML response binding | HTTP POST | Specifies how the SAML token is sent by your identity provider. Includes the `SAMLResponse`, which a user's browser submits back to GitLab. |
+| Sign SAML response | Required | Prevents tampering. |
+| X.509 certificate in response | Required | Signs the response and checks against the provided fingerprint. |
+| Fingerprint algorithm | SHA-1 | GitLab uses a SHA-1 hash of the certificate to sign the SAML Response. |
+| Signature algorithm | SHA-1/SHA-256/SHA-384/SHA-512 | Determines how a response is signed. Also known as the digest method, this can be specified in the SAML response. |
+| Encrypt SAML assertion | Optional | Uses TLS between your identity provider, the user's browser, and GitLab. |
+| Sign SAML assertion | Optional | Validates the integrity of a SAML assertion. When active, signs the whole response. |
+| Check SAML request signature | Optional | Checks the signature on the SAML response. |
+| Default RelayState | Optional | Specifies the URL users should end up on after successfully signing in through SAML at your identity provider. |
+| NameID format | Persistent | See [NameID format details](../user/group/saml_sso/index.md#nameid-format). |
+| Additional URLs | Optional | May include the issuer (or identifier) or the assertion consumer service URL in other fields on some providers. |
-When configuring the Google Workspace SAML app, be sure to record the following information:
+For example configurations, see the [notes on specific providers](#set-up-identity-providers).
-| | Value | Description |
-|-------------|--------------|-----------------------------------------------------------------------------------|
-| SSO URL | Depends | Google Identity Provider details. Set to the GitLab `idp_sso_target_url` setting. |
-| Certificate | Downloadable | Run `openssl x509 -in <your_certificate.crt> -noout -fingerprint` to generate the SHA1 fingerprint that can be used in the `idp_cert_fingerprint` setting. |
+## Glossary of common terms
-While the Google Workspace Administrator provides IdP metadata, Entity ID, and SHA-256
-fingerprint, they are not required. GitLab does not need that information to
-connect to the Google Workspace SAML app.
+| Term | Description |
+|--------------------------------|-------------|
+| Identity provider (IdP) | The service which manages your user identities, such as Okta or OneLogin. |
+| Service provider (SP) | GitLab can be configured as a SAML 2.0 SP. |
+| Assertion | A piece of information about a user's identity, such as their name or role. Also known as claims or attributes. |
+| Single Sign-On (SSO) | Name of authentication scheme. |
+| Assertion consumer service URL | The callback on GitLab where users are redirected after successfully authenticating with the identity provider. |
+| Issuer | How GitLab identifies itself to the identity provider. Also known as a "Relying party trust identifier". |
+| Certificate fingerprint | Used to confirm that communications over SAML are secure by checking that the server is signing communications with the correct certificate. Also known as a certificate thumbprint. |
## Troubleshooting
diff --git a/doc/operations/error_tracking.md b/doc/operations/error_tracking.md
index 85b64eb7b3e..d0a208c995b 100644
--- a/doc/operations/error_tracking.md
+++ b/doc/operations/error_tracking.md
@@ -127,17 +127,14 @@ Marking an error as resolved indicates that the error has stopped firing events.
If another event occurs, the error reverts to unresolved.
-## Integrated error tracking
+## Integrated error tracking **(FREE SAAS)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/329596) in GitLab 14.4.
> - [Disabled](https://gitlab.com/gitlab-org/gitlab/-/issues/353639) in GitLab 14.9 [with a flag](../administration/feature_flags.md) named `integrated_error_tracking`. Disabled by default.
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/7586) in GitLab 15.6.
-FLAG:
-By default this feature is not available. To make it available on self-managed GitLab, ask an
-administrator to [enable the feature flag](../administration/feature_flags.md)
-named `integrated_error_tracking`. The feature is not ready for production use.
-On GitLab.com, this feature is available.
+NOTE:
+Available only on GitLab.com. This feature is currently in [Open Beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#open-beta).
Integrated error tracking is a lightweight alternative to Sentry backend.
You still use Sentry SDK with your application. But you don't need to deploy Sentry
@@ -149,9 +146,8 @@ settings. By using a GitLab-provided DSN, your application connects to GitLab to
Those errors are stored in the GitLab database and rendered by the GitLab UI, in the same way as
Sentry integration.
-In GitLab 15.6 and later, the integrated error tracking is available as an
-[open Beta](../policy/alpha-beta-support.md#beta-features).
-It now uses a new backend based on the ClickHouse database that enables better scalability.
+In GitLab 15.6 and later, the integrated error tracking
+uses a new backend based on the ClickHouse database that enables better scalability.
Integrated error tracking remains limited in comparison to the Sentry backend, as only a small subset of the
Sentry API is implemented.
diff --git a/doc/operations/incident_management/alerts.md b/doc/operations/incident_management/alerts.md
index d6293cf1479..0dffa15351c 100644
--- a/doc/operations/incident_management/alerts.md
+++ b/doc/operations/incident_management/alerts.md
@@ -39,7 +39,7 @@ The alert list displays the following information:
NOTE:
Check out a live example available from the
-[`tanuki-inc` project page](https://gitlab-examples-ops-incident-setup-everyone-tanuki-inc.34.69.64.147.nip.io/)
+[`tanuki-inc` project page](https://gitlab.com/gitlab-examples/ops/incident-setup/everyone/tanuki-inc)
in GitLab to examine alerts in action.
## Alert severity
@@ -113,7 +113,7 @@ timeline of the alert's investigation and assignment history.
The following actions result in a system note:
- [Updating the status of an alert](#change-an-alerts-status)
-- [Creating an incident based on an alert](#create-an-incident-from-an-alert)
+- [Creating an incident based on an alert](manage_incidents.md#from-an-alert)
- [Assignment of an alert to a user](#assign-an-alert)
- [Escalation of an alert to on-call responders](paging.md#escalating-an-alert)
@@ -149,7 +149,7 @@ To change an alert's status:
1. On the right sidebar, select **Edit**.
1. Select a status.
-To stop email notifications for alert reoccurrences in projects with [email notifications enabled](paging.md#email-notifications-for-alerts),
+To stop email notifications for alert recurrences in projects with [email notifications enabled](paging.md#email-notifications-for-alerts),
change the alert's status away from **Triggered**.
#### Resolve an alert by closing the linked incident
@@ -158,8 +158,8 @@ Prerequisites:
- You must have at least the Reporter role.
-When you close an [incident](incidents.md) that is linked to an alert,
-the linked alert's status changes to **Resolved**.
+When you [close an incident](manage_incidents.md#close-an-incident) that is linked to an alert,
+GitLab [changes the alert's status](#change-an-alerts-status) to **Resolved**.
You are then credited with the alert's status change.
#### As an on-call responder **(PREMIUM)**
@@ -173,25 +173,10 @@ Changing the status has the following effects:
- To **Resolved**: silences all on-call pages for the alert.
- From **Resolved** to **Triggered**: restarts the alert escalating.
-In GitLab 15.1 and earlier, updating the status of an [alert with an associated incident](alerts.md#create-an-incident-from-an-alert)
+In GitLab 15.1 and earlier, updating the status of an [alert with an associated incident](manage_incidents.md#from-an-alert)
also updates the incident status. In [GitLab 15.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356057),
the incident status is independent and does not update when the alert status changes.
-### Create an incident from an alert
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217745) in GitLab 13.1.
-
-The Alert detail view enables you to create an issue with a
-description populated from an alert. To create the issue,
-select the **Create Issue** button. You can then view the issue from the
-alert by selecting the **View Issue** button.
-
-You can also [create incidents for alerts automatically](incidents.md#create-incidents-automatically).
-
-Closing a GitLab issue associated with an alert [changes the alert's status](#change-an-alerts-status) to
-**Resolved**. See [Alert List](#alert-list) for more details
-about alert statuses.
-
### Assign an alert
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab 13.1.
@@ -225,18 +210,7 @@ and clear the user from the list of assignees, or select **Unassigned**.
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab 13.1.
-You can manually create [To-Do list items](../../user/todos.md) for yourself
-from the Alert details screen, and view them later on your **To-Do List**. To
-add a to-do item:
-
-1. Display the list of current alerts:
-
- 1. On the top bar, select **Main menu > Projects** and find your project.
- 1. On the left sidebar, select **Monitor > Alerts**.
-
-1. Select your desired alert to display its **Alert Management Details View**.
-1. On the right sidebar, select **Add a to do**:
-
- ![Alert Details Add a to do](img/alert_detail_add_todo_v13_9.png)
+You can manually create a [to-do item](../../user/todos.md) for yourself
+from an alert, and view it later on your **To-Do List**.
-To view your To-Do List, on the top bar, select **To-Do List** (**{todo-done}**).
+To add a to-do item, on the right sidebar, select **Add a to do**.
diff --git a/doc/operations/incident_management/img/alert_detail_add_todo_v13_9.png b/doc/operations/incident_management/img/alert_detail_add_todo_v13_9.png
deleted file mode 100644
index 5beb1cd0bfb..00000000000
--- a/doc/operations/incident_management/img/alert_detail_add_todo_v13_9.png
+++ /dev/null
Binary files differ
diff --git a/doc/operations/incident_management/img/incident_list_create_v13_3.png b/doc/operations/incident_management/img/incident_list_create_v13_3.png
deleted file mode 100644
index a000c849099..00000000000
--- a/doc/operations/incident_management/img/incident_list_create_v13_3.png
+++ /dev/null
Binary files differ
diff --git a/doc/operations/incident_management/img/incident_list_v14_9.png b/doc/operations/incident_management/img/incident_list_v14_9.png
deleted file mode 100644
index 4cf6f0e6522..00000000000
--- a/doc/operations/incident_management/img/incident_list_v14_9.png
+++ /dev/null
Binary files differ
diff --git a/doc/operations/incident_management/img/incident_list_v15_6.png b/doc/operations/incident_management/img/incident_list_v15_6.png
new file mode 100644
index 00000000000..fe2a91e2eba
--- /dev/null
+++ b/doc/operations/incident_management/img/incident_list_v15_6.png
Binary files differ
diff --git a/doc/operations/incident_management/img/new_incident_create_v13_4.png b/doc/operations/incident_management/img/new_incident_create_v13_4.png
deleted file mode 100644
index 458532736bd..00000000000
--- a/doc/operations/incident_management/img/new_incident_create_v13_4.png
+++ /dev/null
Binary files differ
diff --git a/doc/operations/incident_management/img/pagerduty_incidents_integration_v13_3.png b/doc/operations/incident_management/img/pagerduty_incidents_integration_v13_3.png
deleted file mode 100644
index 08a45d001c2..00000000000
--- a/doc/operations/incident_management/img/pagerduty_incidents_integration_v13_3.png
+++ /dev/null
Binary files differ
diff --git a/doc/operations/incident_management/img/timeline_view_toggle_v14_10.png b/doc/operations/incident_management/img/timeline_view_toggle_v14_10.png
deleted file mode 100644
index 90f1d205fed..00000000000
--- a/doc/operations/incident_management/img/timeline_view_toggle_v14_10.png
+++ /dev/null
Binary files differ
diff --git a/doc/operations/incident_management/incident_timeline_events.md b/doc/operations/incident_management/incident_timeline_events.md
index 58448222356..4af5a815929 100644
--- a/doc/operations/incident_management/incident_timeline_events.md
+++ b/doc/operations/incident_management/incident_timeline_events.md
@@ -78,13 +78,15 @@ The comment is shown on the incident timeline as a timeline event.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/375280) in GitLab 15.6.
-A new timeline event is created when someone [changes the severity](incidents.md#change-severity)
+A new timeline event is created when someone [changes the severity](manage_incidents.md#change-severity)
of an incident.
![Incident timeline event for severity change](img/timeline_event_for_severity_change_v15_6.png)
## Delete an event
+> Ability to delete an event when editing it [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/372265) in GitLab 15.7.
+
You can also delete timeline events.
Prerequisites:
@@ -99,3 +101,9 @@ To delete a timeline event:
1. Select the **Timeline** tab.
1. On the right of a timeline event, select **More actions** (**{ellipsis_v}**) and then select **Delete**.
1. To confirm, select **Delete Event**.
+
+Alternatively:
+
+1. On the right of a timeline event, select **More actions** (**{ellipsis_v}**) and then select **Edit**.
+1. Select **Delete**.
+1. To confirm, select **Delete event**.
diff --git a/doc/operations/incident_management/incidents.md b/doc/operations/incident_management/incidents.md
index a5d38b1a27c..5cfb8a77fc9 100644
--- a/doc/operations/incident_management/incidents.md
+++ b/doc/operations/incident_management/incidents.md
@@ -6,165 +6,78 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Incidents **(FREE)**
-Incidents are critical entities in incident management workflows. They represent
-a service disruption or outage that needs to be restored urgently. GitLab provides
-tools for the triage, response, and remediation of incidents.
+An incident is a service disruption or outage that needs to be restored urgently.
+Incidents are critical in incident management workflows.
+Use GitLab to triage, respond, and remediate incidents.
-## Incident creation
+## Incidents list
-You can create an incident manually or automatically.
+When you [view the incidents list](manage_incidents.md#view-incidents-list), it contains the following:
-### Create incidents manually
-
-> - [Moved](https://gitlab.com/gitlab-org/monitor/monitor/-/issues/24) to GitLab Free in 13.3.
-> - [Permission changed](https://gitlab.com/gitlab-org/gitlab/-/issues/336624) from Guest to Reporter in GitLab 14.5.
-> - Automatic application of the `incident` label [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/290964) in GitLab 14.8.
-
-If you have at least Reporter [permissions](../../user/permissions.md),
-you can create an incident manually from the Incidents List or the Issues List.
-
-To create an incident from the Incidents List:
-
-1. Navigate to **Monitor > Incidents** and select **Create Incident**.
-1. Create a new issue using the `incident` template.
-
-![Incident List Create](img/incident_list_create_v13_3.png)
-
-To create an incident from the Issues List:
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230857) in GitLab 13.4.
-
-1. Go to **Issues > List**, and select **New issue**.
-1. In the **Type** dropdown list, select **Incident**. Only fields relevant to
- incidents are displayed on the page.
-1. Create the incident as needed, and select **Create issue** to save the
- incident.
-
-![Incident List Create](img/new_incident_create_v13_4.png)
-
-### Create incidents automatically **(ULTIMATE)**
+- **State**: To filter incidents by their state, select **Open**, **Closed**,
+ or **All** above the incident list.
+- **Search**: Search for incident titles and descriptions or [filter the list](#filter-the-incidents-list).
+- **Severity**: Severity of a particular incident, which can be one of the following
+ values:
-With at least the Maintainer role, you can enable
-GitLab to create incident automatically whenever an alert is triggered:
+ - **{severity-critical}** Critical - S1
+ - **{severity-high}** High - S2
+ - **{severity-medium}** Medium - S3
+ - **{severity-low}** Low - S4
+ - **{severity-unknown}** Unknown
-1. Navigate to **Settings > Monitor > Incidents** and expand **Incidents**.
-1. Check the **Create an incident** checkbox.
-1. To customize the incident, select an
- [issue template](../../user/project/description_templates.md#create-an-issue-template),
- to include in the [incident summary](#summary).
-1. To send [an email notification](paging.md#email-notifications-for-alerts) to users
- with the Developer role, select
- **Send a separate email notification to Developers**. Email notifications are
- also sent to users with the **Maintainer** and **Owner** roles.
-1. Select **Save changes**.
+- **Incident**: The title of the incident, which attempts to capture the
+ most meaningful information.
+- **Status**: The status of the incident, which can be one of the following values:
-### Create incidents via the PagerDuty webhook
+ - Triggered
+ - Acknowledged
+ - Resolved
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/119018) in GitLab 13.3.
+ In the Premium or Ultimate tier, this field is also linked to [on-call escalation](paging.md#escalating-an-incident) for the incident.
-You can set up a webhook with PagerDuty to automatically create a GitLab incident
-for each PagerDuty incident. This configuration requires you to make changes
-in both PagerDuty and GitLab:
+- **Date created**: How long ago the incident was created. This field uses the
+ standard GitLab pattern of `X time ago`. Hover over this value to see the exact date and time formatted according to your locale.
+- **Assignees**: The user assigned to the incident.
+- **Published**: Whether the incident is published to a [status page](status_page.md).
-1. Sign in as a user with the Maintainer role.
-1. Navigate to **Settings > Monitor > Incidents** and expand **Incidents**.
-1. Select the **PagerDuty integration** tab:
+![Incidents List](img/incident_list_v15_6.png)
- ![PagerDuty incidents integration](img/pagerduty_incidents_integration_v13_3.png)
+For an example of the incident list in action, visit this
+[demo project](https://gitlab.com/gitlab-org/monitor/monitor-sandbox/-/incidents).
-1. Activate the integration, and save the changes in GitLab.
-1. Copy the value of **Webhook URL** for use in a later step.
-1. Follow the steps described in the
- [PagerDuty documentation](https://support.pagerduty.com/docs/webhooks)
- to add the webhook URL to a PagerDuty webhook integration.
+### Sort the incident list
-To confirm the integration is successful, trigger a test incident from PagerDuty to
-confirm that a GitLab incident is created from the incident.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229534) in GitLab 13.3: incidents are sorted by created date by default.
-## Incident list
+The incident list shows incidents sorted by incident created date, showing the newest first.
-Whether you can view an incident depends on the [project visibility level](../../user/public_access.md) and
-the incident's confidentiality status:
+To sort by another column, or to change the sorting order, select the column.
-- Public project and a non-confidential incident: You don't have to be a member of the project.
-- Private project and non-confidential incident: You must have at least the Guest role for the project.
-- Confidential incident (regardless of project visibility): You must have at least the Reporter.
+The columns you can sort by:
-The Incident list is available at **Monitor > Incidents**
-in your project's sidebar. The list contains the following metrics:
+- Severity
+- Status
+- Time to SLA
+- Published
-![Incident List](img/incident_list_v14_9.png)
+### Filter the incidents list
-- **State** - To filter incidents by their state, select **Open**, **Closed**,
- or **All** above the incident list.
-- **Search** - The Incident list supports a simple free text search, which filters
- on the **Title** and **Incident** fields.
-- **Severity** - Severity of a particular incident, which can be one of the following
- values:
- - **{severity-critical}** **Critical - S1**
- - **{severity-high}** **High - S2**
- - **{severity-medium}** **Medium - S3**
- - **{severity-low}** **Low - S4**
- - **{severity-unknown}** **Unknown**
-
- [Editing incident severity](#change-severity) on the incident details page was
- [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229402) in GitLab 13.4.
-
-- **Incident** - The description of the incident, which attempts to capture the
- most meaningful data.
-- **Status** - The status of the incident, which can be one of the following values:
- - **Triggered**
- - **Acknowledged**
- - **Resolved**
-
- In GitLab Premium, this field is also linked to [on-call escalation](paging.md#escalating-an-incident) for the incident.
-
-- **Date created** - How long ago the incident was created. This field uses the
- standard GitLab pattern of `X time ago`, but is supported by a granular date/time
- tooltip depending on the user's locale.
-- **Assignees** - The user assigned to the incident.
-- **Published** - Displays a green check mark (**{check-circle}**) if the incident is published
- to a [Status Page](status_page.md).
-
-The Incident list displays incidents sorted by incident created date.
-([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229534) in GitLab 13.3.)
-To see if a column is sortable, point your mouse at the header. Sortable columns
-display an arrow next to the column name.
-
-Incidents share the [Issues API](../../api/issues.md).
-
-NOTE:
-For a live example of the incident list in action, visit this
-[demo project](https://gitlab.com/gitlab-examples/ops/incident-setup/everyone/tanuki-inc/-/incidents).
+To filter the incident list by author or assignee, enter these values in the search box.
## Incident details
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230847) in GitLab 13.4.
-
-1. On the top bar, select **Main menu > Projects** and find your project.
-1. On the left sidebar, select **Monitor > Incidents**.
-1. Select an incident from the list.
-When you take any of these actions on an incident, GitLab logs a system note and
-displays it in the Incident Details view:
-
-- Updating the severity of an incident
- ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42358) in GitLab 13.5.)
-
-For live examples of GitLab incidents, visit the `tanuki-inc` project's
-[incident list page](https://gitlab.com/gitlab-examples/ops/incident-setup/everyone/tanuki-inc/-/incidents).
-Select any incident in the list to display its incident details page.
-
### Summary
-The summary section for incidents provides both critical details about the
-incident and the contents of the issue template (if applicable). The highlighted
+The summary section for incidents provides critical details about the
+incident and the contents of the issue template (if [selected](../metrics/alerts.md#trigger-actions-from-alerts)). The highlighted
bar at the top of the incident displays from left to right:
- The link to the original alert.
- The alert start time.
- The event count.
-Beneath the highlight bar, GitLab displays a summary that includes the following fields:
+Below the highlight bar, a summary includes the following fields:
- Start time
- Severity
@@ -172,15 +85,18 @@ Beneath the highlight bar, GitLab displays a summary that includes the following
- Monitoring tool
The incident summary can be further customized using
-[GitLab Flavored Markdown](../../user/markdown.md). If the corresponding alert
-[provided Markdown for the incident](../metrics/alerts.md#trigger-actions-from-alerts),
-then the Markdown is appended to the summary after the above alert fields. If an
-[incident template](#create-incidents-automatically) is configured for the
-project, then the template content is appended at the end.
+[GitLab Flavored Markdown](../../user/markdown.md).
+
+If an incident is [created from an alert](../metrics/alerts.md#trigger-actions-from-alerts)
+that provided Markdown for the incident, then the Markdown is appended to the summary.
+If an incident template is configured for the project, then the template content is appended at the end.
Comments are displayed in threads, but can be displayed chronologically
[by toggling on the recent updates view](#recent-updates-view).
+When you make changes to an incident, GitLab creates system notes and
+displays them below the summary.
+
### Metrics **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/235994) in GitLab 13.8.
@@ -198,6 +114,8 @@ If you add a link, you can access the original graph by selecting the hyperlink
### Alert details
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230847) in GitLab 13.4.
+
Incidents show the details of linked alerts in a separate tab. To populate this
tab, the incident must have been created with a linked alert. Incidents
created automatically from alerts have this
@@ -216,144 +134,60 @@ Read more about [timeline events](incident_timeline_events.md) and how to enable
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227836) in GitLab 13.5.
-To quickly see the latest updates on an incident, select
-**{history}** **Turn recent updates view on** in the comment bar to display comments
-un-threaded and ordered chronologically, newest to oldest:
-
-![Recent updates view toggle](img/timeline_view_toggle_v14_10.png)
+To see the latest updates on an incident, select
+**Turn recent updates view on** (**{history}**) on the comment bar. Comments display
+un-threaded and chronologically, newest to oldest.
### Service Level Agreement countdown timer **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241663) in GitLab 13.5.
You can enable the Service Level Agreement Countdown timer on incidents to track
-the Service Level Agreements (SLAs) you hold with your customers. The timer is
+the Service Level Agreements (SLA) you hold with your customers. The timer is
automatically started when the incident is created, and shows the time
remaining before the SLA period expires. The timer is also dynamically updated
every 15 minutes so you do not have to refresh the page to see the time remaining.
+
+Prerequisites:
+
+- You must have at least the Maintainer role for the project.
+
To configure the timer:
-1. Navigate to **Settings > Monitor**.
-1. Scroll to **Incidents** and select **Expand**, then select the
- **Incident settings** tab.
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > Monitor**.
+1. Expand the **Incidents** section, then select the **Incident settings** tab.
1. Select **Activate "time to SLA" countdown timer**.
1. Set a time limit in increments of 15 minutes.
1. Select **Save changes**.
-After you enable the SLA countdown timer, the **Time to SLA** attribute is displayed
-as a column in the Incidents List, and as a field on newly created Incidents. If
+After you enable the SLA countdown timer, the **Time to SLA** column is available in the
+incidents list and as a field on new incidents. If
the incident isn't closed before the SLA period ends, GitLab adds a `missed::SLA`
label to the incident.
-## Assign incidents
-
-Assign incidents to users that are actively responding. Select **Edit** in the
-right-hand side bar to select or clear assignees.
-
-## Associate a milestone
-
-Associate an incident to a milestone by selecting **Edit** next to the milestone feature in the right-hand side bar.
-
-## Change severity
-
-See [Incident List](#incident-list) for a full description of the severity levels available.
-Select **Edit** in the right-hand side bar to change the severity of an incident.
-
-You can also change the severity using the [`/severity` quick action](../../user/project/quick_actions.md).
-
-## Add a to-do item
-
-Add a to-do for incidents that you want to track in your to-do list. Select
-**Add a to do** at the top of the right-hand side bar to add a to-do item.
-
-## Change incident status
-
-> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5716) in GitLab 14.9 [with a flag](../../administration/feature_flags.md) named `incident_escalations`. Disabled by default.
-> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/345769) in GitLab 14.10.
-> - [Feature flag `incident_escalations`](https://gitlab.com/gitlab-org/gitlab/-/issues/345769) removed in GitLab 15.1.
-
-For users with the Developer role or higher, select **Edit** in the **Status** section of the
-right-hand side bar of an incident, then select a status. **Triggered** is the default status for
-new incidents.
-
-In projects with GitLab Premium, on-call responders can respond to [incident pages](paging.md#escalating-an-incident)
-by changing the status. Setting the status to:
-
-- **Resolved** silences on-call pages for the alert.
-- **Acknowledged** limits on-call pages based on the selected [escalation policy](#change-escalation-policy).
-- **Triggered** from **Resolved** restarts the incident escalating from the beginning.
-
-In GitLab 15.1 and earlier, updating the status of an [incident created from an alert](alerts.md#create-an-incident-from-an-alert)
-also updates the alert status. In [GitLab 15.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356057),
-the alert status is independent and does not update when the incident status changes.
-
-## Change escalation policy **(PREMIUM)**
-
-> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5716) in GitLab 14.9 [with a flag](../../administration/feature_flags.md) named `incident_escalations`. Disabled by default.
-> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/345769) in GitLab 14.10.
-> - [Feature flag `incident_escalations`](https://gitlab.com/gitlab-org/gitlab/-/issues/345769) removed in GitLab 15.1.
-
-For users with the Developer role or higher, select **Edit** in the **Escalation policy** section of
-the right-hand side bar of an incident, then select a policy. By default, new incidents do not have
-an escalation policy selected.
-
-Selecting an escalation policy updates the incident status to **Triggered** and begins
-[escalating the incident to on-call responders](paging.md#escalating-an-incident).
-Deselecting an escalation policy halts escalation. Refer to the [incident status](#change-incident-status)
-to manage on-call paging once escalation has begun.
-
-In GitLab 15.1 and earlier, the escalation policy for [incidents created from alerts](alerts.md#create-an-incident-from-an-alert)
-reflects the alert's escalation policy and cannot be changed. In [GitLab 15.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356057),
-the incident escalation policy is independent and can be changed.
-
-## Manage incidents from Slack
-
-Slack slash commands allow you to control GitLab and view GitLab content without leaving Slack.
-
-Learn how to [set up Slack slash commands](../../user/project/integrations/slack_slash_commands.md)
-and how to [use the available slash commands](../../integration/slash_commands.md).
-
-## Associate Zoom calls
-
-GitLab enables you to [associate a Zoom meeting with an issue](../../user/project/issues/associate_zoom_meeting.md)
-for synchronous communication during incident management. After starting a Zoom
-call for an incident, you can associate the conference call with an issue. Your
-team members can join the Zoom call without requesting a link.
-
-## Linked resources
-
-In an incident, you can add [links to various resources](linked_resources.md),
-for example:
-
-- The incident Slack channel
-- Zoom meeting
-- Resources for resolving the incidents
-
-## Embed metrics in incidents
-
-You can embed metrics anywhere [GitLab Markdown](../../user/markdown.md) is
-used, such as descriptions, comments on issues, and merge requests. Embedding
-metrics helps you share them when discussing incidents or performance issues.
-You can output the dashboard directly into any issue, merge request, epic, or
-any other Markdown text field in GitLab by
-[copying and pasting the link to the metrics dashboard](../metrics/embed.md#embedding-gitlab-managed-kubernetes-metrics).
-
-You can embed both [GitLab-hosted metrics](../metrics/embed.md) and
-[Grafana metrics](../metrics/embed_grafana.md) in incidents and issue
-templates.
-
-## Automatically close incidents via recovery alerts
-
-> - [Introduced for Prometheus Integrations](https://gitlab.com/gitlab-org/gitlab/-/issues/13401) in GitLab 12.5.
-> - [Introduced for HTTP Integrations](https://gitlab.com/gitlab-org/gitlab/-/issues/13402) in GitLab 13.4.
-
-With at least the Maintainer role, you can enable
- GitLab to close an incident automatically when a **Recovery Alert** is received:
-
-1. Navigate to **Settings > Monitor > Incidents** and expand **Incidents**.
-1. Check the **Automatically close associated Incident** checkbox.
-1. Select **Save changes**.
-
-When GitLab receives a **Recovery Alert**, it closes the associated incident.
-This action is recorded as a system message on the incident indicating that it
-was closed automatically by the GitLab Alert bot.
+## Related topics
+
+- [Create an incident](manage_incidents.md#create-an-incident)
+- [Create an incident automatically](../metrics/alerts.md#trigger-actions-from-alerts)
+ whenever an alert is triggered
+- [View incidents list](manage_incidents.md#view-incidents-list)
+- [Assign to a user](manage_incidents.md#assign-to-a-user)
+- [Change incident severity](manage_incidents.md#change-severity)
+- [Change incident status](manage_incidents.md#change-status)
+- [Change escalation policy](manage_incidents.md#change-escalation-policy)
+- [Embed metrics](manage_incidents.md#embed-metrics)
+- [Close an incident](manage_incidents.md#close-an-incident)
+- [Automatically close incidents via recovery alerts](manage_incidents.md#automatically-close-incidents-via-recovery-alerts)
+- [Add a to-do item](../../user/todos.md#create-a-to-do-item)
+- [Add labels](../../user/project/labels.md)
+- [Assign a milestone](../../user/project/milestones/index.md)
+- [Make an incident confidential](../../user/project/issues/confidential_issues.md)
+- [Set a due date](../../user/project/issues/due_dates.md)
+- [Toggle notifications](../../user/profile/notifications.md#edit-notification-settings-for-issues-merge-requests-and-epics)
+- [Track spent time](../../user/project/time_tracking.md)
+- [Add a Zoom meeting to an incident](../../user/project/issues/associate_zoom_meeting.md) the same
+ way you add it to an issue.
+- [Linked resources in incidents](linked_resources.md)
+- Create incidents and receive incident notifications [directly from Slack](slack.md).
+- Use the [Issues API](../../api/issues.md) to interact with incidents.
diff --git a/doc/operations/incident_management/integrations.md b/doc/operations/incident_management/integrations.md
index a54556bd3a2..fdcfbe0cb2c 100644
--- a/doc/operations/incident_management/integrations.md
+++ b/doc/operations/incident_management/integrations.md
@@ -164,7 +164,7 @@ curl --request POST \
The authorization key can be used as the `password`. The `username` is left blank:
- username: `<blank>`
-- password: authorization_key
+- password: `<authorization_key>`
```shell
curl --request POST \
@@ -249,7 +249,7 @@ receives a payload with the end time of the alert set. For HTTP Endpoints
without [custom mappings](#map-fields-in-custom-alerts), the expected
field is `end_time`. With custom mappings, you can select the expected field.
-You can also configure the associated [incident to be closed automatically](../incident_management/incidents.md#automatically-close-incidents-via-recovery-alerts) when the alert resolves.
+You can also configure the associated [incident to be closed automatically](../incident_management/manage_incidents.md#automatically-close-incidents-via-recovery-alerts) when the alert resolves.
## Link to your Opsgenie Alerts **(PREMIUM)**
diff --git a/doc/operations/incident_management/linked_resources.md b/doc/operations/incident_management/linked_resources.md
index 40b2bbdc757..eb289076424 100644
--- a/doc/operations/incident_management/linked_resources.md
+++ b/doc/operations/incident_management/linked_resources.md
@@ -15,9 +15,9 @@ you can add linked resources to an incident issue.
Resources you might want link to:
-- Zoom meetings
-- Slack channels or threads
-- Google Docs
+- The incident Slack channel
+- Zoom meeting
+- Resources for resolving the incidents
## View linked resources of an incident
diff --git a/doc/operations/incident_management/manage_incidents.md b/doc/operations/incident_management/manage_incidents.md
new file mode 100644
index 00000000000..75826c2c55e
--- /dev/null
+++ b/doc/operations/incident_management/manage_incidents.md
@@ -0,0 +1,263 @@
+---
+stage: Monitor
+group: Respond
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Manage incidents **(FREE)**
+
+This page collects instructions for all the things you can do with [incidents](incidents.md) or in relation to them.
+
+## Create an incident
+
+You can create an incident manually or automatically.
+
+### From the incidents list
+
+> - [Moved](https://gitlab.com/gitlab-org/monitor/monitor/-/issues/24) to GitLab Free in 13.3.
+> - [Permission changed](https://gitlab.com/gitlab-org/gitlab/-/issues/336624) from Guest to Reporter in GitLab 14.5.
+> - Automatic application of the `incident` label [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/290964) in GitLab 14.8.
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
+To create an incident from the incidents list:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Monitor > Incidents**.
+1. Select **Create incident**.
+
+### From the issues list
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230857) in GitLab 13.4.
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
+To create an incident from the issues list:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Issues > List**, and select **New issue**.
+1. From the **Type** dropdown list, select **Incident**. Only fields relevant to
+ incidents are available on the page.
+1. Select **Create issue**.
+
+### From an alert
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217745) in GitLab 13.1.
+
+Create an incident issue when viewing an [alert](alerts.md).
+The incident description is populated from the alert.
+
+Prerequisites:
+
+- You must have at least the Developer role for the project.
+
+To create an incident from an alert:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Monitor > Alerts**.
+1. Select your desired alert.
+1. Select **Create incident**.
+
+After an incident is created, to view it from the alert, select **View incident**.
+
+When you [close an incident](#close-an-incident) linked to an alert, GitLab
+[changes the alert's status](alerts.md#change-an-alerts-status) to **Resolved**.
+You are then credited with the alert's status change.
+
+### Automatically, when an alert is triggered **(ULTIMATE)**
+
+In the project settings, you can turn on [creating an incident automatically](../metrics/alerts.md#trigger-actions-from-alerts)
+whenever an alert is triggered.
+
+### Using the PagerDuty webhook
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/119018) in GitLab 13.3.
+> - [PagerDuty V3 Webhook](https://support.pagerduty.com/docs/webhooks) support [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/383029) in GitLab 15.7.
+
+You can set up a webhook with PagerDuty to automatically create a GitLab incident
+for each PagerDuty incident. This configuration requires you to make changes
+in both PagerDuty and GitLab.
+
+Prerequisites:
+
+- You must have at least the Maintainer role for the project.
+
+To set up a webhook with PagerDuty:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > Monitor**
+1. Expand **Incidents**.
+1. Select the **PagerDuty integration** tab.
+1. Turn on the **Active** toggle.
+1. Select **Save integration**.
+1. Copy the value of **Webhook URL** for use in a later step.
+1. To add the webhook URL to a PagerDuty webhook integration, follow the steps described in the [PagerDuty documentation](https://support.pagerduty.com/docs/webhooks#manage-v3-webhook-subscriptions).
+
+To confirm the integration is successful, trigger a test incident from PagerDuty to
+check if a GitLab incident is created from the incident.
+
+## View incidents list
+
+To view the [incidents list](incidents.md#incidents-list):
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Monitor > Incidents**.
+
+To view an incident's [details page](incidents.md#incident-details), select it from the list.
+
+### Who can view an incident
+
+Whether you can view an incident depends on the [project visibility level](../../user/public_access.md) and
+the incident's confidentiality status:
+
+- Public project and a non-confidential incident: You don't have to be a member of the project.
+- Private project and non-confidential incident: You must have at least the Guest role for the project.
+- Confidential incident (regardless of project visibility): You must have at least the Reporter role for the project.
+
+## Assign to a user
+
+Assign incidents to users that are actively responding.
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
+To assign a user:
+
+1. In an incident, on the right sidebar, next to **Assignees**, select **Edit**.
+1. From the dropdown list, select one or [multiple users](../../user/project/issues/multiple_assignees_for_issues.md) to add as **assignees**.
+1. Select any area outside the dropdown list.
+
+## Change severity
+
+> Editing severity on incident details page was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229402) in GitLab 13.4.
+
+See [incident list](incidents.md#incidents-list) for a full description of the severity levels available.
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
+To change an incident's severity:
+
+1. In an incident, on the right sidebar, next to **Severity**, select **Edit**.
+1. From the dropdown list, select the new severity.
+
+You can also change the severity using the `/severity` [quick action](../../user/project/quick_actions.md).
+
+## Change status
+
+> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5716) in GitLab 14.9 [with a flag](../../administration/feature_flags.md) named `incident_escalations`. Disabled by default.
+> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/345769) in GitLab 14.10.
+> - [Feature flag `incident_escalations`](https://gitlab.com/gitlab-org/gitlab/-/issues/345769) removed in GitLab 15.1.
+
+Prerequisites:
+
+- You must have at least the Developer role for the project.
+
+To change the status of an incident:
+
+1. In an incident, on the right sidebar, next to **Status**, select **Edit**.
+1. From the dropdown list, select the new severity.
+
+**Triggered** is the default status for new incidents.
+
+### As an on-call responder **(PREMIUM)**
+
+On-call responders can respond to [incident pages](paging.md#escalating-an-incident)
+by changing the status.
+
+Changing the status has the following effects:
+
+- To **Acknowledged**: limits on-call pages based on the project's [escalation policy](escalation_policies.md).
+- To **Resolved**: silences all on-call pages for the incident.
+- From **Resolved** to **Triggered**: restarts the incident escalating.
+
+In GitLab 15.1 and earlier, changing the status of an [incident created from an alert](#from-an-alert)
+also changes the alert status. In [GitLab 15.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356057),
+the alert status is independent and does not change when the incident status changes.
+
+## Change escalation policy **(PREMIUM)**
+
+Prerequisites:
+
+- You must have at least the Developer role for the project.
+
+To change the escalation policy of an incident:
+
+1. In an incident, on the right sidebar, next to **Escalation policy**, select **Edit**.
+1. From the dropdown list, select the escalation policy.
+
+By default, new incidents do not have an escalation policy selected.
+
+Selecting an escalation policy [changes the incident status](#change-status) to **Triggered** and begins
+[escalating the incident to on-call responders](paging.md#escalating-an-incident).
+
+In GitLab 15.1 and earlier, the escalation policy for [incidents created from alerts](#from-an-alert)
+reflects the alert's escalation policy and cannot be changed. In [GitLab 15.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356057),
+the incident escalation policy is independent and can be changed.
+
+## Embed metrics
+
+You can embed metrics anywhere [GitLab Flavored Markdown](../../user/markdown.md) is
+used, like descriptions or comments. Embedding
+metrics helps you share them when discussing incidents or performance issues.
+
+To embed metrics in a Markdown text box in GitLab,
+[paste the link to the dashboard](../metrics/embed.md#embedding-gitlab-managed-kubernetes-metrics).
+
+You can embed both [GitLab-hosted metrics](../metrics/embed.md) (deprecated) and
+[Grafana metrics](../metrics/embed_grafana.md) in incidents and issue
+templates.
+
+## Close an incident
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
+To close an incident, in the top right, select **Close incident**.
+
+When you close an incident that is linked to an [alert](alerts.md),
+the linked alert's status changes to **Resolved**.
+You are then credited with the alert's status change.
+
+### Automatically close incidents via recovery alerts
+
+> [Introduced for HTTP integrations](https://gitlab.com/gitlab-org/gitlab/-/issues/13402) in GitLab 13.4.
+
+Turn on closing an incident automatically when GitLab receives a recovery alert
+from a HTTP or Prometheus webhook.
+
+Prerequisites:
+
+- You must have at least the Maintainer role for the project.
+
+To configure the setting:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > Monitor**.
+1. Expand the **Incidents** section.
+1. Select the **Automatically close associated incident** checkbox.
+1. Select **Save changes**.
+
+When GitLab receives a recovery alert, it closes the associated incident.
+This action is recorded as a system note on the incident indicating that it
+was closed automatically by the GitLab Alert bot.
+
+## Other actions
+
+Because incidents in GitLab are built on top of [issues](../../user/project/issues/index.md),
+they have the following actions in common:
+
+- [Add a to-do item](../../user/todos.md#create-a-to-do-item)
+- [Add labels](../../user/project/labels.md#assign-and-unassign-labels)
+- [Assign a milestone](../../user/project/milestones/index.md#assign-a-milestone-to-an-issue-or-merge-request)
+- [Make an incident confidential](../../user/project/issues/confidential_issues.md)
+- [Set a due date](../../user/project/issues/due_dates.md)
+- [Toggle notifications](../../user/profile/notifications.md#edit-notification-settings-for-issues-merge-requests-and-epics)
+- [Track time spent](../../user/project/time_tracking.md)
diff --git a/doc/operations/incident_management/paging.md b/doc/operations/incident_management/paging.md
index dcb17048d35..70c67977b73 100644
--- a/doc/operations/incident_management/paging.md
+++ b/doc/operations/incident_management/paging.md
@@ -14,12 +14,11 @@ notifications using the methods described on this page.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/216326) in GitLab 13.1.
-Responders can be paged via Slack using the
-[Slack Notifications Service](../../user/project/integrations/slack.md), which you
-can configure for new alerts and new incidents. After configuring, responders
-receive a **single** page via Slack. To set up Slack notifications on your mobile
-device, make sure to enable notifications for the Slack app on your phone so
-you never miss a page.
+The GitLab for Slack app can be used to receive important incident notifications.
+
+When [the GitLab for Slack app is configured](slack.md), incident responders are notified in Slack
+every time a new incident is declared. To ensure you don't miss any important incident notifications
+on your mobile device, enable notifications for Slack on your phone.
## Email notifications for alerts
@@ -55,12 +54,14 @@ or stop alert escalations by [updating the alert's status](alerts.md#change-an-a
> - [Feature flag `incident_escalations`](https://gitlab.com/gitlab-org/gitlab/-/issues/345769) removed in GitLab 15.1.
For incidents, paging on-call responders is optional for each individual incident.
-To begin escalating the incident, [set the incident's escalation policy](incidents.md#change-escalation-policy).
+
+To begin escalating the incident, [set the incident's escalation policy](manage_incidents.md#change-escalation-policy).
+
For each escalation rule, the designated on-call responders receive one email when
-the rule fires. You can respond to a page or stop incident escalations by
-[updating the incident's status](incidents.md#change-incident-status) or, if applicable,
-[unsetting the incident's escalation policy](incidents.md#change-escalation-policy).
+the rule fires. Respond to a page or stop incident escalations by
+[changing the incident's status](manage_incidents.md#change-status) or
+changing the incident's escalation policy back to **No escalation policy**.
-In GitLab 15.1 and earlier, [incidents created from alerts](alerts.md#create-an-incident-from-an-alert)
+In GitLab 15.1 and earlier, [incidents created from alerts](manage_incidents.md#from-an-alert)
do not support independent escalation. In [GitLab 15.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356057),
all incidents can be escalated independently.
diff --git a/doc/operations/incident_management/slack.md b/doc/operations/incident_management/slack.md
new file mode 100644
index 00000000000..1ab1391ea2a
--- /dev/null
+++ b/doc/operations/incident_management/slack.md
@@ -0,0 +1,117 @@
+---
+stage: Monitor
+group: Respond
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Incident management for Slack **(FREE SAAS)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/344856) in GitLab 15.7 [with a flag](../../administration/feature_flags.md) named `incident_declare_slash_command`. Disabled by default.
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `incident_declare_slash_command`.
+On GitLab.com, this feature is not available.
+The feature is not ready for production use.
+
+Many teams receive alerts and collaborate in real time during incidents in Slack.
+Use the GitLab for Slack app to:
+
+- Create GitLab incidents from Slack.
+- Receive incident notifications.
+<!-- The below content is commented out until these features are implemented in https://gitlab.com/groups/gitlab-org/-/epics/8545 -->
+<!-- - Send important updates between Slack and GitLab incidents. -->
+
+Incident management for Slack is only available for GitLab.com. Some of the functionality
+described might be available for
+[the self-managed Slack app](../../user/project/integrations/slack_slash_commands.md).
+
+To stay up to date, follow [epic 1211](https://gitlab.com/groups/gitlab-org/-/epics/1211).
+
+## Manage an incident from Slack
+
+Prerequisites:
+
+1. Install the [GitLab for Slack app](../../user/project/integrations/gitlab_slack_application.md).
+ This way, you can use slash commands in Slack to create and update GitLab incidents.
+1. Enable [Slack notifications](../../user/project/integrations/slack.md). Be sure to enable
+ notifications for `Issue` events, and to define a Slack channel to receive the relevant notifications.
+1. Authorize GitLab to take actions on behalf of your Slack user.
+ Each user must do this before they can use any of the incident slash commands.
+
+ To start the authorization flow, try executing a non-incident [Slack slash command](../../integration/slash_commands.md),
+ like `/gitlab <project-alias> issue show <id>`.
+ The `<project-alias>` you select must be a project that has the GitLab for Slack app set up.
+ For more context, visit [issue 377548](https://gitlab.com/gitlab-org/gitlab/-/issues/377548).
+
+<!-- The below content is commented out until these features are implemented in https://gitlab.com/groups/gitlab-org/-/epics/8545 -->
+<!--
+To manage incidents, use the following slash commands in Slack:
+
+| Command | Description |
+| ---------------------------------- | ------------------------------------------- |
+| `/gitlab incident declare` | Creates an incident in GitLab. |
+| `/gitlab incident comment <text>` | Adds a comment on a GitLab incident. |
+| `/gitlab incident timeline <text>` | Adds a timeline event to a GitLab incident. |
+| `/gitlab incident close` | Closes an incident in GitLab. |
+-->
+
+After the GitLab for Slack app is configured, you can also use any of the existing [Slack slash commands](../../user/project/integrations/slack_slash_commands.md).
+
+## Declare an incident
+
+To declare a GitLab incident from Slack:
+
+1. In Slack, in any channel or DM, enter the `/gitlab incident declare` slash command.
+1. From the modal, select the relevant incident details, including:
+
+ - The incident title and description.
+ - The project where the incident should be created.
+ - The severity of the incident.
+
+ If there is an existing [incident template](../metrics/alerts.md#trigger-actions-from-alerts) for your
+ project, that template is automatically applied to the description text box. The template is applied
+ only if the description text box is empty.
+
+ You can also include GitLab [quick actions](../../user/project/quick_actions.md) in the description text box.
+ For example, entering `/link https://example.slack.com/archives/123456789 Dedicated Slack channel`
+ adds a dedicated Slack channel to the incident you create. For a complete list of
+ quick actions for incidents, see [Use GitLab quick actions](#use-gitlab-quick-actions).
+1. Optional. Add a link to an existing Zoom meeting.
+1. Select **Create**.
+
+If the incident is successfully created, Slack shows a confirmation notification.
+
+### Use GitLab quick actions
+
+Use [quick actions](../../user/project/quick_actions.md) in the description text box when creating
+a GitLab incident from Slack. The following quick actions might be most relevant to you:
+
+| Command | Description |
+| ------------------------ | ----------------------------------------- |
+| `/assign @user1 @user2` | Adds an assignee to the GitLab incident. |
+| `/label ~label1 ~label2` | Adds labels to the GitLab incident. |
+| `/link <URL> <text>` | Adds a link to a dedicated Slack channel, runbook, or any relevant resource to the `Related resources` section of an incident. |
+| `/zoom <URL>` | Adds a Zoom meeting link to the incident. |
+
+<!-- The below content is commented out until these features are implemented in https://gitlab.com/groups/gitlab-org/-/epics/8545 -->
+<!-- ### Comment on a GitLab incident
+
+To comment on a GitLab incident from Slack, enter the `/gitlab incident comment <text>` slash command.
+Slack shows a prompt asking you to confirm which incident you'd like to post your comment to.
+
+### Add a timeline event
+
+To add a [timeline event](incident_timeline_events.md) to a GitLab incident from Slack, enter the
+`/gitlab incident timeline <text>` slash command.
+Slack shows a prompt asking you to confirm which incident you'd like to add your timeline event to.
+
+### Close an incident
+
+To close a GitLab incident from Slack when it is resolved, enter the `/gitlab incident close`
+slash command.
+Slack shows a prompt asking you to confirm which incident you'd like to close. -->
+
+## Send GitLab incident notifications to Slack
+
+If you have [enabled notifications](#manage-an-incident-from-slack) for issues, you should receive
+notifications to the selected Slack channel every time an incident is opened, closed, or updated.
diff --git a/doc/operations/metrics/alerts.md b/doc/operations/metrics/alerts.md
index ffd304c8897..4b6bc91cc73 100644
--- a/doc/operations/metrics/alerts.md
+++ b/doc/operations/metrics/alerts.md
@@ -6,8 +6,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Set up alerts for Prometheus metrics **(FREE)**
-> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/42640) to GitLab Free in 12.10.
-
After [configuring metrics for your CI/CD environment](index.md), you can set up
alerting for Prometheus metrics, and
[trigger actions from alerts](#trigger-actions-from-alerts) to notify
@@ -15,31 +13,48 @@ your team when environment performance falls outside of the boundaries you set.
## Prometheus cluster integrations
-Alerts are not currently supported for [Prometheus cluster integrations](../../user/clusters/integrations.md).
+Alerts are not supported for [Prometheus cluster integrations](../../user/clusters/integrations.md).
## Trigger actions from alerts **(ULTIMATE)**
-Alerts can be used to trigger actions, like opening an issue automatically
-(disabled by default since `13.1`). To configure the actions:
+> - Introduced in GitLab 13.1: incidents are not created automatically by default .
+> - Mapping common severity values from the alert payload ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50871) in GitLab 13.9.
+
+Turn on creating [incidents](../incident_management/incidents.md) automatically whenever an alert is triggered.
+
+Prerequisites:
+
+- You must have at least the Maintainer role for the project.
-1. Navigate to your project's **Settings > Monitor > Alerts**.
-1. Enable the option to create issues.
-1. Choose the [issue template](../../user/project/description_templates.md) to create the issue from.
-1. Optionally, select whether to send an email notification to the developers of the project.
+To configure the actions:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > Monitor**.
+1. Expand the **Alerts** section, then select the **Alert settings** tab.
+1. Select the **Create an incident** checkbox.
+1. Optional. To customize the incident, from the **Incident template**, select a template to be
+ appended to the [incident summary](../incident_management/incidents.md#summary).
+ If the dropdown list is empty,
+ [create an issue template](../../user/project/description_templates.md#create-an-issue-template) first.
+1. Optional. To send [an email notification](../incident_management/paging.md#email-notifications-for-alerts), select the
+ **Send a single email notification to Owners and Maintainers for new alerts** checkbox.
1. Select **Save changes**.
-After enabling, GitLab automatically opens an issue when an alert is triggered containing
-values extracted from the [`alerts` field in webhook payload](https://prometheus.io/docs/alerting/latest/configuration/#webhook_config):
+### Fields in automatically created incidents
+
+Incidents [created automatically from an alert](#trigger-actions-from-alerts) are filled with
+values extracted from the `alerts` field in the
+[webhook payload](https://prometheus.io/docs/alerting/latest/configuration/#webhook_config):
-- Issue author: `GitLab Alert Bot`
-- Issue title: Extracted from the alert payload fields `annotations/title`, `annotations/summary`, or `labels/alertname`.
-- Issue description: Extracted from alert payload field `annotations/description`.
+- Incident author: `GitLab Alert Bot`
+- Incident title: Extracted from the alert payload fields `annotations/title`, `annotations/summary`, or `labels/alertname`.
+- Incident description: Extracted from alert payload field `annotations/description`.
- Alert `Summary`: A list of properties from the alert's payload.
- `starts_at`: Alert start time from the payload's `startsAt` field
- `full_query`: Alert query extracted from the payload's `generatorURL` field
- Optional list of attached annotations extracted from `annotations/*`
- Alert [GLFM](../../user/markdown.md): GitLab Flavored Markdown from the payload's `annotations/gitlab_incident_markdown` field.
-- Alert Severity ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50871) in GitLab version 13.9):
+- Alert severity:
Extracted from the alert payload field `labels/severity`. Maps case-insensitive
value to [Alert's severity](../incident_management/alerts.md#alert-severity):
- **Critical**: `critical`, `s1`, `p1`, `emergency`, `fatal`, or any value not in this list
@@ -48,23 +63,17 @@ values extracted from the [`alerts` field in webhook payload](https://prometheus
- **Low**: `low`, `s4`, `p4`, `warn`, `warning`
- **Info**: `info`, `s5`, `p5`, `debug`, `information`, `notice`
-To further customize the issue, you can add labels, mentions, or any other supported
+To further customize the incident, you can add labels, mentions, or any other supported
[quick action](../../user/project/quick_actions.md) in the selected issue template,
which applies to all incidents. To limit quick actions or other information to
only specific types of alerts, use the `annotations/gitlab_incident_markdown` field.
-Since [version 12.2](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/63373),
GitLab tags each incident issue with the `incident` label automatically. If the label
-does not yet exist, it is also created automatically.
-
-If the metric exceeds the threshold of the alert for over 5 minutes, GitLab sends
-an email to all Maintainers and Owners of the project.
+does not yet exist, it's created automatically.
### Recovery alerts
-> [From GitLab 12.5](https://gitlab.com/gitlab-org/gitlab/-/issues/13401), when GitLab receives a recovery alert, it automatically closes the associated issue.
-
-The alert in GitLab will be automatically resolved when Prometheus
+The alert in GitLab is automatically resolved when Prometheus
sends a payload with the field `status` set to `resolved`.
-You can also configure the associated [incident to be closed automatically](../incident_management/incidents.md#automatically-close-incidents-via-recovery-alerts) when the alert resolves.
+You can also configure the associated [incident to be closed automatically](../incident_management/manage_incidents.md#automatically-close-incidents-via-recovery-alerts) when the alert resolves.
diff --git a/doc/operations/metrics/dashboards/panel_types.md b/doc/operations/metrics/dashboards/panel_types.md
index 95e8bd82475..177a55fb85b 100644
--- a/doc/operations/metrics/dashboards/panel_types.md
+++ b/doc/operations/metrics/dashboards/panel_types.md
@@ -40,8 +40,8 @@ Note the following properties:
| Property | Type | Required | Description |
| ------ | ------ | ------ | ------ |
-| type | string | no | Type of panel to be rendered. Optional for area panel types |
-| query_range | string | required | For area panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
+| `type` | string | no | Type of panel to be rendered. Optional for area panel types |
+| `query_range` | string | required | For area panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
![area panel chart](img/prometheus_dashboard_area_panel_type_v12_8.png)
@@ -82,8 +82,8 @@ Note the following properties:
| Property | Type | Required | Description |
| ------ | ------ | ------ | ------ |
-| type | string | required | Must be `anomaly-chart` for anomaly panel types |
-| query_range | yes | required | For anomaly panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) in every metric. |
+| `type` | string | required | Must be `anomaly-chart` for anomaly panel types |
+| `query_range` | yes | required | For anomaly panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) in every metric. |
![anomaly panel type](img/prometheus_dashboard_anomaly_panel_type.png)
@@ -138,8 +138,8 @@ Note the following properties:
| Property | Type | Required | Description |
| ------ | ------ | ------ | ------ |
-| type | string | yes | Type of panel to be rendered. For column panel types, set to `column` |
-| query_range | yes | yes | For column panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
+| `type` | string | yes | Type of panel to be rendered. For column panel types, set to `column` |
+| `query_range` | yes | yes | For column panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
![anomaly panel type](img/prometheus_dashboard_column_panel_type.png)
@@ -201,10 +201,10 @@ panel_groups:
Note the following properties:
| Property | Type | Required | Description |
-| ------ | ------ | ------ | ------ |
-| type | string | yes | Type of panel to be rendered. For single stat panel types, set to `single-stat` |
-| field | string | no | Panels display the value of a metric. For a panel to display the value of a label instead, put the name of the label in this key. |
-| query | string | yes | For single stat panel types, you must use an [instant query](https://prometheus.io/docs/prometheus/latest/querying/api/#instant-queries) |
+| ------- | ------ | ------ | ------ |
+| `type` | string | yes | Type of panel to be rendered. For single stat panel types, set to `single-stat` |
+| `field` | string | no | Panels display the value of a metric. For a panel to display the value of a label instead, put the name of the label in this key. |
+| `query` | string | yes | For single stat panel types, you must use an [instant query](https://prometheus.io/docs/prometheus/latest/querying/api/#instant-queries). |
![single stat panel type](img/prometheus_dashboard_single_stat_panel_type.png)
@@ -263,15 +263,15 @@ panel_groups:
Note the following properties:
-| Property | Type | Required | Description |
-| ------ | ------ | ------ | ------ |
-| type | string | yes | Type of panel to be rendered. For gauge panel types, set to `gauge`. |
-| min_value | number | no, defaults to `0` | The minimum value of the gauge chart axis. If either of `min_value` or `max_value` are not set, they both get their default values. |
-| max_value | number | no, defaults to `100` | The maximum value of the gauge chart axis. If either of `min_value` or `max_value` are not set, they both get their default values. |
-| split | number | no, defaults to `10` | The amount of split segments on the gauge chart axis. |
-| thresholds | object | no | Thresholds configuration for the gauge chart axis. |
-| format | string | no, defaults to `engineering` | Unit format used. See the [full list of units](yaml_number_format.md). |
-| query | string | yes | For gauge panel types, you must use an [instant query](https://prometheus.io/docs/prometheus/latest/querying/api/#instant-queries). |
+| Property | Type | Required | Description |
+| ------------ | ------ | ------ | ------ |
+| `type` | string | yes | Type of panel to be rendered. For gauge panel types, set to `gauge`. |
+| `min_value` | number | no, defaults to `0` | The minimum value of the gauge chart axis. If either of `min_value` or `max_value` are not set, they both get their default values. |
+| `max_value` | number | no, defaults to `100` | The maximum value of the gauge chart axis. If either of `min_value` or `max_value` are not set, they both get their default values. |
+| `split` | number | no, defaults to `10` | The amount of split segments on the gauge chart axis. |
+| `thresholds` | object | no | Thresholds configuration for the gauge chart axis. |
+| `format` | string | no, defaults to `engineering` | Unit format used. See the [full list of units](yaml_number_format.md). |
+| `query` | string | yes | For gauge panel types, you must use an [instant query](https://prometheus.io/docs/prometheus/latest/querying/api/#instant-queries). |
### Thresholds properties
@@ -306,12 +306,12 @@ Note the following properties:
| Property | Type | Required | Description |
| ------ | ------ | ------ | ------ |
-| type | string | yes | Type of panel to be rendered. For heatmap panel types, set to `heatmap` |
-| query_range | yes | yes | For area panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
+| `type` | string | yes | Type of panel to be rendered. For heatmap panel types, set to `heatmap` |
+| `query_range` | yes | yes | For area panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
![heatmap panel type](img/heatmap_panel_type.png)
WARNING:
When a query returns too many data points, the heatmap data bucket dimensions tend downwards to 0, making the chart's data invisible, as shown in the image below. To fix this problem, limit the amount of data returned by changing the time range filter on the metrics dashboard UI, or adding the **step** property to your dashboard's YAML file.
-![heatmap chart_too_much_data](img/heatmap_chart_too_much_data_v_13_2.png)
+![heatmap chart with too much data](img/heatmap_chart_too_much_data_v_13_2.png)
diff --git a/doc/operations/metrics/dashboards/variables.md b/doc/operations/metrics/dashboards/variables.md
index 5cd4dcdfa17..2881c084115 100644
--- a/doc/operations/metrics/dashboards/variables.md
+++ b/doc/operations/metrics/dashboards/variables.md
@@ -38,7 +38,7 @@ must be lowercase. The supported variables are:
- `ci_environment_name`
- `__range`
-### environment_filter
+### `environment_filter`
`environment_filter` is automatically expanded to `container_name!="POD",environment="ENVIRONMENT_NAME"`
where `ENVIRONMENT_NAME` is the name of the current environment.
@@ -46,7 +46,7 @@ where `ENVIRONMENT_NAME` is the name of the current environment.
For example, a Prometheus query like `container_memory_usage_bytes{ {{environment_filter}} }`
becomes `container_memory_usage_bytes{ container_name!="POD",environment="production" }`.
-### __range
+### `__range`
The `__range` variable is useful in Prometheus
[range vector selectors](https://prometheus.io/docs/prometheus/latest/querying/basics/#range-vector-selectors).
diff --git a/doc/operations/metrics/dashboards/yaml.md b/doc/operations/metrics/dashboards/yaml.md
index 399a8ecb615..68eddc6f087 100644
--- a/doc/operations/metrics/dashboards/yaml.md
+++ b/doc/operations/metrics/dashboards/yaml.md
@@ -156,7 +156,7 @@ To confirm your dashboard definition contains valid YAML syntax:
Files with valid syntax display **Metrics Dashboard YAML definition is valid**,
and files with invalid syntax display **Metrics Dashboard YAML definition is invalid**.
-![Metrics Dashboard_YAML_syntax_validation](img/prometheus_dashboard_yaml_validation_v13_1.png)
+![Metrics dashboard YAML syntax validation](img/prometheus_dashboard_yaml_validation_v13_1.png)
When **Metrics Dashboard YAML definition is invalid** at least one of the following messages is displayed:
diff --git a/doc/operations/tracing.md b/doc/operations/tracing.md
deleted file mode 100644
index b3c0763bbbc..00000000000
--- a/doc/operations/tracing.md
+++ /dev/null
@@ -1,14 +0,0 @@
----
-stage: Monitor
-group: Respond
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
-remove_date: '2022-11-01'
-redirect_to: 'index.md'
----
-
-# Tracing (removed) **(FREE SELF)**
-
-This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/346540) in GitLab 14.7
-and [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/346540) in GitLab 15.2.
-We are working on an alternative to replace tracing.
-To learn more, visit the [Observability direction page](https://about.gitlab.com/direction/monitor/observability/).
diff --git a/doc/policy/maintenance.md b/doc/policy/maintenance.md
index 2bfc7f0243a..fcb6e5c1b20 100644
--- a/doc/policy/maintenance.md
+++ b/doc/policy/maintenance.md
@@ -36,7 +36,7 @@ The following table describes the version types and their release cadence:
| Version type | Description | Cadence |
|:-------------|:------------|:--------|
-| Major | For significant changes, or when any backward-incompatible changes are introduced to the public API. | Yearly. The next major release is GitLab 15.0 on May 22, 2022. GitLab schedules major releases on May 22 each year, by default. |
+| Major | For significant changes, or when any backward-incompatible changes are introduced to the public API. | Yearly. The next major release is GitLab 16.0 on May 22, 2023. GitLab schedules major releases on May 22 each year, by default. |
| Minor | For when new backward-compatible functionality is introduced to the public API, a minor feature is introduced, or when a set of smaller features is rolled out. | Monthly on the 22nd. |
| Patch | For backward-compatible bug fixes that fix incorrect behavior. See [Patch releases](#patch-releases). | As needed. |
diff --git a/doc/raketasks/backup_gitlab.md b/doc/raketasks/backup_gitlab.md
index 47696fc1f99..aea61e36037 100644
--- a/doc/raketasks/backup_gitlab.md
+++ b/doc/raketasks/backup_gitlab.md
@@ -36,7 +36,7 @@ The backup command requires [additional parameters](backup_restore.md#back-up-an
your installation is using PgBouncer, for either performance reasons or when using it with a Patroni cluster.
WARNING:
-The backup command doesn't verify if another backup is already running, as described in
+Before GitLab 15.5.0, the backup command doesn't verify if another backup is already running, as described in
[issue 362593](https://gitlab.com/gitlab-org/gitlab/-/issues/362593). We strongly recommend
you make sure that all backups are complete before starting a new one.
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 2ac79a913f3..796cb71321a 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -85,6 +85,9 @@ In the following cases, consider using file system data transfer or snapshots as
- Your GitLab instance has a lot of forked projects and the regular backup task duplicates the Git data for all of them.
- Your GitLab instance has a problem and using the regular backup and import Rake tasks isn't possible.
+WARNING:
+Gitaly Cluster [does not support snapshot backups](../administration/gitaly/index.md#snapshot-backup-and-recovery-limitations).
+
When considering using file system data transfer or snapshots:
- Don't use these methods to migrate from one operating system to another. The operating systems of the source and destination should be as similar as possible. For example,
@@ -887,7 +890,7 @@ Truncate the filenames in the `uploads` table:
- `current_filename`: a filename that is currently more than 246 characters long.
- `new_filename`: a filename that has been truncated to 246 characters maximum.
- - `new_path`: new path considering the new_filename (truncated).
+ - `new_path`: new path considering the `new_filename` (truncated).
Once you validate the batch results, you must change the batch size (`row_id`) using the following sequence of numbers (10000 to 20000). Repeat this process until you reach the last record in the `uploads` table.
@@ -981,3 +984,98 @@ Truncate the filenames on the filesystem. You must manually rename the files in
#### Re-run the backup task
After following all the previous steps, re-run the backup task.
+
+### Restoring database backup fails when `pg_stat_statements` was previously enabled
+
+The GitLab backup of the PostgreSQL database includes all SQL statements required to enable extensions that were
+previously enabled in the database.
+
+The `pg_stat_statements` extension can only be enabled or disabled by a PostgreSQL user with `superuser` role.
+As the restore process uses a database user with limited permissions, it can't execute the following SQL statements:
+
+```sql
+DROP EXTENSION IF EXISTS pg_stat_statements;
+CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA public;
+```
+
+When trying to restore the backup in a PostgreSQL instance that doesn't have the `pg_stats_statements` extension,
+the following error message is displayed:
+
+```plaintext
+ERROR: permission denied to create extension "pg_stat_statements"
+HINT: Must be superuser to create this extension.
+ERROR: extension "pg_stat_statements" does not exist
+```
+
+When trying to restore in an instance that has the `pg_stats_statements` extension enabled, the cleaning up step
+fails with an error message similar to the following:
+
+```plaintext
+rake aborted!
+ActiveRecord::StatementInvalid: PG::InsufficientPrivilege: ERROR: must be owner of view pg_stat_statements
+/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:42:in `block (4 levels) in <top (required)>'
+/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:41:in `each'
+/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:41:in `block (3 levels) in <top (required)>'
+/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/backup.rake:71:in `block (3 levels) in <top (required)>'
+/opt/gitlab/embedded/bin/bundle:23:in `load'
+/opt/gitlab/embedded/bin/bundle:23:in `<main>'
+Caused by:
+PG::InsufficientPrivilege: ERROR: must be owner of view pg_stat_statements
+/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:42:in `block (4 levels) in <top (required)>'
+/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:41:in `each'
+/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:41:in `block (3 levels) in <top (required)>'
+/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/backup.rake:71:in `block (3 levels) in <top (required)>'
+/opt/gitlab/embedded/bin/bundle:23:in `load'
+/opt/gitlab/embedded/bin/bundle:23:in `<main>'
+Tasks: TOP => gitlab:db:drop_tables
+(See full trace by running task with --trace)
+```
+
+#### Prevent the dump file to include `pg_stat_statements`
+
+To prevent the inclusion of the extension in the PostgreSQL dump file that is part of the backup bundle,
+enable the extension in any schema except the `public` schema:
+
+```sql
+CREATE SCHEMA adm;
+CREATE EXTENSION pg_stat_statements SCHEMA adm;
+```
+
+If the extension was previously enabled in the `public` schema, move it to a new one:
+
+```sql
+CREATE SCHEMA adm;
+ALTER EXTENSION pg_stat_statements SET SCHEMA adm;
+```
+
+To query the `pg_stat_statements` data after changing the schema, prefix the view name with the new schema:
+
+```sql
+SELECT * FROM adm.pg_stat_statements limit 0;
+```
+
+To make it compatible with third-party monitoring solutions that expect it to be enabled in the `public` schema,
+you need to include it in the `search_path`:
+
+```sql
+set search_path to public,adm;
+```
+
+#### Fix an existing dump file to remove references to `pg_stat_statements`
+
+To fix an existing backup file, do the following changes:
+
+1. Extract from the backup the following file: `db/database.sql.gz`.
+1. Decompress the file or use an editor that is capable of handling it compressed.
+1. Remove the following lines, or similar ones:
+
+ ```sql
+ CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA public;
+ ```
+
+ ```sql
+ COMMENT ON EXTENSION pg_stat_statements IS 'track planning and execution statistics of all SQL statements executed';
+ ```
+
+1. Save the changes and recompress the file.
+1. Update the backup file with the modified `db/database.sql.gz`.
diff --git a/doc/raketasks/cleanup.md b/doc/raketasks/cleanup.md
index a67fec26a9b..60c81b26c05 100644
--- a/doc/raketasks/cleanup.md
+++ b/doc/raketasks/cleanup.md
@@ -4,7 +4,7 @@ group: Distribution
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Clean up **(FREE SELF)**
+# Clean up Rake tasks **(FREE SELF)**
GitLab provides Rake tasks for cleaning up GitLab instances.
@@ -35,7 +35,8 @@ You can also specify the project with `PROJECT_ID` instead of `PROJECT_PATH`.
For example:
```shell
-$ sudo gitlab-rake gitlab:cleanup:orphan_lfs_file_references PROJECT_PATH="gitlab-org/gitlab-foss"
+$ sudo gitlab-rake gitlab:cleanup:orphan_lfs_file_references PROJECT_ID="13083"
+
I, [2019-12-13T16:35:31.764962 #82356] INFO -- : Looking for orphan LFS files for project GitLab Org / GitLab Foss
I, [2019-12-13T16:35:31.923659 #82356] INFO -- : Removed invalid references: 12
```
diff --git a/doc/raketasks/generate_sample_prometheus_data.md b/doc/raketasks/generate_sample_prometheus_data.md
index ec7f54a41c2..7445065c77e 100644
--- a/doc/raketasks/generate_sample_prometheus_data.md
+++ b/doc/raketasks/generate_sample_prometheus_data.md
@@ -4,7 +4,7 @@ group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Generate sample Prometheus data **(FREE SELF)**
+# Generate sample Prometheus data Rake task **(FREE SELF)**
This command runs Prometheus queries for each of the metrics of a specific environment
for a series of time intervals to now:
diff --git a/doc/raketasks/index.md b/doc/raketasks/index.md
index 4a157688a41..0d5a826b0a1 100644
--- a/doc/raketasks/index.md
+++ b/doc/raketasks/index.md
@@ -7,15 +7,13 @@ comments: false
# Rake tasks **(FREE SELF)**
-GitLab provides [Rake](https://ruby.github.io/rake/) tasks to assist you with
-common administration and operational processes.
+GitLab provides [Rake](https://ruby.github.io/rake/) tasks to assist you with common administration and operational
+processes.
You can perform GitLab Rake tasks by using:
-- `gitlab-rake <raketask>` for [Omnibus GitLab](https://docs.gitlab.com/omnibus/index.html)
- installations.
-- `bundle exec rake <raketask>` for [source](../install/installation.md)
- installations.
+- `gitlab-rake <raketask>` for [Omnibus GitLab](https://docs.gitlab.com/omnibus/index.html) installations.
+- `bundle exec rake <raketask>` for [source](../install/installation.md) installations.
## Available Rake tasks
@@ -39,7 +37,7 @@ The following Rake tasks are available for use with GitLab:
| [Praefect Rake tasks](../administration/raketasks/praefect.md) | [Praefect](../administration/gitaly/praefect.md)-related tasks. |
| [Project import/export](../administration/raketasks/project_import_export.md) | Prepare for [project exports and imports](../user/project/settings/import_export.md). |
| [Sample Prometheus data](generate_sample_prometheus_data.md) | Generate sample Prometheus data. |
-| [Sidekiq job migration](sidekiq_job_migration.md) | Migrate Sidekiq jobs scheduled for future dates to a new queue. |
+| [Sidekiq job migration](../administration/sidekiq/sidekiq_job_migration.md) | Migrate Sidekiq jobs scheduled for future dates to a new queue. |
| [SMTP maintenance](../administration/raketasks/smtp.md) | SMTP-related tasks. |
| [SPDX license list import](spdx.md) | Import a local copy of the [SPDX license list](https://spdx.org/licenses/) for matching [License Compliance policies](../user/compliance/license_compliance/index.md). |
| [Repository storage](../administration/raketasks/storage.md) | List and migrate existing projects and attachments from legacy storage to hashed storage. |
diff --git a/doc/raketasks/list_repos.md b/doc/raketasks/list_repos.md
index 57d24a2942d..258d41aa190 100644
--- a/doc/raketasks/list_repos.md
+++ b/doc/raketasks/list_repos.md
@@ -4,7 +4,7 @@ group: Distribution
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Listing repository directories **(FREE SELF)**
+# List repository directories Rake task **(FREE SELF)**
You can print a list of all Git repositories on disk managed by GitLab.
diff --git a/doc/raketasks/migrate_snippets.md b/doc/raketasks/migrate_snippets.md
index 62d2866e499..e51edc5c133 100644
--- a/doc/raketasks/migrate_snippets.md
+++ b/doc/raketasks/migrate_snippets.md
@@ -4,7 +4,7 @@ group: Editor
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Migration to versioned snippets **(FREE SELF)**
+# Migrate to versioned snippets Rake tasks **(FREE SELF)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/215861) in GitLab 13.0.
diff --git a/doc/raketasks/sidekiq_job_migration.md b/doc/raketasks/sidekiq_job_migration.md
deleted file mode 100644
index 45a0cbaa267..00000000000
--- a/doc/raketasks/sidekiq_job_migration.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../administration/sidekiq/sidekiq_job_migration.md'
-remove_date: '2022-11-11'
----
-
-This document was moved to [another location](../administration/sidekiq/sidekiq_job_migration.md).
-
-<!-- This redirect file can be deleted after <2022-11-11>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/raketasks/spdx.md b/doc/raketasks/spdx.md
index 31f860f45de..608139fa404 100644
--- a/doc/raketasks/spdx.md
+++ b/doc/raketasks/spdx.md
@@ -4,7 +4,7 @@ group: Composition Analysis
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# SPDX license list import **(ULTIMATE SELF)**
+# SPDX license list import Rake task **(ULTIMATE SELF)**
GitLab provides a Rake task for uploading a fresh copy of the [SPDX license list](https://spdx.org/licenses/)
to a GitLab instance. This list is needed for matching the names of [License Compliance policies](../user/compliance/license_compliance/index.md).
diff --git a/doc/raketasks/user_management.md b/doc/raketasks/user_management.md
index 84d943e2c21..7756774e432 100644
--- a/doc/raketasks/user_management.md
+++ b/doc/raketasks/user_management.md
@@ -4,9 +4,10 @@ group: Distribution
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# User management **(FREE SELF)**
+# User management Rake tasks **(FREE SELF)**
-GitLab provides Rake tasks for user management.
+GitLab provides Rake tasks for managing users. Administrators can also use the Admin Area to
+[manage users](../user/admin_area/index.md#administering-users).
## Add user as a developer to all projects
diff --git a/doc/raketasks/web_hooks.md b/doc/raketasks/web_hooks.md
index b4d01d21dc7..3bd9d7e2d2e 100644
--- a/doc/raketasks/web_hooks.md
+++ b/doc/raketasks/web_hooks.md
@@ -4,7 +4,7 @@ group: Distribution
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Webhooks administration **(FREE SELF)**
+# Webhooks administration Rake tasks **(FREE SELF)**
GitLab provides Rake tasks for webhooks management.
diff --git a/doc/raketasks/x509_signatures.md b/doc/raketasks/x509_signatures.md
index 3404f6ae9cf..364264ae204 100644
--- a/doc/raketasks/x509_signatures.md
+++ b/doc/raketasks/x509_signatures.md
@@ -4,7 +4,7 @@ group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# X.509 signatures **(FREE SELF)**
+# X.509 signatures Rake task **(FREE SELF)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/122159) in GitLab 12.10.
diff --git a/doc/security/crime_vulnerability.md b/doc/security/crime_vulnerability.md
index 463ccb7b629..e5d8d858df2 100644
--- a/doc/security/crime_vulnerability.md
+++ b/doc/security/crime_vulnerability.md
@@ -53,7 +53,7 @@ SPDY support earlier than version 4 is advertised.
From the report above it is important to note that Nessus is only checking if
TLS advertises the SPDY protocol earlier than version 4. It does not perform an
attack nor does it check if compression is enabled. The Nessus scanner alone
-cannot tell that SPDY's compression is disabled and not subject to the CRIME
+cannot tell that SPDY compression is disabled and not subject to the CRIME
vulnerability.
## References
diff --git a/doc/security/password_storage.md b/doc/security/password_storage.md
index bd514de6e2c..67ef161e634 100644
--- a/doc/security/password_storage.md
+++ b/doc/security/password_storage.md
@@ -21,7 +21,7 @@ GitLab uses the [Devise](https://github.com/heartcombo/devise) authentication
library to hash user passwords. Created password hashes have these attributes:
- **Hashing**:
- - **BCrypt**: By default, the [`bcrypt`](https://en.wikipedia.org/wiki/Bcrypt) hashing
+ - **bcrypt**: By default, the [`bcrypt`](https://en.wikipedia.org/wiki/Bcrypt) hashing
function is used to generate the hash of the provided password. This cryptographic hashing function is
strong and industry-standard.
- **PBKDF2+SHA512**: PBKDF2+SHA512 is supported:
@@ -29,7 +29,7 @@ library to hash user passwords. Created password hashes have these attributes:
- In GitLab 15.6 and later when [FIPS mode](../development/fips_compliance.md) is enabled (feature flags are not required).
- **Stretching**: Password hashes are [stretched](https://en.wikipedia.org/wiki/Key_stretching)
to harden against brute-force attacks. By default, GitLab uses a stretching
- factor of 10 for BCrypt and 20,000 for PBKDF2 + SHA512.
+ factor of 10 for bcrypt and 20,000 for PBKDF2 + SHA512.
- **Salting**: A [cryptographic salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
is added to each password to harden against pre-computed hash and dictionary
attacks. To increase security, each salt is randomly generated for each
diff --git a/doc/security/rate_limits.md b/doc/security/rate_limits.md
index 20a81ed0c30..929609cd4a4 100644
--- a/doc/security/rate_limits.md
+++ b/doc/security/rate_limits.md
@@ -141,6 +141,19 @@ This is to mitigate the risk of misuses, such as mass discovery of usernames in
The **rate limit** is 20 calls per minute per IP address.
+### Project Jobs API endpoint
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104912) in GitLab 15.7 [with a flag](../administration/feature_flags.md) named `ci_enforce_rate_limits_jobs_api`. Disabled by default.
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available,
+ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `ci_enforce_rate_limits_jobs_api`.
+The feature is not ready for production use.
+
+There is a rate limit for the endpoint `project/:id/jobs`, which is enforced to reduce timeouts when retrieving jobs.
+
+The **rate limit** is 600 calls per minute per signed-in user.
+
## Troubleshooting
### Rack Attack is denylisting the load balancer
diff --git a/doc/security/reset_user_password.md b/doc/security/reset_user_password.md
index 248737fc908..38c52912d5c 100644
--- a/doc/security/reset_user_password.md
+++ b/doc/security/reset_user_password.md
@@ -7,7 +7,7 @@ type: howto
# Reset a user's password **(FREE SELF)**
-You can reset user passwords by using a Rake task, a Rails console, or the
+You can reset user passwords by using the UI, a Rake task, a Rails console, or the
[Users API](../api/users.md#user-modification).
## Prerequisites
@@ -16,6 +16,18 @@ To reset a user password, you must be an administrator of a self-managed GitLab
The user's new password must meet all [password requirements](../user/profile/user_passwords.md#password-requirements).
+## Use the UI
+
+To reset a user's password in the UI:
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Overview > Users**.
+1. For the user whose password you want to update, select **Edit** (**{pencil-square}**).
+1. In the **Password** area, type a password and password confirmation.
+1. Select **Save changes**.
+
+A confirmation is displayed.
+
## Use a Rake task
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52347) in GitLab 13.9.
diff --git a/doc/security/user_email_confirmation.md b/doc/security/user_email_confirmation.md
index ffc537c8f10..c3f19c92f91 100644
--- a/doc/security/user_email_confirmation.md
+++ b/doc/security/user_email_confirmation.md
@@ -13,7 +13,7 @@ they confirm their email address.
1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General** (`/admin/application_settings/general`).
-1. Expand the **Sign-up restrictions** section and look for the **Send confirmation email on sign-up** option.
+1. Expand the **Sign-up restrictions** section and look for the **Email confirmation settings** options.
## Confirmation token expiry
diff --git a/doc/subscriptions/gitlab_com/index.md b/doc/subscriptions/gitlab_com/index.md
index 6f1bf0df044..552e9c46c4a 100644
--- a/doc/subscriptions/gitlab_com/index.md
+++ b/doc/subscriptions/gitlab_com/index.md
@@ -78,7 +78,7 @@ A top-level group can be [changed](../../user/group/manage.md#change-a-groups-pa
Every user is included in seat usage, with the following exceptions:
- Users who are pending approval.
-- Members with the Guest role on an Ultimate subscription.
+- Members with the [Guest role on an Ultimate subscription](#free-guest-users).
- GitLab-created service accounts:
- [Ghost User](../../user/profile/account/delete_account.md#associated-records).
- Bots such as:
@@ -102,9 +102,16 @@ To view a list of seats being used:
1. On the left sidebar, select **Settings > Usage Quotas**.
1. On the **Seats** tab, view usage information.
-The seat usage listing is updated live, but the usage statistics on the billing page are updated
-only once per day. For this reason there can be a minor difference between the seat usage listing
-and the billing page.
+The data in seat usage listing, **Seats in use**, and **Seats in subscription** are updated live.
+The counts for **Max seats used** and **Seats owed** are updated once per day.
+
+To view your subscription information and a summary of seat counts:
+
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > Billing**.
+
+The usage statistics are updated once per day, which may cause
+a difference between the information in the **Usage Quotas** page and the **Billing page**.
### Search seat usage
@@ -153,6 +160,16 @@ For example, if you purchase a subscription for 10 users:
Seats owed = 12 - 10 (Maximum users - users in subscription)
+### Free Guest users **(ULTIMATE)**
+
+In the **Ultimate** tier, users who are assigned the Guest role do not consume a seat.
+The user must not be assigned any other role, anywhere in the instance.
+
+- If your project is private or internal, a user with the Guest role has
+ [a set of permissions](../../user/permissions.md#project-members-permissions).
+- If your project is public, all users, including those with the Guest role
+ can access your project.
+
### Add users to your subscription
Your subscription cost is based on the maximum number of seats you use during the billing period.
@@ -214,6 +231,37 @@ amounts at which the alert displays.
| 100-999 | 8% of seats have been used. |
| 1000+ | 5% of seats have been used |
+## Change the linked account
+
+To change the GitLab.com account linked to your Customers Portal account:
+
+1. Log in to the
+ [Customers Portal](https://customers.gitlab.com/customers/sign_in).
+1. In a separate browser tab, go to [GitLab SaaS](https://gitlab.com/users/sign_in) and ensure you
+ are not logged in.
+1. On the Customers Portal page, select **My account > Account details**.
+1. Under **Your GitLab.com account**, select **Change linked account**.
+1. Log in to the [GitLab SaaS](https://gitlab.com/users/sign_in) account you want to link to the Customers Portal
+ account.
+
+## Change the linked namespace
+
+To change the namespace linked to a subscription:
+
+1. Log in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in) with a
+ [linked](#change-the-linked-account) GitLab SaaS account.
+1. Navigate to the **Manage Purchases** page.
+1. Select **Change linked namespace**.
+1. Select the desired group from the **This subscription is for** dropdown. For a group to appear here, you must have the Owner role for that group.
+1. Select **Proceed to checkout**.
+
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+For a demo, see [Linking GitLab Subscription to the Namespace](https://youtu.be/qAq8pyFP-a0).
+
+Subscription charges are calculated based on the total number of users in a group, including its subgroups and nested projects. If the [total number of users](#view-seat-usage) exceeds the number of seats in your subscription, your account is charged for the additional users and you need to pay for the overage before you can change the linked namespace.
+
+Only one namespace can be linked to a subscription.
+
## Upgrade your GitLab SaaS subscription tier
To upgrade your [GitLab tier](https://about.gitlab.com/pricing/):
@@ -275,10 +323,17 @@ For details on upgrading your subscription tier, see
### Automatic subscription renewal
-When you enable automatic renewal, the subscription automatically renews on the
-expiration date without a gap in available service. An invoice is
-generated for the renewal and available for viewing or download on the
-[View invoices](https://customers.gitlab.com/receipts) page.
+When a subscription is set to auto-renew, it renews automatically on the
+expiration date without a gap in available service. Subscriptions purchased through Customers Portal or GitLab.com are set to auto-renew by default. The number of seats is adjusted to fit the [number of billable users in your group](#view-seat-usage) at the time of renewal. You can view and download your renewal invoice on the
+[View invoices](https://customers.gitlab.com/receipts) page. If your account has a [saved credit card](../index.md#change-your-payment-method), the card is charged for the invoice amount. If we are unable to process a payment or the auto-renewal fails for any other reason, you have 14 days to renew your subscription. After that, your access is downgraded.
+
+#### Email notifications
+
+15 days before a subscription automatically renews, an email is sent with information about the renewal.
+
+- If your credit card is expired, the email tells you how to update it.
+- If you have any outstanding overages, the email tells you to contact our Sales team.
+- If there are no issues, the email specifies the names and quantity of the products being renewed. The email also includes the total amount you owe. If your usage increases or decreases before renewal, this amount can change.
#### Enable or disable automatic subscription renewal
@@ -335,7 +390,7 @@ locked. Projects can only be unlocked by purchasing more storage subscription un
Prerequisite:
-- You must have at least the Owner role.
+- You must have the Owner role.
You can purchase a storage subscription for your personal or group namespace.
@@ -408,7 +463,7 @@ and for communicating directly with the relevant GitLab team members.
If your credit card is declined when purchasing a GitLab subscription, possible reasons include:
-- The credit card details provided are incorrect.
+- The credit card details provided are incorrect. The most common cause for this is an incomplete or dummy address.
- The credit card account has insufficient funds.
- You are using a virtual credit card and it has insufficient funds, or has expired.
- The transaction exceeds the credit limit.
@@ -417,7 +472,7 @@ If your credit card is declined when purchasing a GitLab subscription, possible
Check with your financial institution to confirm if any of these reasons apply. If they don't
apply, contact [GitLab Support](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293).
-### Unable to link subcription to namespace
+### Unable to link subscription to namespace
If you cannot link a subscription to your namespace, ensure that you have the Owner role
for that namespace.
diff --git a/doc/subscriptions/gitlab_dedicated/index.md b/doc/subscriptions/gitlab_dedicated/index.md
index c503c501eeb..b471d1d971f 100644
--- a/doc/subscriptions/gitlab_dedicated/index.md
+++ b/doc/subscriptions/gitlab_dedicated/index.md
@@ -7,23 +7,25 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# GitLab Dedicated
NOTE:
-GitLab Dedicated is currently in limited availability. [Contact us](#contact-us) if you are interested.
+GitLab Dedicated is currently in limited availability. You can learn more and join the waitlist [on our website](https://about.gitlab.com/single-tenant-saas).
GitLab Dedicated is a fully isolated, single-tenant SaaS service that is:
- Hosted and managed by GitLab, Inc.
-- Deployed in a region of choice on AWS.
+- Deployed on AWS in a cloud region of your choice (see the [regions that are not supported](#aws-regions-not-supported)).
-GitLab Dedicated enables you to offload the operational overhead of managing the DevOps Platform. It offers a high level of tenant isolation and deployment customization, ideal for enterprises in highly-regulated industries. By deploying your GitLab instance onto separate Cloud Infrastructure from other tenants, GitLab Dedicated helps you better meet your security and compliance requirements.
+GitLab Dedicated removes the overhead of platform management to increase your operational efficiency, reduce risk, and enhance the speed and agility of your organization. Each GitLab Dedicated instance is highly available with disaster recovery and deployed into the cloud region of your choice. GitLab teams fully manage the maintenance and operations of each isolated instance, so customers can access our latest product improvements while meeting the most complex compliance standards.
+
+It's the offering of choice for enterprises and organizations in highly regulated industries that have complex regulatory, compliance, and data residency requirements.
## Available features
-- Authentication: Support for instance-level [SAML OmniAuth](../../integration/saml.md) functionality. GitLab Dedicated acts as the service provider, and you must provide the necessary [configuration](../../integration/saml.md#general-setup) in order for GitLab to communicate with your IdP. This is provided during onboarding.
- - SAML [request signing](../../integration/saml.md#request-signing-optional), [group sync](../../user/group/saml_sso/group_sync.md#configure-saml-group-sync), and [SAML groups](../../integration/saml.md#saml-groups) are supported.
+- Authentication: Support for instance-level [SAML OmniAuth](../../integration/saml.md) functionality. GitLab Dedicated acts as the service provider, and you must provide the necessary [configuration](../../integration/saml.md#configure-saml-support-in-gitlab) in order for GitLab to communicate with your IdP. This is provided during onboarding.
+ - SAML [request signing](../../integration/saml.md#sign-saml-authentication-requests-optional), [group sync](../../user/group/saml_sso/group_sync.md#configure-saml-group-sync), and [SAML groups](../../integration/saml.md#configure-users-based-on-saml-group-membership) are supported.
- Networking:
- - Public connectivity with support for IP Allowlists. During onboarding, you can optionally specify a list of IP addresses that can access your Dedicated instance. Subsequently, when an IP not on the allowlist tries to access your instance the connection will be refused.
+ - Public connectivity with support for IP Allowlists. During onboarding, you can optionally specify a list of IP addresses that can access your GitLab Dedicated instance. Subsequently, when an IP not on the allowlist tries to access your instance the connection is refused.
- Optional. Private connectivity via [AWS PrivateLink](https://aws.amazon.com/privatelink/).
- You can specify an AWS IAM Principal and preferred Availability Zones during onboarding to enable this functionality. Both Ingress and Egress Private Links are supported. When connecting to an internal service running in your VPC over https via PrivateLink, Dedicated supports the ability to use a private SSL certificate, which can be provided during onboarding.
+ You can specify an AWS IAM Principal and preferred Availability Zones during onboarding to enable this functionality. Both Ingress and Egress PrivateLinks are supported. When connecting to an internal service running in your VPC over HTTPS via PrivateLink, GitLab Dedicated supports the ability to use a private SSL certificate, which can be provided during onboarding.
- Upgrades:
- Monthly upgrades tracking one release behind the latest (n-1), with the latest security release.
- Out of band security patches provided for high severity releases.
@@ -53,7 +55,7 @@ The following features will not be supported:
- Mattermost
- Server-side Git hooks
-### Dedicated service features
+### GitLab Dedicated service features
The following operational features are not available:
@@ -74,55 +76,14 @@ The following AWS regions are not available:
- Cape Town (`af-south-1`)
- Milan (`eu-south-1`)
- Paris (`eu-west-3`)
-- GovCloud
+- Zurich (`eu-central-2`)
+- GovCloud (US-East) (`us-gov-east-1`)
+- GovCloud (US-West) (`us-gov-west-1`)
## Planned features
-Learn more about the planned improvements to Dedicated on the public [direction page](https://about.gitlab.com/direction/saas-platforms/dedicated/).
-
-## Contact us
-
-Fill in the following form to contact us and learn more about this offering.
-
-<!-- markdownlint-disable -->
-
-<!-- NOTE: The following form only shows when the site is served under HTTPS,
- so it will not appear when developing locally or in a review app.
- See https://gitlab.com/gitlab-com/marketing/marketing-operations/-/issues/6238#note_923358643
--->
-
-<script src="https://page.gitlab.com/js/forms2/js/forms2.min.js"></script>
-<form id="mktoForm_3226"></form>
-<script>MktoForms2.loadForm("https://page.gitlab.com", "194-VVC-221", 3226);</script>
-<style>
- #mktoForm_3226 {
- font-size: .875rem !important;
- }
- .mktoLabel {
- margin-top: 1rem !important;
- padding-bottom: .5rem !important;
- font-weight: 600;
- }
- .mktoHtmlText,
- #LblPhone,
- .mktoTextField,
- #commentCapture,
- .mktoField,
- .mktoButtonRow button {
- width: 20rem !important;
- }
- .mktoHtmlText {
- font-size: .875rem;
- }
- .mktoButtonRow {
- margin: 1em 0;
- }
- .mktoButtonRow span {
- margin-left: 0 !important;
- }
- .mktoButtonRow button {
- margin: 1em 0 1.5em !important;
- }
-</style>
-
-<!-- markdownlint-enable -->
+Learn more about the planned improvements to GitLab Dedicated on the public [direction page](https://about.gitlab.com/direction/saas-platforms/dedicated/).
+
+## Learn more about GitLab Dedicated and join our waitlist
+
+As we scale this new offering, we are making GitLab Dedicated available by inviting customers to learn more and join our waitlist [on our website](https://about.gitlab.com/single-tenant-saas).
diff --git a/doc/subscriptions/img/add-license.png b/doc/subscriptions/img/add-license.png
new file mode 100644
index 00000000000..157f0921796
--- /dev/null
+++ b/doc/subscriptions/img/add-license.png
Binary files differ
diff --git a/doc/subscriptions/index.md b/doc/subscriptions/index.md
index 47c1f730746..98ce40c9e9b 100644
--- a/doc/subscriptions/index.md
+++ b/doc/subscriptions/index.md
@@ -73,11 +73,13 @@ With the [Customers Portal](https://customers.gitlab.com/) you can:
- [Change account owner information](#change-account-owner-information)
- [Change your company details](#change-your-company-details)
- [Change your payment method](#change-your-payment-method)
-- [Change the linked account](#change-the-linked-account)
-- [Change the namespace the subscription is linked to](#change-the-linked-namespace)
+- [Change the linked account](gitlab_com/index.md#change-the-linked-account)
+- [Change the namespace the subscription is linked to](gitlab_com/index.md#change-the-linked-namespace)
- [Change customers portal account password](#change-customers-portal-account-password)
-The Customers Portal is available only to customers who purchased their subscription from GitLab. If you made your purchase through a partner or reseller, you must contact them directly for assistance with your subscription.
+The Customers Portal is available only to customers who purchased their
+subscription from GitLab. If you made your purchase through a partner or
+reseller, you must contact them directly for assistance with your subscription.
### Change account owner information
@@ -132,40 +134,6 @@ method as the default:
1. **Edit** the selected payment method and check the **Make default payment method** checkbox.
1. Select **Save Changes**.
-### Change the linked account
-
-To change the GitLab.com account linked to your Customers Portal account:
-
-1. Log in to the
- [Customers Portal](https://customers.gitlab.com/customers/sign_in).
-1. In a separate browser tab, go to [GitLab SaaS](https://gitlab.com) and ensure you
- are not logged in.
-1. On the Customers Portal page, select **My account > Account details**.
-1. Under **Your GitLab.com account**, select **Change linked account**. If the account is not yet linked, select **Link my GitLab.com account**.
-1. Log in to the [GitLab SaaS](https://gitlab.com) account you want to link to the Customers Portal
- account.
-
-### Change the linked namespace
-
-To change the namespace linked to a subscription:
-
-1. Log in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in) with a
- [linked](#change-the-linked-account) GitLab SaaS account.
-1. Navigate to the **Manage Purchases** page.
-1. Select **Change linked namespace**.
-1. Select the desired group from the **Select user or group** dropdown list. For a group to appear
- here, you must have the Owner role for that group.
-1. Select **Proceed to checkout**.
-
-If the group you want to link does not appear in the dropdown list, check:
-
-- You have [linked your Customers Portal account with your GitLab.com account](#change-the-linked-account).
-- That the linked account is a member of the group you want to select, and you are assigned the Owner role.
-
-Subscription charges are calculated based on the total number of users in a group, including its subgroups and nested projects. If the [total number of users](gitlab_com/index.md#view-seat-usage) exceeds the number of seats in your subscription, your account is charged for the additional users and you need to pay for the overage before you can change the linked namespace.
-
-Only one namespace can be linked to a subscription.
-
### Change Customers Portal account password
To change the password for this customers portal account:
@@ -187,9 +155,6 @@ For qualifying open source projects, the [GitLab for Open Source Program](https:
#### Meeting GitLab for Open Source Program requirements
-NOTE:
-GitLab for Open Source Program benefits apply to an entire GitLab namespace. To qualify for the GitLab for Open Source Program, all projects in an applicant's namespace must meet program requirements. Applicants submit materials related to one project in the applying namespace, and the open source program team uses that project to verify eligibility of the entire namespace.
-
To meet GitLab for Open Source Program requirements, first add an OSI-approved open source license to all projects in your namespace.
To add a license to a project:
@@ -197,8 +162,13 @@ To add a license to a project:
1. On the top bar, select **Main menu > Projects** and find your project.
1. On the overview page, select **Add LICENSE**. If the license you want is not available as a license template, manually copy the entire, unaltered [text of your chosen license](https://opensource.org/licenses/alphabetical) into the `LICENSE` file. Note that GitLab defaults to **All rights reserved** if users do not perform this action.
+![Add license](img/add-license.png)
+
Applicants must add the correct license to each project in their respective groups or namespaces. When you're sure you're using OSI-approved licenses for your projects, you can take your screenshots.
+NOTE:
+GitLab for Open Source Program benefits apply to an entire GitLab namespace. To qualify for the GitLab for Open Source Program, all projects in an applicant's namespace must meet program requirements. Applicants submit materials related to one project in the applying namespace, and the open source program team uses that project to verify eligibility of the entire namespace.
+
#### Verification for Open Source Program
Next, take screenshots of your project to confirm that project's eligibility. You must upload three screenshots:
diff --git a/doc/subscriptions/self_managed/index.md b/doc/subscriptions/self_managed/index.md
index c4110fe4638..93cf5afad63 100644
--- a/doc/subscriptions/self_managed/index.md
+++ b/doc/subscriptions/self_managed/index.md
@@ -63,7 +63,7 @@ billable user, with the following exceptions:
[blocked users](../../user/admin_area/moderate_users.md#block-a-user) don't count as billable users in the current subscription. When they are either deactivated or blocked they release a _billable user_ seat. However, they may
count toward overages in the subscribed seat count.
- Users who are [pending approval](../../user/admin_area/moderate_users.md#users-pending-approval).
-- Members with the Guest role on an Ultimate subscription.
+- Members with the [Guest role on an Ultimate subscription](#free-guest-users).
- Users without project or group memberships on an Ultimate subscription.
- GitLab-created service accounts:
- [Ghost User](../../user/profile/account/delete_account.md#associated-records).
@@ -97,6 +97,21 @@ If you add more users to your GitLab instance than you are licensed for, payment
If you do not add these users during the renewal process, your license key will not work.
+#### Free Guest users **(ULTIMATE)**
+
+In the **Ultimate** tier, users who are assigned the Guest role do not consume a seat.
+The user must not be assigned any other role, anywhere in the instance.
+
+- If your project is private or internal, a user with the Guest role has
+ [a set of permissions](../../user/permissions.md#project-members-permissions).
+- If your project is public, all users, including those with the Guest role
+ can access your project.
+
+NOTE:
+If a user creates a project, they are assigned the Maintainer or Owner role.
+To prevent a user from creating projects, as an administrator, you can mark the user
+as [external](../../user/admin_area/external_users.md).
+
### Tips for managing users and subscription seats
Managing the number of users against the number of subscription seats can be a challenge:
diff --git a/doc/topics/authentication/index.md b/doc/topics/authentication/index.md
index 12ff5cb50b8..763e16bc31d 100644
--- a/doc/topics/authentication/index.md
+++ b/doc/topics/authentication/index.md
@@ -12,7 +12,7 @@ This page gathers all the resources for the topic **Authentication** within GitL
- [SSH](../../user/ssh.md)
- [Two-factor authentication](../../user/profile/account/two_factor_authentication.md)
-- [Why do I keep getting signed out?](../../user/profile/index.md#why-do-i-keep-getting-signed-out)
+- [Why do you keep getting signed out?](../../user/profile/index.md#why-do-you-keep-getting-signed-out)
- **Articles:**
- [Support for Universal 2nd Factor Authentication - YubiKeys](https://about.gitlab.com/blog/2016/06/22/gitlab-adds-support-for-u2f/)
- [Security Webcast with Yubico](https://about.gitlab.com/blog/2016/08/31/gitlab-and-yubico-security-webcast/)
diff --git a/doc/topics/autodevops/cicd_variables.md b/doc/topics/autodevops/cicd_variables.md
new file mode 100644
index 00000000000..db2b052784d
--- /dev/null
+++ b/doc/topics/autodevops/cicd_variables.md
@@ -0,0 +1,331 @@
+---
+stage: Configure
+group: Configure
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# CI/CD variables
+
+Use CI/CD variables to set up the Auto DevOps domain, provide a custom
+Helm chart, or scale your application.
+
+## Build and deployment variables
+
+Use these variables to customize and deploy your build.
+
+| **CI/CD variable** | **Description** |
+|-----------------------------------------|------------------------------------|
+| `ADDITIONAL_HOSTS` | Fully qualified domain names specified as a comma-separated list that are added to the Ingress hosts. |
+| `<ENVIRONMENT>_ADDITIONAL_HOSTS` | For a specific environment, the fully qualified domain names specified as a comma-separated list that are added to the Ingress hosts. This takes precedence over `ADDITIONAL_HOSTS`. |
+| `AUTO_BUILD_IMAGE_VERSION` | Customize the image version used for the `build` job. See [list of versions](https://gitlab.com/gitlab-org/cluster-integration/auto-build-image/-/releases). |
+| `AUTO_DEPLOY_IMAGE_VERSION` | Customize the image version used for Kubernetes deployment jobs. See [list of versions](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/releases). |
+| `AUTO_DEVOPS_ATOMIC_RELEASE` | As of GitLab 13.0, Auto DevOps uses [`--atomic`](https://v2.helm.sh/docs/helm/#options-43) for Helm deployments by default. Set this variable to `false` to disable the use of `--atomic` |
+| `AUTO_DEVOPS_BUILD_IMAGE_CNB_ENABLED` | Set to `false` to use Herokuish instead of Cloud Native Buildpacks with Auto Build. [More details](stages.md#auto-build-using-cloud-native-buildpacks). |
+| `AUTO_DEVOPS_BUILD_IMAGE_CNB_BUILDER` | The builder used when building with Cloud Native Buildpacks. The default builder is `heroku/buildpacks:18`. [More details](stages.md#auto-build-using-cloud-native-buildpacks). |
+| `AUTO_DEVOPS_BUILD_IMAGE_EXTRA_ARGS` | Extra arguments to be passed to the `docker build` command. Using quotes doesn't prevent word splitting. [More details](customize.md#passing-arguments-to-docker-build). |
+| `AUTO_DEVOPS_BUILD_IMAGE_FORWARDED_CI_VARIABLES` | A [comma-separated list of CI/CD variable names](customize.md#forward-cicd-variables-to-the-build-environment) to be forwarded to the build environment (the buildpack builder or `docker build`). |
+| `AUTO_DEVOPS_BUILD_IMAGE_CNB_PORT` | In GitLab 15.0 and later, port exposed by the generated Docker image. Set to `false` to prevent exposing any ports. Defaults to `5000`. |
+| `AUTO_DEVOPS_CHART` | Helm Chart used to deploy your apps. Defaults to the one [provided by GitLab](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app). |
+| `AUTO_DEVOPS_CHART_REPOSITORY` | Helm Chart repository used to search for charts. Defaults to `https://charts.gitlab.io`. |
+| `AUTO_DEVOPS_CHART_REPOSITORY_NAME` | Used to set the name of the Helm repository. Defaults to `gitlab`. |
+| `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME` | Used to set a username to connect to the Helm repository. Defaults to no credentials. Also set `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD`. |
+| `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD` | Used to set a password to connect to the Helm repository. Defaults to no credentials. Also set `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME`. |
+| `AUTO_DEVOPS_CHART_REPOSITORY_PASS_CREDENTIALS` | From GitLab 14.2, set to a non-empty value to enable forwarding of the Helm repository credentials to the chart server when the chart artifacts are on a different host than repository. |
+| `AUTO_DEVOPS_COMMON_NAME` | From GitLab 15.5, set to a valid domain name to customize the common name used for the TLS certificate. Defaults to `le-$CI_PROJECT_ID.$KUBE_INGRESS_BASE_DOMAIN`. Set to `false` to not set this alternative host on the Ingress. |
+| `AUTO_DEVOPS_DEPLOY_DEBUG` | From GitLab 13.1, if this variable is present, Helm outputs debug logs. |
+| `AUTO_DEVOPS_ALLOW_TO_FORCE_DEPLOY_V<N>` | From [auto-deploy-image](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image) v1.0.0, if this variable is present, a new major version of chart is forcibly deployed. For more information, see [Ignore warnings and continue deploying](upgrading_auto_deploy_dependencies.md#ignore-warnings-and-continue-deploying). |
+| `BUILDPACK_URL` | A full Buildpack URL. [Must point to a URL supported by Pack or Herokuish](customize.md#custom-buildpacks). |
+| `CANARY_ENABLED` | Used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). |
+| `BUILDPACK_VOLUMES` | Specify one or more [Buildpack volumes to mount](stages.md#mount-volumes-into-the-build-container). Use a pipe `|` as list separator. |
+| `CANARY_PRODUCTION_REPLICAS` | Number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md) in the production environment. Takes precedence over `CANARY_REPLICAS`. Defaults to 1. |
+| `CANARY_REPLICAS` | Number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md). Defaults to 1. |
+| `CI_APPLICATION_REPOSITORY` | The repository of container image being built or deployed, `$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG`. For more details, read [Custom container image](customize.md#custom-container-image). |
+| `CI_APPLICATION_TAG` | The tag of the container image being built or deployed, `$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG`. For more details, read [Custom container image](customize.md#custom-container-image). |
+| `DAST_AUTO_DEPLOY_IMAGE_VERSION` | Customize the image version used for DAST deployments on the default branch. Should usually be the same as `AUTO_DEPLOY_IMAGE_VERSION`. See [list of versions](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/releases). |
+| `DOCKERFILE_PATH` | From GitLab 13.2, allows overriding the [default Dockerfile path for the build stage](customize.md#custom-dockerfile) |
+| `HELM_RELEASE_NAME` | From GitLab 12.1, allows the `helm` release name to be overridden. Can be used to assign unique release names when deploying multiple projects to a single namespace. |
+| `HELM_UPGRADE_VALUES_FILE` | From GitLab 12.6, allows the `helm upgrade` values file to be overridden. Defaults to `.gitlab/auto-deploy-values.yaml`. |
+| `HELM_UPGRADE_EXTRA_ARGS` | Allows extra options in `helm upgrade` commands when deploying the application. Using quotes doesn't prevent word splitting. |
+| `INCREMENTAL_ROLLOUT_MODE` | If present, can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. Set to `manual` for manual deployment jobs or `timed` for automatic rollout deployments with a 5 minute delay each one. |
+| `K8S_SECRET_*` | Any variable prefixed with [`K8S_SECRET_`](#configure-application-secret-variables) is made available by Auto DevOps as environment variables to the deployed application. |
+| `KUBE_CONTEXT` | From GitLab 14.5, can be used to select a context to use from `KUBECONFIG`. When `KUBE_CONTEXT` is blank, the default context in `KUBECONFIG` (if any) is used. A context must be selected when used [with the agent for Kubernetes](../../user/clusters/agent/ci_cd_workflow.md). |
+| `KUBE_INGRESS_BASE_DOMAIN` | Can be used to set a domain per cluster. See [cluster domains](../../user/project/clusters/gitlab_managed_clusters.md#base-domain) for more information. |
+| `KUBE_NAMESPACE` | The namespace used for deployments. When using certificate-based clusters, [this value should not be overwritten directly](../../user/project/clusters/deploy_to_cluster.md#custom-namespace). |
+| `KUBECONFIG` | The kubeconfig to use for deployments. User-provided values take priority over GitLab-provided values. |
+| `PRODUCTION_REPLICAS` | Number of replicas to deploy in the production environment. Takes precedence over `REPLICAS` and defaults to 1. For zero downtime upgrades, set to 2 or greater. |
+| `REPLICAS` | Number of replicas to deploy. Defaults to 1. Change this variable instead of [modifying](customize.md#customize-values-for-helm-chart) `replicaCount`. |
+| `ROLLOUT_RESOURCE_TYPE` | Allows specification of the resource type being deployed when using a custom Helm chart. Default value is `deployment`. |
+| `ROLLOUT_STATUS_DISABLED` | From GitLab 12.0, used to disable rollout status check because it does not support all resource types, for example, `cronjob`. |
+| `STAGING_ENABLED` | Used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). |
+| `TRACE` | Set to any value to make Helm commands produce verbose output. You can use this setting to help diagnose Auto DevOps deployment problems. |
+
+## Database variables
+
+Use these variables to integrate CI/CD with PostgreSQL databases.
+
+| **CI/CD variable** | **Description** |
+|-----------------------------------------|------------------------------------|
+| `DB_INITIALIZE` | Used to specify the command to run to initialize the application's PostgreSQL database. Runs inside the application pod. |
+| `DB_MIGRATE` | Used to specify the command to run to migrate the application's PostgreSQL database. Runs inside the application pod. |
+| `POSTGRES_ENABLED` | Whether PostgreSQL is enabled. Defaults to `true`. Set to `false` to disable the automatic deployment of PostgreSQL. |
+| `POSTGRES_USER` | The PostgreSQL user. Defaults to `user`. Set it to use a custom username. |
+| `POSTGRES_PASSWORD` | The PostgreSQL password. Defaults to `testing-password`. Set it to use a custom password. |
+| `POSTGRES_DB` | The PostgreSQL database name. Defaults to the value of [`$CI_ENVIRONMENT_SLUG`](../../ci/variables/index.md#predefined-cicd-variables). Set it to use a custom database name. |
+| `POSTGRES_VERSION` | Tag for the [`postgres` Docker image](https://hub.docker.com/_/postgres) to use. Defaults to `9.6.16` for tests and deployments as of GitLab 13.0 (previously `9.6.2`). If `AUTO_DEVOPS_POSTGRES_CHANNEL` is set to `1`, deployments uses the default version `9.6.2`. |
+| `POSTGRES_HELM_UPGRADE_VALUES_FILE` | In GitLab 13.8 and later, and when using [auto-deploy-image v2](upgrading_auto_deploy_dependencies.md), this variable allows the `helm upgrade` values file for PostgreSQL to be overridden. Defaults to `.gitlab/auto-deploy-postgres-values.yaml`. |
+| `POSTGRES_HELM_UPGRADE_EXTRA_ARGS` | In GitLab 13.8 and later, and when using [auto-deploy-image v2](upgrading_auto_deploy_dependencies.md), this variable allows extra PostgreSQL options in `helm upgrade` commands when deploying the application. Note that using quotes doesn't prevent word splitting. |
+| `POSTGRES_CHART_REPOSITORY` | Helm Chart repository used to search for PostgreSQL chart. Defaults to `https://raw.githubusercontent.com/bitnami/charts/eb5f9a9513d987b519f0ecd732e7031241c50328/bitnami`. |
+| `POSTGRES_CHART_VERSION` | Helm Chart version used for PostgreSQL chart. Defaults to `8.2.1`. |
+
+## Job-disabling variables
+
+Use these variables to disable CI/CD jobs.
+
+| **Job name** | **CI/CD variable** | **GitLab version** | **Description** |
+|----------------------------------------|---------------------------------|-----------------------|-----------------|
+| `.fuzz_base` | `COVFUZZ_DISABLED` | [From GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34984) | [Read more](../../user/application_security/coverage_fuzzing/index.md) about how `.fuzz_base` provide capability for your own jobs. If the variable is present, your jobs aren't created. |
+| `apifuzzer_fuzz` | `API_FUZZING_DISABLED` | [From GitLab 13.3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39135) | If the variable is present, the job isn't created. |
+| `build` | `BUILD_DISABLED` | | If the variable is present, the job isn't created. |
+| `build_artifact` | `BUILD_DISABLED` | | If the variable is present, the job isn't created. |
+| `bandit-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `brakeman-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `canary` | `CANARY_ENABLED` | | This manual job is created if the variable is present. |
+| `cluster_image_scanning` | `CLUSTER_IMAGE_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
+| `code_intelligence` | `CODE_INTELLIGENCE_DISABLED` | From GitLab 13.6 | If the variable is present, the job isn't created. |
+| `code_quality` | `CODE_QUALITY_DISABLED` | | If the variable is present, the job isn't created. |
+| `container_scanning` | `CONTAINER_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
+| `dast` | `DAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `dast_environment_deploy` | `DAST_DISABLED_FOR_DEFAULT_BRANCH` or `DAST_DISABLED` | [From GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17789) | If either variable is present, the job isn't created. |
+| `dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
+| `eslint-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `flawfinder-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `gemnasium-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
+| `gemnasium-maven-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
+| `gemnasium-python-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
+| `gosec-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `kubesec-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `license_management` | `LICENSE_MANAGEMENT_DISABLED` | GitLab 12.7 and earlier | If the variable is present, the job isn't created. Job deprecated [from GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22773) |
+| `license_scanning` | `LICENSE_MANAGEMENT_DISABLED` | [From GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22773) | If the variable is present, the job isn't created. |
+| `load_performance` | `LOAD_PERFORMANCE_DISABLED` | From GitLab 13.2 | If the variable is present, the job isn't created. |
+| `nodejs-scan-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `performance` | `PERFORMANCE_DISABLED` | GitLab 13.12 and earlier | Browser performance. If the variable is present, the job isn't created. Replaced by `browser_performance`. |
+| `browser_performance` | `BROWSER_PERFORMANCE_DISABLED` | From GitLab 14.0 | Browser performance. If the variable is present, the job isn't created. Replaces `performance`. |
+| `phpcs-security-audit-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `pmd-apex-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `review` | `REVIEW_DISABLED` | | If the variable is present, the job isn't created. |
+| `review:stop` | `REVIEW_DISABLED` | | Manual job. If the variable is present, the job isn't created. |
+| `sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `sast:container` | `CONTAINER_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
+| `secret_detection` | `SECRET_DETECTION_DISABLED` | From GitLab 13.1 | If the variable is present, the job isn't created. |
+| `secret_detection_default_branch` | `SECRET_DETECTION_DISABLED` | [From GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22773) | If the variable is present, the job isn't created. |
+| `security-code-scan-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `secrets-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `sobelaw-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `stop_dast_environment` | `DAST_DISABLED_FOR_DEFAULT_BRANCH` or `DAST_DISABLED` | [From GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17789) | If either variable is present, the job isn't created. |
+| `spotbugs-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `test` | `TEST_DISABLED` | | If the variable is present, the job isn't created. |
+| `staging` | `STAGING_ENABLED` | | The job is created if the variable is present. |
+| `stop_review` | `REVIEW_DISABLED` | | If the variable is present, the job isn't created. |
+
+## Configure application secret variables
+
+Some deployed applications require access to secret variables.
+Auto DevOps detects CI/CD variables starting with `K8S_SECRET_`,
+and makes them available to the deployed application as
+environment variables.
+
+To configure secret variables:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > CI/CD**.
+1. Expand **Variables**.
+1. Create a CI/CD variable with the prefix `K8S_SECRET_`. For example, you
+ can create a variable called `K8S_SECRET_RAILS_MASTER_KEY`.
+1. Run an Auto DevOps pipeline, either by manually creating a new
+ pipeline or by pushing a code change to GitLab.
+
+### Kubernetes secrets
+
+Auto DevOps pipelines use your application secret variables to
+populate a Kubernetes secret. This secret is unique per environment.
+When deploying your application, the secret is loaded as environment
+variables in the container running the application. For example, if
+you create a secret called `K8S_SECRET_RAILS_MASTER_KEY`, your
+Kubernetes secret might look like:
+
+```shell
+$ kubectl get secret production-secret -n minimal-ruby-app-54 -o yaml
+
+apiVersion: v1
+data:
+ RAILS_MASTER_KEY: MTIzNC10ZXN0
+kind: Secret
+metadata:
+ creationTimestamp: 2018-12-20T01:48:26Z
+ name: production-secret
+ namespace: minimal-ruby-app-54
+ resourceVersion: "429422"
+ selfLink: /api/v1/namespaces/minimal-ruby-app-54/secrets/production-secret
+ uid: 57ac2bfd-03f9-11e9-b812-42010a9400e4
+type: Opaque
+```
+
+## Update application secrets
+
+Environment variables are generally immutable in a Kubernetes pod.
+If you update an application secret and then manually
+create a new pipeline, running applications do not receive the
+updated secret.
+
+To update application secrets, either:
+
+- Push a code update to GitLab to force the Kubernetes deployment to recreate pods.
+- Manually delete running pods to cause Kubernetes to create new pods with updated
+ secrets.
+
+Variables with multi-line values are not supported due to
+limitations with the Auto DevOps scripting environment.
+
+## Configure replica variables
+
+Add replica variables when you want to scale your deployments:
+
+1. Add a replica variable as a [project CI/CD variable](../../ci/variables/index.md#add-a-cicd-variable-to-a-project).
+1. To scale your application, redeploy it.
+
+ WARNING:
+ Do not scale your application using Kubernetes directly. Helm might not detect the change,
+ and subsequent deployments with Auto DevOps can undo your changes.
+
+### Custom replica variables
+
+You can create custom replica variables with the format `<TRACK>_<ENV>_REPLICAS`:
+
+- `<TRACK>` is the all-caps value of the `track`
+ [Kubernetes label](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/)
+ set in the Helm Chart app definition. If `track` is not set, omit `<TRACK>` from the custom variable.
+- `<ENV>` is the all-caps environment name of the deploy job set in
+ `.gitlab-ci.yml`.
+
+For example, if the environment is `qa` and the track is
+`foo`, create an environment variable called `FOO_QA_REPLICAS`:
+
+```yaml
+QA testing:
+ stage: deploy
+ environment:
+ name: qa
+ script:
+ - deploy foo
+```
+
+The track `foo` must be defined in the application's Helm chart.
+For example:
+
+```yaml
+replicaCount: 1
+image:
+ repository: gitlab.example.com/group/project
+ tag: stable
+ pullPolicy: Always
+ secrets:
+ - name: gitlab-registry
+application:
+ track: foo
+ tier: web
+service:
+ enabled: true
+ name: web
+ type: ClusterIP
+ url: http://my.host.com/
+ externalPort: 5000
+ internalPort: 5000
+```
+
+## Deploy policy for staging and production environments
+
+Auto DevOps typically uses continuous deployment, and pushes
+automatically to the `production` environment whenever a new pipeline
+runs on the default branch. To deploy to production manually, you can
+use the `STAGING_ENABLED` CI/CD variable.
+
+If you set `STAGING_ENABLED`, GitLab automatically deploys the
+application to a `staging` environment. When you're ready to deploy to
+production, GitLab creates a `production_manual` job.
+
+You can also enable manual deployment in your [project settings](requirements.md#auto-devops-deployment-strategy).
+
+## Deploy policy for canary environments **(PREMIUM)**
+
+You can use a [canary environment](../../user/project/canary_deployments.md) before
+deploying any changes to production.
+
+If you set `CANARY_ENABLED`, GitLab creates two [manual jobs](../../ci/pipelines/index.md#add-manual-interaction-to-your-pipeline):
+
+- `canary` - Deploys the application to the canary environment.
+- `production_manual` - Deploys the application to production.
+
+## Incremental rollout to production **(PREMIUM)**
+
+Use an incremental rollout to continuously deploy your application,
+starting with only a few pods. You can increase the number of pods
+manually.
+
+You can enable manual deployment in your [project settings](requirements.md#auto-devops-deployment-strategy),
+or by setting `INCREMENTAL_ROLLOUT_MODE` to `manual`.
+
+If you set `INCREMENTAL_ROLLOUT_MODE` to `manual`, GitLab creates four
+manual jobs:
+
+1. `rollout 10%`
+1. `rollout 25%`
+1. `rollout 50%`
+1. `rollout 100%`
+
+The percentage is based on the `REPLICAS` CI/CD variable, and defines the number of
+pods used for deployment. For example, if the value is `10` and you run the
+`10%` rollout job, your application is deployed to only one pod.
+
+You can run the rollout jobs in any order. To scale down, rerun a
+lower percentage job.
+
+After you run the `rollout 100%` job, you cannot scale down, and must
+[rollback your deployment](../../ci/environments/index.md#retry-or-roll-back-a-deployment).
+
+### Example incremental rollout configurations
+
+Without `INCREMENTAL_ROLLOUT_MODE` and without `STAGING_ENABLED`:
+
+![Staging and rollout disabled](img/rollout_staging_disabled.png)
+
+Without `INCREMENTAL_ROLLOUT_MODE` and with `STAGING_ENABLED`:
+
+![Staging enabled](img/staging_enabled.png)
+
+With `INCREMENTAL_ROLLOUT_MODE` set to `manual` and without `STAGING_ENABLED`:
+
+![Rollout enabled](img/rollout_enabled.png)
+
+With `INCREMENTAL_ROLLOUT_MODE` set to `manual` and with `STAGING_ENABLED`:
+
+![Rollout and staging enabled](img/rollout_staging_enabled.png)
+
+WARNING:
+This configuration is deprecated, and is scheduled to be removed in the future.
+
+## Timed incremental rollout to production **(PREMIUM)**
+
+Use a timed incremental rollout to continuously deploy your application, starting with
+only a few pods.
+
+You can enable timed incremental deployment in your [project settings](requirements.md#auto-devops-deployment-strategy),
+or by setting the `INCREMENTAL_ROLLOUT_MODE` CI/CD variable to `timed`.
+
+If you set `INCREMENTAL_ROLLOUT_MODE` to `timed`, GitLab creates four jobs:
+
+1. `timed rollout 10%`
+1. `timed rollout 25%`
+1. `timed rollout 50%`
+1. `timed rollout 100%`
+
+There is a five-minute delay between jobs.
diff --git a/doc/topics/autodevops/cloud_deployments/auto_devops_with_gke.md b/doc/topics/autodevops/cloud_deployments/auto_devops_with_gke.md
index 8a041b08a4d..78c51572973 100644
--- a/doc/topics/autodevops/cloud_deployments/auto_devops_with_gke.md
+++ b/doc/topics/autodevops/cloud_deployments/auto_devops_with_gke.md
@@ -231,7 +231,7 @@ takes you to the pod's logs page.
NOTE:
The example shows only one pod hosting the application at the moment, but you can add
-more pods by defining the [`REPLICAS` CI/CD variable](../customize.md#cicd-variables)
+more pods by defining the [`REPLICAS` CI/CD variable](../cicd_variables.md)
in **Settings > CI/CD > Variables**.
### Work with branches
@@ -300,7 +300,7 @@ and customized to fit your workflow. Here are some helpful resources for further
1. [Auto DevOps](../index.md)
1. [Multiple Kubernetes clusters](../multiple_clusters_auto_devops.md)
-1. [Incremental rollout to production](../customize.md#incremental-rollout-to-production)
-1. [Disable jobs you don't need with CI/CD variables](../customize.md#cicd-variables)
+1. [Incremental rollout to production](../cicd_variables.md#incremental-rollout-to-production)
+1. [Disable jobs you don't need with CI/CD variables](../cicd_variables.md)
1. [Use your own buildpacks to build your application](../customize.md#custom-buildpacks)
1. [Prometheus monitoring](../../../user/project/integrations/prometheus.md)
diff --git a/doc/topics/autodevops/customize.md b/doc/topics/autodevops/customize.md
index 264ac790453..c42f5825b6a 100644
--- a/doc/topics/autodevops/customize.md
+++ b/doc/topics/autodevops/customize.md
@@ -105,7 +105,7 @@ You can override this behavior by defining specific variables:
These variables also affect Auto Build and Auto Container Scanning. If you don't want to build and push an image to
`$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG`, consider
-including only `Jobs/Deploy.gitlab-ci.yml`, or [disabling the `build` jobs](#disable-jobs).
+including only `Jobs/Deploy.gitlab-ci.yml`, or [disabling the `build` jobs](cicd_variables.md#job-disabling-variables).
If you use Auto Container Scanning and set a value for `$CI_APPLICATION_REPOSITORY`, then you should
also update `$CS_DEFAULT_BRANCH_IMAGE`. See [Setting the default branch image](../../user/application_security/container_scanning/index.md#setting-the-default-branch-image)
@@ -187,11 +187,11 @@ You can override the default values in the `values.yaml` file in the
- Adding a file named `.gitlab/auto-deploy-values.yaml` to your repository, which is
automatically used, if found.
- Adding a file with a different name or path to the repository, and setting the
- `HELM_UPGRADE_VALUES_FILE` [CI/CD variable](#cicd-variables) with
+ `HELM_UPGRADE_VALUES_FILE` [CI/CD variable](cicd_variables.md) with
the path and name.
Some values cannot be overridden with the options above. Settings like `replicaCount` should instead be overridden with the `REPLICAS`
-[build and deployment](#build-and-deployment) CI/CD variable. Follow [this issue](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/issues/31) for more information.
+[build and deployment](cicd_variables.md#build-and-deployment-variables) CI/CD variable. Follow [this issue](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/issues/31) for more information.
NOTE:
For GitLab 12.5 and earlier, use the `HELM_UPGRADE_EXTRA_ARGS` variable
@@ -308,13 +308,13 @@ You can configure many Auto DevOps jobs to run in an [offline environment](../..
To support applications requiring a database,
[PostgreSQL](https://www.postgresql.org/) is provisioned by default. The credentials to access
the database are preconfigured, but can be customized by setting the associated
-[CI/CD variables](#cicd-variables). You can use these credentials to define a `DATABASE_URL`:
+[CI/CD variables](cicd_variables.md). You can use these credentials to define a `DATABASE_URL`:
```yaml
postgres://user:password@postgres-host:postgres-port/postgres-database
```
-### Upgrading PostgresSQL
+### Upgrading PostgreSQL
WARNING:
The CI/CD variable `AUTO_DEVOPS_POSTGRES_CHANNEL` that controls default provisioned
@@ -340,9 +340,9 @@ To set custom values, do one of the following:
- Add a file named `.gitlab/auto-deploy-postgres-values.yaml` to your repository. If found, this
file is used automatically. This file is used by default for PostgreSQL Helm upgrades.
- Add a file with a different name or path to the repository, and set the
- `POSTGRES_HELM_UPGRADE_VALUES_FILE` [environment variable](#database) with the path
+ `POSTGRES_HELM_UPGRADE_VALUES_FILE` [environment variable](cicd_variables.md#database-variables) with the path
and name.
-- Set the `POSTGRES_HELM_UPGRADE_EXTRA_ARGS` [environment variable](#database).
+- Set the `POSTGRES_HELM_UPGRADE_EXTRA_ARGS` [environment variable](cicd_variables.md#database-variables).
### Using external PostgreSQL database providers
@@ -371,342 +371,6 @@ You must define environment-scoped CI/CD variables for `POSTGRES_ENABLED` and
You must ensure that your Kubernetes cluster has network access to wherever
PostgreSQL is hosted.
-## CI/CD variables
-
-The following variables can be used for setting up the Auto DevOps domain,
-providing a custom Helm chart, or scaling your application. PostgreSQL can
-also be customized, and you can use a [custom buildpack](#custom-buildpacks).
-
-### Build and deployment
-
-The following table lists CI/CD variables related to building and deploying
-applications.
-
-| **CI/CD Variable** | **Description** |
-|-----------------------------------------|------------------------------------|
-| `ADDITIONAL_HOSTS` | Fully qualified domain names specified as a comma-separated list that are added to the Ingress hosts. |
-| `<ENVIRONMENT>_ADDITIONAL_HOSTS` | For a specific environment, the fully qualified domain names specified as a comma-separated list that are added to the Ingress hosts. This takes precedence over `ADDITIONAL_HOSTS`. |
-| `AUTO_BUILD_IMAGE_VERSION` | Customize the image version used for the `build` job. See [list of versions](https://gitlab.com/gitlab-org/cluster-integration/auto-build-image/-/releases). |
-| `AUTO_DEPLOY_IMAGE_VERSION` | Customize the image version used for Kubernetes deployment jobs. See [list of versions](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/releases). |
-| `AUTO_DEVOPS_ATOMIC_RELEASE` | As of GitLab 13.0, Auto DevOps uses [`--atomic`](https://v2.helm.sh/docs/helm/#options-43) for Helm deployments by default. Set this variable to `false` to disable the use of `--atomic` |
-| `AUTO_DEVOPS_BUILD_IMAGE_CNB_ENABLED` | Set to `false` to use Herokuish instead of Cloud Native Buildpacks with Auto Build. [More details](stages.md#auto-build-using-cloud-native-buildpacks). |
-| `AUTO_DEVOPS_BUILD_IMAGE_CNB_BUILDER` | The builder used when building with Cloud Native Buildpacks. The default builder is `heroku/buildpacks:18`. [More details](stages.md#auto-build-using-cloud-native-buildpacks). |
-| `AUTO_DEVOPS_BUILD_IMAGE_EXTRA_ARGS` | Extra arguments to be passed to the `docker build` command. Note that using quotes doesn't prevent word splitting. [More details](#passing-arguments-to-docker-build). |
-| `AUTO_DEVOPS_BUILD_IMAGE_FORWARDED_CI_VARIABLES` | A [comma-separated list of CI/CD variable names](#forward-cicd-variables-to-the-build-environment) to be forwarded to the build environment (the buildpack builder or `docker build`). |
-| `AUTO_DEVOPS_BUILD_IMAGE_CNB_PORT` | In GitLab 15.0 and later, port exposed by the generated Docker image. Set to `false` to prevent exposing any ports. Defaults to `5000`. |
-| `AUTO_DEVOPS_CHART` | Helm Chart used to deploy your apps. Defaults to the one [provided by GitLab](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app). |
-| `AUTO_DEVOPS_CHART_REPOSITORY` | Helm Chart repository used to search for charts. Defaults to `https://charts.gitlab.io`. |
-| `AUTO_DEVOPS_CHART_REPOSITORY_NAME` | Used to set the name of the Helm repository. Defaults to `gitlab`. |
-| `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME` | Used to set a username to connect to the Helm repository. Defaults to no credentials. Also set `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD`. |
-| `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD` | Used to set a password to connect to the Helm repository. Defaults to no credentials. Also set `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME`. |
-| `AUTO_DEVOPS_CHART_REPOSITORY_PASS_CREDENTIALS` | From GitLab 14.2, set to a non-empty value to enable forwarding of the Helm repository credentials to the chart server when the chart artifacts are on a different host than repository. |
-| `AUTO_DEVOPS_COMMON_NAME` | From GitLab 15.5, set to a valid domain name to customize the common name used for the TLS certificate. Defaults to `le-$CI_PROJECT_ID.$KUBE_INGRESS_BASE_DOMAIN`. Set to `false` to not set this alternative host on the Ingress. |
-| `AUTO_DEVOPS_DEPLOY_DEBUG` | From GitLab 13.1, if this variable is present, Helm outputs debug logs. |
-| `AUTO_DEVOPS_ALLOW_TO_FORCE_DEPLOY_V<N>` | From [auto-deploy-image](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image) v1.0.0, if this variable is present, a new major version of chart is forcibly deployed. For more information, see [Ignore warnings and continue deploying](upgrading_auto_deploy_dependencies.md#ignore-warnings-and-continue-deploying). |
-| `BUILDPACK_URL` | Buildpack's full URL. [Must point to a URL supported by Pack or Herokuish](#custom-buildpacks). |
-| `CANARY_ENABLED` | Used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). |
-| `BUILDPACK_VOLUMES` | Specify one or more [Buildpack volumes to mount](stages.md#mount-volumes-into-the-build-container). Use a pipe `|` as list separator. |
-| `CANARY_PRODUCTION_REPLICAS` | Number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md) in the production environment. Takes precedence over `CANARY_REPLICAS`. Defaults to 1. |
-| `CANARY_REPLICAS` | Number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md). Defaults to 1. |
-| `CI_APPLICATION_REPOSITORY` | The repository of container image being built or deployed, `$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG`. For more details, read [Custom container image](#custom-container-image). |
-| `CI_APPLICATION_TAG` | The tag of the container image being built or deployed, `$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG`. For more details, read [Custom container image](#custom-container-image). |
-| `DAST_AUTO_DEPLOY_IMAGE_VERSION` | Customize the image version used for DAST deployments on the default branch. Should usually be the same as `AUTO_DEPLOY_IMAGE_VERSION`. See [list of versions](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/releases). |
-| `DOCKERFILE_PATH` | From GitLab 13.2, allows overriding the [default Dockerfile path for the build stage](#custom-dockerfile) |
-| `HELM_RELEASE_NAME` | From GitLab 12.1, allows the `helm` release name to be overridden. Can be used to assign unique release names when deploying multiple projects to a single namespace. |
-| `HELM_UPGRADE_VALUES_FILE` | From GitLab 12.6, allows the `helm upgrade` values file to be overridden. Defaults to `.gitlab/auto-deploy-values.yaml`. |
-| `HELM_UPGRADE_EXTRA_ARGS` | Allows extra options in `helm upgrade` commands when deploying the application. Note that using quotes doesn't prevent word splitting. |
-| `INCREMENTAL_ROLLOUT_MODE` | If present, can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. Set to `manual` for manual deployment jobs or `timed` for automatic rollout deployments with a 5 minute delay each one. |
-| `K8S_SECRET_*` | Any variable prefixed with [`K8S_SECRET_`](#application-secret-variables) is made available by Auto DevOps as environment variables to the deployed application. |
-| `KUBE_CONTEXT` | From GitLab 14.5, can be used to select a context to use from `KUBECONFIG`. When `KUBE_CONTEXT` is blank, the default context in `KUBECONFIG` (if any) is used. A context must be selected when used [with the agent for Kubernetes](../../user/clusters/agent/ci_cd_workflow.md). |
-| `KUBE_INGRESS_BASE_DOMAIN` | Can be used to set a domain per cluster. See [cluster domains](../../user/project/clusters/gitlab_managed_clusters.md#base-domain) for more information. |
-| `KUBE_NAMESPACE` | The namespace used for deployments. When using certificate-based clusters, [this value should not be overwritten directly](../../user/project/clusters/deploy_to_cluster.md#custom-namespace). |
-| `KUBECONFIG` | The kubeconfig to use for deployments. User-provided values take priority over GitLab-provided values. |
-| `PRODUCTION_REPLICAS` | Number of replicas to deploy in the production environment. Takes precedence over `REPLICAS` and defaults to 1. For zero downtime upgrades, set to 2 or greater. |
-| `REPLICAS` | Number of replicas to deploy. Defaults to 1. Change this variable instead of [modifying](#customize-values-for-helm-chart) `replicaCount`. |
-| `ROLLOUT_RESOURCE_TYPE` | Allows specification of the resource type being deployed when using a custom Helm chart. Default value is `deployment`. |
-| `ROLLOUT_STATUS_DISABLED` | From GitLab 12.0, used to disable rollout status check because it does not support all resource types, for example, `cronjob`. |
-| `STAGING_ENABLED` | Used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). |
-| `TRACE` | Set to any value to make Helm commands produce verbose output. You can use this setting to help diagnose Auto DevOps deployment problems. |
-
-NOTE:
-After you set up your replica variables using a
-[project CI/CD variable](../../ci/variables/index.md),
-you can scale your application by redeploying it.
-
-WARNING:
-You should *not* scale your application using Kubernetes directly. This can
-cause confusion with Helm not detecting the change, and subsequent deploys with
-Auto DevOps can undo your changes.
-
-### Database
-
-The following table lists CI/CD variables related to the database.
-
-| **CI/CD Variable** | **Description** |
-|-----------------------------------------|------------------------------------|
-| `DB_INITIALIZE` | Used to specify the command to run to initialize the application's PostgreSQL database. Runs inside the application pod. |
-| `DB_MIGRATE` | Used to specify the command to run to migrate the application's PostgreSQL database. Runs inside the application pod. |
-| `POSTGRES_ENABLED` | Whether PostgreSQL is enabled. Defaults to `true`. Set to `false` to disable the automatic deployment of PostgreSQL. |
-| `POSTGRES_USER` | The PostgreSQL user. Defaults to `user`. Set it to use a custom username. |
-| `POSTGRES_PASSWORD` | The PostgreSQL password. Defaults to `testing-password`. Set it to use a custom password. |
-| `POSTGRES_DB` | The PostgreSQL database name. Defaults to the value of [`$CI_ENVIRONMENT_SLUG`](../../ci/variables/index.md#predefined-cicd-variables). Set it to use a custom database name. |
-| `POSTGRES_VERSION` | Tag for the [`postgres` Docker image](https://hub.docker.com/_/postgres) to use. Defaults to `9.6.16` for tests and deployments as of GitLab 13.0 (previously `9.6.2`). If `AUTO_DEVOPS_POSTGRES_CHANNEL` is set to `1`, deployments uses the default version `9.6.2`. |
-| `POSTGRES_HELM_UPGRADE_VALUES_FILE` | In GitLab 13.8 and later, and when using [auto-deploy-image v2](upgrading_auto_deploy_dependencies.md), this variable allows the `helm upgrade` values file for PostgreSQL to be overridden. Defaults to `.gitlab/auto-deploy-postgres-values.yaml`. |
-| `POSTGRES_HELM_UPGRADE_EXTRA_ARGS` | In GitLab 13.8 and later, and when using [auto-deploy-image v2](upgrading_auto_deploy_dependencies.md), this variable allows extra PostgreSQL options in `helm upgrade` commands when deploying the application. Note that using quotes doesn't prevent word splitting. |
-| `POSTGRES_CHART_REPOSITORY` | Helm Chart repository used to search for PostgreSQL chart. Defaults to `https://raw.githubusercontent.com/bitnami/charts/eb5f9a9513d987b519f0ecd732e7031241c50328/bitnami`. |
-| `POSTGRES_CHART_VERSION` | Helm Chart version used for PostgreSQL chart. Defaults to `8.2.1`. |
-
-### Disable jobs
-
-The following table lists variables used to disable jobs.
-
-| **Job Name** | **CI/CDVariable** | **GitLab version** | **Description** |
-|----------------------------------------|---------------------------------|-----------------------|-----------------|
-| `.fuzz_base` | `COVFUZZ_DISABLED` | [From GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34984) | [Read more](../../user/application_security/coverage_fuzzing/index.md) about how `.fuzz_base` provide capability for your own jobs. If the variable is present, your jobs aren't created. |
-| `apifuzzer_fuzz` | `API_FUZZING_DISABLED` | [From GitLab 13.3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39135) | If the variable is present, the job isn't created. |
-| `build` | `BUILD_DISABLED` | | If the variable is present, the job isn't created. |
-| `build_artifact` | `BUILD_DISABLED` | | If the variable is present, the job isn't created. |
-| `bandit-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
-| `brakeman-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
-| `canary` | `CANARY_ENABLED` | | This manual job is created if the variable is present. |
-| `cluster_image_scanning` | `CLUSTER_IMAGE_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
-| `code_intelligence` | `CODE_INTELLIGENCE_DISABLED` | From GitLab 13.6 | If the variable is present, the job isn't created. |
-| `code_quality` | `CODE_QUALITY_DISABLED` | | If the variable is present, the job isn't created. |
-| `container_scanning` | `CONTAINER_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
-| `dast` | `DAST_DISABLED` | | If the variable is present, the job isn't created. |
-| `dast_environment_deploy` | `DAST_DISABLED_FOR_DEFAULT_BRANCH` or `DAST_DISABLED` | [From GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17789) | If either variable is present, the job isn't created. |
-| `dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
-| `eslint-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
-| `flawfinder-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
-| `gemnasium-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
-| `gemnasium-maven-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
-| `gemnasium-python-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
-| `gosec-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
-| `kubesec-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
-| `license_management` | `LICENSE_MANAGEMENT_DISABLED` | GitLab 12.7 and earlier | If the variable is present, the job isn't created. Job deprecated [from GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22773) |
-| `license_scanning` | `LICENSE_MANAGEMENT_DISABLED` | [From GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22773) | If the variable is present, the job isn't created. |
-| `load_performance` | `LOAD_PERFORMANCE_DISABLED` | From GitLab 13.2 | If the variable is present, the job isn't created. |
-| `nodejs-scan-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
-| `performance` | `PERFORMANCE_DISABLED` | GitLab 13.12 and earlier | Browser performance. If the variable is present, the job isn't created. Replaced by `browser_performance`. |
-| `browser_performance` | `BROWSER_PERFORMANCE_DISABLED` | From GitLab 14.0 | Browser performance. If the variable is present, the job isn't created. Replaces `performance`. |
-| `phpcs-security-audit-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
-| `pmd-apex-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
-| `review` | `REVIEW_DISABLED` | | If the variable is present, the job isn't created. |
-| `review:stop` | `REVIEW_DISABLED` | | Manual job. If the variable is present, the job isn't created. |
-| `sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
-| `sast:container` | `CONTAINER_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
-| `secret_detection` | `SECRET_DETECTION_DISABLED` | From GitLab 13.1 | If the variable is present, the job isn't created. |
-| `secret_detection_default_branch` | `SECRET_DETECTION_DISABLED` | [From GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22773) | If the variable is present, the job isn't created. |
-| `security-code-scan-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
-| `secrets-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
-| `sobelaw-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
-| `stop_dast_environment` | `DAST_DISABLED_FOR_DEFAULT_BRANCH` or `DAST_DISABLED` | [From GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17789) | If either variable is present, the job isn't created. |
-| `spotbugs-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
-| `test` | `TEST_DISABLED` | | If the variable is present, the job isn't created. |
-| `staging` | `STAGING_ENABLED` | | The job is created if the variable is present. |
-| `stop_review` | `REVIEW_DISABLED` | | If the variable is present, the job isn't created. |
-
-### Application secret variables
-
-Some applications need to define secret variables that are accessible by the deployed
-application. Auto DevOps detects CI/CD variables starting with `K8S_SECRET_`, and makes
-these prefixed variables available to the deployed application as environment variables.
-
-To configure your application variables:
-
-1. On the top bar, select **Main menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > CI/CD**.
-1. Expand **Variables**.
-1. Create a CI/CD variable, ensuring the key is prefixed with
- `K8S_SECRET_`. For example, you can create a variable with key
- `K8S_SECRET_RAILS_MASTER_KEY`.
-
-1. Run an Auto DevOps pipeline, either by manually creating a new
- pipeline or by pushing a code change to GitLab.
-
-Auto DevOps pipelines take your application secret variables to
-populate a Kubernetes secret. This secret is unique per environment.
-When deploying your application, the secret is loaded as environment
-variables in the container running the application. Following the
-example above, you can see the secret below containing the
-`RAILS_MASTER_KEY` variable.
-
-```shell
-$ kubectl get secret production-secret -n minimal-ruby-app-54 -o yaml
-
-apiVersion: v1
-data:
- RAILS_MASTER_KEY: MTIzNC10ZXN0
-kind: Secret
-metadata:
- creationTimestamp: 2018-12-20T01:48:26Z
- name: production-secret
- namespace: minimal-ruby-app-54
- resourceVersion: "429422"
- selfLink: /api/v1/namespaces/minimal-ruby-app-54/secrets/production-secret
- uid: 57ac2bfd-03f9-11e9-b812-42010a9400e4
-type: Opaque
-```
-
-Environment variables are generally considered immutable in a Kubernetes pod.
-If you update an application secret without changing any code, then manually
-create a new pipeline, any running application pods don't receive
-the updated secrets. To update the secrets, either:
-
-- Push a code update to GitLab to force the Kubernetes deployment to recreate pods.
-- Manually delete running pods to cause Kubernetes to create new pods with updated
- secrets.
-
-Variables with multi-line values are not currently supported due to
-limitations with the current Auto DevOps scripting environment.
-
-### Advanced replica variables setup
-
-Apart from the two replica-related variables for production mentioned above,
-you can also use other variables for different environments.
-
-The Kubernetes' label named `track`, GitLab CI/CD environment names, and the
-replicas environment variable are combined into the format `TRACK_ENV_REPLICAS`,
-enabling you to define your own variables for scaling the pod's replicas:
-
-- `TRACK`: The capitalized value of the `track`
- [Kubernetes label](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/)
- in the Helm Chart app definition. If not set, it isn't taken into account
- to the variable name.
-- `ENV`: The capitalized environment name of the deploy job, set in
- `.gitlab-ci.yml`.
-
-In the example below, the environment's name is `qa`, and it deploys the track
-`foo`, which results in an environment variable named `FOO_QA_REPLICAS`:
-
-```yaml
-QA testing:
- stage: deploy
- environment:
- name: qa
- script:
- - deploy foo
-```
-
-The track `foo` being referenced must also be defined in the application's Helm chart, like:
-
-```yaml
-replicaCount: 1
-image:
- repository: gitlab.example.com/group/project
- tag: stable
- pullPolicy: Always
- secrets:
- - name: gitlab-registry
-application:
- track: foo
- tier: web
-service:
- enabled: true
- name: web
- type: ClusterIP
- url: http://my.host.com/
- externalPort: 5000
- internalPort: 5000
-```
-
-### Deploy policy for staging and production environments
-
-NOTE:
-You can also set this inside your [project's settings](requirements.md#auto-devops-deployment-strategy).
-
-The normal behavior of Auto DevOps is to use continuous deployment, pushing
-automatically to the `production` environment every time a new pipeline is run
-on the default branch. However, there are cases where you might want to use a
-staging environment, and deploy to production manually. For this scenario, the
-`STAGING_ENABLED` CI/CD variable was introduced.
-
-If you define `STAGING_ENABLED` with a non-empty value, then GitLab automatically deploys the application
-to a `staging` environment, and creates a `production_manual` job for
-you when you're ready to manually deploy to production.
-
-### Deploy policy for canary environments **(PREMIUM)**
-
-You can use a [canary environment](../../user/project/canary_deployments.md) before
-deploying any changes to production.
-
-If you define `CANARY_ENABLED` with a non-empty value, then two manual jobs are created:
-
-- `canary` - Deploys the application to the canary environment.
-- `production_manual` - Manually deploys the application to production.
-
-### Incremental rollout to production **(PREMIUM)**
-
-NOTE:
-You can also set this inside your [project's settings](requirements.md#auto-devops-deployment-strategy).
-
-When you're ready to deploy a new version of your app to production, you may want
-to use an incremental rollout to replace just a few pods with the latest code to
-check how the application is behaving before manually increasing the rollout up to 100%.
-
-If `INCREMENTAL_ROLLOUT_MODE` is set to `manual` in your project, then instead
-of the standard `production` job, 4 different
-[manual jobs](../../ci/pipelines/index.md#add-manual-interaction-to-your-pipeline)
-are created:
-
-1. `rollout 10%`
-1. `rollout 25%`
-1. `rollout 50%`
-1. `rollout 100%`
-
-The percentage is based on the `REPLICAS` CI/CD variable, and defines the number of
-pods you want to have for your deployment. If the value is `10`, and you run the
-`10%` rollout job, there is `1` new pod and `9` old ones.
-
-To start a job, select the play icon (**{play}**) next to the job's name. You're not
-required to go from `10%` to `100%`, you can jump to whatever job you want.
-You can also scale down by running a lower percentage job, just before hitting
-`100%`. Once you get to `100%`, you can't scale down, and you'd have to roll
-back by redeploying the old version using the
-[rollback button](../../ci/environments/index.md#retry-or-roll-back-a-deployment) in the
-environment page.
-
-Below, you can see how the pipeline appears if the rollout or staging
-variables are defined.
-
-Without `INCREMENTAL_ROLLOUT_MODE` and without `STAGING_ENABLED`:
-
-![Staging and rollout disabled](img/rollout_staging_disabled.png)
-
-Without `INCREMENTAL_ROLLOUT_MODE` and with `STAGING_ENABLED`:
-
-![Staging enabled](img/staging_enabled.png)
-
-With `INCREMENTAL_ROLLOUT_MODE` set to `manual` and without `STAGING_ENABLED`:
-
-![Rollout enabled](img/rollout_enabled.png)
-
-With `INCREMENTAL_ROLLOUT_MODE` set to `manual` and with `STAGING_ENABLED`
-
-![Rollout and staging enabled](img/rollout_staging_enabled.png)
-
-WARNING:
-This configuration is deprecated, and is scheduled to be removed in the future.
-
-### Timed incremental rollout to production **(PREMIUM)**
-
-NOTE:
-You can also set this inside your [project's settings](requirements.md#auto-devops-deployment-strategy).
-
-This configuration is based on
-[incremental rollout to production](#incremental-rollout-to-production).
-
-Everything behaves the same way, except:
-
-- To enable it, set the `INCREMENTAL_ROLLOUT_MODE` CI/CD variable to `timed`.
-- Instead of the standard `production` job, the following jobs are created with
- a 5 minute delay between each:
-
- 1. `timed rollout 10%`
- 1. `timed rollout 25%`
- 1. `timed rollout 50%`
- 1. `timed rollout 100%`
-
## Auto DevOps banner
The following Auto DevOps banner displays for users with Maintainer or greater
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index 73e87aa190f..d16229b525f 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -129,7 +129,7 @@ for the subgroups and projects where you don't want to use it.
Prerequisites:
-- You must have at least the Owner role for the group.
+- You must have the Owner role for the group.
To enable Auto DevOps for a group:
diff --git a/doc/topics/autodevops/multiple_clusters_auto_devops.md b/doc/topics/autodevops/multiple_clusters_auto_devops.md
index af8c54a8edd..cf775a35eb7 100644
--- a/doc/topics/autodevops/multiple_clusters_auto_devops.md
+++ b/doc/topics/autodevops/multiple_clusters_auto_devops.md
@@ -25,7 +25,7 @@ To deploy your environments to different Kubernetes clusters:
1. [Install a GitLab Agent on each cluster](../../user/clusters/agent/index.md).
1. [Configure each agent to access your project](../../user/clusters/agent/install/index.md#configure-your-agent).
1. [Install NGINX Ingress Controller](cloud_deployments/auto_devops_with_gke.md#install-ingress) in each cluster. Save the IP address and Kubernetes namespace for the next step.
-1. [Configure the Auto DevOps CI/CD Pipeline variables](customize.md#build-and-deployment)
+1. [Configure the Auto DevOps CI/CD Pipeline variables](cicd_variables.md#build-and-deployment-variables)
- Set up a `KUBE_CONTEXT` variable [for each environment](../../ci/variables/index.md#limit-the-environment-scope-of-a-cicd-variable). The value must point to the agent of the relevant cluster.
- Set up a `KUBE_INGRESS_BASE_DOMAIN`. You must [configure the base domain](requirements.md#auto-devops-base-domain) for each environment to point to the Ingress of the relevant cluster.
- Add a `KUBE_NAMESPACE` variable with a value of the Kubernetes namespace you want your deployments to target. You can scope the variable to multiple environments.
@@ -44,8 +44,8 @@ NOTE:
| Cluster name | Cluster environment scope | `KUBE_INGRESS_BASE_DOMAIN` value | `KUBE CONTEXT` value | Variable environment scope | Notes |
| :------------| :-------------------------| :------------------------------- | :--------------------------------- | :--------------------------|:--|
| review | `review/*` | `review.example.com` | `path/to/project:review-agent` | `review/*` | A review cluster that runs all [Review Apps](../../ci/review_apps/index.md).|
-| staging | `staging` | `staging.example.com` | `path/to/project:staging-agent` | `staging` | Optional. A staging cluster that runs the deployments of the staging environments. You must [enable it first](customize.md#deploy-policy-for-staging-and-production-environments). |
-| production | `production` | `example.com` | `path/to/project:production-agent` | `production` | A production cluster that runs the production environment deployments. You can use [incremental rollouts](customize.md#incremental-rollout-to-production). |
+| staging | `staging` | `staging.example.com` | `path/to/project:staging-agent` | `staging` | Optional. A staging cluster that runs the deployments of the staging environments. You must [enable it first](cicd_variables.md#deploy-policy-for-staging-and-production-environments). |
+| production | `production` | `example.com` | `path/to/project:production-agent` | `production` | A production cluster that runs the production environment deployments. You can use [incremental rollouts](cicd_variables.md#incremental-rollout-to-production). |
## Test your configuration
diff --git a/doc/topics/autodevops/prepare_deployment.md b/doc/topics/autodevops/prepare_deployment.md
index 7805f9cd9c6..ebba20ca821 100644
--- a/doc/topics/autodevops/prepare_deployment.md
+++ b/doc/topics/autodevops/prepare_deployment.md
@@ -21,8 +21,8 @@ that works best for your needs:
| Deployment strategy | Setup | Methodology |
|--|--|--|
| **Continuous deployment to production** | Enables [Auto Deploy](stages.md#auto-deploy) with the default branch continuously deployed to production. | Continuous deployment to production.|
-| **Continuous deployment to production using timed incremental rollout** | Sets the [`INCREMENTAL_ROLLOUT_MODE`](customize.md#timed-incremental-rollout-to-production) variable to `timed`. | Continuously deploy to production with a 5 minutes delay between rollouts. |
-| **Automatic deployment to staging, manual deployment to production** | Sets [`STAGING_ENABLED`](customize.md#deploy-policy-for-staging-and-production-environments) to `1` and [`INCREMENTAL_ROLLOUT_MODE`](customize.md#incremental-rollout-to-production) to `manual`. | The default branch is continuously deployed to staging and continuously delivered to production. |
+| **Continuous deployment to production using timed incremental rollout** | Sets the [`INCREMENTAL_ROLLOUT_MODE`](cicd_variables.md#timed-incremental-rollout-to-production) variable to `timed`. | Continuously deploy to production with a 5 minutes delay between rollouts. |
+| **Automatic deployment to staging, manual deployment to production** | Sets [`STAGING_ENABLED`](cicd_variables.md#deploy-policy-for-staging-and-production-environments) to `1` and [`INCREMENTAL_ROLLOUT_MODE`](cicd_variables.md#incremental-rollout-to-production) to `manual`. | The default branch is continuously deployed to staging and continuously delivered to production. |
You can choose the deployment method when enabling Auto DevOps or later:
diff --git a/doc/topics/autodevops/requirements.md b/doc/topics/autodevops/requirements.md
index 7c69e0327c8..f5e06843e60 100644
--- a/doc/topics/autodevops/requirements.md
+++ b/doc/topics/autodevops/requirements.md
@@ -36,8 +36,8 @@ that works best for your needs:
| Deployment strategy | Setup | Methodology |
|--|--|--|
| **Continuous deployment to production** | Enables [Auto Deploy](stages.md#auto-deploy) with the default branch continuously deployed to production. | Continuous deployment to production.|
-| **Continuous deployment to production using timed incremental rollout** | Sets the [`INCREMENTAL_ROLLOUT_MODE`](customize.md#timed-incremental-rollout-to-production) variable to `timed`. | Continuously deploy to production with a 5 minutes delay between rollouts. |
-| **Automatic deployment to staging, manual deployment to production** | Sets [`STAGING_ENABLED`](customize.md#deploy-policy-for-staging-and-production-environments) to `1` and [`INCREMENTAL_ROLLOUT_MODE`](customize.md#incremental-rollout-to-production) to `manual`. | The default branch is continuously deployed to staging and continuously delivered to production. |
+| **Continuous deployment to production using timed incremental rollout** | Sets the [`INCREMENTAL_ROLLOUT_MODE`](cicd_variables.md#timed-incremental-rollout-to-production) variable to `timed`. | Continuously deploy to production with a 5 minutes delay between rollouts. |
+| **Automatic deployment to staging, manual deployment to production** | Sets [`STAGING_ENABLED`](cicd_variables.md#deploy-policy-for-staging-and-production-environments) to `1` and [`INCREMENTAL_ROLLOUT_MODE`](cicd_variables.md#incremental-rollout-to-production) to `manual`. | The default branch is continuously deployed to staging and continuously delivered to production. |
You can choose the deployment method when enabling Auto DevOps or later:
diff --git a/doc/topics/autodevops/stages.md b/doc/topics/autodevops/stages.md
index 022ee131e40..0ca9c6fa3de 100644
--- a/doc/topics/autodevops/stages.md
+++ b/doc/topics/autodevops/stages.md
@@ -385,7 +385,7 @@ default, but the
[Auto DevOps template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml)
contains job definitions for these tasks if you want to enable them.
-You can use [CI/CD variables](customize.md#cicd-variables) to automatically
+You can use [CI/CD variables](cicd_variables.md) to automatically
scale your pod replicas, and to apply custom arguments to the Auto DevOps `helm upgrade`
commands. This is an easy way to
[customize the Auto Deploy Helm chart](customize.md#custom-helm-chart).
@@ -620,4 +620,4 @@ for updates.
This stage is enabled by default. You can disable it by adding the
`CODE_INTELLIGENCE_DISABLED` CI/CD variable. Read more about
-[disabling Auto DevOps jobs](../../topics/autodevops/customize.md#disable-jobs).
+[disabling Auto DevOps jobs](../../topics/autodevops/cicd_variables.md#job-disabling-variables).
diff --git a/doc/topics/autodevops/troubleshooting.md b/doc/topics/autodevops/troubleshooting.md
index ae3cc42223f..ef420323b32 100644
--- a/doc/topics/autodevops/troubleshooting.md
+++ b/doc/topics/autodevops/troubleshooting.md
@@ -13,7 +13,7 @@ Auto DevOps, and any available workarounds.
Set the CI/CD variable `TRACE` to any value to make Helm commands produce verbose output. You can use this output to diagnose Auto DevOps deployment problems.
-You can resolve some problems with Auto DevOps deployment by changing advanced Auto DevOps configuration variables. Read more about [customizing Auto DevOps CI/CD variables](customize.md#cicd-variables).
+You can resolve some problems with Auto DevOps deployment by changing advanced Auto DevOps configuration variables. Read more about [customizing Auto DevOps CI/CD variables](cicd_variables.md).
## Unable to select a buildpack
@@ -40,7 +40,7 @@ The following are possible reasons:
If your pipeline fails with the following message:
```plaintext
-Found errors in your .gitlab-ci.yml:
+Unable to create pipeline
jobs:test config key may not be used with `rules`: only
```
diff --git a/doc/topics/autodevops/upgrading_auto_deploy_dependencies.md b/doc/topics/autodevops/upgrading_auto_deploy_dependencies.md
index 602bdec6140..4fafc89cac1 100644
--- a/doc/topics/autodevops/upgrading_auto_deploy_dependencies.md
+++ b/doc/topics/autodevops/upgrading_auto_deploy_dependencies.md
@@ -128,7 +128,7 @@ with the [v1 auto-deploy-image](#use-a-specific-version-of-auto-deploy-dependenc
> [Introduced](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/merge_requests/109) in GitLab 13.4.
-Auto Deploy supports advanced deployment strategies such as [canary deployments](customize.md#deploy-policy-for-canary-environments)
+Auto Deploy supports advanced deployment strategies such as [canary deployments](cicd_variables.md#deploy-policy-for-canary-environments)
and [incremental rollouts](../../ci/environments/incremental_rollouts.md).
Previously, `auto-deploy-image` created one service to balance the traffic between
@@ -171,7 +171,7 @@ Alternatively, you can use the [v13.12 Auto DevOps templates archive](https://gi
### Ignore warnings and continue deploying
If you are certain that the new chart version is safe to be deployed, you can add
-the `AUTO_DEVOPS_FORCE_DEPLOY_V<major-version-number>` [CI/CD variable](customize.md#build-and-deployment)
+the `AUTO_DEVOPS_FORCE_DEPLOY_V<major-version-number>` [CI/CD variable](cicd_variables.md#build-and-deployment-variables)
to force the deployment to continue.
For example, if you want to deploy the `v2.0.0` chart on a deployment that previously
diff --git a/doc/topics/awesome_co.md b/doc/topics/awesome_co.md
index 49e39542b2b..fc5f79b4d18 100644
--- a/doc/topics/awesome_co.md
+++ b/doc/topics/awesome_co.md
@@ -71,7 +71,7 @@ AwesomeCo seeding uses FactoryBot definitions from `spec/factories` which ...
1. Are always up-to-date
1. Execute on the lowest-level (`ActiveRecord`) possible to create data as quickly as possible
-> _from the [FactoryBot README](https://github.com/thoughtbot/factory_bot#readme_) : factory_bot is a fixtures replacement with a straightforward definition syntax, support for multiple build
+> From the [FactoryBot README](https://github.com/thoughtbot/factory_bot#readme_) : `factory_bot` is a fixtures replacement with a straightforward definition syntax, support for multiple build
> strategies (saved instances, unsaved instances, attribute hashes, and stubbed objects), and support for multiple factories for the same class, including factory
> inheritance
diff --git a/doc/topics/git/feature_branch_development.md b/doc/topics/git/feature_branch_development.md
index de9f9980811..d53c8eae835 100644
--- a/doc/topics/git/feature_branch_development.md
+++ b/doc/topics/git/feature_branch_development.md
@@ -77,7 +77,7 @@ In this case, the feature branch would be `release-X-Y`. Assuming the `release-X
1. After you select **Create merge request**, an option to **Change branches** displays. Select that option.
-1. In the **New Merge Request** screen, you can now select the **Source** and **Target** branches.
+1. In the **New merge request** screen, you can now select the **Source** and **Target** branches.
In the screenshot shown,
we have selected `test-branch` as the source, and `release-13-0` as the target.
@@ -87,14 +87,14 @@ we have selected `test-branch` as the source, and `release-13-0` as the target.
You should see an entry similar to:
```plaintext
- New Merge Request
+ New merge request
From test-branch into release-13-0
```
An entry like this confirms your merge request's destination.
-1. Make any additional changes in the **New Merge Request** screen, and select **Submit merge request**.
+1. Make any additional changes in the **New merge request** screen, and select **Create merge request**.
1. In the new merge request, look for **Request to merge**. An entry similar to this displays:
```plaintext
diff --git a/doc/topics/git/lfs/index.md b/doc/topics/git/lfs/index.md
index 98710315652..731705fd72e 100644
--- a/doc/topics/git/lfs/index.md
+++ b/doc/topics/git/lfs/index.md
@@ -148,6 +148,12 @@ LFS object.
Technical details about how this works can be found in the [development documentation for LFS](../../../development/lfs.md#including-lfs-blobs-in-project-archives).
+## Related topics
+
+- Blog post: [Getting started with Git LFS](https://about.gitlab.com/blog/2017/01/30/getting-started-with-git-lfs-tutorial/)
+- [Git LFS developer information](../../../development/lfs.md)
+- [GitLab Git Large File Storage (LFS) Administration](../../../administration/lfs/index.md) for self-managed instances
+
## Troubleshooting
### Encountered `n` files that should have been pointers, but weren't
diff --git a/doc/topics/git/subtree.md b/doc/topics/git/subtree.md
index 1d624b45348..a8a665d4e13 100644
--- a/doc/topics/git/subtree.md
+++ b/doc/topics/git/subtree.md
@@ -16,9 +16,7 @@ comments: false
- Add: `git subtree add --prefix <target-folder> <url> <branch> --squash`
- Pull: `git subtree pull --prefix <target-folder> <url> <branch> --squash`
-- Push: `git subtree add --prefix <target-folder> <url> <branch>`
-- Ex: `git config alias.sbp 'subtree pull --prefix st /
- git@gitlab.com:balameb/subtree-nested-example.git master --squash'`
+- Push: `git subtree push --prefix <target-folder> <url> <branch>`
```shell
# Add an alias
diff --git a/doc/topics/offline/quick_start_guide.md b/doc/topics/offline/quick_start_guide.md
index bb2f651786c..015fd9fc720 100644
--- a/doc/topics/offline/quick_start_guide.md
+++ b/doc/topics/offline/quick_start_guide.md
@@ -205,3 +205,13 @@ users are on the most up-to-date instances of GitLab. These two services can be
environments so that they do not attempt and fail to reach out to GitLab services.
Learn more about [disabling usage statistics](../../user/admin_area/settings/usage_statistics.md#enable-or-disable-usage-statistics).
+
+### Configure NTP
+
+In GitLab 15.4 and 15.5, Gitaly Cluster doesn't function if `pool.ntp.org` is unreachable.
+[Customize the time server setting](../../administration/gitaly/praefect.md#customize-time-server-setting) on the Gitaly
+and Praefect servers so they can use an accessible NTP server.
+
+On offline instances, the [GitLab Geo check Rake task](../../administration/geo/replication/troubleshooting.md#can-geo-detect-the-current-site-correctly)
+always fails because it uses `pool.ntp.org`. This error can be ignored but you can
+[read more about how to work around it](../../administration/geo/replication/troubleshooting.md#message-machine-clock-is-synchronized--exception).
diff --git a/doc/topics/plan_and_track.md b/doc/topics/plan_and_track.md
index cf5dfe488db..862c41aa4d9 100644
--- a/doc/topics/plan_and_track.md
+++ b/doc/topics/plan_and_track.md
@@ -11,12 +11,16 @@ with milestones and track your team's time. Learn how to save time with
quick actions, see how GitLab renders Markdown text, and learn how to
use Git to interact with GitLab.
+<!-- vale gitlab.Spelling = NO -->
+
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For a thorough demo of Plan features, see
[Multi-team planning with GitLab Ultimate](https://www.youtube.com/watch?v=KmASFwSap7c).
In this video, Gabe describes a use case of a multi-team organization that uses GitLab
with [Scaled Agile Framework (SAFe)](https://about.gitlab.com/solutions/agile-delivery/scaled-agile/).
+<!-- vale gitlab.Spelling = YES -->
+
## Basic workflow features
Planning features everyone needs to use day-to-day.
diff --git a/doc/tutorials/index.md b/doc/tutorials/index.md
index 9b264a8c636..c1b538bafbe 100644
--- a/doc/tutorials/index.md
+++ b/doc/tutorials/index.md
@@ -19,10 +19,8 @@ and running quickly.
| [GitLab 101](https://levelup.gitlab.com/learn/course/gitlab101) | Learn the basics of GitLab in this certification course. | **{star}** |
| <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Use GitLab for DevOps](https://www.youtube.com/watch?v=7q9Y1Cv-ib0) (12m 34s) | Use GitLab through the entire DevOps lifecycle, from planning to monitoring. | **{star}** |
| [Use Markdown at GitLab](../user/markdown.md) | GitLab Flavored Markdown (GLFM) is used in many areas of GitLab, for example, in merge requests. | **{star}** |
-| [GitLab 201](https://levelup.gitlab.com/learn/course/gitlab-201-certification) | Go beyond the basics to learn more about using GitLab for your work. | |
| <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Learn GitLab project walkthrough](https://www.youtube.com/watch?v=-oaI2WEKdI4&list=PL05JrBw4t0KofkHq4GZJ05FnNGa11PQ4d) (59m 2s) | Step through the tutorial-style issues in the **Learn GitLab** project. If you don't have this project, download [the export file](https://gitlab.com/gitlab-org/gitlab/-/blob/master/vendor/project_templates/learn_gitlab_ultimate.tar.gz) and [import it to a new project](../user/project/settings/import_export.md#import-a-project-and-its-data). | |
| [Productivity tips](https://about.gitlab.com/blog/2021/02/18/improve-your-gitlab-productivity-with-these-10-tips/) | Get tips to help make you a productive GitLab user. | |
-| <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Structure a multi-team organization](https://www.youtube.com/watch?v=KmASFwSap7c) (37m 37s) | Learn to use issues, milestones, epics, labels, and more to plan and manage your work. | |
## Use Git
@@ -33,6 +31,7 @@ the most out of GitLab.
|-------|-------------|--------------------|
| [Make your first Git commit](make_your_first_git_commit.md) | Create a project, edit a file, and commit changes to a Git repository from the command line. | **{star}** |
| [Start using Git on the command line](../gitlab-basics/start-using-git.md) | Learn how to set up Git, clone repositories, and work with branches. | **{star}** |
+| [Take advantage of Git rebase](https://about.gitlab.com/blog/2022/10/06/take-advantage-of-git-rebase/)| Learn how to use the `rebase` command in your workflow. | |
| [Git cheat sheet](https://about.gitlab.com/images/press/git-cheat-sheet.pdf) | Download a PDF of common Git commands. | |
## Plan your work in projects
@@ -42,10 +41,9 @@ collaborating, and more.
| Topic | Description | Good for beginners |
|-------|-------------|--------------------|
-| [Create a project from a template](https://gitlab.com/projects/new#create_from_template) | For hands-on learning, select **Sample GitLab Project** and create a project with example issues and merge requests. | **{star}** |
+| [Create a project from a template](https://gitlab.com/projects/new#create_from_template) | Choose a project template and create a project with files to get you started. | |
| [Migrate to GitLab](../user/project/import/index.md) | If you are coming to GitLab from another platform, you can import or convert your projects. | |
| [Run an agile iteration](agile_sprint.md) | Use group, projects, and iterations to run an agile development iteration. |
-| <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Use GitLab for multi-team planning (SAFe)](https://www.youtube.com/watch?v=KmASFwSap7c) (37m 37s) | A use case of a multi-team organization that uses GitLab with [Scaled Agile Framework (SAFe)](https://about.gitlab.com/solutions/agile-delivery/scaled-agile/). |
## Use CI/CD pipelines
@@ -87,6 +85,7 @@ GitLab can check your application for security vulnerabilities.
| Topic | Description | Good for beginners |
|-------|-------------|--------------------|
| [Set up dependency scanning](https://about.gitlab.com/blog/2021/01/14/try-dependency-scanning/) | Try out dependency scanning, which checks for known vulnerabilities in dependencies. | **{star}** |
+| [Get started with GitLab application security](../user/application_security/get-started-security.md) | Follow recommended steps to set up security tools. | |
## Work with a self-managed instance
@@ -95,13 +94,12 @@ can help you manage and configure your instance.
| Topic | Description | Good for beginners |
|-------|-------------|--------------------|
-| [Install GitLab](../install/index.md) | Install GitLab according to your requirements.| |
+| [Install GitLab](../install/install_methods.md) | Install GitLab according to your requirements.| |
| [Get started administering GitLab](../administration/get_started.md) | Configure your organization and its authentication, then secure, monitor, and back up GitLab. | |
-| [Secure your instance](https://about.gitlab.com/blog/2020/05/20/gitlab-instance-security-best-practices/) | Implement security features for your instance. | |
## Integrate with GitLab
-GitLab [integrates](../integration/index.md) with a number of third-party services,
+GitLab [integrates](../user/project/integrations/index.md) with a number of third-party services,
enabling you to work with those services directly from GitLab.
| Topic | Description | Good for beginners |
diff --git a/doc/tutorials/move_personal_project_to_a_group.md b/doc/tutorials/move_personal_project_to_a_group.md
index ad86851393a..1431dc48d99 100644
--- a/doc/tutorials/move_personal_project_to_a_group.md
+++ b/doc/tutorials/move_personal_project_to_a_group.md
@@ -4,35 +4,29 @@ group: Tutorials
info: For assistance with this tutorial, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-other-projects-and-subjects.
---
-# Move your personal project to a group **(FREE SAAS)**
+# Tutorial: Move your personal project to a group **(FREE SAAS)**
-This tutorial will show you how to move a personal project to a group.
+If you created a project under a [personal namespace](../user/namespace/index.md),
+you can perform common tasks, like managing issue and merge requests,
+and using source control and CI/CD.
-## Why is a group important?
+However, at some point you might outgrow your personal project and
+want to move your project to a group namespace instead. With a group namespace, you can:
-In GitLab, you use [groups](../user/group/index.md)
-to manage one or more related projects at the same time.
-A group gives you some great benefits. For example, you can:
-
-- Manage permissions for your projects.
-- View all of the issues and merge requests for the projects in the group.
-- View all unique users in your namespace, across all projects.
+- Give a group of users access to your project, rather than adding users one-by-one.
+- View all issues and merge requests for all projects in the group.
+- View all unique users in the group namespace, across all projects.
- Manage usage quotas.
-- Start a trial or upgrade to a paid tier. This option is important if you're
+- Start a trial or upgrade to a paid subscription tier. This option is important if you're
impacted by the [changes to user limits](https://about.gitlab.com/blog/2022/03/24/efficient-free-tier/),
and need more users.
-However, if you're working in a [personal project](../user/project/working_with_projects.md#view-personal-projects),
-you can't use these features. Personal projects are created under your
-[personal namespace](../user/namespace/index.md). They're not part of a group,
-so you can't get any of the benefits and features of a group.
-
-But don't worry! You can move your existing personal project to a group.
-The next steps show you how.
+This tutorial shows you how to move your project from a personal namespace
+to a group namespace.
## Steps
-Here's an overview of what we're going to do:
+Here's an overview of the steps:
1. [Create a group](#create-a-group).
1. [Move your project to a group](#move-your-project-to-a-group).
@@ -59,8 +53,8 @@ If you don't have a group, create one:
Before you move your project to a group:
- You must have the Owner role for the project.
-- Remove any [container images](../user/packages/container_registry/index.md#limitations)
- and [NPM packages](../user/packages/npm_registry/index.md#limitations).
+- Remove any [container images](../user/packages/container_registry/index.md#known-issues)
+- Remove any npm packages. If you transfer a project to a different root namespace, the project must not contain any npm packages. When you update the path of a user or group, or transfer a subgroup or project, you must remove any npm packages first. You cannot update the root namespace of a project with npm packages. Make sure you update your .npmrc files to follow the naming convention and run npm publish if necessary.
Now you're ready to move your project:
diff --git a/doc/update/background_migrations.md b/doc/update/background_migrations.md
new file mode 100644
index 00000000000..2e0bd1bb348
--- /dev/null
+++ b/doc/update/background_migrations.md
@@ -0,0 +1,461 @@
+---
+stage: Data Stores
+group: Database
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Check for background migrations before upgrading
+
+Certain releases may require different migrations to be
+finished before you update to the newer version.
+
+There are two kinds of migrations:
+
+- [Background migrations](#background-migrations)
+- [Batched background migrations](#batched-background-migrations) (available in GitLab 14.0 and later)
+
+Background migrations and batched migrations are not the same, so you should check that both are
+complete before updating.
+
+Decrease the time required to complete these migrations by increasing the number of
+[Sidekiq workers](../administration/sidekiq/extra_sidekiq_processes.md)
+that can process jobs in the `background_migration` queue.
+
+## Background migrations
+
+### Pending migrations
+
+**For Omnibus installations:**
+
+```shell
+sudo gitlab-rails runner -e production 'puts Gitlab::BackgroundMigration.remaining'
+sudo gitlab-rails runner -e production 'puts Gitlab::Database::BackgroundMigration::BatchedMigration.queued.count'
+```
+
+**For installations from source:**
+
+```shell
+cd /home/git/gitlab
+sudo -u git -H bundle exec rails runner -e production 'puts Gitlab::BackgroundMigration.remaining'
+sudo -u git -H bundle exec rails runner -e production 'puts Gitlab::Database::BackgroundMigration::BatchedMigration.queued.count'
+```
+
+### Failed migrations
+
+**For Omnibus installations:**
+
+For GitLab 14.0-14.9:
+
+```shell
+sudo gitlab-rails runner -e production 'puts Gitlab::Database::BackgroundMigration::BatchedMigration.failed.count'
+```
+
+For GitLab 14.10 and later:
+
+```shell
+sudo gitlab-rails runner -e production 'puts Gitlab::Database::BackgroundMigration::BatchedMigration.with_status(:failed).count'
+```
+
+**For installations from source:**
+
+For GitLab 14.0-14.9:
+
+```shell
+cd /home/git/gitlab
+sudo -u git -H bundle exec rails runner -e production 'puts Gitlab::Database::BackgroundMigration::BatchedMigration.failed.count'
+```
+
+For GitLab 14.10 and later:
+
+```shell
+cd /home/git/gitlab
+sudo -u git -H bundle exec rails runner -e production 'puts Gitlab::Database::BackgroundMigration::BatchedMigration.with_status(:failed).count'
+```
+
+## Batched background migrations **(FREE SELF)**
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51332) in GitLab 13.11, [behind a feature flag](../user/feature_flags.md), disabled by default.
+> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/329511) in GitLab 13.12.
+> - Enabled on GitLab.com.
+> - Recommended for production use.
+> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-batched-background-migrations).
+
+There can be [risks when disabling released features](../administration/feature_flags.md#risks-when-disabling-released-features).
+Refer to this feature's version history for more details.
+
+To update database tables in batches, GitLab can use batched background migrations. These migrations
+are created by GitLab developers and run automatically on upgrade. However, such migrations are
+limited in scope to help with migrating some `integer` database columns to `bigint`. This is needed to
+prevent integer overflow for some tables.
+
+Some installations [may need to run GitLab 14.0 for at least a day](index.md#1400) to complete the database changes introduced by that upgrade.
+
+Batched background migrations are handled by Sidekiq and [run in isolation](../development/database/batched_background_migrations.md#isolation), so an instance can remain operational while the migrations are processed. However, there may be performance degradation on larger instances that are heavily used while batched background migrations are run, so it's a good idea to [actively monitor the Sidekiq status](../user/admin_area/index.md#background-jobs) until all migrations are completed.
+
+### Check the status of batched background migrations
+
+To check the status of batched background migrations:
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Monitoring > Background Migrations**.
+
+ ![queued batched background migrations table](img/batched_background_migrations_queued_v14_0.png)
+
+All migrations must have a `Finished` status before you upgrade GitLab.
+
+The status of batched background migrations can also be queried directly in the database.
+
+1. Log into a `psql` prompt according to the directions for your instance's installation method
+(for example, `sudo gitlab-psql` for Omnibus installations).
+1. Run the following query in the `psql` session to see details on incomplete batched background migrations:
+
+ ```sql
+ select job_class_name, table_name, column_name, job_arguments from batched_background_migrations where status <> 3;
+ ```
+
+If the migrations are not finished and you try to update to a later version,
+GitLab prompts you with an error:
+
+```plaintext
+Expected batched background migration for the given configuration to be marked as 'finished', but it is 'active':
+```
+
+If you get this error, [check the batched background migration options](#database-migrations-failing-because-of-batched-background-migration-not-finished) to complete the upgrade.
+
+### Enable or disable batched background migrations
+
+WARNING:
+If you disable this feature flag, GitLab upgrades may fail.
+
+Batched background migrations are under development but ready for production use.
+It is deployed behind a feature flag that is **enabled by default**.
+[GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md)
+can opt to disable it.
+
+To enable it:
+
+```ruby
+Feature.enable(:execute_batched_migrations_on_schedule)
+```
+
+To disable it:
+
+```ruby
+Feature.disable(:execute_batched_migrations_on_schedule)
+```
+
+### Pause batched background migrations in GitLab 14.x
+
+To pause an ongoing batched background migration, use the `disable` command above.
+This command causes the migration to complete the current batch, and then wait to start the next batch.
+
+Use the following database queries to see the state of the current batched background migration:
+
+1. Obtain the ID of the running migration:
+
+ ```sql
+ SELECT
+ id,
+ job_class_name,
+ table_name,
+ column_name,
+ job_arguments
+ FROM batched_background_migrations
+ WHERE status <> 3;
+ ```
+
+1. Run this query, replacing `XX` with the ID you obtained in the previous step,
+ to see the status of the migration:
+
+ ```sql
+ SELECT
+ started_at,
+ finished_at,
+ finished_at - started_at AS duration,
+ min_value,
+ max_value,
+ batch_size,
+ sub_batch_size
+ FROM batched_background_migration_jobs
+ WHERE batched_background_migration_id = XX
+ ORDER BY id DESC
+ limit 10;
+ ```
+
+1. Run the query multiple times within a few minutes to ensure no new row has been added.
+ If no new row has been added, the migration has been paused.
+
+1. After confirming the migration has paused, restart the migration (using the `enable`
+ command above) to proceed with the batch when ready. On larger instances,
+ background migrations can take as long as 48 hours to complete each batch.
+
+### Automatic batch size optimization
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60133)
+> in GitLab 13.12, [behind a feature flag](../user/feature_flags.md),
+> [enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/329511).
+> - Enabled on GitLab.com.
+> - Recommended for production use.
+> - For GitLab self-managed instances, GitLab administrators can opt to
+> [disable it](#enable-or-disable-automatic-batch-size-optimization).
+
+There can be [risks when disabling released features](../administration/feature_flags.md#risks-when-disabling-released-features).
+Refer to this feature's version history for more details.
+
+To maximize throughput of batched background migrations (in terms of the number of tuples updated per time unit), batch sizes are automatically adjusted based on how long the previous batches took to complete.
+
+### Enable or disable automatic batch size optimization
+
+Automatic batch size optimization for batched background migrations is under development but ready for production use.
+It is deployed behind a feature flag that is **enabled by default**.
+[GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md)
+can opt to disable it.
+
+To enable it:
+
+```ruby
+Feature.enable(:optimize_batched_migrations)
+```
+
+To disable it:
+
+```ruby
+Feature.disable(:optimize_batched_migrations)
+```
+
+## Troubleshooting
+
+### Database migrations failing because of batched background migration not finished
+
+When updating to GitLab 14.2 or later there might be a database migration failing with a message like:
+
+```plaintext
+StandardError: An error has occurred, all later migrations canceled:
+
+Expected batched background migration for the given configuration to be marked as 'finished', but it is 'active':
+ {:job_class_name=>"CopyColumnUsingBackgroundMigrationJob", :table_name=>"push_event_payloads", :column_name=>"event_id", :job_arguments=>[["event_id"], ["event_id_convert_to_bigint"]]}
+```
+
+First, check if you have followed the [version-specific upgrade instructions for 14.2](../update/index.md#1420).
+If you have, you can [manually finish the batched background migration](#manually-finishing-a-batched-background-migration).
+If you haven't, choose one of the following methods:
+
+1. [Rollback and upgrade](#roll-back-and-follow-the-required-upgrade-path) through one of the required
+versions before updating to 14.2+.
+1. [Roll forward](#roll-forward-and-finish-the-migrations-on-the-upgraded-version), staying on the current
+version and manually ensuring that the batched migrations complete successfully.
+
+#### Roll back and follow the required upgrade path
+
+1. [Rollback and restore the previously installed version](../raketasks/backup_restore.md)
+1. Update to either 14.0.5 or 14.1 **before** updating to 14.2+
+1. [Check the status](#check-the-status-of-batched-background-migrations) of the batched background migrations and
+make sure they are all marked as finished before attempting to upgrade again. If any remain marked as active,
+you can [manually finish them](#manually-finishing-a-batched-background-migration).
+
+#### Roll forward and finish the migrations on the upgraded version
+
+##### For a deployment with downtime
+
+To run all the batched background migrations, it can take a significant amount of time
+depending on the size of your GitLab installation.
+
+1. [Check the status](#check-the-status-of-batched-background-migrations) of the batched background migrations in the
+database, and [manually run them](#manually-finishing-a-batched-background-migration) with the appropriate
+arguments until the status query returns no rows.
+1. When the status of all of all them is marked as complete, re-run migrations for your installation.
+1. [Complete the database migrations](../administration/raketasks/maintenance.md#run-incomplete-database-migrations) from your GitLab upgrade:
+
+ ```plaintext
+ sudo gitlab-rake db:migrate
+ ```
+
+1. Run a reconfigure:
+
+ ```plaintext
+ sudo gitlab-ctl reconfigure
+ ```
+
+1. Finish the upgrade for your installation.
+
+##### For a no-downtime deployment
+
+As the failing migrations are post-deployment migrations, you can remain on a running instance of the upgraded
+version and wait for the batched background migrations to finish normally.
+
+1. [Check the status](#check-the-status-of-batched-background-migrations) of the batched background migration from
+the error message, and make sure it is listed as finished. If it is still active, either wait until it is done,
+or [manually finish it](#manually-finishing-a-batched-background-migration).
+1. Re-run migrations for your installation, so the remaining post-deployment migrations finish.
+
+### Manually finishing a batched background migration
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62634) in GitLab 14.1
+
+If you need to manually finish a batched background migration due to an
+error, you can run:
+
+```shell
+sudo gitlab-rake gitlab:background_migrations:finalize[<job_class_name>,<table_name>,<column_name>,'<job_arguments>']
+```
+
+Replace the values in angle brackets with the correct
+arguments. For example, if you receive an error similar to this:
+
+```plaintext
+StandardError: An error has occurred, all later migrations canceled:
+
+Expected batched background migration for the given configuration to be marked as 'finished', but it is 'active':
+ {:job_class_name=>"CopyColumnUsingBackgroundMigrationJob", :table_name=>"push_event_payloads", :column_name=>"event_id", :job_arguments=>[["event_id"], ["event_id_convert_to_bigint"]]}
+```
+
+Plug the arguments from the error message into the command:
+
+```shell
+sudo gitlab-rake gitlab:background_migrations:finalize[CopyColumnUsingBackgroundMigrationJob,push_event_payloads,event_id,'[["event_id"]\, ["event_id_convert_to_bigint"]]']
+```
+
+If you need to manually run a batched background migration to continue an upgrade, you can
+[check the status](#check-the-status-of-batched-background-migrations) in the database and get the
+arguments from the query results. For example, if the query returns this:
+
+```plaintext
+ job_class_name | table_name | column_name | job_arguments
+---------------------------------------+------------+-------------+------------------------------------
+ CopyColumnUsingBackgroundMigrationJob | events | id | [["id"], ["id_convert_to_bigint"]]
+ ```
+
+The results from the query can be plugged into the command:
+
+```shell
+sudo gitlab-rake gitlab:background_migrations:finalize[CopyColumnUsingBackgroundMigrationJob,events,id,'[["id"]\, ["id_convert_to_bigint"]]']
+```
+
+### The `BackfillNamespaceIdForNamespaceRoute` batched migration job fails
+
+In GitLab 14.8, the `BackfillNamespaceIdForNamespaceRoute` batched background migration job
+may fail to complete. When retried, a `500 Server Error` is returned. This issue was
+[resolved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82387) in GitLab 14.9.
+
+To resolve this issue, [upgrade GitLab](../update/index.md) from 14.8 to 14.9.
+You can ignore the failed batch migration until after you update to GitLab 14.9.
+
+### Background migrations remain in the Sidekiq queue
+
+WARNING:
+The following operations can disrupt your GitLab performance. They run a number of Sidekiq jobs that perform various database or file updates.
+
+Run the following check. If it returns non-zero and the count does not decrease over time, follow the rest of the steps in this section.
+
+```shell
+# For Omnibus installations:
+sudo gitlab-rails runner -e production 'puts Gitlab::BackgroundMigration.remaining'
+
+# For installations from source:
+cd /home/git/gitlab
+sudo -u git -H bundle exec rails runner -e production 'puts Gitlab::BackgroundMigration.remaining'
+```
+
+It is safe to re-execute the following commands, especially if you have 1000+ pending jobs which would likely overflow your runtime memory.
+
+**For Omnibus installations**
+
+```shell
+# Start the rails console
+sudo gitlab-rails c
+
+# Execute the following in the rails console
+scheduled_queue = Sidekiq::ScheduledSet.new
+pending_job_classes = scheduled_queue.select { |job| job["class"] == "BackgroundMigrationWorker" }.map { |job| job["args"].first }.uniq
+pending_job_classes.each { |job_class| Gitlab::BackgroundMigration.steal(job_class) }
+```
+
+**For installations from source**
+
+```shell
+# Start the rails console
+sudo -u git -H bundle exec rails RAILS_ENV=production
+
+# Execute the following in the rails console
+scheduled_queue = Sidekiq::ScheduledSet.new
+pending_job_classes = scheduled_queue.select { |job| job["class"] == "BackgroundMigrationWorker" }.map { |job| job["args"].first }.uniq
+pending_job_classes.each { |job_class| Gitlab::BackgroundMigration.steal(job_class) }
+```
+
+### Background migrations stuck in 'pending' state
+
+WARNING:
+The following operations can disrupt your GitLab performance. They run a number
+of Sidekiq jobs that perform various database or file updates.
+
+- GitLab 13.6 introduced an issue where a background migration named
+ `BackfillJiraTrackerDeploymentType2` can be permanently stuck in a
+ **pending** state across upgrades. To clean up this stuck migration, see the
+ [13.6.0 version-specific instructions](index.md#1360).
+- GitLab 14.2 introduced an issue where a background migration named
+ `BackfillDraftStatusOnMergeRequests` can be permanently stuck in a
+ **pending** state across upgrades when the instance lacks records that match
+ the migration's target. To clean up this stuck migration, see the
+ [14.2.0 version-specific instructions](index.md#1420).
+- GitLab 14.4 introduced an issue where a background migration named
+ `PopulateTopicsTotalProjectsCountCache` can be permanently stuck in a
+ **pending** state across upgrades when the instance lacks records that match
+ the migration's target. To clean up this stuck migration, see the
+ [14.4.0 version-specific instructions](index.md#1440).
+- GitLab 14.5 introduced an issue where a background migration named
+ `UpdateVulnerabilityOccurrencesLocation` can be permanently stuck in a
+ **pending** state across upgrades when the instance lacks records that match
+ the migration's target. To clean up this stuck migration, see the
+ [14.5.0 version-specific instructions](index.md#1450).
+- GitLab 14.8 introduced an issue where a background migration named
+ `PopulateTopicsNonPrivateProjectsCount` can be permanently stuck in a
+ **pending** state across upgrades. To clean up this stuck migration, see the
+ [14.8.0 version-specific instructions](index.md#1480).
+- GitLab 14.9 introduced an issue where a background migration named
+ `ResetDuplicateCiRunnersTokenValuesOnProjects` can be permanently stuck in a
+ **pending** state across upgrades when the instance lacks records that match
+ the migration's target. To clean up this stuck migration, see the
+ [14.9.0 version-specific instructions](index.md#1490).
+
+For other background migrations stuck in pending, run the following check. If
+it returns non-zero and the count does not decrease over time, follow the rest
+of the steps in this section.
+
+```shell
+# For Omnibus installations:
+sudo gitlab-rails runner -e production 'puts Gitlab::Database::BackgroundMigrationJob.pending.count'
+
+# For installations from source:
+cd /home/git/gitlab
+sudo -u git -H bundle exec rails runner -e production 'puts Gitlab::Database::BackgroundMigrationJob.pending.count'
+```
+
+It is safe to re-attempt these migrations to clear them out from a pending status:
+
+**For Omnibus installations**
+
+```shell
+# Start the rails console
+sudo gitlab-rails c
+
+# Execute the following in the rails console
+Gitlab::Database::BackgroundMigrationJob.pending.find_each do |job|
+ puts "Running pending job '#{job.class_name}' with arguments #{job.arguments}"
+ result = Gitlab::BackgroundMigration.perform(job.class_name, job.arguments)
+ puts "Result: #{result}"
+end
+```
+
+**For installations from source**
+
+```shell
+# Start the rails console
+sudo -u git -H bundle exec rails RAILS_ENV=production
+
+# Execute the following in the rails console
+Gitlab::Database::BackgroundMigrationJob.pending.find_each do |job|
+ puts "Running pending job '#{job.class_name}' with arguments #{job.arguments}"
+ result = Gitlab::BackgroundMigration.perform(job.class_name, job.arguments)
+ puts "Result: #{result}"
+end
+```
diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md
index 353fbf191b5..ed49a4ade09 100644
--- a/doc/update/deprecations.md
+++ b/doc/update/deprecations.md
@@ -34,6 +34,9 @@ In each release, GitLab announces features that are deprecated and no longer rec
Each deprecated feature will be removed in a future release.
Some features cause breaking changes when they are removed.
+**{rss}** **To be notified of upcoming breaking changes**,
+add this URL to your RSS feed reader: `https://about.gitlab.com/breaking-changes.xml`
+
DISCLAIMER:
This page contains information related to upcoming products, features, and functionality.
It is important to note that the information presented is for informational purposes only.
@@ -45,11 +48,77 @@ sole discretion of GitLab Inc.
<div class="announcement-milestone">
-## Announced in 15.6
+## Announced in 15.7
<div class="deprecation removal-160 breaking-change">
-### Configuration fields in GitLab Runner Helm Chart
+### DAST API scans using DAST template is deprecated
+
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+With the move to the new DAST API analyzer and the `DAST-API.gitlab-ci.yml` template for DAST API scans, we will be removing the ability to scan APIs with the DAST analyzer. Use of the `DAST.gitlab-ci.yml` or `DAST-latest.gitlab-ci.yml` templates for API scans is deprecated as of GitLab 15.7 and will no longer work in GitLab 16.0. Please use `DAST-API.gitlab-ci.yml` template and refer to the [DAST API analyzer](https://docs.gitlab.com/ee/user/application_security/dast_api/#configure-dast-api-with-an-openapi-specification) documentation for configuration details.
+
+</div>
+
+<div class="deprecation removal-160 breaking-change">
+
+### DAST API variables
+
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+With the switch to the new DAST API analyzer in GitLab 15.6, two legacy DAST API variables are being deprecated. The variables `DAST_API_HOST_OVERRIDE` and `DAST_API_SPECIFICATION` will no longer be used for DAST API scans.
+
+`DAST_API_HOST_OVERRIDE` has been deprecated in favor of using the `DAST_API_TARGET_URL` to automatically override the host in the OpenAPI specification.
+
+`DAST_API_SPECIFICATION` has been deprecated in favor of `DAST_API_OPENAPI`. To continue using an OpenAPI specification to guide the test, users must replace the `DAST_API_SPECIFICATION` variable with the `DAST_API_OPENAPI` variable. The value can remain the same, but the variable name must be replaced.
+
+These two variables will be removed in GitLab 16.0.
+
+</div>
+
+<div class="deprecation removal-160 breaking-change">
+
+### DAST ZAP advanced configuration variables deprecation
+
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+With the new browser-based DAST analyzer GA in GitLab 15.7, we are working towards making it the default DAST analyzer at some point in the future. In preparation for this, the following legacy DAST variables are being deprecated and scheduled for removal in GitLab 16.0: `DAST_ZAP_CLI_OPTIONS` and `DAST_ZAP_LOG_CONFIGURATION`. These variables allowed for advanced configuration of the legacy DAST analyzer, which was based on OWASP ZAP. The new browser-based analyzer will not include the same functionality, as these were specific to how ZAP worked.
+
+These three variables will be removed in GitLab 16.0.
+
+</div>
+
+<div class="deprecation removal-160 breaking-change">
+
+### DAST report variables deprecation
+
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+With the new browser-based DAST analyzer GA in GitLab 15.7, we are working towards making it the default DAST analyzer at some point in the future. In preparation for this, the following legacy DAST variables are being deprecated and scheduled for removal in GitLab 16.0: `DAST_HTML_REPORT`, `DAST_XML_REPORT`, and `DAST_MARKDOWN_REPORT`. These reports relied on the legacy DAST analyzer and we do not plan to implement them in the new browser-based analyzer. As of GitLab 16.0, these report artifacts will no longer be generated.
+
+These three variables will be removed in GitLab 16.0.
+
+</div>
+
+<div class="deprecation removal-160 breaking-change">
+
+### KAS Metrics Port in GitLab Helm Chart
End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br />
Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
@@ -58,13 +127,14 @@ WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
Review the details carefully before upgrading.
-From GitLab 13.6, users can [specify any runner configuration in the GitLab Runner Helm chart](https://docs.gitlab.com/runner/install/kubernetes.html). When we implemented this feature, we deprecated values in the GitLab Helm Chart configuration that were specific to GitLab Runner. These fields are deprecated and we plan to remove them in v1.0 of the GitLab Runner Helm chart.
+The `gitlab.kas.metrics.port` has been deprecated in favor of the new `gitlab.kas.observability.port` configuration field for the [GitLab Helm Chart](https://gitlab.com/gitlab-org/charts/gitlab/-/merge_requests/2839).
+This port is used for much more than just metrics, which warranted this change to avoid confusion in configuration.
</div>
<div class="deprecation removal-160 breaking-change">
-### GitLab Runner registration token in Runner Operator
+### Shimo integration
End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br />
Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
@@ -73,13 +143,28 @@ WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
Review the details carefully before upgrading.
-The [`runner-registration-token`](https://docs.gitlab.com/runner/install/operator.html#install-the-kubernetes-operator) parameter that uses the OpenShift and k8s Vanilla Operator to install a runner on Kubernetes is deprecated. GitLab plans to introduce a new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/) in GitLab 15.8, which introduces a new method for registering runners and eliminates the legacy runner registration token.
+The [Shimo Workspace integration](https://docs.gitlab.com/ee/user/project/integrations/shimo.html) has been deprecated
+and will be moved to the JiHu GitLab codebase.
</div>
<div class="deprecation removal-160 breaking-change">
-### `POST /api/v4/runners` method to register runners
+### Single merge request changes API endpoint
+
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+The endpoint to get [changes from a single merge request](https://docs.gitlab.com/ee/api/merge_requests.html#get-single-merge-request-changes) has been deprecated in favor the [list merge request diffs](https://docs.gitlab.com/ee/api/merge_requests.html#list-merge-request-diffs) endpoint. API users are encouraged to switch to the new diffs endpoint instead. The `changes from a single merge request` endpoint will be removed in v5 of the GitLab REST API.
+
+</div>
+
+<div class="deprecation removal-160 breaking-change">
+
+### Support for REST API endpoints that reset runner registration tokens
End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br />
Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
@@ -88,10 +173,13 @@ WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
Review the details carefully before upgrading.
-The `POST` method operation on the `/api/v4/runners` endpoint is deprecated.
-This endpoint and method [registers](https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner) a runner
-with a GitLab instance at the instance, group, or project level through the API. We plan to remove this endpoint
-and method in GitLab 16.0.
+The support for runner registration tokens is deprecated. As a consequence, the REST API endpoints to reset a registration token are also deprecated and will
+be removed in GitLab 16.0.
+The deprecated endpoints are:
+
+- `POST /runners/reset_registration_token`
+- `POST /projects/:id/runners/reset_registration_token`
+- `POST /groups/:id/runners/reset_registration_token`
In GitLab 15.8, we plan to implement a new method to bind runners to a GitLab instance,
as part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/).
@@ -103,7 +191,46 @@ From GitLab 16.0 and later, the runner registration methods implemented by the n
<div class="deprecation removal-160 breaking-change">
-### `gitlab-runner register` command
+### Support for periods (`.`) in Terraform state names might break existing states
+
+End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br />
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+Previously, Terraform state names containing periods were not supported. However, you could still use state names with periods via a workaround.
+
+GitLab 15.7 [adds full support](https://docs.gitlab.com/ee/user/infrastructure/iac/troubleshooting.html#state-not-found-if-the-state-name-contains-a-period) for state names that contain periods. If you used a workaround to handle these state names, your jobs might fail, or it might look like you've run Terraform for the first time.
+
+To resolve the issue:
+
+ 1. Change any references to the state file by excluding the period and any characters that follow.
+ - For example, if your state name is `state.name`, change all references to `state`.
+ 1. Run your Terraform commands.
+
+To use the full state name, including the period, [migrate to the full state file](https://docs.gitlab.com/ee/user/infrastructure/iac/terraform_state.html#migrate-to-a-gitlab-managed-terraform-state).
+
+</div>
+
+<div class="deprecation removal-160 breaking-change">
+
+### The Phabricator task importer is deprecated
+
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+The [Phabricator task importer](https://docs.gitlab.com/ee/user/project/import/phabricator.html) is being deprecated. Phabricator itself as a project is no longer actively maintained since June 1, 2021. We haven't observed imports using this tool. There has been no activity on the open related issues on GitLab.
+
+</div>
+
+<div class="deprecation removal-160 breaking-change">
+
+### The `gitlab-runner exec` command is deprecated
End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br />
Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
@@ -112,21 +239,126 @@ WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
Review the details carefully before upgrading.
-The command to [register](https://docs.gitlab.com/runner/register/) a runner, `gitlab-runner register` is deprecated.
+The [`gitlab-runner exec`](https://docs.gitlab.com/runner/commands/#gitlab-runner-exec) command is deprecated and will be fully removed from GitLab Runner in 16.0. The `gitlab-runner exec` feature was initially developed to provide the ability to validate a GitLab CI pipeline on a local system without needing to commit the updates to a GitLab instance. However, with the continued evolution of GitLab CI, replicating all GitLab CI features into `gitlab-runner exec` was no longer viable. Pipeline syntax and validation [simulation](https://docs.gitlab.com/ee/ci/pipeline_editor/#simulate-a-cicd-pipeline) are available in the GitLab pipeline editor.
+
+</div>
+
+<div class="deprecation removal-160 breaking-change">
+
+### ZenTao integration
+
+End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br />
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+The [ZenTao product integration](https://docs.gitlab.com/ee/user/project/integrations/zentao.html) has been deprecated
+and will be moved to the JiHu GitLab codebase.
+
+</div>
+
+<div class="deprecation removal-160 breaking-change">
+
+### `POST ci/lint` API endpoint deprecated
+
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+The `POST ci/lint` API endpoint is deprecated in 15.7, and will be removed in 16.0. This endpoint does not validate the full range of CI/CD configuration options. Instead, use [`POST /projects/:id/ci/lint`](https://docs.gitlab.com/15.5/ee/api/lint.html#validate-a-ci-yaml-configuration-with-a-namespace), which properly validates CI/CD configuration.
+
+</div>
+</div>
+
+<div class="announcement-milestone">
+
+## Announced in 15.6
+
+<div class="deprecation removal-160 breaking-change">
+
+### Configuration fields in GitLab Runner Helm Chart
+
+End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br />
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+From GitLab 13.6, users can [specify any runner configuration in the GitLab Runner Helm chart](https://docs.gitlab.com/runner/install/kubernetes.html). When we implemented this feature, we deprecated values in the GitLab Helm Chart configuration that were specific to GitLab Runner. These fields are deprecated and we plan to remove them in v1.0 of the GitLab Runner Helm chart.
+
+</div>
+
+<div class="deprecation removal-170 breaking-change">
+
+### GitLab Runner registration token in Runner Operator
+
+End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br />
+Planned removal: GitLab <span class="removal-milestone">17.0</span> (2024-05-22)
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+The [`runner-registration-token`](https://docs.gitlab.com/runner/install/operator.html#install-the-kubernetes-operator) parameter that uses the OpenShift and k8s Vanilla Operator to install a runner on Kubernetes is deprecated. GitLab plans to introduce a new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/) in GitLab 15.8, which introduces a new method for registering runners and eliminates the legacy runner registration token.
+
+</div>
+
+<div class="deprecation removal-170 breaking-change">
+
+### Registration tokens and server-side runner arguments in `POST /api/v4/runners` endpoint
+
+End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br />
+Planned removal: GitLab <span class="removal-milestone">17.0</span> (2024-05-22)
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+The support for registration tokens and certain runner configuration arguments in the `POST` method operation on the `/api/v4/runners` endpoint is deprecated.
+This endpoint [registers](https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner) a runner
+with a GitLab instance at the instance, group, or project level through the API. We plan to remove the support for
+registration tokens and certain configuration arguments in this endpoint in GitLab 17.0.
+
+In GitLab 15.8, we plan to implement a new method to bind runners to a GitLab instance,
+as part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/).
+This new architecture introduces a new method for registering runners and will eliminate the legacy
+[runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens).
+From GitLab 17.0 and later, the runner registration methods implemented by the new GitLab Runner token architecture will be the only supported methods.
+
+</div>
+
+<div class="deprecation removal-170 breaking-change">
+
+### Registration tokens and server-side runner arguments in `gitlab-runner register` command
+
+End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br />
+Planned removal: GitLab <span class="removal-milestone">17.0</span> (2024-05-22)
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+The support for registration tokens and certain configuration arguments in the command to [register](https://docs.gitlab.com/runner/register/) a runner, `gitlab-runner register` is deprecated.
GitLab plans to introduce a new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/) in GitLab 15.8,
which introduces a new method for registering runners and eliminates the legacy
[runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens).
-The new method will involve passing a [runner authentication token](https://docs.gitlab.com/ee/security/token_overview.html#runner-authentication-tokens-also-called-runner-tokens)
-to a new `gitlab-runner deploy` command.
+The new method will involve creating the runner in the GitLab UI and passing the
+[runner authentication token](https://docs.gitlab.com/ee/security/token_overview.html#runner-authentication-tokens-also-called-runner-tokens)
+to the `gitlab-runner register` command.
</div>
-<div class="deprecation removal-160 breaking-change">
+<div class="deprecation removal-170 breaking-change">
### `runnerRegistrationToken` parameter for GitLab Runner Helm Chart
End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br />
-Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
+Planned removal: GitLab <span class="removal-milestone">17.0</span> (2024-05-22)
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
@@ -136,9 +368,9 @@ The [`runnerRegistrationToken`](https://docs.gitlab.com/runner/install/kubernete
As part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/), in GitLab 15.8 we plan to introduce:
-- A new method to bind runners to a GitLab instance.
+- A new method to bind runners to a GitLab instance leveraging `runnerToken`.
- A unique system ID saved to the `config.toml`, which will ensure traceability between jobs and runners.
-From GitLab 16.0 and later, the methods to register runners introduced by the new GitLab Runner token architecture will be the only supported methods.
+From GitLab 17.0 and later, the methods to register runners introduced by the new GitLab Runner token architecture will be the only supported methods.
</div>
@@ -165,7 +397,7 @@ The `merge_status` field in the [merge request API](https://docs.gitlab.com/ee/a
### File Type variable expansion in `.gitlab-ci.yml`
-Planned removal: GitLab <span class="removal-milestone">15.7</span> ()
+Planned removal: GitLab <span class="removal-milestone">15.7</span> (2022-12-22)
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
@@ -269,7 +501,7 @@ GitLab's operational container scanning capabilities no longer require starboard
### Toggle behavior of `/draft` quick action in merge requests
-Planned removal: GitLab <span class="removal-milestone">16.0</span> (2022-05-22)
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
@@ -904,7 +1136,7 @@ WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
Review the details carefully before upgrading.
-The feature flag `PUSH_RULES_SUPERSEDE_CODE_OWNERS` is being removed in GitLab 15.0. Upon its removal, push rules will supersede CODEOWNERS. The CODEOWNERS feature will no longer be available for access control.
+The feature flag `PUSH_RULES_SUPERSEDE_CODE_OWNERS` is being removed in GitLab 15.0. Upon its removal, push rules will supersede Code Owners. Even if Code Owner approval is required, a push rule that explicitly allows a specific user to push code supersedes the Code Owners setting.
</div>
@@ -976,7 +1208,7 @@ To align with this change, API calls to list external status checks will also re
### GraphQL API Runner will not accept `status` filter values of `active` or `paused`
-Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-04-22)
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
@@ -999,7 +1231,7 @@ status value can be used in place of `active` since GitLab 14.8.
### GraphQL ID and GlobalID compatibility
-Planned removal: GitLab <span class="removal-milestone">15.0</span> (2022-04-22)
+Planned removal: GitLab <span class="removal-milestone">15.0</span> (2022-05-22)
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
@@ -1156,7 +1388,7 @@ The `instanceStatisticsMeasurements` GraphQL node has been renamed to `usageTren
### REST and GraphQL API Runner usage of `active` replaced by `paused`
-Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-04-22)
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
@@ -1481,7 +1713,7 @@ The new security approvals feature is similar to vulnerability check. For exampl
### `CI_BUILD_*` predefined variables
-Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-04-22)
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
@@ -1945,7 +2177,7 @@ Administrators who need to add runners for multiple projects can register a runn
### GraphQL API Runner status will not return `paused`
-Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-04-22)
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
diff --git a/doc/update/index.md b/doc/update/index.md
index dbac4304897..d838f8dda34 100644
--- a/doc/update/index.md
+++ b/doc/update/index.md
@@ -73,210 +73,12 @@ from the chart version to GitLab version to determine the [upgrade path](#upgrad
See the guide to [plan your GitLab upgrade](plan_your_upgrade.md).
-## Checking for background migrations before upgrading
+## Check for background migrations before upgrading
Certain releases may require different migrations to be
finished before you update to the newer version.
-[Batched migrations](#batched-background-migrations) are a migration type available in GitLab 14.0 and later.
-Background migrations and batched migrations are not the same, so you should check that both are
-complete before updating.
-
-Decrease the time required to complete these migrations by increasing the number of
-[Sidekiq workers](../administration/sidekiq/extra_sidekiq_processes.md)
-that can process jobs in the `background_migration` queue.
-
-### Background migrations
-
-#### Pending migrations
-
-**For Omnibus installations:**
-
-```shell
-sudo gitlab-rails runner -e production 'puts Gitlab::BackgroundMigration.remaining'
-sudo gitlab-rails runner -e production 'puts Gitlab::Database::BackgroundMigration::BatchedMigration.queued.count'
-```
-
-**For installations from source:**
-
-```shell
-cd /home/git/gitlab
-sudo -u git -H bundle exec rails runner -e production 'puts Gitlab::BackgroundMigration.remaining'
-sudo -u git -H bundle exec rails runner -e production 'puts Gitlab::Database::BackgroundMigration::BatchedMigration.queued.count'
-```
-
-#### Failed migrations
-
-**For Omnibus installations:**
-
-For GitLab 14.0-14.9:
-
-```shell
-sudo gitlab-rails runner -e production 'puts Gitlab::Database::BackgroundMigration::BatchedMigration.failed.count'
-```
-
-For GitLab 14.10 and later:
-
-```shell
-sudo gitlab-rails runner -e production 'puts Gitlab::Database::BackgroundMigration::BatchedMigration.with_status(:failed).count'
-```
-
-**For installations from source:**
-
-For GitLab 14.0-14.9:
-
-```shell
-cd /home/git/gitlab
-sudo -u git -H bundle exec rails runner -e production 'puts Gitlab::Database::BackgroundMigration::BatchedMigration.failed.count'
-```
-
-For GitLab 14.10 and later:
-
-```shell
-cd /home/git/gitlab
-sudo -u git -H bundle exec rails runner -e production 'puts Gitlab::Database::BackgroundMigration::BatchedMigration.with_status(:failed).count'
-```
-
-### Batched background migrations
-
-GitLab 14.0 introduced [batched background migrations](../user/admin_area/monitoring/background_migrations.md).
-
-Some installations [may need to run GitLab 14.0 for at least a day](#1400) to complete the database changes introduced by that upgrade.
-
-Batched background migrations are handled by Sidekiq and [run in isolation](../development/database/batched_background_migrations.md#isolation), so an instance can remain operational while the migrations are processed. However, there may be performance degradation on larger instances that are heavily used while batched background migrations are run, so it's a good idea to [actively monitor the Sidekiq status](../user/admin_area/index.md#background-jobs) until all migrations are completed.
-
-#### Check the status of batched background migrations
-
-To check the status of batched background migrations:
-
-1. On the top bar, select **Main menu > Admin**.
-1. On the left sidebar, select **Monitoring > Background Migrations**.
-
- ![queued batched background migrations table](img/batched_background_migrations_queued_v14_0.png)
-
-All migrations must have a `Finished` status before you upgrade GitLab.
-
-The status of batched background migrations can also be queried directly in the database.
-
-1. Log into a `psql` prompt according to the directions for your instance's installation method
-(for example, `sudo gitlab-psql` for Omnibus installations).
-1. Run the following query in the `psql` session to see details on incomplete batched background migrations:
-
- ```sql
- select job_class_name, table_name, column_name, job_arguments from batched_background_migrations where status <> 3;
- ```
-
-If the migrations are not finished and you try to update to a later version,
-GitLab prompts you with an error:
-
-```plaintext
-Expected batched background migration for the given configuration to be marked as 'finished', but it is 'active':
-```
-
-If you get this error, [check the batched background migration options](../user/admin_area/monitoring/background_migrations.md#database-migrations-failing-because-of-batched-background-migration-not-finished) to complete the upgrade.
-
-### What do you do if your background migrations are stuck?
-
-WARNING:
-The following operations can disrupt your GitLab performance. They run a number of Sidekiq jobs that perform various database or file updates.
-
-#### Background migrations remain in the Sidekiq queue
-
-Run the following check. If it returns non-zero and the count does not decrease over time, follow the rest of the steps in this section.
-
-```shell
-# For Omnibus installations:
-sudo gitlab-rails runner -e production 'puts Gitlab::BackgroundMigration.remaining'
-
-# For installations from source:
-cd /home/git/gitlab
-sudo -u git -H bundle exec rails runner -e production 'puts Gitlab::BackgroundMigration.remaining'
-```
-
-It is safe to re-execute the following commands, especially if you have 1000+ pending jobs which would likely overflow your runtime memory.
-
-**For Omnibus installations**
-
-```shell
-# Start the rails console
-sudo gitlab-rails c
-
-# Execute the following in the rails console
-scheduled_queue = Sidekiq::ScheduledSet.new
-pending_job_classes = scheduled_queue.select { |job| job["class"] == "BackgroundMigrationWorker" }.map { |job| job["args"].first }.uniq
-pending_job_classes.each { |job_class| Gitlab::BackgroundMigration.steal(job_class) }
-```
-
-**For installations from source**
-
-```shell
-# Start the rails console
-sudo -u git -H bundle exec rails RAILS_ENV=production
-
-# Execute the following in the rails console
-scheduled_queue = Sidekiq::ScheduledSet.new
-pending_job_classes = scheduled_queue.select { |job| job["class"] == "BackgroundMigrationWorker" }.map { |job| job["args"].first }.uniq
-pending_job_classes.each { |job_class| Gitlab::BackgroundMigration.steal(job_class) }
-```
-
-#### Background migrations stuck in 'pending' state
-
-GitLab 13.6 introduced an issue where a background migration named `BackfillJiraTrackerDeploymentType2` can be permanently stuck in a **pending** state across upgrades. To clean up this stuck migration, see the [13.6.0 version-specific instructions](#1360).
-
-GitLab 14.2 introduced an issue where a background migration named `BackfillDraftStatusOnMergeRequests` can be permanently stuck in a **pending** state across upgrades when the instance lacks records that match the migration's target. To clean up this stuck migration, see the [14.2.0 version-specific instructions](#1420).
-
-GitLab 14.4 introduced an issue where a background migration named `PopulateTopicsTotalProjectsCountCache` can be permanently stuck in a **pending** state across upgrades when the instance lacks records that match the migration's target. To clean up this stuck migration, see the [14.4.0 version-specific instructions](#1440).
-
-GitLab 14.5 introduced an issue where a background migration named `UpdateVulnerabilityOccurrencesLocation` can be permanently stuck in a **pending** state across upgrades when the instance lacks records that match the migration's target. To clean up this stuck migration, see the [14.5.0 version-specific instructions](#1450).
-
-GitLab 14.8 introduced an issue where a background migration named `PopulateTopicsNonPrivateProjectsCount` can be permanently stuck in a **pending** state across upgrades. To clean up this stuck migration, see the [14.8.0 version-specific instructions](#1480).
-
-GitLab 14.9 introduced an issue where a background migration named `ResetDuplicateCiRunnersTokenValuesOnProjects` can be permanently stuck in a **pending** state across upgrades when the instance lacks records that match the migration's target. To clean up this stuck migration, see the [14.9.0 version-specific instructions](#1490).
-
-For other background migrations stuck in pending, run the following check. If it returns non-zero and the count does not decrease over time, follow the rest of the steps in this section.
-
-```shell
-# For Omnibus installations:
-sudo gitlab-rails runner -e production 'puts Gitlab::Database::BackgroundMigrationJob.pending.count'
-
-# For installations from source:
-cd /home/git/gitlab
-sudo -u git -H bundle exec rails runner -e production 'puts Gitlab::Database::BackgroundMigrationJob.pending.count'
-```
-
-It is safe to re-attempt these migrations to clear them out from a pending status:
-
-**For Omnibus installations**
-
-```shell
-# Start the rails console
-sudo gitlab-rails c
-
-# Execute the following in the rails console
-Gitlab::Database::BackgroundMigrationJob.pending.find_each do |job|
- puts "Running pending job '#{job.class_name}' with arguments #{job.arguments}"
- result = Gitlab::BackgroundMigration.perform(job.class_name, job.arguments)
- puts "Result: #{result}"
-end
-```
-
-**For installations from source**
-
-```shell
-# Start the rails console
-sudo -u git -H bundle exec rails RAILS_ENV=production
-
-# Execute the following in the rails console
-Gitlab::Database::BackgroundMigrationJob.pending.find_each do |job|
- puts "Running pending job '#{job.class_name}' with arguments #{job.arguments}"
- result = Gitlab::BackgroundMigration.perform(job.class_name, job.arguments)
- puts "Result: #{result}"
-end
-```
-
-#### Batched migrations (GitLab 14.0 and later)
-
-See [troubleshooting batched background migrations](../user/admin_area/monitoring/background_migrations.md#troubleshooting).
+For more information, see [background migrations](background_migrations.md).
## Dealing with running CI/CD pipelines and jobs
@@ -360,7 +162,7 @@ A *major* upgrade requires the following steps:
1. Upgrade to the "dot zero" release of the next major version (`X.0.Z`).
1. Optional. Follow the [upgrade path](#upgrade-paths), and proceed with upgrading to newer releases of that major version.
-It's also important to ensure that any [background migrations have been fully completed](#checking-for-background-migrations-before-upgrading)
+It's also important to ensure that any [background migrations have been fully completed](background_migrations.md)
before upgrading to a new major version.
If you have enabled the [Elasticsearch integration](../integration/advanced_search/elasticsearch.md) **(PREMIUM SELF)**, then
@@ -384,7 +186,7 @@ Find where your version sits in the upgrade path below, and upgrade GitLab
accordingly, while also consulting the
[version-specific upgrade instructions](#version-specific-upgrading-instructions):
-`8.11.Z` -> `8.12.0` -> `8.17.7` -> `9.5.10` -> `10.8.7` -> [`11.11.8`](#1200) -> `12.0.12` -> [`12.1.17`](#1210) -> [`12.10.14`](#12100) -> `13.0.14` -> [`13.1.11`](#1310) -> [`13.8.8`](#1388) -> [`13.12.15`](#13120) -> [`14.0.12`](#1400) -> [`14.3.6`](#1430) -> [`14.9.5`](#1490) -> [`14.10.Z`](#14100) -> [`15.0.Z`](#1500) -> [`15.1.Z`](#1510) (for GitLab instances with multiple web nodes) -> [`15.4.0`](#1540) -> [latest `15.Y.Z`](https://gitlab.com/gitlab-org/gitlab/-/releases)
+`8.11.Z` -> `8.12.0` -> `8.17.7` -> `9.0.13` -> `9.5.10` -> `10.0.7` -> `10.8.7` -> `11.0.6` -> [`11.11.8`](#1200) -> `12.0.12` -> [`12.1.17`](#1210) -> [`12.10.14`](#12100) -> `13.0.14` -> [`13.1.11`](#1310) -> [`13.8.8`](#1388) -> [`13.12.15`](#13120) -> [`14.0.12`](#1400) -> [`14.3.6`](#1430) -> [`14.9.5`](#1490) -> [`14.10.Z`](#14100) -> [`15.0.Z`](#1500) -> [`15.1.Z`](#1510) (for GitLab instances with multiple web nodes) -> [`15.4.0`](#1540) -> [latest `15.Y.Z`](https://gitlab.com/gitlab-org/gitlab/-/releases)
NOTE:
When not explicitly specified, upgrade GitLab to the latest available patch
@@ -398,18 +200,18 @@ The following table, while not exhaustive, shows some examples of the supported
upgrade paths.
Additional steps between the mentioned versions are possible. We list the minimally necessary steps only.
-| Target version | Your version | Supported upgrade path | Note |
-| -------------- | ------------ | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
-| `15.1.0` | `14.6.2` | `14.6.2` -> `14.9.5` -> `14.10.5` -> `15.0.2` -> `15.1.0` | Three intermediate versions are required: `14.9` and `14.10`, `15.0`, then `15.1.0`. |
-| `15.0.0` | `14.6.2` | `14.6.2` -> `14.9.5` -> `14.10.5` -> `15.0.2` | Two intermediate versions are required: `14.9` and `14.10`, then `15.0.0`. |
-| `14.6.2` | `13.10.2` | `13.10.2` -> `13.12.15` -> `14.0.12` -> `14.3.6` => `14.6.2` | Three intermediate versions are required: `13.12` and `14.0`, `14.3`, then `14.6.2`. |
-| `14.1.8` | `13.9.2` | `13.9.2` -> `13.12.15` -> `14.0.12` -> `14.1.8` | Two intermediate versions are required: `13.12` and `14.0`, then `14.1.8`. |
-| `13.12.15` | `12.9.2` | `12.9.2` -> `12.10.14` -> `13.0.14` -> `13.1.11` -> `13.8.8` -> `13.12.15` | Four intermediate versions are required: `12.10`, `13.0`, `13.1` and `13.8.8`, then `13.12.15`. |
-| `13.2.10` | `11.5.0` | `11.5.0` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.10.14` -> `13.0.14` -> `13.1.11` -> `13.2.10` | Six intermediate versions are required: `11.11`, `12.0`, `12.1`, `12.10`, `13.0` and `13.1`, then `13.2.10`. |
-| `12.10.14` | `11.3.4` | `11.3.4` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.10.14` | Three intermediate versions are required: `11.11`, `12.0` and `12.1`, then `12.10.14`. |
-| `12.9.5` | `10.4.5` | `10.4.5` -> `10.8.7` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.9.5` | Four intermediate versions are required: `10.8`, `11.11`, `12.0` and `12.1`, then `12.9.5`. |
-| `12.2.5` | `9.2.6` | `9.2.6` -> `9.5.10` -> `10.8.7` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.2.5` | Five intermediate versions are required: `9.5`, `10.8`, `11.11`, `12.0`, and `12.1`, then `12.2.5`. |
-| `11.3.4` | `8.13.4` | `8.13.4` -> `8.17.7` -> `9.5.10` -> `10.8.7` -> `11.3.4` | `8.17.7` is the last version in version 8, `9.5.10` is the last version in version 9, `10.8.7` is the last version in version 10. |
+| Target version | Your version | Supported upgrade path | Note |
+| -------------- | ------------ | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
+| `15.1.0` | `14.6.2` | `14.6.2` -> `14.9.5` -> `14.10.5` -> `15.0.2` -> `15.1.0` | Three intermediate versions are required: `14.9`, `14.10`, and `15.0`. |
+| `15.0.0` | `14.6.2` | `14.6.2` -> `14.9.5` -> `14.10.5` -> `15.0.2` | Two intermediate versions are required: `14.9` and `14.10`. |
+| `14.6.2` | `13.10.2` | `13.10.2` -> `13.12.15` -> `14.0.12` -> `14.3.6` => `14.6.2` | Three intermediate versions are required: `13.12`, `14.0`, and `14.3`. |
+| `14.1.8` | `13.9.2` | `13.9.2` -> `13.12.15` -> `14.0.12` -> `14.1.8` | Two intermediate versions are required: `13.12` and `14.0`. |
+| `13.12.15` | `12.9.2` | `12.9.2` -> `12.10.14` -> `13.0.14` -> `13.1.11` -> `13.8.8` -> `13.12.15` | Four intermediate versions are required: `12.10`, `13.0`, `13.1`, and `13.8`. |
+| `13.2.10` | `11.5.0` | `11.5.0` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.10.14` -> `13.0.14` -> `13.1.11` -> `13.2.10` | Six intermediate versions are required: `11.11`, `12.0`, `12.1`, `12.10`, `13.0`, and `13.1`. |
+| `12.10.14` | `11.3.4` | `11.3.4` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.10.14` | Three intermediate versions are required: `11.11`, `12.0`, and `12.1`. |
+| `12.9.5` | `10.4.5` | `10.4.5` -> `10.8.7` -> `11.0.6` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.9.5` | Five intermediate versions are required: `10.8`, `11.0`, `11.11`, `12.0`, and `12.1`. |
+| `12.2.5` | `9.2.6` | `9.2.6` -> `9.5.10` -> `10.0.7` -> `10.8.7` -> `11.0.6` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.2.5` | Seven intermediate versions are required: `9.5`, `10.0`, `10.8`, `11.0`, `11.11`, `12.0`, and `12.1`. |
+| `11.3.4` | `8.13.4` | `8.13.4` -> `8.17.7` -> `9.0.13` -> `9.5.10` -> `10.0.7` -> `10.8.7` -> `11.0.6` -> `11.3.4` | Six intermediate versions are required: `8.17`, `9.0`, `9.5`, `10.0`, `10.8`, and `11.0`. |
## Upgrading between editions
@@ -471,13 +273,68 @@ NOTE:
Specific information that follow related to Ruby and Git versions do not apply to [Omnibus installations](https://docs.gitlab.com/omnibus/)
and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with appropriate Ruby and Git versions and are not using system binaries for Ruby and Git. There is no need to install Ruby or Git when utilizing these two approaches.
+### 15.7.0
+
+- This version validates a `NOT NULL DB` constraint on the `issues.work_item_type_id` column.
+ To upgrade to this version, no records with a `NULL` `work_item_type_id` should exist on the `issues` table.
+ There are multiple `BackfillWorkItemTypeIdForIssues` background migrations that will be finalized with
+ the `EnsureWorkItemTypeBackfillMigrationFinished` post-deploy migration.
+- GitLab 15.4.0 introduced a [batched background migration](background_migrations.md#batched-background-migrations) to
+ [backfill `namespace_id` values on issues table](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91921). This
+ migration might take multiple hours or days to complete on larger GitLab instances. Please make sure the migration
+ has completed successfully before upgrading to 15.7.0.
+- A database constraint is added, specifying that the `namespace_id` column on the issues
+ table has no `NULL` values.
+
+ - If the `namespace_id` batched background migration from 15.4 failed (see above) then the 15.7 upgrade
+ fails with a database migration error.
+
+ - On GitLab instances with large issues tables, validating this constraint causes the upgrade to take
+ longer than usual. All database changes need to complete within a one-hour limit:
+
+ ```plaintext
+ FATAL: Mixlib::ShellOut::CommandTimeout: rails_migration[gitlab-rails]
+ [..]
+ Mixlib::ShellOut::CommandTimeout: Command timed out after 3600s:
+ ```
+
+ A workaround exists to [complete the data change and the upgrade manually](package/index.md#mixlibshelloutcommandtimeout-rails_migrationgitlab-rails--command-timed-out-after-3600s).
+- The default Sidekiq `max_concurrency` has been changed to 20. This is now
+ consistent in our documentation and product defaults.
+
+ For example, previously:
+ - Omnibus GitLab default (`sidekiq['max_concurrency']`): 50
+ - From source installation default: 50
+ - Helm chart default (`gitlab.sidekiq.concurrency`): 25
+
+ Reference architectures still use a default of 10 as this is set specifically
+ for those configurations.
+
+ Sites that have configured `max_concurrency` will not be affected by this change.
+ [Read more about the Sidekiq concurrency setting](../administration/sidekiq/extra_sidekiq_processes.md#concurrency).
+
### 15.6.0
+- You should use one of the [officially supported PostgreSQL versions](../administration/package_information/postgresql_versions.md). Some database migrations can cause stability and performance issues with older PostgreSQL versions.
- Git 2.37.0 and later is required by Gitaly. For installations from source, we recommend you use the [Git version provided by Gitaly](../install/installation.md#git).
+- A database change to modify the behavior of four indexes fails on instances
+ where these indexes do not exist:
+
+ ```plaintext
+ Caused by:
+ PG::UndefinedTable: ERROR: relation "index_issues_on_title_trigram" does not exist
+ ```
+
+ The other three indexes are: `index_merge_requests_on_title_trigram`, `index_merge_requests_on_description_trigram`,
+ and `index_issues_on_description_trigram`.
+
+ This issue was [fixed in GitLab 15.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105375) and backported
+ to GitLab 15.6.2. The issue can also be worked around:
+ [read about how to create these indexes](https://gitlab.com/gitlab-org/gitlab/-/issues/378343#note_1199863087).
### 15.5.0
-- GitLab 15.4.0 introduced a default [Sidekiq routing rule](../administration/sidekiq/extra_sidekiq_routing.md) that routes all jobs to the `default` queue. For instances using [queue selectors](../administration/sidekiq/extra_sidekiq_processes.md#queue-selector), this will cause [performance problems](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1991) as some Sidekiq processes will be idle.
+- GitLab 15.4.0 introduced a default [Sidekiq routing rule](../administration/sidekiq/extra_sidekiq_routing.md) that routes all jobs to the `default` queue. For instances using [queue selectors](../administration/sidekiq/processing_specific_job_classes.md#queue-selectors), this will cause [performance problems](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1991) as some Sidekiq processes will be idle.
- The default routing rule has been reverted in 15.5.4, so upgrading to that version or later will return to the previous behavior.
- If a GitLab instance now listens only to the `default` queue (which is not currently recommended), it will be required to add this routing rule back in `/etc/gitlab/gitlab.rb`:
@@ -485,12 +342,22 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
sidekiq['routing_rules'] = [['*', 'default']]
```
+### 15.4.1
+
+A [license caching issue](https://gitlab.com/gitlab-org/gitlab/-/issues/376706) prevents some premium features of GitLab from working correctly if you add a new license. Workarounds for this issue:
+
+- Restart all Rails, Sidekiq and Gitaly nodes after applying a new license. This clears the relevant license caches and allows all premium features to operate correctly.
+- Upgrade to a version that is not impacted by this issue. The following upgrade paths are available for impacted versions:
+ - 15.2.5 --> 15.3.5
+ - 15.3.0 - 15.3.4 --> 15.3.5
+ - 15.4.1 --> 15.4.3
+
### 15.4.0
-- GitLab 15.4.0 includes a [batched background migration](#batched-background-migrations) to [remove incorrect values from `expire_at` in `ci_job_artifacts` table](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89318).
+- GitLab 15.4.0 includes a [batched background migration](background_migrations.md#batched-background-migrations) to [remove incorrect values from `expire_at` in `ci_job_artifacts` table](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89318).
This migration might take hours or days to complete on larger GitLab instances.
- By default, Gitaly and Praefect nodes use the time server at `pool.ntp.org`. If your instance can not connect to `pool.ntp.org`, [configure the `NTP_HOST` variable](../administration/gitaly/praefect.md#customize-time-server-setting).
-- GitLab 15.4.0 introduced a default [Sidekiq routing rule](../administration/sidekiq/extra_sidekiq_routing.md) that routes all jobs to the `default` queue. For instances using [queue selectors](../administration/sidekiq/extra_sidekiq_processes.md#queue-selector), this will cause [performance problems](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1991) as some Sidekiq processes will be idle.
+- GitLab 15.4.0 introduced a default [Sidekiq routing rule](../administration/sidekiq/extra_sidekiq_routing.md) that routes all jobs to the `default` queue. For instances using [queue selectors](../administration/sidekiq/processing_specific_job_classes.md#queue-selectors), this will cause [performance problems](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1991) as some Sidekiq processes will be idle.
- The default routing rule has been reverted in 15.4.5, so upgrading to that version or later will return to the previous behavior.
- If a GitLab instance now listens only to the `default` queue (which is not currently recommended), it will be required to add this routing rule back in `/etc/gitlab/gitlab.rb`:
@@ -498,15 +365,83 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
sidekiq['routing_rules'] = [['*', 'default']]
```
+- New Git repositories created in Gitaly cluster [no longer use the `@hashed` storage path](#change-to-praefect-generated-replica-paths-in-gitlab-153). Server
+ hooks for new repositories must be copied into a different location.
+- The structure of `/etc/gitlab/gitlab-secrets.json` was modified in [GitLab 15.4](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/6310),
+ and new configuration was added to `gitlab_pages`, `grafana`, and `mattermost` sections.
+ In a highly available or GitLab Geo environment, secrets need to be the same on all nodes.
+ If you're manually syncing the secrets file across nodes, or manually specifying secrets in
+ `/etc/gitlab/gitlab.rb`, make sure `/etc/gitlab/gitlab-secrets.json` is the same on all nodes.
+- GitLab 15.4.0 introduced a [batched background migration](background_migrations.md#batched-background-migrations) to
+ [backfill `namespace_id` values on issues table](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91921). This
+ migration might take multiple hours or days to complete on larger GitLab instances. Please make sure the migration
+ has completed successfully before upgrading to 15.7.0 or later.
+
+### 15.3.4
+
+A [license caching issue](https://gitlab.com/gitlab-org/gitlab/-/issues/376706) prevents some premium features of GitLab from working correctly if you add a new license. Workarounds for this issue:
+
+- Restart all Rails, Sidekiq and Gitaly nodes after applying a new license. This clears the relevant license caches and allows all premium features to operate correctly.
+- Upgrade to a version that is not impacted by this issue. The following upgrade paths are available for impacted versions:
+ - 15.2.5 --> 15.3.5
+ - 15.3.0 - 15.3.4 --> 15.3.5
+ - 15.4.1 --> 15.4.3
+
### 15.3.3
- In GitLab 15.3.3, [SAML Group Links](../api/groups.md#saml-group-links) API `access_level` attribute type changed to `integer`. See
-[valid access levels](../api/members.md#valid-access-levels) documentation.
+[the API documentation](../api/members.md).
+- A [license caching issue](https://gitlab.com/gitlab-org/gitlab/-/issues/376706) prevents some premium features of GitLab from working correctly if you add a new license. Workarounds for this issue:
+
+ - Restart all Rails, Sidekiq and Gitaly nodes after applying a new license. This clears the relevant license caches and allows all premium features to operate correctly.
+ - Upgrade to a version that is not impacted by this issue. The following upgrade paths are available for impacted versions:
+ - 15.2.5 --> 15.3.5
+ - 15.3.0 - 15.3.4 --> 15.3.5
+ - 15.4.1 --> 15.4.3
+
+### 15.3.2
+
+A [license caching issue](https://gitlab.com/gitlab-org/gitlab/-/issues/376706) prevents some premium features of GitLab from working correctly if you add a new license. Workarounds for this issue:
+
+- Restart all Rails, Sidekiq and Gitaly nodes after applying a new license. This clears the relevant license caches and allows all premium features to operate correctly.
+- Upgrade to a version that is not impacted by this issue. The following upgrade paths are available for impacted versions:
+ - 15.2.5 --> 15.3.5
+ - 15.3.0 - 15.3.4 --> 15.3.5
+ - 15.4.1 --> 15.4.3
+
+### 15.3.1
+
+A [license caching issue](https://gitlab.com/gitlab-org/gitlab/-/issues/376706) prevents some premium features of GitLab from working correctly if you add a new license. Workarounds for this issue:
+
+- Restart all Rails, Sidekiq and Gitaly nodes after applying a new license. This clears the relevant license caches and allows all premium features to operate correctly.
+- Upgrade to a version that is not impacted by this issue. The following upgrade paths are available for impacted versions:
+ - 15.2.5 --> 15.3.5
+ - 15.3.0 - 15.3.4 --> 15.3.5
+ - 15.4.1 --> 15.4.3
### 15.3.0
- [Incorrect deletion of object storage files on Geo secondary sites](https://gitlab.com/gitlab-org/gitlab/-/issues/371397) can occur in certain situations. See [Geo: Incorrect object storage LFS file deletion on secondary site issue in GitLab 15.0.0 to 15.3.2](#geo-incorrect-object-storage-lfs-file-deletion-on-secondary-sites-in-gitlab-1500-to-1532).
- LFS transfers can [redirect to the primary from secondary site mid-session](https://gitlab.com/gitlab-org/gitlab/-/issues/371571) causing failed pull and clone requests when [Geo proxying](../administration/geo/secondary_proxy/index.md) is enabled. Geo proxying is enabled by default in GitLab 15.1 and later. See [Geo: LFS transfer redirect to primary from secondary site mid-session issue in GitLab 15.1.0 to 15.3.2](#geo-lfs-transfers-redirect-to-primary-from-secondary-site-mid-session-in-gitlab-1510-to-1532) for more details.
+- New Git repositories created in Gitaly cluster [no longer use the `@hashed` storage path](#change-to-praefect-generated-replica-paths-in-gitlab-153). Server
+ hooks for new repositories must be copied into a different location.
+- A [license caching issue](https://gitlab.com/gitlab-org/gitlab/-/issues/376706) prevents some premium features of GitLab from working correctly if you add a new license. Workarounds for this issue:
+
+ - Restart all Rails, Sidekiq and Gitaly nodes after applying a new license. This clears the relevant license caches and allows all premium features to operate correctly.
+ - Upgrade to a version that is not impacted by this issue. The following upgrade paths are available for impacted versions:
+ - 15.2.5 --> 15.3.5
+ - 15.3.0 - 15.3.4 --> 15.3.5
+ - 15.4.1 --> 15.4.3
+
+### 15.2.5
+
+A [license caching issue](https://gitlab.com/gitlab-org/gitlab/-/issues/376706) prevents some premium features of GitLab from working correctly if you add a new license. Workarounds for this issue:
+
+- Restart all Rails, Sidekiq and Gitaly nodes after applying a new license. This clears the relevant license caches and allows all premium features to operate correctly.
+- Upgrade to a version that is not impacted by this issue. The following upgrade paths are available for impacted versions:
+ - 15.2.5 --> 15.3.5
+ - 15.3.0 - 15.3.4 --> 15.3.5
+ - 15.4.1 --> 15.4.3
### 15.2.0
@@ -514,7 +449,7 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
[upgraded to 15.1](#1510) before upgrading to 15.2 (and later) due to a
configuration change in Rails that can result in inconsistent ETag key
generation.
-- Some Sidekiq workers were renamed in this release. To avoid any disruption, [run the Rake tasks to migrate any pending jobs](../administration/sidekiq/sidekiq_job_migration.md#future-jobs) before starting the upgrade to GitLab 15.2.0.
+- Some Sidekiq workers were renamed in this release. To avoid any disruption, [run the Rake tasks to migrate any pending jobs](../administration/sidekiq/sidekiq_job_migration.md#migrate-queued-and-future-jobs) before starting the upgrade to GitLab 15.2.0.
- Gitaly now executes its binaries in a [runtime location](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/4670). By default on Omnibus GitLab,
this path is `/var/opt/gitlab/gitaly/run/`. If this location is mounted with `noexec`, merge requests generate the following error:
@@ -564,6 +499,8 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
than `<custom_hooks_dir>/<hook_name>`.
- [Incorrect deletion of object storage files on Geo secondary sites](https://gitlab.com/gitlab-org/gitlab/-/issues/371397) can occur in certain situations. See [Geo: Incorrect object storage LFS file deletion on secondary site issue in GitLab 15.0.0 to 15.3.2](#geo-incorrect-object-storage-lfs-file-deletion-on-secondary-sites-in-gitlab-1500-to-1532).
- The `FF_GITLAB_REGISTRY_HELPER_IMAGE` [feature flag](../administration/feature_flags.md#enable-or-disable-the-feature) is removed and helper images are always pulled from GitLab Registry.
+- The `AES256-GCM-SHA384` SSL cipher is no longer allowed by NGINX.
+ See how you can [add the cipher back](https://docs.gitlab.com/omnibus/update/gitlab_15_changes.html#aes256-gcm-sha384-ssl-cipher-no-longer-allowed-by-default-by-nginx) to the allow list.
### 14.10.0
@@ -591,11 +528,11 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
### 14.9.0
- Database changes made by the upgrade to GitLab 14.9 can take hours or days to complete on larger GitLab instances.
- These [batched background migrations](#batched-background-migrations) update whole database tables to ensure corresponding
+ These [batched background migrations](background_migrations.md#batched-background-migrations) update whole database tables to ensure corresponding
records in `namespaces` table for each record in `projects` table.
After you update to 14.9.0 or a later 14.9 patch version,
- [batched background migrations must finish](#batched-background-migrations)
+ [batched background migrations must finish](background_migrations.md#batched-background-migrations)
before you update to a later version.
If the migrations are not finished and you try to update to a later version,
@@ -671,7 +608,7 @@ that may remain stuck permanently in a **pending** state.
[an issue with job retries](https://gitlab.com/gitlab-org/gitlab/-/issues/357822), first upgrade
to GitLab 14.7.x and make sure all batched migrations have finished.
- If upgrading from version 14.3.0 or later, you might notice a failed
- [batched migration](../user/admin_area/monitoring/background_migrations.md) named
+ [batched migration](background_migrations.md#batched-background-migrations) named
`BackfillNamespaceIdForNamespaceRoute`. You can [ignore](https://gitlab.com/gitlab-org/gitlab/-/issues/357822)
this. Retry it after you upgrade to version 14.9.x.
- If you run external PostgreSQL, particularly AWS RDS,
@@ -794,7 +731,7 @@ that may remain stuck permanently in a **pending** state when the instance lacks
### 14.3.0
- [Instances running 14.0.0 - 14.0.4 should not upgrade directly to GitLab 14.2 or later](#upgrading-to-later-14y-releases).
-- Ensure [batched background migrations finish](#batched-background-migrations) before upgrading
+- Ensure [batched background migrations finish](background_migrations.md#batched-background-migrations) before upgrading
to 14.3.Z from earlier GitLab 14 releases.
- Ruby 2.7.4 is required. Refer to [the Ruby installation instructions](../install/installation.md#2-ruby)
for how to proceed.
@@ -827,15 +764,109 @@ for how to proceed.
gitlab-psql`):
```sql
- select count(*) from background_migration_jobs where class_name = 'MigrateMergeRequestDiffCommitUsers' and status = 0;
+ select status, count(*) from background_migration_jobs
+ where class_name = 'MigrateMergeRequestDiffCommitUsers' group by status;
+ ```
+
+ As jobs are completed, the database records change from `0` (pending) to `1`. If the number of
+ pending jobs doesn't decrease after a while, it's possible that the
+ `MigrateMergeRequestDiffCommitUsers` background migration jobs have failed. You
+ can check for errors in the Sidekiq logs:
+
+ ```shell
+ sudo grep MigrateMergeRequestDiffCommitUsers /var/log/gitlab/sidekiq/current | grep -i error
```
+ If needed, you can attempt to run the `MigrateMergeRequestDiffCommitUsers` background
+ migration jobs manually in the [GitLab Rails Console](../administration/operations/rails_console.md).
+ This can be done using Sidekiq asynchronously, or by using a Rails process directly:
+
+ - Using Sidekiq to schedule jobs asynchronously:
+
+ ```ruby
+ # For the first run, only attempt to execute 1 migration. If successful, increase
+ # the limit for subsequent runs
+ limit = 1
+
+ jobs = Gitlab::Database::BackgroundMigrationJob.for_migration_class('MigrateMergeRequestDiffCommitUsers').pending.to_a
+
+ pp "#{jobs.length} jobs remaining"
+
+ jobs.first(limit).each do |job|
+ BackgroundMigrationWorker.perform_in(5.minutes, 'MigrateMergeRequestDiffCommitUsers', job.arguments)
+ end
+ ```
+
+ NOTE:
+ The queued jobs can be monitored using Sidekiq's admin panel, which can be accessed at the `/admin/sidekiq` endpoint URI.
+
+ - Using a Rails process to run jobs synchronously:
+
+ ```ruby
+ def process(concurrency: 1)
+ queue = Queue.new
+
+ Gitlab::Database::BackgroundMigrationJob
+ .where(class_name: 'MigrateMergeRequestDiffCommitUsers', status: 0)
+ .each { |job| queue << job }
+
+ concurrency
+ .times
+ .map do
+ Thread.new do
+ Thread.abort_on_exception = true
+
+ loop do
+ job = queue.pop(true)
+ time = Benchmark.measure do
+ Gitlab::BackgroundMigration::MigrateMergeRequestDiffCommitUsers
+ .new
+ .perform(*job.arguments)
+ end
+
+ puts "#{job.id} finished in #{time.real.round(2)} seconds"
+ rescue ThreadError
+ break
+ end
+ end
+ end
+ .each(&:join)
+ end
+
+ ActiveRecord::Base.logger.level = Logger::ERROR
+ process
+ ```
+
+ NOTE:
+ When using Rails to execute these background migrations synchronously, make sure that the machine running the process has sufficient resources to handle the task. If the process gets terminated, it's likely due to insufficient memory available. If your SSH session times out after a while, it might be necessary to run the previous code by using a terminal multiplexer like `screen` or `tmux`.
+
- See [Maintenance mode issue in GitLab 13.9 to 14.4](#maintenance-mode-issue-in-gitlab-139-to-144).
+- You may see the following error when setting up two factor authentication (2FA) for accounts
+ that authenticate using an LDAP password:
+
+ ```plaintext
+ You must provide a valid current password
+ ```
+
+ - The error occurs because verification is incorrectly performed against accounts'
+ randomly generated internal GitLab passwords, not the LDAP passwords.
+ - This is [fixed in GitLab 14.5.0 and backported to 14.4.3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73538).
+ - Workarounds:
+ - Instead of upgrading to GitLab 14.3.x to comply with the supported upgrade path:
+ 1. Upgrade to 14.4.5.
+ 1. Make sure the [`MigrateMergeRequestDiffCommitUsers` background migration](#1430) has finished.
+ 1. Upgrade to GitLab 14.5 or later.
+ - Reset the random password for affected accounts, using [the Rake task](../security/reset_user_password.md#use-a-rake-task):
+
+ ```plaintext
+ sudo gitlab-rake "gitlab:password:reset[user_handle]"
+ ```
+
### 14.2.0
- [Instances running 14.0.0 - 14.0.4 should not upgrade directly to GitLab 14.2 or later](#upgrading-to-later-14y-releases).
-- Ensure [batched background migrations finish](#batched-background-migrations) before upgrading
+- Ensure [batched background migrations finish](background_migrations.md#batched-background-migrations) before upgrading
to 14.2.Z from earlier GitLab 14 releases.
- GitLab 14.2.0 contains background migrations to [address Primary Key overflow risk for tables with an integer PK](https://gitlab.com/groups/gitlab-org/-/epics/4785) for the tables listed below:
- [`ci_build_needs`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65216)
@@ -879,7 +910,7 @@ for how to proceed.
It is not required for instances already running 14.0.5 (or later) to stop at 14.1.Z.
14.1 is included on the upgrade path for the broadest compatibility
with self-managed installations, and ensure 14.0.0-14.0.4 installations do not
- encounter issues with [batched background migrations](#batched-background-migrations).
+ encounter issues with [batched background migrations](background_migrations.md#batched-background-migrations).
- Upgrading to GitLab [14.5](#1450) (or later) may take a lot longer if you do not upgrade to at least 14.1
first. The 14.1 merge request diff commits database migration can take hours to run, but runs in the
@@ -901,14 +932,14 @@ Prerequisites:
Long running batched background database migrations:
- Database changes made by the upgrade to GitLab 14.0 can take hours or days to complete on larger GitLab instances.
- These [batched background migrations](#batched-background-migrations) update whole database tables to mitigate primary key overflow and must be finished before upgrading to GitLab 14.2 or later.
+ These [batched background migrations](background_migrations.md#batched-background-migrations) update whole database tables to mitigate primary key overflow and must be finished before upgrading to GitLab 14.2 or later.
- Due to an issue where `BatchedBackgroundMigrationWorkers` were
[not working](https://gitlab.com/gitlab-org/charts/gitlab/-/issues/2785#note_614738345)
for self-managed instances, a [fix was created](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65106)
that requires an update to at least 14.0.5. The fix was also released in [14.1.0](#1410).
After you update to 14.0.5 or a later 14.0 patch version,
- [batched background migrations must finish](#batched-background-migrations)
+ [batched background migrations must finish](background_migrations.md#batched-background-migrations)
before you update to a later version.
If the migrations are not finished and you try to update to a later version,
@@ -918,7 +949,7 @@ Long running batched background database migrations:
Expected batched background migration for the given configuration to be marked as 'finished', but it is 'active':
```
- See how to [resolve this error](../user/admin_area/monitoring/background_migrations.md#database-migrations-failing-because-of-batched-background-migration-not-finished).
+ See how to [resolve this error](background_migrations.md#database-migrations-failing-because-of-batched-background-migration-not-finished).
Other issues:
@@ -933,11 +964,11 @@ Other issues:
#### Upgrading to later 14.Y releases
- Instances running 14.0.0 - 14.0.4 should not upgrade directly to GitLab 14.2 or later,
- because of [batched background migrations](#batched-background-migrations).
+ because of [batched background migrations](background_migrations.md#batched-background-migrations).
1. Upgrade first to either:
- 14.0.5 or a later 14.0.Z patch release.
- 14.1.0 or a later 14.1.Z patch release.
- 1. [Batched background migrations must finish](#batched-background-migrations)
+ 1. [Batched background migrations must finish](background_migrations.md#batched-background-migrations)
before you update to a later version [and may take longer than usual](#1400).
### 13.12.0
@@ -1040,7 +1071,7 @@ See [Maintenance mode issue in GitLab 13.9 to 14.4](#maintenance-mode-issue-in-g
### 13.8.8
-GitLab 13.8 includes a background migration to address [an issue with duplicate service records](https://gitlab.com/gitlab-org/gitlab/-/issues/290008). If duplicate services are present, this background migration must complete before a unique index is applied to the services table, which was [introduced in GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52563). Upgrades from GitLab 13.8 and earlier to later versions must include an intermediate upgrade to GitLab 13.8.8 and [must wait until the background migrations complete](#checking-for-background-migrations-before-upgrading) before proceeding.
+GitLab 13.8 includes a background migration to address [an issue with duplicate service records](https://gitlab.com/gitlab-org/gitlab/-/issues/290008). If duplicate services are present, this background migration must complete before a unique index is applied to the services table, which was [introduced in GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52563). Upgrades from GitLab 13.8 and earlier to later versions must include an intermediate upgrade to GitLab 13.8.8 and [must wait until the background migrations complete](background_migrations.md) before proceeding.
If duplicate services are still present, an upgrade to 13.9.x or later results in a failed upgrade with the following error:
@@ -1157,9 +1188,14 @@ any downgrades would result to all sessions being invalidated and users are logg
### 12.1.0
-If you are planning to upgrade from `12.0.Z` to `12.10.Z`, it is necessary to
-perform an intermediary upgrade to `12.1.Z` before upgrading to `12.10.Z` to
-avoid issues like [#215141](https://gitlab.com/gitlab-org/gitlab/-/issues/215141).
+- If you are planning to upgrade from `12.0.Z` to `12.10.Z`, it is necessary to
+ perform an intermediary upgrade to `12.1.Z` before upgrading to `12.10.Z` to
+ avoid issues like [#215141](https://gitlab.com/gitlab-org/gitlab/-/issues/215141).
+
+- Support for MySQL was removed in GitLab 12.1. Existing users using GitLab with
+ MySQL/MariaDB should
+ [migrate to PostgreSQL](https://gitlab.com/gitlab-org/gitlab/-/blob/v15.6.0-ee/doc/update/mysql_to_postgresql.md)
+ before upgrading.
### 12.0.0
@@ -1181,6 +1217,19 @@ After upgraded to 11.11.8 you can safely upgrade to 12.0.Z.
See our [documentation on upgrade paths](../policy/maintenance.md#upgrade-recommendations)
for more information.
+### Change to Praefect-generated replica paths in GitLab 15.3
+
+New Git repositories created in Gitaly cluster no longer use the `@hashed` storage path.
+
+Praefect now generates replica paths for use by Gitaly cluster.
+This change is a pre-requisite for Gitaly cluster atomically creating, deleting, and
+renaming Git repositories.
+
+To identify the replica path, [query the Praefect repository metadata](../administration/gitaly/troubleshooting.md#view-repository-metadata)
+and pass the `@hashed` storage path to `-relative-path`.
+
+With this information, you can correctly install [server hooks](../administration/server_hooks.md).
+
### Maintenance mode issue in GitLab 13.9 to 14.4
When [Maintenance mode](../administration/maintenance_mode/index.md) is enabled, users cannot sign in with SSO, SAML, or LDAP.
@@ -1232,9 +1281,4 @@ This issue is resolved in GitLab 15.3.3, so customers with the following configu
## Miscellaneous
-- [MySQL to PostgreSQL](mysql_to_postgresql.md) guides you through migrating
- your database from MySQL to PostgreSQL.
-- [Restoring from backup after a failed upgrade](restore_after_failure.md)
-- [Upgrading PostgreSQL Using Slony](upgrading_postgresql_using_slony.md), for
- upgrading a PostgreSQL database with minimal downtime.
- [Managing PostgreSQL extensions](../install/postgresql_extensions.md)
diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md
index 14914eee27c..ad36a9ff534 100644
--- a/doc/update/mysql_to_postgresql.md
+++ b/doc/update/mysql_to_postgresql.md
@@ -2,307 +2,10 @@
stage: Data Stores
group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+remove_date: '2023-02-28'
+redirect_to: 'index.md'
---
-# Migrating from MySQL to PostgreSQL **(FREE SELF)**
+# Migrating from MySQL to PostgreSQL (removed) **(FREE SELF)**
-This guide documents how to take a working GitLab instance that uses MySQL and
-migrate it to a PostgreSQL database.
-
-## Requirements
-
-NOTE:
-Support for MySQL was removed in GitLab 12.1. This procedure should be performed
-**before** installing GitLab 12.1.
-
-[pgLoader](https://pgloader.io/) 3.4.1+ is required, confirm with `pgloader -V`.
-
-You can install it directly from your distribution, for example in
-Debian/Ubuntu:
-
-1. Search for the version:
-
- ```shell
- apt-cache madison pgloader
- ```
-
-1. If the version is 3.4.1+, install it with:
-
- ```shell
- sudo apt-get install pgloader
- ```
-
- If your distribution's version is too old, use PostgreSQL's repository:
-
- ```shell
- # Add repository
- sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
-
- # Add key
- sudo apt-get install wget ca-certificates
- wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
-
- # Install package
- sudo apt-get update
- sudo apt-get install pgloader
- ```
-
-For other distributions, follow the instructions in PostgreSQL's
-[download page](https://www.postgresql.org/download/) to add their repository
-and then install `pgloader`.
-
-If you are migrating to a Docker based installation, you must install
-pgLoader in the container as it is not included in the container image.
-
-1. Start a shell session in the context of the running container:
-
- ```shell
- docker exec -it gitlab bash
- ```
-
-1. Install pgLoader:
-
- ```shell
- apt-get update
- apt-get -y install pgloader
- ```
-
-## Omnibus GitLab installations
-
-For [Omnibus GitLab packages](https://about.gitlab.com/install/), you first
-enable the bundled PostgreSQL:
-
-1. Stop GitLab:
-
- ```shell
- sudo gitlab-ctl stop
- ```
-
-1. Edit `/etc/gitlab/gitlab.rb` to enable bundled PostgreSQL:
-
- ```ruby
- postgresql['enable'] = true
- ```
-
-1. Edit `/etc/gitlab/gitlab.rb` to use the bundled PostgreSQL. Review all of the
- settings beginning with `db_` (such as `gitlab_rails['db_adapter']`). To use
- the default values, you can comment all of them out.
-
-1. [Reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure)
- for the changes to take effect.
-
-1. Start Puma and PostgreSQL so that we can prepare the schema:
-
- ```shell
- sudo gitlab-ctl start puma
- sudo gitlab-ctl start postgresql
- ```
-
-1. Run the following commands to prepare the schema:
-
- ```shell
- sudo gitlab-rake db:create db:migrate
- ```
-
-1. Stop Puma to prevent other database access from interfering with the loading of data:
-
- ```shell
- sudo gitlab-ctl stop puma
- ```
-
-After these steps, you have a fresh PostgreSQL database with up-to-date schema.
-
-Next, use `pgloader` to migrate the data from the old MySQL database to the
-new PostgreSQL one:
-
-1. Save the following snippet in a `commands.load` file, and edit with your
- MySQL database `username`, `password` and `host`:
-
- ```sql
- LOAD DATABASE
- FROM mysql://username:password@host/gitlabhq_production
- INTO postgresql://gitlab-psql@unix://var/opt/gitlab/postgresql:/gitlabhq_production
-
- WITH include no drop, truncate, disable triggers, create no tables,
- create no indexes, preserve index names, no foreign keys,
- data only
-
- SET MySQL PARAMETERS
- net_read_timeout = '90',
- net_write_timeout = '180'
-
- ALTER SCHEMA 'gitlabhq_production' RENAME TO 'public'
-
- ;
- ```
-
-1. Start the migration:
-
- ```shell
- sudo -u gitlab-psql pgloader commands.load
- ```
-
-1. After the migration finishes, you should see a summary table that looks like
- the following:
-
- ```plaintext
- table name read imported errors total time
- ----------------------------------------------- --------- --------- --------- --------------
- fetch meta data 119 119 0 0.388s
- Truncate 119 119 0 1.134s
- ----------------------------------------------- --------- --------- --------- --------------
- public.abuse_reports 0 0 0 0.490s
- public.appearances 0 0 0 0.488s
- .
- .
- .
- public.web_hook_logs 0 0 0 1.080s
- ----------------------------------------------- --------- --------- --------- --------------
- COPY Threads Completion 4 4 0 2.008s
- Reset Sequences 113 113 0 0.304s
- Install Comments 0 0 0 0.000s
- ----------------------------------------------- --------- --------- --------- --------------
- Total import time 1894 1894 0 12.497s
- ```
-
- If there is no output for more than 30 minutes, it's possible `pgloader` encountered an error. See
- the [troubleshooting guide](#troubleshooting) for more details.
-
-1. Start GitLab:
-
- ```shell
- sudo gitlab-ctl start
- ```
-
-You can now verify that everything works as expected by visiting GitLab.
-
-## Source installations
-
-For installations from source that use MySQL, you must first
-[install PostgreSQL and create a database](../install/installation.md#6-database).
-
-After the database is created, go on with the following steps:
-
-1. Stop GitLab:
-
- ```shell
- sudo service gitlab stop
- ```
-
-1. Switch database from MySQL to PostgreSQL
-
- ```shell
- cd /home/git/gitlab
- sudo -u git mv config/database.yml config/database.yml.bak
- sudo -u git cp config/database.yml.postgresql config/database.yml
- sudo -u git -H chmod o-rwx config/database.yml
- ```
-
-1. Install Gems related to PostgreSQL
-
- ```shell
- sudo -u git -H rm .bundle/config
- sudo -u git -H bundle install --deployment --without development test mysql aws kerberos
- ```
-
-1. Run the following commands to prepare the schema:
-
- ```shell
- sudo -u git -H bundle exec rake db:create db:migrate RAILS_ENV=production
- ```
-
-After these steps, you have a fresh PostgreSQL database with up-to-date schema.
-
-Next, use `pgloader` to migrate the data from the old MySQL database to the
-new PostgreSQL one:
-
-1. Save the following snippet in a `commands.load` file, and edit with your
- MySQL `username`, `password` and `host`:
-
- ```sql
- LOAD DATABASE
- FROM mysql://username:password@host/gitlabhq_production
- INTO postgresql://postgres@unix://var/run/postgresql:/gitlabhq_production
-
- WITH include no drop, truncate, disable triggers, create no tables,
- create no indexes, preserve index names, no foreign keys,
- data only
-
- SET MySQL PARAMETERS
- net_read_timeout = '90',
- net_write_timeout = '180'
-
- ALTER SCHEMA 'gitlabhq_production' RENAME TO 'public'
-
- ;
- ```
-
-1. Start the migration:
-
- ```shell
- sudo -u postgres pgloader commands.load
- ```
-
-1. After the migration finishes, you should see a summary table that looks like
- the following:
-
- ```plaintext
- table name read imported errors total time
- ----------------------------------------------- --------- --------- --------- --------------
- fetch meta data 119 119 0 0.388s
- Truncate 119 119 0 1.134s
- ----------------------------------------------- --------- --------- --------- --------------
- public.abuse_reports 0 0 0 0.490s
- public.appearances 0 0 0 0.488s
- .
- .
- .
- public.web_hook_logs 0 0 0 1.080s
- ----------------------------------------------- --------- --------- --------- --------------
- COPY Threads Completion 4 4 0 2.008s
- Reset Sequences 113 113 0 0.304s
- Install Comments 0 0 0 0.000s
- ----------------------------------------------- --------- --------- --------- --------------
- Total import time 1894 1894 0 12.497s
- ```
-
- If there is no output for more than 30 minutes, it's possible `pgloader` encountered an error. See
- the [troubleshooting guide](#troubleshooting) for more details.
-
-1. Start GitLab:
-
- ```shell
- sudo service gitlab start
- ```
-
-You can now verify that everything works as expected by visiting GitLab.
-
-## Troubleshooting
-
-Sometimes, you might encounter some errors during or after the migration.
-
-### Database error permission denied
-
-The PostgreSQL user that you use for the migration **must** have **superuser** privileges.
-Otherwise, you may see a similar message to the following:
-
-```plaintext
-debugger invoked on a CL-POSTGRES-ERROR:INSUFFICIENT-PRIVILEGE in thread
- #<THREAD "lparallel" RUNNING {10078A3513}>:
- Database error 42501: permission denied: "RI_ConstraintTrigger_a_20937" is a system trigger
- QUERY: ALTER TABLE ci_builds DISABLE TRIGGER ALL;
- 2017-08-23T00:36:56.782000Z ERROR Database error 42501: permission denied: "RI_ConstraintTrigger_c_20864" is a system trigger
- QUERY: ALTER TABLE approver_groups DISABLE TRIGGER ALL;
-```
-
-### 500 errors after the migration
-
-If you experience 500 errors after the migration, try to clear the cache:
-
-```shell
-# Omnibus GitLab
-sudo gitlab-rake cache:clear
-
-# Installations from source
-sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
-```
+Support for MySQL was removed in GitLab 12.1.
diff --git a/doc/update/package/index.md b/doc/update/package/index.md
index e0a4a388f56..575194793c2 100644
--- a/doc/update/package/index.md
+++ b/doc/update/package/index.md
@@ -16,7 +16,7 @@ GitLab package.
- If you are upgrading from a non-package installation to a GitLab package installation, see
[Upgrading from a non-package installation to a GitLab package installation](https://docs.gitlab.com/omnibus/update/convert_to_omnibus.html).
- Ensure that any
- [background migrations](../index.md#checking-for-background-migrations-before-upgrading)
+ [background migrations](../background_migrations.md)
are fully completed. Upgrading
before background migrations have finished can lead to data corruption.
We recommend performing upgrades between major and minor releases no more than once per
@@ -178,9 +178,12 @@ To download and install GitLab:
# Debian/Ubuntu
dpkg -i <package_name>
- # CentOS/RHEL
+ # RHEL/CentOS 6 and 7
rpm -Uvh <package_name>
+ # RHEL/CentOS 8
+ dnf install <package_name>
+
# SUSE
zypper install <package_name>
```
diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md
index e0c0cdf31f9..efbe1bc7fcd 100644
--- a/doc/update/patch_versions.md
+++ b/doc/update/patch_versions.md
@@ -136,5 +136,5 @@ If all items are green, then congratulations upgrade complete!
### 11. Make sure background migrations are finished
-[Check the status of background migrations](../user/admin_area/monitoring/background_migrations.md#check-the-status-of-background-migrations)
+[Check the status of background migrations](../update/background_migrations.md)
and make sure they are finished.
diff --git a/doc/update/plan_your_upgrade.md b/doc/update/plan_your_upgrade.md
index 8169645e278..667ed37af02 100644
--- a/doc/update/plan_your_upgrade.md
+++ b/doc/update/plan_your_upgrade.md
@@ -126,7 +126,7 @@ to your instance and then upgrade it for any relevant features you're using.
- Account for any [version-specific changes](package/index.md#version-specific-changes).
- Check the [OS compatibility with the target GitLab version](../administration/package_information/supported_os.md).
- Due to background migrations, plan to pause before any further upgrades.
- [All migrations must finish running](index.md#checking-for-background-migrations-before-upgrading)
+ [All migrations must finish running](background_migrations.md)
before the next upgrade.
- If available in your starting version, consider
[turning on maintenance mode](../administration/maintenance_mode/index.md) during the
@@ -173,7 +173,7 @@ If you have Kubernetes clusters connected with GitLab, [upgrade your GitLab agen
#### Elasticsearch
Before updating GitLab, confirm Advanced Search migrations are complete by
-[checking for pending advanced search migrations](index.md#checking-for-pending-advanced-search-migrations).
+[checking for pending advanced search migrations](background_migrations.md).
After updating GitLab, you may have to upgrade
[Elasticsearch if the new version breaks compatibility](../integration/advanced_search/elasticsearch.md#version-requirements).
diff --git a/doc/update/removals.md b/doc/update/removals.md
index 0bc82403f60..10d937f0f16 100644
--- a/doc/update/removals.md
+++ b/doc/update/removals.md
@@ -9,6 +9,9 @@ info: "See the Technical Writers assigned to Development Guidelines: https://abo
In each release, GitLab removes features that were deprecated in an earlier release.
Some features cause breaking changes when they are removed.
+**{rss}** **To be notified of upcoming breaking changes**,
+add this URL to your RSS feed reader: `https://about.gitlab.com/breaking-changes.xml`
+
<!-- vale off -->
<!--
@@ -43,6 +46,43 @@ To change the approvals required for a merge request, you should no longer use t
Instead, use the [`/approval_rules` endpoint](https://docs.gitlab.com/ee/api/merge_request_approvals.html#merge-request-level-mr-approvals) to [create](https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-merge-request-level-rule) or [update](https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-merge-request-level-rule) the approval rules for a merge request.
+## Removed in 15.8
+
+### CiliumNetworkPolicy within the auto deploy Helm chart is removed
+
+All functionality related to the GitLab Container Network Security and Container Host Security categories was deprecated in GitLab 14.8 and scheduled for removal in GitLab 15.0. The [CiliumNetworkPolicy definition](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/blob/master/assets/auto-deploy-app/values.yaml#L175) that exists as part of the [GitLab Auto Deploy Helm chart](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app) was not removed as scheduled in GitLab 15.0. This policy is planned to be removed in the GitLab 15.8 release.
+
+If you want to preserve this functionality, you can follow one of these two paths:
+
+1. Fork the [GitLab Auto Deploy Helm chart](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app) into the `chart/` path within your project
+1. Set `AUTO_DEPLOY_IMAGE_VERSION` and `DAST_AUTO_DEPLOY_IMAGE_VERSION` to the most recent version of the image that included the CiliumNetworkPolicy
+
+## Removed in 15.7
+
+### Flowdock integration
+
+As of December 22, 2022, we are removing the Flowdock integration because the service was shut down on August 15, 2022.
+
+## Removed in 15.6
+
+### NFS as Git repository storage is no longer supported
+
+As of November 22, 2022, we have removed support for customers using NFS for Git repository storage. This was
+originally planned for May 22, 2022, but in an effort to allow continued maturity of Gitaly Cluster, we delayed
+our removal of support date until now. Please see our official [Statement of Support](https://about.gitlab.com/support/statement-of-support/#gitaly-and-nfs)
+for further information.
+
+This change in support follows the development deprecation for NFS for Git repository storage that occurred in GitLab 14.0.
+
+Gitaly Cluster offers tremendous benefits for our customers such as:
+
+- [Variable replication factors](https://docs.gitlab.com/ee/administration/gitaly/index.html#replication-factor).
+- [Strong consistency](https://docs.gitlab.com/ee/administration/gitaly/index.html#strong-consistency).
+- [Distributed read capabilities](https://docs.gitlab.com/ee/administration/gitaly/index.html#distributed-reads).
+
+We encourage customers currently using NFS for Git repositories to migrate as soon as possible by reviewing our documentation on
+[migrating to Gitaly Cluster](https://docs.gitlab.com/ee/administration/gitaly/index.html#migrate-to-gitaly-cluster).
+
## Removed in 15.4
### SAST analyzer consolidation and CI/CD template changes
@@ -730,7 +770,7 @@ WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
Review the details carefully before upgrading.
-The `push_rules_supersede_code_owners` feature flag has been removed in GitLab 15.0. From now on, push rules will supersede the `CODEOWNERS` file. The code owners feature is no longer available for access control.
+The `push_rules_supersede_code_owners` feature flag has been removed in GitLab 15.0. From now on, push rules will supersede the `CODEOWNERS` file. Even if Code Owner approval is required, a push rule that explicitly allows a specific user to push code supersedes the Code Owners setting.
### `type` and `types` keyword from CI/CD configuration
diff --git a/doc/update/restore_after_failure.md b/doc/update/restore_after_failure.md
index cc0b188a0f8..92b68410dca 100644
--- a/doc/update/restore_after_failure.md
+++ b/doc/update/restore_after_failure.md
@@ -2,64 +2,11 @@
stage: Systems
group: Distribution
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+remove_date: '2023-02-28'
+redirect_to: '../raketasks/backup_restore.md'
---
-# Restoring from backup after a failed upgrade **(FREE SELF)**
+# Restoring from backup after a failed upgrade (removed) **(FREE SELF)**
-Upgrades are usually smooth and restoring from backup is a rare occurrence.
-However, it's important to know how to recover when problems do arise.
-
-## Roll back to an earlier version and restore a backup
-
-In some cases after a failed upgrade, the fastest solution is to roll back to
-the previous version you were using. We recommend this path because the failed
-upgrade might have made database changes that cannot be readily reverted.
-
-First, roll back the code or package. For source installations this involves
-checking out the older version (branch or tag). For Omnibus installations this
-means installing the older
-[`.deb` or `.rpm` package](https://packages.gitlab.com/gitlab). Then, restore from a
-backup.
-Follow the instructions in the
-[Backup and Restore](../raketasks/backup_restore.md#restore-gitlab)
-documentation.
-
-## Potential problems on the next upgrade
-
-When a rollback is necessary it can produce problems on subsequent upgrade
-attempts. This is because some tables may have been added during the failed
-upgrade. If these tables are still present after you restore from the
-older backup it can lead to migration failures on future upgrades.
-
-We drop all tables prior to importing the backup to prevent this problem.
-
-Example error:
-
-```plaintext
-== 20151103134857 CreateLfsObjects: migrating =================================
--- create_table(:lfs_objects)
-rake aborted!
-StandardError: An error has occurred, this and all later migrations canceled:
-
-PG::DuplicateTable: ERROR: relation "lfs_objects" already exists
-```
-
-Copy the version from the error. In this case the version number is
-`20151103134857`.
-
-WARNING:
-Use the following steps only if you are certain you must do them.
-
-1. Pass the version to a database Rake task to manually mark the migration as
- complete.
-
- ```shell
- # Source install
- sudo -u git -H bundle exec rake gitlab:db:mark_migration_complete[20151103134857] RAILS_ENV=production
-
- # Omnibus install
- sudo gitlab-rake gitlab:db:mark_migration_complete[20151103134857]
- ```
-
-1. After the migration is successfully marked, run the Rake `db:migrate` task again.
-1. Repeat this process until all failed migrations are complete.
+This content was removed in GitLab 15.7.
+Use the [backup and restore](../raketasks/backup_restore.md) documentation instead.
diff --git a/doc/update/upgrading_from_source.md b/doc/update/upgrading_from_source.md
index f5b85330f3b..852b54c7339 100644
--- a/doc/update/upgrading_from_source.md
+++ b/doc/update/upgrading_from_source.md
@@ -29,7 +29,7 @@ to identify the ideal upgrade path.
Before upgrading to a new major version, you should ensure that any background
migration jobs from previous releases have been completed. To see the current size of the `background_migration` queue,
-[Check for background migrations before upgrading](index.md#checking-for-background-migrations-before-upgrading).
+[Check for background migrations before upgrading](background_migrations.md).
## Guidelines for all versions
diff --git a/doc/update/upgrading_postgresql_using_slony.md b/doc/update/upgrading_postgresql_using_slony.md
index a645eb220ad..6d2abee3fc6 100644
--- a/doc/update/upgrading_postgresql_using_slony.md
+++ b/doc/update/upgrading_postgresql_using_slony.md
@@ -2,479 +2,11 @@
stage: Data Stores
group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+remove_date: '2023-02-28'
+redirect_to: '../administration/postgresql/replication_and_failover.md'
---
-# Upgrading PostgreSQL Using Slony **(FREE SELF)**
+# Upgrading PostgreSQL Using Slony (removed) **(FREE SELF)**
-This guide describes the steps one can take to upgrade their PostgreSQL database
-to the latest version without the need for hours of downtime. This guide assumes
-you have two database servers: one database server running an older version of
-PostgreSQL (for example, 9.2.18) and one server running a newer version (for example, 9.6.0).
-
-For this process, a PostgreSQL replication tool called
-[Slony](https://www.slony.info/) is used. Slony allows replication between different
-PostgreSQL versions and as such can be used to upgrade a cluster with a minimal
-amount of downtime.
-
-This guide often refers to the user `gitlab-psql`, which is the
-user used to run the various PostgreSQL OS processes. If you are using a
-different user (for example, `postgres`), replace `gitlab-psql` with the name
-of said user. This guide also assumes your database is called
-`gitlabhq_production`. If you happen to use a different database name you should
-change this accordingly.
-
-## Database Dumps
-
-Slony only replicates data and not any schema changes. As a result you must
-ensure that all databases have the same database structure.
-
-To do so, generate a dump of the current database. This dump only
-contains the structure, not any data. To generate this dump run the following
-command on your active database server:
-
-```shell
-sudo -u gitlab-psql /opt/gitlab/embedded/bin/pg_dump -h /var/opt/gitlab/postgresql -p 5432 -U gitlab-psql -s -f /tmp/structure.sql gitlabhq_production
-```
-
-If you're not using the Omnibus GitLab package you may have to adjust the paths to
-`pg_dump` and the PostgreSQL installation directory to match the paths of your
-configuration.
-
-After the structure dump is generated, generate another dump for the
-`schema_migrations` table. This table doesn't have any primary keys and as such
-can't be replicated by Slony. To generate a dump of the `schema_migrations` table, run the following command on your active database server:
-
-```shell
-sudo -u gitlab-psql /opt/gitlab/embedded/bin/pg_dump -h /var/opt/gitlab/postgresql/ -p 5432 -U gitlab-psql -a -t schema_migrations -f /tmp/migrations.sql gitlabhq_production
-```
-
-Next, move these files somewhere accessible by the new database
-server. The easiest way is to download these files to your local system:
-
-```shell
-scp your-user@production-database-host:/tmp/*.sql /tmp
-```
-
-This copies all the SQL files located in `/tmp` to your local system's
-`/tmp` directory. Once copied you can safely remove the files from the database
-server.
-
-## Installing Slony
-
-Use Slony to upgrade the database without requiring a long downtime.
-Slony can be downloaded from <https://www.slony.info/>. If you have installed
-PostgreSQL using your operating system's package manager you may also be able to
-install Slony using said package manager.
-
-When compiling Slony from source you *must* use the following commands to do so:
-
-```shell
-./configure --prefix=/path/to/installation/directory --with-perltools --with-pgconfigdir=/path/to/directory/containing/pg_config/bin
-make
-make install
-```
-
-Omnibus users can use the following commands:
-
-```shell
-./configure --prefix=/opt/gitlab/embedded --with-perltools --with-pgconfigdir=/opt/gitlab/embedded/bin
-make
-make install
-```
-
-This assumes you have installed GitLab into `/opt/gitlab`.
-
-To test if Slony is installed properly, run the following commands:
-
-```shell
-test -f /opt/gitlab/embedded/bin/slonik && echo 'Slony installed' || echo 'Slony not installed'
-test -f /opt/gitlab/embedded/bin/slonik_init_cluster && echo 'Slony Perl tools are available' || echo 'Slony Perl tools are not available'
-/opt/gitlab/embedded/bin/slonik -v
-```
-
-This assumes Slony was installed to `/opt/gitlab/embedded`. If Slony was
-installed properly the output of these commands is (the mentioned `slonik`
-version may be different):
-
-```plaintext
-Slony installed
-Slony Perl tools are available
-slonik version 2.2.5
-```
-
-## Slony User
-
-Next, set up a PostgreSQL user that Slony can use to replicate your
-database. To do so, sign in to your production database using `psql` using a
-super-user account. After signing in, run the following SQL queries:
-
-```sql
-CREATE ROLE slony WITH SUPERUSER LOGIN REPLICATION ENCRYPTED PASSWORD 'password string here';
-ALTER ROLE slony SET statement_timeout TO 0;
-```
-
-Make sure you replace "password string here" with an actual password for the
-user. A password is required. This user must be created on both the old and
-new database server using the same password.
-
-After creating the user, be sure to note the password, as the password is needed
-later.
-
-## Configuring Slony
-
-You can now start configuring Slony. Slony uses a configuration file for
-most of the work so it is important to set this up with care. Your configuration
-specifies where to put log files, how Slony should connect to the databases,
-etc.
-
-First, create some required directories and set the correct
-permissions. To do so, run the following commands on both the old and new
-database server:
-
-```shell
-sudo mkdir -p /var/log/gitlab/slony /var/run/slony1 /var/opt/gitlab/postgresql/slony
-sudo chown gitlab-psql:root /var/log/gitlab/slony /var/run/slony1 /var/opt/gitlab/postgresql/slony
-```
-
-Here `gitlab-psql` is the user used to run the PostgreSQL database processes. If
-you are using a different user you should replace this with the name of said
-user.
-
-Now that the directories are in place you can create the configuration file
-by using the following template:
-
-```perl
-if ($ENV{"SLONYNODES"}) {
- require $ENV{"SLONYNODES"};
-} else {
- $CLUSTER_NAME = 'slony_replication';
- $LOGDIR = '/var/log/gitlab/slony';
- $MASTERNODE = 1;
- $DEBUGLEVEL = 2;
-
- add_node(host => 'OLD_HOST', dbname => 'gitlabhq_production', port =>5432,
- user=>'slony', password=>'SLONY_PASSWORD', node=>1);
-
- add_node(host => 'NEW_HOST', dbname => 'gitlabhq_production', port =>5432,
- user=>'slony', password=>'SLONY_PASSWORD', node=>2, parent=>1 );
-}
-
-$SLONY_SETS = {
- "set1" => {
- "set_id" => 1,
- "table_id" => 1,
- "sequence_id" => 1,
- "pkeyedtables" => [
- TABLES
- ],
- },
-};
-
-if ($ENV{"SLONYSET"}) {
- require $ENV{"SLONYSET"};
-}
-
-# Please do not add or change anything below this point.
-1;
-```
-
-Replace the following placeholders in this file to use it:
-
-- `OLD_HOST`: the address of the old database server.
-- `NEW_HOST`: the address of the new database server.
-- `SLONY_PASSWORD`: the password of the Slony user created earlier.
-- `TABLES`: the tables to replicate.
-
-Generate the list of tables to replicate by running the following
-command on your old PostgreSQL database:
-
-```shell
-sudo gitlab-psql gitlabhq_production -c "select concat('\"', schemaname, '.', tablename, '\",') from pg_catalog.pg_tables where schemaname = 'public' and tableowner = 'gitlab' and tablename != 'schema_migrations' order by tablename asc;" -t
-```
-
-If you're not using Omnibus you should replace `gitlab-psql` with the
-appropriate path to the `psql` executable.
-
-The above command outputs a list of tables in a format that can be copy-pasted
-directly into the above configuration file. Make sure to _replace_ `TABLES` with
-this output, don't just append it below it. The result looks like this:
-
-```perl
-"pkeyedtables" => [
- "public.abuse_reports",
- "public.appearances",
- "public.application_settings",
- ... more rows here ...
-]
-```
-
-After you have the configuration file generated you must install it on both the
-old and new database. To do so, place it in
-`/var/opt/gitlab/postgresql/slony/slon_tools.conf` (for which you created the
-directory earlier on).
-
-Now that the configuration file is in place, you can _finally_ start replicating
-the database. First, set up the schema in the new database by making
-sure that the SQL files generated earlier are in the `/tmp`
-directory of the new server. After these files are in place start a `psql`
-session on this server:
-
-```shell
-sudo gitlab-psql gitlabhq_production
-```
-
-Now run the following commands:
-
-```plaintext
-\i /tmp/structure.sql
-\i /tmp/migrations.sql
-```
-
-To verify if the structure is in place close the session (`\q`), start it again, then
-run `\d`. If all went well you should see output along the lines of the
-following:
-
-```plaintext
- List of relations
- Schema | Name | Type | Owner
---------+---------------------------------------------+----------+-------------
- public | abuse_reports | table | gitlab
- public | abuse_reports_id_seq | sequence | gitlab
- public | appearances | table | gitlab
- public | appearances_id_seq | sequence | gitlab
- public | application_settings | table | gitlab
- public | application_settings_id_seq | sequence | gitlab
- public | approvals | table | gitlab
- ... more rows here ...
-```
-
-Now you can initialize the required tables and other processes for
-the replication process. To do so, run the following on the old database:
-
-```shell
-sudo -u gitlab-psql /opt/gitlab/embedded/bin/slonik_init_cluster --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf | /opt/gitlab/embedded/bin/slonik
-```
-
-If all went well this produces something along the lines of:
-
-```plaintext
-<stdin>:10: Set up replication nodes
-<stdin>:13: Next: configure paths for each node/origin
-<stdin>:16: Replication nodes prepared
-<stdin>:17: Please start a slon replication daemon for each node
-```
-
-Next, start a replication node on every server. To do so, run the
-following on the old database:
-
-```shell
-sudo -u gitlab-psql /opt/gitlab/embedded/bin/slon_start 1 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf
-```
-
-This should produce an output like the following:
-
-```plaintext
-Invoke slon for node 1 - /opt/gitlab/embedded/bin/slon -p /var/run/slony1/slony_replication_node1.pid -s 1000 -d2 slony_replication 'host=192.168.0.7 dbname=gitlabhq_production user=slony port=5432 password=hieng8ezohHuCeiqu0leeghai4aeyahp' > /var/log/gitlab/slony/node1/gitlabhq_production-2016-10-06.log 2>&1 &
-Slon successfully started for cluster slony_replication, node node1
-PID [26740]
-Start the watchdog process as well...
-```
-
-Next, run the following command on the _new_ database server:
-
-```shell
-sudo -u gitlab-psql /opt/gitlab/embedded/bin/slon_start 2 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf
-```
-
-This produces similar output if all went well.
-
-After Slony starts, you must tell the new database server what it should replicate. Run the following command on the _new_ database server:
-
-```shell
-sudo -u gitlab-psql /opt/gitlab/embedded/bin/slonik_create_set 1 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf | /opt/gitlab/embedded/bin/slonik
-```
-
-This should produce an output like the following:
-
-```plaintext
-<stdin>:11: Subscription set 1 (set1) created
-<stdin>:12: Adding tables to the subscription set
-<stdin>:16: Add primary keyed table public.abuse_reports
-<stdin>:20: Add primary keyed table public.appearances
-<stdin>:24: Add primary keyed table public.application_settings
-... more rows here ...
-<stdin>:327: Adding sequences to the subscription set
-<stdin>:328: All tables added
-```
-
-Finally, you can start the replication process by running the following on the
-_new_ database server:
-
-```shell
-sudo -u gitlab-psql /opt/gitlab/embedded/bin/slonik_subscribe_set 1 2 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf | /opt/gitlab/embedded/bin/slonik
-```
-
-This should produce the following output:
-
-```plaintext
-<stdin>:6: Subscribed nodes to set 1
-```
-
-At this point the new database server starts replicating the data of the old
-database server. This process can take anywhere from a few minutes to hours, if
-not days. Unfortunately Slony itself doesn't really provide a way of knowing
-when the two databases are in sync. To get an estimate of the progress you can
-use the following shell script:
-
-```shell
-#!/usr/bin/env bash
-
-set -e
-
-user='slony'
-pass='SLONY_PASSWORD'
-
-function main {
- while :
- do
- local source
- local target
-
- source=$(PGUSER="${user}" PGPASSWORD="${pass}" /opt/gitlab/embedded/bin/psql -h OLD_HOST gitlabhq_production -c "select pg_size_pretty(pg_database_size('gitlabhq_production'));" -t -A)
- target=$(PGUSER="${user}" PGPASSWORD="${pass}" /opt/gitlab/embedded/bin/psql -h NEW_HOST gitlabhq_production -c "select pg_size_pretty(pg_database_size('gitlabhq_production'));" -t -A)
-
- echo "$(date): ${target} of ${source}" >> progress.log
- echo "$(date): ${target} of ${source}"
-
- sleep 60
- done
-}
-
-main
-```
-
-This script compares the sizes of the old and new database every minute and
-prints the results to STDOUT as well as logging it to a file. Make sure to replace
-`SLONY_PASSWORD`, `OLD_HOST`, and `NEW_HOST` with the correct values.
-
-## Stopping Replication
-
-Eventually, the two databases become in sync. At this point, there is a few minutes of downtime that you must plan for before the replicated database is available. During this time, the replication process should stop and all Slony data should be removed from both databases. After the replication process finishes, GitLab can restart and is able to use the newly-replicated database.
-
-First, stop all of GitLab. Omnibus users can do so by running the
-following on their GitLab servers:
-
-```shell
-sudo gitlab-ctl stop puma
-sudo gitlab-ctl stop sidekiq
-sudo gitlab-ctl stop mailroom
-```
-
-If you have any other processes that use PostgreSQL, you should also stop those.
-
-After everything successfully stops, be sure to update any configuration settings
-and DNS records so they all point to the new database.
-
-When the configuration is complete, stop the replication
-process. It's crucial that no new data is written to the databases at this point,
-as this data is discarded.
-
-To stop replication, run the following on both database servers:
-
-```shell
-sudo -u gitlab-psql /opt/gitlab/embedded/bin/slon_kill --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf
-```
-
-This stops all the Slony processes on the host the command was executed on.
-
-## Resetting Sequences
-
-The above setup does not replicate database sequences, as such these must be
-reset manually in the target database. You can use the following script for
-this:
-
-```shell
-#!/usr/bin/env bash
-set -e
-
-function main {
- local fix_sequences
- local fix_owners
-
- fix_sequences='/tmp/fix_sequences.sql'
- fix_owners='/tmp/fix_owners.sql'
-
- # The SQL queries were taken from
- # https://wiki.postgresql.org/wiki/Fixing_Sequences
- sudo gitlab-psql gitlabhq_production -t -c "
- SELECT 'ALTER SEQUENCE '|| quote_ident(MIN(schema_name)) ||'.'|| quote_ident(MIN(seq_name))
- ||' OWNED BY '|| quote_ident(MIN(TABLE_NAME)) ||'.'|| quote_ident(MIN(column_name)) ||';'
- FROM (
- SELECT
- n.nspname AS schema_name,
- c.relname AS TABLE_NAME,
- a.attname AS column_name,
- SUBSTRING(d.adsrc FROM E'^nextval\\(''([^'']*)''(?:::text|::regclass)?\\)') AS seq_name
- FROM pg_class c
- JOIN pg_attribute a ON (c.oid=a.attrelid)
- JOIN pg_attrdef d ON (a.attrelid=d.adrelid AND a.attnum=d.adnum)
- JOIN pg_namespace n ON (c.relnamespace=n.oid)
- WHERE has_schema_privilege(n.oid,'USAGE')
- AND n.nspname NOT LIKE 'pg!_%' escape '!'
- AND has_table_privilege(c.oid,'SELECT')
- AND (NOT a.attisdropped)
- AND d.adsrc ~ '^nextval'
- ) seq
- GROUP BY seq_name HAVING COUNT(*)=1;
- " > "${fix_owners}"
-
- sudo gitlab-psql gitlabhq_production -t -c "
- SELECT 'SELECT SETVAL(' ||
- quote_literal(quote_ident(PGT.schemaname) || '.' || quote_ident(S.relname)) ||
- ', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' ||
- quote_ident(PGT.schemaname)|| '.'||quote_ident(T.relname)|| ';'
- FROM pg_class AS S,
- pg_depend AS D,
- pg_class AS T,
- pg_attribute AS C,
- pg_tables AS PGT
- WHERE S.relkind = 'S'
- AND S.oid = D.objid
- AND D.refobjid = T.oid
- AND D.refobjid = C.attrelid
- AND D.refobjsubid = C.attnum
- AND T.relname = PGT.tablename
- ORDER BY S.relname;
- " > "${fix_sequences}"
-
- sudo gitlab-psql gitlabhq_production -f "${fix_owners}"
- sudo gitlab-psql gitlabhq_production -f "${fix_sequences}"
-
- rm "${fix_owners}" "${fix_sequences}"
-}
-
-main
-```
-
-Upload this script to the _target_ server and execute it as follows:
-
-```shell
-sudo bash path/to/the/script/above.sh
-```
-
-This corrects the ownership of sequences and reset the next value for the
-`id` column to the next available value.
-
-## Removing Slony
-
-The final step is to remove all Slony related data. To do so, run the following
-command on the _target_ server:
-
-```shell
-sudo gitlab-psql gitlabhq_production -c "DROP SCHEMA _slony_replication CASCADE;"
-```
-
-Once done you can safely remove any Slony related files (for example, the log
-directory), and uninstall Slony if desired. At this point you can start your
-GitLab instance again and if all went well it should be using your new database
-server.
+This content was removed in GitLab 15.7.
+Patroni has been used for database replication since GitLab 14.0. To perform upgrades, use the [Patroni replication documentation](../administration/postgresql/replication_and_failover.md) instead.
diff --git a/doc/update/with_downtime.md b/doc/update/with_downtime.md
index dfe64a3c2a9..2ad928927a4 100644
--- a/doc/update/with_downtime.md
+++ b/doc/update/with_downtime.md
@@ -6,10 +6,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Multi-node upgrades with downtime **(FREE SELF)**
-NOTE:
-This process is a work in progress. You're welcome to provide feedback by either raising a ticket to support,
-or [commenting on this issue](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6244).
-
While you can upgrade a multi-node GitLab deployment [with zero downtime](zero_downtime.md),
there are a number of constraints. In particular, you can upgrade to only one minor release
at a time, for example, from 14.6 to 14.7, then to 14.8, etc.
@@ -37,9 +33,6 @@ At a high level, the process is:
substitute the instructions for Omnibus GitLab with your cloud provider's instructions.
1. Upgrade the GitLab application (Sidekiq, Puma) and start the application up.
-If you are a Community Edition user, replace `gitlab-ee` with
-`gitlab-ce` in the following commands.
-
## Stop writes to the database
Shut down Puma and Sidekiq on all servers running these processes:
@@ -56,16 +49,7 @@ sudo gitlab-ctl stop puma
In summary:
1. Check the Consul nodes are all healthy.
-1. Upgrade the GitLab package on all your Consul servers:
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
-
+1. [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories) on all your Consul servers.
1. Restart all GitLab services **one node at a time**:
```shell
@@ -106,15 +90,7 @@ The Praefect nodes, however, can be upgraded via an AMI redeployment process:
## Upgrade the Gitaly nodes not part of Gitaly cluster
-For Gitaly servers which are not part of Gitaly cluster, update the GitLab package:
-
-```shell
-# Debian/Ubuntu
-sudo apt-get update && sudo apt-get install gitlab-ee
-
-# Centos/RHEL
-sudo yum install gitlab-ee
-```
+For Gitaly servers which are not part of Gitaly cluster, [upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
If you have multiple Gitaly shards or have multiple load-balanced Gitaly nodes
using NFS, it doesn't matter in which order you upgrade the Gitaly servers.
@@ -123,15 +99,7 @@ using NFS, it doesn't matter in which order you upgrade the Gitaly servers.
For unclustered PostgreSQL servers:
-1. Upgrade the GitLab package:
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
+1. [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
1. The upgrade process does not restart PostgreSQL when the binaries are upgraded.
Restart to load the new version:
@@ -161,15 +129,7 @@ Follow the following process:
sudo gitlab-ctl patroni members
```
-1. Upgrade the GitLab package on one of the replica nodes:
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
+1. [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories) on one of the replica nodes.
1. Restart to load the new version:
@@ -194,27 +154,11 @@ Follow the following process:
If you run PgBouncer on your Rails (application) nodes, then
PgBouncer are upgraded as part of the application server upgrade.
-Upgrade the PgBouncer nodes:
-
-```shell
-# Debian/Ubuntu
-sudo apt-get update && sudo apt-get install gitlab-ee
-
-# Centos/RHEL
-sudo yum install gitlab-ee
-```
+[Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories) on the PgBouncer nodes.
## Upgrade the Redis node
-Upgrade a standalone Redis server by updating the GitLab package:
-
-```shell
-# Debian/Ubuntu
-sudo apt-get update && sudo apt-get install gitlab-ee
-
-# Centos/RHEL
-sudo yum install gitlab-ee
-```
+Upgrade a standalone Redis server by [upgrading the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
## Upgrade Redis HA (using Sentinel) **(PREMIUM SELF)**
@@ -269,15 +213,7 @@ running all database migrations. On the deploy node:
sudo gitlab-ctl reconfigure
```
-1. Upgrade the GitLab package:
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
+1. [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
1. If you modified `gitlab.rb` on the deploy node to bypass PgBouncer:
1. Update `gitlab.rb` on the deploy node. Change `gitlab_rails['db_host']`
@@ -300,15 +236,7 @@ set to anything in `gitlab.rb` on these nodes.
They can be upgraded in parallel:
-1. Upgrade the GitLab package:
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
+1. [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
1. Ensure all services are restarted:
@@ -318,12 +246,4 @@ They can be upgraded in parallel:
## Upgrade the Monitor node
-Upgrade the GitLab package:
-
-```shell
-# Debian/Ubuntu
-sudo apt-get update && sudo apt-get install gitlab-ee
-
-# Centos/RHEL
-sudo yum install gitlab-ee
-```
+[Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
diff --git a/doc/update/zero_downtime.md b/doc/update/zero_downtime.md
index eb1d6cef606..deda12145da 100644
--- a/doc/update/zero_downtime.md
+++ b/doc/update/zero_downtime.md
@@ -54,12 +54,12 @@ Certain major/minor releases may require a set of background migrations to be
finished. To guarantee this, such a release processes any remaining jobs
before continuing the upgrading procedure. While this doesn't require downtime
(if the above conditions are met) we require that you
-[wait for background migrations to complete](index.md#checking-for-background-migrations-before-upgrading)
+[wait for background migrations to complete](background_migrations.md)
between each major/minor release upgrade.
The time necessary to complete these migrations can be reduced by
increasing the number of Sidekiq workers that can process jobs in the
`background_migration` queue. To see the size of this queue,
-[Check for background migrations before upgrading](index.md#checking-for-background-migrations-before-upgrading).
+[Check for background migrations before upgrading](background_migrations.md).
As a guideline, any database smaller than 10 GB doesn't take too much time to
upgrade; perhaps an hour at most per minor release. Larger databases however may
@@ -132,18 +132,7 @@ load balancer to latest GitLab version.
sudo touch /etc/gitlab/skip-auto-reconfigure
```
- 1. Update the GitLab package:
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
-
- If you are a Community Edition user, replace `gitlab-ee` with
- `gitlab-ce` in the above command.
+ 1. [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
1. Get the regular migrations and latest code in place. Before running this step,
the deploy node's `/etc/gitlab/gitlab.rb` configuration file must have
@@ -193,17 +182,7 @@ Before you update the main GitLab application you must (in order):
#### Upgrade Gitaly nodes
-Upgrade the Gitaly nodes one at a time to ensure access to Git repositories is maintained:
-
-```shell
-# Debian/Ubuntu
-sudo apt-get update && sudo apt-get install gitlab-ee
-
-# Centos/RHEL
-sudo yum install gitlab-ee
-```
-
-If you are a Community Edition user, replace `gitlab-ee` with `gitlab-ce` in the above command.
+[Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories) on the Gitaly nodes one at a time to ensure access to Git repositories is maintained.
#### Upgrade Praefect
@@ -226,17 +205,7 @@ node first and run database migrations.
1. On the **Praefect deploy node**:
- 1. Upgrade the GitLab package:
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
-
- If you are a Community Edition user, replace `gitlab-ee` with `gitlab-ce` in the command above.
+ 1. [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
1. To apply the Praefect database migrations and restart Praefect, run:
@@ -246,13 +215,7 @@ node first and run database migrations.
1. On all **remaining Praefect nodes**:
- 1. Upgrade the GitLab package:
-
- ```shell
- sudo apt-get update && sudo apt-get install gitlab-ee
- ```
-
- If you are a Community Edition user, replace `gitlab-ee` with `gitlab-ce` in the command above.
+ 1. [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
1. Ensure nodes are running the latest code:
@@ -279,17 +242,7 @@ node throughout the process.
**PostgreSQL only nodes**
-- Update the GitLab package
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
-
- If you are a Community Edition user, replace `gitlab-ee` with `gitlab-ce` in the above command.
+- [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
- Ensure nodes are running the latest code
@@ -299,17 +252,7 @@ node throughout the process.
**Deploy node**
-- Update the GitLab package
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
-
- If you are a Community Edition user, replace `gitlab-ee` with `gitlab-ce` in the above command.
+- [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
- If you're using PgBouncer:
@@ -341,13 +284,7 @@ node throughout the process.
**All nodes _excluding_ the Deploy node**
-- Update the GitLab package
-
- ```shell
- sudo apt-get update && sudo apt-get install gitlab-ee
- ```
-
- If you are a Community Edition user, replace `gitlab-ee` with `gitlab-ce` in the above command.
+- [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
- Ensure nodes are running the latest code
@@ -514,15 +451,7 @@ Log in to your **primary** node, executing the following:
sudo gitlab-ctl reconfigure
```
-1. Update the GitLab package:
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
+1. [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
1. To get the database migrations and latest code in place, run:
@@ -552,15 +481,7 @@ On each **secondary** node, executing the following:
sudo gitlab-ctl reconfigure
```
-1. Update the GitLab package:
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
+1. [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
1. To get the database migrations and latest code in place, run:
@@ -669,15 +590,7 @@ sudo touch /etc/gitlab/skip-auto-reconfigure
**On primary Gitaly only nodes**
-1. Update the GitLab package
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
+1. [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
1. Ensure nodes are running the latest code
@@ -687,15 +600,7 @@ sudo touch /etc/gitlab/skip-auto-reconfigure
**On the primary "deploy node"**
-1. Update the GitLab package
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
+1. [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
1. If you're using PgBouncer:
@@ -737,15 +642,7 @@ sudo touch /etc/gitlab/skip-auto-reconfigure
**On all primary nodes _excluding_ the primary "deploy node"**
-1. Update the GitLab package
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
+1. [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
1. Ensure nodes are running the latest code
@@ -784,15 +681,7 @@ sudo touch /etc/gitlab/skip-auto-reconfigure
**On secondary Gitaly only nodes**
-1. Update the GitLab package
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
+1. [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
1. Ensure nodes are running the latest code
@@ -802,15 +691,7 @@ sudo touch /etc/gitlab/skip-auto-reconfigure
**On the secondary "deploy node"**
-1. Update the GitLab package
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
+1. [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
1. To get the regular database migrations and latest code in place, run
@@ -837,15 +718,7 @@ sudo touch /etc/gitlab/skip-auto-reconfigure
**On all secondary nodes _excluding_ the secondary "deploy node"**
-1. Update the GitLab package
-
- ```shell
- # Debian/Ubuntu
- sudo apt-get update && sudo apt-get install gitlab-ee
-
- # Centos/RHEL
- sudo yum install gitlab-ee
- ```
+1. [Upgrade the GitLab package](package/index.md#upgrade-to-a-specific-version-using-the-official-repositories).
1. Ensure nodes are running the latest code
diff --git a/doc/user/admin_area/external_users.md b/doc/user/admin_area/external_users.md
new file mode 100644
index 00000000000..8b968a3da01
--- /dev/null
+++ b/doc/user/admin_area/external_users.md
@@ -0,0 +1,77 @@
+---
+stage: Manage
+group: Authentication and Authorization
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# External users **(FREE SELF)**
+
+In cases where it is desired that a user has access only to some internal or
+private projects, there is the option of creating **External Users**. This
+feature may be useful when for example a contractor is working on a given
+project and should only have access to that project.
+
+External users:
+
+- Cannot create project, groups, and snippets in their personal namespaces.
+- Can only create projects (including forks), subgroups, and snippets within top-level groups to which they are explicitly granted access.
+- Can only access public projects and projects to which they are explicitly granted access,
+ thus hiding all other internal or private ones from them (like being
+ logged out).
+- Can only access public groups and groups to which they are explicitly granted access,
+ thus hiding all other internal or private ones from them (like being
+ logged out).
+- Can only access public snippets.
+
+Access can be granted by adding the user as member to the project or group.
+Like usual users, they receive a role in the project or group with all
+the abilities that are mentioned in the [permissions table](../permissions.md#project-members-permissions).
+For example, if an external user is added as Guest, and your project is internal or
+private, they do not have access to the code; you need to grant the external
+user access at the Reporter level or above if you want them to have access to the code. You should
+always take into account the
+[project's visibility and permissions settings](../project/settings/index.md#configure-project-visibility-features-and-permissions)
+as well as the permission level of the user.
+
+NOTE:
+External users still count towards a license seat.
+
+An administrator can flag a user as external by either of the following methods:
+
+- [Through the API](../../api/users.md#user-modification).
+- Using the GitLab UI:
+ 1. On the top bar, select **Main menu > Admin**.
+ 1. On the left sidebar, select **Overview > Users** to create a new user or edit an existing one.
+ There, you can find the option to flag the user as external.
+
+Additionally, users can be set as external users using:
+
+- [SAML groups](../../integration/saml.md#external-groups).
+- [LDAP groups](../../administration/auth/ldap/ldap_synchronization.md#external-groups).
+
+## Set a new user to external
+
+By default, new users are not set as external users. This behavior can be changed
+by an administrator:
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Settings > General**.
+1. Expand the **Account and limit** section.
+
+If you change the default behavior of creating new users as external, you
+have the option to narrow it down by defining a set of internal users.
+The **Internal users** field allows specifying an email address regex pattern to
+identify default internal users. New users whose email address matches the regex
+pattern are set to internal by default rather than an external collaborator.
+
+The regex pattern format is in Ruby, but it needs to be convertible to JavaScript,
+and the ignore case flag is set (`/regex pattern/i`). Here are some examples:
+
+- Use `\.internal@domain\.com$` to mark email addresses ending with
+ `.internal@domain.com` as internal.
+- Use `^(?:(?!\.ext@domain\.com).)*$\r?` to mark users with email addresses
+ not including `.ext@domain.com` as internal.
+
+WARNING:
+Be aware that this regex could lead to a
+[regular expression denial of service (ReDoS) attack](https://en.wikipedia.org/wiki/ReDoS).
diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md
index 4c34d82dc02..c9b6a077c73 100644
--- a/doc/user/admin_area/index.md
+++ b/doc/user/admin_area/index.md
@@ -163,7 +163,7 @@ You can impersonate a user in the following ways:
1. Select **Impersonate**.
- With the API, using [impersonation tokens](../../api/index.md#impersonation-tokens).
-All impersonation activities are [captured with audit events](../../administration/audit_events.md#impersonation-data).
+All impersonation activities are [captured with audit events](../../administration/audit_events.md#user-impersonation).
By default, impersonation is enabled. GitLab can be configured to [disable impersonation](../../api/index.md#disable-impersonation).
diff --git a/doc/user/admin_area/license_file.md b/doc/user/admin_area/license_file.md
index d821d9bab23..9c35a8c1269 100644
--- a/doc/user/admin_area/license_file.md
+++ b/doc/user/admin_area/license_file.md
@@ -25,7 +25,8 @@ Otherwise, to add your license:
1. Select **Add license**.
NOTE:
-In GitLab 14.1.x through 14.10.x, you can access the **Add License** page directly from the URL, `<YourGitLabURL>/admin/license/new`. In GitLab 15.0 and later, the path is `<YourGitLabURL>/admin/subscription`.
+In GitLab 14.7.x to 14.9.x, you can add the license file with the UI.
+In GitLab 14.1.x to 14.7, if you have already activated your subscription with an activation code, you cannot access **Add License** from the Admin Area. You must access **Add License** directly from the URL, `<YourGitLabURL>/admin/license/new`.
## Add your license file during installation
diff --git a/doc/user/admin_area/monitoring/background_migrations.md b/doc/user/admin_area/monitoring/background_migrations.md
index f16cb219f2b..b4a6f7f66fb 100644
--- a/doc/user/admin_area/monitoring/background_migrations.md
+++ b/doc/user/admin_area/monitoring/background_migrations.md
@@ -1,244 +1,11 @@
---
-stage: Data Stores
-group: Database
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+redirect_to: '../../../update/background_migrations.md'
+remove_date: '2023-03-11'
---
-# Batched background migrations **(FREE SELF)**
+This document was moved to [another location](../../../update/background_migrations.md).
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51332) in GitLab 13.11.
-> - [Deployed behind a feature flag](../../../user/feature_flags.md), disabled by default.
-> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/329511) in GitLab 13.12.
-> - Enabled on GitLab.com.
-> - Recommended for production use.
-> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-batched-background-migrations).
-
-There can be [risks when disabling released features](../../../administration/feature_flags.md#risks-when-disabling-released-features).
-Refer to this feature's version history for more details.
-
-To update database tables in batches, GitLab can use batched background migrations. These migrations
-are created by GitLab developers and run automatically on upgrade. However, such migrations are
-limited in scope to help with migrating some `integer` database columns to `bigint`. This is needed to
-prevent integer overflow for some tables.
-
-## Check the status of background migrations
-
-All migrations must have a `Finished` status before you [upgrade GitLab](../../../update/index.md).
-You can [check the status of existing migrations](../../../update/index.md#batched-background-migrations).
-
-## Enable or disable batched background migrations
-
-WARNING:
-If you disable this feature flag, GitLab upgrades may fail.
-
-Batched background migrations are under development but ready for production use.
-It is deployed behind a feature flag that is **enabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
-can opt to disable it.
-
-To enable it:
-
-```ruby
-Feature.enable(:execute_batched_migrations_on_schedule)
-```
-
-To disable it:
-
-```ruby
-Feature.disable(:execute_batched_migrations_on_schedule)
-```
-
-### Pause batched background migrations in GitLab 14.x
-
-To pause an ongoing batched background migration, use the `disable` command above.
-This command causes the migration to complete the current batch, and then wait to start the next batch.
-
-Use the following database queries to see the state of the current batched background migration:
-
-1. Obtain the ID of the running migration:
-
- ```sql
- SELECT
- id,
- job_class_name,
- table_name,
- column_name,
- job_arguments
- FROM batched_background_migrations
- WHERE status <> 3;
- ```
-
-1. Run this query, replacing `XX` with the ID you obtained in the previous step,
- to see the status of the migration:
-
- ```sql
- SELECT
- started_at,
- finished_at,
- finished_at - started_at AS duration,
- min_value,
- max_value,
- batch_size,
- sub_batch_size
- FROM batched_background_migration_jobs
- WHERE batched_background_migration_id = XX
- ORDER BY id DESC
- limit 10;
- ```
-
-1. Run the query multiple times within a few minutes to ensure no new row has been added.
- If no new row has been added, the migration has been paused.
-
-1. After confirming the migration has paused, restart the migration (using the `enable`
- command above) to proceed with the batch when ready. On larger instances,
- background migrations can take as long as 48 hours to complete each batch.
-
-## Automatic batch size optimization
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60133) in GitLab 13.12.
-> - [Deployed behind a feature flag](../../../user/feature_flags.md), disabled by default.
-> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/329511) in GitLab 13.12.
-> - Enabled on GitLab.com.
-> - Recommended for production use.
-> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-automatic-batch-size-optimization).
-
-There can be [risks when disabling released features](../../../administration/feature_flags.md#risks-when-disabling-released-features).
-Refer to this feature's version history for more details.
-
-To maximize throughput of batched background migrations (in terms of the number of tuples updated per time unit), batch sizes are automatically adjusted based on how long the previous batches took to complete.
-
-## Enable or disable automatic batch size optimization
-
-Automatic batch size optimization for batched background migrations is under development but ready for production use.
-It is deployed behind a feature flag that is **enabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
-can opt to disable it.
-
-To enable it:
-
-```ruby
-Feature.enable(:optimize_batched_migrations)
-```
-
-To disable it:
-
-```ruby
-Feature.disable(:optimize_batched_migrations)
-```
-
-## Troubleshooting
-
-### Database migrations failing because of batched background migration not finished
-
-When updating to GitLab 14.2 or later there might be a database migration failing with a message like:
-
-```plaintext
-StandardError: An error has occurred, all later migrations canceled:
-
-Expected batched background migration for the given configuration to be marked as 'finished', but it is 'active':
- {:job_class_name=>"CopyColumnUsingBackgroundMigrationJob", :table_name=>"push_event_payloads", :column_name=>"event_id", :job_arguments=>[["event_id"], ["event_id_convert_to_bigint"]]}
-```
-
-First, check if you have followed the [version-specific upgrade instructions for 14.2](../../../update/index.md#1420).
-If you have, you can [manually finish the batched background migration](#manually-finishing-a-batched-background-migration).
-If you haven't, choose one of the following methods:
-
-1. [Rollback and upgrade](#roll-back-and-follow-the-required-upgrade-path) through one of the required
-versions before updating to 14.2+.
-1. [Roll forward](#roll-forward-and-finish-the-migrations-on-the-upgraded-version), staying on the current
-version and manually ensuring that the batched migrations complete successfully.
-
-#### Roll back and follow the required upgrade path
-
-1. [Rollback and restore the previously installed version](../../../update/restore_after_failure.md#roll-back-to-an-earlier-version-and-restore-a-backup)
-1. Update to either 14.0.5 or 14.1 **before** updating to 14.2+
-1. [Check the status](#check-the-status-of-background-migrations) of the batched background migrations and
-make sure they are all marked as finished before attempting to upgrade again. If any remain marked as active,
-you can [manually finish them](#manually-finishing-a-batched-background-migration).
-
-#### Roll forward and finish the migrations on the upgraded version
-
-##### For a deployment with downtime
-
-To run all the batched background migrations, it can take a significant amount of time
-depending on the size of your GitLab installation.
-
-1. [Check the status](#check-the-status-of-background-migrations) of the batched background migrations in the
-database, and [manually run them](#manually-finishing-a-batched-background-migration) with the appropriate
-arguments until the status query returns no rows.
-1. When the status of all of all them is marked as complete, re-run migrations for your installation.
-1. [Complete the database migrations](../../../administration/raketasks/maintenance.md#run-incomplete-database-migrations) from your GitLab upgrade:
-
- ```plaintext
- sudo gitlab-rake db:migrate
- ```
-
-1. Run a reconfigure:
-
- ```plaintext
- sudo gitlab-ctl reconfigure
- ```
-
-1. Finish the upgrade for your installation.
-
-##### For a no-downtime deployment
-
-As the failing migrations are post-deployment migrations, you can remain on a running instance of the upgraded
-version and wait for the batched background migrations to finish normally.
-
-1. [Check the status](#check-the-status-of-background-migrations) of the batched background migration from
-the error message, and make sure it is listed as finished. If it is still active, either wait until it is done,
-or [manually finish it](#manually-finishing-a-batched-background-migration).
-1. Re-run migrations for your installation, so the remaining post-deployment migrations finish.
-
-### Manually finishing a batched background migration
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62634) in GitLab 14.1
-
-If you need to manually finish a batched background migration due to an
-error, you can run:
-
-```shell
-sudo gitlab-rake gitlab:background_migrations:finalize[<job_class_name>,<table_name>,<column_name>,'<job_arguments>']
-```
-
-Replace the values in angle brackets with the correct
-arguments. For example, if you receive an error similar to this:
-
-```plaintext
-StandardError: An error has occurred, all later migrations canceled:
-
-Expected batched background migration for the given configuration to be marked as 'finished', but it is 'active':
- {:job_class_name=>"CopyColumnUsingBackgroundMigrationJob", :table_name=>"push_event_payloads", :column_name=>"event_id", :job_arguments=>[["event_id"], ["event_id_convert_to_bigint"]]}
-```
-
-Plug the arguments from the error message into the command:
-
-```shell
-sudo gitlab-rake gitlab:background_migrations:finalize[CopyColumnUsingBackgroundMigrationJob,push_event_payloads,event_id,'[["event_id"]\, ["event_id_convert_to_bigint"]]']
-```
-
-If you need to manually run a batched background migration to continue an upgrade, you can
-[check the status](#check-the-status-of-background-migrations) in the database and get the
-arguments from the query results. For example, if the query returns this:
-
-```plaintext
- job_class_name | table_name | column_name | job_arguments
----------------------------------------+------------+-------------+------------------------------------
- CopyColumnUsingBackgroundMigrationJob | events | id | [["id"], ["id_convert_to_bigint"]]
- ```
-
-The results from the query can be plugged into the command:
-
-```shell
-sudo gitlab-rake gitlab:background_migrations:finalize[CopyColumnUsingBackgroundMigrationJob,events,id,'[["id"]\, ["id_convert_to_bigint"]]']
-```
-
-### The `BackfillNamespaceIdForNamespaceRoute` batched migration job fails
-
-In GitLab 14.8, the `BackfillNamespaceIdForNamespaceRoute` batched background migration job
-may fail to complete. When retried, a `500 Server Error` is returned. This issue was
-[resolved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82387) in GitLab 14.9.
-
-To resolve this issue, [upgrade GitLab](../../../update/index.md) from 14.8 to 14.9.
-You can ignore the failed batch migration until after you update to GitLab 14.9.
+<!-- This redirect file can be deleted after <2023-03-11>. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/user/admin_area/monitoring/health_check.md b/doc/user/admin_area/monitoring/health_check.md
index 2939a8b0418..668d34af024 100644
--- a/doc/user/admin_area/monitoring/health_check.md
+++ b/doc/user/admin_area/monitoring/health_check.md
@@ -133,7 +133,7 @@ This check is being exempt from Rack Attack.
## Sidekiq
-Learn how to configure the [Sidekiq health checks](../../../administration/sidekiq_health_check.md).
+Learn how to configure the [Sidekiq health checks](../../../administration/sidekiq/sidekiq_health_check.md).
<!-- ## Troubleshooting
diff --git a/doc/user/admin_area/settings/account_and_limit_settings.md b/doc/user/admin_area/settings/account_and_limit_settings.md
index 44a6bbb9d8e..b235b812416 100644
--- a/doc/user/admin_area/settings/account_and_limit_settings.md
+++ b/doc/user/admin_area/settings/account_and_limit_settings.md
@@ -57,7 +57,7 @@ You can change the maximum push size for your instance:
1. On the left sidebar, select **Settings > General**, then expand **Account and limit**.
1. Increase or decrease by changing the value in **Maximum push size (MB)**.
-For GitLab.com application limits, read [GitLab application limits](../../../administration/instance_limits.md#max-push-size).
+For GitLab.com push size limits, read [accounts and limit settings](../../gitlab_com/index.md#account-and-limit-settings).
NOTE:
When you [add files to a repository](../../project/repository/web_editor.md#create-a-file)
diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md
index adca9c85af1..81e51aaef37 100644
--- a/doc/user/admin_area/settings/continuous_integration.md
+++ b/doc/user/admin_area/settings/continuous_integration.md
@@ -46,7 +46,10 @@ limit on the number of [CI/CD minutes](../../../ci/pipelines/cicd_minutes.md) yo
## Enable a specific runner for multiple projects
-To enable a specific runner for one or more projects:
+If you have already registered a [specific runner](../../../ci/runners/runners_scope.md#specific-runners)
+you can assign that runner to other projects.
+
+To enable a specific runner for more than one project:
1. On the top bar, select **Main menu > Admin**.
1. From the left sidebar, select **Overview > Runners**.
@@ -326,7 +329,7 @@ To set the maximum file size:
1. Enter the maximum file size, in bytes.
1. Select **Save size limits**.
-## Prevent users from registering runners
+## Restrict runner registration by all users in an instance
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22225) in GitLab 14.1.
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/368008) in GitLab 15.5.
@@ -335,7 +338,7 @@ GitLab administrators can adjust who is allowed to register runners, by showing
By default, all members of a project and group are able to register runners.
-To change this:
+To restrict all users in an instance from registering runners:
1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
@@ -347,6 +350,22 @@ To change this:
WARNING:
When the registration sections are hidden in the UI, members of the project or group that need to register runners must contact the administrators. If you plan to prevent registration, ensure users have access to the runners they need to run jobs.
+## Restrict runner registration by all members in a group
+
+Prerequisites:
+
+- Runner registration must be enabled for [all users in the instance](#restrict-runner-registration-by-all-users-in-an-instance).
+
+GitLab administrators can adjust group permissions to restrict runner registration by group members.
+
+To restrict runner registration by members in a specific group:
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Overview > Groups** and find your group.
+1. Select **Edit**.
+1. Clear the **New group runners can be registered** checkbox if you want to disable runner registration by all members in the group. If the setting is read-only, you must enable runner registration for the [instance](#restrict-runner-registration-by-all-users-in-an-instance).
+1. Select **Save changes**.
+
## Troubleshooting
### 413 Request Entity Too Large
diff --git a/doc/user/admin_area/settings/img/mirror_settings.png b/doc/user/admin_area/settings/img/mirror_settings.png
deleted file mode 100644
index 090db6808a7..00000000000
--- a/doc/user/admin_area/settings/img/mirror_settings.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/admin_area/settings/img/mirror_settings_v15_7.png b/doc/user/admin_area/settings/img/mirror_settings_v15_7.png
new file mode 100644
index 00000000000..5da41dbeceb
--- /dev/null
+++ b/doc/user/admin_area/settings/img/mirror_settings_v15_7.png
Binary files differ
diff --git a/doc/user/admin_area/settings/index.md b/doc/user/admin_area/settings/index.md
index 08c4a0d0167..b2b702bde7c 100644
--- a/doc/user/admin_area/settings/index.md
+++ b/doc/user/admin_area/settings/index.md
@@ -200,3 +200,12 @@ for the entire GitLab instance:
1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Scroll to the **Localization** section, and select your desired first day of the week.
+
+## Default language
+
+You can change the [Default language](../../profile/preferences.md)
+for the entire GitLab instance:
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Settings > Preferences**.
+1. Scroll to the **Localization** section, and select your desired default language.
diff --git a/doc/user/admin_area/settings/sidekiq_job_limits.md b/doc/user/admin_area/settings/sidekiq_job_limits.md
index 7a437d877ca..a25bc0e85e4 100644
--- a/doc/user/admin_area/settings/sidekiq_job_limits.md
+++ b/doc/user/admin_area/settings/sidekiq_job_limits.md
@@ -9,7 +9,7 @@ type: reference
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68982) in GitLab 14.3.
-[Sidekiq](../../../administration/sidekiq.md) jobs get stored in
+[Sidekiq](../../../administration/sidekiq/index.md) jobs get stored in
Redis. To avoid excessive memory for Redis, we:
- Compress job arguments before storing them in Redis.
diff --git a/doc/user/admin_area/settings/sign_in_restrictions.md b/doc/user/admin_area/settings/sign_in_restrictions.md
index 4ea420d7ca6..6ec3d082114 100644
--- a/doc/user/admin_area/settings/sign_in_restrictions.md
+++ b/doc/user/admin_area/settings/sign_in_restrictions.md
@@ -68,7 +68,7 @@ For more information, see the [list of settings that can be accessed through API
Open the [Rails console](../../../administration/operations/rails_console.md) and run the following:
```ruby
-::Gitlab::CurrentSettings.update_attributes!(admin_mode: true)
+::Gitlab::CurrentSettings.update!(admin_mode true)
```
#### Use the UI to enable Admin Mode
@@ -84,7 +84,7 @@ To enable Admin Mode through the UI:
To turn on Admin Mode for your current session and access potentially dangerous resources:
-1. On the top bar, select **Enable Admin Mode**.
+1. On the top bar, select **Main menu > Enter Admin Mode**.
1. Try to access any part of the UI with `/admin` in the URL (which requires administrator access).
When Admin Mode status is disabled or turned off, administrators cannot access resources unless
@@ -95,7 +95,11 @@ if they try to open a private group or project, unless they are members of that
authentication are supported by Admin Mode. Admin Mode status is stored in the current user session and remains active until either:
- It is explicitly disabled.
-- It is disabled automatically after a timeout.
+- It is disabled automatically after six hours.
+
+### Turn off Admin Mode for your session
+
+To turn off Admin Mode for your current session, on the top bar, select **Main menu > Leave Admin mode**.
### Limitations of Admin Mode
diff --git a/doc/user/admin_area/settings/sign_up_restrictions.md b/doc/user/admin_area/settings/sign_up_restrictions.md
index 76415596dce..8aabe503065 100644
--- a/doc/user/admin_area/settings/sign_up_restrictions.md
+++ b/doc/user/admin_area/settings/sign_up_restrictions.md
@@ -60,7 +60,7 @@ To enforce confirmation of the email address used for new sign ups:
1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**, and expand **Sign-up restrictions**.
-1. Select the **Send confirmation email on sign-up** checkbox, then select **Save changes**.
+1. Under **Email confirmation settings**, select **Hard**.
## User cap
@@ -190,6 +190,12 @@ To disable it:
Feature.disable(:soft_email_confirmation)
```
+## Set up LDAP user filter
+
+You can limit GitLab access to a subset of the LDAP users on your LDAP server.
+
+See the [documentation on setting up an LDAP user filter](../../../administration/auth/ldap/index.md#set-up-ldap-user-filter) for more information.
+
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
diff --git a/doc/user/admin_area/settings/terraform_limits.md b/doc/user/admin_area/settings/terraform_limits.md
new file mode 100644
index 00000000000..4e54c7a3459
--- /dev/null
+++ b/doc/user/admin_area/settings/terraform_limits.md
@@ -0,0 +1,27 @@
+---
+stage: Configure
+group: Configure
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+type: reference
+---
+
+# Terraform limits **(FREE SELF)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352951) in GitLab 15.7.
+
+You can limit the total storage of [Terraform state files](../../../administration/terraform_state.md).
+The limit applies to each individual
+state file version, and is checked whenever a new version is created.
+
+To add a storage limit:
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Settings > Preferences**.
+1. Expand **Terraform limits**.
+1. Adjust the size limit.
+
+## Available settings
+
+| Setting | Default | Description |
+|------------------------------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Terraform state size limit (bytes) | 0 | Terraform state files that exceed this size are not saved, and associated Terraform operations are rejected. Set to 0 to allow files of unlimited size. |
diff --git a/doc/user/admin_area/settings/visibility_and_access_controls.md b/doc/user/admin_area/settings/visibility_and_access_controls.md
index 6d878bcb01c..4da0f5da3f4 100644
--- a/doc/user/admin_area/settings/visibility_and_access_controls.md
+++ b/doc/user/admin_area/settings/visibility_and_access_controls.md
@@ -219,7 +219,7 @@ If only one protocol is enabled:
- The project page shows only the allowed protocol's URL, with no option to
change it.
-- GitLab shows a tooltip when you hover over the URL's protocol, if user action
+- GitLab shows a tooltip when you hover over the protocol for the URL, if user action
(such as adding a SSH key or setting a password) is required:
![Project URL with SSH only access](img/restricted_url.png)
@@ -273,7 +273,7 @@ This option is enabled by default. By disabling it, both
[pull mirroring](../../project/repository/mirror/pull.md) and [push mirroring](../../project/repository/mirror/push.md) no longer
work in every repository. They can only be re-enabled by an administrator user on a per-project basis.
-![Mirror settings](img/mirror_settings.png)
+![Mirror settings](img/mirror_settings_v15_7.png)
## Configure globally-allowed IP address ranges
diff --git a/doc/user/analytics/dora_metrics.md b/doc/user/analytics/dora_metrics.md
index b5f37203817..fba0d0e98ff 100644
--- a/doc/user/analytics/dora_metrics.md
+++ b/doc/user/analytics/dora_metrics.md
@@ -104,7 +104,7 @@ To retrieve metrics for change failure rate, use the [GraphQL](../../api/graphql
### Insights: Custom DORA reporting
-Custom charts to visualize DORA data with Insights YAML-based reports.
+Custom charts to visualize DORA data with [Insights YAML-based reports](../../user/project/insights/index.md#dora-query-parameters).
With this new visualization, software leaders can track metrics improvements, understand patterns in their metrics trends, and compare performance between groups and projects.
@@ -113,7 +113,7 @@ With this new visualization, software leaders can track metrics improvements, un
Deployment frequency is calculated based on the deployments record, which is created for typical push-based deployments.
These deployment records are not created for pull-based deployments, for example when Container Images are connected to GitLab with an agent.
-To track DORA metrics in these cases, you can [create a deployment record](../../api/deployments.md#create-a-deployment) using the Deployments API.
+To track DORA metrics in these cases, you can [create a deployment record](../../api/deployments.md#create-a-deployment) using the Deployments API. See also the documentation page for [Track deployments of an external deployment tool](../../ci/environments/external_deployment_tools.md).
### Supported DORA metrics in GitLab
diff --git a/doc/user/analytics/index.md b/doc/user/analytics/index.md
index 01fe466755c..38d92180c7d 100644
--- a/doc/user/analytics/index.md
+++ b/doc/user/analytics/index.md
@@ -12,7 +12,7 @@ Instance-level analytics make it possible to aggregate analytics across
GitLab, so that users can view information across multiple projects and groups
in one place.
-[Learn more about instance-level analytics](../admin_area/analytics/index.md).
+For more information, see [instance-level analytics](../admin_area/analytics/index.md).
## Group-level analytics
diff --git a/doc/user/analytics/value_stream_analytics.md b/doc/user/analytics/value_stream_analytics.md
index ab68c897da8..0906f7d17a7 100644
--- a/doc/user/analytics/value_stream_analytics.md
+++ b/doc/user/analytics/value_stream_analytics.md
@@ -204,7 +204,7 @@ Value stream analytics records the following times for each stage:
- **Review**: 14:00 to 19:00: 5 hrs
- **Staging**: 19:00 to 19:30: 30 minutes
-There are some additional considerations for this example:
+Keep in mind the following observations related to this example:
- Although this example specifies the issue number in a later commit, the process
still collects analytics data for the issue.
diff --git a/doc/user/application_security/api_fuzzing/create_har_files.md b/doc/user/application_security/api_fuzzing/create_har_files.md
index e7eaf47121b..5e033a75902 100644
--- a/doc/user/application_security/api_fuzzing/create_har_files.md
+++ b/doc/user/application_security/api_fuzzing/create_har_files.md
@@ -109,7 +109,7 @@ responses in HAR format.
have an account, first create an account.
1. Browse pages that call an API. Fiddler automatically captures the requests.
1. Select one or more requests, then from the context menu, select **Export > Selected Sessions**.
-1. In the **Choose Format** dropdown list select **HTTPArchive v1.2**.
+1. In the **Choose Format** dropdown list select **HTTP Archive v1.2**.
1. Enter a filename and select **Save**.
Fiddler shows a popup message confirming the export has succeeded.
diff --git a/doc/user/application_security/api_fuzzing/index.md b/doc/user/application_security/api_fuzzing/index.md
index 03eed6fdbf8..e4ca512bdc6 100644
--- a/doc/user/application_security/api_fuzzing/index.md
+++ b/doc/user/application_security/api_fuzzing/index.md
@@ -1551,13 +1551,13 @@ When testing an API it can be useful to exclude certain paths. For example, you
To verify the paths are excluded, review the `Tested Operations` and `Excluded Operations` portion of the job output. You should not see any excluded paths listed under `Tested Operations`.
```plaintext
-2021-05-27 21:51:08 [INF] API Security: --[ Tested Operations ]-------------------------
-2021-05-27 21:51:08 [INF] API Security: 201 POST http://target:7777/api/users CREATED
-2021-05-27 21:51:08 [INF] API Security: ------------------------------------------------
-2021-05-27 21:51:08 [INF] API Security: --[ Excluded Operations ]-----------------------
-2021-05-27 21:51:08 [INF] API Security: GET http://target:7777/api/messages
-2021-05-27 21:51:08 [INF] API Security: POST http://target:7777/api/messages
-2021-05-27 21:51:08 [INF] API Security: ------------------------------------------------
+2021-05-27 21:51:08 [INF] API Fuzzing: --[ Tested Operations ]-------------------------
+2021-05-27 21:51:08 [INF] API Fuzzing: 201 POST http://target:7777/api/users CREATED
+2021-05-27 21:51:08 [INF] API Fuzzing: ------------------------------------------------
+2021-05-27 21:51:08 [INF] API Fuzzing: --[ Excluded Operations ]-----------------------
+2021-05-27 21:51:08 [INF] API Fuzzing: GET http://target:7777/api/messages
+2021-05-27 21:51:08 [INF] API Fuzzing: POST http://target:7777/api/messages
+2021-05-27 21:51:08 [INF] API Fuzzing: ------------------------------------------------
```
#### Examples of excluding paths
@@ -1589,7 +1589,7 @@ variables:
While testing an API you may might want to exclude a parameter (query string, header, or body element) from testing. This may be needed because a parameter always causes a failure, slows down testing, or for other reasons. To exclude parameters you can use one of the following variables: `FUZZAPI_EXCLUDE_PARAMETER_ENV` or `FUZZAPI_EXCLUDE_PARAMETER_FILE`.
-The `FUZZAPI_EXCLUDE_PARAMETER_ENV` allows providing a JSON string containing excluded parameters. This is a good option if the JSON is short and will not often change. Another option is the variable `FUZZAPI_EXCLUDE_PARAMETER_FILE`. This variable is set to a file path that can be checked into the repository, created by another job as an artifact, or generated at runtime from a pre script using `FUZZAPI_PRE_SCRIPT`.
+The `FUZZAPI_EXCLUDE_PARAMETER_ENV` allows providing a JSON string containing excluded parameters. This is a good option if the JSON is short and will not often change. Another option is the variable `FUZZAPI_EXCLUDE_PARAMETER_FILE`. This variable is set to a file path that can be checked into the repository, created by another job as an artifact, or generated at runtime from a pre-script using `FUZZAPI_PRE_SCRIPT`.
#### Exclude parameters using a JSON document
@@ -1821,13 +1821,13 @@ As an alternative to excluding by paths, you can filter by any other component i
In your job output you can check if any URLs matched any provided regular expression from `FUZZAPI_EXCLUDE_URLS`. Matching operations are listed in the **Excluded Operations** section. Operations listed in the **Excluded Operations** should not be listed in the **Tested Operations** section. For example the following portion of a job output:
```plaintext
-2021-05-27 21:51:08 [INF] API Security: --[ Tested Operations ]-------------------------
-2021-05-27 21:51:08 [INF] API Security: 201 POST http://target:7777/api/users CREATED
-2021-05-27 21:51:08 [INF] API Security: ------------------------------------------------
-2021-05-27 21:51:08 [INF] API Security: --[ Excluded Operations ]-----------------------
-2021-05-27 21:51:08 [INF] API Security: GET http://target:7777/api/messages
-2021-05-27 21:51:08 [INF] API Security: POST http://target:7777/api/messages
-2021-05-27 21:51:08 [INF] API Security: ------------------------------------------------
+2021-05-27 21:51:08 [INF] API Fuzzing: --[ Tested Operations ]-------------------------
+2021-05-27 21:51:08 [INF] API Fuzzing: 201 POST http://target:7777/api/users CREATED
+2021-05-27 21:51:08 [INF] API Fuzzing: ------------------------------------------------
+2021-05-27 21:51:08 [INF] API Fuzzing: --[ Excluded Operations ]-----------------------
+2021-05-27 21:51:08 [INF] API Fuzzing: GET http://target:7777/api/messages
+2021-05-27 21:51:08 [INF] API Fuzzing: POST http://target:7777/api/messages
+2021-05-27 21:51:08 [INF] API Fuzzing: ------------------------------------------------
```
NOTE:
@@ -2242,18 +2242,18 @@ The first step to resolving performance issues is to understand what is contribu
The API Fuzzing job output contains helpful information about how fast we are testing, how fast each operation being tested responds, and summary information. Let's take a look at some sample output to see how it can be used in tracking down performance issues:
```shell
-API Security: Loaded 10 operations from: assets/har-large-response/large_responses.har
-API Security:
-API Security: Testing operation [1/10]: 'GET http://target:7777/api/large_response_json'.
-API Security: - Parameters: (Headers: 4, Query: 0, Body: 0)
-API Security: - Request body size: 0 Bytes (0 bytes)
-API Security:
-API Security: Finished testing operation 'GET http://target:7777/api/large_response_json'.
-API Security: - Excluded Parameters: (Headers: 0, Query: 0, Body: 0)
-API Security: - Performed 767 requests
-API Security: - Average response body size: 130 MB
-API Security: - Average call time: 2 seconds and 82.69 milliseconds (2.082693 seconds)
-API Security: - Time to complete: 14 minutes, 8 seconds and 788.36 milliseconds (848.788358 seconds)
+API Fuzzing: Loaded 10 operations from: assets/har-large-response/large_responses.har
+API Fuzzing:
+API Fuzzing: Testing operation [1/10]: 'GET http://target:7777/api/large_response_json'.
+API Fuzzing: - Parameters: (Headers: 4, Query: 0, Body: 0)
+API Fuzzing: - Request body size: 0 Bytes (0 bytes)
+API Fuzzing:
+API Fuzzing: Finished testing operation 'GET http://target:7777/api/large_response_json'.
+API Fuzzing: - Excluded Parameters: (Headers: 0, Query: 0, Body: 0)
+API Fuzzing: - Performed 767 requests
+API Fuzzing: - Average response body size: 130 MB
+API Fuzzing: - Average call time: 2 seconds and 82.69 milliseconds (2.082693 seconds)
+API Fuzzing: - Time to complete: 14 minutes, 8 seconds and 788.36 milliseconds (848.788358 seconds)
```
This job console output snippet starts by telling us how many operations were found (10), followed by notifications that testing has started on a specific operation and a summary of the operation has been completed. The summary is the most interesting part of this log output. In the summary, we can see that it took API Fuzzing 767 requests to fully test this operation and its related fields. We can also see that the average response time was 2 seconds and the time to complete was 14 minutes for this one operation.
@@ -2443,7 +2443,7 @@ See the following documentation sections for assistance:
See [Performance Tuning and Testing Speed](#performance-tuning-and-testing-speed)
-### Error waiting for API Security 'http://127.0.0.1:5000' to become available
+### Error waiting for API Fuzzing 'http://127.0.0.1:5000' to become available
A bug exists in versions of the API Fuzzing analyzer prior to v1.6.196 that can cause a background process to fail under certain conditions. The solution is to update to a newer version of the API Fuzzing analyzer.
@@ -2456,6 +2456,11 @@ If the issue is occurring with versions v1.6.196 or greater, contact Support and
1. The `gl-api-security-scanner.log` file available as a job artifact. In the right-hand panel of the job details page, select the **Browse** button.
1. The `apifuzzer_fuzz` job definition from your `.gitlab-ci.yml` file.
+**Error message**
+
+- In [GitLab 15.6 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/376078), `Error waiting for API Fuzzing 'http://127.0.0.1:5000' to become available`
+- In GitLab 15.5 and earlier, `Error waiting for API Security 'http://127.0.0.1:5000' to become available`.
+
### `Failed to start session with scanner. Please retry, and if the problem persists reach out to support.`
The API Fuzzing engine outputs an error message when it cannot establish a connection with the scanner application component. The error message is shown in the job output window of the `apifuzzer_fuzz` job. A common cause for this issue is that the background component cannot use the selected port as it's already in use. This error can occur intermittently if timing plays a part (race condition). This issue occurs most often with Kubernetes environments when other services are mapped into the container causing port conflicts.
@@ -2648,6 +2653,50 @@ API Fuzzing uses the specified media types in the OpenAPI document to generate r
1. Review the supported media types in the [OpenAPI Specification](#openapi-specification) section.
1. Edit your OpenAPI document, allowing at least a given operation to accept any of the supported media types. Alternatively, a supported media type could be set in the OpenAPI document level and get applied to all operations. This step may require changes in your application to ensure the supported media type is accepted by the application.
+### ``Error, error occurred trying to download `<URL>`: There was an error when retrieving content from Uri:' <URL>'. Error:The SSL connection could not be established, see inner exception.``
+
+API fuzzing is compatible with a broad range of TLS configurations, including outdated protocols and ciphers.
+Despite broad support, you might encounter connection errors. This error occurs because API fuzzing could not establish a secure connection with the server at the given URL.
+
+To resolve the issue:
+
+If the host in the error message supports non-TLS connections, change `https://` to `http://` in your configuration.
+For example, if an error occurs with the following configuration:
+
+```yaml
+stages:
+ - fuzz
+
+include:
+ - template: API-Fuzzing.gitlab-ci.yml
+
+variables:
+ FUZZAPI_TARGET_URL: https://test-deployment/
+ FUZZAPI_OPENAPI: https://specs/openapi.json
+```
+
+Change the prefix of `FUZZAPI_OPENAPI` from `https://` to `http://`:
+
+```yaml
+stages:
+ - fuzz
+
+include:
+ - template: API-Fuzzing.gitlab-ci.yml
+
+variables:
+ FUZZAPI_TARGET_URL: https://test-deployment/
+ FUZZAPI_OPENAPI: http://specs/openapi.json
+```
+
+If you cannot use a non-TLS connection to access the URL, contact the Support team for help.
+
+You can expedite the investigation with the [testssl.sh tool](https://testssl.sh/). From a machine with a bash shell and connectivity to the affected server:
+
+1. Download the latest release `zip` or `tar.gz` file and extract from <https://github.com/drwetter/testssl.sh/releases>.
+1. Run `./testssl.sh --log https://specs`.
+1. Attach the log file to your support ticket.
+
## Get support or request an improvement
To get support for your particular problem use the [getting help channels](https://about.gitlab.com/get-help/).
diff --git a/doc/user/application_security/configuration/index.md b/doc/user/application_security/configuration/index.md
index d9ba4640855..ea422f0b33c 100644
--- a/doc/user/application_security/configuration/index.md
+++ b/doc/user/application_security/configuration/index.md
@@ -67,7 +67,7 @@ You can configure the following security controls:
- Can be configured by adding a configuration block to your agent configuration. For more details, read [Operational Container Scanning](../../clusters/agent/vulnerabilities.md#enable-operational-container-scanning).
- [Secret Detection](../secret_detection/index.md)
- Select **Configure with a merge request** to create a merge request with the changes required to
- enable Secret Detection. For more details, read [Enable Secret Detection using a merge request](../secret_detection/index.md#enable-secret-detection-using-a-merge-request).
+ enable Secret Detection. For more details, read [Use an automatically configured merge request](../secret_detection/index.md#use-an-automatically-configured-merge-request).
- [API Fuzzing](../api_fuzzing/index.md)
- Select **Enable API Fuzzing** to use API Fuzzing for the current project. For more details, read [API Fuzzing](../../../user/application_security/api_fuzzing/index.md#enable-web-api-fuzzing).
- [Coverage Fuzzing](../coverage_fuzzing/index.md)
diff --git a/doc/user/application_security/container_scanning/index.md b/doc/user/application_security/container_scanning/index.md
index 6fc01a716b2..e198f967eea 100644
--- a/doc/user/application_security/container_scanning/index.md
+++ b/doc/user/application_security/container_scanning/index.md
@@ -90,12 +90,12 @@ To enable container scanning in your pipeline, you need the following:
## Configuration
To enable container scanning, add the
-[`Container-Scanning.gitlab-ci.yml` template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml)
+[`Container-Scanning.gitlab-ci.yml` template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Container-Scanning.gitlab-ci.yml)
to your `.gitlab-ci.yml` file:
```yaml
include:
- - template: Security/Container-Scanning.gitlab-ci.yml
+ - template: Jobs/Container-Scanning.gitlab-ci.yml
```
The included template:
@@ -117,7 +117,7 @@ registry, and scans the image:
```yaml
include:
- template: Jobs/Build.gitlab-ci.yml
- - template: Security/Container-Scanning.gitlab-ci.yml
+ - template: Jobs/Container-Scanning.gitlab-ci.yml
container_scanning:
variables:
@@ -142,7 +142,7 @@ enables verbose output for the analyzer:
```yaml
include:
- - template: Security/Container-Scanning.gitlab-ci.yml
+ - template: Jobs/Container-Scanning.gitlab-ci.yml
variables:
SECURE_LOG_LEVEL: 'debug'
@@ -154,7 +154,7 @@ To scan images located in a registry other than the project's, use the following
```yaml
include:
- - template: Security/Container-Scanning.gitlab-ci.yml
+ - template: Jobs/Container-Scanning.gitlab-ci.yml
container_scanning:
variables:
@@ -178,7 +178,7 @@ container_scanning:
- export AWS_ECR_PASSWORD=$(aws ecr get-login-password --region region)
include:
- - template: Security/Container-Scanning.gitlab-ci.yml
+ - template: Jobs/Container-Scanning.gitlab-ci.yml
CS_IMAGE: <aws_account_id>.dkr.ecr.<region>.amazonaws.com/<image>:<tag>
CS_REGISTRY_USER: AWS
CS_REGISTRY_PASSWORD: "$AWS_ECR_PASSWORD"
@@ -199,7 +199,7 @@ For example:
```yaml
include:
- - template: Security/Container-Scanning.gitlab-ci.yml
+ - template: Jobs/Container-Scanning.gitlab-ci.yml
container_scanning:
variables:
@@ -223,7 +223,7 @@ By default, the report only includes packages managed by the Operating System (O
```yaml
include:
- - template: Security/Container-Scanning.gitlab-ci.yml
+ - template: Jobs/Container-Scanning.gitlab-ci.yml
container_scanning:
variables:
@@ -345,7 +345,7 @@ This example sets `GIT_STRATEGY` to `fetch`:
```yaml
include:
- - template: Security/Container-Scanning.gitlab-ci.yml
+ - template: Jobs/Container-Scanning.gitlab-ci.yml
container_scanning:
variables:
@@ -391,7 +391,7 @@ duplicated:
```yaml
include:
- - template: Security/Container-Scanning.gitlab-ci.yml
+ - template: Jobs/Container-Scanning.gitlab-ci.yml
container_scanning:
variables:
@@ -567,7 +567,7 @@ and you may be able to make occasional updates on your own.
For more information, see [the specific steps on how to update an image with a pipeline](#automating-container-scanning-vulnerability-database-updates-with-a-pipeline).
-For details on saving and transporting Docker images as a file, see Docker's documentation on
+For details on saving and transporting Docker images as a file, see the Docker documentation on
[`docker save`](https://docs.docker.com/engine/reference/commandline/save/), [`docker load`](https://docs.docker.com/engine/reference/commandline/load/),
[`docker export`](https://docs.docker.com/engine/reference/commandline/export/), and [`docker import`](https://docs.docker.com/engine/reference/commandline/import/).
@@ -577,7 +577,7 @@ For details on saving and transporting Docker images as a file, see Docker's doc
```yaml
include:
- - template: Security/Container-Scanning.gitlab-ci.yml
+ - template: Jobs/Container-Scanning.gitlab-ci.yml
container_scanning:
image: $CI_REGISTRY/namespace/gitlab-container-scanning
@@ -628,7 +628,7 @@ This example shows the configuration needed to scan images in a private [Google
```yaml
include:
- - template: Security/Container-Scanning.gitlab-ci.yml
+ - template: Jobs/Container-Scanning.gitlab-ci.yml
container_scanning:
variables:
diff --git a/doc/user/application_security/dast/authentication.md b/doc/user/application_security/dast/authentication.md
new file mode 100644
index 00000000000..d4f91639dbc
--- /dev/null
+++ b/doc/user/application_security/dast/authentication.md
@@ -0,0 +1,527 @@
+---
+stage: Secure
+group: Dynamic Analysis
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+type: reference, howto
+---
+
+# Authentication (DAST) **(ULTIMATE)**
+
+WARNING:
+**Never** run an authenticated scan against a production server.
+Authenticated scans may perform *any* function that the authenticated user can,
+including modifying or deleting data, submitting forms, and following links.
+Only run an authenticated scan against a test server.
+
+Authentication logs a user in before a DAST scan so that the analyzer can test
+as much of the application as possible when searching for vulnerabilities.
+
+DAST uses a browser to authenticate the user so that the login form has the necessary JavaScript
+and styling required to submit the form. DAST finds the username and password fields and fills them with their respective values.
+The login form is submitted, and when the response returns, a series of checks verify if authentication was successful.
+DAST saves the credentials for reuse when crawling the target application.
+
+If DAST fails to authenticate, the scan halts and the CI job fails.
+
+Authentication supports single-step login forms, multi-step login forms, single sign-on, and authenticating to URLs outside of the configured target URL.
+
+## Getting started
+
+NOTE:
+We recommend periodically confirming that the analyzer's authentication is still working, as this tends to break over
+time due to changes to the application.
+
+To run a DAST authenticated scan:
+
+- Read the [prerequisite](#prerequisites) conditions for authentication.
+- [Update your target website](#update-the-target-website) to a landing page of an authenticated user.
+- If your login form has the username, password and submit button on a single page, use the [CI/CD variables](#available-cicd-variables) to configure [single-step](#configuration-for-a-single-step-login-form) login form authentication.
+- If your login form has the username and password fields on different pages, use the [CI/CD variables](#available-cicd-variables) to configure [multi-step](#configuration-for-a-multi-step-login-form) login form authentication.
+- Make sure the user isn't [logged out](#excluding-logout-urls) during the scan.
+
+### Prerequisites
+
+- You are using either the [DAST proxy-based analyzer](proxy-based.md) or the [DAST browser-based analyzer](browser_based.md).
+- You know the URL of the login form of your application. Alternatively, you know how to navigate to the login form from the authentication URL (see [clicking to navigate to the login form](#clicking-to-navigate-to-the-login-form)).
+- You have the username and password of the user you would like to authenticate as during the scan.
+- You know the [selectors](#finding-an-elements-selector) of the username and password HTML fields that DAST will use to input the respective values.
+- You know the element's [selector](#finding-an-elements-selector) that will submit the login form when selected.
+- You have thought about how you can [verify](#verifying-authentication-is-successful) whether or not authentication was successful.
+- You have checked the [known limitations](#known-limitations) to ensure DAST can authenticate to your application.
+
+### Available CI/CD variables
+
+| CI/CD variable | Type | Description |
+|:-----------------------------------------------|:------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `DAST_AUTH_COOKIES` | string | Set to a comma-separated list of cookie names to specify which cookies are used for authentication. |
+| `DAST_AUTH_REPORT` | boolean | Used in combination with exporting the `gl-dast-debug-auth-report.html` artifact to aid in debugging authentication issues. |
+| `DAST_AUTH_URL` <sup>1</sup> | URL | The URL of the page containing the sign-in HTML form on the target website. `DAST_USERNAME` and `DAST_PASSWORD` are submitted with the login form to create an authenticated scan. Example: `https://login.example.com`. |
+| `DAST_AUTH_VERIFICATION_LOGIN_FORM` | boolean | Verifies successful authentication by checking for the absence of a login form once the login form has been submitted. |
+| `DAST_AUTH_VERIFICATION_SELECTOR` | [selector](#finding-an-elements-selector) | Verifies successful authentication by checking for presence of a selector once the login form has been submitted. Example: `css:.user-photo`. |
+| `DAST_AUTH_VERIFICATION_URL` <sup>1</sup> | URL | Verifies successful authentication by checking the URL in the browser once the login form has been submitted. Example: `"https://example.com/loggedin_page"`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/207335) in GitLab 13.8. |
+| `DAST_BROWSER_PATH_TO_LOGIN_FORM` <sup>1</sup> | [selector](#finding-an-elements-selector) | Comma-separated list of selectors that are selected prior to attempting to enter `DAST_USERNAME` and `DAST_PASSWORD` into the login form. Example: `"css:.navigation-menu,css:.login-menu-item"`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/326633) in GitLab 14.1. |
+| `DAST_EXCLUDE_URLS` <sup>1</sup> | URLs | The URLs to skip during the authenticated scan; comma-separated. Regular expression syntax can be used to match multiple URLs. For example, `.*` matches an arbitrary character sequence. |
+| `DAST_FIRST_SUBMIT_FIELD` | string | The `id` or `name` of the element that when selected submits the username form of a multi-page login process. For example, `css:button[type='user-submit']`. [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9894) in GitLab 12.4. |
+| `DAST_PASSWORD` <sup>1</sup> | string | The password to authenticate to in the website. Example: `P@55w0rd!` |
+| `DAST_PASSWORD_FIELD` | string | The selector of password field at the sign-in HTML form. Example: `id:password` |
+| `DAST_SUBMIT_FIELD` | string | The `id` or `name` of the element that when selected submits the login form or the password form of a multi-page login process. For example, `css:button[type='submit']`. [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9894) in GitLab 12.4. |
+| `DAST_USERNAME` <sup>1</sup> | string | The username to authenticate to in the website. Example: `admin` |
+| `DAST_USERNAME_FIELD` <sup>1</sup> | string | The selector of username field at the sign-in HTML form. Example: `name:username` |
+
+1. Available to an on-demand proxy-based DAST scan.
+
+### Update the target website
+
+The target website, defined using the CI/CD variable `DAST_WEBSITE`, is the URL DAST uses to begin crawling your application.
+
+For best crawl results on an authenticated scan, the target website should be a URL accessible only after the user is authenticated.
+Often, this is the URL of the page the user lands on after they're logged in.
+
+For example:
+
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+
+dast:
+ variables:
+ DAST_WEBSITE: "https://example.com/dashboard/welcome"
+ DAST_AUTH_URL: "https://example.com/login"
+```
+
+### Configuration for a single-step login form
+
+A single-step login form has all login form elements on a single page.
+Configuration requires the CI/CD variables `DAST_AUTH_URL`, `DAST_USERNAME`, `DAST_USERNAME_FIELD`, `DAST_PASSWORD`, `DAST_PASSWORD_FIELD`, and `DAST_SUBMIT_FIELD` to be defined for the DAST job.
+
+It is recommended to set up the URL and selectors of fields in the job definition YAML, for example:
+
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+
+dast:
+ variables:
+ DAST_WEBSITE: "https://example.com"
+ DAST_AUTH_URL: "https://example.com/login"
+ DAST_USERNAME_FIELD: "css:[name=username]"
+ DAST_PASSWORD_FIELD: "css:[name=password]"
+ DAST_SUBMIT_FIELD: "css:button[type=submit]"
+```
+
+Do **not** define `DAST_USERNAME` and `DAST_PASSWORD` in the YAML job definition file as this could present a security risk. Instead, create them as masked CI/CD variables using the GitLab UI.
+See [Custom CI/CI variables](../../../ci/variables/index.md#custom-cicd-variables) for more information.
+
+### Configuration for a multi-step login form
+
+A multi-step login form has two pages. The first page has a form with the username and a next submit button.
+If the username is valid, a second form on the subsequent page has the password and the form submit button.
+
+Configuration requires the CI/CD variables `DAST_AUTH_URL`, `DAST_USERNAME`, `DAST_USERNAME_FIELD`, `DAST_FIRST_SUBMIT_FIELD`, `DAST_PASSWORD`, `DAST_PASSWORD_FIELD`, and `DAST_SUBMIT_FIELD` to be defined for the DAST job.
+
+It is recommended to set up the URL and selectors of fields in the job definition YAML, for example:
+
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+
+dast:
+ variables:
+ DAST_WEBSITE: "https://example.com"
+ DAST_AUTH_URL: "https://example.com/login"
+ DAST_USERNAME_FIELD: "css:[name=username]"
+ DAST_FIRST_SUBMIT_FIELD: "css:button[name=next]"
+ DAST_PASSWORD_FIELD: "css:[name=password]"
+ DAST_SUBMIT_FIELD: "css:button[type=submit]"
+```
+
+Do **not** define `DAST_USERNAME` and `DAST_PASSWORD` in the YAML job definition file as this could present a security risk. Instead, create them as masked CI/CD variables using the GitLab UI.
+See [Custom CI/CI variables](../../../ci/variables/index.md#custom-cicd-variables) for more information.
+
+### Configuration for Single Sign-On (SSO)
+
+If a user can log into an application, then in most cases, DAST will also be able to log in.
+This is the case even when an application uses Single Sign-on. Applications using SSO solutions should configure DAST
+authentication using the [single-step](#configuration-for-a-single-step-login-form) or [multi-step](#configuration-for-a-multi-step-login-form) login form configuration guides.
+
+DAST supports authentication processes where a user is redirected to an external Identity Provider's site to log in.
+Check the [known limitations](#known-limitations) of DAST authentication to determine if your SSO authentication process is supported.
+
+### Clicking to navigate to the login form
+
+Define `DAST_BROWSER_PATH_TO_LOGIN_FORM` to provide a path of elements to click on from the `DAST_AUTH_URL` so that DAST can access the
+login form. This is useful for applications that show the login form in a pop-up (modal) window or when the login form does not
+have a unique URL.
+
+For example:
+
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+
+dast:
+ variables:
+ DAST_WEBSITE: "https://example.com"
+ DAST_AUTH_URL: "https://example.com/login"
+ DAST_BROWSER_PATH_TO_LOGIN_FORM: "css:.navigation-menu,css:.login-menu-item"
+```
+
+### Excluding logout URLs
+
+If DAST crawls the logout URL while running an authenticated scan, the user will be logged out, resulting in the remainder of the scan being unauthenticated.
+It is therefore recommended to exclude logout URLs using the CI/CD variable `DAST_EXCLUDE_URLS`. DAST will not access any excluded URLs, ensuring the user remains logged in.
+
+Provided URLs can be either absolute URLs, or regular expressions of URL paths relative to the base path of the `DAST_WEBSITE`. For example:
+
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+
+dast:
+ variables:
+ DAST_WEBSITE: "https://example.com/welcome/home"
+ DAST_EXCLUDE_URLS: "https://example.com/logout,/user/.*/logout"
+```
+
+### Finding an element's selector
+
+Selectors are used by CI/CD variables to specify the location of an element displayed on a page in a browser.
+Selectors have the format `type`:`search string`. DAST searches for the selector using the search string based on the type.
+
+| Selector type | Example | Description |
+|---------------|------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `css` | `css:.password-field` | Searches for a HTML element having the supplied CSS selector. Selectors should be as specific as possible for performance reasons. |
+| `id` | `id:element` | Searches for an HTML element with the provided element ID. |
+| `name` | `name:element` | Searches for an HTML element with the provided element name. |
+| `xpath` | `xpath://input[@id="my-button"]/a` | Searches for a HTML element with the provided XPath. Note that XPath searches are expected to be less performant than other searches. |
+| None provided | `a.click-me` | Defaults to searching using a CSS selector. **{warning}** **[Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/383348)** in GitLab 15.8. Replaced by explicitly declaring the selector type. |
+
+#### Find selectors with Google Chrome
+
+Chrome DevTools element selector tool is an effective way to find a selector.
+
+1. Open Chrome and navigate to the page where you would like to find a selector, for example, the login page for your site.
+1. Open the `Elements` tab in Chrome DevTools with the keyboard shortcut `Command + Shift + c` in macOS or `Ctrl + Shift + c` in Windows.
+1. Select the `Select an element in the page to select it` tool.
+ ![search-elements](img/dast_auth_browser_scan_search_elements.png)
+1. Select the field on your page that you would like to know the selector for.
+1. Once the tool is active, highlight a field you wish to view the details of.
+ ![highlight](img/dast_auth_browser_scan_highlight.png)
+1. Once highlighted, you can see the element's details, including attributes that would make a good candidate for a selector.
+
+In this example, the `id="user_login"` appears to be a good candidate. You can use this as a selector as the DAST username field by setting
+`DAST_USERNAME_FIELD: "id:user_login"`.
+
+#### Choose the right selector
+
+Judicious choice of selector leads to a scan that is resilient to the application changing.
+
+In order of preference, it is recommended to choose as selectors:
+
+- `id` fields. These are generally unique on a page, and rarely change.
+- `name` fields. These are generally unique on a page, and rarely change.
+- `class` values specific to the field, such as the selector `"css:.username"` for the `username` class on the username field.
+- Presence of field specific data attributes, such as the selector, `"css:[data-username]"` when the `data-username` field has any value on the username field.
+- Multiple `class` hierarchy values, such as the selector `"css:.login-form .username"` when there are multiple elements with class `username` but only one nested inside the element with the class `login-form`.
+
+When using selectors to locate specific fields we recommend you avoid searching on:
+
+- Any `id`, `name`, `attribute`, `class` or `value` that is dynamically generated.
+- Generic class names, such as `column-10` and `dark-grey`.
+- XPath searches as they are less performant than other selector searches.
+- Unscoped searches, such as those beginning with `css:*` and `xpath://*`.
+
+## Verifying authentication is successful
+
+Once DAST has submitted the login form, a verification process takes place
+to determine if authentication succeeded. The scan will halt with an error if authentication is unsuccessful.
+
+Following the submission of the login form, authentication is determined to be unsuccessful when:
+
+- The login submit HTTP response has a `400` or `500` series status code.
+- Any [verification check](#verification-checks) fails.
+- An [authentication token](#authentication-tokens) with a sufficiently random value is not set during the authentication process.
+
+### Verification checks
+
+Verification checks run checks on the state of the browser once authentication is complete
+to determine further if authentication succeeded.
+
+DAST will test for the absence of a login form if no verification checks are configured.
+
+#### Verify based on the URL
+
+Define `DAST_AUTH_VERIFICATION_URL` as the URL displayed in the browser tab once the login form is successfully submitted.
+
+DAST will compare the verification URL to the URL in the browser after authentication.
+If they are not the same, authentication is unsuccessful.
+
+For example:
+
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+
+dast:
+ variables:
+ DAST_WEBSITE: "https://example.com"
+ DAST_AUTH_VERIFICATION_URL: "https://example.com/user/welcome"
+```
+
+#### Verify based on presence of an element
+
+Define `DAST_AUTH_VERIFICATION_SELECTOR` as a [selector](#finding-an-elements-selector) that will find one or many elements on the page
+displayed once the login form is successfully submitted. If no element is found, authentication is unsuccessful.
+Searching for the selector on the page displayed when login fails should return no elements.
+
+For example:
+
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+
+dast:
+ variables:
+ DAST_WEBSITE: "https://example.com"
+ DAST_AUTH_VERIFICATION_SELECTOR: "css:.welcome-user"
+```
+
+#### Verify based on absence of a login form
+
+Define `DAST_AUTH_VERIFICATION_LOGIN_FORM` as `"true"` to indicate that DAST should search for the login form on the
+page displayed once the login form is successfully submitted. If a login form is still present after logging in, authentication is unsuccessful.
+
+For example:
+
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+
+dast:
+ variables:
+ DAST_WEBSITE: "https://example.com"
+ DAST_AUTH_VERIFICATION_LOGIN_FORM: "true"
+```
+
+### Authentication tokens
+
+DAST records authentication tokens set during the authentication process.
+Authentication tokens are loaded into new browsers when DAST opens them so the user can remain logged in throughout the scan.
+
+To record tokens, DAST takes a snapshot of cookies, local storage, and session storage values set by the application before
+the authentication process. DAST does the same after authentication and uses the difference to determine which were created
+by the authentication process.
+
+DAST considers cookies, local storage and session storage values set with sufficiently "random" values to be authentication tokens.
+For example, `sessionID=HVxzpS8GzMlPAc2e39uyIVzwACIuGe0H` would be viewed as an authentication token, while `ab_testing_group=A1` would not.
+
+The CI/CD variable `DAST_AUTH_COOKIES` can be used to specify the names of authentication cookies and bypass the randomness check used by DAST.
+Not only can this make the authentication process more robust, but it can also increase vulnerability check accuracy for checks that
+inspect authentication tokens.
+
+For example:
+
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+
+dast:
+ variables:
+ DAST_WEBSITE: "https://example.com"
+ DAST_AUTH_COOKIES: "sessionID,refreshToken"
+```
+
+## Known limitations
+
+- DAST cannot bypass a CAPTCHA if the authentication flow includes one. Please turn these off in the testing environment for the application being scanned.
+- DAST cannot handle multi-factor authentication like one-time passwords (OTP) by using SMS, biometrics, or authenticator apps. Please turn these off in the testing environment for the application being scanned.
+- DAST cannot authenticate to applications that do not set an [authentication token](#authentication-tokens) during login.
+- DAST cannot authenticate to applications that require more than two inputs to be filled out. Two inputs must be supplied, username and password.
+
+## Troubleshooting
+
+The [logs](#read-the-logs) provide insight into what DAST is doing and expecting during the authentication process. For more detailed
+information, configure the [authentication report](#configure-the-authentication-report).
+
+For more information about particular error messages or situations see [known problems](#known-problems).
+
+The browser-based analyzer is used to authenticate the user. For advanced troubleshooting, see [browser-based troubleshooting](browser_based_troubleshooting.md).
+
+### Read the logs
+
+The console output of the DAST CI/CD job shows information about the authentication process using the `AUTH` log module.
+For example, the following log shows failed authentication for a multi-step login form.
+Authentication failed because a home page should be displayed after login. Instead, the login form was still present.
+
+```plaintext
+2022-11-16T13:43:02.000 INF AUTH attempting to authenticate
+2022-11-16T13:43:02.000 INF AUTH loading login page LoginURL=https://example.com/login
+2022-11-16T13:43:10.000 INF AUTH multi-step authentication detected
+2022-11-16T13:43:15.000 INF AUTH verifying if user submit was successful true_when="HTTP status code < 400"
+2022-11-16T13:43:15.000 INF AUTH requirement is satisfied, no login HTTP message detected want="HTTP status code < 400"
+2022-11-16T13:43:20.000 INF AUTH verifying if login attempt was successful true_when="HTTP status code < 400 and has authentication token and no login form found (no element found when searching using selector css:[id=email] or css:[id=password] or css:[id=submit])"
+2022-11-24T14:43:20.000 INF AUTH requirement is satisfied, HTTP login request returned status code 200 url=https://example.com/user/login?error=invalid%20credentials want="HTTP status code < 400"
+2022-11-16T13:43:21.000 INF AUTH requirement is unsatisfied, login form was found want="no login form found (no element found when searching using selector css:[id=email] or css:[id=password] or css:[id=submit])"
+2022-11-16T13:43:21.000 INF AUTH login attempt failed error="authentication failed: failed to authenticate user"
+```
+
+### Configure the authentication report
+
+An authentication report can be saved as a CI/CD job artifact to assist with understanding the cause of an authentication failure.
+
+The report contains steps during the login process, HTTP requests and responses, the Document Object Model (DOM) and screenshots.
+
+![dast-auth-report](img/dast_auth_report.jpg)
+
+An example configuration where the authentication debug report is exported may look like the following:
+
+```yaml
+dast:
+ variables:
+ DAST_WEBSITE: "https://example.com"
+ DAST_AUTH_REPORT: "true"
+ artifacts:
+ paths: [gl-dast-debug-auth-report.html]
+ when: always
+```
+
+### Known problems
+
+#### Login form not found
+
+DAST failed to find a login form when loading the login page, often because the authentication URL could not be loaded.
+The log reports a fatal error such as:
+
+```plaintext
+2022-12-07T12:44:02.838 INF AUTH loading login page LoginURL=[authentication URL]
+2022-12-07T12:44:11.119 FTL MAIN authentication failed: login form not found
+```
+
+Suggested actions:
+
+- Generate the [authentication report](#configure-the-authentication-report) to inspect HTTP response.
+- Check the target application authentication is deployed and running.
+- Check the `DAST_AUTH_URL` is correct.
+- Check the GitLab Runner can access the `DAST_AUTH_URL`.
+- Check the `DAST_BROWSER_PATH_TO_LOGIN_FORM` is valid if used.
+
+#### Scan doesn't crawl authenticated pages
+
+If DAST captures the wrong [authentication tokens](#authentication-tokens) during the authentication process then
+the scan can't crawl authenticated pages. Names of cookies and storage authentication tokens are written to the log. For example:
+
+```plaintext
+2022-11-24T14:42:31.492 INF AUTH authentication token cookies names=["sessionID"]
+2022-11-24T14:42:31.492 INF AUTH authentication token storage events keys=["token"]
+```
+
+Suggested actions:
+
+- Generate the [authentication report](#configure-the-authentication-report) and look at the screenshot from the `Login submit` to verify that the login worked as expected.
+- Verify the logged authentication tokens are those used by your application.
+- If using cookies to store authentication tokens, set the names of the authentication token cookies using `DAST_AUTH_COOKIES`.
+
+#### Unable to find elements with selector
+
+DAST failed to find the username, password, first submit button, or submit button elements. The log reports a fatal error such as:
+
+```plaintext
+2022-12-07T13:14:11.545 FTL MAIN authentication failed: unable to find elements with selector: css:#username
+```
+
+Suggested actions:
+
+- Generate the [authentication report](#configure-the-authentication-report) to use the screenshot from the `Login page` to verify that the page loaded correctly.
+- Load the login page in a browser and verify the [selectors](#finding-an-elements-selector) configured in `DAST_USERNAME_FIELD`, `DAST_PASSWORD_FIELD`, `DAST_FIRST_SUBMIT_FIELD`, and `DAST_SUBMIT_FIELD` are correct.
+
+#### Failed to authenticate user
+
+DAST failed to authenticate due to a failed login verification check. The log reports a fatal error such as:
+
+```plaintext
+2022-12-07T06:39:49.483 INF AUTH verifying if login attempt was successful true_when="HTTP status code < 400 and has authentication token and no login form found (no element found when searching using selector css:[name=username] or css:[name=password] or css:button[type=\"submit\"])"
+2022-12-07T06:39:49.484 INF AUTH requirement is satisfied, HTTP login request returned status code 303 url=http://auth-manual:8090/login want="HTTP status code < 400"
+2022-12-07T06:39:49.513 INF AUTH requirement is unsatisfied, login form was found want="no login form found (no element found when searching using selector css:[name=username] or css:[name=password] or css:button[type=\"submit\"])"
+2022-12-07T06:39:49.589 INF AUTH login attempt failed error="authentication failed: failed to authenticate user"
+2022-12-07T06:39:53.626 FTL MAIN authentication failed: failed to authenticate user
+```
+
+Suggested actions:
+
+- Look in the log for the `requirement is unsatisfied`. Respond to the appropriate error.
+
+#### Requirement unsatisfied, login form was found
+
+Applications typically display a dashboard when the user logs in and the login form with an error message when the
+username or password is incorrect.
+
+This error occurs when DAST detects the login form on the page displayed after authenticating the user,
+indicating that the login attempt failed.
+
+```plaintext
+2022-12-07T06:39:49.513 INF AUTH requirement is unsatisfied, login form was found want="no login form found (no element found when searching using selector css:[name=username] or css:[name=password] or css:button[type=\"submit\"])"
+```
+
+Suggested actions:
+
+- Verify that the username and password/authentication credentials used are correct.
+- Generate the [authentication report](#configure-the-authentication-report) and verify the `Request` for the `Login submit` is correct.
+- It's possible that the authentication report `Login submit` request and response are empty. This occurs when there is no request that would result
+ in a full page reload, such as a request made when submitting a HTML form. This occurs when using websockets or AJAX to submit the login form.
+- If the page displayed following user authentication genuinely has elements matching the login form selectors, configure `DAST_AUTH_VERIFICATION_URL`
+ or `DAST_AUTH_VERIFICATION_SELECTOR` to use an alternate method of verifying the login attempt.
+
+#### Requirement unsatisfied, selector returned no results
+
+DAST cannot find an element matching the selector provided in `DAST_AUTH_VERIFICATION_SELECTOR` on the page displayed following user login.
+
+```plaintext
+2022-12-07T06:39:33.239 INF AUTH requirement is unsatisfied, searching DOM using selector returned no results want="has element css:[name=welcome]"
+```
+
+Suggested actions:
+
+- Generate the [authentication report](#configure-the-authentication-report) and look at the screenshot from the `Login submit` to verify that the expected page is displayed.
+- Ensure the `DAST_AUTH_VERIFICATION_SELECTOR` [selector](#finding-an-elements-selector) is correct.
+
+#### Requirement unsatisfied, browser not at URL
+
+DAST detected that the page displayed following user login has a URL different to what was expected according to `DAST_AUTH_VERIFICATION_URL`.
+
+```plaintext
+2022-12-07T11:28:00.241 INF AUTH requirement is unsatisfied, browser is not at URL browser_url="https://example.com/home" want="is at url https://example.com/user/dashboard"
+```
+
+Suggested actions:
+
+- Generate the [authentication report](#configure-the-authentication-report) and look at the screenshot from the `Login submit` to verify that the expected page is displayed.
+- Ensure the `DAST_AUTH_VERIFICATION_URL` is correct.
+
+#### Requirement unsatisfied, HTTP login request status code
+
+The HTTP response when loading the login form or submitting the form had a status code of 400 (client error)
+or 500 (server error).
+
+```plaintext
+2022-12-07T06:39:53.626 INF AUTH requirement is unsatisfied, HTTP login request returned status code 502 url="https://example.com/user/login" want="HTTP status code < 400"
+```
+
+- Verify that the username and password/authentication credentials used are correct.
+- Generate the [authentication report](#configure-the-authentication-report) and verify the `Request` for the `Login submit` is correct.
+- Verify the target application works as expected.
+
+#### Requirement unsatisfied, no authentication token
+
+DAST could not detect an [authentication token](#authentication-tokens) created during the authentication process.
+
+```plaintext
+2022-12-07T11:25:29.010 INF AUTH authentication token cookies names=[]
+2022-12-07T11:25:29.010 INF AUTH authentication token storage events keys=[]
+2022-12-07T11:25:29.010 INF AUTH requirement is unsatisfied, no basic authentication, cookie or storage event authentication token detected want="has authentication token"
+```
+
+Suggestion actions:
+
+- Generate the [authentication report](#configure-the-authentication-report) and look at the screenshot from the `Login submit` to verify that the login worked as expected.
+- Using the browser's developer tools, investigate the cookies and local/session storage objects created while logging in. Ensure there is an authentication token created with sufficiently random value.
+- If using cookies to store authentication tokens, set the names of the authentication token cookies using `DAST_AUTH_COOKIES`.
diff --git a/doc/user/application_security/dast/browser_based.md b/doc/user/application_security/dast/browser_based.md
index c0a97c0ff92..7377f31d0ce 100644
--- a/doc/user/application_security/dast/browser_based.md
+++ b/doc/user/application_security/dast/browser_based.md
@@ -10,39 +10,144 @@ type: reference, howto
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/323423) in GitLab 13.12.
WARNING:
-This product is in an early-access stage and is considered a [beta](../../../policy/alpha-beta-support.md#beta-features) feature.
+This product is in an early-access stage and is considered a [beta](../../../policy/alpha-beta-support.md#beta-features)
+feature.
-GitLab DAST's browser-based analyzer was built by GitLab to test Single Page Applications (SPAs) and
-traditional web applications. It both crawls the web application and analyzes the resulting output
-for vulnerabilities. Analysis of modern applications, heavily reliant on JavaScript, is vital to
-ensuring DAST coverage.
+WARNING:
+Do not run DAST scans against a production server. Not only can it perform *any* function that
+a user can, such as clicking buttons or submitting forms, but it may also trigger bugs, leading to modification or loss of production data. Only run DAST scans against a test server.
+
+The DAST browser-based analyzer was built by GitLab to scan modern-day web applications for vulnerabilities.
+Scans run in a browser to optimize testing applications heavily dependent on JavaScript, such as single-page applications.
+See [how DAST scans an application](#how-dast-scans-an-application) for more information.
+
+To add the analyzer to your CI/CD pipeline, see [getting started](#getting-started).
+
+## How DAST scans an application
+
+A scan performs the following steps:
+
+1. [Authenticate](authentication.md), if configured.
+1. [Crawl](#crawling-an-application) the target application to discover the surface area of the application by performing user actions such as following links, clicking buttons, and filling out forms.
+1. [Passive scan](#passive-scans) to search for vulnerabilities in HTTP messages and pages discovered while crawling.
+1. [Active scan](#active-scans) to search for vulnerabilities by injecting payloads into HTTP requests recorded during the crawl phase.
+
+### Crawling an application
+
+A "navigation" is an action a user might take on a page, such as clicking buttons, clicking anchor links, opening menu items, or filling out forms.
+A "navigation path" is a sequence of navigation actions representing how a user might traverse an application.
+DAST discovers the surface area of an application by crawling pages and content and identifying navigation paths.
+
+Crawling is initialized with a navigation path containing one navigation that loads the target application URL in a specially-instrumented Chromium browser.
+DAST then crawls navigation paths until all have been crawled.
+
+To crawl a navigation path, DAST opens a browser window and instructs it to perform all the navigation actions in the navigation path.
+When the browser has finished loading the result of the final action, DAST inspects the page for actions a user might take,
+creates a new navigation for each found, and adds them to the navigation path to form new navigation paths. For example:
+
+- DAST processes navigation path `LoadURL[https://example.com]`.
+- DAST finds two user actions, `LeftClick[class=menu]` and `LeftClick[id=users]`.
+- DAST creates two new navigation paths, `LoadURL[https://example.com] -> LeftClick[class=menu]` and `LoadURL[https://example.com] -> LeftClick[id=users]`.
+- Crawling begins on the two new navigation paths.
+
+It's common for an HTML element to exist in multiple places in an application, such as a menu visible on every page.
+Duplicate elements can cause crawlers to crawl the same pages again or become stuck in a loop.
+DAST uses an element uniqueness calculation based on HTML attributes to discard new navigation actions it has previously crawled.
+
+### Passive scans
+
+Passive scans check for vulnerabilities in the pages discovered during the crawl phase of the scan.
+Passive scans are enabled by default.
+
+The checks search HTTP messages, cookies, storage events, console events, and DOM for vulnerabilities.
+Examples of passive checks include searching for exposed credit cards, exposed secret tokens, missing content security policies, and redirection to untrusted locations.
+
+See [checks](checks/index.md) for more information about individual checks.
+
+### Active scans
+
+Active scans check for vulnerabilities by injecting attack payloads into HTTP requests recorded during the crawl phase of the scan.
+Active scans are disabled by default due to the nature of their probing attacks.
+
+DAST analyzes each recorded HTTP request for injection locations, such as query values, header values, cookie values, form posts, and JSON string values.
+Attack payloads are injected into the injection location, forming a new request.
+DAST sends the request to the target application and uses the HTTP response to determine attack success.
+
+Active scans run two types of active check:
+
+- A match response attack analyzes the response content to determine attack success. For example, if an attack attempts to read the system password file, a finding is created when the response body contains evidence of the password file.
+- A timing attack uses the response time to determine attack success. For example, if an attack attempts to force the target application to sleep, a finding is created when the application takes longer to respond than the sleep time. Timing attacks are repeated multiple times with different attack payloads to minimize false positives.
+
+A simplified timing attack works as follows:
+
+1. The crawl phase records the HTTP request `https://example.com?search=people`.
+1. DAST analyzes the URL and finds a URL parameter injection location `https://example.com?search=[INJECT]`.
+1. The active check defines a payload, `sleep 10`, that attempts to get a Linux host to sleep.
+1. DAST send a new HTTP request to the target application with the injected payload `https://example.com?search=sleep%2010`.
+1. The target application is vulnerable if it executes the query parameter value as a system command without validation, for example, `system(params[:search])`
+1. DAST creates a finding if the response time takes longer than 10 seconds.
+
+## Getting started
+
+To run a DAST scan:
+
+- Read the [prerequisite](index.md#prerequisites) conditions for running a DAST scan.
+- Create a [DAST job](#create-a-dast-cicd-job) in your CI/CD pipeline.
+- [Authenticate](#authentication) as a user if your application requires it.
+
+### Create a DAST CI/CD job
+
+> - This template was [updated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62597) to DAST_VERSION: 2 in
+ GitLab 14.0.
+> - This template was [updated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87183) to DAST_VERSION: 3 in
+ GitLab 15.0.
+
+To add DAST scanning to your application, use the DAST job defined
+in the GitLab DAST CI/CD template file. Updates to the template are provided with GitLab
+upgrades, allowing you to benefit from any improvements and additions.
+
+To create the CI/CD job:
+
+1. Include the appropriate CI/CD template:
-The browser-based scanner works by loading the target application into a specially-instrumented
-Chromium browser. A snapshot of the page is taken before a search to find any actions that a user
-might perform, such as selecting on a link or filling in a form. For each action found, the
-browser-based scanner executes it, takes a new snapshot, and determines what in the page changed
-from the previous snapshot. Crawling continues by taking more snapshots and finding subsequent
-actions. The benefit of scanning by following user actions in a browser is that the crawler can
-interact with the target application much like a real user would, identifying complex flows that
-traditional web crawlers don't understand. This results in better coverage of the website.
+ - [`DAST.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml):
+ Stable version of the DAST CI/CD template.
+ - [`DAST.latest.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml):
+ Latest version of the DAST template. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/254325)
+ in GitLab 13.8).
-The browser-based scanner should provide greater coverage for most web applications, compared
-with the current DAST AJAX crawler. While both crawlers are
-used together with the current DAST scanner, the combination of the browser-based crawler with the
-current DAST scanner is much more effective at finding and testing every page in an application.
+ WARNING:
+ The latest version of the template may include breaking changes. Use the
+ stable template unless you need a feature provided only in the latest template.
-## Enable browser-based analyzer
+ For more information about template versioning, see the
+ [CI/CD documentation](../../../development/cicd/templates.md#latest-version).
-To enable the browser-based analyzer:
+1. Add a `dast` stage to your GitLab CI/CD stages configuration.
-1. Ensure the DAST [prerequisites](index.md#prerequisites) are met.
-1. Include the [DAST CI/CD template](proxy-based.md#include-the-dast-template).
-1. Set the target website using the [`DAST_WEBSITE` CI/CD variable](proxy-based.md#available-cicd-variables).
-1. Set the CI/CD variable `DAST_BROWSER_SCAN` to `true`.
+1. Define the URL to be scanned by DAST by using one of these methods:
-Example extract of `.gitlab-ci.yml` file:
+ - Set the `DAST_WEBSITE` [CI/CD variable](../../../ci/yaml/index.md#variables).
+ If set, this value takes precedence.
+
+ - Adding the URL in an `environment_url.txt` file at your project's root is great for testing in
+ dynamic environments. To run DAST against an application dynamically created during a GitLab CI/CD
+ pipeline, write the application URL to an `environment_url.txt` file. DAST automatically reads the
+ URL to find the scan target.
+
+ You can see an [example of this in our Auto DevOps CI YAML](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml).
+
+1. Set the `DAST_BROWSER_SCAN` [CI/CD variable](../../../ci/yaml/index.md#variables) to `"true"`.
+
+For example:
```yaml
+stages:
+ - build
+ - test
+ - deploy
+ - dast
+
include:
- template: DAST.gitlab-ci.yml
@@ -52,36 +157,58 @@ dast:
DAST_BROWSER_SCAN: "true"
```
+### Authentication
+
+The browser-based analyzer can authenticate a user prior to a scan. See [Authentication](authentication.md) for
+configuration instructions.
+
### Available CI/CD variables
-The browser-based crawler can be configured using CI/CD variables.
-
-| CI/CD variable | Type | Example | Description |
-|----------------------------------------------| ----------------| --------------------------------- | ------------|
-| `DAST_WEBSITE` | URL | `http://www.site.com` | The URL of the website to scan. |
-| `DAST_BROWSER_SCAN` | boolean | `true` | Configures DAST to use the browser-based crawler engine. |
-| `DAST_BROWSER_ALLOWED_HOSTS` | List of strings | `site.com,another.com` | Hostnames included in this variable are considered in scope when crawled. By default the `DAST_WEBSITE` hostname is included in the allowed hosts list. |
-| `DAST_BROWSER_EXCLUDED_HOSTS` | List of strings | `site.com,another.com` | Hostnames included in this variable are considered excluded and connections are forcibly dropped. |
-| `DAST_BROWSER_EXCLUDED_ELEMENTS` | selector | `a[href='2.html'],css:.no-follow` | Comma-separated list of selectors that are ignored when scanning. |
-| `DAST_BROWSER_IGNORED_HOSTS` | List of strings | `site.com,another.com` | Hostnames included in this variable are accessed but not reported against. |
-| `DAST_BROWSER_MAX_ACTIONS` | number | `10000` | The maximum number of actions that the crawler performs. For example, selecting a link, or filling a form. |
-| `DAST_BROWSER_MAX_DEPTH` | number | `10` | The maximum number of chained actions that the crawler takes. For example, `Click -> Form Fill -> Click` is a depth of three. |
-| `DAST_BROWSER_NUMBER_OF_BROWSERS` | number | `3` | The maximum number of concurrent browser instances to use. For shared runners on GitLab.com, we recommended a maximum of three. Private runners with more resources may benefit from a higher number, but are likely to produce little benefit after five to seven instances. |
-| `DAST_BROWSER_COOKIES` | dictionary | `abtesting_group:3,region:locked` | A cookie name and value to be added to every request. |
-| `DAST_BROWSER_LOG` | List of strings | `brows:debug,auth:debug` | A list of modules and their intended log level. |
-| `DAST_BROWSER_NAVIGATION_TIMEOUT` | [Duration string](https://pkg.go.dev/time#ParseDuration) | `15s` | The maximum amount of time to wait for a browser to navigate from one page to another. |
-| `DAST_BROWSER_ACTION_TIMEOUT` | [Duration string](https://pkg.go.dev/time#ParseDuration) | `7s` | The maximum amount of time to wait for a browser to complete an action. |
-| `DAST_BROWSER_STABILITY_TIMEOUT` | [Duration string](https://pkg.go.dev/time#ParseDuration) | `7s` | The maximum amount of time to wait for a browser to consider a page loaded and ready for analysis. |
-| `DAST_BROWSER_NAVIGATION_STABILITY_TIMEOUT` | [Duration string](https://pkg.go.dev/time#ParseDuration) | `7s` | The maximum amount of time to wait for a browser to consider a page loaded and ready for analysis after a navigation completes. |
-| `DAST_BROWSER_ACTION_STABILITY_TIMEOUT` | [Duration string](https://pkg.go.dev/time#ParseDuration) | `800ms` | The maximum amount of time to wait for a browser to consider a page loaded and ready for analysis after completing an action. |
-| `DAST_BROWSER_SEARCH_ELEMENT_TIMEOUT` | [Duration string](https://pkg.go.dev/time#ParseDuration) | `3s` | The maximum amount of time to allow the browser to search for new elements or navigations. |
-| `DAST_BROWSER_EXTRACT_ELEMENT_TIMEOUT` | [Duration string](https://pkg.go.dev/time#ParseDuration) | `5s` | The maximum amount of time to allow the browser to extract newly found elements or navigations. |
-| `DAST_BROWSER_ELEMENT_TIMEOUT` | [Duration string](https://pkg.go.dev/time#ParseDuration) | `600ms` | The maximum amount of time to wait for an element before determining it is ready for analysis. |
-| `DAST_BROWSER_PAGE_READY_SELECTOR` | selector | `css:#page-is-ready` | Selector that when detected as visible on the page, indicates to the analyzer that the page has finished loading and the scan can continue. Note: When this selector is set, but the element is not found, the scanner waits for the period defined in `DAST_BROWSER_STABILITY_TIMEOUT` before continuing the scan. This can significantly increase scanning time if the element is not present on multiple pages within the site. |
-
-The [DAST variables](proxy-based.md#available-cicd-variables) `SECURE_ANALYZERS_PREFIX`, `DAST_FULL_SCAN_ENABLED`, `DAST_AUTO_UPDATE_ADDONS`, `DAST_EXCLUDE_RULES`, `DAST_REQUEST_HEADERS`, `DAST_HTML_REPORT`, `DAST_MARKDOWN_REPORT`, `DAST_XML_REPORT`,
-`DAST_AUTH_URL`, `DAST_USERNAME`, `DAST_PASSWORD`, `DAST_USERNAME_FIELD`, `DAST_PASSWORD_FIELD`, `DAST_FIRST_SUBMIT_FIELD`, `DAST_SUBMIT_FIELD`, `DAST_EXCLUDE_URLS`, `DAST_AUTH_VERIFICATION_URL`, `DAST_BROWSER_AUTH_VERIFICATION_SELECTOR`, `DAST_BROWSER_AUTH_VERIFICATION_LOGIN_FORM`, `DAST_BROWSER_AUTH_REPORT`,
-`DAST_INCLUDE_ALPHA_VULNERABILITIES`, `DAST_PATHS_FILE`, `DAST_PATHS`, `DAST_ZAP_CLI_OPTIONS`, and `DAST_ZAP_LOG_CONFIGURATION` are also compatible with browser-based crawler scans.
+These CI/CD variables are specific to the browser-based DAST analyzer. They can be used to customize the behavior of
+DAST to your requirements.
+For authentication CI/CD variables, see [Authentication](authentication.md).
+
+| CI/CD variable | Type | Example | Description |
+|:--------------------------------------------|:---------------------------------------------------------|----------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `DAST_ADVERTISE_SCAN` | boolean | `true` | Set to `true` to add a `Via` header to every request sent, advertising that the request was sent as part of a GitLab DAST scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/334947) in GitLab 14.1. |
+| `DAST_BROWSER_ACTION_STABILITY_TIMEOUT` | [Duration string](https://pkg.go.dev/time#ParseDuration) | `800ms` | The maximum amount of time to wait for a browser to consider a page loaded and ready for analysis after completing an action. |
+| `DAST_BROWSER_ACTION_TIMEOUT` | [Duration string](https://pkg.go.dev/time#ParseDuration) | `7s` | The maximum amount of time to wait for a browser to complete an action. |
+| `DAST_BROWSER_ALLOWED_HOSTS` | List of strings | `site.com,another.com` | Hostnames included in this variable are considered in scope when crawled. By default the `DAST_WEBSITE` hostname is included in the allowed hosts list. |
+| `DAST_BROWSER_COOKIES` | dictionary | `abtesting_group:3,region:locked` | A cookie name and value to be added to every request. |
+| `DAST_BROWSER_CRAWL_GRAPH` | boolean | `true` | Set to `true` to generate an SVG graph of navigation paths visited during crawl phase of the scan. |
+| `DAST_BROWSER_DEVTOOLS_LOG` | string | `Default:messageAndBody,truncate:2000` | Set to log protocol messages between DAST and the Chromium browser. | |
+| `DAST_BROWSER_ELEMENT_TIMEOUT` | [Duration string](https://pkg.go.dev/time#ParseDuration) | `600ms` | The maximum amount of time to wait for an element before determining it is ready for analysis. |
+| `DAST_BROWSER_EXCLUDED_ELEMENTS` | selector | `a[href='2.html'],css:.no-follow` | Comma-separated list of selectors that are ignored when scanning. |
+| `DAST_BROWSER_EXCLUDED_HOSTS` | List of strings | `site.com,another.com` | Hostnames included in this variable are considered excluded and connections are forcibly dropped. |
+| `DAST_BROWSER_EXTRACT_ELEMENT_TIMEOUT` | [Duration string](https://pkg.go.dev/time#ParseDuration) | `5s` | The maximum amount of time to allow the browser to extract newly found elements or navigations. |
+| `DAST_BROWSER_FILE_LOG` | List of strings | `brows:debug,auth:debug` | A list of modules and their intended logging level for use in the file log. |
+| `DAST_BROWSER_FILE_LOG_PATH` | string | `/output/browserker.log` | Set to the path of the file log. |
+| `DAST_BROWSER_IGNORED_HOSTS` | List of strings | `site.com,another.com` | Hostnames included in this variable are accessed, not attacked, and not reported against. |
+| `DAST_BROWSER_INCLUDE_ONLY_RULES` | List of strings | `16.1,16.2,16.3` | Comma-separated list of check identifiers to use for the scan. |
+| `DAST_BROWSER_LOG` | List of strings | `brows:debug,auth:debug` | A list of modules and their intended logging level for use in the console log. |
+| `DAST_BROWSER_LOG_CHROMIUM_OUTPUT` | boolean | `true` | Set to `true` to log Chromium `STDOUT` and `STDERR`. |
+| `DAST_BROWSER_MAX_ACTIONS` | number | `10000` | The maximum number of actions that the crawler performs. For example, selecting a link, or filling a form. |
+| `DAST_BROWSER_MAX_DEPTH` | number | `10` | The maximum number of chained actions that the crawler takes. For example, `Click -> Form Fill -> Click` is a depth of three. |
+| `DAST_BROWSER_MAX_RESPONSE_SIZE_MB` | number | `15` | The maximum size of a HTTP response body. Responses with bodies larger than this are blocked by the browser. Defaults to 10 MB. |
+| `DAST_BROWSER_NAVIGATION_STABILITY_TIMEOUT` | [Duration string](https://pkg.go.dev/time#ParseDuration) | `7s` | The maximum amount of time to wait for a browser to consider a page loaded and ready for analysis after a navigation completes. |
+| `DAST_BROWSER_NAVIGATION_TIMEOUT` | [Duration string](https://pkg.go.dev/time#ParseDuration) | `15s` | The maximum amount of time to wait for a browser to navigate from one page to another. |
+| `DAST_BROWSER_NUMBER_OF_BROWSERS` | number | `3` | The maximum number of concurrent browser instances to use. For shared runners on GitLab.com, we recommended a maximum of three. Private runners with more resources may benefit from a higher number, but are likely to produce little benefit after five to seven instances. |
+| `DAST_BROWSER_PAGE_READY_SELECTOR` | selector | `css:#page-is-ready` | Selector that when detected as visible on the page, indicates to the analyzer that the page has finished loading and the scan can continue. |
+| `DAST_BROWSER_SCAN` | boolean | `true` | Required to be `true` to run a browser-based scan. |
+| `DAST_BROWSER_SEARCH_ELEMENT_TIMEOUT` | [Duration string](https://pkg.go.dev/time#ParseDuration) | `3s` | The maximum amount of time to allow the browser to search for new elements or user actions. |
+| `DAST_BROWSER_STABILITY_TIMEOUT` | [Duration string](https://pkg.go.dev/time#ParseDuration) | `7s` | The maximum amount of time to wait for a browser to consider a page loaded and ready for analysis. |
+| `DAST_EXCLUDE_RULES` | string | `10020,10026` | Set to a comma-separated list of ZAP Vulnerability Rule IDs to exclude them from running during the scan. Rule IDs are numbers and can be found from the DAST log or on the [ZAP project](https://www.zaproxy.org/docs/alerts/). |
+| `DAST_EXCLUDE_URLS` | URLs | `https://example.com/.*/sign-out` | The URLs to skip during the authenticated scan; comma-separated. Regular expression syntax can be used to match multiple URLs. For example, `.*` matches an arbitrary character sequence. |
+| `DAST_FULL_SCAN_ENABLED` | boolean | `true` | Set to `true` to run both passive and active checks. Default: `false` |
+| `DAST_PATHS` | string | `/page1.html,/category1/page3.html` | Set to a comma-separated list of URL paths relative to `DAST_WEBSITE` for DAST to scan. |
+| `DAST_PATHS_FILE` | string | `/builds/project/urls.txt` | Set to a file path containing a list of URL paths relative to `DAST_WEBSITE` for DAST to scan. The file must be plain text with one path per line. |
+| `DAST_PKCS12_CERTIFICATE_BASE64` | string | `ZGZkZ2p5NGd...` | The PKCS12 certificate used for sites that require Mutual TLS. Must be encoded as base64 text. |
+| `DAST_PKCS12_PASSWORD` | string | `password` | The password of the certificate used in `DAST_PKCS12_CERTIFICATE_BASE64`. Create sensitive [custom CI/CI variables](../../../ci/variables/index.md#custom-cicd-variables) using the GitLab UI. |
+| `DAST_REQUEST_HEADERS` | string | `Cache-control:no-cache` | Set to a comma-separated list of request header names and values. Headers are added to every request made to `DAST_BROWSER_ALLOWED_HOSTS` by DAST. |
+| `DAST_SKIP_TARGET_CHECK` | boolean | `true` | Set to `true` to prevent DAST from checking that the target is available before scanning. Default: `false`. |
+| `DAST_TARGET_AVAILABILITY_TIMEOUT` | number | `60` | Time limit in seconds to wait for target availability. |
+| `DAST_WEBSITE` | URL | `https://example.com` | The URL of the website to scan. |
+| `SECURE_ANALYZERS_PREFIX` | URL | `registry.organization.com` | Set the Docker registry base address from which to download the analyzer. |
## Vulnerability detection
@@ -150,53 +277,8 @@ dast:
NOTE:
Adjusting these values may impact scan time because they adjust how long each browser waits for various activities to complete.
-## Debugging scans using logging
-
-Logging can be used to help you troubleshoot a scan.
-
-The CI/CD variable `DAST_BROWSER_LOG` configures the logging level for particular modules of the crawler. Each module represents a component of the browser-based crawler and is separated so that debug logs can be configured just for the area of the crawler that requires further inspection. For more details, see [Crawler modules](#crawler-modules).
-
-For example, the following job definition enables the browsing module and the authentication module to be logged in debug-mode:
-
-```yaml
-include:
- - template: DAST.gitlab-ci.yml
-
-dast:
- variables:
- DAST_WEBSITE: "https://my.site.com"
- DAST_BROWSER_SCAN: "true"
- DAST_BROWSER_LOG: "brows:debug,auth:debug"
-```
-
-### Log message format
+## Artifacts
-Log messages have the format `[time] [log level] [log module] [message] [additional properties]`. For example, the following log entry has level `INFO`, is part of the `CRAWL` log module, and has the message `Crawled path`.
-
-```txt
-2021-04-21T00:34:04.000 INF CRAWL Crawled path nav_id=0cc7fd path="LoadURL [https://my.site.com:8090]"
-```
-
-### Crawler modules
-
-The modules that can be configured for logging are as follows:
-
-| Log module | Component overview |
-| ---------- | ----------- |
-| `AUTH` | Used for creating an authenticated scan. |
-| `BROWS` | Used for querying the state or page of the browser. |
-| `BPOOL` | The set of browsers that are leased out for crawling. |
-| `CRAWL` | Used for the core crawler algorithm. |
-| `DATAB` | Used for persisting data to the internal database. |
-| `LEASE` | Used to create browsers to add them to the browser pool. |
-| `MAIN` | Used for the flow of the main event loop of the crawler. |
-| `NAVDB` | Used for persistence mechanisms to store navigation entries. |
-| `REPT` | Used for generating reports. |
-| `STAT` | Used for general statistics while running the scan. |
-
-### Artifacts
-
-DAST's browser-based analyzer generates artifacts that can help you understand how the scanner works.
Using the latest version of the DAST [template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml) these artifacts are exposed for download by default.
The list of artifacts includes the following files:
@@ -204,3 +286,7 @@ The list of artifacts includes the following files:
- `gl-dast-debug-auth-report.html`
- `gl-dast-debug-crawl-report.html`
- `gl-dast-crawl-graph.svg`
+
+## Troubleshooting
+
+See [troubleshooting](browser_based_troubleshooting.md) for more information.
diff --git a/doc/user/application_security/dast/browser_based_troubleshooting.md b/doc/user/application_security/dast/browser_based_troubleshooting.md
new file mode 100644
index 00000000000..78f2723ee38
--- /dev/null
+++ b/doc/user/application_security/dast/browser_based_troubleshooting.md
@@ -0,0 +1,300 @@
+---
+stage: Secure
+group: Dynamic Analysis
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+type: reference, howto
+---
+
+# Troubleshooting DAST browser-based analyzer **(ULTIMATE)**
+
+The following troubleshooting scenarios have been collected from customer support cases. If you
+experience a problem not addressed here, or the information here does not fix your problem, create a
+support ticket. For more details, see the [GitLab Support](https://about.gitlab.com/support/) page.
+
+## When something goes wrong
+
+When something goes wrong with a DAST scan, if you have a particular error message then check [known problems](#known-problems).
+
+Otherwise, try to discover the problem by answering the following questions:
+
+- [What is the expected outcome?](#what-is-the-expected-outcome)
+- [Is the outcome achievable by a human?](#is-the-outcome-achievable-by-a-human)
+- [Any reason why DAST would not work?](#any-reason-why-dast-would-not-work)
+- [How does your application work?](#how-does-your-application-work)
+- [What is DAST doing?](#what-is-dast-doing)
+
+### What is the expected outcome?
+
+Many users who encounter issues with a DAST scan have a good high-level idea of what they think the scanner should be doing. For example,
+it's not scanning particular pages, or it's not selecting a button on the page.
+
+As much as possible, try to isolate the problem to help narrow the search for a solution. For example, take the situation where DAST isn't scanning a particular page.
+From where should DAST have found the page? What path did it take to navigate there? Were there elements on the referring page that DAST should have selected, but did not?
+
+### Is the outcome achievable by a human?
+
+DAST cannot scan an application if a human cannot manually traverse the application.
+
+Knowing the outcome you expect, try to replicate it manually using a browser on your machine. For example:
+
+- Open a new incognito/private browser window.
+- Open Developer Tools. Keep an eye on the console for error messages.
+ - In Chrome: `View -> Developer -> Developer Tools`.
+ - In Firefox: `Tools -> Browser Tools -> Web Developer Tools`.
+- If authenticating:
+ - Navigate to the `DAST_AUTH_URL`.
+ - Type in the `DAST_USERNAME` in the `DAST_USERNAME_FIELD`.
+ - Type in the `DAST_PASSWORD` in the `DAST_PASSWORD_FIELD`.
+ - Select the `DAST_SUBMIT_FIELD`.
+- Select links and fill in forms. Navigate to the pages that aren't scanning correctly.
+- Observe how your application behaves. Notice if there is anything that might cause problems for an automated scanner.
+
+### Any reason why DAST would not work?
+
+DAST cannot scan correctly when:
+
+- There is a CAPTCHA. Please turn these off in the testing environment for the application being scanned.
+- It does not have access to the target application. Ensure the GitLab Runner can access the application using the URLs used in the DAST configuration.
+
+### How does your application work?
+
+Understanding how your application works is vital to figuring out why a DAST scan isn't working. For example, the following situations
+may require additional configuration settings.
+
+- Is there a popup modal that hides elements?
+- Does a loaded page change dramatically after a certain period of time?
+- Is the application especially slow or fast to load?
+- Is the target application jerky while loading?
+- Does the application work differently based on the client's location?
+- Is the application a single-page application?
+- Does the application submit HTML forms, or does it use JavaScript and AJAX?
+- Does the application use websockets?
+- Does the application use a specific web framework?
+- Does selecting buttons run JavaScript before continuing the form submit? Is it fast, slow?
+- Is it possible DAST could be selecting or searching for elements before either the element or page is ready?
+
+### What is DAST doing?
+
+Logging remains the best way to understand what DAST is doing:
+
+- [Browser-based analyzer logging](#browser-based-analyzer-logging), useful for understanding what the analyzer is doing.
+- [Chromium DevTools logging](#chromium-devtools-logging), useful to inspect the communication between DAST and Chromium.
+- [Chromium Logs](#chromium-logs), useful for logging errors when Chromium crashes unexpectedly.
+
+## Browser-based analyzer logging
+
+The analyzer log is one of the most useful tools to help diagnose problems with a scan. Different parts of the analyzer can be logged at different levels.
+
+### Log message format
+
+Log messages have the format `[time] [log level] [log module] [message] [additional properties]`.
+
+For example, the following log entry has level `INFO`, is part of the `CRAWL` log module, has the message `Crawled path` and the additional properties `nav_id` and `path`.
+
+```txt
+2021-04-21T00:34:04.000 INF CRAWL Crawled path nav_id=0cc7fd path="LoadURL [https://my.site.com:8090]"
+```
+
+### Log destination
+
+Logs are sent either to file or to console (the CI/CD job log). You can configure each destination to accept different logs using
+the environment variables `DAST_BROWSER_LOG` for console logs and `DAST_BROWSER_FILE_LOG` for file logs.
+
+In the following example, the file log defaults to `DEBUG` level, the console log defaults to `INFO` level and logs the `AUTH` module at `DEBUG` level.
+
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+
+dast:
+ variables:
+ DAST_BROWSER_LOG: "auth:debug"
+ DAST_BROWSER_FILE_LOG: "loglevel:debug"
+ DAST_BROWSER_FILE_LOG_PATH: "/zap/wrk/dast-scan.log"
+ artifacts:
+ paths:
+ - dast-scan.log
+ when: always
+```
+
+### Log levels
+
+The log levels that can be configured are as follows:
+
+| Log module | Component overview | More |
+|-------------------------|--------------------------------------------------------------------------|----------------------------------|
+| `TRACE` | Used for specific, often noisy inner workings of a feature. | |
+| `DEBUG` | Describes the inner-workings of a feature. Used for diagnostic purposes. | |
+| `INFO` | Describes the high level flow of the scan and the results. | Default level if none specified. |
+| `WARN` | Describes an error situation where DAST recovers and continues the scan. | |
+| `FATAL`/`ERROR`/`PANIC` | Describes unrecoverable errors prior to exit. | |
+
+### Log modules
+
+`LOGLEVEL` configures the default log level for the log destination. If any of the following modules are configured,
+DAST uses the log level for that module in preference to the default log level.
+
+The modules that can be configured for logging are as follows:
+
+| Log module | Component overview |
+|------------|---------------------------------------------------------------------------------------------------|
+| `ACTIV` | Used for active attacks. |
+| `AUTH` | Used for creating an authenticated scan. |
+| `BPOOL` | The set of browsers that are leased out for crawling. |
+| `BROWS` | Used for querying the state or page of the browser. |
+| `CACHE` | Used for reporting on cache hit and miss for cached HTTP resources. |
+| `CHROM` | Used to log Chrome DevTools messages. |
+| `CONTA` | Used for the container that collects parts of HTTP requests and responses from DevTools messages. |
+| `CRAWL` | Used for the core crawler algorithm. |
+| `DATAB` | Used for persisting data to the internal database. |
+| `LEASE` | Used to create browsers to add them to the browser pool. |
+| `MAIN` | Used for the flow of the main event loop of the crawler. |
+| `NAVDB` | Used for persistence mechanisms to store navigation entries. |
+| `REGEX` | Used for recording performance statistics when running regular expressions. |
+| `REPT` | Used for generating reports. |
+| `STAT` | Used for general statistics while running the scan. |
+| `VLDFN` | Used for loading and parsing vulnerability definitions. |
+| `WEBGW` | Used to log messages sent to the target application when running active checks. |
+
+### Example - log crawled paths
+
+Set the log module `CRAWL` to `DEBUG` to log navigation paths found during the crawl phase of the scan. This is useful for understanding
+if DAST is crawling your target application correctly.
+
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+
+dast:
+ variables:
+ DAST_BROWSER_LOG: "crawl:debug"
+```
+
+For example, the following output shows that four anchor links we discovered during the crawl of the page at `https://example.com`.
+
+```plaintext
+2022-11-17T11:18:05.578 DBG CRAWL executing step nav_id=6ec647d8255c729160dd31cb124e6f89 path="LoadURL [https://example.com]" step=1
+...
+2022-11-17T11:18:11.900 DBG CRAWL found new navigations browser_id=2243909820020928961 nav_count=4 nav_id=6ec647d8255c729160dd31cb124e6f89 of=1 step=1
+2022-11-17T11:18:11.901 DBG CRAWL adding navigation action="LeftClick [a href=/page1.html]" nav=bd458cc1fc2d7c6fb984464b6d968866 parent_nav=6ec647d8255c729160dd31cb124e6f89
+2022-11-17T11:18:11.901 DBG CRAWL adding navigation action="LeftClick [a href=/page2.html]" nav=6dcb25f9f9ece3ee0071ac2e3166d8e6 parent_nav=6ec647d8255c729160dd31cb124e6f89
+2022-11-17T11:18:11.901 DBG CRAWL adding navigation action="LeftClick [a href=/page3.html]" nav=89efbb0c6154d6c6d85a63b61a7cdc6f parent_nav=6ec647d8255c729160dd31cb124e6f89
+2022-11-17T11:18:11.901 DBG CRAWL adding navigation action="LeftClick [a href=/page4.html]" nav=f29b4f4e0bdee70f5255de7fc080f04d parent_nav=6ec647d8255c729160dd31cb124e6f89
+```
+
+## Chromium DevTools logging
+
+WARNING:
+Logging DevTools messages is a security risk. The output contains secrets such as usernames, passwords and authentication tokens.
+The output is uploaded to the GitLab server and may be visible in job logs.
+
+The DAST Browser-based scanner orchestrates a Chromium browser using the [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/).
+Logging DevTools messages helps provide transparency into what the browser is doing. For example, if selecting a button does not work, a DevTools message might show that the cause is a CORS error in a browser console log.
+Logs that contain DevTools messages can be very large in size. For this reason, it should only be enabled on jobs with a short duration.
+
+To log all DevTools messages, turn the `CHROM` log module to `trace` and configure logging levels. The following are examples of DevTools logs:
+
+```plaintext
+2022-12-05T06:27:24.280 TRC CHROM event received {"method":"Fetch.requestPaused","params":{"requestId":"interception-job-3.0","request":{"url":"http://auth-auto:8090/font-awesome.min.css","method":"GET","headers":{"Accept":"text/css,*/*;q=0.1","Referer":"http://auth-auto:8090/login.html","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/105.0.5195.102 Safari/537.36"},"initialPriority":"VeryHigh","referrerPolicy":"strict-origin-when-cross-origin"},"frameId":"A706468B01C2FFAA2EB6ED365FF95889","resourceType":"Stylesheet","networkId":"39.3"}} method=Fetch.requestPaused
+2022-12-05T06:27:24.280 TRC CHROM request sent {"id":47,"method":"Fetch.continueRequest","params":{"requestId":"interception-job-3.0","headers":[{"name":"Accept","value":"text/css,*/*;q=0.1"},{"name":"Referer","value":"http://auth-auto:8090/login.html"},{"name":"User-Agent","value":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/105.0.5195.102 Safari/537.36"}]}} id=47 method=Fetch.continueRequest
+2022-12-05T06:27:24.281 TRC CHROM response received {"id":47,"result":{}} id=47 method=Fetch.continueRequest
+```
+
+### Customizing DevTools log levels
+
+Chrome DevTools requests, responses and events are namespaced by domain. DAST allows each domain and each domain with message to have different logging configuration.
+The environment variable `DAST_BROWSER_DEVTOOLS_LOG` accepts a semi-colon separated list of logging configurations.
+Logging configurations are declared using the structure `[domain/message]:[what-to-log][,truncate:[max-message-size]]`.
+
+- `domain/message` references what is being logged.
+ - `Default` can be used as a value to represent all domains and messages.
+ - Can be a domain, for example, `Browser`, `CSS`, `Page`, `Network`.
+ - Can be a domain with a message, for example, `Network.responseReceived`.
+ - If multiple configurations apply, the most specific configuration is used.
+- `what-to-log` references whether and what to log.
+ - `message` logs that a message was received and does not log the message content.
+ - `messageAndBody` logs the message with the message content. Recommended to be used with `truncate`.
+ - `suppress` does not log the message. Used to silence noisy domains and messages.
+- `truncate` is an optional configuration to limit the size of the message printed.
+
+### Example - log all DevTools messages
+
+Used to log everything when you're not sure where to start.
+
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+
+dast:
+ variables:
+ DAST_BROWSER_FILE_LOG: "chrom:trace"
+ DAST_BROWSER_FILE_LOG_PATH: "/zap/wrk/dast-scan.log"
+ DAST_BROWSER_DEVTOOLS_LOG: "Default:messageAndBody,truncate:2000"
+ artifacts:
+ paths:
+ - dast-scan.log
+ when: always
+```
+
+### Example - log HTTP messages
+
+Useful for when a resource isn't loading correctly. HTTP message events are logged, as is the decision to continue or
+fail the request. Any errors in the browser console are also logged.
+
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+
+dast:
+ variables:
+ DAST_BROWSER_FILE_LOG: "chrom:trace"
+ DAST_BROWSER_FILE_LOG_PATH: "/zap/wrk/dast-scan.log"
+ DAST_BROWSER_DEVTOOLS_LOG: "Default:suppress;Fetch:messageAndBody,truncate:2000;Network:messageAndBody,truncate:2000;Log:messageAndBody,truncate:2000;Console:messageAndBody,truncate:2000"
+ artifacts:
+ paths:
+ - dast-scan.log
+ when: always
+```
+
+## Chromium logs
+
+In the rare event that Chromium crashes, it can be helpful to write the Chromium process `STDOUT` and `STDERR` to log.
+Setting the environment variable `DAST_BROWSER_LOG_CHROMIUM_OUTPUT` to `true` achieves this purpose.
+
+DAST starts and stops many Chromium processes. DAST sends each process output to all log destinations with the log module `LEASE` and log level `INFO`.
+
+For example:
+
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+
+dast:
+ variables:
+ DAST_BROWSER_LOG_CHROMIUM_OUTPUT: "true"
+```
+
+## Known problems
+
+### Logs contain `response body exceeds allowed size`
+
+By default DAST processes HTTP requests where the HTTP response body is 10 MB or less. Otherwise, DAST blocks the response
+which can cause scans to fail. This constraint is intended to reduce memory consumption during a scan.
+
+An example log is as follows, where DAST blocked the JavaScript file found at `https://example.com/large.js` as it's size is greater than the limit:
+
+```plaintext
+2022-12-05T06:28:43.093 WRN BROWS response body exceeds allowed size allowed_size_bytes=1000000 browser_id=752944257619431212 nav_id=ae23afe2acbce2c537657a9112926f1a of=1 request_id=interception-job-2.0 response_size_bytes=9333408 step=1 url=https://example.com/large.js
+2022-12-05T06:28:58.104 WRN CONTA request failed, attempting to continue scan error=net::ERR_BLOCKED_BY_RESPONSE index=0 requestID=38.2 url=https://example.com/large.js
+```
+
+This can be changed using the configuration `DAST_MAX_RESPONSE_SIZE_MB`. For example,
+
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+
+dast:
+ variables:
+ DAST_MAX_RESPONSE_SIZE_MB: "25"
+```
diff --git a/doc/user/application_security/dast/checks/16.2.md b/doc/user/application_security/dast/checks/16.2.md
index a317b9418a1..2051b118009 100644
--- a/doc/user/application_security/dast/checks/16.2.md
+++ b/doc/user/application_security/dast/checks/16.2.md
@@ -40,5 +40,5 @@ the `Server` header.
- [CWE](https://cwe.mitre.org/data/definitions/16.html)
- [Apache ServerTokens](https://blog.mozilla.org/security/2016/08/26/mitigating-mime-confusion-attacks-in-firefox/)
-- [NGINX server_tokens](https://nginx.org/en/docs/http/ngx_http_core_module.html#server_tokens)
+- [NGINX `server_tokens`](https://nginx.org/en/docs/http/ngx_http_core_module.html#server_tokens)
- [IIS 10 Remove Server Header](https://learn.microsoft.com/en-us/iis/configuration/system.webserver/security/requestfiltering/#attributes)
diff --git a/doc/user/application_security/dast/checks/16.3.md b/doc/user/application_security/dast/checks/16.3.md
index d9e6f6f8d92..d1799baa517 100644
--- a/doc/user/application_security/dast/checks/16.3.md
+++ b/doc/user/application_security/dast/checks/16.3.md
@@ -32,4 +32,4 @@ information from the `X-Powered-By` header.
## Links
- [CWE](https://cwe.mitre.org/data/definitions/16.html)
-- [PHP expose_php](https://www.php.net/manual/en/ini.core.php#ini.expose-php)
+- [PHP `expose_php`](https://www.php.net/manual/en/ini.core.php#ini.expose-php)
diff --git a/doc/user/application_security/dast/checks/548.1.md b/doc/user/application_security/dast/checks/548.1.md
index b6907db5928..6cef8ccdb63 100644
--- a/doc/user/application_security/dast/checks/548.1.md
+++ b/doc/user/application_security/dast/checks/548.1.md
@@ -41,5 +41,5 @@ indexing.
- [CWE](https://cwe.mitre.org/data/definitions/548.html)
- [Apache Options](https://httpd.apache.org/docs/2.4/mod/core.html#options)
-- [NGINX autoindex](https://nginx.org/en/docs/http/ngx_http_autoindex_module.html)
-- [IIS directoryBrowse element](https://learn.microsoft.com/en-us/iis/configuration/system.webserver/directorybrowse)
+- [NGINX `autoindex`](https://nginx.org/en/docs/http/ngx_http_autoindex_module.html)
+- [IIS `directoryBrowse` element](https://learn.microsoft.com/en-us/iis/configuration/system.webserver/directorybrowse)
diff --git a/doc/user/application_security/dast/checks/798.33.md b/doc/user/application_security/dast/checks/798.33.md
index 536faefdb51..4761ac9d157 100644
--- a/doc/user/application_security/dast/checks/798.33.md
+++ b/doc/user/application_security/dast/checks/798.33.md
@@ -4,11 +4,11 @@ group: Dynamic Analysis
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Exposure of confidential secret or token Droneci Access Token
+# Exposure of confidential secret or token Drone CI Access Token
## Description
-The response body contains content that matches the pattern of a Droneci Access Token.
+The response body contains content that matches the pattern of a Drone CI Access Token.
Exposing this value could allow attackers to gain access to all resources granted by this token.
## Remediation
diff --git a/doc/user/application_security/dast/checks/798.49.md b/doc/user/application_security/dast/checks/798.49.md
index 7ea3a65fbfa..41a3e8ace3d 100644
--- a/doc/user/application_security/dast/checks/798.49.md
+++ b/doc/user/application_security/dast/checks/798.49.md
@@ -4,11 +4,11 @@ group: Dynamic Analysis
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Exposure of confidential secret or token Freshbooks Access Token
+# Exposure of confidential secret or token FreshBooks Access Token
## Description
-The response body contains content that matches the pattern of a Freshbooks Access Token.
+The response body contains content that matches the pattern of a FreshBooks Access Token.
Exposing this value could allow attackers to gain access to all resources granted by this token.
## Remediation
diff --git a/doc/user/application_security/dast/checks/798.65.md b/doc/user/application_security/dast/checks/798.65.md
index f2ebfb988b2..083bfec3350 100644
--- a/doc/user/application_security/dast/checks/798.65.md
+++ b/doc/user/application_security/dast/checks/798.65.md
@@ -4,11 +4,11 @@ group: Dynamic Analysis
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Exposure of confidential secret or token Launchdarkly Access Token
+# Exposure of confidential secret or token LaunchDarkly Access Token
## Description
-The response body contains content that matches the pattern of a Launchdarkly Access Token.
+The response body contains content that matches the pattern of a LaunchDarkly Access Token.
Exposing this value could allow attackers to gain access to all resources granted by this token.
## Remediation
diff --git a/doc/user/application_security/dast/checks/798.97.md b/doc/user/application_security/dast/checks/798.97.md
index d3035b05bbb..711288eba9c 100644
--- a/doc/user/application_security/dast/checks/798.97.md
+++ b/doc/user/application_security/dast/checks/798.97.md
@@ -4,11 +4,11 @@ group: Dynamic Analysis
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Exposure of confidential secret or token Rubygem API token
+# Exposure of confidential secret or token RubyGems API token
## Description
-The response body contains content that matches the pattern of a Rubygem API token.
+The response body contains content that matches the pattern of a RubyGems API token.
Exposing this value could allow attackers to gain access to all resources granted by this token.
## Remediation
diff --git a/doc/user/application_security/dast/checks/829.1.md b/doc/user/application_security/dast/checks/829.1.md
index f18634b72d9..7df250c2047 100644
--- a/doc/user/application_security/dast/checks/829.1.md
+++ b/doc/user/application_security/dast/checks/829.1.md
@@ -20,7 +20,7 @@ applications users would be protected from the malicious alterations.
All identified resources should be sourced from the same domain as the target application. If this is not
possible, it is strongly recommended that all `script` tags that implement `src` values, or `link` tags
that implement the `href` values include Sub-Resource Integrity. To generate SRI integrity values the
-[srihash](https://www.srihash.org/) tool can be used, or by running one of the following commands:
+[SRI hash](https://www.srihash.org/) tool can be used, or by running one of the following commands:
- `cat FILENAME.js | openssl dgst -sha384 -binary | openssl base64 -A`
- `shasum -b -a 384 FILENAME.js | awk '{ print $1 }' | xxd -r -p | base64`
diff --git a/doc/user/application_security/dast/checks/829.2.md b/doc/user/application_security/dast/checks/829.2.md
index 19490afe676..d9d3e5a6341 100644
--- a/doc/user/application_security/dast/checks/829.2.md
+++ b/doc/user/application_security/dast/checks/829.2.md
@@ -19,7 +19,7 @@ them with known good versions.
All identified resources should be sourced from the same domain as the target application. If this is not
possible, it is strongly recommended that all `script` tags that implement `src` values, or `link` tags
that implement the `href` values include Sub-Resource Integrity. To generate SRI integrity values the
-[srihash](https://www.srihash.org/) tool can be used, or by running one of the following commands:
+[SRI hash](https://www.srihash.org/) tool can be used, or by running one of the following commands:
- `cat FILENAME.js | openssl dgst -sha384 -binary | openssl base64 -A`
- `shasum -b -a 384 FILENAME.js | awk '{ print $1 }' | xxd -r -p | base64`
diff --git a/doc/user/application_security/dast/checks/index.md b/doc/user/application_security/dast/checks/index.md
index 9466734f9cf..56406b24586 100644
--- a/doc/user/application_security/dast/checks/index.md
+++ b/doc/user/application_security/dast/checks/index.md
@@ -69,7 +69,7 @@ The [DAST browser-based crawler](../browser_based.md) provides a number of vulne
| [798.30](798.30.md) | Exposure of confidential secret or token Dropbox API secret | High | Passive |
| [798.31](798.31.md) | Exposure of confidential secret or token Dropbox long lived API token | High | Passive |
| [798.32](798.32.md) | Exposure of confidential secret or token Dropbox short lived API token | High | Passive |
-| [798.33](798.33.md) | Exposure of confidential secret or token Droneci Access Token | High | Passive |
+| [798.33](798.33.md) | Exposure of confidential secret or token Drone CI Access Token | High | Passive |
| [798.34](798.34.md) | Exposure of confidential secret or token Duffel API token | High | Passive |
| [798.35](798.35.md) | Exposure of confidential secret or token Dynatrace API token | High | Passive |
| [798.36](798.36.md) | Exposure of confidential secret or token EasyPost API token | High | Passive |
@@ -84,7 +84,7 @@ The [DAST browser-based crawler](../browser_based.md) provides a number of vulne
| [798.46](798.46.md) | Exposure of confidential secret or token Flutterwave Secret Key | High | Passive |
| [798.47](798.47.md) | Exposure of confidential secret or token Flutterwave Encryption Key | High | Passive |
| [798.48](798.48.md) | Exposure of confidential secret or token Frame.io API token | High | Passive |
-| [798.49](798.49.md) | Exposure of confidential secret or token Freshbooks Access Token | High | Passive |
+| [798.49](798.49.md) | Exposure of confidential secret or token FreshBooks Access Token | High | Passive |
| [798.50](798.50.md) | Exposure of confidential secret or token GoCardless API token | High | Passive |
| [798.52](798.52.md) | Exposure of confidential secret or token GitHub Personal Access Token | High | Passive |
| [798.53](798.53.md) | Exposure of confidential secret or token GitHub OAuth Access Token | High | Passive |
@@ -99,7 +99,7 @@ The [DAST browser-based crawler](../browser_based.md) provides a number of vulne
| [798.62](798.62.md) | Exposure of confidential secret or token Kraken Access Token | High | Passive |
| [798.63](798.63.md) | Exposure of confidential secret or token Kucoin Access Token | High | Passive |
| [798.64](798.64.md) | Exposure of confidential secret or token Kucoin Secret Key | High | Passive |
-| [798.65](798.65.md) | Exposure of confidential secret or token Launchdarkly Access Token | High | Passive |
+| [798.65](798.65.md) | Exposure of confidential secret or token LaunchDarkly Access Token | High | Passive |
| [798.66](798.66.md) | Exposure of confidential secret or token Linear API Token | High | Passive |
| [798.67](798.67.md) | Exposure of confidential secret or token Linear Client Secret | High | Passive |
| [798.68](798.68.md) | Exposure of confidential secret or token LinkedIn Client ID | High | Passive |
@@ -126,7 +126,7 @@ The [DAST browser-based crawler](../browser_based.md) provides a number of vulne
| [798.94](798.94.md) | Exposure of confidential secret or token Private Key | High | Passive |
| [798.95](798.95.md) | Exposure of confidential secret or token Pulumi API token | High | Passive |
| [798.96](798.96.md) | Exposure of confidential secret or token PyPI upload token | High | Passive |
-| [798.97](798.97.md) | Exposure of confidential secret or token Rubygem API token | High | Passive |
+| [798.97](798.97.md) | Exposure of confidential secret or token RubyGem API token | High | Passive |
| [798.98](798.98.md) | Exposure of confidential secret or token RapidAPI Access Token | High | Passive |
| [798.99](798.99.md) | Exposure of confidential secret or token Sendbird Access ID | High | Passive |
| [798.100](798.100.md) | Exposure of confidential secret or token Sendbird Access Token | High | Passive |
diff --git a/doc/user/application_security/dast/dast_troubleshooting.md b/doc/user/application_security/dast/dast_troubleshooting.md
index 61a7520bf7c..0dcf203a3a9 100644
--- a/doc/user/application_security/dast/dast_troubleshooting.md
+++ b/doc/user/application_security/dast/dast_troubleshooting.md
@@ -5,7 +5,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
type: reference, howto
---
-# Troubleshooting Dynamic Application Security Testing (DAST) **(ULTIMATE)**
+# Troubleshooting DAST proxy-based analyzer **(ULTIMATE)**
The following troubleshooting scenarios have been collected from customer support cases. If you
experience a problem not addressed here, or the information here does not fix your problem, create a
diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md
index d78a8fca98f..283e48ec499 100644
--- a/doc/user/application_security/dast/index.md
+++ b/doc/user/application_security/dast/index.md
@@ -25,7 +25,7 @@ whitepaper.
GitLab provides the following DAST analyzers, one or more of which may be useful depending on the kind of application you're testing.
-For scanning websites, use one of:
+For scanning websites, use one of:
- The [DAST proxy-based analyzer](proxy-based.md) for scanning traditional applications serving simple HTML. The proxy-based analyzer can be run automatically or on-demand.
- The [DAST browser-based analyzer](browser_based.md) for scanning applications that make heavy use of JavaScript. This includes single page web applications.
diff --git a/doc/user/application_security/dast/proxy-based.md b/doc/user/application_security/dast/proxy-based.md
index ec98b809fb7..fc78018bdad 100644
--- a/doc/user/application_security/dast/proxy-based.md
+++ b/doc/user/application_security/dast/proxy-based.md
@@ -24,6 +24,40 @@ The analyzer uses the [OWASP Zed Attack Proxy](https://www.zaproxy.org/) (ZAP) t
to attack your application and produce a more extensive security report. It can be very
useful when combined with [Review Apps](../../../ci/review_apps/index.md).
+## Templates
+
+> - The DAST latest template was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/254325) in GitLab 13.8.
+> - All DAST templates were [updated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62597) to DAST_VERSION: 2 in GitLab 14.0.
+> - All DAST templates were [updated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87183) to DAST_VERSION: 3 in GitLab 15.0.
+
+GitLab DAST configuration is defined in CI/CD templates. Updates to the template are provided with
+GitLab upgrades, allowing you to benefit from any improvements and additions.
+
+Available templates:
+
+- [`DAST.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml): Stable version of the DAST CI/CD template.
+- [`DAST.latest.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml): Latest version of the DAST template.
+
+WARNING:
+The latest version of the template may include breaking changes. Use the stable template unless you
+need a feature provided only in the latest template.
+
+For more information about template versioning, see the
+[CI/CD documentation](../../../development/cicd/templates.md#latest-version).
+
+## DAST versions
+
+By default, the DAST template uses the latest major version of the DAST Docker image. You can choose
+how DAST updates, using the `DAST_VERSION` variable:
+
+- Automatically update DAST with new features and fixes by pinning to a major
+ version (such as `1`).
+- Only update fixes by pinning to a minor version (such as `1.6`).
+- Prevent all updates by pinning to a specific version (such as `1.6.4`).
+
+Find the latest DAST versions on the [DAST releases](https://gitlab.com/gitlab-org/security-products/dast/-/releases)
+page.
+
## DAST run options
You can use DAST to examine your web application:
@@ -46,58 +80,32 @@ To enable DAST to run automatically, either:
- Enable [Auto DAST](../../../topics/autodevops/stages.md#auto-dast) (provided
by [Auto DevOps](../../../topics/autodevops/index.md)).
-- [Include the DAST template](#include-the-dast-template) in your existing
- `.gitlab-ci.yml` file.
-- [Configure DAST using the UI](#configure-dast-using-the-ui).
-
-#### Include the DAST template
+- [Edit the `.gitlab.ci.yml` file manually](#edit-the-gitlabciyml-file-manually).
+- [Use an automatically configured merge request](#configure-dast-using-the-ui).
-> - This template was [updated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62597) to DAST_VERSION: 2 in GitLab 14.0.
-> - This template was [updated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87183) to DAST_VERSION: 3 in GitLab 15.0.
+#### Edit the `.gitlab.ci.yml` file manually
-If you want to manually add DAST to your application, the DAST job is defined
-in a CI/CD template file. Updates to the template are provided with GitLab
-upgrades, allowing you to benefit from any improvements and additions.
+In this method you manually edit the existing `.gitlab-ci.yml` file. Use this method if your GitLab CI/CD configuration file is complex.
To include the DAST template:
-1. Select the CI/CD template you want to use:
-
- - [`DAST.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml):
- Stable version of the DAST CI/CD template.
- - [`DAST.latest.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml):
- Latest version of the DAST template. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/254325)
- in GitLab 13.8).
-
- WARNING:
- The latest version of the template may include breaking changes. Use the
- stable template unless you need a feature provided only in the latest template.
-
- For more information about template versioning, see the
- [CI/CD documentation](../../../development/cicd/templates.md#latest-version).
-
-1. Add a `dast` stage to your GitLab CI stages configuration:
-
- ```yaml
- stages:
- - dast
- ```
-
-1. Add the template to GitLab, based on your version of GitLab:
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **CI/CD > Editor**.
+1. Copy and paste the following to the bottom of the `.gitlab-ci.yml` file.
- - In GitLab 11.9 and later, [include](../../../ci/yaml/index.md#includetemplate)
- the template by adding the following to your `.gitlab-ci.yml` file:
+ To use the DAST stable template:
- ```yaml
- include:
- - template: <template_file.yml>
+ ```yaml
+ include:
+ - template: DAST.gitlab-ci.yml
+ ```
- variables:
- DAST_WEBSITE: https://example.com
- ```
+ To use the DAST latest template:
- - In GitLab 11.8 and earlier, add the contents of the template to your
- `.gitlab_ci.yml` file.
+ ```yaml
+ include:
+ - template: DAST.latest.gitlab-ci.yml
+ ```
1. Define the URL to be scanned by DAST by using one of these methods:
@@ -125,9 +133,13 @@ To include the DAST template:
You can see an example of this in our
[Auto DevOps CI YAML](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml)
file.
+1. Select the **Validate** tab, then select **Validate pipeline**.
+ The message **Simulation completed successfully** indicates the file is valid.
+1. Select the **Edit** tab.
+1. Optional. In **Commit message**, customize the commit message.
+1. Select **Commit changes**.
-The included template creates a `dast` job in your CI/CD pipeline and scans
-your project's running application for possible vulnerabilities.
+Pipelines now include a DAST job.
The results are saved as a
[DAST report artifact](../../../ci/yaml/artifacts_reports.md#artifactsreportsdast)
@@ -137,21 +149,12 @@ always take the latest DAST artifact available. Behind the scenes, the
is used to run the tests on the specified URL and scan it for possible
vulnerabilities.
-By default, the DAST template uses the latest major version of the DAST Docker
-image. Using the `DAST_VERSION` variable, you can choose how DAST updates:
-
-- Automatically update DAST with new features and fixes by pinning to a major
- version (such as `1`).
-- Only update fixes by pinning to a minor version (such as `1.6`).
-- Prevent all updates by pinning to a specific version (such as `1.6.4`).
-
-Find the latest DAST versions on the [Releases](https://gitlab.com/gitlab-org/security-products/dast/-/releases)
-page.
-
#### Configure DAST using the UI
-You can enable or configure DAST settings using the UI. The generated settings are formatted so they
-can be conveniently pasted into the `.gitlab-ci.yml` file.
+In this method you select options in the UI. Based on your selections, a code
+snippet is created that you paste into the `.gitlab-ci.yml` file.
+
+To configure DAST using the UI:
1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > Configuration**.
@@ -168,102 +171,17 @@ can be conveniently pasted into the `.gitlab-ci.yml` file.
1. To add the snippet to your project's `.gitlab-ci.yml` file, select
**Copy code and open `.gitlab-ci.yml` file**. The Pipeline Editor opens.
1. Paste the snippet into the `.gitlab-ci.yml` file.
- 1. Select the **Lint** tab to confirm the edited `.gitlab-ci.yml` file is valid.
- 1. Select the **Edit** tab, then select **Commit changes**.
+ 1. Select the **Validate** tab, then select **Validate pipeline**.
+ The message **Simulation completed successfully** indicates the file is valid.
+ 1. Select the **Edit** tab.
+ 1. Optional. In **Commit message**, customize the commit message.
+ 1. Select **Commit changes**.
-When the snippet is committed to the `.gitlab-ci.yml` file, pipelines include a DAST job.
+Pipelines now include a DAST job.
### API scan
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10928) in GitLab 12.10.
-> - A new DAST API scanning engine was introduced in GitLab 13.10.
-
-Using an API specification as a scan's target is a useful way to seed URLs for scanning an API.
-Vulnerability rules in an API scan are different than those in a normal website scan.
-
-A new DAST API scanning engine is available in GitLab 13.12 and later. For more details, see [DAST API scanning engine](../dast_api). The new scanning engine supports REST, SOAP, GraphQL, and generic APIs using forms, XML, and JSON. Testing can be performed using OpenAPI, Postman Collections, and HTTP Archive (HAR) documents.
-
-The target API instance's base URL is provided by using the `DAST_API_TARGET_URL` variable or an `environment_url.txt` file.
-
-#### Specification format
-
-API scans support OpenAPI V2 and OpenAPI V3 specifications. You can define these specifications using `JSON` or `YAML`.
-
-#### Import API specification from a URL
-
-If your API specification is accessible at a URL, you can pass that URL in directly as the target.
-The specification does not have to be hosted on the same host as the API being tested.
-
-```yaml
-include:
- - template: DAST-API.gitlab-ci.yml
-
-variables:
- DAST_API_SPECIFICATION: http://my.api/api-specification.yml
-```
-
-#### Import API specification from a file
-
-If your API specification file is in your repository, you can provide its filename as the target.
-
-```yaml
-dast:
- variables:
- GIT_STRATEGY: fetch
- DAST_API_SPECIFICATION: api-specification.yml
-```
-
-#### Full API scan
-
-API scans support full scanning, which can be enabled by using the `DAST_FULL_SCAN_ENABLED`
-CI/CD variable. Domain validation is not supported for full API scans.
-
-#### Host override
-
-Specifications often define a host, which contains a domain name and a port. The
-host referenced may be different than the host of the API's review instance.
-This can cause incorrect URLs to be imported, or a scan on an incorrect host.
-Use the `DAST_API_HOST_OVERRIDE` CI/CD variable to override these values.
-
-WARNING:
-When using the API host override feature, you cannot use the `$DAST_WEBSITE` variable to override the hostname.
-A host override is _only_ supported when importing the API specification from a URL. Attempts to override the
-host throw an error when the API specification is imported from a file. This is due to a limitation in the
-ZAP OpenAPI extension.
-
-For example, with a OpenAPI V3 specification containing:
-
-```yaml
-servers:
- - url: https://api.host.com
-```
-
-If the test version of the API is running at `https://api-test.host.com`, then
-the following DAST configuration can be used:
-
-```yaml
-include:
- - template: DAST-API.gitlab-ci.yml
-
-variables:
- DAST_API_SPECIFICATION: http://api-test.host.com/api-specification.yml
- DAST_API_HOST_OVERRIDE: api-test.host.com
-```
-
-#### Authentication using headers
-
-Tokens in request headers are often used as a way to authenticate API requests.
-You can achieve this by using the `DAST_REQUEST_HEADERS` CI/CD variable.
-Headers are applied to every request DAST makes.
-
-```yaml
-include:
- - template: DAST-API.gitlab-ci.yml
-
-variables:
- DAST_API_SPECIFICATION: http://api-test.api.com/api-specification.yml
- DAST_REQUEST_HEADERS: "Authorization: Bearer my.token"
-```
+- The [DAST API analyzer](../dast_api/index.md) is used for scanning web APIs. Web API technologies such as GraphQL, REST, and SOAP are supported.
### URL scan
@@ -336,6 +254,10 @@ When using `DAST_PATHS` and `DAST_PATHS_FILE`, note the following:
To perform a [full scan](#full-scan) on the listed paths, use the `DAST_FULL_SCAN_ENABLED` CI/CD variable.
+## Authentication
+
+The proxy-based analyzer uses the browser-based analyzer to authenticate a user prior to a scan. See [Authentication](authentication.md) for configuration instructions.
+
## Customize DAST settings
You can customize the behavior of DAST using both CI/CD variables and command-line options. Use of CI/CD
@@ -427,61 +349,49 @@ To enable Mutual TLS:
#### Available CI/CD variables
These CI/CD variables are specific to DAST. They can be used to customize the behavior of DAST to your requirements.
+For authentication CI/CD variables, see [Authentication](authentication.md).
WARNING:
All customization of GitLab security scanning tools should be tested in a merge request before
merging these changes to the default branch. Failure to do so can give unexpected results,
including a large number of false positives.
-| CI/CD variable | Type | Description |
-|:-------------------------------------------------|:--------------|:------------------------------|
-| `DAST_ADVERTISE_SCAN` | boolean | Set to `true` to add a `Via` header to every request sent, advertising that the request was sent as part of a GitLab DAST scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/334947) in GitLab 14.1. |
-| `DAST_AGGREGATE_VULNERABILITIES` | boolean | Vulnerability aggregation is set to `true` by default. To disable this feature and see each vulnerability individually set to `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/254043) in GitLab 14.0. |
-| `DAST_API_HOST_OVERRIDE` <sup>1</sup> | string | Used to override domains defined in API specification files. Only supported when importing the API specification from a URL. Example: `example.com:8080`. |
-| `DAST_API_SPECIFICATION` <sup>1</sup> | URL or string | The API specification to import. The specification can be hosted at a URL, or the name of a file present in the `/zap/wrk` directory. The variable `DAST_WEBSITE` must be specified if this is omitted. |
-| `DAST_AUTH_REPORT` <sup>2</sup> | boolean | Used in combination with exporting the `gl-dast-debug-auth-report.html` artifact to aid in debugging authentication issues. |
-| `DAST_AUTH_EXCLUDE_URLS` <sup>2</sup> | URLs | **{warning}** **[Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/289959)** in GitLab 14.0. Replaced by `DAST_EXCLUDE_URLS`. The URLs to skip during the authenticated scan; comma-separated. Regular expression syntax can be used to match multiple URLs. For example, `.*` matches an arbitrary character sequence. Not supported for API scans. |
-| `DAST_AUTH_URL` <sup>1,2</sup> | URL | The URL of the page containing the sign-in HTML form on the target website. `DAST_USERNAME` and `DAST_PASSWORD` are submitted with the login form to create an authenticated scan. Not supported for API scans. Example: `https://login.example.com`. |
-| `DAST_AUTH_VERIFICATION_LOGIN_FORM` <sup>2</sup> | boolean | Verifies successful authentication by checking for the lack of a login form once the login form has been submitted. |
-| `DAST_AUTH_VERIFICATION_SELECTOR` <sup>2</sup> | selector | Verifies successful authentication by checking for presence of a selector once the login form has been submitted. Example: `css:.user-photo`. |
-| `DAST_AUTH_VERIFICATION_URL` <sup>1,2</sup> | URL | A URL only accessible to logged in users that DAST can use to confirm successful authentication. If provided, DAST exits if it cannot access the URL. Example: `"http://example.com/loggedin_page"`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/207335) in GitLab 13.8. |
-| `DAST_AUTO_UPDATE_ADDONS` | boolean | ZAP add-ons are pinned to specific versions in the DAST Docker image. Set to `true` to download the latest versions when the scan starts. Default: `false`. |
-| `DAST_BROWSER_PATH_TO_LOGIN_FORM` <sup>1,2</sup> | selector | Comma-separated list of selectors that are selected prior to attempting to enter `DAST_USERNAME` and `DAST_PASSWORD` into the login form. Example: `"css:.navigation-menu,css:.login-menu-item"`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/326633) in GitLab 14.1. |
-| `DAST_DEBUG` <sup>1</sup> | boolean | Enable debug message output. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
-| `DAST_EXCLUDE_RULES` | string | Set to a comma-separated list of Vulnerability Rule IDs to exclude them from running during the scan. Rule IDs are numbers and can be found from the DAST log or on the [ZAP project](https://www.zaproxy.org/docs/alerts/). For example, `HTTP Parameter Override` has a rule ID of `10026`. Cannot be used when `DAST_ONLY_INCLUDE_RULES` is set. **Note:** In earlier versions of GitLab the excluded rules were executed but vulnerabilities they generated were suppressed. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118641) in GitLab 12.10. |
-| `DAST_EXCLUDE_URLS` <sup>1,2</sup> | URLs | The URLs to skip during the authenticated scan; comma-separated. Regular expression syntax can be used to match multiple URLs. For example, `.*` matches an arbitrary character sequence. Not supported for API scans. Example, `http://example.com/sign-out`. |
-| `DAST_FIRST_SUBMIT_FIELD` <sup>2</sup> | string | The `id` or `name` of the element that when selected submits the username form of a multi-page login process. For example, `css:button[type='user-submit']`. [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9894) in GitLab 12.4. |
-| `DAST_FULL_SCAN_DOMAIN_VALIDATION_REQUIRED` | boolean | **{warning}** **[Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/293595)** in GitLab 14.0. Set to `true` to require domain validation when running DAST full scans. Not supported for API scans. Default: `false` |
-| `DAST_FULL_SCAN_ENABLED` <sup>1</sup> | boolean | Set to `true` to run a [ZAP Full Scan](https://github.com/zaproxy/zaproxy/wiki/ZAP-Full-Scan) instead of a [ZAP Baseline Scan](https://github.com/zaproxy/zaproxy/wiki/ZAP-Baseline-Scan). Default: `false` |
-| `DAST_HTML_REPORT` | string | The filename of the HTML report written at the end of a scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
-| `DAST_INCLUDE_ALPHA_VULNERABILITIES` | boolean | Set to `true` to include alpha passive and active scan rules. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
-| `DAST_MARKDOWN_REPORT` | string | The filename of the Markdown report written at the end of a scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
-| `DAST_MASK_HTTP_HEADERS` | string | Comma-separated list of request and response headers to be masked (GitLab 13.1). Must contain **all** headers to be masked. Refer to [list of headers that are masked by default](#hide-sensitive-information). |
-| `DAST_MAX_URLS_PER_VULNERABILITY` | number | The maximum number of URLs reported for a single vulnerability. `DAST_MAX_URLS_PER_VULNERABILITY` is set to `50` by default. To list all the URLs set to `0`. [Introduced](https://gitlab.com/gitlab-org/security-products/dast/-/merge_requests/433) in GitLab 13.12. |
-| `DAST_ONLY_INCLUDE_RULES` | string | Set to a comma-separated list of Vulnerability Rule IDs to configure the scan to run only them. Rule IDs are numbers and can be found from the DAST log or on the [ZAP project](https://www.zaproxy.org/docs/alerts/). Cannot be used when `DAST_EXCLUDE_RULES` is set. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/250651) in GitLab 13.12. |
-| `DAST_PASSWORD` <sup>1,2</sup> | string | The password to authenticate to in the website. Example: `P@55w0rd!` |
-| `DAST_PASSWORD_FIELD` <sup>1,2</sup> | string | The selector of password field at the sign-in HTML form. Example: `id:password` |
-| `DAST_PATHS` | string | Set to a comma-separated list of URLs for DAST to scan. For example, `/page1.html,/category1/page3.html,/page2.html`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214120) in GitLab 13.4. |
-| `DAST_PATHS_FILE` | string | The file path containing the paths within `DAST_WEBSITE` to scan. The file must be plain text with one path per line. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/258825) in GitLab 13.6. |
-| `DAST_PKCS12_CERTIFICATE_BASE64` | string | The PKCS12 certificate used for sites that require Mutual TLS. Must be encoded as base64 text. |
-| `DAST_PKCS12_PASSWORD` | string | The password of the certificate used in `DAST_PKCS12_CERTIFICATE_BASE64`. |
-| `DAST_REQUEST_HEADERS` <sup>1</sup> | string | Set to a comma-separated list of request header names and values. Headers are added to every request made by DAST. For example, `Cache-control: no-cache,User-Agent: DAST/1.0` |
-| `DAST_SKIP_TARGET_CHECK` | boolean | Set to `true` to prevent DAST from checking that the target is available before scanning. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229067) in GitLab 13.8. |
-| `DAST_SPIDER_MINS` <sup>1</sup> | number | The maximum duration of the spider scan in minutes. Set to `0` for unlimited. Default: One minute, or unlimited when the scan is a full scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
-| `DAST_SPIDER_START_AT_HOST` | boolean | Set to `false` to prevent DAST from resetting the target to its host before scanning. When `true`, non-host targets `http://test.site/some_path` is reset to `http://test.site` before scan. Default: `true`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/258805) in GitLab 13.6. |
-| `DAST_SUBMIT_FIELD` <sup>2</sup> | string | The `id` or `name` of the element that when selected submits the login form or the password form of a multi-page login process. For example, `css:button[type='submit']`. [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9894) in GitLab 12.4. |
-| `DAST_TARGET_AVAILABILITY_TIMEOUT` <sup>1</sup> | number | Time limit in seconds to wait for target availability. |
-| `DAST_USE_AJAX_SPIDER` <sup>1</sup> | boolean | Set to `true` to use the AJAX spider in addition to the traditional spider, useful for crawling sites that require JavaScript. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
-| `DAST_USERNAME` <sup>1,2</sup> | string | The username to authenticate to in the website. Example: `admin` |
-| `DAST_USERNAME_FIELD` <sup>1,2</sup> | string | The selector of username field at the sign-in HTML form. Example: `name:username` |
-| `DAST_XML_REPORT` | string | The filename of the XML report written at the end of a scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
-| `DAST_WEBSITE` <sup>1</sup> | URL | The URL of the website to scan. The variable `DAST_API_SPECIFICATION` must be specified if this is omitted. |
-| `DAST_ZAP_CLI_OPTIONS` | string | ZAP server command-line options. For example, `-Xmx3072m` would set the Java maximum memory allocation pool size. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
-| `DAST_ZAP_LOG_CONFIGURATION` | string | Set to a semicolon-separated list of additional log4j properties for the ZAP Server. Example: `logger.httpsender.name=org.parosproxy.paros.network.HttpSender;logger.httpsender.level=debug;logger.sitemap.name=org.parosproxy.paros.model.SiteMap;logger.sitemap.level=debug;` |
-| `SECURE_ANALYZERS_PREFIX` | URL | Set the Docker registry base address from which to download the analyzer. |
+| CI/CD variable | Type | Description |
+|:------------------------------------------------|:--------------|:------------------------------|
+| `DAST_ADVERTISE_SCAN` | boolean | Set to `true` to add a `Via` header to every request sent, advertising that the request was sent as part of a GitLab DAST scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/334947) in GitLab 14.1. |
+| `DAST_AGGREGATE_VULNERABILITIES` | boolean | Vulnerability aggregation is set to `true` by default. To disable this feature and see each vulnerability individually set to `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/254043) in GitLab 14.0. |
+| `DAST_API_HOST_OVERRIDE` <sup>1</sup> | string | **{warning}** **[Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/383467)** in GitLab 15.7. Replaced by [DAST API scan](../dast_api/index.md#available-cicd-variables). Used to override domains defined in API specification files. Only supported when importing the API specification from a URL. Example: `example.com:8080`. |
+| `DAST_API_SPECIFICATION` <sup>1</sup> | URL or string | **{warning}** **[Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/383467)** in GitLab 15.7. Replaced by [DAST API scan](../dast_api/index.md#available-cicd-variables). The API specification to import. The specification can be hosted at a URL, or the name of a file present in the `/zap/wrk` directory. The variable `DAST_WEBSITE` must be specified if this is omitted. |
+| `DAST_AUTH_EXCLUDE_URLS` | URLs | **{warning}** **[Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/289959)** in GitLab 14.0. Replaced by `DAST_EXCLUDE_URLS`. The URLs to skip during the authenticated scan; comma-separated. Regular expression syntax can be used to match multiple URLs. For example, `.*` matches an arbitrary character sequence. |
+| `DAST_AUTO_UPDATE_ADDONS` | boolean | ZAP add-ons are pinned to specific versions in the DAST Docker image. Set to `true` to download the latest versions when the scan starts. Default: `false`. |
+| `DAST_DEBUG` <sup>1</sup> | boolean | Enable debug message output. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
+| `DAST_EXCLUDE_RULES` | string | Set to a comma-separated list of Vulnerability Rule IDs to exclude them from running during the scan. Rule IDs are numbers and can be found from the DAST log or on the [ZAP project](https://www.zaproxy.org/docs/alerts/). For example, `HTTP Parameter Override` has a rule ID of `10026`. Cannot be used when `DAST_ONLY_INCLUDE_RULES` is set. **Note:** In earlier versions of GitLab the excluded rules were executed but vulnerabilities they generated were suppressed. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118641) in GitLab 12.10. |
+| `DAST_EXCLUDE_URLS` <sup>1</sup> | URLs | The URLs to skip during the authenticated scan; comma-separated. Regular expression syntax can be used to match multiple URLs. For example, `.*` matches an arbitrary character sequence. Example, `http://example.com/sign-out`. |
+| `DAST_FULL_SCAN_DOMAIN_VALIDATION_REQUIRED` | boolean | **{warning}** **[Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/293595)** in GitLab 14.0. Set to `true` to require domain validation when running DAST full scans. Default: `false` |
+| `DAST_FULL_SCAN_ENABLED` <sup>1</sup> | boolean | Set to `true` to run a [ZAP Full Scan](https://github.com/zaproxy/zaproxy/wiki/ZAP-Full-Scan) instead of a [ZAP Baseline Scan](https://github.com/zaproxy/zaproxy/wiki/ZAP-Baseline-Scan). Default: `false` |
+| `DAST_HTML_REPORT` | string | **{warning}** **[Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/384340)** in GitLab 15.7. The filename of the HTML report written at the end of a scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
+| `DAST_INCLUDE_ALPHA_VULNERABILITIES` | boolean | Set to `true` to include alpha passive and active scan rules. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
+| `DAST_MARKDOWN_REPORT` | string | **{warning}** **[Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/384340)** in GitLab 15.7. The filename of the Markdown report written at the end of a scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
+| `DAST_MASK_HTTP_HEADERS` | string | Comma-separated list of request and response headers to be masked (GitLab 13.1). Must contain **all** headers to be masked. Refer to [list of headers that are masked by default](#hide-sensitive-information). |
+| `DAST_MAX_URLS_PER_VULNERABILITY` | number | The maximum number of URLs reported for a single vulnerability. `DAST_MAX_URLS_PER_VULNERABILITY` is set to `50` by default. To list all the URLs set to `0`. [Introduced](https://gitlab.com/gitlab-org/security-products/dast/-/merge_requests/433) in GitLab 13.12. |
+| `DAST_ONLY_INCLUDE_RULES` | string | Set to a comma-separated list of Vulnerability Rule IDs to configure the scan to run only them. Rule IDs are numbers and can be found from the DAST log or on the [ZAP project](https://www.zaproxy.org/docs/alerts/). Cannot be used when `DAST_EXCLUDE_RULES` is set. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/250651) in GitLab 13.12. |
+| `DAST_PATHS` | string | Set to a comma-separated list of URLs for DAST to scan. For example, `/page1.html,/category1/page3.html,/page2.html`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214120) in GitLab 13.4. |
+| `DAST_PATHS_FILE` | string | The file path containing the paths within `DAST_WEBSITE` to scan. The file must be plain text with one path per line. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/258825) in GitLab 13.6. |
+| `DAST_PKCS12_CERTIFICATE_BASE64` | string | The PKCS12 certificate used for sites that require Mutual TLS. Must be encoded as base64 text. |
+| `DAST_PKCS12_PASSWORD` | string | The password of the certificate used in `DAST_PKCS12_CERTIFICATE_BASE64`. |
+| `DAST_REQUEST_HEADERS` <sup>1</sup> | string | Set to a comma-separated list of request header names and values. Headers are added to every request made by DAST. For example, `Cache-control: no-cache,User-Agent: DAST/1.0` |
+| `DAST_SKIP_TARGET_CHECK` | boolean | Set to `true` to prevent DAST from checking that the target is available before scanning. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229067) in GitLab 13.8. |
+| `DAST_SPIDER_MINS` <sup>1</sup> | number | The maximum duration of the spider scan in minutes. Set to `0` for unlimited. Default: One minute, or unlimited when the scan is a full scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
+| `DAST_SPIDER_START_AT_HOST` | boolean | Set to `false` to prevent DAST from resetting the target to its host before scanning. When `true`, non-host targets `http://test.site/some_path` is reset to `http://test.site` before scan. Default: `true`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/258805) in GitLab 13.6. |
+| `DAST_TARGET_AVAILABILITY_TIMEOUT` <sup>1</sup> | number | Time limit in seconds to wait for target availability. |
+| `DAST_USE_AJAX_SPIDER` <sup>1</sup> | boolean | Set to `true` to use the AJAX spider in addition to the traditional spider, useful for crawling sites that require JavaScript. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
+| `DAST_XML_REPORT` | string | **{warning}** **[Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/384340)** in GitLab 15.7. The filename of the XML report written at the end of a scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
+| `DAST_WEBSITE` <sup>1</sup> | URL | The URL of the website to scan. |
+| `DAST_ZAP_CLI_OPTIONS` | string | **{warning}** **[Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/383467)** in GitLab 15.7. ZAP server command-line options. For example, `-Xmx3072m` would set the Java maximum memory allocation pool size. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
+| `DAST_ZAP_LOG_CONFIGURATION` | string | **{warning}** **[Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/383467)** in GitLab 15.7. Set to a semicolon-separated list of additional log4j properties for the ZAP Server. Example: `logger.httpsender.name=org.parosproxy.paros.network.HttpSender;logger.httpsender.level=debug;logger.sitemap.name=org.parosproxy.paros.model.SiteMap;logger.sitemap.level=debug;` |
+| `SECURE_ANALYZERS_PREFIX` | URL | Set the Docker registry base address from which to download the analyzer. |
1. Available to an on-demand DAST scan.
-1. Used for authentication.
### Customize DAST using command-line options
@@ -528,242 +438,6 @@ variables:
DAST_ZAP_CLI_OPTIONS: "-config replacer.full_list(0).description=auth -config replacer.full_list(0).enabled=true -config replacer.full_list(0).matchtype=REQ_HEADER -config replacer.full_list(0).matchstr=Authorization -config replacer.full_list(0).regex=false -config replacer.full_list(0).replacement=TOKEN"
```
-## Authentication
-
-NOTE:
-We highly recommend you configure the scanner to authenticate to the application. If you don't, it cannot check most of the application for security risks, as most
-of your application is likely not accessible without authentication. We also recommend
-you periodically confirm the scanner's authentication is still working, as this tends to break over
-time due to authentication changes to the application.
-
-Create masked CI/CD variables to pass the credentials that DAST uses.
-To create masked variables for the username and password, see [Create a custom variable in the UI](../../../ci/variables/index.md#custom-cicd-variables).
-The key of the username variable must be `DAST_USERNAME`,
-and the key of the password variable must be `DAST_PASSWORD`.
-
-After DAST has authenticated with the application, all cookies are collected from the web browser.
-For each cookie a matching session token is created for use by ZAP. This ensures ZAP is recognized
-by the application as correctly authenticated.
-
-Authentication supports single form logins, multi-step login forms, and authenticating to URLs outside of the configured target URL.
-
-WARNING:
-**Never** run an authenticated scan against a production server. When an authenticated
-scan is run, it may perform *any* function that the authenticated user can. This
-includes actions like modifying and deleting data, submitting forms, and following links.
-Only run an authenticated scan against a test server.
-
-### SSO
-
-DAST can authenticate to websites making use of SSO, with the following restrictions:
-
-- DAST cannot bypass a CAPTCHA if the authentication flow includes one.
-- DAST cannot handle multi-factor authentication like one-time passwords (OTP) by using SMS or authenticator apps.
-- DAST must get a cookie, or a local or session storage, with a sufficiently random value.
-
-The [authentication debug output](#configure-the-authentication-debug-output) can be helpful for troubleshooting SSO authentication
-with DAST.
-
-### Log in using automatic detection of the login form
-
-By providing a `DAST_USERNAME`, `DAST_PASSWORD`, and `DAST_AUTH_URL`, DAST attempts to authenticate to the
-target application by locating the login form based on a determination about whether or not the form contains username or password fields.
-
-Automatic detection is "best-effort", and depending on the application being scanned may provide either a resilient login experience or one that fails to authenticate the user.
-
-Login process:
-
-1. The `DAST_AUTH_URL` is loaded into the browser, and any forms on the page are located.
- 1. If a form contains a username and password field, `DAST_USERNAME` and `DAST_PASSWORD` is inputted into the respective fields, the form submit button is selected and the user is logged in.
- 1. If a form contains only a username field, it is assumed that the login form is multi-step.
- 1. The `DAST_USERNAME` is inputted into the username field and the form submit button is selected.
- 1. The subsequent pages loads where it is expected that a form exists and contains a password field. If found, `DAST_PASSWORD` is inputted, form submit button is selected and the user is logged in.
-
-### Log in using explicit selection of the login form
-
-By providing a `DAST_USERNAME_FIELD`, `DAST_PASSWORD_FIELD`, and `DAST_SUBMIT_FIELD`, in addition to the fields required for automatic login,
-DAST attempts to authenticate to the target application by locating the login form based on the selectors provided.
-Most applications benefit from this approach to authentication.
-
-Login process:
-
-1. The `DAST_AUTH_URL` is loaded into the browser, and any forms on the page are located.
- 1. If the `DAST_FIRST_SUBMIT_FIELD` is not defined, then `DAST_USERNAME` is inputted into `DAST_USERNAME_FIELD`, `DAST_PASSWORD` is inputted into `DAST_PASSWORD_FIELD`, `DAST_SUBMIT_FIELD` is selected and the user is logged in.
- 1. If the `DAST_FIRST_SUBMIT_FIELD` is defined, then it is assumed that the login form is multi-step.
- 1. The `DAST_USERNAME` is inputted into the `DAST_USERNAME_FIELD` field and the `DAST_FIRST_SUBMIT_FIELD` is selected.
- 1. The subsequent pages loads where the `DAST_PASSWORD` is inputted into the `DAST_PASSWORD_FIELD` field, the `DAST_SUBMIT_FIELD` is selected and the user is logged in.
-
-### Verifying successful login
-
-Once the login form has been submitted, DAST determines if the login was successful. Unsuccessful attempts at authentication cause the scan to halt.
-
-Following the submission of the login form, authentication is determined to be unsuccessful when:
-
-- A `400` or `500` series HTTP response status code is returned.
-- A new cookie/browser storage value determined to be sufficiently random has not been set.
-
-In addition to these checks, the user can configure their own verification checks.
-Each of the following checks can be used in conjunction with one another, if none are configured by default the presence of a login form is checked.
-
-#### Verifying based on the URL
-
-When `DAST_AUTH_VERIFICATION_URL` is configured, the URL displayed in the browser tab post login form submission is directly compared to the URL in the CI/CD variable.
-If these are not exactly the same, authentication is deemed to be unsuccessful.
-
-For example:
-
-```yaml
-include:
- - template: DAST.gitlab-ci.yml
-
-dast:
- variables:
- DAST_WEBSITE: "https://example.com"
- DAST_BROWSER_SCAN: "true" # use the browser-based GitLab DAST crawler
- ...
- DAST_AUTH_VERIFICATION_URL: "https://example.com/user/welcome"
-```
-
-#### Verify based on presence of an element
-
-When `DAST_AUTH_VERIFICATION_SELECTOR` is configured, the page displayed in the browser tab is searched for an element described by the selector in the CI/CD variable.
-If no element is found, authentication is deemed to be unsuccessful.
-
-For example:
-
-```yaml
-include:
- - template: DAST.gitlab-ci.yml
-
-dast:
- variables:
- DAST_WEBSITE: "https://example.com"
- DAST_BROWSER_SCAN: "true" # use the browser-based GitLab DAST crawler
- ...
- DAST_AUTH_VERIFICATION_SELECTOR: "css:.welcome-user"
-```
-
-#### Verify based on presence of a login form
-
-When `DAST_AUTH_VERIFICATION_LOGIN_FORM` is configured, the page displayed in the browser tab is searched for a form that is detected to be a login form.
-If any such form is found, authentication is deemed to be unsuccessful.
-
-For example:
-
-```yaml
-include:
- - template: DAST.gitlab-ci.yml
-
-dast:
- variables:
- DAST_WEBSITE: "https://example.com"
- DAST_BROWSER_SCAN: "true" # use the browser-based GitLab DAST crawler
- ...
- DAST_AUTH_VERIFICATION_LOGIN_FORM: "true"
-```
-
-### View the login form
-
-Many web applications show the user the login form in a pop-up (modal) window.
-For these applications, navigating to the form requires both:
-
-- A starting URL.
-- A list of elements to select to display the modal window.
-
-When `DAST_BROWSER_PATH_TO_LOGIN_FORM` is present, like in this example:
-
-```yaml
-include:
- - template: DAST.gitlab-ci.yml
-
-dast:
- variables:
- DAST_WEBSITE: "https://my.site.com"
- DAST_BROWSER_SCAN: "true" # use the browser-based GitLab DAST crawler
- ...
- DAST_AUTH_URL: "https://my.site.com/admin"
- DAST_BROWSER_PATH_TO_LOGIN_FORM: "css:.navigation-menu,css:.login-menu-item"
-```
-
-DAST performs these actions:
-
-1. Load the `DAST_AUTH_URL` page, such as `https://my.site.com/admin`.
-1. After the page loads, DAST selects elements found by the selectors described
- in `DAST_BROWSER_PATH_TO_LOGIN_FORM`. This example opens the navigation menu
- and selects the login menu, to display the login modal window.
-1. To continue the authentication process, DAST fills in the username and password
- on the login form.
-
-### Configure the authentication debug output
-
-It is often difficult to understand the cause of an authentication failure when running DAST in a CI/CD pipeline.
-To assist users in debugging authentication issues, a debug report can be generated and saved as a job artifact.
-This HTML report contains all steps made during the login process, along with HTTP requests and responses, the Document Object Model (DOM) and screenshots.
-
-![dast-auth-report](img/dast_auth_report.jpg)
-
-An example configuration where the authentication debug report is exported may look like the following:
-
-```yaml
-dast:
- variables:
- DAST_WEBSITE: "https://example.com"
- DAST_BROWSER_SCAN: "true" # use the browser-based GitLab DAST crawler
- ...
- DAST_AUTH_REPORT: "true"
- artifacts:
- paths: [gl-dast-debug-auth-report.html]
- when: always
-```
-
-### Selectors
-
-Selectors are used by CI/CD variables to specify the location of an element displayed on a page in a browser.
-Selectors have the format `type`:`search string`. The crawler searches for the selector using the search string based on the type.
-
-| Selector type | Example | Description |
-| ------------- | ---------------------------------- | ----------- |
-| `css` | `css:.password-field` | Searches for a HTML element having the supplied CSS selector. Selectors should be as specific as possible for performance reasons. |
-| `id` | `id:element` | Searches for an HTML element with the provided element ID. |
-| `name` | `name:element` | Searches for an HTML element with the provided element name. |
-| `xpath` | `xpath://input[@id="my-button"]/a` | Searches for a HTML element with the provided XPath. Note that XPath searches are expected to be less performant than other searches. |
-| None provided | `a.click-me` | Defaults to searching using a CSS selector. |
-
-#### Find selectors with Google Chrome
-
-Chrome DevTools element selector tool is an effective way to find a selector.
-
-1. Open Chrome and navigate to the page where you would like to find a selector, for example, the login page for your site.
-1. Open the `Elements` tab in Chrome DevTools with the keyboard shortcut `Command + Shift + c` in macOS or `Ctrl + Shift + c` in Windows.
-1. Select the `Select an element in the page to select it` tool.
- ![search-elements](img/dast_auth_browser_scan_search_elements.png)
-1. Select the field on your page that you would like to know the selector for.
-1. Once the tool is active, highlight a field you wish to view the details of.
- ![highlight](img/dast_auth_browser_scan_highlight.png)
-1. Once highlighted, you can see the element's details, including attributes that would make a good candidate for a selector.
-
-In this example, the `id="user_login"` appears to be a good candidate. You can use this as a selector as the DAST username field by setting
-`DAST_USERNAME_FIELD: "id:user_login"`.
-
-#### Choose the right selector
-
-Judicious choice of selector leads to a scan that is resilient to the application changing.
-
-In order of preference, it is recommended to choose as selectors:
-
-- `id` fields. These are generally unique on a page, and rarely change.
-- `name` fields. These are generally unique on a page, and rarely change.
-- `class` values specific to the field, such as the selector `"css:.username"` for the `username` class on the username field.
-- Presence of field specific data attributes, such as the selector, `"css:[data-username]"` when the `data-username` field has any value on the username field.
-- Multiple `class` hierarchy values, such as the selector `"css:.login-form .username"` when there are multiple elements with class `username` but only one nested inside the element with the class `login-form`.
-
-When using selectors to locate specific fields we recommend you avoid searching on:
-
-- Any `id`, `name`, `attribute`, `class` or `value` that is dynamically generated.
-- Generic class names, such as `column-10` and `dark-grey`.
-- XPath searches as they are less performant than other selector searches.
-- Unscoped searches, such as those beginning with `css:*` and `xpath://*`.
-
### Bleeding-edge vulnerability definitions
ZAP first creates rules in the `alpha` class. After a testing period with
@@ -804,8 +478,6 @@ An on-demand DAST scan:
- Is associated with your project's default branch.
- Is saved on creation so it can be run later.
-### On-demand scan modes
-
An on-demand scan can be run in active or passive mode:
- _Passive mode_ is the default and runs a ZAP Baseline Scan.
@@ -814,35 +486,20 @@ An on-demand scan can be run in active or passive mode:
### View on-demand DAST scans
-To view running completed and scheduled on-demand DAST scans for a project, go to
-**Security & Compliance > On-demand Scans** in the left sidebar.
+To view on-demand scans, from your project's home page, go to **Security & Compliance > On-demand
+scans** in the left sidebar.
-- To view both running and completed scans, select **All**.
-- To view running scans only, select **Running**.
-- To view finished scans, select **Finished**. A finished scan is a scan that either succeeded,
- failed, or was canceled.
-- To view scheduled scans, select **Scheduled**. It shows on-demand scans that have a schedule
- set up. Those are _not_ included in the **All** tab.
-- To view saved on-demand scan profiles, select **Scan library**.
- Those are _not_ included in the **All** tab.
+On-demand scans are grouped by their status. The scan library contains all available on-demand
+scans.
-#### Cancel an on-demand scan
+From the **On-demand scans** page you can:
-To cancel a pending or running on-demand scan, select **Cancel** (**{cancel}**) in the
-on-demand scans list.
-
-#### Retry an on-demand scan
-
-To retry a scan that failed or succeeded with warnings, select **Retry** (**{retry}**) in the
-on-demand scans list.
-
-#### View an on-demand scan's results
-
-To view a finished scan's results, select **View results** in the on-demand scans list.
-
-#### Edit an on-demand scan
-
-To edit an on-demand scan's settings, select **Edit** (**{pencil}**) in the **Scheduled** tab.
+- [Run](#run-an-on-demand-dast-scan) an on-demand scan.
+- View the results of an on-demand scan.
+- Cancel (**{cancel}**) a pending or running on-demand scan.
+- Retry (**{retry}**) a scan that failed, or succeeded with warnings.
+- [Edit](#edit-an-on-demand-scan) (**{pencil}**) an on-demand scan's settings.
+- [Delete](#delete-an-on-demand-scan) an on-demand scan.
### Run an on-demand DAST scan
@@ -925,44 +582,37 @@ To schedule a scan:
1. To run the on-demand scan immediately, select **Save and run scan**. To [run](#run-a-saved-on-demand-scan) it according to the schedule you set, select
**Save scan**.
-#### List saved on-demand scans
-
-To list saved on-demand scans:
-
-1. From your project's home page, go to **Security & Compliance > On-demand Scans**.
-1. Select the **Scan library** tab.
-
-#### View details of an on-demand scan
+### View details of an on-demand scan
To view details of an on-demand scan:
-1. From your project's home page, go to **Security & Compliance > On-demand Scans**.
+1. From your project's home page, go to **Security & Compliance > On-demand scans**.
1. Select the **Scan library** tab.
1. In the saved scan's row select **More actions** (**{ellipsis_v}**), then select **Edit**.
-#### Edit an on-demand scan
+### Edit an on-demand scan
To edit an on-demand scan:
-1. From your project's home page, go to **Security & Compliance > On-demand Scans**.
+1. From your project's home page, go to **Security & Compliance > On-demand scans**.
1. Select the **Scan library** tab.
1. In the saved scan's row select **More actions** (**{ellipsis_v}**), then select **Edit**.
1. Edit the form.
1. Select **Save scan**.
-#### Delete an on-demand scan
+### Delete an on-demand scan
To delete an on-demand scan:
-1. From your project's home page, go to **Security & Compliance > On-demand Scans**.
+1. From your project's home page, go to **Security & Compliance > On-demand scans**.
1. Select the **Scan library** tab.
1. In the saved scan's row select **More actions** (**{ellipsis_v}**), then select **Delete**.
-1. Select **Delete** to confirm the deletion.
+1. On the confirmation dialog, select **Delete**.
## Site profile
-> - Scan method [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/345837) in GitLab 15.6.
-> - File URL [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/345837) in GitLab 15.6.
+> - Site profile features, scan method and file URL, were [enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/345837) in GitLab 15.6.
+> - GraphQL endpoint path feature was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/378692) in GitLab 15.7.
A site profile defines the attributes and configuration details of the deployed application,
website, or API to be scanned by DAST. A site profile can be referenced in `.gitlab-ci.yml` and
@@ -984,10 +634,11 @@ A site profile contains:
- **Password form field**: The name of password field at the sign-in HTML form.
- **Submit form field**: The `id` or `name` of the element that when selected submits the sign-in HTML form.
-- **Scan method**: A type of method to perform API testing. The supported methods are OpenAPI, Postman Collections, and HTTP Archive (HAR) documents.
-- **File URL**: The URL of the OpenAPI, Postman Collection, or HTTP Archive file.
+- **Scan method**: A type of method to perform API testing. The supported methods are OpenAPI, Postman Collections, HTTP Archive (HAR), or GraphQL.
+ - **GraphQL endpoint path**: The path to the GraphQL endpoint. This path is concatenated with the target URL to provide the URI for the scan to test. The GraphQL endpoint must support introspection queries.
+ - **File URL**: The URL of the OpenAPI, Postman Collection, or HTTP Archive file.
-When an API site type is selected, a [host override](#host-override) is used to ensure the API being scanned is on the same host as the target. This is done to reduce the risk of running an active scan against the wrong API.
+When an API site type is selected, a host override is used to ensure the API being scanned is on the same host as the target. This is done to reduce the risk of running an active scan against the wrong API.
When configured, request headers and password fields are encrypted using [`aes-256-gcm`](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) before being stored in the database.
This data can only be read and decrypted with a valid secrets file.
@@ -1235,9 +886,7 @@ and DAST site profiles are included in the [audit log](../../../administration/a
The DAST tool outputs a `gl-dast-report.json` report file containing details of the scan and its results.
This file is included in the job's artifacts. JSON is the default format, but
you can output the report in Markdown, HTML, and XML formats. To specify an alternative
-format, use a [CI/CD variable](#available-cicd-variables). You can also use a CI/CD variable
-to configure the job to output the `gl-dast-debug-auth-report.html` file which helps when debugging
-authentication issues.
+format, use a [CI/CD variable](#available-cicd-variables).
For details of the report's schema, see the [schema for DAST reports](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/master/dist/dast-report-format.json). Example reports can be found in the
[DAST repository](https://gitlab.com/gitlab-org/security-products/dast/-/tree/main/test/end-to-end/expect).
diff --git a/doc/user/application_security/dast/run_dast_offline.md b/doc/user/application_security/dast/run_dast_offline.md
index 05c6b74fbcd..7cb4eff8e68 100644
--- a/doc/user/application_security/dast/run_dast_offline.md
+++ b/doc/user/application_security/dast/run_dast_offline.md
@@ -39,7 +39,7 @@ process by which external resources can be imported or temporarily accessed.
These scanners are [periodically updated](../index.md#vulnerability-scanner-maintenance)
with new definitions, and you may be able to make occasional updates on your own.
-For details on saving and transporting Docker images as a file, see Docker's documentation on
+For details on saving and transporting Docker images as a file, see the Docker documentation on
[`docker save`](https://docs.docker.com/engine/reference/commandline/save/),
[`docker load`](https://docs.docker.com/engine/reference/commandline/load/),
[`docker export`](https://docs.docker.com/engine/reference/commandline/export/), and
diff --git a/doc/user/application_security/dast_api/index.md b/doc/user/application_security/dast_api/index.md
index d77be0f0ca9..63276eba871 100644
--- a/doc/user/application_security/dast_api/index.md
+++ b/doc/user/application_security/dast_api/index.md
@@ -217,8 +217,9 @@ DAST API supports testing GraphQL endpoints multiple ways:
- Test using a Postman Collection containing GraphQL queries.
This section documents how to test using a GraphQL schema. The GraphQL schema support in
-DAST API is able to query the schema from endpoints that support introspection.
+DAST API is able to query the schema from endpoints that support [introspection](https://graphql.org/learn/introspection/).
Introspection is enabled by default to allow tools like GraphiQL to work.
+For details on how to enable introspection, see your GraphQL framework documentation.
#### DAST API scanning with a GraphQL endpoint URL
@@ -1046,6 +1047,8 @@ can be added, removed, and modified by creating a custom configuration.
|[`DAST_API_EXCLUDE_URLS`](#exclude-urls) | Exclude API URL from testing. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/357195) in GitLab 14.10. |
|[`DAST_API_EXCLUDE_PARAMETER_ENV`](#exclude-parameters) | JSON string containing excluded parameters. |
|[`DAST_API_EXCLUDE_PARAMETER_FILE`](#exclude-parameters) | Path to a JSON file containing excluded parameters. |
+|[`DAST_API_REQUEST_HEADERS`](#request-headers) | A comma-separated (`,`) list of headers to include on each scan request. Consider using `DAST_API_REQUEST_HEADERS_BASE64` when storing secret header values in a [masked variable](../../../ci/variables/index.md#mask-a-cicd-variable), which has character set restrictions. |
+|[`DAST_API_REQUEST_HEADERS_BASE64`](#request-headers) | A comma-separated (`,`) list of headers to include on each scan request, Base64-encoded. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/378440) in GitLab 15.6. |
|[`DAST_API_OPENAPI`](#openapi-specification) | OpenAPI specification file or URL. |
|[`DAST_API_OPENAPI_RELAXED_VALIDATION`](#openapi-specification) | Relax document validation. Default is disabled. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345950) in GitLab 14.7. |
|[`DAST_API_OPENAPI_ALL_MEDIA_TYPES`](#openapi-specification) | Use all supported media types instead of one when generating requests. Causes test duration to be longer. Default is disabled. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333304) in GitLab 14.10. |
@@ -1488,6 +1491,61 @@ variables:
In the previous sample, you could use the script `user-pre-scan-set-up.sh` to also install new runtimes or applications that later on you could use in our overrides command.
+## Request Headers
+
+The request headers feature lets you specify fixed values for the headers during the scan session. For example, you can use the configuration variable `DAST_API_REQUEST_HEADERS` to set a fixed value in the `Cache-Control` header. If the headers you need to set include sensitive values like the `Authorization` header, use the [masked variable](../../../ci/variables/index.md#mask-a-cicd-variable) feature along with the [variable `DAST_API_REQUEST_HEADERS_BASE64`](#base64).
+
+Note that if the `Authorization` header or any other header needs to get updated while the scan is in progress, consider using the [overrides](#overrides) feature.
+
+The variable `DAST_API_REQUEST_HEADERS` lets you specify a comma-separated (`,`) list of headers. These headers are included on each request that the scanner performs. Each header entry in the list consists of a name followed by a colon (`:`) and then by its value. Whitespace before the key or value is ignored. For example, to declare a header name `Cache-Control` with the value `max-age=604800`, the header entry is `Cache-Control: max-age=604800`. To use two headers, `Cache-Control: max-age=604800` and `Age: 100`, set `DAST_API_REQUEST_HEADERS` variable to `Cache-Control: max-age=604800, Age: 100`.
+
+The order in which the different headers are provided into the variable `DAST_API_REQUEST_HEADERS` does not affect the result. Setting `DAST_API_REQUEST_HEADERS` to `Cache-Control: max-age=604800, Age: 100` produces the same result as setting it to `Age: 100, Cache-Control: max-age=604800`.
+
+### Base64
+
+The `DAST_API_REQUEST_HEADERS_BASE64` variable accepts the same list of headers as `DAST_API_REQUEST_HEADERS`, with the only difference that the entire value of the variable must be Base64-encoded. For example, to set `DAST_API_REQUEST_HEADERS_BASE64` variable to `Authorization: QmVhcmVyIFRPS0VO, Cache-control: bm8tY2FjaGU=`, ensure you convert the list to its Base64 equivalent: `QXV0aG9yaXphdGlvbjogUW1WaGNtVnlJRlJQUzBWTywgQ2FjaGUtY29udHJvbDogYm04dFkyRmphR1U9`, and the Base64-encoded value must be used. This is useful when storing secret header values in a [masked variable](../../../ci/variables/index.md#mask-a-cicd-variable), which has character set restrictions.
+
+WARNING:
+Base64 is used to support the [masked variable](../../../ci/variables/index.md#mask-a-cicd-variable) feature. Base64 encoding is not by itself a security measure, because sensitive values can be easily decoded.
+
+### Example: Adding a list of headers on each request using plain text
+
+In the following example of a `.gitlab-ci.yml`, `DAST_API_REQUEST_HEADERS` configuration variable is set to provide two header values as explained in [request headers](#request-headers).
+
+```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
+variables:
+ DAST_API_PROFILE: Quick
+ DAST_API_OPENAPI: test-api-specification.json
+ DAST_API_TARGET_URL: http://test-deployment/
+ DAST_API_REQUEST_HEADERS: 'Cache-control: no-cache, Save-Data: on'
+```
+
+### Example: Using a masked CI/CD variable
+
+The following `.gitlab-ci.yml` sample assumes the [masked variable](../../../ci/variables/index.md#mask-a-cicd-variable) `SECRET_REQUEST_HEADERS_BASE64` is defined as a [group or instance level CI/CD variable defined in the UI](../../../ci/variables/index.md#add-a-cicd-variable-to-an-instance). The value of `SECRET_REQUEST_HEADERS_BASE64` is set to `WC1BQ01FLVNlY3JldDogc31jcnt0ISwgWC1BQ01FLVRva2VuOiA3MDVkMTZmNWUzZmI=`, which is the Base64-encoded text version of `X-ACME-Secret: s3cr3t!, X-ACME-Token: 705d16f5e3fb`. Then, it can be used as follows:
+
+```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
+variables:
+ DAST_API_PROFILE: Quick
+ DAST_API_OPENAPI: test-api-specification.json
+ DAST_API_TARGET_URL: http://test-deployment/
+ DAST_API_REQUEST_HEADERS_BASE64: $SECRET_REQUEST_HEADERS_BASE64
+```
+
+Consider using `DAST_API_REQUEST_HEADERS_BASE64` when storing secret header values in a [masked variable](../../../ci/variables/index.md#mask-a-cicd-variable), which has character set restrictions.
+
## Exclude Paths
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211892) in GitLab 14.0.
@@ -1497,13 +1555,13 @@ When testing an API it can be useful to exclude certain paths. For example, you
To verify the paths are excluded, review the `Tested Operations` and `Excluded Operations` portion of the job output. You should not see any excluded paths listed under `Tested Operations`.
```plaintext
-2021-05-27 21:51:08 [INF] API Security: --[ Tested Operations ]-------------------------
-2021-05-27 21:51:08 [INF] API Security: 201 POST http://target:7777/api/users CREATED
-2021-05-27 21:51:08 [INF] API Security: ------------------------------------------------
-2021-05-27 21:51:08 [INF] API Security: --[ Excluded Operations ]-----------------------
-2021-05-27 21:51:08 [INF] API Security: GET http://target:7777/api/messages
-2021-05-27 21:51:08 [INF] API Security: POST http://target:7777/api/messages
-2021-05-27 21:51:08 [INF] API Security: ------------------------------------------------
+2021-05-27 21:51:08 [INF] DAST API: --[ Tested Operations ]-------------------------
+2021-05-27 21:51:08 [INF] DAST API: 201 POST http://target:7777/api/users CREATED
+2021-05-27 21:51:08 [INF] DAST API: ------------------------------------------------
+2021-05-27 21:51:08 [INF] DAST API: --[ Excluded Operations ]-----------------------
+2021-05-27 21:51:08 [INF] DAST API: GET http://target:7777/api/messages
+2021-05-27 21:51:08 [INF] DAST API: POST http://target:7777/api/messages
+2021-05-27 21:51:08 [INF] DAST API: ------------------------------------------------
```
### Examples
@@ -1548,7 +1606,7 @@ variables:
While testing an API you may might want to exclude a parameter (query string, header, or body element) from testing. This may be needed because a parameter always causes a failure, slows down testing, or for other reasons. To exclude parameters, you can set one of the following variables: `DAST_API_EXCLUDE_PARAMETER_ENV` or `DAST_API_EXCLUDE_PARAMETER_FILE`.
-The `DAST_API_EXCLUDE_PARAMETER_ENV` allows providing a JSON string containing excluded parameters. This is a good option if the JSON is short and will not often change. Another option is the variable `DAST_API_EXCLUDE_PARAMETER_FILE`. This variable is set to a file path that can be checked into the repository, created by another job as an artifact, or generated at runtime with a pre script using `DAST_API_PRE_SCRIPT`.
+The `DAST_API_EXCLUDE_PARAMETER_ENV` allows providing a JSON string containing excluded parameters. This is a good option if the JSON is short and will not often change. Another option is the variable `DAST_API_EXCLUDE_PARAMETER_FILE`. This variable is set to a file path that can be checked into the repository, created by another job as an artifact, or generated at runtime with a pre-script using `DAST_API_PRE_SCRIPT`.
#### Exclude parameters using a JSON document
@@ -1780,13 +1838,13 @@ As an alternative to excluding by paths, you can filter by any other component i
In your job output you can check if any URLs matched any provided regular expression from `DAST_API_EXCLUDE_URLS`. Matching operations are listed in the **Excluded Operations** section. Operations listed in the **Excluded Operations** should not be listed in the **Tested Operations** section. For example the following portion of a job output:
```plaintext
-2021-05-27 21:51:08 [INF] API Security: --[ Tested Operations ]-------------------------
-2021-05-27 21:51:08 [INF] API Security: 201 POST http://target:7777/api/users CREATED
-2021-05-27 21:51:08 [INF] API Security: ------------------------------------------------
-2021-05-27 21:51:08 [INF] API Security: --[ Excluded Operations ]-----------------------
-2021-05-27 21:51:08 [INF] API Security: GET http://target:7777/api/messages
-2021-05-27 21:51:08 [INF] API Security: POST http://target:7777/api/messages
-2021-05-27 21:51:08 [INF] API Security: ------------------------------------------------
+2021-05-27 21:51:08 [INF] DAST API: --[ Tested Operations ]-------------------------
+2021-05-27 21:51:08 [INF] DAST API: 201 POST http://target:7777/api/users CREATED
+2021-05-27 21:51:08 [INF] DAST API: ------------------------------------------------
+2021-05-27 21:51:08 [INF] DAST API: --[ Excluded Operations ]-----------------------
+2021-05-27 21:51:08 [INF] DAST API: GET http://target:7777/api/messages
+2021-05-27 21:51:08 [INF] DAST API: POST http://target:7777/api/messages
+2021-05-27 21:51:08 [INF] DAST API: ------------------------------------------------
```
NOTE:
@@ -2083,18 +2141,18 @@ The first step to resolving performance issues is to understand what is contribu
The DAST API job output contains helpful information about how fast we are testing, how fast each operation being tested responds, and summary information. Let's take a look at some sample output to see how it can be used in tracking down performance issues:
```shell
-API Security: Loaded 10 operations from: assets/har-large-response/large_responses.har
-API Security:
-API Security: Testing operation [1/10]: 'GET http://target:7777/api/large_response_json'.
-API Security: - Parameters: (Headers: 4, Query: 0, Body: 0)
-API Security: - Request body size: 0 Bytes (0 bytes)
-API Security:
-API Security: Finished testing operation 'GET http://target:7777/api/large_response_json'.
-API Security: - Excluded Parameters: (Headers: 0, Query: 0, Body: 0)
-API Security: - Performed 767 requests
-API Security: - Average response body size: 130 MB
-API Security: - Average call time: 2 seconds and 82.69 milliseconds (2.082693 seconds)
-API Security: - Time to complete: 14 minutes, 8 seconds and 788.36 milliseconds (848.788358 seconds)
+DAST API: Loaded 10 operations from: assets/har-large-response/large_responses.har
+DAST API:
+DAST API: Testing operation [1/10]: 'GET http://target:7777/api/large_response_json'.
+DAST API: - Parameters: (Headers: 4, Query: 0, Body: 0)
+DAST API: - Request body size: 0 Bytes (0 bytes)
+DAST API:
+DAST API: Finished testing operation 'GET http://target:7777/api/large_response_json'.
+DAST API: - Excluded Parameters: (Headers: 0, Query: 0, Body: 0)
+DAST API: - Performed 767 requests
+DAST API: - Average response body size: 130 MB
+DAST API: - Average call time: 2 seconds and 82.69 milliseconds (2.082693 seconds)
+DAST API: - Time to complete: 14 minutes, 8 seconds and 788.36 milliseconds (848.788358 seconds)
```
This job console output snippet starts by telling us how many operations were found (10), followed by notifications that testing has started on a specific operation and a summary of the operation has been completed. The summary is the most interesting part of this log output. In the summary, we can see that it took DAST API 767 requests to fully test this operation and its related fields. We can also see that the average response time was 2 seconds and the time to complete was 14 minutes for this one operation.
@@ -2281,7 +2339,7 @@ See the following documentation sections for assistance:
See [Performance Tuning and Testing Speed](#performance-tuning-and-testing-speed)
-### Error waiting for API Security 'http://127.0.0.1:5000' to become available
+### Error waiting for DAST API 'http://127.0.0.1:5000' to become available
A bug exists in versions of the DAST API analyzer prior to v1.6.196 that can cause a background process to fail under certain conditions. The solution is to update to a newer version of the DAST API analyzer.
@@ -2294,6 +2352,11 @@ If the issue is occurring with versions v1.6.196 or greater, contact Support and
1. The `gl-api-security-scanner.log` file available as a job artifact. In the right-hand panel of the job details page, select the **Browse** button.
1. The `dast_api` job definition from your `.gitlab-ci.yml` file.
+**Error message**
+
+- In [GitLab 15.6 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/376078), `Error waiting for DAST API 'http://127.0.0.1:5000' to become available`
+- In GitLab 15.5 and earlier, `Error waiting for API Security 'http://127.0.0.1:5000' to become available`.
+
### `Failed to start scanner session (version header not found)`
The DAST API engine outputs an error message when it cannot establish a connection with the scanner application component. The error message is shown in the job output window of the `dast_api` job. A common cause of this issue is changing the `DAST_API_API` variable from its default.
@@ -2446,6 +2509,50 @@ DAST API uses the specified media types in the OpenAPI document to generate requ
1. Review supported media types in the [OpenAPI Specification](#openapi-specification) section.
1. Edit your OpenAPI document, allowing at least a given operation to accept any of the supported media types. Alternatively, a supported media type could be set in the OpenAPI document level and get applied to all operations. This step may require changes in your application to ensure the supported media type is accepted by the application.
+### ``Error, error occurred trying to download `<URL>`: There was an error when retrieving content from Uri:' <URL>'. Error:The SSL connection could not be established, see inner exception.``
+
+DAST API is compatible with a broad range of TLS configurations, including outdated protocols and ciphers.
+Despite broad support, you might encounter connection errors. This error occurs because DAST API could not establish a secure connection with the server at the given URL.
+
+To resolve the issue:
+
+If the host in the error message supports non-TLS connections, change `https://` to `http://` in your configuration.
+For example, if an error occurs with the following configuration:
+
+```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
+variables:
+ DAST_API_TARGET_URL: https://test-deployment/
+ DAST_API_OPENAPI: https://specs/openapi.json
+```
+
+Change the prefix of `DAST_API_OPENAPI` from `https://` to `http://`:
+
+```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
+variables:
+ DAST_API_TARGET_URL: https://test-deployment/
+ DAST_API_OPENAPI: http://specs/openapi.json
+```
+
+If you cannot use a non-TLS connection to access the URL, contact the Support team for help.
+
+You can expedite the investigation with the [testssl.sh tool](https://testssl.sh/). From a machine with a bash shell and connectivity to the affected server:
+
+1. Download the latest release `zip` or `tar.gz` file and extract from <https://github.com/drwetter/testssl.sh/releases>.
+1. Run `./testssl.sh --log https://specs`.
+1. Attach the log file to your support ticket.
+
## Get support or request an improvement
To get support for your particular problem, use the [getting help channels](https://about.gitlab.com/get-help/).
diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md
index 4ed9ceedb4d..a4957c96db4 100644
--- a/doc/user/application_security/dependency_scanning/index.md
+++ b/doc/user/application_security/dependency_scanning/index.md
@@ -109,7 +109,7 @@ maximum of two directory levels from the repository's root. For example, the
`gemnasium-dependency_scanning` job is enabled if a repository contains either `Gemfile`,
`api/Gemfile`, or `api/client/Gemfile`, but not if the only supported dependency file is `api/v1/client/Gemfile`.
-For Java and Python, when a supported depedency file is detected, Dependency Scanning attempts to build the project and execute some Java or Python commands to get the list of dependencies. For all other projects, the lock file is parsed to obtain the list of dependencies without needing to build the project first.
+For Java and Python, when a supported dependency file is detected, Dependency Scanning attempts to build the project and execute some Java or Python commands to get the list of dependencies. For all other projects, the lock file is parsed to obtain the list of dependencies without needing to build the project first.
When a supported dependency file is detected, all dependencies, including transitive dependencies are analyzed. There is no limit to the depth of nested or transitive dependencies that are analyzed.
@@ -309,7 +309,7 @@ table.supported-languages ul {
<li>
<a id="notes-regarding-supported-languages-and-package-managers-3"></a>
<p>
- npm is only supported when <code>lockfileVersion = 1</code> or <code>lockfileVersion = 2</code>. Work to add support for <code>lockfileVersion = 3</code> is being tracked in issue <a href="https://gitlab.com/gitlab-org/gitlab/-/issues/365176">GitLab#365176</a>.
+ npm is supported for <code>lockfileVersion = 1</code>, <code>lockfileVersion = 2</code>, and <code>lockfileVersion = 3</code>. Support for <code>lockfileVersion = 3</code> was <a href="https://gitlab.com/gitlab-org/gitlab/-/issues/365176">introduced</a> in GitLab 15.7.
</p>
</li>
<li>
@@ -437,7 +437,7 @@ To support the following package managers, the GitLab analyzers proceed in two s
<li>
<a id="exported-dependency-information-notes-4"></a>
<p>
- Because of the implementation of <code>go build</code>, the Go build process requires network access, a pre-loaded modcache via <code>go mod download</code>, or vendored dependencies. For more information,
+ Because of the implementation of <code>go build</code>, the Go build process requires network access, a pre-loaded mod cache via <code>go mod download</code>, or vendored dependencies. For more information,
refer to the Go documentation on <a href="https://pkg.go.dev/cmd/go#hdr-Compile_packages_and_dependencies">compiling packages and dependencies</a>.
</p>
</li>
@@ -867,13 +867,8 @@ Here's an example dependency scanning report:
### CycloneDX Software Bill of Materials
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/350509) in GitLab 14.8 in [Beta](../../../policy/alpha-beta-support.md#beta-features).
-
-NOTE:
-CycloneDX SBOMs are a [Beta](../../../policy/alpha-beta-support.md#beta-features) feature,
-and the reports are subject to change during the beta period. Do not build integrations
-that rely on the format of these SBOMs staying consistent, as the format might change
-before the feature is made generally available.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/350509) in GitLab 14.8 in [Beta](../../../policy/alpha-beta-support.md#beta-features).
+> - Generally available in GitLab 15.7.
In addition to the [JSON report file](#reports-json-format), the [Gemnasium](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium)
Dependency Scanning tool outputs a [CycloneDX](https://cyclonedx.org/) Software Bill of Materials (SBOM) for
@@ -996,7 +991,7 @@ process by which external resources can be imported or temporarily accessed.
These scanners are [periodically updated](../index.md#vulnerability-scanner-maintenance)
with new definitions, and you may be able to make occasional updates on your own.
-For details on saving and transporting Docker images as a file, see Docker's documentation on
+For details on saving and transporting Docker images as a file, see the Docker documentation on
[`docker save`](https://docs.docker.com/engine/reference/commandline/save/), [`docker load`](https://docs.docker.com/engine/reference/commandline/load/),
[`docker export`](https://docs.docker.com/engine/reference/commandline/export/), and [`docker import`](https://docs.docker.com/engine/reference/commandline/import/).
@@ -1257,7 +1252,7 @@ file in the `CI_BUILDS_DIR` directory triggers the dependency scanning job.
We recommend committing the lock files, which prevents this warning.
-### I no longer get the latest Docker image after setting `DS_MAJOR_VERSION` or `DS_ANALYZER_IMAGE`
+### You no longer get the latest Docker image after setting `DS_MAJOR_VERSION` or `DS_ANALYZER_IMAGE`
If you have manually set `DS_MAJOR_VERSION` or `DS_ANALYZER_IMAGE` for specific reasons,
and now must update your configuration to again get the latest patched versions of our
diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md
index 5ddfa99fc81..6629c798cfa 100644
--- a/doc/user/application_security/index.md
+++ b/doc/user/application_security/index.md
@@ -240,9 +240,9 @@ reports are available to download. To download a report, select
### Ultimate
-A merge request contains a security widget which displays a summary of the new results. New results are determined by comparing the current findings against existing findings in the target (default) branch (if there are prior findings).
+A merge request contains a security widget which displays a summary of the new results. New results are determined by comparing the findings of the merge request against the findings of the most recent completed pipeline (`success`, `failed`, `canceled` or `skipped`) for the latest commit in the target branch.
-We recommend you run a scan of the `default` branch before enabling feature branch scans for your developers. Otherwise, there is no base for comparison and all feature branches display the full scan results in the merge request security widget.
+If security scans have not run for the most recent completed pipeline in the target branch there is no base for comparison. The vulnerabilities from the merge request findings will be listed as new in the merge request security widget. We recommend you run a scan of the `default` (target) branch before enabling feature branch scans for your developers.
The merge request security widget displays only a subset of the vulnerabilities in the generated JSON artifact because it contains both new and existing findings.
@@ -339,7 +339,7 @@ custom job:
The above `.gitlab-ci.yml` causes a linting error:
```plaintext
-Found errors in your .gitlab-ci.yml:
+Unable to create pipeline
- dependency_scanning job: chosen stage does not exist; available stages are .pre
- unit-tests
- .post
@@ -590,7 +590,7 @@ like [`SAST.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/l
the following error may occur, depending on your GitLab CI/CD configuration:
```plaintext
-Found errors in your .gitlab-ci.yml:
+Unable to create pipeline
jobs:sast config key may not be used with `rules`: only/except
```
@@ -683,7 +683,7 @@ This can be used for offline setups or for anyone wishing to use [Auto DevOps](.
Instructions are available in the [legacy template project](https://gitlab.com/gitlab-org/auto-devops-v12-10).
-#### Vulnerabilities are found, but the job succeeds. How can I have a pipeline fail instead?
+#### Vulnerabilities are found, but the job succeeds. How can you have a pipeline fail instead?
In these circumstances, that the job succeeds is the default behavior. The job's status indicates
success or failure of the analyzer itself. Analyzer results are displayed in the
diff --git a/doc/user/application_security/offline_deployments/index.md b/doc/user/application_security/offline_deployments/index.md
index 2db8e9522db..05e56560f95 100644
--- a/doc/user/application_security/offline_deployments/index.md
+++ b/doc/user/application_security/offline_deployments/index.md
@@ -117,7 +117,7 @@ This template should be used in a new, empty project, with a `.gitlab-ci.yml` fi
```yaml
include:
- - template: Secure-Binaries.gitlab-ci.yml
+ - template: Security/Secure-Binaries.gitlab-ci.yml
```
The pipeline downloads the Docker images needed for the Security Scanners and saves them as
diff --git a/doc/user/application_security/policies/index.md b/doc/user/application_security/policies/index.md
index f6d22ab28cd..a214d0d2cec 100644
--- a/doc/user/application_security/policies/index.md
+++ b/doc/user/application_security/policies/index.md
@@ -34,6 +34,10 @@ project and the security policy project, this is not recommended. Keeping the se
project separate from the development project allows for complete separation of duties between
security/compliance teams and development teams.
+You should not link a security policy project to a development project and to the group
+or sub-group the development project belongs to at the same time. Linking this way will result in
+approval rules from the Scan Result Policy not being applied to merge requests in the development project.
+
All security policies are stored in the `.gitlab/security-policies/policy.yml` YAML file inside the
linked security policy project. The format for this YAML is specific to the type of policy that is
stored there. Examples and schema information are available for the following policy types:
@@ -140,10 +144,10 @@ for more information on the product direction of security policies within GitLab
## Troubleshooting
-### `Branch name does not follow the pattern 'update-policy-<timestamp>'`
+### `Branch name 'update-policy-<timestamp>' does not follow the pattern '<branch_name_regex>'`
When you create a new security policy or change an existing policy, a new branch is automatically created with the branch name following the pattern `update-policy-<timestamp>`. For example: `update-policy-1659094451`.
-If you have group or instance push rules that do not allow branch name patterns that contain the text `update-policy-<timestamp>`, you will get an error that states `Branch name does not follow the pattern 'update-policy-<timestamp>'`.
+If you have group or instance [push rules that do not allow branch name patterns](../../project/repository/push_rules.md#validate-branch-names) that contain the text `update-policy-<timestamp>`, you will get an error that states `Branch name 'update-policy-<timestamp>' does not follow the pattern '<branch_name_regex>'`.
The workaround is to amend your group or instance push rules to allow branches following the pattern `update-policy-` followed by an integer timestamp.
diff --git a/doc/user/application_security/policies/scan-execution-policies.md b/doc/user/application_security/policies/scan-execution-policies.md
index f950d5116b1..c9c48c0c926 100644
--- a/doc/user/application_security/policies/scan-execution-policies.md
+++ b/doc/user/application_security/policies/scan-execution-policies.md
@@ -8,6 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - Group-level security policies were [introduced](https://gitlab.com/groups/gitlab-org/-/epics/4425) in GitLab 15.2.
> - Group-level security policies were [enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/356258) in GitLab 15.4.
+> - Operational container scanning [introduced](https://gitlab.com/groups/gitlab-org/-/epics/3410) in GitLab 15.5
Group, subgroup, or project owners can use scan execution policies to require that security scans run on a specified
schedule or with the project (or multiple projects if the policy is defined at a group or subgroup level) pipeline. Required scans are injected into the CI pipeline as new jobs
@@ -86,16 +87,20 @@ This rule enforces the defined actions and schedules a scan on the provided date
| Field | Type | Possible values | Description |
|------------|------|-----------------|-------------|
| `type` | `string` | `schedule` | The rule's type. |
-| `branches` | `array` of `string` | `*` or the branch's name | The branch the given policy applies to (supports wildcard). |
+| `branches` | `array` of `string` | `*` or the branch's name | The branch the given policy applies to (supports wildcard). This field is required if the `agents` field is not set. |
| `cadence` | `string` | CRON expression (for example, `0 0 * * *`) | A whitespace-separated string containing five fields that represents the scheduled time. |
-| `agents` | `object` | | The name of the [GitLab agents](../../clusters/agent/index.md) where [cluster image scanning](../../clusters/agent/vulnerabilities.md) will run. The object key is the name of the Kubernetes cluster configured for your project in GitLab. You can use the optional value of the object to select and scan specific Kubernetes resources. |
+| `agents` | `object` | | The name of the [GitLab agents](../../clusters/agent/index.md) where [cluster image scanning](../../clusters/agent/vulnerabilities.md) will run. The object key is the name of the Kubernetes agent configured for your project in GitLab. This field is required if the `branches` field is not set. |
GitLab supports the following types of CRON syntax for the `cadence` field:
- A daily cadence of once per hour at a specified hour, for example: `0 18 * * *`
- A weekly cadence of once per week on a specified day and at a specified hour, for example: `0 13 * * 0`
-Other elements of the CRON syntax may work in the cadence field, however, GitLab does not officially test or support them. The CRON expression is evaluated in UTC by default. If you have a self-managed GitLab instance and have [changed the server timezone](../../../administration/timezone.md), the CRON expression is evaluated with the new timezone.
+NOTE:
+Other elements of the [CRON syntax]((https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm)) may work in the cadence field if supported by the [cron](https://github.com/robfig/cron) we are using in our implementation, however, GitLab does not officially test or support them.
+
+NOTE:
+If using the `agents` field, required for `Operational Container Scanning`, the CRON expression is evaluated in [UTC](https://www.timeanddate.com/worldclock/timezone/utc) using the system-time of the Kubernetes-agent pod. If not using the `agents` field, the CRON expression is evaluated in standard [UTC](https://www.timeanddate.com/worldclock/timezone/utc) time from GitLab.com. If you have a self-managed GitLab instance and have [changed the server timezone](../../../administration/timezone.md), the CRON expression is evaluated with the new timezone.
### `agent` schema
@@ -108,20 +113,26 @@ Use this schema to define `agents` objects in the [`schedule` rule type](#schedu
#### Policy example
```yaml
-- name: Enforce Container Scanning in cluster connected through gitlab-agent for production and staging namespaces
+- name: Enforce Container Scanning in cluster connected through my-gitlab-agent for default and kube-system namespaces
enabled: true
rules:
- type: schedule
cadence: '0 10 * * *'
agents:
- gitlab-agent:
+ <agent-name>:
namespaces:
- - 'production'
- - 'staging'
+ - 'default'
+ - 'kube-system'
actions:
- scan: container_scanning
```
+The keys for a schedule rule are:
+
+- `cadence` (required): a [CRON expression](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm) for when the scans will be run
+- `agents:<agent-name>` (required): The name of the agent to use for scanning
+- `agents:<agent-name>:namespaces` (optional): The Kubernetes namespaces to scan. If omitted, all namespaces will be scanned.
+
## `scan` action type
This action executes the selected `scan` with additional parameters when conditions for at least one
@@ -133,6 +144,7 @@ rule in the defined policy are met.
| `site_profile` | `string` | Name of the selected [DAST site profile](../dast/proxy-based.md#site-profile). | The DAST site profile to execute the DAST scan. This field should only be set if `scan` type is `dast`. |
| `scanner_profile` | `string` or `null` | Name of the selected [DAST scanner profile](../dast/proxy-based.md#scanner-profile). | The DAST scanner profile to execute the DAST scan. This field should only be set if `scan` type is `dast`.|
| `variables` | `object` | | A set of CI variables, supplied as an array of `key: value` pairs, to apply and enforce for the selected scan. The `key` is the variable name, with its `value` provided as a string. This parameter supports any variable that the GitLab CI job supports for the specified scan. |
+| `tags` | `array` of `string` | | A list of runner tags for the policy. The policy jobs will be run by runner with the specified tags. |
Note the following:
@@ -152,7 +164,6 @@ Note the following:
mode when executed as part of a scheduled scan.
- A container scanning scan that is configured for the `pipeline` rule type ignores the agent defined in the `agents` object. The `agents` object is only considered for `schedule` rule types.
An agent with a name provided in the `agents` object must be created and configured for the project.
-- The Dependency Scanning and SAST scans use the default templates and run in a [child pipeline](../../../ci/pipelines/downstream_pipelines.md#parent-child-pipelines).
## Example security policies project
diff --git a/doc/user/application_security/policies/scan-result-policies.md b/doc/user/application_security/policies/scan-result-policies.md
index 7482df18cc3..5df910efb15 100644
--- a/doc/user/application_security/policies/scan-result-policies.md
+++ b/doc/user/application_security/policies/scan-result-policies.md
@@ -184,3 +184,12 @@ It corresponds to a single object from the previous example:
user_approvers:
- adalberto.dare
```
+
+## Example situations where scan result policies require additional approval
+
+There are several situations where the scan result policy will require an additional approval step. For example:
+
+- The number of security jobs is reduced in the working branch and no longer matches the number of security jobs in the target branch. Users can't skip the Scanning Result Policies by removing scanning jobs from the CI configuration.
+- Someone stops a pipeline security job, and users can't skip the security scan.
+- A job in a merge request fails and is configured with `allow_failure: false`. As a result, the pipeline is in a blocked state.
+- A pipeline has a manual job that must run successfully for the entire pipeline to pass.
diff --git a/doc/user/application_security/sast/customize_rulesets.md b/doc/user/application_security/sast/customize_rulesets.md
index a0742eb79a7..3d8ad6c8bf6 100644
--- a/doc/user/application_security/sast/customize_rulesets.md
+++ b/doc/user/application_security/sast/customize_rulesets.md
@@ -11,251 +11,423 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> passthrough chains. Expanded to include additional passthrough types of `file`, `git`, and `url` in GitLab 14.6.
> - [Added](https://gitlab.com/gitlab-org/gitlab/-/issues/235359) support for overriding rules in GitLab 14.8.
-You can customize the default scanning rules provided by our SAST analyzers.
-Ruleset customization supports the following that can be used
-simultaneously:
+You can customize the behavior of our SAST analyzers by [defining a ruleset configuration file](#create-the-configuration-file) in the
+repository being scanned. There are two kinds of customization:
-- [Disabling predefined rules](#disable-predefined-analyzer-rules). Available for all analyzers.
-- [Overriding predefined rules](#override-predefined-analyzer-rules). Available for all analyzers.
-- Modifying the default behavior of a given analyzer by [synthesizing and passing a custom configuration](#synthesize-a-custom-configuration). Available for only `nodejs-scan`, `gosec`, and `semgrep`.
+- Modifying the behavior of **predefined rules**. This includes:
+ - [Disabling predefined rules](#disable-predefined-rules). Available for all analyzers.
+ - [Overriding predefined rules](#override-predefined-rules). Available for all analyzers.
+- Replacing predefined rules by [synthesizing a custom configuration](#synthesize-a-custom-configuration)
+ using **passthroughs**. Available for only [nodejs-scan](https://gitlab.com/gitlab-org/security-products/analyzers/nodejs-scan)
+ and [semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep).
-To customize the default scanning rules, create a file containing custom rules. These rules
-are passed through to the analyzer's underlying scanner tools.
+## Disable predefined rules
-To create a custom ruleset:
+You can disable predefined rules for any SAST analyzer. Disabled rules won't appear
+on the [Pipeline Security](../index.md#view-security-scan-information-in-the-pipeline-security-tab)
+tab or the [Vulnerability Report](../index.md#view-security-scan-information-in-the-vulnerability-report).
-1. Create a `.gitlab` directory at the root of your project, if one doesn't already exist.
-1. Create a custom ruleset file named `sast-ruleset.toml` in the `.gitlab` directory.
+Disabling rules has a retroactive effect. The analyzer continues to scan for the
+vulnerability, but findings are omitted from the [`gl-sast-report.json` artifact](index.md#reports-json-format).
+
+See the [Schema](#schema) and [Examples](#examples) sections for information on how
+to configure this behavior.
+
+## Override predefined rules
+
+Certain attributes of predefined rules can be overridden for any SAST analyzer. This
+can be useful when adapting SAST to your existing workflow or tools. For example, you
+might want to override the severity of a vulnerability based on organizational policy,
+or choose a different message to display in the Vulnerability Report.
+
+See the [Schema](#schema) and [Examples](#examples) sections for information on how
+to configure this behavior.
+
+## Synthesize a custom configuration
+
+You can completely replace the predefined rules of some SAST analyzers:
+
+- [nodejs-scan](https://gitlab.com/gitlab-org/security-products/analyzers/nodejs-scan) - you
+ can replace the default [njsscan configuration file](https://github.com/ajinabraham/njsscan#configure-njsscan)
+ with your own.
+- [semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) - you can replace
+ the [GitLab-maintained ruleset](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep/-/tree/main/rules)
+ with your own.
-## Disable predefined analyzer rules
+You provide your customizations via passthroughs, which are composed into a
+passthrough chain at runtime and evaluated to produce a complete configuration. The
+underlying scanner is then executed against this new configuration.
-To disable analyzer rules:
+There are multiple passthrough types that let you provide configuration in different
+ways, such as using a file committed to your repository or inline in the ruleset
+configuration file. You can also choose how subsequent passthroughs in the chain are
+handled; they can overwrite or append to previous configuration.
-1. Set the `disabled` flag to `true` in the context of a `ruleset` section
+See the [Schema](#schema) and [Examples](#examples) sections for information on how
+to configure this behavior.
-1. In one or more `ruleset.identifier` sub sections, list the rules that you want disabled. Every `ruleset.identifier` section has:
+## Create the configuration file
-- a `type` field, to name the predefined rule identifier that the targeted analyzer uses.
-- a `value` field, to name the rule to be disabled.
+To create the ruleset configuration file:
-### Example: Disable predefined rules of SAST analyzers
+1. Create a `.gitlab` directory at the root of your project, if one doesn't already exist.
+1. Create a file named `sast-ruleset.toml` in the `.gitlab` directory.
+
+## Schema
+
+### The top-level section
+
+The top-level section contains one or more _configuration sections_, defined as [TOML tables](https://toml.io/en/v1.0.0#table).
+
+| Setting | Description |
+| --------| ----------- |
+| `[$analyzer]` | Declares a configuration section for an analyzer. The name follows the snake-case names defined in the list of [SAST analyzers](analyzers.md#sast-analyzers). |
-In the following example, the disabled rules are assigned to `eslint`
-and `sobelow` by matching the `type` and `value` of identifiers:
+Configuration example:
```toml
-[eslint]
- [[eslint.ruleset]]
- disable = true
- [eslint.ruleset.identifier]
- type = "eslint_rule_id"
- value = "security/detect-object-injection"
+[semgrep]
+...
+```
- [[eslint.ruleset]]
- disable = true
- [eslint.ruleset.identifier]
- type = "cwe"
- value = "185"
+Avoid creating configuration sections that modify existing rules _and_ synthesize a custom ruleset, as
+the latter replaces predefined rules completely.
-[sobelow]
- [[sobelow.ruleset]]
- disable = true
- [sobelow.ruleset.identifier]
- type = "sobelow_rule_id"
- value = "sql_injection"
+### The `[$analyzer]` configuration section
+
+The `[$analyzer]` section lets you customize the behavior of an analyzer. Valid properties
+differ based on the kind of configuration you're making.
+
+| Setting | Applies to | Description |
+| --------| -------------- | ----------- |
+| `[[$analyzer.ruleset]]` | Predefined rules | Defines modifications to an existing rule. |
+| `interpolate` | All | If set to `true`, you can use `$VAR` in the configuration to evaluate environment variables. Use this feature with caution, so you don't leak secrets or tokens. (Default: `false`) |
+| `description` | Passthroughs | Description of the custom ruleset. |
+| `targetdir` | Passthroughs | The directory where the final configuration should be persisted. If empty, a directory with a random name is created. The directory can contain up to 100MB of files. |
+| `validate` | Passthroughs | If set to `true`, the content of each passthrough is validated. The validation works for `yaml`, `xml`, `json` and `toml` content. The proper validator is identified based on the extension used in the `target` parameter of the `[[$analyzer.passthrough]]` section. (Default: `false`) |
+| `timeout` | Passthroughs | The maximum time to spend to evaluate the passthrough chain, before timing out. The timeout cannot exceed 300 seconds. (Default: 60) |
+
+#### `interpolate`
+
+WARNING:
+To reduce the risk of leaking secrets, use this feature with caution.
+
+The example below shows a configuration that uses the `$GITURL` environment variable to access a
+private repository. The variable contains a username and token (for example `https://user:token@url`), so
+they're not explicitly stored in the configuration file.
+
+```toml
+[semgrep]
+ description = "My private Semgrep ruleset"
+ interpolate = true
+
+ [[semgrep.passthrough]]
+ type = "git"
+ value = "$GITURL"
+ ref = "refs/heads/main"
```
-Those vulnerabilities containing the provided type and value are now disabled, meaning
-they won't be displayed in Merge Request nor the Vulnerability Report.
+### The `[[$analyzer.ruleset]]` section
-## Override predefined analyzer rules
+The `[[$analyzer.ruleset]]` section targets and modifies a single predefined rule. You can define
+one to many of these sections per analyzer.
-To override analyzer rules:
+| Setting | Description |
+| --------| ----------- |
+| `disable` | Whether the rule should be disabled. (Default: `false`) |
+| `[$analyzer.ruleset.identifier]` | Selects the predefined rule to be modified. |
+| `[$analyzer.ruleset.override]` | Defines the overrides for the rule. |
-1. In one or more `ruleset.identifier` subsections, list the rules that you want to override. Every `ruleset.identifier` section has:
+Configuration example:
- - a `type` field, to name the predefined rule identifier that the targeted analyzer uses.
- - a `value` field, to name the rule to be overridden.
+```toml
+[semgrep]
+ [[semgrep.ruleset]]
+ disable = true
+ ...
+```
-1. In the `ruleset.override` context of a `ruleset` section,
- provide the keys to override. Any combination of keys can be
- overridden. Valid keys are:
+### The `[$analyzer.ruleset.identifier]` section
- - description
- - message
- - name
- - severity (valid options are: Critical, High, Medium, Low, Unknown, Info)
+The `[$analyzer.ruleset.identifier]` section defines the identifiers of the predefined
+rule that you wish to modify.
-### Example: Override predefined rules of SAST analyzers
+| Setting | Description |
+| --------| ----------- |
+| `type` | The type of identifier used by the predefined rule. |
+| `value` | The value of the identifier used by the predefined rule. |
-Before adding a ruleset, we verify which vulnerability will be overwritten by viewing the [`gl-sast-report.json`](index.md#reports-json-format):
+You can look up the correct values for `type` and `value` by viewing the
+[`gl-sast-report.json`](index.md#reports-json-format) produced by the analyzer.
+You can download this file as a job artifact from the analyzer's CI job.
+
+For example, the snippet below shows a finding from a `semgrep` rule with three
+identifiers. The `type` and `value` keys in the JSON object correspond to the
+values you should provide in this section.
```json
-"identifiers": [
+...
+ "vulnerabilities": [
+ {
+ "id": "7331a4b7093875f6eb9f6eb1755b30cc792e9fb3a08c9ce673fb0d2207d7c9c9",
+ "category": "sast",
+ "message": "Key Exchange without Entity Authentication",
+ "description": "Audit the use of ssh.InsecureIgnoreHostKey\n",
+ ...
+ "identifiers": [
{
- "type": "gosec_rule_id",
- "name": "Gosec Rule ID G307",
- "value": "G307"
+ "type": "semgrep_id",
+ "name": "gosec.G106-1",
+ "value": "gosec.G106-1"
},
{
- "type": "CWE",
- "name": "CWE-703",
- "value": "703",
- "url": "https://cwe.mitre.org/data/definitions/703.html"
+ "type": "cwe",
+ "name": "CWE-322",
+ "value": "322",
+ "url": "https://cwe.mitre.org/data/definitions/322.html"
+ },
+ {
+ "type": "gosec_rule_id",
+ "name": "Gosec Rule ID G106",
+ "value": "G106"
}
]
+ }
+ ...
+ ]
+...
```
-In the following example, rules from `gosec` are matched by the `type`
-and `value` of identifiers and then overridden:
+Configuration example:
```toml
-[gosec]
- [[gosec.ruleset]]
- [gosec.ruleset.identifier]
- type = "CWE"
- value = "703"
- [gosec.ruleset.override]
+[semgrep]
+ [[semgrep.ruleset]]
+ [semgrep.ruleset.identifier]
+ type = "semgrep_id"
+ value = "gosec.G106-1
+ ...
+```
+
+### The `[$analyzer.ruleset.override]` section
+
+The `[$analyzer.ruleset.override]` section allows you to override attributes of a predefined rule.
+
+| Setting | Description |
+| --------| ----------- |
+| `description` | A detailed description of the issue. |
+| `message` | (Deprecated) A description of the issue. |
+| `name` | The name of the rule. |
+| `severity` | The severity of the rule. Valid options are: `Critical`, `High`, `Medium`, `Low`, `Unknown`, `Info`) |
+
+NOTE:
+While `message` is populated by the analyzers, it has been [deprecated](https://gitlab.com/gitlab-org/security-products/analyzers/report/-/blob/1d86d5f2e61dc38c775fb0490ee27a45eee4b8b3/vulnerability.go#L22)
+in favor of `name` and `description`.
+
+Configuration example:
+
+```toml
+[semgrep]
+ [[semgrep.ruleset]]
+ [semgrep.ruleset.override]
severity = "Critical"
+ name = "Command injection"
+ ...
```
-If a vulnerability is found with a type `CWE` with a value of `703` then
-the vulnerability severity is overwritten to `Critical`.
+### The `[[$analyzer.passthrough]]` section
-## Synthesize a custom configuration
+NOTE:
+This is currently supported by the `nodejs-scan` and `semgrep` analyzers only.
-To create a custom configuration, you can use passthrough chains.
+The `[[$analyzer.passthrough]]` section allows you to synthesize a custom configuration for an analyzer. You
+can define up to 20 of these sections per analyzer. Passthroughs are composed into a _passthrough chain_
+that evaluates into a complete configuration that replaces the predefined rules of the analyzer.
-A passthrough is a single step in a passthrough chain. The passthrough is evaluated
-in a sequence to incrementally build a configuration. The configuration is then
-passed to the target analyzer.
+Passthroughs are evaluated in order. Passthroughs listed later in the chain have
+a higher precedence and can overwrite or append to data yielded by previous
+passthroughs (depending on the `mode`). This is useful for cases where you need
+to use or modify an existing configuration.
-A configuration section for an analyzer has the following
-parameters:
+The amount of data generated by a single passthrough is limited to 1MB.
-| Parameter | Explanation |
-| ------------- | ------ |
-| `description` | Description about the analyzer configuration section. |
-| `targetdir` | The `targetdir` parameter defines the directory where the final configuration is located. If `targetdir` is empty, the analyzer uses a random directory. The maximum size of `targetdir` is 100MB. |
-| `validate` | If set to `true`, the target files for passthroughs (`raw`, `file` and `url`) are validated. The validation works for `yaml`, `xml`, `json` and `toml` files. The proper validator is identified based on the extension of the target file. By default, `validate` is set to `false`. |
-| `interpolate` | If set to `true`, environment variable interpolation is enabled so that the configuration uses secrets/tokens. We advise using this feature with caution to not leak any secrets. By default, `interpolate` is set to `false`. |
-| `timeout` | The total `timeout` for the evaluation of a passthrough chain is set to 60 seconds. If `timeout` is not set, the default timeout is 60 seconds. The timeout cannot exceed 300 seconds. |
+| Setting | Applies to | Description |
+| ------- | ---------- | ----------- |
+| `type` | All | One of `file`, `raw`, `git` or `url`. |
+| `target` | All | The target file to contain the data written by the passthrough evaluation. If empty, a random filename is used. |
+| `mode` | All | If `overwrite`, the `target` file is overwritten. If `append`, new content is appended to the `target` file. Note that the `git` type only supports `overwrite`. (Default: `overwrite`) |
+| `ref` | `type = "git"` | Contains the name of the branch or the SHA to pull. When using a branch name, specify it in the form `refs/heads/<branch>`, not `refs/remotes/<remote_name>/<branch>`. |
+| `subdir` | `type = "git"` | Used to select a subdirectory of the Git repository as the configuration source. |
+| `value` | All | For the `file`, `url`, and `git` types, defines the location of the file or Git repository. For the `raw` type, contains the inline configuration. |
+| `validator` | All | Used to explicitly invoke validators (`xml`, `yaml`, `json`, `toml`) on the target file after the evaluation of a passthrough. |
-A configuration section can include one or more passthrough sections. The maximum number of passthrough sections is 20.
-There are several types of passthroughs:
+#### Passthrough types
| Type | Description |
-| ------ | ------ |
-| `file` | Use a file that is already available in the Git repository. |
+| ------ | ----------- |
+| `file` | Use a file that is present in the Git repository. |
| `raw` | Provide the configuration inline. |
| `git` | Pull the configuration from a remote Git repository. |
-| `url` | Fetch the analyzer configuration through HTTP. |
+| `url` | Fetch the configuration using HTTP. |
-If multiple passthrough sections are defined in a passthrough chain, their
-position in the chain defines the order in which they are evaluated.
+WARNING:
+When using the `raw` passthrough with a YAML snippet, it's recommended to format all indentation
+in the `sast-ruleset.toml` file as spaces. The YAML specification mandates spaces over tabs, and the
+analyzer will fail to parse your custom ruleset unless the indentation is represented accordingly.
-- Passthroughs listed later in the chain sequence have a higher precedence.
-- Passthroughs with a higher precedence overwrite (default) and append data
- yielded by previous passthroughs. This is useful for cases where you need to
- use or modify an existing configuration.
+## Examples
-Configure a passthrough these parameters:
+### Disable predefined rules of SAST analyzers
-| Parameter | Explanation |
-| ------------ | ----------- |
-| `type` | One of `file`, `raw`, `git` or `url`. |
-| `target` | The target file that contains the data written by the passthrough evaluation. If no value is provided, a random target file is generated. |
-| `mode` | `overwrite`: if `target` exists, overwrites the file; `append`: append to file instead. The default is `overwrite`. |
-| `ref` | This option only applies to the `git` passthrough type and contains the name of the branch or the SHA to be used. When using a branch name, specify it in the form `refs/heads/<branch>`, not `refs/remotes/<remote_name>/<branch>`. |
-| `subdir` | This option only applies to the `git` passthrough type and can be used to only consider a certain subdirectory of the source Git repository. |
-| `value` | For the `file` `url` and `git` types, `value` defines the source location of the file/Git repository; for the `raw` type, `value` carries the raw content to be passed through. |
-| `validator` | Can be used to explicitly invoke validators (`xml`, `yaml`, `json`, `toml`) on the target files after the application of a passthrough. Per default, no validator is set. |
+With the following custom ruleset configuration, the following rules are omitted from the report:
-The amount of data generated by a single passthrough is limited to 1MB.
+- `semgrep` rules with a `semgrep_id` of `gosec.G106-1` or a `cwe` of `322`.
+- `sobelow` rules with a `sobelow_rule_id` of `sql_injection`.
+- `flawfinder` rules with a `flawfinder_func_name` of `memcpy`.
-## Passthrough configuration examples
+```toml
+[semgrep]
+ [[semgrep.ruleset]]
+ disable = true
+ [semgrep.ruleset.identifier]
+ type = "semgrep_id"
+ value = "gosec.G106-1"
-### Raw passthrough for nodejs-scan
+ [[semgrep.ruleset]]
+ disable = true
+ [semgrep.ruleset.identifier]
+ type = "cwe"
+ value = "322"
-Define a custom analyzer configuration. In this example, customized rules are
-defined for the `nodejs-scan` scanner:
+[sobelow]
+ [[sobelow.ruleset]]
+ disable = true
+ [sobelow.ruleset.identifier]
+ type = "sobelow_rule_id"
+ value = "sql_injection"
+
+[flawfinder]
+ [[flawfinder.ruleset]]
+ disable = true
+ [flawfinder.ruleset.identifier]
+ type = "flawfinder_func_name"
+ value = "memcpy"
+```
+
+### Override predefined rules of SAST analyzers
+
+With the following custom ruleset configuration, vulnerabilities found with
+`semgrep` with a type `CWE` and a value `322` will have their severity
+overridden to `Critical`.
+
+```toml
+[semgrep]
+ [[semgrep.ruleset]]
+ [semgrep.ruleset.identifier]
+ type = "CWE"
+ value = "322"
+ [semgrep.ruleset.override]
+ severity = "Critical"
+```
+
+### Synthesize a custom configuration using a raw passthrough for `nodejs-scan`
+
+With the following custom ruleset configuration, the predefined behavior
+of the `nodejs-scan` analyzer is replaced with a custom configuration.
+
+The syntax used for the `value` follows the [njsscan config format](https://github.com/ajinabraham/njsscan#configure-njsscan).
```toml
[nodejs-scan]
- description = 'custom ruleset for nodejs-scan'
+ description = "My custom ruleset for nodejs-scan"
[[nodejs-scan.passthrough]]
type = "raw"
value = '''
+---
- nodejs-extensions:
- .js
-
+
template-extensions:
- .new
- .hbs
- ''
-
+
ignore-filenames:
-- skip.js
-
+ - skip.js
+
ignore-paths:
- __MACOSX
- skip_dir
- node_modules
-
+
ignore-extensions:
- .hbs
-
+
ignore-rules:
- regex_injection_dos
- pug_jade_template
- express_xss
-
'''
```
-### File passthrough for Gosec
+### Synthesize a custom configuration using a file passthrough for `semgrep`
-Provide the name of the file containing a custom analyzer configuration. In
-this example, customized rules for the `gosec` scanner are contained in the
-file `gosec-config.json`:
+With the following custom ruleset configuration, the predefined ruleset
+of the `semgrep` analyzer is replaced with a custom ruleset contained in
+a file called `my-semgrep-rules.yaml` in the repository being scanned.
+
+```yaml
+# my-semgrep-rules.yml
+---
+rules:
+- id: my-custom-rule
+ pattern: print("Hello World")
+ message: |
+ Unauthorized use of Hello World.
+ severity: CRITICAL
+ languages:
+ - python
+```
```toml
-[gosec]
- description = 'custom ruleset for gosec'
+[semgrep]
+ description = "My custom ruleset for Semgrep"
- [[gosec.passthrough]]
+ [[semgrep.passthrough]]
type = "file"
- value = "gosec-config.json"
+ value = "my-semgrep-rules.yml"
```
-### Passthrough chain for Semgrep
+### Synthesize a custom configuration using a passthrough chain for `semgrep`
-In the below example, we generate a custom configuration under the `/sgrules`
-target directory with a total `timeout` of 60 seconds.
+With the following custom ruleset configuration, the predefined ruleset
+of the `semgrep` analyzer is replaced with a custom ruleset produced by
+evaluating a chain of four passthroughs. Each passthrough produces a file
+that's written to the `/sgrules` directory within the container. A
+`timeout` of 60 seconds is set in case any Git remotes are unresponsive.
-Several passthrouh types generate a configuration for the target analyzer:
+Different passthrough types are demonstrated in this example:
-- Two `git` passthrough sections pull the head of branch
- `refs/heads/test` from the `myrules` Git repository, and revision
- `97f7686` from the `sast-rules` Git repository. From the `sast-rules` Git
- repository, only data from the `go` subdirectory is considered.
+- Two `git` passthroughs, the first pulling `refs/heads/test` from the
+ `myrules` Git repository, and the second pulling revision `97f7686`
+ from the `sast-rules` repository, and considering only files in the
+ `go` subdirectory.
- The `sast-rules` entry has a higher precedence because it appears later in
the configuration.
- - If there is a filename collision between files in both repositories, files
- from the `sast` repository overwrite files from the `myrules` repository,
- as `sast-rules` has higher precedence.
-- The `raw` entry creates a file named `insecure.yml` under `/sgrules`. The
- full path is `/sgrules/insecure.yml`.
-- The `url` entry fetches a configuration made available through a URL and
- stores it in the `/sgrules/gosec.yml` file.
+ - If there's a filename collision between the two checkouts, files
+ from the `sast-rules` repository will overwrite files from the
+ `myrules` repository.
+- A `raw` passthrough, which writes its `value` to `/sgrules/insecure.yml`.
+- A `url` passthrough, which fetches a configuration hosted at a URL and
+ writes it to `/sgrules/gosec.yml`.
Afterwards, Semgrep is invoked with the final configuration located under
`/sgrules`.
```toml
[semgrep]
- description = 'semgrep custom rules configuration'
+ description = "My custom ruleset for Semgrep"
targetdir = "/sgrules"
timeout = 60
@@ -277,15 +449,15 @@ Afterwards, Semgrep is invoked with the final configuration located under
rules:
- id: "insecure"
patterns:
- - pattern: "func insecure() {...}"
+ - pattern: "func insecure() {...}"
message: |
Insecure function insecure detected
metadata:
cwe: "CWE-200: Exposure of Sensitive Information to an Unauthorized Actor"
severity: "ERROR"
languages:
- - "go"
- """
+ - "go"
+"""
[[semgrep.passthrough]]
type = "url"
@@ -293,89 +465,93 @@ rules:
target = "gosec.yml"
```
-### Interpolation
-
-The code snippet below shows an example configuration that uses an environment
-variable `$GITURL` to access a private repositories with a Git URL. The variable contains
-a username and token in the `value` field (for example `https://user:token@url`).
-It does not explicitly store credentials in the configuration file. To reduce the risk of leaking secrets through created paths and files, use this feature with caution.
-
-```toml
-[semgrep]
- description = 'semgrep custom rules configuration'
- targetdir = "/sgrules"
- interpolate = true
-
- [[semgrep.passthrough]]
- type = "git"
- value = "$GITURL"
- ref = "refs/heads/main"
-```
-
-### Configure the append mode for passthroughs
-
-To append data to previous passthroughs, use the `append` mode for the
-passthrough types `file`, `url`, and `raw`.
+### Configure the mode for passthroughs in a chain
-Passthroughs in `override` mode overwrite files
-created when preceding passthroughs in the chain find a naming
-collision. If `mode` is set to `append`, a passthrough appends data to the
-files created by its predecessors instead of overwriting.
+You can choose how to handle filename conflicts that occur between
+passthroughs in a chain. The default behavior is to overwrite
+existing files with the same name, but you can choose `mode = append`
+instead to append the content of later files onto earlier ones.
-In the below Semgrep configuration,`/sgrules/insecure.yml` assembles two passthroughs. The rules are:
+You can use the `append` mode for the `file`, `url`, and `raw`
+passthrough types only.
-- `insecure`
-- `secret`
-
-These rules add a search pattern to the analyzer and extends Semgrep capabilities.
-
-For passthrough chains we recommend that you enable validation. To enable validation,
-you can either:
-
-- set `validate` to `true`
-
-- set a passthrough `validator` to `xml`, `json`, `yaml`, or `toml`.
+With the following custom ruleset configuration, two `raw` passthroughs
+are used to iteratively assemble the `/sgrules/my-rules.yml` file, which
+is then provided to Semgrep as the ruleset. Each passthrough appends a
+single rule to the ruleset. The first passthrough is responsible for
+initialising the top-level `rules` object, according to the
+[Semgrep rule syntax](https://semgrep.dev/docs/writing-rules/rule-syntax/).
```toml
[semgrep]
- description = 'semgrep custom rules configuration'
+ description = "My custom ruleset for Semgrep"
targetdir = "/sgrules"
validate = true
[[semgrep.passthrough]]
type = "raw"
- target = "insecure.yml"
+ target = "my-rules.yml"
value = """
rules:
- id: "insecure"
patterns:
- - pattern: "func insecure() {...}"
+ - pattern: "func insecure() {...}"
message: |
- Insecure function insecure detected
+ Insecure function 'insecure' detected
metadata:
- cwe: "...
+ cwe: "..."
severity: "ERROR"
languages:
- - "go"
+ - "go"
"""
[[semgrep.passthrough]]
type = "raw"
mode = "append"
- target = "insecure.yml"
+ target = "my-rules.yml"
value = """
- id: "secret"
patterns:
- - pattern-either:
- - pattern: "$MASK = \"...\""
- - metavariable-regex:
- metavariable: "$MASK"
- regex: "(password|pass|passwd|pwd|secret|token)"
+ - pattern-either:
+ - pattern: '$MASK = "..."'
+ - metavariable-regex:
+ metavariable: "$MASK"
+ regex: "(password|pass|passwd|pwd|secret|token)"
message: |
- Use of Hard-coded Password
+ Use of hard-coded password
+ metadata:
cwe: "..."
severity: "ERROR"
languages:
- - "go"
+ - "go"
"""
```
+
+```yaml
+# /sgrules/my-rules.yml
+rules:
+- id: "insecure"
+ patterns:
+ - pattern: "func insecure() {...}"
+ message: |
+ Insecure function 'insecure' detected
+ metadata:
+ cwe: "..."
+ severity: "ERROR"
+ languages:
+ - "go"
+- id: "secret"
+ patterns:
+ - pattern-either:
+ - pattern: '$MASK = "..."'
+ - metavariable-regex:
+ metavariable: "$MASK"
+ regex: "(password|pass|passwd|pwd|secret|token)"
+ message: |
+ Use of hard-coded password
+ metadata:
+ cwe: "..."
+ severity: "ERROR"
+ languages:
+ - "go"
+```
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index b1bc9794ced..94719224254 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -105,6 +105,7 @@ Check the [SAST direction page](https://about.gitlab.com/direction/secure/static
| React | [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with GitLab-managed rules | 13.10 |
| Ruby | [brakeman](https://gitlab.com/gitlab-org/security-products/analyzers/brakeman) | 13.9 |
| Ruby on Rails | [brakeman](https://gitlab.com/gitlab-org/security-products/analyzers/brakeman) | 10.3 |
+| Scala (any build system) | [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with GitLab-managed rules | 15.7 |
| Scala<sup>2</sup> | [SpotBugs](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs) with the find-sec-bugs plugin | 11.0 (SBT) & 11.9 (Gradle, Maven) |
| Swift (iOS) | [MobSF (beta)](https://gitlab.com/gitlab-org/security-products/analyzers/mobsf) | 13.5 |
| TypeScript<sup>3</sup> | [ESLint security plugin](https://gitlab.com/gitlab-org/security-products/analyzers/eslint) | 11.9, [merged](https://gitlab.com/gitlab-org/gitlab/-/issues/36059) with ESLint in 13.2 |
@@ -694,7 +695,7 @@ The process for importing Docker images into a local offline Docker registry dep
process by which external resources can be imported or temporarily accessed. These scanners are [periodically updated](../index.md#vulnerability-scanner-maintenance)
with new definitions, and you may be able to make occasional updates on your own.
-For details on saving and transporting Docker images as a file, see Docker's documentation on
+For details on saving and transporting Docker images as a file, see the Docker documentation on
[`docker save`](https://docs.docker.com/engine/reference/commandline/save/), [`docker load`](https://docs.docker.com/engine/reference/commandline/load/),
[`docker export`](https://docs.docker.com/engine/reference/commandline/export/), and [`docker import`](https://docs.docker.com/engine/reference/commandline/import/).
diff --git a/doc/user/application_security/secret_detection/index.md b/doc/user/application_security/secret_detection/index.md
index 8a066cf1be1..d955170ece2 100644
--- a/doc/user/application_security/secret_detection/index.md
+++ b/doc/user/application_security/secret_detection/index.md
@@ -82,43 +82,57 @@ To enable Secret Detection, either:
- Enable [Auto DevOps](../../../topics/autodevops/index.md), which includes [Auto Secret Detection](../../../topics/autodevops/stages.md#auto-secret-detection).
-- [Enable Secret Detection by including the template](#enable-secret-detection-by-including-the-template).
+- [Edit the `.gitlab.ci.yml` file manually](#edit-the-gitlabciyml-file-manually). Use this method if
+ your `.gitlab-ci.yml` file is complex.
-- [Enable Secret Detection using a merge request](#enable-secret-detection-using-a-merge-request).
+- [Use an automatically configured merge request](#use-an-automatically-configured-merge-request).
-### Enable Secret Detection by including the template
+### Edit the `.gitlab.ci.yml` file manually
-You should use this method if you have an existing GitLab CI/CD configuration file.
+This method requires you to manually edit the existing `.gitlab-ci.yml` file. Use this method if
+your GitLab CI/CD configuration file is complex.
-Add the following extract to your `.gitlab-ci.yml` file:
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **CI/CD > Editor**.
+1. Copy and paste the following to the bottom of the `.gitlab-ci.yml` file:
-```yaml
-include:
- - template: Jobs/Secret-Detection.gitlab-ci.yml
-```
+ ```yaml
+ include:
+ - template: Jobs/Secret-Detection.gitlab-ci.yml
+ ```
-Pipelines now include a Secret Detection job, and the results are included in the merge request
-widget.
+1. Select the **Validate** tab, then select **Validate pipeline**.
+ The message **Simulation completed successfully** indicates the file is valid.
+1. Select the **Edit** tab.
+1. Optional. In the **Commit message** text box, customize the commit message.
+1. In the **Branch** text box, enter the name of the default branch.
+1. Select **Commit changes**.
-### Enable Secret Detection using a merge request
+Pipelines now include a Secret Detection job.
+
+### Use an automatically configured merge request
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4496) in GitLab 13.11, deployed behind a feature flag, enabled by default.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/329886) in GitLab 14.1.
+This method automatically prepares a merge request, with the Secret Detection template included in
+the `.gitlab-ci.yml` file. You then merge the merge request to enable Secret Detection.
+
NOTE:
This method works best with no existing `.gitlab-ci.yml` file, or with a minimal configuration
file. If you have a complex GitLab configuration file it may not be parsed successfully, and an
-error may occur.
+error may occur. In that case, use the [manual](#edit-the-gitlabciyml-file-manually) method instead.
-To enable Secret Detection using a merge request:
+To enable Secret Detection automatically:
1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > Configuration**.
1. In the **Secret Detection** row, select **Configure with a merge request**.
+1. Optional. Complete the fields.
+1. Select **Create merge request**.
1. Review and merge the merge request.
-Pipelines now include a Secret Detection job, and the results are included in the merge request
-widget.
+Pipelines now include a Secret Detection job.
## Responding to a leaked secret
diff --git a/doc/user/application_security/secret_detection/post_processing.md b/doc/user/application_security/secret_detection/post_processing.md
index 8dbe459d4af..9c74467bce5 100644
--- a/doc/user/application_security/secret_detection/post_processing.md
+++ b/doc/user/application_security/secret_detection/post_processing.md
@@ -6,7 +6,13 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Secret Detection post-processing and revocation **(FREE SAAS)**
-> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4639) in GitLab 13.6.
+> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4639) in GitLab 13.6.
+> - [Disabled by default for GitLab personal access tokens](https://gitlab.com/gitlab-org/gitlab/-/issues/371658) in GitLab 15.6 [with a flag](../../../administration/feature_flags.md) named `gitlab_pat_auto_revocation`. Available to GitLab.com only.
+
+FLAG:
+By default, auto revocation of GitLab personal access tokens is not available. To opt-in on GitLab.com
+during the [Beta period](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha-beta-ga), please
+[let us know by completing this form](https://docs.google.com/forms/d/e/1FAIpQLSdRbFhvA5jvI-Rt_Qnl1PQ1znOXKK8m6lRtmM0uva4upetKvQ/viewform).
GitLab supports running post-processing hooks after detecting a secret. These
hooks can perform actions, like notifying the cloud service that issued the secret.
@@ -16,7 +22,7 @@ The cloud provider can then confirm the credentials and take remediation actions
- Reissuing a secret.
- Notifying the creator of the secret.
-GitLab SaaS supports post-processing for Amazon Web Services (AWS).
+GitLab SaaS supports post-processing for [GitLab personal access tokens](../../profile/personal_access_tokens.md) and Amazon Web Services (AWS).
Post-processing workflows vary by supported cloud providers.
Post-processing is limited to a project's default branch. The epic
diff --git a/doc/user/application_security/terminology/index.md b/doc/user/application_security/terminology/index.md
index f156d60be8f..2666d91c5aa 100644
--- a/doc/user/application_security/terminology/index.md
+++ b/doc/user/application_security/terminology/index.md
@@ -83,11 +83,6 @@ once.
A finding that doesn't exist but is incorrectly reported as existing.
-### Feedback
-
-Feedback the user provides about a finding. Types of feedback include dismissal, creating an issue,
-or creating a merge request.
-
### Finding
An asset that has the potential to be vulnerable, identified in a project by an analyzer. Assets
@@ -96,6 +91,11 @@ applications, and infrastructure.
Findings are all potential vulnerability items scanners identify in MRs/feature branches. Only after merging to default does a finding become a [vulnerability](#vulnerability).
+You can interact with vulnerability findings in two ways.
+
+1. You can open an issue or merge request for the vulnerability finding.
+1. You can dismiss the vulnerability finding. Dismissing the finding hides it from the default views.
+
### Grouping
A flexible and non-destructive way to visually organize vulnerabilities in groups when there are multiple findings
@@ -164,15 +164,15 @@ table.package-managers-and-types ul {
<tbody>
<tr>
<td>gem</td>
- <td><a href="https://bundler.io/">bundler</a></td>
+ <td><a href="https://bundler.io/">Bundler</a></td>
</tr>
<tr>
- <td>packagist</td>
- <td><a href="https://getcomposer.org/">composer</a></td>
+ <td>Packagist</td>
+ <td><a href="https://getcomposer.org/">Composer</a></td>
</tr>
<tr>
- <td>conan</td>
- <td><a href="https://conan.io/">conan</a></td>
+ <td>Conan</td>
+ <td><a href="https://conan.io/">Conan</a></td>
</tr>
<tr>
<td>go</td>
@@ -180,10 +180,10 @@ table.package-managers-and-types ul {
</tr>
<tr>
<td rowspan="3">maven</td>
- <td><a href="https://gradle.org/">gradle</a></td>
+ <td><a href="https://gradle.org/">Gradle</a></td>
</tr>
<tr>
- <td><a href="https://maven.apache.org/">maven</a></td>
+ <td><a href="https://maven.apache.org/">Maven</a></td>
</tr>
<tr>
<td><a href="https://www.scala-sbt.org">sbt</a></td>
@@ -196,12 +196,12 @@ table.package-managers-and-types ul {
<td><a href="https://classic.yarnpkg.com/en">yarn</a></td>
</tr>
<tr>
- <td>nuget</td>
- <td><a href="https://www.nuget.org/">nuget</a></td>
+ <td>NuGet</td>
+ <td><a href="https://www.nuget.org/">NuGet</a></td>
</tr>
<tr>
- <td rowspan="4">pypi</td>
- <td><a href="https://setuptools.pypa.io/en/latest/">setuptools</a></td>
+ <td rowspan="4">PyPI</td>
+ <td><a href="https://setuptools.pypa.io/en/latest/">Setuptools</a></td>
</tr>
<tr>
<td><a href="https://pip.pypa.io/en/stable">pip</a></td>
diff --git a/doc/user/application_security/vulnerabilities/index.md b/doc/user/application_security/vulnerabilities/index.md
index 9ddb1bb51e2..e86f9ff4673 100644
--- a/doc/user/application_security/vulnerabilities/index.md
+++ b/doc/user/application_security/vulnerabilities/index.md
@@ -59,6 +59,8 @@ To change a vulnerability's status from its Vulnerability Page:
1. From the **Status** dropdown list select a status, then select **Change status**.
1. Optionally, at the bottom of the page, add a comment to the log entry.
+The Actions log records each status change along with which user changed the status and the time of the change.
+
## Creating an issue for a vulnerability
From a vulnerability's page you can create an issue to track all action taken to resolve or
diff --git a/doc/user/application_security/vulnerability_report/index.md b/doc/user/application_security/vulnerability_report/index.md
index 2b78dde4f63..dd919889b4d 100644
--- a/doc/user/application_security/vulnerability_report/index.md
+++ b/doc/user/application_security/vulnerability_report/index.md
@@ -260,7 +260,7 @@ To add a new vulnerability finding from your project level Vulnerability Report
1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > Vulnerability Report**.
-1. Select **Submit Vulnerability**.
+1. Select **Submit vulnerability**.
1. Complete the fields and submit the form.
You will be brought to the newly created vulnerability's detail page. Manually created records appear in the
diff --git a/doc/user/application_security/vulnerability_report/pipeline.md b/doc/user/application_security/vulnerability_report/pipeline.md
index 9e20e4f6f78..57c18cb045e 100644
--- a/doc/user/application_security/vulnerability_report/pipeline.md
+++ b/doc/user/application_security/vulnerability_report/pipeline.md
@@ -79,6 +79,23 @@ incorporated once the pipeline finishes.
| Resolved | No | Needs triage (Detected) |
| N/A (i.e.: new vulnerability) | No | Needs triage (Detected) |
+## Retention period for vulnerabilities
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/351524) in GitLab 15.5.
+
+GitLab has the following retention policies for vulnerabilities on non-default branches. Vulnerabilities are no longer available:
+
+- When the related CI job artifact expires.
+- 90 days after the pipeline is created, even if the related CI job artifacts are locked.
+
+To view vulnerabilities, either:
+
+- Run a new pipeline.
+- Download the related CI job artifacts if they are available.
+
+NOTE:
+This does not apply for the vulnerabilities existing on the default branch.
+
## Deduplication process
When a pipeline contains jobs that produce multiple security reports of the same type, it is possible that the same
diff --git a/doc/user/asciidoc.md b/doc/user/asciidoc.md
index 3ceecf88c5e..6b642363dd6 100644
--- a/doc/user/asciidoc.md
+++ b/doc/user/asciidoc.md
@@ -9,6 +9,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
GitLab uses the [Asciidoctor](https://asciidoctor.org) gem to convert AsciiDoc content to HTML5.
Consult the [Asciidoctor User Manual](https://asciidoctor.org/docs/user-manual/) for a complete Asciidoctor reference.
+You can use AsciiDoc in the following areas:
+
+- Wiki pages
+- AsciiDoc documents (`.adoc` or `.asciidoc`) inside repositories
+
## Syntax
Here's a brief reference of the most commonly used AsciiDoc syntax.
diff --git a/doc/user/clusters/agent/ci_cd_workflow.md b/doc/user/clusters/agent/ci_cd_workflow.md
index 454be3c53c7..2a66549f9cb 100644
--- a/doc/user/clusters/agent/ci_cd_workflow.md
+++ b/doc/user/clusters/agent/ci_cd_workflow.md
@@ -60,6 +60,7 @@ Authorization configuration can take one or two minutes to propagate.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/327850) in GitLab 14.4.
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/346566) to remove hierarchy restrictions in GitLab 15.6.
+> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/356831) to allow authorizing projects in a user namespace in GitLab 15.7.
To authorize the agent to access the GitLab project where you keep Kubernetes manifests:
@@ -73,7 +74,7 @@ To authorize the agent to access the GitLab project where you keep Kubernetes ma
- id: path/to/project
```
- - Authorized projects must have the same root group as the agent's configuration project.
+ - Authorized projects must have the same root group or user namespace as the agent's configuration project.
- You can install additional agents into the same cluster to accommodate additional hierarchies.
- You can authorize up to 100 projects.
@@ -286,6 +287,35 @@ The identity can be specified with the following keys:
See the [official Kubernetes documentation for details](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation).
+## Restrict project and group access to specific environments **(FREE)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/343885) in GitLab 15.7.
+
+By default, if your agent is [available to a project](#authorize-the-agent), all of the project's CI/CD jobs can use that agent.
+
+To restrict access to the agent to only jobs with specific environments, add `environments` to `ci_access.projects` or `ci_access.groups`. For example:
+
+ ```yaml
+ ci_access:
+ projects:
+ - id: path/to/project-1
+ - id: path/to/project-2
+ environments:
+ - staging
+ - review/*
+ groups:
+ - id: path/to/group-1
+ environments:
+ - production
+ ```
+
+In this example:
+
+- All CI/CD jobs under `project-1` can access the agent.
+- CI/CD jobs under `project-2` with `staging` or `review/*` environments can access the agent.
+ - `*` is a wildcard, so `review/*` matches all environments under `review`.
+- CI/CD jobs for projects under `group-1` with `production` environments can access the agent.
+
## Related topics
- [Self-paced classroom workshop](https://gitlab-for-eks.awsworkshop.io) (Uses AWS EKS, but you can use for other Kubernetes clusters)
diff --git a/doc/user/clusters/agent/gitops.md b/doc/user/clusters/agent/gitops.md
index c0d4a5e088f..bd7dfb3abee 100644
--- a/doc/user/clusters/agent/gitops.md
+++ b/doc/user/clusters/agent/gitops.md
@@ -9,6 +9,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/259669) in GitLab 13.7.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/332227) in GitLab 14.0, the `resource_inclusions` and `resource_exclusions` attributes were removed and `reconcile_timeout`, `dry_run_strategy`, `prune`, `prune_timeout`, `prune_propagation_policy`, and `inventory_policy` attributes were added.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/346567) from GitLab Premium to GitLab Free in 15.3.
+> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/346585) to make the `id` attribute optional in GitLab 15.7.
+> - Specifying a branch, tag, or commit reference to fetch the Kubernetes manifest files [introduced](https://gitlab.com/groups/gitlab-org/-/epics/4516) in GitLab 15.7.
With GitOps, you can manage containerized clusters and applications from a Git repository that:
@@ -64,6 +66,10 @@ The following snippet shows an example of the possible keys and values for the G
gitops:
manifest_projects:
- id: gitlab-org/cluster-integration/gitlab-agent
+ ref: # either `branch`, `tag` or `commit` can be specified
+ branch: production
+ # commit: <mysha>
+ # tag: v1.0
default_namespace: my-ns
paths:
# Read all YAML files from this directory.
@@ -83,7 +89,11 @@ gitops:
| Keyword | Description |
|--|--|
| `manifest_projects` | Projects where your Kubernetes manifests are stored. The agent monitors the files in the repositories in these projects. When manifest files change, the agent deploys the changes to the cluster. |
-| `id` | Required. Path to a Git repository that has Kubernetes manifests in YAML or JSON format. No authentication mechanisms are currently supported. |
+| `id` | Path to a Git repository that has Kubernetes manifests in YAML or JSON format. No authentication mechanisms are supported. Default is the agent configuration repository. |
+| `ref` | Optional. Git reference in the configured Git repository to fetch the Kubernetes manifest files from. If not specified or empty, the default branch is used. If specified, it must contain either `branch`, `tag`, or `commit`. |
+| `ref.branch` | Branch name in the configured Git repository to fetch the Kubernetes manifest files from. |
+| `ref.tag` | Tag name in the configured Git repository to fetch the Kubernetes manifest files from. |
+| `ref.commit` | Commit SHA in the configured Git repository to fetch the Kubernetes manifest files from. |
| `default_namespace` | Namespace to use if not set explicitly in object manifest. Also used for inventory `ConfigMap` objects. |
| `paths` | Repository paths to scan for manifest files. Directories with names that start with a dot `(.)` are ignored. |
| `paths[].glob` | Required. See [doublestar](https://github.com/bmatcuk/doublestar#about) and [the match function](https://pkg.go.dev/github.com/bmatcuk/doublestar/v2#Match) for globbing rules. |
diff --git a/doc/user/clusters/agent/gitops/helm.md b/doc/user/clusters/agent/gitops/helm.md
index 0ec87376636..b9a59d37f5d 100644
--- a/doc/user/clusters/agent/gitops/helm.md
+++ b/doc/user/clusters/agent/gitops/helm.md
@@ -6,7 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Using Helm charts to update a Kubernetes cluster (Alpha) **(FREE)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/371019) in GitLab 15.4.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/371019) in GitLab 15.4.
+> - Specifying a branch, tag, or commit reference to fetch the Kubernetes manifest files [introduced](https://gitlab.com/groups/gitlab-org/-/epics/4516) in GitLab 15.7.
You can deploy Helm charts to your Kubernetes cluster and keep the resources in your cluster in sync
with your charts and values. To do this, you use the pull-based GitOps features of the agent for
@@ -51,6 +52,8 @@ gitops:
source:
project:
id: my-group/my-project-with-chart
+ ref:
+ branch: production
path: dir-in-project/with/charts
namespace: my-ns
max_history: 1
@@ -68,6 +71,10 @@ gitops:
| `max_history` | Optional. Maximum number of release [revisions to store in the cluster](https://helm.sh/docs/helm/helm_history/). |
| `source` | Required. From where the chart should get installed. Only supports project sources. |
| `source.project.id` | Required. ID of the project where Helm chart is committed. Authentication is not supported. |
+| `source.project.ref` | Optional. Git reference in the configured Git repository to fetch the Chart from. If not specified or empty, the default branch is used. If specified, it must contain either `branch`, `tag`, or `commit`. |
+| `source.project.ref.branch` | Branch name in the configured Git repository to fetch the Chart from. |
+| `source.project.ref.tag` | Tag name in the configured Git repository to fetch the Chart from. |
+| `source.project.ref.commit` | Commit SHA in the configured Git repository to fetch the Chart from. |
| `source.project.path` | Optional. Path of the chart in the project repository. Root of the repository is used by default. Should be the directory with the `Chart.yaml` file. |
## Custom values
diff --git a/doc/user/clusters/agent/install/index.md b/doc/user/clusters/agent/install/index.md
index 2030052e3b0..62767f1dfd9 100644
--- a/doc/user/clusters/agent/install/index.md
+++ b/doc/user/clusters/agent/install/index.md
@@ -123,7 +123,7 @@ To install the agent on your cluster using Helm:
1. In your computer, open a terminal and [connect to your cluster](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/).
1. Run the command you copied when you [registered your agent with GitLab](#register-the-agent-with-gitlab).
-Optionally, you can [customize the Helm installation](#customize-the-helm-installation).
+Optionally, you can [customize the Helm installation](#customize-the-helm-installation). If you install the agent on a production system, you should customize the Helm installation to skip creating the service account.
##### Customize the Helm installation
diff --git a/doc/user/clusters/agent/vulnerabilities.md b/doc/user/clusters/agent/vulnerabilities.md
index dcb3276deb5..d9a9981d211 100644
--- a/doc/user/clusters/agent/vulnerabilities.md
+++ b/doc/user/clusters/agent/vulnerabilities.md
@@ -12,17 +12,19 @@ info: To determine the technical writer assigned to the Stage/Group associated w
To view cluster vulnerabilities, you can view the [vulnerability report](../../application_security/vulnerabilities/index.md).
You can also configure your agent so the vulnerabilities are displayed with other agent information in GitLab.
-## Enable operational container scanning **(ULTIMATE)**
+## Enable operational container scanning
-You can use operational container scanning
-to scan container images in your cluster for security vulnerabilities.
+You can use operational container scanning to scan container images in your cluster for security vulnerabilities. You
+can enable the scanner to run on a cadence as configured via the agent, or setup scan execution policies within a
+project that houses the agent.
NOTE:
In GitLab 15.0 and later, you do not need to install Starboard operator in the Kubernetes cluster.
-To begin scanning all resources in your cluster, add a `container_scanning`
-configuration block to your agent configuration with a `cadence` field
-containing a CRON expression for when the scans will be run.
+### Enable via agent configuration
+
+To enable scanning of all images within your Kubernetes cluster via the agent configuration, add a `container_scanning` configuration block to your agent
+configuration with a `cadence` field containing a [CRON expression](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm) for when the scans will be run.
```yaml
container_scanning:
@@ -34,29 +36,67 @@ The `cadence` field is required. GitLab supports the following types of CRON syn
- A daily cadence of once per hour at a specified hour, for example: `0 18 * * *`
- A weekly cadence of once per week on a specified day and at a specified hour, for example: `0 13 * * 0`
-It is possible that other elements of the CRON syntax will work in the cadence field, however, GitLab does not officially test or support them.
+NOTE:
+Other elements of the [CRON syntax](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm) may work in the cadence field if supported by the [cron](https://github.com/robfig/cron) we are using in our implementation, however, GitLab does not officially test or support them.
+
+NOTE:
+The CRON expression is evaluated in [UTC](https://www.timeanddate.com/worldclock/timezone/utc) using the system-time of the Kubernetes-agent pod.
By default, operational container scanning will attempt to scan the workloads in all
-namespaces for vulnerabilities. The `vulnerability_report` block has a `namespaces`
+namespaces for vulnerabilities. You can set the `vulnerability_report` block with the `namespaces`
field which can be used to restrict which namespaces are scanned. For example,
-if you would like to scan only the `development`, `staging`, and `production`
-namespaces, you can use this configuration:
+if you would like to scan only the `default`, `kube-system` namespaces, you can use this configuration:
```yaml
container_scanning:
cadence: '0 0 * * *'
vulnerability_report:
namespaces:
- - development
- - staging
- - production
+ - default
+ - kube-system
```
-## View cluster vulnerabilities
+## Enable via scan execution policies
+
+To enable scanning of all images within your Kubernetes cluster via scan execution policies, we can use the
+[scan execution policy editor](../../application_security/policies/scan-execution-policies.md#scan-execution-policy-editor)
+in order to create a new schedule rule.
-Prerequisite:
+NOTE:
+The Kubernetes agent must be running in your cluster in order to scan running container images
+
+Here is an example of a policy which enables operational container scanning within the cluster the Kubernetes agent is attached to:
+
+```yaml
+- name: Enforce Container Scanning in cluster connected through my-gitlab-agent for default and kube-system namespaces
+ enabled: true
+ rules:
+ - type: schedule
+ cadence: '0 10 * * *'
+ agents:
+ <agent-name>:
+ namespaces:
+ - 'default'
+ - 'kube-system'
+ actions:
+ - scan: container_scanning
+```
-- You must have at least the Developer role.
+The keys for a schedule rule are:
+
+- cadence (required): a [CRON expression](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm) for when the scans will be run
+- agents:<agent-name> (required): The name of the agent to use for scanning
+- agents:<agent-name>:namespaces (optional): The Kubernetes namespaces to scan. If omitted, all namespaces will be scanned
+
+NOTE:
+Other elements of the [CRON syntax](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm) may work in the cadence field if supported by the [cron](https://github.com/robfig/cron) we are using in our implementation, however, GitLab does not officially test or support them.
+
+NOTE:
+The CRON expression is evaluated in [UTC](https://www.timeanddate.com/worldclock/timezone/utc) using the system-time of the Kubernetes-agent pod.
+
+You can view the complete schema within the [scan execution policy documentation](../../application_security/policies/scan-execution-policies.md#scan-execution-policies-schema).
+
+## View cluster vulnerabilities
To view vulnerability information in GitLab:
@@ -68,3 +108,6 @@ To view vulnerability information in GitLab:
![Cluster agent security tab UI](../img/cluster_agent_security_tab_v14_8.png)
This information can also be found under [operational vulnerabilities](../../../user/application_security/vulnerability_report/index.md#operational-vulnerabilities).
+
+NOTE:
+You must have at least the Developer role.
diff --git a/doc/user/clusters/management_project_template.md b/doc/user/clusters/management_project_template.md
index cbe577b9b74..a8d874ed608 100644
--- a/doc/user/clusters/management_project_template.md
+++ b/doc/user/clusters/management_project_template.md
@@ -102,7 +102,7 @@ The [built-in supported applications](https://gitlab.com/gitlab-org/project-temp
- [Cert-manager](../infrastructure/clusters/manage/management_project_applications/certmanager.md)
- [GitLab Runner](../infrastructure/clusters/manage/management_project_applications/runner.md)
- [Ingress](../infrastructure/clusters/manage/management_project_applications/ingress.md)
-- [Prometheus](../infrastructure/clusters/manage/management_project_applications/prometheus.md)
+- [Prometheus](../../operations/metrics/index.md)
- [Vault](../infrastructure/clusters/manage/management_project_applications/vault.md)
Each application has an `applications/{app}/values.yaml` file.
diff --git a/doc/user/compliance/license_compliance/index.md b/doc/user/compliance/license_compliance/index.md
index fb5ce37c563..34434ef046a 100644
--- a/doc/user/compliance/license_compliance/index.md
+++ b/doc/user/compliance/license_compliance/index.md
@@ -177,7 +177,7 @@ directory of your project.
Depending on your language, you may need to specify the path to the individual
projects of a monorepo using the `LICENSE_FINDER_CLI_OPTS` variable. Passing in
the project paths can significantly speed up builds over using the `--recursive`
-license_finder option.
+License Finder option.
```yaml
include:
@@ -660,7 +660,7 @@ The process for importing Docker images into a local offline Docker registry dep
process by which external resources can be imported or temporarily accessed. Note that these scanners are [updated periodically](../../application_security/index.md#vulnerability-scanner-maintenance)
with new definitions, so consider if you are able to make periodic updates yourself.
-For details on saving and transporting Docker images as a file, see Docker's documentation on
+For details on saving and transporting Docker images as a file, see the Docker documentation on
[`docker save`](https://docs.docker.com/engine/reference/commandline/save/), [`docker load`](https://docs.docker.com/engine/reference/commandline/load/),
[`docker export`](https://docs.docker.com/engine/reference/commandline/export/), and [`docker import`](https://docs.docker.com/engine/reference/commandline/import/).
diff --git a/doc/user/free_user_limit.md b/doc/user/free_user_limit.md
index 35777847947..4f569098d4d 100644
--- a/doc/user/free_user_limit.md
+++ b/doc/user/free_user_limit.md
@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Free user limit **(FREE SAAS)**
-From October 19, 2022, a five-user limit will apply to top-level [namespaces](namespace/index.md) with private visibility on GitLab SaaS. These limits will roll out gradually, and impacted users will be notified in GitLab.com at least 60 days before the limit is applied.
+A five-user limit applies to top-level [namespaces](namespace/index.md) with private visibility on GitLab SaaS. This limit is being rolled out gradually, and impacted users will be notified in GitLab.com at least 60 days before the limit is applied.
When the five-user limit is applied, top-level private namespaces exceeding the user limit are placed in a read-only state. These namespaces cannot write new data to repositories, Git Large File Storage (LFS), packages, or registries.
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index d3d50ee1a8f..9bb1c4e968c 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -189,7 +189,7 @@ the default value [is the same as for self-managed instances](../admin_area/sett
|-------------------------------|--------------------|
| [Repository size including LFS](../admin_area/settings/account_and_limit_settings.md#repository-size-limit) | 10 GB |
| [Maximum import size](../project/settings/import_export.md#maximum-import-file-size) | 5 GB |
-| Maximum attachment size | 10 MB |
+| Maximum attachment size | 100 MB |
If you are near or over the repository size limit, you can either
[reduce your repository size with Git](../project/repository/reducing_the_repo_size_using_git.md)
@@ -314,7 +314,7 @@ The list of GitLab.com specific settings (and their defaults) is as follows:
| `max_wal_senders` | 32 | 0 |
| `max_wal_size` | 5GB | 1GB |
| `shared_buffers` | 112896MB | Based on how much memory is available |
-| `shared_preload_libraries` | pg_stat_statements | empty |
+| `shared_preload_libraries` | `pg_stat_statements` | empty |
| `shmall` | 30146560 | Based on the server's capabilities |
| `shmmax` | 123480309760 | Based on the server's capabilities |
| `wal_buffers` | 16MB | -1 |
@@ -445,11 +445,18 @@ If more than the maximum number of allowed connections occur concurrently, they
are dropped and users get
[an `ssh_exchange_identification` error](../../topics/git/troubleshooting_git.md#ssh_exchange_identification-error).
-### Import/export
+### Group and project import by uploading export files
-To help avoid abuse, project and group imports, exports, and export downloads
-are rate limited. See [Project import/export rate limits](../../user/project/settings/import_export.md#rate-limits) and [Group import/export rate limits](../../user/group/settings/import_export.md#rate-limits)
-for details.
+To help avoid abuse, the following are rate limited:
+
+- Project and group imports.
+- Group and project exports that use files.
+- Export downloads.
+
+For more information, see:
+
+- [Project import/export rate limits](../../user/project/settings/import_export.md#rate-limits).
+- [Group import/export rate limits](../../user/group/import/index.md#rate-limits).
### Non-configurable limits
diff --git a/doc/user/group/access_and_permissions.md b/doc/user/group/access_and_permissions.md
index 13a1fd31ee4..a7358db54df 100644
--- a/doc/user/group/access_and_permissions.md
+++ b/doc/user/group/access_and_permissions.md
@@ -228,7 +228,10 @@ projects in a group, allowing tighter control over project membership.
For example, if you want to lock the group for an [Audit Event](../../administration/audit_events.md),
you can guarantee that project membership cannot be modified during the audit.
-You can still invite groups or to add members to groups, implicitly giving members access to projects in the **locked** group.
+If group membership lock is enabled, the group owner can still:
+
+- Invite groups or add members to groups to give them access to projects in the **locked** group.
+- Change the role of group members.
The setting does not cascade. Projects in subgroups observe the subgroup configuration, ignoring the parent group.
@@ -239,8 +242,10 @@ To prevent members from being added to projects in a group:
1. Under **Membership**, select **Users cannot be added to projects in this group**.
1. Select **Save changes**.
-All users who previously had permissions can no longer add members to a group.
-API requests to add a new user to a project are not possible.
+After you lock the membership for a group:
+
+- All users who previously had permissions can no longer add members to a group.
+- API requests to add a new user to a project are not possible.
## Manage group memberships via LDAP **(PREMIUM SELF)**
diff --git a/doc/user/group/compliance_frameworks.md b/doc/user/group/compliance_frameworks.md
index 7bd545003db..0e976cec866 100644
--- a/doc/user/group/compliance_frameworks.md
+++ b/doc/user/group/compliance_frameworks.md
@@ -21,7 +21,7 @@ Group owners can create, edit, and delete compliance frameworks:
1. Expand the **Compliance frameworks** section.
1. Create, edit, or delete compliance frameworks.
-## Set a default compliance framework
+## Default compliance frameworks
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/375036) in GitLab 15.6.
@@ -29,6 +29,20 @@ Group owners can set a default compliance framework. The default framework is ap
that are created within that group. It does not affect the framework applied to the existing projects. The default
framework cannot be deleted.
+A compliance framework that is set to default has a **default** label.
+
+### Set and remove as default
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/375038) in GitLab 15.7.
+
+Group owners can set a compliance framework as default (or remove the setting):
+
+1. On the top bar, select **Main menu > Groups > View all groups** and find your group.
+1. On the left sidebar, select **Settings > General**.
+1. Expand the **Compliance frameworks** section and locate the compliance framework to set (or remove) as default.
+1. Select the vertical ellipsis (**{ellipsis_v}**) for the compliance frame and then select **Set default** (or
+ **Remove default**).
+
### Example GraphQL mutations for setting a default compliance framework
Creating a new compliance framework and setting it as the default framework for the group.
@@ -191,7 +205,7 @@ When creating such an MR against a project with CF pipelines, the above snippet
This is because in the context of the target project, `$CI_COMMIT_REF_NAME` evaluates to a non-existing branch name.
To get the correct context, use `$CI_MERGE_REQUEST_SOURCE_PROJECT_PATH` instead of `$CI_PROJECT_PATH`.
-This variable is only availabe in
+This variable is only available in
[merge request pipelines](../../ci/pipelines/merge_request_pipelines.md).
For example, for a configuration that supports both merge request pipelines originating in project forks and branch pipelines,
diff --git a/doc/user/group/contribution_analytics/index.md b/doc/user/group/contribution_analytics/index.md
index b1efd2e9251..ddf468e39b0 100644
--- a/doc/user/group/contribution_analytics/index.md
+++ b/doc/user/group/contribution_analytics/index.md
@@ -24,8 +24,7 @@ To view Contribution Analytics:
## Using Contribution Analytics
-There are three main bar graphs that illustrate the number of contributions per group
-member for the following:
+Three bar graphs illustrate the number of contributions made by each group member:
- Push events
- Merge requests
diff --git a/doc/user/group/devops_adoption/index.md b/doc/user/group/devops_adoption/index.md
index 67263f15f06..a81b61c50ce 100644
--- a/doc/user/group/devops_adoption/index.md
+++ b/doc/user/group/devops_adoption/index.md
@@ -12,7 +12,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - DAST and SAST metrics [added](https://gitlab.com/gitlab-org/gitlab/-/issues/328033) in GitLab 14.1.
> - Fuzz Testing metrics [added](https://gitlab.com/gitlab-org/gitlab/-/issues/330398) in GitLab 14.2.
> - Dependency Scanning metrics [added](https://gitlab.com/gitlab-org/gitlab/-/issues/328034) in GitLab 14.2.
-> - Multiselect [added](https://gitlab.com/gitlab-org/gitlab/-/issues/333586) in GitLab 14.2.
+> - Multi-select [added](https://gitlab.com/gitlab-org/gitlab/-/issues/333586) in GitLab 14.2.
> - Overview table [added](https://gitlab.com/gitlab-org/gitlab/-/issues/335638) in GitLab 14.3.
> - Adoption over time chart [added](https://gitlab.com/gitlab-org/gitlab/-/issues/337561) in GitLab 14.4.
diff --git a/doc/user/group/epics/manage_epics.md b/doc/user/group/epics/manage_epics.md
index 61aa5c5fe02..8e7b6fd82ad 100644
--- a/doc/user/group/epics/manage_epics.md
+++ b/doc/user/group/epics/manage_epics.md
@@ -494,7 +494,8 @@ The maximum number of direct child epics is 100.
### Child epics from other groups
-> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/8502) in GitLab 15.6 [with a flag](../../../administration/feature_flags.md) named `child_epics_from_different_hierarchies`. Disabled by default.
+> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/8502) in GitLab 15.6 [with a flag](../../../administration/feature_flags.md) named `child_epics_from_different_hierarchies`. Disabled by default.
+> - Minimum required role for the group [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/382503) from Reporter to Guest in GitLab 15.7.
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available per group, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `child_epics_from_different_hierarchies`.
@@ -504,16 +505,18 @@ You can add a child epic that belongs to a group that is different from the pare
Prerequisites:
-- You must have at least the Reporter role for both the child and parent epics' groups.
+- You must have at least the Guest role for both the child and parent epics' groups.
- Multi-level child epics must be available for both the child and parent epics' groups.
To add a child epic from another group, paste the epic's URL when [adding an existing epic](#add-a-child-epic-to-an-epic).
### Add a child epic to an epic
+> Minimum required role for the group [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/382503) from Reporter to Guest in GitLab 15.7.
+
Prerequisites:
-- You must have at least the Reporter role for the parent epic's group.
+- You must have at least the Guest role for the parent epic's group.
To add a new epic as child epic:
@@ -534,7 +537,8 @@ To add an existing epic as child epic:
### Move child epics between epics
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/33039) in GitLab 13.0.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/33039) in GitLab 13.0.
+> - Minimum required role for the group [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/382503) from Reporter to Guest in GitLab 15.7.
New child epics appear at the top of the list in the **Epics and Issues** tab.
You can move child epics from one epic to another.
@@ -543,7 +547,7 @@ Issues and child epics cannot be intermingled.
Prerequisites:
-- You must have at least the Reporter role for the parent epic's group.
+- You must have at least the Guest role for the parent epic's group.
To move child epics to another epic:
@@ -552,14 +556,15 @@ To move child epics to another epic:
### Reorder child epics assigned to an epic
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9367) in GitLab 12.5.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9367) in GitLab 12.5.
+> - Minimum required role for the group [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/382503) from Reporter to Guest in GitLab 15.7.
New child epics appear at the top of the list in the **Epics and Issues** tab.
You can reorder the list of child epics.
Prerequisites:
-- You must have at least the Reporter role for the parent epic's group.
+- You must have at least the Guest role for the parent epic's group.
To reorder child epics assigned to an epic:
@@ -568,9 +573,11 @@ To reorder child epics assigned to an epic:
### Remove a child epic from a parent epic
+> Minimum required role for the group [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/382503) from Reporter to Guest in GitLab 15.7.
+
Prerequisites:
-- You must have at least the Reporter role for the parent epic's group.
+- You must have at least the Guest role for the parent epic's group.
To remove a child epic from a parent epic:
diff --git a/doc/user/group/import/index.md b/doc/user/group/import/index.md
index 49515dd8a11..9a671ff6679 100644
--- a/doc/user/group/import/index.md
+++ b/doc/user/group/import/index.md
@@ -4,7 +4,23 @@ group: Import
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Migrating groups with GitLab Migration **(FREE)**
+# Migrating GitLab groups **(FREE)**
+
+You can migrate GitLab groups:
+
+- From self-managed GitLab to GitLab.com.
+- From GitLab.com to self-managed GitLab.
+- From one self-managed GitLab instance to another.
+- Between groups in the same GitLab instance.
+
+You can migrate groups in two ways:
+
+- By direct transfer (recommended).
+- By uploading an export file.
+
+If you migrate from GitLab.com to self-managed GitLab, an administrator can create users on the self-managed GitLab instance.
+
+## Migrate groups by direct transfer (recommended)
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/249160) in GitLab 13.7 for group resources [with a flag](../../feature_flags.md) named `bulk_import`. Disabled by default.
> - Group items [enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/338985) in GitLab 14.3.
@@ -18,78 +34,103 @@ On self-managed GitLab, by default [migrating project items](#migrated-project-i
this feature, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named
`bulk_import_projects`. On GitLab.com, migration of both groups and projects is available.
-Users with the Owner role on a top-level group can use GitLab Migration to migrate the group to:
+Prerequisites:
+
+- Network connection between instances or GitLab.com. Must support HTTPS.
+- Owner role on the top-level group to migrate.
+
+You can import top-level groups to:
- Another top-level group.
- The subgroup of any existing top-level group.
- Another GitLab instance, including GitLab.com.
-Migrating groups using GitLab Migration is not the same as [migrating groups using file exports](../settings/import_export.md).
-Importing and exporting groups using file exports requires you to export a group to a file and then import that file in
-another GitLab instance. Migrating groups using GitLab Migration automates this step.
+You can migrate:
+
+- By direct transfer using either the UI or the [API](../../../api/bulk_imports.md).
+- Many groups at once.
-## Import your groups into GitLab
+When migrating a top-level group to GitLab.com, all its subgroups and projects are migrated too.
-When you migrate a group, you connect to your GitLab instance and then choose
-groups to import. Not all the data is migrated. See:
+Not all group and project resources are imported. See list of migrated resources below:
- [Migrated group items](#migrated-group-items).
- [Migrated project items](#migrated-project-items).
-Leave feedback about group migration in [the relevant issue](https://gitlab.com/gitlab-org/gitlab/-/issues/284495).
+### Preparation
+
+GitLab maps users and their contributions correctly provided:
+
+- Users already exist on the target GitLab instance.
+- Users have a public email on the source GitLab instance that matches their primary email on the target GitLab instance.
+- Users' primary email addresses on the target GitLab instance are confirmed. Most users receives an email asking them to confirm their email address.
+- When using an OmniAuth provider like SAML, GitLab and SAML accounts of users on GitLab must be linked. All users on the target GitLab instance must sign in
+ and verify their account on the target GitLab instance.
-NOTE:
You might need to reconfigure your firewall to prevent blocking the connection on the self-managed
instance.
-### Connect to the remote GitLab instance
+If you use [SAML SSO for GitLab.com groups](../../group/saml_sso/index.md),
+contributing users must have [linked their SAML identity to their GitLab.com account](../../group/saml_sso/index.md#linking-saml-to-your-existing-gitlabcom-account).
-Before you begin, ensure that the target GitLab instance can communicate with the source over HTTPS
-(HTTP is not supported). You might need to reconfigure your firewall to prevent blocking the connection on the self-managed
-instance.
+When migrating to GitLab.com, you must create users manually unless [SCIM](../../group/saml_sso/scim_setup.md) is used. Creating users with the API is only
+available to self-managed instances because it requires administrator access.
-Then create the group you want to import into, and connect:
+### Connect to the source GitLab instance
-1. Create a new group or subgroup:
+Create the group you want to import to and connect the source:
- - On the top bar, select `+` and then **New group**.
- - Or, on an existing group's page, in the top right, select **New subgroup**.
+1. Create either:
-1. Select **Import group**.
-1. Enter the source URL of your GitLab instance.
+ - A new group. On the top bar, select **{plus-square}**, then **New group**, and select **Import group**.
+ - A new subgroup. On existing group's page, either:
+ - Select **New subgroup**.
+ - On the top bar, Select **{plus-square}** and then **New subgroup**. Then on the left sidebar, select the **import an existing group** link.
+1. Enter the URL of your source GitLab instance.
1. Generate or copy a [personal access token](../../../user/profile/personal_access_tokens.md)
- with the `api` and `read_repository` scopes on your remote GitLab instance.
-1. Enter the [personal access token](../../../user/profile/personal_access_tokens.md) for your remote GitLab instance.
+ with the `api` scope on your source GitLab instance. Both `api` and `read_repository` scopes are required when migrating from GitLab 15.0 and earlier.
+1. Enter the [personal access token](../../../user/profile/personal_access_tokens.md) for your source GitLab instance.
1. Select **Connect instance**.
### Select the groups to import
-After you have authorized access to the GitLab instance, you are redirected to the GitLab Group
-Migration importer page. The remote groups you have the Owner role for are listed.
+After you have authorized access to the source GitLab instance, you are redirected to the GitLab group
+importer page. The top-level groups on the connected source instance you have the Owner role for are listed.
-1. By default, the proposed group namespaces match the names as they exist in remote instance, but based on your permissions, you can choose to edit these names before you proceed to import any of them.
+1. By default, the proposed group namespaces match the names as they exist in source instance, but based on your permissions, you can choose to edit these names before you proceed to import any of them.
1. Next to the groups you want to import, select **Import**.
1. The **Status** column shows the import status of each group. If you leave the page open, it updates in real-time.
1. After a group has been imported, select its GitLab path to open its GitLab URL.
![Group Importer page](img/bulk_imports_v14_1.png)
-## Automate group and project import **(PREMIUM)**
+### Group import history
-For information on automating user, group, and project import API calls, see
-[Automate group and project import](../../project/import/index.md#automate-group-and-project-import).
+You can view all groups migrated by you by direct transfer listed on the group import history page. This list includes:
+
+- Paths of source groups.
+- Paths of destination groups.
+- Start date of each import.
+- Status of each import.
+- Error details if any errors occurred.
-## Migrated group items
+To view group import history:
+
+1. Sign in to GitLab.
+1. On the top bar, select **Create new…** (**{plus-square}**).
+1. Select **New group**.
+1. Select **Import group**.
+1. Select **History** in the upper right corner.
+1. If there are any errors for a particular import, you can see them by selecting **Details**.
+
+### Migrated group items
The [`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/group/import_export.yml)
-file for groups lists many of the items migrated when migrating groups using group migration. View this file in the branch
+file for groups lists many of the items imported when migrating groups by direct transfer. View this file in the branch
for your version of GitLab to see the list of items relevant to you. For example,
[`import_export.yml` on the `14-10-stable-ee` branch](https://gitlab.com/gitlab-org/gitlab/-/blob/14-10-stable-ee/lib/gitlab/import_export/group/import_export.yml).
-Migrating projects with file exports uses the same export and import mechanisms as creating projects from templates at the [group](../custom_project_templates.md) and
-[instance](../../admin_area/custom_project_templates.md) levels. Therefore, the list of exported items is the same.
-
-Items that are migrated to the target instance include:
+Group items that are migrated to the target instance include:
- Badges ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292431) in 13.11)
- Board Lists
@@ -114,24 +155,24 @@ Items that are migrated to the target instance include:
Any other items are **not** migrated.
-## Migrated project items
+### Migrated project items
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267945) in GitLab 14.4 [with a flag](../../feature_flags.md) named `bulk_import_projects`. Disabled by default.
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/339941) in GitLab 15.6.
FLAG:
-On self-managed GitLab, migrating project resources are not available by default. To make them available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `bulk_import_projects`. On GitLab.com, migration of project resources is available.
+On self-managed GitLab, migrating project resources when migrating groups is not available by default. To make it available ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `bulk_import_projects`. On GitLab.com, groups are migrated with all their projects by default.
The [`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/project/import_export.yml)
-file for projects lists many of the items migrated when migrating projects using group migration. View this file in the branch
+file for projects lists many of the items imported when migrating projects using group migration. View this file in the branch
for your version of GitLab to see the list of items relevant to you. For example,
[`import_export.yml` on the `14-10-stable-ee` branch](https://gitlab.com/gitlab-org/gitlab/-/blob/14-10-stable-ee/lib/gitlab/import_export/project/import_export.yml).
-Migrating projects with file exports uses the same export and import mechanisms as creating projects from templates at the [group](../../group/custom_project_templates.md) and
-[instance](../../admin_area/custom_project_templates.md) levels. Therefore, the list of exported items is the same.
+Project items that are migrated to the target instance include:
- Projects ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267945) in GitLab 14.4)
- Auto DevOps ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339410) in GitLab 14.6)
+ - Badges ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75029) in GitLab 14.6)
- Branches (including protected branches) ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339414) in GitLab 14.7)
- CI Pipelines ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339407) in GitLab 14.6)
- Designs ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339421) in GitLab 15.1)
@@ -141,6 +182,8 @@ Migrating projects with file exports uses the same export and import mechanisms
- Issue resource milestone events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
- Issue resource iteration events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
- Merge request URL references ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267947) in GitLab 15.6)
+ - Time Tracking ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267946) in GitLab 14.4)
+ - Issue boards ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71661) in GitLab 14.4)
- Labels ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339419) in GitLab 14.4)
- LFS Objects ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339405) in GitLab 14.8)
- Members ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/341886) in GitLab 14.8)
@@ -151,18 +194,33 @@ Migrating projects with file exports uses the same export and import mechanisms
- Merge request resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
- Merge request resource milestone events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
- Issue URL references ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267947) in GitLab 15.6)
- - Migrate Push Rules ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339403) in GitLab 14.6)
- - Pull Requests (including external pull requests) ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339409) in GitLab 14.5)
+ - Time Tracking ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339403) in GitLab 14.5)
+ - Push Rules ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339403) in GitLab 14.6)
+ - Milestones ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339417) in GitLab 14.5)
+ - External Pull Requests ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339409) in GitLab 14.5)
- Pipeline History ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339412) in GitLab 14.6)
- Pipeline Schedules ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339408) in GitLab 14.8)
+ - Project Features ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74722) in GitLab 14.6)
- Releases ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339422) in GitLab 15.1)
- Release Evidences ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/360567) in GitLab 15.1)
- Repositories ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267945) in GitLab 14.4)
- Snippets ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/343438) in GitLab 14.6)
+ - Settings ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339416) in GitLab 14.6)
+ - Avatar ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75249) in GitLab 14.6)
+ - Container Expiration Policy ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75653) in GitLab 14.6)
+ - Project Properties ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75898) in GitLab 14.6)
+ - Service Desk ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75653) in GitLab 14.6)
- Uploads ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339401) in GitLab 14.5)
- Wikis ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345923) in GitLab 14.6)
-## Troubleshooting Group Migration
+Items excluded from migration, because they contain sensitive information:
+
+- Pipeline Triggers.
+
+Migrating projects with file exports uses the same export and import mechanisms as creating projects from templates at the [group](../../group/custom_project_templates.md) and
+[instance](../../admin_area/custom_project_templates.md) levels. Therefore, the list of exported items is the same.
+
+### Troubleshooting
In a [rails console session](../../../administration/operations/rails_console.md#starting-a-rails-console-session),
you can find the failure or error messages for the group import attempt using:
@@ -184,7 +242,10 @@ entities.map(&:failures).flatten
entities.where(status: [-1]).pluck(:destination_name, :destination_namespace, :status)
```
-### Stale imports
+You can also see all migrated entities with any failures related to them using an
+[API endpoint](../../../api/bulk_imports.md#list-all-gitlab-migrations-entities).
+
+#### Stale imports
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352985) in GitLab 14.10.
@@ -199,10 +260,12 @@ import = BulkImports::Entity.where(namespace_id: Group.id).map(&:bulk_import)
import.status #=> 3 means that the import timed out.
```
-### Error: `404 Group Not Found`
+#### Error: `404 Group Not Found`
+
+If you attempt to import a group that has a path comprised of only numbers (for example, `5000`), GitLab attempts to
+find the group by ID instead of the path. This causes a `404 Group Not Found` error in GitLab 15.4 and earlier.
-If you attempt to import a group that has a path comprised of only numbers (for example, `5000`), GitLab attempts to find the group by ID instead of the
-path. This causes a `404 Group Not Found` error. To solve this, the source group path must be changed to include a non-numerical character using either:
+To solve this, you must change the source group path to include a non-numerical character using either:
- The GitLab UI:
@@ -212,3 +275,170 @@ path. This causes a `404 Group Not Found` error. To solve this, the source group
1. Under **Change group URL**, change the group URL to include non-numeric characters.
- The [Groups API](../../../api/groups.md#update-group).
+
+### Provide feedback
+
+Please leave your feedback about migrating groups by direct transfer in
+[the feedback issue](https://gitlab.com/gitlab-org/gitlab/-/issues/284495).
+
+## Migrate groups by uploading an export file (deprecated)
+
+> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2888) in GitLab 13.0 as an experimental feature. May change in future releases.
+> - [Deprecated](https://gitlab.com/groups/gitlab-org/-/epics/4619) in GitLab 14.6.
+
+WARNING:
+This feature was [deprecated](https://gitlab.com/groups/gitlab-org/-/epics/4619) in GitLab 14.6 and replaced by
+[migrating groups by direct transfer](#migrate-groups-by-direct-transfer-recommended). To follow progress on a solution for
+[offline environments](../../application_security/offline_deployments/index.md), see
+[the relevant epic](https://gitlab.com/groups/gitlab-org/-/epics/8985).
+
+Prerequisites:
+
+- Owner role on the group to migrate.
+
+Using file exports, you can:
+
+- Export any group to a file and upload that file to another GitLab instance or to another location on the same instance.
+- Use either the GitLab UI or the [API](../../../api/group_import_export.md).
+- Migrate groups one by one, then export and import each project for the groups one by one.
+
+GitLab maps user contributions correctly when an admin access token is used to perform the import. GitLab does not map
+user contributions correctly when you are importing from a self-managed instance to GitLab.com. Correct mapping of user
+contributions when importing from a self-managed instance to GitLab.com can be preserved with paid involvement of
+Professional Services team.
+
+Note the following:
+
+- Exports are stored in a temporary directory and are deleted every 24 hours by a specific worker.
+- To preserve group-level relationships from imported projects, export and import groups first so that projects can
+ be imported into the desired group structure.
+- Imported groups are given a `private` visibility level, unless imported into a parent group.
+- If imported into a parent group, a subgroup inherits the same level of visibility unless otherwise restricted.
+- You can export groups from the [Community Edition to the Enterprise Edition](https://about.gitlab.com/install/ce-or-ee/)
+ and vice versa. The Enterprise Edition retains some group data that isn't part of the Community Edition. If you're
+ exporting a group from the Enterprise Edition to the Community Edition, you may lose this data. For more information,
+ see [downgrading from EE to CE](../../../index.md).
+
+### Exported contents
+
+The [`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/group/import_export.yml)
+file for groups lists items exported and imported when migrating groups using file exports. View this file in the branch
+for your version of GitLab to see the list of items relevant to you. For example,
+[`import_export.yml` on the `14-10-stable-ee` branch](https://gitlab.com/gitlab-org/gitlab/-/blob/14-10-stable-ee/lib/gitlab/import_export/group/import_export.yml).
+
+Group items that are exported include:
+
+- Milestones
+- Labels
+- Boards and Board Lists
+- Badges
+- Subgroups (including all the aforementioned data)
+- Epics
+ - Epic resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
+- Events
+- [Wikis](../../project/wiki/group.md)
+ ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53247) in GitLab 13.9)
+- Iterations cadences ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95372) in 15.4)
+
+Items that are **not** exported include:
+
+- Projects
+- Runner tokens
+- SAML discovery tokens
+
+### Preparation
+
+- To preserve the member list and their respective permissions on imported groups, review the users in these groups. Make
+sure these users exist before importing the desired groups.
+- Users must set a public email in the source GitLab instance that matches one of their verified emails in the target GitLab instance.
+
+### Enable export for a group
+
+Prerequisite:
+
+- You must have the Owner role for the group.
+
+To enable import and export for a group:
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Settings > General**.
+1. Expand **Visibility and access controls**.
+1. In the **Import sources** section, select the checkboxes for the sources you want.
+
+### Export a group
+
+Prerequisites:
+
+- You must have the Owner role for the group.
+
+To export the contents of a group:
+
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > General**.
+1. In the **Advanced** section, select **Export Group**.
+1. After the export is generated, you should receive an email with a link to the [exported contents](#exported-contents)
+ in a compressed tar archive, with contents in NDJSON format.
+1. Alternatively, you can download the export from the UI:
+
+ 1. Return to your group's **Settings > General** page.
+ 1. In the **Advanced** section, select **Download export**.
+ You can also generate a new file by selecting **Regenerate export**.
+
+You can also export a group [using the API](../../../api/group_import_export.md).
+
+### Import the group
+
+1. Create a new group:
+ - On the top bar, select **Create new…** (**{plus-square}**) and then **New group**.
+ - On an existing group's page, select the **New subgroup** button.
+1. Select **Import group**.
+1. Enter your group name.
+1. Accept or modify the associated group URL.
+1. Select **Choose file**.
+1. Select the file that you exported in the [Export a group](#export-a-group) section.
+1. To begin importing, select **Import group**.
+
+Your newly imported group page appears after the operation completes.
+
+NOTE:
+The maximum import file size can be set by the administrator, default is `0` (unlimited).
+As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the
+[Application settings API](../../../api/settings.md#change-application-settings) or the
+[Admin Area](../../admin_area/settings/account_and_limit_settings.md).
+Default [modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50MB to 0 in GitLab 13.8.
+
+### Rate limits
+
+To help avoid abuse, by default, users are rate limited to:
+
+| Request Type | Limit |
+| ---------------- | ---------------------------------------- |
+| Export | 6 groups per minute |
+| Download export | 1 download per group per minute |
+| Import | 6 groups per minute |
+
+### Version history
+
+#### 14.0+
+
+In GitLab 14.0, the JSON format is no longer supported for project and group exports. To allow for a
+transitional period, you can still import any JSON exports. The new format for imports and exports
+is NDJSON.
+
+#### 13.0+
+
+GitLab can import bundles that were exported from a different GitLab deployment.
+This ability is limited to two previous GitLab [minor](../../../policy/maintenance.md#versioning)
+releases, which is similar to our process for [Security Releases](../../../policy/maintenance.md#security-releases).
+
+For example:
+
+| Current version | Can import bundles exported from |
+|-----------------|----------------------------------|
+| 13.0 | 13.0, 12.10, 12.9 |
+| 13.1 | 13.1, 13.0, 12.10 |
+
+## Automate group and project import **(PREMIUM)**
+
+For information on automating user, group, and project import API calls, see
+[Automate group and project import](../../project/import/index.md#automate-group-and-project-import).
diff --git a/doc/user/group/manage.md b/doc/user/group/manage.md
index a63e6c6dd7f..414b80d0f1d 100644
--- a/doc/user/group/manage.md
+++ b/doc/user/group/manage.md
@@ -78,7 +78,7 @@ If you don't want to wait, you can remove a group immediately.
Prerequisites:
-- You must have at least the Owner role for a group.
+- You must have the Owner role for a group.
- You have [marked the group for deletion](#remove-a-group).
To immediately remove a group marked for deletion:
@@ -172,7 +172,7 @@ Prerequisite:
1. On the left sidebar, select **Group information > Members**.
1. Select **Invite members**.
1. Fill in the fields.
- - The role applies to all projects in the group. [Learn more about permissions](../permissions.md).
+ - The role applies to all projects in the group. For more information, see [permissions](../permissions.md).
- On the **Access expiration date**, the user can no longer access projects in the group.
1. Select **Invite**.
@@ -530,10 +530,10 @@ in a subgroup has access to the templates for that subgroup and
any immediate parent groups.
To learn how to create templates for issues and merge requests, see
-[Description templates](../project/description_templates.md).
+[description templates](../project/description_templates.md).
Define project templates at a group level by setting a group as the template source.
-[Learn more about group-level project templates](custom_project_templates.md).
+For more information, see [group-level project templates](custom_project_templates.md).
### Enable group file template **(PREMIUM)**
diff --git a/doc/user/group/saml_sso/group_sync.md b/doc/user/group/saml_sso/group_sync.md
index 001c73b6979..80d145fc6bb 100644
--- a/doc/user/group/saml_sso/group_sync.md
+++ b/doc/user/group/saml_sso/group_sync.md
@@ -10,9 +10,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/363084) for self-managed instances in GitLab 15.1.
WARNING:
-Changing Group Sync configuration can remove users from the mapped GitLab group.
+Adding or changing Group Sync configuration can remove users from the mapped GitLab group.
Removal happens if there is any mismatch between the group names and the list of `groups` in the SAML response.
-If changes must be made, ensure either the SAML response includes the `groups` attribute
+Before making changes, ensure either the SAML response includes the `groups` attribute
and the `AttributeValue` value matches the **SAML Group Name** in GitLab,
or that all groups are removed from GitLab to disable Group Sync.
@@ -21,17 +21,29 @@ For a demo of Group Sync using Azure, see [Demo: SAML Group Sync](https://youtu.
## Configure SAML Group Sync
+NOTE:
+You must include the SAML configuration block on all Sidekiq nodes in addition to Rails application nodes if you:
+
+- Use SAML Group Sync.
+- Have multiple GitLab nodes, for example in a distributed or highly available architecture.
+
+WARNING:
+To prevent users being accidentally removed from the GitLab group, follow these instructions closely before
+enabling Group Sync in GitLab.
+
To configure SAML Group Sync:
-- For GitLab self-managed:
- 1. Configure the [SAML OmniAuth Provider](../../../integration/saml.md).
- 1. Ensure your SAML identity provider sends an attribute statement with the same name as the value of the `groups_attribute` setting.
-- For GitLab.com:
- 1. See [SAML SSO for GitLab.com groups](index.md).
- 1. Ensure your SAML identity provider sends an attribute statement named `Groups` or `groups`.
+1. Configure the identity Provider:
+ - For self-managed GitLab, see the [SAML OmniAuth Provider documentation](../../../integration/saml.md).
+ - For GitLab.com, see the [SAML SSO for GitLab.com groups documentation](index.md).
+
+1. Capture [a SAML response](troubleshooting.md#saml-debugging-tools) during the sign-in process to confirm your SAML identity provider sends an attribute statement:
+ - For self-managed GitLab, with the same name as the value of the `groups_attribute` setting.
+ - For GitLab.com, named `Groups` or `groups`.
NOTE:
-The value for `Groups` or `groups` in the SAML response can be either the group name or the group ID.
+The value for `Groups` or `groups` in the SAML response may be either the group name or an ID.
+For example, Azure AD sends the Azure Group Object ID instead of the name. Use the ID value when configuring [SAML Group Links](#configure-saml-group-links).
```xml
<saml:AttributeStatement>
@@ -55,7 +67,7 @@ a SAML identity provider group name to a GitLab role. This can be done for a top
To link the SAML groups:
-1. In **SAML Group Name**, enter the value of the relevant `saml:AttributeValue`.
+1. In **SAML Group Name**, enter the value of the relevant `saml:AttributeValue`. The value entered here must exactly match the value sent in the SAML response. For some IdPs, this may be a group ID or object ID (Azure AD) instead of a friendly group name.
1. Choose the role in **Access Level**.
1. Select **Save**.
1. Repeat to add additional group links if required.
@@ -177,4 +189,4 @@ Because of a [known issue with Azure AD](https://support.esri.com/en/technical-a
in the user's SAML assertion.
To work around this issue, allow more than 150 group IDs to be sent in SAML token using configuration steps in the
-[Azure AD documentation](https://support.esri.com/en/technical-article/000022190).
+[Azure AD documentation](https://support.esri.com/en/technical-article/000022190).
diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md
index 1c5e7ff0115..bd10560e138 100644
--- a/doc/user/group/saml_sso/index.md
+++ b/doc/user/group/saml_sso/index.md
@@ -53,7 +53,6 @@ GitLab.com uses the SAML NameID to identify users. The NameID element:
also case-insensitive, which can result in users being unable to sign in.
The relevant field name and recommended value for supported providers are in the [provider specific notes](#providers).
-appropriate corresponding field.
WARNING:
Once users have signed into GitLab using the SSO SAML setup, changing the `NameID` breaks the configuration and potentially locks users out of the GitLab group.
@@ -72,7 +71,7 @@ must be specified as an attribute named `email` or `mail`.
You can configure the following attributes with GitLab.com Group SAML:
- `username` or `nickname`. We recommend you configure only one of these.
-- The [attributes available](../../../integration/saml.md#assertions) to self-managed GitLab instances.
+- The [attributes available](../../../integration/saml.md#configure-assertions) to self-managed GitLab instances.
### Metadata configuration
@@ -98,7 +97,7 @@ After you set up your identity provider to work with GitLab, you must configure
![Group SAML Settings for GitLab.com](img/group_saml_settings_v13_12.png)
NOTE:
-The certificate [fingerprint algorithm](../../../integration/saml.md#notes-on-configuring-your-identity-provider) must be in SHA1. When configuring the identity provider (such as [Google Workspace](#google-workspace-setup-notes)), use a secure signature algorithm.
+The certificate [fingerprint algorithm](../../../integration/saml.md#configure-saml-on-your-idp) must be in SHA1. When configuring the identity provider (such as [Google Workspace](#google-workspace-setup-notes)), use a secure signature algorithm.
### Additional configuration information
@@ -124,7 +123,7 @@ It can also help to compare the XML response from your provider with our [exampl
> - [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/215155) in GitLab 15.5 [with a flag](../../../administration/feature_flags.md) named `transparent_sso_enforcement` to include transparent enforcement even when SSO enforcement is not enabled. Disabled on GitLab.com.
FLAG:
-On self-managed GitLab, transparent SSO enforcement is unavailable. On GitLab.com, transparent SSO enforcement is unavailable and can be configured by GitLab.com administrators only.
+On self-managed GitLab, transparent SSO enforcement is unavailable. On GitLab.com, see the [Transparent SSO rollout](https://gitlab.com/gitlab-org/gitlab/-/issues/375788) issue for the current status.
SSO is enforced when users access groups and projects in the organization's group hierarchy. Users can view other groups and projects without SSO sign in.
@@ -178,13 +177,24 @@ When SSO is enforced, users are not immediately revoked. If the user:
- Has an active session, they can continue accessing the group for up to 24 hours until the identity
provider session times out.
+### Selectively enable and disable transparent SSO enforcement
+
+There are two feature flags associated with this feature to allow precise control. If a customer has a problem with transparent SSO on GitLab.com, GitLab can help troubleshoot and override the feature flag as necessary.
+
+**`transparent_sso_enforcement`:** This feature flag should only be enabled or disabled by the Authentication and Authorization group
+or in the case of a serious and widespread issue affecting many groups or users. See [issue 375788](https://gitlab.com/gitlab-org/gitlab/-/issues/375788) for the current GitLab.com rollout status.
+
+**`transparent_sso_enforcement_override`:** When the `transparent_sso_enforcement` feature flag is enabled, support or production teams can
+turn off transparent SSO by enabling this feature flag for a specific customer group. **Enabling** this feature flag
+disables transparent SSO enforcement.
+
## Providers
The SAML standard means that you can use a wide range of identity providers with GitLab. Your identity provider might have relevant documentation. It can be generic SAML documentation or specifically targeted for GitLab.
When [configuring your identity provider](#configure-your-identity-provider), consider the notes below for specific providers to help avoid common issues and as a guide for terminology used.
-For providers not listed below, you can refer to the [instance SAML notes on configuring an identity provider](../../../integration/saml.md#notes-on-configuring-your-identity-provider)
+For providers not listed below, you can refer to the [instance SAML notes on configuring an identity provider](../../../integration/saml.md#configure-saml-on-your-idp)
for additional guidance on information your identity provider may require.
GitLab provides the following information for guidance only.
@@ -338,10 +348,14 @@ When a user tries to sign in with Group SSO, GitLab attempts to find or create a
### Linking SAML to your existing GitLab.com account
+> **Remember me** checkbox [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/121569) in GitLab 15.7.
+
To link SAML to your existing GitLab.com account:
1. Sign in to your GitLab.com account. [Reset your password](https://gitlab.com/users/password/new) if necessary.
1. Locate and visit the **GitLab single sign-on URL** for the group you're signing in to. A group owner can find this on the group's **Settings > SAML SSO** page. If the sign-in URL is configured, users can connect to the GitLab app from the identity provider.
+1. Optional. Select the **Remember me** checkbox to stay signed in to GitLab for 2 weeks. You may still be asked to re-authenticate with your SAML provider
+ more frequently.
1. Select **Authorize**.
1. Enter your credentials on the identity provider if prompted.
1. You are then redirected back to GitLab.com and should now have access to the group. In the future, you can use SAML to sign in to GitLab.com.
diff --git a/doc/user/group/saml_sso/troubleshooting.md b/doc/user/group/saml_sso/troubleshooting.md
index 7dafd2c5075..f8075e62ecc 100644
--- a/doc/user/group/saml_sso/troubleshooting.md
+++ b/doc/user/group/saml_sso/troubleshooting.md
@@ -52,7 +52,7 @@ You can use one of the following to troubleshoot SAML:
- A [quick start guide to start a Docker container](../../../administration/troubleshooting/test_environments.md#saml)
with a plug and play SAML 2.0 identity provider if you only require a SAML provider.
- A local environment by
- [enabling SAML for groups on a self-managed instance](../../../integration/saml.md#configuring-group-saml-on-a-self-managed-gitlab-instance).
+ [enabling SAML for groups on a self-managed instance](../../../integration/saml.md#group-saml-on-a-self-managed-gitlab-instance).
## Verify configuration
@@ -96,7 +96,14 @@ In these cases, use one of the [SAML debugging tools](#saml-debugging-tools), or
a group owner can get a copy of the SAML response from when they select
the "Verify SAML Configuration" button on the group SSO Settings page.
-Use a base64 decoder to see a human-readable version of the SAML response.
+Use a base64 decoder to see a human-readable version of the SAML response. To avoid pasting the SAML response online to decode it, you can use your
+browser's console in the developers tools:
+
+```javascript
+atob(decodeURI("<paste_SAML_response_here>"))
+```
+
+You should get the SAML response in XML format as output.
## Configuration errors
@@ -138,7 +145,7 @@ Make sure this information is provided.
Another issue that can result in this error is when the correct information is being sent by
the identity provider, but the attributes don't match the names in the OmniAuth `info` hash. In this case,
you must set `attribute_statements` in the SAML configuration to
-[map the attribute names in your SAML Response to the corresponding OmniAuth `info` hash names](../../../integration/saml.md#attribute_statements).
+[map the attribute names in your SAML Response to the corresponding OmniAuth `info` hash names](../../../integration/saml.md#map-saml-response-attribute-names).
## User sign in banner error messages
@@ -221,7 +228,7 @@ If all users are receiving a `404` when attempting to sign in using SAML, confir
[there is an active subscription](../../../subscriptions/gitlab_com/index.md#view-your-gitlab-saas-subscription) being used in this SAML SSO namespace.
If you receive a `404` during setup when using "verify configuration", make sure you have used the correct
-[SHA-1 generated fingerprint](../../../integration/saml.md#notes-on-configuring-your-identity-provider).
+[SHA-1 generated fingerprint](../../../integration/saml.md#configure-saml-on-your-idp).
If a user is trying to sign in for the first time and the GitLab single sign-on URL has not [been configured](index.md#configure-your-identity-provider), they may see a 404.
As outlined in the [user access section](index.md#linking-saml-to-your-existing-gitlabcom-account), a group Owner needs to provide the URL to users.
@@ -262,3 +269,15 @@ Pay particular attention to the following 403 errors:
- `app_not_configured`
- `app_not_configured_for_user`
+
+## SAML Name ID and email address do not match your user account **(PREMIUM SAAS)**
+
+If users encounter the error `SAML Name ID and email address do not match your user account. Contact an administrator.`
+this means:
+
+- The NameID value sent by SAML does not match the existing SAML identity `extern_uid` value.
+- Either the SAML response did not include an email address or the email address did not match the user's GitLab email address.
+
+A GitLab group Owner can use the [SAML API](../../../api/saml.md) to update the user's SAML `extern_uid`.
+The `extern_uid` value must match the Name ID value sent by the SAML identity provider (IdP). Depending on the IdP configuration
+this may be a generated unique ID, an email address, or other value.
diff --git a/doc/user/group/settings/group_access_tokens.md b/doc/user/group/settings/group_access_tokens.md
index 158e1654c6e..6e0caa633eb 100644
--- a/doc/user/group/settings/group_access_tokens.md
+++ b/doc/user/group/settings/group_access_tokens.md
@@ -68,6 +68,11 @@ To create a group access token:
A group access token is displayed. Save the group access token somewhere safe. After you leave or refresh the page, you can't view it again.
+WARNING:
+Group access tokens are treated as [internal users](../../../development/internal_users.md).
+If an internal user creates a group access token, that token is able to access all
+groups that have visibility level set to [Internal](../../public_access.md).
+
## Create a group access token using Rails console
GitLab 14.6 and earlier doesn't support creating group access tokens using the UI
diff --git a/doc/user/group/settings/import_export.md b/doc/user/group/settings/import_export.md
index cec17688902..ff64a7dcd54 100644
--- a/doc/user/group/settings/import_export.md
+++ b/doc/user/group/settings/import_export.md
@@ -1,164 +1,11 @@
---
-stage: Manage
-group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+redirect_to: '../import/index.md'
+remove_date: '2023-03-08'
---
-# Migrating groups using file exports (deprecated) **(FREE)**
+This document was moved to [another location](../import/index.md).
-> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2888) in GitLab 13.0 as an experimental feature. May change in future releases.
-> - [Deprecated](https://gitlab.com/groups/gitlab-org/-/epics/4619) in GitLab 14.6.
-
-WARNING:
-This feature was [deprecated](https://gitlab.com/groups/gitlab-org/-/epics/4619) in GitLab 14.6 and replaced by
-[a different migration method](../import/index.md). To follow progress on a solution for
-[offline environments](../../application_security/offline_deployments/index.md), see
-[the relevant issue](https://gitlab.com/gitlab-org/gitlab/-/issues/363406).
-
-You can export groups, with all their related data, from one GitLab instance to another. You can also:
-
-- [Migrate groups](../import/index.md) using the preferred method.
-- [Migrate projects using file exports](../../project/settings/import_export.md).
-
-## Enable export for a group
-
-Prerequisite:
-
-- You must have the Owner role for the group.
-
-To enable import and export for a group:
-
-1. On the top bar, select **Main menu > Admin**.
-1. On the left sidebar, select **Settings > General**.
-1. Expand **Visibility and access controls**.
-1. In the **Import sources** section, select the checkboxes for the sources you want.
-
-## Important Notes
-
-Note the following:
-
-- Exports are stored in a temporary directory and are deleted every 24 hours by a specific worker.
-- To preserve group-level relationships from imported projects, run the Group Import/Export first, to allow projects to
-be imported into the desired group structure.
-- Imported groups are given a `private` visibility level, unless imported into a parent group.
-- If imported into a parent group, a subgroup inherits the same level of visibility unless otherwise restricted.
-- To preserve the member list and their respective permissions on imported groups, review the users in these groups. Make
-sure these users exist before importing the desired groups.
-- Users must set a public email in the source GitLab instance that matches one of their verified emails in the target GitLab instance.
-
-### Exported contents
-
-The [`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/group/import_export.yml)
-file for groups lists many of the items exported and imported when migrating groups using file exports. View this file in the branch
-for your version of GitLab to see the list of items relevant to you. For example,
-[`import_export.yml` on the `14-10-stable-ee` branch](https://gitlab.com/gitlab-org/gitlab/-/blob/14-10-stable-ee/lib/gitlab/import_export/group/import_export.yml).
-
-Migrating projects with file exports uses the same export and import mechanisms as creating projects from templates at the [group](../custom_project_templates.md) and
-[instance](../../admin_area/custom_project_templates.md) levels. Therefore, the list of exported items is the same.
-
-Items that are exported include:
-
-- Milestones
-- Labels
-- Boards and Board Lists
-- Badges
-- Subgroups (including all the aforementioned data)
-- Epics
- - Epic resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
-- Events
-- [Wikis](../../project/wiki/group.md)
- ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53247) in GitLab 13.9)
-- Iterations cadences ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95372) in 15.4)
-
-Items that are **not** exported include:
-
-- Projects
-- Runner tokens
-- SAML discovery tokens
-
-NOTE:
-For more details on the specific data persisted in a group export, see the
-[`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/group/import_export.yml) file.
-
-## Export a group
-
-Prerequisites:
-
-- You must have the Owner role for the group.
-
-To export the contents of a group:
-
-1. On the top bar, select **Main menu > Groups** and find your group.
-1. On the left sidebar, select **Settings > General**.
-1. In the **Advanced** section, select **Export Group**.
-1. After the export is generated, you should receive an email with a link to the [exported contents](#exported-contents)
- in a compressed tar archive, with contents in NDJSON format.
-1. Alternatively, you can download the export from the UI:
-
- 1. Return to your group's **Settings > General** page.
- 1. In the **Advanced** section, select **Download export**.
- You can also generate a new file by selecting **Regenerate export**.
-
-NOTE:
-The maximum import file size can be set by the Administrator, default is `0` (unlimited).
-As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the [Application settings API](../../../api/settings.md#change-application-settings) or the [Admin Area](../../admin_area/settings/account_and_limit_settings.md). Default [modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50MB to 0 in GitLab 13.8.
-
-You can also use the [group import/export API](../../../api/group_import_export.md).
-
-### Between CE and EE
-
-You can export groups from the [Community Edition to the Enterprise Edition](https://about.gitlab.com/install/ce-or-ee/) and vice versa.
-
-The Enterprise Edition retains some group data that isn't part of the Community Edition. If you're exporting a group from the Enterprise Edition to the Community Edition, you may lose this data. For more information, see [downgrading from EE to CE](../../../index.md).
-
-## Import the group
-
-1. Create a new group:
- - On the top bar, select **New** (**{plus}**) and then **New group**.
- - On an existing group's page, select the **New subgroup** button.
-1. Select **Import group**.
-1. Enter your group name.
-1. Accept or modify the associated group URL.
-1. Select **Choose file**.
-1. Select the file that you exported in the [Export a group](#export-a-group) section.
-1. To begin importing, select **Import group**.
-
-Your newly imported group page appears after the operation completes.
-
-## Automate group and project import **(PREMIUM)**
-
-For information on automating user, group, and project import API calls, see
-[Automate group and project import](../../project/import/index.md#automate-group-and-project-import).
-
-## Version history
-
-### 14.0+
-
-In GitLab 14.0, the JSON format is no longer supported for project and group exports. To allow for a
-transitional period, you can still import any JSON exports. The new format for imports and exports
-is NDJSON.
-
-### 13.0+
-
-GitLab can import bundles that were exported from a different GitLab deployment.
-This ability is limited to two previous GitLab [minor](../../../policy/maintenance.md#versioning)
-releases, which is similar to our process for [Security Releases](../../../policy/maintenance.md#security-releases).
-
-For example:
-
-| Current version | Can import bundles exported from |
-|-----------------|----------------------------------|
-| 13.0 | 13.0, 12.10, 12.9 |
-| 13.1 | 13.1, 13.0, 12.10 |
-
-## Rate Limits
-
-To help avoid abuse, by default, users are rate limited to:
-
-| Request Type | Limit |
-| ---------------- | ---------------------------------------- |
-| Export | 6 groups per minute |
-| Download export | 1 download per group per minute |
-| Import | 6 groups per minute |
-
-GitLab.com may have [different settings](../../gitlab_com/index.md#importexport) from the defaults.
+<!-- This redirect file can be deleted after <2023-03-08>. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/user/group/value_stream_analytics/index.md b/doc/user/group/value_stream_analytics/index.md
index 980618ac3ae..1c02ca59e3d 100644
--- a/doc/user/group/value_stream_analytics/index.md
+++ b/doc/user/group/value_stream_analytics/index.md
@@ -223,7 +223,7 @@ Value stream analytics records the following times for each stage:
- **Review**: 14:00 to 19:00: 5 hrs
- **Staging**: 19:00 to 19:30: 30 minutes
-There are some additional considerations for this example:
+Keep in mind the following observations related to this example:
- This example demonstrates that it doesn't matter if your first
commit doesn't mention the issue number, you can do this later in any commit
diff --git a/doc/user/img/markdown_logo.png b/doc/user/img/markdown_logo.png
index 5184851b6cf..b5b08738106 100644
--- a/doc/user/img/markdown_logo.png
+++ b/doc/user/img/markdown_logo.png
Binary files differ
diff --git a/doc/user/infrastructure/clusters/connect/new_gke_cluster.md b/doc/user/infrastructure/clusters/connect/new_gke_cluster.md
index 6b8e2003b8d..7e6a0495d90 100644
--- a/doc/user/infrastructure/clusters/connect/new_gke_cluster.md
+++ b/doc/user/infrastructure/clusters/connect/new_gke_cluster.md
@@ -94,7 +94,7 @@ Use CI/CD environment variables to configure your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Variables**.
1. Set the variable `BASE64_GOOGLE_CREDENTIALS` to the `base64` encoded JSON file you just created.
-1. Set the variable `TF_VAR_gcp_project` to your GCP's `project` name.
+1. Set the variable `TF_VAR_gcp_project` to your GCP `project` name.
1. Set the variable `TF_VAR_agent_token` to the agent token displayed in the previous task.
1. Set the variable `TF_VAR_kas_address` to the agent server address displayed in the previous task.
diff --git a/doc/user/infrastructure/clusters/manage/management_project_applications/prometheus.md b/doc/user/infrastructure/clusters/manage/management_project_applications/prometheus.md
deleted file mode 100644
index 64d325dedc6..00000000000
--- a/doc/user/infrastructure/clusters/manage/management_project_applications/prometheus.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../../../../../operations/metrics/index.md'
-remove_date: '2022-11-03'
----
-
-This document was moved to [another location](../../../../../operations/metrics/index.md).
-
-<!-- This redirect file can be deleted after <2022-11-03>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/user/infrastructure/clusters/manage/management_project_applications/sentry.md b/doc/user/infrastructure/clusters/manage/management_project_applications/sentry.md
deleted file mode 100644
index f42e9c83120..00000000000
--- a/doc/user/infrastructure/clusters/manage/management_project_applications/sentry.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: '../../../../../operations/error_tracking.md'
-remove_date: '2022-11-03'
----
-
-This document was moved to [another location](../../../../../operations/error_tracking.md).
-
-<!-- This redirect file can be deleted after <2022-11-03>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/user/infrastructure/iac/index.md b/doc/user/infrastructure/iac/index.md
index 866b16652fa..f9891934067 100644
--- a/doc/user/infrastructure/iac/index.md
+++ b/doc/user/infrastructure/iac/index.md
@@ -72,10 +72,12 @@ To use a Terraform template:
include:
# To fetch the latest template, use:
- template: Terraform.latest.gitlab-ci.yml
+ # To fetch the advanced latest template, use:
+ - template: Terraform/Base.latest.gitlab-ci.yml
# To fetch the stable template, use:
+ - template: Terraform.gitlab-ci.yml
+ # To fetch the advanced stable template, use:
- template: Terraform/Base.gitlab-ci.yml
- # To fetch the advanced template, use:
- - template: Terraform/Base.latest.gitlab-ci.yml
```
1. Add the variables as described below:
@@ -91,6 +93,10 @@ To use a Terraform template:
1. Optional. Override in your `.gitlab-ci.yml` file the attributes present
in the template you fetched to customize your configuration.
+### Terraform template recipes
+
+For GitLab-curated template recipes, see [Terraform template recipes](terraform_template_recipes.md).
+
## Related topics
- View [the images that contain the `gitlab-terraform` shell script](https://gitlab.com/gitlab-org/terraform-images).
diff --git a/doc/user/infrastructure/iac/terraform_state.md b/doc/user/infrastructure/iac/terraform_state.md
index a690cc78121..fc86210ed56 100644
--- a/doc/user/infrastructure/iac/terraform_state.md
+++ b/doc/user/infrastructure/iac/terraform_state.md
@@ -6,7 +6,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# GitLab-managed Terraform state **(FREE)**
-> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2673) in GitLab 13.0.
+> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2673) in GitLab 13.0.
+> - Support for state names that contain periods introduced in GitLab 15.7 [with a flag](../../../administration/feature_flags.md) named `allow_dots_on_tf_state_names`. Disabled by default. [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106861) in GitLab 15.7.
+
+FLAG:
+On self-managed GitLab, by default support for state names that contain periods is not available. To make it available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `allow_dots_on_tf_state_names`. On GitLab.com, support for state names that contain periods is available. Requests for state files might generate HTTP 404 errors after enabling this feature. For more information, see [Troubleshooting the Terraform integration with GitLab](troubleshooting.md#state-not-found-if-the-state-name-contains-a-period).
Terraform uses state files to store details about your infrastructure configuration.
With Terraform remote [backends](https://www.terraform.io/language/settings/backends/configuration),
diff --git a/doc/user/infrastructure/iac/terraform_template_recipes.md b/doc/user/infrastructure/iac/terraform_template_recipes.md
new file mode 100644
index 00000000000..ab2c8c1c48a
--- /dev/null
+++ b/doc/user/infrastructure/iac/terraform_template_recipes.md
@@ -0,0 +1,183 @@
+---
+stage: Configure
+group: Configure
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Terraform template recipes **(FREE)**
+
+You can customize your Terraform integration by adding the recipes on
+this page to your pipeline.
+
+If you'd like to share your own Terraform configuration, consider
+[contributing a recipe](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/user/infrastructure/iac/terraform_template_recipes.md)
+to this page.
+
+## Enable a `terraform destroy` job
+
+Add the following snippet to your `.gitlab-ci.yml`:
+
+```yaml
+include:
+ - template: Terraform.latest.gitlab-ci.yml
+
+destroy:
+ extends: .terraform:destroy
+```
+
+The `destroy` job is part of the `cleanup` stage. Like the `deploy`
+job, the `destroy` job is always `manual` and is not tied to the
+default branch.
+
+## Run a custom `terraform` command in a job
+
+To define a job that runs a custom `terraform` command, the
+`gitlab-terraform` wrapper can be used in any job:
+
+```yaml
+include:
+ - template: Terraform.latest.gitlab-ci.yml
+
+state-list:
+ stage: validate # you can use any stage, just make sure to define it
+ script: gitlab-terraform state list
+```
+
+The `gitlab-terraform` command sets up a `terraform` command and runs
+it with the given arguments.
+
+To run this job in the Terraform state-specific [resource group](../../../ci/resource_groups/index.md),
+assign the job with `resource_group`:
+
+```yaml
+include:
+ - template: Terraform.latest.gitlab-ci.yml
+
+state-list:
+ stage: validate # you can use any stage, just make sure to define it
+ resource_group: ${TF_STATE_NAME}
+ script: gitlab-terraform state list
+```
+
+## Add custom debug tools to jobs
+
+The default image used by Terraform template jobs contains only minimal tooling.
+However, you might want to add additional tools for debugging.
+
+To add an additional tool:
+
+1. Install the tool in the `before_script` of a job or pipeline.
+1. Use the tool in the `script` or `after_script` block.
+ - If you use the `script` block, be sure to re-add the template job commands.
+
+For example, the following snippet installs `bash` and `jq` in the `before_script` for all
+jobs in the pipeline:
+
+```yaml
+include:
+ - template: Terraform.latest.gitlab-ci.yml
+
+default:
+ before_script: apk add --update bash jq
+```
+
+To add it to only the `build` and `deploy` jobs, add it to those jobs directly:
+
+```yaml
+include:
+ - template: Terraform.latest.gitlab-ci.yml
+
+build:
+ before_script: apk add --update bash jq
+
+deploy:
+ before_script: apk add --update bash jq
+```
+
+## Add custom container images
+
+For debug tools and simple installations, you should
+[add a custom debug tool to your job](#add-custom-debug-tools-to-jobs).
+If your tool is complex or benefits from caching,
+you can create a custom container image based on the
+[`gitlab-terraform`](https://gitlab.com/gitlab-org/terraform-images) images.
+You can use your custom image in subsequent Terraform jobs.
+
+To define a custom container image:
+
+1. Define a new `Dockerfile` with custom tooling. For example, install `bash` and `jq` in `.gitlab/ci/Dockerfile`:
+
+ ```dockerfile
+ FROM registry.gitlab.com/gitlab-org/terraform-images/stable:latest
+
+ RUN apk add --update bash jq
+ ```
+
+1. In a new job, define a `prepare` stage that builds the image whenever the `Dockerfile` changes.
+ - The built image is pushed to the [GitLab Container Registry](../../packages/container_registry). A tag is applied to indicate whether the image was built from a merge request or from the default branch.
+1. Use your image in your Terraform jobs, such as `build` and `deploy`.
+ - You can combine your image with specialized `before_script` configurations to perform setup commands, like to generate inputs for Terraform.
+
+For example, a fully functioning pipeline configuration might look like:
+
+```yaml
+include:
+ - template: Terraform.latest.gitlab-ci.yml
+
+variables:
+ IMAGE_TAG: latest
+
+workflow:
+ rules:
+ - if: $CI_MERGE_REQUEST_IID
+ changes:
+ - .gitlab/ci/Dockerfile
+ variables:
+ IMAGE_TAG: ${CI_COMMIT_REF_SLUG}
+ - when: always
+
+stages:
+ - prepare
+ - validate
+ - test
+ - build
+ - deploy
+ - cleanup
+
+prepare:image:
+ needs: []
+ stage: prepare
+ image:
+ name: gcr.io/kaniko-project/executor:v1.9.0-debug
+ entrypoint: [""]
+ rules:
+ # Tag with the commit SHA if we're in an MR
+ - if: $CI_MERGE_REQUEST_IID
+ changes:
+ - .gitlab/ci/Dockerfile
+ variables:
+ DOCKER_TAG: $CI_COMMIT_REF_SLUG
+ # If we're on our main branch, tag with "latest"
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+ changes:
+ - .gitlab/ci/Dockerfile
+ variables:
+ DOCKER_TAG: latest
+ before_script:
+ # Authenticate to the docker registry and dependency proxy
+ - echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json
+ script:
+ - /kaniko/executor
+ --context "${CI_PROJECT_DIR}/.gitlab/ci"
+ --cache=true
+ --dockerfile "${CI_PROJECT_DIR}/.gitlab/ci/Dockerfile"
+ --destination "${CI_REGISTRY_IMAGE}:${DOCKER_TAG}"
+
+build:
+ image: ${CI_REGISTRY_IMAGE}:${IMAGE_TAG}
+
+deploy:
+ image: ${CI_REGISTRY_IMAGE}:${IMAGE_TAG}
+```
+
+For an example repository, see the [GitLab Terraform template usage project](https://gitlab.com/gitlab-org/configure/examples/terraform-template-usage).
diff --git a/doc/user/infrastructure/iac/troubleshooting.md b/doc/user/infrastructure/iac/troubleshooting.md
index 3921c6a7dc8..ad1821fbe10 100644
--- a/doc/user/infrastructure/iac/troubleshooting.md
+++ b/doc/user/infrastructure/iac/troubleshooting.md
@@ -131,3 +131,41 @@ To permit a user with the Developer role to run destructive commands, you need a
1. Set the value of `TF_USERNAME` to the username of your project access token.
1. Set the value of `TF_PASSWORD` to the password of your project access token.
1. Optional. Protect the variables to make them only available in pipelines that run on protected branches or protected tags.
+
+### State not found if the state name contains a period
+
+GitLab 15.6 and earlier returned 404 errors if the state name contained a period and Terraform attempted
+a state lock.
+
+You could work around this limitation by adding `-lock=false` to your Terraform commands. The GitLab backend
+accepted the request, but internally stripped the period and any characters that followed from the state name.
+For example, a state named `foo.bar` would be stored as `foo`. However, this workaround wasn't recommended,
+and could even cause state name collisions.
+
+In GitLab 15.7 and later, [state names with periods are supported](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106861). If you use the `-lock=false` workaround and upgrade to GitLab 15.7 or later,
+your jobs might fail. The failure is caused by the GitLab backend storing a new state with the full state name, which diverges from the existing state name.
+
+To fix the failing jobs, rename your state names to exclude the period and any characters that follow it. For example,
+if you use the Terraform template:
+
+```yaml
+include:
+ - template: Terraform.gitlab-ci.yml
+
+variables:
+ TF_STATE_NAME: foo
+```
+
+If your `TF_HTTP_ADDRESS`, `TF_HTTP_LOCK_ADDRESS` and `TF_HTTP_UNLOCK_ADDRESS` are set, be sure
+to update the state names there.
+
+Alternatively, you can [migrate your terraform state](terraform_state.md#migrate-to-a-gitlab-managed-terraform-state).
+
+#### Self-managed GitLab instances
+
+By default, support for state names with periods is not enabled on self-managed GitLab.
+You can enable it from the Rails console:
+
+```ruby
+Feature.enable(:allow_dots_on_tf_state_names)
+```
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index b6f3ba1cfdd..17b91eb9483 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -1,6 +1,6 @@
---
-stage: Create
-group: Source Code
+stage: Plan
+group: Project Management
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
@@ -211,7 +211,7 @@ You can use it to point out a :bug: or warn about :speak_no_evil: patches. And i
If you're new to this, don't be :fearful:. You can join the emoji :family:. Just look up one of the supported codes.
-Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
+Consult the [Emoji Cheat Sheet](https://www.webfx.com/tools/emoji-cheat-sheet/) for a list of all supported emoji codes. :thumbsup:
```
Sometimes you want to <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/public/-/emojis/2/monkey.png" width="20px" height="20px" style="display:inline;margin:0;border: 0"> around a bit and add some <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/public/-/emojis/2/star2.png" width="20px" height="20px" style="display:inline;margin:0;border: 0"> to your <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/public/-/emojis/2/speech_balloon.png" width="20px" height="20px" style="display:inline;margin:0;border: 0">. Well we have a gift for you:
@@ -374,12 +374,12 @@ a^2+b^2=c^2
#### LaTeX-compatible fencing
-> Introduced in GitLab 15.4 [with a flag](../administration/feature_flags.md) named `markdown_dollar_math`. Disabled by default.
+> Introduced in GitLab 15.4 [with a flag](../administration/feature_flags.md) named `markdown_dollar_math`. Disabled by default. Enabled on GitLab.com.
[View this topic in GitLab](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/user/markdown.md#latex-compatible-fencing).
FLAG:
-On self-managed GitLab, by default this feature is not available. To make it available,
+On self-managed GitLab, by default this feature is not available. To make it available per group,
ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `markdown_dollar_math`.
On GitLab.com, this feature is available.
The feature is not ready for production use.
@@ -1017,8 +1017,25 @@ Do not change to a reference style link.
![alt text](img/markdown_logo.png "Title Text")
-In the rare case where you must set a specific height or width for an image,
-you can use the `img` HTML tag instead of Markdown and set its `height` and
+#### Change the image dimensions
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/28118) in GitLab 15.7.
+
+You can control the width and height of an image by following the image with
+an attribute list.
+The value must an integer with a unit of either `px` (default) or `%`.
+
+For example
+
+```markdown
+![alt text](img/markdown_logo.png "Title Text"){width=100 height=100px}
+
+![alt text](img/markdown_logo.png "Title Text"){width=75%}
+```
+
+![alt text](img/markdown_logo.png "Title Text"){width=100 height=100px}
+
+You can also use the `img` HTML tag instead of Markdown and set its `height` and
`width` parameters.
#### Videos
@@ -1283,7 +1300,7 @@ Some text to show that the reference links can follow later.
Using header ID anchors:
-- This line links to [a section on a different Markdown page, using a "#" and the header ID](permissions.md#project-features-permissions)
+- This line links to [a section on a different Markdown page, using a "#" and the header ID](permissions.md#project-members-permissions)
- This line links to [a different section on the same page, using a "#" and the header ID](#header-ids-and-links)
Using references:
@@ -1530,6 +1547,7 @@ Tables are not part of the core Markdown specification, but are part of GitLab F
by pipes (`|`).
- You **can** have blank cells.
1. Column widths are calculated dynamically based on the content of the cells.
+1. To use the pipe character (`|`) in the text and not as table delimiter, you must escape it with a backslash (`\|`).
Example:
@@ -1581,7 +1599,8 @@ use `<br>` tags to force a cell to have multiple lines:
| Item2 | This item has:<br>- Multiple items<br>- That we want listed separately |
You can use HTML formatting in GitLab itself to add [task lists](#task-lists) with checkboxes,
-but they do not render properly on `docs.gitlab.com`:
+but they do not render properly on `docs.gitlab.com`. Note that these tasks will not save their
+state when selected, like regular GitLab task lists.
```markdown
| header 1 | header 2 |
@@ -1590,6 +1609,31 @@ but they do not render properly on `docs.gitlab.com`:
| cell 3 | <ul><li> - [ ] Task one </li><li> - [ ] Task two </li></ul> |
```
+To have fully functioning task lists in a table, create an HTML table with Markdown in the cells:
+
+```html
+<table>
+<thead>
+<tr><th>header 1</th><th>header 2</th></tr>
+</thead>
+<tbody>
+<tr>
+<td>cell 1</td>
+<td>cell 2</td>
+</tr>
+<tr>
+<td>cell 3</td>
+<td>
+
+- [ ] Task one
+- [ ] Task two
+
+</td>
+</tr>
+</tbody>
+</table>
+```
+
##### Copy and paste from a spreadsheet
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27205) in GitLab 12.7.
diff --git a/doc/user/namespace/index.md b/doc/user/namespace/index.md
index 36e048868ad..7d26600cc38 100644
--- a/doc/user/namespace/index.md
+++ b/doc/user/namespace/index.md
@@ -9,20 +9,24 @@ info: To determine the technical writer assigned to the Stage/Group associated w
In GitLab, a *namespace* provides one place to organize your related projects. Projects in one namespace are separate from projects in other namespaces,
which means you can use the same name for projects in different namespaces.
+## Types of namespaces
+
GitLab has two types of namespaces:
-- A *Personal* namespace, which is based on your username and provided to you when you create your account.
- - If you change your username, the project and namespace URLs in your account also change. Before you change your username,
- read about [repository redirects](../project/repository/index.md#what-happens-when-a-repository-path-changes).
+- A *personal* namespace, which is based on your username and provided to you when you create your account.
- You cannot create subgroups in a personal namespace.
- Groups in your namespace do not inherit your namespace permissions and group features.
- - All the *Personal Projects* created will fall under the scope of this namespace.
+ - All the projects you create are under the scope of this namespace.
+ - If you change your username, the project and namespace URLs in your account also change. Before you change your username,
+ read about [repository redirects](../project/repository/index.md#what-happens-when-a-repository-path-changes).
-- A *group* or *subgroup* namespace:
+- A *group* or *subgroup* namespace, which is based on the group or subgroup name:
- You can create multiple subgroups to manage multiple projects.
- - You can change the URL of group and subgroup namespaces.
- You can configure settings specifically for each subgroup and project in the namespace.
- When you create a subgroup, it inherits some of the parent group settings. You can view these in the subgroup **Settings**.
+ - You can change the URL of group and subgroup namespaces.
+
+## Determine which type of namespace you're viewing
To determine whether you're viewing a group or personal namespace, you can view the URL. For example:
diff --git a/doc/user/operations_dashboard/index.md b/doc/user/operations_dashboard/index.md
index 0a65ea0f64d..619d822adf2 100644
--- a/doc/user/operations_dashboard/index.md
+++ b/doc/user/operations_dashboard/index.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Release
+group: Release
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
diff --git a/doc/user/packages/conan_repository/index.md b/doc/user/packages/conan_repository/index.md
index 3d3fe35fd65..dd6605c2f01 100644
--- a/doc/user/packages/conan_repository/index.md
+++ b/doc/user/packages/conan_repository/index.md
@@ -270,8 +270,9 @@ Prerequisites:
```
NOTE:
-If you try to install the package you just created in this tutorial, the package
-already exists on your local computer, so this command has no effect.
+If you try installing the package you created in this tutorial, the install command
+will have no effect because the package already exists.
+Delete `~/.conan/data` to clean up the packages stored in the cache.
## Remove a Conan package
diff --git a/doc/user/packages/container_registry/index.md b/doc/user/packages/container_registry/index.md
index 65e7918d827..4b4d6190dc2 100644
--- a/doc/user/packages/container_registry/index.md
+++ b/doc/user/packages/container_registry/index.md
@@ -6,11 +6,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# GitLab Container Registry **(FREE)**
-> - The group-level Container Registry was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23315) in GitLab 12.10.
-> - Searching by image repository name was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31322) in GitLab 13.0.
+> Searching by image repository name was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31322) in GitLab 13.0.
NOTE:
-If you pull container images from Docker Hub, you can also use the [GitLab Dependency Proxy](../dependency_proxy/index.md#use-the-dependency-proxy-for-docker-images) to avoid running into rate limits and speed up your pipelines.
+If you pull container images from Docker Hub, you can use the [GitLab Dependency Proxy](../dependency_proxy/index.md#use-the-dependency-proxy-for-docker-images)
+to avoid rate limits and speed up your pipelines.
With the Docker Container Registry integrated into GitLab, every GitLab project can
have its own space to store its Docker images.
@@ -28,26 +28,26 @@ You can view the Container Registry for a project or group.
1. Go to your project or group.
1. Go to **Packages and registries > Container Registry**.
-You can search, sort, filter, and [delete](#delete-images-from-within-gitlab)
+You can search, sort, filter, and [delete](#delete-images-using-the-gitlab-ui)
containers on this page. You can share a filtered view by copying the URL from your browser.
Only members of the project or group can access a private project's Container Registry.
+Images downloaded from a private registry may be [available to other users in a shared runner](https://docs.gitlab.com/runner/security/index.html#usage-of-private-docker-images-with-if-not-present-pull-policy).
If a project is public, so is the Container Registry.
### View the tags of a specific image
-You can view a list of tags associated with a given container image:
+You can use the Container Registry **Tag Details** page to view a list of tags associated with a given container image:
1. Go to your project or group.
1. Go to **Packages and registries > Container Registry**.
1. Select the container image you are interested in.
-This brings up the Container Registry **Tag Details** page. You can view details about each tag,
-such as when it was published, how much storage it consumes, and the manifest and configuration
-digests.
+You can view details about each tag, such as when it was published, how much storage it consumes,
+and the manifest and configuration digests.
-You can search, sort (by tag name), filter, and [delete](#delete-images-from-within-gitlab)
+You can search, sort (by tag name), filter, and [delete](#delete-images-using-the-gitlab-ui)
tags on this page. You can share a filtered view by copying the URL from your browser.
## Use images from the Container Registry
@@ -67,7 +67,7 @@ To download and run a container image hosted in the GitLab Container Registry:
docker run [options] registry.example.com/group/project/image [arguments]
```
-[Authentication](#authenticate-with-the-container-registry) is needed to download images from private repository.
+[Authentication](#authenticate-with-the-container-registry) is needed to download images from a private repository.
For more information on running Docker containers, visit the
[Docker documentation](https://docs.docker.com/get-started/).
@@ -85,7 +85,7 @@ then your image must be named `gitlab.example.com/mynamespace/myproject` at a mi
You can append additional names to the end of an image name, up to two levels deep.
-For example, these are all valid image names for images within the project named `myproject`:
+For example, these are all valid image names for images in the project named `myproject`:
```plaintext
registry.example.com/mynamespace/myproject:some-tag
@@ -192,12 +192,12 @@ You can configure your `.gitlab-ci.yml` file to build and push images to the Con
- Before building, use `docker build --pull` to fetch changes to base images. It takes slightly
longer, but it ensures your image is up-to-date.
- Before each `docker run`, do an explicit `docker pull` to fetch
- the image that was just built. This is especially important if you are
+ the image that was just built. This step is especially important if you are
using multiple runners that cache images locally.
If you use the Git SHA in your image tag, each job is unique and you
should never have a stale image. However, it's still possible to have a
- stale image if you re-build a given commit after a dependency has changed.
+ stale image if you rebuild a given commit after a dependency has changed.
- Don't build directly to the `latest` tag because multiple jobs may be
happening simultaneously.
@@ -234,12 +234,12 @@ build:
- docker push $IMAGE_TAG
```
-Here, `$CI_REGISTRY_IMAGE` would be resolved to the address of the registry tied
-to this project. Since `$CI_COMMIT_REF_NAME` resolves to the branch or tag name,
-and your branch name can contain forward slashes (for example, `feature/my-feature`), it is
-safer to use `$CI_COMMIT_REF_SLUG` as the image tag. This is due to that image tags
-cannot contain forward slashes. We also declare our own variable, `$IMAGE_TAG`,
-combining the two to save us some typing in the `script` section.
+In this example, `$CI_REGISTRY_IMAGE` resolves to the address of the registry tied
+to this project. `$CI_COMMIT_REF_NAME` resolves to the branch or tag name, which
+can contain forward slashes. Image tags can't contain forward slashes. Use
+`$CI_COMMIT_REF_SLUG` as the image tag. You can declare the variable, `$IMAGE_TAG`,
+combining `$CI_REGISTRY_IMAGE` and `$CI_REGISTRY_IMAGE` to save some typing in the
+`script` section.
Here's a more elaborate example that splits up the tasks into 4 pipeline stages,
including two tests that run in parallel. The `build` is stored in the container
@@ -385,17 +385,18 @@ unreferenced, administrators must run [garbage collection](../../../administrati
On GitLab.com, the latest version of the Container Registry includes an automatic online garbage
collector. For more information, see [this blog post](https://about.gitlab.com/blog/2021/10/25/gitlab-com-container-registry-update/).
-This is an instance-wide feature, rolling out gradually to a subset of the user base, so some new image repositories created
-from GitLab 14.5 onwards are served by this new version of the Container Registry. In this new
-version of the Container Registry, layers that aren't referenced by any image manifest, and image
-manifests that have no tags and aren't referenced by another manifest (such as multi-architecture
-images), are automatically scheduled for deletion after 24 hours if left unreferenced.
+The automatic online garbage collector is an instance-wide feature, rolling out gradually to a subset
+of the user base. Some new image repositories created from GitLab 14.5 onward are served by this
+new version of the Container Registry. In this new version of the Container Registry, layers that aren't
+referenced by any image manifest, and image manifests that have no tags and aren't referenced by another
+manifest (such as multi-architecture images), are automatically scheduled for deletion after 24 hours if
+left unreferenced.
-### Delete images from within GitLab
+### Delete images using the GitLab UI
-To delete images from within GitLab:
+To delete images using the GitLab UI:
-1. Navigate to your project's or group's **Packages and registries > Container Registry**.
+1. Go to your project's or group's **Packages and registries > Container Registry**.
1. From the **Container Registry** page, you can select what you want to delete,
by either:
@@ -419,7 +420,7 @@ information, see the following endpoints:
### Delete images using GitLab CI/CD
WARNING:
-GitLab CI/CD doesn't provide a built-in way to remove your images, but this example
+GitLab CI/CD doesn't provide a built-in way to remove your images. This example
uses a third-party tool called [reg](https://github.com/genuinetools/reg)
that talks to the GitLab Registry API. You are responsible for your own actions.
For assistance with this tool, see
@@ -455,21 +456,18 @@ build_image:
- main
delete_image:
- image: docker:20.10.16
+ before_script:
+ - curl --fail --show-error --location "https://github.com/genuinetools/reg/releases/download/v$REG_VERSION/reg-linux-amd64" --output ./reg
+ - echo "$REG_SHA256 ./reg" | sha256sum -c -
+ - chmod a+x ./reg
+ image: curlimages/curl:7.86.0
+ script:
+ - ./reg rm -d --auth-url $CI_REGISTRY -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $IMAGE_TAG
stage: clean
- services:
- - docker:20.10.16-dind
variables:
IMAGE_TAG: $CI_PROJECT_PATH:$CI_COMMIT_REF_SLUG
REG_SHA256: ade837fc5224acd8c34732bf54a94f579b47851cc6a7fd5899a98386b782e228
REG_VERSION: 0.16.1
- before_script:
- - apk add --no-cache curl
- - curl --fail --show-error --location "https://github.com/genuinetools/reg/releases/download/v$REG_VERSION/reg-linux-amd64" --output /usr/local/bin/reg
- - echo "$REG_SHA256 /usr/local/bin/reg" | sha256sum -c -
- - chmod a+x /usr/local/bin/reg
- script:
- - /usr/local/bin/reg rm -d --auth-url $CI_REGISTRY -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $IMAGE_TAG
only:
- branches
except:
@@ -487,16 +485,14 @@ defined in the `delete_image` job.
You can create a per-project [cleanup policy](reduce_container_registry_storage.md#cleanup-policy) to ensure older tags and images are regularly removed from the
Container Registry.
-## Limitations
+## Known issues
-- Moving or renaming existing Container Registry repositories is not supported
- once you have pushed images, because the images are stored in a path that matches
- the repository path. To move or rename a repository with a
- Container Registry, you must delete all existing images.
- Community suggestions to work around this limitation have been shared in
- [issue 18383](https://gitlab.com/gitlab-org/gitlab/-/issues/18383#possible-workaround).
-- Prior to GitLab 12.10, any tags that use the same image ID as the `latest` tag
- are not deleted by the cleanup policy.
+Moving or renaming existing Container Registry repositories is not supported
+after you have pushed images. The images are stored in a path that matches
+the repository path. To move or rename a repository with a
+Container Registry, you must delete all existing images.
+Community suggestions to work around this known issue have been shared in
+[issue 18383](https://gitlab.com/gitlab-org/gitlab/-/issues/18383#possible-workaround).
## Disable the Container Registry for a project
@@ -530,7 +526,7 @@ for more details about the permissions that this setting grants to users.
is internal or private, the Container Registry is also internal or private.
- **Only Project Members**: The Container Registry is visible only to project members with
- Reporter role or higher. This is similar to the behavior of a private project with Container
+ Reporter role or higher. This visibility is similar to the behavior of a private project with Container
Registry visibility set to **Everyone With Access**.
1. Select **Save changes**.
@@ -541,7 +537,7 @@ The ability to view the Container Registry and pull images is controlled by the
visibility permissions. You can change this through the [visibility setting on the UI](#change-visibility-of-the-container-registry)
or the [API](../../../api/container_registry.md#change-the-visibility-of-the-container-registry).
[Other permissions](../../permissions.md)
-such as updating the Container Registry, pushing or deleting images, and so on are not affected by
+such as updating the Container Registry and pushing or deleting images are not affected by
this setting. However, disabling the Container Registry disables all Container Registry operations.
| | | Anonymous<br/>(Everyone on internet) | Guest | Reporter, Developer, Maintainer, Owner |
@@ -553,188 +549,3 @@ this setting. However, disabling the Container Registry disables all Container R
| Private project with Container Registry visibility <br/> set to **Everyone With Access** (UI) or `enabled` (API) | View Container Registry <br/> and pull images | No | No | Yes |
| Private project with Container Registry visibility <br/> set to **Only Project Members** (UI) or `private` (API) | View Container Registry <br/> and pull images | No | No | Yes |
| Any project with Container Registry `disabled` | All operations on Container Registry | No | No | No |
-
-## Troubleshooting the GitLab Container Registry
-
-### Migrating OCI container images to GitLab Container Registry
-
-Migrating built container images to the GitLab registry is not a current feature. However, an [epic](https://gitlab.com/groups/gitlab-org/-/epics/5210) is open to track the work on this feature.
-
-Some third-party tools can help migrate container images, for example, [skopeo](https://github.com/containers/skopeo), which can [copy container images](https://github.com/containers/skopeo#copying-images) between various storage mechanisms. You can use skopeo to copy from container registries, container storage backends, local directories, and local OCI-layout directories to the GitLab Container Registry.
-
-### Docker connection error
-
-A Docker connection error can occur when there are special characters in either the group,
-project or branch name. Special characters can include:
-
-- Leading underscore
-- Trailing hyphen/dash
-
-To get around this, you can [change the group path](../../group/manage.md#change-a-groups-path),
-[change the project path](../../project/settings/index.md#rename-a-repository) or change the branch
-name.
-
-You may also get a `404 Not Found` or `Unknown Manifest` message if you are using
-a Docker Engine version earlier than 17.12. Later versions of Docker Engine use
-[the v2 API](https://docs.docker.com/registry/spec/manifest-v2-2/).
-
-The images in your GitLab Container Registry must also use the Docker v2 API.
-For information on how to update your images, see the [Docker help](https://docs.docker.com/registry/spec/deprecated-schema-v1).
-
-### `Blob unknown to registry` error when pushing a manifest list
-
-When [pushing a Docker manifest list](https://docs.docker.com/engine/reference/commandline/manifest/#create-and-push-a-manifest-list)
-to the GitLab Container Registry, you may receive the error
-`manifest blob unknown: blob unknown to registry`. This is likely caused by having multiple images
-with different architectures, spread out over several repositories instead of the same repository.
-
-For example, you may have two images, each representing an architecture:
-
-- The `amd64` platform
-- The `arm64v8` platform
-
-To build a multi-arch image with these images, you must push them to the same repository as the
-multi-arch image.
-
-To address the `Blob unknown to registry` error, include the architecture in the tag name of
-individual images. For example, use `mygroup/myapp:1.0.0-amd64` and `mygroup/myapp:1.0.0-arm64v8`.
-You can then tag the manifest list with `mygroup/myapp:1.0.0`.
-
-### Troubleshoot as a GitLab server administrator
-
-Troubleshooting the GitLab Container Registry, most of the times, requires
-you to sign in to GitLab server with administrator access.
-
-[Read how to troubleshoot the Container Registry](../../../administration/packages/container_registry.md#troubleshooting).
-
-### Unable to change path or transfer a project
-
-If you try to change a project's path or transfer a project to a new namespace,
-you may receive one of the following errors:
-
-- "Project cannot be transferred, because tags are present in its container registry."
-- "Namespace cannot be moved because at least one project has tags in container registry."
-
-This issue occurs when the project has images in the Container Registry.
-You must delete or move these images before you can change the path or transfer
-the project.
-
-The following procedure uses these sample project names:
-
-- For the current project: `gitlab.example.com/org/build/sample_project/cr:v2.9.1`
-- For the new project: `gitlab.example.com/new_org/build/new_sample_project/cr:v2.9.1`
-
-Use your own URLs to complete the following steps:
-
-1. Download the Docker images on your computer:
-
- ```shell
- docker login gitlab.example.com
- docker pull gitlab.example.com/org/build/sample_project/cr:v2.9.1
- ```
-
- NOTE:
- For container registry authentication, use either a
- [personal access token](../../profile/personal_access_tokens.md) or a
- [deploy token](../../project/deploy_tokens/index.md).
-
-1. Rename the images to match the new project name:
-
- ```shell
- docker tag gitlab.example.com/org/build/sample_project/cr:v2.9.1 gitlab.example.com/new_org/build/new_sample_project/cr:v2.9.1
- ```
-
-1. Delete the images in the old project by using the [UI](#delete-images) or [API](../../../api/packages.md#delete-a-project-package).
- There may be a delay while the images are queued and deleted.
-1. Change the path or transfer the project by going to **Settings > General**
- and expanding **Advanced**.
-1. Restore the images:
-
- ```shell
- docker push gitlab.example.com/new_org/build/new_sample_project/cr:v2.9.1
- ```
-
-Follow [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/18383) for details.
-
-### Tags on S3 backend remain after successful deletion requests
-
-With S3 as your storage backend, tags may remain even though:
-
-- In the UI, you see that the tags are scheduled for deletion.
-- In the API, you get an HTTP `200` response.
-- The registry log shows a successful `Delete` request.
-
-An example `DELETE` request in the registry log:
-
-```shell
-{"content_type":"","correlation_id":"01FQGNSKVMHQEAVE21KYTJN2P4","duration_ms":62,"host":"localhost:5000","level":"info","method":"DELETE","msg":"access","proto":"HTTP/1.1","referrer":"","remote_addr":"127.0.0.1:47498","remote_ip":"127.0.0.1","status":202,"system":"http","time":"2021-12-22T08:58:15Z","ttfb_ms":62,"uri":"/v2/<path to repo>/tags/reference/<tag_name>","user_agent":"GitLab/<version>","written_bytes":0}
-```
-
-There may be some errors not properly cached. Follow these steps to investigate further:
-
-1. In your configuration file, set the registry's log level to `debug`, and the S3 driver's log
- level to `logdebugwithhttpbody`. For Omnibus, make these edits in the `gitlab.rb` file:
-
- ```shell
- # Change the registry['log_level'] to debug
- registry['log_level'] = 'debug'
-
- # Set log level for registry log from storage side
- registry['storage'] = {
- 's3' => {
- 'bucket' => 'your-s3-bucket',
- 'region' => 'your-s3-region'
- },
-
- 'loglevel' = "logdebugwithhttpbody"
- }
- ```
-
- Then save and reconfigure GitLab:
-
- ```shell
- sudo gitlab-ctl reconfigure
- ```
-
-1. Attempt to delete one or more tags using the GitLab UI or API.
-
-1. Inspect the registry logs and look for a response from S3. Although the response could be
- `200 OK`, the body might have the error `AccessDenied`. This indicates a permission problem from
- the S3 side.
-
-1. Ensure your S3 configuration has the `deleteObject` permission scope. Here's an
- [example role for an S3 bucket](../../../administration/object_storage.md#iam-permissions).
- Once adjusted, trigger another tag deletion. You should be able to successfully delete tags.
-
-Follow [this issue](https://gitlab.com/gitlab-org/container-registry/-/issues/551) for details.
-
-### Tags temporarily cannot be marked for deletion
-
-GitLab is [migrating to the next generation of the Container Registry](https://gitlab.com/groups/gitlab-org/-/epics/5523).
-During the migration, you may encounter difficulty deleting tags.
-If you encounter an error, it's likely that your image repository is in the process of being migrated.
-Wait a few minutes and try again.
-
-### `unauthorized: authentication required` when pushing large images
-
-When pushing large images, you might get an error like the following:
-
-```shell
-docker push gitlab.example.com/myproject/docs:latest
-The push refers to a repository [gitlab.example.com/myproject/docs]
-630816f32edb: Preparing
-530d5553aec8: Preparing
-...
-4b0bab9ff599: Waiting
-d1c800db26c7: Waiting
-42755cf4ee95: Waiting
-unauthorized: authentication required
-```
-
-On self-managed GitLab instances, by default, tokens for the Container Registry expire every five minutes.
-When pushing larger images, or images that take longer than five minutes to push,
-you might encounter this error. On GitLab.com, the expiration time is 15 minutes.
-
-If you are using self-managed GitLab, you can ask an administrator to
-[increase the token duration](../../../administration/packages/container_registry.md#increase-token-duration)
-if necessary.
diff --git a/doc/user/packages/container_registry/reduce_container_registry_data_transfer.md b/doc/user/packages/container_registry/reduce_container_registry_data_transfer.md
index 74cbcba2ffc..0ce9635e05a 100644
--- a/doc/user/packages/container_registry/reduce_container_registry_data_transfer.md
+++ b/doc/user/packages/container_registry/reduce_container_registry_data_transfer.md
@@ -44,7 +44,7 @@ Consider using a smaller base image, such as [Alpine Linux](https://alpinelinux.
An Alpine image is around 5MB, which is several times smaller than popular base images such as
[Debian](https://hub.docker.com/_/debian).
If your application is distributed as a self-contained static binary, such as for Go applications,
-you can also consider using Docker's [scratch](https://hub.docker.com/_/scratch/)
+you can also consider using the Docker [scratch](https://hub.docker.com/_/scratch/)
base image.
If you need to use a specific base image OS, look for `-slim` or `-minimal` variants, as this helps
diff --git a/doc/user/packages/container_registry/reduce_container_registry_storage.md b/doc/user/packages/container_registry/reduce_container_registry_storage.md
index 23d835ddf5f..cbf9af633ac 100644
--- a/doc/user/packages/container_registry/reduce_container_registry_storage.md
+++ b/doc/user/packages/container_registry/reduce_container_registry_storage.md
@@ -4,17 +4,18 @@ group: Container Registry
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Reduce Container Registry Storage **(FREE)**
+# Reduce Container Registry storage **(FREE)**
-Container registries become large over time without cleanup. When a large number of images or tags are added:
+Container registries can grow in size over time if you don't manage your registry usage. For example,
+if you add a large number of images or tags:
-- Fetching the list of available tags or images becomes slower.
+- Retrieving the list of available tags or images becomes slower.
- They take up a large amount of storage space on the server.
-We recommend deleting unnecessary images and tags and setting up a [cleanup policy](#cleanup-policy)
+You should delete unnecessary images and tags and set up a [cleanup policy](#cleanup-policy)
to automatically manage your container registry usage.
-## Check Container Registry Storage Use
+## Check Container Registry storage use
The Usage Quotas page (**Settings > Usage Quotas > Storage**) displays storage usage for Packages.
This page includes the Container Registry usage, which is only available on GitLab.com.
@@ -23,10 +24,16 @@ metadata database. Support for improvements is proposed in epic [5523](https://g
You cannot use the Container Registry in self-managed instances, but epic [5521](https://gitlab.com/groups/gitlab-org/-/epics/5521) proposes to change this behavior.
Image layers stored in the Container Registry are deduplicated at the root namespace level.
-If you tag the same image more than once in the same repository or across distinct
-repositories under the same root namespace, it is only counted once.
-If an image layer is shared across multiple images, in the same
-container repository, project, group, or across different repositories, it is only counted once.
+
+An image is only counted once if:
+
+- You tag the same image more than once in the same repository.
+- You tag the same image across distinct repositories under the same root namespace.
+
+An image layer is only counted once if:
+
+- You share the image layer across multiple images in the same container repository, project, or group.
+- You share the image layer across different repositories.
Only layers that are referenced by tagged images are accounted for. Untagged images and any layers
referenced exclusively by them are subject to [online garbage collection](index.md#delete-images).
@@ -50,7 +57,7 @@ To delete the underlying layers and images that aren't associated with any tags,
### Enable the cleanup policy
-Cleanup policies can be run on all projects, with these exceptions:
+You can run cleanup policies on all projects with these exceptions:
- For self-managed GitLab instances, the project must have been created
in GitLab 12.8 or later. However, an administrator can enable the cleanup policy
@@ -63,7 +70,7 @@ Cleanup policies can be run on all projects, with these exceptions:
ApplicationSetting.last.update(container_expiration_policies_enable_historic_entries: true)
```
- Enabling cleanup policies on all project can impact performance, especially if you
+ Enabling cleanup policies on all projects can impact performance, especially if you
are using an [external registry](#use-with-external-container-registries).
WARNING:
@@ -72,34 +79,34 @@ GitLab.com that don't have a container image.
### How the cleanup policy works
-The cleanup policy collects all tags in the Container Registry and excludes tags
-until only the tags to be deleted remain.
+The cleanup policy collects all tags in the Container Registry and excludes tags until the only
+tags you want to delete remain.
The cleanup policy searches for images based on the tag name. Support for full path matching is tracked in issue [281071](https://gitlab.com/gitlab-org/gitlab/-/issues/281071).
The cleanup policy:
1. Collects all tags for a given repository in a list.
-1. Excludes the tag named `latest` from the list.
-1. Evaluates the `name_regex` (tags to expire), excluding non-matching names from the list.
-1. Excludes from the list any tags matching the `name_regex_keep` value (tags to preserve).
+1. Excludes the tag named `latest`.
+1. Evaluates the `name_regex` (tags to expire), excluding non-matching names.
+1. Excludes any tags matching the `name_regex_keep` value (tags to preserve).
1. Excludes any tags that do not have a manifest (not part of the options in the UI).
1. Orders the remaining tags by `created_date`.
-1. Excludes from the list the N tags based on the `keep_n` value (Number of tags to retain).
-1. Excludes from the list the tags more recent than the `older_than` value (Expiration interval).
-1. Finally, the remaining tags in the list are deleted from the Container Registry.
+1. Excludes the N tags based on the `keep_n` value (Number of tags to retain).
+1. Excludes the tags more recent than the `older_than` value (Expiration interval).
+1. Deletes the remaining tags in the list from the Container Registry.
WARNING:
On GitLab.com, the execution time for the cleanup policy is limited. Some tags may remain in
the Container Registry after the policy runs. The next time the policy runs, the remaining tags are included.
-It may take multiple runs for all tags to be deleted.
+It may take multiple runs to delete all tags.
WARNING:
GitLab self-managed installations support third-party container registries that comply with the
[Docker Registry HTTP API V2](https://docs.docker.com/registry/spec/api/)
-specification. However, this specification does not include a tag delete operation. Therefore, when
-interacting with third-party container registries, GitLab uses a workaround to delete tags. See the
-[related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/15737)
+specification. However, this specification does not include a tag delete operation. Therefore, GitLab uses a
+workaround to delete tags when interacting with third-party container registries. Refer to
+issue [15737](https://gitlab.com/gitlab-org/gitlab/-/issues/15737)
for more information. Due to possible implementation variations, this workaround is not guaranteed
to work with all third-party registries in the same predictable way. If you use the GitLab Container
Registry, this workaround is not required because we implemented a special tag delete operation. In
@@ -115,18 +122,18 @@ To create a cleanup policy in the UI:
1. Expand the **Clean up image tags** section.
1. Complete the fields.
- | Field | Description |
- |---------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|
- | **Toggle** | Turn the policy on or off. |
- | **Run cleanup** | How often the policy should run. |
- | **Keep the most recent** | How many tags to _always_ keep for each image. |
- | **Keep tags matching** | The regex pattern that determines which tags to preserve. The `latest` tag is always preserved. For all tags, use `.*`. See other [regex pattern examples](#regex-pattern-examples). |
- | **Remove tags older than** | Remove only tags older than X days. |
- | **Remove tags matching** | The regex pattern that determines which tags to remove. This value cannot be blank. For all tags, use `.*`. See other [regex pattern examples](#regex-pattern-examples). |
+ | Field | Description |
+ |----------------------------|-------------------------------------------------|
+ | **Toggle** | Turn the policy on or off. |
+ | **Run cleanup** | How often the policy should run. |
+ | **Keep the most recent** | How many tags to _always_ keep for each image. |
+ | **Keep tags matching** | A regex pattern that determines which tags to preserve. The `latest` tag is always preserved. For all tags, use `.*`. See other [regex pattern examples](#regex-pattern-examples). |
+ | **Remove tags older than** | Remove only tags older than X days. |
+ | **Remove tags matching** | A regex pattern that determines which tags to remove. This value cannot be blank. For all tags, use `.*`. See other [regex pattern examples](#regex-pattern-examples). |
1. Select **Save**.
-Depending on the interval you chose, the policy is scheduled to run.
+The policy runs on the scheduled interval you selected.
NOTE:
If you edit the policy and select **Save** again, the interval is reset.
@@ -135,7 +142,8 @@ If you edit the policy and select **Save** again, the interval is reset.
Cleanup policies use regex patterns to determine which tags should be preserved or removed, both in the UI and the API.
-Regex patterns are automatically surrounded with `\A` and `\Z` anchors. Do not include any `\A`, `\Z`, `^` or `$` token in the regex patterns as they are not necessary.
+Regex patterns are automatically surrounded with `\A` and `\Z` anchors. Therefore, you do not need to include any
+`\A`, `\Z`, `^` or `$` tokens in the regex patterns.
Here are some examples of regex patterns you can use:
@@ -180,17 +188,17 @@ Here are some examples of regex patterns you can use:
Cleanup policies are executed as a background process. This process is complex, and depending on the number of tags to delete,
the process can take time to finish.
-To prevent server resource starvation, the following application settings are available:
+You can use the following application settings to prevent server resource starvation:
- `container_registry_expiration_policies_worker_capacity`: the maximum number of cleanup workers
- running concurrently. This must be greater than or equal to `0`. We recommend starting with a low
- number and increasing it after monitoring the resources used by the background workers. To remove
+ running concurrently. This value must be greater than or equal to `0`. You should start with a low
+ number and increase it after monitoring the resources used by the background workers. To remove
all workers and not execute the cleanup policies, set this to `0`. The default value is `4`.
- `container_registry_delete_tags_service_timeout`: the maximum time (in seconds) that the cleanup
process can take to delete a batch of tags. The default value is `250`.
- `container_registry_cleanup_tags_service_max_list_size`: the maximum number of tags that can be
- deleted in a single execution. Additional tags must be deleted in another execution. We recommend
- starting with a low number and increasing it after monitoring that container images are properly
+ deleted in a single execution. Additional tags must be deleted in another execution. You should
+ start with a low number and increase it after verifying that container images are properly
deleted. The default value is `200`.
- `container_registry_expiration_policies_caching`: enable or disable tag creation timestamp caching
during execution of policies. Cached timestamps are stored in [Redis](../../../development/architecture.md#redis).
@@ -213,7 +221,8 @@ You can set, update, and disable the cleanup policies using the GitLab API.
Examples:
-- Select all tags, keep at least 1 tag per image, clean up any tag older than 14 days, run once a month, preserve any images with the name `main` and the policy is enabled:
+- Select all tags, keep at least 1 tag per image, clean up any tag older than 14 days, run once a month, preserve
+any images with the name `main`, and the policy is enabled:
```shell
curl --request PUT --header 'Content-Type: application/json;charset=UTF-8' --header "PRIVATE-TOKEN: <your_access_token>" \
@@ -251,14 +260,14 @@ See the API documentation for further details: [Edit project API](../../../api/p
When using an [external container registry](../../../administration/packages/container_registry.md#use-an-external-container-registry-with-gitlab-as-an-auth-endpoint),
running a cleanup policy on a project may have some performance risks.
-If a project runs a policy to remove thousands of tags
+If a project runs a policy to remove thousands of tags,
the GitLab background jobs may get backed up or fail completely.
-For projects created before GitLab 12.8, we recommend you enable container cleanup policies
+For projects created before GitLab 12.8, you should enable container cleanup policies
only if the number of tags being cleaned up is minimal.
## More Container Registry storage reduction options
-Here are some other options to reduce your project's use of Container Registry storage:
+Here are some other options you can use to reduce the Container Registry storage used by your project:
- Use the [GitLab UI](index.md#delete-images)
to delete individual image tags or the entire repository containing all the tags.
@@ -330,6 +339,10 @@ the tags. To create the list and delete the tags:
1. Remove any tags that you want to keep from the `list_o_tags.out` file. For example, you can use `sed` to
parse the file and remove the tags.
+ ::Tabs
+
+ :::TabTitle Linux
+
```shell
# Remove the `latest` tag from the file
sed -i '/latest/d' list_o_tags.out
@@ -344,12 +357,24 @@ the tags. To create the list and delete the tags:
sed -i '/_v3$/d' list_o_tags.out
```
- If you are running macOS, you must add `.bak` to the commands. For example:
+ :::TabTitle macOS
```shell
+ # Remove the `latest` tag from the file
sed -i .bak '/latest/d' list_o_tags.out
+
+ # Remove the first N tags from the file
+ sed -i .bak '1,Nd' list_o_tags.out
+
+ # Remove the tags starting with `Av` from the file
+ sed -i .bak '/^Av/d' list_o_tags.out
+
+ # Remove the tags ending with `_v3` from the file
+ sed -i .bak '/_v3$/d' list_o_tags.out
```
+ ::EndTabs
+
1. Double-check the `list_o_tags.out` file to make sure it contains only the tags that you want to
delete.
diff --git a/doc/user/packages/container_registry/troubleshoot_container_registry.md b/doc/user/packages/container_registry/troubleshoot_container_registry.md
new file mode 100644
index 00000000000..eac7e0fcacd
--- /dev/null
+++ b/doc/user/packages/container_registry/troubleshoot_container_registry.md
@@ -0,0 +1,129 @@
+---
+stage: Package
+group: Container Registry
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Troubleshooting the GitLab Container Registry
+
+You must sign in to GitLab with administrator rights to troubleshoot most issues with the GitLab Container Registry.
+
+You can find [additional troubleshooting information](../../../administration/packages/container_registry.md#troubleshooting) in the GitLab Container Registry administration documentation.
+
+## Migrating OCI container images to GitLab Container Registry
+
+Migrating container images to the GitLab registry is not supported, but [epic](https://gitlab.com/groups/gitlab-org/-/epics/5210) proposes to change this behavior.
+
+You can use third-party tools to migrate container images. For example, [skopeo](https://github.com/containers/skopeo), can [copy container images](https://github.com/containers/skopeo#copying-images) between various storage mechanisms. You can use skopeo to copy from container registries, container storage backends, local directories, and local OCI-layout directories to the GitLab Container Registry.
+
+## Docker connection error
+
+A Docker connection error can occur when there are special characters in either the group,
+project, or branch name. Special characters include:
+
+- A leading underscore.
+- A trailing hyphen or dash.
+
+To resolve this error, you can change the [group path](../../group/manage.md#change-a-groups-path),
+the [project path](../../project/settings/index.md#rename-a-repository) or the branch name.
+
+You may get a `404 Not Found` or `Unknown Manifest` error message if you use
+Docker Engine 17.11 or earlier. Current versions of Docker Engine use
+the [v2 API](https://docs.docker.com/registry/spec/manifest-v2-2/).
+
+The images in your GitLab Container Registry must use the Docker v2 API.
+For information on how to update version 1 images to version 2, see the [Docker documentation](https://docs.docker.com/registry/spec/deprecated-schema-v1).
+
+## `Blob unknown to registry` error when pushing a manifest list
+
+When [pushing a Docker manifest list](https://docs.docker.com/engine/reference/commandline/manifest/#create-and-push-a-manifest-list)
+to the GitLab Container Registry, you may receive the error
+`manifest blob unknown: blob unknown to registry`. This error is likely caused by having multiple images
+with different architectures spread out over several repositories instead of the same repository.
+
+For example, you may have two images, each representing an architecture:
+
+- The `amd64` platform.
+- The `arm64v8` platform.
+
+To build a multi-arch image with these images, you must push them to the same repository as the
+multi-arch image.
+
+To address the `Blob unknown to registry` error, include the architecture in the tag name of
+individual images. For example, use `mygroup/myapp:1.0.0-amd64` and `mygroup/myapp:1.0.0-arm64v8`.
+You can then tag the manifest list with `mygroup/myapp:1.0.0`.
+
+## Unable to change project path or transfer a project
+
+If you try to change a project path or transfer a project to a new namespace,
+you may receive one of the following errors:
+
+- Project cannot be transferred because tags are present in its container registry.
+- Namespace cannot be moved because at least one project has tags in the container registry.
+
+This error occurs when the project has images in the Container Registry.
+You must delete or move these images before you change the path or transfer
+the project.
+
+The following procedure uses these sample project names:
+
+- For the current project: `gitlab.example.com/org/build/sample_project/cr:v2.9.1`.
+- For the new project: `gitlab.example.com/new_org/build/new_sample_project/cr:v2.9.1`.
+
+1. Download the Docker images on your computer:
+
+ ```shell
+ docker login gitlab.example.com
+ docker pull gitlab.example.com/org/build/sample_project/cr:v2.9.1
+ ```
+
+ NOTE:
+ Use either a [personal access token](../../profile/personal_access_tokens.md) or a
+ [deploy token](../../project/deploy_tokens/index.md) to authenticate your user account.
+
+1. Rename the images to match the new project name:
+
+ ```shell
+ docker tag gitlab.example.com/org/build/sample_project/cr:v2.9.1 gitlab.example.com/new_org/build/new_sample_project/cr:v2.9.1
+ ```
+
+1. Delete the images in the old project by using the [UI](index.md#delete-images) or [API](../../../api/packages.md#delete-a-project-package).
+ There may be a delay while the images are queued and deleted.
+1. Change the path or transfer the project:
+
+ 1. On the top bar, select **Main menu > Projects** and find your project.
+ 1. On the left sidebar, select **Settings > General**.
+ 1. Expand the **Advanced** section.
+ 1. In the **Change path** text box, edit the path.
+ 1. Select **Change path**.
+
+1. Restore the images:
+
+ ```shell
+ docker push gitlab.example.com/new_org/build/new_sample_project/cr:v2.9.1
+ ```
+
+See this [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/18383) for details.
+
+## `unauthorized: authentication required` when pushing large images
+
+When pushing large images, you may see an authentication error like the following:
+
+```shell
+docker push gitlab.example.com/myproject/docs:latest
+The push refers to a repository [gitlab.example.com/myproject/docs]
+630816f32edb: Preparing
+530d5553aec8: Preparing
+...
+4b0bab9ff599: Waiting
+d1c800db26c7: Waiting
+42755cf4ee95: Waiting
+unauthorized: authentication required
+```
+
+This error happens when your authentication token expires before the image push is complete. By default, tokens for
+the Container Registry on self-managed GitLab instances expire every five minutes. On GitLab.com, the token expiration
+time is set to 15 minutes.
+
+If you are using self-managed GitLab, you can ask an administrator to
+[increase the token duration](../../../administration/packages/container_registry.md#increase-token-duration).
diff --git a/doc/user/packages/generic_packages/index.md b/doc/user/packages/generic_packages/index.md
index 563f35f2f4f..9b49f946984 100644
--- a/doc/user/packages/generic_packages/index.md
+++ b/doc/user/packages/generic_packages/index.md
@@ -118,7 +118,7 @@ API or the UI.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/293755) in GitLab 13.12.
> - [Required permissions](https://gitlab.com/gitlab-org/gitlab/-/issues/350682) changed from developer to maintainer in GitLab 15.0.
-To prevent users from publishing duplicate generic packages, you can use the [GraphQl API](../../../api/graphql/reference/index.md#packagesettings)
+To prevent users from publishing duplicate generic packages, you can use the [GraphQL API](../../../api/graphql/reference/index.md#packagesettings)
or the UI.
In the UI:
diff --git a/doc/user/packages/gradle_repository/index.md b/doc/user/packages/gradle_repository/index.md
new file mode 100644
index 00000000000..4247c13297d
--- /dev/null
+++ b/doc/user/packages/gradle_repository/index.md
@@ -0,0 +1,372 @@
+---
+stage: Package
+group: Package Registry
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Maven packages in the Package Registry **(FREE)**
+
+Publish [Maven](https://maven.apache.org) artifacts in your project's Package Registry using Gradle.
+Then, install the packages whenever you need to use them as a dependency.
+
+For documentation of the specific API endpoints that the Maven package manager
+client uses, see the [Maven API documentation](../../../api/packages/maven.md).
+
+Learn how to build a [Gradle](../workflows/build_packages.md#gradle) package.
+
+## Publish to the GitLab Package Registry
+
+### Tokens
+
+You need a token to publish a package. Different tokens are available depending on what you're trying to
+achieve. For more information, review the [guidance on tokens](../package_registry/index.md#authenticate-with-the-registry).
+
+- If your organization uses two-factor authentication (2FA), you must use a personal access token with the scope set to `api`.
+- If you publish a package via CI/CD pipelines, you must use a CI job token.
+
+Create a token and save it to use later in the process.
+
+## Authenticate to the Package Registry with Gradle
+
+### Authenticate with a personal access token or deploy token in Gradle
+
+In [your `GRADLE_USER_HOME` directory](https://docs.gradle.org/current/userguide/directory_layout.html#dir:gradle_user_home),
+create a file `gradle.properties` with the following content:
+
+```properties
+gitLabPrivateToken=REPLACE_WITH_YOUR_TOKEN
+```
+
+Your token name depends on which token you use.
+
+| Token type | Token name |
+| --------------------- | --------------- |
+| Personal access token | `Private-Token` |
+| Deploy token | `Deploy-Token` |
+
+Add a `repositories` section to your
+[`build.gradle`](https://docs.gradle.org/current/userguide/tutorial_using_tasks.html)
+file:
+
+```groovy
+repositories {
+ maven {
+ url "https://gitlab.example.com/api/v4/groups/<group>/-/packages/maven"
+ name "GitLab"
+ credentials(HttpHeaderCredentials) {
+ name = 'REPLACE_WITH_TOKEN_NAME'
+ value = gitLabPrivateToken
+ }
+ authentication {
+ header(HttpHeaderAuthentication)
+ }
+ }
+}
+```
+
+Or add it to your `build.gradle.kts` file if you are using Kotlin DSL:
+
+```kotlin
+repositories {
+ maven {
+ url = uri("https://gitlab.example.com/api/v4/groups/<group>/-/packages/maven")
+ name = "GitLab"
+ credentials(HttpHeaderCredentials::class) {
+ name = "REPLACE_WITH_TOKEN_NAME"
+ value = findProperty("gitLabPrivateToken") as String?
+ }
+ authentication {
+ create("header", HttpHeaderAuthentication::class)
+ }
+ }
+}
+```
+
+### Authenticate with a CI job token in Gradle
+
+To authenticate with a CI job token, add a `repositories` section to your
+[`build.gradle`](https://docs.gradle.org/current/userguide/tutorial_using_tasks.html)
+file:
+
+```groovy
+repositories {
+ maven {
+ url "${CI_API_V4_URL}/groups/<group>/-/packages/maven"
+ name "GitLab"
+ credentials(HttpHeaderCredentials) {
+ name = 'Job-Token'
+ value = System.getenv("CI_JOB_TOKEN")
+ }
+ authentication {
+ header(HttpHeaderAuthentication)
+ }
+ }
+}
+```
+
+Or add it to your `build.gradle.kts` file if you are using Kotlin DSL:
+
+```kotlin
+repositories {
+ maven {
+ url = uri("$CI_API_V4_URL/groups/<group>/-/packages/maven")
+ name = "GitLab"
+ credentials(HttpHeaderCredentials::class) {
+ name = "Job-Token"
+ value = System.getenv("CI_JOB_TOKEN")
+ }
+ authentication {
+ create("header", HttpHeaderAuthentication::class)
+ }
+ }
+}
+```
+
+### Naming convention
+
+You can use one of three API endpoints to install a Maven package. You must publish a package to a project, but note which endpoint
+you use to install the package. The option you choose determines the settings you add to your `pom.xml` file for publishing.
+
+The three endpoints are:
+
+- **Project-level**: Use when you have a few Maven packages that are not in the same GitLab group.
+- **Group-level**: Use when installing packages from many different projects in the same GitLab group. GitLab does not guarantee the uniqueness of package names in the group. You can have two projects with the same package name and package version. As a result, GitLab serves whichever one is more recent.
+- **Instance-level**: Use when installing many packages from different GitLab groups or in their own namespace.
+
+**Only packages with the same path as the project** are exposed by the instance-level endpoint.
+
+| Project | Package | Instance-level endpoint available |
+| ------------------- | -------------------------------- | --------------------------------- |
+| `foo/bar` | `foo/bar/1.0-SNAPSHOT` | Yes |
+| `gitlab-org/gitlab` | `foo/bar/1.0-SNAPSHOT` | No |
+| `gitlab-org/gitlab` | `gitlab-org/gitlab/1.0-SNAPSHOT` | Yes |
+
+#### Endpoint URLs
+
+| Endpoint | Endpoint URL | Additional information |
+| -------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
+| Project | `https://gitlab.example.com/api/v4/projects/<project_id>/packages/maven` | Replace `gitlab.example.com` with your domain name. Replace `<project_id>` with your project ID found on your project's homepage. |
+| Group | `https://gitlab.example.com/api/v4/groups/<group_id>/-/packages/maven` | Replace `gitlab.example.com` with your domain name. Replace `<group_id>` with your group ID found on your group's homepage. |
+| Instance | `https:///gitlab.example.com/api/v4/packages/maven` | Replace `gitlab.example.com` with your domain name. |
+
+In all cases, to publish a package, you need:
+
+- A project-specific URL in the `distributionManagement` section.
+- A `repository` and `distributionManagement` section.
+
+### Edit the Groovy DSL or Kotlin DSL
+
+The Gradle Groovy DSL `repositories` section should look like this:
+
+```groovy
+repositories {
+ maven {
+ url "<your_endpoint_url>"
+ name "GitLab"
+ }
+}
+```
+
+In Kotlin DSL:
+
+```kotlin
+repositories {
+ maven {
+ url = uri("<your_endpoint_url>")
+ name = "GitLab"
+ }
+}
+```
+
+- Replace `<your_endpoint_url>` with the [endpoint](#endpoint-urls) you chose.
+
+## Publish using Gradle
+
+Your token name depends on which token you use.
+
+| Token type | Token name |
+| --------------------- | --------------- |
+| Personal access token | `Private-Token` |
+| Deploy token | `Deploy-Token` |
+
+To publish a package by using Gradle:
+
+1. Add the Gradle plugin [`maven-publish`](https://docs.gradle.org/current/userguide/publishing_maven.html) to the plugins section:
+
+ In Groovy DSL:
+
+ ```groovy
+ plugins {
+ id 'java'
+ id 'maven-publish'
+ }
+ ```
+
+ In Kotlin DSL:
+
+ ```kotlin
+ plugins {
+ java
+ `maven-publish`
+ }
+ ```
+
+1. Add a `publishing` section:
+
+ In Groovy DSL:
+
+ ```groovy
+ publishing {
+ publications {
+ library(MavenPublication) {
+ from components.java
+ }
+ }
+ repositories {
+ maven {
+ url "https://gitlab.example.com/api/v4/projects/<PROJECT_ID>/packages/maven"
+ credentials(HttpHeaderCredentials) {
+ name = "REPLACE_WITH_TOKEN_NAME"
+ value = gitLabPrivateToken // the variable resides in $GRADLE_USER_HOME/gradle.properties
+ }
+ authentication {
+ header(HttpHeaderAuthentication)
+ }
+ }
+ }
+ }
+ ```
+
+ In Kotlin DSL:
+
+ ```kotlin
+ publishing {
+ publications {
+ create<MavenPublication>("library") {
+ from(components["java"])
+ }
+ }
+ repositories {
+ maven {
+ url = uri("https://gitlab.example.com/api/v4/projects/<PROJECT_ID>/packages/maven")
+ credentials(HttpHeaderCredentials::class) {
+ name = "REPLACE_WITH_TOKEN_NAME"
+ value =
+ findProperty("gitLabPrivateToken") as String? // the variable resides in $GRADLE_USER_HOME/gradle.properties
+ }
+ authentication {
+ create("header", HttpHeaderAuthentication::class)
+ }
+ }
+ }
+ }
+ ```
+
+1. Replace `PROJECT_ID` with your project ID, which you can find on your project's home page.
+
+1. Run the publish task:
+
+ ```shell
+ gradle publish
+ ```
+
+Go to your project's **Packages and registries** page and view the published packages.
+
+## Install a package
+
+To install a package from the GitLab Package Registry, you must configure
+the [remote and authenticate](#authenticate-to-the-package-registry-with-gradle).
+After configuring the remote and authenticate, you can install a package from a project, group, or namespace.
+
+If multiple packages have the same name and version, when you install
+a package, the most recently-published package is retrieved.
+
+Add a [dependency](https://docs.gradle.org/current/userguide/declaring_dependencies.html) to `build.gradle` in the dependencies section:
+
+```groovy
+dependencies {
+ implementation 'com.mycompany.mydepartment:my-project:1.0-SNAPSHOT'
+}
+```
+
+Or to `build.gradle.kts` if you are using Kotlin DSL:
+
+```kotlin
+dependencies {
+ implementation("com.mycompany.mydepartment:my-project:1.0-SNAPSHOT")
+}
+```
+
+## Helpful hints
+
+For the complete list of helpful hints, see the [Maven documentation](../maven_repository/index.md#helpful-hints).
+
+### Create Maven packages with GitLab CI/CD by using Gradle
+
+You can create a package each time the `main` branch
+is updated.
+
+1. Authenticate with [a CI job token in Gradle](#authenticate-with-a-ci-job-token-in-gradle).
+
+1. Add a `deploy` job to your `.gitlab-ci.yml` file:
+
+ ```yaml
+ deploy:
+ image: gradle:6.5-jdk11
+ script:
+ - 'gradle publish'
+ only:
+ - main
+ ```
+
+1. Commit files to your repository.
+
+When the pipeline is successful, the Maven package is created.
+
+### Publishing a package with the same name or version
+
+When you publish a package with the same name and version as an existing package, the new package
+files are added to the existing package. You can still use the UI or API to access and view the
+existing package's older assets.
+
+Consider using the Packages API or the UI to delete older package versions.
+
+### Do not allow duplicate Maven packages
+
+To prevent users from publishing duplicate Maven packages, you can use the [GraphQl API](../../../api/graphql/reference/index.md#packagesettings) or the UI.
+
+In the UI:
+
+1. For your group, go to **Settings > Packages and registries**.
+1. Expand the **Package Registry** section.
+1. Turn on the **Do not allow duplicates** toggle.
+1. Optional. To allow some duplicate packages, in the **Exceptions** box, enter a regex pattern that matches the names and/or versions of packages you want to allow.
+
+Your changes are automatically saved.
+
+### Request forwarding to Maven Central
+
+FLAG:
+By default, this feature is not available for self-managed. To make it available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `maven_central_request_forwarding`.
+This feature is not available for SaaS users.
+
+When a Maven package is not found in the Package Registry, the request is forwarded
+to [Maven Central](https://search.maven.org/).
+
+When the feature flag is enabled, administrators can disable this behavior in the
+[Continuous Integration settings](../../admin_area/settings/continuous_integration.md).
+
+There are many ways to configure your Maven project to request packages
+in Maven Central from GitLab. Maven repositories are queried in a
+[specific order](https://maven.apache.org/guides/mini/guide-multiple-repositories.html#repository-order).
+By default, maven-central is usually checked first through the
+[Super POM](https://maven.apache.org/guides/introduction/introduction-to-the-pom.html#Super_POM), so
+GitLab needs to be configured to be queried before maven-central.
+
+[Using GitLab as a mirror of the central proxy](../maven_repository/index.md#setting-gitlab-as-a-mirror-for-the-central-proxy) is one
+way to force GitLab to be queried in place of maven-central.
+
+Maven forwarding is restricted to only the project level and
+group level [endpoints](#naming-convention). The instance-level endpoint
+has naming restrictions that prevent it from being used for packages that don't follow that convention and also
+introduces too much security risk for supply-chain style attacks.
diff --git a/doc/user/packages/helm_repository/index.md b/doc/user/packages/helm_repository/index.md
index bba68494c2d..785ef344c8e 100644
--- a/doc/user/packages/helm_repository/index.md
+++ b/doc/user/packages/helm_repository/index.md
@@ -72,7 +72,7 @@ Once built, a chart can be uploaded to the desired channel with `curl` or `helm
### Release channels
You can publish Helm charts to channels in GitLab. Channels are a method you can use to differentiate Helm chart repositories.
-For example, you can use `stable` and `devel` as channels to allow users to add the `stable` repo while `devel` charts are isolated.
+For example, you can use `stable` and `devel` as channels to allow users to add the `stable` repository while `devel` charts are isolated.
## Use CI/CD to publish a Helm package
diff --git a/doc/user/packages/maven_repository/index.md b/doc/user/packages/maven_repository/index.md
index 2aa71e111fb..c6c2f238564 100644
--- a/doc/user/packages/maven_repository/index.md
+++ b/doc/user/packages/maven_repository/index.md
@@ -4,9 +4,7 @@ group: Package Registry
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Maven packages in the Package Repository **(FREE)**
-
-> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) from GitLab Premium to GitLab Free in 13.3.
+# Maven packages in the Package Registry **(FREE)**
Publish [Maven](https://maven.apache.org) artifacts in your project's Package Registry.
Then, install the packages whenever you need to use them as a dependency.
@@ -14,75 +12,29 @@ Then, install the packages whenever you need to use them as a dependency.
For documentation of the specific API endpoints that the Maven package manager
client uses, see the [Maven API documentation](../../../api/packages/maven.md).
-Learn how to build a [Maven](../workflows/build_packages.md#maven) or [Gradle](../workflows/build_packages.md#gradle) package.
-
-## Authenticate to the Package Registry with Maven
-
-To authenticate to the Package Registry, you need one of the following:
+Learn how to build a [Maven](../workflows/build_packages.md#maven) package.
-- A [personal access token](../../../user/profile/personal_access_tokens.md) with the scope set to `api`.
-- A [deploy token](../../project/deploy_tokens/index.md) with the scope set to `read_package_registry`, `write_package_registry`, or both.
-- A [CI_JOB_TOKEN](#authenticate-with-a-ci-job-token-in-maven).
+## Publish to the GitLab Package Registry
-### Authenticate with a personal access token in Maven
-
-To use a personal access token, add this section to your
-[`settings.xml`](https://maven.apache.org/settings.html) file.
+### Authenticate to the Package Registry
-The `name` must be `Private-Token`.
-
-```xml
-<settings>
- <servers>
- <server>
- <id>gitlab-maven</id>
- <configuration>
- <httpHeaders>
- <property>
- <name>Private-Token</name>
- <value>REPLACE_WITH_YOUR_PERSONAL_ACCESS_TOKEN</value>
- </property>
- </httpHeaders>
- </configuration>
- </server>
- </servers>
-</settings>
-```
+You need an token to publish a package. There are different tokens available depending on what you're trying to achieve. For more information, review the [guidance on tokens](../package_registry/index.md#authenticate-with-the-registry).
-### Authenticate with a deploy token in Maven
+Create a token and save it to use later in the process.
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213566) deploy token authentication in GitLab 13.0.
-> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) from GitLab Premium to GitLab Free in 13.3.
+### Edit the `settings.xml`
-To use a deploy token, add this section to your
+Add the following section to your
[`settings.xml`](https://maven.apache.org/settings.html) file.
-The `name` must be `Deploy-Token`.
-
-```xml
-<settings>
- <servers>
- <server>
- <id>gitlab-maven</id>
- <configuration>
- <httpHeaders>
- <property>
- <name>Deploy-Token</name>
- <value>REPLACE_WITH_YOUR_DEPLOY_TOKEN</value>
- </property>
- </httpHeaders>
- </configuration>
- </server>
- </servers>
-</settings>
-```
-
-### Authenticate with a CI job token in Maven
-
-To authenticate with a CI job token, add this section to your
-[`settings.xml`](https://maven.apache.org/settings.html) file.
+NOTE:
+The `<name>` field must be named to match the token you chose.
-The `name` must be `Job-Token`.
+| Token type | Name must be | Token |
+| --------------------- | --------------- | ---------------------------------------------------------------------- |
+| Personal access token | `Private-Token` | Paste token as-is, or define an environment variable to hold the token |
+| Deploy token | `Deploy-Token` | Paste token as-is, or define an environment variable to hold the token |
+| CI Job token | `Job-Token` | `${CI_JOB_TOKEN}` |
```xml
<settings>
@@ -92,8 +44,8 @@ The `name` must be `Job-Token`.
<configuration>
<httpHeaders>
<property>
- <name>Job-Token</name>
- <value>${CI_JOB_TOKEN}</value>
+ <name>REPLACE_WITH_NAME</name>
+ <value>REPLACE_WITH_TOKEN</value>
</property>
</httpHeaders>
</configuration>
@@ -102,361 +54,70 @@ The `name` must be `Job-Token`.
</settings>
```
-Read more about [how to create Maven packages using GitLab CI/CD](#create-maven-packages-with-gitlab-cicd).
-
-## Authenticate to the Package Registry with Gradle
-
-To authenticate to the Package Registry, you need either a personal access token or deploy token.
-
-- If you use a [personal access token](../../../user/profile/personal_access_tokens.md), set the scope to `api`.
-- If you use a [deploy token](../../project/deploy_tokens/index.md), set the scope to `read_package_registry`, `write_package_registry`, or both.
-
-### Authenticate with a personal access token in Gradle
-
-In [your `GRADLE_USER_HOME` directory](https://docs.gradle.org/current/userguide/directory_layout.html#dir:gradle_user_home),
-create a file `gradle.properties` with the following content:
-
-```properties
-gitLabPrivateToken=REPLACE_WITH_YOUR_PERSONAL_ACCESS_TOKEN
-```
-
-Add a `repositories` section to your
-[`build.gradle`](https://docs.gradle.org/current/userguide/tutorial_using_tasks.html)
-file:
-
-```groovy
-repositories {
- maven {
- url "https://gitlab.example.com/api/v4/groups/<group>/-/packages/maven"
- name "GitLab"
- credentials(HttpHeaderCredentials) {
- name = 'Private-Token'
- value = gitLabPrivateToken
- }
- authentication {
- header(HttpHeaderAuthentication)
- }
- }
-}
-```
-
-Or add it to your `build.gradle.kts` file if you are using Kotlin DSL:
-
-```kotlin
-repositories {
- maven {
- url = uri("https://gitlab.example.com/api/v4/groups/<group>/-/packages/maven")
- name = "GitLab"
- credentials(HttpHeaderCredentials::class) {
- name = "Private-Token"
- value = findProperty("gitLabPrivateToken") as String?
- }
- authentication {
- create("header", HttpHeaderAuthentication::class)
- }
- }
-}
-```
+### Naming convention
-### Authenticate with a deploy token in Gradle
-
-To authenticate with a deploy token, add a `repositories` section to your
-[`build.gradle`](https://docs.gradle.org/current/userguide/tutorial_using_tasks.html)
-file:
-
-```groovy
-repositories {
- maven {
- url "https://gitlab.example.com/api/v4/groups/<group>/-/packages/maven"
- name "GitLab"
- credentials(HttpHeaderCredentials) {
- name = 'Deploy-Token'
- value = '<deploy-token>'
- }
- authentication {
- header(HttpHeaderAuthentication)
- }
- }
-}
-```
+You can use one of three endpoints to install a Maven package. You must publish a package to a project, but the endpoint you choose determines the settings you add to your `pom.xml` file for publishing.
-Or add it to your `build.gradle.kts` file if you are using Kotlin DSL:
-
-```kotlin
-repositories {
- maven {
- url = uri("https://gitlab.example.com/api/v4/groups/<group>/-/packages/maven")
- name = "GitLab"
- credentials(HttpHeaderCredentials::class) {
- name = "Deploy-Token"
- value = "<deploy-token>"
- }
- authentication {
- create("header", HttpHeaderAuthentication::class)
- }
- }
-}
-```
+The three endpoints are:
-### Authenticate with a CI job token in Gradle
-
-To authenticate with a CI job token, add a `repositories` section to your
-[`build.gradle`](https://docs.gradle.org/current/userguide/tutorial_using_tasks.html)
-file:
-
-```groovy
-repositories {
- maven {
- url "${CI_API_V4_URL}/groups/<group>/-/packages/maven"
- name "GitLab"
- credentials(HttpHeaderCredentials) {
- name = 'Job-Token'
- value = System.getenv("CI_JOB_TOKEN")
- }
- authentication {
- header(HttpHeaderAuthentication)
- }
- }
-}
-```
+- **Project-level**: Use when you have a few Maven packages and they are not in the same GitLab group.
+- **Group-level**: Use when you want to install packages from many different projects in the same GitLab group. GitLab does not guarantee the uniqueness of package names within the group. You can have two projects with the same package name and package version. As a result, GitLab serves whichever one is more recent.
+- **Instance-level**: Use when you have many packages in different GitLab groups or in their own namespace.
-Or add it to your `build.gradle.kts` file if you are using Kotlin DSL:
-
-```kotlin
-repositories {
- maven {
- url = uri("$CI_API_V4_URL/groups/<group>/-/packages/maven")
- name = "GitLab"
- credentials(HttpHeaderCredentials::class) {
- name = "Job-Token"
- value = System.getenv("CI_JOB_TOKEN")
- }
- authentication {
- create("header", HttpHeaderAuthentication::class)
- }
- }
-}
-```
+**Only packages that have the same path as the project** are exposed by the instance-level endpoint.
-## Use the GitLab endpoint for Maven packages
+| Project | Package | Instance-level endpoint available |
+| ------------------- | -------------------------------- | --------------------------------- |
+| `foo/bar` | `foo/bar/1.0-SNAPSHOT` | Yes |
+| `gitlab-org/gitlab` | `foo/bar/1.0-SNAPSHOT` | No |
+| `gitlab-org/gitlab` | `gitlab-org/gitlab/1.0-SNAPSHOT` | Yes |
-To use the GitLab endpoint for Maven packages, choose an option:
+#### Endpoint URLs
-- **Project-level**: To publish Maven packages to a project, use a project-level endpoint.
- To install Maven packages, use a project-level endpoint when you have few Maven packages
- and they are not in the same GitLab group.
-- **Group-level**: Use a group-level endpoint when you want to install packages from
- many different projects in the same GitLab group.
-- **Instance-level**: Use an instance-level endpoint when you want to install many
- packages from different GitLab groups or in their own namespace.
+| Endpoint | Endpoint URL for `pom.xml` | Additional information |
+| -------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
+| Project | `https://gitlab.example.com/api/v4/projects/<project_id>/packages/maven` | Replace `gitlab.example.com` with your domain name. Replace `<project_id>` with your project ID, found on your project's homepage. |
+| Group | `https://gitlab.example.com/api/v4/groups/<group_id>/-/packages/maven` | Replace `gitlab.example.com` with your domain name. Replace `<group_id>` with your group ID, found on your group's homepage. |
+| Instance | `https:///gitlab.example.com/api/v4/packages/maven` | Replace `gitlab.example.com` with your domain name. |
-The option you choose determines the settings you add to your `pom.xml` file.
+### Edit the `pom.xml` for publishing
-In all cases, to publish a package, you need:
+No matter which endpoint you choose, you must have:
- A project-specific URL in the `distributionManagement` section.
- A `repository` and `distributionManagement` section.
-### Project-level Maven endpoint
-
-The relevant `repository` section of your `pom.xml`
-in Maven should look like this:
+The relevant `repository` section of your `pom.xml` in Maven should look like this:
```xml
<repositories>
<repository>
<id>gitlab-maven</id>
- <url>https://gitlab.example.com/api/v4/projects/PROJECT_ID/packages/maven</url>
+ <url><your_endpoint_url></url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>gitlab-maven</id>
- <url>https://gitlab.example.com/api/v4/projects/PROJECT_ID/packages/maven</url>
+ <url>https://gitlab.example.com/api/v4/projects/<project_id>/packages/maven</url>
</repository>
<snapshotRepository>
<id>gitlab-maven</id>
- <url>https://gitlab.example.com/api/v4/projects/PROJECT_ID/packages/maven</url>
+ <url>https://gitlab.example.com/api/v4/projects/<project_id>/packages/maven</url>
</snapshotRepository>
</distributionManagement>
```
-The corresponding section in Gradle Groovy DSL would be:
-
-```groovy
-repositories {
- maven {
- url "https://gitlab.example.com/api/v4/projects/PROJECT_ID/packages/maven"
- name "GitLab"
- }
-}
-```
-
-In Kotlin DSL:
-
-```kotlin
-repositories {
- maven {
- url = uri("https://gitlab.example.com/api/v4/projects/PROJECT_ID/packages/maven")
- name = "GitLab"
- }
-}
-```
-
-- The `id` is what you [defined in `settings.xml`](#authenticate-to-the-package-registry-with-maven).
-- The `PROJECT_ID` is your project ID, which you can view on your project's home page.
-- Replace `gitlab.example.com` with your domain name.
-- For retrieving artifacts, use either the
- [URL-encoded](../../../api/index.md#namespaced-path-encoding) path of the project
- (like `group%2Fproject`) or the project's ID (like `42`). However, only the
- project's ID can be used for publishing.
-
-### Group-level Maven endpoint
-
-> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) from GitLab Premium to GitLab Free in 13.3.
-
-If you rely on many packages, it might be inefficient to include the `repository` section
-with a unique URL for each package. Instead, you can use the group-level endpoint for
-all the Maven packages stored within one GitLab group. Only packages you have access to
-are available for download.
-
-The group-level endpoint works with any package names, so you
-have more flexibility in naming, compared to the [instance-level endpoint](#instance-level-maven-endpoint).
-However, GitLab does not guarantee the uniqueness of package names within
-the group. You can have two projects with the same package name and package
-version. As a result, GitLab serves whichever one is more recent.
-
-This example shows the relevant `repository` section of your `pom.xml` file.
-You still need a project-specific URL for publishing a package in
-the `distributionManagement` section:
-
-```xml
-<repositories>
- <repository>
- <id>gitlab-maven</id>
- <url>https://gitlab.example.com/api/v4/groups/GROUP_ID/-/packages/maven</url>
- </repository>
-</repositories>
-<distributionManagement>
- <repository>
- <id>gitlab-maven</id>
- <url>https://gitlab.example.com/api/v4/projects/PROJECT_ID/packages/maven</url>
- </repository>
- <snapshotRepository>
- <id>gitlab-maven</id>
- <url>https://gitlab.example.com/api/v4/projects/PROJECT_ID/packages/maven</url>
- </snapshotRepository>
-</distributionManagement>
-```
-
-For Gradle, the corresponding `repositories` section in Groovy DSL would look like:
-
-```groovy
-repositories {
- maven {
- url "https://gitlab.example.com/api/v4/groups/GROUP_ID/-/packages/maven"
- name "GitLab"
- }
-}
-```
-
-In Kotlin DSL:
-
-```kotlin
-repositories {
- maven {
- url = uri("https://gitlab.example.com/api/v4/groups/GROUP_ID/-/packages/maven")
- name = "GitLab"
- }
-}
-```
-
-- For the `id`, use what you [defined in `settings.xml`](#authenticate-to-the-package-registry-with-maven).
-- For `GROUP_ID`, use your group ID, which you can view on your group's home page.
-- For `PROJECT_ID`, use your project ID, which you can view on your project's home page.
+- The `id` is what you [defined in `settings.xml`](#edit-the-settingsxml).
+- The `<your_endpoint_url>` depends on which [endpoint](#endpoint-urls) you choose.
- Replace `gitlab.example.com` with your domain name.
-- For retrieving artifacts, use either the
- [URL-encoded](../../../api/index.md#namespaced-path-encoding) path of the group
- (like `group%2Fsubgroup`) or the group's ID (like `12`).
-
-### Instance-level Maven endpoint
-
-> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) from GitLab Premium to GitLab Free in 13.3.
-
-If you rely on many packages, it might be inefficient to include the `repository` section
-with a unique URL for each package. Instead, you can use the instance-level endpoint for
-all Maven packages stored in GitLab. All packages you have access to are available
-for download.
-
-**Only packages that have the same path as the project** are exposed by
-the instance-level endpoint.
-
-| Project | Package | Instance-level endpoint available |
-| ------------------- | -------------------------------- | --------------------------------- |
-| `foo/bar` | `foo/bar/1.0-SNAPSHOT` | Yes |
-| `gitlab-org/gitlab` | `foo/bar/1.0-SNAPSHOT` | No |
-| `gitlab-org/gitlab` | `gitlab-org/gitlab/1.0-SNAPSHOT` | Yes |
-
-This example shows how relevant `repository` section of your `pom.xml`.
-You still need a project-specific URL in the `distributionManagement` section.
-
-```xml
-<repositories>
- <repository>
- <id>gitlab-maven</id>
- <url>https://gitlab.example.com/api/v4/packages/maven</url>
- </repository>
-</repositories>
-<distributionManagement>
- <repository>
- <id>gitlab-maven</id>
- <url>https://gitlab.example.com/api/v4/projects/PROJECT_ID/packages/maven</url>
- </repository>
- <snapshotRepository>
- <id>gitlab-maven</id>
- <url>https://gitlab.example.com/api/v4/projects/PROJECT_ID/packages/maven</url>
- </snapshotRepository>
-</distributionManagement>
-```
-
-The corresponding repositories section in Gradle Groovy DSL would look like:
-
-```groovy
-repositories {
- maven {
- url "https://gitlab.example.com/api/v4/packages/maven"
- name "GitLab"
- }
-}
-```
-
-In Kotlin DSL:
-
-```kotlin
-repositories {
- maven {
- url = uri("https://gitlab.example.com/api/v4/packages/maven")
- name = "GitLab"
- }
-}
-```
-
-- The `id` is what you [defined in `settings.xml`](#authenticate-to-the-package-registry-with-maven).
-- The `PROJECT_ID` is your project ID, which you can view on your project's home page.
-- Replace `gitlab.example.com` with your domain name.
-- For retrieving artifacts, use either the
- [URL-encoded](../../../api/index.md#namespaced-path-encoding) path of the project
- (like `group%2Fproject`) or the project's ID (like `42`). However, only the
- project's ID can be used for publishing.
## Publish a package
-After you have set up the [remote and authentication](#authenticate-to-the-package-registry-with-maven)
-and [configured your project](#use-the-gitlab-endpoint-for-maven-packages),
+After you have set up the [authentication](#authenticate-to-the-package-registry)
+and [chosen an endpoint for publishing](#naming-convention),
publish a Maven package to your project.
-### Publish by using Maven
-
To publish a package by using Maven:
```shell
@@ -474,122 +135,13 @@ If the deploy is successful, the build success message should be displayed:
The message should also show that the package was published to the correct location:
```shell
-Uploading to gitlab-maven: https://gitlab.example.com/api/v4/projects/PROJECT_ID/packages/maven/com/mycompany/mydepartment/my-project/1.0-SNAPSHOT/my-project-1.0-20200128.120857-1.jar
+Uploading to gitlab-maven: https://example.com/api/v4/projects/PROJECT_ID/packages/maven/com/mycompany/mydepartment/my-project/1.0-SNAPSHOT/my-project-1.0-20200128.120857-1.jar
```
-### Publish by using Gradle
-
-To publish a package by using Gradle:
-
-1. Add the Gradle plugin [`maven-publish`](https://docs.gradle.org/current/userguide/publishing_maven.html) to the plugins section:
-
- In Groovy DSL:
-
- ```groovy
- plugins {
- id 'java'
- id 'maven-publish'
- }
- ```
-
- In Kotlin DSL:
-
- ```kotlin
- plugins {
- java
- `maven-publish`
- }
- ```
-
-1. Add a `publishing` section:
-
- In Groovy DSL:
-
- ```groovy
- publishing {
- publications {
- library(MavenPublication) {
- from components.java
- }
- }
- repositories {
- maven {
- url "https://gitlab.example.com/api/v4/projects/<PROJECT_ID>/packages/maven"
- credentials(HttpHeaderCredentials) {
- name = "Private-Token"
- value = gitLabPrivateToken // the variable resides in $GRADLE_USER_HOME/gradle.properties
- }
- authentication {
- header(HttpHeaderAuthentication)
- }
- }
- }
- }
- ```
-
- In Kotlin DSL:
-
- ```kotlin
- publishing {
- publications {
- create<MavenPublication>("library") {
- from(components["java"])
- }
- }
- repositories {
- maven {
- url = uri("https://gitlab.example.com/api/v4/projects/<PROJECT_ID>/packages/maven")
- credentials(HttpHeaderCredentials::class) {
- name = "Private-Token"
- value =
- findProperty("gitLabPrivateToken") as String? // the variable resides in $GRADLE_USER_HOME/gradle.properties
- }
- authentication {
- create("header", HttpHeaderAuthentication::class)
- }
- }
- }
- }
- ```
-
-1. Replace `PROJECT_ID` with your project ID, which can be found on your project's home page.
-
-1. Run the publish task:
-
- ```shell
- gradle publish
- ```
-
-Now navigate to your project's **Packages and registries** page and view the published artifacts.
-
-### Publishing a package with the same name or version
-
-When you publish a package with the same name and version as an existing package, the new package
-files are added to the existing package. You can still use the UI or API to access and view the
-existing package's older files.
-
-To delete these older package versions, consider using the Packages API or the UI.
-
-#### Do not allow duplicate Maven packages
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/296895) in GitLab 13.9.
-> - [Required permissions](https://gitlab.com/gitlab-org/gitlab/-/issues/350682) changed from developer to maintainer in GitLab 15.0.
-
-To prevent users from publishing duplicate Maven packages, you can use the [GraphQl API](../../../api/graphql/reference/index.md#packagesettings) or the UI.
-
-In the UI:
-
-1. For your group, go to **Settings > Packages and registries**.
-1. Expand the **Package Registry** section.
-1. Turn on the **Reject duplicates** toggle.
-1. Optional. To allow some duplicate packages, in the **Exceptions** box, enter a regex pattern that matches the names and/or versions of packages you want to allow.
-
-Your changes are automatically saved.
-
## Install a package
To install a package from the GitLab Package Registry, you must configure
-the [remote and authenticate](#authenticate-to-the-package-registry-with-maven).
+the [remote and authenticate](#authenticate-to-the-package-registry).
When this is completed, you can install a package from a project,
group, or namespace.
@@ -633,8 +185,8 @@ You can install packages by using the Maven `dependency:get` [command](https://m
mvn dependency:get -Dartifact=com.nickkipling.app:nick-test-app:1.1-SNAPSHOT -DremoteRepositories=gitlab-maven::::<gitlab endpoint url> -s <path to settings.xml>
```
- - `<gitlab endpoint url>` is the URL of the GitLab [endpoint](#use-the-gitlab-endpoint-for-maven-packages).
- - `<path to settings.xml>` is the path to the `settings.xml` file that contains the [authentication details](#authenticate-to-the-package-registry-with-maven).
+ - `<gitlab endpoint url>` is the URL of the GitLab [endpoint](#endpoint-urls).
+ - `<path to settings.xml>` is the path to the `settings.xml` file that contains the [authentication details](#edit-the-settingsxml).
NOTE:
The repository IDs in the command(`gitlab-maven`) and the `settings.xml` file must match.
@@ -645,34 +197,34 @@ The message should show that the package is downloading from the Package Registr
Downloading from gitlab-maven: http://gitlab.example.com/api/v4/projects/PROJECT_ID/packages/maven/com/mycompany/mydepartment/my-project/1.0-SNAPSHOT/my-project-1.0-20200128.120857-1.pom
```
-NOTE:
-In the GitLab UI, on the Package Registry page for Maven, you can view and copy these commands.
+## Helpful hints
-### Use Gradle
+### Publishing a package with the same name or version
-Add a [dependency](https://docs.gradle.org/current/userguide/declaring_dependencies.html) to `build.gradle` in the dependencies section:
+When you publish a package with the same name and version as an existing package, the new package
+files are added to the existing package. You can still use the UI or API to access and view the
+existing package's older assets.
-```groovy
-dependencies {
- implementation 'com.mycompany.mydepartment:my-project:1.0-SNAPSHOT'
-}
-```
+To delete older package versions, consider using the Packages API or the UI.
-Or to `build.gradle.kts` if you are using Kotlin DSL:
+### Do not allow duplicate Maven packages
-```kotlin
-dependencies {
- implementation("com.mycompany.mydepartment:my-project:1.0-SNAPSHOT")
-}
-```
+To prevent users from publishing duplicate Maven packages, you can use the [GraphQl API](../../../api/graphql/reference/index.md#packagesettings) or the UI.
-### Request forwarding to Maven Central
+In the UI:
+
+1. For your group, go to **Settings > Packages and registries**.
+1. Expand the **Package Registry** section.
+1. Turn on the **Do not allow duplicates** toggle.
+1. Optional. To allow some duplicate packages, in the **Exceptions** box, enter a regex pattern that matches the names and/or versions of packages you want to allow.
+
+Your changes are automatically saved.
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/362657) behind a [feature flag](../../feature_flags.md), disabled by default in GitLab 15.4
+### Request forwarding to Maven Central
FLAG:
-On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `maven_central_request_forwarding`.
-On GitLab.com, this feature is not available.
+By default this feature is not available for self-managed. To make it available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `maven_central_request_forwarding`.
+This feature is not available for SaaS users.
When a Maven package is not found in the Package Registry, the request is forwarded
to [Maven Central](https://search.maven.org/).
@@ -690,8 +242,8 @@ GitLab needs to be configured to be queried before maven-central.
[Using GitLab as a mirror of the central proxy](#setting-gitlab-as-a-mirror-for-the-central-proxy) is one
way to force GitLab to be queried in place of maven-central.
-Maven forwarding is restricted to only the [project level](#project-level-maven-endpoint) and
-[group level](#group-level-maven-endpoint) endpoints. The [instance level endpoint](#instance-level-maven-endpoint)
+Maven forwarding is restricted to only the project level and
+group level [endpoints](#naming-convention). The instance level endpoint
has naming restrictions that prevent it from being used for packages that don't follow that convention and also
introduces too much security risk for supply-chain style attacks.
@@ -710,7 +262,7 @@ section to your `settings.xml`:
<httpHeaders>
<property>
<name>Private-Token</name>
- <value>{personal_access_token}</value>
+ <value><personal_access_token></value>
</property>
</httpHeaders>
</configuration>
@@ -720,25 +272,19 @@ section to your `settings.xml`:
<mirror>
<id>central-proxy</id>
<name>GitLab proxy of central repo</name>
- <url>https://gitlab.example.com/api/v4/projects/{project_id}/packages/maven</url>
+ <url>https://gitlab.example.com/api/v4/projects/<project_id>/packages/maven</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
</settings>
```
-## Remove a package
-
-For your project, go to **Packages and registries > Package Registry**.
-
-To remove a package, select the red trash icon or, from the package details, the **Delete** button.
-
-## Create Maven packages with GitLab CI/CD
+### Create Maven packages with GitLab CI/CD
After you have configured your repository to use the Package Repository for Maven,
you can configure GitLab CI/CD to build new packages automatically.
-### Create Maven packages with GitLab CI/CD by using Maven
+### Create Maven packages with GitLab CI/CD using Maven
You can create a new package each time the `main` branch is updated.
@@ -808,37 +354,51 @@ user's home location. In this example:
- The user is `root`, because the job runs in a Docker container.
- Maven uses the configured CI/CD variables.
-### Create Maven packages with GitLab CI/CD by using Gradle
+### Version validation
-You can create a package each time the `main` branch
-is updated.
+The version string is validated by using the following regex.
-1. Authenticate with [a CI job token in Gradle](#authenticate-with-a-ci-job-token-in-gradle).
+```ruby
+\A(?!.*\.\.)[\w+.-]+\z
+```
-1. Add a `deploy` job to your `.gitlab-ci.yml` file:
+You can experiment with the regex and try your version strings on [this regular expression editor](https://rubular.com/r/rrLQqUXjfKEoL6).
- ```yaml
- deploy:
- image: gradle:6.5-jdk11
- script:
- - 'gradle publish'
- only:
- - main
- ```
+### Useful Maven command-line options
-1. Commit files to your repository.
+There are some [Maven command-line options](https://maven.apache.org/ref/current/maven-embedder/cli.html)
+that you can use when performing tasks with GitLab CI/CD.
-When the pipeline is successful, the package is created.
+- File transfer progress can make the CI logs hard to read.
+ Option `-ntp,--no-transfer-progress` was added in
+ [3.6.1](https://maven.apache.org/docs/3.6.1/release-notes.html#User_visible_Changes).
+ Alternatively, look at `-B,--batch-mode`
+ [or lower level logging changes.](https://stackoverflow.com/questions/21638697/disable-maven-download-progress-indication)
-### Version validation
+- Specify where to find the `pom.xml` file (`-f,--file`):
-The version string is validated by using the following regex.
+ ```yaml
+ package:
+ script:
+ - 'mvn --no-transfer-progress -f helloworld/pom.xml package'
+ ```
-```ruby
-\A(?!.*\.\.)[\w+.-]+\z
-```
+- Specify where to find the user settings (`-s,--settings`) instead of
+ [the default location](https://maven.apache.org/settings.html). There's also a `-gs,--global-settings` option:
+
+ ```yaml
+ package:
+ script:
+ - 'mvn -s settings/ci.xml package'
+ ```
-You can play around with the regex and try your version strings on [this regular expression editor](https://rubular.com/r/rrLQqUXjfKEoL6).
+### Supported CLI commands
+
+The GitLab Maven repository supports the following Maven CLI commands:
+
+- `mvn deploy`: Publish your package to the Package Registry.
+- `mvn install`: Install packages specified in your Maven project.
+- `mvn dependency:get`: Install a specific package.
## Troubleshooting
@@ -870,34 +430,6 @@ mvn deploy \
WARNING:
When you set these options, all network requests are logged and a large amount of output is generated.
-### Useful Maven command-line options
-
-There are some [Maven command-line options](https://maven.apache.org/ref/current/maven-embedder/cli.html)
-that you can use when performing tasks with GitLab CI/CD.
-
-- File transfer progress can make the CI logs hard to read.
- Option `-ntp,--no-transfer-progress` was added in
- [3.6.1](https://maven.apache.org/docs/3.6.1/release-notes.html#User_visible_Changes).
- Alternatively, look at `-B,--batch-mode`
- [or lower level logging changes.](https://stackoverflow.com/questions/21638697/disable-maven-download-progress-indication)
-
-- Specify where to find the `pom.xml` file (`-f,--file`):
-
- ```yaml
- package:
- script:
- - 'mvn --no-transfer-progress -f helloworld/pom.xml package'
- ```
-
-- Specify where to find the user settings (`-s,--settings`) instead of
- [the default location](https://maven.apache.org/settings.html). There's also a `-gs,--global-settings` option:
-
- ```yaml
- package:
- script:
- - 'mvn -s settings/ci.xml package'
- ```
-
### Verify your Maven settings
If you encounter issues within CI/CD that relate to the `settings.xml` file, try adding
@@ -916,11 +448,3 @@ package:
- 'mvn help:system'
- 'mvn package'
```
-
-## Supported CLI commands
-
-The GitLab Maven repository supports the following Maven CLI commands:
-
-- `mvn deploy`: Publish your package to the Package Registry.
-- `mvn install`: Install packages specified in your Maven project.
-- `mvn dependency:get`: Install a specific package.
diff --git a/doc/user/packages/npm_registry/index.md b/doc/user/packages/npm_registry/index.md
index 5d2efc52ba9..c62999100c1 100644
--- a/doc/user/packages/npm_registry/index.md
+++ b/doc/user/packages/npm_registry/index.md
@@ -6,250 +6,97 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# npm packages in the Package Registry **(FREE)**
-> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) from GitLab Premium to GitLab Free in 13.3.
-
-Publish npm packages in your project's Package Registry. Then install the
-packages whenever you need to use them as a dependency.
-
-Only [scoped](https://docs.npmjs.com/misc/scope/) packages are supported.
-
-For documentation of the specific API endpoints that the npm package manager
-client uses, see the [npm API documentation](../../../api/packages/npm.md).
-
-WARNING:
-Never hardcode GitLab tokens (or any tokens) directly in `.npmrc` files or any other files that can
-be committed to a repository.
+For documentation of the specific API endpoints that the npm package manager client uses, see the [npm API documentation](../../../api/packages/npm.md).
Learn how to build an [npm](../workflows/build_packages.md#npm) or [yarn](../workflows/build_packages.md#yarn) package.
-## Use the GitLab endpoint for npm packages
-
-To use the GitLab endpoint for npm packages, choose an option:
+Watch a [video demo](https://youtu.be/yvLxtkvsFDA) of how to publish npm packages to the GitLab Package Registry.
-- **Project-level**: Use when you have few npm packages and they are not in
- the same GitLab group. The [package naming convention](#package-naming-convention) is not enforced at this level.
- Instead, you should use a [scope](https://docs.npmjs.com/cli/v6/using-npm/scope/) for your package.
- When you use a scope, the registry URL is [updated](#authenticate-to-the-package-registry) only for that scope.
-- **Instance-level**: Use when you have many npm packages in different
- GitLab groups or in their own namespace. Be sure to comply with the [package naming convention](#package-naming-convention).
+## Publish to GitLab Package Registry
-Some features such as [publishing](#publish-an-npm-package) a package is only available on the project-level endpoint.
+### Authentication to the Package Registry
-## Authenticate to the Package Registry
+You need an token to publish a package. There are different tokens available depending on what you're trying to achieve. For more information, review the [guidance on tokens](../../../user/packages/package_registry/index.md#authenticate-with-the-registry).
-You must authenticate with the Package Registry when the project
-is private. Public projects do not require authentication.
+- If your organization uses two factor authentication (2FA), you must use a personal access token with the scope set to `api`.
+- If you are publishing a package via CI/CD pipelines, you must use a CI job token.
-To authenticate, use one of the following:
+Create a token and save it to use later in the process.
-- A [personal access token](../../../user/profile/personal_access_tokens.md)
- (required for two-factor authentication (2FA)), with the scope set to `api`.
-- A [deploy token](../../project/deploy_tokens/index.md), with the scope set to `read_package_registry`, `write_package_registry`, or both.
-- It's not recommended, but you can use [OAuth tokens](../../../api/oauth2.md#resource-owner-password-credentials-flow).
- Standard OAuth tokens cannot authenticate to the GitLab npm Registry. You must use a personal access token with OAuth headers.
-- A [CI job token](#authenticate-with-a-ci-job-token).
-- Your npm package name must be in the format of [`@scope/package-name`](#package-naming-convention).
- It must match exactly, including the case.
+### Naming convention
-### Authenticate with a personal access token or deploy token
+Depending on how the package will be installed, you may need to adhere to the naming convention.
-To authenticate with the Package Registry, you need a [personal access token](../../profile/personal_access_tokens.md) or [deploy token](../../project/deploy_tokens/index.md).
+You can use one of two API endpoints to install packages:
-#### Project-level npm endpoint
+- **Instance-level**: Use when you have many npm packages in different GitLab groups or in their own namespace.
+- **Project-level**: Use when you have few npm packages and they are not in the same GitLab group.
-To use the [project-level](#use-the-gitlab-endpoint-for-npm-packages) npm endpoint, set your npm configuration:
+If you plan to install a package through the [project level](#install-from-the-project-level), then you do not have to adhere to the naming convention.
-```shell
-# Set URL for your scoped packages.
-# For example package with name `@foo/bar` will use this URL for download
-npm config set @foo:registry https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/
-
-# Add the token for the scoped packages URL. Replace <your_project_id>
-# with the project where your package is located.
-npm config set -- '//gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken' "<your_token>"
-```
-
-- `<your_project_id>` is your project ID, found on the project's home page.
-- `<your_token>` is your personal access token or deploy token.
-- Replace `gitlab.example.com` with your domain name.
+If you plan to install a package through the [instance level](#install-from-the-instance-level), then you must name your package with a [scope](https://docs.npmjs.com/misc/scope/). Scoped packages begin with a `@` have the format of `@owner/package-name`. You can set up the scope for your package in the `.npmrc` file and by using the `publishConfig` option in the `package.json`.
-You should now be able to publish and install npm packages in your project.
+- The value used for the `@scope` is the root of the project that will host the packages and not the root
+ of the project with the source code of the package itself. The scope should be lowercase.
+- The package name can be anything you want
-If you encounter an error with [Yarn](https://classic.yarnpkg.com/en/), view
-[troubleshooting steps](#troubleshooting).
+| Project URL | Package Registry in | Scope | Full package name |
+| ------------------------------------------------------- | ------------------- | --------- | ---------------------- |
+| `https://gitlab.com/my-org/engineering-group/analytics` | Analytics | `@my-org` | `@my-org/package-name` |
-#### Instance-level npm endpoint
-
-NOTE:
-Note: Using `CI_JOB_TOKEN` to install npm packages with dependencies in another project will give you 404 errors. You can use a [personal access token](../../profile/personal_access_tokens.md) as a workaround. [GitLab-#352962](https://gitlab.com/gitlab-org/gitlab/-/issues/352962) proposes a fix to this bug.
-
-To use the [instance-level](#use-the-gitlab-endpoint-for-npm-packages) npm endpoint, set your npm configuration:
+Make sure that the name of your package in the `package.json` file matches this convention:
```shell
-# Set URL for your scoped packages.
-# For example package with name `@foo/bar` will use this URL for download
-npm config set @foo:registry https://gitlab.example.com/api/v4/packages/npm/
-
-# Add the token for the scoped packages URL. This will allow you to download
-# `@foo/` packages from private projects.
-npm config set -- '//gitlab.example.com/api/v4/packages/npm/:_authToken' "<your_token>"
-```
-
-- `<your_token>` is your personal access token or deploy token.
-- Replace `gitlab.example.com` with your domain name.
-
-You should now be able to install npm packages in your project.
-
-If you encounter an error with [Yarn](https://classic.yarnpkg.com/en/), view
-[troubleshooting steps](#troubleshooting).
-
-### Authenticate with a CI job token
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9104) in GitLab 12.5.
-> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) from GitLab Premium to GitLab Free in 13.3.
-
-If you're using npm with GitLab CI/CD, a CI job token can be used instead of a personal access token or deploy token.
-The token inherits the permissions of the user that generates the pipeline.
-
-#### Project-level npm endpoint
-
-To use the [project-level](#use-the-gitlab-endpoint-for-npm-packages) npm endpoint, add a corresponding section to your `.npmrc` file:
-
-```ini
-@foo:registry=https://gitlab.example.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/
-//gitlab.example.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}
+"name": "@my-org/package-name"
```
-#### Instance-level npm endpoint
-
-To use the [instance-level](#use-the-gitlab-endpoint-for-npm-packages) npm endpoint, add a corresponding section to your `.npmrc` file:
-
-```ini
-@foo:registry=https://gitlab.example.com/api/v4/packages/npm/
-//gitlab.example.com/api/v4/packages/npm/:_authToken=${CI_JOB_TOKEN}
-```
+## Publishing a package via the command line
-#### Use variables to avoid hard-coding auth token values
+### Authenticating via the `.npmrc`
-To avoid hard-coding the `authToken` value, you may use a variable in its place:
+Create or edit the `.npmrc` file in the same directory as your `package.json`. Include the following lines in the `.npmrc` file:
```shell
-npm config set -- '//gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken' "${NPM_TOKEN}"
-npm config set -- '//gitlab.example.com/api/v4/packages/npm/:_authToken' "${NPM_TOKEN}"
+@scope:registry=https://your_domain_name/api/v4/projects/your_project_id/packages/npm/
+//your_domain_name/api/v4/projects/your_project_id/packages/npm/:_authToken="${NPM_TOKEN}"
```
-Then, you can run `npm publish` either locally or by using GitLab CI/CD.
+- Replace `@scope` with the [root level group](#naming-convention) of the project you're publishing to the package to.
+- Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
+- Replace `your_project_id` is your project ID, found on the project's home page.
+- `"${NPM_TOKEN}"` will be associated with the token you created later in the process.
-- **Locally:** Export `NPM_TOKEN` before publishing:
-
- ```shell
- NPM_TOKEN=<your_token> npm publish
- ```
-
-- **GitLab CI/CD:** Set an `NPM_TOKEN` [CI/CD variable](../../../ci/variables/index.md)
- under your project's **Settings > CI/CD > Variables**.
+WARNING:
+Never hardcode GitLab tokens (or any tokens) directly in `.npmrc` files or any other files that can
+be committed to a repository.
-## Working with private registries
+### Publishing a package via the command line
-When working with private repositories, you may want to configure additional settings to ensure a secure communication channel:
+Associate your [token](#authentication-to-the-package-registry) with the `"${NPM_TOKEN}"` in the `.npmrc`. Replace `your_token` with a deploy token, group access token, project access token, or personal access token.
```shell
-# Force npm to always require authentication when accessing the registry, even for GET requests.
-npm config set always-auth true
-```
-
-## Package naming convention
-
-When you use the [instance-level endpoint](#use-the-gitlab-endpoint-for-npm-packages), only the packages with names in the format of `@scope/package-name` are available.
-
-- The `@scope` is the root namespace of the GitLab project. To follow npm's convention, it should be
- lowercase. However, the GitLab package registry allows for uppercase. Before GitLab 13.10, the
- `@scope` had to be a case-sensitive match of the GitLab project's root namespace. This was
- problematic because the npm public registry does not allow uppercase letters. GitLab 13.10 relaxes
- this requirement and translates uppercase in the GitLab `@scope` to lowercase for npm. For
- example, a package `@MyScope/package-name` in GitLab becomes `@myscope/package-name` for npm.
-- The `package-name` can be whatever you want.
-
-NOTE:
-The value used for the `@scope` is the root of the project that will end up hosting the packages and not the root
-of the project with the source code of the package itself. For example, assume your package source code is located
-at `source-code-group/package-code` and deployed to a package registry inside `registries-group/registry-project`.
-In this case, the `@scope` needs to be `@registries-group` and not `@source-code-group`.
-
-For example, if your project is `https://gitlab.example.com/my-org/engineering-group/team-amazing/analytics`,
-the root namespace is `my-org`. When you publish a package, it must have `my-org` as the scope.
-
-| Project | Package | Supported |
-| ------------------- | -------------------- | --------- |
-| `my-org/bar` | `@my-org/bar` | Yes |
-| `my-org/bar/baz` | `@my-org/baz` | Yes |
-| `My-Org/Bar/baz` | `@my-org/Baz` | Yes |
-| `My-Org/Bar/baz` | `@My-Org/Baz` | Yes |
-| `my-org/bar/buz` | `@my-org/anything` | Yes |
-| `gitlab-org/gitlab` | `@gitlab-org/gitlab` | Yes |
-| `gitlab-org/gitlab` | `@foo/bar` | No |
-
-In GitLab, this regex validates all package names from all package managers:
-
-```plaintext
-/\A\@?(([\w\-\.\+]*)\/)*([\w\-\.]+)@?(([\w\-\.\+]*)\/)*([\w\-\.]*)\z/
+NPM_TOKEN=your_token npm publish
```
-This regex allows almost all of the characters that npm allows, with a few exceptions (for example, `~` is not allowed).
-
-The regex also allows for capital letters, while npm does not.
+Your package should now publish to the Package Registry.
-## Limitations
+## Publishing a package via a CI/CD pipeline
-When you update the path of a user or group, or transfer a subgroup or project,
-you must remove any npm packages first. You cannot update the root namespace
-of a project with npm packages. Make sure you update your `.npmrc` files to follow
-the naming convention and run `npm publish` if necessary.
+### Authenticating via the `.npmrc`
-## Publish an npm package
-
-Prerequisites:
-
-- [Authenticate](#authenticate-to-the-package-registry) to the Package Registry.
-- Set a [project-level npm endpoint](#use-the-gitlab-endpoint-for-npm-packages).
-
-To upload an npm package to your project, run this command:
+Create or edit the `.npmrc` file in the same directory as your `package.json` in a GitLab project. Include the following lines in the `.npmrc` file:
```shell
-npm publish
+@scope:registry=https://your_domain_name/api/v4/projects/your_project_id/packages/npm/
+//your_domain_name/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}
```
-To view the package, go to your project's **Packages and registries**.
+- Replace `@scope` with the [root level group](#naming-convention) of the project you're publishing to the package to.
+- The `${CI_PROJECT_ID}` and `${CI_JOB_TOKEN}` are [predefined variables](../../../ci/variables/predefined_variables.md) that are available in the pipeline and do not need to be replaced.
-You can also define `"publishConfig"` for your project in `package.json`. For example:
+### Publishing a package via a CI/CD pipeline
-```json
-{
- "publishConfig": {
- "@foo:registry": " https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/"
- }
-}
-```
-
-This forces the package to publish only to the specified registry.
-
-If you try to publish a package [with a name that already exists](#publishing-packages-with-the-same-name-or-version) within
-a given scope, you get a `403 Forbidden!` error.
-
-## Publish an npm package by using CI/CD
-
-Prerequisites:
-
-- [Authenticate](#authenticate-to-the-package-registry) to the Package Registry.
-- Set a [project-level npm endpoint](#use-the-gitlab-endpoint-for-npm-packages).
-- Your npm package name must be in the format of [`@scope/package-name`](#package-naming-convention).
- It must match exactly, including the case. This is different than the
- npm naming convention, but it is required to work with the GitLab Package Registry.
-
-To work with npm commands within [GitLab CI/CD](../../../ci/index.md), you can use
-`CI_JOB_TOKEN` in place of the personal access token or deploy token in your commands.
-
-An example `.gitlab-ci.yml` file for publishing npm packages:
+In the GitLab project that houses your `.npmrc` and `package.json`, edit or create a `.gitlab-ci.yml` file. For example:
```yaml
image: node:latest
@@ -262,143 +109,105 @@ deploy:
script:
- echo "//${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}">.npmrc
- npm publish
- environment: production
```
-See the
-[Publish npm packages to the GitLab Package Registry using semantic-release](../../../ci/examples/semantic-release.md)
-step-by-step guide and demo project for a complete example.
-
-## Configure the GitLab npm registry with Yarn 2
-
-You can get started with Yarn 2 by following the [Yarn documentation](https://yarnpkg.com/getting-started/install/).
+Your package should now publish to the Package Registry when the pipeline runs.
-To publish and install with the project-level npm endpoint, set the following configuration in
-`.yarnrc.yml`:
+## Install a package
-```yaml
-npmScopes:
- foo:
- npmRegistryServer: 'https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/'
- npmPublishRegistry: 'https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/'
-
-npmRegistries:
- //gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:
- npmAlwaysAuth: true
- npmAuthToken: '<your_token>'
-```
+If multiple packages have the same name and version, when you install a package, the most recently-published package is retrieved.
-For the instance-level npm endpoint, use this Yarn 2 configuration in `.yarnrc.yml`:
+You can install a package from a GitLab project or instance:
-```yaml
-npmScopes:
- foo:
- npmRegistryServer: 'https://gitlab.example.com/api/v4/packages/npm/'
-
-npmRegistries:
- //gitlab.example.com/api/v4/packages/npm/:
- npmAlwaysAuth: true
- npmAuthToken: '<your_token>'
-```
+- **Instance-level**: Use when you have many npm packages in different GitLab groups or in their own namespace.
+- **Project-level**: Use when you have few npm packages and they are not in the same GitLab group.
-In this configuration:
+### Install from the instance level
-- Replace `<your_token>` with your personal access token or deploy token.
-- Replace `<your_project_id>` with your project's ID, which you can find on the project's home page.
-- Replace `gitlab.example.com` with your domain name.
-- Your scope is `foo`, without `@`.
+WARNING:
+In order to install a package from the instance level, the package must have been published following the scoped [naming convention](#naming-convention).
-## Publishing packages with the same name or version
+1. Authenticate to the Package Registry
-You cannot publish a package if a package of the same name and version already exists.
-You must delete the existing package first.
+ If you would like to install a package from a private project, you will need to authenticate to the Package Registry. Skip this step if the project is not private.
-This rule has a different impact depending on the package name:
+ ```shell
+ npm config set -- //your_domain_name/api/v4/packages/npm/:_authToken=your_token
+ ```
-- For packages following the [naming convention](#package-naming-convention), you can't publish a
- package with a duplicate name and version to the root namespace.
-- For packages not following the [naming convention](#package-naming-convention), you can't publish
- a package with a duplicate name and version to the project you target with the upload.
+ - Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
+ - Replace `your_token` with a deploy token, group access token, project access token, or personal access token.
-This aligns with npmjs.org's behavior. However, npmjs.org does not ever let you publish
-the same version more than once, even if it has been deleted.
+1. Set the registry
-## `package.json` limitations
+ ```shell
+ npm config set @scope:registry https://your_domain_name.com/api/v4/packages/npm/
+ ```
-You can't publish a package if its `package.json` file exceeds 20,000 characters.
+ - Replace `@scope` with the [root level group](#naming-convention) of the project you're installing to the package from.
+ - Replace `your_domain_name` with your domain name, for example `gitlab.com`.
+ - Replace `your_token` with a deploy token, group access token, project access token, or personal access token.
-## Install a package
+1. Install the package
-npm packages are commonly-installed by using the `npm` or `yarn` commands
-in a JavaScript project. You can install a package from the scope of a project or instance.
+ ```shell
+ npm install @scope/my-package
+ ```
-If multiple packages have the same name and version, when you install a package, the most recently-published package is retrieved.
+### Install from the project level
-1. Set the URL for scoped packages.
+1. Authenticate to the Package Registry
- For [instance-level endpoints](#use-the-gitlab-endpoint-for-npm-packages) run:
+ If you would like to install a package from a private project, you will need to authenticate to the Package Registry. Skip this step if the project is not private.
```shell
- npm config set @foo:registry https://gitlab.example.com/api/v4/packages/npm/
+ npm config set -- //your_domain_name/api/v4/projects/your_project_id/packages/npm/:_authToken=your_token
```
- - Replace `@foo` with your scope.
- - Replace `gitlab.example.com` with your domain name.
+ - Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
+ - Replace `your_project_id` is your project ID, found on the project's home page.
+ - Replace `your_token` with a deploy token, group access token, project access token, or personal access token.
- For [project-level endpoints](#use-the-gitlab-endpoint-for-npm-packages) run:
+1. Set the registry
```shell
- npm config set @foo:registry https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/
+ npm config set @scope:registry=https://your_domain_name/api/v4/projects/your_project_id/packages/npm/
```
- - Replace `@foo` with your scope.
- - Replace `gitlab.example.com` with your domain name.
- - Replace `<your_project_id>` with your project ID, found on the project's home page.
+ - Replace `@scope` with the [root level group](#naming-convention) of the project you're installing to the package from.
+ - Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
+ - Replace `your_project_id` is your project ID, found on the project's home page.
-1. Ensure [authentication](#authenticate-to-the-package-registry) is configured.
-
-1. To install a package in your project, run:
+1. Install the package
```shell
- npm install @my-scope/my-package
+ npm install @scope/my-package
```
- Or if you're using Yarn:
+## Helpful hints
- ```shell
- yarn add @my-scope/my-package
- ```
+### Package forwarding to npmjs.com
-In [GitLab 12.9 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/55344),
-when an npm package is not found in the Package Registry, the request is forwarded to [npmjs.com](https://www.npmjs.com/).
+When an npm package is not found in the Package Registry, the request is forwarded to [npmjs.com](https://www.npmjs.com/).
Administrators can disable this behavior in the [Continuous Integration settings](../../admin_area/settings/continuous_integration.md).
+Group owners can disable this behavior in the group Packages and Registries settings.
+
### Install npm packages from other organizations
You can route package requests to organizations and users outside of GitLab.
-To do this, add lines to your `.npmrc` file. Replace `my-org` with the namespace or group that owns your project's repository,
+To do this, add lines to your `.npmrc` file. Replace `@my-other-org` with the namespace or group that owns your project's repository,
and use your organization's URL. The name is case-sensitive and must match the name of your group or namespace exactly.
-Use environment variables to set up your tokens: `export MY_TOKEN="<your token>"`.
-
```shell
-@foo:registry=https://gitlab.example.com/api/v4/packages/npm/
-//gitlab.example.com/api/v4/packages/npm/:_authToken=${MY_TOKEN}
-//gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken=${MY_TOKEN}
-
-@my-other-org:registry=https://gitlab.example.com/api/v4/packages/npm/
-//gitlab.example.com/api/v4/packages/npm/:_authToken=${MY_TOKEN}
-//gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken=${MY_TOKEN}
+@scope:registry=https://my_domain_name.com/api/v4/packages/npm/
+@my-other-org:registry=https://my_domain_name.example.com/api/v4/packages/npm/
```
### npm metadata
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11867) in GitLab 12.6.
-> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) from GitLab Premium to GitLab Free in 13.3.
-> - [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/330929) in GitLab 14.5.
-
The GitLab Package Registry exposes the following attributes to the npm client.
These are similar to the [abbreviated metadata format](https://github.com/npm/registry/blob/9e368cf6aaca608da5b2c378c0d53f475298b916/docs/responses/package-metadata.md#abbreviated-metadata-format):
@@ -417,10 +226,7 @@ These are similar to the [abbreviated metadata format](https://github.com/npm/re
- `engines`
- `_hasShrinkwrap`
-## Add npm distribution tags
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9425) in GitLab 12.8.
-> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) from GitLab Premium to GitLab Free in 13.3.
+### Add npm distribution tags
You can add [distribution tags](https://docs.npmjs.com/cli/dist-tag/) to newly-published packages.
Tags are optional and can be assigned to only one package at a time.
@@ -443,87 +249,46 @@ View [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/258835) for deta
Due to a bug in npm 6.9.0, deleting distribution tags fails. Make sure your npm version is 6.9.1 or later.
-## Troubleshooting
-
-When troubleshooting npm issues, first run the same command with the `--verbose` flag to confirm
-what registry you are hitting.
-
-To improve performance, npm caches files related to a package. Note that npm doesn't remove data by
-itself. The cache grows as new packages are installed. If you encounter issues, clear the cache with
-this command:
-
-```shell
-npm cache clean --force
-```
-
-### Error running Yarn with the Package Registry for npm registry
-
-If you are using [Yarn](https://classic.yarnpkg.com/en/) with the npm registry, you may get
-an error message like:
-
-```shell
-yarn install v1.15.2
-warning package.json: No license field
-info No lockfile found.
-warning XXX: No license field
-[1/4] 🔠Resolving packages...
-[2/4] 🚚 Fetching packages...
-error An unexpected error occurred: "https://gitlab.example.com/api/v4/projects/XXX/packages/npm/XXX/XXX/-/XXX/XXX-X.X.X.tgz: Request failed \"404 Not Found\"".
-info If you think this is a bug, please open a bug report with the information provided in "/Users/XXX/gitlab-migration/module-util/yarn-error.log".
-info Visit https://classic.yarnpkg.com/en/docs/cli/install for documentation about this command
-```
-
-In this case, try adding this to your `.npmrc` file (and replace `<your_token>`
-with your personal access token or deploy token):
+### Supported CLI commands
-```plaintext
-//gitlab.example.com/api/v4/projects/:_authToken=<your_token>
-```
+The GitLab npm repository supports the following commands for the npm CLI (`npm`) and yarn CLI
+(`yarn`):
-You can also use `yarn config` instead of `npm config` when setting your auth-token dynamically:
+- `npm install`: Install npm packages.
+- `npm publish`: Publish an npm package to the registry.
+- `npm dist-tag add`: Add a dist-tag to an npm package.
+- `npm dist-tag ls`: List dist-tags for a package.
+- `npm dist-tag rm`: Delete a dist-tag.
+- `npm ci`: Install npm packages directly from your `package-lock.json` file.
+- `npm view`: Show package metadata.
-```shell
-yarn config set '//gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken' "<your_token>"
-yarn config set '//gitlab.example.com/api/v4/packages/npm/:_authToken' "<your_token>"
-```
+## Troubleshooting
### `npm publish` targets default npm registry (`registry.npmjs.org`)
Ensure that your package scope is set consistently in your `package.json` and `.npmrc` files.
-For example, if your project name in GitLab is `foo/my-package`, then your `package.json` file
+For example, if your project name in GitLab is `@scope/my-package`, then your `package.json` file
should look like:
```json
{
- "name": "@foo/my-package",
- "version": "1.0.0",
- "description": "Example package for GitLab npm registry"
+ "name": "@scope/my-package"
}
```
And the `.npmrc` file should look like:
-```ini
-//gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken=<your_token>
-//gitlab.example.com/api/v4/packages/npm/:_authToken=<your_token>
-@foo:registry=https://gitlab.example.com/api/v4/packages/npm/
-```
-
-### `npm install` returns `Error: Failed to replace env in config: ${npm_TOKEN}`
-
-You do not need a token to run `npm install` unless your project is private. The token is only required to publish. If the `.npmrc` file was checked in with a reference to `$npm_TOKEN`, you can remove it. If you prefer to leave the reference in, you must set a value prior to running `npm install` or set the value by using [GitLab CI/CD variables](../../../ci/variables/index.md):
-
```shell
-NPM_TOKEN=<your_token> npm install
+@scope:registry=https://your_domain_name/api/v4/projects/your_project_id/packages/npm/
+//your_domain_name/api/v4/projects/your_project_id/packages/npm/:_authToken="${NPM_TOKEN}"
```
### `npm install` returns `npm ERR! 403 Forbidden`
If you get this error, ensure that:
-- The Package Registry is enabled in your project settings. Although the Package Registry is enabled
- by default, it's possible to [disable it](../package_registry/index.md#disable-the-package-registry).
+- The Package Registry is enabled in your project settings. Although the Package Registry is enabled by default, it's possible to [disable it](../package_registry/index.md#disable-the-package-registry).
- Your token is not expired and has appropriate permissions.
- A package with the same name or version doesn't already exist within the given scope.
- The scoped packages URL includes a trailing slash:
@@ -534,30 +299,25 @@ If you get this error, ensure that:
If you get this error, one of the following problems could be causing it.
-#### Package name does not meet the naming convention
+### Package name does not meet the naming convention
-Your package name may not meet the
-[`@scope/package-name` package naming convention](#package-naming-convention).
+Your package name may not meet the [`@scope/package-name` package naming convention](#naming-convention).
-Ensure the name meets the convention exactly, including the case.
-Then try to publish again.
+Ensure the name meets the convention exactly, including the case. Then try to publish again.
-#### Package already exists
+### Package already exists
-Your package has already been published to another project in the same
-root namespace and therefore cannot be published again using the same name.
+Your package has already been published to another project in the same root namespace and therefore cannot be published again using the same name.
-This is also true even if the prior published package shares the same name,
-but not the version.
+This is also true even if the prior published package shares the same name, but not the version.
-#### Package JSON file is too large
+### Package JSON file is too large
-Make sure that your `package.json` file does not [exceed `20,000` characters](#packagejson-limitations).
+Make sure that your `package.json` file does not exceed `20,000` characters.
### `npm publish` returns `npm ERR! 500 Internal Server Error - PUT`
-This is a [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/238950) in GitLab
-13.3.x and later. The error in the logs will appear as:
+This is a [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/238950) in GitLab 13.3.x and later. The error in the logs will appear as:
```plaintext
>NoMethodError - undefined method `preferred_language' for #<Rack::Response
@@ -572,22 +332,7 @@ This might be accompanied by another error:
This is usually a permissions issue with either:
- `'packages_storage_path'` default `/var/opt/gitlab/gitlab-rails/shared/packages/`.
-- The remote bucket if [object storage](../../../administration/packages/index.md#using-object-storage)
+- The remote bucket if [object storage](../../../administration/packages/index.md#use-object-storage)
is used.
In the latter case, ensure the bucket exists and GitLab has write access to it.
-
-## Supported CLI commands
-
-The GitLab npm repository supports the following commands for the npm CLI (`npm`) and yarn CLI
-(`yarn`):
-
-- `npm install`: Install npm packages.
-- `npm publish`: Publish an npm package to the registry.
-- `npm dist-tag add`: Add a dist-tag to an npm package.
-- `npm dist-tag ls`: List dist-tags for a package.
-- `npm dist-tag rm`: Delete a dist-tag.
-- `npm ci`: Install npm packages directly from your `package-lock.json` file.
-- `npm view`: Show package metadata.
-- `yarn add`: Install an npm package.
-- `yarn update`: Update your dependencies.
diff --git a/doc/user/packages/nuget_repository/img/visual_studio_adding_nuget_source.png b/doc/user/packages/nuget_repository/img/visual_studio_adding_nuget_source.png
deleted file mode 100644
index 7397403f4bf..00000000000
--- a/doc/user/packages/nuget_repository/img/visual_studio_adding_nuget_source.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/packages/nuget_repository/img/visual_studio_nuget_source_added.png b/doc/user/packages/nuget_repository/img/visual_studio_nuget_source_added.png
deleted file mode 100644
index 8c14a14e304..00000000000
--- a/doc/user/packages/nuget_repository/img/visual_studio_nuget_source_added.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/packages/nuget_repository/index.md b/doc/user/packages/nuget_repository/index.md
index 956202bb990..540db463f0a 100644
--- a/doc/user/packages/nuget_repository/index.md
+++ b/doc/user/packages/nuget_repository/index.md
@@ -118,27 +118,25 @@ A project-level endpoint is also required to install NuGet packages from a proje
To use the [project-level](#use-the-gitlab-endpoint-for-nuget-packages) NuGet endpoint, add the Package Registry as a source with Visual Studio:
1. Open [Visual Studio](https://visualstudio.microsoft.com/vs/).
-1. In Windows, select **File > Options**. On macOS, select **Visual Studio > Preferences**.
+1. In Windows, select **Tools > Options**. On macOS, select **Visual Studio > Preferences**.
1. In the **NuGet** section, select **Sources** to view a list of all your NuGet sources.
1. Select **Add**.
1. Complete the following fields:
- **Name**: Name for the source.
- - **Location**: `https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/nuget/index.json`,
+ - **Source**: `https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/nuget/index.json`,
where `<your_project_id>` is your project ID, and `gitlab.example.com` is
your domain name.
- - **Username**: Your GitLab username or deploy token username.
- - **Password**: Your personal access token or deploy token.
-
- ![Visual Studio Adding a NuGet source](img/visual_studio_adding_nuget_source.png)
1. Select **Save**.
+1. When you access the package, you must enter your **Username** and **Password**:
-The source is displayed in your list.
+ - **Username**: Your GitLab username or deploy token username.
+ - **Password**: Your personal access token or deploy token.
-![Visual Studio NuGet source added](img/visual_studio_nuget_source_added.png)
+The source is displayed in your list.
-If you get a warning, ensure that the **Location**, **Username**, and
+If you get a warning, ensure that the **Source**, **Username**, and
**Password** are correct.
#### Group-level endpoint
@@ -148,27 +146,25 @@ To install a package from a group, use a group-level endpoint.
To use the [group-level](#use-the-gitlab-endpoint-for-nuget-packages) NuGet endpoint, add the Package Registry as a source with Visual Studio:
1. Open [Visual Studio](https://visualstudio.microsoft.com/vs/).
-1. In Windows, select **File > Options**. On macOS, select **Visual Studio > Preferences**.
+1. In Windows, select **Tools > Options**. On macOS, select **Visual Studio > Preferences**.
1. In the **NuGet** section, select **Sources** to view a list of all your NuGet sources.
1. Select **Add**.
1. Complete the following fields:
- **Name**: Name for the source.
- - **Location**: `https://gitlab.example.com/api/v4/groups/<your_group_id>/-/packages/nuget/index.json`,
+ - **Source**: `https://gitlab.example.com/api/v4/groups/<your_group_id>/-/packages/nuget/index.json`,
where `<your_group_id>` is your group ID, and `gitlab.example.com` is
your domain name.
- - **Username**: Your GitLab username or deploy token username.
- - **Password**: Your personal access token or deploy token.
-
- ![Visual Studio Adding a NuGet source](img/visual_studio_adding_nuget_source.png)
1. Select **Save**.
+1. When you access the package, you must enter your **Username** and **Password**.
-The source is displayed in your list.
+ - **Username**: Your GitLab username or deploy token username.
+ - **Password**: Your personal access token or deploy token.
-![Visual Studio NuGet source added](img/visual_studio_nuget_source_added.png)
+The source is displayed in your list.
-If you get a warning, ensure that the **Location**, **Username**, and
+If you get a warning, ensure that the **Source**, **Username**, and
**Password** are correct.
### Add a source with the .NET CLI
diff --git a/doc/user/packages/package_registry/index.md b/doc/user/packages/package_registry/index.md
index 1aeb98fd48a..caa305999c5 100644
--- a/doc/user/packages/package_registry/index.md
+++ b/doc/user/packages/package_registry/index.md
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) from GitLab Premium to GitLab Free in 13.3.
With the GitLab Package Registry, you can use GitLab as a private or public registry for a variety
-of [supported package managers](#supported-package-managers).
+of [supported package managers](supported_package_managers.md).
You can publish and share packages, which can be consumed as a dependency in downstream projects.
## Package workflows
@@ -60,6 +60,9 @@ For most package types, the following credential types are valid:
allows access to packages in the project running the job for the users running the pipeline.
Access to other external projects can be configured.
+- If your organization uses two factor authentication (2FA), you must use a personal access token with the scope set to `api`.
+- If you are publishing a package via CI/CD pipelines, you must use a CI job token.
+
NOTE:
If you have not activated the "Packages" feature for your project at **Settings > General > Project features**, you will receive a 403 Forbidden response.
Accessing package registry via deploy token is not available when external authorization is enabled.
@@ -78,7 +81,7 @@ Learn more about using the GitLab Package Registry with CI/CD:
- [Conan](../conan_repository/index.md#publish-a-conan-package-by-using-cicd)
- [Generic](../generic_packages/index.md#publish-a-generic-package-by-using-cicd)
- [Maven](../maven_repository/index.md#create-maven-packages-with-gitlab-cicd)
-- [npm](../npm_registry/index.md#publish-an-npm-package-by-using-cicd)
+- [npm](../npm_registry/index.md#publishing-a-package-via-a-cicd-pipeline)
- [NuGet](../nuget_repository/index.md#publish-a-nuget-package-by-using-cicd)
- [PyPI](../pypi_repository/index.md#authenticate-with-a-ci-job-token)
- [RubyGems](../rubygems_registry/index.md#authenticate-with-a-ci-job-token)
@@ -117,50 +120,40 @@ The **Packages and registries > Package Registry** entry is removed from the sid
[Project-level permissions](../../permissions.md)
determine actions such as downloading, pushing, or deleting packages.
-The visibility of the Package Registry is independent of the repository and can't be controlled from
+The visibility of the Package Registry is independent of the repository and can be controlled from
your project's settings. For example, if you have a public project and set the repository visibility
-to **Only Project Members**, the Package Registry is then public. However, disabling the Package
+to **Only Project Members**, the Package Registry is then public. Disabling the Package
Registry disables all Package Registry operations.
-[GitLab-#329253](https://gitlab.com/gitlab-org/gitlab/-/issues/329253)
-proposes adding the ability to control Package Registry visibility from the UI.
-
-| | | Anonymous<br/>(everyone on internet) | Guest | Reporter, Developer, Maintainer, Owner |
-| -------------------- | --------------------- | --------- | ----- | ------------------------------------------ |
-| Public project with Package Registry enabled | View Package Registry <br/> and pull packages | Yes | Yes | Yes |
-| Internal project with Package Registry enabled | View Package Registry <br/> and pull packages | No | Yes | Yes |
-| Private project with Package Registry enabled | View Package Registry <br/> and pull packages | No | No | Yes |
-| Any project with Package Registry disabled | All operations on Package Registry | No | No | No |
-
-## Supported package managers
+| Project visibility | Action | [Role](../../permissions.md#roles) required |
+|--------------------|-----------------------|---------------------------------------------------------|
+| Public | View Package Registry | `n/a`, everyone on the internet can perform this action |
+| Public | Publish a package | Developer or higher |
+| Public | Pull a package | `n/a`, everyone on the internet can perform this action |
+| Internal | View Package Registry | Guest or higher |
+| Internal | Publish a package | Developer or higher |
+| Internal | Pull a package | Guest or higher(1) |
+| Private | View Package Registry | Reporter or higher |
+| Private | Publish a package | Developer or higher |
+| Private | Pull a package | Reporter or higher(1) |
-WARNING:
-Not all package manager formats are ready for production use. To view each format's status, see the
-table's **Status** column.
+### Allow anyone to pull from Package Registry
-The Package Registry supports the following formats:
+> Introduced in GitLab 15.7 [with a flag](../../../administration/feature_flags.md) named `package_registry_access_level`. Enabled by default.
-| Package type | GitLab version | Status |
-| ------------ | -------------- |------- |
-| [Maven](../maven_repository/index.md) | 11.3+ | GA |
-| [npm](../npm_registry/index.md) | 11.7+ | GA |
-| [NuGet](../nuget_repository/index.md) | 12.8+ | GA |
-| [PyPI](../pypi_repository/index.md) | 12.10+ | GA |
-| [Generic packages](../generic_packages/index.md) | 13.5+ | GA |
-| [Composer](../composer_repository/index.md) | 13.2+ | [Beta](https://gitlab.com/groups/gitlab-org/-/epics/6817) |
-| [Conan](../conan_repository/index.md) | 12.6+ | [Beta](https://gitlab.com/groups/gitlab-org/-/epics/6816) |
-| [Helm](../helm_repository/index.md) | 14.1+ | [Beta](https://gitlab.com/groups/gitlab-org/-/epics/6366) |
-| [Debian](../debian_repository/index.md) | 14.2+ | [Alpha](https://gitlab.com/groups/gitlab-org/-/epics/6057) |
-| [Go](../go_proxy/index.md) | 13.1+ | [Alpha](https://gitlab.com/groups/gitlab-org/-/epics/3043) |
-| [Ruby gems](../rubygems_registry/index.md) | 13.10+ | [Alpha](https://gitlab.com/groups/gitlab-org/-/epics/3200) |
+FLAG:
+On self-managed GitLab, by default this feature is available. To disable it,
+ask an administrator to [disable the feature flag](../../../administration/feature_flags.md) named `package_registry_access_level`.
-[Status](../../../policy/alpha-beta-support.md):
+If you want to allow anyone (everyone on the internet) to pull from the Package Registry, no matter what the project visibility is, you can use the additional toggle `Allow anyone to pull from Package Registry` that appears when the project visibility is Private or Internal.
-- Alpha: behind a feature flag and not officially supported.
-- Beta: several known issues that may prevent expected use.
-- GA (Generally Available): ready for production use at any scale.
+Several known issues exist when you allow anyone to pull from the Package Registry:
-You can also use the [API](../../../api/packages.md) to administer the Package Registry.
+- Project-level endpoints are supported. Group-level and instance-level endpoints are not supported. Support for group-level endpoints is proposed in [issue 383537](https://gitlab.com/gitlab-org/gitlab/-/issues/383537).
+- It does not work with the [Composer](../composer_repository/index.md#install-a-composer-package), because Composer only has a group endpoint.
+- It does not work with the [Debian](../debian_repository/index.md#install-a-package) repository. Support for the Debian repository is proposed in [issue 385258](https://gitlab.com/gitlab-org/gitlab/-/issues/385258).
+- It does not work with the [Ruby gems](../rubygems_registry/index.md#install-a-ruby-gem) repository. Support for the Ruby gems repository is proposed in [issue 385259](https://gitlab.com/gitlab-org/gitlab/-/issues/385259).
+- It will work with Conan, but using [`conan search`](../conan_repository/index.md#search-for-conan-packages-in-the-package-registry) does not work.
## Accepting contributions
@@ -170,8 +163,8 @@ guides you through the process.
<!-- vale gitlab.Spelling = NO -->
-| Format | Status |
-| ------ | ------ |
+| Format | Status |
+| --------- | ------------------------------------------------------------- |
| Chef | [#36889](https://gitlab.com/gitlab-org/gitlab/-/issues/36889) |
| CocoaPods | [#36890](https://gitlab.com/gitlab-org/gitlab/-/issues/36890) |
| Conda | [#36891](https://gitlab.com/gitlab-org/gitlab/-/issues/36891) |
diff --git a/doc/user/packages/package_registry/supported_hash_types.md b/doc/user/packages/package_registry/supported_hash_types.md
new file mode 100644
index 00000000000..6d7dbf87468
--- /dev/null
+++ b/doc/user/packages/package_registry/supported_hash_types.md
@@ -0,0 +1,25 @@
+---
+stage: Package
+group: Package Registry
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Supported hash types **(FREE)**
+
+Hash values are used to ensure you are using the correct package. You can view these values in the user interface or with the [API](../../../api/packages.md).
+
+The Package Registry supports the following hash types:
+
+| Package type | Supported hashes |
+|--------------------------------------------------|----------------------------------|
+| [Maven](../maven_repository/index.md) | MD5, SHA1 |
+| [npm](../npm_registry/index.md) | SHA1 |
+| [NuGet](../nuget_repository/index.md) | not applicable |
+| [PyPI](../pypi_repository/index.md) | MD5, SHA256 |
+| [Generic packages](../generic_packages/index.md) | SHA256 |
+| [Composer](../composer_repository/index.md) | not applicable |
+| [Conan](../conan_repository/index.md) | MD5, SHA1 |
+| [Helm](../helm_repository/index.md) | not applicable |
+| [Debian](../debian_repository/index.md) | MD5, SHA1, SHA256 |
+| [Go](../go_proxy/index.md) | MD5, SHA1, SHA256 |
+| [Ruby gems](../rubygems_registry/index.md) | MD5, SHA1, SHA256 (gemspec only) |
diff --git a/doc/user/packages/package_registry/supported_package_managers.md b/doc/user/packages/package_registry/supported_package_managers.md
new file mode 100644
index 00000000000..75b8c95a0fa
--- /dev/null
+++ b/doc/user/packages/package_registry/supported_package_managers.md
@@ -0,0 +1,34 @@
+---
+stage: Package
+group: Package Registry
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Supported package managers **(FREE)**
+
+WARNING:
+Not all package manager formats are ready for production use.
+
+The Package Registry supports the following package manager types:
+
+| Package type | GitLab version | Status |
+| ------------------------------------------------ | -------------- | ---------------------------------------------------------- |
+| [Maven](../maven_repository/index.md) | 11.3+ | GA |
+| [npm](../npm_registry/index.md) | 11.7+ | GA |
+| [NuGet](../nuget_repository/index.md) | 12.8+ | GA |
+| [PyPI](../pypi_repository/index.md) | 12.10+ | GA |
+| [Generic packages](../generic_packages/index.md) | 13.5+ | GA |
+| [Composer](../composer_repository/index.md) | 13.2+ | [Beta](https://gitlab.com/groups/gitlab-org/-/epics/6817) |
+| [Conan](../conan_repository/index.md) | 12.6+ | [Beta](https://gitlab.com/groups/gitlab-org/-/epics/6816) |
+| [Helm](../helm_repository/index.md) | 14.1+ | [Beta](https://gitlab.com/groups/gitlab-org/-/epics/6366) |
+| [Debian](../debian_repository/index.md) | 14.2+ | [Alpha](https://gitlab.com/groups/gitlab-org/-/epics/6057) |
+| [Go](../go_proxy/index.md) | 13.1+ | [Alpha](https://gitlab.com/groups/gitlab-org/-/epics/3043) |
+| [Ruby gems](../rubygems_registry/index.md) | 13.10+ | [Alpha](https://gitlab.com/groups/gitlab-org/-/epics/3200) |
+
+[Status](../../../policy/alpha-beta-support.md):
+
+- Alpha: behind a feature flag and not officially supported.
+- Beta: several known issues that may prevent expected use.
+- GA (Generally Available): ready for production use at any scale.
+
+You can also use the [API](../../../api/packages.md) to administer the Package Registry.
diff --git a/doc/user/packages/pypi_repository/index.md b/doc/user/packages/pypi_repository/index.md
index f6ed9654882..0e2fc7ca7da 100644
--- a/doc/user/packages/pypi_repository/index.md
+++ b/doc/user/packages/pypi_repository/index.md
@@ -283,6 +283,32 @@ characters are removed.
A `pip install` request for `my.package` looks for packages that match any of
the three characters, such as `my-package`, `my_package`, and `my....package`.
+## Using `requirements.txt`
+
+If you want pip to access your private registry, add the `--extra-index-url` parameter along with the URL for your registry to your `requirements.txt` file.
+
+```plaintext
+--extra-index-url https://gitlab.example.com/api/v4/projects/<project_id>/packages/pypi/simple
+package-name==1.0.0
+```
+
+If this is a private registry, you can authenticate in a couple of ways. For example:
+
+- Using your `requirements.txt` file:
+
+```plaintext
+--extra-index-url https://__token__:<your_personal_token>@gitlab.example.com/api/v4/projects/<project_id>/packages/pypi/simple
+package-name==1.0.0
+```
+
+- Using a `~/.netrc` file:
+
+```plaintext
+machine gitlab.example.com
+login __token__
+password <your_personal_token>
+```
+
## Troubleshooting
To improve performance, the pip command caches files related to a package. Note that pip doesn't remove data by
@@ -293,6 +319,18 @@ this command:
pip cache purge
```
+### Multiple `index-url` or `extra-index-url` parameters
+
+You can define multiple `index-url` and `extra-index-url` parameters.
+
+If you use the same domain name (such as `gitlab.example.com`) multiple times with different authentication
+tokens, `pip` may not be able to find your packages. This problem is due to how `pip`
+[registers and stores your tokens](https://github.com/pypa/pip/pull/10904#issuecomment-1126690115) during commands executions.
+
+To workaround this issue, you can use a [group deploy token](../../project/deploy_tokens/index.md) with the
+scope `read_package_registry` from a common parent group for all projects or groups targeted by the
+`index-url` and `extra-index-url` values.
+
## Supported CLI commands
The GitLab PyPI repository supports the following CLI commands:
diff --git a/doc/user/packages/terraform_module_registry/index.md b/doc/user/packages/terraform_module_registry/index.md
index 2b99ff807ec..9b09d846034 100644
--- a/doc/user/packages/terraform_module_registry/index.md
+++ b/doc/user/packages/terraform_module_registry/index.md
@@ -50,7 +50,7 @@ error `{"error":"404 Not Found"}`.
Example request using a personal access token:
```shell
-curl --header "PRIVATE-TOKEN: <your_access_token>" \
+curl --fail-with-body --header "PRIVATE-TOKEN: <your_access_token>" \
--upload-file path/to/file.tgz \
"https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/terraform/modules/my-module/my-system/0.0.1/file"
```
@@ -66,7 +66,7 @@ Example response:
Example request using a deploy token:
```shell
-curl --header "DEPLOY-TOKEN: <deploy_token>" \
+curl --fail-with-body --header "DEPLOY-TOKEN: <deploy_token>" \
--upload-file path/to/file.tgz \
"https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/terraform/modules/my-module/my-system/0.0.1/file"
```
@@ -127,14 +127,14 @@ upload:
script:
- TERRAFORM_MODULE_NAME=$(echo "${TERRAFORM_MODULE_NAME}" | tr " _" -) # module-name must not have spaces or underscores, so translate them to hyphens
- tar -vczf ${TERRAFORM_MODULE_NAME}-${TERRAFORM_MODULE_SYSTEM}-${TERRAFORM_MODULE_VERSION}.tgz -C ${TERRAFORM_MODULE_DIR} --exclude=./.git .
- - 'curl --location --header "JOB-TOKEN: ${CI_JOB_TOKEN}"
+ - 'curl --fail-with-body --location --header "JOB-TOKEN: ${CI_JOB_TOKEN}"
--upload-file ${TERRAFORM_MODULE_NAME}-${TERRAFORM_MODULE_SYSTEM}-${TERRAFORM_MODULE_VERSION}.tgz
${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/terraform/modules/${TERRAFORM_MODULE_NAME}/${TERRAFORM_MODULE_SYSTEM}/${TERRAFORM_MODULE_VERSION}/file'
rules:
- if: $CI_COMMIT_TAG
```
-To trigger this upload job, add a Git tag to your commit. Ensure the tag follows the [Semantic Versioning Specification](https://semver.org/) that Terraform requires. The `rules:if: $CI_COMMIT_TAG` ensures that only tagged commits to your repo trigger the module upload job.
+To trigger this upload job, add a Git tag to your commit. Ensure the tag follows the [Semantic Versioning Specification](https://semver.org/) that Terraform requires. The `rules:if: $CI_COMMIT_TAG` ensures that only tagged commits to your repository trigger the module upload job.
For other ways to control jobs in your CI/CD pipeline, refer to the [`.gitlab-ci.yml`](../../../ci/yaml/index.md) keyword reference.
## Example projects
diff --git a/doc/user/packages/workflows/build_packages.md b/doc/user/packages/workflows/build_packages.md
index ec971195ea9..eab1e4392e3 100644
--- a/doc/user/packages/workflows/build_packages.md
+++ b/doc/user/packages/workflows/build_packages.md
@@ -302,7 +302,7 @@ The npm version is shown in the output:
```
1. Enter responses to the questions. Ensure the **package name** follows
- the [naming convention](../npm_registry/index.md#package-naming-convention) and is scoped to the project or group where the registry exists.
+ the [naming convention](../npm_registry/index.md#naming-convention) and is scoped to the project or group where the registry exists.
## Yarn
@@ -334,7 +334,7 @@ The Yarn version is shown in the output:
```
1. Enter responses to the questions. Ensure the **package name** follows
- the [naming convention](../npm_registry/index.md#package-naming-convention) and is scoped to the
+ the [naming convention](../npm_registry/index.md#naming-convention) and is scoped to the
project or group where the registry exists.
A `package.json` file is created.
diff --git a/doc/user/packages/workflows/project_registry.md b/doc/user/packages/workflows/project_registry.md
index df4b087f6d5..371ab83a4fb 100644
--- a/doc/user/packages/workflows/project_registry.md
+++ b/doc/user/packages/workflows/project_registry.md
@@ -50,15 +50,15 @@ If you're using npm, create an `.npmrc` file. Add the appropriate URL for publis
packages to your project. Finally, add a section to your `package.json` file.
Follow the instructions in the
-[GitLab Package Registry npm documentation](../npm_registry/index.md#authenticate-to-the-package-registry). After
+[GitLab Package Registry npm documentation](../npm_registry/index.md#authentication-to-the-package-registry). After
you do this, you can publish your npm package to your project using `npm publish`, as described in the
-[publishing packages](../npm_registry/index.md#publish-an-npm-package) section.
+[publishing packages](../npm_registry/index.md#publish-to-gitlab-package-registry) section.
### Maven
If you are using Maven, you update your `pom.xml` file with distribution sections. These updates include the
-appropriate URL for your project, as described in the [GitLab Maven Repository documentation](../maven_repository/index.md#project-level-maven-endpoint).
-Then, you need to add a `settings.xml` file and [include your access token](../maven_repository/index.md#authenticate-with-a-personal-access-token-in-maven).
+appropriate URL for your project, as described in the [GitLab Maven Repository documentation](../maven_repository/index.md#naming-convention).
+Then, you need to add a `settings.xml` file and [include your access token](../maven_repository/index.md#authenticate-to-the-package-registry).
Now you can [publish Maven packages](../maven_repository/index.md#publish-a-package) to your project.
### Conan
diff --git a/doc/user/packages/yarn_repository/index.md b/doc/user/packages/yarn_repository/index.md
new file mode 100644
index 00000000000..7e2f45019cd
--- /dev/null
+++ b/doc/user/packages/yarn_repository/index.md
@@ -0,0 +1,248 @@
+---
+stage: Package
+group: Package Registry
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Publish packages with Yarn
+
+Publish npm packages in your project's Package Registry using Yarn. Then install the
+packages whenever you need to use them as a dependency.
+
+Learn how to build a [yarn](../workflows/build_packages.md#yarn) package.
+
+You can get started with Yarn 2 by following the [Yarn documentation](https://yarnpkg.com/getting-started/install/).
+
+## Publish to GitLab Package Registry
+
+### Authentication to the Package Registry
+
+You need a token to publish a package. Different tokens are available depending on what you're trying to
+achieve. For more information, review the [guidance on tokens](../../../user/packages/package_registry/index.md#authenticate-with-the-registry).
+
+- If your organization uses two-factor authentication (2FA), you must use a personal access token with the scope set to `api`.
+- If you publish a package via CI/CD pipelines, you must use a CI job token.
+
+Create a token and save it to use later in the process.
+
+### Naming convention
+
+Depending on how you install the package, you may need to adhere to the naming convention.
+
+You can use one of two API endpoints to install packages:
+
+- **Instance-level**: Use when you have many npm packages in different GitLab groups or in their own namespace.
+- **Project-level**: Use when you have a few npm packages, and they are not in the same GitLab group.
+
+If you plan to install a package through the [project level](#install-from-the-project-level), you do not have to
+adhere to the naming convention.
+
+If you plan to install a package through the [instance level](#install-from-the-instance-level), then you must name
+your package with a [scope](https://docs.npmjs.com/misc/scope/). Scoped packages begin with a `@` and have the
+`@owner/package-name` format. You can set up the scope for your package in the `.yarnrc.yml` file and by using the
+`publishConfig` option in the `package.json`.
+
+- The value used for the `@scope` is the root of the project that hosts the packages and not the root
+ of the project with the package's source code. The scope should be lowercase.
+- The package name can be anything you want
+
+| Project URL | Package Registry in | Scope | Full package name |
+| ------------------------------------------------------- | ------------------- | --------- | ---------------------- |
+| `https://gitlab.com/my-org/engineering-group/analytics` | Analytics | `@my-org` | `@my-org/package-name` |
+
+### Configuring `.yarnrc.yml` to publish from the project level
+
+To publish with the project-level npm endpoint, set the following configuration in
+`.yarnrc.yml`:
+
+```yaml
+npmScopes:
+ foo:
+ npmRegistryServer: 'https://<your_domain>/api/v4/projects/<your_project_id>/packages/npm/'
+ npmPublishRegistry: 'https://<your_domain>/api/v4/projects/<your_project_id>/packages/npm/'
+
+npmRegistries:
+ //gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:
+ npmAlwaysAuth: true
+ npmAuthToken: '<your_token>'
+```
+
+In this configuration:
+
+- Replace `<your_domain>` with your domain name.
+- Replace `<your_project_id>` with your project's ID, which you can find on the project's home page.
+- Replace `<your_token>` with a deploy token, group access token, project access token, or personal access token.
+
+### Configuring `.yarnrc.yml` to publish from the instance level
+
+For the instance-level npm endpoint, use this Yarn 2 configuration in `.yarnrc.yml`:
+
+```yaml
+npmScopes:
+ <scope>:
+ npmRegistryServer: 'https://<your_domain>/api/v4/packages/npm/'
+
+npmRegistries:
+ //gitlab.example.com/api/v4/packages/npm/:
+ npmAlwaysAuth: true
+ npmAuthToken: '<your_token>'
+```
+
+In this configuration:
+
+- Replace `<your_domain>` with your domain name.
+- Your scope is `<scope>`, without `@`.
+- Replace `<your_token>` with a deploy token, group access token, project access token, or personal access token.
+
+### Publishing a package via the command line
+
+Publish a package:
+
+```shell
+npm publish
+```
+
+Your package should now publish to the Package Registry.
+
+### Publishing via a CI/CD pipeline
+
+In the GitLab project that houses your `yarnrc.yml`, edit or create a `.gitlab-ci.yml` file. For example:
+
+```yaml
+image: node:latest
+
+stages:
+ - deploy
+
+deploy:
+ stage: deploy
+ script:
+ - npm publish
+```
+
+Your package should now publish to the Package Registry when the pipeline runs.
+
+## Install a package
+
+If multiple packages have the same name and version, the most recently-published package is retrieved when you install a package.
+
+You can install a package from a GitLab project or instance:
+
+- **Instance-level**: Use when you have many npm packages in different GitLab groups or in their own namespace.
+- **Project-level**: Use when you have a few npm packages, and they are not in the same GitLab group.
+
+### Install from the instance level
+
+WARNING:
+You must use packages published with the scoped [naming convention](#naming-convention) when you install a package from the instance level.
+
+1. Authenticate to the Package Registry
+
+ If you install a package from a private project, you must authenticate to the Package Registry. Skip this step if the project is not private.
+
+ ```shell
+ npm config set -- //your_domain_name/api/v4/packages/npm/:_authToken=your_token
+ ```
+
+ - Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
+ - Replace `your_token` with a deploy token, group access token, project access token, or personal access token.
+
+1. Set the registry
+
+ ```shell
+ npm config set @scope:registry https://your_domain_name.com/api/v4/packages/npm/
+ ```
+
+ - Replace `@scope` with the [root level group](#naming-convention) of the project you're installing to the package from.
+ - Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
+ - Replace `your_token` with a deploy token, group access token, project access token, or personal access token.
+
+1. Install the package
+
+ ```shell
+ yarn add @scope/my-package
+ ```
+
+### Install from the project level
+
+1. Authenticate to the Package Registry
+
+ If you install a package from a private project, you must authenticate to the Package Registry. Skip this step if the project is not private.
+
+ ```shell
+ npm config set -- //your_domain_name/api/v4/projects/your_project_id/packages/npm/:_authToken=your_token
+ ```
+
+ - Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
+ - Replace `your_project_id` is your project ID, found on the project's home page.
+ - Replace `your_token` with a deploy token, group access token, project access token, or personal access token.
+
+1. Set the registry
+
+ ```shell
+ npm config set @scope:registry=https://your_domain_name/api/v4/projects/your_project_id/packages/npm/
+ ```
+
+ - Replace `@scope` with the [root level group](#naming-convention) of the project you're installing to the package from.
+ - Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
+ - Replace `your_project_id` is your project ID, found on the project's home page.
+
+1. Install the package
+
+ ```shell
+ yarn add @scope/my-package
+ ```
+
+## Helpful hints
+
+For full helpful hints information, refer to the [npm documentation](../npm_registry/index.md#helpful-hints).
+
+### Supported CLI commands
+
+The GitLab npm repository supports the following commands for the npm CLI (`npm`) and yarn CLI
+(`yarn`):
+
+- `npm install`: Install npm packages.
+- `npm publish`: Publish an npm package to the registry.
+- `npm dist-tag add`: Add a dist-tag to an npm package.
+- `npm dist-tag ls`: List dist-tags for a package.
+- `npm dist-tag rm`: Delete a dist-tag.
+- `npm ci`: Install npm packages directly from your `package-lock.json` file.
+- `npm view`: Show package metadata.
+- `yarn add`: Install an npm package.
+- `yarn update`: Update your dependencies.
+
+## Troubleshooting
+
+For full troubleshooting information, refer to the [npm documentation](../npm_registry/index.md#troubleshooting).
+
+### Error running Yarn with the Package Registry for the npm registry
+
+If you are using [Yarn](https://classic.yarnpkg.com/en/) with the npm registry, you may get
+an error message like:
+
+```shell
+yarn install v1.15.2
+warning package.json: No license field
+info No lockfile found.
+warning XXX: No license field
+[1/4] 🔠Resolving packages...
+[2/4] 🚚 Fetching packages...
+error An unexpected error occurred: "https://gitlab.example.com/api/v4/projects/XXX/packages/npm/XXX/XXX/-/XXX/XXX-X.X.X.tgz: Request failed \"404 Not Found\"".
+info If you think this is a bug, please open a bug report with the information provided in "/Users/XXX/gitlab-migration/module-util/yarn-error.log".
+info Visit https://classic.yarnpkg.com/en/docs/cli/install for documentation about this command
+```
+
+In this case, try adding this to your `.npmrc` file (and replace `<your_token>`
+with your personal access token or deploy token):
+
+```plaintext
+//gitlab.example.com/api/v4/projects/:_authToken=<your_token>
+```
+
+You can also use `yarn config` instead of `npm config` when setting your auth-token dynamically:
+
+```shell
+yarn config set '//gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken' "<your_token>"
+yarn config set '//gitlab.example.com/api/v4/packages/npm/:_authToken' "<your_token>"
+```
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 8e152a8c190..f3702b848fa 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -6,35 +6,35 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Permissions and roles **(FREE)**
-Users have different abilities depending on the role they have in a
-particular group or project. If a user is both in a project's group and the
-project itself, the highest role is used.
+When you add a user to a project or group, you assign them a role.
+The role determines which actions they can take in GitLab.
-On [public and internal projects](../api/projects.md#project-visibility-level), the Guest role
-(not to be confused with [Guest user](#free-guest-users)) is not enforced.
+If you add a user to both a project's group and the
+project itself, the higher role is used.
-When a member leaves a team's project, all the assigned [issues](project/issues/index.md) and
-[merge requests](project/merge_requests/index.md) are automatically unassigned.
+GitLab [administrators](../administration/index.md) have all permissions.
-GitLab [administrators](../administration/index.md) receive all permissions.
+## Roles
-To add or import a user, you can follow the
-[project members documentation](project/members/index.md).
+The available roles are:
-## Principles behind permissions
+- Guest (This role applies to [private and internal projects](../user/public_access.md) only.)
+- Reporter
+- Developer
+- Maintainer
+- Owner
-See our [product handbook on permissions](https://about.gitlab.com/handbook/product/gitlab-the-product/#permissions-in-gitlab).
+A user assigned the Guest role has the least permissions,
+and the Owner has the most.
-## Instance-wide user permissions
-
-By default, users can create top-level groups and change their
-usernames. A GitLab administrator can configure the GitLab instance to
-[modify this behavior](../administration/user_settings.md).
+By default, all users can create top-level groups and change their
+usernames. A GitLab administrator can [change this behavior](../administration/user_settings.md)
+for the GitLab instance.
## Project members permissions
-- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/219299) in GitLab 14.8, personal namespace owners appear with Owner role in new projects in their namespace. Introduced [with a flag](../administration/feature_flags.md) named `personal_project_owner_with_owner_access`. Disabled by default.
-- [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/351919) in GitLab 14.9. Feature flag `personal_project_owner_with_owner_access` [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/219299).
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/219299) in GitLab 14.8, personal namespace owners appear with Owner role in new projects in their namespace. Introduced [with a flag](../administration/feature_flags.md) named `personal_project_owner_with_owner_access`. Disabled by default.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/351919) in GitLab 14.9. Feature flag `personal_project_owner_with_owner_access` [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/219299).
A user's role determines what permissions they have on a project. The Owner role provides all permissions but is
available only:
@@ -79,14 +79,17 @@ The following table lists project permissions available for each role:
| [GitLab Pages](project/pages/index.md):<br>Manage | | | | ✓ | ✓ |
| [GitLab Pages](project/pages/index.md):<br>Manage GitLab Pages domains and certificates | | | | ✓ | ✓ |
| [GitLab Pages](project/pages/index.md):<br>Remove GitLab Pages | | | | ✓ | ✓ |
-| [Incident Management](../operations/incident_management/index.md):<br>View [alerts](../operations/incident_management/alerts.md) | | ✓ | ✓ | ✓ | ✓ |
| [Incident Management](../operations/incident_management/index.md):<br>Assign an alert | ✓ | ✓ | ✓ | ✓ | ✓ |
-| [Incident Management](../operations/incident_management/index.md):<br>[Change an alert status](../operations/incident_management/alerts.md#change-an-alerts-status) | | ✓ | ✓ | ✓ | ✓ |
-| [Incident Management](../operations/incident_management/index.md):<br>View [incident](../operations/incident_management/incidents.md) | ✓ | ✓ | ✓ | ✓ | ✓ |
-| [Incident Management](../operations/incident_management/index.md):<br>Create [incident](../operations/incident_management/incidents.md) | | ✓ | ✓ | ✓ | ✓ |
-| [Incident Management](../operations/incident_management/index.md):<br>View [on-call schedules](../operations/incident_management/oncall_schedules.md) | | ✓ | ✓ | ✓ | ✓ |
| [Incident Management](../operations/incident_management/index.md):<br>Participate in on-call rotation | ✓ | ✓ | ✓ | ✓ | ✓ |
+| [Incident Management](../operations/incident_management/index.md):<br>View [incident](../operations/incident_management/incidents.md) | ✓ | ✓ | ✓ | ✓ | ✓ |
+| [Incident Management](../operations/incident_management/index.md):<br>Change [alert status](../operations/incident_management/alerts.md#change-an-alerts-status) | | ✓ | ✓ | ✓ | ✓ |
+| [Incident Management](../operations/incident_management/index.md):<br>Change [incident severity](../operations/incident_management/manage_incidents.md#change-severity) | | ✓ | ✓ | ✓ | ✓ |
+| [Incident Management](../operations/incident_management/index.md):<br>Create [incident](../operations/incident_management/incidents.md) | | ✓ | ✓ | ✓ | ✓ |
+| [Incident Management](../operations/incident_management/index.md):<br>View [alerts](../operations/incident_management/alerts.md) | | ✓ | ✓ | ✓ | ✓ |
| [Incident Management](../operations/incident_management/index.md):<br>View [escalation policies](../operations/incident_management/escalation_policies.md) | | ✓ | ✓ | ✓ | ✓ |
+| [Incident Management](../operations/incident_management/index.md):<br>View [on-call schedules](../operations/incident_management/oncall_schedules.md) | | ✓ | ✓ | ✓ | ✓ |
+| [Incident Management](../operations/incident_management/index.md):<br>Change [incident escalation status](../operations/incident_management/manage_incidents.md#change-status) | | | ✓ | ✓ | ✓ |
+| [Incident Management](../operations/incident_management/index.md):<br>Change [incident escalation policy](../operations/incident_management/manage_incidents.md#change-escalation-policy) | | | ✓ | ✓ | ✓ |
| [Incident Management](../operations/incident_management/index.md):<br>Manage [on-call schedules](../operations/incident_management/oncall_schedules.md) | | | | ✓ | ✓ |
| [Incident Management](../operations/incident_management/index.md):<br>Manage [escalation policies](../operations/incident_management/escalation_policies.md) | | | | ✓ | ✓ |
| [Issue boards](project/issue_board.md):<br>Create or delete lists | | ✓ | ✓ | ✓ | ✓ |
@@ -161,7 +164,7 @@ The following table lists project permissions available for each role:
| [Projects](project/index.md):<br>Edit comments (posted by any user) | | | | ✓ | ✓ |
| [Projects](project/index.md):<br>Edit project badges | | | | ✓ | ✓ |
| [Projects](project/index.md):<br>Edit project settings | | | | ✓ | ✓ |
-| [Projects](project/index.md):<br>Export project | | | | ✓ | ✓ |
+| [Projects](project/index.md):<br>[Export project](project/settings/import_export.md) | | | | ✓ | ✓ |
| [Projects](project/index.md):<br>Manage [project access tokens](project/settings/project_access_tokens.md) (*11*) | | | | ✓ (*20*) | ✓ |
| [Projects](project/index.md):<br>Manage [Project Operations](../operations/index.md) | | | | ✓ | ✓ |
| [Projects](project/index.md):<br>Rename project | | | | ✓ | ✓ |
@@ -204,7 +207,7 @@ The following table lists project permissions available for each role:
| [Security dashboard](application_security/security_dashboard/index.md):<br>Use security dashboard | | | ✓ | ✓ | ✓ |
| [Security dashboard](application_security/security_dashboard/index.md):<br>View vulnerability | | | ✓ | ✓ | ✓ |
| [Security dashboard](application_security/security_dashboard/index.md):<br>View vulnerability findings in [dependency list](application_security/dependency_list/index.md) | | | ✓ | ✓ | ✓ |
-| [Tasks](tasks.md):<br>Create (*17*) | ✓ | ✓ | ✓ | ✓ | ✓ |
+| [Tasks](tasks.md):<br>Create (*17*) | | ✓ | ✓ | ✓ | ✓ |
| [Tasks](tasks.md):<br>Edit | | ✓ | ✓ | ✓ | ✓ |
| [Tasks](tasks.md):<br>Remove from issue | | ✓ | ✓ | ✓ | ✓ |
| [Tasks](tasks.md):<br>Delete (*21*) | | | | | ✓ |
@@ -218,7 +221,7 @@ The following table lists project permissions available for each role:
<!-- markdownlint-disable MD029 -->
1. On self-managed GitLab instances, guest users are able to perform this action only on
- public and internal projects (not on private projects). [External users](#external-users)
+ public and internal projects (not on private projects). [External users](admin_area/external_users.md)
must be given explicit access even if the project is internal. For GitLab.com, see the
[GitLab.com visibility settings](gitlab_com/index.md#visibility-settings).
2. Guest users can only view the [confidential issues](project/issues/confidential_issues.md) they created themselves or are assigned to.
@@ -327,29 +330,7 @@ This table shows granted privileges for jobs triggered by specific types of user
| Push source and LFS | | | | |
1. Only if the triggering user is not an external one.
-1. Only if the triggering user is a member of the project.
-
-### Wiki and issues
-
-Project features like [wikis](project/wiki/index.md) and issues can be hidden from users depending on
-which visibility level you select on project settings.
-
-- Disabled: disabled for everyone
-- Only team members: only team members can see even if your project is public or internal
-- Everyone with access: everyone can see depending on your project's visibility level
-- Everyone: enabled for everyone (only available for GitLab Pages)
-
-### Protected branches
-
-Additional restrictions can be applied on a per-branch basis with [protected branches](project/protected_branches.md).
-Additionally, you can customize permissions to allow or prevent project Developers or Maintainers
-from pushing to a protected branch. Read through the documentation on
-[protected branches](project/protected_branches.md) to learn more.
-
-### Value stream analytics permissions
-
-Find the current permissions on the value stream analytics dashboard, as described in
-[related documentation](analytics/value_stream_analytics.md#access-permissions-for-value-stream-analytics).
+1. Only if the triggering user is a member of the project. See also [Usage of private Docker images with `if-not-present` pull policy](http://docs.gitlabl.com/runner/security/index.html#usage-of-private-docker-images-with-if-not-present-pull-policy).
### File Locking permissions **(PREMIUM)**
@@ -380,6 +361,7 @@ The following table lists group permissions available for each role:
| Action | Guest | Reporter | Developer | Maintainer | Owner |
|-----------------------------------------------------------------------------------------|-------|----------|-----------|------------|-------|
+| Add/remove [child epics](group/epics/manage_epics.md#multi-level-child-epics) | ✓ (8) | ✓ | ✓ | ✓ | ✓ |
| Add an issue to an [epic](group/epics/index.md) | ✓ (7) | ✓ (7) | ✓ (7) | ✓ (7) | ✓ (7) |
| Browse group | ✓ | ✓ | ✓ | ✓ | ✓ |
| Pull a container image using the dependency proxy | ✓ | ✓ | ✓ | ✓ | ✓ |
@@ -397,6 +379,7 @@ The following table lists group permissions available for each role:
| Pull [packages](packages/index.md) | | ✓ | ✓ | ✓ | ✓ |
| Delete [packages](packages/index.md) | | | | ✓ | ✓ |
| Create/edit/delete [Maven and generic package duplicate settings](packages/generic_packages/index.md#do-not-allow-duplicate-generic-packages) | | | | ✓ | ✓ |
+| Enable/disable package request forwarding | | | | ✓ | ✓ |
| Pull a Container Registry image | ✓ (6) | ✓ | ✓ | ✓ | ✓ |
| Remove a Container Registry image | | | ✓ | ✓ | ✓ |
| View [Group DevOps Adoption](group/devops_adoption/index.md) | | ✓ | ✓ | ✓ | ✓ |
@@ -448,6 +431,7 @@ The following table lists group permissions available for each role:
5. In addition, if your group is public or internal, all users who can see the group can also see group wiki pages.
6. Users can only view events based on their individual actions.
7. You must have permission to [view the epic](group/epics/manage_epics.md#who-can-view-an-epic) and edit the issue.
+8. You must have permission to [view](group/epics/manage_epics.md#who-can-view-an-epic) the parent and child epics.
<!-- markdownlint-enable MD029 -->
@@ -460,109 +444,6 @@ nested groups if you have membership in one of its parents.
To learn more, read through the documentation on
[subgroups memberships](group/subgroups/index.md#subgroup-membership).
-## External users **(FREE SELF)**
-
-In cases where it is desired that a user has access only to some internal or
-private projects, there is the option of creating **External Users**. This
-feature may be useful when for example a contractor is working on a given
-project and should only have access to that project.
-
-External users:
-
-- Cannot create project, groups, and snippets in their personal namespaces.
-- Can only create projects (including forks), subgroups, and snippets within top-level groups to which they are explicitly granted access.
-- Can only access public projects and projects to which they are explicitly granted access,
- thus hiding all other internal or private ones from them (like being
- logged out).
-- Can only access public groups and groups to which they are explicitly granted access,
- thus hiding all other internal or private ones from them (like being
- logged out).
-- Can only access public snippets.
-
-Access can be granted by adding the user as member to the project or group.
-Like usual users, they receive a role in the project or group with all
-the abilities that are mentioned in the [permissions table above](#project-members-permissions).
-For example, if an external user is added as Guest, and your project is internal or
-private, they do not have access to the code; you need to grant the external
-user access at the Reporter level or above if you want them to have access to the code. You should
-always take into account the
-[project's visibility and permissions settings](project/settings/index.md#configure-project-visibility-features-and-permissions)
-as well as the permission level of the user.
-
-NOTE:
-External users still count towards a license seat.
-
-An administrator can flag a user as external by either of the following methods:
-
-- [Through the API](../api/users.md#user-modification).
-- Using the GitLab UI:
- 1. On the top bar, select **Main menu > Admin**.
- 1. On the left sidebar, select **Overview > Users** to create a new user or edit an existing one.
- There, you can find the option to flag the user as external.
-
-Additionally, users can be set as external users using:
-
-- [SAML groups](../integration/saml.md#external-groups).
-- [LDAP groups](../administration/auth/ldap/ldap_synchronization.md#external-groups).
-
-### Setting new users to external
-
-By default, new users are not set as external users. This behavior can be changed
-by an administrator:
-
-1. On the top bar, select **Main menu > Admin**.
-1. On the left sidebar, select **Settings > General**.
-1. Expand the **Account and limit** section.
-
-If you change the default behavior of creating new users as external, you
-have the option to narrow it down by defining a set of internal users.
-The **Internal users** field allows specifying an email address regex pattern to
-identify default internal users. New users whose email address matches the regex
-pattern are set to internal by default rather than an external collaborator.
-
-The regex pattern format is in Ruby, but it needs to be convertible to JavaScript,
-and the ignore case flag is set (`/regex pattern/i`). Here are some examples:
-
-- Use `\.internal@domain\.com$` to mark email addresses ending with
- `.internal@domain.com` as internal.
-- Use `^(?:(?!\.ext@domain\.com).)*$\r?` to mark users with email addresses
- not including `.ext@domain.com` as internal.
-
-WARNING:
-Be aware that this regex could lead to a
-[regular expression denial of service (ReDoS) attack](https://en.wikipedia.org/wiki/ReDoS).
-
-## Free Guest users **(ULTIMATE)**
-
-When a user is given the Guest role on a project, group, or both, and holds no
-higher permission level on any other project or group on the GitLab instance,
-the user is considered a guest user by GitLab and does not consume a license seat.
-There is no other specific "guest" designation for newly created users.
-
-If the user is assigned a higher role on any projects or groups, the user
-takes a license seat. If a user creates a project, the user becomes a Maintainer
-on the project, resulting in the use of a license seat. Also, note that if your
-project is internal or private, Guest users have all the abilities that are
-mentioned in the [permissions table above](#project-members-permissions) (they
-are unable to browse the project's repository, for example).
-
-NOTE:
-To prevent a guest user from creating projects, as an administrator, you can edit the
-user's profile to mark the user as [external](#external-users).
-Beware though that even if a user is external, if they already have Reporter or
-higher permissions in any project or group, they are **not** counted as a
-free guest user.
-
-## Auditor users **(PREMIUM SELF)**
-
-Auditor users are given read-only access to all projects, groups, and other
-resources on the GitLab instance.
-
-An Auditor user should be able to access all projects and groups of a GitLab instance
-with the permissions described on the documentation on [auditor users permissions](../administration/auditor_users.md#auditor-user-permissions-and-restrictions).
-
-[Read more about Auditor users.](../administration/auditor_users.md)
-
## Users with minimal access **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40942) in GitLab 13.4.
@@ -571,6 +452,11 @@ Owners can add members with a "minimal access" role to a parent group. Such user
projects and subgroups underneath. Owners must explicitly add these "minimal access" users to the specific subgroups and
projects.
+You can use minimal access to give the same member more than one role in a group:
+
+1. Add the member to the parent group with a minimal access role.
+1. Invite the member as a direct member with a specific role in any subgroup or project in that group.
+
Because of an [outstanding issue](https://gitlab.com/gitlab-org/gitlab/-/issues/267996), when minimal access users:
- Sign in with standard web authentication, they receive a `404` error when accessing the parent group.
@@ -584,16 +470,6 @@ Users with even a "minimal access" role are counted against your number of licen
requirement does not apply for [GitLab Ultimate](https://about.gitlab.com/pricing/)
subscriptions.
-## Project features
-
-Project features like wiki and issues can be hidden from users depending on
-which visibility level you select on project settings.
-
-- Disabled: disabled for everyone.
-- Only team members: only team members can see, even if your project is public or internal.
-- Everyone with access: everyone can see depending on your project visibility level.
-- Everyone: enabled for everyone (only available for GitLab Pages).
-
## Release permissions with protected tags
[The permission to create tags](project/protected_tags.md) is used to define if a user can
@@ -602,12 +478,12 @@ create, edit, and delete [Releases](project/releases/index.md).
See [Release permissions](project/releases/index.md#release-permissions)
for more information.
-## LDAP users permissions
-
-LDAP user permissions can be manually overridden by an administrator.
-Read through the documentation on [LDAP users permissions](group/access_and_permissions.md#manage-group-memberships-via-ldap) to learn more.
-
-## Project aliases
+## Related topics
-Project aliases can only be read, created and deleted by a GitLab administrator.
-Read through the documentation on [Project aliases](../user/project/import/index.md#project-aliases) to learn more.
+- [The GitLab principles behind permissions](https://about.gitlab.com/handbook/product/gitlab-the-product/#permissions-in-gitlab)
+- [Members](project/members/index.md)
+- Customize permissions on [protected branches](project/protected_branches.md)
+- [LDAP users permissions](group/access_and_permissions.md#manage-group-memberships-via-ldap)
+- [Value stream analytics permissions](analytics/value_stream_analytics.md#access-permissions-for-value-stream-analytics)
+- [Project aliases](../user/project/import/index.md#project-aliases)
+- [Auditor users](../administration/auditor_users.md)
diff --git a/doc/user/product_analytics/index.md b/doc/user/product_analytics/index.md
index 8e340fff32a..46f8b57a64c 100644
--- a/doc/user/product_analytics/index.md
+++ b/doc/user/product_analytics/index.md
@@ -4,25 +4,51 @@ group: Product Analytics
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Product analytics **(ULTIMATE)** **Alpha**
+# Product analytics **(ULTIMATE)**
-> Introduced in GitLab 15.4 [with a flag](../../administration/feature_flags.md) named `cube_api_proxy`. Disabled by default.
+> Introduced in GitLab 15.4 as an [Alpha](../../policy/alpha-beta-support.md#alpha-features) feature [with a flag](../../administration/feature_flags.md) named `cube_api_proxy`. Disabled by default.
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available per project or for your entire instance, ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `cube_api_proxy`.
On GitLab.com, this feature is not available.
This feature is not ready for production use.
-## Overview
+This page is a work in progress, and we're updating the information as we add more features.
+For more information, visit the [Product Analytics group direction page](https://about.gitlab.com/direction/analytics/product-analytics/).
-You can view the [product category](https://about.gitlab.com/direction/analytics/product-analytics/) page for more information about our direction. This page is a work in progress and will be updated as we add more features.
+## Enable product analytics
+
+You can enable and configure product analytics to track events
+within your project applications on a self-managed instance.
+
+Prerequisite:
+
+- You must be an administrator of a self-managed GitLab instance.
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Settings > General**.
+1. Expand the **Product analytics** section.
+1. Select **Enable product analytics** and enter the configuration values.
+ The following table shows the required configuration parameters and example values:
+
+ | Name | Value |
+ |------------------------------|------------------------------------------------------------|
+ | Jitsu host | `https://jitsu.gitlab.com` |
+ | Jitsu project ID | `g0maofw84gx5sjxgse2k` |
+ | Jitsu administrator email | `jitsu.admin@gitlab.com` |
+ | Jitsu administrator password | `<your_password>` |
+ | Clickhouse URL | `https://<username>:<password>@clickhouse.gitlab.com:8123` |
+ | Cube API URL | `https://cube.gitlab.com` |
+ | Cube API key | `25718201b3e9...ae6bbdc62dbb` |
+
+1. Select **Save changes**.
## Product analytics dashboards
Each project can define an unlimited number of dashboards. These dashboards are defined using our YAML schema and stored
-in the `.gitlab/product_analytics/dashboards/` directory. The name of the file is the name of the dashboard, and visualizations are shared across dashboards..
+in the `.gitlab/product_analytics/dashboards/` directory of a project repository. The name of the file is the name of the dashboard, and visualizations are shared across dashboards.
-Project maintainers can enforce approval rules on dashboard changes, and dashboards can be versioned in source control.
+Project maintainers can enforce approval rules on dashboard changes using features such as code owners and approval rules. Dashboards are versioned in source control with the rest of a project's code.
### Define a dashboard
@@ -30,8 +56,8 @@ To define a dashboard:
1. In `.gitlab/product_analytics/dashboards/`, create a directory named like the dashboard. Each dashboard should have its own directory.
1. In the new directory, create a `.yaml` file with the same name as the directory. This file contains the dashboard definition, and must conform to the JSON schema defined in `ee/app/validators/json_schemas/product_analytics_dashboard.json`.
-1. In the `.gitlab/product_analytics/dashboards/visualizations/` directory, create a `yaml` file. This file defines the visualization type for the dashboard, and must conform to the schema in
-`ee/app/validators/json_schemas/product_analytics_visualization.json`.
+1. In the `.gitlab/product_analytics/dashboards/visualizations/` directory, create a `yaml` file. This file defines the visualization type for the dashboard, and must conform to the schema in
+ `ee/app/validators/json_schemas/product_analytics_visualization.json`.
The example below includes three dashboards and one visualization that applies to all dashboards.
diff --git a/doc/user/profile/account/delete_account.md b/doc/user/profile/account/delete_account.md
index 5e2908a26e1..18b4e53fb31 100644
--- a/doc/user/profile/account/delete_account.md
+++ b/doc/user/profile/account/delete_account.md
@@ -56,6 +56,7 @@ When deleting users, you can either:
- Issues.
- Merge requests.
- Notes and comments.
+ - Releases.
- Personal access tokens.
- Snippets.
diff --git a/doc/user/profile/active_sessions.md b/doc/user/profile/active_sessions.md
index 430d1c3dc9f..7a837258cb2 100644
--- a/doc/user/profile/active_sessions.md
+++ b/doc/user/profile/active_sessions.md
@@ -40,7 +40,7 @@ To revoke an active session:
NOTE:
When any session is revoked all **Remember me** tokens for all
-devices are revoked. See [Why do I keep getting signed out?](index.md#why-do-i-keep-getting-signed-out)
+devices are revoked. See [Why do you keep getting signed out?](index.md#why-do-you-keep-getting-signed-out)
for more information about the **Remember me** feature.
<!-- ## Troubleshooting
diff --git a/doc/user/profile/contributions_calendar.md b/doc/user/profile/contributions_calendar.md
index 6df7ad56c5e..d66e555970a 100644
--- a/doc/user/profile/contributions_calendar.md
+++ b/doc/user/profile/contributions_calendar.md
@@ -21,50 +21,20 @@ The contribution calendar only displays contributions from the last 12 months, b
GitLab tracks the following contribution events:
-- `approved`
- - Merge request
-- `closed`
- - [Epic](../group/epics/index.md)
- - Issue
- - Merge request
- - Milestone
-- `commented` on any `Noteable` record.
- - Alert
- - Commit
- - Design
- - Issue
- - Merge request
- - Snippet
-- `created`
- - Design
- - [Epic](../group/epics/index.md)
- - Issue
- - Merge request
- - Milestone
- - Project
- - Wiki page
-- `destroyed`
- - Design
- - Milestone
- - Wiki page
-- `expired`
- - Project membership
-- `joined`
- - Project membership
-- `left`
- - Project membership
-- `merged`
- - Merge request
-- `pushed` commits to (or deleted commits from) a repository, individually or in bulk.
- - Project
-- `reopened`
- - [Epic](../group/epics/index.md)
- - Issue
- - Merge request
- - Milestone
-- `updated`
- - Design
- - Wiki page
+| Event | Contribution |
+| ----- | ------------ |
+| `approved` | Merge request |
+| `closed` | [Epic](../group/epics/index.md), Issue, Merge request, Milestone, Work item |
+| `commented` on any `Noteable` record. | Alert, Commit, Design, Issue, Merge request, Snippet |
+| `created` | Design, Epic, Issue, Merge request, Milestone, Project, Wiki page, Work item |
+| `destroyed` | Design, Milestone, Wiki page |
+| `expired` | Project membership |
+| `joined` | Project membership |
+| `left` | Project membership |
+| `merged` | Merge request |
+| `pushed` commits to (or deleted commits from) a repository, individually or in bulk. | Project |
+| `reopened` | Epic, Issue, Merge request, Milestone |
+| `updated` | Design, Wiki page |
### View daily contributions
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index 4adf6c351df..d6c5bd6c108 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -328,7 +328,7 @@ To view a user's activity in a top-level Activity view:
## Troubleshooting
-### Why do I keep getting signed out?
+### Why do you keep getting signed out?
When you sign in to the main GitLab application, a `_gitlab_session` cookie is
set. When you close your browser, the cookie is cleared client-side
@@ -368,7 +368,7 @@ GitLab uses both session and persistent cookies:
- Session cookie: Session cookies are normally removed at the end of the browser session when
the browser is closed. The `_gitlab_session` cookie has no fixed expiration date. However,
- it expires based on its [`session_expire_delay`](#why-do-i-keep-getting-signed-out).
+ it expires based on its [`session_expire_delay`](#why-do-you-keep-getting-signed-out).
- Persistent cookie: The `remember_user_token` is a cookie with an expiration date of two weeks.
GitLab activates this cookie if you select **Remember Me** when you sign in.
diff --git a/doc/user/profile/notifications.md b/doc/user/profile/notifications.md
index 1deb4842107..d0a420a4bbd 100644
--- a/doc/user/profile/notifications.md
+++ b/doc/user/profile/notifications.md
@@ -76,7 +76,7 @@ For each project and group you can select one of the following levels:
| Participate | Receive notifications for threads you have participated in. |
| On mention | Receive notifications when you are [mentioned](../discussions/index.md#mentions) in a comment. |
| Disabled | Receive no notifications. |
-| Custom | Receive notifications for selected events. |
+| Custom | Receive notifications for selected events and threads you have participated in. |
### Global notification settings
diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md
index dce8684d993..664d22959a2 100644
--- a/doc/user/profile/preferences.md
+++ b/doc/user/profile/preferences.md
@@ -96,7 +96,7 @@ A diff compares the old/removed content with the new/added content (for example,
[Markdown inline diff](../markdown.md#inline-diff)).
Typically, the colors red and green are used for removed and added lines in diffs.
The exact colors depend on the selected [syntax highlighting theme](#syntax-highlighting-theme).
-The colors may lead to difficulties in case of red–green color blindness.
+The colors may lead to difficulties in case of red-green color blindness.
For this reason, you can customize the following colors:
@@ -203,6 +203,22 @@ NOTE:
This feature is experimental, and choosing absolute times might break certain layouts.
Open an issue if you notice that using absolute times breaks a layout.
+## Web IDE
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/370139) in GitLab 15.7 [with a flag](../../administration/feature_flags.md) named `vscode_web_ide`. Disabled by default.
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `vscode_web_ide`. On GitLab.com, this feature is available.
+
+The [Web IDE Beta](../project/web_ide_beta/index.md) is
+the default editing environment when the `vscode_web_ide` feature
+flag is enabled.
+
+To stop using the Web IDE Beta:
+
+1. In the **Web IDE** section, select the **Opt out of the Web IDE Beta** checkbox.
+1. Select **Save changes**.
+
## Integrations
Configure your preferences with third-party services which provide enhancements to your GitLab experience.
diff --git a/doc/user/profile/user_passwords.md b/doc/user/profile/user_passwords.md
index b8dbdcdd956..9c1ba8852d2 100644
--- a/doc/user/profile/user_passwords.md
+++ b/doc/user/profile/user_passwords.md
@@ -61,12 +61,8 @@ Self-managed installations can configure the following additional password requi
## Block weak passwords
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23610) in GitLab 15.4 [with a flag](../../administration/feature_flags.md) named `block_weak_passwords`, weak passwords aren't accepted. Disabled by default on self-managed.
-> - [Enabled](https://gitlab.com/gitlab-org/gitlab/-/issues/363445) on GitLab.com.
-
-FLAG:
-On self-managed GitLab, by default blocking weak passwords is not available. To make it available, ask an administrator
-to [enable the feature flag](../../administration/feature_flags.md) named `block_weak_passwords`. On GitLab.com, this
-feature is available but can be configured by GitLab.com administrators only.
+> - [Enabled](https://gitlab.com/gitlab-org/gitlab/-/issues/363445) on GitLab.com in GitLab 15.6.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/363445) and enabled on self-managed in GitLab 15.7. Feature flag `block_weak_passwords` removed.
GitLab disallows weak passwords. Your password is considered weak when it:
diff --git a/doc/user/project/badges.md b/doc/user/project/badges.md
index 5d1d10fc37d..dc650bd9482 100644
--- a/doc/user/project/badges.md
+++ b/doc/user/project/badges.md
@@ -89,6 +89,8 @@ which are evaluated when displaying the badge. The following placeholders
are available:
- `%{project_path}`: Path of a project including the parent groups
+- `%{project_title}`: Title of a project
+- `%{project_name}`: Name of a project
- `%{project_id}`: Database ID associated with a project
- `%{default_branch}`: Default branch name configured for a project's repository
- `%{commit_sha}`: ID of the most recent commit to the default branch of a
diff --git a/doc/user/project/canary_deployments.md b/doc/user/project/canary_deployments.md
index 3e6a9acc304..95f38c7e354 100644
--- a/doc/user/project/canary_deployments.md
+++ b/doc/user/project/canary_deployments.md
@@ -68,7 +68,7 @@ Here's an example setup flow from scratch:
If it isn't, follow the documentation to specify the image version.
1. [Run a new Auto DevOps pipeline](../../ci/pipelines/index.md#run-a-pipeline-manually)
and make sure that the `production` job succeeds and creates a production environment.
-1. Configure a [`canary` deployment job for Auto DevOps pipelines](../../topics/autodevops/customize.md#deploy-policy-for-canary-environments).
+1. Configure a [`canary` deployment job for Auto DevOps pipelines](../../topics/autodevops/cicd_variables.md#deploy-policy-for-canary-environments).
1. [Run a new Auto DevOps pipeline](../../ci/pipelines/index.md#run-a-pipeline-manually)
and make sure that the `canary` job succeeds and creates a canary deployment with Canary Ingress.
diff --git a/doc/user/project/clusters/add_eks_clusters.md b/doc/user/project/clusters/add_eks_clusters.md
index b3d381c3148..52288af101a 100644
--- a/doc/user/project/clusters/add_eks_clusters.md
+++ b/doc/user/project/clusters/add_eks_clusters.md
@@ -195,7 +195,7 @@ If a default Storage Class doesn't already exist and is desired, follow Amazon's
to create one.
Alternatively, disable PostgreSQL by setting the project variable
-[`POSTGRES_ENABLED`](../../../topics/autodevops/customize.md#cicd-variables) to `false`.
+[`POSTGRES_ENABLED`](../../../topics/autodevops/cicd_variables.md#cicd-variables) to `false`.
## Deploy the app to EKS
diff --git a/doc/user/project/clusters/cluster_access.md b/doc/user/project/clusters/cluster_access.md
index c9b3596d92f..529e7a6da12 100644
--- a/doc/user/project/clusters/cluster_access.md
+++ b/doc/user/project/clusters/cluster_access.md
@@ -49,16 +49,16 @@ GitLab creates the following resources for RBAC clusters.
| Name | Type | Details | Created when |
|:----------------------|:---------------------|:-----------------------------------------------------------------------------------------------------------|:-----------------------|
| `gitlab` | `ServiceAccount` | `default` namespace | Creating a new cluster |
-| `gitlab-admin` | `ClusterRoleBinding` | [`cluster-admin`](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) roleRef | Creating a new cluster |
+| `gitlab-admin` | `ClusterRoleBinding` | [`cluster-admin`](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) role | Creating a new cluster |
| `gitlab-token` | `Secret` | Token for `gitlab` ServiceAccount | Creating a new cluster |
| Environment namespace | `Namespace` | Contains all environment-specific resources | Deploying to a cluster |
| Environment namespace | `ServiceAccount` | Uses namespace of environment | Deploying to a cluster |
| Environment namespace | `Secret` | Token for environment ServiceAccount | Deploying to a cluster |
-| Environment namespace | `RoleBinding` | [`admin`](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) roleRef | Deploying to a cluster |
+| Environment namespace | `RoleBinding` | [`admin`](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) role | Deploying to a cluster |
The environment namespace `RoleBinding` was
[updated](https://gitlab.com/gitlab-org/gitlab/-/issues/31113) in GitLab 13.6
-to `admin` roleRef. Previously, the `edit` roleRef was used.
+to `admin` role. Previously, the `edit` role was used.
## ABAC cluster resources
diff --git a/doc/user/project/deploy_tokens/index.md b/doc/user/project/deploy_tokens/index.md
index 3dd6f14ea70..5f279ddda5b 100644
--- a/doc/user/project/deploy_tokens/index.md
+++ b/doc/user/project/deploy_tokens/index.md
@@ -216,19 +216,3 @@ Prerequisites:
- A deploy token with `read_registry` and `write_registry` scopes.
Follow the dependency proxy [authentication instructions](../../packages/dependency_proxy/index.md).
-
-## Troubleshooting
-
-### Error: `api error: Repository or object not found:`
-
-When using a group deploy token to clone from LFS objects, you might get `404 Not Found` responses
-and this error message. This occurs because of a bug, documented in
-[issue 235398](https://gitlab.com/gitlab-org/gitlab/-/issues/235398).
-
-```plaintext
-api error: Repository or object not found:
-https://<URL-with-token>.git/info/lfs/objects/batch
-Check that it exists and that you have proper access to it
-```
-
-The workaround is to use a project deploy token.
diff --git a/doc/user/project/description_templates.md b/doc/user/project/description_templates.md
index 40c36236932..ffbe7447aa8 100644
--- a/doc/user/project/description_templates.md
+++ b/doc/user/project/description_templates.md
@@ -40,7 +40,7 @@ To create an issue description template:
where `mytemplate` is the name of your issue template.
1. Commit to your default branch.
-To check if this has worked correctly, [create a new issue](issues/managing_issues.md#create-an-issue)
+To check if this has worked correctly, [create a new issue](issues/create_issues.md)
and see if you can find your description template in the **Choose a template** dropdown list.
## Create a merge request template
@@ -81,7 +81,23 @@ To discard any changes to the description you've made after selecting the templa
NOTE:
You can create shortcut links to create an issue using a designated template.
-For example: `https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Feature%20proposal`. Read more about [creating issues using a URL with prefilled values](issues/managing_issues.md#using-a-url-with-prefilled-values).
+For example: `https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Feature%20proposal`. Read more about [creating issues using a URL with prefilled values](issues/create_issues.md#using-a-url-with-prefilled-values).
+
+### Supported variables in merge request templates
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89810) in GitLab 15.7.
+
+When you save a merge request for the first time, GitLab replaces these variables in
+your merge request template with their values:
+
+| Variable | Description | Output example |
+|----------|-------------|----------------|
+| `%{all_commits}` | Messages from all commits in the merge request. Limited to 100 most recent commits. Skips commit bodies exceeding 100 KiB and merge commit messages. | `* Feature introduced` <br><br> `This commit implements feature` <br> `Changelog:added` <br><br> `* Bug fixed` <br><br> `* Documentation improved` <br><br>`This commit introduced better docs.` |
+| `%{co_authored_by}` | Names and emails of commit authors in a `Co-authored-by` Git commit trailer format. Limited to authors of 100 most recent commits in merge request. | `Co-authored-by: Zane Doe <zdoe@example.com>` <br> `Co-authored-by: Blake Smith <bsmith@example.com>` |
+| `%{first_commit}` | Full message of the first commit in merge request diff. | `Update README.md` |
+| `%{first_multiline_commit}` | Full message of the first commit that's not a merge commit and has more than one line in message body. Merge request title if all commits aren't multiline. | `Update README.md`<br><br>`Improved project description in readme file.` |
+| `%{source_branch}` | The name of the branch being merged. | `my-feature-branch` |
+| `%{target_branch}` | The name of the branch that the changes are applied to. | `main` |
### Set instance-level description templates **(PREMIUM SELF)**
diff --git a/doc/user/project/import/bitbucket.md b/doc/user/project/import/bitbucket.md
index 3f1a2dcfe2b..98b46650b42 100644
--- a/doc/user/project/import/bitbucket.md
+++ b/doc/user/project/import/bitbucket.md
@@ -104,7 +104,7 @@ current Bitbucket public name, and reconnect if there's a mismatch:
1. [Use the API to get the currently authenticated user](../../../api/users.md#for-normal-users-1).
-1. In the API's response, the `identities` attribute contains the Bitbucket account that exists in
+1. In the API response, the `identities` attribute contains the Bitbucket account that exists in
the GitLab database. If the `extern_uid` doesn't match the current Bitbucket public name, the
user should reconnect their Bitbucket account in the [GitLab profile service sign-in](https://gitlab.com/-/profile/account).
diff --git a/doc/user/project/import/bitbucket_server.md b/doc/user/project/import/bitbucket_server.md
index 1f34c6d4ad9..d7fa1338c55 100644
--- a/doc/user/project/import/bitbucket_server.md
+++ b/doc/user/project/import/bitbucket_server.md
@@ -63,6 +63,10 @@ The following items are changed when they are imported:
## User assignment
+Prerequisite:
+
+- Authentication token with administrator access.
+
When issues and pull requests are importing, the importer tries to find the author's email address
with a confirmed email address in the GitLab user database. If no such user is available, the
project creator is set as the author. The importer appends a note in the comment to mark the
diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md
index c0b13c76322..07a21d8b941 100644
--- a/doc/user/project/import/github.md
+++ b/doc/user/project/import/github.md
@@ -59,11 +59,10 @@ their GitHub authors and assignees in the database of the GitLab instance. Pull
GitLab.
For this association to succeed, each GitHub author and assignee in the repository
-must meet one of the following conditions prior to the import:
-
-- Have previously logged in to a GitLab account using the GitHub icon.
-- Have a GitHub account with a [public-facing email address](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/setting-your-commit-email-address)
- that matches their GitLab account's email address.
+must have a [public-facing email address](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/setting-your-commit-email-address)
+on GitHub that matches their GitLab email address (regardless of how the account was created).
+If their email address from GitHub is set as their secondary email address in GitLab, it must be
+confirmed.
GitLab content imports that use GitHub accounts require that the GitHub public-facing email address is populated. This means
all comments and contributions are properly mapped to the same user in GitLab. GitHub Enterprise does not require this
@@ -73,10 +72,8 @@ field to be populated so you may have to add it on existing accounts.
### Use the GitHub integration
-Before you begin, ensure that any GitHub users who you want to map to GitLab users have either:
-
-- A GitLab account that has logged in using the GitHub icon.
-- A GitLab account with an email address that matches the [publicly visible email address](https://docs.github.com/en/rest/users#get-a-user) in the profile of the GitHub user
+Before you begin, ensure that any GitHub user you want to map to a GitLab user has a GitLab email address that matches their
+[publicly visible email address](https://docs.github.com/en/rest/users#get-a-user) on GitHub.
If you are importing to GitLab.com, you can alternatively import GitHub repositories using a [personal access token](#use-a-github-token).
We do not recommend this method, as it does not associate all user activity (such as issues and pull requests) with matching GitLab users.
@@ -96,7 +93,10 @@ If you are using a self-managed GitLab instance or if you are importing from Git
### Use a GitHub token
-NOTE:
+Prerequisite:
+
+- Authentication token with administrator access.
+
Using a personal access token to import projects is not recommended. If you are a GitLab.com user,
you can use a personal access token to import your project from GitHub, but this method cannot
associate all user activity (such as issues and pull requests) with matching GitLab users.
@@ -222,21 +222,26 @@ References to pull requests and issues are preserved. Each imported repository m
[visibility level is restricted](../../public_access.md#restrict-use-of-public-or-internal-projects), in which case it
defaults to the default project visibility.
-### Branch protection rules
+### Branch protection rules and project settings
+
+When they are imported, supported GitHub branch protection rules are mapped to either:
+
+- GitLab branch protection rules.
+- Project-wide GitLab settings.
-Supported GitHub branch protection rules are mapped to GitLab branch protection rules or project-wide GitLab settings when they are imported:
+| GitHub rule | GitLab rule | Introduced in |
+| :---------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------ |
+| **Require conversation resolution before merging** for the project's default branch | **All threads must be resolved** [project setting](../../discussions/index.md#prevent-merge-unless-all-threads-are-resolved) | [GitLab 15.5](https://gitlab.com/gitlab-org/gitlab/-/issues/371110) |
+| **Require a pull request before merging** | **No one** option in the **Allowed to push** list of [branch protection settings](../protected_branches.md#configure-a-protected-branch) | [GitLab 15.5](https://gitlab.com/gitlab-org/gitlab/-/issues/370951) |
+| **Require signed commits** for the project's default branch | **Reject unsigned commits** GitLab [push rule](../repository/push_rules.md#prevent-unintended-consequences) **(PREMIUM)** | [GitLab 15.5](https://gitlab.com/gitlab-org/gitlab/-/issues/370949) |
+| **Allow force pushes - Everyone** | **Allowed to force push** [branch protection setting](../protected_branches.md#allow-force-push-on-a-protected-branch) | [GitLab 15.6](https://gitlab.com/gitlab-org/gitlab/-/issues/370943) |
+| **Require a pull request before merging - Require review from Code Owners** | **Require approval from code owners** [branch protection setting](../protected_branches.md#require-code-owner-approval-on-a-protected-branch) **(PREMIUM)** | [GitLab 15.6](https://gitlab.com/gitlab-org/gitlab/-/issues/376683) |
-- GitHub rule **Require conversation resolution before merging** for the project's default branch is mapped to the [**All threads must be resolved** GitLab setting](../../discussions/index.md#prevent-merge-unless-all-threads-are-resolved). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/371110) in GitLab 15.5.
-- GitHub rule **Require a pull request before merging** is mapped to the **No one** option in the **Allowed to push** list of the branch protection rule. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/370951) in GitLab 15.5.
-- GitHub rule **Require a pull request before merging - Require review from Code Owners** is mapped to the
- [**Code owner approval** branch protection rule](../protected_branches.md#require-code-owner-approval-on-a-protected-branch). Requires GitLab Premium or higher.
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/376683) in GitLab 15.6.
-- GitHub rule **Require signed commits** for the project's default branch is mapped to the **Reject unsigned commits** GitLab push rule. Requires GitLab Premium or higher.
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/370949) in GitLab 15.5.
-- GitHub rule **Allow force pushes - Everyone** is mapped to the [**Allowed to force push** branch protection rule](../protected_branches.md#allow-force-push-on-a-protected-branch). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/370943) in GitLab 15.6.
-- GitHub rule **Allow force pushes - Specify who can force push** is proposed in issue [370945](https://gitlab.com/gitlab-org/gitlab/-/issues/370945).
-- Support for GitHub rule **Require status checks to pass before merging** was proposed in issue [370948](https://gitlab.com/gitlab-org/gitlab/-/issues/370948). However, this rule cannot be translated during project import into GitLab due to technical difficulties.
-You can still create [status checks](../merge_requests/status_checks.md) in GitLab yourself.
+Mapping GitHub rule **Require status checks to pass before merging** to
+[external status checks](../merge_requests/status_checks.md) was considered in issue
+[370948](https://gitlab.com/gitlab-org/gitlab/-/issues/370948). However, this rule is not imported during project import
+into GitLab due to technical difficulties. You can still create [external status checks](../merge_requests/status_checks.md)
+manually.
## Alternative way to import notes and diff notes
@@ -274,7 +279,7 @@ Feature.disable(:github_importer_lower_per_page_limit, group)
## Import from GitHub Enterprise on an internal network
-If your GitHub Enterprise instance is on a internal network that is unaccessible to the internet, you can use a reverse proxy
+If your GitHub Enterprise instance is on a internal network that is inaccessible to the internet, you can use a reverse proxy
to allow GitLab.com to access the instance.
The proxy needs to:
diff --git a/doc/user/project/import/img/gitlab_import_history_page_v14_10.png b/doc/user/project/import/img/gitlab_import_history_page_v14_10.png
deleted file mode 100644
index 812696a8faa..00000000000
--- a/doc/user/project/import/img/gitlab_import_history_page_v14_10.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/import/index.md b/doc/user/project/import/index.md
index 1b5a658d209..208bce90453 100644
--- a/doc/user/project/import/index.md
+++ b/doc/user/project/import/index.md
@@ -5,76 +5,59 @@ group: Import
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Migrate projects to a GitLab instance **(FREE)**
-
-See these documents to migrate to GitLab:
-
-- [From Bitbucket Cloud](bitbucket.md)
-- [From Bitbucket Server (also known as Stash)](bitbucket_server.md)
-- [From ClearCase](clearcase.md)
-- [From CVS](cvs.md)
-- [From FogBugz](fogbugz.md)
-- [From GitHub.com or GitHub Enterprise](github.md)
-- [From GitLab.com](gitlab_com.md)
-- [From Gitea](gitea.md)
-- [From Perforce](perforce.md)
-- [From SVN](svn.md)
-- [From TFVC](tfvc.md)
-- [From repository by URL](repo_by_url.md)
-- [By uploading a manifest file (AOSP)](manifest.md)
-- [From Phabricator](phabricator.md)
-- [From Jira (issues only)](jira.md)
+# Import and migrate projects **(FREE)**
-You can also import any Git repository through HTTP from the **New Project** page. Note that if the
-repository is too large, the import can timeout.
-
-You can also [connect your external repository to get CI/CD benefits](../../../ci/ci_cd_for_external_repos/index.md).
+If you want to bring existing projects to GitLab or copy GitLab projects to a different location, you can:
-## Project import history
-
-You can view all project imports created by you. This list includes the following:
+- Import projects from external systems using one of the [available importers](#available-project-importers).
+- Migrate GitLab projects:
+ - Between two GitLab self-managed instances.
+ - Between a self-managed instance and GitLab.com in both directions.
+ - In the same GitLab instance.
-- Source (without credentials for security reasons)
-- Destination
-- Status
-- Error details if the import failed
+For any type of source and target, you can migrate projects:
-To view project import history:
+- As part of a [GitLab group migration](../../group/import/index.md). You can't migrate only chosen projects,
+ but you can migrate many projects at once within a group.
+- Using [file exports](../settings/import_export.md). With this method you can migrate projects one by one. No network
+ connection between instances is required.
-1. Sign in to GitLab.
-1. On the top bar, select **New** (**{plus}**).
-1. Select **New project/repository**.
-1. Select **Import project**.
-1. Select **History**.
+If you only need to migrate Git repositories, you can [import each project by URL](repo_by_url.md). However, you can't
+import issues and merge requests this way. To retain metadata like issues and merge requests, either:
-![Project import history page](img/gitlab_import_history_page_v14_10.png)
+- [Migrate projects with groups](../../group/import/index.md).
+- Use [file exports](../settings/import_export.md) to import projects.
-The history also includes projects created from [built-in](../working_with_projects.md#create-a-project-from-a-built-in-template)
-or [custom](../working_with_projects.md#create-a-project-from-a-built-in-template)
-templates. GitLab uses [import repository by URL](repo_by_url.md)
-to create a new project from a template.
+Keep in mind the limitations of [migrating using file exports](../settings/import_export.md#items-that-are-exported).
+When migrating from self-managed to GitLab.com, user associations (such as comment author)
+are changed to the user who is importing the projects.
-## LFS authentication
+## Available project importers
-When importing a project that contains LFS objects, if the project has an [`.lfsconfig`](https://github.com/git-lfs/git-lfs/blob/master/docs/man/git-lfs-config.5.ronn)
-file with a URL host (`lfs.url`) different from the repository URL host, LFS files are not downloaded.
+You can import projects from:
-## Migrate from self-managed GitLab to GitLab.com
+- [Bitbucket Cloud](bitbucket.md)
+- [Bitbucket Server (also known as Stash)](bitbucket_server.md)
+- [ClearCase](clearcase.md)
+- [CVS](cvs.md)
+- [FogBugz](fogbugz.md)
+- [GitHub.com or GitHub Enterprise](github.md)
+- [GitLab.com](gitlab_com.md)
+- [Gitea](gitea.md)
+- [Perforce](perforce.md)
+- [From SVN](https://git-scm.com/book/en/v2/Git-and-Other-Systems-Git-as-a-Client)
+- [TFVC](tfvc.md)
+- [Repository by URL](repo_by_url.md)
+- [Uloading a manifest file (AOSP)](manifest.md)
+- [Phabricator](phabricator.md)
+- [Jira (issues only)](jira.md)
-If you only need to migrate Git repositories, you can [import each project by URL](repo_by_url.md).
-However, you can't import issues and merge requests this way. To retain all metadata like issues and
-merge requests, use the [import/export feature](../settings/import_export.md)
-to export projects from self-managed GitLab and import those projects into GitLab.com. All GitLab
-user associations (such as comment author) are changed to the user importing the project. For more
-information, see the prerequisites and important notes in these sections:
+You can also import any Git repository through HTTP from the **New Project** page. Note that if the
+repository is too large, the import can timeout.
-- [Export a project and its data](../settings/import_export.md#export-a-project-and-its-data).
-- [Import the project](../settings/import_export.md#import-a-project-and-its-data).
+You can then [connect your external repository to get CI/CD benefits](../../../ci/ci_cd_for_external_repos/index.md).
-NOTE:
-When migrating to GitLab.com, you must create users manually unless [SCIM](../../../user/group/saml_sso/scim_setup.md)
-will be used. Creating users with the API is limited to self-managed instances as it requires
-administrator access.
+## Migrate using the API
To migrate all data from self-managed to GitLab.com, you can leverage the [API](../../../api/index.md).
Migrate the assets in this order:
@@ -83,17 +66,9 @@ Migrate the assets in this order:
1. [Projects](../../../api/projects.md)
1. [Project variables](../../../api/project_level_variables.md)
-Keep in mind the limitations of the [import/export feature](../settings/import_export.md#items-that-are-exported).
-
You must still migrate your [Container Registry](../../packages/container_registry/index.md)
over a series of Docker pulls and pushes. Re-run any CI pipelines to retrieve any build artifacts.
-## Migrate from GitLab.com to self-managed GitLab
-
-The process is essentially the same as [migrating from self-managed GitLab to GitLab.com](#migrate-from-self-managed-gitlab-to-gitlabcom).
-The main difference is that an administrator can create users on the self-managed GitLab instance
-through the UI or the [users API](../../../api/users.md#user-creation).
-
## Migrate between two self-managed GitLab instances
To migrate from an existing self-managed GitLab instance to a new self-managed GitLab instance, it's
@@ -105,12 +80,36 @@ The backups produced don't depend on the operating system running GitLab. You ca
the restore method to switch between different operating system distributions or versions, as long
as the same GitLab version [is available for installation](../../../administration/package_information/supported_os.md).
-To instead merge two self-managed GitLab instances together, use the instructions in
-[Migrate from self-managed GitLab to GitLab.com](#migrate-from-self-managed-gitlab-to-gitlabcom).
-This method is useful when both self-managed instances have existing data that must be preserved.
+Also note that administrators can use the [Users API](../../../api/users.md) to migrate users.
+
+## View project import history
+
+You can view all project imports created by you. This list includes the following:
+
+- Paths of source projects if projects were imported from external systems, or import method if GitLab projects were migrated.
+- Paths of destination projects.
+- Start date of each import.
+- Status of each import.
+- Error details if any errors occurred.
+
+To view project import history:
+
+1. Sign in to GitLab.
+1. On the top bar, select **Create new...** (**{plus-square}**).
+1. Select **New project/repository**.
+1. Select **Import project**.
+1. Select **History** in the upper right corner.
+1. If there are any errors for a particular import, you can see them by selecting **Details**.
+
+The history also includes projects created from [built-in](../working_with_projects.md#create-a-project-from-a-built-in-template)
+or [custom](../working_with_projects.md#create-a-project-from-a-built-in-template)
+templates. GitLab uses [import repository by URL](repo_by_url.md)
+to create a new project from a template.
+
+## LFS authentication
-Also note that administrators can use the [Users API](../../../api/users.md)
-to migrate users.
+When importing a project that contains LFS objects, if the project has an [`.lfsconfig`](https://github.com/git-lfs/git-lfs/blob/master/docs/man/git-lfs-config.5.ronn)
+file with a URL host (`lfs.url`) different from the repository URL host, LFS files are not downloaded.
## Project aliases **(PREMIUM SELF)**
diff --git a/doc/user/project/import/svn.md b/doc/user/project/import/svn.md
index 1687d621e2e..6730ef862e6 100644
--- a/doc/user/project/import/svn.md
+++ b/doc/user/project/import/svn.md
@@ -1,91 +1,11 @@
---
-type: howto
-stage: Manage
-group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+redirect_to: 'index.md'
+remove_date: '2023-03-15'
---
-# Migrate from Subversion to GitLab **(FREE)**
+This document was moved to [another location](index.md).
-GitLab uses Git as its version control system. If you're using Subversion (SVN) as your version control system,
-you can migrate to using a Git repository in GitLab using `svn2git`.
-
-You can follow the steps on this page to migrate to Git if your SVN repository:
-
-- Has a standard format (trunk, branches, and tags).
-- Is not nested.
-
-For a non-standard repository see the [`svn2git` documentation](https://github.com/nirvdrum/svn2git).
-
-We recommend a hard cut over from SVN to Git and GitLab. Run the migration command once and then have all users use the
-new GitLab repository immediately.
-
-## Install `svn2git`
-
-Install `svn2git` on a local workstation rather than the GitLab server:
-
-- On all systems you can install as a Ruby gem if you already have Ruby and Git installed:
-
- ```shell
- sudo gem install svn2git
- ```
-
-- On Debian-based Linux distributions you can install the native packages:
-
- ```shell
- sudo apt-get install git-core git-svn ruby
- ```
-
-## Prepare an authors file (recommended)
-
-Prepare an authors file so `svn2git` can map SVN authors to Git authors. If you choose not to create the authors file,
-commits are not attributed to the correct GitLab user.
-
-To map authors, you must map every author present on changes in the SVN repository. If you don't, the
-migration fails and you have to update the author file accordingly.
-
-1. Search through the SVN repository and output a list of authors:
-
- ```shell
- svn log --quiet | grep -E "r[0-9]+ \| .+ \|" | cut -d'|' -f2 | sed 's/ //g' | sort | uniq
- ```
-
-1. Use the output from the last command to construct the authors file. Create a file called `authors.txt` and add one
- mapping per line. For example:
-
- ```plaintext
- sidneyjones = Sidney Jones <sidneyjones@example.com>
- ```
-
-## Migrate SVN repository to Git repository
-
-`svn2git` supports excluding certain file paths, branches, tags, and more. See
-the [`svn2git` documentation](https://github.com/nirvdrum/svn2git) or run `svn2git --help` for full documentation on all of
-the available options.
-
-For each repository to migrate:
-
-1. Create a new directory and change into it.
-1. For repositories that:
-
- - Don't require a username and password, run:
-
- ```shell
- svn2git https://svn.example.com/path/to/repo --authors /path/to/authors.txt
- ```
-
- - Do require a username and password, run:
-
- ```shell
- svn2git https://svn.example.com/path/to/repo --authors /path/to/authors.txt --username <username> --password <password>
- ```
-
-1. Create a new GitLab project for your migrated code.
-1. Copy the SSH or HTTP(S) repository URL from the GitLab project page.
-1. Add the GitLab repository as a Git remote and push all the changes. This pushes all commits, branches, and tags.
-
- ```shell
- git remote add origin git@gitlab.example.com:<group>/<project>.git
- git push --all origin
- git push --tags origin
- ```
+<!-- This redirect file can be deleted after <2023-03-15>. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/user/project/integrations/bamboo.md b/doc/user/project/integrations/bamboo.md
index fceec006a1a..db90bafaaa5 100644
--- a/doc/user/project/integrations/bamboo.md
+++ b/doc/user/project/integrations/bamboo.md
@@ -63,7 +63,7 @@ Bamboo. For example, `https://bamboo.example.com/browse/PROJ-PLAN`.
## Update Bamboo build status in GitLab
-You can use a script that uses the [commit status API](../../../api/commits.md#post-the-build-status-to-a-commit)
+You can use a script that uses the [commit status API](../../../api/commits.md#set-the-pipeline-status-of-a-commit)
and Bamboo build variables to:
- Update the commit with the build status.
diff --git a/doc/user/project/integrations/gitlab_slack_application.md b/doc/user/project/integrations/gitlab_slack_application.md
index 8d6fdf882b7..50b52421a5a 100644
--- a/doc/user/project/integrations/gitlab_slack_application.md
+++ b/doc/user/project/integrations/gitlab_slack_application.md
@@ -10,7 +10,7 @@ NOTE:
The GitLab for Slack app is only configurable for GitLab.com. It does **not**
work for on-premises installations where you can configure
[Slack slash commands](slack_slash_commands.md) instead. See
-[Slack application integration for self-managed instances](https://gitlab.com/gitlab-org/gitlab/-/issues/28164)
+[Slack application integration for self-managed instances](https://gitlab.com/groups/gitlab-org/-/epics/1211)
for our plans to make the app configurable for all GitLab installations.
Slack provides a native application that you can enable with your project's
@@ -36,12 +36,12 @@ workspace to be able to install a new application. See
To enable the GitLab integration for your Slack workspace:
-1. Go to your project's **Settings > Integration > Slack application** (only
+1. Go to your project's **Settings > Integration > GitLab for Slack app** (only
visible on GitLab.com).
-1. Select **Install Slack app**.
+1. Select **Install GitLab for Slack app**.
1. Select **Allow** on Slack's confirmation screen.
-You can also select **Reinstall Slack app** to update the app in your Slack workspace
+You can also select **Reinstall GitLab for Slack app** to update the app in your Slack workspace
to the latest version. See [Version history](#version-history) for details.
## Create a project alias for Slack
@@ -50,7 +50,7 @@ To create a project alias on GitLab.com for Slack integration:
1. Go to your project's home page.
1. Go to **Settings > Integrations** (only visible on GitLab.com).
-1. On the **Integrations** page, select **Slack application**.
+1. On the **Integrations** page, select **GitLab for Slack app**.
1. The current **Project Alias**, if any, is displayed. To edit this value,
select **Edit**.
1. Enter your desired alias, and select **Save changes**.
@@ -91,7 +91,7 @@ As a workaround, ensure your app is up to date.
To update an existing Slack integration:
1. Go to your [chat settings](https://gitlab.com/-/profile/chat_names).
-1. Next to your project, select **Slack application**.
-1. Select **Reinstall Slack app**.
+1. Next to your project, select **GitLab for Slack app**.
+1. Select **Reinstall GitLab for Slack app**.
Alternatively, you can [configure a new Slack integration](https://about.gitlab.com/solutions/slack/).
diff --git a/doc/user/project/integrations/index.md b/doc/user/project/integrations/index.md
index 77444570499..769a45fc6ff 100644
--- a/doc/user/project/integrations/index.md
+++ b/doc/user/project/integrations/index.md
@@ -58,7 +58,6 @@ You can configure the following integrations.
| [Emails on push](emails_on_push.md) | Send commits and diff of each push by email. | **{dotted-circle}** No |
| [EWM](ewm.md) | Use IBM Engineering Workflow Management as the issue tracker. | **{dotted-circle}** No |
| [External wiki](../wiki/index.md#link-an-external-wiki) | Link an external wiki. | **{dotted-circle}** No |
-| [Flowdock](../../../api/integrations.md#flowdock) | Send notifications from GitLab to Flowdock flows. | **{dotted-circle}** No |
| [GitHub](github.md) | Obtain statuses for commits and pull requests. | **{dotted-circle}** No |
| [Google Chat](hangouts_chat.md) | Send notifications from your GitLab project to a room in Google Chat. | **{dotted-circle}** No |
| [Harbor](harbor.md) | Use Harbor as the container registry. | **{dotted-circle}** No |
@@ -77,7 +76,7 @@ You can configure the following integrations.
| Pushover | Get real-time notifications on your device. | **{dotted-circle}** No |
| [Redmine](redmine.md) | Use Redmine as the issue tracker. | **{dotted-circle}** No |
| [Shimo Workspace](shimo.md) | Use Shimo instead of the GitLab Wiki. | **{dotted-circle}** No |
-| [Slack application](gitlab_slack_application.md) | Use Slack's official GitLab application. | **{dotted-circle}** No |
+| [GitLab for Slack app](gitlab_slack_application.md) | Use Slack's official GitLab application. | **{dotted-circle}** No |
| [Slack notifications](slack.md) | Send notifications about project events to Slack. | **{dotted-circle}** No |
| [Slack slash commands](slack_slash_commands.md) | Enable slash commands in a workspace. | **{dotted-circle}** No |
| [Unify Circuit](unify_circuit.md) | Send notifications about project events to Unify Circuit. | **{dotted-circle}** No |
diff --git a/doc/user/project/integrations/mlflow_client.md b/doc/user/project/integrations/mlflow_client.md
index 82bfd08e926..bd14021ab1c 100644
--- a/doc/user/project/integrations/mlflow_client.md
+++ b/doc/user/project/integrations/mlflow_client.md
@@ -19,9 +19,9 @@ Setting up your integrations requires minimal changes to existing code.
GitLab plays the role of proxy server, both for artifact storage and tracking data. It reflects the
MLFlow [Scenario 5](https://www.mlflow.org/docs/latest/tracking.html#scenario-5-mlflow-tracking-server-enabled-with-proxied-artifact-storage-access).
-## Enable MFlow Client Integration
+## Enable MLFlow Client Integration
-Complete this task to enable MFlow Client Integration.
+Complete this task to enable MLFlow Client Integration.
Prerequisites:
@@ -49,17 +49,17 @@ that can be explored by selecting an experiment.
- The API GitLab supports is the one defined at MLFlow version 1.28.0.
- API endpoints not listed above are not supported.
-- During creation of experiments and runs, tags are ExperimentTags and RunTags are ignored.
-- MLFLow Model Registry is not supported.
+- During creation of experiments and runs, tags are ExperimentTags and RunTags are stored, even though they are not displayed.
+- MLFlow Model Registry is not supported.
## Supported methods and caveats
This is a list of methods we support from the MLFlow client. Other methods might be supported but were not
tested. More information can be found in the [MLFlow Documentation](https://www.mlflow.org/docs/1.28.0/python_api/mlflow.html).
-### `set_experiment`
+### `set_experiment()`
-Accepts both experiment_name and experiment_id
+Accepts both `experiment_name` and `experiment_id`
### `start_run()`
diff --git a/doc/user/project/integrations/slack.md b/doc/user/project/integrations/slack.md
index d34c558ebbc..2ded5799c23 100644
--- a/doc/user/project/integrations/slack.md
+++ b/doc/user/project/integrations/slack.md
@@ -36,10 +36,6 @@ to control GitLab from Slack. Slash commands are configured separately.
- *To send messages to channels,* enter the Slack channel names, separated by
commas.
- *To send direct messages,* use the Member ID found in the user's Slack profile.
-
- NOTE:
- Usernames and private channels are not supported.
-
1. In **Webhook**, enter the webhook URL you copied in the
[Slack configuration](#configure-slack) step.
1. Optional. In **Username**, enter the username of the Slack bot that sends
diff --git a/doc/user/project/integrations/webhook_events.md b/doc/user/project/integrations/webhook_events.md
index 60187b9a682..63282d6ec6e 100644
--- a/doc/user/project/integrations/webhook_events.md
+++ b/doc/user/project/integrations/webhook_events.md
@@ -1019,7 +1019,7 @@ Payload example:
```
NOTE:
-The fields `assignee_id`, `state`, `merge_status` are deprecated.
+The fields `assignee_id`, `state`, `merge_status` are [deprecated](../../../api/merge_requests.md).
## Wiki page events
@@ -1171,7 +1171,7 @@ Payload example:
"project":{
"id": 41,
"web_url": "https://gitlab.example.com/gitlab-org/upstream-project",
- "path_with_namespace": "gitlab-org/upstream-project",
+ "path_with_namespace": "gitlab-org/upstream-project"
},
"pipeline_id": 30,
"job_id": 3401
@@ -1475,6 +1475,8 @@ Payload example:
"deployable_id": 796,
"deployable_url": "http://10.126.0.2:3000/root/test-deployment-webhooks/-/jobs/796",
"environment": "staging",
+ "environment_slug": "staging",
+ "environment_external_url": "https://staging.example.com",
"project": {
"id": 30,
"name": "test-deployment-webhooks",
diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md
index ef6957ac2d8..3d45e947c4c 100644
--- a/doc/user/project/integrations/webhooks.md
+++ b/doc/user/project/integrations/webhooks.md
@@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
[Webhooks](https://en.wikipedia.org/wiki/Webhook) are custom HTTP callbacks
that you define. They are usually triggered by an
-event, such as pushing code to a repository or posting a comment on a blog.
+event, such as pushing code to a repository or posting a comment on an issue.
When the event occurs, the source app makes an HTTP request to the URI
configured for the webhook. The action to take may be anything. For example,
you can use webhooks to:
@@ -52,7 +52,7 @@ specific to a group, including:
## Configure a webhook in GitLab
-You can configure a webhook for a group or a project.
+To configure a webhook for a project or group:
1. In your project or group, on the left sidebar, select **Settings > Webhooks**.
1. In **URL**, enter the URL of the webhook endpoint.
@@ -62,6 +62,40 @@ You can configure a webhook for a group or a project.
1. Optional. Clear the **Enable SSL verification** checkbox to disable [SSL verification](index.md#manage-ssl-verification).
1. Select **Add webhook**.
+## Mask sensitive portions of webhook URLs
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/99995) in GitLab 15.5 [with a flag](../../../administration/feature_flags.md) named `webhook_form_mask_url`. Disabled by default.
+> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/376106) in GitLab 15.6.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/376106) in GitLab 15.7. Feature flag `webhook_form_mask_url` removed.
+
+You can define and mask sensitive portions of webhook URLs and replace them
+with configured values any number of times when webhooks are executed.
+Sensitive portions do not get logged and are encrypted at rest in the database.
+
+To mask sensitive portions of the webhook URL:
+
+1. In your project or group, on the left sidebar, select **Settings > Webhooks**.
+1. In **URL**, enter the full webhook URL.
+1. Select **Mask portions of URL**.
+1. In **Sensitive portion of URL**, enter the portion you want to mask.
+1. In **How it looks in the UI**, enter the masking value.
+
+To interpolate sensitive portions for each webhook, use `url_variables`.
+For example, if a webhook has the following URL:
+
+```plaintext
+https://{subdomain}.example.com/{path}?key={value}
+```
+
+You must define the following variables:
+
+- `subdomain`
+- `path`
+- `value`
+
+Variable names can contain only lowercase letters (`a-z`), numbers (`0-9`), or underscores (`_`).
+You can define URL variables directly using the REST API.
+
## Configure your webhook receiver endpoint
Webhook receiver endpoints should be fast and stable.
@@ -86,27 +120,22 @@ Endpoints should follow these best practices:
### Failing webhooks
-> Introduced in GitLab 13.12 [with a flag](../../../administration/feature_flags.md) named `web_hooks_disable_failed`. Disabled by default.
-
-FLAG:
-On self-managed GitLab, by default this feature is not available. To make it available,
-ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `web_hooks_disable_failed`.
-On GitLab.com, this feature is not available.
-The feature is not ready for production use.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60837) in GitLab 13.12 [with a flag](../../../administration/feature_flags.md) named `web_hooks_disable_failed`. Disabled by default.
+> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/329849) in GitLab 15.7.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/329849) in GitLab 15.7. Feature flag `web_hooks_disable_failed` removed.
If a webhook fails repeatedly, it may be disabled automatically.
Webhooks that return response codes in the `5xx` range are understood to be failing
-intermittently, and are temporarily disabled. This lasts initially
-for 10 minutes. If the hook continues to fail, the back-off period is
-extended on each retry, up to a maximum disabled period of 24 hours.
+intermittently and are temporarily disabled. These webhooks are initially disabled
+for 1 minute, which is extended on each retry up to a maximum of 24 hours.
-Webhooks that return failure codes in the `4xx` range are understood to be
-misconfigured, and these are disabled until you manually re-enable
-them. These webhooks are not automatically retried.
+Webhooks that return response codes in the `4xx` range are understood to be
+misconfigured and are permanently disabled until you manually re-enable
+them yourself.
-See [troubleshooting](#troubleshoot-webhooks) for information on
-how to see if a webhook is disabled, and how to re-enable it.
+See [Troubleshooting](#troubleshoot-webhooks) for more information on
+disabled webhooks and how to re-enable them.
## Test a webhook
@@ -190,10 +219,10 @@ You can filter push events by branch. Use one of the following options to filter
- **All branches**: push events from all branches.
- **Wildcard pattern**: push events from a branch that matches a wildcard pattern (for example, `*-stable` or `production/*`).
-- **Regular expression**: push events from a branch that matches a regular expression (for example, `(feature|hotfix)/*`).
+- **Regular expression**: push events from a branch that matches a regular expression (for example, `^(feature|hotfix)/`).
-You can configure branch filtering
-in the [webhook settings](#configure-a-webhook-in-gitlab) in your project.
+To configure branch filtering for a project or group, see
+[Configure a webhook in GitLab](#configure-a-webhook-in-gitlab).
## How image URLs are displayed in the webhook body
@@ -302,13 +331,7 @@ GitLab expects a response in [10 seconds](../../../user/gitlab_com/index.md#othe
### Re-enable disabled webhooks
> - Introduced in GitLab 15.2 [with a flag](../../../administration/feature_flags.md) named `webhooks_failed_callout`. Disabled by default.
-> - The [`web_hooks_disable_failed` flag](#failing-webhooks) must also be enabled for this feature to work. Disabled by default.
-
-FLAG:
-On self-managed GitLab, by default this feature is not available. To make it available,
-ask an administrator to [enable the feature flags](../../../administration/feature_flags.md) named `webhooks_failed_callout` and `web_hooks_disable_failed`.
-On GitLab.com, this feature is not available.
-The feature is not ready for production use.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/365535) in GitLab 15.7. Feature flag `webhooks_failed_callout` removed.
If a webhook is failing, a banner displays at the top of the edit page explaining
why it is disabled, and when it will be automatically re-enabled. For example:
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index c2952b23615..234faa893eb 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -463,6 +463,7 @@ You can edit the following issue attributes in the right sidebar:
- Confidentiality
- Due date
- [Epic](../group/epics/index.md)
+- [Health status](issues/managing_issues.md#health-status)
- [Iteration](../group/iterations/index.md)
- Labels
- Milestone
diff --git a/doc/user/project/issues/create_issues.md b/doc/user/project/issues/create_issues.md
new file mode 100644
index 00000000000..3c2e20c1250
--- /dev/null
+++ b/doc/user/project/issues/create_issues.md
@@ -0,0 +1,221 @@
+---
+stage: Plan
+group: Project Management
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Create an issue **(FREE)**
+
+When you create an issue, you are prompted to enter the fields of the issue.
+If you know the values you want to assign to an issue, you can use
+[quick actions](../quick_actions.md) to enter them.
+
+You can create an issue in many ways in GitLab:
+
+- [From a project](#from-a-project)
+- [From a group](#from-a-group)
+- [From another issue or incident](#from-another-issue-or-incident)
+- [From an issue board](#from-an-issue-board)
+- [By sending an email](#by-sending-an-email)
+- [Using a URL with prefilled values](#using-a-url-with-prefilled-values)
+- [Using Service Desk](#using-service-desk)
+
+## From a project
+
+Prerequisites:
+
+- You must have at least the Guest role for the project.
+
+To create an issue:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. Either:
+
+ - On the left sidebar, select **Issues**, and then, in the top right corner, select **New issue**.
+ - On the top bar, select the plus sign (**{plus-square}**) and then, under **This project**,
+ select **New issue**.
+
+1. Complete the [fields](#fields-in-the-new-issue-form).
+1. Select **Create issue**.
+
+The newly created issue opens.
+
+## From a group
+
+Issues belong to projects, but when you're in a group, you can access and create issues that belong
+to the projects in the group.
+
+Prerequisites:
+
+- You must have at least the Guest role for the project in the group.
+
+To create an issue from a group:
+
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Issues**.
+1. In the top right corner, select **Select project to create issue**.
+1. Select the project you'd like to create an issue for. The button now reflects the selected
+ project.
+1. Select **New issue in `<project name>`**.
+1. Complete the [fields](#fields-in-the-new-issue-form).
+1. Select **Create issue**.
+
+The newly created issue opens.
+
+The project you selected most recently becomes the default for your next visit.
+This can save you a lot of time, if you mostly create issues for the same project.
+
+## From another issue or incident
+
+> - New issue becoming linked to the issue of origin [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68226) in GitLab 14.3.
+> - **Relate to…** checkbox [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/198494) in GitLab 14.9.
+
+You can create a new issue from an existing one. The two issues can then be marked as related.
+
+Prerequisites:
+
+- You must have at least the Guest role for the project.
+
+To create an issue from another issue:
+
+1. In an existing issue, select the vertical ellipsis (**{ellipsis_v}**).
+1. Select **New related issue**.
+1. Complete the [fields](#fields-in-the-new-issue-form).
+ The new issue form has a **Relate to issue #123** checkbox, where `123` is the ID of the
+ issue of origin. If you keep this checkbox checked, the two issues become
+ [linked](related_issues.md).
+1. Select **Create issue**.
+
+The newly created issue opens.
+
+## From an issue board
+
+You can create a new issue from an [issue board](../issue_board.md).
+
+Prerequisites:
+
+- You must have at least the Guest role for the project.
+
+To create an issue from a project issue board:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. Select **Issues > Boards**.
+1. At the top of a board list, select **New issue** (**{plus-square}**).
+1. Enter the issue's title.
+1. Select **Create issue**.
+
+To create an issue from a group issue board:
+
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. Select **Issues > Boards**.
+1. At the top of a board list, select **New issue** (**{plus-square}**).
+1. Enter the issue's title.
+1. Under **Projects**, select the project in the group that the issue should belong to.
+1. Select **Create issue**.
+
+The issue is created and shows up in the board list. It shares the list's characteristic, so, for
+example, if the list is scoped to a label `Frontend`, the new issue also has this label.
+
+## By sending an email
+
+> - Generated email address format changed in GitLab 11.7.
+> - The older format is still supported, so existing aliases and contacts still work.
+
+You can send an email to create an issue in a project on the project's
+**Issues List** page.
+
+Prerequisites:
+
+- Your GitLab instance must have [incoming email](../../../administration/incoming_email.md)
+ configured.
+- There must be at least one issue in the issue list.
+- You must have at least the Guest role for the project.
+
+To email an issue to a project:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. Select **Issues**.
+1. At the bottom of the page, select **Email a new issue to this project**.
+1. To copy the email address, select **Copy** (**{copy-to-clipboard}**).
+1. From your email client, send an email to this address.
+ The subject is used as the title of the new issue, and the email body becomes the description.
+ You can use [Markdown](../../markdown.md) and [quick actions](../quick_actions.md).
+
+A new issue is created, with your user as the author.
+You can save this address as a contact in your email client to use it again.
+
+WARNING:
+The email address you see is a private email address, generated just for you.
+**Keep it to yourself**, because anyone who knows it can create issues or merge requests as if they
+were you.
+
+To regenerate the email address:
+
+1. On the issues list, select **Email a new issue to this project**.
+1. Select **reset this token**.
+
+## Using a URL with prefilled values
+
+> - Ability to use both `issuable_template` and `issue[description]` in the same URL [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80554) in GitLab 14.9.
+> - Ability to specify `add_related_issue` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/198494) in GitLab 14.9.
+
+To link directly to the new issue page with prefilled fields, use query
+string parameters in a URL. You can embed a URL in an external
+HTML page to create issues with certain fields prefilled.
+
+| Field | URL parameter | Notes |
+| -------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
+| Title | `issue[title]` | Must be [URL-encoded](../../../api/index.md#namespaced-path-encoding). |
+| Issue type | `issue[issue_type]` | Either `incident` or `issue`. |
+| Description template | `issuable_template` | Must be [URL-encoded](../../../api/index.md#namespaced-path-encoding). |
+| Description | `issue[description]` | Must be [URL-encoded](../../../api/index.md#namespaced-path-encoding). If used in combination with `issuable_template` or a [default issue template](../description_templates.md#set-a-default-template-for-merge-requests-and-issues), the `issue[description]` value is appended to the template. |
+| Confidential | `issue[confidential]` | If `true`, the issue is marked as confidential. |
+| Relate to… | `add_related_issue` | A numeric issue ID. If present, the issue form shows a [**Relate to…** checkbox](#from-another-issue-or-incident) to optionally link the new issue to the specified existing issue. |
+
+Adapt these examples to form your new issue URL with prefilled fields.
+To create an issue in the GitLab project:
+
+- With a prefilled title and description:
+
+ ```plaintext
+ https://gitlab.com/gitlab-org/gitlab/-/issues/new?issue[title]=Whoa%2C%20we%27re%20half-way%20there&issue[description]=Whoa%2C%20livin%27%20in%20a%20URL
+ ```
+
+- With a prefilled title and description template:
+
+ ```plaintext
+ https://gitlab.com/gitlab-org/gitlab/-/issues/new?issue[title]=Validate%20new%20concept&issuable_template=Feature%20Proposal%20-%20basic
+ ```
+
+- With a prefilled title, description, and marked as confidential:
+
+ ```plaintext
+ https://gitlab.com/gitlab-org/gitlab/-/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea&issue[confidential]=true
+ ```
+
+## Using Service Desk
+
+To offer email support, enable [Service Desk](../service_desk.md) for your project.
+
+Now, when your customer sends a new email, a new issue can be created in
+the appropriate project and followed up from there.
+
+### Fields in the new issue form
+
+> - Adding the new issue to an epic [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13847) in GitLab 13.1.
+> - Iteration field [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/233517) in GitLab 15.6.
+
+When you're creating a new issue, you can complete the following fields:
+
+- Title
+- Type: either issue (default) or incident
+- [Description template](../description_templates.md): overwrites anything in the Description text box
+- Description: you can use [Markdown](../../markdown.md) and [quick actions](../quick_actions.md)
+- Checkbox to make the issue [confidential](confidential_issues.md)
+- [Assignees](managing_issues.md#assignee)
+- [Weight](issue_weight.md)
+- [Epic](../../group/epics/index.md)
+- [Due date](due_dates.md)
+- [Milestone](../milestones/index.md)
+- [Labels](../labels.md)
+- [Iteration](../../group/iterations/index.md)
diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md
index 09067b69696..6c9a645d817 100644
--- a/doc/user/project/issues/index.md
+++ b/doc/user/project/issues/index.md
@@ -29,7 +29,7 @@ To learn how the GitLab Strategic Marketing department uses GitLab issues with [
## Related topics
-- [Create issues](managing_issues.md#create-an-issue)
+- [Create issues](create_issues.md)
- [Create an issue from a template](../../project/description_templates.md#use-the-templates)
- [Edit issues](managing_issues.md#edit-an-issue)
- [Move issues](managing_issues.md#move-an-issue)
diff --git a/doc/user/project/issues/issue_weight.md b/doc/user/project/issues/issue_weight.md
index 1ba5a4415e0..d852ad3262b 100644
--- a/doc/user/project/issues/issue_weight.md
+++ b/doc/user/project/issues/issue_weight.md
@@ -36,7 +36,7 @@ When you change the weight of an issue, the new value overwrites the previous va
### When you create an issue
-To set the issue weight when you [create an issue](managing_issues.md#create-an-issue), enter a
+To set the issue weight when you [create an issue](create_issues.md), enter a
number under **Weight**.
### From an existing issue
diff --git a/doc/user/project/issues/managing_issues.md b/doc/user/project/issues/managing_issues.md
index 8cd211a51c7..ea90dda88f6 100644
--- a/doc/user/project/issues/managing_issues.md
+++ b/doc/user/project/issues/managing_issues.md
@@ -6,224 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Manage issues **(FREE)**
-[GitLab Issues](index.md) are the fundamental medium for collaborating on ideas and
-planning work in GitLab.
-
-## Create an issue
-
-When you create an issue, you are prompted to enter the fields of the issue.
-If you know the values you want to assign to an issue, you can use
-[quick actions](../quick_actions.md) to enter them.
-
-You can create an issue in many ways in GitLab:
-
-- [From a project](#from-a-project)
-- [From a group](#from-a-group)
-- [From another issue or incident](#from-another-issue-or-incident)
-- [From an issue board](#from-an-issue-board)
-- [By sending an email](#by-sending-an-email)
-- [Using a URL with prefilled values](#using-a-url-with-prefilled-values)
-- [Using Service Desk](#using-service-desk)
-
-### From a project
-
-Prerequisites:
-
-- You must have at least the Guest role for the project.
-
-To create an issue:
-
-1. On the top bar, select **Main menu > Projects** and find your project.
-1. Either:
-
- - On the left sidebar, select **Issues**, and then, in the top right corner, select **New issue**.
- - On the top bar, select the plus sign (**{plus-square}**) and then, under **This project**,
- select **New issue**.
-
-1. Complete the [fields](#fields-in-the-new-issue-form).
-1. Select **Create issue**.
-
-The newly created issue opens.
-
-### From a group
-
-Issues belong to projects, but when you're in a group, you can access and create issues that belong
-to the projects in the group.
-
-Prerequisites:
-
-- You must have at least the Guest role for the project in the group.
-
-To create an issue from a group:
-
-1. On the top bar, select **Main menu > Groups** and find your group.
-1. On the left sidebar, select **Issues**.
-1. In the top right corner, select **Select project to create issue**.
-1. Select the project you'd like to create an issue for. The button now reflects the selected
- project.
-1. Select **New issue in `<project name>`**.
-1. Complete the [fields](#fields-in-the-new-issue-form).
-1. Select **Create issue**.
-
-The newly created issue opens.
-
-The project you selected most recently becomes the default for your next visit.
-This can save you a lot of time, if you mostly create issues for the same project.
-
-### From another issue or incident
-
-> - New issue becoming linked to the issue of origin [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68226) in GitLab 14.3.
-> - **Relate to…** checkbox [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/198494) in GitLab 14.9.
-
-You can create a new issue from an existing one. The two issues can then be marked as related.
-
-Prerequisites:
-
-- You must have at least the Guest role for the project.
-
-To create an issue from another issue:
-
-1. In an existing issue, select the vertical ellipsis (**{ellipsis_v}**).
-1. Select **New related issue**.
-1. Complete the [fields](#fields-in-the-new-issue-form).
- The new issue form has a **Relate to issue #123** checkbox, where `123` is the ID of the
- issue of origin. If you keep this checkbox checked, the two issues become
- [linked](related_issues.md).
-1. Select **Create issue**.
-
-The newly created issue opens.
-
-### From an issue board
-
-You can create a new issue from an [issue board](../issue_board.md).
-
-Prerequisites:
-
-- You must have at least the Guest role for the project.
-
-To create an issue from a project issue board:
-
-1. On the top bar, select **Main menu > Projects** and find your project.
-1. Select **Issues > Boards**.
-1. At the top of a board list, select **New issue** (**{plus-square}**).
-1. Enter the issue's title.
-1. Select **Create issue**.
-
-To create an issue from a group issue board:
-
-1. On the top bar, select **Main menu > Groups** and find your group.
-1. Select **Issues > Boards**.
-1. At the top of a board list, select **New issue** (**{plus-square}**).
-1. Enter the issue's title.
-1. Under **Projects**, select the project in the group that the issue should belong to.
-1. Select **Create issue**.
-
-The issue is created and shows up in the board list. It shares the list's characteristic, so, for
-example, if the list is scoped to a label `Frontend`, the new issue also has this label.
-
-### By sending an email
-
-> - Generated email address format changed in GitLab 11.7.
-> - The older format is still supported, so existing aliases and contacts still work.
-
-You can send an email to create an issue in a project on the project's
-**Issues List** page.
-
-Prerequisites:
-
-- Your GitLab instance must have [incoming email](../../../administration/incoming_email.md)
- configured.
-- There must be at least one issue in the issue list.
-- You must have at least the Guest role for the project.
-
-To email an issue to a project:
-
-1. On the top bar, select **Main menu > Projects** and find your project.
-1. Select **Issues**.
-1. At the bottom of the page, select **Email a new issue to this project**.
-1. To copy the email address, select **Copy** (**{copy-to-clipboard}**).
-1. From your email client, send an email to this address.
- The subject is used as the title of the new issue, and the email body becomes the description.
- You can use [Markdown](../../markdown.md) and [quick actions](../quick_actions.md).
-
-A new issue is created, with your user as the author.
-You can save this address as a contact in your email client to use it again.
-
-WARNING:
-The email address you see is a private email address, generated just for you.
-**Keep it to yourself**, because anyone who knows it can create issues or merge requests as if they
-were you.
-
-To regenerate the email address:
-
-1. On the issues list, select **Email a new issue to this project**.
-1. Select **reset this token**.
-
-### Using a URL with prefilled values
-
-> - Ability to use both `issuable_template` and `issue[description]` in the same URL [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80554) in GitLab 14.9.
-> - Ability to specify `add_related_issue` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/198494) in GitLab 14.9.
-
-To link directly to the new issue page with prefilled fields, use query
-string parameters in a URL. You can embed a URL in an external
-HTML page to create issues with certain fields prefilled.
-
-| Field | URL parameter | Notes |
-| -------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
-| Title | `issue[title]` | Must be [URL-encoded](../../../api/index.md#namespaced-path-encoding). |
-| Issue type | `issue[issue_type]` | Either `incident` or `issue`. |
-| Description template | `issuable_template` | Must be [URL-encoded](../../../api/index.md#namespaced-path-encoding). |
-| Description | `issue[description]` | Must be [URL-encoded](../../../api/index.md#namespaced-path-encoding). If used in combination with `issuable_template` or a [default issue template](../description_templates.md#set-a-default-template-for-merge-requests-and-issues), the `issue[description]` value is appended to the template. |
-| Confidential | `issue[confidential]` | If `true`, the issue is marked as confidential. |
-| Relate to… | `add_related_issue` | A numeric issue ID. If present, the issue form shows a [**Relate to…** checkbox](#from-another-issue-or-incident) to optionally link the new issue to the specified existing issue. |
-
-Adapt these examples to form your new issue URL with prefilled fields.
-To create an issue in the GitLab project:
-
-- With a prefilled title and description:
-
- ```plaintext
- https://gitlab.com/gitlab-org/gitlab/-/issues/new?issue[title]=Whoa%2C%20we%27re%20half-way%20there&issue[description]=Whoa%2C%20livin%27%20in%20a%20URL
- ```
-
-- With a prefilled title and description template:
-
- ```plaintext
- https://gitlab.com/gitlab-org/gitlab/-/issues/new?issue[title]=Validate%20new%20concept&issuable_template=Feature%20Proposal%20-%20basic
- ```
-
-- With a prefilled title, description, and marked as confidential:
-
- ```plaintext
- https://gitlab.com/gitlab-org/gitlab/-/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea&issue[confidential]=true
- ```
-
-### Using Service Desk
-
-To offer email support, enable [Service Desk](../service_desk.md) for your project.
-
-Now, when your customer sends a new email, a new issue can be created in
-the appropriate project and followed up from there.
-
-### Fields in the new issue form
-
-> - Adding the new issue to an epic [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13847) in GitLab 13.1.
-> - Iteration field [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/233517) in GitLab 15.6.
-
-When you're creating a new issue, you can complete the following fields:
-
-- Title
-- Type: either issue (default) or incident
-- [Description template](../description_templates.md): overwrites anything in the Description text box
-- Description: you can use [Markdown](../../markdown.md) and [quick actions](../quick_actions.md)
-- Checkbox to make the issue [confidential](confidential_issues.md)
-- [Assignees](#assignee)
-- [Weight](issue_weight.md)
-- [Epic](../../group/epics/index.md)
-- [Due date](due_dates.md)
-- [Milestone](../milestones/index.md)
-- [Labels](../labels.md)
-- [Iteration](../../group/iterations/index.md)
+After you create an issue, you can start working with it.
## Edit an issue
@@ -239,27 +22,7 @@ To edit an issue:
1. Edit the available fields.
1. Select **Save changes**.
-### Reorder list items in the issue description
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15260) in GitLab 15.0.
-
-When you view an issue that has a list in the description, you can also reorder the list items.
-
-Prerequisites:
-
-- You must have at least the Reporter role for the project, be the author of the issue, or be
- assigned to the issue.
-- The issue's description must have an [ordered, unordered](../../markdown.md#lists), or
- [task](../../markdown.md#task-lists) list.
-
-To reorder list items, when viewing an issue:
-
-1. Hover over the list item row to make the drag icon (**{drag-vertical}**) visible.
-1. Select and hold the drag icon.
-1. Drag the row to the new position in the list.
-1. Release the drag icon.
-
-### Bulk edit issues from a project
+## Bulk edit issues from a project
> - Assigning epic [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/210470) in GitLab 13.2.
> - Editing health status [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218395) in GitLab 13.2.
@@ -283,7 +46,7 @@ To edit multiple issues at the same time:
When bulk editing issues in a project, you can edit the following attributes:
- Status (open or closed)
-- [Assignees](#assignee)
+- [Assignees](managing_issues.md#assignee)
- [Epic](../../group/epics/index.md)
- [Milestone](../milestones/index.md)
- [Labels](../labels.md)
@@ -396,6 +159,26 @@ To do it:
1. To exit the Rails console, enter `quit`.
+## Reorder list items in the issue description
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15260) in GitLab 15.0.
+
+When you view an issue that has a list in the description, you can also reorder the list items.
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project, be the author of the issue, or be
+ assigned to the issue.
+- The issue's description must have an [ordered, unordered](../../markdown.md#lists), or
+ [task](../../markdown.md#task-lists) list.
+
+To reorder list items, when viewing an issue:
+
+1. Hover over the list item row to make the drag icon (**{drag-vertical}**) visible.
+1. Select and hold the drag icon.
+1. Drag the row to the new position in the list.
+1. Release the drag icon.
+
## Close an issue
When you decide that an issue is resolved or no longer needed, you can close it.
@@ -514,8 +297,7 @@ Prerequisites:
- You must have [administrator access](../../../administration/index.md) to your GitLab instance.
-To change the default issue closing pattern, edit the
-[`gitlab.rb` or `gitlab.yml` file](../../../administration/issue_closing_pattern.md)
+Learn how to change the default [issue closing pattern](../../../administration/issue_closing_pattern.md).
of your installation.
## Change the issue type
@@ -622,7 +404,7 @@ GitLab displays the results on-screen, but you can also
### Filter with the OR operator
-> OR filtering for assignees was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23532) in GitLab 15.6 [with a flag](../../../administration/feature_flags.md) named `or_issuable_queries`. Disabled by default.
+> OR filtering for author and assignee was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23532) in GitLab 15.6 [with a flag](../../../administration/feature_flags.md) named `or_issuable_queries`. Disabled by default.
FLAG:
On self-managed GitLab, by default this feature is not available.
@@ -633,7 +415,7 @@ When this feature is enabled, you can use the OR operator (**is one of: `||`**)
when you [filter the list of issues](#filter-the-list-of-issues).
`is one of` represents an inclusive OR. For example, if you filter by `Assignee is one of Sidney Jones` and
-`Assignee is one of Zhang Wei`, GitLab shows issues where either Sidney, Zhang, or both of them are assignees.
+`Assignee is one of Zhang Wei`, GitLab shows issues where either `Sidney`, `Zhang`, or both of them are assignees.
### Filter issues by ID
@@ -674,22 +456,6 @@ To copy the issue's email address:
1. Go to the issue.
1. On the right sidebar, next to **Issue email**, select **Copy Reference** (**{copy-to-clipboard}**).
-## Real-time sidebar
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/17589) in GitLab 13.3. Disabled by default.
-> - [Enabled on GitLab.com](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/3413) in GitLab 13.9.
-> - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/17589) in GitLab 14.5.
-> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/17589) in GitLab 14.9. Feature flags `real_time_issue_sidebar` and `broadcast_issue_updates` removed.
-
-Some sections of the right sidebar are updated in real time.
-When you're viewing an issue and somebody changes one of the values,
-you can see the change without having to refresh the page.
-
-The following sections are updated in real time:
-
-- [Assignee](#assignee)
-- [Labels](../labels.md#assign-and-unassign-labels)
-
## Assignee
An issue can be assigned to one or [more users](multiple_assignees_for_issues.md).
@@ -708,6 +474,8 @@ To change the assignee on an issue:
1. From the dropdown list, select the user to add as an assignee.
1. Select any area outside the dropdown list.
+The assignee is changed without having to refresh the page.
+
## Similar issues
To prevent duplication of issues on the same topic, GitLab searches for similar issues
diff --git a/doc/user/project/issues/sorting_issue_lists.md b/doc/user/project/issues/sorting_issue_lists.md
index 6a1a791645e..a2f90d5c444 100644
--- a/doc/user/project/issues/sorting_issue_lists.md
+++ b/doc/user/project/issues/sorting_issue_lists.md
@@ -101,6 +101,19 @@ title in this order:
- Numbers
- Letters: first Latin, then accented (for example, `ö`)
+## Sorting by health status **(ULTIMATE)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/377841) in GitLab 15.7.
+
+When you sort by **Health**, the issue list changes to sort by the
+[health status](managing_issues.md#health-status) of the issues
+When in descending order, the issues are shown in the following order:
+
+1. **At risk** issues
+1. **Needs attention** issues
+1. **On track** issues
+1. All other issues
+
## Sorting by weight
When you sort by **Weight**, the issue list changes to sort ascending by the
diff --git a/doc/user/project/members/index.md b/doc/user/project/members/index.md
index e8ec954df8f..a7627f12657 100644
--- a/doc/user/project/members/index.md
+++ b/doc/user/project/members/index.md
@@ -61,7 +61,7 @@ To add a user to a project:
1. Select **Invite members**.
1. Enter an email address and select a [role](../../permissions.md).
1. Optional. Select an **Access expiration date**.
- From that date onwards, the user can no longer access the project.
+ From that date onward, the user can no longer access the project.
1. Select **Invite**.
If the user has a GitLab account, they are added to the members list.
@@ -110,7 +110,7 @@ To add a group to a project:
1. Select a group.
1. Select the highest [role](../../permissions.md) for users in the group.
1. Optional. Select an **Access expiration date**.
- From that date onwards, the group can no longer access the project.
+ From that date onward, the group can no longer access the project.
1. Select **Invite**.
The members of the group are not displayed on the **Members** tab.
diff --git a/doc/user/project/members/share_project_with_groups.md b/doc/user/project/members/share_project_with_groups.md
index 52cc9fc4f9b..b9887212be0 100644
--- a/doc/user/project/members/share_project_with_groups.md
+++ b/doc/user/project/members/share_project_with_groups.md
@@ -9,83 +9,77 @@ info: To determine the technical writer assigned to the Stage/Group associated w
You can share projects with other [groups](../../group/index.md). This makes it
possible to add a group of users to a project with a single action.
-## Groups as collections of users
+For example, if `Project A` belongs to `Group 1`, the members of `Group 1` have access to the project.
+If `Project A` already belongs to another `Group 2`, the owner of `Group 2` can share `Project A`
+with `Group 1`, so that both members of `Group 1` and `Group 2` have access to the project.
-Groups are used primarily to [create collections of projects](../../group/index.md), but you can also
-take advantage of the fact that groups define collections of _users_, namely the group
-members.
+When a project is shared with a group:
-## Share a project with a group of users
-
-> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 13.11 from a form to a modal
- window [with a flag](../../feature_flags.md). Disabled by default.
-> - Modal window [enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/247208)
- in GitLab 14.8.
-> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/352526) in GitLab 14.9.
- [Feature flag `invite_members_group_modal`](https://gitlab.com/gitlab-org/gitlab/-/issues/352526) removed.
-
-You can share a project only with:
-
-- Groups for which you have an explicitly defined [membership](index.md).
-- Groups that contain a nested subgroup or project for which you have an explicitly defined role.
+- All group members, including members of subgroups or projects that belong to the group,
+are assigned the same role in the project.
+Each member's role is displayed in **Project information > Members > Max role**.
+- The group is listed in the **Groups** tab.
+- The project is listed on the group dashboard.
-Administrators can share projects with any group in the instance.
+Be aware of the restrictions that apply when you share projects with:
-The primary mechanism to give a group of users, say 'Engineering', access to a project,
-say 'Project Acme', in GitLab is to make the 'Engineering' group the owner of 'Project
-Acme'. But what if 'Project Acme' already belongs to another group, say 'Open Source'?
-This is where the group sharing feature can be of use.
+- [Groups with a more restrictive visibility level](#share-projects-with-groups-with-a-more-restrictive-visibility-level).
+- [Restricted sharing](#prevent-project-sharing).
-To share 'Project Acme' with the 'Engineering' group:
+## Share projects with groups with a more restrictive visibility level
-1. For 'Project Acme' use the left navigation menu to go to **Project information > Members**.
-1. Select **Invite a group**.
-1. Add the 'Engineering' group with the maximum access level of your choice.
-1. Optional. Select an **Access expiration date**.
-1. Select **Invite**.
+You can share projects only down the group's organization structure.
+This means you can share a project with a group that has a more restrictive
+[visibility level](../../public_access.md#project-and-group-visibility) than the project,
+but not with a group that has a less restrictive visibility level.
-After sharing 'Project Acme' with 'Engineering':
+For example, you can share:
-- The group is listed in the **Groups** tab.
-- The project is listed on the group dashboard.
-- All members, including members from the ancestors of the 'Engineering' group, gain access to 'Project Acme' with an access level based on the outcome of [maximum access level](#maximum-access-level).
+- A public project with a private group.
+- A public project with an internal group.
+- An internal project with a private group.
-When you share a project, be aware of the following restrictions and outcomes:
+This restriction applies to subgroups as well. For example, `group/subgroup01/project`:
-- [Maximum access level](#maximum-access-level)
-- [Sharing projects with groups of a higher restrictive visibility level](#sharing-projects-with-groups-of-a-higher-restrictive-visibility-level)
-- [Sharing project with group lock](#share-project-with-group-lock)
+- Can't be shared with `group`.
+- Can be shared with `group/subgroup02` or `group/subgroup01/subgroup03`.
-## Maximum access level
+When you share a project with a group that has a more restrictive visibility level than the project:
-In the example above, the maximum access level of 'Developer' for members from 'Engineering' means that users with higher access levels in 'Engineering' ('Maintainer' or 'Owner') only have 'Developer' access to 'Project Acme'.
-
-### Share a project with a subgroup
-
-You can't share a project with a group that's an ancestor of a [subgroup](../../group/subgroups/index.md) the project is
-in. That means you can only share down the hierarchy. For example, `group/subgroup01/project`:
+- The group name is visible to all users that can view the project members page.
+- Owners of the project have access to members of the group when they mention them in issues or merge requests.
+- Project members who are direct or indirect members of the group can see
+group members listed in addition to members of the project.
-- Can not be shared with `group`.
-- Can be shared with `group/subgroup02` or `group/subgroup01/subgroup03`.
+## Share a project with a group
-## Sharing projects with groups of a higher restrictive visibility level
+> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 13.11 from a form to a modal
+ window [with a flag](../../feature_flags.md). Disabled by default.
+> - Modal window [enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/247208)
+ in GitLab 14.8.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/352526) in GitLab 14.9.
+ [Feature flag `invite_members_group_modal`](https://gitlab.com/gitlab-org/gitlab/-/issues/352526) removed.
-There are several outcomes you must be aware of when you share a project with a group that has a more restrictive [visibility level](../../public_access.md#project-and-group-visibility) than the project. For example, when you:
+You can share a project only with groups:
-- Share a public project with a private group.
-- Share a public project with an internal group.
-- Share an internal project with a private group.
+- Where you have an explicitly defined [membership](index.md).
+- That contain a nested subgroup or project you have an explicitly defined role for.
+- You are an administrator of.
-The following outcomes occur:
+To share a project with a group:
-- The group name is visible to all users that can view the project members page.
-- Owners of the project have access to members of the group when they mention them in issues or merge requests.
-- Project members who are direct or indirect members of the group can see group members listed in addition to members of the project.
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. In the left navigation menu, select **Project information > Members**.
+1. Select **Invite a group**.
+1. **Select a group** you want to add to the project.
+1. **Select a role** you want to assign to the group.
+1. Optional. Select an **Access expiration date**.
+1. Select **Invite**.
-## Share project with group lock
+## Prevent project sharing
-It is possible to prevent projects in a group from
+You can prevent members of a group from
[sharing a project with another group](../members/share_project_with_groups.md).
-This allows for tighter control over project access.
+This restriction allows for tighter control over project access.
-Learn more about [Share with group lock](../../group/access_and_permissions.md#prevent-a-project-from-being-shared-with-groups).
+For more information, see [Prevent a project from being shared with groups](../../group/access_and_permissions.md#prevent-a-project-from-being-shared-with-groups).
diff --git a/doc/user/project/merge_requests/approvals/index.md b/doc/user/project/merge_requests/approvals/index.md
index eb460225858..92ff78082e3 100644
--- a/doc/user/project/merge_requests/approvals/index.md
+++ b/doc/user/project/merge_requests/approvals/index.md
@@ -22,8 +22,10 @@ flexibility:
- Specify a list of users who act as [code owners](../../code_owners.md) for specific files,
and require their approval before work can merge.
-You can configure merge request approvals on a per-project basis, and
-[on the group level](../../../group/manage.md#group-merge-request-approval-settings). Administrators of
+You can configure merge request approvals on a per-project basis, and some approvals can be configured
+[on the group level](../../../group/manage.md#group-merge-request-approval-settings). Support for
+group-level settings for merge request approval rules is tracked in this
+[epic](https://gitlab.com/groups/gitlab-org/-/epics/4367). Administrators of
[GitLab Premium](https://about.gitlab.com/pricing/) and
[GitLab Ultimate](https://about.gitlab.com/pricing/) self-managed GitLab instances
can also configure approvals
diff --git a/doc/user/project/merge_requests/approvals/rules.md b/doc/user/project/merge_requests/approvals/rules.md
index e09a1318981..5f81db10cf4 100644
--- a/doc/user/project/merge_requests/approvals/rules.md
+++ b/doc/user/project/merge_requests/approvals/rules.md
@@ -182,7 +182,7 @@ granting them push access:
1. [Create a new group](../../../group/manage.md#create-a-group).
1. [Add the user to the group](../../../group/manage.md#add-users-to-a-group),
and select the Reporter role for the user.
-1. [Share the project with your group](../../members/share_project_with_groups.md#share-a-project-with-a-group-of-users),
+1. [Share the project with your group](../../members/share_project_with_groups.md#share-a-project-with-a-group),
based on the Reporter role.
1. Go to your project and select **Settings > Merge requests**.
1. In the **Merge request approvals** section, scroll to **Approval rules**, and either:
diff --git a/doc/user/project/merge_requests/approvals/settings.md b/doc/user/project/merge_requests/approvals/settings.md
index a2a12b22c3b..a8acab3898b 100644
--- a/doc/user/project/merge_requests/approvals/settings.md
+++ b/doc/user/project/merge_requests/approvals/settings.md
@@ -21,22 +21,24 @@ To view or edit merge request approval settings:
### Approval settings
-These settings limit who can approve merge requests.
-
-| Setting | Description |
-| ------ | ------ |
-| [Prevent approval by author](#prevent-approval-by-author) | When enabled, the author of a merge request cannot approve it. |
-| [Prevent approvals by users who add commits](#prevent-approvals-by-users-who-add-commits) | When enabled, users who have committed to a merge request cannot approve it. |
-| [Prevent editing approval rules in merge requests](#prevent-editing-approval-rules-in-merge-requests) | When enabled, users can't override the project's approval rules on merge requests. |
-| [Require user password to approve](#require-user-password-to-approve) | Force potential approvers to first authenticate with a password. |
-
-You can further define what happens to existing approvals when commits are added to the merge request.
-
-| Setting | Description |
-| ------ | ------ |
-| Keep approvals | Do not remove approvals. |
-| [Remove all approvals](#remove-all-approvals-when-commits-are-added-to-the-source-branch) | Remove all existing approvals. |
-| [Remove approvals by Code Owners if their files changed](#remove-approvals-by-code-owners-if-their-files-changed) | If a Code Owner has approved the merge request, and the commit changes files they are the Code Owner for, their approval is removed. |
+These settings limit who can approve merge requests:
+
+- [**Prevent approval by author**](#prevent-approval-by-author):
+ Prevents the author of a merge request from approving it.
+- [**Prevent approvals by users who add commits**](#prevent-approvals-by-users-who-add-commits):
+ Prevents users who add commits to a merge request from also approving it.
+- [**Prevent editing approval rules in merge requests**](#prevent-editing-approval-rules-in-merge-requests):
+ Prevents users from overriding project level approval rules on merge requests.
+- [**Require user password to approve**](#require-user-password-to-approve):
+ Force potential approvers to first authenticate with a password.
+- Code Owner approval removals: Define what happens to existing approvals when
+ commits are added to the merge request.
+ - **Keep approvals**: Do not remove any approvals.
+ - [**Remove all approvals**](#remove-all-approvals-when-commits-are-added-to-the-source-branch):
+ Remove all existing approvals.
+ - [**Remove approvals by Code Owners if their files changed**](#remove-approvals-by-code-owners-if-their-files-changed):
+ If a Code Owner approves a merge request, and a later commit changes files
+ they are a Code Owner for, their approval is removed.
## Prevent approval by author
diff --git a/doc/user/project/merge_requests/changes.md b/doc/user/project/merge_requests/changes.md
index 6e8b0cb1a75..f6e02dc0dfe 100644
--- a/doc/user/project/merge_requests/changes.md
+++ b/doc/user/project/merge_requests/changes.md
@@ -136,15 +136,10 @@ Files marked as viewed are not shown to you again unless either:
- New changes are made to its content.
- You clear the **Viewed** checkbox.
-## Show merge request conflicts in diff **(FREE SELF)**
+## Show merge request conflicts in diff
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/232484) in GitLab 13.5 [with a flag](../../../administration/feature_flags.md) named `display_merge_conflicts_in_diff`. Disabled by default.
-
-FLAG:
-On self-managed GitLab, by default this feature is not available. To make it available,
-ask an administrator to [enable the feature flag](../../../administration/feature_flags.md)
-named `display_merge_conflicts_in_diff`. On GitLab.com, this feature is not available.
-The feature is not ready for production use.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/232484) in GitLab 13.5 [with a flag](../../../administration/feature_flags.md) named `display_merge_conflicts_in_diff`. Disabled by default.
+> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/276918) in GitLab 15.7.
To avoid displaying the changes that are already on target branch in the diff,
we compare the merge request's source branch with HEAD of the target branch.
diff --git a/doc/user/project/merge_requests/commit_templates.md b/doc/user/project/merge_requests/commit_templates.md
index 75c2bdffae8..a14d8bddd24 100644
--- a/doc/user/project/merge_requests/commit_templates.md
+++ b/doc/user/project/merge_requests/commit_templates.md
@@ -70,6 +70,7 @@ GitLab creates a squash commit message with this template:
> - [Added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75639) `url`, `approved_by`, and `merged_by` variables in GitLab 14.7.
> - [Added](https://gitlab.com/gitlab-org/gitlab/-/issues/20421) `co_authored_by` variable in GitLab 14.7.
> - [Added](https://gitlab.com/gitlab-org/gitlab/-/issues/26303) `all_commits` variable in GitLab 14.9.
+> - [Added](https://gitlab.com/gitlab-org/gitlab/-/issues/378352) `reviewed_by` variable in GitLab 15.7.
Commit message templates support these variables:
@@ -84,7 +85,8 @@ Commit message templates support these variables:
| `%{first_commit}` | Full message of the first commit in merge request diff. | `Update README.md` |
| `%{first_multiline_commit}` | Full message of the first commit that's not a merge commit and has more than one line in message body. Merge request title if all commits aren't multiline. | `Update README.md`<br><br>`Improved project description in readme file.` |
| `%{url}` | Full URL to the merge request. | `https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1` |
-| `%{approved_by}` | Line-separated list of the merge request approvers. | `Approved-by: Sidney Jones <sjones@example.com>` <br> `Approved-by: Zhang Wei <zwei@example.com>` |
+| `%{reviewed_by}` | Line-separated list of the merge request reviewers, based on users who submit a review via batch comments, in a `Reviewed-by` Git commit trailer format. | `Reviewed-by: Sidney Jones <sjones@example.com>` <br> `Reviewed-by: Zhang Wei <zwei@example.com>` |
+| `%{approved_by}` | Line-separated list of the merge request approvers in a `Approved-by` Git commit trailer format. | `Approved-by: Sidney Jones <sjones@example.com>` <br> `Approved-by: Zhang Wei <zwei@example.com>` |
| `%{merged_by}` | User who merged the merge request. | `Alex Garcia <agarcia@example.com>` |
| `%{co_authored_by}` | Names and emails of commit authors in a `Co-authored-by` Git commit trailer format. Limited to authors of 100 most recent commits in merge request. | `Co-authored-by: Zane Doe <zdoe@example.com>` <br> `Co-authored-by: Blake Smith <bsmith@example.com>` |
| `%{all_commits}` | Messages from all commits in the merge request. Limited to 100 most recent commits. Skips commit bodies exceeding 100KiB and merge commit messages. | `* Feature introduced` <br><br> `This commit implements feature` <br> `Changelog:added` <br><br> `* Bug fixed` <br><br> `* Documentation improved` <br><br>`This commit introduced better docs.`|
diff --git a/doc/user/project/merge_requests/commits.md b/doc/user/project/merge_requests/commits.md
index a6ae3ac80a5..a9f67c39ae8 100644
--- a/doc/user/project/merge_requests/commits.md
+++ b/doc/user/project/merge_requests/commits.md
@@ -1,56 +1,11 @@
---
-stage: Create
-group: Code Review
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
-type: index, reference
+redirect_to: '../merge_requests/index.md'
+remove_date: '2023-03-12'
---
-# Commits tab in merge requests **(FREE)**
+This document was removed.
-The **Commits** tab in a merge request displays a sequential list of commits
-to the Git branch your merge request is based on. From this page, you can review
-full commit messages and copy a commit's SHA when you need to
-[cherry-pick changes](cherry_pick_changes.md).
-
-## Merge requests commit navigation
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/18140) in GitLab 13.0.
-
-To seamlessly navigate among commits in a merge request:
-
-1. Select the **Commits** tab.
-1. Select a commit to open it in the single-commit view.
-1. Navigate through the commits by either:
-
- - Selecting **Prev** and **Next** buttons below the tab buttons.
- - Using the <kbd>X</kbd> and <kbd>C</kbd> keyboard shortcuts.
-
-![Merge requests commit navigation](img/commit_nav_v13_11.png)
-
-## View merge request commits in context
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/29274) in GitLab 13.12 [with a flag](../../../administration/feature_flags.md) named `context_commits`. Enabled by default.
-> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/320757) in GitLab 14.8.
-> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/320757) in GitLab 14.9. [Feature flag `context_commits`](https://gitlab.com/gitlab-org/gitlab/-/issues/320757) removed.
-
-When reviewing a merge request, it helps to have more context about the changes
-made. That includes unchanged lines in unchanged files, and previous commits
-that have already merged that the change is built on.
-
-To add previously merged commits to a merge request for more context:
-
-1. Go to your merge request.
-1. Select the **Commits** tab.
-1. Scroll to the end of the list of commits, and select **Add previously merged commits**:
-
- ![Add previously merged commits button](img/add_previously_merged_commits_button_v14_1.png)
-
-1. Select the commits that you want to add.
-1. Select **Save changes**.
-
-To view the changes done on those previously merged commits:
-
-1. On your merge request, select the **Changes** tab.
-1. Scroll to **(file-tree)** **Compare** and select **previously merged commits**:
-
- ![Previously merged commits](img/previously_merged_commits_v14_1.png)
+<!-- This redirect file can be deleted after <2023-03-12>. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/user/project/merge_requests/conflicts.md b/doc/user/project/merge_requests/conflicts.md
index 24f22924a08..6063d64a721 100644
--- a/doc/user/project/merge_requests/conflicts.md
+++ b/doc/user/project/merge_requests/conflicts.md
@@ -75,7 +75,7 @@ To resolve less-complex conflicts from the GitLab user interface:
Resolving conflicts merges the target branch of the merge request into the
source branch, using the version of the text you chose. If the source branch is
`feature` and the target branch is `main`, these actions are similar to running
-`git checkout feature; git merge main` locally.
+`git switch feature; git merge main` locally.
## Resolve conflicts in the inline editor
@@ -101,7 +101,7 @@ most control over each change:
1. Open the terminal and check out your feature branch. For example, `my-feature-branch`:
```shell
- git checkout my-feature-branch
+ git switch my-feature-branch
```
1. [Rebase your branch](../../../topics/git/git_rebase.md#regular-rebase) against the
diff --git a/doc/user/project/merge_requests/creating_merge_requests.md b/doc/user/project/merge_requests/creating_merge_requests.md
index df11d5a1d8d..eae4db2d4f7 100644
--- a/doc/user/project/merge_requests/creating_merge_requests.md
+++ b/doc/user/project/merge_requests/creating_merge_requests.md
@@ -102,7 +102,7 @@ You can create a merge request from your fork to contribute back to the main pro
change the default target branch (which can be useful if you are working in a
forked project).
1. Select **Compare branches and continue**.
-1. Select **Submit merge request**.
+1. Select **Create merge request**.
After your work is merged, if you don't intend to
make any other contributions to the upstream project, you can unlink your
diff --git a/doc/user/project/merge_requests/img/add_previously_merged_commits_button_v14_1.png b/doc/user/project/merge_requests/img/add_previously_merged_commits_button_v14_1.png
deleted file mode 100644
index e60e869f854..00000000000
--- a/doc/user/project/merge_requests/img/add_previously_merged_commits_button_v14_1.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/img/commit_nav_v13_11.png b/doc/user/project/merge_requests/img/commit_nav_v13_11.png
deleted file mode 100644
index a9bc8fa6bee..00000000000
--- a/doc/user/project/merge_requests/img/commit_nav_v13_11.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/img/previously_merged_commits_v14_1.png b/doc/user/project/merge_requests/img/previously_merged_commits_v14_1.png
deleted file mode 100644
index 4f49fad10ad..00000000000
--- a/doc/user/project/merge_requests/img/previously_merged_commits_v14_1.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/merge_request_dependencies.md b/doc/user/project/merge_requests/merge_request_dependencies.md
deleted file mode 100644
index 6242a77e931..00000000000
--- a/doc/user/project/merge_requests/merge_request_dependencies.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-redirect_to: 'dependencies.md'
-remove_date: '2022-11-22'
----
-
-This document was moved to [another location](dependencies.md).
-
-<!-- This redirect file can be deleted after <2022-11-22>. -->
-<!-- Redirects that point to other docs in the same project expire in three months. -->
-<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/user/project/merge_requests/methods/index.md b/doc/user/project/merge_requests/methods/index.md
index e72c927198e..249a98f1779 100644
--- a/doc/user/project/merge_requests/methods/index.md
+++ b/doc/user/project/merge_requests/methods/index.md
@@ -10,47 +10,107 @@ type: reference, concepts
The merge method you select for your project determines how the changes in your
merge requests are merged into an existing branch.
+The examples on this page assume a `main` branch with commits A, C, and E, and a
+`feature` branch with commits B and D:
+
+```mermaid
+gitGraph
+ commit id: "A"
+ branch feature
+ commit id: "B"
+ commit id: "D"
+ checkout main
+ commit id: "C"
+ commit id: "E"
+```
+
## Configure a project's merge method
1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Merge requests**.
-1. In the **Merge method** section, select your desired merge method.
+1. Select your desired **Merge method** from these options:
+ - Merge commit
+ - Merge commit with semi-linear history
+ - Fast-forward merge
+1. In **Squash commits when merging**, select the default behavior for handling commits:
+ - **Do not allow**: Squashing is never performed, and the user cannot change the behavior.
+ - **Allow**: Squashing is off by default, but the user can change the behavior.
+ - **Encourage**: Squashing is on by default, but the user can change the behavior.
+ - **Require**: Squashing is always performed, and the user cannot change the behavior.
1. Select **Save changes**.
## Merge commit
-This setting is the default. It always creates a separate merge commit,
-even when using [squash](../squash_and_merge.md). An example commit graph generated using this merge method:
+By default, GitLab creates a merge commit when a branch is merged into `main`.
+A separate merge commit is always created, regardless of whether or not commits
+are [squashed when merging](../squash_and_merge.md). This strategy can result
+in both a squash commit and a merge commit being added to your `main` branch.
+
+These diagrams show how the `feature` branch merges into `main` if you use the
+**Merge commit** strategy. They are equivalent to the command `git merge --no-ff <feature>`,
+and selecting `Merge commit` as the **Merge method** in the GitLab UI:
+
+The merge strategy:
```mermaid
+%%{init: { 'gitGraph': {'logLevel': 'debug', 'showBranches': true, 'showCommitLabel':true,'mainBranchName': 'main'}} }%%
gitGraph
- commit id: "Init"
- branch mr-branch-1
- commit
- checkout main
- commit
- branch mr-branch-2
- commit
- checkout mr-branch-1
- commit
- checkout main
- branch squash-mr
- commit id: "Squashed commits"
- checkout main
- merge squash-mr
- merge mr-branch-1
- commit
- merge mr-branch-2
+ commit id: "A"
+ branch feature
+ commit id: "B"
+ commit id: "D"
+ checkout main
+ commit id: "C"
+ commit id: "E"
+ merge feature
+```
+
+After a feature branch is merged with the **Merge commit** method, your `main` branch
+looks like this:
+
+```mermaid
+%%{init: { 'gitGraph': {'logLevel': 'debug', 'showBranches': true, 'showCommitLabel':true,'mainBranchName': 'main'}} }%%
+gitGraph
+ commit id: "A"
+ commit id: "C"
+ commit id: "E"
+ commit id: "squash commit"
+ commit id: "merge commit"
```
-- For regular merges, it is equivalent to the command `git merge --no-ff <source-branch>`.
-- For squash merges, it squashes all commits in the source branch before merging it normally. It performs actions similar to:
+In comparison, a **squash merge** constructs a squash commit, a virtual copy of all commits
+from the `feature` branch. The original commits (B and D) remain unchanged
+on the `feature` branch, and the squash commit is placed on the `main` branch:
+
+```mermaid
+%%{init: { 'gitGraph': {'showBranches': true, 'showCommitLabel':true,'mainBranchName': 'main'}} }%%
+gitGraph
+ commit id:"A"
+ branch feature
+ checkout main
+ commit id:"C"
+ checkout feature
+ commit id:"B"
+ commit id:"D"
+ checkout main
+ commit id:"E"
+ commit id:"squash commit" type: HIGHLIGHT
+```
+
+The squash merge graph is equivalent to these settings in the GitLab UI:
+
+- **Merge method**: Merge commit.
+- **Squash commits when merging** should be set to either:
+ - Require.
+ - Either Allow or Encourage, and squashing must be selected on the merge request.
+
+The squash merge graph is also equivalent to these commands:
```shell
- git checkout `git merge-base <source-branch> <target-branch>`
- git merge --squash <source-branch>
+ git checkout `git merge-base feature main`
+ git merge --squash <feature>
SOURCE_SHA=`git rev-parse HEAD`
- git checkout <target-branch>
+ git checkout <main>
git merge --no-ff $SOURCE_SHA
```
@@ -58,7 +118,8 @@ gitGraph
A merge commit is created for every merge, but the branch is only merged if
a fast-forward merge is possible. This ensures that if the merge request build
-succeeded, the target branch build also succeeds after the merge. An example commit graph generated using this merge method:
+succeeded, the target branch build also succeeds after the merge. An example
+commit graph generated using this merge method:
```mermaid
gitGraph
@@ -113,8 +174,8 @@ This method is equivalent to `git merge --ff <source-branch>` for regular merges
When the fast-forward merge
([`--ff-only`](https://git-scm.com/docs/git-merge#git-merge---ff-only)) setting
-is enabled, no merge commits are created and all merges are fast-forwarded,
-which means that merging is only allowed if the branch can be fast-forwarded.
+is enabled, no merge commits are created and all merges are fast-forwarded.
+Merging is only allowed if the branch can be fast-forwarded.
When a fast-forward merge is not possible, the user is given the option to rebase, see
[Rebasing in (semi-)linear merge methods](#rebasing-in-semi-linear-merge-methods).
@@ -136,11 +197,16 @@ In these merge methods, you can merge only when your source branch is up-to-date
- Fast-forward merge.
If a fast-forward merge is not possible but a conflict-free rebase is possible,
-GitLab offers you the [`/rebase` quick action](../../../../topics/git/git_rebase.md#rebase-from-the-gitlab-ui),
-and the ability to select **Rebase** from the user interface.
+GitLab provides:
+
+- The [`/rebase` quick action](../../../../topics/git/git_rebase.md#rebase-from-the-gitlab-ui).
+- The option to select **Rebase** in the user interface.
+
+You must rebase the source branch locally before a fast-forward merge if both
+conditions are true:
-If the target branch is ahead of the source branch and a conflict-free rebase is
-not possible, you must rebase the source branch locally before you can do a fast-forward merge.
+- The target branch is ahead of the source branch.
+- A conflict-free rebase is not possible.
![Fast forward merge rebase locally](../img/ff_merge_rebase_locally.png)
diff --git a/doc/user/project/merge_requests/reviews/data_usage.md b/doc/user/project/merge_requests/reviews/data_usage.md
index 3a3af7a24bc..f17015aef4e 100644
--- a/doc/user/project/merge_requests/reviews/data_usage.md
+++ b/doc/user/project/merge_requests/reviews/data_usage.md
@@ -27,7 +27,7 @@ This feature is designed as a progressive enhancement to the existing GitLab Rev
## Model Accuracy
-Organizations use many different processes for code review. Some focus on senior engineers reviewing junior engineer's code, others have hierarchical organizational structure based reviews. Suggested Reviewers is focused on contextual reviewers based on historical merge request activity by users. While we will continue evolving the underlying ML model to better serve various code review use cases and processes Suggested Reviewers does not replace the usage of other code review features like Code Owners and [Approval Rules](../approvals/rules.md). Reviewer selection is highly subjective therefore, we do not expect Suggested Reviewers to provide perfect suggestions everytime.
+Organizations use many different processes for code review. Some focus on senior engineers reviewing junior engineer's code, others have hierarchical organizational structure based reviews. Suggested Reviewers is focused on contextual reviewers based on historical merge request activity by users. While we will continue evolving the underlying ML model to better serve various code review use cases and processes Suggested Reviewers does not replace the usage of other code review features like Code Owners and [Approval Rules](../approvals/rules.md). Reviewer selection is highly subjective therefore, we do not expect Suggested Reviewers to provide perfect suggestions every time.
Through analysis of beta customer usage, we find that the Suggested Reviewers ML model provides suggestions that are adopted in 60% of cases. We will be introducing a feedback mechanism into the Suggested Reviewers feature in the future to allow users to flag bad reviewer suggestions to help improve the model. Additionally we will be offering an opt-in feature in the future which will allow the model to use your project's data for training the underlying model.
@@ -39,6 +39,6 @@ Suggested Reviewers is off by default and requires a Project Owner or Admin to e
Suggested Reviewers operates completely within the GitLab.com infrastructure providing the same level of [privacy](https://about.gitlab.com/privacy/) and [security](https://about.gitlab.com/security/) of any other feature of GitLab.com.
-No new additional data is collected to enable this feature. GitLab is inferencing your merge request against a trained machine learning model. The content of your source code is not used as training data. Your data also never leaves GitLab.com, all training and inference is done within GitLab.com infrastructure.
+No new additional data is collected to enable this feature. GitLab infers your merge request against a trained machine learning model. The content of your source code is not used as training data. Your data also never leaves GitLab.com, all training and inference is done within GitLab.com infrastructure.
[Read more about the security of GitLab.com](https://about.gitlab.com/security/faq/)
diff --git a/doc/user/project/merge_requests/reviews/img/suggestion_code_block_editor_v12_8.png b/doc/user/project/merge_requests/reviews/img/suggestion_code_block_editor_v12_8.png
deleted file mode 100644
index 927b4f812a5..00000000000
--- a/doc/user/project/merge_requests/reviews/img/suggestion_code_block_editor_v12_8.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/reviews/index.md b/doc/user/project/merge_requests/reviews/index.md
index 4c503211513..9a75c038dbc 100644
--- a/doc/user/project/merge_requests/reviews/index.md
+++ b/doc/user/project/merge_requests/reviews/index.md
@@ -71,6 +71,52 @@ if you [approve a merge request](../approvals/index.md#approve-a-merge-request)
are shown in the reviewer list, a green check mark **{check-circle-filled}**
displays next to your name.
+### Download merge request changes as a diff
+
+To download the changes included in a merge request as a diff:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Merge requests**.
+1. Select your merge request.
+1. On the top right, select **Code > Plain diff**.
+
+If you know the URL of the merge request, you can also download the diff from
+the command line by appending `.diff` to the URL. This example downloads the diff
+for merge request `000000`:
+
+```plaintext
+https://gitlab.com/gitlab-org/gitlab/-/merge_requests/000000.diff
+```
+
+To download and apply the diff in a one-line CLI command:
+
+```shell
+curl "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/000000.diff" | git apply
+```
+
+### Download merge request changes as a patch file
+
+To download the changes included in a merge request as a patch file:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Merge requests**.
+1. Select your merge request.
+1. On the top right, select **Code > Email patches**.
+
+If you know the URL of the merge request, you can also download the patch from
+the command line by appending `.patch` to the URL. This example downloads the patch
+file for merge request `000000`:
+
+```plaintext
+https://gitlab.com/gitlab-org/gitlab/-/merge_requests/000000.patch
+```
+
+To download and apply the patch in a one-line CLI command using [`git am`](https://git-scm.com/docs/git-am):
+
+```shell
+curl "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/000000.patch" | git am
+```
+
### Submit a review
You can submit your completed review in multiple ways:
diff --git a/doc/user/project/merge_requests/reviews/suggestions.md b/doc/user/project/merge_requests/reviews/suggestions.md
index 832f78d18a1..668dece9fda 100644
--- a/doc/user/project/merge_requests/reviews/suggestions.md
+++ b/doc/user/project/merge_requests/reviews/suggestions.md
@@ -74,7 +74,13 @@ To add a suggestion that includes a
[fenced code block](../../../markdown.md#code-spans-and-blocks), wrap your suggestion
in four backticks instead of three:
-![A comment editor with a suggestion with a fenced code block](img/suggestion_code_block_editor_v12_8.png)
+~~~markdown
+````suggestion:-0+2
+```shell
+git config --global receive.advertisepushoptions true
+```
+````
+~~~
![Output of a comment with a suggestion with a fenced code block](img/suggestion_code_block_output_v12_8.png)
diff --git a/doc/user/project/merge_requests/status_checks.md b/doc/user/project/merge_requests/status_checks.md
index d330ccdefb6..74c3b3e24b6 100644
--- a/doc/user/project/merge_requests/status_checks.md
+++ b/doc/user/project/merge_requests/status_checks.md
@@ -6,7 +6,7 @@ type: reference, concepts
disqus_identifier: 'https://docs.gitlab.com/ee/user/project/merge_requests/status_checks.html'
---
-# External Status Checks **(ULTIMATE)**
+# External status checks **(ULTIMATE)**
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3869) in GitLab 14.0, disabled behind the `:ff_external_status_checks` feature flag.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/320783) in GitLab 14.1.
diff --git a/doc/user/project/ml/experiment_tracking/img/candidate_v15_7.png b/doc/user/project/ml/experiment_tracking/img/candidate_v15_7.png
new file mode 100644
index 00000000000..fb2e2e706d6
--- /dev/null
+++ b/doc/user/project/ml/experiment_tracking/img/candidate_v15_7.png
Binary files differ
diff --git a/doc/user/project/ml/experiment_tracking/img/candidates.png b/doc/user/project/ml/experiment_tracking/img/candidates.png
deleted file mode 100644
index df70a01a2bd..00000000000
--- a/doc/user/project/ml/experiment_tracking/img/candidates.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/ml/experiment_tracking/img/candidates_v15_7.png b/doc/user/project/ml/experiment_tracking/img/candidates_v15_7.png
new file mode 100644
index 00000000000..58dfe94a108
--- /dev/null
+++ b/doc/user/project/ml/experiment_tracking/img/candidates_v15_7.png
Binary files differ
diff --git a/doc/user/project/ml/experiment_tracking/img/experiments.png b/doc/user/project/ml/experiment_tracking/img/experiments.png
deleted file mode 100644
index a6472406b90..00000000000
--- a/doc/user/project/ml/experiment_tracking/img/experiments.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/ml/experiment_tracking/img/experiments_v15_7.png b/doc/user/project/ml/experiment_tracking/img/experiments_v15_7.png
new file mode 100644
index 00000000000..a7d4a3e559f
--- /dev/null
+++ b/doc/user/project/ml/experiment_tracking/img/experiments_v15_7.png
Binary files differ
diff --git a/doc/user/project/ml/experiment_tracking/index.md b/doc/user/project/ml/experiment_tracking/index.md
index e274bd7f38e..a7096d633a0 100644
--- a/doc/user/project/ml/experiment_tracking/index.md
+++ b/doc/user/project/ml/experiment_tracking/index.md
@@ -16,9 +16,11 @@ engineering, and so on, to improve the performance of the model. Keeping track o
artifacts so that the data scientist can later replicate the experiment is not trivial. Machine learning experiment
tracking enables them to log parameters, metrics, and artifacts directly into GitLab, giving easy access later on.
-![List of Experiments](img/experiments.png)
+![List of Experiments](img/experiments_v15_7.png)
-![Experiment Candidates](img/candidates.png)
+![Experiment Candidates](img/candidates_v15_7.png)
+
+![Candidate Detail](img/candidate_v15_7.png)
## What is an experiment?
@@ -53,18 +55,19 @@ integration. More information on how to use GitLab as a backend for MLFlow Clien
### Exploring model candidates
To list the current active experiments, navigate to `https/-/ml/experiments`. To display all trials
-that have been logged, along with their metrics and parameters, selecting an experiment.
+that have been logged, along with their metrics and parameters, select an experiment. To display details for a candidate,
+select **Details**.
### Logging artifacts
Trial artifacts are saved as [generic packages](../../../packages/generic_packages/index.md), and follow all their
conventions. After an artifact is logged for a candidate, all artifacts logged for the candidate are listed in the
-package registry. The package name for a candidate is `ml_candidate_<candidate_id>`, with version `-`.
+package registry. The package name for a candidate is `ml_candidate_<candidate_id>`, with version `-`. The link to the
+artifacts can also be accessed from the **Experiment Candidates** list or **Candidate detail**.
### Limitations and future
- Searching experiments, searching trials, visual comparison of trials, and creating, deleting and updating experiments and trials through GitLab UI is under development.
-- No support for experiment and trial metadata that do not classify as parameters or metrics.
## Disabling or enabling the Feature
@@ -73,4 +76,6 @@ On GitLab.com, this feature is currently on private testing.
## Feedback, roadmap and reports
-For updates on the development, feedback and bug reports, refer to the [development epic](https://gitlab.com/groups/gitlab-org/-/epics/8560).
+For updates on the development, refer to the [development epic](https://gitlab.com/groups/gitlab-org/-/epics/8560).
+
+For feedback, bug reports and feature requests, refer to the [feedback issue](https://gitlab.com/gitlab-org/gitlab/-/issues/381660).
diff --git a/doc/user/project/pages/custom_domains_ssl_tls_certification/dns_concepts.md b/doc/user/project/pages/custom_domains_ssl_tls_certification/dns_concepts.md
index 1d32091b294..197524f2fc5 100644
--- a/doc/user/project/pages/custom_domains_ssl_tls_certification/dns_concepts.md
+++ b/doc/user/project/pages/custom_domains_ssl_tls_certification/dns_concepts.md
@@ -35,7 +35,7 @@ for the most popular hosting services:
- [123-reg](https://www.123-reg.co.uk/support/domains/domain-name-server-dns-management-guide/)
- [Amazon](https://docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-custom-domain-walkthrough.html)
- [Bluehost](https://www.bluehost.com/help/article/dns-management-add-edit-or-delete-dns-entries)
-- [Cloudflare](https://support.cloudflare.com/hc/en-us/articles/201720164-Creating-a-Cloudflare-account-and-adding-a-website)
+- [Cloudflare](https://developers.cloudflare.com/fundamentals/get-started/setup/)
- [cPanel](https://documentation.cpanel.net/display/84Docs/Edit+DNS+Zone)
- [DigitalOcean](https://docs.digitalocean.com/products/networking/dns/how-to/manage-records/)
- [DreamHost](https://help.dreamhost.com/hc/en-us/articles/360035516812)
diff --git a/doc/user/project/pages/custom_domains_ssl_tls_certification/img/add_certificate_to_pages.png b/doc/user/project/pages/custom_domains_ssl_tls_certification/img/add_certificate_to_pages.png
deleted file mode 100644
index d92a981dc60..00000000000
--- a/doc/user/project/pages/custom_domains_ssl_tls_certification/img/add_certificate_to_pages.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md b/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md
index 6378d962ffe..dc23540bd1b 100644
--- a/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md
+++ b/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md
@@ -52,15 +52,14 @@ this document for an [overview on DNS records](dns_concepts.md).
#### 1. Add a custom domain
-Navigate to your project's **Setting > Pages** and select **+ New domain**
-to add your custom domain to GitLab Pages. You can choose whether to:
+To add your custom domain to GitLab Pages:
-- Add an [SSL/TLS certificate](#adding-an-ssltls-certificate-to-pages).
-- Leave it blank (it can be added later).
-
-Select **Create New Domain**.
-
-![Add new domain](img/add_certificate_to_pages.png)
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > Pages**.
+1. In the top right, select **New Domain**.
+1. In **Domain**, enter your domain.
+1. Optional. In **Certificate**, turn off the **Automatic certificate management using Let's Encrypt** toggle to add an [SSL/TLS certificate](#adding-an-ssltls-certificate-to-pages). You can also add the certificate and key later.
+1. Select **Create New Domain**.
#### 2. Get the verification code
@@ -292,8 +291,6 @@ meet these requirements.
- To add the certificate to a domain previously added, go to your
project's **Settings > Pages**, locate your domain name, select **Details** and **Edit** to add the certificate.
-![Pages project - adding certificates](img/add_certificate_to_pages.png)
-
1. Add the PEM certificate to its corresponding field.
1. If your certificate is missing its intermediate, copy
and paste the root certificate (usually available from your CA website)
diff --git a/doc/user/project/pages/getting_started/pages_ci_cd_template.md b/doc/user/project/pages/getting_started/pages_ci_cd_template.md
index caf98e8a8a4..339ab239588 100644
--- a/doc/user/project/pages/getting_started/pages_ci_cd_template.md
+++ b/doc/user/project/pages/getting_started/pages_ci_cd_template.md
@@ -26,13 +26,12 @@ these steps, you may have to do additional configuration for the Pages site to g
If everything is configured correctly, the site can take approximately 30 minutes to deploy.
-You can watch the pipeline run by navigating to **CI/CD > Pipelines**.
+To view the pipeline, go to **CI/CD > Pipelines**.
When the pipeline is finished, go to **Settings > Pages** to find the link to
your Pages website.
-To view the HTML and other assets that were created for the site,
-go to the **Pipelines** tab, view the job, and on the right side,
-select **Download artifacts**.
-
For every change pushed to your repository, GitLab CI/CD runs a new pipeline
that immediately publishes your changes to the Pages site.
+
+To view the HTML and other assets that were created for the site,
+[download the job artifacts](../../../../ci/pipelines/job_artifacts.md#download-job-artifacts).
diff --git a/doc/user/project/pages/getting_started/pages_forked_sample_project.md b/doc/user/project/pages/getting_started/pages_forked_sample_project.md
index 69c60cab4b3..9841e52a089 100644
--- a/doc/user/project/pages/getting_started/pages_forked_sample_project.md
+++ b/doc/user/project/pages/getting_started/pages_forked_sample_project.md
@@ -29,32 +29,37 @@ When the pipeline is finished, go to **Settings > Pages** to find the link to yo
For every change pushed to your repository, GitLab CI/CD runs a new pipeline
that immediately publishes your changes to the Pages site.
-To view the HTMl and other assets that were created for the site,
-go to the **Pipelines** tab, view the job, and on the right side,
-select **Download artifacts**.
+## Remove the fork relationship
-You can take some **optional** further steps:
+If you want to contribute to the project you forked from,
+you can keep the forked relationship. Otherwise:
-- Remove the fork relationship. If you want to contribute to the project you forked from,
- you can keep this relationship. Otherwise, go to your project's **Settings > General**,
- expand **Advanced settings**, and scroll down to **Remove fork relationship**:
+1. On the left sidebar, select **Settings > General**.
+1. Expand **Advanced settings**.
+1. Select **Remove fork relationship**.
- ![Remove fork relationship](../img/remove_fork_relationship_v13_1.png)
+## Change the URL
-- Change the URL to match your namespace. If your Pages site is hosted on GitLab.com,
- you can rename it to `<namespace>.gitlab.io`, where `<namespace>` is your GitLab namespace
- (the one you chose when you forked the project).
+You can change the URL to match your namespace.
+If your Pages site is hosted on GitLab.com,
+you can rename it to `<namespace>.gitlab.io`, where `<namespace>` is your GitLab namespace
+(the one you chose when you forked the project).
- - Go to your project's **Settings > General** and expand **Advanced**. Scroll down to
- **Change path** and change the path to `<namespace>.gitlab.io`.
+1. On the left sidebar, select **Settings > General**.
+1. Expand **Advanced**.
+1. In **Change path**, update the path to `<namespace>.gitlab.io`.
- For example, if your project's URL is `gitlab.com/gitlab-tests/jekyll`, your namespace is
- `gitlab-tests`.
+ For example, if your project's URL is `gitlab.com/gitlab-tests/jekyll`, your namespace is
+ `gitlab-tests`.
- If you set the repository path to `gitlab-tests.gitlab.io`,
- the resulting URL for your Pages website is `https://gitlab-tests.gitlab.io`.
+ If you set the repository path to `gitlab-tests.gitlab.io`,
+ the resulting URL for your Pages website is `https://gitlab-tests.gitlab.io`.
- ![Change repository's path](../img/change_path_v12_10.png)
+ ![Change repository's path](../img/change_path_v12_10.png)
- - Now go to your SSG's configuration file and change the [base URL](../getting_started_part_one.md#urls-and-base-urls)
- from `"project-name"` to `""`. The project name setting varies by SSG and may not be in the configuration file.
+1. Open your SSG configuration file and change the [base URL](../getting_started_part_one.md#urls-and-base-urls)
+ from `"project-name"` to `""`. The project name setting varies by SSG and may not be in the configuration file.
+
+## Related topics
+
+- [Download the job artifacts](../../../../ci/pipelines/job_artifacts.md#download-job-artifacts)
diff --git a/doc/user/project/pages/getting_started/pages_from_scratch.md b/doc/user/project/pages/getting_started/pages_from_scratch.md
index c0a1e8f16e0..ebadf39a984 100644
--- a/doc/user/project/pages/getting_started/pages_from_scratch.md
+++ b/doc/user/project/pages/getting_started/pages_from_scratch.md
@@ -420,9 +420,8 @@ Now GitLab CI/CD not only builds the website, but also:
- **Caches** dependencies installed with Bundler.
- **Continuously deploys** every push to the `main` branch.
-To view the HTMl and other assets that were created for the site,
-go to the **Pipelines** tab, view the job, and on the right side,
-select **Download artifacts**.
+To view the HTML and other assets that were created for the site,
+[download the job artifacts](../../../../ci/pipelines/job_artifacts.md#download-job-artifacts).
## Related topics
diff --git a/doc/user/project/pages/getting_started/pages_new_project_template.md b/doc/user/project/pages/getting_started/pages_new_project_template.md
index e4890954d13..a0f9753a40c 100644
--- a/doc/user/project/pages/getting_started/pages_new_project_template.md
+++ b/doc/user/project/pages/getting_started/pages_new_project_template.md
@@ -29,6 +29,5 @@ your Pages website.
For every change pushed to your repository, GitLab CI/CD runs a new pipeline
that immediately publishes your changes to the Pages site.
-To view the HTMl and other assets that were created for the site,
-go to the **Pipelines** tab, view the job, and on the right side,
-select **Download artifacts**.
+To view the HTML and other assets that were created for the site,
+[download the job artifacts](../../../../ci/pipelines/job_artifacts.md#download-job-artifacts).
diff --git a/doc/user/project/pages/getting_started/pages_ui.md b/doc/user/project/pages/getting_started/pages_ui.md
index ba97fcb8749..a6069b473e6 100644
--- a/doc/user/project/pages/getting_started/pages_ui.md
+++ b/doc/user/project/pages/getting_started/pages_ui.md
@@ -4,59 +4,69 @@ group: Incubation
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Tutorial: Use the GitLab UI to deploy your static site **(FREE)**
+# Create a Pages deployment for your static site **(FREE)**
-This tutorial assumes you have a project that either:
+If you already have a GitLab project that contains your static site or framework,
+you can generate a GitLab Pages website from it.
-- Generates static sites or a client-rendered single-page application (SPA),
- such as [Eleventy](https://www.11ty.dev), [Astro](https://astro.build), or [Jekyll](https://jekyllrb.com).
-- Contains a framework configured for static output, such as [Next.js](https://nextjs.org),
- [Nuxt.js](https://nuxtjs.org), or [SvelteKit](https://kit.svelte.dev).
+When you provide basic information in the UI, a `.gitlab-ci.yml` file is created
+and a merge request opened. When you commit the merge request,
+a pipeline deploys your Pages website.
-## Update your app to output files to the `public` folder
+## Prerequisites
-GitLab Pages requires all files intended to be part of the published website to
-be in a root-level folder called `public`. If you create this folder during the build
-pipeline, committing it to Git is not required.
+- Your app must [output files to the `public` folder](../public_folder.md). If you create
+ this folder during the build pipeline, you do not need to commit it to Git.
-For detailed instructions, read [Configure the public files folder](../public_folder.md).
+ WARNING:
+ This step is important. Ensure your files are in a root-level `public` folder.
-## Set up the `.gitlab-ci.yml` file
+- You must have a project that either:
+ - Generates static sites or a client-rendered single-page application (SPA),
+ like [Eleventy](https://www.11ty.dev), [Astro](https://astro.build), or [Jekyll](https://jekyllrb.com).
+ - Contains a framework configured for static output, such as [Next.js](https://nextjs.org),
+ [Nuxt.js](https://nuxtjs.org), or [SvelteKit](https://kit.svelte.dev).
+- GitLab Pages must be enabled for the project. (To enable, go to **Settings > General**,
+ expand **Visibility, project features, permissions**, and turn on the **Pages** toggle.)
-GitLab helps you write the `.gitlab-ci.yml` needed to create your first GitLab Pages
-deployment pipeline. Rather than building the file from scratch, it asks you to
-provide the build commands, and creates the necessary boilerplate for you.
+## Create the Pages deployment
-To build your YAML file from the GitLab UI:
+To complete the setup and generate a GitLab Pages deployment:
1. On the top bar, select **Main menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > Pages** to display the friendly
- interface **Get Started With Pages**.
-1. If your framework's build process does not need one of the provided build
- commands, you can either:
+1. On the left sidebar, select **Settings > Pages**. A **Get Started with Pages** form appears.
+ If this form is not available, see [Troubleshooting](#if-the-get-started-with-pages-form-is-not-available).
+1. For **Step 1**, enter an image name and verify that your files are in a `public` folder.
+1. Select **Next**.
+1. For **Step 2**, enter your installation steps. If your framework's build process does not
+ need one of the provided build commands, you can either:
- Skip the step by selecting **Next**.
- Enter `:` (the bash "do nothing" command) if you still want to incorporate that
step's boilerplate into your `.gitlab-ci.yml` file.
-1. Optional. Edit and adjust the generated `.gitlab-ci.yml` file as needed.
-1. Commit your `.gitlab-ci.yml` to your repository. This commit triggers your first
+1. Select **Next**.
+1. For **Step 3**, enter scripts that indicate how to build your application.
+1. Select **Next**.
+1. Optional. Edit the generated `.gitlab-ci.yml` file as needed.
+1. For **Step 4**, add a commit message and select **Commit**. This commit triggers your first
GitLab Pages deployment.
-To view the HTMl and other assets that were created for the site,
-go to **CI/CD > Pipelines**, view the job, and on the right side,
-select **Download artifacts**.
+To view the running pipeline, go to **CI/CD > Pipelines**.
+
+To view the artifacts that were created during the deployment, view the job,
+and on the right side, select **Download artifacts**.
## Troubleshooting
-### If you can't see the "Get Started with Pages" interface
+### If the `Get Started with Pages` form is not available
-GitLab doesn't show this interface if you have either:
+When you go to **Settings > Pages**, the form is not available if you:
- Deployed a GitLab Pages site before.
-- Committed a `.gitlab-ci.yml` through this interface at least once.
+- Committed a `.gitlab-ci.yml` through the forms at least one time.
-To fix this problem:
+To fix this issue:
-- If you see the message **Waiting for the Pages Pipeline to complete**, select
- **Start over** to start the wizard again.
+- If the message **Waiting for the Pages Pipeline to complete** appears, select
+ **Start over** to start the form again.
- If your project has previously deployed GitLab Pages successfully,
- [manually update](pages_from_scratch.md) your `.gitlab-ci.yml`.
+ [manually update](pages_from_scratch.md) your `.gitlab-ci.yml` file.
diff --git a/doc/user/project/pages/getting_started_part_one.md b/doc/user/project/pages/getting_started_part_one.md
index 588d94729e2..a0c8073d6eb 100644
--- a/doc/user/project/pages/getting_started_part_one.md
+++ b/doc/user/project/pages/getting_started_part_one.md
@@ -87,7 +87,7 @@ Every Static Site Generator (SSG) default configuration expects
to find your website under a (sub)domain (`example.com`), not
in a subdirectory of that domain (`example.com/subdir`). Therefore,
whenever you publish a project website (`namespace.gitlab.io/project-name`),
-you must look for this configuration (base URL) on your SSG's
+you must look for this configuration (base URL) on your static site generator's
documentation and set it up to reflect this pattern.
For example, for a Jekyll site, the `baseurl` is defined in the Jekyll
diff --git a/doc/user/project/pages/img/remove_fork_relationship_v13_1.png b/doc/user/project/pages/img/remove_fork_relationship_v13_1.png
deleted file mode 100644
index 84aa2e571c7..00000000000
--- a/doc/user/project/pages/img/remove_fork_relationship_v13_1.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/pages/public_folder.md b/doc/user/project/pages/public_folder.md
index a19e296b954..8c9f1cbec86 100644
--- a/doc/user/project/pages/public_folder.md
+++ b/doc/user/project/pages/public_folder.md
@@ -2,40 +2,39 @@
description: 'Learn how to configure the build output folder for the most
common static site generators'
stage: Create
-group: Incubation
+group: Editor
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Configure the public files folder **(FREE)**
-GitLab Pages requires all files you intend to be available in the published website to
-be in a root-level folder called `public`. This page describe how
-to set this up for some common static site generators.
+All the files that should be accessible by the browser must be in a root-level folder called `public`.
-## Guide by framework
+Follow these instructions to configure the `public` folder
+for the following frameworks.
-### Eleventy
+## Eleventy
-For Eleventy, you should either:
+For Eleventy, you should do one of the following:
-1. Add the `--output=public` flag in Eleventy's build commands, for example:
+- Add the `--output=public` flag in Eleventy's build commands, for example:
- `npx @11ty/eleventy --input=path/to/sourcefiles --output=public`
+ `npx @11ty/eleventy --input=path/to/sourcefiles --output=public`
-1. Add the following to your `.eleventy.js` file:
+- Add the following to your `.eleventy.js` file:
- ```javascript
- // .eleventy.js
- module.exports = function(eleventyConfig) {
- return {
- dir: {
- output: "public"
- }
- }
- };
- ```
+ ```javascript
+ // .eleventy.js
+ module.exports = function(eleventyConfig) {
+ return {
+ dir: {
+ output: "public"
+ }
+ }
+ };
+ ```
-### Astro
+## Astro
By default, Astro uses the `public` folder to store static assets. For GitLab Pages,
rename that folder to a collision-free alternative first:
@@ -65,11 +64,11 @@ rename that folder to a collision-free alternative first:
});
```
-### SvelteKit
+## SvelteKit
NOTE:
GitLab Pages supports only static sites. For SvelteKit,
-we recommend using [`adapter-static`](https://kit.svelte.dev/docs/adapters#supported-environments-static-sites).
+you can use [`adapter-static`](https://kit.svelte.dev/docs/adapters#supported-environments-static-sites).
When using `adapter-static`, add the following to your `svelte.config.js`:
@@ -86,11 +85,11 @@ export default {
};
```
-### Next.js
+## Next.js
NOTE:
-GitLab Pages supports only static sites. For Next.js, we
-recommend using Next's [Static HTML export functionality](https://nextjs.org/docs/advanced-features/static-html-export)
+GitLab Pages supports only static sites. For Next.js, you can use
+Next's [Static HTML export functionality](https://nextjs.org/docs/advanced-features/static-html-export).
Use the `-o public` flag after `next export` as the build command, for
example:
@@ -118,7 +117,7 @@ GitLab Pages supports only static sites.
1. Configure your Nuxt.js application for
[Static Site Generation](https://nuxtjs.org/docs/features/deployment-targets/#static-hosting).
-### Vite
+## Vite
Update your `vite.config.js` to include the following:
@@ -131,7 +130,7 @@ export default {
}
```
-### Webpack
+## Webpack
Update your `webpack.config.js` to include the following:
@@ -147,9 +146,9 @@ module.exports = {
## Should you commit the `public` folder?
Not necessarily. However, when the GitLab Pages deploy pipeline runs, it looks
-for an [artifact](../../../ci/pipelines/job_artifacts.md) of that name. So
+for an [artifact](../../../ci/pipelines/job_artifacts.md) of that name.
If you set up a job that creates the `public` folder before deploy, such as by
running `npm run build`, committing the folder isn't required.
If you prefer to build your site locally, you can commit the `public` folder and
-omit the build step during the job, instead.
+omit the build step during the job instead.
diff --git a/doc/user/project/pages/redirects.md b/doc/user/project/pages/redirects.md
index 96de457c7f7..cf0c0dbff82 100644
--- a/doc/user/project/pages/redirects.md
+++ b/doc/user/project/pages/redirects.md
@@ -108,9 +108,8 @@ and an [HTTP status code](#http-status-codes):
## Rewrites
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/merge_requests/458) in GitLab 14.3.
-> - Enabled on GitLab.com.
-> - Disabled by default in self-managed GitLab behind the [`FF_ENABLE_PLACEHOLDERS` feature flag](#feature-flag-for-rewrites).
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/merge_requests/458) in GitLab 14.3 [with a flag](../../../administration/feature_flags.md) named `FF_ENABLE_PLACEHOLDERS`. Disabled by default.
+> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/619) in GitLab 15.2.
Provide a status code of `200` to serve the content of the `to` path when the
request matches the `from`:
@@ -267,28 +266,3 @@ However, there are some minor differences:
- Netlify redirects to `/new/:placeholder` (with a
literal `:placeholder`).
- GitLab redirects to `/new/`.
-
-## Feature flag for rewrites
-
-FLAG:
-Rewrites in GitLab Pages is under development, and is deployed behind a feature flag
-that is **disabled by default**.
-
-To enable rewrites, for [Omnibus installations](../../../administration/pages/index.md), define the
-`FF_ENABLE_PLACEHOLDERS` environment variable in the
-[global settings](../../../administration/pages/index.md#global-settings).
-Add the following line to `/etc/gitlab/gitlab.rb` and
-[reconfigure the instance](../../../administration/restart_gitlab.md#omnibus-gitlab-reconfigure):
-
-```ruby
-gitlab_pages['env']['FF_ENABLE_PLACEHOLDERS'] = 'true'
-```
-
-For [source installations](../../../administration/pages/source.md), define the
-`FF_ENABLE_PLACEHOLDERS` environment variable, then
-[restart GitLab](../../../administration/restart_gitlab.md#installations-from-source):
-
-```shell
-export FF_ENABLE_PLACEHOLDERS="true"
-/path/to/pages/bin/gitlab-pages -config gitlab-pages.conf
-```
diff --git a/doc/user/project/push_options.md b/doc/user/project/push_options.md
index b31ef858d59..9e5413b020e 100644
--- a/doc/user/project/push_options.md
+++ b/doc/user/project/push_options.md
@@ -61,10 +61,10 @@ time as pushing changes:
| `merge_request.target=<branch_name>` | Set the target of the merge request to a particular branch or upstream project, such as: `git push -o merge_request.target=project_path/branch` | [11.10](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/26752) |
| `merge_request.merge_when_pipeline_succeeds` | Set the merge request to [merge when its pipeline succeeds](merge_requests/merge_when_pipeline_succeeds.md). | [11.10](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/26752) |
| `merge_request.remove_source_branch` | Set the merge request to remove the source branch when it's merged. | [12.2](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/64320) |
-| `merge_request.title="<title>"` | Set the title of the merge request. Ex: `git push -o merge_request.title="The title I want"`. | [12.2](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/64320) |
-| `merge_request.description="<description>"` | Set the description of the merge request. Ex: `git push -o merge_request.description="The description I want"`. | [12.2](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/64320) |
-| `merge_request.draft` | Mark the merge request as a draft. Ex: `git push -o merge_request.draft`. | [15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/296673) |
-| `merge_request.milestone="<milestone>"` | Set the milestone of the merge request. Ex: `git push -o merge_request.milestone="3.0"`. | [14.1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63960) |
+| `merge_request.title="<title>"` | Set the title of the merge request. For example: `git push -o merge_request.title="The title I want"`. | [12.2](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/64320) |
+| `merge_request.description="<description>"` | Set the description of the merge request. For example: `git push -o merge_request.description="The description I want"`. | [12.2](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/64320) |
+| `merge_request.draft` | Mark the merge request as a draft. For example: `git push -o merge_request.draft`. | [15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/296673) |
+| `merge_request.milestone="<milestone>"` | Set the milestone of the merge request. For example: `git push -o merge_request.milestone="3.0"`. | [14.1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63960) |
| `merge_request.label="<label>"` | Add labels to the merge request. If the label does not exist, it is created. For example, for two labels: `git push -o merge_request.label="label1" -o merge_request.label="label2"`. | [12.3](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/31831) |
| `merge_request.unlabel="<label>"` | Remove labels from the merge request. For example, for two labels: `git push -o merge_request.unlabel="label1" -o merge_request.unlabel="label2"`. | [12.3](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/31831) |
| `merge_request.assign="<user>"` | Assign users to the merge request. Accepts username or user ID. For example, for two users: `git push -o merge_request.assign="user1" -o merge_request.assign="user2"`. | [13.10](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25904), support for usernames added in [15.5](https://gitlab.com/gitlab-org/gitlab/-/issues/344276) |
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 7b7619cfeb5..a73a5329688 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -72,13 +72,13 @@ threads. Some quick actions might not be available to all subscription tiers.
| `/done` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Mark to do as done. |
| `/draft` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Set the [draft status](merge_requests/drafts.md). Use for toggling the draft status ([deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92654) in GitLab 15.4.) |
| `/due <date>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set due date. Examples of valid `<date>` include `in 2 days`, `this Friday` and `December 31st`. |
-| `/duplicate <#issue>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Close this issue and mark as a duplicate of another issue. Also, mark both as related. |
+| `/duplicate <#issue>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Close this issue. Mark as a duplicate of, and related to, issue `<#issue>`. |
| `/epic <epic>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add to epic `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic. |
| `/estimate <time>` or `/estimate_time <time>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Set time estimate. For example, `/estimate 1mo 2w 3d 4h 5m`. Learn more about [time tracking](time_tracking.md). Alias `/estimate_time` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16501) in GitLab 15.6. |
| `/health_status <value>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set [health status](issues/managing_issues.md#health-status). Valid options for `<value>` are `on_track`, `needs_attention`, and `at_risk` ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213814) in GitLab 14.7). |
| `/invite_email email1 email2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add up to six email participants. This action is behind feature flag `issue_email_participants` and is not yet supported in issue templates. |
| `/iteration *iteration:"iteration name"` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set iteration. For example, to set the `Late in July` iteration: `/iteration *iteration:"Late in July"` ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196795) in GitLab 13.1). |
-| `/label ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add one or more labels. Label names can also start without a tilde (`~`), but mixed syntax is not supported. |
+| `/label ~label1 ~label2` or `/labels ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add one or more labels. Label names can also start without a tilde (`~`), but mixed syntax is not supported. |
| `/lock` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Lock the discussions. |
| `/link` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add a link and description to [linked resources](../../operations/incident_management/linked_resources.md) in an incident ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/374964) in GitLab 15.5). |
| `/merge` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Merge changes. Depending on the project setting, this may be [when the pipeline succeeds](merge_requests/merge_when_pipeline_succeeds.md), or adding to a [Merge Train](../../ci/pipelines/merge_trains.md). |
diff --git a/doc/user/project/releases/index.md b/doc/user/project/releases/index.md
index 75a25678125..6d5378bddb7 100644
--- a/doc/user/project/releases/index.md
+++ b/doc/user/project/releases/index.md
@@ -184,20 +184,20 @@ is not available.
## Edit a release
-Only users with at least the Developer role can edit releases.
-Read more about [Release permissions](#release-permissions).
+To edit the details of a release after it's created, you can use the
+[Update a release API](../../../api/releases/index.md#update-a-release) or the UI.
-To edit the details of a release:
+Prerequisites:
+
+- You must have at least the Developer role.
+
+In the UI:
1. On the left sidebar, select **Deployments > Releases**.
1. In the top-right corner of the release you want to modify, select **Edit this release** (the pencil icon).
1. On the **Edit Release** page, change the release's details.
1. Select **Save changes**.
-You can edit the release title, notes, associated milestones, and asset links.
-To change the release date use the
-[Releases API](../../../api/releases/index.md#update-a-release).
-
## Delete a release
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213862) in GitLab 15.2
@@ -209,11 +209,15 @@ Prerequisites:
- You must have at least the Developer role. Read more about [Release permissions](#release-permissions).
-To delete a release in the UI:
+To delete a release, use either the
+[Delete a release API](../../../api/releases/index.md#delete-a-release) or the UI.
+
+In the UI:
1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Releases**.
-1. In the top-right corner of the release you want to delete, select **Edit this release** (**{pencil}**).
+1. In the top-right corner of the release you want to delete, select **Edit this release**
+ (**{pencil}**).
1. On the **Edit Release** page, select **Delete**.
1. Select **Delete release**.
@@ -449,11 +453,11 @@ In the API:
## Release permissions
-> [The permission model for create, update and delete actions was fixed](https://gitlab.com/gitlab-org/gitlab/-/issues/327505) in GitLab 14.1.
+> Fixes to the permission model for create, update and delete actions [were introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/327505) in GitLab 14.1.
### View a release and download assets
-> [Changes were made to the Guest role access](https://gitlab.com/gitlab-org/gitlab/-/issues/335209) in GitLab 14.5.
+> Changes to the Guest role [were introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335209) in GitLab 14.5.
- Users with at least the Reporter role
have read and download access to the project releases.
diff --git a/doc/user/project/repository/branches/default.md b/doc/user/project/repository/branches/default.md
index 6801899160d..87caeee73e3 100644
--- a/doc/user/project/repository/branches/default.md
+++ b/doc/user/project/repository/branches/default.md
@@ -36,11 +36,15 @@ the [Git commands you need](#update-the-default-branch-name-in-your-repository)
## Change the default branch name for a project
-To update the default branch name for an individual [project](../../index.md):
+Prerequisites:
-1. Sign in to GitLab with at least the Maintainer role.
+- You have the Owner or Maintainer role in the project.
+
+To update the default branch for an individual [project](../../index.md):
+
+1. On the top bar, select **Main menu > Projects** and find your project.
1. In the left navigation menu, go to **Settings > Repository**.
-1. Expand **Default branch**, and select a new default branch.
+1. Expand **Default branch**. For **Initial default branch name**, select a new default branch.
1. Optional. Select the **Auto-close referenced issues on default branch** checkbox to close
issues when a merge request
[uses a closing pattern](../../issues/managing_issues.md#closing-issues-automatically).
@@ -66,8 +70,8 @@ groups and subgroups can override this instance-wide setting for their projects.
1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Repository**.
-1. Expand **Default initial branch name**.
-1. Change the default initial branch to a custom name of your choice.
+1. Expand **Default branch**.
+1. For **Initial default branch name**, select a new default branch.
1. Select **Save changes**.
Projects created on this instance after you change the setting use the
@@ -78,11 +82,12 @@ overrides it.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/221014) in GitLab 13.6.
-Users with at least the Owner role of groups and subgroups can configure the default branch name for a group:
+Users with the Owner role of groups and subgroups can configure the default branch name for a group:
-1. Go to the group **Settings > Repository**.
+1. On the top bar, select **Main menu > Group** and find your group.
+1. On the left sidebar, select **Settings > Repository**.
1. Expand **Default branch**.
-1. Change the default initial branch to a custom name of your choice.
+1. For **Initial default branch name**, select a new default branch.
1. Select **Save changes**.
Projects created in this group after you change the setting use the custom branch name,
diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md
index 6cc7394e7b3..a86e32b4721 100644
--- a/doc/user/project/repository/branches/index.md
+++ b/doc/user/project/repository/branches/index.md
@@ -101,10 +101,13 @@ This feature allows you to search and select branches quickly. Search results ap
- Branches with names that matched search terms exactly.
- Other branches with names that include search terms, sorted alphabetically.
-Sometimes when you have hundreds of branches you may want a more flexible matching pattern. In such cases you can use the following:
+Sometimes when you have hundreds of branches you may want a more flexible matching pattern. In such cases you can use the following operators:
-- `^feature` matches only branch names that begin with 'feature'.
-- `feature$` matches only branch names that end with 'feature'.
+- `^` matches beginning of branch name, for example `^feat` would match `feat/user-authentication`
+- `$` matches end of branch name, for example `widget$` would match `feat/search-box-widget`
+- `*` wildcard matcher, for example `branch*cache*` would match `fix/branch-search-cache-expiration`
+
+These operators can be mixed, for example `^chore/*migration$` would match `chore/user-data-migration`
## Swap revisions
@@ -116,14 +119,26 @@ The Swap revisions feature allows you to swap the Source and Target revisions. W
![After swap revisions](img/swap_revisions_after_v13_12.png)
-<!-- ## Troubleshooting
+## Troubleshooting
+
+### Error: ambiguous `HEAD` branch exists
+
+In versions of Git earlier than 2.16.0, you could create a branch named `HEAD`.
+This branch named `HEAD` collides with the internal reference (also named `HEAD`)
+Git uses to describe the active (checked out) branch. This naming collision can
+prevent you from updating the default branch of your repository:
+
+```plaintext
+Error: Could not set the default branch. Do you have a branch named 'HEAD' in your repository?
+```
+
+To fix this problem:
-Include any troubleshooting steps that you can foresee. If you know beforehand what issues
-one might have when setting this up, or when something is changed, or on upgrading, it's
-important to describe those, too. Think of things that may go wrong and include them here.
-This is important to minimize requests for support, and to avoid doc comments with
-questions that you know someone might ask.
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Repository > Branches**.
+1. Search for a branch named `HEAD`.
+1. Make sure the branch has no uncommitted changes.
+1. Select **Delete branch**, then **Yes, delete branch**.
-Each scenario can be a third-level heading, for example `### Getting error message X`.
-If you have none to add when creating a doc, leave this section in place
-but commented out to help encourage others to add to it in the future. -->
+Git versions [2.16.0 and later](https://github.com/git/git/commit/a625b092cc59940521789fe8a3ff69c8d6b14eb2),
+prevent you from creating a branch with this name.
diff --git a/doc/user/project/repository/gpg_signed_commits/index.md b/doc/user/project/repository/gpg_signed_commits/index.md
index a1f57f51f26..6b67ffd0e59 100644
--- a/doc/user/project/repository/gpg_signed_commits/index.md
+++ b/doc/user/project/repository/gpg_signed_commits/index.md
@@ -4,7 +4,7 @@ group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Signing commits with GPG **(FREE)**
+# Sign commits with GPG **(FREE)**
You can sign the commits you make in a GitLab repository with a
GPG ([GNU Privacy Guard](https://gnupg.org/)) key. When you add a cryptographic
@@ -160,14 +160,6 @@ to use this key:
git config --global user.signingkey <KEY ID>
```
-1. Optional. If Git uses `gpg` and you get errors like `secret key not available`
- or `gpg: signing failed: secret key not available`, run this command to
- use `gpg2` instead:
-
- ```shell
- git config --global gpg.program gpg2
- ```
-
### Sign your Git commits
After you [add your public key to your account](#add-a-gpg-key-to-your-account),
@@ -246,6 +238,7 @@ If you must unverify both future and past commits,
## Related topics
- [Sign commits and tags with X.509 certificates](../x509_signed_commits/index.md)
+- [Sign commits with SSH keys](../ssh_signed_commits/index.md)
- [Commits API](../../../../api/commits.md)
- GPG resources:
- [Git Tools - Signing Your Work](https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work)
@@ -269,3 +262,27 @@ or a GPG key. The verification process for both methods can fail for multiple re
| `UNVERIFIED_KEY` | The key associated with the GPG signature has no verified email address associated with the committer. | Add and verify the email to your GitLab profile, [update the GPG key to include the email address](https://security.stackexchange.com/a/261468), or amend the commit to use a different committer email address. |
| `UNKNOWN_KEY` | The GPG key associated with the GPG signature for this commit is unknown to GitLab. | [Add the GPG key](#add-a-gpg-key-to-your-account) to your GitLab profile. |
| `MULTIPLE_SIGNATURES` | Multiple GPG or X.509 signatures have been found for the commit. | Amend the commit to use only one GPG or X.509 signature. |
+
+### Secret key not available
+
+If you receive the errors `secret key not available`
+or `gpg: signing failed: secret key not available`, try using `gpg2` instead of `gpg`:
+
+```shell
+git config --global gpg.program gpg2
+```
+
+If your GPG key is password protected and the password entry prompt does not appear,
+add `export GPG_TTY=$(tty)` to your shell's `rc` file (commonly `~/.bashrc` or `~/.zshrc`)
+
+### GPG failed to sign the data
+
+If your GPG key is password protected and you receive the error:
+
+```shell
+error: gpg failed to sign the data
+fatal: failed to write commit object
+```
+
+If the password entry prompt does not appear, add `export GPG_TTY=$(tty)` to your shell's `rc` file
+(commonly `~/.bashrc` or `~/.zshrc`) and restart your terminal.
diff --git a/doc/user/project/repository/ssh_signed_commits/index.md b/doc/user/project/repository/ssh_signed_commits/index.md
new file mode 100644
index 00000000000..06affa54a51
--- /dev/null
+++ b/doc/user/project/repository/ssh_signed_commits/index.md
@@ -0,0 +1,174 @@
+---
+stage: Create
+group: Source Code
+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
+---
+
+# Sign commits with SSH keys **(FREE)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/343879) in GitLab 15.7 [with a flag](../../../../administration/feature_flags.md) named `ssh_commit_signatures`. Enabled by default.
+
+FLAG:
+On self-managed GitLab, by default this feature is available. To hide the feature,
+ask an administrator to [disable the feature flag](../../../../administration/feature_flags.md) named `ssh_commit_signatures`.
+On GitLab.com, this feature is available.
+
+Use SSH keys to sign Git commits in the same manner as
+[GPG signed commits](../gpg_signed_commits/index.md). When you sign commits
+with SSH keys, GitLab uses the SSH public keys associated with your
+GitLab account to cryptographically verify the commit signature.
+If successful, GitLab displays a **Verified** label on the commit.
+
+You may use the same SSH keys for `git+ssh` authentication to GitLab
+and signing commit signatures as long as their usage type is **Authentication & Signing**.
+It can be verified on the page for [adding an SSH key to your GitLab account](../../../ssh.md#add-an-ssh-key-to-your-gitlab-account).
+
+To learn more about managing the SSH keys associated with your GitLab account, read
+[use SSH keys to communicate with GitLab](../../../ssh.md).
+
+## Configure Git to sign commits with your SSH key
+
+After you [create an SSH key](../../../ssh.md#generate-an-ssh-key-pair) and
+[add it to your GitLab account](../../../ssh.md#add-an-ssh-key-to-your-gitlab-account)
+or [generate it using a password manager](../../../ssh.md#generate-an-ssh-key-pair-with-a-password-manager),
+configure Git to begin using the key.
+
+Prerequisites:
+
+- Git 2.34.0 or newer.
+- OpenSSH 8.0 or newer.
+
+ NOTE:
+ OpenSSH 8.7 has broken signing functionality. If you are on OpenSSH 8.7, upgrade to OpenSSH 8.8.
+
+- A SSH key with the usage type of either **Authentication & Signing** or **Signing**.
+ The SSH key must be one of these types:
+ - [ED25519](../../../ssh.md#ed25519-ssh-keys) (recommended)
+ - [RSA](../../../ssh.md#rsa-ssh-keys)
+
+To configure Git to use your key:
+
+1. Configure Git to use SSH for commit signing:
+
+ ```shell
+ git config --global gpg.format ssh
+ ```
+
+1. Specify which SSH key should be used as the signing key, changing the filename
+ (here, `~/.ssh/examplekey`) to the location of your key. The filename may
+ differ, depending on how you generated your key:
+
+ ```shell
+ git config --global user.signingkey ~/.ssh/examplekey
+ ```
+
+## Sign commits with your SSH key
+
+Prerequisites:
+
+- You've [created an SSH key](../../../ssh.md#generate-an-ssh-key-pair).
+- You've [added the key](../../../ssh.md#add-an-ssh-key-to-your-gitlab-account) to your GitLab account.
+- You've [configured Git to sign commits](#configure-git-to-sign-commits-with-your-ssh-key) with your SSH key.
+
+To sign a commit:
+
+1. Use the `-S` flag when signing your commits:
+
+ ```shell
+ git commit -S -m "My commit msg"
+ ```
+
+1. Optional. If you don't want to type the `-S` flag every time you commit, tell
+ Git to sign your commits automatically:
+
+ ```shell
+ git config --global commit.gpgsign true
+ ```
+
+1. If your SSH key is protected, Git prompts you to enter your passphrase.
+1. Push to GitLab.
+1. Check that your commits [are verified](#verify-commits).
+ Signature verification uses the `allowed_signers` file to associate emails and SSH keys.
+ For help configuring this file, read [Verify commits locally](#verify-commits-locally).
+
+## Verify commits
+
+You can review commits for a merge request, or for an entire project, to confirm
+they are signed:
+
+1. To review commits for a project:
+ 1. On the top bar, select **Main menu > Projects** and find your project.
+ 1. On the left sidebar, select **Repository > Commits**.
+1. To review commits for a merge request:
+ 1. On the top bar, select **Main menu > Projects** and find your project.
+ 1. On the left sidebar, select **Merge requests**, then select your merge request.
+ 1. Select **Commits**.
+1. Identify the commit you want to review. Signed commits show either a **Verified**
+ or **Unverified** badge, depending on the verification status of the signature.
+ Unsigned commits do not display a badge.
+1. To display the signature details for a commit, select **Verified**. GitLab shows
+ the SSH key's fingerprint.
+
+## Verify commits locally
+
+To verify commits locally, create an
+[allowed signers file](https://man7.org/linux/man-pages/man1/ssh-keygen.1.html#ALLOWED_SIGNERS)
+for Git to associate SSH public keys with users:
+
+1. Create an allowed signers file:
+
+ ```shell
+ touch allowed_signers
+ ```
+
+1. Configure the `allowed_signers` file in Git:
+
+ ```shell
+ git config gpg.ssh.allowedSignersFile "$(pwd)/allowed_signers"
+ ```
+
+1. Add your entry to the allowed signers file. Use this command to add your
+ email address and public SSH key to the `allowed_signers` file. Replace `<MY_KEY>`
+ with the name of your key, and `~/.ssh/allowed_signers`
+ with the location of your project's `allowed_signers` file:
+
+ ```shell
+ # Modify this line to meet your needs.
+ # Declaring the `git` namespace helps prevent cross-protocol attacks.
+ echo "$(git config --get user.email) namespaces=\"git\" $(cat ~/.ssh/<MY_KEY>.pub)" >> ~/.ssh/allowed_signers
+ ```
+
+ The resulting entry in the `allowed_signers` file contains your email address, key type,
+ and key contents, like this:
+
+ ```plaintext
+ example@gitlab.com namespaces="git" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAmaTS47vRmsKyLyK1jlIFJn/i8wdGQ3J49LYyIYJ2hv
+ ```
+
+1. Repeat the previous step for each user who you want to verify signatures for.
+ Consider checking this file in to your Git repository if you want to locally
+ verify signatures for many different contributors.
+
+1. Use `git log --show-signature` to view the signature status for the commits:
+
+ ```shell
+ $ git log --show-signature
+
+ commit e2406b6cd8ebe146835ceab67ff4a5a116e09154 (HEAD -> main, origin/main, origin/HEAD)
+ Good "git" signature for johndoe@example.com with ED25519 key SHA256:Ar44iySGgxic+U6Dph4Z9Rp+KDaix5SFGFawovZLAcc
+ Author: John Doe <johndoe@example.com>
+ Date: Tue Nov 29 06:54:15 2022 -0600
+
+ SSH signed commit
+ ```
+
+## Revoke an SSH key for signing commits
+
+You can't revoke an SSH key used for signing commits. To learn more, read
+[Add revocation for SSH keys](https://gitlab.com/gitlab-org/gitlab/-/issues/382984).
+
+## Related topics
+
+- [Sign commits and tags with X.509 certificates](../x509_signed_commits/index.md)
+- [Sign commits with GPG](../gpg_signed_commits/index.md)
+- [Commits API](../../../../api/commits.md)
diff --git a/doc/user/project/repository/web_editor.md b/doc/user/project/repository/web_editor.md
index 773662edb17..cc89ca0fb1a 100644
--- a/doc/user/project/repository/web_editor.md
+++ b/doc/user/project/repository/web_editor.md
@@ -73,7 +73,7 @@ To close the preview panel, do one of the following:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56159) in GitLab 13.11 for self-managed instances.
Web Editor enables you to highlight a single line by adding specially formatted
-hash information to the URL's file path segment. For example, the file path segment
+hash information to the file path segment of the URL. For example, the file path segment
`MY_FILE.js#L3` instructs the Web Editor to highlight line 3.
The Web Editor also enables you to highlight multiple lines using a similar pattern. In
diff --git a/doc/user/project/repository/x509_signed_commits/index.md b/doc/user/project/repository/x509_signed_commits/index.md
index e16f5e4defe..42f7be30822 100644
--- a/doc/user/project/repository/x509_signed_commits/index.md
+++ b/doc/user/project/repository/x509_signed_commits/index.md
@@ -160,6 +160,8 @@ can start signing your tags:
## Related topics
- [Rake task for X.509 signatures](../../../../raketasks/x509_signatures.md)
+- [Sign commits with GPG](../gpg_signed_commits/index.md)
+- [Sign commits with SSH keys](../ssh_signed_commits/index.md)
## Troubleshooting
diff --git a/doc/user/project/service_desk.md b/doc/user/project/service_desk.md
index 199f25f1122..0b52440d1e6 100644
--- a/doc/user/project/service_desk.md
+++ b/doc/user/project/service_desk.md
@@ -202,39 +202,52 @@ worker and it would not recognize `incoming_email` emails.
To configure a custom mailbox for Service Desk with IMAP, add the following snippets to your configuration file in full:
-- Example for installations from source:
-
- ```yaml
- service_desk_email:
- enabled: true
- address: "project_contact+%{key}@example.com"
- user: "project_contact@example.com"
- password: "[REDACTED]"
- host: "imap.gmail.com"
- port: 993
- ssl: true
- start_tls: false
- log_path: "log/mailroom.log"
- mailbox: "inbox"
- idle_timeout: 60
- expunge_deleted: true
- ```
+::Tabs
-- Example for Omnibus GitLab installations:
+:::TabTitle Linux package (Omnibus)
- ```ruby
- gitlab_rails['service_desk_email_enabled'] = true
- gitlab_rails['service_desk_email_address'] = "project_contact+%{key}@gmail.com"
- gitlab_rails['service_desk_email_email'] = "project_contact@gmail.com"
- gitlab_rails['service_desk_email_password'] = "[REDACTED]"
- gitlab_rails['service_desk_email_mailbox_name'] = "inbox"
- gitlab_rails['service_desk_email_idle_timeout'] = 60
- gitlab_rails['service_desk_email_log_file'] = "/var/log/gitlab/mailroom/mail_room_json.log"
- gitlab_rails['service_desk_email_host'] = "imap.gmail.com"
- gitlab_rails['service_desk_email_port'] = 993
- gitlab_rails['service_desk_email_ssl'] = true
- gitlab_rails['service_desk_email_start_tls'] = false
- ```
+NOTE:
+In GitLab 15.3 and later, Service Desk uses `webhook` (internal API call) by default instead of enqueuing a Sidekiq job.
+To use `webhook` on an Omnibus installation running GitLab 15.3, you must generate a secret file.
+For more context, visit [Omnibus GitLab MR 5927](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/5927).
+In GitLab 15.4, reconfiguring an Omnibus installation generates this secret file automatically, so no secret file configuration setting is needed.
+For details, visit [issue 1462](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1462).
+
+```ruby
+gitlab_rails['service_desk_email_enabled'] = true
+gitlab_rails['service_desk_email_address'] = "project_contact+%{key}@gmail.com"
+gitlab_rails['service_desk_email_email'] = "project_contact@gmail.com"
+gitlab_rails['service_desk_email_password'] = "[REDACTED]"
+gitlab_rails['service_desk_email_mailbox_name'] = "inbox"
+gitlab_rails['service_desk_email_idle_timeout'] = 60
+gitlab_rails['service_desk_email_log_file'] = "/var/log/gitlab/mailroom/mail_room_json.log"
+gitlab_rails['service_desk_email_host'] = "imap.gmail.com"
+gitlab_rails['service_desk_email_port'] = 993
+gitlab_rails['service_desk_email_ssl'] = true
+gitlab_rails['service_desk_email_start_tls'] = false
+```
+
+:::TabTitle Self-compiled (source)
+
+```yaml
+service_desk_email:
+ enabled: true
+ address: "project_contact+%{key}@example.com"
+ user: "project_contact@example.com"
+ password: "[REDACTED]"
+ host: "imap.gmail.com"
+ delivery_method: webhook
+ secret_file: .gitlab-mailroom-secret
+ port: 993
+ ssl: true
+ start_tls: false
+ log_path: "log/mailroom.log"
+ mailbox: "inbox"
+ idle_timeout: 60
+ expunge_deleted: true
+```
+
+::EndTabs
The configuration options are the same as for configuring
[incoming email](../../administration/incoming_email.md#set-it-up).
@@ -360,12 +373,17 @@ does not count toward the license limit count.
### Moving a Service Desk issue
+> [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/372246) in GitLab 15.7: customers continue receiving notifications when a Service Desk issue is moved.
+
Service Desk issues can be moved like any other issue in GitLab.
You can move a Service Desk issue the same way you
[move a regular issue](issues/managing_issues.md#move-an-issue) in GitLab.
-If a Service Desk issue is moved to a different project the customer who created the issue stops receiving emails.
+If a Service Desk issue is moved to a different project with Service Desk enabled,
+the customer who created the issue continues to receive email notifications.
+Because a moved issue is first closed, then copied, the customer is considered to be a participant
+in both issues. They continue to receive any notifications in the old issue and the new one.
## Troubleshooting Service Desk
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index 3c12bb9b80f..d83a80ddb13 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -12,7 +12,22 @@ then imported into a new GitLab instance. You can also:
- [Migrate groups](../../group/import/index.md) using the preferred method.
- [Migrate groups using file exports](../../group/settings/import_export.md).
-## Set up project import/export
+GitLab maps user contributions correctly when an admin access token is used to perform the import.
+
+As a result, migrating projects using file exports does not map user contributions correctly when you are importing
+projects from a self-managed instance to GitLab.com.
+
+Instead, all GitLab user associations (such as comment author) are changed to the user importing the project. For more
+information, see the prerequisites and important notes in these sections:
+
+- [Export a project and its data](../settings/import_export.md#export-a-project-and-its-data).
+- [Import the project](../settings/import_export.md#import-a-project-and-its-data).
+
+To preserve contribution history, [migrate using direct transfer](../../group/import/index.md#migrate-groups-by-direct-transfer-recommended).
+
+If you migrate from GitLab.com to self-managed GitLab, an administrator can create users on the self-managed GitLab instance.
+
+## Set up project to migrate using file exports
Before you can import or export a project and its data, you must set it up.
@@ -24,8 +39,7 @@ Before you can import or export a project and its data, you must set it up.
## Between CE and EE
You can export projects from the [Community Edition to the Enterprise Edition](https://about.gitlab.com/install/ce-or-ee/)
-and vice versa. This assumes [version history](#version-history)
-requirements are met.
+and vice versa. This assumes [version history](#version-history) requirements are met.
If you're exporting a project from the Enterprise Edition to the Community Edition, you may lose
data that is retained only in the Enterprise Edition. For more information, see
@@ -37,8 +51,7 @@ Before you can import a project, you must export it.
Prerequisites:
-- Review the list of [items that are exported](#items-that-are-exported).
- Not all items are exported.
+- Review the list of [items that are exported](#items-that-are-exported). Not all items are exported.
- You must have at least the Maintainer role for the project.
To export a project and its data, follow these steps:
@@ -134,7 +147,7 @@ To import a project:
1. Enter your project name and URL. Then select the file you exported previously.
1. Select **Import project** to begin importing. Your newly imported project page appears shortly.
-To get the status of an import, you can query it through the [Project import/export API](../../../api/project_import_export.md#import-status).
+To get the status of an import, you can query it through the [API](../../../api/project_import_export.md#import-status).
As described in the API documentation, the query may return an import error or exceptions.
### Changes to imported items
@@ -187,7 +200,7 @@ Imported users can be mapped by their public email addresses on self-managed ins
For project migration imports performed over GitLab.com groups, preserving author information is
possible through a [professional services engagement](https://about.gitlab.com/services/migration/).
-## Rate Limits
+## Rate limits
To help avoid abuse, by default, users are rate limited to:
@@ -197,9 +210,6 @@ To help avoid abuse, by default, users are rate limited to:
| Download export | 1 download per group per minute |
| Import | 6 projects per minute |
-GitLab.com may have [different settings](../../gitlab_com/index.md#importexport)
-from the defaults.
-
## Version history
### 14.0+
@@ -211,8 +221,8 @@ is NDJSON.
### 13.0+
Starting with GitLab 13.0, GitLab can import bundles that were exported from a different GitLab deployment.
-This ability is limited to two previous GitLab [minor](../../../policy/maintenance.md#versioning)
-releases, which is similar to our process for [Security Releases](../../../policy/maintenance.md#security-releases).
+**This ability is limited to two previous GitLab [minor](../../../policy/maintenance.md#versioning)
+releases**, which is similar to our process for [Security Releases](../../../policy/maintenance.md#security-releases).
For example:
@@ -221,36 +231,9 @@ For example:
| 13.0 | 13.0, 12.10, 12.9 |
| 13.1 | 13.1, 13.0, 12.10 |
-### 12.x
-
-Prior to 13.0 this was a defined compatibility table:
-
-| Exporting GitLab version | Importing GitLab version |
-| -------------------------- | -------------------------- |
-| 11.7 to 12.10 | 11.7 to 12.10 |
-| 11.1 to 11.6 | 11.1 to 11.6 |
-| 10.8 to 11.0 | 10.8 to 11.0 |
-| 10.4 to 10.7 | 10.4 to 10.7 |
-| 10.3 | 10.3 |
-| 10.0 to 10.2 | 10.0 to 10.2 |
-| 9.4 to 9.6 | 9.4 to 9.6 |
-| 9.2 to 9.3 | 9.2 to 9.3 |
-| 8.17 to 9.1 | 8.17 to 9.1 |
-| 8.13 to 8.16 | 8.13 to 8.16 |
-| 8.12 | 8.12 |
-| 8.10.3 to 8.11 | 8.10.3 to 8.11 |
-| 8.10.0 to 8.10.2 | 8.10.0 to 8.10.2 |
-| 8.9.5 to 8.9.11 | 8.9.5 to 8.9.11 |
-| 8.9.0 to 8.9.4 | 8.9.0 to 8.9.4 |
-
-Projects can be exported and imported only between versions of GitLab with matching Import/Export versions.
-
-For example, 8.10.3 and 8.11 have the same Import/Export version (0.1.3)
-and the exports between them are compatible.
-
## Related topics
-- [Project import/export API](../../../api/project_import_export.md)
-- [Project import/export administration Rake tasks](../../../administration/raketasks/project_import_export.md)
-- [Group import/export](../../group/settings/import_export.md)
-- [Group import/export API](../../../api/group_import_export.md)
+- [Project import and export API](../../../api/project_import_export.md)
+- [Project import and export administration Rake tasks](../../../administration/raketasks/project_import_export.md)
+- [Migrating GitLab groups](../../group/import/index.md)
+- [Group import and export API](../../../api/group_import_export.md)
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index a872a339433..a88427ab20b 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Workspace
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments"
+info: 'To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments'
type: reference, index, howto
---
@@ -75,41 +75,44 @@ To configure visibility, features, and permissions for a project:
Use the toggles to enable or disable features in the project.
-| Option | More access limit options | Description |
-|:---------------------------------|:--------------------------|:--------------|
-| **Issues** | ✓ | Activates the GitLab issues tracker. |
-| **Repository** | ✓ | Enables [repository](../repository/index.md) functionality |
+| Option | More access limit options | Description |
+| :------------------------------- | :------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **Issues** | ✓ | Activates the GitLab issues tracker. |
+| **Repository** | ✓ | Enables [repository](../repository/index.md) functionality |
| **Merge requests** | ✓ | Enables [merge request](../merge_requests/index.md) functionality; also see [Merge request settings](#configure-merge-request-settings-for-a-project). |
-| **Forks** | ✓ | Enables [forking](../repository/forking_workflow.md) functionality. |
-| **Git Large File Storage (LFS)** | | Enables the use of [large files](../../../topics/git/lfs/index.md#git-large-file-storage-lfs). |
-| **Packages** | | Supports configuration of a [package registry](../../../administration/packages/index.md#gitlab-package-registry-administration) functionality. |
-| **CI/CD** | ✓ | Enables [CI/CD](../../../ci/index.md) functionality. |
-| **Container Registry** | | Activates a [registry](../../packages/container_registry/index.md) for your Docker images. |
-| **Analytics** | ✓ | Enables [analytics](../../analytics/index.md). |
-| **Requirements** | ✓ | Control access to [Requirements Management](../requirements/index.md). |
-| **Security & Compliance** | ✓ | Control access to [security features](../../application_security/index.md). |
-| **Wiki** | ✓ | Enables a separate system for [documentation](../wiki/index.md). |
-| **Snippets** | ✓ | Enables [sharing of code and text](../../snippets.md). |
-| **Pages** | ✓ | Allows you to [publish static websites](../pages/index.md). |
-| **Metrics Dashboard** | ✓ | Control access to [metrics dashboard](../integrations/prometheus.md). |
-| **Releases** | ✓ | Control access to [Releases](../releases/index.md). |
+| **Forks** | ✓ | Enables [forking](../repository/forking_workflow.md) functionality. |
+| **Git Large File Storage (LFS)** | | Enables the use of [large files](../../../topics/git/lfs/index.md#git-large-file-storage-lfs). |
+| **Packages** | | Supports configuration of a [package registry](../../../administration/packages/index.md#gitlab-package-registry-administration) functionality. |
+| **CI/CD** | ✓ | Enables [CI/CD](../../../ci/index.md) functionality. |
+| **Container Registry** | | Activates a [registry](../../packages/container_registry/index.md) for your Docker images. |
+| **Analytics** | ✓ | Enables [analytics](../../analytics/index.md). |
+| **Requirements** | ✓ | Control access to [Requirements Management](../requirements/index.md). |
+| **Security & Compliance** | ✓ | Control access to [security features](../../application_security/index.md). |
+| **Wiki** | ✓ | Enables a separate system for [documentation](../wiki/index.md). |
+| **Snippets** | ✓ | Enables [sharing of code and text](../../snippets.md). |
+| **Pages** | ✓ | Allows you to [publish static websites](../pages/index.md). |
+| **Metrics Dashboard** | ✓ | Control access to [metrics dashboard](../integrations/prometheus.md). |
+| **Releases** | ✓ | Control access to [Releases](../releases/index.md). |
| **Environments** | ✓ | Control access to [Environments and Deployments](../../../ci/environments/index.md). |
| **Feature flags** | ✓ | Control access to [Feature Flags](../../../operations/feature_flags.md). |
-| **Monitor** | ✓ | Control access to [Monitor](../../../operations/index.md) features. |
-| **Infrastructure** | ✓ | Control access to [Infrastructure](../../infrastructure/index.md) features. |
+| **Monitor** | ✓ | Control access to [Monitor](../../../operations/index.md) features. |
+| **Infrastructure** | ✓ | Control access to [Infrastructure](../../infrastructure/index.md) features. |
When you disable a feature, the following additional features are also disabled:
- If you disable the **Issues** feature, project users cannot use:
+
- **Issue Boards**
- **Service Desk**
- Project users can still access **Milestones** from merge requests.
- If you disable **Issues** and **Merge Requests**, project users cannot use:
+
- **Labels**
- **Milestones**
- If you disable **Repository**, project users cannot access:
+
- **Merge requests**
- **CI/CD**
- **Container Registry**
@@ -236,10 +239,8 @@ Prerequisites:
- You must have at least the Maintainer role for the [group](../../group/manage.md#create-a-group) to which you are transferring.
- You must be the Owner of the project you transfer.
- The group must allow creation of new projects.
-- The project must not contain any [container images](../../packages/container_registry/index.md#limitations).
- - If you transfer a project to a different root namespace,
- the project must not contain any
- [NPM packages](../../packages/npm_registry/index.md#limitations).
+- The project must not contain any [container images](../../packages/container_registry/index.md#known-issues).
+- Remove any npm packages. If you transfer a project to a different root namespace, the project must not contain any npm packages. When you update the path of a user or group, or transfer a subgroup or project, you must remove any npm packages first. You cannot update the root namespace of a project with npm packages. Make sure you update your .npmrc files to follow the naming convention and run npm publish if necessary.
To transfer a project:
@@ -262,7 +263,7 @@ When you transfer a project from a namespace licensed for GitLab SaaS Premium or
- [Project access tokens](../../../user/project/settings/project_access_tokens.md) are revoked
- [Pipeline subscriptions](../../../ci/pipelines/index.md#trigger-a-pipeline-when-an-upstream-project-is-rebuilt)
-and [test cases](../../../ci/test_cases/index.md) are deleted.
+ and [test cases](../../../ci/test_cases/index.md) are deleted.
## Delete a project
@@ -270,7 +271,7 @@ You can mark a project to be deleted.
Prerequisite:
-- You must have at least the Owner role for a project.
+- You must have the Owner role for a project.
To delete a project:
@@ -308,7 +309,7 @@ If you don't want to wait, you can delete a project immediately.
Prerequisites:
-- You must have at least the Owner role for a project.
+- You must have the Owner role for a project.
- You have [marked the project for deletion](#delete-a-project).
To immediately delete a project marked for deletion:
@@ -361,11 +362,11 @@ Configure [alert integrations](../../../operations/incident_management/integrati
#### Alert integration
-Automatically [create](../../../operations/incident_management/incidents.md#create-incidents-automatically), [notify on](../../../operations/incident_management/paging.md#email-notifications-for-alerts), and [resolve](../../../operations/incident_management/incidents.md#automatically-close-incidents-via-recovery-alerts) incidents based on GitLab alerts.
+Automatically [create](../../../operations/metrics/alerts.md#trigger-actions-from-alerts), [notify on](../../../operations/incident_management/paging.md#email-notifications-for-alerts), and [resolve](../../../operations/incident_management/manage_incidents.md#automatically-close-incidents-via-recovery-alerts) incidents based on GitLab alerts.
#### PagerDuty integration
-[Create incidents in GitLab for each PagerDuty incident](../../../operations/incident_management/incidents.md#create-incidents-via-the-pagerduty-webhook).
+[Create incidents in GitLab for each PagerDuty incident](../../../operations/incident_management/manage_incidents.md#using-the-pagerduty-webhook).
#### Incident settings
diff --git a/doc/user/project/settings/project_access_tokens.md b/doc/user/project/settings/project_access_tokens.md
index f27672a1b07..cb7ba2a7df2 100644
--- a/doc/user/project/settings/project_access_tokens.md
+++ b/doc/user/project/settings/project_access_tokens.md
@@ -69,6 +69,11 @@ To create a project access token:
A project access token is displayed. Save the project access token somewhere safe. After you leave or refresh the page, you can't view it again.
+WARNING:
+Project access tokens are treated as [internal users](../../../development/internal_users.md).
+If an internal user creates a project access token, that token is able to access
+all projects that have visibility level set to [Internal](../../public_access.md).
+
## Revoke a project access token
To revoke a project access token:
diff --git a/doc/user/project/time_tracking.md b/doc/user/project/time_tracking.md
index 71b8c911839..186ca0ba9f0 100644
--- a/doc/user/project/time_tracking.md
+++ b/doc/user/project/time_tracking.md
@@ -74,6 +74,25 @@ Prerequisites:
- You must have at least the Reporter role for the project.
+#### Using the user interface
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101563) in GitLab 15.7.
+
+To add a time entry using the user interface:
+
+1. In the **Time tracking** section of the sidebar, select **Add time entry** (**{plus}**). A modal opens.
+1. Enter:
+
+ - The amount of time spent.
+ - Optional. When it was spent.
+ - Optional. A summary.
+
+1. Select **Save**.
+
+The **Spent** total in the sidebar is updated and you can view all entries in a [time tracking report](#view-a-time-tracking-report).
+
+#### Using a quick action
+
To enter time spent, use the `/spend` [quick action](quick_actions.md), followed by the time.
For example, if you need
@@ -90,15 +109,10 @@ Draft MR and respond to initial comments
/spend 30m
```
-### Add time spent on a specific date
-
-Prerequisites:
-
-- You must have at least the Reporter role for the project.
+To log when time was spent, enter a date after the time, using the `YYYY-MM-DD` format.
-You can log time in the past by providing a date after the time.
For example, to log 1 hour of time spent on 31 January 2021,
-type `/spend 1h 2021-01-31`.
+enter `/spend 1h 2021-01-31`.
If you type a future date, no time is logged.
diff --git a/doc/user/project/web_ide/index.md b/doc/user/project/web_ide/index.md
index 0200e9c4e7d..fb100986df9 100644
--- a/doc/user/project/web_ide/index.md
+++ b/doc/user/project/web_ide/index.md
@@ -10,6 +10,10 @@ The Web Integrated Development Environment (IDE) editor streamlines the process
to contribute changes to your projects, by providing an advanced editor with
commit staging.
+NOTE:
+The Web IDE is being updated to use VS Code. For details,
+see [Web IDE Beta](../web_ide_beta/index.md).
+
## Open the Web IDE
Use the <kbd>.</kbd> [keyboard shortcut](../../shortcuts.md) to open the Web IDE.
@@ -80,7 +84,7 @@ If you are missing Syntax Highlighting support for any language, we prepared a s
> - Full Solarized Dark Theme [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/219228) in GitLab 13.1.
> - Full [Solarized Light](https://gitlab.com/gitlab-org/gitlab/-/issues/221035) and [Monokai](https://gitlab.com/gitlab-org/gitlab/-/issues/221034) Themes introduced in GitLab 13.6.
-All the themes GitLab supports for syntax highlighting are applied to the Web IDE's entire screen.
+All the themes GitLab supports for syntax highlighting are applied to the entire Web IDE screen.
You can pick a theme from your [profile preferences](../../profile/preferences.md).
| Solarized Dark Theme | Dark Theme |
@@ -459,12 +463,3 @@ The Web IDE has a few limitations:
- If the terminal displays **Connection Failure**, then the terminal could not
connect to the runner. Try to stop and restart the terminal. If the
problem persists, double check your runner configuration.
-
-## VSCode Reimplementation
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95169) in GitLab 15.4 [with a flag](../../../administration/feature_flags.md) named `vscode_web_ide`. Disabled by default.
-
-As announced in [this blog post](https://about.gitlab.com/blog/2022/05/23/the-future-of-the-gitlab-web-ide/),
-the current implementation of the Web IDE will be replaced with a [VSCode inspired implementation](https://gitlab.com/groups/gitlab-org/-/epics/7683).
-
-This effort is currently under development. Follow [this epic](https://gitlab.com/groups/gitlab-org/-/epics/7683) for updates and more information.
diff --git a/doc/user/project/web_ide_beta/img/fuzzy_finder_v15_7.png b/doc/user/project/web_ide_beta/img/fuzzy_finder_v15_7.png
new file mode 100644
index 00000000000..66ebae15e98
--- /dev/null
+++ b/doc/user/project/web_ide_beta/img/fuzzy_finder_v15_7.png
Binary files differ
diff --git a/doc/user/project/web_ide_beta/index.md b/doc/user/project/web_ide_beta/index.md
new file mode 100644
index 00000000000..ef07cca465d
--- /dev/null
+++ b/doc/user/project/web_ide_beta/index.md
@@ -0,0 +1,103 @@
+---
+stage: Create
+group: Editor
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Web IDE Beta **(FREE)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95169) in GitLab 15.4 [with a flag](../../../administration/feature_flags.md) named `vscode_web_ide`. Disabled by default.
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `vscode_web_ide`. On GitLab.com, this feature is available.
+
+As announced in [this blog post](https://about.gitlab.com/blog/2022/05/23/the-future-of-the-gitlab-web-ide/),
+the current implementation of the Web IDE is being replaced with an
+implementation inspired by Visual Studio Code.
+
+This effort is currently under development. For updates,
+see [this epic](https://gitlab.com/groups/gitlab-org/-/epics/7683).
+
+## Enable the Web IDE Beta
+
+To use the Web IDE Beta on a self-managed GitLab instance,
+ensure that the `vscode_web_ide` feature flag
+[is enabled](../../../administration/feature_flags.md).
+
+On GitLab.com, this feature is available by default. However, you can
+[stop using it if you choose](#stop-using-the-web-ide-beta).
+
+## Use the Web IDE Beta
+
+To open the Web IDE Beta from anywhere in the UI:
+
+- Use the <kbd>.</kbd> [keyboard shortcut](../../shortcuts.md).
+
+You can also open the Web IDE Beta when viewing a file, the repository file list,
+and from merge requests.
+
+### Use when viewing a file or the repository file list
+
+To open the Web IDE Beta from a file or the repository file list:
+
+- On the top right of the page, select **Open in Web IDE**.
+
+If **Open in Web IDE** is not visible:
+
+1. Next to **Edit** or **Gitpod**, select the down arrow (**{chevron-lg-down}**).
+1. From the list, select **Open in Web IDE**.
+1. Select **Open in Web IDE**.
+
+### Use when viewing a merge request
+
+To open the Web IDE Beta from a merge request:
+
+1. Go to your merge request.
+1. In the upper right corner, select **Code > Open in Web IDE**.
+
+## Open a file in the Web IDE Beta
+
+To open any file by its name:
+
+1. Type **Command** + **`P`** (<kbd>⌘</kbd> + <kbd>P</kbd>).
+1. Type the name of your file.
+
+![fuzzy_finder_v15_7](img/fuzzy_finder_v15_7.png)
+
+## Search across files
+
+You can use VS Code to quickly search all files in the currently opened folder.
+
+To enter your search term:
+
+1. Type **Shift** + **Command** + **`F`** (<kbd>⇧</kbd> + <kbd>⌘</kbd> + <kbd>F</kbd>).
+1. Enter your search term.
+
+In the Web IDE Beta, only partial results from opened files are displayed.
+Full file search is planned for a later date.
+
+## View list of changed files
+
+To view the list of files you changed in the Web IDE Beta:
+
+- On the VS Code Activity Bar, on the left, select the Source Control icon:
+
+Your `CHANGES`, `STAGED CHANGES` and `MERGE CHANGES` are displayed.
+
+For details, see [the VS Code documentation](https://code.visualstudio.com/docs/sourcecontrol/overview#_commit).
+
+## Known issues
+
+The [Web Terminal](../web_ide/index.md#interactive-web-terminals-for-the-web-ide)
+and [Live Preview](../web_ide/index.md#live-preview) are not available in the Web IDE Beta.
+
+These features may become available at a later date.
+
+### Stop using the Web IDE Beta
+
+If you do not want to use the Web IDE Beta, you can change your personal preferences.
+
+1. On the top bar, in the top right corner, select your avatar.
+1. Select **Preferences**.
+1. In the **Web IDE** section, select the **Opt out of the Web IDE Beta** checkbox.
+1. Select **Save changes**.
diff --git a/doc/user/project/wiki/group.md b/doc/user/project/wiki/group.md
index 0e9b76e943d..53aaa41319b 100644
--- a/doc/user/project/wiki/group.md
+++ b/doc/user/project/wiki/group.md
@@ -16,8 +16,6 @@ Group wikis are similar to [project wikis](index.md), with a few limitations:
- [Git LFS](../../../topics/git/lfs/index.md) is not supported.
- Group wikis are not included in [global search](../../search/advanced_search.md).
- Changes to group wikis don't show up in the [group's activity feed](../../group/manage.md#group-activity-analytics).
-- Group wikis are enabled by default for GitLab Premium and higher tiers.
- You [can't turn them off from the GitLab user interface](https://gitlab.com/gitlab-org/gitlab/-/issues/208413).
For updates, follow [the epic that tracks feature parity with project wikis](https://gitlab.com/groups/gitlab-org/-/epics/2782).
diff --git a/doc/user/project/wiki/index.md b/doc/user/project/wiki/index.md
index eedc44be3f9..04a6f9bee80 100644
--- a/doc/user/project/wiki/index.md
+++ b/doc/user/project/wiki/index.md
@@ -14,7 +14,7 @@ to keep it in the same project as your code, you can use the wiki GitLab provide
in each GitLab project. Every wiki is a separate Git repository, so you can create
wiki pages in the web interface, or [locally using Git](#create-or-edit-wiki-pages-locally).
-GitLab wikis support Markdown, RDoc, AsciiDoc, and Org for content.
+GitLab wikis support Markdown, Rdoc, AsciiDoc, and Org for content.
Wiki pages written in Markdown support all [Markdown features](../../markdown.md),
and also provide some [wiki-specific behavior](../../markdown.md#wiki-specific-markdown)
for links.
@@ -338,11 +338,12 @@ GitLab provides a WYSIWYG editing experience for GitLab Flavored Markdown in wik
Support includes:
-- Text formatting options, including bold, italics, block quotes, headings, and inline code.
-- List formatting for unordered, numbered, and checklists.
-- Creating and editing the structure of tables.
+- Formatting text, including using bold, italics, block quotes, headings, and inline code.
+- Formatting ordered lists, unordered lists, and checklists.
+- Creating and editing table structure.
- Inserting and formatting code blocks with syntax highlighting.
-- Live preview of Mermaid, PlantUML, and Kroki diagrams ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86701) in GitLab 15.2).
+- Previewing Mermaid, PlantUML, and Kroki diagrams ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86701) in GitLab 15.2).
+- Creating and editing HTML comments ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104084) in GitLab 15.7).
### Use the Content Editor
diff --git a/doc/user/project/working_with_projects.md b/doc/user/project/working_with_projects.md
index 067a0303277..77a7c48658e 100644
--- a/doc/user/project/working_with_projects.md
+++ b/doc/user/project/working_with_projects.md
@@ -347,10 +347,27 @@ You can sort projects by:
You can also choose to hide or show archived projects.
+## Change the visibility of individual features in a project
+
+You can change the visibility of individual features in a project.
+
+Prerequisite:
+
+- You must have the Owner role for the project.
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > General**.
+1. Expand **Visibility, project features, permissions**.
+1. Use the toggle by each feature you want to turn on or off, or change access for.
+1. Select **Save changes**.
+
## Leave a project
-If you leave a project, you are no longer a project
-member and cannot contribute.
+When you leave a project:
+
+- You are no longer a project member and cannot contribute.
+- All the issues and merge requests that were assigned
+ to you are unassigned.
To leave a project:
diff --git a/doc/user/public_access.md b/doc/user/public_access.md
index bdc711f2098..acfc28a6f65 100644
--- a/doc/user/public_access.md
+++ b/doc/user/public_access.md
@@ -7,16 +7,17 @@ type: reference
# Project and group visibility **(FREE)**
-GitLab allows users with the Owner role to set a project's or group's visibility as:
+If you have the Owner role, you can set a project's or group's visibility as:
- **Public**
-- **Internal**
+- **[Internal](#internal-projects-and-groups)**
- **Private**
-These visibility levels affect who can see the project in the public access directory (`/public`
-for your GitLab instance). For example, <https://gitlab.com/public>.
-You can control the visibility of individual features with
-[project feature settings](permissions.md#project-features).
+These visibility levels affect who can see the project in the public access directory
+(for example, <https://gitlab.com/public>).
+
+For more granular control, you can determine
+[which features in a project are visible](project/working_with_projects.md#change-the-visibility-of-individual-features-in-a-project).
The visibility setting of a project must be at least as restrictive
as the visibility of its parent group.
@@ -38,16 +39,16 @@ By default, `/public` is visible to unauthenticated users. However, if the
[**Public** visibility level](admin_area/settings/visibility_and_access_controls.md#restrict-visibility-levels)
is restricted, `/public` is visible only to signed-in users.
-## Internal projects and groups
+## Internal projects and groups **(FREE SELF)**
Internal projects can be cloned by any signed-in user except
-[external users](permissions.md#external-users).
+[external users](admin_area/external_users.md).
They are also listed in the public access directory (`/public`), but only for signed-in users.
Internal groups can have internal or private subgroups.
-Any signed-in users except [external users](permissions.md#external-users) have the
+Any signed-in users except [external users](admin_area/external_users.md) have the
Guest role on the repository.
NOTE:
@@ -66,6 +67,8 @@ Private groups can only have private subgroups.
## Change project visibility
+You can change the visibility of a project.
+
Prerequisite:
- You must have the Owner role for a project.
@@ -78,6 +81,8 @@ Prerequisite:
## Change group visibility
+You can change the visibility of all projects in a group.
+
Prerequisites:
- You must have the Owner role for a group.
diff --git a/doc/user/read_only_namespaces.md b/doc/user/read_only_namespaces.md
new file mode 100644
index 00000000000..b11aac9b00c
--- /dev/null
+++ b/doc/user/read_only_namespaces.md
@@ -0,0 +1,48 @@
+---
+stage: Growth
+group: Acquisition
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+noindex: true
+---
+
+# Read-only namespaces **(FREE SAAS)**
+
+In GitLab SaaS, a top-level namespace is placed in a read-only state when it either:
+
+- Exceeds the [free user limit](free_user_limit.md) when the namespace visibility is private.
+- Exceeds the [storage usage quota](usage_quotas.md), regardless of namespace visibility.
+
+While a namespace is in a read-only state, a banner appears at the
+top of the page.
+
+Your ability to write new data to read-only namespaces is restricted. For more
+information, see [Restricted actions](#restricted-actions).
+
+## Remove the read-only state
+
+To restore a namespace to its normal state, you can:
+
+- For exceeded free user limits:
+ - [Reduce the number of members](free_user_limit.md#manage-members-in-your-namespace) in your namespace.
+ - [Start a free trial](https://gitlab.com/-/trial_registrations/new), which includes an unlimited number of members.
+ - [Purchase a paid tier](https://about.gitlab.com/pricing/).
+- For exceeded storage quota:
+ - [Purchase more storage for the namespace](../subscriptions/gitlab_com/index.md#purchase-more-storage-and-transfer).
+ - [Manage your storage usage](usage_quotas.md#manage-your-storage-usage).
+
+## Restricted actions
+
+| Feature | Action restricted |
+|---------|-------------------|
+| Container Registry | Create, edit, and delete cleanup policies <br> Push an image to the container registry |
+| Merge Requests | Create and update an MR |
+| Package Registry | Publish a package |
+| Repositories | Add tags <br> Create new branches <br> Create and update commit status <br> Push and force push to non-protected branches <br> Push and force push to protected branches <br> Upload files <br> Create merge requests |
+| CI/CD | Create, edit, admin, and run pipelines <br> Create, edit, admin, and run builds <br> Create and edit admin environments <br> Create and edit admin deployments <br> Create and edit admin clusters <br> Create and edit admin releases |
+| Namespaces | **For exceeded free user limits:** Invite new users |
+
+## Related topics
+
+- [Frequently Asked Questions - GitLab SaaS Free Tier](https://about.gitlab.com/pricing/faq-efficient-free-tier/)
+- [Free user limit](free_user_limit.md)
+- [Storage usage quotas](usage_quotas.md)
diff --git a/doc/user/report_abuse.md b/doc/user/report_abuse.md
index fab01973104..fde839c3ba4 100644
--- a/doc/user/report_abuse.md
+++ b/doc/user/report_abuse.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Anti-Abuse
+group: Anti-Abuse
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
@@ -27,7 +27,7 @@ You can report a user through their:
To report abuse from a user's profile page:
1. Anywhere in GitLab, select the name of the user.
-1. In the top right corner of the user's profile, select **Report abuse** (**{information-o}**).
+1. In the top right corner of the user's profile, select **Report abuse to administrator** (**{information-o}**).
1. Complete an abuse report.
1. Select **Send report**.
@@ -36,7 +36,7 @@ To report abuse from a user's profile page:
To report abuse from a user's comment:
1. In the comment, in the top right corner, select **More actions** (**{ellipsis_v}**).
-1. Select **Report abuse to admin**.
+1. Select **Report abuse to administrator**.
1. Complete an abuse report.
1. Select **Send report**.
@@ -47,14 +47,14 @@ A URL to the reported user's comment is pre-filled in the abuse report's
## Report abuse from an issue
1. On the issue, in the top right corner, select the vertical ellipsis (**{ellipsis_v}**).
-1. Select **Report abuse**.
+1. Select **Report abuse to administrator**.
1. Submit an abuse report.
1. Select **Send report**.
## Report abuse from a merge request
1. On the merge request, in the top right corner, select the vertical ellipsis (**{ellipsis_v}**).
-1. Select **Report abuse**.
+1. Select **Report abuse to administrator**.
1. Submit an abuse report.
1. Select **Send report**.
diff --git a/doc/user/reserved_names.md b/doc/user/reserved_names.md
index 1ab22fb846d..3eaf3178f3a 100644
--- a/doc/user/reserved_names.md
+++ b/doc/user/reserved_names.md
@@ -19,10 +19,13 @@ under the `TOP_LEVEL_ROUTES`, `PROJECT_WILDCARD_ROUTES` and `GROUP_ROUTES` lists
## Limitations on project and group names
-- Special characters are not permitted at the start or end of project or group names. They are permitted in any other location of the name.
-- Project or group names cannot end in `.git` or `.atom`.
+- Project or group names must start with a letter, digit, emoji, or "_".
- Project or group names can only contain letters, digits, emojis, "_", ".", "+", dashes, or spaces.
-- Paths can only contain letters, digits, "_", "-", and "."
+- Project or group slugs must start with a letter or digit.
+- Project or group slugs can only contain letters, digits, '_', '.', '+', or dashes.
+- Project or group slugs must not contain consecutive special characters.
+- Project or group slugs cannot end with a special character.
+- Project or group slugs cannot end in `.git` or `.atom`.
## Reserved project names
diff --git a/doc/user/search/advanced_search.md b/doc/user/search/advanced_search.md
index 925fc7e6036..ed1d3b1d290 100644
--- a/doc/user/search/advanced_search.md
+++ b/doc/user/search/advanced_search.md
@@ -29,11 +29,27 @@ You can use Advanced Search in:
- Commits
- Project wikis (not [group wikis](../project/wiki/group.md))
-## Configure Advanced Search
+For Advanced Search:
+
+- You can only search files smaller than 1 MB.
+ For self-managed GitLab instances, an administrator can
+ [change this limit](../../integration/advanced_search/elasticsearch.md#advanced-search-configuration).
+- You can't use any of the following characters in the search query:
+
+ ```plaintext
+ . , : ; / ` ' = ? $ & ^ | ~ < > ( ) { } [ ] @
+ ```
+
+- Only the default branch of a project is indexed for code search.
+ In a non-default branch, basic search is used.
+- Search results show only the first match in a file,
+ but there might be more results in that file.
+
+## Enable Advanced Search
- On GitLab.com, Advanced Search is enabled for groups with paid subscriptions.
- For self-managed GitLab instances, an administrator must
- [configure Advanced Search](../../integration/advanced_search/elasticsearch.md).
+ [enable Advanced Search](../../integration/advanced_search/elasticsearch.md#enable-advanced-search).
## Syntax
diff --git a/doc/user/search/img/basic_search_results_v15_1.png b/doc/user/search/img/basic_search_results_v15_1.png
deleted file mode 100644
index 0de0b976d7d..00000000000
--- a/doc/user/search/img/basic_search_results_v15_1.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/search/img/basic_search_v15_1.png b/doc/user/search/img/basic_search_v15_1.png
deleted file mode 100644
index 069d62ca80c..00000000000
--- a/doc/user/search/img/basic_search_v15_1.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/search/img/search_navbar_v15_7.png b/doc/user/search/img/search_navbar_v15_7.png
new file mode 100644
index 00000000000..9175347b36f
--- /dev/null
+++ b/doc/user/search/img/search_navbar_v15_7.png
Binary files differ
diff --git a/doc/user/search/img/search_scope_v15_7.png b/doc/user/search/img/search_scope_v15_7.png
new file mode 100644
index 00000000000..6395b5f1cda
--- /dev/null
+++ b/doc/user/search/img/search_scope_v15_7.png
Binary files differ
diff --git a/doc/user/search/index.md b/doc/user/search/index.md
index 9bff2a91ec8..9536f7fe40a 100644
--- a/doc/user/search/index.md
+++ b/doc/user/search/index.md
@@ -53,12 +53,12 @@ Global search only flags with an error any search that includes more than:
To start a search, type your search query in the search bar on the top-right of the screen.
You must type at least two characters.
-![basic search](img/basic_search_v15_1.png)
+![search navbar](img/search_navbar_v15_7.png)
After the results are displayed, you can modify the search, select a different type of data to
search, or choose a specific group or project.
-![basic_search_results](img/basic_search_results_v15_1.png)
+![search scope](img/search_scope_v15_7.png)
## Search in code
@@ -150,6 +150,7 @@ To delete filter tokens one at a time, the <kbd>⌥</kbd> (Mac) / <kbd>Control</
In the search bar, you can view autocomplete suggestions for:
- Projects and groups
+- Users
- Various help pages (try and type **API help**)
- Project feature pages (try and type **milestones**)
- Various settings pages (try and type **user settings**)
diff --git a/doc/user/shortcuts.md b/doc/user/shortcuts.md
index f9e61ad78ad..64f9b53f891 100644
--- a/doc/user/shortcuts.md
+++ b/doc/user/shortcuts.md
@@ -37,6 +37,7 @@ These shortcuts are available in most areas of GitLab:
| <kbd>f</kbd> | Put cursor in the filter bar. |
| <kbd>Shift</kbd> + <kbd>i</kbd> | Go to your Issues page. |
| <kbd>Shift</kbd> + <kbd>m</kbd> | Go to your [Merge requests](project/merge_requests/index.md) page. |
+| <kbd>Shift</kbd> + <kbd>r</kbd> | Go to your Review requests page. |
| <kbd>Shift</kbd> + <kbd>t</kbd> | Go to your To-Do List page. |
| <kbd>p</kbd>, then <kbd>b</kbd> | Show or hide the Performance Bar. |
| <kbd>Escape</kbd> | Hide tooltips or popovers. |
diff --git a/doc/user/snippets.md b/doc/user/snippets.md
index 6a8184e9ca1..ee84f8a4169 100644
--- a/doc/user/snippets.md
+++ b/doc/user/snippets.md
@@ -267,3 +267,7 @@ creating a new snippet, use this workaround:
1. Enter any string into the text area for the second file.
1. Scroll back to the first filename, and select **Delete file**.
1. Create the rest of your file, and select **Create snippet** when done.
+
+## Related topics
+
+- [Configure snippet settings](../administration/snippets/index.md) on a self-managed GitLab instance
diff --git a/doc/user/ssh.md b/doc/user/ssh.md
index 85332446334..1d82d301e9a 100644
--- a/doc/user/ssh.md
+++ b/doc/user/ssh.md
@@ -270,6 +270,7 @@ You can use [1Password](https://1password.com/) and the [1Password browser exten
1. You can then select **Create SSH Key** or select an existing SSH key to fill in the public key.
1. In the **Title** box, type a description, like `Work Laptop` or
`Home Workstation`.
+1. Optional. Select the **Usage type** of the key. It can be used either for `Authentication` or `Signing` or both. `Authentication & Signing` is the default value.
1. Optional. Update **Expiration date** to modify the default expiration date.
1. Select **Add key**.
@@ -314,6 +315,7 @@ To use SSH with GitLab, copy your public key to your GitLab account:
`ssh-ed25519`, `sk-ecdsa-sha2-nistp256@openssh.com`, or `sk-ssh-ed25519@openssh.com`, and may end with a comment.
1. In the **Title** box, type a description, like `Work Laptop` or
`Home Workstation`.
+1. Optional. Select the **Usage type** of the key. It can be used either for `Authentication` or `Signing` or both. `Authentication & Signing` is the default value.
1. Optional. Update **Expiration date** to modify the default expiration date.
In:
- GitLab 13.12 and earlier, the expiration date is informational only. It doesn't prevent
diff --git a/doc/user/tasks.md b/doc/user/tasks.md
index 5fed887ca74..ffe7777fac0 100644
--- a/doc/user/tasks.md
+++ b/doc/user/tasks.md
@@ -49,7 +49,7 @@ the task opens in a full-page view.
Prerequisites:
-- You must have at least the Guest role for the project, or the project must be public.
+- You must have at least the Reporter role for the project, or the project must be public.
To create a task:
@@ -207,10 +207,9 @@ To set a start date:
## Add a task to a milestone
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367463) in GitLab 15.5 [with a flag](../administration/feature_flags.md) named `work_items_mvc_2`. Disabled by default.
-
-FLAG:
-On self-managed GitLab, by default this feature is not available. To make it available per group, ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `work_items_mvc_2`. On GitLab.com, this feature is not available. The feature is not ready for production use.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367463) in GitLab 15.5 [with a flag](../administration/feature_flags.md) named `work_items_mvc_2`. Disabled by default.
+> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/367463) to feature flag named `work_items_mvc` in GitLab 15.7. Disabled by default.
+> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/367463) in GitLab 15.7.
You can add a task to a [milestone](project/milestones/index.md).
You can see the milestone title when you view a task.
@@ -249,10 +248,13 @@ To set issue weight of a task:
## Add a task to an iteration **(PREMIUM)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367456) in GitLab 15.5 [with a flag](../administration/feature_flags.md) named `work_items_mvc_2`. Disabled by default.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367456) in GitLab 15.5 [with a flag](../administration/feature_flags.md) named `work_items_mvc_2`. Disabled by default.
+> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/367456) to feature flag named `work_items_mvc` in GitLab 15.7. Disabled by default.
+> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/367456) in GitLab 15.7.
FLAG:
-On self-managed GitLab, by default this feature is not available. To make it available per group, ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `work_items_mvc_2`. On GitLab.com, this feature is not available. The feature is not ready for production use.
+On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to [disable the feature flag](../administration/feature_flags.md) named `work_items_mvc`.
+On GitLab.com, this feature is available.
You can add a task to an [iteration](group/iterations/index.md).
You can see the iteration title and period only when you view a task.
diff --git a/doc/user/todos.md b/doc/user/todos.md
index d7deda33a65..4102d31cfc4 100644
--- a/doc/user/todos.md
+++ b/doc/user/todos.md
@@ -87,6 +87,7 @@ You can manually add an item to your To-Do List.
- [Merge request](project/merge_requests/index.md)
- [Epic](group/epics/index.md)
- [Design](project/issues/design_management.md)
+ - [Incident](../operations/incident_management/incidents.md)
1. On the right sidebar, at the top, select **Add a to do**.
@@ -135,7 +136,7 @@ You can manually mark a to-do item as done.
There are two ways to do this:
-- In the To-Do List, to the right of the to-do item, select **Done**.
+- In the To-Do List, to the right of the to-do item, select **Mark as done** (**{check}**).
- In the sidebar of an issue, merge request, or epic, select **Mark as done**.
![Mark as done from the sidebar](img/todos_mark_done_sidebar_v14_1.png)
diff --git a/doc/user/upgrade_email_bypass.md b/doc/user/upgrade_email_bypass.md
index ef58d8aec66..afbdcbcdf09 100644
--- a/doc/user/upgrade_email_bypass.md
+++ b/doc/user/upgrade_email_bypass.md
@@ -76,7 +76,7 @@ Your primary email address is not confirmed.
You can assure your users that they have not been [Blocked](admin_area/moderate_users.md#block-and-unblock-users) by an administrator.
When affected users see this message, they must confirm their email address before they can commit code.
-## What do I need to know as an administrator of a GitLab self-managed Instance?
+## What do you need to know as an administrator of a GitLab self-managed Instance?
You have the following options to help your users:
@@ -85,7 +85,7 @@ You have the following options to help your users:
As an administrator, you may also confirm a user in the [Admin Area](admin_area/index.md#administering-users).
-## What do I do if I am an administrator and I am locked out?
+## What do you do if you are an administrator and you're locked out?
If you are an administrator and cannot otherwise verify your email address, sign in to your GitLab
instance with a [Rails console session](../administration/operations/rails_console.md#starting-a-rails-console-session).
@@ -97,7 +97,7 @@ admin.confirmed_at = Time.zone.now
admin.save!
```
-## How do I force-confirm all users on my self-managed instance?
+## How do you force-confirm all users on your self-managed instance?
If you are an administrator and would like to force-confirm all users on your system, sign in to your GitLab
instance with a [Rails console session](../administration/operations/rails_console.md#starting-a-rails-console-session).
diff --git a/doc/user/usage_quotas.md b/doc/user/usage_quotas.md
index 7df5ade215d..0e5703caffc 100644
--- a/doc/user/usage_quotas.md
+++ b/doc/user/usage_quotas.md
@@ -126,8 +126,7 @@ Storage types that add to the total namespace storage are:
- Wiki
- Snippets
-If your total namespace storage exceeds the available namespace storage quota, all projects under the namespace are locked.
-A locked project cannot push to the repository, run pipelines and jobs, or build and push packages.
+If your total namespace storage exceeds the available namespace storage quota, all projects under the namespace become read-only. Your ability to write new data is restricted until the read-only state is removed. For more information, see [Restricted actions](../user/read_only_namespaces.md#restricted-actions).
To prevent exceeding the namespace storage quota, you can:
diff --git a/doc/user/workspace/index.md b/doc/user/workspace/index.md
index 5bcd96cd4a5..4a8b083e3a2 100644
--- a/doc/user/workspace/index.md
+++ b/doc/user/workspace/index.md
@@ -26,12 +26,12 @@ everything you do as a GitLab administrator, including:
Our goal is to reach feature parity between SaaS and self-managed installations, with all
[Admin Area settings](/ee/user/admin_area/settings/index.md) moving to either:
-- Groups. Available in the Workspace, top-level group namespaces, and subgroups.
+- Groups. Available in the Workspace, top-level groups, and subgroups.
- Hardware Controls. For functionality that does not apply to groups, Hardware Controls are only
applicable to self-managed installations. There is one Hardware Controls section per installation.
-To learn about the current state of workspace development,
-see [epic 4257](https://gitlab.com/groups/gitlab-org/-/epics/4257).
+For more information about the state of workspace development,
+see [epic 9265](https://gitlab.com/groups/gitlab-org/-/epics/9265).
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For a video introduction to the new hierarchy concept for groups and projects for epics, see
diff --git a/glfm_specification/input/gitlab_flavored_markdown/glfm_example_metadata.yml b/glfm_specification/input/gitlab_flavored_markdown/glfm_example_metadata.yml
index 7fc1f9d9be0..84ce1d7a368 100644
--- a/glfm_specification/input/gitlab_flavored_markdown/glfm_example_metadata.yml
+++ b/glfm_specification/input/gitlab_flavored_markdown/glfm_example_metadata.yml
@@ -12,3 +12,20 @@
08_03_00__gitlab_internal_extension_markdown__markdown_preview_api_request_overrides__006:
ee: true
api_request_override_path: /groups/glfm_group/-/wikis/new_page/preview_markdown
+08_04_01__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_group__001:
+ api_request_override_path: /groups/glfm_group/preview_markdown
+08_04_02__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_project__001:
+ api_request_override_path: /glfm_group/glfm_project/preview_markdown
+08_04_03__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_project_wiki__001:
+ api_request_override_path: /glfm_group/glfm_project/-/wikis/new_page/preview_markdown
+08_04_04__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_group__001:
+ api_request_override_path: /groups/glfm_group/preview_markdown
+08_04_05__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_project__001:
+ api_request_override_path: /glfm_group/glfm_project/preview_markdown
+08_04_06__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_project_wiki__001:
+ api_request_override_path: /glfm_group/glfm_project/-/wikis/new_page/preview_markdown
+08_04_07__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_group_wiki__001:
+ ee: true
+ api_request_override_path: /groups/glfm_group/-/wikis/new_page/preview_markdown
+08_04_46__gitlab_internal_extension_markdown__migrated_golden_master_examples__reference_for_project_wiki__001:
+ api_request_override_path: /glfm_group/glfm_project/-/wikis/new_page/preview_markdown
diff --git a/glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml b/glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml
index a0ce7f44a3c..501d06692ea 100644
--- a/glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml
+++ b/glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml
@@ -60,3 +60,51 @@
skip_running_conformance_wysiwyg_tests: Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
skip_running_snapshot_wysiwyg_html_tests: Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
skip_running_snapshot_prosemirror_json_tests: Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+08_04_06__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_project_wiki__001:
+ skip_update_example_snapshot_html_wysiwyg: The current frontend example doesn't include the path, need to look into why not
+ skip_update_example_snapshot_prosemirror_json: The current frontend example doesn't include the path, need to look into why not
+ skip_running_conformance_wysiwyg_tests: The current frontend example doesn't include the path, need to look into why not
+ skip_running_snapshot_wysiwyg_html_tests: The current frontend example doesn't include the path, need to look into why not
+ skip_running_snapshot_prosemirror_json_tests: The current frontend example doesn't include the path, need to look into why not
+08_04_44__gitlab_internal_extension_markdown__migrated_golden_master_examples__ordered_task_list__001:
+ skip_update_example_snapshot_html_wysiwyg: Example currently fails. See https://gitlab.com/gitlab-org/gitlab/-/issues/383866
+ skip_update_example_snapshot_prosemirror_json: Example currently fails. See https://gitlab.com/gitlab-org/gitlab/-/issues/383866
+ skip_running_conformance_wysiwyg_tests: Example currently fails. See https://gitlab.com/gitlab-org/gitlab/-/issues/383866
+ skip_running_snapshot_wysiwyg_html_tests: Example currently fails. See https://gitlab.com/gitlab-org/gitlab/-/issues/383866
+ skip_running_snapshot_prosemirror_json_tests: Example currently fails. See https://gitlab.com/gitlab-org/gitlab/-/issues/383866
+08_04_50__gitlab_internal_extension_markdown__migrated_golden_master_examples__task_list__001:
+ skip_update_example_snapshot_html_wysiwyg: Example currently fails. See https://gitlab.com/gitlab-org/gitlab/-/issues/383866
+ skip_update_example_snapshot_prosemirror_json: Example currently fails. See https://gitlab.com/gitlab-org/gitlab/-/issues/383866
+ skip_running_conformance_wysiwyg_tests: Example currently fails. See https://gitlab.com/gitlab-org/gitlab/-/issues/383866
+ skip_running_snapshot_wysiwyg_html_tests: Example currently fails. See https://gitlab.com/gitlab-org/gitlab/-/issues/383866
+ skip_running_snapshot_prosemirror_json_tests: Example currently fails. See https://gitlab.com/gitlab-org/gitlab/-/issues/383866
+08_05_00__gitlab_internal_extension_markdown__image_attributes__001:
+ skip_update_example_snapshot_html_wysiwyg: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_update_example_snapshot_prosemirror_json: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_running_conformance_wysiwyg_tests: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_running_snapshot_wysiwyg_html_tests: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_running_snapshot_prosemirror_json_tests: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+08_05_00__gitlab_internal_extension_markdown__image_attributes__002:
+ skip_update_example_snapshot_html_wysiwyg: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_update_example_snapshot_prosemirror_json: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_running_conformance_wysiwyg_tests: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_running_snapshot_wysiwyg_html_tests: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_running_snapshot_prosemirror_json_tests: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+08_05_00__gitlab_internal_extension_markdown__image_attributes__003:
+ skip_update_example_snapshot_html_wysiwyg: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_update_example_snapshot_prosemirror_json: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_running_conformance_wysiwyg_tests: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_running_snapshot_wysiwyg_html_tests: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_running_snapshot_prosemirror_json_tests: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+08_05_00__gitlab_internal_extension_markdown__image_attributes__004:
+ skip_update_example_snapshot_html_wysiwyg: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_update_example_snapshot_prosemirror_json: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_running_conformance_wysiwyg_tests: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_running_snapshot_wysiwyg_html_tests: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_running_snapshot_prosemirror_json_tests: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+08_05_00__gitlab_internal_extension_markdown__image_attributes__005:
+ skip_update_example_snapshot_html_wysiwyg: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_update_example_snapshot_prosemirror_json: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_running_conformance_wysiwyg_tests: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_running_snapshot_wysiwyg_html_tests: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
+ skip_running_snapshot_prosemirror_json_tests: WYSYWIG and prosemirror examples not generated correctly. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106733#note_1206201340
diff --git a/glfm_specification/input/gitlab_flavored_markdown/glfm_internal_extensions.md b/glfm_specification/input/gitlab_flavored_markdown/glfm_internal_extensions.md
index d2b94806826..63518a93f71 100644
--- a/glfm_specification/input/gitlab_flavored_markdown/glfm_internal_extensions.md
+++ b/glfm_specification/input/gitlab_flavored_markdown/glfm_internal_extensions.md
@@ -110,4 +110,640 @@ also requires an EE license enabling the `group_wikis` feature:
.
<p><a href="group-wikis-test-file">group-wikis-test-file</a></p>
````````````````````````````````
+
+## Migrated golden master examples
+
+### attachment_image_for_group
+
+```````````````````````````````` example gitlab
+![test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### attachment_image_for_project
+
+```````````````````````````````` example gitlab
+![test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### attachment_image_for_project_wiki
+
+```````````````````````````````` example gitlab
+![test-file](test-file.png)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### attachment_link_for_group
+
+```````````````````````````````` example gitlab
+[test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### attachment_link_for_project
+
+```````````````````````````````` example gitlab
+[test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### attachment_link_for_project_wiki
+
+```````````````````````````````` example gitlab
+[test-file](test-file.zip)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### attachment_link_for_group_wiki
+
+```````````````````````````````` example gitlab
+[test-file](test-file.zip)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### audio
+
+```````````````````````````````` example gitlab
+![Sample Audio](https://gitlab.com/gitlab.mp3)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### audio_and_video_in_lists
+
+```````````````````````````````` example gitlab
+* ![Sample Audio](https://gitlab.com/1.mp3)
+* ![Sample Video](https://gitlab.com/2.mp4)
+
+1. ![Sample Video](https://gitlab.com/1.mp4)
+2. ![Sample Audio](https://gitlab.com/2.mp3)
+
+* [x] ![Sample Audio](https://gitlab.com/1.mp3)
+* [x] ![Sample Audio](https://gitlab.com/2.mp3)
+* [x] ![Sample Video](https://gitlab.com/3.mp4)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### blockquote
+
+```````````````````````````````` example gitlab
+> This is a blockquote
+>
+> This is another one
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### bold
+
+```````````````````````````````` example gitlab
+**bold**
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### bullet_list_style_1
+
+```````````````````````````````` example gitlab
+* list item 1
+* list item 2
+ * embedded list item 3
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### bullet_list_style_2
+
+```````````````````````````````` example gitlab
+- list item 1
+- list item 2
+ * embedded list item 3
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### bullet_list_style_3
+
+```````````````````````````````` example gitlab
++ list item 1
++ list item 2
+ - embedded list item 3
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### code_block_javascript
+
+```````````````````````````````` example gitlab
+```javascript
+ console.log('hello world')
+```
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### code_block_plaintext
+
+```````````````````````````````` example gitlab
+```
+ plaintext
+```
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### code_block_unknown
+
+```````````````````````````````` example gitlab
+```foobar
+ custom_language = >> this <<
+```
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### color_chips
+
+```````````````````````````````` example gitlab
+- `#F00`
+- `#F00A`
+- `#FF0000`
+- `#FF0000AA`
+- `RGB(0,255,0)`
+- `RGB(0%,100%,0%)`
+- `RGBA(0,255,0,0.3)`
+- `HSL(540,70%,50%)`
+- `HSLA(540,70%,50%,0.3)`
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### description_list
+
+```````````````````````````````` example gitlab
+<dl>
+<dt>Frog</dt>
+<dd>Wet green thing</dd>
+<dt>Rabbit</dt>
+<dd>Warm fluffy thing</dd>
+<dt>Punt</dt>
+<dd>Kick a ball</dd>
+<dd>Take a bet</dd>
+<dt>Color</dt>
+<dt>Colour</dt>
+<dd>
+
+Any hue except _white_ or **black**
+
+</dd>
+</dl>
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### details
+
+```````````````````````````````` example gitlab
+<details>
+<summary>This is the visible summary of the collapsible section</summary>
+
+1. collapsed markdown
+2. more collapsed markdown
+
+</details>
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### diagram_kroki_nomnoml
+
+```````````````````````````````` example gitlab
+```nomnoml
+ #stroke: #a86128
+ [<frame>Decorator pattern|
+ [<abstract>Component||+ operation()]
+ [Client] depends --> [Component]
+ [Decorator|- next: Component]
+ [Decorator] decorates -- [ConcreteComponent]
+ [Component] <:- [Decorator]
+ [Component] <:- [ConcreteComponent]
+ ]
+```
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### diagram_plantuml
+
+```````````````````````````````` example gitlab
+```plantuml
+ Alice -> Bob: Authentication Request
+ Bob --> Alice: Authentication Response
+
+ Alice -> Bob: Another authentication Request
+ Alice <-- Bob: Another authentication Response
+```
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### diagram_plantuml_unicode
+
+```````````````````````````````` example gitlab
+```plantuml
+A -> B : Text with norwegian characters: æøå
+```
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### div
+
+```````````````````````````````` example gitlab
+<div>plain text</div>
+<div>
+
+just a plain ol' div, not much to _expect_!
+
+</div>
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### emoji
+
+```````````````````````````````` example gitlab
+:sparkles: :heart: :100:
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### emphasis
+
+```````````````````````````````` example gitlab
+_emphasized text_
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### figure
+
+```````````````````````````````` example gitlab
+<figure>
+
+![Elephant at sunset](elephant-sunset.jpg)
+
+<figcaption>An elephant at sunset</figcaption>
+</figure>
+<figure>
+
+![A crocodile wearing crocs](croc-crocs.jpg)
+
+<figcaption>
+
+A crocodile wearing _crocs_!
+
+</figcaption>
+</figure>
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### footnotes
+
+```````````````````````````````` example gitlab
+A footnote reference tag looks like this: [^1]
+
+This reference tag is a mix of letters and numbers. [^footnote]
+
+[^1]: This is the text inside a footnote.
+
+[^footnote]: This is another footnote.
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### frontmatter_json
+
+```````````````````````````````` example gitlab
+;;;
+{
+ "title": "Page title"
+}
+;;;
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### frontmatter_toml
+
+```````````````````````````````` example gitlab
++++
+title = "Page title"
++++
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### frontmatter_yaml
+
+```````````````````````````````` example gitlab
+---
+title: Page title
+---
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### hard_break
+
+```````````````````````````````` example gitlab
+This is a line after a\
+hard break
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### headings
+
+```````````````````````````````` example gitlab
+# Heading 1
+
+## Heading 2
+
+### Heading 3
+
+#### Heading 4
+
+##### Heading 5
+
+###### Heading 6
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### horizontal_rule
+
+```````````````````````````````` example gitlab
+---
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### html_marks
+
+```````````````````````````````` example gitlab
+* Content editor is ~~great~~<ins>amazing</ins>.
+* If the changes <abbr title="Looks good to merge">LGTM</abbr>, please <abbr title="Merge when pipeline succeeds">MWPS</abbr>.
+* The English song <q>Oh I do like to be beside the seaside</q> looks like this in Hebrew: <span dir="rtl">××”, ×× ×™ ×והב להיות ליד חוף ×”×™×</span>. In the computer's memory, this is stored as <bdo dir="ltr">××”, ×× ×™ ×והב להיות ליד חוף ×”×™×</bdo>.
+* <cite>The Scream</cite> by Edvard Munch. Painted in 1893.
+* <dfn>HTML</dfn> is the standard markup language for creating web pages.
+* Do not forget to buy <mark>milk</mark> today.
+* This is a paragraph and <small>smaller text goes here</small>.
+* The concert starts at <time datetime="20:00">20:00</time> and you'll be able to enjoy the band for at least <time datetime="PT2H30M">2h 30m</time>.
+* Press <kbd>Ctrl</kbd> + <kbd>C</kbd> to copy text (Windows).
+* WWF's goal is to: <q>Build a future where people live in harmony with nature.</q> We hope they succeed.
+* The error occurred was: <samp>Keyboard not found. Press F1 to continue.</samp>
+* The area of a triangle is: 1/2 x <var>b</var> x <var>h</var>, where <var>b</var> is the base, and <var>h</var> is the vertical height.
+* <ruby>æ¼¢<rt>ã„ㄢˋ</rt></ruby>
+* C<sub>7</sub>H<sub>16</sub> + O<sub>2</sub> → CO<sub>2</sub> + H<sub>2</sub>O
+* The **Pythagorean theorem** is often expressed as <var>a<sup>2</sup></var> + <var>b<sup>2</sup></var> = <var>c<sup>2</sup></var>
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### image
+
+```````````````````````````````` example gitlab
+![alt text](https://gitlab.com/logo.png)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### inline_code
+
+```````````````````````````````` example gitlab
+`code`
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### inline_diff
+
+```````````````````````````````` example gitlab
+* {-deleted-}
+* {+added+}
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### label
+
+```````````````````````````````` example gitlab
+~bug
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### link
+
+```````````````````````````````` example gitlab
+[GitLab](https://gitlab.com)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### math
+
+```````````````````````````````` example gitlab
+This math is inline $`a^2+b^2=c^2`$.
+
+This is on a separate line:
+
+```math
+a^2+b^2=c^2
+```
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### ordered_list
+
+```````````````````````````````` example gitlab
+1. list item 1
+2. list item 2
+3. list item 3
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### ordered_list_with_start_order
+
+```````````````````````````````` example gitlab
+134. list item 1
+135. list item 2
+136. list item 3
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### ordered_task_list
+
+```````````````````````````````` example gitlab
+1. [x] hello
+2. [x] world
+3. [ ] example
+ 1. [ ] of nested
+ 1. [x] task list
+ 2. [ ] items
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### ordered_task_list_with_order
+
+```````````````````````````````` example gitlab
+4893. [x] hello
+4894. [x] world
+4895. [ ] example
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### reference_for_project_wiki
+
+```````````````````````````````` example gitlab
+Hi @gfm_user - thank you for reporting this ~"UX bug" (#1) we hope to fix it in %1.1 as part of !1
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### strike
+
+```````````````````````````````` example gitlab
+~~del~~
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### table
+
+```````````````````````````````` example gitlab
+| header | header |
+|--------|--------|
+| `code` | cell with **bold** |
+| ~~strike~~ | cell with _italic_ |
+
+# content after table
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### table_of_contents
+
+```````````````````````````````` example gitlab
+[[_TOC_]]
+
+# Lorem
+
+Well, that's just like... your opinion.. man.
+
+## Ipsum
+
+### Dolar
+
+# Sit amit
+
+### I don't know
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### task_list
+
+```````````````````````````````` example gitlab
+* [x] hello
+* [x] world
+* [ ] example
+ * [ ] of nested
+ * [x] task list
+ * [ ] items
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### video
+
+```````````````````````````````` example gitlab
+![Sample Video](https://gitlab.com/gitlab.mp4)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### word_break
+
+```````````````````````````````` example gitlab
+Fernstraßen<wbr>bau<wbr>privat<wbr>finanzierungs<wbr>gesetz
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+## Image Attributes
+
+See
+[Change the image dimensions](https://docs.gitlab.com/ee/user/markdown.html#change-the-image-dimensions)
+in the GitLab Flavored Markdown documentation.
+
+The `width` and `height` attributes for an image can be specified directly after
+the image markdown.
+
+General syntax conforms to the
+[commonmark-hs attribute syntax](https://github.com/jgm/commonmark-hs/blob/master/commonmark-extensions/test/attributes.md)
+where it makes sense.
+
+```````````````````````````````` example gitlab
+![](https://gitlab.com/logo.png){width="100" height="100"}
+.
+<p><img src="https://gitlab.com/logo.png" width="100" height="100"></p>
+````````````````````````````````
+
+`%` and `px` units may also be specified.
+
+```````````````````````````````` example gitlab
+![](https://gitlab.com/logo.png){width="100%"}
+.
+<p><img src="https://gitlab.com/logo.png" width="100%"></p>
+````````````````````````````````
+
+```````````````````````````````` example gitlab
+![](https://gitlab.com/logo.png){height="100px"}
+.
+<p><img src="https://gitlab.com/logo.png" height="100px"></p>
+````````````````````````````````
+
+Whitespace is tolerated around the delimiters:
+
+```````````````````````````````` example gitlab
+![](https://gitlab.com/logo.png){ width="100" height="100" }
+.
+<p><img src="https://gitlab.com/logo.png" width="100" height="100"></p>
+````````````````````````````````
+
+Attributes must immediately follow the image markdown.
+
+```````````````````````````````` example gitlab
+![](https://gitlab.com/logo.png) {width="100" height="100"}
+.
+<p><img src="https://gitlab.com/logo.png"> {width="100" height="100"}</p>
+````````````````````````````````
+
<!-- END TESTS -->
diff --git a/glfm_specification/input/gitlab_flavored_markdown/glfm_official_specification.md b/glfm_specification/input/gitlab_flavored_markdown/glfm_official_specification.md
index e45ae62309d..bab74857cc3 100644
--- a/glfm_specification/input/gitlab_flavored_markdown/glfm_official_specification.md
+++ b/glfm_specification/input/gitlab_flavored_markdown/glfm_official_specification.md
@@ -2,6 +2,16 @@
TODO: Write a GitLab-specific version of the GitHub Flavored Markdown intro section.
+NOTE: The example numbering in this document does not start at "1", because this official specification
+only contains a subset of all the examples which are supported by GitLab Flavored Markdown. See
+[`snapshot_spec.html`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_example_snapshots/snapshot_spec.html)
+for a complete list of all examples, which are a superset of examples from:
+
+- CommonMark
+- GitHub Flavored Markdown
+- GitLab Flavored Markdown Official Specification (the same ones from this specifiation)
+- GitLab Flavored Markdown Internal Extensions.
+
<!-- BEGIN TESTS -->
# GitLab Official Specification Markdown
@@ -211,8 +221,14 @@ See
[table of contents](https://docs.gitlab.com/ee/user/markdown.html#table-of-contents)
in the GitLab Flavored Markdown documentation.
+NOTE: Because of this bug (https://gitlab.com/gitlab-org/gitlab/-/issues/359077),
+we cannot actually include the `TOC` tag with single brackets in backticks
+in this Markdown document, otherwise it would render a table of contents inline
+right here. So, it's been switched to `[` + `TOC` + `]` instead. This can be reverted
+once that bug is fixed.
+
A table of contents is an unordered list that links to subheadings in the document.
-Add either the `[[_TOC_]]` or `[TOC]` tag on its own line.
+Add either the `[[_TOC_]]` tag or the `[` + `TOC` + `]` tag on its own line.
```````````````````````````````` example gitlab
[TOC]
diff --git a/glfm_specification/output_example_snapshots/examples_index.yml b/glfm_specification/output_example_snapshots/examples_index.yml
index 45d10679a71..d08f16c7313 100644
--- a/glfm_specification/output_example_snapshots/examples_index.yml
+++ b/glfm_specification/output_example_snapshots/examples_index.yml
@@ -2084,3 +2084,174 @@
08_03_00__gitlab_internal_extension_markdown__markdown_preview_api_request_overrides__006:
spec_example_position: 697
source_specification: gitlab
+08_04_01__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_group__001:
+ spec_example_position: 698
+ source_specification: gitlab
+08_04_02__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_project__001:
+ spec_example_position: 699
+ source_specification: gitlab
+08_04_03__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_project_wiki__001:
+ spec_example_position: 700
+ source_specification: gitlab
+08_04_04__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_group__001:
+ spec_example_position: 701
+ source_specification: gitlab
+08_04_05__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_project__001:
+ spec_example_position: 702
+ source_specification: gitlab
+08_04_06__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_project_wiki__001:
+ spec_example_position: 703
+ source_specification: gitlab
+08_04_07__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_group_wiki__001:
+ spec_example_position: 704
+ source_specification: gitlab
+08_04_08__gitlab_internal_extension_markdown__migrated_golden_master_examples__audio__001:
+ spec_example_position: 705
+ source_specification: gitlab
+08_04_09__gitlab_internal_extension_markdown__migrated_golden_master_examples__audio_and_video_in_lists__001:
+ spec_example_position: 706
+ source_specification: gitlab
+08_04_10__gitlab_internal_extension_markdown__migrated_golden_master_examples__blockquote__001:
+ spec_example_position: 707
+ source_specification: gitlab
+08_04_11__gitlab_internal_extension_markdown__migrated_golden_master_examples__bold__001:
+ spec_example_position: 708
+ source_specification: gitlab
+08_04_12__gitlab_internal_extension_markdown__migrated_golden_master_examples__bullet_list_style_1__001:
+ spec_example_position: 709
+ source_specification: gitlab
+08_04_13__gitlab_internal_extension_markdown__migrated_golden_master_examples__bullet_list_style_2__001:
+ spec_example_position: 710
+ source_specification: gitlab
+08_04_14__gitlab_internal_extension_markdown__migrated_golden_master_examples__bullet_list_style_3__001:
+ spec_example_position: 711
+ source_specification: gitlab
+08_04_15__gitlab_internal_extension_markdown__migrated_golden_master_examples__code_block_javascript__001:
+ spec_example_position: 712
+ source_specification: gitlab
+08_04_16__gitlab_internal_extension_markdown__migrated_golden_master_examples__code_block_plaintext__001:
+ spec_example_position: 713
+ source_specification: gitlab
+08_04_17__gitlab_internal_extension_markdown__migrated_golden_master_examples__code_block_unknown__001:
+ spec_example_position: 714
+ source_specification: gitlab
+08_04_18__gitlab_internal_extension_markdown__migrated_golden_master_examples__color_chips__001:
+ spec_example_position: 715
+ source_specification: gitlab
+08_04_19__gitlab_internal_extension_markdown__migrated_golden_master_examples__description_list__001:
+ spec_example_position: 716
+ source_specification: gitlab
+08_04_20__gitlab_internal_extension_markdown__migrated_golden_master_examples__details__001:
+ spec_example_position: 717
+ source_specification: gitlab
+08_04_21__gitlab_internal_extension_markdown__migrated_golden_master_examples__diagram_kroki_nomnoml__001:
+ spec_example_position: 718
+ source_specification: gitlab
+08_04_22__gitlab_internal_extension_markdown__migrated_golden_master_examples__diagram_plantuml__001:
+ spec_example_position: 719
+ source_specification: gitlab
+08_04_23__gitlab_internal_extension_markdown__migrated_golden_master_examples__diagram_plantuml_unicode__001:
+ spec_example_position: 720
+ source_specification: gitlab
+08_04_24__gitlab_internal_extension_markdown__migrated_golden_master_examples__div__001:
+ spec_example_position: 721
+ source_specification: gitlab
+08_04_25__gitlab_internal_extension_markdown__migrated_golden_master_examples__emoji__001:
+ spec_example_position: 722
+ source_specification: gitlab
+08_04_26__gitlab_internal_extension_markdown__migrated_golden_master_examples__emphasis__001:
+ spec_example_position: 723
+ source_specification: gitlab
+08_04_27__gitlab_internal_extension_markdown__migrated_golden_master_examples__figure__001:
+ spec_example_position: 724
+ source_specification: gitlab
+08_04_28__gitlab_internal_extension_markdown__migrated_golden_master_examples__footnotes__001:
+ spec_example_position: 725
+ source_specification: gitlab
+08_04_29__gitlab_internal_extension_markdown__migrated_golden_master_examples__frontmatter_json__001:
+ spec_example_position: 726
+ source_specification: gitlab
+08_04_30__gitlab_internal_extension_markdown__migrated_golden_master_examples__frontmatter_toml__001:
+ spec_example_position: 727
+ source_specification: gitlab
+08_04_31__gitlab_internal_extension_markdown__migrated_golden_master_examples__frontmatter_yaml__001:
+ spec_example_position: 728
+ source_specification: gitlab
+08_04_32__gitlab_internal_extension_markdown__migrated_golden_master_examples__hard_break__001:
+ spec_example_position: 729
+ source_specification: gitlab
+08_04_33__gitlab_internal_extension_markdown__migrated_golden_master_examples__headings__001:
+ spec_example_position: 730
+ source_specification: gitlab
+08_04_34__gitlab_internal_extension_markdown__migrated_golden_master_examples__horizontal_rule__001:
+ spec_example_position: 731
+ source_specification: gitlab
+08_04_35__gitlab_internal_extension_markdown__migrated_golden_master_examples__html_marks__001:
+ spec_example_position: 732
+ source_specification: gitlab
+08_04_36__gitlab_internal_extension_markdown__migrated_golden_master_examples__image__001:
+ spec_example_position: 733
+ source_specification: gitlab
+08_04_37__gitlab_internal_extension_markdown__migrated_golden_master_examples__inline_code__001:
+ spec_example_position: 734
+ source_specification: gitlab
+08_04_38__gitlab_internal_extension_markdown__migrated_golden_master_examples__inline_diff__001:
+ spec_example_position: 735
+ source_specification: gitlab
+08_04_39__gitlab_internal_extension_markdown__migrated_golden_master_examples__label__001:
+ spec_example_position: 736
+ source_specification: gitlab
+08_04_40__gitlab_internal_extension_markdown__migrated_golden_master_examples__link__001:
+ spec_example_position: 737
+ source_specification: gitlab
+08_04_41__gitlab_internal_extension_markdown__migrated_golden_master_examples__math__001:
+ spec_example_position: 738
+ source_specification: gitlab
+08_04_42__gitlab_internal_extension_markdown__migrated_golden_master_examples__ordered_list__001:
+ spec_example_position: 739
+ source_specification: gitlab
+08_04_43__gitlab_internal_extension_markdown__migrated_golden_master_examples__ordered_list_with_start_order__001:
+ spec_example_position: 740
+ source_specification: gitlab
+08_04_44__gitlab_internal_extension_markdown__migrated_golden_master_examples__ordered_task_list__001:
+ spec_example_position: 741
+ source_specification: gitlab
+08_04_45__gitlab_internal_extension_markdown__migrated_golden_master_examples__ordered_task_list_with_order__001:
+ spec_example_position: 742
+ source_specification: gitlab
+08_04_46__gitlab_internal_extension_markdown__migrated_golden_master_examples__reference_for_project_wiki__001:
+ spec_example_position: 743
+ source_specification: gitlab
+08_04_47__gitlab_internal_extension_markdown__migrated_golden_master_examples__strike__001:
+ spec_example_position: 744
+ source_specification: gitlab
+08_04_48__gitlab_internal_extension_markdown__migrated_golden_master_examples__table__001:
+ spec_example_position: 745
+ source_specification: gitlab
+08_04_49__gitlab_internal_extension_markdown__migrated_golden_master_examples__table_of_contents__001:
+ spec_example_position: 746
+ source_specification: gitlab
+08_04_50__gitlab_internal_extension_markdown__migrated_golden_master_examples__task_list__001:
+ spec_example_position: 747
+ source_specification: gitlab
+08_04_51__gitlab_internal_extension_markdown__migrated_golden_master_examples__video__001:
+ spec_example_position: 748
+ source_specification: gitlab
+08_04_52__gitlab_internal_extension_markdown__migrated_golden_master_examples__word_break__001:
+ spec_example_position: 749
+ source_specification: gitlab
+08_05_00__gitlab_internal_extension_markdown__image_attributes__001:
+ spec_example_position: 750
+ source_specification: gitlab
+08_05_00__gitlab_internal_extension_markdown__image_attributes__002:
+ spec_example_position: 751
+ source_specification: gitlab
+08_05_00__gitlab_internal_extension_markdown__image_attributes__003:
+ spec_example_position: 752
+ source_specification: gitlab
+08_05_00__gitlab_internal_extension_markdown__image_attributes__004:
+ spec_example_position: 753
+ source_specification: gitlab
+08_05_00__gitlab_internal_extension_markdown__image_attributes__005:
+ spec_example_position: 754
+ source_specification: gitlab
diff --git a/glfm_specification/output_example_snapshots/html.yml b/glfm_specification/output_example_snapshots/html.yml
index bdd0777ce17..06e88ce4b81 100644
--- a/glfm_specification/output_example_snapshots/html.yml
+++ b/glfm_specification/output_example_snapshots/html.yml
@@ -1911,7 +1911,7 @@
<!-- foo -->*bar*
<p><em>baz</em></p>
static: |-
- *bar*
+ <!-- foo -->*bar*
<p data-sourcepos="2:1-2:5" dir="auto"><em>baz</em></p>
wysiwyg: |-
<p>*bar*
@@ -1933,8 +1933,11 @@
bar
baz -->
<p>okay</p>
- static: |2-
+ static: |-
+ <!-- Foo
+ bar
+ baz -->
<p data-sourcepos="5:1-5:4" dir="auto">okay</p>
wysiwyg: |-
<p>okay</p>
@@ -1988,10 +1991,12 @@
<!-- foo -->
<pre><code>&lt;!-- foo --&gt;
</code></pre>
- static: " \n<div class=\"gl-relative markdown-code-block js-markdown-code\">\n<pre
- data-sourcepos=\"3:5-3:16\" lang=\"plaintext\" class=\"code highlight js-syntax-highlight
- language-plaintext\" data-canonical-lang=\"\" v-pre=\"true\"><code><span id=\"LC1\"
- class=\"line\" lang=\"plaintext\">&lt;!-- foo --&gt;</span></code></pre>\n<copy-code></copy-code>\n</div>"
+ static: |2-
+ <!-- foo -->
+ <div class="gl-relative markdown-code-block js-markdown-code">
+ <pre data-sourcepos="3:5-3:16" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;!-- foo --&gt;</span></code></pre>
+ <copy-code></copy-code>
+ </div>
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>&lt;!-- foo --&gt;</code></pre>
04_06_00__leaf_blocks__html_blocks__036:
@@ -4311,7 +4316,7 @@
<li data-sourcepos="1:1-1:5">foo</li>
<li data-sourcepos="2:1-3:0">bar</li>
</ul>
-
+ <!-- -->
<ul data-sourcepos="6:1-7:5" dir="auto">
<li data-sourcepos="6:1-6:5">baz</li>
<li data-sourcepos="7:1-7:5">bim</li>
@@ -4343,7 +4348,7 @@
<p data-sourcepos="5:5-5:7">foo</p>
</li>
</ul>
-
+ <!-- -->
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-sourcepos="9:5-9:8" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">code</span></code></pre>
<copy-code></copy-code>
@@ -7382,7 +7387,8 @@
<p>foo <!-- this is a
comment - with hyphen --></p>
static: |-
- <p data-sourcepos="1:1-2:25" dir="auto">foo </p>
+ <p data-sourcepos="1:1-2:25" dir="auto">foo <!-- this is a
+ comment - with hyphen --></p>
wysiwyg: |-
<p>foo </p>
06_11_00__inlines__raw_html__014:
@@ -7942,3 +7948,705 @@
<p><a href="group-wikis-test-file">group-wikis-test-file</a></p>
static: |-
<p data-sourcepos="1:1-1:46" dir="auto"><a href="/groups/glfm_group/-/wikis/group-wikis-test-file" data-canonical-src="group-wikis-test-file">group-wikis-test-file</a></p>
+08_04_01__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_group__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:69" dir="auto"><a class="no-attachment-icon gfm" href="/groups/glfm_group/-/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" target="_blank" rel="noopener noreferrer" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" data-link="true"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="test-file" decoding="async" class="lazy gfm" data-src="/groups/glfm_group/-/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png"></a></p>
+ wysiwyg: |-
+ <p><img src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" alt="test-file"></p>
+08_04_02__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_project__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:69" dir="auto"><a class="no-attachment-icon gfm" href="/glfm_group/glfm_project/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" target="_blank" rel="noopener noreferrer" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" data-link="true"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="test-file" decoding="async" class="lazy gfm" data-src="/glfm_group/glfm_project/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png"></a></p>
+ wysiwyg: |-
+ <p><img src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" alt="test-file"></p>
+08_04_03__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_project_wiki__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:27" dir="auto"><a class="no-attachment-icon" href="/glfm_group/glfm_project/-/wikis/test-file.png" target="_blank" rel="noopener noreferrer" data-canonical-src="test-file.png"><img alt="test-file" decoding="async" class="lazy" data-src="/glfm_group/glfm_project/-/wikis/test-file.png" data-canonical-src="test-file.png"></a></p>
+ wysiwyg: |-
+ <p><img src="test-file.png" alt="test-file"></p>
+08_04_04__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_group__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:68" dir="auto"><a href="/groups/glfm_group/-/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip" data-link="true" class="gfm">test-file</a></p>
+ wysiwyg: |-
+ <p><a target="_blank" rel="noopener noreferrer nofollow" href="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip">test-file</a></p>
+08_04_05__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_project__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:68" dir="auto"><a href="/glfm_group/glfm_project/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip" data-link="true" class="gfm">test-file</a></p>
+ wysiwyg: |-
+ <p><a target="_blank" rel="noopener noreferrer nofollow" href="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip">test-file</a></p>
+08_04_06__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_project_wiki__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:26" dir="auto"><a href="/glfm_group/glfm_project/-/wikis/test-file.zip" data-canonical-src="test-file.zip">test-file</a></p>
+08_04_07__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_group_wiki__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:26" dir="auto"><a href="/groups/glfm_group/-/wikis/test-file.zip" data-canonical-src="test-file.zip">test-file</a></p>
+ wysiwyg: |-
+ <p><a target="_blank" rel="noopener noreferrer nofollow" href="test-file.zip">test-file</a></p>
+08_04_08__gitlab_internal_extension_markdown__migrated_golden_master_examples__audio__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:46" dir="auto"><span class="media-container audio-container"><audio src="https://gitlab.com/gitlab.mp3" controls="true" data-setup="{}" data-title="Sample Audio"></audio><a href="https://gitlab.com/gitlab.mp3" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Audio'">Sample Audio</a></span></p>
+ wysiwyg: |-
+ <p><span class="media-container audio-container"><audio src="https://gitlab.com/gitlab.mp3" controls="true" data-setup="{}" data-title="Sample Audio"></audio><a href="https://gitlab.com/gitlab.mp3">Sample Audio</a></span></p>
+08_04_09__gitlab_internal_extension_markdown__migrated_golden_master_examples__audio_and_video_in_lists__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <ul data-sourcepos="1:1-3:0" dir="auto">
+ <li data-sourcepos="1:1-1:43"><span class="media-container audio-container"><audio src="https://gitlab.com/1.mp3" controls="true" data-setup="{}" data-title="Sample Audio"></audio><a href="https://gitlab.com/1.mp3" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Audio'">Sample Audio</a></span></li>
+ <li data-sourcepos="2:1-3:0"><span class="media-container video-container"><video src="https://gitlab.com/2.mp4" controls="true" data-setup="{}" data-title="Sample Video" width="400" preload="metadata"></video><a href="https://gitlab.com/2.mp4" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Video'">Sample Video</a></span></li>
+ </ul>
+ <ol data-sourcepos="4:1-6:0" dir="auto">
+ <li data-sourcepos="4:1-4:44"><span class="media-container video-container"><video src="https://gitlab.com/1.mp4" controls="true" data-setup="{}" data-title="Sample Video" width="400" preload="metadata"></video><a href="https://gitlab.com/1.mp4" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Video'">Sample Video</a></span></li>
+ <li data-sourcepos="5:1-6:0"><span class="media-container audio-container"><audio src="https://gitlab.com/2.mp3" controls="true" data-setup="{}" data-title="Sample Audio"></audio><a href="https://gitlab.com/2.mp3" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Audio'">Sample Audio</a></span></li>
+ </ol>
+ <ul data-sourcepos="7:1-9:47" class="task-list" dir="auto">
+ <li data-sourcepos="7:1-7:47" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" checked disabled> <span class="media-container audio-container"><audio src="https://gitlab.com/1.mp3" controls="true" data-setup="{}" data-title="Sample Audio"></audio><a href="https://gitlab.com/1.mp3" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Audio'">Sample Audio</a></span>
+ </li>
+ <li data-sourcepos="8:1-8:47" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" checked disabled> <span class="media-container audio-container"><audio src="https://gitlab.com/2.mp3" controls="true" data-setup="{}" data-title="Sample Audio"></audio><a href="https://gitlab.com/2.mp3" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Audio'">Sample Audio</a></span>
+ </li>
+ <li data-sourcepos="9:1-9:47" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" checked disabled> <span class="media-container video-container"><video src="https://gitlab.com/3.mp4" controls="true" data-setup="{}" data-title="Sample Video" width="400" preload="metadata"></video><a href="https://gitlab.com/3.mp4" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Video'">Sample Video</a></span>
+ </li>
+ </ul>
+ wysiwyg: |-
+ <ul bullet="*"><li><p><span class="media-container audio-container"><audio src="https://gitlab.com/1.mp3" controls="true" data-setup="{}" data-title="Sample Audio"></audio><a href="https://gitlab.com/1.mp3">Sample Audio</a></span></p></li><li><p><span class="media-container video-container"><video src="https://gitlab.com/2.mp4" controls="true" data-setup="{}" data-title="Sample Video"></video><a href="https://gitlab.com/2.mp4">Sample Video</a></span></p></li></ul>
+ <ol parens="false"><li><p><span class="media-container video-container"><video src="https://gitlab.com/1.mp4" controls="true" data-setup="{}" data-title="Sample Video"></video><a href="https://gitlab.com/1.mp4">Sample Video</a></span></p></li><li><p><span class="media-container audio-container"><audio src="https://gitlab.com/2.mp3" controls="true" data-setup="{}" data-title="Sample Audio"></audio><a href="https://gitlab.com/2.mp3">Sample Audio</a></span></p></li></ol>
+ <ul start="1" parens="false" data-type="taskList"><li data-checked="true" data-type="taskItem"><label><input type="checkbox" checked="checked"><span></span></label><div><p><span class="media-container audio-container"><audio src="https://gitlab.com/1.mp3" controls="true" data-setup="{}" data-title="Sample Audio"></audio><a href="https://gitlab.com/1.mp3">Sample Audio</a></span></p></div></li><li data-checked="true" data-type="taskItem"><label><input type="checkbox" checked="checked"><span></span></label><div><p><span class="media-container audio-container"><audio src="https://gitlab.com/2.mp3" controls="true" data-setup="{}" data-title="Sample Audio"></audio><a href="https://gitlab.com/2.mp3">Sample Audio</a></span></p></div></li><li data-checked="true" data-type="taskItem"><label><input type="checkbox" checked="checked"><span></span></label><div><p><span class="media-container video-container"><video src="https://gitlab.com/3.mp4" controls="true" data-setup="{}" data-title="Sample Video"></video><a href="https://gitlab.com/3.mp4">Sample Video</a></span></p></div></li></ul>
+08_04_10__gitlab_internal_extension_markdown__migrated_golden_master_examples__blockquote__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <blockquote data-sourcepos="1:1-3:21" dir="auto">
+ <p data-sourcepos="1:3-1:22">This is a blockquote</p>
+ <p data-sourcepos="3:3-3:21">This is another one</p>
+ </blockquote>
+ wysiwyg: |-
+ <blockquote multiline="false"><p>This is a blockquote</p><p>This is another one</p></blockquote>
+08_04_11__gitlab_internal_extension_markdown__migrated_golden_master_examples__bold__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:8" dir="auto"><strong>bold</strong></p>
+ wysiwyg: |-
+ <p><strong>bold</strong></p>
+08_04_12__gitlab_internal_extension_markdown__migrated_golden_master_examples__bullet_list_style_1__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <ul data-sourcepos="1:1-3:24" dir="auto">
+ <li data-sourcepos="1:1-1:13">list item 1</li>
+ <li data-sourcepos="2:1-3:24">list item 2
+ <ul data-sourcepos="3:3-3:24">
+ <li data-sourcepos="3:3-3:24">embedded list item 3</li>
+ </ul>
+ </li>
+ </ul>
+ wysiwyg: |-
+ <ul bullet="*"><li><p>list item 1</p></li><li><p>list item 2</p><ul bullet="*"><li><p>embedded list item 3</p></li></ul></li></ul>
+08_04_13__gitlab_internal_extension_markdown__migrated_golden_master_examples__bullet_list_style_2__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <ul data-sourcepos="1:1-3:24" dir="auto">
+ <li data-sourcepos="1:1-1:13">list item 1</li>
+ <li data-sourcepos="2:1-3:24">list item 2
+ <ul data-sourcepos="3:3-3:24">
+ <li data-sourcepos="3:3-3:24">embedded list item 3</li>
+ </ul>
+ </li>
+ </ul>
+ wysiwyg: |-
+ <ul bullet="*"><li><p>list item 1</p></li><li><p>list item 2</p><ul bullet="*"><li><p>embedded list item 3</p></li></ul></li></ul>
+08_04_14__gitlab_internal_extension_markdown__migrated_golden_master_examples__bullet_list_style_3__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <ul data-sourcepos="1:1-3:24" dir="auto">
+ <li data-sourcepos="1:1-1:13">list item 1</li>
+ <li data-sourcepos="2:1-3:24">list item 2
+ <ul data-sourcepos="3:3-3:24">
+ <li data-sourcepos="3:3-3:24">embedded list item 3</li>
+ </ul>
+ </li>
+ </ul>
+ wysiwyg: |-
+ <ul bullet="*"><li><p>list item 1</p></li><li><p>list item 2</p><ul bullet="*"><li><p>embedded list item 3</p></li></ul></li></ul>
+08_04_15__gitlab_internal_extension_markdown__migrated_golden_master_examples__code_block_javascript__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <div class="gl-relative markdown-code-block js-markdown-code">
+ <pre data-sourcepos="1:1-3:3" lang="javascript" class="code highlight js-syntax-highlight language-javascript" v-pre="true"><code><span id="LC1" class="line" lang="javascript"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">hello world</span><span class="dl">'</span><span class="p">)</span></span></code></pre>
+ <copy-code></copy-code>
+ </div>
+ wysiwyg: |-
+ <pre language="javascript" class="content-editor-code-block undefined code highlight"><code> console.log('hello world')</code></pre>
+08_04_16__gitlab_internal_extension_markdown__migrated_golden_master_examples__code_block_plaintext__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <div class="gl-relative markdown-code-block js-markdown-code">
+ <pre data-sourcepos="1:1-3:3" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> plaintext</span></code></pre>
+ <copy-code></copy-code>
+ </div>
+ wysiwyg: |-
+ <pre class="content-editor-code-block undefined code highlight"><code> plaintext</code></pre>
+08_04_17__gitlab_internal_extension_markdown__migrated_golden_master_examples__code_block_unknown__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <div class="gl-relative markdown-code-block js-markdown-code">
+ <pre data-sourcepos="1:1-3:3" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="foobar" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> custom_language = &gt;&gt; this &lt;&lt;</span></code></pre>
+ <copy-code></copy-code>
+ </div>
+ wysiwyg: |-
+ <pre language="foobar" class="content-editor-code-block undefined code highlight"><code> custom_language = &gt;&gt; this &lt;&lt;</code></pre>
+08_04_18__gitlab_internal_extension_markdown__migrated_golden_master_examples__color_chips__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <ul data-sourcepos="1:1-9:25" dir="auto">
+ <li data-sourcepos="1:1-1:8"><code>#F00<span class="gfm-color_chip"><span style="background-color: #F00;"></span></span></code></li>
+ <li data-sourcepos="2:1-2:9"><code>#F00A<span class="gfm-color_chip"><span style="background-color: #F00A;"></span></span></code></li>
+ <li data-sourcepos="3:1-3:11"><code>#FF0000<span class="gfm-color_chip"><span style="background-color: #FF0000;"></span></span></code></li>
+ <li data-sourcepos="4:1-4:13"><code>#FF0000AA<span class="gfm-color_chip"><span style="background-color: #FF0000AA;"></span></span></code></li>
+ <li data-sourcepos="5:1-5:16"><code>RGB(0,255,0)<span class="gfm-color_chip"><span style="background-color: RGB(0,255,0);"></span></span></code></li>
+ <li data-sourcepos="6:1-6:19"><code>RGB(0%,100%,0%)<span class="gfm-color_chip"><span style="background-color: RGB(0%,100%,0%);"></span></span></code></li>
+ <li data-sourcepos="7:1-7:21"><code>RGBA(0,255,0,0.3)<span class="gfm-color_chip"><span style="background-color: RGBA(0,255,0,0.3);"></span></span></code></li>
+ <li data-sourcepos="8:1-8:20"><code>HSL(540,70%,50%)<span class="gfm-color_chip"><span style="background-color: HSL(540,70%,50%);"></span></span></code></li>
+ <li data-sourcepos="9:1-9:25"><code>HSLA(540,70%,50%,0.3)<span class="gfm-color_chip"><span style="background-color: HSLA(540,70%,50%,0.3);"></span></span></code></li>
+ </ul>
+ wysiwyg: |-
+ <ul bullet="*"><li><p><code>#F00</code></p></li><li><p><code>#F00A</code></p></li><li><p><code>#FF0000</code></p></li><li><p><code>#FF0000AA</code></p></li><li><p><code>RGB(0,255,0)</code></p></li><li><p><code>RGB(0%,100%,0%)</code></p></li><li><p><code>RGBA(0,255,0,0.3)</code></p></li><li><p><code>HSL(540,70%,50%)</code></p></li><li><p><code>HSLA(540,70%,50%,0.3)</code></p></li></ul>
+08_04_19__gitlab_internal_extension_markdown__migrated_golden_master_examples__description_list__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <dl>
+ <dt>Frog</dt>
+ <dd>Wet green thing</dd>
+ <dt>Rabbit</dt>
+ <dd>Warm fluffy thing</dd>
+ <dt>Punt</dt>
+ <dd>Kick a ball</dd>
+ <dd>Take a bet</dd>
+ <dt>Color</dt>
+ <dt>Colour</dt>
+ <dd>
+ <p data-sourcepos="13:1-13:35">Any hue except <em>white</em> or <strong>black</strong></p>
+ </dd>
+ </dl>
+ wysiwyg: |-
+ <p></p>
+08_04_20__gitlab_internal_extension_markdown__migrated_golden_master_examples__details__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <details>
+ <summary>This is the visible summary of the collapsible section</summary>
+ <ol data-sourcepos="4:1-6:0">
+ <li data-sourcepos="4:1-4:21">collapsed markdown</li>
+ <li data-sourcepos="5:1-6:0">more collapsed markdown</li>
+ </ol>
+ </details>
+ wysiwyg: |-
+ <p></p>
+08_04_21__gitlab_internal_extension_markdown__migrated_golden_master_examples__diagram_kroki_nomnoml__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <div class="gl-relative markdown-code-block js-markdown-code">
+ <pre data-sourcepos="1:1-11:3" lang="nomnoml" class="code highlight js-syntax-highlight language-nomnoml" v-pre="true"><code><span id="LC1" class="line" lang="nomnoml"> #stroke: #a86128</span>
+ <span id="LC2" class="line" lang="nomnoml"> [&lt;frame&gt;Decorator pattern|</span>
+ <span id="LC3" class="line" lang="nomnoml"> [&lt;abstract&gt;Component||+ operation()]</span>
+ <span id="LC4" class="line" lang="nomnoml"> [Client] depends --&gt; [Component]</span>
+ <span id="LC5" class="line" lang="nomnoml"> [Decorator|- next: Component]</span>
+ <span id="LC6" class="line" lang="nomnoml"> [Decorator] decorates -- [ConcreteComponent]</span>
+ <span id="LC7" class="line" lang="nomnoml"> [Component] &lt;:- [Decorator]</span>
+ <span id="LC8" class="line" lang="nomnoml"> [Component] &lt;:- [ConcreteComponent]</span>
+ <span id="LC9" class="line" lang="nomnoml"> ]</span></code></pre>
+ <copy-code></copy-code>
+ </div>
+ wysiwyg: |-
+ <pre language="nomnoml" class="content-editor-code-block undefined code highlight"><code> #stroke: #a86128
+ [&lt;frame&gt;Decorator pattern|
+ [&lt;abstract&gt;Component||+ operation()]
+ [Client] depends --&gt; [Component]
+ [Decorator|- next: Component]
+ [Decorator] decorates -- [ConcreteComponent]
+ [Component] &lt;:- [Decorator]
+ [Component] &lt;:- [ConcreteComponent]
+ ]</code></pre>
+08_04_22__gitlab_internal_extension_markdown__migrated_golden_master_examples__diagram_plantuml__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <div class="gl-relative markdown-code-block js-markdown-code">
+ <pre data-sourcepos="1:1-7:3" lang="plantuml" class="code highlight js-syntax-highlight language-plantuml" v-pre="true"><code><span id="LC1" class="line" lang="plantuml"> Alice -&gt; Bob: Authentication Request</span>
+ <span id="LC2" class="line" lang="plantuml"> Bob --&gt; Alice: Authentication Response</span>
+ <span id="LC3" class="line" lang="plantuml"></span>
+ <span id="LC4" class="line" lang="plantuml"> Alice -&gt; Bob: Another authentication Request</span>
+ <span id="LC5" class="line" lang="plantuml"> Alice &lt;-- Bob: Another authentication Response</span></code></pre>
+ <copy-code></copy-code>
+ </div>
+ wysiwyg: |-
+ <div><pre language="plantuml" class="content-editor-code-block code highlight" isdiagram="true" showpreview="true"><code> Alice -&gt; Bob: Authentication Request
+ Bob --&gt; Alice: Authentication Response
+
+ Alice -&gt; Bob: Another authentication Request
+ Alice &lt;-- Bob: Another authentication Response</code></pre></div>
+08_04_23__gitlab_internal_extension_markdown__migrated_golden_master_examples__diagram_plantuml_unicode__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <div class="gl-relative markdown-code-block js-markdown-code">
+ <pre data-sourcepos="1:1-3:3" lang="plantuml" class="code highlight js-syntax-highlight language-plantuml" v-pre="true"><code><span id="LC1" class="line" lang="plantuml">A -&gt; B : Text with norwegian characters: æøå</span></code></pre>
+ <copy-code></copy-code>
+ </div>
+ wysiwyg: |-
+ <div><pre language="plantuml" class="content-editor-code-block code highlight" isdiagram="true" showpreview="true"><code>A -&gt; B : Text with norwegian characters: æøå</code></pre></div>
+08_04_24__gitlab_internal_extension_markdown__migrated_golden_master_examples__div__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <div>plain text</div>
+ <div>
+ <p data-sourcepos="4:1-4:43">just a plain ol' div, not much to <em>expect</em>!</p>
+ </div>
+ wysiwyg: |-
+ <div><p>plain text</p></div>
+ <div><p>just a plain ol' div, not much to <em>expect</em>!</p></div>
+08_04_25__gitlab_internal_extension_markdown__migrated_golden_master_examples__emoji__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: "<p data-sourcepos=\"1:1-1:24\" dir=\"auto\"><gl-emoji title=\"sparkles\"
+ data-name=\"sparkles\" data-unicode-version=\"6.0\">✨</gl-emoji> <gl-emoji title=\"heavy
+ black heart\" data-name=\"heart\" data-unicode-version=\"1.1\">â¤</gl-emoji> <gl-emoji
+ title=\"hundred points symbol\" data-name=\"100\" data-unicode-version=\"6.0\">\U0001F4AF</gl-emoji></p>"
+ wysiwyg: |-
+ <p>:sparkles: :heart: :100:</p>
+08_04_26__gitlab_internal_extension_markdown__migrated_golden_master_examples__emphasis__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:17" dir="auto"><em>emphasized text</em></p>
+ wysiwyg: |-
+ <p><em>emphasized text</em></p>
+08_04_27__gitlab_internal_extension_markdown__migrated_golden_master_examples__figure__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <figure>
+ <p data-sourcepos="3:1-3:42"><a class="no-attachment-icon" href="elephant-sunset.jpg" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Elephant at sunset" decoding="async" class="lazy" data-src="elephant-sunset.jpg"></a></p>
+ <figcaption>An elephant at sunset</figcaption>
+ </figure>
+ <figure>
+ <p data-sourcepos="9:1-9:44"><a class="no-attachment-icon" href="croc-crocs.jpg" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A crocodile wearing crocs" decoding="async" class="lazy" data-src="croc-crocs.jpg"></a></p>
+ <figcaption>
+ <p data-sourcepos="13:1-13:28">A crocodile wearing <em>crocs</em>!</p>
+ </figcaption>
+ </figure>
+ wysiwyg: |-
+ <p></p>
+08_04_28__gitlab_internal_extension_markdown__migrated_golden_master_examples__footnotes__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:46" dir="auto">A footnote reference tag looks like this: <sup class="footnote-ref"><a href="#fn-1-42" id="fnref-1-42" data-footnote-ref>1</a></sup></p>
+ <p data-sourcepos="3:1-3:63" dir="auto">This reference tag is a mix of letters and numbers. <sup class="footnote-ref"><a href="#fn-footnote-42" id="fnref-footnote-42" data-footnote-ref>2</a></sup></p>
+ <section data-footnotes class="footnotes">
+ <ol>
+ <li id="fn-1-42">
+ <p data-sourcepos="5:7-5:41">This is the text inside a footnote. <a href="#fnref-1-42" data-footnote-backref aria-label="Back to content" class="footnote-backref"><gl-emoji title="leftwards arrow with hook" data-name="leftwards_arrow_with_hook" data-unicode-version="1.1">↩</gl-emoji></a></p>
+ </li>
+ <li id="fn-footnote-42">
+ <p data-sourcepos="7:14-7:38">This is another footnote. <a href="#fnref-footnote-42" data-footnote-backref aria-label="Back to content" class="footnote-backref"><gl-emoji title="leftwards arrow with hook" data-name="leftwards_arrow_with_hook" data-unicode-version="1.1">↩</gl-emoji></a></p>
+ </li>
+ </ol>
+ </section>
+ wysiwyg: |-
+ <p>A footnote reference tag looks like this: <sup identifier="1">1</sup></p>
+ <p>This reference tag is a mix of letters and numbers. <sup identifier="footnote">footnote</sup></p>
+ <div node="footnoteDefinition(paragraph(&quot;This is the text inside a footnote.&quot;))" htmlattributes="[object Object]"><p>This is the text inside a footnote.</p></div>
+ <div node="footnoteDefinition(paragraph(&quot;This is another footnote.&quot;))" htmlattributes="[object Object]"><p>This is another footnote.</p></div>
+08_04_29__gitlab_internal_extension_markdown__migrated_golden_master_examples__frontmatter_json__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <div class="gl-relative markdown-code-block js-markdown-code">
+ <pre data-sourcepos="1:1-5:3" lang="json" class="code highlight js-syntax-highlight language-json" data-lang-params="frontmatter" v-pre="true"><code><span id="LC1" class="line" lang="json"><span class="p">{</span></span>
+ <span id="LC2" class="line" lang="json"><span class="w"> </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Page title"</span></span>
+ <span id="LC3" class="line" lang="json"><span class="p">}</span></span></code></pre>
+ <copy-code></copy-code>
+ </div>
+ wysiwyg: |-
+ <pre language="json" class="content-editor-code-block undefined code highlight" isfrontmatter="true"><code>{
+ "title": "Page title"
+ }</code></pre>
+08_04_30__gitlab_internal_extension_markdown__migrated_golden_master_examples__frontmatter_toml__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <div class="gl-relative markdown-code-block js-markdown-code">
+ <pre data-sourcepos="1:1-3:3" lang="toml" class="code highlight js-syntax-highlight language-toml" data-lang-params="frontmatter" v-pre="true"><code><span id="LC1" class="line" lang="toml"><span class="py">title</span> <span class="p">=</span> <span class="s">"Page title"</span></span></code></pre>
+ <copy-code></copy-code>
+ </div>
+ wysiwyg: |-
+ <pre language="toml" class="content-editor-code-block undefined code highlight" isfrontmatter="true"><code>title = "Page title"</code></pre>
+08_04_31__gitlab_internal_extension_markdown__migrated_golden_master_examples__frontmatter_yaml__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <div class="gl-relative markdown-code-block js-markdown-code">
+ <pre data-sourcepos="1:1-3:3" lang="yaml" class="code highlight js-syntax-highlight language-yaml" data-lang-params="frontmatter" v-pre="true"><code><span id="LC1" class="line" lang="yaml"><span class="na">title</span><span class="pi">:</span> <span class="s">Page title</span></span></code></pre>
+ <copy-code></copy-code>
+ </div>
+ wysiwyg: |-
+ <pre language="yaml" class="content-editor-code-block undefined code highlight" isfrontmatter="true"><code>title: Page title</code></pre>
+08_04_32__gitlab_internal_extension_markdown__migrated_golden_master_examples__hard_break__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-2:10" dir="auto">This is a line after a<br>
+ hard break</p>
+ wysiwyg: |-
+ <p>This is a line after a<br>
+ hard break</p>
+08_04_33__gitlab_internal_extension_markdown__migrated_golden_master_examples__headings__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <h1 data-sourcepos="1:1-1:11" dir="auto">
+ <a id="user-content-heading-1" class="anchor" href="#heading-1" aria-hidden="true"></a>Heading 1</h1>
+ <h2 data-sourcepos="3:1-3:12" dir="auto">
+ <a id="user-content-heading-2" class="anchor" href="#heading-2" aria-hidden="true"></a>Heading 2</h2>
+ <h3 data-sourcepos="5:1-5:13" dir="auto">
+ <a id="user-content-heading-3" class="anchor" href="#heading-3" aria-hidden="true"></a>Heading 3</h3>
+ <h4 data-sourcepos="7:1-7:14" dir="auto">
+ <a id="user-content-heading-4" class="anchor" href="#heading-4" aria-hidden="true"></a>Heading 4</h4>
+ <h5 data-sourcepos="9:1-9:15" dir="auto">
+ <a id="user-content-heading-5" class="anchor" href="#heading-5" aria-hidden="true"></a>Heading 5</h5>
+ <h6 data-sourcepos="11:1-11:16" dir="auto">
+ <a id="user-content-heading-6" class="anchor" href="#heading-6" aria-hidden="true"></a>Heading 6</h6>
+ wysiwyg: |-
+ <h1>Heading 1</h1>
+ <h2>Heading 2</h2>
+ <h3>Heading 3</h3>
+ <h4>Heading 4</h4>
+ <h5>Heading 5</h5>
+ <h6>Heading 6</h6>
+08_04_34__gitlab_internal_extension_markdown__migrated_golden_master_examples__horizontal_rule__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <hr data-sourcepos="1:1-1:3">
+ wysiwyg: |-
+ <hr>
+08_04_35__gitlab_internal_extension_markdown__migrated_golden_master_examples__html_marks__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: "<ul data-sourcepos=\"1:1-15:130\" dir=\"auto\">\n<li data-sourcepos=\"1:1-1:48\">Content
+ editor is <del>great</del><ins>amazing</ins>.</li>\n<li data-sourcepos=\"2:1-2:126\">If
+ the changes <abbr title=\"Looks good to merge\">LGTM</abbr>, please <abbr title=\"Merge
+ when pipeline succeeds\">MWPS</abbr>.</li>\n<li data-sourcepos=\"3:1-3:288\">The
+ English song <q>Oh I do like to be beside the seaside</q> looks like this in Hebrew:
+ <span dir=\"rtl\">××”, ×× ×™ ×והב להיות ליד חוף ×”×™×</span>. In the computer's memory,
+ this is stored as <bdo dir=\"ltr\">××”, ×× ×™ ×והב להיות ליד חוף ×”×™×</bdo>.</li>\n<li
+ data-sourcepos=\"4:1-4:59\">\n<cite>The Scream</cite> by Edvard Munch. Painted
+ in 1893.</li>\n<li data-sourcepos=\"5:1-5:73\">\n<dfn>HTML</dfn> is the standard
+ markup language for creating web pages.</li>\n<li data-sourcepos=\"6:1-6:47\">Do
+ not forget to buy <mark>milk</mark> today.</li>\n<li data-sourcepos=\"7:1-7:64\">This
+ is a paragraph and <small>smaller text goes here</small>.</li>\n<li data-sourcepos=\"8:1-8:149\">The
+ concert starts at <time datetime=\"20:00\">20:00</time> and you'll be able to
+ enjoy the band for at least <time datetime=\"PT2H30M\">2h 30m</time>.</li>\n<li
+ data-sourcepos=\"9:1-9:62\">Press <kbd>Ctrl</kbd> + <kbd>C</kbd> to copy text
+ (Windows).</li>\n<li data-sourcepos=\"10:1-10:105\">WWF's goal is to: <q>Build
+ a future where people live in harmony with nature.</q> We hope they succeed.</li>\n<li
+ data-sourcepos=\"11:1-11:80\">The error occurred was: <samp>Keyboard not found.
+ Press F1 to continue.</samp>\n</li>\n<li data-sourcepos=\"12:1-12:136\">The area
+ of a triangle is: 1/2 x <var>b</var> x <var>h</var>, where <var>b</var> is the
+ base, and <var>h</var> is the vertical height.</li>\n<li data-sourcepos=\"13:1-13:35\"><ruby>æ¼¢<rt>ã„ㄢˋ</rt></ruby></li>\n<li
+ data-sourcepos=\"14:1-14:79\">C<sub>7</sub>H<sub>16</sub> + O<sub>2</sub> \t CO<sub>2</sub>
+ + H<sub>2</sub>O</li>\n<li data-sourcepos=\"15:1-15:130\">The <strong>Pythagorean
+ theorem</strong> is often expressed as <var>a<sup>2</sup></var> + <var>b<sup>2</sup></var>
+ = <var>c<sup>2</sup></var>\n</li>\n</ul>"
+ wysiwyg: "<ul bullet=\"*\"><li><p>Content editor is<s>great</s></p><p>.</p></li><li><p>If
+ the changes</p><p>, please</p><p>.</p></li><li><p>The English song</p><p> looks
+ like this in Hebrew:</p><p>. In the computer's memory, this is stored as</p><p>.</p></li><li><p>
+ by Edvard Munch. Painted in 1893.</p></li><li><p> is the standard markup language
+ for creating web pages.</p></li><li><p>Do not forget to buy</p><p> today.</p></li><li><p>This
+ is a paragraph and</p><p>.</p></li><li><p>The concert starts at</p><p> and you'll
+ be able to enjoy the band for at least</p><p>.</p></li><li><p>Press</p><p> +</p><p>
+ to copy text (Windows).</p></li><li><p>WWF's goal is to:</p><p> We hope they succeed.</p></li><li><p>The
+ error occurred was:</p></li><li><p>The area of a triangle is: 1/2 x</p><p> x</p><p>,
+ where</p><p> is the base, and</p><p> is the vertical height.</p></li><li><p></p></li><li><p>C</p><p>H</p><p>
+ + O</p><p> \t CO</p><p> + H</p><p>O</p></li><li><p>The<strong>Pythagorean theorem</strong>
+ is often expressed as</p><p> +</p><p> =</p></li></ul>"
+08_04_36__gitlab_internal_extension_markdown__migrated_golden_master_examples__image__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:40" dir="auto"><a class="no-attachment-icon" href="https://gitlab.com/logo.png" target="_blank" rel="nofollow noreferrer noopener"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="alt text" decoding="async" class="lazy" data-src="https://gitlab.com/logo.png"></a></p>
+ wysiwyg: |-
+ <p><img src="https://gitlab.com/logo.png" alt="alt text"></p>
+08_04_37__gitlab_internal_extension_markdown__migrated_golden_master_examples__inline_code__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:6" dir="auto"><code>code</code></p>
+ wysiwyg: |-
+ <p><code>code</code></p>
+08_04_38__gitlab_internal_extension_markdown__migrated_golden_master_examples__inline_diff__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <ul data-sourcepos="1:1-2:11" dir="auto">
+ <li data-sourcepos="1:1-1:13"><span class="idiff left right deletion">deleted</span></li>
+ <li data-sourcepos="2:1-2:11"><span class="idiff left right addition">added</span></li>
+ </ul>
+ wysiwyg: |-
+ <ul bullet="*"><li><p>{-deleted-}</p></li><li><p>{+added+}</p></li></ul>
+08_04_39__gitlab_internal_extension_markdown__migrated_golden_master_examples__label__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:4" dir="auto">~bug</p>
+ wysiwyg: |-
+ <p>~bug</p>
+08_04_40__gitlab_internal_extension_markdown__migrated_golden_master_examples__link__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:28" dir="auto"><a href="https://gitlab.com" rel="nofollow noreferrer noopener" target="_blank">GitLab</a></p>
+ wysiwyg: |-
+ <p><a target="_blank" rel="noopener noreferrer nofollow" href="https://gitlab.com">GitLab</a></p>
+08_04_41__gitlab_internal_extension_markdown__migrated_golden_master_examples__math__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:36" dir="auto">This math is inline <code class="code math js-render-math" data-math-style="inline">a^2+b^2=c^2</code>.</p>
+ <p data-sourcepos="3:1-3:27" dir="auto">This is on a separate line:</p>
+ <div class="gl-relative markdown-code-block js-markdown-code">
+ <pre data-sourcepos="5:1-7:3" lang="math" data-math-style="display" class="js-render-math code highlight js-syntax-highlight language-math" v-pre="true"><code><span id="LC1" class="line" lang="math">a^2+b^2=c^2</span></code></pre>
+ <copy-code></copy-code>
+ </div>
+ wysiwyg: |-
+ <p>This math is inline $<code>a^2+b^2=c^2</code>$.</p>
+ <p>This is on a separate line:</p>
+ <pre language="math" class="content-editor-code-block undefined code highlight"><code>a^2+b^2=c^2</code></pre>
+08_04_42__gitlab_internal_extension_markdown__migrated_golden_master_examples__ordered_list__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <ol data-sourcepos="1:1-3:14" dir="auto">
+ <li data-sourcepos="1:1-1:14">list item 1</li>
+ <li data-sourcepos="2:1-2:14">list item 2</li>
+ <li data-sourcepos="3:1-3:14">list item 3</li>
+ </ol>
+ wysiwyg: |-
+ <ol parens="false"><li><p>list item 1</p></li><li><p>list item 2</p></li><li><p>list item 3</p></li></ol>
+08_04_43__gitlab_internal_extension_markdown__migrated_golden_master_examples__ordered_list_with_start_order__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <ol start="134" data-sourcepos="1:1-3:16" dir="auto">
+ <li data-sourcepos="1:1-1:16">list item 1</li>
+ <li data-sourcepos="2:1-2:16">list item 2</li>
+ <li data-sourcepos="3:1-3:16">list item 3</li>
+ </ol>
+ wysiwyg: |-
+ <ol parens="false"><li><p>list item 1</p></li><li><p>list item 2</p></li><li><p>list item 3</p></li></ol>
+08_04_44__gitlab_internal_extension_markdown__migrated_golden_master_examples__ordered_task_list__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <ol data-sourcepos="1:1-6:18" class="task-list" dir="auto">
+ <li data-sourcepos="1:1-1:12" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" checked disabled> hello</li>
+ <li data-sourcepos="2:1-2:12" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" checked disabled> world</li>
+ <li data-sourcepos="3:1-6:18" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" disabled> example
+ <ol data-sourcepos="4:4-6:18" class="task-list">
+ <li data-sourcepos="4:4-6:18" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" disabled> of nested
+ <ol data-sourcepos="5:7-6:18" class="task-list">
+ <li data-sourcepos="5:7-5:22" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" checked disabled> task list</li>
+ <li data-sourcepos="6:7-6:18" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" disabled> items</li>
+ </ol>
+ </li>
+ </ol>
+ </li>
+ </ol>
+08_04_45__gitlab_internal_extension_markdown__migrated_golden_master_examples__ordered_task_list_with_order__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <ol start="4893" data-sourcepos="1:1-3:17" class="task-list" dir="auto">
+ <li data-sourcepos="1:1-1:15" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" checked disabled> hello</li>
+ <li data-sourcepos="2:1-2:15" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" checked disabled> world</li>
+ <li data-sourcepos="3:1-3:17" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" disabled> example</li>
+ </ol>
+ wysiwyg: |-
+ <ol start="1" parens="false" data-type="taskList"><li data-checked="true" data-type="taskItem"><label><input type="checkbox" checked="checked"><span></span></label><div><p>hello</p></div></li><li data-checked="true" data-type="taskItem"><label><input type="checkbox" checked="checked"><span></span></label><div><p>world</p></div></li><li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label><div><p>example</p></div></li></ol>
+08_04_46__gitlab_internal_extension_markdown__migrated_golden_master_examples__reference_for_project_wiki__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:98" dir="auto">Hi @gfm_user - thank you for reporting this ~"UX bug" (#1) we hope to fix it in %1.1 as part of !1</p>
+ wysiwyg: |-
+ <p>Hi @gfm_user - thank you for reporting this ~"UX bug" (#1) we hope to fix it in %1.1 as part of !1</p>
+08_04_47__gitlab_internal_extension_markdown__migrated_golden_master_examples__strike__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:7" dir="auto"><del>del</del></p>
+ wysiwyg: |-
+ <p><s>del</s></p>
+08_04_48__gitlab_internal_extension_markdown__migrated_golden_master_examples__table__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <table data-sourcepos="1:1-4:35" dir="auto">
+ <thead>
+ <tr data-sourcepos="1:1-1:19">
+ <th data-sourcepos="1:2-1:9">header</th>
+ <th data-sourcepos="1:11-1:18">header</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr data-sourcepos="3:1-3:31">
+ <td data-sourcepos="3:2-3:9"><code>code</code></td>
+ <td data-sourcepos="3:11-3:30">cell with <strong>bold</strong>
+ </td>
+ </tr>
+ <tr data-sourcepos="4:1-4:35">
+ <td data-sourcepos="4:2-4:13"><del>strike</del></td>
+ <td data-sourcepos="4:15-4:34">cell with <em>italic</em>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h1 data-sourcepos="6:1-6:21" dir="auto">
+ <a id="user-content-content-after-table" class="anchor" href="#content-after-table" aria-hidden="true"></a>content after table</h1>
+ wysiwyg: |-
+ <table><tbody><tr><th colspan="1" rowspan="1"><p>header</p></th><th colspan="1" rowspan="1"><p>header</p></th></tr><tr><td colspan="1" rowspan="1"><p><code>code</code></p></td><td colspan="1" rowspan="1"><p>cell with <strong>bold</strong></p></td></tr><tr><td colspan="1" rowspan="1"><p><s>strike</s></p></td><td colspan="1" rowspan="1"><p>cell with <em>italic</em></p></td></tr></tbody></table>
+ <h1>content after table</h1>
+08_04_49__gitlab_internal_extension_markdown__migrated_golden_master_examples__table_of_contents__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <ul class="section-nav">
+ <li>
+ <a href="#lorem">Lorem</a><ul><li>
+ <a href="#ipsum">Ipsum</a><ul><li><a href="#dolar">Dolar</a></li></ul>
+ </li></ul>
+ </li>
+ <li>
+ <a href="#sit-amit">Sit amit</a><ul><li><a href="#i-dont-know">I don't know</a></li></ul>
+ </li>
+ </ul>
+ <h1 data-sourcepos="3:1-3:7" dir="auto">
+ <a id="user-content-lorem" class="anchor" href="#lorem" aria-hidden="true"></a>Lorem</h1>
+ <p data-sourcepos="5:1-5:45" dir="auto">Well, that's just like... your opinion.. man.</p>
+ <h2 data-sourcepos="7:1-7:8" dir="auto">
+ <a id="user-content-ipsum" class="anchor" href="#ipsum" aria-hidden="true"></a>Ipsum</h2>
+ <h3 data-sourcepos="9:1-9:9" dir="auto">
+ <a id="user-content-dolar" class="anchor" href="#dolar" aria-hidden="true"></a>Dolar</h3>
+ <h1 data-sourcepos="11:1-11:10" dir="auto">
+ <a id="user-content-sit-amit" class="anchor" href="#sit-amit" aria-hidden="true"></a>Sit amit</h1>
+ <h3 data-sourcepos="13:1-13:16" dir="auto">
+ <a id="user-content-i-dont-know" class="anchor" href="#i-dont-know" aria-hidden="true"></a>I don't know</h3>
+ wysiwyg: |-
+ <div class="table-of-contents gl-border-1 gl-border-solid gl-text-center gl-border-gray-100 gl-mb-5">Table of contents</div>
+ <h1>Lorem</h1>
+ <p>Well, that's just like... your opinion.. man.</p>
+ <h2>Ipsum</h2>
+ <h3>Dolar</h3>
+ <h1>Sit amit</h1>
+ <h3>I don't know</h3>
+08_04_50__gitlab_internal_extension_markdown__migrated_golden_master_examples__task_list__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <ul data-sourcepos="1:1-6:15" class="task-list" dir="auto">
+ <li data-sourcepos="1:1-1:11" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" checked disabled> hello</li>
+ <li data-sourcepos="2:1-2:11" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" checked disabled> world</li>
+ <li data-sourcepos="3:1-6:15" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" disabled> example
+ <ul data-sourcepos="4:3-6:15" class="task-list">
+ <li data-sourcepos="4:3-6:15" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" disabled> of nested
+ <ul data-sourcepos="5:5-6:15" class="task-list">
+ <li data-sourcepos="5:5-5:19" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" checked disabled> task list</li>
+ <li data-sourcepos="6:5-6:15" class="task-list-item">
+ <task-button></task-button><input type="checkbox" class="task-list-item-checkbox" disabled> items</li>
+ </ul>
+ </li>
+ </ul>
+ </li>
+ </ul>
+08_04_51__gitlab_internal_extension_markdown__migrated_golden_master_examples__video__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:46" dir="auto"><span class="media-container video-container"><video src="https://gitlab.com/gitlab.mp4" controls="true" data-setup="{}" data-title="Sample Video" width="400" preload="metadata"></video><a href="https://gitlab.com/gitlab.mp4" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Video'">Sample Video</a></span></p>
+ wysiwyg: |-
+ <p><span class="media-container video-container"><video src="https://gitlab.com/gitlab.mp4" controls="true" data-setup="{}" data-title="Sample Video"></video><a href="https://gitlab.com/gitlab.mp4">Sample Video</a></span></p>
+08_04_52__gitlab_internal_extension_markdown__migrated_golden_master_examples__word_break__001:
+ canonical: |
+ TODO: Write canonical HTML for this example
+ static: |-
+ <p data-sourcepos="1:1-1:60" dir="auto">Fernstraßen<wbr>bau<wbr>privat<wbr>finanzierungs<wbr>gesetz</wbr></wbr></wbr></wbr></p>
+ wysiwyg: |-
+ <p>Fernstraßenbauprivatfinanzierungsgesetz</p>
+08_05_00__gitlab_internal_extension_markdown__image_attributes__001:
+ canonical: |
+ <p><img src="https://gitlab.com/logo.png" width="100" height="100"></p>
+ static: |-
+ <p data-sourcepos="1:1-1:58" dir="auto"><a class="no-attachment-icon" href="https://gitlab.com/logo.png" target="_blank" rel="nofollow noreferrer noopener"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="" width="100" height="100" decoding="async" class="lazy" data-src="https://gitlab.com/logo.png"></a></p>
+08_05_00__gitlab_internal_extension_markdown__image_attributes__002:
+ canonical: |
+ <p><img src="https://gitlab.com/logo.png" width="100%"></p>
+ static: |-
+ <p data-sourcepos="1:1-1:46" dir="auto"><a class="no-attachment-icon" href="https://gitlab.com/logo.png" target="_blank" rel="nofollow noreferrer noopener"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="" width="100%" decoding="async" class="lazy" data-src="https://gitlab.com/logo.png"></a></p>
+08_05_00__gitlab_internal_extension_markdown__image_attributes__003:
+ canonical: |
+ <p><img src="https://gitlab.com/logo.png" height="100px"></p>
+ static: |-
+ <p data-sourcepos="1:1-1:48" dir="auto"><a class="no-attachment-icon" href="https://gitlab.com/logo.png" target="_blank" rel="nofollow noreferrer noopener"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="" height="100px" decoding="async" class="lazy" data-src="https://gitlab.com/logo.png"></a></p>
+08_05_00__gitlab_internal_extension_markdown__image_attributes__004:
+ canonical: |
+ <p><img src="https://gitlab.com/logo.png" width="100" height="100"></p>
+ static: |-
+ <p data-sourcepos="1:1-1:60" dir="auto"><a class="no-attachment-icon" href="https://gitlab.com/logo.png" target="_blank" rel="nofollow noreferrer noopener"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="" width="100" height="100" decoding="async" class="lazy" data-src="https://gitlab.com/logo.png"></a></p>
+08_05_00__gitlab_internal_extension_markdown__image_attributes__005:
+ canonical: |
+ <p><img src="https://gitlab.com/logo.png"> {width="100" height="100"}</p>
+ static: |-
+ <p data-sourcepos="1:1-1:59" dir="auto"><a class="no-attachment-icon" href="https://gitlab.com/logo.png" target="_blank" rel="nofollow noreferrer noopener"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="" decoding="async" class="lazy" data-src="https://gitlab.com/logo.png"></a> {width="100" height="100"}</p>
diff --git a/glfm_specification/output_example_snapshots/markdown.yml b/glfm_specification/output_example_snapshots/markdown.yml
index 867108a6cc5..c5145803088 100644
--- a/glfm_specification/output_example_snapshots/markdown.yml
+++ b/glfm_specification/output_example_snapshots/markdown.yml
@@ -2273,3 +2273,287 @@
[project-wikis-test-file](project-wikis-test-file)
08_03_00__gitlab_internal_extension_markdown__markdown_preview_api_request_overrides__006: |
[group-wikis-test-file](group-wikis-test-file)
+08_04_01__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_group__001: |
+ ![test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png)
+08_04_02__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_project__001: |
+ ![test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png)
+08_04_03__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_project_wiki__001: |
+ ![test-file](test-file.png)
+08_04_04__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_group__001: |
+ [test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip)
+08_04_05__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_project__001: |
+ [test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip)
+08_04_06__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_project_wiki__001: |
+ [test-file](test-file.zip)
+08_04_07__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_group_wiki__001: |
+ [test-file](test-file.zip)
+08_04_08__gitlab_internal_extension_markdown__migrated_golden_master_examples__audio__001: |
+ ![Sample Audio](https://gitlab.com/gitlab.mp3)
+08_04_09__gitlab_internal_extension_markdown__migrated_golden_master_examples__audio_and_video_in_lists__001: |
+ * ![Sample Audio](https://gitlab.com/1.mp3)
+ * ![Sample Video](https://gitlab.com/2.mp4)
+
+ 1. ![Sample Video](https://gitlab.com/1.mp4)
+ 2. ![Sample Audio](https://gitlab.com/2.mp3)
+
+ * [x] ![Sample Audio](https://gitlab.com/1.mp3)
+ * [x] ![Sample Audio](https://gitlab.com/2.mp3)
+ * [x] ![Sample Video](https://gitlab.com/3.mp4)
+08_04_10__gitlab_internal_extension_markdown__migrated_golden_master_examples__blockquote__001: |
+ > This is a blockquote
+ >
+ > This is another one
+08_04_11__gitlab_internal_extension_markdown__migrated_golden_master_examples__bold__001: |
+ **bold**
+08_04_12__gitlab_internal_extension_markdown__migrated_golden_master_examples__bullet_list_style_1__001: |
+ * list item 1
+ * list item 2
+ * embedded list item 3
+08_04_13__gitlab_internal_extension_markdown__migrated_golden_master_examples__bullet_list_style_2__001: |
+ - list item 1
+ - list item 2
+ * embedded list item 3
+08_04_14__gitlab_internal_extension_markdown__migrated_golden_master_examples__bullet_list_style_3__001: |
+ + list item 1
+ + list item 2
+ - embedded list item 3
+08_04_15__gitlab_internal_extension_markdown__migrated_golden_master_examples__code_block_javascript__001: |
+ ```javascript
+ console.log('hello world')
+ ```
+08_04_16__gitlab_internal_extension_markdown__migrated_golden_master_examples__code_block_plaintext__001: |
+ ```
+ plaintext
+ ```
+08_04_17__gitlab_internal_extension_markdown__migrated_golden_master_examples__code_block_unknown__001: |
+ ```foobar
+ custom_language = >> this <<
+ ```
+08_04_18__gitlab_internal_extension_markdown__migrated_golden_master_examples__color_chips__001: |
+ - `#F00`
+ - `#F00A`
+ - `#FF0000`
+ - `#FF0000AA`
+ - `RGB(0,255,0)`
+ - `RGB(0%,100%,0%)`
+ - `RGBA(0,255,0,0.3)`
+ - `HSL(540,70%,50%)`
+ - `HSLA(540,70%,50%,0.3)`
+08_04_19__gitlab_internal_extension_markdown__migrated_golden_master_examples__description_list__001: |
+ <dl>
+ <dt>Frog</dt>
+ <dd>Wet green thing</dd>
+ <dt>Rabbit</dt>
+ <dd>Warm fluffy thing</dd>
+ <dt>Punt</dt>
+ <dd>Kick a ball</dd>
+ <dd>Take a bet</dd>
+ <dt>Color</dt>
+ <dt>Colour</dt>
+ <dd>
+
+ Any hue except _white_ or **black**
+
+ </dd>
+ </dl>
+08_04_20__gitlab_internal_extension_markdown__migrated_golden_master_examples__details__001: |
+ <details>
+ <summary>This is the visible summary of the collapsible section</summary>
+
+ 1. collapsed markdown
+ 2. more collapsed markdown
+
+ </details>
+08_04_21__gitlab_internal_extension_markdown__migrated_golden_master_examples__diagram_kroki_nomnoml__001: |
+ ```nomnoml
+ #stroke: #a86128
+ [<frame>Decorator pattern|
+ [<abstract>Component||+ operation()]
+ [Client] depends --> [Component]
+ [Decorator|- next: Component]
+ [Decorator] decorates -- [ConcreteComponent]
+ [Component] <:- [Decorator]
+ [Component] <:- [ConcreteComponent]
+ ]
+ ```
+08_04_22__gitlab_internal_extension_markdown__migrated_golden_master_examples__diagram_plantuml__001: |
+ ```plantuml
+ Alice -> Bob: Authentication Request
+ Bob --> Alice: Authentication Response
+
+ Alice -> Bob: Another authentication Request
+ Alice <-- Bob: Another authentication Response
+ ```
+08_04_23__gitlab_internal_extension_markdown__migrated_golden_master_examples__diagram_plantuml_unicode__001: |
+ ```plantuml
+ A -> B : Text with norwegian characters: æøå
+ ```
+08_04_24__gitlab_internal_extension_markdown__migrated_golden_master_examples__div__001: |
+ <div>plain text</div>
+ <div>
+
+ just a plain ol' div, not much to _expect_!
+
+ </div>
+08_04_25__gitlab_internal_extension_markdown__migrated_golden_master_examples__emoji__001: |
+ :sparkles: :heart: :100:
+08_04_26__gitlab_internal_extension_markdown__migrated_golden_master_examples__emphasis__001: |
+ _emphasized text_
+08_04_27__gitlab_internal_extension_markdown__migrated_golden_master_examples__figure__001: |
+ <figure>
+
+ ![Elephant at sunset](elephant-sunset.jpg)
+
+ <figcaption>An elephant at sunset</figcaption>
+ </figure>
+ <figure>
+
+ ![A crocodile wearing crocs](croc-crocs.jpg)
+
+ <figcaption>
+
+ A crocodile wearing _crocs_!
+
+ </figcaption>
+ </figure>
+08_04_28__gitlab_internal_extension_markdown__migrated_golden_master_examples__footnotes__001: |
+ A footnote reference tag looks like this: [^1]
+
+ This reference tag is a mix of letters and numbers. [^footnote]
+
+ [^1]: This is the text inside a footnote.
+
+ [^footnote]: This is another footnote.
+08_04_29__gitlab_internal_extension_markdown__migrated_golden_master_examples__frontmatter_json__001: |
+ ;;;
+ {
+ "title": "Page title"
+ }
+ ;;;
+08_04_30__gitlab_internal_extension_markdown__migrated_golden_master_examples__frontmatter_toml__001: |
+ +++
+ title = "Page title"
+ +++
+08_04_31__gitlab_internal_extension_markdown__migrated_golden_master_examples__frontmatter_yaml__001: |
+ ---
+ title: Page title
+ ---
+08_04_32__gitlab_internal_extension_markdown__migrated_golden_master_examples__hard_break__001: |
+ This is a line after a\
+ hard break
+08_04_33__gitlab_internal_extension_markdown__migrated_golden_master_examples__headings__001: |
+ # Heading 1
+
+ ## Heading 2
+
+ ### Heading 3
+
+ #### Heading 4
+
+ ##### Heading 5
+
+ ###### Heading 6
+08_04_34__gitlab_internal_extension_markdown__migrated_golden_master_examples__horizontal_rule__001: |
+ ---
+08_04_35__gitlab_internal_extension_markdown__migrated_golden_master_examples__html_marks__001: "*
+ Content editor is ~~great~~<ins>amazing</ins>.\n* If the changes <abbr title=\"Looks
+ good to merge\">LGTM</abbr>, please <abbr title=\"Merge when pipeline succeeds\">MWPS</abbr>.\n*
+ The English song <q>Oh I do like to be beside the seaside</q> looks like this in
+ Hebrew: <span dir=\"rtl\">××”, ×× ×™ ×והב להיות ליד חוף ×”×™×</span>. In the computer's
+ memory, this is stored as <bdo dir=\"ltr\">××”, ×× ×™ ×והב להיות ליד חוף ×”×™×</bdo>.\n*
+ <cite>The Scream</cite> by Edvard Munch. Painted in 1893.\n* <dfn>HTML</dfn> is
+ the standard markup language for creating web pages.\n* Do not forget to buy <mark>milk</mark>
+ today.\n* This is a paragraph and <small>smaller text goes here</small>.\n* The
+ concert starts at <time datetime=\"20:00\">20:00</time> and you'll be able to enjoy
+ the band for at least <time datetime=\"PT2H30M\">2h 30m</time>.\n* Press <kbd>Ctrl</kbd>
+ + <kbd>C</kbd> to copy text (Windows).\n* WWF's goal is to: <q>Build a future where
+ people live in harmony with nature.</q> We hope they succeed.\n* The error occurred
+ was: <samp>Keyboard not found. Press F1 to continue.</samp>\n* The area of a triangle
+ is: 1/2 x <var>b</var> x <var>h</var>, where <var>b</var> is the base, and <var>h</var>
+ is the vertical height.\n* <ruby>æ¼¢<rt>ã„ㄢˋ</rt></ruby>\n* C<sub>7</sub>H<sub>16</sub>
+ + O<sub>2</sub> \t CO<sub>2</sub> + H<sub>2</sub>O\n* The **Pythagorean theorem**
+ is often expressed as <var>a<sup>2</sup></var> + <var>b<sup>2</sup></var> = <var>c<sup>2</sup></var>\n"
+08_04_36__gitlab_internal_extension_markdown__migrated_golden_master_examples__image__001: |
+ ![alt text](https://gitlab.com/logo.png)
+08_04_37__gitlab_internal_extension_markdown__migrated_golden_master_examples__inline_code__001: |
+ `code`
+08_04_38__gitlab_internal_extension_markdown__migrated_golden_master_examples__inline_diff__001: |
+ * {-deleted-}
+ * {+added+}
+08_04_39__gitlab_internal_extension_markdown__migrated_golden_master_examples__label__001: |
+ ~bug
+08_04_40__gitlab_internal_extension_markdown__migrated_golden_master_examples__link__001: |
+ [GitLab](https://gitlab.com)
+08_04_41__gitlab_internal_extension_markdown__migrated_golden_master_examples__math__001: |
+ This math is inline $`a^2+b^2=c^2`$.
+
+ This is on a separate line:
+
+ ```math
+ a^2+b^2=c^2
+ ```
+08_04_42__gitlab_internal_extension_markdown__migrated_golden_master_examples__ordered_list__001: |
+ 1. list item 1
+ 2. list item 2
+ 3. list item 3
+08_04_43__gitlab_internal_extension_markdown__migrated_golden_master_examples__ordered_list_with_start_order__001: |
+ 134. list item 1
+ 135. list item 2
+ 136. list item 3
+08_04_44__gitlab_internal_extension_markdown__migrated_golden_master_examples__ordered_task_list__001: |
+ 1. [x] hello
+ 2. [x] world
+ 3. [ ] example
+ 1. [ ] of nested
+ 1. [x] task list
+ 2. [ ] items
+08_04_45__gitlab_internal_extension_markdown__migrated_golden_master_examples__ordered_task_list_with_order__001: |
+ 4893. [x] hello
+ 4894. [x] world
+ 4895. [ ] example
+08_04_46__gitlab_internal_extension_markdown__migrated_golden_master_examples__reference_for_project_wiki__001: |
+ Hi @gfm_user - thank you for reporting this ~"UX bug" (#1) we hope to fix it in %1.1 as part of !1
+08_04_47__gitlab_internal_extension_markdown__migrated_golden_master_examples__strike__001: |
+ ~~del~~
+08_04_48__gitlab_internal_extension_markdown__migrated_golden_master_examples__table__001: |
+ | header | header |
+ |--------|--------|
+ | `code` | cell with **bold** |
+ | ~~strike~~ | cell with _italic_ |
+
+ # content after table
+08_04_49__gitlab_internal_extension_markdown__migrated_golden_master_examples__table_of_contents__001: |
+ [[_TOC_]]
+
+ # Lorem
+
+ Well, that's just like... your opinion.. man.
+
+ ## Ipsum
+
+ ### Dolar
+
+ # Sit amit
+
+ ### I don't know
+08_04_50__gitlab_internal_extension_markdown__migrated_golden_master_examples__task_list__001: |
+ * [x] hello
+ * [x] world
+ * [ ] example
+ * [ ] of nested
+ * [x] task list
+ * [ ] items
+08_04_51__gitlab_internal_extension_markdown__migrated_golden_master_examples__video__001: |
+ ![Sample Video](https://gitlab.com/gitlab.mp4)
+08_04_52__gitlab_internal_extension_markdown__migrated_golden_master_examples__word_break__001: |
+ Fernstraßen<wbr>bau<wbr>privat<wbr>finanzierungs<wbr>gesetz
+08_05_00__gitlab_internal_extension_markdown__image_attributes__001: |
+ ![](https://gitlab.com/logo.png){width="100" height="100"}
+08_05_00__gitlab_internal_extension_markdown__image_attributes__002: |
+ ![](https://gitlab.com/logo.png){width="100%"}
+08_05_00__gitlab_internal_extension_markdown__image_attributes__003: |
+ ![](https://gitlab.com/logo.png){height="100px"}
+08_05_00__gitlab_internal_extension_markdown__image_attributes__004: |
+ ![](https://gitlab.com/logo.png){ width="100" height="100" }
+08_05_00__gitlab_internal_extension_markdown__image_attributes__005: |
+ ![](https://gitlab.com/logo.png) {width="100" height="100"}
diff --git a/glfm_specification/output_example_snapshots/prosemirror_json.yml b/glfm_specification/output_example_snapshots/prosemirror_json.yml
index de54518a574..f6110185348 100644
--- a/glfm_specification/output_example_snapshots/prosemirror_json.yml
+++ b/glfm_specification/output_example_snapshots/prosemirror_json.yml
@@ -7,7 +7,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -26,7 +27,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -45,7 +47,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -119,7 +122,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -148,7 +152,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -181,7 +186,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -204,7 +210,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -439,7 +446,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -993,7 +1001,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -1441,7 +1450,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -1706,6 +1716,7 @@
"attrs": {
"language": "yaml",
"class": "code highlight",
+ "langParams": null,
"isFrontmatter": true
},
"content": [
@@ -1762,6 +1773,7 @@
"attrs": {
"language": "yaml",
"class": "code highlight",
+ "langParams": null,
"isFrontmatter": true
}
}
@@ -1806,7 +1818,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -1977,7 +1990,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2084,7 +2098,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2103,7 +2118,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2122,7 +2138,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2156,7 +2173,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2196,7 +2214,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2221,7 +2240,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2243,7 +2263,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2262,7 +2283,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2281,7 +2303,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2300,7 +2323,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2319,7 +2343,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2358,7 +2383,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2377,7 +2403,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2396,7 +2423,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2415,7 +2443,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2434,7 +2463,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
}
}
]
@@ -2447,7 +2477,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2472,7 +2503,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2502,7 +2534,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
}
}
]
@@ -2515,7 +2548,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
}
}
]
@@ -2528,7 +2562,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2547,7 +2582,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2566,7 +2602,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2585,7 +2622,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2604,7 +2642,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2623,7 +2662,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2642,7 +2682,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2676,7 +2717,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2704,7 +2746,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2744,7 +2787,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2775,7 +2819,8 @@
"type": "codeBlock",
"attrs": {
"language": "ruby",
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2794,7 +2839,8 @@
"type": "codeBlock",
"attrs": {
"language": "ruby",
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2813,7 +2859,8 @@
"type": "codeBlock",
"attrs": {
"language": ";",
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
}
}
]
@@ -2850,7 +2897,8 @@
"type": "codeBlock",
"attrs": {
"language": "aa",
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -2869,7 +2917,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -3641,7 +3690,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -3663,7 +3713,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -3861,7 +3912,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -4583,7 +4635,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -4611,7 +4664,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -5124,7 +5178,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -6090,7 +6145,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -6257,7 +6313,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -6272,7 +6329,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -6297,7 +6355,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
}
}
]
@@ -6315,7 +6374,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
}
}
]
@@ -6749,7 +6809,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -6796,7 +6857,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -6851,7 +6913,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -6987,7 +7050,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -7230,7 +7294,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -7297,7 +7362,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -7455,7 +7521,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -7497,7 +7564,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -7520,7 +7588,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -7542,7 +7611,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -7574,7 +7644,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -7596,7 +7667,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -7632,7 +7704,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -7654,7 +7727,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -7801,7 +7875,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -7822,7 +7897,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -8120,7 +8196,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -8179,7 +8256,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -8238,7 +8316,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -8278,7 +8357,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -8316,7 +8396,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -9410,7 +9491,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -9705,7 +9787,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -9991,7 +10074,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -10195,7 +10279,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -10323,7 +10408,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -10663,7 +10749,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -10682,7 +10769,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -10808,7 +10896,8 @@
"type": "codeBlock",
"attrs": {
"language": "foo+bar",
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -10996,7 +11085,8 @@
"type": "codeBlock",
"attrs": {
"language": "föö",
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -11035,7 +11125,8 @@
"type": "codeBlock",
"attrs": {
"language": null,
- "class": "code highlight"
+ "class": "code highlight",
+ "langParams": null
},
"content": [
{
@@ -14577,6 +14668,8 @@
"title": "*",
"uploading": false,
"canonicalSrc": "foo",
+ "width": null,
+ "height": null,
"isReference": false
}
}
@@ -15723,6 +15816,8 @@
"title": null,
"uploading": false,
"canonicalSrc": "moon.jpg",
+ "width": null,
+ "height": null,
"isReference": false
},
"marks": [
@@ -15851,6 +15946,8 @@
"title": null,
"uploading": false,
"canonicalSrc": "uri3",
+ "width": null,
+ "height": null,
"isReference": false
}
}
@@ -16265,6 +16362,8 @@
"title": null,
"uploading": false,
"canonicalSrc": "moon.jpg",
+ "width": null,
+ "height": null,
"isReference": false
},
"marks": [
@@ -18112,6 +18211,8 @@
"title": "title",
"uploading": false,
"canonicalSrc": "/url",
+ "width": null,
+ "height": null,
"isReference": false
}
}
@@ -18134,6 +18235,8 @@
"title": "train & tracks",
"uploading": false,
"canonicalSrc": "foo *bar*",
+ "width": null,
+ "height": null,
"isReference": true
}
}
@@ -18170,6 +18273,8 @@
"title": null,
"uploading": false,
"canonicalSrc": "/url2",
+ "width": null,
+ "height": null,
"isReference": false
}
}
@@ -18192,6 +18297,8 @@
"title": null,
"uploading": false,
"canonicalSrc": "/url2",
+ "width": null,
+ "height": null,
"isReference": false
}
}
@@ -18214,6 +18321,8 @@
"title": "train & tracks",
"uploading": false,
"canonicalSrc": "foo *bar*",
+ "width": null,
+ "height": null,
"isReference": true
}
}
@@ -18250,6 +18359,8 @@
"title": "train & tracks",
"uploading": false,
"canonicalSrc": "foobar",
+ "width": null,
+ "height": null,
"isReference": true
}
}
@@ -18286,6 +18397,8 @@
"title": null,
"uploading": false,
"canonicalSrc": "train.jpg",
+ "width": null,
+ "height": null,
"isReference": false
}
}
@@ -18312,6 +18425,8 @@
"title": "title",
"uploading": false,
"canonicalSrc": "/path/to/train.jpg",
+ "width": null,
+ "height": null,
"isReference": false
}
}
@@ -18334,6 +18449,8 @@
"title": null,
"uploading": false,
"canonicalSrc": "url",
+ "width": null,
+ "height": null,
"isReference": false
}
}
@@ -18356,6 +18473,8 @@
"title": null,
"uploading": false,
"canonicalSrc": "/url",
+ "width": null,
+ "height": null,
"isReference": false
}
}
@@ -18378,6 +18497,8 @@
"title": null,
"uploading": false,
"canonicalSrc": "bar",
+ "width": null,
+ "height": null,
"isReference": true
}
}
@@ -18414,6 +18535,8 @@
"title": null,
"uploading": false,
"canonicalSrc": "bar",
+ "width": null,
+ "height": null,
"isReference": true
}
}
@@ -18450,6 +18573,8 @@
"title": "title",
"uploading": false,
"canonicalSrc": "foo",
+ "width": null,
+ "height": null,
"isReference": true
}
}
@@ -18486,6 +18611,8 @@
"title": "title",
"uploading": false,
"canonicalSrc": "*foo* bar",
+ "width": null,
+ "height": null,
"isReference": true
}
}
@@ -18522,6 +18649,8 @@
"title": "title",
"uploading": false,
"canonicalSrc": "foo",
+ "width": null,
+ "height": null,
"isReference": true
}
}
@@ -18558,6 +18687,8 @@
"title": "title",
"uploading": false,
"canonicalSrc": "foo",
+ "width": null,
+ "height": null,
"isReference": true
}
},
@@ -18598,6 +18729,8 @@
"title": "title",
"uploading": false,
"canonicalSrc": "foo",
+ "width": null,
+ "height": null,
"isReference": true
}
}
@@ -18634,6 +18767,8 @@
"title": "title",
"uploading": false,
"canonicalSrc": "*foo* bar",
+ "width": null,
+ "height": null,
"isReference": true
}
}
@@ -18694,6 +18829,8 @@
"title": "title",
"uploading": false,
"canonicalSrc": "foo",
+ "width": null,
+ "height": null,
"isReference": true
}
}
@@ -20676,6 +20813,7 @@
"attrs": {
"language": "yaml",
"class": "code highlight",
+ "langParams": null,
"isFrontmatter": true
},
"content": [
@@ -20696,6 +20834,7 @@
"attrs": {
"language": "toml",
"class": "code highlight",
+ "langParams": null,
"isFrontmatter": true
},
"content": [
@@ -20716,6 +20855,7 @@
"attrs": {
"language": "json",
"class": "code highlight",
+ "langParams": null,
"isFrontmatter": true
},
"content": [
@@ -21010,3 +21150,2434 @@
}
]
}
+08_04_01__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_group__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "image",
+ "attrs": {
+ "src": "/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png",
+ "alt": "test-file",
+ "title": null,
+ "uploading": false,
+ "canonicalSrc": "/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png",
+ "width": null,
+ "height": null,
+ "isReference": false
+ }
+ }
+ ]
+ }
+ ]
+ }
+08_04_02__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_project__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "image",
+ "attrs": {
+ "src": "/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png",
+ "alt": "test-file",
+ "title": null,
+ "uploading": false,
+ "canonicalSrc": "/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png",
+ "width": null,
+ "height": null,
+ "isReference": false
+ }
+ }
+ ]
+ }
+ ]
+ }
+08_04_03__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_project_wiki__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "image",
+ "attrs": {
+ "src": "test-file.png",
+ "alt": "test-file",
+ "title": null,
+ "uploading": false,
+ "canonicalSrc": "test-file.png",
+ "width": null,
+ "height": null,
+ "isReference": false
+ }
+ }
+ ]
+ }
+ ]
+ }
+08_04_04__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_group__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "link",
+ "attrs": {
+ "href": "/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip",
+ "target": "_blank",
+ "class": null,
+ "title": null,
+ "canonicalSrc": "/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip",
+ "isReference": false
+ }
+ }
+ ],
+ "text": "test-file"
+ }
+ ]
+ }
+ ]
+ }
+08_04_05__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_project__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "link",
+ "attrs": {
+ "href": "/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip",
+ "target": "_blank",
+ "class": null,
+ "title": null,
+ "canonicalSrc": "/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip",
+ "isReference": false
+ }
+ }
+ ],
+ "text": "test-file"
+ }
+ ]
+ }
+ ]
+ }
+08_04_06__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_project_wiki__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "link",
+ "attrs": {
+ "href": "test-file.zip",
+ "target": "_blank",
+ "class": null,
+ "title": null,
+ "canonicalSrc": "test-file.zip",
+ "isReference": false
+ }
+ }
+ ],
+ "text": "test-file"
+ }
+ ]
+ }
+ ]
+ }
+08_04_07__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_group_wiki__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "link",
+ "attrs": {
+ "href": "test-file.zip",
+ "target": "_blank",
+ "class": null,
+ "title": null,
+ "canonicalSrc": "test-file.zip",
+ "isReference": false
+ }
+ }
+ ],
+ "text": "test-file"
+ }
+ ]
+ }
+ ]
+ }
+08_04_08__gitlab_internal_extension_markdown__migrated_golden_master_examples__audio__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "audio",
+ "attrs": {
+ "uploading": false,
+ "src": "https://gitlab.com/gitlab.mp3",
+ "canonicalSrc": "https://gitlab.com/gitlab.mp3",
+ "alt": "Sample Audio"
+ }
+ }
+ ]
+ }
+ ]
+ }
+08_04_09__gitlab_internal_extension_markdown__migrated_golden_master_examples__audio_and_video_in_lists__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "bulletList",
+ "attrs": {
+ "bullet": "*"
+ },
+ "content": [
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "audio",
+ "attrs": {
+ "uploading": false,
+ "src": "https://gitlab.com/1.mp3",
+ "canonicalSrc": "https://gitlab.com/1.mp3",
+ "alt": "Sample Audio"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "video",
+ "attrs": {
+ "uploading": false,
+ "src": "https://gitlab.com/2.mp4",
+ "canonicalSrc": "https://gitlab.com/2.mp4",
+ "alt": "Sample Video"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "orderedList",
+ "attrs": {
+ "start": 1,
+ "parens": false
+ },
+ "content": [
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "video",
+ "attrs": {
+ "uploading": false,
+ "src": "https://gitlab.com/1.mp4",
+ "canonicalSrc": "https://gitlab.com/1.mp4",
+ "alt": "Sample Video"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "audio",
+ "attrs": {
+ "uploading": false,
+ "src": "https://gitlab.com/2.mp3",
+ "canonicalSrc": "https://gitlab.com/2.mp3",
+ "alt": "Sample Audio"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "taskList",
+ "attrs": {
+ "numeric": false,
+ "start": 1,
+ "parens": false
+ },
+ "content": [
+ {
+ "type": "taskItem",
+ "attrs": {
+ "checked": true
+ },
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "audio",
+ "attrs": {
+ "uploading": false,
+ "src": "https://gitlab.com/1.mp3",
+ "canonicalSrc": "https://gitlab.com/1.mp3",
+ "alt": "Sample Audio"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "taskItem",
+ "attrs": {
+ "checked": true
+ },
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "audio",
+ "attrs": {
+ "uploading": false,
+ "src": "https://gitlab.com/2.mp3",
+ "canonicalSrc": "https://gitlab.com/2.mp3",
+ "alt": "Sample Audio"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "taskItem",
+ "attrs": {
+ "checked": true
+ },
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "video",
+ "attrs": {
+ "uploading": false,
+ "src": "https://gitlab.com/3.mp4",
+ "canonicalSrc": "https://gitlab.com/3.mp4",
+ "alt": "Sample Video"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+08_04_10__gitlab_internal_extension_markdown__migrated_golden_master_examples__blockquote__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "blockquote",
+ "attrs": {
+ "multiline": false
+ },
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "This is a blockquote"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "This is another one"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+08_04_11__gitlab_internal_extension_markdown__migrated_golden_master_examples__bold__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "bold"
+ }
+ ],
+ "text": "bold"
+ }
+ ]
+ }
+ ]
+ }
+08_04_12__gitlab_internal_extension_markdown__migrated_golden_master_examples__bullet_list_style_1__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "bulletList",
+ "attrs": {
+ "bullet": "*"
+ },
+ "content": [
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "list item 1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "list item 2"
+ }
+ ]
+ },
+ {
+ "type": "bulletList",
+ "attrs": {
+ "bullet": "*"
+ },
+ "content": [
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "embedded list item 3"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+08_04_13__gitlab_internal_extension_markdown__migrated_golden_master_examples__bullet_list_style_2__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "bulletList",
+ "attrs": {
+ "bullet": "*"
+ },
+ "content": [
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "list item 1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "list item 2"
+ }
+ ]
+ },
+ {
+ "type": "bulletList",
+ "attrs": {
+ "bullet": "*"
+ },
+ "content": [
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "embedded list item 3"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+08_04_14__gitlab_internal_extension_markdown__migrated_golden_master_examples__bullet_list_style_3__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "bulletList",
+ "attrs": {
+ "bullet": "*"
+ },
+ "content": [
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "list item 1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "list item 2"
+ }
+ ]
+ },
+ {
+ "type": "bulletList",
+ "attrs": {
+ "bullet": "*"
+ },
+ "content": [
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "embedded list item 3"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+08_04_15__gitlab_internal_extension_markdown__migrated_golden_master_examples__code_block_javascript__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "codeBlock",
+ "attrs": {
+ "language": "javascript",
+ "class": "code highlight",
+ "langParams": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": " console.log('hello world')"
+ }
+ ]
+ }
+ ]
+ }
+08_04_16__gitlab_internal_extension_markdown__migrated_golden_master_examples__code_block_plaintext__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "codeBlock",
+ "attrs": {
+ "language": null,
+ "class": "code highlight",
+ "langParams": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": " plaintext"
+ }
+ ]
+ }
+ ]
+ }
+08_04_17__gitlab_internal_extension_markdown__migrated_golden_master_examples__code_block_unknown__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "codeBlock",
+ "attrs": {
+ "language": "foobar",
+ "class": "code highlight",
+ "langParams": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": " custom_language = >> this <<"
+ }
+ ]
+ }
+ ]
+ }
+08_04_18__gitlab_internal_extension_markdown__migrated_golden_master_examples__color_chips__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "bulletList",
+ "attrs": {
+ "bullet": "*"
+ },
+ "content": [
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "code"
+ }
+ ],
+ "text": "#F00"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "code"
+ }
+ ],
+ "text": "#F00A"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "code"
+ }
+ ],
+ "text": "#FF0000"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "code"
+ }
+ ],
+ "text": "#FF0000AA"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "code"
+ }
+ ],
+ "text": "RGB(0,255,0)"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "code"
+ }
+ ],
+ "text": "RGB(0%,100%,0%)"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "code"
+ }
+ ],
+ "text": "RGBA(0,255,0,0.3)"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "code"
+ }
+ ],
+ "text": "HSL(540,70%,50%)"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "code"
+ }
+ ],
+ "text": "HSLA(540,70%,50%,0.3)"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+08_04_19__gitlab_internal_extension_markdown__migrated_golden_master_examples__description_list__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph"
+ }
+ ]
+ }
+08_04_20__gitlab_internal_extension_markdown__migrated_golden_master_examples__details__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph"
+ }
+ ]
+ }
+08_04_21__gitlab_internal_extension_markdown__migrated_golden_master_examples__diagram_kroki_nomnoml__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "codeBlock",
+ "attrs": {
+ "language": "nomnoml",
+ "class": "code highlight",
+ "langParams": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": " #stroke: #a86128\n [<frame>Decorator pattern|\n [<abstract>Component||+ operation()]\n [Client] depends --> [Component]\n [Decorator|- next: Component]\n [Decorator] decorates -- [ConcreteComponent]\n [Component] <:- [Decorator]\n [Component] <:- [ConcreteComponent]\n ]"
+ }
+ ]
+ }
+ ]
+ }
+08_04_22__gitlab_internal_extension_markdown__migrated_golden_master_examples__diagram_plantuml__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "diagram",
+ "attrs": {
+ "language": "plantuml",
+ "isDiagram": true,
+ "showPreview": true
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": " Alice -> Bob: Authentication Request\n Bob --> Alice: Authentication Response\n\n Alice -> Bob: Another authentication Request\n Alice <-- Bob: Another authentication Response"
+ }
+ ]
+ }
+ ]
+ }
+08_04_23__gitlab_internal_extension_markdown__migrated_golden_master_examples__diagram_plantuml_unicode__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "diagram",
+ "attrs": {
+ "language": "plantuml",
+ "isDiagram": true,
+ "showPreview": true
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "A -> B : Text with norwegian characters: æøå"
+ }
+ ]
+ }
+ ]
+ }
+08_04_24__gitlab_internal_extension_markdown__migrated_golden_master_examples__div__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "div",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "plain text"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "div",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "just a plain ol' div, not much to "
+ },
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "italic"
+ }
+ ],
+ "text": "expect"
+ },
+ {
+ "type": "text",
+ "text": "!"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+08_04_25__gitlab_internal_extension_markdown__migrated_golden_master_examples__emoji__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": ":sparkles: :heart: :100:"
+ }
+ ]
+ }
+ ]
+ }
+08_04_26__gitlab_internal_extension_markdown__migrated_golden_master_examples__emphasis__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "italic"
+ }
+ ],
+ "text": "emphasized text"
+ }
+ ]
+ }
+ ]
+ }
+08_04_27__gitlab_internal_extension_markdown__migrated_golden_master_examples__figure__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph"
+ }
+ ]
+ }
+08_04_28__gitlab_internal_extension_markdown__migrated_golden_master_examples__footnotes__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "A footnote reference tag looks like this: "
+ },
+ {
+ "type": "footnoteReference",
+ "attrs": {
+ "identifier": "1",
+ "label": "1"
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "This reference tag is a mix of letters and numbers. "
+ },
+ {
+ "type": "footnoteReference",
+ "attrs": {
+ "identifier": "footnote",
+ "label": "footnote"
+ }
+ }
+ ]
+ },
+ {
+ "type": "footnoteDefinition",
+ "attrs": {
+ "identifier": "1",
+ "label": "1"
+ },
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "This is the text inside a footnote."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "footnoteDefinition",
+ "attrs": {
+ "identifier": "footnote",
+ "label": "footnote"
+ },
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "This is another footnote."
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+08_04_29__gitlab_internal_extension_markdown__migrated_golden_master_examples__frontmatter_json__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "frontmatter",
+ "attrs": {
+ "language": "json",
+ "class": "code highlight",
+ "langParams": null,
+ "isFrontmatter": true
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "{\n \"title\": \"Page title\"\n}"
+ }
+ ]
+ }
+ ]
+ }
+08_04_30__gitlab_internal_extension_markdown__migrated_golden_master_examples__frontmatter_toml__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "frontmatter",
+ "attrs": {
+ "language": "toml",
+ "class": "code highlight",
+ "langParams": null,
+ "isFrontmatter": true
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "title = \"Page title\""
+ }
+ ]
+ }
+ ]
+ }
+08_04_31__gitlab_internal_extension_markdown__migrated_golden_master_examples__frontmatter_yaml__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "frontmatter",
+ "attrs": {
+ "language": "yaml",
+ "class": "code highlight",
+ "langParams": null,
+ "isFrontmatter": true
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "title: Page title"
+ }
+ ]
+ }
+ ]
+ }
+08_04_32__gitlab_internal_extension_markdown__migrated_golden_master_examples__hard_break__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "This is a line after a"
+ },
+ {
+ "type": "hardBreak"
+ },
+ {
+ "type": "text",
+ "text": "\nhard break"
+ }
+ ]
+ }
+ ]
+ }
+08_04_33__gitlab_internal_extension_markdown__migrated_golden_master_examples__headings__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "Heading 1"
+ }
+ ]
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 2
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "Heading 2"
+ }
+ ]
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 3
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "Heading 3"
+ }
+ ]
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 4
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "Heading 4"
+ }
+ ]
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 5
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "Heading 5"
+ }
+ ]
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 6
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "Heading 6"
+ }
+ ]
+ }
+ ]
+ }
+08_04_34__gitlab_internal_extension_markdown__migrated_golden_master_examples__horizontal_rule__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "horizontalRule"
+ }
+ ]
+ }
+08_04_35__gitlab_internal_extension_markdown__migrated_golden_master_examples__html_marks__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "bulletList",
+ "attrs": {
+ "bullet": "*"
+ },
+ "content": [
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "Content editor is"
+ },
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "strike"
+ }
+ ],
+ "text": "great"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "If the changes"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": ", please"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "The English song"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": " looks like this in Hebrew:"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": ". In the computer's memory, this is stored as"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": " by Edvard Munch. Painted in 1893."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": " is the standard markup language for creating web pages."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "Do not forget to buy"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": " today."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "This is a paragraph and"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "The concert starts at"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": " and you'll be able to enjoy the band for at least"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "Press"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": " +"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": " to copy text (Windows)."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "WWF's goal is to:"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": " We hope they succeed."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "The error occurred was:"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "The area of a triangle is: 1/2 x"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": " x"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": ", where"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": " is the base, and"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": " is the vertical height."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph"
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "C"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "H"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": " + O"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": " \t CO"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": " + H"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "O"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "The"
+ },
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "bold"
+ }
+ ],
+ "text": "Pythagorean theorem"
+ },
+ {
+ "type": "text",
+ "text": " is often expressed as"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": " +"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": " ="
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+08_04_36__gitlab_internal_extension_markdown__migrated_golden_master_examples__image__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "image",
+ "attrs": {
+ "src": "https://gitlab.com/logo.png",
+ "alt": "alt text",
+ "title": null,
+ "uploading": false,
+ "canonicalSrc": "https://gitlab.com/logo.png",
+ "width": null,
+ "height": null,
+ "isReference": false
+ }
+ }
+ ]
+ }
+ ]
+ }
+08_04_37__gitlab_internal_extension_markdown__migrated_golden_master_examples__inline_code__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "code"
+ }
+ ],
+ "text": "code"
+ }
+ ]
+ }
+ ]
+ }
+08_04_38__gitlab_internal_extension_markdown__migrated_golden_master_examples__inline_diff__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "bulletList",
+ "attrs": {
+ "bullet": "*"
+ },
+ "content": [
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "{-deleted-}"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "{+added+}"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+08_04_39__gitlab_internal_extension_markdown__migrated_golden_master_examples__label__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "~bug"
+ }
+ ]
+ }
+ ]
+ }
+08_04_40__gitlab_internal_extension_markdown__migrated_golden_master_examples__link__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "link",
+ "attrs": {
+ "href": "https://gitlab.com",
+ "target": "_blank",
+ "class": null,
+ "title": null,
+ "canonicalSrc": "https://gitlab.com",
+ "isReference": false
+ }
+ }
+ ],
+ "text": "GitLab"
+ }
+ ]
+ }
+ ]
+ }
+08_04_41__gitlab_internal_extension_markdown__migrated_golden_master_examples__math__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "This math is inline $"
+ },
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "code"
+ }
+ ],
+ "text": "a^2+b^2=c^2"
+ },
+ {
+ "type": "text",
+ "text": "$."
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "This is on a separate line:"
+ }
+ ]
+ },
+ {
+ "type": "codeBlock",
+ "attrs": {
+ "language": "math",
+ "class": "code highlight",
+ "langParams": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "a^2+b^2=c^2"
+ }
+ ]
+ }
+ ]
+ }
+08_04_42__gitlab_internal_extension_markdown__migrated_golden_master_examples__ordered_list__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "orderedList",
+ "attrs": {
+ "start": 1,
+ "parens": false
+ },
+ "content": [
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "list item 1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "list item 2"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "list item 3"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+08_04_43__gitlab_internal_extension_markdown__migrated_golden_master_examples__ordered_list_with_start_order__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "orderedList",
+ "attrs": {
+ "start": 1,
+ "parens": false
+ },
+ "content": [
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "list item 1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "list item 2"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "listItem",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "list item 3"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+08_04_45__gitlab_internal_extension_markdown__migrated_golden_master_examples__ordered_task_list_with_order__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "taskList",
+ "attrs": {
+ "numeric": true,
+ "start": 1,
+ "parens": false
+ },
+ "content": [
+ {
+ "type": "taskItem",
+ "attrs": {
+ "checked": true
+ },
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "hello"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "taskItem",
+ "attrs": {
+ "checked": true
+ },
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "world"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "taskItem",
+ "attrs": {
+ "checked": false
+ },
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "example"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+08_04_46__gitlab_internal_extension_markdown__migrated_golden_master_examples__reference_for_project_wiki__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "Hi @gfm_user - thank you for reporting this ~\"UX bug\" (#1) we hope to fix it in %1.1 as part of !1"
+ }
+ ]
+ }
+ ]
+ }
+08_04_47__gitlab_internal_extension_markdown__migrated_golden_master_examples__strike__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "strike"
+ }
+ ],
+ "text": "del"
+ }
+ ]
+ }
+ ]
+ }
+08_04_48__gitlab_internal_extension_markdown__migrated_golden_master_examples__table__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "table",
+ "attrs": {
+ "isMarkdown": null
+ },
+ "content": [
+ {
+ "type": "tableRow",
+ "content": [
+ {
+ "type": "tableHeader",
+ "attrs": {
+ "colspan": 1,
+ "rowspan": 1,
+ "colwidth": null
+ },
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "header"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "tableHeader",
+ "attrs": {
+ "colspan": 1,
+ "rowspan": 1,
+ "colwidth": null
+ },
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "header"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "tableRow",
+ "content": [
+ {
+ "type": "tableCell",
+ "attrs": {
+ "colspan": 1,
+ "rowspan": 1,
+ "colwidth": null
+ },
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "code"
+ }
+ ],
+ "text": "code"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "tableCell",
+ "attrs": {
+ "colspan": 1,
+ "rowspan": 1,
+ "colwidth": null
+ },
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "cell with "
+ },
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "bold"
+ }
+ ],
+ "text": "bold"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "tableRow",
+ "content": [
+ {
+ "type": "tableCell",
+ "attrs": {
+ "colspan": 1,
+ "rowspan": 1,
+ "colwidth": null
+ },
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "strike"
+ }
+ ],
+ "text": "strike"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "tableCell",
+ "attrs": {
+ "colspan": 1,
+ "rowspan": 1,
+ "colwidth": null
+ },
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "cell with "
+ },
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "italic"
+ }
+ ],
+ "text": "italic"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "content after table"
+ }
+ ]
+ }
+ ]
+ }
+08_04_49__gitlab_internal_extension_markdown__migrated_golden_master_examples__table_of_contents__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "tableOfContents"
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "Lorem"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "Well, that's just like... your opinion.. man."
+ }
+ ]
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 2
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "Ipsum"
+ }
+ ]
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 3
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "Dolar"
+ }
+ ]
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "Sit amit"
+ }
+ ]
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 3
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "I don't know"
+ }
+ ]
+ }
+ ]
+ }
+08_04_51__gitlab_internal_extension_markdown__migrated_golden_master_examples__video__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "video",
+ "attrs": {
+ "uploading": false,
+ "src": "https://gitlab.com/gitlab.mp4",
+ "canonicalSrc": "https://gitlab.com/gitlab.mp4",
+ "alt": "Sample Video"
+ }
+ }
+ ]
+ }
+ ]
+ }
+08_04_52__gitlab_internal_extension_markdown__migrated_golden_master_examples__word_break__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "Fernstraßenbauprivatfinanzierungsgesetz"
+ }
+ ]
+ }
+ ]
+ }
diff --git a/glfm_specification/output_example_snapshots/snapshot_spec.html b/glfm_specification/output_example_snapshots/snapshot_spec.html
index 20cfc4cf84f..080712d1b4e 100644
--- a/glfm_specification/output_example_snapshots/snapshot_spec.html
+++ b/glfm_specification/output_example_snapshots/snapshot_spec.html
@@ -1,103 +1,478 @@
-<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1:1-4:3" lang="yaml" class="code highlight js-syntax-highlight language-yaml" data-lang-params="frontmatter" v-pre="true"><code><span id="LC1" class="line" lang="yaml"><span class="na">title</span><span class="pi">:</span> <span class="s">GitLab Flavored Markdown (GLFM) Spec</span></span>
-<span id="LC2" class="line" lang="yaml"><span class="na">version</span><span class="pi">:</span> <span class="s">alpha</span></span></code></pre>
-<copy-code></copy-code>
-</div>
-<h1 data-sourcepos="5:1-5:15" dir="auto">
+<!DOCTYPE html>
+<!-- NOTE: Styling is based on the CommonMark specification template: -->
+<!-- - https://github.com/commonmark/commonmark-spec/blob/master/tools/make_spec.lua -->
+<!-- - https://github.com/commonmark/commonmark-spec/blob/master/tools/template.html -->
+<!-- -->
+<!-- NOTE: 'TODO:' comments will be followed up as task(s) on this issue: -->
+<!-- - https://gitlab.com/gitlab-org/gitlab/-/issues/361241 -->
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>GitLab Flavored Markdown Internal Extensions</title>
+ <style type="text/css">
+ body {
+ font-family: Helvetica, arial, freesans, clean, sans-serif;
+ line-height: 1.4;
+ max-width: 48em;
+ margin: auto;
+ padding: 0 0.5em 4em;
+ color: #333333;
+ background-color: #ffffff;
+ font-size: 13pt;
+ }
+
+ div#TOC ul { list-style: none; }
+ h1 {
+ font-size: 140%;
+ font-weight: bold;
+ border-top: 1px solid gray;
+ padding-top: 0.5em;
+ }
+
+ h2 {
+ font-size: 120%;
+ font-weight: bold;
+ }
+
+ h3 {
+ font-size: 110%;
+ font-weight: bold;
+ }
+
+ h4 {
+ font-size: 100%;
+ font-weight: bold;
+ }
+
+ /* NOTE: "font-weight: bold" was applied to "a.definition" class in original CommonMark */
+ /* template, but in practice it was applied to all anchors */
+ a {
+ font-weight: bold;
+ }
+
+
+ /* TODO: Format whitespace in examples. This will require preprocessing to insert spans around them. */
+ /*span.space { position: relative; }*/
+ /*span.space:after {*/
+ /* content: "·";*/
+ /* position: absolute;*/
+ /* !* create a mark that indicates a space (trick from D. Greenspan) *!*/
+ /* top: 0; bottom: 7px; left: 1px; right: 1px;*/
+ /* color: #aaaaaa;*/
+ /*}*/
+ /*@media print {*/
+ /* a.dingus { display: none; }*/
+ /*}*/
+
+ div.example {
+ overflow: hidden;
+ }
+
+ p {
+ text-align: justify;
+ }
+
+ pre {
+ padding: 0.5em;
+ margin: 0.2em 0 0.5em;
+ font-size: 88%;
+ }
+
+ pre {
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+ }
+
+ code {
+ font-family: monospace;
+ background-color: #d3e1e4;
+ }
+
+ pre > code {
+ background-color: transparent;
+ }
+
+ .example {
+ font-size: 0; /* hack to get width: 50% to work on inline-block */
+ padding-bottom: 6pt;
+ }
+
+ .column pre {
+ font-size: 11pt;
+ padding: 2pt 6pt;
+ }
+
+ div.examplenum {
+ font-size: 11pt;
+ text-align: left;
+ margin-bottom: 10px;
+ }
+
+ div.column {
+ display: inline-block;
+ width: 50%;
+ vertical-align: top;
+ }
+
+ div.example > div:nth-child(2) {
+ clear: left;
+ background-color: #d3e1e4;
+ }
+
+ div.example > div:nth-child(3) {
+ clear: right;
+ background-color: #c9cace;
+ }
+
+ @media print {
+ @page {
+ size: auto;
+ margin: 1.2in 1.2in 1.2in 1.2in;
+ }
+
+ body {
+ margin: 0;
+ line-height: 1.2;
+ font-size: 10pt;
+ }
+
+ .column pre {
+ font-size: 9pt;
+ }
+
+ div.examplenum {
+ font-size: 9pt;
+ }
+ }
+ </style>
+ <!-- TODO: Extract this javascript out to a separate file and unit test it -->
+ <script type="text/javascript">
+ /* NOTE: The following code performs many of the pre-processing steps originally handled */
+ /* in https://github.com/commonmark/commonmark-spec/blob/master/tools/make_spec.lua */
+
+ /* Adds a div.example wrapper around each pair of example code blocks. */
+ function addAttributesToExampleWrapperDivs() {
+ const exampleAnchorTags = document.querySelectorAll("a[href^=\"#example-\"]");
+ for (const exampleAnchorTag of exampleAnchorTags) {
+ const examplenumDiv = exampleAnchorTag.parentElement;
+ examplenumDiv.classList.add("examplenum");
+ const exampleDiv = examplenumDiv.parentElement;
+ exampleDiv.classList.add("example");
+ exampleDiv.id = exampleAnchorTag.getAttribute("href").substring(1);
+ }
+ }
+
+ function addColumnClassToMarkdownDivs() {
+ const markdownCodeBlockDivs = document.querySelectorAll("div.markdown-code-block");
+ for (const markdownCodeBlockDiv of markdownCodeBlockDivs) {
+ markdownCodeBlockDiv.classList.add("column");
+ }
+ }
+
+ function addNumbersToHeaders() {
+ const headers = document.querySelectorAll('h1,h2,h3');
+ let h1Index = -1; // NOTE: -1 because we don't assign a number to the title
+ let h2Index = 0;
+ let h3Index = 0;
+ const tocEntries = [];
+ for (const header of headers) {
+ if (h1Index === -1) {
+ h1Index++;
+ continue;
+ }
+
+ const originalHeaderTextContent = header.textContent.trim();
+ const headerAnchor = originalHeaderTextContent.toLowerCase().replaceAll(' ', '-');
+ header.id = headerAnchor;
+ let indent;
+ let headerTextContent;
+ if (header.tagName === 'H1') {
+ h1Index++;
+ h2Index = 0;
+ h3Index = 0;
+ header.textContent = headerTextContent = h1Index + ' ' + originalHeaderTextContent;
+ indent = 0;
+ } else if (header.tagName === 'H2') {
+ h2Index++;
+ h3Index = 0;
+ header.textContent =
+ headerTextContent = h1Index + '.' + h2Index + ' ' + originalHeaderTextContent;
+ indent = 1;
+ } else if (header.tagName === 'H3') {
+ h3Index++;
+ header.textContent = headerTextContent =
+ h1Index + '.' + h2Index + '.' + h3Index + ' ' + originalHeaderTextContent;
+ indent = 2;
+ }
+ tocEntries.push({headerAnchor, headerTextContent, indent});
+ }
+ }
+
+ document.addEventListener("DOMContentLoaded", function(_event) {
+ addAttributesToExampleWrapperDivs();
+ addColumnClassToMarkdownDivs();
+ const tocEntries = addNumbersToHeaders();
+ addToc(tocEntries);
+ });
+
+ /* NOTE: The following code is to support the "Try it" interactive "dingus", which */
+ /* we do not yet support. But it is being left here for comparison context with the */
+ /* original CommonMark template. */
+ // $$(document).ready(function() {
+ // $$("div.example").each(function(e) {
+ // var t = $$(this).find('code.language-markdown').text();
+ // $$(this).find('a.dingus').click(function(f) {
+ // window.open('/dingus/?text=' +
+ // encodeURIComponent(t.replace(/→/g,"\t")));
+ // });
+ // });
+ // $$("code.language-markdown").dblclick(function(e) { window.open('/dingus/?text=' +
+ // encodeURIComponent($$(this).text()));
+ // });
+ // });
+ </script>
+</head>
+<body>
+<h1 class="title">GitLab Flavored Markdown Internal Extensions</h1>
+<div class="version">Version alpha</div>
+
+<ul class="section-nav">
+<li>
+<a href="#preliminaries">Preliminaries</a><ul>
+<li><a href="#characters-and-lines">Characters and lines</a></li>
+<li><a href="#tabs">Tabs</a></li>
+<li><a href="#insecure-characters">Insecure characters</a></li>
+</ul>
+</li>
+<li>
+<a href="#blocks-and-inlines">Blocks and inlines</a><ul>
+<li><a href="#precedence">Precedence</a></li>
+<li><a href="#container-blocks-and-leaf-blocks">Container blocks and leaf blocks</a></li>
+</ul>
+</li>
+<li>
+<a href="#leaf-blocks">Leaf blocks</a><ul>
+<li><a href="#thematic-breaks">Thematic breaks</a></li>
+<li><a href="#atx-headings">ATX headings</a></li>
+<li><a href="#setext-headings">Setext headings</a></li>
+<li><a href="#indented-code-blocks">Indented code blocks</a></li>
+<li><a href="#fenced-code-blocks">Fenced code blocks</a></li>
+<li><a href="#html-blocks">HTML blocks</a></li>
+<li><a href="#link-reference-definitions">Link reference definitions</a></li>
+<li><a href="#paragraphs">Paragraphs</a></li>
+<li><a href="#blank-lines">Blank lines</a></li>
+<li><a href="#tables-extension">Tables (extension)</a></li>
+</ul>
+</li>
+<li>
+<a href="#container-blocks">Container blocks</a><ul>
+<li><a href="#block-quotes">Block quotes</a></li>
+<li>
+<a href="#list-items">List items</a><ul><li><a href="#motivation">Motivation</a></li></ul>
+</li>
+<li><a href="#task-list-items-extension">Task list items (extension)</a></li>
+<li><a href="#lists">Lists</a></li>
+</ul>
+</li>
+<li>
+<a href="#inlines">Inlines</a><ul>
+<li><a href="#backslash-escapes">Backslash escapes</a></li>
+<li><a href="#entity-and-numeric-character-references">Entity and numeric character references</a></li>
+<li><a href="#code-spans">Code spans</a></li>
+<li><a href="#emphasis-and-strong-emphasis">Emphasis and strong emphasis</a></li>
+<li><a href="#strikethrough-extension">Strikethrough (extension)</a></li>
+<li><a href="#links">Links</a></li>
+<li><a href="#images">Images</a></li>
+<li><a href="#autolinks">Autolinks</a></li>
+<li><a href="#autolinks-extension">Autolinks (extension)</a></li>
+<li><a href="#raw-html">Raw HTML</a></li>
+<li><a href="#disallowed-raw-html-extension">Disallowed Raw HTML (extension)</a></li>
+<li><a href="#hard-line-breaks">Hard line breaks</a></li>
+<li><a href="#soft-line-breaks">Soft line breaks</a></li>
+<li><a href="#textual-content">Textual content</a></li>
+</ul>
+</li>
+<li>
+<a href="#gitlab-official-specification-markdown">GitLab Official Specification Markdown</a><ul>
+<li><a href="#footnotes">Footnotes</a></li>
+<li><a href="#task-list-items">Task list items</a></li>
+<li><a href="#front-matter">Front matter</a></li>
+<li><a href="#table-of-contents">Table of contents</a></li>
+</ul>
+</li>
+<li>
+<a href="#gitlab-internal-extension-markdown">GitLab Internal Extension Markdown</a><ul>
+<li><a href="#audio">Audio</a></li>
+<li><a href="#video">Video</a></li>
+<li><a href="#markdown-preview-api-request-overrides">Markdown Preview API Request Overrides</a></li>
+<li>
+<a href="#migrated-golden-master-examples">Migrated golden master examples</a><ul>
+<li><a href="#attachment_image_for_group">attachment_image_for_group</a></li>
+<li><a href="#attachment_image_for_project">attachment_image_for_project</a></li>
+<li><a href="#attachment_image_for_project_wiki">attachment_image_for_project_wiki</a></li>
+<li><a href="#attachment_link_for_group">attachment_link_for_group</a></li>
+<li><a href="#attachment_link_for_project">attachment_link_for_project</a></li>
+<li><a href="#attachment_link_for_project_wiki">attachment_link_for_project_wiki</a></li>
+<li><a href="#attachment_link_for_group_wiki">attachment_link_for_group_wiki</a></li>
+<li><a href="#audio-1">audio</a></li>
+<li><a href="#audio_and_video_in_lists">audio_and_video_in_lists</a></li>
+<li><a href="#blockquote">blockquote</a></li>
+<li><a href="#bold">bold</a></li>
+<li><a href="#bullet_list_style_1">bullet_list_style_1</a></li>
+<li><a href="#bullet_list_style_2">bullet_list_style_2</a></li>
+<li><a href="#bullet_list_style_3">bullet_list_style_3</a></li>
+<li><a href="#code_block_javascript">code_block_javascript</a></li>
+<li><a href="#code_block_plaintext">code_block_plaintext</a></li>
+<li><a href="#code_block_unknown">code_block_unknown</a></li>
+<li><a href="#color_chips">color_chips</a></li>
+<li><a href="#description_list">description_list</a></li>
+<li><a href="#details">details</a></li>
+<li><a href="#diagram_kroki_nomnoml">diagram_kroki_nomnoml</a></li>
+<li><a href="#diagram_plantuml">diagram_plantuml</a></li>
+<li><a href="#diagram_plantuml_unicode">diagram_plantuml_unicode</a></li>
+<li><a href="#div">div</a></li>
+<li><a href="#emoji">emoji</a></li>
+<li><a href="#emphasis">emphasis</a></li>
+<li><a href="#figure">figure</a></li>
+<li><a href="#footnotes-1">footnotes</a></li>
+<li><a href="#frontmatter_json">frontmatter_json</a></li>
+<li><a href="#frontmatter_toml">frontmatter_toml</a></li>
+<li><a href="#frontmatter_yaml">frontmatter_yaml</a></li>
+<li><a href="#hard_break">hard_break</a></li>
+<li><a href="#headings">headings</a></li>
+<li><a href="#horizontal_rule">horizontal_rule</a></li>
+<li><a href="#html_marks">html_marks</a></li>
+<li><a href="#image">image</a></li>
+<li><a href="#inline_code">inline_code</a></li>
+<li><a href="#inline_diff">inline_diff</a></li>
+<li><a href="#label">label</a></li>
+<li><a href="#link">link</a></li>
+<li><a href="#math">math</a></li>
+<li><a href="#ordered_list">ordered_list</a></li>
+<li><a href="#ordered_list_with_start_order">ordered_list_with_start_order</a></li>
+<li><a href="#ordered_task_list">ordered_task_list</a></li>
+<li><a href="#ordered_task_list_with_order">ordered_task_list_with_order</a></li>
+<li><a href="#reference_for_project_wiki">reference_for_project_wiki</a></li>
+<li><a href="#strike">strike</a></li>
+<li><a href="#table">table</a></li>
+<li><a href="#table_of_contents">table_of_contents</a></li>
+<li><a href="#task_list">task_list</a></li>
+<li><a href="#video-1">video</a></li>
+<li><a href="#word_break">word_break</a></li>
+</ul>
+</li>
+<li><a href="#image-attributes">Image Attributes</a></li>
+</ul>
+</li>
+</ul>
+<h1 data-sourcepos="3:1-3:15" dir="auto">
<a id="user-content-preliminaries" class="anchor" href="#preliminaries" aria-hidden="true"></a>Preliminaries</h1>
-<h2 data-sourcepos="7:1-7:23" dir="auto">
+<h2 data-sourcepos="5:1-5:23" dir="auto">
<a id="user-content-characters-and-lines" class="anchor" href="#characters-and-lines" aria-hidden="true"></a>Characters and lines</h2>
-<p data-sourcepos="9:1-10:9" dir="auto">Any sequence of [characters] is a valid CommonMark
+<p data-sourcepos="7:1-8:9" dir="auto">Any sequence of [characters] is a valid CommonMark
document.</p>
-<p data-sourcepos="12:1-15:26" dir="auto">A <a href="@">character</a> is a Unicode code point. Although some
+<p data-sourcepos="10:1-13:26" dir="auto">A <a href="@">character</a> is a Unicode code point. Although some
code points (for example, combining accents) do not correspond to
characters in an intuitive sense, all code points count as characters
for purposes of this spec.</p>
-<p data-sourcepos="17:1-19:22" dir="auto">This spec does not specify an encoding; it thinks of lines as composed
+<p data-sourcepos="15:1-17:22" dir="auto">This spec does not specify an encoding; it thinks of lines as composed
of [characters] rather than bytes. A conforming parser may be limited
to a certain encoding.</p>
-<p data-sourcepos="21:1-23:50" dir="auto">A <a href="@">line</a> is a sequence of zero or more [characters]
+<p data-sourcepos="19:1-21:50" dir="auto">A <a href="@">line</a> is a sequence of zero or more [characters]
other than newline (<code>U+000A</code>) or carriage return (<code>U+000D</code>),
followed by a [line ending] or by the end of file.</p>
-<p data-sourcepos="25:1-27:18" dir="auto">A <a href="@">line ending</a> is a newline (<code>U+000A</code>), a carriage return
+<p data-sourcepos="23:1-25:18" dir="auto">A <a href="@">line ending</a> is a newline (<code>U+000A</code>), a carriage return
(<code>U+000D</code>) not followed by a newline, or a carriage return and a
following newline.</p>
-<p data-sourcepos="29:1-30:59" dir="auto">A line containing no characters, or a line containing only spaces
+<p data-sourcepos="27:1-28:59" dir="auto">A line containing no characters, or a line containing only spaces
(<code>U+0020</code>) or tabs (<code>U+0009</code>), is called a <a href="@">blank line</a>.</p>
-<p data-sourcepos="32:1-32:73" dir="auto">The following definitions of character classes will be used in this spec:</p>
-<p data-sourcepos="34:1-36:52" dir="auto">A <a href="@">whitespace character</a> is a space
+<p data-sourcepos="30:1-30:73" dir="auto">The following definitions of character classes will be used in this spec:</p>
+<p data-sourcepos="32:1-34:52" dir="auto">A <a href="@">whitespace character</a> is a space
(<code>U+0020</code>), tab (<code>U+0009</code>), newline (<code>U+000A</code>), line tabulation (<code>U+000B</code>),
form feed (<code>U+000C</code>), or carriage return (<code>U+000D</code>).</p>
-<p data-sourcepos="38:1-39:12" dir="auto"><a href="@">Whitespace</a> is a sequence of one or more [whitespace
+<p data-sourcepos="36:1-37:12" dir="auto"><a href="@">Whitespace</a> is a sequence of one or more [whitespace
characters].</p>
-<p data-sourcepos="41:1-44:11" dir="auto">A <a href="@">Unicode whitespace character</a> is
+<p data-sourcepos="39:1-42:11" dir="auto">A <a href="@">Unicode whitespace character</a> is
any code point in the Unicode <code>Zs</code> general category, or a tab (<code>U+0009</code>),
carriage return (<code>U+000D</code>), newline (<code>U+000A</code>), or form feed
(<code>U+000C</code>).</p>
-<p data-sourcepos="46:1-47:40" dir="auto"><a href="@">Unicode whitespace</a> is a sequence of one
+<p data-sourcepos="44:1-45:40" dir="auto"><a href="@">Unicode whitespace</a> is a sequence of one
or more [Unicode whitespace characters].</p>
-<p data-sourcepos="49:1-49:25" dir="auto">A <a href="@">space</a> is <code>U+0020</code>.</p>
-<p data-sourcepos="51:1-52:37" dir="auto">A <a href="@">non-whitespace character</a> is any character
+<p data-sourcepos="47:1-47:25" dir="auto">A <a href="@">space</a> is <code>U+0020</code>.</p>
+<p data-sourcepos="49:1-50:37" dir="auto">A <a href="@">non-whitespace character</a> is any character
that is not a [whitespace character].</p>
-<p data-sourcepos="54:1-59:38" dir="auto">An <a href="@">ASCII punctuation character</a>
+<p data-sourcepos="52:1-57:38" dir="auto">An <a href="@">ASCII punctuation character</a>
is <code>!</code>, <code>"</code>, <code>#</code>, <code>$</code>, <code>%</code>, <code>&amp;</code>, <code>'</code>, <code>(</code>, <code>)</code>,
<code>*</code>, <code>+</code>, <code>,</code>, <code>-</code>, <code>.</code>, <code>/</code> (U+0021–2F),
<code>:</code>, <code>;</code>, <code>&lt;</code>, <code>=</code>, <code>&gt;</code>, <code>?</code>, <code>@</code> (U+003A–0040),
<code>[</code>, <code>\</code>, <code>]</code>, <code>^</code>, <code>_</code>, <code>`</code> (U+005B–0060),
<code>{</code>, <code>|</code>, <code>}</code>, or <code>~</code> (U+007B–007E).</p>
-<p data-sourcepos="61:1-63:76" dir="auto">A <a href="@">punctuation character</a> is an [ASCII
+<p data-sourcepos="59:1-61:76" dir="auto">A <a href="@">punctuation character</a> is an [ASCII
punctuation character] or anything in
the general Unicode categories <code>Pc</code>, <code>Pd</code>, <code>Pe</code>, <code>Pf</code>, <code>Pi</code>, <code>Po</code>, or <code>Ps</code>.</p>
-<h2 data-sourcepos="65:1-65:7" dir="auto">
+<h2 data-sourcepos="63:1-63:7" dir="auto">
<a id="user-content-tabs" class="anchor" href="#tabs" aria-hidden="true"></a>Tabs</h2>
-<p data-sourcepos="67:1-70:16" dir="auto">Tabs in lines are not expanded to [spaces]. However,
+<p data-sourcepos="65:1-68:16" dir="auto">Tabs in lines are not expanded to [spaces]. However,
in contexts where whitespace helps to define block structure,
tabs behave as if they were replaced by spaces with a tab stop
of 4 characters.</p>
-<p data-sourcepos="72:1-75:8" dir="auto">Thus, for example, a tab can be used instead of four spaces
+<p data-sourcepos="70:1-73:8" dir="auto">Thus, for example, a tab can be used instead of four spaces
in an indented code block. (Note, however, that internal
tabs are passed through as literal tabs, not expanded to
spaces.)</p>
+<div>
+<div><a href="#example-1">Example 1</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="77:1-79:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">→foo→baz→→bim</span></code></pre>
+<pre data-sourcepos="78:1-80:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">→foo→baz→→bim</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="81:1-84:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo→baz→→bim</span>
+<pre data-sourcepos="82:1-85:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo→baz→→bim</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-2">Example 2</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="86:1-88:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> →foo→baz→→bim</span></code></pre>
+<pre data-sourcepos="91:1-93:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> →foo→baz→→bim</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="90:1-93:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo→baz→→bim</span>
+<pre data-sourcepos="95:1-98:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo→baz→→bim</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-3">Example 3</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="95:1-98:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> a→a</span>
+<pre data-sourcepos="104:1-107:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> a→a</span>
<span id="LC2" class="line" lang="plaintext"> á½â†’a</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="100:1-104:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;a→a</span>
+<pre data-sourcepos="109:1-113:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;a→a</span>
<span id="LC2" class="line" lang="plaintext">á½â†’a</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="106:1-108:38" dir="auto">In the following example, a continuation paragraph of a list
+</div>
+<p data-sourcepos="116:1-118:38" dir="auto">In the following example, a continuation paragraph of a list
item is indented with a tab; this has exactly the same effect
as indentation with four spaces would:</p>
+<div>
+<div><a href="#example-4">Example 4</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="110:1-114:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> - foo</span>
+<pre data-sourcepos="123:1-127:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> - foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">→bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="116:1-123:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="129:1-136:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span>
@@ -105,14 +480,17 @@ as indentation with four spaces would:</p>
<span id="LC6" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-5">Example 5</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="125:1-129:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
+<pre data-sourcepos="142:1-146:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">→→bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="131:1-139:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="148:1-156:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt; bar</span>
@@ -121,7 +499,8 @@ as indentation with four spaces would:</p>
<span id="LC7" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="141:1-148:36" dir="auto">Normally the <code>&gt;</code> that begins a block quote may be followed
+</div>
+<p data-sourcepos="159:1-166:36" dir="auto">Normally the <code>&gt;</code> that begins a block quote may be followed
optionally by a space, which is not considered part of the
content. In the following case <code>&gt;</code> is followed by a tab,
which is treated as if it were expanded into three spaces.
@@ -129,23 +508,28 @@ Since one of these spaces is considered part of the
delimiter, <code>foo</code> is considered to be indented six spaces
inside the block quote context, so we get an indented
code block starting with two spaces.</p>
+<div>
+<div><a href="#example-6">Example 6</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="150:1-152:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt;→→foo</span></code></pre>
+<pre data-sourcepos="171:1-173:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt;→→foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="154:1-159:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="175:1-180:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt; foo</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-7">Example 7</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="161:1-163:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">-→→foo</span></code></pre>
+<pre data-sourcepos="186:1-188:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">-→→foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="165:1-172:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="190:1-197:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt; foo</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
@@ -153,25 +537,31 @@ code block starting with two spaces.</p>
<span id="LC6" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-8">Example 8</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="175:1-178:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> foo</span>
+<pre data-sourcepos="204:1-207:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> foo</span>
<span id="LC2" class="line" lang="plaintext">→bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="180:1-184:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo</span>
+<pre data-sourcepos="209:1-213:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo</span>
<span id="LC2" class="line" lang="plaintext">bar</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-9">Example 9</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="186:1-190:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> - foo</span>
+<pre data-sourcepos="219:1-223:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> - foo</span>
<span id="LC2" class="line" lang="plaintext"> - bar</span>
<span id="LC3" class="line" lang="plaintext">→ - baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="192:1-204:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="225:1-237:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo</span>
<span id="LC3" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;bar</span>
@@ -184,52 +574,62 @@ code block starting with two spaces.</p>
<span id="LC11" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-10">Example 10</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="206:1-208:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">#→Foo</span></code></pre>
+<pre data-sourcepos="243:1-245:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">#→Foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="210:1-212:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;Foo&lt;/h1&gt;</span></code></pre>
+<pre data-sourcepos="247:1-249:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;Foo&lt;/h1&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-11">Example 11</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="214:1-216:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*→*→*→</span></code></pre>
+<pre data-sourcepos="255:1-257:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*→*→*→</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="218:1-220:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
+<pre data-sourcepos="259:1-261:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="223:1-223:22" dir="auto">
+</div>
+<h2 data-sourcepos="265:1-265:22" dir="auto">
<a id="user-content-insecure-characters" class="anchor" href="#insecure-characters" aria-hidden="true"></a>Insecure characters</h2>
-<p data-sourcepos="225:1-226:42" dir="auto">For security reasons, the Unicode character <code>U+0000</code> must be replaced
+<p data-sourcepos="267:1-268:42" dir="auto">For security reasons, the Unicode character <code>U+0000</code> must be replaced
with the REPLACEMENT CHARACTER (<code>U+FFFD</code>).</p>
-<h1 data-sourcepos="228:1-228:20" dir="auto">
+<h1 data-sourcepos="270:1-270:20" dir="auto">
<a id="user-content-blocks-and-inlines" class="anchor" href="#blocks-and-inlines" aria-hidden="true"></a>Blocks and inlines</h1>
-<p data-sourcepos="230:1-235:54" dir="auto">We can think of a document as a sequence of
+<p data-sourcepos="272:1-277:54" dir="auto">We can think of a document as a sequence of
<a href="@">blocks</a>---structural elements like paragraphs, block
quotations, lists, headings, rules, and code blocks. Some blocks (like
block quotes and list items) contain other blocks; others (like
headings and paragraphs) contain <a href="@">inline</a> content---text,
links, emphasized text, images, code spans, and so on.</p>
-<h2 data-sourcepos="237:1-237:13" dir="auto">
+<h2 data-sourcepos="279:1-279:13" dir="auto">
<a id="user-content-precedence" class="anchor" href="#precedence" aria-hidden="true"></a>Precedence</h2>
-<p data-sourcepos="239:1-241:59" dir="auto">Indicators of block structure always take precedence over indicators
+<p data-sourcepos="281:1-283:59" dir="auto">Indicators of block structure always take precedence over indicators
of inline structure. So, for example, the following is a list with
two items, not a list with one item containing a code span:</p>
+<div>
+<div><a href="#example-12">Example 12</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="243:1-246:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- `one</span>
+<pre data-sourcepos="288:1-291:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- `one</span>
<span id="LC2" class="line" lang="plaintext">- two`</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="248:1-253:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="293:1-298:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;`one&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;two`&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="256:1-263:66" dir="auto">This means that parsing can proceed in two steps: first, the block
+</div>
+<p data-sourcepos="302:1-309:66" dir="auto">This means that parsing can proceed in two steps: first, the block
structure of the document can be discerned; second, text lines inside
paragraphs, headings, and other block constructs can be parsed for inline
structure. The second step requires information about link reference
@@ -237,143 +637,181 @@ definitions that will be available only at the end of the first
step. Note that the first step requires processing lines in sequence,
but the second can be parallelized, since the inline parsing of
one block element does not affect the inline parsing of any other.</p>
-<h2 data-sourcepos="265:1-265:35" dir="auto">
+<h2 data-sourcepos="311:1-311:35" dir="auto">
<a id="user-content-container-blocks-and-leaf-blocks" class="anchor" href="#container-blocks-and-leaf-blocks" aria-hidden="true"></a>Container blocks and leaf blocks</h2>
-<p data-sourcepos="267:1-270:13" dir="auto">We can divide blocks into two types:
+<p data-sourcepos="313:1-316:13" dir="auto">We can divide blocks into two types:
<a href="@">container blocks</a>,
which can contain other blocks, and <a href="@">leaf blocks</a>,
which cannot.</p>
-<h1 data-sourcepos="272:1-272:13" dir="auto">
+<h1 data-sourcepos="318:1-318:13" dir="auto">
<a id="user-content-leaf-blocks" class="anchor" href="#leaf-blocks" aria-hidden="true"></a>Leaf blocks</h1>
-<p data-sourcepos="274:1-275:18" dir="auto">This section describes the different kinds of leaf block that make up a
+<p data-sourcepos="320:1-321:18" dir="auto">This section describes the different kinds of leaf block that make up a
Markdown document.</p>
-<h2 data-sourcepos="277:1-277:18" dir="auto">
+<h2 data-sourcepos="323:1-323:18" dir="auto">
<a id="user-content-thematic-breaks" class="anchor" href="#thematic-breaks" aria-hidden="true"></a>Thematic breaks</h2>
-<p data-sourcepos="279:1-282:20" dir="auto">A line consisting of 0-3 spaces of indentation, followed by a sequence
+<p data-sourcepos="325:1-328:20" dir="auto">A line consisting of 0-3 spaces of indentation, followed by a sequence
of three or more matching <code>-</code>, <code>_</code>, or <code>*</code> characters, each followed
optionally by any number of spaces or tabs, forms a
<a href="@">thematic break</a>.</p>
+<div>
+<div><a href="#example-13">Example 13</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="284:1-288:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">***</span>
+<pre data-sourcepos="333:1-337:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">***</span>
<span id="LC2" class="line" lang="plaintext">---</span>
<span id="LC3" class="line" lang="plaintext">___</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="290:1-294:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span>
+<pre data-sourcepos="339:1-343:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;hr /&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="297:1-297:17" dir="auto">Wrong characters:</p>
+</div>
+<p data-sourcepos="347:1-347:17" dir="auto">Wrong characters:</p>
+<div>
+<div><a href="#example-14">Example 14</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="299:1-301:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">+++</span></code></pre>
+<pre data-sourcepos="352:1-354:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">+++</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="303:1-305:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;+++&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="356:1-358:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;+++&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-15">Example 15</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="308:1-310:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">===</span></code></pre>
+<pre data-sourcepos="365:1-367:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">===</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="312:1-314:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;===&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="369:1-371:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;===&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="317:1-317:22" dir="auto">Not enough characters:</p>
+</div>
+<p data-sourcepos="375:1-375:22" dir="auto">Not enough characters:</p>
+<div>
+<div><a href="#example-16">Example 16</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="319:1-323:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">--</span>
+<pre data-sourcepos="380:1-384:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">--</span>
<span id="LC2" class="line" lang="plaintext">**</span>
<span id="LC3" class="line" lang="plaintext">__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="325:1-329:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;--</span>
+<pre data-sourcepos="386:1-390:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;--</span>
<span id="LC2" class="line" lang="plaintext">**</span>
<span id="LC3" class="line" lang="plaintext">__&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="332:1-332:39" dir="auto">One to three spaces indent are allowed:</p>
+</div>
+<p data-sourcepos="394:1-394:39" dir="auto">One to three spaces indent are allowed:</p>
+<div>
+<div><a href="#example-17">Example 17</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="334:1-338:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ***</span>
+<pre data-sourcepos="399:1-403:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ***</span>
<span id="LC2" class="line" lang="plaintext"> ***</span>
<span id="LC3" class="line" lang="plaintext"> ***</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="340:1-344:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span>
+<pre data-sourcepos="405:1-409:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;hr /&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="347:1-347:24" dir="auto">Four spaces is too many:</p>
+</div>
+<p data-sourcepos="413:1-413:24" dir="auto">Four spaces is too many:</p>
+<div>
+<div><a href="#example-18">Example 18</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="349:1-351:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ***</span></code></pre>
+<pre data-sourcepos="418:1-420:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ***</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="353:1-356:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;***</span>
+<pre data-sourcepos="422:1-425:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;***</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-19">Example 19</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="359:1-362:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="432:1-435:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext"> ***</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="364:1-367:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
+<pre data-sourcepos="437:1-440:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
<span id="LC2" class="line" lang="plaintext">***&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="370:1-370:39" dir="auto">More than three characters may be used:</p>
+</div>
+<p data-sourcepos="444:1-444:39" dir="auto">More than three characters may be used:</p>
+<div>
+<div><a href="#example-20">Example 20</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="372:1-374:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_____________________________________</span></code></pre>
+<pre data-sourcepos="449:1-451:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_____________________________________</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="376:1-378:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
+<pre data-sourcepos="453:1-455:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="381:1-381:42" dir="auto">Spaces are allowed between the characters:</p>
+</div>
+<p data-sourcepos="459:1-459:42" dir="auto">Spaces are allowed between the characters:</p>
+<div>
+<div><a href="#example-21">Example 21</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="383:1-385:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> - - -</span></code></pre>
+<pre data-sourcepos="464:1-466:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> - - -</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="387:1-389:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
+<pre data-sourcepos="468:1-470:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-22">Example 22</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="392:1-394:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ** * ** * ** * **</span></code></pre>
+<pre data-sourcepos="477:1-479:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ** * ** * ** * **</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="396:1-398:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
+<pre data-sourcepos="481:1-483:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-23">Example 23</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="401:1-403:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- - - -</span></code></pre>
+<pre data-sourcepos="490:1-492:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- - - -</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="405:1-407:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
+<pre data-sourcepos="494:1-496:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="410:1-410:30" dir="auto">Spaces are allowed at the end:</p>
+</div>
+<p data-sourcepos="500:1-500:30" dir="auto">Spaces are allowed at the end:</p>
+<div>
+<div><a href="#example-24">Example 24</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="412:1-414:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- - - - </span></code></pre>
+<pre data-sourcepos="505:1-507:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- - - - </span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="416:1-418:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
+<pre data-sourcepos="509:1-511:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="421:1-421:51" dir="auto">However, no other characters may occur in the line:</p>
+</div>
+<p data-sourcepos="515:1-515:51" dir="auto">However, no other characters may occur in the line:</p>
+<div>
+<div><a href="#example-25">Example 25</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="423:1-429:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_ _ _ _ a</span>
+<pre data-sourcepos="520:1-526:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_ _ _ _ a</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">a------</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -381,30 +819,36 @@ optionally by any number of spaces or tabs, forms a
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="431:1-435:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_ _ _ _ a&lt;/p&gt;</span>
+<pre data-sourcepos="528:1-532:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_ _ _ _ a&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;a------&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;---a---&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="438:1-439:33" dir="auto">It is required that all of the [non-whitespace characters] be the same.
+</div>
+<p data-sourcepos="536:1-537:33" dir="auto">It is required that all of the [non-whitespace characters] be the same.
So, this is not a thematic break:</p>
+<div>
+<div><a href="#example-26">Example 26</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="441:1-443:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> *-*</span></code></pre>
+<pre data-sourcepos="542:1-544:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> *-*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="445:1-447:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;-&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="546:1-548:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;-&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="450:1-450:56" dir="auto">Thematic breaks do not need blank lines before or after:</p>
+</div>
+<p data-sourcepos="552:1-552:56" dir="auto">Thematic breaks do not need blank lines before or after:</p>
+<div>
+<div><a href="#example-27">Example 27</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="452:1-456:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
+<pre data-sourcepos="557:1-561:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
<span id="LC2" class="line" lang="plaintext">***</span>
<span id="LC3" class="line" lang="plaintext">- bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="458:1-466:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="563:1-571:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;hr /&gt;</span>
@@ -413,45 +857,54 @@ So, this is not a thematic break:</p>
<span id="LC7" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="469:1-469:42" dir="auto">Thematic breaks can interrupt a paragraph:</p>
+</div>
+<p data-sourcepos="575:1-575:42" dir="auto">Thematic breaks can interrupt a paragraph:</p>
+<div>
+<div><a href="#example-28">Example 28</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="471:1-475:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="580:1-584:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext">***</span>
<span id="LC3" class="line" lang="plaintext">bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="477:1-481:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo&lt;/p&gt;</span>
+<pre data-sourcepos="586:1-590:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;hr /&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="484:1-488:71" dir="auto">If a line of dashes that meets the above conditions for being a
+</div>
+<p data-sourcepos="594:1-598:71" dir="auto">If a line of dashes that meets the above conditions for being a
thematic break could also be interpreted as the underline of a [setext
heading], the interpretation as a
[setext heading] takes precedence. Thus, for example,
this is a setext heading, not a paragraph followed by a thematic break:</p>
+<div>
+<div><a href="#example-29">Example 29</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="490:1-494:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="603:1-607:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext">---</span>
<span id="LC3" class="line" lang="plaintext">bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="496:1-499:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;Foo&lt;/h2&gt;</span>
+<pre data-sourcepos="609:1-612:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;Foo&lt;/h2&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="502:1-503:63" dir="auto">When both a thematic break and a list item are possible
+</div>
+<p data-sourcepos="616:1-617:63" dir="auto">When both a thematic break and a list item are possible
interpretations of a line, the thematic break takes precedence:</p>
+<div>
+<div><a href="#example-30">Example 30</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="505:1-509:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* Foo</span>
+<pre data-sourcepos="622:1-626:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* Foo</span>
<span id="LC2" class="line" lang="plaintext">* * *</span>
<span id="LC3" class="line" lang="plaintext">* Bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="511:1-519:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="628:1-636:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;Foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;hr /&gt;</span>
@@ -460,14 +913,17 @@ interpretations of a line, the thematic break takes precedence:</p>
<span id="LC7" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="522:1-522:68" dir="auto">If you want a thematic break in a list item, use a different bullet:</p>
+</div>
+<p data-sourcepos="640:1-640:68" dir="auto">If you want a thematic break in a list item, use a different bullet:</p>
+<div>
+<div><a href="#example-31">Example 31</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="524:1-527:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- Foo</span>
+<pre data-sourcepos="645:1-648:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- Foo</span>
<span id="LC2" class="line" lang="plaintext">- * * *</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="529:1-536:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="650:1-657:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;Foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;hr /&gt;</span>
@@ -475,9 +931,10 @@ interpretations of a line, the thematic break takes precedence:</p>
<span id="LC6" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="539:1-539:15" dir="auto">
+</div>
+<h2 data-sourcepos="661:1-661:15" dir="auto">
<a id="user-content-atx-headings" class="anchor" href="#atx-headings" aria-hidden="true"></a>ATX headings</h2>
-<p data-sourcepos="541:1-551:35" dir="auto">An <a href="@">ATX heading</a>
+<p data-sourcepos="663:1-673:35" dir="auto">An <a href="@">ATX heading</a>
consists of a string of characters, parsed as inline content, between an
opening sequence of 1--6 unescaped <code>#</code> characters and an optional
closing sequence of any number of unescaped <code>#</code> characters.
@@ -488,9 +945,11 @@ preceded by a [space] and may be followed by spaces only. The opening
heading are stripped of leading and trailing spaces before being parsed
as inline content. The heading level is equal to the number of <code>#</code>
characters in the opening sequence.</p>
-<p data-sourcepos="553:1-553:16" dir="auto">Simple headings:</p>
+<p data-sourcepos="675:1-675:16" dir="auto">Simple headings:</p>
+<div>
+<div><a href="#example-32">Example 32</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="555:1-562:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"># foo</span>
+<pre data-sourcepos="680:1-687:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"># foo</span>
<span id="LC2" class="line" lang="plaintext">## foo</span>
<span id="LC3" class="line" lang="plaintext">### foo</span>
<span id="LC4" class="line" lang="plaintext">#### foo</span>
@@ -499,7 +958,7 @@ characters in the opening sequence.</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="564:1-571:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;foo&lt;/h1&gt;</span>
+<pre data-sourcepos="689:1-696:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;foo&lt;/h1&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h2&gt;foo&lt;/h2&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;h3&gt;foo&lt;/h3&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;h4&gt;foo&lt;/h4&gt;</span>
@@ -507,200 +966,252 @@ characters in the opening sequence.</p>
<span id="LC6" class="line" lang="plaintext">&lt;h6&gt;foo&lt;/h6&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="574:1-574:46" dir="auto">More than six <code>#</code> characters is not a heading:</p>
+</div>
+<p data-sourcepos="700:1-700:46" dir="auto">More than six <code>#</code> characters is not a heading:</p>
+<div>
+<div><a href="#example-33">Example 33</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="576:1-578:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">####### foo</span></code></pre>
+<pre data-sourcepos="705:1-707:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">####### foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="580:1-582:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;####### foo&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="709:1-711:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;####### foo&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="585:1-591:9" dir="auto">At least one space is required between the <code>#</code> characters and the
+</div>
+<p data-sourcepos="715:1-721:9" dir="auto">At least one space is required between the <code>#</code> characters and the
heading's contents, unless the heading is empty. Note that many
implementations currently do not require the space. However, the
space was required by the
<a href="http://www.aaronsw.com/2002/atx/atx.py" rel="nofollow noreferrer noopener" target="_blank">original ATX implementation</a>,
and it helps prevent things like the following from being parsed as
headings:</p>
+<div>
+<div><a href="#example-34">Example 34</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="593:1-597:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">#5 bolt</span>
+<pre data-sourcepos="726:1-730:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">#5 bolt</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">#hashtag</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="599:1-602:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;#5 bolt&lt;/p&gt;</span>
+<pre data-sourcepos="732:1-735:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;#5 bolt&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;#hashtag&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="605:1-605:56" dir="auto">This is not a heading, because the first <code>#</code> is escaped:</p>
+</div>
+<p data-sourcepos="739:1-739:56" dir="auto">This is not a heading, because the first <code>#</code> is escaped:</p>
+<div>
+<div><a href="#example-35">Example 35</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="607:1-609:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">\## foo</span></code></pre>
+<pre data-sourcepos="744:1-746:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">\## foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="611:1-613:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;## foo&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="748:1-750:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;## foo&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="616:1-616:31" dir="auto">Contents are parsed as inlines:</p>
+</div>
+<p data-sourcepos="754:1-754:31" dir="auto">Contents are parsed as inlines:</p>
+<div>
+<div><a href="#example-36">Example 36</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="618:1-620:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"># foo *bar* \*baz\*</span></code></pre>
+<pre data-sourcepos="759:1-761:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"># foo *bar* \*baz\*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="622:1-624:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;foo &lt;em&gt;bar&lt;/em&gt; *baz*&lt;/h1&gt;</span></code></pre>
+<pre data-sourcepos="763:1-765:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;foo &lt;em&gt;bar&lt;/em&gt; *baz*&lt;/h1&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="627:1-627:71" dir="auto">Leading and trailing [whitespace] is ignored in parsing inline content:</p>
+</div>
+<p data-sourcepos="769:1-769:71" dir="auto">Leading and trailing [whitespace] is ignored in parsing inline content:</p>
+<div>
+<div><a href="#example-37">Example 37</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="629:1-631:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"># foo </span></code></pre>
+<pre data-sourcepos="774:1-776:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"># foo </span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="633:1-635:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;foo&lt;/h1&gt;</span></code></pre>
+<pre data-sourcepos="778:1-780:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;foo&lt;/h1&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="638:1-638:44" dir="auto">One to three spaces indentation are allowed:</p>
+</div>
+<p data-sourcepos="784:1-784:44" dir="auto">One to three spaces indentation are allowed:</p>
+<div>
+<div><a href="#example-38">Example 38</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="640:1-644:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ### foo</span>
+<pre data-sourcepos="789:1-793:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ### foo</span>
<span id="LC2" class="line" lang="plaintext"> ## foo</span>
<span id="LC3" class="line" lang="plaintext"> # foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="646:1-650:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h3&gt;foo&lt;/h3&gt;</span>
+<pre data-sourcepos="795:1-799:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h3&gt;foo&lt;/h3&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h2&gt;foo&lt;/h2&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;h1&gt;foo&lt;/h1&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="653:1-653:25" dir="auto">Four spaces are too much:</p>
+</div>
+<p data-sourcepos="803:1-803:25" dir="auto">Four spaces are too much:</p>
+<div>
+<div><a href="#example-39">Example 39</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="655:1-657:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> # foo</span></code></pre>
+<pre data-sourcepos="808:1-810:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> # foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="659:1-662:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;# foo</span>
+<pre data-sourcepos="812:1-815:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;# foo</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-40">Example 40</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="665:1-668:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo</span>
+<pre data-sourcepos="822:1-825:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo</span>
<span id="LC2" class="line" lang="plaintext"> # bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="670:1-673:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo</span>
+<pre data-sourcepos="827:1-830:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo</span>
<span id="LC2" class="line" lang="plaintext"># bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="676:1-676:49" dir="auto">A closing sequence of <code>#</code> characters is optional:</p>
+</div>
+<p data-sourcepos="834:1-834:49" dir="auto">A closing sequence of <code>#</code> characters is optional:</p>
+<div>
+<div><a href="#example-41">Example 41</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="678:1-681:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">## foo ##</span>
+<pre data-sourcepos="839:1-842:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">## foo ##</span>
<span id="LC2" class="line" lang="plaintext"> ### bar ###</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="683:1-686:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;foo&lt;/h2&gt;</span>
+<pre data-sourcepos="844:1-847:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;foo&lt;/h2&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h3&gt;bar&lt;/h3&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="689:1-689:55" dir="auto">It need not be the same length as the opening sequence:</p>
+</div>
+<p data-sourcepos="851:1-851:55" dir="auto">It need not be the same length as the opening sequence:</p>
+<div>
+<div><a href="#example-42">Example 42</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="691:1-694:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"># foo ##################################</span>
+<pre data-sourcepos="856:1-859:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"># foo ##################################</span>
<span id="LC2" class="line" lang="plaintext">##### foo ##</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="696:1-699:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;foo&lt;/h1&gt;</span>
+<pre data-sourcepos="861:1-864:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;foo&lt;/h1&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h5&gt;foo&lt;/h5&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="702:1-702:46" dir="auto">Spaces are allowed after the closing sequence:</p>
+</div>
+<p data-sourcepos="868:1-868:46" dir="auto">Spaces are allowed after the closing sequence:</p>
+<div>
+<div><a href="#example-43">Example 43</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="704:1-706:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">### foo ### </span></code></pre>
+<pre data-sourcepos="873:1-875:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">### foo ### </span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="708:1-710:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h3&gt;foo&lt;/h3&gt;</span></code></pre>
+<pre data-sourcepos="877:1-879:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h3&gt;foo&lt;/h3&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="713:1-715:8" dir="auto">A sequence of <code>#</code> characters with anything but [spaces] following it
+</div>
+<p data-sourcepos="883:1-885:8" dir="auto">A sequence of <code>#</code> characters with anything but [spaces] following it
is not a closing sequence, but counts as part of the contents of the
heading:</p>
+<div>
+<div><a href="#example-44">Example 44</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="717:1-719:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">### foo ### b</span></code></pre>
+<pre data-sourcepos="890:1-892:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">### foo ### b</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="721:1-723:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h3&gt;foo ### b&lt;/h3&gt;</span></code></pre>
+<pre data-sourcepos="894:1-896:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h3&gt;foo ### b&lt;/h3&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="726:1-726:49" dir="auto">The closing sequence must be preceded by a space:</p>
+</div>
+<p data-sourcepos="900:1-900:49" dir="auto">The closing sequence must be preceded by a space:</p>
+<div>
+<div><a href="#example-45">Example 45</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="728:1-730:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"># foo#</span></code></pre>
+<pre data-sourcepos="905:1-907:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"># foo#</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="732:1-734:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;foo#&lt;/h1&gt;</span></code></pre>
+<pre data-sourcepos="909:1-911:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;foo#&lt;/h1&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="737:1-738:24" dir="auto">Backslash-escaped <code>#</code> characters do not count as part
+</div>
+<p data-sourcepos="915:1-916:24" dir="auto">Backslash-escaped <code>#</code> characters do not count as part
of the closing sequence:</p>
+<div>
+<div><a href="#example-46">Example 46</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="740:1-744:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">### foo \###</span>
+<pre data-sourcepos="921:1-925:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">### foo \###</span>
<span id="LC2" class="line" lang="plaintext">## foo #\##</span>
<span id="LC3" class="line" lang="plaintext"># foo \#</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="746:1-750:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h3&gt;foo ###&lt;/h3&gt;</span>
+<pre data-sourcepos="927:1-931:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h3&gt;foo ###&lt;/h3&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h2&gt;foo ###&lt;/h2&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;h1&gt;foo #&lt;/h1&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="753:1-754:41" dir="auto">ATX headings need not be separated from surrounding content by blank
+</div>
+<p data-sourcepos="935:1-936:41" dir="auto">ATX headings need not be separated from surrounding content by blank
lines, and they can interrupt paragraphs:</p>
+<div>
+<div><a href="#example-47">Example 47</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="756:1-760:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">****</span>
+<pre data-sourcepos="941:1-945:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">****</span>
<span id="LC2" class="line" lang="plaintext">## foo</span>
<span id="LC3" class="line" lang="plaintext">****</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="762:1-766:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span>
+<pre data-sourcepos="947:1-951:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h2&gt;foo&lt;/h2&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-48">Example 48</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="769:1-773:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo bar</span>
+<pre data-sourcepos="958:1-962:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo bar</span>
<span id="LC2" class="line" lang="plaintext"># baz</span>
<span id="LC3" class="line" lang="plaintext">Bar foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="775:1-779:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo bar&lt;/p&gt;</span>
+<pre data-sourcepos="964:1-968:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo bar&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h1&gt;baz&lt;/h1&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;Bar foo&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="782:1-782:26" dir="auto">ATX headings can be empty:</p>
+</div>
+<p data-sourcepos="972:1-972:26" dir="auto">ATX headings can be empty:</p>
+<div>
+<div><a href="#example-49">Example 49</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="784:1-788:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">## </span>
+<pre data-sourcepos="977:1-981:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">## </span>
<span id="LC2" class="line" lang="plaintext">#</span>
<span id="LC3" class="line" lang="plaintext">### ###</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="790:1-794:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;&lt;/h2&gt;</span>
+<pre data-sourcepos="983:1-987:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;&lt;/h2&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h1&gt;&lt;/h1&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;h3&gt;&lt;/h3&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="797:1-797:18" dir="auto">
+</div>
+<h2 data-sourcepos="991:1-991:18" dir="auto">
<a id="user-content-setext-headings" class="anchor" href="#setext-headings" aria-hidden="true"></a>Setext headings</h2>
-<p data-sourcepos="799:1-807:54" dir="auto">A <a href="@">setext heading</a> consists of one or more
+<p data-sourcepos="993:1-1001:54" dir="auto">A <a href="@">setext heading</a> consists of one or more
lines of text, each containing at least one [non-whitespace
character], with no more than 3 spaces indentation, followed by
a [setext heading underline]. The lines of text must be such
@@ -709,24 +1220,26 @@ they would be interpreted as a paragraph: they cannot be
interpretable as a [code fence], [ATX heading][ATX headings],
[block quote][block quotes], [thematic break][thematic breaks],
[list item][list items], or [HTML block][HTML blocks].</p>
-<p data-sourcepos="809:1-814:40" dir="auto">A <a href="@">setext heading underline</a> is a sequence of
+<p data-sourcepos="1003:1-1008:40" dir="auto">A <a href="@">setext heading underline</a> is a sequence of
<code>=</code> characters or a sequence of <code>-</code> characters, with no more than 3
spaces indentation and any number of trailing spaces. If a line
containing a single <code>-</code> can be interpreted as an
empty [list items], it should be interpreted this way
and not as a [setext heading underline].</p>
-<p data-sourcepos="816:1-820:8" dir="auto">The heading is a level 1 heading if <code>=</code> characters are used in
+<p data-sourcepos="1010:1-1014:8" dir="auto">The heading is a level 1 heading if <code>=</code> characters are used in
the [setext heading underline], and a level 2 heading if <code>-</code>
characters are used. The contents of the heading are the result
of parsing the preceding lines of text as CommonMark inline
content.</p>
-<p data-sourcepos="822:1-825:5" dir="auto">In general, a setext heading need not be preceded or followed by a
+<p data-sourcepos="1016:1-1019:5" dir="auto">In general, a setext heading need not be preceded or followed by a
blank line. However, it cannot interrupt a paragraph, so when a
setext heading comes after a paragraph, a blank line is needed between
them.</p>
-<p data-sourcepos="827:1-827:16" dir="auto">Simple examples:</p>
+<p data-sourcepos="1021:1-1021:16" dir="auto">Simple examples:</p>
+<div>
+<div><a href="#example-50">Example 50</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="829:1-835:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo *bar*</span>
+<pre data-sourcepos="1026:1-1032:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo *bar*</span>
<span id="LC2" class="line" lang="plaintext">=========</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">Foo *bar*</span>
@@ -734,40 +1247,49 @@ them.</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="837:1-840:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;Foo &lt;em&gt;bar&lt;/em&gt;&lt;/h1&gt;</span>
+<pre data-sourcepos="1034:1-1037:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;Foo &lt;em&gt;bar&lt;/em&gt;&lt;/h1&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h2&gt;Foo &lt;em&gt;bar&lt;/em&gt;&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="843:1-843:54" dir="auto">The content of the header may span more than one line:</p>
+</div>
+<p data-sourcepos="1041:1-1041:54" dir="auto">The content of the header may span more than one line:</p>
+<div>
+<div><a href="#example-51">Example 51</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="845:1-849:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo *bar</span>
+<pre data-sourcepos="1046:1-1050:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo *bar</span>
<span id="LC2" class="line" lang="plaintext">baz*</span>
<span id="LC3" class="line" lang="plaintext">====</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="851:1-854:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;Foo &lt;em&gt;bar</span>
+<pre data-sourcepos="1052:1-1055:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;Foo &lt;em&gt;bar</span>
<span id="LC2" class="line" lang="plaintext">baz&lt;/em&gt;&lt;/h1&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="856:1-859:13" dir="auto">The contents are the result of parsing the headings's raw
+</div>
+<p data-sourcepos="1058:1-1061:13" dir="auto">The contents are the result of parsing the headings's raw
content as inlines. The heading's raw content is formed by
concatenating the lines and removing initial and final
[whitespace].</p>
+<div>
+<div><a href="#example-52">Example 52</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="861:1-865:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> Foo *bar</span>
+<pre data-sourcepos="1066:1-1070:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> Foo *bar</span>
<span id="LC2" class="line" lang="plaintext">baz*→</span>
<span id="LC3" class="line" lang="plaintext">====</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="867:1-870:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;Foo &lt;em&gt;bar</span>
+<pre data-sourcepos="1072:1-1075:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;Foo &lt;em&gt;bar</span>
<span id="LC2" class="line" lang="plaintext">baz&lt;/em&gt;&lt;/h1&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="873:1-873:34" dir="auto">The underlining can be any length:</p>
+</div>
+<p data-sourcepos="1079:1-1079:34" dir="auto">The underlining can be any length:</p>
+<div>
+<div><a href="#example-53">Example 53</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="875:1-881:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="1084:1-1090:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext">-------------------------</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">Foo</span>
@@ -775,14 +1297,17 @@ concatenating the lines and removing initial and final
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="883:1-886:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;Foo&lt;/h2&gt;</span>
+<pre data-sourcepos="1092:1-1095:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;Foo&lt;/h2&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h1&gt;Foo&lt;/h1&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="889:1-890:33" dir="auto">The heading content can be indented up to three spaces, and need
+</div>
+<p data-sourcepos="1099:1-1100:33" dir="auto">The heading content can be indented up to three spaces, and need
not line up with the underlining:</p>
+<div>
+<div><a href="#example-54">Example 54</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="892:1-901:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> Foo</span>
+<pre data-sourcepos="1105:1-1114:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> Foo</span>
<span id="LC2" class="line" lang="plaintext">---</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext"> Foo</span>
@@ -793,14 +1318,17 @@ not line up with the underlining:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="903:1-907:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;Foo&lt;/h2&gt;</span>
+<pre data-sourcepos="1116:1-1120:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;Foo&lt;/h2&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h2&gt;Foo&lt;/h2&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;h1&gt;Foo&lt;/h1&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="910:1-910:31" dir="auto">Four spaces indent is too much:</p>
+</div>
+<p data-sourcepos="1124:1-1124:31" dir="auto">Four spaces indent is too much:</p>
+<div>
+<div><a href="#example-55">Example 55</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="912:1-918:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> Foo</span>
+<pre data-sourcepos="1129:1-1135:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> Foo</span>
<span id="LC2" class="line" lang="plaintext"> ---</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext"> Foo</span>
@@ -808,7 +1336,7 @@ not line up with the underlining:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="920:1-927:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;Foo</span>
+<pre data-sourcepos="1137:1-1144:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;Foo</span>
<span id="LC2" class="line" lang="plaintext">---</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">Foo</span>
@@ -816,31 +1344,40 @@ not line up with the underlining:</p>
<span id="LC6" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="930:1-931:25" dir="auto">The setext heading underline can be indented up to three spaces, and
+</div>
+<p data-sourcepos="1148:1-1149:25" dir="auto">The setext heading underline can be indented up to three spaces, and
may have trailing spaces:</p>
+<div>
+<div><a href="#example-56">Example 56</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="933:1-936:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="1154:1-1157:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext"> ---- </span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="938:1-940:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;Foo&lt;/h2&gt;</span></code></pre>
+<pre data-sourcepos="1159:1-1161:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;Foo&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="943:1-943:24" dir="auto">Four spaces is too much:</p>
+</div>
+<p data-sourcepos="1165:1-1165:24" dir="auto">Four spaces is too much:</p>
+<div>
+<div><a href="#example-57">Example 57</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="945:1-948:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="1170:1-1173:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext"> ---</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="950:1-953:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
+<pre data-sourcepos="1175:1-1178:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
<span id="LC2" class="line" lang="plaintext">---&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="956:1-956:60" dir="auto">The setext heading underline cannot contain internal spaces:</p>
+</div>
+<p data-sourcepos="1182:1-1182:60" dir="auto">The setext heading underline cannot contain internal spaces:</p>
+<div>
+<div><a href="#example-58">Example 58</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="958:1-964:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="1187:1-1193:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext">= =</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">Foo</span>
@@ -848,36 +1385,45 @@ may have trailing spaces:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="966:1-971:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
+<pre data-sourcepos="1195:1-1200:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
<span id="LC2" class="line" lang="plaintext">= =&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;Foo&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="974:1-974:62" dir="auto">Trailing spaces in the content line do not cause a line break:</p>
+</div>
+<p data-sourcepos="1204:1-1204:62" dir="auto">Trailing spaces in the content line do not cause a line break:</p>
+<div>
+<div><a href="#example-59">Example 59</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="976:1-979:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo </span>
+<pre data-sourcepos="1209:1-1212:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo </span>
<span id="LC2" class="line" lang="plaintext">-----</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="981:1-983:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;Foo&lt;/h2&gt;</span></code></pre>
+<pre data-sourcepos="1214:1-1216:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;Foo&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="986:1-986:32" dir="auto">Nor does a backslash at the end:</p>
+</div>
+<p data-sourcepos="1220:1-1220:32" dir="auto">Nor does a backslash at the end:</p>
+<div>
+<div><a href="#example-60">Example 60</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="988:1-991:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo\</span>
+<pre data-sourcepos="1225:1-1228:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo\</span>
<span id="LC2" class="line" lang="plaintext">----</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="993:1-995:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;Foo\&lt;/h2&gt;</span></code></pre>
+<pre data-sourcepos="1230:1-1232:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;Foo\&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="998:1-999:66" dir="auto">Since indicators of block structure take precedence over
+</div>
+<p data-sourcepos="1236:1-1237:66" dir="auto">Since indicators of block structure take precedence over
indicators of inline structure, the following are setext headings:</p>
+<div>
+<div><a href="#example-61">Example 61</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1001:1-1009:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`Foo</span>
+<pre data-sourcepos="1242:1-1250:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`Foo</span>
<span id="LC2" class="line" lang="plaintext">----</span>
<span id="LC3" class="line" lang="plaintext">`</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -887,70 +1433,85 @@ indicators of inline structure, the following are setext headings:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1011:1-1016:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;`Foo&lt;/h2&gt;</span>
+<pre data-sourcepos="1252:1-1257:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;`Foo&lt;/h2&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;`&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;h2&gt;&amp;lt;a title=&amp;quot;a lot&lt;/h2&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;of dashes&amp;quot;/&amp;gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1019:1-1020:36" dir="auto">The setext heading underline cannot be a [lazy continuation
+</div>
+<p data-sourcepos="1261:1-1262:36" dir="auto">The setext heading underline cannot be a [lazy continuation
line] in a list item or block quote:</p>
+<div>
+<div><a href="#example-62">Example 62</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1022:1-1025:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; Foo</span>
+<pre data-sourcepos="1267:1-1270:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; Foo</span>
<span id="LC2" class="line" lang="plaintext">---</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1027:1-1032:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="1272:1-1277:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;Foo&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/blockquote&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-63">Example 63</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1035:1-1039:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; foo</span>
+<pre data-sourcepos="1284:1-1288:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; foo</span>
<span id="LC2" class="line" lang="plaintext">bar</span>
<span id="LC3" class="line" lang="plaintext">===</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1041:1-1047:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="1290:1-1296:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;foo</span>
<span id="LC3" class="line" lang="plaintext">bar</span>
<span id="LC4" class="line" lang="plaintext">===&lt;/p&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-64">Example 64</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1050:1-1053:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- Foo</span>
+<pre data-sourcepos="1303:1-1306:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- Foo</span>
<span id="LC2" class="line" lang="plaintext">---</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1055:1-1060:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="1308:1-1313:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;Foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1063:1-1065:25" dir="auto">A blank line is needed between a paragraph and a following
+</div>
+<p data-sourcepos="1317:1-1319:25" dir="auto">A blank line is needed between a paragraph and a following
setext heading, since otherwise the paragraph becomes part
of the heading's content:</p>
+<div>
+<div><a href="#example-65">Example 65</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1067:1-1071:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="1324:1-1328:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext">Bar</span>
<span id="LC3" class="line" lang="plaintext">---</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1073:1-1076:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;Foo</span>
+<pre data-sourcepos="1330:1-1333:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;Foo</span>
<span id="LC2" class="line" lang="plaintext">Bar&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1079:1-1080:16" dir="auto">But in general a blank line is not required before or after
+</div>
+<p data-sourcepos="1337:1-1338:16" dir="auto">But in general a blank line is not required before or after
setext headings:</p>
+<div>
+<div><a href="#example-66">Example 66</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1082:1-1089:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">---</span>
+<pre data-sourcepos="1343:1-1350:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">---</span>
<span id="LC2" class="line" lang="plaintext">Foo</span>
<span id="LC3" class="line" lang="plaintext">---</span>
<span id="LC4" class="line" lang="plaintext">Bar</span>
@@ -959,104 +1520,125 @@ setext headings:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1091:1-1096:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span>
+<pre data-sourcepos="1352:1-1357:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h2&gt;Foo&lt;/h2&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;h2&gt;Bar&lt;/h2&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;Baz&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1099:1-1099:32" dir="auto">Setext headings cannot be empty:</p>
+</div>
+<p data-sourcepos="1361:1-1361:32" dir="auto">Setext headings cannot be empty:</p>
+<div>
+<div><a href="#example-67">Example 67</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1101:1-1104:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"></span>
+<pre data-sourcepos="1366:1-1369:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"></span>
<span id="LC2" class="line" lang="plaintext">====</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1106:1-1108:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;====&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="1371:1-1373:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;====&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1111:1-1113:55" dir="auto">Setext heading text lines must not be interpretable as block
+</div>
+<p data-sourcepos="1377:1-1379:55" dir="auto">Setext heading text lines must not be interpretable as block
constructs other than paragraphs. So, the line of dashes
in these examples gets interpreted as a thematic break:</p>
+<div>
+<div><a href="#example-68">Example 68</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1115:1-1118:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">---</span>
+<pre data-sourcepos="1384:1-1387:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">---</span>
<span id="LC2" class="line" lang="plaintext">---</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1120:1-1123:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span>
+<pre data-sourcepos="1389:1-1392:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr /&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-69">Example 69</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1126:1-1129:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
+<pre data-sourcepos="1399:1-1402:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
<span id="LC2" class="line" lang="plaintext">-----</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1131:1-1136:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="1404:1-1409:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-70">Example 70</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1139:1-1142:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> foo</span>
+<pre data-sourcepos="1416:1-1419:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> foo</span>
<span id="LC2" class="line" lang="plaintext">---</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1144:1-1148:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo</span>
+<pre data-sourcepos="1421:1-1425:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-71">Example 71</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1151:1-1154:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; foo</span>
+<pre data-sourcepos="1432:1-1435:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; foo</span>
<span id="LC2" class="line" lang="plaintext">-----</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1156:1-1161:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="1437:1-1442:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/blockquote&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1164:1-1165:22" dir="auto">If you want a heading with <code>&gt; foo</code> as its literal text, you can
+</div>
+<p data-sourcepos="1446:1-1447:22" dir="auto">If you want a heading with <code>&gt; foo</code> as its literal text, you can
use backslash escapes:</p>
+<div>
+<div><a href="#example-72">Example 72</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1167:1-1170:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">\&gt; foo</span>
+<pre data-sourcepos="1452:1-1455:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">\&gt; foo</span>
<span id="LC2" class="line" lang="plaintext">------</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1172:1-1174:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;&amp;gt; foo&lt;/h2&gt;</span></code></pre>
+<pre data-sourcepos="1457:1-1459:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;&amp;gt; foo&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1177:1-1179:48" dir="auto"><strong>Compatibility note:</strong> Most existing Markdown implementations
+</div>
+<p data-sourcepos="1463:1-1465:48" dir="auto"><strong>Compatibility note:</strong> Most existing Markdown implementations
do not allow the text of setext headings to span multiple lines.
But there is no consensus about how to interpret</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1181:1-1186:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown">Foo</span>
+<pre data-sourcepos="1467:1-1472:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown">Foo</span>
<span id="LC2" class="line" lang="markdown"><span class="gh">bar</span></span>
<span id="LC3" class="line" lang="markdown"><span class="gh">---</span></span>
<span id="LC4" class="line" lang="markdown">baz</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1188:1-1188:44" dir="auto">One can find four different interpretations:</p>
-<ol data-sourcepos="1190:1-1194:0" dir="auto">
-<li data-sourcepos="1190:1-1190:50">paragraph "Foo", heading "bar", paragraph "baz"</li>
-<li data-sourcepos="1191:1-1191:55">paragraph "Foo bar", thematic break, paragraph "baz"</li>
-<li data-sourcepos="1192:1-1192:30">paragraph "Foo bar --- baz"</li>
-<li data-sourcepos="1193:1-1194:0">heading "Foo bar", paragraph "baz"</li>
+<p data-sourcepos="1474:1-1474:44" dir="auto">One can find four different interpretations:</p>
+<ol data-sourcepos="1476:1-1480:0" dir="auto">
+<li data-sourcepos="1476:1-1476:50">paragraph "Foo", heading "bar", paragraph "baz"</li>
+<li data-sourcepos="1477:1-1477:55">paragraph "Foo bar", thematic break, paragraph "baz"</li>
+<li data-sourcepos="1478:1-1478:30">paragraph "Foo bar --- baz"</li>
+<li data-sourcepos="1479:1-1480:0">heading "Foo bar", paragraph "baz"</li>
</ol>
-<p data-sourcepos="1195:1-1198:43" dir="auto">We find interpretation 4 most natural, and interpretation 4
+<p data-sourcepos="1481:1-1484:43" dir="auto">We find interpretation 4 most natural, and interpretation 4
increases the expressive power of CommonMark, by allowing
multiline headings. Authors who want interpretation 1 can
put a blank line after the first paragraph:</p>
+<div>
+<div><a href="#example-73">Example 73</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1200:1-1206:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="1489:1-1495:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">bar</span>
<span id="LC4" class="line" lang="plaintext">---</span>
@@ -1064,15 +1646,18 @@ put a blank line after the first paragraph:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1208:1-1212:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo&lt;/p&gt;</span>
+<pre data-sourcepos="1497:1-1501:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h2&gt;bar&lt;/h2&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;baz&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1215:1-1216:19" dir="auto">Authors who want interpretation 2 can put blank lines around
+</div>
+<p data-sourcepos="1505:1-1506:19" dir="auto">Authors who want interpretation 2 can put blank lines around
the thematic break,</p>
+<div>
+<div><a href="#example-74">Example 74</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1218:1-1225:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="1511:1-1518:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext">bar</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">---</span>
@@ -1081,78 +1666,90 @@ the thematic break,</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1227:1-1232:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
+<pre data-sourcepos="1520:1-1525:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
<span id="LC2" class="line" lang="plaintext">bar&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;hr /&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;baz&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1235:1-1236:19" dir="auto">or use a thematic break that cannot count as a [setext heading
+</div>
+<p data-sourcepos="1529:1-1530:19" dir="auto">or use a thematic break that cannot count as a [setext heading
underline], such as</p>
+<div>
+<div><a href="#example-75">Example 75</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1238:1-1243:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="1535:1-1540:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext">bar</span>
<span id="LC3" class="line" lang="plaintext">* * *</span>
<span id="LC4" class="line" lang="plaintext">baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1245:1-1250:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
+<pre data-sourcepos="1542:1-1547:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
<span id="LC2" class="line" lang="plaintext">bar&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;hr /&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;baz&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1253:1-1253:60" dir="auto">Authors who want interpretation 3 can use backslash escapes:</p>
+</div>
+<p data-sourcepos="1551:1-1551:60" dir="auto">Authors who want interpretation 3 can use backslash escapes:</p>
+<div>
+<div><a href="#example-76">Example 76</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1255:1-1260:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="1556:1-1561:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext">bar</span>
<span id="LC3" class="line" lang="plaintext">\---</span>
<span id="LC4" class="line" lang="plaintext">baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1262:1-1267:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
+<pre data-sourcepos="1563:1-1568:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
<span id="LC2" class="line" lang="plaintext">bar</span>
<span id="LC3" class="line" lang="plaintext">---</span>
<span id="LC4" class="line" lang="plaintext">baz&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="1270:1-1270:23" dir="auto">
+</div>
+<h2 data-sourcepos="1572:1-1572:23" dir="auto">
<a id="user-content-indented-code-blocks" class="anchor" href="#indented-code-blocks" aria-hidden="true"></a>Indented code blocks</h2>
-<p data-sourcepos="1272:1-1278:44" dir="auto">An <a href="@">indented code block</a> is composed of one or more
+<p data-sourcepos="1574:1-1580:44" dir="auto">An <a href="@">indented code block</a> is composed of one or more
[indented chunks] separated by blank lines.
An <a href="@">indented chunk</a> is a sequence of non-blank lines,
each indented four or more spaces. The contents of the code block are
the literal contents of the lines, including trailing
[line endings], minus four spaces of indentation.
An indented code block has no [info string].</p>
-<p data-sourcepos="1280:1-1283:11" dir="auto">An indented code block cannot interrupt a paragraph, so there must be
+<p data-sourcepos="1582:1-1585:11" dir="auto">An indented code block cannot interrupt a paragraph, so there must be
a blank line between a paragraph and a following indented code block.
(A blank line is not needed, however, between a code block and a following
paragraph.)</p>
+<div>
+<div><a href="#example-77">Example 77</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1285:1-1288:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> a simple</span>
+<pre data-sourcepos="1590:1-1593:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> a simple</span>
<span id="LC2" class="line" lang="plaintext"> indented code block</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1290:1-1294:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;a simple</span>
+<pre data-sourcepos="1595:1-1599:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;a simple</span>
<span id="LC2" class="line" lang="plaintext"> indented code block</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1297:1-1299:65" dir="auto">If there is any ambiguity between an interpretation of indentation
+</div>
+<p data-sourcepos="1603:1-1605:65" dir="auto">If there is any ambiguity between an interpretation of indentation
as a code block and as indicating that material belongs to a [list
item][list items], the list item interpretation takes precedence:</p>
+<div>
+<div><a href="#example-78">Example 78</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1301:1-1305:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> - foo</span>
+<pre data-sourcepos="1610:1-1614:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> - foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1307:1-1314:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="1616:1-1623:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span>
@@ -1160,14 +1757,17 @@ item][list items], the list item interpretation takes precedence:</p>
<span id="LC6" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-79">Example 79</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1317:1-1321:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. foo</span>
+<pre data-sourcepos="1630:1-1634:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> - bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1323:1-1332:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
+<pre data-sourcepos="1636:1-1645:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;ul&gt;</span>
@@ -1177,26 +1777,32 @@ item][list items], the list item interpretation takes precedence:</p>
<span id="LC8" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1336:1-1337:12" dir="auto">The contents of a code block are literal text, and do not get parsed
+</div>
+<p data-sourcepos="1650:1-1651:12" dir="auto">The contents of a code block are literal text, and do not get parsed
as Markdown:</p>
+<div>
+<div><a href="#example-80">Example 80</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1339:1-1344:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &lt;a/&gt;</span>
+<pre data-sourcepos="1656:1-1661:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &lt;a/&gt;</span>
<span id="LC2" class="line" lang="plaintext"> *hi*</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext"> - one</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1346:1-1352:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&amp;lt;a/&amp;gt;</span>
+<pre data-sourcepos="1663:1-1669:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&amp;lt;a/&amp;gt;</span>
<span id="LC2" class="line" lang="plaintext">*hi*</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">- one</span>
<span id="LC5" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1355:1-1355:51" dir="auto">Here we have three chunks separated by blank lines:</p>
+</div>
+<p data-sourcepos="1673:1-1673:51" dir="auto">Here we have three chunks separated by blank lines:</p>
+<div>
+<div><a href="#example-81">Example 81</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1357:1-1365:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> chunk1</span>
+<pre data-sourcepos="1678:1-1686:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> chunk1</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> chunk2</span>
<span id="LC4" class="line" lang="plaintext"> </span>
@@ -1206,7 +1812,7 @@ as Markdown:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1367:1-1376:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;chunk1</span>
+<pre data-sourcepos="1688:1-1697:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;chunk1</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">chunk2</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -1216,52 +1822,64 @@ as Markdown:</p>
<span id="LC8" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1379:1-1380:24" dir="auto">Any initial spaces beyond four will be included in the content, even
+</div>
+<p data-sourcepos="1701:1-1702:24" dir="auto">Any initial spaces beyond four will be included in the content, even
in interior blank lines:</p>
+<div>
+<div><a href="#example-82">Example 82</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1382:1-1386:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> chunk1</span>
+<pre data-sourcepos="1707:1-1711:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> chunk1</span>
<span id="LC2" class="line" lang="plaintext"> </span>
<span id="LC3" class="line" lang="plaintext"> chunk2</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1388:1-1393:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;chunk1</span>
+<pre data-sourcepos="1713:1-1718:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;chunk1</span>
<span id="LC2" class="line" lang="plaintext"> </span>
<span id="LC3" class="line" lang="plaintext"> chunk2</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1396:1-1397:37" dir="auto">An indented code block cannot interrupt a paragraph. (This
+</div>
+<p data-sourcepos="1722:1-1723:37" dir="auto">An indented code block cannot interrupt a paragraph. (This
allows hanging indents and the like.)</p>
+<div>
+<div><a href="#example-83">Example 83</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1399:1-1403:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="1728:1-1732:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext"> bar</span>
<span id="LC3" class="line" lang="plaintext"></span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1405:1-1408:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
+<pre data-sourcepos="1734:1-1737:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
<span id="LC2" class="line" lang="plaintext">bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1411:1-1413:20" dir="auto">However, any non-blank line with fewer than four leading spaces ends
+</div>
+<p data-sourcepos="1741:1-1743:20" dir="auto">However, any non-blank line with fewer than four leading spaces ends
the code block immediately. So a paragraph may occur immediately
after indented code:</p>
+<div>
+<div><a href="#example-84">Example 84</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1415:1-1418:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> foo</span>
+<pre data-sourcepos="1748:1-1751:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> foo</span>
<span id="LC2" class="line" lang="plaintext">bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1420:1-1424:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo</span>
+<pre data-sourcepos="1753:1-1757:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1427:1-1428:7" dir="auto">And indented code can occur immediately before and after other kinds of
+</div>
+<p data-sourcepos="1761:1-1762:7" dir="auto">And indented code can occur immediately before and after other kinds of
blocks:</p>
+<div>
+<div><a href="#example-85">Example 85</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1430:1-1437:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"># Heading</span>
+<pre data-sourcepos="1767:1-1774:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"># Heading</span>
<span id="LC2" class="line" lang="plaintext"> foo</span>
<span id="LC3" class="line" lang="plaintext">Heading</span>
<span id="LC4" class="line" lang="plaintext">------</span>
@@ -1270,7 +1888,7 @@ blocks:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1439:1-1447:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;Heading&lt;/h1&gt;</span>
+<pre data-sourcepos="1776:1-1784:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;Heading&lt;/h1&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;h2&gt;Heading&lt;/h2&gt;</span>
@@ -1279,22 +1897,28 @@ blocks:</p>
<span id="LC7" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1450:1-1450:53" dir="auto">The first line can be indented more than four spaces:</p>
+</div>
+<p data-sourcepos="1788:1-1788:53" dir="auto">The first line can be indented more than four spaces:</p>
+<div>
+<div><a href="#example-86">Example 86</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1452:1-1455:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> foo</span>
+<pre data-sourcepos="1793:1-1796:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> foo</span>
<span id="LC2" class="line" lang="plaintext"> bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1457:1-1461:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt; foo</span>
+<pre data-sourcepos="1798:1-1802:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt; foo</span>
<span id="LC2" class="line" lang="plaintext">bar</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1464:1-1465:23" dir="auto">Blank lines preceding or following an indented code block
+</div>
+<p data-sourcepos="1806:1-1807:23" dir="auto">Blank lines preceding or following an indented code block
are not included in it:</p>
+<div>
+<div><a href="#example-87">Example 87</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1467:1-1473:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"></span>
+<pre data-sourcepos="1812:1-1818:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"></span>
<span id="LC2" class="line" lang="plaintext"> </span>
<span id="LC3" class="line" lang="plaintext"> foo</span>
<span id="LC4" class="line" lang="plaintext"> </span>
@@ -1302,35 +1926,39 @@ are not included in it:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1475:1-1478:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo</span>
+<pre data-sourcepos="1820:1-1823:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1481:1-1481:57" dir="auto">Trailing spaces are included in the code block's content:</p>
+</div>
+<p data-sourcepos="1827:1-1827:57" dir="auto">Trailing spaces are included in the code block's content:</p>
+<div>
+<div><a href="#example-88">Example 88</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1483:1-1485:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> foo </span></code></pre>
+<pre data-sourcepos="1832:1-1834:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> foo </span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1487:1-1490:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo </span>
+<pre data-sourcepos="1836:1-1839:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo </span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="1494:1-1494:21" dir="auto">
+</div>
+<h2 data-sourcepos="1844:1-1844:21" dir="auto">
<a id="user-content-fenced-code-blocks" class="anchor" href="#fenced-code-blocks" aria-hidden="true"></a>Fenced code blocks</h2>
-<p data-sourcepos="1496:1-1500:61" dir="auto">A <a href="@">code fence</a> is a sequence
+<p data-sourcepos="1846:1-1850:61" dir="auto">A <a href="@">code fence</a> is a sequence
of at least three consecutive backtick characters (<code>`</code>) or
tildes (<code>~</code>). (Tildes and backticks cannot be mixed.)
A <a href="@">fenced code block</a>
begins with a code fence, indented no more than three spaces.</p>
-<p data-sourcepos="1502:1-1508:34" dir="auto">The line with the opening code fence may optionally contain some text
+<p data-sourcepos="1852:1-1858:34" dir="auto">The line with the opening code fence may optionally contain some text
following the code fence; this is trimmed of leading and trailing
whitespace and called the <a href="@">info string</a>. If the [info string] comes
after a backtick fence, it may not contain any backtick
characters. (The reason for this restriction is that otherwise
some inline code would be incorrectly interpreted as the
beginning of a fenced code block.)</p>
-<p data-sourcepos="1510:1-1517:43" dir="auto">The content of the code block consists of all subsequent lines, until
+<p data-sourcepos="1860:1-1867:43" dir="auto">The content of the code block consists of all subsequent lines, until
a closing [code fence] of the same type as the code block
began with (backticks or tildes), and with at least as many backticks
or tildes as the opening code fence. If the leading code fence is
@@ -1338,7 +1966,7 @@ indented N spaces, then up to N spaces of indentation are removed from
each line of the content (if present). (If a content line is not
indented, it is preserved unchanged. If it is indented less than N
spaces, all of the indentation is removed.)</p>
-<p data-sourcepos="1519:1-1527:25" dir="auto">The closing code fence may be indented up to three spaces, and may be
+<p data-sourcepos="1869:1-1877:25" dir="auto">The closing code fence may be indented up to three spaces, and may be
followed only by spaces, which are ignored. If the end of the
containing block (or document) is reached and no closing code fence
has been found, the code block contains all of the lines after the
@@ -1347,188 +1975,229 @@ document). (An alternative spec would require backtracking in the
event that a closing code fence is not found. But this makes parsing
much less efficient, and there seems to be no real down side to the
behavior described here.)</p>
-<p data-sourcepos="1529:1-1530:36" dir="auto">A fenced code block may interrupt a paragraph, and does not require
+<p data-sourcepos="1879:1-1880:36" dir="auto">A fenced code block may interrupt a paragraph, and does not require
a blank line either before or after.</p>
-<p data-sourcepos="1532:1-1536:42" dir="auto">The content of a code fence is treated as literal text, not parsed
+<p data-sourcepos="1882:1-1886:42" dir="auto">The content of a code fence is treated as literal text, not parsed
as inlines. The first word of the [info string] is typically used to
specify the language of the code sample, and rendered in the <code>class</code>
attribute of the <code>code</code> tag. However, this spec does not mandate any
particular treatment of the [info string].</p>
-<p data-sourcepos="1538:1-1538:40" dir="auto">Here is a simple example with backticks:</p>
+<p data-sourcepos="1888:1-1888:40" dir="auto">Here is a simple example with backticks:</p>
+<div>
+<div><a href="#example-89">Example 89</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1540:1-1545:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
+<pre data-sourcepos="1893:1-1898:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
<span id="LC2" class="line" lang="plaintext">&lt;</span>
<span id="LC3" class="line" lang="plaintext"> &gt;</span>
<span id="LC4" class="line" lang="plaintext">```</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1547:1-1551:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&amp;lt;</span>
+<pre data-sourcepos="1900:1-1904:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&amp;lt;</span>
<span id="LC2" class="line" lang="plaintext"> &amp;gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1554:1-1554:12" dir="auto">With tildes:</p>
+</div>
+<p data-sourcepos="1908:1-1908:12" dir="auto">With tildes:</p>
+<div>
+<div><a href="#example-90">Example 90</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1556:1-1561:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~~</span>
+<pre data-sourcepos="1913:1-1918:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~~</span>
<span id="LC2" class="line" lang="plaintext">&lt;</span>
<span id="LC3" class="line" lang="plaintext"> &gt;</span>
<span id="LC4" class="line" lang="plaintext">~~~</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1563:1-1567:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&amp;lt;</span>
+<pre data-sourcepos="1920:1-1924:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&amp;lt;</span>
<span id="LC2" class="line" lang="plaintext"> &amp;gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1569:1-1569:41" dir="auto">Fewer than three backticks is not enough:</p>
+</div>
+<p data-sourcepos="1927:1-1927:41" dir="auto">Fewer than three backticks is not enough:</p>
+<div>
+<div><a href="#example-91">Example 91</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1571:1-1575:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">``</span>
+<pre data-sourcepos="1932:1-1936:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">``</span>
<span id="LC2" class="line" lang="plaintext">foo</span>
<span id="LC3" class="line" lang="plaintext">``</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1577:1-1579:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="1938:1-1940:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1581:1-1582:6" dir="auto">The closing code fence must use the same character as the opening
+</div>
+<p data-sourcepos="1943:1-1944:6" dir="auto">The closing code fence must use the same character as the opening
fence:</p>
+<div>
+<div><a href="#example-92">Example 92</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1584:1-1589:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
+<pre data-sourcepos="1949:1-1954:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
<span id="LC2" class="line" lang="plaintext">aaa</span>
<span id="LC3" class="line" lang="plaintext">~~~</span>
<span id="LC4" class="line" lang="plaintext">```</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1591:1-1595:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
+<pre data-sourcepos="1956:1-1960:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
<span id="LC2" class="line" lang="plaintext">~~~</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-93">Example 93</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1598:1-1603:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~~</span>
+<pre data-sourcepos="1967:1-1972:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~~</span>
<span id="LC2" class="line" lang="plaintext">aaa</span>
<span id="LC3" class="line" lang="plaintext">```</span>
<span id="LC4" class="line" lang="plaintext">~~~</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1605:1-1609:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
+<pre data-sourcepos="1974:1-1978:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
<span id="LC2" class="line" lang="plaintext">```</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1612:1-1612:69" dir="auto">The closing code fence must be at least as long as the opening fence:</p>
+</div>
+<p data-sourcepos="1982:1-1982:69" dir="auto">The closing code fence must be at least as long as the opening fence:</p>
+<div>
+<div><a href="#example-94">Example 94</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1614:1-1619:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">````</span>
+<pre data-sourcepos="1987:1-1992:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">````</span>
<span id="LC2" class="line" lang="plaintext">aaa</span>
<span id="LC3" class="line" lang="plaintext">```</span>
<span id="LC4" class="line" lang="plaintext">``````</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1621:1-1625:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
+<pre data-sourcepos="1994:1-1998:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
<span id="LC2" class="line" lang="plaintext">```</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-95">Example 95</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1628:1-1633:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~~~</span>
+<pre data-sourcepos="2005:1-2010:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~~~</span>
<span id="LC2" class="line" lang="plaintext">aaa</span>
<span id="LC3" class="line" lang="plaintext">~~~</span>
<span id="LC4" class="line" lang="plaintext">~~~~</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1635:1-1639:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
+<pre data-sourcepos="2012:1-2016:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
<span id="LC2" class="line" lang="plaintext">~~~</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1642:1-1643:74" dir="auto">Unclosed code blocks are closed by the end of the document
+</div>
+<p data-sourcepos="2020:1-2021:74" dir="auto">Unclosed code blocks are closed by the end of the document
(or the enclosing [block quote][block quotes] or [list item][list items]):</p>
+<div>
+<div><a href="#example-96">Example 96</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1645:1-1647:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span></code></pre>
+<pre data-sourcepos="2026:1-2028:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1649:1-1651:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
+<pre data-sourcepos="2030:1-2032:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-97">Example 97</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1654:1-1659:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`````</span>
+<pre data-sourcepos="2039:1-2044:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`````</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">```</span>
<span id="LC4" class="line" lang="plaintext">aaa</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1661:1-1666:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;</span>
+<pre data-sourcepos="2046:1-2051:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;</span>
<span id="LC2" class="line" lang="plaintext">```</span>
<span id="LC3" class="line" lang="plaintext">aaa</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-98">Example 98</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1669:1-1674:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; ```</span>
+<pre data-sourcepos="2058:1-2063:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; ```</span>
<span id="LC2" class="line" lang="plaintext">&gt; aaa</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">bbb</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1676:1-1682:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="2065:1-2071:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/blockquote&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;p&gt;bbb&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1685:1-1685:53" dir="auto">A code block can have all empty lines as its content:</p>
+</div>
+<p data-sourcepos="2075:1-2075:53" dir="auto">A code block can have all empty lines as its content:</p>
+<div>
+<div><a href="#example-99">Example 99</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1687:1-1692:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
+<pre data-sourcepos="2080:1-2085:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> </span>
<span id="LC4" class="line" lang="plaintext">```</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1694:1-1698:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;</span>
+<pre data-sourcepos="2087:1-2091:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;</span>
<span id="LC2" class="line" lang="plaintext"> </span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1701:1-1701:26" dir="auto">A code block can be empty:</p>
+</div>
+<p data-sourcepos="2095:1-2095:26" dir="auto">A code block can be empty:</p>
+<div>
+<div><a href="#example-100">Example 100</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1703:1-1706:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
+<pre data-sourcepos="2100:1-2103:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
<span id="LC2" class="line" lang="plaintext">```</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1708:1-1710:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
+<pre data-sourcepos="2105:1-2107:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1713:1-1715:11" dir="auto">Fences can be indented. If the opening fence is indented,
+</div>
+<p data-sourcepos="2111:1-2113:11" dir="auto">Fences can be indented. If the opening fence is indented,
content lines will have equivalent opening indentation removed,
if present:</p>
+<div>
+<div><a href="#example-101">Example 101</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1717:1-1722:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ```</span>
+<pre data-sourcepos="2118:1-2123:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ```</span>
<span id="LC2" class="line" lang="plaintext"> aaa</span>
<span id="LC3" class="line" lang="plaintext">aaa</span>
<span id="LC4" class="line" lang="plaintext">```</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1724:1-1728:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
+<pre data-sourcepos="2125:1-2129:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
<span id="LC2" class="line" lang="plaintext">aaa</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-102">Example 102</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1731:1-1737:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ```</span>
+<pre data-sourcepos="2136:1-2142:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ```</span>
<span id="LC2" class="line" lang="plaintext">aaa</span>
<span id="LC3" class="line" lang="plaintext"> aaa</span>
<span id="LC4" class="line" lang="plaintext">aaa</span>
@@ -1536,14 +2205,17 @@ if present:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1739:1-1744:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
+<pre data-sourcepos="2144:1-2149:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
<span id="LC2" class="line" lang="plaintext">aaa</span>
<span id="LC3" class="line" lang="plaintext">aaa</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-103">Example 103</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1747:1-1753:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ```</span>
+<pre data-sourcepos="2156:1-2162:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ```</span>
<span id="LC2" class="line" lang="plaintext"> aaa</span>
<span id="LC3" class="line" lang="plaintext"> aaa</span>
<span id="LC4" class="line" lang="plaintext"> aaa</span>
@@ -1551,90 +2223,111 @@ if present:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1755:1-1760:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
+<pre data-sourcepos="2164:1-2169:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
<span id="LC2" class="line" lang="plaintext"> aaa</span>
<span id="LC3" class="line" lang="plaintext">aaa</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1763:1-1763:56" dir="auto">Four spaces indentation produces an indented code block:</p>
+</div>
+<p data-sourcepos="2173:1-2173:56" dir="auto">Four spaces indentation produces an indented code block:</p>
+<div>
+<div><a href="#example-104">Example 104</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1765:1-1769:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ```</span>
+<pre data-sourcepos="2178:1-2182:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ```</span>
<span id="LC2" class="line" lang="plaintext"> aaa</span>
<span id="LC3" class="line" lang="plaintext"> ```</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1771:1-1776:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;```</span>
+<pre data-sourcepos="2184:1-2189:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;```</span>
<span id="LC2" class="line" lang="plaintext">aaa</span>
<span id="LC3" class="line" lang="plaintext">```</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1779:1-1780:41" dir="auto">Closing fences may be indented by 0-3 spaces, and their indentation
+</div>
+<p data-sourcepos="2193:1-2194:41" dir="auto">Closing fences may be indented by 0-3 spaces, and their indentation
need not match that of the opening fence:</p>
+<div>
+<div><a href="#example-105">Example 105</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1782:1-1786:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
+<pre data-sourcepos="2199:1-2203:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
<span id="LC2" class="line" lang="plaintext">aaa</span>
<span id="LC3" class="line" lang="plaintext"> ```</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1788:1-1791:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
+<pre data-sourcepos="2205:1-2208:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-106">Example 106</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1794:1-1798:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ```</span>
+<pre data-sourcepos="2215:1-2219:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ```</span>
<span id="LC2" class="line" lang="plaintext">aaa</span>
<span id="LC3" class="line" lang="plaintext"> ```</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1800:1-1803:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
+<pre data-sourcepos="2221:1-2224:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1806:1-1806:61" dir="auto">This is not a closing fence, because it is indented 4 spaces:</p>
+</div>
+<p data-sourcepos="2228:1-2228:61" dir="auto">This is not a closing fence, because it is indented 4 spaces:</p>
+<div>
+<div><a href="#example-107">Example 107</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1808:1-1812:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
+<pre data-sourcepos="2233:1-2237:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
<span id="LC2" class="line" lang="plaintext">aaa</span>
<span id="LC3" class="line" lang="plaintext"> ```</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1814:1-1818:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
+<pre data-sourcepos="2239:1-2243:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
<span id="LC2" class="line" lang="plaintext"> ```</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1822:1-1822:65" dir="auto">Code fences (opening and closing) cannot contain internal spaces:</p>
+</div>
+<p data-sourcepos="2248:1-2248:65" dir="auto">Code fences (opening and closing) cannot contain internal spaces:</p>
+<div>
+<div><a href="#example-108">Example 108</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1824:1-1827:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">``` ```</span>
+<pre data-sourcepos="2253:1-2256:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">``` ```</span>
<span id="LC2" class="line" lang="plaintext">aaa</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1829:1-1832:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt; &lt;/code&gt;</span>
+<pre data-sourcepos="2258:1-2261:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt; &lt;/code&gt;</span>
<span id="LC2" class="line" lang="plaintext">aaa&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-109">Example 109</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1835:1-1839:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~~~~~</span>
+<pre data-sourcepos="2268:1-2272:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~~~~~</span>
<span id="LC2" class="line" lang="plaintext">aaa</span>
<span id="LC3" class="line" lang="plaintext">~~~ ~~</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1841:1-1845:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
+<pre data-sourcepos="2274:1-2278:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
<span id="LC2" class="line" lang="plaintext">~~~ ~~</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1848:1-1849:53" dir="auto">Fenced code blocks can interrupt paragraphs, and can be followed
+</div>
+<p data-sourcepos="2282:1-2283:53" dir="auto">Fenced code blocks can interrupt paragraphs, and can be followed
directly by paragraphs, without a blank line between:</p>
+<div>
+<div><a href="#example-110">Example 110</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1851:1-1857:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo</span>
+<pre data-sourcepos="2288:1-2294:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo</span>
<span id="LC2" class="line" lang="plaintext">```</span>
<span id="LC3" class="line" lang="plaintext">bar</span>
<span id="LC4" class="line" lang="plaintext">```</span>
@@ -1642,16 +2335,19 @@ directly by paragraphs, without a blank line between:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1859:1-1864:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
+<pre data-sourcepos="2296:1-2301:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;bar</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;baz&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1867:1-1868:34" dir="auto">Other blocks can also occur before and after fenced code blocks
+</div>
+<p data-sourcepos="2305:1-2306:34" dir="auto">Other blocks can also occur before and after fenced code blocks
without an intervening blank line:</p>
+<div>
+<div><a href="#example-111">Example 111</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1870:1-1877:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo</span>
+<pre data-sourcepos="2311:1-2318:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo</span>
<span id="LC2" class="line" lang="plaintext">---</span>
<span id="LC3" class="line" lang="plaintext">~~~</span>
<span id="LC4" class="line" lang="plaintext">bar</span>
@@ -1660,20 +2356,23 @@ without an intervening blank line:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1879:1-1884:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;foo&lt;/h2&gt;</span>
+<pre data-sourcepos="2320:1-2325:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h2&gt;foo&lt;/h2&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;bar</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;h1&gt;baz&lt;/h1&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1887:1-1892:45" dir="auto">An [info string] can be provided after the opening code fence.
+</div>
+<p data-sourcepos="2329:1-2334:45" dir="auto">An [info string] can be provided after the opening code fence.
Although this spec doesn't mandate any particular treatment of
the info string, the first word is typically used to specify
the language of the code block. In HTML output, the language is
normally indicated by adding a class to the <code>code</code> element consisting
of <code>language-</code> followed by the language name.</p>
+<div>
+<div><a href="#example-112">Example 112</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1894:1-1900:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```ruby</span>
+<pre data-sourcepos="2339:1-2345:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```ruby</span>
<span id="LC2" class="line" lang="plaintext">def foo(x)</span>
<span id="LC3" class="line" lang="plaintext"> return 3</span>
<span id="LC4" class="line" lang="plaintext">end</span>
@@ -1681,14 +2380,17 @@ of <code>language-</code> followed by the language name.</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1902:1-1907:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code class="language-ruby"&gt;def foo(x)</span>
+<pre data-sourcepos="2347:1-2352:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code class="language-ruby"&gt;def foo(x)</span>
<span id="LC2" class="line" lang="plaintext"> return 3</span>
<span id="LC3" class="line" lang="plaintext">end</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-113">Example 113</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1910:1-1916:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~~~ ruby startline=3 $%@#$</span>
+<pre data-sourcepos="2359:1-2365:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~~~ ruby startline=3 $%@#$</span>
<span id="LC2" class="line" lang="plaintext">def foo(x)</span>
<span id="LC3" class="line" lang="plaintext"> return 3</span>
<span id="LC4" class="line" lang="plaintext">end</span>
@@ -1696,61 +2398,74 @@ of <code>language-</code> followed by the language name.</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1918:1-1923:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code class="language-ruby"&gt;def foo(x)</span>
+<pre data-sourcepos="2367:1-2372:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code class="language-ruby"&gt;def foo(x)</span>
<span id="LC2" class="line" lang="plaintext"> return 3</span>
<span id="LC3" class="line" lang="plaintext">end</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-114">Example 114</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1926:1-1929:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">````;</span>
+<pre data-sourcepos="2379:1-2382:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">````;</span>
<span id="LC2" class="line" lang="plaintext">````</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1931:1-1933:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code class="language-;"&gt;&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
+<pre data-sourcepos="2384:1-2386:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code class="language-;"&gt;&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1936:1-1936:65" dir="auto">[Info strings] for backtick code blocks cannot contain backticks:</p>
+</div>
+<p data-sourcepos="2390:1-2390:65" dir="auto">[Info strings] for backtick code blocks cannot contain backticks:</p>
+<div>
+<div><a href="#example-115">Example 115</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1938:1-1941:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">``` aa ```</span>
+<pre data-sourcepos="2395:1-2398:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">``` aa ```</span>
<span id="LC2" class="line" lang="plaintext">foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1943:1-1946:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;aa&lt;/code&gt;</span>
+<pre data-sourcepos="2400:1-2403:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;aa&lt;/code&gt;</span>
<span id="LC2" class="line" lang="plaintext">foo&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1949:1-1949:70" dir="auto">[Info strings] for tilde code blocks can contain backticks and tildes:</p>
+</div>
+<p data-sourcepos="2407:1-2407:70" dir="auto">[Info strings] for tilde code blocks can contain backticks and tildes:</p>
+<div>
+<div><a href="#example-116">Example 116</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1951:1-1955:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~~ aa ``` ~~~</span>
+<pre data-sourcepos="2412:1-2416:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~~ aa ``` ~~~</span>
<span id="LC2" class="line" lang="plaintext">foo</span>
<span id="LC3" class="line" lang="plaintext">~~~</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1957:1-1960:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code class="language-aa"&gt;foo</span>
+<pre data-sourcepos="2418:1-2421:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code class="language-aa"&gt;foo</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="1963:1-1963:47" dir="auto">Closing code fences cannot have [info strings]:</p>
+</div>
+<p data-sourcepos="2425:1-2425:47" dir="auto">Closing code fences cannot have [info strings]:</p>
+<div>
+<div><a href="#example-117">Example 117</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1965:1-1969:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
+<pre data-sourcepos="2430:1-2434:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
<span id="LC2" class="line" lang="plaintext">``` aaa</span>
<span id="LC3" class="line" lang="plaintext">```</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1971:1-1974:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;``` aaa</span>
+<pre data-sourcepos="2436:1-2439:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;``` aaa</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="1978:1-1978:14" dir="auto">
+</div>
+<h2 data-sourcepos="2444:1-2444:14" dir="auto">
<a id="user-content-html-blocks" class="anchor" href="#html-blocks" aria-hidden="true"></a>HTML blocks</h2>
-<p data-sourcepos="1980:1-1981:53" dir="auto">An <a href="@">HTML block</a> is a group of lines that is treated
+<p data-sourcepos="2446:1-2447:53" dir="auto">An <a href="@">HTML block</a> is a group of lines that is treated
as raw HTML (and will not be escaped in HTML output).</p>
-<p data-sourcepos="1983:1-1991:50" dir="auto">There are seven kinds of [HTML block], which can be defined by their
+<p data-sourcepos="2449:1-2457:50" dir="auto">There are seven kinds of [HTML block], which can be defined by their
start and end conditions. The block begins with a line that meets a
<a href="@">start condition</a> (after up to three spaces optional indentation).
It ends with the first subsequent line that meets a matching <a href="@">end
@@ -1759,35 +2474,35 @@ the <a href="#container-blocks">container block</a> containing the current HTML
block, if no line is encountered that meets the [end condition]. If
the first line meets both the [start condition] and the [end
condition], the block will contain just that line.</p>
-<ol data-sourcepos="1993:1-2034:0" dir="auto">
-<li data-sourcepos="1993:1-1999:0">
-<p data-sourcepos="1993:5-1998:30"><strong>Start condition:</strong> line begins with the string <code>&lt;script</code>,
+<ol data-sourcepos="2459:1-2500:0" dir="auto">
+<li data-sourcepos="2459:1-2465:0">
+<p data-sourcepos="2459:5-2464:30"><strong>Start condition:</strong> line begins with the string <code>&lt;script</code>,
<code>&lt;pre</code>, or <code>&lt;style</code> (case-insensitive), followed by whitespace,
the string <code>&gt;</code>, or the end of the line.<br>
<strong>End condition:</strong> line contains an end tag
<code>&lt;/script&gt;</code>, <code>&lt;/pre&gt;</code>, or <code>&lt;/style&gt;</code> (case-insensitive; it
need not match the start tag).</p>
</li>
-<li data-sourcepos="2000:1-2002:0">
-<p data-sourcepos="2000:5-2001:51"><strong>Start condition:</strong> line begins with the string <code>&lt;!--</code>.<br>
+<li data-sourcepos="2466:1-2468:0">
+<p data-sourcepos="2466:5-2467:51"><strong>Start condition:</strong> line begins with the string <code>&lt;!--</code>.<br>
<strong>End condition:</strong> line contains the string <code>--&gt;</code>.</p>
</li>
-<li data-sourcepos="2003:1-2005:0">
-<p data-sourcepos="2003:5-2004:49"><strong>Start condition:</strong> line begins with the string <code>&lt;?</code>.<br>
+<li data-sourcepos="2469:1-2471:0">
+<p data-sourcepos="2469:5-2470:49"><strong>Start condition:</strong> line begins with the string <code>&lt;?</code>.<br>
<strong>End condition:</strong> line contains the string <code>?&gt;</code>.</p>
</li>
-<li data-sourcepos="2006:1-2009:0">
-<p data-sourcepos="2006:5-2008:51"><strong>Start condition:</strong> line begins with the string <code>&lt;!</code>
+<li data-sourcepos="2472:1-2475:0">
+<p data-sourcepos="2472:5-2474:51"><strong>Start condition:</strong> line begins with the string <code>&lt;!</code>
followed by an uppercase ASCII letter.<br>
<strong>End condition:</strong> line contains the character <code>&gt;</code>.</p>
</li>
-<li data-sourcepos="2010:1-2013:0">
-<p data-sourcepos="2010:5-2012:50"><strong>Start condition:</strong> line begins with the string
+<li data-sourcepos="2476:1-2479:0">
+<p data-sourcepos="2476:5-2478:50"><strong>Start condition:</strong> line begins with the string
<code>&lt;![CDATA[</code>.<br>
<strong>End condition:</strong> line contains the string <code>]]&gt;</code>.</p>
</li>
-<li data-sourcepos="2014:1-2028:0">
-<p data-sourcepos="2014:5-2027:54"><strong>Start condition:</strong> line begins the string <code>&lt;</code> or <code>&lt;/</code>
+<li data-sourcepos="2480:1-2494:0">
+<p data-sourcepos="2480:5-2493:54"><strong>Start condition:</strong> line begins the string <code>&lt;</code> or <code>&lt;/</code>
followed by one of the strings (case-insensitive) <code>address</code>,
<code>article</code>, <code>aside</code>, <code>base</code>, <code>basefont</code>, <code>blockquote</code>, <code>body</code>,
<code>caption</code>, <code>center</code>, <code>col</code>, <code>colgroup</code>, <code>dd</code>, <code>details</code>, <code>dialog</code>,
@@ -1802,25 +2517,27 @@ by [whitespace], the end of the line, the string <code>&gt;</code>, or
the string <code>/&gt;</code>.<br>
<strong>End condition:</strong> line is followed by a [blank line].</p>
</li>
-<li data-sourcepos="2029:1-2034:0">
-<p data-sourcepos="2029:5-2033:54"><strong>Start condition:</strong> line begins with a complete [open tag]
+<li data-sourcepos="2495:1-2500:0">
+<p data-sourcepos="2495:5-2499:54"><strong>Start condition:</strong> line begins with a complete [open tag]
(with any [tag name] other than <code>script</code>,
<code>style</code>, or <code>pre</code>) or a complete [closing tag],
followed only by [whitespace] or the end of the line.<br>
<strong>End condition:</strong> line is followed by a [blank line].</p>
</li>
</ol>
-<p data-sourcepos="2035:1-2040:19" dir="auto">HTML blocks continue until they are closed by their appropriate
+<p data-sourcepos="2501:1-2506:19" dir="auto">HTML blocks continue until they are closed by their appropriate
[end condition], or the last line of the document or other <a href="#container-blocks">container
block</a>. This means any HTML <strong>within an HTML
block</strong> that might otherwise be recognised as a start condition will
be ignored by the parser and passed through as-is, without changing
the parser's state.</p>
-<p data-sourcepos="2042:1-2044:51" dir="auto">For instance, <code>&lt;pre&gt;</code> within a HTML block started by <code>&lt;table&gt;</code> will not affect
+<p data-sourcepos="2508:1-2510:51" dir="auto">For instance, <code>&lt;pre&gt;</code> within a HTML block started by <code>&lt;table&gt;</code> will not affect
the parser state; as the HTML block was started in by start condition 6, it
will end at any blank line. This can be surprising:</p>
+<div>
+<div><a href="#example-118">Example 118</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2046:1-2054:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;&lt;tr&gt;&lt;td&gt;</span>
+<pre data-sourcepos="2515:1-2523:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;&lt;tr&gt;&lt;td&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;pre&gt;</span>
<span id="LC3" class="line" lang="plaintext">**Hello**,</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -1830,7 +2547,7 @@ will end at any blank line. This can be surprising:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2056:1-2063:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;&lt;tr&gt;&lt;td&gt;</span>
+<pre data-sourcepos="2525:1-2532:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;&lt;tr&gt;&lt;td&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;pre&gt;</span>
<span id="LC3" class="line" lang="plaintext">**Hello**,</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;world&lt;/em&gt;.</span>
@@ -1838,17 +2555,20 @@ will end at any blank line. This can be surprising:</p>
<span id="LC6" class="line" lang="plaintext">&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2065:1-2067:55" dir="auto">In this case, the HTML block is terminated by the newline — the <code>**Hello**</code>
+</div>
+<p data-sourcepos="2535:1-2537:55" dir="auto">In this case, the HTML block is terminated by the newline — the <code>**Hello**</code>
text remains verbatim — and regular parsing resumes, with a paragraph,
emphasised <code>world</code> and inline and block HTML following.</p>
-<p data-sourcepos="2069:1-2072:65" dir="auto">All types of [HTML blocks] except type 7 may interrupt
+<p data-sourcepos="2539:1-2542:65" dir="auto">All types of [HTML blocks] except type 7 may interrupt
a paragraph. Blocks of type 7 may not interrupt a paragraph.
(This restriction is intended to prevent unwanted interpretation
of long tags inside a wrapped paragraph as starting HTML blocks.)</p>
-<p data-sourcepos="2074:1-2075:10" dir="auto">Some simple examples follow. Here are some basic HTML blocks
+<p data-sourcepos="2544:1-2545:10" dir="auto">Some simple examples follow. Here are some basic HTML blocks
of type 6:</p>
+<div>
+<div><a href="#example-119">Example 119</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2077:1-2087:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
+<pre data-sourcepos="2550:1-2560:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
<span id="LC2" class="line" lang="plaintext"> &lt;tr&gt;</span>
<span id="LC3" class="line" lang="plaintext"> &lt;td&gt;</span>
<span id="LC4" class="line" lang="plaintext"> hi</span>
@@ -1860,7 +2580,7 @@ of type 6:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2089:1-2098:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
+<pre data-sourcepos="2562:1-2571:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
<span id="LC2" class="line" lang="plaintext"> &lt;tr&gt;</span>
<span id="LC3" class="line" lang="plaintext"> &lt;td&gt;</span>
<span id="LC4" class="line" lang="plaintext"> hi</span>
@@ -1870,32 +2590,41 @@ of type 6:</p>
<span id="LC8" class="line" lang="plaintext">&lt;p&gt;okay.&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-120">Example 120</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2101:1-2105:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &lt;div&gt;</span>
+<pre data-sourcepos="2578:1-2582:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &lt;div&gt;</span>
<span id="LC2" class="line" lang="plaintext"> *hello*</span>
<span id="LC3" class="line" lang="plaintext"> &lt;foo&gt;&lt;a&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2107:1-2111:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &lt;div&gt;</span>
+<pre data-sourcepos="2584:1-2588:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &lt;div&gt;</span>
<span id="LC2" class="line" lang="plaintext"> *hello*</span>
<span id="LC3" class="line" lang="plaintext"> &lt;foo&gt;&lt;a&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2114:1-2114:42" dir="auto">A block can also start with a closing tag:</p>
+</div>
+<p data-sourcepos="2592:1-2592:42" dir="auto">A block can also start with a closing tag:</p>
+<div>
+<div><a href="#example-121">Example 121</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2116:1-2119:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;/div&gt;</span>
+<pre data-sourcepos="2597:1-2600:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;/div&gt;</span>
<span id="LC2" class="line" lang="plaintext">*foo*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2121:1-2124:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;/div&gt;</span>
+<pre data-sourcepos="2602:1-2605:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;/div&gt;</span>
<span id="LC2" class="line" lang="plaintext">*foo*</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2127:1-2127:68" dir="auto">Here we have two HTML blocks with a Markdown paragraph between them:</p>
+</div>
+<p data-sourcepos="2609:1-2609:68" dir="auto">Here we have two HTML blocks with a Markdown paragraph between them:</p>
+<div>
+<div><a href="#example-122">Example 122</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2129:1-2135:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;DIV CLASS="foo"&gt;</span>
+<pre data-sourcepos="2614:1-2620:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;DIV CLASS="foo"&gt;</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">*Markdown*</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -1903,198 +2632,243 @@ of type 6:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2137:1-2141:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;DIV CLASS="foo"&gt;</span>
+<pre data-sourcepos="2622:1-2626:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;DIV CLASS="foo"&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;Markdown&lt;/em&gt;&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/DIV&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2144:1-2145:47" dir="auto">The tag on the first line can be partial, as long
+</div>
+<p data-sourcepos="2630:1-2631:47" dir="auto">The tag on the first line can be partial, as long
as it is split where there would be whitespace:</p>
+<div>
+<div><a href="#example-123">Example 123</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2147:1-2151:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div id="foo"</span>
+<pre data-sourcepos="2636:1-2640:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div id="foo"</span>
<span id="LC2" class="line" lang="plaintext"> class="bar"&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/div&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2153:1-2157:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div id="foo"</span>
+<pre data-sourcepos="2642:1-2646:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div id="foo"</span>
<span id="LC2" class="line" lang="plaintext"> class="bar"&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/div&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-124">Example 124</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2160:1-2164:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div id="foo" class="bar</span>
+<pre data-sourcepos="2653:1-2657:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div id="foo" class="bar</span>
<span id="LC2" class="line" lang="plaintext"> baz"&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/div&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2166:1-2170:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div id="foo" class="bar</span>
+<pre data-sourcepos="2659:1-2663:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div id="foo" class="bar</span>
<span id="LC2" class="line" lang="plaintext"> baz"&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/div&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2173:1-2173:31" dir="auto">An open tag need not be closed:</p>
+</div>
+<p data-sourcepos="2667:1-2667:31" dir="auto">An open tag need not be closed:</p>
+<div>
+<div><a href="#example-125">Example 125</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2174:1-2179:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;</span>
+<pre data-sourcepos="2671:1-2676:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;</span>
<span id="LC2" class="line" lang="plaintext">*foo*</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">*bar*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2181:1-2185:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;</span>
+<pre data-sourcepos="2678:1-2682:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;</span>
<span id="LC2" class="line" lang="plaintext">*foo*</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;bar&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2189:1-2190:17" dir="auto">A partial tag need not even be completed (garbage
+</div>
+<p data-sourcepos="2687:1-2688:17" dir="auto">A partial tag need not even be completed (garbage
in, garbage out):</p>
+<div>
+<div><a href="#example-126">Example 126</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2192:1-2195:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div id="foo"</span>
+<pre data-sourcepos="2693:1-2696:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div id="foo"</span>
<span id="LC2" class="line" lang="plaintext">*hi*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2197:1-2200:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div id="foo"</span>
+<pre data-sourcepos="2698:1-2701:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div id="foo"</span>
<span id="LC2" class="line" lang="plaintext">*hi*</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-127">Example 127</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2203:1-2206:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div class</span>
+<pre data-sourcepos="2708:1-2711:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div class</span>
<span id="LC2" class="line" lang="plaintext">foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2208:1-2211:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div class</span>
+<pre data-sourcepos="2713:1-2716:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div class</span>
<span id="LC2" class="line" lang="plaintext">foo</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2214:1-2215:35" dir="auto">The initial tag doesn't even need to be a valid
+</div>
+<p data-sourcepos="2720:1-2721:35" dir="auto">The initial tag doesn't even need to be a valid
tag, as long as it starts like one:</p>
+<div>
+<div><a href="#example-128">Example 128</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2217:1-2220:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div *???-&amp;&amp;&amp;-&lt;---</span>
+<pre data-sourcepos="2726:1-2729:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div *???-&amp;&amp;&amp;-&lt;---</span>
<span id="LC2" class="line" lang="plaintext">*foo*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2222:1-2225:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div *???-&amp;&amp;&amp;-&lt;---</span>
+<pre data-sourcepos="2731:1-2734:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div *???-&amp;&amp;&amp;-&lt;---</span>
<span id="LC2" class="line" lang="plaintext">*foo*</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2228:1-2229:7" dir="auto">In type 6 blocks, the initial tag need not be on a line by
+</div>
+<p data-sourcepos="2738:1-2739:7" dir="auto">In type 6 blocks, the initial tag need not be on a line by
itself:</p>
+<div>
+<div><a href="#example-129">Example 129</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2231:1-2233:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;&lt;a href="bar"&gt;*foo*&lt;/a&gt;&lt;/div&gt;</span></code></pre>
+<pre data-sourcepos="2744:1-2746:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;&lt;a href="bar"&gt;*foo*&lt;/a&gt;&lt;/div&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2235:1-2237:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;&lt;a href="bar"&gt;*foo*&lt;/a&gt;&lt;/div&gt;</span></code></pre>
+<pre data-sourcepos="2748:1-2750:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;&lt;a href="bar"&gt;*foo*&lt;/a&gt;&lt;/div&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-130">Example 130</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2240:1-2244:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;&lt;tr&gt;&lt;td&gt;</span>
+<pre data-sourcepos="2757:1-2761:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;&lt;tr&gt;&lt;td&gt;</span>
<span id="LC2" class="line" lang="plaintext">foo</span>
<span id="LC3" class="line" lang="plaintext">&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2246:1-2250:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;&lt;tr&gt;&lt;td&gt;</span>
+<pre data-sourcepos="2763:1-2767:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;&lt;tr&gt;&lt;td&gt;</span>
<span id="LC2" class="line" lang="plaintext">foo</span>
<span id="LC3" class="line" lang="plaintext">&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2253:1-2257:43" dir="auto">Everything until the next blank line or end of document
+</div>
+<p data-sourcepos="2771:1-2775:43" dir="auto">Everything until the next blank line or end of document
gets included in the HTML block. So, in the following
example, what looks like a Markdown code block
is actually part of the HTML block, which continues until a blank
line or the end of the document is reached:</p>
+<div>
+<div><a href="#example-131">Example 131</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2259:1-2264:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;&lt;/div&gt;</span>
+<pre data-sourcepos="2780:1-2785:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;&lt;/div&gt;</span>
<span id="LC2" class="line" lang="plaintext">``` c</span>
<span id="LC3" class="line" lang="plaintext">int x = 33;</span>
<span id="LC4" class="line" lang="plaintext">```</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2266:1-2271:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;&lt;/div&gt;</span>
+<pre data-sourcepos="2787:1-2792:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;&lt;/div&gt;</span>
<span id="LC2" class="line" lang="plaintext">``` c</span>
<span id="LC3" class="line" lang="plaintext">int x = 33;</span>
<span id="LC4" class="line" lang="plaintext">```</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2274:1-2276:51" dir="auto">To start an [HTML block] with a tag that is <em>not</em> in the
+</div>
+<p data-sourcepos="2796:1-2798:51" dir="auto">To start an [HTML block] with a tag that is <em>not</em> in the
list of block-level tags in (6), you must put the tag by
itself on the first line (and it must be complete):</p>
+<div>
+<div><a href="#example-132">Example 132</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2278:1-2282:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="foo"&gt;</span>
+<pre data-sourcepos="2803:1-2807:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="foo"&gt;</span>
<span id="LC2" class="line" lang="plaintext">*bar*</span>
<span id="LC3" class="line" lang="plaintext">&lt;/a&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2284:1-2288:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="foo"&gt;</span>
+<pre data-sourcepos="2809:1-2813:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="foo"&gt;</span>
<span id="LC2" class="line" lang="plaintext">*bar*</span>
<span id="LC3" class="line" lang="plaintext">&lt;/a&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2291:1-2291:49" dir="auto">In type 7 blocks, the [tag name] can be anything:</p>
+</div>
+<p data-sourcepos="2817:1-2817:49" dir="auto">In type 7 blocks, the [tag name] can be anything:</p>
+<div>
+<div><a href="#example-133">Example 133</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2293:1-2297:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;Warning&gt;</span>
+<pre data-sourcepos="2822:1-2826:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;Warning&gt;</span>
<span id="LC2" class="line" lang="plaintext">*bar*</span>
<span id="LC3" class="line" lang="plaintext">&lt;/Warning&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2299:1-2303:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;Warning&gt;</span>
+<pre data-sourcepos="2828:1-2832:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;Warning&gt;</span>
<span id="LC2" class="line" lang="plaintext">*bar*</span>
<span id="LC3" class="line" lang="plaintext">&lt;/Warning&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-134">Example 134</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2306:1-2310:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;i class="foo"&gt;</span>
+<pre data-sourcepos="2839:1-2843:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;i class="foo"&gt;</span>
<span id="LC2" class="line" lang="plaintext">*bar*</span>
<span id="LC3" class="line" lang="plaintext">&lt;/i&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2312:1-2316:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;i class="foo"&gt;</span>
+<pre data-sourcepos="2845:1-2849:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;i class="foo"&gt;</span>
<span id="LC2" class="line" lang="plaintext">*bar*</span>
<span id="LC3" class="line" lang="plaintext">&lt;/i&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-135">Example 135</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2319:1-2322:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;/ins&gt;</span>
+<pre data-sourcepos="2856:1-2859:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;/ins&gt;</span>
<span id="LC2" class="line" lang="plaintext">*bar*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2324:1-2327:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;/ins&gt;</span>
+<pre data-sourcepos="2861:1-2864:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;/ins&gt;</span>
<span id="LC2" class="line" lang="plaintext">*bar*</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2330:1-2334:59" dir="auto">These rules are designed to allow us to work with tags that
+</div>
+<p data-sourcepos="2868:1-2872:59" dir="auto">These rules are designed to allow us to work with tags that
can function as either block-level or inline-level tags.
The <code>&lt;del&gt;</code> tag is a nice example. We can surround content with
<code>&lt;del&gt;</code> tags in three different ways. In this case, we get a raw
HTML block, because the <code>&lt;del&gt;</code> tag is on a line by itself:</p>
+<div>
+<div><a href="#example-136">Example 136</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2336:1-2340:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;del&gt;</span>
+<pre data-sourcepos="2877:1-2881:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;del&gt;</span>
<span id="LC2" class="line" lang="plaintext">*foo*</span>
<span id="LC3" class="line" lang="plaintext">&lt;/del&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2342:1-2346:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;del&gt;</span>
+<pre data-sourcepos="2883:1-2887:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;del&gt;</span>
<span id="LC2" class="line" lang="plaintext">*foo*</span>
<span id="LC3" class="line" lang="plaintext">&lt;/del&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2349:1-2351:54" dir="auto">In this case, we get a raw HTML block that just includes
+</div>
+<p data-sourcepos="2891:1-2893:54" dir="auto">In this case, we get a raw HTML block that just includes
the <code>&lt;del&gt;</code> tag (because it ends with the following blank
line). So the contents get interpreted as CommonMark:</p>
+<div>
+<div><a href="#example-137">Example 137</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2353:1-2359:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;del&gt;</span>
+<pre data-sourcepos="2898:1-2904:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;del&gt;</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">*foo*</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -2102,32 +2876,38 @@ line). So the contents get interpreted as CommonMark:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2361:1-2365:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;del&gt;</span>
+<pre data-sourcepos="2906:1-2910:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;del&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;/em&gt;&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/del&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2368:1-2371:29" dir="auto">Finally, in this case, the <code>&lt;del&gt;</code> tags are interpreted
+</div>
+<p data-sourcepos="2914:1-2917:29" dir="auto">Finally, in this case, the <code>&lt;del&gt;</code> tags are interpreted
as [raw HTML] <em>inside</em> the CommonMark paragraph. (Because
the tag is not on a line by itself, we get inline HTML
rather than an [HTML block].)</p>
+<div>
+<div><a href="#example-138">Example 138</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2373:1-2375:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;del&gt;*foo*&lt;/del&gt;</span></code></pre>
+<pre data-sourcepos="2922:1-2924:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;del&gt;*foo*&lt;/del&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2377:1-2379:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;del&gt;&lt;em&gt;foo&lt;/em&gt;&lt;/del&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="2926:1-2928:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;del&gt;&lt;em&gt;foo&lt;/em&gt;&lt;/del&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2382:1-2387:50" dir="auto">HTML tags designed to contain literal content
+</div>
+<p data-sourcepos="2932:1-2937:50" dir="auto">HTML tags designed to contain literal content
(<code>script</code>, <code>style</code>, <code>pre</code>), comments, processing instructions,
and declarations are treated somewhat differently.
Instead of ending at the first blank line, these blocks
end at the first line containing a corresponding end tag.
As a result, these blocks can contain blank lines:</p>
-<p data-sourcepos="2389:1-2389:19" dir="auto">A pre tag (type 1):</p>
+<p data-sourcepos="2939:1-2939:19" dir="auto">A pre tag (type 1):</p>
+<div>
+<div><a href="#example-139">Example 139</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2391:1-2399:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre language="haskell"&gt;&lt;code&gt;</span>
+<pre data-sourcepos="2944:1-2952:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre language="haskell"&gt;&lt;code&gt;</span>
<span id="LC2" class="line" lang="plaintext">import Text.HTML.TagSoup</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">main :: IO ()</span>
@@ -2137,7 +2917,7 @@ As a result, these blocks can contain blank lines:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2401:1-2409:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre language="haskell"&gt;&lt;code&gt;</span>
+<pre data-sourcepos="2954:1-2962:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre language="haskell"&gt;&lt;code&gt;</span>
<span id="LC2" class="line" lang="plaintext">import Text.HTML.TagSoup</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">main :: IO ()</span>
@@ -2146,9 +2926,12 @@ As a result, these blocks can contain blank lines:</p>
<span id="LC7" class="line" lang="plaintext">&lt;p&gt;okay&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2412:1-2412:22" dir="auto">A script tag (type 1):</p>
+</div>
+<p data-sourcepos="2966:1-2966:22" dir="auto">A script tag (type 1):</p>
+<div>
+<div><a href="#example-140">Example 140</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2414:1-2421:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;script type="text/javascript"&gt;</span>
+<pre data-sourcepos="2971:1-2978:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;script type="text/javascript"&gt;</span>
<span id="LC2" class="line" lang="plaintext">// JavaScript example</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">document.getElementById("demo").innerHTML = "Hello JavaScript!";</span>
@@ -2157,7 +2940,7 @@ As a result, these blocks can contain blank lines:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2423:1-2430:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;script type="text/javascript"&gt;</span>
+<pre data-sourcepos="2980:1-2987:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;script type="text/javascript"&gt;</span>
<span id="LC2" class="line" lang="plaintext">// JavaScript example</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">document.getElementById("demo").innerHTML = "Hello JavaScript!";</span>
@@ -2165,9 +2948,12 @@ As a result, these blocks can contain blank lines:</p>
<span id="LC6" class="line" lang="plaintext">&lt;p&gt;okay&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2433:1-2433:21" dir="auto">A style tag (type 1):</p>
+</div>
+<p data-sourcepos="2991:1-2991:21" dir="auto">A style tag (type 1):</p>
+<div>
+<div><a href="#example-141">Example 141</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2435:1-2443:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;style</span>
+<pre data-sourcepos="2996:1-3004:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;style</span>
<span id="LC2" class="line" lang="plaintext"> type="text/css"&gt;</span>
<span id="LC3" class="line" lang="plaintext">h1 {color:red;}</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -2177,7 +2963,7 @@ As a result, these blocks can contain blank lines:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2445:1-2453:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;style</span>
+<pre data-sourcepos="3006:1-3014:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;style</span>
<span id="LC2" class="line" lang="plaintext"> type="text/css"&gt;</span>
<span id="LC3" class="line" lang="plaintext">h1 {color:red;}</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -2186,45 +2972,54 @@ As a result, these blocks can contain blank lines:</p>
<span id="LC7" class="line" lang="plaintext">&lt;p&gt;okay&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2456:1-2458:28" dir="auto">If there is no matching end tag, the block will end at the
+</div>
+<p data-sourcepos="3018:1-3020:28" dir="auto">If there is no matching end tag, the block will end at the
end of the document (or the enclosing [block quote][block quotes]
or [list item][list items]):</p>
+<div>
+<div><a href="#example-142">Example 142</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2460:1-2465:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;style</span>
+<pre data-sourcepos="3025:1-3030:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;style</span>
<span id="LC2" class="line" lang="plaintext"> type="text/css"&gt;</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2467:1-2472:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;style</span>
+<pre data-sourcepos="3032:1-3037:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;style</span>
<span id="LC2" class="line" lang="plaintext"> type="text/css"&gt;</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">foo</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-143">Example 143</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2475:1-2480:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; &lt;div&gt;</span>
+<pre data-sourcepos="3044:1-3049:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; &lt;div&gt;</span>
<span id="LC2" class="line" lang="plaintext">&gt; foo</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2482:1-2488:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="3051:1-3057:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;div&gt;</span>
<span id="LC3" class="line" lang="plaintext">foo</span>
<span id="LC4" class="line" lang="plaintext">&lt;/blockquote&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-144">Example 144</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2491:1-2494:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- &lt;div&gt;</span>
+<pre data-sourcepos="3064:1-3067:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- &lt;div&gt;</span>
<span id="LC2" class="line" lang="plaintext">- foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2496:1-2503:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="3069:1-3076:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;div&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/li&gt;</span>
@@ -2232,44 +3027,56 @@ or [list item][list items]):</p>
<span id="LC6" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2506:1-2506:56" dir="auto">The end tag can occur on the same line as the start tag:</p>
+</div>
+<p data-sourcepos="3080:1-3080:56" dir="auto">The end tag can occur on the same line as the start tag:</p>
+<div>
+<div><a href="#example-145">Example 145</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2508:1-2511:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;style&gt;p{color:red;}&lt;/style&gt;</span>
+<pre data-sourcepos="3085:1-3088:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;style&gt;p{color:red;}&lt;/style&gt;</span>
<span id="LC2" class="line" lang="plaintext">*foo*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2513:1-2516:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;style&gt;p{color:red;}&lt;/style&gt;</span>
+<pre data-sourcepos="3090:1-3093:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;style&gt;p{color:red;}&lt;/style&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-146">Example 146</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2519:1-2522:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;!-- foo --&gt;*bar*</span>
+<pre data-sourcepos="3100:1-3103:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;!-- foo --&gt;*bar*</span>
<span id="LC2" class="line" lang="plaintext">*baz*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2524:1-2527:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;!-- foo --&gt;*bar*</span>
+<pre data-sourcepos="3105:1-3108:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;!-- foo --&gt;*bar*</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;baz&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2530:1-2531:45" dir="auto">Note that anything on the last line after the
+</div>
+<p data-sourcepos="3112:1-3113:45" dir="auto">Note that anything on the last line after the
end tag will be included in the [HTML block]:</p>
+<div>
+<div><a href="#example-147">Example 147</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2533:1-2537:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;script&gt;</span>
+<pre data-sourcepos="3118:1-3122:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;script&gt;</span>
<span id="LC2" class="line" lang="plaintext">foo</span>
<span id="LC3" class="line" lang="plaintext">&lt;/script&gt;1. *bar*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2539:1-2543:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;script&gt;</span>
+<pre data-sourcepos="3124:1-3128:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;script&gt;</span>
<span id="LC2" class="line" lang="plaintext">foo</span>
<span id="LC3" class="line" lang="plaintext">&lt;/script&gt;1. *bar*</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2546:1-2546:19" dir="auto">A comment (type 2):</p>
+</div>
+<p data-sourcepos="3132:1-3132:19" dir="auto">A comment (type 2):</p>
+<div>
+<div><a href="#example-148">Example 148</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2548:1-2554:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;!-- Foo</span>
+<pre data-sourcepos="3137:1-3143:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;!-- Foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">bar</span>
<span id="LC4" class="line" lang="plaintext"> baz --&gt;</span>
@@ -2277,16 +3084,19 @@ end tag will be included in the [HTML block]:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2556:1-2562:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;!-- Foo</span>
+<pre data-sourcepos="3145:1-3151:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;!-- Foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">bar</span>
<span id="LC4" class="line" lang="plaintext"> baz --&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;p&gt;okay&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2566:1-2566:34" dir="auto">A processing instruction (type 3):</p>
+</div>
+<p data-sourcepos="3156:1-3156:34" dir="auto">A processing instruction (type 3):</p>
+<div>
+<div><a href="#example-149">Example 149</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2568:1-2575:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;?php</span>
+<pre data-sourcepos="3161:1-3168:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;?php</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> echo '&gt;';</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -2295,7 +3105,7 @@ end tag will be included in the [HTML block]:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2577:1-2584:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;?php</span>
+<pre data-sourcepos="3170:1-3177:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;?php</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> echo '&gt;';</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -2303,18 +3113,24 @@ end tag will be included in the [HTML block]:</p>
<span id="LC6" class="line" lang="plaintext">&lt;p&gt;okay&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2587:1-2587:23" dir="auto">A declaration (type 4):</p>
+</div>
+<p data-sourcepos="3181:1-3181:23" dir="auto">A declaration (type 4):</p>
+<div>
+<div><a href="#example-150">Example 150</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2589:1-2591:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;!DOCTYPE html&gt;</span></code></pre>
+<pre data-sourcepos="3186:1-3188:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;!DOCTYPE html&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2593:1-2595:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;!DOCTYPE html&gt;</span></code></pre>
+<pre data-sourcepos="3190:1-3192:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;!DOCTYPE html&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2598:1-2598:15" dir="auto">CDATA (type 5):</p>
+</div>
+<p data-sourcepos="3196:1-3196:15" dir="auto">CDATA (type 5):</p>
+<div>
+<div><a href="#example-151">Example 151</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2600:1-2614:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;![CDATA[</span>
+<pre data-sourcepos="3201:1-3215:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;![CDATA[</span>
<span id="LC2" class="line" lang="plaintext">function matchwo(a,b)</span>
<span id="LC3" class="line" lang="plaintext">{</span>
<span id="LC4" class="line" lang="plaintext"> if (a &lt; b &amp;&amp; a &lt; 0) then {</span>
@@ -2330,7 +3146,7 @@ end tag will be included in the [HTML block]:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2616:1-2630:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;![CDATA[</span>
+<pre data-sourcepos="3217:1-3231:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;![CDATA[</span>
<span id="LC2" class="line" lang="plaintext">function matchwo(a,b)</span>
<span id="LC3" class="line" lang="plaintext">{</span>
<span id="LC4" class="line" lang="plaintext"> if (a &lt; b &amp;&amp; a &lt; 0) then {</span>
@@ -2345,96 +3161,112 @@ end tag will be included in the [HTML block]:</p>
<span id="LC13" class="line" lang="plaintext">&lt;p&gt;okay&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2633:1-2633:54" dir="auto">The opening tag can be indented 1-3 spaces, but not 4:</p>
+</div>
+<p data-sourcepos="3235:1-3235:54" dir="auto">The opening tag can be indented 1-3 spaces, but not 4:</p>
+<div>
+<div><a href="#example-152">Example 152</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2635:1-2639:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &lt;!-- foo --&gt;</span>
+<pre data-sourcepos="3240:1-3244:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &lt;!-- foo --&gt;</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> &lt;!-- foo --&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2641:1-2645:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &lt;!-- foo --&gt;</span>
+<pre data-sourcepos="3246:1-3250:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &lt;!-- foo --&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&amp;lt;!-- foo --&amp;gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-153">Example 153</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2648:1-2652:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &lt;div&gt;</span>
+<pre data-sourcepos="3257:1-3261:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &lt;div&gt;</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> &lt;div&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2654:1-2658:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &lt;div&gt;</span>
+<pre data-sourcepos="3263:1-3267:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &lt;div&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&amp;lt;div&amp;gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2661:1-2662:25" dir="auto">An HTML block of types 1--6 can interrupt a paragraph, and need not be
+</div>
+<p data-sourcepos="3271:1-3272:25" dir="auto">An HTML block of types 1--6 can interrupt a paragraph, and need not be
preceded by a blank line.</p>
+<div>
+<div><a href="#example-154">Example 154</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2664:1-2669:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="3277:1-3282:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext">&lt;div&gt;</span>
<span id="LC3" class="line" lang="plaintext">bar</span>
<span id="LC4" class="line" lang="plaintext">&lt;/div&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2671:1-2676:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo&lt;/p&gt;</span>
+<pre data-sourcepos="3284:1-3289:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;div&gt;</span>
<span id="LC3" class="line" lang="plaintext">bar</span>
<span id="LC4" class="line" lang="plaintext">&lt;/div&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2679:1-2681:7" dir="auto">However, a following blank line is needed, except at the end of
+</div>
+<p data-sourcepos="3293:1-3295:7" dir="auto">However, a following blank line is needed, except at the end of
a document, and except for blocks of types 1--5, [above][HTML
block]:</p>
+<div>
+<div><a href="#example-155">Example 155</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2683:1-2688:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;</span>
+<pre data-sourcepos="3300:1-3305:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;</span>
<span id="LC2" class="line" lang="plaintext">bar</span>
<span id="LC3" class="line" lang="plaintext">&lt;/div&gt;</span>
<span id="LC4" class="line" lang="plaintext">*foo*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2690:1-2695:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;</span>
+<pre data-sourcepos="3307:1-3312:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;</span>
<span id="LC2" class="line" lang="plaintext">bar</span>
<span id="LC3" class="line" lang="plaintext">&lt;/div&gt;</span>
<span id="LC4" class="line" lang="plaintext">*foo*</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2698:1-2698:51" dir="auto">HTML blocks of type 7 cannot interrupt a paragraph:</p>
+</div>
+<p data-sourcepos="3316:1-3316:51" dir="auto">HTML blocks of type 7 cannot interrupt a paragraph:</p>
+<div>
+<div><a href="#example-156">Example 156</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2700:1-2704:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="3321:1-3325:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext">&lt;a href="bar"&gt;</span>
<span id="LC3" class="line" lang="plaintext">baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2706:1-2710:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
+<pre data-sourcepos="3327:1-3331:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
<span id="LC2" class="line" lang="plaintext">&lt;a href="bar"&gt;</span>
<span id="LC3" class="line" lang="plaintext">baz&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2713:1-2714:26" dir="auto">This rule differs from John Gruber's original Markdown syntax
+</div>
+<p data-sourcepos="3335:1-3336:26" dir="auto">This rule differs from John Gruber's original Markdown syntax
specification, which says:</p>
-<blockquote data-sourcepos="2716:1-2719:51" dir="auto">
-<p data-sourcepos="2716:3-2719:51">The only restrictions are that block-level HTML elements —
+<blockquote data-sourcepos="3338:1-3341:51" dir="auto">
+<p data-sourcepos="3338:3-3341:51">The only restrictions are that block-level HTML elements —
e.g. <code>&lt;div&gt;</code>, <code>&lt;table&gt;</code>, <code>&lt;pre&gt;</code>, <code>&lt;p&gt;</code>, etc. — must be separated from
surrounding content by blank lines, and the start and end tags of the
block should not be indented with tabs or spaces.</p>
</blockquote>
-<p data-sourcepos="2721:1-2722:5" dir="auto">In some ways Gruber's rule is more restrictive than the one given
+<p data-sourcepos="3343:1-3344:5" dir="auto">In some ways Gruber's rule is more restrictive than the one given
here:</p>
-<ul data-sourcepos="2724:1-2728:0" dir="auto">
-<li data-sourcepos="2724:1-2724:61">It requires that an HTML block be preceded by a blank line.</li>
-<li data-sourcepos="2725:1-2725:49">It does not allow the start tag to be indented.</li>
-<li data-sourcepos="2726:1-2728:0">It requires a matching end tag, which it also does not allow to
+<ul data-sourcepos="3346:1-3350:0" dir="auto">
+<li data-sourcepos="3346:1-3346:61">It requires that an HTML block be preceded by a blank line.</li>
+<li data-sourcepos="3347:1-3347:49">It does not allow the start tag to be indented.</li>
+<li data-sourcepos="3348:1-3350:0">It requires a matching end tag, which it also does not allow to
be indented.</li>
</ul>
-<p data-sourcepos="2729:1-2730:34" dir="auto">Most Markdown implementations (including some of Gruber's own) do not
+<p data-sourcepos="3351:1-3352:34" dir="auto">Most Markdown implementations (including some of Gruber's own) do not
respect all of these restrictions.</p>
-<p data-sourcepos="2732:1-2739:61" dir="auto">There is one respect, however, in which Gruber's rule is more liberal
+<p data-sourcepos="3354:1-3361:61" dir="auto">There is one respect, however, in which Gruber's rule is more liberal
than the one given here, since it allows blank lines to occur inside
an HTML block. There are two reasons for disallowing them here.
First, it removes the need to parse balanced tags, which is
@@ -2442,9 +3274,11 @@ expensive and can require backtracking from the end of the document
if no matching end tag is found. Second, it provides a very simple
and flexible way of including Markdown content inside HTML tags:
simply separate the Markdown from the HTML using blank lines:</p>
-<p data-sourcepos="2741:1-2741:8" dir="auto">Compare:</p>
+<p data-sourcepos="3363:1-3363:8" dir="auto">Compare:</p>
+<div>
+<div><a href="#example-157">Example 157</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2743:1-2749:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;</span>
+<pre data-sourcepos="3368:1-3374:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">*Emphasized* text.</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -2452,34 +3286,40 @@ simply separate the Markdown from the HTML using blank lines:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2751:1-2755:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;</span>
+<pre data-sourcepos="3376:1-3380:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;Emphasized&lt;/em&gt; text.&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/div&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-158">Example 158</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2758:1-2762:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;</span>
+<pre data-sourcepos="3387:1-3391:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;</span>
<span id="LC2" class="line" lang="plaintext">*Emphasized* text.</span>
<span id="LC3" class="line" lang="plaintext">&lt;/div&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2764:1-2768:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;</span>
+<pre data-sourcepos="3393:1-3397:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;</span>
<span id="LC2" class="line" lang="plaintext">*Emphasized* text.</span>
<span id="LC3" class="line" lang="plaintext">&lt;/div&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2771:1-2775:22" dir="auto">Some Markdown implementations have adopted a convention of
+</div>
+<p data-sourcepos="3401:1-3405:22" dir="auto">Some Markdown implementations have adopted a convention of
interpreting content inside tags as text if the open tag has
the attribute <code>markdown=1</code>. The rule given above seems a simpler and
more elegant way of achieving the same expressive power, which is also
much simpler to parse.</p>
-<p data-sourcepos="2777:1-2780:59" dir="auto">The main potential drawback is that one can no longer paste HTML
+<p data-sourcepos="3407:1-3410:59" dir="auto">The main potential drawback is that one can no longer paste HTML
blocks into Markdown documents with 100% reliability. However,
<em>in most cases</em> this will work fine, because the blank lines in
HTML are usually followed by HTML block tags. For example:</p>
+<div>
+<div><a href="#example-159">Example 159</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2782:1-2794:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
+<pre data-sourcepos="3415:1-3427:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">&lt;tr&gt;</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -2493,7 +3333,7 @@ HTML are usually followed by HTML block tags. For example:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2796:1-2804:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
+<pre data-sourcepos="3429:1-3437:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;tr&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;td&gt;</span>
<span id="LC4" class="line" lang="plaintext">Hi</span>
@@ -2502,11 +3342,14 @@ HTML are usually followed by HTML block tags. For example:</p>
<span id="LC7" class="line" lang="plaintext">&lt;/table&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2807:1-2809:23" dir="auto">There are problems, however, if the inner tags are indented
+</div>
+<p data-sourcepos="3441:1-3443:23" dir="auto">There are problems, however, if the inner tags are indented
<em>and</em> separated by spaces, as then they will be interpreted as
an indented code block:</p>
+<div>
+<div><a href="#example-160">Example 160</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2811:1-2823:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
+<pre data-sourcepos="3448:1-3460:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> &lt;tr&gt;</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -2520,7 +3363,7 @@ an indented code block:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2825:1-2834:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
+<pre data-sourcepos="3462:1-3471:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
<span id="LC2" class="line" lang="plaintext"> &lt;tr&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&amp;lt;td&amp;gt;</span>
<span id="LC4" class="line" lang="plaintext"> Hi</span>
@@ -2530,13 +3373,14 @@ an indented code block:</p>
<span id="LC8" class="line" lang="plaintext">&lt;/table&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2837:1-2840:26" dir="auto">Fortunately, blank lines are usually not necessary and can be
+</div>
+<p data-sourcepos="3475:1-3478:26" dir="auto">Fortunately, blank lines are usually not necessary and can be
deleted. The exception is inside <code>&lt;pre&gt;</code> tags, but as described
[above][HTML blocks], raw HTML blocks starting with <code>&lt;pre&gt;</code>
<em>can</em> contain blank lines.</p>
-<h2 data-sourcepos="2842:1-2842:29" dir="auto">
+<h2 data-sourcepos="3480:1-3480:29" dir="auto">
<a id="user-content-link-reference-definitions" class="anchor" href="#link-reference-definitions" aria-hidden="true"></a>Link reference definitions</h2>
-<p data-sourcepos="2844:1-2852:61" dir="auto">A <a href="@">link reference definition</a>
+<p data-sourcepos="3482:1-3490:61" dir="auto">A <a href="@">link reference definition</a>
consists of a [link label], indented up to three spaces, followed
by a colon (<code>:</code>), optional [whitespace] (including up to one
[line ending]), a [link destination],
@@ -2545,24 +3389,29 @@ optional [whitespace] (including up to one
title], which if it is present must be separated
from the [link destination] by [whitespace].
No further [non-whitespace characters] may occur on the line.</p>
-<p data-sourcepos="2854:1-2859:5" dir="auto">A [link reference definition]
+<p data-sourcepos="3492:1-3497:5" dir="auto">A [link reference definition]
does not correspond to a structural element of a document. Instead, it
defines a label which can be used in [reference links]
and reference-style [images] elsewhere in the document. [Link
reference definitions] can come either before or after the links that use
them.</p>
+<div>
+<div><a href="#example-161">Example 161</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2861:1-2865:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url "title"</span>
+<pre data-sourcepos="3502:1-3506:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url "title"</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2867:1-2869:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="3508:1-3510:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-162">Example 162</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2872:1-2878:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> [foo]: </span>
+<pre data-sourcepos="3517:1-3523:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> [foo]: </span>
<span id="LC2" class="line" lang="plaintext"> /url </span>
<span id="LC3" class="line" lang="plaintext"> 'the title' </span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -2570,21 +3419,27 @@ them.</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2880:1-2882:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="the title"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="3525:1-3527:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="the title"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-163">Example 163</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2885:1-2889:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[Foo*bar\]]:my_(url) 'title (with parens)'</span>
+<pre data-sourcepos="3534:1-3538:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[Foo*bar\]]:my_(url) 'title (with parens)'</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[Foo*bar\]]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2891:1-2893:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="my_(url)" title="title (with parens)"&gt;Foo*bar]&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="3540:1-3542:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="my_(url)" title="title (with parens)"&gt;Foo*bar]&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-164">Example 164</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2896:1-2902:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[Foo bar]:</span>
+<pre data-sourcepos="3549:1-3555:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[Foo bar]:</span>
<span id="LC2" class="line" lang="plaintext">&lt;my url&gt;</span>
<span id="LC3" class="line" lang="plaintext">'title'</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -2592,12 +3447,15 @@ them.</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2904:1-2906:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="my%20url" title="title"&gt;Foo bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="3557:1-3559:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="my%20url" title="title"&gt;Foo bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2909:1-2909:41" dir="auto">The title may extend over multiple lines:</p>
+</div>
+<p data-sourcepos="3563:1-3563:41" dir="auto">The title may extend over multiple lines:</p>
+<div>
+<div><a href="#example-165">Example 165</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2911:1-2919:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url '</span>
+<pre data-sourcepos="3568:1-3576:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url '</span>
<span id="LC2" class="line" lang="plaintext">title</span>
<span id="LC3" class="line" lang="plaintext">line1</span>
<span id="LC4" class="line" lang="plaintext">line2</span>
@@ -2607,16 +3465,19 @@ them.</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2921:1-2927:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="</span>
+<pre data-sourcepos="3578:1-3584:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="</span>
<span id="LC2" class="line" lang="plaintext">title</span>
<span id="LC3" class="line" lang="plaintext">line1</span>
<span id="LC4" class="line" lang="plaintext">line2</span>
<span id="LC5" class="line" lang="plaintext">"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2930:1-2930:43" dir="auto">However, it may not contain a [blank line]:</p>
+</div>
+<p data-sourcepos="3588:1-3588:43" dir="auto">However, it may not contain a [blank line]:</p>
+<div>
+<div><a href="#example-166">Example 166</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2932:1-2938:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url 'title</span>
+<pre data-sourcepos="3593:1-3599:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url 'title</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">with blank line'</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -2624,178 +3485,223 @@ them.</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2940:1-2944:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo]: /url 'title&lt;/p&gt;</span>
+<pre data-sourcepos="3601:1-3605:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo]: /url 'title&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;with blank line'&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;[foo]&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2947:1-2947:25" dir="auto">The title may be omitted:</p>
+</div>
+<p data-sourcepos="3609:1-3609:25" dir="auto">The title may be omitted:</p>
+<div>
+<div><a href="#example-167">Example 167</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2949:1-2954:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]:</span>
+<pre data-sourcepos="3614:1-3619:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]:</span>
<span id="LC2" class="line" lang="plaintext">/url</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">[foo]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2956:1-2958:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="3621:1-3623:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2961:1-2961:40" dir="auto">The link destination may not be omitted:</p>
+</div>
+<p data-sourcepos="3627:1-3627:40" dir="auto">The link destination may not be omitted:</p>
+<div>
+<div><a href="#example-168">Example 168</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2963:1-2967:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]:</span>
+<pre data-sourcepos="3632:1-3636:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]:</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2969:1-2972:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo]:&lt;/p&gt;</span>
+<pre data-sourcepos="3638:1-3641:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo]:&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;[foo]&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2974:2-2975:16" dir="auto">However, an empty link destination may be specified using
+</div>
+<p data-sourcepos="3644:2-3645:16" dir="auto">However, an empty link destination may be specified using
angle brackets:</p>
+<div>
+<div><a href="#example-169">Example 169</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2977:1-2981:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: &lt;&gt;</span>
+<pre data-sourcepos="3650:1-3654:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: &lt;&gt;</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2983:1-2985:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href=""&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="3656:1-3658:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href=""&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="2987:1-2988:11" dir="auto">The title must be separated from the link destination by
+</div>
+<p data-sourcepos="3661:1-3662:11" dir="auto">The title must be separated from the link destination by
whitespace:</p>
+<div>
+<div><a href="#example-170">Example 170</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2990:1-2994:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: &lt;bar&gt;(baz)</span>
+<pre data-sourcepos="3667:1-3671:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: &lt;bar&gt;(baz)</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="2996:1-2999:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo]: &lt;bar&gt;(baz)&lt;/p&gt;</span>
+<pre data-sourcepos="3673:1-3676:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo]: &lt;bar&gt;(baz)&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;[foo]&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3002:1-3003:24" dir="auto">Both title and destination can contain backslash escapes
+</div>
+<p data-sourcepos="3680:1-3681:24" dir="auto">Both title and destination can contain backslash escapes
and literal backslashes:</p>
+<div>
+<div><a href="#example-171">Example 171</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3005:1-3009:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url\bar\*baz "foo\"bar\baz"</span>
+<pre data-sourcepos="3686:1-3690:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url\bar\*baz "foo\"bar\baz"</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3011:1-3013:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url%5Cbar*baz" title="foo&amp;quot;bar\baz"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="3692:1-3694:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url%5Cbar*baz" title="foo&amp;quot;bar\baz"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3016:1-3016:52" dir="auto">A link can come before its corresponding definition:</p>
+</div>
+<p data-sourcepos="3698:1-3698:52" dir="auto">A link can come before its corresponding definition:</p>
+<div>
+<div><a href="#example-172">Example 172</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3018:1-3022:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]</span>
+<pre data-sourcepos="3703:1-3707:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: url</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3024:1-3026:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="url"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="3709:1-3711:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="url"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3029:1-3030:11" dir="auto">If there are several matching definitions, the first one takes
+</div>
+<p data-sourcepos="3715:1-3716:11" dir="auto">If there are several matching definitions, the first one takes
precedence:</p>
+<div>
+<div><a href="#example-173">Example 173</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3032:1-3037:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]</span>
+<pre data-sourcepos="3721:1-3726:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: first</span>
<span id="LC4" class="line" lang="plaintext">[foo]: second</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3039:1-3041:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="first"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="3728:1-3730:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="first"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3044:1-3045:33" dir="auto">As noted in the section on [Links], matching of labels is
+</div>
+<p data-sourcepos="3734:1-3735:33" dir="auto">As noted in the section on [Links], matching of labels is
case-insensitive (see [matches]).</p>
+<div>
+<div><a href="#example-174">Example 174</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3047:1-3051:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[FOO]: /url</span>
+<pre data-sourcepos="3740:1-3744:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[FOO]: /url</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[Foo]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3053:1-3055:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url"&gt;Foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="3746:1-3748:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url"&gt;Foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-175">Example 175</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3058:1-3062:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[ΑΓΩ]: /φου</span>
+<pre data-sourcepos="3755:1-3759:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[ΑΓΩ]: /φου</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[αγω]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3064:1-3066:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/%CF%86%CE%BF%CF%85"&gt;αγω&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="3761:1-3763:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/%CF%86%CE%BF%CF%85"&gt;αγω&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3069:1-3070:39" dir="auto">Here is a link reference definition with no corresponding link.
+</div>
+<p data-sourcepos="3767:1-3768:39" dir="auto">Here is a link reference definition with no corresponding link.
It contributes nothing to the document.</p>
+<div>
+<div><a href="#example-176">Example 176</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3072:1-3074:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url</span></code></pre>
+<pre data-sourcepos="3773:1-3775:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3076:1-3077:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code></code></pre>
+<pre data-sourcepos="3777:1-3778:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3080:1-3080:20" dir="auto">Here is another one:</p>
+</div>
+<p data-sourcepos="3782:1-3782:20" dir="auto">Here is another one:</p>
+<div>
+<div><a href="#example-177">Example 177</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3082:1-3087:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[</span>
+<pre data-sourcepos="3787:1-3792:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[</span>
<span id="LC2" class="line" lang="plaintext">foo</span>
<span id="LC3" class="line" lang="plaintext">]: /url</span>
<span id="LC4" class="line" lang="plaintext">bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3089:1-3091:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="3794:1-3796:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3094:1-3095:44" dir="auto">This is not a link reference definition, because there are
+</div>
+<p data-sourcepos="3800:1-3801:44" dir="auto">This is not a link reference definition, because there are
[non-whitespace characters] after the title:</p>
+<div>
+<div><a href="#example-178">Example 178</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3097:1-3099:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url "title" ok</span></code></pre>
+<pre data-sourcepos="3806:1-3808:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url "title" ok</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3101:1-3103:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo]: /url &amp;quot;title&amp;quot; ok&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="3810:1-3812:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo]: /url &amp;quot;title&amp;quot; ok&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3106:1-3106:57" dir="auto">This is a link reference definition, but it has no title:</p>
+</div>
+<p data-sourcepos="3816:1-3816:57" dir="auto">This is a link reference definition, but it has no title:</p>
+<div>
+<div><a href="#example-179">Example 179</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3108:1-3111:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url</span>
+<pre data-sourcepos="3821:1-3824:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url</span>
<span id="LC2" class="line" lang="plaintext">"title" ok</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3113:1-3115:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;quot;title&amp;quot; ok&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="3826:1-3828:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;quot;title&amp;quot; ok&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3118:1-3119:12" dir="auto">This is not a link reference definition, because it is indented
+</div>
+<p data-sourcepos="3832:1-3833:12" dir="auto">This is not a link reference definition, because it is indented
four spaces:</p>
+<div>
+<div><a href="#example-180">Example 180</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3121:1-3125:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> [foo]: /url "title"</span>
+<pre data-sourcepos="3838:1-3842:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> [foo]: /url "title"</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3127:1-3131:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;[foo]: /url &amp;quot;title&amp;quot;</span>
+<pre data-sourcepos="3844:1-3848:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;[foo]: /url &amp;quot;title&amp;quot;</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;[foo]&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3134:1-3135:13" dir="auto">This is not a link reference definition, because it occurs inside
+</div>
+<p data-sourcepos="3852:1-3853:13" dir="auto">This is not a link reference definition, because it occurs inside
a code block:</p>
+<div>
+<div><a href="#example-181">Example 181</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3137:1-3143:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
+<pre data-sourcepos="3858:1-3864:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
<span id="LC2" class="line" lang="plaintext">[foo]: /url</span>
<span id="LC3" class="line" lang="plaintext">```</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -2803,67 +3709,82 @@ a code block:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3145:1-3149:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;[foo]: /url</span>
+<pre data-sourcepos="3866:1-3870:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;[foo]: /url</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;[foo]&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3152:1-3152:59" dir="auto">A [link reference definition] cannot interrupt a paragraph.</p>
+</div>
+<p data-sourcepos="3874:1-3874:59" dir="auto">A [link reference definition] cannot interrupt a paragraph.</p>
+<div>
+<div><a href="#example-182">Example 182</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3154:1-3159:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="3879:1-3884:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext">[bar]: /baz</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">[bar]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3161:1-3165:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
+<pre data-sourcepos="3886:1-3890:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo</span>
<span id="LC2" class="line" lang="plaintext">[bar]: /baz&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;[bar]&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3168:1-3169:65" dir="auto">However, it can directly follow other block elements, such as headings
+</div>
+<p data-sourcepos="3894:1-3895:65" dir="auto">However, it can directly follow other block elements, such as headings
and thematic breaks, and it need not be followed by a blank line.</p>
+<div>
+<div><a href="#example-183">Example 183</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3171:1-3175:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"># [Foo]</span>
+<pre data-sourcepos="3900:1-3904:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"># [Foo]</span>
<span id="LC2" class="line" lang="plaintext">[foo]: /url</span>
<span id="LC3" class="line" lang="plaintext">&gt; bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3177:1-3182:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;&lt;a href="/url"&gt;Foo&lt;/a&gt;&lt;/h1&gt;</span>
+<pre data-sourcepos="3906:1-3911:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;&lt;a href="/url"&gt;Foo&lt;/a&gt;&lt;/h1&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-184">Example 184</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3184:1-3189:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url</span>
+<pre data-sourcepos="3917:1-3922:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url</span>
<span id="LC2" class="line" lang="plaintext">bar</span>
<span id="LC3" class="line" lang="plaintext">===</span>
<span id="LC4" class="line" lang="plaintext">[foo]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3191:1-3194:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;bar&lt;/h1&gt;</span>
+<pre data-sourcepos="3924:1-3927:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h1&gt;bar&lt;/h1&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-185">Example 185</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3196:1-3200:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url</span>
+<pre data-sourcepos="3933:1-3937:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url</span>
<span id="LC2" class="line" lang="plaintext">===</span>
<span id="LC3" class="line" lang="plaintext">[foo]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3202:1-3205:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;===</span>
+<pre data-sourcepos="3939:1-3942:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;===</span>
<span id="LC2" class="line" lang="plaintext">&lt;a href="/url"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3208:1-3209:61" dir="auto">Several [link reference definitions]
+</div>
+<p data-sourcepos="3946:1-3947:61" dir="auto">Several [link reference definitions]
can occur one after another, without intervening blank lines.</p>
+<div>
+<div><a href="#example-186">Example 186</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3211:1-3220:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /foo-url "foo"</span>
+<pre data-sourcepos="3952:1-3961:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /foo-url "foo"</span>
<span id="LC2" class="line" lang="plaintext">[bar]: /bar-url</span>
<span id="LC3" class="line" lang="plaintext"> "bar"</span>
<span id="LC4" class="line" lang="plaintext">[baz]: /baz-url</span>
@@ -2874,63 +3795,75 @@ can occur one after another, without intervening blank lines.</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3222:1-3226:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/foo-url" title="foo"&gt;foo&lt;/a&gt;,</span>
+<pre data-sourcepos="3963:1-3967:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/foo-url" title="foo"&gt;foo&lt;/a&gt;,</span>
<span id="LC2" class="line" lang="plaintext">&lt;a href="/bar-url" title="bar"&gt;bar&lt;/a&gt;,</span>
<span id="LC3" class="line" lang="plaintext">&lt;a href="/baz-url"&gt;baz&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3229:1-3232:12" dir="auto">[Link reference definitions] can occur
+</div>
+<p data-sourcepos="3971:1-3974:12" dir="auto">[Link reference definitions] can occur
inside block containers, like lists and block quotations. They
affect the entire document, not just the container in which they
are defined:</p>
+<div>
+<div><a href="#example-187">Example 187</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3234:1-3238:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]</span>
+<pre data-sourcepos="3979:1-3983:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">&gt; [foo]: /url</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3240:1-3244:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url"&gt;foo&lt;/a&gt;&lt;/p&gt;</span>
+<pre data-sourcepos="3985:1-3989:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url"&gt;foo&lt;/a&gt;&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3247:1-3251:19" dir="auto">Whether something is a [link reference definition] is
+</div>
+<p data-sourcepos="3993:1-3997:19" dir="auto">Whether something is a [link reference definition] is
independent of whether the link reference it defines is
used in the document. Thus, for example, the following
document contains just a link reference definition, and
no visible content:</p>
+<div>
+<div><a href="#example-188">Example 188</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3253:1-3255:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url</span></code></pre>
+<pre data-sourcepos="4002:1-4004:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3257:1-3258:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code></code></pre>
+<pre data-sourcepos="4006:1-4007:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="3261:1-3261:13" dir="auto">
+</div>
+<h2 data-sourcepos="4011:1-4011:13" dir="auto">
<a id="user-content-paragraphs" class="anchor" href="#paragraphs" aria-hidden="true"></a>Paragraphs</h2>
-<p data-sourcepos="3263:1-3268:13" dir="auto">A sequence of non-blank lines that cannot be interpreted as other
+<p data-sourcepos="4013:1-4018:13" dir="auto">A sequence of non-blank lines that cannot be interpreted as other
kinds of blocks forms a <a href="@">paragraph</a>.
The contents of the paragraph are the result of parsing the
paragraph's raw content as inlines. The paragraph's raw content
is formed by concatenating the lines and removing initial and final
[whitespace].</p>
-<p data-sourcepos="3270:1-3270:37" dir="auto">A simple example with two paragraphs:</p>
+<p data-sourcepos="4020:1-4020:37" dir="auto">A simple example with two paragraphs:</p>
+<div>
+<div><a href="#example-189">Example 189</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3272:1-3276:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">aaa</span>
+<pre data-sourcepos="4025:1-4029:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">aaa</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">bbb</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3278:1-3281:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aaa&lt;/p&gt;</span>
+<pre data-sourcepos="4031:1-4034:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aaa&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;bbb&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3284:1-3284:58" dir="auto">Paragraphs can contain multiple lines, but no blank lines:</p>
+</div>
+<p data-sourcepos="4038:1-4038:58" dir="auto">Paragraphs can contain multiple lines, but no blank lines:</p>
+<div>
+<div><a href="#example-190">Example 190</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3286:1-3292:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">aaa</span>
+<pre data-sourcepos="4043:1-4049:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">aaa</span>
<span id="LC2" class="line" lang="plaintext">bbb</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">ccc</span>
@@ -2938,94 +3871,115 @@ is formed by concatenating the lines and removing initial and final
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3294:1-3299:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aaa</span>
+<pre data-sourcepos="4051:1-4056:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aaa</span>
<span id="LC2" class="line" lang="plaintext">bbb&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;ccc</span>
<span id="LC4" class="line" lang="plaintext">ddd&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3302:1-3302:54" dir="auto">Multiple blank lines between paragraph have no effect:</p>
+</div>
+<p data-sourcepos="4060:1-4060:54" dir="auto">Multiple blank lines between paragraph have no effect:</p>
+<div>
+<div><a href="#example-191">Example 191</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3304:1-3309:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">aaa</span>
+<pre data-sourcepos="4065:1-4070:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">aaa</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">bbb</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3311:1-3314:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aaa&lt;/p&gt;</span>
+<pre data-sourcepos="4072:1-4075:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aaa&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;bbb&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3317:1-3317:27" dir="auto">Leading spaces are skipped:</p>
+</div>
+<p data-sourcepos="4079:1-4079:27" dir="auto">Leading spaces are skipped:</p>
+<div>
+<div><a href="#example-192">Example 192</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3319:1-3322:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> aaa</span>
+<pre data-sourcepos="4084:1-4087:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> aaa</span>
<span id="LC2" class="line" lang="plaintext"> bbb</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3324:1-3327:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aaa</span>
+<pre data-sourcepos="4089:1-4092:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aaa</span>
<span id="LC2" class="line" lang="plaintext">bbb&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3330:1-3331:40" dir="auto">Lines after the first may be indented any amount, since indented
+</div>
+<p data-sourcepos="4096:1-4097:40" dir="auto">Lines after the first may be indented any amount, since indented
code blocks cannot interrupt paragraphs.</p>
+<div>
+<div><a href="#example-193">Example 193</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3333:1-3337:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">aaa</span>
+<pre data-sourcepos="4102:1-4106:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">aaa</span>
<span id="LC2" class="line" lang="plaintext"> bbb</span>
<span id="LC3" class="line" lang="plaintext"> ccc</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3339:1-3343:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aaa</span>
+<pre data-sourcepos="4108:1-4112:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aaa</span>
<span id="LC2" class="line" lang="plaintext">bbb</span>
<span id="LC3" class="line" lang="plaintext">ccc&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3346:1-3347:44" dir="auto">However, the first line may be indented at most three spaces,
+</div>
+<p data-sourcepos="4116:1-4117:44" dir="auto">However, the first line may be indented at most three spaces,
or an indented code block will be triggered:</p>
+<div>
+<div><a href="#example-194">Example 194</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3349:1-3352:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> aaa</span>
+<pre data-sourcepos="4122:1-4125:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> aaa</span>
<span id="LC2" class="line" lang="plaintext">bbb</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3354:1-3357:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aaa</span>
+<pre data-sourcepos="4127:1-4130:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aaa</span>
<span id="LC2" class="line" lang="plaintext">bbb&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-195">Example 195</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3360:1-3363:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> aaa</span>
+<pre data-sourcepos="4137:1-4140:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> aaa</span>
<span id="LC2" class="line" lang="plaintext">bbb</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3365:1-3369:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
+<pre data-sourcepos="4142:1-4146:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;aaa</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;bbb&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3372:1-3374:7" dir="auto">Final spaces are stripped before inline parsing, so a paragraph
+</div>
+<p data-sourcepos="4150:1-4152:7" dir="auto">Final spaces are stripped before inline parsing, so a paragraph
that ends with two or more spaces will not end with a [hard line
break]:</p>
+<div>
+<div><a href="#example-196">Example 196</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3376:1-3379:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">aaa </span>
+<pre data-sourcepos="4157:1-4160:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">aaa </span>
<span id="LC2" class="line" lang="plaintext">bbb </span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3381:1-3384:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aaa&lt;br /&gt;</span>
+<pre data-sourcepos="4162:1-4165:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aaa&lt;br /&gt;</span>
<span id="LC2" class="line" lang="plaintext">bbb&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="3387:1-3387:14" dir="auto">
+</div>
+<h2 data-sourcepos="4169:1-4169:14" dir="auto">
<a id="user-content-blank-lines" class="anchor" href="#blank-lines" aria-hidden="true"></a>Blank lines</h2>
-<p data-sourcepos="3389:1-3391:22" dir="auto">[Blank lines] between block-level elements are ignored,
+<p data-sourcepos="4171:1-4173:22" dir="auto">[Blank lines] between block-level elements are ignored,
except for the role they play in determining whether a [list]
is [tight] or [loose].</p>
-<p data-sourcepos="3393:1-3393:70" dir="auto">Blank lines at the beginning and end of the document are also ignored.</p>
+<p data-sourcepos="4175:1-4175:70" dir="auto">Blank lines at the beginning and end of the document are also ignored.</p>
+<div>
+<div><a href="#example-197">Example 197</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3395:1-3404:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> </span>
+<pre data-sourcepos="4180:1-4189:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> </span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">aaa</span>
<span id="LC4" class="line" lang="plaintext"> </span>
@@ -3036,34 +3990,37 @@ is [tight] or [loose].</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3406:1-3409:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aaa&lt;/p&gt;</span>
+<pre data-sourcepos="4191:1-4194:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aaa&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h1&gt;aaa&lt;/h1&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
<div>
-<h2 data-sourcepos="3413:1-3413:21">
+<h2 data-sourcepos="4199:1-4199:21">
<a id="user-content-tables-extension" class="anchor" href="#tables-extension" aria-hidden="true"></a>Tables (extension)</h2>
-<p data-sourcepos="3415:1-3416:10">GFM enables the <code>table</code> extension, where an additional leaf block type is
+<p data-sourcepos="4201:1-4202:10">GFM enables the <code>table</code> extension, where an additional leaf block type is
available.</p>
-<p data-sourcepos="3418:1-3420:23">A <a href="@">table</a> is an arrangement of data with rows and columns, consisting of a
+<p data-sourcepos="4204:1-4206:23">A <a href="@">table</a> is an arrangement of data with rows and columns, consisting of a
single header row, a [delimiter row] separating the header from the data, and
zero or more data rows.</p>
-<p data-sourcepos="3422:1-3426:23">Each row consists of cells containing arbitrary text, in which [inlines] are
+<p data-sourcepos="4208:1-4212:23">Each row consists of cells containing arbitrary text, in which [inlines] are
parsed, separated by pipes (<code>|</code>). A leading and trailing pipe is also
recommended for clarity of reading, and if there's otherwise parsing ambiguity.
Spaces between pipes and cell content are trimmed. Block-level elements cannot
be inserted in a table.</p>
-<p data-sourcepos="3428:1-3430:40">The <a href="@">delimiter row</a> consists of cells whose only content are hyphens (<code>-</code>),
+<p data-sourcepos="4214:1-4216:40">The <a href="@">delimiter row</a> consists of cells whose only content are hyphens (<code>-</code>),
and optionally, a leading or trailing colon (<code>:</code>), or both, to indicate left,
right, or center alignment respectively.</p>
+<div>
+<div><a href="#example-198">Example 198</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3432:1-3436:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| foo | bar |</span>
+<pre data-sourcepos="4221:1-4225:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| foo | bar |</span>
<span id="LC2" class="line" lang="plaintext">| --- | --- |</span>
<span id="LC3" class="line" lang="plaintext">| baz | bim |</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3438:1-3453:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
+<pre data-sourcepos="4227:1-4242:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;thead&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;tr&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;th&gt;foo&lt;/th&gt;</span>
@@ -3079,16 +4036,19 @@ right, or center alignment respectively.</p>
<span id="LC14" class="line" lang="plaintext">&lt;/table&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3455:1-3456:74">Cells in one column don't need to match length, though it's easier to read if
+</div>
+<p data-sourcepos="4245:1-4246:74">Cells in one column don't need to match length, though it's easier to read if
they are. Likewise, use of leading and trailing pipes may be inconsistent:</p>
+<div>
+<div><a href="#example-199">Example 199</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3458:1-3462:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| abc | defghi |</span>
+<pre data-sourcepos="4251:1-4255:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| abc | defghi |</span>
<span id="LC2" class="line" lang="plaintext">:-: | -----------:</span>
<span id="LC3" class="line" lang="plaintext">bar | baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3464:1-3479:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
+<pre data-sourcepos="4257:1-4272:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;thead&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;tr&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;th align="center"&gt;abc&lt;/th&gt;</span>
@@ -3104,17 +4064,20 @@ they are. Likewise, use of leading and trailing pipes may be inconsistent:</p>
<span id="LC14" class="line" lang="plaintext">&lt;/table&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3481:1-3482:13">Include a pipe in a cell's content by escaping it, including inside other
+</div>
+<p data-sourcepos="4275:1-4276:13">Include a pipe in a cell's content by escaping it, including inside other
inline spans:</p>
+<div>
+<div><a href="#example-200">Example 200</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3484:1-3489:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| f\|oo |</span>
+<pre data-sourcepos="4281:1-4286:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| f\|oo |</span>
<span id="LC2" class="line" lang="plaintext">| ------ |</span>
<span id="LC3" class="line" lang="plaintext">| b `\|` az |</span>
<span id="LC4" class="line" lang="plaintext">| b **\|** im |</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3491:1-3507:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
+<pre data-sourcepos="4288:1-4304:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;thead&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;tr&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;th&gt;f|oo&lt;/th&gt;</span>
@@ -3131,17 +4094,20 @@ inline spans:</p>
<span id="LC15" class="line" lang="plaintext">&lt;/table&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3509:1-3510:22">The table is broken at the first empty line, or beginning of another
+</div>
+<p data-sourcepos="4307:1-4308:22">The table is broken at the first empty line, or beginning of another
block-level structure:</p>
+<div>
+<div><a href="#example-201">Example 201</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3512:1-3517:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| abc | def |</span>
+<pre data-sourcepos="4313:1-4318:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| abc | def |</span>
<span id="LC2" class="line" lang="plaintext">| --- | --- |</span>
<span id="LC3" class="line" lang="plaintext">| bar | baz |</span>
<span id="LC4" class="line" lang="plaintext">&gt; bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3519:1-3537:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
+<pre data-sourcepos="4320:1-4338:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;thead&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;tr&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;th&gt;abc&lt;/th&gt;</span>
@@ -3160,8 +4126,11 @@ block-level structure:</p>
<span id="LC17" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-202">Example 202</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3539:1-3546:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| abc | def |</span>
+<pre data-sourcepos="4344:1-4351:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| abc | def |</span>
<span id="LC2" class="line" lang="plaintext">| --- | --- |</span>
<span id="LC3" class="line" lang="plaintext">| bar | baz |</span>
<span id="LC4" class="line" lang="plaintext">bar</span>
@@ -3170,7 +4139,7 @@ block-level structure:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3548:1-3568:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
+<pre data-sourcepos="4353:1-4373:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;thead&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;tr&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;th&gt;abc&lt;/th&gt;</span>
@@ -3191,32 +4160,38 @@ block-level structure:</p>
<span id="LC19" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3570:1-3571:31">The header row must match the [delimiter row] in the number of cells. If not,
+</div>
+<p data-sourcepos="4376:1-4377:31">The header row must match the [delimiter row] in the number of cells. If not,
a table will not be recognized:</p>
+<div>
+<div><a href="#example-203">Example 203</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3573:1-3577:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| abc | def |</span>
+<pre data-sourcepos="4382:1-4386:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| abc | def |</span>
<span id="LC2" class="line" lang="plaintext">| --- |</span>
<span id="LC3" class="line" lang="plaintext">| bar |</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3579:1-3583:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;| abc | def |</span>
+<pre data-sourcepos="4388:1-4392:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;| abc | def |</span>
<span id="LC2" class="line" lang="plaintext">| --- |</span>
<span id="LC3" class="line" lang="plaintext">| bar |&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3585:1-3587:65">The remainder of the table's rows may vary in the number of cells. If there
+</div>
+<p data-sourcepos="4395:1-4397:65">The remainder of the table's rows may vary in the number of cells. If there
are a number of cells fewer than the number of cells in the header row, empty
cells are inserted. If there are greater, the excess is ignored:</p>
+<div>
+<div><a href="#example-204">Example 204</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3589:1-3594:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| abc | def |</span>
+<pre data-sourcepos="4402:1-4407:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| abc | def |</span>
<span id="LC2" class="line" lang="plaintext">| --- | --- |</span>
<span id="LC3" class="line" lang="plaintext">| bar |</span>
<span id="LC4" class="line" lang="plaintext">| bar | baz | boo |</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3596:1-3615:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
+<pre data-sourcepos="4409:1-4428:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;thead&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;tr&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;th&gt;abc&lt;/th&gt;</span>
@@ -3236,14 +4211,17 @@ cells are inserted. If there are greater, the excess is ignored:</p>
<span id="LC18" class="line" lang="plaintext">&lt;/table&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3617:1-3617:75">If there are no rows in the body, no <code>&lt;tbody&gt;</code> is generated in HTML output:</p>
+</div>
+<p data-sourcepos="4431:1-4431:75">If there are no rows in the body, no <code>&lt;tbody&gt;</code> is generated in HTML output:</p>
+<div>
+<div><a href="#example-205">Example 205</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3619:1-3622:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| abc | def |</span>
+<pre data-sourcepos="4436:1-4439:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| abc | def |</span>
<span id="LC2" class="line" lang="plaintext">| --- | --- |</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3624:1-3633:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
+<pre data-sourcepos="4441:1-4450:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;table&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;thead&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;tr&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;th&gt;abc&lt;/th&gt;</span>
@@ -3254,39 +4232,40 @@ cells are inserted. If there are greater, the excess is ignored:</p>
<copy-code></copy-code>
</div>
</div>
-<h1 data-sourcepos="3637:1-3637:18" dir="auto">
+</div>
+<h1 data-sourcepos="4455:1-4455:18" dir="auto">
<a id="user-content-container-blocks" class="anchor" href="#container-blocks" aria-hidden="true"></a>Container blocks</h1>
-<p data-sourcepos="3639:1-3642:45" dir="auto">A <a href="#container-blocks">container block</a> is a block that has other
+<p data-sourcepos="4457:1-4460:45" dir="auto">A <a href="#container-blocks">container block</a> is a block that has other
blocks as its contents. There are two basic kinds of container blocks:
[block quotes] and [list items].
[Lists] are meta-containers for [list items].</p>
-<p data-sourcepos="3644:1-3645:26" dir="auto">We define the syntax for container blocks recursively. The general
+<p data-sourcepos="4462:1-4463:26" dir="auto">We define the syntax for container blocks recursively. The general
form of the definition is:</p>
-<blockquote data-sourcepos="3647:1-3649:35" dir="auto">
-<p data-sourcepos="3647:3-3649:35">If X is a sequence of blocks, then the result of
+<blockquote data-sourcepos="4465:1-4467:35" dir="auto">
+<p data-sourcepos="4465:3-4467:35">If X is a sequence of blocks, then the result of
transforming X in such-and-such a way is a container of type Y
with these blocks as its content.</p>
</blockquote>
-<p data-sourcepos="3651:1-3655:52" dir="auto">So, we explain what counts as a block quote or list item by explaining
+<p data-sourcepos="4469:1-4473:52" dir="auto">So, we explain what counts as a block quote or list item by explaining
how these can be <em>generated</em> from their contents. This should suffice
to define the syntax, although it does not give a recipe for <em>parsing</em>
these constructions. (A recipe is provided below in the section entitled
<a href="#appendix-a-parsing-strategy">A parsing strategy</a>.)</p>
-<h2 data-sourcepos="3657:1-3657:15" dir="auto">
+<h2 data-sourcepos="4475:1-4475:15" dir="auto">
<a id="user-content-block-quotes" class="anchor" href="#block-quotes" aria-hidden="true"></a>Block quotes</h2>
-<p data-sourcepos="3659:1-3661:78" dir="auto">A <a href="@">block quote marker</a>
+<p data-sourcepos="4477:1-4479:78" dir="auto">A <a href="@">block quote marker</a>
consists of 0-3 spaces of initial indent, plus (a) the character <code>&gt;</code> together
with a following space, or (b) a single character <code>&gt;</code> not followed by a space.</p>
-<p data-sourcepos="3663:1-3663:42" dir="auto">The following rules define [block quotes]:</p>
-<ol data-sourcepos="3665:1-3682:0" dir="auto">
-<li data-sourcepos="3665:1-3669:0">
-<p data-sourcepos="3665:5-3668:54"><strong>Basic case.</strong> If a string of lines <em>Ls</em> constitute a sequence
+<p data-sourcepos="4481:1-4481:42" dir="auto">The following rules define [block quotes]:</p>
+<ol data-sourcepos="4483:1-4500:0" dir="auto">
+<li data-sourcepos="4483:1-4487:0">
+<p data-sourcepos="4483:5-4486:54"><strong>Basic case.</strong> If a string of lines <em>Ls</em> constitute a sequence
of blocks <em>Bs</em>, then the result of prepending a [block quote
marker] to the beginning of each line in <em>Ls</em>
is a <a href="#block-quotes">block quote</a> containing <em>Bs</em>.</p>
</li>
-<li data-sourcepos="3670:1-3679:0">
-<p data-sourcepos="3670:5-3678:48"><strong>Laziness.</strong> If a string of lines <em>Ls</em> constitute a <a href="#block-quotes">block
+<li data-sourcepos="4488:1-4497:0">
+<p data-sourcepos="4488:5-4496:48"><strong>Laziness.</strong> If a string of lines <em>Ls</em> constitute a <a href="#block-quotes">block
quote</a> with contents <em>Bs</em>, then the result of deleting
the initial [block quote marker] from one or
more lines in which the next [non-whitespace character] after the [block
@@ -3296,138 +4275,161 @@ text] is a block quote with <em>Bs</em> as its content.
that will be parsed as part of the content of a paragraph, but does
not occur at the beginning of the paragraph.</p>
</li>
-<li data-sourcepos="3680:1-3682:0">
-<p data-sourcepos="3680:5-3681:65"><strong>Consecutiveness.</strong> A document cannot contain two [block
+<li data-sourcepos="4498:1-4500:0">
+<p data-sourcepos="4498:5-4499:65"><strong>Consecutiveness.</strong> A document cannot contain two [block
quotes] in a row unless there is a [blank line] between them.</p>
</li>
</ol>
-<p data-sourcepos="3683:1-3683:54" dir="auto">Nothing else counts as a <a href="#block-quotes">block quote</a>.</p>
-<p data-sourcepos="3685:1-3685:25" dir="auto">Here is a simple example:</p>
+<p data-sourcepos="4501:1-4501:54" dir="auto">Nothing else counts as a <a href="#block-quotes">block quote</a>.</p>
+<p data-sourcepos="4503:1-4503:25" dir="auto">Here is a simple example:</p>
+<div>
+<div><a href="#example-206">Example 206</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3687:1-3691:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; # Foo</span>
+<pre data-sourcepos="4508:1-4512:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; # Foo</span>
<span id="LC2" class="line" lang="plaintext">&gt; bar</span>
<span id="LC3" class="line" lang="plaintext">&gt; baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3693:1-3699:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4514:1-4520:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h1&gt;Foo&lt;/h1&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;bar</span>
<span id="LC4" class="line" lang="plaintext">baz&lt;/p&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3702:1-3702:51" dir="auto">The spaces after the <code>&gt;</code> characters can be omitted:</p>
+</div>
+<p data-sourcepos="4524:1-4524:51" dir="auto">The spaces after the <code>&gt;</code> characters can be omitted:</p>
+<div>
+<div><a href="#example-207">Example 207</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3704:1-3708:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt;# Foo</span>
+<pre data-sourcepos="4529:1-4533:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt;# Foo</span>
<span id="LC2" class="line" lang="plaintext">&gt;bar</span>
<span id="LC3" class="line" lang="plaintext">&gt; baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3710:1-3716:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4535:1-4541:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h1&gt;Foo&lt;/h1&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;bar</span>
<span id="LC4" class="line" lang="plaintext">baz&lt;/p&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3719:1-3719:46" dir="auto">The <code>&gt;</code> characters can be indented 1-3 spaces:</p>
+</div>
+<p data-sourcepos="4545:1-4545:46" dir="auto">The <code>&gt;</code> characters can be indented 1-3 spaces:</p>
+<div>
+<div><a href="#example-208">Example 208</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3721:1-3725:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &gt; # Foo</span>
+<pre data-sourcepos="4550:1-4554:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &gt; # Foo</span>
<span id="LC2" class="line" lang="plaintext"> &gt; bar</span>
<span id="LC3" class="line" lang="plaintext"> &gt; baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3727:1-3733:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4556:1-4562:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h1&gt;Foo&lt;/h1&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;bar</span>
<span id="LC4" class="line" lang="plaintext">baz&lt;/p&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3736:1-3736:34" dir="auto">Four spaces gives us a code block:</p>
+</div>
+<p data-sourcepos="4566:1-4566:34" dir="auto">Four spaces gives us a code block:</p>
+<div>
+<div><a href="#example-209">Example 209</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3738:1-3742:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &gt; # Foo</span>
+<pre data-sourcepos="4571:1-4575:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &gt; # Foo</span>
<span id="LC2" class="line" lang="plaintext"> &gt; bar</span>
<span id="LC3" class="line" lang="plaintext"> &gt; baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3744:1-3749:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&amp;gt; # Foo</span>
+<pre data-sourcepos="4577:1-4582:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&amp;gt; # Foo</span>
<span id="LC2" class="line" lang="plaintext">&amp;gt; bar</span>
<span id="LC3" class="line" lang="plaintext">&amp;gt; baz</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3752:1-3753:30" dir="auto">The Laziness clause allows us to omit the <code>&gt;</code> before
+</div>
+<p data-sourcepos="4586:1-4587:30" dir="auto">The Laziness clause allows us to omit the <code>&gt;</code> before
[paragraph continuation text]:</p>
+<div>
+<div><a href="#example-210">Example 210</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3755:1-3759:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; # Foo</span>
+<pre data-sourcepos="4592:1-4596:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; # Foo</span>
<span id="LC2" class="line" lang="plaintext">&gt; bar</span>
<span id="LC3" class="line" lang="plaintext">baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3761:1-3767:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4598:1-4604:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h1&gt;Foo&lt;/h1&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;bar</span>
<span id="LC4" class="line" lang="plaintext">baz&lt;/p&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3770:1-3771:19" dir="auto">A block quote can contain some lazy and some non-lazy
+</div>
+<p data-sourcepos="4608:1-4609:19" dir="auto">A block quote can contain some lazy and some non-lazy
continuation lines:</p>
+<div>
+<div><a href="#example-211">Example 211</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3773:1-3777:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; bar</span>
+<pre data-sourcepos="4614:1-4618:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; bar</span>
<span id="LC2" class="line" lang="plaintext">baz</span>
<span id="LC3" class="line" lang="plaintext">&gt; foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3779:1-3785:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4620:1-4626:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;bar</span>
<span id="LC3" class="line" lang="plaintext">baz</span>
<span id="LC4" class="line" lang="plaintext">foo&lt;/p&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3788:1-3790:61" dir="auto">Laziness only applies to lines that would have been continuations of
+</div>
+<p data-sourcepos="4630:1-4632:61" dir="auto">Laziness only applies to lines that would have been continuations of
paragraphs had they been prepended with [block quote markers].
For example, the <code>&gt; </code> cannot be omitted in the second line of</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3792:1-3795:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="gt">&gt; foo</span></span>
+<pre data-sourcepos="4634:1-4637:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="gt">&gt; foo</span></span>
<span id="LC2" class="line" lang="markdown"><span class="gt">&gt; ---</span></span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3797:1-3797:29" dir="auto">without changing the meaning:</p>
+<p data-sourcepos="4639:1-4639:29" dir="auto">without changing the meaning:</p>
+<div>
+<div><a href="#example-212">Example 212</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3799:1-3802:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; foo</span>
+<pre data-sourcepos="4644:1-4647:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; foo</span>
<span id="LC2" class="line" lang="plaintext">---</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3804:1-3809:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4649:1-4654:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/blockquote&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;hr /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3812:1-3812:52" dir="auto">Similarly, if we omit the <code>&gt; </code> in the second line of</p>
+</div>
+<p data-sourcepos="4658:1-4658:52" dir="auto">Similarly, if we omit the <code>&gt; </code> in the second line of</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3814:1-3817:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="gt">&gt; - foo</span></span>
+<pre data-sourcepos="4660:1-4663:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="gt">&gt; - foo</span></span>
<span id="LC2" class="line" lang="markdown"><span class="gt">&gt; - bar</span></span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3819:1-3819:47" dir="auto">then the block quote ends after the first line:</p>
+<p data-sourcepos="4665:1-4665:47" dir="auto">then the block quote ends after the first line:</p>
+<div>
+<div><a href="#example-213">Example 213</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3821:1-3824:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; - foo</span>
+<pre data-sourcepos="4670:1-4673:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; - foo</span>
<span id="LC2" class="line" lang="plaintext">- bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3826:1-3835:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4675:1-4684:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;foo&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/ul&gt;</span>
@@ -3437,15 +4439,18 @@ For example, the <code>&gt; </code> cannot be omitted in the second line of</p>
<span id="LC8" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3838:1-3839:53" dir="auto">For the same reason, we can't omit the <code>&gt; </code> in front of
+</div>
+<p data-sourcepos="4688:1-4689:53" dir="auto">For the same reason, we can't omit the <code>&gt; </code> in front of
subsequent lines of an indented or fenced code block:</p>
+<div>
+<div><a href="#example-214">Example 214</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3841:1-3844:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; foo</span>
+<pre data-sourcepos="4694:1-4697:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; foo</span>
<span id="LC2" class="line" lang="plaintext"> bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3846:1-3853:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4699:1-4706:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/blockquote&gt;</span>
@@ -3453,86 +4458,104 @@ subsequent lines of an indented or fenced code block:</p>
<span id="LC6" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-215">Example 215</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3856:1-3860:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; ```</span>
+<pre data-sourcepos="4713:1-4717:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; ```</span>
<span id="LC2" class="line" lang="plaintext">foo</span>
<span id="LC3" class="line" lang="plaintext">```</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3862:1-3868:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4719:1-4725:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/blockquote&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3871:1-3872:19" dir="auto">Note that in the following case, we have a [lazy
+</div>
+<p data-sourcepos="4729:1-4730:19" dir="auto">Note that in the following case, we have a [lazy
continuation line]:</p>
+<div>
+<div><a href="#example-216">Example 216</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3874:1-3877:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; foo</span>
+<pre data-sourcepos="4735:1-4738:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; foo</span>
<span id="LC2" class="line" lang="plaintext"> - bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3879:1-3884:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4740:1-4745:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;foo</span>
<span id="LC3" class="line" lang="plaintext">- bar&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3887:1-3887:24" dir="auto">To see why, note that in</p>
+</div>
+<p data-sourcepos="4749:1-4749:24" dir="auto">To see why, note that in</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3889:1-3892:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="gt">&gt; foo</span></span>
+<pre data-sourcepos="4751:1-4754:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="gt">&gt; foo</span></span>
<span id="LC2" class="line" lang="markdown"><span class="gt">&gt; - bar</span></span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3894:1-3896:61" dir="auto">the <code>- bar</code> is indented too far to start a list, and can't
+<p data-sourcepos="4756:1-4758:61" dir="auto">the <code>- bar</code> is indented too far to start a list, and can't
be an indented code block because indented code blocks cannot
interrupt paragraphs, so it is [paragraph continuation text].</p>
-<p data-sourcepos="3898:1-3898:27" dir="auto">A block quote can be empty:</p>
+<p data-sourcepos="4760:1-4760:27" dir="auto">A block quote can be empty:</p>
+<div>
+<div><a href="#example-217">Example 217</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3900:1-3902:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt;</span></code></pre>
+<pre data-sourcepos="4765:1-4767:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3904:1-3907:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4769:1-4772:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-218">Example 218</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3910:1-3914:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt;</span>
+<pre data-sourcepos="4779:1-4783:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt;</span>
<span id="LC2" class="line" lang="plaintext">&gt; </span>
<span id="LC3" class="line" lang="plaintext">&gt; </span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3916:1-3919:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4785:1-4788:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3922:1-3922:52" dir="auto">A block quote can have initial or final blank lines:</p>
+</div>
+<p data-sourcepos="4792:1-4792:52" dir="auto">A block quote can have initial or final blank lines:</p>
+<div>
+<div><a href="#example-219">Example 219</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3924:1-3928:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt;</span>
+<pre data-sourcepos="4797:1-4801:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt;</span>
<span id="LC2" class="line" lang="plaintext">&gt; foo</span>
<span id="LC3" class="line" lang="plaintext">&gt; </span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3930:1-3934:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4803:1-4807:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3937:1-3937:43" dir="auto">A blank line always separates block quotes:</p>
+</div>
+<p data-sourcepos="4811:1-4811:43" dir="auto">A blank line always separates block quotes:</p>
+<div>
+<div><a href="#example-220">Example 220</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3939:1-3943:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; foo</span>
+<pre data-sourcepos="4816:1-4820:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">&gt; bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3945:1-3952:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4822:1-4829:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/blockquote&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;blockquote&gt;</span>
@@ -3540,61 +4563,73 @@ interrupt paragraphs, so it is [paragraph continuation text].</p>
<span id="LC6" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3955:1-3958:44" dir="auto">(Most current Markdown implementations, including John Gruber's
+</div>
+<p data-sourcepos="4833:1-4836:44" dir="auto">(Most current Markdown implementations, including John Gruber's
original <code>Markdown.pl</code>, will parse this example as a single block quote
with two paragraphs. But it seems better to allow the author to decide
whether two block quotes or one are wanted.)</p>
-<p data-sourcepos="3960:1-3961:28" dir="auto">Consecutiveness means that if we put these block quotes together,
+<p data-sourcepos="4838:1-4839:28" dir="auto">Consecutiveness means that if we put these block quotes together,
we get a single block quote:</p>
+<div>
+<div><a href="#example-221">Example 221</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3963:1-3966:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; foo</span>
+<pre data-sourcepos="4844:1-4847:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; foo</span>
<span id="LC2" class="line" lang="plaintext">&gt; bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3968:1-3973:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4849:1-4854:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;foo</span>
<span id="LC3" class="line" lang="plaintext">bar&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3976:1-3976:46" dir="auto">To get a block quote with two paragraphs, use:</p>
+</div>
+<p data-sourcepos="4858:1-4858:46" dir="auto">To get a block quote with two paragraphs, use:</p>
+<div>
+<div><a href="#example-222">Example 222</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3978:1-3982:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; foo</span>
+<pre data-sourcepos="4863:1-4867:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; foo</span>
<span id="LC2" class="line" lang="plaintext">&gt;</span>
<span id="LC3" class="line" lang="plaintext">&gt; bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3984:1-3989:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4869:1-4874:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="3992:1-3992:38" dir="auto">Block quotes can interrupt paragraphs:</p>
+</div>
+<p data-sourcepos="4878:1-4878:38" dir="auto">Block quotes can interrupt paragraphs:</p>
+<div>
+<div><a href="#example-223">Example 223</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3994:1-3997:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo</span>
+<pre data-sourcepos="4883:1-4886:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo</span>
<span id="LC2" class="line" lang="plaintext">&gt; bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="3999:1-4004:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
+<pre data-sourcepos="4888:1-4893:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4007:1-4008:7" dir="auto">In general, blank lines are not needed before or after block
+</div>
+<p data-sourcepos="4897:1-4898:7" dir="auto">In general, blank lines are not needed before or after block
quotes:</p>
+<div>
+<div><a href="#example-224">Example 224</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4010:1-4014:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; aaa</span>
+<pre data-sourcepos="4903:1-4907:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; aaa</span>
<span id="LC2" class="line" lang="plaintext">***</span>
<span id="LC3" class="line" lang="plaintext">&gt; bbb</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4016:1-4024:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4909:1-4917:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;aaa&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/blockquote&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;hr /&gt;</span>
@@ -3603,56 +4638,68 @@ quotes:</p>
<span id="LC7" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4027:1-4028:40" dir="auto">However, because of laziness, a blank line is needed between
+</div>
+<p data-sourcepos="4921:1-4922:40" dir="auto">However, because of laziness, a blank line is needed between
a block quote and a following paragraph:</p>
+<div>
+<div><a href="#example-225">Example 225</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4030:1-4033:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; bar</span>
+<pre data-sourcepos="4927:1-4930:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; bar</span>
<span id="LC2" class="line" lang="plaintext">baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4035:1-4040:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4932:1-4937:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;bar</span>
<span id="LC3" class="line" lang="plaintext">baz&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-226">Example 226</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4043:1-4047:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; bar</span>
+<pre data-sourcepos="4944:1-4948:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; bar</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4049:1-4054:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4950:1-4955:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/blockquote&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;baz&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-227">Example 227</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4057:1-4061:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; bar</span>
+<pre data-sourcepos="4962:1-4966:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; bar</span>
<span id="LC2" class="line" lang="plaintext">&gt;</span>
<span id="LC3" class="line" lang="plaintext">baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4063:1-4068:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4968:1-4973:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/blockquote&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;baz&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4071:1-4073:19" dir="auto">It is a consequence of the Laziness rule that any number
+</div>
+<p data-sourcepos="4977:1-4979:19" dir="auto">It is a consequence of the Laziness rule that any number
of initial <code>&gt;</code>s may be omitted on a continuation line of a
nested block quote:</p>
+<div>
+<div><a href="#example-228">Example 228</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4075:1-4078:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; &gt; &gt; foo</span>
+<pre data-sourcepos="4984:1-4987:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; &gt; &gt; foo</span>
<span id="LC2" class="line" lang="plaintext">bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4080:1-4089:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="4989:1-4998:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;foo</span>
@@ -3662,14 +4709,17 @@ nested block quote:</p>
<span id="LC8" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-229">Example 229</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4092:1-4096:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt;&gt;&gt; foo</span>
+<pre data-sourcepos="5005:1-5009:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt;&gt;&gt; foo</span>
<span id="LC2" class="line" lang="plaintext">&gt; bar</span>
<span id="LC3" class="line" lang="plaintext">&gt;&gt;baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4098:1-4108:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="5011:1-5021:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;foo</span>
@@ -3680,18 +4730,21 @@ nested block quote:</p>
<span id="LC9" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4111:1-4114:8" dir="auto">When including an indented code block in a block quote,
+</div>
+<p data-sourcepos="5025:1-5028:8" dir="auto">When including an indented code block in a block quote,
remember that the [block quote marker] includes
both the <code>&gt;</code> and a following space. So <em>five spaces</em> are needed after
the <code>&gt;</code>:</p>
+<div>
+<div><a href="#example-230">Example 230</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4116:1-4120:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; code</span>
+<pre data-sourcepos="5033:1-5037:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; code</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">&gt; not code</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4122:1-4130:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="5039:1-5047:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;code</span>
<span id="LC3" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/blockquote&gt;</span>
@@ -3700,21 +4753,22 @@ the <code>&gt;</code>:</p>
<span id="LC7" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="4134:1-4134:13" dir="auto">
+</div>
+<h2 data-sourcepos="5052:1-5052:13" dir="auto">
<a id="user-content-list-items" class="anchor" href="#list-items" aria-hidden="true"></a>List items</h2>
-<p data-sourcepos="4136:1-4137:49" dir="auto">A <a href="@">list marker</a> is a
+<p data-sourcepos="5054:1-5055:49" dir="auto">A <a href="@">list marker</a> is a
[bullet list marker] or an [ordered list marker].</p>
-<p data-sourcepos="4139:1-4140:32" dir="auto">A <a href="@">bullet list marker</a>
+<p data-sourcepos="5057:1-5058:32" dir="auto">A <a href="@">bullet list marker</a>
is a <code>-</code>, <code>+</code>, or <code>*</code> character.</p>
-<p data-sourcepos="4142:1-4146:18" dir="auto">An <a href="@">ordered list marker</a>
+<p data-sourcepos="5060:1-5064:18" dir="auto">An <a href="@">ordered list marker</a>
is a sequence of 1--9 arabic digits (<code>0-9</code>), followed by either a
<code>.</code> character or a <code>)</code> character. (The reason for the length
limit is that with 10 digits we start seeing integer overflows
in some browsers.)</p>
-<p data-sourcepos="4148:1-4148:40" dir="auto">The following rules define [list items]:</p>
-<ol data-sourcepos="4150:1-4169:0" dir="auto">
-<li data-sourcepos="4150:1-4169:0">
-<p data-sourcepos="4150:5-4158:45"><strong>Basic case.</strong> If a sequence of lines <em>Ls</em> constitute a sequence of
+<p data-sourcepos="5066:1-5066:40" dir="auto">The following rules define [list items]:</p>
+<ol data-sourcepos="5068:1-5087:0" dir="auto">
+<li data-sourcepos="5068:1-5087:0">
+<p data-sourcepos="5068:5-5076:45"><strong>Basic case.</strong> If a sequence of lines <em>Ls</em> constitute a sequence of
blocks <em>Bs</em> starting with a [non-whitespace character], and <em>M</em> is a
list marker of width <em>W</em> followed by 1 ≤ <em>N</em> ≤ 4 spaces, then the result
of prepending <em>M</em> and the following spaces to the first line of
@@ -3723,21 +4777,23 @@ list item with <em>Bs</em> as its contents. The type of the list item
(bullet or ordered) is determined by the type of its list marker.
If the list item is ordered, then it is also assigned a start
number, based on the ordered list marker.</p>
-<p data-sourcepos="4160:5-4160:15">Exceptions:</p>
-<ol data-sourcepos="4162:5-4169:0">
-<li data-sourcepos="4162:5-4166:57">When the first list item in a [list] interrupts
+<p data-sourcepos="5078:5-5078:15">Exceptions:</p>
+<ol data-sourcepos="5080:5-5087:0">
+<li data-sourcepos="5080:5-5084:57">When the first list item in a [list] interrupts
a paragraph---that is, when it starts on a line that would
otherwise count as [paragraph continuation text]---then (a)
the lines <em>Ls</em> must not begin with a blank line, and (b) if
the list item is ordered, the start number must be 1.</li>
-<li data-sourcepos="4167:5-4169:0">If any line is a [thematic break][thematic breaks] then
+<li data-sourcepos="5085:5-5087:0">If any line is a [thematic break][thematic breaks] then
that line is not a list item.</li>
</ol>
</li>
</ol>
-<p data-sourcepos="4170:1-4170:34" dir="auto">For example, let <em>Ls</em> be the lines</p>
+<p data-sourcepos="5088:1-5088:34" dir="auto">For example, let <em>Ls</em> be the lines</p>
+<div>
+<div><a href="#example-231">Example 231</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4172:1-4179:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">A paragraph</span>
+<pre data-sourcepos="5093:1-5100:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">A paragraph</span>
<span id="LC2" class="line" lang="plaintext">with two lines.</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext"> indented code</span>
@@ -3746,7 +4802,7 @@ that line is not a list item.</li>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4181:1-4189:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;A paragraph</span>
+<pre data-sourcepos="5102:1-5110:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;A paragraph</span>
<span id="LC2" class="line" lang="plaintext">with two lines.&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;indented code</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
@@ -3755,11 +4811,14 @@ that line is not a list item.</li>
<span id="LC7" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4192:1-4194:30" dir="auto">And let <em>M</em> be the marker <code>1.</code>, and <em>N</em> = 2. Then rule #1 says
+</div>
+<p data-sourcepos="5114:1-5116:30" dir="auto">And let <em>M</em> be the marker <code>1.</code>, and <em>N</em> = 2. Then rule #1 says
that the following is an ordered list item with start number 1,
and the same contents as <em>Ls</em>:</p>
+<div>
+<div><a href="#example-232">Example 232</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4196:1-4203:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. A paragraph</span>
+<pre data-sourcepos="5121:1-5128:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. A paragraph</span>
<span id="LC2" class="line" lang="plaintext"> with two lines.</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext"> indented code</span>
@@ -3768,7 +4827,7 @@ and the same contents as <em>Ls</em>:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4205:1-4217:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
+<pre data-sourcepos="5130:1-5142:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;A paragraph</span>
<span id="LC4" class="line" lang="plaintext">with two lines.&lt;/p&gt;</span>
@@ -3781,36 +4840,42 @@ and the same contents as <em>Ls</em>:</p>
<span id="LC11" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4220:1-4226:5" dir="auto">The most important thing to notice is that the position of
+</div>
+<p data-sourcepos="5146:1-5152:5" dir="auto">The most important thing to notice is that the position of
the text after the list marker determines how much indentation
is needed in subsequent blocks in the list item. If the list
marker takes up two spaces, and there are three spaces between
the list marker and the next [non-whitespace character], then blocks
must be indented five spaces in order to fall under the list
item.</p>
-<p data-sourcepos="4228:1-4229:24" dir="auto">Here are some examples showing how far content must be indented to be
+<p data-sourcepos="5154:1-5155:24" dir="auto">Here are some examples showing how far content must be indented to be
put under the list item:</p>
+<div>
+<div><a href="#example-233">Example 233</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4231:1-4235:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- one</span>
+<pre data-sourcepos="5160:1-5164:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- one</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> two</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4237:1-4242:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="5166:1-5171:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;one&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;two&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-234">Example 234</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4245:1-4249:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- one</span>
+<pre data-sourcepos="5178:1-5182:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- one</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> two</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4251:1-4258:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="5184:1-5191:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;one&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;two&lt;/p&gt;</span>
@@ -3818,28 +4883,34 @@ put under the list item:</p>
<span id="LC6" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-235">Example 235</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4261:1-4265:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> - one</span>
+<pre data-sourcepos="5198:1-5202:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> - one</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> two</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4267:1-4273:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="5204:1-5210:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;one&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt; two</span>
<span id="LC5" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-236">Example 236</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4276:1-4280:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> - one</span>
+<pre data-sourcepos="5217:1-5221:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> - one</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> two</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4282:1-4289:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="5223:1-5230:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;one&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;two&lt;/p&gt;</span>
@@ -3847,21 +4918,24 @@ put under the list item:</p>
<span id="LC6" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4292:1-4298:13" dir="auto">It is tempting to think of this in terms of columns: the continuation
+</div>
+<p data-sourcepos="5234:1-5240:13" dir="auto">It is tempting to think of this in terms of columns: the continuation
blocks must be indented at least to the column of the first
[non-whitespace character] after the list marker. However, that is not quite right.
The spaces after the list marker determine how much relative indentation
is needed. Which column this indentation reaches will depend on
how the list item is embedded in other constructions, as shown by
this example:</p>
+<div>
+<div><a href="#example-237">Example 237</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4300:1-4304:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &gt; &gt; 1. one</span>
+<pre data-sourcepos="5245:1-5249:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> &gt; &gt; 1. one</span>
<span id="LC2" class="line" lang="plaintext">&gt;&gt;</span>
<span id="LC3" class="line" lang="plaintext">&gt;&gt; two</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4306:1-4317:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="5251:1-5262:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;</span>
@@ -3873,21 +4947,24 @@ this example:</p>
<span id="LC10" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4320:1-4322:67" dir="auto">Here <code>two</code> occurs in the same column as the list marker <code>1.</code>,
+</div>
+<p data-sourcepos="5266:1-5268:67" dir="auto">Here <code>two</code> occurs in the same column as the list marker <code>1.</code>,
but is actually contained in the list item, because there is
sufficient indentation after the last containing blockquote marker.</p>
-<p data-sourcepos="4324:1-4327:38" dir="auto">The converse is also possible. In the following example, the word <code>two</code>
+<p data-sourcepos="5270:1-5273:38" dir="auto">The converse is also possible. In the following example, the word <code>two</code>
occurs far to the right of the initial text of the list item, <code>one</code>, but
it is not considered part of the list item, because it is not indented
far enough past the blockquote marker:</p>
+<div>
+<div><a href="#example-238">Example 238</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4329:1-4333:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt;&gt;- one</span>
+<pre data-sourcepos="5278:1-5282:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt;&gt;- one</span>
<span id="LC2" class="line" lang="plaintext">&gt;&gt;</span>
<span id="LC3" class="line" lang="plaintext"> &gt; &gt; two</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4335:1-4344:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="5284:1-5293:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;one&lt;/li&gt;</span>
@@ -3897,30 +4974,36 @@ far enough past the blockquote marker:</p>
<span id="LC8" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4347:1-4348:51" dir="auto">Note that at least one space is needed between the list marker and
+</div>
+<p data-sourcepos="5297:1-5298:51" dir="auto">Note that at least one space is needed between the list marker and
any following content, so these are not list items:</p>
+<div>
+<div><a href="#example-239">Example 239</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4350:1-4354:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">-one</span>
+<pre data-sourcepos="5303:1-5307:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">-one</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">2.two</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4356:1-4359:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;-one&lt;/p&gt;</span>
+<pre data-sourcepos="5309:1-5312:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;-one&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;2.two&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4362:1-4363:15" dir="auto">A list item may contain blocks that are separated by more than
+</div>
+<p data-sourcepos="5316:1-5317:15" dir="auto">A list item may contain blocks that are separated by more than
one blank line.</p>
+<div>
+<div><a href="#example-240">Example 240</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4365:1-4370:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
+<pre data-sourcepos="5322:1-5327:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext"> bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4372:1-4379:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="5329:1-5336:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span>
@@ -3928,9 +5011,12 @@ one blank line.</p>
<span id="LC6" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4382:1-4382:42" dir="auto">A list item may contain any kind of block:</p>
+</div>
+<p data-sourcepos="5340:1-5340:42" dir="auto">A list item may contain any kind of block:</p>
+<div>
+<div><a href="#example-241">Example 241</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4384:1-4394:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. foo</span>
+<pre data-sourcepos="5345:1-5355:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> ```</span>
<span id="LC4" class="line" lang="plaintext"> bar</span>
@@ -3942,7 +5028,7 @@ one blank line.</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4396:1-4408:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
+<pre data-sourcepos="5357:1-5369:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;bar</span>
@@ -3955,10 +5041,13 @@ one blank line.</p>
<span id="LC11" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4411:1-4412:43" dir="auto">A list item that contains an indented code block will preserve
+</div>
+<p data-sourcepos="5373:1-5374:43" dir="auto">A list item that contains an indented code block will preserve
empty lines within the code block verbatim.</p>
+<div>
+<div><a href="#example-242">Example 242</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4414:1-4421:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- Foo</span>
+<pre data-sourcepos="5379:1-5386:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- Foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> bar</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -3967,7 +5056,7 @@ empty lines within the code block verbatim.</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4423:1-4434:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="5388:1-5399:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;Foo&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;bar</span>
@@ -3979,57 +5068,73 @@ empty lines within the code block verbatim.</p>
<span id="LC10" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4436:1-4436:65" dir="auto">Note that ordered list start numbers must be nine digits or less:</p>
+</div>
+<p data-sourcepos="5402:1-5402:65" dir="auto">Note that ordered list start numbers must be nine digits or less:</p>
+<div>
+<div><a href="#example-243">Example 243</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4438:1-4440:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">123456789. ok</span></code></pre>
+<pre data-sourcepos="5407:1-5409:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">123456789. ok</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4442:1-4446:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol start="123456789"&gt;</span>
+<pre data-sourcepos="5411:1-5415:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol start="123456789"&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;ok&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-244">Example 244</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4449:1-4451:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1234567890. not ok</span></code></pre>
+<pre data-sourcepos="5422:1-5424:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1234567890. not ok</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4453:1-4455:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;1234567890. not ok&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="5426:1-5428:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;1234567890. not ok&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4458:1-4458:33" dir="auto">A start number may begin with 0s:</p>
+</div>
+<p data-sourcepos="5432:1-5432:33" dir="auto">A start number may begin with 0s:</p>
+<div>
+<div><a href="#example-245">Example 245</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4460:1-4462:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">0. ok</span></code></pre>
+<pre data-sourcepos="5437:1-5439:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">0. ok</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4464:1-4468:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol start="0"&gt;</span>
+<pre data-sourcepos="5441:1-5445:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol start="0"&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;ok&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-246">Example 246</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4471:1-4473:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">003. ok</span></code></pre>
+<pre data-sourcepos="5452:1-5454:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">003. ok</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4475:1-4479:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol start="3"&gt;</span>
+<pre data-sourcepos="5456:1-5460:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol start="3"&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;ok&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4482:1-4482:35" dir="auto">A start number may not be negative:</p>
+</div>
+<p data-sourcepos="5464:1-5464:35" dir="auto">A start number may not be negative:</p>
+<div>
+<div><a href="#example-247">Example 247</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4484:1-4486:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">-1. not ok</span></code></pre>
+<pre data-sourcepos="5469:1-5471:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">-1. not ok</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4488:1-4490:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;-1. not ok&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="5473:1-5475:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;-1. not ok&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<ol start="2" data-sourcepos="4494:1-4504:0" dir="auto">
-<li data-sourcepos="4494:1-4504:0">
+</div>
+<ol start="2" data-sourcepos="5480:1-5490:0" dir="auto">
+<li data-sourcepos="5480:1-5490:0">
<strong>Item starting with indented code.</strong> If a sequence of lines <em>Ls</em>
constitute a sequence of blocks <em>Bs</em> starting with an indented code
block, and <em>M</em> is a list marker of width <em>W</em> followed by
@@ -4041,17 +5146,19 @@ list item (bullet or ordered) is determined by the type of its list
marker. If the list item is ordered, then it is also assigned a
start number, based on the ordered list marker.</li>
</ol>
-<p data-sourcepos="4505:1-4507:39" dir="auto">An indented code block will have to be indented four spaces beyond
+<p data-sourcepos="5491:1-5493:39" dir="auto">An indented code block will have to be indented four spaces beyond
the edge of the region where text will be included in the list item.
In the following case that is 6 spaces:</p>
+<div>
+<div><a href="#example-248">Example 248</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4509:1-4513:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
+<pre data-sourcepos="5498:1-5502:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4515:1-4523:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="5504:1-5512:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;bar</span>
@@ -4060,15 +5167,18 @@ In the following case that is 6 spaces:</p>
<span id="LC7" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4526:1-4526:33" dir="auto">And in this case it is 11 spaces:</p>
+</div>
+<p data-sourcepos="5516:1-5516:33" dir="auto">And in this case it is 11 spaces:</p>
+<div>
+<div><a href="#example-249">Example 249</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4528:1-4532:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> 10. foo</span>
+<pre data-sourcepos="5521:1-5525:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> 10. foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4534:1-4542:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol start="10"&gt;</span>
+<pre data-sourcepos="5527:1-5535:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol start="10"&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;bar</span>
@@ -4077,11 +5187,14 @@ In the following case that is 6 spaces:</p>
<span id="LC7" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4545:1-4547:12" dir="auto">If the <em>first</em> block in the list item is an indented code block,
+</div>
+<p data-sourcepos="5539:1-5541:12" dir="auto">If the <em>first</em> block in the list item is an indented code block,
then by rule #2, the contents must be indented <em>one</em> space after the
list marker:</p>
+<div>
+<div><a href="#example-250">Example 250</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4549:1-4555:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> indented code</span>
+<pre data-sourcepos="5546:1-5552:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> indented code</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">paragraph</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -4089,15 +5202,18 @@ list marker:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4557:1-4563:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;indented code</span>
+<pre data-sourcepos="5554:1-5560:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;indented code</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;paragraph&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;more code</span>
<span id="LC5" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-251">Example 251</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4566:1-4572:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. indented code</span>
+<pre data-sourcepos="5567:1-5573:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. indented code</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> paragraph</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -4105,7 +5221,7 @@ list marker:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4574:1-4584:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
+<pre data-sourcepos="5575:1-5585:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;indented code</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
@@ -4116,10 +5232,13 @@ list marker:</p>
<span id="LC9" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4587:1-4588:22" dir="auto">Note that an additional space indent is interpreted as space
+</div>
+<p data-sourcepos="5589:1-5590:22" dir="auto">Note that an additional space indent is interpreted as space
inside the code block:</p>
+<div>
+<div><a href="#example-252">Example 252</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4590:1-4596:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. indented code</span>
+<pre data-sourcepos="5595:1-5601:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. indented code</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> paragraph</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -4127,7 +5246,7 @@ inside the code block:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4598:1-4608:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
+<pre data-sourcepos="5603:1-5613:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt; indented code</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
@@ -4138,49 +5257,58 @@ inside the code block:</p>
<span id="LC9" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4611:1-4617:55" dir="auto">Note that rules #1 and #2 only apply to two cases: (a) cases
+</div>
+<p data-sourcepos="5617:1-5623:55" dir="auto">Note that rules #1 and #2 only apply to two cases: (a) cases
in which the lines to be included in a list item begin with a
[non-whitespace character], and (b) cases in which
they begin with an indented code
block. In a case like the following, where the first block begins with
a three-space indent, the rules do not allow us to form a list item by
indenting the whole thing and prepending a list marker:</p>
+<div>
+<div><a href="#example-253">Example 253</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4619:1-4623:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> foo</span>
+<pre data-sourcepos="5628:1-5632:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4625:1-4628:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
+<pre data-sourcepos="5634:1-5637:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-254">Example 254</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4631:1-4635:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
+<pre data-sourcepos="5644:1-5648:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4637:1-4642:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="5650:1-5655:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4645:1-4648:15" dir="auto">This is not a significant restriction, because when a block begins
+</div>
+<p data-sourcepos="5659:1-5662:15" dir="auto">This is not a significant restriction, because when a block begins
with 1-3 spaces indent, the indentation can always be removed without
a change in interpretation, allowing rule #1 to be applied. So, in
the above case:</p>
+<div>
+<div><a href="#example-255">Example 255</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4650:1-4654:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
+<pre data-sourcepos="5667:1-5671:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4656:1-4663:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="5673:1-5680:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;bar&lt;/p&gt;</span>
@@ -4188,8 +5316,9 @@ the above case:</p>
<span id="LC6" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<ol start="3" data-sourcepos="4666:1-4677:0" dir="auto">
-<li data-sourcepos="4666:1-4677:0">
+</div>
+<ol start="3" data-sourcepos="5684:1-5695:0" dir="auto">
+<li data-sourcepos="5684:1-5695:0">
<strong>Item starting with a blank line.</strong> If a sequence of lines <em>Ls</em>
starting with a single [blank line] constitute a (possibly empty)
sequence of blocks <em>Bs</em>, not separated from each other by more than
@@ -4202,9 +5331,11 @@ list item (bullet or ordered) is determined by the type of its list
marker. If the list item is ordered, then it is also assigned a
start number, based on the ordered list marker.</li>
</ol>
-<p data-sourcepos="4678:1-4678:72" dir="auto">Here are some list items that start with a blank line but are not empty:</p>
+<p data-sourcepos="5696:1-5696:72" dir="auto">Here are some list items that start with a blank line but are not empty:</p>
+<div>
+<div><a href="#example-256">Example 256</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4680:1-4689:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">-</span>
+<pre data-sourcepos="5701:1-5710:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">-</span>
<span id="LC2" class="line" lang="plaintext"> foo</span>
<span id="LC3" class="line" lang="plaintext">-</span>
<span id="LC4" class="line" lang="plaintext"> ```</span>
@@ -4215,7 +5346,7 @@ start number, based on the ordered list marker.</li>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4691:1-4703:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="5712:1-5724:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;bar</span>
@@ -4228,94 +5359,115 @@ start number, based on the ordered list marker.</li>
<span id="LC11" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4705:1-4706:66" dir="auto">When the list item starts with a blank line, the number of spaces
+</div>
+<p data-sourcepos="5727:1-5728:66" dir="auto">When the list item starts with a blank line, the number of spaces
following the list marker doesn't change the required indentation:</p>
+<div>
+<div><a href="#example-257">Example 257</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4708:1-4711:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- </span>
+<pre data-sourcepos="5733:1-5736:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- </span>
<span id="LC2" class="line" lang="plaintext"> foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4713:1-4717:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="5738:1-5742:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4720:1-4722:5" dir="auto">A list item can begin with at most one blank line.
+</div>
+<p data-sourcepos="5746:1-5748:5" dir="auto">A list item can begin with at most one blank line.
In the following example, <code>foo</code> is not part of the list
item:</p>
+<div>
+<div><a href="#example-258">Example 258</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4724:1-4728:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">-</span>
+<pre data-sourcepos="5753:1-5757:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">-</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4730:1-4735:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="5759:1-5764:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4738:1-4738:34" dir="auto">Here is an empty bullet list item:</p>
+</div>
+<p data-sourcepos="5768:1-5768:34" dir="auto">Here is an empty bullet list item:</p>
+<div>
+<div><a href="#example-259">Example 259</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4740:1-4744:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
+<pre data-sourcepos="5773:1-5777:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
<span id="LC2" class="line" lang="plaintext">-</span>
<span id="LC3" class="line" lang="plaintext">- bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4746:1-4752:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="5779:1-5785:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;bar&lt;/li&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4755:1-4755:72" dir="auto">It does not matter whether there are spaces following the [list marker]:</p>
+</div>
+<p data-sourcepos="5789:1-5789:72" dir="auto">It does not matter whether there are spaces following the [list marker]:</p>
+<div>
+<div><a href="#example-260">Example 260</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4757:1-4761:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
+<pre data-sourcepos="5794:1-5798:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
<span id="LC2" class="line" lang="plaintext">- </span>
<span id="LC3" class="line" lang="plaintext">- bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4763:1-4769:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="5800:1-5806:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;bar&lt;/li&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4772:1-4772:35" dir="auto">Here is an empty ordered list item:</p>
+</div>
+<p data-sourcepos="5810:1-5810:35" dir="auto">Here is an empty ordered list item:</p>
+<div>
+<div><a href="#example-261">Example 261</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4774:1-4778:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. foo</span>
+<pre data-sourcepos="5815:1-5819:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. foo</span>
<span id="LC2" class="line" lang="plaintext">2.</span>
<span id="LC3" class="line" lang="plaintext">3. bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4780:1-4786:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
+<pre data-sourcepos="5821:1-5827:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;bar&lt;/li&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4789:1-4789:48" dir="auto">A list may start or end with an empty list item:</p>
+</div>
+<p data-sourcepos="5831:1-5831:48" dir="auto">A list may start or end with an empty list item:</p>
+<div>
+<div><a href="#example-262">Example 262</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4791:1-4793:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*</span></code></pre>
+<pre data-sourcepos="5836:1-5838:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4795:1-4799:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="5840:1-5844:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4801:1-4801:57" dir="auto">However, an empty list item cannot interrupt a paragraph:</p>
+</div>
+<p data-sourcepos="5847:1-5847:57" dir="auto">However, an empty list item cannot interrupt a paragraph:</p>
+<div>
+<div><a href="#example-263">Example 263</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4803:1-4809:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo</span>
+<pre data-sourcepos="5852:1-5858:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo</span>
<span id="LC2" class="line" lang="plaintext">*</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">foo</span>
@@ -4323,23 +5475,26 @@ item:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4811:1-4816:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo</span>
+<pre data-sourcepos="5860:1-5865:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo</span>
<span id="LC2" class="line" lang="plaintext">*&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;foo</span>
<span id="LC4" class="line" lang="plaintext">1.&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<ol start="4" data-sourcepos="4819:1-4824:0" dir="auto">
-<li data-sourcepos="4819:1-4824:0">
+</div>
+<ol start="4" data-sourcepos="5869:1-5874:0" dir="auto">
+<li data-sourcepos="5869:1-5874:0">
<strong>Indentation.</strong> If a sequence of lines <em>Ls</em> constitutes a list item
according to rule #1, #2, or #3, then the result of indenting each line
of <em>Ls</em> by 1-3 spaces (the same for each line) also constitutes a
list item with the same contents and attributes. If a line is
empty, then it need not be indented.</li>
</ol>
-<p data-sourcepos="4825:1-4825:19" dir="auto">Indented one space:</p>
+<p data-sourcepos="5875:1-5875:19" dir="auto">Indented one space:</p>
+<div>
+<div><a href="#example-264">Example 264</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4827:1-4834:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> 1. A paragraph</span>
+<pre data-sourcepos="5880:1-5887:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> 1. A paragraph</span>
<span id="LC2" class="line" lang="plaintext"> with two lines.</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext"> indented code</span>
@@ -4348,7 +5503,7 @@ empty, then it need not be indented.</li>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4836:1-4848:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
+<pre data-sourcepos="5889:1-5901:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;A paragraph</span>
<span id="LC4" class="line" lang="plaintext">with two lines.&lt;/p&gt;</span>
@@ -4361,9 +5516,12 @@ empty, then it need not be indented.</li>
<span id="LC11" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4851:1-4851:20" dir="auto">Indented two spaces:</p>
+</div>
+<p data-sourcepos="5905:1-5905:20" dir="auto">Indented two spaces:</p>
+<div>
+<div><a href="#example-265">Example 265</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4853:1-4860:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> 1. A paragraph</span>
+<pre data-sourcepos="5910:1-5917:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> 1. A paragraph</span>
<span id="LC2" class="line" lang="plaintext"> with two lines.</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext"> indented code</span>
@@ -4372,7 +5530,7 @@ empty, then it need not be indented.</li>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4862:1-4874:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
+<pre data-sourcepos="5919:1-5931:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;A paragraph</span>
<span id="LC4" class="line" lang="plaintext">with two lines.&lt;/p&gt;</span>
@@ -4385,9 +5543,12 @@ empty, then it need not be indented.</li>
<span id="LC11" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4877:1-4877:22" dir="auto">Indented three spaces:</p>
+</div>
+<p data-sourcepos="5935:1-5935:22" dir="auto">Indented three spaces:</p>
+<div>
+<div><a href="#example-266">Example 266</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4879:1-4886:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> 1. A paragraph</span>
+<pre data-sourcepos="5940:1-5947:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> 1. A paragraph</span>
<span id="LC2" class="line" lang="plaintext"> with two lines.</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext"> indented code</span>
@@ -4396,7 +5557,7 @@ empty, then it need not be indented.</li>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4888:1-4900:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
+<pre data-sourcepos="5949:1-5961:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;A paragraph</span>
<span id="LC4" class="line" lang="plaintext">with two lines.&lt;/p&gt;</span>
@@ -4409,9 +5570,12 @@ empty, then it need not be indented.</li>
<span id="LC11" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4903:1-4903:38" dir="auto">Four spaces indent gives a code block:</p>
+</div>
+<p data-sourcepos="5965:1-5965:38" dir="auto">Four spaces indent gives a code block:</p>
+<div>
+<div><a href="#example-267">Example 267</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4905:1-4912:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> 1. A paragraph</span>
+<pre data-sourcepos="5970:1-5977:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> 1. A paragraph</span>
<span id="LC2" class="line" lang="plaintext"> with two lines.</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext"> indented code</span>
@@ -4420,7 +5584,7 @@ empty, then it need not be indented.</li>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4914:1-4922:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;1. A paragraph</span>
+<pre data-sourcepos="5979:1-5987:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;1. A paragraph</span>
<span id="LC2" class="line" lang="plaintext"> with two lines.</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext"> indented code</span>
@@ -4429,8 +5593,9 @@ empty, then it need not be indented.</li>
<span id="LC7" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<ol start="5" data-sourcepos="4926:1-4934:0" dir="auto">
-<li data-sourcepos="4926:1-4934:0">
+</div>
+<ol start="5" data-sourcepos="5992:1-6000:0" dir="auto">
+<li data-sourcepos="5992:1-6000:0">
<strong>Laziness.</strong> If a string of lines <em>Ls</em> constitute a <a href="#list-items">list
item</a> with contents <em>Bs</em>, then the result of deleting
some or all of the indentation from one or more lines in which the
@@ -4440,9 +5605,11 @@ list item with the same contents and attributes. The unindented
lines are called
<a href="@">lazy continuation line</a>s.</li>
</ol>
-<p data-sourcepos="4935:1-4935:50" dir="auto">Here is an example with [lazy continuation lines]:</p>
+<p data-sourcepos="6001:1-6001:50" dir="auto">Here is an example with [lazy continuation lines]:</p>
+<div>
+<div><a href="#example-268">Example 268</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4937:1-4944:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> 1. A paragraph</span>
+<pre data-sourcepos="6006:1-6013:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> 1. A paragraph</span>
<span id="LC2" class="line" lang="plaintext">with two lines.</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext"> indented code</span>
@@ -4451,7 +5618,7 @@ lines are called
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4946:1-4958:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
+<pre data-sourcepos="6015:1-6027:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;A paragraph</span>
<span id="LC4" class="line" lang="plaintext">with two lines.&lt;/p&gt;</span>
@@ -4464,27 +5631,33 @@ lines are called
<span id="LC11" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4961:1-4961:37" dir="auto">Indentation can be partially deleted:</p>
+</div>
+<p data-sourcepos="6031:1-6031:37" dir="auto">Indentation can be partially deleted:</p>
+<div>
+<div><a href="#example-269">Example 269</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4963:1-4966:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> 1. A paragraph</span>
+<pre data-sourcepos="6036:1-6039:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> 1. A paragraph</span>
<span id="LC2" class="line" lang="plaintext"> with two lines.</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4968:1-4973:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
+<pre data-sourcepos="6041:1-6046:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;A paragraph</span>
<span id="LC3" class="line" lang="plaintext">with two lines.&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="4976:1-4976:63" dir="auto">These examples show how laziness can work in nested structures:</p>
+</div>
+<p data-sourcepos="6050:1-6050:63" dir="auto">These examples show how laziness can work in nested structures:</p>
+<div>
+<div><a href="#example-270">Example 270</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4978:1-4981:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; 1. &gt; Blockquote</span>
+<pre data-sourcepos="6055:1-6058:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; 1. &gt; Blockquote</span>
<span id="LC2" class="line" lang="plaintext">continued here.</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4983:1-4994:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="6060:1-6071:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;blockquote&gt;</span>
@@ -4496,13 +5669,16 @@ lines are called
<span id="LC10" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-271">Example 271</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="4997:1-5000:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; 1. &gt; Blockquote</span>
+<pre data-sourcepos="6078:1-6081:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; 1. &gt; Blockquote</span>
<span id="LC2" class="line" lang="plaintext">&gt; continued here.</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5002:1-5013:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
+<pre data-sourcepos="6083:1-6094:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;blockquote&gt;</span>
@@ -4514,25 +5690,28 @@ lines are called
<span id="LC10" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<ol start="6" data-sourcepos="5017:1-5019:0" dir="auto">
-<li data-sourcepos="5017:1-5019:0">
+</div>
+<ol start="6" data-sourcepos="6099:1-6101:0" dir="auto">
+<li data-sourcepos="6099:1-6101:0">
<strong>That's all.</strong> Nothing that is not counted as a list item by rules
#1--5 counts as a <a href="#list-items">list item</a>.</li>
</ol>
-<p data-sourcepos="5020:1-5023:17" dir="auto">The rules for sublists follow from the general rules
+<p data-sourcepos="6102:1-6105:17" dir="auto">The rules for sublists follow from the general rules
[above][List items]. A sublist must be indented the same number
of spaces a paragraph would need to be in order to be included
in the list item.</p>
-<p data-sourcepos="5025:1-5025:43" dir="auto">So, in this case we need two spaces indent:</p>
+<p data-sourcepos="6107:1-6107:43" dir="auto">So, in this case we need two spaces indent:</p>
+<div>
+<div><a href="#example-272">Example 272</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5027:1-5032:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
+<pre data-sourcepos="6112:1-6117:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
<span id="LC2" class="line" lang="plaintext"> - bar</span>
<span id="LC3" class="line" lang="plaintext"> - baz</span>
<span id="LC4" class="line" lang="plaintext"> - boo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5034:1-5050:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="6119:1-6135:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo</span>
<span id="LC3" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;bar</span>
@@ -4549,16 +5728,19 @@ in the list item.</p>
<span id="LC15" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5053:1-5053:18" dir="auto">One is not enough:</p>
+</div>
+<p data-sourcepos="6139:1-6139:18" dir="auto">One is not enough:</p>
+<div>
+<div><a href="#example-273">Example 273</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5055:1-5060:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
+<pre data-sourcepos="6144:1-6149:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
<span id="LC2" class="line" lang="plaintext"> - bar</span>
<span id="LC3" class="line" lang="plaintext"> - baz</span>
<span id="LC4" class="line" lang="plaintext"> - boo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5062:1-5069:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="6151:1-6158:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;bar&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;baz&lt;/li&gt;</span>
@@ -4566,14 +5748,17 @@ in the list item.</p>
<span id="LC6" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5072:1-5072:52" dir="auto">Here we need four, because the list marker is wider:</p>
+</div>
+<p data-sourcepos="6162:1-6162:52" dir="auto">Here we need four, because the list marker is wider:</p>
+<div>
+<div><a href="#example-274">Example 274</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5074:1-5077:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">10) foo</span>
+<pre data-sourcepos="6167:1-6170:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">10) foo</span>
<span id="LC2" class="line" lang="plaintext"> - bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5079:1-5087:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol start="10"&gt;</span>
+<pre data-sourcepos="6172:1-6180:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol start="10"&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo</span>
<span id="LC3" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;bar&lt;/li&gt;</span>
@@ -4582,14 +5767,17 @@ in the list item.</p>
<span id="LC7" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5090:1-5090:20" dir="auto">Three is not enough:</p>
+</div>
+<p data-sourcepos="6184:1-6184:20" dir="auto">Three is not enough:</p>
+<div>
+<div><a href="#example-275">Example 275</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5092:1-5095:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">10) foo</span>
+<pre data-sourcepos="6189:1-6192:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">10) foo</span>
<span id="LC2" class="line" lang="plaintext"> - bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5097:1-5104:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol start="10"&gt;</span>
+<pre data-sourcepos="6194:1-6201:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol start="10"&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/ol&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;ul&gt;</span>
@@ -4597,13 +5785,16 @@ in the list item.</p>
<span id="LC6" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5107:1-5107:45" dir="auto">A list may be the first block in a list item:</p>
+</div>
+<p data-sourcepos="6205:1-6205:45" dir="auto">A list may be the first block in a list item:</p>
+<div>
+<div><a href="#example-276">Example 276</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5109:1-5111:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- - foo</span></code></pre>
+<pre data-sourcepos="6210:1-6212:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- - foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5113:1-5121:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="6214:1-6222:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;foo&lt;/li&gt;</span>
@@ -4612,12 +5803,15 @@ in the list item.</p>
<span id="LC7" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-277">Example 277</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5124:1-5126:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. - 2. foo</span></code></pre>
+<pre data-sourcepos="6229:1-6231:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. - 2. foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5128:1-5140:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
+<pre data-sourcepos="6233:1-6245:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;</span>
@@ -4630,16 +5824,19 @@ in the list item.</p>
<span id="LC11" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5143:1-5143:34" dir="auto">A list item can contain a heading:</p>
+</div>
+<p data-sourcepos="6249:1-6249:34" dir="auto">A list item can contain a heading:</p>
+<div>
+<div><a href="#example-278">Example 278</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5145:1-5150:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- # Foo</span>
+<pre data-sourcepos="6254:1-6259:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- # Foo</span>
<span id="LC2" class="line" lang="plaintext">- Bar</span>
<span id="LC3" class="line" lang="plaintext"> ---</span>
<span id="LC4" class="line" lang="plaintext"> baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5152:1-5161:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="6261:1-6270:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;h1&gt;Foo&lt;/h1&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/li&gt;</span>
@@ -4649,38 +5846,39 @@ in the list item.</p>
<span id="LC8" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h3 data-sourcepos="5164:1-5164:14" dir="auto">
+</div>
+<h3 data-sourcepos="6274:1-6274:14" dir="auto">
<a id="user-content-motivation" class="anchor" href="#motivation" aria-hidden="true"></a>Motivation</h3>
-<p data-sourcepos="5166:1-5166:64" dir="auto">John Gruber's Markdown spec says the following about list items:</p>
-<ol data-sourcepos="5168:1-5187:0" dir="auto">
-<li data-sourcepos="5168:1-5171:0">
-<p data-sourcepos="5168:4-5170:20">"List markers typically start at the left margin, but may be indented
+<p data-sourcepos="6276:1-6276:64" dir="auto">John Gruber's Markdown spec says the following about list items:</p>
+<ol data-sourcepos="6278:1-6297:0" dir="auto">
+<li data-sourcepos="6278:1-6281:0">
+<p data-sourcepos="6278:4-6280:20">"List markers typically start at the left margin, but may be indented
by up to three spaces. List markers must be followed by one or more
spaces or a tab."</p>
</li>
-<li data-sourcepos="5172:1-5174:0">
-<p data-sourcepos="5172:4-5173:48">"To make lists look nice, you can wrap items with hanging indents....
+<li data-sourcepos="6282:1-6284:0">
+<p data-sourcepos="6282:4-6283:48">"To make lists look nice, you can wrap items with hanging indents....
But if you don't want to, you don't have to."</p>
</li>
-<li data-sourcepos="5175:1-5178:0">
-<p data-sourcepos="5175:4-5177:8">"List items may consist of multiple paragraphs. Each subsequent
+<li data-sourcepos="6285:1-6288:0">
+<p data-sourcepos="6285:4-6287:8">"List items may consist of multiple paragraphs. Each subsequent
paragraph in a list item must be indented by either 4 spaces or one
tab."</p>
</li>
-<li data-sourcepos="5179:1-5181:0">
-<p data-sourcepos="5179:4-5180:55">"It looks nice if you indent every line of the subsequent paragraphs,
+<li data-sourcepos="6289:1-6291:0">
+<p data-sourcepos="6289:4-6290:55">"It looks nice if you indent every line of the subsequent paragraphs,
but here again, Markdown will allow you to be lazy."</p>
</li>
-<li data-sourcepos="5182:1-5184:0">
-<p data-sourcepos="5182:4-5183:35">"To put a blockquote within a list item, the blockquote's <code>&gt;</code>
+<li data-sourcepos="6292:1-6294:0">
+<p data-sourcepos="6292:4-6293:35">"To put a blockquote within a list item, the blockquote's <code>&gt;</code>
delimiters need to be indented."</p>
</li>
-<li data-sourcepos="5185:1-5187:0">
-<p data-sourcepos="5185:4-5186:44">"To put a code block within a list item, the code block needs to be
+<li data-sourcepos="6295:1-6297:0">
+<p data-sourcepos="6295:4-6296:44">"To put a code block within a list item, the code block needs to be
indented twice — 8 spaces or two tabs."</p>
</li>
</ol>
-<p data-sourcepos="5188:1-5197:18" dir="auto">These rules specify that a paragraph under a list item must be indented
+<p data-sourcepos="6298:1-6307:18" dir="auto">These rules specify that a paragraph under a list item must be indented
four spaces (presumably, from the left margin, rather than the start of
the list marker, but this is not said), and that code under a list item
must be indented eight spaces instead of the usual four. They also say
@@ -4690,7 +5888,7 @@ about other kinds of block-level content, it is certainly reasonable to
infer that <em>all</em> block elements under a list item, including other
lists, must be indented four spaces. This principle has been called the
<em>four-space rule</em>.</p>
-<p data-sourcepos="5199:1-5210:48" dir="auto">The four-space rule is clear and principled, and if the reference
+<p data-sourcepos="6309:1-6320:48" dir="auto">The four-space rule is clear and principled, and if the reference
implementation <code>Markdown.pl</code> had followed it, it probably would have
become the standard. However, <code>Markdown.pl</code> allowed paragraphs and
sublists to start with only two spaces indentation, at least on the
@@ -4702,33 +5900,33 @@ determining what comes under a list item. (Pandoc and python-Markdown,
for example, stuck with Gruber's syntax description and the four-space
rule, while discount, redcarpet, marked, PHP Markdown, and others
followed <code>Markdown.pl</code>'s behavior more closely.)</p>
-<p data-sourcepos="5212:1-5217:45" dir="auto">Unfortunately, given the divergences between implementations, there
+<p data-sourcepos="6322:1-6327:45" dir="auto">Unfortunately, given the divergences between implementations, there
is no way to give a spec for list items that will be guaranteed not
to break any existing documents. However, the spec given here should
correctly handle lists formatted with either the four-space rule or
the more forgiving <code>Markdown.pl</code> behavior, provided they are laid out
in a way that is natural for a human to read.</p>
-<p data-sourcepos="5219:1-5225:22" dir="auto">The strategy here is to let the width and indentation of the list marker
+<p data-sourcepos="6329:1-6335:22" dir="auto">The strategy here is to let the width and indentation of the list marker
determine the indentation necessary for blocks to fall under the list
item, rather than having a fixed and arbitrary number. The writer can
think of the body of the list item as a unit which gets indented to the
right enough to fit the list marker (and any indentation on the list
marker). (The laziness rule, #5, then allows continuation lines to be
unindented if needed.)</p>
-<p data-sourcepos="5227:1-5229:39" dir="auto">This rule is superior, we claim, to any rule requiring a fixed level of
+<p data-sourcepos="6337:1-6339:39" dir="auto">This rule is superior, we claim, to any rule requiring a fixed level of
indentation from the margin. The four-space rule is clear but
unnatural. It is quite unintuitive that</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5231:1-5237:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="p">-</span> foo</span>
+<pre data-sourcepos="6341:1-6347:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="p">-</span> foo</span>
<span id="LC2" class="line" lang="markdown"></span>
<span id="LC3" class="line" lang="markdown"> bar</span>
<span id="LC4" class="line" lang="markdown"></span>
<span id="LC5" class="line" lang="markdown"><span class="p"> -</span> baz</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5239:1-5239:60" dir="auto">should be parsed as two lists with an intervening paragraph,</p>
+<p data-sourcepos="6349:1-6349:60" dir="auto">should be parsed as two lists with an intervening paragraph,</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5241:1-5249:3" lang="html" class="code highlight js-syntax-highlight language-html" v-pre="true"><code><span id="LC1" class="line" lang="html"><span class="nt">&lt;ul&gt;</span></span>
+<pre data-sourcepos="6351:1-6359:3" lang="html" class="code highlight js-syntax-highlight language-html" v-pre="true"><code><span id="LC1" class="line" lang="html"><span class="nt">&lt;ul&gt;</span></span>
<span id="LC2" class="line" lang="html"><span class="nt">&lt;li&gt;</span>foo<span class="nt">&lt;/li&gt;</span></span>
<span id="LC3" class="line" lang="html"><span class="nt">&lt;/ul&gt;</span></span>
<span id="LC4" class="line" lang="html"><span class="nt">&lt;p&gt;</span>bar<span class="nt">&lt;/p&gt;</span></span>
@@ -4737,9 +5935,9 @@ unnatural. It is quite unintuitive that</p>
<span id="LC7" class="line" lang="html"><span class="nt">&lt;/ul&gt;</span></span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5251:1-5251:58" dir="auto">as the four-space rule demands, rather than a single list,</p>
+<p data-sourcepos="6361:1-6361:58" dir="auto">as the four-space rule demands, rather than a single list,</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5253:1-5263:3" lang="html" class="code highlight js-syntax-highlight language-html" v-pre="true"><code><span id="LC1" class="line" lang="html"><span class="nt">&lt;ul&gt;</span></span>
+<pre data-sourcepos="6363:1-6373:3" lang="html" class="code highlight js-syntax-highlight language-html" v-pre="true"><code><span id="LC1" class="line" lang="html"><span class="nt">&lt;ul&gt;</span></span>
<span id="LC2" class="line" lang="html"><span class="nt">&lt;li&gt;</span></span>
<span id="LC3" class="line" lang="html"><span class="nt">&lt;p&gt;</span>foo<span class="nt">&lt;/p&gt;</span></span>
<span id="LC4" class="line" lang="html"><span class="nt">&lt;p&gt;</span>bar<span class="nt">&lt;/p&gt;</span></span>
@@ -4750,22 +5948,22 @@ unnatural. It is quite unintuitive that</p>
<span id="LC9" class="line" lang="html"><span class="nt">&lt;/ul&gt;</span></span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5265:1-5266:62" dir="auto">The choice of four spaces is arbitrary. It can be learned, but it is
+<p data-sourcepos="6375:1-6376:62" dir="auto">The choice of four spaces is arbitrary. It can be learned, but it is
not likely to be guessed, and it trips up beginners regularly.</p>
-<p data-sourcepos="5268:1-5272:20" dir="auto">Would it help to adopt a two-space rule? The problem is that such
+<p data-sourcepos="6378:1-6382:20" dir="auto">Would it help to adopt a two-space rule? The problem is that such
a rule, together with the rule allowing 1--3 spaces indentation of the
initial list marker, allows text that is indented <em>less than</em> the
original list marker to be included in the list item. For example,
<code>Markdown.pl</code> parses</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5274:1-5278:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="p"> -</span> one</span>
+<pre data-sourcepos="6384:1-6388:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="p"> -</span> one</span>
<span id="LC2" class="line" lang="markdown"></span>
<span id="LC3" class="line" lang="markdown"> two</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5280:1-5280:59" dir="auto">as a single list item, with <code>two</code> a continuation paragraph:</p>
+<p data-sourcepos="6390:1-6390:59" dir="auto">as a single list item, with <code>two</code> a continuation paragraph:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5282:1-5289:3" lang="html" class="code highlight js-syntax-highlight language-html" v-pre="true"><code><span id="LC1" class="line" lang="html"><span class="nt">&lt;ul&gt;</span></span>
+<pre data-sourcepos="6392:1-6399:3" lang="html" class="code highlight js-syntax-highlight language-html" v-pre="true"><code><span id="LC1" class="line" lang="html"><span class="nt">&lt;ul&gt;</span></span>
<span id="LC2" class="line" lang="html"><span class="nt">&lt;li&gt;</span></span>
<span id="LC3" class="line" lang="html"><span class="nt">&lt;p&gt;</span>one<span class="nt">&lt;/p&gt;</span></span>
<span id="LC4" class="line" lang="html"><span class="nt">&lt;p&gt;</span>two<span class="nt">&lt;/p&gt;</span></span>
@@ -4773,16 +5971,16 @@ original list marker to be included in the list item. For example,
<span id="LC6" class="line" lang="html"><span class="nt">&lt;/ul&gt;</span></span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5291:1-5291:13" dir="auto">and similarly</p>
+<p data-sourcepos="6401:1-6401:13" dir="auto">and similarly</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5293:1-5297:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="gt">&gt; - one</span></span>
+<pre data-sourcepos="6403:1-6407:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="gt">&gt; - one</span></span>
<span id="LC2" class="line" lang="markdown"><span class="gt">&gt;</span></span>
<span id="LC3" class="line" lang="markdown"><span class="gt">&gt; two</span></span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5299:1-5299:2" dir="auto">as</p>
+<p data-sourcepos="6409:1-6409:2" dir="auto">as</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5301:1-5310:3" lang="html" class="code highlight js-syntax-highlight language-html" v-pre="true"><code><span id="LC1" class="line" lang="html"><span class="nt">&lt;blockquote&gt;</span></span>
+<pre data-sourcepos="6411:1-6420:3" lang="html" class="code highlight js-syntax-highlight language-html" v-pre="true"><code><span id="LC1" class="line" lang="html"><span class="nt">&lt;blockquote&gt;</span></span>
<span id="LC2" class="line" lang="html"><span class="nt">&lt;ul&gt;</span></span>
<span id="LC3" class="line" lang="html"><span class="nt">&lt;li&gt;</span></span>
<span id="LC4" class="line" lang="html"><span class="nt">&lt;p&gt;</span>one<span class="nt">&lt;/p&gt;</span></span>
@@ -4792,33 +5990,33 @@ original list marker to be included in the list item. For example,
<span id="LC8" class="line" lang="html"><span class="nt">&lt;/blockquote&gt;</span></span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5312:1-5312:30" dir="auto">This is extremely unintuitive.</p>
-<p data-sourcepos="5314:1-5319:52" dir="auto">Rather than requiring a fixed indent from the margin, we could require
+<p data-sourcepos="6422:1-6422:30" dir="auto">This is extremely unintuitive.</p>
+<p data-sourcepos="6424:1-6429:52" dir="auto">Rather than requiring a fixed indent from the margin, we could require
a fixed indent (say, two spaces, or even one space) from the list marker (which
may itself be indented). This proposal would remove the last anomaly
discussed. Unlike the spec presented above, it would count the following
as a list item with a subparagraph, even though the paragraph <code>bar</code>
is not indented as far as the first paragraph <code>foo</code>:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5321:1-5325:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="p"> 10.</span> foo</span>
+<pre data-sourcepos="6431:1-6435:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="p"> 10.</span> foo</span>
<span id="LC2" class="line" lang="markdown"></span>
<span id="LC3" class="line" lang="markdown"> bar </span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5327:1-5330:62" dir="auto">Arguably this text does read like a list item with <code>bar</code> as a subparagraph,
+<p data-sourcepos="6437:1-6440:62" dir="auto">Arguably this text does read like a list item with <code>bar</code> as a subparagraph,
which may count in favor of the proposal. However, on this proposal indented
code would have to be indented six spaces after the list marker. And this
would break a lot of existing Markdown, which has the pattern:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5332:1-5336:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="p">1.</span> foo</span>
+<pre data-sourcepos="6442:1-6446:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="p">1.</span> foo</span>
<span id="LC2" class="line" lang="markdown"></span>
<span id="LC3" class="line" lang="markdown"><span class="sb"> indented code</span></span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5338:1-5340:28" dir="auto">where the code is indented eight spaces. The spec above, by contrast, will
+<p data-sourcepos="6448:1-6450:28" dir="auto">where the code is indented eight spaces. The spec above, by contrast, will
parse this text as expected, since the code block's indentation is measured
from the beginning of <code>foo</code>.</p>
-<p data-sourcepos="5342:1-5348:62" dir="auto">The one case that needs special treatment is a list item that <em>starts</em>
+<p data-sourcepos="6452:1-6458:62" dir="auto">The one case that needs special treatment is a list item that <em>starts</em>
with indented code. How much indentation is required in that case, since
we don't have a "first paragraph" to measure from? Rule #2 simply stipulates
that in such cases, we require one space indentation from the list marker
@@ -4826,46 +6024,51 @@ that in such cases, we require one space indentation from the list marker
four-space rule in cases where the list marker plus its initial indentation
takes four spaces (a common case), but diverge in other cases.</p>
<div>
-<h2 data-sourcepos="5352:1-5352:30">
+<h2 data-sourcepos="6462:1-6462:30">
<a id="user-content-task-list-items-extension" class="anchor" href="#task-list-items-extension" aria-hidden="true"></a>Task list items (extension)</h2>
-<p data-sourcepos="5354:1-5355:26">GFM enables the <code>tasklist</code> extension, where an additional processing step is
+<p data-sourcepos="6464:1-6465:26">GFM enables the <code>tasklist</code> extension, where an additional processing step is
performed on [list items].</p>
-<p data-sourcepos="5357:1-5359:46">A <a href="@">task list item</a> is a [list item][list items] where the first block in it
+<p data-sourcepos="6467:1-6469:46">A <a href="@">task list item</a> is a [list item][list items] where the first block in it
is a paragraph which begins with a [task list item marker] and at least one
whitespace character before any other content.</p>
-<p data-sourcepos="5361:1-5363:55">A <a href="@">task list item marker</a> consists of an optional number of spaces, a left
+<p data-sourcepos="6471:1-6473:55">A <a href="@">task list item marker</a> consists of an optional number of spaces, a left
bracket (<code>[</code>), either a whitespace character or the letter <code>x</code> in either
lowercase or uppercase, and then a right bracket (<code>]</code>).</p>
-<p data-sourcepos="5365:1-5366:70">When rendered, the [task list item marker] is replaced with a semantic checkbox element;
+<p data-sourcepos="6475:1-6476:70">When rendered, the [task list item marker] is replaced with a semantic checkbox element;
in an HTML output, this would be an <code>&lt;input type="checkbox"&gt;</code> element.</p>
-<p data-sourcepos="5368:1-5369:50">If the character between the brackets is a whitespace character, the checkbox
+<p data-sourcepos="6478:1-6479:50">If the character between the brackets is a whitespace character, the checkbox
is unchecked. Otherwise, the checkbox is checked.</p>
-<p data-sourcepos="5371:1-5374:28">This spec does not define how the checkbox elements are interacted with: in practice,
+<p data-sourcepos="6481:1-6484:28">This spec does not define how the checkbox elements are interacted with: in practice,
implementors are free to render the checkboxes as disabled or inmutable elements,
or they may dynamically handle dynamic interactions (i.e. checking, unchecking) in
the final rendered document.</p>
+<div>
+<div><a href="#example-279">Example 279</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5376:1-5379:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [ ] foo</span>
+<pre data-sourcepos="6489:1-6492:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [ ] foo</span>
<span id="LC2" class="line" lang="plaintext">- [x] bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5381:1-5386:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="6494:1-6499:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;&lt;input checked="" disabled="" type="checkbox"&gt; bar&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5388:1-5388:37">Task lists can be arbitrarily nested:</p>
+</div>
+<p data-sourcepos="6502:1-6502:37">Task lists can be arbitrarily nested:</p>
+<div>
+<div><a href="#example-280">Example 280</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5390:1-5395:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [x] foo</span>
+<pre data-sourcepos="6507:1-6512:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [x] foo</span>
<span id="LC2" class="line" lang="plaintext"> - [ ] bar</span>
<span id="LC3" class="line" lang="plaintext"> - [x] baz</span>
<span id="LC4" class="line" lang="plaintext">- [ ] bim</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5397:1-5407:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="6514:1-6524:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;&lt;input checked="" disabled="" type="checkbox"&gt; foo</span>
<span id="LC3" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; bar&lt;/li&gt;</span>
@@ -4877,41 +6080,44 @@ the final rendered document.</p>
<copy-code></copy-code>
</div>
</div>
-<h2 data-sourcepos="5411:1-5411:8" dir="auto">
+</div>
+<h2 data-sourcepos="6529:1-6529:8" dir="auto">
<a id="user-content-lists" class="anchor" href="#lists" aria-hidden="true"></a>Lists</h2>
-<p data-sourcepos="5413:1-5415:46" dir="auto">A <a href="@">list</a> is a sequence of one or more
+<p data-sourcepos="6531:1-6533:46" dir="auto">A <a href="@">list</a> is a sequence of one or more
list items [of the same type]. The list items
may be separated by any number of blank lines.</p>
-<p data-sourcepos="5417:1-5422:30" dir="auto">Two list items are <a href="@">of the same type</a>
+<p data-sourcepos="6535:1-6540:30" dir="auto">Two list items are <a href="@">of the same type</a>
if they begin with a [list marker] of the same type.
Two list markers are of the
same type if (a) they are bullet list markers using the same character
(<code>-</code>, <code>+</code>, or <code>*</code>) or (b) they are ordered list numbers with the same
delimiter (either <code>.</code> or <code>)</code>).</p>
-<p data-sourcepos="5424:1-5428:39" dir="auto">A list is an <a href="@">ordered list</a>
+<p data-sourcepos="6542:1-6546:39" dir="auto">A list is an <a href="@">ordered list</a>
if its constituent list items begin with
[ordered list markers], and a
<a href="@">bullet list</a> if its constituent list
items begin with [bullet list markers].</p>
-<p data-sourcepos="5430:1-5433:12" dir="auto">The <a href="@">start number</a>
+<p data-sourcepos="6548:1-6551:12" dir="auto">The <a href="@">start number</a>
of an [ordered list] is determined by the list number of
its initial list item. The numbers of subsequent list items are
disregarded.</p>
-<p data-sourcepos="5435:1-5440:65" dir="auto">A list is <a href="@">loose</a> if any of its constituent
+<p data-sourcepos="6553:1-6558:65" dir="auto">A list is <a href="@">loose</a> if any of its constituent
list items are separated by blank lines, or if any of its constituent
list items directly contain two block-level elements with a blank line
between them. Otherwise a list is <a href="@">tight</a>.
(The difference in HTML output is that paragraphs in a loose list are
wrapped in <code>&lt;p&gt;</code> tags, while paragraphs in a tight list are not.)</p>
-<p data-sourcepos="5442:1-5442:64" dir="auto">Changing the bullet or ordered list delimiter starts a new list:</p>
+<p data-sourcepos="6560:1-6560:64" dir="auto">Changing the bullet or ordered list delimiter starts a new list:</p>
+<div>
+<div><a href="#example-281">Example 281</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5444:1-5448:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
+<pre data-sourcepos="6565:1-6569:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
<span id="LC2" class="line" lang="plaintext">- bar</span>
<span id="LC3" class="line" lang="plaintext">+ baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5450:1-5458:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="6571:1-6579:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;bar&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/ul&gt;</span>
@@ -4920,14 +6126,17 @@ wrapped in <code>&lt;p&gt;</code> tags, while paragraphs in a tight list are not
<span id="LC7" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-282">Example 282</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5461:1-5465:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. foo</span>
+<pre data-sourcepos="6586:1-6590:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. foo</span>
<span id="LC2" class="line" lang="plaintext">2. bar</span>
<span id="LC3" class="line" lang="plaintext">3) baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5467:1-5475:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
+<pre data-sourcepos="6592:1-6600:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;bar&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/ol&gt;</span>
@@ -4936,107 +6145,119 @@ wrapped in <code>&lt;p&gt;</code> tags, while paragraphs in a tight list are not
<span id="LC7" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5478:1-5480:5" dir="auto">In CommonMark, a list can interrupt a paragraph. That is,
+</div>
+<p data-sourcepos="6604:1-6606:5" dir="auto">In CommonMark, a list can interrupt a paragraph. That is,
no blank line is needed to separate a paragraph from a following
list:</p>
+<div>
+<div><a href="#example-283">Example 283</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5482:1-5486:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
+<pre data-sourcepos="6611:1-6615:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo</span>
<span id="LC2" class="line" lang="plaintext">- bar</span>
<span id="LC3" class="line" lang="plaintext">- baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5488:1-5494:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo&lt;/p&gt;</span>
+<pre data-sourcepos="6617:1-6623:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;bar&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;baz&lt;/li&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5496:1-5497:37" dir="auto"><code>Markdown.pl</code> does not allow this, through fear of triggering a list
+</div>
+<p data-sourcepos="6626:1-6627:37" dir="auto"><code>Markdown.pl</code> does not allow this, through fear of triggering a list
via a numeral in a hard-wrapped line:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5499:1-5502:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown">The number of windows in my house is</span>
+<pre data-sourcepos="6629:1-6632:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown">The number of windows in my house is</span>
<span id="LC2" class="line" lang="markdown"><span class="p">14.</span> The number of doors is 6.</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5504:1-5506:6" dir="auto">Oddly, though, <code>Markdown.pl</code> <em>does</em> allow a blockquote to
+<p data-sourcepos="6634:1-6636:6" dir="auto">Oddly, though, <code>Markdown.pl</code> <em>does</em> allow a blockquote to
interrupt a paragraph, even though the same considerations might
apply.</p>
-<p data-sourcepos="5508:1-5510:35" dir="auto">In CommonMark, we do allow lists to interrupt paragraphs, for
+<p data-sourcepos="6638:1-6640:35" dir="auto">In CommonMark, we do allow lists to interrupt paragraphs, for
two reasons. First, it is natural and not uncommon for people
to start lists without blank lines:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5512:1-5517:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown">I need to buy</span>
+<pre data-sourcepos="6642:1-6647:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown">I need to buy</span>
<span id="LC2" class="line" lang="markdown"><span class="p">-</span> new shoes</span>
<span id="LC3" class="line" lang="markdown"><span class="p">-</span> a coat</span>
<span id="LC4" class="line" lang="markdown"><span class="p">-</span> a plane ticket</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5519:1-5519:29" dir="auto">Second, we are attracted to a</p>
-<blockquote data-sourcepos="5521:1-5524:54" dir="auto">
-<p data-sourcepos="5521:3-5524:54"><a href="@">principle of uniformity</a>:
+<p data-sourcepos="6649:1-6649:29" dir="auto">Second, we are attracted to a</p>
+<blockquote data-sourcepos="6651:1-6654:54" dir="auto">
+<p data-sourcepos="6651:3-6654:54"><a href="@">principle of uniformity</a>:
if a chunk of text has a certain
meaning, it will continue to have the same meaning when put into a
container block (such as a list item or blockquote).</p>
</blockquote>
-<p data-sourcepos="5526:1-5527:47" dir="auto">(Indeed, the spec for [list items] and [block quotes] presupposes
+<p data-sourcepos="6656:1-6657:47" dir="auto">(Indeed, the spec for [list items] and [block quotes] presupposes
this principle.) This principle implies that if</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5529:1-5534:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="p"> *</span> I need to buy</span>
+<pre data-sourcepos="6659:1-6664:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="p"> *</span> I need to buy</span>
<span id="LC2" class="line" lang="markdown"><span class="p"> -</span> new shoes</span>
<span id="LC3" class="line" lang="markdown"><span class="p"> -</span> a coat</span>
<span id="LC4" class="line" lang="markdown"><span class="p"> -</span> a plane ticket</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5536:1-5539:4" dir="auto">is a list item containing a paragraph followed by a nested sublist,
+<p data-sourcepos="6666:1-6669:4" dir="auto">is a list item containing a paragraph followed by a nested sublist,
as all Markdown implementations agree it is (though the paragraph
may be rendered without <code>&lt;p&gt;</code> tags, since the list is "tight"),
then</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5541:1-5546:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown">I need to buy</span>
+<pre data-sourcepos="6671:1-6676:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown">I need to buy</span>
<span id="LC2" class="line" lang="markdown"><span class="p">-</span> new shoes</span>
<span id="LC3" class="line" lang="markdown"><span class="p">-</span> a coat</span>
<span id="LC4" class="line" lang="markdown"><span class="p">-</span> a plane ticket</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5548:1-5548:61" dir="auto">by itself should be a paragraph followed by a nested sublist.</p>
-<p data-sourcepos="5550:1-5555:30" dir="auto">Since it is well established Markdown practice to allow lists to
+<p data-sourcepos="6678:1-6678:61" dir="auto">by itself should be a paragraph followed by a nested sublist.</p>
+<p data-sourcepos="6680:1-6685:30" dir="auto">Since it is well established Markdown practice to allow lists to
interrupt paragraphs inside list items, the [principle of
uniformity] requires us to allow this outside list items as
well. (<a href="http://docutils.sourceforge.net/rst.html" rel="nofollow noreferrer noopener" target="_blank">reStructuredText</a>
takes a different approach, requiring blank lines before lists
even inside other list items.)</p>
-<p data-sourcepos="5557:1-5559:28" dir="auto">In order to solve of unwanted lists in paragraphs with
+<p data-sourcepos="6687:1-6689:28" dir="auto">In order to solve of unwanted lists in paragraphs with
hard-wrapped numerals, we allow only lists starting with <code>1</code> to
interrupt paragraphs. Thus,</p>
+<div>
+<div><a href="#example-284">Example 284</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5561:1-5564:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">The number of windows in my house is</span>
+<pre data-sourcepos="6694:1-6697:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">The number of windows in my house is</span>
<span id="LC2" class="line" lang="plaintext">14. The number of doors is 6.</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5566:1-5569:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;The number of windows in my house is</span>
+<pre data-sourcepos="6699:1-6702:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;The number of windows in my house is</span>
<span id="LC2" class="line" lang="plaintext">14. The number of doors is 6.&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5571:1-5571:51" dir="auto">We may still get an unintended result in cases like</p>
+</div>
+<p data-sourcepos="6705:1-6705:51" dir="auto">We may still get an unintended result in cases like</p>
+<div>
+<div><a href="#example-285">Example 285</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5573:1-5576:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">The number of windows in my house is</span>
+<pre data-sourcepos="6710:1-6713:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">The number of windows in my house is</span>
<span id="LC2" class="line" lang="plaintext">1. The number of doors is 6.</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5578:1-5583:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;The number of windows in my house is&lt;/p&gt;</span>
+<pre data-sourcepos="6715:1-6720:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;The number of windows in my house is&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;The number of doors is 6.&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5585:1-5585:57" dir="auto">but this rule should prevent most spurious list captures.</p>
-<p data-sourcepos="5587:1-5587:53" dir="auto">There can be any number of blank lines between items:</p>
+</div>
+<p data-sourcepos="6723:1-6723:57" dir="auto">but this rule should prevent most spurious list captures.</p>
+<p data-sourcepos="6725:1-6725:53" dir="auto">There can be any number of blank lines between items:</p>
+<div>
+<div><a href="#example-286">Example 286</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5589:1-5596:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
+<pre data-sourcepos="6730:1-6737:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">- bar</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -5045,7 +6266,7 @@ interrupt paragraphs. Thus,</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5598:1-5610:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="6739:1-6751:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/li&gt;</span>
@@ -5058,8 +6279,11 @@ interrupt paragraphs. Thus,</p>
<span id="LC11" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-287">Example 287</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5612:1-5619:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
+<pre data-sourcepos="6757:1-6764:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
<span id="LC2" class="line" lang="plaintext"> - bar</span>
<span id="LC3" class="line" lang="plaintext"> - baz</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -5068,7 +6292,7 @@ interrupt paragraphs. Thus,</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5621:1-5636:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="6766:1-6781:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo</span>
<span id="LC3" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;bar</span>
@@ -5084,12 +6308,15 @@ interrupt paragraphs. Thus,</p>
<span id="LC14" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5639:1-5642:8" dir="auto">To separate consecutive lists of the same type, or to separate a
+</div>
+<p data-sourcepos="6785:1-6788:8" dir="auto">To separate consecutive lists of the same type, or to separate a
list from an indented code block that would otherwise be parsed
as a subparagraph of the final list item, you can insert a blank HTML
comment:</p>
+<div>
+<div><a href="#example-288">Example 288</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5644:1-5652:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
+<pre data-sourcepos="6793:1-6801:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
<span id="LC2" class="line" lang="plaintext">- bar</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">&lt;!-- --&gt;</span>
@@ -5099,7 +6326,7 @@ comment:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5654:1-5664:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="6803:1-6813:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;foo&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;bar&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/ul&gt;</span>
@@ -5110,8 +6337,11 @@ comment:</p>
<span id="LC9" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-289">Example 289</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5667:1-5677:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
+<pre data-sourcepos="6820:1-6830:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> notcode</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -5123,7 +6353,7 @@ comment:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5679:1-5692:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="6832:1-6845:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;notcode&lt;/p&gt;</span>
@@ -5137,12 +6367,15 @@ comment:</p>
<span id="LC12" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5695:1-5698:5" dir="auto">List items need not be indented to the same level. The following
+</div>
+<p data-sourcepos="6849:1-6852:5" dir="auto">List items need not be indented to the same level. The following
list items will be treated as items at the same list level,
since none is indented enough to belong to the previous list
item:</p>
+<div>
+<div><a href="#example-290">Example 290</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5700:1-5708:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
+<pre data-sourcepos="6857:1-6865:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
<span id="LC2" class="line" lang="plaintext"> - b</span>
<span id="LC3" class="line" lang="plaintext"> - c</span>
<span id="LC4" class="line" lang="plaintext"> - d</span>
@@ -5152,7 +6385,7 @@ item:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5710:1-5720:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="6867:1-6877:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;a&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;b&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;c&lt;/li&gt;</span>
@@ -5163,8 +6396,11 @@ item:</p>
<span id="LC9" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-291">Example 291</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5723:1-5729:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. a</span>
+<pre data-sourcepos="6884:1-6890:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. a</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> 2. b</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -5172,7 +6408,7 @@ item:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5731:1-5743:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
+<pre data-sourcepos="6892:1-6904:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;a&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/li&gt;</span>
@@ -5185,11 +6421,14 @@ item:</p>
<span id="LC11" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5745:1-5747:52" dir="auto">Note, however, that list items may not be indented more than
+</div>
+<p data-sourcepos="6907:1-6909:52" dir="auto">Note, however, that list items may not be indented more than
three spaces. Here <code>- e</code> is treated as a paragraph continuation
line, because it is indented more than three spaces:</p>
+<div>
+<div><a href="#example-292">Example 292</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5749:1-5755:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
+<pre data-sourcepos="6914:1-6920:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
<span id="LC2" class="line" lang="plaintext"> - b</span>
<span id="LC3" class="line" lang="plaintext"> - c</span>
<span id="LC4" class="line" lang="plaintext"> - d</span>
@@ -5197,7 +6436,7 @@ line, because it is indented more than three spaces:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5757:1-5765:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="6922:1-6930:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;a&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;b&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;c&lt;/li&gt;</span>
@@ -5206,11 +6445,14 @@ line, because it is indented more than three spaces:</p>
<span id="LC7" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5767:1-5769:11" dir="auto">And here, <code>3. c</code> is treated as in indented code block,
+</div>
+<p data-sourcepos="6933:1-6935:11" dir="auto">And here, <code>3. c</code> is treated as in indented code block,
because it is indented four spaces and preceded by a
blank line.</p>
+<div>
+<div><a href="#example-293">Example 293</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5771:1-5777:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. a</span>
+<pre data-sourcepos="6940:1-6946:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. a</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> 2. b</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -5218,7 +6460,7 @@ blank line.</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5779:1-5790:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
+<pre data-sourcepos="6948:1-6959:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;a&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/li&gt;</span>
@@ -5230,17 +6472,20 @@ blank line.</p>
<span id="LC10" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5793:1-5794:22" dir="auto">This is a loose list, because there is a blank line between
+</div>
+<p data-sourcepos="6963:1-6964:22" dir="auto">This is a loose list, because there is a blank line between
two of the list items:</p>
+<div>
+<div><a href="#example-294">Example 294</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5796:1-5801:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
+<pre data-sourcepos="6969:1-6974:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
<span id="LC2" class="line" lang="plaintext">- b</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">- c</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5803:1-5815:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="6976:1-6988:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;a&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/li&gt;</span>
@@ -5253,16 +6498,19 @@ two of the list items:</p>
<span id="LC11" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5818:1-5818:37" dir="auto">So is this, with a empty second item:</p>
+</div>
+<p data-sourcepos="6992:1-6992:37" dir="auto">So is this, with a empty second item:</p>
+<div>
+<div><a href="#example-295">Example 295</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5820:1-5825:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* a</span>
+<pre data-sourcepos="6997:1-7002:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* a</span>
<span id="LC2" class="line" lang="plaintext">*</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">* c</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5827:1-5837:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="7004:1-7014:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;a&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/li&gt;</span>
@@ -5273,11 +6521,14 @@ two of the list items:</p>
<span id="LC9" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5840:1-5842:31" dir="auto">These are loose lists, even though there is no space between the items,
+</div>
+<p data-sourcepos="7018:1-7020:31" dir="auto">These are loose lists, even though there is no space between the items,
because one of the items directly contains two block-level elements
with a blank line between them:</p>
+<div>
+<div><a href="#example-296">Example 296</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5844:1-5850:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
+<pre data-sourcepos="7025:1-7031:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
<span id="LC2" class="line" lang="plaintext">- b</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext"> c</span>
@@ -5285,7 +6536,7 @@ with a blank line between them:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5852:1-5865:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="7033:1-7046:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;a&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/li&gt;</span>
@@ -5299,8 +6550,11 @@ with a blank line between them:</p>
<span id="LC12" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-297">Example 297</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5868:1-5874:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
+<pre data-sourcepos="7053:1-7059:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
<span id="LC2" class="line" lang="plaintext">- b</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext"> [ref]: /url</span>
@@ -5308,7 +6562,7 @@ with a blank line between them:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5876:1-5888:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="7061:1-7073:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;a&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/li&gt;</span>
@@ -5321,9 +6575,12 @@ with a blank line between them:</p>
<span id="LC11" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5891:1-5891:66" dir="auto">This is a tight list, because the blank lines are in a code block:</p>
+</div>
+<p data-sourcepos="7077:1-7077:66" dir="auto">This is a tight list, because the blank lines are in a code block:</p>
+<div>
+<div><a href="#example-298">Example 298</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5893:1-5901:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
+<pre data-sourcepos="7082:1-7090:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
<span id="LC2" class="line" lang="plaintext">- ```</span>
<span id="LC3" class="line" lang="plaintext"> b</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -5333,7 +6590,7 @@ with a blank line between them:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5903:1-5914:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="7092:1-7103:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;a&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;b</span>
@@ -5345,11 +6602,14 @@ with a blank line between them:</p>
<span id="LC10" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5917:1-5919:24" dir="auto">This is a tight list, because the blank line is between two
+</div>
+<p data-sourcepos="7107:1-7109:24" dir="auto">This is a tight list, because the blank line is between two
paragraphs of a sublist. So the sublist is loose while
the outer list is tight:</p>
+<div>
+<div><a href="#example-299">Example 299</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5921:1-5927:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
+<pre data-sourcepos="7114:1-7120:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
<span id="LC2" class="line" lang="plaintext"> - b</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext"> c</span>
@@ -5357,7 +6617,7 @@ the outer list is tight:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5929:1-5941:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="7122:1-7134:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;a</span>
<span id="LC3" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;</span>
@@ -5370,17 +6630,20 @@ the outer list is tight:</p>
<span id="LC11" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5944:1-5945:12" dir="auto">This is a tight list, because the blank line is inside the
+</div>
+<p data-sourcepos="7138:1-7139:12" dir="auto">This is a tight list, because the blank line is inside the
block quote:</p>
+<div>
+<div><a href="#example-300">Example 300</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5947:1-5952:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* a</span>
+<pre data-sourcepos="7144:1-7149:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* a</span>
<span id="LC2" class="line" lang="plaintext"> &gt; b</span>
<span id="LC3" class="line" lang="plaintext"> &gt;</span>
<span id="LC4" class="line" lang="plaintext">* c</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5954:1-5963:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="7151:1-7160:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;a</span>
<span id="LC3" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;b&lt;/p&gt;</span>
@@ -5390,10 +6653,13 @@ block quote:</p>
<span id="LC8" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5966:1-5967:33" dir="auto">This list is tight, because the consecutive block elements
+</div>
+<p data-sourcepos="7164:1-7165:33" dir="auto">This list is tight, because the consecutive block elements
are not separated by blank lines:</p>
+<div>
+<div><a href="#example-301">Example 301</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5969:1-5976:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
+<pre data-sourcepos="7170:1-7177:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
<span id="LC2" class="line" lang="plaintext"> &gt; b</span>
<span id="LC3" class="line" lang="plaintext"> ```</span>
<span id="LC4" class="line" lang="plaintext"> c</span>
@@ -5402,7 +6668,7 @@ are not separated by blank lines:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5978:1-5989:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="7179:1-7190:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;a</span>
<span id="LC3" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;b&lt;/p&gt;</span>
@@ -5414,24 +6680,30 @@ are not separated by blank lines:</p>
<span id="LC10" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="5992:1-5992:33" dir="auto">A single-paragraph list is tight:</p>
+</div>
+<p data-sourcepos="7194:1-7194:33" dir="auto">A single-paragraph list is tight:</p>
+<div>
+<div><a href="#example-302">Example 302</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5994:1-5996:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span></code></pre>
+<pre data-sourcepos="7199:1-7201:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="5998:1-6002:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="7203:1-7207:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;a&lt;/li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-303">Example 303</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6005:1-6008:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
+<pre data-sourcepos="7214:1-7217:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
<span id="LC2" class="line" lang="plaintext"> - b</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6010:1-6018:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="7219:1-7227:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;a</span>
<span id="LC3" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;b&lt;/li&gt;</span>
@@ -5440,10 +6712,13 @@ are not separated by blank lines:</p>
<span id="LC7" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6021:1-6022:36" dir="auto">This list is loose, because of the blank line between the
+</div>
+<p data-sourcepos="7231:1-7232:36" dir="auto">This list is loose, because of the blank line between the
two block elements in the list item:</p>
+<div>
+<div><a href="#example-304">Example 304</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6024:1-6030:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. ```</span>
+<pre data-sourcepos="7237:1-7243:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. ```</span>
<span id="LC2" class="line" lang="plaintext"> foo</span>
<span id="LC3" class="line" lang="plaintext"> ```</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -5451,7 +6726,7 @@ two block elements in the list item:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6032:1-6040:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
+<pre data-sourcepos="7245:1-7253:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;foo</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span>
@@ -5460,16 +6735,19 @@ two block elements in the list item:</p>
<span id="LC7" class="line" lang="plaintext">&lt;/ol&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6043:1-6043:51" dir="auto">Here the outer list is loose, the inner list tight:</p>
+</div>
+<p data-sourcepos="7257:1-7257:51" dir="auto">Here the outer list is loose, the inner list tight:</p>
+<div>
+<div><a href="#example-305">Example 305</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6045:1-6050:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* foo</span>
+<pre data-sourcepos="7262:1-7267:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* foo</span>
<span id="LC2" class="line" lang="plaintext"> * bar</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext"> baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6052:1-6062:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="7269:1-7279:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;ul&gt;</span>
@@ -5480,8 +6758,11 @@ two block elements in the list item:</p>
<span id="LC9" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-306">Example 306</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6065:1-6073:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
+<pre data-sourcepos="7286:1-7294:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- a</span>
<span id="LC2" class="line" lang="plaintext"> - b</span>
<span id="LC3" class="line" lang="plaintext"> - c</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -5491,7 +6772,7 @@ two block elements in the list item:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6075:1-6092:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="7296:1-7313:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;a&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;ul&gt;</span>
@@ -5509,46 +6790,58 @@ two block elements in the list item:</p>
<span id="LC16" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h1 data-sourcepos="6095:1-6095:9" dir="auto">
+</div>
+<h1 data-sourcepos="7317:1-7317:9" dir="auto">
<a id="user-content-inlines" class="anchor" href="#inlines" aria-hidden="true"></a>Inlines</h1>
-<p data-sourcepos="6097:1-6099:21" dir="auto">Inlines are parsed sequentially from the beginning of the character
+<p data-sourcepos="7319:1-7321:21" dir="auto">Inlines are parsed sequentially from the beginning of the character
stream to the end (left to right, in left-to-right languages).
Thus, for example, in</p>
+<div>
+<div><a href="#example-307">Example 307</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6101:1-6103:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`hi`lo`</span></code></pre>
+<pre data-sourcepos="7326:1-7328:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`hi`lo`</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6105:1-6107:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;hi&lt;/code&gt;lo`&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7330:1-7332:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;hi&lt;/code&gt;lo`&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6109:1-6110:9" dir="auto"><code>hi</code> is parsed as code, leaving the backtick at the end as a literal
+</div>
+<p data-sourcepos="7335:1-7336:9" dir="auto"><code>hi</code> is parsed as code, leaving the backtick at the end as a literal
backtick.</p>
-<h2 data-sourcepos="6113:1-6113:20" dir="auto">
+<h2 data-sourcepos="7339:1-7339:20" dir="auto">
<a id="user-content-backslash-escapes" class="anchor" href="#backslash-escapes" aria-hidden="true"></a>Backslash escapes</h2>
-<p data-sourcepos="6115:1-6115:57" dir="auto">Any ASCII punctuation character may be backslash-escaped:</p>
+<p data-sourcepos="7341:1-7341:57" dir="auto">Any ASCII punctuation character may be backslash-escaped:</p>
+<div>
+<div><a href="#example-308">Example 308</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6117:1-6119:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">\!\"\#\$\%\&amp;\'\(\)\*\+\,\-\.\/\:\;\&lt;\=\&gt;\?\@\[\\\]\^\_\`\{\|\}\~</span></code></pre>
+<pre data-sourcepos="7346:1-7348:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">\!\"\#\$\%\&amp;\'\(\)\*\+\,\-\.\/\:\;\&lt;\=\&gt;\?\@\[\\\]\^\_\`\{\|\}\~</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6121:1-6123:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;!&amp;quot;#$%&amp;amp;'()*+,-./:;&amp;lt;=&amp;gt;?@[\]^_`{|}~&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7350:1-7352:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;!&amp;quot;#$%&amp;amp;'()*+,-./:;&amp;lt;=&amp;gt;?@[\]^_`{|}~&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6126:1-6127:12" dir="auto">Backslashes before other characters are treated as literal
+</div>
+<p data-sourcepos="7356:1-7357:12" dir="auto">Backslashes before other characters are treated as literal
backslashes:</p>
+<div>
+<div><a href="#example-309">Example 309</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6129:1-6131:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">\→\A\a\ \3\φ\«</span></code></pre>
+<pre data-sourcepos="7362:1-7364:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">\→\A\a\ \3\φ\«</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6133:1-6135:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;\→\A\a\ \3\φ\«&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7366:1-7368:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;\→\A\a\ \3\φ\«&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6138:1-6139:39" dir="auto">Escaped characters are treated as regular characters and do
+</div>
+<p data-sourcepos="7372:1-7373:39" dir="auto">Escaped characters are treated as regular characters and do
not have their usual Markdown meanings:</p>
+<div>
+<div><a href="#example-310">Example 310</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6141:1-6151:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">\*not emphasized*</span>
+<pre data-sourcepos="7378:1-7388:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">\*not emphasized*</span>
<span id="LC2" class="line" lang="plaintext">\&lt;br/&gt; not a tag</span>
<span id="LC3" class="line" lang="plaintext">\[not a link](/foo)</span>
<span id="LC4" class="line" lang="plaintext">\`not code`</span>
@@ -5560,7 +6853,7 @@ not have their usual Markdown meanings:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6153:1-6163:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*not emphasized*</span>
+<pre data-sourcepos="7390:1-7400:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*not emphasized*</span>
<span id="LC2" class="line" lang="plaintext">&amp;lt;br/&amp;gt; not a tag</span>
<span id="LC3" class="line" lang="plaintext">[not a link](/foo)</span>
<span id="LC4" class="line" lang="plaintext">`not code`</span>
@@ -5571,115 +6864,146 @@ not have their usual Markdown meanings:</p>
<span id="LC9" class="line" lang="plaintext">&amp;amp;ouml; not a character entity&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6166:1-6166:65" dir="auto">If a backslash is itself escaped, the following character is not:</p>
+</div>
+<p data-sourcepos="7404:1-7404:65" dir="auto">If a backslash is itself escaped, the following character is not:</p>
+<div>
+<div><a href="#example-311">Example 311</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6168:1-6170:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">\\*emphasis*</span></code></pre>
+<pre data-sourcepos="7409:1-7411:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">\\*emphasis*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6172:1-6174:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;\&lt;em&gt;emphasis&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7413:1-7415:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;\&lt;em&gt;emphasis&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6177:1-6177:58" dir="auto">A backslash at the end of the line is a [hard line break]:</p>
+</div>
+<p data-sourcepos="7419:1-7419:58" dir="auto">A backslash at the end of the line is a [hard line break]:</p>
+<div>
+<div><a href="#example-312">Example 312</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6179:1-6182:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo\</span>
+<pre data-sourcepos="7424:1-7427:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo\</span>
<span id="LC2" class="line" lang="plaintext">bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6184:1-6187:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;br /&gt;</span>
+<pre data-sourcepos="7429:1-7432:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;br /&gt;</span>
<span id="LC2" class="line" lang="plaintext">bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6190:1-6191:9" dir="auto">Backslash escapes do not work in code blocks, code spans, autolinks, or
+</div>
+<p data-sourcepos="7436:1-7437:9" dir="auto">Backslash escapes do not work in code blocks, code spans, autolinks, or
raw HTML:</p>
+<div>
+<div><a href="#example-313">Example 313</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6193:1-6195:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`` \[\` ``</span></code></pre>
+<pre data-sourcepos="7442:1-7444:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`` \[\` ``</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6197:1-6199:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;\[\`&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7446:1-7448:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;\[\`&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-314">Example 314</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6202:1-6204:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> \[\]</span></code></pre>
+<pre data-sourcepos="7455:1-7457:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> \[\]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6206:1-6209:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;\[\]</span>
+<pre data-sourcepos="7459:1-7462:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;\[\]</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-315">Example 315</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6212:1-6216:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~~</span>
+<pre data-sourcepos="7469:1-7473:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~~</span>
<span id="LC2" class="line" lang="plaintext">\[\]</span>
<span id="LC3" class="line" lang="plaintext">~~~</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6218:1-6221:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;\[\]</span>
+<pre data-sourcepos="7475:1-7478:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;\[\]</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-316">Example 316</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6224:1-6226:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;http://example.com?find=\*&gt;</span></code></pre>
+<pre data-sourcepos="7485:1-7487:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;http://example.com?find=\*&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6228:1-6230:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://example.com?find=%5C*"&gt;http://example.com?find=\*&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7489:1-7491:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://example.com?find=%5C*"&gt;http://example.com?find=\*&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-317">Example 317</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6233:1-6235:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="/bar\/)"&gt;</span></code></pre>
+<pre data-sourcepos="7498:1-7500:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="/bar\/)"&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6237:1-6239:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="/bar\/)"&gt;</span></code></pre>
+<pre data-sourcepos="7502:1-7504:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="/bar\/)"&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6242:1-6243:60" dir="auto">But they work in all other contexts, including URLs and link titles,
+</div>
+<p data-sourcepos="7508:1-7509:60" dir="auto">But they work in all other contexts, including URLs and link titles,
link references, and [info strings] in [fenced code blocks]:</p>
+<div>
+<div><a href="#example-318">Example 318</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6245:1-6247:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo](/bar\* "ti\*tle")</span></code></pre>
+<pre data-sourcepos="7514:1-7516:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo](/bar\* "ti\*tle")</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6249:1-6251:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/bar*" title="ti*tle"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7518:1-7520:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/bar*" title="ti*tle"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-319">Example 319</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6254:1-6258:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]</span>
+<pre data-sourcepos="7527:1-7531:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /bar\* "ti\*tle"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6260:1-6262:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/bar*" title="ti*tle"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7533:1-7535:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/bar*" title="ti*tle"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-320">Example 320</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6265:1-6269:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">``` foo\+bar</span>
+<pre data-sourcepos="7542:1-7546:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">``` foo\+bar</span>
<span id="LC2" class="line" lang="plaintext">foo</span>
<span id="LC3" class="line" lang="plaintext">```</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6271:1-6274:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code class="language-foo+bar"&gt;foo</span>
+<pre data-sourcepos="7548:1-7551:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code class="language-foo+bar"&gt;foo</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="6278:1-6278:42" dir="auto">
+</div>
+<h2 data-sourcepos="7556:1-7556:42" dir="auto">
<a id="user-content-entity-and-numeric-character-references" class="anchor" href="#entity-and-numeric-character-references" aria-hidden="true"></a>Entity and numeric character references</h2>
-<p data-sourcepos="6280:1-6282:30" dir="auto">Valid HTML entity references and numeric character references
+<p data-sourcepos="7558:1-7560:30" dir="auto">Valid HTML entity references and numeric character references
can be used in place of the corresponding Unicode character,
with the following exceptions:</p>
-<ul data-sourcepos="6284:1-6293:0" dir="auto">
-<li data-sourcepos="6284:1-6286:0">
-<p data-sourcepos="6284:3-6285:24">Entity and character references are not recognized in code
+<ul data-sourcepos="7562:1-7571:0" dir="auto">
+<li data-sourcepos="7562:1-7564:0">
+<p data-sourcepos="7562:3-7563:24">Entity and character references are not recognized in code
blocks and code spans.</p>
</li>
-<li data-sourcepos="6287:1-6293:0">
-<p data-sourcepos="6287:3-6292:9">Entity and character references cannot stand in place of
+<li data-sourcepos="7565:1-7571:0">
+<p data-sourcepos="7565:3-7570:9">Entity and character references cannot stand in place of
special characters that define structural elements in
CommonMark. For example, although <code>&amp;#42;</code> can be used
in place of a literal <code>*</code> character, <code>&amp;#42;</code> cannot replace
@@ -5687,292 +7011,366 @@ in place of a literal <code>*</code> character, <code>&amp;#42;</code> cannot re
breaks.</p>
</li>
</ul>
-<p data-sourcepos="6294:1-6296:49" dir="auto">Conforming CommonMark parsers need not store information about
+<p data-sourcepos="7572:1-7574:49" dir="auto">Conforming CommonMark parsers need not store information about
whether a particular character was represented in the source
using a Unicode character or an entity reference.</p>
-<p data-sourcepos="6298:1-6302:47" dir="auto"><a href="@">Entity references</a> consist of <code>&amp;</code> + any of the valid
+<p data-sourcepos="7576:1-7580:47" dir="auto"><a href="@">Entity references</a> consist of <code>&amp;</code> + any of the valid
HTML5 entity names + <code>;</code>. The
document <a href="https://html.spec.whatwg.org/multipage/entities.json" rel="nofollow noreferrer noopener" target="_blank">https://html.spec.whatwg.org/multipage/entities.json</a>
is used as an authoritative source for the valid entity
references and their corresponding code points.</p>
+<div>
+<div><a href="#example-321">Example 321</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6304:1-6308:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;nbsp; &amp;amp; &amp;copy; &amp;AElig; &amp;Dcaron;</span>
+<pre data-sourcepos="7585:1-7589:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;nbsp; &amp;amp; &amp;copy; &amp;AElig; &amp;Dcaron;</span>
<span id="LC2" class="line" lang="plaintext">&amp;frac34; &amp;HilbertSpace; &amp;DifferentialD;</span>
<span id="LC3" class="line" lang="plaintext">&amp;ClockwiseContourIntegral; &amp;ngE;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6310:1-6314:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;  &amp;amp; © Æ Ď</span>
+<pre data-sourcepos="7591:1-7595:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;  &amp;amp; © Æ Ď</span>
<span id="LC2" class="line" lang="plaintext">¾ ℋ ⅆ</span>
<span id="LC3" class="line" lang="plaintext">∲ ≧̸&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6317:1-6323:58" dir="auto"><a href="@">Decimal numeric character
+</div>
+<p data-sourcepos="7599:1-7605:58" dir="auto"><a href="@">Decimal numeric character
references</a>
consist of <code>&amp;#</code> + a string of 1--7 arabic digits + <code>;</code>. A
numeric character reference is parsed as the corresponding
Unicode character. Invalid Unicode code points will be replaced by
the REPLACEMENT CHARACTER (<code>U+FFFD</code>). For security reasons,
the code point <code>U+0000</code> will also be replaced by <code>U+FFFD</code>.</p>
+<div>
+<div><a href="#example-322">Example 322</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6325:1-6327:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;#35; &amp;#1234; &amp;#992; &amp;#0;</span></code></pre>
+<pre data-sourcepos="7610:1-7612:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;#35; &amp;#1234; &amp;#992; &amp;#0;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6329:1-6331:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;# Ӓ Ϡ �&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7614:1-7616:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;# Ӓ Ϡ �&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6334:1-6338:62" dir="auto"><a href="@">Hexadecimal numeric character
+</div>
+<p data-sourcepos="7620:1-7624:62" dir="auto"><a href="@">Hexadecimal numeric character
references</a> consist of <code>&amp;#</code> +
either <code>X</code> or <code>x</code> + a string of 1-6 hexadecimal digits + <code>;</code>.
They too are parsed as the corresponding Unicode character (this
time specified with a hexadecimal numeral instead of decimal).</p>
+<div>
+<div><a href="#example-323">Example 323</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6340:1-6342:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;#X22; &amp;#XD06; &amp;#xcab;</span></code></pre>
+<pre data-sourcepos="7629:1-7631:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;#X22; &amp;#XD06; &amp;#xcab;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6344:1-6346:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;quot; ആ ಫ&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7633:1-7635:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;quot; ആ ಫ&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6349:1-6349:26" dir="auto">Here are some nonentities:</p>
+</div>
+<p data-sourcepos="7639:1-7639:26" dir="auto">Here are some nonentities:</p>
+<div>
+<div><a href="#example-324">Example 324</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6351:1-6356:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;nbsp &amp;x; &amp;#; &amp;#x;</span>
+<pre data-sourcepos="7644:1-7649:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;nbsp &amp;x; &amp;#; &amp;#x;</span>
<span id="LC2" class="line" lang="plaintext">&amp;#987654321;</span>
<span id="LC3" class="line" lang="plaintext">&amp;#abcdef0;</span>
<span id="LC4" class="line" lang="plaintext">&amp;ThisIsNotDefined; &amp;hi?;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6358:1-6363:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;amp;nbsp &amp;amp;x; &amp;amp;#; &amp;amp;#x;</span>
+<pre data-sourcepos="7651:1-7656:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;amp;nbsp &amp;amp;x; &amp;amp;#; &amp;amp;#x;</span>
<span id="LC2" class="line" lang="plaintext">&amp;amp;#987654321;</span>
<span id="LC3" class="line" lang="plaintext">&amp;amp;#abcdef0;</span>
<span id="LC4" class="line" lang="plaintext">&amp;amp;ThisIsNotDefined; &amp;amp;hi?;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6366:1-6368:60" dir="auto">Although HTML5 does accept some entity references
+</div>
+<p data-sourcepos="7660:1-7662:60" dir="auto">Although HTML5 does accept some entity references
without a trailing semicolon (such as <code>&amp;copy</code>), these are not
recognized here, because it makes the grammar too ambiguous:</p>
+<div>
+<div><a href="#example-325">Example 325</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6370:1-6372:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;copy</span></code></pre>
+<pre data-sourcepos="7667:1-7669:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;copy</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6374:1-6376:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;amp;copy&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7671:1-7673:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;amp;copy&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6379:1-6380:39" dir="auto">Strings that are not on the list of HTML5 named entities are not
+</div>
+<p data-sourcepos="7677:1-7678:39" dir="auto">Strings that are not on the list of HTML5 named entities are not
recognized as entity references either:</p>
+<div>
+<div><a href="#example-326">Example 326</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6382:1-6384:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;MadeUpEntity;</span></code></pre>
+<pre data-sourcepos="7683:1-7685:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;MadeUpEntity;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6386:1-6388:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;amp;MadeUpEntity;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7687:1-7689:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;amp;MadeUpEntity;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6391:1-6393:62" dir="auto">Entity and numeric character references are recognized in any
+</div>
+<p data-sourcepos="7693:1-7695:62" dir="auto">Entity and numeric character references are recognized in any
context besides code spans or code blocks, including
URLs, [link titles], and [fenced code block][] [info strings]:</p>
+<div>
+<div><a href="#example-327">Example 327</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6395:1-6397:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="&amp;ouml;&amp;ouml;.html"&gt;</span></code></pre>
+<pre data-sourcepos="7700:1-7702:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="&amp;ouml;&amp;ouml;.html"&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6399:1-6401:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="&amp;ouml;&amp;ouml;.html"&gt;</span></code></pre>
+<pre data-sourcepos="7704:1-7706:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="&amp;ouml;&amp;ouml;.html"&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-328">Example 328</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6404:1-6406:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo](/f&amp;ouml;&amp;ouml; "f&amp;ouml;&amp;ouml;")</span></code></pre>
+<pre data-sourcepos="7713:1-7715:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo](/f&amp;ouml;&amp;ouml; "f&amp;ouml;&amp;ouml;")</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6408:1-6410:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/f%C3%B6%C3%B6" title="föö"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7717:1-7719:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/f%C3%B6%C3%B6" title="föö"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-329">Example 329</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6413:1-6417:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]</span>
+<pre data-sourcepos="7726:1-7730:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /f&amp;ouml;&amp;ouml; "f&amp;ouml;&amp;ouml;"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6419:1-6421:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/f%C3%B6%C3%B6" title="föö"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7732:1-7734:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/f%C3%B6%C3%B6" title="föö"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-330">Example 330</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6424:1-6428:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">``` f&amp;ouml;&amp;ouml;</span>
+<pre data-sourcepos="7741:1-7745:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">``` f&amp;ouml;&amp;ouml;</span>
<span id="LC2" class="line" lang="plaintext">foo</span>
<span id="LC3" class="line" lang="plaintext">```</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6430:1-6433:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code class="language-föö"&gt;foo</span>
+<pre data-sourcepos="7747:1-7750:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code class="language-föö"&gt;foo</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6436:1-6437:35" dir="auto">Entity and numeric character references are treated as literal
+</div>
+<p data-sourcepos="7754:1-7755:35" dir="auto">Entity and numeric character references are treated as literal
text in code spans and code blocks:</p>
+<div>
+<div><a href="#example-331">Example 331</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6439:1-6441:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`f&amp;ouml;&amp;ouml;`</span></code></pre>
+<pre data-sourcepos="7760:1-7762:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`f&amp;ouml;&amp;ouml;`</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6443:1-6445:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;f&amp;amp;ouml;&amp;amp;ouml;&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7764:1-7766:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;f&amp;amp;ouml;&amp;amp;ouml;&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-332">Example 332</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6448:1-6450:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> f&amp;ouml;f&amp;ouml;</span></code></pre>
+<pre data-sourcepos="7773:1-7775:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> f&amp;ouml;f&amp;ouml;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6452:1-6455:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;f&amp;amp;ouml;f&amp;amp;ouml;</span>
+<pre data-sourcepos="7777:1-7780:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;&lt;code&gt;f&amp;amp;ouml;f&amp;amp;ouml;</span>
<span id="LC2" class="line" lang="plaintext">&lt;/code&gt;&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6458:1-6460:10" dir="auto">Entity and numeric character references cannot be used
+</div>
+<p data-sourcepos="7784:1-7786:10" dir="auto">Entity and numeric character references cannot be used
in place of symbols indicating structure in CommonMark
documents.</p>
+<div>
+<div><a href="#example-333">Example 333</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6462:1-6465:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;#42;foo&amp;#42;</span>
+<pre data-sourcepos="7791:1-7794:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;#42;foo&amp;#42;</span>
<span id="LC2" class="line" lang="plaintext">*foo*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6467:1-6470:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*foo*</span>
+<pre data-sourcepos="7796:1-7799:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*foo*</span>
<span id="LC2" class="line" lang="plaintext">&lt;em&gt;foo&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-334">Example 334</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6472:1-6476:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;#42; foo</span>
+<pre data-sourcepos="7805:1-7809:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;#42; foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">* foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6478:1-6483:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;* foo&lt;/p&gt;</span>
+<pre data-sourcepos="7811:1-7816:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;* foo&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;li&gt;foo&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-335">Example 335</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6485:1-6487:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo&amp;#10;&amp;#10;bar</span></code></pre>
+<pre data-sourcepos="7822:1-7824:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo&amp;#10;&amp;#10;bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6489:1-6493:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo</span>
+<pre data-sourcepos="7826:1-7830:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-336">Example 336</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6495:1-6497:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;#9;foo</span></code></pre>
+<pre data-sourcepos="7836:1-7838:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&amp;#9;foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6499:1-6501:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;→foo&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7840:1-7842:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;→foo&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-337">Example 337</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6504:1-6506:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[a](url &amp;quot;tit&amp;quot;)</span></code></pre>
+<pre data-sourcepos="7849:1-7851:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[a](url &amp;quot;tit&amp;quot;)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6508:1-6510:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[a](url &amp;quot;tit&amp;quot;)&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7853:1-7855:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[a](url &amp;quot;tit&amp;quot;)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="6513:1-6513:13" dir="auto">
+</div>
+<h2 data-sourcepos="7859:1-7859:13" dir="auto">
<a id="user-content-code-spans" class="anchor" href="#code-spans" aria-hidden="true"></a>Code spans</h2>
-<p data-sourcepos="6515:1-6517:36" dir="auto">A <a href="@">backtick string</a>
+<p data-sourcepos="7861:1-7863:36" dir="auto">A <a href="@">backtick string</a>
is a string of one or more backtick characters (<code>`</code>) that is neither
preceded nor followed by a backtick.</p>
-<p data-sourcepos="6519:1-6522:15" dir="auto">A <a href="@">code span</a> begins with a backtick string and ends with
+<p data-sourcepos="7865:1-7868:15" dir="auto">A <a href="@">code span</a> begins with a backtick string and ends with
a backtick string of equal length. The contents of the code span are
the characters between the two backtick strings, normalized in the
following ways:</p>
-<ul data-sourcepos="6524:1-6531:0" dir="auto">
-<li data-sourcepos="6524:1-6524:50">First, [line endings] are converted to [spaces].</li>
-<li data-sourcepos="6525:1-6531:0">If the resulting string both begins <em>and</em> ends with a [space]
+<ul data-sourcepos="7870:1-7877:0" dir="auto">
+<li data-sourcepos="7870:1-7870:50">First, [line endings] are converted to [spaces].</li>
+<li data-sourcepos="7871:1-7877:0">If the resulting string both begins <em>and</em> ends with a [space]
character, but does not consist entirely of [space]
characters, a single [space] character is removed from the
front and back. This allows you to include code that begins
or ends with backtick characters, which must be separated by
whitespace from the opening or closing backtick strings.</li>
</ul>
-<p data-sourcepos="6532:1-6532:27" dir="auto">This is a simple code span:</p>
+<p data-sourcepos="7878:1-7878:27" dir="auto">This is a simple code span:</p>
+<div>
+<div><a href="#example-338">Example 338</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6534:1-6536:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`foo`</span></code></pre>
+<pre data-sourcepos="7883:1-7885:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`foo`</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6538:1-6540:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7887:1-7889:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6543:1-6545:15" dir="auto">Here two backticks are used, because the code contains a backtick.
+</div>
+<p data-sourcepos="7893:1-7895:15" dir="auto">Here two backticks are used, because the code contains a backtick.
This example also illustrates stripping of a single leading and
trailing space:</p>
+<div>
+<div><a href="#example-339">Example 339</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6547:1-6549:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`` foo ` bar ``</span></code></pre>
+<pre data-sourcepos="7900:1-7902:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`` foo ` bar ``</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6551:1-6553:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo ` bar&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7904:1-7906:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo ` bar&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6556:1-6557:7" dir="auto">This example shows the motivation for stripping leading and trailing
+</div>
+<p data-sourcepos="7910:1-7911:7" dir="auto">This example shows the motivation for stripping leading and trailing
spaces:</p>
+<div>
+<div><a href="#example-340">Example 340</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6559:1-6561:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">` `` `</span></code></pre>
+<pre data-sourcepos="7916:1-7918:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">` `` `</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6563:1-6565:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;``&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7920:1-7922:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;``&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6567:1-6567:39" dir="auto">Note that only <em>one</em> space is stripped:</p>
+</div>
+<p data-sourcepos="7925:1-7925:39" dir="auto">Note that only <em>one</em> space is stripped:</p>
+<div>
+<div><a href="#example-341">Example 341</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6569:1-6571:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">` `` `</span></code></pre>
+<pre data-sourcepos="7930:1-7932:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">` `` `</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6573:1-6575:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt; `` &lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7934:1-7936:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt; `` &lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6577:1-6578:20" dir="auto">The stripping only happens if the space is on both
+</div>
+<p data-sourcepos="7939:1-7940:20" dir="auto">The stripping only happens if the space is on both
sides of the string:</p>
+<div>
+<div><a href="#example-342">Example 342</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6580:1-6582:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">` a`</span></code></pre>
+<pre data-sourcepos="7945:1-7947:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">` a`</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6584:1-6586:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt; a&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7949:1-7951:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt; a&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6588:1-6589:21" dir="auto">Only [spaces], and not [unicode whitespace] in general, are
+</div>
+<p data-sourcepos="7954:1-7955:21" dir="auto">Only [spaces], and not [unicode whitespace] in general, are
stripped in this way:</p>
+<div>
+<div><a href="#example-343">Example 343</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6591:1-6593:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">` b `</span></code></pre>
+<pre data-sourcepos="7960:1-7962:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">` b `</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6595:1-6597:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt; b &lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7964:1-7966:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt; b &lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6599:1-6599:58" dir="auto">No stripping occurs if the code span contains only spaces:</p>
+</div>
+<p data-sourcepos="7969:1-7969:58" dir="auto">No stripping occurs if the code span contains only spaces:</p>
+<div>
+<div><a href="#example-344">Example 344</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6601:1-6604:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">` `</span>
+<pre data-sourcepos="7974:1-7977:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">` `</span>
<span id="LC2" class="line" lang="plaintext">` `</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6606:1-6609:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt; &lt;/code&gt;</span>
+<pre data-sourcepos="7979:1-7982:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt; &lt;/code&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;code&gt; &lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6612:1-6612:39" dir="auto">[Line endings] are treated like spaces:</p>
+</div>
+<p data-sourcepos="7986:1-7986:39" dir="auto">[Line endings] are treated like spaces:</p>
+<div>
+<div><a href="#example-345">Example 345</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6614:1-6620:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">``</span>
+<pre data-sourcepos="7991:1-7997:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">``</span>
<span id="LC2" class="line" lang="plaintext">foo</span>
<span id="LC3" class="line" lang="plaintext">bar </span>
<span id="LC4" class="line" lang="plaintext">baz</span>
@@ -5980,306 +7378,349 @@ stripped in this way:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6622:1-6624:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo bar baz&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="7999:1-8001:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo bar baz&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-346">Example 346</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6626:1-6630:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">``</span>
+<pre data-sourcepos="8007:1-8011:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">``</span>
<span id="LC2" class="line" lang="plaintext">foo </span>
<span id="LC3" class="line" lang="plaintext">``</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6632:1-6634:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo &lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8013:1-8015:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo &lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6637:1-6637:34" dir="auto">Interior spaces are not collapsed:</p>
+</div>
+<p data-sourcepos="8019:1-8019:34" dir="auto">Interior spaces are not collapsed:</p>
+<div>
+<div><a href="#example-347">Example 347</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6639:1-6642:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`foo bar </span>
+<pre data-sourcepos="8024:1-8027:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`foo bar </span>
<span id="LC2" class="line" lang="plaintext">baz`</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6644:1-6646:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo bar baz&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8029:1-8031:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo bar baz&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6648:1-6650:26" dir="auto">Note that browsers will typically collapse consecutive spaces
+</div>
+<p data-sourcepos="8034:1-8036:26" dir="auto">Note that browsers will typically collapse consecutive spaces
when rendering <code>&lt;code&gt;</code> elements, so it is recommended that
the following CSS be used:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6652:5-6654:0" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">code{white-space: pre-wrap;}</span></code></pre>
+<pre data-sourcepos="8038:5-8040:0" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">code{white-space: pre-wrap;}</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6655:1-6656:22" dir="auto">Note that backslash escapes do not work in code spans. All backslashes
+<p data-sourcepos="8041:1-8042:22" dir="auto">Note that backslash escapes do not work in code spans. All backslashes
are treated literally:</p>
+<div>
+<div><a href="#example-348">Example 348</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6658:1-6660:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`foo\`bar`</span></code></pre>
+<pre data-sourcepos="8047:1-8049:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`foo\`bar`</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6662:1-6664:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo\&lt;/code&gt;bar`&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8051:1-8053:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo\&lt;/code&gt;bar`&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6667:1-6669:59" dir="auto">Backslash escapes are never needed, because one can always choose a
+</div>
+<p data-sourcepos="8057:1-8059:59" dir="auto">Backslash escapes are never needed, because one can always choose a
string of <em>n</em> backtick characters as delimiters, where the code does
not contain any strings of exactly <em>n</em> backtick characters.</p>
+<div>
+<div><a href="#example-349">Example 349</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6671:1-6673:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">``foo`bar``</span></code></pre>
+<pre data-sourcepos="8064:1-8066:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">``foo`bar``</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6675:1-6677:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo`bar&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8068:1-8070:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo`bar&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-350">Example 350</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6679:1-6681:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">` foo `` bar `</span></code></pre>
+<pre data-sourcepos="8076:1-8078:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">` foo `` bar `</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6683:1-6685:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo `` bar&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8080:1-8082:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;foo `` bar&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6688:1-6691:5" dir="auto">Code span backticks have higher precedence than any other inline
+</div>
+<p data-sourcepos="8086:1-8089:5" dir="auto">Code span backticks have higher precedence than any other inline
constructs except HTML tags and autolinks. Thus, for example, this is
not parsed as emphasized text, since the second <code>*</code> is part of a code
span:</p>
+<div>
+<div><a href="#example-351">Example 351</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6693:1-6695:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo`*`</span></code></pre>
+<pre data-sourcepos="8094:1-8096:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo`*`</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6697:1-6699:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*foo&lt;code&gt;*&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8098:1-8100:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*foo&lt;code&gt;*&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6702:1-6702:33" dir="auto">And this is not parsed as a link:</p>
+</div>
+<p data-sourcepos="8104:1-8104:33" dir="auto">And this is not parsed as a link:</p>
+<div>
+<div><a href="#example-352">Example 352</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6704:1-6706:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[not a `link](/foo`)</span></code></pre>
+<pre data-sourcepos="8109:1-8111:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[not a `link](/foo`)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6708:1-6710:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[not a &lt;code&gt;link](/foo&lt;/code&gt;)&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8113:1-8115:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[not a &lt;code&gt;link](/foo&lt;/code&gt;)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6713:1-6714:19" dir="auto">Code spans, HTML tags, and autolinks have the same precedence.
+</div>
+<p data-sourcepos="8119:1-8120:19" dir="auto">Code spans, HTML tags, and autolinks have the same precedence.
Thus, this is code:</p>
+<div>
+<div><a href="#example-353">Example 353</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6716:1-6718:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`&lt;a href="`"&gt;`</span></code></pre>
+<pre data-sourcepos="8125:1-8127:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`&lt;a href="`"&gt;`</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6720:1-6722:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;&amp;lt;a href=&amp;quot;&lt;/code&gt;&amp;quot;&amp;gt;`&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8129:1-8131:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;&amp;lt;a href=&amp;quot;&lt;/code&gt;&amp;quot;&amp;gt;`&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6725:1-6725:24" dir="auto">But this is an HTML tag:</p>
+</div>
+<p data-sourcepos="8135:1-8135:24" dir="auto">But this is an HTML tag:</p>
+<div>
+<div><a href="#example-354">Example 354</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6727:1-6729:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="`"&gt;`</span></code></pre>
+<pre data-sourcepos="8140:1-8142:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="`"&gt;`</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6731:1-6733:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="`"&gt;`&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8144:1-8146:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="`"&gt;`&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6736:1-6736:17" dir="auto">And this is code:</p>
+</div>
+<p data-sourcepos="8150:1-8150:17" dir="auto">And this is code:</p>
+<div>
+<div><a href="#example-355">Example 355</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6738:1-6740:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`&lt;http://foo.bar.`baz&gt;`</span></code></pre>
+<pre data-sourcepos="8155:1-8157:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`&lt;http://foo.bar.`baz&gt;`</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6742:1-6744:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;&amp;lt;http://foo.bar.&lt;/code&gt;baz&amp;gt;`&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8159:1-8161:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;&amp;lt;http://foo.bar.&lt;/code&gt;baz&amp;gt;`&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6747:1-6747:24" dir="auto">But this is an autolink:</p>
+</div>
+<p data-sourcepos="8165:1-8165:24" dir="auto">But this is an autolink:</p>
+<div>
+<div><a href="#example-356">Example 356</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6749:1-6751:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;http://foo.bar.`baz&gt;`</span></code></pre>
+<pre data-sourcepos="8170:1-8172:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;http://foo.bar.`baz&gt;`</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6753:1-6755:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://foo.bar.%60baz"&gt;http://foo.bar.`baz&lt;/a&gt;`&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8174:1-8176:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://foo.bar.%60baz"&gt;http://foo.bar.`baz&lt;/a&gt;`&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6758:1-6759:31" dir="auto">When a backtick string is not closed by a matching backtick string,
+</div>
+<p data-sourcepos="8180:1-8181:31" dir="auto">When a backtick string is not closed by a matching backtick string,
we just have literal backticks:</p>
+<div>
+<div><a href="#example-357">Example 357</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6761:1-6763:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```foo``</span></code></pre>
+<pre data-sourcepos="8186:1-8188:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```foo``</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6765:1-6767:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;```foo``&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8190:1-8192:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;```foo``&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-358">Example 358</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6770:1-6772:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`foo</span></code></pre>
+<pre data-sourcepos="8199:1-8201:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`foo</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6774:1-6776:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;`foo&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8203:1-8205:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;`foo&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6778:1-6779:47" dir="auto">The following case also illustrates the need for opening and
+</div>
+<p data-sourcepos="8208:1-8209:47" dir="auto">The following case also illustrates the need for opening and
closing backtick strings to be equal in length:</p>
+<div>
+<div><a href="#example-359">Example 359</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6781:1-6783:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`foo``bar``</span></code></pre>
+<pre data-sourcepos="8214:1-8216:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`foo``bar``</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6785:1-6787:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;`foo&lt;code&gt;bar&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8218:1-8220:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;`foo&lt;code&gt;bar&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="6790:1-6790:31" dir="auto">
+</div>
+<h2 data-sourcepos="8224:1-8224:31" dir="auto">
<a id="user-content-emphasis-and-strong-emphasis" class="anchor" href="#emphasis-and-strong-emphasis" aria-hidden="true"></a>Emphasis and strong emphasis</h2>
-<p data-sourcepos="6792:1-6793:73" dir="auto">John Gruber's original <a href="http://daringfireball.net/projects/markdown/syntax#em" rel="nofollow noreferrer noopener" target="_blank">Markdown syntax
+<p data-sourcepos="8226:1-8227:73" dir="auto">John Gruber's original <a href="http://daringfireball.net/projects/markdown/syntax#em" rel="nofollow noreferrer noopener" target="_blank">Markdown syntax
description</a> says:</p>
-<blockquote data-sourcepos="6795:1-6798:6" dir="auto">
-<p data-sourcepos="6795:3-6798:6">Markdown treats asterisks (<code>*</code>) and underscores (<code>_</code>) as indicators of
+<blockquote data-sourcepos="8229:1-8232:6" dir="auto">
+<p data-sourcepos="8229:3-8232:6">Markdown treats asterisks (<code>*</code>) and underscores (<code>_</code>) as indicators of
emphasis. Text wrapped with one <code>*</code> or <code>_</code> will be wrapped with an HTML
<code>&lt;em&gt;</code> tag; double <code>*</code>'s or <code>_</code>'s will be wrapped with an HTML <code>&lt;strong&gt;</code>
tag.</p>
</blockquote>
-<p data-sourcepos="6800:1-6804:57" dir="auto">This is enough for most users, but these rules leave much undecided,
+<p data-sourcepos="8234:1-8238:57" dir="auto">This is enough for most users, but these rules leave much undecided,
especially when it comes to nested emphasis. The original
<code>Markdown.pl</code> test suite makes it clear that triple <code>***</code> and
<code>___</code> delimiters can be used for strong emphasis, and most
implementations have also allowed the following patterns:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6806:1-6812:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="gs">***strong emph**</span><span class="err">*</span></span>
+<pre data-sourcepos="8240:1-8246:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="gs">***strong emph**</span><span class="err">*</span></span>
<span id="LC2" class="line" lang="markdown"><span class="gs">***strong**</span> in emph<span class="err">*</span></span>
<span id="LC3" class="line" lang="markdown"><span class="gs">***emph* in strong**</span></span>
<span id="LC4" class="line" lang="markdown"><span class="gs">**in strong *emph**</span><span class="err">*</span></span>
<span id="LC5" class="line" lang="markdown"><span class="ge">*in emph **strong***</span></span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6814:1-6816:9" dir="auto">The following patterns are less widely supported, but the intent
+<p data-sourcepos="8248:1-8250:9" dir="auto">The following patterns are less widely supported, but the intent
is clear and they are useful (especially in contexts like bibliography
entries):</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6818:1-6821:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="ge">*emph *</span>with emph<span class="ge">* in it*</span></span>
+<pre data-sourcepos="8252:1-8255:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="ge">*emph *</span>with emph<span class="ge">* in it*</span></span>
<span id="LC2" class="line" lang="markdown"><span class="gs">**strong **</span>with strong<span class="gs">** in it**</span></span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6823:1-6826:31" dir="auto">Many implementations have also restricted intraword emphasis to
+<p data-sourcepos="8257:1-8260:31" dir="auto">Many implementations have also restricted intraword emphasis to
the <code>*</code> forms, to avoid unwanted emphasis in words containing
internal underscores. (It is best practice to put these in code
spans, but users often do not.)</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6828:1-6831:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown">internal emphasis: foo<span class="ge">*bar*</span>baz</span>
+<pre data-sourcepos="8262:1-8265:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown">internal emphasis: foo<span class="ge">*bar*</span>baz</span>
<span id="LC2" class="line" lang="markdown">no emphasis: foo_bar_baz</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="6833:1-6834:55" dir="auto">The rules given below capture all of these patterns, while allowing
+<p data-sourcepos="8267:1-8268:55" dir="auto">The rules given below capture all of these patterns, while allowing
for efficient parsing strategies that do not backtrack.</p>
-<p data-sourcepos="6836:1-6840:38" dir="auto">First, some definitions. A <a href="@">delimiter run</a> is either
+<p data-sourcepos="8270:1-8274:38" dir="auto">First, some definitions. A <a href="@">delimiter run</a> is either
a sequence of one or more <code>*</code> characters that is not preceded or
followed by a non-backslash-escaped <code>*</code> character, or a sequence
of one or more <code>_</code> characters that is not preceded or followed by
a non-backslash-escaped <code>_</code> character.</p>
-<p data-sourcepos="6842:1-6848:37" dir="auto">A <a href="@">left-flanking delimiter run</a> is
+<p data-sourcepos="8276:1-8282:37" dir="auto">A <a href="@">left-flanking delimiter run</a> is
a [delimiter run] that is (1) not followed by [Unicode whitespace],
and either (2a) not followed by a [punctuation character], or
(2b) followed by a [punctuation character] and
preceded by [Unicode whitespace] or a [punctuation character].
For purposes of this definition, the beginning and the end of
the line count as Unicode whitespace.</p>
-<p data-sourcepos="6850:1-6856:37" dir="auto">A <a href="@">right-flanking delimiter run</a> is
+<p data-sourcepos="8284:1-8290:37" dir="auto">A <a href="@">right-flanking delimiter run</a> is
a [delimiter run] that is (1) not preceded by [Unicode whitespace],
and either (2a) not preceded by a [punctuation character], or
(2b) preceded by a [punctuation character] and
followed by [Unicode whitespace] or a [punctuation character].
For purposes of this definition, the beginning and the end of
the line count as Unicode whitespace.</p>
-<p data-sourcepos="6858:1-6858:41" dir="auto">Here are some examples of delimiter runs.</p>
-<ul data-sourcepos="6860:3-6891:0" dir="auto">
-<li data-sourcepos="6860:3-6868:0">
-<p data-sourcepos="6860:5-6860:41">left-flanking but not right-flanking:</p>
+<p data-sourcepos="8292:1-8292:41" dir="auto">Here are some examples of delimiter runs.</p>
+<ul data-sourcepos="8294:3-8325:0" dir="auto">
+<li data-sourcepos="8294:3-8302:0">
+<p data-sourcepos="8294:5-8294:41">left-flanking but not right-flanking:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6862:5-6867:7" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">***abc</span>
+<pre data-sourcepos="8296:5-8301:7" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">***abc</span>
<span id="LC2" class="line" lang="plaintext"> _abc</span>
<span id="LC3" class="line" lang="plaintext">**"abc"</span>
<span id="LC4" class="line" lang="plaintext"> _"abc"</span></code></pre>
<copy-code></copy-code>
</div>
</li>
-<li data-sourcepos="6869:3-6877:0">
-<p data-sourcepos="6869:5-6869:41">right-flanking but not left-flanking:</p>
+<li data-sourcepos="8303:3-8311:0">
+<p data-sourcepos="8303:5-8303:41">right-flanking but not left-flanking:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6871:5-6876:7" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> abc***</span>
+<pre data-sourcepos="8305:5-8310:7" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> abc***</span>
<span id="LC2" class="line" lang="plaintext"> abc_</span>
<span id="LC3" class="line" lang="plaintext">"abc"**</span>
<span id="LC4" class="line" lang="plaintext">"abc"_</span></code></pre>
<copy-code></copy-code>
</div>
</li>
-<li data-sourcepos="6878:3-6884:0">
-<p data-sourcepos="6878:5-6878:33">Both left and right-flanking:</p>
+<li data-sourcepos="8312:3-8318:0">
+<p data-sourcepos="8312:5-8312:33">Both left and right-flanking:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6880:5-6883:7" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> abc***def</span>
+<pre data-sourcepos="8314:5-8317:7" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> abc***def</span>
<span id="LC2" class="line" lang="plaintext">"abc"_"def"</span></code></pre>
<copy-code></copy-code>
</div>
</li>
-<li data-sourcepos="6885:3-6891:0">
-<p data-sourcepos="6885:5-6885:36">Neither left nor right-flanking:</p>
+<li data-sourcepos="8319:3-8325:0">
+<p data-sourcepos="8319:5-8319:36">Neither left nor right-flanking:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="6887:5-6890:7" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">abc *** def</span>
+<pre data-sourcepos="8321:5-8324:7" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">abc *** def</span>
<span id="LC2" class="line" lang="plaintext">a _ b</span></code></pre>
<copy-code></copy-code>
</div>
</li>
</ul>
-<p data-sourcepos="6892:1-6898:49" dir="auto">(The idea of distinguishing left-flanking and right-flanking
+<p data-sourcepos="8326:1-8332:49" dir="auto">(The idea of distinguishing left-flanking and right-flanking
delimiter runs based on the character before and the character
after comes from Roopesh Chander's
<a href="http://www.vfmd.org/vfmd-spec/specification/#procedure-for-identifying-emphasis-tags" rel="nofollow noreferrer noopener" target="_blank">vfmd</a>.
vfmd uses the terminology "emphasis indicator string" instead of "delimiter
run," and its rules for distinguishing left- and right-flanking runs
are a bit more complex than the ones given here.)</p>
-<p data-sourcepos="6900:1-6900:56" dir="auto">The following rules define emphasis and strong emphasis:</p>
-<ol data-sourcepos="6902:1-6966:0" dir="auto">
-<li data-sourcepos="6902:1-6904:0">
-<p data-sourcepos="6902:5-6903:71">A single <code>*</code> character <a href="@">can open emphasis</a>
+<p data-sourcepos="8334:1-8334:56" dir="auto">The following rules define emphasis and strong emphasis:</p>
+<ol data-sourcepos="8336:1-8400:0" dir="auto">
+<li data-sourcepos="8336:1-8338:0">
+<p data-sourcepos="8336:5-8337:71">A single <code>*</code> character <a href="@">can open emphasis</a>
iff (if and only if) it is part of a [left-flanking delimiter run].</p>
</li>
-<li data-sourcepos="6905:1-6910:0">
-<p data-sourcepos="6905:5-6909:28">A single <code>_</code> character [can open emphasis] iff
+<li data-sourcepos="8339:1-8344:0">
+<p data-sourcepos="8339:5-8343:28">A single <code>_</code> character [can open emphasis] iff
it is part of a [left-flanking delimiter run]
and either (a) not part of a [right-flanking delimiter run]
or (b) part of a [right-flanking delimiter run]
preceded by punctuation.</p>
</li>
-<li data-sourcepos="6911:1-6913:0">
-<p data-sourcepos="6911:5-6912:55">A single <code>*</code> character <a href="@">can close emphasis</a>
+<li data-sourcepos="8345:1-8347:0">
+<p data-sourcepos="8345:5-8346:55">A single <code>*</code> character <a href="@">can close emphasis</a>
iff it is part of a [right-flanking delimiter run].</p>
</li>
-<li data-sourcepos="6914:1-6919:0">
-<p data-sourcepos="6914:5-6918:28">A single <code>_</code> character [can close emphasis] iff
+<li data-sourcepos="8348:1-8353:0">
+<p data-sourcepos="8348:5-8352:28">A single <code>_</code> character [can close emphasis] iff
it is part of a [right-flanking delimiter run]
and either (a) not part of a [left-flanking delimiter run]
or (b) part of a [left-flanking delimiter run]
followed by punctuation.</p>
</li>
-<li data-sourcepos="6920:1-6922:0">
-<p data-sourcepos="6920:5-6921:54">A double <code>**</code> <a href="@">can open strong emphasis</a>
+<li data-sourcepos="8354:1-8356:0">
+<p data-sourcepos="8354:5-8355:54">A double <code>**</code> <a href="@">can open strong emphasis</a>
iff it is part of a [left-flanking delimiter run].</p>
</li>
-<li data-sourcepos="6923:1-6928:0">
-<p data-sourcepos="6923:5-6927:28">A double <code>__</code> [can open strong emphasis] iff
+<li data-sourcepos="8357:1-8362:0">
+<p data-sourcepos="8357:5-8361:28">A double <code>__</code> [can open strong emphasis] iff
it is part of a [left-flanking delimiter run]
and either (a) not part of a [right-flanking delimiter run]
or (b) part of a [right-flanking delimiter run]
preceded by punctuation.</p>
</li>
-<li data-sourcepos="6929:1-6931:0">
-<p data-sourcepos="6929:5-6930:55">A double <code>**</code> <a href="@">can close strong emphasis</a>
+<li data-sourcepos="8363:1-8365:0">
+<p data-sourcepos="8363:5-8364:55">A double <code>**</code> <a href="@">can close strong emphasis</a>
iff it is part of a [right-flanking delimiter run].</p>
</li>
-<li data-sourcepos="6932:1-6937:0">
-<p data-sourcepos="6932:5-6936:28">A double <code>__</code> [can close strong emphasis] iff
+<li data-sourcepos="8366:1-8371:0">
+<p data-sourcepos="8366:5-8370:28">A double <code>__</code> [can close strong emphasis] iff
it is part of a [right-flanking delimiter run]
and either (a) not part of a [left-flanking delimiter run]
or (b) part of a [left-flanking delimiter run]
followed by punctuation.</p>
</li>
-<li data-sourcepos="6938:1-6947:0">
-<p data-sourcepos="6938:5-6946:19">Emphasis begins with a delimiter that [can open emphasis] and ends
+<li data-sourcepos="8372:1-8381:0">
+<p data-sourcepos="8372:5-8380:19">Emphasis begins with a delimiter that [can open emphasis] and ends
with a delimiter that [can close emphasis], and that uses the same
character (<code>_</code> or <code>*</code>) as the opening delimiter. The
opening and closing delimiters must belong to separate
@@ -6289,8 +7730,8 @@ delimiter runs containing the opening and closing delimiters
must not be a multiple of 3 unless both lengths are
multiples of 3.</p>
</li>
-<li data-sourcepos="6948:1-6958:0">
-<p data-sourcepos="6948:5-6957:23">Strong emphasis begins with a delimiter that
+<li data-sourcepos="8382:1-8392:0">
+<p data-sourcepos="8382:5-8391:23">Strong emphasis begins with a delimiter that
[can open strong emphasis] and ends with a delimiter that
[can close strong emphasis], and that uses the same character
(<code>_</code> or <code>*</code>) as the opening delimiter. The
@@ -6301,45 +7742,45 @@ the delimiter runs containing the opening and closing
delimiters must not be a multiple of 3 unless both lengths
are multiples of 3.</p>
</li>
-<li data-sourcepos="6959:1-6962:0">
-<p data-sourcepos="6959:5-6961:25">A literal <code>*</code> character cannot occur at the beginning or end of
+<li data-sourcepos="8393:1-8396:0">
+<p data-sourcepos="8393:5-8395:25">A literal <code>*</code> character cannot occur at the beginning or end of
<code>*</code>-delimited emphasis or <code>**</code>-delimited strong emphasis, unless it
is backslash-escaped.</p>
</li>
-<li data-sourcepos="6963:1-6966:0">
-<p data-sourcepos="6963:5-6965:25">A literal <code>_</code> character cannot occur at the beginning or end of
+<li data-sourcepos="8397:1-8400:0">
+<p data-sourcepos="8397:5-8399:25">A literal <code>_</code> character cannot occur at the beginning or end of
<code>_</code>-delimited emphasis or <code>__</code>-delimited strong emphasis, unless it
is backslash-escaped.</p>
</li>
</ol>
-<p data-sourcepos="6967:1-6968:43" dir="auto">Where rules 1--12 above are compatible with multiple parsings,
+<p data-sourcepos="8401:1-8402:43" dir="auto">Where rules 1--12 above are compatible with multiple parsings,
the following principles resolve ambiguity:</p>
-<ol start="13" data-sourcepos="6970:1-6995:0" dir="auto">
-<li data-sourcepos="6970:1-6973:0">
-<p data-sourcepos="6970:5-6972:28">The number of nestings should be minimized. Thus, for example,
+<ol start="13" data-sourcepos="8404:1-8429:0" dir="auto">
+<li data-sourcepos="8404:1-8407:0">
+<p data-sourcepos="8404:5-8406:28">The number of nestings should be minimized. Thus, for example,
an interpretation <code>&lt;strong&gt;...&lt;/strong&gt;</code> is always preferred to
<code>&lt;em&gt;&lt;em&gt;...&lt;/em&gt;&lt;/em&gt;</code>.</p>
</li>
-<li data-sourcepos="6974:1-6976:0">
-<p data-sourcepos="6974:5-6975:49">An interpretation <code>&lt;em&gt;&lt;strong&gt;...&lt;/strong&gt;&lt;/em&gt;</code> is always
+<li data-sourcepos="8408:1-8410:0">
+<p data-sourcepos="8408:5-8409:49">An interpretation <code>&lt;em&gt;&lt;strong&gt;...&lt;/strong&gt;&lt;/em&gt;</code> is always
preferred to <code>&lt;strong&gt;&lt;em&gt;...&lt;/em&gt;&lt;/strong&gt;</code>.</p>
</li>
-<li data-sourcepos="6977:1-6982:0">
-<p data-sourcepos="6977:5-6981:34">When two potential emphasis or strong emphasis spans overlap,
+<li data-sourcepos="8411:1-8416:0">
+<p data-sourcepos="8411:5-8415:34">When two potential emphasis or strong emphasis spans overlap,
so that the second begins before the first ends and ends after
the first ends, the first takes precedence. Thus, for example,
<code>*foo _bar* baz_</code> is parsed as <code>&lt;em&gt;foo _bar&lt;/em&gt; baz_</code> rather
than <code>*foo &lt;em&gt;bar* baz&lt;/em&gt;</code>.</p>
</li>
-<li data-sourcepos="6983:1-6988:0">
-<p data-sourcepos="6983:5-6987:49">When there are two potential emphasis or strong emphasis spans
+<li data-sourcepos="8417:1-8422:0">
+<p data-sourcepos="8417:5-8421:49">When there are two potential emphasis or strong emphasis spans
with the same closing delimiter, the shorter one (the one that
opens later) takes precedence. Thus, for example,
<code>**foo **bar baz**</code> is parsed as <code>**foo &lt;strong&gt;bar baz&lt;/strong&gt;</code>
rather than <code>&lt;strong&gt;foo **bar baz&lt;/strong&gt;</code>.</p>
</li>
-<li data-sourcepos="6989:1-6995:0">
-<p data-sourcepos="6989:5-6994:26">Inline code spans, links, images, and HTML tags group more tightly
+<li data-sourcepos="8423:1-8429:0">
+<p data-sourcepos="8423:5-8428:26">Inline code spans, links, images, and HTML tags group more tightly
than emphasis. So, when there is a choice between an interpretation
that contains one of these elements and one that does not, the
former always wins. Thus, for example, <code>*[foo*](bar)</code> is
@@ -6347,1269 +7788,1668 @@ parsed as <code>*&lt;a href="bar"&gt;foo*&lt;/a&gt;</code> rather than as
<code>&lt;em&gt;[foo&lt;/em&gt;](bar)</code>.</p>
</li>
</ol>
-<p data-sourcepos="6996:1-6996:60" dir="auto">These rules can be illustrated through a series of examples.</p>
-<p data-sourcepos="6998:1-6998:7" dir="auto">Rule 1:</p>
+<p data-sourcepos="8430:1-8430:60" dir="auto">These rules can be illustrated through a series of examples.</p>
+<p data-sourcepos="8432:1-8432:7" dir="auto">Rule 1:</p>
+<div>
+<div><a href="#example-360">Example 360</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7000:1-7002:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo bar*</span></code></pre>
+<pre data-sourcepos="8437:1-8439:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo bar*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7004:1-7006:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo bar&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8441:1-8443:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo bar&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7009:1-7010:66" dir="auto">This is not emphasis, because the opening <code>*</code> is followed by
+</div>
+<p data-sourcepos="8447:1-8448:66" dir="auto">This is not emphasis, because the opening <code>*</code> is followed by
whitespace, and hence not part of a [left-flanking delimiter run]:</p>
+<div>
+<div><a href="#example-361">Example 361</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7012:1-7014:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">a * foo bar*</span></code></pre>
+<pre data-sourcepos="8453:1-8455:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">a * foo bar*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7016:1-7018:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;a * foo bar*&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8457:1-8459:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;a * foo bar*&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7021:1-7023:44" dir="auto">This is not emphasis, because the opening <code>*</code> is preceded
+</div>
+<p data-sourcepos="8463:1-8465:44" dir="auto">This is not emphasis, because the opening <code>*</code> is preceded
by an alphanumeric and followed by punctuation, and hence
not part of a [left-flanking delimiter run]:</p>
+<div>
+<div><a href="#example-362">Example 362</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7025:1-7027:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">a*"foo"*</span></code></pre>
+<pre data-sourcepos="8470:1-8472:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">a*"foo"*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7029:1-7031:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;a*&amp;quot;foo&amp;quot;*&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8474:1-8476:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;a*&amp;quot;foo&amp;quot;*&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7034:1-7034:52" dir="auto">Unicode nonbreaking spaces count as whitespace, too:</p>
+</div>
+<p data-sourcepos="8480:1-8480:52" dir="auto">Unicode nonbreaking spaces count as whitespace, too:</p>
+<div>
+<div><a href="#example-363">Example 363</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7036:1-7038:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* a *</span></code></pre>
+<pre data-sourcepos="8485:1-8487:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* a *</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7040:1-7042:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;* a *&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8489:1-8491:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;* a *&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7045:1-7045:41" dir="auto">Intraword emphasis with <code>*</code> is permitted:</p>
+</div>
+<p data-sourcepos="8495:1-8495:41" dir="auto">Intraword emphasis with <code>*</code> is permitted:</p>
+<div>
+<div><a href="#example-364">Example 364</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7047:1-7049:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo*bar*</span></code></pre>
+<pre data-sourcepos="8500:1-8502:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo*bar*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7051:1-7053:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;em&gt;bar&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8504:1-8506:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;em&gt;bar&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-365">Example 365</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7056:1-7058:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">5*6*78</span></code></pre>
+<pre data-sourcepos="8513:1-8515:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">5*6*78</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7060:1-7062:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;5&lt;em&gt;6&lt;/em&gt;78&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8517:1-8519:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;5&lt;em&gt;6&lt;/em&gt;78&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7065:1-7065:7" dir="auto">Rule 2:</p>
+</div>
+<p data-sourcepos="8523:1-8523:7" dir="auto">Rule 2:</p>
+<div>
+<div><a href="#example-366">Example 366</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7067:1-7069:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo bar_</span></code></pre>
+<pre data-sourcepos="8528:1-8530:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo bar_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7071:1-7073:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo bar&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8532:1-8534:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo bar&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7076:1-7077:11" dir="auto">This is not emphasis, because the opening <code>_</code> is followed by
+</div>
+<p data-sourcepos="8538:1-8539:11" dir="auto">This is not emphasis, because the opening <code>_</code> is followed by
whitespace:</p>
+<div>
+<div><a href="#example-367">Example 367</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7079:1-7081:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_ foo bar_</span></code></pre>
+<pre data-sourcepos="8544:1-8546:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_ foo bar_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7083:1-7085:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_ foo bar_&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8548:1-8550:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_ foo bar_&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7088:1-7089:47" dir="auto">This is not emphasis, because the opening <code>_</code> is preceded
+</div>
+<p data-sourcepos="8554:1-8555:47" dir="auto">This is not emphasis, because the opening <code>_</code> is preceded
by an alphanumeric and followed by punctuation:</p>
+<div>
+<div><a href="#example-368">Example 368</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7091:1-7093:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">a_"foo"_</span></code></pre>
+<pre data-sourcepos="8560:1-8562:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">a_"foo"_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7095:1-7097:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;a_&amp;quot;foo&amp;quot;_&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8564:1-8566:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;a_&amp;quot;foo&amp;quot;_&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7100:1-7100:46" dir="auto">Emphasis with <code>_</code> is not allowed inside words:</p>
+</div>
+<p data-sourcepos="8570:1-8570:46" dir="auto">Emphasis with <code>_</code> is not allowed inside words:</p>
+<div>
+<div><a href="#example-369">Example 369</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7102:1-7104:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo_bar_</span></code></pre>
+<pre data-sourcepos="8575:1-8577:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo_bar_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7106:1-7108:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo_bar_&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8579:1-8581:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo_bar_&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-370">Example 370</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7111:1-7113:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">5_6_78</span></code></pre>
+<pre data-sourcepos="8588:1-8590:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">5_6_78</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7115:1-7117:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;5_6_78&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8592:1-8594:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;5_6_78&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-371">Example 371</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7120:1-7122:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">приÑтанÑм_ÑтремÑÑ‚ÑÑ_</span></code></pre>
+<pre data-sourcepos="8601:1-8603:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">приÑтанÑм_ÑтремÑÑ‚ÑÑ_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7124:1-7126:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;приÑтанÑм_ÑтремÑÑ‚ÑÑ_&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8605:1-8607:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;приÑтанÑм_ÑтремÑÑ‚ÑÑ_&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7129:1-7130:47" dir="auto">Here <code>_</code> does not generate emphasis, because the first delimiter run
+</div>
+<p data-sourcepos="8611:1-8612:47" dir="auto">Here <code>_</code> does not generate emphasis, because the first delimiter run
is right-flanking and the second left-flanking:</p>
+<div>
+<div><a href="#example-372">Example 372</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7132:1-7134:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">aa_"bb"_cc</span></code></pre>
+<pre data-sourcepos="8617:1-8619:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">aa_"bb"_cc</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7136:1-7138:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aa_&amp;quot;bb&amp;quot;_cc&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8621:1-8623:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;aa_&amp;quot;bb&amp;quot;_cc&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7141:1-7143:12" dir="auto">This is emphasis, even though the opening delimiter is
+</div>
+<p data-sourcepos="8627:1-8629:12" dir="auto">This is emphasis, even though the opening delimiter is
both left- and right-flanking, because it is preceded by
punctuation:</p>
+<div>
+<div><a href="#example-373">Example 373</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7145:1-7147:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo-_(bar)_</span></code></pre>
+<pre data-sourcepos="8634:1-8636:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo-_(bar)_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7149:1-7151:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo-&lt;em&gt;(bar)&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8638:1-8640:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo-&lt;em&gt;(bar)&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7154:1-7154:7" dir="auto">Rule 3:</p>
-<p data-sourcepos="7156:1-7157:32" dir="auto">This is not emphasis, because the closing delimiter does
+</div>
+<p data-sourcepos="8644:1-8644:7" dir="auto">Rule 3:</p>
+<p data-sourcepos="8646:1-8647:32" dir="auto">This is not emphasis, because the closing delimiter does
not match the opening delimiter:</p>
+<div>
+<div><a href="#example-374">Example 374</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7159:1-7161:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo*</span></code></pre>
+<pre data-sourcepos="8652:1-8654:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7163:1-7165:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_foo*&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8656:1-8658:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_foo*&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7168:1-7169:11" dir="auto">This is not emphasis, because the closing <code>*</code> is preceded by
+</div>
+<p data-sourcepos="8662:1-8663:11" dir="auto">This is not emphasis, because the closing <code>*</code> is preceded by
whitespace:</p>
+<div>
+<div><a href="#example-375">Example 375</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7171:1-7173:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo bar *</span></code></pre>
+<pre data-sourcepos="8668:1-8670:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo bar *</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7175:1-7177:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*foo bar *&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8672:1-8674:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*foo bar *&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7180:1-7180:36" dir="auto">A newline also counts as whitespace:</p>
+</div>
+<p data-sourcepos="8678:1-8678:36" dir="auto">A newline also counts as whitespace:</p>
+<div>
+<div><a href="#example-376">Example 376</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7182:1-7185:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo bar</span>
+<pre data-sourcepos="8683:1-8686:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo bar</span>
<span id="LC2" class="line" lang="plaintext">*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7187:1-7190:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*foo bar</span>
+<pre data-sourcepos="8688:1-8691:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*foo bar</span>
<span id="LC2" class="line" lang="plaintext">*&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7193:1-7195:58" dir="auto">This is not emphasis, because the second <code>*</code> is
+</div>
+<p data-sourcepos="8695:1-8697:58" dir="auto">This is not emphasis, because the second <code>*</code> is
preceded by punctuation and followed by an alphanumeric
(hence it is not part of a [right-flanking delimiter run]:</p>
+<div>
+<div><a href="#example-377">Example 377</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7197:1-7199:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*(*foo)</span></code></pre>
+<pre data-sourcepos="8702:1-8704:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*(*foo)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7201:1-7203:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*(*foo)&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8706:1-8708:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*(*foo)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7206:1-7207:18" dir="auto">The point of this restriction is more easily appreciated
+</div>
+<p data-sourcepos="8712:1-8713:18" dir="auto">The point of this restriction is more easily appreciated
with this example:</p>
+<div>
+<div><a href="#example-378">Example 378</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7209:1-7211:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*(*foo*)*</span></code></pre>
+<pre data-sourcepos="8718:1-8720:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*(*foo*)*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7213:1-7215:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;(&lt;em&gt;foo&lt;/em&gt;)&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8722:1-8724:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;(&lt;em&gt;foo&lt;/em&gt;)&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7218:1-7218:39" dir="auto">Intraword emphasis with <code>*</code> is allowed:</p>
+</div>
+<p data-sourcepos="8728:1-8728:39" dir="auto">Intraword emphasis with <code>*</code> is allowed:</p>
+<div>
+<div><a href="#example-379">Example 379</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7220:1-7222:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo*bar</span></code></pre>
+<pre data-sourcepos="8733:1-8735:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo*bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7224:1-7226:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;/em&gt;bar&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8737:1-8739:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;/em&gt;bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7230:1-7230:7" dir="auto">Rule 4:</p>
-<p data-sourcepos="7232:1-7233:11" dir="auto">This is not emphasis, because the closing <code>_</code> is preceded by
+</div>
+<p data-sourcepos="8744:1-8744:7" dir="auto">Rule 4:</p>
+<p data-sourcepos="8746:1-8747:11" dir="auto">This is not emphasis, because the closing <code>_</code> is preceded by
whitespace:</p>
+<div>
+<div><a href="#example-380">Example 380</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7235:1-7237:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo bar _</span></code></pre>
+<pre data-sourcepos="8752:1-8754:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo bar _</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7239:1-7241:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_foo bar _&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8756:1-8758:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_foo bar _&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7244:1-7245:56" dir="auto">This is not emphasis, because the second <code>_</code> is
+</div>
+<p data-sourcepos="8762:1-8763:56" dir="auto">This is not emphasis, because the second <code>_</code> is
preceded by punctuation and followed by an alphanumeric:</p>
+<div>
+<div><a href="#example-381">Example 381</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7247:1-7249:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_(_foo)</span></code></pre>
+<pre data-sourcepos="8768:1-8770:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_(_foo)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7251:1-7253:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_(_foo)&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8772:1-8774:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_(_foo)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7256:1-7256:33" dir="auto">This is emphasis within emphasis:</p>
+</div>
+<p data-sourcepos="8778:1-8778:33" dir="auto">This is emphasis within emphasis:</p>
+<div>
+<div><a href="#example-382">Example 382</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7258:1-7260:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_(_foo_)_</span></code></pre>
+<pre data-sourcepos="8783:1-8785:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_(_foo_)_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7262:1-7264:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;(&lt;em&gt;foo&lt;/em&gt;)&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8787:1-8789:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;(&lt;em&gt;foo&lt;/em&gt;)&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7267:1-7267:41" dir="auto">Intraword emphasis is disallowed for <code>_</code>:</p>
+</div>
+<p data-sourcepos="8793:1-8793:41" dir="auto">Intraword emphasis is disallowed for <code>_</code>:</p>
+<div>
+<div><a href="#example-383">Example 383</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7269:1-7271:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo_bar</span></code></pre>
+<pre data-sourcepos="8798:1-8800:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo_bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7273:1-7275:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_foo_bar&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8802:1-8804:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_foo_bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-384">Example 384</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7278:1-7280:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_приÑтанÑм_ÑтремÑÑ‚ÑÑ</span></code></pre>
+<pre data-sourcepos="8811:1-8813:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_приÑтанÑм_ÑтремÑÑ‚ÑÑ</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7282:1-7284:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_приÑтанÑм_ÑтремÑÑ‚ÑÑ&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8815:1-8817:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_приÑтанÑм_ÑтремÑÑ‚ÑÑ&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-385">Example 385</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7287:1-7289:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo_bar_baz_</span></code></pre>
+<pre data-sourcepos="8824:1-8826:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo_bar_baz_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7291:1-7293:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo_bar_baz&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8828:1-8830:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo_bar_baz&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7296:1-7298:12" dir="auto">This is emphasis, even though the closing delimiter is
+</div>
+<p data-sourcepos="8834:1-8836:12" dir="auto">This is emphasis, even though the closing delimiter is
both left- and right-flanking, because it is followed by
punctuation:</p>
+<div>
+<div><a href="#example-386">Example 386</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7300:1-7302:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_(bar)_.</span></code></pre>
+<pre data-sourcepos="8841:1-8843:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_(bar)_.</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7304:1-7306:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;(bar)&lt;/em&gt;.&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8845:1-8847:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;(bar)&lt;/em&gt;.&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7309:1-7309:7" dir="auto">Rule 5:</p>
+</div>
+<p data-sourcepos="8851:1-8851:7" dir="auto">Rule 5:</p>
+<div>
+<div><a href="#example-387">Example 387</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7311:1-7313:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo bar**</span></code></pre>
+<pre data-sourcepos="8856:1-8858:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo bar**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7315:1-7317:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo bar&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8860:1-8862:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo bar&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7320:1-7321:23" dir="auto">This is not strong emphasis, because the opening delimiter is
+</div>
+<p data-sourcepos="8866:1-8867:23" dir="auto">This is not strong emphasis, because the opening delimiter is
followed by whitespace:</p>
+<div>
+<div><a href="#example-388">Example 388</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7323:1-7325:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">** foo bar**</span></code></pre>
+<pre data-sourcepos="8872:1-8874:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">** foo bar**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7327:1-7329:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;** foo bar**&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8876:1-8878:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;** foo bar**&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7332:1-7334:44" dir="auto">This is not strong emphasis, because the opening <code>**</code> is preceded
+</div>
+<p data-sourcepos="8882:1-8884:44" dir="auto">This is not strong emphasis, because the opening <code>**</code> is preceded
by an alphanumeric and followed by punctuation, and hence
not part of a [left-flanking delimiter run]:</p>
+<div>
+<div><a href="#example-389">Example 389</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7336:1-7338:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">a**"foo"**</span></code></pre>
+<pre data-sourcepos="8889:1-8891:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">a**"foo"**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7340:1-7342:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;a**&amp;quot;foo&amp;quot;**&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8893:1-8895:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;a**&amp;quot;foo&amp;quot;**&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7345:1-7345:49" dir="auto">Intraword strong emphasis with <code>**</code> is permitted:</p>
+</div>
+<p data-sourcepos="8899:1-8899:49" dir="auto">Intraword strong emphasis with <code>**</code> is permitted:</p>
+<div>
+<div><a href="#example-390">Example 390</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7347:1-7349:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo**bar**</span></code></pre>
+<pre data-sourcepos="8904:1-8906:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo**bar**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7351:1-7353:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;strong&gt;bar&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8908:1-8910:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;strong&gt;bar&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7356:1-7356:7" dir="auto">Rule 6:</p>
+</div>
+<p data-sourcepos="8914:1-8914:7" dir="auto">Rule 6:</p>
+<div>
+<div><a href="#example-391">Example 391</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7358:1-7360:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo bar__</span></code></pre>
+<pre data-sourcepos="8919:1-8921:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo bar__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7362:1-7364:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo bar&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8923:1-8925:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo bar&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7367:1-7368:23" dir="auto">This is not strong emphasis, because the opening delimiter is
+</div>
+<p data-sourcepos="8929:1-8930:23" dir="auto">This is not strong emphasis, because the opening delimiter is
followed by whitespace:</p>
+<div>
+<div><a href="#example-392">Example 392</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7370:1-7372:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__ foo bar__</span></code></pre>
+<pre data-sourcepos="8935:1-8937:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__ foo bar__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7374:1-7376:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__ foo bar__&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8939:1-8941:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__ foo bar__&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7379:1-7379:31" dir="auto">A newline counts as whitespace:</p>
+</div>
+<p data-sourcepos="8945:1-8945:31" dir="auto">A newline counts as whitespace:</p>
+<div>
+<div><a href="#example-393">Example 393</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7380:1-7383:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__</span>
+<pre data-sourcepos="8949:1-8952:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__</span>
<span id="LC2" class="line" lang="plaintext">foo bar__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7385:1-7388:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__</span>
+<pre data-sourcepos="8954:1-8957:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__</span>
<span id="LC2" class="line" lang="plaintext">foo bar__&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7391:1-7392:47" dir="auto">This is not strong emphasis, because the opening <code>__</code> is preceded
+</div>
+<p data-sourcepos="8961:1-8962:47" dir="auto">This is not strong emphasis, because the opening <code>__</code> is preceded
by an alphanumeric and followed by punctuation:</p>
+<div>
+<div><a href="#example-394">Example 394</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7394:1-7396:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">a__"foo"__</span></code></pre>
+<pre data-sourcepos="8967:1-8969:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">a__"foo"__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7398:1-7400:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;a__&amp;quot;foo&amp;quot;__&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8971:1-8973:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;a__&amp;quot;foo&amp;quot;__&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7403:1-7403:49" dir="auto">Intraword strong emphasis is forbidden with <code>__</code>:</p>
+</div>
+<p data-sourcepos="8977:1-8977:49" dir="auto">Intraword strong emphasis is forbidden with <code>__</code>:</p>
+<div>
+<div><a href="#example-395">Example 395</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7405:1-7407:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo__bar__</span></code></pre>
+<pre data-sourcepos="8982:1-8984:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo__bar__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7409:1-7411:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo__bar__&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8986:1-8988:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo__bar__&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-396">Example 396</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7414:1-7416:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">5__6__78</span></code></pre>
+<pre data-sourcepos="8995:1-8997:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">5__6__78</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7418:1-7420:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;5__6__78&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="8999:1-9001:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;5__6__78&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-397">Example 397</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7423:1-7425:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">приÑтанÑм__ÑтремÑÑ‚ÑÑ__</span></code></pre>
+<pre data-sourcepos="9008:1-9010:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">приÑтанÑм__ÑтремÑÑ‚ÑÑ__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7427:1-7429:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;приÑтанÑм__ÑтремÑÑ‚ÑÑ__&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9012:1-9014:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;приÑтанÑм__ÑтремÑÑ‚ÑÑ__&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-398">Example 398</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7432:1-7434:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo, __bar__, baz__</span></code></pre>
+<pre data-sourcepos="9021:1-9023:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo, __bar__, baz__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7436:1-7438:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo, &lt;strong&gt;bar&lt;/strong&gt;, baz&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9025:1-9027:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo, &lt;strong&gt;bar&lt;/strong&gt;, baz&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7441:1-7443:12" dir="auto">This is strong emphasis, even though the opening delimiter is
+</div>
+<p data-sourcepos="9031:1-9033:12" dir="auto">This is strong emphasis, even though the opening delimiter is
both left- and right-flanking, because it is preceded by
punctuation:</p>
+<div>
+<div><a href="#example-399">Example 399</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7445:1-7447:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo-__(bar)__</span></code></pre>
+<pre data-sourcepos="9038:1-9040:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo-__(bar)__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7449:1-7451:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo-&lt;strong&gt;(bar)&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9042:1-9044:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo-&lt;strong&gt;(bar)&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7455:1-7455:7" dir="auto">Rule 7:</p>
-<p data-sourcepos="7457:1-7458:14" dir="auto">This is not strong emphasis, because the closing delimiter is preceded
+</div>
+<p data-sourcepos="9049:1-9049:7" dir="auto">Rule 7:</p>
+<p data-sourcepos="9051:1-9052:14" dir="auto">This is not strong emphasis, because the closing delimiter is preceded
by whitespace:</p>
+<div>
+<div><a href="#example-400">Example 400</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7460:1-7462:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo bar **</span></code></pre>
+<pre data-sourcepos="9057:1-9059:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo bar **</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7464:1-7466:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;**foo bar **&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9061:1-9063:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;**foo bar **&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7469:1-7470:9" dir="auto">(Nor can it be interpreted as an emphasized <code>*foo bar *</code>, because of
+</div>
+<p data-sourcepos="9067:1-9068:9" dir="auto">(Nor can it be interpreted as an emphasized <code>*foo bar *</code>, because of
Rule 11.)</p>
-<p data-sourcepos="7472:1-7473:56" dir="auto">This is not strong emphasis, because the second <code>**</code> is
+<p data-sourcepos="9070:1-9071:56" dir="auto">This is not strong emphasis, because the second <code>**</code> is
preceded by punctuation and followed by an alphanumeric:</p>
+<div>
+<div><a href="#example-401">Example 401</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7475:1-7477:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**(**foo)</span></code></pre>
+<pre data-sourcepos="9076:1-9078:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**(**foo)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7479:1-7481:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;**(**foo)&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9080:1-9082:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;**(**foo)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7484:1-7485:20" dir="auto">The point of this restriction is more easily appreciated
+</div>
+<p data-sourcepos="9086:1-9087:20" dir="auto">The point of this restriction is more easily appreciated
with these examples:</p>
+<div>
+<div><a href="#example-402">Example 402</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7487:1-7489:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*(**foo**)*</span></code></pre>
+<pre data-sourcepos="9092:1-9094:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*(**foo**)*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7491:1-7493:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;(&lt;strong&gt;foo&lt;/strong&gt;)&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9096:1-9098:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;(&lt;strong&gt;foo&lt;/strong&gt;)&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-403">Example 403</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7496:1-7499:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**Gomphocarpus (*Gomphocarpus physocarpus*, syn.</span>
+<pre data-sourcepos="9105:1-9108:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**Gomphocarpus (*Gomphocarpus physocarpus*, syn.</span>
<span id="LC2" class="line" lang="plaintext">*Asclepias physocarpa*)**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7501:1-7504:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;Gomphocarpus (&lt;em&gt;Gomphocarpus physocarpus&lt;/em&gt;, syn.</span>
+<pre data-sourcepos="9110:1-9113:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;Gomphocarpus (&lt;em&gt;Gomphocarpus physocarpus&lt;/em&gt;, syn.</span>
<span id="LC2" class="line" lang="plaintext">&lt;em&gt;Asclepias physocarpa&lt;/em&gt;)&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-404">Example 404</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7507:1-7509:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo "*bar*" foo**</span></code></pre>
+<pre data-sourcepos="9120:1-9122:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo "*bar*" foo**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7511:1-7513:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &amp;quot;&lt;em&gt;bar&lt;/em&gt;&amp;quot; foo&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9124:1-9126:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &amp;quot;&lt;em&gt;bar&lt;/em&gt;&amp;quot; foo&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7516:1-7516:19" dir="auto">Intraword emphasis:</p>
+</div>
+<p data-sourcepos="9130:1-9130:19" dir="auto">Intraword emphasis:</p>
+<div>
+<div><a href="#example-405">Example 405</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7518:1-7520:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo**bar</span></code></pre>
+<pre data-sourcepos="9135:1-9137:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo**bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7522:1-7524:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo&lt;/strong&gt;bar&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9139:1-9141:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo&lt;/strong&gt;bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7527:1-7527:7" dir="auto">Rule 8:</p>
-<p data-sourcepos="7529:1-7530:23" dir="auto">This is not strong emphasis, because the closing delimiter is
+</div>
+<p data-sourcepos="9145:1-9145:7" dir="auto">Rule 8:</p>
+<p data-sourcepos="9147:1-9148:23" dir="auto">This is not strong emphasis, because the closing delimiter is
preceded by whitespace:</p>
+<div>
+<div><a href="#example-406">Example 406</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7532:1-7534:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo bar __</span></code></pre>
+<pre data-sourcepos="9153:1-9155:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo bar __</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7536:1-7538:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__foo bar __&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9157:1-9159:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__foo bar __&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7541:1-7542:56" dir="auto">This is not strong emphasis, because the second <code>__</code> is
+</div>
+<p data-sourcepos="9163:1-9164:56" dir="auto">This is not strong emphasis, because the second <code>__</code> is
preceded by punctuation and followed by an alphanumeric:</p>
+<div>
+<div><a href="#example-407">Example 407</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7544:1-7546:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__(__foo)</span></code></pre>
+<pre data-sourcepos="9169:1-9171:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__(__foo)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7548:1-7550:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__(__foo)&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9173:1-9175:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__(__foo)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7553:1-7554:18" dir="auto">The point of this restriction is more easily appreciated
+</div>
+<p data-sourcepos="9179:1-9180:18" dir="auto">The point of this restriction is more easily appreciated
with this example:</p>
+<div>
+<div><a href="#example-408">Example 408</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7556:1-7558:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_(__foo__)_</span></code></pre>
+<pre data-sourcepos="9185:1-9187:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_(__foo__)_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7560:1-7562:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;(&lt;strong&gt;foo&lt;/strong&gt;)&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9189:1-9191:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;(&lt;strong&gt;foo&lt;/strong&gt;)&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7565:1-7565:49" dir="auto">Intraword strong emphasis is forbidden with <code>__</code>:</p>
+</div>
+<p data-sourcepos="9195:1-9195:49" dir="auto">Intraword strong emphasis is forbidden with <code>__</code>:</p>
+<div>
+<div><a href="#example-409">Example 409</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7567:1-7569:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo__bar</span></code></pre>
+<pre data-sourcepos="9200:1-9202:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo__bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7571:1-7573:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__foo__bar&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9204:1-9206:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__foo__bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-410">Example 410</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7576:1-7578:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__приÑтанÑм__ÑтремÑÑ‚ÑÑ</span></code></pre>
+<pre data-sourcepos="9213:1-9215:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__приÑтанÑм__ÑтремÑÑ‚ÑÑ</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7580:1-7582:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__приÑтанÑм__ÑтремÑÑ‚ÑÑ&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9217:1-9219:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__приÑтанÑм__ÑтремÑÑ‚ÑÑ&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-411">Example 411</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7585:1-7587:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo__bar__baz__</span></code></pre>
+<pre data-sourcepos="9226:1-9228:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo__bar__baz__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7589:1-7591:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo__bar__baz&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9230:1-9232:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo__bar__baz&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7594:1-7596:12" dir="auto">This is strong emphasis, even though the closing delimiter is
+</div>
+<p data-sourcepos="9236:1-9238:12" dir="auto">This is strong emphasis, even though the closing delimiter is
both left- and right-flanking, because it is followed by
punctuation:</p>
+<div>
+<div><a href="#example-412">Example 412</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7598:1-7600:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__(bar)__.</span></code></pre>
+<pre data-sourcepos="9243:1-9245:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__(bar)__.</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7602:1-7604:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;(bar)&lt;/strong&gt;.&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9247:1-9249:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;(bar)&lt;/strong&gt;.&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7607:1-7607:7" dir="auto">Rule 9:</p>
-<p data-sourcepos="7609:1-7610:16" dir="auto">Any nonempty sequence of inline elements can be the contents of an
+</div>
+<p data-sourcepos="9253:1-9253:7" dir="auto">Rule 9:</p>
+<p data-sourcepos="9255:1-9256:16" dir="auto">Any nonempty sequence of inline elements can be the contents of an
emphasized span.</p>
+<div>
+<div><a href="#example-413">Example 413</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7612:1-7614:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo [bar](/url)*</span></code></pre>
+<pre data-sourcepos="9261:1-9263:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo [bar](/url)*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7616:1-7618:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;a href="/url"&gt;bar&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9265:1-9267:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;a href="/url"&gt;bar&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-414">Example 414</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7621:1-7624:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo</span>
+<pre data-sourcepos="9274:1-9277:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo</span>
<span id="LC2" class="line" lang="plaintext">bar*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7626:1-7629:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo</span>
+<pre data-sourcepos="9279:1-9282:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo</span>
<span id="LC2" class="line" lang="plaintext">bar&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7632:1-7633:16" dir="auto">In particular, emphasis and strong emphasis can be nested
+</div>
+<p data-sourcepos="9286:1-9287:16" dir="auto">In particular, emphasis and strong emphasis can be nested
inside emphasis:</p>
+<div>
+<div><a href="#example-415">Example 415</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7635:1-7637:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo __bar__ baz_</span></code></pre>
+<pre data-sourcepos="9292:1-9294:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo __bar__ baz_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7639:1-7641:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;strong&gt;bar&lt;/strong&gt; baz&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9296:1-9298:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;strong&gt;bar&lt;/strong&gt; baz&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-416">Example 416</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7644:1-7646:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo _bar_ baz_</span></code></pre>
+<pre data-sourcepos="9305:1-9307:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo _bar_ baz_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7648:1-7650:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;em&gt;bar&lt;/em&gt; baz&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9309:1-9311:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;em&gt;bar&lt;/em&gt; baz&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-417">Example 417</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7653:1-7655:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo_ bar_</span></code></pre>
+<pre data-sourcepos="9318:1-9320:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo_ bar_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7657:1-7659:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;&lt;em&gt;foo&lt;/em&gt; bar&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9322:1-9324:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;&lt;em&gt;foo&lt;/em&gt; bar&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-418">Example 418</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7662:1-7664:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo *bar**</span></code></pre>
+<pre data-sourcepos="9331:1-9333:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo *bar**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7666:1-7668:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;em&gt;bar&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9335:1-9337:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;em&gt;bar&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-419">Example 419</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7671:1-7673:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo **bar** baz*</span></code></pre>
+<pre data-sourcepos="9344:1-9346:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo **bar** baz*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7675:1-7677:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;strong&gt;bar&lt;/strong&gt; baz&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9348:1-9350:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;strong&gt;bar&lt;/strong&gt; baz&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-420">Example 420</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7679:1-7681:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo**bar**baz*</span></code></pre>
+<pre data-sourcepos="9356:1-9358:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo**bar**baz*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7683:1-7685:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;strong&gt;bar&lt;/strong&gt;baz&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9360:1-9362:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;strong&gt;bar&lt;/strong&gt;baz&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7687:1-7687:51" dir="auto">Note that in the preceding case, the interpretation</p>
+</div>
+<p data-sourcepos="9365:1-9365:51" dir="auto">Note that in the preceding case, the interpretation</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7689:1-7691:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="nt">&lt;p&gt;&lt;em&gt;</span>foo<span class="nt">&lt;/em&gt;&lt;em&gt;</span>bar<span class="nt">&lt;em&gt;&lt;/em&gt;</span>baz<span class="nt">&lt;/em&gt;&lt;/p&gt;</span></span></code></pre>
+<pre data-sourcepos="9367:1-9369:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown"><span class="nt">&lt;p&gt;&lt;em&gt;</span>foo<span class="nt">&lt;/em&gt;&lt;em&gt;</span>bar<span class="nt">&lt;em&gt;&lt;/em&gt;</span>baz<span class="nt">&lt;/em&gt;&lt;/p&gt;</span></span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7694:1-7699:32" dir="auto">is precluded by the condition that a delimiter that
+<p data-sourcepos="9372:1-9377:32" dir="auto">is precluded by the condition that a delimiter that
can both open and close (like the <code>*</code> after <code>foo</code>)
cannot form emphasis if the sum of the lengths of
the delimiter runs containing the opening and
closing delimiters is a multiple of 3 unless
both lengths are multiples of 3.</p>
-<p data-sourcepos="7702:1-7703:34" dir="auto">For the same reason, we don't get two consecutive
+<p data-sourcepos="9380:1-9381:34" dir="auto">For the same reason, we don't get two consecutive
emphasis sections in this example:</p>
+<div>
+<div><a href="#example-421">Example 421</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7705:1-7707:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo**bar*</span></code></pre>
+<pre data-sourcepos="9386:1-9388:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo**bar*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7709:1-7711:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo**bar&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9390:1-9392:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo**bar&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7714:1-7717:8" dir="auto">The same condition ensures that the following
+</div>
+<p data-sourcepos="9396:1-9399:8" dir="auto">The same condition ensures that the following
cases are all strong emphasis nested inside
emphasis, even when the interior spaces are
omitted:</p>
+<div>
+<div><a href="#example-422">Example 422</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7720:1-7722:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">***foo** bar*</span></code></pre>
+<pre data-sourcepos="9405:1-9407:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">***foo** bar*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7724:1-7726:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;&lt;strong&gt;foo&lt;/strong&gt; bar&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9409:1-9411:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;&lt;strong&gt;foo&lt;/strong&gt; bar&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-423">Example 423</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7729:1-7731:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo **bar***</span></code></pre>
+<pre data-sourcepos="9418:1-9420:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo **bar***</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7733:1-7735:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;strong&gt;bar&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9422:1-9424:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;strong&gt;bar&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-424">Example 424</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7738:1-7740:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo**bar***</span></code></pre>
+<pre data-sourcepos="9431:1-9433:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo**bar***</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7742:1-7744:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;strong&gt;bar&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9435:1-9437:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;strong&gt;bar&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7747:1-7749:34" dir="auto">When the lengths of the interior closing and opening
+</div>
+<p data-sourcepos="9441:1-9443:34" dir="auto">When the lengths of the interior closing and opening
delimiter runs are <em>both</em> multiples of 3, though,
they can match to create emphasis:</p>
+<div>
+<div><a href="#example-425">Example 425</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7751:1-7753:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo***bar***baz</span></code></pre>
+<pre data-sourcepos="9448:1-9450:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo***bar***baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7755:1-7757:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;em&gt;&lt;strong&gt;bar&lt;/strong&gt;&lt;/em&gt;baz&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9452:1-9454:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;em&gt;&lt;strong&gt;bar&lt;/strong&gt;&lt;/em&gt;baz&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-426">Example 426</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7759:1-7761:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo******bar*********baz</span></code></pre>
+<pre data-sourcepos="9460:1-9462:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo******bar*********baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7763:1-7765:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;bar&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;***baz&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9464:1-9466:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;bar&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;***baz&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7768:1-7768:42" dir="auto">Indefinite levels of nesting are possible:</p>
+</div>
+<p data-sourcepos="9470:1-9470:42" dir="auto">Indefinite levels of nesting are possible:</p>
+<div>
+<div><a href="#example-427">Example 427</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7770:1-7772:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo **bar *baz* bim** bop*</span></code></pre>
+<pre data-sourcepos="9475:1-9477:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo **bar *baz* bim** bop*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7774:1-7776:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;strong&gt;bar &lt;em&gt;baz&lt;/em&gt; bim&lt;/strong&gt; bop&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9479:1-9481:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;strong&gt;bar &lt;em&gt;baz&lt;/em&gt; bim&lt;/strong&gt; bop&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-428">Example 428</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7779:1-7781:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo [*bar*](/url)*</span></code></pre>
+<pre data-sourcepos="9488:1-9490:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo [*bar*](/url)*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7783:1-7785:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;a href="/url"&gt;&lt;em&gt;bar&lt;/em&gt;&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9492:1-9494:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;a href="/url"&gt;&lt;em&gt;bar&lt;/em&gt;&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7788:1-7788:50" dir="auto">There can be no empty emphasis or strong emphasis:</p>
+</div>
+<p data-sourcepos="9498:1-9498:50" dir="auto">There can be no empty emphasis or strong emphasis:</p>
+<div>
+<div><a href="#example-429">Example 429</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7790:1-7792:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">** is not an empty emphasis</span></code></pre>
+<pre data-sourcepos="9503:1-9505:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">** is not an empty emphasis</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7794:1-7796:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;** is not an empty emphasis&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9507:1-9509:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;** is not an empty emphasis&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-430">Example 430</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7799:1-7801:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**** is not an empty strong emphasis</span></code></pre>
+<pre data-sourcepos="9516:1-9518:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**** is not an empty strong emphasis</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7803:1-7805:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;**** is not an empty strong emphasis&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9520:1-9522:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;**** is not an empty strong emphasis&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7809:1-7809:8" dir="auto">Rule 10:</p>
-<p data-sourcepos="7811:1-7812:25" dir="auto">Any nonempty sequence of inline elements can be the contents of an
+</div>
+<p data-sourcepos="9527:1-9527:8" dir="auto">Rule 10:</p>
+<p data-sourcepos="9529:1-9530:25" dir="auto">Any nonempty sequence of inline elements can be the contents of an
strongly emphasized span.</p>
+<div>
+<div><a href="#example-431">Example 431</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7814:1-7816:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo [bar](/url)**</span></code></pre>
+<pre data-sourcepos="9535:1-9537:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo [bar](/url)**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7818:1-7820:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &lt;a href="/url"&gt;bar&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9539:1-9541:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &lt;a href="/url"&gt;bar&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-432">Example 432</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7823:1-7826:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo</span>
+<pre data-sourcepos="9548:1-9551:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo</span>
<span id="LC2" class="line" lang="plaintext">bar**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7828:1-7831:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo</span>
+<pre data-sourcepos="9553:1-9556:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo</span>
<span id="LC2" class="line" lang="plaintext">bar&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7834:1-7835:23" dir="auto">In particular, emphasis and strong emphasis can be nested
+</div>
+<p data-sourcepos="9560:1-9561:23" dir="auto">In particular, emphasis and strong emphasis can be nested
inside strong emphasis:</p>
+<div>
+<div><a href="#example-433">Example 433</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7837:1-7839:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo _bar_ baz__</span></code></pre>
+<pre data-sourcepos="9566:1-9568:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo _bar_ baz__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7841:1-7843:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &lt;em&gt;bar&lt;/em&gt; baz&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9570:1-9572:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &lt;em&gt;bar&lt;/em&gt; baz&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-434">Example 434</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7846:1-7848:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo __bar__ baz__</span></code></pre>
+<pre data-sourcepos="9579:1-9581:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo __bar__ baz__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7850:1-7852:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &lt;strong&gt;bar&lt;/strong&gt; baz&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9583:1-9585:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &lt;strong&gt;bar&lt;/strong&gt; baz&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-435">Example 435</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7855:1-7857:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">____foo__ bar__</span></code></pre>
+<pre data-sourcepos="9592:1-9594:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">____foo__ bar__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7859:1-7861:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;&lt;strong&gt;foo&lt;/strong&gt; bar&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9596:1-9598:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;&lt;strong&gt;foo&lt;/strong&gt; bar&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-436">Example 436</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7864:1-7866:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo **bar****</span></code></pre>
+<pre data-sourcepos="9605:1-9607:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo **bar****</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7868:1-7870:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &lt;strong&gt;bar&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9609:1-9611:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &lt;strong&gt;bar&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-437">Example 437</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7873:1-7875:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo *bar* baz**</span></code></pre>
+<pre data-sourcepos="9618:1-9620:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo *bar* baz**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7877:1-7879:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &lt;em&gt;bar&lt;/em&gt; baz&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9622:1-9624:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &lt;em&gt;bar&lt;/em&gt; baz&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-438">Example 438</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7882:1-7884:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo*bar*baz**</span></code></pre>
+<pre data-sourcepos="9631:1-9633:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo*bar*baz**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7886:1-7888:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo&lt;em&gt;bar&lt;/em&gt;baz&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9635:1-9637:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo&lt;em&gt;bar&lt;/em&gt;baz&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-439">Example 439</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7891:1-7893:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">***foo* bar**</span></code></pre>
+<pre data-sourcepos="9644:1-9646:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">***foo* bar**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7895:1-7897:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;&lt;em&gt;foo&lt;/em&gt; bar&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9648:1-9650:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;&lt;em&gt;foo&lt;/em&gt; bar&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-440">Example 440</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7900:1-7902:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo *bar***</span></code></pre>
+<pre data-sourcepos="9657:1-9659:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo *bar***</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7904:1-7906:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &lt;em&gt;bar&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9661:1-9663:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &lt;em&gt;bar&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7909:1-7909:42" dir="auto">Indefinite levels of nesting are possible:</p>
+</div>
+<p data-sourcepos="9667:1-9667:42" dir="auto">Indefinite levels of nesting are possible:</p>
+<div>
+<div><a href="#example-441">Example 441</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7911:1-7914:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo *bar **baz**</span>
+<pre data-sourcepos="9672:1-9675:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo *bar **baz**</span>
<span id="LC2" class="line" lang="plaintext">bim* bop**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7916:1-7919:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &lt;em&gt;bar &lt;strong&gt;baz&lt;/strong&gt;</span>
+<pre data-sourcepos="9677:1-9680:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &lt;em&gt;bar &lt;strong&gt;baz&lt;/strong&gt;</span>
<span id="LC2" class="line" lang="plaintext">bim&lt;/em&gt; bop&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-442">Example 442</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7922:1-7924:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo [*bar*](/url)**</span></code></pre>
+<pre data-sourcepos="9687:1-9689:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo [*bar*](/url)**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7926:1-7928:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &lt;a href="/url"&gt;&lt;em&gt;bar&lt;/em&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9691:1-9693:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo &lt;a href="/url"&gt;&lt;em&gt;bar&lt;/em&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7931:1-7931:50" dir="auto">There can be no empty emphasis or strong emphasis:</p>
+</div>
+<p data-sourcepos="9697:1-9697:50" dir="auto">There can be no empty emphasis or strong emphasis:</p>
+<div>
+<div><a href="#example-443">Example 443</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7933:1-7935:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__ is not an empty emphasis</span></code></pre>
+<pre data-sourcepos="9702:1-9704:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__ is not an empty emphasis</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7937:1-7939:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__ is not an empty emphasis&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9706:1-9708:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__ is not an empty emphasis&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-444">Example 444</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7942:1-7944:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">____ is not an empty strong emphasis</span></code></pre>
+<pre data-sourcepos="9715:1-9717:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">____ is not an empty strong emphasis</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7946:1-7948:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;____ is not an empty strong emphasis&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9719:1-9721:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;____ is not an empty strong emphasis&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="7952:1-7952:8" dir="auto">Rule 11:</p>
+</div>
+<p data-sourcepos="9726:1-9726:8" dir="auto">Rule 11:</p>
+<div>
+<div><a href="#example-445">Example 445</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7954:1-7956:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo ***</span></code></pre>
+<pre data-sourcepos="9731:1-9733:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo ***</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7958:1-7960:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo ***&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9735:1-9737:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo ***&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-446">Example 446</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7963:1-7965:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo *\**</span></code></pre>
+<pre data-sourcepos="9744:1-9746:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo *\**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7967:1-7969:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;em&gt;*&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9748:1-9750:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;em&gt;*&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-447">Example 447</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7972:1-7974:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo *_*</span></code></pre>
+<pre data-sourcepos="9757:1-9759:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo *_*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7976:1-7978:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;em&gt;_&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9761:1-9763:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;em&gt;_&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-448">Example 448</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7981:1-7983:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo *****</span></code></pre>
+<pre data-sourcepos="9770:1-9772:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo *****</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7985:1-7987:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo *****&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9774:1-9776:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo *****&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-449">Example 449</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7990:1-7992:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo **\***</span></code></pre>
+<pre data-sourcepos="9783:1-9785:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo **\***</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7994:1-7996:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;strong&gt;*&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9787:1-9789:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;strong&gt;*&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-450">Example 450</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="7999:1-8001:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo **_**</span></code></pre>
+<pre data-sourcepos="9796:1-9798:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo **_**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8003:1-8005:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;strong&gt;_&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9800:1-9802:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;strong&gt;_&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8008:1-8010:32" dir="auto">Note that when delimiters do not match evenly, Rule 11 determines
+</div>
+<p data-sourcepos="9806:1-9808:32" dir="auto">Note that when delimiters do not match evenly, Rule 11 determines
that the excess literal <code>*</code> characters will appear outside of the
emphasis, rather than inside it:</p>
+<div>
+<div><a href="#example-451">Example 451</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8012:1-8014:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo*</span></code></pre>
+<pre data-sourcepos="9813:1-9815:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8016:1-8018:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*&lt;em&gt;foo&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9817:1-9819:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*&lt;em&gt;foo&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-452">Example 452</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8021:1-8023:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo**</span></code></pre>
+<pre data-sourcepos="9826:1-9828:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8025:1-8027:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;/em&gt;*&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9830:1-9832:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;/em&gt;*&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-453">Example 453</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8030:1-8032:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">***foo**</span></code></pre>
+<pre data-sourcepos="9839:1-9841:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">***foo**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8034:1-8036:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*&lt;strong&gt;foo&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9843:1-9845:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*&lt;strong&gt;foo&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-454">Example 454</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8039:1-8041:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">****foo*</span></code></pre>
+<pre data-sourcepos="9852:1-9854:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">****foo*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8043:1-8045:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;***&lt;em&gt;foo&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9856:1-9858:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;***&lt;em&gt;foo&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-455">Example 455</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8048:1-8050:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo***</span></code></pre>
+<pre data-sourcepos="9865:1-9867:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo***</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8052:1-8054:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo&lt;/strong&gt;*&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9869:1-9871:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo&lt;/strong&gt;*&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-456">Example 456</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8057:1-8059:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo****</span></code></pre>
+<pre data-sourcepos="9878:1-9880:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo****</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8061:1-8063:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;/em&gt;***&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9882:1-9884:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;/em&gt;***&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8067:1-8067:8" dir="auto">Rule 12:</p>
+</div>
+<p data-sourcepos="9889:1-9889:8" dir="auto">Rule 12:</p>
+<div>
+<div><a href="#example-457">Example 457</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8069:1-8071:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo ___</span></code></pre>
+<pre data-sourcepos="9894:1-9896:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo ___</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8073:1-8075:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo ___&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9898:1-9900:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo ___&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-458">Example 458</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8078:1-8080:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo _\__</span></code></pre>
+<pre data-sourcepos="9907:1-9909:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo _\__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8082:1-8084:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;em&gt;_&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9911:1-9913:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;em&gt;_&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-459">Example 459</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8087:1-8089:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo _*_</span></code></pre>
+<pre data-sourcepos="9920:1-9922:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo _*_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8091:1-8093:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;em&gt;*&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9924:1-9926:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;em&gt;*&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-460">Example 460</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8096:1-8098:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo _____</span></code></pre>
+<pre data-sourcepos="9933:1-9935:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo _____</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8100:1-8102:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo _____&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9937:1-9939:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo _____&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-461">Example 461</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8105:1-8107:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo __\___</span></code></pre>
+<pre data-sourcepos="9946:1-9948:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo __\___</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8109:1-8111:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;strong&gt;_&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9950:1-9952:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;strong&gt;_&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-462">Example 462</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8114:1-8116:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo __*__</span></code></pre>
+<pre data-sourcepos="9959:1-9961:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo __*__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8118:1-8120:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;strong&gt;*&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9963:1-9965:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;strong&gt;*&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-463">Example 463</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8123:1-8125:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo_</span></code></pre>
+<pre data-sourcepos="9972:1-9974:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8127:1-8129:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_&lt;em&gt;foo&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9976:1-9978:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_&lt;em&gt;foo&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8132:1-8134:32" dir="auto">Note that when delimiters do not match evenly, Rule 12 determines
+</div>
+<p data-sourcepos="9982:1-9984:32" dir="auto">Note that when delimiters do not match evenly, Rule 12 determines
that the excess literal <code>_</code> characters will appear outside of the
emphasis, rather than inside it:</p>
+<div>
+<div><a href="#example-464">Example 464</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8136:1-8138:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo__</span></code></pre>
+<pre data-sourcepos="9989:1-9991:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8140:1-8142:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;/em&gt;_&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="9993:1-9995:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;/em&gt;_&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-465">Example 465</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8145:1-8147:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">___foo__</span></code></pre>
+<pre data-sourcepos="10002:1-10004:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">___foo__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8149:1-8151:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_&lt;strong&gt;foo&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10006:1-10008:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_&lt;strong&gt;foo&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-466">Example 466</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8154:1-8156:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">____foo_</span></code></pre>
+<pre data-sourcepos="10015:1-10017:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">____foo_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8158:1-8160:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;___&lt;em&gt;foo&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10019:1-10021:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;___&lt;em&gt;foo&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-467">Example 467</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8163:1-8165:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo___</span></code></pre>
+<pre data-sourcepos="10028:1-10030:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo___</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8167:1-8169:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo&lt;/strong&gt;_&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10032:1-10034:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo&lt;/strong&gt;_&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-468">Example 468</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8172:1-8174:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo____</span></code></pre>
+<pre data-sourcepos="10041:1-10043:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo____</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8176:1-8178:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;/em&gt;___&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10045:1-10047:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;/em&gt;___&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8181:1-8182:44" dir="auto">Rule 13 implies that if you want emphasis nested directly inside
+</div>
+<p data-sourcepos="10051:1-10052:44" dir="auto">Rule 13 implies that if you want emphasis nested directly inside
emphasis, you must use different delimiters:</p>
+<div>
+<div><a href="#example-469">Example 469</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8184:1-8186:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo**</span></code></pre>
+<pre data-sourcepos="10057:1-10059:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8188:1-8190:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10061:1-10063:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-470">Example 470</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8193:1-8195:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*_foo_*</span></code></pre>
+<pre data-sourcepos="10070:1-10072:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*_foo_*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8197:1-8199:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;&lt;em&gt;foo&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10074:1-10076:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;&lt;em&gt;foo&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-471">Example 471</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8202:1-8204:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo__</span></code></pre>
+<pre data-sourcepos="10083:1-10085:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__foo__</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8206:1-8208:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10087:1-10089:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;foo&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-472">Example 472</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8211:1-8213:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_*foo*_</span></code></pre>
+<pre data-sourcepos="10096:1-10098:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_*foo*_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8215:1-8217:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;&lt;em&gt;foo&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10100:1-10102:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;&lt;em&gt;foo&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8220:1-8221:21" dir="auto">However, strong emphasis within strong emphasis is possible without
+</div>
+<p data-sourcepos="10106:1-10107:21" dir="auto">However, strong emphasis within strong emphasis is possible without
switching delimiters:</p>
+<div>
+<div><a href="#example-473">Example 473</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8223:1-8225:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">****foo****</span></code></pre>
+<pre data-sourcepos="10112:1-10114:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">****foo****</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8227:1-8229:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;&lt;strong&gt;foo&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10116:1-10118:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;&lt;strong&gt;foo&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-474">Example 474</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8232:1-8234:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">____foo____</span></code></pre>
+<pre data-sourcepos="10125:1-10127:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">____foo____</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8236:1-8238:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;&lt;strong&gt;foo&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10129:1-10131:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;&lt;strong&gt;foo&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8242:1-8243:11" dir="auto">Rule 13 can be applied to arbitrarily long sequences of
+</div>
+<p data-sourcepos="10136:1-10137:11" dir="auto">Rule 13 can be applied to arbitrarily long sequences of
delimiters:</p>
+<div>
+<div><a href="#example-475">Example 475</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8245:1-8247:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">******foo******</span></code></pre>
+<pre data-sourcepos="10142:1-10144:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">******foo******</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8249:1-8251:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;foo&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10146:1-10148:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;foo&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8254:1-8254:8" dir="auto">Rule 14:</p>
+</div>
+<p data-sourcepos="10152:1-10152:8" dir="auto">Rule 14:</p>
+<div>
+<div><a href="#example-476">Example 476</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8256:1-8258:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">***foo***</span></code></pre>
+<pre data-sourcepos="10157:1-10159:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">***foo***</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8260:1-8262:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;&lt;strong&gt;foo&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10161:1-10163:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;&lt;strong&gt;foo&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-477">Example 477</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8265:1-8267:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_____foo_____</span></code></pre>
+<pre data-sourcepos="10170:1-10172:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_____foo_____</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8269:1-8271:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;strong&gt;foo&lt;/strong&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10174:1-10176:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;strong&gt;foo&lt;/strong&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8274:1-8274:8" dir="auto">Rule 15:</p>
+</div>
+<p data-sourcepos="10180:1-10180:8" dir="auto">Rule 15:</p>
+<div>
+<div><a href="#example-478">Example 478</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8276:1-8278:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo _bar* baz_</span></code></pre>
+<pre data-sourcepos="10185:1-10187:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo _bar* baz_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8280:1-8282:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo _bar&lt;/em&gt; baz_&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10189:1-10191:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo _bar&lt;/em&gt; baz_&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-479">Example 479</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8285:1-8287:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo __bar *baz bim__ bam*</span></code></pre>
+<pre data-sourcepos="10198:1-10200:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo __bar *baz bim__ bam*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8289:1-8291:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;strong&gt;bar *baz bim&lt;/strong&gt; bam&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10202:1-10204:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo &lt;strong&gt;bar *baz bim&lt;/strong&gt; bam&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8294:1-8294:8" dir="auto">Rule 16:</p>
+</div>
+<p data-sourcepos="10208:1-10208:8" dir="auto">Rule 16:</p>
+<div>
+<div><a href="#example-480">Example 480</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8296:1-8298:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo **bar baz**</span></code></pre>
+<pre data-sourcepos="10213:1-10215:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**foo **bar baz**</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8300:1-8302:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;**foo &lt;strong&gt;bar baz&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10217:1-10219:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;**foo &lt;strong&gt;bar baz&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-481">Example 481</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8305:1-8307:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo *bar baz*</span></code></pre>
+<pre data-sourcepos="10226:1-10228:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo *bar baz*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8309:1-8311:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*foo &lt;em&gt;bar baz&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10230:1-10232:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*foo &lt;em&gt;bar baz&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8314:1-8314:8" dir="auto">Rule 17:</p>
+</div>
+<p data-sourcepos="10236:1-10236:8" dir="auto">Rule 17:</p>
+<div>
+<div><a href="#example-482">Example 482</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8316:1-8318:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*[bar*](/url)</span></code></pre>
+<pre data-sourcepos="10241:1-10243:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*[bar*](/url)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8320:1-8322:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*&lt;a href="/url"&gt;bar*&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10245:1-10247:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*&lt;a href="/url"&gt;bar*&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-483">Example 483</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8325:1-8327:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo [bar_](/url)</span></code></pre>
+<pre data-sourcepos="10254:1-10256:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_foo [bar_](/url)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8329:1-8331:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_foo &lt;a href="/url"&gt;bar_&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10258:1-10260:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;_foo &lt;a href="/url"&gt;bar_&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-484">Example 484</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8334:1-8336:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*&lt;img src="foo" title="*"/&gt;</span></code></pre>
+<pre data-sourcepos="10267:1-10269:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*&lt;img src="foo" title="*"/&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8338:1-8340:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*&lt;img src="foo" title="*"/&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10271:1-10273:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*&lt;img src="foo" title="*"/&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-485">Example 485</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8343:1-8345:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**&lt;a href="**"&gt;</span></code></pre>
+<pre data-sourcepos="10280:1-10282:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**&lt;a href="**"&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8347:1-8349:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;**&lt;a href="**"&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10284:1-10286:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;**&lt;a href="**"&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-486">Example 486</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8352:1-8354:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__&lt;a href="__"&gt;</span></code></pre>
+<pre data-sourcepos="10293:1-10295:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__&lt;a href="__"&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8356:1-8358:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__&lt;a href="__"&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10297:1-10299:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__&lt;a href="__"&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-487">Example 487</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8361:1-8363:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*a `*`*</span></code></pre>
+<pre data-sourcepos="10306:1-10308:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*a `*`*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8365:1-8367:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;a &lt;code&gt;*&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10310:1-10312:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;a &lt;code&gt;*&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-488">Example 488</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8370:1-8372:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_a `_`_</span></code></pre>
+<pre data-sourcepos="10319:1-10321:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_a `_`_</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8374:1-8376:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;a &lt;code&gt;_&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10323:1-10325:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;a &lt;code&gt;_&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-489">Example 489</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8379:1-8381:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**a&lt;http://foo.bar/?q=**&gt;</span></code></pre>
+<pre data-sourcepos="10332:1-10334:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**a&lt;http://foo.bar/?q=**&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8383:1-8385:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;**a&lt;a href="http://foo.bar/?q=**"&gt;http://foo.bar/?q=**&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10336:1-10338:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;**a&lt;a href="http://foo.bar/?q=**"&gt;http://foo.bar/?q=**&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-490">Example 490</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8388:1-8390:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__a&lt;http://foo.bar/?q=__&gt;</span></code></pre>
+<pre data-sourcepos="10345:1-10347:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__a&lt;http://foo.bar/?q=__&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8392:1-8394:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__a&lt;a href="http://foo.bar/?q=__"&gt;http://foo.bar/?q=__&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10349:1-10351:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;__a&lt;a href="http://foo.bar/?q=__"&gt;http://foo.bar/?q=__&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
<div>
-<h2 data-sourcepos="8399:1-8399:28">
+<h2 data-sourcepos="10357:1-10357:28">
<a id="user-content-strikethrough-extension" class="anchor" href="#strikethrough-extension" aria-hidden="true"></a>Strikethrough (extension)</h2>
-<p data-sourcepos="8401:1-8402:10">GFM enables the <code>strikethrough</code> extension, where an additional emphasis type is
+<p data-sourcepos="10359:1-10360:10">GFM enables the <code>strikethrough</code> extension, where an additional emphasis type is
available.</p>
-<p data-sourcepos="8404:1-8404:59">Strikethrough text is any text wrapped in two tildes (<code>~</code>).</p>
+<p data-sourcepos="10362:1-10362:59">Strikethrough text is any text wrapped in two tildes (<code>~</code>).</p>
+<div>
+<div><a href="#example-491">Example 491</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8406:1-8408:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~Hi~~ Hello, world!</span></code></pre>
+<pre data-sourcepos="10367:1-10369:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~Hi~~ Hello, world!</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8410:1-8412:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;del&gt;Hi&lt;/del&gt; Hello, world!&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10371:1-10373:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;del&gt;Hi&lt;/del&gt; Hello, world!&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8414:1-8415:17">As with regular emphasis delimiters, a new paragraph will cause strikethrough
+</div>
+<p data-sourcepos="10376:1-10377:17">As with regular emphasis delimiters, a new paragraph will cause strikethrough
parsing to cease:</p>
+<div>
+<div><a href="#example-492">Example 492</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8417:1-8421:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This ~~has a</span>
+<pre data-sourcepos="10382:1-10386:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This ~~has a</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">new paragraph~~.</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8423:1-8426:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;This ~~has a&lt;/p&gt;</span>
+<pre data-sourcepos="10388:1-10391:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;This ~~has a&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;new paragraph~~.&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
</div>
-<h2 data-sourcepos="8430:1-8430:8" dir="auto">
+</div>
+<h2 data-sourcepos="10396:1-10396:8" dir="auto">
<a id="user-content-links" class="anchor" href="#links" aria-hidden="true"></a>Links</h2>
-<p data-sourcepos="8432:1-8437:13" dir="auto">A link contains [link text] (the visible text), a [link destination]
+<p data-sourcepos="10398:1-10403:13" dir="auto">A link contains [link text] (the visible text), a [link destination]
(the URI that is the link destination), and optionally a [link title].
There are two basic kinds of links in Markdown. In [inline links] the
destination and title are given immediately after the link text. In
[reference links] the destination and title are defined elsewhere in
the document.</p>
-<p data-sourcepos="8439:1-8441:22" dir="auto">A <a href="@">link text</a> consists of a sequence of zero or more
+<p data-sourcepos="10405:1-10407:22" dir="auto">A <a href="@">link text</a> consists of a sequence of zero or more
inline elements enclosed by square brackets (<code>[</code> and <code>]</code>). The
following rules apply:</p>
-<ul data-sourcepos="8443:1-8459:0" dir="auto">
-<li data-sourcepos="8443:1-8446:0">
-<p data-sourcepos="8443:3-8445:43">Links may not contain other links, at any level of nesting. If
+<ul data-sourcepos="10409:1-10425:0" dir="auto">
+<li data-sourcepos="10409:1-10412:0">
+<p data-sourcepos="10409:3-10411:43">Links may not contain other links, at any level of nesting. If
multiple otherwise valid link definitions appear nested inside each
other, the inner-most definition is used.</p>
</li>
-<li data-sourcepos="8447:1-8451:0">
-<p data-sourcepos="8447:3-8450:22">Brackets are allowed in the [link text] only if (a) they
+<li data-sourcepos="10413:1-10417:0">
+<p data-sourcepos="10413:3-10416:22">Brackets are allowed in the [link text] only if (a) they
are backslash-escaped or (b) they appear as a matched pair of brackets,
with an open bracket <code>[</code>, a sequence of zero or more inlines, and
a close bracket <code>]</code>.</p>
</li>
-<li data-sourcepos="8452:1-8456:0">
-<p data-sourcepos="8452:3-8455:25">Backtick [code spans], [autolinks], and raw [HTML tags] bind more tightly
+<li data-sourcepos="10418:1-10422:0">
+<p data-sourcepos="10418:3-10421:25">Backtick [code spans], [autolinks], and raw [HTML tags] bind more tightly
than the brackets in link text. Thus, for example,
<code>[foo`]`</code> could not be a link text, since the second <code>]</code>
is part of a code span.</p>
</li>
-<li data-sourcepos="8457:1-8459:0">
-<p data-sourcepos="8457:3-8458:78">The brackets in link text bind more tightly than markers for
+<li data-sourcepos="10423:1-10425:0">
+<p data-sourcepos="10423:3-10424:78">The brackets in link text bind more tightly than markers for
[emphasis and strong emphasis]. Thus, for example, <code>*[foo*](url)</code> is a link.</p>
</li>
</ul>
-<p data-sourcepos="8460:1-8460:42" dir="auto">A <a href="@">link destination</a> consists of either</p>
-<ul data-sourcepos="8462:1-8473:0" dir="auto">
-<li data-sourcepos="8462:1-8465:0">
-<p data-sourcepos="8462:3-8464:27">a sequence of zero or more characters between an opening <code>&lt;</code> and a
+<p data-sourcepos="10426:1-10426:42" dir="auto">A <a href="@">link destination</a> consists of either</p>
+<ul data-sourcepos="10428:1-10439:0" dir="auto">
+<li data-sourcepos="10428:1-10431:0">
+<p data-sourcepos="10428:3-10430:27">a sequence of zero or more characters between an opening <code>&lt;</code> and a
closing <code>&gt;</code> that contains no line breaks or unescaped
<code>&lt;</code> or <code>&gt;</code> characters, or</p>
</li>
-<li data-sourcepos="8466:1-8473:0">
-<p data-sourcepos="8466:3-8472:23">a nonempty sequence of characters that does not start with
+<li data-sourcepos="10432:1-10439:0">
+<p data-sourcepos="10432:3-10438:23">a nonempty sequence of characters that does not start with
<code>&lt;</code>, does not include ASCII space or control characters, and
includes parentheses only if (a) they are backslash-escaped or
(b) they are part of a balanced pair of unescaped parentheses.
@@ -7618,27 +9458,27 @@ avoid performance issues, but at least three levels of nesting
should be supported.)</p>
</li>
</ul>
-<p data-sourcepos="8474:1-8474:37" dir="auto">A <a href="@">link title</a> consists of either</p>
-<ul data-sourcepos="8476:1-8487:0" dir="auto">
-<li data-sourcepos="8476:1-8479:0">
-<p data-sourcepos="8476:3-8478:23">a sequence of zero or more characters between straight double-quote
+<p data-sourcepos="10440:1-10440:37" dir="auto">A <a href="@">link title</a> consists of either</p>
+<ul data-sourcepos="10442:1-10453:0" dir="auto">
+<li data-sourcepos="10442:1-10445:0">
+<p data-sourcepos="10442:3-10444:23">a sequence of zero or more characters between straight double-quote
characters (<code>"</code>), including a <code>"</code> character only if it is
backslash-escaped, or</p>
</li>
-<li data-sourcepos="8480:1-8483:0">
-<p data-sourcepos="8480:3-8482:23">a sequence of zero or more characters between straight single-quote
+<li data-sourcepos="10446:1-10449:0">
+<p data-sourcepos="10446:3-10448:23">a sequence of zero or more characters between straight single-quote
characters (<code>'</code>), including a <code>'</code> character only if it is
backslash-escaped, or</p>
</li>
-<li data-sourcepos="8484:1-8487:0">
-<p data-sourcepos="8484:3-8486:20">a sequence of zero or more characters between matching parentheses
+<li data-sourcepos="10450:1-10453:0">
+<p data-sourcepos="10450:3-10452:20">a sequence of zero or more characters between matching parentheses
(<code>(...)</code>), including a <code>(</code> or <code>)</code> character only if it is
backslash-escaped.</p>
</li>
</ul>
-<p data-sourcepos="8488:1-8489:15" dir="auto">Although [link titles] may span multiple lines, they may not contain
+<p data-sourcepos="10454:1-10455:15" dir="auto">Although [link titles] may span multiple lines, they may not contain
a [blank line].</p>
-<p data-sourcepos="8491:1-8501:6" dir="auto">An <a href="@">inline link</a> consists of a [link text] followed immediately
+<p data-sourcepos="10457:1-10467:6" dir="auto">An <a href="@">inline link</a> consists of a [link text] followed immediately
by a left parenthesis <code>(</code>, optional [whitespace], an optional
[link destination], an optional [link title] separated from the link
destination by [whitespace], optional [whitespace], and a right
@@ -7649,164 +9489,214 @@ The link's URI consists of the link destination, excluding enclosing
above. The link's title consists of the link title, excluding its
enclosing delimiters, with backslash-escapes in effect as described
above.</p>
-<p data-sourcepos="8503:1-8503:29" dir="auto">Here is a simple inline link:</p>
+<p data-sourcepos="10469:1-10469:29" dir="auto">Here is a simple inline link:</p>
+<div>
+<div><a href="#example-493">Example 493</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8505:1-8507:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/uri "title")</span></code></pre>
+<pre data-sourcepos="10474:1-10476:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/uri "title")</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8509:1-8511:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri" title="title"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10478:1-10480:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri" title="title"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8514:1-8514:25" dir="auto">The title may be omitted:</p>
+</div>
+<p data-sourcepos="10484:1-10484:25" dir="auto">The title may be omitted:</p>
+<div>
+<div><a href="#example-494">Example 494</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8516:1-8518:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/uri)</span></code></pre>
+<pre data-sourcepos="10489:1-10491:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/uri)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8520:1-8522:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10493:1-10495:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8525:1-8525:50" dir="auto">Both the title and the destination may be omitted:</p>
+</div>
+<p data-sourcepos="10499:1-10499:50" dir="auto">Both the title and the destination may be omitted:</p>
+<div>
+<div><a href="#example-495">Example 495</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8527:1-8529:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link]()</span></code></pre>
+<pre data-sourcepos="10504:1-10506:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link]()</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8531:1-8533:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href=""&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10508:1-10510:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href=""&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-496">Example 496</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8536:1-8538:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](&lt;&gt;)</span></code></pre>
+<pre data-sourcepos="10517:1-10519:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](&lt;&gt;)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8540:1-8542:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href=""&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10521:1-10523:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href=""&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8544:1-8545:28" dir="auto">The destination can only contain spaces if it is
+</div>
+<p data-sourcepos="10526:1-10527:28" dir="auto">The destination can only contain spaces if it is
enclosed in pointy brackets:</p>
+<div>
+<div><a href="#example-497">Example 497</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8547:1-8549:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/my uri)</span></code></pre>
+<pre data-sourcepos="10532:1-10534:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/my uri)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8551:1-8553:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[link](/my uri)&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10536:1-10538:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[link](/my uri)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-498">Example 498</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8555:1-8557:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](&lt;/my uri&gt;)</span></code></pre>
+<pre data-sourcepos="10544:1-10546:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](&lt;/my uri&gt;)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8559:1-8561:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/my%20uri"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10548:1-10550:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/my%20uri"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8563:1-8564:36" dir="auto">The destination cannot contain line breaks,
+</div>
+<p data-sourcepos="10553:1-10554:36" dir="auto">The destination cannot contain line breaks,
even if enclosed in pointy brackets:</p>
+<div>
+<div><a href="#example-499">Example 499</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8566:1-8569:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](foo</span>
+<pre data-sourcepos="10559:1-10562:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](foo</span>
<span id="LC2" class="line" lang="plaintext">bar)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8571:1-8574:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[link](foo</span>
+<pre data-sourcepos="10564:1-10567:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[link](foo</span>
<span id="LC2" class="line" lang="plaintext">bar)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-500">Example 500</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8576:1-8579:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](&lt;foo</span>
+<pre data-sourcepos="10573:1-10576:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](&lt;foo</span>
<span id="LC2" class="line" lang="plaintext">bar&gt;)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8581:1-8584:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[link](&lt;foo</span>
+<pre data-sourcepos="10578:1-10581:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[link](&lt;foo</span>
<span id="LC2" class="line" lang="plaintext">bar&gt;)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8586:1-8587:19" dir="auto">The destination can contain <code>)</code> if it is enclosed
+</div>
+<p data-sourcepos="10584:1-10585:19" dir="auto">The destination can contain <code>)</code> if it is enclosed
in pointy brackets:</p>
+<div>
+<div><a href="#example-501">Example 501</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8589:1-8591:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[a](&lt;b)c&gt;)</span></code></pre>
+<pre data-sourcepos="10590:1-10592:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[a](&lt;b)c&gt;)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8593:1-8595:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="b)c"&gt;a&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10594:1-10596:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="b)c"&gt;a&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8597:1-8597:53" dir="auto">Pointy brackets that enclose links must be unescaped:</p>
+</div>
+<p data-sourcepos="10599:1-10599:53" dir="auto">Pointy brackets that enclose links must be unescaped:</p>
+<div>
+<div><a href="#example-502">Example 502</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8599:1-8601:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](&lt;foo\&gt;)</span></code></pre>
+<pre data-sourcepos="10604:1-10606:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](&lt;foo\&gt;)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8603:1-8605:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[link](&amp;lt;foo&amp;gt;)&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10608:1-10610:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[link](&amp;lt;foo&amp;gt;)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8607:1-8608:24" dir="auto">These are not links, because the opening pointy bracket
+</div>
+<p data-sourcepos="10613:1-10614:24" dir="auto">These are not links, because the opening pointy bracket
is not matched properly:</p>
+<div>
+<div><a href="#example-503">Example 503</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8610:1-8614:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[a](&lt;b)c</span>
+<pre data-sourcepos="10619:1-10623:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[a](&lt;b)c</span>
<span id="LC2" class="line" lang="plaintext">[a](&lt;b)c&gt;</span>
<span id="LC3" class="line" lang="plaintext">[a](&lt;b&gt;c)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8616:1-8620:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[a](&amp;lt;b)c</span>
+<pre data-sourcepos="10625:1-10629:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[a](&amp;lt;b)c</span>
<span id="LC2" class="line" lang="plaintext">[a](&amp;lt;b)c&amp;gt;</span>
<span id="LC3" class="line" lang="plaintext">[a](&lt;b&gt;c)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8622:1-8622:55" dir="auto">Parentheses inside the link destination may be escaped:</p>
+</div>
+<p data-sourcepos="10632:1-10632:55" dir="auto">Parentheses inside the link destination may be escaped:</p>
+<div>
+<div><a href="#example-504">Example 504</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8624:1-8626:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](\(foo\))</span></code></pre>
+<pre data-sourcepos="10637:1-10639:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](\(foo\))</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8628:1-8630:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="(foo)"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10641:1-10643:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="(foo)"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8632:1-8633:9" dir="auto">Any number of parentheses are allowed without escaping, as long as they are
+</div>
+<p data-sourcepos="10646:1-10647:9" dir="auto">Any number of parentheses are allowed without escaping, as long as they are
balanced:</p>
+<div>
+<div><a href="#example-505">Example 505</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8635:1-8637:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](foo(and(bar)))</span></code></pre>
+<pre data-sourcepos="10652:1-10654:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](foo(and(bar)))</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8639:1-8641:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="foo(and(bar))"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10656:1-10658:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="foo(and(bar))"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8643:1-8644:13" dir="auto">However, if you have unbalanced parentheses, you need to escape or use the
+</div>
+<p data-sourcepos="10661:1-10662:13" dir="auto">However, if you have unbalanced parentheses, you need to escape or use the
<code>&lt;...&gt;</code> form:</p>
+<div>
+<div><a href="#example-506">Example 506</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8646:1-8648:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](foo\(and\(bar\))</span></code></pre>
+<pre data-sourcepos="10667:1-10669:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](foo\(and\(bar\))</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8650:1-8652:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="foo(and(bar)"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10671:1-10673:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="foo(and(bar)"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-507">Example 507</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8655:1-8657:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](&lt;foo(and(bar)&gt;)</span></code></pre>
+<pre data-sourcepos="10680:1-10682:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](&lt;foo(and(bar)&gt;)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8659:1-8661:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="foo(and(bar)"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10684:1-10686:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="foo(and(bar)"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8664:1-8665:12" dir="auto">Parentheses and other symbols can also be escaped, as usual
+</div>
+<p data-sourcepos="10690:1-10691:12" dir="auto">Parentheses and other symbols can also be escaped, as usual
in Markdown:</p>
+<div>
+<div><a href="#example-508">Example 508</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8667:1-8669:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](foo\)\:)</span></code></pre>
+<pre data-sourcepos="10696:1-10698:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](foo\)\:)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8671:1-8673:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="foo):"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10700:1-10702:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="foo):"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8676:1-8676:52" dir="auto">A link can contain fragment identifiers and queries:</p>
+</div>
+<p data-sourcepos="10706:1-10706:52" dir="auto">A link can contain fragment identifiers and queries:</p>
+<div>
+<div><a href="#example-509">Example 509</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8678:1-8684:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](#fragment)</span>
+<pre data-sourcepos="10711:1-10717:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](#fragment)</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[link](http://example.com#fragment)</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -7814,22 +9704,26 @@ in Markdown:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8686:1-8690:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="#fragment"&gt;link&lt;/a&gt;&lt;/p&gt;</span>
+<pre data-sourcepos="10719:1-10723:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="#fragment"&gt;link&lt;/a&gt;&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://example.com#fragment"&gt;link&lt;/a&gt;&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://example.com?foo=3#frag"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8693:1-8694:17" dir="auto">Note that a backslash before a non-escapable character is
+</div>
+<p data-sourcepos="10727:1-10728:17" dir="auto">Note that a backslash before a non-escapable character is
just a backslash:</p>
+<div>
+<div><a href="#example-510">Example 510</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8696:1-8698:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](foo\bar)</span></code></pre>
+<pre data-sourcepos="10733:1-10735:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](foo\bar)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8700:1-8702:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="foo%5Cbar"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10737:1-10739:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="foo%5Cbar"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8705:1-8712:52" dir="auto">URL-escaping should be left alone inside the destination, as all
+</div>
+<p data-sourcepos="10743:1-10750:52" dir="auto">URL-escaping should be left alone inside the destination, as all
URL-escaped characters are also valid URL characters. Entity and
numerical character references in the destination will be parsed
into the corresponding Unicode code points, as usual. These may
@@ -7837,77 +9731,98 @@ be optionally URL-escaped when written as HTML, but this spec
does not enforce any particular policy for rendering URLs in
HTML or other formats. Renderers may make different decisions
about how to escape or normalize URLs in the output.</p>
+<div>
+<div><a href="#example-511">Example 511</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8714:1-8716:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](foo%20b&amp;auml;)</span></code></pre>
+<pre data-sourcepos="10755:1-10757:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](foo%20b&amp;auml;)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8718:1-8720:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="foo%20b%C3%A4"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10759:1-10761:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="foo%20b%C3%A4"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8723:1-8725:23" dir="auto">Note that, because titles can often be parsed as destinations,
+</div>
+<p data-sourcepos="10765:1-10767:23" dir="auto">Note that, because titles can often be parsed as destinations,
if you try to omit the destination and keep the title, you'll
get unexpected results:</p>
+<div>
+<div><a href="#example-512">Example 512</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8727:1-8729:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link]("title")</span></code></pre>
+<pre data-sourcepos="10772:1-10774:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link]("title")</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8731:1-8733:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="%22title%22"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10776:1-10778:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="%22title%22"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8736:1-8736:62" dir="auto">Titles may be in single quotes, double quotes, or parentheses:</p>
+</div>
+<p data-sourcepos="10782:1-10782:62" dir="auto">Titles may be in single quotes, double quotes, or parentheses:</p>
+<div>
+<div><a href="#example-513">Example 513</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8738:1-8742:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/url "title")</span>
+<pre data-sourcepos="10787:1-10791:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/url "title")</span>
<span id="LC2" class="line" lang="plaintext">[link](/url 'title')</span>
<span id="LC3" class="line" lang="plaintext">[link](/url (title))</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8744:1-8748:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;link&lt;/a&gt;</span>
+<pre data-sourcepos="10793:1-10797:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;link&lt;/a&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;a href="/url" title="title"&gt;link&lt;/a&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;a href="/url" title="title"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8751:1-8752:22" dir="auto">Backslash escapes and entity and numeric character references
+</div>
+<p data-sourcepos="10801:1-10802:22" dir="auto">Backslash escapes and entity and numeric character references
may be used in titles:</p>
+<div>
+<div><a href="#example-514">Example 514</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8754:1-8756:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/url "title \"&amp;quot;")</span></code></pre>
+<pre data-sourcepos="10807:1-10809:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/url "title \"&amp;quot;")</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8758:1-8760:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title &amp;quot;&amp;quot;"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10811:1-10813:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title &amp;quot;&amp;quot;"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8763:1-8764:64" dir="auto">Titles must be separated from the link using a [whitespace].
+</div>
+<p data-sourcepos="10817:1-10818:64" dir="auto">Titles must be separated from the link using a [whitespace].
Other [Unicode whitespace] like non-breaking space doesn't work.</p>
+<div>
+<div><a href="#example-515">Example 515</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8766:1-8768:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/url "title")</span></code></pre>
+<pre data-sourcepos="10823:1-10825:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/url "title")</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8770:1-8772:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url%C2%A0%22title%22"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10827:1-10829:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url%C2%A0%22title%22"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8775:1-8775:56" dir="auto">Nested balanced quotes are not allowed without escaping:</p>
+</div>
+<p data-sourcepos="10833:1-10833:56" dir="auto">Nested balanced quotes are not allowed without escaping:</p>
+<div>
+<div><a href="#example-516">Example 516</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8777:1-8779:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/url "title "and" title")</span></code></pre>
+<pre data-sourcepos="10838:1-10840:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/url "title "and" title")</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8781:1-8783:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[link](/url &amp;quot;title &amp;quot;and&amp;quot; title&amp;quot;)&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10842:1-10844:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[link](/url &amp;quot;title &amp;quot;and&amp;quot; title&amp;quot;)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8786:1-8786:67" dir="auto">But it is easy to work around this by using a different quote type:</p>
+</div>
+<p data-sourcepos="10848:1-10848:67" dir="auto">But it is easy to work around this by using a different quote type:</p>
+<div>
+<div><a href="#example-517">Example 517</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8788:1-8790:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/url 'title "and" title')</span></code></pre>
+<pre data-sourcepos="10853:1-10855:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/url 'title "and" title')</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8792:1-8794:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title &amp;quot;and&amp;quot; title"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10857:1-10859:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title &amp;quot;and&amp;quot; title"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8797:1-8810:61" dir="auto">(Note: <code>Markdown.pl</code> did allow double quotes inside a double-quoted
+</div>
+<p data-sourcepos="10863:1-10876:61" dir="auto">(Note: <code>Markdown.pl</code> did allow double quotes inside a double-quoted
title, and its test suite included a test demonstrating this.
But it is hard to see a good rationale for the extra complexity this
brings, since there are already many ways---backslash escaping,
@@ -7921,170 +9836,221 @@ with <code>"</code> and end with <code>)</code>. <code>Markdown.pl</code> 1.0.1
titles with no closing quotation mark, though 1.0.2b8 does not.
It seems preferable to adopt a simple, rational rule that works
the same way in inline links and link reference definitions.)</p>
-<p data-sourcepos="8812:1-8812:57" dir="auto">[Whitespace] is allowed around the destination and title:</p>
+<p data-sourcepos="10878:1-10878:57" dir="auto">[Whitespace] is allowed around the destination and title:</p>
+<div>
+<div><a href="#example-518">Example 518</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8814:1-8817:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link]( /uri</span>
+<pre data-sourcepos="10883:1-10886:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link]( /uri</span>
<span id="LC2" class="line" lang="plaintext"> "title" )</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8819:1-8821:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri" title="title"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10888:1-10890:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri" title="title"&gt;link&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8824:1-8825:22" dir="auto">But it is not allowed between the link text and the
+</div>
+<p data-sourcepos="10894:1-10895:22" dir="auto">But it is not allowed between the link text and the
following parenthesis:</p>
+<div>
+<div><a href="#example-519">Example 519</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8827:1-8829:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link] (/uri)</span></code></pre>
+<pre data-sourcepos="10900:1-10902:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link] (/uri)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8831:1-8833:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[link] (/uri)&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10904:1-10906:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[link] (/uri)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8836:1-8837:24" dir="auto">The link text may contain balanced brackets, but not unbalanced ones,
+</div>
+<p data-sourcepos="10910:1-10911:24" dir="auto">The link text may contain balanced brackets, but not unbalanced ones,
unless they are escaped:</p>
+<div>
+<div><a href="#example-520">Example 520</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8839:1-8841:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link [foo [bar]]](/uri)</span></code></pre>
+<pre data-sourcepos="10916:1-10918:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link [foo [bar]]](/uri)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8843:1-8845:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;link [foo [bar]]&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10920:1-10922:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;link [foo [bar]]&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-521">Example 521</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8848:1-8850:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link] bar](/uri)</span></code></pre>
+<pre data-sourcepos="10929:1-10931:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link] bar](/uri)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8852:1-8854:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[link] bar](/uri)&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10933:1-10935:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[link] bar](/uri)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-522">Example 522</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8857:1-8859:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link [bar](/uri)</span></code></pre>
+<pre data-sourcepos="10942:1-10944:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link [bar](/uri)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8861:1-8863:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[link &lt;a href="/uri"&gt;bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10946:1-10948:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[link &lt;a href="/uri"&gt;bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-523">Example 523</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8866:1-8868:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link \[bar](/uri)</span></code></pre>
+<pre data-sourcepos="10955:1-10957:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link \[bar](/uri)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8870:1-8872:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;link [bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10959:1-10961:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;link [bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8875:1-8875:41" dir="auto">The link text may contain inline content:</p>
+</div>
+<p data-sourcepos="10965:1-10965:41" dir="auto">The link text may contain inline content:</p>
+<div>
+<div><a href="#example-524">Example 524</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8877:1-8879:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link *foo **bar** `#`*](/uri)</span></code></pre>
+<pre data-sourcepos="10970:1-10972:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link *foo **bar** `#`*](/uri)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8881:1-8883:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;link &lt;em&gt;foo &lt;strong&gt;bar&lt;/strong&gt; &lt;code&gt;#&lt;/code&gt;&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10974:1-10976:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;link &lt;em&gt;foo &lt;strong&gt;bar&lt;/strong&gt; &lt;code&gt;#&lt;/code&gt;&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-525">Example 525</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8886:1-8888:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[![moon](moon.jpg)](/uri)</span></code></pre>
+<pre data-sourcepos="10983:1-10985:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[![moon](moon.jpg)](/uri)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8890:1-8892:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;&lt;img src="moon.jpg" alt="moon" /&gt;&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="10987:1-10989:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;&lt;img src="moon.jpg" alt="moon" /&gt;&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8895:1-8895:68" dir="auto">However, links may not contain other links, at any level of nesting.</p>
+</div>
+<p data-sourcepos="10993:1-10993:68" dir="auto">However, links may not contain other links, at any level of nesting.</p>
+<div>
+<div><a href="#example-526">Example 526</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8897:1-8899:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo [bar](/uri)](/uri)</span></code></pre>
+<pre data-sourcepos="10998:1-11000:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo [bar](/uri)](/uri)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8901:1-8903:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo &lt;a href="/uri"&gt;bar&lt;/a&gt;](/uri)&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11002:1-11004:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo &lt;a href="/uri"&gt;bar&lt;/a&gt;](/uri)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-527">Example 527</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8906:1-8908:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo *[bar [baz](/uri)](/uri)*](/uri)</span></code></pre>
+<pre data-sourcepos="11011:1-11013:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo *[bar [baz](/uri)](/uri)*](/uri)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8910:1-8912:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo &lt;em&gt;[bar &lt;a href="/uri"&gt;baz&lt;/a&gt;](/uri)&lt;/em&gt;](/uri)&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11015:1-11017:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo &lt;em&gt;[bar &lt;a href="/uri"&gt;baz&lt;/a&gt;](/uri)&lt;/em&gt;](/uri)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-528">Example 528</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8915:1-8917:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![[[foo](uri1)](uri2)](uri3)</span></code></pre>
+<pre data-sourcepos="11024:1-11026:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![[[foo](uri1)](uri2)](uri3)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8919:1-8921:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="uri3" alt="[foo](uri2)" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11028:1-11030:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="uri3" alt="[foo](uri2)" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8924:1-8925:18" dir="auto">These cases illustrate the precedence of link text grouping over
+</div>
+<p data-sourcepos="11034:1-11035:18" dir="auto">These cases illustrate the precedence of link text grouping over
emphasis grouping:</p>
+<div>
+<div><a href="#example-529">Example 529</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8927:1-8929:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*[foo*](/uri)</span></code></pre>
+<pre data-sourcepos="11040:1-11042:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*[foo*](/uri)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8931:1-8933:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*&lt;a href="/uri"&gt;foo*&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11044:1-11046:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*&lt;a href="/uri"&gt;foo*&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-530">Example 530</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8936:1-8938:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo *bar](baz*)</span></code></pre>
+<pre data-sourcepos="11053:1-11055:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo *bar](baz*)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8940:1-8942:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="baz*"&gt;foo *bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11057:1-11059:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="baz*"&gt;foo *bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8945:1-8946:11" dir="auto">Note that brackets that <em>aren't</em> part of links do not take
+</div>
+<p data-sourcepos="11063:1-11064:11" dir="auto">Note that brackets that <em>aren't</em> part of links do not take
precedence:</p>
+<div>
+<div><a href="#example-531">Example 531</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8948:1-8950:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo [bar* baz]</span></code></pre>
+<pre data-sourcepos="11069:1-11071:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo [bar* baz]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8952:1-8954:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo [bar&lt;/em&gt; baz]&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11073:1-11075:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo [bar&lt;/em&gt; baz]&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8957:1-8958:33" dir="auto">These cases illustrate the precedence of HTML tags, code spans,
+</div>
+<p data-sourcepos="11079:1-11080:33" dir="auto">These cases illustrate the precedence of HTML tags, code spans,
and autolinks over link grouping:</p>
+<div>
+<div><a href="#example-532">Example 532</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8960:1-8962:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo &lt;bar attr="](baz)"&gt;</span></code></pre>
+<pre data-sourcepos="11085:1-11087:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo &lt;bar attr="](baz)"&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8964:1-8966:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo &lt;bar attr="](baz)"&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11089:1-11091:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo &lt;bar attr="](baz)"&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-533">Example 533</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8969:1-8971:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo`](/uri)`</span></code></pre>
+<pre data-sourcepos="11098:1-11100:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo`](/uri)`</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8973:1-8975:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo&lt;code&gt;](/uri)&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11102:1-11104:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo&lt;code&gt;](/uri)&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-534">Example 534</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8978:1-8980:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo&lt;http://example.com/?search=](uri)&gt;</span></code></pre>
+<pre data-sourcepos="11111:1-11113:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo&lt;http://example.com/?search=](uri)&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="8982:1-8984:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo&lt;a href="http://example.com/?search=%5D(uri)"&gt;http://example.com/?search=](uri)&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11115:1-11117:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo&lt;a href="http://example.com/?search=%5D(uri)"&gt;http://example.com/?search=](uri)&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="8987:1-8989:41" dir="auto">There are three kinds of <a href="@">reference link</a>s:
+</div>
+<p data-sourcepos="11121:1-11123:41" dir="auto">There are three kinds of <a href="@">reference link</a>s:
<a href="#full-reference-link">full</a>, <a href="#collapsed-reference-link">collapsed</a>,
and <a href="#shortcut-reference-link">shortcut</a>.</p>
-<p data-sourcepos="8991:1-8993:71" dir="auto">A <a href="@">full reference link</a>
+<p data-sourcepos="11125:1-11127:71" dir="auto">A <a href="@">full reference link</a>
consists of a [link text] immediately followed by a [link label]
that [matches] a [link reference definition] elsewhere in the document.</p>
-<p data-sourcepos="8995:1-9001:9" dir="auto">A <a href="@">link label</a> begins with a left bracket (<code>[</code>) and ends
+<p data-sourcepos="11129:1-11135:9" dir="auto">A <a href="@">link label</a> begins with a left bracket (<code>[</code>) and ends
with the first right bracket (<code>]</code>) that is not backslash-escaped.
Between these brackets there must be at least one [non-whitespace character].
Unescaped square bracket characters are not allowed inside the
opening and closing square brackets of [link labels]. A link
label can have at most 999 characters inside the square
brackets.</p>
-<p data-sourcepos="9003:1-9010:69" dir="auto">One label <a href="@">matches</a>
+<p data-sourcepos="11137:1-11144:69" dir="auto">One label <a href="@">matches</a>
another just in case their normalized forms are equal. To normalize a
label, strip off the opening and closing brackets,
perform the <em>Unicode case fold</em>, strip leading and trailing
@@ -8092,202 +10058,253 @@ perform the <em>Unicode case fold</em>, strip leading and trailing
[whitespace] to a single space. If there are multiple
matching reference link definitions, the one that comes first in the
document is used. (It is desirable in such cases to emit a warning.)</p>
-<p data-sourcepos="9012:1-9014:37" dir="auto">The contents of the first link label are parsed as inlines, which are
+<p data-sourcepos="11146:1-11148:37" dir="auto">The contents of the first link label are parsed as inlines, which are
used as the link's text. The link's URI and title are provided by the
matching [link reference definition].</p>
-<p data-sourcepos="9016:1-9016:25" dir="auto">Here is a simple example:</p>
+<p data-sourcepos="11150:1-11150:25" dir="auto">Here is a simple example:</p>
+<div>
+<div><a href="#example-535">Example 535</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9018:1-9022:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][bar]</span>
+<pre data-sourcepos="11155:1-11159:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][bar]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[bar]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9024:1-9026:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11161:1-11163:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9029:1-9030:22" dir="auto">The rules for the [link text] are the same as with
+</div>
+<p data-sourcepos="11167:1-11168:22" dir="auto">The rules for the [link text] are the same as with
[inline links]. Thus:</p>
-<p data-sourcepos="9032:1-9033:24" dir="auto">The link text may contain balanced brackets, but not unbalanced ones,
+<p data-sourcepos="11170:1-11171:24" dir="auto">The link text may contain balanced brackets, but not unbalanced ones,
unless they are escaped:</p>
+<div>
+<div><a href="#example-536">Example 536</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9035:1-9039:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link [foo [bar]]][ref]</span>
+<pre data-sourcepos="11176:1-11180:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link [foo [bar]]][ref]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[ref]: /uri</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9041:1-9043:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;link [foo [bar]]&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11182:1-11184:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;link [foo [bar]]&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-537">Example 537</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9046:1-9050:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link \[bar][ref]</span>
+<pre data-sourcepos="11191:1-11195:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link \[bar][ref]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[ref]: /uri</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9052:1-9054:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;link [bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11197:1-11199:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;link [bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9057:1-9057:41" dir="auto">The link text may contain inline content:</p>
+</div>
+<p data-sourcepos="11203:1-11203:41" dir="auto">The link text may contain inline content:</p>
+<div>
+<div><a href="#example-538">Example 538</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9059:1-9063:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link *foo **bar** `#`*][ref]</span>
+<pre data-sourcepos="11208:1-11212:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link *foo **bar** `#`*][ref]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[ref]: /uri</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9065:1-9067:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;link &lt;em&gt;foo &lt;strong&gt;bar&lt;/strong&gt; &lt;code&gt;#&lt;/code&gt;&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11214:1-11216:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;link &lt;em&gt;foo &lt;strong&gt;bar&lt;/strong&gt; &lt;code&gt;#&lt;/code&gt;&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-539">Example 539</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9070:1-9074:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[![moon](moon.jpg)][ref]</span>
+<pre data-sourcepos="11223:1-11227:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[![moon](moon.jpg)][ref]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[ref]: /uri</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9076:1-9078:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;&lt;img src="moon.jpg" alt="moon" /&gt;&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11229:1-11231:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;&lt;img src="moon.jpg" alt="moon" /&gt;&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9081:1-9081:68" dir="auto">However, links may not contain other links, at any level of nesting.</p>
+</div>
+<p data-sourcepos="11235:1-11235:68" dir="auto">However, links may not contain other links, at any level of nesting.</p>
+<div>
+<div><a href="#example-540">Example 540</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9083:1-9087:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo [bar](/uri)][ref]</span>
+<pre data-sourcepos="11240:1-11244:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo [bar](/uri)][ref]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[ref]: /uri</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9089:1-9091:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo &lt;a href="/uri"&gt;bar&lt;/a&gt;]&lt;a href="/uri"&gt;ref&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11246:1-11248:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo &lt;a href="/uri"&gt;bar&lt;/a&gt;]&lt;a href="/uri"&gt;ref&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-541">Example 541</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9094:1-9098:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo *bar [baz][ref]*][ref]</span>
+<pre data-sourcepos="11255:1-11259:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo *bar [baz][ref]*][ref]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[ref]: /uri</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9100:1-9102:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo &lt;em&gt;bar &lt;a href="/uri"&gt;baz&lt;/a&gt;&lt;/em&gt;]&lt;a href="/uri"&gt;ref&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11261:1-11263:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo &lt;em&gt;bar &lt;a href="/uri"&gt;baz&lt;/a&gt;&lt;/em&gt;]&lt;a href="/uri"&gt;ref&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9105:1-9106:38" dir="auto">(In the examples above, we have two [shortcut reference links]
+</div>
+<p data-sourcepos="11267:1-11268:38" dir="auto">(In the examples above, we have two [shortcut reference links]
instead of one [full reference link].)</p>
-<p data-sourcepos="9108:1-9109:18" dir="auto">The following cases illustrate the precedence of link text grouping over
+<p data-sourcepos="11270:1-11271:18" dir="auto">The following cases illustrate the precedence of link text grouping over
emphasis grouping:</p>
+<div>
+<div><a href="#example-542">Example 542</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9111:1-9115:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*[foo*][ref]</span>
+<pre data-sourcepos="11276:1-11280:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*[foo*][ref]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[ref]: /uri</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9117:1-9119:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*&lt;a href="/uri"&gt;foo*&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11282:1-11284:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*&lt;a href="/uri"&gt;foo*&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-543">Example 543</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9122:1-9126:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo *bar][ref]</span>
+<pre data-sourcepos="11291:1-11295:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo *bar][ref]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[ref]: /uri</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9128:1-9130:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;foo *bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11297:1-11299:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;foo *bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9133:1-9134:33" dir="auto">These cases illustrate the precedence of HTML tags, code spans,
+</div>
+<p data-sourcepos="11303:1-11304:33" dir="auto">These cases illustrate the precedence of HTML tags, code spans,
and autolinks over link grouping:</p>
+<div>
+<div><a href="#example-544">Example 544</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9136:1-9140:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo &lt;bar attr="][ref]"&gt;</span>
+<pre data-sourcepos="11309:1-11313:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo &lt;bar attr="][ref]"&gt;</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[ref]: /uri</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9142:1-9144:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo &lt;bar attr="][ref]"&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11315:1-11317:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo &lt;bar attr="][ref]"&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-545">Example 545</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9147:1-9151:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo`][ref]`</span>
+<pre data-sourcepos="11324:1-11328:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo`][ref]`</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[ref]: /uri</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9153:1-9155:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo&lt;code&gt;][ref]&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11330:1-11332:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo&lt;code&gt;][ref]&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-546">Example 546</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9158:1-9162:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo&lt;http://example.com/?search=][ref]&gt;</span>
+<pre data-sourcepos="11339:1-11343:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo&lt;http://example.com/?search=][ref]&gt;</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[ref]: /uri</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9164:1-9166:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo&lt;a href="http://example.com/?search=%5D%5Bref%5D"&gt;http://example.com/?search=][ref]&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11345:1-11347:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo&lt;a href="http://example.com/?search=%5D%5Bref%5D"&gt;http://example.com/?search=][ref]&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9169:1-9169:29" dir="auto">Matching is case-insensitive:</p>
+</div>
+<p data-sourcepos="11351:1-11351:29" dir="auto">Matching is case-insensitive:</p>
+<div>
+<div><a href="#example-547">Example 547</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9171:1-9175:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][BaR]</span>
+<pre data-sourcepos="11356:1-11360:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][BaR]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[bar]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9177:1-9179:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11362:1-11364:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9182:1-9182:26" dir="auto">Unicode case fold is used:</p>
+</div>
+<p data-sourcepos="11368:1-11368:26" dir="auto">Unicode case fold is used:</p>
+<div>
+<div><a href="#example-548">Example 548</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9184:1-9188:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[Толпой][Толпой] is a Russian word.</span>
+<pre data-sourcepos="11373:1-11377:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[Толпой][Толпой] is a Russian word.</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[ТОЛПОЙ]: /url</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9190:1-9192:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url"&gt;Толпой&lt;/a&gt; is a Russian word.&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11379:1-11381:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url"&gt;Толпой&lt;/a&gt; is a Russian word.&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9195:1-9196:33" dir="auto">Consecutive internal [whitespace] is treated as one space for
+</div>
+<p data-sourcepos="11385:1-11386:33" dir="auto">Consecutive internal [whitespace] is treated as one space for
purposes of determining matching:</p>
+<div>
+<div><a href="#example-549">Example 549</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9198:1-9203:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[Foo</span>
+<pre data-sourcepos="11391:1-11396:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[Foo</span>
<span id="LC2" class="line" lang="plaintext"> bar]: /url</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">[Baz][Foo bar]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9205:1-9207:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url"&gt;Baz&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11398:1-11400:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url"&gt;Baz&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9210:1-9211:13" dir="auto">No [whitespace] is allowed between the [link text] and the
+</div>
+<p data-sourcepos="11404:1-11405:13" dir="auto">No [whitespace] is allowed between the [link text] and the
[link label]:</p>
+<div>
+<div><a href="#example-550">Example 550</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9213:1-9217:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo] [bar]</span>
+<pre data-sourcepos="11410:1-11414:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo] [bar]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[bar]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9219:1-9221:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo] &lt;a href="/url" title="title"&gt;bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11416:1-11418:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo] &lt;a href="/url" title="title"&gt;bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-551">Example 551</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9224:1-9229:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]</span>
+<pre data-sourcepos="11425:1-11430:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]</span>
<span id="LC2" class="line" lang="plaintext">[bar]</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">[bar]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9231:1-9234:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo]</span>
+<pre data-sourcepos="11432:1-11435:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo]</span>
<span id="LC2" class="line" lang="plaintext">&lt;a href="/url" title="title"&gt;bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9237:1-9246:9" dir="auto">This is a departure from John Gruber's original Markdown syntax
+</div>
+<p data-sourcepos="11439:1-11448:9" dir="auto">This is a departure from John Gruber's original Markdown syntax
description, which explicitly allows whitespace between the link
text and the link label. It brings reference links in line with
[inline links], which (according to both original Markdown and
@@ -8298,24 +10315,26 @@ link text and the link label, then in the following we will have
a single reference link, not two shortcut reference links, as
intended:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9248:1-9254:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown">[foo]</span>
+<pre data-sourcepos="11450:1-11456:3" lang="markdown" class="code highlight js-syntax-highlight language-markdown" v-pre="true"><code><span id="LC1" class="line" lang="markdown">[foo]</span>
<span id="LC2" class="line" lang="markdown">[bar]</span>
<span id="LC3" class="line" lang="markdown"></span>
<span id="LC4" class="line" lang="markdown"><span class="p">[</span><span class="ss">foo</span><span class="p">]:</span> <span class="sx">/url1</span></span>
<span id="LC5" class="line" lang="markdown"><span class="p">[</span><span class="ss">bar</span><span class="p">]:</span> <span class="sx">/url2</span></span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9256:1-9262:20" dir="auto">(Note that [shortcut reference links] were introduced by Gruber
+<p data-sourcepos="11458:1-11464:20" dir="auto">(Note that [shortcut reference links] were introduced by Gruber
himself in a beta version of <code>Markdown.pl</code>, but never included
in the official syntax description. Without shortcut reference
links, it is harmless to allow space between the link text and
link label; but once shortcut references are introduced, it is
too dangerous to allow this, as it frequently leads to
unintended results.)</p>
-<p data-sourcepos="9264:1-9265:18" dir="auto">When there are multiple matching [link reference definitions],
+<p data-sourcepos="11466:1-11467:18" dir="auto">When there are multiple matching [link reference definitions],
the first is used:</p>
+<div>
+<div><a href="#example-552">Example 552</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9267:1-9273:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url1</span>
+<pre data-sourcepos="11472:1-11478:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]: /url1</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url2</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -8323,92 +10342,116 @@ the first is used:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9275:1-9277:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url1"&gt;bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11480:1-11482:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url1"&gt;bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9280:1-9282:40" dir="auto">Note that matching is performed on normalized strings, not parsed
+</div>
+<p data-sourcepos="11486:1-11488:40" dir="auto">Note that matching is performed on normalized strings, not parsed
inline content. So the following does not match, even though the
labels define equivalent inline content:</p>
+<div>
+<div><a href="#example-553">Example 553</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9284:1-9288:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[bar][foo\!]</span>
+<pre data-sourcepos="11493:1-11497:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[bar][foo\!]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo!]: /url</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9290:1-9292:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[bar][foo!]&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11499:1-11501:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[bar][foo!]&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9295:1-9296:18" dir="auto">[Link labels] cannot contain brackets, unless they are
+</div>
+<p data-sourcepos="11505:1-11506:18" dir="auto">[Link labels] cannot contain brackets, unless they are
backslash-escaped:</p>
+<div>
+<div><a href="#example-554">Example 554</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9298:1-9302:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][ref[]</span>
+<pre data-sourcepos="11511:1-11515:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][ref[]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[ref[]: /uri</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9304:1-9307:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo][ref[]&lt;/p&gt;</span>
+<pre data-sourcepos="11517:1-11520:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo][ref[]&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;[ref[]: /uri&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-555">Example 555</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9310:1-9314:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][ref[bar]]</span>
+<pre data-sourcepos="11527:1-11531:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][ref[bar]]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[ref[bar]]: /uri</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9316:1-9319:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo][ref[bar]]&lt;/p&gt;</span>
+<pre data-sourcepos="11533:1-11536:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo][ref[bar]]&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;[ref[bar]]: /uri&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-556">Example 556</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9322:1-9326:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[[foo]]]</span>
+<pre data-sourcepos="11543:1-11547:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[[foo]]]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[[[foo]]]: /url</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9328:1-9331:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[[[foo]]]&lt;/p&gt;</span>
+<pre data-sourcepos="11549:1-11552:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[[[foo]]]&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;[[[foo]]]: /url&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-557">Example 557</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9334:1-9338:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][ref\[]</span>
+<pre data-sourcepos="11559:1-11563:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][ref\[]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[ref\[]: /uri</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9340:1-9342:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11565:1-11567:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9345:1-9345:55" dir="auto">Note that in this example <code>]</code> is not backslash-escaped:</p>
+</div>
+<p data-sourcepos="11571:1-11571:55" dir="auto">Note that in this example <code>]</code> is not backslash-escaped:</p>
+<div>
+<div><a href="#example-558">Example 558</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9347:1-9351:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[bar\\]: /uri</span>
+<pre data-sourcepos="11576:1-11580:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[bar\\]: /uri</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[bar\\]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9353:1-9355:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;bar\&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11582:1-11584:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/uri"&gt;bar\&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9358:1-9358:68" dir="auto">A [link label] must contain at least one [non-whitespace character]:</p>
+</div>
+<p data-sourcepos="11588:1-11588:68" dir="auto">A [link label] must contain at least one [non-whitespace character]:</p>
+<div>
+<div><a href="#example-559">Example 559</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9360:1-9364:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[]</span>
+<pre data-sourcepos="11593:1-11597:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[]: /uri</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9366:1-9369:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[]&lt;/p&gt;</span>
+<pre data-sourcepos="11599:1-11602:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[]&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;[]: /uri&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-560">Example 560</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9372:1-9378:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[</span>
+<pre data-sourcepos="11609:1-11615:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[</span>
<span id="LC2" class="line" lang="plaintext"> ]</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">[</span>
@@ -8416,13 +10459,14 @@ backslash-escaped:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9380:1-9385:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[</span>
+<pre data-sourcepos="11617:1-11622:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[</span>
<span id="LC2" class="line" lang="plaintext">]&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;[</span>
<span id="LC4" class="line" lang="plaintext">]: /uri&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9388:1-9395:40" dir="auto">A <a href="@">collapsed reference link</a>
+</div>
+<p data-sourcepos="11626:1-11633:40" dir="auto">A <a href="@">collapsed reference link</a>
consists of a [link label] that [matches] a
[link reference definition] elsewhere in the
document, followed by the string <code>[]</code>.
@@ -8430,52 +10474,64 @@ The contents of the first link label are parsed as inlines,
which are used as the link's text. The link's URI and title are
provided by the matching reference link definition. Thus,
<code>[foo][]</code> is equivalent to <code>[foo][foo]</code>.</p>
+<div>
+<div><a href="#example-561">Example 561</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9397:1-9401:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][]</span>
+<pre data-sourcepos="11638:1-11642:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9403:1-9405:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11644:1-11646:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-562">Example 562</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9408:1-9412:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[*foo* bar][]</span>
+<pre data-sourcepos="11653:1-11657:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[*foo* bar][]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[*foo* bar]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9414:1-9416:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;&lt;em&gt;foo&lt;/em&gt; bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11659:1-11661:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;&lt;em&gt;foo&lt;/em&gt; bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9419:1-9419:37" dir="auto">The link labels are case-insensitive:</p>
+</div>
+<p data-sourcepos="11665:1-11665:37" dir="auto">The link labels are case-insensitive:</p>
+<div>
+<div><a href="#example-563">Example 563</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9421:1-9425:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[Foo][]</span>
+<pre data-sourcepos="11670:1-11674:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[Foo][]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9427:1-9429:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;Foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11676:1-11678:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;Foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9433:1-9434:41" dir="auto">As with full reference links, [whitespace] is not
+</div>
+<p data-sourcepos="11683:1-11684:41" dir="auto">As with full reference links, [whitespace] is not
allowed between the two sets of brackets:</p>
+<div>
+<div><a href="#example-564">Example 564</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9436:1-9441:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo] </span>
+<pre data-sourcepos="11689:1-11694:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo] </span>
<span id="LC2" class="line" lang="plaintext">[]</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">[foo]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9443:1-9446:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;foo&lt;/a&gt;</span>
+<pre data-sourcepos="11696:1-11699:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;foo&lt;/a&gt;</span>
<span id="LC2" class="line" lang="plaintext">[]&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9449:1-9456:41" dir="auto">A <a href="@">shortcut reference link</a>
+</div>
+<p data-sourcepos="11703:1-11710:41" dir="auto">A <a href="@">shortcut reference link</a>
consists of a [link label] that [matches] a
[link reference definition] elsewhere in the
document and is not followed by <code>[]</code> or a link label.
@@ -8483,177 +10539,222 @@ The contents of the first link label are parsed as inlines,
which are used as the link's text. The link's URI and title
are provided by the matching link reference definition.
Thus, <code>[foo]</code> is equivalent to <code>[foo][]</code>.</p>
+<div>
+<div><a href="#example-565">Example 565</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9458:1-9462:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]</span>
+<pre data-sourcepos="11715:1-11719:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9464:1-9466:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11721:1-11723:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-566">Example 566</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9469:1-9473:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[*foo* bar]</span>
+<pre data-sourcepos="11730:1-11734:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[*foo* bar]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[*foo* bar]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9475:1-9477:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;&lt;em&gt;foo&lt;/em&gt; bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11736:1-11738:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;&lt;em&gt;foo&lt;/em&gt; bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-567">Example 567</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9480:1-9484:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[*foo* bar]]</span>
+<pre data-sourcepos="11745:1-11749:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[*foo* bar]]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[*foo* bar]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9486:1-9488:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[&lt;a href="/url" title="title"&gt;&lt;em&gt;foo&lt;/em&gt; bar&lt;/a&gt;]&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11751:1-11753:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[&lt;a href="/url" title="title"&gt;&lt;em&gt;foo&lt;/em&gt; bar&lt;/a&gt;]&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-568">Example 568</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9491:1-9495:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[bar [foo]</span>
+<pre data-sourcepos="11760:1-11764:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[bar [foo]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9497:1-9499:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[[bar &lt;a href="/url"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11766:1-11768:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[[bar &lt;a href="/url"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9502:1-9502:37" dir="auto">The link labels are case-insensitive:</p>
+</div>
+<p data-sourcepos="11772:1-11772:37" dir="auto">The link labels are case-insensitive:</p>
+<div>
+<div><a href="#example-569">Example 569</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9504:1-9508:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[Foo]</span>
+<pre data-sourcepos="11777:1-11781:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[Foo]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9510:1-9512:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;Foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11783:1-11785:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url" title="title"&gt;Foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9515:1-9515:48" dir="auto">A space after the link text should be preserved:</p>
+</div>
+<p data-sourcepos="11789:1-11789:48" dir="auto">A space after the link text should be preserved:</p>
+<div>
+<div><a href="#example-570">Example 570</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9517:1-9521:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo] bar</span>
+<pre data-sourcepos="11794:1-11798:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo] bar</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9523:1-9525:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url"&gt;foo&lt;/a&gt; bar&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11800:1-11802:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url"&gt;foo&lt;/a&gt; bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9528:1-9529:31" dir="auto">If you just want bracketed text, you can backslash-escape the
+</div>
+<p data-sourcepos="11806:1-11807:31" dir="auto">If you just want bracketed text, you can backslash-escape the
opening bracket to avoid links:</p>
+<div>
+<div><a href="#example-571">Example 571</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9531:1-9535:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">\[foo]</span>
+<pre data-sourcepos="11812:1-11816:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">\[foo]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9537:1-9539:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo]&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11818:1-11820:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo]&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9542:1-9543:26" dir="auto">Note that this is a link, because a link label ends with the first
+</div>
+<p data-sourcepos="11824:1-11825:26" dir="auto">Note that this is a link, because a link label ends with the first
following closing bracket:</p>
+<div>
+<div><a href="#example-572">Example 572</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9545:1-9549:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo*]: /url</span>
+<pre data-sourcepos="11830:1-11834:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo*]: /url</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">*[foo*]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9551:1-9553:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*&lt;a href="/url"&gt;foo*&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11836:1-11838:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;*&lt;a href="/url"&gt;foo*&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9556:1-9557:11" dir="auto">Full and compact references take precedence over shortcut
+</div>
+<p data-sourcepos="11842:1-11843:11" dir="auto">Full and compact references take precedence over shortcut
references:</p>
+<div>
+<div><a href="#example-573">Example 573</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9559:1-9564:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][bar]</span>
+<pre data-sourcepos="11848:1-11853:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][bar]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url1</span>
<span id="LC4" class="line" lang="plaintext">[bar]: /url2</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9566:1-9568:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url2"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11855:1-11857:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url2"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-574">Example 574</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9570:1-9574:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][]</span>
+<pre data-sourcepos="11863:1-11867:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url1</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9576:1-9578:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url1"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11869:1-11871:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url1"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9580:1-9580:34" dir="auto">Inline links also take precedence:</p>
+</div>
+<p data-sourcepos="11874:1-11874:34" dir="auto">Inline links also take precedence:</p>
+<div>
+<div><a href="#example-575">Example 575</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9582:1-9586:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]()</span>
+<pre data-sourcepos="11879:1-11883:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo]()</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url1</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9588:1-9590:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href=""&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11885:1-11887:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href=""&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-576">Example 576</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9592:1-9596:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo](not a link)</span>
+<pre data-sourcepos="11893:1-11897:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo](not a link)</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url1</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9598:1-9600:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url1"&gt;foo&lt;/a&gt;(not a link)&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11899:1-11901:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url1"&gt;foo&lt;/a&gt;(not a link)&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9602:1-9603:23" dir="auto">In the following case <code>[bar][baz]</code> is parsed as a reference,
+</div>
+<p data-sourcepos="11904:1-11905:23" dir="auto">In the following case <code>[bar][baz]</code> is parsed as a reference,
<code>[foo]</code> as normal text:</p>
+<div>
+<div><a href="#example-577">Example 577</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9605:1-9609:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][bar][baz]</span>
+<pre data-sourcepos="11910:1-11914:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][bar][baz]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[baz]: /url</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9611:1-9613:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo]&lt;a href="/url"&gt;bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11916:1-11918:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo]&lt;a href="/url"&gt;bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9616:1-9617:19" dir="auto">Here, though, <code>[foo][bar]</code> is parsed as a reference, since
+</div>
+<p data-sourcepos="11922:1-11923:19" dir="auto">Here, though, <code>[foo][bar]</code> is parsed as a reference, since
<code>[bar]</code> is defined:</p>
+<div>
+<div><a href="#example-578">Example 578</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9619:1-9624:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][bar][baz]</span>
+<pre data-sourcepos="11928:1-11933:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][bar][baz]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[baz]: /url1</span>
<span id="LC4" class="line" lang="plaintext">[bar]: /url2</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9626:1-9628:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url2"&gt;foo&lt;/a&gt;&lt;a href="/url1"&gt;baz&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11935:1-11937:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="/url2"&gt;foo&lt;/a&gt;&lt;a href="/url1"&gt;baz&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9631:1-9632:65" dir="auto">Here <code>[foo]</code> is not parsed as a shortcut reference, because it
+</div>
+<p data-sourcepos="11941:1-11942:65" dir="auto">Here <code>[foo]</code> is not parsed as a shortcut reference, because it
is followed by a link label (even though <code>[bar]</code> is not defined):</p>
+<div>
+<div><a href="#example-579">Example 579</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9634:1-9639:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][bar][baz]</span>
+<pre data-sourcepos="11947:1-11952:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[foo][bar][baz]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[baz]: /url1</span>
<span id="LC4" class="line" lang="plaintext">[foo]: /url2</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9641:1-9643:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo]&lt;a href="/url1"&gt;bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11954:1-11956:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[foo]&lt;a href="/url1"&gt;bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="9647:1-9647:9" dir="auto">
+</div>
+<h2 data-sourcepos="11961:1-11961:9" dir="auto">
<a id="user-content-images" class="anchor" href="#images" aria-hidden="true"></a>Images</h2>
-<p data-sourcepos="9649:1-9657:55" dir="auto">Syntax for images is like the syntax for links, with one
+<p data-sourcepos="11963:1-11971:55" dir="auto">Syntax for images is like the syntax for links, with one
difference. Instead of [link text], we have an
<a href="@">image description</a>. The rules for this are the
same as for [link text], except that (a) an
@@ -8662,482 +10763,616 @@ image description starts with <code>![</code> rather than <code>[</code>, and
An image description has inline elements
as its contents. When an image is rendered to HTML,
this is standardly used as the image's <code>alt</code> attribute.</p>
+<div>
+<div><a href="#example-580">Example 580</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9659:1-9661:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo](/url "title")</span></code></pre>
+<pre data-sourcepos="11976:1-11978:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo](/url "title")</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9663:1-9665:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="foo" title="title" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11980:1-11982:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="foo" title="title" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-581">Example 581</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9668:1-9672:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo *bar*]</span>
+<pre data-sourcepos="11989:1-11993:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo *bar*]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo *bar*]: train.jpg "train &amp; tracks"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9674:1-9676:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="train.jpg" alt="foo bar" title="train &amp;amp; tracks" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="11995:1-11997:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="train.jpg" alt="foo bar" title="train &amp;amp; tracks" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-582">Example 582</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9679:1-9681:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo ![bar](/url)](/url2)</span></code></pre>
+<pre data-sourcepos="12004:1-12006:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo ![bar](/url)](/url2)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9683:1-9685:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url2" alt="foo bar" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12008:1-12010:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url2" alt="foo bar" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-583">Example 583</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9688:1-9690:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo [bar](/url)](/url2)</span></code></pre>
+<pre data-sourcepos="12017:1-12019:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo [bar](/url)](/url2)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9692:1-9694:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url2" alt="foo bar" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12021:1-12023:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url2" alt="foo bar" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9697:1-9702:40" dir="auto">Though this spec is concerned with parsing, not rendering, it is
+</div>
+<p data-sourcepos="12027:1-12032:40" dir="auto">Though this spec is concerned with parsing, not rendering, it is
recommended that in rendering to HTML, only the plain string content
of the [image description] be used. Note that in
the above example, the alt attribute's value is <code>foo bar</code>, not <code>foo [bar](/url)</code> or <code>foo &lt;a href="/url"&gt;bar&lt;/a&gt;</code>. Only the plain string
content is rendered, without formatting.</p>
+<div>
+<div><a href="#example-584">Example 584</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9704:1-9708:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo *bar*][]</span>
+<pre data-sourcepos="12037:1-12041:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo *bar*][]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo *bar*]: train.jpg "train &amp; tracks"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9710:1-9712:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="train.jpg" alt="foo bar" title="train &amp;amp; tracks" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12043:1-12045:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="train.jpg" alt="foo bar" title="train &amp;amp; tracks" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-585">Example 585</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9715:1-9719:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo *bar*][foobar]</span>
+<pre data-sourcepos="12052:1-12056:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo *bar*][foobar]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[FOOBAR]: train.jpg "train &amp; tracks"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9721:1-9723:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="train.jpg" alt="foo bar" title="train &amp;amp; tracks" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12058:1-12060:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="train.jpg" alt="foo bar" title="train &amp;amp; tracks" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-586">Example 586</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9726:1-9728:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo](train.jpg)</span></code></pre>
+<pre data-sourcepos="12067:1-12069:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo](train.jpg)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9730:1-9732:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="train.jpg" alt="foo" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12071:1-12073:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="train.jpg" alt="foo" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-587">Example 587</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9735:1-9737:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">My ![foo bar](/path/to/train.jpg "title" )</span></code></pre>
+<pre data-sourcepos="12080:1-12082:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">My ![foo bar](/path/to/train.jpg "title" )</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9739:1-9741:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;My &lt;img src="/path/to/train.jpg" alt="foo bar" title="title" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12084:1-12086:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;My &lt;img src="/path/to/train.jpg" alt="foo bar" title="title" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-588">Example 588</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9744:1-9746:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo](&lt;url&gt;)</span></code></pre>
+<pre data-sourcepos="12093:1-12095:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo](&lt;url&gt;)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9748:1-9750:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="url" alt="foo" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12097:1-12099:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="url" alt="foo" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-589">Example 589</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9753:1-9755:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![](/url)</span></code></pre>
+<pre data-sourcepos="12106:1-12108:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![](/url)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9757:1-9759:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12110:1-12112:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9762:1-9762:16" dir="auto">Reference-style:</p>
+</div>
+<p data-sourcepos="12116:1-12116:16" dir="auto">Reference-style:</p>
+<div>
+<div><a href="#example-590">Example 590</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9764:1-9768:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo][bar]</span>
+<pre data-sourcepos="12121:1-12125:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo][bar]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[bar]: /url</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9770:1-9772:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="foo" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12127:1-12129:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="foo" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-591">Example 591</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9775:1-9779:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo][bar]</span>
+<pre data-sourcepos="12136:1-12140:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo][bar]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[BAR]: /url</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9781:1-9783:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="foo" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12142:1-12144:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="foo" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9786:1-9786:10" dir="auto">Collapsed:</p>
+</div>
+<p data-sourcepos="12148:1-12148:10" dir="auto">Collapsed:</p>
+<div>
+<div><a href="#example-592">Example 592</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9788:1-9792:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo][]</span>
+<pre data-sourcepos="12153:1-12157:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo][]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9794:1-9796:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="foo" title="title" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12159:1-12161:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="foo" title="title" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-593">Example 593</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9799:1-9803:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![*foo* bar][]</span>
+<pre data-sourcepos="12168:1-12172:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![*foo* bar][]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[*foo* bar]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9805:1-9807:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="foo bar" title="title" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12174:1-12176:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="foo bar" title="title" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9810:1-9810:32" dir="auto">The labels are case-insensitive:</p>
+</div>
+<p data-sourcepos="12180:1-12180:32" dir="auto">The labels are case-insensitive:</p>
+<div>
+<div><a href="#example-594">Example 594</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9812:1-9816:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![Foo][]</span>
+<pre data-sourcepos="12185:1-12189:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![Foo][]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9818:1-9820:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="Foo" title="title" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12191:1-12193:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="Foo" title="title" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9823:1-9824:33" dir="auto">As with reference links, [whitespace] is not allowed
+</div>
+<p data-sourcepos="12197:1-12198:33" dir="auto">As with reference links, [whitespace] is not allowed
between the two sets of brackets:</p>
+<div>
+<div><a href="#example-595">Example 595</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9826:1-9831:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo] </span>
+<pre data-sourcepos="12203:1-12208:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo] </span>
<span id="LC2" class="line" lang="plaintext">[]</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">[foo]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9833:1-9836:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="foo" title="title" /&gt;</span>
+<pre data-sourcepos="12210:1-12213:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="foo" title="title" /&gt;</span>
<span id="LC2" class="line" lang="plaintext">[]&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9839:1-9839:9" dir="auto">Shortcut:</p>
+</div>
+<p data-sourcepos="12217:1-12217:9" dir="auto">Shortcut:</p>
+<div>
+<div><a href="#example-596">Example 596</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9841:1-9845:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo]</span>
+<pre data-sourcepos="12222:1-12226:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![foo]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9847:1-9849:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="foo" title="title" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12228:1-12230:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="foo" title="title" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-597">Example 597</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9852:1-9856:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![*foo* bar]</span>
+<pre data-sourcepos="12237:1-12241:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![*foo* bar]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[*foo* bar]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9858:1-9860:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="foo bar" title="title" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12243:1-12245:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="foo bar" title="title" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9863:1-9863:56" dir="auto">Note that link labels cannot contain unescaped brackets:</p>
+</div>
+<p data-sourcepos="12249:1-12249:56" dir="auto">Note that link labels cannot contain unescaped brackets:</p>
+<div>
+<div><a href="#example-598">Example 598</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9865:1-9869:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![[foo]]</span>
+<pre data-sourcepos="12254:1-12258:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![[foo]]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[[foo]]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9871:1-9874:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;![[foo]]&lt;/p&gt;</span>
+<pre data-sourcepos="12260:1-12263:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;![[foo]]&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;[[foo]]: /url &amp;quot;title&amp;quot;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9877:1-9877:37" dir="auto">The link labels are case-insensitive:</p>
+</div>
+<p data-sourcepos="12267:1-12267:37" dir="auto">The link labels are case-insensitive:</p>
+<div>
+<div><a href="#example-599">Example 599</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9879:1-9883:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![Foo]</span>
+<pre data-sourcepos="12272:1-12276:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![Foo]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9885:1-9887:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="Foo" title="title" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12278:1-12280:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="/url" alt="Foo" title="title" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9890:1-9891:33" dir="auto">If you just want a literal <code>!</code> followed by bracketed text, you can
+</div>
+<p data-sourcepos="12284:1-12285:33" dir="auto">If you just want a literal <code>!</code> followed by bracketed text, you can
backslash-escape the opening <code>[</code>:</p>
+<div>
+<div><a href="#example-600">Example 600</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9893:1-9897:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">!\[foo]</span>
+<pre data-sourcepos="12290:1-12294:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">!\[foo]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9899:1-9901:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;![foo]&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12296:1-12298:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;![foo]&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9904:1-9905:4" dir="auto">If you want a link after a literal <code>!</code>, backslash-escape the
+</div>
+<p data-sourcepos="12302:1-12303:4" dir="auto">If you want a link after a literal <code>!</code>, backslash-escape the
<code>!</code>:</p>
+<div>
+<div><a href="#example-601">Example 601</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9907:1-9911:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">\![foo]</span>
+<pre data-sourcepos="12308:1-12312:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">\![foo]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[foo]: /url "title"</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9913:1-9915:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;!&lt;a href="/url" title="title"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12314:1-12316:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;!&lt;a href="/url" title="title"&gt;foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="9918:1-9918:12" dir="auto">
+</div>
+<h2 data-sourcepos="12320:1-12320:12" dir="auto">
<a id="user-content-autolinks" class="anchor" href="#autolinks" aria-hidden="true"></a>Autolinks</h2>
-<p data-sourcepos="9920:1-9922:18" dir="auto"><a href="@">Autolink</a>s are absolute URIs and email addresses inside
+<p data-sourcepos="12322:1-12324:18" dir="auto"><a href="@">Autolink</a>s are absolute URIs and email addresses inside
<code>&lt;</code> and <code>&gt;</code>. They are parsed as links, with the URL or email address
as the link label.</p>
-<p data-sourcepos="9924:1-9926:52" dir="auto">A <a href="@">URI autolink</a> consists of <code>&lt;</code>, followed by an
+<p data-sourcepos="12326:1-12328:52" dir="auto">A <a href="@">URI autolink</a> consists of <code>&lt;</code>, followed by an
[absolute URI] followed by <code>&gt;</code>. It is parsed as
a link to the URI, with the URI as the link's label.</p>
-<p data-sourcepos="9928:1-9933:25" dir="auto">An <a href="@">absolute URI</a>,
+<p data-sourcepos="12330:1-12335:25" dir="auto">An <a href="@">absolute URI</a>,
for these purposes, consists of a [scheme] followed by a colon (<code>:</code>)
followed by zero or more characters other than ASCII
[whitespace] and control characters, <code>&lt;</code>, and <code>&gt;</code>. If
the URI includes these characters, they must be percent-encoded
(e.g. <code>%20</code> for a space).</p>
-<p data-sourcepos="9935:1-9938:37" dir="auto">For purposes of this spec, a <a href="@">scheme</a> is any sequence
+<p data-sourcepos="12337:1-12340:37" dir="auto">For purposes of this spec, a <a href="@">scheme</a> is any sequence
of 2--32 characters beginning with an ASCII letter and followed
by any combination of ASCII letters, digits, or the symbols plus
("+"), period ("."), or hyphen ("-").</p>
-<p data-sourcepos="9940:1-9940:30" dir="auto">Here are some valid autolinks:</p>
+<p data-sourcepos="12342:1-12342:30" dir="auto">Here are some valid autolinks:</p>
+<div>
+<div><a href="#example-602">Example 602</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9942:1-9944:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;http://foo.bar.baz&gt;</span></code></pre>
+<pre data-sourcepos="12347:1-12349:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;http://foo.bar.baz&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9946:1-9948:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://foo.bar.baz"&gt;http://foo.bar.baz&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12351:1-12353:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://foo.bar.baz"&gt;http://foo.bar.baz&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-603">Example 603</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9951:1-9953:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean&gt;</span></code></pre>
+<pre data-sourcepos="12360:1-12362:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9955:1-9957:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://foo.bar.baz/test?q=hello&amp;amp;id=22&amp;amp;boolean"&gt;http://foo.bar.baz/test?q=hello&amp;amp;id=22&amp;amp;boolean&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12364:1-12366:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://foo.bar.baz/test?q=hello&amp;amp;id=22&amp;amp;boolean"&gt;http://foo.bar.baz/test?q=hello&amp;amp;id=22&amp;amp;boolean&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-604">Example 604</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9960:1-9962:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;irc://foo.bar:2233/baz&gt;</span></code></pre>
+<pre data-sourcepos="12373:1-12375:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;irc://foo.bar:2233/baz&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9964:1-9966:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="irc://foo.bar:2233/baz"&gt;irc://foo.bar:2233/baz&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12377:1-12379:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="irc://foo.bar:2233/baz"&gt;irc://foo.bar:2233/baz&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9969:1-9969:23" dir="auto">Uppercase is also fine:</p>
+</div>
+<p data-sourcepos="12383:1-12383:23" dir="auto">Uppercase is also fine:</p>
+<div>
+<div><a href="#example-605">Example 605</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9971:1-9973:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;MAILTO:FOO@BAR.BAZ&gt;</span></code></pre>
+<pre data-sourcepos="12388:1-12390:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;MAILTO:FOO@BAR.BAZ&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9975:1-9977:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="MAILTO:FOO@BAR.BAZ"&gt;MAILTO:FOO@BAR.BAZ&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12392:1-12394:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="MAILTO:FOO@BAR.BAZ"&gt;MAILTO:FOO@BAR.BAZ&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="9980:1-9983:18" dir="auto">Note that many strings that count as [absolute URIs] for
+</div>
+<p data-sourcepos="12398:1-12401:18" dir="auto">Note that many strings that count as [absolute URIs] for
purposes of this spec are not valid URIs, because their
schemes are not registered or because of other problems
with their syntax:</p>
+<div>
+<div><a href="#example-606">Example 606</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9985:1-9987:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a+b+c:d&gt;</span></code></pre>
+<pre data-sourcepos="12406:1-12408:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a+b+c:d&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9989:1-9991:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="a+b+c:d"&gt;a+b+c:d&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12410:1-12412:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="a+b+c:d"&gt;a+b+c:d&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-607">Example 607</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9994:1-9996:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;made-up-scheme://foo,bar&gt;</span></code></pre>
+<pre data-sourcepos="12419:1-12421:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;made-up-scheme://foo,bar&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="9998:1-10000:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="made-up-scheme://foo,bar"&gt;made-up-scheme://foo,bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12423:1-12425:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="made-up-scheme://foo,bar"&gt;made-up-scheme://foo,bar&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-608">Example 608</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10003:1-10005:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;http://../&gt;</span></code></pre>
+<pre data-sourcepos="12432:1-12434:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;http://../&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10007:1-10009:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://../"&gt;http://../&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12436:1-12438:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://../"&gt;http://../&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-609">Example 609</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10012:1-10014:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;localhost:5001/foo&gt;</span></code></pre>
+<pre data-sourcepos="12445:1-12447:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;localhost:5001/foo&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10016:1-10018:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="localhost:5001/foo"&gt;localhost:5001/foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12449:1-12451:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="localhost:5001/foo"&gt;localhost:5001/foo&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10021:1-10021:36" dir="auto">Spaces are not allowed in autolinks:</p>
+</div>
+<p data-sourcepos="12455:1-12455:36" dir="auto">Spaces are not allowed in autolinks:</p>
+<div>
+<div><a href="#example-610">Example 610</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10023:1-10025:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;http://foo.bar/baz bim&gt;</span></code></pre>
+<pre data-sourcepos="12460:1-12462:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;http://foo.bar/baz bim&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10027:1-10029:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;http://foo.bar/baz bim&amp;gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12464:1-12466:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;http://foo.bar/baz bim&amp;gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10032:1-10032:47" dir="auto">Backslash-escapes do not work inside autolinks:</p>
+</div>
+<p data-sourcepos="12470:1-12470:47" dir="auto">Backslash-escapes do not work inside autolinks:</p>
+<div>
+<div><a href="#example-611">Example 611</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10034:1-10036:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;http://example.com/\[\&gt;</span></code></pre>
+<pre data-sourcepos="12475:1-12477:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;http://example.com/\[\&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10038:1-10040:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://example.com/%5C%5B%5C"&gt;http://example.com/\[\&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12479:1-12481:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://example.com/%5C%5B%5C"&gt;http://example.com/\[\&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10043:1-10046:55" dir="auto">An <a href="@">email autolink</a>
+</div>
+<p data-sourcepos="12485:1-12488:55" dir="auto">An <a href="@">email autolink</a>
consists of <code>&lt;</code>, followed by an [email address],
followed by <code>&gt;</code>. The link's label is the email address,
and the URL is <code>mailto:</code> followed by the email address.</p>
-<p data-sourcepos="10048:1-10051:83" dir="auto">An <a href="@">email address</a>,
+<p data-sourcepos="12490:1-12493:83" dir="auto">An <a href="@">email address</a>,
for these purposes, is anything that matches
the <a href="https://html.spec.whatwg.org/multipage/forms.html#e-mail-state-(type=email)" rel="nofollow noreferrer noopener" target="_blank">non-normative regex from the HTML5
spec</a>:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10053:5-10055:0" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">/^[a-zA-Z0-9.!#$%&amp;'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?</span>
+<pre data-sourcepos="12495:5-12497:0" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">/^[a-zA-Z0-9.!#$%&amp;'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?</span>
<span id="LC2" class="line" lang="plaintext">(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10056:1-10056:28" dir="auto">Examples of email autolinks:</p>
+<p data-sourcepos="12498:1-12498:28" dir="auto">Examples of email autolinks:</p>
+<div>
+<div><a href="#example-612">Example 612</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10058:1-10060:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;foo@bar.example.com&gt;</span></code></pre>
+<pre data-sourcepos="12503:1-12505:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;foo@bar.example.com&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10062:1-10064:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="mailto:foo@bar.example.com"&gt;foo@bar.example.com&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12507:1-12509:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="mailto:foo@bar.example.com"&gt;foo@bar.example.com&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-613">Example 613</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10067:1-10069:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;foo+special@Bar.baz-bar0.com&gt;</span></code></pre>
+<pre data-sourcepos="12516:1-12518:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;foo+special@Bar.baz-bar0.com&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10071:1-10073:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="mailto:foo+special@Bar.baz-bar0.com"&gt;foo+special@Bar.baz-bar0.com&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12520:1-12522:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="mailto:foo+special@Bar.baz-bar0.com"&gt;foo+special@Bar.baz-bar0.com&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10076:1-10076:53" dir="auto">Backslash-escapes do not work inside email autolinks:</p>
+</div>
+<p data-sourcepos="12526:1-12526:53" dir="auto">Backslash-escapes do not work inside email autolinks:</p>
+<div>
+<div><a href="#example-614">Example 614</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10078:1-10080:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;foo\+@bar.example.com&gt;</span></code></pre>
+<pre data-sourcepos="12531:1-12533:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;foo\+@bar.example.com&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10082:1-10084:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;foo+@bar.example.com&amp;gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12535:1-12537:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;foo+@bar.example.com&amp;gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10087:1-10087:24" dir="auto">These are not autolinks:</p>
+</div>
+<p data-sourcepos="12541:1-12541:24" dir="auto">These are not autolinks:</p>
+<div>
+<div><a href="#example-615">Example 615</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10089:1-10091:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;&gt;</span></code></pre>
+<pre data-sourcepos="12546:1-12548:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10093:1-10095:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;&amp;gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12550:1-12552:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;&amp;gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-616">Example 616</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10098:1-10100:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt; http://foo.bar &gt;</span></code></pre>
+<pre data-sourcepos="12559:1-12561:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt; http://foo.bar &gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10102:1-10104:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt; http://foo.bar &amp;gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12563:1-12565:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt; http://foo.bar &amp;gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-617">Example 617</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10107:1-10109:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;m:abc&gt;</span></code></pre>
+<pre data-sourcepos="12572:1-12574:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;m:abc&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10111:1-10113:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;m:abc&amp;gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12576:1-12578:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;m:abc&amp;gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-618">Example 618</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10116:1-10118:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;foo.bar.baz&gt;</span></code></pre>
+<pre data-sourcepos="12585:1-12587:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;foo.bar.baz&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10120:1-10122:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;foo.bar.baz&amp;gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12589:1-12591:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;foo.bar.baz&amp;gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-619">Example 619</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10125:1-10127:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">http://example.com</span></code></pre>
+<pre data-sourcepos="12598:1-12600:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">http://example.com</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10129:1-10131:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;http://example.com&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12602:1-12604:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;http://example.com&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-620">Example 620</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10134:1-10136:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo@bar.example.com</span></code></pre>
+<pre data-sourcepos="12611:1-12613:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo@bar.example.com</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10138:1-10140:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo@bar.example.com&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12615:1-12617:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo@bar.example.com&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
<div>
-<h2 data-sourcepos="10144:1-10144:24">
+<h2 data-sourcepos="12622:1-12622:24">
<a id="user-content-autolinks-extension" class="anchor" href="#autolinks-extension" aria-hidden="true"></a>Autolinks (extension)</h2>
-<p data-sourcepos="10146:1-10147:29">GFM enables the <code>autolink</code> extension, where autolinks will be recognised in a
+<p data-sourcepos="12624:1-12625:29">GFM enables the <code>autolink</code> extension, where autolinks will be recognised in a
greater number of conditions.</p>
-<p data-sourcepos="10149:1-10153:8">[Autolink]s can also be constructed without requiring the use of <code>&lt;</code> and to <code>&gt;</code>
+<p data-sourcepos="12627:1-12631:8">[Autolink]s can also be constructed without requiring the use of <code>&lt;</code> and to <code>&gt;</code>
to delimit them, although they will be recognized under a smaller set of
circumstances. All such recognized autolinks can only come at the beginning of
a line, after whitespace, or any of the delimiting characters <code>*</code>, <code>_</code>, <code>~</code>,
and <code>(</code>.</p>
-<p data-sourcepos="10155:1-10161:73">An <a href="@">extended www autolink</a> will be recognized
+<p data-sourcepos="12633:1-12639:73">An <a href="@">extended www autolink</a> will be recognized
when the text <code>www.</code> is found followed by a [valid domain].
A <a href="@">valid domain</a> consists of segments
of alphanumeric characters, underscores (<code>_</code>) and hyphens (<code>-</code>)
separated by periods (<code>.</code>).
There must be at least one period,
and no underscores may be present in the last two segments of the domain.</p>
-<p data-sourcepos="10163:1-10163:49">The scheme <code>http</code> will be inserted automatically:</p>
+<p data-sourcepos="12641:1-12641:49">The scheme <code>http</code> will be inserted automatically:</p>
+<div>
+<div><a href="#example-621">Example 621</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10165:1-10167:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">www.commonmark.org</span></code></pre>
+<pre data-sourcepos="12646:1-12648:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">www.commonmark.org</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10169:1-10171:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://www.commonmark.org"&gt;www.commonmark.org&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12650:1-12652:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://www.commonmark.org"&gt;www.commonmark.org&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10173:1-10173:77">After a [valid domain], zero or more non-space non-<code>&lt;</code> characters may follow:</p>
+</div>
+<p data-sourcepos="12655:1-12655:77">After a [valid domain], zero or more non-space non-<code>&lt;</code> characters may follow:</p>
+<div>
+<div><a href="#example-622">Example 622</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10175:1-10177:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Visit www.commonmark.org/help for more information.</span></code></pre>
+<pre data-sourcepos="12660:1-12662:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Visit www.commonmark.org/help for more information.</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10179:1-10181:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Visit &lt;a href="http://www.commonmark.org/help"&gt;www.commonmark.org/help&lt;/a&gt; for more information.&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12664:1-12666:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Visit &lt;a href="http://www.commonmark.org/help"&gt;www.commonmark.org/help&lt;/a&gt; for more information.&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10183:1-10183:64">We then apply <a href="@">extended autolink path validation</a> as follows:</p>
-<p data-sourcepos="10185:1-10187:21">Trailing punctuation (specifically, <code>?</code>, <code>!</code>, <code>.</code>, <code>,</code>, <code>:</code>, <code>*</code>, <code>_</code>, and <code>~</code>)
+</div>
+<p data-sourcepos="12669:1-12669:64">We then apply <a href="@">extended autolink path validation</a> as follows:</p>
+<p data-sourcepos="12671:1-12673:21">Trailing punctuation (specifically, <code>?</code>, <code>!</code>, <code>.</code>, <code>,</code>, <code>:</code>, <code>*</code>, <code>_</code>, and <code>~</code>)
will not be considered part of the autolink, though they may be included in the
interior of the link:</p>
+<div>
+<div><a href="#example-623">Example 623</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10189:1-10193:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Visit www.commonmark.org.</span>
+<pre data-sourcepos="12678:1-12682:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Visit www.commonmark.org.</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">Visit www.commonmark.org/a.b.</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10195:1-10198:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Visit &lt;a href="http://www.commonmark.org"&gt;www.commonmark.org&lt;/a&gt;.&lt;/p&gt;</span>
+<pre data-sourcepos="12684:1-12687:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Visit &lt;a href="http://www.commonmark.org"&gt;www.commonmark.org&lt;/a&gt;.&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;Visit &lt;a href="http://www.commonmark.org/a.b"&gt;www.commonmark.org/a.b&lt;/a&gt;.&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10200:1-10203:76">When an autolink ends in <code>)</code>, we scan the entire autolink for the total number
+</div>
+<p data-sourcepos="12690:1-12693:76">When an autolink ends in <code>)</code>, we scan the entire autolink for the total number
of parentheses. If there is a greater number of closing parentheses than
opening ones, we don't consider the unmatched trailing parentheses part of the
autolink, in order to facilitate including an autolink inside a parenthesis:</p>
+<div>
+<div><a href="#example-624">Example 624</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10205:1-10213:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">www.google.com/search?q=Markup+(business)</span>
+<pre data-sourcepos="12698:1-12706:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">www.google.com/search?q=Markup+(business)</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">www.google.com/search?q=Markup+(business)))</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -9147,53 +11382,65 @@ autolink, in order to facilitate including an autolink inside a parenthesis:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10215:1-10220:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://www.google.com/search?q=Markup+(business)"&gt;www.google.com/search?q=Markup+(business)&lt;/a&gt;&lt;/p&gt;</span>
+<pre data-sourcepos="12708:1-12713:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://www.google.com/search?q=Markup+(business)"&gt;www.google.com/search?q=Markup+(business)&lt;/a&gt;&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://www.google.com/search?q=Markup+(business)"&gt;www.google.com/search?q=Markup+(business)&lt;/a&gt;))&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;(&lt;a href="http://www.google.com/search?q=Markup+(business)"&gt;www.google.com/search?q=Markup+(business)&lt;/a&gt;)&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;(&lt;a href="http://www.google.com/search?q=Markup+(business)"&gt;www.google.com/search?q=Markup+(business)&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10222:1-10224:8">This check is only done when the link ends in a closing parentheses <code>)</code>, so if
+</div>
+<p data-sourcepos="12716:1-12718:8">This check is only done when the link ends in a closing parentheses <code>)</code>, so if
the only parentheses are in the interior of the autolink, no special rules are
applied:</p>
+<div>
+<div><a href="#example-625">Example 625</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10226:1-10228:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">www.google.com/search?q=(business))+ok</span></code></pre>
+<pre data-sourcepos="12723:1-12725:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">www.google.com/search?q=(business))+ok</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10230:1-10232:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://www.google.com/search?q=(business))+ok"&gt;www.google.com/search?q=(business))+ok&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12727:1-12729:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://www.google.com/search?q=(business))+ok"&gt;www.google.com/search?q=(business))+ok&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10234:1-10237:13">If an autolink ends in a semicolon (<code>;</code>), we check to see if it appears to
+</div>
+<p data-sourcepos="12732:1-12735:13">If an autolink ends in a semicolon (<code>;</code>), we check to see if it appears to
resemble an [entity reference][entity references]; if the preceding text is <code>&amp;</code>
followed by one or more alphanumeric characters. If so, it is excluded from
the autolink:</p>
+<div>
+<div><a href="#example-626">Example 626</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10239:1-10243:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">www.google.com/search?q=commonmark&amp;hl=en</span>
+<pre data-sourcepos="12740:1-12744:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">www.google.com/search?q=commonmark&amp;hl=en</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">www.google.com/search?q=commonmark&amp;hl;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10245:1-10248:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://www.google.com/search?q=commonmark&amp;amp;hl=en"&gt;www.google.com/search?q=commonmark&amp;amp;hl=en&lt;/a&gt;&lt;/p&gt;</span>
+<pre data-sourcepos="12746:1-12749:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://www.google.com/search?q=commonmark&amp;amp;hl=en"&gt;www.google.com/search?q=commonmark&amp;amp;hl=en&lt;/a&gt;&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://www.google.com/search?q=commonmark"&gt;www.google.com/search?q=commonmark&lt;/a&gt;&amp;amp;hl;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10250:1-10250:33"><code>&lt;</code> immediately ends an autolink.</p>
+</div>
+<p data-sourcepos="12752:1-12752:33"><code>&lt;</code> immediately ends an autolink.</p>
+<div>
+<div><a href="#example-627">Example 627</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10252:1-10254:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">www.commonmark.org/he&lt;lp</span></code></pre>
+<pre data-sourcepos="12757:1-12759:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">www.commonmark.org/he&lt;lp</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10256:1-10258:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://www.commonmark.org/he"&gt;www.commonmark.org/he&lt;/a&gt;&amp;lt;lp&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12761:1-12763:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://www.commonmark.org/he"&gt;www.commonmark.org/he&lt;/a&gt;&amp;lt;lp&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10260:1-10263:36">An <a href="@">extended url autolink</a> will be recognised when one of the schemes
+</div>
+<p data-sourcepos="12766:1-12769:36">An <a href="@">extended url autolink</a> will be recognised when one of the schemes
<code>http://</code>, <code>https://</code>, or <code>ftp://</code>, followed by a [valid domain], then zero or
more non-space non-<code>&lt;</code> characters according to
[extended autolink path validation]:</p>
+<div>
+<div><a href="#example-628">Example 628</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10265:1-10271:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">http://commonmark.org</span>
+<pre data-sourcepos="12774:1-12780:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">http://commonmark.org</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">(Visit https://encrypted.google.com/search?q=Markup+(business))</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -9201,45 +11448,54 @@ more non-space non-<code>&lt;</code> characters according to
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10273:1-10277:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://commonmark.org"&gt;http://commonmark.org&lt;/a&gt;&lt;/p&gt;</span>
+<pre data-sourcepos="12782:1-12786:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="http://commonmark.org"&gt;http://commonmark.org&lt;/a&gt;&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;(Visit &lt;a href="https://encrypted.google.com/search?q=Markup+(business)"&gt;https://encrypted.google.com/search?q=Markup+(business)&lt;/a&gt;)&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;Anonymous FTP is available at &lt;a href="ftp://foo.bar.baz"&gt;ftp://foo.bar.baz&lt;/a&gt;.&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10280:1-10282:20">An <a href="@">extended email autolink</a> will be recognised when an email address is
+</div>
+<p data-sourcepos="12790:1-12792:20">An <a href="@">extended email autolink</a> will be recognised when an email address is
recognised within any text node. Email addresses are recognised according to
the following rules:</p>
-<ul data-sourcepos="10284:1-10290:0">
-<li data-sourcepos="10284:1-10284:75">One ore more characters which are alphanumeric, or <code>.</code>, <code>-</code>, <code>_</code>, or <code>+</code>.</li>
-<li data-sourcepos="10285:1-10285:16">An <code>@</code> symbol.</li>
-<li data-sourcepos="10286:1-10290:0">One or more characters which are alphanumeric, or <code>-</code> or <code>_</code>,
+<ul data-sourcepos="12794:1-12800:0">
+<li data-sourcepos="12794:1-12794:75">One ore more characters which are alphanumeric, or <code>.</code>, <code>-</code>, <code>_</code>, or <code>+</code>.</li>
+<li data-sourcepos="12795:1-12795:16">An <code>@</code> symbol.</li>
+<li data-sourcepos="12796:1-12800:0">One or more characters which are alphanumeric, or <code>-</code> or <code>_</code>,
separated by periods (<code>.</code>).
There must be at least one period.
The last character must not be one of <code>-</code> or <code>_</code>.</li>
</ul>
-<p data-sourcepos="10291:1-10291:71">The scheme <code>mailto:</code> will automatically be added to the generated link:</p>
+<p data-sourcepos="12801:1-12801:71">The scheme <code>mailto:</code> will automatically be added to the generated link:</p>
+<div>
+<div><a href="#example-629">Example 629</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10293:1-10295:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo@bar.baz</span></code></pre>
+<pre data-sourcepos="12806:1-12808:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo@bar.baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10297:1-10299:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="mailto:foo@bar.baz"&gt;foo@bar.baz&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12810:1-12812:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="mailto:foo@bar.baz"&gt;foo@bar.baz&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10301:1-10301:44"><code>+</code> can occur before the <code>@</code>, but not after.</p>
+</div>
+<p data-sourcepos="12815:1-12815:44"><code>+</code> can occur before the <code>@</code>, but not after.</p>
+<div>
+<div><a href="#example-630">Example 630</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10303:1-10305:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">hello@mail+xyz.example isn't valid, but hello+xyz@mail.example is.</span></code></pre>
+<pre data-sourcepos="12820:1-12822:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">hello@mail+xyz.example isn't valid, but hello+xyz@mail.example is.</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10307:1-10309:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;hello@mail+xyz.example isn't valid, but &lt;a href="mailto:hello+xyz@mail.example"&gt;hello+xyz@mail.example&lt;/a&gt; is.&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12824:1-12826:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;hello@mail+xyz.example isn't valid, but &lt;a href="mailto:hello+xyz@mail.example"&gt;hello+xyz@mail.example&lt;/a&gt; is.&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10311:1-10313:12"><code>.</code>, <code>-</code>, and <code>_</code> can occur on both sides of the <code>@</code>, but only <code>.</code> may occur at
+</div>
+<p data-sourcepos="12829:1-12831:12"><code>.</code>, <code>-</code>, and <code>_</code> can occur on both sides of the <code>@</code>, but only <code>.</code> may occur at
the end of the email address, in which case it will not be considered part of
the address:</p>
+<div>
+<div><a href="#example-631">Example 631</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10315:1-10323:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">a.b-c_d@a.b</span>
+<pre data-sourcepos="12836:1-12844:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">a.b-c_d@a.b</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">a.b-c_d@a.b.</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -9249,295 +11505,361 @@ the address:</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10325:1-10330:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="mailto:a.b-c_d@a.b"&gt;a.b-c_d@a.b&lt;/a&gt;&lt;/p&gt;</span>
+<pre data-sourcepos="12846:1-12851:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="mailto:a.b-c_d@a.b"&gt;a.b-c_d@a.b&lt;/a&gt;&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;&lt;a href="mailto:a.b-c_d@a.b"&gt;a.b-c_d@a.b&lt;/a&gt;.&lt;/p&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;a.b-c_d@a.b-&lt;/p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;p&gt;a.b-c_d@a.b_&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
</div>
-<h2 data-sourcepos="10334:1-10334:11" dir="auto">
+</div>
+<h2 data-sourcepos="12856:1-12856:11" dir="auto">
<a id="user-content-raw-html" class="anchor" href="#raw-html" aria-hidden="true"></a>Raw HTML</h2>
-<p data-sourcepos="10336:1-10339:57" dir="auto">Text between <code>&lt;</code> and <code>&gt;</code> that looks like an HTML tag is parsed as a
+<p data-sourcepos="12858:1-12861:57" dir="auto">Text between <code>&lt;</code> and <code>&gt;</code> that looks like an HTML tag is parsed as a
raw HTML tag and will be rendered in HTML without escaping.
Tag and attribute names are not limited to current HTML tags,
so custom tags (and even, say, DocBook tags) may be used.</p>
-<p data-sourcepos="10341:1-10341:29" dir="auto">Here is the grammar for tags:</p>
-<p data-sourcepos="10343:1-10345:14" dir="auto">A <a href="@">tag name</a> consists of an ASCII letter
+<p data-sourcepos="12863:1-12863:29" dir="auto">Here is the grammar for tags:</p>
+<p data-sourcepos="12865:1-12867:14" dir="auto">A <a href="@">tag name</a> consists of an ASCII letter
followed by zero or more ASCII letters, digits, or
hyphens (<code>-</code>).</p>
-<p data-sourcepos="10347:1-10349:32" dir="auto">An <a href="@">attribute</a> consists of [whitespace],
+<p data-sourcepos="12869:1-12871:32" dir="auto">An <a href="@">attribute</a> consists of [whitespace],
an [attribute name], and an optional
[attribute value specification].</p>
-<p data-sourcepos="10351:1-10354:52" dir="auto">An <a href="@">attribute name</a>
+<p data-sourcepos="12873:1-12876:52" dir="auto">An <a href="@">attribute name</a>
consists of an ASCII letter, <code>_</code>, or <code>:</code>, followed by zero or more ASCII
letters, digits, <code>_</code>, <code>.</code>, <code>:</code>, or <code>-</code>. (Note: This is the XML
specification restricted to ASCII. HTML5 is laxer.)</p>
-<p data-sourcepos="10356:1-10359:7" dir="auto">An <a href="@">attribute value specification</a>
+<p data-sourcepos="12878:1-12881:7" dir="auto">An <a href="@">attribute value specification</a>
consists of optional [whitespace],
a <code>=</code> character, optional [whitespace], and an [attribute
value].</p>
-<p data-sourcepos="10361:1-10363:72" dir="auto">An <a href="@">attribute value</a>
+<p data-sourcepos="12883:1-12885:72" dir="auto">An <a href="@">attribute value</a>
consists of an [unquoted attribute value],
a [single-quoted attribute value], or a [double-quoted attribute value].</p>
-<p data-sourcepos="10365:1-10367:60" dir="auto">An <a href="@">unquoted attribute value</a>
+<p data-sourcepos="12887:1-12889:60" dir="auto">An <a href="@">unquoted attribute value</a>
is a nonempty string of characters not
including [whitespace], <code>"</code>, <code>'</code>, <code>=</code>, <code>&lt;</code>, <code>&gt;</code>, or <code>`</code>.</p>
-<p data-sourcepos="10369:1-10371:46" dir="auto">A <a href="@">single-quoted attribute value</a>
+<p data-sourcepos="12891:1-12893:46" dir="auto">A <a href="@">single-quoted attribute value</a>
consists of <code>'</code>, zero or more
characters not including <code>'</code>, and a final <code>'</code>.</p>
-<p data-sourcepos="10373:1-10375:46" dir="auto">A <a href="@">double-quoted attribute value</a>
+<p data-sourcepos="12895:1-12897:46" dir="auto">A <a href="@">double-quoted attribute value</a>
consists of <code>"</code>, zero or more
characters not including <code>"</code>, and a final <code>"</code>.</p>
-<p data-sourcepos="10377:1-10379:31" dir="auto">An <a href="@">open tag</a> consists of a <code>&lt;</code> character, a [tag name],
+<p data-sourcepos="12899:1-12901:31" dir="auto">An <a href="@">open tag</a> consists of a <code>&lt;</code> character, a [tag name],
zero or more [attributes], optional [whitespace], an optional <code>/</code>
character, and a <code>&gt;</code> character.</p>
-<p data-sourcepos="10381:1-10382:57" dir="auto">A <a href="@">closing tag</a> consists of the string <code>&lt;/</code>, a
+<p data-sourcepos="12903:1-12904:57" dir="auto">A <a href="@">closing tag</a> consists of the string <code>&lt;/</code>, a
[tag name], optional [whitespace], and the character <code>&gt;</code>.</p>
-<p data-sourcepos="10384:1-10387:63" dir="auto">An <a href="@">HTML comment</a> consists of <code>&lt;!--</code> + <em>text</em> + <code>--&gt;</code>,
+<p data-sourcepos="12906:1-12909:63" dir="auto">An <a href="@">HTML comment</a> consists of <code>&lt;!--</code> + <em>text</em> + <code>--&gt;</code>,
where <em>text</em> does not start with <code>&gt;</code> or <code>-&gt;</code>, does not end with <code>-</code>,
and does not contain <code>--</code>. (See the
<a href="http://www.w3.org/TR/html5/syntax.html#comments" rel="nofollow noreferrer noopener" target="_blank">HTML5 spec</a>.)</p>
-<p data-sourcepos="10389:1-10392:5" dir="auto">A <a href="@">processing instruction</a>
+<p data-sourcepos="12911:1-12914:5" dir="auto">A <a href="@">processing instruction</a>
consists of the string <code>&lt;?</code>, a string
of characters not including the string <code>?&gt;</code>, and the string
<code>?&gt;</code>.</p>
-<p data-sourcepos="10394:1-10397:37" dir="auto">A <a href="@">declaration</a> consists of the
+<p data-sourcepos="12916:1-12919:37" dir="auto">A <a href="@">declaration</a> consists of the
string <code>&lt;!</code>, a name consisting of one or more uppercase ASCII letters,
[whitespace], a string of characters not including the
character <code>&gt;</code>, and the character <code>&gt;</code>.</p>
-<p data-sourcepos="10399:1-10401:28" dir="auto">A <a href="@">CDATA section</a> consists of
+<p data-sourcepos="12921:1-12923:28" dir="auto">A <a href="@">CDATA section</a> consists of
the string <code>&lt;![CDATA[</code>, a string of characters not including the string
<code>]]&gt;</code>, and the string <code>]]&gt;</code>.</p>
-<p data-sourcepos="10403:1-10405:21" dir="auto">An <a href="@">HTML tag</a> consists of an [open tag], a [closing tag],
+<p data-sourcepos="12925:1-12927:21" dir="auto">An <a href="@">HTML tag</a> consists of an [open tag], a [closing tag],
an [HTML comment], a [processing instruction], a [declaration],
or a [CDATA section].</p>
-<p data-sourcepos="10407:1-10407:31" dir="auto">Here are some simple open tags:</p>
+<p data-sourcepos="12929:1-12929:31" dir="auto">Here are some simple open tags:</p>
+<div>
+<div><a href="#example-632">Example 632</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10409:1-10411:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a&gt;&lt;bab&gt;&lt;c2c&gt;</span></code></pre>
+<pre data-sourcepos="12934:1-12936:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a&gt;&lt;bab&gt;&lt;c2c&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10413:1-10415:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a&gt;&lt;bab&gt;&lt;c2c&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12938:1-12940:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a&gt;&lt;bab&gt;&lt;c2c&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10418:1-10418:15" dir="auto">Empty elements:</p>
+</div>
+<p data-sourcepos="12944:1-12944:15" dir="auto">Empty elements:</p>
+<div>
+<div><a href="#example-633">Example 633</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10420:1-10422:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a/&gt;&lt;b2/&gt;</span></code></pre>
+<pre data-sourcepos="12949:1-12951:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a/&gt;&lt;b2/&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10424:1-10426:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a/&gt;&lt;b2/&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="12953:1-12955:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a/&gt;&lt;b2/&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10429:1-10429:24" dir="auto">[Whitespace] is allowed:</p>
+</div>
+<p data-sourcepos="12959:1-12959:24" dir="auto">[Whitespace] is allowed:</p>
+<div>
+<div><a href="#example-634">Example 634</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10431:1-10434:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a /&gt;&lt;b2</span>
+<pre data-sourcepos="12964:1-12967:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a /&gt;&lt;b2</span>
<span id="LC2" class="line" lang="plaintext">data="foo" &gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10436:1-10439:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a /&gt;&lt;b2</span>
+<pre data-sourcepos="12969:1-12972:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a /&gt;&lt;b2</span>
<span id="LC2" class="line" lang="plaintext">data="foo" &gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10442:1-10442:16" dir="auto">With attributes:</p>
+</div>
+<p data-sourcepos="12976:1-12976:16" dir="auto">With attributes:</p>
+<div>
+<div><a href="#example-635">Example 635</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10444:1-10447:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a foo="bar" bam = 'baz &lt;em&gt;"&lt;/em&gt;'</span>
+<pre data-sourcepos="12981:1-12984:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a foo="bar" bam = 'baz &lt;em&gt;"&lt;/em&gt;'</span>
<span id="LC2" class="line" lang="plaintext">_boolean zoop:33=zoop:33 /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10449:1-10452:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a foo="bar" bam = 'baz &lt;em&gt;"&lt;/em&gt;'</span>
+<pre data-sourcepos="12986:1-12989:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a foo="bar" bam = 'baz &lt;em&gt;"&lt;/em&gt;'</span>
<span id="LC2" class="line" lang="plaintext">_boolean zoop:33=zoop:33 /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10455:1-10455:29" dir="auto">Custom tag names can be used:</p>
+</div>
+<p data-sourcepos="12993:1-12993:29" dir="auto">Custom tag names can be used:</p>
+<div>
+<div><a href="#example-636">Example 636</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10457:1-10459:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo &lt;responsive-image src="foo.jpg" /&gt;</span></code></pre>
+<pre data-sourcepos="12998:1-13000:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo &lt;responsive-image src="foo.jpg" /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10461:1-10463:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo &lt;responsive-image src="foo.jpg" /&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13002:1-13004:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo &lt;responsive-image src="foo.jpg" /&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10466:1-10466:38" dir="auto">Illegal tag names, not parsed as HTML:</p>
+</div>
+<p data-sourcepos="13008:1-13008:38" dir="auto">Illegal tag names, not parsed as HTML:</p>
+<div>
+<div><a href="#example-637">Example 637</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10468:1-10470:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;33&gt; &lt;__&gt;</span></code></pre>
+<pre data-sourcepos="13013:1-13015:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;33&gt; &lt;__&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10472:1-10474:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;33&amp;gt; &amp;lt;__&amp;gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13017:1-13019:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;33&amp;gt; &amp;lt;__&amp;gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10477:1-10477:24" dir="auto">Illegal attribute names:</p>
+</div>
+<p data-sourcepos="13023:1-13023:24" dir="auto">Illegal attribute names:</p>
+<div>
+<div><a href="#example-638">Example 638</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10479:1-10481:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a h*#ref="hi"&gt;</span></code></pre>
+<pre data-sourcepos="13028:1-13030:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a h*#ref="hi"&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10483:1-10485:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;a h*#ref=&amp;quot;hi&amp;quot;&amp;gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13032:1-13034:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;a h*#ref=&amp;quot;hi&amp;quot;&amp;gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10488:1-10488:25" dir="auto">Illegal attribute values:</p>
+</div>
+<p data-sourcepos="13038:1-13038:25" dir="auto">Illegal attribute values:</p>
+<div>
+<div><a href="#example-639">Example 639</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10490:1-10492:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="hi'&gt; &lt;a href=hi'&gt;</span></code></pre>
+<pre data-sourcepos="13043:1-13045:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="hi'&gt; &lt;a href=hi'&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10494:1-10496:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;a href=&amp;quot;hi'&amp;gt; &amp;lt;a href=hi'&amp;gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13047:1-13049:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;a href=&amp;quot;hi'&amp;gt; &amp;lt;a href=hi'&amp;gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10499:1-10499:21" dir="auto">Illegal [whitespace]:</p>
+</div>
+<p data-sourcepos="13053:1-13053:21" dir="auto">Illegal [whitespace]:</p>
+<div>
+<div><a href="#example-640">Example 640</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10501:1-10506:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt; a&gt;&lt;</span>
+<pre data-sourcepos="13058:1-13063:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt; a&gt;&lt;</span>
<span id="LC2" class="line" lang="plaintext">foo&gt;&lt;bar/ &gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;foo bar=baz</span>
<span id="LC4" class="line" lang="plaintext">bim!bop /&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10508:1-10513:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt; a&amp;gt;&amp;lt;</span>
+<pre data-sourcepos="13065:1-13070:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt; a&amp;gt;&amp;lt;</span>
<span id="LC2" class="line" lang="plaintext">foo&amp;gt;&amp;lt;bar/ &amp;gt;</span>
<span id="LC3" class="line" lang="plaintext">&amp;lt;foo bar=baz</span>
<span id="LC4" class="line" lang="plaintext">bim!bop /&amp;gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10516:1-10516:21" dir="auto">Missing [whitespace]:</p>
+</div>
+<p data-sourcepos="13074:1-13074:21" dir="auto">Missing [whitespace]:</p>
+<div>
+<div><a href="#example-641">Example 641</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10518:1-10520:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href='bar'title=title&gt;</span></code></pre>
+<pre data-sourcepos="13079:1-13081:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href='bar'title=title&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10522:1-10524:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;a href='bar'title=title&amp;gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13083:1-13085:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;a href='bar'title=title&amp;gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10527:1-10527:13" dir="auto">Closing tags:</p>
+</div>
+<p data-sourcepos="13089:1-13089:13" dir="auto">Closing tags:</p>
+<div>
+<div><a href="#example-642">Example 642</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10529:1-10531:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;/a&gt;&lt;/foo &gt;</span></code></pre>
+<pre data-sourcepos="13094:1-13096:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;/a&gt;&lt;/foo &gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10533:1-10535:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;/a&gt;&lt;/foo &gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13098:1-13100:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;/a&gt;&lt;/foo &gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10538:1-10538:34" dir="auto">Illegal attributes in closing tag:</p>
+</div>
+<p data-sourcepos="13104:1-13104:34" dir="auto">Illegal attributes in closing tag:</p>
+<div>
+<div><a href="#example-643">Example 643</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10540:1-10542:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;/a href="foo"&gt;</span></code></pre>
+<pre data-sourcepos="13109:1-13111:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;/a href="foo"&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10544:1-10546:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;/a href=&amp;quot;foo&amp;quot;&amp;gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13113:1-13115:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;/a href=&amp;quot;foo&amp;quot;&amp;gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10549:1-10549:9" dir="auto">Comments:</p>
+</div>
+<p data-sourcepos="13119:1-13119:9" dir="auto">Comments:</p>
+<div>
+<div><a href="#example-644">Example 644</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10551:1-10554:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo &lt;!-- this is a</span>
+<pre data-sourcepos="13124:1-13127:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo &lt;!-- this is a</span>
<span id="LC2" class="line" lang="plaintext">comment - with hyphen --&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10556:1-10559:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;!-- this is a</span>
+<pre data-sourcepos="13129:1-13132:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;!-- this is a</span>
<span id="LC2" class="line" lang="plaintext">comment - with hyphen --&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-645">Example 645</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10562:1-10564:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo &lt;!-- not a comment -- two hyphens --&gt;</span></code></pre>
+<pre data-sourcepos="13139:1-13141:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo &lt;!-- not a comment -- two hyphens --&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10566:1-10568:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &amp;lt;!-- not a comment -- two hyphens --&amp;gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13143:1-13145:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &amp;lt;!-- not a comment -- two hyphens --&amp;gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10571:1-10571:13" dir="auto">Not comments:</p>
+</div>
+<p data-sourcepos="13149:1-13149:13" dir="auto">Not comments:</p>
+<div>
+<div><a href="#example-646">Example 646</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10573:1-10577:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo &lt;!--&gt; foo --&gt;</span>
+<pre data-sourcepos="13154:1-13158:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo &lt;!--&gt; foo --&gt;</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">foo &lt;!-- foo---&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10579:1-10582:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &amp;lt;!--&amp;gt; foo --&amp;gt;&lt;/p&gt;</span>
+<pre data-sourcepos="13160:1-13163:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &amp;lt;!--&amp;gt; foo --&amp;gt;&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;foo &amp;lt;!-- foo---&amp;gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10585:1-10585:24" dir="auto">Processing instructions:</p>
+</div>
+<p data-sourcepos="13167:1-13167:24" dir="auto">Processing instructions:</p>
+<div>
+<div><a href="#example-647">Example 647</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10587:1-10589:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo &lt;?php echo $a; ?&gt;</span></code></pre>
+<pre data-sourcepos="13172:1-13174:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo &lt;?php echo $a; ?&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10591:1-10593:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;?php echo $a; ?&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13176:1-13178:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;?php echo $a; ?&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10596:1-10596:13" dir="auto">Declarations:</p>
+</div>
+<p data-sourcepos="13182:1-13182:13" dir="auto">Declarations:</p>
+<div>
+<div><a href="#example-648">Example 648</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10598:1-10600:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo &lt;!ELEMENT br EMPTY&gt;</span></code></pre>
+<pre data-sourcepos="13187:1-13189:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo &lt;!ELEMENT br EMPTY&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10602:1-10604:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;!ELEMENT br EMPTY&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13191:1-13193:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;!ELEMENT br EMPTY&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10607:1-10607:15" dir="auto">CDATA sections:</p>
+</div>
+<p data-sourcepos="13197:1-13197:15" dir="auto">CDATA sections:</p>
+<div>
+<div><a href="#example-649">Example 649</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10609:1-10611:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo &lt;![CDATA[&gt;&amp;&lt;]]&gt;</span></code></pre>
+<pre data-sourcepos="13202:1-13204:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo &lt;![CDATA[&gt;&amp;&lt;]]&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10613:1-10615:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;![CDATA[&gt;&amp;&lt;]]&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13206:1-13208:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;![CDATA[&gt;&amp;&lt;]]&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10618:1-10619:11" dir="auto">Entity and numeric character references are preserved in HTML
+</div>
+<p data-sourcepos="13212:1-13213:11" dir="auto">Entity and numeric character references are preserved in HTML
attributes:</p>
+<div>
+<div><a href="#example-650">Example 650</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10621:1-10623:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo &lt;a href="&amp;ouml;"&gt;</span></code></pre>
+<pre data-sourcepos="13218:1-13220:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo &lt;a href="&amp;ouml;"&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10625:1-10627:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;a href="&amp;ouml;"&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13222:1-13224:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;a href="&amp;ouml;"&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10630:1-10630:49" dir="auto">Backslash escapes do not work in HTML attributes:</p>
+</div>
+<p data-sourcepos="13228:1-13228:49" dir="auto">Backslash escapes do not work in HTML attributes:</p>
+<div>
+<div><a href="#example-651">Example 651</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10632:1-10634:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo &lt;a href="\*"&gt;</span></code></pre>
+<pre data-sourcepos="13233:1-13235:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo &lt;a href="\*"&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10636:1-10638:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;a href="\*"&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13237:1-13239:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo &lt;a href="\*"&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-652">Example 652</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10641:1-10643:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="\""&gt;</span></code></pre>
+<pre data-sourcepos="13246:1-13248:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="\""&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10645:1-10647:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;a href=&amp;quot;&amp;quot;&amp;quot;&amp;gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13250:1-13252:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&amp;lt;a href=&amp;quot;&amp;quot;&amp;quot;&amp;gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
<div>
-<h2 data-sourcepos="10652:1-10652:34">
+<h2 data-sourcepos="13258:1-13258:34">
<a id="user-content-disallowed-raw-html-extension" class="anchor" href="#disallowed-raw-html-extension" aria-hidden="true"></a>Disallowed Raw HTML (extension)</h2>
-<p data-sourcepos="10654:1-10655:36">GFM enables the <code>tagfilter</code> extension, where the following HTML tags will be
+<p data-sourcepos="13260:1-13261:36">GFM enables the <code>tagfilter</code> extension, where the following HTML tags will be
filtered when rendering HTML output:</p>
-<ul data-sourcepos="10657:1-10666:0">
-<li data-sourcepos="10657:1-10657:11"><code>&lt;title&gt;</code></li>
-<li data-sourcepos="10658:1-10658:14"><code>&lt;textarea&gt;</code></li>
-<li data-sourcepos="10659:1-10659:11"><code>&lt;style&gt;</code></li>
-<li data-sourcepos="10660:1-10660:9"><code>&lt;xmp&gt;</code></li>
-<li data-sourcepos="10661:1-10661:12"><code>&lt;iframe&gt;</code></li>
-<li data-sourcepos="10662:1-10662:13"><code>&lt;noembed&gt;</code></li>
-<li data-sourcepos="10663:1-10663:14"><code>&lt;noframes&gt;</code></li>
-<li data-sourcepos="10664:1-10664:12"><code>&lt;script&gt;</code></li>
-<li data-sourcepos="10665:1-10666:0"><code>&lt;plaintext&gt;</code></li>
+<ul data-sourcepos="13263:1-13272:0">
+<li data-sourcepos="13263:1-13263:11"><code>&lt;title&gt;</code></li>
+<li data-sourcepos="13264:1-13264:14"><code>&lt;textarea&gt;</code></li>
+<li data-sourcepos="13265:1-13265:11"><code>&lt;style&gt;</code></li>
+<li data-sourcepos="13266:1-13266:9"><code>&lt;xmp&gt;</code></li>
+<li data-sourcepos="13267:1-13267:12"><code>&lt;iframe&gt;</code></li>
+<li data-sourcepos="13268:1-13268:13"><code>&lt;noembed&gt;</code></li>
+<li data-sourcepos="13269:1-13269:14"><code>&lt;noframes&gt;</code></li>
+<li data-sourcepos="13270:1-13270:12"><code>&lt;script&gt;</code></li>
+<li data-sourcepos="13271:1-13272:0"><code>&lt;plaintext&gt;</code></li>
</ul>
-<p data-sourcepos="10667:1-10670:71">Filtering is done by replacing the leading <code>&lt;</code> with the entity <code>&amp;lt;</code>. These
+<p data-sourcepos="13273:1-13276:71">Filtering is done by replacing the leading <code>&lt;</code> with the entity <code>&amp;lt;</code>. These
tags are chosen in particular as they change how HTML is interpreted in a way
unique to them (i.e. nested HTML is interpreted differently), and this is
usually undesireable in the context of other rendered Markdown content.</p>
-<p data-sourcepos="10672:1-10672:39">All other HTML tags are left untouched.</p>
+<p data-sourcepos="13278:1-13278:39">All other HTML tags are left untouched.</p>
+<div>
+<div><a href="#example-653">Example 653</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10674:1-10680:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;strong&gt; &lt;title&gt; &lt;style&gt; &lt;em&gt;</span>
+<pre data-sourcepos="13283:1-13289:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;strong&gt; &lt;title&gt; &lt;style&gt; &lt;em&gt;</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC4" class="line" lang="plaintext"> &lt;xmp&gt; is disallowed. &lt;XMP&gt; is also disallowed.</span>
@@ -9545,252 +11867,315 @@ usually undesireable in the context of other rendered Markdown content.</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10682:1-10687:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt; &amp;lt;title&gt; &amp;lt;style&gt; &lt;em&gt;&lt;/p&gt;</span>
+<pre data-sourcepos="13291:1-13296:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt; &amp;lt;title&gt; &amp;lt;style&gt; &lt;em&gt;&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;blockquote&gt;</span>
<span id="LC3" class="line" lang="plaintext"> &amp;lt;xmp&gt; is disallowed. &amp;lt;XMP&gt; is also disallowed.</span>
<span id="LC4" class="line" lang="plaintext">&lt;/blockquote&gt;</span></code></pre>
<copy-code></copy-code>
</div>
</div>
-<h2 data-sourcepos="10691:1-10691:19" dir="auto">
+</div>
+<h2 data-sourcepos="13301:1-13301:19" dir="auto">
<a id="user-content-hard-line-breaks" class="anchor" href="#hard-line-breaks" aria-hidden="true"></a>Hard line breaks</h2>
-<p data-sourcepos="10693:1-10696:27" dir="auto">A line break (not in a code span or HTML tag) that is preceded
+<p data-sourcepos="13303:1-13306:27" dir="auto">A line break (not in a code span or HTML tag) that is preceded
by two or more spaces and does not occur at the end of a block
is parsed as a <a href="@">hard line break</a> (rendered
in HTML as a <code>&lt;br /&gt;</code> tag):</p>
+<div>
+<div><a href="#example-654">Example 654</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10698:1-10701:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo </span>
+<pre data-sourcepos="13311:1-13314:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo </span>
<span id="LC2" class="line" lang="plaintext">baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10703:1-10706:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;br /&gt;</span>
+<pre data-sourcepos="13316:1-13319:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;br /&gt;</span>
<span id="LC2" class="line" lang="plaintext">baz&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10709:1-10710:48" dir="auto">For a more visible alternative, a backslash before the
+</div>
+<p data-sourcepos="13323:1-13324:48" dir="auto">For a more visible alternative, a backslash before the
[line ending] may be used instead of two spaces:</p>
+<div>
+<div><a href="#example-655">Example 655</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10712:1-10715:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo\</span>
+<pre data-sourcepos="13329:1-13332:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo\</span>
<span id="LC2" class="line" lang="plaintext">baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10717:1-10720:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;br /&gt;</span>
+<pre data-sourcepos="13334:1-13337:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;br /&gt;</span>
<span id="LC2" class="line" lang="plaintext">baz&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10723:1-10723:33" dir="auto">More than two spaces can be used:</p>
+</div>
+<p data-sourcepos="13341:1-13341:33" dir="auto">More than two spaces can be used:</p>
+<div>
+<div><a href="#example-656">Example 656</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10725:1-10728:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo </span>
+<pre data-sourcepos="13346:1-13349:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo </span>
<span id="LC2" class="line" lang="plaintext">baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10730:1-10733:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;br /&gt;</span>
+<pre data-sourcepos="13351:1-13354:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;br /&gt;</span>
<span id="LC2" class="line" lang="plaintext">baz&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10736:1-10736:61" dir="auto">Leading spaces at the beginning of the next line are ignored:</p>
+</div>
+<p data-sourcepos="13358:1-13358:61" dir="auto">Leading spaces at the beginning of the next line are ignored:</p>
+<div>
+<div><a href="#example-657">Example 657</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10738:1-10741:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo </span>
+<pre data-sourcepos="13363:1-13366:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo </span>
<span id="LC2" class="line" lang="plaintext"> bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10743:1-10746:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;br /&gt;</span>
+<pre data-sourcepos="13368:1-13371:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;br /&gt;</span>
<span id="LC2" class="line" lang="plaintext">bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-658">Example 658</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10749:1-10752:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo\</span>
+<pre data-sourcepos="13378:1-13381:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo\</span>
<span id="LC2" class="line" lang="plaintext"> bar</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10754:1-10757:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;br /&gt;</span>
+<pre data-sourcepos="13383:1-13386:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;br /&gt;</span>
<span id="LC2" class="line" lang="plaintext">bar&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10760:1-10761:26" dir="auto">Line breaks can occur inside emphasis, links, and other constructs
+</div>
+<p data-sourcepos="13390:1-13391:26" dir="auto">Line breaks can occur inside emphasis, links, and other constructs
that allow inline content:</p>
+<div>
+<div><a href="#example-659">Example 659</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10763:1-10766:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo </span>
+<pre data-sourcepos="13396:1-13399:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo </span>
<span id="LC2" class="line" lang="plaintext">bar*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10768:1-10771:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;br /&gt;</span>
+<pre data-sourcepos="13401:1-13404:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;br /&gt;</span>
<span id="LC2" class="line" lang="plaintext">bar&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-660">Example 660</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10774:1-10777:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo\</span>
+<pre data-sourcepos="13411:1-13414:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">*foo\</span>
<span id="LC2" class="line" lang="plaintext">bar*</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10779:1-10782:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;br /&gt;</span>
+<pre data-sourcepos="13416:1-13419:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;foo&lt;br /&gt;</span>
<span id="LC2" class="line" lang="plaintext">bar&lt;/em&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10785:1-10785:42" dir="auto">Line breaks do not occur inside code spans</p>
+</div>
+<p data-sourcepos="13423:1-13423:42" dir="auto">Line breaks do not occur inside code spans</p>
+<div>
+<div><a href="#example-661">Example 661</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10787:1-10790:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`code </span>
+<pre data-sourcepos="13428:1-13431:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`code </span>
<span id="LC2" class="line" lang="plaintext">span`</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10792:1-10794:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;code span&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13433:1-13435:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;code span&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-662">Example 662</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10797:1-10800:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`code\</span>
+<pre data-sourcepos="13442:1-13445:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`code\</span>
<span id="LC2" class="line" lang="plaintext">span`</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10802:1-10804:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;code\ span&lt;/code&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13447:1-13449:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;code&gt;code\ span&lt;/code&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10807:1-10807:13" dir="auto">or HTML tags:</p>
+</div>
+<p data-sourcepos="13453:1-13453:13" dir="auto">or HTML tags:</p>
+<div>
+<div><a href="#example-663">Example 663</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10809:1-10812:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="foo </span>
+<pre data-sourcepos="13458:1-13461:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="foo </span>
<span id="LC2" class="line" lang="plaintext">bar"&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10814:1-10817:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="foo </span>
+<pre data-sourcepos="13463:1-13466:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="foo </span>
<span id="LC2" class="line" lang="plaintext">bar"&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-664">Example 664</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10820:1-10823:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="foo\</span>
+<pre data-sourcepos="13473:1-13476:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;a href="foo\</span>
<span id="LC2" class="line" lang="plaintext">bar"&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10825:1-10828:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="foo\</span>
+<pre data-sourcepos="13478:1-13481:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="foo\</span>
<span id="LC2" class="line" lang="plaintext">bar"&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10831:1-10833:20" dir="auto">Hard line breaks are for separating inline content within a block.
+</div>
+<p data-sourcepos="13485:1-13487:20" dir="auto">Hard line breaks are for separating inline content within a block.
Neither syntax for hard line breaks works at the end of a paragraph or
other block element:</p>
+<div>
+<div><a href="#example-665">Example 665</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10835:1-10837:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo\</span></code></pre>
+<pre data-sourcepos="13492:1-13494:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo\</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10839:1-10841:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo\&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13496:1-13498:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo\&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-666">Example 666</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10844:1-10846:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo </span></code></pre>
+<pre data-sourcepos="13505:1-13507:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo </span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10848:1-10850:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13509:1-13511:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-667">Example 667</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10853:1-10855:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">### foo\</span></code></pre>
+<pre data-sourcepos="13518:1-13520:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">### foo\</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10857:1-10859:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h3&gt;foo\&lt;/h3&gt;</span></code></pre>
+<pre data-sourcepos="13522:1-13524:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h3&gt;foo\&lt;/h3&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-668">Example 668</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10862:1-10864:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">### foo </span></code></pre>
+<pre data-sourcepos="13531:1-13533:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">### foo </span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10866:1-10868:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h3&gt;foo&lt;/h3&gt;</span></code></pre>
+<pre data-sourcepos="13535:1-13537:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;h3&gt;foo&lt;/h3&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="10871:1-10871:19" dir="auto">
+</div>
+<h2 data-sourcepos="13541:1-13541:19" dir="auto">
<a id="user-content-soft-line-breaks" class="anchor" href="#soft-line-breaks" aria-hidden="true"></a>Soft line breaks</h2>
-<p data-sourcepos="10873:1-10877:62" dir="auto">A regular line break (not in a code span or HTML tag) that is not
+<p data-sourcepos="13543:1-13547:62" dir="auto">A regular line break (not in a code span or HTML tag) that is not
preceded by two or more spaces or a backslash is parsed as a
<a href="@">softbreak</a>. (A softbreak may be rendered in HTML either as a
[line ending] or as a space. The result will be the same in
browsers. In the examples here, a [line ending] will be used.)</p>
+<div>
+<div><a href="#example-669">Example 669</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10879:1-10882:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo</span>
+<pre data-sourcepos="13552:1-13555:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo</span>
<span id="LC2" class="line" lang="plaintext">baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10884:1-10887:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo</span>
+<pre data-sourcepos="13557:1-13560:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo</span>
<span id="LC2" class="line" lang="plaintext">baz&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10890:1-10891:8" dir="auto">Spaces at the end of the line and beginning of the next line are
+</div>
+<p data-sourcepos="13564:1-13565:8" dir="auto">Spaces at the end of the line and beginning of the next line are
removed:</p>
+<div>
+<div><a href="#example-670">Example 670</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10893:1-10896:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo </span>
+<pre data-sourcepos="13570:1-13573:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo </span>
<span id="LC2" class="line" lang="plaintext"> baz</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10898:1-10901:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo</span>
+<pre data-sourcepos="13575:1-13578:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;foo</span>
<span id="LC2" class="line" lang="plaintext">baz&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10904:1-10905:25" dir="auto">A conforming parser may render a soft line break in HTML either as a
+</div>
+<p data-sourcepos="13582:1-13583:25" dir="auto">A conforming parser may render a soft line break in HTML either as a
line break or as a space.</p>
-<p data-sourcepos="10907:1-10908:20" dir="auto">A renderer may also provide an option to render soft line breaks
+<p data-sourcepos="13585:1-13586:20" dir="auto">A renderer may also provide an option to render soft line breaks
as hard line breaks.</p>
-<h2 data-sourcepos="10910:1-10910:18" dir="auto">
+<h2 data-sourcepos="13588:1-13588:18" dir="auto">
<a id="user-content-textual-content" class="anchor" href="#textual-content" aria-hidden="true"></a>Textual content</h2>
-<p data-sourcepos="10912:1-10913:35" dir="auto">Any characters not given an interpretation by the above rules will
+<p data-sourcepos="13590:1-13591:35" dir="auto">Any characters not given an interpretation by the above rules will
be parsed as plain textual content.</p>
+<div>
+<div><a href="#example-671">Example 671</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10915:1-10917:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">hello $.;'there</span></code></pre>
+<pre data-sourcepos="13596:1-13598:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">hello $.;'there</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10919:1-10921:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;hello $.;'there&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13600:1-13602:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;hello $.;'there&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-672">Example 672</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10924:1-10926:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo χÏῆν</span></code></pre>
+<pre data-sourcepos="13609:1-13611:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Foo χÏῆν</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10928:1-10930:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo χÏῆν&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13613:1-13615:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Foo χÏῆν&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="10933:1-10933:39" dir="auto">Internal spaces are preserved verbatim:</p>
+</div>
+<p data-sourcepos="13619:1-13619:39" dir="auto">Internal spaces are preserved verbatim:</p>
+<div>
+<div><a href="#example-673">Example 673</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10935:1-10937:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Multiple spaces</span></code></pre>
+<pre data-sourcepos="13624:1-13626:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Multiple spaces</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10939:1-10941:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Multiple spaces&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="13628:1-13630:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;Multiple spaces&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h1 data-sourcepos="10944:1-10944:40" dir="auto">
+</div>
+<h1 data-sourcepos="13634:1-13634:40" dir="auto">
<a id="user-content-gitlab-official-specification-markdown" class="anchor" href="#gitlab-official-specification-markdown" aria-hidden="true"></a>GitLab Official Specification Markdown</h1>
-<p data-sourcepos="10946:1-10949:104" dir="auto">Currently, only some of the GitLab-specific markdown features are
+<p data-sourcepos="13636:1-13639:104" dir="auto">Currently, only some of the GitLab-specific markdown features are
listed in this section. We may eventually add all
GitLab-specific features currently listed as supported in the
<a href="https://docs.gitlab.com/ee/user/markdown.html" rel="nofollow noreferrer noopener" target="_blank">user-facing documentation for GitLab Flavored Markdown</a>.</p>
-<p data-sourcepos="10951:1-10952:69" dir="auto">There is currently only this single top-level heading, but the
+<p data-sourcepos="13641:1-13642:69" dir="auto">There is currently only this single top-level heading, but the
examples may be split into multiple top-level headings in the future.</p>
-<h2 data-sourcepos="10954:1-10954:12" dir="auto">
+<h2 data-sourcepos="13644:1-13644:12" dir="auto">
<a id="user-content-footnotes" class="anchor" href="#footnotes" aria-hidden="true"></a>Footnotes</h2>
-<p data-sourcepos="10956:1-10957:143" dir="auto">See
+<p data-sourcepos="13646:1-13647:143" dir="auto">See
<a href="https://docs.gitlab.com/ee/user/markdown.html#footnotes" rel="nofollow noreferrer noopener" target="_blank">the footnotes section of the user-facing documentation for GitLab Flavored Markdown</a>.</p>
+<div>
+<div><a href="#example-674">Example 674</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10959:1-10963:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">footnote reference tag [^fortytwo]</span>
+<pre data-sourcepos="13652:1-13656:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">footnote reference tag [^fortytwo]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[^fortytwo]: footnote text</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="10965:1-10985:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;</span>
+<pre data-sourcepos="13658:1-13678:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;</span>
<span id="LC2" class="line" lang="plaintext">footnote reference tag</span>
<span id="LC3" class="line" lang="plaintext">&lt;sup&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;a href="#fn-fortytwo-42" id="fnref-fortytwo-42" data-footnote-ref&gt;</span>
@@ -9811,23 +12196,26 @@ examples may be split into multiple top-level headings in the future.</p>
<span id="LC19" class="line" lang="plaintext">&lt;/section&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="10987:1-10987:18" dir="auto">
+</div>
+<h2 data-sourcepos="13681:1-13681:18" dir="auto">
<a id="user-content-task-list-items" class="anchor" href="#task-list-items" aria-hidden="true"></a>Task list items</h2>
-<p data-sourcepos="10989:1-10990:117" dir="auto">See
+<p data-sourcepos="13683:1-13684:117" dir="auto">See
<a href="https://docs.gitlab.com/ee/user/markdown.html#task-lists" rel="nofollow noreferrer noopener" target="_blank">Task lists</a> in the GitLab Flavored Markdown documentation.</p>
-<p data-sourcepos="10992:1-10995:39" dir="auto">Task list items (checkboxes) are defined as a GitHub Flavored Markdown extension in a section above.
+<p data-sourcepos="13686:1-13689:39" dir="auto">Task list items (checkboxes) are defined as a GitHub Flavored Markdown extension in a section above.
GitLab extends the behavior of task list items to support additional features.
Some of these features are in-progress, and should not yet be considered part of the official
GitLab Flavored Markdown specification.</p>
-<p data-sourcepos="10997:1-10997:85" dir="auto">Some of the behavior of task list items is implemented as client-side JavaScript/CSS.</p>
-<p data-sourcepos="10999:1-10999:80" dir="auto">The following are some basic examples; more examples may be added in the future.</p>
-<p data-sourcepos="11001:1-11001:16" dir="auto">Incomplete task:</p>
+<p data-sourcepos="13691:1-13691:85" dir="auto">Some of the behavior of task list items is implemented as client-side JavaScript/CSS.</p>
+<p data-sourcepos="13693:1-13693:80" dir="auto">The following are some basic examples; more examples may be added in the future.</p>
+<p data-sourcepos="13695:1-13695:16" dir="auto">Incomplete task:</p>
+<div>
+<div><a href="#example-675">Example 675</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11003:1-11005:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [ ] incomplete</span></code></pre>
+<pre data-sourcepos="13700:1-13702:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [ ] incomplete</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11007:1-11015:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="13704:1-13712:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;task-button/&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;input type="checkbox" disabled/&gt;</span>
@@ -9836,13 +12224,16 @@ GitLab Flavored Markdown specification.</p>
<span id="LC7" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="11017:1-11017:15" dir="auto">Completed task:</p>
+</div>
+<p data-sourcepos="13715:1-13715:15" dir="auto">Completed task:</p>
+<div>
+<div><a href="#example-676">Example 676</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11019:1-11021:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [x] completed</span></code></pre>
+<pre data-sourcepos="13720:1-13722:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [x] completed</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11023:1-11031:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="13724:1-13732:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;task-button/&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;input type="checkbox" checked disabled/&gt;</span>
@@ -9851,13 +12242,16 @@ GitLab Flavored Markdown specification.</p>
<span id="LC7" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="11033:1-11033:18" dir="auto">Inapplicable task:</p>
+</div>
+<p data-sourcepos="13735:1-13735:18" dir="auto">Inapplicable task:</p>
+<div>
+<div><a href="#example-677">Example 677</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11035:1-11037:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [~] inapplicable</span></code></pre>
+<pre data-sourcepos="13740:1-13742:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [~] inapplicable</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11039:1-11049:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="13744:1-13754:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;task-button/&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;input type="checkbox" data-inapplicable disabled&gt;</span>
@@ -9868,16 +12262,19 @@ GitLab Flavored Markdown specification.</p>
<span id="LC9" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="11051:1-11052:50" dir="auto">Inapplicable task in a "loose" list. Note that the <code>&lt;del&gt;</code> tag is not applied to the
+</div>
+<p data-sourcepos="13757:1-13758:50" dir="auto">Inapplicable task in a "loose" list. Note that the <code>&lt;del&gt;</code> tag is not applied to the
loose text; it has strikethrough applied with CSS.</p>
+<div>
+<div><a href="#example-678">Example 678</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11054:1-11058:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [~] inapplicable</span>
+<pre data-sourcepos="13763:1-13767:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [~] inapplicable</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> text in loose list</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11060:1-11075:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="13769:1-13784:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;task-button/&gt;</span>
@@ -9893,45 +12290,54 @@ loose text; it has strikethrough applied with CSS.</p>
<span id="LC14" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="11077:1-11077:15" dir="auto">
+</div>
+<h2 data-sourcepos="13787:1-13787:15" dir="auto">
<a id="user-content-front-matter" class="anchor" href="#front-matter" aria-hidden="true"></a>Front matter</h2>
-<p data-sourcepos="11079:1-11080:121" dir="auto">See
+<p data-sourcepos="13789:1-13790:121" dir="auto">See
<a href="https://docs.gitlab.com/ee/user/markdown.html#front-matter" rel="nofollow noreferrer noopener" target="_blank">Front matter</a> in the GitLab Flavored Markdown documentation.</p>
-<p data-sourcepos="11082:1-11083:95" dir="auto">Front matter is metadata included at the beginning of a Markdown document, preceding the content.
+<p data-sourcepos="13792:1-13793:95" dir="auto">Front matter is metadata included at the beginning of a Markdown document, preceding the content.
This data can be used by static site generators like Jekyll, Hugo, and many other applications.</p>
-<p data-sourcepos="11085:1-11085:18" dir="auto">YAML front matter:</p>
+<p data-sourcepos="13795:1-13795:18" dir="auto">YAML front matter:</p>
+<div>
+<div><a href="#example-679">Example 679</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11087:1-11091:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">---</span>
+<pre data-sourcepos="13800:1-13804:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">---</span>
<span id="LC2" class="line" lang="plaintext">title: YAML front matter</span>
<span id="LC3" class="line" lang="plaintext">---</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11093:1-11099:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;</span>
+<pre data-sourcepos="13806:1-13812:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;code&gt;</span>
<span id="LC3" class="line" lang="plaintext">title: YAML front matter</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="11101:1-11101:18" dir="auto">TOML front matter:</p>
+</div>
+<p data-sourcepos="13815:1-13815:18" dir="auto">TOML front matter:</p>
+<div>
+<div><a href="#example-680">Example 680</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11103:1-11107:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">+++</span>
+<pre data-sourcepos="13820:1-13824:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">+++</span>
<span id="LC2" class="line" lang="plaintext">title: TOML front matter</span>
<span id="LC3" class="line" lang="plaintext">+++</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11109:1-11115:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;</span>
+<pre data-sourcepos="13826:1-13832:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;code&gt;</span>
<span id="LC3" class="line" lang="plaintext">title: TOML front matter</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="11117:1-11117:18" dir="auto">JSON front matter:</p>
+</div>
+<p data-sourcepos="13835:1-13835:18" dir="auto">JSON front matter:</p>
+<div>
+<div><a href="#example-681">Example 681</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11119:1-11125:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">;;;</span>
+<pre data-sourcepos="13840:1-13846:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">;;;</span>
<span id="LC2" class="line" lang="plaintext">{</span>
<span id="LC3" class="line" lang="plaintext"> "title": "JSON front matter"</span>
<span id="LC4" class="line" lang="plaintext">}</span>
@@ -9939,7 +12345,7 @@ This data can be used by static site generators like Jekyll, Hugo, and many othe
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11127:1-11135:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;</span>
+<pre data-sourcepos="13848:1-13856:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;code&gt;</span>
<span id="LC3" class="line" lang="plaintext">{</span>
<span id="LC4" class="line" lang="plaintext"> "title": "JSON front matter"</span>
@@ -9948,9 +12354,12 @@ This data can be used by static site generators like Jekyll, Hugo, and many othe
<span id="LC7" class="line" lang="plaintext">&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="11137:1-11137:66" dir="auto">Front matter blocks should be inserted at the top of the document:</p>
+</div>
+<p data-sourcepos="13859:1-13859:66" dir="auto">Front matter blocks should be inserted at the top of the document:</p>
+<div>
+<div><a href="#example-682">Example 682</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11139:1-11145:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">text</span>
+<pre data-sourcepos="13864:1-13870:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">text</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">---</span>
<span id="LC4" class="line" lang="plaintext">title: YAML front matter</span>
@@ -9958,103 +12367,43 @@ This data can be used by static site generators like Jekyll, Hugo, and many othe
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11147:1-11151:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;text&lt;/p&gt;</span>
+<pre data-sourcepos="13872:1-13876:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;text&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;hr&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;h2&gt;title: YAML front matter&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="11153:1-11153:74" dir="auto">Front matter block delimiters shouldn’t be preceded by space characters:</p>
+</div>
+<p data-sourcepos="13879:1-13879:74" dir="auto">Front matter block delimiters shouldn’t be preceded by space characters:</p>
+<div>
+<div><a href="#example-683">Example 683</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11155:1-11159:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ---</span>
+<pre data-sourcepos="13884:1-13888:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ---</span>
<span id="LC2" class="line" lang="plaintext">title: YAML front matter</span>
<span id="LC3" class="line" lang="plaintext">---</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11161:1-11164:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr&gt;</span>
+<pre data-sourcepos="13890:1-13893:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h2&gt;title: YAML front matter&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="11166:1-11166:20" dir="auto">
+</div>
+<h2 data-sourcepos="13896:1-13896:20" dir="auto">
<a id="user-content-table-of-contents" class="anchor" href="#table-of-contents" aria-hidden="true"></a>Table of contents</h2>
-<p data-sourcepos="11168:1-11170:46" dir="auto">See
+<p data-sourcepos="13898:1-13900:46" dir="auto">See
<a href="https://docs.gitlab.com/ee/user/markdown.html#table-of-contents" rel="nofollow noreferrer noopener" target="_blank">table of contents</a>
in the GitLab Flavored Markdown documentation.</p>
-<p data-sourcepos="11172:1-11173:58" dir="auto">A table of contents is an unordered list that links to subheadings in the document.
-Add either the <code>[[_TOC_]]</code> or </p><ul class="section-nav">
-<li>
-<a href="#preliminaries">Preliminaries</a><ul>
-<li><a href="#characters-and-lines">Characters and lines</a></li>
-<li><a href="#tabs">Tabs</a></li>
-<li><a href="#insecure-characters">Insecure characters</a></li>
-</ul>
-</li>
-<li>
-<a href="#blocks-and-inlines">Blocks and inlines</a><ul>
-<li><a href="#precedence">Precedence</a></li>
-<li><a href="#container-blocks-and-leaf-blocks">Container blocks and leaf blocks</a></li>
-</ul>
-</li>
-<li>
-<a href="#leaf-blocks">Leaf blocks</a><ul>
-<li><a href="#thematic-breaks">Thematic breaks</a></li>
-<li><a href="#atx-headings">ATX headings</a></li>
-<li><a href="#setext-headings">Setext headings</a></li>
-<li><a href="#indented-code-blocks">Indented code blocks</a></li>
-<li><a href="#fenced-code-blocks">Fenced code blocks</a></li>
-<li><a href="#html-blocks">HTML blocks</a></li>
-<li><a href="#link-reference-definitions">Link reference definitions</a></li>
-<li><a href="#paragraphs">Paragraphs</a></li>
-<li><a href="#blank-lines">Blank lines</a></li>
-<li><a href="#tables-extension">Tables (extension)</a></li>
-</ul>
-</li>
-<li>
-<a href="#container-blocks">Container blocks</a><ul>
-<li><a href="#block-quotes">Block quotes</a></li>
-<li>
-<a href="#list-items">List items</a><ul><li><a href="#motivation">Motivation</a></li></ul>
-</li>
-<li><a href="#task-list-items-extension">Task list items (extension)</a></li>
-<li><a href="#lists">Lists</a></li>
-</ul>
-</li>
-<li>
-<a href="#inlines">Inlines</a><ul>
-<li><a href="#backslash-escapes">Backslash escapes</a></li>
-<li><a href="#entity-and-numeric-character-references">Entity and numeric character references</a></li>
-<li><a href="#code-spans">Code spans</a></li>
-<li><a href="#emphasis-and-strong-emphasis">Emphasis and strong emphasis</a></li>
-<li><a href="#strikethrough-extension">Strikethrough (extension)</a></li>
-<li><a href="#links">Links</a></li>
-<li><a href="#images">Images</a></li>
-<li><a href="#autolinks">Autolinks</a></li>
-<li><a href="#autolinks-extension">Autolinks (extension)</a></li>
-<li><a href="#raw-html">Raw HTML</a></li>
-<li><a href="#disallowed-raw-html-extension">Disallowed Raw HTML (extension)</a></li>
-<li><a href="#hard-line-breaks">Hard line breaks</a></li>
-<li><a href="#soft-line-breaks">Soft line breaks</a></li>
-<li><a href="#textual-content">Textual content</a></li>
-</ul>
-</li>
-<li>
-<a href="#gitlab-official-specification-markdown">GitLab Official Specification Markdown</a><ul>
-<li><a href="#footnotes">Footnotes</a></li>
-<li><a href="#task-list-items">Task list items</a></li>
-<li><a href="#front-matter">Front matter</a></li>
-<li><a href="#table-of-contents">Table of contents</a></li>
-</ul>
-</li>
-<li>
-<a href="#gitlab-internal-extension-markdown">GitLab Internal Extension Markdown</a><ul>
-<li><a href="#audio">Audio</a></li>
-<li><a href="#video">Video</a></li>
-<li><a href="#markdown-preview-api-request-overrides">Markdown Preview API Request Overrides</a></li>
-</ul>
-</li>
-</ul> tag on its own line.
+<p data-sourcepos="13902:1-13906:23" dir="auto">NOTE: Because of this bug (<a href="https://gitlab.com/gitlab-org/gitlab/-/issues/359077" rel="nofollow noreferrer noopener" target="_blank">https://gitlab.com/gitlab-org/gitlab/-/issues/359077</a>),
+we cannot actually include the <code>TOC</code> tag with single brackets in backticks
+in this Markdown document, otherwise it would render a table of contents inline
+right here. So, it's been switched to <code>[</code> + <code>TOC</code> + <code>]</code> instead. This can be reverted
+once that bug is fixed.</p>
+<p data-sourcepos="13908:1-13909:76" dir="auto">A table of contents is an unordered list that links to subheadings in the document.
+Add either the <code>[[_TOC_]]</code> tag or the <code>[</code> + <code>TOC</code> + <code>]</code> tag on its own line.</p>
+<div>
+<div><a href="#example-684">Example 684</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11175:1-11181:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[TOC]</span>
+<pre data-sourcepos="13914:1-13920:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[TOC]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"># Heading 1</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -10062,7 +12411,7 @@ Add either the <code>[[_TOC_]]</code> or </p><ul class="section-nav">
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11183:1-11194:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;nav&gt;</span>
+<pre data-sourcepos="13922:1-13933:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;nav&gt;</span>
<span id="LC2" class="line" lang="plaintext"> &lt;ul&gt;</span>
<span id="LC3" class="line" lang="plaintext"> &lt;li&gt;&lt;a href="#heading-1"&gt;Heading 1&lt;/a&gt;&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext"> &lt;ul&gt;</span>
@@ -10074,8 +12423,11 @@ Add either the <code>[[_TOC_]]</code> or </p><ul class="section-nav">
<span id="LC10" class="line" lang="plaintext">&lt;h2&gt;Heading 2&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-685">Example 685</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11196:1-11202:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[_TOC_]]</span>
+<pre data-sourcepos="13939:1-13945:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[_TOC_]]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"># Heading 1</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -10083,7 +12435,7 @@ Add either the <code>[[_TOC_]]</code> or </p><ul class="section-nav">
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11204:1-11215:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;nav&gt;</span>
+<pre data-sourcepos="13947:1-13958:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;nav&gt;</span>
<span id="LC2" class="line" lang="plaintext"> &lt;ul&gt;</span>
<span id="LC3" class="line" lang="plaintext"> &lt;li&gt;&lt;a href="#heading-1"&gt;Heading 1&lt;/a&gt;&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext"> &lt;ul&gt;</span>
@@ -10095,10 +12447,13 @@ Add either the <code>[[_TOC_]]</code> or </p><ul class="section-nav">
<span id="LC10" class="line" lang="plaintext">&lt;h2&gt;Heading 2&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="11217:1-11218:5" dir="auto">A table of contents is a block element. It should preceded and followed by a blank
+</div>
+<p data-sourcepos="13961:1-13962:5" dir="auto">A table of contents is a block element. It should preceded and followed by a blank
line.</p>
+<div>
+<div><a href="#example-686">Example 686</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11220:1-11226:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[_TOC_]]</span>
+<pre data-sourcepos="13967:1-13973:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[_TOC_]]</span>
<span id="LC2" class="line" lang="plaintext">text</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">text</span>
@@ -10106,19 +12461,22 @@ line.</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11228:1-11231:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[[&lt;em&gt;TOC&lt;/em&gt;]]text&lt;/p&gt;</span>
+<pre data-sourcepos="13975:1-13978:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[[&lt;em&gt;TOC&lt;/em&gt;]]text&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;text[TOC]&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="11233:1-11233:60" dir="auto">A table of contents can be indented with up to three spaces.</p>
+</div>
+<p data-sourcepos="13981:1-13981:60" dir="auto">A table of contents can be indented with up to three spaces.</p>
+<div>
+<div><a href="#example-687">Example 687</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11235:1-11239:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> [[_TOC_]]</span>
+<pre data-sourcepos="13986:1-13990:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> [[_TOC_]]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"># Heading 1</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11241:1-11248:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;nav&gt;</span>
+<pre data-sourcepos="13992:1-13999:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;nav&gt;</span>
<span id="LC2" class="line" lang="plaintext"> &lt;ul&gt;</span>
<span id="LC3" class="line" lang="plaintext"> &lt;li&gt;&lt;a href="#heading-1"&gt;Heading 1&lt;/a&gt;&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext"> &lt;/ul&gt;</span>
@@ -10126,120 +12484,1069 @@ line.</p>
<span id="LC6" class="line" lang="plaintext">&lt;h1&gt;Heading 1&lt;/h1&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h1 data-sourcepos="11250:1-11250:36" dir="auto">
+</div>
+<h1 data-sourcepos="14002:1-14002:36" dir="auto">
<a id="user-content-gitlab-internal-extension-markdown" class="anchor" href="#gitlab-internal-extension-markdown" aria-hidden="true"></a>GitLab Internal Extension Markdown</h1>
-<h2 data-sourcepos="11252:1-11252:8" dir="auto">
+<h2 data-sourcepos="14004:1-14004:8" dir="auto">
<a id="user-content-audio" class="anchor" href="#audio" aria-hidden="true"></a>Audio</h2>
-<p data-sourcepos="11254:1-11255:107" dir="auto">See
+<p data-sourcepos="14006:1-14007:107" dir="auto">See
<a href="https://docs.gitlab.com/ee/user/markdown.html#audio" rel="nofollow noreferrer noopener" target="_blank">audio</a> in the GitLab Flavored Markdown documentation.</p>
-<p data-sourcepos="11257:1-11259:63" dir="auto">GLFM renders image elements as an audio player as long as the resource’s file extension is
+<p data-sourcepos="14009:1-14011:63" dir="auto">GLFM renders image elements as an audio player as long as the resource’s file extension is
one of the following supported audio extensions <code>.mp3</code>, <code>.oga</code>, <code>.ogg</code>, <code>.spx</code>, and <code>.wav</code>.
Audio ignore the alternative text part of an image declaration.</p>
+<div>
+<div><a href="#example-688">Example 688</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11261:1-11263:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![audio](audio.oga "audio title")</span></code></pre>
+<pre data-sourcepos="14016:1-14018:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![audio](audio.oga "audio title")</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11265:1-11267:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;audio src="audio.oga" title="audio title"&gt;&lt;/audio&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="14020:1-14022:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;audio src="audio.oga" title="audio title"&gt;&lt;/audio&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="11269:1-11269:41" dir="auto">Reference definitions work audio as well:</p>
+</div>
+<p data-sourcepos="14025:1-14025:41" dir="auto">Reference definitions work audio as well:</p>
+<div>
+<div><a href="#example-689">Example 689</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11271:1-11275:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[audio]: audio.oga "audio title"</span>
+<pre data-sourcepos="14030:1-14034:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[audio]: audio.oga "audio title"</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">![audio][audio]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11277:1-11279:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;audio src="audio.oga" title="audio title"&gt;&lt;/audio&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="14036:1-14038:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;audio src="audio.oga" title="audio title"&gt;&lt;/audio&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="11281:1-11281:8" dir="auto">
+</div>
+<h2 data-sourcepos="14041:1-14041:8" dir="auto">
<a id="user-content-video" class="anchor" href="#video" aria-hidden="true"></a>Video</h2>
-<p data-sourcepos="11283:1-11284:109" dir="auto">See
+<p data-sourcepos="14043:1-14044:109" dir="auto">See
<a href="https://docs.gitlab.com/ee/user/markdown.html#videos" rel="nofollow noreferrer noopener" target="_blank">videos</a> in the GitLab Flavored Markdown documentation.</p>
-<p data-sourcepos="11286:1-11288:64" dir="auto">GLFM renders image elements as a video player as long as the resource’s file extension is
+<p data-sourcepos="14046:1-14048:64" dir="auto">GLFM renders image elements as a video player as long as the resource’s file extension is
one of the following supported video extensions <code>.mp4</code>, <code>.m4v</code>, <code>.mov</code>, <code>.webm</code>, and <code>.ogv</code>.
Videos ignore the alternative text part of an image declaration.</p>
+<div>
+<div><a href="#example-690">Example 690</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11291:1-11293:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![video](video.m4v "video title")</span></code></pre>
+<pre data-sourcepos="14054:1-14056:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![video](video.m4v "video title")</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11295:1-11297:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;video src="video.m4v" title="video title"&gt;&lt;/video&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="14058:1-14060:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;video src="video.m4v" title="video title"&gt;&lt;/video&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="11299:1-11299:41" dir="auto">Reference definitions work video as well:</p>
+</div>
+<p data-sourcepos="14063:1-14063:41" dir="auto">Reference definitions work video as well:</p>
+<div>
+<div><a href="#example-691">Example 691</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11301:1-11305:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[video]: video.mov "video title"</span>
+<pre data-sourcepos="14068:1-14072:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[video]: video.mov "video title"</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">![video][video]</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11307:1-11309:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;video src="video.mov" title="video title"&gt;&lt;/video&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="14074:1-14076:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;video src="video.mov" title="video title"&gt;&lt;/video&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="11311:1-11311:41" dir="auto">
+</div>
+<h2 data-sourcepos="14079:1-14079:41" dir="auto">
<a id="user-content-markdown-preview-api-request-overrides" class="anchor" href="#markdown-preview-api-request-overrides" aria-hidden="true"></a>Markdown Preview API Request Overrides</h2>
-<p data-sourcepos="11313:1-11315:42" dir="auto">This section contains examples of all controllers which use <code>PreviewMarkdown</code> module
+<p data-sourcepos="14081:1-14083:42" dir="auto">This section contains examples of all controllers which use <code>PreviewMarkdown</code> module
and use different <code>markdown_context_params</code>. They exercise the various <code>preview_markdown</code>
endpoints via <code>glfm_example_metadata.yml</code>.</p>
-<p data-sourcepos="11318:1-11318:75" dir="auto"><code>preview_markdown</code> exercising <code>groups</code> API endpoint and <code>UploadLinkFilter</code>:</p>
+<p data-sourcepos="14086:1-14086:75" dir="auto"><code>preview_markdown</code> exercising <code>groups</code> API endpoint and <code>UploadLinkFilter</code>:</p>
+<div>
+<div><a href="#example-692">Example 692</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11320:1-11322:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[groups-test-file](/uploads/groups-test-file)</span></code></pre>
+<pre data-sourcepos="14091:1-14093:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[groups-test-file](/uploads/groups-test-file)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11324:1-11326:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="groups-test-file"&gt;groups-test-file&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="14095:1-14097:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="groups-test-file"&gt;groups-test-file&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="11328:1-11328:81" dir="auto"><code>preview_markdown</code> exercising <code>projects</code> API endpoint and <code>RepositoryLinkFilter</code>:</p>
+</div>
+<p data-sourcepos="14100:1-14100:81" dir="auto"><code>preview_markdown</code> exercising <code>projects</code> API endpoint and <code>RepositoryLinkFilter</code>:</p>
+<div>
+<div><a href="#example-693">Example 693</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11330:1-11332:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[projects-test-file](projects-test-file)</span></code></pre>
+<pre data-sourcepos="14105:1-14107:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[projects-test-file](projects-test-file)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11334:1-11336:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="projects-test-file"&gt;projects-test-file&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="14109:1-14111:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="projects-test-file"&gt;projects-test-file&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="11338:1-11338:83" dir="auto"><code>preview_markdown</code> exercising <code>projects</code> API endpoint and <code>SnippetReferenceFilter</code>:</p>
+</div>
+<p data-sourcepos="14114:1-14114:83" dir="auto"><code>preview_markdown</code> exercising <code>projects</code> API endpoint and <code>SnippetReferenceFilter</code>:</p>
+<div>
+<div><a href="#example-694">Example 694</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11340:1-11342:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This project snippet ID reference IS filtered: $88888</span></code></pre>
+<pre data-sourcepos="14119:1-14121:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This project snippet ID reference IS filtered: $88888</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11344:1-11346:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;This project snippet ID reference IS filtered: $88888&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="14123:1-14125:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;This project snippet ID reference IS filtered: $88888&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="11348:1-11351:50" dir="auto"><code>preview_markdown</code> exercising personal (non-project) <code>snippets</code> API endpoint. This is
+</div>
+<p data-sourcepos="14128:1-14131:50" dir="auto"><code>preview_markdown</code> exercising personal (non-project) <code>snippets</code> API endpoint. This is
only used by the comment field on personal snippets. It has no unique custom markdown
extension behavior, and specifically does not render snippet references via
<code>SnippetReferenceFilter</code>, even if the ID is valid.</p>
+<div>
+<div><a href="#example-695">Example 695</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11353:1-11355:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This personal snippet ID reference is not filtered: $99999</span></code></pre>
+<pre data-sourcepos="14136:1-14138:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This personal snippet ID reference is not filtered: $99999</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11357:1-11359:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;This personal snippet ID reference is not filtered: $99999&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="14140:1-14142:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;This personal snippet ID reference is not filtered: $99999&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="11361:1-11361:80" dir="auto"><code>preview_markdown</code> exercising project <code>wikis</code> API endpoint and <code>WikiLinkFilter</code>:</p>
+</div>
+<p data-sourcepos="14145:1-14145:80" dir="auto"><code>preview_markdown</code> exercising project <code>wikis</code> API endpoint and <code>WikiLinkFilter</code>:</p>
+<div>
+<div><a href="#example-696">Example 696</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11363:1-11365:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[project-wikis-test-file](project-wikis-test-file)</span></code></pre>
+<pre data-sourcepos="14150:1-14152:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[project-wikis-test-file](project-wikis-test-file)</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11367:1-11369:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="project-wikis-test-file"&gt;project-wikis-test-file&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="14154:1-14156:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="project-wikis-test-file"&gt;project-wikis-test-file&lt;/a&gt;&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="11371:1-11372:63" dir="auto"><code>preview_markdown</code> exercising group <code>wikis</code> API endpoint and <code>WikiLinkFilter</code>. This example
+</div>
+<p data-sourcepos="14159:1-14160:63" dir="auto"><code>preview_markdown</code> exercising group <code>wikis</code> API endpoint and <code>WikiLinkFilter</code>. This example
also requires an EE license enabling the <code>group_wikis</code> feature:</p>
+<div>
+<div><a href="#example-697">Example 697</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14165:1-14167:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[group-wikis-test-file](group-wikis-test-file)</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14169:1-14171:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="group-wikis-test-file"&gt;group-wikis-test-file&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h2 data-sourcepos="14174:1-14174:34" dir="auto">
+<a id="user-content-migrated-golden-master-examples" class="anchor" href="#migrated-golden-master-examples" aria-hidden="true"></a>Migrated golden master examples</h2>
+<h3 data-sourcepos="14176:1-14176:30" dir="auto">
+<a id="user-content-attachment_image_for_group" class="anchor" href="#attachment_image_for_group" aria-hidden="true"></a>attachment_image_for_group</h3>
+<div>
+<div><a href="#example-698">Example 698</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14181:1-14183:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png)</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14185:1-14187:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14190:1-14190:32" dir="auto">
+<a id="user-content-attachment_image_for_project" class="anchor" href="#attachment_image_for_project" aria-hidden="true"></a>attachment_image_for_project</h3>
+<div>
+<div><a href="#example-699">Example 699</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14195:1-14197:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png)</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14199:1-14201:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14204:1-14204:37" dir="auto">
+<a id="user-content-attachment_image_for_project_wiki" class="anchor" href="#attachment_image_for_project_wiki" aria-hidden="true"></a>attachment_image_for_project_wiki</h3>
+<div>
+<div><a href="#example-700">Example 700</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14209:1-14211:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![test-file](test-file.png)</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14213:1-14215:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14218:1-14218:29" dir="auto">
+<a id="user-content-attachment_link_for_group" class="anchor" href="#attachment_link_for_group" aria-hidden="true"></a>attachment_link_for_group</h3>
+<div>
+<div><a href="#example-701">Example 701</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14223:1-14225:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip)</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14227:1-14229:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14232:1-14232:31" dir="auto">
+<a id="user-content-attachment_link_for_project" class="anchor" href="#attachment_link_for_project" aria-hidden="true"></a>attachment_link_for_project</h3>
+<div>
+<div><a href="#example-702">Example 702</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14237:1-14239:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip)</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14241:1-14243:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14246:1-14246:36" dir="auto">
+<a id="user-content-attachment_link_for_project_wiki" class="anchor" href="#attachment_link_for_project_wiki" aria-hidden="true"></a>attachment_link_for_project_wiki</h3>
+<div>
+<div><a href="#example-703">Example 703</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14251:1-14253:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[test-file](test-file.zip)</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14255:1-14257:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14260:1-14260:34" dir="auto">
+<a id="user-content-attachment_link_for_group_wiki" class="anchor" href="#attachment_link_for_group_wiki" aria-hidden="true"></a>attachment_link_for_group_wiki</h3>
+<div>
+<div><a href="#example-704">Example 704</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14265:1-14267:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[test-file](test-file.zip)</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14269:1-14271:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14274:1-14274:9" dir="auto">
+<a id="user-content-audio-1" class="anchor" href="#audio-1" aria-hidden="true"></a>audio</h3>
+<div>
+<div><a href="#example-705">Example 705</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14279:1-14281:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![Sample Audio](https://gitlab.com/gitlab.mp3)</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14283:1-14285:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14288:1-14288:28" dir="auto">
+<a id="user-content-audio_and_video_in_lists" class="anchor" href="#audio_and_video_in_lists" aria-hidden="true"></a>audio_and_video_in_lists</h3>
+<div>
+<div><a href="#example-706">Example 706</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14293:1-14303:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* ![Sample Audio](https://gitlab.com/1.mp3)</span>
+<span id="LC2" class="line" lang="plaintext">* ![Sample Video](https://gitlab.com/2.mp4)</span>
+<span id="LC3" class="line" lang="plaintext"></span>
+<span id="LC4" class="line" lang="plaintext">1. ![Sample Video](https://gitlab.com/1.mp4)</span>
+<span id="LC5" class="line" lang="plaintext">2. ![Sample Audio](https://gitlab.com/2.mp3)</span>
+<span id="LC6" class="line" lang="plaintext"></span>
+<span id="LC7" class="line" lang="plaintext">* [x] ![Sample Audio](https://gitlab.com/1.mp3)</span>
+<span id="LC8" class="line" lang="plaintext">* [x] ![Sample Audio](https://gitlab.com/2.mp3)</span>
+<span id="LC9" class="line" lang="plaintext">* [x] ![Sample Video](https://gitlab.com/3.mp4)</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14305:1-14307:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14310:1-14310:14" dir="auto">
+<a id="user-content-blockquote" class="anchor" href="#blockquote" aria-hidden="true"></a>blockquote</h3>
+<div>
+<div><a href="#example-707">Example 707</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14315:1-14319:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&gt; This is a blockquote</span>
+<span id="LC2" class="line" lang="plaintext">&gt;</span>
+<span id="LC3" class="line" lang="plaintext">&gt; This is another one</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14321:1-14323:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14326:1-14326:8" dir="auto">
+<a id="user-content-bold" class="anchor" href="#bold" aria-hidden="true"></a>bold</h3>
+<div>
+<div><a href="#example-708">Example 708</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14331:1-14333:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">**bold**</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14335:1-14337:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14340:1-14340:23" dir="auto">
+<a id="user-content-bullet_list_style_1" class="anchor" href="#bullet_list_style_1" aria-hidden="true"></a>bullet_list_style_1</h3>
+<div>
+<div><a href="#example-709">Example 709</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14345:1-14349:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* list item 1</span>
+<span id="LC2" class="line" lang="plaintext">* list item 2</span>
+<span id="LC3" class="line" lang="plaintext"> * embedded list item 3</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14351:1-14353:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14356:1-14356:23" dir="auto">
+<a id="user-content-bullet_list_style_2" class="anchor" href="#bullet_list_style_2" aria-hidden="true"></a>bullet_list_style_2</h3>
+<div>
+<div><a href="#example-710">Example 710</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14361:1-14365:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- list item 1</span>
+<span id="LC2" class="line" lang="plaintext">- list item 2</span>
+<span id="LC3" class="line" lang="plaintext"> * embedded list item 3</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14367:1-14369:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14372:1-14372:23" dir="auto">
+<a id="user-content-bullet_list_style_3" class="anchor" href="#bullet_list_style_3" aria-hidden="true"></a>bullet_list_style_3</h3>
+<div>
+<div><a href="#example-711">Example 711</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11374:1-11376:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[group-wikis-test-file](group-wikis-test-file)</span></code></pre>
+<pre data-sourcepos="14377:1-14381:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">+ list item 1</span>
+<span id="LC2" class="line" lang="plaintext">+ list item 2</span>
+<span id="LC3" class="line" lang="plaintext"> - embedded list item 3</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="11378:1-11380:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;a href="group-wikis-test-file"&gt;group-wikis-test-file&lt;/a&gt;&lt;/p&gt;</span></code></pre>
+<pre data-sourcepos="14383:1-14385:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<h3 data-sourcepos="14388:1-14388:25" dir="auto">
+<a id="user-content-code_block_javascript" class="anchor" href="#code_block_javascript" aria-hidden="true"></a>code_block_javascript</h3>
+<div>
+<div><a href="#example-712">Example 712</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14393:1-14397:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```javascript</span>
+<span id="LC2" class="line" lang="plaintext"> console.log('hello world')</span>
+<span id="LC3" class="line" lang="plaintext">```</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14399:1-14401:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14404:1-14404:24" dir="auto">
+<a id="user-content-code_block_plaintext" class="anchor" href="#code_block_plaintext" aria-hidden="true"></a>code_block_plaintext</h3>
+<div>
+<div><a href="#example-713">Example 713</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14409:1-14413:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```</span>
+<span id="LC2" class="line" lang="plaintext"> plaintext</span>
+<span id="LC3" class="line" lang="plaintext">```</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14415:1-14417:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14420:1-14420:22" dir="auto">
+<a id="user-content-code_block_unknown" class="anchor" href="#code_block_unknown" aria-hidden="true"></a>code_block_unknown</h3>
+<div>
+<div><a href="#example-714">Example 714</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14425:1-14429:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```foobar</span>
+<span id="LC2" class="line" lang="plaintext"> custom_language = &gt;&gt; this &lt;&lt;</span>
+<span id="LC3" class="line" lang="plaintext">```</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14431:1-14433:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14436:1-14436:15" dir="auto">
+<a id="user-content-color_chips" class="anchor" href="#color_chips" aria-hidden="true"></a>color_chips</h3>
+<div>
+<div><a href="#example-715">Example 715</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14441:1-14451:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- `#F00`</span>
+<span id="LC2" class="line" lang="plaintext">- `#F00A`</span>
+<span id="LC3" class="line" lang="plaintext">- `#FF0000`</span>
+<span id="LC4" class="line" lang="plaintext">- `#FF0000AA`</span>
+<span id="LC5" class="line" lang="plaintext">- `RGB(0,255,0)`</span>
+<span id="LC6" class="line" lang="plaintext">- `RGB(0%,100%,0%)`</span>
+<span id="LC7" class="line" lang="plaintext">- `RGBA(0,255,0,0.3)`</span>
+<span id="LC8" class="line" lang="plaintext">- `HSL(540,70%,50%)`</span>
+<span id="LC9" class="line" lang="plaintext">- `HSLA(540,70%,50%,0.3)`</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14453:1-14455:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14458:1-14458:20" dir="auto">
+<a id="user-content-description_list" class="anchor" href="#description_list" aria-hidden="true"></a>description_list</h3>
+<div>
+<div><a href="#example-716">Example 716</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14463:1-14480:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;dl&gt;</span>
+<span id="LC2" class="line" lang="plaintext">&lt;dt&gt;Frog&lt;/dt&gt;</span>
+<span id="LC3" class="line" lang="plaintext">&lt;dd&gt;Wet green thing&lt;/dd&gt;</span>
+<span id="LC4" class="line" lang="plaintext">&lt;dt&gt;Rabbit&lt;/dt&gt;</span>
+<span id="LC5" class="line" lang="plaintext">&lt;dd&gt;Warm fluffy thing&lt;/dd&gt;</span>
+<span id="LC6" class="line" lang="plaintext">&lt;dt&gt;Punt&lt;/dt&gt;</span>
+<span id="LC7" class="line" lang="plaintext">&lt;dd&gt;Kick a ball&lt;/dd&gt;</span>
+<span id="LC8" class="line" lang="plaintext">&lt;dd&gt;Take a bet&lt;/dd&gt;</span>
+<span id="LC9" class="line" lang="plaintext">&lt;dt&gt;Color&lt;/dt&gt;</span>
+<span id="LC10" class="line" lang="plaintext">&lt;dt&gt;Colour&lt;/dt&gt;</span>
+<span id="LC11" class="line" lang="plaintext">&lt;dd&gt;</span>
+<span id="LC12" class="line" lang="plaintext"></span>
+<span id="LC13" class="line" lang="plaintext">Any hue except _white_ or **black**</span>
+<span id="LC14" class="line" lang="plaintext"></span>
+<span id="LC15" class="line" lang="plaintext">&lt;/dd&gt;</span>
+<span id="LC16" class="line" lang="plaintext">&lt;/dl&gt;</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14482:1-14484:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14487:1-14487:11" dir="auto">
+<a id="user-content-details" class="anchor" href="#details" aria-hidden="true"></a>details</h3>
+<div>
+<div><a href="#example-717">Example 717</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14492:1-14500:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;details&gt;</span>
+<span id="LC2" class="line" lang="plaintext">&lt;summary&gt;This is the visible summary of the collapsible section&lt;/summary&gt;</span>
+<span id="LC3" class="line" lang="plaintext"></span>
+<span id="LC4" class="line" lang="plaintext">1. collapsed markdown</span>
+<span id="LC5" class="line" lang="plaintext">2. more collapsed markdown</span>
+<span id="LC6" class="line" lang="plaintext"></span>
+<span id="LC7" class="line" lang="plaintext">&lt;/details&gt;</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14502:1-14504:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14507:1-14507:25" dir="auto">
+<a id="user-content-diagram_kroki_nomnoml" class="anchor" href="#diagram_kroki_nomnoml" aria-hidden="true"></a>diagram_kroki_nomnoml</h3>
+<div>
+<div><a href="#example-718">Example 718</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14512:1-14524:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```nomnoml</span>
+<span id="LC2" class="line" lang="plaintext"> #stroke: #a86128</span>
+<span id="LC3" class="line" lang="plaintext"> [&lt;frame&gt;Decorator pattern|</span>
+<span id="LC4" class="line" lang="plaintext"> [&lt;abstract&gt;Component||+ operation()]</span>
+<span id="LC5" class="line" lang="plaintext"> [Client] depends --&gt; [Component]</span>
+<span id="LC6" class="line" lang="plaintext"> [Decorator|- next: Component]</span>
+<span id="LC7" class="line" lang="plaintext"> [Decorator] decorates -- [ConcreteComponent]</span>
+<span id="LC8" class="line" lang="plaintext"> [Component] &lt;:- [Decorator]</span>
+<span id="LC9" class="line" lang="plaintext"> [Component] &lt;:- [ConcreteComponent]</span>
+<span id="LC10" class="line" lang="plaintext"> ]</span>
+<span id="LC11" class="line" lang="plaintext">```</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14526:1-14528:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14531:1-14531:20" dir="auto">
+<a id="user-content-diagram_plantuml" class="anchor" href="#diagram_plantuml" aria-hidden="true"></a>diagram_plantuml</h3>
+<div>
+<div><a href="#example-719">Example 719</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14536:1-14544:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```plantuml</span>
+<span id="LC2" class="line" lang="plaintext"> Alice -&gt; Bob: Authentication Request</span>
+<span id="LC3" class="line" lang="plaintext"> Bob --&gt; Alice: Authentication Response</span>
+<span id="LC4" class="line" lang="plaintext"></span>
+<span id="LC5" class="line" lang="plaintext"> Alice -&gt; Bob: Another authentication Request</span>
+<span id="LC6" class="line" lang="plaintext"> Alice &lt;-- Bob: Another authentication Response</span>
+<span id="LC7" class="line" lang="plaintext">```</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14546:1-14548:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14551:1-14551:28" dir="auto">
+<a id="user-content-diagram_plantuml_unicode" class="anchor" href="#diagram_plantuml_unicode" aria-hidden="true"></a>diagram_plantuml_unicode</h3>
+<div>
+<div><a href="#example-720">Example 720</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14556:1-14560:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">```plantuml</span>
+<span id="LC2" class="line" lang="plaintext">A -&gt; B : Text with norwegian characters: æøå</span>
+<span id="LC3" class="line" lang="plaintext">```</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14562:1-14564:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14567:1-14567:7" dir="auto">
+<a id="user-content-div" class="anchor" href="#div" aria-hidden="true"></a>div</h3>
+<div>
+<div><a href="#example-721">Example 721</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14572:1-14579:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;div&gt;plain text&lt;/div&gt;</span>
+<span id="LC2" class="line" lang="plaintext">&lt;div&gt;</span>
+<span id="LC3" class="line" lang="plaintext"></span>
+<span id="LC4" class="line" lang="plaintext">just a plain ol' div, not much to _expect_!</span>
+<span id="LC5" class="line" lang="plaintext"></span>
+<span id="LC6" class="line" lang="plaintext">&lt;/div&gt;</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14581:1-14583:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14586:1-14586:9" dir="auto">
+<a id="user-content-emoji" class="anchor" href="#emoji" aria-hidden="true"></a>emoji</h3>
+<div>
+<div><a href="#example-722">Example 722</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14591:1-14593:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">:sparkles: :heart: :100:</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14595:1-14597:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14600:1-14600:12" dir="auto">
+<a id="user-content-emphasis" class="anchor" href="#emphasis" aria-hidden="true"></a>emphasis</h3>
+<div>
+<div><a href="#example-723">Example 723</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14605:1-14607:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_emphasized text_</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14609:1-14611:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14614:1-14614:10" dir="auto">
+<a id="user-content-figure" class="anchor" href="#figure" aria-hidden="true"></a>figure</h3>
+<div>
+<div><a href="#example-724">Example 724</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14619:1-14636:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;figure&gt;</span>
+<span id="LC2" class="line" lang="plaintext"></span>
+<span id="LC3" class="line" lang="plaintext">![Elephant at sunset](elephant-sunset.jpg)</span>
+<span id="LC4" class="line" lang="plaintext"></span>
+<span id="LC5" class="line" lang="plaintext">&lt;figcaption&gt;An elephant at sunset&lt;/figcaption&gt;</span>
+<span id="LC6" class="line" lang="plaintext">&lt;/figure&gt;</span>
+<span id="LC7" class="line" lang="plaintext">&lt;figure&gt;</span>
+<span id="LC8" class="line" lang="plaintext"></span>
+<span id="LC9" class="line" lang="plaintext">![A crocodile wearing crocs](croc-crocs.jpg)</span>
+<span id="LC10" class="line" lang="plaintext"></span>
+<span id="LC11" class="line" lang="plaintext">&lt;figcaption&gt;</span>
+<span id="LC12" class="line" lang="plaintext"></span>
+<span id="LC13" class="line" lang="plaintext">A crocodile wearing _crocs_!</span>
+<span id="LC14" class="line" lang="plaintext"></span>
+<span id="LC15" class="line" lang="plaintext">&lt;/figcaption&gt;</span>
+<span id="LC16" class="line" lang="plaintext">&lt;/figure&gt;</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14638:1-14640:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14643:1-14643:13" dir="auto">
+<a id="user-content-footnotes-1" class="anchor" href="#footnotes-1" aria-hidden="true"></a>footnotes</h3>
+<div>
+<div><a href="#example-725">Example 725</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14648:1-14656:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">A footnote reference tag looks like this: [^1]</span>
+<span id="LC2" class="line" lang="plaintext"></span>
+<span id="LC3" class="line" lang="plaintext">This reference tag is a mix of letters and numbers. [^footnote]</span>
+<span id="LC4" class="line" lang="plaintext"></span>
+<span id="LC5" class="line" lang="plaintext">[^1]: This is the text inside a footnote.</span>
+<span id="LC6" class="line" lang="plaintext"></span>
+<span id="LC7" class="line" lang="plaintext">[^footnote]: This is another footnote.</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14658:1-14660:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14663:1-14663:20" dir="auto">
+<a id="user-content-frontmatter_json" class="anchor" href="#frontmatter_json" aria-hidden="true"></a>frontmatter_json</h3>
+<div>
+<div><a href="#example-726">Example 726</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14668:1-14674:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">;;;</span>
+<span id="LC2" class="line" lang="plaintext">{</span>
+<span id="LC3" class="line" lang="plaintext"> "title": "Page title"</span>
+<span id="LC4" class="line" lang="plaintext">}</span>
+<span id="LC5" class="line" lang="plaintext">;;;</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14676:1-14678:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14681:1-14681:20" dir="auto">
+<a id="user-content-frontmatter_toml" class="anchor" href="#frontmatter_toml" aria-hidden="true"></a>frontmatter_toml</h3>
+<div>
+<div><a href="#example-727">Example 727</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14686:1-14690:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">+++</span>
+<span id="LC2" class="line" lang="plaintext">title = "Page title"</span>
+<span id="LC3" class="line" lang="plaintext">+++</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14692:1-14694:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14697:1-14697:20" dir="auto">
+<a id="user-content-frontmatter_yaml" class="anchor" href="#frontmatter_yaml" aria-hidden="true"></a>frontmatter_yaml</h3>
+<div>
+<div><a href="#example-728">Example 728</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14702:1-14706:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">---</span>
+<span id="LC2" class="line" lang="plaintext">title: Page title</span>
+<span id="LC3" class="line" lang="plaintext">---</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14708:1-14710:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14713:1-14713:14" dir="auto">
+<a id="user-content-hard_break" class="anchor" href="#hard_break" aria-hidden="true"></a>hard_break</h3>
+<div>
+<div><a href="#example-729">Example 729</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14718:1-14721:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This is a line after a\</span>
+<span id="LC2" class="line" lang="plaintext">hard break</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14723:1-14725:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14728:1-14728:12" dir="auto">
+<a id="user-content-headings" class="anchor" href="#headings" aria-hidden="true"></a>headings</h3>
+<div>
+<div><a href="#example-730">Example 730</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14733:1-14745:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"># Heading 1</span>
+<span id="LC2" class="line" lang="plaintext"></span>
+<span id="LC3" class="line" lang="plaintext">## Heading 2</span>
+<span id="LC4" class="line" lang="plaintext"></span>
+<span id="LC5" class="line" lang="plaintext">### Heading 3</span>
+<span id="LC6" class="line" lang="plaintext"></span>
+<span id="LC7" class="line" lang="plaintext">#### Heading 4</span>
+<span id="LC8" class="line" lang="plaintext"></span>
+<span id="LC9" class="line" lang="plaintext">##### Heading 5</span>
+<span id="LC10" class="line" lang="plaintext"></span>
+<span id="LC11" class="line" lang="plaintext">###### Heading 6</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14747:1-14749:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14752:1-14752:19" dir="auto">
+<a id="user-content-horizontal_rule" class="anchor" href="#horizontal_rule" aria-hidden="true"></a>horizontal_rule</h3>
+<div>
+<div><a href="#example-731">Example 731</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14757:1-14759:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">---</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14761:1-14763:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14766:1-14766:14" dir="auto">
+<a id="user-content-html_marks" class="anchor" href="#html_marks" aria-hidden="true"></a>html_marks</h3>
+<div>
+<div><a href="#example-732">Example 732</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14771:1-14787:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* Content editor is ~~great~~&lt;ins&gt;amazing&lt;/ins&gt;.</span>
+<span id="LC2" class="line" lang="plaintext">* If the changes &lt;abbr title="Looks good to merge"&gt;LGTM&lt;/abbr&gt;, please &lt;abbr title="Merge when pipeline succeeds"&gt;MWPS&lt;/abbr&gt;.</span>
+<span id="LC3" class="line" lang="plaintext">* The English song &lt;q&gt;Oh I do like to be beside the seaside&lt;/q&gt; looks like this in Hebrew: &lt;span dir="rtl"&gt;××”, ×× ×™ ×והב להיות ליד חוף ×”×™×&lt;/span&gt;. In the computer's memory, this is stored as &lt;bdo dir="ltr"&gt;××”, ×× ×™ ×והב להיות ליד חוף ×”×™×&lt;/bdo&gt;.</span>
+<span id="LC4" class="line" lang="plaintext">* &lt;cite&gt;The Scream&lt;/cite&gt; by Edvard Munch. Painted in 1893.</span>
+<span id="LC5" class="line" lang="plaintext">* &lt;dfn&gt;HTML&lt;/dfn&gt; is the standard markup language for creating web pages.</span>
+<span id="LC6" class="line" lang="plaintext">* Do not forget to buy &lt;mark&gt;milk&lt;/mark&gt; today.</span>
+<span id="LC7" class="line" lang="plaintext">* This is a paragraph and &lt;small&gt;smaller text goes here&lt;/small&gt;.</span>
+<span id="LC8" class="line" lang="plaintext">* The concert starts at &lt;time datetime="20:00"&gt;20:00&lt;/time&gt; and you'll be able to enjoy the band for at least &lt;time datetime="PT2H30M"&gt;2h 30m&lt;/time&gt;.</span>
+<span id="LC9" class="line" lang="plaintext">* Press &lt;kbd&gt;Ctrl&lt;/kbd&gt; + &lt;kbd&gt;C&lt;/kbd&gt; to copy text (Windows).</span>
+<span id="LC10" class="line" lang="plaintext">* WWF's goal is to: &lt;q&gt;Build a future where people live in harmony with nature.&lt;/q&gt; We hope they succeed.</span>
+<span id="LC11" class="line" lang="plaintext">* The error occurred was: &lt;samp&gt;Keyboard not found. Press F1 to continue.&lt;/samp&gt;</span>
+<span id="LC12" class="line" lang="plaintext">* The area of a triangle is: 1/2 x &lt;var&gt;b&lt;/var&gt; x &lt;var&gt;h&lt;/var&gt;, where &lt;var&gt;b&lt;/var&gt; is the base, and &lt;var&gt;h&lt;/var&gt; is the vertical height.</span>
+<span id="LC13" class="line" lang="plaintext">* &lt;ruby&gt;æ¼¢&lt;rt&gt;ã„ㄢˋ&lt;/rt&gt;&lt;/ruby&gt;</span>
+<span id="LC14" class="line" lang="plaintext">* C&lt;sub&gt;7&lt;/sub&gt;H&lt;sub&gt;16&lt;/sub&gt; + O&lt;sub&gt;2&lt;/sub&gt; → CO&lt;sub&gt;2&lt;/sub&gt; + H&lt;sub&gt;2&lt;/sub&gt;O</span>
+<span id="LC15" class="line" lang="plaintext">* The **Pythagorean theorem** is often expressed as &lt;var&gt;a&lt;sup&gt;2&lt;/sup&gt;&lt;/var&gt; + &lt;var&gt;b&lt;sup&gt;2&lt;/sup&gt;&lt;/var&gt; = &lt;var&gt;c&lt;sup&gt;2&lt;/sup&gt;&lt;/var&gt;</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14789:1-14791:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14794:1-14794:9" dir="auto">
+<a id="user-content-image" class="anchor" href="#image" aria-hidden="true"></a>image</h3>
+<div>
+<div><a href="#example-733">Example 733</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14799:1-14801:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![alt text](https://gitlab.com/logo.png)</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14803:1-14805:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14808:1-14808:15" dir="auto">
+<a id="user-content-inline_code" class="anchor" href="#inline_code" aria-hidden="true"></a>inline_code</h3>
+<div>
+<div><a href="#example-734">Example 734</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14813:1-14815:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">`code`</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14817:1-14819:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14822:1-14822:15" dir="auto">
+<a id="user-content-inline_diff" class="anchor" href="#inline_diff" aria-hidden="true"></a>inline_diff</h3>
+<div>
+<div><a href="#example-735">Example 735</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14827:1-14830:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* {-deleted-}</span>
+<span id="LC2" class="line" lang="plaintext">* {+added+}</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14832:1-14834:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14837:1-14837:9" dir="auto">
+<a id="user-content-label" class="anchor" href="#label" aria-hidden="true"></a>label</h3>
+<div>
+<div><a href="#example-736">Example 736</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14842:1-14844:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~bug</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14846:1-14848:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14851:1-14851:8" dir="auto">
+<a id="user-content-link" class="anchor" href="#link" aria-hidden="true"></a>link</h3>
+<div>
+<div><a href="#example-737">Example 737</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14856:1-14858:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[GitLab](https://gitlab.com)</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14860:1-14862:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14865:1-14865:8" dir="auto">
+<a id="user-content-math" class="anchor" href="#math" aria-hidden="true"></a>math</h3>
+<div>
+<div><a href="#example-738">Example 738</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14870:1-14878:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This math is inline $`a^2+b^2=c^2`$.</span>
+<span id="LC2" class="line" lang="plaintext"></span>
+<span id="LC3" class="line" lang="plaintext">This is on a separate line:</span>
+<span id="LC4" class="line" lang="plaintext"></span>
+<span id="LC5" class="line" lang="plaintext">```math</span>
+<span id="LC6" class="line" lang="plaintext">a^2+b^2=c^2</span>
+<span id="LC7" class="line" lang="plaintext">```</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14880:1-14882:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14885:1-14885:16" dir="auto">
+<a id="user-content-ordered_list" class="anchor" href="#ordered_list" aria-hidden="true"></a>ordered_list</h3>
+<div>
+<div><a href="#example-739">Example 739</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14890:1-14894:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. list item 1</span>
+<span id="LC2" class="line" lang="plaintext">2. list item 2</span>
+<span id="LC3" class="line" lang="plaintext">3. list item 3</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14896:1-14898:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14901:1-14901:33" dir="auto">
+<a id="user-content-ordered_list_with_start_order" class="anchor" href="#ordered_list_with_start_order" aria-hidden="true"></a>ordered_list_with_start_order</h3>
+<div>
+<div><a href="#example-740">Example 740</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14906:1-14910:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">134. list item 1</span>
+<span id="LC2" class="line" lang="plaintext">135. list item 2</span>
+<span id="LC3" class="line" lang="plaintext">136. list item 3</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14912:1-14914:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14917:1-14917:21" dir="auto">
+<a id="user-content-ordered_task_list" class="anchor" href="#ordered_task_list" aria-hidden="true"></a>ordered_task_list</h3>
+<div>
+<div><a href="#example-741">Example 741</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14922:1-14929:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">1. [x] hello</span>
+<span id="LC2" class="line" lang="plaintext">2. [x] world</span>
+<span id="LC3" class="line" lang="plaintext">3. [ ] example</span>
+<span id="LC4" class="line" lang="plaintext"> 1. [ ] of nested</span>
+<span id="LC5" class="line" lang="plaintext"> 1. [x] task list</span>
+<span id="LC6" class="line" lang="plaintext"> 2. [ ] items</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14931:1-14933:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14936:1-14936:32" dir="auto">
+<a id="user-content-ordered_task_list_with_order" class="anchor" href="#ordered_task_list_with_order" aria-hidden="true"></a>ordered_task_list_with_order</h3>
+<div>
+<div><a href="#example-742">Example 742</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14941:1-14945:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">4893. [x] hello</span>
+<span id="LC2" class="line" lang="plaintext">4894. [x] world</span>
+<span id="LC3" class="line" lang="plaintext">4895. [ ] example</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14947:1-14949:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14952:1-14952:30" dir="auto">
+<a id="user-content-reference_for_project_wiki" class="anchor" href="#reference_for_project_wiki" aria-hidden="true"></a>reference_for_project_wiki</h3>
+<div>
+<div><a href="#example-743">Example 743</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14957:1-14959:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Hi @gfm_user - thank you for reporting this ~"UX bug" (#1) we hope to fix it in %1.1 as part of !1</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14961:1-14963:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14966:1-14966:10" dir="auto">
+<a id="user-content-strike" class="anchor" href="#strike" aria-hidden="true"></a>strike</h3>
+<div>
+<div><a href="#example-744">Example 744</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14971:1-14973:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">~~del~~</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14975:1-14977:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14980:1-14980:9" dir="auto">
+<a id="user-content-table" class="anchor" href="#table" aria-hidden="true"></a>table</h3>
+<div>
+<div><a href="#example-745">Example 745</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14985:1-14992:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">| header | header |</span>
+<span id="LC2" class="line" lang="plaintext">|--------|--------|</span>
+<span id="LC3" class="line" lang="plaintext">| `code` | cell with **bold** |</span>
+<span id="LC4" class="line" lang="plaintext">| ~~strike~~ | cell with _italic_ |</span>
+<span id="LC5" class="line" lang="plaintext"></span>
+<span id="LC6" class="line" lang="plaintext"># content after table</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="14994:1-14996:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="14999:1-14999:21" dir="auto">
+<a id="user-content-table_of_contents" class="anchor" href="#table_of_contents" aria-hidden="true"></a>table_of_contents</h3>
+<div>
+<div><a href="#example-746">Example 746</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15004:1-15018:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[_TOC_]]</span>
+<span id="LC2" class="line" lang="plaintext"></span>
+<span id="LC3" class="line" lang="plaintext"># Lorem</span>
+<span id="LC4" class="line" lang="plaintext"></span>
+<span id="LC5" class="line" lang="plaintext">Well, that's just like... your opinion.. man.</span>
+<span id="LC6" class="line" lang="plaintext"></span>
+<span id="LC7" class="line" lang="plaintext">## Ipsum</span>
+<span id="LC8" class="line" lang="plaintext"></span>
+<span id="LC9" class="line" lang="plaintext">### Dolar</span>
+<span id="LC10" class="line" lang="plaintext"></span>
+<span id="LC11" class="line" lang="plaintext"># Sit amit</span>
+<span id="LC12" class="line" lang="plaintext"></span>
+<span id="LC13" class="line" lang="plaintext">### I don't know</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15020:1-15022:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="15025:1-15025:13" dir="auto">
+<a id="user-content-task_list" class="anchor" href="#task_list" aria-hidden="true"></a>task_list</h3>
+<div>
+<div><a href="#example-747">Example 747</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15030:1-15037:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* [x] hello</span>
+<span id="LC2" class="line" lang="plaintext">* [x] world</span>
+<span id="LC3" class="line" lang="plaintext">* [ ] example</span>
+<span id="LC4" class="line" lang="plaintext"> * [ ] of nested</span>
+<span id="LC5" class="line" lang="plaintext"> * [x] task list</span>
+<span id="LC6" class="line" lang="plaintext"> * [ ] items</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15039:1-15041:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="15044:1-15044:9" dir="auto">
+<a id="user-content-video-1" class="anchor" href="#video-1" aria-hidden="true"></a>video</h3>
+<div>
+<div><a href="#example-748">Example 748</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15049:1-15051:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![Sample Video](https://gitlab.com/gitlab.mp4)</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15053:1-15055:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h3 data-sourcepos="15058:1-15058:14" dir="auto">
+<a id="user-content-word_break" class="anchor" href="#word_break" aria-hidden="true"></a>word_break</h3>
+<div>
+<div><a href="#example-749">Example 749</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15063:1-15065:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">Fernstraßen&lt;wbr&gt;bau&lt;wbr&gt;privat&lt;wbr&gt;finanzierungs&lt;wbr&gt;gesetz</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15067:1-15069:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">TODO: Write canonical HTML for this example</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<h2 data-sourcepos="15072:1-15072:19" dir="auto">
+<a id="user-content-image-attributes" class="anchor" href="#image-attributes" aria-hidden="true"></a>Image Attributes</h2>
+<p data-sourcepos="15074:1-15076:46" dir="auto">See
+<a href="https://docs.gitlab.com/ee/user/markdown.html#change-the-image-dimensions" rel="nofollow noreferrer noopener" target="_blank">Change the image dimensions</a>
+in the GitLab Flavored Markdown documentation.</p>
+<p data-sourcepos="15078:1-15079:19" dir="auto">The <code>width</code> and <code>height</code> attributes for an image can be specified directly after
+the image markdown.</p>
+<p data-sourcepos="15081:1-15083:21" dir="auto">General syntax conforms to the
+<a href="https://github.com/jgm/commonmark-hs/blob/master/commonmark-extensions/test/attributes.md" rel="nofollow noreferrer noopener" target="_blank">commonmark-hs attribute syntax</a>
+where it makes sense.</p>
+<div>
+<div><a href="#example-750">Example 750</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15088:1-15090:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![](https://gitlab.com/logo.png){width="100" height="100"}</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15092:1-15094:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="https://gitlab.com/logo.png" width="100" height="100"&gt;&lt;/p&gt;</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<p data-sourcepos="15097:1-15097:41" dir="auto"><code>%</code> and <code>px</code> units may also be specified.</p>
+<div>
+<div><a href="#example-751">Example 751</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15102:1-15104:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![](https://gitlab.com/logo.png){width="100%"}</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15106:1-15108:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="https://gitlab.com/logo.png" width="100%"&gt;&lt;/p&gt;</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<div>
+<div><a href="#example-752">Example 752</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15114:1-15116:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![](https://gitlab.com/logo.png){height="100px"}</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15118:1-15120:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="https://gitlab.com/logo.png" height="100px"&gt;&lt;/p&gt;</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<p data-sourcepos="15123:1-15123:46" dir="auto">Whitespace is tolerated around the delimiters:</p>
+<div>
+<div><a href="#example-753">Example 753</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15128:1-15130:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![](https://gitlab.com/logo.png){ width="100" height="100" }</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15132:1-15134:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="https://gitlab.com/logo.png" width="100" height="100"&gt;&lt;/p&gt;</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+<p data-sourcepos="15137:1-15137:54" dir="auto">Attributes must immediately follow the image markdown.</p>
+<div>
+<div><a href="#example-754">Example 754</a></div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15142:1-15144:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">![](https://gitlab.com/logo.png) {width="100" height="100"}</span></code></pre>
+<copy-code></copy-code>
+</div>
+<div class="gl-relative markdown-code-block js-markdown-code">
+<pre data-sourcepos="15146:1-15148:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;img src="https://gitlab.com/logo.png"&gt; {width="100" height="100"}&lt;/p&gt;</span></code></pre>
+<copy-code></copy-code>
+</div>
+</div>
+
+</body>
+</html>
+
diff --git a/glfm_specification/output_example_snapshots/snapshot_spec.md b/glfm_specification/output_example_snapshots/snapshot_spec.md
index ed96c36a58a..ccee9c1707e 100644
--- a/glfm_specification/output_example_snapshots/snapshot_spec.md
+++ b/glfm_specification/output_example_snapshots/snapshot_spec.md
@@ -1,5 +1,5 @@
---
-title: GitLab Flavored Markdown (GLFM) Spec
+title: GitLab Flavored Markdown Internal Extensions
version: alpha
...
# Preliminaries
@@ -9803,8 +9803,14 @@ See
[table of contents](https://docs.gitlab.com/ee/user/markdown.html#table-of-contents)
in the GitLab Flavored Markdown documentation.
+NOTE: Because of this bug (https://gitlab.com/gitlab-org/gitlab/-/issues/359077),
+we cannot actually include the `TOC` tag with single brackets in backticks
+in this Markdown document, otherwise it would render a table of contents inline
+right here. So, it's been switched to `[` + `TOC` + `]` instead. This can be reverted
+once that bug is fixed.
+
A table of contents is an unordered list that links to subheadings in the document.
-Add either the `[[_TOC_]]` or `[TOC]` tag on its own line.
+Add either the `[[_TOC_]]` tag or the `[` + `TOC` + `]` tag on its own line.
```````````````````````````````` example gitlab
[TOC]
@@ -9984,3 +9990,639 @@ also requires an EE license enabling the `group_wikis` feature:
.
<p><a href="group-wikis-test-file">group-wikis-test-file</a></p>
````````````````````````````````
+
+## Migrated golden master examples
+
+### attachment_image_for_group
+
+```````````````````````````````` example gitlab
+![test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### attachment_image_for_project
+
+```````````````````````````````` example gitlab
+![test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### attachment_image_for_project_wiki
+
+```````````````````````````````` example gitlab
+![test-file](test-file.png)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### attachment_link_for_group
+
+```````````````````````````````` example gitlab
+[test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### attachment_link_for_project
+
+```````````````````````````````` example gitlab
+[test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### attachment_link_for_project_wiki
+
+```````````````````````````````` example gitlab
+[test-file](test-file.zip)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### attachment_link_for_group_wiki
+
+```````````````````````````````` example gitlab
+[test-file](test-file.zip)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### audio
+
+```````````````````````````````` example gitlab
+![Sample Audio](https://gitlab.com/gitlab.mp3)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### audio_and_video_in_lists
+
+```````````````````````````````` example gitlab
+* ![Sample Audio](https://gitlab.com/1.mp3)
+* ![Sample Video](https://gitlab.com/2.mp4)
+
+1. ![Sample Video](https://gitlab.com/1.mp4)
+2. ![Sample Audio](https://gitlab.com/2.mp3)
+
+* [x] ![Sample Audio](https://gitlab.com/1.mp3)
+* [x] ![Sample Audio](https://gitlab.com/2.mp3)
+* [x] ![Sample Video](https://gitlab.com/3.mp4)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### blockquote
+
+```````````````````````````````` example gitlab
+> This is a blockquote
+>
+> This is another one
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### bold
+
+```````````````````````````````` example gitlab
+**bold**
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### bullet_list_style_1
+
+```````````````````````````````` example gitlab
+* list item 1
+* list item 2
+ * embedded list item 3
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### bullet_list_style_2
+
+```````````````````````````````` example gitlab
+- list item 1
+- list item 2
+ * embedded list item 3
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### bullet_list_style_3
+
+```````````````````````````````` example gitlab
++ list item 1
++ list item 2
+ - embedded list item 3
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### code_block_javascript
+
+```````````````````````````````` example gitlab
+```javascript
+ console.log('hello world')
+```
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### code_block_plaintext
+
+```````````````````````````````` example gitlab
+```
+ plaintext
+```
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### code_block_unknown
+
+```````````````````````````````` example gitlab
+```foobar
+ custom_language = >> this <<
+```
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### color_chips
+
+```````````````````````````````` example gitlab
+- `#F00`
+- `#F00A`
+- `#FF0000`
+- `#FF0000AA`
+- `RGB(0,255,0)`
+- `RGB(0%,100%,0%)`
+- `RGBA(0,255,0,0.3)`
+- `HSL(540,70%,50%)`
+- `HSLA(540,70%,50%,0.3)`
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### description_list
+
+```````````````````````````````` example gitlab
+<dl>
+<dt>Frog</dt>
+<dd>Wet green thing</dd>
+<dt>Rabbit</dt>
+<dd>Warm fluffy thing</dd>
+<dt>Punt</dt>
+<dd>Kick a ball</dd>
+<dd>Take a bet</dd>
+<dt>Color</dt>
+<dt>Colour</dt>
+<dd>
+
+Any hue except _white_ or **black**
+
+</dd>
+</dl>
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### details
+
+```````````````````````````````` example gitlab
+<details>
+<summary>This is the visible summary of the collapsible section</summary>
+
+1. collapsed markdown
+2. more collapsed markdown
+
+</details>
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### diagram_kroki_nomnoml
+
+```````````````````````````````` example gitlab
+```nomnoml
+ #stroke: #a86128
+ [<frame>Decorator pattern|
+ [<abstract>Component||+ operation()]
+ [Client] depends --> [Component]
+ [Decorator|- next: Component]
+ [Decorator] decorates -- [ConcreteComponent]
+ [Component] <:- [Decorator]
+ [Component] <:- [ConcreteComponent]
+ ]
+```
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### diagram_plantuml
+
+```````````````````````````````` example gitlab
+```plantuml
+ Alice -> Bob: Authentication Request
+ Bob --> Alice: Authentication Response
+
+ Alice -> Bob: Another authentication Request
+ Alice <-- Bob: Another authentication Response
+```
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### diagram_plantuml_unicode
+
+```````````````````````````````` example gitlab
+```plantuml
+A -> B : Text with norwegian characters: æøå
+```
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### div
+
+```````````````````````````````` example gitlab
+<div>plain text</div>
+<div>
+
+just a plain ol' div, not much to _expect_!
+
+</div>
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### emoji
+
+```````````````````````````````` example gitlab
+:sparkles: :heart: :100:
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### emphasis
+
+```````````````````````````````` example gitlab
+_emphasized text_
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### figure
+
+```````````````````````````````` example gitlab
+<figure>
+
+![Elephant at sunset](elephant-sunset.jpg)
+
+<figcaption>An elephant at sunset</figcaption>
+</figure>
+<figure>
+
+![A crocodile wearing crocs](croc-crocs.jpg)
+
+<figcaption>
+
+A crocodile wearing _crocs_!
+
+</figcaption>
+</figure>
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### footnotes
+
+```````````````````````````````` example gitlab
+A footnote reference tag looks like this: [^1]
+
+This reference tag is a mix of letters and numbers. [^footnote]
+
+[^1]: This is the text inside a footnote.
+
+[^footnote]: This is another footnote.
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### frontmatter_json
+
+```````````````````````````````` example gitlab
+;;;
+{
+ "title": "Page title"
+}
+;;;
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### frontmatter_toml
+
+```````````````````````````````` example gitlab
++++
+title = "Page title"
++++
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### frontmatter_yaml
+
+```````````````````````````````` example gitlab
+---
+title: Page title
+---
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### hard_break
+
+```````````````````````````````` example gitlab
+This is a line after a\
+hard break
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### headings
+
+```````````````````````````````` example gitlab
+# Heading 1
+
+## Heading 2
+
+### Heading 3
+
+#### Heading 4
+
+##### Heading 5
+
+###### Heading 6
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### horizontal_rule
+
+```````````````````````````````` example gitlab
+---
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### html_marks
+
+```````````````````````````````` example gitlab
+* Content editor is ~~great~~<ins>amazing</ins>.
+* If the changes <abbr title="Looks good to merge">LGTM</abbr>, please <abbr title="Merge when pipeline succeeds">MWPS</abbr>.
+* The English song <q>Oh I do like to be beside the seaside</q> looks like this in Hebrew: <span dir="rtl">××”, ×× ×™ ×והב להיות ליד חוף ×”×™×</span>. In the computer's memory, this is stored as <bdo dir="ltr">××”, ×× ×™ ×והב להיות ליד חוף ×”×™×</bdo>.
+* <cite>The Scream</cite> by Edvard Munch. Painted in 1893.
+* <dfn>HTML</dfn> is the standard markup language for creating web pages.
+* Do not forget to buy <mark>milk</mark> today.
+* This is a paragraph and <small>smaller text goes here</small>.
+* The concert starts at <time datetime="20:00">20:00</time> and you'll be able to enjoy the band for at least <time datetime="PT2H30M">2h 30m</time>.
+* Press <kbd>Ctrl</kbd> + <kbd>C</kbd> to copy text (Windows).
+* WWF's goal is to: <q>Build a future where people live in harmony with nature.</q> We hope they succeed.
+* The error occurred was: <samp>Keyboard not found. Press F1 to continue.</samp>
+* The area of a triangle is: 1/2 x <var>b</var> x <var>h</var>, where <var>b</var> is the base, and <var>h</var> is the vertical height.
+* <ruby>æ¼¢<rt>ã„ㄢˋ</rt></ruby>
+* C<sub>7</sub>H<sub>16</sub> + O<sub>2</sub> → CO<sub>2</sub> + H<sub>2</sub>O
+* The **Pythagorean theorem** is often expressed as <var>a<sup>2</sup></var> + <var>b<sup>2</sup></var> = <var>c<sup>2</sup></var>
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### image
+
+```````````````````````````````` example gitlab
+![alt text](https://gitlab.com/logo.png)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### inline_code
+
+```````````````````````````````` example gitlab
+`code`
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### inline_diff
+
+```````````````````````````````` example gitlab
+* {-deleted-}
+* {+added+}
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### label
+
+```````````````````````````````` example gitlab
+~bug
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### link
+
+```````````````````````````````` example gitlab
+[GitLab](https://gitlab.com)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### math
+
+```````````````````````````````` example gitlab
+This math is inline $`a^2+b^2=c^2`$.
+
+This is on a separate line:
+
+```math
+a^2+b^2=c^2
+```
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### ordered_list
+
+```````````````````````````````` example gitlab
+1. list item 1
+2. list item 2
+3. list item 3
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### ordered_list_with_start_order
+
+```````````````````````````````` example gitlab
+134. list item 1
+135. list item 2
+136. list item 3
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### ordered_task_list
+
+```````````````````````````````` example gitlab
+1. [x] hello
+2. [x] world
+3. [ ] example
+ 1. [ ] of nested
+ 1. [x] task list
+ 2. [ ] items
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### ordered_task_list_with_order
+
+```````````````````````````````` example gitlab
+4893. [x] hello
+4894. [x] world
+4895. [ ] example
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### reference_for_project_wiki
+
+```````````````````````````````` example gitlab
+Hi @gfm_user - thank you for reporting this ~"UX bug" (#1) we hope to fix it in %1.1 as part of !1
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### strike
+
+```````````````````````````````` example gitlab
+~~del~~
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### table
+
+```````````````````````````````` example gitlab
+| header | header |
+|--------|--------|
+| `code` | cell with **bold** |
+| ~~strike~~ | cell with _italic_ |
+
+# content after table
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### table_of_contents
+
+```````````````````````````````` example gitlab
+[[_TOC_]]
+
+# Lorem
+
+Well, that's just like... your opinion.. man.
+
+## Ipsum
+
+### Dolar
+
+# Sit amit
+
+### I don't know
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### task_list
+
+```````````````````````````````` example gitlab
+* [x] hello
+* [x] world
+* [ ] example
+ * [ ] of nested
+ * [x] task list
+ * [ ] items
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### video
+
+```````````````````````````````` example gitlab
+![Sample Video](https://gitlab.com/gitlab.mp4)
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+### word_break
+
+```````````````````````````````` example gitlab
+Fernstraßen<wbr>bau<wbr>privat<wbr>finanzierungs<wbr>gesetz
+.
+TODO: Write canonical HTML for this example
+````````````````````````````````
+
+## Image Attributes
+
+See
+[Change the image dimensions](https://docs.gitlab.com/ee/user/markdown.html#change-the-image-dimensions)
+in the GitLab Flavored Markdown documentation.
+
+The `width` and `height` attributes for an image can be specified directly after
+the image markdown.
+
+General syntax conforms to the
+[commonmark-hs attribute syntax](https://github.com/jgm/commonmark-hs/blob/master/commonmark-extensions/test/attributes.md)
+where it makes sense.
+
+```````````````````````````````` example gitlab
+![](https://gitlab.com/logo.png){width="100" height="100"}
+.
+<p><img src="https://gitlab.com/logo.png" width="100" height="100"></p>
+````````````````````````````````
+
+`%` and `px` units may also be specified.
+
+```````````````````````````````` example gitlab
+![](https://gitlab.com/logo.png){width="100%"}
+.
+<p><img src="https://gitlab.com/logo.png" width="100%"></p>
+````````````````````````````````
+
+```````````````````````````````` example gitlab
+![](https://gitlab.com/logo.png){height="100px"}
+.
+<p><img src="https://gitlab.com/logo.png" height="100px"></p>
+````````````````````````````````
+
+Whitespace is tolerated around the delimiters:
+
+```````````````````````````````` example gitlab
+![](https://gitlab.com/logo.png){ width="100" height="100" }
+.
+<p><img src="https://gitlab.com/logo.png" width="100" height="100"></p>
+````````````````````````````````
+
+Attributes must immediately follow the image markdown.
+
+```````````````````````````````` example gitlab
+![](https://gitlab.com/logo.png) {width="100" height="100"}
+.
+<p><img src="https://gitlab.com/logo.png"> {width="100" height="100"}</p>
+````````````````````````````````
+
diff --git a/glfm_specification/output_spec/spec.html b/glfm_specification/output_spec/spec.html
index a10b7f91d91..2a4d03a3fdd 100644
--- a/glfm_specification/output_spec/spec.html
+++ b/glfm_specification/output_spec/spec.html
@@ -1,32 +1,290 @@
-<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="1:1-4:3" lang="yaml" class="code highlight js-syntax-highlight language-yaml" data-lang-params="frontmatter" v-pre="true"><code><span id="LC1" class="line" lang="yaml"><span class="na">title</span><span class="pi">:</span> <span class="s">GitLab Flavored Markdown (GLFM) Spec</span></span>
-<span id="LC2" class="line" lang="yaml"><span class="na">version</span><span class="pi">:</span> <span class="s">alpha</span></span></code></pre>
-<copy-code></copy-code>
-</div>
-<h1 data-sourcepos="5:1-5:14" dir="auto">
-<a id="user-content-introduction" class="anchor" href="#introduction" aria-hidden="true"></a>Introduction</h1>
-<p data-sourcepos="7:1-7:84" dir="auto">TODO: Write a GitLab-specific version of the GitHub Flavored Markdown intro section.</p>
+<!DOCTYPE html>
+<!-- NOTE: Styling is based on the CommonMark specification template: -->
+<!-- - https://github.com/commonmark/commonmark-spec/blob/master/tools/make_spec.lua -->
+<!-- - https://github.com/commonmark/commonmark-spec/blob/master/tools/template.html -->
+<!-- -->
+<!-- NOTE: 'TODO:' comments will be followed up as task(s) on this issue: -->
+<!-- - https://gitlab.com/gitlab-org/gitlab/-/issues/361241 -->
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>GitLab Flavored Markdown Official Specification</title>
+ <style type="text/css">
+ body {
+ font-family: Helvetica, arial, freesans, clean, sans-serif;
+ line-height: 1.4;
+ max-width: 48em;
+ margin: auto;
+ padding: 0 0.5em 4em;
+ color: #333333;
+ background-color: #ffffff;
+ font-size: 13pt;
+ }
+
+ div#TOC ul { list-style: none; }
+ h1 {
+ font-size: 140%;
+ font-weight: bold;
+ border-top: 1px solid gray;
+ padding-top: 0.5em;
+ }
+
+ h2 {
+ font-size: 120%;
+ font-weight: bold;
+ }
+
+ h3 {
+ font-size: 110%;
+ font-weight: bold;
+ }
+
+ h4 {
+ font-size: 100%;
+ font-weight: bold;
+ }
+
+ /* NOTE: "font-weight: bold" was applied to "a.definition" class in original CommonMark */
+ /* template, but in practice it was applied to all anchors */
+ a {
+ font-weight: bold;
+ }
+
+
+ /* TODO: Format whitespace in examples. This will require preprocessing to insert spans around them. */
+ /*span.space { position: relative; }*/
+ /*span.space:after {*/
+ /* content: "·";*/
+ /* position: absolute;*/
+ /* !* create a mark that indicates a space (trick from D. Greenspan) *!*/
+ /* top: 0; bottom: 7px; left: 1px; right: 1px;*/
+ /* color: #aaaaaa;*/
+ /*}*/
+ /*@media print {*/
+ /* a.dingus { display: none; }*/
+ /*}*/
+
+ div.example {
+ overflow: hidden;
+ }
+
+ p {
+ text-align: justify;
+ }
+
+ pre {
+ padding: 0.5em;
+ margin: 0.2em 0 0.5em;
+ font-size: 88%;
+ }
+
+ pre {
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+ }
+
+ code {
+ font-family: monospace;
+ background-color: #d3e1e4;
+ }
+
+ pre > code {
+ background-color: transparent;
+ }
+
+ .example {
+ font-size: 0; /* hack to get width: 50% to work on inline-block */
+ padding-bottom: 6pt;
+ }
-<h1 data-sourcepos="10:1-10:40" dir="auto">
+ .column pre {
+ font-size: 11pt;
+ padding: 2pt 6pt;
+ }
+
+ div.examplenum {
+ font-size: 11pt;
+ text-align: left;
+ margin-bottom: 10px;
+ }
+
+ div.column {
+ display: inline-block;
+ width: 50%;
+ vertical-align: top;
+ }
+
+ div.example > div:nth-child(2) {
+ clear: left;
+ background-color: #d3e1e4;
+ }
+
+ div.example > div:nth-child(3) {
+ clear: right;
+ background-color: #c9cace;
+ }
+
+ @media print {
+ @page {
+ size: auto;
+ margin: 1.2in 1.2in 1.2in 1.2in;
+ }
+
+ body {
+ margin: 0;
+ line-height: 1.2;
+ font-size: 10pt;
+ }
+
+ .column pre {
+ font-size: 9pt;
+ }
+
+ div.examplenum {
+ font-size: 9pt;
+ }
+ }
+ </style>
+ <!-- TODO: Extract this javascript out to a separate file and unit test it -->
+ <script type="text/javascript">
+ /* NOTE: The following code performs many of the pre-processing steps originally handled */
+ /* in https://github.com/commonmark/commonmark-spec/blob/master/tools/make_spec.lua */
+
+ /* Adds a div.example wrapper around each pair of example code blocks. */
+ function addAttributesToExampleWrapperDivs() {
+ const exampleAnchorTags = document.querySelectorAll("a[href^=\"#example-\"]");
+ for (const exampleAnchorTag of exampleAnchorTags) {
+ const examplenumDiv = exampleAnchorTag.parentElement;
+ examplenumDiv.classList.add("examplenum");
+ const exampleDiv = examplenumDiv.parentElement;
+ exampleDiv.classList.add("example");
+ exampleDiv.id = exampleAnchorTag.getAttribute("href").substring(1);
+ }
+ }
+
+ function addColumnClassToMarkdownDivs() {
+ const markdownCodeBlockDivs = document.querySelectorAll("div.markdown-code-block");
+ for (const markdownCodeBlockDiv of markdownCodeBlockDivs) {
+ markdownCodeBlockDiv.classList.add("column");
+ }
+ }
+
+ function addNumbersToHeaders() {
+ const headers = document.querySelectorAll('h1,h2,h3');
+ let h1Index = -1; // NOTE: -1 because we don't assign a number to the title
+ let h2Index = 0;
+ let h3Index = 0;
+ const tocEntries = [];
+ for (const header of headers) {
+ if (h1Index === -1) {
+ h1Index++;
+ continue;
+ }
+
+ const originalHeaderTextContent = header.textContent.trim();
+ const headerAnchor = originalHeaderTextContent.toLowerCase().replaceAll(' ', '-');
+ header.id = headerAnchor;
+ let indent;
+ let headerTextContent;
+ if (header.tagName === 'H1') {
+ h1Index++;
+ h2Index = 0;
+ h3Index = 0;
+ header.textContent = headerTextContent = h1Index + ' ' + originalHeaderTextContent;
+ indent = 0;
+ } else if (header.tagName === 'H2') {
+ h2Index++;
+ h3Index = 0;
+ header.textContent =
+ headerTextContent = h1Index + '.' + h2Index + ' ' + originalHeaderTextContent;
+ indent = 1;
+ } else if (header.tagName === 'H3') {
+ h3Index++;
+ header.textContent = headerTextContent =
+ h1Index + '.' + h2Index + '.' + h3Index + ' ' + originalHeaderTextContent;
+ indent = 2;
+ }
+ tocEntries.push({headerAnchor, headerTextContent, indent});
+ }
+ }
+
+ document.addEventListener("DOMContentLoaded", function(_event) {
+ addAttributesToExampleWrapperDivs();
+ addColumnClassToMarkdownDivs();
+ const tocEntries = addNumbersToHeaders();
+ addToc(tocEntries);
+ });
+
+ /* NOTE: The following code is to support the "Try it" interactive "dingus", which */
+ /* we do not yet support. But it is being left here for comparison context with the */
+ /* original CommonMark template. */
+ // $$(document).ready(function() {
+ // $$("div.example").each(function(e) {
+ // var t = $$(this).find('code.language-markdown').text();
+ // $$(this).find('a.dingus').click(function(f) {
+ // window.open('/dingus/?text=' +
+ // encodeURIComponent(t.replace(/→/g,"\t")));
+ // });
+ // });
+ // $$("code.language-markdown").dblclick(function(e) { window.open('/dingus/?text=' +
+ // encodeURIComponent($$(this).text()));
+ // });
+ // });
+ </script>
+</head>
+<body>
+<h1 class="title">GitLab Flavored Markdown Official Specification</h1>
+<div class="version">Version alpha</div>
+
+<ul class="section-nav">
+<li><a href="#introduction">Introduction</a></li>
+<li>
+<a href="#gitlab-official-specification-markdown">GitLab Official Specification Markdown</a><ul>
+<li><a href="#footnotes">Footnotes</a></li>
+<li><a href="#task-list-items">Task list items</a></li>
+<li><a href="#front-matter">Front matter</a></li>
+<li><a href="#table-of-contents">Table of contents</a></li>
+</ul>
+</li>
+</ul>
+<h1 data-sourcepos="3:1-3:14" dir="auto">
+<a id="user-content-introduction" class="anchor" href="#introduction" aria-hidden="true"></a>Introduction</h1>
+<p data-sourcepos="5:1-5:84" dir="auto">TODO: Write a GitLab-specific version of the GitHub Flavored Markdown intro section.</p>
+<p data-sourcepos="7:1-10:75" dir="auto">NOTE: The example numbering in this document does not start at "1", because this official specification
+only contains a subset of all the examples which are supported by GitLab Flavored Markdown. See
+<a href="https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_example_snapshots/snapshot_spec.html" rel="nofollow noreferrer noopener" target="_blank"><code>snapshot_spec.html</code></a>
+for a complete list of all examples, which are a superset of examples from:</p>
+<ul data-sourcepos="12:1-16:0" dir="auto">
+<li data-sourcepos="12:1-12:12">CommonMark</li>
+<li data-sourcepos="13:1-13:26">GitHub Flavored Markdown</li>
+<li data-sourcepos="14:1-14:88">GitLab Flavored Markdown Official Specification (the same ones from this specifiation)</li>
+<li data-sourcepos="15:1-16:0">GitLab Flavored Markdown Internal Extensions.</li>
+</ul>
+<!-- BEGIN TESTS -->
+<h1 data-sourcepos="18:1-18:40" dir="auto">
<a id="user-content-gitlab-official-specification-markdown" class="anchor" href="#gitlab-official-specification-markdown" aria-hidden="true"></a>GitLab Official Specification Markdown</h1>
-<p data-sourcepos="12:1-15:104" dir="auto">Currently, only some of the GitLab-specific markdown features are
+<p data-sourcepos="20:1-23:104" dir="auto">Currently, only some of the GitLab-specific markdown features are
listed in this section. We may eventually add all
GitLab-specific features currently listed as supported in the
<a href="https://docs.gitlab.com/ee/user/markdown.html" rel="nofollow noreferrer noopener" target="_blank">user-facing documentation for GitLab Flavored Markdown</a>.</p>
-<p data-sourcepos="17:1-18:69" dir="auto">There is currently only this single top-level heading, but the
+<p data-sourcepos="25:1-26:69" dir="auto">There is currently only this single top-level heading, but the
examples may be split into multiple top-level headings in the future.</p>
-<h2 data-sourcepos="20:1-20:12" dir="auto">
+<h2 data-sourcepos="28:1-28:12" dir="auto">
<a id="user-content-footnotes" class="anchor" href="#footnotes" aria-hidden="true"></a>Footnotes</h2>
-<p data-sourcepos="22:1-23:143" dir="auto">See
+<p data-sourcepos="30:1-31:143" dir="auto">See
<a href="https://docs.gitlab.com/ee/user/markdown.html#footnotes" rel="nofollow noreferrer noopener" target="_blank">the footnotes section of the user-facing documentation for GitLab Flavored Markdown</a>.</p>
+<div>
+<div><a href="#example-674">Example 674</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="25:1-29:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">footnote reference tag [^fortytwo]</span>
+<pre data-sourcepos="36:1-40:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">footnote reference tag [^fortytwo]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[^fortytwo]: footnote text</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="31:1-51:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;</span>
+<pre data-sourcepos="42:1-62:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;</span>
<span id="LC2" class="line" lang="plaintext">footnote reference tag</span>
<span id="LC3" class="line" lang="plaintext">&lt;sup&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;a href="#fn-fortytwo-42" id="fnref-fortytwo-42" data-footnote-ref&gt;</span>
@@ -47,23 +305,26 @@ examples may be split into multiple top-level headings in the future.</p>
<span id="LC19" class="line" lang="plaintext">&lt;/section&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="53:1-53:18" dir="auto">
+</div>
+<h2 data-sourcepos="65:1-65:18" dir="auto">
<a id="user-content-task-list-items" class="anchor" href="#task-list-items" aria-hidden="true"></a>Task list items</h2>
-<p data-sourcepos="55:1-56:117" dir="auto">See
+<p data-sourcepos="67:1-68:117" dir="auto">See
<a href="https://docs.gitlab.com/ee/user/markdown.html#task-lists" rel="nofollow noreferrer noopener" target="_blank">Task lists</a> in the GitLab Flavored Markdown documentation.</p>
-<p data-sourcepos="58:1-61:39" dir="auto">Task list items (checkboxes) are defined as a GitHub Flavored Markdown extension in a section above.
+<p data-sourcepos="70:1-73:39" dir="auto">Task list items (checkboxes) are defined as a GitHub Flavored Markdown extension in a section above.
GitLab extends the behavior of task list items to support additional features.
Some of these features are in-progress, and should not yet be considered part of the official
GitLab Flavored Markdown specification.</p>
-<p data-sourcepos="63:1-63:85" dir="auto">Some of the behavior of task list items is implemented as client-side JavaScript/CSS.</p>
-<p data-sourcepos="65:1-65:80" dir="auto">The following are some basic examples; more examples may be added in the future.</p>
-<p data-sourcepos="67:1-67:16" dir="auto">Incomplete task:</p>
+<p data-sourcepos="75:1-75:85" dir="auto">Some of the behavior of task list items is implemented as client-side JavaScript/CSS.</p>
+<p data-sourcepos="77:1-77:80" dir="auto">The following are some basic examples; more examples may be added in the future.</p>
+<p data-sourcepos="79:1-79:16" dir="auto">Incomplete task:</p>
+<div>
+<div><a href="#example-675">Example 675</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="69:1-71:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [ ] incomplete</span></code></pre>
+<pre data-sourcepos="84:1-86:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [ ] incomplete</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="73:1-81:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="88:1-96:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;task-button/&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;input type="checkbox" disabled/&gt;</span>
@@ -72,13 +333,16 @@ GitLab Flavored Markdown specification.</p>
<span id="LC7" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="83:1-83:15" dir="auto">Completed task:</p>
+</div>
+<p data-sourcepos="99:1-99:15" dir="auto">Completed task:</p>
+<div>
+<div><a href="#example-676">Example 676</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="85:1-87:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [x] completed</span></code></pre>
+<pre data-sourcepos="104:1-106:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [x] completed</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="89:1-97:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="108:1-116:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;task-button/&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;input type="checkbox" checked disabled/&gt;</span>
@@ -87,13 +351,16 @@ GitLab Flavored Markdown specification.</p>
<span id="LC7" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="99:1-99:18" dir="auto">Inapplicable task:</p>
+</div>
+<p data-sourcepos="119:1-119:18" dir="auto">Inapplicable task:</p>
+<div>
+<div><a href="#example-677">Example 677</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="101:1-103:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [~] inapplicable</span></code></pre>
+<pre data-sourcepos="124:1-126:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [~] inapplicable</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="105:1-115:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="128:1-138:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;task-button/&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;input type="checkbox" data-inapplicable disabled&gt;</span>
@@ -104,16 +371,19 @@ GitLab Flavored Markdown specification.</p>
<span id="LC9" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="117:1-118:50" dir="auto">Inapplicable task in a "loose" list. Note that the <code>&lt;del&gt;</code> tag is not applied to the
+</div>
+<p data-sourcepos="141:1-142:50" dir="auto">Inapplicable task in a "loose" list. Note that the <code>&lt;del&gt;</code> tag is not applied to the
loose text; it has strikethrough applied with CSS.</p>
+<div>
+<div><a href="#example-678">Example 678</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="120:1-124:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [~] inapplicable</span>
+<pre data-sourcepos="147:1-151:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [~] inapplicable</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> text in loose list</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="126:1-141:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
+<pre data-sourcepos="153:1-168:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;p&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;task-button/&gt;</span>
@@ -129,45 +399,54 @@ loose text; it has strikethrough applied with CSS.</p>
<span id="LC14" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="143:1-143:15" dir="auto">
+</div>
+<h2 data-sourcepos="171:1-171:15" dir="auto">
<a id="user-content-front-matter" class="anchor" href="#front-matter" aria-hidden="true"></a>Front matter</h2>
-<p data-sourcepos="145:1-146:121" dir="auto">See
+<p data-sourcepos="173:1-174:121" dir="auto">See
<a href="https://docs.gitlab.com/ee/user/markdown.html#front-matter" rel="nofollow noreferrer noopener" target="_blank">Front matter</a> in the GitLab Flavored Markdown documentation.</p>
-<p data-sourcepos="148:1-149:95" dir="auto">Front matter is metadata included at the beginning of a Markdown document, preceding the content.
+<p data-sourcepos="176:1-177:95" dir="auto">Front matter is metadata included at the beginning of a Markdown document, preceding the content.
This data can be used by static site generators like Jekyll, Hugo, and many other applications.</p>
-<p data-sourcepos="151:1-151:18" dir="auto">YAML front matter:</p>
+<p data-sourcepos="179:1-179:18" dir="auto">YAML front matter:</p>
+<div>
+<div><a href="#example-679">Example 679</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="153:1-157:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">---</span>
+<pre data-sourcepos="184:1-188:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">---</span>
<span id="LC2" class="line" lang="plaintext">title: YAML front matter</span>
<span id="LC3" class="line" lang="plaintext">---</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="159:1-165:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;</span>
+<pre data-sourcepos="190:1-196:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;code&gt;</span>
<span id="LC3" class="line" lang="plaintext">title: YAML front matter</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="167:1-167:18" dir="auto">TOML front matter:</p>
+</div>
+<p data-sourcepos="199:1-199:18" dir="auto">TOML front matter:</p>
+<div>
+<div><a href="#example-680">Example 680</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="169:1-173:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">+++</span>
+<pre data-sourcepos="204:1-208:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">+++</span>
<span id="LC2" class="line" lang="plaintext">title: TOML front matter</span>
<span id="LC3" class="line" lang="plaintext">+++</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="175:1-181:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;</span>
+<pre data-sourcepos="210:1-216:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;code&gt;</span>
<span id="LC3" class="line" lang="plaintext">title: TOML front matter</span>
<span id="LC4" class="line" lang="plaintext">&lt;/code&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="183:1-183:18" dir="auto">JSON front matter:</p>
+</div>
+<p data-sourcepos="219:1-219:18" dir="auto">JSON front matter:</p>
+<div>
+<div><a href="#example-681">Example 681</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="185:1-191:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">;;;</span>
+<pre data-sourcepos="224:1-230:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">;;;</span>
<span id="LC2" class="line" lang="plaintext">{</span>
<span id="LC3" class="line" lang="plaintext"> "title": "JSON front matter"</span>
<span id="LC4" class="line" lang="plaintext">}</span>
@@ -175,7 +454,7 @@ This data can be used by static site generators like Jekyll, Hugo, and many othe
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="193:1-201:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;</span>
+<pre data-sourcepos="232:1-240:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;pre&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;code&gt;</span>
<span id="LC3" class="line" lang="plaintext">{</span>
<span id="LC4" class="line" lang="plaintext"> "title": "JSON front matter"</span>
@@ -184,9 +463,12 @@ This data can be used by static site generators like Jekyll, Hugo, and many othe
<span id="LC7" class="line" lang="plaintext">&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="203:1-203:66" dir="auto">Front matter blocks should be inserted at the top of the document:</p>
+</div>
+<p data-sourcepos="243:1-243:66" dir="auto">Front matter blocks should be inserted at the top of the document:</p>
+<div>
+<div><a href="#example-682">Example 682</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="205:1-211:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">text</span>
+<pre data-sourcepos="248:1-254:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">text</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">---</span>
<span id="LC4" class="line" lang="plaintext">title: YAML front matter</span>
@@ -194,42 +476,43 @@ This data can be used by static site generators like Jekyll, Hugo, and many othe
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="213:1-217:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;text&lt;/p&gt;</span>
+<pre data-sourcepos="256:1-260:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;text&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;hr&gt;</span>
<span id="LC3" class="line" lang="plaintext">&lt;h2&gt;title: YAML front matter&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="219:1-219:74" dir="auto">Front matter block delimiters shouldn’t be preceded by space characters:</p>
+</div>
+<p data-sourcepos="263:1-263:74" dir="auto">Front matter block delimiters shouldn’t be preceded by space characters:</p>
+<div>
+<div><a href="#example-683">Example 683</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="221:1-225:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ---</span>
+<pre data-sourcepos="268:1-272:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ---</span>
<span id="LC2" class="line" lang="plaintext">title: YAML front matter</span>
<span id="LC3" class="line" lang="plaintext">---</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="227:1-230:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr&gt;</span>
+<pre data-sourcepos="274:1-277:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;hr&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;h2&gt;title: YAML front matter&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<h2 data-sourcepos="232:1-232:20" dir="auto">
+</div>
+<h2 data-sourcepos="280:1-280:20" dir="auto">
<a id="user-content-table-of-contents" class="anchor" href="#table-of-contents" aria-hidden="true"></a>Table of contents</h2>
-<p data-sourcepos="234:1-236:46" dir="auto">See
+<p data-sourcepos="282:1-284:46" dir="auto">See
<a href="https://docs.gitlab.com/ee/user/markdown.html#table-of-contents" rel="nofollow noreferrer noopener" target="_blank">table of contents</a>
in the GitLab Flavored Markdown documentation.</p>
-<p data-sourcepos="238:1-239:58" dir="auto">A table of contents is an unordered list that links to subheadings in the document.
-Add either the <code>[[_TOC_]]</code> or </p><ul class="section-nav">
-<li><a href="#introduction">Introduction</a></li>
-<li>
-<a href="#gitlab-official-specification-markdown">GitLab Official Specification Markdown</a><ul>
-<li><a href="#footnotes">Footnotes</a></li>
-<li><a href="#task-list-items">Task list items</a></li>
-<li><a href="#front-matter">Front matter</a></li>
-<li><a href="#table-of-contents">Table of contents</a></li>
-</ul>
-</li>
-</ul> tag on its own line.
+<p data-sourcepos="286:1-290:23" dir="auto">NOTE: Because of this bug (<a href="https://gitlab.com/gitlab-org/gitlab/-/issues/359077" rel="nofollow noreferrer noopener" target="_blank">https://gitlab.com/gitlab-org/gitlab/-/issues/359077</a>),
+we cannot actually include the <code>TOC</code> tag with single brackets in backticks
+in this Markdown document, otherwise it would render a table of contents inline
+right here. So, it's been switched to <code>[</code> + <code>TOC</code> + <code>]</code> instead. This can be reverted
+once that bug is fixed.</p>
+<p data-sourcepos="292:1-293:76" dir="auto">A table of contents is an unordered list that links to subheadings in the document.
+Add either the <code>[[_TOC_]]</code> tag or the <code>[</code> + <code>TOC</code> + <code>]</code> tag on its own line.</p>
+<div>
+<div><a href="#example-684">Example 684</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="241:1-247:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[TOC]</span>
+<pre data-sourcepos="298:1-304:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[TOC]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"># Heading 1</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -237,7 +520,7 @@ Add either the <code>[[_TOC_]]</code> or </p><ul class="section-nav">
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="249:1-260:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;nav&gt;</span>
+<pre data-sourcepos="306:1-317:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;nav&gt;</span>
<span id="LC2" class="line" lang="plaintext"> &lt;ul&gt;</span>
<span id="LC3" class="line" lang="plaintext"> &lt;li&gt;&lt;a href="#heading-1"&gt;Heading 1&lt;/a&gt;&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext"> &lt;ul&gt;</span>
@@ -249,8 +532,11 @@ Add either the <code>[[_TOC_]]</code> or </p><ul class="section-nav">
<span id="LC10" class="line" lang="plaintext">&lt;h2&gt;Heading 2&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<div>
+<div><a href="#example-685">Example 685</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="262:1-268:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[_TOC_]]</span>
+<pre data-sourcepos="323:1-329:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[_TOC_]]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"># Heading 1</span>
<span id="LC4" class="line" lang="plaintext"></span>
@@ -258,7 +544,7 @@ Add either the <code>[[_TOC_]]</code> or </p><ul class="section-nav">
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="270:1-281:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;nav&gt;</span>
+<pre data-sourcepos="331:1-342:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;nav&gt;</span>
<span id="LC2" class="line" lang="plaintext"> &lt;ul&gt;</span>
<span id="LC3" class="line" lang="plaintext"> &lt;li&gt;&lt;a href="#heading-1"&gt;Heading 1&lt;/a&gt;&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext"> &lt;ul&gt;</span>
@@ -270,10 +556,13 @@ Add either the <code>[[_TOC_]]</code> or </p><ul class="section-nav">
<span id="LC10" class="line" lang="plaintext">&lt;h2&gt;Heading 2&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="283:1-284:5" dir="auto">A table of contents is a block element. It should preceded and followed by a blank
+</div>
+<p data-sourcepos="345:1-346:5" dir="auto">A table of contents is a block element. It should preceded and followed by a blank
line.</p>
+<div>
+<div><a href="#example-686">Example 686</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="286:1-292:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[_TOC_]]</span>
+<pre data-sourcepos="351:1-357:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[_TOC_]]</span>
<span id="LC2" class="line" lang="plaintext">text</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">text</span>
@@ -281,19 +570,22 @@ line.</p>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="294:1-297:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[[&lt;em&gt;TOC&lt;/em&gt;]]text&lt;/p&gt;</span>
+<pre data-sourcepos="359:1-362:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;[[&lt;em&gt;TOC&lt;/em&gt;]]text&lt;/p&gt;</span>
<span id="LC2" class="line" lang="plaintext">&lt;p&gt;text[TOC]&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
-<p data-sourcepos="299:1-299:60" dir="auto">A table of contents can be indented with up to three spaces.</p>
+</div>
+<p data-sourcepos="365:1-365:60" dir="auto">A table of contents can be indented with up to three spaces.</p>
+<div>
+<div><a href="#example-687">Example 687</a></div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="301:1-305:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> [[_TOC_]]</span>
+<pre data-sourcepos="370:1-374:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> [[_TOC_]]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"># Heading 1</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
-<pre data-sourcepos="307:1-314:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;nav&gt;</span>
+<pre data-sourcepos="376:1-383:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;nav&gt;</span>
<span id="LC2" class="line" lang="plaintext"> &lt;ul&gt;</span>
<span id="LC3" class="line" lang="plaintext"> &lt;li&gt;&lt;a href="#heading-1"&gt;Heading 1&lt;/a&gt;&lt;/li&gt;</span>
<span id="LC4" class="line" lang="plaintext"> &lt;/ul&gt;</span>
@@ -301,4 +593,9 @@ line.</p>
<span id="LC6" class="line" lang="plaintext">&lt;h1&gt;Heading 1&lt;/h1&gt;</span></code></pre>
<copy-code></copy-code>
</div>
+</div>
+<!-- END TESTS -->
+
+</body>
+</html>
diff --git a/glfm_specification/output_spec/spec.txt b/glfm_specification/output_spec/spec.txt
index e16975ac51f..350acea770c 100644
--- a/glfm_specification/output_spec/spec.txt
+++ b/glfm_specification/output_spec/spec.txt
@@ -1,11 +1,21 @@
---
-title: GitLab Flavored Markdown (GLFM) Spec
+title: GitLab Flavored Markdown Official Specification
version: alpha
...
# Introduction
TODO: Write a GitLab-specific version of the GitHub Flavored Markdown intro section.
+NOTE: The example numbering in this document does not start at "1", because this official specification
+only contains a subset of all the examples which are supported by GitLab Flavored Markdown. See
+[`snapshot_spec.html`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_example_snapshots/snapshot_spec.html)
+for a complete list of all examples, which are a superset of examples from:
+
+- CommonMark
+- GitHub Flavored Markdown
+- GitLab Flavored Markdown Official Specification (the same ones from this specifiation)
+- GitLab Flavored Markdown Internal Extensions.
+
<!-- BEGIN TESTS -->
# GitLab Official Specification Markdown
@@ -215,8 +225,14 @@ See
[table of contents](https://docs.gitlab.com/ee/user/markdown.html#table-of-contents)
in the GitLab Flavored Markdown documentation.
+NOTE: Because of this bug (https://gitlab.com/gitlab-org/gitlab/-/issues/359077),
+we cannot actually include the `TOC` tag with single brackets in backticks
+in this Markdown document, otherwise it would render a table of contents inline
+right here. So, it's been switched to `[` + `TOC` + `]` instead. This can be reverted
+once that bug is fixed.
+
A table of contents is an unordered list that links to subheadings in the document.
-Add either the `[[_TOC_]]` or `[TOC]` tag on its own line.
+Add either the `[[_TOC_]]` tag or the `[` + `TOC` + `]` tag on its own line.
```````````````````````````````` example gitlab
[TOC]
diff --git a/jest.config.base.js b/jest.config.base.js
index 30e11122f81..05967b51b88 100644
--- a/jest.config.base.js
+++ b/jest.config.base.js
@@ -1,6 +1,7 @@
const IS_EE = require('./config/helpers/is_ee_env');
const isESLint = require('./config/helpers/is_eslint');
const IS_JH = require('./config/helpers/is_jh_env');
+const { TEST_HOST } = require('./spec/frontend/__helpers__/test_constants');
module.exports = (path, options = {}) => {
const {
@@ -25,7 +26,7 @@ module.exports = (path, options = {}) => {
]);
}
- const glob = `${path}/**/*_spec.js`;
+ const glob = `${path}/**/*@([._])spec.js`;
let testMatch = [`<rootDir>/${glob}`];
if (IS_EE) {
testMatch.push(`<rootDir>/ee/${glob}`);
@@ -43,6 +44,8 @@ module.exports = (path, options = {}) => {
const TEST_FIXTURES_PATTERN = 'test_fixtures(/.*)$';
const moduleNameMapper = {
+ '^~(/.*)\\?(worker|raw)$': '<rootDir>/app/assets/javascripts$1',
+ '^(.*)\\?(worker|raw)$': '$1',
'^~(/.*)$': '<rootDir>/app/assets/javascripts$1',
'^ee_component(/.*)$':
'<rootDir>/app/assets/javascripts/vue_shared/components/empty_component.js',
@@ -63,6 +66,7 @@ module.exports = (path, options = {}) => {
'^jest/(.*)$': '<rootDir>/spec/frontend/$1',
'^ee_else_ce_jest/(.*)$': '<rootDir>/spec/frontend/$1',
'^jquery$': '<rootDir>/node_modules/jquery/dist/jquery.slim.js',
+ '^@sentry/browser$': '<rootDir>/app/assets/javascripts/sentry/sentry_browser_wrapper.js',
...extModuleNameMapper,
};
@@ -153,6 +157,7 @@ module.exports = (path, options = {}) => {
'dateformat',
'lowlight',
'vscode-languageserver-types',
+ 'yaml',
...gfmParserDependencies,
];
@@ -183,11 +188,16 @@ module.exports = (path, options = {}) => {
'^.+\\.(md|zip|png|yml|yaml)$': './spec/frontend/__helpers__/raw_transformer.js',
},
transformIgnorePatterns: [`node_modules/(?!(${transformIgnoreNodeModules.join('|')}))`],
- timers: 'legacy',
+ fakeTimers: {
+ enableGlobally: true,
+ doNotFake: ['nextTick', 'setImmediate'],
+ legacyFakeTimers: true,
+ },
testEnvironment: '<rootDir>/spec/frontend/environment.js',
testEnvironmentOptions: {
IS_EE,
IS_JH,
+ url: TEST_HOST,
},
testRunner: 'jest-jasmine2',
};
diff --git a/jest.config.contract.js b/jest.config.contract.js
new file mode 100644
index 00000000000..224d50f87d6
--- /dev/null
+++ b/jest.config.contract.js
@@ -0,0 +1,6 @@
+module.exports = () => {
+ return {
+ modulePaths: ['<rootDir>/spec/contracts/consumer/node_modules/'],
+ roots: ['spec/contracts/consumer', 'ee/spec/contracts/consumer'],
+ };
+};
diff --git a/jest.config.integration.js b/jest.config.integration.js
index e2ce32218e0..0693a500990 100644
--- a/jest.config.integration.js
+++ b/jest.config.integration.js
@@ -24,6 +24,8 @@ module.exports = {
'^jh_else_ce_test_helpers(/.*)$': '<rootDir>/jh/spec/frontend_integration/test_helpers$1',
},
}),
- timers: 'real',
+ fakeTimers: {
+ enableGlobally: false,
+ },
testTimeout: process.env.CI ? 20000 : 7000,
};
diff --git a/lefthook.yml b/lefthook.yml
index dc2c0b70c6f..d62a90d150e 100644
--- a/lefthook.yml
+++ b/lefthook.yml
@@ -1,5 +1,7 @@
pre-push:
parallel: true
+ skip:
+ - ref: master
commands:
danger:
run: bundle exec rake danger_local
@@ -17,7 +19,7 @@ pre-push:
tags: view haml style
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
glob: '*.html.haml'
- run: REVEAL_RUBOCOP_TODO=0 bundle exec haml-lint --config .haml-lint.yml {files}
+ run: REVEAL_RUBOCOP_TODO=0 bundle exec haml-lint --parallel --config .haml-lint.yml {files}
markdownlint:
tags: documentation style
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
@@ -26,7 +28,7 @@ pre-push:
yamllint:
tags: backend style
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
- glob: '*.{yml,yaml}'
+ glob: '*.{yml,yaml}{,.*}'
run: scripts/lint-yaml.sh {files}
stylelint:
tags: stylesheet css style
@@ -43,23 +45,28 @@ pre-push:
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
glob: '*.{rb,rake}'
run: REVEAL_RUBOCOP_TODO=0 bundle exec rubocop --parallel --force-exclusion {files}
+ sidekiq-queues:
+ tags: backend
+ files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
+ glob: '{,ee/}app/workers/*.rb'
+ run: bundle exec rake gitlab:sidekiq:queues:check
graphql_docs:
tags: documentation
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
glob: '{app/graphql/**/*.rb,ee/app/graphql/**/*.rb}'
run: bundle exec rake gitlab:graphql:check_docs
- vale: # Requires Vale: https://docs.gitlab.com/ee/development/documentation/testing.html#install-linters
+ vale: # Requires Vale: https://docs.gitlab.com/ee/development/documentation/testing.html#install-linters
tags: documentation style
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
glob: 'doc/*.md'
run: 'if [ $VALE_WARNINGS ]; then minWarnings=warning; else minWarnings=error; fi; if command -v vale > /dev/null 2>&1; then if ! vale --config .vale.ini --minAlertLevel $minWarnings {files}; then echo "ERROR: Fix any linting errors and make sure you are using the latest version of Vale."; exit 1; fi; else echo "ERROR: Vale not found. For more information, see https://docs.errata.ai/vale/install."; exit 1; fi'
gettext:
- skip: true # This is disabled by default. You can enable this check by adding skip: false in lefhook-local.yml https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md#skip
+ skip: true # This is disabled by default. You can enable this check by adding skip: false in lefhook-local.yml https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md#skip
tags: backend frontend view haml
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD | while read file;do git diff --unified=1 $(git merge-base origin/master HEAD)..HEAD $file | grep -Fqe '_(' && echo $file;done; true
- glob: "*.{haml,rb,js,vue}"
+ glob: '*.{haml,rb,js,vue}'
run: bin/rake gettext:updated_check
- docs-metadata: # See https://docs.gitlab.com/ee/development/documentation/#metadata
+ docs-metadata: # See https://docs.gitlab.com/ee/development/documentation/#metadata
tags: documentation style
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
glob: 'doc/*.md'
@@ -79,7 +86,14 @@ pre-push:
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
glob: 'data/removals/*.yml'
run: echo "Changes to removals files detected. Checking removals..\n"; bundle exec rake gitlab:docs:check_removals
+ scripts:
+ "merge_conflicts":
+ skip: true # This is disabled by default. You can enable this check by adding skip: false in lefhook-local.yml https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md#skip
+ runner: bash
+pre-commit:
+ parallel: true
+ commands:
secrets-detection:
tags: secrets
- files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
- run: 'if command -v ripsecrets > /dev/null 2>&1; then ripsecrets --strict-ignore {files}; else echo "WARNING: ripsecrets is not installed. Please install it."; fi'
+ files: git diff --name-only --diff-filter=d --staged
+ run: 'if command -v gitleaks > /dev/null 2>&1; then gitleaks protect --no-banner --staged --redact --verbose; else echo "WARNING: gitleaks is not installed. Please install it. See https://github.com/zricethezav/gitleaks#installing."; fi'
diff --git a/lib/api/admin/batched_background_migrations.rb b/lib/api/admin/batched_background_migrations.rb
index e8cc08a23be..7e612b5b66a 100644
--- a/lib/api/admin/batched_background_migrations.rb
+++ b/lib/api/admin/batched_background_migrations.rb
@@ -12,7 +12,15 @@ module API
namespace 'admin' do
resources 'batched_background_migrations/:id' do
- desc 'Retrieve a batched background migration'
+ desc 'Retrieve a batched background migration' do
+ success ::API::Entities::BatchedBackgroundMigration
+ failure [
+ { code: 401, message: '401 Unauthorized' },
+ { code: 403, message: '403 Forbidden' },
+ { code: 404, message: '404 Not found' }
+ ]
+ tags %w[batched_background_migrations]
+ end
params do
optional :database,
type: String,
@@ -31,7 +39,15 @@ module API
end
resources 'batched_background_migrations' do
- desc 'Get the list of the batched background migrations'
+ desc 'Get the list of batched background migrations' do
+ success ::API::Entities::BatchedBackgroundMigration
+ failure [
+ { code: 401, message: '401 Unauthorized' },
+ { code: 403, message: '403 Forbidden' }
+ ]
+ is_array true
+ tags %w[batched_background_migrations]
+ end
params do
optional :database,
type: String,
@@ -48,7 +64,16 @@ module API
end
resources 'batched_background_migrations/:id/resume' do
- desc 'Resume a batched background migration'
+ desc 'Resume a batched background migration' do
+ success ::API::Entities::BatchedBackgroundMigration
+ failure [
+ { code: 401, message: '401 Unauthorized' },
+ { code: 403, message: '403 Forbidden' },
+ { code: 404, message: '404 Not found' },
+ { code: 422, message: 'You can resume only `paused` batched background migrations.' }
+ ]
+ tags %w[batched_background_migrations]
+ end
params do
optional :database,
type: String,
@@ -73,7 +98,16 @@ module API
end
resources 'batched_background_migrations/:id/pause' do
- desc 'Pause a batched background migration'
+ desc 'Pause a batched background migration' do
+ success ::API::Entities::BatchedBackgroundMigration
+ failure [
+ { code: 401, message: '401 Unauthorized' },
+ { code: 403, message: '403 Forbidden' },
+ { code: 404, message: '404 Not found' },
+ { code: 422, message: 'You can pause only `active` batched background migrations.' }
+ ]
+ tags %w[batched_background_migrations]
+ end
params do
optional :database,
type: String,
diff --git a/lib/api/admin/plan_limits.rb b/lib/api/admin/plan_limits.rb
index 49b41b44a18..5ef56d3326f 100644
--- a/lib/api/admin/plan_limits.rb
+++ b/lib/api/admin/plan_limits.rb
@@ -70,6 +70,8 @@ module API
optional :terraform_module_max_file_size, type: Integer,
desc: 'Maximum Terraform Module package file size in bytes'
optional :storage_size_limit, type: Integer, desc: 'Maximum storage size for the root namespace in megabytes'
+ optional :pipeline_hierarchy_size, type: Integer,
+ desc: "Maximum number of downstream pipelines in a pipeline's hierarchy tree"
end
put "application/plan_limits" do
params = declared_params(include_missing: false)
diff --git a/lib/api/alert_management_alerts.rb b/lib/api/alert_management_alerts.rb
index f57b7d00c81..9e28ee049d0 100644
--- a/lib/api/alert_management_alerts.rb
+++ b/lib/api/alert_management_alerts.rb
@@ -6,12 +6,21 @@ module API
urgency :low
params do
- requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
- requires :alert_iid, type: Integer, desc: 'The IID of the Alert'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project',
+ documentation: { example: 17 }
+ requires :alert_iid, type: Integer, desc: 'The IID of the Alert',
+ documentation: { example: 23 }
end
resource :projects, requirements: ::API::API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace ':id/alert_management_alerts/:alert_iid/metric_images' do
+ desc 'Workhorse authorize metric image file upload' do
+ success code: 200
+ failure [
+ { code: 403, message: 'Forbidden' }
+ ]
+ tags %w[alert_management]
+ end
post 'authorize' do
authorize!(:upload_alert_management_metric_image, find_project_alert(request.params[:alert_iid]))
@@ -29,13 +38,20 @@ module API
end
desc 'Upload a metric image for an alert' do
- success Entities::MetricImage
+ consumes ['multipart/form-data']
+ success code: 200, model: Entities::MetricImage
+ failure [
+ { code: 403, message: 'Forbidden' }
+ ]
+ tags %w[alert_management]
end
params do
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The image file to be uploaded',
documentation: { type: 'file' }
- optional :url, type: String, desc: 'The url to view more metric info'
- optional :url_text, type: String, desc: 'A description of the image or URL'
+ optional :url, type: String, desc: 'The url to view more metric info',
+ documentation: { example: 'https://example.com/metric' }
+ optional :url_text, type: String, desc: 'A description of the image or URL',
+ documentation: { example: 'An example metric' }
end
post do
require_gitlab_workhorse!
@@ -61,7 +77,14 @@ module API
end
end
- desc 'Metric Images for alert'
+ desc 'Metric Images for alert' do
+ success code: 200, model: Entities::MetricImage
+ is_array true
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[alert_management]
+ end
get do
alert = find_project_alert(params[:alert_iid])
@@ -73,12 +96,21 @@ module API
end
desc 'Update a metric image for an alert' do
- success Entities::MetricImage
+ consumes ['multipart/form-data']
+ success code: 200, model: Entities::MetricImage
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 422, message: 'Unprocessable entity' }
+ ]
+ tags %w[alert_management]
end
params do
- requires :metric_image_id, type: Integer, desc: 'The ID of metric image'
- optional :url, type: String, desc: 'The url to view more metric info'
- optional :url_text, type: String, desc: 'A description of the image or URL'
+ requires :metric_image_id, type: Integer, desc: 'The ID of metric image',
+ documentation: { example: 42 }
+ optional :url, type: String, desc: 'The url to view more metric info',
+ documentation: { example: 'https://example.com/metric' }
+ optional :url_text, type: String, desc: 'A description of the image or URL',
+ documentation: { example: 'An example metric' }
end
put ':metric_image_id' do
alert = find_project_alert(params[:alert_iid])
@@ -97,10 +129,16 @@ module API
end
desc 'Remove a metric image for an alert' do
- success Entities::MetricImage
+ success code: 204, model: Entities::MetricImage
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 422, message: 'Unprocessable entity' }
+ ]
+ tags %w[alert_management]
end
params do
- requires :metric_image_id, type: Integer, desc: 'The ID of metric image'
+ requires :metric_image_id, type: Integer, desc: 'The ID of metric image',
+ documentation: { example: 42 }
end
delete ':metric_image_id' do
alert = find_project_alert(params[:alert_iid])
diff --git a/lib/api/api.rb b/lib/api/api.rb
index ffb0cdf8991..b23b11d0c29 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -171,9 +171,11 @@ module API
namespace do
# Keep in alphabetical order
mount ::API::AccessRequests
+ mount ::API::Admin::BatchedBackgroundMigrations
mount ::API::Admin::Ci::Variables
mount ::API::Admin::InstanceClusters
mount ::API::Admin::PlanLimits
+ mount ::API::AlertManagementAlerts
mount ::API::Appearance
mount ::API::Applications
mount ::API::Avatar
@@ -181,10 +183,13 @@ module API
mount ::API::Branches
mount ::API::BroadcastMessages
mount ::API::BulkImports
+ mount ::API::Ci::JobArtifacts
+ mount ::API::Groups
mount ::API::Ci::Jobs
mount ::API::Ci::ResourceGroups
mount ::API::Ci::Runner
mount ::API::Ci::Runners
+ mount ::API::Ci::SecureFiles
mount ::API::Ci::Pipelines
mount ::API::Ci::PipelineSchedules
mount ::API::Ci::Triggers
@@ -193,6 +198,13 @@ module API
mount ::API::Clusters::Agents
mount ::API::Commits
mount ::API::CommitStatuses
+ mount ::API::ComposerPackages
+ mount ::API::ConanInstancePackages
+ mount ::API::ConanProjectPackages
+ mount ::API::ContainerRegistryEvent
+ mount ::API::ContainerRepositories
+ mount ::API::DebianGroupPackages
+ mount ::API::DebianProjectPackages
mount ::API::DependencyProxy
mount ::API::DeployKeys
mount ::API::DeployTokens
@@ -200,54 +212,75 @@ module API
mount ::API::Environments
mount ::API::ErrorTracking::ClientKeys
mount ::API::ErrorTracking::ProjectSettings
+ mount ::API::Events
mount ::API::FeatureFlags
mount ::API::FeatureFlagsUserLists
mount ::API::Features
mount ::API::Files
mount ::API::FreezePeriods
+ mount ::API::GenericPackages
mount ::API::Geo
mount ::API::GoProxy
mount ::API::GroupAvatar
mount ::API::GroupClusters
mount ::API::GroupContainerRepositories
+ mount ::API::GroupDebianDistributions
mount ::API::GroupExport
mount ::API::GroupImport
mount ::API::GroupPackages
mount ::API::GroupVariables
+ mount ::API::HelmPackages
mount ::API::ImportBitbucketServer
mount ::API::ImportGithub
mount ::API::Integrations
+ mount ::API::Integrations::JiraConnect::Subscriptions
mount ::API::Invitations
mount ::API::IssueLinks
mount ::API::Keys
mount ::API::Lint
mount ::API::Markdown
+ mount ::API::MavenPackages
+ mount ::API::Members
mount ::API::MergeRequestApprovals
+ mount ::API::MergeRequests
mount ::API::MergeRequestDiffs
mount ::API::Metadata
mount ::API::Metrics::Dashboard::Annotations
mount ::API::Metrics::UserStarredDashboards
+ mount ::API::Namespaces
+ mount ::API::NpmInstancePackages
+ mount ::API::NpmProjectPackages
+ mount ::API::NugetGroupPackages
+ mount ::API::NugetProjectPackages
mount ::API::PackageFiles
+ mount ::API::Pages
mount ::API::PersonalAccessTokens::SelfInformation
mount ::API::PersonalAccessTokens
mount ::API::ProjectClusters
+ mount ::API::ProjectContainerRepositories
+ mount ::API::ProjectDebianDistributions
mount ::API::ProjectEvents
mount ::API::ProjectExport
mount ::API::ProjectHooks
mount ::API::ProjectImport
+ mount ::API::ProjectPackages
mount ::API::ProjectRepositoryStorageMoves
mount ::API::ProjectSnippets
mount ::API::ProjectSnapshots
mount ::API::ProjectStatistics
mount ::API::ProjectTemplates
+ mount ::API::Projects
mount ::API::ProtectedBranches
mount ::API::ProtectedTags
+ mount ::API::PypiPackages
mount ::API::Releases
mount ::API::Release::Links
mount ::API::RemoteMirrors
mount ::API::Repositories
mount ::API::ResourceAccessTokens
mount ::API::ResourceMilestoneEvents
+ mount ::API::RpmProjectPackages
+ mount ::API::RubygemPackages
mount ::API::Snippets
mount ::API::SnippetRepositoryStorageMoves
mount ::API::Statistics
@@ -260,6 +293,9 @@ module API
mount ::API::Terraform::StateVersion
mount ::API::Topics
mount ::API::Unleash
+ mount ::API::UsageData
+ mount ::API::UsageDataNonSqlMetrics
+ mount ::API::UsageDataQueries
mount ::API::UserCounts
mount ::API::Wikis
@@ -267,57 +303,27 @@ module API
end
# Keep in alphabetical order
- mount ::API::Admin::BatchedBackgroundMigrations
mount ::API::Admin::Sidekiq
- mount ::API::AlertManagementAlerts
mount ::API::AwardEmoji
mount ::API::Boards
- mount ::API::Ci::JobArtifacts
+ mount ::API::Ci::Pipelines
+ mount ::API::Ci::PipelineSchedules
mount ::API::Ci::SecureFiles
- mount ::API::ComposerPackages
- mount ::API::ConanInstancePackages
- mount ::API::ConanProjectPackages
- mount ::API::ContainerRegistryEvent
- mount ::API::ContainerRepositories
- mount ::API::DebianGroupPackages
- mount ::API::DebianProjectPackages
mount ::API::Discussions
mount ::API::ErrorTracking::Collector
- mount ::API::Events
- mount ::API::GenericPackages
mount ::API::GroupBoards
- mount ::API::GroupDebianDistributions
mount ::API::GroupLabels
mount ::API::GroupMilestones
- mount ::API::Groups
- mount ::API::HelmPackages
- mount ::API::Integrations::JiraConnect::Subscriptions
mount ::API::Issues
mount ::API::Labels
- mount ::API::MavenPackages
- mount ::API::Members
- mount ::API::MergeRequests
- mount ::API::Namespaces
mount ::API::Notes
mount ::API::NotificationSettings
- mount ::API::NpmInstancePackages
- mount ::API::NpmProjectPackages
- mount ::API::NugetGroupPackages
- mount ::API::NugetProjectPackages
- mount ::API::Pages
mount ::API::PagesDomains
- mount ::API::ProjectContainerRepositories
- mount ::API::ProjectDebianDistributions
mount ::API::ProjectEvents
mount ::API::ProjectMilestones
- mount ::API::ProjectPackages
- mount ::API::Projects
mount ::API::ProtectedTags
- mount ::API::PypiPackages
mount ::API::ResourceLabelEvents
mount ::API::ResourceStateEvents
- mount ::API::RpmProjectPackages
- mount ::API::RubygemPackages
mount ::API::Search
mount ::API::Settings
mount ::API::SidekiqMetrics
@@ -327,7 +333,6 @@ module API
mount ::API::Todos
mount ::API::UsageData
mount ::API::UsageDataNonSqlMetrics
- mount ::API::UsageDataQueries
mount ::API::Users
mount ::API::Ml::Mlflow
end
diff --git a/lib/api/appearance.rb b/lib/api/appearance.rb
index 69f1521ef2a..2cef1b27504 100644
--- a/lib/api/appearance.rb
+++ b/lib/api/appearance.rb
@@ -26,6 +26,7 @@ module API
end
params do
optional :title, type: String, desc: 'Instance title on the sign in / sign up page'
+ optional :short_title, type: String, desc: 'Short title for Progressive Web App'
optional :description, type: String, desc: 'Markdown text shown on the sign in / sign up page'
# TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960
optional :logo, type: File, desc: 'Instance image used on the sign in / sign up page' # rubocop:disable Scalability/FileUploads
diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb
index e419a025508..f7a39db7249 100644
--- a/lib/api/award_emoji.rb
+++ b/lib/api/award_emoji.rb
@@ -80,7 +80,7 @@ module API
delete "#{endpoint}/:award_id", feature_category: awardable_params[:feature_category] do
award = awardable.award_emoji.find(params[:award_id])
- unauthorized! unless award.user == current_user || current_user&.admin?
+ unauthorized! unless award.user == current_user || current_user&.can_admin_all_resources?
destroy_conditionally!(award)
end
diff --git a/lib/api/ci/job_artifacts.rb b/lib/api/ci/job_artifacts.rb
index 352ad04c982..3788f5bec41 100644
--- a/lib/api/ci/job_artifacts.rb
+++ b/lib/api/ci/job_artifacts.rb
@@ -16,18 +16,25 @@ module API
end
end
- prepend_mod_with('API::Ci::JobArtifacts') # rubocop: disable Cop/InjectEnterpriseEditionModule
-
params do
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Download the artifacts archive from a job' do
detail 'This feature was introduced in GitLab 8.10'
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
- requires :ref_name, type: String, desc: 'The ref from repository'
- requires :job, type: String, desc: 'The name for the job'
+ requires :ref_name, type: String,
+ desc: 'Branch or tag name in repository. `HEAD` or `SHA` references are not supported.'
+ requires :job, type: String, desc: 'The name of the job.'
+ optional :job_token, type: String,
+ desc: 'To be used with triggers for multi-project pipelines, ' \
+ 'available only on Premium and Ultimate tiers.'
end
route_setting :authentication, job_token_allowed: true
get ':id/jobs/artifacts/:ref_name/download',
@@ -43,11 +50,21 @@ module API
desc 'Download a specific file from artifacts archive from a ref' do
detail 'This feature was introduced in GitLab 11.5'
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
- requires :ref_name, type: String, desc: 'The ref from repository'
- requires :job, type: String, desc: 'The name for the job'
- requires :artifact_path, type: String, desc: 'Artifact path'
+ requires :ref_name, type: String,
+ desc: 'Branch or tag name in repository. `HEAD` or `SHA` references are not supported.'
+ requires :job, type: String, desc: 'The name of the job.'
+ requires :artifact_path, type: String, desc: 'Path to a file inside the artifacts archive.'
+ optional :job_token, type: String,
+ desc: 'To be used with triggers for multi-project pipelines, ' \
+ 'available only on Premium and Ultimate tiers.'
end
route_setting :authentication, job_token_allowed: true
get ':id/jobs/artifacts/:ref_name/raw/*artifact_path',
@@ -69,9 +86,17 @@ module API
desc 'Download the artifacts archive from a job' do
detail 'This feature was introduced in GitLab 8.5'
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
requires :job_id, type: Integer, desc: 'The ID of a job'
+ optional :job_token, type: String,
+ desc: 'To be used with triggers for multi-project pipelines, ' \
+ 'available only on Premium and Ultimate tiers.'
end
route_setting :authentication, job_token_allowed: true
get ':id/jobs/:job_id/artifacts', urgency: :low do
@@ -85,10 +110,19 @@ module API
desc 'Download a specific file from artifacts archive' do
detail 'This feature was introduced in GitLab 10.0'
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
requires :job_id, type: Integer, desc: 'The ID of a job'
- requires :artifact_path, type: String, desc: 'Artifact path'
+ requires :artifact_path, type: String, desc: 'Path to a file inside the artifacts archive.'
+ optional :job_token, type: String,
+ desc: 'To be used with triggers for multi-project pipelines, ' \
+ 'available only on Premium and Ultimate tiers.'
end
route_setting :authentication, job_token_allowed: true
get ':id/jobs/:job_id/artifacts/*artifact_path', urgency: :low, format: false do
@@ -113,6 +147,11 @@ module API
desc 'Keep the artifacts to prevent them from being deleted' do
success ::API::Entities::Ci::Job
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
requires :job_id, type: Integer, desc: 'The ID of a job'
@@ -132,6 +171,12 @@ module API
desc 'Delete the artifacts files from a job' do
detail 'This feature was introduced in GitLab 11.9'
+ success code: 204
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 409, message: 'Conflict' }
+ ]
end
params do
requires :job_id, type: Integer, desc: 'The ID of a job'
@@ -148,7 +193,14 @@ module API
status :no_content
end
- desc 'Expire the artifacts files from a project'
+ desc 'Expire the artifacts files from a project' do
+ success code: 202
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 409, message: 'Conflict' }
+ ]
+ end
delete ':id/artifacts' do
authorize_destroy_artifacts!
@@ -162,3 +214,5 @@ module API
end
end
end
+
+API::Ci::JobArtifacts.prepend_mod
diff --git a/lib/api/ci/jobs.rb b/lib/api/ci/jobs.rb
index 9e41e1c0d8f..bb57a717f7c 100644
--- a/lib/api/ci/jobs.rb
+++ b/lib/api/ci/jobs.rb
@@ -49,13 +49,15 @@ module API
end
# rubocop: disable CodeReuse/ActiveRecord
get ':id/jobs', urgency: :low, feature_category: :continuous_integration do
+ check_rate_limit!(:jobs_index, scope: current_user) if enforce_jobs_api_rate_limits(@project)
+
authorize_read_builds!
builds = user_project.builds.order('id DESC')
builds = filter_builds(builds, params[:scope])
-
builds = builds.preload(:user, :job_artifacts_archive, :job_artifacts, :runner, :tags, pipeline: :project)
- present paginate(builds), with: Entities::Ci::Job
+
+ present paginate(builds, without_count: true), with: Entities::Ci::Job
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -255,16 +257,19 @@ module API
pipeline = current_authenticated_job.pipeline
project = current_authenticated_job.project
- agent_authorizations = ::Clusters::AgentAuthorizationsFinder.new(project).execute
project_groups = project.group&.self_and_ancestor_ids&.map { |id| { id: id } } || []
user_access_level = project.team.max_member_access(current_user.id)
roles_in_project = Gitlab::Access.sym_options_with_owner
.select { |_role, role_access_level| role_access_level <= user_access_level }
.map(&:first)
- environment = if persisted_environment = current_authenticated_job.actual_persisted_environment
- { tier: persisted_environment.tier, slug: persisted_environment.slug }
- end
+ persisted_environment = current_authenticated_job.actual_persisted_environment
+ environment = { tier: persisted_environment.tier, slug: persisted_environment.slug } if persisted_environment
+
+ agent_authorizations = ::Clusters::Agents::FilterAuthorizationsService.new(
+ ::Clusters::AgentAuthorizationsFinder.new(project).execute,
+ environment: persisted_environment&.name
+ ).execute
# See https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/doc/kubernetes_ci_access.md#apiv4joballowed_agents-api
{
diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb
index c7d1887638a..b073eb49bf1 100644
--- a/lib/api/ci/runner.rb
+++ b/lib/api/ci/runner.rb
@@ -229,15 +229,17 @@ module API
params do
requires :id, type: Integer, desc: %q(Job's ID)
optional :token, type: String, desc: %q(Job's authentication token)
+ optional :debug_trace, type: Boolean, desc: %q(Enable or Disable the debug trace)
end
patch '/:id/trace', urgency: :low, feature_category: :continuous_integration do
job = authenticate_job!(heartbeat_runner: true)
error!('400 Missing header Content-Range', 400) unless request.headers.key?('Content-Range')
content_range = request.headers['Content-Range']
+ debug_trace = Gitlab::Utils.to_boolean(params[:debug_trace])
result = ::Ci::AppendBuildTraceService
- .new(job, content_range: content_range)
+ .new(job, content_range: content_range, debug_trace: debug_trace)
.execute(request.body.read)
if result.status == 403
@@ -256,7 +258,7 @@ module API
header 'X-GitLab-Trace-Update-Interval', job.trace.update_interval.to_s
end
- desc 'Authorize artifacts uploading for job' do
+ desc 'Authorize uploading job artifact' do
http_codes [[200, 'Upload allowed'],
[403, 'Forbidden'],
[405, 'Artifacts support not enabled'],
@@ -270,7 +272,7 @@ module API
# In current runner, filesize parameter would be empty here. This is because archive is streamed by runner,
# so the archive size is not known ahead of time. Streaming is done to not use additional I/O on
# Runner to first save, and then send via Network.
- optional :filesize, type: Integer, desc: %q(Artifacts filesize)
+ optional :filesize, type: Integer, desc: %q(Size of artifact file)
optional :artifact_type, type: String, desc: %q(The type of artifact),
default: 'archive', values: ::Ci::JobArtifact.file_types.keys
@@ -292,7 +294,7 @@ module API
end
end
- desc 'Upload artifacts for job' do
+ desc 'Upload a job artifact' do
success Entities::Ci::JobRequest::Response
http_codes [[201, 'Artifact uploaded'],
[400, 'Bad request'],
@@ -304,7 +306,7 @@ module API
requires :id, type: Integer, desc: %q(Job's ID)
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: %(The artifact file to store (generated by Multipart middleware)), documentation: { type: 'file' }
optional :token, type: String, desc: %q(Job's authentication token)
- optional :expire_in, type: String, desc: %q(Specify when artifacts should expire)
+ optional :expire_in, type: String, desc: %q(Specify when artifact should expire)
optional :artifact_type, type: String, desc: %q(The type of artifact),
default: 'archive', values: ::Ci::JobArtifact.file_types.keys
optional :artifact_format, type: String, desc: %q(The format of artifact),
@@ -333,7 +335,7 @@ module API
end
desc 'Download the artifacts file for job' do
- http_codes [[200, 'Upload allowed'],
+ http_codes [[200, 'Download allowed'],
[401, 'Unauthorized'],
[403, 'Forbidden'],
[404, 'Artifact not found']]
diff --git a/lib/api/ci/runners.rb b/lib/api/ci/runners.rb
index 988c3f4f566..4a6c58b4987 100644
--- a/lib/api/ci/runners.rb
+++ b/lib/api/ci/runners.rb
@@ -58,19 +58,19 @@ module API
end
def authenticate_show_runner!(runner)
- return if runner.instance_type? || current_user.admin?
+ return if runner.instance_type? || current_user.can_read_all_resources?
forbidden!("No access granted") unless can?(current_user, :read_runner, runner)
end
def authenticate_update_runner!(runner)
- return if current_user.admin?
+ return if current_user.can_admin_all_resources?
forbidden!("No access granted") unless can?(current_user, :update_runner, runner)
end
def authenticate_delete_runner!(runner)
- return if current_user.admin?
+ return if current_user.can_admin_all_resources?
forbidden!("Runner associated with more than one project") if runner.runner_projects.count > 1
forbidden!("No access granted") unless can?(current_user, :delete_runner, runner)
@@ -79,14 +79,14 @@ module API
def authenticate_enable_runner!(runner)
forbidden!("Runner is a group runner") if runner.group_type?
- return if current_user.admin?
+ return if current_user.can_admin_all_resources?
forbidden!("Runner is locked") if runner.locked?
forbidden!("No access granted") unless can?(current_user, :assign_runner, runner)
end
def authenticate_list_runners_jobs!(runner)
- return if current_user.admin?
+ return if current_user.can_read_all_resources?
forbidden!("No access granted") unless can?(current_user, :read_builds, runner)
end
diff --git a/lib/api/ci/secure_files.rb b/lib/api/ci/secure_files.rb
index dd628a3413f..6483abcc74e 100644
--- a/lib/api/ci/secure_files.rb
+++ b/lib/api/ci/secure_files.rb
@@ -7,7 +7,6 @@ module API
before do
authenticate!
- feature_flag_enabled?
authorize! :read_secure_files, user_project
end
@@ -16,11 +15,15 @@ module API
default_format :json
params do
- requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project owned by the
+ authenticated user'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc 'List all Secure Files for a Project'
+ desc 'Get list of secure files in a project' do
+ success Entities::Ci::SecureFile
+ tags %w[secure_files]
+ end
params do
use :pagination
end
@@ -30,9 +33,13 @@ module API
present paginate(secure_files), with: Entities::Ci::SecureFile
end
- desc 'Get an individual Secure File'
+ desc 'Get the details of a specific secure file in a project' do
+ success Entities::Ci::SecureFile
+ tags %w[secure_files]
+ failure [{ code: 404, message: '404 Not found' }]
+ end
params do
- requires :id, type: Integer, desc: 'The Secure File ID'
+ requires :id, type: Integer, desc: 'The ID of a secure file'
end
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: true
@@ -41,7 +48,10 @@ module API
present secure_file, with: Entities::Ci::SecureFile
end
- desc 'Download a Secure File'
+ desc 'Download secure file' do
+ failure [{ code: 404, message: '404 Not found' }]
+ tags %w[secure_files]
+ end
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: true
get ':id/secure_files/:secure_file_id/download' do
secure_file = user_project.secure_files.find(params[:secure_file_id])
@@ -58,10 +68,15 @@ module API
authorize! :admin_secure_files, user_project
end
- desc 'Upload a Secure File'
+ desc 'Create a secure file' do
+ success Entities::Ci::SecureFile
+ tags %w[secure_files]
+ failure [{ code: 400, message: '400 Bad Request' }]
+ end
params do
- requires :name, type: String, desc: 'The name of the file'
- requires :file, types: [Rack::Multipart::UploadedFile, ::API::Validations::Types::WorkhorseFile], desc: 'The secure file to be uploaded', documentation: { type: 'file' }
+ requires :name, type: String, desc: 'The name of the file being uploaded. The filename must be unique within
+ the project'
+ requires :file, types: [Rack::Multipart::UploadedFile, ::API::Validations::Types::WorkhorseFile], desc: 'The secure file being uploaded', documentation: { type: 'file' }
end
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: true
post ':id/secure_files' do
@@ -74,17 +89,17 @@ module API
file_too_large! unless secure_file.file.size < ::Ci::SecureFile::FILE_SIZE_LIMIT.to_i
if secure_file.save
- if Feature.enabled?(:secure_files_metadata_parsers, user_project)
- ::Ci::ParseSecureFileMetadataWorker.perform_async(secure_file.id) # rubocop:disable CodeReuse/Worker
- end
-
+ ::Ci::ParseSecureFileMetadataWorker.perform_async(secure_file.id) # rubocop:disable CodeReuse/Worker
present secure_file, with: Entities::Ci::SecureFile
else
render_validation_error!(secure_file)
end
end
- desc 'Delete an individual Secure File'
+ desc 'Remove a secure file' do
+ tags %w[secure_files]
+ failure [{ code: 404, message: '404 Not found' }]
+ end
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: true
delete ':id/secure_files/:secure_file_id' do
secure_file = user_project.secure_files.find(params[:secure_file_id])
@@ -97,10 +112,6 @@ module API
end
helpers do
- def feature_flag_enabled?
- service_unavailable! unless Feature.enabled?(:ci_secure_files, user_project)
- end
-
def read_only_feature_flag_enabled?
service_unavailable! if Feature.enabled?(:ci_secure_files_read_only, user_project, type: :ops)
end
diff --git a/lib/api/clusters/agent_tokens.rb b/lib/api/clusters/agent_tokens.rb
index f65ae465b3d..68eef21903d 100644
--- a/lib/api/clusters/agent_tokens.rb
+++ b/lib/api/clusters/agent_tokens.rb
@@ -27,7 +27,8 @@ module API
use :pagination
end
get do
- agent_tokens = ::Clusters::AgentTokensFinder.new(user_project, current_user, params[:agent_id]).execute
+ agent = ::Clusters::AgentsFinder.new(user_project, current_user).find(params[:agent_id])
+ agent_tokens = ::Clusters::AgentTokensFinder.new(agent, current_user).execute
present paginate(agent_tokens), with: Entities::Clusters::AgentTokenBasic
end
@@ -42,8 +43,7 @@ module API
end
get ':token_id' do
agent = ::Clusters::AgentsFinder.new(user_project, current_user).find(params[:agent_id])
-
- token = agent.agent_tokens.find(params[:token_id])
+ token = ::Clusters::AgentTokensFinder.new(agent, current_user).find(params[:token_id])
present token, with: Entities::Clusters::AgentToken
end
@@ -84,8 +84,7 @@ module API
authorize! :admin_cluster, user_project
agent = ::Clusters::AgentsFinder.new(user_project, current_user).find(params[:agent_id])
-
- token = agent.agent_tokens.find(params[:token_id])
+ token = ::Clusters::AgentTokensFinder.new(agent, current_user).find(params[:token_id])
# Skipping explicit error handling and relying on exceptions
token.revoked!
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index 954b572c9b1..531235dc9b2 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -110,13 +110,23 @@ module API
authorize! :update_pipeline, pipeline
+ # rubocop: disable Performance/ActiveRecordSubtransactionMethods
+ stage = pipeline.stages.safe_find_or_create_by!(name: 'external') do |stage|
+ stage.position = GenericCommitStatus::EXTERNAL_STAGE_IDX
+ stage.project = pipeline.project
+ end
+ # rubocop: enable Performance/ActiveRecordSubtransactionMethods
+
status = GenericCommitStatus.running_or_pending.find_or_initialize_by(
project: user_project,
pipeline: pipeline,
name: name,
ref: ref,
user: current_user,
- protected: user_project.protected_for?(ref)
+ protected: user_project.protected_for?(ref),
+ ci_stage: stage,
+ stage_idx: stage.position,
+ stage: 'external'
)
updatable_optional_attributes = %w[target_url description coverage]
@@ -152,7 +162,7 @@ module API
def all_matching_pipelines
pipelines = user_project.ci_pipelines.newest_first(sha: commit.sha)
pipelines = pipelines.for_ref(params[:ref]) if params[:ref]
- pipelines = pipelines.for_id(params[:pipeline_id]) if params[:pipeline_id]
+ pipelines = pipelines.id_in(params[:pipeline_id]) if params[:pipeline_id]
pipelines
end
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 63a13b83a9b..ad2bbf90917 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -24,6 +24,26 @@ module API
forbidden!("You are not allowed to push into this branch")
end
end
+
+ def track_commit_events
+ return unless find_user_from_warden
+
+ Gitlab::UsageDataCounters::WebIdeCounter.increment_commits_count
+ Gitlab::UsageDataCounters::EditorUniqueCounter.track_web_ide_edit_action(author: current_user, project: user_project)
+ namespace = user_project.namespace
+
+ return unless Feature.enabled?(:route_hll_to_snowplow_phase3, namespace)
+
+ Gitlab::Tracking.event(
+ 'API::Commits',
+ :commit,
+ project: user_project,
+ namespace: namespace,
+ user: current_user,
+ label: 'counts.web_ide_commits',
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis, key_path: 'counts.web_ide_commits').to_context]
+ )
+ end
end
params do
@@ -204,10 +224,7 @@ module API
if result[:status] == :success
commit_detail = user_project.repository.commit(result[:result])
- if find_user_from_warden
- Gitlab::UsageDataCounters::WebIdeCounter.increment_commits_count
- Gitlab::UsageDataCounters::EditorUniqueCounter.track_web_ide_edit_action(author: current_user, project: user_project)
- end
+ track_commit_events
present commit_detail, with: Entities::CommitDetail, include_stats: params[:stats], current_user: current_user
else
diff --git a/lib/api/composer_packages.rb b/lib/api/composer_packages.rb
index d9806fa37d1..3819e6d236d 100644
--- a/lib/api/composer_packages.rb
+++ b/lib/api/composer_packages.rb
@@ -61,32 +61,56 @@ module API
end
params do
- requires :id, type: String, desc: 'The ID of a group'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of a group'
end
resource :group, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- before do
+ after_validation do
user_group
end
- desc 'Composer packages endpoint at group level'
+ desc 'Composer packages endpoint at group level' do
+ detail 'This feature was introduced in GitLab 13.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[composer_packages]
+ end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
get ':id/-/packages/composer/packages', urgency: :low do
presenter.root
end
- desc 'Composer packages endpoint at group level for packages list'
+ desc 'Composer packages endpoint at group level for packages list' do
+ detail 'This feature was introduced in GitLab 13.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[composer_packages]
+ end
params do
- requires :sha, type: String, desc: 'Shasum of current json'
+ requires :sha, type: String, desc: 'Shasum of current json', documentation: { example: '673594f85a55fe3c0eb45df7bd2fa9d95a1601ab' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
get ':id/-/packages/composer/p/:sha', urgency: :low do
presenter.provider
end
- desc 'Composer v2 packages p2 endpoint at group level for package versions metadata'
+ desc 'Composer v2 packages p2 endpoint at group level for package versions metadata' do
+ detail 'This feature was introduced in GitLab 13.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[composer_packages]
+ end
params do
- requires :package_name, type: String, file_path: true, desc: 'The Composer package name'
+ requires :package_name, type: String, file_path: true, desc: 'The Composer package name', documentation: { example: 'my-composer-package' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
get ':id/-/packages/composer/p2/*package_name', requirements: COMPOSER_ENDPOINT_REQUIREMENTS, file_path: true, urgency: :low do
@@ -95,9 +119,17 @@ module API
presenter.package_versions
end
- desc 'Composer packages endpoint at group level for package versions metadata'
+ desc 'Composer packages endpoint at group level for package versions metadata' do
+ detail 'This feature was introduced in GitLab 12.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[composer_packages]
+ end
params do
- requires :package_name, type: String, file_path: true, desc: 'The Composer package name'
+ requires :package_name, type: String, file_path: true, desc: 'The Composer package name', documentation: { example: 'my-composer-package' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
get ':id/-/packages/composer/*package_name', requirements: COMPOSER_ENDPOINT_REQUIREMENTS, file_path: true, urgency: :low do
@@ -109,17 +141,27 @@ module API
end
params do
- requires :id, type: Integer, desc: 'The ID of a project'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of a project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc 'Composer packages endpoint for registering packages'
namespace ':id/packages/composer' do
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
+ desc 'Composer packages endpoint for registering packages' do
+ detail 'This feature was introduced in GitLab 13.1'
+ success code: 201
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[composer_packages]
+ end
params do
- optional :branch, type: String, desc: 'The name of the branch'
- optional :tag, type: String, desc: 'The name of the tag'
+ optional :branch, type: String, desc: 'The name of the branch', documentation: { example: 'release' }
+ optional :tag, type: String, desc: 'The name of the tag', documentation: { example: 'v1.0.0' }
exactly_one_of :tag, :branch
end
post urgency: :low do
@@ -142,15 +184,25 @@ module API
created!
end
+ desc 'Composer package endpoint to download a package archive' do
+ detail 'This feature was introduced in GitLab 13.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[composer_packages]
+ end
params do
- requires :sha, type: String, desc: 'Shasum of current json'
- requires :package_name, type: String, file_path: true, desc: 'The Composer package name'
+ requires :sha, type: String, desc: 'Shasum of current json', documentation: { example: '673594f85a55fe3c0eb45df7bd2fa9d95a1601ab' }
+ requires :package_name, type: String, file_path: true, desc: 'The Composer package name', documentation: { example: 'my-composer-package' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
get 'archives/*package_name', urgency: :default do
- authorize_read_package!(authorized_user_project)
+ project = authorized_user_project(action: :read_package)
- package = authorized_user_project
+ package = project
.packages
.composer
.with_name(params[:package_name])
@@ -160,10 +212,10 @@ module API
not_found! unless metadata
- track_package_event('pull_package', :composer, project: authorized_user_project, namespace: authorized_user_project.namespace)
+ track_package_event('pull_package', :composer, project: project, namespace: project.namespace)
package.touch_last_downloaded_at
- send_git_archive authorized_user_project.repository, ref: metadata.target_sha, format: 'zip', append_sha: true
+ send_git_archive project.repository, ref: metadata.target_sha, format: 'zip', append_sha: true
end
end
end
diff --git a/lib/api/conan_project_packages.rb b/lib/api/conan_project_packages.rb
index 636b5dca5ed..e282443e85c 100644
--- a/lib/api/conan_project_packages.rb
+++ b/lib/api/conan_project_packages.rb
@@ -4,7 +4,7 @@
module API
class ConanProjectPackages < ::API::Base
params do
- requires :id, type: Integer, desc: 'The ID of a project', regexp: %r{\A[1-9]\d*\z}
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
diff --git a/lib/api/concerns/packages/conan_endpoints.rb b/lib/api/concerns/packages/conan_endpoints.rb
index fdbffb1689b..e65e8f8710c 100644
--- a/lib/api/concerns/packages/conan_endpoints.rb
+++ b/lib/api/concerns/packages/conan_endpoints.rb
@@ -53,6 +53,11 @@ module API
desc 'Ping the Conan API' do
detail 'This feature was introduced in GitLab 12.2'
+ success code: 200
+ failure [
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -63,10 +68,15 @@ module API
desc 'Search for packages' do
detail 'This feature was introduced in GitLab 12.4'
+ success code: 200
+ failure [
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
params do
- requires :q, type: String, desc: 'Search query'
+ requires :q, type: String, desc: 'Search query', documentation: { example: 'Hello*' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -86,6 +96,12 @@ module API
desc 'Authenticate user against conan CLI' do
detail 'This feature was introduced in GitLab 12.2'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -98,6 +114,12 @@ module API
desc 'Check for valid user credentials per conan CLI' do
detail 'This feature was introduced in GitLab 12.4'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -109,10 +131,10 @@ module API
end
params do
- requires :package_name, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package name'
- requires :package_version, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package version'
- requires :package_username, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package username'
- requires :package_channel, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package channel'
+ requires :package_name, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package name', documentation: { example: 'my-package' }
+ requires :package_version, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package version', documentation: { example: '1.0' }
+ requires :package_username, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package username', documentation: { example: 'my-group+my-project' }
+ requires :package_channel, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package channel', documentation: { example: 'stable' }
end
namespace 'conans/:package_name/:package_version/:package_username/:package_channel', requirements: PACKAGE_REQUIREMENTS do
after_validation do
@@ -122,14 +144,21 @@ module API
# Get the snapshot
#
# the snapshot is a hash of { filename: md5 hash }
- # md5 hash is the has of that file. This hash is used to diff the files existing on the client
+ # md5 hash is the hash of that file. This hash is used to diff the files existing on the client
# to determine which client files need to be uploaded if no recipe exists the snapshot is empty
desc 'Package Snapshot' do
detail 'This feature was introduced in GitLab 12.5'
+ success code: 200, model: ::API::Entities::ConanPackage::ConanPackageSnapshot
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
params do
- requires :conan_package_reference, type: String, desc: 'Conan package ID'
+ requires :conan_package_reference, type: String, desc: 'Conan package ID', documentation: { example: '103f6067a947f366ef91fc1b7da351c588d1827f' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -149,6 +178,13 @@ module API
desc 'Recipe Snapshot' do
detail 'This feature was introduced in GitLab 12.5'
+ success code: 200, model: ::API::Entities::ConanPackage::ConanRecipeSnapshot
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -168,9 +204,16 @@ module API
# where the url is the download url for the file
desc 'Package Digest' do
detail 'This feature was introduced in GitLab 12.5'
+ success code: 200, model: ::API::Entities::ConanPackage::ConanPackageManifest
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
params do
- requires :conan_package_reference, type: String, desc: 'Conan package ID'
+ requires :conan_package_reference, type: String, desc: 'Conan package ID', documentation: { example: '103f6067a947f366ef91fc1b7da351c588d1827f' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -181,6 +224,13 @@ module API
desc 'Recipe Digest' do
detail 'This feature was introduced in GitLab 12.5'
+ success code: 200, model: ::API::Entities::ConanPackage::ConanRecipeManifest
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -197,10 +247,17 @@ module API
# where the url is the download url for the file
desc 'Package Download Urls' do
detail 'This feature was introduced in GitLab 12.5'
+ success code: 200, model: ::API::Entities::ConanPackage::ConanPackageManifest
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
params do
- requires :conan_package_reference, type: String, desc: 'Conan package ID'
+ requires :conan_package_reference, type: String, desc: 'Conan package ID', documentation: { example: '103f6067a947f366ef91fc1b7da351c588d1827f' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -211,6 +268,13 @@ module API
desc 'Recipe Download Urls' do
detail 'This feature was introduced in GitLab 12.5'
+ success code: 200, model: ::API::Entities::ConanPackage::ConanRecipeManifest
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -228,10 +292,17 @@ module API
# where the url is the upload url for the file that the conan client will use
desc 'Package Upload Urls' do
detail 'This feature was introduced in GitLab 12.4'
+ success code: 200, model: ::API::Entities::ConanPackage::ConanUploadUrls
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
params do
- requires :conan_package_reference, type: String, desc: 'Conan package ID'
+ requires :conan_package_reference, type: String, desc: 'Conan package ID', documentation: { example: '103f6067a947f366ef91fc1b7da351c588d1827f' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -245,6 +316,13 @@ module API
desc 'Recipe Upload Urls' do
detail 'This feature was introduced in GitLab 12.4'
+ success code: 200, model: ::API::Entities::ConanPackage::ConanUploadUrls
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -258,6 +336,13 @@ module API
desc 'Delete Package' do
detail 'This feature was introduced in GitLab 12.5'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -272,11 +357,11 @@ module API
end
params do
- requires :package_name, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package name'
- requires :package_version, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package version'
- requires :package_username, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package username'
- requires :package_channel, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package channel'
- requires :recipe_revision, type: String, regexp: CONAN_REVISION_REGEX, desc: 'Conan Recipe Revision'
+ requires :package_name, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package name', documentation: { example: 'my-package' }
+ requires :package_version, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package version', documentation: { example: '1.0' }
+ requires :package_username, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package username', documentation: { example: 'my-group+my-project' }
+ requires :package_channel, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package channel', documentation: { example: 'stable' }
+ requires :recipe_revision, type: String, regexp: CONAN_REVISION_REGEX, desc: 'Conan Recipe Revision', documentation: { example: '0' }
end
namespace 'files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision', requirements: PACKAGE_REQUIREMENTS do
before do
@@ -288,12 +373,19 @@ module API
end
params do
- requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES
+ requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES, documentation: { example: 'conanfile.py' }
end
namespace 'export/:file_name', requirements: FILE_NAME_REQUIREMENTS do
desc 'Download recipe files' do
detail 'This feature was introduced in GitLab 12.6'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -304,6 +396,14 @@ module API
desc 'Upload recipe package files' do
detail 'This feature was introduced in GitLab 12.6'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
params do
@@ -318,6 +418,14 @@ module API
desc 'Workhorse authorize the conan recipe file' do
detail 'This feature was introduced in GitLab 12.6'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -328,13 +436,19 @@ module API
end
params do
- requires :conan_package_reference, type: String, desc: 'Conan Package ID'
- requires :package_revision, type: String, desc: 'Conan Package Revision'
- requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES
+ requires :conan_package_reference, type: String, desc: 'Conan Package ID', documentation: { example: '103f6067a947f366ef91fc1b7da351c588d1827f' }
+ requires :package_revision, type: String, desc: 'Conan Package Revision', documentation: { example: '0' }
+ requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES, documentation: { example: 'conaninfo.txt' }
end
namespace 'package/:conan_package_reference/:package_revision/:file_name', requirements: FILE_NAME_REQUIREMENTS do
desc 'Download package files' do
detail 'This feature was introduced in GitLab 12.5'
+ success code: 200
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -345,6 +459,14 @@ module API
desc 'Workhorse authorize the conan package file' do
detail 'This feature was introduced in GitLab 12.6'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -355,6 +477,14 @@ module API
desc 'Upload package files' do
detail 'This feature was introduced in GitLab 12.6'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
params do
diff --git a/lib/api/concerns/packages/debian_distribution_endpoints.rb b/lib/api/concerns/packages/debian_distribution_endpoints.rb
index 380966136df..76b996f2301 100644
--- a/lib/api/concerns/packages/debian_distribution_endpoints.rb
+++ b/lib/api/concerns/packages/debian_distribution_endpoints.rb
@@ -25,21 +25,23 @@ module API
namespace 'debian_distributions' do
helpers do
params :optional_distribution_params do
- optional :suite, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Suite'
- optional :origin, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Origin'
- optional :label, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Label'
- optional :version, type: String, regexp: Gitlab::Regex.debian_version_regex, desc: 'The Debian Version'
- optional :description, type: String, desc: 'The Debian Description'
- optional :valid_time_duration_seconds, type: Integer, desc: 'The duration before the Release file should be considered expired by the client'
+ optional :suite, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Suite', documentation: { example: 'unstable' }
+ optional :origin, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Origin', documentation: { example: 'Grep' }
+ optional :label, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Label', documentation: { example: 'grep.be' }
+ optional :version, type: String, regexp: Gitlab::Regex.debian_version_regex, desc: 'The Debian Version', documentation: { example: '12' }
+ optional :description, type: String, desc: 'The Debian Description', documentation: { example: 'My description' }
+ optional :valid_time_duration_seconds, type: Integer, desc: 'The duration before the Release file should be considered expired by the client', documentation: { example: 604800 }
optional :components, type: Array[String],
coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
regexp: Gitlab::Regex.debian_component_regex,
- desc: 'The list of Components'
+ desc: 'The list of Components',
+ documentation: { example: 'main' }
optional :architectures, type: Array[String],
coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
regexp: Gitlab::Regex.debian_architecture_regex,
- desc: 'The list of Architectures'
+ desc: 'The list of Architectures',
+ documentation: { example: 'amd64' }
end
end
@@ -63,11 +65,18 @@ module API
# POST {projects|groups}/:id/debian_distributions
desc 'Create a Debian Distribution' do
detail 'This feature was introduced in 14.0'
- success ::API::Entities::Packages::Debian::Distribution
+ success code: 201, model: ::API::Entities::Packages::Debian::Distribution
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_distribution]
end
params do
- requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename', documentation: { example: 'unstable' }
use :optional_distribution_params
end
post '/' do
@@ -87,12 +96,18 @@ module API
# GET {projects|groups}/:id/debian_distributions
desc 'Get a list of Debian Distributions' do
detail 'This feature was introduced in 14.0'
- success ::API::Entities::Packages::Debian::Distribution
+ success code: 200, model: ::API::Entities::Packages::Debian::Distribution
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_distribution]
end
params do
use :pagination
- optional :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ optional :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename', documentation: { example: 'unstable' }
use :optional_distribution_params
end
get '/' do
@@ -107,11 +122,17 @@ module API
# GET {projects|groups}/:id/debian_distributions/:codename
desc 'Get a Debian Distribution' do
detail 'This feature was introduced in 14.0'
- success ::API::Entities::Packages::Debian::Distribution
+ success code: 200, model: ::API::Entities::Packages::Debian::Distribution
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_distribution]
end
params do
- requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename', documentation: { example: 'unstable' }
end
get '/:codename' do
authorize_read_package!(project_or_group)
@@ -122,11 +143,17 @@ module API
# GET {projects|groups}/:id/debian_distributions/:codename/key
desc 'Get a Debian Distribution Key' do
detail 'This feature was introduced in 14.4'
- success ::API::Entities::Packages::Debian::Distribution
+ success code: 200, model: ::API::Entities::Packages::Debian::Distribution
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_distribution]
end
params do
- requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename', documentation: { example: 'unstable' }
end
get '/:codename/key.asc' do
authorize_read_package!(project_or_group)
@@ -141,11 +168,18 @@ module API
# PUT {projects|groups}/:id/debian_distributions/:codename
desc 'Update a Debian Distribution' do
detail 'This feature was introduced in 14.0'
- success ::API::Entities::Packages::Debian::Distribution
+ success code: 200, model: ::API::Entities::Packages::Debian::Distribution
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_distribution]
end
params do
- requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename', documentation: { example: 'unstable' }
use :optional_distribution_params
end
put '/:codename' do
@@ -165,10 +199,18 @@ module API
# DELETE {projects|groups}/:id/debian_distributions/:codename
desc 'Delete a Debian Distribution' do
detail 'This feature was introduced in 14.0'
+ success code: 202
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_distribution]
end
params do
- requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename', documentation: { example: 'unstable' }
use :optional_distribution_params
end
delete '/:codename' do
diff --git a/lib/api/concerns/packages/debian_package_endpoints.rb b/lib/api/concerns/packages/debian_package_endpoints.rb
index 2883944a745..842250d351b 100644
--- a/lib/api/concerns/packages/debian_package_endpoints.rb
+++ b/lib/api/concerns/packages/debian_package_endpoints.rb
@@ -24,11 +24,11 @@ module API
helpers do
params :shared_package_file_params do
- requires :distribution, type: String, desc: 'The Debian Codename or Suite', regexp: Gitlab::Regex.debian_distribution_regex
- requires :letter, type: String, desc: 'The Debian Classification (first-letter or lib-first-letter)'
- requires :package_name, type: String, desc: 'The Debian Source Package Name', regexp: Gitlab::Regex.debian_package_name_regex
- requires :package_version, type: String, desc: 'The Debian Source Package Version', regexp: Gitlab::Regex.debian_version_regex
- requires :file_name, type: String, desc: 'The Debian File Name'
+ requires :distribution, type: String, desc: 'The Debian Codename or Suite', regexp: Gitlab::Regex.debian_distribution_regex, documentation: { example: 'my-distro' }
+ requires :letter, type: String, desc: 'The Debian Classification (first-letter or lib-first-letter)', documentation: { example: 'a' }
+ requires :package_name, type: String, desc: 'The Debian Source Package Name', regexp: Gitlab::Regex.debian_package_name_regex, documentation: { example: 'my-pkg' }
+ requires :package_version, type: String, desc: 'The Debian Source Package Version', regexp: Gitlab::Regex.debian_version_regex, documentation: { example: '1.0.0' }
+ requires :file_name, type: String, desc: 'The Debian File Name', documentation: { example: 'example_1.0.0~alpha2_amd64.deb' }
end
def distribution_from!(container)
@@ -79,7 +79,7 @@ module API
content_type :txt, 'text/plain'
params do
- requires :distribution, type: String, desc: 'The Debian Codename or Suite', regexp: Gitlab::Regex.debian_distribution_regex
+ requires :distribution, type: String, desc: 'The Debian Codename or Suite', regexp: Gitlab::Regex.debian_distribution_regex, documentation: { example: 'my-distro' }
end
namespace 'dists/*distribution', requirements: DISTRIBUTION_REQUIREMENTS do
@@ -87,6 +87,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format#A.22Release.22_files
desc 'The Release file signature' do
detail 'This feature was introduced in GitLab 13.5'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -98,6 +106,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format#A.22Release.22_files
desc 'The unsigned Release file' do
detail 'This feature was introduced in GitLab 13.5'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -109,6 +125,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format#A.22Release.22_files
desc 'The signed Release file' do
detail 'This feature was introduced in GitLab 13.5'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -117,12 +141,12 @@ module API
end
params do
- requires :component, type: String, desc: 'The Debian Component', regexp: Gitlab::Regex.debian_component_regex
+ requires :component, type: String, desc: 'The Debian Component', regexp: Gitlab::Regex.debian_component_regex, documentation: { example: 'main' }
end
namespace ':component', requirements: COMPONENT_ARCHITECTURE_REQUIREMENTS do
params do
- requires :architecture, type: String, desc: 'The Debian Architecture', regexp: Gitlab::Regex.debian_architecture_regex
+ requires :architecture, type: String, desc: 'The Debian Architecture', regexp: Gitlab::Regex.debian_architecture_regex, documentation: { example: 'binary-amd64' }
end
namespace 'debian-installer/binary-:architecture' do
@@ -130,6 +154,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format#A.22Packages.22_Indices
desc 'The installer (udeb) binary files index' do
detail 'This feature was introduced in GitLab 15.4'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -141,6 +173,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format?action=show&redirect=RepositoryFormat#indices_acquisition_via_hashsums_.28by-hash.29
desc 'The installer (udeb) binary files index by hash' do
detail 'This feature was introduced in GitLab 15.4'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -154,6 +194,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format#A.22Sources.22_Indices
desc 'The source files index' do
detail 'This feature was introduced in GitLab 15.4'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -165,6 +213,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format?action=show&redirect=RepositoryFormat#indices_acquisition_via_hashsums_.28by-hash.29
desc 'The source files index by hash' do
detail 'This feature was introduced in GitLab 15.4'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -174,7 +230,7 @@ module API
end
params do
- requires :architecture, type: String, desc: 'The Debian Architecture', regexp: Gitlab::Regex.debian_architecture_regex
+ requires :architecture, type: String, desc: 'The Debian Architecture', regexp: Gitlab::Regex.debian_architecture_regex, documentation: { example: 'binary-amd64' }
end
namespace 'binary-:architecture', requirements: COMPONENT_ARCHITECTURE_REQUIREMENTS do
@@ -182,6 +238,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format#A.22Packages.22_Indices
desc 'The binary files index' do
detail 'This feature was introduced in GitLab 13.5'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -193,6 +257,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format?action=show&redirect=RepositoryFormat#indices_acquisition_via_hashsums_.28by-hash.29
desc 'The binary files index by hash' do
detail 'This feature was introduced in GitLab 15.4'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
diff --git a/lib/api/concerns/packages/npm_endpoints.rb b/lib/api/concerns/packages/npm_endpoints.rb
index 4cc680068b6..f26b3a1d8c2 100644
--- a/lib/api/concerns/packages/npm_endpoints.rb
+++ b/lib/api/concerns/packages/npm_endpoints.rb
@@ -26,13 +26,39 @@ module API
authenticate_non_get!
end
+ helpers do
+ def redirect_or_present_audit_report
+ redirect_registry_request(
+ forward_to_registry: true,
+ package_type: :npm,
+ path: options[:path][0],
+ body: Gitlab::Json.dump(request.POST),
+ target: project_or_nil,
+ method: route.request_method
+ ) do
+ authorize_read_package!(project)
+
+ status :ok
+ present []
+ end
+ end
+ end
+
params do
requires :package_name, type: String, desc: 'Package name'
end
namespace '-/package/*package_name' do
desc 'Get all tags for a given an NPM package' do
detail 'This feature was introduced in GitLab 12.7'
- success ::API::Entities::NpmPackageTag
+ success [
+ { code: 200, model: ::API::Entities::NpmPackageTag }
+ ]
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[npm_packages]
end
get 'dist-tags', format: false, requirements: ::API::Helpers::Packages::Npm::NPM_ENDPOINT_REQUIREMENTS do
package_name = params[:package_name]
@@ -56,6 +82,14 @@ module API
namespace 'dist-tags/:tag', requirements: ::API::Helpers::Packages::Npm::NPM_ENDPOINT_REQUIREMENTS do
desc 'Create or Update the given tag for the given NPM package and version' do
detail 'This feature was introduced in GitLab 12.7'
+ success code: 204
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[npm_packages]
end
put format: false do
package_name = params[:package_name]
@@ -79,6 +113,14 @@ module API
desc 'Deletes the given tag' do
detail 'This feature was introduced in GitLab 12.7'
+ success code: 204
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[npm_packages]
end
delete format: false do
package_name = params[:package_name]
@@ -104,6 +146,16 @@ module API
desc 'NPM registry metadata endpoint' do
detail 'This feature was introduced in GitLab 11.8'
+ success [
+ { code: 200, model: ::API::Entities::NpmPackage, message: 'Ok' },
+ { code: 302, message: 'Found (redirect)' }
+ ]
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[npm_packages]
end
params do
requires :package_name, type: String, desc: 'Package name'
@@ -130,6 +182,44 @@ module API
with: ::API::Entities::NpmPackage
end
end
+
+ desc 'NPM registry bulk advisory endpoint' do
+ detail 'This feature was introduced in GitLab 15.6'
+ success [
+ { code: 200, message: 'Ok' },
+ { code: 307, message: 'Temporary Redirect' }
+ ]
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ is_array true
+ tags %w[npm_packages]
+ end
+ route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
+ post '-/npm/v1/security/advisories/bulk' do
+ redirect_or_present_audit_report
+ end
+
+ desc 'NPM registry quick audit endpoint' do
+ detail 'This feature was introduced in GitLab 15.6'
+ success [
+ { code: 200, message: 'Ok' },
+ { code: 307, message: 'Temporary Redirect' }
+ ]
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ is_array true
+ tags %w[npm_packages]
+ end
+ route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
+ post '-/npm/v1/security/audits/quick' do
+ redirect_or_present_audit_report
+ end
end
end
end
diff --git a/lib/api/concerns/packages/nuget_endpoints.rb b/lib/api/concerns/packages/nuget_endpoints.rb
index e0328e488c6..31ecb529c3c 100644
--- a/lib/api/concerns/packages/nuget_endpoints.rb
+++ b/lib/api/concerns/packages/nuget_endpoints.rb
@@ -55,6 +55,13 @@ module API
# https://docs.microsoft.com/en-us/nuget/api/service-index
desc 'The NuGet Service Index' do
detail 'This feature was introduced in GitLab 12.6'
+ success code: 200, model: ::API::Entities::Nuget::ServiceIndex
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
end
get 'index', format: :json, urgency: :default do
authorize_read_package!(project_or_group)
@@ -67,7 +74,7 @@ module API
# https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource
params do
- requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX
+ requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX, documentation: { example: 'MyNuGetPkg' }
end
namespace '/metadata/*package_name' do
after_validation do
@@ -76,6 +83,13 @@ module API
desc 'The NuGet Metadata Service - Package name level' do
detail 'This feature was introduced in GitLab 12.8'
+ success code: 200, model: ::API::Entities::Nuget::PackagesMetadata
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
end
get 'index', format: :json, urgency: :low do
present ::Packages::Nuget::PackagesMetadataPresenter.new(find_packages(params[:package_name])),
@@ -84,9 +98,16 @@ module API
desc 'The NuGet Metadata Service - Package name and version level' do
detail 'This feature was introduced in GitLab 12.8'
+ success code: 200, model: ::API::Entities::Nuget::PackageMetadata
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
end
params do
- requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
+ requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX, documentation: { example: '1.0.0' }
end
get '*package_version', format: :json, urgency: :low do
present ::Packages::Nuget::PackageMetadataPresenter.new(find_package(params[:package_name], params[:package_version])),
@@ -96,9 +117,9 @@ module API
# https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource
params do
- optional :q, type: String, desc: 'The search term'
- optional :skip, type: Integer, desc: 'The number of results to skip', default: 0, regexp: NON_NEGATIVE_INTEGER_REGEX
- optional :take, type: Integer, desc: 'The number of results to return', default: Kaminari.config.default_per_page, regexp: POSITIVE_INTEGER_REGEX
+ optional :q, type: String, desc: 'The search term', documentation: { example: 'MyNuGet' }
+ optional :skip, type: Integer, desc: 'The number of results to skip', default: 0, regexp: NON_NEGATIVE_INTEGER_REGEX, documentation: { example: 1 }
+ optional :take, type: Integer, desc: 'The number of results to return', default: Kaminari.config.default_per_page, regexp: POSITIVE_INTEGER_REGEX, documentation: { example: 1 }
optional :prerelease, type: ::Grape::API::Boolean, desc: 'Include prerelease versions', default: true
end
namespace '/query' do
@@ -108,6 +129,13 @@ module API
desc 'The NuGet Search Service' do
detail 'This feature was introduced in GitLab 12.8'
+ success code: 200, model: ::API::Entities::Nuget::SearchResults
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
end
get format: :json, urgency: :low do
search_options = {
diff --git a/lib/api/container_registry_event.rb b/lib/api/container_registry_event.rb
index 9acf2fca1b3..9e59401ddf6 100644
--- a/lib/api/container_registry_event.rb
+++ b/lib/api/container_registry_event.rb
@@ -26,15 +26,21 @@ module API
desc 'Receives notifications from the container registry when an operation occurs' do
detail 'This feature was introduced in GitLab 12.10'
consumes [:json, DOCKER_DISTRIBUTION_EVENTS_V1_JSON]
+ success code: 200, message: 'Success'
+ failure [
+ { code: 401, message: 'Invalid Token' }
+ ]
+ tags %w[container_registry_event]
end
params do
requires :events, type: Array, desc: 'Event notifications' do
requires :action, type: String, desc: 'The action to perform, `push`, `delete`',
values: %w[push delete].freeze
optional :target, type: Hash, desc: 'The target of the action' do
- optional :tag, type: String, desc: 'The target tag'
- optional :repository, type: String, desc: 'The target repository'
- optional :digest, type: String, desc: 'Unique identifier for target image manifest'
+ optional :tag, type: String, desc: 'The target tag', documentation: { example: 'latest' }
+ optional :repository, type: String, desc: 'The target repository', documentation: { example: 'group/p1' }
+ optional :digest, type: String, desc: 'Unique identifier for target image manifest',
+ documentation: { example: 'imagedigest' }
end
end
end
diff --git a/lib/api/container_repositories.rb b/lib/api/container_repositories.rb
index f2dd1fa21fd..b6b5fe10332 100644
--- a/lib/api/container_repositories.rb
+++ b/lib/api/container_repositories.rb
@@ -14,12 +14,17 @@ module API
namespace 'registry' do
params do
- requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
+ requires :id, types: [String, Integer], desc: 'The ID of the repository'
end
resource :repositories, requirements: { id: /[0-9]*/ } do
desc 'Get a container repository' do
detail 'This feature was introduced in GitLab 13.6.'
success Entities::ContainerRegistry::Repository
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Repository Not Found' }
+ ]
+ tags %w[container_registry]
end
params do
optional :tags, type: Boolean, default: false, desc: 'Determines if tags should be included'
diff --git a/lib/api/debian_group_packages.rb b/lib/api/debian_group_packages.rb
index 0962d749558..105a0955912 100644
--- a/lib/api/debian_group_packages.rb
+++ b/lib/api/debian_group_packages.rb
@@ -30,7 +30,7 @@ module API
end
params do
- requires :id, type: String, desc: 'The ID of a group'
+ requires :id, types: [String, Integer], desc: 'The group ID or full group path.'
end
namespace ':id/-/packages/debian' do
@@ -42,8 +42,15 @@ module API
use :shared_package_file_params
end
- desc 'The package' do
+ desc 'Download Debian package' do
detail 'This feature was introduced in GitLab 14.2'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
diff --git a/lib/api/debian_project_packages.rb b/lib/api/debian_project_packages.rb
index df3b6e774ae..23a542e4183 100644
--- a/lib/api/debian_project_packages.rb
+++ b/lib/api/debian_project_packages.rb
@@ -45,8 +45,15 @@ module API
use :shared_package_file_params
end
- desc 'The package' do
+ desc 'Download Debian package' do
detail 'This feature was introduced in GitLab 14.2'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -55,13 +62,25 @@ module API
end
params do
- requires :file_name, type: String, desc: 'The file name'
+ requires :file_name, type: String, desc: 'The file name', documentation: { example: 'example_1.0.0~alpha2_amd64.deb' }
end
namespace ':file_name', requirements: FILE_NAME_REQUIREMENTS do
format :txt
content_type :json, Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
+ desc 'Upload Debian package' do
+ detail 'This feature was introduced in GitLab 14.0'
+ success code: 201
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
+ end
+
# PUT {projects|groups}/:id/packages/debian/:file_name
params do
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)', documentation: { type: 'file' }
@@ -91,6 +110,16 @@ module API
end
# PUT {projects|groups}/:id/packages/debian/:file_name/authorize
+ desc 'Authorize Debian package upload' do
+ detail 'This feature was introduced in GitLab 13.5'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
+ end
route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth, authenticate_non_public: true
put 'authorize' do
authorize_workhorse!(
diff --git a/lib/api/deployments.rb b/lib/api/deployments.rb
index 141f089b5e1..3a0eea677b8 100644
--- a/lib/api/deployments.rb
+++ b/lib/api/deployments.rb
@@ -50,6 +50,14 @@ module API
type: DateTime,
desc: 'Return deployments updated before the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`)'
+ optional :finished_after,
+ type: DateTime,
+ desc: 'Return deployments finished after the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`)'
+
+ optional :finished_before,
+ type: DateTime,
+ desc: 'Return deployments finished before the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`)'
+
optional :environment,
type: String,
desc: 'The name of the environment to filter deployments by'
@@ -64,7 +72,7 @@ module API
authorize! :read_deployment, user_project
deployments =
- DeploymentsFinder.new(params.merge(project: user_project))
+ DeploymentsFinder.new(declared_params(include_missing: false).merge(project: user_project))
.execute.with_api_entity_associations
present paginate(deployments), with: Entities::Deployment
diff --git a/lib/api/entities/appearance.rb b/lib/api/entities/appearance.rb
index a09faf55f48..94a39568393 100644
--- a/lib/api/entities/appearance.rb
+++ b/lib/api/entities/appearance.rb
@@ -4,6 +4,7 @@ module API
module Entities
class Appearance < Grape::Entity
expose :title
+ expose :short_title
expose :description
expose :logo do |appearance, options|
diff --git a/lib/api/entities/basic_success.rb b/lib/api/entities/basic_success.rb
new file mode 100644
index 00000000000..37388f56221
--- /dev/null
+++ b/lib/api/entities/basic_success.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ # Simple representation for endpoints that returns a trivial success response.
+ class BasicSuccess < Grape::Entity
+ expose :success, documentation: { type: 'boolean' } do
+ true
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/batched_background_migration.rb b/lib/api/entities/batched_background_migration.rb
index eba17ff98f4..08e4681e0aa 100644
--- a/lib/api/entities/batched_background_migration.rb
+++ b/lib/api/entities/batched_background_migration.rb
@@ -3,12 +3,12 @@
module API
module Entities
class BatchedBackgroundMigration < Grape::Entity
- expose :id
- expose :job_class_name
- expose :table_name
- expose :status, &:status_name
- expose :progress
- expose :created_at
+ expose :id, documentation: { type: :string, example: "1234" }
+ expose :job_class_name, documentation: { type: :string, example: "CopyColumnUsingBackgroundMigrationJob" }
+ expose :table_name, documentation: { type: :string, example: "events" }
+ expose :status_name, as: :status, override: true, documentation: { type: :string, example: "active" }
+ expose :progress, documentation: { type: :float, example: 50 }
+ expose :created_at, documentation: { type: :dateTime, example: "2022-11-28T16:26:39+02:00" }
end
end
end
diff --git a/lib/api/entities/ci/job_request/hook.rb b/lib/api/entities/ci/job_request/hook.rb
new file mode 100644
index 00000000000..2d155bb1c45
--- /dev/null
+++ b/lib/api/entities/ci/job_request/hook.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ module JobRequest
+ class Hook < Grape::Entity
+ expose :name, :script
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/job_request/response.rb b/lib/api/entities/ci/job_request/response.rb
index 9de415ebacb..cfdbeed79b6 100644
--- a/lib/api/entities/ci/job_request/response.rb
+++ b/lib/api/entities/ci/job_request/response.rb
@@ -23,6 +23,9 @@ module API
expose :runner_variables, as: :variables
expose :steps, using: Entities::Ci::JobRequest::Step
+ expose :runtime_hooks, as: :hooks,
+ using: Entities::Ci::JobRequest::Hook,
+ if: ->(job) { ::Feature.enabled?(:ci_hooks_pre_get_sources_script, job.project) }
expose :image, using: Entities::Ci::JobRequest::Image
expose :services, using: Entities::Ci::JobRequest::Service
expose :artifacts, using: Entities::Ci::JobRequest::Artifacts
diff --git a/lib/api/entities/ci/runner_details.rb b/lib/api/entities/ci/runner_details.rb
index 9b1decca274..8aa134dc669 100644
--- a/lib/api/entities/ci/runner_details.rb
+++ b/lib/api/entities/ci/runner_details.rb
@@ -14,7 +14,7 @@ module API
# rubocop: disable CodeReuse/ActiveRecord
expose :projects, with: Entities::BasicProjectDetails do |runner, options|
- if options[:current_user].admin? # rubocop: disable Cop/UserAdmin
+ if options[:current_user].can_read_all_resources?
runner.projects
else
options[:current_user].authorized_projects.where(id: runner.runner_projects.pluck(:project_id))
@@ -23,7 +23,7 @@ module API
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
expose :groups, with: Entities::BasicGroupDetails do |runner, options|
- if options[:current_user].admin? # rubocop: disable Cop/UserAdmin
+ if options[:current_user].can_read_all_resources?
runner.groups
else
options[:current_user].authorized_groups.where(id: runner.runner_namespaces.pluck(:namespace_id))
diff --git a/lib/api/entities/ci/secure_file.rb b/lib/api/entities/ci/secure_file.rb
index d957e4488fd..a234ada6f82 100644
--- a/lib/api/entities/ci/secure_file.rb
+++ b/lib/api/entities/ci/secure_file.rb
@@ -4,13 +4,14 @@ module API
module Entities
module Ci
class SecureFile < Grape::Entity
- expose :id
- expose :name
- expose :checksum
- expose :checksum_algorithm
- expose :created_at
- expose :expires_at
- expose :metadata
+ expose :id, documentation: { type: 'integer', example: 123 }
+ expose :name, documentation: { type: 'string', example: 'upload-keystore.jks' }
+ expose :checksum,
+documentation: { type: 'string', example: '16630b189ab34b2e3504f4758e1054d2e478deda510b2b08cc0ef38d12e80aac' }
+ expose :checksum_algorithm, documentation: { type: 'string', example: 'sha256' }
+ expose :created_at, documentation: { type: 'dateTime', example: '2022-02-22T22:22:22.222Z' }
+ expose :expires_at, documentation: { type: 'dateTime', example: '2022-09-21T14:56:00.000Z' }
+ expose :metadata, documentation: { type: 'Hash', example: { "id" => "75949910542696343243264405377658443914" } }
end
end
end
diff --git a/lib/api/entities/commit_signature.rb b/lib/api/entities/commit_signature.rb
index 9430dd5e2a2..9c30c3c59ea 100644
--- a/lib/api/entities/commit_signature.rb
+++ b/lib/api/entities/commit_signature.rb
@@ -10,6 +10,8 @@ module API
::API::Entities::GpgCommitSignature.represent commit_signature(commit), options
elsif commit.signature.is_a?(::CommitSignatures::X509CommitSignature)
::API::Entities::X509Signature.represent commit.signature, options
+ elsif commit.signature.is_a?(::CommitSignatures::SshSignature)
+ ::API::Entities::SshSignature.represent(commit.signature, options)
end
end
diff --git a/lib/api/entities/conan_package/conan_package_manifest.rb b/lib/api/entities/conan_package/conan_package_manifest.rb
index e6acfe1912f..70ab498c56a 100644
--- a/lib/api/entities/conan_package/conan_package_manifest.rb
+++ b/lib/api/entities/conan_package/conan_package_manifest.rb
@@ -4,7 +4,7 @@ module API
module Entities
module ConanPackage
class ConanPackageManifest < Grape::Entity
- expose :package_urls, merge: true
+ expose :package_urls, merge: true, documentation: { type: 'object', example: '{ "conan_package.tgz": "https://gitlab.example.com/api/v4/packages/conan/v1/files/my-package/1.0/my-group+my-project/stable/packages/103f6067a947f366ef91fc1b7da351c588d1827f/0/conan_package.tgz"' }
end
end
end
diff --git a/lib/api/entities/conan_package/conan_package_snapshot.rb b/lib/api/entities/conan_package/conan_package_snapshot.rb
index d7fdda09b5a..5cf623c53df 100644
--- a/lib/api/entities/conan_package/conan_package_snapshot.rb
+++ b/lib/api/entities/conan_package/conan_package_snapshot.rb
@@ -4,7 +4,11 @@ module API
module Entities
module ConanPackage
class ConanPackageSnapshot < Grape::Entity
- expose :package_snapshot, merge: true
+ expose :package_snapshot, merge: true,
+ documentation: {
+ type: 'object',
+ example: '{ "conan_package.tgz": "749b29bdf72587081ca03ec033ee59dc" }'
+ }
end
end
end
diff --git a/lib/api/entities/conan_package/conan_recipe_manifest.rb b/lib/api/entities/conan_package/conan_recipe_manifest.rb
index ecaa142cef9..0b29f0c5058 100644
--- a/lib/api/entities/conan_package/conan_recipe_manifest.rb
+++ b/lib/api/entities/conan_package/conan_recipe_manifest.rb
@@ -4,7 +4,7 @@ module API
module Entities
module ConanPackage
class ConanRecipeManifest < Grape::Entity
- expose :recipe_urls, merge: true
+ expose :recipe_urls, merge: true, documentation: { type: 'object', example: '{ "conan_sources.tgz": "https://gitlab.example.com/api/v4/packages/conan/v1/files/my-package/1.0/my-group+my-project/stable/0/export/conan_sources.tgz" }' }
end
end
end
diff --git a/lib/api/entities/conan_package/conan_recipe_snapshot.rb b/lib/api/entities/conan_package/conan_recipe_snapshot.rb
index 09a60d23727..f9806e90816 100644
--- a/lib/api/entities/conan_package/conan_recipe_snapshot.rb
+++ b/lib/api/entities/conan_package/conan_recipe_snapshot.rb
@@ -4,7 +4,11 @@ module API
module Entities
module ConanPackage
class ConanRecipeSnapshot < Grape::Entity
- expose :recipe_snapshot, merge: true
+ expose :recipe_snapshot, merge: true,
+ documentation: {
+ type: 'object',
+ example: '{ "conan_sources.tgz": "eadf19b33f4c3c7e113faabf26e76277" }'
+ }
end
end
end
diff --git a/lib/api/entities/conan_package/conan_upload_urls.rb b/lib/api/entities/conan_package/conan_upload_urls.rb
index c14963c87f5..fd5ea80068c 100644
--- a/lib/api/entities/conan_package/conan_upload_urls.rb
+++ b/lib/api/entities/conan_package/conan_upload_urls.rb
@@ -4,7 +4,7 @@ module API
module Entities
module ConanPackage
class ConanUploadUrls < Grape::Entity
- expose :upload_urls, merge: true
+ expose :upload_urls, merge: true, documentation: { type: 'object', example: '{ "conan_package.tgz": "https://gitlab.example.com/api/v4/packages/conan/v1/files/my-package/1.0/my-group+my-project/stable/0/package/103f6067a947f366ef91fc1b7da351c588d1827f/0/conan_package.tgz" }' }
end
end
end
diff --git a/lib/api/entities/container_registry.rb b/lib/api/entities/container_registry.rb
index d12c8142e69..cadd45cb0eb 100644
--- a/lib/api/entities/container_registry.rb
+++ b/lib/api/entities/container_registry.rb
@@ -4,9 +4,9 @@ module API
module Entities
module ContainerRegistry
class Tag < Grape::Entity
- expose :name
- expose :path
- expose :location
+ expose :name, documentation: { type: 'string', example: 'latest' }
+ expose :path, documentation: { type: 'string', example: 'namespace1/project1/test_image_1:latest' }
+ expose :location, documentation: { type: 'string', example: 'registry.dev/namespace1/project1/test_image_1:latest' }
end
class Repository < Grape::Entity
@@ -19,10 +19,11 @@ module API
expose :location, documentation: { type: 'string', example: 'gitlab.example.com/group/project/releases' }
expose :created_at, documentation: { type: 'dateTime', example: '2019-01-10T13:39:08.229Z' }
expose :expiration_policy_started_at, as: :cleanup_policy_started_at, documentation: { type: 'dateTime', example: '2020-08-17T03:12:35.489Z' }
- expose :tags_count, if: -> (_, options) { options[:tags_count] }
+ expose :tags_count, if: -> (_, options) { options[:tags_count] }, documentation: { type: 'integer', example: 3 }
expose :tags, using: Tag, if: -> (_, options) { options[:tags] }
- expose :delete_api_path, if: ->(object, options) { Ability.allowed?(options[:user], :admin_container_image, object) }
- expose :size, if: -> (_, options) { options[:size] }
+ expose :delete_api_path, if: ->(object, options) { Ability.allowed?(options[:user], :admin_container_image, object) },
+ documentation: { type: 'string', example: 'delete/api/path' }
+ expose :size, if: -> (_, options) { options[:size] }, documentation: { type: 'integer', example: 12345 }
private
@@ -32,11 +33,11 @@ module API
end
class TagDetails < Tag
- expose :revision
- expose :short_revision
- expose :digest
- expose :created_at
- expose :total_size
+ expose :revision, documentation: { type: 'string', example: 'tagrevision' }
+ expose :short_revision, documentation: { type: 'string', example: 'shortrevison' }
+ expose :digest, documentation: { type: 'string', example: 'shadigest' }
+ expose :created_at, documentation: { type: 'dateTime', example: '2022-01-10T13:39:08.229Z' }
+ expose :total_size, documentation: { type: 'integer', example: 3 }
end
end
end
diff --git a/lib/api/entities/event.rb b/lib/api/entities/event.rb
index f750d728e03..e81e89a8393 100644
--- a/lib/api/entities/event.rb
+++ b/lib/api/entities/event.rb
@@ -3,11 +3,15 @@
module API
module Entities
class Event < Grape::Entity
- expose :id
- expose :project_id, :action_name
- expose :target_id, :target_iid, :target_type, :author_id
- expose :target_title
- expose :created_at
+ expose :id, documentation: { type: 'integer', example: 1 }
+ expose :project_id, documentation: { type: 'integer', example: 2 }
+ expose :action_name, documentation: { type: 'string', example: 'closed' }
+ expose :target_id, documentation: { type: 'integer', example: 160 }
+ expose :target_iid, documentation: { type: 'integer', example: 157 }
+ expose :target_type, documentation: { type: 'string', example: 'Issue' }
+ expose :author_id, documentation: { type: 'integer', example: 25 }
+ expose :target_title, documentation: { type: 'string', example: 'Public project search field' }
+ expose :created_at, documentation: { type: 'string', example: '2017-02-09T10:43:19.667Z' }
expose :note, using: Entities::Note, if: ->(event, options) { event.note? }
expose :author, using: Entities::UserBasic, if: ->(event, options) { event.author }
expose :wiki_page, using: Entities::WikiPageBasic, if: ->(event, _options) { event.wiki_page? }
@@ -17,7 +21,7 @@ module API
using: Entities::PushEventPayload,
if: -> (event, _) { event.push_action? }
- expose :author_username do |event, options|
+ expose :author_username, documentation: { type: 'string', example: 'root' } do |event, options|
event.author&.username
end
end
diff --git a/lib/api/entities/issuable_references.rb b/lib/api/entities/issuable_references.rb
index 1bf078847cf..7b966b85800 100644
--- a/lib/api/entities/issuable_references.rb
+++ b/lib/api/entities/issuable_references.rb
@@ -3,15 +3,15 @@
module API
module Entities
class IssuableReferences < Grape::Entity
- expose :short do |issuable|
+ expose :short, documentation: { type: "string", example: "&6" } do |issuable|
issuable.to_reference
end
- expose :relative do |issuable, options|
+ expose :relative, documentation: { type: "string", example: "&6" } do |issuable, options|
issuable.to_reference(options[:group] || options[:project])
end
- expose :full do |issuable|
+ expose :full, documentation: { type: "string", example: "test&6" } do |issuable|
issuable.to_reference(full: true)
end
end
diff --git a/lib/api/entities/issuable_time_stats.rb b/lib/api/entities/issuable_time_stats.rb
index f93b4651b1f..717d2282441 100644
--- a/lib/api/entities/issuable_time_stats.rb
+++ b/lib/api/entities/issuable_time_stats.rb
@@ -7,12 +7,12 @@ module API
Gitlab::TimeTrackingFormatter.output(time_spent)
end
- expose :time_estimate
- expose :total_time_spent
- expose :human_time_estimate
+ expose :time_estimate, documentation: { type: 'integer', example: 12600 }
+ expose :total_time_spent, documentation: { type: 'integer', example: 3600 }
+ expose :human_time_estimate, documentation: { type: 'string', example: '3h 30m' }
with_options(format_with: :time_tracking_formatter) do
- expose :total_time_spent, as: :human_total_time_spent
+ expose :total_time_spent, as: :human_total_time_spent, documentation: { type: 'string', example: '1h' }
end
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/lib/api/entities/metric_image.rb b/lib/api/entities/metric_image.rb
index fd5e3a62e40..3e4566832c9 100644
--- a/lib/api/entities/metric_image.rb
+++ b/lib/api/entities/metric_image.rb
@@ -3,7 +3,13 @@
module API
module Entities
class MetricImage < Grape::Entity
- expose :id, :created_at, :filename, :file_path, :url, :url_text
+ expose :id, documentation: { type: 'integer', example: 23 }
+ expose :created_at, documentation: { type: 'dateTime', example: '2020-11-13T00:06:18.084Z' }
+ expose :filename, documentation: { type: 'string', example: 'file.png' }
+ expose :file_path, documentation: { type: 'string',
+ example: '/uploads/-/system/alert_metric_image/file/23/file.png' }
+ expose :url, documentation: { type: 'string', example: 'https://example.com/metric' }
+ expose :url_text, documentation: { type: 'string', example: 'An example metric' }
end
end
end
diff --git a/lib/api/entities/milestone.rb b/lib/api/entities/milestone.rb
index b191210a234..ea73ade46cd 100644
--- a/lib/api/entities/milestone.rb
+++ b/lib/api/entities/milestone.rb
@@ -10,7 +10,7 @@ module API
expose :state, :created_at, :updated_at
expose :due_date
expose :start_date
- expose :expired?, as: :expired
+ expose :expired
expose :web_url do |milestone, _options|
Gitlab::UrlBuilder.build(milestone)
diff --git a/lib/api/entities/ml/mlflow/experiment.rb b/lib/api/entities/ml/mlflow/experiment.rb
index 54e0fe63985..51650c36d98 100644
--- a/lib/api/entities/ml/mlflow/experiment.rb
+++ b/lib/api/entities/ml/mlflow/experiment.rb
@@ -9,6 +9,7 @@ module API
expose :name
expose(:lifecycle_stage) { |experiment| experiment.deleted_on? ? 'deleted' : 'active' }
expose(:artifact_location) { |experiment| 'not_implemented' }
+ expose :metadata, as: :tags, using: KeyValue
end
end
end
diff --git a/lib/api/entities/ml/mlflow/key_value.rb b/lib/api/entities/ml/mlflow/key_value.rb
new file mode 100644
index 00000000000..cf2c32f6f44
--- /dev/null
+++ b/lib/api/entities/ml/mlflow/key_value.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ml
+ module Mlflow
+ class KeyValue < Grape::Entity
+ expose :name, as: :key
+ expose :value
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ml/mlflow/run.rb b/lib/api/entities/ml/mlflow/run.rb
index 8b16c67611f..01d85e8862b 100644
--- a/lib/api/entities/ml/mlflow/run.rb
+++ b/lib/api/entities/ml/mlflow/run.rb
@@ -9,7 +9,8 @@ module API
expose :itself, using: RunInfo, as: :info
expose :data do
expose :metrics, using: Metric
- expose :params, using: RunParam
+ expose :params, using: KeyValue
+ expose :metadata, as: :tags, using: KeyValue
end
end
end
diff --git a/lib/api/entities/ml/mlflow/run_param.rb b/lib/api/entities/ml/mlflow/run_param.rb
deleted file mode 100644
index 75fee738f8b..00000000000
--- a/lib/api/entities/ml/mlflow/run_param.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- module Ml
- module Mlflow
- class RunParam < Grape::Entity
- expose :name, as: :key
- expose :value
- end
- end
- end
- end
-end
diff --git a/lib/api/entities/namespace.rb b/lib/api/entities/namespace.rb
index f11303d41a6..15bc7d158c4 100644
--- a/lib/api/entities/namespace.rb
+++ b/lib/api/entities/namespace.rb
@@ -3,7 +3,7 @@
module API
module Entities
class Namespace < Entities::NamespaceBasic
- expose :members_count_with_descendants, if: -> (namespace, opts) { expose_members_count_with_descendants?(namespace, opts) } do |namespace, _|
+ expose :members_count_with_descendants, documentation: { type: 'integer', example: 5 }, if: -> (namespace, opts) { expose_members_count_with_descendants?(namespace, opts) } do |namespace, _|
namespace.users_with_descendants.count
end
diff --git a/lib/api/entities/namespace_basic.rb b/lib/api/entities/namespace_basic.rb
index 2b9dd0b5f4d..4264326cdc2 100644
--- a/lib/api/entities/namespace_basic.rb
+++ b/lib/api/entities/namespace_basic.rb
@@ -3,9 +3,15 @@
module API
module Entities
class NamespaceBasic < Grape::Entity
- expose :id, :name, :path, :kind, :full_path, :parent_id, :avatar_url
+ expose :id, documentation: { type: 'integer', example: 2 }
+ expose :name, documentation: { type: 'string', example: 'project' }
+ expose :path, documentation: { type: 'string', example: 'my_project' }
+ expose :kind, documentation: { type: 'string', example: 'project' }
+ expose :full_path, documentation: { type: 'string', example: 'group/my_project' }
+ expose :parent_id, documentation: { type: 'integer', example: 1 }
+ expose :avatar_url, documentation: { type: 'string', example: 'https://example.com/avatar/12345' }
- expose :web_url do |namespace|
+ expose :web_url, documentation: { type: 'string', example: 'https://example.com/group/my_project' } do |namespace|
if namespace.user_namespace?
Gitlab::Routing.url_helpers.user_url(namespace.owner)
else
diff --git a/lib/api/entities/namespace_existence.rb b/lib/api/entities/namespace_existence.rb
index d93078ecdac..ac9511930ab 100644
--- a/lib/api/entities/namespace_existence.rb
+++ b/lib/api/entities/namespace_existence.rb
@@ -3,7 +3,8 @@
module API
module Entities
class NamespaceExistence < Grape::Entity
- expose :exists, :suggests
+ expose :exists, documentation: { type: 'boolean' }
+ expose :suggests, documentation: { type: 'string', is_array: true, example: 'my-group1' }
end
end
end
diff --git a/lib/api/entities/npm_package.rb b/lib/api/entities/npm_package.rb
index b094f3acdb6..ad864f86fd5 100644
--- a/lib/api/entities/npm_package.rb
+++ b/lib/api/entities/npm_package.rb
@@ -3,9 +3,19 @@
module API
module Entities
class NpmPackage < Grape::Entity
- expose :name
- expose :versions
- expose :dist_tags, as: 'dist-tags'
+ expose :name, documentation: { type: 'string', example: 'my_package' }
+ expose :versions,
+ documentation: {
+ type: 'object',
+ example: '{
+ "1.0.0": {
+ "name": "my_package",
+ "version": "1.0.0",
+ "dist": { "shasum": "12345", "tarball": "https://..." }
+ }
+ }'
+ }
+ expose :dist_tags, as: 'dist-tags', documentation: { type: 'object', example: '{ "latest":"1.0.1" }' }
end
end
end
diff --git a/lib/api/entities/npm_package_tag.rb b/lib/api/entities/npm_package_tag.rb
index 7f458fa037f..0a20d18e917 100644
--- a/lib/api/entities/npm_package_tag.rb
+++ b/lib/api/entities/npm_package_tag.rb
@@ -3,7 +3,7 @@
module API
module Entities
class NpmPackageTag < Grape::Entity
- expose :dist_tags, merge: true
+ expose :dist_tags, merge: true, documentation: { type: 'object', example: '{ "latest":"1.0.1" }' }
end
end
end
diff --git a/lib/api/entities/nuget/dependency.rb b/lib/api/entities/nuget/dependency.rb
index b61c37f5882..adb11376cfa 100644
--- a/lib/api/entities/nuget/dependency.rb
+++ b/lib/api/entities/nuget/dependency.rb
@@ -4,10 +4,10 @@ module API
module Entities
module Nuget
class Dependency < Grape::Entity
- expose :id, as: :@id
- expose :type, as: :@type
- expose :name, as: :id
- expose :range
+ expose :id, as: :@id, documentation: { type: 'string', example: 'http://gitlab.com/Sandbox.App/1.0.0.json#dependency' }
+ expose :type, as: :@type, documentation: { type: 'string', example: 'PackageDependency' }
+ expose :name, as: :id, documentation: { type: 'string', example: 'Dependency' }
+ expose :range, documentation: { type: 'string', example: '2.0.0' }
end
end
end
diff --git a/lib/api/entities/nuget/dependency_group.rb b/lib/api/entities/nuget/dependency_group.rb
index dcab9359fcf..8d943050cd8 100644
--- a/lib/api/entities/nuget/dependency_group.rb
+++ b/lib/api/entities/nuget/dependency_group.rb
@@ -4,10 +4,12 @@ module API
module Entities
module Nuget
class DependencyGroup < Grape::Entity
- expose :id, as: :@id
- expose :type, as: :@type
- expose :target_framework, as: :targetFramework, expose_nil: false
- expose :dependencies, using: ::API::Entities::Nuget::Dependency
+ expose :id, as: :@id, documentation: { type: 'string', example: 'http://gitlab.com/Sandbox.App/1.0.0.json#dependencygroup' }
+ expose :type, as: :@type, documentation: { type: 'string', example: 'PackageDependencyGroup' }
+ expose :target_framework, as: :targetFramework, expose_nil: false,
+ documentation: { type: 'string', example: 'fwk test' }
+ expose :dependencies, using: ::API::Entities::Nuget::Dependency,
+ documentation: { is_array: true, type: 'API::Entities::Nuget::Dependency' }
end
end
end
diff --git a/lib/api/entities/nuget/metadatum.rb b/lib/api/entities/nuget/metadatum.rb
index 87caef41a85..256b916cb64 100644
--- a/lib/api/entities/nuget/metadatum.rb
+++ b/lib/api/entities/nuget/metadatum.rb
@@ -4,9 +4,9 @@ module API
module Entities
module Nuget
class Metadatum < Grape::Entity
- expose :project_url, as: :projectUrl, expose_nil: false
- expose :license_url, as: :licenseUrl, expose_nil: false
- expose :icon_url, as: :iconUrl, expose_nil: false
+ expose :project_url, as: :projectUrl, expose_nil: false, documentation: { type: 'string', example: 'http://sandbox.com/project' }
+ expose :license_url, as: :licenseUrl, expose_nil: false, documentation: { type: 'string', example: 'http://sandbox.com/license' }
+ expose :icon_url, as: :iconUrl, expose_nil: false, documentation: { type: 'string', example: 'http://sandbox.com/icon' }
end
end
end
diff --git a/lib/api/entities/nuget/package_metadata.rb b/lib/api/entities/nuget/package_metadata.rb
index e1c2a1ae161..1c94426bdd6 100644
--- a/lib/api/entities/nuget/package_metadata.rb
+++ b/lib/api/entities/nuget/package_metadata.rb
@@ -4,9 +4,10 @@ module API
module Entities
module Nuget
class PackageMetadata < Grape::Entity
- expose :json_url, as: :@id
- expose :archive_url, as: :packageContent
- expose :catalog_entry, as: :catalogEntry, using: ::API::Entities::Nuget::PackageMetadataCatalogEntry
+ expose :json_url, as: :@id, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/1/packages/nuget/metadata/MyNuGetPkg/1.3.0.17.json' }
+ expose :archive_url, as: :packageContent, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/1/packages/nuget/download/MyNuGetPkg/1.3.0.17/helloworld.1.3.0.17.nupkg' }
+ expose :catalog_entry, as: :catalogEntry, using: ::API::Entities::Nuget::PackageMetadataCatalogEntry,
+ documentation: { type: 'API::Entities::Nuget::PackageMetadataCatalogEntry' }
end
end
end
diff --git a/lib/api/entities/nuget/package_metadata_catalog_entry.rb b/lib/api/entities/nuget/package_metadata_catalog_entry.rb
index 5533f857596..ce328c5a5ca 100644
--- a/lib/api/entities/nuget/package_metadata_catalog_entry.rb
+++ b/lib/api/entities/nuget/package_metadata_catalog_entry.rb
@@ -4,15 +4,17 @@ module API
module Entities
module Nuget
class PackageMetadataCatalogEntry < Grape::Entity
- expose :json_url, as: :@id
- expose :authors
- expose :dependency_groups, as: :dependencyGroups, using: ::API::Entities::Nuget::DependencyGroup
- expose :package_name, as: :id
- expose :package_version, as: :version
- expose :tags
- expose :archive_url, as: :packageContent
- expose :summary
- expose :metadatum, using: ::API::Entities::Nuget::Metadatum, merge: true
+ expose :json_url, as: :@id, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/1/packages/nuget/metadata/MyNuGetPkg/1.3.0.17.json' }
+ expose :authors, documentation: { type: 'string', example: 'Author' }
+ expose :dependency_groups, as: :dependencyGroups, using: ::API::Entities::Nuget::DependencyGroup,
+ documentation: { is_array: true, type: 'API::Entities::Nuget::DependencyGroup' }
+ expose :package_name, as: :id, documentation: { type: 'string', example: 'MyNuGetPkg' }
+ expose :package_version, as: :version, documentation: { type: 'string', example: '1.3.0.17' }
+ expose :tags, documentation: { type: 'string', example: 'tag#1 tag#2' }
+ expose :archive_url, as: :packageContent, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/1/packages/nuget/download/MyNuGetPkg/1.3.0.17/helloworld.1.3.0.17.nupkg' }
+ expose :summary, documentation: { type: 'string', example: 'Summary' }
+ expose :metadatum, using: ::API::Entities::Nuget::Metadatum, merge: true,
+ documentation: { type: 'API::Entities::Nuget::Metadatum' }
end
end
end
diff --git a/lib/api/entities/nuget/packages_metadata.rb b/lib/api/entities/nuget/packages_metadata.rb
index 1cdf2491725..e556df0ce1f 100644
--- a/lib/api/entities/nuget/packages_metadata.rb
+++ b/lib/api/entities/nuget/packages_metadata.rb
@@ -4,8 +4,9 @@ module API
module Entities
module Nuget
class PackagesMetadata < Grape::Entity
- expose :count
- expose :items, using: ::API::Entities::Nuget::PackagesMetadataItem
+ expose :count, documentation: { type: 'integer', example: 1 }
+ expose :items, using: ::API::Entities::Nuget::PackagesMetadataItem,
+ documentation: { is_array: true, type: 'API::Entities::Nuget::PackagesMetadataItem' }
end
end
end
diff --git a/lib/api/entities/nuget/packages_metadata_item.rb b/lib/api/entities/nuget/packages_metadata_item.rb
index 84cc79166f3..420a4c3941c 100644
--- a/lib/api/entities/nuget/packages_metadata_item.rb
+++ b/lib/api/entities/nuget/packages_metadata_item.rb
@@ -4,11 +4,12 @@ module API
module Entities
module Nuget
class PackagesMetadataItem < Grape::Entity
- expose :json_url, as: :@id
- expose :lower_version, as: :lower
- expose :upper_version, as: :upper
- expose :packages_count, as: :count
- expose :packages, as: :items, using: ::API::Entities::Nuget::PackageMetadata
+ expose :json_url, as: :@id, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/1/packages/nuget/metadata/MyNuGetPkg/1.3.0.17.json' }
+ expose :lower_version, as: :lower, documentation: { type: 'string', example: '1.3.0.17' }
+ expose :upper_version, as: :upper, documentation: { type: 'string', example: '1.3.0.17' }
+ expose :packages_count, as: :count, documentation: { type: 'integer', example: 1 }
+ expose :packages, as: :items, using: ::API::Entities::Nuget::PackageMetadata,
+ documentation: { is_array: true, type: 'API::Entities::Nuget::PackageMetadata' }
end
end
end
diff --git a/lib/api/entities/nuget/packages_versions.rb b/lib/api/entities/nuget/packages_versions.rb
index 498c6970d5c..e0330300ca7 100644
--- a/lib/api/entities/nuget/packages_versions.rb
+++ b/lib/api/entities/nuget/packages_versions.rb
@@ -4,7 +4,7 @@ module API
module Entities
module Nuget
class PackagesVersions < Grape::Entity
- expose :versions
+ expose :versions, documentation: { type: 'string', is_array: true, example: '1.3.0.17' }
end
end
end
diff --git a/lib/api/entities/nuget/search_result.rb b/lib/api/entities/nuget/search_result.rb
index 8e028cbad95..bb3698de30b 100644
--- a/lib/api/entities/nuget/search_result.rb
+++ b/lib/api/entities/nuget/search_result.rb
@@ -4,17 +4,18 @@ module API
module Entities
module Nuget
class SearchResult < Grape::Entity
- expose :type, as: :@type
- expose :authors
- expose :name, as: :id
- expose :name, as: :title
- expose :summary
- expose :total_downloads, as: :totalDownloads
- expose :verified
- expose :version
+ expose :type, as: :@type, documentation: { type: 'string', example: 'Package' }
+ expose :authors, documentation: { type: 'string', example: 'Author' }
+ expose :name, as: :id, documentation: { type: 'string', example: 'MyNuGetPkg' }
+ expose :name, as: :title, documentation: { type: 'string', example: 'MyNuGetPkg' }
+ expose :summary, documentation: { type: 'string', example: 'Summary' }
+ expose :total_downloads, as: :totalDownloads, documentation: { type: 'integer', example: 1 }
+ expose :verified, documentation: { type: 'boolean' }
+ expose :version, documentation: { type: 'string', example: '1.3.0.17' }
expose :versions, using: ::API::Entities::Nuget::SearchResultVersion
- expose :tags
- expose :metadatum, using: ::API::Entities::Nuget::Metadatum, merge: true
+ expose :tags, documentation: { type: 'string', example: 'tag#1 tag#2' }
+ expose :metadatum, using: ::API::Entities::Nuget::Metadatum, merge: true,
+ documentation: { is_array: true, type: 'API::Entities::Nuget::Metadatum' }
end
end
end
diff --git a/lib/api/entities/nuget/search_result_version.rb b/lib/api/entities/nuget/search_result_version.rb
index 9032c964c44..fb8d8b75f83 100644
--- a/lib/api/entities/nuget/search_result_version.rb
+++ b/lib/api/entities/nuget/search_result_version.rb
@@ -4,9 +4,9 @@ module API
module Entities
module Nuget
class SearchResultVersion < Grape::Entity
- expose :json_url, as: :@id
- expose :version
- expose :downloads
+ expose :json_url, as: :@id, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/1/packages/nuget/metadata/MyNuGetPkg/1.3.0.17.json' }
+ expose :version, documentation: { type: 'string', example: '1.3.0.17' }
+ expose :downloads, documentation: { type: 'integer', example: 1 }
end
end
end
diff --git a/lib/api/entities/nuget/search_results.rb b/lib/api/entities/nuget/search_results.rb
index 22a77dc7b6c..117904a1aff 100644
--- a/lib/api/entities/nuget/search_results.rb
+++ b/lib/api/entities/nuget/search_results.rb
@@ -4,8 +4,9 @@ module API
module Entities
module Nuget
class SearchResults < Grape::Entity
- expose :total_count, as: :totalHits
- expose :data, using: ::API::Entities::Nuget::SearchResult
+ expose :total_count, as: :totalHits, documentation: { type: 'integer', example: 1 }
+ expose :data, using: ::API::Entities::Nuget::SearchResult,
+ documentation: { is_array: true, type: 'API::Entities::Nuget::SearchResult' }
end
end
end
diff --git a/lib/api/entities/nuget/service_index.rb b/lib/api/entities/nuget/service_index.rb
index e57bd04adb9..4ab6c5ddc8b 100644
--- a/lib/api/entities/nuget/service_index.rb
+++ b/lib/api/entities/nuget/service_index.rb
@@ -4,8 +4,8 @@ module API
module Entities
module Nuget
class ServiceIndex < Grape::Entity
- expose :version
- expose :resources
+ expose :version, documentation: { type: 'string', example: '1.3.0.17' }
+ expose :resources, documentation: { type: 'object', is_array: true, example: '{ "@id": "https://gitlab.com/api/v4/projects/1/packages/nuget/query", "@type": "SearchQueryService", "comment": "Filter and search for packages by keyword."}' }
end
end
end
diff --git a/lib/api/entities/package.rb b/lib/api/entities/package.rb
index c92a4677220..ab6cc0fcb0a 100644
--- a/lib/api/entities/package.rb
+++ b/lib/api/entities/package.rb
@@ -26,7 +26,7 @@ module API
expose :status, documentation: { type: 'string', example: 'default' }
expose :_links do
- expose :web_path do |package|
+ expose :web_path, if: ->(package) { package.default? } do |package|
package_path(package)
end
diff --git a/lib/api/entities/packages/debian/distribution.rb b/lib/api/entities/packages/debian/distribution.rb
index 97a3c479f40..a11f4337f38 100644
--- a/lib/api/entities/packages/debian/distribution.rb
+++ b/lib/api/entities/packages/debian/distribution.rb
@@ -5,17 +5,18 @@ module API
module Packages
module Debian
class Distribution < Grape::Entity
- expose :id
- expose :codename
- expose :suite
- expose :origin
- expose :label
- expose :version
- expose :description
- expose :valid_time_duration_seconds
+ expose :id, documentation: { type: 'integer', example: 1 }
+ expose :codename, documentation: { type: 'string', example: 'unstable' }
+ expose :suite, documentation: { type: 'string', example: 'unstable' }
+ expose :origin, documentation: { type: 'string', example: 'Grep' }
+ expose :label, documentation: { type: 'string', example: 'grep.be' }
+ expose :version, documentation: { type: 'string', example: '12' }
+ expose :description, documentation: { type: 'string', example: 'My description' }
+ expose :valid_time_duration_seconds, documentation: { type: 'integer', example: 604800 }
- expose :component_names, as: :components
- expose :architecture_names, as: :architectures
+ expose :component_names, as: :components, documentation: { is_array: true, type: 'string', example: 'main' }
+ expose :architecture_names, as: :architectures,
+ documentation: { is_array: true, type: 'string', example: 'amd64' }
end
end
end
diff --git a/lib/api/entities/plan_limit.rb b/lib/api/entities/plan_limit.rb
index 34018f03eb1..d69be0077f2 100644
--- a/lib/api/entities/plan_limit.rb
+++ b/lib/api/entities/plan_limit.rb
@@ -17,6 +17,7 @@ module API
expose :maven_max_file_size, documentation: { type: 'integer', example: 3221225472 }
expose :npm_max_file_size, documentation: { type: 'integer', example: 524288000 }
expose :nuget_max_file_size, documentation: { type: 'integer', example: 524288000 }
+ expose :pipeline_hierarchy_size, documentation: { type: 'integer', example: 1000 }
expose :pypi_max_file_size, documentation: { type: 'integer', example: 3221225472 }
expose :terraform_module_max_file_size, documentation: { type: 'integer', example: 1073741824 }
expose :storage_size_limit, documentation: { type: 'integer', example: 15000 }
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index 1c1bafbf161..37be6903d8b 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -81,6 +81,10 @@ module API
expose(:container_registry_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :container_registry) }
expose(:security_and_compliance_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :security_and_compliance) }
expose(:releases_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :releases) }
+ expose(:environments_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :environments) }
+ expose(:feature_flags_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :feature_flags) }
+ expose(:infrastructure_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :infrastructure) }
+ expose(:monitor_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :monitor) }
expose :emails_disabled, documentation: { type: 'boolean' }
expose :shared_runners_enabled, documentation: { type: 'boolean' }
diff --git a/lib/api/entities/project_integration.rb b/lib/api/entities/project_integration.rb
index 29bb60a19e5..f4709ce6dab 100644
--- a/lib/api/entities/project_integration.rb
+++ b/lib/api/entities/project_integration.rb
@@ -5,8 +5,8 @@ module API
class ProjectIntegration < Entities::ProjectIntegrationBasic
# Expose serialized properties
expose :properties, documentation: { type: 'Hash', example: { "token" => "secr3t" } } do |integration, options|
- integration.api_field_names.to_h do |name|
- [name, integration.public_send(name)] # rubocop:disable GitlabSecurity/PublicSend
+ integration.api_field_names.index_with do |name|
+ integration.public_send(name) # rubocop:disable GitlabSecurity/PublicSend
end
end
end
diff --git a/lib/api/entities/push_event_payload.rb b/lib/api/entities/push_event_payload.rb
index 6aad5f10177..2d8f0d9344c 100644
--- a/lib/api/entities/push_event_payload.rb
+++ b/lib/api/entities/push_event_payload.rb
@@ -3,8 +3,14 @@
module API
module Entities
class PushEventPayload < Grape::Entity
- expose :commit_count, :action, :ref_type, :commit_from, :commit_to, :ref,
- :commit_title, :ref_count
+ expose :commit_count, documentation: { type: 'integer', example: 1 }
+ expose :action, documentation: { type: 'string', example: 'pushed' }
+ expose :ref_type, documentation: { type: 'string', example: 'branch' }
+ expose :commit_from, documentation: { type: 'string', example: '50d4420237a9de7be1304607147aec22e4a14af7' }
+ expose :commit_to, documentation: { type: 'string', example: 'c5feabde2d8cd023215af4d2ceeb7a64839fc428' }
+ expose :ref, documentation: { type: 'string', example: 'master' }
+ expose :commit_title, documentation: { type: 'string', example: 'Add simple search to projects in public area' }
+ expose :ref_count, documentation: { type: 'integer', example: 1 }
end
end
end
diff --git a/lib/api/entities/ssh_key.rb b/lib/api/entities/ssh_key.rb
index 3db10bb8ec2..37e8ad7b1f5 100644
--- a/lib/api/entities/ssh_key.rb
+++ b/lib/api/entities/ssh_key.rb
@@ -12,6 +12,7 @@ module API
example: 'ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1256k6Yjz\
GGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCdd\
NaP0L+hM7zhFNzjFvpaMgJw0=' }
+ expose :usage_type, documentation: { type: 'string', example: 'auth' }
end
end
end
diff --git a/lib/api/entities/ssh_signature.rb b/lib/api/entities/ssh_signature.rb
new file mode 100644
index 00000000000..dc3800c87c5
--- /dev/null
+++ b/lib/api/entities/ssh_signature.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class SshSignature < Grape::Entity
+ expose :verification_status, documentation: { type: 'string', example: 'unverified' }
+ expose :key, using: 'API::Entities::SSHKey'
+ end
+ end
+end
diff --git a/lib/api/entities/tag_signature.rb b/lib/api/entities/tag_signature.rb
new file mode 100644
index 00000000000..e75fd04109a
--- /dev/null
+++ b/lib/api/entities/tag_signature.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class TagSignature < Grape::Entity
+ expose :signature_type, documentation: { type: 'string', example: 'PGP' }
+
+ expose :signature, merge: true do |tag|
+ ::API::Entities::X509Signature.represent tag.signature if tag.signature_type == :X509
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/todo.rb b/lib/api/entities/todo.rb
index 5bbbb59f565..02dfdb68af9 100644
--- a/lib/api/entities/todo.rb
+++ b/lib/api/entities/todo.rb
@@ -32,6 +32,7 @@ module API
def todo_target_url(todo)
return design_todo_target_url(todo) if todo.for_design?
+ return todo.access_request_url if todo.member_access_requested?
target_type = todo.target_type.gsub('::', '_').underscore
target_url = "#{todo.resource_parent.class.to_s.underscore}_#{target_type}_url"
diff --git a/lib/api/events.rb b/lib/api/events.rb
index 0a0141484ef..d3e8892f3bc 100644
--- a/lib/api/events.rb
+++ b/lib/api/events.rb
@@ -15,8 +15,15 @@ module API
desc "List currently authenticated user's events" do
detail 'This feature was introduced in GitLab 9.3.'
success Entities::Event
+ is_array true
+ failure [
+ { code: 401, message: 'Unauthorized' }
+ ]
end
params do
+ optional :scope, type: String,
+ desc: 'Include all events across a user’s projects',
+ documentation: { example: 'all' }
use :pagination
use :event_filter_params
use :sort_params
@@ -32,12 +39,17 @@ module API
end
params do
- requires :id, type: String, desc: 'The ID or Username of the user'
+ requires :id, type: String, desc: 'The ID or username of the user'
end
resource :users do
desc 'Get the contribution events of a specified user' do
detail 'This feature was introduced in GitLab 8.13.'
success Entities::Event
+ tags %w[events]
+ is_array true
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
end
params do
use :pagination
diff --git a/lib/api/features.rb b/lib/api/features.rb
index 6b6f5cbfb3f..9142591aebd 100644
--- a/lib/api/features.rb
+++ b/lib/api/features.rb
@@ -9,6 +9,8 @@ module API
feature_category :feature_flags
urgency :low
+ BadValueError = Class.new(StandardError)
+
# TODO: remove these helpers with feature flag set_feature_flag_service
helpers do
def gate_value(params)
@@ -18,6 +20,8 @@ module API
when '0', 'false'
false
else
+ raise BadValueError unless params[:value].match? /^\d+(\.\d+)?$/
+
# https://github.com/jnunemaker/flipper/blob/master/lib/flipper/typecast.rb#L47
if params[:value].to_s.include?('.')
params[:value].to_f
@@ -153,7 +157,9 @@ module API
present Feature.get(params[:name]), # rubocop:disable Gitlab/AvoidFeatureGet
with: Entities::Feature, current_user: current_user
end
- rescue Feature::Target::UnknowTargetError => e
+ rescue BadValueError
+ bad_request!("Value must be boolean or numeric, got #{params[:value]}")
+ rescue Feature::Target::UnknownTargetError => e
bad_request!(e.message)
end
diff --git a/lib/api/files.rb b/lib/api/files.rb
index fa749299b9a..b02f1a8728b 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -172,14 +172,24 @@ module API
desc: 'The url encoded path to the file.', documentation: { example: 'lib%2Fclass%2Erb' }
optional :ref, type: String,
desc: 'The name of branch, tag or commit', allow_blank: false, documentation: { example: 'main' }
+ optional :lfs, type: Boolean,
+ desc: 'Retrieve binary data for a file that is an lfs pointer',
+ default: false
end
get ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS, urgency: :low do
assign_file_vars!
- no_cache_headers
- set_http_headers(blob_data)
+ if params[:lfs] && @blob.stored_externally?
+ lfs_object = LfsObject.find_by_oid(@blob.lfs_oid)
+ not_found! unless lfs_object&.project_allowed_access?(@project)
+
+ present_carrierwave_file!(lfs_object.file)
+ else
+ no_cache_headers
+ set_http_headers(blob_data)
- send_git_blob @repo, @blob
+ send_git_blob @repo, @blob
+ end
end
desc 'Get file metadata from repository'
diff --git a/lib/api/freeze_periods.rb b/lib/api/freeze_periods.rb
index 40f1be83028..abd8f4c0b94 100644
--- a/lib/api/freeze_periods.rb
+++ b/lib/api/freeze_periods.rb
@@ -34,7 +34,7 @@ module API
get ":id/freeze_periods" do
authorize! :read_freeze_period, user_project
- freeze_periods = ::FreezePeriodsFinder.new(user_project, current_user).execute
+ freeze_periods = ::Ci::FreezePeriodsFinder.new(user_project, current_user).execute
present paginate(freeze_periods), with: Entities::FreezePeriod, current_user: current_user
end
diff --git a/lib/api/generic_packages.rb b/lib/api/generic_packages.rb
index 3584f8d025a..da5b0930543 100644
--- a/lib/api/generic_packages.rb
+++ b/lib/api/generic_packages.rb
@@ -28,6 +28,13 @@ module API
namespace ':package_name/*package_version/:file_name', requirements: GENERIC_PACKAGES_REQUIREMENTS do
desc 'Workhorse authorize generic package file' do
detail 'This feature was introduced in GitLab 13.5'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[generic_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
@@ -47,6 +54,17 @@ module API
desc 'Upload package file' do
detail 'This feature was introduced in GitLab 13.5'
+ success [
+ { code: 200 },
+ { code: 201 }
+ ]
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[generic_packages]
end
params do
@@ -88,6 +106,13 @@ module API
desc 'Download package file' do
detail 'This feature was introduced in GitLab 13.5'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[generic_packages]
end
params do
diff --git a/lib/api/group_debian_distributions.rb b/lib/api/group_debian_distributions.rb
index 1f43bb0e2b3..0364e2e7b56 100644
--- a/lib/api/group_debian_distributions.rb
+++ b/lib/api/group_debian_distributions.rb
@@ -3,7 +3,7 @@
module API
class GroupDebianDistributions < ::API::Base
params do
- requires :id, type: String, desc: 'The ID of a group'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the group'
end
before do
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index ca99e30fbf7..23db10dbdbf 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -100,7 +100,7 @@ module API
options = {
with: serializer,
current_user: current_user,
- statistics: params[:statistics] && current_user&.admin?
+ statistics: params[:statistics] && current_user&.can_read_all_resources?
}
groups = groups.with_statistics if options[:statistics]
@@ -186,7 +186,7 @@ module API
end
def check_subscription!(group)
- render_api_error!("This group can't be removed because it is linked to a subscription.", :bad_request) if group.paid?
+ render_api_error!("This group can't be removed because it is linked to a subscription.", :bad_request) if group.prevent_delete?
end
end
@@ -195,6 +195,8 @@ module API
desc 'Get a groups list' do
success Entities::Group
+ is_array true
+ tags %w[groups]
end
params do
use :group_list_params
@@ -207,6 +209,7 @@ module API
desc 'Create a group. Available only for users who can create groups.' do
success Entities::Group
+ tags %w[groups]
end
params do
requires :name, type: String, desc: 'The name of the group'
@@ -240,6 +243,7 @@ module API
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Update a group. Available only for users who can administrate groups.' do
success Entities::Group
+ tags %w[groups]
end
params do
optional :name, type: String, desc: 'The name of the group'
@@ -265,6 +269,7 @@ module API
desc 'Get a single group, with containing projects.' do
success Entities::GroupDetail
+ tags %w[groups]
end
params do
use :with_custom_attributes
@@ -278,7 +283,9 @@ module API
present_group_details(params, group, with_projects: params[:with_projects])
end
- desc 'Remove a group.'
+ desc 'Remove a group.' do
+ tags %w[groups]
+ end
delete ":id", feature_category: :subgroups, urgency: :low do
group = find_group!(params[:id])
authorize! :admin_group, group
@@ -289,6 +296,8 @@ module API
desc 'Get a list of projects in this group.' do
success Entities::Project
+ is_array true
+ tags %w[groups]
end
params do
optional :archived, type: Boolean, desc: 'Limit by archived status'
@@ -329,6 +338,8 @@ module API
desc 'Get a list of shared projects in this group' do
success Entities::Project
+ is_array true
+ tags %w[groups]
end
params do
optional :archived, type: Boolean, desc: 'Limit by archived status'
@@ -357,6 +368,8 @@ module API
desc 'Get a list of subgroups in this group.' do
success Entities::Group
+ is_array true
+ tags %w[groups]
end
params do
use :group_list_params
@@ -369,6 +382,8 @@ module API
desc 'Get a list of descendant groups of this group.' do
success Entities::Group
+ is_array true
+ tags %w[groups]
end
params do
use :group_list_params
@@ -382,6 +397,7 @@ module API
desc 'Transfer a project to the group namespace. Available only for admin.' do
success Entities::GroupDetail
+ tags %w[groups]
end
params do
requires :project_id, type: String, desc: 'The ID or path of the project'
@@ -400,7 +416,11 @@ module API
end
end
- desc 'Get the groups to where the current group can be transferred to'
+ desc 'Get the groups to where the current group can be transferred to' do
+ success Entities::Group
+ is_array true
+ tags %w[groups]
+ end
params do
optional :search, type: String, desc: 'Return list of namespaces matching the search criteria'
use :pagination
@@ -415,7 +435,9 @@ module API
present_groups params, groups, serializer: Entities::PublicGroupDetails
end
- desc 'Transfer a group to a new parent group or promote a subgroup to a root group'
+ desc 'Transfer a group to a new parent group or promote a subgroup to a root group' do
+ tags %w[groups]
+ end
params do
optional :group_id,
type: Integer,
@@ -440,6 +462,7 @@ module API
desc 'Share a group with a group' do
success Entities::GroupDetail
+ tags %w[groups]
end
params do
requires :group_id, type: Integer, desc: 'The ID of the group to share'
diff --git a/lib/api/helm_packages.rb b/lib/api/helm_packages.rb
index fa2537bcfc4..8260d8a88f8 100644
--- a/lib/api/helm_packages.rb
+++ b/lib/api/helm_packages.rb
@@ -32,15 +32,21 @@ module API
end
params do
- requires :id, type: String, desc: 'The ID or full path of a project'
+ requires :id, types: [Integer, String], desc: 'The ID or full path of a project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace ':id/packages/helm', requirements: HELM_REQUIREMENTS do
desc 'Download a chart index' do
detail 'This feature was introduced in GitLab 14.0'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' }
+ ]
+ tags %w[helm_packages]
end
params do
- requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex
+ requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex, documentation: { example: 'stable' }
end
get ":channel/index.yaml" do
@@ -56,10 +62,17 @@ module API
desc 'Download a chart' do
detail 'This feature was introduced in GitLab 14.0'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[helm_packages]
end
params do
- requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex
- requires :file_name, type: String, desc: 'Helm package file name'
+ requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex, documentation: { example: 'stable' }
+ requires :file_name, type: String, desc: 'Helm package file name', documentation: { example: 'mychart' }
end
get ":channel/charts/:file_name.tgz" do
project = authorized_user_project(action: :read_package)
@@ -67,16 +80,23 @@ module API
package_file = Packages::Helm::PackageFilesFinder.new(project, params[:channel], file_name: "#{params[:file_name]}.tgz").most_recent!
- track_package_event('pull_package', :helm, project: project, namespace: project.namespace)
+ track_package_event('pull_package', :helm, project: project, namespace: project.namespace, property: 'i_package_helm_user')
present_package_file!(package_file)
end
desc 'Authorize a chart upload from workhorse' do
detail 'This feature was introduced in GitLab 14.0'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[helm_packages]
end
params do
- requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex
+ requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex, documentation: { example: 'stable' }
end
post "api/:channel/charts/authorize" do
authorize_workhorse!(
@@ -88,9 +108,16 @@ module API
desc 'Upload a chart' do
detail 'This feature was introduced in GitLab 14.0'
+ success code: 201
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[helm_packages]
end
params do
- requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex
+ requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex, documentation: { example: 'stable' }
requires :chart, type: ::API::Validations::Types::WorkhorseFile, desc: 'The chart file to be published (generated by Multipart middleware)', documentation: { type: 'file' }
end
post "api/:channel/charts" do
@@ -110,7 +137,8 @@ module API
package, chart_params.merge(build: current_authenticated_job)
).execute
- track_package_event('push_package', :helm, project: authorized_user_project, namespace: authorized_user_project.namespace)
+ track_package_event('push_package', :helm, project: authorized_user_project, namespace: authorized_user_project.namespace,
+ property: 'i_package_helm_user')
::Packages::Helm::ExtractionWorker.perform_async(params[:channel], chart_package_file.id) # rubocop:disable CodeReuse/Worker
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 75e7612bd5b..0b5a471ea12 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -167,6 +167,10 @@ module API
current_authenticated_job.project == project
end
+ def enforce_jobs_api_rate_limits(project)
+ ::Feature.enabled?(:ci_enforce_rate_limits_jobs_api, project)
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def find_group(id)
if id.to_s =~ INTEGER_ID_REGEX
@@ -301,7 +305,7 @@ module API
def authenticated_as_admin!
authenticate!
- forbidden! unless current_user.admin?
+ forbidden! unless current_user.can_admin_all_resources?
end
def authorize!(action, subject = :global, reason = nil)
@@ -710,7 +714,7 @@ module API
unauthorized! unless initial_current_user
- unless initial_current_user.admin?
+ unless initial_current_user.can_admin_all_resources?
forbidden!('Must be admin to use sudo')
end
diff --git a/lib/api/helpers/award_emoji.rb b/lib/api/helpers/award_emoji.rb
index 3ea35381c97..f8417366ea4 100644
--- a/lib/api/helpers/award_emoji.rb
+++ b/lib/api/helpers/award_emoji.rb
@@ -7,7 +7,7 @@ module API
[
{ type: 'issue', resource: :projects, find_by: :iid, feature_category: :team_planning },
{ type: 'merge_request', resource: :projects, find_by: :iid, feature_category: :code_review },
- { type: 'snippet', resource: :projects, find_by: :id, feature_category: :snippets }
+ { type: 'snippet', resource: :projects, find_by: :id, feature_category: :source_code_management }
]
end
@@ -18,18 +18,16 @@ module API
# rubocop: disable CodeReuse/ActiveRecord
def awardable
@awardable ||=
- begin
- if params.include?(:note_id)
- note_id = params.delete(:note_id)
+ if params.include?(:note_id)
+ note_id = params.delete(:note_id)
- awardable.notes.find(note_id)
- elsif params.include?(:issue_iid)
- user_project.issues.find_by!(iid: params[:issue_iid])
- elsif params.include?(:merge_request_iid)
- user_project.merge_requests.find_by!(iid: params[:merge_request_iid])
- elsif params.include?(:snippet_id)
- user_project.snippets.find(params[:snippet_id])
- end
+ awardable.notes.find(note_id)
+ elsif params.include?(:issue_iid)
+ user_project.issues.find_by!(iid: params[:issue_iid])
+ elsif params.include?(:merge_request_iid)
+ user_project.merge_requests.find_by!(iid: params[:merge_request_iid])
+ elsif params.include?(:snippet_id)
+ user_project.snippets.find(params[:snippet_id])
end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/api/helpers/discussions_helpers.rb b/lib/api/helpers/discussions_helpers.rb
index c94199b17bc..182ada54a12 100644
--- a/lib/api/helpers/discussions_helpers.rb
+++ b/lib/api/helpers/discussions_helpers.rb
@@ -8,7 +8,7 @@ module API
# extend it.
{
Issue => :team_planning,
- Snippet => :snippets,
+ Snippet => :source_code_management,
MergeRequest => :code_review,
Commit => :code_review
}
diff --git a/lib/api/helpers/integrations_helpers.rb b/lib/api/helpers/integrations_helpers.rb
index 99273e81730..543449c0349 100644
--- a/lib/api/helpers/integrations_helpers.rb
+++ b/lib/api/helpers/integrations_helpers.rb
@@ -415,14 +415,6 @@ module API
desc: 'The URL of the external wiki'
}
],
- 'flowdock' => [
- {
- required: true,
- name: :token,
- type: String,
- desc: 'Flowdock token'
- }
- ],
'hangouts-chat' => [
{
required: true,
@@ -893,7 +885,6 @@ module API
::Integrations::EmailsOnPush,
::Integrations::Ewm,
::Integrations::ExternalWiki,
- ::Integrations::Flowdock,
::Integrations::HangoutsChat,
::Integrations::Harbor,
::Integrations::Irker,
diff --git a/lib/api/helpers/merge_requests_helpers.rb b/lib/api/helpers/merge_requests_helpers.rb
index eed9fa30d3c..ee3bb49c97f 100644
--- a/lib/api/helpers/merge_requests_helpers.rb
+++ b/lib/api/helpers/merge_requests_helpers.rb
@@ -11,100 +11,107 @@ module API
params :ee_approval_params do
end
- params :merge_requests_negatable_params do
- optional :author_id, type: Integer, desc: 'Return merge requests which are authored by the user with the given ID'
- optional :author_username, type: String, desc: 'Return merge requests which are authored by the user with the given username'
+ params :merge_requests_negatable_params do |options|
+ optional :author_id, type: Integer,
+ desc: "#{options[:prefix]}Returns merge requests created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`."
+ optional :author_username, type: String,
+ desc: "#{options[:prefix]}Returns merge requests created by the given `username`. Mutually exclusive with `author_id`."
mutually_exclusive :author_id, :author_username
-
- optional :assignee_id,
- types: [Integer, String],
- integer_none_any: true,
- desc: 'Return merge requests which are assigned to the user with the given ID'
- optional :assignee_username,
- type: Array[String],
- check_assignees_count: true,
- coerce_with: Validations::Validators::CheckAssigneesCount.coerce,
- desc: 'Return merge requests which are assigned to the user with the given username'
+ optional :assignee_id, types: [Integer, String],
+ integer_none_any: true,
+ desc: "#{options[:prefix]}Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee."
+ optional :assignee_username, type: Array[String],
+ check_assignees_count: true,
+ coerce_with: Validations::Validators::CheckAssigneesCount.coerce,
+ desc: "#{options[:prefix]}Returns merge requests created by the given `username`. Mutually exclusive with `author_id`.",
+ documentation: { is_array: true }
mutually_exclusive :assignee_id, :assignee_username
- optional :reviewer_username,
- type: String,
- desc: 'Return merge requests which have the user as a reviewer with the given username'
-
- optional :labels,
- type: Array[String],
- coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
- desc: 'Comma-separated list of label names'
- optional :milestone, type: String, desc: 'Return merge requests for a specific milestone'
- optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji'
+ optional :reviewer_username, type: String,
+ desc: "#{options[:prefix]}Returns merge requests which have the user as a reviewer with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. Introduced in GitLab 13.8."
+ optional :labels, type: Array[String],
+ coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
+ desc: "#{options[:prefix]}Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive.",
+ documentation: { is_array: true }
+ optional :milestone, type: String,
+ desc: "#{options[:prefix]}Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone."
+ optional :my_reaction_emoji, type: String,
+ desc: "#{options[:prefix]}Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction."
end
params :merge_requests_base_params do
- use :merge_requests_negatable_params
- optional :reviewer_id,
- types: [Integer, String],
- integer_none_any: true,
- desc: 'Return merge requests which have the user as a reviewer with the given ID'
+ use :merge_requests_negatable_params, prefix: ''
+
+ optional :reviewer_id, types: [Integer, String],
+ integer_none_any: true,
+ desc: 'Returns merge requests which have the user as a reviewer with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`.'
mutually_exclusive :reviewer_id, :reviewer_username
- optional :state,
- type: String,
- values: %w[opened closed locked merged all],
- default: 'all',
- desc: 'Return opened, closed, locked, merged, or all merge requests'
- optional :order_by,
- type: String,
- values: Helpers::MergeRequestsHelpers.sort_options,
- default: 'created_at',
- desc: "Return merge requests ordered by #{Helpers::MergeRequestsHelpers.sort_options_help} fields."
- optional :sort,
- type: String,
- values: %w[asc desc],
- default: 'desc',
- desc: 'Return merge requests sorted in `asc` or `desc` order.'
- optional :with_labels_details, type: Boolean, desc: 'Return titles of labels and other details', default: false
- optional :with_merge_status_recheck, type: Boolean, desc: 'Request that stale merge statuses be rechecked asynchronously', default: false
- optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time'
- optional :created_before, type: DateTime, desc: 'Return merge requests created before the specified time'
- optional :updated_after, type: DateTime, desc: 'Return merge requests updated after the specified time'
- optional :updated_before, type: DateTime, desc: 'Return merge requests updated before the specified time'
- optional :view,
- type: String,
- values: %w[simple],
- desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request'
-
- optional :scope,
- type: String,
- values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
- desc: 'Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`'
- optional :source_branch, type: String, desc: 'Return merge requests with the given source branch'
- optional :source_project_id, type: Integer, desc: 'Return merge requests with the given source project id'
- optional :target_branch, type: String, desc: 'Return merge requests with the given target branch'
- optional :search,
- type: String,
- desc: 'Search merge requests for text present in the title, description, or any combination of these'
- optional :in, type: String, desc: '`title`, `description`, or a string joining them with comma'
- optional :wip, type: String, values: %w[yes no], desc: 'Search merge requests for WIP in the title'
- optional :not, type: Hash, desc: 'Parameters to negate' do
- use :merge_requests_negatable_params
- optional :reviewer_id,
- types: Integer,
- desc: 'Return merge requests which have the user as a reviewer with the given ID'
+ optional :state, type: String,
+ values: %w[opened closed locked merged all],
+ default: 'all',
+ desc: 'Returns `all` merge requests or just those that are `opened`, `closed`, `locked`, or `merged`.'
+ optional :order_by, type: String,
+ values: Helpers::MergeRequestsHelpers.sort_options,
+ default: 'created_at',
+ desc: "Returns merge requests ordered by #{Helpers::MergeRequestsHelpers.sort_options_help} fields. Introduced in GitLab 14.8."
+ optional :sort, type: String,
+ values: %w[asc desc],
+ default: 'desc',
+ desc: 'Returns merge requests sorted in `asc` or `desc` order.'
+ optional :with_labels_details, type: Boolean,
+ default: false,
+ desc: 'If `true`, response returns more details for each label in labels field: `:name`,`:color`, `:description`, `:description_html`, `:text_color`'
+ optional :with_merge_status_recheck, type: Boolean,
+ default: false,
+ desc: 'If `true`, this projection requests (but does not guarantee) that the `merge_status` field be recalculated asynchronously. Introduced in GitLab 13.0.'
+ optional :created_after, type: DateTime,
+ desc: 'Returns merge requests created on or after the given time. Expected in ISO 8601 format.',
+ documentation: { example: '2019-03-15T08:00:00Z' }
+ optional :created_before, type: DateTime,
+ desc: 'Returns merge requests created on or before the given time. Expected in ISO 8601 format.',
+ documentation: { example: '2019-03-15T08:00:00Z' }
+ optional :updated_after, type: DateTime,
+ desc: 'Returns merge requests updated on or after the given time. Expected in ISO 8601 format.',
+ documentation: { example: '2019-03-15T08:00:00Z' }
+ optional :updated_before, type: DateTime,
+ desc: 'Returns merge requests updated on or before the given time. Expected in ISO 8601 format.',
+ documentation: { example: '2019-03-15T08:00:00Z' }
+ optional :view, type: String,
+ values: %w[simple],
+ desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request'
+ optional :scope, type: String,
+ values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
+ desc: 'Returns merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`'
+ optional :source_branch, type: String, desc: 'Returns merge requests with the given source branch'
+ optional :source_project_id, type: Integer, desc: 'Returns merge requests with the given source project id'
+ optional :target_branch, type: String, desc: 'Returns merge requests with the given target branch'
+ optional :search, type: String,
+ desc: 'Search merge requests against their `title` and `description`.'
+ optional :in, type: String,
+ desc: 'Modify the scope of the search attribute. `title`, `description`, or a string joining them with comma.',
+ documentation: { example: 'title,description' }
+ optional :wip, type: String,
+ values: %w[yes no],
+ desc: 'Filter merge requests against their `wip` status. `yes` to return only draft merge requests, `no` to return non-draft merge requests.'
+ optional :not, type: Hash, desc: 'Returns merge requests that do not match the parameters supplied' do
+ use :merge_requests_negatable_params, prefix: '`<Negated>` '
+
+ optional :reviewer_id, types: Integer,
+ desc: '`<Negated>` Returns merge requests which have the user as a reviewer with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`.'
mutually_exclusive :reviewer_id, :reviewer_username
end
-
- optional :deployed_before,
- 'Return merge requests deployed before the given date/time'
- optional :deployed_after,
- 'Return merge requests deployed after the given date/time'
- optional :environment,
- 'Returns merge requests deployed to the given environment'
+ optional :deployed_before, desc: 'Returns merge requests deployed before the given date/time. Expected in ISO 8601 format.',
+ documentation: { example: '2019-03-15T08:00:00Z' }
+ optional :deployed_after, desc: 'Returns merge requests deployed after the given date/time. Expected in ISO 8601 format',
+ documentation: { example: '2019-03-15T08:00:00Z' }
+ optional :environment, desc: 'Returns merge requests deployed to the given environment',
+ documentation: { example: '2019-03-15T08:00:00Z' }
end
params :optional_scope_param do
- optional :scope,
- type: String,
- values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
- default: 'created_by_me',
- desc: 'Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`'
+ optional :scope, type: String,
+ values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
+ default: 'created_by_me',
+ desc: 'Returns merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`'
end
def handle_merge_request_errors!(merge_request)
diff --git a/lib/api/helpers/notes_helpers.rb b/lib/api/helpers/notes_helpers.rb
index 45671b09be9..302dac4abf7 100644
--- a/lib/api/helpers/notes_helpers.rb
+++ b/lib/api/helpers/notes_helpers.rb
@@ -9,7 +9,7 @@ module API
{
Issue => :team_planning,
MergeRequest => :code_review,
- Snippet => :snippets
+ Snippet => :source_code_management
}
end
@@ -90,7 +90,12 @@ module API
params = finder_params_by_noteable_type_and_id(noteable_type, noteable_id)
noteable = NotesFinder.new(current_user, params).target
- noteable = nil unless can?(current_user, noteable_read_ability_name(noteable), noteable)
+
+ # Checking `read_note` permission here, because API code does not seem to use NoteFinder to find notes,
+ # but rather pulls notes directly through notes association, so there is no chance to check read_note
+ # permission at service level. With WorkItem model we need to make sure that it has WorkItem::Widgets::Note
+ # available in order to access notes.
+ noteable = nil unless can_read_notes?(noteable)
noteable || not_found!(noteable_type)
end
@@ -147,6 +152,13 @@ module API
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/211538')
end
+
+ private
+
+ def can_read_notes?(noteable)
+ Ability.allowed?(current_user, noteable_read_ability_name(noteable), noteable) &&
+ Ability.allowed?(current_user, :read_note, noteable)
+ end
end
end
end
diff --git a/lib/api/helpers/packages/conan/api_helpers.rb b/lib/api/helpers/packages/conan/api_helpers.rb
index a9d91895cfe..3ea558f3569 100644
--- a/lib/api/helpers/packages/conan/api_helpers.rb
+++ b/lib/api/helpers/packages/conan/api_helpers.rb
@@ -47,14 +47,14 @@ module API
end
def recipe_upload_urls
- { upload_urls: file_names.select(&method(:recipe_file?)).to_h do |file_name|
- [file_name, build_recipe_file_upload_url(file_name)]
+ { upload_urls: file_names.select(&method(:recipe_file?)).index_with do |file_name|
+ build_recipe_file_upload_url(file_name)
end }
end
def package_upload_urls
- { upload_urls: file_names.select(&method(:package_file?)).to_h do |file_name|
- [file_name, build_package_file_upload_url(file_name)]
+ { upload_urls: file_names.select(&method(:package_file?)).index_with do |file_name|
+ build_package_file_upload_url(file_name)
end }
end
@@ -128,7 +128,7 @@ module API
strong_memoize(:project) do
case package_scope
when :project
- find_project!(params[:id])
+ user_project(action: :read_package)
when :instance
full_path = ::Packages::Conan::Metadatum.full_path_from(package_username: params[:package_username])
find_project!(full_path)
diff --git a/lib/api/helpers/packages/dependency_proxy_helpers.rb b/lib/api/helpers/packages/dependency_proxy_helpers.rb
index 1ae863a5a25..4b0e63c8f3b 100644
--- a/lib/api/helpers/packages/dependency_proxy_helpers.rb
+++ b/lib/api/helpers/packages/dependency_proxy_helpers.rb
@@ -19,7 +19,9 @@ module API
def redirect_registry_request(forward_to_registry: false, package_type: nil, target: nil, **options)
if forward_to_registry && redirect_registry_request_available?(package_type, target) && maven_forwarding_ff_enabled?(package_type, target)
::Gitlab::Tracking.event(self.options[:for].name, "#{package_type}_request_forward")
- redirect(registry_url(package_type, options))
+ redirect(registry_url(package_type, options), body: options[:body])
+ # For the requests with POST methods we need to set status 307 in order to keep request's method
+ status :temporary_redirect if options[:method] == 'POST'
else
yield
end
@@ -32,7 +34,7 @@ module API
case package_type
when :npm
- "#{base_url}#{options[:package_name]}"
+ "#{base_url}#{[options[:path], options[:package_name]].compact.join('/')}"
when :pypi
"#{base_url}#{options[:package_name]}/"
when :maven
diff --git a/lib/api/helpers/packages_helpers.rb b/lib/api/helpers/packages_helpers.rb
index 96a10d43401..8d913268405 100644
--- a/lib/api/helpers/packages_helpers.rb
+++ b/lib/api/helpers/packages_helpers.rb
@@ -78,10 +78,18 @@ module API
end
end
- def track_package_event(event_name, scope, **args)
- ::Packages::CreateEventService.new(nil, current_user, event_name: event_name, scope: scope).execute
+ def track_package_event(action, scope, **args)
+ ::Packages::CreateEventService.new(nil, current_user, event_name: action, scope: scope).execute
category = args.delete(:category) || self.options[:for].name
- ::Gitlab::Tracking.event(category, event_name.to_s, **args)
+ event_name = "i_package_#{scope}_user"
+ ::Gitlab::Tracking.event(
+ category,
+ action.to_s,
+ property: event_name,
+ label: 'redis_hll_counters.user_packages.user_packages_total_unique_counts_monthly',
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event_name).to_context],
+ **args
+ )
end
def present_package_file!(package_file, supports_direct_download: true)
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index c95bf0f0c21..9d370176e62 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -37,6 +37,10 @@ module API
optional :container_registry_access_level, type: String, values: %w(disabled private enabled), desc: 'Controls visibility of the container registry. One of `disabled`, `private` or `enabled`. `private` will make the container registry accessible only to project members (reporter role and above). `enabled` will make the container registry accessible to everyone who has access to the project. `disabled` will disable the container registry'
optional :security_and_compliance_access_level, type: String, values: %w(disabled private enabled), desc: 'Security and compliance access level. One of `disabled`, `private` or `enabled`'
optional :releases_access_level, type: String, values: %w(disabled private enabled), desc: 'Releases access level. One of `disabled`, `private` or `enabled`'
+ optional :environments_access_level, type: String, values: %w(disabled private enabled), desc: 'Environments access level. One of `disabled`, `private` or `enabled`'
+ optional :feature_flags_access_level, type: String, values: %w(disabled private enabled), desc: 'Feature flags access level. One of `disabled`, `private` or `enabled`'
+ optional :infrastructure_access_level, type: String, values: %w(disabled private enabled), desc: 'Infrastructure access level. One of `disabled`, `private` or `enabled`'
+ optional :monitor_access_level, type: String, values: %w(disabled private enabled), desc: 'Monitor access level. One of `disabled`, `private` or `enabled`'
optional :emails_disabled, type: Boolean, desc: 'Disable email notifications'
optional :show_default_award_emojis, type: Boolean, desc: 'Show default award emojis'
@@ -183,6 +187,10 @@ module API
:mr_default_target_self,
:enforce_auth_checks_on_uploads,
:releases_access_level,
+ :environments_access_level,
+ :feature_flags_access_level,
+ :infrastructure_access_level,
+ :monitor_access_level,
# TODO: remove in API v5, replaced by *_access_level
:issues_enabled,
diff --git a/lib/api/integrations/jira_connect/subscriptions.rb b/lib/api/integrations/jira_connect/subscriptions.rb
index a6e931ba7bb..cc2199e0ef6 100644
--- a/lib/api/integrations/jira_connect/subscriptions.rb
+++ b/lib/api/integrations/jira_connect/subscriptions.rb
@@ -11,14 +11,22 @@ module API
namespace :integrations do
namespace :jira_connect do
resource :subscriptions do
- desc 'Subscribe a namespace to a JiraConnectInstallation'
+ desc 'Subscribe a namespace to a JiraConnectInstallation' do
+ detail 'Subscribes the namespace to the JiraConnectInstallation'
+ success ::API::Entities::BasicSuccess
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[jira_connect_subscriptions]
+ end
params do
requires :jwt, type: String, desc: 'JWT token for authorization with the Jira Connect installation'
requires :namespace_path, type: String, desc: 'Path for the namespace that should be subscribed'
end
post do
- not_found! unless Feature.enabled?(:jira_connect_oauth, current_user)
-
jwt = Atlassian::JiraConnect::Jwt::Symmetric.new(params[:jwt])
installation = JiraConnectInstallation.find_by_client_key(jwt.iss_claim)
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index c4464666020..dbd5c5f9db1 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -191,7 +191,7 @@ module API
get '/authorized_keys', feature_category: :source_code_management, urgency: :high do
fingerprint = Gitlab::InsecureKeyFingerprint.new(params.fetch(:key)).fingerprint_sha256
- key = Key.find_by_fingerprint_sha256(fingerprint)
+ key = Key.auth.find_by_fingerprint_sha256(fingerprint)
not_found!('Key') if key.nil?
present key, with: Entities::SSHKey
end
diff --git a/lib/api/internal/kubernetes.rb b/lib/api/internal/kubernetes.rb
index d06d1e9862a..777d5019a29 100644
--- a/lib/api/internal/kubernetes.rb
+++ b/lib/api/internal/kubernetes.rb
@@ -6,7 +6,6 @@ module API
class Kubernetes < ::API::Base
include Gitlab::Utils::StrongMemoize
- feature_category :kubernetes_management
before do
check_feature_enabled
authenticate_gitlab_kas_request!
@@ -86,7 +85,7 @@ module API
detail 'Retrieves agent info for the given token'
end
route_setting :authentication, cluster_agent_token_allowed: true
- get '/agent_info', urgency: :low do
+ get '/agent_info', feature_category: :kubernetes_management, urgency: :low do
project = agent.project
status 200
@@ -104,7 +103,7 @@ module API
detail 'Retrieves project info (if authorized)'
end
route_setting :authentication, cluster_agent_token_allowed: true
- get '/project_info', urgency: :low do
+ get '/project_info', feature_category: :kubernetes_management, urgency: :low do
project = find_project(params[:id])
not_found! unless agent_has_access_to_project?(project)
@@ -127,7 +126,7 @@ module API
requires :agent_id, type: Integer, desc: 'ID of the configured Agent'
requires :agent_config, type: JSON, desc: 'Configuration for the Agent'
end
- post '/' do
+ post '/', feature_category: :kubernetes_management do
agent = ::Clusters::Agent.find(params[:agent_id])
::Clusters::Agents::RefreshAuthorizationService.new(agent, config: params[:agent_config]).execute
@@ -147,10 +146,10 @@ module API
end
optional :unique_counters, type: Hash do
- optional :agent_users_using_ci_tunnel, type: Set[Integer], desc: 'A set of user ids that have interacted a CI Tunnel to'
+ optional :agent_users_using_ci_tunnel, type: Array[Integer], desc: 'An array of user ids that have interacted with CI Tunnel'
end
end
- post '/' do
+ post '/', feature_category: :kubernetes_management do
increment_count_events
increment_unique_events
diff --git a/lib/api/markdown.rb b/lib/api/markdown.rb
index 276560f3433..f348e20cc0b 100644
--- a/lib/api/markdown.rb
+++ b/lib/api/markdown.rb
@@ -35,6 +35,11 @@ module API
context[:skip_project_check] = true
end
+ # Disable comments in markdown for IE browsers because comments in IE
+ # could allow script execution.
+ browser = Browser.new(headers['User-Agent'])
+ context[:allow_comments] = !browser.ie?
+
present({ html: Banzai.render_and_post_process(params[:text], context) }, with: Entities::Markdown)
end
end
diff --git a/lib/api/maven_packages.rb b/lib/api/maven_packages.rb
index 30cdaba76ba..411a53a481b 100644
--- a/lib/api/maven_packages.rb
+++ b/lib/api/maven_packages.rb
@@ -107,7 +107,7 @@ module API
def fetch_package(file_name:, project: nil, group: nil)
order_by_package_file = file_name.include?(::Packages::Maven::Metadata.filename) &&
- !params[:path].include?(::Packages::Maven::FindOrCreatePackageService::SNAPSHOT_TERM)
+ !params[:path].include?(::Packages::Maven::FindOrCreatePackageService::SNAPSHOT_TERM)
::Packages::Maven::PackageFinder.new(
current_user,
@@ -150,10 +150,17 @@ module API
desc 'Download the maven package file at instance level' do
detail 'This feature was introduced in GitLab 11.6'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[maven_packages]
end
params do
- requires :path, type: String, desc: 'Package path'
- requires :file_name, type: String, desc: 'Package file name'
+ requires :path, type: String, desc: 'Package path', documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT' }
+ requires :file_name, type: String, desc: 'Package file name', documentation: { example: 'mypkg-1.0-SNAPSHOT.jar' }
end
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
get 'packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
@@ -190,14 +197,24 @@ module API
desc 'Download the maven package file at a group level' do
detail 'This feature was introduced in GitLab 11.7'
+ success [
+ { code: 200 },
+ { code: 302 }
+ ]
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[maven_packages]
end
params do
- requires :id, type: String, desc: 'The ID of a group'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the group'
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
params do
- requires :path, type: String, desc: 'Package path'
- requires :file_name, type: String, desc: 'Package file name'
+ requires :path, type: String, desc: 'Package path', documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT' }
+ requires :file_name, type: String, desc: 'Package file name', documentation: { example: 'mypkg-1.0-SNAPSHOT.jar' }
end
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
get ':id/-/packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
@@ -225,10 +242,20 @@ module API
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Download the maven package file' do
detail 'This feature was introduced in GitLab 11.3'
+ success [
+ { code: 200 },
+ { code: 302 }
+ ]
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[maven_packages]
end
params do
- requires :path, type: String, desc: 'Package path'
- requires :file_name, type: String, desc: 'Package file name'
+ requires :path, type: String, desc: 'Package path', documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT' }
+ requires :file_name, type: String, desc: 'Package file name', documentation: { example: 'mypkg-1.0-SNAPSHOT.jar' }
end
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
get ':id/packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
@@ -250,10 +277,18 @@ module API
desc 'Workhorse authorize the maven package file upload' do
detail 'This feature was introduced in GitLab 11.3'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[maven_packages]
end
params do
- requires :path, type: String, desc: 'Package path'
- requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.maven_file_name_regex
+ requires :path, type: String, desc: 'Package path', documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT' }
+ requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.maven_file_name_regex, documentation: { example: 'mypkg-1.0-SNAPSHOT.pom' }
end
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
put ':id/packages/maven/*path/:file_name/authorize', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
@@ -266,10 +301,19 @@ module API
desc 'Upload the maven package file' do
detail 'This feature was introduced in GitLab 11.3'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' },
+ { code: 422, message: 'Unprocessable Entity' }
+ ]
+ tags %w[maven_packages]
end
params do
- requires :path, type: String, desc: 'Package path'
- requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.maven_file_name_regex
+ requires :path, type: String, desc: 'Package path', documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT' }
+ requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.maven_file_name_regex, documentation: { example: 'mypkg-1.0-SNAPSHOT.pom' }
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)', documentation: { type: 'file' }
end
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
diff --git a/lib/api/members.rb b/lib/api/members.rb
index f4e38207aca..76f4364106b 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -20,6 +20,8 @@ module API
resource source_type.pluralize, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Gets a list of group or project members viewable by the authenticated user.' do
success Entities::Member
+ is_array true
+ tags %w[members]
end
params do
optional :query, type: String, desc: 'A query string to search for members'
@@ -42,6 +44,8 @@ module API
desc 'Gets a list of group or project members viewable by the authenticated user, including those who gained membership through ancestor group.' do
success Entities::Member
+ is_array true
+ tags %w[members]
end
params do
optional :query, type: String, desc: 'A query string to search for members'
@@ -63,6 +67,7 @@ module API
desc 'Gets a member of a group or project.' do
success Entities::Member
+ tags %w[members]
end
params do
requires :user_id, type: Integer, desc: 'The user ID of the member'
@@ -82,6 +87,7 @@ module API
desc 'Gets a member of a group or project, including those who gained membership through ancestor group' do
success Entities::Member
+ tags %w[members]
end
params do
requires :user_id, type: Integer, desc: 'The user ID of the member'
@@ -101,6 +107,7 @@ module API
desc 'Adds a member to a group or project.' do
success Entities::Member
+ tags %w[members]
end
params do
requires :access_level, type: Integer, desc: 'A valid access level (defaults: `30`, developer access level)'
@@ -126,6 +133,7 @@ module API
desc 'Updates a member of a group or project.' do
success Entities::Member
+ tags %w[members]
end
params do
requires :user_id, type: Integer, desc: 'The user ID of the new member'
@@ -153,7 +161,9 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
- desc 'Removes a user from a group or project.'
+ desc 'Removes a user from a group or project.' do
+ tags %w[members]
+ end
params do
requires :user_id, type: Integer, desc: 'The user ID of the member'
optional :skip_subresources, type: Boolean, default: false,
diff --git a/lib/api/merge_request_approvals.rb b/lib/api/merge_request_approvals.rb
index 7622ec717cc..35fdcfe3ab0 100644
--- a/lib/api/merge_request_approvals.rb
+++ b/lib/api/merge_request_approvals.rb
@@ -88,6 +88,24 @@ module API
present_approval(merge_request)
end
+
+ desc 'Remove all merge request approvals' do
+ detail 'Clear all approvals of merge request. This feature was added in GitLab 15.4'
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
+ end
+ put 'reset_approvals', urgency: :low do
+ merge_request = find_project_merge_request(params[:merge_request_iid])
+
+ unauthorized! unless current_user.can?(:reset_merge_request_approvals, merge_request)
+
+ merge_request.approvals.delete_all
+
+ status :accepted
+ end
end
end
end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index bb2861aa221..a9572cf7ce6 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -134,7 +134,13 @@ module API
resource :merge_requests do
desc 'List merge requests' do
+ detail 'Get all merge requests the authenticated user has access to. By default it returns only merge requests created by the current user. To get all merge requests, use parameter `scope=all`.'
success Entities::MergeRequestBasic
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 422, message: 'Unprocessable entity' }
+ ]
+ tags %w[merge_requests]
end
params do
use :merge_requests_params
@@ -150,16 +156,24 @@ module API
end
params do
- requires :id, type: String, desc: 'The ID of a group'
+ requires :id, type: String, desc: 'The ID or URL-encoded path of the group owned by the authenticated user.'
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc 'Get a list of group merge requests' do
+ desc 'List group merge requests' do
+ detail 'Get all merge requests for this group and its subgroups.'
success Entities::MergeRequestBasic
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' },
+ { code: 422, message: 'Unprocessable entity' }
+ ]
+ tags %w[merge_requests]
end
params do
use :merge_requests_params
- optional :non_archived, type: Boolean, desc: 'Return merge requests from non archived projects',
- default: true
+ optional :non_archived, type: Boolean,
+ default: true,
+ desc: 'Returns merge requests from non archived projects only.'
end
get ":id/merge_requests", feature_category: :code_review, urgency: :low do
validate_anonymous_search_access! if declared_params[:search].present?
@@ -170,36 +184,62 @@ module API
end
params do
- requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project.'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
include TimeTrackingEndpoints
helpers do
params :optional_params do
- optional :assignee_id, type: Integer, desc: 'The ID of a user to assign the merge request'
- optional :assignee_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Comma-separated list of assignee ids'
- optional :reviewer_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Comma-separated list of reviewer ids'
- optional :description, type: String, desc: 'The description of the merge request'
- optional :labels, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of label names'
- optional :add_labels, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of label names'
- optional :remove_labels, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of label names'
- optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign the merge request'
- optional :remove_source_branch, type: Boolean, desc: 'Remove source branch when merging'
- optional :allow_collaboration, type: Boolean, desc: 'Allow commits from members who can merge to the target branch'
+ optional :assignee_id, type: Integer, desc: 'Assignee user ID.'
+ optional :assignee_ids, type: Array[Integer],
+ coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce,
+ desc: 'The IDs of the users to assign the merge request to, as a comma-separated list. Set to 0 or provide an empty value to unassign all assignees.',
+ documentation: { is_array: true }
+ optional :reviewer_ids, type: Array[Integer],
+ coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce,
+ desc: 'The IDs of the users to review the merge request, as a comma-separated list. Set to 0 or provide an empty value to unassign all reviewers.',
+ documentation: { is_array: true }
+ optional :description, type: String, desc: 'Description of the merge request. Limited to 1,048,576 characters.'
+ optional :labels, type: Array[String],
+ coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
+ desc: 'Comma-separated label names for a merge request. Set to an empty string to unassign all labels.',
+ documentation: { is_array: true }
+ optional :add_labels, type: Array[String],
+ coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
+ desc: 'Comma-separated label names to add to a merge request.',
+ documentation: { is_array: true }
+ optional :remove_labels, type: Array[String],
+ coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
+ desc: 'Comma-separated label names to remove from a merge request.',
+ documentation: { is_array: true }
+ optional :milestone_id, type: Integer, desc: 'The global ID of a milestone to assign the merge reques to.'
+ optional :remove_source_branch, type: Boolean, desc: 'Flag indicating if a merge request should remove the source branch when merging.'
+ optional :allow_collaboration, type: Boolean, desc: 'Allow commits from members who can merge to the target branch.'
optional :allow_maintainer_to_push, type: Boolean, as: :allow_collaboration, desc: '[deprecated] See allow_collaboration'
- optional :squash, type: Grape::API::Boolean, desc: 'When true, the commits will be squashed into a single commit on merge'
+ optional :squash, type: Grape::API::Boolean, desc: 'Squash commits into a single commit when merging.'
use :optional_params_ee
end
end
- desc 'List merge requests' do
+ desc 'List project merge requests' do
+ detail 'Get all merge requests for this project.'
success Entities::MergeRequestBasic
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' },
+ { code: 422, message: 'Unprocessable entity' }
+ ]
+ tags %w[merge_requests]
end
params do
use :merge_requests_params
- optional :iids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The IID array of merge requests'
+
+ optional :iids, type: Array[Integer],
+ coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce,
+ desc: 'Returns the request having the given `iid`.',
+ documentation: { is_array: true }
end
get ":id/merge_requests", feature_category: :code_review, urgency: :low do
authorize! :read_merge_request, user_project
@@ -226,15 +266,24 @@ module API
**options
end
- desc 'Create a merge request' do
+ desc 'Create merge request' do
+ detail 'Create a new merge request.'
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' },
+ { code: 409, message: 'Conflict' },
+ { code: 422, message: 'Unprocessable entity' }
+ ]
success Entities::MergeRequest
+ tags %w[merge_requests]
end
params do
- requires :title, type: String, desc: 'The title of the merge request'
- requires :source_branch, type: String, desc: 'The source branch'
- requires :target_branch, type: String, desc: 'The target branch'
+ requires :title, type: String, desc: 'The title of the merge request.'
+ requires :source_branch, type: String, desc: 'The source branch.'
+ requires :target_branch, type: String, desc: 'The target branch.'
optional :target_project_id, type: Integer,
- desc: 'The target project of the merge request defaults to the :id of the project'
+ desc: 'The target project of the merge request defaults to the :id of the project.'
use :optional_params
end
post ":id/merge_requests", feature_category: :code_review, urgency: :low do
@@ -253,9 +302,17 @@ module API
present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
end
- desc 'Delete a merge request'
+ desc 'Delete a merge request' do
+ detail 'Only for administrators and project owners. Deletes the merge request in question. '
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' },
+ { code: 412, message: 'Precondition failed' }
+ ]
+ tags %w[merge_requests]
+ end
params do
- requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request'
+ requires :merge_request_iid, type: Integer, desc: 'The internal ID of the merge request.'
end
delete ":id/merge_requests/:merge_request_iid", feature_category: :code_review, urgency: :low do
merge_request = find_project_merge_request(params[:merge_request_iid])
@@ -268,13 +325,19 @@ module API
end
params do
- requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request'
- optional :render_html, type: Boolean, desc: 'Returns the description and title rendered HTML'
- optional :include_diverged_commits_count, type: Boolean, desc: 'Returns the commits count behind the target branch'
- optional :include_rebase_in_progress, type: Boolean, desc: 'Returns whether a rebase operation is ongoing '
+ requires :merge_request_iid, type: Integer, desc: 'The internal ID of the merge request.'
+ optional :render_html, type: Boolean, desc: 'If `true`, response includes rendered HTML for title and description.'
+ optional :include_diverged_commits_count, type: Boolean, desc: 'If `true`, response includes the commits behind the target branch.'
+ optional :include_rebase_in_progress, type: Boolean, desc: 'If `true`, response includes whether a rebase operation is in progress.'
end
- desc 'Get a single merge request' do
+ desc 'Get single merge request' do
+ detail 'Shows information about a single merge request. Note: the `changes_count` value in the response is a string, not an integer. This is because when an merge request has too many changes to display and store, it is capped at 1,000. In that case, the API returns the string `"1000+"` for the changes count.'
+
success Entities::MergeRequest
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
get ':id/merge_requests/:merge_request_iid', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
@@ -289,8 +352,13 @@ module API
include_rebase_in_progress: params[:include_rebase_in_progress]
end
- desc 'Get the participants of a merge request' do
+ desc 'Get single merge request participants' do
+ detail 'Get a list of merge request participants.'
success Entities::UserBasic
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
get ':id/merge_requests/:merge_request_iid/participants', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
@@ -300,8 +368,13 @@ module API
present paginate(participants), with: Entities::UserBasic
end
- desc 'Get the reviewers of a merge request' do
+ desc 'Get single merge request reviewers' do
+ detail 'Get a list of merge request reviewers.'
success Entities::MergeRequestReviewer
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
get ':id/merge_requests/:merge_request_iid/reviewers', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
@@ -311,8 +384,13 @@ module API
present paginate(reviewers), with: Entities::MergeRequestReviewer
end
- desc 'Get the commits of a merge request' do
+ desc 'Get single merge request commits' do
+ detail 'Get a list of merge request commits.'
success Entities::Commit
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
get ':id/merge_requests/:merge_request_iid/commits', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
@@ -324,8 +402,13 @@ module API
present commits, with: Entities::Commit
end
- desc 'Get the context commits of a merge request' do
+ desc 'List merge request context commits' do
+ detail 'Get a list of merge request context commits.'
success Entities::Commit
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
get ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review, urgency: :high do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
@@ -336,10 +419,20 @@ module API
end
params do
- requires :commits, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, allow_blank: false, desc: 'List of context commits sha'
- end
- desc 'create context commits of merge request' do
+ requires :commits, type: Array[String],
+ coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
+ allow_blank: false,
+ desc: 'The context commits’ SHA.',
+ documentation: { is_array: true }
+ end
+ desc 'Create merge request context commits' do
+ detail 'Create a list of merge request context commits.'
success Entities::Commit
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
post ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review do
commit_ids = params[:commits]
@@ -363,9 +456,21 @@ module API
end
params do
- requires :commits, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, allow_blank: false, desc: 'List of context commits sha'
+ requires :commits, type: Array[String],
+ coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
+ allow_blank: false,
+ desc: 'The context commits’ SHA.',
+ documentation: { is_array: true }
+ end
+ desc 'Delete merge request context commits' do
+ detail 'Delete a list of merge request context commits.'
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
- desc 'remove context commits of merge request'
delete ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review do
commit_ids = params[:commits]
merge_request = find_merge_request_with_access(params[:merge_request_iid])
@@ -382,8 +487,13 @@ module API
status 204
end
- desc 'Show the merge request changes' do
+ desc 'Get single merge request changes' do
+ detail 'Shows information about the merge request including its files and changes.'
success Entities::MergeRequestChanges
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
get ':id/merge_requests/:merge_request_iid/changes', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
@@ -395,17 +505,46 @@ module API
access_raw_diffs: to_boolean(params.fetch(:access_raw_diffs, false))
end
- desc 'Get the merge request pipelines' do
+ desc 'Get the merge request diffs' do
+ detail 'Get a list of merge request diffs.'
+ success Entities::Diff
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
+ end
+ params do
+ use :pagination
+ end
+ get ':id/merge_requests/:merge_request_iid/diffs', feature_category: :code_review, urgency: :low do
+ merge_request = find_merge_request_with_access(params[:merge_request_iid])
+
+ present paginate(merge_request.merge_request_diff.paginated_diffs(params[:page], params[:per_page])).diffs, with: Entities::Diff
+ end
+
+ desc 'Get single merge request pipelines' do
+ detail 'Get a list of merge request pipelines.'
success Entities::Ci::PipelineBasic
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
get ':id/merge_requests/:merge_request_iid/pipelines', urgency: :low, feature_category: :continuous_integration do
pipelines = merge_request_pipelines_with_access
-
present paginate(pipelines), with: Entities::Ci::PipelineBasic
end
- desc 'Create a pipeline for merge request' do
+ desc 'Create merge request pipeline' do
+ detail 'Create a new pipeline for a merge request. A pipeline created via this endpoint doesn’t run a regular branch/tag pipeline. It requires `.gitlab-ci.yml` to be configured with `only: [merge_requests]` to create jobs.'
success ::API::Entities::Ci::Pipeline
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 404, message: 'Not found' },
+ { code: 405, message: 'Method not allowed' }
+ ]
+ tags %w[merge_requests]
end
post ':id/merge_requests/:merge_request_iid/pipelines', urgency: :low, feature_category: :continuous_integration do
pipeline = ::MergeRequests::CreatePipelineService
@@ -423,15 +562,25 @@ module API
end
end
- desc 'Update a merge request' do
+ desc 'Update merge request' do
+ detail 'Updates an existing merge request. You can change the target branch, title, or even close the merge request.'
success Entities::MergeRequest
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 404, message: 'Not found' },
+ { code: 409, message: 'Conflict' },
+ { code: 422, message: 'Unprocessable entity' }
+ ]
+ tags %w[merge_requests]
end
params do
- optional :title, type: String, allow_blank: false, desc: 'The title of the merge request'
- optional :target_branch, type: String, allow_blank: false, desc: 'The target branch'
- optional :state_event, type: String, values: %w[close reopen],
- desc: 'Status of the merge request'
- optional :discussion_locked, type: Boolean, desc: 'Whether the MR discussion is locked'
+ optional :title, type: String, allow_blank: false, desc: 'The title of the merge request.'
+ optional :target_branch, type: String, allow_blank: false, desc: 'The target branch.'
+ optional :state_event, type: String,
+ values: %w[close reopen],
+ desc: 'New state (close/reopen).'
+ optional :discussion_locked, type: Boolean,
+ desc: 'Flag indicating if the merge request’s discussion is locked. If the discussion is locked only project members can add, edit or resolve comments.'
use :optional_params
at_least_one_of(*::API::MergeRequests.update_params_at_least_one_of)
@@ -456,17 +605,27 @@ module API
end
desc 'Merge a merge request' do
+ detail 'Accept and merge changes submitted with the merge request using this API.'
success Entities::MergeRequest
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' },
+ { code: 405, message: 'Method not allowed' },
+ { code: 409, message: 'Conflict' },
+ { code: 422, message: 'Unprocessable entity' }
+ ]
+ tags %w[merge_requests]
end
params do
- optional :merge_commit_message, type: String, desc: 'Custom merge commit message'
- optional :squash_commit_message, type: String, desc: 'Custom squash commit message'
+ optional :merge_commit_message, type: String, desc: 'Custom merge commit message.'
+ optional :squash_commit_message, type: String, desc: 'Custom squash commit message.'
optional :should_remove_source_branch, type: Boolean,
- desc: 'When true, the source branch will be deleted if possible'
+ desc: 'If `true`, removes the source branch.'
optional :merge_when_pipeline_succeeds, type: Boolean,
- desc: 'When true, this merge request will be merged when the pipeline succeeds'
- optional :sha, type: String, desc: 'When present, must have the HEAD SHA of the source branch'
- optional :squash, type: Grape::API::Boolean, desc: 'When true, the commits will be squashed into a single commit on merge'
+ desc: 'If `true`, the merge request is merged when the pipeline succeeds.'
+ optional :sha, type: String, desc: 'If present, then this SHA must match the HEAD of the source branch, otherwise the merge fails.'
+ optional :squash, type: Grape::API::Boolean, desc: 'If `true`, the commits are squashed into a single commit on merge.'
end
put ':id/merge_requests/:merge_request_iid/merge', feature_category: :code_review, urgency: :low do
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/4796')
@@ -512,7 +671,13 @@ module API
end
end
- desc 'Returns the up to date merge-ref HEAD commit'
+ desc 'Returns the up to date merge-ref HEAD commit' do
+ detail 'Returns the up to date merge-ref HEAD commit'
+ failure [
+ { code: 400, message: 'Bad request' }
+ ]
+ tags %w[merge_requests]
+ end
get ':id/merge_requests/:merge_request_iid/merge_ref', feature_category: :code_review do
merge_request = find_project_merge_request(params[:merge_request_iid])
@@ -525,8 +690,16 @@ module API
end
end
- desc 'Cancel merge if "Merge When Pipeline Succeeds" is enabled' do
+ desc 'Cancel Merge When Pipeline Succeeds' do
+ detail 'Cancel merge if "Merge When Pipeline Succeeds" is enabled'
success Entities::MergeRequest
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' },
+ { code: 405, message: 'Method not allowed' },
+ { code: 406, message: 'Not acceptable' }
+ ]
+ tags %w[merge_requests]
end
post ':id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_succeeds', feature_category: :code_review do
merge_request = find_project_merge_request(params[:merge_request_iid])
@@ -536,11 +709,17 @@ module API
AutoMergeService.new(merge_request.target_project, current_user).cancel(merge_request)
end
- desc 'Rebase the merge request against its target branch' do
- detail 'This feature was added in GitLab 11.6'
+ desc 'Rebase a merge request' do
+ detail 'Automatically rebase the `source_branch` of the merge request against its `target_branch`. This feature was added in GitLab 11.6'
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' },
+ { code: 409, message: 'Conflict' }
+ ]
+ tags %w[merge_requests]
end
params do
- optional :skip_ci, type: Boolean, desc: 'Do not create CI pipeline'
+ optional :skip_ci, type: Boolean, desc: 'Set to true to skip creating a CI pipeline.'
end
put ':id/merge_requests/:merge_request_iid/rebase', feature_category: :code_review, urgency: :low do
merge_request = find_project_merge_request(params[:merge_request_iid])
@@ -554,22 +733,13 @@ module API
rescue ::MergeRequest::RebaseLockTimeout => e
render_api_error!(e.message, 409)
end
-
- desc 'Remove merge request approvals' do
- detail 'This feature was added in GitLab 15.4'
- end
- put ':id/merge_requests/:merge_request_iid/reset_approvals', feature_category: :code_review, urgency: :low do
- merge_request = find_project_merge_request(params[:merge_request_iid])
-
- unauthorized! unless current_user.bot? && merge_request.eligible_for_approval_by?(current_user)
-
- merge_request.approvals.delete_all
-
- status :accepted
- end
-
- desc 'List issues that will be closed on merge' do
+ desc 'List issues that close on merge' do
+ detail 'Get all the issues that would be closed by merging the provided merge request.'
success Entities::MRNote
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
params do
use :pagination
diff --git a/lib/api/ml/mlflow.rb b/lib/api/ml/mlflow.rb
index 56bfac1530e..54bbe0ee465 100644
--- a/lib/api/ml/mlflow.rb
+++ b/lib/api/ml/mlflow.rb
@@ -126,14 +126,31 @@ module API
end
params do
requires :name, type: String, desc: 'Experiment name'
+ optional :tags, type: Array, desc: 'Tags with information about the experiment'
optional :artifact_location, type: String, desc: 'This will be ignored'
- optional :tags, type: Array, desc: 'This will be ignored'
end
post 'create', urgency: :low do
- present experiment_repository.create!(params[:name]), with: Entities::Ml::Mlflow::NewExperiment
+ present experiment_repository.create!(params[:name], params[:tags]),
+ with: Entities::Ml::Mlflow::NewExperiment
rescue ActiveRecord::RecordInvalid
resource_already_exists!
end
+
+ desc 'Sets a tag for an experiment.' do
+ summary 'Sets a tag for an experiment. '
+
+ detail 'https://www.mlflow.org/docs/1.28.0/rest-api.html#set-experiment-tag'
+ end
+ params do
+ requires :experiment_id, type: String, desc: 'ID of the experiment.'
+ requires :key, type: String, desc: 'Name for the tag.'
+ requires :value, type: String, desc: 'Value for the tag.'
+ end
+ post 'set-experiment-tag', urgency: :low do
+ bad_request! unless experiment_repository.add_tag!(experiment, params[:key], params[:value])
+
+ {}
+ end
end
resource :runs do
@@ -148,10 +165,10 @@ module API
desc: 'Unix timestamp in milliseconds of when the run started.',
default: 0
optional :user_id, type: String, desc: 'This will be ignored'
- optional :tags, type: Array, desc: 'This will be ignored'
+ optional :tags, type: Array, desc: 'Tags are stored, but not displayed'
end
post 'create', urgency: :low do
- present candidate_repository.create!(experiment, params[:start_time]),
+ present candidate_repository.create!(experiment, params[:start_time], params[:tags]),
with: Entities::Ml::Mlflow::Run, packages_url: packages_url
end
@@ -229,6 +246,22 @@ module API
{}
end
+ desc 'Sets a tag for a run.' do
+ summary 'Sets a tag for a run. '
+
+ detail 'https://www.mlflow.org/docs/1.28.0/rest-api.html#set-tag'
+ end
+ params do
+ requires :run_id, type: String, desc: 'UUID of the run.'
+ requires :key, type: String, desc: 'Name for the tag.'
+ requires :value, type: String, desc: 'Value for the tag.'
+ end
+ post 'set-tag', urgency: :low do
+ bad_request! unless candidate_repository.add_tag!(candidate, params[:key], params[:value])
+
+ {}
+ end
+
desc 'Logs multiple parameters and metrics.' do
summary 'Log a batch of metrics and params for a run. Validation errors will block the entire batch, '\
'duplicate errors will be ignored.'
@@ -251,6 +284,7 @@ module API
post 'log-batch', urgency: :low do
candidate_repository.add_metrics(candidate, params[:metrics])
candidate_repository.add_params(candidate, params[:params])
+ candidate_repository.add_tags(candidate, params[:tags])
{}
end
diff --git a/lib/api/namespaces.rb b/lib/api/namespaces.rb
index eeb66c86b3b..2b1007e715a 100644
--- a/lib/api/namespaces.rb
+++ b/lib/api/namespaces.rb
@@ -6,6 +6,8 @@ module API
before { authenticate! }
+ NAMESPACES_TAGS = %w[namespaces].freeze
+
helpers do
params :optional_list_params_ee do
# EE::API::Namespaces would override this helper
@@ -20,12 +22,18 @@ module API
prepend_mod_with('API::Namespaces') # rubocop: disable Cop/InjectEnterpriseEditionModule
resource :namespaces do
- desc 'Get a namespaces list' do
+ desc 'List namespaces' do
+ detail 'Get a list of the namespaces of the authenticated user. If the user is an administrator, a list of all namespaces in the GitLab instance is shown.'
success Entities::Namespace
+ failure [
+ { code: 401, message: 'Unauthorized' }
+ ]
+ is_array true
+ tags NAMESPACES_TAGS
end
params do
- optional :search, type: String, desc: "Search query for namespaces"
- optional :owned_only, type: Boolean, desc: "Owned namespaces only"
+ optional :search, type: String, desc: 'Returns a list of namespaces the user is authorized to view based on the search criteria'
+ optional :owned_only, type: Boolean, desc: 'In GitLab 14.2 and later, returns a list of owned namespaces only'
use :pagination
use :optional_list_params_ee
@@ -46,11 +54,17 @@ module API
present paginate(namespaces), options.reverse_merge(custom_namespace_present_options)
end
- desc 'Get a namespace by ID' do
+ desc 'Get namespace by ID' do
+ detail 'Get a namespace by ID'
success Entities::Namespace
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags NAMESPACES_TAGS
end
params do
- requires :id, type: String, desc: "Namespace's ID or path"
+ requires :id, types: [String, Integer], desc: 'ID or URL-encoded path of the namespace'
end
get ':id', requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS, feature_category: :subgroups, urgency: :low do
user_namespace = find_namespace!(params[:id])
@@ -58,12 +72,17 @@ module API
present user_namespace, with: Entities::Namespace, current_user: current_user
end
- desc 'Get existence of a namespace including alternative suggestions' do
+ desc 'Get existence of a namespace' do
+ detail 'Get existence of a namespace by path. Suggests a new namespace path that does not already exist.'
success Entities::NamespaceExistence
+ failure [
+ { code: 401, message: 'Unauthorized' }
+ ]
+ tags NAMESPACES_TAGS
end
params do
- requires :namespace, type: String, desc: "Namespace's path"
- optional :parent_id, type: Integer, desc: "The ID of the parent namespace. If no ID is specified, only top-level namespaces are considered."
+ requires :namespace, type: String, desc: "Namespace’s path"
+ optional :parent_id, type: Integer, desc: 'The ID of the parent namespace. If no ID is specified, only top-level namespaces are considered.'
end
get ':namespace/exists', requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS, feature_category: :subgroups, urgency: :low do
check_rate_limit!(:namespace_exists, scope: current_user)
diff --git a/lib/api/npm_project_packages.rb b/lib/api/npm_project_packages.rb
index 494b493f5e0..f42ded5ac09 100644
--- a/lib/api/npm_project_packages.rb
+++ b/lib/api/npm_project_packages.rb
@@ -16,6 +16,12 @@ module API
namespace 'projects/:id/packages/npm' do
desc 'Download the NPM tarball' do
detail 'This feature was introduced in GitLab 11.8'
+ success code: 200
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[npm_packages]
end
params do
requires :package_name, type: String, desc: 'Package name'
@@ -33,13 +39,21 @@ module API
package_file = ::Packages::PackageFileFinder
.new(package, params[:file_name]).execute!
- track_package_event('pull_package', package, category: 'API::NpmPackages', project: project, namespace: project.namespace)
+ track_package_event('pull_package', :npm, category: 'API::NpmPackages', project: project, namespace: project.namespace)
present_package_file!(package_file)
end
desc 'Create NPM package' do
detail 'This feature was introduced in GitLab 11.8'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[npm_packages]
end
params do
requires :package_name, type: String, desc: 'Package name'
diff --git a/lib/api/nuget_group_packages.rb b/lib/api/nuget_group_packages.rb
index eb55e4cbf70..c93b24ee544 100644
--- a/lib/api/nuget_group_packages.rb
+++ b/lib/api/nuget_group_packages.rb
@@ -45,7 +45,7 @@ module API
end
params do
- requires :id, type: String, desc: 'The ID of a group', regexp: ::API::Concerns::Packages::NugetEndpoints::POSITIVE_INTEGER_REGEX
+ requires :id, types: [Integer, String], desc: 'The group ID or full group path.', regexp: ::API::Concerns::Packages::NugetEndpoints::POSITIVE_INTEGER_REGEX
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
diff --git a/lib/api/nuget_project_packages.rb b/lib/api/nuget_project_packages.rb
index d549a8be035..aa517661791 100644
--- a/lib/api/nuget_project_packages.rb
+++ b/lib/api/nuget_project_packages.rb
@@ -39,18 +39,19 @@ module API
end
def project_or_group
- authorized_user_project
+ authorized_user_project(action: :read_package)
end
def snowplow_gitlab_standard_context
- { project: authorized_user_project, namespace: authorized_user_project.namespace }
+ { project: project_or_group, namespace: project_or_group.namespace }
end
def authorize_nuget_upload
+ project = project_or_group
authorize_workhorse!(
- subject: project_or_group,
+ subject: project,
has_length: false,
- maximum_size: project_or_group.actual_limits.nuget_max_file_size
+ maximum_size: project.actual_limits.nuget_max_file_size
)
end
@@ -67,8 +68,9 @@ module API
end
def upload_nuget_package_file(symbol_package: false)
- authorize_upload!(project_or_group)
- bad_request!('File is too large') if project_or_group.actual_limits.exceeded?(:nuget_max_file_size, params[:package].size)
+ project = project_or_group
+ authorize_upload!(project)
+ bad_request!('File is too large') if project.actual_limits.exceeded?(:nuget_max_file_size, params[:package].size)
file_params = params.merge(
file: params[:package],
@@ -76,7 +78,7 @@ module API
)
package = ::Packages::CreateTemporaryPackageService.new(
- project_or_group, current_user, declared_params.merge(build: current_authenticated_job)
+ project, current_user, declared_params.merge(build: current_authenticated_job)
).execute(:nuget, name: temp_file_name(symbol_package))
package_file = ::Packages::CreatePackageFileService.new(package, file_params.merge(build: current_authenticated_job))
@@ -100,6 +102,14 @@ module API
# https://docs.microsoft.com/en-us/nuget/api/package-publish-resource
desc 'The NuGet Package Publish endpoint' do
detail 'This feature was introduced in GitLab 12.6'
+ success code: 201
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
end
params do
@@ -121,6 +131,17 @@ module API
forbidden!
end
+
+ desc 'The NuGet Package Authorize endpoint' do
+ detail 'This feature was introduced in GitLab 14.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
+ end
put 'authorize', urgency: :low do
authorize_nuget_upload
end
@@ -128,8 +149,15 @@ module API
# https://docs.microsoft.com/en-us/nuget/api/symbol-package-publish-resource
desc 'The NuGet Symbol Package Publish endpoint' do
detail 'This feature was introduced in GitLab 14.1'
+ success code: 201
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
end
-
params do
use :file_params
end
@@ -149,13 +177,24 @@ module API
forbidden!
end
+
+ desc 'The NuGet Symbol Package Authorize endpoint' do
+ detail 'This feature was introduced in GitLab 14.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
+ end
put 'symbolpackage/authorize', urgency: :low do
authorize_nuget_upload
end
# https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource
params do
- requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX
+ requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX, documentation: { example: 'mynugetpkg.1.3.0.17.nupkg' }
end
namespace '/download/*package_name' do
after_validation do
@@ -164,6 +203,13 @@ module API
desc 'The NuGet Content Service - index request' do
detail 'This feature was introduced in GitLab 12.8'
+ success code: 200, model: ::API::Entities::Nuget::PackagesVersions
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
end
get 'index', format: :json, urgency: :low do
present ::Packages::Nuget::PackagesVersionsPresenter.new(find_packages(params[:package_name])),
@@ -172,10 +218,17 @@ module API
desc 'The NuGet Content Service - content request' do
detail 'This feature was introduced in GitLab 12.8'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
end
params do
- requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
- requires :package_filename, type: String, desc: 'The NuGet package filename', regexp: API::NO_SLASH_URL_PART_REGEX
+ requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX, documentation: { example: '1.3.0.17' }
+ requires :package_filename, type: String, desc: 'The NuGet package filename', regexp: API::NO_SLASH_URL_PART_REGEX, documentation: { example: 'mynugetpkg.1.3.0.17.nupkg' }
end
get '*package_version/*package_filename', format: [:nupkg, :snupkg], urgency: :low do
filename = "#{params[:package_filename]}.#{params[:format]}"
diff --git a/lib/api/pages.rb b/lib/api/pages.rb
index 7e230bd3c67..0cedf7d975f 100644
--- a/lib/api/pages.rb
+++ b/lib/api/pages.rb
@@ -10,11 +10,18 @@ module API
end
params do
- requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
+ requires :id, types: [String, Integer],
+ desc: 'The ID or URL-encoded path of the project owned by the authenticated user'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Unpublish pages' do
- detail 'This feature was introduced in GitLab 12.6'
+ detail 'Remove pages. The user must have administrator access. This feature was introduced in GitLab 12.6'
+ success code: 204
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pages]
end
delete ':id/pages' do
authorize! :remove_pages, user_project
diff --git a/lib/api/project_container_repositories.rb b/lib/api/project_container_repositories.rb
index c5add42decc..e5e6ccdf025 100644
--- a/lib/api/project_container_repositories.rb
+++ b/lib/api/project_container_repositories.rb
@@ -20,9 +20,15 @@ module API
end
route_setting :authentication, job_token_allowed: true, job_token_scope: :project
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc 'Get a project container repositories' do
+ desc 'List container repositories within a project' do
detail 'This feature was introduced in GitLab 11.8.'
success Entities::ContainerRegistry::Repository
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ is_array true
+ tags %w[container_registry]
end
params do
use :pagination
@@ -41,6 +47,13 @@ module API
desc 'Delete repository' do
detail 'This feature was introduced in GitLab 11.8.'
+ success status: :accepted, message: 'Success'
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ is_array true
+ tags %w[container_registry]
end
params do
requires :repository_id, type: Integer, desc: 'The ID of the repository'
@@ -49,18 +62,20 @@ module API
authorize_admin_container_image!
repository.delete_scheduled!
- unless Feature.enabled?(:container_registry_delete_repository_with_cron_worker)
- DeleteContainerRepositoryWorker.perform_async(current_user.id, repository.id) # rubocop:disable CodeReuse/Worker
- end
-
track_package_event('delete_repository', :container, user: current_user, project: user_project, namespace: user_project.namespace)
status :accepted
end
- desc 'Get a list of repositories tags' do
+ desc 'List tags of a repository' do
detail 'This feature was introduced in GitLab 11.8.'
success Entities::ContainerRegistry::Tag
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ is_array true
+ tags %w[container_registry]
end
params do
requires :repository_id, type: Integer, desc: 'The ID of the repository'
@@ -77,6 +92,13 @@ module API
desc 'Delete repository tags (in bulk)' do
detail 'This feature was introduced in GitLab 11.8.'
+ success status: :accepted, message: 'Success'
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[container_registry]
end
params do
requires :repository_id, type: Integer, desc: 'The ID of the repository'
@@ -104,9 +126,15 @@ module API
status :accepted
end
- desc 'Get a details about repository tag' do
+ desc 'Get details about a repository tag' do
detail 'This feature was introduced in GitLab 11.8.'
success Entities::ContainerRegistry::TagDetails
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[container_registry]
end
params do
requires :repository_id, type: Integer, desc: 'The ID of the repository'
@@ -121,6 +149,13 @@ module API
desc 'Delete repository tag' do
detail 'This feature was introduced in GitLab 11.8.'
+ success status: :ok, message: 'Success'
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[container_registry]
end
params do
requires :repository_id, type: Integer, desc: 'The ID of the repository'
diff --git a/lib/api/project_packages.rb b/lib/api/project_packages.rb
index d09c481403f..158ba7465f4 100644
--- a/lib/api/project_packages.rb
+++ b/lib/api/project_packages.rb
@@ -17,9 +17,15 @@ module API
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc 'Get all project packages' do
+ desc 'Get a list of project packages' do
detail 'This feature was introduced in GitLab 11.8'
- success ::API::Entities::Package
+ success code: 200, model: ::API::Entities::Package
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Project Not Found' }
+ ]
+ is_array true
+ tags %w[project_packages]
end
params do
use :pagination
@@ -48,7 +54,12 @@ module API
desc 'Get a single project package' do
detail 'This feature was introduced in GitLab 11.9'
- success ::API::Entities::Package
+ success code: 200, model: ::API::Entities::Package
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[project_packages]
end
params do
requires :package_id, type: Integer, desc: 'The ID of a package'
@@ -58,11 +69,19 @@ module API
package = ::Packages::PackageFinder
.new(user_project, params[:package_id]).execute
+ render_api_error!('Package not found', 404) unless package.default?
+
present package, with: ::API::Entities::Package, user: current_user, namespace: user_project.namespace
end
- desc 'Remove a package' do
+ desc 'Delete a project package' do
detail 'This feature was introduced in GitLab 11.9'
+ success code: 204
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[project_packages]
end
params do
requires :package_id, type: Integer, desc: 'The ID of a package'
diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb
index 93ffb23fea8..7ef722301ca 100644
--- a/lib/api/project_snippets.rb
+++ b/lib/api/project_snippets.rb
@@ -6,7 +6,7 @@ module API
before { check_snippets_enabled }
- feature_category :snippets
+ feature_category :source_code_management
params do
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index fc898c30a71..de39419b70b 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -207,7 +207,10 @@ module API
resource :users, requirements: API::USER_REQUIREMENTS do
desc 'Get a user projects' do
- success Entities::BasicProjectDetails
+ success code: 200, model: Entities::BasicProjectDetails
+ failure [{ code: 404, message: '404 User Not Found' }]
+ tags %w[projects]
+ is_array true
end
params do
requires :user_id, type: String, desc: 'The ID or username of the user'
@@ -225,7 +228,10 @@ module API
end
desc 'Get projects starred by a user' do
- success Entities::BasicProjectDetails
+ success code: 200, model: Entities::BasicProjectDetails
+ failure [{ code: 404, message: '404 User Not Found' }]
+ tags %w[projects]
+ is_array true
end
params do
requires :user_id, type: String, desc: 'The ID or username of the user'
@@ -245,7 +251,9 @@ module API
include CustomAttributesEndpoints
desc 'Get a list of visible projects for authenticated user' do
- success Entities::BasicProjectDetails
+ success code: 200, model: Entities::BasicProjectDetails
+ tags %w[projects]
+ is_array true
end
params do
use :collection_params
@@ -258,12 +266,18 @@ module API
end
desc 'Create new project' do
- success Entities::Project
+ success code: 201, model: Entities::Project
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' },
+ { code: 400, message: 'Bad request' }
+ ]
+ tags %w[projects]
end
params do
- optional :name, type: String, desc: 'The name of the project'
- optional :path, type: String, desc: 'The path of the repository'
- optional :default_branch, type: String, desc: 'The default branch of the project'
+ optional :name, type: String, desc: 'The name of the project', documentation: { example: 'New Project' }
+ optional :path, type: String, desc: 'The path of the repository', documentation: { example: 'new_project' }
+ optional :default_branch, type: String, desc: 'The default branch of the project', documentation: { example: 'main' }
at_least_one_of :name, :path
use :optional_create_project_params
use :create_params
@@ -295,13 +309,19 @@ module API
end
desc 'Create new project for a specified user. Only available to admin users.' do
- success Entities::Project
+ success code: 201, model: Entities::Project
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' },
+ { code: 400, message: 'Bad request' }
+ ]
+ tags %w[projects]
end
params do
- requires :name, type: String, desc: 'The name of the project'
- requires :user_id, type: Integer, desc: 'The ID of a user'
- optional :path, type: String, desc: 'The path of the repository'
- optional :default_branch, type: String, desc: 'The default branch of the project'
+ requires :name, type: String, desc: 'The name of the project', documentation: { example: 'New Project' }
+ requires :user_id, type: Integer, desc: 'The ID of a user', documentation: { example: 1 }
+ optional :path, type: String, desc: 'The path of the repository', documentation: { example: 'new_project' }
+ optional :default_branch, type: String, desc: 'The default branch of the project', documentation: { example: 'main' }
use :optional_project_params
use :optional_create_project_params
use :create_params
@@ -339,7 +359,8 @@ module API
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Get a single project' do
- success Entities::ProjectWithAccess
+ success code: 200, model: Entities::ProjectWithAccess
+ tags %w[projects]
end
params do
use :statistics_params
@@ -364,15 +385,21 @@ module API
end
desc 'Fork new project for the current user or provided namespace.' do
- success Entities::Project
+ success code: 201, model: Entities::Project
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' },
+ { code: 409, message: 'Conflict' }
+ ]
+ tags %w[projects]
end
params do
- optional :namespace, type: String, desc: '(deprecated) The ID or name of the namespace that the project will be forked into'
- optional :namespace_id, type: Integer, desc: 'The ID of the namespace that the project will be forked into'
- optional :namespace_path, type: String, desc: 'The path of the namespace that the project will be forked into'
- optional :path, type: String, desc: 'The path that will be assigned to the fork'
- optional :name, type: String, desc: 'The name that will be assigned to the fork'
- optional :description, type: String, desc: 'The description that will be assigned to the fork'
+ optional :namespace, type: String, desc: '(deprecated) The ID or name of the namespace that the project will be forked into', documentation: { example: 'gitlab' }
+ optional :namespace_id, type: Integer, desc: 'The ID of the namespace that the project will be forked into', documentation: { example: 1 }
+ optional :namespace_path, type: String, desc: 'The path of the namespace that the project will be forked into', documentation: { example: 'new_path/gitlab' }
+ optional :path, type: String, desc: 'The path that will be assigned to the fork', documentation: { example: 'fork' }
+ optional :name, type: String, desc: 'The name that will be assigned to the fork', documentation: { example: 'Fork' }
+ optional :description, type: String, desc: 'The description that will be assigned to the fork', documentation: { example: 'Description' }
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the fork'
optional :mr_default_target_self, type: Boolean, desc: 'Merge requests of this forked project targets itself by default'
end
@@ -410,7 +437,9 @@ module API
end
desc 'List forks of this project' do
- success Entities::Project
+ success code: 200, model: Entities::Project
+ tags %w[projects]
+ is_array true
end
params do
use :collection_params
@@ -422,19 +451,30 @@ module API
present_projects forks, request_scope: user_project
end
- desc 'Check pages access of this project'
+ desc 'Check pages access of this project' do
+ success code: 200
+ failure [
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ tags %w[projects]
+ end
get ':id/pages_access', urgency: :low, feature_category: :pages do
authorize! :read_pages_content, user_project unless user_project.public_pages?
status 200
end
desc 'Update an existing project' do
- success Entities::Project
+ success code: 200, model: Entities::Project
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ tags %w[projects]
end
params do
- optional :name, type: String, desc: 'The name of the project'
- optional :default_branch, type: String, desc: 'The default branch of the project'
- optional :path, type: String, desc: 'The path of the repository'
+ optional :name, type: String, desc: 'The name of the project', documentation: { example: 'project' }
+ optional :default_branch, type: String, desc: 'The default branch of the project', documentation: { example: 'main' }
+ optional :path, type: String, desc: 'The path of the repository', documentation: { example: 'group/project' }
use :optional_project_params
use :optional_update_params
@@ -466,7 +506,11 @@ module API
end
desc 'Archive a project' do
- success Entities::Project
+ success code: 201, model: Entities::Project
+ failure [
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ tags %w[projects]
end
post ':id/archive', feature_category: :projects do
authorize!(:archive_project, user_project)
@@ -477,7 +521,11 @@ module API
end
desc 'Unarchive a project' do
- success Entities::Project
+ success code: 201, model: Entities::Project
+ failure [
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ tags %w[projects]
end
post ':id/unarchive', feature_category: :projects, urgency: :default do
authorize!(:archive_project, user_project)
@@ -488,7 +536,12 @@ module API
end
desc 'Star a project' do
- success Entities::Project
+ success code: 201, model: Entities::Project
+ failure [
+ { code: 304, message: 'Not modified' },
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ tags %w[projects]
end
post ':id/star', feature_category: :projects do
if current_user.starred?(user_project)
@@ -502,7 +555,12 @@ module API
end
desc 'Unstar a project' do
- success Entities::Project
+ success code: 201, model: Entities::Project
+ failure [
+ { code: 304, message: 'Not modified' },
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ tags %w[projects]
end
post ':id/unstar', feature_category: :projects do
if current_user.starred?(user_project)
@@ -516,10 +574,16 @@ module API
end
desc 'Get the users who starred a project' do
- success Entities::UserBasic
+ success code: 200, model: Entities::UserBasic
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ is_array true
+ tags %w[projects]
end
params do
- optional :search, type: String, desc: 'Return list of users matching the search criteria'
+ optional :search, type: String, desc: 'Return list of users matching the search criteria', documentation: { example: 'user' }
use :pagination
end
get ':id/starrers', feature_category: :projects do
@@ -528,23 +592,44 @@ module API
present paginate(starrers), with: Entities::UserStarsProject
end
- desc 'Get languages in project repository'
+ desc 'Get languages in project repository' do
+ success code: 200
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ is_array true
+ tags %w[projects]
+ end
get ':id/languages', feature_category: :source_code_management, urgency: :medium do
::Projects::RepositoryLanguagesService
.new(user_project, current_user)
.execute.to_h { |lang| [lang.name, lang.share] }
end
- desc 'Delete a project'
+ desc 'Delete a project' do
+ success code: 202
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
+ end
delete ":id", feature_category: :projects do
authorize! :remove_project, user_project
delete_project(user_project)
end
- desc 'Mark this project as forked from another'
+ desc 'Mark this project as forked from another' do
+ success code: 201, model: Entities::Project
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
+ end
params do
- requires :forked_from_id, type: String, desc: 'The ID of the project it was forked from'
+ requires :forked_from_id, type: String, desc: 'The ID of the project it was forked from', documentation: { example: 'gitlab' }
end
post ":id/fork/:forked_from_id", feature_category: :source_code_management do
authorize! :admin_project, user_project
@@ -559,12 +644,20 @@ module API
if result
present_project user_project.reset, with: Entities::Project, current_user: current_user
- else
- render_api_error!("Project already forked", 409) if user_project.forked?
+ elsif user_project.forked?
+ render_api_error!("Project already forked", 409)
end
end
- desc 'Remove a forked_from relationship'
+ desc 'Remove a forked_from relationship' do
+ success code: 204
+ failure [
+ { code: 304, message: 'Not modified' },
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
+ end
delete ":id/fork", feature_category: :source_code_management do
authorize! :remove_fork_project, user_project
@@ -576,10 +669,16 @@ module API
end
desc 'Share the project with a group' do
- success Entities::ProjectGroupLink
+ success code: 201, model: Entities::ProjectGroupLink
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
end
params do
- requires :group_id, type: Integer, desc: 'The ID of a group'
+ requires :group_id, type: Integer, desc: 'The ID of a group', documentation: { example: 1 }
requires :group_access, type: Integer, values: Gitlab::Access.values, as: :link_group_access, desc: 'The group access level'
optional :expires_at, type: Date, desc: 'Share expiration date'
end
@@ -601,6 +700,14 @@ module API
end
end
+ desc 'Remove a group share' do
+ success code: 204
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
+ end
params do
requires :group_id, type: Integer, desc: 'The ID of the group'
end
@@ -619,6 +726,12 @@ module API
desc 'Import members from another project' do
detail 'This feature was introduced in GitLab 14.2'
+ success code: 201
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
end
params do
requires :project_id, type: Integer, desc: 'The ID of the source project to import the members from.'
@@ -642,6 +755,11 @@ module API
desc 'Workhorse authorize the file upload' do
detail 'This feature was introduced in GitLab 13.11'
+ success code: 200
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
end
post ':id/uploads/authorize', feature_category: :not_owned do # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned
require_gitlab_workhorse!
@@ -651,7 +769,13 @@ module API
FileUploader.workhorse_authorize(has_length: false, maximum_size: project_attachment_size(user_project))
end
- desc 'Upload a file'
+ desc 'Upload a file' do
+ success code: 201, model: Entities::ProjectUpload
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
+ end
params do
requires :file, types: [Rack::Multipart::UploadedFile, ::API::Validations::Types::WorkhorseFile], desc: 'The attachment file to be uploaded', documentation: { type: 'file' }
end
@@ -666,10 +790,16 @@ module API
end
desc 'Get the users list of a project' do
- success Entities::UserBasic
+ success code: 200, model: Entities::UserBasic
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ is_array true
+ tags %w[projects]
end
params do
- optional :search, type: String, desc: 'Return list of users matching the search criteria'
+ optional :search, type: String, desc: 'Return list of users matching the search criteria', documentation: { example: 'user' }
optional :skip_users, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Filter out users with the specified IDs'
use :pagination
end
@@ -683,10 +813,16 @@ module API
end
desc 'Get ancestor and shared groups for a project' do
- success Entities::PublicGroupDetails
+ success code: 200, model: Entities::PublicGroupDetails
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ is_array true
+ tags %w[projects]
end
params do
- optional :search, type: String, desc: 'Return list of groups matching the search criteria'
+ optional :search, type: String, desc: 'Return list of groups matching the search criteria', documentation: { example: 'group' }
optional :skip_groups, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of group ids to exclude from list'
optional :with_shared, type: Boolean, default: false,
desc: 'Include shared groups'
@@ -705,6 +841,13 @@ module API
desc 'Start the housekeeping task for a project' do
detail 'This feature was introduced in GitLab 9.0.'
+ success code: 201
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Unauthenticated' },
+ { code: 409, message: 'Conflict' }
+ ]
+ tags %w[projects]
end
post ':id/housekeeping', feature_category: :source_code_management do
authorize_admin_project
@@ -718,6 +861,12 @@ module API
desc 'Start a task to recalculate repository size for a project' do
detail 'This feature was introduced in GitLab 15.0.'
+ success code: 201
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ tags %w[projects]
end
post ':id/repository_size', feature_category: :source_code_management do
authorize_admin_project
@@ -727,9 +876,17 @@ module API
::Projects::UpdateStatisticsService.new(user_project, nil, statistics: [:repository_size, :lfs_objects_size]).execute
end
- desc 'Transfer a project to a new namespace'
+ desc 'Transfer a project to a new namespace' do
+ success code: 200, model: Entities::Project
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
+ end
params do
- requires :namespace, type: String, desc: 'The ID or path of the new namespace'
+ requires :namespace, type: String, desc: 'The ID or path of the new namespace', documentation: { example: 'gitlab' }
end
put ":id/transfer", feature_category: :projects do
authorize! :change_namespace, user_project
@@ -744,9 +901,16 @@ module API
end
end
- desc 'Get the namespaces to where the project can be transferred'
+ desc 'Get the namespaces to where the project can be transferred' do
+ success code: 200, model: Entities::PublicGroupDetails
+ failure [
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ is_array true
+ tags %w[projects]
+ end
params do
- optional :search, type: String, desc: 'Return list of namespaces matching the search criteria'
+ optional :search, type: String, desc: 'Return list of namespaces matching the search criteria', documentation: { example: 'search' }
use :pagination
end
get ":id/transfer_locations", feature_category: :projects do
@@ -761,7 +925,11 @@ module API
end
desc 'Show the storage information' do
- success Entities::ProjectRepositoryStorage
+ success code: 200, model: Entities::ProjectRepositoryStorage
+ failure [
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ tags %w[projects]
end
params do
requires :id, type: String, desc: 'ID of a project'
diff --git a/lib/api/pypi_packages.rb b/lib/api/pypi_packages.rb
index 6c649483da1..f9470ce1cb6 100644
--- a/lib/api/pypi_packages.rb
+++ b/lib/api/pypi_packages.rb
@@ -32,12 +32,12 @@ module API
helpers do
params :package_download do
- requires :file_identifier, type: String, desc: 'The PyPi package file identifier', file_path: true
- requires :sha256, type: String, desc: 'The PyPi package sha256 check sum'
+ requires :file_identifier, type: String, desc: 'The PyPi package file identifier', file_path: true, documentation: { example: 'my.pypi.package-0.0.1.tar.gz' }
+ requires :sha256, type: String, desc: 'The PyPi package sha256 check sum', documentation: { example: '5y57017232013c8ac80647f4ca153k3726f6cba62d055cd747844ed95b3c65ff' }
end
params :package_name do
- requires :package_name, type: String, file_path: true, desc: 'The PyPi package name'
+ requires :package_name, type: String, file_path: true, desc: 'The PyPi package name', documentation: { example: 'my.pypi.package' }
end
def present_simple_index(group_or_project)
@@ -102,7 +102,7 @@ module API
end
params do
- requires :id, type: String, desc: 'The ID of a group'
+ requires :id, types: [Integer, String], desc: 'The ID or full path of the group.'
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
after_validation do
@@ -110,6 +110,16 @@ module API
end
namespace ':id/-/packages/pypi' do
+ desc 'Download a package file from a group' do
+ detail 'This feature was introduced in GitLab 13.12'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pypi_packages]
+ end
params do
use :package_download
end
@@ -123,13 +133,20 @@ module API
package = Packages::Pypi::PackageFinder.new(current_user, group, { filename: filename, sha256: params[:sha256] }).execute
package_file = ::Packages::PackageFileFinder.new(package, filename, with_file_name_like: false).execute
- track_package_event('pull_package', :pypi)
+ track_package_event('pull_package', :pypi, namespace: group, project: package.project)
present_package_file!(package_file, supports_direct_download: true)
end
desc 'The PyPi Simple Group Index Endpoint' do
detail 'This feature was introduced in GitLab 15.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pypi_packages]
end
# An API entry point but returns an HTML file instead of JSON.
@@ -141,6 +158,13 @@ module API
desc 'The PyPi Simple Group Package Endpoint' do
detail 'This feature was introduced in GitLab 12.10'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pypi_packages]
end
params do
@@ -164,6 +188,13 @@ module API
namespace ':id/packages/pypi' do
desc 'The PyPi package download endpoint' do
detail 'This feature was introduced in GitLab 12.10'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pypi_packages]
end
params do
@@ -185,6 +216,13 @@ module API
desc 'The PyPi Simple Project Index Endpoint' do
detail 'This feature was introduced in GitLab 15.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pypi_packages]
end
# An API entry point but returns an HTML file instead of JSON.
@@ -196,6 +234,13 @@ module API
desc 'The PyPi Simple Project Package Endpoint' do
detail 'This feature was introduced in GitLab 12.10'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pypi_packages]
end
params do
@@ -211,15 +256,24 @@ module API
desc 'The PyPi Package upload endpoint' do
detail 'This feature was introduced in GitLab 12.10'
+ success code: 201
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' },
+ { code: 422, message: 'Unprocessable Entity' }
+ ]
+ tags %w[pypi_packages]
end
params do
requires :content, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)', documentation: { type: 'file' }
- requires :name, type: String
- requires :version, type: String
- optional :requires_python, type: String
- optional :md5_digest, type: String
- optional :sha256_digest, type: String, regexp: Gitlab::Regex.sha256_regex
+ requires :name, type: String, documentation: { example: 'my.pypi.package' }
+ requires :version, type: String, documentation: { example: '1.3.7' }
+ optional :requires_python, type: String, documentation: { example: '>=3.7' }
+ optional :md5_digest, type: String, documentation: { example: '900150983cd24fb0d6963f7d28e17f72' }
+ optional :sha256_digest, type: String, regexp: Gitlab::Regex.sha256_regex, documentation: { example: 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad' }
end
route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
@@ -243,6 +297,17 @@ module API
forbidden!
end
+ desc 'Authorize the PyPi package upload from workhorse' do
+ detail 'This feature was introduced in GitLab 12.10'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pypi_packages]
+ end
+
route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
post 'authorize' do
project = project!(action: :read_project)
diff --git a/lib/api/release/links.rb b/lib/api/release/links.rb
index c72f90dfdf3..0e83d086a6e 100644
--- a/lib/api/release/links.rb
+++ b/lib/api/release/links.rb
@@ -10,13 +10,13 @@ module API
RELEASE_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS
.merge(tag_name: API::NO_SLASH_URL_PART_REGEX)
- before { authorize! :read_release, user_project }
+ after_validation { authorize! :read_release, user_project }
feature_category :release_orchestration
urgency :low
params do
- requires :id, type: [String, Integer], desc: 'The ID or URL-encoded path of the project'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
end
resource 'projects/:id', requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
params do
diff --git a/lib/api/rpm_project_packages.rb b/lib/api/rpm_project_packages.rb
index 40b8d022c6c..f02d288982a 100644
--- a/lib/api/rpm_project_packages.rb
+++ b/lib/api/rpm_project_packages.rb
@@ -25,7 +25,16 @@ module API
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace ':id/packages/rpm' do
- desc 'Download repository metadata files'
+ desc 'Download repository metadata files' do
+ detail 'This feature was introduced in GitLab 15.7'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[rpm_packages]
+ end
params do
requires :file_name, type: String, desc: 'Repository metadata file name'
end
@@ -40,7 +49,15 @@ module API
present_carrierwave_file!(repository_file.file)
end
- desc 'Download RPM package files'
+ desc 'Download RPM package files' do
+ detail 'This feature was introduced in GitLab 15.7'
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[rpm_packages]
+ end
params do
requires :package_file_id, type: Integer, desc: 'RPM package file id'
requires :file_name, type: String, desc: 'RPM package file name'
@@ -56,7 +73,16 @@ module API
not_found!
end
- desc 'Upload a RPM package'
+ desc 'Upload a RPM package' do
+ detail 'This feature was introduced in GitLab 15.7'
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[rpm_packages]
+ end
post do
authorize_create_package!(authorized_user_project)
@@ -64,6 +90,10 @@ module API
bad_request!('File is too large')
end
+ if Packages::Rpm::RepositoryFile.has_oversized_filelists?(project_id: authorized_user_project.id)
+ bad_request!('Repository packages limit exceeded')
+ end
+
track_package_event(
'push_package',
:rpm,
@@ -76,7 +106,15 @@ module API
not_found!
end
- desc 'Authorize package upload from workhorse'
+ desc 'Authorize package upload from workhorse' do
+ detail 'This feature was introduced in GitLab 15.7'
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[rpm_packages]
+ end
post 'authorize' do
not_found!
end
diff --git a/lib/api/rubygem_packages.rb b/lib/api/rubygem_packages.rb
index 87cf1f66223..af0ceb1acfc 100644
--- a/lib/api/rubygem_packages.rb
+++ b/lib/api/rubygem_packages.rb
@@ -28,19 +28,27 @@ module API
before do
require_packages_enabled!
authenticate_non_get!
+ end
+
+ after_validation do
not_found! unless Feature.enabled?(:rubygem_packages, user_project)
end
params do
- requires :id, type: String, desc: 'The ID or full path of a project'
+ requires :id, types: [Integer, String], desc: 'The ID or URL-encoded path of the project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace ':id/packages/rubygems' do
desc 'Download the spec index file' do
detail 'This feature was introduced in GitLab 13.9'
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[rubygem_packages]
end
params do
- requires :file_name, type: String, desc: 'Spec file name'
+ requires :file_name, type: String, desc: 'Spec file name', documentation: { type: 'file' }
end
get ":file_name", requirements: FILE_NAME_REQUIREMENTS do
# To be implemented in https://gitlab.com/gitlab-org/gitlab/-/issues/299267
@@ -49,9 +57,14 @@ module API
desc 'Download the gemspec file' do
detail 'This feature was introduced in GitLab 13.9'
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[rubygem_packages]
end
params do
- requires :file_name, type: String, desc: 'Gemspec file name'
+ requires :file_name, type: String, desc: 'Gemspec file name', documentation: { type: 'file' }
end
get "quick/Marshal.#{MARSHAL_VERSION}/:file_name", requirements: FILE_NAME_REQUIREMENTS do
# To be implemented in https://gitlab.com/gitlab-org/gitlab/-/issues/299284
@@ -60,9 +73,16 @@ module API
desc 'Download the .gem package' do
detail 'This feature was introduced in GitLab 13.9'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[rubygem_packages]
end
params do
- requires :file_name, type: String, desc: 'Package file name'
+ requires :file_name, type: String, desc: 'Package file name', documentation: { type: 'file' }
end
get "gems/:file_name", requirements: FILE_NAME_REQUIREMENTS do
authorize_read_package!(user_project)
@@ -80,6 +100,12 @@ module API
namespace 'api/v1' do
desc 'Authorize a gem upload from workhorse' do
detail 'This feature was introduced in GitLab 13.9'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' }
+ ]
+ tags %w[rubygem_packages]
end
post 'gems/authorize' do
authorize_workhorse!(
@@ -91,6 +117,13 @@ module API
desc 'Upload a gem' do
detail 'This feature was introduced in GitLab 13.9'
+ success code: 201
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[rubygem_packages]
end
params do
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)', documentation: { type: 'file' }
@@ -133,6 +166,14 @@ module API
desc 'Fetch a list of dependencies' do
detail 'This feature was introduced in GitLab 13.9'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ is_array true
+ tags %w[rubygem_packages]
end
params do
optional :gems, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma delimited gem names'
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 26b7e58bc7a..8b47604fe86 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -66,6 +66,7 @@ module API
requires :eks_secret_access_key, type: String, desc: 'Secret access key for the EKS integration IAM user'
end
optional :email_author_in_body, type: Boolean, desc: 'Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead.'
+ optional :email_confirmation_setting, type: String, values: ApplicationSetting.email_confirmation_settings.keys, desc: "Email confirmation setting, possible values: `off`, `soft`, and `hard`"
optional :enabled_git_access_protocol, type: String, values: %w[ssh http nil], desc: 'Allow only the selected protocols to be used for Git access.'
optional :gitpod_enabled, type: Boolean, desc: 'Enable Gitpod'
given gitpod_enabled: ->(val) { val } do
@@ -90,7 +91,7 @@ module API
end
optional :html_emails_enabled, type: Boolean, desc: 'By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.'
optional :import_sources, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
- values: %w[github bitbucket bitbucket_server gitlab google_code fogbugz git gitlab_project gitea manifest phabricator],
+ values: %w[github bitbucket bitbucket_server gitlab fogbugz git gitlab_project gitea manifest phabricator],
desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com'
optional :in_product_marketing_emails_enabled, type: Boolean, desc: 'By default, in-product marketing emails are enabled. To disable these emails, disable this option.'
optional :invisible_captcha_enabled, type: Boolean, desc: 'Enable Invisible Captcha spam detection during signup.'
@@ -100,6 +101,7 @@ module API
optional :max_import_size, type: Integer, desc: 'Maximum import size in MB'
optional :max_pages_size, type: Integer, desc: 'Maximum size of pages in MB'
optional :max_pages_custom_domains_per_project, type: Integer, desc: 'Maximum number of GitLab Pages custom domains per project'
+ optional :max_terraform_state_size_bytes, type: Integer, desc: "Maximum size in bytes of the Terraform state file. Set this to 0 for unlimited file size."
optional :metrics_method_call_threshold, type: Integer, desc: 'A method call is only tracked when it takes longer to complete than the given amount of milliseconds.'
optional :password_authentication_enabled, type: Boolean, desc: 'Flag indicating if password authentication is enabled for the web interface' # support legacy names, can be removed in v5
optional :password_authentication_enabled_for_web, type: Boolean, desc: 'Flag indicating if password authentication is enabled for the web interface'
@@ -139,7 +141,6 @@ module API
requires :two_factor_grace_period, type: Integer, desc: 'Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication'
end
optional :restricted_visibility_levels, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Selected levels cannot be used by non-admin users for groups, projects or snippets. If the public level is restricted, user profiles are only visible to logged in users.'
- optional :send_user_confirmation_email, type: Boolean, desc: 'Send confirmation email on sign-up'
optional :session_expire_delay, type: Integer, desc: 'Session duration in minutes. GitLab restart is required to apply changes.'
optional :shared_runners_enabled, type: Boolean, desc: 'Enable shared runners for new projects'
given shared_runners_enabled: ->(val) { val } do
@@ -184,6 +185,9 @@ module API
optional :group_runner_token_expiration_interval, type: Integer, desc: 'Token expiration interval for group runners, in seconds'
optional :project_runner_token_expiration_interval, type: Integer, desc: 'Token expiration interval for project runners, in seconds'
optional :pipeline_limit_per_project_user_sha, type: Integer, desc: "Maximum number of pipeline creation requests allowed per minute per user and commit. Set to 0 for unlimited requests per minute."
+ optional :jira_connect_application_key, type: String, desc: "Application ID of the OAuth application that should be used to authenticate with the GitLab.com for Jira Cloud app"
+ optional :jira_connect_proxy_url, type: String, desc: "URL of the GitLab instance that should be used as a proxy for the GitLab.com for Jira Cloud app"
+ optional :bulk_import_enabled, type: Boolean, desc: 'Enable migrating GitLab groups and projects by direct transfer'
Gitlab::SSHPublicKey.supported_types.each do |type|
optional :"#{type}_key_restriction",
diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb
index 36698a220bd..104848206a3 100644
--- a/lib/api/snippets.rb
+++ b/lib/api/snippets.rb
@@ -5,7 +5,7 @@ module API
class Snippets < ::API::Base
include PaginationParams
- feature_category :snippets
+ feature_category :source_code_management
urgency :low
resource :snippets do
diff --git a/lib/api/support/git_access_actor.rb b/lib/api/support/git_access_actor.rb
index 16861a146ae..7a4e6f3e14c 100644
--- a/lib/api/support/git_access_actor.rb
+++ b/lib/api/support/git_access_actor.rb
@@ -16,7 +16,7 @@ module API
def self.from_params(params)
if params[:key_id]
- new(key: Key.find_by_id(params[:key_id]))
+ new(key: Key.auth.find_by_id(params[:key_id]))
elsif params[:user_id]
new(user: UserFinder.new(params[:user_id]).find_by_id)
elsif params[:username]
diff --git a/lib/api/tags.rb b/lib/api/tags.rb
index b412a17bc6f..4ddf22c726f 100644
--- a/lib/api/tags.rb
+++ b/lib/api/tags.rb
@@ -129,6 +129,24 @@ module API
end
end
end
+
+ desc "Get a tag's signature" do
+ success code: 200, model: Entities::TagSignature
+ tags %w[tags]
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ end
+ params do
+ requires :tag_name, type: String, desc: 'The name of the tag'
+ end
+ get ':id/repository/tags/:tag_name/signature', requirements: TAG_ENDPOINT_REQUIREMENTS, feature_category: :source_code_management do
+ tag = user_project.repository.find_tag(params[:tag_name])
+ not_found! 'Tag' unless tag
+ not_found! 'Signature' unless tag.has_signature?
+
+ present tag, with: Entities::TagSignature
+ end
end
end
end
diff --git a/lib/api/terraform/state.rb b/lib/api/terraform/state.rb
index 577d011ebad..bdc9f975970 100644
--- a/lib/api/terraform/state.rb
+++ b/lib/api/terraform/state.rb
@@ -20,6 +20,8 @@ module API
render_api_error!(e.message, 422)
end
+ STATE_NAME_URI_REQUIREMENTS = { name: API::NO_SLASH_URL_PART_REGEX }.freeze
+
before do
authenticate!
authorize! :read_terraform_state, user_project
@@ -45,7 +47,7 @@ module API
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- namespace ':id/terraform/state/:name' do
+ namespace ':id/terraform/state/:name', requirements: STATE_NAME_URI_REQUIREMENTS do
params do
requires :name, type: String, desc: 'The name of a Terraform state'
optional :ID, type: String, limit: 255, desc: 'Terraform state lock ID'
@@ -55,6 +57,17 @@ module API
def remote_state_handler
::Terraform::RemoteStateHandler.new(user_project, current_user, name: params[:name], lock_id: params[:ID])
end
+
+ def not_found_for_dots?
+ Feature.disabled?(:allow_dots_on_tf_state_names) && params[:name].include?(".")
+ end
+
+ # Change the state name to behave like before, https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105674
+ # has been introduced. This behavior can be controlled via `allow_dots_on_tf_state_names` FF.
+ # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106861
+ def legacy_state_name!
+ params[:name] = params[:name].split('.').first
+ end
end
desc 'Get a Terraform state by its name' do
@@ -72,6 +85,8 @@ module API
end
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
get do
+ legacy_state_name! if not_found_for_dots?
+
remote_state_handler.find_with_lock do |state|
no_content! unless state.latest_file && state.latest_file.exists?
@@ -88,17 +103,22 @@ module API
]
failure [
{ code: 403, message: 'Forbidden' },
- { code: 422, message: 'Validation failure' }
+ { code: 422, message: 'Validation failure' },
+ { code: 413, message: 'Request Entity Too Large' }
]
tags %w[terraform_state]
end
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
post do
authorize! :admin_terraform_state, user_project
+ legacy_state_name! if not_found_for_dots?
data = request.body.read
no_content! if data.empty?
+ max_state_size = Gitlab::CurrentSettings.max_terraform_state_size_bytes
+ file_too_large! if max_state_size > 0 && data.size > max_state_size
+
remote_state_handler.handle_with_lock do |state|
state.update_file!(CarrierWaveStringFile.new(data), version: params[:serial], build: current_authenticated_job)
end
@@ -120,6 +140,7 @@ module API
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
delete do
authorize! :admin_terraform_state, user_project
+ legacy_state_name! if not_found_for_dots?
remote_state_handler.find_with_lock do |state|
::Terraform::States::TriggerDestroyService.new(state, current_user: current_user).execute
@@ -151,6 +172,8 @@ module API
requires :Path, type: String, desc: 'Terraform path'
end
post '/lock' do
+ not_found! if not_found_for_dots?
+
authorize! :admin_terraform_state, user_project
status_code = :ok
@@ -194,6 +217,8 @@ module API
optional :ID, type: String, limit: 255, desc: 'Terraform state lock ID'
end
delete '/lock' do
+ not_found! if not_found_for_dots?
+
authorize! :admin_terraform_state, user_project
remote_state_handler.unlock!
diff --git a/lib/api/time_tracking_endpoints.rb b/lib/api/time_tracking_endpoints.rb
index b8323304957..dd8ad2cc144 100644
--- a/lib/api/time_tracking_endpoints.rb
+++ b/lib/api/time_tracking_endpoints.rb
@@ -23,14 +23,12 @@ module API
end
def load_issuable
- @issuable ||= begin
- case issuable_name
- when 'issue'
- find_project_issue(params.delete(issuable_key))
- when 'merge_request'
- find_project_merge_request(params.delete(issuable_key))
- end
- end
+ @issuable ||= case issuable_name
+ when 'issue'
+ find_project_issue(params.delete(issuable_key))
+ when 'merge_request'
+ find_project_merge_request(params.delete(issuable_key))
+ end
end
def update_issuable(attrs)
@@ -54,10 +52,18 @@ module API
issuable_collection_name = issuable_name.pluralize
issuable_key = "#{issuable_name}_iid".to_sym
- desc "Set a time estimate for a project #{issuable_name}"
+ desc "Set a time estimate for a #{issuable_name}" do
+ detail " Sets an estimated time of work for this #{issuable_name}."
+ success Entities::IssuableTimeStats
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags [issuable_collection_name]
+ end
params do
- requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}"
- requires :duration, type: String, desc: 'The duration to be parsed'
+ requires issuable_key, type: Integer, desc: "The internal ID of the #{issuable_name}."
+ requires :duration, type: String, desc: 'The duration in human format.', documentation: { example: '3h30m' }
end
post ":id/#{issuable_collection_name}/:#{issuable_key}/time_estimate" do
authorize! admin_issuable_key, load_issuable
@@ -66,9 +72,17 @@ module API
update_issuable(time_estimate: Gitlab::TimeTrackingFormatter.parse(params.delete(:duration)))
end
- desc "Reset the time estimate for a project #{issuable_name}"
+ desc "Reset the time estimate for a project #{issuable_name}" do
+ detail "Resets the estimated time for this #{issuable_name} to 0 seconds."
+ success Entities::IssuableTimeStats
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags [issuable_collection_name]
+ end
params do
- requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}"
+ requires issuable_key, type: Integer, desc: "The internal ID of the #{issuable_name}."
end
post ":id/#{issuable_collection_name}/:#{issuable_key}/reset_time_estimate" do
authorize! admin_issuable_key, load_issuable
@@ -77,10 +91,18 @@ module API
update_issuable(time_estimate: 0)
end
- desc "Add spent time for a project #{issuable_name}"
+ desc "Add spent time for a #{issuable_name}" do
+ detail "Adds spent time for this #{issuable_name}."
+ success Entities::IssuableTimeStats
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags [issuable_collection_name]
+ end
params do
- requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}"
- requires :duration, type: String, desc: 'The duration to be parsed'
+ requires issuable_key, type: Integer, desc: "The internal ID of the #{issuable_name}."
+ requires :duration, type: String, desc: 'The duration in human format.'
end
post ":id/#{issuable_collection_name}/:#{issuable_key}/add_spent_time" do
authorize! admin_issuable_key, load_issuable
@@ -97,9 +119,17 @@ module API
update_issuable(update_params)
end
- desc "Reset spent time for a project #{issuable_name}"
+ desc "Reset spent time for a #{issuable_name}" do
+ detail "Resets the total spent time for this #{issuable_name} to 0 seconds."
+ success Entities::IssuableTimeStats
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags [issuable_collection_name]
+ end
params do
- requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}"
+ requires issuable_key, type: Integer, desc: "The internal ID of the #{issuable_name}"
end
post ":id/#{issuable_collection_name}/:#{issuable_key}/reset_spent_time" do
authorize! admin_issuable_key, load_issuable
@@ -108,9 +138,17 @@ module API
update_issuable(spend_time: { duration: :reset, user_id: current_user.id })
end
- desc "Show time stats for a project #{issuable_name}"
+ desc "Get time tracking stats" do
+ detail "Get time tracking stats"
+ success Entities::IssuableTimeStats
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags [issuable_collection_name]
+ end
params do
- requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}"
+ requires issuable_key, type: Integer, desc: "The internal ID of the #{issuable_name}"
end
get ":id/#{issuable_collection_name}/:#{issuable_key}/time_stats" do
authorize! read_issuable_key, load_issuable
diff --git a/lib/api/unleash.rb b/lib/api/unleash.rb
index 38ce4bd7f32..b2133dc743a 100644
--- a/lib/api/unleash.rb
+++ b/lib/api/unleash.rb
@@ -63,13 +63,9 @@ module API
cache_context: -> (client) { client.unleash_api_cache_key }
end
- def project
- @project ||= find_project(params[:project_id])
- end
-
def feature_flags_client
strong_memoize(:feature_flags_client) do
- client = Operations::FeatureFlagsClient.find_for_project_and_token(project, unleash_instance_id)
+ client = Operations::FeatureFlagsClient.find_for_project_and_token(params[:project_id], unleash_instance_id)
client.unleash_app_name = unleash_app_name if client
client
end
@@ -86,12 +82,6 @@ module API
def authorize_by_unleash_instance_id!
unauthorized! unless feature_flags_client
end
-
- def feature_flags
- return [] unless unleash_app_name.present?
-
- Operations::FeatureFlag.for_unleash_client(project, unleash_app_name)
- end
end
end
end
diff --git a/lib/api/usage_data.rb b/lib/api/usage_data.rb
index 9e446aff605..3e2023d769f 100644
--- a/lib/api/usage_data.rb
+++ b/lib/api/usage_data.rb
@@ -12,11 +12,18 @@ module API
forbidden!('Invalid CSRF token is provided') unless verified_request?
end
- desc 'Track usage data events' do
+ desc 'Track usage data event' do
detail 'This feature was introduced in GitLab 13.4.'
+ success code: 200
+ failure [
+ { code: 403, message: 'Invalid CSRF token is provided' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[usage_data]
end
params do
- requires :event, type: String, desc: 'The event name that should be tracked'
+ requires :event, type: String, desc: 'The event name that should be tracked',
+ documentation: { example: 'i_quickactions_page' }
end
post 'increment_counter' do
event_name = params[:event]
@@ -26,8 +33,17 @@ module API
status :ok
end
+ desc 'Track usage data event for the current user' do
+ success code: 200
+ failure [
+ { code: 403, message: 'Invalid CSRF token is provided' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[usage_data]
+ end
params do
- requires :event, type: String, desc: 'The event name that should be tracked'
+ requires :event, type: String, desc: 'The event name that should be tracked',
+ documentation: { example: 'i_quickactions_page' }
end
post 'increment_unique_users', urgency: :low do
event_name = params[:event]
@@ -39,6 +55,13 @@ module API
desc 'Get a list of all metric definitions' do
detail 'This feature was introduced in GitLab 13.11.'
+ success code: 200
+ failure [
+ { code: 403, message: 'Invalid CSRF token is provided' },
+ { code: 404, message: 'Not found' }
+ ]
+ produces ['application/yaml']
+ tags %w[usage_data metrics]
end
get 'metric_definitions', urgency: :low do
content_type 'application/yaml'
diff --git a/lib/api/usage_data_non_sql_metrics.rb b/lib/api/usage_data_non_sql_metrics.rb
index 41f369a43b8..81f96a7958b 100644
--- a/lib/api/usage_data_non_sql_metrics.rb
+++ b/lib/api/usage_data_non_sql_metrics.rb
@@ -14,6 +14,12 @@ module API
desc 'Get Non SQL usage ping metrics' do
detail 'This feature was introduced in GitLab 13.11.'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
end
get 'non_sql_metrics' do
diff --git a/lib/api/usage_data_queries.rb b/lib/api/usage_data_queries.rb
index fe972942111..8e85fca4ba9 100644
--- a/lib/api/usage_data_queries.rb
+++ b/lib/api/usage_data_queries.rb
@@ -14,6 +14,12 @@ module API
desc 'Get raw SQL queries for usage data SQL metrics' do
detail 'This feature was introduced in GitLab 13.11.'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
end
get 'queries' do
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 72c121bca03..d2d45c94291 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -133,7 +133,7 @@ module API
get feature_category: :users, urgency: :low do
authenticated_as_admin! if params[:extern_uid].present? && params[:provider].present?
- unless current_user&.admin?
+ unless current_user&.can_read_all_resources?
params.except!(:created_after, :created_before, :order_by, :sort, :two_factor, :without_projects)
end
@@ -151,7 +151,7 @@ module API
users = UsersFinder.new(current_user, params).execute
users = reorder_users(users)
- entity = current_user&.admin? ? Entities::UserWithAdmin : Entities::UserBasic
+ entity = current_user&.can_read_all_resources? ? Entities::UserWithAdmin : Entities::UserBasic
if entity == Entities::UserWithAdmin
users = users.preload(:identities, :u2f_registrations, :webauthn_registrations, :namespace, :followers, :followees, :user_preference)
@@ -177,7 +177,7 @@ module API
get ":id", feature_category: :users, urgency: :low do
forbidden!('Not authorized!') unless current_user
- unless current_user.admin?
+ unless current_user.can_read_all_resources?
check_rate_limit!(:users_get_by_id,
scope: current_user,
users_allowlist: Gitlab::CurrentSettings.current_application_settings.users_get_by_id_limit_allowlist
@@ -188,7 +188,7 @@ module API
not_found!('User') unless user && can?(current_user, :read_user, user)
- opts = { with: current_user.admin? ? Entities::UserDetailsWithAdmin : Entities::User, current_user: current_user }
+ opts = { with: current_user.can_read_all_resources? ? Entities::UserDetailsWithAdmin : Entities::User, current_user: current_user }
user, opts = with_custom_attributes(user, opts)
present user, opts
@@ -333,12 +333,12 @@ module API
not_found!('User') unless user
conflict!('Email has already been taken') if params[:email] &&
- User.by_any_email(params[:email].downcase)
- .where.not(id: user.id).exists?
+ User.by_any_email(params[:email].downcase)
+ .where.not(id: user.id).exists?
conflict!('Username has already been taken') if params[:username] &&
- User.by_username(params[:username])
- .where.not(id: user.id).exists?
+ User.by_username(params[:username])
+ .where.not(id: user.id).exists?
user_params = declared_params(include_missing: false)
admin_making_changes_for_another_user = (current_user != user)
@@ -373,7 +373,8 @@ module API
user = User.find_by_id(params[:id])
not_found!('User') unless user
- forbidden!('Two-factor authentication for admins cannot be disabled via the API. Use the Rails console') if user.admin?
+ # We're disabling Cop/UserAdmin because it checks if the given user (not the current user) is an admin.
+ forbidden!('Two-factor authentication for admins cannot be disabled via the API. Use the Rails console') if user.admin? # rubocop:disable Cop/UserAdmin
result = TwoFactor::DestroyService.new(current_user, user: user).execute
@@ -437,6 +438,8 @@ module API
requires :key, type: String, desc: 'The new SSH key'
requires :title, type: String, desc: 'The title of the new SSH key'
optional :expires_at, type: DateTime, desc: 'The expiration date of the SSH key in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)'
+ optional :usage_type, type: String, values: Key.usage_types.keys, default: 'auth_and_signing',
+ desc: 'Scope of usage for the SSH key'
end
# rubocop: disable CodeReuse/ActiveRecord
post ":user_id/keys", feature_category: :authentication_and_authorization do
@@ -1006,7 +1009,8 @@ module API
end
get feature_category: :users, urgency: :low do
entity =
- if current_user.admin?
+ # We're disabling Cop/UserAdmin because it checks if the given user is an admin.
+ if current_user.admin? # rubocop:disable Cop/UserAdmin
Entities::UserWithAdmin
else
Entities::UserPublic
@@ -1050,6 +1054,8 @@ module API
requires :key, type: String, desc: 'The new SSH key'
requires :title, type: String, desc: 'The title of the new SSH key'
optional :expires_at, type: DateTime, desc: 'The expiration date of the SSH key in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)'
+ optional :usage_type, type: String, values: Key.usage_types.keys, default: 'auth_and_signing',
+ desc: 'Scope of usage for the SSH key'
end
post "keys", feature_category: :authentication_and_authorization do
key = ::Keys::CreateService.new(current_user, declared_params(include_missing: false)).execute
diff --git a/lib/api/v3/github.rb b/lib/api/v3/github.rb
index e4a26838746..db71e823b1d 100644
--- a/lib/api/v3/github.rb
+++ b/lib/api/v3/github.rb
@@ -78,13 +78,13 @@ module API
# rubocop: enable CodeReuse/ActiveRecord
def authorized_merge_requests
- MergeRequestsFinder.new(current_user, authorized_only: !current_user.admin?)
+ MergeRequestsFinder.new(current_user, authorized_only: !current_user.can_read_all_resources?)
.execute.with_jira_integration_associations
end
def authorized_merge_requests_for_project(project)
MergeRequestsFinder
- .new(current_user, authorized_only: !current_user.admin?, project_id: project.id)
+ .new(current_user, authorized_only: !current_user.can_read_all_resources?, project_id: project.id)
.execute.with_jira_integration_associations
end
diff --git a/lib/api/validations/validators/array_none_any.rb b/lib/api/validations/validators/array_none_any.rb
index 3732c1f575c..8c064eefbf2 100644
--- a/lib/api/validations/validators/array_none_any.rb
+++ b/lib/api/validations/validators/array_none_any.rb
@@ -8,7 +8,7 @@ module API
value = params[attr_name]
return if value.is_a?(Array) ||
- [IssuableFinder::Params::FILTER_NONE, IssuableFinder::Params::FILTER_ANY].include?(value.to_s.downcase)
+ [IssuableFinder::Params::FILTER_NONE, IssuableFinder::Params::FILTER_ANY].include?(value.to_s.downcase)
raise Grape::Exceptions::Validation.new(
params: [@scope.full_name(attr_name)],
diff --git a/lib/assets/images/bot_avatars/admin-bot.png b/lib/assets/images/bot_avatars/admin-bot.png
new file mode 100644
index 00000000000..8418676d9b1
--- /dev/null
+++ b/lib/assets/images/bot_avatars/admin-bot.png
Binary files differ
diff --git a/lib/atlassian/jira_connect.rb b/lib/atlassian/jira_connect.rb
index 595cf0ac465..3ebed08280a 100644
--- a/lib/atlassian/jira_connect.rb
+++ b/lib/atlassian/jira_connect.rb
@@ -17,8 +17,16 @@ module Atlassian
private
def gitlab_host
+ return host_from_settings if Gitlab::CurrentSettings.jira_connect_proxy_url?
+
Gitlab.config.gitlab.host
end
+
+ def host_from_settings
+ uri = URI(Gitlab::CurrentSettings.jira_connect_proxy_url)
+
+ uri.hostname + uri.path
+ end
end
end
end
diff --git a/lib/atlassian/jira_connect/client.rb b/lib/atlassian/jira_connect/client.rb
index 3c999920d39..96e285f4eea 100644
--- a/lib/atlassian/jira_connect/client.rb
+++ b/lib/atlassian/jira_connect/client.rb
@@ -76,7 +76,7 @@ module Atlassian
return if items.empty?
r = post('/rest/deployments/0.1/bulk', { deployments: items })
- handle_response(r, 'deployments') { |data| errors(data, 'rejectedDeployments') }
+ handle_response(r, 'deployments') { |data| errors(data, 'rejectedDeployments', r) }
end
def store_build_info(project:, pipelines:, update_sequence_id: nil)
@@ -92,7 +92,7 @@ module Atlassian
return if builds.empty?
r = post('/rest/builds/0.1/bulk', { builds: builds })
- handle_response(r, 'builds') { |data| errors(data, 'rejectedBuilds') }
+ handle_response(r, 'builds') { |data| errors(data, 'rejectedBuilds', r) }
end
def store_dev_info(project:, commits: nil, branches: nil, merge_requests: nil, update_sequence_id: nil)
@@ -132,22 +132,20 @@ module Atlassian
if [200, 202].include?(response.code)
yield data
else
- message = case response.code
- when 400 then { 'errorMessages' => data.map { |e| e['message'] } }
- when 401 then { 'errorMessages' => ['Invalid JWT'] }
- when 403 then { 'errorMessages' => ["App does not support #{name}"] }
- when 413 then { 'errorMessages' => ['Data too large'] + data.map { |e| e['message'] } }
- when 429 then { 'errorMessages' => ['Rate limit exceeded'] }
- when 503 then { 'errorMessages' => ['Service unavailable'] }
- else
- { 'errorMessages' => ['Unknown error'], 'response' => data }
- end
-
- message.merge('responseCode' => response.code)
+ case response.code
+ when 400 then { 'errorMessages' => data.map { |e| e['message'] } }
+ when 401 then { 'errorMessages' => ['Invalid JWT'] }
+ when 403 then { 'errorMessages' => ["App does not support #{name}"] }
+ when 413 then { 'errorMessages' => ['Data too large'] + data.map { |e| e['message'] } }
+ when 429 then { 'errorMessages' => ['Rate limit exceeded'] }
+ when 503 then { 'errorMessages' => ['Service unavailable'] }
+ else
+ { 'errorMessages' => ['Unknown error'], 'response' => data }
+ end.merge('responseCode' => response.code)
end
end
- def errors(data, key)
+ def errors(data, key, response)
messages = if data[key].present?
data[key].flat_map do |rejection|
rejection['errors'].map { |e| e['message'] }
@@ -156,7 +154,13 @@ module Atlassian
[]
end
- { 'errorMessages' => messages }
+ { 'errorMessages' => messages, 'responseCode' => response.code, 'requestBody' => request_body_schema(response) }
+ end
+
+ def request_body_schema(response)
+ Oj.load(response.request.raw_body).deep_transform_values! {}
+ rescue Oj::ParseError, EncodingError, Encoding::UndefinedConversionError
+ 'Request body includes invalid JSON'
end
def user_notes_count(merge_requests)
diff --git a/lib/atlassian/jira_connect/jwt/asymmetric.rb b/lib/atlassian/jira_connect/jwt/asymmetric.rb
index 573a8022752..7c1cf1cabb6 100644
--- a/lib/atlassian/jira_connect/jwt/asymmetric.rb
+++ b/lib/atlassian/jira_connect/jwt/asymmetric.rb
@@ -77,11 +77,7 @@ module Atlassian
end
def public_key_cdn_url
- if public_key_cdn_url_setting.blank? || Feature.disabled?(:jira_connect_oauth_self_managed)
- return DEFAULT_PUBLIC_KEY_CDN_URL
- end
-
- public_key_cdn_url_setting
+ public_key_cdn_url_setting.presence || DEFAULT_PUBLIC_KEY_CDN_URL
end
def public_key_cdn_url_setting
diff --git a/lib/atlassian/jira_connect/serializers/build_entity.rb b/lib/atlassian/jira_connect/serializers/build_entity.rb
index 10e4bb0e709..aa864cb268f 100644
--- a/lib/atlassian/jira_connect/serializers/build_entity.rb
+++ b/lib/atlassian/jira_connect/serializers/build_entity.rb
@@ -24,12 +24,10 @@ module Atlassian
def issue_keys
# extract Jira issue keys from either the source branch/ref or the
# merge request title.
- @issue_keys ||= begin
- pipeline.all_merge_requests.flat_map do |mr|
- src = "#{mr.source_branch} #{mr.title} #{mr.description}"
- JiraIssueKeyExtractor.new(src).issue_keys
- end.uniq
- end
+ @issue_keys ||= pipeline.all_merge_requests.flat_map do |mr|
+ src = "#{mr.source_branch} #{mr.title} #{mr.description}"
+ JiraIssueKeyExtractor.new(src).issue_keys
+ end.uniq
end
private
diff --git a/lib/backup/files.rb b/lib/backup/files.rb
index 55b10c008fb..9b019f16ddd 100644
--- a/lib/backup/files.rb
+++ b/lib/backup/files.rb
@@ -157,7 +157,7 @@ module Backup
end
def backup_files_realpath
- @backup_files_realpath ||= File.join(Gitlab.config.backup.path, File.basename(@app_files_dir) )
+ @backup_files_realpath ||= File.join(Gitlab.config.backup.path, File.basename(@app_files_dir))
end
end
end
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index a8b3c12a2a2..f8424f6250e 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -218,7 +218,7 @@ module Backup
build_backup_information
- definitions.keys.each do |task_name|
+ definitions.each_key do |task_name|
run_create_task(task_name)
end
@@ -239,7 +239,7 @@ module Backup
read_backup_information
verify_backup_version
- definitions.keys.each do |task_name|
+ definitions.each_key do |task_name|
if !skipped?(task_name) && enabled_task?(task_name)
run_restore_task(task_name)
end
@@ -263,7 +263,7 @@ module Backup
def write_backup_information
# Make sure there is a connection
- ::Gitlab::Database.database_base_models.values.each do |base_model|
+ ::Gitlab::Database.database_base_models.each_value do |base_model|
base_model.connection.reconnect!
end
@@ -277,7 +277,7 @@ module Backup
def build_backup_information
@backup_information ||= {
db_version: ActiveRecord::Migrator.current_version.to_s,
- backup_created_at: Time.now,
+ backup_created_at: Time.current,
gitlab_version: Gitlab::VERSION,
tar_version: tar_version,
installation_type: Gitlab::INSTALLATION_TYPE,
@@ -291,7 +291,7 @@ module Backup
@backup_information.merge!(
full_backup_id: full_backup_id,
db_version: ActiveRecord::Migrator.current_version.to_s,
- backup_created_at: Time.zone.now,
+ backup_created_at: Time.current,
gitlab_version: Gitlab::VERSION,
tar_version: tar_version,
installation_type: Gitlab::INSTALLATION_TYPE,
@@ -396,7 +396,7 @@ module Backup
timestamp = matched[1].to_i
- next unless Time.at(timestamp) < (Time.now - keep_time)
+ next unless Time.zone.at(timestamp) < (Time.current - keep_time)
begin
FileUtils.rm(file)
@@ -523,9 +523,7 @@ module Backup
end
def object_storage_config
- @object_storage_config ||= begin
- ObjectStorage::Config.new(Gitlab.config.backup.upload)
- end
+ @object_storage_config ||= ObjectStorage::Config.new(Gitlab.config.backup.upload)
end
def connect_to_remote_directory
@@ -613,7 +611,7 @@ module Backup
end
def puts_time(msg)
- progress.puts "#{Time.now} -- #{msg}"
+ progress.puts "#{Time.current} -- #{msg}"
Gitlab::BackupLogger.info(message: "#{Rainbow.uncolor(msg)}")
end
end
diff --git a/lib/banzai/filter/attributes_filter.rb b/lib/banzai/filter/attributes_filter.rb
new file mode 100644
index 00000000000..ab50b3d6858
--- /dev/null
+++ b/lib/banzai/filter/attributes_filter.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+module Banzai
+ module Filter
+ # Looks for attributes that are specified for an element. Follows the basic syntax laid out
+ # in https://github.com/jgm/commonmark-hs/blob/master/commonmark-extensions/test/attributes.md
+ # For example,
+ # ![](http://example.com/image.jpg){width=50%}
+ #
+ # However we currently have the following limitations:
+ # - only support images
+ # - only support the `width` and `height` attributes
+ # - attributes can not span multiple lines
+ # - unsupported attributes are thrown away
+ class AttributesFilter < HTML::Pipeline::Filter
+ CSS = 'img'
+ XPATH = Gitlab::Utils::Nokogiri.css_to_xpath(CSS).freeze
+
+ ATTRIBUTES_PATTERN = %r{\A(?<matched>\{(?<attributes>.{1,100})\})}.freeze
+ WIDTH_HEIGHT_REGEX = %r{\A(?<name>height|width)="?(?<size>[\w%]{1,10})"?\z}.freeze
+ VALID_SIZE_REGEX = %r{\A\d{1,4}(%|px)?\z}.freeze
+
+ def call
+ doc.xpath(XPATH).each do |img|
+ sibling = img.next
+ next unless sibling && sibling.text? && sibling.content.first == '{'
+
+ match = sibling.content.match(ATTRIBUTES_PATTERN)
+ next unless match && match[:attributes]
+
+ match[:attributes].split(' ').each do |attribute|
+ next unless attribute.match?(WIDTH_HEIGHT_REGEX)
+
+ attribute_match = attribute.match(WIDTH_HEIGHT_REGEX)
+ img[attribute_match[:name].to_sym] = attribute_match[:size] if valid_size?(attribute_match[:size])
+ end
+
+ sibling.content = sibling.content.sub(match[:matched], '')
+ end
+
+ doc
+ end
+
+ private
+
+ def valid_size?(size)
+ size.match?(VALID_SIZE_REGEX)
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/inline_observability_filter.rb b/lib/banzai/filter/inline_observability_filter.rb
new file mode 100644
index 00000000000..27b89073a0e
--- /dev/null
+++ b/lib/banzai/filter/inline_observability_filter.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Banzai
+ module Filter
+ class InlineObservabilityFilter < ::Banzai::Filter::InlineEmbedsFilter
+ # Placeholder element for the frontend to use as an
+ # injection point for observability.
+ def create_element(url)
+ doc.document.create_element(
+ 'div',
+ class: 'js-render-observability',
+ 'data-frame-url': url
+ )
+ end
+
+ # Search params for selecting observability links.
+ def xpath_search
+ "descendant-or-self::a[starts-with(@href, '#{Gitlab::Observability.observability_url}')]"
+ end
+
+ # Creates a new element based on the parameters
+ # obtained from the target link
+ def element_to_embed(node)
+ url = node['href']
+
+ create_element(url)
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/repository_link_filter.rb b/lib/banzai/filter/repository_link_filter.rb
index e95da735647..86beeae01b7 100644
--- a/lib/banzai/filter/repository_link_filter.rb
+++ b/lib/banzai/filter/repository_link_filter.rb
@@ -60,7 +60,7 @@ module Banzai
def get_uri_types(paths)
return {} if paths.empty?
- uri_types = paths.to_h { |name| [name, nil] }
+ uri_types = paths.index_with { nil }
get_blob_types(paths).each do |name, type|
if type == :blob
diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb
index fe189b1b0c9..de9b846efe9 100644
--- a/lib/banzai/filter/sanitization_filter.rb
+++ b/lib/banzai/filter/sanitization_filter.rb
@@ -10,6 +10,8 @@ module Banzai
TABLE_ALIGNMENT_PATTERN = /text-align: (?<alignment>center|left|right)/.freeze
def customize_allowlist(allowlist)
+ allowlist[:allow_comments] = context[:allow_comments]
+
# Allow table alignment; we allow specific text-align values in a
# transformer below
allowlist[:attributes]['th'] = %w[style]
diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index 766715d9e39..489b4d21300 100644
--- a/lib/banzai/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -9,7 +9,7 @@ module Banzai
module Filter
# HTML Filter to highlight fenced code blocks
#
- class SyntaxHighlightFilter < HTML::Pipeline::Filter
+ class SyntaxHighlightFilter < TimeoutHtmlPipelineFilter
include OutputSafety
LANG_PARAMS_DELIMITER = ':'
@@ -19,7 +19,7 @@ module Banzai
CSS = 'pre:not([data-kroki-style]) > code:only-child'
XPATH = Gitlab::Utils::Nokogiri.css_to_xpath(CSS).freeze
- def call
+ def call_with_timeout
doc.xpath(XPATH).each do |node|
highlight_node(node)
end
diff --git a/lib/banzai/filter/timeout_html_pipeline_filter.rb b/lib/banzai/filter/timeout_html_pipeline_filter.rb
new file mode 100644
index 00000000000..b9b71163ab1
--- /dev/null
+++ b/lib/banzai/filter/timeout_html_pipeline_filter.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module Banzai
+ module Filter
+ # HTML Filter that wraps a filter in a Gitlab::RenderTimeout.
+ # This way partial results can be returned, and the entire pipeline
+ # is not killed.
+ #
+ # This should not be used for any filter that must be allowed to complete,
+ # like a `ReferenceRedactorFilter`
+ #
+ class TimeoutHtmlPipelineFilter < HTML::Pipeline::Filter
+ RENDER_TIMEOUT = 10.seconds
+
+ def call
+ Gitlab::RenderTimeout.timeout(foreground: RENDER_TIMEOUT) { call_with_timeout }
+ rescue Timeout::Error => e
+ class_name = self.class.name.demodulize
+ timeout_counter.increment(source: class_name)
+ Gitlab::ErrorTracking.track_exception(e, project_id: context[:project]&.id, class_name: class_name)
+
+ # we've timed out, but some work may have already been completed,
+ # so go ahead and return the document
+ doc
+ end
+
+ def call_with_timeout
+ raise NotImplementedError
+ end
+
+ private
+
+ def timeout_counter
+ Gitlab::Metrics.counter(:banzai_filter_timeouts_total, 'Count of the Banzai filters that time out')
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/ascii_doc_pipeline.rb b/lib/banzai/pipeline/ascii_doc_pipeline.rb
index afd5802de22..8764367426c 100644
--- a/lib/banzai/pipeline/ascii_doc_pipeline.rb
+++ b/lib/banzai/pipeline/ascii_doc_pipeline.rb
@@ -13,7 +13,7 @@ module Banzai
Filter::ImageLazyLoadFilter,
Filter::ImageLinkFilter,
Filter::WikiLinkFilter,
- Filter::SyntaxHighlightFilter,
+ Filter::SyntaxHighlightFilter, # this filter should remain next to last
Filter::AsciiDocPostProcessingFilter
]
end
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index 9b73e413d44..73065571849 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -22,6 +22,7 @@ module Banzai
Filter::MermaidFilter,
Filter::VideoLinkFilter,
Filter::AudioLinkFilter,
+ Filter::AttributesFilter,
Filter::ImageLazyLoadFilter,
Filter::ImageLinkFilter,
*metrics_filters,
@@ -36,8 +37,9 @@ module Banzai
Filter::CustomEmojiFilter,
Filter::TaskListFilter,
Filter::InlineDiffFilter,
+ Filter::InlineObservabilityFilter,
Filter::SetDirectionFilter,
- Filter::SyntaxHighlightFilter
+ Filter::SyntaxHighlightFilter # this filter should remain at the end
]
end
diff --git a/lib/banzai/pipeline/markup_pipeline.rb b/lib/banzai/pipeline/markup_pipeline.rb
index 330914f7238..635d4c0884e 100644
--- a/lib/banzai/pipeline/markup_pipeline.rb
+++ b/lib/banzai/pipeline/markup_pipeline.rb
@@ -10,7 +10,7 @@ module Banzai
Filter::ExternalLinkFilter,
Filter::PlantumlFilter,
Filter::KrokiFilter,
- Filter::SyntaxHighlightFilter
+ Filter::SyntaxHighlightFilter # this filter should remain at the end
]
end
diff --git a/lib/banzai/pipeline/wiki_pipeline.rb b/lib/banzai/pipeline/wiki_pipeline.rb
index caba9570ab9..96b6b9699f4 100644
--- a/lib/banzai/pipeline/wiki_pipeline.rb
+++ b/lib/banzai/pipeline/wiki_pipeline.rb
@@ -4,10 +4,8 @@ module Banzai
module Pipeline
class WikiPipeline < FullPipeline
def self.filters
- @filters ||= begin
- super.insert_before(Filter::ImageLazyLoadFilter, Filter::GollumTagsFilter)
+ @filters ||= super.insert_before(Filter::ImageLazyLoadFilter, Filter::GollumTagsFilter)
.insert_before(Filter::TaskListFilter, Filter::WikiLinkFilter)
- end
end
end
end
diff --git a/lib/banzai/reference_parser/alert_parser.rb b/lib/banzai/reference_parser/alert_parser.rb
index 7b864d26f67..676c0ac40ef 100644
--- a/lib/banzai/reference_parser/alert_parser.rb
+++ b/lib/banzai/reference_parser/alert_parser.rb
@@ -5,14 +5,18 @@ module Banzai
class AlertParser < BaseParser
self.reference_type = :alert
+ def self.reference_class
+ AlertManagement::Alert
+ end
+
def references_relation
AlertManagement::Alert
end
private
- def can_read_reference?(user, alert, node)
- can?(user, :read_alert_management_alert, alert)
+ def can_read_reference?(user, project, node)
+ can?(user, :read_alert_management_alert, project)
end
end
end
diff --git a/lib/banzai/reference_parser/base_parser.rb b/lib/banzai/reference_parser/base_parser.rb
index 19d91876892..88ce63a63fe 100644
--- a/lib/banzai/reference_parser/base_parser.rb
+++ b/lib/banzai/reference_parser/base_parser.rb
@@ -47,6 +47,14 @@ module Banzai
@data_attribute ||= "data-#{reference_type.to_s.dasherize}"
end
+ # Returns a model class to use as a reference.
+ # By default, the method does not take namespaces into account,
+ # thus parser classes can customize the reference class to use
+ # a model name with a namespace
+ def self.reference_class
+ reference_type.to_s.classify.constantize
+ end
+
# context - An instance of `Banzai::RenderContext`.
def initialize(context)
@context = context
diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb
index fbd451efb23..845acf034a5 100644
--- a/lib/bitbucket_server/connection.rb
+++ b/lib/bitbucket_server/connection.rb
@@ -15,6 +15,7 @@ module BitbucketServer
Errno::EHOSTUNREACH,
Net::OpenTimeout,
Net::ReadTimeout,
+ URI::InvalidURIError,
Gitlab::HTTP::BlockedUrlError
].freeze
diff --git a/lib/bulk_imports/clients/http.rb b/lib/bulk_imports/clients/http.rb
index 4c36f226006..8129ff6151c 100644
--- a/lib/bulk_imports/clients/http.rb
+++ b/lib/bulk_imports/clients/http.rb
@@ -55,20 +55,11 @@ module BulkImports
end
def instance_version
- strong_memoize(:instance_version) do
- response = begin
- with_error_handling do
- Gitlab::HTTP.get(resource_url(:version), default_options)
- end
- rescue BulkImports::NetworkError
- # `version` endpoint is not available, try `metadata` endpoint instead
- with_error_handling do
- Gitlab::HTTP.get(resource_url(:metadata), default_options)
- end
- end
+ Gitlab::VersionInfo.parse(metadata['version'])
+ end
- Gitlab::VersionInfo.parse(response.parsed_response['version'])
- end
+ def instance_enterprise
+ Gitlab::Utils.to_boolean(metadata['enterprise'], default: true)
end
def compatible_for_project_migration?
@@ -87,6 +78,22 @@ module BulkImports
end
end
+ def metadata
+ response = begin
+ with_error_handling do
+ Gitlab::HTTP.get(resource_url(:version), default_options)
+ end
+ rescue BulkImports::NetworkError
+ # `version` endpoint is not available, try `metadata` endpoint instead
+ with_error_handling do
+ Gitlab::HTTP.get(resource_url(:metadata), default_options)
+ end
+ end
+
+ response.parsed_response
+ end
+ strong_memoize_attr :metadata
+
# rubocop:disable GitlabSecurity/PublicSend
def request(method, resource, options = {}, &block)
validate_instance_version!
diff --git a/lib/bulk_imports/common/pipelines/uploads_pipeline.rb b/lib/bulk_imports/common/pipelines/uploads_pipeline.rb
index d7b9d6920ea..a1b338aeb9f 100644
--- a/lib/bulk_imports/common/pipelines/uploads_pipeline.rb
+++ b/lib/bulk_imports/common/pipelines/uploads_pipeline.rb
@@ -21,15 +21,16 @@ module BulkImports
end
def load(context, file_path)
- avatar_path = AVATAR_PATTERN.match(file_path)
+ # Validate that the path is OK to load
+ Gitlab::Utils.check_allowed_absolute_path_and_path_traversal!(file_path, [Dir.tmpdir])
+ return if File.directory?(file_path)
+ return if File.lstat(file_path).symlink?
+ avatar_path = AVATAR_PATTERN.match(file_path)
return save_avatar(file_path) if avatar_path
dynamic_path = file_uploader.extract_dynamic_path(file_path)
-
return unless dynamic_path
- return if File.directory?(file_path)
- return if File.lstat(file_path).symlink?
named_captures = dynamic_path.named_captures.symbolize_keys
diff --git a/lib/bulk_imports/groups/stage.rb b/lib/bulk_imports/groups/stage.rb
index 6928ce43191..0db2b1f0698 100644
--- a/lib/bulk_imports/groups/stage.rb
+++ b/lib/bulk_imports/groups/stage.rb
@@ -21,7 +21,7 @@ module BulkImports
# instance version is 15.2.0, 15.2.1, 16.0.0, etc.
def config
- @config ||= {
+ {
group: {
pipeline: BulkImports::Groups::Pipelines::GroupPipeline,
stage: 0
diff --git a/lib/bulk_imports/pipeline.rb b/lib/bulk_imports/pipeline.rb
index 54d5d3209f8..681d6e9aad6 100644
--- a/lib/bulk_imports/pipeline.rb
+++ b/lib/bulk_imports/pipeline.rb
@@ -13,6 +13,7 @@ module BulkImports
CACHE_KEY_EXPIRATION = 2.hours
NDJSON_EXPORT_TIMEOUT = 90.minutes
+ EMPTY_EXPORT_STATUS_TIMEOUT = 5.minutes
def initialize(context)
@context = context
diff --git a/lib/bulk_imports/projects/stage.rb b/lib/bulk_imports/projects/stage.rb
index 2fefdb9055e..73e102696fa 100644
--- a/lib/bulk_imports/projects/stage.rb
+++ b/lib/bulk_imports/projects/stage.rb
@@ -21,7 +21,7 @@ module BulkImports
# instance version is 15.2.0, 15.2.1, 16.0.0, etc.
def config
- @config ||= {
+ {
project: {
pipeline: BulkImports::Projects::Pipelines::ProjectPipeline,
stage: 0
diff --git a/lib/bulk_imports/stage.rb b/lib/bulk_imports/stage.rb
index b45ac139385..5c622db3b01 100644
--- a/lib/bulk_imports/stage.rb
+++ b/lib/bulk_imports/stage.rb
@@ -19,6 +19,8 @@ module BulkImports
private
+ attr_reader :bulk_import, :bulk_import_entity
+
def config
# To be implemented in a sub-class
NotImplementedError
diff --git a/lib/extracts_ref.rb b/lib/extracts_ref.rb
index 9799116038e..f22996df0a5 100644
--- a/lib/extracts_ref.rb
+++ b/lib/extracts_ref.rb
@@ -64,10 +64,16 @@ module ExtractsRef
def assign_ref_vars
@id, @ref, @path = extract_ref_path
@repo = repository_container.repository
-
raise InvalidPathError if @ref.match?(/\s/)
- @commit = @repo.commit(@ref) if @ref.present?
+ return unless @ref.present?
+
+ @commit = if ref_type && Feature.enabled?(:use_ref_type_parameter, @repo.project)
+ @fully_qualified_ref = %(refs/#{ref_type}/#{@ref})
+ @repo.commit(@fully_qualified_ref)
+ else
+ @repo.commit(@ref)
+ end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
@@ -82,6 +88,12 @@ module ExtractsRef
[id, ref, path]
end
+ def ref_type
+ return unless params[:ref_type].present?
+
+ params[:ref_type] == 'tags' ? 'tags' : 'heads'
+ end
+
private
def extract_raw_ref(id)
diff --git a/lib/feature.rb b/lib/feature.rb
index 5841828da0e..d012639d489 100644
--- a/lib/feature.rb
+++ b/lib/feature.rb
@@ -16,6 +16,16 @@ module Feature
end
end
+ class OptOut
+ def initialize(inner)
+ @inner = inner
+ end
+
+ def flipper_id
+ "#{@inner.flipper_id}:opt_out"
+ end
+ end
+
class FlipperGate < Flipper::Adapters::ActiveRecord::Gate
superclass.table_name = 'feature_gates'
end
@@ -25,6 +35,7 @@ module Feature
end
InvalidFeatureFlagError = Class.new(Exception) # rubocop:disable Lint/InheritException
+ InvalidOperation = Class.new(ArgumentError) # rubocop:disable Lint/InheritException
class << self
delegate :group, to: :flipper
@@ -78,7 +89,7 @@ module Feature
# and should not be used outside of Gitaly's `lib/feature/gitaly.rb`
def enabled?(key, thing = nil, type: :development, default_enabled_if_undefined: nil)
if check_feature_flags_definition?
- if thing && !thing.respond_to?(:flipper_id)
+ if thing && !thing.respond_to?(:flipper_id) && !thing.is_a?(Flipper::Types::Group)
raise InvalidFeatureFlagError,
"The thing '#{thing.class.name}' for feature flag '#{key}' needs to include `FeatureGate` or implement `flipper_id`"
end
@@ -87,10 +98,7 @@ module Feature
end
default_enabled = Feature::Definition.default_enabled?(key, default_enabled_if_undefined: default_enabled_if_undefined)
-
- feature_value = with_feature(key) do |feature|
- feature_value = current_feature_value(feature, thing, default_enabled: default_enabled)
- end
+ feature_value = current_feature_value(key, thing, default_enabled: default_enabled)
# If not yielded, then either recursion is happening, or the database does not exist yet, so use default_enabled.
feature_value = default_enabled if feature_value.nil?
@@ -108,6 +116,7 @@ module Feature
def enable(key, thing = true)
log(key: key, action: __method__, thing: thing)
+
return_value = with_feature(key) { _1.enable(thing) }
# rubocop:disable Gitlab/RailsLogger
@@ -120,12 +129,45 @@ module Feature
def disable(key, thing = false)
log(key: key, action: __method__, thing: thing)
+
with_feature(key) { _1.disable(thing) }
end
+ def opted_out?(key, thing)
+ return false unless thing.respond_to?(:flipper_id) # Ignore Feature::Types::Group
+ return false unless persisted_name?(key)
+
+ opt_out = OptOut.new(thing)
+
+ with_feature(key) { _1.actors_value.include?(opt_out.flipper_id) }
+ end
+
+ def opt_out(key, thing)
+ return unless thing.respond_to?(:flipper_id) # Ignore Feature::Types::Group
+
+ log(key: key, action: __method__, thing: thing)
+ opt_out = OptOut.new(thing)
+
+ with_feature(key) { _1.enable(opt_out) }
+ end
+
+ def remove_opt_out(key, thing)
+ return unless thing.respond_to?(:flipper_id) # Ignore Feature::Types::Group
+ return unless persisted_name?(key)
+
+ log(key: key, action: __method__, thing: thing)
+ opt_out = OptOut.new(thing)
+
+ with_feature(key) { _1.disable(opt_out) }
+ end
+
def enable_percentage_of_time(key, percentage)
log(key: key, action: __method__, percentage: percentage)
- with_feature(key) { _1.enable_percentage_of_time(percentage) }
+ with_feature(key) do |flag|
+ raise InvalidOperation, 'Cannot enable percentage of time for a fully-enabled flag' if flag.state == :on
+
+ flag.enable_percentage_of_time(percentage)
+ end
end
def disable_percentage_of_time(key)
@@ -135,7 +177,11 @@ module Feature
def enable_percentage_of_actors(key, percentage)
log(key: key, action: __method__, percentage: percentage)
- with_feature(key) { _1.enable_percentage_of_actors(percentage) }
+ with_feature(key) do |flag|
+ raise InvalidOperation, 'Cannot enable percentage of actors for a fully-enabled flag' if flag.state == :on
+
+ flag.enable_percentage_of_actors(percentage)
+ end
end
def disable_percentage_of_actors(key)
@@ -147,6 +193,7 @@ module Feature
return unless persisted_name?(key)
log(key: key, action: __method__)
+
with_feature(key, &:remove)
end
@@ -189,14 +236,26 @@ module Feature
private
+ # Compute if thing is enabled, taking opt-out overrides into account
# Evaluate if `default enabled: false` or the feature has been persisted.
# `persisted_name?` can potentially generate DB queries and also checks for inclusion
# in an array of feature names (177 at last count), possibly reducing performance by half.
# So we only perform the `persisted` check if `default_enabled: true`
- def current_feature_value(feature, thing, default_enabled:)
- return true if default_enabled && !Feature.persisted_name?(feature.name)
-
- feature.enabled?(thing)
+ def current_feature_value(key, thing, default_enabled:)
+ with_feature(key) do |feature|
+ if default_enabled && !Feature.persisted_name?(feature.name)
+ true
+ else
+ enabled = feature.enabled?(thing)
+
+ if enabled && !thing.nil?
+ opt_out = OptOut.new(thing)
+ feature.actors_value.exclude?(opt_out.flipper_id)
+ else
+ enabled
+ end
+ end
+ end
end
# NOTE: it is not safe to call `Flipper::Feature#enabled?` outside the block
@@ -292,7 +351,7 @@ module Feature
end
class Target
- UnknowTargetError = Class.new(StandardError)
+ UnknownTargetError = Class.new(StandardError)
attr_reader :params
@@ -322,7 +381,7 @@ module Feature
return unless params.key?(:user)
params[:user].split(',').map do |arg|
- UserFinder.new(arg).find_by_username || (raise UnknowTargetError, "#{arg} is not found!")
+ UserFinder.new(arg).find_by_username || (raise UnknownTargetError, "#{arg} is not found!")
end
end
@@ -330,7 +389,7 @@ module Feature
return unless params.key?(:project)
params[:project].split(',').map do |arg|
- Project.find_by_full_path(arg) || (raise UnknowTargetError, "#{arg} is not found!")
+ Project.find_by_full_path(arg) || (raise UnknownTargetError, "#{arg} is not found!")
end
end
@@ -338,7 +397,7 @@ module Feature
return unless params.key?(:group)
params[:group].split(',').map do |arg|
- Group.find_by_full_path(arg) || (raise UnknowTargetError, "#{arg} is not found!")
+ Group.find_by_full_path(arg) || (raise UnknownTargetError, "#{arg} is not found!")
end
end
@@ -347,7 +406,7 @@ module Feature
params[:namespace].split(',').map do |arg|
# We are interested in Group or UserNamespace
- Namespace.without_project_namespaces.find_by_full_path(arg) || (raise UnknowTargetError, "#{arg} is not found!")
+ Namespace.without_project_namespaces.find_by_full_path(arg) || (raise UnknownTargetError, "#{arg} is not found!")
end
end
@@ -356,7 +415,7 @@ module Feature
params[:repository].split(',').map do |arg|
container, _project, _type, _path = Gitlab::RepoPath.parse(arg)
- raise UnknowTargetError, "#{arg} is not found!" if container.nil?
+ raise UnknownTargetError, "#{arg} is not found!" if container.nil?
container.repository
end
diff --git a/lib/feature/definition.rb b/lib/feature/definition.rb
index 270bf46221d..2bad7cfd33d 100644
--- a/lib/feature/definition.rb
+++ b/lib/feature/definition.rb
@@ -7,6 +7,8 @@ module Feature
attr_reader :path
attr_reader :attributes
+ VALID_FEATURE_NAME = %r{^#{Gitlab::Regex.sep_by_1('_', /[a-z0-9]+/)}$}.freeze
+
PARAMS.each do |param|
define_method(param) do
attributes[param]
@@ -38,6 +40,10 @@ module Feature
raise Feature::InvalidFeatureFlagError, "Feature flag is missing name"
end
+ unless VALID_FEATURE_NAME =~ name
+ raise Feature::InvalidFeatureFlagError, "Feature flag '#{name}' is invalid"
+ end
+
unless path.present?
raise Feature::InvalidFeatureFlagError, "Feature flag '#{name}' is missing path"
end
diff --git a/lib/flowdock/git.rb b/lib/flowdock/git.rb
deleted file mode 100644
index 897ee647d87..00000000000
--- a/lib/flowdock/git.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-# frozen_string_literal: true
-require 'flowdock'
-require 'flowdock/git/builder'
-
-module Flowdock
- class Git
- TokenError = Class.new(StandardError)
-
- DEFAULT_PERMANENT_REFS = [
- Regexp.new('refs/heads/master')
- ].freeze
-
- class << self
- def post(ref, from, to, options = {})
- Git.new(ref, from, to, options).post
- end
- end
-
- def initialize(ref, from, to, options = {})
- raise TokenError, "Flowdock API token not found" unless options[:token]
-
- @ref = ref
- @from = from
- @to = to
- @options = options
- @token = options[:token]
- @commit_url = options[:commit_url]
- @diff_url = options[:diff_url]
- @repo_url = options[:repo_url]
- @repo_name = options[:repo_name]
- @permanent_refs = options.fetch(:permanent_refs, DEFAULT_PERMANENT_REFS)
- end
-
- # Send git push notification to Flowdock
- def post
- messages.each do |message|
- ::Flowdock::Client.new(flow_token: @token).post_to_thread(message)
- end
- end
-
- def repo
- @options[:repo]
- end
-
- private
-
- def messages
- Git::Builder.new(repo: repo,
- ref: @ref,
- before: @from,
- after: @to,
- commit_url: @commit_url,
- branch_url: @branch_url,
- diff_url: @diff_url,
- repo_url: @repo_url,
- repo_name: @repo_name,
- permanent_refs: @permanent_refs,
- tags: tags
- ).to_hashes
- end
-
- # Flowdock tags attached to the push notification
- def tags
- Array(@options[:tags]).map { |tag| CGI.escape(tag) }
- end
- end
-end
diff --git a/lib/flowdock/git/builder.rb b/lib/flowdock/git/builder.rb
deleted file mode 100644
index 88d9814950a..00000000000
--- a/lib/flowdock/git/builder.rb
+++ /dev/null
@@ -1,145 +0,0 @@
-# frozen_string_literal: true
-module Flowdock
- class Git
- class Commit
- def initialize(external_thread_id, thread, tags, commit)
- @commit = commit
- @external_thread_id = external_thread_id
- @thread = thread
- @tags = tags
- end
-
- def to_hash
- hash = {
- external_thread_id: @external_thread_id,
- event: "activity",
- author: {
- name: @commit[:author][:name],
- email: @commit[:author][:email]
- },
- title: title,
- thread: @thread,
- body: body
- }
- hash[:tags] = @tags if @tags
- encode(hash)
- end
-
- private
-
- def encode(hash)
- return hash unless "".respond_to?(:encode)
-
- encode_as_utf8(hash)
- end
-
- # This only works on Ruby 1.9
- def encode_as_utf8(obj)
- if obj.is_a? Hash
- obj.each_pair do |key, val|
- encode_as_utf8(val)
- end
- elsif obj.is_a?(Array)
- obj.each do |val|
- encode_as_utf8(val)
- end
- elsif obj.is_a?(String) && obj.encoding != Encoding::UTF_8
- unless obj.force_encoding("UTF-8").valid_encoding?
- obj.force_encoding("ISO-8859-1").encode!(Encoding::UTF_8, invalid: :replace, undef: :replace)
- end
- end
- end
-
- def body
- content = @commit[:message][first_line.size..]
- content.strip! if content
- "<pre>#{content}</pre>" unless content.empty?
- end
-
- def first_line
- @first_line ||= (@commit[:message].split("\n")[0] || @commit[:message])
- end
-
- def title
- commit_id = @commit[:id][0, 7]
- if @commit[:url]
- "<a href=\"#{@commit[:url]}\">#{commit_id}</a> #{message_title}"
- else
- "#{commit_id} #{message_title}"
- end
- end
-
- def message_title
- CGI.escape_html(first_line.strip)
- end
- end
-
- # Class used to build Git payload
- class Builder
- include ::Gitlab::Utils::StrongMemoize
-
- def initialize(opts)
- @repo = opts[:repo]
- @ref = opts[:ref]
- @before = opts[:before]
- @after = opts[:after]
- @opts = opts
- end
-
- def commits
- @repo.commits_between(@before, @after).map do |commit|
- {
- url: @opts[:commit_url] ? @opts[:commit_url] % [commit.sha] : nil,
- id: commit.sha,
- message: commit.message,
- author: {
- name: commit.author_name,
- email: commit.author_email
- }
- }
- end
- end
-
- def ref_name
- @ref.to_s.sub(%r{\Arefs/(heads|tags)/}, '')
- end
-
- def to_hashes
- commits.map do |commit|
- Commit.new(external_thread_id, thread, @opts[:tags], commit).to_hash
- end
- end
-
- private
-
- def thread
- @thread ||= {
- title: thread_title,
- external_url: @opts[:repo_url]
- }
- end
-
- def permanent?
- strong_memoize(:permanent) do
- @opts[:permanent_refs].any? { |regex| regex.match(@ref) }
- end
- end
-
- def thread_title
- action = "updated" if permanent?
- type = @ref =~ %r(^refs/heads/) ? "branch" : "tag"
-
- [@opts[:repo_name], type, ref_name, action].compact.join(" ")
- end
-
- def external_thread_id
- @external_thread_id ||=
- if permanent?
- SecureRandom.hex
- else
- @ref
- end
- end
- end
- end
-end
diff --git a/lib/gem_extensions/active_record/association.rb b/lib/gem_extensions/active_record/association.rb
index c6634a0524a..0969b803ca4 100644
--- a/lib/gem_extensions/active_record/association.rb
+++ b/lib/gem_extensions/active_record/association.rb
@@ -23,13 +23,12 @@ module GemExtensions
def association_scope
if klass
- @association_scope ||= begin # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ @association_scope ||= # rubocop:disable Gitlab/ModuleWithInstanceVariables
if disable_joins
::GemExtensions::ActiveRecord::DisableJoins::Associations::AssociationScope.scope(self)
else
super
end
- end
end
end
end
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index d33120575a2..1190c92ce17 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -29,19 +29,19 @@ module Gitlab
end
def self.revision
- @_revision ||= begin
- if File.exist?(root.join("REVISION"))
- File.read(root.join("REVISION")).strip.freeze
- else
- result = Gitlab::Popen.popen_with_detail(%W[#{config.git.bin_path} log --pretty=format:%h --abbrev=11 -n 1])
-
- if result.status.success?
- result.stdout.chomp.freeze
- else
- "Unknown"
- end
- end
- end
+ @_revision ||= if File.exist?(root.join("REVISION"))
+ File.read(root.join("REVISION")).strip.freeze
+ else
+ result = Gitlab::Popen.popen_with_detail(
+ %W[#{config.git.bin_path} log --pretty=format:%h --abbrev=11 -n 1]
+ )
+
+ if result.status.success?
+ result.stdout.chomp.freeze
+ else
+ "Unknown"
+ end
+ end
end
APP_DIRS_PATTERN = %r{^/?(app|config|ee|lib|spec|\(\w*\))}.freeze
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events.rb b/lib/gitlab/analytics/cycle_analytics/stage_events.rb
index b7a11bc0418..9ac433944a8 100644
--- a/lib/gitlab/analytics/cycle_analytics/stage_events.rb
+++ b/lib/gitlab/analytics/cycle_analytics/stage_events.rb
@@ -80,6 +80,10 @@ module Gitlab
def self.internal_events
INTERNAL_EVENTS
end
+
+ def self.selectable_events
+ (events - internal_events).sort_by(&:name)
+ end
end
end
end
diff --git a/lib/gitlab/application_context.rb b/lib/gitlab/application_context.rb
index b6ad25e700b..06ce1dbdc77 100644
--- a/lib/gitlab/application_context.rb
+++ b/lib/gitlab/application_context.rb
@@ -164,7 +164,11 @@ module Gitlab
end
def include_client?
- set_values.include?(:user) || set_values.include?(:runner) || set_values.include?(:remote_ip)
+ # Don't overwrite an existing more specific client id with an `ip/` one.
+ original_client_id = self.class.current_context_attribute(:client_id).to_s
+ return false if original_client_id.starts_with?('user/') || original_client_id.starts_with?('runner/')
+
+ include_user? || set_values.include?(:runner) || set_values.include?(:remote_ip)
end
def include_user?
@@ -178,8 +182,8 @@ module Gitlab
def client
if runner
"runner/#{runner.id}"
- elsif user
- "user/#{user.id}"
+ elsif user_id
+ "user/#{user_id}"
else
"ip/#{remote_ip}"
end
diff --git a/lib/gitlab/application_rate_limiter.rb b/lib/gitlab/application_rate_limiter.rb
index 507f94d87a5..5b1bf99e297 100644
--- a/lib/gitlab/application_rate_limiter.rb
+++ b/lib/gitlab/application_rate_limiter.rb
@@ -51,8 +51,11 @@ module Gitlab
project_testing_integration: { threshold: 5, interval: 1.minute },
email_verification: { threshold: 10, interval: 10.minutes },
email_verification_code_send: { threshold: 10, interval: 1.hour },
+ phone_verification_send_code: { threshold: 10, interval: 1.hour },
+ phone_verification_verify_code: { threshold: 10, interval: 10.minutes },
namespace_exists: { threshold: 20, interval: 1.minute },
- fetch_google_ip_list: { threshold: 10, interval: 1.minute }
+ fetch_google_ip_list: { threshold: 10, interval: 1.minute },
+ jobs_index: { threshold: 600, interval: 1.minute }
}.freeze
end
diff --git a/lib/gitlab/audit/auditor.rb b/lib/gitlab/audit/auditor.rb
index 4a6e4e2e06e..fddc1f830aa 100644
--- a/lib/gitlab/audit/auditor.rb
+++ b/lib/gitlab/audit/auditor.rb
@@ -59,7 +59,8 @@ module Gitlab
@context = context
@name = @context.fetch(:name, 'audit_operation')
- @stream_only = @context.fetch(:stream_only, false)
+ @is_audit_event_yaml_defined = Gitlab::Audit::Type::Definition.defined?(@name)
+ @stream_only = stream_only?
@author = @context.fetch(:author)
@scope = @context.fetch(:scope)
@target = @context.fetch(:target)
@@ -70,6 +71,14 @@ module Gitlab
@target_details = @context[:target_details]
@authentication_event = @context.fetch(:authentication_event, false)
@authentication_provider = @context[:authentication_provider]
+
+ # TODO: Remove this code once we close https://gitlab.com/gitlab-org/gitlab/-/issues/367870
+ return unless @is_audit_event_yaml_defined
+
+ # rubocop:disable Gitlab/RailsLogger
+ Rails.logger.warn('WARNING: Logging audit events without an event type definition will be deprecated soon.')
+ Rails.logger.warn('See https://docs.gitlab.com/ee/development/audit_event_guide/#event-type-definitions')
+ # rubocop:enable Gitlab/RailsLogger
end
def single_audit
@@ -84,14 +93,23 @@ module Gitlab
end
def record(events)
- log_events(events) unless @stream_only
- send_to_stream(events)
+ @stream_only ? send_to_stream(events) : log_events_and_stream(events)
end
- def log_events(events)
+ def log_events_and_stream(events)
log_authentication_event
- log_to_database(events)
+ saved_events = log_to_database(events)
+
+ # we only want to override events with saved_events when it successfully saves into database.
+ # we are doing so to ensure events in memory reflects events saved in database and have id column.
+ events = saved_events if saved_events.present?
+
+ log_to_file_and_stream(events)
+ end
+
+ def log_to_file_and_stream(events)
log_to_file(events)
+ send_to_stream(events)
end
def audit_enabled?
@@ -102,6 +120,14 @@ module Gitlab
@authentication_event
end
+ def stream_only?
+ if @is_audit_event_yaml_defined
+ Gitlab::Audit::Type::Definition.stream_only?(@name)
+ else
+ @context.fetch(:stream_only, false)
+ end
+ end
+
def log_authentication_event
return unless Gitlab::Database.read_write? && authentication_event?
@@ -145,7 +171,13 @@ module Gitlab
end
def log_to_database(events)
- AuditEvent.bulk_insert!(events)
+ if events.one?
+ events.first.save!
+ events
+ else
+ event_ids = AuditEvent.bulk_insert!(events, returns: :ids)
+ AuditEvent.id_in(event_ids)
+ end
rescue ActiveRecord::RecordInvalid => e
::Gitlab::ErrorTracking.track_exception(e, audit_operation: @name)
end
diff --git a/lib/gitlab/audit/type/definition.rb b/lib/gitlab/audit/type/definition.rb
index f64f66f4ca4..81c88a3a0ae 100644
--- a/lib/gitlab/audit/type/definition.rb
+++ b/lib/gitlab/audit/type/definition.rb
@@ -59,19 +59,36 @@ module Gitlab
end
class << self
+ include ::Gitlab::Utils::StrongMemoize
+
def paths
@paths ||= [Rails.root.join('config', 'audit_events', 'types', '*.yml')]
end
def definitions
- # We lazily load all definitions
- @definitions ||= load_all!
+ load_all!
end
+ strong_memoize_attr :definitions
def get(key)
definitions[key.to_sym]
end
+ def event_names
+ definitions.keys.map(&:to_s)
+ end
+
+ def defined?(key)
+ get(key).present?
+ end
+
+ def stream_only?(key)
+ event_definition = get(key)
+ return false unless event_definition
+
+ event_definition.streamed && !event_definition.saved_to_database
+ end
+
private
def load_all!
diff --git a/lib/gitlab/audit/type/shared.rb b/lib/gitlab/audit/type/shared.rb
index 999b7de13e2..1e3e26d3735 100644
--- a/lib/gitlab/audit/type/shared.rb
+++ b/lib/gitlab/audit/type/shared.rb
@@ -14,7 +14,7 @@ module Gitlab
description
introduced_by_issue
introduced_by_mr
- group
+ feature_category
milestone
saved_to_database
streamed
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index c567df8e133..7e8f9c76dea 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -135,15 +135,13 @@ module Gitlab
# it is important to reset the ban counter once the client has proven
# they are not a 'bad guy'.
rate_limiter.reset!
- else
+ elsif rate_limiter.register_fail!
# Register a login failure so that Rack::Attack can block the next
# request from this IP if needed.
# This returns true when the failures are over the threshold and the IP
# is banned.
- if rate_limiter.register_fail!
- Gitlab::AppLogger.info "IP #{rate_limiter.ip} failed to login " \
+ Gitlab::AppLogger.info "IP #{rate_limiter.ip} failed to login " \
"as #{login} but has been temporarily banned from Git auth"
- end
end
end
diff --git a/lib/gitlab/auth/current_user_mode.rb b/lib/gitlab/auth/current_user_mode.rb
index fc391543f4d..9bd4711c4bb 100644
--- a/lib/gitlab/auth/current_user_mode.rb
+++ b/lib/gitlab/auth/current_user_mode.rb
@@ -106,8 +106,8 @@ module Gitlab
end
def enable_admin_mode!(password: nil, skip_password_validation: false)
- return unless user&.admin?
- return unless skip_password_validation || user&.valid_password?(password)
+ return false unless user&.admin?
+ return false unless skip_password_validation || user&.valid_password?(password)
raise NotRequestedError unless admin_mode_requested?
@@ -115,6 +115,10 @@ module Gitlab
current_session_data[ADMIN_MODE_REQUESTED_TIME_KEY] = nil
current_session_data[ADMIN_MODE_START_TIME_KEY] = Time.now
+
+ audit_user_enable_admin_mode
+
+ true
end
def disable_admin_mode!
@@ -175,6 +179,10 @@ module Gitlab
def privileged_runtime?
Gitlab::Runtime.rake? || Gitlab::Runtime.rails_runner? || Gitlab::Runtime.console?
end
+
+ def audit_user_enable_admin_mode; end
end
end
end
+
+Gitlab::Auth::CurrentUserMode.prepend_mod_with('Gitlab::Auth::CurrentUserMode')
diff --git a/lib/gitlab/auth/ldap/access.rb b/lib/gitlab/auth/ldap/access.rb
index 62a817d7c4d..ea098ff8057 100644
--- a/lib/gitlab/auth/ldap/access.rb
+++ b/lib/gitlab/auth/ldap/access.rb
@@ -12,7 +12,7 @@ module Gitlab
def self.open(user, &block)
Gitlab::Auth::Ldap::Adapter.open(user.ldap_identity.provider) do |adapter|
- block.call(self.new(user, adapter))
+ yield(self.new(user, adapter))
end
end
diff --git a/lib/gitlab/auth/ldap/adapter.rb b/lib/gitlab/auth/ldap/adapter.rb
index 47eca74aa5b..9aedc131e92 100644
--- a/lib/gitlab/auth/ldap/adapter.rb
+++ b/lib/gitlab/auth/ldap/adapter.rb
@@ -11,7 +11,7 @@ module Gitlab
def self.open(provider, &block)
Net::LDAP.open(config(provider).adapter_options) do |ldap|
- block.call(self.new(provider, ldap))
+ yield(self.new(provider, ldap))
end
end
diff --git a/lib/gitlab/auth/ldap/config.rb b/lib/gitlab/auth/ldap/config.rb
index 9dafd59561a..6c99b505797 100644
--- a/lib/gitlab/auth/ldap/config.rb
+++ b/lib/gitlab/auth/ldap/config.rb
@@ -82,7 +82,8 @@ module Gitlab
def adapter_options
opts = base_options.merge(
- encryption: encryption_options
+ encryption: encryption_options,
+ instrumentation_service: ActiveSupport::Notifications
)
opts.merge!(auth_options) if has_auth?
diff --git a/lib/gitlab/auth/ldap/dn.rb b/lib/gitlab/auth/ldap/dn.rb
index a188aa168c1..84bf455c98a 100644
--- a/lib/gitlab/auth/ldap/dn.rb
+++ b/lib/gitlab/auth/ldap/dn.rb
@@ -51,7 +51,7 @@ module Gitlab
##
# Parse a DN into key value pairs using ASN from
- # http://tools.ietf.org/html/rfc2253 section 3.
+ # https://www.rfc-editor.org/rfc/rfc2253 section 3.
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
@@ -231,7 +231,7 @@ module Gitlab
self.class.new(*to_a).to_s.downcase
end
- # https://tools.ietf.org/html/rfc4514 section 2.4 lists these exceptions
+ # https://www.rfc-editor.org/rfc/rfc4514 section 2.4 lists these exceptions
# for DN values. All of the following must be escaped in any normal string
# using a single backslash ('\') as escape. The space character is left
# out here because in a "normalized" string, spaces should only be escaped
diff --git a/lib/gitlab/background_migration/backfill_environment_tiers.rb b/lib/gitlab/background_migration/backfill_environment_tiers.rb
new file mode 100644
index 00000000000..6f381577274
--- /dev/null
+++ b/lib/gitlab/background_migration/backfill_environment_tiers.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # This class backfills the `environments.tier` column by using `guess_tier` logic.
+ # Environments created after 13.10 already have a value, however, environments created before 13.10 don't.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/300741 for more information.
+ class BackfillEnvironmentTiers < BatchedMigrationJob
+ operation_name :backfill_environment_tiers
+
+ # Equivalent to `Environment#guess_tier` pattern matching.
+ PRODUCTION_TIER = 0
+ STAGING_TIER = 1
+ TESTING_TIER = 2
+ DEVELOPMENT_TIER = 3
+ OTHER_TIER = 4
+
+ TIER_REGEXP_PAIR = [
+ { tier: DEVELOPMENT_TIER, regexp: '(dev|review|trunk)' },
+ { tier: TESTING_TIER, regexp: '(test|tst|int|ac(ce|)pt|qa|qc|control|quality)' },
+ { tier: STAGING_TIER, regexp: '(st(a|)g|mod(e|)l|pre|demo|non)' },
+ { tier: PRODUCTION_TIER, regexp: '(pr(o|)d|live)' }
+ ].freeze
+
+ def perform
+ TIER_REGEXP_PAIR.each do |pair|
+ each_sub_batch(
+ batching_scope: ->(relation) { relation.where(tier: nil).where("name ~* '#{pair[:regexp]}'") } # rubocop:disable GitlabSecurity/SqlInjection
+ ) do |sub_batch|
+ sub_batch.update_all(tier: pair[:tier])
+ end
+ end
+
+ each_sub_batch(batching_scope: ->(relation) { relation.where(tier: nil) }) do |sub_batch|
+ sub_batch.update_all(tier: OTHER_TIER)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/backfill_note_discussion_id.rb b/lib/gitlab/background_migration/backfill_note_discussion_id.rb
index da2c31ebd11..ce2698b3cb8 100644
--- a/lib/gitlab/background_migration/backfill_note_discussion_id.rb
+++ b/lib/gitlab/background_migration/backfill_note_discussion_id.rb
@@ -33,8 +33,8 @@ module Gitlab
private
def update_discussion_ids(notes)
- mapping = notes.each_with_object({}) do |note, hash|
- hash[note] = { discussion_id: note.generate_discussion_id }
+ mapping = notes.index_with do |note|
+ { discussion_id: note.generate_discussion_id }
end
Gitlab::Database::BulkUpdate.execute(%i(discussion_id), mapping)
diff --git a/lib/gitlab/background_migration/backfill_project_statistics_storage_size_without_uploads_size.rb b/lib/gitlab/background_migration/backfill_project_statistics_storage_size_without_uploads_size.rb
new file mode 100644
index 00000000000..1a3dd88ea31
--- /dev/null
+++ b/lib/gitlab/background_migration/backfill_project_statistics_storage_size_without_uploads_size.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Back-fill storage_size for project_statistics
+ class BackfillProjectStatisticsStorageSizeWithoutUploadsSize < Gitlab::BackgroundMigration::BatchedMigrationJob
+ def perform
+ # no-op
+ end
+ end
+ end
+end
+
+Gitlab::BackgroundMigration::BackfillProjectStatisticsStorageSizeWithoutUploadsSize.prepend_mod_with('Gitlab::BackgroundMigration::BackfillProjectStatisticsStorageSizeWithoutUploadsSize') # rubocop:disable Layout/LineLength
diff --git a/lib/gitlab/background_migration/batched_migration_job.rb b/lib/gitlab/background_migration/batched_migration_job.rb
index 64401bc0674..973ab20f547 100644
--- a/lib/gitlab/background_migration/batched_migration_job.rb
+++ b/lib/gitlab/background_migration/batched_migration_job.rb
@@ -9,55 +9,68 @@ module Gitlab
# see https://docs.gitlab.com/ee/development/database/batched_background_migrations.html#job-arguments.
class BatchedMigrationJob
include Gitlab::Database::DynamicModelHelpers
+ include Gitlab::ClassAttributes
- def initialize(
- start_id:, end_id:, batch_table:, batch_column:, sub_batch_size:, pause_ms:, job_arguments: [], connection:
- )
+ DEFAULT_FEATURE_CATEGORY = :database
- @start_id = start_id
- @end_id = end_id
- @batch_table = batch_table
- @batch_column = batch_column
- @sub_batch_size = sub_batch_size
- @pause_ms = pause_ms
- @job_arguments = job_arguments
- @connection = connection
- end
+ class << self
+ def generic_instance(batch_table:, batch_column:, job_arguments: [], connection:)
+ new(
+ batch_table: batch_table, batch_column: batch_column,
+ job_arguments: job_arguments, connection: connection,
+ start_id: 0, end_id: 0, sub_batch_size: 0, pause_ms: 0
+ )
+ end
- def self.generic_instance(batch_table:, batch_column:, job_arguments: [], connection:)
- new(
- batch_table: batch_table, batch_column: batch_column,
- job_arguments: job_arguments, connection: connection,
- start_id: 0, end_id: 0, sub_batch_size: 0, pause_ms: 0
- )
- end
+ def job_arguments_count
+ 0
+ end
- def self.job_arguments_count
- 0
- end
+ def operation_name(operation)
+ define_method('operation_name') do
+ operation
+ end
+ end
- def self.operation_name(operation)
- define_method('operation_name') do
- operation
+ def job_arguments(*args)
+ args.each.with_index do |arg, index|
+ define_method(arg) do
+ @job_arguments[index]
+ end
+ end
+
+ define_singleton_method(:job_arguments_count) do
+ args.count
+ end
end
- end
- def self.job_arguments(*args)
- args.each.with_index do |arg, index|
- define_method(arg) do
- @job_arguments[index]
+ def scope_to(scope)
+ define_method(:filter_batch) do |relation|
+ instance_exec(relation, &scope)
end
end
- define_singleton_method(:job_arguments_count) do
- args.count
+ def feature_category(feature_category_name = nil)
+ if feature_category_name.present?
+ set_class_attribute(:feature_category, feature_category_name)
+ else
+ get_class_attribute(:feature_category) || DEFAULT_FEATURE_CATEGORY
+ end
end
end
- def self.scope_to(scope)
- define_method(:filter_batch) do |relation|
- instance_exec(relation, &scope)
- end
+ def initialize(
+ start_id:, end_id:, batch_table:, batch_column:, sub_batch_size:, pause_ms:, job_arguments: [], connection:
+ )
+
+ @start_id = start_id
+ @end_id = end_id
+ @batch_table = batch_table
+ @batch_column = batch_column
+ @sub_batch_size = sub_batch_size
+ @pause_ms = pause_ms
+ @job_arguments = job_arguments
+ @connection = connection
end
def filter_batch(relation)
diff --git a/lib/gitlab/background_migration/delete_orphans_approval_merge_request_rules.rb b/lib/gitlab/background_migration/delete_orphans_approval_merge_request_rules.rb
new file mode 100644
index 00000000000..4b7b7d42c77
--- /dev/null
+++ b/lib/gitlab/background_migration/delete_orphans_approval_merge_request_rules.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Deletes orphans records whenever report_type equals to scan_finding (i.e., 4)
+ class DeleteOrphansApprovalMergeRequestRules < BatchedMigrationJob
+ scope_to ->(relation) { relation.where(report_type: 4) }
+
+ operation_name :delete_all
+
+ def perform
+ each_sub_batch do |sub_batch|
+ sub_batch.where(security_orchestration_policy_configuration_id: nil).delete_all
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/delete_orphans_approval_project_rules.rb b/lib/gitlab/background_migration/delete_orphans_approval_project_rules.rb
new file mode 100644
index 00000000000..33aa1a8d29d
--- /dev/null
+++ b/lib/gitlab/background_migration/delete_orphans_approval_project_rules.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Deletes orphans records whenever report_type equals to scan_finding (i.e., 4)
+ class DeleteOrphansApprovalProjectRules < BatchedMigrationJob
+ operation_name :delete_all
+
+ def perform
+ each_sub_batch do |sub_batch|
+ sub_batch.where(report_type: 4, security_orchestration_policy_configuration_id: nil).delete_all
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_five_mb.rb b/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_five_mb.rb
new file mode 100644
index 00000000000..dcef4f086e2
--- /dev/null
+++ b/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_five_mb.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Set `project_settings.legacy_open_source_license_available` to false for projects less than 5 MB
+ class DisableLegacyOpenSourceLicenseForProjectsLessThanFiveMb < ::Gitlab::BackgroundMigration::BatchedMigrationJob
+ scope_to ->(relation) do
+ relation
+ .where(legacy_open_source_license_available: true)
+ end
+
+ operation_name :disable_legacy_open_source_license_for_projects_less_than_five_mb
+
+ def perform
+ each_sub_batch do |sub_batch|
+ updates = { legacy_open_source_license_available: false, updated_at: Time.current }
+
+ sub_batch
+ .joins('INNER JOIN project_statistics ON project_statistics.project_id = project_settings.project_id')
+ .where('project_statistics.repository_size < ?', 5.megabyte)
+ .update_all(updates)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/fix_approval_project_rules_without_protected_branches.rb b/lib/gitlab/background_migration/fix_approval_project_rules_without_protected_branches.rb
new file mode 100644
index 00000000000..4b283bae79d
--- /dev/null
+++ b/lib/gitlab/background_migration/fix_approval_project_rules_without_protected_branches.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # This class doesn't update approval project rules
+ # as this feature exists only in EE
+ class FixApprovalProjectRulesWithoutProtectedBranches < BatchedMigrationJob
+ def perform; end
+ end
+ end
+end
+
+# rubocop:disable Layout/LineLength
+Gitlab::BackgroundMigration::FixApprovalProjectRulesWithoutProtectedBranches.prepend_mod_with('Gitlab::BackgroundMigration::FixApprovalProjectRulesWithoutProtectedBranches')
+# rubocop:enable Layout/LineLength
diff --git a/lib/gitlab/background_migration/fix_security_scan_statuses.rb b/lib/gitlab/background_migration/fix_security_scan_statuses.rb
new file mode 100644
index 00000000000..b60e739f870
--- /dev/null
+++ b/lib/gitlab/background_migration/fix_security_scan_statuses.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Fixes the `status` attribute of `security_scans` records
+ class FixSecurityScanStatuses < BatchedMigrationJob
+ def perform
+ # no-op. The logic is defined in EE module.
+ end
+ end
+ end
+end
+
+::Gitlab::BackgroundMigration::FixSecurityScanStatuses.prepend_mod
diff --git a/lib/gitlab/background_migration/migrate_vulnerabilities_feedback_to_vulnerabilities_state_transition.rb b/lib/gitlab/background_migration/migrate_vulnerabilities_feedback_to_vulnerabilities_state_transition.rb
new file mode 100644
index 00000000000..81b29b5a6cd
--- /dev/null
+++ b/lib/gitlab/background_migration/migrate_vulnerabilities_feedback_to_vulnerabilities_state_transition.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+# rubocop:disable Style/Documentation
+module Gitlab
+ module BackgroundMigration
+ class MigrateVulnerabilitiesFeedbackToVulnerabilitiesStateTransition < BatchedMigrationJob
+ def perform; end
+ end
+ end
+end
+
+Gitlab::BackgroundMigration::MigrateVulnerabilitiesFeedbackToVulnerabilitiesStateTransition.prepend_mod
+# rubocop:enable Style/Documentation
diff --git a/lib/gitlab/background_migration/prune_stale_project_export_jobs.rb b/lib/gitlab/background_migration/prune_stale_project_export_jobs.rb
new file mode 100644
index 00000000000..a91cda2c427
--- /dev/null
+++ b/lib/gitlab/background_migration/prune_stale_project_export_jobs.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Background migration for deleting stale project import jobs
+ class PruneStaleProjectExportJobs < BatchedMigrationJob
+ EXPIRES_IN = 7.days
+
+ scope_to ->(relation) { relation.where("updated_at < ?", EXPIRES_IN.ago) }
+ operation_name :delete_all
+
+ def perform
+ each_sub_batch(&:delete_all)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/reset_status_on_container_repositories.rb b/lib/gitlab/background_migration/reset_status_on_container_repositories.rb
new file mode 100644
index 00000000000..09cd3b1895f
--- /dev/null
+++ b/lib/gitlab/background_migration/reset_status_on_container_repositories.rb
@@ -0,0 +1,139 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # A job that:
+ # * pickup container repositories with delete_scheduled status.
+ # * check if there are tags linked to it.
+ # * if there are tags, reset the status to nil.
+ class ResetStatusOnContainerRepositories < BatchedMigrationJob
+ DELETE_SCHEDULED_STATUS = 0
+ DUMMY_TAGS = %w[tag].freeze
+ MIGRATOR = 'ResetStatusOnContainerRepositories'
+
+ scope_to ->(relation) { relation.where(status: DELETE_SCHEDULED_STATUS) }
+ operation_name :reset_status_on_container_repositories
+
+ def perform
+ each_sub_batch do |sub_batch|
+ reset_status_if_tags(sub_batch)
+ end
+ end
+
+ private
+
+ def reset_status_if_tags(container_repositories)
+ container_repositories_with_tags = container_repositories.select { |cr| cr.becomes(ContainerRepository).tags? } # rubocop:disable Cop/AvoidBecomes
+
+ ContainerRepository.where(id: container_repositories_with_tags.map(&:id))
+ .update_all(status: nil)
+ end
+
+ # rubocop:disable Style/Documentation
+ module Routable
+ extend ActiveSupport::Concern
+
+ included do
+ has_one :route,
+ as: :source,
+ class_name: '::Gitlab::BackgroundMigration::ResetStatusOnContainerRepositories::Route'
+ end
+
+ def full_path
+ route&.path || build_full_path
+ end
+
+ def build_full_path
+ if parent && path
+ "#{parent.full_path}/#{path}"
+ else
+ path
+ end
+ end
+ end
+
+ class Route < ::ApplicationRecord
+ self.table_name = 'routes'
+ end
+
+ class Namespace < ::ApplicationRecord
+ include ::Gitlab::BackgroundMigration::ResetStatusOnContainerRepositories::Routable
+ include ::Namespaces::Traversal::Recursive
+ include ::Namespaces::Traversal::Linear
+ include ::Gitlab::Utils::StrongMemoize
+
+ self.table_name = 'namespaces'
+ self.inheritance_column = :_type_disabled
+
+ belongs_to :parent,
+ class_name: '::Gitlab::BackgroundMigration::ResetStatusOnContainerRepositories::Namespace'
+
+ def self.polymorphic_name
+ 'Namespace'
+ end
+ end
+
+ class Project < ::ApplicationRecord
+ include ::Gitlab::BackgroundMigration::ResetStatusOnContainerRepositories::Routable
+
+ self.table_name = 'projects'
+
+ belongs_to :namespace,
+ class_name: '::Gitlab::BackgroundMigration::ResetStatusOnContainerRepositories::Namespace'
+
+ alias_method :parent, :namespace
+ alias_attribute :parent_id, :namespace_id
+
+ delegate :root_ancestor, to: :namespace, allow_nil: true
+ end
+
+ class ContainerRepository < ::ApplicationRecord
+ self.table_name = 'container_repositories'
+
+ belongs_to :project,
+ class_name: '::Gitlab::BackgroundMigration::ResetStatusOnContainerRepositories::Project'
+
+ def tags?
+ result = ContainerRegistry.tags_for(path).any?
+ ::Gitlab::BackgroundMigration::Logger.info(
+ migrator: MIGRATOR,
+ has_tags: result,
+ container_repository_id: id,
+ container_repository_path: path
+ )
+ result
+ end
+
+ def path
+ @path ||= [project.full_path, name].select(&:present?).join('/').downcase
+ end
+ end
+
+ class ContainerRegistry
+ class << self
+ def tags_for(path)
+ response = ContainerRegistryClient.repository_tags(path, page_size: 1)
+ return DUMMY_TAGS unless response
+
+ response['tags'] || []
+ rescue StandardError
+ DUMMY_TAGS
+ end
+ end
+ end
+
+ class ContainerRegistryClient
+ def self.repository_tags(path, page_size:)
+ registry_config = ::Gitlab.config.registry
+
+ return { 'tags' => DUMMY_TAGS } unless registry_config.enabled && registry_config.api_url.present?
+
+ pull_token = ::Auth::ContainerRegistryAuthenticationService.pull_access_token(path)
+ client = ::ContainerRegistry::Client.new(registry_config.api_url, token: pull_token)
+ client.repository_tags(path, page_size: page_size)
+ end
+ end
+ # rubocop:enable Style/Documentation
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb b/lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb
index e9a38916999..8aab7d13b45 100644
--- a/lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb
+++ b/lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb
@@ -37,16 +37,16 @@ module Gitlab
end
end
- cloud_mappings = cloud.each_with_object({}) do |tracker_data, hash|
- hash[tracker_data] = { deployment_type: 2 }
+ cloud_mappings = cloud.index_with do
+ { deployment_type: 2 }
end
- server_mappings = server.each_with_object({}) do |tracker_data, hash|
- hash[tracker_data] = { deployment_type: 1 }
+ server_mappings = server.index_with do
+ { deployment_type: 1 }
end
- unknown_mappings = unknown.each_with_object({}) do |tracker_data, hash|
- hash[tracker_data] = { deployment_type: 0 }
+ unknown_mappings = unknown.index_with do
+ { deployment_type: 0 }
end
mappings = cloud_mappings.merge(server_mappings, unknown_mappings)
diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb
index 7de6be45349..49b8ab760f3 100644
--- a/lib/gitlab/bitbucket_import/importer.rb
+++ b/lib/gitlab/bitbucket_import/importer.rb
@@ -98,7 +98,7 @@ module Gitlab
create_labels
- issue_type_id = WorkItems::Type.default_issue_type.id
+ issue_type_id = ::WorkItems::Type.default_issue_type.id
client.issues(repo).each do |issue|
import_issue(issue, issue_type_id)
diff --git a/lib/gitlab/bullet.rb b/lib/gitlab/bullet.rb
index f5f8a316855..9759a82be0c 100644
--- a/lib/gitlab/bullet.rb
+++ b/lib/gitlab/bullet.rb
@@ -10,7 +10,7 @@ module Gitlab
alias_method :extra_logging_enabled?, :enabled?
def configure_bullet?
- defined?(::Bullet) && (enabled? || Rails.env.development?)
+ defined?(::Bullet) && (enabled? || Gitlab.config.bullet.enabled)
end
end
end
diff --git a/lib/gitlab/changes_list.rb b/lib/gitlab/changes_list.rb
index fb75a78a978..999d2ee4356 100644
--- a/lib/gitlab/changes_list.rb
+++ b/lib/gitlab/changes_list.rb
@@ -15,14 +15,12 @@ module Gitlab
end
def changes
- @changes ||= begin
- @raw_changes.map do |change|
- next if change.blank?
+ @changes ||= @raw_changes.map do |change|
+ next if change.blank?
- oldrev, newrev, ref = change.strip.split(' ')
- { oldrev: oldrev, newrev: newrev, ref: ref }
- end.compact
- end
+ oldrev, newrev, ref = change.strip.split(' ')
+ { oldrev: oldrev, newrev: newrev, ref: ref }
+ end.compact
end
end
end
diff --git a/lib/gitlab/ci/ansi2html.rb b/lib/gitlab/ci/ansi2html.rb
index 19819ff7275..42a8b561d34 100644
--- a/lib/gitlab/ci/ansi2html.rb
+++ b/lib/gitlab/ci/ansi2html.rb
@@ -448,8 +448,8 @@ module Gitlab
end
def state
- state = STATE_PARAMS.each_with_object({}) do |param, h|
- h[param] = send(param) # rubocop:disable GitlabSecurity/PublicSend
+ state = STATE_PARAMS.index_with do |param|
+ send(param) # rubocop:disable GitlabSecurity/PublicSend
end
Base64.urlsafe_encode64(state.to_json)
end
diff --git a/lib/gitlab/ci/build/cache.rb b/lib/gitlab/ci/build/cache.rb
index 375e6b4a96f..1cddc9fcc98 100644
--- a/lib/gitlab/ci/build/cache.rb
+++ b/lib/gitlab/ci/build/cache.rb
@@ -8,9 +8,9 @@ module Gitlab
def initialize(cache, pipeline)
cache = Array.wrap(cache)
- @cache = cache.map do |cache|
+ @cache = cache.map.with_index do |cache, index|
Gitlab::Ci::Pipeline::Seed::Build::Cache
- .new(pipeline, cache)
+ .new(pipeline, cache, index)
end
end
diff --git a/lib/gitlab/ci/build/context/build.rb b/lib/gitlab/ci/build/context/build.rb
index a1a8e9288c7..1025e1cc2d7 100644
--- a/lib/gitlab/ci/build/context/build.rb
+++ b/lib/gitlab/ci/build/context/build.rb
@@ -9,25 +9,29 @@ module Gitlab
attr_reader :attributes
- def initialize(pipeline, attributes = {})
+ def initialize(pipeline, attributes = {}, build = nil)
super(pipeline)
+ @build = build
@attributes = attributes
end
def variables
- strong_memoize(:variables) do
- # This is a temporary piece of technical debt to allow us access
- # to the CI variables to evaluate rules before we persist a Build
- # with the result. We should refactor away the extra Build.new,
- # but be able to get CI Variables directly from the Seed::Build.
- stub_build.scoped_variables
- end
+ build.scoped_variables
end
+ strong_memoize_attr :variables
private
+ def build
+ @build || stub_build
+ end
+
def stub_build
+ # This is a temporary piece of technical debt to allow us access
+ # to the CI variables to evaluate rules before we persist a Build
+ # with the result. We should refactor away the extra Build.new,
+ # but be able to get CI Variables directly from the Seed::Build.
::Ci::Build.new(build_attributes)
end
diff --git a/lib/gitlab/ci/build/hook.rb b/lib/gitlab/ci/build/hook.rb
new file mode 100644
index 00000000000..b731228678c
--- /dev/null
+++ b/lib/gitlab/ci/build/hook.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Build
+ class Hook
+ attr_reader :name, :script
+
+ class << self
+ def from_hooks(job)
+ job.options[:hooks].to_a.map do |name, script|
+ new(name.to_s, script)
+ end
+ end
+ end
+
+ def initialize(name, script)
+ @name = name
+ @script = script
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb
index ee537f4efe5..142f0b8dfd8 100644
--- a/lib/gitlab/ci/config.rb
+++ b/lib/gitlab/ci/config.rb
@@ -10,7 +10,7 @@ module Gitlab
ConfigError = Class.new(StandardError)
TIMEOUT_SECONDS = 30.seconds
- TIMEOUT_MESSAGE = 'Resolving config took longer than expected'
+ TIMEOUT_MESSAGE = 'Request timed out when fetching configuration files.'
RESCUE_ERRORS = [
Gitlab::Config::Loader::FormatError,
@@ -26,7 +26,7 @@ module Gitlab
@source_ref_path = pipeline&.source_ref_path
@project = project
- @context = self.logger.instrument(:config_build_context) do
+ @context = self.logger.instrument(:config_build_context, once: true) do
pipeline ||= ::Ci::Pipeline.new(project: project, sha: sha, user: user, source: source)
build_context(project: project, pipeline: pipeline, sha: sha, user: user, parent_pipeline: parent_pipeline)
end
@@ -35,12 +35,16 @@ module Gitlab
@source = source
- @config = self.logger.instrument(:config_expand) do
+ @config = self.logger.instrument(:config_expand, once: true) do
expand_config(config)
end
- @root = self.logger.instrument(:config_compose) do
- Entry::Root.new(@config, project: project, user: user).tap(&:compose!)
+ @root = self.logger.instrument(:config_root, once: true) do
+ Entry::Root.new(@config, project: project, user: user, logger: self.logger)
+ end
+
+ self.logger.instrument(:config_root_compose, once: true) do
+ @root.compose!
end
rescue *rescue_errors => e
raise Config::ConfigError, e.message
@@ -123,23 +127,23 @@ module Gitlab
end
def build_config(config)
- initial_config = logger.instrument(:config_yaml_load) do
+ initial_config = logger.instrument(:config_yaml_load, once: true) do
Config::Yaml.load!(config)
end
- initial_config = logger.instrument(:config_external_process) do
+ initial_config = logger.instrument(:config_external_process, once: true) do
Config::External::Processor.new(initial_config, @context).perform
end
- initial_config = logger.instrument(:config_yaml_extend) do
+ initial_config = logger.instrument(:config_yaml_extend, once: true) do
Config::Extendable.new(initial_config).to_hash
end
- initial_config = logger.instrument(:config_tags_resolve) do
+ initial_config = logger.instrument(:config_tags_resolve, once: true) do
Config::Yaml::Tags::Resolver.new(initial_config).to_hash
end
- logger.instrument(:config_stages_inject) do
+ logger.instrument(:config_stages_inject, once: true) do
Config::EdgeStagesInjector.new(initial_config).to_hash
end
end
@@ -163,7 +167,7 @@ module Gitlab
end
def build_variables(pipeline:)
- logger.instrument(:config_build_variables) do
+ logger.instrument(:config_build_variables, once: true) do
pipeline
.variables_builder
.config_variables
diff --git a/lib/gitlab/ci/config/entry/artifacts.rb b/lib/gitlab/ci/config/entry/artifacts.rb
index 3b0cbc6b69e..27206d7e3a8 100644
--- a/lib/gitlab/ci/config/entry/artifacts.rb
+++ b/lib/gitlab/ci/config/entry/artifacts.rb
@@ -12,6 +12,7 @@ module Gitlab
include ::Gitlab::Config::Entry::Validatable
include ::Gitlab::Config::Entry::Attributable
+ ALLOWED_WHEN = %w[on_success on_failure always].freeze
ALLOWED_KEYS = %i[name untracked paths reports when expire_in expose_as exclude public].freeze
EXPOSE_AS_REGEX = /\A\w[-\w ]*\z/.freeze
EXPOSE_AS_ERROR_MESSAGE = "can contain only letters, digits, '-', '_' and spaces"
@@ -38,10 +39,10 @@ module Gitlab
validates :expose_as, format: { with: EXPOSE_AS_REGEX, message: EXPOSE_AS_ERROR_MESSAGE }, if: :expose_as_present?
validates :exclude, array_of_strings: true
validates :reports, type: Hash
- validates :when,
- inclusion: { in: %w[on_success on_failure always],
- message: 'should be on_success, on_failure ' \
- 'or always' }
+ validates :when, type: String, inclusion: {
+ in: ALLOWED_WHEN,
+ message: "should be one of: #{ALLOWED_WHEN.join(', ')}"
+ }
validates :expire_in, duration: { parser: ::Gitlab::Ci::Build::DurationParser }
end
end
diff --git a/lib/gitlab/ci/config/entry/cache.rb b/lib/gitlab/ci/config/entry/cache.rb
index ab79add688b..a5481071fc5 100644
--- a/lib/gitlab/ci/config/entry/cache.rb
+++ b/lib/gitlab/ci/config/entry/cache.rb
@@ -17,16 +17,16 @@ module Gitlab
validations do
validates :config, type: Hash, allowed_keys: ALLOWED_KEYS
- validates :policy,
- inclusion: { in: ALLOWED_POLICY, message: 'should be pull-push, push, or pull' },
- allow_blank: true
+ validates :policy, type: String, allow_blank: true, inclusion: {
+ in: ALLOWED_POLICY,
+ message: "should be one of: #{ALLOWED_POLICY.join(', ')}"
+ }
with_options allow_nil: true do
- validates :when,
- inclusion: {
- in: ALLOWED_WHEN,
- message: 'should be on_success, on_failure or always'
- }
+ validates :when, type: String, inclusion: {
+ in: ALLOWED_WHEN,
+ message: "should be one of: #{ALLOWED_WHEN.join(', ')}"
+ }
end
end
diff --git a/lib/gitlab/ci/config/entry/default.rb b/lib/gitlab/ci/config/entry/default.rb
index 12d68b755b3..e996b6b1312 100644
--- a/lib/gitlab/ci/config/entry/default.rb
+++ b/lib/gitlab/ci/config/entry/default.rb
@@ -13,9 +13,8 @@ module Gitlab
include ::Gitlab::Config::Entry::Configurable
include ::Gitlab::Config::Entry::Inheritable
- ALLOWED_KEYS = %i[before_script image services
- after_script cache interruptible
- timeout retry tags artifacts].freeze
+ ALLOWED_KEYS = %i[before_script after_script hooks cache image services
+ interruptible timeout retry tags artifacts].freeze
validations do
validates :config, allowed_keys: ALLOWED_KEYS
@@ -25,22 +24,27 @@ module Gitlab
description: 'Script that will be executed before each job.',
inherit: true
- entry :image, Entry::Image,
- description: 'Docker image that will be used to execute jobs.',
- inherit: true
-
- entry :services, Entry::Services,
- description: 'Docker images that will be linked to the container.',
- inherit: true
-
entry :after_script, Entry::Commands,
description: 'Script that will be executed after each job.',
inherit: true
+ entry :hooks, Entry::Hooks,
+ description: 'Commands that will be executed on Runner before/after some events ' \
+ 'such as `clone` and `build-script`.',
+ inherit: false
+
entry :cache, Entry::Caches,
description: 'Configure caching between build jobs.',
inherit: true
+ entry :image, Entry::Image,
+ description: 'Docker image that will be used to execute jobs.',
+ inherit: true
+
+ entry :services, Entry::Services,
+ description: 'Docker images that will be linked to the container.',
+ inherit: true
+
entry :interruptible, ::Gitlab::Config::Entry::Boolean,
description: 'Set jobs interruptible default value.',
inherit: false
diff --git a/lib/gitlab/ci/config/entry/hooks.rb b/lib/gitlab/ci/config/entry/hooks.rb
new file mode 100644
index 00000000000..28bc2e4e7ce
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/hooks.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ class Hooks < ::Gitlab::Config::Entry::Node
+ # `Configurable` alreadys adds `Validatable`
+ include ::Gitlab::Config::Entry::Configurable
+
+ # NOTE: If a new hook is added, inheriting should be changed because a `job:hooks` overrides all
+ # `default:hooks` now. We should implement merging; each hook must be overridden individually.
+ ALLOWED_HOOKS = %i[pre_get_sources_script].freeze
+
+ validations do
+ validates :config, type: Hash, allowed_keys: ALLOWED_HOOKS
+ end
+
+ entry :pre_get_sources_script, Entry::Commands,
+ description: 'Commands that will be executed on Runner before cloning/fetching the Git repository.'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/id_token.rb b/lib/gitlab/ci/config/entry/id_token.rb
new file mode 100644
index 00000000000..12e0975d1b1
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/id_token.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ ##
+ # Entry that represents a JWT definition.
+ #
+ class IdToken < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Attributable
+ include ::Gitlab::Config::Entry::Validatable
+
+ attributes %i[aud]
+
+ validations do
+ validates :config, required_keys: %i[aud], allowed_keys: %i[aud]
+ validates :aud, array_of_strings_or_string: true
+ end
+
+ def value
+ { aud: aud }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb
index 8e7f6ba4326..7c49b59a7f0 100644
--- a/lib/gitlab/ci/config/entry/job.rb
+++ b/lib/gitlab/ci/config/entry/job.rb
@@ -12,9 +12,9 @@ module Gitlab
ALLOWED_WHEN = %w[on_success on_failure always manual delayed].freeze
ALLOWED_KEYS = %i[tags script image services start_in artifacts
- cache dependencies before_script after_script
+ cache dependencies before_script after_script hooks
environment coverage retry parallel interruptible timeout
- release].freeze
+ release id_tokens].freeze
validations do
validates :config, allowed_keys: Gitlab::Ci::Config::Entry::Job.allowed_keys + PROCESSABLE_ALLOWED_KEYS
@@ -59,6 +59,10 @@ module Gitlab
description: 'Commands that will be executed when finishing job.',
inherit: true
+ entry :hooks, Entry::Hooks,
+ description: 'Commands that will be executed on Runner before/after some events; clone, build-script.',
+ inherit: true
+
entry :cache, Entry::Caches,
description: 'Cache definition for this job.',
inherit: true
@@ -116,6 +120,11 @@ module Gitlab
description: 'Indicates whether this job is allowed to fail or not.',
inherit: false
+ entry :id_tokens, ::Gitlab::Config::Entry::ComposableHash,
+ description: 'Configured JWTs for this job',
+ inherit: false,
+ metadata: { composable_class: ::Gitlab::Ci::Config::Entry::IdToken }
+
attributes :script, :tags, :when, :dependencies,
:needs, :retry, :parallel, :start_in,
:interruptible, :timeout,
@@ -155,10 +164,12 @@ module Gitlab
artifacts: artifacts_value,
release: release_value,
after_script: after_script_value,
+ hooks: hooks_pre_get_sources_script_enabled? ? hooks_value : nil,
ignore: ignored?,
allow_failure_criteria: allow_failure_criteria,
needs: needs_defined? ? needs_value : nil,
- scheduling_type: needs_defined? ? :dag : :stage
+ scheduling_type: needs_defined? ? :dag : :stage,
+ id_tokens: id_tokens_value
).compact
end
@@ -183,6 +194,10 @@ module Gitlab
allow_failure_value
end
+
+ def hooks_pre_get_sources_script_enabled?
+ YamlProcessor::FeatureFlags.enabled?(:ci_hooks_pre_get_sources_script)
+ end
end
end
end
diff --git a/lib/gitlab/ci/config/entry/reports.rb b/lib/gitlab/ci/config/entry/reports.rb
index f77876cc926..16844fa88db 100644
--- a/lib/gitlab/ci/config/entry/reports.rb
+++ b/lib/gitlab/ci/config/entry/reports.rb
@@ -16,8 +16,8 @@ module Gitlab
%i[junit codequality sast secret_detection dependency_scanning container_scanning
dast performance browser_performance load_performance license_scanning metrics lsif
dotenv terraform accessibility
- requirements coverage_fuzzing api_fuzzing cluster_image_scanning
- coverage_report cyclonedx].freeze
+ coverage_fuzzing api_fuzzing cluster_image_scanning
+ requirements requirements_v2 coverage_report cyclonedx].freeze
attributes ALLOWED_KEYS
@@ -48,6 +48,7 @@ module Gitlab
validates :terraform, array_of_strings_or_string: true
validates :accessibility, array_of_strings_or_string: true
validates :requirements, array_of_strings_or_string: true
+ validates :requirements_v2, array_of_strings_or_string: true
validates :cyclonedx, array_of_strings_or_string: true
end
end
diff --git a/lib/gitlab/ci/config/entry/root.rb b/lib/gitlab/ci/config/entry/root.rb
index a30e6a0d9c3..a3d57ab6ac6 100644
--- a/lib/gitlab/ci/config/entry/root.rb
+++ b/lib/gitlab/ci/config/entry/root.rb
@@ -50,7 +50,7 @@ module Gitlab
entry :variables, Entry::Variables,
description: 'Environment variables that will be used.',
- metadata: { allowed_value_data: %i[value description expand], allow_array_value: true },
+ metadata: { allowed_value_data: %i[value description expand options] },
reserved: true
entry :stages, Entry::Stages,
@@ -103,12 +103,16 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord
def compose_jobs!
- factory = ::Gitlab::Config::Entry::Factory.new(Entry::Jobs)
- .value(jobs_config)
- .with(key: :jobs, parent: self,
- description: 'Jobs definition for this pipeline')
+ factory = logger.instrument(:config_root_compose_jobs_factory, once: true) do
+ ::Gitlab::Config::Entry::Factory.new(Entry::Jobs)
+ .value(jobs_config)
+ .with(key: :jobs, parent: self,
+ description: 'Jobs definition for this pipeline')
+ end
- @entries[:jobs] = factory.create!
+ @entries[:jobs] = logger.instrument(:config_root_compose_jobs_create, once: true) do
+ factory.create!
+ end
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -123,6 +127,10 @@ module Gitlab
@config = @config.except(*@jobs_config.keys)
end
+
+ def logger
+ metadata[:logger]
+ end
end
end
end
diff --git a/lib/gitlab/ci/config/entry/trigger.rb b/lib/gitlab/ci/config/entry/trigger.rb
index 0f94b3f94fe..4c254a4fa07 100644
--- a/lib/gitlab/ci/config/entry/trigger.rb
+++ b/lib/gitlab/ci/config/entry/trigger.rb
@@ -41,7 +41,7 @@ module Gitlab
validations do
validates :config, presence: true
validates :config, allowed_keys: ALLOWED_KEYS
- validates :project, presence: true
+ validates :project, type: String, presence: true
validates :branch, type: String, allow_nil: true
validates :strategy, type: String, inclusion: { in: %w[depend], message: 'should be depend' }, allow_nil: true
end
diff --git a/lib/gitlab/ci/config/entry/variable.rb b/lib/gitlab/ci/config/entry/variable.rb
index 16091758916..decb568ffc9 100644
--- a/lib/gitlab/ci/config/entry/variable.rb
+++ b/lib/gitlab/ci/config/entry/variable.rb
@@ -10,7 +10,6 @@ module Gitlab
class Variable < ::Gitlab::Config::Entry::Simplifiable
strategy :SimpleVariable, if: -> (config) { SimpleVariable.applies_to?(config) }
strategy :ComplexVariable, if: -> (config) { ComplexVariable.applies_to?(config) }
- strategy :ComplexArrayVariable, if: -> (config) { ComplexArrayVariable.applies_to?(config) }
class SimpleVariable < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Validatable
@@ -41,20 +40,24 @@ module Gitlab
class ComplexVariable < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Validatable
+ include ::Gitlab::Config::Entry::Attributable
class << self
def applies_to?(config)
- config.is_a?(Hash) && !config[:value].is_a?(Array)
+ config.is_a?(Hash)
end
end
+ attributes :value, :description, :expand, :options, prefix: :config
+
validations do
validates :key, alphanumeric: true
- validates :config_value, alphanumeric: true, allow_nil: false, if: :config_value_defined?
- validates :config_description, alphanumeric: true, allow_nil: false, if: :config_description_defined?
- validates :config_expand, boolean: true,
- allow_nil: false,
- if: -> { ci_raw_variables_in_yaml_config_enabled? && config_expand_defined? }
+ validates :config_value, alphanumeric: true, allow_nil: true
+ validates :config_description, alphanumeric: true, allow_nil: true
+ validates :config_expand, boolean: true, allow_nil: true, if: -> {
+ ci_raw_variables_in_yaml_config_enabled?
+ }
+ validates :config_options, array_of_strings: true, allow_nil: true
validate do
allowed_value_data = Array(opt(:allowed_value_data))
@@ -66,91 +69,43 @@ module Gitlab
else
errors.add(:config, "must be a string")
end
+
+ if config_options.present? && config_options.exclude?(config_value)
+ errors.add(:config, 'value must be present in options')
+ end
end
end
def value
+ # Needed since the `Entry::Node` provides `value` (which is current hash)
config_value.to_s
end
def value_with_data
if ci_raw_variables_in_yaml_config_enabled?
{
- value: value,
- raw: (!config_expand if config_expand_defined?)
+ value: config_value.to_s,
+ raw: (!config_expand if has_config_expand?)
}.compact
else
{
- value: value
+ value: config_value.to_s
}.compact
end
end
def value_with_prefill_data
value_with_data.merge(
- description: config_description
+ description: config_description,
+ options: config_options
).compact
end
- def config_value
- @config[:value]
- end
-
- def config_description
- @config[:description]
- end
-
- def config_expand
- @config[:expand]
- end
-
- def config_value_defined?
- config.key?(:value)
- end
-
- def config_description_defined?
- config.key?(:description)
- end
-
- def config_expand_defined?
- config.key?(:expand)
- end
-
def ci_raw_variables_in_yaml_config_enabled?
YamlProcessor::FeatureFlags.enabled?(:ci_raw_variables_in_yaml_config)
end
end
- class ComplexArrayVariable < ComplexVariable
- include ::Gitlab::Config::Entry::Validatable
-
- class << self
- def applies_to?(config)
- config.is_a?(Hash) && config[:value].is_a?(Array)
- end
- end
-
- validations do
- validates :config_value, array_of_strings: true, allow_nil: false, if: :config_value_defined?
-
- validate do
- next if opt(:allow_array_value)
-
- errors.add(:config, 'value must be an alphanumeric string')
- end
- end
-
- def value
- config_value.first
- end
-
- def value_with_prefill_data
- super.merge(
- value_options: config_value
- ).compact
- end
- end
-
class UnknownStrategy < ::Gitlab::Config::Entry::Node
def errors
["variable definition must be either a string or a hash"]
diff --git a/lib/gitlab/ci/config/entry/variables.rb b/lib/gitlab/ci/config/entry/variables.rb
index ef4f74b9f56..e338bce3109 100644
--- a/lib/gitlab/ci/config/entry/variables.rb
+++ b/lib/gitlab/ci/config/entry/variables.rb
@@ -42,7 +42,7 @@ module Gitlab
end
def composable_metadata
- { allowed_value_data: opt(:allowed_value_data), allow_array_value: opt(:allow_array_value) }
+ { allowed_value_data: opt(:allowed_value_data) }
end
end
end
diff --git a/lib/gitlab/ci/config/external/file/base.rb b/lib/gitlab/ci/config/external/file/base.rb
index 57ff606c9ee..65caf4ac47d 100644
--- a/lib/gitlab/ci/config/external/file/base.rb
+++ b/lib/gitlab/ci/config/external/file/base.rb
@@ -47,7 +47,7 @@ module Gitlab
end
def validate!
- validate_execution_time!
+ context.check_execution_time! if ::Feature.disabled?(:ci_refactoring_external_mapper, context.project)
validate_location!
validate_context! if valid?
fetch_and_validate_content! if valid?
@@ -87,10 +87,6 @@ module Gitlab
nil
end
- def validate_execution_time!
- context.check_execution_time!
- end
-
def validate_location!
if invalid_location_type?
errors.push("Included file `#{masked_location}` needs to be a string")
diff --git a/lib/gitlab/ci/config/external/file/remote.rb b/lib/gitlab/ci/config/external/file/remote.rb
index b0c540685d4..ed37357dc53 100644
--- a/lib/gitlab/ci/config/external/file/remote.rb
+++ b/lib/gitlab/ci/config/external/file/remote.rb
@@ -53,7 +53,7 @@ module Gitlab
errors.push("Remote file `#{masked_location}` could not be fetched because of a timeout error!")
rescue Gitlab::HTTP::Error
errors.push("Remote file `#{masked_location}` could not be fetched because of HTTP error!")
- rescue Gitlab::HTTP::BlockedUrlError => e
+ rescue Errno::ECONNREFUSED, Gitlab::HTTP::BlockedUrlError => e
errors.push("Remote file could not be fetched because #{e}!")
end
diff --git a/lib/gitlab/ci/config/external/mapper.rb b/lib/gitlab/ci/config/external/mapper.rb
index fc03ac125fd..a41bc2b39f2 100644
--- a/lib/gitlab/ci/config/external/mapper.rb
+++ b/lib/gitlab/ci/config/external/mapper.rb
@@ -7,6 +7,7 @@ module Gitlab
class Mapper
include Gitlab::Utils::StrongMemoize
+ # Will be removed with FF ci_refactoring_external_mapper
FILE_CLASSES = [
External::File::Local,
External::File::Project,
@@ -15,6 +16,7 @@ module Gitlab
External::File::Artifact
].freeze
+ # Will be removed with FF ci_refactoring_external_mapper
FILE_SUBKEYS = FILE_CLASSES.map { |f| f.name.demodulize.downcase }.freeze
Error = Class.new(StandardError)
@@ -22,27 +24,43 @@ module Gitlab
TooManyIncludesError = Class.new(Error)
def initialize(values, context)
- @locations = Array.wrap(values.fetch(:include, []))
+ @locations = Array.wrap(values.fetch(:include, [])).compact
@context = context
end
def process
- return [] if locations.empty?
+ return [] if @locations.empty?
- logger.instrument(:config_mapper_process) do
- process_without_instrumentation
+ context.logger.instrument(:config_mapper_process) do
+ if ::Feature.enabled?(:ci_refactoring_external_mapper, context.project)
+ process_without_instrumentation
+ else
+ legacy_process_without_instrumentation
+ end
end
end
private
- attr_reader :locations, :context
+ attr_reader :context
delegate :expandset, :logger, to: :context
def process_without_instrumentation
- locations
- .compact
+ locations = Normalizer.new(context).process(@locations)
+ locations = Filter.new(context).process(locations)
+ locations = LocationExpander.new(context).process(locations)
+ locations = VariablesExpander.new(context).process(locations)
+
+ files = Matcher.new(context).process(locations)
+ Verifier.new(context).process(files)
+
+ files
+ end
+
+ # This and the following methods will be removed with FF ci_refactoring_external_mapper
+ def legacy_process_without_instrumentation
+ @locations
.map(&method(:normalize_location))
.filter_map(&method(:verify_rules))
.flat_map(&method(:expand_project_files))
@@ -52,14 +70,8 @@ module Gitlab
.each(&method(:verify!))
end
- def normalize_location(location)
- logger.instrument(:config_mapper_normalize) do
- normalize_location_without_instrumentation(location)
- end
- end
-
# convert location if String to canonical form
- def normalize_location_without_instrumentation(location)
+ def normalize_location(location)
if location.is_a?(String)
expanded_location = expand_variables(location)
normalize_location_string(expanded_location)
diff --git a/lib/gitlab/ci/config/external/mapper/base.rb b/lib/gitlab/ci/config/external/mapper/base.rb
new file mode 100644
index 00000000000..d2f56d0b8f6
--- /dev/null
+++ b/lib/gitlab/ci/config/external/mapper/base.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module External
+ class Mapper
+ # Base class for mapper classes
+ class Base
+ def initialize(context)
+ @context = context
+ end
+
+ def process(*args)
+ context.logger.instrument(mapper_instrumentation_key) do
+ process_without_instrumentation(*args)
+ end
+ end
+
+ private
+
+ attr_reader :context
+
+ def process_without_instrumentation
+ raise NotImplementedError
+ end
+
+ def mapper_instrumentation_key
+ "config_mapper_#{self.class.name.demodulize.downcase}".to_sym
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/external/mapper/filter.rb b/lib/gitlab/ci/config/external/mapper/filter.rb
new file mode 100644
index 00000000000..4d2b26c7d98
--- /dev/null
+++ b/lib/gitlab/ci/config/external/mapper/filter.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module External
+ class Mapper
+ # Filters locations according to rules
+ class Filter < Base
+ private
+
+ def process_without_instrumentation(locations)
+ locations.select do |location|
+ Rules.new(location[:rules]).evaluate(context).pass?
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/external/mapper/location_expander.rb b/lib/gitlab/ci/config/external/mapper/location_expander.rb
new file mode 100644
index 00000000000..a4ca058f0d9
--- /dev/null
+++ b/lib/gitlab/ci/config/external/mapper/location_expander.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module External
+ class Mapper
+ # Expands locations to include all files matching the pattern
+ class LocationExpander < Base
+ private
+
+ def process_without_instrumentation(locations)
+ locations.flat_map do |location|
+ if location[:project]
+ expand_project_files(location)
+ elsif location[:local]
+ expand_wildcard_paths(location)
+ else
+ location
+ end
+ end
+ end
+
+ def expand_project_files(location)
+ Array.wrap(location[:file]).map do |file|
+ location.merge(file: file)
+ end
+ end
+
+ def expand_wildcard_paths(location)
+ return location unless location[:local].include?('*')
+
+ context.project.repository.search_files_by_wildcard_path(location[:local], context.sha).map do |path|
+ { local: path }
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/external/mapper/matcher.rb b/lib/gitlab/ci/config/external/mapper/matcher.rb
new file mode 100644
index 00000000000..85e19ff1ced
--- /dev/null
+++ b/lib/gitlab/ci/config/external/mapper/matcher.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module External
+ class Mapper
+ # Matches the first file type that matches the given location
+ class Matcher < Base
+ FILE_CLASSES = [
+ External::File::Local,
+ External::File::Project,
+ External::File::Remote,
+ External::File::Template,
+ External::File::Artifact
+ ].freeze
+
+ FILE_SUBKEYS = FILE_CLASSES.map { |f| f.name.demodulize.downcase }.freeze
+
+ private
+
+ def process_without_instrumentation(locations)
+ locations.map do |location|
+ matching = FILE_CLASSES.map do |file_class|
+ file_class.new(location, context)
+ end.select(&:matching?)
+
+ if matching.one?
+ matching.first
+ elsif matching.empty?
+ raise Mapper::AmbigiousSpecificationError,
+ "`#{masked_location(location.to_json)}` does not have a valid subkey for include. " \
+ "Valid subkeys are: `#{FILE_SUBKEYS.join('`, `')}`"
+ else
+ raise Mapper::AmbigiousSpecificationError,
+ "Each include must use only one of: `#{FILE_SUBKEYS.join('`, `')}`"
+ end
+ end
+ end
+
+ def masked_location(location)
+ context.mask_variables_from(location)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/external/mapper/normalizer.rb b/lib/gitlab/ci/config/external/mapper/normalizer.rb
new file mode 100644
index 00000000000..8fc798e78a0
--- /dev/null
+++ b/lib/gitlab/ci/config/external/mapper/normalizer.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module External
+ class Mapper
+ # Converts locations to canonical form (local:/remote:) if String
+ class Normalizer < Base
+ def initialize(context)
+ super
+
+ @variables_expander = VariablesExpander.new(context)
+ end
+
+ private
+
+ attr_reader :variables_expander
+
+ def process_without_instrumentation(locations)
+ locations.map do |location|
+ if location.is_a?(String)
+ # We need to expand before normalizing because the information of
+ # whether if it's a remote or local path may be hidden inside the variable.
+ location = variables_expander.expand(location)
+
+ normalize_location_string(location)
+ else
+ location.deep_symbolize_keys
+ end
+ end
+ end
+
+ def normalize_location_string(location)
+ if ::Gitlab::UrlSanitizer.valid?(location)
+ { remote: location }
+ else
+ { local: location }
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/external/mapper/variables_expander.rb b/lib/gitlab/ci/config/external/mapper/variables_expander.rb
new file mode 100644
index 00000000000..fddf04984d8
--- /dev/null
+++ b/lib/gitlab/ci/config/external/mapper/variables_expander.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module External
+ class Mapper
+ # Handles variable expansion
+ class VariablesExpander < Base
+ def expand(data)
+ if data.is_a?(String)
+ expand_variable(data)
+ else
+ transform_and_expand_variable(data)
+ end
+ end
+
+ private
+
+ def process_without_instrumentation(locations)
+ locations.map { |location| expand(location) }
+ end
+
+ def transform_and_expand_variable(data)
+ data.transform_values do |values|
+ case values
+ when Array
+ values.map { |value| expand_variable(value.to_s) }
+ when String
+ expand_variable(values)
+ else
+ values
+ end
+ end
+ end
+
+ def expand_variable(data)
+ ExpandVariables.expand(data, -> { variables })
+ end
+
+ def variables
+ @variables ||= context.variables_hash
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/external/mapper/verifier.rb b/lib/gitlab/ci/config/external/mapper/verifier.rb
new file mode 100644
index 00000000000..6d6f227b940
--- /dev/null
+++ b/lib/gitlab/ci/config/external/mapper/verifier.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module External
+ class Mapper
+ # Fetches file contents and verifies them
+ class Verifier < Base
+ private
+
+ def process_without_instrumentation(files)
+ files.select do |file|
+ verify_max_includes!
+ verify_execution_time!
+
+ file.validate!
+
+ context.expandset.add(file)
+ end
+ end
+
+ def verify_max_includes!
+ return if context.expandset.count < context.max_includes
+
+ raise Mapper::TooManyIncludesError, "Maximum of #{context.max_includes} nested includes are allowed!"
+ end
+
+ def verify_execution_time!
+ context.check_execution_time!
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/external/processor.rb b/lib/gitlab/ci/config/external/processor.rb
index 6a4aee26d80..e15b51fbff4 100644
--- a/lib/gitlab/ci/config/external/processor.rb
+++ b/lib/gitlab/ci/config/external/processor.rb
@@ -32,9 +32,7 @@ module Gitlab
def validate_external_files!
@external_files.each do |file|
- logger.instrument(:config_external_verify) do
- raise IncludeError, file.error_message unless file.valid?
- end
+ raise IncludeError, file.error_message unless file.valid?
end
end
diff --git a/lib/gitlab/ci/environment_matcher.rb b/lib/gitlab/ci/environment_matcher.rb
new file mode 100644
index 00000000000..7d7a7742f68
--- /dev/null
+++ b/lib/gitlab/ci/environment_matcher.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class EnvironmentMatcher
+ def initialize(pattern)
+ @pattern = pattern
+ end
+
+ def match?(environment)
+ return false if pattern.blank?
+
+ exact_match?(environment) || wildcard_match?(environment)
+ end
+
+ private
+
+ attr_reader :pattern, :match_type
+
+ def exact_match?(environment)
+ pattern == environment
+ end
+
+ def wildcard_match?(environment)
+ return false unless wildcard?
+
+ wildcard_regex.match?(environment)
+ end
+
+ def wildcard?
+ pattern.include?('*')
+ end
+
+ def wildcard_regex
+ @wildcard_regex ||= Regexp.new(pattern.gsub('*', '.*'))
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/lint.rb b/lib/gitlab/ci/lint.rb
index 51743a1f273..e0112a1b1c2 100644
--- a/lib/gitlab/ci/lint.rb
+++ b/lib/gitlab/ci/lint.rb
@@ -73,7 +73,7 @@ module Gitlab
end
def yaml_processor_result(content, logger)
- logger.instrument(:yaml_process) do
+ logger.instrument(:yaml_process, once: true) do
Gitlab::Ci::YamlProcessor.new(content, project: @project,
user: @current_user,
sha: @sha,
@@ -119,7 +119,7 @@ module Gitlab
environment: job[:environment],
when: job[:when],
allow_failure: job[:allow_failure],
- needs: job.dig(:needs_attributes)
+ needs: job[:needs_attributes]
}
end
end
@@ -130,10 +130,10 @@ module Gitlab
def build_logger
Gitlab::Ci::Pipeline::Logger.new(project: @project) do |l|
l.log_when do |observations|
- values = observations['yaml_process_duration_s']
- next false if values.empty?
+ duration = observations['yaml_process_duration_s']
+ next false unless duration
- values.max >= LOG_MAX_DURATION_THRESHOLD
+ duration >= LOG_MAX_DURATION_THRESHOLD
end
end
end
diff --git a/lib/gitlab/ci/parsers/security/common.rb b/lib/gitlab/ci/parsers/security/common.rb
index 0ac012b9fd1..67817c9f832 100644
--- a/lib/gitlab/ci/parsers/security/common.rb
+++ b/lib/gitlab/ci/parsers/security/common.rb
@@ -65,8 +65,14 @@ module Gitlab
)
end
+ # New Oj parsers are not thread safe, therefore,
+ # we need to initialize them for each thread.
+ def introspect_parser
+ Thread.current[:introspect_parser] ||= Oj::Introspect.new(filter: "remediations")
+ end
+
def report_data
- @report_data ||= Gitlab::Json.parse!(json_data)
+ @report_data ||= introspect_parser.parse(json_data)
end
def report_version
diff --git a/lib/gitlab/ci/pipeline/chain/build/associations.rb b/lib/gitlab/ci/pipeline/chain/build/associations.rb
index b5d63691849..b484a88a381 100644
--- a/lib/gitlab/ci/pipeline/chain/build/associations.rb
+++ b/lib/gitlab/ci/pipeline/chain/build/associations.rb
@@ -31,7 +31,8 @@ module Gitlab
source_pipeline: @command.bridge.pipeline,
source_project: @command.bridge.project,
source_bridge: @command.bridge,
- project: @command.project
+ project: @command.project,
+ source_partition_id: @command.bridge.partition_id
)
end
diff --git a/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb b/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb
index 07a3aff1862..53c8a7ac122 100644
--- a/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb
+++ b/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb
@@ -11,11 +11,10 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord
def perform!
- ff_enabled = Feature.enabled?(:ci_skip_auto_cancelation_on_child_pipelines, project)
- return if ff_enabled && pipeline.parent_pipeline? # skip if child pipeline
+ return if pipeline.parent_pipeline? # skip if child pipeline
return unless project.auto_cancel_pending_pipelines?
- Gitlab::OptimisticLocking.retry_lock(auto_cancelable_pipelines(ff_enabled), name: 'cancel_pending_pipelines') do |cancelables|
+ Gitlab::OptimisticLocking.retry_lock(auto_cancelable_pipelines, name: 'cancel_pending_pipelines') do |cancelables|
cancelables.select(:id).each_batch(of: BATCH_SIZE) do |cancelables_batch|
auto_cancel_interruptible_pipelines(cancelables_batch.ids)
end
@@ -29,19 +28,14 @@ module Gitlab
private
- def auto_cancelable_pipelines(ff_enabled)
- relation = project.all_pipelines
+ def auto_cancelable_pipelines
+ project.all_pipelines
.created_after(1.week.ago)
.ci_and_parent_sources
.for_ref(pipeline.ref)
.where_not_sha(project.commit(pipeline.ref).try(:id))
.alive_or_scheduled
-
- if ff_enabled
- relation.id_not_in(pipeline.id)
- else
- relation.id_not_in(pipeline.same_family_pipeline_ids)
- end
+ .id_not_in(pipeline.id)
end
def auto_cancel_interruptible_pipelines(pipeline_ids)
diff --git a/lib/gitlab/ci/pipeline/chain/command.rb b/lib/gitlab/ci/pipeline/chain/command.rb
index 5ec04b4889e..31b130b5ab7 100644
--- a/lib/gitlab/ci/pipeline/chain/command.rb
+++ b/lib/gitlab/ci/pipeline/chain/command.rb
@@ -98,7 +98,7 @@ module Gitlab
def observe_step_duration(step_class, duration)
step = step_class.name.underscore.parameterize(separator: '_')
- logger.observe("pipeline_step_#{step}_duration_s", duration)
+ logger.observe("pipeline_step_#{step}_duration_s", duration, once: true)
if Feature.enabled?(:ci_pipeline_creation_step_duration_tracking, type: :ops)
metrics.pipeline_creation_step_duration_histogram
@@ -107,14 +107,14 @@ module Gitlab
end
def observe_creation_duration(duration)
- logger.observe(:pipeline_creation_duration_s, duration)
+ logger.observe(:pipeline_creation_duration_s, duration, once: true)
metrics.pipeline_creation_duration_histogram
- .observe({}, duration.seconds)
+ .observe({ gitlab: gitlab_org_project?.to_s }, duration.seconds)
end
def observe_pipeline_size(pipeline)
- logger.observe(:pipeline_size_count, pipeline.total_size)
+ logger.observe(:pipeline_size_count, pipeline.total_size, once: true)
metrics.pipeline_size_histogram
.observe({ source: pipeline.source.to_s, plan: project.actual_plan_name }, pipeline.total_size)
@@ -157,6 +157,10 @@ module Gitlab
def full_git_ref_name_unavailable?
ref == origin_ref
end
+
+ def gitlab_org_project?
+ project.full_path == 'gitlab-org/gitlab'
+ end
end
end
end
diff --git a/lib/gitlab/ci/pipeline/chain/config/process.rb b/lib/gitlab/ci/pipeline/chain/config/process.rb
index 5548fca320f..ad6b2fd3411 100644
--- a/lib/gitlab/ci/pipeline/chain/config/process.rb
+++ b/lib/gitlab/ci/pipeline/chain/config/process.rb
@@ -11,7 +11,7 @@ module Gitlab
def perform!
raise ArgumentError, 'missing config content' unless @command.config_content
- result = logger.instrument(:pipeline_config_process) do
+ result = logger.instrument(:pipeline_config_process, once: true) do
processor = ::Gitlab::Ci::YamlProcessor.new(
@command.config_content, {
project: project,
diff --git a/lib/gitlab/ci/pipeline/chain/create.rb b/lib/gitlab/ci/pipeline/chain/create.rb
index 207b4b5ff8b..d4c4f94c7d3 100644
--- a/lib/gitlab/ci/pipeline/chain/create.rb
+++ b/lib/gitlab/ci/pipeline/chain/create.rb
@@ -9,7 +9,7 @@ module Gitlab
include Gitlab::Utils::StrongMemoize
def perform!
- logger.instrument_with_sql(:pipeline_save) do
+ logger.instrument_once_with_sql(:pipeline_save) do
BulkInsertableAssociations.with_bulk_insert do
::Ci::BulkInsertableTags.with_bulk_insert_tags do
pipeline.transaction do
diff --git a/lib/gitlab/ci/pipeline/chain/ensure_environments.rb b/lib/gitlab/ci/pipeline/chain/ensure_environments.rb
index 1b9dd158733..ebea6a538ef 100644
--- a/lib/gitlab/ci/pipeline/chain/ensure_environments.rb
+++ b/lib/gitlab/ci/pipeline/chain/ensure_environments.rb
@@ -16,7 +16,7 @@ module Gitlab
private
def ensure_environment(build)
- ::Environments::CreateForBuildService.new.execute(build, merge_request: @command.merge_request)
+ ::Environments::CreateForBuildService.new.execute(build)
end
end
end
diff --git a/lib/gitlab/ci/pipeline/chain/seed.rb b/lib/gitlab/ci/pipeline/chain/seed.rb
index feae123f216..ae98c55e425 100644
--- a/lib/gitlab/ci/pipeline/chain/seed.rb
+++ b/lib/gitlab/ci/pipeline/chain/seed.rb
@@ -13,7 +13,7 @@ module Gitlab
raise ArgumentError, 'missing workflow rules result' unless @command.workflow_rules_result
# Allocate next IID. This operation must be outside of transactions of pipeline creations.
- logger.instrument(:pipeline_allocate_seed_attributes) do
+ logger.instrument(:pipeline_allocate_seed_attributes, once: true) do
pipeline.ensure_project_iid!
pipeline.ensure_ci_ref!
end
@@ -25,7 +25,7 @@ module Gitlab
##
# Gather all runtime build/stage errors
#
- seed_errors = logger.instrument(:pipeline_seed_evaluation) do
+ seed_errors = logger.instrument(:pipeline_seed_evaluation, once: true) do
pipeline_seed.errors
end
@@ -44,7 +44,7 @@ module Gitlab
def pipeline_seed
strong_memoize(:pipeline_seed) do
- logger.instrument(:pipeline_seed_initialization) do
+ logger.instrument(:pipeline_seed_initialization, once: true) do
stages_attributes = @command.yaml_processor_result.stages_attributes
Gitlab::Ci::Pipeline::Seed::Pipeline.new(context, stages_attributes)
@@ -61,7 +61,7 @@ module Gitlab
end
def root_variables
- logger.instrument(:pipeline_seed_merge_variables) do
+ strong_memoize(:root_variables) do
::Gitlab::Ci::Variables::Helpers.merge_variables(
@command.yaml_processor_result.root_variables,
@command.workflow_rules_result.variables
diff --git a/lib/gitlab/ci/pipeline/logger.rb b/lib/gitlab/ci/pipeline/logger.rb
index 4b7cbae5004..f393406b549 100644
--- a/lib/gitlab/ci/pipeline/logger.rb
+++ b/lib/gitlab/ci/pipeline/logger.rb
@@ -23,7 +23,7 @@ module Gitlab
log_conditions.push(block)
end
- def instrument(operation)
+ def instrument(operation, once: false)
return yield unless enabled?
raise ArgumentError, 'block not given' unless block_given?
@@ -32,63 +32,72 @@ module Gitlab
result = yield
- observe("#{operation}_duration_s", current_monotonic_time - op_started_at)
+ observe("#{operation}_duration_s", current_monotonic_time - op_started_at, once: once)
result
end
- def instrument_with_sql(operation, &block)
+ def instrument_once_with_sql(operation, &block)
op_start_db_counters = current_db_counter_payload
- result = instrument(operation, &block)
+ result = instrument(operation, once: true, &block)
- observe_sql_counters(operation, op_start_db_counters, current_db_counter_payload)
+ observe_sql_counters(operation, op_start_db_counters, current_db_counter_payload, once: true)
result
end
- def observe(operation, value)
+ def observe(operation, value, once: false)
return unless enabled?
- observations[operation.to_s].push(value)
+ if once
+ observations[operation.to_s] = value
+ else
+ observations[operation.to_s] ||= []
+ observations[operation.to_s].push(value)
+ end
end
def commit(pipeline:, caller:)
return unless log?
- attributes = {
- class: self.class.name.to_s,
- pipeline_creation_caller: caller,
- project_id: project&.id, # project is not available when called from `/ci/lint`
- pipeline_persisted: pipeline.persisted?,
- pipeline_source: pipeline.source,
- pipeline_creation_service_duration_s: age
- }
-
- if pipeline.persisted?
- attributes[:pipeline_builds_tags_count] = pipeline.tags_count
- attributes[:pipeline_builds_distinct_tags_count] = pipeline.distinct_tags_count
- attributes[:pipeline_id] = pipeline.id
+ Gitlab::ApplicationContext.with_context(project: project) do
+ attributes = Gitlab::ApplicationContext.current.merge(
+ class: self.class.name.to_s,
+ pipeline_creation_caller: caller,
+ project_id: project&.id, # project is not available when called from `/ci/lint`
+ pipeline_persisted: pipeline.persisted?,
+ pipeline_source: pipeline.source,
+ pipeline_creation_service_duration_s: age
+ )
+
+ if pipeline.persisted?
+ attributes[:pipeline_builds_tags_count] = pipeline.tags_count
+ attributes[:pipeline_builds_distinct_tags_count] = pipeline.distinct_tags_count
+ attributes[:pipeline_id] = pipeline.id
+ end
+
+ attributes.compact!
+ attributes.stringify_keys!
+ attributes.merge!(observations_hash)
+
+ destination.info(attributes)
end
-
- attributes.compact!
- attributes.stringify_keys!
- attributes.merge!(observations_hash)
-
- destination.info(attributes)
end
def observations_hash
- observations.transform_values do |values|
- next if values.empty?
-
- {
- 'count' => values.size,
- 'min' => values.min,
- 'max' => values.max,
- 'sum' => values.sum,
- 'avg' => values.sum / values.size
- }
+ observations.transform_values do |observation|
+ next if observation.blank?
+
+ if observation.is_a?(Array)
+ {
+ 'count' => observation.size,
+ 'max' => observation.max,
+ 'sum' => observation.sum
+ }
+ else
+ observation
+ end
end.compact
end
@@ -110,21 +119,20 @@ module Gitlab
end
def enabled?
- strong_memoize(:enabled) do
- ::Feature.enabled?(:ci_pipeline_creation_logger, project, type: :ops)
- end
+ ::Feature.enabled?(:ci_pipeline_creation_logger, project, type: :ops)
end
+ strong_memoize_attr :enabled?, :enabled
def observations
- @observations ||= Hash.new { |hash, key| hash[key] = [] }
+ @observations ||= {}
end
- def observe_sql_counters(operation, start_db_counters, end_db_counters)
+ def observe_sql_counters(operation, start_db_counters, end_db_counters, once: false)
end_db_counters.each do |key, value|
result = value - start_db_counters.fetch(key, 0)
next if result == 0
- observe("#{operation}_#{key}", result)
+ observe("#{operation}_#{key}", result, once: once)
end
end
diff --git a/lib/gitlab/ci/pipeline/metrics.rb b/lib/gitlab/ci/pipeline/metrics.rb
index c3e0f043b44..04565beeecc 100644
--- a/lib/gitlab/ci/pipeline/metrics.rb
+++ b/lib/gitlab/ci/pipeline/metrics.rb
@@ -9,7 +9,8 @@ module Gitlab
def self.pipeline_creation_duration_histogram
name = :gitlab_ci_pipeline_creation_duration_seconds
comment = 'Pipeline creation duration'
- labels = {}
+ # @gitlab: boolean value - if project is gitlab-org/gitlab
+ labels = { gitlab: false }
buckets = [0.01, 0.05, 0.1, 0.5, 1.0, 2.0, 5.0, 20.0, 50.0, 240.0]
::Gitlab::Metrics.histogram(name, comment, labels, buckets)
diff --git a/lib/gitlab/ci/pipeline/seed/build.rb b/lib/gitlab/ci/pipeline/seed/build.rb
index 2e4267e986b..b0b79b994c1 100644
--- a/lib/gitlab/ci/pipeline/seed/build.rb
+++ b/lib/gitlab/ci/pipeline/seed/build.rb
@@ -9,12 +9,13 @@ module Gitlab
delegate :dig, to: :@seed_attributes
- def initialize(context, attributes, stages_for_needs_lookup = [])
+ def initialize(context, attributes, stages_for_needs_lookup, stage)
@context = context
@pipeline = context.pipeline
@seed_attributes = attributes
@stages_for_needs_lookup = stages_for_needs_lookup.compact
@needs_attributes = dig(:needs_attributes)
+ @stage = stage
@resource_group_key = attributes.delete(:resource_group_key)
@job_variables = @seed_attributes.delete(:job_variables)
@root_variables_inheritance = @seed_attributes.delete(:root_variables_inheritance) { true }
@@ -33,6 +34,8 @@ module Gitlab
.new(attributes.delete(:cache), @pipeline)
calculate_yaml_variables!
+
+ @processable = initialize_processable
end
def name
@@ -40,21 +43,20 @@ module Gitlab
end
def included?
- strong_memoize(:inclusion) do
- logger.instrument(:pipeline_seed_build_inclusion) do
- if @using_rules
- rules_result.pass?
- elsif @using_only || @using_except
- all_of_only? && none_of_except?
- else
- true
- end
+ logger.instrument(:pipeline_seed_build_inclusion) do
+ if @using_rules
+ rules_result.pass?
+ elsif @using_only || @using_except
+ all_of_only? && none_of_except?
+ else
+ true
end
end
end
+ strong_memoize_attr :included?, :inclusion
def errors
- strong_memoize(:errors) do
+ logger.instrument(:pipeline_seed_build_errors) do
# We check rules errors before checking "included?" because rules affects its inclusion status.
next rules_errors if rules_errors
next unless included?
@@ -62,14 +64,22 @@ module Gitlab
[needs_errors, variable_expansion_errors].compact.flatten
end
end
+ strong_memoize_attr :errors
+ # TODO: Method used only in specs. Replace with `to_resource.attributes` when
+ # the feature flag ci_reuse_build_in_seed_context is removed.
+ # Then remove this method.
def attributes
- @seed_attributes
- .deep_merge(pipeline_attributes)
- .deep_merge(rules_attributes)
- .deep_merge(allow_failure_criteria_attributes)
- .deep_merge(@cache.cache_attributes)
- .deep_merge(runner_tags)
+ if reuse_build_in_seed_context?
+ initial_attributes.deep_merge(evaluated_attributes)
+ else
+ @seed_attributes
+ .deep_merge(pipeline_attributes)
+ .deep_merge(rules_attributes)
+ .deep_merge(allow_failure_criteria_attributes)
+ .deep_merge(@cache.cache_attributes)
+ .deep_merge(runner_tags)
+ end
end
def bridge?
@@ -80,12 +90,30 @@ module Gitlab
end
def to_resource
- strong_memoize(:resource) do
- initialize_processable
+ logger.instrument(:pipeline_seed_build_to_resource) do
+ if reuse_build_in_seed_context?
+ # The `options` attribute need to be entirely reassigned because they may
+ # be overridden by evaluated_attributes.
+ # We also don't want to reassign all the `initial_attributes` since those
+ # can affect performance. We only want to assign what's changed.
+ assignable_attributes = initial_attributes.slice(:options)
+ .deep_merge(evaluated_attributes)
+ processable.assign_attributes(assignable_attributes)
+ processable
+ else
+ legacy_initialize_processable
+ end
end
end
+ strong_memoize_attr :to_resource
- def initialize_processable
+ private
+
+ attr_reader :processable
+
+ delegate :logger, to: :@context
+
+ def legacy_initialize_processable
if bridge?
::Ci::Bridge.new(attributes)
else
@@ -93,9 +121,28 @@ module Gitlab
end
end
- private
+ def initialize_processable
+ return unless reuse_build_in_seed_context?
- delegate :logger, to: :@context
+ if bridge?
+ ::Ci::Bridge.new(initial_attributes)
+ else
+ ::Ci::Build.new(initial_attributes)
+ end
+ end
+
+ def initial_attributes
+ @seed_attributes
+ .deep_merge(pipeline_attributes)
+ .deep_merge(ci_stage: @stage)
+ .deep_merge(@cache.cache_attributes)
+ end
+
+ def evaluated_attributes
+ rules_attributes
+ .deep_merge(allow_failure_criteria_attributes)
+ .deep_merge(runner_tags)
+ end
def all_of_only?
@only.all? { |spec| spec.satisfied_by?(@pipeline, evaluate_context) }
@@ -155,40 +202,39 @@ module Gitlab
end
def rules_attributes
- strong_memoize(:rules_attributes) do
- next {} unless @using_rules
+ return {} unless @using_rules
- rules_variables_result = ::Gitlab::Ci::Variables::Helpers.merge_variables(
- @seed_attributes[:yaml_variables], rules_result.variables
- )
+ rules_variables_result = ::Gitlab::Ci::Variables::Helpers.merge_variables(
+ @seed_attributes[:yaml_variables], rules_result.variables
+ )
- rules_result.build_attributes.merge(yaml_variables: rules_variables_result)
- end
+ rules_result.build_attributes.merge(yaml_variables: rules_variables_result)
end
+ strong_memoize_attr :rules_attributes
def rules_result
- strong_memoize(:rules_result) do
- @rules.evaluate(@pipeline, evaluate_context)
- end
+ @rules.evaluate(@pipeline, evaluate_context)
end
+ strong_memoize_attr :rules_result
def rules_errors
- strong_memoize(:rules_errors) do
- ["Failed to parse rule for #{name}: #{rules_result.errors.join(', ')}"] if rules_result.errors.present?
- end
+ ["Failed to parse rule for #{name}: #{rules_result.errors.join(', ')}"] if rules_result.errors.present?
end
+ strong_memoize_attr :rules_errors
def evaluate_context
- strong_memoize(:evaluate_context) do
+ if reuse_build_in_seed_context?
+ Gitlab::Ci::Build::Context::Build.new(@pipeline, @seed_attributes, processable)
+ else
Gitlab::Ci::Build::Context::Build.new(@pipeline, @seed_attributes)
end
end
+ strong_memoize_attr :evaluate_context
def runner_tags
- strong_memoize(:runner_tags) do
- { tag_list: evaluate_runner_tags }.compact
- end
+ { tag_list: evaluate_runner_tags }.compact
end
+ strong_memoize_attr :runner_tags
def evaluate_runner_tags
@seed_attributes.delete(:tag_list)&.map do |tag|
@@ -211,6 +257,11 @@ module Gitlab
from: @context.root_variables, to: @job_variables, inheritance: @root_variables_inheritance
)
end
+
+ def reuse_build_in_seed_context?
+ Feature.enabled?(:ci_reuse_build_in_seed_context, @pipeline.project)
+ end
+ strong_memoize_attr :reuse_build_in_seed_context?, :reuse_build_in_seed_context
end
end
end
diff --git a/lib/gitlab/ci/pipeline/seed/build/cache.rb b/lib/gitlab/ci/pipeline/seed/build/cache.rb
index 78ffaaa7e81..781065a63db 100644
--- a/lib/gitlab/ci/pipeline/seed/build/cache.rb
+++ b/lib/gitlab/ci/pipeline/seed/build/cache.rb
@@ -6,7 +6,7 @@ module Gitlab
module Seed
class Build
class Cache
- def initialize(pipeline, cache)
+ def initialize(pipeline, cache, custom_key_prefix)
@pipeline = pipeline
local_cache = cache.to_h.deep_dup
@key = local_cache.delete(:key)
@@ -14,6 +14,7 @@ module Gitlab
@policy = local_cache.delete(:policy)
@untracked = local_cache.delete(:untracked)
@when = local_cache.delete(:when)
+ @custom_key_prefix = custom_key_prefix
raise ArgumentError, "unknown cache keys: #{local_cache.keys}" if local_cache.any?
end
@@ -45,6 +46,7 @@ module Gitlab
def key_from_files
return unless @key.is_a?(Hash)
+ @key[:prefix] ||= @custom_key_prefix.to_s
[@key[:prefix], files_digest].select(&:present?).join('-')
end
diff --git a/lib/gitlab/ci/pipeline/seed/pipeline.rb b/lib/gitlab/ci/pipeline/seed/pipeline.rb
index 9e609debeed..57ad2546f1c 100644
--- a/lib/gitlab/ci/pipeline/seed/pipeline.rb
+++ b/lib/gitlab/ci/pipeline/seed/pipeline.rb
@@ -38,8 +38,10 @@ module Gitlab
private
+ delegate :logger, to: :@context
+
def stage_seeds
- strong_memoize(:stage_seeds) do
+ logger.instrument(:pipeline_seed_stage_seeds) do
seeds = @stages_attributes.inject([]) do |previous_stages, attributes|
seed = Gitlab::Ci::Pipeline::Seed::Stage.new(@context, attributes, previous_stages)
previous_stages + [seed]
@@ -48,6 +50,7 @@ module Gitlab
seeds.select(&:included?)
end
end
+ strong_memoize_attr :stage_seeds
end
end
end
diff --git a/lib/gitlab/ci/pipeline/seed/stage.rb b/lib/gitlab/ci/pipeline/seed/stage.rb
index 1c4247bd5ee..c3e94529634 100644
--- a/lib/gitlab/ci/pipeline/seed/stage.rb
+++ b/lib/gitlab/ci/pipeline/seed/stage.rb
@@ -10,54 +10,49 @@ module Gitlab
delegate :size, to: :seeds
delegate :dig, to: :seeds
- def initialize(context, attributes, previous_stages)
- @context = context
- @pipeline = context.pipeline
- @attributes = attributes
- @previous_stages = previous_stages
-
- @builds = attributes.fetch(:builds).map do |attributes|
- Seed::Build.new(context, attributes, previous_stages + [self])
+ attr_reader :attributes
+
+ def initialize(context, stage_attributes, previous_stages)
+ pipeline = context.pipeline
+ @attributes = {
+ name: stage_attributes.fetch(:name),
+ position: stage_attributes.fetch(:index),
+ pipeline: pipeline,
+ project: pipeline.project,
+ partition_id: pipeline.partition_id
+ }
+
+ @stage = ::Ci::Stage.new(@attributes)
+
+ @builds = stage_attributes.fetch(:builds).map do |build_attributes|
+ Seed::Build.new(context, build_attributes, previous_stages + [self], @stage)
end
end
- def attributes
- { name: @attributes.fetch(:name),
- position: @attributes.fetch(:index),
- pipeline: @pipeline,
- project: @pipeline.project,
- partition_id: @pipeline.partition_id }
- end
-
def seeds
- strong_memoize(:seeds) do
- @builds.select(&:included?)
- end
+ @builds.select(&:included?)
end
+ strong_memoize_attr :seeds
def errors
- strong_memoize(:errors) do
- @builds.flat_map(&:errors).compact
- end
+ @builds.flat_map(&:errors).compact
end
+ strong_memoize_attr :errors
def seeds_names
- strong_memoize(:seeds_names) do
- seeds.map(&:name).to_set
- end
+ seeds.map(&:name).to_set
end
+ strong_memoize_attr :seeds_names
def included?
seeds.any?
end
def to_resource
- strong_memoize(:stage) do
- ::Ci::Stage.new(attributes).tap do |stage|
- stage.statuses = seeds.map(&:to_resource)
- end
- end
+ @stage.statuses = seeds.map(&:to_resource)
+ @stage
end
+ strong_memoize_attr :to_resource
end
end
end
diff --git a/lib/gitlab/ci/reports/security/finding.rb b/lib/gitlab/ci/reports/security/finding.rb
index dd9b9cc6d55..92a91854358 100644
--- a/lib/gitlab/ci/reports/security/finding.rb
+++ b/lib/gitlab/ci/reports/security/finding.rb
@@ -83,8 +83,8 @@ module Gitlab
message
cve
solution
- ].each_with_object({}) do |key, hash|
- hash[key] = public_send(key) # rubocop:disable GitlabSecurity/PublicSend
+ ].index_with do |key|
+ public_send(key) # rubocop:disable GitlabSecurity/PublicSend
end
end
@@ -98,7 +98,7 @@ module Gitlab
end
def unsafe?(severity_levels, report_types)
- severity.to_s.in?(severity_levels) && (report_types.blank? || report_type.to_s.in?(report_types) )
+ severity.to_s.in?(severity_levels) && (report_types.blank? || report_type.to_s.in?(report_types))
end
def eql?(other)
diff --git a/lib/gitlab/ci/reports/security/finding_key.rb b/lib/gitlab/ci/reports/security/finding_key.rb
index ad047fbf904..d42a0ea5b2e 100644
--- a/lib/gitlab/ci/reports/security/finding_key.rb
+++ b/lib/gitlab/ci/reports/security/finding_key.rb
@@ -15,7 +15,7 @@ module Gitlab
has_fingerprints? && other.has_fingerprints? &&
location_fingerprint == other.location_fingerprint &&
- identifier_fingerprint == other.identifier_fingerprint
+ identifier_fingerprint == other.identifier_fingerprint
end
def hash
diff --git a/lib/gitlab/ci/reports/security/identifier.rb b/lib/gitlab/ci/reports/security/identifier.rb
index 4ba943cdcbc..0ff6be6acc4 100644
--- a/lib/gitlab/ci/reports/security/identifier.rb
+++ b/lib/gitlab/ci/reports/security/identifier.rb
@@ -31,8 +31,8 @@ module Gitlab
fingerprint
name
url
- ].each_with_object({}) do |key, hash|
- hash[key] = public_send(key) # rubocop:disable GitlabSecurity/PublicSend
+ ].index_with do |key|
+ public_send(key) # rubocop:disable GitlabSecurity/PublicSend
end
end
diff --git a/lib/gitlab/ci/reports/security/reports.rb b/lib/gitlab/ci/reports/security/reports.rb
index 5c08381d5cc..8425881a4ab 100644
--- a/lib/gitlab/ci/reports/security/reports.rb
+++ b/lib/gitlab/ci/reports/security/reports.rb
@@ -21,29 +21,6 @@ module Gitlab
def findings
reports.values.flat_map(&:findings)
end
-
- def violates_default_policy_against?(target_reports, vulnerabilities_allowed, severity_levels, vulnerability_states, report_types = [])
- if Feature.enabled?(:require_approval_on_scan_removal, pipeline.project) && scan_removed?(target_reports)
- return true
- end
-
- unsafe_findings_count(target_reports, severity_levels, vulnerability_states, report_types) > vulnerabilities_allowed
- end
-
- def unsafe_findings_uuids(severity_levels, report_types)
- findings.select { |finding| finding.unsafe?(severity_levels, report_types) }.map(&:uuid)
- end
-
- private
-
- def unsafe_findings_count(target_reports, severity_levels, vulnerability_states, report_types)
- new_uuids = unsafe_findings_uuids(severity_levels, report_types) - target_reports&.unsafe_findings_uuids(severity_levels, report_types).to_a
- new_uuids.count
- end
-
- def scan_removed?(target_reports)
- (target_reports&.reports&.keys.to_a - reports.keys).any?
- end
end
end
end
diff --git a/lib/gitlab/ci/reports/test_suite.rb b/lib/gitlab/ci/reports/test_suite.rb
index d0388c65f58..dcc593b4403 100644
--- a/lib/gitlab/ci/reports/test_suite.rb
+++ b/lib/gitlab/ci/reports/test_suite.rb
@@ -96,8 +96,8 @@ module Gitlab
end
def sort_by_execution_time_desc
- @test_cases = @test_cases.keys.each_with_object({}) do |key, hash|
- hash[key] = @test_cases[key].sort_by { |_key, test_case| -test_case.execution_time }.to_h
+ @test_cases = @test_cases.keys.index_with do |key|
+ @test_cases[key].sort_by { |_key, test_case| -test_case.execution_time }.to_h
end
end
end
diff --git a/lib/gitlab/ci/runner_instructions.rb b/lib/gitlab/ci/runner_instructions.rb
index 68c911d3dbb..bcda2fec5ba 100644
--- a/lib/gitlab/ci/runner_instructions.rb
+++ b/lib/gitlab/ci/runner_instructions.rb
@@ -22,7 +22,8 @@ module Gitlab
osx: {
human_readable_name: "macOS",
download_locations: {
- amd64: "https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64"
+ amd64: "https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64",
+ arm64: "https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-arm64"
},
install_script_template_path: "lib/gitlab/ci/runner_instructions/templates/osx/install.sh",
runner_executable: "gitlab-runner"
@@ -61,7 +62,7 @@ module Gitlab
def install_script
with_error_handling [Gitlab::Ci::RunnerInstructions::ArgumentError] do
- raise Gitlab::Ci::RunnerInstructions::ArgumentError, s_('Architecture not found for OS') unless environment[:download_locations].key?(@arch.to_sym)
+ raise Gitlab::Ci::RunnerInstructions::ArgumentError, _('Architecture not found for OS') unless environment[:download_locations].key?(@arch.to_sym)
replace_variables(get_file(environment[:install_script_template_path]))
end
@@ -69,7 +70,7 @@ module Gitlab
def register_command
with_error_handling [Gitlab::Ci::RunnerInstructions::ArgumentError, Gitlab::Access::AccessDeniedError] do
- raise Gitlab::Ci::RunnerInstructions::ArgumentError, s_('No runner executable') unless environment[:runner_executable]
+ raise Gitlab::Ci::RunnerInstructions::ArgumentError, _('No runner executable') unless environment[:runner_executable]
server_url = Gitlab::Routing.url_helpers.root_url(only_path: false)
runner_executable = environment[:runner_executable]
@@ -90,12 +91,12 @@ module Gitlab
end
def environment
- @environment ||= OS[@os.to_sym] || ( raise Gitlab::Ci::RunnerInstructions::ArgumentError, s_('Invalid OS') )
+ @environment ||= OS[@os.to_sym] || (raise Gitlab::Ci::RunnerInstructions::ArgumentError, _('Invalid OS'))
end
def validate_params
- @errors << s_('Missing OS') unless @os.present?
- @errors << s_('Missing arch') unless @arch.present?
+ @errors << _('Missing OS') unless @os.present?
+ @errors << _('Missing arch') unless @arch.present?
end
def replace_variables(expression)
diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
index fddcc1492a8..11420b05dfb 100644
--- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
@@ -177,11 +177,11 @@ include:
- template: Jobs/Browser-Performance-Testing.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml
- template: Jobs/Helm-2to3.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Helm-2to3.gitlab-ci.yml
- template: Security/DAST.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml
- - template: Security/Container-Scanning.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
- - template: Security/Dependency-Scanning.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
- - template: Security/License-Scanning.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/License-Scanning.gitlab-ci.yml
- - template: Security/SAST.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
- - template: Security/Secret-Detection.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/Secret-Detection.gitlab-ci.yml
+ - template: Jobs/Container-Scanning.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Container-Scanning.gitlab-ci.yml
+ - template: Jobs/Dependency-Scanning.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Dependency-Scanning.gitlab-ci.yml
+ - template: Jobs/License-Scanning.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/License-Scanning.gitlab-ci.yml
+ - template: Jobs/SAST.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml
+ - template: Jobs/Secret-Detection.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Secret-Detection.gitlab-ci.yml
# The latest build job generates a dotenv report artifact with a CI_APPLICATION_TAG
# that also includes the image digest. This configures Auto Deploy to receive
diff --git a/lib/gitlab/ci/templates/Gradle.gitlab-ci.yml b/lib/gitlab/ci/templates/Gradle.gitlab-ci.yml
index 671925c5df6..16ce85548df 100644
--- a/lib/gitlab/ci/templates/Gradle.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Gradle.gitlab-ci.yml
@@ -11,13 +11,6 @@
image: gradle:alpine
-# Disable the Gradle daemon for Continuous Integration servers as correctness
-# is usually a priority over speed in CI environments. Using a fresh
-# runtime for each build is more reliable since the runtime is completely
-# isolated from any previous builds.
-variables:
- GRADLE_OPTS: "-Dorg.gradle.daemon=false"
-
before_script:
- GRADLE_USER_HOME="$(pwd)/.gradle"
- export GRADLE_USER_HOME
diff --git a/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml
index fcf2ac7de7a..026ddf4a17a 100644
--- a/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml
@@ -7,7 +7,7 @@ browser_performance:
variables:
DOCKER_TLS_CERTDIR: ""
SITESPEED_IMAGE: sitespeedio/sitespeed.io
- SITESPEED_VERSION: 14.1.0
+ SITESPEED_VERSION: 26.1.0
SITESPEED_OPTIONS: ''
services:
- name: 'docker:20.10.12-dind'
diff --git a/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.latest.gitlab-ci.yml
index 04b7dacf2dd..218c2f79e6a 100644
--- a/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.latest.gitlab-ci.yml
@@ -7,7 +7,7 @@ browser_performance:
variables:
DOCKER_TLS_CERTDIR: ""
SITESPEED_IMAGE: sitespeedio/sitespeed.io
- SITESPEED_VERSION: 14.1.0
+ SITESPEED_VERSION: latest
SITESPEED_OPTIONS: ''
services:
- name: 'docker:20.10.12-dind'
diff --git a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
index 23efed212f8..b4beeb60dfd 100644
--- a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
@@ -8,7 +8,8 @@ code_quality:
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
- CODE_QUALITY_IMAGE: "$CI_TEMPLATE_REGISTRY_HOST/gitlab-org/ci-cd/codequality:0.87.0"
+ CODE_QUALITY_IMAGE_TAG: "0.87.3"
+ CODE_QUALITY_IMAGE: "$CI_TEMPLATE_REGISTRY_HOST/gitlab-org/ci-cd/codequality:$CODE_QUALITY_IMAGE_TAG"
needs: []
script:
- export SOURCE_CODE=$PWD
diff --git a/lib/gitlab/ci/templates/Jobs/Container-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Container-Scanning.gitlab-ci.yml
new file mode 100644
index 00000000000..fa609afc5a8
--- /dev/null
+++ b/lib/gitlab/ci/templates/Jobs/Container-Scanning.gitlab-ci.yml
@@ -0,0 +1,54 @@
+# To contribute improvements to CI/CD templates, please follow the Development guide at:
+# https://docs.gitlab.com/ee/development/cicd/templates.html
+# This specific template is located at:
+# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Container-Scanning.gitlab-ci.yml
+
+# Use this template to enable container scanning in your project.
+# You should add this template to an existing `.gitlab-ci.yml` file by using the `include:`
+# keyword.
+# The template should work without modifications but you can customize the template settings if
+# needed: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
+#
+# Requirements:
+# - A `test` stage to be present in the pipeline.
+# - You must define the image to be scanned in the CS_IMAGE variable. If CS_IMAGE is the
+# same as $CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG, you can skip this.
+# - Container registry credentials defined by `CS_REGISTRY_USER` and `CS_REGISTRY_PASSWORD` variables if the
+# image to be scanned is in a private registry.
+# - For auto-remediation, a readable Dockerfile in the root of the project or as defined by the
+# CS_DOCKERFILE_PATH variable.
+#
+# Configure container scanning with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html).
+# List of available variables: https://docs.gitlab.com/ee/user/application_security/container_scanning/#available-variables
+
+variables:
+ CS_ANALYZER_IMAGE: "$CI_TEMPLATE_REGISTRY_HOST/security-products/container-scanning:5"
+
+container_scanning:
+ image: "$CS_ANALYZER_IMAGE$CS_IMAGE_SUFFIX"
+ stage: test
+ variables:
+ # To provide a `vulnerability-allowlist.yml` file, override the GIT_STRATEGY variable in your
+ # `.gitlab-ci.yml` file and set it to `fetch`.
+ # For details, see the following links:
+ # https://docs.gitlab.com/ee/user/application_security/container_scanning/index.html#overriding-the-container-scanning-template
+ # https://docs.gitlab.com/ee/user/application_security/container_scanning/#vulnerability-allowlisting
+ GIT_STRATEGY: none
+ allow_failure: true
+ artifacts:
+ reports:
+ container_scanning: gl-container-scanning-report.json
+ dependency_scanning: gl-dependency-scanning-report.json
+ paths: [gl-container-scanning-report.json, gl-dependency-scanning-report.json]
+ dependencies: []
+ script:
+ - gtcs scan
+ rules:
+ - if: $CONTAINER_SCANNING_DISABLED
+ when: never
+ - if: $CI_COMMIT_BRANCH &&
+ $CI_GITLAB_FIPS_MODE == "true" &&
+ $CS_ANALYZER_IMAGE !~ /-(fips|ubi)\z/
+ variables:
+ CS_IMAGE_SUFFIX: -fips
+ - if: $CI_COMMIT_BRANCH
diff --git a/lib/gitlab/ci/templates/Jobs/Container-Scanning.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Container-Scanning.latest.gitlab-ci.yml
new file mode 100644
index 00000000000..f750bda2a3f
--- /dev/null
+++ b/lib/gitlab/ci/templates/Jobs/Container-Scanning.latest.gitlab-ci.yml
@@ -0,0 +1,68 @@
+# To contribute improvements to CI/CD templates, please follow the Development guide at:
+# https://docs.gitlab.com/ee/development/cicd/templates.html
+# This specific template is located at:
+# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Container-Scanning.latest.gitlab-ci.yml
+
+# Use this template to enable container scanning in your project.
+# You should add this template to an existing `.gitlab-ci.yml` file by using the `include:`
+# keyword.
+# The template should work without modifications but you can customize the template settings if
+# needed: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
+#
+# Requirements:
+# - A `test` stage to be present in the pipeline.
+# - You must define the image to be scanned in the CS_IMAGE variable. If CS_IMAGE is the
+# same as $CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG, you can skip this.
+# - Container registry credentials defined by `CS_REGISTRY_USER` and `CS_REGISTRY_PASSWORD` variables if the
+# image to be scanned is in a private registry.
+# - For auto-remediation, a readable Dockerfile in the root of the project or as defined by the
+# CS_DOCKERFILE_PATH variable.
+#
+# Configure container scanning with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html).
+# List of available variables: https://docs.gitlab.com/ee/user/application_security/container_scanning/#available-variables
+
+variables:
+ CS_ANALYZER_IMAGE: "$CI_TEMPLATE_REGISTRY_HOST/security-products/container-scanning:5"
+
+container_scanning:
+ image: "$CS_ANALYZER_IMAGE$CS_IMAGE_SUFFIX"
+ stage: test
+ variables:
+ # To provide a `vulnerability-allowlist.yml` file, override the GIT_STRATEGY variable in your
+ # `.gitlab-ci.yml` file and set it to `fetch`.
+ # For details, see the following links:
+ # https://docs.gitlab.com/ee/user/application_security/container_scanning/index.html#overriding-the-container-scanning-template
+ # https://docs.gitlab.com/ee/user/application_security/container_scanning/#vulnerability-allowlisting
+ GIT_STRATEGY: none
+ allow_failure: true
+ artifacts:
+ reports:
+ container_scanning: gl-container-scanning-report.json
+ dependency_scanning: gl-dependency-scanning-report.json
+ paths: [gl-container-scanning-report.json, gl-dependency-scanning-report.json]
+ dependencies: []
+ script:
+ - gtcs scan
+ rules:
+ - if: $CONTAINER_SCANNING_DISABLED
+ when: never
+
+ # Add the job to merge request pipelines if there's an open merge request.
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
+ $CI_GITLAB_FIPS_MODE == "true" &&
+ $CS_ANALYZER_IMAGE !~ /-(fips|ubi)\z/
+ variables:
+ CS_IMAGE_SUFFIX: -fips
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+
+ # Don't add it to a *branch* pipeline if it's already in a merge request pipeline.
+ - if: $CI_OPEN_MERGE_REQUESTS
+ when: never
+
+ # Add the job to branch pipelines.
+ - if: $CI_COMMIT_BRANCH &&
+ $CI_GITLAB_FIPS_MODE == "true" &&
+ $CS_ANALYZER_IMAGE !~ /-(fips|ubi)\z/
+ variables:
+ CS_IMAGE_SUFFIX: -fips
+ - if: $CI_COMMIT_BRANCH
diff --git a/lib/gitlab/ci/templates/Jobs/Load-Performance-Testing.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Load-Performance-Testing.gitlab-ci.yml
index 936d8751fe1..12105e0e95d 100644
--- a/lib/gitlab/ci/templates/Jobs/Load-Performance-Testing.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Load-Performance-Testing.gitlab-ci.yml
@@ -4,8 +4,8 @@ load_performance:
allow_failure: true
variables:
DOCKER_TLS_CERTDIR: ""
- K6_IMAGE: loadimpact/k6
- K6_VERSION: 0.27.0
+ K6_IMAGE: grafana/k6
+ K6_VERSION: 0.41.0
K6_TEST_FILE: raw.githubusercontent.com/grafana/k6/master/samples/http_get.js
K6_OPTIONS: ''
K6_DOCKER_OPTIONS: ''
diff --git a/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml
index a6d47e31de2..2c5027cdb43 100644
--- a/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml
@@ -238,6 +238,8 @@ semgrep-sast:
- '**/*.java'
- '**/*.cs'
- '**/*.html'
+ - '**/*.scala'
+ - '**/*.sc'
sobelow-sast:
extends: .sast-analyzer
diff --git a/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml
index 4600468ef30..58709d3ab62 100644
--- a/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml
@@ -299,6 +299,8 @@ semgrep-sast:
- '**/*.java'
- '**/*.html'
- '**/*.cs'
+ - '**/*.scala'
+ - '**/*.sc'
- if: $CI_OPEN_MERGE_REQUESTS # Don't add it to a *branch* pipeline if it's already in a merge request pipeline.
when: never
- if: $CI_COMMIT_BRANCH # If there's no open merge request, add it to a *branch* pipeline instead.
@@ -313,6 +315,8 @@ semgrep-sast:
- '**/*.java'
- '**/*.html'
- '**/*.cs'
+ - '**/*.scala'
+ - '**/*.sc'
sobelow-sast:
extends: .sast-analyzer
diff --git a/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
index 79a08c33fdf..879d6a7a468 100644
--- a/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
@@ -1,54 +1,5 @@
-# To contribute improvements to CI/CD templates, please follow the Development guide at:
-# https://docs.gitlab.com/ee/development/cicd/templates.html
-# This specific template is located at:
-# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
+# This template moved to Jobs/Container-Scanning.gitlab-ci.yml in GitLab 15.6
+# Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/381665
-# Use this template to enable container scanning in your project.
-# You should add this template to an existing `.gitlab-ci.yml` file by using the `include:`
-# keyword.
-# The template should work without modifications but you can customize the template settings if
-# needed: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
-#
-# Requirements:
-# - A `test` stage to be present in the pipeline.
-# - You must define the image to be scanned in the CS_IMAGE variable. If CS_IMAGE is the
-# same as $CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG, you can skip this.
-# - Container registry credentials defined by `CS_REGISTRY_USER` and `CS_REGISTRY_PASSWORD` variables if the
-# image to be scanned is in a private registry.
-# - For auto-remediation, a readable Dockerfile in the root of the project or as defined by the
-# CS_DOCKERFILE_PATH variable.
-#
-# Configure container scanning with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html).
-# List of available variables: https://docs.gitlab.com/ee/user/application_security/container_scanning/#available-variables
-
-variables:
- CS_ANALYZER_IMAGE: "$CI_TEMPLATE_REGISTRY_HOST/security-products/container-scanning:5"
-
-container_scanning:
- image: "$CS_ANALYZER_IMAGE$CS_IMAGE_SUFFIX"
- stage: test
- variables:
- # To provide a `vulnerability-allowlist.yml` file, override the GIT_STRATEGY variable in your
- # `.gitlab-ci.yml` file and set it to `fetch`.
- # For details, see the following links:
- # https://docs.gitlab.com/ee/user/application_security/container_scanning/index.html#overriding-the-container-scanning-template
- # https://docs.gitlab.com/ee/user/application_security/container_scanning/#vulnerability-allowlisting
- GIT_STRATEGY: none
- allow_failure: true
- artifacts:
- reports:
- container_scanning: gl-container-scanning-report.json
- dependency_scanning: gl-dependency-scanning-report.json
- paths: [gl-container-scanning-report.json, gl-dependency-scanning-report.json]
- dependencies: []
- script:
- - gtcs scan
- rules:
- - if: $CONTAINER_SCANNING_DISABLED
- when: never
- - if: $CI_COMMIT_BRANCH &&
- $CI_GITLAB_FIPS_MODE == "true" &&
- $CS_ANALYZER_IMAGE !~ /-(fips|ubi)\z/
- variables:
- CS_IMAGE_SUFFIX: -fips
- - if: $CI_COMMIT_BRANCH
+include:
+ template: Jobs/Container-Scanning.gitlab-ci.yml
diff --git a/lib/gitlab/ci/templates/Security/Container-Scanning.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Container-Scanning.latest.gitlab-ci.yml
index f7b1d12b3b3..7a4f451314e 100644
--- a/lib/gitlab/ci/templates/Security/Container-Scanning.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Container-Scanning.latest.gitlab-ci.yml
@@ -1,68 +1,5 @@
-# To contribute improvements to CI/CD templates, please follow the Development guide at:
-# https://docs.gitlab.com/ee/development/cicd/templates.html
-# This specific template is located at:
-# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
+# This template moved to Jobs/Container-Scanning.latest.gitlab-ci.yml in GitLab 15.6
+# Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/381665
-# Use this template to enable container scanning in your project.
-# You should add this template to an existing `.gitlab-ci.yml` file by using the `include:`
-# keyword.
-# The template should work without modifications but you can customize the template settings if
-# needed: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
-#
-# Requirements:
-# - A `test` stage to be present in the pipeline.
-# - You must define the image to be scanned in the CS_IMAGE variable. If CS_IMAGE is the
-# same as $CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG, you can skip this.
-# - Container registry credentials defined by `CS_REGISTRY_USER` and `CS_REGISTRY_PASSWORD` variables if the
-# image to be scanned is in a private registry.
-# - For auto-remediation, a readable Dockerfile in the root of the project or as defined by the
-# CS_DOCKERFILE_PATH variable.
-#
-# Configure container scanning with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html).
-# List of available variables: https://docs.gitlab.com/ee/user/application_security/container_scanning/#available-variables
-
-variables:
- CS_ANALYZER_IMAGE: "$CI_TEMPLATE_REGISTRY_HOST/security-products/container-scanning:5"
-
-container_scanning:
- image: "$CS_ANALYZER_IMAGE$CS_IMAGE_SUFFIX"
- stage: test
- variables:
- # To provide a `vulnerability-allowlist.yml` file, override the GIT_STRATEGY variable in your
- # `.gitlab-ci.yml` file and set it to `fetch`.
- # For details, see the following links:
- # https://docs.gitlab.com/ee/user/application_security/container_scanning/index.html#overriding-the-container-scanning-template
- # https://docs.gitlab.com/ee/user/application_security/container_scanning/#vulnerability-allowlisting
- GIT_STRATEGY: none
- allow_failure: true
- artifacts:
- reports:
- container_scanning: gl-container-scanning-report.json
- dependency_scanning: gl-dependency-scanning-report.json
- paths: [gl-container-scanning-report.json, gl-dependency-scanning-report.json]
- dependencies: []
- script:
- - gtcs scan
- rules:
- - if: $CONTAINER_SCANNING_DISABLED
- when: never
-
- # Add the job to merge request pipelines if there's an open merge request.
- - if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
- $CI_GITLAB_FIPS_MODE == "true" &&
- $CS_ANALYZER_IMAGE !~ /-(fips|ubi)\z/
- variables:
- CS_IMAGE_SUFFIX: -fips
- - if: $CI_PIPELINE_SOURCE == "merge_request_event"
-
- # Don't add it to a *branch* pipeline if it's already in a merge request pipeline.
- - if: $CI_OPEN_MERGE_REQUESTS
- when: never
-
- # Add the job to branch pipelines.
- - if: $CI_COMMIT_BRANCH &&
- $CI_GITLAB_FIPS_MODE == "true" &&
- $CS_ANALYZER_IMAGE !~ /-(fips|ubi)\z/
- variables:
- CS_IMAGE_SUFFIX: -fips
- - if: $CI_COMMIT_BRANCH
+include:
+ template: Jobs/Container-Scanning.latest.gitlab-ci.yml
diff --git a/lib/gitlab/ci/templates/Security/Coverage-Fuzzing.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Coverage-Fuzzing.gitlab-ci.yml
index d933007ec61..89944e347f6 100644
--- a/lib/gitlab/ci/templates/Security/Coverage-Fuzzing.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Coverage-Fuzzing.gitlab-ci.yml
@@ -16,7 +16,7 @@ variables:
COVFUZZ_VERSION: v3
# This is for users who have an offline environment and will have to replicate gitlab-cov-fuzz release binaries
# to their own servers
- COVFUZZ_URL_PREFIX: "https://gitlab.com/gitlab-org/security-products/analyzers/gitlab-cov-fuzz/-/raw"
+ COVFUZZ_URL_PREFIX: "https://gitlab.com/security-products/gitlab-cov-fuzz/-/raw"
coverage_fuzzing_unlicensed:
diff --git a/lib/gitlab/ci/templates/Security/Coverage-Fuzzing.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Coverage-Fuzzing.latest.gitlab-ci.yml
index feed4c47157..4f6ba427058 100644
--- a/lib/gitlab/ci/templates/Security/Coverage-Fuzzing.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Coverage-Fuzzing.latest.gitlab-ci.yml
@@ -16,7 +16,7 @@ variables:
COVFUZZ_VERSION: v3
# This is for users who have an offline environment and will have to replicate gitlab-cov-fuzz release binaries
# to their own servers
- COVFUZZ_URL_PREFIX: "https://gitlab.com/gitlab-org/security-products/analyzers/gitlab-cov-fuzz/-/raw"
+ COVFUZZ_URL_PREFIX: "https://gitlab.com/security-products/gitlab-cov-fuzz/-/raw"
coverage_fuzzing_unlicensed:
diff --git a/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml
index 40060e96dff..c43296b5865 100644
--- a/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml
@@ -51,7 +51,4 @@ dast:
$REVIEW_DISABLED
when: never
- if: $CI_COMMIT_BRANCH &&
- ($CI_KUBERNETES_ACTIVE || $KUBECONFIG) &&
- $GITLAB_FEATURES =~ /\bdast\b/
- - if: $CI_COMMIT_BRANCH &&
$GITLAB_FEATURES =~ /\bdast\b/
diff --git a/lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml
index 50e9bb5431d..27bcc14bcf5 100644
--- a/lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml
@@ -55,9 +55,6 @@ dast:
# Add the job to merge request pipelines if there's an open merge request.
- if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
- ($CI_KUBERNETES_ACTIVE || $KUBECONFIG) &&
- $GITLAB_FEATURES =~ /\bdast\b/
- - if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
$GITLAB_FEATURES =~ /\bdast\b/
# Don't add it to a *branch* pipeline if it's already in a merge request pipeline.
@@ -66,9 +63,6 @@ dast:
# Add the job to branch pipelines.
- if: $CI_COMMIT_BRANCH &&
- ($CI_KUBERNETES_ACTIVE || $KUBECONFIG) &&
- $GITLAB_FEATURES =~ /\bdast\b/
- - if: $CI_COMMIT_BRANCH &&
$GITLAB_FEATURES =~ /\bdast\b/
after_script:
# Remove any debug.log files because they might contain secrets.
diff --git a/lib/gitlab/ci/templates/Security/Secure-Binaries.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Secure-Binaries.gitlab-ci.yml
index fd04c86e6c7..631f6cecddf 100644
--- a/lib/gitlab/ci/templates/Security/Secure-Binaries.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Secure-Binaries.gitlab-ci.yml
@@ -9,9 +9,9 @@
# Usage:
#
# include:
-# - template: Secure-Binaries.gitlab-ci.yml
+# - template: Security/Secure-Binaries.gitlab-ci.yml
#
-# Docs: https://docs.gitlab.com/ee/topics/airgap/
+# Docs: https://docs.gitlab.com/ee/user/application_security/offline_deployments/
variables:
# Setting this variable will affect all Security templates
@@ -38,7 +38,7 @@ variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
services:
- - docker:stable-dind
+ - docker:dind
script:
- docker info
- env
diff --git a/lib/gitlab/ci/templates/Verify/Browser-Performance.gitlab-ci.yml b/lib/gitlab/ci/templates/Verify/Browser-Performance.gitlab-ci.yml
index c3113ffebf3..c1a90955f7f 100644
--- a/lib/gitlab/ci/templates/Verify/Browser-Performance.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Verify/Browser-Performance.gitlab-ci.yml
@@ -17,10 +17,10 @@ browser_performance:
variables:
URL: ''
SITESPEED_IMAGE: sitespeedio/sitespeed.io
- SITESPEED_VERSION: 14.1.0
+ SITESPEED_VERSION: 26.1.0
SITESPEED_OPTIONS: ''
services:
- - docker:stable-dind
+ - docker:dind
script:
- mkdir gitlab-exporter
# Busybox wget does not support proxied HTTPS, get the real thing.
diff --git a/lib/gitlab/ci/templates/Verify/Browser-Performance.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Verify/Browser-Performance.latest.gitlab-ci.yml
index c9f0c173692..adc92fde5ae 100644
--- a/lib/gitlab/ci/templates/Verify/Browser-Performance.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Verify/Browser-Performance.latest.gitlab-ci.yml
@@ -17,10 +17,10 @@ browser_performance:
variables:
URL: ''
SITESPEED_IMAGE: sitespeedio/sitespeed.io
- SITESPEED_VERSION: 14.1.0
+ SITESPEED_VERSION: latest
SITESPEED_OPTIONS: ''
services:
- - docker:stable-dind
+ - docker:dind
script:
- mkdir gitlab-exporter
# Busybox wget does not support proxied HTTPS, get the real thing.
diff --git a/lib/gitlab/ci/templates/Verify/Load-Performance-Testing.gitlab-ci.yml b/lib/gitlab/ci/templates/Verify/Load-Performance-Testing.gitlab-ci.yml
index bf5cfbb519d..a907915587a 100644
--- a/lib/gitlab/ci/templates/Verify/Load-Performance-Testing.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Verify/Load-Performance-Testing.gitlab-ci.yml
@@ -15,13 +15,13 @@ load_performance:
stage: performance
image: docker:git
variables:
- K6_IMAGE: loadimpact/k6
- K6_VERSION: 0.27.0
+ K6_IMAGE: grafana/k6
+ K6_VERSION: 0.41.0
K6_TEST_FILE: raw.githubusercontent.com/grafana/k6/master/samples/http_get.js
K6_OPTIONS: ''
K6_DOCKER_OPTIONS: ''
services:
- - docker:stable-dind
+ - docker:dind
script:
- docker run --rm -v "$(pwd)":/k6 -w /k6 $K6_DOCKER_OPTIONS $K6_IMAGE:$K6_VERSION run $K6_TEST_FILE --summary-export=load-performance.json $K6_OPTIONS
artifacts:
diff --git a/lib/gitlab/ci/variables/builder.rb b/lib/gitlab/ci/variables/builder.rb
index 8db8ea3a720..8e18d57b724 100644
--- a/lib/gitlab/ci/variables/builder.rb
+++ b/lib/gitlab/ci/variables/builder.rb
@@ -53,7 +53,7 @@ module Gitlab
# https://gitlab.com/groups/gitlab-org/configure/-/epics/8
# Until then, we need to make both the old and the new KUBECONFIG contexts available
collection.concat(deployment_variables(environment: environment, job: job))
- template = ::Ci::GenerateKubeconfigService.new(pipeline, token: job.try(:token)).execute
+ template = ::Ci::GenerateKubeconfigService.new(pipeline, token: job.try(:token), environment: environment).execute
kubeconfig_yaml = collection['KUBECONFIG']&.value
template.merge_yaml(kubeconfig_yaml) if kubeconfig_yaml.present?
@@ -135,6 +135,9 @@ module Gitlab
variables.append(key: 'CI_NODE_INDEX', value: job.options[:instance].to_s) if job.options&.include?(:instance)
variables.append(key: 'CI_NODE_TOTAL', value: ci_node_total_value(job).to_s)
+ # Set environment name here so we can access it when evaluating the job's rules
+ variables.append(key: 'CI_ENVIRONMENT_NAME', value: job.environment) if job.environment
+
# legacy variables
variables.append(key: 'CI_BUILD_NAME', value: job.name)
variables.append(key: 'CI_BUILD_STAGE', value: job.stage_name)
diff --git a/lib/gitlab/ci/yaml_processor/result.rb b/lib/gitlab/ci/yaml_processor/result.rb
index ff255543d3b..f2c1ad0575d 100644
--- a/lib/gitlab/ci/yaml_processor/result.rb
+++ b/lib/gitlab/ci/yaml_processor/result.rb
@@ -107,6 +107,7 @@ module Gitlab
cache: job[:cache],
resource_group_key: job[:resource_group],
scheduling_type: job[:scheduling_type],
+ id_tokens: job[:id_tokens],
options: {
image: job[:image],
services: job[:services],
@@ -118,6 +119,7 @@ module Gitlab
before_script: job[:before_script],
script: job[:script],
after_script: job[:after_script],
+ hooks: job[:hooks],
environment: job[:environment],
resource_group_key: job[:resource_group],
retry: job[:retry],
diff --git a/lib/gitlab/cluster/rack_timeout_observer.rb b/lib/gitlab/cluster/rack_timeout_observer.rb
index 5182b2be148..15dd6a59e19 100644
--- a/lib/gitlab/cluster/rack_timeout_observer.rb
+++ b/lib/gitlab/cluster/rack_timeout_observer.rb
@@ -3,6 +3,7 @@
module Gitlab
module Cluster
class RackTimeoutObserver
+ include ActionView::Helpers::SanitizeHelper
TRANSITION_STATES = %i(ready active).freeze
def initialize
@@ -28,9 +29,9 @@ module Gitlab
params = controller_params(env) || grape_params(env) || {}
{
- controller: params['controller'],
- action: params['action'],
- route: params['route'],
+ controller: sanitize(params['controller']),
+ action: sanitize(params['action']),
+ route: sanitize(params['route']),
state: info.state
}
end
diff --git a/lib/gitlab/color.rb b/lib/gitlab/color.rb
index 01c534c15a0..7d9280ddba2 100644
--- a/lib/gitlab/color.rb
+++ b/lib/gitlab/color.rb
@@ -215,13 +215,11 @@ module Gitlab
def rgb
return [] unless valid?
- @rgb ||= begin
- if @value.length == 4
- @value[1, 4].scan(/./).map { |v| (v * 2).hex }
- else
- @value[1, 7].scan(/.{2}/).map(&:hex)
- end
- end
+ @rgb ||= if @value.length == 4
+ @value[1, 4].scan(/./).map { |v| (v * 2).hex }
+ else
+ @value[1, 7].scan(/.{2}/).map(&:hex)
+ end
end
end
end
diff --git a/lib/gitlab/config/entry/attributable.rb b/lib/gitlab/config/entry/attributable.rb
index d266d5218de..c8ad2521574 100644
--- a/lib/gitlab/config/entry/attributable.rb
+++ b/lib/gitlab/config/entry/attributable.rb
@@ -7,19 +7,21 @@ module Gitlab
extend ActiveSupport::Concern
class_methods do
- def attributes(*attributes)
+ def attributes(*attributes, prefix: nil)
attributes.flatten.each do |attribute|
- if method_defined?(attribute)
- raise ArgumentError, "Method '#{attribute}' already defined in '#{name}'"
+ attribute_method = prefix ? "#{prefix}_#{attribute}" : attribute
+
+ if method_defined?(attribute_method)
+ raise ArgumentError, "Method '#{attribute_method}' already defined in '#{name}'"
end
- define_method(attribute) do
+ define_method(attribute_method) do
return unless config.is_a?(Hash)
config[attribute]
end
- define_method("has_#{attribute}?") do
+ define_method("has_#{attribute_method}?") do
config.is_a?(Hash) && config.key?(attribute)
end
end
diff --git a/lib/gitlab/conflict/file.rb b/lib/gitlab/conflict/file.rb
index d40a6323d4f..7bcbcf84a4e 100644
--- a/lib/gitlab/conflict/file.rb
+++ b/lib/gitlab/conflict/file.rb
@@ -236,14 +236,12 @@ module Gitlab
else
:modified_target_removed_source
end
+ elsif our_path.present? && their_path.present?
+ :both_added
+ elsif their_path.blank?
+ diff_file.renamed_file? ? :renamed_same_file : :removed_target_renamed_source
else
- if our_path.present? && their_path.present?
- :both_added
- elsif their_path.blank?
- diff_file.renamed_file? ? :renamed_same_file : :removed_target_renamed_source
- else
- :removed_source_renamed_target
- end
+ :removed_source_renamed_target
end
end
diff --git a/lib/gitlab/content_security_policy/config_loader.rb b/lib/gitlab/content_security_policy/config_loader.rb
index 29e8e631fb7..8b1298d0561 100644
--- a/lib/gitlab/content_security_policy/config_loader.rb
+++ b/lib/gitlab/content_security_policy/config_loader.rb
@@ -43,7 +43,10 @@ module Gitlab
allow_websocket_connections(directives)
allow_cdn(directives, Settings.gitlab.cdn_host) if Settings.gitlab.cdn_host.present?
- allow_sentry(directives) if Gitlab.config.sentry&.enabled && Gitlab.config.sentry&.clientside_dsn
+ # Support for Sentry setup via configuration files will be removed in 16.0
+ # in favor of Gitlab::CurrentSettings.
+ allow_legacy_sentry(directives) if Gitlab.config.sentry&.enabled && Gitlab.config.sentry&.clientside_dsn
+ allow_sentry(directives) if Gitlab::CurrentSettings.try(:sentry_enabled) && Gitlab::CurrentSettings.try(:sentry_clientside_dsn)
allow_framed_gitlab_paths(directives)
allow_customersdot(directives) if ENV['CUSTOMER_PORTAL_URL'].present?
allow_review_apps(directives) if ENV['REVIEW_APPS_ENABLED']
@@ -135,13 +138,22 @@ module Gitlab
append_to_directive(directives, 'frame_src', customersdot_host)
end
- def self.allow_sentry(directives)
+ def self.allow_legacy_sentry(directives)
+ # Support for Sentry setup via configuration files will be removed in 16.0
+ # in favor of Gitlab::CurrentSettings.
sentry_dsn = Gitlab.config.sentry.clientside_dsn
sentry_uri = URI(sentry_dsn)
append_to_directive(directives, 'connect_src', "#{sentry_uri.scheme}://#{sentry_uri.host}")
end
+ def self.allow_sentry(directives)
+ sentry_dsn = Gitlab::CurrentSettings.sentry_clientside_dsn
+ sentry_uri = URI(sentry_dsn)
+
+ append_to_directive(directives, 'connect_src', "#{sentry_uri.scheme}://#{sentry_uri.host}")
+ end
+
def self.allow_letter_opener(directives)
append_to_directive(directives, 'frame_src', Gitlab::Utils.append_path(Gitlab.config.gitlab.url, '/rails/letter_opener/'))
end
diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb
index a45380aca6c..2068a9ae7d5 100644
--- a/lib/gitlab/contributions_calendar.rb
+++ b/lib/gitlab/contributions_calendar.rb
@@ -31,7 +31,7 @@ module Gitlab
repo_events = events_created_between(start_time, end_time, :repository)
.where(action: :pushed)
issue_events = events_created_between(start_time, end_time, :issues)
- .where(action: [:created, :closed], target_type: "Issue")
+ .where(action: [:created, :closed], target_type: %w[Issue WorkItem])
mr_events = events_created_between(start_time, end_time, :merge_requests)
.where(action: [:merged, :created, :closed], target_type: "MergeRequest")
note_events = events_created_between(start_time, end_time, :merge_requests)
diff --git a/lib/gitlab/counters/buffered_counter.rb b/lib/gitlab/counters/buffered_counter.rb
new file mode 100644
index 00000000000..56593b642a9
--- /dev/null
+++ b/lib/gitlab/counters/buffered_counter.rb
@@ -0,0 +1,113 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Counters
+ class BufferedCounter
+ include Gitlab::ExclusiveLeaseHelpers
+
+ WORKER_DELAY = 10.minutes
+ WORKER_LOCK_TTL = 10.minutes
+
+ LUA_FLUSH_INCREMENT_SCRIPT = <<~LUA
+ local increment_key, flushed_key = KEYS[1], KEYS[2]
+ local increment_value = redis.call("get", increment_key) or 0
+ local flushed_value = redis.call("incrby", flushed_key, increment_value)
+ if flushed_value == 0 then
+ redis.call("del", increment_key, flushed_key)
+ else
+ redis.call("del", increment_key)
+ end
+ return flushed_value
+ LUA
+
+ def initialize(counter_record, attribute)
+ @counter_record = counter_record
+ @attribute = attribute
+ end
+
+ def get
+ redis_state do |redis|
+ redis.get(key).to_i
+ end
+ end
+
+ def increment(amount)
+ result = redis_state do |redis|
+ redis.incrby(key, amount)
+ end
+
+ FlushCounterIncrementsWorker.perform_in(WORKER_DELAY, counter_record.class.name, counter_record.id, attribute)
+
+ result
+ end
+
+ def reset!
+ counter_record.update!(attribute => 0)
+
+ redis_state do |redis|
+ redis.del(key)
+ end
+ end
+
+ def commit_increment!
+ with_exclusive_lease do
+ flush_amount = amount_to_be_flushed
+ next if flush_amount == 0
+
+ counter_record.transaction do
+ counter_record.update_counters_with_lease({ attribute => flush_amount })
+ remove_flushed_key
+ end
+
+ counter_record.execute_after_commit_callbacks
+ end
+
+ counter_record.reset.read_attribute(attribute)
+ end
+
+ # amount_to_be_flushed returns the total value to be flushed.
+ # The total value is the sum of the following:
+ # - current value in the increment_key
+ # - any existing value in the flushed_key that has not been flushed
+ def amount_to_be_flushed
+ redis_state do |redis|
+ redis.eval(LUA_FLUSH_INCREMENT_SCRIPT, keys: [key, flushed_key])
+ end
+ end
+
+ def key
+ project_id = counter_record.project.id
+ record_name = counter_record.class
+ record_id = counter_record.id
+
+ "project:{#{project_id}}:counters:#{record_name}:#{record_id}:#{attribute}"
+ end
+
+ def flushed_key
+ "#{key}:flushed"
+ end
+
+ private
+
+ attr_reader :counter_record, :attribute
+
+ def remove_flushed_key
+ redis_state do |redis|
+ redis.del(flushed_key)
+ end
+ end
+
+ def redis_state(&block)
+ Gitlab::Redis::SharedState.with(&block)
+ end
+
+ def with_exclusive_lease(&block)
+ lock_key = "#{key}:locked"
+
+ in_lock(lock_key, ttl: WORKER_LOCK_TTL, &block)
+ rescue Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError
+ # a worker is already updating the counters
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/counters/legacy_counter.rb b/lib/gitlab/counters/legacy_counter.rb
new file mode 100644
index 00000000000..06951514ec3
--- /dev/null
+++ b/lib/gitlab/counters/legacy_counter.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Counters
+ # This class is a wrapper over ActiveRecord counter
+ # for attributes that have not adopted Redis-backed BufferedCounter.
+ class LegacyCounter
+ def initialize(counter_record, attribute)
+ @counter_record = counter_record
+ @attribute = attribute
+ @current_value = counter_record.method(attribute).call
+ end
+
+ def increment(amount)
+ updated = counter_record.class.update_counters(counter_record.id, { attribute => amount })
+
+ if updated == 1
+ counter_record.execute_after_commit_callbacks
+ @current_value += amount
+ end
+
+ @current_value
+ end
+
+ def reset!
+ counter_record.update!(attribute => 0)
+ end
+
+ private
+
+ attr_reader :counter_record, :attribute
+ end
+ end
+end
diff --git a/lib/gitlab/data_builder/deployment.rb b/lib/gitlab/data_builder/deployment.rb
index a9c69e3f997..7055f64937d 100644
--- a/lib/gitlab/data_builder/deployment.rb
+++ b/lib/gitlab/data_builder/deployment.rb
@@ -35,6 +35,8 @@ module Gitlab
deployable_id: deployment.deployable_id,
deployable_url: deployable_url,
environment: deployment.environment.name,
+ environment_slug: deployment.environment.slug,
+ environment_external_url: deployment.environment.external_url,
project: deployment.project.hook_attrs,
short_sha: deployment.short_sha,
user: deployment.deployed_by&.hook_attrs,
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 04cf056199c..51d5bfcee38 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -101,7 +101,8 @@ module Gitlab
gitlab_main: [self.database_base_models.fetch(:main)],
gitlab_ci: [self.database_base_models[:ci] || self.database_base_models.fetch(:main)], # use CI or fallback to main
gitlab_shared: database_base_models_with_gitlab_shared.values, # all models
- gitlab_internal: database_base_models.values # all models
+ gitlab_internal: database_base_models.values, # all models
+ gitlab_pm: [self.database_base_models.fetch(:main)] # package metadata models
}.with_indifferent_access.freeze
end
diff --git a/lib/gitlab/database/bulk_update.rb b/lib/gitlab/database/bulk_update.rb
index d68be19047e..4b4a9b38fd8 100644
--- a/lib/gitlab/database/bulk_update.rb
+++ b/lib/gitlab/database/bulk_update.rb
@@ -157,7 +157,7 @@ module Gitlab
def self.execute(columns, mapping, &to_class)
raise ArgumentError if mapping.blank?
- entries_by_class = mapping.group_by { |k, v| to_class ? to_class.call(k) : k.class }
+ entries_by_class = mapping.group_by { |k, v| to_class ? yield(k) : k.class }
entries_by_class.each do |model, entries|
Setter.new(model, columns, entries).update!
diff --git a/lib/gitlab/database/count/exact_count_strategy.rb b/lib/gitlab/database/count/exact_count_strategy.rb
index 345c7e44b05..11c83786aa4 100644
--- a/lib/gitlab/database/count/exact_count_strategy.rb
+++ b/lib/gitlab/database/count/exact_count_strategy.rb
@@ -18,9 +18,7 @@ module Gitlab
end
def count
- models.each_with_object({}) do |model, data|
- data[model] = model.count
- end
+ models.index_with(&:count)
rescue *CONNECTION_ERRORS
{}
end
diff --git a/lib/gitlab/database/gitlab_schema.rb b/lib/gitlab/database/gitlab_schema.rb
index 365a4283d4c..0f848ed40fb 100644
--- a/lib/gitlab/database/gitlab_schema.rb
+++ b/lib/gitlab/database/gitlab_schema.rb
@@ -6,14 +6,16 @@
# Each table / view needs to have assigned gitlab_schema. Names supported today:
#
# - gitlab_shared - defines a set of tables that are found on all databases (data accessed is dependent on connection)
-# - gitlab_main / gitlab_ci - defines a set of tables that can only exist on a given database
+# - gitlab_main / gitlab_ci - defines a set of tables that can only exist on a given application database
+# - gitlab_geo - defines a set of tables that can only exist on the geo database
+# - gitlab_internal - defines all internal tables of Rails and PostgreSQL
#
# Tables for the purpose of tests should be prefixed with `_test_my_table_name`
module Gitlab
module Database
module GitlabSchema
- GITLAB_SCHEMAS_FILE = 'lib/gitlab/database/gitlab_schemas.yml'
+ DICTIONARY_PATH = 'db/docs/'
# These tables are deleted/renamed, but still referenced by migrations.
# This is needed for now, but should be removed in the future
@@ -55,7 +57,7 @@ module Gitlab
tables.map { |table| table_schema(table) }.to_set
end
- def self.table_schema(name)
+ def self.table_schema(name, undefined: true)
schema_name, table_name = name.split('.', 2) # Strip schema name like: `public.`
# Most of names do not have schemas, ensure that this is table
@@ -68,7 +70,7 @@ module Gitlab
table_name.gsub!(/_[0-9]+$/, '')
# Tables that are properly mapped
- if gitlab_schema = tables_to_schema[table_name]
+ if gitlab_schema = views_and_tables_to_schema[table_name]
return gitlab_schema
end
@@ -84,6 +86,8 @@ module Gitlab
return :gitlab_ci if table_name.start_with?('_test_gitlab_ci_')
+ return :gitlab_geo if table_name.start_with?('_test_gitlab_geo_')
+
# All tables that start with `_test_` without a following schema are shared and ignored
return :gitlab_shared if table_name.start_with?('_test_')
@@ -91,15 +95,39 @@ module Gitlab
return :gitlab_internal if table_name.start_with?('pg_')
# When undefined it's best to return a unique name so that we don't incorrectly assume that 2 undefined schemas belong on the same database
- :"undefined_#{table_name}"
+ undefined ? :"undefined_#{table_name}" : nil
+ end
+
+ def self.dictionary_path_globs
+ [Rails.root.join(DICTIONARY_PATH, '*.yml')]
+ end
+
+ def self.view_path_globs
+ [Rails.root.join(DICTIONARY_PATH, 'views', '*.yml')]
+ end
+
+ def self.views_and_tables_to_schema
+ @views_and_tables_to_schema ||= self.tables_to_schema.merge(self.views_to_schema)
end
def self.tables_to_schema
- @tables_to_schema ||= YAML.load_file(Rails.root.join(GITLAB_SCHEMAS_FILE))
+ @tables_to_schema ||= Dir.glob(self.dictionary_path_globs).each_with_object({}) do |file_path, dic|
+ data = YAML.load_file(file_path)
+
+ dic[data['table_name']] = data['gitlab_schema'].to_sym
+ end
+ end
+
+ def self.views_to_schema
+ @views_to_schema ||= Dir.glob(self.view_path_globs).each_with_object({}) do |file_path, dic|
+ data = YAML.load_file(file_path)
+
+ dic[data['view_name']] = data['gitlab_schema'].to_sym
+ end
end
def self.schema_names
- @schema_names ||= self.tables_to_schema.values.to_set
+ @schema_names ||= self.views_and_tables_to_schema.values.to_set
end
end
end
diff --git a/lib/gitlab/database/gitlab_schemas.yml b/lib/gitlab/database/gitlab_schemas.yml
deleted file mode 100644
index bf6ebb21f7d..00000000000
--- a/lib/gitlab/database/gitlab_schemas.yml
+++ /dev/null
@@ -1,606 +0,0 @@
-abuse_reports: :gitlab_main
-agent_activity_events: :gitlab_main
-agent_group_authorizations: :gitlab_main
-agent_project_authorizations: :gitlab_main
-alert_management_alert_assignees: :gitlab_main
-alert_management_alerts: :gitlab_main
-alert_management_alert_metric_images: :gitlab_main
-alert_management_alert_user_mentions: :gitlab_main
-alert_management_http_integrations: :gitlab_main
-allowed_email_domains: :gitlab_main
-analytics_cycle_analytics_aggregations: :gitlab_main
-analytics_cycle_analytics_group_stages: :gitlab_main
-analytics_cycle_analytics_group_value_streams: :gitlab_main
-analytics_cycle_analytics_issue_stage_events: :gitlab_main
-analytics_cycle_analytics_merge_request_stage_events: :gitlab_main
-analytics_cycle_analytics_project_stages: :gitlab_main
-analytics_cycle_analytics_project_value_streams: :gitlab_main
-analytics_cycle_analytics_stage_event_hashes: :gitlab_main
-analytics_devops_adoption_segments: :gitlab_main
-analytics_devops_adoption_snapshots: :gitlab_main
-analytics_language_trend_repository_languages: :gitlab_main
-analytics_usage_trends_measurements: :gitlab_main
-appearances: :gitlab_main
-application_settings: :gitlab_main
-application_setting_terms: :gitlab_main
-approval_merge_request_rules_approved_approvers: :gitlab_main
-approval_merge_request_rules: :gitlab_main
-approval_merge_request_rules_groups: :gitlab_main
-approval_merge_request_rule_sources: :gitlab_main
-approval_merge_request_rules_users: :gitlab_main
-approval_project_rules: :gitlab_main
-approval_project_rules_groups: :gitlab_main
-approval_project_rules_protected_branches: :gitlab_main
-approval_project_rules_users: :gitlab_main
-approvals: :gitlab_main
-approver_groups: :gitlab_main
-approvers: :gitlab_main
-ar_internal_metadata: :gitlab_internal
-atlassian_identities: :gitlab_main
-audit_events_external_audit_event_destinations: :gitlab_main
-audit_events: :gitlab_main
-audit_events_streaming_headers: :gitlab_main
-audit_events_streaming_event_type_filters: :gitlab_main
-authentication_events: :gitlab_main
-award_emoji: :gitlab_main
-aws_roles: :gitlab_main
-background_migration_jobs: :gitlab_shared
-badges: :gitlab_main
-banned_users: :gitlab_main
-batched_background_migration_jobs: :gitlab_shared
-batched_background_migrations: :gitlab_shared
-board_assignees: :gitlab_main
-board_group_recent_visits: :gitlab_main
-board_labels: :gitlab_main
-board_project_recent_visits: :gitlab_main
-boards_epic_board_labels: :gitlab_main
-boards_epic_board_positions: :gitlab_main
-boards_epic_board_recent_visits: :gitlab_main
-boards_epic_boards: :gitlab_main
-boards_epic_lists: :gitlab_main
-boards_epic_list_user_preferences: :gitlab_main
-boards_epic_user_preferences: :gitlab_main
-boards: :gitlab_main
-board_user_preferences: :gitlab_main
-broadcast_messages: :gitlab_main
-bulk_import_configurations: :gitlab_main
-bulk_import_entities: :gitlab_main
-bulk_import_exports: :gitlab_main
-bulk_import_export_uploads: :gitlab_main
-bulk_import_failures: :gitlab_main
-bulk_imports: :gitlab_main
-bulk_import_trackers: :gitlab_main
-chat_names: :gitlab_main
-chat_teams: :gitlab_main
-ci_build_needs: :gitlab_ci
-ci_build_pending_states: :gitlab_ci
-ci_build_report_results: :gitlab_ci
-ci_builds: :gitlab_ci
-ci_builds_metadata: :gitlab_ci
-ci_builds_runner_session: :gitlab_ci
-ci_build_trace_chunks: :gitlab_ci
-ci_build_trace_metadata: :gitlab_ci
-ci_daily_build_group_report_results: :gitlab_ci
-ci_deleted_objects: :gitlab_ci
-ci_freeze_periods: :gitlab_ci
-ci_group_variables: :gitlab_ci
-ci_instance_variables: :gitlab_ci
-ci_job_artifacts: :gitlab_ci
-ci_job_token_project_scope_links: :gitlab_ci
-ci_job_variables: :gitlab_ci
-ci_job_artifact_states: :gitlab_ci
-ci_minutes_additional_packs: :gitlab_ci
-ci_namespace_monthly_usages: :gitlab_ci
-ci_namespace_mirrors: :gitlab_ci
-ci_partitions: :gitlab_ci
-ci_pending_builds: :gitlab_ci
-ci_pipeline_artifacts: :gitlab_ci
-ci_pipeline_chat_data: :gitlab_ci
-ci_pipeline_messages: :gitlab_ci
-ci_pipeline_schedules: :gitlab_ci
-ci_pipeline_schedule_variables: :gitlab_ci
-ci_pipelines_config: :gitlab_ci
-ci_pipeline_metadata: :gitlab_ci
-ci_pipelines: :gitlab_ci
-ci_pipeline_variables: :gitlab_ci
-ci_platform_metrics: :gitlab_ci
-ci_project_monthly_usages: :gitlab_ci
-ci_project_mirrors: :gitlab_ci
-ci_refs: :gitlab_ci
-ci_resource_groups: :gitlab_ci
-ci_resources: :gitlab_ci
-ci_runner_namespaces: :gitlab_ci
-ci_runner_projects: :gitlab_ci
-ci_runner_versions: :gitlab_ci
-ci_runners: :gitlab_ci
-ci_running_builds: :gitlab_ci
-ci_sources_pipelines: :gitlab_ci
-ci_secure_files: :gitlab_ci
-ci_secure_file_states: :gitlab_ci
-ci_sources_projects: :gitlab_ci
-ci_stages: :gitlab_ci
-ci_subscriptions_projects: :gitlab_ci
-ci_trigger_requests: :gitlab_ci
-ci_triggers: :gitlab_ci
-ci_unit_test_failures: :gitlab_ci
-ci_unit_tests: :gitlab_ci
-ci_variables: :gitlab_ci
-cluster_agents: :gitlab_main
-cluster_agent_tokens: :gitlab_main
-cluster_enabled_grants: :gitlab_main
-cluster_groups: :gitlab_main
-cluster_platforms_kubernetes: :gitlab_main
-cluster_projects: :gitlab_main
-cluster_providers_aws: :gitlab_main
-cluster_providers_gcp: :gitlab_main
-clusters_applications_cert_managers: :gitlab_main
-clusters_applications_cilium: :gitlab_main
-clusters_applications_crossplane: :gitlab_main
-clusters_applications_helm: :gitlab_main
-clusters_applications_ingress: :gitlab_main
-clusters_applications_jupyter: :gitlab_main
-clusters_applications_knative: :gitlab_main
-clusters_applications_prometheus: :gitlab_main
-clusters_applications_runners: :gitlab_main
-clusters: :gitlab_main
-clusters_integration_prometheus: :gitlab_main
-clusters_kubernetes_namespaces: :gitlab_main
-commit_user_mentions: :gitlab_main
-compliance_management_frameworks: :gitlab_main
-container_expiration_policies: :gitlab_main
-container_repositories: :gitlab_main
-content_blocked_states: :gitlab_main
-conversational_development_index_metrics: :gitlab_main
-coverage_fuzzing_corpuses: :gitlab_main
-csv_issue_imports: :gitlab_main
-custom_emoji: :gitlab_main
-customer_relations_contacts: :gitlab_main
-customer_relations_organizations: :gitlab_main
-dast_profile_schedules: :gitlab_main
-dast_profiles: :gitlab_main
-dast_profiles_pipelines: :gitlab_main
-dast_scanner_profiles_builds: :gitlab_main
-dast_scanner_profiles: :gitlab_main
-dast_site_profiles_builds: :gitlab_main
-dast_site_profile_secret_variables: :gitlab_main
-dast_site_profiles: :gitlab_main
-dast_site_profiles_pipelines: :gitlab_main
-dast_sites: :gitlab_main
-dast_site_tokens: :gitlab_main
-dast_site_validations: :gitlab_main
-dependency_proxy_blob_states: :gitlab_main
-dependency_proxy_blobs: :gitlab_main
-dependency_proxy_group_settings: :gitlab_main
-dependency_proxy_image_ttl_group_policies: :gitlab_main
-dependency_proxy_manifests: :gitlab_main
-deploy_keys_projects: :gitlab_main
-deployment_approvals: :gitlab_main
-deployment_clusters: :gitlab_main
-deployment_merge_requests: :gitlab_main
-deployments: :gitlab_main
-deploy_tokens: :gitlab_main
-description_versions: :gitlab_main
-design_management_designs: :gitlab_main
-design_management_designs_versions: :gitlab_main
-design_management_versions: :gitlab_main
-design_user_mentions: :gitlab_main
-detached_partitions: :gitlab_shared
-diff_note_positions: :gitlab_main
-dora_configurations: :gitlab_main
-dora_daily_metrics: :gitlab_main
-draft_notes: :gitlab_main
-elastic_index_settings: :gitlab_main
-elastic_reindexing_slices: :gitlab_main
-elastic_reindexing_subtasks: :gitlab_main
-elastic_reindexing_tasks: :gitlab_main
-elasticsearch_indexed_namespaces: :gitlab_main
-elasticsearch_indexed_projects: :gitlab_main
-emails: :gitlab_main
-environments: :gitlab_main
-epic_issues: :gitlab_main
-epic_metrics: :gitlab_main
-epics: :gitlab_main
-epic_user_mentions: :gitlab_main
-error_tracking_client_keys: :gitlab_main
-error_tracking_error_events: :gitlab_main
-error_tracking_errors: :gitlab_main
-events: :gitlab_main
-evidences: :gitlab_main
-experiments: :gitlab_main
-experiment_subjects: :gitlab_main
-external_approval_rules: :gitlab_main
-external_approval_rules_protected_branches: :gitlab_main
-external_pull_requests: :gitlab_ci
-external_status_checks: :gitlab_main
-external_status_checks_protected_branches: :gitlab_main
-feature_gates: :gitlab_main
-features: :gitlab_main
-fork_network_members: :gitlab_main
-fork_networks: :gitlab_main
-geo_cache_invalidation_events: :gitlab_main
-geo_container_repository_updated_events: :gitlab_main
-geo_event_log: :gitlab_main
-geo_events: :gitlab_main
-geo_hashed_storage_attachments_events: :gitlab_main
-geo_hashed_storage_migrated_events: :gitlab_main
-geo_node_namespace_links: :gitlab_main
-geo_nodes: :gitlab_main
-geo_node_statuses: :gitlab_main
-geo_repositories_changed_events: :gitlab_main
-geo_repository_created_events: :gitlab_main
-geo_repository_deleted_events: :gitlab_main
-geo_repository_renamed_events: :gitlab_main
-geo_repository_updated_events: :gitlab_main
-geo_reset_checksum_events: :gitlab_main
-ghost_user_migrations: :gitlab_main
-gitlab_subscription_histories: :gitlab_main
-gitlab_subscriptions: :gitlab_main
-gpg_keys: :gitlab_main
-gpg_key_subkeys: :gitlab_main
-gpg_signatures: :gitlab_main
-grafana_integrations: :gitlab_main
-group_custom_attributes: :gitlab_main
-group_crm_settings: :gitlab_main
-group_deletion_schedules: :gitlab_main
-group_deploy_keys: :gitlab_main
-group_deploy_keys_groups: :gitlab_main
-group_deploy_tokens: :gitlab_main
-group_features: :gitlab_main
-group_group_links: :gitlab_main
-group_import_states: :gitlab_main
-group_merge_request_approval_settings: :gitlab_main
-group_repository_storage_moves: :gitlab_main
-group_wiki_repositories: :gitlab_main
-historical_data: :gitlab_main
-identities: :gitlab_main
-import_export_uploads: :gitlab_main
-import_failures: :gitlab_main
-incident_management_escalation_policies: :gitlab_main
-incident_management_escalation_rules: :gitlab_main
-incident_management_issuable_escalation_statuses: :gitlab_main
-incident_management_oncall_participants: :gitlab_main
-incident_management_oncall_rotations: :gitlab_main
-incident_management_oncall_schedules: :gitlab_main
-incident_management_oncall_shifts: :gitlab_main
-incident_management_pending_alert_escalations: :gitlab_main
-incident_management_pending_issue_escalations: :gitlab_main
-incident_management_timeline_events: :gitlab_main
-incident_management_timeline_event_tags: :gitlab_main
-incident_management_timeline_event_tag_links: :gitlab_main
-index_statuses: :gitlab_main
-in_product_marketing_emails: :gitlab_main
-insights: :gitlab_main
-integrations: :gitlab_main
-internal_ids: :gitlab_main
-ip_restrictions: :gitlab_main
-issuable_metric_images: :gitlab_main
-issuable_resource_links: :gitlab_main
-issuable_severities: :gitlab_main
-issuable_slas: :gitlab_main
-issue_assignees: :gitlab_main
-issue_customer_relations_contacts: :gitlab_main
-issue_emails: :gitlab_main
-issue_email_participants: :gitlab_main
-issue_links: :gitlab_main
-issue_metrics: :gitlab_main
-issue_search_data: :gitlab_main
-issues: :gitlab_main
-issues_prometheus_alert_events: :gitlab_main
-issues_self_managed_prometheus_alert_events: :gitlab_main
-issue_tracker_data: :gitlab_main
-issue_user_mentions: :gitlab_main
-iterations_cadences: :gitlab_main
-jira_connect_installations: :gitlab_main
-jira_connect_subscriptions: :gitlab_main
-jira_imports: :gitlab_main
-jira_tracker_data: :gitlab_main
-keys: :gitlab_main
-label_links: :gitlab_main
-label_priorities: :gitlab_main
-labels: :gitlab_main
-ldap_group_links: :gitlab_main
-lfs_file_locks: :gitlab_main
-lfs_objects: :gitlab_main
-lfs_objects_projects: :gitlab_main
-lfs_object_states: :gitlab_main
-licenses: :gitlab_main
-lists: :gitlab_main
-list_user_preferences: :gitlab_main
-loose_foreign_keys_deleted_records: :gitlab_shared
-member_roles: :gitlab_main
-member_tasks: :gitlab_main
-members: :gitlab_main
-merge_request_assignees: :gitlab_main
-merge_request_blocks: :gitlab_main
-merge_request_cleanup_schedules: :gitlab_main
-merge_requests_compliance_violations: :gitlab_main
-merge_request_context_commit_diff_files: :gitlab_main
-merge_request_context_commits: :gitlab_main
-merge_request_diff_commits: :gitlab_main
-merge_request_diff_commit_users: :gitlab_main
-merge_request_diff_details: :gitlab_main
-merge_request_diff_files: :gitlab_main
-merge_request_diffs: :gitlab_main
-merge_request_metrics: :gitlab_main
-merge_request_predictions: :gitlab_main
-merge_request_reviewers: :gitlab_main
-merge_requests_closing_issues: :gitlab_main
-merge_requests: :gitlab_main
-merge_request_user_mentions: :gitlab_main
-merge_trains: :gitlab_main
-metrics_dashboard_annotations: :gitlab_main
-metrics_users_starred_dashboards: :gitlab_main
-milestone_releases: :gitlab_main
-milestones: :gitlab_main
-ml_candidates: :gitlab_main
-ml_experiments: :gitlab_main
-ml_candidate_metrics: :gitlab_main
-ml_candidate_params: :gitlab_main
-namespace_admin_notes: :gitlab_main
-namespace_aggregation_schedules: :gitlab_main
-namespace_bans: :gitlab_main
-namespace_limits: :gitlab_main
-namespace_package_settings: :gitlab_main
-namespace_root_storage_statistics: :gitlab_main
-namespace_ci_cd_settings: :gitlab_main
-namespace_commit_emails: :gitlab_main
-namespace_settings: :gitlab_main
-namespace_details: :gitlab_main
-namespaces: :gitlab_main
-namespaces_sync_events: :gitlab_main
-namespace_statistics: :gitlab_main
-note_diff_files: :gitlab_main
-notes: :gitlab_main
-notification_settings: :gitlab_main
-oauth_access_grants: :gitlab_main
-oauth_access_tokens: :gitlab_main
-oauth_applications: :gitlab_main
-oauth_openid_requests: :gitlab_main
-onboarding_progresses: :gitlab_main
-operations_feature_flags_clients: :gitlab_main
-operations_feature_flag_scopes: :gitlab_main
-operations_feature_flags: :gitlab_main
-operations_feature_flags_issues: :gitlab_main
-operations_scopes: :gitlab_main
-operations_strategies: :gitlab_main
-operations_strategies_user_lists: :gitlab_main
-operations_user_lists: :gitlab_main
-p_ci_builds_metadata: :gitlab_ci
-packages_build_infos: :gitlab_main
-packages_cleanup_policies: :gitlab_main
-packages_composer_cache_files: :gitlab_main
-packages_composer_metadata: :gitlab_main
-packages_conan_file_metadata: :gitlab_main
-packages_conan_metadata: :gitlab_main
-packages_debian_file_metadata: :gitlab_main
-packages_debian_group_architectures: :gitlab_main
-packages_debian_group_component_files: :gitlab_main
-packages_debian_group_components: :gitlab_main
-packages_debian_group_distribution_keys: :gitlab_main
-packages_debian_group_distributions: :gitlab_main
-packages_debian_project_architectures: :gitlab_main
-packages_debian_project_component_files: :gitlab_main
-packages_debian_project_components: :gitlab_main
-packages_debian_project_distribution_keys: :gitlab_main
-packages_debian_project_distributions: :gitlab_main
-packages_debian_publications: :gitlab_main
-packages_dependencies: :gitlab_main
-packages_dependency_links: :gitlab_main
-packages_events: :gitlab_main
-packages_helm_file_metadata: :gitlab_main
-packages_maven_metadata: :gitlab_main
-packages_npm_metadata: :gitlab_main
-packages_rpm_metadata: :gitlab_main
-packages_nuget_dependency_link_metadata: :gitlab_main
-packages_nuget_metadata: :gitlab_main
-packages_package_file_build_infos: :gitlab_main
-packages_package_files: :gitlab_main
-packages_rpm_repository_files: :gitlab_main
-packages_packages: :gitlab_main
-packages_pypi_metadata: :gitlab_main
-packages_rubygems_metadata: :gitlab_main
-packages_tags: :gitlab_main
-pages_deployments: :gitlab_main
-pages_deployment_states: :gitlab_main
-pages_domain_acme_orders: :gitlab_main
-pages_domains: :gitlab_main
-path_locks: :gitlab_main
-personal_access_tokens: :gitlab_main
-plan_limits: :gitlab_main
-plans: :gitlab_main
-pool_repositories: :gitlab_main
-postgres_async_indexes: :gitlab_shared
-postgres_autovacuum_activity: :gitlab_shared
-postgres_constraints: :gitlab_shared
-postgres_foreign_keys: :gitlab_shared
-postgres_index_bloat_estimates: :gitlab_shared
-postgres_indexes: :gitlab_shared
-postgres_partitioned_tables: :gitlab_shared
-postgres_partitions: :gitlab_shared
-postgres_reindex_actions: :gitlab_shared
-postgres_reindex_queued_actions: :gitlab_shared
-product_analytics_events_experimental: :gitlab_main
-programming_languages: :gitlab_main
-project_access_tokens: :gitlab_main
-project_alerting_settings: :gitlab_main
-project_aliases: :gitlab_main
-project_authorizations: :gitlab_main
-project_auto_devops: :gitlab_main
-project_build_artifacts_size_refreshes: :gitlab_main
-project_ci_cd_settings: :gitlab_main
-project_ci_feature_usages: :gitlab_main
-project_compliance_framework_settings: :gitlab_main
-project_custom_attributes: :gitlab_main
-project_daily_statistics: :gitlab_main
-project_deploy_tokens: :gitlab_main
-project_error_tracking_settings: :gitlab_main
-project_export_jobs: :gitlab_main
-project_features: :gitlab_main
-project_feature_usages: :gitlab_main
-project_group_links: :gitlab_main
-project_import_data: :gitlab_main
-project_incident_management_settings: :gitlab_main
-project_metrics_settings: :gitlab_main
-project_mirror_data: :gitlab_main
-project_pages_metadata: :gitlab_main
-project_relation_export_uploads: :gitlab_main
-project_relation_exports: :gitlab_main
-project_repositories: :gitlab_main
-project_repository_states: :gitlab_main
-project_repository_storage_moves: :gitlab_main
-project_security_settings: :gitlab_main
-project_settings: :gitlab_main
-projects: :gitlab_main
-projects_sync_events: :gitlab_main
-project_statistics: :gitlab_main
-project_topics: :gitlab_main
-project_wiki_repositories: :gitlab_main
-project_wiki_repository_states: :gitlab_main
-prometheus_alert_events: :gitlab_main
-prometheus_alerts: :gitlab_main
-prometheus_metrics: :gitlab_main
-protected_branches: :gitlab_main
-protected_branch_merge_access_levels: :gitlab_main
-protected_branch_push_access_levels: :gitlab_main
-protected_branch_unprotect_access_levels: :gitlab_main
-protected_environment_approval_rules: :gitlab_main
-protected_environment_deploy_access_levels: :gitlab_main
-protected_environments: :gitlab_main
-protected_tag_create_access_levels: :gitlab_main
-protected_tags: :gitlab_main
-push_event_payloads: :gitlab_main
-push_rules: :gitlab_main
-raw_usage_data: :gitlab_main
-redirect_routes: :gitlab_main
-related_epic_links: :gitlab_main
-release_links: :gitlab_main
-releases: :gitlab_main
-remote_mirrors: :gitlab_main
-repository_languages: :gitlab_main
-required_code_owners_sections: :gitlab_main
-requirements: :gitlab_main
-requirements_management_test_reports: :gitlab_main
-resource_iteration_events: :gitlab_main
-resource_label_events: :gitlab_main
-resource_milestone_events: :gitlab_main
-resource_state_events: :gitlab_main
-resource_weight_events: :gitlab_main
-reviews: :gitlab_main
-routes: :gitlab_main
-saml_group_links: :gitlab_main
-saml_providers: :gitlab_main
-saved_replies: :gitlab_main
-sbom_components: :gitlab_main
-sbom_occurrences: :gitlab_main
-sbom_component_versions: :gitlab_main
-sbom_sources: :gitlab_main
-sbom_vulnerable_component_versions: :gitlab_main
-schema_migrations: :gitlab_internal
-scim_identities: :gitlab_main
-scim_oauth_access_tokens: :gitlab_main
-security_findings: :gitlab_main
-security_orchestration_policy_configurations: :gitlab_main
-security_orchestration_policy_rule_schedules: :gitlab_main
-security_scans: :gitlab_main
-security_training_providers: :gitlab_main
-security_trainings: :gitlab_main
-self_managed_prometheus_alert_events: :gitlab_main
-sent_notifications: :gitlab_main
-sentry_issues: :gitlab_main
-serverless_domain_cluster: :gitlab_main
-service_desk_settings: :gitlab_main
-shards: :gitlab_main
-slack_integrations: :gitlab_main
-smartcard_identities: :gitlab_main
-snippet_repositories: :gitlab_main
-snippet_repository_storage_moves: :gitlab_main
-snippets: :gitlab_main
-snippet_statistics: :gitlab_main
-snippet_user_mentions: :gitlab_main
-software_license_policies: :gitlab_main
-software_licenses: :gitlab_main
-spam_logs: :gitlab_main
-sprints: :gitlab_main
-ssh_signatures: :gitlab_main
-status_check_responses: :gitlab_main
-status_page_published_incidents: :gitlab_main
-status_page_settings: :gitlab_main
-subscriptions: :gitlab_main
-suggestions: :gitlab_main
-system_note_metadata: :gitlab_main
-taggings: :gitlab_ci
-tags: :gitlab_ci
-term_agreements: :gitlab_main
-terraform_states: :gitlab_main
-terraform_state_versions: :gitlab_main
-timelogs: :gitlab_main
-timelog_categories: :gitlab_main
-todos: :gitlab_main
-token_with_ivs: :gitlab_main
-topics: :gitlab_main
-trending_projects: :gitlab_main
-u2f_registrations: :gitlab_main
-upcoming_reconciliations: :gitlab_main
-uploads: :gitlab_main
-upload_states: :gitlab_main
-user_agent_details: :gitlab_main
-user_callouts: :gitlab_main
-user_canonical_emails: :gitlab_main
-user_credit_card_validations: :gitlab_main
-user_custom_attributes: :gitlab_main
-user_details: :gitlab_main
-user_follow_users: :gitlab_main
-user_group_callouts: :gitlab_main
-user_project_callouts: :gitlab_main
-user_highest_roles: :gitlab_main
-user_interacted_projects: :gitlab_main
-user_phone_number_validations: :gitlab_main
-user_permission_export_uploads: :gitlab_main
-user_preferences: :gitlab_main
-users: :gitlab_main
-users_ops_dashboard_projects: :gitlab_main
-users_security_dashboard_projects: :gitlab_main
-users_star_projects: :gitlab_main
-users_statistics: :gitlab_main
-user_statuses: :gitlab_main
-user_synced_attributes_metadata: :gitlab_main
-verification_codes: :gitlab_main
-vulnerabilities: :gitlab_main
-vulnerability_advisories: :gitlab_main
-vulnerability_exports: :gitlab_main
-vulnerability_external_issue_links: :gitlab_main
-vulnerability_feedback: :gitlab_main
-vulnerability_finding_evidences: :gitlab_main
-vulnerability_finding_links: :gitlab_main
-vulnerability_finding_signatures: :gitlab_main
-vulnerability_findings_remediations: :gitlab_main
-vulnerability_flags: :gitlab_main
-vulnerability_historical_statistics: :gitlab_main
-vulnerability_identifiers: :gitlab_main
-vulnerability_issue_links: :gitlab_main
-vulnerability_merge_request_links: :gitlab_main
-vulnerability_occurrence_identifiers: :gitlab_main
-vulnerability_occurrence_pipelines: :gitlab_main
-vulnerability_occurrences: :gitlab_main
-vulnerability_reads: :gitlab_main
-vulnerability_remediations: :gitlab_main
-vulnerability_scanners: :gitlab_main
-vulnerability_state_transitions: :gitlab_main
-vulnerability_statistics: :gitlab_main
-vulnerability_user_mentions: :gitlab_main
-webauthn_registrations: :gitlab_main
-web_hook_logs: :gitlab_main
-web_hooks: :gitlab_main
-wiki_page_meta: :gitlab_main
-wiki_page_slugs: :gitlab_main
-work_item_parent_links: :gitlab_main
-work_item_types: :gitlab_main
-x509_certificates: :gitlab_main
-x509_commit_signatures: :gitlab_main
-x509_issuers: :gitlab_main
-zentao_tracker_data: :gitlab_main
-# dingtalk_tracker_data JiHu-specific, see https://jihulab.com/gitlab-cn/gitlab/-/merge_requests/417
-dingtalk_tracker_data: :gitlab_main
-zoom_meetings: :gitlab_main
-batched_background_migration_job_transition_logs: :gitlab_shared
-user_namespace_callouts: :gitlab_main
diff --git a/lib/gitlab/database/load_balancing/connection_proxy.rb b/lib/gitlab/database/load_balancing/connection_proxy.rb
index 8799f8d8af8..f0343f9d8b5 100644
--- a/lib/gitlab/database/load_balancing/connection_proxy.rb
+++ b/lib/gitlab/database/load_balancing/connection_proxy.rb
@@ -95,7 +95,7 @@ module Gitlab
# name - The name of the method to call on a connection object.
def read_using_load_balancer(...)
if current_session.use_primary? &&
- !current_session.use_replicas_for_read_queries?
+ !current_session.use_replicas_for_read_queries?
@load_balancer.read_write do |connection|
connection.send(...)
end
diff --git a/lib/gitlab/database/load_balancing/service_discovery.rb b/lib/gitlab/database/load_balancing/service_discovery.rb
index 52a9e8798d4..3295301a2d7 100644
--- a/lib/gitlab/database/load_balancing/service_discovery.rb
+++ b/lib/gitlab/database/load_balancing/service_discovery.rb
@@ -125,13 +125,6 @@ module Gitlab
old_host_list_length: current.length
)
replace_hosts(from_dns)
- else
- ::Gitlab::Database::LoadBalancing::Logger.info(
- event: :host_list_unchanged,
- message: "Unchanged host list for service discovery",
- host_list_length: from_dns.length,
- old_host_list_length: current.length
- )
end
interval
diff --git a/lib/gitlab/database/load_balancing/sidekiq_client_middleware.rb b/lib/gitlab/database/load_balancing/sidekiq_client_middleware.rb
index 13afbd8fd37..619f11ae890 100644
--- a/lib/gitlab/database/load_balancing/sidekiq_client_middleware.rb
+++ b/lib/gitlab/database/load_balancing/sidekiq_client_middleware.rb
@@ -57,7 +57,7 @@ module Gitlab
if uses_primary?
load_balancer.primary_write_location
else
- load_balancer.host.database_replica_location
+ load_balancer.host&.database_replica_location || load_balancer.primary_write_location
end
end
diff --git a/lib/gitlab/database/load_balancing/sidekiq_server_middleware.rb b/lib/gitlab/database/load_balancing/sidekiq_server_middleware.rb
index 737852d5ccb..f7b8d2514ba 100644
--- a/lib/gitlab/database/load_balancing/sidekiq_server_middleware.rb
+++ b/lib/gitlab/database/load_balancing/sidekiq_server_middleware.rb
@@ -97,14 +97,8 @@ module Gitlab
end
def databases_in_sync?(wal_locations)
- locations = if Feature.enabled?(:indifferent_wal_location_keys)
- wal_locations.with_indifferent_access
- else
- wal_locations
- end
-
::Gitlab::Database::LoadBalancing.each_load_balancer.all? do |lb|
- if (location = locations[lb.name])
+ if (location = wal_locations.with_indifferent_access[lb.name])
lb.select_up_to_date_host(location)
else
# If there's no entry for a load balancer it means the Sidekiq
diff --git a/lib/gitlab/database/lock_writes_manager.rb b/lib/gitlab/database/lock_writes_manager.rb
index 2594ee04b35..e3ae2892668 100644
--- a/lib/gitlab/database/lock_writes_manager.rb
+++ b/lib/gitlab/database/lock_writes_manager.rb
@@ -10,18 +10,34 @@ module Gitlab
# See https://www.postgresql.org/message-id/16934.1568989957%40sss.pgh.pa.us
EXPECTED_TRIGGER_RECORD_COUNT = 3
+ def self.tables_to_lock(connection)
+ Gitlab::Database::GitlabSchema.tables_to_schema.each do |table_name, schema_name|
+ yield table_name, schema_name
+ end
+
+ Gitlab::Database::SharedModel.using_connection(connection) do
+ Postgresql::DetachedPartition.find_each do |detached_partition|
+ yield detached_partition.fully_qualified_table_name, detached_partition.table_schema
+ end
+ end
+ end
+
def initialize(table_name:, connection:, database_name:, logger: nil, dry_run: false)
@table_name = table_name
@connection = connection
@database_name = database_name
@logger = logger
@dry_run = dry_run
+
+ @table_name_without_schema = ActiveRecord::ConnectionAdapters::PostgreSQL::Utils
+ .extract_schema_qualified_name(table_name)
+ .identifier
end
def table_locked_for_writes?(table_name)
query = <<~SQL
SELECT COUNT(*) from information_schema.triggers
- WHERE event_object_table = '#{table_name}'
+ WHERE event_object_table = '#{table_name_without_schema}'
AND trigger_name = '#{write_trigger_name(table_name)}'
SQL
@@ -56,7 +72,7 @@ module Gitlab
private
- attr_reader :table_name, :connection, :database_name, :logger, :dry_run
+ attr_reader :table_name, :connection, :database_name, :logger, :dry_run, :table_name_without_schema
def execute_sql_statement(sql)
if dry_run
@@ -99,7 +115,7 @@ module Gitlab
end
def write_trigger_name(table_name)
- "gitlab_schema_write_trigger_for_#{table_name}"
+ "gitlab_schema_write_trigger_for_#{table_name_without_schema}"
end
end
end
diff --git a/lib/gitlab/database/migration.rb b/lib/gitlab/database/migration.rb
index ab8b6988c3d..4d38920f571 100644
--- a/lib/gitlab/database/migration.rb
+++ b/lib/gitlab/database/migration.rb
@@ -51,6 +51,10 @@ module Gitlab
include Gitlab::Database::MigrationHelpers::RestrictGitlabSchema
end
+ class V2_1 < V2_0 # rubocop:disable Naming/ClassAndModuleCamelCase
+ include Gitlab::Database::MigrationHelpers::AutomaticLockWritesOnTables
+ end
+
def self.[](version)
version = version.to_s
name = "V#{version.tr('.', '_')}"
@@ -61,7 +65,7 @@ module Gitlab
# The current version to be used in new migrations
def self.current_version
- 2.0
+ 2.1
end
end
end
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 16416dd2507..4858a96c173 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -10,6 +10,7 @@ module Gitlab
include Migrations::TimeoutHelpers
include Migrations::ConstraintsHelpers
include Migrations::ExtensionHelpers
+ include Migrations::SidekiqHelpers
include DynamicModelHelpers
include RenameTableHelpers
include AsyncIndexes::MigrationHelpers
@@ -497,17 +498,6 @@ module Gitlab
end
end
- # Adds a column with a default value without locking an entire table.
- #
- # @deprecated With PostgreSQL 11, adding columns with a default does not lead to a table rewrite anymore.
- # As such, this method is not needed anymore and the default `add_column` helper should be used.
- # This helper is subject to be removed in a >13.0 release.
- def add_column_with_default(table, column, type, default:, limit: nil, allow_null: false)
- raise 'Deprecated: add_column_with_default does not support being passed blocks anymore' if block_given?
-
- add_column(table, column, type, default: default, limit: limit, null: allow_null)
- end
-
# Renames a column without requiring downtime.
#
# Concurrent renames work by using database triggers to ensure both the
@@ -1027,38 +1017,6 @@ module Gitlab
rescue ArgumentError
end
- # Remove any instances of deprecated job classes lingering in queues.
- #
- # rubocop:disable Cop/SidekiqApiUsage
- def sidekiq_remove_jobs(job_klass:)
- Sidekiq::Queue.new(job_klass.queue).each do |job|
- job.delete if job.klass == job_klass.to_s
- end
-
- Sidekiq::RetrySet.new.each do |retri|
- retri.delete if retri.klass == job_klass.to_s
- end
-
- Sidekiq::ScheduledSet.new.each do |scheduled|
- scheduled.delete if scheduled.klass == job_klass.to_s
- end
- end
- # rubocop:enable Cop/SidekiqApiUsage
-
- def sidekiq_queue_migrate(queue_from, to:)
- while sidekiq_queue_length(queue_from) > 0
- Sidekiq.redis do |conn|
- conn.rpoplpush "queue:#{queue_from}", "queue:#{to}"
- end
- end
- end
-
- def sidekiq_queue_length(queue_name)
- Sidekiq.redis do |conn|
- conn.llen("queue:#{queue_name}")
- end
- end
-
def check_trigger_permissions!(table)
unless Grant.create_and_execute_trigger?(table)
dbname = ApplicationRecord.database.database_name
diff --git a/lib/gitlab/database/migration_helpers/automatic_lock_writes_on_tables.rb b/lib/gitlab/database/migration_helpers/automatic_lock_writes_on_tables.rb
new file mode 100644
index 00000000000..0aa4b0d01c4
--- /dev/null
+++ b/lib/gitlab/database/migration_helpers/automatic_lock_writes_on_tables.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module MigrationHelpers
+ module AutomaticLockWritesOnTables
+ extend ActiveSupport::Concern
+
+ included do
+ class_attribute :skip_automatic_lock_on_writes
+ end
+
+ def exec_migration(connection, direction)
+ return super if %w[main ci].exclude?(Gitlab::Database.db_config_name(connection))
+ return super if automatic_lock_on_writes_disabled?
+
+ # This compares the tables only on the `public` schema. Partitions are not affected
+ tables = connection.tables
+ super
+ new_tables = connection.tables - tables
+
+ new_tables.each do |table_name|
+ lock_writes_on_table(connection, table_name) if should_lock_writes_on_table?(table_name)
+ end
+ end
+
+ private
+
+ def automatic_lock_on_writes_disabled?
+ # Feature flags are set on the main database, see tables features/feature_gates.
+ # That is why we switch the ActiveRecord::Base.connection temporarily here back to the 'main' database
+ # for the cases when the migration is targeting another database, like the 'ci' database.
+ with_restored_connection_stack do |_|
+ Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas.with_suppressed do
+ skip_automatic_lock_on_writes ||
+ Gitlab::Utils.to_boolean(ENV['SKIP_AUTOMATIC_LOCK_ON_WRITES']) ||
+ Feature.disabled?(:automatic_lock_writes_on_table, type: :ops)
+ end
+ end
+ end
+
+ def should_lock_writes_on_table?(table_name)
+ # currently gitlab_schema represents only present existing tables, this is workaround for deleted tables
+ # that should be skipped as they will be removed in a future migration.
+ return false if Gitlab::Database::GitlabSchema::DELETED_TABLES[table_name]
+
+ table_schema = Gitlab::Database::GitlabSchema.table_schema(table_name.to_s, undefined: false)
+
+ if table_schema.nil?
+ error_message = <<~ERROR
+ No gitlab_schema is defined for the table #{table_name}. Please consider
+ adding it to the database dictionary.
+ More info: https://docs.gitlab.com/ee/development/database/database_dictionary.html
+ ERROR
+ raise error_message
+ end
+
+ return false unless %i[gitlab_main gitlab_ci].include?(table_schema)
+
+ Gitlab::Database.gitlab_schemas_for_connection(connection).exclude?(table_schema)
+ end
+
+ def lock_writes_on_table(connection, table_name)
+ database_name = Gitlab::Database.db_config_name(connection)
+ LockWritesManager.new(
+ table_name: table_name,
+ connection: connection,
+ database_name: database_name,
+ logger: Logger.new($stdout)
+ ).lock_writes
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/migrations/batched_migration_last_id.rb b/lib/gitlab/database/migrations/batched_migration_last_id.rb
new file mode 100644
index 00000000000..c77a2e9a375
--- /dev/null
+++ b/lib/gitlab/database/migrations/batched_migration_last_id.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module Migrations
+ class BatchedMigrationLastId
+ FILE_NAME = 'last-batched-background-migration-id.txt'
+
+ def initialize(connection, base_dir)
+ @connection = connection
+ @base_dir = base_dir
+ end
+
+ def store
+ File.open(file_path, 'wb') { |file| file.write(last_background_migration_id) }
+ end
+
+ # Reads the last id from the file
+ #
+ # @info casts the file content into an +Integer+.
+ # Casts any unexpected content to +nil+
+ #
+ # @example
+ # Integer('4', exception: false) # => 4
+ # Integer('', exception: false) # => nil
+ #
+ # @return [Integer, nil]
+ def read
+ return unless File.exist?(file_path)
+
+ Integer(File.read(file_path).presence, exception: false)
+ end
+
+ private
+
+ attr_reader :connection, :base_dir
+
+ def file_path
+ @file_path ||= base_dir.join(FILE_NAME)
+ end
+
+ def last_background_migration_id
+ Gitlab::Database::SharedModel.using_connection(connection) do
+ Gitlab::Database::BackgroundMigration::BatchedMigration.maximum(:id)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/migrations/runner.rb b/lib/gitlab/database/migrations/runner.rb
index 27b161419b2..ed55081c9ab 100644
--- a/lib/gitlab/database/migrations/runner.rb
+++ b/lib/gitlab/database/migrations/runner.rb
@@ -29,16 +29,14 @@ module Gitlab
def batched_background_migrations(for_database:, legacy_mode: false)
runner = nil
- result_dir = if legacy_mode
- BASE_RESULT_DIR.join('background_migrations')
- else
- BASE_RESULT_DIR.join(for_database.to_s, 'background_migrations')
- end
+ result_dir = background_migrations_dir(for_database, legacy_mode)
# Only one loop iteration since we pass `only:` here
Gitlab::Database::EachDatabase.each_database_connection(only: for_database) do |connection|
+ from_id = batched_migrations_last_id(for_database).read
+
runner = Gitlab::Database::Migrations::TestBatchedBackgroundRunner
- .new(result_dir: result_dir, connection: connection)
+ .new(result_dir: result_dir, connection: connection, from_id: from_id)
end
runner
@@ -66,6 +64,18 @@ module Gitlab
end
# rubocop:enable Database/MultipleDatabases
+ def batched_migrations_last_id(for_database)
+ runner = nil
+ base_dir = background_migrations_dir(for_database, false)
+
+ Gitlab::Database::EachDatabase.each_database_connection(only: for_database) do |connection|
+ runner = Gitlab::Database::Migrations::BatchedMigrationLastId
+ .new(connection, base_dir)
+ end
+
+ runner
+ end
+
private
def migrations_for_up(database)
@@ -90,6 +100,12 @@ module Gitlab
existing_versions.include?(migration.version) && versions_this_branch.include?(migration.version)
end
end
+
+ def background_migrations_dir(db, legacy_mode)
+ return BASE_RESULT_DIR.join('background_migrations') if legacy_mode
+
+ BASE_RESULT_DIR.join(db.to_s, 'background_migrations')
+ end
end
attr_reader :direction, :result_dir, :migrations
diff --git a/lib/gitlab/database/migrations/sidekiq_helpers.rb b/lib/gitlab/database/migrations/sidekiq_helpers.rb
new file mode 100644
index 00000000000..c536b33bbdf
--- /dev/null
+++ b/lib/gitlab/database/migrations/sidekiq_helpers.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module Migrations
+ # rubocop:disable Cop/SidekiqApiUsage
+ # rubocop:disable Cop/SidekiqRedisCall
+ module SidekiqHelpers
+ # Constants for default sidekiq_remove_jobs values
+ DEFAULT_MAX_ATTEMPTS = 5
+ DEFAULT_TIMES_IN_A_ROW = 2
+
+ # Probabilistically removes job_klasses from their specific queues, the
+ # retry set and the scheduled set.
+ #
+ # If jobs are still being processed at the same time, then there is a
+ # small chance it will not remove all instances of job_klass. To
+ # minimize this risk, it repeatedly removes matching jobs from each
+ # until nothing is removed twice in a row.
+ #
+ # Before calling this method, you should make sure that job_klass is no
+ # longer being scheduled within the running application.
+ def sidekiq_remove_jobs(
+ job_klasses:,
+ times_in_a_row: DEFAULT_TIMES_IN_A_ROW,
+ max_attempts: DEFAULT_MAX_ATTEMPTS
+ )
+
+ kwargs = { times_in_a_row: times_in_a_row, max_attempts: max_attempts }
+
+ job_klasses_queues = job_klasses
+ .select { |job_klass| job_klass.to_s.safe_constantize.present? }
+ .map { |job_klass| job_klass.safe_constantize.queue }
+ .uniq
+
+ job_klasses_queues.each do |queue|
+ delete_jobs_for(
+ set: Sidekiq::Queue.new(queue),
+ job_klasses: job_klasses,
+ kwargs: kwargs
+ )
+ end
+
+ delete_jobs_for(
+ set: Sidekiq::RetrySet.new,
+ kwargs: kwargs,
+ job_klasses: job_klasses
+ )
+
+ delete_jobs_for(
+ set: Sidekiq::ScheduledSet.new,
+ kwargs: kwargs,
+ job_klasses: job_klasses
+ )
+ end
+
+ def sidekiq_queue_migrate(queue_from, to:)
+ while sidekiq_queue_length(queue_from) > 0
+ Sidekiq.redis do |conn|
+ conn.rpoplpush "queue:#{queue_from}", "queue:#{to}"
+ end
+ end
+ end
+
+ def sidekiq_queue_length(queue_name)
+ Sidekiq.redis do |conn|
+ conn.llen("queue:#{queue_name}")
+ end
+ end
+
+ private
+
+ # Handle the "jobs deleted" tracking that is needed in order to track
+ # whether a job was deleted or not.
+ def delete_jobs_for(set:, kwargs:, job_klasses:)
+ until_equal_to(0, **kwargs) do
+ set.count do |job|
+ job_klasses.include?(job.klass) && job.delete
+ end
+ end
+ end
+
+ # Control how many times in a row you want to see a job deleted 0
+ # times. The idea is that if you see 0 jobs deleted x number of times
+ # in a row you've *likely* covered the case in which the queue was
+ # mutating while this was running.
+ def until_equal_to(target, times_in_a_row:, max_attempts:)
+ streak = 0
+
+ result = { attempts: 0, success: false }
+
+ 1.upto(max_attempts) do |current_attempt|
+ # yield's return value is a count of "jobs_deleted"
+ if yield == target
+ streak += 1
+ elsif streak > 0
+ streak = 0
+ end
+
+ result[:attempts] = current_attempt
+ result[:success] = streak == times_in_a_row
+
+ break if result[:success]
+ end
+ result
+ end
+ end
+ # rubocop:enable Cop/SidekiqApiUsage
+ # rubocop:enable Cop/SidekiqRedisCall
+ end
+ end
+end
diff --git a/lib/gitlab/database/migrations/test_batched_background_runner.rb b/lib/gitlab/database/migrations/test_batched_background_runner.rb
index 46855ca1921..a16103f452c 100644
--- a/lib/gitlab/database/migrations/test_batched_background_runner.rb
+++ b/lib/gitlab/database/migrations/test_batched_background_runner.rb
@@ -6,16 +6,17 @@ module Gitlab
class TestBatchedBackgroundRunner < BaseBackgroundRunner
include Gitlab::Database::DynamicModelHelpers
- def initialize(result_dir:, connection:)
+ def initialize(result_dir:, connection:, from_id:)
super(result_dir: result_dir, connection: connection)
@connection = connection
+ @from_id = from_id
end
def jobs_by_migration_name
Gitlab::Database::SharedModel.using_connection(connection) do
Gitlab::Database::BackgroundMigration::BatchedMigration
.executable
- .created_after(3.hours.ago) # Simple way to exclude migrations already running before migration testing
+ .where('id > ?', from_id)
.to_h do |migration|
batching_strategy = migration.batch_class.new(connection: connection)
@@ -102,6 +103,10 @@ module Gitlab
end
end
end
+
+ private
+
+ attr_reader :from_id
end
end
end
diff --git a/lib/gitlab/database/obsolete_ignored_columns.rb b/lib/gitlab/database/obsolete_ignored_columns.rb
index ad5473f1b74..2b88ab12380 100644
--- a/lib/gitlab/database/obsolete_ignored_columns.rb
+++ b/lib/gitlab/database/obsolete_ignored_columns.rb
@@ -23,8 +23,8 @@ module Gitlab
private
def ignored_columns_safe_to_remove_for(klass)
- ignores = ignored_and_not_present(klass).each_with_object({}) do |col, h|
- h[col] = klass.ignored_columns_details[col.to_sym]
+ ignores = ignored_and_not_present(klass).index_with do |col|
+ klass.ignored_columns_details[col.to_sym]
end
ignores.select { |_, i| i&.safe_to_remove? }
diff --git a/lib/gitlab/database/partitioning/single_numeric_list_partition.rb b/lib/gitlab/database/partitioning/single_numeric_list_partition.rb
index 4e38eea963b..fd99062974c 100644
--- a/lib/gitlab/database/partitioning/single_numeric_list_partition.rb
+++ b/lib/gitlab/database/partitioning/single_numeric_list_partition.rb
@@ -19,7 +19,7 @@ module Gitlab
attr_reader :table, :value
- def initialize(table, value, partition_name: nil )
+ def initialize(table, value, partition_name: nil)
@table = table
@value = value
@partition_name = partition_name
diff --git a/lib/gitlab/database/postgres_hll/buckets.rb b/lib/gitlab/database/postgres_hll/buckets.rb
index cbc9544d905..3f64eee030e 100644
--- a/lib/gitlab/database/postgres_hll/buckets.rb
+++ b/lib/gitlab/database/postgres_hll/buckets.rb
@@ -61,7 +61,7 @@ module Gitlab
num_uniques = (
((TOTAL_BUCKETS**2) * (0.7213 / (1 + 1.079 / TOTAL_BUCKETS))) /
- (num_zero_buckets + buckets.values.sum { |bucket_hash| 2**(-1 * bucket_hash) } )
+ (num_zero_buckets + buckets.values.sum { |bucket_hash| 2**(-1 * bucket_hash) })
).to_i
if num_zero_buckets > 0 && num_uniques < 2.5 * TOTAL_BUCKETS
diff --git a/lib/gitlab/database/query_analyzers/prevent_cross_database_modification.rb b/lib/gitlab/database/query_analyzers/prevent_cross_database_modification.rb
index 3b1751c863d..dd10e0d7992 100644
--- a/lib/gitlab/database/query_analyzers/prevent_cross_database_modification.rb
+++ b/lib/gitlab/database/query_analyzers/prevent_cross_database_modification.rb
@@ -165,8 +165,8 @@ module Gitlab
def self.in_factory_bot_create?
Rails.env.test? && caller_locations.any? do |l|
l.path.end_with?('lib/factory_bot/evaluation.rb') && l.label == 'create' ||
- l.path.end_with?('lib/factory_bot/strategy/create.rb') ||
- l.path.end_with?('shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb') && l.label == 'create_existing_record'
+ l.path.end_with?('lib/factory_bot/strategy/create.rb') ||
+ l.path.end_with?('shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb') && l.label == 'create_existing_record'
end
end
end
diff --git a/lib/gitlab/database/query_analyzers/query_recorder.rb b/lib/gitlab/database/query_analyzers/query_recorder.rb
index 88fe829c3d2..b54f3442512 100644
--- a/lib/gitlab/database/query_analyzers/query_recorder.rb
+++ b/lib/gitlab/database/query_analyzers/query_recorder.rb
@@ -4,7 +4,7 @@ module Gitlab
module Database
module QueryAnalyzers
class QueryRecorder < Base
- LOG_FILE = 'rspec/query_recorder.ndjson'
+ LOG_PATH = 'query_recorder/'
class << self
def raw?
@@ -12,8 +12,9 @@ module Gitlab
end
def enabled?
- # Only enable QueryRecorder in CI
- ENV['CI'].present?
+ # Only enable QueryRecorder in CI on database MRs or default branch
+ ENV['CI_MERGE_REQUEST_LABELS']&.include?('database') ||
+ (ENV['CI_COMMIT_REF_NAME'].present? && ENV['CI_COMMIT_REF_NAME'] == ENV['CI_DEFAULT_BRANCH'])
end
def analyze(sql)
@@ -24,11 +25,14 @@ module Gitlab
log_query(payload)
end
+ def log_file
+ Rails.root.join(LOG_PATH, "#{ENV.fetch('CI_JOB_NAME_SLUG', 'rspec')}.ndjson")
+ end
+
private
def log_query(payload)
- log_path = Rails.root.join(LOG_FILE)
- log_dir = File.dirname(log_path)
+ log_dir = Rails.root.join(LOG_PATH)
# Create log directory if it does not exist since it is only created
# ahead of time by certain CI jobs
@@ -36,7 +40,7 @@ module Gitlab
log_line = "#{Gitlab::Json.dump(payload)}\n"
- File.write(log_path, log_line, mode: 'a')
+ File.write(log_file, log_line, mode: 'a')
end
end
end
diff --git a/lib/gitlab/database/schema_cache_with_renamed_table.rb b/lib/gitlab/database/schema_cache_with_renamed_table.rb
index 74900dc0d26..6da76803f7c 100644
--- a/lib/gitlab/database/schema_cache_with_renamed_table.rb
+++ b/lib/gitlab/database/schema_cache_with_renamed_table.rb
@@ -40,10 +40,8 @@ module Gitlab
end
def renamed_tables_cache
- @renamed_tables ||= begin
- Gitlab::Database::TABLES_TO_BE_RENAMED.select do |old_name, new_name|
- connection.view_exists?(old_name)
- end
+ @renamed_tables ||= Gitlab::Database::TABLES_TO_BE_RENAMED.select do |old_name, new_name|
+ connection.view_exists?(old_name)
end
end
diff --git a/lib/gitlab/database/schema_cleaner.rb b/lib/gitlab/database/schema_cleaner.rb
index c3cdcf1450d..2c8d0a4eb6d 100644
--- a/lib/gitlab/database/schema_cleaner.rb
+++ b/lib/gitlab/database/schema_cleaner.rb
@@ -25,7 +25,23 @@ module Gitlab
# The intention here is to not introduce an assumption about the standard schema,
# unless we have a good reason to do so.
structure.gsub!(/public\.(\w+)/, '\1')
- structure.gsub!(/CREATE EXTENSION IF NOT EXISTS (\w+) WITH SCHEMA public;/, 'CREATE EXTENSION IF NOT EXISTS \1;')
+ structure.gsub!(
+ /CREATE EXTENSION IF NOT EXISTS (\w+) WITH SCHEMA public;/,
+ 'CREATE EXTENSION IF NOT EXISTS \1;'
+ )
+
+ # Table lock-writes triggers should not be added to the schema
+ # These triggers are added by the rake task gitlab:db:lock_writes for a decomposed database.
+ structure.gsub!(
+ %r{
+ ^CREATE.TRIGGER.gitlab_schema_write_trigger_\w+
+ \s
+ BEFORE.INSERT.OR.DELETE.OR.UPDATE.OR.TRUNCATE.ON.\w+
+ \s
+ FOR.EACH.STATEMENT.EXECUTE.FUNCTION.gitlab_schema_prevent_write\(\);$
+ }x,
+ ''
+ )
structure.gsub!(/\n{3,}/, "\n\n")
diff --git a/lib/gitlab/database/tables_sorted_by_foreign_keys.rb b/lib/gitlab/database/tables_sorted_by_foreign_keys.rb
index 9f096904d31..b2a7f5442e9 100644
--- a/lib/gitlab/database/tables_sorted_by_foreign_keys.rb
+++ b/lib/gitlab/database/tables_sorted_by_foreign_keys.rb
@@ -26,15 +26,32 @@ module Gitlab
# it maps the tables to the tables that depend on it
def tables_dependencies
- @tables.to_h do |table_name|
- [table_name, all_foreign_keys[table_name]&.map(&:from_table).to_a]
+ @tables.index_with do |table_name|
+ all_foreign_keys[table_name]
end
end
def all_foreign_keys
- @all_foreign_keys ||= @tables.flat_map do |table_name|
- @connection.foreign_keys(table_name)
- end.group_by(&:to_table)
+ @all_foreign_keys ||= @tables.each_with_object(Hash.new { |h, k| h[k] = [] }) do |table, hash|
+ foreign_keys_for(table).each do |fk|
+ hash[fk.to_table] << table
+ end
+ end
+ end
+
+ def foreign_keys_for(table)
+ # Detached partitions like gitlab_partitions_dynamic._test_gitlab_partition_20220101
+ # store their foreign keys in the public schema.
+ #
+ # See spec/lib/gitlab/database/tables_sorted_by_foreign_keys_spec.rb
+ # for an example
+ name = ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(table)
+
+ if name.schema == ::Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA.to_s
+ @connection.foreign_keys(name.identifier)
+ else
+ @connection.foreign_keys(table)
+ end
end
end
end
diff --git a/lib/gitlab/database/tables_truncate.rb b/lib/gitlab/database/tables_truncate.rb
index 8380bf23899..807ecdb862a 100644
--- a/lib/gitlab/database/tables_truncate.rb
+++ b/lib/gitlab/database/tables_truncate.rb
@@ -19,24 +19,32 @@ module Gitlab
logger&.info "DRY RUN:" if dry_run
- connection = Gitlab::Database.database_base_models[database_name].connection
-
schemas_for_connection = Gitlab::Database.gitlab_schemas_for_connection(connection)
tables_to_truncate = Gitlab::Database::GitlabSchema.tables_to_schema.reject do |_, schema_name|
- (GITLAB_SCHEMAS_TO_IGNORE.union(schemas_for_connection)).include?(schema_name)
+ GITLAB_SCHEMAS_TO_IGNORE.union(schemas_for_connection).include?(schema_name)
end.keys
+ Gitlab::Database::SharedModel.using_connection(connection) do
+ Postgresql::DetachedPartition.find_each do |detached_partition|
+ next if GITLAB_SCHEMAS_TO_IGNORE.union(schemas_for_connection).include?(detached_partition.table_schema)
+
+ tables_to_truncate << detached_partition.fully_qualified_table_name
+ end
+ end
+
tables_sorted = Gitlab::Database::TablesSortedByForeignKeys.new(connection, tables_to_truncate).execute
# Checking if all the tables have the write-lock triggers
# to make sure we are deleting the right tables on the right database.
tables_sorted.flatten.each do |table_name|
- query = <<~SQL
- SELECT COUNT(*) from information_schema.triggers
- WHERE event_object_table = '#{table_name}'
- AND trigger_name = 'gitlab_schema_write_trigger_for_#{table_name}'
- SQL
-
- if connection.select_value(query) == 0
+ lock_writes_manager = Gitlab::Database::LockWritesManager.new(
+ table_name: table_name,
+ connection: connection,
+ database_name: database_name,
+ logger: logger,
+ dry_run: dry_run
+ )
+
+ unless lock_writes_manager.table_locked_for_writes?(table_name)
raise "Table '#{table_name}' is not locked for writes. Run the rake task gitlab:db:lock_writes first"
end
end
@@ -51,18 +59,26 @@ module Gitlab
# min_batch_size is the minimum number of new tables to truncate at each stage.
# But in each stage we have also have to truncate the already truncated tables in the previous stages
logger&.info "Truncating legacy tables for the database #{database_name}"
- truncate_tables_in_batches(connection, tables_sorted, min_batch_size)
+ truncate_tables_in_batches(tables_sorted)
end
private
attr_accessor :database_name, :min_batch_size, :logger, :dry_run, :until_table
- def truncate_tables_in_batches(connection, tables_sorted, min_batch_size)
+ def connection
+ @connection ||= Gitlab::Database.database_base_models[database_name].connection
+ end
+
+ def truncate_tables_in_batches(tables_sorted)
truncated_tables = []
tables_sorted.flatten.each do |table|
- sql_statement = "SELECT set_config('lock_writes.#{table}', 'false', false)"
+ table_name_without_schema = ActiveRecord::ConnectionAdapters::PostgreSQL::Utils
+ .extract_schema_qualified_name(table)
+ .identifier
+
+ sql_statement = "SELECT set_config('lock_writes.#{table_name_without_schema}', 'false', false)"
logger&.info(sql_statement)
connection.execute(sql_statement) unless dry_run
end
diff --git a/lib/gitlab/database/type/indifferent_jsonb.rb b/lib/gitlab/database/type/indifferent_jsonb.rb
new file mode 100644
index 00000000000..69bbcb383ba
--- /dev/null
+++ b/lib/gitlab/database/type/indifferent_jsonb.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module Type
+ # Extends Rails' Jsonb data type to deserialize it into indifferent access Hash.
+ #
+ # Example:
+ #
+ # class SomeModel < ApplicationRecord
+ # # some_model.a_field is of type `jsonb`
+ # attribute :a_field, :ind_jsonb
+ # end
+ class IndifferentJsonb < ::ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Jsonb
+ def type
+ :ind_jsonb
+ end
+
+ def deserialize(value)
+ data = super
+ return unless data
+
+ ::Gitlab::Utils.deep_indifferent_access(data)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database_importers/work_items/hierarchy_restrictions_importer.rb b/lib/gitlab/database_importers/work_items/hierarchy_restrictions_importer.rb
new file mode 100644
index 00000000000..1181c259a5c
--- /dev/null
+++ b/lib/gitlab/database_importers/work_items/hierarchy_restrictions_importer.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module DatabaseImporters
+ module WorkItems
+ module HierarchyRestrictionsImporter
+ def self.upsert_restrictions
+ objective = find_or_create_type(::WorkItems::Type::TYPE_NAMES[:objective])
+ key_result = find_or_create_type(::WorkItems::Type::TYPE_NAMES[:key_result])
+ issue = find_or_create_type(::WorkItems::Type::TYPE_NAMES[:issue])
+ task = find_or_create_type(::WorkItems::Type::TYPE_NAMES[:task])
+ incident = find_or_create_type(::WorkItems::Type::TYPE_NAMES[:incident])
+
+ restrictions = [
+ { parent_type_id: objective.id, child_type_id: objective.id, maximum_depth: 9 },
+ { parent_type_id: objective.id, child_type_id: key_result.id, maximum_depth: 1 },
+ { parent_type_id: issue.id, child_type_id: task.id, maximum_depth: 1 },
+ { parent_type_id: incident.id, child_type_id: task.id, maximum_depth: 1 }
+ ]
+
+ ::WorkItems::HierarchyRestriction.upsert_all(
+ restrictions,
+ unique_by: :index_work_item_hierarchy_restrictions_on_parent_and_child
+ )
+ end
+
+ def self.find_or_create_type(name)
+ type = ::WorkItems::Type.find_by_name_and_namespace_id(name, nil)
+ return type if type
+
+ Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.upsert_types
+ ::WorkItems::Type.find_by_name_and_namespace_id(name, nil)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/diff/file_collection/compare.rb b/lib/gitlab/diff/file_collection/compare.rb
index 6d8395d048d..df0d71f3db2 100644
--- a/lib/gitlab/diff/file_collection/compare.rb
+++ b/lib/gitlab/diff/file_collection/compare.rb
@@ -4,7 +4,15 @@ module Gitlab
module Diff
module FileCollection
class Compare < Base
+ delegate :limit_value, :current_page, :next_page, :prev_page, :total_count, :total_pages, to: :@pagination
+
def initialize(compare, project:, diff_options:, diff_refs: nil)
+ @pagination = Gitlab::PaginationDelegate.new(
+ page: diff_options&.delete(:page),
+ per_page: diff_options&.delete(:per_page),
+ count: diff_options&.delete(:count)
+ )
+
super(compare,
project: project,
diff_options: diff_options,
diff --git a/lib/gitlab/diff/file_collection/merge_request_diff_batch.rb b/lib/gitlab/diff/file_collection/merge_request_diff_batch.rb
index 0a601bde612..56027d6a4de 100644
--- a/lib/gitlab/diff/file_collection/merge_request_diff_batch.rb
+++ b/lib/gitlab/diff/file_collection/merge_request_diff_batch.rb
@@ -10,6 +10,8 @@ module Gitlab
# separate file keys (https://gitlab.com/gitlab-org/gitlab/issues/30550).
#
class MergeRequestDiffBatch < MergeRequestDiffBase
+ include PaginatedDiffs
+
DEFAULT_BATCH_PAGE = 1
DEFAULT_BATCH_SIZE = 30
@@ -25,41 +27,8 @@ module Gitlab
}
end
- override :diffs
- def diffs
- strong_memoize(:diffs) do
- @merge_request_diff.opening_external_diff do
- # Avoiding any extra queries.
- collection = @paginated_collection.to_a
-
- # The offset collection and calculation is required so that we
- # know how much has been loaded in previous batches, collapsing
- # the current paginated set accordingly (collection limit calculation).
- # See: https://docs.gitlab.com/ee/development/diffs.html#diff-collection-limits
- #
- offset_index = collection.first&.index
- options = diff_options.dup
-
- collection =
- if offset_index && offset_index > 0
- offset_collection = relation.limit(offset_index) # rubocop:disable CodeReuse/ActiveRecord
- options[:offset_index] = offset_index
- offset_collection + collection
- else
- collection
- end
-
- Gitlab::Git::DiffCollection.new(collection.map(&:to_hash), options)
- end
- end
- end
-
private
- def relation
- @merge_request_diff.merge_request_diff_files
- end
-
# rubocop: disable CodeReuse/ActiveRecord
def load_paginated_collection(batch_page, batch_size, diff_options)
batch_page ||= DEFAULT_BATCH_PAGE
diff --git a/lib/gitlab/diff/file_collection/paginated_diffs.rb b/lib/gitlab/diff/file_collection/paginated_diffs.rb
new file mode 100644
index 00000000000..63c186affe9
--- /dev/null
+++ b/lib/gitlab/diff/file_collection/paginated_diffs.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Diff
+ module FileCollection
+ module PaginatedDiffs
+ include Gitlab::Utils::StrongMemoize
+ extend ::Gitlab::Utils::Override
+
+ override :diffs
+ def diffs
+ merge_request_diff.opening_external_diff do
+ # Avoiding any extra queries.
+ collection = paginated_collection.to_a
+
+ # The offset collection and calculation is required so that we
+ # know how much has been loaded in previous batches, collapsing
+ # the current paginated set accordingly (collection limit calculation).
+ # See: https://docs.gitlab.com/ee/development/diffs.html#diff-collection-limits
+ #
+ offset_index = collection.first&.index
+ options = diff_options.dup
+
+ collection =
+ if offset_index && offset_index > 0
+ offset_collection = relation.limit(offset_index) # rubocop:disable CodeReuse/ActiveRecord
+ options[:offset_index] = offset_index
+ offset_collection + collection
+ else
+ collection
+ end
+
+ Gitlab::Git::DiffCollection.new(collection.map(&:to_hash), options)
+ end
+ end
+ strong_memoize_attr :diffs
+
+ private
+
+ attr_reader :merge_request_diff, :paginated_collection
+
+ def relation
+ merge_request_diff.merge_request_diff_files
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/diff/file_collection/paginated_merge_request_diff.rb b/lib/gitlab/diff/file_collection/paginated_merge_request_diff.rb
new file mode 100644
index 00000000000..37abad81305
--- /dev/null
+++ b/lib/gitlab/diff/file_collection/paginated_merge_request_diff.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Diff
+ module FileCollection
+ # Builds a traditional paginated diff file collection using Kaminari
+ # `per` and `per_page` which is different from how `MergeRequestDiffBatch`
+ # works (e.g. supports gradual loading).
+ class PaginatedMergeRequestDiff < MergeRequestDiffBase
+ include PaginatedDiffs
+
+ DEFAULT_PAGE = 1
+ DEFAULT_PER_PAGE = 30
+
+ delegate :limit_value, :current_page, :next_page, :prev_page, :total_count,
+ :total_pages, to: :paginated_collection
+
+ def initialize(merge_request_diff, page, per_page)
+ super(merge_request_diff, diff_options: nil)
+
+ @paginated_collection = load_paginated_collection(page, per_page)
+ end
+
+ private
+
+ def load_paginated_collection(page, per_page)
+ page ||= DEFAULT_PAGE
+ per_page ||= DEFAULT_PER_PAGE
+
+ relation.page(page).per([per_page.to_i, DEFAULT_PER_PAGE].min)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/diff/parser.rb b/lib/gitlab/diff/parser.rb
index 924c28e3db5..b29c75ed467 100644
--- a/lib/gitlab/diff/parser.rb
+++ b/lib/gitlab/diff/parser.rb
@@ -73,7 +73,7 @@ module Gitlab
private
def filename?(line)
- line.start_with?( '--- /dev/null', '+++ /dev/null', '--- a', '+++ b',
+ line.start_with?('--- /dev/null', '+++ /dev/null', '--- a', '+++ b',
'+++ a', # The line will start with `+++ a` in the reverse diff of an orphan commit
'--- /tmp/diffy', '+++ /tmp/diffy')
end
diff --git a/lib/gitlab/email/receiver.rb b/lib/gitlab/email/receiver.rb
index 1e03f5d17ee..32794a6c99d 100644
--- a/lib/gitlab/email/receiver.rb
+++ b/lib/gitlab/email/receiver.rb
@@ -192,7 +192,7 @@ module Gitlab
auto_submitted = mail.header['Auto-Submitted']&.value
# Mail::Field#value would strip leading and trailing whitespace
- # See also https://tools.ietf.org/html/rfc3834
+ # See also https://www.rfc-editor.org/rfc/rfc3834
auto_submitted && auto_submitted != 'no'
end
diff --git a/lib/gitlab/error_tracking/error_repository/open_api_strategy.rb b/lib/gitlab/error_tracking/error_repository/open_api_strategy.rb
index cc822e4c10b..e168fa10630 100644
--- a/lib/gitlab/error_tracking/error_repository/open_api_strategy.rb
+++ b/lib/gitlab/error_tracking/error_repository/open_api_strategy.rb
@@ -228,7 +228,7 @@ module Gitlab
def configured_api_url
url = Gitlab::CurrentSettings.current_application_settings.error_tracking_api_url ||
- 'http://localhost:8080'
+ 'http://localhost:8080'
Gitlab::UrlBlocker.validate!(url, schemes: %w[http https], allow_localhost: true)
diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb
index 721518c6fcc..8e48b482462 100644
--- a/lib/gitlab/favicon.rb
+++ b/lib/gitlab/favicon.rb
@@ -34,11 +34,9 @@ module Gitlab
end
def available_status_names
- @available_status_names ||= begin
- Dir.glob(Rails.root.join('app', 'assets', 'images', 'ci_favicons', '*.png'))
+ @available_status_names ||= Dir.glob(Rails.root.join('app', 'assets', 'images', 'ci_favicons', '*.png'))
.map { |file| File.basename(file, '.png') }
.sort
- end
end
private
diff --git a/lib/gitlab/gfm/uploads_rewriter.rb b/lib/gitlab/gfm/uploads_rewriter.rb
index 58b46a85aae..05d680c139c 100644
--- a/lib/gitlab/gfm/uploads_rewriter.rb
+++ b/lib/gitlab/gfm/uploads_rewriter.rb
@@ -54,8 +54,6 @@ module Gitlab
file = find_file(match[:secret], match[:file])
# No file will be returned for a path traversal
- return '' if file.nil?
-
return markdown unless file.try(:exists?)
klass = @target_parent.is_a?(Namespace) ? NamespaceFileUploader : FileUploader
diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb
index 4b877bf44da..8e1b51fcec5 100644
--- a/lib/gitlab/git.rb
+++ b/lib/gitlab/git.rb
@@ -16,6 +16,7 @@ module Gitlab
CommitError = Class.new(BaseError)
OSError = Class.new(BaseError)
UnknownRef = Class.new(BaseError)
+ AmbiguousRef = Class.new(BaseError)
CommandTimedOut = Class.new(CommandError)
InvalidPageToken = Class.new(BaseError)
InvalidRefFormatError = Class.new(BaseError)
diff --git a/lib/gitlab/git/base_error.rb b/lib/gitlab/git/base_error.rb
index a7eaa82b347..0b0fdef54cc 100644
--- a/lib/gitlab/git/base_error.rb
+++ b/lib/gitlab/git/base_error.rb
@@ -1,20 +1,50 @@
# frozen_string_literal: true
+require 'grpc'
module Gitlab
module Git
class BaseError < StandardError
DEBUG_ERROR_STRING_REGEX = /(.*?) debug_error_string:.*$/m.freeze
+ GRPC_CODES = {
+ '0' => 'ok',
+ '1' => 'cancelled',
+ '2' => 'unknown',
+ '3' => 'invalid_argument',
+ '4' => 'deadline_exceeded',
+ '5' => 'not_found',
+ '6' => 'already_exists',
+ '7' => 'permission_denied',
+ '8' => 'resource_exhausted',
+ '9' => 'failed_precondition',
+ '10' => 'aborted',
+ '11' => 'out_of_range',
+ '12' => 'unimplemented',
+ '13' => 'internal',
+ '14' => 'unavailable',
+ '15' => 'data_loss',
+ '16' => 'unauthenticated'
+ }.freeze
+
+ attr_reader :status, :code, :service
def initialize(msg = nil)
- if msg
- raw_message = msg.to_s
- match = DEBUG_ERROR_STRING_REGEX.match(raw_message)
- raw_message = match[1] if match
+ super && return if msg.nil?
+
+ set_grpc_error_code(msg) if msg.is_a?(::GRPC::BadStatus)
+
+ super(build_raw_message(msg))
+ end
+
+ def build_raw_message(message)
+ raw_message = message.to_s
+ match = DEBUG_ERROR_STRING_REGEX.match(raw_message)
+ match ? match[1] : raw_message
+ end
- super(raw_message)
- else
- super
- end
+ def set_grpc_error_code(grpc_error)
+ @status = grpc_error.code
+ @code = GRPC_CODES[@status.to_s]
+ @service = 'git'
end
end
end
diff --git a/lib/gitlab/git/cross_repo.rb b/lib/gitlab/git/cross_repo.rb
new file mode 100644
index 00000000000..d44657e7db1
--- /dev/null
+++ b/lib/gitlab/git/cross_repo.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Git
+ class CrossRepo
+ attr_reader :source_repo, :target_repo
+
+ def initialize(source_repo, target_repo)
+ @source_repo = source_repo
+ @target_repo = target_repo
+ end
+
+ def execute(target_ref, &blk)
+ ensuring_ref_in_source(target_ref, &blk)
+ end
+
+ private
+
+ def ensuring_ref_in_source(ref, &blk)
+ return yield ref if source_repo == target_repo
+
+ # If the commit doesn't exist in the target, there's nothing we can do
+ commit_id = target_repo.commit(ref)&.sha
+ return unless commit_id
+
+ # The commit pointed to by ref may exist in the source even when they
+ # are different repositories. This is particularly true of close forks,
+ # but may also be the case if a temporary ref for this comparison has
+ # already been created in the past, and the result hasn't been GC'd yet.
+ return yield commit_id if source_repo.commit(commit_id)
+
+ # Worst case: the commit is not in the source repo so we need to fetch
+ # it. Use a temporary ref and clean up afterwards
+ with_commit_in_source_tmp(commit_id, &blk)
+ end
+
+ # Fetch the ref into source_repo from target_repo, using a temporary ref
+ # name that will be deleted once the method completes. This is a no-op if
+ # fetching the source branch fails
+ def with_commit_in_source_tmp(commit_id, &blk)
+ tmp_ref = "refs/#{::Repository::REF_TMP}/#{SecureRandom.hex}"
+
+ yield commit_id if source_repo.fetch_source_branch!(target_repo, commit_id, tmp_ref)
+ ensure
+ source_repo.delete_refs(tmp_ref) # best-effort
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/cross_repo_comparer.rb b/lib/gitlab/git/cross_repo_comparer.rb
deleted file mode 100644
index d42b2a3bd98..00000000000
--- a/lib/gitlab/git/cross_repo_comparer.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Git
- class CrossRepoComparer
- attr_reader :source_repo, :target_repo
-
- def initialize(source_repo, target_repo)
- @source_repo = source_repo
- @target_repo = target_repo
- end
-
- def compare(source_ref, target_ref, straight:)
- ensuring_ref_in_source(target_ref) do |target_commit_id|
- Gitlab::Git::Compare.new(
- source_repo,
- target_commit_id,
- source_ref,
- straight: straight
- )
- end
- end
-
- private
-
- def ensuring_ref_in_source(ref, &blk)
- return yield ref if source_repo == target_repo
-
- # If the commit doesn't exist in the target, there's nothing we can do
- commit_id = target_repo.commit(ref)&.sha
- return unless commit_id
-
- # The commit pointed to by ref may exist in the source even when they
- # are different repositories. This is particularly true of close forks,
- # but may also be the case if a temporary ref for this comparison has
- # already been created in the past, and the result hasn't been GC'd yet.
- return yield commit_id if source_repo.commit(commit_id)
-
- # Worst case: the commit is not in the source repo so we need to fetch
- # it. Use a temporary ref and clean up afterwards
- with_commit_in_source_tmp(commit_id, &blk)
- end
-
- # Fetch the ref into source_repo from target_repo, using a temporary ref
- # name that will be deleted once the method completes. This is a no-op if
- # fetching the source branch fails
- def with_commit_in_source_tmp(commit_id, &blk)
- tmp_ref = "refs/#{::Repository::REF_TMP}/#{SecureRandom.hex}"
-
- yield commit_id if source_repo.fetch_source_branch!(target_repo, commit_id, tmp_ref)
- ensure
- source_repo.delete_refs(tmp_ref) # best-effort
- end
- end
- end
-end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 3b5151ef4f2..2f9cfe3e764 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -116,9 +116,9 @@ module Gitlab
# Returns an Array of branch names
# sorted by name ASC
def branch_names
- wrapped_gitaly_errors do
- gitaly_ref_client.branch_names
- end
+ refs = list_refs([Gitlab::Git::BRANCH_REF_PREFIX])
+
+ refs.map { |ref| Gitlab::Git.branch_name(ref.name) }
end
# Returns an Array of Branches
@@ -134,6 +134,10 @@ module Gitlab
wrapped_gitaly_errors do
gitaly_ref_client.find_branch(name)
end
+ rescue Gitlab::Git::AmbiguousRef
+ # Gitaly returns "reference is ambiguous" error in case when users request
+ # branch "my-branch", when another branch "my-branch/branch" exists.
+ # We handle this error here and return nil for this case.
end
def find_tag(name)
@@ -158,9 +162,7 @@ module Gitlab
# Returns the number of valid branches
def branch_count
- wrapped_gitaly_errors do
- gitaly_ref_client.count_branch_names
- end
+ branch_names.count
end
def rename(new_relative_path)
@@ -202,16 +204,14 @@ module Gitlab
# Returns the number of valid tags
def tag_count
- wrapped_gitaly_errors do
- gitaly_ref_client.count_tag_names
- end
+ tag_names.count
end
# Returns an Array of tag names
def tag_names
- wrapped_gitaly_errors do
- gitaly_ref_client.tag_names
- end
+ refs = list_refs([Gitlab::Git::TAG_REF_PREFIX])
+
+ refs.map { |ref| Gitlab::Git.tag_name(ref.name) }
end
# Returns an Array of Tags
@@ -385,6 +385,12 @@ module Gitlab
end
end
+ def check_objects_exist(refs)
+ wrapped_gitaly_errors do
+ gitaly_commit_client.object_existence_map(Array.wrap(refs))
+ end
+ end
+
def new_blobs(newrevs, dynamic_timeout: nil)
newrevs = Array.wrap(newrevs).reject { |rev| rev.blank? || rev == ::Gitlab::Git::BLANK_SHA }
return [] if newrevs.empty?
@@ -823,9 +829,14 @@ module Gitlab
end
def compare_source_branch(target_branch_name, source_repository, source_branch_name, straight:)
- CrossRepoComparer
- .new(source_repository, self)
- .compare(source_branch_name, target_branch_name, straight: straight)
+ CrossRepo.new(source_repository, self).execute(target_branch_name) do |target_commit_id|
+ Gitlab::Git::Compare.new(
+ source_repository,
+ target_commit_id,
+ source_branch_name,
+ straight: straight
+ )
+ end
end
def write_ref(ref_path, ref, old_ref: nil)
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index da2a81983ec..344dd27589c 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -157,10 +157,10 @@ module Gitlab
# for deploy tokens and builds
def can_download?
deploy_key_can_download_code? ||
- deploy_token_can_download? ||
- build_can_download? ||
- user_can_download? ||
- guest_can_download?
+ deploy_token_can_download? ||
+ build_can_download? ||
+ user_can_download? ||
+ guest_can_download?
end
def check_container!
@@ -339,7 +339,7 @@ module Gitlab
def check_change_access!
if changes == ANY
can_push = deploy_key? ||
- user_can_push? ||
+ user_can_push? ||
project&.any_branch_allows_collaboration?(user_access.user)
unless can_push
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index 6bcf4802fbe..de66ca7305f 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -571,7 +571,7 @@ module Gitlab
end
def encode_repeated(array)
- Google::Protobuf::RepeatedField.new(:bytes, array.map { |s| encode_binary(s) } )
+ Google::Protobuf::RepeatedField.new(:bytes, array.map { |s| encode_binary(s) })
end
def call_find_commit(revision)
diff --git a/lib/gitlab/gitaly_client/namespace_service.rb b/lib/gitlab/gitaly_client/namespace_service.rb
index dbcebec3aa2..05aee2fa55d 100644
--- a/lib/gitlab/gitaly_client/namespace_service.rb
+++ b/lib/gitlab/gitaly_client/namespace_service.rb
@@ -40,6 +40,13 @@ module Gitlab
gitaly_client_call(:rename_namespace, request, timeout: GitalyClient.fast_timeout)
end
+ def exists?(name)
+ request = Gitaly::NamespaceExistsRequest.new(storage_name: @storage, name: name)
+
+ response = gitaly_client_call(:namespace_exists, request, timeout: GitalyClient.fast_timeout)
+ response.exists
+ end
+
private
def gitaly_client_call(type, request, timeout: nil)
diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb
index 2312def5efc..66f70ed9dc6 100644
--- a/lib/gitlab/gitaly_client/operation_service.rb
+++ b/lib/gitlab/gitaly_client/operation_service.rb
@@ -452,6 +452,14 @@ module Gitlab
when :index_update
raise Gitlab::Git::Index::IndexError, index_error_message(detailed_error.index_update)
else
+ # Some invalid path errors are caught by Gitaly directly and returned
+ # as an :index_update error, while others are found by libgit2 and
+ # come as generic errors. We need to convert the latter as IndexErrors
+ # as well.
+ if e.to_status.details.start_with?('invalid path')
+ raise Gitlab::Git::Index::IndexError, e.to_status.details
+ end
+
raise e
end
end
@@ -600,17 +608,17 @@ module Gitlab
case index_error.error_type
when :ERROR_TYPE_EMPTY_PATH
- "Received empty path"
+ "You must provide a file path"
when :ERROR_TYPE_INVALID_PATH
- "Invalid path: #{encoded_path}"
+ "invalid path: '#{encoded_path}'"
when :ERROR_TYPE_DIRECTORY_EXISTS
- "Directory already exists: #{encoded_path}"
+ "A directory with this name already exists"
when :ERROR_TYPE_DIRECTORY_TRAVERSAL
- "Directory traversal in path escapes repository: #{encoded_path}"
+ "Path cannot include directory traversal"
when :ERROR_TYPE_FILE_EXISTS
- "File already exists: #{encoded_path}"
+ "A file with this name already exists"
when :ERROR_TYPE_FILE_NOT_FOUND
- "File not found: #{encoded_path}"
+ "A file with this name doesn't exist"
else
"Unknown error performing git operation"
end
diff --git a/lib/gitlab/gitaly_client/ref_service.rb b/lib/gitlab/gitaly_client/ref_service.rb
index de76ade76cb..da579276101 100644
--- a/lib/gitlab/gitaly_client/ref_service.rb
+++ b/lib/gitlab/gitaly_client/ref_service.rb
@@ -17,6 +17,8 @@ module Gitlab
'desc' => Gitaly::SortDirection::DESCENDING
}.freeze
+ AMBIGUOUS_REFERENCE = 'reference is ambiguous'
+
# 'repository' is a Gitlab::Git::Repository
def initialize(repository)
@repository = repository
@@ -54,26 +56,6 @@ module Gitlab
Gitlab::Git.branch_name(response.name)
end
- def branch_names
- request = Gitaly::FindAllBranchNamesRequest.new(repository: @gitaly_repo)
- response = gitaly_client_call(@storage, :ref_service, :find_all_branch_names, request, timeout: GitalyClient.fast_timeout)
- consume_refs_response(response) { |name| Gitlab::Git.branch_name(name) }
- end
-
- def tag_names
- request = Gitaly::FindAllTagNamesRequest.new(repository: @gitaly_repo)
- response = gitaly_client_call(@storage, :ref_service, :find_all_tag_names, request, timeout: GitalyClient.fast_timeout)
- consume_refs_response(response) { |name| Gitlab::Git.tag_name(name) }
- end
-
- def count_tag_names
- tag_names.count
- end
-
- def count_branch_names
- branch_names.count
- end
-
def local_branches(sort_by: nil, pagination_params: nil)
request = Gitaly::FindLocalBranchesRequest.new(repository: @gitaly_repo, pagination_params: pagination_params)
request.sort_by = sort_local_branches_by_param(sort_by) if sort_by
@@ -109,6 +91,10 @@ module Gitlab
target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit)
Gitlab::Git::Branch.new(@repository, branch.name.dup, branch.target_commit.id, target_commit)
+ rescue GRPC::BadStatus => e
+ raise e unless e.message.include?(AMBIGUOUS_REFERENCE)
+
+ raise Gitlab::Git::AmbiguousRef, "branch is ambiguous: #{branch_name}"
end
def find_tag(tag_name)
diff --git a/lib/gitlab/gitaly_client/with_feature_flag_actors.rb b/lib/gitlab/gitaly_client/with_feature_flag_actors.rb
index 92fc524b724..3d81292da16 100644
--- a/lib/gitlab/gitaly_client/with_feature_flag_actors.rb
+++ b/lib/gitlab/gitaly_client/with_feature_flag_actors.rb
@@ -16,8 +16,6 @@ module Gitlab
# gitaly_client_call performs Gitaly calls including collected feature flag actors. The actors are retrieved
# from repository actor and memoized. The service must set `self.repository_actor = a_repository` beforehand.
def gitaly_client_call(*args, **kargs)
- return GitalyClient.call(*args, **kargs) unless actors_aware_gitaly_calls?
-
unless repository_actor
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(
Feature::InvalidFeatureFlagError.new("gitaly_client_call called without setting repository_actor")
@@ -34,11 +32,8 @@ module Gitlab
end
end
- # gitaly_feature_flag_actors returns a hash of actors implied from input repository. If actors_aware_gitaly_calls
- # flag is not on, this method returns an empty hash.
+ # gitaly_feature_flag_actors returns a hash of actors implied from input repository.
def gitaly_feature_flag_actors(repository)
- return {} unless actors_aware_gitaly_calls?
-
container = find_repository_container(repository)
{
repository: repository,
@@ -92,10 +87,6 @@ module Gitlab
repository.container
end
end
-
- def actors_aware_gitaly_calls?
- Feature.enabled?(:actors_aware_gitaly_calls)
- end
end
end
end
diff --git a/lib/gitlab/github_gists_import/importer/gist_importer.rb b/lib/gitlab/github_gists_import/importer/gist_importer.rb
new file mode 100644
index 00000000000..a5e87d3cf7d
--- /dev/null
+++ b/lib/gitlab/github_gists_import/importer/gist_importer.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubGistsImport
+ module Importer
+ class GistImporter
+ attr_reader :gist, :user
+
+ FileCountLimitError = Class.new(StandardError)
+
+ # gist - An instance of `Gitlab::GithubGistsImport::Representation::Gist`.
+ def initialize(gist, user_id)
+ @gist = gist
+ @user = User.find(user_id)
+ end
+
+ def execute
+ snippet = build_snippet
+ import_repository(snippet) if snippet.save!
+
+ return ServiceResponse.success unless max_snippet_files_count_exceeded?(snippet)
+
+ fail_and_track(snippet)
+ end
+
+ private
+
+ def build_snippet
+ attrs = {
+ title: gist.truncated_title,
+ visibility_level: gist.visibility_level,
+ content: gist.first_file[:file_content],
+ file_name: gist.first_file[:file_name],
+ author: user,
+ created_at: gist.created_at,
+ updated_at: gist.updated_at
+ }
+
+ PersonalSnippet.new(attrs)
+ end
+
+ def import_repository(snippet)
+ resolved_address = get_resolved_address
+
+ snippet.create_repository
+ snippet.repository.fetch_as_mirror(gist.git_pull_url, forced: true, resolved_address: resolved_address)
+ rescue StandardError
+ remove_snippet_and_repository(snippet)
+
+ raise
+ end
+
+ def get_resolved_address
+ validated_pull_url, host = Gitlab::UrlBlocker.validate!(gist.git_pull_url,
+ schemes: Project::VALID_IMPORT_PROTOCOLS,
+ ports: Project::VALID_IMPORT_PORTS,
+ allow_localhost: allow_local_requests?,
+ allow_local_network: allow_local_requests?)
+
+ host.present? ? validated_pull_url.host.to_s : ''
+ end
+
+ def max_snippet_files_count_exceeded?(snippet)
+ snippet.all_files.size > Snippet.max_file_limit
+ end
+
+ def remove_snippet_and_repository(snippet)
+ snippet.repository.remove if snippet.repository_exists?
+ snippet.destroy
+ end
+
+ def allow_local_requests?
+ Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services?
+ end
+
+ def fail_and_track(snippet)
+ remove_snippet_and_repository(snippet)
+
+ ServiceResponse.error(message: 'Snippet max file count exceeded').track_exception(as: FileCountLimitError)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_gists_import/importer/gists_importer.rb b/lib/gitlab/github_gists_import/importer/gists_importer.rb
new file mode 100644
index 00000000000..08744dbaf5f
--- /dev/null
+++ b/lib/gitlab/github_gists_import/importer/gists_importer.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubGistsImport
+ module Importer
+ class GistsImporter
+ attr_reader :user, :client, :already_imported_cache_key
+
+ ALREADY_IMPORTED_CACHE_KEY = 'github-gists-importer/already-imported/%{user}'
+ RESULT_CONTEXT = Struct.new(:success?, :error, :waiter, :next_attempt_in, keyword_init: true)
+
+ def initialize(user, token)
+ @user = user
+ @client = Gitlab::GithubImport::Client.new(token, parallel: true)
+ @already_imported_cache_key = format(ALREADY_IMPORTED_CACHE_KEY, user: user.id)
+ end
+
+ def execute
+ waiter = spread_parallel_import
+
+ expire_already_imported_cache!
+
+ RESULT_CONTEXT.new(success?: true, waiter: waiter)
+ rescue Gitlab::GithubImport::RateLimitError => e
+ RESULT_CONTEXT.new(success?: false, error: e, next_attempt_in: client.rate_limit_resets_in)
+ rescue StandardError => e
+ RESULT_CONTEXT.new(success?: false, error: e)
+ end
+
+ private
+
+ def spread_parallel_import
+ waiter = JobWaiter.new
+ worker_arguments = fetch_gists_to_import.map { |gist_hash| [user.id, gist_hash, waiter.key] }
+ waiter.jobs_remaining = worker_arguments.size
+
+ schedule_bulk_perform(worker_arguments)
+ waiter
+ end
+
+ def fetch_gists_to_import
+ page_counter = Gitlab::GithubImport::PageCounter.new(user, :gists, 'github-gists-importer')
+ collection = []
+
+ client.each_page(:gists, nil, page: page_counter.current) do |page|
+ next unless page_counter.set(page.number)
+
+ collection += gists_from(page)
+ end
+
+ page_counter.expire!
+
+ collection
+ end
+
+ def gists_from(page)
+ page.objects.each.with_object([]) do |gist, page_collection|
+ gist = gist.to_h
+ next if already_imported?(gist)
+
+ page_collection << ::Gitlab::GithubGistsImport::Representation::Gist.from_api_response(gist).to_hash
+
+ mark_as_imported(gist)
+ end
+ end
+
+ def schedule_bulk_perform(worker_arguments)
+ # rubocop:disable Scalability/BulkPerformWithContext
+ Gitlab::ApplicationContext.with_context(user: user) do
+ Gitlab::GithubGistsImport::ImportGistWorker.bulk_perform_in(
+ 1.second,
+ worker_arguments,
+ batch_size: 1000,
+ batch_delay: 1.minute
+ )
+ end
+ # rubocop:enable Scalability/BulkPerformWithContext
+ end
+
+ def already_imported?(gist)
+ Gitlab::Cache::Import::Caching.set_includes?(already_imported_cache_key, gist[:id])
+ end
+
+ def mark_as_imported(gist)
+ Gitlab::Cache::Import::Caching.set_add(already_imported_cache_key, gist[:id])
+ end
+
+ def expire_already_imported_cache!
+ Gitlab::Cache::Import::Caching
+ .expire(already_imported_cache_key, Gitlab::Cache::Import::Caching::SHORTER_TIMEOUT)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_gists_import/representation/gist.rb b/lib/gitlab/github_gists_import/representation/gist.rb
new file mode 100644
index 00000000000..0d309a98f38
--- /dev/null
+++ b/lib/gitlab/github_gists_import/representation/gist.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubGistsImport
+ module Representation
+ class Gist
+ include Gitlab::GithubImport::Representation::ToHash
+ include Gitlab::GithubImport::Representation::ExposeAttribute
+
+ attr_reader :attributes
+
+ expose_attribute :id, :description, :is_public, :created_at, :updated_at, :files, :git_pull_url
+
+ # Builds a gist from a GitHub API response.
+ #
+ # gist - An instance of `Hash` containing the gist
+ # details.
+ def self.from_api_response(gist, additional_data = {})
+ hash = {
+ id: gist[:id],
+ description: gist[:description],
+ is_public: gist[:public],
+ files: gist[:files],
+ git_pull_url: gist[:git_pull_url],
+ created_at: gist[:created_at],
+ updated_at: gist[:updated_at]
+ }
+
+ new(hash)
+ end
+
+ # Builds a new gist using a Hash that was built from a JSON payload.
+ def self.from_json_hash(raw_hash)
+ new(Gitlab::GithubImport::Representation.symbolize_hash(raw_hash))
+ end
+
+ # attributes - A hash containing the raw gist details. The keys of this
+ # Hash (and any nested hashes) must be symbols.
+ def initialize(attributes)
+ @attributes = attributes
+ end
+
+ # Gist description can be an empty string, so we returning nil to use first file
+ # name as a title in such case on snippet creation
+ # Gist description has a limit of 256, while the snippet's title can be up to 255
+ def truncated_title
+ title = description.presence || first_file[:file_name]
+
+ title.truncate(255)
+ end
+
+ def visibility_level
+ is_public ? Gitlab::VisibilityLevel::PUBLIC : Gitlab::VisibilityLevel::PRIVATE
+ end
+
+ def first_file
+ _key, value = files.first
+
+ {
+ file_name: value[:filename],
+ file_content: Gitlab::HTTP.try_get(value[:raw_url])&.body
+ }
+ end
+
+ def github_identifiers
+ { id: id }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_gists_import/status.rb b/lib/gitlab/github_gists_import/status.rb
new file mode 100644
index 00000000000..e997eb0bf88
--- /dev/null
+++ b/lib/gitlab/github_gists_import/status.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubGistsImport
+ class Status
+ IMPORT_STATUS_KEY = 'gitlab:github-gists-import:%{user_id}'
+ EXPIRATION_TIME = 24.hours
+
+ def initialize(user_id)
+ @user_id = user_id
+ end
+
+ def start!
+ change_status('started')
+ end
+
+ def fail!
+ change_status('failed')
+ end
+
+ def finish!
+ change_status('finished')
+ end
+
+ def started?
+ Gitlab::Redis::SharedState.with { |redis| redis.get(import_status_key) == 'started' }
+ end
+
+ private
+
+ def change_status(status_name)
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set(import_status_key, status_name)
+ redis.expire(import_status_key, EXPIRATION_TIME) unless status_name == 'started'
+ end
+ end
+
+ def import_status_key
+ format(IMPORT_STATUS_KEY, user_id: @user_id)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/bulk_importing.rb b/lib/gitlab/github_import/bulk_importing.rb
index 28a39128ec9..0c91eff1d10 100644
--- a/lib/gitlab/github_import/bulk_importing.rb
+++ b/lib/gitlab/github_import/bulk_importing.rb
@@ -10,25 +10,38 @@ module Gitlab
def initialize(project, client)
@project = project
@client = client
+ @validation_errors = []
end
# Builds and returns an Array of objects to bulk insert into the
- # database.
+ # database and array of validation errors if object is invalid.
#
# enum - An Enumerable that returns the objects to turn into database
# rows.
def build_database_rows(enum)
+ errors = []
rows = enum.each_with_object([]) do |(object, _), result|
- result << build(object) unless already_imported?(object)
+ next if already_imported?(object)
+
+ attrs = build_attributes(object)
+ build_record = model.new(attrs)
+
+ if build_record.invalid?
+ log_error(object[:id], build_record.errors.full_messages)
+ errors << build_record.errors
+ next
+ end
+
+ result << attrs
end
log_and_increment_counter(rows.size, :fetched)
- rows
+ [rows, errors]
end
# Bulk inserts the given rows into the database.
- def bulk_insert(model, rows, batch_size: 100)
+ def bulk_insert(rows, batch_size: 100)
rows.each_slice(batch_size) do |slice|
ApplicationRecord.legacy_bulk_insert(model.table_name, slice) # rubocop:disable Gitlab/BulkInsert
@@ -40,6 +53,23 @@ module Gitlab
raise NotImplementedError
end
+ def bulk_insert_failures(validation_errors)
+ rows = validation_errors.map do |error|
+ correlation_id_value = Labkit::Correlation::CorrelationId.current_or_new_id
+
+ {
+ source: self.class.name,
+ exception_class: 'ActiveRecord::RecordInvalid',
+ exception_message: error.full_messages.first.truncate(255),
+ correlation_id_value: correlation_id_value,
+ retry_count: nil,
+ created_at: Time.zone.now
+ }
+ end
+
+ project.import_failures.insert_all(rows)
+ end
+
private
def log_and_increment_counter(value, operation)
@@ -57,6 +87,16 @@ module Gitlab
value: value
)
end
+
+ def log_error(object_id, messages)
+ Gitlab::Import::Logger.error(
+ import_type: :github,
+ project_id: project.id,
+ importer: self.class.name,
+ message: messages,
+ github_identifier: object_id
+ )
+ end
end
end
end
diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb
index d6060141bce..065410693e5 100644
--- a/lib/gitlab/github_import/client.rb
+++ b/lib/gitlab/github_import/client.rb
@@ -15,6 +15,7 @@ module Gitlab
# end
class Client
include ::Gitlab::Utils::StrongMemoize
+ include ::Gitlab::GithubImport::Clients::SearchRepos
attr_reader :octokit
@@ -182,19 +183,6 @@ module Gitlab
end
end
- def search_repos_by_name(name, options = {})
- with_retry { octokit.search_repositories(search_query(str: name, type: :name), options).to_h }
- end
-
- def search_query(str:, type:, include_collaborations: true, include_orgs: true)
- query = "#{str} in:#{type} is:public,private user:#{octokit.user.to_h[:login]}"
-
- query = [query, collaborations_subquery].join(' ') if include_collaborations
- query = [query, organizations_subquery].join(' ') if include_orgs
-
- query
- end
-
# Returns `true` if we're still allowed to perform API calls.
# Search API has rate limit of 30, use lowered threshold when search is used.
def requests_remaining?
diff --git a/lib/gitlab/github_import/clients/proxy.rb b/lib/gitlab/github_import/clients/proxy.rb
new file mode 100644
index 00000000000..f6d1c8ed23c
--- /dev/null
+++ b/lib/gitlab/github_import/clients/proxy.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Clients
+ class Proxy
+ attr_reader :client
+
+ def initialize(access_token, client_options)
+ @client = pick_client(access_token, client_options)
+ end
+
+ def repos(search_text, pagination_options)
+ return { repos: filtered(client.repos, search_text) } if use_legacy?
+
+ if use_graphql?
+ fetch_repos_via_graphql(search_text, pagination_options)
+ else
+ fetch_repos_via_rest(search_text, pagination_options)
+ end
+ end
+
+ private
+
+ def fetch_repos_via_rest(search_text, pagination_options)
+ { repos: client.search_repos_by_name(search_text, pagination_options)[:items] }
+ end
+
+ def fetch_repos_via_graphql(search_text, pagination_options)
+ response = client.search_repos_by_name_graphql(search_text, pagination_options)
+ {
+ repos: response.dig(:data, :search, :nodes),
+ page_info: response.dig(:data, :search, :pageInfo)
+ }
+ end
+
+ def pick_client(access_token, client_options)
+ return Gitlab::GithubImport::Client.new(access_token) unless use_legacy?
+
+ Gitlab::LegacyGithubImport::Client.new(access_token, **client_options)
+ end
+
+ def filtered(collection, search_text)
+ return collection if search_text.blank?
+
+ collection.select { |item| item[:name].to_s.downcase.include?(search_text) }
+ end
+
+ def use_legacy?
+ Feature.disabled?(:remove_legacy_github_client)
+ end
+
+ def use_graphql?
+ Feature.enabled?(:github_client_fetch_repos_via_graphql)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/clients/search_repos.rb b/lib/gitlab/github_import/clients/search_repos.rb
new file mode 100644
index 00000000000..bcd226087e7
--- /dev/null
+++ b/lib/gitlab/github_import/clients/search_repos.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Clients
+ module SearchRepos
+ def search_repos_by_name_graphql(name, options = {})
+ with_retry do
+ octokit.post(
+ '/graphql',
+ { query: graphql_search_repos_body(name, options) }.to_json
+ ).to_h
+ end
+ end
+
+ def search_repos_by_name(name, options = {})
+ with_retry do
+ octokit.search_repositories(
+ search_repos_query(str: name, type: :name),
+ options
+ ).to_h
+ end
+ end
+
+ private
+
+ def graphql_search_repos_body(name, options)
+ query = search_repos_query(str: name, type: :name)
+ query = "query: \"#{query}\""
+ first = options[:first].present? ? ", first: #{options[:first]}" : ''
+ after = options[:after].present? ? ", after: \"#{options[:after]}\"" : ''
+ <<-TEXT
+ {
+ search(type: REPOSITORY, #{query}#{first}#{after}) {
+ nodes {
+ __typename
+ ... on Repository {
+ id: databaseId
+ name
+ full_name: nameWithOwner
+ owner { login }
+ }
+ }
+ pageInfo {
+ startCursor
+ endCursor
+ hasNextPage
+ hasPreviousPage
+ }
+ }
+ }
+ TEXT
+ end
+
+ def search_repos_query(str:, type:, include_collaborations: true, include_orgs: true)
+ query = "#{str} in:#{type} is:public,private user:#{octokit.user.to_h[:login]}"
+
+ query = [query, collaborations_subquery].join(' ') if include_collaborations
+ query = [query, organizations_subquery].join(' ') if include_orgs
+
+ query
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/importer/diff_note_importer.rb b/lib/gitlab/github_import/importer/diff_note_importer.rb
index a9f8483d8c3..44ffcd7a1e4 100644
--- a/lib/gitlab/github_import/importer/diff_note_importer.rb
+++ b/lib/gitlab/github_import/importer/diff_note_importer.rb
@@ -18,7 +18,6 @@ module Gitlab
def execute
return if merge_request_id.blank?
- note.project = project
note.merge_request = merge_request
build_author_attributes
@@ -65,7 +64,7 @@ module Gitlab
# To work around this we're using bulk_insert with a single row. This
# allows us to efficiently insert data (even if it's just 1 row)
# without having to use all sorts of hacks to disable callbacks.
- ApplicationRecord.legacy_bulk_insert(LegacyDiffNote.table_name, [{
+ attributes = {
noteable_type: note.noteable_type,
system: false,
type: 'LegacyDiffNote',
@@ -79,7 +78,12 @@ module Gitlab
created_at: note.created_at,
updated_at: note.updated_at,
st_diff: note.diff_hash.to_yaml
- }])
+ }
+
+ diff_note = LegacyDiffNote.new(attributes.merge(importing: true))
+ diff_note.validate!
+
+ ApplicationRecord.legacy_bulk_insert(LegacyDiffNote.table_name, [attributes])
end
# rubocop:enabled Gitlab/BulkInsert
diff --git a/lib/gitlab/github_import/importer/issue_importer.rb b/lib/gitlab/github_import/importer/issue_importer.rb
index d964bae3dd2..b477468d327 100644
--- a/lib/gitlab/github_import/importer/issue_importer.rb
+++ b/lib/gitlab/github_import/importer/issue_importer.rb
@@ -60,6 +60,9 @@ module Gitlab
work_item_type_id: issue.work_item_type_id
}
+ issue = project.issues.new(attributes.merge(importing: true))
+ issue.validate!
+
insert_and_return_id(attributes, project.issues)
rescue ActiveRecord::InvalidForeignKey
# It's possible the project has been deleted since scheduling this
diff --git a/lib/gitlab/github_import/importer/label_links_importer.rb b/lib/gitlab/github_import/importer/label_links_importer.rb
index 5e248c7cfc5..52c87dda347 100644
--- a/lib/gitlab/github_import/importer/label_links_importer.rb
+++ b/lib/gitlab/github_import/importer/label_links_importer.rb
@@ -22,7 +22,7 @@ module Gitlab
def create_labels
time = Time.zone.now
- rows = []
+ items = []
target_id = find_target_id
issue.label_names.each do |label_name|
@@ -31,16 +31,16 @@ module Gitlab
# the project's labels.
next unless (label_id = label_finder.id_for(label_name))
- rows << {
+ items << LabelLink.new(
label_id: label_id,
target_id: target_id,
target_type: issue.issuable_type,
created_at: time,
updated_at: time
- }
+ )
end
- ApplicationRecord.legacy_bulk_insert(LabelLink.table_name, rows) # rubocop:disable Gitlab/BulkInsert
+ LabelLink.bulk_insert!(items)
end
def find_target_id
diff --git a/lib/gitlab/github_import/importer/labels_importer.rb b/lib/gitlab/github_import/importer/labels_importer.rb
index 9a011f17a18..d5d1cd28b7c 100644
--- a/lib/gitlab/github_import/importer/labels_importer.rb
+++ b/lib/gitlab/github_import/importer/labels_importer.rb
@@ -13,7 +13,10 @@ module Gitlab
# rubocop: enable CodeReuse/ActiveRecord
def execute
- bulk_insert(Label, build_labels)
+ rows, validation_errors = build_labels
+
+ bulk_insert(rows)
+ bulk_insert_failures(validation_errors) if validation_errors.any?
build_labels_cache
end
@@ -29,7 +32,7 @@ module Gitlab
LabelFinder.new(project).build_cache
end
- def build(label)
+ def build_attributes(label)
time = Time.zone.now
{
@@ -49,6 +52,10 @@ module Gitlab
def object_type
:label
end
+
+ def model
+ Label
+ end
end
end
end
diff --git a/lib/gitlab/github_import/importer/lfs_objects_importer.rb b/lib/gitlab/github_import/importer/lfs_objects_importer.rb
index 775afd5f53a..d064278e4a0 100644
--- a/lib/gitlab/github_import/importer/lfs_objects_importer.rb
+++ b/lib/gitlab/github_import/importer/lfs_objects_importer.rb
@@ -27,9 +27,9 @@ module Gitlab
end
def each_object_to_import
- lfs_objects = Projects::LfsPointers::LfsObjectDownloadListService.new(project).execute
+ download_service = Projects::LfsPointers::LfsObjectDownloadListService.new(project)
- lfs_objects.each do |object|
+ download_service.each_list_item do |object|
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
yield object
diff --git a/lib/gitlab/github_import/importer/milestones_importer.rb b/lib/gitlab/github_import/importer/milestones_importer.rb
index 1a3a54d0053..560fbdc66e3 100644
--- a/lib/gitlab/github_import/importer/milestones_importer.rb
+++ b/lib/gitlab/github_import/importer/milestones_importer.rb
@@ -13,7 +13,10 @@ module Gitlab
# rubocop: enable CodeReuse/ActiveRecord
def execute
- bulk_insert(Milestone, build_milestones)
+ rows, validation_errors = build_milestones
+
+ bulk_insert(rows)
+ bulk_insert_failures(validation_errors) if validation_errors.any?
build_milestones_cache
end
@@ -29,7 +32,7 @@ module Gitlab
MilestoneFinder.new(project).build_cache
end
- def build(milestone)
+ def build_attributes(milestone)
{
iid: milestone[:number],
title: milestone[:title],
@@ -53,6 +56,10 @@ module Gitlab
def object_type
:milestone
end
+
+ def model
+ Milestone
+ end
end
end
end
diff --git a/lib/gitlab/github_import/importer/note_importer.rb b/lib/gitlab/github_import/importer/note_importer.rb
index 69b7b2c2a38..04da015a33f 100644
--- a/lib/gitlab/github_import/importer/note_importer.rb
+++ b/lib/gitlab/github_import/importer/note_importer.rb
@@ -33,6 +33,9 @@ module Gitlab
updated_at: note.updated_at
}
+ note = Note.new(attributes.merge(importing: true))
+ note.validate!
+
# We're using bulk_insert here so we can bypass any validations and
# callbacks. Running these would result in a lot of unnecessary SQL
# queries being executed when importing large projects.
diff --git a/lib/gitlab/github_import/importer/pull_request_importer.rb b/lib/gitlab/github_import/importer/pull_request_importer.rb
index 3c17ea1195e..5690a2cc997 100644
--- a/lib/gitlab/github_import/importer/pull_request_importer.rb
+++ b/lib/gitlab/github_import/importer/pull_request_importer.rb
@@ -61,6 +61,9 @@ module Gitlab
updated_at: pull_request.updated_at
}
+ mr = project.merge_requests.new(attributes.merge(importing: true))
+ mr.validate!
+
create_merge_request_without_hooks(project, attributes, pull_request.iid)
end
@@ -93,7 +96,7 @@ module Gitlab
return if project.repository.branch_exists?(source_branch)
project.repository.add_branch(project.creator, source_branch, pull_request.source_branch_sha)
- rescue Gitlab::Git::CommandError => e
+ rescue Gitlab::Git::PreReceiveError, Gitlab::Git::CommandError => e
Gitlab::ErrorTracking.track_exception(e,
source_branch: source_branch,
project_id: merge_request.project.id,
diff --git a/lib/gitlab/github_import/importer/pull_request_merged_by_importer.rb b/lib/gitlab/github_import/importer/pull_request_merged_by_importer.rb
index 640914acf4d..f05aa26a449 100644
--- a/lib/gitlab/github_import/importer/pull_request_merged_by_importer.rb
+++ b/lib/gitlab/github_import/importer/pull_request_merged_by_importer.rb
@@ -4,42 +4,62 @@ module Gitlab
module GithubImport
module Importer
class PullRequestMergedByImporter
+ # pull_request - An instance of
+ # `Gitlab::GithubImport::Representation::PullRequest`
+ # project - An instance of `Project`
+ # client - An instance of `Gitlab::GithubImport::Client`
def initialize(pull_request, project, client)
- @project = project
@pull_request = pull_request
+ @project = project
@client = client
end
def execute
- merge_request = project.merge_requests.find_by_iid(pull_request.iid)
- timestamp = Time.new.utc
- merged_at = pull_request.merged_at
user_finder = GithubImport::UserFinder.new(project, client)
- gitlab_user_id = user_finder.user_id_for(pull_request.merged_by)
+ gitlab_user_id = begin
+ user_finder.user_id_for(pull_request.merged_by)
+ rescue ::Octokit::NotFound
+ nil
+ end
+
+ metrics_upsert(gitlab_user_id)
+
+ add_note!
+ end
+
+ private
+
+ attr_reader :project, :pull_request, :client
+
+ def metrics_upsert(gitlab_user_id)
MergeRequest::Metrics.upsert({
target_project_id: project.id,
merge_request_id: merge_request.id,
merged_by_id: gitlab_user_id,
- merged_at: merged_at,
+ merged_at: pull_request.merged_at,
created_at: timestamp,
updated_at: timestamp
}, unique_by: :merge_request_id)
+ end
- unless gitlab_user_id
- merge_request.notes.create!(
- importing: true,
- note: missing_author_note,
- author_id: project.creator_id,
- project: project,
- created_at: merged_at
- )
- end
+ def add_note!
+ merge_request.notes.create!(
+ importing: true,
+ note: missing_author_note,
+ author_id: project.creator_id,
+ project: project,
+ created_at: pull_request.merged_at
+ )
end
- private
+ def merge_request
+ @merge_request ||= project.merge_requests.find_by_iid(pull_request.iid)
+ end
- attr_reader :project, :pull_request, :client
+ def timestamp
+ @timestamp ||= Time.new.utc
+ end
def missing_author_note
s_("GitHubImporter|*Merged by: %{author} at %{timestamp}*") % {
diff --git a/lib/gitlab/github_import/importer/pull_request_review_importer.rb b/lib/gitlab/github_import/importer/pull_request_review_importer.rb
index b11af90aa6f..de66f310edf 100644
--- a/lib/gitlab/github_import/importer/pull_request_review_importer.rb
+++ b/lib/gitlab/github_import/importer/pull_request_review_importer.rb
@@ -4,6 +4,9 @@ module Gitlab
module GithubImport
module Importer
class PullRequestReviewImporter
+ # review - An instance of `Gitlab::GithubImport::Representation::PullRequestReview`
+ # project - An instance of `Project`
+ # client - An instance of `Gitlab::GithubImport::Client`
def initialize(review, project, client)
@review = review
@project = project
@@ -13,7 +16,12 @@ module Gitlab
def execute
user_finder = GithubImport::UserFinder.new(project, client)
- gitlab_user_id = user_finder.user_id_for(review.author)
+
+ gitlab_user_id = begin
+ user_finder.user_id_for(review.author)
+ rescue ::Octokit::NotFound
+ nil
+ end
if gitlab_user_id
add_review_note!(gitlab_user_id)
diff --git a/lib/gitlab/github_import/importer/releases_importer.rb b/lib/gitlab/github_import/importer/releases_importer.rb
index fe6da30bbf8..62d579fda08 100644
--- a/lib/gitlab/github_import/importer/releases_importer.rb
+++ b/lib/gitlab/github_import/importer/releases_importer.rb
@@ -16,7 +16,10 @@ module Gitlab
# to generate HTML version - you also need to regenerate it in
# Gitlab::GithubImport::Importer::NoteAttachmentsImporter.
def execute
- bulk_insert(Release, build_releases)
+ rows, validation_errors = build_releases
+
+ bulk_insert(rows)
+ bulk_insert_failures(validation_errors) if validation_errors.any?
end
def build_releases
@@ -27,7 +30,7 @@ module Gitlab
existing_tags.include?(release[:tag_name]) || release[:tag_name].nil?
end
- def build(release)
+ def build_attributes(release)
existing_tags.add(release[:tag_name])
{
@@ -66,6 +69,10 @@ module Gitlab
def user_finder
@user_finder ||= GithubImport::UserFinder.new(project, client)
end
+
+ def model
+ Release
+ end
end
end
end
diff --git a/lib/gitlab/github_import/markdown/attachment.rb b/lib/gitlab/github_import/markdown/attachment.rb
index a5cf5ffa60e..1c814e34a39 100644
--- a/lib/gitlab/github_import/markdown/attachment.rb
+++ b/lib/gitlab/github_import/markdown/attachment.rb
@@ -28,6 +28,7 @@ module Gitlab
def from_markdown_image(markdown_node)
url = markdown_node.url
+ return unless url
return unless github_url?(url, media: true)
return unless whitelisted_type?(url, media: true)
@@ -37,6 +38,7 @@ module Gitlab
def from_markdown_link(markdown_node)
url = markdown_node.url
+ return unless url
return unless github_url?(url, docs: true)
return unless whitelisted_type?(url, docs: true)
@@ -46,7 +48,7 @@ module Gitlab
def from_inline_html(markdown_node)
img = Nokogiri::HTML.parse(markdown_node.string_content).xpath('//img')[0]
- return unless img
+ return if img.nil? || img[:src].blank?
return unless github_url?(img[:src], media: true)
return unless whitelisted_type?(img[:src], media: true)
diff --git a/lib/gitlab/github_import/page_counter.rb b/lib/gitlab/github_import/page_counter.rb
index 3face4c794b..c238ccb8932 100644
--- a/lib/gitlab/github_import/page_counter.rb
+++ b/lib/gitlab/github_import/page_counter.rb
@@ -9,10 +9,10 @@ module Gitlab
attr_reader :cache_key
# The base cache key to use for storing the last page number.
- CACHE_KEY = 'github-importer/page-counter/%{project}/%{collection}'
+ CACHE_KEY = '%{import_type}/page-counter/%{object}/%{collection}'
- def initialize(project, collection)
- @cache_key = CACHE_KEY % { project: project.id, collection: collection }
+ def initialize(object, collection, import_type = 'github-importer')
+ @cache_key = CACHE_KEY % { import_type: import_type, object: object.id, collection: collection }
end
# Sets the page number to the given value.
diff --git a/lib/gitlab/github_import/representation/diff_note.rb b/lib/gitlab/github_import/representation/diff_note.rb
index f3be90834c7..9259d0295d5 100644
--- a/lib/gitlab/github_import/representation/diff_note.rb
+++ b/lib/gitlab/github_import/representation/diff_note.rb
@@ -4,18 +4,15 @@ module Gitlab
module GithubImport
module Representation
class DiffNote
- include Gitlab::Utils::StrongMemoize
include ToHash
include ExposeAttribute
- NOTEABLE_TYPE = 'MergeRequest'
NOTEABLE_ID_REGEX = %r{/pull/(?<iid>\d+)}i.freeze
- DISCUSSION_CACHE_KEY = 'github-importer/discussion-id-map/%{project_id}/%{noteable_id}/%{original_note_id}'
expose_attribute :noteable_id, :commit_id, :file_path,
:diff_hunk, :author, :created_at, :updated_at,
:original_commit_id, :note_id, :end_line, :start_line,
- :side, :in_reply_to_id
+ :side, :in_reply_to_id, :discussion_id
# Builds a diff note from a GitHub API response.
#
@@ -45,7 +42,8 @@ module Gitlab
end_line: note[:line],
start_line: note[:start_line],
side: note[:side],
- in_reply_to_id: note[:in_reply_to_id]
+ in_reply_to_id: note[:in_reply_to_id],
+ discussion_id: DiffNotes::DiscussionId.new(note).find_or_generate
}
new(hash)
@@ -59,7 +57,7 @@ module Gitlab
new(hash)
end
- attr_accessor :merge_request, :project
+ attr_accessor :merge_request
# attributes - A Hash containing the raw note details. The keys of this
# Hash must be Symbols.
@@ -74,7 +72,7 @@ module Gitlab
end
def noteable_type
- NOTEABLE_TYPE
+ DiffNotes::DiscussionId::NOTEABLE_TYPE
end
def contains_suggestion?
@@ -127,12 +125,6 @@ module Gitlab
}
end
- def discussion_id
- strong_memoize(:discussion_id) do
- (in_reply_to_id.present? && current_discussion_id) || generate_discussion_id
- end
- end
-
private
# Required by ExposeAttribute
@@ -149,32 +141,6 @@ module Gitlab
def addition?
side == 'RIGHT'
end
-
- def generate_discussion_id
- Discussion.discussion_id(
- Struct
- .new(:noteable_id, :noteable_type)
- .new(merge_request.id, NOTEABLE_TYPE)
- ).tap do |discussion_id|
- cache_discussion_id(discussion_id)
- end
- end
-
- def cache_discussion_id(discussion_id)
- Gitlab::Cache::Import::Caching.write(discussion_id_cache_key(note_id), discussion_id)
- end
-
- def current_discussion_id
- Gitlab::Cache::Import::Caching.read(discussion_id_cache_key(in_reply_to_id))
- end
-
- def discussion_id_cache_key(id)
- DISCUSSION_CACHE_KEY % {
- project_id: project.id,
- noteable_id: merge_request.id,
- original_note_id: id
- }
- end
end
end
end
diff --git a/lib/gitlab/github_import/representation/diff_notes/discussion_id.rb b/lib/gitlab/github_import/representation/diff_notes/discussion_id.rb
new file mode 100644
index 00000000000..38b560f21c0
--- /dev/null
+++ b/lib/gitlab/github_import/representation/diff_notes/discussion_id.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Representation
+ module DiffNotes
+ class DiscussionId
+ NOTEABLE_TYPE = 'MergeRequest'
+ DISCUSSION_CACHE_REGEX = %r{/(?<repo>[^/]*)/pull/(?<iid>\d+)}i.freeze
+ DISCUSSION_CACHE_KEY = 'github-importer/discussion-id-map/%{project}/%{noteable_id}/%{original_note_id}'
+
+ def initialize(note)
+ @note = note
+ @matches = note[:html_url].match(DISCUSSION_CACHE_REGEX)
+ end
+
+ def find_or_generate
+ (note[:in_reply_to_id].present? && current_discussion_id) || generate_discussion_id
+ end
+
+ private
+
+ attr_reader :note, :matches
+
+ def generate_discussion_id
+ discussion_id = Discussion.discussion_id(
+ Struct
+ .new(:noteable_id, :noteable_type)
+ .new(matches[:iid].to_i, NOTEABLE_TYPE)
+ )
+ cache_discussion_id(discussion_id)
+ end
+
+ def cache_discussion_id(discussion_id)
+ Gitlab::Cache::Import::Caching.write(
+ discussion_id_cache_key(note[:id]), discussion_id
+ )
+ end
+
+ def current_discussion_id
+ Gitlab::Cache::Import::Caching.read(
+ discussion_id_cache_key(note[:in_reply_to_id])
+ )
+ end
+
+ def discussion_id_cache_key(id)
+ format(DISCUSSION_CACHE_KEY,
+ project: matches[:repo],
+ noteable_id: matches[:iid].to_i,
+ original_note_id: id
+ )
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/gl_repository/repo_type.rb b/lib/gitlab/gl_repository/repo_type.rb
index 05278b2dd35..7792ef55b28 100644
--- a/lib/gitlab/gl_repository/repo_type.rb
+++ b/lib/gitlab/gl_repository/repo_type.rb
@@ -66,10 +66,10 @@ module Gitlab
def valid?(repository_path)
repository_path.end_with?(path_suffix) &&
- (
- !snippet? ||
- repository_path.match?(Gitlab::PathRegex.full_snippets_repository_path_regex)
- )
+ (
+ !snippet? ||
+ repository_path.match?(Gitlab::PathRegex.full_snippets_repository_path_regex)
+ )
end
private
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index ecb57bfc1a2..12cdcf445f7 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -65,8 +65,10 @@ module Gitlab
push_frontend_feature_flag(:security_auto_fix)
push_frontend_feature_flag(:new_header_search)
push_frontend_feature_flag(:source_editor_toolbar)
+ push_frontend_feature_flag(:vscode_web_ide, current_user)
push_frontend_feature_flag(:integration_slack_app_notifications)
push_frontend_feature_flag(:vue_group_select)
+ push_frontend_feature_flag(:new_fonts, current_user)
end
# Exposes the state of a feature flag to the frontend code.
diff --git a/lib/gitlab/graphql/expose_permissions.rb b/lib/gitlab/graphql/expose_permissions.rb
index ab9ed354673..070d3c188a4 100644
--- a/lib/gitlab/graphql/expose_permissions.rb
+++ b/lib/gitlab/graphql/expose_permissions.rb
@@ -5,11 +5,15 @@ module Gitlab
module ExposePermissions
extend ActiveSupport::Concern
prepended do
- def self.expose_permissions(permission_type, description: 'Permissions for the current user on the resource')
+ def self.expose_permissions(
+ permission_type,
+ description: 'Permissions for the current user on the resource',
+ &block)
field :user_permissions, permission_type,
description: description,
null: false,
- method: :itself
+ method: :itself,
+ &block
end
end
end
diff --git a/lib/gitlab/graphql/extensions/forward_only_externally_paginated_array_extension.rb b/lib/gitlab/graphql/extensions/forward_only_externally_paginated_array_extension.rb
new file mode 100644
index 00000000000..651b4266756
--- /dev/null
+++ b/lib/gitlab/graphql/extensions/forward_only_externally_paginated_array_extension.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+module Gitlab
+ module Graphql
+ module Extensions
+ # This extension is meant for resolvers that only support forward looking pagination. So in order to limit
+ # confusion for allowed GraphQL pagination arguments on the field, we limit this to just `first` and `after`.
+ class ForwardOnlyExternallyPaginatedArrayExtension < ExternallyPaginatedArrayExtension
+ def apply
+ field.argument :after, GraphQL::Types::String,
+ description: "Returns the elements in the list that come after the specified cursor.",
+ required: false
+ field.argument :first, GraphQL::Types::Int,
+ description: "Returns the first _n_ elements from the list.",
+ required: false
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/limit/field_call_count.rb b/lib/gitlab/graphql/limit/field_call_count.rb
index 4165970a2a6..3a02e8abbb5 100644
--- a/lib/gitlab/graphql/limit/field_call_count.rb
+++ b/lib/gitlab/graphql/limit/field_call_count.rb
@@ -14,9 +14,18 @@ module Gitlab
private
def increment_call_count(context)
+ query_id = fetch_query_id(context)
+
context[:call_count] ||= {}
- context[:call_count][field] ||= 0
- context[:call_count][field] += 1
+ context[:call_count][query_id] ||= {}
+ context[:call_count][query_id][field] ||= 0
+ context[:call_count][query_id][field] += 1
+ end
+
+ def fetch_query_id(context)
+ context.query.operation_fingerprint
+ rescue TypeError
+ ''
end
def limit
diff --git a/lib/gitlab/graphql/pagination/keyset/connection.rb b/lib/gitlab/graphql/pagination/keyset/connection.rb
index eca4d42fb9a..208ca5f2d24 100644
--- a/lib/gitlab/graphql/pagination/keyset/connection.rb
+++ b/lib/gitlab/graphql/pagination/keyset/connection.rb
@@ -59,16 +59,7 @@ module Gitlab
if before
true
elsif first
- if Feature.enabled?(:graphql_keyset_pagination_without_next_page_query)
- limited_nodes.size > limit_value
- else
- case sliced_nodes
- when Array
- sliced_nodes.size > limit_value
- else
- sliced_nodes.limit(1).offset(limit_value).exists? # rubocop: disable CodeReuse/ActiveRecord
- end
- end
+ limited_nodes.size > limit_value
else
false
end
@@ -126,15 +117,9 @@ module Gitlab
@has_previous_page = paginated_nodes.count > limit_value
@has_previous_page ? paginated_nodes.last(limit_value) : paginated_nodes
elsif loaded?(sliced_nodes)
- if Feature.enabled?(:graphql_keyset_pagination_without_next_page_query)
- sliced_nodes.take(limit_value + 1) # rubocop: disable CodeReuse/ActiveRecord
- else
- sliced_nodes.take(limit_value) # rubocop: disable CodeReuse/ActiveRecord
- end
- elsif Feature.enabled?(:graphql_keyset_pagination_without_next_page_query)
- sliced_nodes.limit(limit_value + 1).to_a
+ sliced_nodes.take(limit_value + 1) # rubocop: disable CodeReuse/ActiveRecord
else
- sliced_nodes.limit(limit_value)
+ sliced_nodes.limit(limit_value + 1).to_a
end
end
end
diff --git a/lib/gitlab/group_search_results.rb b/lib/gitlab/group_search_results.rb
index 4eea96f8344..b112740c4ad 100644
--- a/lib/gitlab/group_search_results.rb
+++ b/lib/gitlab/group_search_results.rb
@@ -12,23 +12,11 @@ module Gitlab
# rubocop:disable CodeReuse/ActiveRecord
def users
- # get all groups the current user has access to
- # ignore order inherited from GroupsFinder to improve performance
- current_user_groups = GroupsFinder.new(current_user).execute.unscope(:order)
+ groups = group.self_and_hierarchy_intersecting_with_user_groups(current_user)
+ members = GroupMember.where(group: groups).non_invite
- # the hierarchy of the current group
- group_groups = @group.self_and_hierarchy.unscope(:order)
-
- # the groups where the above hierarchies intersect
- intersect_groups = group_groups.where(id: current_user_groups)
-
- # members of @group hierarchy where the user has access to the groups
- members = GroupMember.where(group: intersect_groups).non_invite
-
- # get all users the current user has access to (-> `SearchResults#users`), which also applies the query
users = super
- # filter users that belong to the previously selected groups
users.where(id: members.select(:user_id))
end
# rubocop:enable CodeReuse/ActiveRecord
diff --git a/lib/gitlab/http.rb b/lib/gitlab/http.rb
index 567c4dc899f..b05767c7ed4 100644
--- a/lib/gitlab/http.rb
+++ b/lib/gitlab/http.rb
@@ -59,7 +59,7 @@ module Gitlab
raise ReadTotalTimeout, "Request timed out after #{elapsed} seconds"
end
- block.call fragment if block
+ yield fragment if block
end
rescue HTTParty::RedirectionTooDeep
raise RedirectionTooDeep
diff --git a/lib/gitlab/http_connection_adapter.rb b/lib/gitlab/http_connection_adapter.rb
index 7b1657d3854..3ef60be67a9 100644
--- a/lib/gitlab/http_connection_adapter.rb
+++ b/lib/gitlab/http_connection_adapter.rb
@@ -44,7 +44,8 @@ module Gitlab
Gitlab::UrlBlocker.validate!(url, allow_local_network: allow_local_requests?,
allow_localhost: allow_local_requests?,
allow_object_storage: allow_object_storage?,
- dns_rebind_protection: dns_rebind_protection?)
+ dns_rebind_protection: dns_rebind_protection?,
+ schemes: %w[http https])
rescue Gitlab::UrlBlocker::BlockedUrlError => e
raise Gitlab::HTTP::BlockedUrlError, "URL '#{url}' is blocked: #{e.message}"
end
diff --git a/lib/gitlab/i18n.rb b/lib/gitlab/i18n.rb
index a42cac61a55..7a42ffca779 100644
--- a/lib/gitlab/i18n.rb
+++ b/lib/gitlab/i18n.rb
@@ -50,30 +50,30 @@ module Gitlab
'eo' => 0,
'es' => 35,
'fil_PH' => 0,
- 'fr' => 85,
+ 'fr' => 94,
'gl_ES' => 0,
'id_ID' => 0,
'it' => 1,
'ja' => 30,
- 'ko' => 21,
- 'nb_NO' => 25,
+ 'ko' => 20,
+ 'nb_NO' => 24,
'nl_NL' => 0,
'pl_PL' => 3,
- 'pt_BR' => 58,
- 'ro_RO' => 98,
- 'ru' => 25,
+ 'pt_BR' => 57,
+ 'ro_RO' => 96,
+ 'ru' => 26,
'si_LK' => 11,
'tr_TR' => 11,
'uk' => 52,
- 'zh_CN' => 98,
+ 'zh_CN' => 97,
'zh_HK' => 1,
- 'zh_TW' => 100
+ 'zh_TW' => 99
}.freeze
private_constant :TRANSLATION_LEVELS
- def selectable_locales
+ def selectable_locales(minimum_translation_level = MINIMUM_TRANSLATION_LEVEL)
AVAILABLE_LANGUAGES.reject do |code, _name|
- percentage_translated_for(code) < MINIMUM_TRANSLATION_LEVEL
+ percentage_translated_for(code) < minimum_translation_level
end
end
diff --git a/lib/gitlab/import_export/base/relation_factory.rb b/lib/gitlab/import_export/base/relation_factory.rb
index b05d9cb2489..d1fd45882d3 100644
--- a/lib/gitlab/import_export/base/relation_factory.rb
+++ b/lib/gitlab/import_export/base/relation_factory.rb
@@ -211,23 +211,21 @@ module Gitlab
def existing_or_new_object
# Only find existing records to avoid mapping tables such as milestones
# Otherwise always create the record, skipping the extra SELECT clause.
- @existing_or_new_object ||= begin
- if existing_object?
- attribute_hash = attribute_hash_for(['events'])
-
- existing_object.assign_attributes(attribute_hash) if attribute_hash.any?
-
- existing_object
- else
- # Because of single-type inheritance, we need to be careful to use the `type` field
- # See https://gitlab.com/gitlab-org/gitlab/issues/34860#note_235321497
- inheritance_column = relation_class.try(:inheritance_column)
- inheritance_attributes = parsed_relation_hash.slice(inheritance_column)
- object = relation_class.new(inheritance_attributes)
- object.assign_attributes(parsed_relation_hash)
- object
- end
- end
+ @existing_or_new_object ||= if existing_object?
+ attribute_hash = attribute_hash_for(['events'])
+
+ existing_object.assign_attributes(attribute_hash) if attribute_hash.any?
+
+ existing_object
+ else
+ # Because of single-type inheritance, we need to be careful to use the `type` field
+ # See https://gitlab.com/gitlab-org/gitlab/issues/34860#note_235321497
+ inheritance_column = relation_class.try(:inheritance_column)
+ inheritance_attributes = parsed_relation_hash.slice(inheritance_column)
+ object = relation_class.new(inheritance_attributes)
+ object.assign_attributes(parsed_relation_hash)
+ object
+ end
end
def attribute_hash_for(attributes)
diff --git a/lib/gitlab/import_export/decompressed_archive_size_validator.rb b/lib/gitlab/import_export/decompressed_archive_size_validator.rb
index aa66fe8a5ae..564008e7a73 100644
--- a/lib/gitlab/import_export/decompressed_archive_size_validator.rb
+++ b/lib/gitlab/import_export/decompressed_archive_size_validator.rb
@@ -35,7 +35,7 @@ module Gitlab
Timeout.timeout(TIMEOUT_LIMIT) do
stderr_r, stderr_w = IO.pipe
- stdout, wait_threads = Open3.pipeline_r(*command, pgroup: true, err: stderr_w )
+ stdout, wait_threads = Open3.pipeline_r(*command, pgroup: true, err: stderr_w)
# When validation is performed on a small archive (e.g. 100 bytes)
# `wait_thr` finishes before we can get process group id. Do not
diff --git a/lib/gitlab/import_export/group/import_export.yml b/lib/gitlab/import_export/group/import_export.yml
index a08efdf400b..7f3254be3e8 100644
--- a/lib/gitlab/import_export/group/import_export.yml
+++ b/lib/gitlab/import_export/group/import_export.yml
@@ -132,3 +132,23 @@ ee:
- :push_event_payload
- iterations_cadences:
- :iterations
+
+# When associated resources are from outside the group, you might need to
+# validate that a user who is exporting the group can access these
+# associations. `include_if_exportable` accepts an array of associations for a
+# resource. During export, the `exportable_association?` method on the
+# resource is called with the association's name and user to validate if
+# associated resource can be included in the export.
+#
+# This definition will call epic's `exportable_association?(:parent,
+# current_user: current_user)` method and include epic's parent association
+# for each epic only if the method returns true:
+#
+# include_if_exportable:
+# group:
+# epics:
+# - :parent
+include_if_exportable:
+ group:
+ epics:
+ - :parent
diff --git a/lib/gitlab/import_export/json/legacy_reader.rb b/lib/gitlab/import_export/json/legacy_reader.rb
index dc80c92f507..ee360020556 100644
--- a/lib/gitlab/import_export/json/legacy_reader.rb
+++ b/lib/gitlab/import_export/json/legacy_reader.rb
@@ -27,7 +27,7 @@ module Gitlab
end
def read_hash
- Gitlab::Json.parse(IO.read(@path))
+ Gitlab::Json.parse(::File.read(@path))
rescue StandardError => e
Gitlab::ErrorTracking.log_exception(e)
raise Gitlab::ImportExport::Error, 'Incorrect JSON format'
diff --git a/lib/gitlab/import_export/lfs_restorer.rb b/lib/gitlab/import_export/lfs_restorer.rb
index 9931b09e9ca..83aab6d031e 100644
--- a/lib/gitlab/import_export/lfs_restorer.rb
+++ b/lib/gitlab/import_export/lfs_restorer.rb
@@ -71,7 +71,7 @@ module Gitlab
@lfs_json ||=
begin
- json = IO.read(lfs_json_path)
+ json = File.read(lfs_json_path)
Gitlab::Json.parse(json)
rescue StandardError
raise Gitlab::ImportExport::Error, 'Incorrect JSON format'
diff --git a/lib/gitlab/import_export/members_mapper.rb b/lib/gitlab/import_export/members_mapper.rb
index c94549a2b3f..0ad19f82e71 100644
--- a/lib/gitlab/import_export/members_mapper.rb
+++ b/lib/gitlab/import_export/members_mapper.rb
@@ -15,15 +15,13 @@ module Gitlab
def map
@map ||=
- begin
- @exported_members.each_with_object(missing_keys_tracking_hash) do |member, hash|
- if member['user']
- old_user_id = member['user']['id']
- existing_user_id = existing_users_email_map[get_email(member)]
- hash[old_user_id] = existing_user_id if existing_user_id && add_team_member(member, existing_user_id)
- else
- add_team_member(member)
- end
+ @exported_members.each_with_object(missing_keys_tracking_hash) do |member, hash|
+ if member['user']
+ old_user_id = member['user']['id']
+ existing_user_id = existing_users_email_map[get_email(member)]
+ hash[old_user_id] = existing_user_id if existing_user_id && add_team_member(member, existing_user_id)
+ else
+ add_team_member(member)
end
end
end
diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml
index 2d9c8d1108e..cc69ed55744 100644
--- a/lib/gitlab/import_export/project/import_export.yml
+++ b/lib/gitlab/import_export/project/import_export.yml
@@ -763,6 +763,19 @@ excluded_attributes:
- :import_type
- :import_source
- :integrations
+ - :push_hooks_integrations
+ - :tag_push_hooks_integrations
+ - :issue_hooks_integrations
+ - :confidential_issue_hooks_integrations
+ - :merge_request_hooks_integrations
+ - :note_hooks_integrations
+ - :confidential_note_hooks_integrations
+ - :job_hooks_integrations
+ - :archive_trace_hooks_integrations
+ - :pipeline_hooks_integrations
+ - :wiki_page_hooks_integrations
+ - :deployment_hooks_integrations
+ - :alert_hooks_integrations
- :mirror
- :runners_token
- :runners_token_encrypted
@@ -1209,7 +1222,9 @@ ee:
- :description
iterations_cadence:
- :title
-
+ excluded_attributes:
+ project:
+ - :vulnerability_hooks_integrations
preloads:
issues:
epic:
diff --git a/lib/gitlab/import_export/project/tree_saver.rb b/lib/gitlab/import_export/project/tree_saver.rb
index bd34cd3ff6e..05b96f7e8ce 100644
--- a/lib/gitlab/import_export/project/tree_saver.rb
+++ b/lib/gitlab/import_export/project/tree_saver.rb
@@ -81,15 +81,13 @@ module Gitlab
end
def json_writer
- @json_writer ||= begin
- if ::Feature.enabled?(:project_export_as_ndjson, @project.namespace)
- full_path = File.join(@shared.export_path, 'tree')
- Gitlab::ImportExport::Json::NdjsonWriter.new(full_path)
- else
- full_path = File.join(@shared.export_path, ImportExport.project_filename)
- Gitlab::ImportExport::Json::LegacyWriter.new(full_path, allowed_path: 'project')
- end
- end
+ @json_writer ||= if ::Feature.enabled?(:project_export_as_ndjson, @project.namespace)
+ full_path = File.join(@shared.export_path, 'tree')
+ Gitlab::ImportExport::Json::NdjsonWriter.new(full_path)
+ else
+ full_path = File.join(@shared.export_path, ImportExport.project_filename)
+ Gitlab::ImportExport::Json::LegacyWriter.new(full_path, allowed_path: 'project')
+ end
end
end
end
diff --git a/lib/gitlab/import_export/remote_stream_upload.rb b/lib/gitlab/import_export/remote_stream_upload.rb
index f3bd241c0bd..1fb3faf0767 100644
--- a/lib/gitlab/import_export/remote_stream_upload.rb
+++ b/lib/gitlab/import_export/remote_stream_upload.rb
@@ -25,6 +25,7 @@ module Gitlab
end
end
end
+
class StreamError < StandardError
attr_reader :response_body
@@ -33,6 +34,7 @@ module Gitlab
@response_body = response_body
end
end
+
class ChunkStream
DEFAULT_BUFFER_SIZE = 128.kilobytes
diff --git a/lib/gitlab/import_export/repo_restorer.rb b/lib/gitlab/import_export/repo_restorer.rb
index 1c6629cf942..cc214d730fe 100644
--- a/lib/gitlab/import_export/repo_restorer.rb
+++ b/lib/gitlab/import_export/repo_restorer.rb
@@ -46,6 +46,11 @@ module Gitlab
)
Repositories::DestroyService.new(repository).execute
+
+ # Because Gitlab::Git::Repository#remove happens inside a run_after_commit
+ # callback in the Repositories::DestroyService#execute we need to trigger
+ # the callback.
+ repository.project.touch
end
end
end
diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb
index 5d3a6b0c6e1..a94ea6f595b 100644
--- a/lib/gitlab/import_sources.rb
+++ b/lib/gitlab/import_sources.rb
@@ -15,7 +15,6 @@ module Gitlab
ImportSource.new('bitbucket', 'Bitbucket Cloud', Gitlab::BitbucketImport::Importer),
ImportSource.new('bitbucket_server', 'Bitbucket Server', Gitlab::BitbucketServerImport::Importer),
ImportSource.new('gitlab', 'GitLab.com', Gitlab::GitlabImport::Importer),
- ImportSource.new('google_code', 'Google Code', nil),
ImportSource.new('fogbugz', 'FogBugz', Gitlab::FogbugzImport::Importer),
ImportSource.new('git', 'Repository by URL', nil),
ImportSource.new('gitlab_project', 'GitLab export', Gitlab::ImportExport::Importer),
diff --git a/lib/gitlab/incident_management/pager_duty/incident_issue_description.rb b/lib/gitlab/incident_management/pager_duty/incident_issue_description.rb
index 6aeeb1d31aa..cbc4f126293 100644
--- a/lib/gitlab/incident_management/pager_duty/incident_issue_description.rb
+++ b/lib/gitlab/incident_management/pager_duty/incident_issue_description.rb
@@ -19,7 +19,7 @@ module Gitlab
"**Incident key:** #{incident_payload['incident_key']}",
"**Created at:** #{markdown_incident_created_at}",
"**Assignees:** #{markdown_assignees.join(', ')}",
- "**Impacted services:** #{markdown_impacted_services.join(', ')}"
+ "**Impacted service:** #{markdown_impacted_service}"
].join(markdown_line_break)
end
@@ -47,10 +47,9 @@ module Gitlab
end
end
- def markdown_impacted_services
- Array(incident_payload['impacted_services']).map do |is|
- markdown_link(is['summary'], is['url'])
- end
+ def markdown_impacted_service
+ service = incident_payload['impacted_service']
+ markdown_link(service['summary'], service['url']) unless service.nil?
end
def markdown_link(label, url)
diff --git a/lib/gitlab/instrumentation/redis.rb b/lib/gitlab/instrumentation/redis.rb
index a371930621d..a664656c467 100644
--- a/lib/gitlab/instrumentation/redis.rb
+++ b/lib/gitlab/instrumentation/redis.rb
@@ -39,7 +39,8 @@ module Gitlab
end
end
- %i[get_request_count query_time read_bytes write_bytes].each do |method|
+ %i[get_request_count get_cross_slot_request_count get_allowed_cross_slot_request_count query_time read_bytes
+ write_bytes].each do |method|
define_method method do
STORAGES.sum(&method)
end
diff --git a/lib/gitlab/instrumentation/redis_base.rb b/lib/gitlab/instrumentation/redis_base.rb
index 268c6cdf459..de24132a28e 100644
--- a/lib/gitlab/instrumentation/redis_base.rb
+++ b/lib/gitlab/instrumentation/redis_base.rb
@@ -45,6 +45,16 @@ module Gitlab
::RequestStore[write_bytes_key] += num_bytes
end
+ def increment_cross_slot_request_count(amount = 1)
+ ::RequestStore[cross_slots_key] ||= 0
+ ::RequestStore[cross_slots_key] += amount
+ end
+
+ def increment_allowed_cross_slot_request_count(amount = 1)
+ ::RequestStore[allowed_cross_slots_key] ||= 0
+ ::RequestStore[allowed_cross_slots_key] += amount
+ end
+
def get_request_count
::RequestStore[request_count_key] || 0
end
@@ -61,13 +71,32 @@ module Gitlab
::RequestStore[call_details_key] ||= []
end
+ def get_cross_slot_request_count
+ ::RequestStore[cross_slots_key] || 0
+ end
+
+ def get_allowed_cross_slot_request_count
+ ::RequestStore[allowed_cross_slots_key] || 0
+ end
+
def query_time
query_time = ::RequestStore[call_duration_key] || 0
query_time.round(::Gitlab::InstrumentationHelper::DURATION_PRECISION)
end
def redis_cluster_validate!(commands)
- ::Gitlab::Instrumentation::RedisClusterValidator.validate!(commands) if @redis_cluster_validation
+ return true unless @redis_cluster_validation
+
+ result = ::Gitlab::Instrumentation::RedisClusterValidator.validate(commands)
+ return true if result.nil?
+
+ if !result[:valid] && !result[:allowed] && (Rails.env.development? || Rails.env.test?)
+ raise RedisClusterValidator::CrossSlotError, "Redis command #{result[:command_name]} arguments hash to different slots. See https://docs.gitlab.com/ee/development/redis.html#multi-key-commands"
+ end
+
+ increment_allowed_cross_slot_request_count if result[:allowed]
+
+ result[:valid]
end
def enable_redis_cluster_validation
@@ -122,6 +151,14 @@ module Gitlab
strong_memoize(:call_details_key) { build_key(:redis_call_details) }
end
+ def cross_slots_key
+ strong_memoize(:cross_slots_key) { build_key(:redis_cross_slot_request_count) }
+ end
+
+ def allowed_cross_slots_key
+ strong_memoize(:allowed_cross_slots_key) { build_key(:redis_allowed_cross_slot_request_count) }
+ end
+
def build_key(namespace)
"#{storage_key}_#{namespace}"
end
diff --git a/lib/gitlab/instrumentation/redis_cluster_validator.rb b/lib/gitlab/instrumentation/redis_cluster_validator.rb
index 36d3e088956..1567e54d8da 100644
--- a/lib/gitlab/instrumentation/redis_cluster_validator.rb
+++ b/lib/gitlab/instrumentation/redis_cluster_validator.rb
@@ -183,19 +183,22 @@ module Gitlab
CrossSlotError = Class.new(StandardError)
class << self
- def validate!(commands)
- return unless Rails.env.development? || Rails.env.test?
- return if allow_cross_slot_commands?
+ def validate(commands)
return if commands.empty?
# early exit for single-command (non-pipelined) if it is a single-key-command
command_name = commands.size > 1 ? "PIPELINE/MULTI" : commands.first.first.to_s.upcase
return if commands.size == 1 && REDIS_COMMANDS.dig(command_name, :single_key)
- key_slots = commands.map { |command| key_slots(command) }.flatten
- if key_slots.uniq.many? # rubocop: disable CodeReuse/ActiveRecord
- raise CrossSlotError, "Redis command #{command_name} arguments hash to different slots. See https://docs.gitlab.com/ee/development/redis.html#multi-key-commands"
- end
+ keys = commands.map { |command| extract_keys(command) }.flatten
+
+ {
+ # calculate key-slots only if not allowed
+ valid: allow_cross_slot_commands? || !has_cross_slot_keys?(keys),
+ command_name: command_name,
+ key_count: keys.size,
+ allowed: allow_cross_slot_commands?
+ }
end
# Keep track of the call stack to allow nested calls to work.
@@ -210,15 +213,17 @@ module Gitlab
private
- def key_slots(command)
+ def extract_keys(command)
argument_positions = REDIS_COMMANDS[command.first.to_s.upcase]
return [] unless argument_positions
arguments = command.flatten[argument_positions[:first]..argument_positions[:last]]
- arguments.each_slice(argument_positions[:step]).map do |args|
- key_slot(args.first)
- end
+ arguments.each_slice(argument_positions[:step]).map(&:first)
+ end
+
+ def has_cross_slot_keys?(keys)
+ keys.map { |key| key_slot(key) }.uniq.many? # rubocop: disable CodeReuse/ActiveRecord
end
def allow_cross_slot_commands?
diff --git a/lib/gitlab/instrumentation/redis_interceptor.rb b/lib/gitlab/instrumentation/redis_interceptor.rb
index f19279df2fe..35dd7cbfeb8 100644
--- a/lib/gitlab/instrumentation/redis_interceptor.rb
+++ b/lib/gitlab/instrumentation/redis_interceptor.rb
@@ -33,7 +33,10 @@ module Gitlab
def instrument_call(commands)
start = Gitlab::Metrics::System.monotonic_time # must come first so that 'start' is always defined
instrumentation_class.instance_count_request(commands.size)
- instrumentation_class.redis_cluster_validate!(commands)
+
+ if !instrumentation_class.redis_cluster_validate!(commands) && ::RequestStore.active?
+ instrumentation_class.increment_cross_slot_request_count
+ end
yield
rescue ::Redis::BaseError => ex
@@ -62,13 +65,11 @@ module Gitlab
# This count is an approximation that omits the Redis protocol overhead
# of type prefixes, length prefixes and line endings.
command.each do |x|
- size += begin
- if x.is_a? Array
- x.inject(0) { |sum, y| sum + y.to_s.bytesize }
- else
- x.to_s.bytesize
- end
- end
+ size += if x.is_a? Array
+ x.inject(0) { |sum, y| sum + y.to_s.bytesize }
+ else
+ x.to_s.bytesize
+ end
end
instrumentation_class.increment_write_bytes(size)
diff --git a/lib/gitlab/instrumentation/redis_payload.rb b/lib/gitlab/instrumentation/redis_payload.rb
index 86a6525c8d0..62a4d1a846f 100644
--- a/lib/gitlab/instrumentation/redis_payload.rb
+++ b/lib/gitlab/instrumentation/redis_payload.rb
@@ -20,6 +20,8 @@ module Gitlab
{
"#{key_prefix}_calls": -> { get_request_count },
+ "#{key_prefix}_cross_slot_calls": -> { get_cross_slot_request_count },
+ "#{key_prefix}_allowed_cross_slot_calls": -> { get_allowed_cross_slot_request_count },
"#{key_prefix}_duration_s": -> { query_time },
"#{key_prefix}_read_bytes": -> { read_bytes },
"#{key_prefix}_write_bytes": -> { write_bytes }
diff --git a/lib/gitlab/instrumentation_helper.rb b/lib/gitlab/instrumentation_helper.rb
index b8a2567b775..15a760fada0 100644
--- a/lib/gitlab/instrumentation_helper.rb
+++ b/lib/gitlab/instrumentation_helper.rb
@@ -35,6 +35,7 @@ module Gitlab
instrument_uploads(payload)
instrument_rate_limiting_gates(payload)
instrument_global_search_api(payload)
+ instrument_ldap(payload)
end
def instrument_gitaly(payload)
@@ -136,6 +137,14 @@ module Gitlab
payload.merge!(::Gitlab::Instrumentation::GlobalSearchApi.payload)
end
+ def instrument_ldap(payload)
+ ldap_count = Gitlab::Metrics::Subscribers::Ldap.count
+
+ return if ldap_count == 0
+
+ payload.merge! Gitlab::Metrics::Subscribers::Ldap.payload
+ end
+
# Returns the queuing duration for a Sidekiq job in seconds, as a float, if the
# `enqueued_at` field or `created_at` field is available.
#
diff --git a/lib/gitlab/issuable_metadata.rb b/lib/gitlab/issuable_metadata.rb
index d0702fb5c7d..12ec6447251 100644
--- a/lib/gitlab/issuable_metadata.rb
+++ b/lib/gitlab/issuable_metadata.rb
@@ -27,8 +27,8 @@ module Gitlab
def data
return {} if issuable_ids.empty?
- issuable_ids.each_with_object({}) do |id, issuable_meta|
- issuable_meta[id] = metadata_for_issuable(id)
+ issuable_ids.index_with do |id|
+ metadata_for_issuable(id)
end
end
diff --git a/lib/gitlab/jira/http_client.rb b/lib/gitlab/jira/http_client.rb
index 02b0c902a70..7abfe8e38e8 100644
--- a/lib/gitlab/jira/http_client.rb
+++ b/lib/gitlab/jira/http_client.rb
@@ -35,12 +35,6 @@ module Gitlab
request_params[:base_uri] = uri.to_s
request_params.merge!(auth_params)
- if Feature.enabled?(:jira_raise_timeouts, type: :ops)
- request_params[:open_timeout] = 2.minutes
- request_params[:read_timeout] = 2.minutes
- request_params[:write_timeout] = 2.minutes
- end
-
result = Gitlab::HTTP.public_send(http_method, path, **request_params) # rubocop:disable GitlabSecurity/PublicSend
@authenticated = result.response.is_a?(Net::HTTPOK)
store_cookies(result) if options[:use_cookies]
diff --git a/lib/gitlab/jira_import/issues_importer.rb b/lib/gitlab/jira_import/issues_importer.rb
index 5057317ae01..7b031c26b72 100644
--- a/lib/gitlab/jira_import/issues_importer.rb
+++ b/lib/gitlab/jira_import/issues_importer.rb
@@ -16,7 +16,7 @@ module Gitlab
@start_at = Gitlab::JiraImport.get_issues_next_start_at(project.id)
@imported_items_cache_key = JiraImport.already_imported_cache_key(:issues, project.id)
@job_waiter = JobWaiter.new
- @issue_type_id = WorkItems::Type.default_issue_type.id
+ @issue_type_id = ::WorkItems::Type.default_issue_type.id
end
def execute
diff --git a/lib/gitlab/jwt_authenticatable.rb b/lib/gitlab/jwt_authenticatable.rb
index 08d9f69497e..7c36bbf3426 100644
--- a/lib/gitlab/jwt_authenticatable.rb
+++ b/lib/gitlab/jwt_authenticatable.rb
@@ -3,7 +3,7 @@
module Gitlab
module JwtAuthenticatable
# Supposedly the effective key size for HMAC-SHA256 is 256 bits, i.e. 32
- # bytes https://tools.ietf.org/html/rfc4868#section-2.6
+ # bytes https://www.rfc-editor.org/rfc/rfc4868#section-2.6
SECRET_LENGTH = 32
def self.included(base)
diff --git a/lib/gitlab/jwt_token.rb b/lib/gitlab/jwt_token.rb
index 11bc5479b6e..83aa7fa4a15 100644
--- a/lib/gitlab/jwt_token.rb
+++ b/lib/gitlab/jwt_token.rb
@@ -42,7 +42,7 @@ module Gitlab
def ==(other)
self.id == other.id &&
- self.payload == other.payload
+ self.payload == other.payload
end
def issued_at=(value)
diff --git a/lib/gitlab/kubernetes/helm/v2/install_command.rb b/lib/gitlab/kubernetes/helm/v2/install_command.rb
index 10e16723e45..c50db6bf177 100644
--- a/lib/gitlab/kubernetes/helm/v2/install_command.rb
+++ b/lib/gitlab/kubernetes/helm/v2/install_command.rb
@@ -36,13 +36,13 @@ module Gitlab
# installation and uprade of applications
def install_command
command = ['helm', 'upgrade', name, chart] +
- install_flag +
- rollback_support_flag +
- reset_values_flag +
- optional_version_flag +
- rbac_create_flag +
- namespace_flag +
- value_flag
+ install_flag +
+ rollback_support_flag +
+ reset_values_flag +
+ optional_version_flag +
+ rbac_create_flag +
+ namespace_flag +
+ value_flag
command.shelljoin
end
diff --git a/lib/gitlab/kubernetes/helm/v2/patch_command.rb b/lib/gitlab/kubernetes/helm/v2/patch_command.rb
index 2855e6444b1..40e56771e47 100644
--- a/lib/gitlab/kubernetes/helm/v2/patch_command.rb
+++ b/lib/gitlab/kubernetes/helm/v2/patch_command.rb
@@ -37,10 +37,10 @@ module Gitlab
def upgrade_command
command = ['helm', 'upgrade', name, chart] +
- reuse_values_flag +
- version_flag +
- namespace_flag +
- value_flag
+ reuse_values_flag +
+ version_flag +
+ namespace_flag +
+ value_flag
command.shelljoin
end
diff --git a/lib/gitlab/kubernetes/helm/v3/install_command.rb b/lib/gitlab/kubernetes/helm/v3/install_command.rb
index 20d17f49115..8d521f0dcd4 100644
--- a/lib/gitlab/kubernetes/helm/v3/install_command.rb
+++ b/lib/gitlab/kubernetes/helm/v3/install_command.rb
@@ -33,13 +33,13 @@ module Gitlab
# installation and uprade of applications
def install_command
command = ['helm', 'upgrade', name, chart] +
- install_flag +
- rollback_support_flag +
- reset_values_flag +
- optional_version_flag +
- rbac_create_flag +
- namespace_flag +
- value_flag
+ install_flag +
+ rollback_support_flag +
+ reset_values_flag +
+ optional_version_flag +
+ rbac_create_flag +
+ namespace_flag +
+ value_flag
command.shelljoin
end
diff --git a/lib/gitlab/kubernetes/helm/v3/patch_command.rb b/lib/gitlab/kubernetes/helm/v3/patch_command.rb
index 00f340591e7..1278e524bd2 100644
--- a/lib/gitlab/kubernetes/helm/v3/patch_command.rb
+++ b/lib/gitlab/kubernetes/helm/v3/patch_command.rb
@@ -34,10 +34,10 @@ module Gitlab
def upgrade_command
command = ['helm', 'upgrade', name, chart] +
- reuse_values_flag +
- version_flag +
- namespace_flag +
- value_flag
+ reuse_values_flag +
+ version_flag +
+ namespace_flag +
+ value_flag
command.shelljoin
end
diff --git a/lib/gitlab/kubernetes/kube_client.rb b/lib/gitlab/kubernetes/kube_client.rb
index 92ffa65fe74..44e53e9ec70 100644
--- a/lib/gitlab/kubernetes/kube_client.rb
+++ b/lib/gitlab/kubernetes/kube_client.rb
@@ -161,7 +161,7 @@ module Gitlab
def validate_url!
return if Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services?
- Gitlab::UrlBlocker.validate!(api_prefix, allow_local_network: false)
+ Gitlab::UrlBlocker.validate!(api_prefix, allow_local_network: false, schemes: %w[http https])
end
def service_account_exists?(resource)
diff --git a/lib/gitlab/memory/jemalloc.rb b/lib/gitlab/memory/jemalloc.rb
index e20e186cab9..6025e6ab6f2 100644
--- a/lib/gitlab/memory/jemalloc.rb
+++ b/lib/gitlab/memory/jemalloc.rb
@@ -14,41 +14,22 @@ module Gitlab
STATS_DEFAULT_FORMAT = :json
- FILENAME_PREFIX = 'jemalloc_stats'
-
# Return jemalloc stats as a string.
def stats(format: STATS_DEFAULT_FORMAT)
- verify_format!(format)
-
- with_malloc_stats_print do |stats_print|
- StringIO.new.tap { |io| write_stats(stats_print, io, STATS_FORMATS[format]) }.string
- end
+ dump_stats(StringIO.new, format: format).string
end
- # Write jemalloc stats to the given directory
- # @param [String] path Directory path the dump will be put into
- # @param [String] tmp_dir Directory path the dump will be streaming to. It is moved to `path` when finished.
- # @param [String] format `json` or `txt`
- # @param [String] filename_label Optional custom string that will be injected into the file name, e.g. `worker_0`
- # @return [String] Full path to the resulting dump file
- def dump_stats(path:, tmp_dir: Dir.tmpdir, format: STATS_DEFAULT_FORMAT, filename_label: nil)
+ # Streams jemalloc stats to the given IO object.
+ def dump_stats(io, format: STATS_DEFAULT_FORMAT)
verify_format!(format)
format_settings = STATS_FORMATS[format]
- tmp_file_path = File.join(tmp_dir, file_name(format_settings[:extension], filename_label))
- file_path = File.join(path, file_name(format_settings[:extension], filename_label))
with_malloc_stats_print do |stats_print|
- File.open(tmp_file_path, 'wb') do |io|
- write_stats(stats_print, io, format_settings)
- end
+ write_stats(stats_print, io, format_settings)
end
- # On OSX, `with_malloc_stats_print` is no-op, and, as result, no file will be written
- return unless File.exist?(tmp_file_path)
-
- FileUtils.mv(tmp_file_path, file_path)
- file_path
+ io
end
private
@@ -95,10 +76,6 @@ module Gitlab
stats_print.call(callback, nil, format[:options])
end
-
- def file_name(extension, filename_label)
- [FILENAME_PREFIX, $$, filename_label, Time.current.to_i, extension].reject(&:blank?).join('.')
- end
end
end
end
diff --git a/lib/gitlab/memory/reporter.rb b/lib/gitlab/memory/reporter.rb
new file mode 100644
index 00000000000..710c89c6216
--- /dev/null
+++ b/lib/gitlab/memory/reporter.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Memory
+ class Reporter
+ attr_reader :reports_path
+
+ def initialize(reports_path: nil, logger: Gitlab::AppLogger)
+ @reports_path = reports_path || ENV["GITLAB_DIAGNOSTIC_REPORTS_PATH"] || Dir.mktmpdir
+ @logger = logger
+
+ @worker_id = ::Prometheus::PidProvider.worker_id
+ @worker_uuid = SecureRandom.uuid
+
+ init_prometheus_metrics
+ end
+
+ def run_report(report)
+ return false unless report.active?
+
+ @logger.info(
+ log_labels(
+ message: 'started',
+ perf_report: report.name
+ ))
+
+ start_monotonic_time = Gitlab::Metrics::System.monotonic_time
+ start_thread_cpu_time = Gitlab::Metrics::System.thread_cpu_time
+
+ report_file = store_report(report)
+
+ cpu_s = Gitlab::Metrics::System.thread_cpu_duration(start_thread_cpu_time)
+ duration_s = Gitlab::Metrics::System.monotonic_time - start_monotonic_time
+
+ @logger.info(
+ log_labels(
+ message: 'finished',
+ perf_report: report.name,
+ cpu_s: cpu_s.round(2),
+ duration_s: duration_s.round(2),
+ perf_report_file: report_file,
+ perf_report_size_bytes: file_size(report_file)
+ ))
+
+ @report_duration_counter.increment({ report: report.name }, duration_s)
+
+ true
+ rescue StandardError => e
+ @logger.error(
+ log_labels(
+ message: 'failed',
+ perf_report: report.name,
+ error: e.inspect
+ ))
+
+ false
+ end
+
+ private
+
+ def store_report(report)
+ # Store report in tmp subdir while it is still streaming.
+ # This will clearly separate finished reports from the files we are still writing to.
+ tmp_dir = File.join(@reports_path, 'tmp')
+ FileUtils.mkdir_p(tmp_dir)
+
+ report_file = file_name(report)
+ tmp_file_path = File.join(tmp_dir, report_file)
+
+ io_r, io_w = IO.pipe
+ pid = nil
+ File.open(tmp_file_path, 'wb') do |file|
+ extras = {
+ in: io_r,
+ out: file,
+ err: $stderr
+ }
+ pid = Process.spawn('gzip', '--fast', **extras)
+ io_r.close
+
+ report.run(io_w)
+ io_w.close
+
+ Process.waitpid(pid)
+ end
+
+ File.join(@reports_path, report_file).tap do |report_file_path|
+ FileUtils.mv(tmp_file_path, report_file_path)
+ end
+ ensure
+ [io_r, io_w].each(&:close)
+
+ # Make sure we don't leave any running processes behind.
+ Gitlab::ProcessManagement.signal(pid, :KILL) if pid
+ end
+
+ def log_labels(**extra_labels)
+ {
+ pid: $$,
+ worker_id: @worker_id,
+ perf_report_worker_uuid: @worker_uuid
+ }.merge(extra_labels)
+ end
+
+ def file_name(report)
+ timestamp = Time.current.strftime('%Y-%m-%d.%H:%M:%S:%L')
+
+ report_id = [@worker_id, @worker_uuid].join(".")
+
+ [report.name, timestamp, report_id, 'gz'].compact_blank.join('.')
+ end
+
+ def file_size(file_path)
+ File.size(file_path.to_s)
+ rescue Errno::ENOENT
+ 0
+ end
+
+ def init_prometheus_metrics
+ default_labels = { pid: @worker_id }
+
+ @report_duration_counter = Gitlab::Metrics.counter(
+ :gitlab_diag_report_duration_seconds_total,
+ 'Total time elapsed for running diagnostic report',
+ default_labels
+ )
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/memory/reports/heap_dump.rb b/lib/gitlab/memory/reports/heap_dump.rb
new file mode 100644
index 00000000000..95779407f12
--- /dev/null
+++ b/lib/gitlab/memory/reports/heap_dump.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Memory
+ module Reports
+ class HeapDump
+ class << self
+ def enqueue!
+ @write_heap_dump = true
+ end
+
+ def enqueued?
+ !!@write_heap_dump
+ end
+ end
+
+ def name
+ 'heap_dump'
+ end
+
+ def active?
+ Feature.enabled?(:report_heap_dumps, type: :ops)
+ end
+
+ def run(writer)
+ return false unless self.class.enqueued?
+
+ ObjectSpace.dump_all(output: writer)
+
+ true
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/memory/reports/jemalloc_stats.rb b/lib/gitlab/memory/reports/jemalloc_stats.rb
index 05f0717d7c3..cfda409594f 100644
--- a/lib/gitlab/memory/reports/jemalloc_stats.rb
+++ b/lib/gitlab/memory/reports/jemalloc_stats.rb
@@ -4,70 +4,19 @@ module Gitlab
module Memory
module Reports
class JemallocStats
- # On prod, Jemalloc reports sizes were ~2.5 MB:
- # https://gitlab.com/gitlab-com/gl-infra/reliability/-/issues/15993#note_1014767214
- # We configured 1GB emptyDir per pod:
- # https://gitlab.com/gitlab-com/gl-infra/k8s-workloads/gitlab-com/-/merge_requests/1949
- # The pod will be evicted when the size limit is exceeded. We never want this to happen, for availability.
- #
- # With the default, we have a headroom (250*2.5MB=625<1000 MB) to fit into configured emptyDir.
- # It would allow us to keep 3+ days worth of reports for 6 workers running every 2 hours: 3*6*12=216<250
- #
- # The cleanup logic will be redundant after we'll implement the uploads, which would perform the cleanup.
- DEFAULT_MAX_REPORTS_STORED = 250
-
- def initialize(reports_path:)
- @reports_path = reports_path
-
- # Store report in tmp subdir while it is still streaming.
- # This will clearly separate finished reports from the files we are still writing to.
- @tmp_dir = File.join(@reports_path, 'tmp')
- FileUtils.mkdir_p(@tmp_dir)
+ def name
+ 'jemalloc_stats'
end
- def run
+ def run(writer)
return unless active?
- Gitlab::Memory::Jemalloc.dump_stats(path: reports_path, tmp_dir: @tmp_dir, filename_label: worker_id).tap do
- cleanup
- end
+ Gitlab::Memory::Jemalloc.dump_stats(writer)
end
def active?
Feature.enabled?(:report_jemalloc_stats, type: :ops)
end
-
- private
-
- attr_reader :reports_path
-
- def cleanup
- reports_files_modified_order[0...-max_reports_stored].each do |f|
- File.unlink(f) if File.exist?(f)
- rescue Errno::ENOENT
- # Path does not exist: Ignore. We already check `File.exist?`
- # Rescue to be extra safe, because each worker could perform a cleanup
- end
- end
-
- def reports_files_modified_order
- pattern = File.join(reports_path, "#{Gitlab::Memory::Jemalloc::FILENAME_PREFIX}*")
-
- Dir.glob(pattern).sort_by do |f|
- test('M', f)
- rescue Errno::ENOENT
- # Path does not exist: Return any timestamp to proceed with the sort
- Time.current
- end
- end
-
- def worker_id
- ::Prometheus::PidProvider.worker_id
- end
-
- def max_reports_stored
- ENV["GITLAB_DIAGNOSTIC_REPORTS_JEMALLOC_MAX_REPORTS_STORED"] || DEFAULT_MAX_REPORTS_STORED
- end
end
end
end
diff --git a/lib/gitlab/memory/reports_daemon.rb b/lib/gitlab/memory/reports_daemon.rb
index 0dfc31235e7..9bbfe81116d 100644
--- a/lib/gitlab/memory/reports_daemon.rb
+++ b/lib/gitlab/memory/reports_daemon.rb
@@ -7,9 +7,7 @@ module Gitlab
DEFAULT_SLEEP_MAX_DELTA_S = 600 # 0..10 minutes
DEFAULT_SLEEP_BETWEEN_REPORTS_S = 120 # 2 minutes
- DEFAULT_REPORTS_PATH = Dir.tmpdir
-
- def initialize(**options)
+ def initialize(reporter: nil, reports: nil, **options)
super
@alive = true
@@ -21,31 +19,20 @@ module Gitlab
@sleep_between_reports_s =
ENV['GITLAB_DIAGNOSTIC_REPORTS_SLEEP_BETWEEN_REPORTS_S']&.to_i || DEFAULT_SLEEP_BETWEEN_REPORTS_S
- @reports_path =
- ENV["GITLAB_DIAGNOSTIC_REPORTS_PATH"] || DEFAULT_REPORTS_PATH
-
- @reports = [Gitlab::Memory::Reports::JemallocStats.new(reports_path: reports_path)]
-
- init_prometheus_metrics
+ @reporter = reporter || Reporter.new
+ @reports = reports || [
+ Gitlab::Memory::Reports::JemallocStats.new
+ ]
end
- attr_reader :sleep_s, :sleep_max_delta_s, :sleep_between_reports_s, :reports_path
+ attr_reader :sleep_s, :sleep_max_delta_s, :sleep_between_reports_s
def run_thread
while alive
sleep interval_with_jitter
reports.select(&:active?).each do |report|
- start_monotonic_time = Gitlab::Metrics::System.monotonic_time
- start_thread_cpu_time = Gitlab::Metrics::System.thread_cpu_time
-
- file_path = report.run
-
- cpu_s = Gitlab::Metrics::System.thread_cpu_duration(start_thread_cpu_time)
- duration_s = Gitlab::Metrics::System.monotonic_time - start_monotonic_time
-
- log_report(label: report_label(report), cpu_s: cpu_s, duration_s: duration_s, size: file_size(file_path))
- @report_duration_counter.increment({ report: report_label(report) }, duration_s)
+ @reporter.run_report(report)
sleep sleep_between_reports_s
end
@@ -62,45 +49,9 @@ module Gitlab
sleep_s + rand(sleep_max_delta_s)
end
- def log_report(label:, duration_s:, cpu_s:, size:)
- Gitlab::AppLogger.info(
- message: 'finished',
- pid: $$,
- worker_id: worker_id,
- perf_report: label,
- duration_s: duration_s.round(2),
- cpu_s: cpu_s.round(2),
- perf_report_size_bytes: size
- )
- end
-
- def worker_id
- ::Prometheus::PidProvider.worker_id
- end
-
- def report_label(report)
- report.class.to_s.demodulize.underscore
- end
-
def stop_working
@alive = false
end
-
- def init_prometheus_metrics
- default_labels = { pid: worker_id }
-
- @report_duration_counter = Gitlab::Metrics.counter(
- :gitlab_diag_report_duration_seconds_total,
- 'Total time elapsed for running diagnostic report',
- default_labels
- )
- end
-
- def file_size(file_path)
- File.size(file_path.to_s)
- rescue Errno::ENOENT
- 0
- end
end
end
end
diff --git a/lib/gitlab/memory/watchdog.rb b/lib/gitlab/memory/watchdog.rb
index 19dfc640b5d..25af5bd781a 100644
--- a/lib/gitlab/memory/watchdog.rb
+++ b/lib/gitlab/memory/watchdog.rb
@@ -50,27 +50,17 @@ module Gitlab
def initialize
@configuration = Configuration.new
@alive = true
-
- init_prometheus_metrics
end
##
- # Configuration for Watchdog, use like:
- #
- # watchdog.configure do |config|
- # config.handler = Gitlab::Memory::Watchdog::TermProcessHandler
- # config.sleep_time_seconds = 60
- # config.logger = Gitlab::AppLogger
- # config.monitors do |stack|
- # stack.push MyMonitorClass, args*, max_strikes:, kwargs**, &block
- # end
- # end
+ # Configuration for Watchdog, see Gitlab::Memory::Watchdog::Configurator
+ # for examples.
def configure
- yield @configuration
+ yield configuration
end
def call
- logger.info(log_labels.merge(message: 'started'))
+ event_reporter.started(log_labels)
while @alive
sleep(sleep_time_seconds)
@@ -78,35 +68,45 @@ module Gitlab
monitor if Feature.enabled?(:gitlab_memory_watchdog, type: :ops)
end
- logger.info(log_labels.merge(message: 'stopped'))
+ event_reporter.stopped(log_labels(memwd_reason: @reason).compact)
end
- def stop
+ def stop(reason: nil)
+ @reason = reason
@alive = false
end
private
+ attr_reader :configuration
+
+ delegate :event_reporter, :monitors, :sleep_time_seconds, to: :configuration
+
def monitor
- @configuration.monitors.call_each do |result|
+ if monitors.empty?
+ stop(reason: 'monitors are not configured')
+ return
+ end
+
+ monitors.call_each do |result|
break unless @alive
next unless result.threshold_violated?
- @counter_violations.increment(reason: result.monitor_name)
+ event_reporter.threshold_violated(result.monitor_name)
next unless result.strikes_exceeded?
- @alive = !memory_limit_exceeded_callback(result.monitor_name, result.payload)
+ strike_exceeded_callback(result.monitor_name, result.payload)
end
end
- def memory_limit_exceeded_callback(monitor_name, monitor_payload)
- all_labels = log_labels.merge(monitor_payload)
- logger.warn(all_labels)
- @counter_violations_handled.increment(reason: monitor_name)
+ def strike_exceeded_callback(monitor_name, monitor_payload)
+ event_reporter.strikes_exceeded(monitor_name, log_labels(monitor_payload))
+
+ Gitlab::Memory::Reports::HeapDump.enqueue!
- handler.call
+ stop(reason: 'successfully handled') if handler.call
end
def handler
@@ -114,46 +114,13 @@ module Gitlab
# all that happens is we collect logs and Prometheus events for fragmentation violations.
return NullHandler.instance unless Feature.enabled?(:enforce_memory_watchdog, type: :ops)
- @configuration.handler
- end
-
- def logger
- @configuration.logger
+ configuration.handler
end
- def sleep_time_seconds
- @configuration.sleep_time_seconds
- end
-
- def log_labels
- {
- pid: $$,
- worker_id: worker_id,
+ def log_labels(extra = {})
+ extra.merge(
memwd_handler_class: handler.class.name,
- memwd_sleep_time_s: sleep_time_seconds,
- memwd_rss_bytes: process_rss_bytes
- }
- end
-
- def process_rss_bytes
- Gitlab::Metrics::System.memory_usage_rss[:total]
- end
-
- def worker_id
- ::Prometheus::PidProvider.worker_id
- end
-
- def init_prometheus_metrics
- default_labels = { pid: worker_id }
- @counter_violations = Gitlab::Metrics.counter(
- :gitlab_memwd_violations_total,
- 'Total number of times a Ruby process violated a memory threshold',
- default_labels
- )
- @counter_violations_handled = Gitlab::Metrics.counter(
- :gitlab_memwd_violations_handled_total,
- 'Total number of times Ruby process memory violations were handled',
- default_labels
+ memwd_sleep_time_s: sleep_time_seconds
)
end
end
diff --git a/lib/gitlab/memory/watchdog/configuration.rb b/lib/gitlab/memory/watchdog/configuration.rb
index 793f75adf59..5c459220be8 100644
--- a/lib/gitlab/memory/watchdog/configuration.rb
+++ b/lib/gitlab/memory/watchdog/configuration.rb
@@ -10,7 +10,6 @@ module Gitlab
end
def push(monitor_class, *args, **kwargs, &block)
- remove(monitor_class)
@monitors.push(build_monitor_state(monitor_class, *args, **kwargs, &block))
end
@@ -20,16 +19,17 @@ module Gitlab
end
end
- private
-
- def remove(monitor_class)
- @monitors.delete_if { |monitor| monitor.monitor_class == monitor_class }
+ def empty?
+ @monitors.empty?
end
- def build_monitor_state(monitor_class, *args, max_strikes:, **kwargs, &block)
+ private
+
+ def build_monitor_state(monitor_class, *args, max_strikes:, monitor_name: nil, **kwargs, &block)
monitor = build_monitor(monitor_class, *args, **kwargs, &block)
+ monitor_name ||= monitor_class.name.demodulize.underscore
- Gitlab::Memory::Watchdog::MonitorState.new(monitor, max_strikes: max_strikes)
+ Gitlab::Memory::Watchdog::MonitorState.new(monitor, max_strikes: max_strikes, monitor_name: monitor_name)
end
def build_monitor(monitor_class, *args, **kwargs, &block)
@@ -39,7 +39,7 @@ module Gitlab
DEFAULT_SLEEP_TIME_SECONDS = 60
- attr_writer :logger, :handler, :sleep_time_seconds
+ attr_writer :event_reporter, :handler, :sleep_time_seconds
def monitors
@monitor_stack ||= MonitorStack.new
@@ -51,8 +51,8 @@ module Gitlab
@handler ||= NullHandler.instance
end
- def logger
- @logger ||= Gitlab::Logger.new($stdout)
+ def event_reporter
+ @event_reporter ||= EventReporter.new
end
# Used to control the frequency with which the watchdog will wake up and poll the GC.
diff --git a/lib/gitlab/memory/watchdog/configurator.rb b/lib/gitlab/memory/watchdog/configurator.rb
index 82b1b02b63f..04c04cbde02 100644
--- a/lib/gitlab/memory/watchdog/configurator.rb
+++ b/lib/gitlab/memory/watchdog/configurator.rb
@@ -4,36 +4,43 @@ module Gitlab
module Memory
class Watchdog
class Configurator
+ DEFAULT_PUMA_WORKER_RSS_LIMIT_MB = 1200
+ DEFAULT_SLEEP_INTERVAL_S = 60
+ DEFAULT_SIDEKIQ_SLEEP_INTERVAL_S = 3
+ MIN_SIDEKIQ_SLEEP_INTERVAL_S = 2
+ DEFAULT_MAX_STRIKES = 5
+ DEFAULT_MAX_HEAP_FRAG = 0.5
+ DEFAULT_MAX_MEM_GROWTH = 3.0
+ # grace_time / sleep_interval = max_strikes allowed for Sidekiq process to violate defined limits.
+ DEFAULT_SIDEKIQ_GRACE_TIME_S = 300
+
class << self
def configure_for_puma
- lambda do |config|
- sleep_time_seconds = ENV.fetch('GITLAB_MEMWD_SLEEP_TIME_SEC', 60).to_i
- config.logger = Gitlab::AppLogger
+ ->(config) do
config.handler = Gitlab::Memory::Watchdog::PumaHandler.new
- config.sleep_time_seconds = sleep_time_seconds
+ config.sleep_time_seconds = ENV.fetch('GITLAB_MEMWD_SLEEP_TIME_SEC', DEFAULT_SLEEP_INTERVAL_S).to_i
config.monitors(&configure_monitors_for_puma)
end
end
def configure_for_sidekiq
- lambda do |config|
- sleep_time_seconds = [ENV.fetch('SIDEKIQ_MEMORY_KILLER_CHECK_INTERVAL', 3).to_i, 2].max
- config.logger = Sidekiq.logger
+ ->(config) do
config.handler = Gitlab::Memory::Watchdog::TermProcessHandler.new
- config.sleep_time_seconds = sleep_time_seconds
+ config.sleep_time_seconds = sidekiq_sleep_time
config.monitors(&configure_monitors_for_sidekiq)
+ config.event_reporter = SidekiqEventReporter.new
end
end
private
def configure_monitors_for_puma
- lambda do |stack|
- max_strikes = ENV.fetch('GITLAB_MEMWD_MAX_STRIKES', 5).to_i
+ ->(stack) do
+ max_strikes = ENV.fetch('GITLAB_MEMWD_MAX_STRIKES', DEFAULT_MAX_STRIKES).to_i
if Gitlab::Utils.to_boolean(ENV['DISABLE_PUMA_WORKER_KILLER'])
- max_heap_frag = ENV.fetch('GITLAB_MEMWD_MAX_HEAP_FRAG', 0.5).to_f
- max_mem_growth = ENV.fetch('GITLAB_MEMWD_MAX_MEM_GROWTH', 3.0).to_f
+ max_heap_frag = ENV.fetch('GITLAB_MEMWD_MAX_HEAP_FRAG', DEFAULT_MAX_HEAP_FRAG).to_f
+ max_mem_growth = ENV.fetch('GITLAB_MEMWD_MAX_MEM_GROWTH', DEFAULT_MAX_MEM_GROWTH).to_f
# stack.push MonitorClass, args*, max_strikes:, kwargs**, &block
stack.push Gitlab::Memory::Watchdog::Monitor::HeapFragmentation,
@@ -44,17 +51,44 @@ module Gitlab
max_mem_growth: max_mem_growth,
max_strikes: max_strikes
else
- memory_limit = ENV.fetch('PUMA_WORKER_MAX_MEMORY', 1200).to_i
+ memory_limit = ENV.fetch('PUMA_WORKER_MAX_MEMORY', DEFAULT_PUMA_WORKER_RSS_LIMIT_MB).to_i
stack.push Gitlab::Memory::Watchdog::Monitor::RssMemoryLimit,
- memory_limit: memory_limit.megabytes,
+ memory_limit_bytes: memory_limit.megabytes,
max_strikes: max_strikes
end
end
end
+ def sidekiq_sleep_time
+ [
+ ENV.fetch('SIDEKIQ_MEMORY_KILLER_CHECK_INTERVAL', DEFAULT_SIDEKIQ_SLEEP_INTERVAL_S).to_i,
+ MIN_SIDEKIQ_SLEEP_INTERVAL_S
+ ].max
+ end
+
def configure_monitors_for_sidekiq
- # NOP - At the moment we don't run watchdog for Sidekiq
+ ->(stack) do
+ if ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'].to_i.nonzero?
+ soft_limit_bytes = ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'].to_i.kilobytes
+ grace_time = ENV.fetch('SIDEKIQ_MEMORY_KILLER_GRACE_TIME', DEFAULT_SIDEKIQ_GRACE_TIME_S).to_i
+ max_strikes = grace_time / sidekiq_sleep_time
+
+ stack.push Gitlab::Memory::Watchdog::Monitor::RssMemoryLimit,
+ memory_limit_bytes: soft_limit_bytes,
+ max_strikes: max_strikes.to_i,
+ monitor_name: :rss_memory_soft_limit
+ end
+
+ if ENV['SIDEKIQ_MEMORY_KILLER_HARD_LIMIT_RSS'].to_i.nonzero?
+ hard_limit_bytes = ENV['SIDEKIQ_MEMORY_KILLER_HARD_LIMIT_RSS'].to_i.kilobytes
+
+ stack.push Gitlab::Memory::Watchdog::Monitor::RssMemoryLimit,
+ memory_limit_bytes: hard_limit_bytes,
+ max_strikes: 0,
+ monitor_name: :rss_memory_hard_limit
+ end
+ end
end
end
end
diff --git a/lib/gitlab/memory/watchdog/event_reporter.rb b/lib/gitlab/memory/watchdog/event_reporter.rb
new file mode 100644
index 00000000000..c37426cb660
--- /dev/null
+++ b/lib/gitlab/memory/watchdog/event_reporter.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Memory
+ class Watchdog
+ class EventReporter
+ include ::Gitlab::Utils::StrongMemoize
+
+ attr_reader :logger
+
+ def initialize(logger: Gitlab::AppLogger)
+ @logger = logger
+ init_prometheus_metrics
+ end
+
+ def started(labels = {})
+ logger.info(message: 'started', **log_labels(labels))
+ end
+
+ def stopped(labels = {})
+ logger.info(message: 'stopped', **log_labels(labels))
+ end
+
+ def threshold_violated(monitor_name)
+ @counter_violations.increment(reason: monitor_name)
+ end
+
+ def strikes_exceeded(monitor_name, labels = {})
+ logger.warn(log_labels(labels))
+
+ @counter_violations_handled.increment(reason: monitor_name)
+ end
+
+ private
+
+ def log_labels(extra = {})
+ extra.merge(
+ pid: $$,
+ worker_id: worker_id,
+ memwd_rss_bytes: process_rss_bytes
+ )
+ end
+
+ def process_rss_bytes
+ Gitlab::Metrics::System.memory_usage_rss[:total]
+ end
+
+ def worker_id
+ ::Prometheus::PidProvider.worker_id
+ end
+
+ def init_prometheus_metrics
+ default_labels = { pid: worker_id }
+ @counter_violations = Gitlab::Metrics.counter(
+ :gitlab_memwd_violations_total,
+ 'Total number of times a Ruby process violated a memory threshold',
+ default_labels
+ )
+ @counter_violations_handled = Gitlab::Metrics.counter(
+ :gitlab_memwd_violations_handled_total,
+ 'Total number of times Ruby process memory violations were handled',
+ default_labels
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/memory/watchdog/monitor/heap_fragmentation.rb b/lib/gitlab/memory/watchdog/monitor/heap_fragmentation.rb
index 8f230980eac..ce99b68464e 100644
--- a/lib/gitlab/memory/watchdog/monitor/heap_fragmentation.rb
+++ b/lib/gitlab/memory/watchdog/monitor/heap_fragmentation.rb
@@ -4,10 +4,7 @@ module Gitlab
module Memory
class Watchdog
module Monitor
- # A monitor that observes Ruby heap fragmentation and calls
- # memory_violation_callback when the Ruby heap has been fragmented for an extended
- # period of time.
- #
+ # A monitor that observes Ruby heap fragmentation.
# See Gitlab::Metrics::Memory for how heap fragmentation is defined.
class HeapFragmentation
attr_reader :max_heap_fragmentation
diff --git a/lib/gitlab/memory/watchdog/monitor/rss_memory_limit.rb b/lib/gitlab/memory/watchdog/monitor/rss_memory_limit.rb
index 3e7de024630..ac71592294c 100644
--- a/lib/gitlab/memory/watchdog/monitor/rss_memory_limit.rb
+++ b/lib/gitlab/memory/watchdog/monitor/rss_memory_limit.rb
@@ -5,29 +5,38 @@ module Gitlab
class Watchdog
module Monitor
class RssMemoryLimit
- attr_reader :memory_limit
+ attr_reader :memory_limit_bytes
- def initialize(memory_limit:)
- @memory_limit = memory_limit
+ def initialize(memory_limit_bytes:)
+ @memory_limit_bytes = memory_limit_bytes
+ init_memory_limit_metrics
end
def call
- worker_rss = Gitlab::Metrics::System.memory_usage_rss[:total]
+ worker_rss_bytes = Gitlab::Metrics::System.memory_usage_rss[:total]
- return { threshold_violated: false, payload: {} } if worker_rss <= memory_limit
+ return { threshold_violated: false, payload: {} } if worker_rss_bytes <= memory_limit_bytes
- { threshold_violated: true, payload: payload(worker_rss, memory_limit) }
+ { threshold_violated: true, payload: payload(worker_rss_bytes, memory_limit_bytes) }
end
private
- def payload(worker_rss, memory_limit)
+ def payload(worker_rss_bytes, memory_limit_bytes)
{
message: 'rss memory limit exceeded',
- memwd_rss_bytes: worker_rss,
- memwd_max_rss_bytes: memory_limit
+ memwd_rss_bytes: worker_rss_bytes,
+ memwd_max_rss_bytes: memory_limit_bytes
}
end
+
+ def init_memory_limit_metrics
+ rss_memory_limit = Gitlab::Metrics.gauge(
+ :gitlab_memwd_max_memory_limit,
+ 'The configured fixed limit for rss memory'
+ )
+ rss_memory_limit.set({}, memory_limit_bytes)
+ end
end
end
end
diff --git a/lib/gitlab/memory/watchdog/monitor_state.rb b/lib/gitlab/memory/watchdog/monitor_state.rb
index 73be5de3e45..bb083fedf2c 100644
--- a/lib/gitlab/memory/watchdog/monitor_state.rb
+++ b/lib/gitlab/memory/watchdog/monitor_state.rb
@@ -5,12 +5,12 @@ module Gitlab
class Watchdog
class MonitorState
class Result
- attr_reader :payload
+ attr_reader :payload, :monitor_name
- def initialize(strikes_exceeded:, threshold_violated:, monitor_class:, payload: )
+ def initialize(strikes_exceeded:, threshold_violated:, monitor_name:, payload:)
@strikes_exceeded = strikes_exceeded
@threshold_violated = threshold_violated
- @monitor_class = monitor_class
+ @monitor_name = monitor_name.to_s.to_sym
@payload = payload
end
@@ -21,15 +21,12 @@ module Gitlab
def threshold_violated?
@threshold_violated
end
-
- def monitor_name
- @monitor_class.name.demodulize.underscore.to_sym
- end
end
- def initialize(monitor, max_strikes:)
+ def initialize(monitor, max_strikes:, monitor_name:)
@monitor = monitor
@max_strikes = max_strikes
+ @monitor_name = monitor_name
@strikes = 0
end
@@ -47,16 +44,12 @@ module Gitlab
build_result(monitor_result)
end
- def monitor_class
- @monitor.class
- end
-
private
def build_result(monitor_result)
Result.new(
strikes_exceeded: strikes_exceeded?,
- monitor_class: monitor_class,
+ monitor_name: @monitor_name,
threshold_violated: monitor_result[:threshold_violated],
payload: payload.merge(monitor_result[:payload]))
end
diff --git a/lib/gitlab/memory/watchdog/sidekiq_event_reporter.rb b/lib/gitlab/memory/watchdog/sidekiq_event_reporter.rb
new file mode 100644
index 00000000000..473ed1b8094
--- /dev/null
+++ b/lib/gitlab/memory/watchdog/sidekiq_event_reporter.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Memory
+ class Watchdog
+ class SidekiqEventReporter
+ include ::Gitlab::Utils::StrongMemoize
+
+ delegate :threshold_violated, :started, :stopped, :logger, to: :event_reporter
+
+ def initialize(logger: ::Sidekiq.logger)
+ @event_reporter = EventReporter.new(logger: logger)
+ @sidekiq_daemon_monitor = Gitlab::SidekiqDaemon::Monitor.instance
+ init_prometheus_metrics
+ end
+
+ def strikes_exceeded(monitor_name, labels = {})
+ running_jobs = fetch_running_jobs
+ labels[:running_jobs] = running_jobs
+ increment_worker_counters(running_jobs)
+
+ event_reporter.strikes_exceeded(monitor_name, labels)
+ end
+
+ private
+
+ attr_reader :event_reporter
+
+ def fetch_running_jobs
+ @sidekiq_daemon_monitor.jobs.map do |jid, job|
+ {
+ jid: jid,
+ worker_class: job[:worker_class].name
+ }
+ end
+ end
+
+ def increment_worker_counters(running_jobs)
+ running_jobs.each do |job|
+ @sidekiq_watchdog_running_jobs_counter.increment({ worker_class: job[:worker_class] })
+ end
+ end
+
+ def init_prometheus_metrics
+ @sidekiq_watchdog_running_jobs_counter = ::Gitlab::Metrics.counter(
+ :sidekiq_watchdog_running_jobs_total,
+ 'Current running jobs when limit was reached'
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/merge_requests/commit_message_generator.rb b/lib/gitlab/merge_requests/commit_message_generator.rb
deleted file mode 100644
index ef5c63925c2..00000000000
--- a/lib/gitlab/merge_requests/commit_message_generator.rb
+++ /dev/null
@@ -1,95 +0,0 @@
-# frozen_string_literal: true
-module Gitlab
- module MergeRequests
- class CommitMessageGenerator
- def initialize(merge_request:, current_user:)
- @merge_request = merge_request
- @current_user = @merge_request.metrics&.merged_by || @merge_request.merge_user || current_user
- end
-
- def merge_message
- return unless @merge_request.target_project.merge_commit_template.present?
-
- replace_placeholders(@merge_request.target_project.merge_commit_template)
- end
-
- def squash_message
- return unless @merge_request.target_project.squash_commit_template.present?
-
- replace_placeholders(@merge_request.target_project.squash_commit_template, squash: true)
- end
-
- private
-
- attr_reader :merge_request
- attr_reader :current_user
-
- PLACEHOLDERS = {
- 'source_branch' => ->(merge_request, _, _) { merge_request.source_branch.to_s },
- 'target_branch' => ->(merge_request, _, _) { merge_request.target_branch.to_s },
- 'title' => ->(merge_request, _, _) { merge_request.title },
- 'issues' => ->(merge_request, _, _) do
- return if merge_request.visible_closing_issues_for.blank?
-
- closes_issues_references = merge_request.visible_closing_issues_for.map do |issue|
- issue.to_reference(merge_request.target_project)
- end
- "Closes #{closes_issues_references.to_sentence}"
- end,
- 'description' => ->(merge_request, _, _) { merge_request.description },
- 'reference' => ->(merge_request, _, _) { merge_request.to_reference(full: true) },
- 'first_commit' => -> (merge_request, _, _) { merge_request.first_commit&.safe_message&.strip },
- 'first_multiline_commit' => -> (merge_request, _, _) { merge_request.first_multiline_commit&.safe_message&.strip.presence || merge_request.title },
- 'url' => ->(merge_request, _, _) { Gitlab::UrlBuilder.build(merge_request) },
- 'approved_by' => ->(merge_request, _, _) { merge_request.approved_by_users.map { |user| "Approved-by: #{user.name} <#{user.commit_email_or_default}>" }.join("\n") },
- 'merged_by' => ->(_, user, _) { "#{user&.name} <#{user&.commit_email_or_default}>" },
- 'co_authored_by' => ->(merge_request, merged_by, squash) do
- commit_author = squash ? merge_request.author : merged_by
- merge_request.recent_commits
- .to_h { |commit| [commit.author_email, commit.author_name] }
- .except(commit_author&.commit_email_or_default)
- .map { |author_email, author_name| "Co-authored-by: #{author_name} <#{author_email}>" }
- .join("\n")
- end,
- 'all_commits' => -> (merge_request, _, _) do
- merge_request
- .recent_commits
- .without_merge_commits
- .map do |commit|
- if commit.safe_message&.bytesize&.>(100.kilobytes)
- "* #{commit.title}\n\n-- Skipped commit body exceeding 100KiB in size."
- else
- "* #{commit.safe_message&.strip}"
- end
- end
- .join("\n\n")
- end
- }.freeze
-
- PLACEHOLDERS_COMBINED_REGEX = /%{(#{Regexp.union(PLACEHOLDERS.keys)})}/.freeze
-
- def replace_placeholders(message, squash: false)
- # Convert CRLF to LF.
- message = message.delete("\r")
-
- used_variables = message.scan(PLACEHOLDERS_COMBINED_REGEX).map { |value| value[0] }.uniq
- values = used_variables.to_h do |variable_name|
- ["%{#{variable_name}}", PLACEHOLDERS[variable_name].call(merge_request, current_user, squash)]
- end
- names_of_empty_variables = values.filter_map { |name, value| name if value.blank? }
-
- # Remove lines that contain empty variable placeholder and nothing else.
- if names_of_empty_variables.present?
- # If there is blank line or EOF after it, remove blank line before it as well.
- message = message.gsub(/\n\n#{Regexp.union(names_of_empty_variables)}(\n\n|\Z)/, '\1')
- # Otherwise, remove only the line it is in.
- message = message.gsub(/^#{Regexp.union(names_of_empty_variables)}\n/, '')
- end
- # Substitute all variables with their values.
- message = message.gsub(Regexp.union(values.keys), values) if values.present?
-
- message
- end
- end
- end
-end
diff --git a/lib/gitlab/merge_requests/message_generator.rb b/lib/gitlab/merge_requests/message_generator.rb
new file mode 100644
index 00000000000..5113fbdcd7b
--- /dev/null
+++ b/lib/gitlab/merge_requests/message_generator.rb
@@ -0,0 +1,142 @@
+# frozen_string_literal: true
+module Gitlab
+ module MergeRequests
+ class MessageGenerator
+ def initialize(merge_request:, current_user:)
+ @merge_request = merge_request
+ @current_user = @merge_request.metrics&.merged_by || @merge_request.merge_user || current_user
+ end
+
+ def merge_commit_message
+ return unless @merge_request.target_project.merge_commit_template.present?
+
+ replace_placeholders(@merge_request.target_project.merge_commit_template, allowed_placeholders: PLACEHOLDERS)
+ end
+
+ def squash_commit_message
+ return unless @merge_request.target_project.squash_commit_template.present?
+
+ replace_placeholders(
+ @merge_request.target_project.squash_commit_template,
+ allowed_placeholders: PLACEHOLDERS,
+ squash: true
+ )
+ end
+
+ def new_mr_description
+ return unless @merge_request.description.present?
+
+ replace_placeholders(
+ @merge_request.description,
+ allowed_placeholders: ALLOWED_NEW_MR_PLACEHOLDERS,
+ keep_carriage_return: true
+ )
+ end
+
+ private
+
+ attr_reader :merge_request, :current_user
+
+ PLACEHOLDERS = {
+ 'source_branch' => ->(merge_request, _, _) { merge_request.source_branch.to_s },
+ 'target_branch' => ->(merge_request, _, _) { merge_request.target_branch.to_s },
+ 'title' => ->(merge_request, _, _) { merge_request.title },
+ 'issues' => ->(merge_request, _, _) do
+ return if merge_request.visible_closing_issues_for.blank?
+
+ closes_issues_references = merge_request.visible_closing_issues_for.map do |issue|
+ issue.to_reference(merge_request.target_project)
+ end
+ "Closes #{closes_issues_references.to_sentence}"
+ end,
+ 'description' => ->(merge_request, _, _) { merge_request.description },
+ 'reference' => ->(merge_request, _, _) { merge_request.to_reference(full: true) },
+ 'first_commit' => -> (merge_request, _, _) {
+ return unless merge_request.persisted? || merge_request.compare_commits.present?
+
+ merge_request.first_commit&.safe_message&.strip
+ },
+ 'first_multiline_commit' => -> (merge_request, _, _) {
+ merge_request.first_multiline_commit&.safe_message&.strip.presence || merge_request.title
+ },
+ 'url' => ->(merge_request, _, _) { Gitlab::UrlBuilder.build(merge_request) },
+ 'reviewed_by' => ->(merge_request, _, _) {
+ merge_request.reviewed_by_users
+ .map { |user| "Reviewed-by: #{user.name} <#{user.commit_email_or_default}>" }
+ .join("\n")
+ },
+ 'approved_by' => ->(merge_request, _, _) {
+ merge_request.approved_by_users
+ .map { |user| "Approved-by: #{user.name} <#{user.commit_email_or_default}>" }
+ .join("\n")
+ },
+ 'merged_by' => ->(_, user, _) { "#{user&.name} <#{user&.commit_email_or_default}>" },
+ 'co_authored_by' => ->(merge_request, merged_by, squash) do
+ commit_author = squash ? merge_request.author : merged_by
+ merge_request.recent_commits
+ .to_h { |commit| [commit.author_email, commit.author_name] }
+ .except(commit_author&.commit_email_or_default)
+ .map { |author_email, author_name| "Co-authored-by: #{author_name} <#{author_email}>" }
+ .join("\n")
+ end,
+ 'all_commits' => -> (merge_request, _, _) do
+ merge_request
+ .recent_commits
+ .without_merge_commits
+ .map do |commit|
+ if commit.safe_message&.bytesize&.>(100.kilobytes)
+ "* #{commit.title}\n\n-- Skipped commit body exceeding 100KiB in size."
+ else
+ "* #{commit.safe_message&.strip}"
+ end
+ end
+ .join("\n\n")
+ end
+ }.freeze
+
+ # A new merge request that is in the process of being created and hasn't
+ # been persisted to the database.
+ #
+ # Limit the placeholders to a subset of the available ones where the
+ # placeholders wouldn't make sense in context. Disallowed placeholders
+ # will be replaced with an empty string.
+ ALLOWED_NEW_MR_PLACEHOLDERS = %w[
+ source_branch
+ target_branch
+ first_commit
+ first_multiline_commit
+ co_authored_by
+ all_commits
+ ].freeze
+
+ PLACEHOLDERS_COMBINED_REGEX = /%{(#{Regexp.union(PLACEHOLDERS.keys)})}/.freeze
+
+ def replace_placeholders(message, allowed_placeholders: [], squash: false, keep_carriage_return: false)
+ # Convert CRLF to LF.
+ message = message.delete("\r") unless keep_carriage_return
+
+ used_variables = message.scan(PLACEHOLDERS_COMBINED_REGEX).map { |value| value[0] }.uniq
+ values = used_variables.to_h do |variable_name|
+ replacement = if allowed_placeholders.include?(variable_name)
+ PLACEHOLDERS[variable_name].call(merge_request, current_user, squash)
+ end
+
+ ["%{#{variable_name}}", replacement]
+ end
+ names_of_empty_variables = values.filter_map { |name, value| name if value.blank? }
+
+ # Remove lines that contain empty variable placeholder and nothing else.
+ if names_of_empty_variables.present?
+ # If there is blank line or EOF after it, remove blank line before it as well.
+ message = message.gsub(/\n\n#{Regexp.union(names_of_empty_variables)}(\n\n|\Z)/, '\1')
+ # Otherwise, remove only the line it is in.
+ message = message.gsub(/^#{Regexp.union(names_of_empty_variables)}\n/, '')
+ end
+ # Substitute all variables with their values.
+ message = message.gsub(Regexp.union(values.keys), values) if values.present?
+
+ message
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/metrics.rb b/lib/gitlab/metrics.rb
index 6d7ecb53ec3..e99761a0459 100644
--- a/lib/gitlab/metrics.rb
+++ b/lib/gitlab/metrics.rb
@@ -20,6 +20,10 @@ module Gitlab
status.to_i.between?(200, 499)
end
+ def self.server_error?(status)
+ status.to_i >= 500
+ end
+
# Tracks an event.
#
# See `Gitlab::Metrics::Transaction#add_event` for more details.
diff --git a/lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb b/lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb
index e2b43798b22..531e4079632 100644
--- a/lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb
+++ b/lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb
@@ -63,13 +63,11 @@ module Gitlab
end
def prometheus_metrics_attributes
- @prometheus_metrics_attributes ||= begin
- Dashboard::Transformers::Yml::V1::PrometheusMetrics.new(
- dashboard_hash,
+ @prometheus_metrics_attributes ||= Dashboard::Transformers::Yml::V1::PrometheusMetrics.new(
+ dashboard_hash,
project: project,
dashboard_path: dashboard_path
- ).execute
- end
+ ).execute
end
end
end
diff --git a/lib/gitlab/metrics/dashboard/validator.rb b/lib/gitlab/metrics/dashboard/validator.rb
index 1e8dc059968..57b4b5c068d 100644
--- a/lib/gitlab/metrics/dashboard/validator.rb
+++ b/lib/gitlab/metrics/dashboard/validator.rb
@@ -16,6 +16,8 @@ module Gitlab
errors.empty? || raise(errors.first)
end
+ private
+
def errors(content, schema_path = DASHBOARD_SCHEMA_PATH, dashboard_path: nil, project: nil)
Validator::Client
.new(content, schema_path, dashboard_path: dashboard_path, project: project)
diff --git a/lib/gitlab/metrics/global_search_slis.rb b/lib/gitlab/metrics/global_search_slis.rb
index 200c6eb4043..fc2e805047a 100644
--- a/lib/gitlab/metrics/global_search_slis.rb
+++ b/lib/gitlab/metrics/global_search_slis.rb
@@ -14,9 +14,6 @@ module Gitlab
def initialize_slis!
Gitlab::Metrics::Sli::Apdex.initialize_sli(:global_search, possible_labels)
-
- return unless Feature.enabled?(:global_search_error_rate_sli)
-
Gitlab::Metrics::Sli::ErrorRate.initialize_sli(:global_search, possible_labels)
end
@@ -28,8 +25,6 @@ module Gitlab
end
def record_error_rate(error:, search_type:, search_level:, search_scope:)
- return unless Feature.enabled?(:global_search_error_rate_sli)
-
Gitlab::Metrics::Sli::ErrorRate[:global_search].increment(
labels: labels(search_type: search_type, search_level: search_level, search_scope: search_scope),
error: error
diff --git a/lib/gitlab/metrics/rails_slis.rb b/lib/gitlab/metrics/rails_slis.rb
index 71da0085c8c..9fd4eec479e 100644
--- a/lib/gitlab/metrics/rails_slis.rb
+++ b/lib/gitlab/metrics/rails_slis.rb
@@ -6,6 +6,7 @@ module Gitlab
class << self
def initialize_request_slis!
Gitlab::Metrics::Sli::Apdex.initialize_sli(:rails_request, possible_request_labels)
+ initialize_rails_request_error_rate
Gitlab::Metrics::Sli::Apdex.initialize_sli(:graphql_query, possible_graphql_query_labels)
end
@@ -13,6 +14,10 @@ module Gitlab
Gitlab::Metrics::Sli::Apdex[:rails_request]
end
+ def request_error_rate
+ Gitlab::Metrics::Sli::ErrorRate[:rails_request]
+ end
+
def graphql_query_apdex
Gitlab::Metrics::Sli::Apdex[:graphql_query]
end
@@ -58,6 +63,12 @@ module Gitlab
}
end
end
+
+ def initialize_rails_request_error_rate
+ return unless Feature.enabled?(:gitlab_metrics_error_rate_sli, type: :development)
+
+ Gitlab::Metrics::Sli::ErrorRate.initialize_sli(:rails_request, possible_request_labels)
+ end
end
end
end
diff --git a/lib/gitlab/metrics/requests_rack_middleware.rb b/lib/gitlab/metrics/requests_rack_middleware.rb
index d7fe983c553..0172de8731d 100644
--- a/lib/gitlab/metrics/requests_rack_middleware.rb
+++ b/lib/gitlab/metrics/requests_rack_middleware.rb
@@ -75,15 +75,17 @@ module Gitlab
begin
status, headers, body = @app.call(env)
+ return [status, headers, body] if health_endpoint
- elapsed = ::Gitlab::Metrics::System.monotonic_time - started
-
- if !health_endpoint && ::Gitlab::Metrics.record_duration_for_status?(status)
+ urgency = urgency_for_env(env)
+ if ::Gitlab::Metrics.record_duration_for_status?(status)
+ elapsed = ::Gitlab::Metrics::System.monotonic_time - started
self.class.http_request_duration_seconds.observe({ method: method }, elapsed)
-
- record_apdex(env, elapsed)
+ record_apdex(urgency, elapsed)
end
+ record_error(urgency, status)
+
[status, headers, body]
rescue StandardError
self.class.rack_uncaught_errors_count.increment
@@ -115,15 +117,22 @@ module Gitlab
::Gitlab::ApplicationContext.current_context_attribute(:caller_id)
end
- def record_apdex(env, elapsed)
- urgency = urgency_for_env(env)
-
+ def record_apdex(urgency, elapsed)
Gitlab::Metrics::RailsSlis.request_apdex.increment(
labels: labels_from_context.merge(request_urgency: urgency.name),
success: elapsed < urgency.duration
)
end
+ def record_error(urgency, status)
+ return unless Feature.enabled?(:gitlab_metrics_error_rate_sli, type: :development)
+
+ Gitlab::Metrics::RailsSlis.request_error_rate.increment(
+ labels: labels_from_context.merge(request_urgency: urgency.name),
+ error: ::Gitlab::Metrics.server_error?(status)
+ )
+ end
+
def labels_from_context
{
feature_category: feature_category.presence || FEATURE_CATEGORY_DEFAULT,
diff --git a/lib/gitlab/metrics/subscribers/ldap.rb b/lib/gitlab/metrics/subscribers/ldap.rb
new file mode 100644
index 00000000000..9cac5f41090
--- /dev/null
+++ b/lib/gitlab/metrics/subscribers/ldap.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Metrics
+ module Subscribers
+ class Ldap < ActiveSupport::Subscriber
+ # This namespace is configured in the Net::LDAP library, and appears
+ # at the end of the event key, e.g. `open.net_ldap`
+ attach_to :net_ldap
+
+ COUNTER = :net_ldap_count
+ DURATION = :net_ldap_duration_s
+
+ # Assembled from methods that are instrumented inside Net::LDAP
+ OBSERVABLE_EVENTS = %i[
+ open
+ bind
+ add
+ modify
+ modify_password
+ rename
+ delete
+ search
+ ].freeze
+
+ class << self
+ # @return [Integer] the total number of LDAP requests
+ def count
+ Gitlab::SafeRequestStore[COUNTER].to_i
+ end
+
+ # @return [Float] the total duration spent on LDAP requests
+ def duration
+ Gitlab::SafeRequestStore[DURATION].to_f
+ end
+
+ # Used in Gitlab::InstrumentationHelper to merge the LDAP stats
+ # into the log output
+ #
+ # @return [Hash<Integer, Float>] a hash of the stored statistics
+ def payload
+ {
+ net_ldap_count: count,
+ net_ldap_duration_s: duration
+ }
+ end
+ end
+
+ # Called when an event is triggered in ActiveSupport::Notifications
+ #
+ # This method is aliased to the various events triggered by the
+ # Net::LDAP library, as the method will be called by those names
+ # when triggered.
+ #
+ # It stores statistics in the request for output to logs, and also
+ # resubmits the event data into Prometheus for monitoring purposes.
+ def observe_event(event)
+ add_to_request_store(event)
+ expose_metrics(event)
+ end
+
+ OBSERVABLE_EVENTS.each do |event|
+ alias_method event, :observe_event
+ end
+
+ private
+
+ def current_transaction
+ ::Gitlab::Metrics::WebTransaction.current || ::Gitlab::Metrics::BackgroundTransaction.current
+ end
+
+ # Track these events as statistics for the current requests, for logging purposes
+ def add_to_request_store(event)
+ return unless Gitlab::SafeRequestStore.active?
+
+ Gitlab::SafeRequestStore[COUNTER] = Gitlab::SafeRequestStore[COUNTER].to_i + 1
+ Gitlab::SafeRequestStore[DURATION] = Gitlab::SafeRequestStore[DURATION].to_f + event.duration.to_f
+ end
+
+ # Converts the observed events into Prometheus metrics
+ def expose_metrics(event)
+ return unless current_transaction
+
+ # event.name will be, for example, `search.net_ldap`
+ # and so we only want the first part, which is the
+ # true name of the event
+ labels = { name: event.name.split(".").first }
+
+ current_transaction.increment(:gitlab_net_ldap_total, 1, labels) do
+ docstring 'Net::LDAP calls'
+ label_keys labels.keys
+ end
+
+ current_transaction.observe(:gitlab_net_ldap_duration_seconds, event.duration, labels) do
+ docstring 'Net::LDAP time'
+ buckets [0.001, 0.01, 0.1, 1.0, 2.0, 5.0]
+ label_keys labels.keys
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/metrics/subscribers/rails_cache.rb b/lib/gitlab/metrics/subscribers/rails_cache.rb
index b5e087d107b..b12db9df66d 100644
--- a/lib/gitlab/metrics/subscribers/rails_cache.rb
+++ b/lib/gitlab/metrics/subscribers/rails_cache.rb
@@ -8,6 +8,17 @@ module Gitlab
class RailsCache < ActiveSupport::Subscriber
attach_to :active_support
+ def cache_read_multi(event)
+ observe(:read_multi, event.duration)
+
+ return unless current_transaction
+
+ current_transaction.observe(:gitlab_cache_read_multikey_count, event.payload[:key].size) do
+ buckets [10, 50, 100, 1000]
+ docstring 'Number of keys for mget in read_multi/fetch_multi'
+ end
+ end
+
def cache_read(event)
observe(:read, event.duration)
diff --git a/lib/gitlab/middleware/compressed_json.rb b/lib/gitlab/middleware/compressed_json.rb
index f66dfe44054..80916eab5ac 100644
--- a/lib/gitlab/middleware/compressed_json.rb
+++ b/lib/gitlab/middleware/compressed_json.rb
@@ -4,7 +4,18 @@ module Gitlab
module Middleware
class CompressedJson
COLLECTOR_PATH = '/api/v4/error_tracking/collector'
+ PACKAGES_PATH = %r{
+ \A/api/v4/ (?# prefix)
+ (?:projects/
+ (?<project_id>
+ .+ (?# at least one character)
+ )/
+ )? (?# projects segment)
+ packages/npm/-/npm/v1/security/
+ (?:(?:advisories/bulk)|(?:audits/quick))\z (?# end)
+ }xi.freeze
MAXIMUM_BODY_SIZE = 200.kilobytes.to_i
+ UNSAFE_CHARACTERS = %r{[!"#&'()*+,./:;<>=?@\[\]^`{}|~$]}xi.freeze
def initialize(app)
@app = app
@@ -60,7 +71,21 @@ module Gitlab
end
def match_path?(env)
- env['PATH_INFO'].start_with?((File.join(relative_url, COLLECTOR_PATH)))
+ env['PATH_INFO'].start_with?((File.join(relative_url, COLLECTOR_PATH))) ||
+ match_packages_path?(env)
+ end
+
+ def match_packages_path?(env)
+ match_data = env['PATH_INFO'].delete_prefix(relative_url).match(PACKAGES_PATH)
+ return false unless match_data
+
+ return true unless match_data[:project_id] # instance level endpoint was matched
+
+ url_encoded?(match_data[:project_id])
+ end
+
+ def url_encoded?(project_id)
+ project_id !~ UNSAFE_CHARACTERS
end
end
end
diff --git a/lib/gitlab/middleware/go.rb b/lib/gitlab/middleware/go.rb
index dcbb4557377..13f7ab36823 100644
--- a/lib/gitlab/middleware/go.rb
+++ b/lib/gitlab/middleware/go.rb
@@ -72,8 +72,8 @@ module Gitlab
"#{project_url}.git"
end
- meta_import_tag = tag :meta, name: 'go-import', content: "#{import_prefix} git #{repository_url}"
- meta_source_tag = tag :meta, name: 'go-source', content: "#{import_prefix} #{project_url} #{project_url}/-/tree/#{branch}{/dir} #{project_url}/-/blob/#{branch}{/dir}/{file}#L{line}"
+ meta_import_tag = tag.meta(name: 'go-import', content: "#{import_prefix} git #{repository_url}")
+ meta_source_tag = tag.meta(name: 'go-source', content: "#{import_prefix} #{project_url} #{project_url}/-/tree/#{branch}{/dir} #{project_url}/-/blob/#{branch}{/dir}/{file}#L{line}")
head_tag = content_tag :head, meta_import_tag + meta_source_tag
html_tag = content_tag :html, head_tag + body_tag
[html_tag, 200]
diff --git a/lib/gitlab/other_markup.rb b/lib/gitlab/other_markup.rb
index 0dd6b8a809c..2368ea3ad28 100644
--- a/lib/gitlab/other_markup.rb
+++ b/lib/gitlab/other_markup.rb
@@ -3,18 +3,34 @@
module Gitlab
# Parser/renderer for markups without other special support code.
module OtherMarkup
+ RENDER_TIMEOUT = 10.seconds
+
# Public: Converts the provided markup into HTML.
#
# input - the source text in a markup format
#
def self.render(file_name, input, context)
- html = GitHub::Markup.render(file_name, input)
- .force_encoding(input.encoding)
+ html = render_markup(file_name, input, context).force_encoding(input.encoding)
+
context[:pipeline] ||= :markup
html = Banzai.render(html, context)
-
html.html_safe
end
+
+ def self.render_markup(file_name, input, context)
+ Gitlab::RenderTimeout.timeout(foreground: RENDER_TIMEOUT) { GitHub::Markup.render(file_name, input) }
+ rescue Timeout::Error => e
+ class_name = name.demodulize
+ timeout_counter.increment(source: class_name)
+ Gitlab::ErrorTracking.track_exception(e, project_id: context[:project]&.id, class_name: class_name,
+ file_name: file_name)
+
+ ActionController::Base.helpers.simple_format(input)
+ end
+
+ def self.timeout_counter
+ Gitlab::Metrics.counter(:banzai_filter_timeouts_total, 'Count of the Banzai filters that time out')
+ end
end
end
diff --git a/lib/gitlab/pages/cache_control.rb b/lib/gitlab/pages/cache_control.rb
index 187d5f907e4..be39e52b342 100644
--- a/lib/gitlab/pages/cache_control.rb
+++ b/lib/gitlab/pages/cache_control.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'set'
+
module Gitlab
module Pages
class CacheControl
@@ -9,7 +11,9 @@ module Gitlab
# To avoid delivering expired deployment URL in the cached payload,
# use a longer expiration time in the deployment URL
DEPLOYMENT_EXPIRATION = (EXPIRE + 12.hours)
- CACHE_KEY_FORMAT = 'pages_domain_for_%{type}_%{id}_%{settings}'
+
+ SETTINGS_CACHE_KEY = 'pages_domain_for_%{type}_%{id}'
+ PAYLOAD_CACHE_KEY = '%{settings_cache_key}_%{settings_hash}'
class << self
def for_project(project_id)
@@ -29,29 +33,63 @@ module Gitlab
end
def cache_key
- strong_memoize(:cache_key) do
- CACHE_KEY_FORMAT % {
- type: @type,
- id: @id,
- settings: settings
- }
+ strong_memoize(:payload_cache_key) do
+ cache_settings_hash!
+
+ payload_cache_key_for(settings_hash)
end
end
+ # Invalidates the cache.
+ #
+ # Since rails nodes and sidekiq nodes have different application settings,
+ # and the invalidation happens in a sidekiq node, we have to use the
+ # cached settings hash to build the payload cache key to be invalidated.
def clear_cache
- Rails.cache.delete(cache_key)
+ keys = cached_settings_hashes
+ .map { |hash| payload_cache_key_for(hash) }
+ .push(settings_cache_key)
+
+ Rails.cache.delete_multi(keys)
end
private
- def settings
- values = ::Gitlab.config.pages.dup
+ # Since rails nodes and sidekiq nodes have different application settings,
+ # we cache the application settings hash when creating the payload cache
+ # so we can use these values to invalidate the cache in a sidekiq node later.
+ def cache_settings_hash!
+ cached = cached_settings_hashes.to_set
+ Rails.cache.write(settings_cache_key, cached.add(settings_hash))
+ end
+
+ def cached_settings_hashes
+ Rails.cache.read(settings_cache_key) || []
+ end
+
+ def payload_cache_key_for(settings_hash)
+ PAYLOAD_CACHE_KEY % {
+ settings_cache_key: settings_cache_key,
+ settings_hash: settings_hash
+ }
+ end
- values['app_settings'] = ::Gitlab::CurrentSettings.attributes.slice(
- 'force_pages_access_control'
- )
+ def settings_cache_key
+ strong_memoize(:settings_cache_key) do
+ SETTINGS_CACHE_KEY % { type: @type, id: @id }
+ end
+ end
- ::Digest::SHA256.hexdigest(values.inspect)
+ def settings_hash
+ strong_memoize(:settings_hash) do
+ values = ::Gitlab.config.pages.dup
+
+ values['app_settings'] = ::Gitlab::CurrentSettings.attributes.slice(
+ 'force_pages_access_control'
+ )
+
+ ::Digest::SHA256.hexdigest(values.inspect)
+ end
end
end
end
diff --git a/lib/gitlab/pagination/cursor_based_keyset.rb b/lib/gitlab/pagination/cursor_based_keyset.rb
index 2d9fb0a50fc..199ec16d4df 100644
--- a/lib/gitlab/pagination/cursor_based_keyset.rb
+++ b/lib/gitlab/pagination/cursor_based_keyset.rb
@@ -22,7 +22,7 @@ module Gitlab
def self.available?(cursor_based_request_context, relation)
available_for_type?(relation) &&
- order_satisfied?(relation, cursor_based_request_context)
+ order_satisfied?(relation, cursor_based_request_context)
end
def self.enforced_for_type?(relation)
diff --git a/lib/gitlab/pagination/offset_pagination.rb b/lib/gitlab/pagination/offset_pagination.rb
index 00304f48dc5..a98199dae2e 100644
--- a/lib/gitlab/pagination/offset_pagination.rb
+++ b/lib/gitlab/pagination/offset_pagination.rb
@@ -11,15 +11,17 @@ module Gitlab
@request_context = request_context
end
- def paginate(relation, exclude_total_headers: false, skip_default_order: false)
- paginate_with_limit_optimization(add_default_order(relation, skip_default_order: skip_default_order)).tap do |data|
+ def paginate(relation, exclude_total_headers: false, skip_default_order: false, without_count: false)
+ ordered_relation = add_default_order(relation, skip_default_order: skip_default_order)
+
+ paginate_with_limit_optimization(ordered_relation, without_count: without_count).tap do |data|
add_pagination_headers(data, exclude_total_headers)
end
end
private
- def paginate_with_limit_optimization(relation)
+ def paginate_with_limit_optimization(relation, without_count:)
pagination_data = if needs_pagination?(relation)
relation.page(params[:page]).per(params[:per_page])
else
@@ -28,8 +30,7 @@ module Gitlab
return pagination_data unless pagination_data.is_a?(ActiveRecord::Relation)
- limited_total_count = pagination_data.total_count_with_limit
- if limited_total_count > Kaminari::ActiveRecordRelationMethods::MAX_COUNT_LIMIT
+ if without_count || exceeeds_count?(pagination_data)
# The call to `total_count_with_limit` memoizes `@arel` because of a call to `references_eager_loaded_tables?`
# We need to call `reset` because `without_count` relies on `@arel` being unmemoized
pagination_data.reset.without_count
@@ -78,6 +79,12 @@ module Gitlab
# Ensure there is in total at least 1 page
[paginated_data.total_pages, 1].max
end
+
+ def exceeeds_count?(paginated_data)
+ limited_total_count = paginated_data.total_count_with_limit
+
+ limited_total_count > Kaminari::ActiveRecordRelationMethods::MAX_COUNT_LIMIT
+ end
end
end
end
diff --git a/lib/gitlab/patch/prependable.rb b/lib/gitlab/patch/prependable.rb
index 1ed341e1c26..b974c0b2c7f 100644
--- a/lib/gitlab/patch/prependable.rb
+++ b/lib/gitlab/patch/prependable.rb
@@ -26,7 +26,7 @@ module Gitlab
# https://github.com/rails/rails/pull/42067
#
# Let's keep our own implementation, until the issue is fixed
- Module.instance_method(:prepend_features).bind(self).call(base)
+ Module.instance_method(:prepend_features).bind_call(self, base)
if const_defined?(:ClassMethods)
klass_methods = const_get(:ClassMethods, false)
diff --git a/lib/gitlab/phabricator_import/project_creator.rb b/lib/gitlab/phabricator_import/project_creator.rb
index c842798ca74..4de9eaa9500 100644
--- a/lib/gitlab/phabricator_import/project_creator.rb
+++ b/lib/gitlab/phabricator_import/project_creator.rb
@@ -55,13 +55,11 @@ module Gitlab
end
def project_feature_attributes
+ # everything disabled except for issues
@project_features_attributes ||=
- begin
- # everything disabled except for issues
- ProjectFeature::FEATURES.to_h do |feature|
- [ProjectFeature.access_level_attribute(feature), ProjectFeature::DISABLED]
- end.merge(ProjectFeature.access_level_attribute(:issues) => ProjectFeature::ENABLED)
- end
+ ProjectFeature::FEATURES.to_h do |feature|
+ [ProjectFeature.access_level_attribute(feature), ProjectFeature::DISABLED]
+ end.merge(ProjectFeature.access_level_attribute(:issues) => ProjectFeature::ENABLED)
end
def import_data
diff --git a/lib/gitlab/process_management.rb b/lib/gitlab/process_management.rb
index f8a1a3a97de..89ffd71c2d8 100644
--- a/lib/gitlab/process_management.rb
+++ b/lib/gitlab/process_management.rb
@@ -40,15 +40,6 @@ module Gitlab
pids.each { |pid| signal(pid, signal) }
end
- # Waits for the given process to complete using a separate thread.
- def self.wait_async(pid)
- Thread.new do
- Process.wait(pid)
- rescue StandardError
- nil # There is no reason to return `Errno::ECHILD` if it catches a `TypeError`
- end
- end
-
# Returns true if all the processes are alive.
def self.all_alive?(pids)
pids.each do |pid|
diff --git a/lib/gitlab/process_supervisor.rb b/lib/gitlab/process_supervisor.rb
index 714034f043d..09e923d1449 100644
--- a/lib/gitlab/process_supervisor.rb
+++ b/lib/gitlab/process_supervisor.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require_relative './daemon'
+
module Gitlab
# Given a set of process IDs, the supervisor can monitor processes
# for being alive and invoke a callback if some or all should go away.
diff --git a/lib/gitlab/profiler.rb b/lib/gitlab/profiler.rb
index f8a85f693bc..5af06e82c55 100644
--- a/lib/gitlab/profiler.rb
+++ b/lib/gitlab/profiler.rb
@@ -123,18 +123,18 @@ module Gitlab
def self.with_custom_logger(logger)
original_colorize_logging = ActiveSupport::LogSubscriber.colorize_logging
- original_activerecord_logger = ApplicationRecord.logger
+ original_activerecord_logger = ActiveRecord::Base.logger
original_actioncontroller_logger = ActionController::Base.logger
if logger
ActiveSupport::LogSubscriber.colorize_logging = false
- ApplicationRecord.logger = logger
+ ActiveRecord::Base.logger = logger
ActionController::Base.logger = logger
end
yield.tap do
ActiveSupport::LogSubscriber.colorize_logging = original_colorize_logging
- ApplicationRecord.logger = original_activerecord_logger
+ ActiveRecord::Base.logger = original_activerecord_logger
ActionController::Base.logger = original_actioncontroller_logger
end
end
diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb
index 8cc96970ebd..13718e63b25 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -125,17 +125,15 @@ module Gitlab
def wiki_blobs(limit: count_limit)
return [] unless Ability.allowed?(@current_user, :read_wiki, @project)
- @wiki_blobs ||= begin
- if project.wiki_enabled? && query.present?
- if project.wiki.empty?
- []
- else
- Gitlab::WikiFileFinder.new(project, repository_wiki_ref).find(query, content_match_cutoff: limit)
- end
- else
- []
- end
- end
+ @wiki_blobs ||= if project.wiki_enabled? && query.present?
+ if project.wiki.empty?
+ []
+ else
+ Gitlab::WikiFileFinder.new(project, repository_wiki_ref).find(query, content_match_cutoff: limit)
+ end
+ else
+ []
+ end
end
def notes
@@ -195,3 +193,5 @@ module Gitlab
end
end
end
+
+Gitlab::ProjectSearchResults.prepend_mod_with('Gitlab::ProjectSearchResults')
diff --git a/lib/gitlab/project_template.rb b/lib/gitlab/project_template.rb
index 51a5bedc44b..9bc0001be81 100644
--- a/lib/gitlab/project_template.rb
+++ b/lib/gitlab/project_template.rb
@@ -60,6 +60,7 @@ module Gitlab
ProjectTemplate.new('dotnetcore', '.NET Core', _('A .NET Core console application template, customizable for any .NET Core project'), 'https://gitlab.com/gitlab-org/project-templates/dotnetcore', 'illustrations/third-party-logos/dotnet.svg'),
ProjectTemplate.new('android', 'Android', _('A ready-to-go template for use with Android apps'), 'https://gitlab.com/gitlab-org/project-templates/android', 'illustrations/logos/android.svg'),
ProjectTemplate.new('gomicro', 'Go Micro', _('Go Micro is a framework for micro service development'), 'https://gitlab.com/gitlab-org/project-templates/go-micro', 'illustrations/logos/gomicro.svg'),
+ ProjectTemplate.new('bridgetown', 'Pages/Bridgetown', _('Everything you need to create a GitLab Pages site using Bridgetown'), 'https://gitlab.com/gitlab-org/project-templates/bridgetown'),
ProjectTemplate.new('gatsby', 'Pages/Gatsby', _('Everything you need to create a GitLab Pages site using Gatsby'), 'https://gitlab.com/pages/gatsby', 'illustrations/third-party-logos/gatsby.svg'),
ProjectTemplate.new('hugo', 'Pages/Hugo', _('Everything you need to create a GitLab Pages site using Hugo'), 'https://gitlab.com/pages/hugo', 'illustrations/logos/hugo.svg'),
ProjectTemplate.new('pelican', 'Pages/Pelican', _('Everything you need to create a GitLab Pages site using Pelican'), 'https://gitlab.com/pages/pelican', 'illustrations/third-party-logos/pelican.svg'),
@@ -79,7 +80,8 @@ module Gitlab
ProjectTemplate.new('tencent_serverless_framework', 'Tencent Serverless Framework/NextjsSSR', _('A project boilerplate for Tencent Serverless Framework that uses Next.js SSR'), 'https://gitlab.com/gitlab-org/project-templates/nextjsssr_demo', 'illustrations/logos/tencent_serverless_framework.svg'),
ProjectTemplate.new('jsonnet', 'Jsonnet for Dynamic Child Pipelines', _('An example showing how to use Jsonnet with GitLab dynamic child pipelines'), 'https://gitlab.com/gitlab-org/project-templates/jsonnet'),
ProjectTemplate.new('cluster_management', 'GitLab Cluster Management', _('An example project for managing Kubernetes clusters integrated with GitLab'), 'https://gitlab.com/gitlab-org/project-templates/cluster-management'),
- ProjectTemplate.new('kotlin_native_linux', 'Kotlin Native Linux', _('A basic template for developing Linux programs using Kotlin Native'), 'https://gitlab.com/gitlab-org/project-templates/kotlin-native-linux')
+ ProjectTemplate.new('kotlin_native_linux', 'Kotlin Native Linux', _('A basic template for developing Linux programs using Kotlin Native'), 'https://gitlab.com/gitlab-org/project-templates/kotlin-native-linux'),
+ ProjectTemplate.new('typo3_distribution', 'TYPO3 Distribution', _('A template for starting a new TYPO3 project'), 'https://gitlab.com/ochorocho/typo3-distribution', 'illustrations/logos/typo3.svg')
].freeze
end
# rubocop:enable Metrics/AbcSize
diff --git a/lib/gitlab/prometheus_client.rb b/lib/gitlab/prometheus_client.rb
index dda28ffdf90..6a5613ddd98 100644
--- a/lib/gitlab/prometheus_client.rb
+++ b/lib/gitlab/prometheus_client.rb
@@ -150,7 +150,7 @@ module Gitlab
end
def get(path, args)
- Gitlab::HTTP.get(path, { query: args }.merge(http_options) )
+ Gitlab::HTTP.get(path, { query: args }.merge(http_options))
rescue *Gitlab::HTTP::HTTP_ERRORS => e
raise PrometheusClient::ConnectionError, e.message
end
diff --git a/lib/gitlab/quick_actions/issuable_actions.rb b/lib/gitlab/quick_actions/issuable_actions.rb
index 0b37c80dc5f..a12457d89c9 100644
--- a/lib/gitlab/quick_actions/issuable_actions.rb
+++ b/lib/gitlab/quick_actions/issuable_actions.rb
@@ -84,7 +84,7 @@ module Gitlab
current_user.can?(:"set_#{quick_action_target.to_ability_name}_metadata", quick_action_target) &&
find_labels.any?
end
- command :label do |labels_param|
+ command :label, :labels do |labels_param|
run_label_command(labels: find_labels(labels_param), command: :label, updates_key: :add_label_ids)
end
diff --git a/lib/gitlab/quick_actions/issue_actions.rb b/lib/gitlab/quick_actions/issue_actions.rb
index e74c58e45b1..14e9e66e037 100644
--- a/lib/gitlab/quick_actions/issue_actions.rb
+++ b/lib/gitlab/quick_actions/issue_actions.rb
@@ -80,7 +80,7 @@ module Gitlab
desc { _('Mark this issue as a duplicate of another issue') }
explanation do |duplicate_reference|
- _("Marks this issue as a duplicate of %{duplicate_reference}.") % { duplicate_reference: duplicate_reference }
+ _("Closes this issue. Marks as related to, and a duplicate of, %{duplicate_reference}.") % { duplicate_reference: duplicate_reference }
end
params '#issue'
types Issue
@@ -94,7 +94,7 @@ module Gitlab
if canonical_issue.present?
@updates[:canonical_issue_id] = canonical_issue.id
- message = _("Marked this issue as a duplicate of %{duplicate_param}.") % { duplicate_param: duplicate_param }
+ message = _("Closed this issue. Marked as related to, and a duplicate of, %{duplicate_param}.") % { duplicate_param: duplicate_param }
else
message = _('Failed to mark this issue as a duplicate because referenced issue was not found.')
end
diff --git a/lib/gitlab/quick_actions/issue_and_merge_request_actions.rb b/lib/gitlab/quick_actions/issue_and_merge_request_actions.rb
index 8b1ff5d298a..e549ee2e43a 100644
--- a/lib/gitlab/quick_actions/issue_and_merge_request_actions.rb
+++ b/lib/gitlab/quick_actions/issue_and_merge_request_actions.rb
@@ -92,7 +92,7 @@ module Gitlab
types Issue, MergeRequest
condition do
quick_action_target.supports_milestone? &&
- current_user.can?(:"set_#{quick_action_target.to_ability_name}_metadata", quick_action_target) &&
+ current_user.can?(:"set_#{quick_action_target.to_ability_name}_metadata", quick_action_target) &&
find_milestones(project, state: 'active').any?
end
parse_params do |milestone_param|
@@ -156,7 +156,7 @@ module Gitlab
types Issue, MergeRequest
condition do
quick_action_target.supports_time_tracking? &&
- current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project)
+ current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project)
end
parse_params do |raw_duration|
Gitlab::TimeTrackingFormatter.parse(raw_duration)
@@ -179,7 +179,7 @@ module Gitlab
types Issue, MergeRequest
condition do
quick_action_target.supports_time_tracking? &&
- current_user.can?(:"admin_#{quick_action_target.to_ability_name}", quick_action_target)
+ current_user.can?(:"admin_#{quick_action_target.to_ability_name}", quick_action_target)
end
parse_params do |raw_time_date|
Gitlab::QuickActions::SpendTimeAndDateSeparator.new(raw_time_date).execute
diff --git a/lib/gitlab/rack_attack.rb b/lib/gitlab/rack_attack.rb
index f5fb6b5af3d..bedbe9c0bff 100644
--- a/lib/gitlab/rack_attack.rb
+++ b/lib/gitlab/rack_attack.rb
@@ -49,7 +49,7 @@ module Gitlab
#
# - Retry-After: the remaining duration in seconds until the quota is
# reset. This is a standardized HTTP header:
- # https://tools.ietf.org/html/rfc7231#page-69
+ # https://www.rfc-editor.org/rfc/rfc7231#page-69
#
# - RateLimit-Reset: the point of time that the request quota is reset, in Unix time
#
diff --git a/lib/gitlab/rack_attack/request.rb b/lib/gitlab/rack_attack/request.rb
index 08a5ddb6ad1..d7abacb5b67 100644
--- a/lib/gitlab/rack_attack/request.rb
+++ b/lib/gitlab/rack_attack/request.rb
@@ -79,96 +79,96 @@ module Gitlab
def throttle_unauthenticated_api?
api_request? &&
- !should_be_skipped? &&
- !frontend_request? &&
- !throttle_unauthenticated_packages_api? &&
- !throttle_unauthenticated_files_api? &&
- !throttle_unauthenticated_deprecated_api? &&
- Gitlab::Throttle.settings.throttle_unauthenticated_api_enabled &&
- unauthenticated?
+ !should_be_skipped? &&
+ !frontend_request? &&
+ !throttle_unauthenticated_packages_api? &&
+ !throttle_unauthenticated_files_api? &&
+ !throttle_unauthenticated_deprecated_api? &&
+ Gitlab::Throttle.settings.throttle_unauthenticated_api_enabled &&
+ unauthenticated?
end
def throttle_unauthenticated_web?
(web_request? || frontend_request?) &&
- !should_be_skipped? &&
- # TODO: Column will be renamed in https://gitlab.com/gitlab-org/gitlab/-/issues/340031
- Gitlab::Throttle.settings.throttle_unauthenticated_enabled &&
- unauthenticated?
+ !should_be_skipped? &&
+ # TODO: Column will be renamed in https://gitlab.com/gitlab-org/gitlab/-/issues/340031
+ Gitlab::Throttle.settings.throttle_unauthenticated_enabled &&
+ unauthenticated?
end
def throttle_authenticated_api?
api_request? &&
- !frontend_request? &&
- !throttle_authenticated_packages_api? &&
- !throttle_authenticated_files_api? &&
- !throttle_authenticated_deprecated_api? &&
- Gitlab::Throttle.settings.throttle_authenticated_api_enabled
+ !frontend_request? &&
+ !throttle_authenticated_packages_api? &&
+ !throttle_authenticated_files_api? &&
+ !throttle_authenticated_deprecated_api? &&
+ Gitlab::Throttle.settings.throttle_authenticated_api_enabled
end
def throttle_authenticated_web?
(web_request? || frontend_request?) &&
- !throttle_authenticated_git_lfs? &&
- Gitlab::Throttle.settings.throttle_authenticated_web_enabled
+ !throttle_authenticated_git_lfs? &&
+ Gitlab::Throttle.settings.throttle_authenticated_web_enabled
end
def throttle_unauthenticated_protected_paths?
post? &&
- !should_be_skipped? &&
- protected_path? &&
- Gitlab::Throttle.protected_paths_enabled? &&
- unauthenticated?
+ !should_be_skipped? &&
+ protected_path? &&
+ Gitlab::Throttle.protected_paths_enabled? &&
+ unauthenticated?
end
def throttle_authenticated_protected_paths_api?
post? &&
- api_request? &&
- protected_path? &&
- Gitlab::Throttle.protected_paths_enabled?
+ api_request? &&
+ protected_path? &&
+ Gitlab::Throttle.protected_paths_enabled?
end
def throttle_authenticated_protected_paths_web?
post? &&
- web_request? &&
- protected_path? &&
- Gitlab::Throttle.protected_paths_enabled?
+ web_request? &&
+ protected_path? &&
+ Gitlab::Throttle.protected_paths_enabled?
end
def throttle_unauthenticated_packages_api?
packages_api_path? &&
- Gitlab::Throttle.settings.throttle_unauthenticated_packages_api_enabled &&
- unauthenticated?
+ Gitlab::Throttle.settings.throttle_unauthenticated_packages_api_enabled &&
+ unauthenticated?
end
def throttle_authenticated_packages_api?
packages_api_path? &&
- Gitlab::Throttle.settings.throttle_authenticated_packages_api_enabled
+ Gitlab::Throttle.settings.throttle_authenticated_packages_api_enabled
end
def throttle_authenticated_git_lfs?
git_lfs_path? &&
- Gitlab::Throttle.settings.throttle_authenticated_git_lfs_enabled
+ Gitlab::Throttle.settings.throttle_authenticated_git_lfs_enabled
end
def throttle_unauthenticated_files_api?
files_api_path? &&
- Gitlab::Throttle.settings.throttle_unauthenticated_files_api_enabled &&
- unauthenticated?
+ Gitlab::Throttle.settings.throttle_unauthenticated_files_api_enabled &&
+ unauthenticated?
end
def throttle_authenticated_files_api?
files_api_path? &&
- Gitlab::Throttle.settings.throttle_authenticated_files_api_enabled
+ Gitlab::Throttle.settings.throttle_authenticated_files_api_enabled
end
def throttle_unauthenticated_deprecated_api?
deprecated_api_request? &&
- Gitlab::Throttle.settings.throttle_unauthenticated_deprecated_api_enabled &&
- unauthenticated?
+ Gitlab::Throttle.settings.throttle_unauthenticated_deprecated_api_enabled &&
+ unauthenticated?
end
def throttle_authenticated_deprecated_api?
deprecated_api_request? &&
- Gitlab::Throttle.settings.throttle_authenticated_deprecated_api_enabled
+ Gitlab::Throttle.settings.throttle_authenticated_deprecated_api_enabled
end
private
diff --git a/lib/gitlab/redis/multi_store.rb b/lib/gitlab/redis/multi_store.rb
index 12cb1fc6153..4f58bee49d0 100644
--- a/lib/gitlab/redis/multi_store.rb
+++ b/lib/gitlab/redis/multi_store.rb
@@ -10,6 +10,7 @@ module Gitlab
'Value not found on the redis primary store. Read from the redis secondary store successful.'
end
end
+
class PipelinedDiffError < StandardError
def initialize(result_primary, result_secondary)
@result_primary = result_primary
@@ -22,6 +23,7 @@ module Gitlab
"Result from the secondary: #{@result_secondary.inspect}."
end
end
+
class MethodMissingError < StandardError
def message
'Method missing. Falling back to execute method on the redis secondary store.'
diff --git a/lib/gitlab/redis/wrapper.rb b/lib/gitlab/redis/wrapper.rb
index 75dbccb965d..0e5389dc995 100644
--- a/lib/gitlab/redis/wrapper.rb
+++ b/lib/gitlab/redis/wrapper.rb
@@ -144,11 +144,20 @@ module Gitlab
def redis_store_options
config = raw_config_hash
+ config[:instrumentation_class] ||= self.class.instrumentation_class
+
+ if config[:cluster].present?
+ config[:db] = 0 # Redis Cluster only supports db 0
+ config
+ else
+ parse_redis_url(config)
+ end
+ end
+
+ def parse_redis_url(config)
redis_url = config.delete(:url)
redis_uri = URI.parse(redis_url)
- config[:instrumentation_class] ||= self.class.instrumentation_class
-
if redis_uri.scheme == 'unix'
# Redis::Store does not handle Unix sockets well, so let's do it for them
config[:path] = redis_uri.path
@@ -178,7 +187,7 @@ module Gitlab
{ url: '' }
end
- if config_hash[:url].blank?
+ if config_hash[:url].blank? && config_hash[:cluster].blank?
config_hash[:url] = legacy_fallback_urls[self.class.store_name] || legacy_fallback_urls[self.class.config_fallback.store_name]
end
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index c5798bec0d7..540394f04bd 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -4,7 +4,8 @@ module Gitlab
# Extract possible GFM references from an arbitrary String for further processing.
class ReferenceExtractor < Banzai::ReferenceExtractor
REFERABLES = %i(user issue label milestone mentioned_user mentioned_group mentioned_project
- merge_request snippet commit commit_range directly_addressed_user epic iteration vulnerability).freeze
+ merge_request snippet commit commit_range directly_addressed_user epic iteration vulnerability
+ alert).freeze
attr_accessor :project, :current_user, :author
def initialize(project, current_user = nil)
@@ -71,7 +72,7 @@ module Gitlab
return @pattern if @pattern
patterns = REFERABLES.map do |type|
- Banzai::ReferenceParser[type].reference_type.to_s.classify.constantize.try(:reference_pattern)
+ Banzai::ReferenceParser[type].reference_class.try(:reference_pattern)
end.uniq
@pattern = Regexp.union(patterns.compact)
diff --git a/lib/gitlab/repository_size_error_message.rb b/lib/gitlab/repository_size_error_message.rb
index 8da840779c9..f5d82e61187 100644
--- a/lib/gitlab/repository_size_error_message.rb
+++ b/lib/gitlab/repository_size_error_message.rb
@@ -6,7 +6,7 @@ module Gitlab
delegate :current_size, :limit, :exceeded_size, :additional_repo_storage_available?, to: :@checker
- # @param checher [RepositorySizeChecker]
+ # @param checker [RepositorySizeChecker]
def initialize(checker)
@checker = checker
end
diff --git a/lib/gitlab/safe_request_store.rb b/lib/gitlab/safe_request_store.rb
index 664afd1cc21..203d7d10532 100644
--- a/lib/gitlab/safe_request_store.rb
+++ b/lib/gitlab/safe_request_store.rb
@@ -40,7 +40,7 @@ module Gitlab
def self.delete_if(&block)
return unless RequestStore.active?
- storage.delete_if { |k, v| block.call(k) }
+ storage.delete_if { |k, v| yield(k) }
end
end
end
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index bc59d4ce943..ba822955133 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -24,9 +24,7 @@ module Gitlab
#
# @return [String] secret token
def secret_token
- @secret_token ||= begin
- File.read(Gitlab.config.gitlab_shell.secret_file).chomp
- end
+ @secret_token ||= File.read(Gitlab.config.gitlab_shell.secret_file).chomp
end
# Ensure gitlab shell has a secret token stored in the secret_file
diff --git a/lib/gitlab/sidekiq_daemon/memory_killer.rb b/lib/gitlab/sidekiq_daemon/memory_killer.rb
index d5227e7a007..4bf9fd8470a 100644
--- a/lib/gitlab/sidekiq_daemon/memory_killer.rb
+++ b/lib/gitlab/sidekiq_daemon/memory_killer.rb
@@ -35,6 +35,7 @@ module Gitlab
@enabled = true
@metrics = init_metrics
+ @sidekiq_daemon_monitor = Gitlab::SidekiqDaemon::Monitor.instance
end
private
@@ -78,7 +79,7 @@ module Gitlab
rescue StandardError => e
log_exception(e, __method__)
rescue Exception => e # rubocop:disable Lint/RescueException
- log_exception(e, __method__ )
+ log_exception(e, __method__)
raise e
end
end
@@ -188,22 +189,17 @@ module Gitlab
def increment_worker_counters(running_jobs, deadline_exceeded)
running_jobs.each do |job|
- @metrics[:sidekiq_memory_killer_running_jobs].increment( { worker_class: job[:worker_class], deadline_exceeded: deadline_exceeded } )
+ @metrics[:sidekiq_memory_killer_running_jobs].increment({ worker_class: job[:worker_class], deadline_exceeded: deadline_exceeded })
end
end
def fetch_running_jobs
- jobs = []
- Gitlab::SidekiqDaemon::Monitor.instance.jobs_mutex.synchronize do
- jobs = Gitlab::SidekiqDaemon::Monitor.instance.jobs.map do |jid, job|
- {
- jid: jid,
- worker_class: job[:worker_class].name
- }
- end
+ @sidekiq_daemon_monitor.jobs.map do |jid, job|
+ {
+ jid: jid,
+ worker_class: job[:worker_class].name
+ }
end
-
- jobs
end
def out_of_range_description(rss, hard_limit, soft_limit, deadline_exceeded)
@@ -269,10 +265,8 @@ module Gitlab
end
def rss_increase_by_jobs
- Gitlab::SidekiqDaemon::Monitor.instance.jobs_mutex.synchronize do
- Gitlab::SidekiqDaemon::Monitor.instance.jobs.sum do |job|
- rss_increase_by_job(job)
- end
+ @sidekiq_daemon_monitor.jobs.sum do |_, job|
+ rss_increase_by_job(job)
end
end
@@ -297,7 +291,7 @@ module Gitlab
end
def any_jobs?
- Gitlab::SidekiqDaemon::Monitor.instance.jobs.any?
+ @sidekiq_daemon_monitor.jobs.any?
end
end
end
diff --git a/lib/gitlab/sidekiq_daemon/monitor.rb b/lib/gitlab/sidekiq_daemon/monitor.rb
index 655e95c82d3..125402c1e4b 100644
--- a/lib/gitlab/sidekiq_daemon/monitor.rb
+++ b/lib/gitlab/sidekiq_daemon/monitor.rb
@@ -15,9 +15,6 @@ module Gitlab
# that should not be caught by application
CancelledError = Class.new(Exception) # rubocop:disable Lint/InheritException
- attr_reader :jobs
- attr_reader :jobs_mutex
-
def initialize
super
@@ -31,8 +28,8 @@ module Gitlab
end
def within_job(worker_class, jid, queue)
- jobs_mutex.synchronize do
- jobs[jid] = { worker_class: worker_class, thread: Thread.current, started_at: Gitlab::Metrics::System.monotonic_time }
+ @jobs_mutex.synchronize do
+ @jobs[jid] = { worker_class: worker_class, thread: Thread.current, started_at: Gitlab::Metrics::System.monotonic_time }
end
if cancelled?(jid)
@@ -48,8 +45,8 @@ module Gitlab
yield
ensure
- jobs_mutex.synchronize do
- jobs.delete(jid)
+ @jobs_mutex.synchronize do
+ @jobs.delete(jid)
end
end
@@ -65,6 +62,12 @@ module Gitlab
end
end
+ def jobs
+ @jobs_mutex.synchronize do
+ @jobs.dup
+ end
+ end
+
private
def run_thread
@@ -166,14 +169,14 @@ module Gitlab
# This is why it passes thread in block,
# to ensure that we do process this thread
def find_thread_unsafe(jid)
- jobs.dig(jid, :thread)
+ @jobs.dig(jid, :thread)
end
def find_thread_with_lock(jid)
# don't try to lock if we cannot find the thread
return unless find_thread_unsafe(jid)
- jobs_mutex.synchronize do
+ @jobs_mutex.synchronize do
find_thread_unsafe(jid).tap do |thread|
yield(thread) if thread
end
diff --git a/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb b/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb
index 357e9d41187..4f7cd340461 100644
--- a/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb
+++ b/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb
@@ -253,7 +253,7 @@ module Gitlab
def with_redis
if Feature.enabled?(:use_primary_and_secondary_stores_for_duplicate_jobs) ||
- Feature.enabled?(:use_primary_store_as_default_for_duplicate_jobs)
+ Feature.enabled?(:use_primary_store_as_default_for_duplicate_jobs)
# TODO: Swap for Gitlab::Redis::SharedState after store transition
# https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/923
Gitlab::Redis::DuplicateJobs.with { |redis| yield redis }
diff --git a/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies.rb b/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies.rb
index 63e8bee4443..fc6a849da95 100644
--- a/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies.rb
+++ b/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies.rb
@@ -7,9 +7,9 @@ module Gitlab
UnknownStrategyError = Class.new(StandardError)
STRATEGIES = {
- until_executing: UntilExecuting,
- until_executed: UntilExecuted,
- none: None
+ until_executing: ::Gitlab::SidekiqMiddleware::DuplicateJobs::Strategies::UntilExecuting,
+ until_executed: ::Gitlab::SidekiqMiddleware::DuplicateJobs::Strategies::UntilExecuted,
+ none: ::Gitlab::SidekiqMiddleware::DuplicateJobs::Strategies::None
}.freeze
def self.for(name)
diff --git a/lib/gitlab/sidekiq_status.rb b/lib/gitlab/sidekiq_status.rb
index 17234bdf519..778d278146d 100644
--- a/lib/gitlab/sidekiq_status.rb
+++ b/lib/gitlab/sidekiq_status.rb
@@ -120,7 +120,7 @@ module Gitlab
def self.with_redis
if Feature.enabled?(:use_primary_and_secondary_stores_for_sidekiq_status) ||
- Feature.enabled?(:use_primary_store_as_default_for_sidekiq_status)
+ Feature.enabled?(:use_primary_store_as_default_for_sidekiq_status)
# TODO: Swap for Gitlab::Redis::SharedState after store transition
# https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/923
Gitlab::Redis::SidekiqStatus.with { |redis| yield redis }
diff --git a/lib/gitlab/slash_commands/application_help.rb b/lib/gitlab/slash_commands/application_help.rb
index bfdb65a816d..94abc8b4508 100644
--- a/lib/gitlab/slash_commands/application_help.rb
+++ b/lib/gitlab/slash_commands/application_help.rb
@@ -3,6 +3,11 @@
module Gitlab
module SlashCommands
class ApplicationHelp < BaseCommand
+ def initialize(project, params)
+ @project = project
+ @params = params
+ end
+
def execute
Gitlab::SlashCommands::Presenters::Help
.new(project, commands, params)
@@ -16,11 +21,7 @@ module Gitlab
end
def commands
- Gitlab::SlashCommands::Command.new(
- project,
- chat_name,
- params
- ).commands
+ Gitlab::SlashCommands::Command.commands
end
end
end
diff --git a/lib/gitlab/slash_commands/command.rb b/lib/gitlab/slash_commands/command.rb
index 265eda46489..f8b55f1a91d 100644
--- a/lib/gitlab/slash_commands/command.rb
+++ b/lib/gitlab/slash_commands/command.rb
@@ -3,7 +3,7 @@
module Gitlab
module SlashCommands
class Command < BaseCommand
- def commands
+ def self.commands
commands = [
Gitlab::SlashCommands::IssueShow,
Gitlab::SlashCommands::IssueNew,
@@ -15,7 +15,7 @@ module Gitlab
Gitlab::SlashCommands::Run
]
- if Feature.enabled?(:incident_declare_slash_command, current_user)
+ if Feature.enabled?(:incident_declare_slash_command)
commands << Gitlab::SlashCommands::IncidentManagement::IncidentNew
end
@@ -50,7 +50,7 @@ module Gitlab
private
def available_commands
- commands.keep_if do |klass|
+ self.class.commands.keep_if do |klass|
klass.available?(project)
end
end
diff --git a/lib/gitlab/slash_commands/deploy.rb b/lib/gitlab/slash_commands/deploy.rb
index 9fcefd99f81..16a4875be91 100644
--- a/lib/gitlab/slash_commands/deploy.rb
+++ b/lib/gitlab/slash_commands/deploy.rb
@@ -54,7 +54,7 @@ module Gitlab
return unless environment
actions = environment.actions_for(to).select do |action|
- action.starts_environment?
+ action.deployment_job?
end
if actions.many?
diff --git a/lib/gitlab/sql/pattern.rb b/lib/gitlab/sql/pattern.rb
index d13ccde8576..cd5587bbaef 100644
--- a/lib/gitlab/sql/pattern.rb
+++ b/lib/gitlab/sql/pattern.rb
@@ -51,16 +51,14 @@ module Gitlab
if words.any?
words.map { |word| arel_column.matches(to_pattern(word, use_minimum_char_limit: use_minimum_char_limit)) }.reduce(:and)
- else
+ elsif lower_exact_match
# No words of at least 3 chars, but we can search for an exact
# case insensitive match with the query as a whole
- if lower_exact_match
- Arel::Nodes::NamedFunction
+ Arel::Nodes::NamedFunction
.new('LOWER', [arel_column])
.eq(query)
- else
- arel_column.matches(sanitize_sql_like(query))
- end
+ else
+ arel_column.matches(sanitize_sql_like(query))
end
end
diff --git a/lib/gitlab/ssh/signature.rb b/lib/gitlab/ssh/signature.rb
index 3b4df9a8d0c..a654d5b2ff1 100644
--- a/lib/gitlab/ssh/signature.rb
+++ b/lib/gitlab/ssh/signature.rb
@@ -9,6 +9,8 @@ module Gitlab
class Signature
include Gitlab::Utils::StrongMemoize
+ GIT_NAMESPACE = 'git'
+
def initialize(signature_text, signed_text, committer_email)
@signature_text = signature_text
@signed_text = signed_text
@@ -18,11 +20,16 @@ module Gitlab
def verification_status
strong_memoize(:verification_status) do
next :unverified unless all_attributes_present?
- next :unverified unless valid_signature_blob? && committer
+ next :unverified unless valid_signature_blob?
next :unknown_key unless signed_by_key
+ next :other_user unless committer
next :other_user unless signed_by_key.user == committer
- :verified
+ if signed_by_user_email_verified?
+ :verified
+ else
+ :unverified
+ end
end
end
@@ -30,7 +37,7 @@ module Gitlab
strong_memoize(:signed_by_key) do
next unless key_fingerprint
- Key.find_by_fingerprint_sha256(key_fingerprint)
+ Key.signing.find_by_fingerprint_sha256(key_fingerprint)
end
end
@@ -48,6 +55,7 @@ module Gitlab
# still need to check that the key belongs to the committer.
def valid_signature_blob?
return false unless signature
+ return false unless signature.namespace == GIT_NAMESPACE
signature.verify(@signed_text)
end
@@ -55,7 +63,11 @@ module Gitlab
def committer
# Lookup by email because users can push verified commits that were made
# by someone else. For example: Doing a rebase.
- strong_memoize(:committer) { User.find_by_any_email(@committer_email, confirmed: true) }
+ strong_memoize(:committer) { User.find_by_any_email(@committer_email) }
+ end
+
+ def signed_by_user_email_verified?
+ signed_by_key.user.verified_emails.include?(@committer_email)
end
def signature
diff --git a/lib/gitlab/task_helpers.rb b/lib/gitlab/task_helpers.rb
index 54db31ffd6c..9dba8c99b99 100644
--- a/lib/gitlab/task_helpers.rb
+++ b/lib/gitlab/task_helpers.rb
@@ -149,18 +149,6 @@ module Gitlab
end
end
- def all_repos
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab.config.repositories.storages.each_value do |repository_storage|
- IO.popen(%W(find #{repository_storage.legacy_disk_path} -mindepth 2 -type d -name *.git)) do |find|
- find.each_line do |path|
- yield path.chomp
- end
- end
- end
- end
- end
-
def repository_storage_paths_args
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
Gitlab.config.repositories.storages.values.map { |rs| rs.legacy_disk_path }
diff --git a/lib/gitlab/template/base_template.rb b/lib/gitlab/template/base_template.rb
index ededc3db18e..223e3d40751 100644
--- a/lib/gitlab/template/base_template.rb
+++ b/lib/gitlab/template/base_template.rb
@@ -121,8 +121,8 @@ module Gitlab
grouped = items.group_by(&:category)
categories = grouped.keys
- categories.each_with_object({}) do |category, hash|
- hash[category] = grouped[category].map do |item|
+ categories.index_with do |category|
+ grouped[category].map do |item|
{ name: item.name, id: item.key, key: item.key, project_id: item.try(:project_id) }
end
end
diff --git a/lib/gitlab/timeless.rb b/lib/gitlab/timeless.rb
index b72d33113dd..ed0b7b4ed87 100644
--- a/lib/gitlab/timeless.rb
+++ b/lib/gitlab/timeless.rb
@@ -8,9 +8,9 @@ module Gitlab
# negative arity means arguments are optional
if block.arity == 1 || block.arity < 0
- block.call(model)
+ yield(model)
else
- block.call
+ yield
end
ensure
diff --git a/lib/gitlab/tracking/destinations/snowplow.rb b/lib/gitlab/tracking/destinations/snowplow.rb
index ddcd4693738..fd877bc0137 100644
--- a/lib/gitlab/tracking/destinations/snowplow.rb
+++ b/lib/gitlab/tracking/destinations/snowplow.rb
@@ -14,7 +14,15 @@ module Gitlab
def event(category, action, label: nil, property: nil, value: nil, context: nil)
return unless enabled?
- tracker.track_struct_event(category, action, label, property, value, context, (Time.now.to_f * 1000).to_i)
+ tracker.track_struct_event(
+ category: category,
+ action: action,
+ label: label,
+ property: property,
+ value: value,
+ context: context,
+ tstamp: (Time.now.to_f * 1000).to_i
+ )
increment_total_events_counter
end
@@ -54,19 +62,21 @@ module Gitlab
def tracker
@tracker ||= SnowplowTracker::Tracker.new(
- emitter,
- SnowplowTracker::Subject.new,
- SNOWPLOW_NAMESPACE,
- app_id
+ emitters: [emitter],
+ subject: SnowplowTracker::Subject.new,
+ namespace: SNOWPLOW_NAMESPACE,
+ app_id: app_id
)
end
def emitter
SnowplowTracker::AsyncEmitter.new(
- hostname,
- protocol: protocol,
- on_success: method(:increment_successful_events_emissions),
- on_failure: method(:failure_callback)
+ endpoint: hostname,
+ options: {
+ protocol: protocol,
+ on_success: method(:increment_successful_events_emissions),
+ on_failure: method(:failure_callback)
+ }
)
end
diff --git a/lib/gitlab/tracking/incident_management.rb b/lib/gitlab/tracking/incident_management.rb
index df2a0658b36..a912fdbaeca 100644
--- a/lib/gitlab/tracking/incident_management.rb
+++ b/lib/gitlab/tracking/incident_management.rb
@@ -17,7 +17,7 @@ module Gitlab
details = label ? { label: label, property: v } : {}
- ::Gitlab::Tracking.event('IncidentManagement::Settings', "#{prefix}_#{key}", **details )
+ ::Gitlab::Tracking.event('IncidentManagement::Settings', "#{prefix}_#{key}", **details)
end
end
diff --git a/lib/gitlab/tracking/service_ping_context.rb b/lib/gitlab/tracking/service_ping_context.rb
index 393cd647e7f..d31ca69a10c 100644
--- a/lib/gitlab/tracking/service_ping_context.rb
+++ b/lib/gitlab/tracking/service_ping_context.rb
@@ -4,21 +4,51 @@ module Gitlab
module Tracking
class ServicePingContext
SCHEMA_URL = 'iglu:com.gitlab/gitlab_service_ping/jsonschema/1-0-0'
- ALLOWED_SOURCES = %i[redis_hll].freeze
+ REDISHLL_SOURCE = :redis_hll
+ REDIS_SOURCE = :redis
- def initialize(data_source:, event:)
+ ALLOWED_SOURCES = [REDISHLL_SOURCE, REDIS_SOURCE].freeze
+
+ def initialize(data_source:, event: nil, key_path: nil)
+ check_configuration(data_source, event, key_path)
+
+ @payload = { data_source: data_source }
+
+ payload[:event_name] = event if data_source.eql? REDISHLL_SOURCE
+ payload[:key_path] = key_path if data_source.eql? REDIS_SOURCE
+ end
+
+ def to_context
+ SnowplowTracker::SelfDescribingJson.new(SCHEMA_URL, payload)
+ end
+
+ def to_h
+ {
+ schema: SCHEMA_URL,
+ data: @payload
+ }
+ end
+
+ private
+
+ attr_reader :payload
+
+ def check_configuration(data_source, event, key_path)
unless ALLOWED_SOURCES.include?(data_source)
- raise ArgumentError, "#{data_source} is not acceptable data source for ServicePingContext"
+ configuration_error("#{data_source} is not acceptable data source for ServicePingContext")
end
- @payload = {
- data_source: data_source,
- event_name: event
- }
+ if REDISHLL_SOURCE.eql?(data_source) && event.nil?
+ configuration_error("event attribute can not be missing for #{REDISHLL_SOURCE} data source")
+ end
+
+ return unless REDIS_SOURCE.eql?(data_source) && key_path.nil?
+
+ configuration_error("key_path attribute can not be missing for #{REDIS_SOURCE} data source")
end
- def to_context
- SnowplowTracker::SelfDescribingJson.new(SCHEMA_URL, @payload)
+ def configuration_error(message)
+ Gitlab::ErrorTracking.track_and_raise_for_dev_exception(ArgumentError.new(message))
end
end
end
diff --git a/lib/gitlab/url_blocker.rb b/lib/gitlab/url_blocker.rb
index 1e447923a39..00e609511f2 100644
--- a/lib/gitlab/url_blocker.rb
+++ b/lib/gitlab/url_blocker.rb
@@ -22,8 +22,8 @@ module Gitlab
# rubocop:disable Metrics/ParameterLists
def validate!(
url,
+ schemes:,
ports: [],
- schemes: [],
allow_localhost: false,
allow_local_network: true,
allow_object_storage: false,
@@ -35,6 +35,8 @@ module Gitlab
return [nil, nil] if url.nil?
+ raise ArgumentError, 'The schemes is a required argument' if schemes.blank?
+
# Param url can be a string, URI or Addressable::URI
uri = parse_url(url)
@@ -204,7 +206,7 @@ module Gitlab
end
def validate_scheme(scheme, schemes)
- if scheme.blank? || (schemes.any? && !schemes.include?(scheme))
+ if scheme.blank? || (schemes.any? && schemes.exclude?(scheme))
raise BlockedUrlError, "Only allowed schemes are #{schemes.join(', ')}"
end
end
diff --git a/lib/gitlab/usage/metrics/aggregates.rb b/lib/gitlab/usage/metrics/aggregates.rb
index 02d9fa74289..4b38809dde4 100644
--- a/lib/gitlab/usage/metrics/aggregates.rb
+++ b/lib/gitlab/usage/metrics/aggregates.rb
@@ -11,6 +11,7 @@ module Gitlab
UnknownAggregationOperator = Class.new(AggregatedMetricError)
UnknownAggregationSource = Class.new(AggregatedMetricError)
DisallowedAggregationTimeFrame = Class.new(AggregatedMetricError)
+ UndefinedEvents = Class.new(AggregatedMetricError)
DATABASE_SOURCE = 'database'
REDIS_SOURCE = 'redis_hll'
diff --git a/lib/gitlab/usage/metrics/aggregates/aggregate.rb b/lib/gitlab/usage/metrics/aggregates/aggregate.rb
index 78f1ddc8a29..8d816c8d902 100644
--- a/lib/gitlab/usage/metrics/aggregates/aggregate.rb
+++ b/lib/gitlab/usage/metrics/aggregates/aggregate.rb
@@ -76,3 +76,5 @@ module Gitlab
end
end
end
+
+Gitlab::Usage::Metrics::Aggregates::Aggregate.prepend_mod
diff --git a/lib/gitlab/usage/metrics/aggregates/sources/calculations/intersection.rb b/lib/gitlab/usage/metrics/aggregates/sources/calculations/intersection.rb
index dabf757c8a7..c8c248905f7 100644
--- a/lib/gitlab/usage/metrics/aggregates/sources/calculations/intersection.rb
+++ b/lib/gitlab/usage/metrics/aggregates/sources/calculations/intersection.rb
@@ -18,10 +18,8 @@ module Gitlab
subset_powers_data = subsets_intersection_powers(metric_names, start_date, end_date, recorded_at, subset_powers_cache)
# calculate last component of the equation |A & B & C & D| = .... - |A + B + C + D|
- power_of_union_of_all_metrics = begin
- subset_powers_cache[metric_names.size][metric_names.join('_+_')] ||= \
- calculate_metrics_union(metric_names: metric_names, start_date: start_date, end_date: end_date, recorded_at: recorded_at)
- end
+ power_of_union_of_all_metrics = subset_powers_cache[metric_names.size][metric_names.join('_+_')] ||= \
+ calculate_metrics_union(metric_names: metric_names, start_date: start_date, end_date: end_date, recorded_at: recorded_at)
# in order to determine if part of equation (|A & B & C|, |A & B & C & D|), that represents the intersection that we need to calculate,
# is positive or negative in particular equation we need to determine if number of subsets is even or odd. Please take a look at two examples below
diff --git a/lib/gitlab/usage/metrics/instrumentations/count_merge_request_authors_metric.rb b/lib/gitlab/usage/metrics/instrumentations/count_merge_request_authors_metric.rb
deleted file mode 100644
index a7f8bca8e08..00000000000
--- a/lib/gitlab/usage/metrics/instrumentations/count_merge_request_authors_metric.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Usage
- module Metrics
- module Instrumentations
- class CountMergeRequestAuthorsMetric < DatabaseMetric
- operation :distinct_count, column: :author_id
-
- relation { MergeRequest }
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/usage/metrics/instrumentations/database_metric.rb b/lib/gitlab/usage/metrics/instrumentations/database_metric.rb
index f0d5298870c..f731057309e 100644
--- a/lib/gitlab/usage/metrics/instrumentations/database_metric.rb
+++ b/lib/gitlab/usage/metrics/instrumentations/database_metric.rb
@@ -117,6 +117,8 @@ module Gitlab
case time_frame
when '28d'
monthly_time_range_db_params(column: self.class.metric_timestamp_column)
+ when '7d'
+ weekly_time_range_db_params(column: self.class.metric_timestamp_column)
when 'all'
{}
when 'none'
diff --git a/lib/gitlab/usage/service_ping/payload_keys_processor.rb b/lib/gitlab/usage/service_ping/payload_keys_processor.rb
index ea2043ffb83..89931d8c012 100644
--- a/lib/gitlab/usage/service_ping/payload_keys_processor.rb
+++ b/lib/gitlab/usage/service_ping/payload_keys_processor.rb
@@ -28,8 +28,8 @@ module Gitlab
payload.map do |key, value|
if has_metric_definition?(key, parents)
parents.dup.append(key).join('.')
- else
- payload_keys(value, parents.dup << key) if value.is_a?(Hash)
+ elsif value.is_a?(Hash)
+ payload_keys(value, parents.dup << key)
end
end
end
diff --git a/lib/gitlab/usage/time_frame.rb b/lib/gitlab/usage/time_frame.rb
index 39b0855b917..e2eed969200 100644
--- a/lib/gitlab/usage/time_frame.rb
+++ b/lib/gitlab/usage/time_frame.rb
@@ -21,6 +21,10 @@ module Gitlab
def monthly_time_range_db_params(column: nil)
{ (column || DEFAULT_TIMESTAMP_COLUMN) => 30.days.ago..2.days.ago }
end
+
+ def weekly_time_range_db_params(column: nil)
+ { (column || DEFAULT_TIMESTAMP_COLUMN) => 9.days.ago..2.days.ago }
+ end
end
end
end
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index 5021dac453f..24f6cc725f6 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -157,7 +157,6 @@ module Gitlab
runners_usage,
integrations_usage,
user_preferences_usage,
- container_expiration_policies_usage,
service_desk_counts
).tap do |data|
data[:snippets] = add(data[:personal_snippets], data[:project_snippets])
@@ -300,7 +299,7 @@ module Gitlab
object_store: {
enabled: alt_usage_data { config['enabled'] },
direct_upload: alt_usage_data { config['direct_upload'] },
- background_upload: alt_usage_data { config['background_upload'] },
+ background_upload: alt_usage_data { false }, # This setting no longer exists
provider: alt_usage_data { config['connection']['provider'] }
}
}
@@ -328,26 +327,6 @@ module Gitlab
end
# rubocop: disable CodeReuse/ActiveRecord
- def container_expiration_policies_usage
- results = {}
- start = minimum_id(Project)
- finish = maximum_id(Project)
-
- # rubocop: disable UsageData/LargeTable
- base = ::ContainerExpirationPolicy.active
- # rubocop: enable UsageData/LargeTable
-
- # rubocop: disable UsageData/LargeTable
- ::ContainerExpirationPolicy.older_than_options.keys.each do |value|
- results["projects_with_expiration_policy_enabled_with_older_than_set_to_#{value}".to_sym] = distinct_count(base.where(older_than: value), :project_id, start: start, finish: finish)
- end
- # rubocop: enable UsageData/LargeTable
-
- results[:projects_with_expiration_policy_enabled_with_older_than_unset] = distinct_count(base.where(older_than: nil), :project_id, start: start, finish: finish)
-
- results
- end
-
def integrations_usage
# rubocop: disable UsageData/LargeTable:
Integration.available_integration_names(include_dev: false).each_with_object({}) do |name, response|
@@ -611,10 +590,6 @@ module Gitlab
{}
end
- def redis_hll_counters
- { redis_hll_counters: ::Gitlab::UsageDataCounters::HLLRedisCounter.unique_events_data }
- end
-
def action_monthly_active_users(time_period)
counter = Gitlab::UsageDataCounters::EditorUniqueCounter
date_range = { date_from: time_period[:created_at].first, date_to: time_period[:created_at].last }
@@ -665,7 +640,6 @@ module Gitlab
.merge(topology_usage_data)
.merge(usage_activity_by_stage)
.merge(usage_activity_by_stage(:usage_activity_by_stage_monthly, monthly_time_range_db_params))
- .merge(redis_hll_counters)
end
def metric_time_period(time_period)
@@ -794,8 +768,8 @@ module Gitlab
# rubocop:disable CodeReuse/ActiveRecord
def distinct_count_user_auth_by_provider(time_period)
- counts = auth_providers_except_ldap.each_with_object({}) do |provider, hash|
- hash[provider] = distinct_count(
+ counts = auth_providers_except_ldap.index_with do |provider|
+ distinct_count(
::AuthenticationEvent.success.for_provider(provider).where(time_period), :user_id)
end
diff --git a/lib/gitlab/usage_data_counters/editor_unique_counter.rb b/lib/gitlab/usage_data_counters/editor_unique_counter.rb
index 5ede840661a..0b448f68153 100644
--- a/lib/gitlab/usage_data_counters/editor_unique_counter.rb
+++ b/lib/gitlab/usage_data_counters/editor_unique_counter.rb
@@ -45,20 +45,23 @@ module Gitlab
private
- def track_unique_action(action, author, time, project = nil)
+ def track_unique_action(event_name, author, time, project = nil)
return unless author
if Feature.enabled?(:route_hll_to_snowplow_phase2)
Gitlab::Tracking.event(
+ name,
'ide_edit',
- action.to_s,
+ property: event_name.to_s,
project: project,
namespace: project&.namespace,
- user: author
+ user: author,
+ label: 'usage_activity_by_stage_monthly.create.action_monthly_active_users_ide_edit',
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event_name).to_context]
)
end
- Gitlab::UsageDataCounters::HLLRedisCounter.track_event(action, values: author.id, time: time)
+ Gitlab::UsageDataCounters::HLLRedisCounter.track_event(event_name, values: author.id, time: time)
end
def count_unique(actions, date_from, date_to)
diff --git a/lib/gitlab/usage_data_counters/hll_redis_counter.rb b/lib/gitlab/usage_data_counters/hll_redis_counter.rb
index 24a87ae01f4..992cec2d174 100644
--- a/lib/gitlab/usage_data_counters/hll_redis_counter.rb
+++ b/lib/gitlab/usage_data_counters/hll_redis_counter.rb
@@ -25,32 +25,6 @@ module Gitlab
pipeline_authoring
].freeze
- CATEGORIES_COLLECTED_FROM_METRICS_DEFINITIONS = %w[
- analytics
- ci_users
- deploy_token_packages
- code_review
- ecosystem
- error_tracking
- ide_edit
- importer
- incident_management
- incident_management_alerts
- issues_edit
- kubernetes_agent
- manage
- pipeline_authoring
- quickactions
- search
- secure
- snippets
- source_code
- terraform
- testing
- user_packages
- work_items
- ].freeze
-
# Track event on entity_id
# Increment a Redis HLL counter for unique event_name and entity_id
#
@@ -114,41 +88,12 @@ module Gitlab
@categories ||= known_events.map { |event| event[:category] }.uniq
end
- def categories_collected_from_metrics_definitions
- CATEGORIES_COLLECTED_FROM_METRICS_DEFINITIONS
- end
-
# @param category [String] the category name
# @return [Array<String>] list of event names for given category
def events_for_category(category)
known_events.select { |event| event[:category] == category.to_s }.map { |event| event[:name] }
end
- # Recent 7 or 28 days unique events data for events defined in /lib/gitlab/usage_data_counters/known_events/
- #
- # - For metrics for which we store a key per day, we have the last 7 days or last 28 days of data.
- # - For metrics for which we store a key per week, we have the last complete week or last 4 complete weeks
- # daily or weekly information is in the file we have for events definition /lib/gitlab/usage_data_counters/known_events/
- # - Most of the metrics have weekly aggregation. We recommend this as it generates fewer keys in Redis to store.
- # - The aggregation used doesn't affect data granulation.
- def unique_events_data
- categories_pending_migration.each_with_object({}) do |category, category_results|
- events_names = events_for_category(category)
-
- event_results = events_names.each_with_object({}) do |event, hash|
- hash["#{event}_weekly"] = unique_events(**weekly_time_range.merge(event_names: [event])) unless event == "i_package_composer_deploy_token"
- hash["#{event}_monthly"] = unique_events(**monthly_time_range.merge(event_names: [event]))
- end
-
- if eligible_for_totals?(events_names) && CATEGORIES_FOR_TOTALS.include?(category)
- event_results["#{category}_total_unique_counts_weekly"] = unique_events(**weekly_time_range.merge(event_names: events_names))
- event_results["#{category}_total_unique_counts_monthly"] = unique_events(**monthly_time_range.merge(event_names: events_names))
- end
-
- category_results["#{category}"] = event_results
- end
- end
-
def known_event?(event_name)
event_for(event_name).present?
end
@@ -166,16 +111,13 @@ module Gitlab
private
- def categories_pending_migration
- (categories - categories_collected_from_metrics_definitions)
- end
-
def track(values, event_name, context: '', time: Time.zone.now)
return unless ::ServicePing::ServicePingSettings.enabled?
event = event_for(event_name)
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(UnknownEvent.new("Unknown event #{event_name}")) unless event.present?
+ return if event.blank?
return unless feature_enabled?(event)
Gitlab::Redis::HLL.add(key: redis_key(event, time, context), value: values, expiry: expiry(event))
diff --git a/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb b/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb
index dda72f7fa3b..477fa288874 100644
--- a/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb
+++ b/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb
@@ -173,7 +173,7 @@ module Gitlab
private
- def track_snowplow_action(action, author, project)
+ def track_snowplow_action(event_name, author, project)
return unless Feature.enabled?(:route_hll_to_snowplow_phase2, project.namespace)
return unless author
@@ -181,17 +181,18 @@ module Gitlab
ISSUE_CATEGORY,
ISSUE_ACTION,
label: ISSUE_LABEL,
- property: action,
+ property: event_name,
project: project,
namespace: project.namespace,
- user: author
+ user: author,
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event_name).to_context]
)
end
- def track_unique_action(action, author)
+ def track_unique_action(event_name, author)
return unless author
- Gitlab::UsageDataCounters::HLLRedisCounter.track_event(action, values: author.id)
+ Gitlab::UsageDataCounters::HLLRedisCounter.track_event(event_name, values: author.id)
end
end
end
diff --git a/lib/gitlab/usage_data_counters/known_events/ci_templates.yml b/lib/gitlab/usage_data_counters/known_events/ci_templates.yml
index 5b80f6c6c0d..b9f143a3a56 100644
--- a/lib/gitlab/usage_data_counters/known_events/ci_templates.yml
+++ b/lib/gitlab/usage_data_counters/known_events/ci_templates.yml
@@ -347,6 +347,14 @@
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
+- name: p_ci_templates_jobs_container_scanning
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
+- name: p_ci_templates_jobs_container_scanning_latest
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
- name: p_ci_templates_jobs_dependency_scanning_latest
category: ci_templates
redis_slot: ci_templates
@@ -519,6 +527,10 @@
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
+- name: p_ci_templates_implicit_jobs_container_scanning
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
- name: p_ci_templates_implicit_jobs_dast_default_branch_deploy
category: ci_templates
redis_slot: ci_templates
diff --git a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
index 0bd809f8aa5..3bb6655d762 100644
--- a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
+++ b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
@@ -39,6 +39,10 @@
redis_slot: code_review
category: code_review
aggregation: weekly
+- name: i_code_review_create_mr
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
- name: i_code_review_user_create_mr
redis_slot: code_review
category: code_review
@@ -256,7 +260,6 @@
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_diff_searches
- name: i_code_review_total_suggestions_applied
redis_slot: code_review
category: code_review
@@ -427,3 +430,28 @@
redis_slot: code_review
category: code_review
aggregation: weekly
+## Security Reports
+- name: i_code_review_merge_request_widget_security_reports_view
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+- name: i_code_review_merge_request_widget_security_reports_full_report_clicked
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+- name: i_code_review_merge_request_widget_security_reports_expand
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+- name: i_code_review_merge_request_widget_security_reports_expand_success
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+- name: i_code_review_merge_request_widget_security_reports_expand_warning
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+- name: i_code_review_merge_request_widget_security_reports_expand_failed
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
diff --git a/lib/gitlab/usage_data_counters/known_events/common.yml b/lib/gitlab/usage_data_counters/known_events/common.yml
index c1720b26a22..a64b7c4032b 100644
--- a/lib/gitlab/usage_data_counters/known_events/common.yml
+++ b/lib/gitlab/usage_data_counters/known_events/common.yml
@@ -131,7 +131,6 @@
category: testing
redis_slot: testing
aggregation: weekly
- feature_flag: usage_data_ci_i_testing_coverage_report_uploaded
# Project Management group
- name: g_project_management_issue_title_changed
category: issues_edit
diff --git a/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter.rb b/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter.rb
index 93137b762ec..10dae35d0bf 100644
--- a/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter.rb
+++ b/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter.rb
@@ -6,7 +6,8 @@ module Gitlab
MR_DIFFS_ACTION = 'i_code_review_mr_diffs'
MR_DIFFS_SINGLE_FILE_ACTION = 'i_code_review_mr_single_file_diffs'
MR_DIFFS_USER_SINGLE_FILE_ACTION = 'i_code_review_user_single_file_diffs'
- MR_CREATE_ACTION = 'i_code_review_user_create_mr'
+ MR_CREATE_ACTION = 'i_code_review_create_mr'
+ MR_USER_CREATE_ACTION = 'i_code_review_user_create_mr'
MR_CLOSE_ACTION = 'i_code_review_user_close_mr'
MR_REOPEN_ACTION = 'i_code_review_user_reopen_mr'
MR_MERGE_ACTION = 'i_code_review_user_merge_mr'
@@ -62,8 +63,24 @@ module Gitlab
track_unique_action_by_user(MR_DIFFS_USER_SINGLE_FILE_ACTION, user)
end
- def track_create_mr_action(user:)
- track_unique_action_by_user(MR_CREATE_ACTION, user)
+ def track_create_mr_action(user:, merge_request:)
+ track_unique_action_by_user(MR_USER_CREATE_ACTION, user)
+ track_unique_action_by_merge_request(MR_CREATE_ACTION, merge_request)
+
+ project = merge_request.target_project
+ return unless Feature.enabled?(:route_hll_to_snowplow_phase2, project.namespace)
+
+ Gitlab::Tracking.event(
+ name,
+ :create,
+ project: project,
+ namespace: project.namespace,
+ user: user,
+ property: MR_CREATE_ACTION,
+ label: 'redis_hll_counters.code_review.i_code_review_create_mr_monthly',
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll,
+ event: MR_CREATE_ACTION).to_context]
+ )
end
def track_close_mr_action(user:)
@@ -85,11 +102,15 @@ module Gitlab
return unless Feature.enabled?(:route_hll_to_snowplow_phase2, project.namespace)
Gitlab::Tracking.event(
- 'merge_requests',
- MR_APPROVE_ACTION,
+ name,
+ :approve,
project: project,
namespace: project.namespace,
- user: user
+ user: user,
+ property: MR_APPROVE_ACTION,
+ label: 'redis_hll_counters.code_review.i_code_review_user_approve_mr_monthly',
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll,
+ event: MR_APPROVE_ACTION).to_context]
)
end
diff --git a/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb b/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb
index f88bbc41c70..639da9bfee0 100644
--- a/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb
+++ b/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb
@@ -5,7 +5,16 @@ module Gitlab
class MergeRequestWidgetExtensionCounter < BaseCounter
KNOWN_EVENTS = %w[view full_report_clicked expand expand_success expand_warning expand_failed].freeze
PREFIX = 'i_code_review_merge_request_widget'
- WIDGETS = %w[accessibility code_quality license_compliance status_checks terraform test_summary metrics].freeze
+ WIDGETS = %w[
+ accessibility
+ code_quality
+ license_compliance
+ status_checks
+ terraform
+ test_summary
+ metrics
+ security_reports
+ ].freeze
class << self
private
diff --git a/lib/gitlab/utils/delegator_override/validator.rb b/lib/gitlab/utils/delegator_override/validator.rb
index 4449fa75877..7fa5cc4deef 100644
--- a/lib/gitlab/utils/delegator_override/validator.rb
+++ b/lib/gitlab/utils/delegator_override/validator.rb
@@ -67,7 +67,7 @@ module Gitlab
(delegator_class.instance_methods - allowlist).each do |method_name|
target_classes.each do |target_class|
- next unless target_class.instance_methods.include?(method_name)
+ next unless target_class.method_defined?(method_name)
errors << generate_error(method_name, target_class, delegator_class)
end
diff --git a/lib/gitlab/utils/override.rb b/lib/gitlab/utils/override.rb
index 39670a835a6..f83ebba7c3f 100644
--- a/lib/gitlab/utils/override.rb
+++ b/lib/gitlab/utils/override.rb
@@ -67,8 +67,8 @@ module Gitlab
private
def instance_method_defined?(klass, name)
- klass.instance_methods(false).include?(name) ||
- klass.private_instance_methods(false).include?(name)
+ klass.method_defined?(name, false) ||
+ klass.private_method_defined?(name, false)
end
def find_direct_method(klass, name)
diff --git a/lib/gitlab/utils/sanitize_node_link.rb b/lib/gitlab/utils/sanitize_node_link.rb
index b0dfa087fcf..9c34302f75e 100644
--- a/lib/gitlab/utils/sanitize_node_link.rb
+++ b/lib/gitlab/utils/sanitize_node_link.rb
@@ -30,7 +30,7 @@ module Gitlab
# Remove all invalid scheme characters before checking against the
# list of unsafe protocols.
#
- # See https://tools.ietf.org/html/rfc3986#section-3.1
+ # See https://www.rfc-editor.org/rfc/rfc3986#section-3.1
#
def safe_protocol?(scheme)
return false unless scheme
diff --git a/lib/gitlab/utils/strong_memoize.rb b/lib/gitlab/utils/strong_memoize.rb
index 6456ad08924..7e78363dae5 100644
--- a/lib/gitlab/utils/strong_memoize.rb
+++ b/lib/gitlab/utils/strong_memoize.rb
@@ -16,16 +16,6 @@ module Gitlab
# include Gitlab::Utils::StrongMemoize
#
# def trigger_from_token
- # strong_memoize(:trigger) do
- # Ci::Trigger.find_by_token(params[:token].to_s)
- # end
- # end
- #
- # Or like:
- #
- # include Gitlab::Utils::StrongMemoize
- #
- # def trigger_from_token
# Ci::Trigger.find_by_token(params[:token].to_s)
# end
# strong_memoize_attr :trigger_from_token
@@ -99,6 +89,15 @@ module Gitlab
def do_strong_memoize(klass, method_name, member_name)
method = klass.instance_method(method_name)
+ unless method.arity == 0
+ raise <<~ERROR
+ Using `strong_memoize_attr` on methods with parameters is not supported.
+
+ Use `strong_memoize_with` instead.
+ See https://docs.gitlab.com/ee/development/utilities.html#strongmemoize
+ ERROR
+ end
+
# Methods defined within a class method are already public by default, so we don't need to
# explicitly make them public.
scope = %i[private protected].find do |scope|
@@ -106,9 +105,9 @@ module Gitlab
.include? method_name
end
- klass.define_method(method_name) do |*args, &block|
+ klass.define_method(method_name) do |&block|
strong_memoize(member_name) do
- method.bind_call(self, *args, &block)
+ method.bind_call(self, &block)
end
end
diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb
index 7360585df43..8b016a09889 100644
--- a/lib/gitlab/visibility_level.rb
+++ b/lib/gitlab/visibility_level.rb
@@ -11,7 +11,7 @@ module Gitlab
included do
scope :public_only, -> { where(visibility_level: PUBLIC) }
- scope :public_and_internal_only, -> { where(visibility_level: [PUBLIC, INTERNAL] ) }
+ scope :public_and_internal_only, -> { where(visibility_level: [PUBLIC, INTERNAL]) }
scope :private_only, -> { where(visibility_level: PRIVATE) }
scope :non_public_only, -> { where.not(visibility_level: PUBLIC) }
diff --git a/lib/gitlab/work_items/work_item_hierarchy.rb b/lib/gitlab/work_items/work_item_hierarchy.rb
new file mode 100644
index 00000000000..e71bf2bce6a
--- /dev/null
+++ b/lib/gitlab/work_items/work_item_hierarchy.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module WorkItems
+ class WorkItemHierarchy < ObjectHierarchy
+ extend ::Gitlab::Utils::Override
+
+ private
+
+ def middle_table
+ ::WorkItems::ParentLink.arel_table
+ end
+
+ def from_tables(cte)
+ [objects_table, cte.table, middle_table]
+ end
+
+ override :parent_id_column
+ def parent_id_column(cte)
+ middle_table[:work_item_parent_id]
+ end
+
+ override :ancestor_conditions
+ def ancestor_conditions(cte)
+ conditions = middle_table[:work_item_parent_id].eq(objects_table[:id]).and(
+ middle_table[:work_item_id].eq(cte.table[:id])
+ )
+
+ with_type_filter(conditions, cte)
+ end
+
+ override :descendant_conditions
+ def descendant_conditions(cte)
+ conditions = middle_table[:work_item_id].eq(objects_table[:id]).and(
+ middle_table[:work_item_parent_id].eq(cte.table[:id])
+ )
+
+ with_type_filter(conditions, cte)
+ end
+
+ def with_type_filter(conditions, cte)
+ return conditions unless options[:same_type]
+
+ conditions.and(objects_table[:work_item_type_id].eq(cte.table[:work_item_type_id]))
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb
index 0d5daeefe90..02418c45e73 100644
--- a/lib/gitlab/workhorse.rb
+++ b/lib/gitlab/workhorse.rb
@@ -33,7 +33,7 @@ module Gitlab
GitalyServer: {
address: Gitlab::GitalyClient.address(repository.storage),
token: Gitlab::GitalyClient.token(repository.storage),
- features: Feature::Gitaly.server_feature_flags(
+ call_metadata: Feature::Gitaly.server_feature_flags(
user: ::Feature::Gitaly.user_actor(user),
repository: repository,
project: ::Feature::Gitaly.project_actor(repository.container),
@@ -48,6 +48,12 @@ module Gitlab
attrs[:GitConfigOptions] << "receive.maxInputSize=#{receive_max_input_size.megabytes}"
end
+ attrs[:GitalyServer][:call_metadata].merge!(
+ 'user_id' => attrs[:GL_ID].presence,
+ 'username' => attrs[:GL_USERNAME].presence,
+ 'remote_ip' => Gitlab::ApplicationContext.current_context_attribute(:remote_ip).presence
+ ).compact!
+
attrs
end
@@ -257,7 +263,7 @@ module Gitlab
{
address: Gitlab::GitalyClient.address(repository.shard),
token: Gitlab::GitalyClient.token(repository.shard),
- features: Feature::Gitaly.server_feature_flags(
+ call_metadata: Feature::Gitaly.server_feature_flags(
user: ::Feature::Gitaly.user_actor,
repository: repository,
project: ::Feature::Gitaly.project_actor(repository.container),
diff --git a/lib/gitlab/x509/signature.rb b/lib/gitlab/x509/signature.rb
index f8a6980f208..d6bbb8bb2cb 100644
--- a/lib/gitlab/x509/signature.rb
+++ b/lib/gitlab/x509/signature.rb
@@ -6,6 +6,7 @@ module Gitlab
module X509
class Signature
include Gitlab::Utils::StrongMemoize
+ include SignatureType
attr_reader :signature_text, :signed_text, :created_at
@@ -16,14 +17,18 @@ module Gitlab
@created_at = created_at
end
+ def type
+ :x509
+ end
+
def x509_certificate
return if certificate_attributes.nil?
X509Certificate.safe_create!(certificate_attributes) unless verified_signature.nil?
end
- def user
- strong_memoize(:user) { User.find_by_any_email(@email) }
+ def signed_by_user
+ strong_memoize(:signed_by_user) { User.find_by_any_email(@email) }
end
def verified_signature
@@ -33,11 +38,11 @@ module Gitlab
def verification_status
return :unverified if
x509_certificate.nil? ||
- x509_certificate.revoked? ||
- !verified_signature ||
- user.nil?
+ x509_certificate.revoked? ||
+ !verified_signature ||
+ signed_by_user.nil?
- if user.verified_emails.include?(@email.downcase) && certificate_email.casecmp?(@email)
+ if signed_by_user.verified_emails.include?(@email.downcase) && certificate_email.casecmp?(@email)
:verified
else
:unverified
diff --git a/lib/gitlab_edition.rb b/lib/gitlab_edition.rb
index 5e3ed35ace4..2a668537534 100644
--- a/lib/gitlab_edition.rb
+++ b/lib/gitlab_edition.rb
@@ -49,7 +49,7 @@ module GitlabEdition
# The behavior needs to be synchronised with
# config/helpers/is_ee_env.js
root.join('ee/app/models/license.rb').exist? &&
- !%w[true 1].include?(ENV['FOSS_ONLY'].to_s)
+ !%w[true 1].include?(ENV['FOSS_ONLY'].to_s) # rubocop:disable Rails/NegateInclude
end
def self.jh?
@@ -57,8 +57,8 @@ module GitlabEdition
@is_jh =
ee? &&
- root.join('jh').exist? &&
- !%w[true 1].include?(ENV['EE_ONLY'].to_s)
+ root.join('jh').exist? &&
+ !%w[true 1].include?(ENV['EE_ONLY'].to_s) # rubocop:disable Rails/NegateInclude
end
def self.ee
diff --git a/lib/google_api/cloud_platform/client.rb b/lib/google_api/cloud_platform/client.rb
index 38a1a968aec..e37bd1f7606 100644
--- a/lib/google_api/cloud_platform/client.rb
+++ b/lib/google_api/cloud_platform/client.rb
@@ -237,8 +237,8 @@ module GoogleApi
end
def make_addons_config(enable_addons)
- enable_addons.each_with_object({}) do |addon, hash|
- hash[addon] = { disabled: false }
+ enable_addons.index_with do |addon|
+ { disabled: false }
end
end
diff --git a/lib/kramdown/converter/commonmark.rb b/lib/kramdown/converter/commonmark.rb
index 33ec9dd1fbc..a903d541d81 100644
--- a/lib/kramdown/converter/commonmark.rb
+++ b/lib/kramdown/converter/commonmark.rb
@@ -21,9 +21,9 @@ module Kramdown
res = super
if [:ul, :dl, :ol, :codeblock].include?(el.type) && opts[:next] &&
- ([el.type, :codeblock].include?(opts[:next].type) ||
- (opts[:next].type == :blank && opts[:nnext] &&
- [el.type, :codeblock].include?(opts[:nnext].type)))
+ ([el.type, :codeblock].include?(opts[:next].type) ||
+ (opts[:next].type == :blank && opts[:nnext] &&
+ [el.type, :codeblock].include?(opts[:nnext].type)))
# replace the end of block character
res.sub!(/\^\n\n\z/m, "#{END_OF_BLOCK}\n\n")
end
@@ -43,7 +43,7 @@ module Kramdown
if el.children.first && el.children.first.type == :p && !el.children.first.options[:transparent]
if el.children.size == 1 && @stack.last.children.last == el &&
- (@stack.last.children.any? { |c| c.children.first.type != :p } || @stack.last.children.size == 1)
+ (@stack.last.children.any? { |c| c.children.first.type != :p } || @stack.last.children.size == 1)
# replace the end of block character
res.sub!(/\^\n\z/m, "#{END_OF_BLOCK}\n")
end
diff --git a/lib/pager_duty/validator/schemas/message.json b/lib/pager_duty/validator/schemas/message.json
index b1a3185cd1a..eeec6657587 100644
--- a/lib/pager_duty/validator/schemas/message.json
+++ b/lib/pager_duty/validator/schemas/message.json
@@ -1,44 +1,77 @@
{
"type": "object",
- "required": ["event", "incident"],
+ "required": [
+ "event"
+ ],
"properties": {
- "event": { "type": "string" },
- "incident": {
+ "event": {
"type": "object",
"required": [
- "html_url",
- "incident_number",
- "title",
- "status",
- "created_at",
- "urgency",
- "incident_key"
- ],
- "properties": {
- "html_url": { "type": "string" },
- "incindent_number": { "type": "integer" },
- "title": { "type": "string" },
- "status": { "type": "string" },
- "created_at": { "type": "string" },
- "urgency": { "type": "string", "enum": ["high", "low"] },
- "incident_key": { "type": ["string", "null"] },
- "assignments": {
- "type": "array",
- "items": {
- "assignee": {
- "type": "array",
- "items": {
- "summary": { "type": "string" },
- "html_url": { "type": "string" }
+ "data"
+ ]
+ },
+ "properties": {
+ "data": {
+ "type": "object",
+ "required": [
+ "html_url",
+ "number",
+ "title",
+ "status",
+ "created_at",
+ "urgency",
+ "incident_key"
+ ],
+ "properties": {
+ "html_url": {
+ "type": "string"
+ },
+ "number": {
+ "type": "integer"
+ },
+ "title": {
+ "type": "string"
+ },
+ "status": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "string"
+ },
+ "urgency": {
+ "type": "string",
+ "enum": [
+ "high",
+ "low"
+ ]
+ },
+ "incident_key": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "assignee": {
+ "type": "array",
+ "items": {
+ "summary": {
+ "type": "string"
+ },
+ "html_url": {
+ "type": "string"
+ }
+ }
+ },
+ "service": {
+ "type": "object",
+ "items": {
+ "summary": {
+ "type": "string"
+ },
+ "html_url": {
+ "type": "string"
}
}
- }
- },
- "impacted_services": {
- "type": "array",
- "items": {
- "summary": { "type": "string" },
- "html_url": { "type": "string" }
}
}
}
diff --git a/lib/pager_duty/webhook_payload_parser.rb b/lib/pager_duty/webhook_payload_parser.rb
index c17e3df1a72..e65341232f5 100644
--- a/lib/pager_duty/webhook_payload_parser.rb
+++ b/lib/pager_duty/webhook_payload_parser.rb
@@ -13,7 +13,7 @@ module PagerDuty
end
def call
- Array(payload['messages']).map { |msg| parse_message(msg) }.reject(&:empty?)
+ parse_message(payload)
end
private
@@ -24,41 +24,47 @@ module PagerDuty
return {} unless valid_message?(message)
{
- 'event' => message['event'],
- 'incident' => parse_incident(message['incident'])
+ 'event' => message.dig('event', 'event_type'),
+ 'incident' => parse_incident(message.dig('event', 'data'))
}
end
def parse_incident(incident)
+ return {} unless incident
+
{
'url' => incident['html_url'],
- 'incident_number' => incident['incident_number'],
+ 'incident_number' => incident['number'],
'title' => incident['title'],
'status' => incident['status'],
'created_at' => incident['created_at'],
'urgency' => incident['urgency'],
'incident_key' => incident['incident_key'],
'assignees' => reject_empty(parse_assignees(incident)),
- 'impacted_services' => reject_empty(parse_impacted_services(incident))
+ 'impacted_service' => parse_impacted_service(incident)
}
end
def parse_assignees(incident)
- Array(incident['assignments']).map do |a|
+ return [] unless incident
+
+ Array(incident['assignees']).map do |a|
{
- 'summary' => a.dig('assignee', 'summary'),
- 'url' => a.dig('assignee', 'html_url')
+ 'summary' => a['summary'],
+ 'url' => a['html_url']
}
end
end
- def parse_impacted_services(incident)
- Array(incident['impacted_services']).map do |is|
- {
- 'summary' => is['summary'],
- 'url' => is['html_url']
- }
- end
+ def parse_impacted_service(incident)
+ return {} unless incident
+
+ return {} if incident.dig('service', 'summary').blank? && incident.dig('service', 'html_url').blank?
+
+ {
+ 'summary' => incident.dig('service', 'summary'),
+ 'url' => incident.dig('service', 'html_url')
+ }
end
def reject_empty(entities)
diff --git a/lib/security/ci_configuration/container_scanning_build_action.rb b/lib/security/ci_configuration/container_scanning_build_action.rb
index 82f9f7d0320..f04c221fc40 100644
--- a/lib/security/ci_configuration/container_scanning_build_action.rb
+++ b/lib/security/ci_configuration/container_scanning_build_action.rb
@@ -12,7 +12,7 @@ module Security
def template
return 'Auto-DevOps.gitlab-ci.yml' if @auto_devops_enabled
- 'Security/Container-Scanning.gitlab-ci.yml'
+ 'Jobs/Container-Scanning.gitlab-ci.yml'
end
def comment
diff --git a/lib/security/ci_configuration/sast_build_action.rb b/lib/security/ci_configuration/sast_build_action.rb
index 448d4fbeacb..2b1964f7c87 100644
--- a/lib/security/ci_configuration/sast_build_action.rb
+++ b/lib/security/ci_configuration/sast_build_action.rb
@@ -68,7 +68,7 @@ module Security
end
def auto_devops_stages
- auto_devops_template = YAML.safe_load( Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps').content )
+ auto_devops_template = YAML.safe_load(Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps').content)
auto_devops_template['stages']
end
diff --git a/lib/security/weak_passwords.rb b/lib/security/weak_passwords.rb
index 42b02132933..0772ef42fea 100644
--- a/lib/security/weak_passwords.rb
+++ b/lib/security/weak_passwords.rb
@@ -9,6 +9,14 @@ module Security
# random password.
MINIMUM_SUBSTRING_SIZE = 4
+ # Passwords of 64+ characters are more likely to randomly include a
+ # forbidden substring.
+ #
+ # This length was chosen somewhat arbitrarily, balancing security,
+ # usability, and skipping checks on `::User.random_password` which
+ # is 128 chars. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105755
+ PASSWORD_SUBSTRING_CHECK_MAX_LENGTH = 64
+
class << self
# Returns true when the password is on a list of weak passwords,
# or contains predictable substrings derived from user attributes.
@@ -72,7 +80,11 @@ module Security
# Case-insensitively checks whether a password includes a dynamic
# list of substrings. Substrings which are too short are not
# predictable and may occur randomly, and therefore not checked.
+ # Similarly passwords which are long enough to inadvertently and
+ # randomly include a substring are not checked.
def contains_predicatable_substring?(password, substrings)
+ return unless password.length < PASSWORD_SUBSTRING_CHECK_MAX_LENGTH
+
substrings = substrings.filter_map do |substring|
substring.downcase if substring.length >= MINIMUM_SUBSTRING_SIZE
end
diff --git a/lib/serializers/json.rb b/lib/serializers/json.rb
deleted file mode 100644
index 6564f53d2da..00000000000
--- a/lib/serializers/json.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-module Serializers
- # Make the resulting hash have deep indifferent access
- class Json
- class << self
- def dump(obj)
- obj
- end
-
- def load(data)
- return if data.nil?
-
- Gitlab::Utils.deep_indifferent_access(data)
- end
- end
- end
-end
diff --git a/lib/service_ping/build_payload.rb b/lib/service_ping/build_payload.rb
index 3553b624ae0..0f19cd55a4c 100644
--- a/lib/service_ping/build_payload.rb
+++ b/lib/service_ping/build_payload.rb
@@ -20,8 +20,8 @@ module ServicePing
if has_metric_definition?(key_path)
include_metric?(key_path)
- else
- filtered_usage_data(node, parents.dup << label) if node.is_a?(Hash)
+ elsif node.is_a?(Hash)
+ filtered_usage_data(node, parents.dup << label)
end
end
end
diff --git a/lib/sidebars/groups/menus/packages_registries_menu.rb b/lib/sidebars/groups/menus/packages_registries_menu.rb
index 873f11f8a5b..e115ca669d4 100644
--- a/lib/sidebars/groups/menus/packages_registries_menu.rb
+++ b/lib/sidebars/groups/menus/packages_registries_menu.rb
@@ -51,8 +51,8 @@ module Sidebars
def harbor_registry_menu_item
if Feature.disabled?(:harbor_registry_integration) ||
- context.group.harbor_integration.nil? ||
- !context.group.harbor_integration.activated?
+ context.group.harbor_integration.nil? ||
+ !context.group.harbor_integration.activated?
return nil_menu_item(:harbor_registry)
end
@@ -66,7 +66,7 @@ module Sidebars
def dependency_proxy_menu_item
setting_does_not_exist_or_is_enabled = !context.group.dependency_proxy_setting ||
- context.group.dependency_proxy_setting.enabled
+ context.group.dependency_proxy_setting.enabled
return nil_menu_item(:dependency_proxy) unless can?(context.current_user, :read_dependency_proxy, context.group)
return nil_menu_item(:dependency_proxy) unless setting_does_not_exist_or_is_enabled
diff --git a/lib/sidebars/menu.rb b/lib/sidebars/menu.rb
index d9d294ff982..dfd88c99a0c 100644
--- a/lib/sidebars/menu.rb
+++ b/lib/sidebars/menu.rb
@@ -43,7 +43,7 @@ module Sidebars
# Value from menus is something like: [{ path: 'foo', path: 'bar', controller: :foo }]
# This method filters the information and returns: { path: ['foo', 'bar'], controller: :foo }
def all_active_routes
- @all_active_routes ||= begin
+ @all_active_routes ||=
([active_routes] + renderable_items.map(&:active_routes)).flatten.each_with_object({}) do |pairs, hash|
pairs.each do |k, v|
hash[k] ||= []
@@ -53,7 +53,6 @@ module Sidebars
hash
end
- end
end
# Returns whether the menu has any menu item, no
diff --git a/lib/sidebars/projects/menus/analytics_menu.rb b/lib/sidebars/projects/menus/analytics_menu.rb
index b9bcc3267d6..643b7ebcd5a 100644
--- a/lib/sidebars/projects/menus/analytics_menu.rb
+++ b/lib/sidebars/projects/menus/analytics_menu.rb
@@ -45,9 +45,9 @@ module Sidebars
def ci_cd_analytics_menu_item
if !context.project.feature_available?(:builds, context.current_user) ||
- !can?(context.current_user, :read_build, context.project) ||
- !can?(context.current_user, :read_ci_cd_analytics, context.project) ||
- context.project.empty_repo?
+ !can?(context.current_user, :read_build, context.project) ||
+ !can?(context.current_user, :read_ci_cd_analytics, context.project) ||
+ context.project.empty_repo?
return ::Sidebars::NilMenuItem.new(item_id: :ci_cd_analytics)
end
diff --git a/lib/sidebars/projects/menus/deployments_menu.rb b/lib/sidebars/projects/menus/deployments_menu.rb
index 9904d533f47..5f789748288 100644
--- a/lib/sidebars/projects/menus/deployments_menu.rb
+++ b/lib/sidebars/projects/menus/deployments_menu.rb
@@ -62,7 +62,7 @@ module Sidebars
def releases_menu_item
if !can?(context.current_user, :read_release, context.project) ||
- context.project.empty_repo?
+ context.project.empty_repo?
return ::Sidebars::NilMenuItem.new(item_id: :releases)
end
diff --git a/lib/sidebars/projects/menus/hidden_menu.rb b/lib/sidebars/projects/menus/hidden_menu.rb
index c273ee8b74f..5db46a1279c 100644
--- a/lib/sidebars/projects/menus/hidden_menu.rb
+++ b/lib/sidebars/projects/menus/hidden_menu.rb
@@ -29,8 +29,8 @@ module Sidebars
end
def graph_menu_item
- if !can?(context.current_user, :download_code, context.project) ||
- context.project.empty_repo?
+ if !can?(context.current_user, :read_code, context.project) ||
+ context.project.empty_repo?
return ::Sidebars::NilMenuItem.new(item_id: :graph)
end
@@ -72,8 +72,8 @@ module Sidebars
end
def commits_menu_item
- if !can?(context.current_user, :download_code, context.project) ||
- context.project.empty_repo?
+ if !can?(context.current_user, :read_code, context.project) ||
+ context.project.empty_repo?
return ::Sidebars::NilMenuItem.new(item_id: :commits)
end
diff --git a/lib/sidebars/projects/menus/infrastructure_menu.rb b/lib/sidebars/projects/menus/infrastructure_menu.rb
index a8ac3d10f83..04c9ab77729 100644
--- a/lib/sidebars/projects/menus/infrastructure_menu.rb
+++ b/lib/sidebars/projects/menus/infrastructure_menu.rb
@@ -35,11 +35,7 @@ module Sidebars
private
def feature_enabled?
- if ::Feature.enabled?(:split_operations_visibility_permissions, context.project)
- context.project.feature_available?(:infrastructure, context.current_user)
- else
- context.project.feature_available?(:operations, context.current_user)
- end
+ context.project.feature_available?(:infrastructure, context.current_user)
end
def kubernetes_menu_item
diff --git a/lib/sidebars/projects/menus/monitor_menu.rb b/lib/sidebars/projects/menus/monitor_menu.rb
index 035634702db..fea71e4aefd 100644
--- a/lib/sidebars/projects/menus/monitor_menu.rb
+++ b/lib/sidebars/projects/menus/monitor_menu.rb
@@ -41,11 +41,7 @@ module Sidebars
private
def feature_enabled?
- if ::Feature.enabled?(:split_operations_visibility_permissions, context.project)
- context.project.feature_available?(:monitor, context.current_user)
- else
- context.project.feature_available?(:operations, context.current_user)
- end
+ context.project.feature_available?(:monitor, context.current_user)
end
def metrics_dashboard_menu_item
diff --git a/lib/sidebars/projects/menus/repository_menu.rb b/lib/sidebars/projects/menus/repository_menu.rb
index 1b46323089c..735be5a5133 100644
--- a/lib/sidebars/projects/menus/repository_menu.rb
+++ b/lib/sidebars/projects/menus/repository_menu.rb
@@ -6,7 +6,7 @@ module Sidebars
class RepositoryMenu < ::Sidebars::Menu
override :configure_menu_items
def configure_menu_items
- return false unless can?(context.current_user, :download_code, context.project)
+ return false unless can?(context.current_user, :read_code, context.project)
return false if context.project.empty_repo?
add_item(files_menu_item)
@@ -56,9 +56,15 @@ module Sidebars
end
def commits_menu_item
+ link = if Feature.enabled?(:use_ref_type_parameter, context.project)
+ project_commits_path(context.project, context.current_ref, ref_type: ref_type_from_context(context))
+ else
+ project_commits_path(context.project, context.current_ref)
+ end
+
::Sidebars::MenuItem.new(
title: _('Commits'),
- link: project_commits_path(context.project, context.current_ref),
+ link: link,
active_routes: { controller: %w(commit commits) },
item_id: :commits,
container_html_options: { id: 'js-onboarding-commits-link' }
@@ -87,18 +93,30 @@ module Sidebars
def contributors_menu_item
return false unless context.project.analytics_enabled?
+ link = if Feature.enabled?(:use_ref_type_parameter, context.project)
+ project_graph_path(context.project, context.current_ref, ref_type: ref_type_from_context(context))
+ else
+ project_graph_path(context.project, context.current_ref)
+ end
+
::Sidebars::MenuItem.new(
title: _('Contributors'),
- link: project_graph_path(context.project, context.current_ref),
+ link: link,
active_routes: { path: 'graphs#show' },
item_id: :contributors
)
end
def graphs_menu_item
+ link = if Feature.enabled?(:use_ref_type_parameter, context.project)
+ project_network_path(context.project, context.current_ref, ref_type: ref_type_from_context(context))
+ else
+ project_network_path(context.project, context.current_ref)
+ end
+
::Sidebars::MenuItem.new(
title: _('Graph'),
- link: project_network_path(context.project, context.current_ref),
+ link: link,
active_routes: { controller: :network },
item_id: :graphs
)
@@ -112,6 +130,12 @@ module Sidebars
item_id: :compare
)
end
+
+ def ref_type_from_context(context)
+ ref_type = context.try(:ref_type)
+ ref_type ||= 'heads' if context.current_ref == context.project.repository.root_ref
+ ref_type
+ end
end
end
end
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index 1ad89fdc364..092c7d32ffb 100755
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -65,11 +65,7 @@ if ! cd "$app_root" ; then
echo "Failed to cd into $app_root, exiting!"; exit 1
fi
-if [ -z "$SIDEKIQ_WORKERS" ]; then
- sidekiq_pid_path="$pid_path/sidekiq.pid"
-else
- sidekiq_pid_path="$pid_path/sidekiq-cluster.pid"
-fi
+sidekiq_pid_path="$pid_path/sidekiq-cluster.pid"
### Init Script functions
diff --git a/lib/support/systemd/gitlab-sidekiq.service b/lib/support/systemd/gitlab-sidekiq.service
index cab741010ed..d8585a59085 100644
--- a/lib/support/systemd/gitlab-sidekiq.service
+++ b/lib/support/systemd/gitlab-sidekiq.service
@@ -10,13 +10,14 @@ Type=notify
User=git
WorkingDirectory=/home/git/gitlab
Environment=RAILS_ENV=production
-ExecStart=/usr/local/bin/bundle exec sidekiq --config /home/git/gitlab/config/sidekiq_queues.yml --environment production
+Environment=SIDEKIQ_QUEUES=*
+ExecStart=/home/git/gitlab/bin/sidekiq-cluster $SIDEKIQ_QUEUES -P /home/git/gitlab/tmp/pids/sidekiq.pid
+NotifyAccess=all
PIDFile=/home/git/gitlab/tmp/pids/sidekiq.pid
Restart=on-failure
RestartSec=1
SyslogIdentifier=gitlab-sidekiq
Slice=gitlab.slice
-WatchdogSec=10
[Install]
WantedBy=gitlab.target
diff --git a/lib/system_check/app/gitlab_cable_config_exists_check.rb b/lib/system_check/app/gitlab_cable_config_exists_check.rb
new file mode 100644
index 00000000000..c13dade1b4c
--- /dev/null
+++ b/lib/system_check/app/gitlab_cable_config_exists_check.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module SystemCheck
+ module App
+ class GitlabCableConfigExistsCheck < SystemCheck::BaseCheck
+ set_name 'Cable config exists?'
+
+ def check?
+ cable_config_file = Rails.root.join('config/cable.yml')
+
+ File.exist?(cable_config_file)
+ end
+
+ def show_error
+ try_fixing_it(
+ 'Copy config/cable.yml.example to config/cable.yml',
+ 'Update config/cable.yml to match your setup'
+ )
+ for_more_information(
+ see_installation_guide_section('GitLab')
+ )
+ fix_and_rerun
+ end
+ end
+ end
+end
diff --git a/lib/system_check/app/gitlab_resque_config_exists_check.rb b/lib/system_check/app/gitlab_resque_config_exists_check.rb
new file mode 100644
index 00000000000..fb835553737
--- /dev/null
+++ b/lib/system_check/app/gitlab_resque_config_exists_check.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module SystemCheck
+ module App
+ class GitlabResqueConfigExistsCheck < SystemCheck::BaseCheck
+ set_name 'Resque config exists?'
+
+ def check?
+ resque_config_file = Rails.root.join('config/resque.yml')
+
+ File.exist?(resque_config_file)
+ end
+
+ def show_error
+ try_fixing_it(
+ 'Copy config/resque.yml.example to config/resque.yml',
+ 'Update config/resque.yml to match your setup'
+ )
+ for_more_information(
+ see_installation_guide_section('GitLab')
+ )
+ fix_and_rerun
+ end
+ end
+ end
+end
diff --git a/lib/system_check/helpers.rb b/lib/system_check/helpers.rb
index 8fbfe7af713..3ead1cae8df 100644
--- a/lib/system_check/helpers.rb
+++ b/lib/system_check/helpers.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
module SystemCheck
+ # Helpers used inside a SystemCheck instance to standardize output responses
module Helpers
include ::Gitlab::TaskHelpers
diff --git a/lib/system_check/multi_check_helpers.rb b/lib/system_check/multi_check_helpers.rb
new file mode 100644
index 00000000000..1b06864a63e
--- /dev/null
+++ b/lib/system_check/multi_check_helpers.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module SystemCheck
+ # Helpers used inside a SystemCheck instance to standardize output responses
+ # when using a multi_check version
+ module MultiCheckHelpers
+ def print_skipped(reason)
+ $stdout.puts 'skipped'.color(:magenta)
+
+ $stdout.puts ' Reason:'.color(:blue)
+ $stdout.puts " #{reason}"
+ end
+
+ def print_warning(reason)
+ $stdout.puts 'warning'.color(:magenta)
+
+ $stdout.puts ' Reason:'.color(:blue)
+ $stdout.puts " #{reason}"
+ end
+
+ def print_failure(reason)
+ $stdout.puts 'no'.color(:red)
+
+ $stdout.puts ' Reason:'.color(:blue)
+ $stdout.puts " #{reason}"
+ end
+
+ def print_pass
+ $stdout.puts self.class.check_pass.color(:green)
+ end
+ end
+end
diff --git a/lib/system_check/rake_task/app_task.rb b/lib/system_check/rake_task/app_task.rb
index 1eb7a35b40a..20332d4b24b 100644
--- a/lib/system_check/rake_task/app_task.rb
+++ b/lib/system_check/rake_task/app_task.rb
@@ -17,6 +17,8 @@ module SystemCheck
SystemCheck::App::OrphanedGroupMembersCheck,
SystemCheck::App::GitlabConfigExistsCheck,
SystemCheck::App::GitlabConfigUpToDateCheck,
+ SystemCheck::App::GitlabCableConfigExistsCheck,
+ SystemCheck::App::GitlabResqueConfigExistsCheck,
SystemCheck::App::LogWritableCheck,
SystemCheck::App::TmpWritableCheck,
SystemCheck::App::UploadsDirectoryExistsCheck,
diff --git a/lib/system_check/sidekiq_check.rb b/lib/system_check/sidekiq_check.rb
index ab048433b37..777e06f7501 100644
--- a/lib/system_check/sidekiq_check.rb
+++ b/lib/system_check/sidekiq_check.rb
@@ -5,6 +5,8 @@ module SystemCheck
class SidekiqCheck < BaseCheck
set_name 'Sidekiq:'
+ SYSTEMD_UNIT_PATH = '/run/systemd/units/invocation:gitlab-sidekiq.service'
+
def multi_check
check_sidekiq_running
only_one_sidekiq_running
@@ -37,9 +39,9 @@ module SystemCheck
$stdout.print 'Number of Sidekiq processes (cluster/worker) ... '
- if (cluster_count == 1 && worker_count > 0) || (cluster_count == 0 && worker_count == 1)
+ if cluster_count == 1 && worker_count >= 1
$stdout.puts "#{cluster_count}/#{worker_count}".color(:green)
- elsif File.symlink?('/run/systemd/units/invocation:gitlab-sidekiq.service')
+ elsif File.symlink?(SYSTEMD_UNIT_PATH)
$stdout.puts "#{cluster_count}/#{worker_count}".color(:red)
try_fixing_it(
'sudo systemctl restart gitlab-sidekiq.service'
diff --git a/lib/tasks/contracts/merge_requests.rake b/lib/tasks/contracts/merge_requests.rake
index 2ee7ec07a96..61823f0cf1a 100644
--- a/lib/tasks/contracts/merge_requests.rake
+++ b/lib/tasks/contracts/merge_requests.rake
@@ -4,29 +4,35 @@ return if Rails.env.production?
require 'pact/tasks/verification_task'
-contracts = File.expand_path('../../../spec/contracts/contracts/project/merge_request', __dir__)
provider = File.expand_path('../../../spec/contracts/provider', __dir__)
namespace :contracts do
+ require_relative "../../../spec/contracts/provider/helpers/contract_source_helper"
+
namespace :merge_requests do
- Pact::VerificationTask.new(:diffs_batch) do |pact|
+ Pact::VerificationTask.new(:get_diffs_batch) do |pact|
+ pact_helper_location = "pact_helpers/project/merge_requests/show/get_diffs_batch_helper.rb"
+
pact.uri(
- "#{contracts}/show/mergerequest#show-merge_request_diffs_batch_endpoint.json",
- pact_helper: "#{provider}/pact_helpers/project/merge_request/show/diffs_batch_helper.rb"
+ Provider::ContractSourceHelper.contract_location(:rake, pact_helper_location),
+ pact_helper: "#{provider}/#{pact_helper_location}"
)
end
- Pact::VerificationTask.new(:diffs_metadata) do |pact|
+ Pact::VerificationTask.new(:get_diffs_metadata) do |pact|
+ pact_helper_location = "pact_helpers/project/merge_requests/show/get_diffs_metadata_helper.rb"
pact.uri(
- "#{contracts}/show/mergerequest#show-merge_request_diffs_metadata_endpoint.json",
- pact_helper: "#{provider}/pact_helpers/project/merge_request/show/diffs_metadata_helper.rb"
+ Provider::ContractSourceHelper.contract_location(:rake, pact_helper_location),
+ pact_helper: "#{provider}/#{pact_helper_location}"
)
end
- Pact::VerificationTask.new(:discussions) do |pact|
+ Pact::VerificationTask.new(:get_discussions) do |pact|
+ pact_helper_location = "pact_helpers/project/merge_requests/show/get_discussions_helper.rb"
+
pact.uri(
- "#{contracts}/show/mergerequest#show-merge_request_discussions_endpoint.json",
- pact_helper: "#{provider}/pact_helpers/project/merge_request/show/discussions_helper.rb"
+ Provider::ContractSourceHelper.contract_location(:rake, pact_helper_location),
+ pact_helper: "#{provider}/#{pact_helper_location}"
)
end
diff --git a/lib/tasks/contracts/pipeline_schedules.rake b/lib/tasks/contracts/pipeline_schedules.rake
index 75080d41ebe..b4c87d2e3c9 100644
--- a/lib/tasks/contracts/pipeline_schedules.rake
+++ b/lib/tasks/contracts/pipeline_schedules.rake
@@ -4,15 +4,18 @@ return if Rails.env.production?
require 'pact/tasks/verification_task'
-contracts = File.expand_path('../../../spec/contracts/contracts/project/pipeline_schedule', __dir__)
provider = File.expand_path('../../../spec/contracts/provider', __dir__)
namespace :contracts do
+ require_relative "../../../spec/contracts/provider/helpers/contract_source_helper"
+
namespace :pipeline_schedules do
Pact::VerificationTask.new(:update_pipeline_schedule) do |pact|
+ pact_helper_location = "pact_helpers/project/pipeline_schedules/edit/put_edit_a_pipeline_schedule_helper.rb"
+
pact.uri(
- "#{contracts}/edit/pipelineschedules#edit-put_edit_a_pipeline_schedule.json",
- pact_helper: "#{provider}/pact_helpers/project/pipeline_schedule/update_pipeline_schedule_helper.rb"
+ Provider::ContractSourceHelper.contract_location(:rake, pact_helper_location),
+ pact_helper: "#{provider}/#{pact_helper_location}"
)
end
diff --git a/lib/tasks/contracts/pipelines.rake b/lib/tasks/contracts/pipelines.rake
index 5a8d7791233..55a7baa4539 100644
--- a/lib/tasks/contracts/pipelines.rake
+++ b/lib/tasks/contracts/pipelines.rake
@@ -4,40 +4,45 @@ return if Rails.env.production?
require 'pact/tasks/verification_task'
-contracts = File.expand_path('../../../spec/contracts/contracts/project/pipeline', __dir__)
provider = File.expand_path('../../../spec/contracts/provider', __dir__)
namespace :contracts do
+ require_relative "../../../spec/contracts/provider/helpers/contract_source_helper"
+
namespace :pipelines do
Pact::VerificationTask.new(:create_a_new_pipeline) do |pact|
+ pact_helper_location = "pact_helpers/project/pipelines/new/post_create_a_new_pipeline_helper.rb"
+
pact.uri(
- "#{contracts}/new/pipelines#new-post_create_a_new_pipeline.json",
- pact_helper: "#{provider}/pact_helpers/project/pipeline/index/create_a_new_pipeline_helper.rb"
+ Provider::ContractSourceHelper.contract_location(:rake, pact_helper_location),
+ pact_helper: "#{provider}/#{pact_helper_location}"
)
end
Pact::VerificationTask.new(:get_list_project_pipelines) do |pact|
+ pact_helper_location = "pact_helpers/project/pipelines/index/get_list_project_pipelines_helper.rb"
+
pact.uri(
- "#{contracts}/index/pipelines#index-get_list_project_pipelines.json",
- pact_helper: "#{provider}/pact_helpers/project/pipeline/index/get_list_project_pipelines_helper.rb"
+ Provider::ContractSourceHelper.contract_location(:rake, pact_helper_location),
+ pact_helper: "#{provider}/#{pact_helper_location}"
)
end
Pact::VerificationTask.new(:get_pipeline_header_data) do |pact|
- # pact.uri(
- # "http://localhost:9292/pacts/provider/GET%20pipeline%20header%20data/consumer/Pipelines%23show/latest",
- # pact_helper: "#{provider}/pact_helpers/project/pipeline/show/get_pipeline_header_data_helper.rb"
- # )
+ pact_helper_location = "pact_helpers/project/pipelines/show/get_pipeline_header_data_helper.rb"
+
pact.uri(
- "#{contracts}/show/pipelines#show-get_pipeline_header_data.json",
- pact_helper: "#{provider}/pact_helpers/project/pipeline/show/get_pipeline_header_data_helper.rb"
+ Provider::ContractSourceHelper.contract_location(:rake, pact_helper_location),
+ pact_helper: "#{provider}/#{pact_helper_location}"
)
end
Pact::VerificationTask.new(:delete_pipeline) do |pact|
+ pact_helper_location = "pact_helpers/project/pipelines/show/delete_pipeline_helper.rb"
+
pact.uri(
- "#{contracts}/show/pipelines#show-delete_pipeline.json",
- pact_helper: "#{provider}/pact_helpers/project/pipeline/show/delete_pipeline_helper.rb"
+ Provider::ContractSourceHelper.contract_location(:rake, pact_helper_location),
+ pact_helper: "#{provider}/#{pact_helper_location}"
)
end
diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake
index 129f4c0ff0e..22ca5d9039c 100644
--- a/lib/tasks/dev.rake
+++ b/lib/tasks/dev.rake
@@ -76,13 +76,9 @@ namespace :dev do
namespace :copy_db do
ALLOWED_DATABASES = %w[ci].freeze
- defined_copy_db_tasks = []
-
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
next unless ALLOWED_DATABASES.include?(name)
- defined_copy_db_tasks << name
-
desc "Copies the #{name} database from the main database"
task name => :environment do
Rake::Task["dev:terminate_all_connections"].invoke
@@ -94,16 +90,5 @@ namespace :dev do
warn "Database '#{db_config.database}' already exists"
end
end
-
- ALLOWED_DATABASES.each do |name|
- next if defined_copy_db_tasks.include?(name)
-
- # :nocov: we cannot mock ActiveRecord::Tasks::DatabaseTasks in time
- # Workaround for GDK issue, see
- # https://gitlab.com/gitlab-org/gitlab-development-kit/-/issues/1464
- desc "No-op task"
- task name
- # :nocov:
- end
end
end
diff --git a/lib/tasks/gitlab/assets.rake b/lib/tasks/gitlab/assets.rake
index 12a8cb01e9e..d8c0b1007e6 100644
--- a/lib/tasks/gitlab/assets.rake
+++ b/lib/tasks/gitlab/assets.rake
@@ -137,7 +137,7 @@ namespace :gitlab do
File.open(gzip, 'wb+') do |f|
gz = Zlib::GzipWriter.new(f, Zlib::BEST_COMPRESSION)
gz.mtime = mtime
- gz.write IO.binread(file)
+ gz.write File.binread(file)
gz.close
File.utime(mtime, mtime, f.path)
@@ -154,7 +154,9 @@ namespace :gitlab do
desc 'GitLab | Assets | Check that scss mixins do not introduce any sideffects'
task :check_page_bundle_mixins_css_for_sideeffects do
- system('./scripts/frontend/check_page_bundle_mixins_css_for_sideeffects.js')
+ unless system('./scripts/frontend/check_page_bundle_mixins_css_for_sideeffects.js')
+ abort 'Error: At least one CSS changes introduces an unwanted sideeffect'.color(:red)
+ end
end
end
end
diff --git a/lib/tasks/gitlab/cleanup.rake b/lib/tasks/gitlab/cleanup.rake
index f908a7606fa..49d2d9fed03 100644
--- a/lib/tasks/gitlab/cleanup.rake
+++ b/lib/tasks/gitlab/cleanup.rake
@@ -15,13 +15,11 @@ namespace :gitlab do
if Gitlab::Auth::Ldap::Access.allowed?(user)
puts " [OK]".color(:green)
+ elsif block_flag
+ user.block! unless user.blocked?
+ puts " [BLOCKED]".color(:red)
else
- if block_flag
- user.block! unless user.blocked?
- puts " [BLOCKED]".color(:red)
- else
- puts " [NOT IN LDAP]".color(:yellow)
- end
+ puts " [NOT IN LDAP]".color(:yellow)
end
end
diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake
index 4ef0c396f4a..f0264456201 100644
--- a/lib/tasks/gitlab/db.rake
+++ b/lib/tasks/gitlab/db.rake
@@ -316,6 +316,7 @@ namespace :gitlab do
all_databases.each do |db|
desc "Run migrations on #{db} with instrumentation"
task db => :environment do
+ Gitlab::Database::Migrations::Runner.batched_migrations_last_id(db).store
Gitlab::Database::Migrations::Runner.up(database: db).run
end
end
@@ -406,7 +407,7 @@ namespace :gitlab do
Rails.application.eager_load!
tables = Gitlab::Database.database_base_models.flat_map { |_, m| m.connection.tables }
- classes = tables.to_h { |t| [t, []] }
+ classes = tables.index_with { [] }
Gitlab::Database.database_base_models.each do |_, model_class|
model_class
diff --git a/lib/tasks/gitlab/db/lock_writes.rake b/lib/tasks/gitlab/db/lock_writes.rake
index 421c6a90fdd..a856aa77abc 100644
--- a/lib/tasks/gitlab/db/lock_writes.rake
+++ b/lib/tasks/gitlab/db/lock_writes.rake
@@ -6,7 +6,8 @@ namespace :gitlab do
task lock_writes: [:environment, 'gitlab:db:validate_config'] do
Gitlab::Database::EachDatabase.each_database_connection(include_shared: false) do |connection, database_name|
schemas_for_connection = Gitlab::Database.gitlab_schemas_for_connection(connection)
- Gitlab::Database::GitlabSchema.tables_to_schema.each do |table_name, schema_name|
+
+ Gitlab::Database::LockWritesManager.tables_to_lock(connection) do |table_name, schema_name|
# TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/366834
next if schema_name == :gitlab_geo
@@ -30,7 +31,7 @@ namespace :gitlab do
desc "GitLab | DB | Remove all triggers that prevents writes from all databases"
task unlock_writes: :environment do
Gitlab::Database::EachDatabase.each_database_connection do |connection, database_name|
- Gitlab::Database::GitlabSchema.tables_to_schema.each do |table_name, schema_name|
+ Gitlab::Database::LockWritesManager.tables_to_lock(connection) do |table_name, schema_name|
# TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/366834
next if schema_name == :gitlab_geo
diff --git a/lib/tasks/gitlab/feature_categories.rake b/lib/tasks/gitlab/feature_categories.rake
new file mode 100644
index 00000000000..cecfaf3cb36
--- /dev/null
+++ b/lib/tasks/gitlab/feature_categories.rake
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+namespace :gitlab do
+ namespace :feature_categories do
+ desc 'GitLab | Feature categories | Build index page for groups'
+ task index: :environment do
+ require 'pathname'
+
+ controller_actions = Gitlab::RequestEndpoints
+ .all_controller_actions
+ .each_with_object({}) do |(controller, action), hash|
+ feature_category = controller.feature_category_for_action(action).to_s
+
+ hash[feature_category] ||= []
+ hash[feature_category] << {
+ klass: controller.to_s,
+ action: action,
+ source_location: source_location(controller, action)
+ }
+ end
+
+ endpoints = Gitlab::RequestEndpoints.all_api_endpoints.each_with_object({}) do |route, hash|
+ klass = route.app.options[:for]
+ path = API::Base.path_for_app(route.app)
+ feature_category = klass.feature_category_for_action(path).to_s
+
+ hash[feature_category] ||= []
+ hash[feature_category] << {
+ klass: klass.to_s,
+ action: path,
+ source_location: source_location(klass)
+ }
+ end
+
+ workers = Gitlab::SidekiqConfig.workers_for_all_queues_yml.flatten.each_with_object({}) do |worker, hash|
+ feature_category = worker.get_feature_category.to_s
+
+ next unless worker.klass.name
+
+ hash[feature_category] ||= []
+ hash[feature_category] << {
+ klass: worker.klass.name,
+ source_location: source_location(worker.klass.name)
+ }
+ end
+
+ database_tables = Dir['db/docs/*.yml'].each_with_object({}) do |file, hash|
+ yaml = YAML.safe_load(File.read(file))
+ table_name = yaml['table_name']
+
+ yaml['feature_categories'].each do |feature_category|
+ hash[feature_category] ||= []
+ hash[feature_category] << table_name
+ end
+ end
+
+ puts YAML.dump('controller_actions' => controller_actions,
+ 'api_endpoints' => endpoints,
+ 'sidekiq_workers' => workers,
+ 'database_tables' => database_tables)
+ end
+
+ def source_location(klass, method = nil)
+ file, line =
+ if method && klass.method_defined?(method)
+ klass.instance_method(method).source_location
+ else
+ Kernel.const_source_location(klass.to_s)
+ end
+
+ relative = Pathname.new(file).relative_path_from(Rails.root).to_s
+
+ if relative.starts_with?('../') || relative.starts_with?('/')
+ nil
+ else
+ [relative, line]
+ end
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake
index 161c7dd38ac..4f7053b7629 100644
--- a/lib/tasks/gitlab/info.rake
+++ b/lib/tasks/gitlab/info.rake
@@ -85,11 +85,9 @@ namespace :gitlab do
puts ""
puts "GitLab Shell".color(:yellow)
puts "Version:\t#{Gitlab::Shell.version || "unknown".color(:red)}"
- puts "Repository storage paths:"
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab.config.repositories.storages.each do |name, repository_storage|
- puts "- #{name}: \t#{repository_storage.legacy_disk_path}"
- end
+ puts "Repository storages:"
+ Gitlab.config.repositories.storages.each do |name, repository_storage|
+ puts "- #{name}: \t#{repository_storage.gitaly_address}"
end
puts "GitLab Shell path:\t\t#{Gitlab.config.gitlab_shell.path}"
end
diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index cf9876366aa..a5dcb23450f 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -53,13 +53,11 @@ namespace :gitlab do
path_to_repo = project.repository.path_to_repo
if File.exist?(path_to_repo)
print '-'
- else
- if Gitlab::Shell.new.create_repository(project.repository_storage,
+ elsif Gitlab::Shell.new.create_repository(project.repository_storage,
project.disk_path)
- print '.'
- else
- print 'F'
- end
+ print '.'
+ else
+ print 'F'
end
end
end
@@ -81,7 +79,7 @@ namespace :gitlab do
authorized_keys.clear
- Key.find_in_batches(batch_size: 1000) do |keys|
+ Key.auth.find_in_batches(batch_size: 1000) do |keys|
unless authorized_keys.batch_add_keys(keys)
puts "Failed to add keys...".color(:red)
exit 1
diff --git a/lib/tasks/gitlab/sidekiq.rake b/lib/tasks/gitlab/sidekiq.rake
index dc472305304..34ef4b139c3 100644
--- a/lib/tasks/gitlab/sidekiq.rake
+++ b/lib/tasks/gitlab/sidekiq.rake
@@ -10,21 +10,21 @@ namespace :gitlab do
desc 'GitLab | Sidekiq | Migrate jobs in the scheduled set to new queue names'
task schedule: :environment do
::Gitlab::SidekiqMigrateJobs
- .new(::Gitlab::SidekiqConfig.worker_queue_mappings, logger: Logger.new($stdout) )
+ .new(::Gitlab::SidekiqConfig.worker_queue_mappings, logger: Logger.new($stdout))
.migrate_set('schedule')
end
desc 'GitLab | Sidekiq | Migrate jobs in the retry set to new queue names'
task retry: :environment do
::Gitlab::SidekiqMigrateJobs
- .new(::Gitlab::SidekiqConfig.worker_queue_mappings, logger: Logger.new($stdout) )
+ .new(::Gitlab::SidekiqConfig.worker_queue_mappings, logger: Logger.new($stdout))
.migrate_set('retry')
end
desc 'GitLab | Sidekiq | Migrate jobs in queues outside of routing rules'
task queued: :environment do
::Gitlab::SidekiqMigrateJobs
- .new(::Gitlab::SidekiqConfig.worker_queue_mappings, logger: Logger.new($stdout) )
+ .new(::Gitlab::SidekiqConfig.worker_queue_mappings, logger: Logger.new($stdout))
.migrate_queues
end
end
@@ -130,5 +130,10 @@ namespace :gitlab do
end
end
end
+
+ namespace :queues do
+ desc 'GitLab | Sidekiq | Validate all_queues.yml and sidekiq_queues.yml match worker definitions'
+ task check: ['gitlab:sidekiq:all_queues_yml:check', 'gitlab:sidekiq:sidekiq_queues_yml:check', :environment]
+ end
end
end
diff --git a/lib/tasks/gitlab/tw/codeowners.rake b/lib/tasks/gitlab/tw/codeowners.rake
index 7a2dee3e2e4..ec2ea623e02 100644
--- a/lib/tasks/gitlab/tw/codeowners.rake
+++ b/lib/tasks/gitlab/tw/codeowners.rake
@@ -81,6 +81,10 @@ namespace :tw do
CodeOwnerRule.new('Workspace', '@lciutacu')
].freeze
+ ERRORS_EXCLUDED_FILES = [
+ '/doc/architecture'
+ ].freeze
+
CODEOWNERS_BLOCK_BEGIN = "# Begin rake-managed-docs-block"
CODEOWNERS_BLOCK_END = "# End rake-managed-docs-block"
@@ -105,16 +109,17 @@ namespace :tw do
Dir.glob(path) do |file|
yaml_data = YAML.load_file(file)
document = Document.new(yaml_data['group'], yaml_data['redirect_to'])
+ relative_file = file.delete_prefix(Dir.pwd)
if document.missing_metadata?
- errors << file
+ errors << relative_file unless ERRORS_EXCLUDED_FILES.any? { |element| relative_file.starts_with?(element) }
next
end
writer = writer_for_group(document.group)
next unless writer
- mappings << DocumentOwnerMapping.new(file.delete_prefix(Dir.pwd), writer) if document.has_a_valid_group?
+ mappings << DocumentOwnerMapping.new(relative_file, writer) if document.has_a_valid_group?
end
deduplicated_mappings = Set.new
@@ -139,10 +144,16 @@ namespace :tw do
File.write(codeowners_path, new_codeowners_content)
+ if current_codeowners_content == new_codeowners_content
+ puts "~ CODEOWNERS already up to date".color(:yellow)
+ else
+ puts "✓ CODEOWNERS updated".color(:green)
+ end
+
if errors.present?
- puts "-----"
- puts "ERRORS - the following files are missing the correct metadata:"
- errors.map { |file| puts file.gsub(Dir.pwd, ".") }
+ puts ""
+ puts "✘ Files with missing metadata found:".color(:red)
+ errors.map { |file| puts file }
end
end
end
diff --git a/lib/tasks/gitlab/update_templates.rake b/lib/tasks/gitlab/update_templates.rake
index d67ad340007..e87f478ac42 100644
--- a/lib/tasks/gitlab/update_templates.rake
+++ b/lib/tasks/gitlab/update_templates.rake
@@ -24,10 +24,11 @@ namespace :gitlab do
raise "This rake task is not meant for production instances"
end
- admin = User.find_by(admin: true)
+ # Find an admin user with an SSH key
+ admin = User.where(admin: true).joins(:keys).where.not(keys: { id: nil }).take
unless admin
- raise "No admin user could be found"
+ raise "No admin user with SSH key could be found"
end
tmp_namespace_path = "tmp-project-import-#{Time.now.to_i}"
@@ -73,6 +74,8 @@ namespace :gitlab do
Commit SHA: #{commit_sha}
MSG
+ local_remote = project.ssh_url_to_repo
+
Dir.mktmpdir do |tmpdir|
Dir.chdir(tmpdir) do
Gitlab::TaskHelpers.run_command!(['wget', project_archive_uri, '-O', 'archive.tar.gz'])
@@ -80,13 +83,9 @@ namespace :gitlab do
extracted_project_basename = Dir['*/'].first
Dir.chdir(extracted_project_basename) do
Gitlab::TaskHelpers.run_command!(%w(git init --initial-branch=master))
+ Gitlab::TaskHelpers.run_command!(%W(git remote add origin #{local_remote}))
Gitlab::TaskHelpers.run_command!(%w(git add .))
Gitlab::TaskHelpers.run_command!(['git', 'commit', '--author', 'GitLab <root@localhost>', '--message', commit_message])
-
- # Hacky workaround to push to the project in a way that works with both GDK and the test environment
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab::TaskHelpers.run_command!(['git', 'remote', 'add', 'origin', "file://#{project.repository.raw.path}"])
- end
Gitlab::TaskHelpers.run_command!(['git', 'push', '-u', 'origin', 'master'])
end
end
diff --git a/lib/tasks/gitlab/usage_data.rake b/lib/tasks/gitlab/usage_data.rake
index 159b70cd673..32db5e2dff6 100644
--- a/lib/tasks/gitlab/usage_data.rake
+++ b/lib/tasks/gitlab/usage_data.rake
@@ -51,9 +51,12 @@ namespace :gitlab do
desc 'GitLab | UsageDataMetrics | Generate raw SQL metrics queries for RSpec'
task generate_sql_metrics_queries: :environment do
+ require 'active_support/testing/time_helpers'
+ include ActiveSupport::Testing::TimeHelpers
+
path = Rails.root.join('tmp', 'test')
- queries = Timecop.freeze(2021, 1, 1) do
+ queries = travel_to(Time.utc(2021, 1, 1)) do
Gitlab::Usage::ServicePingReport.for(output: :metrics_queries)
end
diff --git a/lib/version_check.rb b/lib/version_check.rb
index 35014f3ddf0..9b7ab440328 100644
--- a/lib/version_check.rb
+++ b/lib/version_check.rb
@@ -5,6 +5,9 @@ require "base64"
class VersionCheck
include ReactiveCaching
+ # Increment when format of cache value is changed
+ CACHE_VERSION = 1
+
## Version Check Reactive Caching
## This cache stores the external API response from https://version.gitlab.com
##
@@ -61,7 +64,7 @@ class VersionCheck
end
def id
- Gitlab::VERSION
+ [Gitlab::VERSION, Gitlab.revision, CACHE_VERSION].join('-')
end
def calculate_reactive_cache(*)
@@ -69,13 +72,19 @@ class VersionCheck
case response&.code
when 200
- response.body
+ Gitlab::Json.parse(response.body)
+ else
+ { error: 'version check failed', status: response&.code }
end
+ rescue JSON::ParserError
+ { error: 'parsing version check response failed', status: response&.code }
end
def response
with_reactive_cache do |data|
- Gitlab::Json.parse(data) if data
+ raise InvalidateReactiveCache if !data.is_a?(Hash) || data[:error]
+
+ data
end
end
end
diff --git a/locale/am_ET/gitlab.po b/locale/am_ET/gitlab.po
index f222ced9e73..598caf2511d 100644
--- a/locale/am_ET/gitlab.po
+++ b/locale/am_ET/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: am\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:24\n"
+"PO-Revision-Date: 2022-12-10 06:44\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] "%d ተጨማሪ አስተያየት"
msgstr[1] "%d ተጨማሪ አስተያየቶች"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr "%{group_name} በቡድን የሚተዳደሩ መለያዎችን ይጠá
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/ar_SA/gitlab.po b/locale/ar_SA/gitlab.po
index 097f8d01003..c7f019c4b7b 100644
--- a/locale/ar_SA/gitlab.po
+++ b/locale/ar_SA/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: ar\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:22\n"
+"PO-Revision-Date: 2022-12-10 06:39\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -559,6 +559,15 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -826,6 +835,12 @@ msgstr[5] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -1066,13 +1081,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -1270,9 +1285,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -1327,7 +1339,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1528,6 +1540,15 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1567,6 +1588,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1690,12 +1714,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2896,6 +2926,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2983,6 +3016,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -3178,6 +3214,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -3478,7 +3517,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3541,9 +3580,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -5074,6 +5110,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -5191,9 +5230,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5764,6 +5800,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -6184,6 +6223,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -6229,6 +6271,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -6460,9 +6505,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -7498,6 +7540,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7612,9 +7663,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7624,6 +7672,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7645,7 +7696,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7666,9 +7720,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7690,6 +7741,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7723,6 +7777,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7735,6 +7795,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7789,6 +7852,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -8599,6 +8665,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8647,6 +8716,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8776,9 +8848,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8794,7 +8863,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8818,6 +8887,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -9061,9 +9133,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -9076,6 +9154,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -9088,6 +9169,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -9097,6 +9181,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -9106,6 +9193,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9805,6 +9898,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10630,6 +10726,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10654,6 +10753,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10669,15 +10771,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10843,7 +10954,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10963,6 +11074,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -11002,12 +11116,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -11284,9 +11392,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -11470,9 +11575,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11785,6 +11887,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11917,9 +12022,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11998,6 +12100,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -12034,6 +12139,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -12259,6 +12400,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12673,12 +12817,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12691,9 +12841,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12712,6 +12874,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12745,6 +12910,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12805,6 +12973,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12952,6 +13123,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -13327,6 +13501,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -13384,6 +13561,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -14200,6 +14383,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -14236,6 +14422,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -14344,9 +14533,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -14515,6 +14710,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15844,6 +16042,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -16207,9 +16408,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -16303,6 +16501,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -16618,6 +16819,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16783,6 +16993,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16813,9 +17026,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16834,9 +17059,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16891,13 +17113,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -17221,9 +17446,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -17380,9 +17602,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -17641,6 +17860,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17860,15 +18082,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17905,6 +18118,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -18049,10 +18265,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -18136,15 +18361,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -18187,6 +18403,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -19042,6 +19261,15 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -19291,6 +19519,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -19318,6 +19549,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -19495,6 +19729,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -19588,6 +19825,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19777,7 +20020,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19864,6 +20107,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19969,6 +20215,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -20140,7 +20389,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -20161,9 +20410,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -20293,6 +20539,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -20302,9 +20551,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20794,12 +21040,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20833,9 +21085,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -21004,6 +21253,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -21028,6 +21280,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -21067,7 +21322,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -21109,6 +21364,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -21121,9 +21382,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -21481,6 +21751,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -22048,12 +22321,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -22372,6 +22651,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -22648,6 +22930,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -22681,6 +22966,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -23029,6 +23317,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -23077,7 +23368,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -23548,9 +23839,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -23689,9 +23977,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -24124,9 +24409,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -24277,6 +24559,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -24358,6 +24643,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -24427,9 +24715,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -24508,15 +24793,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -24607,6 +24895,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -24712,6 +25003,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -25411,10 +25705,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -25465,6 +25759,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -25570,6 +25867,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -26068,6 +26368,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -26089,6 +26392,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -27232,6 +27541,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -27295,9 +27607,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -27511,6 +27820,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -27694,6 +28006,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -27736,6 +28051,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -28009,9 +28330,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -28111,6 +28429,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -28789,9 +29110,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -29176,6 +29512,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -29257,9 +29596,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -29365,9 +29701,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -29653,6 +29986,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -29746,6 +30082,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -29761,6 +30100,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -29785,6 +30130,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29857,6 +30214,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29872,6 +30232,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29884,6 +30247,9 @@ msgstr[5] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29938,6 +30304,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29986,9 +30355,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -30013,6 +30379,15 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -30028,6 +30403,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -30097,6 +30475,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -30160,6 +30541,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -30403,6 +30793,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -31318,6 +31732,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -31474,9 +31891,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -31630,6 +32044,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -31714,6 +32131,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -31726,9 +32146,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -31846,16 +32272,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -32218,6 +32653,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -32752,12 +33193,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -32839,9 +33286,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32956,6 +33400,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -33001,6 +33448,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -33130,7 +33580,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -33211,6 +33661,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -33733,9 +34186,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -34411,6 +34861,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -34612,6 +35065,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34951,6 +35407,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -35005,10 +35464,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -35371,6 +35827,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -35626,6 +36085,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -35704,7 +36166,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -35803,6 +36265,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -35875,7 +36340,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -35902,6 +36367,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35923,6 +36391,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -36010,6 +36481,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -36028,6 +36502,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -36067,12 +36544,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -36169,6 +36649,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -36235,6 +36718,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -36283,12 +36769,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -36406,6 +36886,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -36433,13 +36916,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -36481,6 +36967,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -36496,6 +36985,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -36562,6 +37060,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -36589,9 +37093,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -36604,18 +37114,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -36700,6 +37219,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -37261,6 +37783,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -37318,9 +37843,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -37336,6 +37870,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -37372,6 +37909,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -37444,6 +37984,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -37453,12 +37996,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -37528,6 +38080,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -37555,6 +38110,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -37591,6 +38149,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -37600,9 +38161,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -37747,6 +38323,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37972,6 +38551,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -38077,6 +38659,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -38569,6 +39154,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -38695,6 +39283,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -38857,9 +39448,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -39013,7 +39601,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -39028,13 +39616,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -39058,13 +39649,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -39202,10 +39796,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -39247,6 +39841,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -39265,6 +39862,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -39334,6 +39934,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -39727,6 +40330,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -39832,6 +40438,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -39895,6 +40504,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -40636,6 +41248,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -40726,9 +41341,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -41011,6 +41623,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -41611,6 +42229,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -41983,9 +42604,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -42043,9 +42661,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -42091,6 +42706,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -42211,6 +42829,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -42250,6 +42871,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -42343,9 +42967,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -42535,6 +43156,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -42553,7 +43177,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -43027,6 +43651,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -43285,6 +43915,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -43318,9 +43951,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -43435,6 +44065,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -43528,6 +44161,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -43561,6 +44197,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -43576,13 +44215,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -43597,22 +44236,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -43987,6 +44626,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -44023,6 +44668,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -44200,6 +44848,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -44323,6 +44977,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -44404,6 +45061,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -44587,6 +45247,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -44605,18 +45268,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -44644,9 +45301,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -44656,9 +45310,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -44785,10 +45436,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -44797,12 +45445,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -44815,9 +45457,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -44851,7 +45490,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -44860,16 +45499,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -45043,6 +45682,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -45313,9 +45955,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -45505,6 +46144,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -45577,6 +46219,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -45595,6 +46240,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -45643,10 +46291,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -45658,7 +46306,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -45715,6 +46363,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -45724,6 +46387,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -46396,6 +47071,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -46471,9 +47149,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -46489,24 +47164,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -46537,6 +47200,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -46603,9 +47278,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -46654,13 +47326,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -46699,9 +47368,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -47101,6 +47767,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -47110,9 +47779,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -47131,6 +47797,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -47146,9 +47818,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -47173,6 +47842,9 @@ msgstr[5] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -47182,6 +47854,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -47194,16 +47872,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -47212,12 +47893,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -47227,12 +47914,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -47245,9 +47938,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -47311,7 +48013,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -47341,6 +48043,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -47512,10 +48217,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -47554,15 +48259,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -47629,9 +48325,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -47650,18 +48343,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -47743,6 +48430,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -47797,6 +48487,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -47863,6 +48556,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -47914,9 +48610,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -48094,6 +48787,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -48112,7 +48808,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -48148,6 +48844,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -48295,15 +48994,6 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
msgid "Your groups"
msgstr ""
@@ -48343,6 +49033,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -48505,6 +49204,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -48694,6 +49396,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -48751,6 +49456,24 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -48808,10 +49531,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -48982,7 +49708,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -49168,6 +49894,15 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -49333,9 +50068,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -49534,6 +50266,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -49573,6 +50308,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -50086,6 +50827,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -50095,10 +50839,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -50314,6 +51058,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -50377,6 +51124,15 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "running"
msgstr ""
diff --git a/locale/as_IN/gitlab.po b/locale/as_IN/gitlab.po
index a1df2dbdd04..68a1729759f 100644
--- a/locale/as_IN/gitlab.po
+++ b/locale/as_IN/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: as\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:24\n"
+"PO-Revision-Date: 2022-12-10 06:43\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/az_AZ/gitlab.po b/locale/az_AZ/gitlab.po
index 9e1e857d819..5a292577b04 100644
--- a/locale/az_AZ/gitlab.po
+++ b/locale/az_AZ/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: az\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:22\n"
+"PO-Revision-Date: 2022-12-10 06:42\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/ba_RU/gitlab.po b/locale/ba_RU/gitlab.po
index de0d110a7c5..df7afccfeb7 100644
--- a/locale/ba_RU/gitlab.po
+++ b/locale/ba_RU/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: ba\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:24\n"
+"PO-Revision-Date: 2022-12-10 06:43\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -289,6 +289,10 @@ msgid "%d more comment"
msgid_plural "%d more comments"
msgstr[0] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -426,6 +430,12 @@ msgstr[0] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -616,13 +626,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -815,9 +825,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -867,7 +874,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1003,6 +1010,10 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1042,6 +1053,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1160,12 +1174,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2231,6 +2251,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2318,6 +2341,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2513,6 +2539,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2813,7 +2842,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -2876,9 +2905,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4404,6 +4430,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4521,9 +4550,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5054,6 +5080,9 @@ msgid "Are you sure you want to import %d repository?"
msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5459,6 +5488,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5504,6 +5536,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5735,9 +5770,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6753,6 +6785,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -6867,9 +6908,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -6879,6 +6917,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -6900,7 +6941,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -6921,9 +6965,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -6945,6 +6986,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -6978,6 +7022,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -6990,6 +7040,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7044,6 +7097,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7849,6 +7905,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -7897,6 +7956,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8016,9 +8078,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8034,7 +8093,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8058,6 +8117,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8301,9 +8363,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8316,6 +8384,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8328,6 +8399,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8337,6 +8411,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8346,6 +8423,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9035,6 +9118,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -9855,6 +9941,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -9879,6 +9968,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -9894,15 +9986,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10068,7 +10169,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10188,6 +10289,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10227,12 +10331,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10494,9 +10592,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10680,9 +10775,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -10995,6 +11087,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11127,9 +11222,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11208,6 +11300,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11244,6 +11339,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11469,6 +11600,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -11878,12 +12012,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -11896,9 +12036,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -11917,6 +12069,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -11950,6 +12105,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12010,6 +12168,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12157,6 +12318,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12527,6 +12691,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12584,6 +12751,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13370,6 +13543,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13406,6 +13582,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13509,9 +13688,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13675,6 +13860,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -14984,6 +15172,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15347,9 +15538,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15443,6 +15631,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15758,6 +15949,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -15918,6 +16118,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -15948,9 +16151,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -15969,9 +16184,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16026,13 +16238,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16346,9 +16561,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16505,9 +16717,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16761,6 +16970,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -16980,15 +17192,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17025,6 +17228,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17169,10 +17375,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17251,10 +17466,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17297,6 +17508,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18142,6 +18356,10 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18391,6 +18609,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18418,6 +18639,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18595,6 +18819,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18688,6 +18915,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -18877,7 +19110,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -18964,6 +19197,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19069,6 +19305,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19240,7 +19479,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19261,9 +19500,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19393,6 +19629,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19402,9 +19641,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -19874,12 +20110,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -19913,9 +20155,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20084,6 +20323,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20108,6 +20350,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20147,7 +20392,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20189,6 +20434,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20201,9 +20452,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20546,6 +20806,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21113,12 +21376,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21437,6 +21706,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21708,6 +21980,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21741,6 +22016,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22089,6 +22367,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22137,7 +22418,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22603,9 +22884,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22744,9 +23022,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23179,9 +23454,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23332,6 +23604,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23413,6 +23688,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23482,9 +23760,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23558,15 +23833,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23652,6 +23930,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23757,6 +24038,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24426,10 +24710,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24480,6 +24764,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24585,6 +24872,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25083,6 +25373,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25104,6 +25397,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26227,6 +26526,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26290,9 +26592,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26501,6 +26800,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26679,6 +26981,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26721,6 +27026,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -26994,9 +27305,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27096,6 +27404,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27749,9 +28060,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28131,6 +28457,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28207,9 +28536,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28315,9 +28641,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28603,6 +28926,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28691,6 +29017,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28706,6 +29035,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28730,6 +29065,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -28802,6 +29149,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -28817,6 +29167,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28824,6 +29177,9 @@ msgstr[0] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -28878,6 +29234,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -28926,9 +29285,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -28948,6 +29304,10 @@ msgid "PackageRegistry|You are about to delete 1 asset. This operation is irreve
msgid_plural "PackageRegistry|You are about to delete %d assets. This operation is irreversible."
msgstr[0] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -28963,6 +29323,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29032,6 +29395,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29095,6 +29461,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29338,6 +29713,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30253,6 +30652,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30409,9 +30811,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30565,6 +30964,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30649,6 +31051,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30661,9 +31066,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30781,16 +31192,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31153,6 +31573,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31687,12 +32113,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31774,9 +32206,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -31891,6 +32320,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -31936,6 +32368,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32065,7 +32500,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32146,6 +32581,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32668,9 +33106,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33341,6 +33776,9 @@ msgid "Refreshing in a second to show the updated status..."
msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33537,6 +33975,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -33876,6 +34317,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -33930,10 +34374,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34266,6 +34707,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34511,6 +34955,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34584,7 +35031,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34678,6 +35125,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34735,7 +35185,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34762,6 +35212,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -34783,6 +35236,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -34865,6 +35321,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -34883,6 +35342,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -34922,12 +35384,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35019,6 +35484,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35085,6 +35553,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35133,12 +35604,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35251,6 +35716,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35278,13 +35746,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35326,6 +35797,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35341,6 +35815,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35407,6 +35890,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35434,9 +35923,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35449,18 +35944,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35545,6 +36049,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36051,6 +36558,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36108,9 +36618,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36126,6 +36645,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36162,6 +36684,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36234,6 +36759,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36243,12 +36771,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36318,6 +36855,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36345,6 +36885,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36381,6 +36924,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36390,9 +36936,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36537,6 +37098,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -36762,6 +37326,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -36867,6 +37434,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37354,6 +37924,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37480,6 +38053,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37637,9 +38213,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -37793,7 +38366,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -37808,13 +38381,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -37838,13 +38414,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -37982,10 +38561,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38027,6 +38606,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38045,6 +38627,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38114,6 +38699,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38507,6 +39095,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38612,6 +39203,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38675,6 +39269,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39411,6 +40008,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39501,9 +40101,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -39786,6 +40383,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40361,6 +40964,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40723,9 +41329,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -40783,9 +41386,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -40831,6 +41431,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -40951,6 +41554,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -40990,6 +41596,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41083,9 +41692,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41275,6 +41881,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41293,7 +41902,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -41767,6 +42376,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42015,6 +42630,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42048,9 +42666,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42165,6 +42780,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42258,6 +42876,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42291,6 +42912,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42306,13 +42930,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42327,22 +42951,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42712,6 +43336,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -42748,6 +43378,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -42925,6 +43558,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43048,6 +43687,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43129,6 +43771,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43312,6 +43957,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43330,18 +43978,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43369,9 +44011,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43381,9 +44020,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43510,10 +44146,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43522,12 +44155,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43540,9 +44167,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43576,7 +44200,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43585,16 +44209,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -43768,6 +44392,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44033,9 +44660,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44225,6 +44849,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44297,6 +44924,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44315,6 +44945,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44363,10 +44996,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44378,7 +45011,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44435,6 +45068,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44444,6 +45092,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45101,6 +45761,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45176,9 +45839,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45194,24 +45854,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45242,6 +45890,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45308,9 +45968,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45359,13 +46016,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push events"
-msgstr ""
-
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45404,9 +46058,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -45796,6 +46447,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -45805,9 +46459,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -45826,6 +46477,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -45841,9 +46498,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -45863,6 +46517,9 @@ msgstr[0] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -45872,6 +46529,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -45884,16 +46547,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -45902,12 +46568,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -45917,12 +46589,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -45935,9 +46613,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46001,7 +46688,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46031,6 +46718,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46197,10 +46887,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46239,10 +46929,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46309,9 +46995,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46330,18 +47013,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46423,6 +47100,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46477,6 +47157,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46538,6 +47221,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46589,9 +47275,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -46769,6 +47452,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -46787,7 +47473,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -46823,6 +47509,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -46965,10 +47654,6 @@ msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-
msgid "Your groups"
msgstr ""
@@ -47008,6 +47693,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47165,6 +47859,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47339,6 +48036,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47391,6 +48091,14 @@ msgid "change"
msgid_plural "changes"
msgstr[0] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47448,10 +48156,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47607,7 +48318,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -47783,6 +48494,10 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -47938,9 +48653,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48119,6 +48831,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48158,6 +48873,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48646,6 +49367,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48655,10 +49379,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -48859,6 +49583,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -48912,6 +49639,10 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+
msgid "running"
msgstr ""
diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po
index 0489c2facdd..d5f753ef86c 100644
--- a/locale/bg/gitlab.po
+++ b/locale/bg/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: bg\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:22\n"
+"PO-Revision-Date: 2022-12-10 06:39\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "Клони"
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr "протича в момента"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr "Етикет"
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr "От Ñъздаването на проблема до внедрÑването в крайната верÑиÑ"
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr "Ðов клон"
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "Ðов проблем"
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr "ЗаÑвка за доÑтъп"
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr "Преминаване към клон/етикет"
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr "Без звезда"
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr "ÐÑма доÑтатъчно данни за този етап."
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr "ОттеглÑне на заÑвката за доÑтъп"
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr "Вашето име"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/bg/gitlab.po.time_stamp b/locale/bg/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/bg/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/bn_BD/gitlab.po b/locale/bn_BD/gitlab.po
index 16cc8a5598c..6ed45ca92ec 100644
--- a/locale/bn_BD/gitlab.po
+++ b/locale/bn_BD/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: bn\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:21\n"
+"PO-Revision-Date: 2022-12-10 06:42\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/bn_IN/gitlab.po b/locale/bn_IN/gitlab.po
index 040b870aac8..3fa97ab8505 100644
--- a/locale/bn_IN/gitlab.po
+++ b/locale/bn_IN/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: bn-IN\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:24\n"
+"PO-Revision-Date: 2022-12-10 06:44\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/br_FR/gitlab.po b/locale/br_FR/gitlab.po
index 56634d5dde3..1b89aae59d6 100644
--- a/locale/br_FR/gitlab.po
+++ b/locale/br_FR/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: br-FR\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:25\n"
+"PO-Revision-Date: 2022-12-10 06:43\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -505,6 +505,14 @@ msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -746,6 +754,12 @@ msgstr[4] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -976,13 +990,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -1179,9 +1193,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -1235,7 +1246,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1423,6 +1434,14 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1462,6 +1481,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1584,12 +1606,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2763,6 +2791,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2850,6 +2881,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -3045,6 +3079,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -3345,7 +3382,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3408,9 +3445,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4940,6 +4974,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -5057,9 +5094,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5622,6 +5656,9 @@ msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -6039,6 +6076,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -6084,6 +6124,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -6315,9 +6358,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -7349,6 +7389,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7463,9 +7512,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7475,6 +7521,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7496,7 +7545,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7517,9 +7569,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7541,6 +7590,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7574,6 +7626,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7586,6 +7644,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7640,6 +7701,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -8449,6 +8513,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8497,6 +8564,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8624,9 +8694,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8642,7 +8709,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8666,6 +8733,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8909,9 +8979,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8924,6 +9000,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8936,6 +9015,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8945,6 +9027,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8954,6 +9039,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9651,6 +9742,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10475,6 +10569,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10499,6 +10596,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10514,15 +10614,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10688,7 +10797,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10808,6 +10917,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10847,12 +10959,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -11126,9 +11232,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -11312,9 +11415,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11627,6 +11727,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11759,9 +11862,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11840,6 +11940,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11876,6 +11979,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -12101,6 +12240,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12514,12 +12656,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12532,9 +12680,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12553,6 +12713,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12586,6 +12749,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12646,6 +12812,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12793,6 +12962,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -13167,6 +13339,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -13224,6 +13399,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -14034,6 +14215,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -14070,6 +14254,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -14177,9 +14364,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -14347,6 +14540,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15672,6 +15868,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -16035,9 +16234,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -16131,6 +16327,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -16446,6 +16645,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16610,6 +16818,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16640,9 +16851,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16661,9 +16884,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16718,13 +16938,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -17046,9 +17269,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -17205,9 +17425,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -17465,6 +17682,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17684,15 +17904,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17729,6 +17940,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17873,10 +18087,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17959,14 +18182,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -18009,6 +18224,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18862,6 +19080,14 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -19111,6 +19337,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -19138,6 +19367,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -19315,6 +19547,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -19408,6 +19643,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19597,7 +19838,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19684,6 +19925,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19789,6 +20033,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19960,7 +20207,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19981,9 +20228,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -20113,6 +20357,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -20122,9 +20369,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20610,12 +20854,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20649,9 +20899,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20820,6 +21067,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20844,6 +21094,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20883,7 +21136,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20925,6 +21178,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20937,9 +21196,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -21294,6 +21562,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21861,12 +22132,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -22185,6 +22462,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -22460,6 +22740,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -22493,6 +22776,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22841,6 +23127,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22889,7 +23178,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -23359,9 +23648,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -23500,9 +23786,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23935,9 +24218,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -24088,6 +24368,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -24169,6 +24452,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -24238,9 +24524,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -24318,15 +24601,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -24416,6 +24702,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -24521,6 +24810,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -25214,10 +25506,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -25268,6 +25560,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -25373,6 +25668,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25871,6 +26169,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25892,6 +26193,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -27031,6 +27338,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -27094,9 +27404,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -27309,6 +27616,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -27491,6 +27801,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -27533,6 +27846,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27806,9 +28125,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27908,6 +28224,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -28581,9 +28900,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28967,6 +29301,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -29047,9 +29384,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -29155,9 +29489,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -29443,6 +29774,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -29535,6 +29869,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -29550,6 +29887,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -29574,6 +29917,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29646,6 +30001,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29661,6 +30019,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29672,6 +30033,9 @@ msgstr[4] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29726,6 +30090,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29774,9 +30141,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29800,6 +30164,14 @@ msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29815,6 +30187,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29884,6 +30259,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29947,6 +30325,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -30190,6 +30577,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -31105,6 +31516,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -31261,9 +31675,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -31417,6 +31828,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -31501,6 +31915,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -31513,9 +31930,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -31633,16 +32056,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -32005,6 +32437,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -32539,12 +32977,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -32626,9 +33070,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32743,6 +33184,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32788,6 +33232,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32917,7 +33364,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32998,6 +33445,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -33520,9 +33970,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -34197,6 +34644,9 @@ msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -34397,6 +34847,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34736,6 +35189,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34790,10 +35246,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -35150,6 +35603,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -35403,6 +35859,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -35480,7 +35939,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -35578,6 +36037,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -35647,7 +36109,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -35674,6 +36136,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35695,6 +36160,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35781,6 +36249,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35799,6 +36270,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35838,12 +36312,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35939,6 +36416,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -36005,6 +36485,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -36053,12 +36536,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -36175,6 +36652,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -36202,13 +36682,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -36250,6 +36733,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -36265,6 +36751,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -36331,6 +36826,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -36358,9 +36859,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -36373,18 +36880,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -36469,6 +36985,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -37019,6 +37538,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -37076,9 +37598,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -37094,6 +37625,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -37130,6 +37664,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -37202,6 +37739,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -37211,12 +37751,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -37286,6 +37835,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -37313,6 +37865,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -37349,6 +37904,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -37358,9 +37916,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -37505,6 +38078,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37730,6 +38306,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37835,6 +38414,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -38326,6 +38908,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -38452,6 +39037,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -38613,9 +39201,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38769,7 +39354,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38784,13 +39369,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38814,13 +39402,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38958,10 +39549,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -39003,6 +39594,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -39021,6 +39615,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -39090,6 +39687,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -39483,6 +40083,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -39588,6 +40191,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -39651,6 +40257,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -40391,6 +41000,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -40481,9 +41093,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40766,6 +41375,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -41361,6 +41976,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -41731,9 +42349,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41791,9 +42406,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41839,6 +42451,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41959,6 +42574,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41998,6 +42616,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -42091,9 +42712,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -42283,6 +42901,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -42301,7 +42922,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42775,6 +43396,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -43031,6 +43658,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -43064,9 +43694,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -43181,6 +43808,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -43274,6 +43904,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -43307,6 +43940,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -43322,13 +43958,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -43343,22 +43979,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -43732,6 +44368,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43768,6 +44410,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43945,6 +44590,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -44068,6 +44719,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -44149,6 +44803,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -44332,6 +44989,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -44350,18 +45010,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -44389,9 +45043,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -44401,9 +45052,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -44530,10 +45178,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -44542,12 +45187,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -44560,9 +45199,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -44596,7 +45232,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -44605,16 +45241,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44788,6 +45424,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -45057,9 +45696,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -45249,6 +45885,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -45321,6 +45960,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -45339,6 +45981,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -45387,10 +46032,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -45402,7 +46047,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -45459,6 +46104,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -45468,6 +46128,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -46137,6 +46809,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -46212,9 +46887,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -46230,24 +46902,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -46278,6 +46938,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -46344,9 +47016,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -46395,13 +47064,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push events"
-msgstr ""
-
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -46440,9 +47106,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46840,6 +47503,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46849,9 +47515,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46870,6 +47533,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46885,9 +47554,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46911,6 +47577,9 @@ msgstr[4] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46920,6 +47589,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46932,16 +47607,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46950,12 +47628,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46965,12 +47649,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46983,9 +47673,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -47049,7 +47748,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -47079,6 +47778,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -47249,10 +47951,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -47291,14 +47993,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -47365,9 +48059,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -47386,18 +48077,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -47479,6 +48164,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -47533,6 +48221,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -47598,6 +48289,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -47649,9 +48343,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47829,6 +48520,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47847,7 +48541,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47883,6 +48577,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -48029,14 +48726,6 @@ msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-
msgid "Your groups"
msgstr ""
@@ -48076,6 +48765,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -48237,6 +48935,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -48423,6 +49124,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -48479,6 +49183,22 @@ msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -48536,10 +49256,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -48707,7 +49430,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48891,6 +49614,14 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -49054,9 +49785,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -49251,6 +49979,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -49290,6 +50021,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -49798,6 +50535,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -49807,10 +50547,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -50023,6 +50763,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -50084,6 +50827,14 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+
msgid "running"
msgstr ""
diff --git a/locale/bs_BA/gitlab.po b/locale/bs_BA/gitlab.po
index 3c545ed44ca..7a6f0f840d4 100644
--- a/locale/bs_BA/gitlab.po
+++ b/locale/bs_BA/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: bs\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:24\n"
+"PO-Revision-Date: 2022-12-10 06:43\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -397,6 +397,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -586,6 +592,12 @@ msgstr[2] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -796,13 +808,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -997,9 +1009,6 @@ msgstr "%{openedEpics} otvoreno, %{closedEpics} zatvoreno"
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr "%{openedIssues} otvoreno, %{closedIssues} zatvoreno"
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -1051,7 +1060,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1213,6 +1222,12 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1252,6 +1267,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1372,12 +1390,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2497,6 +2521,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2584,6 +2611,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "Dodatne minute"
@@ -2779,6 +2809,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -3079,7 +3112,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3142,9 +3175,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4672,6 +4702,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4789,9 +4822,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5338,6 +5368,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5749,6 +5782,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5794,6 +5830,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -6025,9 +6064,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -7051,6 +7087,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7165,9 +7210,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "Grane"
@@ -7177,6 +7219,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7198,7 +7243,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7219,9 +7267,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7243,6 +7288,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7276,6 +7324,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7288,6 +7342,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7342,6 +7399,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -8149,6 +8209,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8197,6 +8260,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8320,9 +8386,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8338,7 +8401,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8362,6 +8425,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8605,9 +8671,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8620,6 +8692,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8632,6 +8707,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8641,6 +8719,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8650,6 +8731,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9343,6 +9430,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10165,6 +10255,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10189,6 +10282,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10204,15 +10300,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10378,7 +10483,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10498,6 +10603,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10537,12 +10645,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10810,9 +10912,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10996,9 +11095,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11311,6 +11407,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11443,9 +11542,6 @@ msgstr "Kreiraj zadatak"
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11524,6 +11620,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11560,6 +11659,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11785,6 +11920,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12196,12 +12334,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12214,9 +12358,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12235,6 +12391,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12268,6 +12427,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12328,6 +12490,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12475,6 +12640,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12847,6 +13015,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12904,6 +13075,12 @@ msgstr "Standardni prvi dan sedmice"
msgid "Default first day of the week in calendars and date pickers."
msgstr "Standardni prvi dan sedmice u kalendarima i izbornicima datuma."
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13702,6 +13879,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13738,6 +13918,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13843,9 +14026,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -14011,6 +14200,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15328,6 +15520,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15691,9 +15886,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr "Ukloni zadatak"
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15787,6 +15979,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -16102,6 +16297,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16264,6 +16468,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16294,9 +16501,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16315,9 +16534,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16372,13 +16588,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16696,9 +16915,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16855,9 +17071,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -17113,6 +17326,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17332,15 +17548,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17377,6 +17584,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17521,10 +17731,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17605,12 +17824,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17653,6 +17866,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18502,6 +18718,12 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18751,6 +18973,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18778,6 +19003,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18955,6 +19183,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -19048,6 +19279,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19237,7 +19474,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19324,6 +19561,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19429,6 +19669,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19600,7 +19843,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19621,9 +19864,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19753,6 +19993,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19762,9 +20005,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20242,12 +20482,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20281,9 +20527,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20452,6 +20695,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20476,6 +20722,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20515,7 +20764,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20557,6 +20806,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20569,9 +20824,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20920,6 +21184,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21487,12 +21754,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21811,6 +22084,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -22084,6 +22360,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -22117,6 +22396,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22465,6 +22747,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22513,7 +22798,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22981,9 +23266,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -23122,9 +23404,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23557,9 +23836,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23710,6 +23986,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23791,6 +24070,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23860,9 +24142,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23938,15 +24217,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -24034,6 +24316,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -24139,6 +24424,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24820,10 +25108,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24874,6 +25162,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24979,6 +25270,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25477,6 +25771,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25498,6 +25795,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26629,6 +26932,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26692,9 +26998,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26905,6 +27208,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -27085,6 +27391,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -27127,6 +27436,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "Novi zadatak"
@@ -27400,9 +27715,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27502,6 +27814,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -28165,9 +28480,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28549,6 +28879,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28627,9 +28960,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28735,9 +29065,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -29023,6 +29350,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -29113,6 +29443,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -29128,6 +29461,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -29152,6 +29491,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29224,6 +29575,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29239,6 +29593,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29248,6 +29605,9 @@ msgstr[2] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29302,6 +29662,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29350,9 +29713,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29374,6 +29734,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29389,6 +29755,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29458,6 +29827,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29521,6 +29893,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29764,6 +30145,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30679,6 +31084,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30835,9 +31243,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30991,6 +31396,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -31075,6 +31483,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -31087,9 +31498,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -31207,16 +31624,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31579,6 +32005,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -32113,12 +32545,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -32200,9 +32638,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32317,6 +32752,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32362,6 +32800,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32491,7 +32932,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32572,6 +33013,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -33094,9 +33538,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33769,6 +34210,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33967,6 +34411,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34306,6 +34753,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34360,10 +34810,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34708,6 +35155,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34957,6 +35407,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -35032,7 +35485,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -35128,6 +35581,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -35191,7 +35647,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -35218,6 +35674,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35239,6 +35698,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35323,6 +35785,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35341,6 +35806,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35380,12 +35848,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35479,6 +35950,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35545,6 +36019,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35593,12 +36070,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35713,6 +36184,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35740,13 +36214,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35788,6 +36265,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35803,6 +36283,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35869,6 +36358,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35896,9 +36391,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35911,18 +36412,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -36007,6 +36517,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36535,6 +37048,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36592,9 +37108,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36610,6 +37135,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36646,6 +37174,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36718,6 +37249,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36727,12 +37261,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36802,6 +37345,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36829,6 +37375,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36865,6 +37414,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36874,9 +37426,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -37021,6 +37588,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37246,6 +37816,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37351,6 +37924,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37840,6 +38416,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37966,6 +38545,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -38125,9 +38707,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38281,7 +38860,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38296,13 +38875,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38326,13 +38908,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38470,10 +39055,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38515,6 +39100,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38533,6 +39121,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38602,6 +39193,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38995,6 +39589,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -39100,6 +39697,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -39163,6 +39763,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39901,6 +40504,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39991,9 +40597,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40276,6 +40879,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40861,6 +41470,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -41227,9 +41839,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41287,9 +41896,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41335,6 +41941,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41455,6 +42064,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41494,6 +42106,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41587,9 +42202,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41779,6 +42391,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41797,7 +42412,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42271,6 +42886,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42523,6 +43144,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42556,9 +43180,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42673,6 +43294,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42766,6 +43390,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42799,6 +43426,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42814,13 +43444,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42835,22 +43465,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -43222,6 +43852,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43258,6 +43894,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43435,6 +44074,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43558,6 +44203,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43639,6 +44287,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43822,6 +44473,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43840,18 +44494,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43879,9 +44527,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43891,9 +44536,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -44020,10 +44662,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -44032,12 +44671,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -44050,9 +44683,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -44086,7 +44716,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -44095,16 +44725,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44278,6 +44908,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44545,9 +45178,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44737,6 +45367,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44809,6 +45442,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44827,6 +45463,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44875,10 +45514,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44890,7 +45529,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44947,6 +45586,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44956,6 +45610,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45619,6 +46285,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45694,9 +46363,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45712,24 +46378,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45760,6 +46414,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45826,9 +46492,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45877,13 +46540,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45922,9 +46582,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46318,6 +46975,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46327,9 +46987,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46348,6 +47005,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46363,9 +47026,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46387,6 +47047,9 @@ msgstr[2] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46396,6 +47059,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46408,16 +47077,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46426,12 +47098,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46441,12 +47119,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46459,9 +47143,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46525,7 +47218,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46555,6 +47248,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46723,10 +47419,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46765,12 +47461,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46837,9 +47527,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46858,18 +47545,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46951,6 +47632,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -47005,6 +47689,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -47068,6 +47755,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -47119,9 +47809,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47299,6 +47986,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47317,7 +48007,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47353,6 +48043,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47497,12 +48190,6 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "Your groups"
msgstr ""
@@ -47542,6 +48229,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47701,6 +48397,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47881,6 +48580,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47935,6 +48637,18 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47992,10 +48706,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -48157,7 +48874,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48337,6 +49054,12 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48496,9 +49219,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48685,6 +49405,9 @@ msgstr "je već povezan s GitLab zadatkom. Novi zadatak neće biti povezan."
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48724,6 +49447,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -49222,6 +49951,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -49231,10 +49963,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49441,6 +50173,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49498,6 +50233,12 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "running"
msgstr ""
diff --git a/locale/ca_ES/gitlab.po b/locale/ca_ES/gitlab.po
index 7ff88c91f7a..7ba6a054a81 100644
--- a/locale/ca_ES/gitlab.po
+++ b/locale/ca_ES/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: ca\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:22\n"
+"PO-Revision-Date: 2022-12-10 06:39\n"
msgid " %{start} to %{end}"
msgstr " Des de %{start} fins %{end}"
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "ag."
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "Branques"
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr "Actives"
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr "Mostra les branques actives"
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr "S'està connectant..."
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr "Etiqueta"
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr "Descripció:"
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr "feb."
msgid "February"
msgstr "febrer"
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr "Color del tipus de lletra"
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr "Identitats"
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr "Tanca"
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr "Més informació"
msgid "More information"
msgstr "Més informació"
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/cs_CZ/gitlab.po b/locale/cs_CZ/gitlab.po
index fc0cddd408f..0b1519d8010 100644
--- a/locale/cs_CZ/gitlab.po
+++ b/locale/cs_CZ/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: cs\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:22\n"
+"PO-Revision-Date: 2022-12-10 06:39\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -451,6 +451,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -666,6 +673,12 @@ msgstr[3] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -886,13 +899,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -1088,9 +1101,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -1143,7 +1153,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1318,6 +1328,13 @@ msgstr ""
msgid "%{text} is available"
msgstr "%{text} je k dispozici"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1357,6 +1374,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1478,12 +1498,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2630,6 +2656,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2717,6 +2746,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2912,6 +2944,9 @@ msgstr "Zastavení úloh selhalo"
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -3212,7 +3247,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3275,9 +3310,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4806,6 +4838,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4923,9 +4958,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5480,6 +5512,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5894,6 +5929,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5939,6 +5977,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "Srp"
@@ -6170,9 +6211,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -7200,6 +7238,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7314,9 +7361,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "Větve"
@@ -7326,6 +7370,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr "Aktivní"
@@ -7347,7 +7394,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr "Porovnat"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7368,9 +7418,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr "Filtrovat podle názvu větve"
@@ -7392,6 +7439,9 @@ msgstr "Přehled"
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr "Zobrazit aktivní větve"
@@ -7425,6 +7475,12 @@ msgstr "Výchozí větev nelze odstranit"
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7437,6 +7493,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7491,6 +7550,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -8299,6 +8361,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8347,6 +8412,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8472,9 +8540,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8490,7 +8555,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8514,6 +8579,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8757,9 +8825,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8772,6 +8846,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8784,6 +8861,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8793,6 +8873,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8802,6 +8885,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9497,6 +9586,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10320,6 +10412,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10344,6 +10439,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10359,15 +10457,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10533,7 +10640,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10653,6 +10760,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10692,12 +10802,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10968,9 +11072,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -11154,9 +11255,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11469,6 +11567,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11601,9 +11702,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11682,6 +11780,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11718,6 +11819,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11943,6 +12080,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12355,12 +12495,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12373,9 +12519,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12394,6 +12552,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12427,6 +12588,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12487,6 +12651,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12634,6 +12801,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -13007,6 +13177,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -13064,6 +13237,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13868,6 +14047,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13904,6 +14086,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -14010,9 +14195,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -14179,6 +14370,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15500,6 +15694,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15863,9 +16060,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15959,6 +16153,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -16274,6 +16471,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16437,6 +16643,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16467,9 +16676,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16488,9 +16709,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16545,13 +16763,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16871,9 +17092,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -17030,9 +17248,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -17289,6 +17504,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17508,15 +17726,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17553,6 +17762,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17697,10 +17909,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17782,13 +18003,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17831,6 +18045,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18682,6 +18899,13 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18931,6 +19155,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18958,6 +19185,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -19135,6 +19365,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -19228,6 +19461,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19417,7 +19656,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19504,6 +19743,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19609,6 +19851,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19780,7 +20025,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19801,9 +20046,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19933,6 +20175,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19942,9 +20187,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20426,12 +20668,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20465,9 +20713,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20636,6 +20881,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20660,6 +20908,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20699,7 +20950,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20741,6 +20992,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20753,9 +21010,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -21107,6 +21373,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21674,12 +21943,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21998,6 +22273,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -22272,6 +22550,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -22305,6 +22586,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22653,6 +22937,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22701,7 +22988,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -23170,9 +23457,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -23311,9 +23595,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23746,9 +24027,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23899,6 +24177,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23980,6 +24261,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -24049,9 +24333,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -24128,15 +24409,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -24225,6 +24509,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -24330,6 +24617,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -25017,10 +25307,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -25071,6 +25361,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -25176,6 +25469,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25674,6 +25970,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25695,6 +25994,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26830,6 +27135,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26893,9 +27201,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -27107,6 +27412,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -27288,6 +27596,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -27330,6 +27641,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27603,9 +27920,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27705,6 +28019,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -28373,9 +28690,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28758,6 +29090,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28837,9 +29172,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28945,9 +29277,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -29233,6 +29562,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -29324,6 +29656,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -29339,6 +29674,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -29363,6 +29704,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29435,6 +29788,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29450,6 +29806,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29460,6 +29819,9 @@ msgstr[3] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29514,6 +29876,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29562,9 +29927,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29587,6 +29949,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29602,6 +29971,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29671,6 +30043,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29734,6 +30109,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29977,6 +30361,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30892,6 +31300,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -31048,9 +31459,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -31204,6 +31612,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -31288,6 +31699,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -31300,9 +31714,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -31420,16 +31840,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31792,6 +32221,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -32326,12 +32761,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -32413,9 +32854,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32530,6 +32968,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32575,6 +33016,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32704,7 +33148,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32785,6 +33229,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -33307,9 +33754,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33983,6 +34427,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -34182,6 +34629,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34521,6 +34971,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34575,10 +35028,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34929,6 +35379,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -35180,6 +35633,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -35256,7 +35712,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -35353,6 +35809,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -35419,7 +35878,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -35446,6 +35905,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35467,6 +35929,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35552,6 +36017,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35570,6 +36038,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35609,12 +36080,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35709,6 +36183,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35775,6 +36252,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35823,12 +36303,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35944,6 +36418,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35971,13 +36448,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -36019,6 +36499,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -36034,6 +36517,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -36100,6 +36592,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -36127,9 +36625,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -36142,18 +36646,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -36238,6 +36751,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36777,6 +37293,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36834,9 +37353,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36852,6 +37380,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36888,6 +37419,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36960,6 +37494,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36969,12 +37506,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -37044,6 +37590,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -37071,6 +37620,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -37107,6 +37659,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -37116,9 +37671,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -37263,6 +37833,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37488,6 +38061,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37593,6 +38169,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -38083,6 +38662,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -38209,6 +38791,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -38369,9 +38954,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38525,7 +39107,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38540,13 +39122,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38570,13 +39155,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38714,10 +39302,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38759,6 +39347,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38777,6 +39368,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38846,6 +39440,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -39239,6 +39836,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -39344,6 +39944,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -39407,6 +40010,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -40146,6 +40752,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -40236,9 +40845,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40521,6 +41127,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -41111,6 +41723,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -41479,9 +42094,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41539,9 +42151,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41587,6 +42196,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41707,6 +42319,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41746,6 +42361,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41839,9 +42457,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -42031,6 +42646,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -42049,7 +42667,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42523,6 +43141,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42777,6 +43401,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42810,9 +43437,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42927,6 +43551,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -43020,6 +43647,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -43053,6 +43683,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -43068,13 +43701,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -43089,22 +43722,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -43477,6 +44110,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43513,6 +44152,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43690,6 +44332,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43813,6 +44461,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43894,6 +44545,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -44077,6 +44731,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -44095,18 +44752,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -44134,9 +44785,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -44146,9 +44794,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -44275,10 +44920,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -44287,12 +44929,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -44305,9 +44941,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -44341,7 +44974,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -44350,16 +44983,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44533,6 +45166,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44801,9 +45437,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44993,6 +45626,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -45065,6 +45701,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -45083,6 +45722,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -45131,10 +45773,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -45146,7 +45788,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -45203,6 +45845,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -45212,6 +45869,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45878,6 +46547,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45953,9 +46625,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45971,24 +46640,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -46019,6 +46676,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -46085,9 +46754,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -46136,13 +46802,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -46181,9 +46844,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46579,6 +47239,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46588,9 +47251,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46609,6 +47269,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46624,9 +47290,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46649,6 +47312,9 @@ msgstr[3] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46658,6 +47324,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46670,16 +47342,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46688,12 +47363,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46703,12 +47384,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46721,9 +47408,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46787,7 +47483,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46817,6 +47513,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46986,10 +47685,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -47028,13 +47727,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -47101,9 +47793,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -47122,18 +47811,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -47215,6 +47898,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -47269,6 +47955,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -47333,6 +48022,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -47384,9 +48076,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47564,6 +48253,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47582,7 +48274,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47618,6 +48310,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47763,13 +48458,6 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "Your groups"
msgstr ""
@@ -47809,6 +48497,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47969,6 +48666,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -48152,6 +48852,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -48207,6 +48910,20 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -48264,10 +48981,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -48432,7 +49152,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48614,6 +49334,13 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48775,9 +49502,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48968,6 +49692,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -49007,6 +49734,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -49510,6 +50243,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -49519,10 +50255,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49732,6 +50468,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49791,6 +50530,13 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "running"
msgstr ""
diff --git a/locale/cy_GB/gitlab.po b/locale/cy_GB/gitlab.po
index 4f0319894d6..04d719002e2 100644
--- a/locale/cy_GB/gitlab.po
+++ b/locale/cy_GB/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: cy\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:25\n"
+"PO-Revision-Date: 2022-12-10 06:42\n"
msgid " %{start} to %{end}"
msgstr " %{start} i %{end}"
@@ -559,6 +559,15 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -826,6 +835,12 @@ msgstr[5] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -1066,13 +1081,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -1270,9 +1285,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -1327,7 +1339,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1528,6 +1540,15 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1567,6 +1588,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1690,12 +1714,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2896,6 +2926,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2983,6 +3016,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -3178,6 +3214,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -3478,7 +3517,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3541,9 +3580,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -5074,6 +5110,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -5191,9 +5230,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5764,6 +5800,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -6184,6 +6223,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -6229,6 +6271,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -6460,9 +6505,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -7498,6 +7540,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7612,9 +7663,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7624,6 +7672,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7645,7 +7696,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7666,9 +7720,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7690,6 +7741,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7723,6 +7777,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7735,6 +7795,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7789,6 +7852,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -8599,6 +8665,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8647,6 +8716,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8776,9 +8848,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8794,7 +8863,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8818,6 +8887,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -9061,9 +9133,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -9076,6 +9154,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -9088,6 +9169,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -9097,6 +9181,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -9106,6 +9193,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9805,6 +9898,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10630,6 +10726,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10654,6 +10753,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10669,15 +10771,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10843,7 +10954,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10963,6 +11074,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -11002,12 +11116,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -11284,9 +11392,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -11470,9 +11575,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11785,6 +11887,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11917,9 +12022,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11998,6 +12100,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -12034,6 +12139,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -12259,6 +12400,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12673,12 +12817,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12691,9 +12841,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12712,6 +12874,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12745,6 +12910,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12805,6 +12973,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12952,6 +13123,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -13327,6 +13501,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -13384,6 +13561,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -14200,6 +14383,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -14236,6 +14422,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -14344,9 +14533,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -14515,6 +14710,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15844,6 +16042,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -16207,9 +16408,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -16303,6 +16501,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -16618,6 +16819,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16783,6 +16993,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16813,9 +17026,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16834,9 +17059,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16891,13 +17113,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -17221,9 +17446,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -17380,9 +17602,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -17641,6 +17860,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17860,15 +18082,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17905,6 +18118,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -18049,10 +18265,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -18136,15 +18361,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -18187,6 +18403,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -19042,6 +19261,15 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -19291,6 +19519,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -19318,6 +19549,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -19495,6 +19729,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -19588,6 +19825,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19777,7 +20020,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19864,6 +20107,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19969,6 +20215,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -20140,7 +20389,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -20161,9 +20410,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -20293,6 +20539,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -20302,9 +20551,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20794,12 +21040,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20833,9 +21085,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -21004,6 +21253,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -21028,6 +21280,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -21067,7 +21322,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -21109,6 +21364,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -21121,9 +21382,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -21481,6 +21751,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -22048,12 +22321,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -22372,6 +22651,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -22648,6 +22930,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -22681,6 +22966,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -23029,6 +23317,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -23077,7 +23368,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -23548,9 +23839,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -23689,9 +23977,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -24124,9 +24409,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -24277,6 +24559,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -24358,6 +24643,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -24427,9 +24715,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -24508,15 +24793,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -24607,6 +24895,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -24712,6 +25003,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -25411,10 +25705,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -25465,6 +25759,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -25570,6 +25867,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -26068,6 +26368,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -26089,6 +26392,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -27232,6 +27541,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -27295,9 +27607,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -27511,6 +27820,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -27694,6 +28006,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -27736,6 +28051,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -28009,9 +28330,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -28111,6 +28429,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -28789,9 +29110,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -29176,6 +29512,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -29257,9 +29596,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -29365,9 +29701,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -29653,6 +29986,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -29746,6 +30082,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -29761,6 +30100,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -29785,6 +30130,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29857,6 +30214,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29872,6 +30232,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29884,6 +30247,9 @@ msgstr[5] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29938,6 +30304,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29986,9 +30355,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -30013,6 +30379,15 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -30028,6 +30403,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -30097,6 +30475,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -30160,6 +30541,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -30403,6 +30793,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -31318,6 +31732,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -31474,9 +31891,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -31630,6 +32044,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -31714,6 +32131,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -31726,9 +32146,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -31846,16 +32272,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -32218,6 +32653,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -32752,12 +33193,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -32839,9 +33286,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32956,6 +33400,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -33001,6 +33448,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -33130,7 +33580,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -33211,6 +33661,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -33733,9 +34186,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -34411,6 +34861,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -34612,6 +35065,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34951,6 +35407,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -35005,10 +35464,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -35371,6 +35827,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -35626,6 +36085,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -35704,7 +36166,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -35803,6 +36265,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -35875,7 +36340,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -35902,6 +36367,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35923,6 +36391,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -36010,6 +36481,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -36028,6 +36502,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -36067,12 +36544,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -36169,6 +36649,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -36235,6 +36718,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -36283,12 +36769,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -36406,6 +36886,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -36433,13 +36916,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -36481,6 +36967,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -36496,6 +36985,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -36562,6 +37060,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -36589,9 +37093,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -36604,18 +37114,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -36700,6 +37219,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -37261,6 +37783,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -37318,9 +37843,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -37336,6 +37870,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -37372,6 +37909,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -37444,6 +37984,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -37453,12 +37996,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -37528,6 +38080,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -37555,6 +38110,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -37591,6 +38149,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -37600,9 +38161,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -37747,6 +38323,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37972,6 +38551,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -38077,6 +38659,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -38569,6 +39154,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -38695,6 +39283,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -38857,9 +39448,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -39013,7 +39601,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -39028,13 +39616,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -39058,13 +39649,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -39202,10 +39796,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -39247,6 +39841,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -39265,6 +39862,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -39334,6 +39934,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -39727,6 +40330,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -39832,6 +40438,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -39895,6 +40504,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -40636,6 +41248,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -40726,9 +41341,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -41011,6 +41623,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -41611,6 +42229,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -41983,9 +42604,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -42043,9 +42661,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -42091,6 +42706,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -42211,6 +42829,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -42250,6 +42871,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -42343,9 +42967,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -42535,6 +43156,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -42553,7 +43177,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -43027,6 +43651,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -43285,6 +43915,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -43318,9 +43951,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -43435,6 +44065,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -43528,6 +44161,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -43561,6 +44197,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -43576,13 +44215,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -43597,22 +44236,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -43987,6 +44626,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -44023,6 +44668,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -44200,6 +44848,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -44323,6 +44977,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -44404,6 +45061,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -44587,6 +45247,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -44605,18 +45268,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -44644,9 +45301,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -44656,9 +45310,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -44785,10 +45436,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -44797,12 +45445,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -44815,9 +45457,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -44851,7 +45490,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -44860,16 +45499,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -45043,6 +45682,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -45313,9 +45955,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -45505,6 +46144,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -45577,6 +46219,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -45595,6 +46240,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -45643,10 +46291,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -45658,7 +46306,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -45715,6 +46363,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -45724,6 +46387,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -46396,6 +47071,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -46471,9 +47149,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -46489,24 +47164,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -46537,6 +47200,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -46603,9 +47278,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -46654,13 +47326,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -46699,9 +47368,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -47101,6 +47767,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -47110,9 +47779,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -47131,6 +47797,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -47146,9 +47818,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -47173,6 +47842,9 @@ msgstr[5] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -47182,6 +47854,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -47194,16 +47872,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -47212,12 +47893,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -47227,12 +47914,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -47245,9 +47938,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -47311,7 +48013,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -47341,6 +48043,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -47512,10 +48217,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -47554,15 +48259,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -47629,9 +48325,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -47650,18 +48343,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -47743,6 +48430,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -47797,6 +48487,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -47863,6 +48556,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -47914,9 +48610,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -48094,6 +48787,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -48112,7 +48808,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -48148,6 +48844,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -48295,15 +48994,6 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
msgid "Your groups"
msgstr ""
@@ -48343,6 +49033,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -48505,6 +49204,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -48694,6 +49396,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -48751,6 +49456,24 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -48808,10 +49531,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -48982,7 +49708,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -49168,6 +49894,15 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -49333,9 +50068,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -49534,6 +50266,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -49573,6 +50308,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -50086,6 +50827,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -50095,10 +50839,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -50314,6 +51058,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -50377,6 +51124,15 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "running"
msgstr ""
diff --git a/locale/da_DK/gitlab.po b/locale/da_DK/gitlab.po
index 2dbe3f728bc..c55160c9fba 100644
--- a/locale/da_DK/gitlab.po
+++ b/locale/da_DK/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: da\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:22\n"
+"PO-Revision-Date: 2022-12-10 06:39\n"
msgid " %{start} to %{end}"
msgstr " %{start} til %{end}"
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] "%d kommentar mere"
msgstr[1] "%d kommentarer mere"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%d afventende kommentar"
@@ -506,6 +511,12 @@ msgstr[1] "%{bold_start}%{count}%{bold_end} åbnede sammenlægningsanmodninger"
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr "%{code_open}Maskeret:%{code_close} Skjult i joblogge. Skal matche maskeringskrav."
@@ -706,15 +717,15 @@ msgstr "%{group_name} bruger gruppehåndterede kontoer. Du skal oprette en ny Gi
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr "%{group_name}&%{epic_iid} &middot; oprettede %{epic_created} af %{author}"
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type} blev slettet"
-
-msgid "%{hook_type} was scheduled for deletion"
-msgstr "%{hook_type} blev planlagt til sletning"
-
msgid "%{host} sign-in from new location"
msgstr "Indlogning på %{host} fra ny placering"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
+msgstr ""
+
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr ""
+
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr "%{integrations_link_start}Integreringer%{link_end} giver dig mulighed for at gøre programmer fra tredjepart en del af dit GitLab-workflow. Hvis de tilgængelige integreringer ikke opfylder dine behov, så overvej at bruge en %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr "%{openedEpics} åbne, %{closedEpics} lukkede"
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr "%{openedIssues} åbne, %{closedIssues} lukkede"
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr "%{reportType} %{status}"
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr "%{reportType} registrerede %{totalStart}%{total}%{totalEnd} potentielle %{vulnMessage}"
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr "%{template_project_id} er ukendt eller ugyldigt"
msgid "%{text} is available"
msgstr "%{text} er tilgængelig"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr "%{timebox_type} understøtter ikke burnupdiagrammer"
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr "%{userName} (kan ikke sammenlægge)"
@@ -1266,12 +1282,18 @@ msgstr "(+%{count}&nbsp;regler)"
msgid "(Group Managed Account)"
msgstr "(gruppehåndteret konto)"
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr "(ingen ændringer)"
msgid "(UTC %{offset}) %{timezone}"
msgstr "(UTC %{offset}) %{timezone}"
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr "(tjek forløb)"
@@ -2364,6 +2386,9 @@ msgstr "Tilføj systemhook"
msgid "Add text to the sign-in page. Markdown enabled."
msgstr "Tilføj tekst til indlogningssiden. Markdown er aktiveret."
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr "Tilføj til tavle"
@@ -2451,6 +2476,9 @@ msgstr "Tilføjet i denne version"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "Tilføjelse af nye programmer er deaktiveret i din GitLab-instans. Kontakt din GitLab-administrator for at få tilladelsen"
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "Yderligere minutter"
@@ -2646,6 +2674,9 @@ msgstr "Stop af job mislykkedes"
msgid "AdminArea|Total users"
msgstr "Brugere i alt"
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr "Brugere"
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr "Vælg en CI-/CD-skabelon"
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr "De seneste artefakter for alle job i de nyeste pipelines som er lykkedes i hvert projekt gemmes og udløber ikke."
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr "Der opstod en fejl. Log venligst ind igen."
msgid "An error occurred. Please try again."
msgstr "Der opstod en fejl. Prøv venligst igen."
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr "Et eksempelprojekt til håndtering af Kubernetes-klynger integreret med GitLab"
@@ -4655,9 +4686,6 @@ msgstr "Alle milepæle"
msgid "Any namespace"
msgstr "Alle navnerum"
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr "Program-id"
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] "Er du sikker på, at du vil importere %d depot?"
msgstr[1] "Er du sikker på, at du vil importere %d depoter?"
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr "Er du sikker på, at du vil låse %{path}?"
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "Aug."
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr "Tilgængelige delte runnere:"
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "Grene"
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr "Aktiv"
@@ -7049,8 +7092,11 @@ msgstr ""
msgid "Branches|Compare"
msgstr "Sammenlign"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "Slet alle grene som er sammenlagt ind i '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
+msgstr ""
msgid "Branches|Delete branch"
msgstr "Slet gren"
@@ -7070,9 +7116,6 @@ msgstr "Slet beskyttet gren. Er du HELT SIKKER?"
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr "Sletning af grenen %{strongStart}%{branchName}%{strongEnd} kan ikke fortrydes. Er du sikker?"
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "Sletning af de sammenlagte grene kan ikke fortrydes. Er du sikker?"
-
msgid "Branches|Filter by branch name"
msgstr "Filtrér efter grennavn"
@@ -7094,6 +7137,9 @@ msgstr "Oversigt"
msgid "Branches|Please type the following to confirm:"
msgstr "Skriv venligst følgende for at bekræfte:"
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr "Vis aktive grene"
@@ -7127,6 +7173,12 @@ msgstr "Standardgrenen kan ikke slettes"
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr "For at forkaste de lokale ændringer og overskrive grenen med upstream-versionen skal du slette den her og vælge 'Opdater nu' ovenover."
@@ -7139,6 +7191,9 @@ msgstr "Ja, slet gren"
msgid "Branches|Yes, delete protected branch"
msgstr "Ja, slet beskyttet gren"
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr "Der opstod en fejl under hentning af artefakterne"
msgid "BuildArtifacts|Loading artifacts"
msgstr "Indlæser artefakter"
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr "Indbygget"
@@ -7999,6 +8057,9 @@ msgstr "Pipeline #%{pipeline_id} %{humanized_status} om %{duration}"
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr "Pipeline %{pipeline_link} af %{ref_type} %{ref_link} af %{user_combined_name} %{humanized_status}"
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr "Mærkat"
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr "Tjek dine Kubernetes-klyngeaftryk for kendte sårbarheder."
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "Tjekker tilgængelighed af %{text} …"
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr "Rediger"
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,8 +8247,8 @@ msgstr "Kunne ikke indlæse lande. Prøv venligst igen."
msgid "Checkout|Failed to load states. Please try again."
msgstr "Kunne ikke indlæse tilstande. Prøv venligst igen."
-msgid "Checkout|Failed to load the payment form. Please try again."
-msgstr "Kunne ikke indlæse betalingsformularen. Prøv venligst igen."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
+msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
msgstr "Kunne ikke registrere kreditkort. Prøv venligst igen."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr "Navn på virksomhed eller organisation som bruger GitLab"
@@ -8453,9 +8517,15 @@ msgstr "kører"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr "Kan ikke bruge maskeret variabel med nuværende værdi"
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr "Miljøer"
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr "Nøgle"
msgid "CiVariables|Masked"
msgstr "Maskeret"
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr "Fjern variabel"
msgid "CiVariables|Remove variable row"
msgstr "Fjern variabelrække"
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr "Omfang"
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr "Tilstand"
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr "Type"
@@ -8498,6 +8577,12 @@ msgstr "Værdi"
msgid "CiVariables|Variables"
msgstr "Variabler"
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr "* (alle miljøer)"
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr "Ukendt bruger"
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr "Slet overholdelsesframeworket %{framework}"
@@ -10034,6 +10125,9 @@ msgstr "Fejl ved hentning af overholdelsesframeworkdata. Opdater venligst siden"
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr "Fejl ved hentning af overholdelsesframeworkdata. Opdater venligst siden eller prøv et andet framework"
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr "Kan ikke gemme overholdelsesframeworket. Prøv venligst igen"
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr "Konfigurer %{link}-integreringen."
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr "Opretter forbindelse"
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr "Opretter forbindelse ..."
@@ -10382,12 +10488,6 @@ msgstr "Beholderregisteraftryk"
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr "Beholderregister er ikke aktiveret på GitLab-instansen. Spørg en administrator om at aktivere den for at få Auto DevOps til at virke."
-msgid "Container repositories"
-msgstr "Beholderdepoter"
-
-msgid "Container repository"
-msgstr "Beholderdepot"
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr " Besøg venligst %{linkStart}administratorindstillingerne%{linkEnd} for at aktivere funktionen."
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr "Styr e-mails som er linket til din konto"
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr "Kunne ikke uploade dine designs da en eller flere af de uploadede filer
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr "Land"
@@ -11285,9 +11382,6 @@ msgstr "Opret problemstilling"
msgid "Create issue to resolve all threads"
msgstr "Opret problemstilling for at løse alle tråde"
-msgid "Create iteration"
-msgstr "Opret gennemløb"
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr "Opret udklip"
msgid "Create tag %{tagName}"
msgstr "Opret mærkatet %{tagName}"
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr "Opret emne"
@@ -11402,6 +11499,42 @@ msgstr "Du har ikke tilladelse til at oprette grupper."
msgid "CreateTag|Tag"
msgstr "Mærkat"
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr "%{name} (standard)"
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr "Kreditkort:"
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr "%{startDate} - %{endDate}"
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr "Dage fra sammenlægning til udsendelse"
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr "Udsendelseshyppighed"
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "Diagrammet viser mediantiden mellem en sammenlægningsanmodning, der sammenlægges og udsendes i produktionsmiljøer, som er baseret på værdien %{linkStart}deployment_tier%{linkEnd}."
+msgid "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr "Udsendelser"
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr "Beskrivelse:"
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr "Beskrivende etiket"
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr "Indtast mindst tre tegn for at søge"
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr "Indtast din Bitbucket-server-URL og personlige adgangstoken nedenunder"
@@ -15519,9 +15712,6 @@ msgstr "Fjern epic"
msgid "Epics|Remove issue"
msgstr "Fjern problemstilling"
-msgid "Epics|Show more"
-msgstr "Vis mere"
-
msgid "Epics|Something went wrong while creating child epics."
msgstr "Noget gik galt under oprettelse af underepics."
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr "Fejl ved hentning af referencer"
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr "Fejl ved hentning af afhængighedsliste. Tjek venligst din netværksforbindelse og prøv igen."
@@ -15930,6 +16123,15 @@ msgstr "Regelsættet har ingen eskaleringsregler."
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr "Estimat"
@@ -16091,6 +16293,9 @@ msgstr "Alle kan bidrage"
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr "Alt hvad du har brug for til at oprette et GitLab Pages-websted med Gatsby"
@@ -16121,9 +16326,21 @@ msgstr "Bevissamling"
msgid "Exactly one of %{attributes} is required"
msgstr "Præcist én af %{attributes} kræves"
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr "Eksempel: @sub\\.company\\.com$"
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr "Uden sammenlægningscommits. Begrænset til 6.000 commits."
msgid "Execution time"
msgstr "Udførelsestid"
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr "Eksisterende grennavn, mærkat eller commit-SHA"
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr "Udfold sidebjælke"
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr "Forventede dokumenter: %{expected_documents}"
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr "Kunne ikke installere."
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr "Kunne ikke opdatere problemstillingsstatus"
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr "Kunne ikke opgradere."
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr "Feb."
msgid "February"
msgstr "Februar"
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr "Flyt fokus til filterlinje"
@@ -17201,6 +17406,9 @@ msgstr "Aktiviteter for fulgte brugere"
msgid "Followed users"
msgstr "Fulgte brugere"
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr "Skriftfarve"
@@ -17345,12 +17553,21 @@ msgstr "Igangværende forgrening"
msgid "Forks"
msgstr "Forgreninger"
-msgid "Format: %{dateFormat}"
-msgstr "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|Up to date with upstream repository"
msgstr ""
+msgid "Format: %{dateFormat}"
+msgstr "Format: %{dateFormat}"
+
msgid "Found errors in your %{gitlab_ci_yml}:"
msgstr "Fandt fejl i din %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr "Fra %{code_open}%{source_title}%{code_close} ind i"
msgid "From %{providerTitle}"
msgstr "Fra %{providerTitle}"
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr "Generelle pipelines"
msgid "General settings"
msgstr "Generelle indstillinger"
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr "GitLab-version"
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr "Gå til dine sammenlægningsanmodninger"
msgid "Go to your projects"
msgstr "GÃ¥ til dine projekter"
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr "GÃ¥ til dine udklip"
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr "Graf"
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr "Medlemmer tilføjet"
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr "Inden for 3 år"
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr "SAML-gruppenavn"
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,8 +19661,8 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
-msgstr "Vælg en undergruppe som skal bruges som kilden til tilpassede projektskabeloner i gruppen."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
+msgstr ""
msgid "GroupSettings|Select parent group"
msgstr ""
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr "Auto DevOps-pipelinen kører hvis ingen alternativ CI-konfigurationsfil bliver fundet."
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr "Der var et problem med opdatering af Auto DevOps-pipeline: %{error_messages}."
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr "Opret gruppe"
@@ -19582,9 +19823,6 @@ msgstr "Opret ny gruppe"
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr "GitLab kilde-URL"
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr "Højeste rolle:"
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr "Startside"
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr "Hook blev opdateret."
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr "Identiteter"
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr "Forbedr kundesupport med serviceskranke"
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr "Hændelse"
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr "%{hours} timer, %{minutes} minutter tilbage"
@@ -21624,6 +21895,9 @@ msgstr "Sletning af indeks er annulleret"
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr "Aktivér kommentarer"
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr "GitLab-administratorere kan opsætte integreringer som alle grupper og p
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr "GitLab-administratorere kan opsætte integreringer som alle projekter i en gruppe nedarver og bruger som standard. Integreringerne gælder alle projekter som ikke allerede bruger tilpassede indstillinger. Du kan tilsidesætte tilpassede indstillinger for et projekt hvis indstillingerne er nødvendige på det niveau. Lær mere om %{integrations_link_start}håndtering af integrering på gruppeniveau%{link_end}."
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr "Håndtering af integrering på gruppeniveau"
@@ -22277,6 +22557,9 @@ msgstr "Inviter dine kollegaer"
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr "Vi har bemærket at du ikke har inviteret nogen til gruppen. Invitér dine kollegaer, så du kan debattere problemstillinger, samarbejde på sammenlægningsanmodninger og dele din viden."
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr "Titel"
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr "Det er nu muligt at %{action} filer som er gemt i LFS med webgrænsefladen"
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr "Fejl ved indlæsning af gennemløbskadencer."
msgid "Iterations|Iteration cadences"
msgstr "Gennemløbskadencer"
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr "Nøgle"
msgid "Key (PEM)"
msgstr "Nøgle (PEM)"
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr "Kubernetes-klynger"
msgid "Kubernetes deployment not found"
msgstr "Kubernetes-udsendelse ikke fundet"
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr "LDAP"
@@ -23748,15 +24025,18 @@ msgstr "Etiketter"
msgid "Labels"
msgstr "Etiketter"
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr "Etiketter kan anvendes på %{features}. Gruppeetiketter er tilgængelige for alle projekter i gruppen."
-
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "Etiketter kan anvendes på problemstillinger og sammenlægningsanmodninger for at kategorisere dem."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr "Sidst ændret"
@@ -23948,6 +24231,9 @@ msgstr "Lær mere"
msgid "Learn More."
msgstr "Lær mere."
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,12 +24909,12 @@ msgstr "LÃ¥ste filer"
msgid "Locked by %{fileLockUserName}"
msgstr "LÃ¥st af %{fileLockUserName}"
+msgid "Locked files"
+msgstr ""
+
msgid "Locked the discussion."
msgstr "LÃ¥ste debatten."
-msgid "Locks give the ability to lock specific file or folder."
-msgstr "Låse gør det muligt at låse en bestemte fil eller mappe."
-
msgid "Locks the discussion."
msgstr "LÃ¥ser debatten."
@@ -24677,6 +24963,9 @@ msgstr "Logo fjernes. Er du sikker?"
msgid "Logs"
msgstr "Logge"
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr "HÃ¥ndter web-IDE-funktioner."
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr "%{member_name} inviterede dig til at deltage i GitLab"
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr "Invitation til at deltage i %{project_or_group} %{project_or_group_name}"
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr "Medlemmer"
@@ -26428,6 +26729,9 @@ msgstr "Tilføj projekter"
msgid "Modal|Close"
msgstr "Luk"
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr "Ændret"
@@ -26491,9 +26795,6 @@ msgstr "Mere information"
msgid "More information"
msgstr "Mere information"
-msgid "More information and share feedback"
-msgstr "Mere information og del feedback"
-
msgid "More information is available|here"
msgstr "Der findes mere information|her"
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr "Ny gren"
msgid "New branch unavailable"
msgstr "Ny gren utilgængelig"
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr "Ny identitet"
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "Ny problemstilling"
@@ -27197,9 +27510,6 @@ msgstr "Ingen problemstillinger fundet"
msgid "No iteration"
msgstr "Ingen gennemløb"
-msgid "No iterations to show"
-msgstr "Ingen gennemløb at vise"
-
msgid "No job log"
msgstr "Ingen joblog"
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr "Antal shards"
msgid "OK"
msgstr "OK"
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr "Ã…bnes i et nyt vindue"
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr "Conan"
msgid "PackageRegistry|Conan Command"
msgstr "Conan-kommando"
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr "Kopiér .pypirc-indhold"
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr "%{linkStart}Se dokumentationen%{linkEnd} for mere information om NuGet-r
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr "%{linkStart}Se dokumentationen%{linkEnd} for mere information om PyPi-registeret."
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr "Generisk"
@@ -29013,6 +29362,9 @@ msgstr "NuGet-kommando"
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr "Pakkeregister"
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr "Vis PyPi-kommandoer"
msgid "PackageRegistry|Show Yarn commands"
msgstr "Vis Yarn-kommandoer"
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr "Type"
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr "Du er ved at slette version %{version} af %{name}. Er du sikker?"
@@ -29176,6 +29539,9 @@ msgstr "npm"
msgid "PackageRegistry|published by %{author}"
msgstr "udgivet af %{author}"
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr "Parameter"
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr "Forælder"
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr "Adgangskoder skal være unikke og ikke være brugt på andre websteder eller tjenester."
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr "Vælg et navn"
@@ -30466,6 +30868,9 @@ msgstr "Tjek venligst din e-mail (%{email}) for at bekræfte at du ejer adressen
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr "Prøv venligst at opdatere siden. Hvis problemet fortsætter, så kontak
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "Vælg hvilket indhold du vil se på et projekts oversigtsside."
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr "Tema for syntaksfremhævning"
msgid "Preferences|Tab width"
msgstr "Bredde på tabulatorstop"
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr "Indstillingen giver dig mulighed for at tilpasse opførslen på systemla
msgid "Preferences|Time preferences"
msgstr "Præferencer for tid"
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr "Brug relative tidspunkter"
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr "Fortsæt"
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr "Opdater brugernavn"
msgid "Profiles|Upload new avatar"
msgstr "Upload ny avatar"
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr "Brug en privat e-mail - %{email}"
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr "Tillad"
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr "Vis altid belønningsemojiknapper med tommelfinger-op og tommelfinger-ned på problemstillinger, sammenlægningsanmodninger og udklip."
msgid "ProjectSettings|Analytics"
msgstr "Analyse"
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr "Hver sammenlægning opretter en sammenlægningscommit."
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr "Ingen sammenlægningscommits er oprettet."
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr "Offentlig"
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr "Brugere kan anmode om adgang"
msgid "ProjectSettings|View and edit files in this project."
msgstr "Vis og rediger filer i projektet."
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr "Netlify/ren HTML"
msgid "ProjectTemplates|NodeJS Express"
msgstr "NodeJS Express"
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr "Pages/Gatsby"
@@ -32881,9 +33322,6 @@ msgstr "Beskyttet gren"
msgid "Protected Branches"
msgstr "Beskyttede grene"
-msgid "Protected Environment"
-msgstr "Beskyttet miljø"
-
msgid "Protected Paths: requests"
msgstr "Beskyttede stier: anmodninger"
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] "Opdaterer om et sekund for at vise den opdateret status ..."
msgstr[1] "Opdaterer om %d sekunder for at vise den opdateret status ..."
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr "Runbook"
msgid "ReleaseAssetLinkType|Runbooks"
msgstr "Runbooks"
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr "Udgivelsesdato"
@@ -34091,6 +34535,9 @@ msgstr "Genåbn denne %{quick_action_target}"
msgid "Reopened this %{quick_action_target}."
msgstr "Genåbnede denne %{quick_action_target}."
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr "Genåbner denne %{quick_action_target}."
@@ -34145,11 +34592,8 @@ msgstr "Besvar …"
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr "Rapportér misbrug"
-
-msgid "Report abuse to admin"
-msgstr "Rapportér misbrug til administrator"
+msgid "Report abuse to administrator"
+msgstr ""
msgid "Report couldn't be prepared."
msgstr ""
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr "Anmod om adgang"
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr "Anmod om en ny"
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr "Genoptag"
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr "Kør husarbejde"
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr "Kør manuelle eller forsinkede job"
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr "Der er opstået en fejl under hentning af instruktioner"
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr "IP-adresse"
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr "Installer en runner"
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr "Ny registreringstoken genereret!"
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr "Runner #%{runner_id}"
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr "Mærkater"
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr "Kører"
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr "SSH-værtsnøgler er ikke tilgængelige på systemet. Brug venligst komm
msgid "SSH key"
msgstr "SSH-nøgle"
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr "SSH-nøgler"
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr "Søg"
msgid "Search GitLab"
msgstr "Søg på GitLab"
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr "Søg efter en gruppe"
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr "Nyt regelsæt"
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr "Sikkerhedsregelsætprojekt blev linket"
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr "Vælg sikkerhedsprojekt"
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr "%{firstProject} og %{secondProject}"
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr "%{firstProject}, %{secondProject} og %{rest}"
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr "Tilføj eller fjern projekter som skal overvåges i sikkerhedsområdet. Projekter som er medtaget i listen får deres resultater vist i sikkerhedsbetjeningspanelet og sårbarhedsrapporten."
@@ -36632,9 +37181,24 @@ msgstr "Tilføj projekter"
msgid "SecurityReports|All activity"
msgstr "Al aktivitet"
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr "Ingen sårbarheder fundet for pipelinen"
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr "Vælg en fil i den venstre sidebjælke for at begynde redigering. Herefter vil du være i stand til at committe dine ændringer."
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr "Vælg en etiket"
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr "Vælg kontrollanter"
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr "Vælg kilde"
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr "Delte runnere"
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr "Delte projekter"
@@ -37723,6 +38299,9 @@ msgstr "Vis seneste version"
msgid "Show list"
msgstr "Vis liste"
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr "Vis én fil ad gangen"
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr "Log ind på GitLab"
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,11 +38808,11 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
-msgstr "Nogen redigerede problemstillingen på samme tid som dig. Tjek venligst %{linkStart}problemstillingen%{linkEnd} og sørg for at dine ændringer ikke utilsigtet fjerner deres."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr "Nogen redigerede denne %{issueType} på samme tid som dig. Beskrivelsen er blevet opdateret og du skal foretage dine ændringer igen."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr "Noget gik galt ved omorganisering af designs. Prøv venligst igen"
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr "Noget gik galt under arkivering af et krav."
msgid "Something went wrong while closing the epic. Please try again later."
msgstr "Noget gik galt under lukning af epicen. Prøv venligst igen senere."
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr "Noget gik galt under lukning af sammenlægningsanmodningen. Prøv venligst igen senere."
@@ -38358,6 +38946,9 @@ msgstr "Noget gik galt under hentning af pakkelisten."
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr "Noget gik galt under indhentelse af Let's Encrypt-certifikatet."
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr "Noget gik galt under forfremmelse af problemstillingen til en epic. Prøv venligst igen."
@@ -38751,6 +39342,9 @@ msgstr "Gør dine pipelines hurtigere med behovrelationer"
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr "Squash commit-meddelelse"
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr "Start søgning"
@@ -38919,6 +39516,9 @@ msgstr "Statistik"
msgid "Status"
msgstr "Status"
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr "Skift gren"
msgid "Switch branch/tag"
msgstr "Skift gren/mærkat"
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr "Skift til GitLab Next"
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr "Team"
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr "Der er ingen spamlogge"
msgid "There are no abuse reports!"
msgstr "Der er ingen misbrugsrapporter!"
-msgid "There are no archived projects yet"
-msgstr "Der er endnu ingen arkiverede projekter"
-
msgid "There are no archived requirements"
msgstr "Der er ingen arkiverede krav"
@@ -41035,9 +41641,6 @@ msgstr "Der er ingen åbne testsager"
msgid "There are no packages yet"
msgstr "Der er endnu ingen pakker"
-msgid "There are no projects shared with this group yet"
-msgstr "Der er ingen projekter som er delt med gruppen endnu"
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr "Der er for meget data at udregne. Ændr venligst din markering."
msgid "There was a problem communicating with your device."
msgstr "Der var problemer med at kommunikere med din enhed."
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr "Der opstod en fejl under hentning af udsendelsesfrysningerne."
msgid "There was an error fetching the environments information."
msgstr "Der opstod en fejl ved hentning af miljøinformationen."
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr "Der opstod en fejl ved hentning af jobbene for dit projekt."
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr "Der opstod en fejl ved indhentning af Jira-brugerne."
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr "Der opstod en fejl ved gemning af dine ændringer."
@@ -41335,9 +41947,6 @@ msgstr "%{viewer} kunne ikke vises fordi %{reason}. Du kan i stedet %{options}."
msgid "This Cron pattern is invalid"
msgstr "Cron-mønsteret er ugyldigt"
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr "Epicen findes ikke eller du har ikke tilstrækkelig tilladelse."
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr "Gruppen"
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr "Tilbageværende tid"
msgid "Time spent"
msgstr "Tid brugt"
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr "Titel:"
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr "Titler og beskrivelser"
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr "Log ind på GitLab på %{gitlab_url} for at genaktivere din konto."
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr "Udløsere"
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr "Kan ikke hente grenliste for projektet."
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr "Utilfreds?"
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr "ms"
@@ -43384,6 +44029,9 @@ msgstr "Uløst"
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr "Fjern stjernemarkering"
@@ -43567,6 +44215,9 @@ msgstr "Uploader ændringer til terminal"
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr "Upstream"
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr "Forbrug af CI-minutter efter projekt"
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr "Kodepakker og beholderaftryk."
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr "Ubegrænset"
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr "Wiki"
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr "Brug søgelinjen øverst på siden"
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr "Personlige projekter"
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr "Udtales: %{pronunciation}"
-msgid "UserProfile|Report abuse"
-msgstr "Rapportér misbrug"
-
msgid "UserProfile|Retry"
msgstr "Prøv igen"
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,12 +45255,12 @@ msgstr "Stop"
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
-msgstr ""
-
msgid "Variable"
msgstr "Variabel"
+msgid "Variable value will be evaluated as raw string."
+msgstr ""
+
msgid "Variable will be masked in job logs."
msgstr ""
@@ -44634,7 +45270,7 @@ msgstr "Variabler"
msgid "Variables can be:"
msgstr "Variabler kan være:"
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr "Version %{versionNumber}"
msgid "Version %{versionNumber} (latest)"
msgstr "Version %{versionNumber} (seneste)"
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr "Vi registererede potentiel spam i %{humanized_resource_name}. Løs venli
msgid "We don't have enough data to show this stage."
msgstr "Vi har ikke nok data til at vise stadiet."
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr "Vi har fundet følgende fejl:"
@@ -45435,9 +46101,6 @@ msgstr "WebAuthn-enheder (%{length})"
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr "WebAuthn virker kun med HTTPS-aktiverede websteder. Kontakt din administrator for flere detaljer."
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr "Forgren projekt"
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr "Du kan ikke redigere filer direkte i projektet. Forgren projektet og indsend en sammenlægningsanmodning med dine ændringer."
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr "Webhook:"
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr "Kommentarer"
-
msgid "Webhooks|Confidential comments"
msgstr "Fortrolige kommentarer"
@@ -45618,15 +46278,12 @@ msgstr "Hændelser for medlem"
msgid "Webhooks|Merge request events"
msgstr "Hændelser for sammenlægningsanmodning"
+msgid "Webhooks|Must match part of URL"
+msgstr ""
+
msgid "Webhooks|Pipeline events"
msgstr "Hændelser for pipeline"
-msgid "Webhooks|Push events"
-msgstr "Hændelser for push"
-
-msgid "Webhooks|Push to the repository."
-msgstr ""
-
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
msgstr ""
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr "Udløser"
-msgid "Webhooks|URL"
-msgstr "URL"
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr "Oprettes"
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr "Vil udsende til"
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr "Skriv en kommentar …"
msgid "Write a description or drag your files here…"
msgstr "Skriv en beskrivelse eller træk dine filer hertil …"
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,11 +47153,11 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr "Du prøver på at uploade noget andet end et billede. Upload venligst en .png, .jpg, .jpeg, .gif, .bmp, .tiff eller .ico."
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
-msgstr "Du kan %{gitlabLinkStart}løse konflikter på GitLab%{gitlabLinkEnd} eller %{resolveLocallyStart}løse det lokalt%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
+msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
-msgstr "Du kan %{resolveLocallyStart}løse det lokalt%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
+msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
msgstr ""
@@ -46502,11 +47195,6 @@ msgstr "Du kan altid ændre din URL senere"
msgid "You can always edit this later"
msgstr "Du kan altid ændre det senere"
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr "Du kan finde mere information om GitLab-abonnementer i %{subscriptions_d
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "Du kan invitere et nyt medlem til %{project_name} eller invitere en anden gruppe."
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr "Du kan nu lukke vinduet."
-msgid "You can now export your security dashboard to a CSV report."
-msgstr "Du kan nu eksportere dit sikkerhedsbetjeningspanel til en CSV-rapport."
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr "Du kan kun %{action} filer når du er på en gren"
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr "Du kunne ikke oprette en ny udløser."
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr "Du har ingen seneste søgninger"
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr "Du har ikke tilstrækkelige tilladelser til at oprette en HTTP-integreri
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr "Du har ikke tilstrækkelige tilladelser til at oprette en vagtplan for projektet"
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr "Du har opsat 2FA til din konto! Hvis du mister adgang til din 2FA-enhed,
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr "Du har afvist %{user}"
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr "Din GitLab-gruppe"
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr "Dine grupper"
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr "Dine grupper"
@@ -47275,6 +47961,15 @@ msgstr "Dit navn"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr "`end_time` skal ikke være mere end en måned efter `start_time`"
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr "må ikke ændres"
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr "Rettet:"
msgid "ciReport|Found %{issuesWithCount}"
msgstr "Fandt %{issuesWithCount}"
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr "kommenterede på %{link_to_project}"
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr "commit %{commit_id}"
@@ -48217,9 +48936,6 @@ msgstr "example.com"
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr "overstiger grænsen på %{bytes} byte"
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr "er ikke tilladt eftersom gruppen ikke er topniveaugruppe."
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr "skal være større end startdato"
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr "seneste aktivitet"
@@ -49205,6 +49936,11 @@ msgstr "depot:"
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/de/gitlab.po b/locale/de/gitlab.po
index 8ed95d1bd53..5730cf23003 100644
--- a/locale/de/gitlab.po
+++ b/locale/de/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: de\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:22\n"
+"PO-Revision-Date: 2022-12-10 06:40\n"
msgid " %{start} to %{end}"
msgstr " %{start} bis %{end}"
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] "%d weiterer Kommentar"
msgstr[1] "%d weiterere Kommentare"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%d ausstehender Kommentar"
@@ -506,6 +511,12 @@ msgstr[1] "%{bold_start}%{count}%{bold_end} offene Merge Requests"
msgid "%{chartTitle} no data series"
msgstr "%{chartTitle} keine Datenreihe"
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr "%{code_open}Maskiert:%{code_close} In Job-Logs versteckt. Muss Maskier-Bedingungen erfüllen."
@@ -706,15 +717,15 @@ msgstr "%{group_name} nutzt Accounts, die von einer Gruppe verwaltet werden. Du
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr "%{group_name}&%{epic_iid} &middot; erstellt %{epic_created} von %{author}"
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type} wurde gelöscht"
-
-msgid "%{hook_type} was scheduled for deletion"
-msgstr "%{hook_type} wurde zum Löschen vorgemerkt"
-
msgid "%{host} sign-in from new location"
msgstr "%{host} meldet sich von einem neuen Ort"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
+msgstr ""
+
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr ""
+
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr "%{integrations_link_start}Integrationen%{link_end} ermöglichen es dir, Anwendungen von Drittanbietern in deinen GitLab-Workflow aufzunehmen. Wenn die verfügbaren Integrationen nicht deinen Anforderungen entsprechen, solltest du einen %{webhooks_link_start}Webhook%{link_end} verwenden."
@@ -906,9 +917,6 @@ msgstr "%{openedEpics} offen, %{closedEpics} geschlossen"
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr "%{openedIssues} offen, %{closedIssues} geschlossen"
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr "%{over_limit_message} Um mehr Mitglieder zu bekommen, kann ein Eigentümer der Gruppe eine Testversion starten oder ein Upgrade auf eine kostenpflichtige Version durchführen."
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr "%{reportType} %{status}"
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr "%{reportType} erkannte %{totalStart}%{total}%{totalEnd} potentielle %{vulnMessage}"
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr "%{template_project_id} ist unbekannt oder ungültig"
msgid "%{text} is available"
msgstr "%{text} ist verfügbar"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr "%{type} unterstützt nur den Namen %{name}"
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr "%{userName} (kann nicht mergen)"
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr "(Gruppenadministrierte Konten)"
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr "(Begrenzt auf %{quota} Pipeline-Minuten pro Monat)"
+
msgid "(No changes)"
msgstr "(Keine Änderungen)"
msgid "(UTC %{offset}) %{timezone}"
msgstr "(UTC %{offset}) %{timezone}"
+msgid "(Unlimited pipeline minutes)"
+msgstr "(Unbegrenzte Pipeline-Minuten)"
+
msgid "(check progress)"
msgstr "(Fortschritt überprüfen)"
@@ -2364,6 +2386,9 @@ msgstr "System-Hook hinzufügen"
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr "In dieser Version hinzugefügt"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "Das Hinzufügen neuer Anwendungen ist in deiner GitLab-Instanz deaktiviert. Bitte kontaktiere deine(n) GitLab-Administrator(in), um die Berechtigung zu erhalten"
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "Zusätzliche Minuten"
@@ -2646,6 +2674,9 @@ msgstr "Stoppen von Jobs ist fehlgeschlagen"
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr "Ein Fehler ist aufgetreten. Bitte versuche es erneut."
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr "Jeder Namensraum"
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr "App ID"
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr "Wert"
msgid "AuditStreams|Verification token"
msgstr "Bestätigungstoken"
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "Aug"
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr "Verfügbare spezifische Runner"
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr "Benutzer"
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "Branches"
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr "Aktiv"
@@ -7049,8 +7092,11 @@ msgstr ""
msgid "Branches|Compare"
msgstr "Vergleichen"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "Lösche alle Branches, die in \"%{default_branch}\" zusammengeführt werden"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
+msgstr ""
msgid "Branches|Delete branch"
msgstr "Branch löschen"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "Löschen der zusammengeführten Branches kann nicht rückgängig gemacht werden. Bist du sicher?"
-
msgid "Branches|Filter by branch name"
msgstr "Nach Zweignamen filtern"
@@ -7094,6 +7137,9 @@ msgstr "Ãœbersicht"
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr "Aktive Branches anzeigen"
@@ -7127,6 +7173,12 @@ msgstr "Der Standardbranch kann nicht gelöscht werden"
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr "Um die lokalen Änderungen zu verwerfen und den Branch mit der Upstream-Version zu überschreiben, lösche ihn hier und wähle oben \"Jetzt aktualisieren\"."
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr "Integriert"
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr "Tag"
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "Prüfe %{text} Verfügbarkeit…"
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr "Muss 1 oder mehr sein. Kann nicht dezimal sein."
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr "Name des Unternehmens oder der Organisation, die GitLab verwendet"
@@ -8453,9 +8517,15 @@ msgstr "laufend"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr "Variablenschlüssel eingeben"
@@ -8468,6 +8538,9 @@ msgstr "Schlüssel"
msgid "CiVariables|Masked"
msgstr "Maskiert"
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr "Variable jetzt löschen"
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr "Geltungsbereich"
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr "Status"
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr "Typ"
@@ -8498,6 +8577,12 @@ msgstr "Wert"
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr "* (Alle Umgebungen)"
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr "Die %{link}-Integration konfigurieren."
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr "Verbinden"
msgid "Connecting to terminal sync service"
msgstr "Verbinde zum Terminal-Sync-Dienst"
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr "Verbinden..."
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr "Lege mit deinem Konto verknüpfte E-Mails fest"
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr "Ticket erstellen"
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr "Snippet erstellen"
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr "Fehlerrate ändern"
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr "Tage für einen offenen Vorfall"
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr "Mediane Zeit, in der ein Incident in einer Produktionsumgebung im angege
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr "Keine Vorfälle in diesem Zeitraum"
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr "Standardwert für den ersten Wochentag"
msgid "Default first day of the week in calendars and date pickers."
msgstr "Standardwert für den ersten Wochentag im Kalender und in der Datumsauswahl."
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr "Bereitstellungen"
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr "Beschreibung:"
+msgid "Descriptions"
+msgstr "Beschreibungen"
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr "Entwickler"
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr "Gib unten deine Bitbucket-Server-URL und deinen persönlichen Zugriffstoken ein"
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr "Ticket löschen"
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr "Beim Erstellen untergeordneter Epics ist etwas schief gelaufen."
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr "Fehler beim Abrufen der Referenzen"
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr "Jede(r) kann beitragen"
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr "Beweissammlung"
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr "Menüleiste ausklappen"
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr "Installation fehlgeschlagen."
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr "Konnte Ticket-Status nicht aktualisieren."
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr "Upgrade fehlgeschlagen."
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr "Feb"
msgid "February"
msgstr "Februar"
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr "Schriftfarbe"
@@ -17345,10 +17553,19 @@ msgstr "Fork wird erstellt"
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr "Von der Ticketbeschreibung bis zur Bereitstellung"
@@ -17475,6 +17687,9 @@ msgstr "Allgemeine Pipelines"
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr "Generiere einen Standardsatz von Labels"
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr "GitLab erstellt einen Branch in deinem Fork und startet eine Merge-Anfrage."
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr "Diagramm"
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr "Instanz verbinden"
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr "Erstelle dies in den %{pat_link_start}Benutzereinstellungen%{pat_link_end} der GitLab-Quellinstanz. Verwende aus %{short_living_link_start}Sicherheitsgründen%{short_living_link_end} beim Erstellen des Tokens ein kurzes Ablaufdatum."
-
msgid "GroupsNew|GitLab source URL"
msgstr "GitLab-Quell-URL"
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr "Identitäten"
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr "Im Falle einer Pull-Spiegelung ist Ihr Benutzer der Autor aller Ereignisse im Aktivitäts-Feed, die das Ergebnis einer Aktualisierung sind, wie z. B. das Erstellen neuer Zweige oder das Pushen neuer Commits an vorhandene Zweige."
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr "Gibt an, ob dieser Runner Jobs ohne Tags auswählen kann"
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr "Informiere Benutzer:innen ohne SSH-Schlüssel, dass einer hinzugefügt werden muss, um pushen über SSH zu ermöglichen."
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr "Titel"
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr "Iterationen sollen am %{weekday}s beginnen."
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr "Labels"
msgid "Labels"
msgstr "Labels"
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr "Labels können auf %{features} angewendet werden. Gruppenlabels stehen für jedes Projekt innerhalb der Gruppe zur Verfügung."
-
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "Labels können Tickets und Merge-Requests hinzugefügt werden, um diese zu kategorisieren."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr "Zuletzt bearbeitet von %{link_start}%{avatar} %{name}%{link_end}"
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr "Erfahre, wie du %{link_start} zu den integrierten Vorlagen %{link_end} beiträgst"
@@ -24623,11 +24909,11 @@ msgstr "Gesperrte Dateien"
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
-msgstr "Sperrungen bieten die Möglichkeit, bestimmte Dateien oder Ordner zu sperren."
+msgid "Locked the discussion."
+msgstr ""
msgid "Locks the discussion."
msgstr ""
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr "Protokolle"
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr "Mitglieder"
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr "Schließen"
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr "Geändert"
@@ -26491,9 +26795,6 @@ msgstr "Mehr Info"
msgid "More information"
msgstr "Mehr Informationen"
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr "Neuer Branch"
msgid "New branch unavailable"
msgstr "Neuer Branch ist nicht verfügbar"
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr "Neue Identität"
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "Neues Ticket"
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr "Kein Jobprotokoll"
@@ -27299,6 +27609,9 @@ msgstr "Keine Ergebnisse"
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr "OK"
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr "Nur zugänglich für %{membersPageLinkStart}Projektmitglieder%{membersPa
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr "Wird in einem neuen Fenster geöffnet"
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr "Conan-Befehl"
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr "Anzahl der zu behaltenden doppelten Assets"
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr "Um deine Suche zu erweitern, änder oder entferne die obigen Filter."
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr "Pakete"
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr "Ãœbergeordnet"
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "Wähle aus, welche Inhalte du auf der Projektübersichtsseite sehen möchtest."
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr "Syntax-Highlight Theme"
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr "Zeiteinstellungen"
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr "Benutzernamen aktualisieren"
msgid "Profiles|Upload new avatar"
msgstr "Neuen Avatar hochladen"
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr "Private E-Mail verwenden - %{email}"
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] "In einer Sekunde aktualisieren, um den aktualisierten Status anzuzeigen..."
msgstr[1] "In %d Sekunden aktualisieren, um den aktualisierten Status anzuzeigen..."
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr "Veröffentlicht"
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,11 +34592,8 @@ msgstr "Antworten…"
msgid "Report Finding not found"
msgstr "Berichtsergebnis nicht gefunden"
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
-msgstr "Missbrauch an Admins melden"
+msgid "Report abuse to administrator"
+msgstr "Missbrauch an den Administrator melden"
msgid "Report couldn't be prepared."
msgstr ""
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr "Zugriff anfragen"
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr "Fortsetzen"
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr "Eine neue Version ist verfügbar"
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr "Verfügbar"
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr "Leerlauf"
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr "Keine Ergebnisse gefunden"
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr "Runner Registrierung"
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr "Laufend"
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr "Laufend"
@@ -35509,13 +35980,16 @@ msgstr "SAML Single Sign-On"
msgid "SAML single sign-on for %{group_name}"
msgstr "SAML Single Sign-on für %{group_name}"
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr "Öffentlicher SSH-Schlüssel"
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr "Suchen"
msgid "Search GitLab"
msgstr "GitLab durchsuchen"
+msgid "Search Within"
+msgstr "Suche in"
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr "Vererbt"
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr "Projekte hinzufügen"
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr "Alle Cluster"
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr "Alle Projekte"
+
msgid "SecurityReports|All severities"
msgstr "Alle Schweregrade"
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr "Nicht verfügbar"
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr "Ein Label auswählen"
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr "Geteilte Runner"
+msgid "Shared Runners:"
+msgstr "Gemeinsame Runner:"
+
msgid "Shared projects"
msgstr "Geteilte Projekte"
@@ -37723,6 +38299,9 @@ msgstr "Neuste Version zeigen"
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr "Die Slack-Integration ermöglicht es dir, mit GitLab über Slash-Befehle
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr "Suche starten"
@@ -38919,6 +39516,9 @@ msgstr "Statistiken"
msgid "Status"
msgstr "Status"
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr "Zu Branch/Tag wechseln"
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr "Team"
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr "Es gibt noch keine archivierten Projekte"
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr "Es gibt noch keine Pakete"
-msgid "There are no projects shared with this group yet"
-msgstr "Es gibt noch keine geteilten Projekte mit dieser Gruppe"
-
msgid "There are no secure files yet."
msgstr "Hier sind noch keine sicheren Dateien."
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr "Diese Gruppe"
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr "Verbleibende Zeit"
msgid "Time spent"
msgstr "Benötigte Zeit"
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr "Titel"
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr "Versuche, dich mit deinem Benutzernamen oder deiner E-Mail-Adresse anzum
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr "Leider konnte deine E-Mail an GitLab nicht verarbeitet werden."
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr "Entfavorisieren"
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr "Hochgeladene Dateien"
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr "Hilfelink zu Nutzungsquoten"
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr "Verwende die Suchleiste oben auf dieser Seite"
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr "Persönliche Projekte"
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr "Ausgesprochen als: %{pronunciation}"
-msgid "UserProfile|Report abuse"
-msgstr "Missbrauch melden"
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr "Gültig ab"
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,12 +45255,12 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
-msgstr ""
-
msgid "Variable"
msgstr "Variable"
+msgid "Variable value will be evaluated as raw string."
+msgstr ""
+
msgid "Variable will be masked in job logs."
msgstr ""
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr "Variablen können sein:"
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr "Wir haben potenziellen Spam in %{humanized_resource_name} gefunden. Bitt
msgid "We don't have enough data to show this stage."
msgstr "Es liegen nicht genügend Daten vor, um diese Phase anzuzeigen."
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr "Du kannst Dateien in diesem Projekt nicht direkt bearbeiten. Verzweige dieses Projekt und reiche eine Zusammenführungsanfrage mit deinen Änderungen ein."
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr "Webhook wurde erstellt"
+
+msgid "Webhook was deleted"
+msgstr "Webhook wurde gelöscht"
+
+msgid "Webhook was scheduled for deletion"
+msgstr "Webhook wurde zum Löschen vorgemerkt"
+
+msgid "Webhook was updated"
+msgstr "Webhook wurde aktualisiert"
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr "Wird veröffentlicht"
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr "Zugriffsanfrage widerrufen"
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr "Aufgabe hinzufügen"
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,30 +46812,39 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
+msgid "WorkItem|Existing task"
+msgstr ""
+
msgid "WorkItem|Expand tasks"
msgstr ""
msgid "WorkItem|Incident"
msgstr ""
-msgid "WorkItem|Introducing tasks"
-msgstr "Aufgaben vorstellen"
-
msgid "WorkItem|Issue"
msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr "Du bist bei GitLab angemeldet als:"
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "Du kannst ein neues Mitglied oder eine andere Gruppe zu %{project_name} einladen."
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr "Du kannst dein Sicherheits-Dashboard jetzt in einen CSV-Bericht exportieren."
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr "Du hast noch keine Abonnements"
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr "Du hast %{user} abgelehnt"
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr "Deine Gruppen"
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr "Deine Gruppen"
@@ -47275,6 +47961,15 @@ msgstr "Dein Name"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr "darf nicht leer sein"
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,8 +48596,8 @@ msgstr "Behoben:"
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
-msgstr ""
+msgid "ciReport|Full report"
+msgstr "Vollständiger Bericht"
msgid "ciReport|Generic Report"
msgstr ""
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr "überschreitet die maximale Länge (100 Benutzernamen)"
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48400,6 +49116,9 @@ msgid "is already associated to a GitLab Issue. New issue will not be associated
msgstr ""
msgid "is already linked to this vulnerability"
+msgstr "ist bereits mit dieser Sicherheitslücke verknüpft"
+
+msgid "is already present in ancestors"
msgstr ""
msgid "is an invalid IP address range"
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr "muss nach dem Anfangsdatum liegen"
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/de/gitlab.po.time_stamp b/locale/de/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/de/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/el_GR/gitlab.po b/locale/el_GR/gitlab.po
index 518de409ad9..f7a74885900 100644
--- a/locale/el_GR/gitlab.po
+++ b/locale/el_GR/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: el\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:22\n"
+"PO-Revision-Date: 2022-12-10 06:40\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/en_GB/gitlab.po b/locale/en_GB/gitlab.po
index a5f557c22ce..ddca9843cd6 100644
--- a/locale/en_GB/gitlab.po
+++ b/locale/en_GB/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: en-GB\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:24\n"
+"PO-Revision-Date: 2022-12-10 06:42\n"
msgid " %{start} to %{end}"
msgstr " %{start} to %{end}"
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] "%d more comment"
msgstr[1] "%d more comments"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%d pending comment"
@@ -506,6 +511,12 @@ msgstr[1] "%{bold_start}%{count}%{bold_end} opened merge requests"
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
@@ -706,15 +717,15 @@ msgstr "%{group_name} uses group managed accounts. You need to create a new GitL
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type} was deleted"
-
-msgid "%{hook_type} was scheduled for deletion"
-msgstr "%{hook_type} was scheduled for deletion"
-
msgid "%{host} sign-in from new location"
msgstr "%{host} sign-in from new location"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
+msgstr ""
+
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr ""
+
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr "Value"
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr "Must be %{minimumNumberOfUsers} (your seats in use) or more."
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr "Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr "Change failure rate"
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr "Days for an open incident"
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr "Median time an incident was open in a production environment over the gi
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr "No incidents during this period"
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr "Time to restore service"
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr "Enter any colour."
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr "Font Colour"
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr "Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr "Last 30 days"
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr "Projects in %{group} cannot be shared with other groups"
msgid "GroupSettings|Reporting"
msgstr "Reporting"
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr "Horizontal rule"
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr "Maximum login attempts exceeded. Wait %{interval} and try again."
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr "Verification code"
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr "Verification successful"
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr "No results"
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr "Only accessible by %{membersPageLinkStart}project members%{membersPageLi
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr "Duplicate packages"
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr "Colour for added lines"
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr "A new version is available"
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr "How do we upgrade GitLab runner?"
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr "No results found"
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr "Runners are the agents that run your CI/CD jobs. Follow the %{linkStart}
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr "Sign in to GitLab to connect your organisation's account"
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr "Inherited"
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr "Spent at"
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr "There are no secure files yet."
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr "There was a problem fetching CRM contacts."
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr "Wireframe"
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] "Assignees"
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr "Closed"
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr "Create task"
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr "Remove"
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr "Undo"
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr "You have insufficient permissions to manage resource links for this incident"
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,11 +48431,14 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr "%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
-msgstr "%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{scanner}: Loading resulted in an error"
+msgstr ""
msgid "ciReport|: Loading resulted in an error"
msgstr ""
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/eo/gitlab.po b/locale/eo/gitlab.po
index fc511ade48a..6a9e62f75b2 100644
--- a/locale/eo/gitlab.po
+++ b/locale/eo/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: eo\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:25\n"
+"PO-Revision-Date: 2022-12-10 06:43\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "Branĉoj"
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr "plenumiÄanta"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr "Etikedo"
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr "De la kreado de la problemo Äis la disponigado en la publika versio"
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr "Nova branĉo"
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "Nova problemo"
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr "Peti atingeblon"
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr "Iri al branĉo/etikedo"
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr "Malsteligi"
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr "Ne estas sufiĉe da datenoj por montri ĉi tiun etapon."
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr "Nuligi la peton pri atingeblo"
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr "Via nomo"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/eo/gitlab.po.time_stamp b/locale/eo/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/eo/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/es/gitlab.po b/locale/es/gitlab.po
index ba03c5c0c09..4b34a8e1cf2 100644
--- a/locale/es/gitlab.po
+++ b/locale/es/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: es-ES\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:22\n"
+"PO-Revision-Date: 2022-12-10 06:39\n"
msgid " %{start} to %{end}"
msgstr " %{start} hasta %{end}"
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] "%d comentario más"
msgstr[1] "%d comentarios más"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%d comentario pendiente"
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr "%{code_open}Enmascarado:%{code_close} Oculto en los registros de los trabajos. Debe coincidir con los requisitos de enmascaramiento."
@@ -706,15 +717,15 @@ msgstr "%{group_name} utiliza cuentas de grupo administradas. Debe crear una nue
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr "%{group_name}&%{epic_iid} &middot; creado %{epic_created} por %{author}"
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type} eliminado"
-
-msgid "%{hook_type} was scheduled for deletion"
-msgstr "Se ha programado %{hook_type} para su eliminación"
-
msgid "%{host} sign-in from new location"
msgstr "%{host} iniciar sesión desde una nueva ubicación"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
+msgstr ""
+
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr ""
+
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr "Las %{integrations_link_start}integraciones%{link_end} le permiten utilizar aplicaciones de terceros en su flujo de trabajo de GitLab. Si las integraciones disponibles no satisfacen sus necesidades. Puede considerar utilizar un %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr "%{openedEpics} abiertas, %{closedEpics} cerradas"
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr "%{openedIssues} abiertas, %{closedIssues} cerradas"
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr "%{reportType} %{status}"
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr "%{reportType} detectó %{totalStart}%{total}%{totalEnd} potencial %{vulnMessage}"
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr "%{template_project_id} es desconocido o no es válido"
msgid "%{text} is available"
msgstr "%{text} esta disponible"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr "%{type} solo admite %{name} nombre"
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr "(+%{count}&nbsp;reglas)"
msgid "(Group Managed Account)"
msgstr "(Cuenta administrada de grupo)"
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr "(Sin cambios)"
msgid "(UTC %{offset}) %{timezone}"
msgstr "(UTC %{offset}) %{timezone}"
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr "(comprobar el progreso)"
@@ -2364,6 +2386,9 @@ msgstr "Añadir hook del sistema"
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr "Agregar al tablero"
@@ -2451,6 +2476,9 @@ msgstr "Añadido en esta versión"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "Se ha deshabilitado la opción de añadir nuevas aplicaciones en su instancia de GitLab. Póngase en contacto con su administrador de GitLab para obtener el permiso"
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "Minutos adicionales"
@@ -2646,6 +2674,9 @@ msgstr "Error al detener trabajos"
msgid "AdminArea|Total users"
msgstr "Usuarios totales"
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr "Usuarios"
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr "Seleccionar una plantilla de CI/CD"
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr "Se ha producido un error. Por favor, inicie sesión de nuevo."
msgid "An error occurred. Please try again."
msgstr "Se ha producido un error. Por favor inténtelo de nuevo."
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr "Cualquier hito"
msgid "Any namespace"
msgstr "Cualquier espacio de nombres"
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr "App ID"
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] "¿Está seguro que desea importar el repositorio %d?"
msgstr[1] "¿Está seguro que desea importar los repositorios %d?"
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr "¿Está seguro de que desea bloquear %{path}?"
@@ -5604,6 +5635,9 @@ msgstr "Eliminar %{link}"
msgid "AuditStreams|Destination URL"
msgstr "URL de destino"
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr "Los destinos reciben todos los datos del evento de auditoría"
@@ -5649,6 +5683,9 @@ msgstr "AuditStreams|Valor"
msgid "AuditStreams|Verification token"
msgstr "Token de verificación"
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "Ago"
@@ -5880,9 +5917,6 @@ msgstr "Grupo de ejecutores disponible: %{runners}"
msgid "Available on-demand"
msgstr "Disponible bajo demanda"
-msgid "Available shared runners:"
-msgstr "Ejecutores compartidos disponibles:"
-
msgid "Available specific runners"
msgstr "Ejecutores dedicados disponibles"
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "Ramas"
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr "Activo"
@@ -7049,8 +7092,11 @@ msgstr ""
msgid "Branches|Compare"
msgstr "Comparar"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "Borrar todas las ramas que se fusionen con '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
+msgstr ""
msgid "Branches|Delete branch"
msgstr "Eliminar la rama"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "El borrado de las ramas fusionadas no podrá deshacerse. ¿Está usted seguro?"
-
msgid "Branches|Filter by branch name"
msgstr "Filtrar por nombre de rama"
@@ -7094,6 +7137,9 @@ msgstr "Descripción general"
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr "Mostrar ramas activas"
@@ -7127,6 +7173,12 @@ msgstr "No se puede eliminar la rama por defecto"
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr "Para descartar los cambios locales y sobreescribir la rama actual en el servidor, elimínela aquí y seleccione 'Actualizar ahora' en el enlace situado la parte superior."
@@ -7139,6 +7191,9 @@ msgstr "Sí, eliminar la rama"
msgid "Branches|Yes, delete protected branch"
msgstr "Sí, eliminar rama la protegida"
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr "Está a punto de eliminar permanentemente la rama %{branchName}."
@@ -7193,6 +7248,9 @@ msgstr "Se ha producido un error mientras al recuperar los artefactos"
msgid "BuildArtifacts|Loading artifacts"
msgstr "Cargando artefactos"
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr "Integrado"
@@ -7999,6 +8057,9 @@ msgstr "Pipeline #%{pipeline_id} %{humanized_status} en %{duration}"
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr "Tag"
@@ -8047,6 +8108,9 @@ msgstr "Compruebe sus imágenes de Docker para detectar vulnerabilidades conocid
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "Comprobando disponibilidad de %{text}..."
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr "Editar"
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr "Nombre de la empresa u organización que está utilizando GitLab"
@@ -8453,9 +8517,15 @@ msgstr "en ejecución"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr "No se puede utilizar una variable enmascarada con el valor actual"
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr "Entornos"
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr "Clave"
msgid "CiVariables|Masked"
msgstr "Máscara"
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr "Eliminar fila de variables"
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr "Alcance"
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr "Estado"
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr "Tipo"
@@ -8498,6 +8577,12 @@ msgstr "Valor"
msgid "CiVariables|Variables"
msgstr "Variables"
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr "* (Todos los entornos)"
@@ -9189,6 +9274,9 @@ msgstr "Token revocado por %{userName}"
msgid "ClusterAgents|Unknown user"
msgstr "Usuario desconocido"
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr "Configuración no encontrada"
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr "El nombre es obligatorio"
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr "Configurar la %{link} integración."
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr "Conectando"
msgid "Connecting to terminal sync service"
msgstr "Conectando al terminal de servicio de sincronización"
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr "Conectando..."
@@ -10382,12 +10488,6 @@ msgstr "Imágenes del registro de contenedores"
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr "El registro de contenedores no está activado en esta instancia de GitLab. Solicite a un administrador que lo habilite para que Auto DevOps funcione."
-msgid "Container repositories"
-msgstr "Repositorios de contenedores"
-
-msgid "Container repository"
-msgstr "Repositorio de contenedores"
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr "Etiqueta marcada correctamente para su eliminación."
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr "Etiquetas marcadas correctamente para su eliminación."
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr "Controle los correos electrónicos vinculados a su cuenta"
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr "País"
@@ -11285,9 +11382,6 @@ msgstr "Crear incidencia"
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr "Crear iteración"
-
msgid "Create label"
msgstr "Crear etiqueta"
@@ -11366,6 +11460,9 @@ msgstr "Crear fragmento de código"
msgid "Create tag %{tagName}"
msgstr "Crear etiqueta %{tagName}"
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr "Crear tema"
@@ -11402,6 +11499,42 @@ msgstr "No tiene permisos para crear grupos."
msgid "CreateTag|Tag"
msgstr "Etiqueta"
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr "%{name} (por defecto)"
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr "Tarjeta de crédito:"
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr "Vulnerabilidades críticas presentes"
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr "Cambiar tasa de fallo"
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr "Días para un incidente abierto"
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr "La mediana de tiempo en el que se abrió un incidente en un entorno de p
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr "Sin incidencias durante este periodo"
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr "DORA4Metrics|Tiempo para restaurar el servicio"
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr "Deshabilitado"
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr "Mínimo = 1 segundo, Máximo = 3600 segundos"
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr "Nuevo perfil de análisis"
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr "Días para hacer merge"
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr "Primer día de la semana por defecto"
msgid "Default first day of the week in calendars and date pickers."
msgstr "Primer día de la semana por defecto en calendarios y selectores de fechas."
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr "Límite predeterminado de proyectos"
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr "Despliegues"
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr "Descripción:"
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr "Etiqueta descriptiva"
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr "Adopción DevOps"
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr "Introduzca al menos tres caracteres para buscar"
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr "Introduzca a continuación, la URL de su servidor de Bitbucket y su token de acceso personal"
@@ -15519,9 +15712,6 @@ msgstr "Eliminar tarea épica"
msgid "Epics|Remove issue"
msgstr "Eliminar la incidencia"
-msgid "Epics|Show more"
-msgstr "Mostrar más"
-
msgid "Epics|Something went wrong while creating child epics."
msgstr "Se ha producido un error al crear las tareas épicas hijas."
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr "Se ha producido un error al obtener los refs"
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr "Se ha producido un error al obtener la lista de dependencias. Por favor revise su conexión a internet y vuelva a intentarlo."
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr "minutos"
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr "Estimación"
@@ -16091,6 +16293,9 @@ msgstr "Todo el mundo puede colaborar"
msgid "Everything on your to-do list is marked as done."
msgstr "Todo lo que está en su lista de tareas se ha marcado como hecho."
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr "Recopilación de evidencias"
msgid "Exactly one of %{attributes} is required"
msgstr "Exactamente uno de los %{attributes} es necesario"
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr "Ejemplo: @sub\\.empresa\\.com$"
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr "Tiempo de ejecución"
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr "Expandir barra lateral"
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr "Documentos esperados: %{expected_documents}"
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr "Se ha producido un error al obtener ref."
-msgid "Failed to install."
-msgstr "Se ha producido un error al instalar."
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr "Se ha producido un error al actualizar el estado de la incidencia"
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr "Se ha producido un error al actualizar."
-
msgid "Failed to upload object map file"
msgstr "Se ha producido un error al cargar el archivo de mapa de objetos"
@@ -16937,6 +17148,9 @@ msgstr "Feb"
msgid "February"
msgstr "Febrero"
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr "Actividad de los usuarios seguidos"
msgid "Followed users"
msgstr "Usuarios seguidos"
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr "Color de la fuente"
@@ -17345,12 +17553,21 @@ msgstr "Fork en progreso"
msgid "Forks"
msgstr "Forks"
-msgid "Format: %{dateFormat}"
-msgstr "Formato: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|Up to date with upstream repository"
msgstr ""
+msgid "Format: %{dateFormat}"
+msgstr "Formato: %{dateFormat}"
+
msgid "Found errors in your %{gitlab_ci_yml}:"
msgstr "Se han encontrado errores en su archivo %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr "De %{providerTitle}"
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr "Desde la creación de la incidencia hasta el despliegue a producción"
@@ -17475,6 +17687,9 @@ msgstr "Pipelines"
msgid "General settings"
msgstr "Configuración general"
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr "Generar un conjunto predeterminado de etiquetas"
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr "Ir a sus merge requests"
msgid "Go to your projects"
msgstr "Ir a sus proyectos"
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr "Ir a sus fragmentos de código"
@@ -18868,6 +19097,12 @@ msgstr "Conceder permisos de escritura a esta clave"
msgid "Graph"
msgstr "Gráfico"
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr "Dependencias del trabajo"
@@ -19057,7 +19292,7 @@ msgstr "GroupActivityMetrics|Últimos 30 días"
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr "Dentro de 3 años"
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr "Nombre del grupo SAML"
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr "Nombre del grupo SAML %{saml_group_name}"
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr "Salida de la respuesta SAML"
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr "GroupSettings|Informes"
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr "Se ha producido un error al actualizar el pipeline de Auto DevOps: %{error_messages}."
@@ -19573,6 +19811,9 @@ msgstr "Conectar instancia"
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr "Contacte con un administrador para habilitar las opciones para importar su grupo."
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr "Crear grupo"
@@ -19582,9 +19823,6 @@ msgstr "Crear nuevo grupo"
msgid "GroupsNew|Create subgroup"
msgstr "Crear subgrupo"
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr "Estructura no disponible"
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr "Hay vulnerabilidades altas o desconocidas presentes"
msgid "Highest role:"
msgstr "Rol más alto:"
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr "Página principal"
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr "Falló la ejecución del hook. Asegúrese de que el grupo tiene, al menos, un proyecto con commits."
-msgid "Hook was successfully updated."
-msgstr "El hook se actualizó correctamente."
-
msgid "Horizontal rule"
msgstr "Regla horizontal"
@@ -20268,6 +20509,9 @@ msgstr "Identificadores"
msgid "Identities"
msgstr "Identidades"
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr "Crear un proyecto"
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr "Se han superado el número máximo de intentos de inicio de sesión. Esp
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr "IdentityVerification|Código de verificación"
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr "IdentityVerification|Verificación correcta"
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr "Verifique su identidad"
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr "Incidente"
msgid "Incident Management Limits"
msgstr "Límites de gestión de incidentes"
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr "Plantilla de incidente (opcional)."
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr "%{hours} horas, %{minutes} minutos restantes"
@@ -21624,6 +21895,9 @@ msgstr "Se ha cancelado la eliminación del índice"
msgid "Indicates whether this runner can pick jobs without tags"
msgstr "Indica si este ejecutor puede seleccionar trabajos sin etiquetas"
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr "Informa a los usuarios que no hayan cargado las claves SSH que no pueden hacer push sobre SSH hasta que se agregue una"
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr "Habilitar comentarios"
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr "Los miembros se han añadido correctamente"
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr "Título"
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr "No hay trabajos a mostrar"
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr "Este trabajo está bloqueado porque el proyecto no tiene ningún ejecuto
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr "Clave"
msgid "Key (PEM)"
msgstr "Clave (PEM)"
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr "Clústeres de Kubernetes"
msgid "Kubernetes deployment not found"
msgstr "Despliegue de Kubernetes no encontrado"
-msgid "Kubernetes error: %{error_code}"
-msgstr "Error de Kubernetes: %{error_code}"
-
msgid "LDAP"
msgstr "LDAP"
@@ -23748,15 +24025,18 @@ msgstr "Etiquetas"
msgid "Labels"
msgstr "Etiquetas"
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr "Las etiquetas se pueden aplicar a %{features}. Las etiquetas de los grupos se encuentran disponibles para cualquier proyecto dentro del grupo."
-
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "Las etiquetas se pueden aplicar a incidencias y merge requests para categorizarlos."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr "Etiquetas sin incidencias en esta iteración:"
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr "Obtener mas información"
msgid "Learn More."
msgstr "Obtener mas información."
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr "Aprenda como contribuir %{link_start}a las plantillas integradas%{link_end}"
@@ -24623,12 +24909,12 @@ msgstr "Archivos bloqueados"
msgid "Locked by %{fileLockUserName}"
msgstr "Bloqueado por %{fileLockUserName}"
+msgid "Locked files"
+msgstr ""
+
msgid "Locked the discussion."
msgstr "Bloqueó la discusión."
-msgid "Locks give the ability to lock specific file or folder."
-msgstr "Los bloqueos brindan la capacidad de bloquear archivos o carpetas específicos."
-
msgid "Locks the discussion."
msgstr "Bloquea la discusión."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr "Logs"
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr "Hay vulnerabilidades bajas presentes"
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr "Miembros"
@@ -26428,6 +26729,9 @@ msgstr "Añadir proyectos"
msgid "Modal|Close"
msgstr "Cerrar"
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr "Modificado"
@@ -26491,9 +26795,6 @@ msgstr "Más información"
msgid "More information"
msgstr "Más información"
-msgid "More information and share feedback"
-msgstr "Más información y compartir comentarios"
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr "Nueva rama"
msgid "New branch unavailable"
msgstr "Nueva rama no disponible"
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr "Nuevo título épico confidencial "
@@ -26924,6 +27231,12 @@ msgstr "¡Se ha generado un nuevo token de acceso para la verificación de estad
msgid "New identity"
msgstr "Nueva identidad"
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "Nueva incidencia"
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr "Sin iteración"
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr "No hay registro de tareas"
@@ -27299,6 +27609,9 @@ msgstr "No hay resultados"
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr "OK"
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr "El objeto no existe en el servidor o no tiene permisos para acceder a él"
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr "No hay ningún análisis programado."
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr "Ver resultados"
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr "Abre en una nueva ventana"
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr "La operación ha fallado. Por favor, compruebe los registros del pod para %{pod_name} para más obtener más información."
-
msgid "Operation not allowed"
msgstr "Operación no permitida"
@@ -28813,6 +29138,9 @@ msgstr "Conan"
msgid "PackageRegistry|Conan Command"
msgstr "Comando de Conan"
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr "Copiar el contenido de .pypirc"
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr "PackageRegistry|Paquetes duplicados"
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr "Para obtener más información sobre el registro de NuGet , %{linkStart}
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr "Para obtener más información sobre el registro PyPiPi, %{linkStart}vea la documentación%{linkEnd}."
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr "Genérico"
@@ -29013,6 +29362,9 @@ msgstr "Comando NuGet"
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr "Registro de paquetes"
@@ -29028,6 +29380,9 @@ msgstr "Paquete eliminado correctamente"
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] "El paquete tiene %{updatesCount} actualizacion archivada"
@@ -29036,6 +29391,9 @@ msgstr[1] "El paquete tiene %{updatesCount} actualizaciones archivadas"
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr "Paquete actualizado por el commit %{link} en la rama %{branch}, construido por pipeline %{pipeline}, y publicado en el registro %{datetime}"
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr "Mostrar comandos de PyPi"
msgid "PackageRegistry|Show Yarn commands"
msgstr "Mostrar comandos de Yarn"
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr "Tipo"
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr "No se ha podido obtener la información de la versión del paquete."
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr "Está a punto de eliminar la versión %{version} de %{name}. ¿Está seguro?"
@@ -29176,6 +29539,9 @@ msgstr "npm"
msgid "PackageRegistry|published by %{author}"
msgstr "publicado por %{author}"
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr "Parámetro"
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr "El parámetro \"job_id\" no puede exceder la longitud de %{job_id_max_size}"
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr "Padre"
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr "Escoja un nombre"
@@ -30466,6 +30868,9 @@ msgstr "Por favor revise su correo electrónico (%{email}) para verificar que es
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr "Por favor, complete su perfil con una dirección de correo electrónico"
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr "Por favor, escriba %{phrase_code} para continuar o cierre esta ventana modal para cancelar."
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr "Utilice este formulario para informar a los administradores sobre los usuarios que crean spam en las incidencias, en los comentarios o se comportan de una manera inadecuada."
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "Seleccione que contenido desea ver en la página de resumen de un proyecto."
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr "Tema del resaltado de la sintaxis"
msgid "Preferences|Tab width"
msgstr "Ancho de pestaña"
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr "Esta configuración le permite personalizar el comportamiento del diseñ
msgid "Preferences|Time preferences"
msgstr "Preferencias de hora"
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr "Utilizar tiempos relativos"
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr "Continuar"
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr "Actualizar el nombre de usuario"
msgid "Profiles|Upload new avatar"
msgstr "Cargar un avatar nuevo"
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr "Utilice un correo electrónico privado - %{email}"
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr "Permitir"
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr "Cada proyecto puede tener su propio espacio para almacenar sus imágenes Docker"
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr "Visibilidad del proyecto"
msgid "ProjectSettings|Public"
msgstr "Público"
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr "Ver y editar archivos en este proyecto."
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr "Netlify/HTML plano"
msgid "ProjectTemplates|NodeJS Express"
msgstr "NodeJS Express"
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr "Pages/Gatsby"
@@ -32881,9 +33322,6 @@ msgstr "Rama protegida"
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr "Entorno protegido"
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] "Actualizar en un segundo para mostrar el estado actualizado..."
msgstr[1] "Actualizar en %d segundos para mostrar el estado actualizado..."
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr "Reabrir este%{quick_action_target}"
msgid "Reopened this %{quick_action_target}."
msgstr "Reabrió este %{quick_action_target}."
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr "Reabrir este %{quick_action_target}."
@@ -34145,11 +34592,8 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr "Informar de un abuso"
-
-msgid "Report abuse to admin"
-msgstr "Informar de un abuso al administrador"
+msgid "Report abuse to administrator"
+msgstr ""
msgid "Report couldn't be prepared."
msgstr ""
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr "Solicitar acceso"
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr "Reanudar"
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr "Tiempo de revisión"
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr "Ejecutar tareas de mantenimiento"
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr "Hay una nueva versión disponible"
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr "Asociado con uno o más proyectos"
msgid "Runners|Available"
msgstr "Disponible"
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr "Disponible para todos los proyectos"
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr "¿Cómo actualizamos el ejecutor de GitLab?"
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr "Runners|No se encontraron resultados"
@@ -35249,6 +35717,9 @@ msgstr "Ejecutor #%{runner_id}"
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr "Los ejecutores son los agentes de GitLab que ejecutan sus trabajos de CI
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr "Propietario"
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr "En ejecución"
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr "Inicie sesión en GitLab para conectar la cuenta de su organización"
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr "Clave pública SSH"
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr "Verificación SSL:"
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr "Buscar"
msgid "Search GitLab"
msgstr "Buscar en GitLab"
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr "Seleccione un proyecto"
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr "Heredado"
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr "%{firstProject} y %{secondProject}"
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr "%{firstProject}, %{secondProject} y %{rest}"
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr "Añadir proyectos"
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr "No se encontraron vulnerabilidades"
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr "No se encontraron vulnerabilidades para este pipeline"
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr "Oops, algo parece que no está correcto."
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr "Seleccione un archivo en la barra lateral izquierda para comenzar la edición. Posteriormente, podrá hacer commit de sus cambios."
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr "Seleccione una etiqueta"
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr "Seleccionar revisor(es)"
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr "Ejecturores compartidos"
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr "Proyectos compartidos"
@@ -37723,6 +38299,9 @@ msgstr "Mostrar la última versión"
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr "Iniciar sesión en GitLab"
@@ -38037,7 +38613,7 @@ msgstr "La integración con Slack le permite interactuar con GitLab mediante com
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,14 +38628,17 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr "Alias del proyecto"
-msgid "SlackIntegration|Reinstall Slack app"
-msgstr "Reinstalar la aplicación Slack"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
+msgstr ""
msgid "SlackIntegration|Remove project"
msgstr "Eliminar proyecto"
@@ -38082,14 +38661,17 @@ msgstr "Nombre del equipo"
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr "Esta integración permite a los usuarios realizar operaciones comunes en este proyecto mediante la introducción de comandos en Slack."
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr "Token de verificación"
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr "Ahora puede cerrar esta ventana e ir a su espacio de trabajo de Slack."
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
-msgstr "Es posible que deba reinstalar de nuevo la aplicación Slack cuando %{linkStart}realicemos actualizaciones o cambiemos los permisos%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
msgstr ""
@@ -38226,12 +38808,12 @@ msgstr "Algunas tareas épicas pueden estar ocultas debido a los filtros aplicad
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr "Algunos dominios comunes no están permitidos. %{learn_more_link}"
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
+msgstr ""
+
msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr "Alguien editó este archivo al mismo tiempo que usted. Por favor revise %{link_start} el archivo %{icon}%{link_end}} y asegúrese de que los cambios que ha realizado no eliminen sin querer los cambios realizados por la otra persona."
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
-msgstr "Alguien editó esta incidencia al mismo tiempo que usted. Por favor revise %{linkStart} esta incidencia%{linkEnd} y asegúrese de que los cambios que ha realizado no eliminen sin querer los cambios realizados por la otra persona."
-
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr "Alguien editó este %{issueType} al mismo tiempo que usted. La descripción se ha actualizado y deberá volver a realizar los cambios."
@@ -38271,6 +38853,9 @@ msgstr "Se ha producido un error al intentar cargar los contactos de la incidenc
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr "Se ha producido un error al archivar un requisito."
msgid "Something went wrong while closing the epic. Please try again later."
msgstr "Se ha producido un error al cerrar la tarea épica . ¡Por favor, inténtelo de nuevo más tarde!"
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr "Se produjo un error al obtener la lista de paquetes."
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr "Gastado en"
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr "Modificar el mensaje de commit"
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr "Iniciar una búsqueda"
@@ -38919,6 +39516,9 @@ msgstr "Estadísticas"
msgid "Status"
msgstr "Estado"
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr "Se volvió a intentar el estado."
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr "Cambiar rama"
msgid "Switch branch/tag"
msgstr "Cambiar rama/etiqueta"
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr "Cambiar a GitLab Next"
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr "Equipo"
@@ -40611,6 +41217,9 @@ msgstr "El siguiente token de acceso personal fue revocado por un administrador,
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr "La siguiente clave SSH fue eliminada por un administrador, %{username}."
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr "No se exportarán los siguientes elementos:"
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr "Aún no hay proyectos archivados"
-
msgid "There are no archived requirements"
msgstr "No hay requisitos archivados"
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr "Todavía no hay paquetes"
-msgid "There are no projects shared with this group yet"
-msgstr "Aún no hay proyectos compartidos con este grupo"
-
msgid "There are no secure files yet."
msgstr "Todavía no hay archivos seguros."
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr "Se ha producido un problema al conectarse con su dispositivo."
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr "Se ha producido un error al recuperar los contactos desde CRM."
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr "Se ha producido un error al obtener información sobre los entornos."
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr "Se ha producido un error al restablecer los minutos de ejecución de los
msgid "There was an error retrieving the Jira users."
msgstr "Se ha producido un error al recuperar los usuarios de Jira."
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr "Se ha producido un error al guardar sus cambios."
@@ -41335,9 +41947,6 @@ msgstr "Este %{viewer} no se puede mostrar porque %{reason}. En su lugar, puedes
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr "Esta tarea épica no existe o no tiene permisos suficientes."
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr "Esta característica requiere que el almacenamiento local esté activado"
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr "Este grupo"
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr "Tiempo restante"
msgid "Time spent"
msgstr "Tiempo dedicado"
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr "Título:"
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr "Títulos y descripciones"
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr "Para añadir la entrada manualmente, proporcione los siguientes detalles a la aplicación en su teléfono."
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr "El disparador se actualizó correctamente."
msgid "Triggerer"
msgstr "Disparador"
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr "Desafortunadamente, su mensaje de correo electrónico a GitLab no pudo s
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr "ms"
@@ -43384,6 +44029,9 @@ msgstr "Sin resolver"
msgid "Unschedule job"
msgstr "No programado"
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr "No Destacar"
@@ -43567,6 +44215,9 @@ msgstr "Subir los cambios al terminal"
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr "Upstream"
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr "%{help_link_start}Los ejecutores compartidos%{help_link_end} están deshabilitados, por lo que no hay límites establecidos para el uso de los pipelines"
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr "%{percentageLeft} de almacenamiento comprado está disponible"
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr "Periodo actual de uso"
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr "Este espacio de nombres no tiene proyectos que utilicen ejecutores compartidos"
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr "Ilimitado"
-
msgid "UsageQuota|Uploads"
msgstr "Subidas"
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr "Wiki"
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr "Proyectos personales"
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr "Informar de abuso"
-
msgid "UserProfile|Retry"
msgstr "Reintentar"
@@ -44481,6 +45108,9 @@ msgstr "Utilizar la estrategia de cifrado requerida cuando falta el campo cifrad
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr "Valido desde"
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,12 +45255,12 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
-msgstr ""
-
msgid "Variable"
msgstr "Variable"
+msgid "Variable value will be evaluated as raw string."
+msgstr ""
+
msgid "Variable will be masked in job logs."
msgstr ""
@@ -44634,7 +45270,7 @@ msgstr "Variables"
msgid "Variables can be:"
msgstr "Las variables pueden ser:"
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr "Versión %{versionNumber}"
msgid "Version %{versionNumber} (latest)"
msgstr "Versión %{versionNumber} (última)"
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr "Actualizado"
@@ -44700,6 +45351,18 @@ msgstr "Actualizar lo antes posible"
msgid "VersionCheck|Update available"
msgstr "Actualización disponible"
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr "Su versión de GitLab"
@@ -45360,6 +46023,9 @@ msgstr "Hemos detectado spam potencial en %{humanized_resource_name}. Por favor
msgid "We don't have enough data to show this stage."
msgstr "No hay suficientes datos para mostrar en esta etapa."
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr "Hemos encontrado los siguientes errores:"
@@ -45435,9 +46101,6 @@ msgstr "Dispositivos WebAuthn (%{length})"
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr "WebAuthn sólo funciona con sitios web habilitados para HTTPS. Póngase en contacto con su administrador para obtener más detalles."
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr "Ajustes Webhook"
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr "Webhook:"
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr "Comentarios"
-
msgid "Webhooks|Confidential comments"
msgstr "Comentarios confidenciales"
@@ -45618,14 +46278,11 @@ msgstr "Eventos de los miembros"
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr "Eventos de pipelines"
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
-msgstr ""
+msgid "Webhooks|Pipeline events"
+msgstr "Eventos de pipelines"
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
msgstr ""
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr "Disparador"
-msgid "Webhooks|URL"
-msgstr "URL"
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr "Se creará"
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr "Se desplegará a"
@@ -46066,9 +46723,6 @@ msgstr "Malla"
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr "Retirar solicitud de acceso"
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr "Añadir tarea"
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] "Asignados"
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr "Elemento hijo eliminado"
@@ -46134,6 +46794,12 @@ msgstr "Cerrado"
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr "Crear tarea"
@@ -46146,30 +46812,39 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
+msgid "WorkItem|Existing task"
+msgstr ""
+
msgid "WorkItem|Expand tasks"
msgstr ""
msgid "WorkItem|Incident"
msgstr ""
-msgid "WorkItem|Introducing tasks"
-msgstr "Introducción de tareas"
-
msgid "WorkItem|Issue"
msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr "Eliminar"
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr "Deshacer"
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr "Escriba un comentario…"
msgid "Write a description or drag your files here…"
msgstr "Escriba una descripción o arrastre sus archivos aquí.."
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr "Siempre puede editar esto más tarde"
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr "Ya puede cerrar esta ventana."
-msgid "You can now export your security dashboard to a CSV report."
-msgstr "Ahora puede exportar su panel de seguridad a un informe CSV."
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr "Puede enviar un merge request para tener este cambio en la rama original."
msgid "You can now submit a merge request to get this change into the original project."
msgstr "Puede enviar un merge request para tener este cambio en el proyecto original."
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr "No es posible crear un nuevo disparador."
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr "Aún no tiene ninguna suscripción"
@@ -46741,6 +47423,9 @@ msgstr "No tiene ninguna búsquedas reciente"
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr "No tiene permisos para crear este proyecto"
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr "Ya ha habilitado la autenticación de dos pasos utilizando una contraseÃ
msgid "You've rejected %{user}"
msgstr "Ha rechazado a %{user}"
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr "Su grupo de GitLab"
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr "Sus grupos"
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr "Tus grupos"
@@ -47275,6 +47961,15 @@ msgstr "Tu nombre"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr "Su nuevo %{accessTokenType}"
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr "ciReport|%{scanner} detectó %{number} nuevo potencial %{vulnStr}"
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr "%{scanner} no ha detectado nada nuevo %{vulnStr}"
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,8 +48596,8 @@ msgstr "Solucionado:"
msgid "ciReport|Found %{issuesWithCount}"
msgstr "Encontrado %{issuesWithCount}"
-msgid "ciReport|Full Report"
-msgstr "Informe completo"
+msgid "ciReport|Full report"
+msgstr ""
msgid "ciReport|Generic Report"
msgstr ""
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr "comentado en %{link_to_project}"
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr "commit %{commit_id}"
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr "excede la longitud máxima (100 nombres de usuario)"
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr "este es un rango de direcciones IP no válido"
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr "debe ser mayor que la fecha de inicio"
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr "repositorio:"
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/es/gitlab.po.time_stamp b/locale/es/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/es/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/et_EE/gitlab.po b/locale/et_EE/gitlab.po
index eefececf5aa..490bfbe18e0 100644
--- a/locale/et_EE/gitlab.po
+++ b/locale/et_EE/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: et\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:21\n"
+"PO-Revision-Date: 2022-12-10 06:42\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/fa_IR/gitlab.po b/locale/fa_IR/gitlab.po
index 54400b98ac9..f8434a72c3b 100644
--- a/locale/fa_IR/gitlab.po
+++ b/locale/fa_IR/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: fa\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:21\n"
+"PO-Revision-Date: 2022-12-10 06:42\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/fi_FI/gitlab.po b/locale/fi_FI/gitlab.po
index 3e0ca3caa63..deb43e86198 100644
--- a/locale/fi_FI/gitlab.po
+++ b/locale/fi_FI/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: fi\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:22\n"
+"PO-Revision-Date: 2022-12-10 06:40\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/fil_PH/gitlab.po b/locale/fil_PH/gitlab.po
index 862adcf8340..0331d753177 100644
--- a/locale/fil_PH/gitlab.po
+++ b/locale/fil_PH/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: fil\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:25\n"
+"PO-Revision-Date: 2022-12-10 06:43\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/fr/gitlab.po b/locale/fr/gitlab.po
index 892b4268642..f49c48bcf0e 100644
--- a/locale/fr/gitlab.po
+++ b/locale/fr/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: fr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:22\n"
+"PO-Revision-Date: 2022-12-11 07:24\n"
msgid " %{start} to %{end}"
msgstr " %{start} à %{end}"
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] "%d autre commentaire"
msgstr[1] "%d autres commentaires"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] "%d paquet"
+msgstr[1] "%d paquets"
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%d commentaire en attente"
@@ -465,7 +470,7 @@ msgid "%{actionText} & close %{noteable}"
msgstr "%{actionText} et fermer %{noteable}"
msgid "%{actionText} & reopen %{noteable}"
-msgstr "%{actionText} et réouvrir %{noteable}"
+msgstr "%{actionText} et rouvrir %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr "%{address} est une plage d'adresses IP non valide"
@@ -506,6 +511,12 @@ msgstr[1] "%{bold_start}%{count}%{bold_end} demandes de fusion ouvertes"
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr "%{codeStart}$%{codeEnd} sera traité comme le début d'une référence à une autre variable."
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr "%{code_open} Développée :%{code_close} Les variables avec %{code_open}$%{code_close} seront traitées comme le début d'une référence vers une autre variable."
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr "%{code_open}Masqué :%{code_close} Masqué dans les journaux des logs. Doit correspondre aux exigences de masquage."
@@ -602,8 +613,8 @@ msgstr "%{count} sur %{total}"
msgid "%{count} participant"
msgid_plural "%{count} participants"
-msgstr[0] "%{count} participant·e"
-msgstr[1] "%{count} participant·e·s"
+msgstr[0] "%{count} participant"
+msgstr[1] "%{count} participants"
msgid "%{count} project"
msgid_plural "%{count} projects"
@@ -668,10 +679,10 @@ msgid "%{emailPrefix}@company.com"
msgstr "%{emailPrefix}@societe.fr"
msgid "%{enable_service_ping_link_start}Enable%{link_end} or %{generate_manually_link_start}generate%{link_end} Service Ping to preview and download service usage data payload."
-msgstr ""
+msgstr "%{enable_service_ping_link_start}Activez%{link_end} ou %{generate_manually_link_start}générez%{link_end} le Ping de Service pour prévisualiser et télécharger la charge utile des données d'utilisation du service."
msgid "%{extra} more downstream pipelines"
-msgstr ""
+msgstr "%{extra} pipelines de plus en aval"
msgid "%{filePath} deleted"
msgstr "%{filePath} supprimé"
@@ -706,17 +717,17 @@ msgstr "%{group_name} utilise les comptes gérés du groupe. Vous devez créer u
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr "%{group_name}&%{epic_iid} &middot; créée %{epic_created} par %{author}"
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type} a été supprimé"
-
-msgid "%{hook_type} was scheduled for deletion"
-msgstr "%{hook_type} était programmé pour la suppression"
-
msgid "%{host} sign-in from new location"
msgstr "%{host} connexion depuis un nouvel emplacement"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
+msgstr "%{human_readable_key} fait plus de %{max_value_length} caractères"
+
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr "%{human_readable_key} fait moins de %{min_value_length} caractères"
+
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
-msgstr "Les %{integrations_link_start}Intégrations%{link_end} vous permettent d'inclure des applications tierces à votre flux de travail GitLab. Si les intégrations disponibles ne répondent pas à vos besoins, envisagez l'utilisation de %{webhooks_link_start}webhooks%{link_end}."
+msgstr "Les %{integrations_link_start}Intégrations%{link_end} vous permettent d'inclure des applications tierces à votre flux de travail GitLab. Si les intégrations disponibles ne répondent pas à vos besoins, envisagez l'utilisation des %{webhooks_link_start}crochets web%{link_end}."
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} sera supprimé ! Êtesâ€vous sûr ?"
@@ -906,9 +917,6 @@ msgstr "%{openedEpics} ouvertes, %{closedEpics} fermées"
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr "%{openedIssues} ouverts, %{closedIssues} fermés"
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr "%{over_limit_message} Pour pouvoir disposer de plus de membres, un propriétaire du groupe peut commencer une période d'essai ou faire une mise à niveau vers une édition payante."
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr "%{over_limit_message} Pour obtenir plus de sièges, %{link_start}passez à une édition payante%{link_end}."
@@ -925,7 +933,7 @@ msgid "%{percent}%% complete"
msgstr "%{percent} %% effectués"
msgid "%{percent}%{percentSymbol} complete"
-msgstr "%{percent}%{percentSymbol} completé"
+msgstr "Terminée à %{percent}%{percentSymbol}"
msgid "%{placeholder} is not a valid color scheme"
msgstr "%{placeholder} n'est pas un jeu de couleurs valide"
@@ -959,8 +967,8 @@ msgstr "%{reportType} %{status}"
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr "%{reportType} a détecté %{totalStart}%{total}%{totalEnd} %{vulnMessage} possibles"
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
-msgstr "%{reportType} n’a détecté aucune %{totalStart}nouvelle%{totalEnd} vulnérabilité."
+msgid "%{reportType} detected no new vulnerabilities."
+msgstr "%{reportType} n'a détecté aucune nouvelle vulnérabilité."
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
msgstr "%{retryButtonStart}Réessayez%{retryButtonEnd} ou %{newFileButtonStart}joignez un nouveau fichier%{newFileButtonEnd}."
@@ -1034,7 +1042,7 @@ msgid "%{start} to %{end}"
msgstr "%{start} à %{end}"
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
-msgstr ""
+msgstr "%{strongOpen} Avertissement :%{strongClose} les liens de groupe SAML peuvent conduire GitLab a retirer automatiquement des membres de certains groupes."
msgid "%{strongStart}%{count}%{strongEnd} commit"
msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
@@ -1108,11 +1116,16 @@ msgstr "%{template_project_id} est inconnu ou invalide"
msgid "%{text} is available"
msgstr "%{text} est disponible"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
-msgstr "%{timebox_type} ne prend pas en charge les graphiques burnup"
+msgstr "%{timebox_type} ne prend pas en charge les graphiques d'évolution"
msgid "%{timebox_type} must have a start and due date"
-msgstr ""
+msgstr "%{timebox_type} doit avoir une date de début et une date d'échéance"
msgid "%{time} UTC"
msgstr "%{time} UTC"
@@ -1136,7 +1149,7 @@ msgid "%{total_warnings} warning(s) found:"
msgstr "%{total_warnings} avertissement(s) trouvé(s) :"
msgid "%{total} remaining issue weight"
-msgstr ""
+msgstr "%{total} poids de ticket restant(s)"
msgid "%{total} warnings found: showing first %{warningsDisplayed}"
msgstr "%{total} avertissements trouvés : affichage des %{warningsDisplayed} premiers"
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr "%{url} (facultatif)"
+
msgid "%{userName} (cannot merge)"
msgstr "%{userName} (ne peut pas fusionner)"
@@ -1199,10 +1215,10 @@ msgid "%{verb} this %{noun} as ready."
msgstr "%{verb} ce(tte) %{noun} comme étant prêt(e)."
msgid "%{webhooks_link_start}%{webhook_type}%{link_end} enable you to send notifications to web applications in response to events in a group or project."
-msgstr "%{webhooks_link_start}%{webhook_type}%{link_end} vous permet d'envoyer des notifications aux applications web en réponse à des événements dans un groupe ou dans un projet."
+msgstr "Les %{webhooks_link_start}%{webhook_type}%{link_end} vous permettent d'envoyer des notifications aux applications web en réponse à des événements dans un groupe ou dans un projet."
msgid "%{webhooks_link_start}%{webhook_type}%{link_end} enable you to send notifications to web applications in response to events in a group or project. We recommend using an %{integrations_link_start}integration%{link_end} in preference to a webhook."
-msgstr "%{webhooks_link_start}%{webhook_type}%{link_end} vous permet d'envoyer des notifications aux applications web en réponse aux événements d'un groupe ou d'un projet. Nous recommandons d'utiliser une %{integrations_link_start}intégration%{link_end} plutôt qu'un webhook."
+msgstr "Les %{webhooks_link_start}%{webhook_type}%{link_end} vous permettent d'envoyer des notifications aux applications web en réponse aux événements d'un groupe ou d'un projet. Nous recommandons d'utiliser une %{integrations_link_start}intégration%{link_end} plutôt qu'un crochet web."
msgid "%{widget} options"
msgstr ""
@@ -1211,22 +1227,22 @@ msgid "%{wildcards_link_start}Wildcards%{wildcards_link_end} such as %{code_tag_
msgstr "Les %{wildcards_link_start}caractères génériques%{wildcards_link_end} tels que %{code_tag_start}v*%{code_tag_end} ou %{code_tag_start}*-release%{code_tag_end} sont pris en charge."
msgid "'%{data}' at %{location} does not match format: %{format}"
-msgstr ""
+msgstr "« %{data} » sur %{location} ne correspond pas au format : %{format}"
msgid "'%{data}' at %{location} does not match pattern: %{pattern}"
-msgstr ""
+msgstr "« %{data} » sur %{location} ne correspond pas au motif : %{pattern}"
msgid "'%{data}' at %{location} is invalid: error_type=%{type}"
-msgstr ""
+msgstr "« %{data} » sur %{location} n'est pas valide : error_type=%{type}"
msgid "'%{data}' at %{location} is not of type: %{type}"
msgstr "« %{data} » à %{location} n'est pas de type : %{type}"
msgid "'%{data}' at %{location} is not one of: %{enum}"
-msgstr ""
+msgstr "« %{data} » sur %{location} ne fait pas partie de : %{enum}"
msgid "'%{data}' at %{location} is not: %{const}"
-msgstr ""
+msgstr "« %{data} » sur %{location} n'est pas : %{const}"
msgid "'%{level}' is not a valid visibility level"
msgstr "« %{level} » n'est pas un niveau de visibilité valide"
@@ -1266,12 +1282,18 @@ msgstr "(+%{count}&nbsp;règles)"
msgid "(Group Managed Account)"
msgstr "(Compte Géré de Groupe)"
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr "(Minutes de pipeline limitées à %{quota} par mois)"
+
msgid "(No changes)"
msgstr "(Aucun changement)"
msgid "(UTC %{offset}) %{timezone}"
msgstr "(UTC %{offset}) %{timezone}"
+msgid "(Unlimited pipeline minutes)"
+msgstr "(Minutes de pipeline illimitées)"
+
msgid "(check progress)"
msgstr "(suivre la progression)"
@@ -1332,7 +1354,7 @@ msgid "+%{more_assignees_count}"
msgstr "+%{more_assignees_count}"
msgid "+%{more_assignees_count} more assignees"
-msgstr ""
+msgstr "+%{more_assignees_count} autres personnes assignées"
msgid "+%{more_reviewers_count}"
msgstr "+%{more_reviewers_count}"
@@ -1350,7 +1372,7 @@ msgid ", or "
msgstr ", ou "
msgid "- %{policy_name} (notifying after %{elapsed_time} minutes unless %{status})"
-msgstr ""
+msgstr "- %{policy_name} (notification après %{elapsed_time} minutes à moins d'être %{status})"
msgid "- Add or remove a user."
msgstr "- Ajouter ou supprimer un utilisateur."
@@ -1415,8 +1437,8 @@ msgstr "0 octets"
msgid "1 Code quality finding"
msgid_plural "%d Code quality findings"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 découverte de la qualité de code"
+msgstr[1] "%d découvertes de la qualité de code"
msgid "1 Day"
msgid_plural "%d Days"
@@ -1460,8 +1482,8 @@ msgstr[1] "%d clés de déploiement"
msgid "1 follower"
msgid_plural "%{count} followers"
-msgstr[0] "1 abonné"
-msgstr[1] "%{count} abonnés"
+msgstr[0] "1 suiveur"
+msgstr[1] "%{count} suiveurs"
msgid "1 group"
msgid_plural "%d groups"
@@ -1555,7 +1577,7 @@ msgid "2FA"
msgstr "Authentification à deux facteurs"
msgid "2FADevice|Registered On"
-msgstr ""
+msgstr "Enregistré sur"
msgid "3 hours"
msgstr "3 heures"
@@ -1564,10 +1586,10 @@ msgid "30 minutes"
msgstr "30 minutes"
msgid "30+ contributions"
-msgstr ""
+msgstr "30 contributions et plus"
msgid "403|Please contact your GitLab administrator to get permission."
-msgstr "Veuillez contacter votre administrateur·rice GitLab afin d’obtenir l’autorisation."
+msgstr "Veuillez contacter votre administrateur GitLab afin d’obtenir l’autorisation."
msgid "403|You don't have the permission to access this page."
msgstr "Vous n’avez pas l’autorisation d’accéder à cette page."
@@ -1627,7 +1649,7 @@ msgid "A Work Item can be a parent or a child, but not both."
msgstr "Un Élément de Travail peut être un parent ou un enfant, mais pas les deux à la fois."
msgid "A basic page and serverless function that uses AWS Lambda, AWS API Gateway, and GitLab Pages"
-msgstr ""
+msgstr "Une page de base et une fonction sans serveur qui utilisent AWS Lambda, AWS API Gateway et GitLab Pages"
msgid "A basic template for developing Linux programs using Kotlin Native"
msgstr "Un modèle de base pour développer des programmes Linux à l'aide de Kotlin Native"
@@ -1681,10 +1703,10 @@ msgid "A new Auto DevOps pipeline has been created, go to the Pipelines page for
msgstr "Un nouveau pipeline Auto DevOps a été créé, allez à la page Pipelines pour plus de détails"
msgid "A new Release %{tag} for %{name} was published. Visit the %{release_link_start}Releases page%{release_link_end} to read more about it."
-msgstr ""
+msgstr "Une nouvelle Version %{tag} de %{name} a été publiée. Consultez la %{release_link_start}page Versions%{release_link_end} pour en savoir plus à son propos."
msgid "A new Release %{tag} for %{name} was published. Visit the Releases page to read more about it:"
-msgstr ""
+msgstr "Une nouvelle Version %{tag} de %{name} a été publiée. Consultez la page des Versions pour en savoir plus à son propos :"
msgid "A new email address has been added to your GitLab account: %{email}"
msgstr "Une nouvelle adresse e-mail a été ajoutée à votre compte GitLab: %{email}"
@@ -1726,7 +1748,7 @@ msgid "A project boilerplate for Tencent Serverless Framework that uses Next.js
msgstr "Un modèle de projet pour Tencent Serverless Framework qui utilise SSR avec Next.js"
msgid "A project containing issues for each audit inquiry in the HIPAA Audit Protocol published by the U.S. Department of Health & Human Services"
-msgstr ""
+msgstr "Un projet contenant des tickets pour chacun des points à auditer dans le Protocole d'Audit HIPAA publié par le Département de la Santé et des Services sociaux américain"
msgid "A project’s repository name defines its URL (the one you use to access the project via a browser) and its place on the file disk where GitLab is installed. %{link_start}Learn more.%{link_end}"
msgstr "Le nom du dépôt d'un projet définit son URL (celle que vous utilisez pour accéder au projet via un navigateur) ainsi que son emplacement sur le disque de fichiers où GitLab est installé. %{link_start}En savoir plus.%{link_end}"
@@ -1750,7 +1772,7 @@ msgid "A title is required"
msgstr "Un titre est requis"
msgid "A user with write access to the source branch selected this option"
-msgstr "Une personne avec un accès en écriture à la branche source a sélectionné cette option"
+msgstr "Un utilisateur avec un accès en écriture à la branche source a sélectionné cette option"
msgid "ACTION REQUIRED: Something went wrong while obtaining the Let's Encrypt certificate for GitLab Pages domain '%{domain}'"
msgstr "ACTION REQUISE : Une erreur s'est produite lors de l'obtention du certificat Let's Encrypt pour le domaine GitLab Pages « %{domain} »"
@@ -1774,7 +1796,7 @@ msgid "API key"
msgstr "Clé de l'API"
msgid "API?"
-msgstr ""
+msgstr "API ?"
msgid "APIFuzzing|$VARIABLE_WITH_PASSWORD"
msgstr "$VARIABLE_WITH_PASSWORD"
@@ -1798,7 +1820,7 @@ msgid "APIFuzzing|Configure HTTP basic authentication values. Other authenticati
msgstr "Configurez les valeurs pour l'authentification HTTP de base. D'autres méthodes d'authentification sont prises en charge. %{linkStart}En savoir plus%{linkEnd}."
msgid "APIFuzzing|Customize your project's API fuzzing configuration options and copy the code snippet to your .gitlab-ci.yml file to apply any changes. Note that this tool does not reflect or update your .gitlab-ci.yml file automatically. For details of more advanced configuration options, see the %{docsLinkStart}GitLab API Fuzzing documentation%{docsLinkEnd}."
-msgstr ""
+msgstr "Personnalisez les options de configuration des tests d’API par injection de données aléatoires de vos projets et copiez l'extrait de code dans votre fichier .gitlab-ci.yml pour appliquer les modifications. Notez que cet outil ne reflète pas, ni ne met à jour, votre fichier .gitlab-ci.yml automatiquement. Pour plus de détails sur les options de configuration avancées, consultez la %{docsLinkStart}documentation de GitLab sur les tests d'API par injection de données aléatoires%{docsLinkEnd}."
msgid "APIFuzzing|Enable authentication"
msgstr "Activer l'authentification"
@@ -1846,13 +1868,13 @@ msgid "APIFuzzing|There are three ways to perform scans."
msgstr "Il existe trois façons d'effectuer des analyses."
msgid "APIFuzzing|Tip: Insert the following variables anywhere below stages and include"
-msgstr ""
+msgstr "Astuce : Insérez les variables suivantes n'importe où en-dessous de stages et include"
msgid "APIFuzzing|Tip: Insert this part below all include"
-msgstr ""
+msgstr "Astuce : Insérez cette partie en-dessous de tous les include"
msgid "APIFuzzing|Tip: Insert this part below all stages"
-msgstr ""
+msgstr "Astuce : Insérez cette partie en-dessous de tous les stages"
msgid "APIFuzzing|To prevent a security leak, authentication info must be added as a %{ciVariablesLinkStart}CI variable%{ciVariablesLinkEnd}. A user with maintainer access rights can manage CI variables in the %{ciSettingsLinkStart}Settings%{ciSettingsLinkEnd} area. We detected that you are not a maintainer. Commit your changes and assign them to a maintainer to update the credentials before merging."
msgstr "Pour éviter une faille de sécurité, les infos d'authentification doivent être ajoutées sous la forme d'une %{ciVariablesLinkStart}variable CI%{ciVariablesLinkEnd}. Un utilisateur disposant de droits d'accès mainteneur peut gérer les variables CI dans la section %{ciSettingsLinkStart}Paramètres%{ciSettingsLinkEnd}. Nous avons identifié que nous n'êtes pas un mainteneur. Soumettez vos modifications puis assignez-les à un mainteneur pour mettre à jour les identifiants avant la fusion."
@@ -2023,7 +2045,7 @@ msgid "AccessTokens|The last time a token was used"
msgstr "La dernière fois qu'un jeton a été utilisé"
msgid "AccessTokens|They are the only accepted password when you have Two-Factor Authentication (2FA) enabled."
-msgstr ""
+msgstr "Ce sont les seuls mots de passe acceptés lorsque l'Authentification à Deux Facteurs (A2F) est activée."
msgid "AccessTokens|You can also use personal access tokens to authenticate against Git over HTTP."
msgstr "Vous pouvez également utiliser des jetons d'accès personnels pour vous authentifier auprès de Git via HTTP."
@@ -2089,7 +2111,7 @@ msgid "AccountValidation|Verification is required to discourage and reduce the a
msgstr "La vérification est nécessaire pour décourager et réduire les abus sur l'infrastructure de GitLab. Si vous l'effectuez avec une carte de débit ou de crédit, %{strong_start}GitLab ne fera aucun prélèvement, celle-ci ne sera utilisée que pour la validation.%{strong_end} %{learn_more_link}"
msgid "Acknowledge"
-msgstr ""
+msgstr "Acquitter"
msgid "Action"
msgstr "Action"
@@ -2131,7 +2153,7 @@ msgid "Add \"%{value}\""
msgstr "Ajouter « %{value} »"
msgid "Add %{linkStart}assets%{linkEnd} to your Release. GitLab automatically includes read-only assets, like source code and release evidence."
-msgstr ""
+msgstr "Ajoutez des %{linkStart}ressources%{linkEnd} à votre Version. GitLab inclut automatiquement des ressources en lecture seule, comme le code source et les preuves de publication."
msgid "Add CHANGELOG"
msgstr "Ajouter un CHANGELOG"
@@ -2182,10 +2204,10 @@ msgid "Add a collapsible section"
msgstr "Ajouter une section rétractable"
msgid "Add a comment to this line"
-msgstr ""
+msgstr "Ajouter un commentaire à cette ligne"
msgid "Add a comment to this line or drag for multiple lines"
-msgstr ""
+msgstr "Ajoutez un commentaire à cette ligne ou faites glisser pour plusieurs lignes"
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr "Ajouter une note interne confidentielle à ce %{noteableDisplayName}."
@@ -2194,7 +2216,7 @@ msgid "Add a custom message with details about the instance's shared runners. Th
msgstr "Ajoutez un message personnalisé avec les détails concernant les exécuteurs partagés de l'instance. Il sera visible quand vous afficherez les exécuteurs des projets et des groupes. Markdown est pris en charge."
msgid "Add a general comment to this %{noteableDisplayName}."
-msgstr ""
+msgstr "Ajoutez un commentaire générique à ce %{noteableDisplayName}."
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr "Ajoutez une page d’accueil à votre wiki contenant des informations sur votre projet. GitLab l’affichera ici à la place de ce message."
@@ -2224,7 +2246,7 @@ msgid "Add a title..."
msgstr "Ajouter un titre..."
msgid "Add a to do"
-msgstr "Ajouter une tâche"
+msgstr "Ajouter un pense-bête"
msgid "Add an SSH key"
msgstr "Ajouter une clé SSH"
@@ -2290,7 +2312,7 @@ msgid "Add email participant(s)"
msgstr "Ajouter un ou plusieurs participants par courriel"
msgid "Add environment"
-msgstr ""
+msgstr "Ajoutez un environnement"
msgid "Add existing confidential %{issuableType}"
msgstr "Ajouter un(e) %{issuableType} confidentiel(le) existant(e)"
@@ -2335,7 +2357,7 @@ msgid "Add or remove previously merged commits"
msgstr ""
msgid "Add or subtract spent time"
-msgstr ""
+msgstr "Ajouter ou soustraire du temps passé"
msgid "Add people"
msgstr "Ajouter des personnes"
@@ -2356,7 +2378,7 @@ msgid "Add request manually"
msgstr "Ajouter une requête manuellement"
msgid "Add suggestion to batch"
-msgstr ""
+msgstr "Ajouter une suggestion au lot"
msgid "Add system hook"
msgstr "Ajouter un crochet système"
@@ -2364,6 +2386,9 @@ msgstr "Ajouter un crochet système"
msgid "Add text to the sign-in page. Markdown enabled."
msgstr "Ajouter du texte à la page de connexion. Markdown activé."
+msgid "Add time entry"
+msgstr "Ajouter une entrée de temps"
+
msgid "Add to board"
msgstr "Ajouter au tableau"
@@ -2392,7 +2417,7 @@ msgid "Add variable"
msgstr "Ajouter une variable"
msgid "Add vulnerability finding"
-msgstr ""
+msgstr "Ajouter une découverte de vulnérabilité"
msgid "Add webhook"
msgstr "Ajouter un crochet web"
@@ -2446,13 +2471,16 @@ msgid "Added for this merge request"
msgstr ""
msgid "Added in this version"
-msgstr ""
+msgstr "Ajoutée dans cette version"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "L’ajout de nouvelles applications est désactivé dans votre instance GitLab. Veuillez contacter votre administrateur GitLab pour en obtenir la permission."
+msgid "Additional diagram formats"
+msgstr "Formats de diagrammes supplémentaires"
+
msgid "Additional minutes"
-msgstr ""
+msgstr "Minutes supplémentaires"
msgid "Additional minutes:"
msgstr "Minutes supplémentaires :"
@@ -2494,7 +2522,7 @@ msgid "Adds a timeline event to incident."
msgstr "Ajoute un événement de la chronologie à l'incident."
msgid "Adds a to do."
-msgstr "Ajoute une tâche."
+msgstr "Ajoute un pense-bête."
msgid "Adds an issue to an epic."
msgstr "Ajoute un ticket à une épopée."
@@ -2646,6 +2674,9 @@ msgstr "L’arrêt des tâches a échoué"
msgid "AdminArea|Total users"
msgstr "Nombre total d'utilisateurs"
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr "Utilisateurs"
@@ -2716,7 +2747,7 @@ msgid "AdminSettings|A Let's Encrypt account will be configured for this GitLab
msgstr "Un compte Let's Encrypt sera configuré pour cette instance GitLab à l'aide de cette adresse de messagerie. Vous recevrez des courriels pour vous avertir de l'expiration des certificats. %{link_start}En savoir plus.%{link_end}"
msgid "AdminSettings|Affects all new and existing groups."
-msgstr ""
+msgstr "Affecte tous les nouveaux groupes et groupes existants."
msgid "AdminSettings|All new projects can use the instance's shared runners by default."
msgstr "Tous les nouveaux projets peuvent utiliser les exécuteurs partagés de l'instance par défaut."
@@ -2737,7 +2768,7 @@ msgid "AdminSettings|Configure limits on the number of repositories users can do
msgstr "Configurer les limites du nombre de dépôts que les utilisateurs peuvent télécharger dans un temps donné."
msgid "AdminSettings|Configure product analytics to track events within your project applications."
-msgstr ""
+msgstr "Configurez l'analytique des produits pour suivre les événements au sein des applications de votre projet."
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr "Définir quand les projets inactifs doivent être automatiquement supprimés. %{linkStart}Qu'est-ce-qu'un projet inactif ?%{linkEnd}"
@@ -2797,7 +2828,7 @@ msgid "AdminSettings|Enable pipeline suggestion banner"
msgstr "Activer la bannière de suggestion de pipeline"
msgid "AdminSettings|Enable product analytics"
-msgstr ""
+msgstr "Activer l'analytique des produits"
msgid "AdminSettings|Enable shared runners for new projects"
msgstr "Activer les exécuteurs partagés pour les nouveaux projets"
@@ -2809,7 +2840,7 @@ msgid "AdminSettings|Enable smartcn custom analyzer: Search"
msgstr "Activer l'analyseur personnalisé smartcn : Recherche"
msgid "AdminSettings|Enabled"
-msgstr ""
+msgstr "Activé"
msgid "AdminSettings|Enforce invitation flow for groups and projects"
msgstr "Imposer le flux d'invitation pour les groupes et les projets"
@@ -2836,7 +2867,7 @@ msgid "AdminSettings|If there isn't any existing index, GitLab creates one."
msgstr "Si aucun index n'existe, GitLab en crée un."
msgid "AdminSettings|Import sources"
-msgstr ""
+msgstr "Sources d'importation"
msgid "AdminSettings|Inactive project deletion"
msgstr "Suppression de projet inactif"
@@ -2872,7 +2903,7 @@ msgid "AdminSettings|Maximum duration of a session for Git operations when 2FA i
msgstr "Durée maximale d'une session pour les opérations Git lorsque l'A2F est activée."
msgid "AdminSettings|Maximum number of DAG dependencies that a job can have"
-msgstr ""
+msgstr "Nombre maximum de dépendances qu'une tâche peut avoir dans les DAG"
msgid "AdminSettings|Maximum number of active pipelines per project"
msgstr "Nombre maximum de pipelines actifs par projet"
@@ -2887,7 +2918,7 @@ msgid "AdminSettings|Maximum number of pipeline schedules"
msgstr "Nombre maximum de planifications de pipelines"
msgid "AdminSettings|Maximum number of pipeline subscriptions to and from a project"
-msgstr ""
+msgstr "Nombre maximum d'abonnements de pipelines à un projet ou depuis un projet"
msgid "AdminSettings|Maximum number of runners registered per group"
msgstr "Nombre maximum d'exécuteurs enregistrés par groupe"
@@ -2902,7 +2933,7 @@ msgid "AdminSettings|New CI/CD variables in projects and groups default to prote
msgstr "Nouvelles variables CI/CD des projets et des groupes protégées par défaut."
msgid "AdminSettings|No required pipeline"
-msgstr ""
+msgstr "Aucun pipeline requis"
msgid "AdminSettings|Only enable search after installing the plugin, enabling indexing, and recreating the index."
msgstr "N'activer la recherche qu'après l'installation du plugin, l'activation de l'indexation et la régénération de l'index."
@@ -2914,7 +2945,7 @@ msgid "AdminSettings|Preview payload"
msgstr "Aperçu de la charge utile"
msgid "AdminSettings|Project export"
-msgstr ""
+msgstr "Exportation de projet"
msgid "AdminSettings|Project runners expiration"
msgstr "Expiration des exécuteurs de projet"
@@ -2929,7 +2960,7 @@ msgid "AdminSettings|Require users to prove ownership of custom domains"
msgstr "Exiger des utilisateurs qu'ils prouvent qu'ils sont propriétaires de domaines personnalisés"
msgid "AdminSettings|Required pipeline configuration"
-msgstr ""
+msgstr "Configuration du pipeline requise"
msgid "AdminSettings|Requires %{linkStart}email notifications%{linkEnd}"
msgstr "Nécessite les %{linkStart}notifications par courriel%{linkEnd}"
@@ -2938,7 +2969,7 @@ msgid "AdminSettings|Restrict group access by IP address. %{link_start}Learn mor
msgstr "Restreindre l'accès au groupe selon l'adresse IP. %{link_start}En savoir plus%{link_end}."
msgid "AdminSettings|Save %{name} limits"
-msgstr ""
+msgstr "Enregistrer les limites « %{name} »"
msgid "AdminSettings|Search with Elasticsearch enabled"
msgstr "Recherche avec Elasticsearch activée"
@@ -2946,8 +2977,8 @@ msgstr "Recherche avec Elasticsearch activée"
msgid "AdminSettings|Select a CI/CD template"
msgstr "Sélectionnez un modèle de CI/CD"
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
-msgstr "Sélectionnez un groupe à utiliser comme source pour les modèles de projet de niveau instance."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
+msgstr "Sélectionnez un groupe à utiliser comme source de modèles personnalisés pour les nouveaux projets. %{link_start}En savoir plus%{link_end}."
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
msgstr "Sélectionnez pour désactiver l'accès public aux sites Pages, ce qui oblige les utilisateurs à se connecter pour accéder aux sites Pages de votre instance. %{link_start}En savoir plus%{link_end}"
@@ -2995,13 +3026,13 @@ msgid "AdminSettings|Size and domain settings for Pages static sites."
msgstr "Paramètres de taille et de domaine pour les sites statiques Pages."
msgid "AdminSettings|The ID of the project in Jitsu. The project contains all analytics instances."
-msgstr ""
+msgstr "L'ID du projet dans Jitsu. Le projet contient toutes les instances analytiques."
msgid "AdminSettings|The URL of your Cube instance."
msgstr "L'URL de votre instance Cube."
msgid "AdminSettings|The default domain to use for Auto Review Apps and Auto Deploy stages in all projects."
-msgstr ""
+msgstr "Le domaine par défaut à utiliser pour l'Automatisation des Applications de Revue et les Auto Déploiements des étapes dans tous les projets."
msgid "AdminSettings|The host of your Jitsu instance."
msgstr "L'hôte de votre instance Jitsu."
@@ -3009,9 +3040,6 @@ msgstr "L'hôte de votre instance Jitsu."
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr "Les derniers artéfacts de toutes les tâches des pipelines qui ont réussi le plus récemment dans chaque projet sont enregistrés et n'expirent pas."
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr "Les projets de ce groupe peuvent être sélectionnés comme modèles pour les nouveaux projets créés sur l'instance. %{link_start}En savoir plus.%{link_end} "
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr "Le modèle pour la configuration requise du pipeline peut être un de ceux fournis par GitLab, ou un modèle personnalisé ajouté à un dépôt de modèles d'instance. %{link_start}Comment puis-je créer un dépôt de modèles d'instance ?%{link_end}"
@@ -3028,16 +3056,16 @@ msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr "Nombre total de tâches dans les pipelines actuellement actifs"
msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
-msgstr ""
+msgstr "Utiliser AWS OpenSearch Service avec les identifiants IAM"
msgid "AdminSettings|Used to connect Jitsu to the Clickhouse instance."
-msgstr ""
+msgstr "Utilisé pour connecter Jitsu à l'instance Clickhouse."
msgid "AdminSettings|Used to generate short-lived API access tokens."
-msgstr ""
+msgstr "Utilisé pour générer des jetons d'accès d'API de courte durée."
msgid "AdminSettings|Used to retrieve dashboard data from the Cube instance."
-msgstr ""
+msgstr "Utilisé pour récupérer les données du tableau de bord à partir de l'instance de Cube."
msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr "Les utilisateurs et les groupes doivent accepter l’invitation avant de pouvoir être ajoutés à un groupe ou à un projet."
@@ -3058,7 +3086,7 @@ msgid "AdminStatistics|Active Users"
msgstr "Utilisateurs actifs"
msgid "AdminStatistics|Forks"
-msgstr ""
+msgstr "Divergences"
msgid "AdminStatistics|Issues"
msgstr "Tickets"
@@ -3070,7 +3098,7 @@ msgid "AdminStatistics|Milestones"
msgstr "Jalons"
msgid "AdminStatistics|Notes"
-msgstr ""
+msgstr "Notes"
msgid "AdminStatistics|SSH Keys"
msgstr "Clés SSH"
@@ -3112,13 +3140,13 @@ msgid "AdminUsers|Access"
msgstr "Accès"
msgid "AdminUsers|Access Git repositories"
-msgstr ""
+msgstr "Accéder aux dépôts Git"
msgid "AdminUsers|Access level"
msgstr "Niveau d'accès"
msgid "AdminUsers|Access the API"
-msgstr ""
+msgstr "Accéder à l'API"
msgid "AdminUsers|Activate"
msgstr "Activer"
@@ -3142,7 +3170,7 @@ msgid "AdminUsers|Admins"
msgstr "Admins"
msgid "AdminUsers|An error occurred while fetching this user's contributions, and the request cannot return the number of issues, merge requests, groups, and projects linked to this user. If you proceed with deleting the user, all their contributions will still be deleted."
-msgstr ""
+msgstr "Une erreur s'est produite lors de la récupération des contributions de cet utilisateur et la requête ne peut pas renvoyer le nombre de tickets, de demandes de fusion, de groupes et de projets associés à celui-ci. Si vous continuez avec la suppression de cet utilisateur, toutes ses contributions seront quand même supprimées."
msgid "AdminUsers|Approve"
msgstr "Approuver"
@@ -3202,7 +3230,7 @@ msgid "AdminUsers|Cannot sign in or access instance information"
msgstr "Impossible de se connecter ou d'accéder aux informations de l'instance"
msgid "AdminUsers|Cannot unblock LDAP blocked users"
-msgstr ""
+msgstr "Impossible de débloquer des utilisateurs bloqués au niveau LDAP"
msgid "AdminUsers|Cohorts"
msgstr "Cohortes"
@@ -3256,13 +3284,13 @@ msgid "AdminUsers|Here are some helpful links to help you manage your instance:"
msgstr "Voici quelques liens utiles pour vous aider à gérer votre instance :"
msgid "AdminUsers|If you have any questions about this process please consult our %{doc_link} or %{support_link}."
-msgstr ""
+msgstr "Si vous avez des questions à propos de ce processus, veuillez consulter notre %{doc_link} ou notre %{support_link}."
msgid "AdminUsers|Important information about usage on your GitLab instance"
msgstr "Informations importantes sur l’utilisation de votre instance GitLab"
msgid "AdminUsers|Is using seat"
-msgstr ""
+msgstr "Utilise un siège"
msgid "AdminUsers|Issues authored by this user are hidden from other users."
msgstr "Les tickets rédigés par cet utilisateur sont cachés aux autres utilisateurs."
@@ -3319,7 +3347,7 @@ msgid "AdminUsers|Regular"
msgstr "Normal"
msgid "AdminUsers|Regular users have access to their groups and projects."
-msgstr ""
+msgstr "Les utilisateurs normaux ont accès à leurs groupes et projets."
msgid "AdminUsers|Reject"
msgstr "Rejeter"
@@ -3334,7 +3362,7 @@ msgid "AdminUsers|Reset link will be generated and sent to the user. User will b
msgstr "Le lien de réinitialisation sera généré et envoyé à l'utilisateur. Celui-ci sera forcé de définir un mot de passe lors de sa première connexion."
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
-msgstr ""
+msgstr "Restaurez l'accès utilisateur au compte, y compris Web, Git et API."
msgid "AdminUsers|Search by name, email, or username"
msgstr "Rechercher par nom, courriel ou nom d'utilisateur"
@@ -3343,7 +3371,7 @@ msgid "AdminUsers|Search users"
msgstr "Rechercher des utilisateurs"
msgid "AdminUsers|Send email to users"
-msgstr ""
+msgstr "Envoyer un courriel aux utilisateurs"
msgid "AdminUsers|Skype"
msgstr "Skype"
@@ -3433,7 +3461,7 @@ msgid "AdminUsers|When banned:"
msgstr "Quand il est banni :"
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
-msgstr ""
+msgstr "Lorsque l'utilisateur se reconnecte, son compte est réactivé en tant que compte pleinement actif"
msgid "AdminUsers|Will be deleted"
msgstr ""
@@ -3445,7 +3473,7 @@ msgid "AdminUsers|You are about to permanently delete the user %{username}. Issu
msgstr "Vous êtes sur le point de supprimer définitivement l’utilisateur %{username}. Les tickets, les demandes de fusion et les groupes qui lui sont liés seront transférés à un « utilisateur fantôme » global au système. Pour éviter la perte de données, pensez à utiliser à la place la fonctionnalité %{strongStart}bloquer l'utilisateur%{strongEnd}. L'action %{strongStart}Supprimer l'utilisateur%{strongEnd} terminée, elle ne pourra pas être annulée ni récupérée."
msgid "AdminUsers|You are about to permanently delete the user %{username}. This will delete all issues, merge requests, groups, and projects linked to them. To avoid data loss, consider using the %{strongStart}Block user%{strongEnd} feature instead. After you %{strongStart}Delete user%{strongEnd}, you cannot undo this action or recover the data."
-msgstr ""
+msgstr "Vous êtes sur le point de supprimer définitivement l’utilisateur %{username}. Cela supprimera tous les tickets, demandes de fusion, groupes et projets qui lui sont liés. Pour éviter de perdre des données, pensez à utiliser à la place la fonctionnalité %{strongStart}Bloquer l'utilisateur%{strongEnd} . L'action %{strongStart}Supprimer l'utilisateur%{strongEnd} terminée, il ne vous sera pas possible de l'annuler ni de récupérer les données."
msgid "AdminUsers|You can always block their account again if needed."
msgstr "Vous pourrez toujours bloquer son compte de nouveau si besoin."
@@ -3475,7 +3503,7 @@ msgid "AdminUsers|Your GitLab instance has reached the maximum allowed %{user_do
msgstr "Votre instance GitLab a atteint son %{user_doc_link} maximum autorisé défini par un administrateur de l'instance."
msgid "AdminUsers|approve them"
-msgstr ""
+msgstr "les approuver"
msgid "AdminUsers|contact our support team"
msgstr "Contactez notre équipe d'assistance"
@@ -3505,7 +3533,7 @@ msgid "Admin|Learn more about quarterly reconciliation"
msgstr "En savoir plus sur le rapprochement trimestriel"
msgid "Admin|Note"
-msgstr ""
+msgstr "Notes"
msgid "Admin|Quarterly reconciliation will occur on %{qrtlyDate}"
msgstr "Le rapprochement trimestriel aura lieu le %{qrtlyDate}"
@@ -3523,7 +3551,7 @@ msgid "Admin|Your instance has reached its user cap"
msgstr "Votre instance a atteint sa limite d'utilisateurs"
msgid "Advanced"
-msgstr ""
+msgstr "Paramètres avancés"
msgid "Advanced Search"
msgstr "Recherche avancée"
@@ -3532,13 +3560,13 @@ msgid "Advanced Settings"
msgstr "Paramètres avancés"
msgid "Advanced export options"
-msgstr ""
+msgstr "Options d'exportation avancées"
msgid "AdvancedSearch|Elasticsearch version not compatible"
msgstr "Version d'Elasticsearch non compatible"
msgid "AdvancedSearch|Introduced in GitLab 13.1, before using %{reindexing_link_start}zero-downtime reindexing%{link_end} and %{migrations_link_start}Advanced Search migrations%{link_end}, you need to %{recreate_link_start}recreate your index%{link_end}."
-msgstr ""
+msgstr "Introduit dans GitLab 13.1, avant d'utiliser la %{reindexing_link_start}réindexation sans temps d'arrêt%{link_end} et les %{migrations_link_start}migrations de Recherche Avancée%{link_end}, vous devez %{recreate_link_start}recréer votre index%{link_end}."
msgid "AdvancedSearch|Pause indexing and upgrade Elasticsearch to a supported version."
msgstr "Suspendre l'indexation et mettre à niveau Elasticsearch vers une version prise en charge."
@@ -3571,7 +3599,7 @@ msgid "After the export is complete, download the data file from a notification
msgstr "Une fois l'exportation terminée, téléchargez le fichier de données depuis le courriel de notification ou depuis cette page. Vous pourrez ensuite l'importer sur la page %{strong_text_start}Créer un nouveau groupe%{strong_text_end} d'une autre instance GitLab."
msgid "After you've reviewed these contribution guidelines, you'll be all set to"
-msgstr ""
+msgstr "Après avoir pris connaissance de ces consignes de contribution, vous serez prêt à"
msgid "Akismet"
msgstr "Akismet"
@@ -3586,7 +3614,7 @@ msgid "Alert"
msgstr "Alerte"
msgid "AlertManagement|Acknowledged"
-msgstr ""
+msgstr "Acquitté"
msgid "AlertManagement|Activity feed"
msgstr "Flux d'activité"
@@ -3598,10 +3626,10 @@ msgid "AlertManagement|Alert assignee(s): %{assignees}"
msgstr "Destinataire(s) des alertes : %{assignees}"
msgid "AlertManagement|Alert detail"
-msgstr ""
+msgstr "Détail de l'alerte"
msgid "AlertManagement|Alert details"
-msgstr ""
+msgstr "Détails de l'alerte"
msgid "AlertManagement|Alert status: %{status}"
msgstr "État de l'alerte : %{status}"
@@ -3613,10 +3641,10 @@ msgid "AlertManagement|All alerts"
msgstr "Toutes les alertes"
msgid "AlertManagement|Assign status"
-msgstr ""
+msgstr "Assigner un état"
msgid "AlertManagement|Assignees"
-msgstr ""
+msgstr "Assigné(e)s"
msgid "AlertManagement|Authorize external service"
msgstr "Autoriser un service externe"
@@ -3625,7 +3653,7 @@ msgid "AlertManagement|Create incident"
msgstr "Créer un incident"
msgid "AlertManagement|Display alerts from all your monitoring tools directly within GitLab. Streamline the investigation of your alerts and the escalation of alerts to incidents."
-msgstr ""
+msgstr "Affichez les alertes de tous vos outils de supervision directement dans GitLab. Rationalisez l'examen de vos alertes et leur escalade en incidents."
msgid "AlertManagement|Edit"
msgstr "Modifier"
@@ -3661,19 +3689,19 @@ msgid "AlertManagement|No alerts to display."
msgstr "Aucune alerte à afficher."
msgid "AlertManagement|None"
-msgstr ""
+msgstr "Aucun"
msgid "AlertManagement|Open"
-msgstr ""
+msgstr "Ouvrir"
msgid "AlertManagement|Please try again."
msgstr "Veuillez réessayer."
msgid "AlertManagement|Reported %{when}"
-msgstr ""
+msgstr "Signalée %{when}"
msgid "AlertManagement|Reported %{when} by %{tool}"
-msgstr ""
+msgstr "Signalée %{when} par %{tool}"
msgid "AlertManagement|Resolved"
msgstr "Résolue"
@@ -3712,7 +3740,7 @@ msgid "AlertManagement|There was an error while updating the status of the alert
msgstr "Une erreur s’est produite lors de la mise à jour de l'état de l’alerte."
msgid "AlertManagement|There was an error while updating the to-do item of the alert."
-msgstr "Une erreur s’est produite lors de la mise à jour de l'élément « À faire » de l'alerte."
+msgstr "Une erreur s’est produite lors de la mise à jour du pense-bête de l'alerte."
msgid "AlertManagement|This assignee cannot be assigned to this alert."
msgstr "Ce destinataire ne peut pas être assigné à cette alerte."
@@ -3871,7 +3899,7 @@ msgid "AlertSettings|View credentials"
msgstr "Afficher les identifiants"
msgid "AlertSettings|Webhook URL"
-msgstr "URL de Webhook"
+msgstr "URL du crochet web"
msgid "AlertSettings|You can map default GitLab alert fields to your payload keys in the dropdowns below."
msgstr "Vous pouvez associer les champs par défaut des alertes GitLab aux clés de votre charge utile grâce aux listes déroulantes ci-dessous."
@@ -3931,10 +3959,10 @@ msgid "All"
msgstr "Tous"
msgid "All %{replicableType} are being scheduled for %{action}"
-msgstr ""
+msgstr "La totalité des %{replicableType} est planifiée pour %{action}"
msgid "All (default)"
-msgstr ""
+msgstr "Tous (par défaut)"
msgid "All GitLab"
msgstr "Tout GitLab"
@@ -4045,7 +4073,7 @@ msgid "Allow project maintainers to configure repository mirroring"
msgstr "Autoriser les mainteneurs de projet à configurer la mise en miroir de dépôts"
msgid "Allow projects and subgroups to override the group setting"
-msgstr ""
+msgstr "Autoriser les projets et les sous-groupes à outrepasser le paramètre du groupe"
msgid "Allow projects within this group to use Git LFS"
msgstr "Autoriser les projets de ce groupe à utiliser le stockage Git LFS"
@@ -4054,13 +4082,13 @@ msgid "Allow public access to pipelines and job details, including output logs a
msgstr "Autoriser l’accès public aux pipelines et aux détails des tâches, dont les journaux de sortie et les artéfacts."
msgid "Allow this key to push to this repository"
-msgstr ""
+msgstr "Autorisez cette clé à pousser vers ce dépôt"
msgid "Allow use of licensed EE features"
-msgstr ""
+msgstr "Autoriser l'utilisation des fonctionnalités EE sous licence"
msgid "Allow users to dismiss the broadcast message"
-msgstr ""
+msgstr "Autoriser les utilisateurs à rejeter le message de diffusion"
msgid "Allow users to register any application to use GitLab as an OAuth provider"
msgstr "Autoriser les utilisateurs à enregistrer n'importe quelle application pour utiliser GitLab en tant que fournisseur OAuth"
@@ -4078,7 +4106,7 @@ msgid "Allowed to delete projects"
msgstr "Autorisé à supprimer des projets"
msgid "Allowed to fail"
-msgstr ""
+msgstr "Autorisé à échouer"
msgid "Allows projects or subgroups in this group to override the global setting."
msgstr "Autorise les projets ou les sous-groupes de ce groupe à outrepasser le paramètre global."
@@ -4108,13 +4136,13 @@ msgid "Also called \"Relying party service URL\" or \"Reply URL\""
msgstr "Aussi appelée « adresse URL du service du tiers de confiance » ou « URL de réponse »"
msgid "Also remove direct user membership from subgroups and projects"
-msgstr ""
+msgstr "Supprimer également l'appartenance des utilisateurs directs aux projets et sous-groupes"
msgid "Also unassign this user from related issues and merge requests"
-msgstr ""
+msgstr "Désassigner également cet utilisateur des demandes de fusion et des tickets associés"
msgid "Alternate support URL for Help page and Help dropdown."
-msgstr ""
+msgstr "URL alternative de support pour la page d'aide et la liste déroulante d'aide."
msgid "Alternatively, you can convert your account to a managed account by the %{group_name} group."
msgstr "Vous pouvez également convertir votre compte en un compte géré par le groupe %{group_name}."
@@ -4129,10 +4157,10 @@ msgid "Amazon Web Services Logo"
msgstr "Logo Amazon Web Services"
msgid "An %{link_start}alert%{link_end} with the same fingerprint is already open. To change the status of this alert, resolve the linked alert."
-msgstr ""
+msgstr "Une %{link_start}alerte%{link_end} avec la même empreinte est déjà ouverte. Pour modifier son état, résolvez l'alerte liée."
msgid "An Administrator has set the maximum expiration date to %{maxDate}. %{helpLinkStart}Learn more%{helpLinkEnd}."
-msgstr ""
+msgstr "Un administrateur a défini la date d'expiration maximale au %{maxDate}. %{helpLinkStart}En savoir plus%{helpLinkEnd}."
msgid "An Enterprise User GitLab account has been created for you by your organization:"
msgstr ""
@@ -4141,7 +4169,7 @@ msgid "An administrator changed the password for your GitLab account on %{link_t
msgstr "Un administrateur a modifié le mot de passe de votre compte GitLab sur %{link_to}."
msgid "An alert has been resolved in %{project_path}."
-msgstr ""
+msgstr "Une alerte a été résolue dans %{project_path}."
msgid "An alert has been triggered in %{project_path}."
msgstr "Une alerte a été déclenchée dans %{project_path}."
@@ -4228,7 +4256,7 @@ msgid "An error occurred while deleting the pipeline."
msgstr "Une erreur s’est produite lors de la suppression du pipeline."
msgid "An error occurred while detecting host keys"
-msgstr "Une erreur est survenue lors de la détection des clefs de l’hôte"
+msgstr "Une erreur est survenue lors de la détection des clés de l’hôte"
msgid "An error occurred while disabling Service Desk."
msgstr "Une erreur s'est produite lors de la désactivation du Service d'Assistance."
@@ -4240,7 +4268,7 @@ msgid "An error occurred while dismissing the feature highlight. Refresh the pag
msgstr "Une erreur s’est produite lors du rejet de la mise en avant de la fonctionnalité. Actualisez la page et essayez de la rejeter à nouveau."
msgid "An error occurred while drawing job relationship links."
-msgstr ""
+msgstr "Une erreur s'est produite lors du tracé des liens de relations entre tâches."
msgid "An error occurred while enabling Service Desk."
msgstr "Une erreur s'est produite lors de l'activation du Service d'Assistance."
@@ -4258,7 +4286,7 @@ msgid "An error occurred while fetching codequality mr diff reports."
msgstr "Une erreur s'est produite lors de la récupération des rapports sur la qualité de code des diffs de MR."
msgid "An error occurred while fetching commit data."
-msgstr ""
+msgstr "Une erreur s'est produite lors de la récupération des données du commit."
msgid "An error occurred while fetching commits. Retry the search."
msgstr "Une erreur s'est produite lors de la récupération des commits. Réessayez la recherche."
@@ -4291,7 +4319,7 @@ msgid "An error occurred while fetching pending comments"
msgstr "Une erreur est survenue lors de la récupération des commentaires en attente"
msgid "An error occurred while fetching projects autocomplete."
-msgstr ""
+msgstr "Une erreur s'est produite lors de la récupération de l'auto-complétion des projets."
msgid "An error occurred while fetching reference"
msgstr "Une erreur s'est produite lors de la récupération de la référence"
@@ -4333,7 +4361,7 @@ msgid "An error occurred while fetching this tab."
msgstr "Une erreur s'est produite lors de la récupération de cet onglet."
msgid "An error occurred while getting files for - %{branchId}"
-msgstr ""
+msgstr "Une erreur s'est produite lors de l'obtention des fichiers pour - %{branchId}"
msgid "An error occurred while getting issue counts"
msgstr "Une erreur est survenue lors de l'obtention du nombre de tickets"
@@ -4360,7 +4388,7 @@ msgid "An error occurred while loading commit signatures"
msgstr "Une erreur s’est produite lors du chargement des signatures du commit"
msgid "An error occurred while loading designs. Please try again."
-msgstr "Une erreur c'est produite pendant le chargement des designs. Veuillez réessayer."
+msgstr "Une erreur s'est produite lors du chargement des esquisses. Veuillez réessayer."
msgid "An error occurred while loading diff"
msgstr "Une erreur s’est produite lors du chargement du diff"
@@ -4538,6 +4566,9 @@ msgstr "Une erreur s'est produite. Veuillez vous reconnecter."
msgid "An error occurred. Please try again."
msgstr "Une erreur est survenue. Merci de réessayer."
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr "Une erreur s'est produite. Impossible de rouvrir cette demande de fusion."
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr "Un exemple de projet pour gérer des grappes de serveurs Kubernetes intégrées à GitLab"
@@ -4548,7 +4579,7 @@ msgid "An example showing how to use Jsonnet with GitLab dynamic child pipelines
msgstr "Un exemple montrant comment utiliser Jsonnet avec les pipelines enfants dynamiques de GitLab"
msgid "An incident has been resolved in %{project_path}."
-msgstr ""
+msgstr "Un incident a été résolu dans %{project_path}."
msgid "An incident has been triggered in %{project_path}."
msgstr "Un incident a été déclenché dans %{project_path}."
@@ -4608,7 +4639,7 @@ msgid "Analyzing file…"
msgstr "Analyse du fichier…"
msgid "Ancestors"
-msgstr ""
+msgstr "Ancêtres"
msgid "And this registration token:"
msgstr "Et ce jeton d’inscription :"
@@ -4641,10 +4672,10 @@ msgid "Any Milestone"
msgstr "N'importe quel jalon"
msgid "Any encrypted tokens"
-msgstr ""
+msgstr "Tout jeton chiffré"
msgid "Any label"
-msgstr ""
+msgstr "Toute étiquette"
msgid "Any member with at least Developer permissions on the project."
msgstr "Tout membre ayant au moins les permissions Développeur sur le projet."
@@ -4653,10 +4684,7 @@ msgid "Any milestone"
msgstr "N'importe quel jalon"
msgid "Any namespace"
-msgstr ""
-
-msgid "Anyone can register for an account."
-msgstr ""
+msgstr "Tout espace de noms"
msgid "App ID"
msgstr ""
@@ -4671,10 +4699,10 @@ msgid "Appearance was successfully updated."
msgstr "L'apparence a bien été mise à jour."
msgid "Append the comment with %{shrug}"
-msgstr ""
+msgstr "Ajouter %{shrug} dans le commentaire"
msgid "Append the comment with %{tableflip}"
-msgstr ""
+msgstr "Ajouter %{tableflip} dans le commentaire"
msgid "Application"
msgstr "Application"
@@ -4713,7 +4741,7 @@ msgid "ApplicationSettings|After sign-up text"
msgstr ""
msgid "ApplicationSettings|After the instance reaches the user cap, any user who is added or requests access must be approved by an administrator. Leave blank for an unlimited user cap. If you change the user cap to unlimited, you must re-enable %{projectSharingLinkStart}project sharing%{projectSharingLinkEnd} and %{groupSharingLinkStart}group sharing%{groupSharingLinkEnd}."
-msgstr ""
+msgstr "Une fois que l’instance a atteint le plafond d'utilisateurs, tout utilisateur qui est ajouté ou qui demande un accès doit être approuvé par un administrateur. Laissez vide pour qu'il n'y ait pas de limite au plafond du nombre d'utilisateurs. Si vous passez le plafond d'un nombre limité à illimité, vous devez réactiver le %{projectSharingLinkStart}partage de projet%{projectSharingLinkEnd} et le %{groupSharingLinkStart}partage de groupe%{groupSharingLinkEnd}."
msgid "ApplicationSettings|Allowed domains for sign-ups"
msgstr "Domaines autorisés pour les inscriptions"
@@ -4756,7 +4784,7 @@ msgid "ApplicationSettings|Domain denylist"
msgstr "Liste d'exclusion de domaines"
msgid "ApplicationSettings|Email confirmation settings"
-msgstr ""
+msgstr "Paramètres de confirmation des courriels"
msgid "ApplicationSettings|Email restrictions"
msgstr "Restrictions d'adresses de courriel"
@@ -4777,7 +4805,7 @@ msgid "ApplicationSettings|Enter denylist manually"
msgstr "Entrez la liste d'exclusion manuellement"
msgid "ApplicationSettings|Hard"
-msgstr ""
+msgstr "Stricte"
msgid "ApplicationSettings|Minimum password length (number of characters)"
msgstr "Longueur minimale du mot de passe (nombre de caractères)"
@@ -4786,7 +4814,7 @@ msgid "ApplicationSettings|New users can sign up without confirming their email
msgstr "Les nouveaux utilisateurs peuvent s'inscrire sans confirmer leur adresse de courriel."
msgid "ApplicationSettings|Off"
-msgstr ""
+msgstr "Désactivée"
msgid "ApplicationSettings|Only users with e-mail addresses that match these domain(s) can sign up. Wildcards allowed. Use separate lines for multiple entries. Example: domain.com, *.domain.com"
msgstr "Seuls les utilisateurs dont les adresses de courriel correspondent à ce(s) domaine(s ) peuvent s'inscrire. Les jokers sont autorisés. Pour plusieurs entrées, utilisez des lignes séparées. Exemple : domaine.com, *.domaine.com"
@@ -4878,10 +4906,10 @@ msgid "Apply a label"
msgstr "Appliquer une étiquette"
msgid "Apply a template"
-msgstr ""
+msgstr "Appliquer un modèle"
msgid "Apply suggestion"
-msgstr ""
+msgstr "Appliquer la suggestion"
msgid "Apply template"
msgstr ""
@@ -4896,13 +4924,13 @@ msgid "Applying a template will replace the existing issue description. Any chan
msgstr "L'application d'un modèle remplacera la description existante du ticket. Tout les modifications que vous avez apportées seront perdues."
msgid "Applying command"
-msgstr ""
+msgstr "Application de la commande"
msgid "Applying command to %{commandDescription}"
msgstr ""
msgid "Applying multiple commands"
-msgstr ""
+msgstr "Application de plusieurs commandes"
msgid "Applying suggestion..."
msgstr "Application de la suggestion..."
@@ -4936,8 +4964,8 @@ msgstr[1] "%d membres"
msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%{count} approbation requise de la part de %{membersCount}"
+msgstr[1] "%{count} approbations requises de la part de %{membersCount}"
msgid "ApprovalRule|A merge request author collaborating with a merge request approver"
msgstr "Un auteur de demande de fusion collaborant avec un approbateur de demande de fusion"
@@ -5054,28 +5082,28 @@ msgid "Approvals|Section: %section"
msgstr "Section : %section"
msgid "Approve"
-msgstr ""
+msgstr "Approuver"
msgid "Approve a merge request"
-msgstr ""
+msgstr "Approuver une demande de fusion"
msgid "Approve merge request"
msgstr "Approuver la demande de fusion"
msgid "Approve the current merge request."
-msgstr ""
+msgstr "Approuver la demande de fusion actuelle."
msgid "Approved"
-msgstr ""
+msgstr "Approuvée"
msgid "Approved MRs"
-msgstr ""
+msgstr "Demandes de fusion approuvées"
msgid "Approved the current merge request."
-msgstr ""
+msgstr "Demande de fusion actuelle approuvée."
msgid "Approved-By"
-msgstr ""
+msgstr "Approuvée-Par"
msgid "Approver"
msgstr "Approbateur"
@@ -5084,7 +5112,7 @@ msgid "Approvers"
msgstr "Approbateurs"
msgid "Approvers from private group(s) not shown"
-msgstr ""
+msgstr "Les approbateurs issus de groupes privés ne sont pas affichés"
msgid "Apr"
msgstr "avr."
@@ -5108,7 +5136,7 @@ msgid "Archive test case"
msgstr "Archiver le cas de test"
msgid "Archived"
-msgstr ""
+msgstr "Archivées"
msgid "Archived (%{movedToStart}moved%{movedToEnd})"
msgstr "Archivé (%{movedToStart}déplacé%{movedToEnd})"
@@ -5177,7 +5205,7 @@ msgid "Are you sure you want to delete this label?"
msgstr "Êtes-vous sûr de vouloir supprimer cette étiquette ?"
msgid "Are you sure you want to delete this pipeline schedule?"
-msgstr "Êtesâ€vous sûr·e de vouloir supprimer ce pipeline programmé ?"
+msgstr "Êtesâ€vous sûr(e) de vouloir supprimer ce pipeline programmé ?"
msgid "Are you sure you want to delete this pipeline? Doing so will expire all pipeline caches and delete all related objects, such as builds, logs, artifacts, and triggers. This action cannot be undone."
msgstr "Êtes-vous sûr(e) de vouloir supprimer ce pipeline ? Faire ceci fera expirer tous les caches du pipeline et supprimera tous les objets associés, tels que les constructions, les journaux, les artéfacts et les déclencheurs. Cette action ne peut pas être annulée."
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] "Êtes-vous sûr de vouloir importer %d dépôt ?"
msgstr[1] "Êtes-vous sûr de vouloir importer %d dépôts ?"
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr "Êtes-vous sûr(e) de vouloir quitter l'EDI Web ? Toutes les modifications non enregistrées seront perdues."
+
msgid "Are you sure you want to lock %{path}?"
msgstr "Êtesâ€vous sûr(e) de vouloir verrouiller %{path} ?"
@@ -5248,7 +5279,7 @@ msgid "Are you sure you want to reset the error tracking access token?"
msgstr "Êtes-vous sûr de vouloir réinitialiser le jeton d'accès du suivi des erreurs ?"
msgid "Are you sure you want to reset the health check token?"
-msgstr "Êtesâ€vous sûr·e de vouloir réinitialiser le jeton de bilan de santé ?"
+msgstr "Êtesâ€vous sûr(e) de vouloir réinitialiser le jeton de bilan de santé ?"
msgid "Are you sure you want to reset the registration token?"
msgstr "Êtes-vous sûr(e) de vouloir réinitialiser le jeton d’inscription ?"
@@ -5272,7 +5303,7 @@ msgid "Are you sure you want to stop this environment?"
msgstr "Êtesâ€vous sûr(e) de vouloir arrêter cet environnement ?"
msgid "Are you sure you want to unlock %{path_lock_path}?"
-msgstr "Êtesâ€vous sûr·e de vouloir déverrouiller %{path_lock_path} ?"
+msgstr "Êtesâ€vous sûr(e) de vouloir déverrouiller %{path_lock_path} ?"
msgid "Are you sure you want to unlock %{path}?"
msgstr "Êtesâ€vous sûr(e) de vouloir déverrouiller %{path} ?"
@@ -5338,7 +5369,7 @@ msgid "As we continue to build more features for SAST, we'd love your feedback o
msgstr "Comme nous développons toujours davantage de fonctions pour SAST, nous souhaiterions connaître votre avis sur la fonctionnalité de configuration SAST via %{linkStart}cet ticket%{linkEnd}."
msgid "AsanaService|%{user} pushed to branch %{branch} of %{project_name} ( %{commit_url} ):"
-msgstr ""
+msgstr "%{user} a poussé sur la branche %{branch} de %{project_name} ( %{commit_url} ) :"
msgid "AsanaService|Add commit messages as comments to Asana tasks."
msgstr "Ajouter les messages de commit en tant que commentaires sur les tâches Asana."
@@ -5347,7 +5378,7 @@ msgid "AsanaService|Comma-separated list of branches to be automatically inspect
msgstr "Liste des branches à inspecter automatiquement, séparées par des virgules. Laissez vide pour inclure toutes les branches."
msgid "AsanaService|User Personal Access Token. User must have access to the task. All comments are attributed to this user."
-msgstr ""
+msgstr "Jeton d'accès personnel à l'utilisateur. L'utilisateur doit avoir accès à la tâche. Tous les commentaires lui seront attribués."
msgid "Ascending"
msgstr "Croissant"
@@ -5371,7 +5402,7 @@ msgid "Assets:"
msgstr "Ressources :"
msgid "Assign"
-msgstr ""
+msgstr "Assigner"
msgid "Assign Iteration"
msgstr "Assigner l'Itération"
@@ -5392,7 +5423,7 @@ msgid "Assign reviewer"
msgstr "Assigner un relecteur"
msgid "Assign reviewer(s)"
-msgstr ""
+msgstr "Assigner un ou des relecteurs"
msgid "Assign severity"
msgstr "Attribuer la gravité"
@@ -5448,16 +5479,16 @@ msgid "Assignee lists not available with your current license"
msgstr "La liste des personnes assignées n’est pas disponible avec votre licence actuelle"
msgid "Assignee(s)"
-msgstr "Assigné·e(s)"
+msgstr "Assigné(s)"
msgid "Assignees"
msgstr "Assigné(e)s"
msgid "Assigns %{assignee_users_sentence}."
-msgstr ""
+msgstr "Assigne %{assignee_users_sentence}."
msgid "Assigns %{reviewer_users_sentence} as %{reviewer_text}."
-msgstr ""
+msgstr "Assigne %{reviewer_users_sentence} comme %{reviewer_text}."
msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
msgstr "Au moins une approbation d’un propriétaire de code est requise pour modifier les fichiers correspondant aux règles de propriété du code (CODEOWNER)."
@@ -5466,7 +5497,7 @@ msgid "At least one field of %{one_of_required_fields} must be present"
msgstr "Au moins un champ de %{one_of_required_fields} doit être présent"
msgid "At least one of group_id or project_id must be specified"
-msgstr ""
+msgstr "Au minimum un group_id ou un project_id doit être spécifié"
msgid "At least one of your Personal Access Tokens is expired. %{generate_new}"
msgstr "Au moins un de vos Jetons d'Accès Personnel a expiré. %{generate_new}"
@@ -5501,7 +5532,7 @@ msgid "Audit events"
msgstr "Événements d'audit"
msgid "AuditLogs|(removed)"
-msgstr ""
+msgstr "(supprimé)"
msgid "AuditLogs|Action"
msgstr "Action"
@@ -5557,7 +5588,7 @@ msgid "AuditStreams|A header with this name already exists."
msgstr "Un en-tête portant ce nom existe déjà."
msgid "AuditStreams|Active"
-msgstr ""
+msgstr "Actif"
msgid "AuditStreams|Add a custom header"
msgstr "Ajouter un en-tête personnalisé"
@@ -5572,7 +5603,7 @@ msgid "AuditStreams|Add another custom header"
msgstr "Ajouter un autre en-tête personnalisé"
msgid "AuditStreams|Add external stream destination"
-msgstr ""
+msgstr "Ajouter une destination de flux externe"
msgid "AuditStreams|Add header"
msgstr "Ajouter un en-tête"
@@ -5581,19 +5612,19 @@ msgid "AuditStreams|Add streaming destination"
msgstr "Ajouter une destination de flux"
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
-msgstr ""
+msgstr "Une erreur s’est produite lors de la création de la destination du flux d'événements d’audit externe. Veuillez réessayer."
msgid "AuditStreams|An error occurred when deleting external audit event stream destination. Please try it again."
-msgstr ""
+msgstr "Une erreur s’est produite lors de la suppression de la destination du flux d'événements d’audit externe. Veuillez réessayer."
msgid "AuditStreams|An error occurred when fetching external audit event streams. Please try it again"
-msgstr ""
+msgstr "Une erreur s'est produite lors de la récupération des flux d'événements d'audit externes. Veuillez réessayer"
msgid "AuditStreams|An error occurred when updating external audit event stream destination. Please try it again."
-msgstr ""
+msgstr "Une erreur s’est produite lors de la mise à jour de la destination du flux d'événements d’audit externe. Veuillez réessayer."
msgid "AuditStreams|Cancel editing"
-msgstr ""
+msgstr "Annuler l'édition"
msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr "En-têtes HTTP personnalisés (facultatif)"
@@ -5604,9 +5635,12 @@ msgstr "Supprimer %{link}"
msgid "AuditStreams|Destination URL"
msgstr "URL de destination"
-msgid "AuditStreams|Destinations receive all audit event data"
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
msgstr ""
+msgid "AuditStreams|Destinations receive all audit event data"
+msgstr "Les destinations reçoivent toutes les données des événements d'audit"
+
msgid "AuditStreams|Edit %{link}"
msgstr "Modifier %{link}"
@@ -5623,7 +5657,7 @@ msgid "AuditStreams|Save external stream destination"
msgstr "Sauvegarder la destination du flux externe"
msgid "AuditStreams|Setup streaming for audit events"
-msgstr ""
+msgstr "Configurer les flux d'événements d'audit"
msgid "AuditStreams|Stream added successfully"
msgstr "Flux ajouté avec succès"
@@ -5635,13 +5669,13 @@ msgid "AuditStreams|Stream updated successfully"
msgstr "Flux mis à jour avec succès"
msgid "AuditStreams|Streams"
-msgstr ""
+msgstr "Flux"
msgid "AuditStreams|This could include sensitive information. Make sure you trust the destination endpoint."
-msgstr ""
+msgstr "Cela peut inclure des données sensibles. Assurez-vous de pouvoir faire confiance au point de terminaison de la destination."
msgid "AuditStreams|This is great for keeping everything one place."
-msgstr ""
+msgstr "C'est l'idéal pour tout garder au même endroit."
msgid "AuditStreams|Value"
msgstr "Valeur"
@@ -5649,6 +5683,9 @@ msgstr "Valeur"
msgid "AuditStreams|Verification token"
msgstr "Jeton de vérification"
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "août"
@@ -5659,7 +5696,7 @@ msgid "Authenticate"
msgstr "S'authentifier"
msgid "Authenticate user SSH keys without requiring additional configuration. Performance of GitLab can be improved by using the GitLab database instead. %{link_start}How do I configure authentication using the GitLab database? %{link_end}"
-msgstr ""
+msgstr "Authentifier les clés SSH des utilisateurs sans avoir besoin d’une configuration supplémentaire. Les performances de GitLab peuvent être améliorées en utilisant la base de données de GitLab à la place. %{link_start}Comment puis-je configurer l'authentification à l'aide de la base de données de GitLab ? %{link_end}"
msgid "Authenticate with GitHub"
msgstr "S'authentifier avec GitHub"
@@ -5689,7 +5726,7 @@ msgid "Authentication"
msgstr "Authentification"
msgid "Authentication Failure"
-msgstr ""
+msgstr "Échec de l'authentification"
msgid "Authentication Log"
msgstr "Journal d’authentification"
@@ -5716,7 +5753,7 @@ msgid "Authentication via WebAuthn device failed."
msgstr "L'authentification via l'appareil WebAuthn a échoué."
msgid "Author"
-msgstr "Auteur·e"
+msgstr "Auteur"
msgid "Author: %{author_name}"
msgstr "Auteur : %{author_name}"
@@ -5764,7 +5801,7 @@ msgid "AuthorizedApplication|Revoke application"
msgstr "Révoquer l'application"
msgid "Authors: %{authors}"
-msgstr "Auteur·e·s : %{authors}"
+msgstr "Auteurs : %{authors}"
msgid "Auto DevOps"
msgstr "Auto DevOps"
@@ -5809,7 +5846,7 @@ msgid "AutoRemediation| 1 Merge Request"
msgstr "1 Demande de Fusion"
msgid "AutoRemediation|%{mrsCount} ready for review"
-msgstr ""
+msgstr "%{mrsCount} prêtes pour revue"
msgid "AutoRemediation|Auto-fix"
msgstr "Correction auto"
@@ -5824,22 +5861,22 @@ msgid "AutoRemediation|Introducing GitLab auto-fix"
msgstr "Introduction à la correction automatique de GitLab"
msgid "AutoRollback|Automatic rollbacks start when a critical alert is triggered. If the last successful deployment fails to roll back automatically, it can still be done manually."
-msgstr ""
+msgstr "Les retours en arrière automatiques démarrent lorsqu’une alerte critique est déclenchée. Si le dernier déploiement réussi ne parvient pas à être restauré automatiquement, cela peut toujours être fait manuellement."
msgid "AutoRollback|Automatically roll back to the last successful deployment when a critical problem is detected."
msgstr "Restaurer automatiquement le dernier déploiement réussi lorsqu'un problème critique est détecté."
msgid "AutoRollback|Enable automatic rollbacks"
-msgstr ""
+msgstr "Activer les retours en arrière automatiques"
msgid "Autocomplete"
-msgstr ""
+msgstr "Auto-complétion"
msgid "Autocomplete description"
-msgstr ""
+msgstr "Description de l'auto-complétion"
msgid "Autocomplete hint"
-msgstr ""
+msgstr "Indice de l'auto-complétion"
msgid "Autocomplete usage hint"
msgstr ""
@@ -5851,13 +5888,13 @@ msgid "Automatic certificate management using Let's Encrypt"
msgstr "Gestion automatique des certificats à l'aide de Let's Encrypt"
msgid "Automatic deployment rollbacks"
-msgstr ""
+msgstr "Retours en arrière automatiques des déploiements"
msgid "Automatic event tracking provides a traceable history for audits."
msgstr "Le suivi automatique des événements fournit un historique traçable pour les audits."
msgid "Automatically close associated incident when a recovery alert notification resolves an alert"
-msgstr ""
+msgstr "Fermer automatiquement l'incident associé lorsqu'une notification d'alerte pour récupération résoud une alerte"
msgid "Automatically resolved"
msgstr ""
@@ -5872,7 +5909,7 @@ msgid "Available"
msgstr "Disponible"
msgid "Available ID"
-msgstr ""
+msgstr "Disponibilité de l'ID"
msgid "Available group runners: %{runners}"
msgstr "Exécuteurs de groupe disponibles : %{runners}"
@@ -5880,23 +5917,20 @@ msgstr "Exécuteurs de groupe disponibles : %{runners}"
msgid "Available on-demand"
msgstr "Disponible sur demande"
-msgid "Available shared runners:"
-msgstr "Exécuteurs partagés disponibles :"
-
msgid "Available specific runners"
-msgstr ""
+msgstr "Exécuteurs spécifiques disponibles"
msgid "Avatar for %{assigneeName}"
-msgstr ""
+msgstr "Avatar pour %{assigneeName}"
msgid "Avatar will be removed. Are you sure?"
-msgstr "L’avatar sera supprimé. Êtesâ€vous sûr·e ?"
+msgstr "L’avatar sera supprimé. Êtesâ€vous sûr(e) ?"
msgid "Average per day: %{average}"
msgstr "Moyenne par jour : %{average}"
msgid "Awaiting user signup"
-msgstr ""
+msgstr "En attente d'inscription de l'utilisateur"
msgid "Award added"
msgstr "Récompense ajoutée"
@@ -6061,7 +6095,7 @@ msgid "Based on"
msgstr "Basé sur"
msgid "Batch size"
-msgstr ""
+msgstr "Taille du lot"
msgid "Batched Job|Background Migrations"
msgstr "Migrations en arrière-plan"
@@ -6121,7 +6155,7 @@ msgid "BatchedJob|Pause ms"
msgstr ""
msgid "BatchedJob|Pause time (ms)"
-msgstr ""
+msgstr "Temps de pause (ms)"
msgid "BatchedJob|Previous Status"
msgstr "État précédent"
@@ -6133,7 +6167,7 @@ msgid "BatchedJob|Started at"
msgstr "Démarré à"
msgid "BatchedJob|Transition logs:"
-msgstr ""
+msgstr "Journaux des transitions :"
msgid "Be careful. Changing the project's namespace can have unintended side effects."
msgstr "Faites attention. Modifier l'espace de noms du projet peut avoir des effets secondaires imprévus."
@@ -6175,7 +6209,7 @@ msgid "Bi-weekly code coverage"
msgstr "Couverture de code bihebdomadaire"
msgid "Billable Users"
-msgstr ""
+msgstr "Utilisateurs facturables"
msgid "Billing"
msgstr "Facturation"
@@ -6202,7 +6236,7 @@ msgid "BillingPlans|5GB storage"
msgstr "5 Go de stockage"
msgid "BillingPlans|@%{user_name} you are currently using the %{plan_name}."
-msgstr ""
+msgstr "@%{user_name} vous utilisez actuellement le %{plan_name}."
msgid "BillingPlans|Advanced CI/CD"
msgstr "CI/CD avancé"
@@ -6250,7 +6284,7 @@ msgid "BillingPlans|Free upgrade!"
msgstr "Mise à niveau gratuite !"
msgid "BillingPlans|If you would like to downgrade your plan please contact %{support_link_start}Customer Support%{support_link_end}."
-msgstr ""
+msgstr "Si vous souhaitez passer à un forfait inférieur, veuillez contacter le %{support_link_start}Support Client%{support_link_end}."
msgid "BillingPlans|Includes free static websites"
msgstr "Inclut les sites Web statiques gratuits"
@@ -6265,7 +6299,7 @@ msgid "BillingPlans|Learn more about each plan by visiting our %{pricing_page_li
msgstr "En savoir plus sur chacun des forfaits en visitant notre %{pricing_page_link}."
msgid "BillingPlans|Looking to purchase or manage a subscription for your group? Navigate to your %{groups_link} and go to %{strong_open}Settings &gt; Billing.%{strong_close}"
-msgstr "Vous cherchez à acheter ou gérer un abonnement pour votre groupe ? Accédez à vos %{groups_link} puis allez sur %{strong_open}Paramètres &gt; Facturation%{strong_close}"
+msgstr "Vous cherchez à acheter ou gérer un abonnement pour votre groupe ? Accédez à votre %{groups_link} puis allez sur %{strong_open}Paramètres &gt; Facturation%{strong_close}"
msgid "BillingPlans|Loved and trusted by our customers"
msgstr ""
@@ -6283,7 +6317,7 @@ msgid "BillingPlans|Organization wide security, compliance and planning"
msgstr "Sécurité, conformité et planification à l'échelle de l'organisation"
msgid "BillingPlans|Portfolio management"
-msgstr ""
+msgstr "Gestion de portefeuille"
msgid "BillingPlans|Premium"
msgstr "Premium"
@@ -6424,10 +6458,10 @@ msgid "Billings|Reactivate trial"
msgstr "Réactiver l'essai"
msgid "Billings|Seats in use / Seats available"
-msgstr ""
+msgstr "Sièges utilisés / Sièges disponibles"
msgid "Billings|Seats in use / Seats in subscription"
-msgstr ""
+msgstr "Sièges utilisés / Sièges dans l'abonnement"
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr "Les exécuteurs partagés ne peuvent pas être activés tant qu'aucune carte de crédit valide n'est enregistrée."
@@ -6460,7 +6494,7 @@ msgid "Billing|Add seats"
msgstr "Ajouter des sièges"
msgid "Billing|An email address is only visible for users with public emails."
-msgstr ""
+msgstr "Une adresse de courriel n'est visible que pour les utilisateurs ayant des courriels publics."
msgid "Billing|An error occurred while approving %{user}"
msgstr "Une erreur s’est produite lors de l’approbation de %{user}"
@@ -6575,7 +6609,7 @@ msgid "Blocked issue"
msgstr "Ticket bloqué"
msgid "Blocking"
-msgstr ""
+msgstr "Bloquant"
msgid "Blocking epics"
msgstr "Épopées bloquantes"
@@ -6593,7 +6627,7 @@ msgid "Board scope affects which epics are displayed for anyone who visits this
msgstr "La portée du tableau indique quelles épopées sont affichées pour toute les personnes visitant ce tableau"
msgid "Board scope affects which issues are displayed for anyone who visits this board"
-msgstr ""
+msgstr "La portée du tableau définit quels tickets sont affichés pour toute personne qui le consulte"
msgid "BoardNewEpic|Groups"
msgstr "Groupes"
@@ -6876,7 +6910,7 @@ msgid "Branch changed"
msgstr "Branche modifiée"
msgid "Branch defaults"
-msgstr ""
+msgstr "Paramètres par défaut des branches"
msgid "Branch has been updated since the merge was requested."
msgstr "La branche a été mise à jour depuis la fusion a été demandée."
@@ -6888,10 +6922,10 @@ msgid "Branch name"
msgstr "Nom de la branche"
msgid "Branch name template"
-msgstr ""
+msgstr "Modèle de nom de branche"
msgid "Branch not loaded - %{branchId}"
-msgstr ""
+msgstr "Branche non chargée - %{branchId}"
msgid "Branch rules"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr "Les %{linkStart}caractères génériques%{linkEnd} tels que *-stable ou
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr "Les %{linkStart}caractères génériques%{linkEnd} tels que *-stable ou production/* sont pris en charge."
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr "Toutes les branches"
@@ -6912,7 +6955,7 @@ msgid "BranchRules|Allow all users with push access to %{linkStart}force push%{l
msgstr "Autoriser tous les utilisateurs ayant un accès pour pousser à %{linkStart}forcer les poussées%{linkEnd}."
msgid "BranchRules|Allowed to force push"
-msgstr ""
+msgstr "Autorisé à forcer les poussées"
msgid "BranchRules|Allowed to merge"
msgstr "Autorisé à fusionner"
@@ -6933,7 +6976,7 @@ msgid "BranchRules|Approvals"
msgstr "Approbations"
msgid "BranchRules|Approvals to ensure separation of duties for new merge requests. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
+msgstr "Approbations pour assurer la séparation des tâches pour les nouvelles demandes de fusion. %{linkStart}En savoir plus.%{linkEnd}."
msgid "BranchRules|Branch"
msgstr "Branche"
@@ -7016,9 +7059,6 @@ msgstr "Utilisateurs"
msgid "BranchRules|default"
msgstr "par défaut"
-msgid "BranchRules|protected"
-msgstr "protégée"
-
msgid "Branches"
msgstr "Branches"
@@ -7028,6 +7068,9 @@ msgstr "Branches : %{source_branch} vers %{target_branch}"
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr "Branches : %{source_branch} → %{target_branch}"
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr "Une branche ne sera pas supprimée si elle est protégée ou associée à une demande de fusion ouverte."
+
msgid "Branches|Active"
msgstr "Active"
@@ -7049,8 +7092,11 @@ msgstr "Impossible de trouver le commit HEAD de cette branche"
msgid "Branches|Compare"
msgstr "Comparer"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "Supprimer toutes les branches qui ont été fusionnées dans « %{default_branch} »"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
+msgstr "Supprimer toutes les branches fusionnées ?"
msgid "Branches|Delete branch"
msgstr "Supprimer cette branche"
@@ -7070,9 +7116,6 @@ msgstr "Supprimer la branche protégée. Êtes-vous ABSOLUMENT SÛR(E) ?"
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr "La suppression de la branche %{strongStart}%{branchName}%{strongEnd} ne peut être annulée. Êtes-vous sûr ?"
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "La suppression des branches fusionnées ne peut être annulée. Êtesâ€vous sûr(e) ?"
-
msgid "Branches|Filter by branch name"
msgstr "Filtrer par nom de branche"
@@ -7094,6 +7137,9 @@ msgstr "Vue d’ensemble"
msgid "Branches|Please type the following to confirm:"
msgstr "Veuillez taper ce qui suit pour confirmer :"
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr "Veuillez taper ce qui suit pour confirmer : %{codeStart}delete%{codeEnd}."
+
msgid "Branches|Show active branches"
msgstr "Afficher les branches actives"
@@ -7127,6 +7173,12 @@ msgstr "La branche par défaut ne peut pas être supprimée"
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr "Cette branche n'a pas été fusionnée dans %{defaultBranchName}. Pour éviter la perte de données, envisagez de la fusionner avant de la supprimer."
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr "Cela peut inclure des branches fusionnées qui ne sont pas visibles sur l’écran actuel."
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr "Pour rejeter les changements locaux et écraser la branche avec la version du dépôt en amont, veuillez la supprimer ici et cliquer sur « Mettre à jour maintenant » ciâ€dessus."
@@ -7139,6 +7191,9 @@ msgstr "Oui, supprimer la branche"
msgid "Branches|Yes, delete protected branch"
msgstr "Oui, supprimer la branche protégée"
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr "Vous êtes sur le point de %{strongStart}supprimer toutes les branches%{strongEnd} qui ont été fusionnées dans %{codeStart}%{defaultBranch}%{codeEnd}."
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr "Vous êtes sur le point de supprimer définitivement la branche %{branchName}."
@@ -7185,13 +7240,16 @@ msgid "Browse templates"
msgstr "Parcourir les modèles"
msgid "Build cannot be erased"
-msgstr ""
+msgstr "La construction ne peut pas être effacée"
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr "Une erreur s'est produite lors de la récupération des artéfacts"
msgid "BuildArtifacts|Loading artifacts"
-msgstr ""
+msgstr "Chargement des artéfacts"
+
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr "Construction de votre demande de fusion. Patientez quelques instants puis actualisez cette page."
msgid "Built-in"
msgstr "Intégré"
@@ -7317,7 +7375,7 @@ msgid "BulkImport|invalid entity source type"
msgstr "type de source d'entité non valide"
msgid "BulkImport|must be a group"
-msgstr ""
+msgstr "doit être un groupe"
msgid "Bullet list"
msgstr "Liste à puces"
@@ -7329,10 +7387,10 @@ msgid "BurndownChartLabel|Remaining"
msgstr "Restants"
msgid "Burnup chart"
-msgstr ""
+msgstr "Graphique d'évolution"
msgid "Burnup chart could not be generated due to too many events"
-msgstr "Les graphiques d'avancement ne peuvent pas être générés en raison d'un trop grand nombre d'événements"
+msgstr "Le graphique d'évolution ne peut pas être généré en raison d'un trop grand nombre d'événements"
msgid "Business"
msgstr "Entreprise"
@@ -7359,7 +7417,7 @@ msgid "By authenticating with an account tied to an Enterprise e-mail address, i
msgstr "En s'authentifiant avec un compte lié à une adresse électronique d'Entreprise, il est considéré que ce compte est un Utilisateur d'Entreprise. "
msgid "By default, all projects and groups will use the global notifications setting."
-msgstr ""
+msgstr "Par défaut, tous les projets et groupes utiliseront les paramètres de notifications globaux."
msgid "By month"
msgstr "Par mois"
@@ -7392,7 +7450,7 @@ msgid "CI/CD"
msgstr "Intégration et livraison continues"
msgid "CI/CD Analytics"
-msgstr ""
+msgstr "Analytique CI/CD"
msgid "CI/CD Settings"
msgstr "Paramètres CI/CD"
@@ -7407,7 +7465,7 @@ msgid "CI/CD limits"
msgstr "Limites CI/CD"
msgid "CI/CD minutes"
-msgstr ""
+msgstr "Minutes CI/CD"
msgid "CI/CD|No projects have been added to the scope"
msgstr "Aucun projet n'a été ajouté à la portée"
@@ -7454,10 +7512,10 @@ msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr "Une erreur s’est produite lors de la récupération des statistiques de publication"
msgid "CICDAnalytics|Time to restore service"
-msgstr ""
+msgstr "Délai de restauration de service"
msgid "CICDAnalytics|What is shared runner duration?"
-msgstr ""
+msgstr "Qu’est-ce que la durée des exécuteurs partagés ?"
msgid "CICD|Add a %{base_domain_link_start}base domain%{link_end} to your %{kubernetes_cluster_link_start}Kubernetes cluster%{link_end} for your deployment strategy to work."
msgstr "Ajoutez un %{base_domain_link_start}domaine de base%{link_end} à votre %{kubernetes_cluster_link_start}grappe de serveurs Kubernetes%{link_end} pour que votre stratégie de déploiement fonctionne."
@@ -7484,7 +7542,7 @@ msgid "CICD|Default to Auto DevOps pipeline"
msgstr "Pipeline Auto DevOps par défaut"
msgid "CICD|Default to Auto DevOps pipeline for all projects"
-msgstr "Utiliser par défaut le pipeline Auto DevOps pour tout les projets"
+msgstr "Utiliser par défaut le pipeline Auto DevOps pour tous les projets"
msgid "CICD|Deployment strategy"
msgstr "Stratégie de déploiement"
@@ -7496,7 +7554,7 @@ msgid "CICD|Jobs"
msgstr "Tâches"
msgid "CICD|Limit"
-msgstr ""
+msgstr "Limite"
msgid "CICD|Limit CI_JOB_TOKEN access"
msgstr "Limiter l'accès à CI_JOB_TOKEN"
@@ -7523,7 +7581,7 @@ msgid "CICD|group enabled"
msgstr "activé sur le groupe"
msgid "CICD|instance enabled"
-msgstr "instance activée"
+msgstr "activé sur l'instance"
msgid "CLOSED"
msgstr "FERMÉ"
@@ -7559,7 +7617,7 @@ msgid "CVE|Enable CVE ID requests in the issue sidebar"
msgstr "Activer les demandes d'ID de CVE dans la barre latérale des tickets"
msgid "CVE|Request CVE ID"
-msgstr ""
+msgstr "Demander un ID de CVE"
msgid "CVE|Why Request a CVE ID?"
msgstr "Pourquoi Demander un ID de CVE ?"
@@ -7598,13 +7656,13 @@ msgid "Can be manually deployed to"
msgstr ""
msgid "Can be overridden in each project."
-msgstr ""
+msgstr "Peut être outrepassé dans chaque projet."
msgid "Can create groups:"
msgstr "Peut créer des groupes :"
msgid "Can not delete primary training"
-msgstr ""
+msgstr "Impossible de supprimer la formation de base"
msgid "Can't apply as the source branch was deleted."
msgstr "Impossible d'appliquer car la branche source a été supprimée."
@@ -7619,7 +7677,7 @@ msgid "Can't apply this suggestion."
msgstr "Impossible d'appliquer cette suggestion."
msgid "Can't be empty"
-msgstr ""
+msgstr "Ne peut pas être vide"
msgid "Can't create snippet: %{err}"
msgstr "Impossible de créer l'extrait de code : %{err}"
@@ -7706,7 +7764,7 @@ msgid "Cancelled"
msgstr "Annulé"
msgid "Cancelling Preview"
-msgstr ""
+msgstr "Annulation de l'aperçu"
msgid "Cannot assign a confidential epic to a non-confidential issue. Make the issue confidential and try again"
msgstr "Impossible d'assigner une épopée confidentielle à un ticket non confidentiel. Rendez le ticket confidentiel et réessayez"
@@ -7727,13 +7785,13 @@ msgid "Cannot delete %{profile_name} referenced in security policy"
msgstr "Impossible de supprimer %{profile_name} référencé dans la stratégie de sécurité"
msgid "Cannot delete the default framework"
-msgstr ""
+msgstr "Impossible de supprimer le cadre par défaut"
msgid "Cannot have multiple Jira imports running at the same time"
msgstr "Impossible d'exécuter plusieurs importations Jira en même temps"
msgid "Cannot have multiple unresolved alerts"
-msgstr ""
+msgstr "Impossible d'avoir plusieurs alertes non résolues"
msgid "Cannot import because issues are not available in this project."
msgstr "Impossible d'importer car les tickets ne sont pas disponibles dans ce projet."
@@ -7757,7 +7815,7 @@ msgid "Cannot modify provider during creation"
msgstr "Impossible de modifier le fournisseur pendant la création"
msgid "Cannot promote issue because it does not belong to a group."
-msgstr ""
+msgstr "Impossible de promouvoir le ticket car il n'appartient à aucun groupe."
msgid "Cannot promote issue due to insufficient permissions."
msgstr "Impossible de promouvoir le ticket en raison d'autorisations insuffisantes."
@@ -7775,7 +7833,7 @@ msgid "Capacity threshold"
msgstr ""
msgid "Card holder name"
-msgstr ""
+msgstr "Nom du titulaire de la carte"
msgid "Card number:"
msgstr "Numéro de carte :"
@@ -7814,7 +7872,7 @@ msgid "Certificate (PEM)"
msgstr "Certificat (PEM)"
msgid "Certificate Issuer"
-msgstr ""
+msgstr "Émetteur du certificat"
msgid "Certificate Subject"
msgstr ""
@@ -7859,7 +7917,7 @@ msgid "Change status"
msgstr ""
msgid "Change subscription"
-msgstr ""
+msgstr "Modifier l'abonnement"
msgid "Change template"
msgstr "Changer de modèle"
@@ -7871,7 +7929,7 @@ msgid "Change your password"
msgstr "Modifiez votre mot de passe"
msgid "Change your password or recover your current one"
-msgstr ""
+msgstr "Modifiez votre mot de passe ou réinitialisez votre mot de passe actuel"
msgid "ChangeReviewer|Reviewer changed from %{old} to %{new}"
msgstr "Le Relecteur est passé de %{old} à %{new}"
@@ -7889,16 +7947,16 @@ msgid "ChangeTypeAction|GitLab will create a branch in your fork and start a mer
msgstr "GitLab créera une branche dans votre projet divergent et lancera une demande de fusion."
msgid "ChangeTypeAction|Pick into branch"
-msgstr "Sélectionner dans la branche"
+msgstr "Picorer vers la branche"
msgid "ChangeTypeAction|Pick into project"
-msgstr ""
+msgstr "Picorer vers le projet"
msgid "ChangeTypeAction|Revert"
msgstr "Défaire"
msgid "ChangeTypeAction|Revert in branch"
-msgstr ""
+msgstr "Défaire dans la branche"
msgid "ChangeTypeAction|Search branches"
msgstr "Rechercher des branches"
@@ -7937,7 +7995,7 @@ msgid "Changed squash option to %{squash_option}"
msgstr ""
msgid "Changed the title to \"%{title_param}\"."
-msgstr ""
+msgstr "Le titre a été modifié en « %{title_param} »."
msgid "Changes"
msgstr "Modifications"
@@ -7952,7 +8010,7 @@ msgid "Changes suppressed. Click to show."
msgstr "Modifications supprimées. Cliquez pour afficher."
msgid "Changes the title to \"%{title_param}\"."
-msgstr ""
+msgstr "Modifie le titre en « %{title_param} »."
msgid "Changes to the title have not been saved"
msgstr "Les modifications apportées au titre n'ont pas été enregistrées"
@@ -7999,11 +8057,14 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
msgid "ChatMessage|and [%{count} more](%{pipeline_failed_jobs_url})"
-msgstr ""
+msgstr "et [%{count} de plus](%{pipeline_failed_jobs_url})"
msgid "ChatMessage|has failed"
msgstr "a échoué"
@@ -8018,7 +8079,7 @@ msgid "ChatMessage|in %{duration}"
msgstr ""
msgid "ChatMessage|in %{project_link}"
-msgstr ""
+msgstr "dans %{project_link}"
msgid "Check again"
msgstr ""
@@ -8047,11 +8108,14 @@ msgstr "Vérifier les vulnérabilités connues de vos images Docker."
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr "Vérifiez les vulnérabilités connues des images de votre grappe de serveurs Kubernetes."
+msgid "Check your sign-up restrictions"
+msgstr "Vérifiez vos restrictions d'inscription"
+
msgid "Checking %{text} availability…"
msgstr "Vérification de la disponibilité de %{text}…"
msgid "Checking approval status"
-msgstr ""
+msgstr "Vérification de l'état d'approbation"
msgid "Checking branch availability..."
msgstr "Vérification de la disponibilité du nom de branche…"
@@ -8103,13 +8167,13 @@ msgstr[0] "%{quantity} pack de stockage"
msgstr[1] "%{quantity} packs de stockage"
msgid "Checkout|%{selectedPlanText} plan"
-msgstr ""
+msgstr "Forfait %{selectedPlanText}"
msgid "Checkout|%{startDate} - %{endDate}"
msgstr "%{startDate} - %{endDate}"
msgid "Checkout|(may be %{linkStart}charged upon purchase%{linkEnd})"
-msgstr ""
+msgstr "(peut être %{linkStart}facturée lors de l'achat%{linkEnd})"
msgid "Checkout|(x%{numberOfUsers})"
msgstr "(x%{numberOfUsers})"
@@ -8151,7 +8215,7 @@ msgid "Checkout|Continue to billing"
msgstr ""
msgid "Checkout|Continue to payment"
-msgstr ""
+msgstr "Passer au paiement"
msgid "Checkout|Country"
msgstr "Pays"
@@ -8168,9 +8232,6 @@ msgstr "Le chargement du formulaire de carte de crédit a échoué : %{message}"
msgid "Checkout|Edit"
msgstr "Modifier"
-msgid "Checkout|Enter a number greater than 0"
-msgstr "Entrez un nombre supérieur à 0"
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr "Exp %{expirationMonth}/%{expirationYear}"
@@ -8186,8 +8247,8 @@ msgstr "Impossible de charger les pays. Veuillez réessayer."
msgid "Checkout|Failed to load states. Please try again."
msgstr "Échec du chargement des états. Veuillez réessayer."
-msgid "Checkout|Failed to load the payment form. Please try again."
-msgstr "Échec du chargement du formulaire de paiement. Veuillez réessayer."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
+msgstr "Échec du chargement du formulaire de paiement. Actualisez la page et réessayez."
msgid "Checkout|Failed to register credit card. Please try again."
msgstr "Échec de l'enregistrement de la carte de crédit. Veuillez réessayer."
@@ -8199,7 +8260,7 @@ msgid "Checkout|GitLab group"
msgstr "Groupe GitLab"
msgid "Checkout|GitLab plan"
-msgstr ""
+msgstr "forfait GitLab"
msgid "Checkout|Group"
msgstr "Groupe"
@@ -8210,6 +8271,9 @@ msgstr "Doit valoir %{minimumNumberOfUsers} (votre nombre de sièges en cours d'
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr "Doit valoir %{minimumNumberOfUsers} (votre nombre de sièges en cours d'utilisation plus tous les membres hors limite) ou plus. Pour acheter moins de sièges, enlevez des membres du groupe."
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr "Doit être 1 ou plus. Ne peut pas être un nombre décimal."
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr "Nom de l'entreprise ou de l'organisation utilisant GitLab"
@@ -8229,7 +8293,7 @@ msgid "Checkout|Please select a country"
msgstr "Veuillez sélectionner un pays"
msgid "Checkout|Please select a state"
-msgstr ""
+msgstr "Veuillez sélectionner un département"
msgid "Checkout|Purchase details"
msgstr "Détails de l'achat"
@@ -8238,7 +8302,7 @@ msgid "Checkout|Select"
msgstr ""
msgid "Checkout|State"
-msgstr ""
+msgstr "Département"
msgid "Checkout|Storage packs"
msgstr "Packs de stockage"
@@ -8259,7 +8323,7 @@ msgid "Checkout|Success: subscription"
msgstr "Réussite : abonnement"
msgid "Checkout|Tax"
-msgstr ""
+msgstr "Taxe"
msgid "Checkout|Total"
msgstr "Total"
@@ -8325,19 +8389,19 @@ msgid "Child issues and epics"
msgstr "Épopées et tickets enfants"
msgid "Chinese language support using"
-msgstr ""
+msgstr "Prise en charge de la langue chinoise en utilisant"
msgid "Choose File..."
-msgstr ""
+msgstr "Choisir un Fichier..."
msgid "Choose a file"
msgstr "Choisir un fichier"
msgid "Choose a group"
-msgstr ""
+msgstr "Choisissez un groupe"
msgid "Choose a template"
-msgstr ""
+msgstr "Choisissez un modèle"
msgid "Choose a template..."
msgstr "Choisir un modèle…"
@@ -8358,7 +8422,7 @@ msgid "Choose the top-level group for your repository imports."
msgstr "Choisissez le groupe de premier niveau pour vos importations dans le dépôt."
msgid "Choose visibility level, enable/disable project features and their permissions, disable email notifications, and show default award emoji."
-msgstr ""
+msgstr "Choisissez le niveau de visibilité, activez/désactivez les fonctionnalités du projet ainsi que ses permissions, désactivez les notifications par courriel et affichez les émojis de récompense par défaut."
msgid "Choose what content you want to see on a group’s overview page."
msgstr "Choisissez le contenu que vous souhaitez voir sur la page d'aperçu de groupe."
@@ -8453,9 +8517,15 @@ msgstr "en cours"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr "Impossible d'utiliser une Variable Masquée avec la valeur actuelle"
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr "Environnements"
+msgid "CiVariables|Expanded"
+msgstr "Développée"
+
msgid "CiVariables|Input variable key"
msgstr "Nom de la variable"
@@ -8468,6 +8538,9 @@ msgstr "Clé"
msgid "CiVariables|Masked"
msgstr "Masquée"
+msgid "CiVariables|Maximum number of variables reached."
+msgstr "Nombre maximum de variables atteint."
+
msgid "CiVariables|Options"
msgstr "Options"
@@ -8480,6 +8553,9 @@ msgstr "Supprimer la variable"
msgid "CiVariables|Remove variable row"
msgstr "Supprimer cette variable"
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr "Portée"
@@ -8489,6 +8565,9 @@ msgstr "Spécifiez des valeurs de variables à utiliser pendant cette exécution
msgid "CiVariables|State"
msgstr "État"
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr "Ce %{entity} a %{currentVariableCount} variables CI/CD définies. Le nombre maximum de variables par %{entity} est de %{maxVariableLimit}. Pour en ajouter de nouvelles, vous devez réduire le nombre de variables définies."
+
msgid "CiVariables|Type"
msgstr "Type"
@@ -8498,6 +8577,12 @@ msgstr "Valeur"
msgid "CiVariables|Variables"
msgstr "Variables"
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr "Les variables stockent des informations, telles que des mots de passe et des clés secrètes, que vous pouvez utiliser dans les scripts de tâches. Chaque %{entity} peut définir un maximum de %{limit} variables."
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr "Vous avez atteint le nombre maximum de variables disponibles. Pour en ajouter de nouvelles, vous devez réduire le nombre de variables définies."
+
msgid "CiVariable|* (All environments)"
msgstr "* (tout environnement)"
@@ -8514,7 +8599,7 @@ msgid "CiVariable|Search environments"
msgstr "Chercher des environnements"
msgid "Classification Label (optional)"
-msgstr ""
+msgstr "Étiquette de classification (facultatif)"
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr "est indisponible : %{reason}"
@@ -8535,7 +8620,7 @@ msgid "Cleanup policy maximum workers running concurrently"
msgstr "Nombre maximal de workers de la politique de nettoyage s'exécutant simultanément"
msgid "Clear"
-msgstr ""
+msgstr "Effacer"
msgid "Clear %{count} image from cache?"
msgid_plural "Clear %{count} images from cache?"
@@ -8663,7 +8748,7 @@ msgid "Cloned this issue to %{path_to_project}."
msgstr "Ce ticket a été cloné vers %{path_to_project}."
msgid "Clones this issue, without comments, to %{project}."
-msgstr ""
+msgstr "Clone ce ticket, sans les commentaires, vers %{project}."
msgid "Close"
msgstr "Fermer"
@@ -8672,10 +8757,10 @@ msgid "Close %{issueType}"
msgstr "Fermer %{issueType}"
msgid "Close %{noteable}"
-msgstr ""
+msgstr "Fermer %{noteable}"
msgid "Close %{tabname}"
-msgstr ""
+msgstr "Fermer %{tabname}"
msgid "Close design"
msgstr "Fermer le design"
@@ -8690,7 +8775,7 @@ msgid "Close sidebar"
msgstr "Fermer la barre latérale"
msgid "Close this %{quick_action_target}"
-msgstr ""
+msgstr "Fermer ce %{quick_action_target}"
msgid "Closed"
msgstr "Fermé"
@@ -8942,7 +9027,7 @@ msgid "ClusterAgents|Agent access token:"
msgstr "Jeton d'accès de l'agent"
msgid "ClusterAgents|Agent might not be connected to GitLab"
-msgstr ""
+msgstr "L'agent n'est peut-être pas connecté à GitLab"
msgid "ClusterAgents|Agent never connected to GitLab"
msgstr "Agent jamais connecté à GitLab"
@@ -8978,7 +9063,7 @@ msgid "ClusterAgents|Are you sure you want to revoke this token? You cannot undo
msgstr "Êtes-vous sûr de vouloir révoquer ce jeton ? Vous ne pouvez pas annuler cette action."
msgid "ClusterAgents|CI/CD workflow with restricted access"
-msgstr ""
+msgstr "Flux CI/CD à accès limité"
msgid "ClusterAgents|Certificate"
msgstr "Certificat"
@@ -9020,7 +9105,7 @@ msgid "ClusterAgents|Create agent access token"
msgstr "Créer le jeton d'accès de l'agent"
msgid "ClusterAgents|Create agent: %{searchTerm}"
-msgstr ""
+msgstr "Créer l'agent : %{searchTerm}"
msgid "ClusterAgents|Create token"
msgstr "Créer un jeton"
@@ -9029,7 +9114,7 @@ msgid "ClusterAgents|Created by"
msgstr "Créé par"
msgid "ClusterAgents|Created by %{name} %{time}"
-msgstr ""
+msgstr "Créé par %{name} %{time}"
msgid "ClusterAgents|Date created"
msgstr "Date de création"
@@ -9044,7 +9129,7 @@ msgid "ClusterAgents|Delete agent"
msgstr "Supprimer l'agent"
msgid "ClusterAgents|Deprecated"
-msgstr ""
+msgstr "Déprécié"
msgid "ClusterAgents|Description"
msgstr "Description"
@@ -9053,7 +9138,7 @@ msgid "ClusterAgents|Event occurred"
msgstr "Un événement s’est produit"
msgid "ClusterAgents|Failed to create a token"
-msgstr ""
+msgstr "Impossible de créer un jeton"
msgid "ClusterAgents|Failed to register an agent"
msgstr "Échec de l'enregistrement d'un agent"
@@ -9068,7 +9153,7 @@ msgid "ClusterAgents|GitLab agent for Kubernetes"
msgstr "Agent GitLab pour Kubernetes"
msgid "ClusterAgents|Give feedback"
-msgstr ""
+msgstr "Donnez votre avis"
msgid "ClusterAgents|How do I register an agent?"
msgstr "Comment enregistrer un agent ?"
@@ -9077,10 +9162,10 @@ msgid "ClusterAgents|How to update an agent?"
msgstr "Comment mettre à jour un agent ?"
msgid "ClusterAgents|Install using Helm (recommended)"
-msgstr ""
+msgstr "Faire l'installation avec Helm (recommandé)"
msgid "ClusterAgents|Integration Status"
-msgstr ""
+msgstr "État de l'intégration"
msgid "ClusterAgents|Last connected %{timeAgo}."
msgstr "Dernière connexion %{timeAgo}."
@@ -9101,7 +9186,7 @@ msgid "ClusterAgents|Never"
msgstr "Jamais"
msgid "ClusterAgents|Never connected"
-msgstr "Jamais connectés"
+msgstr "Jamais connecté"
msgid "ClusterAgents|No activity occurred in the past day"
msgid_plural "ClusterAgents|No activity occurred in the past %d days"
@@ -9130,7 +9215,7 @@ msgid "ClusterAgents|Register"
msgstr "Enregistrer"
msgid "ClusterAgents|Registering agent"
-msgstr ""
+msgstr "Enregistrement de l'agent"
msgid "ClusterAgents|Requires a Maintainer or greater role to delete agents"
msgstr "Nécessite un rôle Mainteneur ou supérieur pour supprimer des agents"
@@ -9151,7 +9236,7 @@ msgid "ClusterAgents|Security"
msgstr "Sécurité"
msgid "ClusterAgents|See agent activity updates, like tokens created or revoked and clusters connected or not connected."
-msgstr ""
+msgstr "Voir les mises à jour d'activité telles que les jetons créés ou révoqués et les grappes de serveurs connectées ou non connectées."
msgid "ClusterAgents|Select an agent or enter a name to create new"
msgstr "Sélectionnez un agent ou entrez un nom pour en créer un nouveau"
@@ -9189,6 +9274,9 @@ msgstr "Jeton révoqué par %{userName}"
msgid "ClusterAgents|Unknown user"
msgstr "Utilisateur inconnu"
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr "Jeton d’accès valide"
@@ -9277,7 +9365,7 @@ msgid "ClusterIntegration|Any project namespaces"
msgstr "Tout espace de noms de projet"
msgid "ClusterIntegration|Apply for credit"
-msgstr ""
+msgstr "Demander un crédit"
msgid "ClusterIntegration|Authentication Error"
msgstr "Erreur d'authentification"
@@ -9319,7 +9407,7 @@ msgid "ClusterIntegration|Cluster name is required."
msgstr "Le nom de la grappe de serveurs est requis."
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters. %{linkStart}More information%{linkEnd}"
-msgstr ""
+msgstr "Les grappes de serveurs utilisées sont sélectionnées d'après l'ancêtre le plus proche dont la portée d'environnement correspond. Par exemple, les grappes de serveurs d'un projet auront la priorité sur celles du groupe. %{linkStart}En savoir plus%{linkEnd}"
msgid "ClusterIntegration|Connect a Kubernetes cluster"
msgstr "Connecter une grappe de serveurs Kubernetes"
@@ -9397,7 +9485,7 @@ msgid "ClusterIntegration|GitLab Integration"
msgstr "Intégration GitLab"
msgid "ClusterIntegration|GitLab failed to authenticate."
-msgstr ""
+msgstr "GitLab n’a pas pu s’authentifier."
msgid "ClusterIntegration|GitLab failed to connect to the cluster."
msgstr "GitLab n’a pas pu se connecter à la grappe de serveurs."
@@ -9418,7 +9506,7 @@ msgid "ClusterIntegration|HTTP Error"
msgstr "Erreur HTTP"
msgid "ClusterIntegration|If you do not wish to delete all associated GitLab resources, you can simply remove the integration."
-msgstr ""
+msgstr "Si vous ne souhaitez pas supprimer toutes les ressources GitLab associées, vous pouvez simplement retirer l'intégration."
msgid "ClusterIntegration|In order to view the health of your cluster, you must first enable Prometheus in the Integrations tab."
msgstr "Afin de visualiser la santé de votre grappe de serveurs, vous devez d’abord activer Prometheus dans l’onglet Intégrations."
@@ -9487,13 +9575,13 @@ msgid "ClusterIntegration|Remove integration"
msgstr "Retirer l’intégration"
msgid "ClusterIntegration|Remove integration and resources"
-msgstr ""
+msgstr "Retirer l'intégration et les ressources"
msgid "ClusterIntegration|Remove integration and resources?"
-msgstr ""
+msgstr "Retirer l'intégration et les ressources ?"
msgid "ClusterIntegration|Remove integration?"
-msgstr ""
+msgstr "Retirer l'intégration ?"
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr "Supprimer la configuration de cette grappe de serveurs Kubernetes de ce projet. Cela ne supprimera pas votre grappe de serveurs Kubernetes actuelle."
@@ -9562,7 +9650,7 @@ msgid "ClusterIntegration|This will permanently delete the following resources:"
msgstr "Cela supprimera définitivement les ressources suivantes :"
msgid "ClusterIntegration|To remove your integration and resources, type %{clusterName} to confirm:"
-msgstr ""
+msgstr "Pour retirer votre intégration et vos ressources, tapez %{clusterName} pour confirmer :"
msgid "ClusterIntegration|To remove your integration, type %{clusterName} to confirm:"
msgstr "Pour supprimer votre intégration, tapez %{clusterName} pour confirmer :"
@@ -9580,7 +9668,7 @@ msgid "ClusterIntegration|Unknown Error"
msgstr "Erreur inconnue"
msgid "ClusterIntegration|Use GitLab to deploy to your cluster, run jobs, use review apps, and more."
-msgstr ""
+msgstr "Utilisez GitLab pour faire des déploiements sur votre grappe de serveurs, exécuter des tâches, utiliser des applications de revue et plus encore."
msgid "ClusterIntegration|Use the %{linkStart}GitLab agent%{linkEnd} to safely connect your Kubernetes clusters to GitLab. You can deploy your applications, run your pipelines, use Review Apps, and much more."
msgstr "Utilisez l'%{linkStart}agent GitLab%{linkEnd} pour connecter en toute sécurité vos grappes de serveurs Kubernetes à GitLab. Vous pouvez déployer vos applications, exécuter vos pipelines, utiliser des Applications de Revue et bien plus encore."
@@ -9592,7 +9680,7 @@ msgid "ClusterIntegration|Where do you want to create a cluster?"
msgstr "Où souhaitez-vous créer une grappe de serveurs ?"
msgid "ClusterIntegration|You are about to remove your cluster integration and all GitLab-created resources associated with this cluster."
-msgstr ""
+msgstr "Vous êtes sur le point de supprimer votre intégration de grappe de serveurs et toutes les ressources créées par GitLab qui lui sont associées."
msgid "ClusterIntegration|You are about to remove your cluster integration."
msgstr "Vous êtes sur le point de retirer l'intégration de votre grappe de serveurs."
@@ -9664,7 +9752,7 @@ msgid "CodeNavigation|No references found"
msgstr "Aucune référence trouvée"
msgid "CodeOwner|Pattern"
-msgstr ""
+msgstr "Motif"
msgid "CodeQuality|New code quality degradations on this line"
msgstr "Nouvelles dégradations de la qualité de code sur cette ligne"
@@ -9682,10 +9770,10 @@ msgid "Cohorts|Registration month"
msgstr "Mois d'inscription"
msgid "Cohorts|Returning users"
-msgstr ""
+msgstr "Utilisateurs récurrents"
msgid "Cohorts|User cohorts are shown for the last %{months_included} months. Only users with activity are counted in the 'New users' column; inactive users are counted separately."
-msgstr ""
+msgstr "Les cohortes d'utilisateurs sont affichées sur les %{months_included} derniers mois. Seuls les utilisateurs avec une activité sont comptés dans la colonne « Nouveaux utilisateurs » ; les utilisateurs inactifs sont comptés séparément."
msgid "Collapse"
msgstr "Réduire"
@@ -9739,7 +9827,7 @@ msgid "ColorWidget|Error fetching epic color."
msgstr "Erreur lors de la récupération de la couleur de l'épopée."
msgid "Colorize messages"
-msgstr ""
+msgstr "Coloriser les messages"
msgid "ComboSearch is not defined"
msgstr "ComboSearch n'est pas défini"
@@ -9754,16 +9842,16 @@ msgid "Command line instructions"
msgstr ""
msgid "Commands applied"
-msgstr ""
+msgstr "Commandes appliquées"
msgid "Commands did not apply"
-msgstr ""
+msgstr "Les commandes n'ont pas été appliquées"
msgid "Comment"
msgstr "Commenter"
msgid "Comment & resolve thread"
-msgstr ""
+msgstr "Commenter et résoudre le fil"
msgid "Comment & unresolve thread"
msgstr ""
@@ -9793,7 +9881,7 @@ msgid "Commenting on files that replace or are replaced by symbolic links is not
msgstr ""
msgid "Commenting on symbolic links that replace or are replaced by files is not supported"
-msgstr ""
+msgstr "Mettre des commentaires sur des liens symboliques qui remplacent des fichiers, ou qui sont remplacés par des fichiers, n'est pas pris en charge"
msgid "Commenting on this line is not supported"
msgstr ""
@@ -9810,13 +9898,13 @@ msgid "Commit %{commit_id}"
msgstr ""
msgid "Commit (when editing commit message)"
-msgstr ""
+msgstr "Valider (lors de l'édition du message de validation)"
msgid "Commit Message"
msgstr "Message du commit"
msgid "Commit SHA"
-msgstr ""
+msgstr "SHA du commit"
msgid "Commit changes"
msgstr "Valider les modifications"
@@ -9972,7 +10060,7 @@ msgid "Complete"
msgstr ""
msgid "Complete verification to sign in."
-msgstr ""
+msgstr "Terminez la vérification pour vous connecter."
msgid "Complete verification to sign up."
msgstr "Terminez la vérification pour vous inscrire."
@@ -10010,6 +10098,9 @@ msgstr "Configuration du pipeline de conformité (facultatif)"
msgid "ComplianceFrameworks|Configuration not found"
msgstr "Configuration introuvable"
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr "Cadre de conformité par défaut mis à jour avec succès"
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr "Supprimer le cadre de conformité %{framework}"
@@ -10034,8 +10125,11 @@ msgstr "Erreur lors de la récupération des données des cadres de conformité.
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr "Erreur lors de la récupération des données des cadres de conformité. Veuillez actualiser la page ou essayer un autre cadre"
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr "Erreur en définissant les cadres de conformité par défaut"
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
-msgstr ""
+msgstr "Les cadres de conformité qui ont été ajoutés apparaîtront ici."
msgid "ComplianceFrameworks|Invalid format"
msgstr "Format non valide"
@@ -10047,17 +10141,26 @@ msgid "ComplianceFrameworks|Name is required"
msgstr "Le nom est requis"
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
-msgstr ""
+msgstr "Aucun cadre de conformité n'est configuré pour l'instant"
+
+msgid "ComplianceFrameworks|Remove default"
+msgstr "Supprimer la valeur par défaut"
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr "Format requis : %{codeStart}chemin/fichier.y[a]ml@nom-groupe/nom-projet%{codeEnd}. %{linkStart}Qu'est-ce-qu'une configuration de pipeline conforme ?%{linkEnd}"
+msgid "ComplianceFrameworks|Set default"
+msgstr "Définir par défaut"
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr "Impossible d'enregistrer ce cadre de conformité. Veuillez réessayer"
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr "Vous êtes sur le point de supprimer définitivement le cadre de conformité %{framework} de tous les projets sur lesquels il est appliqué, ce qui pourrait supprimer d'autres fonctionnalités. Cela ne pourra pas être annulé."
+msgid "ComplianceFrameworks|default"
+msgstr "par défaut"
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr "Ajouter un framework à %{linkStart}%{groupName}%{linkEnd} et il apparaîtra ici."
@@ -10074,19 +10177,19 @@ msgid "ComplianceFramework|New compliance framework"
msgstr "Nouveau cadre de conformité"
msgid "ComplianceFramework|No compliance frameworks are set up yet"
-msgstr ""
+msgstr "Aucun cadre de conformité n'est configuré pour l'instant"
msgid "ComplianceReport|Approved by author"
-msgstr ""
+msgstr "Approuvée par l'auteur"
msgid "ComplianceReport|Approved by committer"
-msgstr ""
+msgstr "Approuvée par le contributeur"
msgid "ComplianceReport|Less than 2 approvers"
msgstr "Moins de 2 approbateurs"
msgid "ComplianceReport|No violations found"
-msgstr ""
+msgstr "Aucune violation trouvée"
msgid "Component"
msgstr "Composant"
@@ -10191,13 +10294,13 @@ msgid "Configure pipeline"
msgstr "Configurer le pipeline"
msgid "Configure pipelines to deploy web apps, backend services, APIs and static resources to Google Cloud"
-msgstr ""
+msgstr "Configurer les pipelines pour déployer sur Google Cloud des applications web, des services backend, des API et des ressources statiques"
msgid "Configure region"
msgstr "Configurer la région"
msgid "Configure region for environment"
-msgstr ""
+msgstr "Configurer la région pour l'environnement"
msgid "Configure regions"
msgstr "Configurer les régions"
@@ -10223,8 +10326,8 @@ msgstr "Configurer des limitations spécifiques pour les requêtes d'API obsolè
msgid "Configure the %{link} integration."
msgstr "Configurez l’intégration de %{link}."
-msgid "Configure the default first day of the week and time tracking units."
-msgstr "Configurer le premier jour par défaut de la semaine et les unités de suivi du temps."
+msgid "Configure the default first day of the week, time tracking units, and default language."
+msgstr "Configurez le premier jour par défaut de la semaine, les unités de suivi du temps et la langue par défaut."
msgid "Configure the way a user creates a new account."
msgstr "Configurez la manière dont une personne crée un nouveau compte."
@@ -10260,7 +10363,7 @@ msgid "Confirm your account"
msgstr ""
msgid "Confirm your email address"
-msgstr ""
+msgstr "Confirmez votre adresse de courriel"
msgid "Confirmation email sent to %{email}"
msgstr "Courriel de confirmation envoyé à %{email}"
@@ -10272,7 +10375,7 @@ msgid "Confirmed at:"
msgstr "Confirmé le :"
msgid "Confirmed:"
-msgstr ""
+msgstr "Confirmé :"
msgid "Conflict: This file was added both in the source and target branches, but with different contents."
msgstr "Conflit : Ce fichier a été ajouté à la fois dans les branches source et cible, mais avec différents contenus."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr "Connexion au service de synchronisation du terminal"
+msgid "Connecting to the remote environment..."
+msgstr "Connexion à l'environnement distant..."
+
msgid "Connecting..."
msgstr "Connexion en cours…"
@@ -10382,12 +10488,6 @@ msgstr "Images du registre de conteneurs"
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr "Le registre de conteneur n'est pas activé sur cette instance GitLab. Demandez à un administrateur de l'activer afin que Auto DevOps puisse fonctionner."
-msgid "Container repositories"
-msgstr "Dépôts de conteneurs"
-
-msgid "Container repository"
-msgstr "Dépôt de conteneurs"
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr "Veuillez aller dans les %{linkStart}paramètres d'administration%{linkEnd} pour activer cette fonction."
@@ -10398,8 +10498,8 @@ msgstr[1] "%{count} dépôts d'images"
msgid "ContainerRegistry|%{count} Tag"
msgid_plural "ContainerRegistry|%{count} Tags"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%{count} Étiquette"
+msgstr[1] "%{count} Étiquettes"
msgid "ContainerRegistry|%{strongStart}Disabled%{strongEnd} - Tags will not be automatically deleted."
msgstr "%{strongStart}Désactivé%{strongEnd} - Les étiquettes ne seront pas supprimées automatiquement."
@@ -10438,7 +10538,7 @@ msgid "ContainerRegistry|Cleanup is disabled for this project"
msgstr "Le nettoyage est désactivé pour ce projet"
msgid "ContainerRegistry|Cleanup is ongoing"
-msgstr ""
+msgstr "Le nettoyage est en cours"
msgid "ContainerRegistry|Cleanup pending"
msgstr "Nettoyage en attente"
@@ -10474,7 +10574,7 @@ msgid "ContainerRegistry|Copy login command"
msgstr "Copier la commande de connexion"
msgid "ContainerRegistry|Copy push command"
-msgstr ""
+msgstr "Copier la commande pour pousser"
msgid "ContainerRegistry|Delete image repository?"
msgstr "Supprimer le dépôt d’images ?"
@@ -10507,7 +10607,7 @@ msgid "ContainerRegistry|Expiration policy will run in %{time}"
msgstr "La stratégie d'expiration sera exécutée dans %{time}"
msgid "ContainerRegistry|If you are not already logged in, you need to authenticate to the Container Registry by using your GitLab username and password. If you have %{twofaDocLinkStart}Two-Factor Authentication%{twofaDocLinkEnd} enabled, use a %{personalAccessTokensDocLinkStart}Personal Access Token%{personalAccessTokensDocLinkEnd} instead of a password."
-msgstr ""
+msgstr "Si vous n'êtes pas encore connecté, vous devez vous authentifier auprès du Registre de Conteneurs à l'aide de votre nom d'utilisateur et de votre mot de passe GitLab. Si vous avez l'%{twofaDocLinkStart}Authentification à Deux Facteurs%{twofaDocLinkEnd} activée, utilisez un %{personalAccessTokensDocLinkStart}Jeton d'Accès Personnel%{personalAccessTokensDocLinkEnd} au lieu d'un mot de passe."
msgid "ContainerRegistry|Image repository deletion failed"
msgstr "La suppression du dépôt d’images a échoué"
@@ -10652,9 +10752,6 @@ msgstr "Étiquette marquée avec succès pour suppression."
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr "Étiquettes marquées avec succès pour suppression."
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr "Les étiquettes ne peuvent temporairement pas être marquées pour suppression. Veuillez réessayer dans quelques minutes. %{docLinkStart}Plus de détails%{docLinkEnd}."
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr "Les étiquettes qui correspondent à ces règles sont %{strongStart}conservées%{strongEnd}, même si elles correspondent à une règle de suppression ci-dessous. L'étiquette %{secondStrongStart}la plus récente%{secondStrongEnd} est toujours conservée."
@@ -10680,7 +10777,7 @@ msgid "ContainerRegistry|The image repository could not be found."
msgstr "Le dépôt d’images n'a pas pu être trouvé."
msgid "ContainerRegistry|The last tag related to this image was recently removed. This empty image and any associated data will be automatically removed as part of the regular Garbage Collection process. If you have any questions, contact your administrator."
-msgstr ""
+msgstr "La dernière étiquette associée à cette image a été récemment supprimée. Cette image vide et toutes les données associées seront automatiquement supprimées dans le cadre du processus normal de ramasse-miettes. Si vous avez des questions, contactez votre administrateur."
msgid "ContainerRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
msgstr "Le dépôt d'images demandé n'existe pas ou a été supprimé. Si vous pensez qu'il s'agit d'une erreur, essayez de rafraîchir la page."
@@ -10722,7 +10819,7 @@ msgid "ContainerRegistry|With the Container Registry, every project can have its
msgstr "Avec le Registre de Conteneurs, chaque projet peut avoir son propre espace pour stocker ses images Docker. %{docLinkStart}En savoir plus%{docLinkEnd}"
msgid "ContainerRegistry|With the Container Registry, every project can have its own space to store its Docker images. Push at least one Docker image in one of this group's projects in order to show up here. %{docLinkStart}More Information%{docLinkEnd}"
-msgstr ""
+msgstr "Avec le Registre de Conteneurs, chaque projet peut avoir son propre espace de stockage pour ses images Docker. Poussez au moins une image Docker dans un des projets de ce groupe afin qu'elle apparaisse ici. %{docLinkStart}En savoir plus%{docLinkEnd}"
msgid "ContainerRegistry|You are about to remove %{item} tags. Are you sure?"
msgstr ""
@@ -10773,7 +10870,7 @@ msgid "Contribution"
msgstr "Contribution"
msgid "Contribution Analytics"
-msgstr ""
+msgstr "Analytique des contributions"
msgid "ContributionAnalytics|%{created} created, %{closed} closed."
msgstr ""
@@ -10782,7 +10879,7 @@ msgid "ContributionAnalytics|%{created} created, %{merged} merged, %{closed} clo
msgstr ""
msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
-msgstr ""
+msgstr "%{pushes}, plus de %{commits} par %{contributors}."
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
msgstr "Données analytiques des contributions pour les tickets, demandes de fusion et événements de poussée depuis %{start_date}"
@@ -10812,7 +10909,7 @@ msgid "ContributionAnalytics|No pushes for the selected time period."
msgstr "Aucune poussée pour la période sélectionnée."
msgid "ContributionAnalytics|The given date range is larger than 31 days"
-msgstr ""
+msgstr "La plage de dates fournie est supérieure à 31 jours"
msgid "ContributionAnalytics|The to date is earlier than the given from date"
msgstr "La date de fin est antérieure à la date de début indiquée"
@@ -10838,17 +10935,14 @@ msgstr "Contrôler les courriels liés à votre compte"
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr "Contrôler la façon dont est utilisée la variable CI/CD CI_JOB_TOKEN CI/CD pour l'accès de l'API entre les projets."
-msgid "Control how the GitLab Package Registry functions."
-msgstr "Contrôlez le fonctionnement du Registre de Paquets de GitLab."
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
-msgstr ""
+msgstr "Contrôlez si vous acceptez ou non d'afficher du contenu d'amélioration de l'expérience client et des offres de tierces parties dans GitLab."
msgid "Cookie domain"
msgstr "Domaine des cookies"
msgid "Copied"
-msgstr ""
+msgstr "Copie effectuée"
msgid "Copied labels and milestone from %{source_issuable_reference}."
msgstr ""
@@ -10857,7 +10951,7 @@ msgid "Copy"
msgstr "Copier"
msgid "Copy %{accessTokenType}"
-msgstr ""
+msgstr "Copier le %{accessTokenType}"
msgid "Copy %{http_label} clone URL"
msgstr "Copier l’URL %{http_label} de clonage"
@@ -10902,7 +10996,7 @@ msgid "Copy commands"
msgstr "Copier les commandes"
msgid "Copy commit SHA"
-msgstr ""
+msgstr "Copier le SHA du commit"
msgid "Copy environment"
msgstr "Copier l'environnement"
@@ -10932,7 +11026,7 @@ msgid "Copy labels and milestone from %{source_issuable_reference}."
msgstr ""
msgid "Copy labels and milestone from other issue or merge request in this project"
-msgstr ""
+msgstr "Copier les étiquettes et les jalons d'un autre ticket ou demande de fusion dans ce projet"
msgid "Copy link"
msgstr "Copier le lien"
@@ -11040,7 +11134,7 @@ msgid "Could not add admins as members"
msgstr "Impossible d'ajouter des administrateurs en tant que membres"
msgid "Could not apply %{name} command."
-msgstr ""
+msgstr "Impossible d'appliquer la commande %{name}."
msgid "Could not apply %{name} command. %{message}."
msgstr "Impossible d'appliquer la commande %{name} . %{message}."
@@ -11061,7 +11155,7 @@ msgid "Could not connect to Sentry. Refresh the page to try again."
msgstr "Impossible de se connecter à Sentry. Actualisez la page pour réessayer."
msgid "Could not connect to Web IDE file mirror service."
-msgstr ""
+msgstr "Impossible de se connecter au service de miroir de fichier de l'EDI Web."
msgid "Could not create Wiki Repository at this time. Please try again later."
msgstr "Impossible de créer le Dépôt du Wiki pour le moment. Veuillez réessayer plus tard."
@@ -11088,7 +11182,7 @@ msgid "Could not delete wiki page"
msgstr "Impossible de supprimer la page wiki"
msgid "Could not draw the lines for job relationships"
-msgstr ""
+msgstr "Impossible de tracer les lignes pour les relations des tâches."
msgid "Could not fetch policy because existing policy YAML is invalid"
msgstr "Impossible de récupérer la stratégie car le YAML de la stratégie existante n'est pas valide"
@@ -11133,10 +11227,10 @@ msgid "Could not save configuration. Please refresh the page, or try again later
msgstr "Impossible d'enregistrer la configuration. Veuillez actualiser la page ou réessayer plus tard."
msgid "Could not save group ID"
-msgstr ""
+msgstr "Impossible d'enregistrer l'ID de groupe"
msgid "Could not save project ID"
-msgstr ""
+msgstr "Impossible d'enregistrer l'ID de projet"
msgid "Could not save prometheus manual configuration"
msgstr "Impossible d'enregistrer la configuration manuelle de Prometheus"
@@ -11148,9 +11242,12 @@ msgid "Could not update wiki page"
msgstr "Impossible de mettre à jour la page wiki"
msgid "Could not upload your designs as one or more files uploaded are not supported."
-msgstr "Impossible de téléverser vos designs car un ou plusieurs fichiers envoyés ne sont pas pris en charge."
+msgstr "Impossible de téléverser vos esquisses car un ou plusieurs fichiers envoyés ne sont pas pris en charge."
msgid "Couldn't assign policy to project or group"
+msgstr "Impossible d'assigner la stratégie au projet ou au groupe"
+
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
msgstr ""
msgid "Country"
@@ -11229,7 +11326,7 @@ msgid "Create a project"
msgstr "Créer un projet"
msgid "Create an account using:"
-msgstr ""
+msgstr "Créez un compte en utilisant :"
msgid "Create an incident. Incidents are created for each alert triggered."
msgstr "Créer un incident. Les incidents sont créés pour chaque alerte déclenchée."
@@ -11285,9 +11382,6 @@ msgstr "Créer un ticket"
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr "Créer une itération"
-
msgid "Create label"
msgstr "Créer une étiquette"
@@ -11346,7 +11440,7 @@ msgid "Create or import your first project"
msgstr "Créez ou importez votre premier projet"
msgid "Create project"
-msgstr ""
+msgstr "Créer le projet"
msgid "Create project label"
msgstr "Créer une étiquette de projet"
@@ -11366,6 +11460,9 @@ msgstr "Créer un extrait de code"
msgid "Create tag %{tagName}"
msgstr "Créer l'étiquette %{tagName}"
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr "Créer le sujet"
@@ -11402,6 +11499,42 @@ msgstr "Vous n'avez pas la permission de créer des groupes."
msgid "CreateTag|Tag"
msgstr "Étiquette"
+msgid "CreateTimelogForm|Add time entry"
+msgstr "Ajouter une entrée de temps"
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr "Une erreur s'est produite lors de l'enregistrement de l'entrée de temps."
+
+msgid "CreateTimelogForm|Cancel"
+msgstr "Annuler"
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr "Exemple : 1h 30m"
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr "Comment puis-je suivre et estimer le temps ?"
+
+msgid "CreateTimelogForm|Save"
+msgstr "Enregistrer"
+
+msgid "CreateTimelogForm|Spent at"
+msgstr "Passé le"
+
+msgid "CreateTimelogForm|Summary"
+msgstr "Résumé"
+
+msgid "CreateTimelogForm|Time spent"
+msgstr "Temps passé"
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr "%{name} (par défaut)"
@@ -11598,7 +11731,7 @@ msgid "Creating epic"
msgstr "Création de l’épopée en cours"
msgid "Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available."
-msgstr ""
+msgstr "La création de graphes utilise les données en provenance du serveur Prometheus. Si cela prend beaucoup de temps, assurez-vous que les données sont disponibles."
msgid "Creator"
msgstr "Créateur"
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr "Carte de crédit :"
+msgid "Critical - S1"
+msgstr "Critique - S1"
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -11733,7 +11869,7 @@ msgid "Current sign-in ip"
msgstr "IP de connexion actuelle"
msgid "Current vulnerabilities count"
-msgstr ""
+msgstr "Nombre actuel de vulnérabilités"
msgid "CurrentUser|Buy Pipeline minutes"
msgstr "Acheter des minutes de pipeline"
@@ -11754,16 +11890,16 @@ msgid "Currently unable to fetch data for this pipeline."
msgstr "Impossible actuellement de récupérer les données pour ce pipeline."
msgid "Custom (%{language})"
-msgstr ""
+msgstr "Personnalisé (%{language})"
msgid "Custom Attributes"
-msgstr ""
+msgstr "Attributs personnalisés"
msgid "Custom Git clone URL for HTTP(S)"
msgstr "URL de clonage Git personnalisée pour HTTP(S)"
msgid "Custom analyzers: language support"
-msgstr ""
+msgstr "Analyseurs personnalisés : prise en charge des langues"
msgid "Custom hostname (for private commit emails)"
msgstr "Nom d’hôte personnalisé (pour les courriels de commit privés)"
@@ -11790,7 +11926,7 @@ msgid "Custom range (UTC)"
msgstr "Plage personnalisée (UTC)"
msgid "Customer experience improvement and third-party offers"
-msgstr ""
+msgstr "Amélioration de l'expérience client et offres de tierces parties"
msgid "Customer relations"
msgstr "Relation Client"
@@ -11811,13 +11947,13 @@ msgid "Customize how FogBugz email addresses and usernames are imported into Git
msgstr "Personnalisez la manière dont les adresses de courriel et les noms d’utilisateur provenant de FogBugz sont importés dans GitLab. À la prochaine étape, vous pourrez sélectionner les projets que vous souhaitez importer."
msgid "Customize icon"
-msgstr ""
+msgstr "Personnaliser l'icône"
msgid "Customize language and region related settings."
msgstr "Personnaliser les paramètres liés à la langue et à la région."
msgid "Customize name"
-msgstr ""
+msgstr "Personnaliser le nom"
msgid "Customize your pipeline configuration."
msgstr "Personnalisez la configuration de vos pipelines."
@@ -11859,7 +11995,7 @@ msgid "CycleAnalyticsEvent|Issue label was added"
msgstr "L'étiquette de ticket a été ajoutée"
msgid "CycleAnalyticsEvent|Issue label was removed"
-msgstr ""
+msgstr "L’étiquette du ticket a été supprimée"
msgid "CycleAnalyticsEvent|Issue last edited"
msgstr ""
@@ -11925,13 +12061,13 @@ msgid "CycleAnalytics|%{selectedLabelsCount} selected (%{maxLabels} max)"
msgstr ""
msgid "CycleAnalytics|'%{name}' is collecting the data. This can take a few minutes."
-msgstr ""
+msgstr "« %{name} » est en train de collecter les données. Cela peut prendre quelques minutes."
msgid "CycleAnalytics|Average time to completion"
msgstr ""
msgid "CycleAnalytics|Average time to completion (days)"
-msgstr ""
+msgstr "Temps moyen jusqu'à achèvement (en jours)"
msgid "CycleAnalytics|Change Failure Rate"
msgstr "Taux d’échec des changements"
@@ -11958,13 +12094,13 @@ msgid "CycleAnalytics|If you have recently upgraded to GitLab Premium, it can ta
msgstr "Si vous avez récemment mis à niveau vers GitLab Premium, cela peut prendre jusqu'à 30 minutes pour que les données soient collectées et affichées."
msgid "CycleAnalytics|Lead Time for Changes"
-msgstr "Délai de mise à disposition pour les Modifications"
+msgstr "Délai d'Exécution des Changements"
msgid "CycleAnalytics|Number of tasks"
msgstr "Nombre de tâches"
msgid "CycleAnalytics|Only %{maxLabels} labels can be selected at this time"
-msgstr ""
+msgstr "Seules %{maxLabels} étiquettes peuvent être sélectionnées pour le moment"
msgid "CycleAnalytics|Project selected"
msgid_plural "CycleAnalytics|%d projects selected"
@@ -11999,7 +12135,7 @@ msgid "CycleAnalytics|There is no data for 'Total time' available. Adjust the cu
msgstr "Il n'y a aucune donnée disponible pour « Durée totale ». Ajustez les filtres actuels."
msgid "CycleAnalytics|Time to Restore Service"
-msgstr "Temps de restauration du service"
+msgstr "Délai de restauration de service"
msgid "CycleAnalytics|Total time"
msgstr "Durée totale"
@@ -12008,7 +12144,7 @@ msgid "CycleAnalytics|group dropdown filter"
msgstr ""
msgid "CycleAnalytics|not allowed for the given start event"
-msgstr ""
+msgstr "n'est pas autorisé pour l'événement de début donné"
msgid "CycleAnalytics|project dropdown filter"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr "%{startDate} - %{endDate}"
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr "Moyenne (%{days} derniers jours)"
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr "Taux d’échec des changements"
+
msgid "DORA4Metrics|Change failure rate"
msgstr "Taux d’échec des changements"
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr "Taux d’échec des changements (pourcentage)"
+msgid "DORA4Metrics|Cycle time"
+msgstr "Temps de cycle"
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr "Métriques DORA du groupe %{groupName}"
@@ -12055,11 +12197,23 @@ msgstr "Jours pour un incident ouvert"
msgid "DORA4Metrics|Days from merge to deploy"
msgstr "Jours entre la fusion et le déploiement"
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr "Fréquence de déploiement"
+
msgid "DORA4Metrics|Deployment frequency"
msgstr "Fréquence de déploiement"
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr "Délai d'Exécution des Changements"
+
+msgid "DORA4Metrics|Lead time"
+msgstr "Délai de mise à disposition"
+
msgid "DORA4Metrics|Lead time for changes"
-msgstr "Délai d'exécution des modifications"
+msgstr "Délai d'exécution des changements"
msgid "DORA4Metrics|Lead time for changes (median days)"
msgstr "Délai d'exécution des changements (jours médians)"
@@ -12076,6 +12230,9 @@ msgstr "Durée médiane d'ouverture d'un incident dans un environnement de produ
msgid "DORA4Metrics|Month to date"
msgstr "Mois en cours"
+msgid "DORA4Metrics|New issues"
+msgstr "Nouveaux tickets"
+
msgid "DORA4Metrics|No incidents during this period"
msgstr "Aucun incident au cours de cette période"
@@ -12101,7 +12258,7 @@ msgid "DORA4Metrics|Something went wrong while getting lead time data."
msgstr "Une erreur s'est produite lors de l'obtention des données sur le délai de mise à disposition."
msgid "DORA4Metrics|Something went wrong while getting time to restore service data."
-msgstr "Une erreur s'est produite lors de l'obtention des données sur le temps jusqu'à la remise en service."
+msgstr "Une erreur s'est produite lors de l'obtention des données sur le délai de restauration de service."
msgid "DORA4Metrics|The chart displays the frequency of deployments to production environment(s) that are based on the %{linkStart}deployment_tier%{linkEnd} value."
msgstr "Le graphique affiche la fréquence des déploiements vers le(s) environnement(s) de production basé(s) sur la valeur %{linkStart}deployment_tier%{linkEnd}."
@@ -12109,11 +12266,14 @@ msgstr "Le graphique affiche la fréquence des déploiements vers le(s) environn
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 "Le graphique affiche le temps médian entre la fusion d'une demande de fusion et son déploiements vers le(s) environnement(s) de production basé(s) sur la valeur %{linkStart}deployment_tier%{linkEnd}."
+msgid "DORA4Metrics|Time to Restore Service"
+msgstr "Délai de restauration de service"
+
msgid "DORA4Metrics|Time to restore service"
-msgstr "Temps jusqu'à la remise en service"
+msgstr "Délai de restauration de service"
msgid "DORA4Metrics|Time to restore service (median days)"
-msgstr "Temps jusqu'à la remise en service (jours médians)"
+msgstr "Délai de restauration de service (jours médians)"
msgid "DSN"
msgstr ""
@@ -12131,7 +12291,7 @@ msgid "DashboardProjects|Personal"
msgstr "Personnels"
msgid "DashboardProjects|Trending"
-msgstr ""
+msgstr "Tendances"
msgid "Dashboards"
msgstr "Tableaux de bord"
@@ -12140,10 +12300,10 @@ msgid "Dashboard|%{firstProject} and %{secondProject}"
msgstr "%{firstProject} et %{secondProject}"
msgid "Dashboard|%{firstProject}, %{rest}, and %{secondProject}"
-msgstr ""
+msgstr "%{firstProject}, %{rest} et %{secondProject}"
msgid "Dashboard|Unable to add %{invalidProjects}. This dashboard is available for public projects, and private projects in groups with a Premium plan."
-msgstr ""
+msgstr "Impossible d'ajouter %{invalidProjects}. Ce tableau de bord est disponible pour les projets publics et pour les projets privés qui se trouvent dans des groupes avec un forfait Premium."
msgid "DastConfig|Customize DAST settings to suit your requirements. Configuration changes made here override those provided by GitLab and are excluded from updates. For details of more advanced configuration options, see the %{docsLinkStart}GitLab DAST documentation%{docsLinkEnd}."
msgstr "Personnalisez la configuration DAST pour répondre à vos besoins. Les paramètres modifiés ici remplacent ceux fournis par GitLab et sont exclus des mises à jour. Pour les détails sur les options de configuration plus avancées, voir la %{docsLinkStart}documentation de GitLab sur DAST%{docsLinkEnd}."
@@ -12169,6 +12329,9 @@ msgstr "Aucune analyse antérieure trouvée pour ce projet"
msgid "DastConfig|Not enabled"
msgstr "Non activé"
+msgid "DastProfiles|/graphql"
+msgstr "/graphql"
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr "Une analyse passive surveille tous les messages HTTP (requêtes et réponses) envoyés à la cible. Une analyse active attaque la cible pour trouver des vulnérabilités potentielles."
@@ -12316,6 +12479,9 @@ msgstr "Minimum = 1 seconde, Maximum = 3600 secondes"
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr "Surveille toutes les requêtes HTTP envoyées vers la cible pour trouver des vulnérabilités potentielles."
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr "Nouveau profil de scanner"
@@ -12637,7 +12803,7 @@ msgid "DatadogIntegration|Trace your GitLab pipelines with Datadog."
msgstr "Tracer vos pipelines GitLab avec Datadog."
msgid "DatadogIntegration|have an invalid format"
-msgstr ""
+msgstr "a un format non valide"
msgid "Datasource name not found"
msgstr "Nom de la source de données introuvable"
@@ -12676,7 +12842,7 @@ msgid "DayTitle|S"
msgstr ""
msgid "DayTitle|W"
-msgstr ""
+msgstr "M"
msgid "Days"
msgstr "Jours"
@@ -12685,6 +12851,9 @@ msgid "Days of inactivity before deactivation"
msgstr "Jours d'inactivité avant désactivation"
msgid "Days to merge"
+msgstr "Jours jusqu'à la fusion"
+
+msgid "Deactivate"
msgstr ""
msgid "Deactivate dormant users after a period of inactivity"
@@ -12744,8 +12913,14 @@ msgstr "Premier jour de la semaine par défaut"
msgid "Default first day of the week in calendars and date pickers."
msgstr "Le premier jour de la semaine par défaut dans les calendriers et les sélecteurs de date."
+msgid "Default language"
+msgstr "Langue par défaut"
+
+msgid "Default language for users who are not logged in."
+msgstr "Langue par défaut pour les utilisateurs qui ne sont pas connectés."
+
msgid "Default projects limit"
-msgstr ""
+msgstr "Limite de projets par défaut"
msgid "Default timeout"
msgstr "Délai d'expiration par défaut"
@@ -12754,7 +12929,7 @@ msgid "Default: Map a FogBugz account ID to a full name"
msgstr "Par défaut : associer un identifiant de compte FogBugz à un nom complet"
msgid "DefaultBranchLabel|default"
-msgstr ""
+msgstr "branche par défaut"
msgid "DefaultBranchProtection|Both developers and maintainers can push new commits, but cannot force push."
msgstr "Les développeurs et les mainteneurs peuvent pousser de nouveaux commits, mais pas forcer les poussées."
@@ -12763,7 +12938,7 @@ msgid "DefaultBranchProtection|Both developers and maintainers can push new comm
msgstr "Les développeurs et les mainteneurs peuvent pousser de nouveaux commits, forcer les poussées ou supprimer la branche."
msgid "DefaultBranchProtection|Developers cannot push new commits, but are allowed to accept merge requests to the branch. Maintainers can push to the branch."
-msgstr ""
+msgstr "Les développeurs ne peuvent pas pousser de nouveaux commits, mais sont autorisés à accepter des demandes de fusion vers la branche. Les mainteneurs peuvent pousser vers la branche."
msgid "DefaultBranchProtection|Developers cannot push new commits, but maintainers can. No one can force push."
msgstr "Les développeurs ne peuvent pas pousser de nouveaux commits, mais les mainteneurs le peuvent. Ni les uns ni les autres ne peuvent forcer les poussées."
@@ -12802,7 +12977,7 @@ msgid "Definition"
msgstr "Définition"
msgid "Delay 2FA enforcement (hours)"
-msgstr ""
+msgstr "Délai avant d'imposer l'A2F (heures)"
msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr "Êtesâ€vous sûr(e) de vouloir exécuter %{jobName} immédiatement ? Dans le cas contraire, cette tâche sera automatiquement exécutée à l’heure programmée."
@@ -12829,10 +13004,10 @@ msgid "Delete"
msgstr "Supprimer"
msgid "Delete %{issuableType}"
-msgstr ""
+msgstr "Supprimer %{issuableType}"
msgid "Delete %{issuableType}?"
-msgstr ""
+msgstr "Supprimer %{issuableType} ?"
msgid "Delete %{name}"
msgstr "Supprimer %{name}"
@@ -12892,25 +13067,25 @@ msgid "Delete image"
msgstr "Supprimer l'image"
msgid "Delete image repository"
-msgstr ""
+msgstr "Supprimer le dépôt d'images"
msgid "Delete internal note"
-msgstr ""
+msgstr "Supprimer la note interne"
msgid "Delete label"
-msgstr ""
+msgstr "Supprimer l'étiquette"
msgid "Delete label: %{labelName}"
msgstr "Suppression d'étiquette : %{labelName}"
msgid "Delete pipeline"
-msgstr ""
+msgstr "Supprimer le pipeline"
msgid "Delete pipeline schedule"
-msgstr ""
+msgstr "Supprimer la planification de pipeline"
msgid "Delete project"
-msgstr ""
+msgstr "Supprimer le projet"
msgid "Delete release"
msgstr "Supprimer la version"
@@ -12931,13 +13106,13 @@ msgid "Delete snippet?"
msgstr "Supprimer l’extrait ?"
msgid "Delete source branch"
-msgstr ""
+msgstr "Supprimer la branche source"
msgid "Delete source branch when merge request is accepted."
msgstr "Supprimer la branche source lorsque la demande de fusion est acceptée."
msgid "Delete subscription"
-msgstr ""
+msgstr "Supprimer l'abonnement"
msgid "Delete table"
msgstr "Supprimer le tableau"
@@ -12970,10 +13145,10 @@ msgid "DeleteProject|Failed to remove project snippets. Please try again or cont
msgstr "Impossible de supprimer les extraits du projet. Veuillez réessayer ou contacter l’administrateur."
msgid "DeleteProject|Failed to remove some tags in project container registry. Please try again or contact administrator."
-msgstr ""
+msgstr "Échec de la suppression de certaines étiquettes dans le registre de conteneurs du projet. Veuillez réessayer ou contacter l'administrateur."
msgid "DeleteProject|Failed to remove webhooks. Please try again or contact administrator."
-msgstr "Impossible de supprimer les webhooks. Veuillez réessayer ou contacter l'administrateur."
+msgstr "Impossible de supprimer les crochets web. Veuillez réessayer ou contacter l'administrateur."
msgid "DeleteProject|Failed to remove wiki repository. Please try again or contact administrator."
msgstr "Échec de la suppression du dépôt du wiki. Veuillez réessayer ou contacter l'administrateur."
@@ -13063,7 +13238,7 @@ msgid "Deny"
msgstr "Refuser"
msgid "Deny access request"
-msgstr ""
+msgstr "Refuser la demande d'accès"
msgid "Dependencies"
msgstr "Dépendances"
@@ -13073,8 +13248,8 @@ msgstr ""
msgid "Dependencies|%d additional vulnerability not shown"
msgid_plural "Dependencies|%d additional vulnerabilities not shown"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d vulnérabilité supplémentaire non affichée"
+msgstr[1] "%d vulnérabilités supplémentaires non affichées"
msgid "Dependencies|%d more"
msgid_plural "Dependencies|%d more"
@@ -13093,7 +13268,7 @@ msgid "Dependencies|(top level)"
msgstr "(premier niveau)"
msgid "Dependencies|All"
-msgstr ""
+msgstr "Toutes"
msgid "Dependencies|Component"
msgstr "Composant"
@@ -13108,7 +13283,7 @@ msgid "Dependencies|Export as JSON"
msgstr "Exporter en JSON"
msgid "Dependencies|Job failed to generate the dependency list"
-msgstr ""
+msgstr "La tâche n'a pas réussi à générer la liste des dépendances"
msgid "Dependencies|Learn more about dependency paths"
msgstr "En savoir plus sur les chemins de dépendance"
@@ -13123,13 +13298,13 @@ msgid "Dependencies|Location and dependency path"
msgstr "Emplacement et chemin de dépendance"
msgid "Dependencies|Packager"
-msgstr ""
+msgstr "Empaqueteur"
msgid "Dependencies|Software Bill of Materials (SBOM) based on the %{linkStart}latest successful%{linkEnd} scan"
msgstr "Nomenclature logicielle (SBOM) basée sur l'analyse %{linkStart}réussie la plus récente%{linkEnd}"
msgid "Dependencies|The %{codeStartTag}dependency_scanning%{codeEndTag} job has failed and cannot generate the list. Please ensure the job is running properly and run the pipeline again."
-msgstr ""
+msgstr "La tâche %{codeStartTag}dependency_scanning%{codeEndTag} a échoué et ne peut pas générer la liste. Assurez-vous que la tâche s'exécute correctement et réexécutez le pipeline."
msgid "Dependencies|The component dependency path is based on the lock file. There may be several paths. In these cases, the longest path is displayed."
msgstr "Le chemin de dépendance du composant est basé sur le fichier verrou. Il peut y avoir plusieurs chemins. Dans ce cas, c'est le plus long qui est affiché."
@@ -13138,7 +13313,7 @@ msgid "Dependencies|There may be multiple paths"
msgstr "Il peut y avoir plusieurs chemins d'accès"
msgid "Dependencies|Toggle vulnerability list"
-msgstr ""
+msgstr "Afficher/masquer la liste des vulnérabilités"
msgid "Dependencies|Unsupported file(s) detected"
msgstr ""
@@ -13165,7 +13340,7 @@ msgid "DependencyProxy|All items in the cache are scheduled for removal."
msgstr "Tous les éléments du cache sont planifiés pour suppression."
msgid "DependencyProxy|Cached %{time}"
-msgstr ""
+msgstr "Mis en cache %{time}"
msgid "DependencyProxy|Clear cache"
msgstr "Vider le cache"
@@ -13225,13 +13400,13 @@ msgid "Deploy Key"
msgstr "Clé de Déploiement"
msgid "Deploy Keys"
-msgstr "Clefs de déploiement"
+msgstr "Clés de déploiement"
msgid "Deploy Token"
msgstr ""
msgid "Deploy container based web apps on Google managed clusters"
-msgstr ""
+msgstr "Déployer des applications Web basées sur des conteneurs dans des grappes de serveurs gérées par Google"
msgid "Deploy freezes"
msgstr "Gels de déploiement"
@@ -13255,7 +13430,7 @@ msgid "Deploy to..."
msgstr ""
msgid "DeployBoards|To see deployment progress for your environments, make sure you are deploying to %{codeStart}$KUBE_NAMESPACE%{codeEnd} and annotating with %{codeStart}app.gitlab.com/app=$CI_PROJECT_PATH_SLUG%{codeEnd} and %{codeStart}app.gitlab.com/env=$CI_ENVIRONMENT_SLUG%{codeEnd}."
-msgstr ""
+msgstr "Pour voir la progression du déploiement de vos environnements, assurez-vous que vous déployez sur %{codeStart}$KUBE_NAMESPACE%{codeEnd} et annotez avec %{codeStart}app.gitlab.com/app=$CI_PROJECT_PATH_SLUG%{codeEnd} et %{codeStart}app.gitlab.com/env=$CI_ENVIRONMENT_SLUG%{codeEnd}."
msgid "DeployBoard|Kubernetes Pods"
msgstr "Pods Kubernetes"
@@ -13303,19 +13478,19 @@ msgid "DeployKeys|Current project"
msgstr "Projet actuel"
msgid "DeployKeys|Deploy key"
-msgstr "Clef de déploiement"
+msgstr "Clé de déploiement"
msgid "DeployKeys|Enabled deploy keys"
-msgstr "Clefs de déploiement activées"
+msgstr "Clés de déploiement activées"
msgid "DeployKeys|Error enabling deploy key"
-msgstr "Erreur lors de l’activation de la clef de déploiement"
+msgstr "Erreur lors de l’activation de la clé de déploiement"
msgid "DeployKeys|Error getting deploy keys"
-msgstr "Erreur lors de l’obtention des clefs de déploiement"
+msgstr "Erreur lors de l’obtention des clés de déploiement"
msgid "DeployKeys|Error removing deploy key"
-msgstr "Erreur lors de la suppression de la clef de déploiement"
+msgstr "Erreur lors de la suppression de la clé de déploiement"
msgid "DeployKeys|Expand %{count} other projects"
msgstr "Afficher %{count} autres projets"
@@ -13324,25 +13499,25 @@ msgid "DeployKeys|Grant write permissions to this key"
msgstr "Accorder des autorisations d'écriture à cette clé"
msgid "DeployKeys|Loading deploy keys"
-msgstr "Chargement des clefs de déploiement"
+msgstr "Chargement des clés de déploiement"
msgid "DeployKeys|No deploy keys found. Create one with the form above."
-msgstr "Aucune clef de déploiement trouvée. Créezâ€en une avec le formulaire ciâ€dessous."
+msgstr "Aucune clé de déploiement trouvée. Créez-en une avec le formulaire ci-dessous."
msgid "DeployKeys|Privately accessible deploy keys"
-msgstr "Clefs de déploiement à accès privé"
+msgstr "Clés de déploiement à accès privé"
msgid "DeployKeys|Project usage"
msgstr "À l’usage du projet"
msgid "DeployKeys|Publicly accessible deploy keys"
-msgstr "Clefs de déploiement à accès publique"
+msgstr "Clés de déploiement à accès public"
msgid "DeployKeys|Read access only"
msgstr "Accès en lecture seule"
msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
-msgstr "Jetons de déploiement actifs : (%{active_tokens})"
+msgstr "Jetons de déploiement actifs (%{active_tokens})"
msgid "DeployTokens|Allows read and write access to registry images."
msgstr "Autorise l'accès en lecture et écriture sur les images du registre."
@@ -13423,7 +13598,7 @@ msgid "DeployTokens|Scopes (select at least one)"
msgstr "Portées (en sélectionner au moins une)"
msgid "DeployTokens|This %{entity_type} has no active Deploy Tokens."
-msgstr ""
+msgstr "Ce %{entity_type} n’a aucun jeton de déploiement actif."
msgid "DeployTokens|This action cannot be undone."
msgstr "Cette action ne peut pas être annulée."
@@ -13453,22 +13628,22 @@ msgid "DeployTokens|Your new project deploy token has been created."
msgstr "Votre nouveau jeton de déploiement pour votre projet a été créé."
msgid "Deployed"
-msgstr ""
+msgstr "Déployé"
msgid "Deployed to"
msgstr "Déployé sur"
msgid "Deployed-after"
-msgstr ""
+msgstr "Déployé-après"
msgid "Deployed-before"
-msgstr ""
+msgstr "Déployé-avant"
msgid "Deploying to"
msgstr "En cours de déploiement sur"
msgid "Deploying to AWS is easy with GitLab"
-msgstr ""
+msgstr "Le déploiement sur AWS est facile avec GitLab"
msgid "Deployment"
msgstr "Déploiement"
@@ -13483,7 +13658,7 @@ msgid "Deployment Target|Project deployment target (optional)"
msgstr "Cible du déploiement du projet (facultative)"
msgid "Deployment Target|Select the deployment target"
-msgstr ""
+msgstr "Sélectionnez la cible du déploiement"
msgid "Deployment frequency"
msgstr "Fréquence de déploiement"
@@ -13492,7 +13667,7 @@ msgid "DeploymentApprovals|Approvals"
msgstr "Approbations"
msgid "DeploymentApprovals|Approved By"
-msgstr ""
+msgstr "Approuvé par"
msgid "DeploymentApprovals|Approvers"
msgstr "Approbateurs"
@@ -13519,10 +13694,10 @@ msgid "DeploymentApproval|Approved by you %{time}"
msgstr "Approuvé par vous %{time}"
msgid "DeploymentApproval|Approving will run the manual job from deployment #%{deploymentIid}. Rejecting will fail the manual job."
-msgstr ""
+msgstr "Approuver exécutera la tâche manuelle depuis le déploiement #%{deploymentIid}. Rejeter fera échouer la tâche manuelle."
msgid "DeploymentApproval|Deployment tier: %{tier}"
-msgstr ""
+msgstr "Étape du déploiement : %{tier}"
msgid "DeploymentApproval|Environment: %{environment}"
msgstr "Environnement : %{environment}"
@@ -13536,6 +13711,9 @@ msgstr "Rejeté %{time}"
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr "Rejeté par vous %{time}"
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr "Informatique en périphérie (p. ex. Cloudflare Workers)"
+
msgid "DeploymentTarget|GitLab Pages"
msgstr "GitLab Pages"
@@ -13549,7 +13727,7 @@ msgid "DeploymentTarget|Kubernetes (GKE, EKS, OpenShift, and so on)"
msgstr "Kubernetes (GKE, EKS, OpenShift etc.)"
msgid "DeploymentTarget|Managed container runtime (Fargate, Cloud Run, DigitalOcean App)"
-msgstr ""
+msgstr "Environnement d'exécution de conteneur géré (Fargate, Cloud Run, DigitalOcean App)"
msgid "DeploymentTarget|Mobile app store"
msgstr "Magasin d'applications mobiles"
@@ -13564,7 +13742,7 @@ msgid "DeploymentTarget|Registry (package or container)"
msgstr "Registre (paquet ou conteneur)"
msgid "DeploymentTarget|Self-managed container runtime (Podman, Docker Swarm, Docker Compose)"
-msgstr ""
+msgstr "Environnement d'exécution de conteneur autogéré (Podman, Docker Swarm, Docker Compose)"
msgid "DeploymentTarget|Serverless backend (Lambda, Cloud functions)"
msgstr "Backend Serverless (fonctions Lambda, Cloud)"
@@ -13572,6 +13750,9 @@ msgstr "Backend Serverless (fonctions Lambda, Cloud)"
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr "Machine virtuelle (par exemple, EC2)"
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr "Plateforme de Déploiement Web (Netlify, Vercel, Gatsby)"
+
msgid "Deployments"
msgstr "Déploiements"
@@ -13641,13 +13822,13 @@ msgid "Deployment|success"
msgstr "réussi"
msgid "Deprecated API rate limits"
-msgstr ""
+msgstr "Limitations de fréquence des API dépréciées"
msgid "Deprecations|For information on a possible replacement %{epicStart} learn more about Opstrace %{epicEnd}."
-msgstr ""
+msgstr "Pour plus d'informations sur un possible remplacement, %{epicStart} en savoir plus sur Opstrace %{epicEnd}."
msgid "Deprecations|The metrics feature was deprecated in GitLab 14.7."
-msgstr ""
+msgstr "La fonctionnalité des métriques est obsolète depuis 14.7."
msgid "Deprioritize label"
msgstr "Déprioriser l’étiquette"
@@ -13656,7 +13837,7 @@ msgid "Descending"
msgstr "Décroissant"
msgid "Describe the goal of the changes and what reviewers should be aware of."
-msgstr ""
+msgstr "Décrivez l'objectif des modifications et ce que les relecteurs doivent savoir."
msgid "Description"
msgstr "Description"
@@ -13668,7 +13849,7 @@ msgid "Description (optional)"
msgstr "Description (facultative)"
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
-msgstr ""
+msgstr "Description analysée avec %{link_start}GitLab Flavored Markdown%{link_end}"
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}."
msgstr "Description analysée avec %{link_start}GitLab Flavored Markdown%{link_end}."
@@ -13676,11 +13857,17 @@ msgstr "Description analysée avec %{link_start}GitLab Flavored Markdown%{link_e
msgid "Description:"
msgstr "Description :"
+msgid "Descriptions"
+msgstr "Descriptions"
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr "Esquisse"
+
msgid "Design Management files and data"
-msgstr ""
+msgstr "Fichiers et données de la Gestion des Esquisses"
msgid "Design repositories"
msgstr "Dépôts de design"
@@ -13689,7 +13876,7 @@ msgid "Design repository"
msgstr "Dépôt de design"
msgid "DesignManagement|%{current_design} of %{designs_count}"
-msgstr ""
+msgstr "%{current_design} sur %{designs_count}"
msgid "DesignManagement|%{filename} did not change."
msgid_plural "DesignManagement|The designs you tried uploading did not change."
@@ -13697,22 +13884,22 @@ msgstr[0] "%{filename} n'a pas changé."
msgstr[1] "Les designs que vous avez essayé de téléverser n'ont pas changé."
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
-msgstr ""
+msgstr "L'ajout d'une esquisse avec le même nom de fichier remplace le fichier par une nouvelle version."
msgid "DesignManagement|Archive design"
msgstr "Archiver le design"
msgid "DesignManagement|Archive designs"
-msgstr "Archiver les designs"
+msgstr "Archiver les esquisses"
msgid "DesignManagement|Archive selected"
msgstr "Archiver la sélection"
msgid "DesignManagement|Archived designs will still be available in previous versions of the design collection."
-msgstr "Les designs archivés resteront disponibles dans les versions précédentes de la collection de designs."
+msgstr "Les esquisses archivées resteront disponibles dans les versions précédentes de la collection des esquisses."
msgid "DesignManagement|Are you sure you want to archive the selected designs?"
-msgstr "Êtes-vous sûr(e) de vouloir archiver les designs sélectionnés ?"
+msgstr "Êtes-vous sûr(e) de vouloir archiver les esquisses sélectionnées ?"
msgid "DesignManagement|Are you sure you want to cancel creating this comment?"
msgstr "Êtes-vous sûr de vouloir annuler la création de ce commentaire ?"
@@ -13742,13 +13929,13 @@ msgid "DesignManagement|Could not update discussion. Please try again."
msgstr "Impossible de mettre à jour la discussion. Veuillez réessayer."
msgid "DesignManagement|Could not update note. Please try again."
-msgstr ""
+msgstr "Impossible de mettre à jour la note. Veuillez réessayer."
msgid "DesignManagement|Deselect all"
msgstr "Tout désélectionner"
msgid "DesignManagement|Designs"
-msgstr "Designs"
+msgstr "Esquisses"
msgid "DesignManagement|Discard changes"
msgstr "Abandonner les modifications"
@@ -13763,7 +13950,7 @@ msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr "Erreur lors du téléversement d'un nouveau design. Veuillez réessayer."
msgid "DesignManagement|Go back to designs"
-msgstr ""
+msgstr "Retour aux esquisses"
msgid "DesignManagement|Go to next design"
msgstr "Aller au design suivant"
@@ -13778,7 +13965,7 @@ msgid "DesignManagement|Resolve thread"
msgstr ""
msgid "DesignManagement|Resolved Comments"
-msgstr ""
+msgstr "Commentaires résolus"
msgid "DesignManagement|Save comment"
msgstr "Enregistrer le commentaire"
@@ -13787,52 +13974,52 @@ msgid "DesignManagement|Select all"
msgstr "Tout sélectionner"
msgid "DesignManagement|Some of the designs you tried uploading did not change: %{skippedFiles} and %{moreCount} more."
-msgstr "Plusieurs des designs que vous avez essayé de téléverser n'ont pas changé : %{skippedFiles} et %{moreCount} de plus."
+msgstr "Plusieurs des esquisses que vous avez essayées de téléverser n'ont pas changé : %{skippedFiles} et %{moreCount} de plus."
msgid "DesignManagement|Some of the designs you tried uploading did not change: %{skippedFiles}."
-msgstr "Certains des designs que vous avez essayé de téléverser n'ont pas changé : %{skippedFiles}."
+msgstr "Plusieurs des esquisses que vous avez essayées de téléverser n'ont pas changé : %{skippedFiles}."
msgid "DesignManagement|The maximum number of designs allowed to be uploaded is %{upload_limit}. Please try again."
-msgstr "Le nombre maximum de designs pouvant être téléversés est de %{upload_limit}. Veuillez réessayer."
+msgstr "Le nombre maximum d'esquisses pouvant être téléversées est de %{upload_limit}. Veuillez réessayer."
msgid "DesignManagement|There was an error moving your designs. Please upload your designs below."
-msgstr "Une erreur s’est produite lors du déplacement de vos designs. Veuillez téléverser vos designs ci-dessous."
+msgstr "Une erreur s’est produite lors du déplacement de vos esquisses. Veuillez les téléverser ci-dessous."
msgid "DesignManagement|To upload designs, you'll need to enable LFS and have an admin enable hashed storage. %{requirements_link_start}More information%{requirements_link_end}"
-msgstr "Pour téléverser des designs, il est nécessaire d'activer LFS et que l'administrateur ait activé le stockage haché. %{requirements_link_start}Plus d'informations%{requirements_link_end}"
+msgstr "Pour téléverser des esquisses, il est nécessaire d'activer LFS et que l'administrateur ait activé le stockage haché. %{requirements_link_start}En savoir plus%{requirements_link_end}"
msgid "DesignManagement|Unresolve thread"
msgstr ""
msgid "DesignManagement|Upload designs"
-msgstr "Téléverser des designs"
+msgstr "Téléverser des esquisses"
msgid "DesignManagement|Upload skipped. %{reason}"
msgstr "Téléversement ignoré. %{reason}"
msgid "DesignManagement|Your designs are being copied and are on their way… Please refresh to update."
-msgstr "Vos designs sont en cours de copie et sont en route… Veuillez actualiser pour mettre à jour."
+msgstr "Vos esquisses sont en cours de copie et sont en route… Veuillez actualiser pour mettre à jour."
msgid "Designs"
-msgstr "Designs"
+msgstr "Esquisses"
msgid "Destroy"
msgstr "Détruire"
msgid "Detail"
-msgstr ""
+msgstr "Détail"
msgid "Details"
msgstr "Détails"
msgid "Details (default)"
-msgstr ""
+msgstr "Détails (par défaut)"
msgid "Details block"
msgstr "Bloc de détails"
msgid "Detect host keys"
-msgstr "Détecter les clefs de l’hôte"
+msgstr "Détecter les clés de l’hôte"
msgid "DevOps Adoption"
msgstr "Adoption DevOps"
@@ -13843,6 +14030,9 @@ msgstr "Rapports DevOps"
msgid "DevOps adoption"
msgstr "Adoption de DevOps"
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr "Développeur"
@@ -13949,7 +14139,7 @@ msgid "DevopsAdoption|Issues"
msgstr "Tickets"
msgid "DevopsAdoption|MRs"
-msgstr ""
+msgstr "Demandes de fusion"
msgid "DevopsAdoption|No results…"
msgstr "Aucun résultat…"
@@ -14126,7 +14316,7 @@ msgid "Direct member"
msgstr "Membre direct"
msgid "Direct members"
-msgstr "Membres direct"
+msgstr "Membres directs"
msgid "Direct non-authenticated users to this page."
msgstr "Diriger les utilisateurs non authentifiés vers cette page."
@@ -14159,13 +14349,13 @@ msgid "Disable two-factor authentication"
msgstr "Désactiver l’authentification à deux facteurs"
msgid "Disabled"
-msgstr ""
+msgstr "Désactivé"
msgid "Disabled by %{parent} owner"
msgstr ""
msgid "Disabled mirrors can only be enabled by instance owners. It is recommended that you delete them."
-msgstr ""
+msgstr "Les miroirs désactivés ne peuvent être activés que par les propriétaires d'instance. Il est recommandé de les supprimer."
msgid "Discard"
msgstr "Abandonner"
@@ -14222,19 +14412,19 @@ msgid "Discover|Upgrade now"
msgstr ""
msgid "Discuss a specific suggestion or question internally that needs to be resolved."
-msgstr ""
+msgstr "Discutez une suggestion ou une question spécifique en interne qui doit être résolue."
msgid "Discuss a specific suggestion or question internally."
-msgstr ""
+msgstr "Discutez une suggestion ou une question spécifique en interne."
msgid "Discuss a specific suggestion or question that needs to be resolved."
-msgstr ""
+msgstr "Discutez une suggestion ou une question spécifique qui doit être résolue."
msgid "Discuss a specific suggestion or question."
-msgstr ""
+msgstr "Discutez une suggestion ou une question spécifique."
msgid "Discussion to reply to cannot be found"
-msgstr ""
+msgstr "La discussion à laquelle répondre est introuvable"
msgid "Disk Usage"
msgstr "Utilisation du disque"
@@ -14275,7 +14465,7 @@ msgid "Dismissed on pipeline %{pipelineLink} at %{projectLink}"
msgstr ""
msgid "Display"
-msgstr ""
+msgstr "Afficher"
msgid "Display alerts from all configured monitoring tools."
msgstr "Affichage des alertes depuis tous les outils de supervision configurés."
@@ -14296,10 +14486,10 @@ msgid "Display source"
msgstr "Afficher la source"
msgid "Display time tracking in issues in total hours only. %{link_start}What is time tracking?%{link_end}"
-msgstr ""
+msgstr "Affichez le suivi du temps des tickets en total d'heures uniquement. %{link_start}Qu'est-ce que le suivi du temps ?%{link_end}"
msgid "Do not display content for customer experience improvement and offers from third parties"
-msgstr ""
+msgstr "Ne pas afficher le contenu pour l'amélioration de l'expérience client ni les offres de tierces parties"
msgid "Do not force push over diverged refs. After the mirror is created, this setting can only be modified using the API. %{mirroring_docs_link_start}Learn more about this option%{link_closing_tag} and %{mirroring_api_docs_link_start}the API.%{link_closing_tag}"
msgstr "Ne pas forcer les poussées vers des références divergentes. Après la création du miroir, ce paramètre ne peut être modifié que via l'API. %{mirroring_docs_link_start}En savoir plus sur cette option%{link_closing_tag} et %{mirroring_api_docs_link_start}sur l'API.%{link_closing_tag}"
@@ -14389,13 +14579,13 @@ msgid "Download artifacts"
msgstr "Télécharger les artéfacts"
msgid "Download codes"
-msgstr ""
+msgstr "Télécharger les codes"
msgid "Download evidence JSON"
msgstr ""
msgid "Download export"
-msgstr ""
+msgstr "Télécharger l'export"
msgid "Download image"
msgstr "Télécharger l'image"
@@ -14437,7 +14627,7 @@ msgid "Drag to reorder prioritized labels and change their relative priority."
msgstr "Faites glisser les étiquettes prioritaires pour les classer et modifier leur priorité relative."
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
-msgstr "Faites glisser vos designs ici ou %{linkStart}cliquez pour les téléverser%{linkEnd}."
+msgstr "Faites glisser vos esquisses ici ou %{linkStart}cliquez pour les téléverser%{linkEnd}."
msgid "Drop or %{linkStart}upload%{linkEnd} file to attach"
msgstr "Déposez ou %{linkStart}téléversez%{linkEnd} un fichier à joindre"
@@ -14446,7 +14636,7 @@ msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
msgstr "Déposez ou %{linkStart}téléversez%{linkEnd} des fichiers à joindre"
msgid "Drop your designs to start your upload."
-msgstr "Déposez vos designs pour démarrer votre téléversement."
+msgstr "Déposez vos esquisses pour démarrer votre téléversement."
msgid "Drop your files to start your upload."
msgstr "Déposez vos fichiers pour commencer le téléversement."
@@ -14461,7 +14651,7 @@ msgid "DropdownWidget|Cancel"
msgstr "Annuler"
msgid "DropdownWidget|Edit %{issuableAttribute}"
-msgstr ""
+msgstr "Modifier %{issuableAttribute}"
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -14566,7 +14756,7 @@ msgid "Edit %{name}"
msgstr ""
msgid "Edit %{profileType} profile"
-msgstr ""
+msgstr "Modifier le profil du %{profileType}"
msgid "Edit Comment"
msgstr "Modifier le commentaire"
@@ -14605,7 +14795,7 @@ msgid "Edit Slack integration"
msgstr "Éditer l'intégration de Slack"
msgid "Edit Snippet"
-msgstr "Modifier le fragment de code"
+msgstr "Modifier l'extrait de code"
msgid "Edit System Hook"
msgstr "Modifier le Crochet Système"
@@ -14620,7 +14810,7 @@ msgid "Edit comment"
msgstr "Modifier le commentaire"
msgid "Edit commit message"
-msgstr ""
+msgstr "Modifier le message de validation"
msgid "Edit deploy freeze"
msgstr "Modifier le gel de déploiement"
@@ -14659,7 +14849,7 @@ msgid "Edit in pipeline editor"
msgstr "Modifier dans l'éditeur de pipeline"
msgid "Edit in single-file editor"
-msgstr ""
+msgstr "Modifier dans l'éditeur de fichier unique"
msgid "Edit inline"
msgstr ""
@@ -14704,7 +14894,7 @@ msgid "Edit wiki page"
msgstr "Modifier la page wiki"
msgid "Edit your most recent comment in a thread (from an empty textarea)"
-msgstr ""
+msgstr "Modifier votre commentaire le plus récent dans un fil de conversation (à partir d'une zone de texte vide)"
msgid "Edit your search and try again"
msgstr "Modifiez votre recherche et réessayez"
@@ -14737,7 +14927,7 @@ msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr "Valeur du délai d'expiration du client HTTP Elasticsearch en secondes."
msgid "Elasticsearch indexing restrictions"
-msgstr ""
+msgstr "Restrictions d'indexation d'Elasticsearch"
msgid "Elasticsearch indexing started"
msgstr "L'indexation Elasticsearch a démarré"
@@ -14776,7 +14966,7 @@ msgid "Email a new %{name} to this project"
msgstr "Cliquez pour une création de %{name} par courriel"
msgid "Email address suffix"
-msgstr ""
+msgstr "Suffixe de l'adresse de courriel"
msgid "Email address to use for Support Desk"
msgstr "Adresse de courriel à utiliser pour le Service d'Assistance"
@@ -14785,10 +14975,10 @@ msgid "Email could not be sent"
msgstr "Le courriel n'a pas pu être envoyé"
msgid "Email display name"
-msgstr ""
+msgstr "Nom à afficher pour le courriel"
msgid "Email not verified. Please verify your email in Salesforce."
-msgstr ""
+msgstr "Courriel non vérifié. Veuillez vérifier votre couriel dans Salesforce."
msgid "Email notification for unknown sign-ins"
msgstr "Notification par courriel pour les connexions inconnues"
@@ -14860,7 +15050,7 @@ msgid "EmailsOnPushService|Disable code diffs"
msgstr "Désactiver les diffs de code"
msgid "EmailsOnPushService|Don't include possibly sensitive code diffs in notification body."
-msgstr ""
+msgstr "Ne pas inclure les diffs de code dans le corps de la notification pour ne pas exposer d'éventuelles données sensibles."
msgid "EmailsOnPushService|Email the commits and diff of each push to a list of recipients."
msgstr "Envoyer par courriel les commits et les diffs de chaque poussée à une liste de destinataires."
@@ -14884,7 +15074,7 @@ msgid "Embed"
msgstr "Embarquer"
msgid "Empty file"
-msgstr ""
+msgstr "Fichier vide"
msgid "Enable"
msgstr "Activer"
@@ -14980,7 +15170,7 @@ msgid "Enable error tracking"
msgstr "Activer le suivi des erreurs"
msgid "Enable feature to choose access level"
-msgstr ""
+msgstr "Activez la fonctionnalité pour choisir le niveau d'accès"
msgid "Enable for this project"
msgstr "Activer pour ce projet"
@@ -15016,13 +15206,13 @@ msgid "Enable or disable version check and Service Ping."
msgstr "Activer ou désactiver le contrôle de version et le Ping de Service."
msgid "Enable rate limiting for POST requests to the specified paths"
-msgstr ""
+msgstr "Activer la limitation de fréquence pour les requêtes POST vers les chemins spécifiés"
msgid "Enable reCAPTCHA"
msgstr "Activer reCAPTCHA"
msgid "Enable reCAPTCHA for login."
-msgstr ""
+msgstr "Activer reCAPTCHA pour se connecter."
msgid "Enable repository checks"
msgstr "Activer les vérifications de dépôt"
@@ -15049,7 +15239,7 @@ msgid "Enable unauthenticated API request rate limit"
msgstr "Activer la limite de fréquence des requêtes d'API non authentifiées"
msgid "Enable unauthenticated web request rate limit"
-msgstr ""
+msgstr "Activer la limite de fréquence des requêtes web non authentifiées"
msgid "Enable user deactivation emails"
msgstr "Activer les courriels de désactivation d'utilisateur"
@@ -15058,13 +15248,13 @@ msgid "Enable version check"
msgstr "Activer le contrôle de version"
msgid "EnableReviewApp|Add a job in your CI/CD configuration that:"
-msgstr "Ajoutez dans votre configuration CI/CD une tâche qui :"
+msgstr "Ajouter dans votre configuration CI/CD une tâche qui :"
msgid "EnableReviewApp|Copy snippet"
msgstr "Copier l'extrait"
msgid "EnableReviewApp|Have access to infrastructure that can host and deploy the review apps."
-msgstr "A accès à l'infrastructure qui peut héberger et déployer les applications de revue."
+msgstr "Avoir accès à l'infrastructure qui peut héberger et déployer les applications de revue."
msgid "EnableReviewApp|Install and configure a runner to do the deployment."
msgstr "Installer et configurer un exécuteur pour effectuer le déploiement."
@@ -15097,7 +15287,7 @@ msgid "Enabled"
msgstr "activé"
msgid "Enabled Git access protocols"
-msgstr ""
+msgstr "Protocoles d'accès Git activés"
msgid "Enabled OAuth authentication sources"
msgstr "Sources d'authentification OAuth activées"
@@ -15121,7 +15311,7 @@ msgid "Enforce two-factor authentication"
msgstr "Imposer l'authentification à deux facteurs"
msgid "Enforce two-factor authentication for all user sign-ins."
-msgstr ""
+msgstr "Imposer l'authentification à deux facteurs pour toutes les connexions des utilisateurs."
msgid "Enhance security by storing service account keys in secret managers - learn more about %{docLinkStart}secret management with GitLab%{docLinkEnd}"
msgstr "Améliorez la sécurité en stockant les clés de compte de service dans des gestionnaires de secrets - apprenez-en plus sur la %{docLinkStart}gestion de secrets avec GitLab%{docLinkEnd}"
@@ -15156,6 +15346,9 @@ msgstr "Entrez une couleur."
msgid "Enter at least three characters to search"
msgstr "Entrez au moins trois caractères pour effectuer la recherche"
+msgid "Enter at least three characters to search."
+msgstr "Entrez au moins trois caractères pour effectuer la recherche."
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr "Entrez l’URL de votre serveur Bitbucket et votre jeton d’accès personnel ciâ€dessous"
@@ -15166,7 +15359,7 @@ msgid "Enter license key"
msgstr "Entrez la clé de licence"
msgid "Enter merge request URLs"
-msgstr ""
+msgstr "Entrez les URL de demande de fusion"
msgid "Enter new AWS Secret Access Key"
msgstr "Entrez la nouvelle Clé d'Accès Secrète AWS"
@@ -15178,10 +15371,10 @@ msgid "Enter one or more user ID separated by commas"
msgstr "Entrez un ou plusieurs ID utilisateur séparés par des virgules"
msgid "Enter the %{name} description"
-msgstr ""
+msgstr "Entrez la description de %{name}"
msgid "Enter the %{name} title"
-msgstr ""
+msgstr "Entrez le titre de %{name}"
msgid "Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes."
msgstr "Entrez le code de l'application d'authentification à deux facteurs sur votre appareil mobile. Si vous avez perdu votre appareil, vous pouvez saisir un de vos codes de récupération."
@@ -15214,7 +15407,7 @@ msgid "Enter your password to approve"
msgstr "Entrez votre mot de passe pour approuver"
msgid "Enterprise"
-msgstr ""
+msgstr "Entreprise"
msgid "Environment"
msgstr "Environnement"
@@ -15241,7 +15434,7 @@ msgid "EnvironmentDashboard|API"
msgstr "API"
msgid "EnvironmentDashboard|Created through the Deployment API"
-msgstr ""
+msgstr "Créé à l'aide de l'API de déploiement"
msgid "EnvironmentDashboard|You are looking at the last updated environment"
msgstr "Vous consultez le dernier environnement actualisé"
@@ -15256,7 +15449,7 @@ msgid "Environments allow you to track deployments of your application. %{linkSt
msgstr "Les environnements vous permettent de suivre les déploiements de votre application. %{linkStart}Plus d'informations%{linkEnd}."
msgid "Environments in %{name}"
-msgstr ""
+msgstr "Environnements dans %{name}"
msgid "EnvironmentsAlert|%{severity} • %{title} %{text}. %{linkStart}View Details%{linkEnd} · %{startedAt} "
msgstr "%{severity} • %{title} %{text}. %{linkStart}Voir les Détails%{linkEnd} · %{startedAt} "
@@ -15280,7 +15473,7 @@ msgid "EnvironmentsDashboard|Remove"
msgstr "Supprimer"
msgid "EnvironmentsDashboard|The environments dashboard provides a summary of each project's environments' status, including pipeline and alert statuses."
-msgstr ""
+msgstr "Le tableau de bord des environnements fournit un résumé de l'état des environnements de chaque projet, incluant les états des pipelines et des alertes."
msgid "EnvironmentsDashboard|This dashboard displays 3 environments per project, and is linked to the Operations Dashboard. When you add or remove a project from one dashboard, GitLab adds or removes the project from the other. %{linkStart}More information%{linkEnd}"
msgstr "Ce tableau de bord affiche 3 environnements par projet, et est lié au Tableau de bord des Opérations. Lorsque vous ajoutez ou supprimez un projet d'un tableau de bord, GitLab l'ajoute ou le supprime de l’autre. %{linkStart}Plus d'informations%{linkEnd}"
@@ -15298,10 +15491,10 @@ msgid "Environments|An error occurred while making the request."
msgstr "Une erreur s’est produite lors de la requête."
msgid "Environments|An error occurred while re-deploying the environment, please try again"
-msgstr ""
+msgstr "Une erreur s'est produite lors du redéploiement de l'environnement, veuillez réessayer"
msgid "Environments|An error occurred while rolling back the environment, please try again"
-msgstr ""
+msgstr "Une erreur s'est produite lors de la restauration de l'environnement, veuillez réessayer"
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr "Une erreur s’est produite lors de l’arrêt de l’environnement. Veuillez réessayer"
@@ -15373,7 +15566,7 @@ msgid "Environments|New environment"
msgstr "Nouvel environnement"
msgid "Environments|No deployed environments"
-msgstr ""
+msgstr "Aucun environnement déployé"
msgid "Environments|No deployments yet"
msgstr "Aucun déploiement pour le moment"
@@ -15394,7 +15587,7 @@ msgid "Environments|Re-deploy environment"
msgstr "Redéployer l'environnement"
msgid "Environments|Re-deploy environment %{name}?"
-msgstr ""
+msgstr "Redéployer l'environnement %{name} ?"
msgid "Environments|Re-deploy to environment"
msgstr "Redéployer dans l’environnement"
@@ -15403,7 +15596,7 @@ msgid "Environments|Rollback environment"
msgstr "Restaurer l’environnement"
msgid "Environments|Rollback environment %{name}?"
-msgstr ""
+msgstr "Restaurer l'environment %{name} ?"
msgid "Environments|Search by environment name"
msgstr "Rechercher par nom d'environnement"
@@ -15454,7 +15647,7 @@ msgid "Environment|Auto stop %{time}"
msgstr "Arrêt automatique %{time}"
msgid "Environment|Deployment tier"
-msgstr "Type de déploiement"
+msgstr "Étape de déploiement"
msgid "Epic"
msgstr "Épopée"
@@ -15472,7 +15665,7 @@ msgid "Epic details"
msgstr "Détails de l'épopée"
msgid "Epic events"
-msgstr ""
+msgstr "Événements d'épopée"
msgid "Epic not found for given params"
msgstr "Épopée introuvable pour les paramètres donnés"
@@ -15519,9 +15712,6 @@ msgstr "Supprimer l'épopée"
msgid "Epics|Remove issue"
msgstr "Supprimer le ticket"
-msgid "Epics|Show more"
-msgstr "Afficher plus"
-
msgid "Epics|Something went wrong while creating child epics."
msgstr "Une erreur s'est produite lors de la création des épopées enfants."
@@ -15562,7 +15752,7 @@ msgid "Error"
msgstr "Erreur"
msgid "Error Details"
-msgstr ""
+msgstr "Détails de l'erreur"
msgid "Error Tracking"
msgstr "Suivi des erreurs"
@@ -15595,7 +15785,7 @@ msgid "Error fetching branches"
msgstr "Erreur lors de la récupération des branches"
msgid "Error fetching burnup chart data"
-msgstr "Erreur lors de la récupération des données du graphique d'avancement"
+msgstr "Erreur lors de la récupération des données du graphique d'évolution"
msgid "Error fetching diverging counts for branches. Please try again."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr "Erreur lors de la récupération des données de la charge utile."
msgid "Error fetching refs"
msgstr "Erreur lors de la récupération des refs"
+msgid "Error fetching target projects. Please try again."
+msgstr "Une erreur s'est produite lors de la récupération des projets cibles. Veuillez réessayer."
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr "Erreur lors de la récupération de la liste de dépendances. Veuillez vérifier votre connexion réseau et réessayer."
@@ -15646,7 +15839,7 @@ msgid "Error loading merge requests."
msgstr "Erreur lors du chargement des demandes de fusion."
msgid "Error loading milestone tab"
-msgstr ""
+msgstr "Une erreur s'est produite lors du chargement de l'onglet des jalons"
msgid "Error loading project data. Please try again."
msgstr "Erreur lors du chargement des données du projet. Veuillez réessayer."
@@ -15718,10 +15911,10 @@ msgid "Error updating %{issuableType}"
msgstr ""
msgid "Error updating status for all to-do items."
-msgstr ""
+msgstr "Erreur lors de la mise à jour de l'état de tous les pense-bêtes."
msgid "Error updating status of to-do item."
-msgstr ""
+msgstr "Erreur lors de la mise à jour de l'état du pense-bête."
msgid "Error updating the snippet"
msgstr "Erreur lors de la mise à jour de l'extrait de code"
@@ -15778,7 +15971,7 @@ msgid "ErrorTracking|Auth Token"
msgstr "Jeton d'Authentification"
msgid "ErrorTracking|Click Connect to reestablish the connection to Sentry and activate the dropdown."
-msgstr ""
+msgstr "Cliquez sur Connecter pour rétablir la connexion à Sentry et activer la liste déroulante."
msgid "ErrorTracking|Connection failed. Check Auth Token and try again."
msgstr "La connexion a échoué. Vérifiez le Jeton d'Authentification et réessayez."
@@ -15793,10 +15986,10 @@ msgid "ErrorTracking|If you self-host Sentry, enter your Sentry instance's full
msgstr "Si vous auto-hébergez Sentry, entrez l'URL complète de votre instance Sentry. Si vous utilisez la solution d'hébergement de Sentry, entrez https://sentry.io"
msgid "ErrorTracking|Integrated error tracking is %{epicLinkStart}turned off by default%{epicLinkEnd} and no longer active for this project. To re-enable error tracking on self-hosted instances, you can either %{flagLinkStart}turn on the feature flag%{flagLinkEnd} for integrated error tracking, or provide a %{settingsLinkStart}Sentry API URL and Auth Token%{settingsLinkEnd} on your project settings page. However, error tracking is not ready for production use and cannot be enabled on GitLab.com."
-msgstr ""
+msgstr "Le suivi des erreurs intégré est %{epicLinkStart}désactivé par défaut%{epicLinkEnd} et n'est plus actif sur ce projet. Pour le réactiver sur des instances auto-hébergées, vous pouvez soit %{flagLinkStart}activer l'indicateur de fonctionnalité%{flagLinkEnd} du suivi des erreurs intégré, soit fournir les %{settingsLinkStart}URL de l'API Sentry et Jeton d'Authentification%{settingsLinkEnd} sur la page des paramètres de votre projet. Cependant, le suivi des erreurs n'est pas prêt pour un usage en production et ne peut pas être activé sur GitLab.com."
msgid "ErrorTracking|Integrated error tracking is %{epicLinkStart}turned off by default%{epicLinkEnd} and no longer active for this project. To re-enable error tracking on self-hosted instances, you can either %{flagLinkStart}turn on the feature flag%{flagLinkEnd} for integrated error tracking, or provide a Sentry API URL and Auth Token below. However, error tracking is not ready for production use and cannot be enabled on GitLab.com."
-msgstr ""
+msgstr "Le suivi des erreurs intégré est %{epicLinkStart}désactivé par défaut%{epicLinkEnd} et n'est plus actif sur ce projet. Pour le réactiver sur des instances auto-hébergées, vous pouvez soit %{flagLinkStart}activer l'indicateur de fonctionnalité%{flagLinkEnd} du suivi des erreurs intégré, soit fournir les URL de l'API Sentry et Jeton d'Authentification ci-dessous. Cependant, le suivi des erreurs n'est pas prêt pour un usage en production et ne peut pas être activé sur GitLab.com."
msgid "ErrorTracking|No projects available"
msgstr "Aucun projet disponible"
@@ -15930,6 +16123,15 @@ msgstr "Cette politique n’a aucune règle d’escalade."
msgid "EscalationPolicies|mins"
msgstr "min"
+msgid "EscalationStatus|Acknowledged"
+msgstr "Acquitté"
+
+msgid "EscalationStatus|Resolved"
+msgstr "Résolu"
+
+msgid "EscalationStatus|Triggered"
+msgstr "Déclenché"
+
msgid "Estimate"
msgstr ""
@@ -15946,7 +16148,7 @@ msgid "EventFilterBy|Filter by comments"
msgstr "Filtrer par commentaires"
msgid "EventFilterBy|Filter by designs"
-msgstr "Filtrer par design"
+msgstr "Filtrer par esquisses"
msgid "EventFilterBy|Filter by epic events"
msgstr "Filtrer par événements d'épopées"
@@ -15967,7 +16169,7 @@ msgid "EventFilterBy|Filter by wiki"
msgstr "Filtrer par wiki"
msgid "Events"
-msgstr ""
+msgstr "Événements"
msgid "Events API"
msgstr ""
@@ -15976,13 +16178,13 @@ msgid "Event|accepted"
msgstr ""
msgid "Event|added"
-msgstr ""
+msgstr "a ajouté"
msgid "Event|approved"
msgstr ""
msgid "Event|closed"
-msgstr ""
+msgstr "a fermé"
msgid "Event|commented on"
msgstr ""
@@ -16015,7 +16217,7 @@ msgid "Event|pushed to"
msgstr ""
msgid "Event|removed"
-msgstr ""
+msgstr "a supprimé"
msgid "Event|removed due to membership expiration from"
msgstr ""
@@ -16024,7 +16226,7 @@ msgid "Event|updated"
msgstr ""
msgid "Every %{action} attempt has failed: %{job_error_message}. Please try again."
-msgstr ""
+msgstr "Toutes les tentatives pour %{action} ont échoué : %{job_error_message}. Veuillez réessayer."
msgid "Every 3 months"
msgstr "Tous les 3 mois"
@@ -16039,7 +16241,7 @@ msgid "Every 6 months on the %{day} at %{time} %{timezone}"
msgstr "Tous les 6 mois le %{day} à %{time} %{timezone}"
msgid "Every day"
-msgstr ""
+msgstr "Tous les jours"
msgid "Every day (at %{time})"
msgstr "Tous les jours (à %{time})"
@@ -16071,16 +16273,16 @@ msgid "Every week (%{weekday} at %{time})"
msgstr "Toutes les semaines (%{weekday} à %{time})"
msgid "Every week on %{day} at %{time} %{timezone}"
-msgstr ""
+msgstr "Toutes les semaines le %{day} à %{time} %{timezone}"
msgid "Every year"
-msgstr ""
+msgstr "Tous les ans"
msgid "Every year on %{day} at %{time} %{timezone}"
-msgstr ""
+msgstr "Tous les ans le %{day} à %{time} %{timezone}"
msgid "Everyone With Access"
-msgstr ""
+msgstr "Tout le monde ayant accès"
msgid "Everyone can access the wiki."
msgstr "Tout le monde peut accéder au wiki."
@@ -16089,7 +16291,10 @@ msgid "Everyone can contribute"
msgstr "Tout le monde peut contribuer"
msgid "Everything on your to-do list is marked as done."
-msgstr ""
+msgstr "Tous vos pense-bêtes sont marqués comme faits."
+
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr "Tout ce dont vous avez besoin pour créer un site GitLab Pages avec Bridgetown"
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr "Tout ce dont vous avez besoin pour créer un site GitLab Pages avec Gatsby"
@@ -16113,7 +16318,7 @@ msgid "Everything you need to create a GitLab Pages site using Pelican"
msgstr "Tout ce dont vous avez besoin pour créer un site GitLab Pages avec Pelican"
msgid "Everything you need to create a GitLab Pages site using plain HTML"
-msgstr ""
+msgstr "Tout ce dont vous avez besoin pour créer un site GitLab Pages avec du HTML brut"
msgid "Evidence collection"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr "Exemple : (jar|exe)$"
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr "Exemple : ssh\\:\\/\\/"
+
msgid "Examples"
msgstr "Exemples"
@@ -16137,14 +16354,11 @@ msgid "Excluding merge commits. Limited to %{limit} commits."
msgstr "Commits de fusion exclus. Limités à %{limit} commits."
msgid "Excluding merge commits. Limited to 6,000 commits."
-msgstr ""
+msgstr "Commits de fusion exclus. Limités 6000 commits."
msgid "Execution time"
msgstr "Durée d’exécution"
-msgid "Executive Dashboard"
-msgstr "Tableau de bord Exécutif"
-
msgid "Existing branch name, tag, or commit SHA"
msgstr "Nom de branche, étiquette ou SHA de commit existant"
@@ -16199,17 +16413,20 @@ msgstr "Étendre la section des paramètres"
msgid "Expand sidebar"
msgstr "Étendre la barre latérale"
+msgid "Expand variable reference"
+msgstr "Développer la référence de la variable"
+
msgid "Expected documents: %{expected_documents}"
msgstr "Documents attendus : %{expected_documents}"
-msgid "Experiment Candidates"
-msgstr ""
+msgid "Experiment"
+msgstr "Expérience"
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
-msgstr "Doit être un choix unique entre Utilisateur, Espace de noms et Projet."
+msgid "Experiment candidates"
+msgstr ""
msgid "Experiments"
-msgstr ""
+msgstr "Expériences"
msgid "Expiration"
msgstr "Expiration"
@@ -16224,16 +16441,16 @@ msgid "Expiration date:"
msgstr "Date d’expiration :"
msgid "Expired"
-msgstr ""
+msgstr "Expiré"
msgid "Expired %{expiredOn}"
msgstr ""
msgid "Expired:"
-msgstr ""
+msgstr "Expiré :"
msgid "Expires"
-msgstr ""
+msgstr "Expire"
msgid "Expires %{preposition} %{expires_at}"
msgstr "Expire %{preposition} %{expires_at}"
@@ -16242,7 +16459,7 @@ msgid "Expires on"
msgstr "Expire le"
msgid "Expires:"
-msgstr ""
+msgstr "Expire :"
msgid "Explain the problem. If appropriate, provide a link to the relevant issue or comment."
msgstr "Décrivez le problème. Le cas échéant, fournissez un lien vers un ticket ou un commentaire idoine."
@@ -16299,7 +16516,7 @@ msgid "Export merge requests"
msgstr "Exporter les demandes de fusion"
msgid "Export project"
-msgstr ""
+msgstr "Exporter le projet"
msgid "Export requirements"
msgstr "Exporter les exigences"
@@ -16338,7 +16555,7 @@ msgid "ExternalAuthorizationService|Classification label"
msgstr "Étiquette de classification"
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
-msgstr "Lorsqu’aucune étiquette de classification n’est définie, l’étiquette par défaut « %{default_label} » sera utilisée."
+msgstr "Lorsqu’aucune étiquette de classification n’est définie, l’étiquette par défaut `%{default_label}` sera utilisée."
msgid "ExternalAuthorization|Access to projects is validated on an external service using their classification label."
msgstr "L’accès aux projets est validé sur un service externe en utilisant leur étiquette de classification."
@@ -16439,18 +16656,18 @@ msgid "Failed to add a resource link"
msgstr "Échec de l'ajout d'un lien de ressource"
msgid "Failed to apply commands."
-msgstr ""
+msgstr "Échec de l'application des commandes."
msgid "Failed to archive a design. Please try again."
msgid_plural "Failed to archive designs. Please try again."
-msgstr[0] "Echec de l'archivage du design. Essayez à nouveau."
-msgstr[1] "Echec de l'archivage des designs. Essayez à nouveau."
+msgstr[0] "Échec de l'archivage d'une esquisse. Veuillez réessayer."
+msgstr[1] "Échec de l'archivage des esquisses. Veuillez réessayer."
msgid "Failed to assign a reviewer because no user was specified."
msgstr "Échec de l'attribution d'un relecteur car aucun utilisateur n'a été spécifié."
msgid "Failed to assign a user because no user was found."
-msgstr ""
+msgstr "Échec de l'assignation d'un utilisateur car aucun utilisateur n'a été trouvé."
msgid "Failed to cancel auto stop because failed to update the environment."
msgstr "Échec de l'annulation de l'arrêt automatique car la mise à jour de l'environnement a échoué."
@@ -16477,7 +16694,7 @@ msgid "Failed to create a branch for this issue. Please try again."
msgstr "Échec de la création d'une branche pour ce ticket. Veuillez réessayer."
msgid "Failed to create a to-do item for the design."
-msgstr ""
+msgstr "Échec de la création d'un pense-bête pour l'esquisse."
msgid "Failed to create framework"
msgstr "Échec de la création du framework"
@@ -16521,9 +16738,6 @@ msgstr "Échec de la génération du rapport, veuillez réessayer après quelque
msgid "Failed to get ref."
msgstr "Échec d'obtention de la réf."
-msgid "Failed to install."
-msgstr "Échec de l'installation."
-
msgid "Failed to load"
msgstr "Échec du chargement"
@@ -16588,7 +16802,7 @@ msgid "Failed to make repository read-only. %{reason}"
msgstr "Impossible de rendre le dépôt en lecture seule. %{reason}"
msgid "Failed to mark this issue as a duplicate because referenced issue was not found."
-msgstr ""
+msgstr "Impossible de marquer ce ticket comme doublon car le ticket référencé est introuvable."
msgid "Failed to move this issue because label was not found."
msgstr "Échec du déplacement de ce ticket car l'étiquette est introuvable."
@@ -16618,7 +16832,7 @@ msgid "Failed to remove a Zoom meeting"
msgstr "Échec de la suppression d'une réunion Zoom"
msgid "Failed to remove a to-do item for the design."
-msgstr ""
+msgstr "Échec de la suppression d'un pense-bête pour l'esquisse."
msgid "Failed to remove mirror."
msgstr "Impossible de supprimer le miroir."
@@ -16639,7 +16853,7 @@ msgid "Failed to retrieve page"
msgstr "Échec de la récupération de la page"
msgid "Failed to save merge conflicts resolutions. Please try again!"
-msgstr ""
+msgstr "Échec de l'enregistrement des résolutions des conflits de fusion. Veuillez réessayer !"
msgid "Failed to save new settings"
msgstr "Échec de l'enregistrement des nouveaux paramètres"
@@ -16663,7 +16877,7 @@ msgid "Failed to signing using smartcard authentication"
msgstr "Échec de la signature via authentification par carte à puce"
msgid "Failed to toggle the to-do status for the design."
-msgstr ""
+msgstr "Échec du changement d'état du pense-bête pour l'esquisse."
msgid "Failed to update branch!"
msgstr "Échec de la mise à jour de la branche !"
@@ -16680,11 +16894,8 @@ msgstr "Échec de la mise à jour de l'état du ticket"
msgid "Failed to update the Canary Ingress."
msgstr "Échec de la mise à jour du Canari Ingress."
-msgid "Failed to upgrade."
-msgstr "Échec de la mise à niveau."
-
msgid "Failed to upload object map file"
-msgstr ""
+msgstr "Échec du téléversement du fichier listant les objets"
msgid "Failure"
msgstr "Échec"
@@ -16711,7 +16922,7 @@ msgid "Feature Flags"
msgstr "Indicateurs de fonctionnalités"
msgid "Feature deprecation"
-msgstr ""
+msgstr "Dépréciation de fonctionnalité"
msgid "Feature flag status"
msgstr "État de l'indicateur de fonctionnalité"
@@ -16842,7 +17053,7 @@ msgid "FeatureFlags|List details"
msgstr "Détails de la liste"
msgid "FeatureFlags|Loading feature flags"
-msgstr ""
+msgstr "Chargement des indicateurs de fonctionnalités"
msgid "FeatureFlags|More information"
msgstr "Plus d'informations"
@@ -16917,7 +17128,7 @@ msgid "FeatureFlag|Select a user list"
msgstr "Sélectionner une liste d'utilisateurs"
msgid "FeatureFlag|Select the environment scope for this feature flag"
-msgstr ""
+msgstr "Sélectionnez la portée de l'environnement pour cet indicateur de fonctionnalité"
msgid "FeatureFlag|There are no configured user lists"
msgstr "Il n’y a pas de liste d’utilisateurs configurée"
@@ -16937,14 +17148,17 @@ msgstr "févr."
msgid "February"
msgstr "février"
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
msgid "Fetch and check out this merge request's feature branch:"
-msgstr ""
+msgstr "Récupérez et basculez sur la branche de fonctionnalité de cette demande de fusion :"
msgid "Fetching incoming email"
-msgstr ""
+msgstr "Récupération du courriel entrant"
msgid "File"
msgstr "Fichier"
@@ -16953,10 +17167,10 @@ msgid "File %{current} of %{total}"
msgstr "Fichier %{current} sur %{total}"
msgid "File Hooks"
-msgstr "Fichiers « Hooks »"
+msgstr "Fichiers de crochet"
msgid "File Hooks (%{count})"
-msgstr "Fichiers « Hooks » (%{count})"
+msgstr "Fichiers de crochet (%{count})"
msgid "File Tree"
msgstr ""
@@ -16971,7 +17185,7 @@ msgid "File deleted"
msgstr "Fichier supprimé"
msgid "File hooks are similar to system hooks but are executed as files instead of sending data to a URL."
-msgstr "Les fichiers « hooks » sont similaires aux « hooks » système mais sont exécutés sous forme de fichiers plutôt que par l'envoi de données vers une URL."
+msgstr "Les fichiers de crochet sont similaires aux crochets du système mais sont exécutés sous forme de fichiers plutôt que par l'envoi de données vers une URL."
msgid "File mode changed from %{a_mode} to %{b_mode}"
msgstr ""
@@ -17004,7 +17218,7 @@ msgid "Files"
msgstr "Fichiers"
msgid "Files API Rate Limits"
-msgstr ""
+msgstr "Limitations de fréquence de l'API des fichiers"
msgid "Files breadcrumb"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr "Federated Learning of Cohorts (FLoC)"
msgid "FloC|Participate in FLoC"
msgstr "Participer à FLoC"
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr "Entrez votre jeton Flowdock."
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr "Envoyer des notifications d'événements depuis GitLab vers des flux Flowdock."
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr "Envoyer des notifications d'événements depuis GitLab vers des flux Flowdock. %{docs_link}"
-
msgid "Focus filter bar"
msgstr ""
@@ -17190,7 +17395,7 @@ msgid "Fogbugz|Project %{repo} could not be found"
msgstr "Le projet %{repo} n'a pas pu être trouvé"
msgid "Folder/%{name}"
-msgstr ""
+msgstr "Dossier/%{name}"
msgid "Follow"
msgstr "Suivre"
@@ -17201,6 +17406,9 @@ msgstr "Activité des Utilisateurs suivis"
msgid "Followed users"
msgstr "Utilisateurs suivis"
+msgid "Following tags don't exist"
+msgstr "Les étiquettes suivantes n'existent pas"
+
msgid "Font Color"
msgstr "Couleur de la police"
@@ -17232,7 +17440,7 @@ msgid "For files larger than this limit, only index the file name. The file cont
msgstr "Pour les fichiers de taille supérieure à cette limite, n'indexer que le nom. Le contenu du fichier ne sera ni indexé ni accessible aux recherches."
msgid "For general work"
-msgstr ""
+msgstr "Pour un usage générique"
msgid "For individual use, create a separate account under your personal email address, not tied to the Enterprise email domain or group."
msgstr "Pour une utilisation individuelle, créez un compte distinct avec votre adresse électronique personnelle, non relié à votre groupe ou domaine de messagerie Entreprise."
@@ -17250,7 +17458,7 @@ msgid "For more information, go to the "
msgstr "Pour plus d’informations, consultez "
msgid "For more information, see the File Hooks documentation."
-msgstr "Pour plus d'informations, reportez-vous à la documentation sur les fichiers « Hooks »."
+msgstr "Pour plus d'informations, reportez-vous à la documentation sur les fichiers de crochet."
msgid "Forbidden"
msgstr ""
@@ -17345,12 +17553,21 @@ msgstr "Divergence en cours"
msgid "Forks"
msgstr ""
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
msgid "Format: %{dateFormat}"
msgstr "Format : %{dateFormat}"
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
-msgstr "Transférer les demandes de paquets %{package_type} au Registre %{registry_type} dans le cas où les paquets ne sont pas trouvés dans le Registre de Paquets de GitLab"
-
msgid "Found errors in your %{gitlab_ci_yml}:"
msgstr "Erreurs trouvées dans votre %{gitlab_ci_yml} :"
@@ -17367,7 +17584,7 @@ msgid "Free Trial of GitLab.com Ultimate"
msgstr "Essai Gratuit de GitLab.com Ultimate"
msgid "Free User Cap"
-msgstr ""
+msgstr "Plafond d'Utilisateurs Gratuits"
msgid "Free groups are limited to %{free_user_limit} member and the remaining members will get a status of over-limit and lose access to the group."
msgid_plural "Free groups are limited to %{free_user_limit} members and the remaining members will get a status of over-limit and lose access to the group."
@@ -17408,7 +17625,7 @@ msgid "Freeze start"
msgstr "Début du gel"
msgid "Frequency"
-msgstr ""
+msgstr "Fréquence"
msgid "Frequently searched"
msgstr "Fréquemment recherché"
@@ -17420,7 +17637,7 @@ msgid "Friday"
msgstr "Vendredi"
msgid "From"
-msgstr ""
+msgstr "Du"
msgid "From %{code_open}%{source_title}%{code_close} into"
msgstr "De %{code_open}%{source_title}%{code_close} vers"
@@ -17428,11 +17645,6 @@ msgstr "De %{code_open}%{source_title}%{code_close} vers"
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] "À partir du 19 octobre 2022, les groupes gratuits seront limités à %d membre"
-msgstr[1] "À partir du 19 octobre 2022, les groupes gratuits seront limités à %d membres"
-
msgid "From issue creation until deploy to production"
msgstr "Depuis la création du ticket jusqu’au déploiement en production"
@@ -17455,7 +17667,7 @@ msgid "GPG Key ID:"
msgstr "ID de la clé GPG :"
msgid "GPG Keys"
-msgstr "Clefs GPG"
+msgstr "Clés GPG"
msgid "GPG keys allow you to verify signed commits."
msgstr "Les clés GPG vous permettent de vérifier les commits signés."
@@ -17467,13 +17679,16 @@ msgid "General"
msgstr "Général"
msgid "General Settings"
-msgstr ""
+msgstr "Paramètres Généraux"
msgid "General pipelines"
msgstr "Pipelines généraux"
msgid "General settings"
-msgstr ""
+msgstr "Paramètres généraux"
+
+msgid "Generate API key at %{site}"
+msgstr "Générer la clé d'API sur %{site}"
msgid "Generate a default set of labels"
msgstr "Générer un jeu d’étiquettes par défaut"
@@ -17488,7 +17703,7 @@ msgid "Generate project access tokens scoped to this project for your applicatio
msgstr "Générer des jetons d'accès au projet, de portée limitée au projet, pour vos applications qui ont besoin de l'API GitLab."
msgid "Generate site and private keys at"
-msgstr "Générer le site et les clés privées sur"
+msgstr "Générer la clé du site et la clé privée sur"
msgid "Generated with JSON data"
msgstr "Généré avec les données JSON"
@@ -17606,7 +17821,7 @@ msgid "Geo|Checksummed"
msgstr ""
msgid "Geo|Choose specific groups or storage shards"
-msgstr ""
+msgstr "Choisir des groupes ou des fragments de stockage spécifiques"
msgid "Geo|Comma-separated, e.g. '1.1.1.1, 2.2.2.0/24'"
msgstr "Virgule comme séparateur, p. ex. « 1.1.1.1, 2.2.2.0/24 »"
@@ -17690,7 +17905,7 @@ msgid "Geo|Geo Status"
msgstr "État de Geo"
msgid "Geo|Geo allows you to choose specific groups or storage shards to replicate."
-msgstr ""
+msgstr "Geo vous permet de choisir des groupes ou des fragments de stockage spécifiques à répliquer."
msgid "Geo|Geo can replicate objects stored in Object Storage (AWS S3, or other compatible object storage)."
msgstr "Geo peut dupliquer des objets enregistrés dans le Stockage d'Objets (AWS S3 ou autre stockage d'objets compatible)"
@@ -17711,7 +17926,7 @@ msgid "Geo|Healthy"
msgstr "En bonne santé"
msgid "Geo|If enabled, GitLab will handle Object Storage replication using Geo."
-msgstr ""
+msgstr "Si activé, GitLab gérera la réplication du Stockage d'Objets en utilisant Geo."
msgid "Geo|If you want to make changes, you must visit the primary site."
msgstr "Si vous souhaitez apporter des modifications, vous devez aller sur le site principal."
@@ -17843,7 +18058,7 @@ msgid "Geo|Remove site"
msgstr "Supprimer le site"
msgid "Geo|Remove tracking database entry"
-msgstr ""
+msgstr "Supprimer l'entrée de la base de données de suivi"
msgid "Geo|Removing a Geo site stops the synchronization to and from that site. Are you sure?"
msgstr "La suppression d'un site Geo arrête la synchronisation depuis et vers ce site. Êtes-vous sûr(e) ?"
@@ -18020,7 +18235,7 @@ msgid "Geo|Time in seconds"
msgstr ""
msgid "Geo|Tracking database entry will be removed. Are you sure?"
-msgstr ""
+msgstr "L’entrée de la base de données de suivi sera supprimée. Êtes-vous sûr(e) ?"
msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
msgstr "L’entrée de suivi du projet (%{project_id}) a été supprimée avec succès."
@@ -18041,7 +18256,7 @@ msgid "Geo|Unhealthy"
msgstr "En mauvaise santé"
msgid "Geo|Unknown"
-msgstr ""
+msgstr "Inconnu"
msgid "Geo|Updated %{timeAgo}"
msgstr "Mis à jour %{timeAgo}"
@@ -18095,13 +18310,13 @@ msgid "Get started"
msgstr "Commencer"
msgid "Get started with GitLab"
-msgstr ""
+msgstr "Premiers pas avec GitLab"
msgid "Get started with error tracking"
msgstr "Premiers pas avec le suivi des erreurs"
msgid "Get started with performance monitoring"
-msgstr ""
+msgstr "Premiers pas avec la surveillance des performances"
msgid "Get started!"
msgstr ""
@@ -18137,10 +18352,10 @@ msgid "Git revision"
msgstr "Révision de Git"
msgid "Git shallow clone"
-msgstr ""
+msgstr "Clone Git superficiel"
msgid "Git strategy"
-msgstr ""
+msgstr "Stratégie Git"
msgid "Git transfer in progress"
msgstr ""
@@ -18149,7 +18364,7 @@ msgid "Git version"
msgstr "Version de Git"
msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
-msgstr ""
+msgstr "Bannir automatiquement les utilisateurs de l'%{scope} lorsqu'ils dépassent les limites spécifiées"
msgid "GitAbuse|Excluded users"
msgstr "Utilisateurs exclus"
@@ -18260,7 +18475,7 @@ msgid "GitLab commit"
msgstr "Validation GitLab"
msgid "GitLab events trigger webhooks. Use the request details of a webhook to help troubleshoot problems. %{link_start}How do I troubleshoot?%{link_end}"
-msgstr ""
+msgstr "Les événements GitLab déclenchent des crochets web. Utilisez les détails de la requête d'un crochet web pour vous aider à résoudre les problèmes. %{link_start}Comment puis-je résoudre les problèmes ?%{link_end}"
msgid "GitLab export"
msgstr "Export GitLab"
@@ -18272,7 +18487,7 @@ msgid "GitLab group: %{source_link}"
msgstr "Groupe GitLab : %{source_link}"
msgid "GitLab incubates features to explore new use cases. These features are updated regularly, and support is limited"
-msgstr ""
+msgstr "GitLab sert d'incubateur pour des fonctionnalités dont le but est d'explorer des nouveaux cas d'usage. Ces fonctionnalités sont mises à jour régulièrement et le support est limité"
msgid "GitLab informs you if a new version is available. %{link_start}What information does GitLab Inc. collect?%{link_end}"
msgstr "GitLab vous informe si une nouvelle version est disponible. %{link_start}Quelles sont les informations collectées par GitLab Inc. ?%{link_end}"
@@ -18302,7 +18517,7 @@ msgid "GitLab logo"
msgstr "Logo GitLab"
msgid "GitLab metadata URL"
-msgstr ""
+msgstr "URL des métadonnées de GitLab"
msgid "GitLab project export"
msgstr "Exportation de projet GitLab"
@@ -18322,6 +18537,11 @@ msgstr "Version de GitLab"
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr "GitLab va créer une branche dans votre projet divergent et lancer une demande de fusion."
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] "GitLab va imposer cette limite dans le futur. Si vous avez plus de %{free_user_limit} utilisateur au moment de la mise en application, votre espace de noms sera placé dans un état de %{link_start}lecture seule%{link_end}. Pour éviter cette situation, réduisez votre espace de noms à %{free_user_limit} utilisateur ou moins, ou achetez une édition payante."
+msgstr[1] "GitLab va imposer cette limite dans le futur. Si vous avez plus de %{free_user_limit} utilisateurs au moment de la mise en application, votre espace de noms sera placé dans un état de %{link_start}lecture seule%{link_end}. Pour éviter cette situation, réduisez votre espace de noms à %{free_user_limit} utilisateurs ou moins, ou achetez une édition payante."
+
msgid "GitLab.com (SaaS)"
msgstr "GitLab.com (SaaS)"
@@ -18377,7 +18597,7 @@ msgid "GitLabPages|New Domain"
msgstr "Nouveau Domaine"
msgid "GitLabPages|Only project maintainers can remove pages"
-msgstr "Seuls les responsables de projet peuvent supprimer des pages"
+msgstr "Seuls les mainteneurs de projet peuvent supprimer des pages"
msgid "GitLabPages|Pages"
msgstr "Pages"
@@ -18395,7 +18615,7 @@ msgid "GitLabPages|Remove pages"
msgstr "Supprimer les pages"
msgid "GitLabPages|Removing pages will prevent them from being exposed to the outside world."
-msgstr ""
+msgstr "Supprimer des pages les empêchera d'être exposées au monde extérieur."
msgid "GitLabPages|Save changes"
msgstr "Enregistrer les modifications"
@@ -18527,7 +18747,7 @@ msgid "Global notification level"
msgstr "Niveau de notification global"
msgid "Global notification settings"
-msgstr ""
+msgstr "Paramètres de notification globaux"
msgid "GlobalSearch| %{search} %{description} %{scope}"
msgstr " %{search} %{description} %{scope}"
@@ -18571,6 +18791,9 @@ msgstr "Tickets récents"
msgid "GlobalSearch|Recent merge requests"
msgstr "Demandes de fusion récentes"
+msgid "GlobalSearch|Result count is over limit."
+msgstr "Le nombre de résultats est supérieur à la limite."
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr "Résultats mis à jour. %{count} résultats disponibles. Utilisez les touches fléchées haut et bas pour parcourir la liste des résultats de la recherche, ou ENTRÉE pour envoyer."
@@ -18598,6 +18821,9 @@ msgstr "Écrivez pour faire apparaître de nouvelles suggestions ci-dessous"
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr "Utilisez le raccourci-clavier %{kbdOpen}/%{kbdClose} pour lancer une recherche"
+msgid "GlobalSearch|Users"
+msgstr "Utilisateurs"
+
msgid "GlobalSearch|What are you searching for?"
msgstr "Que recherchez-vous ?"
@@ -18641,10 +18867,10 @@ msgid "Go full screen"
msgstr "Plein écran"
msgid "Go to %{source_name}"
-msgstr ""
+msgstr "Aller à %{source_name}"
msgid "Go to commits"
-msgstr ""
+msgstr "Aller aux commits"
msgid "Go to definition"
msgstr "Aller à la définition"
@@ -18662,7 +18888,7 @@ msgid "Go to file"
msgstr "Aller au fichier"
msgid "Go to file permalink (while viewing a file)"
-msgstr ""
+msgstr "Aller au permalien du fichier (lors de la visualisation d'un fichier)"
msgid "Go to files"
msgstr "Aller aux fichiers"
@@ -18698,7 +18924,7 @@ msgid "Go to page %{page}"
msgstr "Aller à la page %{page}"
msgid "Go to parent"
-msgstr ""
+msgstr "Aller au parent"
msgid "Go to parent directory"
msgstr "Aller au répertoire parent"
@@ -18731,13 +18957,13 @@ msgid "Go to the %{b_open}Activity%{b_close} page for %{project_link}."
msgstr "Aller à la page %{b_open}Activité%{b_close} de %{project_link}."
msgid "Go to the 'Admin area &gt; Sign-up restrictions', and check 'Allowed domains for sign-ups'."
-msgstr ""
+msgstr "Allez à l' « Espace d'administration &gt; Restrictions d'inscription » et vérifiez les « Domaines autorisés pour les inscriptions »."
msgid "Go to the 'Admin area &gt; Sign-up restrictions', and check 'Email restrictions for sign-ups'."
-msgstr ""
+msgstr "Allez à l' « Espace d'administration &gt; Restrictions d'inscription » et vérifiez les « Restrictions d'adresses de courriel pour les inscriptions »."
msgid "Go to the 'Admin area &gt; Sign-up restrictions', and check the 'Domain denylist'."
-msgstr ""
+msgstr "Allez à l' « Espace d'administration &gt; Restrictions d'inscription » et vérifiez les « Domaines refusés pour les inscriptions »."
msgid "Go to the activity feed"
msgstr "Aller au flux d'activité"
@@ -18758,7 +18984,7 @@ msgid "Go to wiki"
msgstr "Aller au wiki"
msgid "Go to your To-Do list"
-msgstr "Aller à votre liste de tâches"
+msgstr "Aller à vos pense-bêtes"
msgid "Go to your fork"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr "Aller à vos demandes de fusion"
msgid "Go to your projects"
msgstr "Aller à vos projets"
+msgid "Go to your review requests"
+msgstr "Aller à vos demandes de revue"
+
msgid "Go to your snippets"
msgstr "Aller à vos extraits"
@@ -18818,7 +19047,7 @@ msgid "GoogleCloud|New service account is generated for the selected Google Clou
msgstr "Un nouveau compte de service est généré pour le projet Google Cloud sélectionné"
msgid "GoogleCloud|Refs"
-msgstr ""
+msgstr "Réfs"
msgid "GoogleCloud|Revoke authorizations"
msgstr "Révoquer les autorisations"
@@ -18836,13 +19065,13 @@ msgid "Grafana URL"
msgstr "URL de Grafana"
msgid "Grafana response contains invalid json"
-msgstr ""
+msgstr "La réponse de Grafana contient du json non valide"
msgid "GrafanaIntegration|API token"
msgstr "Jeton d’API"
msgid "GrafanaIntegration|Active"
-msgstr ""
+msgstr "Actif"
msgid "GrafanaIntegration|Enter the %{docLinkStart}Grafana API token%{docLinkEnd}."
msgstr "Entrez le %{docLinkStart}jeton d'API Grafana%{docLinkEnd}."
@@ -18866,7 +19095,13 @@ msgid "Grant write permissions to this key"
msgstr "Accorder des autorisations d'écriture à cette clé"
msgid "Graph"
-msgstr "Graphique"
+msgstr "Graphe"
+
+msgid "GraphQL"
+msgstr "GraphQL"
+
+msgid "GraphQL endpoint path"
+msgstr "Chemin du point de terminaison GraphQL"
msgid "GraphViewType|Job dependencies"
msgstr "Dépendances des tâches"
@@ -18911,7 +19146,7 @@ msgid "Group Hooks"
msgstr "Crochets de groupe"
msgid "Group Owner must have signed in with SAML before enabling Group Managed Accounts"
-msgstr ""
+msgstr "Le propriétaire du groupe doit s'être connecté avec SAML avant d'activer les Comptes Gérés de Groupe"
msgid "Group SAML must be enabled to test"
msgstr "Le SAML de groupe doit être activé afin de pouvoir tester"
@@ -19004,7 +19239,7 @@ msgid "Group navigation"
msgstr ""
msgid "Group overview content"
-msgstr ""
+msgstr "Contenu de la vue d'ensemble du groupe"
msgid "Group path is already taken. We've suggested one that is available."
msgstr "Le chemin du groupe est déjà pris. Nous en avons suggéré un qui est disponible."
@@ -19016,7 +19251,7 @@ msgid "Group pipeline minutes were successfully reset."
msgstr "Les minutes du pipeline du groupe ont été réinitialisées avec succès."
msgid "Group project URLs are prefixed with the group namespace"
-msgstr ""
+msgstr "Les URLs du projet de groupe sont préfixées avec l'espace de noms du groupe"
msgid "Group requires separate account"
msgstr "Le groupe nécessite un compte séparé"
@@ -19037,7 +19272,7 @@ msgid "Group was successfully updated."
msgstr "Le groupe a été mis à jour avec succès."
msgid "Group wikis"
-msgstr ""
+msgstr "Wikis de Groupe"
msgid "Group-level wiki is disabled."
msgstr "Le wiki au niveau du groupe est désactivé."
@@ -19057,7 +19292,7 @@ msgstr "Les 30 derniers jours"
msgid "GroupActivityMetrics|Members added"
msgstr "Membres ajoutés"
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr "Demandes de fusion créées"
msgid "GroupActivityMetrics|Recent activity"
@@ -19130,10 +19365,10 @@ msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of the %
msgstr "Pour afficher la feuille de route, ajoutez une date de début ou une date d'échéance à l'une des %{linkStart}épopées enfants%{linkEnd}."
msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups; from %{startDate} to %{endDate}."
-msgstr ""
+msgstr "Pour afficher la feuille de route, ajoutez une date de début ou une date d'échéance à l'une de vos épopées dans ce groupe ou un de ses sous-groupes ; du %{startDate} au %{endDate}."
msgid "GroupRoadmap|To widen your search, change or remove filters; from %{startDate} to %{endDate}."
-msgstr ""
+msgstr "Pour élargir votre recherche, modifiez ou supprimez des filtres ; du %{startDate} au %{endDate}."
msgid "GroupRoadmap|View epics list"
msgstr "Afficher la liste des épopées"
@@ -19144,6 +19379,9 @@ msgstr "Dans les 3 ans"
msgid "GroupSAML|\"persistent\" recommended"
msgstr "\"persistant\" recommandé"
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr "L'authentification SAML de %{group_name} a échoué : %{message}"
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr "%{strongOpen}Avertissement%{strongClose} - Activer la %{linkStart}mise en œuvre de SSO%{linkEnd} pour réduire les risques de sécurité."
@@ -19208,16 +19446,16 @@ msgid "GroupSAML|Identity provider single sign-on URL"
msgstr "URL d’authentification unique du fournisseur d’identité"
msgid "GroupSAML|Make sure you save this token — you won't be able to access it again."
-msgstr ""
+msgstr "Assurez-vous de sauvegarder ce jeton - vous ne pourrez plus y accéder."
msgid "GroupSAML|Manage your group’s membership while adding another level of security with SAML."
-msgstr ""
+msgstr "Gérez les appartenances à votre groupe tout en ajoutant un autre niveau de sécurité avec SAML."
msgid "GroupSAML|Members"
msgstr "Membres"
msgid "GroupSAML|Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
-msgstr ""
+msgstr "Les membres seront redirigés ici lors de la connexion à votre groupe. Obtenez-la auprès de votre fournisseur d’identité, où elle peut également être appelée « Emplacement de Service SSO », « Point de terminaison d’émission de jeton SAML » ou « URL SAML 2.0/W-Federation »."
msgid "GroupSAML|NameID"
msgstr "NameID"
@@ -19249,6 +19487,9 @@ msgstr "Nom du Groupe SAML"
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr "Nom du Groupe SAML : %{saml_group_name}"
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr "L'ID du nom SAML et l'adresse de courriel ne correspondent pas à votre compte utilisateur. Contactez un administrateur."
+
msgid "GroupSAML|SAML Response Output"
msgstr "Sortie de la réponse SAML"
@@ -19277,7 +19518,7 @@ msgid "GroupSAML|The case-sensitive group name that will be sent by the SAML ide
msgstr "Le nom de groupe sensible à la casse qui sera envoyé par le fournisseur d'identité SAML."
msgid "GroupSAML|This will be set as the access level of users added to the group."
-msgstr ""
+msgstr "Ceci sera défini comme le niveau d'accès des utilisateurs ajoutés au groupe."
msgid "GroupSAML|To be able to enable group-managed accounts, you first need to enable enforced SSO."
msgstr "Afin de pouvoir activer les comptes gérés par le groupe, vous devez d'abord forcer l'authentification SSO."
@@ -19298,7 +19539,7 @@ msgid "GroupSAML|as %{access_level}"
msgstr ""
msgid "GroupSAML|must match stored NameID of \"%{extern_uid}\" to identify user and allow sign in"
-msgstr ""
+msgstr "doit correspondre au NameID stocké de « %{extern_uid} » pour identifier l'utilisateur et permettre la connexion"
msgid "GroupSAML|recommend persistent ID instead of email"
msgstr ""
@@ -19340,7 +19581,7 @@ msgid "GroupSettings|Be careful. Changing a group's parent can have unintended s
msgstr "Faites attention. Changer le parent d'un groupe peut avoir des effets secondaires inattendus. %{learn_more_link_start}En savoir plus.%{learn_more_link_end}"
msgid "GroupSettings|Cannot update the path because there are projects under this group that contain Docker images in their Container Registry. Please remove the images from your projects first and try again."
-msgstr ""
+msgstr "Impossible de mettre à jour le chemin d'accès car il y a des projets dans ce groupe qui contiennent des images Docker dans leur Registre de Conteneurs. Veuillez d'abord supprimer les images de vos projets et réessayez."
msgid "GroupSettings|Change group URL"
msgstr "Modifier l'URL du groupe"
@@ -19358,10 +19599,10 @@ msgid "GroupSettings|Configure compliance frameworks to make them available to p
msgstr "Configurer les cadres de conformité pour les mettre à disposition des projets de ce groupe. %{linkStart}Que sont les cadres de conformité ?%{linkEnd}"
msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
-msgstr ""
+msgstr "Configurer les limites du nombre de dépôts que les utilisateurs peuvent télécharger dans un temps donné."
msgid "GroupSettings|Custom project templates"
-msgstr ""
+msgstr "Modèles de projet personnalisés"
msgid "GroupSettings|Customer relations is enabled"
msgstr "La relation client est activée"
@@ -19420,8 +19661,8 @@ msgstr "Les projets dans %{group} ne peuvent pas être partagés avec d'autres g
msgid "GroupSettings|Reporting"
msgstr "Rapports"
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
-msgstr "Sélectionnez un sous-groupe à utiliser en tant que source pour les modèles de projet personnalisé de ce groupe."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
+msgstr "Sélectionnez un sous-groupe à utiliser comme source de modèles personnalisés pour les nouveaux projets de ce groupe. %{link_start}En savoir plus%{link_end}."
msgid "GroupSettings|Select parent group"
msgstr "Sélectionner le groupe parent"
@@ -19441,9 +19682,6 @@ msgstr "Définit les protections et nom initiaux pour la branche par défaut des
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr "Le pipeline Auto DevOps s’exécute si aucun autre fichier de configuration CI n’est trouvé."
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr "Les projets de ce sous-groupe peuvent être sélectionnés comme modèles pour les nouveaux projets créés dans le groupe. %{link_start}En savoir plus.%{link_end}"
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr "Une erreur s'est produite lors de la mise à jour du pipeline Auto DevOps : %{error_messages}."
@@ -19478,7 +19716,7 @@ msgid "GroupSettings|You can only transfer the group to a group you manage."
msgstr "Vous ne pouvez transférer le groupe que dans un groupe que vous gérez."
msgid "GroupSettings|You will need to update your local repositories to point to the new location."
-msgstr ""
+msgstr "Vous devrez mettre à jour vos dépôts locaux pour indiquer le nouvel emplacement."
msgid "GroupSettings|cannot be changed by you"
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr "Se connecter à l'instance"
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr "Contactez un administrateur pour activer les options pour importer votre groupe."
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr "Créez un jeton avec les portées %{code_start}api%{code_end} et %{code_start}read_repository%{code_end} dans les %{pat_link_start}paramètres de l'utilisateur%{pat_link_end} de l'instance GitLab source. Pour des %{short_living_link_start}raisons de sécurité%{short_living_link_end}, utilisez une date d'expiration proche lors de la création du jeton. Gadrez à l'esprit que des migrations importantes prennent plus de temps."
+
msgid "GroupsNew|Create group"
msgstr "Créer un groupe"
@@ -19582,11 +19823,8 @@ msgstr "Créer un nouveau groupe"
msgid "GroupsNew|Create subgroup"
msgstr "Créer un sous-groupe"
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr "Créez-le dans les %{pat_link_start}paramètres de l'utilisateur%{pat_link_end} de l'instance GitLab source. Pour %{short_living_link_start}des raisons de sécurité%{short_living_link_end}, utilisez une date d'expiration proche lors de sa création."
-
msgid "GroupsNew|GitLab source URL"
-msgstr ""
+msgstr "URL du GitLab source"
msgid "GroupsNew|Groups can also be nested by creating %{linkStart}subgroups%{linkEnd}."
msgstr "Les groupes peuvent également être imbriqués en créant des %{linkStart}sous-groupes%{linkEnd}."
@@ -19634,7 +19872,7 @@ msgid "GroupsNew|e.g. h8d3f016698e..."
msgstr "p. ex. h8d3f016698e..."
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
-msgstr "Êtes-vous sûr·e de vouloir quitter le groupe « %{fullName} » ?"
+msgstr "Êtes-vous sûr(e) de vouloir quitter le groupe « %{fullName} » ?"
msgid "GroupsTree|Delete"
msgstr "Supprimer"
@@ -19643,7 +19881,7 @@ msgid "GroupsTree|Edit"
msgstr "Modifier"
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
-msgstr "Impossible de quitter le groupe. Veuillez vous assurer que vous n’êtes pas seul·e propriétaire."
+msgstr "Impossible de quitter le groupe. Veuillez vous assurer que vous n’êtes pas l'unique propriétaire."
msgid "GroupsTree|Leave group"
msgstr "Quitter le groupe"
@@ -19724,7 +19962,7 @@ msgid "Guest"
msgstr "Invité"
msgid "Guideline"
-msgstr ""
+msgstr "Ligne directrice"
msgid "HAR (HTTP Archive)"
msgstr "HAR (HTTP Archive)"
@@ -19851,7 +20089,7 @@ msgid "Hashed Storage must be enabled to use Geo"
msgstr "Le Stockage haché doit être activé pour utiliser Geo"
msgid "Hashed repository storage paths"
-msgstr ""
+msgstr "Chemins d'accès au stockage du dépôt hachés"
msgid "Hashed storage can't be disabled anymore for new projects"
msgstr "Le stockage haché ne peut plus être désactivé pour les nouveaux projets"
@@ -20003,7 +20241,7 @@ msgid "Hide group projects"
msgstr "Masquer les projets de groupe"
msgid "Hide host keys manual input"
-msgstr "Masquer la saisie manuelle des clefs d’hôtes"
+msgstr "Masquer la saisie manuelle des clés de l'hôte"
msgid "Hide list"
msgstr "Masquer la liste"
@@ -20018,7 +20256,7 @@ msgid "Hide shared projects"
msgstr "Masquer les projets partagés"
msgid "Hide thread"
-msgstr ""
+msgstr "Masquer le fil"
msgid "Hide tooltips or popovers"
msgstr "Masquer les info-bulles et les boîtes flottantes"
@@ -20058,12 +20296,18 @@ msgstr "Structure non disponible"
msgid "Hierarchy|You can start using these items now."
msgstr "Vous pouvez commencer à utiliser ces éléments dès maintenant."
+msgid "High - S2"
+msgstr "Élevée - S2"
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr "Rôle le plus élevé :"
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr "Événements d’alerte :"
@@ -20097,9 +20341,6 @@ msgstr "Page d'accueil"
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr "L'exécution du crochet a échoué. Assurez-vous que le groupe a un projet avec des commits."
-msgid "Hook was successfully updated."
-msgstr "Le crochet a été mis à jour avec succès."
-
msgid "Horizontal rule"
msgstr "Ligne horizontale"
@@ -20125,7 +20366,7 @@ msgid "How do I configure this integration?"
msgstr "Comment configurer cette intégration ?"
msgid "How do I generate it?"
-msgstr ""
+msgstr "Comment puis-je la générer ?"
msgid "How do I mirror repositories?"
msgstr "Comment puis-je faire des miroirs de dépôts ?"
@@ -20263,11 +20504,14 @@ msgid "Identifier"
msgstr "Identifiant"
msgid "Identifiers"
-msgstr ""
+msgstr "Identifiants"
msgid "Identities"
msgstr "Identités"
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr "%{linkStart}Entrez un nouveau numéro de téléphone%{linkEnd}"
+
msgid "IdentityVerification|A new code has been sent."
msgstr "Un nouveau code a été envoyé."
@@ -20292,6 +20536,9 @@ msgstr "Créer un projet"
msgid "IdentityVerification|Didn't receive a code?"
msgstr "Vous n'avez pas reçu de code ?"
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr "Vous n'avez pas reçu de code ? %{linkStart}Envoyer un nouveau code%{linkEnd}"
+
msgid "IdentityVerification|Enter a code."
msgstr "Entrez un code."
@@ -20331,8 +20578,8 @@ msgstr "Le nombre maximal de tentatives de connexion a été dépassé. Attendez
msgid "IdentityVerification|Phone number"
msgstr "Numéro de téléphone"
-msgid "IdentityVerification|Phone number can't be blank."
-msgstr "Le numéro de téléphone ne peut pas être vide."
+msgid "IdentityVerification|Phone number is required."
+msgstr "Le numéro de téléphone est requis."
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
msgstr "Le numéro de téléphone doit contenir %{maxLength} chiffres ou moins."
@@ -20373,6 +20620,12 @@ msgstr "Le code est incorrect. Saisissez-le de nouveau, ou envoyez un nouveau co
msgid "IdentityVerification|Verification code"
msgstr "Code de vérification"
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr "Le code de vérification ne peut pas être vide."
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr "Le code de vérification doit être un nombre."
+
msgid "IdentityVerification|Verification successful"
msgstr "Vérification réussie"
@@ -20385,14 +20638,23 @@ msgstr "Vérifier l'adresse de courriel"
msgid "IdentityVerification|Verify payment method"
msgstr "Méthode de vérification de paiement"
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr "Vérifiez votre identité"
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr "Nous avons envoyé un nouveau code au +%{phoneNumber}"
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr "Nous avons envoyé un code de vérification au +%{phoneNumber}"
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr "Vous pouvez toujours vérifier votre compte plus tard pour créer un groupe."
msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
-msgstr ""
+msgstr "Vous allez recevoir un texte contenant un code. Des frais standards peuvent s'appliquer."
msgid "IdentityVerification|You've reached the maximum amount of resends. Wait %{interval} and try again."
msgstr "Le nombre maximum d'envois a été atteint. Patientez %{interval} et réessayez."
@@ -20449,10 +20711,10 @@ msgid "If this email was added in error, you can remove it here: %{profile_email
msgstr "Si ce courriel a été ajouté par erreur, vous pouvez le supprimer ici : %{profile_emails_url}"
msgid "If this was a mistake you can %{leave_link_start}leave the %{source_type}%{link_end}."
-msgstr ""
+msgstr "S'il s'agit d'une erreur, vous pouvez %{leave_link_start}quitter le %{source_type}%{link_end}."
msgid "If this was a mistake you can leave the %{source_type}."
-msgstr ""
+msgstr "S'il s'agit d'une erreur, vous pouvez quitter le %{source_type}."
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
msgstr "Si vous utilisez GitHub, vous verrez les statuts des pipelines sur GitHub pour vos commits et demandes d’intégration (pull requests). %{more_info_link}"
@@ -20500,10 +20762,10 @@ msgid "If you want to re-enable two-factor authentication, visit the %{settings_
msgstr "Si vous souhaitez réactiver l'authentification à deux facteurs, visitez la page %{settings_link_to}."
msgid "If you want to remove this email address, visit %{profile_link}"
-msgstr ""
+msgstr "Si vous souhaitez supprimer cette adresse de courriel, consultez %{profile_link}"
msgid "If you want to remove this email address, visit the %{settings_link_to} page."
-msgstr ""
+msgstr "Si vous souhaitez supprimer cette adresse de courriel, allez sur la page %{settings_link_to}."
msgid "If you've purchased or renewed your subscription and have an activation code, please enter it below to start the activation process."
msgstr "Si vous avez acheté ou renouvelé votre abonnement et que vous disposez d'un code d'activation, veuillez le saisir ci-dessous pour lancer le processus d'activation."
@@ -20512,7 +20774,7 @@ msgid "If your HTTP repository is not publicly accessible, add your credentials.
msgstr "Si votre dépôt HTTP n'est pas accessible publiquement, ajoutez vos identifiants."
msgid "Ignore"
-msgstr ""
+msgstr "Ignorer"
msgid "Ignored"
msgstr "Ignoré"
@@ -20561,7 +20823,7 @@ msgstr[0] ""
msgstr[1] ""
msgid "Import CSV"
-msgstr ""
+msgstr "Importer un fichier CSV"
msgid "Import Projects from Gitea"
msgstr "Importe des projets depuis Gitea"
@@ -20633,19 +20895,19 @@ msgid "Import repository"
msgstr "Importer un dépôt"
msgid "Import requirements"
-msgstr ""
+msgstr "Importer des exigences"
msgid "Import started by: %{importInitiator}"
msgstr "Importation démarrée par : %{importInitiator}"
msgid "Import tasks"
-msgstr ""
+msgstr "Importer les tâches"
msgid "Import tasks from Phabricator into issues"
-msgstr ""
+msgstr "Importer des tâches depuis Phabricator et les transformer en tickets"
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
-msgstr ""
+msgstr "L'importation a expiré. Elle a pris plus de %{import_jobs_expiration} secondes"
msgid "ImportAProjectModal|Import from a project"
msgstr "Importer depuis un projet"
@@ -20657,7 +20919,7 @@ msgid "ImportAProjectModal|Import project members"
msgstr "Importer les membres du projet"
msgid "ImportAProjectModal|Only project members (not group members) are imported, and they get the same permissions as the project you import from."
-msgstr ""
+msgstr "Seuls les membres du projet (et non les membres du groupe) sont importés, et ils obtiendront les mêmes permissions que celles du projet depuis lequel vous faites l'importation."
msgid "ImportAProjectModal|Successfully imported"
msgstr "Importé avec succès"
@@ -20666,7 +20928,7 @@ msgid "ImportAProjectModal|Unable to import project members"
msgstr "Impossible d’importer les membres du projet"
msgid "ImportAProjectModal|You're importing members to the %{strongStart}%{name}%{strongEnd} project."
-msgstr ""
+msgstr "Vous importez des membres sur le projet %{strongStart}%{name}%{strongEnd}."
msgid "ImportButtons|Connect repositories from"
msgstr "Connecter des dépôts provenant de"
@@ -20733,6 +20995,9 @@ msgstr "Il n'y a pas de dépôt Git valide à cette URL. Si votre dépôt HTTP n
msgid "Improve customer support with Service Desk"
msgstr "Améliorer le support client avec le Service d'Assistance"
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr "En cas de miroir « pull », votre utilisateur sera l’auteur de tous les événements du flux d’activité résultant d’une mise à jour, comme la création de nouvelles branches ou les nouveaux commits poussés vers des branches existantes."
@@ -21300,12 +21565,18 @@ msgstr "Incident"
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr "Détails de l'incident"
msgid "Incident template (optional)."
msgstr "Modèle d'incident (facultatif)."
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr "%{hours} heures, %{minutes} minutes restantes"
@@ -21313,10 +21584,10 @@ msgid "IncidentManagement|%{minutes} minutes remaining"
msgstr "%{minutes} minutes restantes"
msgid "IncidentManagement|Achieved SLA"
-msgstr ""
+msgstr "SLA atteint"
msgid "IncidentManagement|Acknowledged"
-msgstr ""
+msgstr "Acquitté"
msgid "IncidentManagement|All"
msgstr "Tout"
@@ -21373,13 +21644,13 @@ msgid "IncidentManagement|Medium - S3"
msgstr "Moyenne - S3"
msgid "IncidentManagement|Missed SLA"
-msgstr ""
+msgstr "SLA manqué"
msgid "IncidentManagement|No incidents to display."
msgstr "Aucun incident à afficher."
msgid "IncidentManagement|None"
-msgstr ""
+msgstr "Aucun"
msgid "IncidentManagement|Open"
msgstr "Ouvert(s)"
@@ -21406,7 +21677,7 @@ msgid "IncidentManagement|Severity"
msgstr "Gravité"
msgid "IncidentManagement|Status"
-msgstr ""
+msgstr "État"
msgid "IncidentManagement|There are no closed incidents"
msgstr "Il n’y a aucun incident fermé"
@@ -21592,7 +21863,7 @@ msgid "Includes an MVC structure, mvnw and pom.xml to help you get started"
msgstr "Contient une structure MVC et des fichiers mvnw et pom.xml pour vous aider à démarrer"
msgid "Incoming email"
-msgstr ""
+msgstr "Courriel entrant"
msgid "Incoming!"
msgstr ""
@@ -21616,7 +21887,7 @@ msgid "Index"
msgstr "Indexer"
msgid "Index all projects"
-msgstr ""
+msgstr "Indexer tous les projets"
msgid "Index deletion is canceled"
msgstr "La suppression d'index est annulée"
@@ -21624,6 +21895,9 @@ msgstr "La suppression d'index est annulée"
msgid "Indicates whether this runner can pick jobs without tags"
msgstr "Indique si l’exécuteur peut choisir des tâches sans étiquettes (tags)"
+msgid "Info"
+msgstr "Info"
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr "Informer les utilisateurs qui n'ont pas téléversé de clés SSH qu'ils ne peuvent pas pousser sur SSH tant qu'ils n'en ont pas ajouté une"
@@ -21667,10 +21941,10 @@ msgid "InfrastructureRegistry|You have no Terraform modules in your project"
msgstr "Vous n'avez aucun module Terraform dans votre projet"
msgid "Inherited"
-msgstr ""
+msgstr "Hérité"
msgid "Inherited:"
-msgstr ""
+msgstr "Héritée :"
msgid "Initial default branch name"
msgstr "Nom initial de la branche par défaut"
@@ -21685,13 +21959,13 @@ msgid "Inline math"
msgstr ""
msgid "Input host keys manually"
-msgstr "Entrer les clefs d’hôte manuellement"
+msgstr "Entrer les clés d’hôte manuellement"
msgid "Input the remote repository URL"
msgstr "Entrez l'URL du dépôt distant"
msgid "Insert"
-msgstr ""
+msgstr "Insérer"
msgid "Insert a %{rows}x%{cols} table."
msgstr "Insérer un tableau %{rows}x%{cols}."
@@ -21712,7 +21986,7 @@ msgid "Insert image"
msgstr "Insérer une image"
msgid "Insert link"
-msgstr ""
+msgstr "Insérer un lien"
msgid "Insert row after"
msgstr "Insérer une ligne après"
@@ -21721,16 +21995,16 @@ msgid "Insert row before"
msgstr "Insérer une ligne avant"
msgid "Insert suggestion"
-msgstr ""
+msgstr "Insérer une suggestion"
msgid "Insert table"
msgstr "Insérer un tableau"
msgid "Insights"
-msgstr ""
+msgstr "Statistiques"
msgid "Insights|Configure a custom report for insights into your group processes such as amount of issues, bugs, and merge requests per month. %{linkStart}How do I configure an insights report?%{linkEnd}"
-msgstr ""
+msgstr "Configurez un rapport personnalisé pour obtenir des statistiques sur les processus de votre groupe tels que les quantités de tickets, de bogues et de demandes de fusion par mois. %{linkStart}Comment puis-je configurer un rapport statistique ?%{linkEnd}"
msgid "Insights|Some items are not visible beacuse the project was filtered out in the insights.yml file (see the projects.only config for more information)."
msgstr "Certains éléments ne sont pas visibles car des filtres ont été appliqués au projet via le fichier insights.yml (voir la configuration projects.only pour plus d'informations)."
@@ -21855,7 +22129,7 @@ msgid "Integrations|Clear if using a self-signed certificate."
msgstr "Décochez si un certificat auto-signé est utilisé."
msgid "Integrations|Comment detail:"
-msgstr ""
+msgstr "Détail du commentaire :"
msgid "Integrations|Comment settings:"
msgstr "Paramètres des commentaires :"
@@ -21896,6 +22170,9 @@ msgstr "Activer la vérification SSL"
msgid "Integrations|Enable comments"
msgstr "Activer les commentaires"
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr "Les administrateurs de GitLab peuvent configurer des intégrations dont
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr "Les administrateurs de GitLab peuvent configurer des intégrations dont tous les groupes et projets vont hériter et que ceux-ci utiliseront par défaut. Ces intégrations s'appliquent à tous les projets qui n'ont pas déjà de paramètres personnalisés. Vous pouvez outrepasser les paramètres personnalisés d'un projet si les paramètres sont nécessaires à ce niveau. En savoir plus sur la %{integrations_link_start}gestion d'intégration au niveau du groupe%{link_end}."
+msgid "Integrations|GitLab for Slack app"
+msgstr "Appli GitLab pour Slack"
+
msgid "Integrations|Group-level integration management"
msgstr "Gestion d'intégration au niveau du groupe"
@@ -22035,7 +22315,7 @@ msgid "Integrations|This integration, and inheriting projects were reset."
msgstr "Cette intégration et les projets qui en héritent ont été réinitialisés."
msgid "Integrations|To keep this project going, create a new issue."
-msgstr ""
+msgstr "Pour garder ce projet actif, créez un nouveau ticket."
msgid "Integrations|Trigger"
msgstr "Déclencheur"
@@ -22071,7 +22351,7 @@ msgid "Integrations|Your browser is not supported"
msgstr "Votre navigateur n'est pas pris en charge"
msgid "Integrations|ZenTao issues display here when you create issues in your project in ZenTao."
-msgstr ""
+msgstr "Les tickets ZenTao apparaissent ici lorsque vous créez des tickets dans votre projet dans ZenTao."
msgid "Integrations|can't exceed %{recipients_limit}"
msgstr "ne peut pas dépasser %{recipients_limit}"
@@ -22098,7 +22378,7 @@ msgid "Internal - The project can be accessed by any logged in user except exter
msgstr "Interne - Le projet est accessible par tout utilisateur connecté, à l'exception des utilisateurs externes."
msgid "Internal error occurred while delivering this webhook."
-msgstr "Une erreur interne est survenue lors de l'exécution de ce webhook."
+msgstr "Une erreur interne est survenue lors de l'exécution de ce crochet web."
msgid "Internal note"
msgstr "Note interne"
@@ -22110,7 +22390,7 @@ msgid "Internal users cannot be deactivated"
msgstr "Les utilisateurs internes ne peuvent pas être désactivés"
msgid "Interval"
-msgstr ""
+msgstr "Intervalle"
msgid "Interval Pattern"
msgstr "Modèle d’intervalle"
@@ -22134,7 +22414,7 @@ msgid "Invalid URL: %{url}"
msgstr "URL non valide : %{url}"
msgid "Invalid date"
-msgstr ""
+msgstr "Date non valide"
msgid "Invalid date format. Please use UTC format as YYYY-MM-DD"
msgstr ""
@@ -22143,10 +22423,10 @@ msgid "Invalid date range"
msgstr "Plage de dates non valide"
msgid "Invalid feature"
-msgstr ""
+msgstr "Fonctionnalité non valide"
msgid "Invalid field"
-msgstr ""
+msgstr "Champ non valide"
msgid "Invalid file format with specified file type"
msgstr "Format de fichier non valide avec le type de fichier spécifié"
@@ -22161,7 +22441,7 @@ msgid "Invalid hash"
msgstr "Hash non valide"
msgid "Invalid import params"
-msgstr ""
+msgstr "Paramètres d'importation non valides"
msgid "Invalid input, please avoid emojis"
msgstr ""
@@ -22182,13 +22462,13 @@ msgid "Invalid repository bundle for snippet with id %{snippet_id}"
msgstr "Bundle de dépôt non valide pour l'extrait d'id %{snippet_id}"
msgid "Invalid repository path"
-msgstr ""
+msgstr "Chemin de dépôt non valide"
msgid "Invalid rule"
msgstr "Règle non valide"
msgid "Invalid server response"
-msgstr ""
+msgstr "Réponse du serveur non valide"
msgid "Invalid status"
msgstr "État non valide"
@@ -22197,7 +22477,7 @@ msgid "Invalid two-factor code."
msgstr "Code à deux facteurs invalide."
msgid "Invalid yaml"
-msgstr ""
+msgstr "Yaml non valide"
msgid "Invalidated"
msgstr ""
@@ -22230,10 +22510,10 @@ msgid "Invite members"
msgstr "Inviter des membres"
msgid "InviteEmail|%{inviter} invited you to join the %{project_or_group_name} %{project_or_group} as a %{role}"
-msgstr ""
+msgstr "%{inviter} vous a invité à rejoindre le %{project_or_group} %{project_or_group_name} en tant que %{role}"
msgid "InviteEmail|%{inviter} invited you to join the %{strong_start}%{project_or_group_name}%{strong_end}%{br_tag}%{project_or_group} as a %{role}"
-msgstr ""
+msgstr "%{inviter} vous a invité à rejoindre le %{project_or_group}%{br_tag}%{strong_start}%{project_or_group_name}%{strong_end} en tant que %{role}"
msgid "InviteEmail|%{project_or_group} details"
msgstr "Détails du %{project_or_group}"
@@ -22248,7 +22528,7 @@ msgid "InviteEmail|Join your team on GitLab! %{inviter} invited you to %{project
msgstr "Rejoignez votre équipe sur GitLab ! %{inviter} vous a invité sur %{project_or_group_name}"
msgid "InviteEmail|Join your team on GitLab! You are invited to %{project_or_group_name}"
-msgstr ""
+msgstr "Rejoignez votre équipe sur GitLab ! Vous êtes invité sur %{project_or_group_name}"
msgid "InviteEmail|Projects are used to host and collaborate on code, track issues, and continuously build, test, and deploy your app with built-in GitLab CI/CD."
msgstr "Les projets sont utilisés pour héberger et collaborer sur du code, suivre les tickets ainsi que construire, tester et déployer en continu votre application grâce à GitLab CI/CD qui est intégré."
@@ -22277,6 +22557,9 @@ msgstr "Invitez vos collègues"
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr "Nous avons remarqué que vous n’avez invité personne dans ce groupe. Invitez vos collègues afin de pouvoir discuter des tickets, collaborer sur les demandes de fusion et partager vos connaissances."
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr "Pour pouvoir avoir davantage de membres, le propriétaire de cet espace de noms peut %{trialLinkStart}commencer un essai%{trialLinkEnd} ou %{upgradeLinkStart}effectuer une mise à niveau%{upgradeLinkEnd} vers une édition payante."
@@ -22325,8 +22608,8 @@ msgstr "Gérer les membres"
msgid "InviteMembersModal|Members were successfully added"
msgstr "Les membres ont été ajoutés avec succès"
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
-msgstr "Veuillez sélectionner des membres ou saisir des adresses de courriel à inviter"
+msgid "InviteMembersModal|Please add members to invite"
+msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr "Examinez les erreurs d'invitation et réessayez :"
@@ -22364,7 +22647,7 @@ msgid "InviteMembersModal|To get more members an owner of the group can %{trialL
msgstr "Pour pouvoir avoir davantage de membres, un propriétaire de ce groupe peut %{trialLinkStart}commencer une période d'essai%{trialLinkEnd} ou %{upgradeLinkStart}effectuer une mise à niveau%{upgradeLinkEnd} vers une offre payante."
msgid "InviteMembersModal|To invite new users to this namespace, you must remove existing users. You can still add existing namespace users."
-msgstr ""
+msgstr "Pour inviter de nouveaux utilisateurs sur cet espace de noms, vous devez supprimer des utilisateurs existants. Vous pouvez quand même ajouter des utilisateurs d'espaces de noms qui existent déjà."
msgid "InviteMembersModal|Username or email address"
msgstr "Nom d’utilisateur ou adresse de messagerie"
@@ -22454,7 +22737,7 @@ msgid "InviteReminderEmail|This is a friendly reminder that %{inviter} invited y
msgstr "Ceci est pour vous rappeler que %{inviter} vous a invité à rejoindre le %{project_or_group} %{strong_start}%{project_or_group_name}%{strong_end} en tant que %{role}."
msgid "Invited"
-msgstr ""
+msgstr "Invité"
msgid "Invited group allowed email domains must contain a subset of the allowed email domains of the root ancestor group. Go to the group's 'Settings &gt; General' page and check 'Restrict membership by email domain'."
msgstr "Les domaines de messagerie autorisés du groupe invité doivent contenir un sous-ensemble des domaines de messagerie autorisés du groupe au sommet de la hiérarchie. Allez à la page « Paramètres &gt; Général » du groupe et cochez « Restreindre l'adhésion par domaine de messagerie »."
@@ -22499,7 +22782,7 @@ msgid "Is blocked by"
msgstr "Est bloqué par"
msgid "Is using license seat:"
-msgstr ""
+msgstr "Utilise un siège de la licence :"
msgid "Is using seat"
msgstr ""
@@ -22508,13 +22791,13 @@ msgid "IssuableEvents|assigned to"
msgstr ""
msgid "IssuableEvents|removed review request for"
-msgstr ""
+msgstr "a supprimé la demande de revue pour"
msgid "IssuableEvents|requested review from"
msgstr ""
msgid "IssuableEvents|unassigned"
-msgstr ""
+msgstr "a désassigné"
msgid "IssuableStatus|%{wi_type} created %{created_at} by "
msgstr "%{wi_type} créé(e) %{created_at} par "
@@ -22736,7 +23019,7 @@ msgid "Issues must match this scope to appear in this list."
msgstr "Les tickets doivent être dans cette portée pour apparaître dans cette liste."
msgid "Issues with comments, merge requests with diffs and comments, labels, milestones, snippets, and other project entities"
-msgstr ""
+msgstr "Tickets avec leurs commentaires, demandes de fusion avec leurs diffs et leurs commentaires, étiquettes, jalons, extraits et autres entités du projet"
msgid "Issues with label %{label}"
msgstr "Tickets avec l'étiquette %{label}"
@@ -22775,7 +23058,7 @@ msgid "IssuesAnalytics|Total:"
msgstr "Total :"
msgid "Issues|Move selected"
-msgstr ""
+msgstr "Déplacer la sélection"
msgid "Issues|Tasks and test cases can not be moved."
msgstr "Les tâches et les scénarios de test ne peuvent pas être déplacés."
@@ -22792,9 +23075,6 @@ msgstr "Une erreur s'est produite lors du déplacement des tickets."
msgid "Issue|Title"
msgstr "Titre"
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr "L'interface web ne permet pas de %{action} des fichiers qui sont enregistrés sur LFS"
-
msgid "It looks like you have some draft commits in this branch."
msgstr "Il semble que vous ayez des brouillons de commits dans cette branche."
@@ -22904,10 +23184,10 @@ msgid "Iterations|Description"
msgstr "Description"
msgid "Iterations|Done"
-msgstr ""
+msgstr "Terminées"
msgid "Iterations|Due date"
-msgstr ""
+msgstr "Date d'échéance"
msgid "Iterations|Duration"
msgstr "Durée"
@@ -22933,9 +23213,6 @@ msgstr "Erreur lors du chargement des cadences d'itération."
msgid "Iterations|Iteration cadences"
msgstr "Cadences d'itération"
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr "Les itérations sont planifiées pour commencer les %{weekday}s."
@@ -23030,7 +23307,7 @@ msgid "January"
msgstr "janvier"
msgid "Japanese language support using"
-msgstr ""
+msgstr "Prise en charge de la langue japonaise en utilisant"
msgid "Jira display name"
msgstr ""
@@ -23069,7 +23346,7 @@ msgid "JiraConnect|Could not fetch user information from Jira. Check the permiss
msgstr "Impossible de récupérer les informations utilisateur depuis Jira. Vérifiez les permissions dans Jira et réessayez."
msgid "JiraConnect|Create branch for Jira issue %{jiraIssue}"
-msgstr ""
+msgstr "Créer une branche pour le ticket Jira %{jiraIssue}"
msgid "JiraConnect|Failed to create branch."
msgstr "Échec de la création de la branche."
@@ -23084,7 +23361,7 @@ msgid "JiraConnect|Jira Connect Application ID"
msgstr "ID de l'Application Jira Connect"
msgid "JiraConnect|Jira Connect Proxy URL"
-msgstr ""
+msgstr "URL du proxy Jira Connect"
msgid "JiraConnect|New branch was successfully created."
msgstr "La nouvelle branche a été créée avec succès."
@@ -23123,7 +23400,7 @@ msgid "JiraRequest|The credentials for accessing Jira are not valid. Check your
msgstr "Les identifiants pour accéder à Jira ne sont pas valides. Vérifiez les %{docs_link_start}identifiants de votre intégration Jira%{docs_link_end} et réessayez."
msgid "JiraService| on branch %{branch_link}"
-msgstr ""
+msgstr " sur la branche %{branch_link}"
msgid "JiraService|%{jiraDocsLinkStart}Enable the Jira integration%{jiraDocsLinkEnd} to view your Jira issues in GitLab."
msgstr "%{jiraDocsLinkStart}Activer l'intégration de Jira%{jiraDocsLinkEnd} pour voir vos tickets Jira dans GitLab."
@@ -23165,7 +23442,7 @@ msgid "JiraService|Enter new password or API token"
msgstr "Entrez un nouveau mot de passe ou un jeton d'API"
msgid "JiraService|Events for %{noteable_model_name} are disabled."
-msgstr ""
+msgstr "Les événements pour %{noteable_model_name} sont désactivés."
msgid "JiraService|Failed to load Jira issue. View the issue in Jira, or reload the page."
msgstr "Échec du chargement du ticket Jira. Affichez le ticket dans Jira ou rechargez la page."
@@ -23306,13 +23583,13 @@ msgid "Job has been erased"
msgstr "La tâche a été supprimée"
msgid "Job has been successfully erased!"
-msgstr ""
+msgstr "La tâche a été effacée avec succès !"
msgid "Job has wrong arguments format."
-msgstr ""
+msgstr "Le format des arguments de la tâche est incorrect."
msgid "Job is stuck. Check runners."
-msgstr ""
+msgstr "La tâche est bloquée. Vérifiez les exécuteurs."
msgid "Job logs and artifacts"
msgstr "Artéfacts et journaux des tâches"
@@ -23368,9 +23645,6 @@ msgstr "Aucune tâche à afficher"
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr "La recherche de texte brut n’est actuellement pas prise en charge pour la fonction de recherche filtrée. Veuillez utiliser les jetons de recherche disponibles."
-msgid "Jobs|Status"
-msgstr "État"
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr "Une erreur s'est produite lors de la récupération des tâches en échec."
@@ -23426,7 +23700,7 @@ msgid "Job|Failed"
msgstr "Échouée"
msgid "Job|Finished at"
-msgstr ""
+msgstr "Terminée"
msgid "Job|Job artifacts"
msgstr "Artefacts de la tâche"
@@ -23435,7 +23709,7 @@ msgid "Job|Job has been erased"
msgstr "La tâche a été effacée"
msgid "Job|Job has been erased by %{userLink}"
-msgstr ""
+msgstr "La tâche a été effacée par %{userLink}"
msgid "Job|Job log search"
msgstr "Recherche dans le journal de tâches"
@@ -23465,7 +23739,7 @@ msgid "Job|Retry"
msgstr "Réessayer"
msgid "Job|Run again"
-msgstr ""
+msgstr "Réexécuter"
msgid "Job|Running"
msgstr "En cours"
@@ -23504,23 +23778,26 @@ msgid "Job|The artifacts will be removed"
msgstr "Les artéfacts seront supprimés"
msgid "Job|There was a problem retrying the failed job."
-msgstr ""
+msgstr "Une erreur s'est produite lors de la nouvelle tentative de la tâche échouée."
msgid "Job|These artifacts are the latest. They will not be deleted (even if expired) until newer artifacts are available."
msgstr "Ces artéfacts sont les plus récents. Ils ne seront pas supprimés (même s'ils ont expirés) jusqu'à ce que de nouveaux artéfacts soient disponibles."
msgid "Job|This job failed because the necessary resources were not successfully created."
-msgstr ""
+msgstr "Cette tâche a échoué car les ressources nécessaires n'ont pas été créées avec succès."
msgid "Job|This job is stuck because of one of the following problems. There are no active runners online, no runners for the %{linkStart}protected branch%{linkEnd}, or no runners that match all of the job's tags:"
msgstr "Cette tâche est bloquée en raison de l'un des problèmes suivants. Il n'y a aucun exécuteur actif en ligne, aucun exécuteur pour la %{linkStart}branche protégée%{linkEnd}, ou aucun exécuteur qui corresponde à toutes les étiquettes de la tâche :"
msgid "Job|This job is stuck because the project doesn't have any runners online assigned to it."
-msgstr ""
+msgstr "Cette tâche est bloquée car aucun exécuteur en ligne n'a été assigné à ce projet."
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr "Cette tâche est bloquée parce que vous n'avez aucun exécuteur actif pouvant exécuter cette tâche."
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr "En attente de ressource"
@@ -23543,7 +23820,7 @@ msgid "Join GitLab today! You and your team can plan, build, and ship secure cod
msgstr "Rejoignez GitLab dès aujourd'hui ! Vous et votre équipe pouvez planifier, créer et expédier du code sécurisé dans une seule application. Commencez ici gratuitement !"
msgid "Join Zoom meeting"
-msgstr ""
+msgstr "Rejoindre la réunion Zoom"
msgid "Join a project"
msgstr "Rejoindre un projet"
@@ -23576,7 +23853,7 @@ msgid "Just me"
msgstr "Moi uniquement"
msgid "K8s pod health"
-msgstr ""
+msgstr "Santé du pod K8s"
msgid "KEY"
msgstr "CLÉ"
@@ -23588,7 +23865,7 @@ msgid "Keep artifacts from most recent successful jobs"
msgstr "Conserver les artéfacts des tâches réussies les plus récentes"
msgid "Keep divergent refs"
-msgstr ""
+msgstr "Conserver les références divergentes"
msgid "Keeping all SAST analyzers enabled future-proofs the project in case new languages are added later on. Determining which analyzers apply is a process that consumes minimal resources and adds minimal time to the pipeline. Leaving all SAST analyzers enabled ensures maximum coverage."
msgstr "Le fait de garder tous les analyseurs SAST activés couvre le projet dans le cas où de nouveaux langages sont ajoutés après coup. La détermination des analyseurs à appliquer est un processus qui consomme des ressources minimes et qui n'ajoute au pipeline qu'une durée minimale. Laisser tous les analyseurs SAST activés garantit une couverture maximale."
@@ -23602,6 +23879,9 @@ msgstr "Clé"
msgid "Key (PEM)"
msgstr "Clé (PEM)"
+msgid "Key result"
+msgstr "Résultat clé"
+
msgid "Key:"
msgstr "Clé :"
@@ -23657,7 +23937,7 @@ msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
msgstr "Le temps de création de la grappe de serveurs Kubernetes dépasse le délai d’expiration : %{timeout}"
msgid "Kubernetes cluster integration and resources are being removed."
-msgstr ""
+msgstr "L'intégration et les ressources des grappes de serveurs Kubernetes sont en cours de suppression."
msgid "Kubernetes cluster integration was successfully removed."
msgstr "L’intégration de la grappe de serveurs Kubernetes a été supprimée avec succès."
@@ -23671,9 +23951,6 @@ msgstr "Grappes de serveurs Kubernetes"
msgid "Kubernetes deployment not found"
msgstr "Déploiement de Kubernetes introuvable"
-msgid "Kubernetes error: %{error_code}"
-msgstr "Erreur Kubernetes : %{error_code}"
-
msgid "LDAP"
msgstr "LDAP"
@@ -23748,15 +24025,18 @@ msgstr "Étiquettes"
msgid "Labels"
msgstr "Étiquettes"
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr "Des étiquettes peuvent être appliquées à %{features}. Les étiquettes de groupe sont disponibles pour tout projet au sein du groupe."
-
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "Les étiquettes peuvent être appliquées aux tickets et aux demandes de fusion afin de les catégoriser."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
+msgstr "Les étiquettes peuvent être appliquées aux tickets et aux demandes de fusion. Les étiquettes de groupe sont disponibles pour n'importe quel projet au sein du groupe."
+
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr "Des étiquettes peuvent être appliquées aux tickets et aux demandes de fusion. Placer une étoile sur une étiquette rend celle-ci prioritaire."
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr "Les étiquettes peuvent être appliquées aux tickets, aux demandes de fusion et aux épopées. Les étiquettes de groupe sont disponibles pour n'importe quel projet au sein du groupe."
+
msgid "Labels with no issues in this iteration:"
msgstr "Étiquettes sans ticket dans cette itération :"
@@ -23843,6 +24123,9 @@ msgstr "Dernière modification par %{link_start}%{avatar} %{name}%{link_end}"
msgid "Last event"
msgstr "Dernier événement"
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr "Dernière modification"
@@ -23871,7 +24154,7 @@ msgid "Last sign-in at:"
msgstr "Dernière connexion le :"
msgid "Last successful update"
-msgstr ""
+msgstr "Dernière mise à jour réussie"
msgid "Last successful update %{time}."
msgstr "Dernière mise à jour réussie %{time}."
@@ -23892,13 +24175,13 @@ msgid "Last updated %{time} ago"
msgstr "Dernière mise à jour il y a %{time}"
msgid "Last used"
-msgstr ""
+msgstr "Dernière utilisation"
msgid "Last used %{last_used_at} ago"
-msgstr ""
+msgstr "Dernière utilisation il y a %{last_used_at}"
msgid "Last used on:"
-msgstr ""
+msgstr "Dernière utilisation le :"
msgid "Last week"
msgstr "La dernière semaine"
@@ -23948,6 +24231,9 @@ msgstr "En savoir plus"
msgid "Learn More."
msgstr "En savoir plus."
+msgid "Learn about signing commits with SSH keys."
+msgstr "En savoir plus sur la signature des validations avec des clés SSH."
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr "Apprenez comment %{link_start}contribuer aux modèles intégrés%{link_end}"
@@ -24006,10 +24292,10 @@ msgid "Learn more about linking issues and incidents"
msgstr "En savoir plus sur la liaison de tickets et d'incidents"
msgid "Learn more about max seats used"
-msgstr ""
+msgstr "En savoir plus sur le nombre max de sièges utilisés"
msgid "Learn more about seats owed"
-msgstr ""
+msgstr "En savoir plus sur les sièges dus"
msgid "Learn more about shards and replicas in the %{configuration_link_start}Advanced Search configuration%{configuration_link_end} documentation. Changes don't take place until you %{recreated_link_start}recreate%{recreated_link_end} the index."
msgstr "En savoir plus sur les fragments et les réplicas grâce à la documentation sur la %{configuration_link_start}configuration de Recherche Avancée%{configuration_link_end}. Les changements ne seront pas effectifs tant que vous n'aurez pas %{recreated_link_start}recréé%{recreated_link_end} l'index."
@@ -24168,7 +24454,7 @@ msgid "Less restrictive visibility"
msgstr "Visibilité moins restrictive"
msgid "Let's Encrypt does not accept emails on example.com"
-msgstr ""
+msgstr "Let's Encrypt n'accepte pas les courriels sur example.com"
msgid "Let's Encrypt is a free, automated, and open certificate authority (CA) that gives digital certificates in order to enable HTTPS (SSL/TLS) for websites. Learn more about Let's Encrypt configuration by following the %{docs_link_start}documentation on GitLab Pages%{docs_link_end}."
msgstr "Let's Encrypt est une autorité de certification (CA) gratuite, automatisée et ouverte, qui fournit des certificats numériques afin d'activer HTTPS (SSL/TLS) pour les sites Web. En savoir plus sur la configuration de Let's Encrypt en suivant la %{docs_link_start}documentation de GitLab Pages%{docs_link_end}."
@@ -24291,7 +24577,7 @@ msgid "LicenseCompliance|Update approvals"
msgstr ""
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
-msgstr ""
+msgstr "Vous êtes sur le point de supprimer la licence, %{name}, de ce projet."
msgid "LicenseManagement|Allowed"
msgstr "Autorisée"
@@ -24303,10 +24589,10 @@ msgid "LicenseManagement|Uncategorized"
msgstr ""
msgid "Licensed Enterprise Edition features can be used if the project namespace's plan includes the feature, or if the project is public."
-msgstr ""
+msgstr "Les fonctionnalités de l'Édition Entreprise sous licence peuvent être utilisées si le forfait de l'espace de noms du projet inclut la fonctionnalité, ou si le projet est public."
msgid "Licensed Features"
-msgstr ""
+msgstr "Fonctionnalités sous licence"
msgid "Licensed to:"
msgstr "Licence accordée à :"
@@ -24393,7 +24679,7 @@ msgid "Limit the size of Sidekiq jobs stored in Redis."
msgstr "Limiter la taille des tâches Sidekiq stockées dans Redis."
msgid "Limiting mode"
-msgstr ""
+msgstr "Mode de limitation"
msgid "Line changes"
msgstr "Lignes changées"
@@ -24405,7 +24691,7 @@ msgid "Link %{issuableType}s together to show that they're related or that one i
msgstr ""
msgid "Link %{issuableType}s together to show that they're related."
-msgstr ""
+msgstr "Reliez les %{issuableType}s pour mettre en évidence leur relation."
msgid "Link (optional)"
msgstr "Lien (facultatif)"
@@ -24420,7 +24706,7 @@ msgid "Link an external wiki from the project's sidebar. %{docs_link}"
msgstr "Lier un wiki externe à la barre latérale du projet. %{docs_link}"
msgid "Link copied"
-msgstr ""
+msgstr "Lien copié"
msgid "Link text"
msgstr "Texte de lien"
@@ -24438,10 +24724,10 @@ msgid "Link to your Grafana instance."
msgstr "Créer un lien vers votre instance Grafana."
msgid "Linked emails (%{email_count})"
-msgstr ""
+msgstr "Adresses de courriel liées au compte (%{email_count})"
msgid "Linked epics"
-msgstr ""
+msgstr "Épopées liées"
msgid "Linked incidents or issues"
msgstr "Incidents ou tickets liés"
@@ -24456,7 +24742,7 @@ msgid "LinkedIn:"
msgstr "LinkedIn :"
msgid "LinkedPipelines|%{counterLabel} more downstream pipelines"
-msgstr ""
+msgstr "%{counterLabel} pipelines de plus en aval"
msgid "LinkedResources|Add"
msgstr "Ajouter"
@@ -24501,7 +24787,7 @@ msgid "LinkedResources|Use this space to add links to the resources your team ne
msgstr "Utilisez cet espace pour ajouter des liens vers les ressources dont votre équipe a besoin pour travailler à la résolution de l'incident."
msgid "Links"
-msgstr ""
+msgstr "Liens"
msgid "List"
msgstr "Liste"
@@ -24519,13 +24805,13 @@ msgid "List of suitable GCP locations"
msgstr "Liste des emplacements GCP appropriés"
msgid "List of users who are allowed to exceed the rate limit. Example: username1, username2"
-msgstr ""
+msgstr "Liste des utilisateurs qui sont autorisés à dépasser la limite de fréquence. Exemple : nomutilisateur1, nomutilisateur2"
msgid "List options"
msgstr "Options des listes"
msgid "List settings"
-msgstr ""
+msgstr "Paramètres de la liste"
msgid "List the merge requests that must be merged before this one."
msgstr ""
@@ -24549,7 +24835,7 @@ msgid "Load more users"
msgstr "Charger plus d'utilisateurs"
msgid "Loading"
-msgstr ""
+msgstr "Chargement"
msgid "Loading %{name}"
msgstr "Chargement de %{name}"
@@ -24564,7 +24850,7 @@ msgid "Loading more"
msgstr ""
msgid "Loading snippet"
-msgstr ""
+msgstr "Chargement de l'extrait"
msgid "Loading the GitLab IDE..."
msgstr "Chargement de l’EDI de GitLab…"
@@ -24582,7 +24868,7 @@ msgid "Localization"
msgstr "Localisation"
msgid "Location"
-msgstr ""
+msgstr "Emplacement"
msgid "Location:"
msgstr "Emplacement :"
@@ -24606,7 +24892,7 @@ msgid "Lock not found"
msgstr "Verrou non trouvé"
msgid "Lock the discussion"
-msgstr ""
+msgstr "Verrouiller la discussion"
msgid "Lock this %{issuableDisplayName}? Only %{strongStart}project members%{strongEnd} will be able to comment."
msgstr "Verrouiller ce(tte) %{issuableDisplayName} ? Seuls les %{strongStart}membres du projet%{strongEnd} peuvent mettre des commentaires."
@@ -24621,14 +24907,14 @@ msgid "Locked Files"
msgstr "Fichiers verrouillés"
msgid "Locked by %{fileLockUserName}"
+msgstr "Verrouillé par %{fileLockUserName}"
+
+msgid "Locked files"
msgstr ""
msgid "Locked the discussion."
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
-msgstr "Les verrous permettent de verrouiller un fichier ou un dossier spécifique."
-
msgid "Locks the discussion."
msgstr "Verrouille la discussion."
@@ -24677,6 +24963,9 @@ msgstr "Le logo sera supprimé. Êtesâ€vous sûr(e) ?"
msgid "Logs"
msgstr "Journaux"
+msgid "Low - S4"
+msgstr "Basse - S4"
+
msgid "Low vulnerabilities present"
msgstr "Vulnérabilités faibles présentes"
@@ -24687,10 +24976,10 @@ msgid "MD5"
msgstr "MD5"
msgid "MERGED"
-msgstr ""
+msgstr "FUSIONNÉE"
msgid "ML Experiments"
-msgstr ""
+msgstr "Expériences d'AA"
msgid "MR widget|Back to the merge request"
msgstr "Revenir à la demande de fusion"
@@ -24708,13 +24997,13 @@ msgid "MRApprovals|Approvals"
msgstr "Approbations"
msgid "MRApprovals|Approved by"
-msgstr ""
+msgstr "Approuvée par"
msgid "MRApprovals|Approvers"
msgstr "Approbateurs"
msgid "MRApprovals|Commented by"
-msgstr "Mise en commentaire par"
+msgstr "Commentée par"
msgid "MRDiffFile|Changes are too large to be shown."
msgstr "Les modifications sont trop importantes pour être affichées."
@@ -24723,16 +25012,16 @@ msgid "MRDiffFile|View file @ %{commitSha}"
msgstr "Voir le fichier @ %{commitSha}"
msgid "MRDiff|Show changes only"
-msgstr ""
+msgstr "Afficher uniquement les modifications"
msgid "MRDiff|Show full file"
-msgstr ""
+msgstr "Afficher le fichier complet"
msgid "Machine Learning Experiment Tracking is in Incubating Phase"
-msgstr ""
+msgstr "Le Suivi d'Expérience d'Apprentissage Automatique est à l'Étape de l'Incubation"
msgid "Machine Learning Experiments"
-msgstr ""
+msgstr "Expériences d'Apprentissage Automatique"
msgid "Made this %{type} confidential."
msgstr ""
@@ -24759,7 +25048,7 @@ msgid "Make %{type} confidential"
msgstr ""
msgid "Make adjustments to how your GitLab instance is set up."
-msgstr ""
+msgstr "Effectuez des ajustements sur la façon dont votre instance GitLab est configurée."
msgid "Make and review changes in the browser with the Web IDE"
msgstr "Effectuer et examiner les modifications dans le navigateur avec l'EDI Web"
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr "Gérer les fonctionnalités de l'EDI Web."
@@ -24807,7 +25099,7 @@ msgid "Manage labels"
msgstr "Gérer les étiquettes"
msgid "Manage members"
-msgstr ""
+msgstr "Gérer les membres"
msgid "Manage milestones"
msgstr "Gérer les jalons"
@@ -24828,7 +25120,7 @@ msgid "Manage your subscription"
msgstr "Gérer votre abonnement"
msgid "Managed Account"
-msgstr ""
+msgstr "Compte Géré"
msgid "Manifest"
msgstr "Manifeste"
@@ -24915,7 +25207,7 @@ msgid "MarkdownEditor|Add strikethrough text (%{modifier_key}⇧X)"
msgstr "Ajouter un texte barré (%{modifier_key}⇧X)"
msgid "MarkdownEditor|Click to expand"
-msgstr ""
+msgstr "Cliquez pour développer"
msgid "MarkdownEditor|Indent line (%{modifierKey}])"
msgstr ""
@@ -24945,7 +25237,7 @@ msgid "Marked as draft. Can only be merged when marked as ready."
msgstr "Marqué comme brouillon. Ne peut être fusionné que si marqué comme étant prêt."
msgid "Marked as ready. Merging is now allowed."
-msgstr ""
+msgstr "Marquée comme prête. La fusion est maintenant autorisée."
msgid "Marked this %{noun} as ready."
msgstr ""
@@ -24996,10 +25288,10 @@ msgid "MattermostService|After you configure the integration, view your new Matt
msgstr "Après avoir configuré l’intégration, affichez vos nouvelles commandes Mattermost en entrant"
msgid "MattermostService|Command trigger word"
-msgstr ""
+msgstr "Mot déclencheur de la commande"
msgid "MattermostService|Fill in the word that works best for your team."
-msgstr ""
+msgstr "À remplir avec le mot qui convient le mieux à votre équipe."
msgid "MattermostService|Request URL"
msgstr "URL de requête"
@@ -25008,10 +25300,10 @@ msgid "MattermostService|Request method"
msgstr "Méthode de requête"
msgid "MattermostService|Response icon"
-msgstr ""
+msgstr "Icône pour la réponse"
msgid "MattermostService|Response username"
-msgstr ""
+msgstr "Nom d'utilisateur pour la réponse"
msgid "MattermostService|Suggestions:"
msgstr "Suggestions :"
@@ -25023,7 +25315,7 @@ msgid "Max 100,000 events"
msgstr "100000 événements max"
msgid "Max Value"
-msgstr ""
+msgstr "Valeur Maxi"
msgid "Max authenticated Git LFS requests per period per user"
msgstr "Nombre maximal de requêtes Git LFS authentifiées par période et par utilisateur"
@@ -25035,7 +25327,7 @@ msgid "Max role"
msgstr "Rôle max"
msgid "Max seats used"
-msgstr ""
+msgstr "Max de sièges utilisés"
msgid "Max session time"
msgstr "Durée maximale de session"
@@ -25092,7 +25384,7 @@ msgid "Maximum bulk request size (MiB)"
msgstr "Taille maximale de requête en bloc (Mio)"
msgid "Maximum capacity"
-msgstr ""
+msgstr "Capacité maximale"
msgid "Maximum character limit - %{limit}"
msgstr "Limite maximale de caractères - %{limit}"
@@ -25170,7 +25462,7 @@ msgid "Maximum number of %{name} (%{count}) exceeded"
msgstr ""
msgid "Maximum number of changes (branches or tags) in a single push for which webhooks and services trigger (default is 3)."
-msgstr ""
+msgstr "Nombre maximum de modifications (branches ou étiquettes) au sein d'une poussée unique pour lequel les crochets web et les services se déclenchent (3 par défaut)."
msgid "Maximum number of comments exceeded"
msgstr ""
@@ -25191,7 +25483,7 @@ msgid "Maximum number of requests per minute for an unauthenticated IP address"
msgstr "Nombre maximum de requêtes par minute pour une adresse IP non authentifiée"
msgid "Maximum number of requests per minute for each raw path (default is 300). Set to 0 to disable throttling."
-msgstr ""
+msgstr "Nombre maximum de requêtes par minute pour chaque chemin brut (la valeur par défaut est 300). Définir à 0 pour désactiver la limitation."
msgid "Maximum number of unique IP addresses per user."
msgstr "Nombre maximum d'adresses IP uniques par utilisateur."
@@ -25221,13 +25513,13 @@ msgid "Maximum push size"
msgstr "Taille maximale de poussée"
msgid "Maximum push size (MB)"
-msgstr ""
+msgstr "Taille maximale de poussée (Mo)"
msgid "Maximum requests per 10 minutes per user"
-msgstr ""
+msgstr "Nombre maximum de requêtes par période de 10 minutes et par utilisateur"
msgid "Maximum requests per minute"
-msgstr ""
+msgstr "Nombre maximum de requêtes par minute"
msgid "Maximum running slices"
msgstr "Nombre maximum de tranches en cours d'exécution"
@@ -25242,10 +25534,10 @@ msgid "Maximum size of Elasticsearch bulk indexing requests."
msgstr "Taille maximale des requêtes d'indexation en bloc Elasticsearch."
msgid "Maximum size of export files."
-msgstr ""
+msgstr "Taille maximale des fichiers d'exportation."
msgid "Maximum size of import files."
-msgstr ""
+msgstr "Taille maximale des fichiers d'importation."
msgid "Maximum size of individual attachments in comments."
msgstr "Taille maximale des pièces jointes individuelles dans les commentaires."
@@ -25280,6 +25572,9 @@ msgstr "Temps moyen jusqu'à la fusion"
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr "Mesurés en octets de code. Hors code généré ou vendu."
+msgid "Medium - S3"
+msgstr "Moyenne - S3"
+
msgid "Medium timeout"
msgstr "Délai d'expiration moyen"
@@ -25301,6 +25596,12 @@ msgstr "%{member_name} vous a invité à rejoindre GitLab"
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr "Invitation à rejoindre le %{project_or_group} %{project_or_group_name}"
+msgid "MemberRole|can't be changed"
+msgstr "ne peut pas être modifié"
+
+msgid "MemberRole|must be top-level namespace"
+msgstr "doit être un espace de noms de premier niveau"
+
msgid "Members"
msgstr "Membres"
@@ -25372,7 +25673,7 @@ msgid "Members|Are you sure you want to remove this orphaned member from \"%{sou
msgstr "Êtes-vous sûr(e) de vouloir supprimer ce membre orphelin de « %{source} » ?"
msgid "Members|Are you sure you want to revoke the invitation for %{inviteEmail} to join \"%{source}\""
-msgstr ""
+msgstr "Êtes-vous sûr(e) de vouloir révoquer l'invitation pour %{inviteEmail} à rejoindre « %{source} » ?"
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr "Êtes-vous sûr(e) de vouloir retirer votre demande d'accès pour « %{source} »"
@@ -25447,7 +25748,7 @@ msgid "Menu"
msgstr "Menu"
msgid "Merge"
-msgstr ""
+msgstr "Fusionner"
msgid "Merge Conflicts"
msgstr ""
@@ -25468,7 +25769,7 @@ msgid "Merge Requests in Review"
msgstr "Demandes de fusion en Examen"
msgid "Merge Requests merged"
-msgstr ""
+msgstr "Demandes de fusion fusionnées"
msgid "Merge automatically (%{strategy})"
msgstr "Fusionner automatiquement (%{strategy})"
@@ -25489,25 +25790,25 @@ msgid "Merge blocked: the source branch must be rebased onto the target branch."
msgstr "Fusion bloquée : la branche source doit être rebasée sur la branche cible."
msgid "Merge commit SHA"
-msgstr ""
+msgstr "SHA du commit de fusion"
msgid "Merge commit message"
-msgstr ""
+msgstr "Message du commit de fusion"
msgid "Merge details"
-msgstr ""
+msgstr "Détails de la fusion"
msgid "Merge events"
msgstr "Événements de fusion"
msgid "Merge immediately"
-msgstr ""
+msgstr "Fusionner immédiatement"
msgid "Merge in progress"
msgstr ""
msgid "Merge locally"
-msgstr ""
+msgstr "Fusionner localement"
msgid "Merge options"
msgstr "Options de fusion"
@@ -25519,7 +25820,7 @@ msgid "Merge request %{mr_link} was reviewed by %{mr_author}"
msgstr "La demande de fusion %{mr_link} a été examinée par %{mr_author}"
msgid "Merge request actions"
-msgstr ""
+msgstr "Actions de demande de fusion"
msgid "Merge request analytics"
msgstr "Données analytiques des demandes de fusion"
@@ -25540,7 +25841,7 @@ msgid "Merge request locked."
msgstr "Demande de fusion verrouillée."
msgid "Merge request not merged"
-msgstr ""
+msgstr "Demande de fusion non fusionnée"
msgid "Merge request reports"
msgstr "Rapports de demande de fusion"
@@ -25576,13 +25877,13 @@ msgid "Merge unverified changes?"
msgstr "Fusionner les modifications non vérifiées ?"
msgid "Merge when pipeline succeeds"
-msgstr ""
+msgstr "Fusionner lorsque le pipeline réussit"
msgid "Merge..."
msgstr ""
msgid "MergeConflict|Commit to source branch"
-msgstr ""
+msgstr "Valider sur la branche source"
msgid "MergeConflict|Committing..."
msgstr "Validation en cours..."
@@ -25597,19 +25898,19 @@ msgid "MergeConflict|Use theirs"
msgstr ""
msgid "MergeConflict|conflict"
-msgstr ""
+msgstr "conflit"
msgid "MergeConflict|conflicts"
-msgstr ""
+msgstr "conflits"
msgid "MergeConflict|origin//their changes"
msgstr ""
msgid "MergeRequestAnalytics|Assignees"
-msgstr ""
+msgstr "Assignés"
msgid "MergeRequestAnalytics|Date Merged"
-msgstr ""
+msgstr "Date de fusion"
msgid "MergeRequestAnalytics|Line changes"
msgstr "Lignes modifiées"
@@ -25645,7 +25946,7 @@ msgid "MergeRequests|An error occurred while saving the draft comment."
msgstr "Une erreur est survenue lors de l’enregistrement du brouillon du commentaire."
msgid "MergeRequests|Create issue to resolve thread"
-msgstr ""
+msgstr "Créer un ticket pour résoudre le fil de discussion"
msgid "MergeRequests|Reference copied"
msgstr "Référence copiée"
@@ -25717,7 +26018,7 @@ msgid "MergeRequest|Error dismissing suggestion popover. Please try again."
msgstr ""
msgid "MergeRequest|Error loading full diff. Please try again."
-msgstr ""
+msgstr "Erreur lors du chargement du diff complet. Veuillez réessayer."
msgid "MergeRequest|No files found"
msgstr "Aucun fichier trouvé"
@@ -25918,7 +26219,7 @@ msgid "Metrics|Add panel"
msgstr "Ajouter un panneau"
msgid "Metrics|Avg"
-msgstr ""
+msgstr "Moy"
msgid "Metrics|Back to dashboard"
msgstr "Retour au tableau de bord"
@@ -26094,7 +26395,7 @@ msgid "Metrics|There was an error getting options for variable \"%{name}\"."
msgstr "Une erreur s’est produite lors de l’obtention des options pour la variable « %{name} »."
msgid "Metrics|There was an error trying to validate your query"
-msgstr ""
+msgstr "Une erreur s'est produite lors de la tentative pour valider votre requête"
msgid "Metrics|There was an error while retrieving metrics"
msgstr "Une erreur est survenue lors de la récupération des métriques"
@@ -26133,7 +26434,7 @@ msgid "Metrics|Y-axis label"
msgstr "Libellé de l’axe Y"
msgid "Metrics|You can save a copy of this dashboard to your repository so it can be customized. Select a file name and branch to save it."
-msgstr ""
+msgstr "Vous pouvez enregistrer une copie de ce tableau de bord dans votre dépôt afin qu’il puisse être personnalisé. Sélectionnez un nom de fichier et une branche pour l’enregistrer."
msgid "Metrics|You're about to permanently delete this metric. This cannot be undone."
msgstr "Vous êtes sur le point de supprimer définitivement cette métrique. Cette opération est irréversible."
@@ -26342,10 +26643,10 @@ msgid "Milestone|%{percentage}%{percent} complete"
msgstr "terminé à %{percentage}%{percent}"
msgid "Min Value"
-msgstr ""
+msgstr "Valeur Mini"
msgid "Minimal Access"
-msgstr ""
+msgstr "Accès minimal"
msgid "Minimum capacity to be available before we schedule more mirrors preemptively."
msgstr ""
@@ -26369,7 +26670,7 @@ msgid "Mirror user"
msgstr "Utilisateur accédant au miroir"
msgid "Mirrored from %{link}."
-msgstr ""
+msgstr "Mis en miroir à partir de %{link}."
msgid "Mirrored repositories"
msgstr "Dépôts mis en miroir"
@@ -26381,13 +26682,13 @@ msgid "Mirroring settings were successfully updated."
msgstr "Les paramètres de mise en miroir ont été mis à jour avec succès."
msgid "Mirroring settings were successfully updated. The project is being updated."
-msgstr ""
+msgstr "Les paramètres de mise en miroir ont été mis à jour avec succès. Le projet est en cours d'actualisation."
msgid "Mirroring was successfully disabled."
msgstr "La mise en miroir a été désactivée avec succès."
msgid "Mirroring will only be available if the feature is included in the plan of the selected group or user."
-msgstr ""
+msgstr "La mise en miroir ne sera disponible que si la fonctionnalité est incluse dans le forfait de l'utilisateur ou du groupe sélectionné."
msgid "Miscellaneous"
msgstr "Divers"
@@ -26420,7 +26721,7 @@ msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories v
msgstr "Vous ne pourrez ni récupérer ni pousser vers des dépôts via SSH tant que vous n'aurez pas ajouté une clé SSH à votre profil"
msgid "MlExperimentsEmptyState|No Experiments to Show"
-msgstr ""
+msgstr "Aucune Expérience à afficher"
msgid "ModalButton|Add projects"
msgstr "Ajouter des projets"
@@ -26428,14 +26729,17 @@ msgstr "Ajouter des projets"
msgid "Modal|Close"
msgstr "Fermer"
-msgid "Modified"
+msgid "Model candidate details"
msgstr ""
+msgid "Modified"
+msgstr "Modifié"
+
msgid "Modified in this version"
msgstr ""
msgid "Modify commit message"
-msgstr ""
+msgstr "Modifier le message de validation"
msgid "Modify commit messages"
msgstr ""
@@ -26491,11 +26795,8 @@ msgstr "En savoir plus"
msgid "More information"
msgstr "Plus d’informations"
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
-msgstr "Plus d’information disponible|ici"
+msgstr "ici"
msgid "More information."
msgstr "Plus d'informations."
@@ -26507,7 +26808,7 @@ msgid "More topics"
msgstr "Plus de sujets"
msgid "Most common"
-msgstr ""
+msgstr "Le plus commun"
msgid "Most relevant"
msgstr ""
@@ -26525,13 +26826,13 @@ msgid "Move issue"
msgstr "Déplacer le ticket"
msgid "Move issue from one column of the board to another"
-msgstr ""
+msgstr "Déplacer le ticket d'une colonne du tableau vers une autre"
msgid "Move selection down"
-msgstr ""
+msgstr "Déplacer la sélection vers le bas"
msgid "Move selection up"
-msgstr ""
+msgstr "Déplacer la sélection vers le haut"
msgid "Move test case"
msgstr "Déplacer le cas de test"
@@ -26546,7 +26847,7 @@ msgid "MoveIssue|Cannot move issue due to insufficient permissions!"
msgstr "Impossible de déplacer le ticket en raison de permissions insuffisantes !"
msgid "MoveIssue|Cannot move issue to project it originates from!"
-msgstr ""
+msgstr "Impossible de déplacer le ticket vers le projet d'où il provient !"
msgid "MoveIssue|Cannot move issues of '%{issue_type}' type."
msgstr "Impossible de déplacer les tickets de type « %{issue_type} »."
@@ -26555,7 +26856,7 @@ msgid "Moved issue to %{label} column in the board."
msgstr ""
msgid "Moved this issue to %{path_to_project}."
-msgstr ""
+msgstr "Ce ticket a été déplacé dans %{path_to_project}."
msgid "Moves issue to %{label} column in the board."
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr "Action requise : Le stockage a été dépassé pour %{namespace_name}"
msgid "NamespaceStorage|Buy more storage"
msgstr "Acheter plus de stockage"
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr "Les téléversements ne sont pas comptabilisés dans les quotas de stockage de l'espace de noms."
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr "Nous vous recommandons d’acheter du stockage supplémentaire pour vous assurer que votre service ne soit pas interrompu."
@@ -26716,13 +27020,13 @@ msgid "NamespaceUserCap|View pending approvals"
msgstr "Voir les approbations en attente"
msgid "NamespaceUserCap|Your group has reached its billable member limit"
-msgstr ""
+msgstr "Votre groupe a atteint sa limite de membres facturables"
msgid "Namespaces"
msgstr "Espaces de noms"
msgid "Namespaces to index"
-msgstr ""
+msgstr "Espaces de noms à indexer"
msgid "Naming, topics, avatar"
msgstr "Nommage, sujets, avatar"
@@ -26731,7 +27035,7 @@ msgid "Naming, visibility"
msgstr "Nommage, visibilité"
msgid "Navigate to the project to close the milestone."
-msgstr ""
+msgstr "Naviguez jusqu'au projet pour fermer le jalon."
msgid "Navigation bar"
msgstr "Barre de navigation"
@@ -26788,7 +27092,7 @@ msgid "Needs"
msgstr ""
msgid "Needs attention"
-msgstr ""
+msgstr "Nécessite de l'attention"
msgid "Network"
msgstr "Réseau"
@@ -26847,7 +27151,7 @@ msgid "New Milestone"
msgstr "Nouveau jalon"
msgid "New Pages Domain"
-msgstr ""
+msgstr "Nouveau domaine de pages"
msgid "New Password"
msgstr "Nouveau Mot de passe"
@@ -26882,6 +27186,9 @@ msgstr "Nouvelle branche"
msgid "New branch unavailable"
msgstr "Nouvelle branche indisponible"
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr "Nouveau titre d'épopée confidentielle "
@@ -26924,11 +27231,17 @@ msgstr "Un nouveau jeton d'accès au bilan de santé a été généré !"
msgid "New identity"
msgstr "Nouvelle identité"
+msgid "New incident"
+msgstr "Nouvel incident"
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "Nouveau ticket"
msgid "New issue in %{project}"
-msgstr ""
+msgstr "Nouveau ticket dans %{project}"
msgid "New issue title"
msgstr ""
@@ -27006,10 +27319,10 @@ msgid "New topic"
msgstr "Nouveau sujet"
msgid "New users set to external"
-msgstr ""
+msgstr "Nouveaux utilisateurs configurés sur externe"
msgid "New! Suggest changes directly"
-msgstr ""
+msgstr "Nouveau ! Proposez des modifications directement"
msgid "Newest first"
msgstr "Plus récent en premier"
@@ -27048,13 +27361,13 @@ msgid "No %{header} for this request."
msgstr ""
msgid "No %{providerTitle} repositories found"
-msgstr ""
+msgstr "Aucun dépôt %{providerTitle} trouvé"
msgid "No CSV data to display."
msgstr "Pas de données CSV à afficher."
msgid "No Epic"
-msgstr ""
+msgstr "Pas d'Épopée"
msgid "No Google Cloud projects - You need at least one Google Cloud project"
msgstr "Aucun projet Google Cloud - Vous avez besoin d'au moins un projet Google Cloud"
@@ -27066,13 +27379,13 @@ msgid "No Milestone"
msgstr "Aucun jalon"
msgid "No Scopes"
-msgstr ""
+msgstr "Pas de Portée"
msgid "No Work Item Link found"
msgstr "Aucun Lien d'Élément de Travail trouvé"
msgid "No access"
-msgstr ""
+msgstr "Pas d'accès"
msgid "No active admin user found"
msgstr "Aucun administrateur actif n'a été trouvé"
@@ -27120,7 +27433,7 @@ msgid "No commits present here"
msgstr "Aucun commit présent ici"
msgid "No committers"
-msgstr ""
+msgstr "Aucun contributeur"
msgid "No confirmation email received? Check your spam folder or %{request_link_start}request new confirmation email%{request_link_end}."
msgstr "Aucun courriel de confirmation reçu ? Vérifiez dans le dossier des pourriels ou %{request_link_start}demandez un nouveau courriel de confirmation%{request_link_end}."
@@ -27171,7 +27484,7 @@ msgid "No file chosen."
msgstr "Aucun fichier choisi."
msgid "No file hooks found."
-msgstr "Aucun fichier « Hook » trouvé."
+msgstr "Aucun fichier de crochet trouvé."
msgid "No file selected"
msgstr "Aucun fichier sélectionné"
@@ -27186,10 +27499,10 @@ msgid "No forks are available to you."
msgstr "Aucun fork ne vous est disponible."
msgid "No group provided"
-msgstr ""
+msgstr "Aucun groupe fourni"
msgid "No grouping"
-msgstr ""
+msgstr "Pas de regroupement"
msgid "No issues found"
msgstr "Aucun ticket trouvé"
@@ -27197,11 +27510,8 @@ msgstr "Aucun ticket trouvé"
msgid "No iteration"
msgstr "Aucune itération"
-msgid "No iterations to show"
-msgstr "Aucune itération à afficher"
-
msgid "No job log"
-msgstr ""
+msgstr "Aucun journal de tâche"
msgid "No label"
msgstr "Aucun étiquette"
@@ -27216,7 +27526,7 @@ msgid "No matches found"
msgstr "Aucune correspondance trouvée"
msgid "No matching %{issuable} found. Make sure that you are adding a valid %{issuable} ID."
-msgstr ""
+msgstr "Il n'existe pas de %{issuable} qui corresponde. Assurez-vous d'ajouter un ID de %{issuable} valide."
msgid "No matching %{issuable} found. Make sure that you are adding a valid %{issuable} URL."
msgstr ""
@@ -27255,7 +27565,7 @@ msgid "No parent group"
msgstr "Pas de groupe parent"
msgid "No plan"
-msgstr ""
+msgstr "Aucun forfait"
msgid "No policy matches this license"
msgstr "Aucune stratégie ne correspond à cette licence"
@@ -27299,6 +27609,9 @@ msgstr "Aucun résultat"
msgid "No results found"
msgstr "Aucun résultat trouvé"
+msgid "No results found."
+msgstr "Aucun résultat trouvé."
+
msgid "No runner executable"
msgstr "Aucun Exécuteur exécutable"
@@ -27333,7 +27646,7 @@ msgid "No template selected"
msgstr "Aucun modèle sélectionné"
msgid "No test coverage"
-msgstr ""
+msgstr "Aucune couverture de test"
msgid "No triggers exist yet. Use the form above to create one."
msgstr "Il n'y a pas encore de déclencheur. Utilisez le formulaire ci-dessus pour en créer un."
@@ -27352,8 +27665,8 @@ msgstr "Aucun crochet web n'est activé. Sélectionnez les événements déclenc
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} day to renew your subscription."
msgid_plural "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} days to renew your subscription."
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "Pas de soucis, vous pouvez toujours utiliser toutes les fonctionnalités de %{strong}%{plan_name}%{strong_close} pour le moment. Vous avez %{remaining_days} jour pour renouveler votre abonnement."
+msgstr[1] "Pas de soucis, vous pouvez toujours utiliser toutes les fonctionnalités de %{strong}%{plan_name}%{strong_close} pour le moment. Vous avez %{remaining_days} jours pour renouveler votre abonnement."
msgid "No wrap"
msgstr "Pas de retour à la ligne"
@@ -27362,7 +27675,7 @@ msgid "No. of commits"
msgstr "Nb de commits"
msgid "Nobody has starred this repository yet"
-msgstr ""
+msgstr "Personne n'a encore mis en favoris ce dépôt"
msgid "Node was successfully created."
msgstr "Le nœud a été créé avec succès."
@@ -27374,7 +27687,7 @@ msgid "Nodes"
msgstr "NÅ“uds"
msgid "Non-admin users are restricted to read-only access, in both GitLab UI and API."
-msgstr ""
+msgstr "Les utilisateurs non administrateurs sont limités à un accès en lecture seule, à la fois dans l'interface utilisateur et dans l'API GitLab."
msgid "None"
msgstr "Aucun"
@@ -27428,13 +27741,13 @@ msgid "Not supported"
msgstr "Non pris en charge"
msgid "Note"
-msgstr ""
+msgstr "Notes"
msgid "Note creation requests"
msgstr "Demandes de création de note"
msgid "Note parameters are invalid: %{errors}"
-msgstr ""
+msgstr "Les paramètres de la note ne sont pas valides : %{errors}"
msgid "Note that pushing to GitLab requires write access to this repository."
msgstr "Remarquez que l'envoi vers GitLab nécessite un accès en écriture sur ce dépôt."
@@ -27443,13 +27756,13 @@ msgid "Note: As an administrator you may like to configure %{github_integration_
msgstr "Remarque : En tant qu’administrateur ou administratrice, vous pouvez configurer %{github_integration_link}, ce qui vous permettra de vous authentifier via GitHub et de connecter des dépôts sans générer de jeton d’accès personnel."
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
-msgstr "Remarque : En tant qu’administra·teur·trice, vous pouvez configurer %{github_integration_link}, ce qui vous permettra de vous connecter via GitHub et de permettre l’importation de dépôts sans générer de jeton d’accès personnel."
+msgstr "Remarque : En tant qu’administrateur, vous pouvez configurer %{github_integration_link}, ce qui vous permettra de vous connecter via GitHub et de permettre l’importation de dépôts sans générer de jeton d’accès personnel."
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
msgstr "Remarque : Envisagez de demander à votre administrateur ou administratrice GitLab de configurer %{github_integration_link}, ce qui vous permettra de vous authentifier via GitHub et de connecter des dépôts sans générer de jeton d’accès personnel."
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
-msgstr "Remarque : Envisagez de demander à votre administra·teur·trice GitLab de configurer %{github_integration_link}, ce qui vous permettra de vous connecter via GitHub et d’importer des dépôts sans générer de jeton d’accès personnel."
+msgstr "Remarque : Envisagez de demander à votre administrateur GitLab de configurer %{github_integration_link}, ce qui vous permettra de vous connecter via GitHub et d’importer des dépôts sans générer de jeton d’accès personnel."
msgid "Note: current forks will keep their visibility level."
msgstr "Remarque : les forks actuels conserveront leur niveau de visibilité."
@@ -27497,10 +27810,10 @@ msgid "Notes|Show history only"
msgstr "Afficher uniquement l’historique"
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
-msgstr ""
+msgstr "Ce commentaire a changé depuis que vous avez commencé à le modifier, veuillez examiner le %{open_link}commentaire actualisé%{close_link} pour vous assurer qu'aucune information n'est perdue"
msgid "Notes|This internal note will always remain confidential"
-msgstr ""
+msgstr "Cette note interne restera toujours confidentielle"
msgid "Notes|You're only seeing %{boldStart}other activity%{boldEnd} in the feed. To add a comment, switch to one of the following options."
msgstr "Vous ne voyez que les %{boldStart}autres activités%{boldEnd} dans le flux. Pour ajouter un commentaire, basculez sur l'une des options suivantes."
@@ -27518,7 +27831,7 @@ msgid "Notification events"
msgstr "Événement de notifications"
msgid "Notification setting - %{notification_title}"
-msgstr ""
+msgstr "Paramètre de notification - %{notification_title}"
msgid "Notification settings saved"
msgstr "Paramètres de notification enregistrés"
@@ -27625,7 +27938,7 @@ msgid "Notifications"
msgstr "Notifications"
msgid "Notifications have been disabled by the project or group owner"
-msgstr ""
+msgstr "Les notifications ont été désactivées par le propriétaire du projet ou du groupe"
msgid "Notifications off"
msgstr "Notifications désactivées"
@@ -27640,7 +27953,7 @@ msgid "Notifications turned on."
msgstr "Notifications activées."
msgid "Notify users by email when sign-in location is not recognized."
-msgstr ""
+msgstr "Notifiez les utilisateurs par courriel lorsque l'emplacement de connexion n'est pas reconnu."
msgid "Notify|%{author_link}'s issue %{issue_reference_link} is due soon."
msgstr "Le ticket de %{author_link} arrive bientôt à échéance %{issue_reference_link}."
@@ -27919,7 +28232,7 @@ msgid "Nuget metadatum must have at least license_url, project_url or icon_url s
msgstr "Les métadonnées de Nuget doivent au moins avoir license_url, project_url ou icon_url définies."
msgid "Number of Elasticsearch shards and replicas per index:"
-msgstr ""
+msgstr "Nombre de fragments et de répliques Elasticsearch par index :"
msgid "Number of Git pushes after which %{code_start}git gc%{code_end} is run."
msgstr "Nombre de poussées Git après lequel %{code_start}git gc%{code_end} est exécuté."
@@ -27937,7 +28250,7 @@ msgid "Number of commits"
msgstr ""
msgid "Number of commits per MR"
-msgstr ""
+msgstr "Nombre de commits par demande de fusion"
msgid "Number of employees"
msgstr "Nombre d'employés"
@@ -27957,8 +28270,23 @@ msgstr "Nombre de fragments"
msgid "OK"
msgstr "OK"
+msgid "OKR|Existing key result"
+msgstr "Résultat clé existant"
+
+msgid "OKR|Existing objective"
+msgstr "Objectif existant"
+
+msgid "OKR|New key result"
+msgstr "Nouveau résultat clé"
+
+msgid "OKR|New objective"
+msgstr "Nouvel objectif"
+
msgid "Object does not exist on the server or you don't have permissions to access it"
-msgstr ""
+msgstr "L'objet n'existe pas sur le serveur ou vous ne disposez pas des autorisations pour y accéder"
+
+msgid "Objective"
+msgstr "Objectif"
msgid "Observability"
msgstr "Observabilité"
@@ -27988,7 +28316,7 @@ msgid "Oldest first"
msgstr "Plus ancien en premier"
msgid "OmniAuth"
-msgstr ""
+msgstr "OmniAuth"
msgid "On"
msgstr ""
@@ -28002,7 +28330,7 @@ msgid "On the left sidebar, select %{merge_requests_link} to view them."
msgstr "Sur la barre latérale de gauche, sélectionnez %{merge_requests_link} pour les voir."
msgid "On track"
-msgstr ""
+msgstr "En bonne voie"
msgid "On-call Schedules"
msgstr "Calendriers des astreintes"
@@ -28314,7 +28642,7 @@ msgid "OnDemandScans|Start by creating a new profile. Profiles make it easy to s
msgstr "Commencez par la création d'un nouveau profil. Les profils facilitent l'enregistrement et la réutilisation des informations de configuration des outils de sécurité de GitLab."
msgid "OnDemandScans|Start time"
-msgstr ""
+msgstr "Heure de début"
msgid "OnDemandScans|Target"
msgstr "Cible"
@@ -28340,6 +28668,9 @@ msgstr "Il n'y a aucune analyse planifiée."
msgid "OnDemandScans|Timezone"
msgstr "Fuseau horaire"
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr "Voir les résultats"
@@ -28373,7 +28704,7 @@ msgid "One or more contacts were successfully removed."
msgstr "Un ou plusieurs contacts ont été supprimés avec succès."
msgid "One or more groups that you don't have access to."
-msgstr ""
+msgstr "Un ou plusieurs groupes auxquels vous n'avez pas accès."
msgid "One or more of you personal access tokens were revoked"
msgstr "Un ou plusieurs de vos jetons d'accès personnels ont été révoqués"
@@ -28382,7 +28713,7 @@ msgid "One or more of your %{provider} projects cannot be imported into GitLab d
msgstr "Un ou plusieurs de vos projets %{provider} ne peuvent pas être importés directement dans GitLab car ils utilisent Subversion ou Mercurial plutôt que Git pour le contrôle de versions."
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
-msgstr ""
+msgstr "Un ou plusieurs de vos fichiers de dépendance ne sont pas pris en charge et la liste de dépendance est peut-être incomplète. Vous trouverez ci-dessous une liste des types de fichiers pris en charge."
msgid "One or more of your personal access tokens has expired."
msgstr "Un ou plusieurs de vos jetons d'accès personnels ont expiré."
@@ -28394,7 +28725,7 @@ msgid "Only %{workspaceType} members with %{permissions} can view or be notified
msgstr "Seuls les membres du %{workspaceType} ayant un rôle de %{permissions} peuvent voir ce(tte) %{issuableType} et être notifié à son sujet."
msgid "Only 'Reporter' roles and above on tiers Premium and above can see Value Stream Analytics."
-msgstr ""
+msgstr "Seuls les rôles « Rapporteur » et supérieurs sur les forfaits Premium et au-delà peuvent voir l'Analyse des Chaînes de Valeur."
msgid "Only 1 appearances row can exist"
msgstr "Seule 1 ligne d'apparences peut exister"
@@ -28406,7 +28737,7 @@ msgid "Only Issue ID or merge request ID is required"
msgstr "Seul l'ID du ticket ou celui de la demande de fusion est requis"
msgid "Only Project Members"
-msgstr ""
+msgstr "Uniquement les membres du projet"
msgid "Only SSH"
msgstr "Uniquement SSH"
@@ -28417,9 +28748,6 @@ msgstr "Accessible uniquement aux %{membersPageLinkStart}membres du projet%{memb
msgid "Only active projects show up in the search and on the dashboard."
msgstr "Seuls les projets actifs remontent dans la recherche et sur le tableau de bord."
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr "Effectif uniquement lorsque le stockage distant est activé. Définir à 0 pour aucune limite de taille."
@@ -28525,9 +28853,6 @@ msgstr "Ouvrir dans une nouvelle fenêtre"
msgid "Opens new window"
msgstr "Ouvre une nouvelle fenêtre"
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr "L'opération a échoué. Vérifiez les journaux du pod %{pod_name} pour plus de détails."
-
msgid "Operation not allowed"
msgstr "Opération non autorisée"
@@ -28559,10 +28884,10 @@ msgid "Opstrace endpoint for Error Tracking integration"
msgstr "Point d'arrivée Opstrace pour l'intégration du Suivi d'Erreurs"
msgid "Optimize your workflow with CI/CD Pipelines"
-msgstr ""
+msgstr "Optimisez votre flux de travail avec les Pipelines CI/CD"
msgid "Optional"
-msgstr ""
+msgstr "Facultatif"
msgid "Optional parameter \"variables\" must be a Hash. Ex: variables[key1]=value1"
msgstr "Le paramètre optionnel « variables » doit être un hash. Ex. : variables[key1]=valeur1"
@@ -28595,13 +28920,13 @@ msgid "Other information"
msgstr "Autres informations"
msgid "Other merge requests block this MR"
-msgstr ""
+msgstr "D'autres demandes de fusion bloquent celle-ci"
msgid "Other versions"
msgstr "Autres versions"
msgid "Other visibility settings have been disabled by the administrator."
-msgstr ""
+msgstr "Les autres paramètres de visibilité ont été désactivés par l'administrateur."
msgid "Otherwise, click the link below to complete the process."
msgstr "Sinon, cliquez sur le lien ci-dessous pour terminer le processus."
@@ -28616,25 +28941,25 @@ msgid "Out-of-compliance with this project's policies and should be removed"
msgstr "Non conforme(s) aux stratégies de ce projet et à supprimer"
msgid "OutboundRequests|Allow requests to the local network from hooks and services."
-msgstr "Autoriser les requêtes vers le réseau local depuis les hooks et les services."
+msgstr "Autoriser les requêtes vers le réseau local depuis les crochets et les services."
msgid "OutboundRequests|Allow requests to the local network from system hooks"
-msgstr "Autoriser les requêtes vers le réseau local depuis les hooks systèmes"
+msgstr "Autoriser les requêtes vers le réseau local depuis les crochets du système"
msgid "OutboundRequests|Allow requests to the local network from web hooks and services"
-msgstr "Autoriser les requêtes vers le réseau local depuis les webhooks et les services"
+msgstr "Autoriser les requêtes vers le réseau local depuis les crochets web et les services"
msgid "OutboundRequests|Enforce DNS rebinding attack protection"
msgstr "Imposer la protection contre les attaques par DNS rebinding"
msgid "OutboundRequests|Local IP addresses and domain names that hooks and services may access"
-msgstr "Adresses IP locales et noms de domaines auxquels les services et hooks peuvent accéder"
+msgstr "Adresses IP locales et noms de domaines auxquels les services et crochets peuvent accéder"
msgid "OutboundRequests|Outbound requests"
msgstr "Requêtes sortantes"
msgid "OutboundRequests|Requests to these domains and IP addresses are accessible to both system hooks and web hooks even when local requests are not allowed. IP ranges such as 1:0:0:0:0:0:0:0/124 and 127.0.0.0/28 are supported. Domain wildcards are not supported. To separate entries use commas, semicolons, or newlines. The allowlist can hold a maximum of 1000 entries. Domains must be IDNA encoded."
-msgstr "Les requêtes vers ces domaines et adresses IP sont accessibles à la fois aux hooks systèmes et aux hooks Web même lorsque les requêtes locales ne sont pas autorisées. Les plages d'IP telles que 1:0:0:0:0:0:0/124 ou 127.0.0/28 sont prises en charge. Les domaines wildcard ne sont pas pris en charge. Pour séparer les entrées, utilisez des virgules, des points-virgules ou des nouvelles lignes. La liste d'autorisation peut contenir un maximum de 1000 entrées. Les domaines doivent utiliser l'encodage IDNA."
+msgstr "Les requêtes vers ces domaines et adresses IP sont accessibles à la fois aux crochets du système et aux crochets Web même lorsque les requêtes locales ne sont pas autorisées. Les plages d'IP telles que 1:0:0:0:0:0:0/124 ou 127.0.0/28 sont prises en charge. Les domaines wildcard ne sont pas pris en charge. Pour séparer les entrées, utilisez des virgules, des points-virgules ou des nouvelles lignes. La liste d'autorisation peut contenir un maximum de 1000 entrées. Les domaines doivent utiliser l'encodage IDNA."
msgid "OutboundRequests|Resolve IP addresses once and uses them to submit requests."
msgstr "Résoudre les adresses IP une seule fois et les utiliser pour soumettre des requêtes."
@@ -28813,6 +29138,9 @@ msgstr "Conan"
msgid "PackageRegistry|Conan Command"
msgstr "Commande Conan"
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr "Configurez la redirection des paquets et les limites de taille de fichier des paquets."
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr "Copier le contenu de .pypirc"
@@ -28862,7 +29190,7 @@ msgid "PackageRegistry|Copy npm command"
msgstr "Copier la commande npm"
msgid "PackageRegistry|Copy npm setup command"
-msgstr ""
+msgstr "Copier la commande de configuration npm"
msgid "PackageRegistry|Copy registry include"
msgstr "Copier l'inclusion de registre"
@@ -28877,7 +29205,7 @@ msgid "PackageRegistry|Copy yarn command"
msgstr "Copier la commande yarn"
msgid "PackageRegistry|Copy yarn setup command"
-msgstr ""
+msgstr "Copier la commande de configuration yarn"
msgid "PackageRegistry|Created by commit %{link} on branch %{branch}"
msgstr "Créé par le commit %{link} sur la branche %{branch}"
@@ -28902,6 +29230,9 @@ msgstr "Supprimer la ressource du paquet"
msgid "PackageRegistry|Delete package version"
msgstr "Supprimer la version du paquet"
+msgid "PackageRegistry|Delete packages"
+msgstr "Supprimer les paquets"
+
msgid "PackageRegistry|Delete selected"
msgstr "Supprimer la sélection"
@@ -28917,6 +29248,12 @@ msgstr "La suppression de la dernière ressource du paquet supprimera la version
msgid "PackageRegistry|Duplicate packages"
msgstr "Paquets dupliqués"
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr "Erreur de publication"
@@ -28941,6 +29278,18 @@ msgstr "Pour plus d'informations sur le registre NuGet , %{linkStart}consultez l
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr "Pour plus d'informations sur le registre PyPi, %{linkStart}consultez la documentation%{linkEnd}."
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr "Faire suivre les requêtes de paquets %{packageType}"
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr "Faire suivre les requêtes de paquets %{package_type}"
+
+msgid "PackageRegistry|Forward package requests"
+msgstr "Faire suivre les requêtes de paquets"
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr "Faites suivre les requêtes de paquets vers un registre public si les paquets ne sont pas trouvés dans le registre de paquets de GitLab."
+
msgid "PackageRegistry|Generic"
msgstr "Générique"
@@ -29013,6 +29362,9 @@ msgstr "Commande NuGet"
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr "Nombre de ressources dupliquées à conserver"
+msgid "PackageRegistry|Other versions"
+msgstr "Autres versions"
+
msgid "PackageRegistry|Package Registry"
msgstr "Registre de Paquets"
@@ -29028,6 +29380,9 @@ msgstr "Paquet supprimé avec succès"
msgid "PackageRegistry|Package formats"
msgstr "Formats de paquets"
+msgid "PackageRegistry|Package forwarding"
+msgstr "Redirection de paquets"
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] "Le paquet a %{updatesCount} mise à jour archivée"
@@ -29036,6 +29391,9 @@ msgstr[1] "Le paquet a %{updatesCount} mises à jour archivées"
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr "Paquet mis à jour par le commit %{link} sur la branche %{branch}, construit par le pipeline %{pipeline} et publié dans le registre %{datetime}"
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr "Les paquets ont été supprimés avec succès"
+
msgid "PackageRegistry|Permanently delete"
msgstr "Supprimer définitivement"
@@ -29090,6 +29448,9 @@ msgstr "Afficher les commandes PyPi"
msgid "PackageRegistry|Show Yarn commands"
msgstr "Afficher les commandes Yarn"
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr "Une erreur s’est produite lors de la suppression des paquets."
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr "Une erreur s’est produite lors de la suppression de la ressource du paquet."
@@ -29138,9 +29499,6 @@ msgstr "Ce paquet NuGet n'a aucune dépendance."
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr "Pour élargir votre recherche, modifiez ou supprimez les filtres ci-dessus."
-msgid "PackageRegistry|Type"
-msgstr "Type"
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr "Impossible de récupérer les informations de version du paquet."
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] "Vous êtes sur le point de supprimer 1 ressource. Cette opération est irréversible."
msgstr[1] "Vous êtes sur le point de supprimer %d ressources. Cette opération est irréversible."
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] "Vous êtes sur le point de supprimer 1 paquet. Cette opération est irréversible."
+msgstr[1] "Vous êtes sur le point de supprimer %d paquets. Cette opération est irréversible."
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr "Vous êtes sur le point de supprimer la version %{version} de %{name}. Êtes-vous sûr(e) ?"
@@ -29176,6 +29539,9 @@ msgstr "npm"
msgid "PackageRegistry|published by %{author}"
msgstr "publié par %{author}"
+msgid "Packages"
+msgstr "Paquets"
+
msgid "Packages and registries"
msgstr "Paquets & registres"
@@ -29186,22 +29552,22 @@ msgid "PagerDutySettings|Active"
msgstr "Actif"
msgid "PagerDutySettings|Create a GitLab incident for each PagerDuty incident by %{linkStart}configuring a webhook in PagerDuty%{linkEnd}"
-msgstr "Créer un incident GitLab pour chaque incident PagerDuty en %{linkStart}configurant un webhook dans PagerDuty%{linkEnd}"
+msgstr "Créer un incident GitLab pour chaque incident PagerDuty en %{linkStart}configurant un crochet web dans PagerDuty%{linkEnd}"
msgid "PagerDutySettings|Failed to update Webhook URL"
-msgstr "Échec de la mise à jour de l’URL du Webhook"
+msgstr "Échec de la mise à jour de l’URL du crochet web"
msgid "PagerDutySettings|Reset webhook URL"
-msgstr "Réinitialiser l’URL du webhook"
+msgstr "Réinitialiser l’URL du crochet web"
msgid "PagerDutySettings|Resetting the webhook URL for this project will require updating this integration's settings in PagerDuty."
-msgstr "La réinitialisation de l’URL du webhook de ce projet nécessitera la mise à jour des paramètres de cette intégration dans PagerDuty."
+msgstr "La réinitialisation de l’URL du crochet web de ce projet nécessitera la mise à jour des paramètres de cette intégration dans PagerDuty."
msgid "PagerDutySettings|Webhook URL"
-msgstr "URL du Webhook"
+msgstr "URL du crochet web"
msgid "PagerDutySettings|Webhook URL update was successful"
-msgstr "La mise à jour de l'URL du Webhook a réussi"
+msgstr "La mise à jour de l'URL du crochet web a réussi"
msgid "Pages"
msgstr "Pages"
@@ -29243,16 +29609,19 @@ msgid "Parameter"
msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
-msgstr ""
+msgstr "Le paramètre « job_id » ne peut pas dépasser la longueur de %{job_id_max_size}"
+
+msgid "Parameters"
+msgstr "Paramètres"
msgid "Parent"
-msgstr ""
+msgstr "Parent"
msgid "Parent epic doesn't exist."
-msgstr ""
+msgstr "L'épopée parente n'existe pas."
msgid "Parent epic is not present."
-msgstr ""
+msgstr "L'épopée parente n'est pas présente."
msgid "Parsing error for param :embed_json. %{message}"
msgstr "Erreur d'analyse pour le param :embed_json. %{message}"
@@ -29308,6 +29677,15 @@ msgstr "Veuillez entrer votre mot de passe pour confirmer"
msgid "Passwords should be unique and not used for any other sites or services."
msgstr "Les mots de passe doivent être uniques et ne pas être utilisés pour d'autres sites ou services."
+msgid "Password|Not satisfied"
+msgstr "Non satisfait"
+
+msgid "Password|Satisfied"
+msgstr "Satisfait"
+
+msgid "Password|To be satisfied"
+msgstr "À satisfaire"
+
msgid "Password|requires at least one lowercase letter"
msgstr "nécessite au moins une lettre minuscule"
@@ -29321,7 +29699,7 @@ msgid "Password|requires at least one uppercase letter"
msgstr "nécessite au moins une lettre majuscule"
msgid "Past due"
-msgstr ""
+msgstr "Échue"
msgid "Paste a public key here."
msgstr "Collez une clé publique ici."
@@ -29369,7 +29747,7 @@ msgid "Pause"
msgstr "Pause"
msgid "Pause time (ms)"
-msgstr ""
+msgstr "Temps de pause (ms)"
msgid "Paused"
msgstr "En pause"
@@ -29402,7 +29780,7 @@ msgid "People without permission will never get a notification and won't be able
msgstr "Les personnes sans autorisation ne recevront jamais de notifications et ne pourront pas commenter."
msgid "People without permission will never get a notification."
-msgstr ""
+msgstr "Les personnes sans autorisation ne recevront jamais de notification."
msgid "Per your subscription agreement with GitLab, you must report your license usage data on a monthly basis. GitLab uses this data to keep your subscription up to date. To report your license usage data, export your license usage file and email it to %{renewal_service_email}. If you need an updated license, GitLab will send the license to the email address registered in the %{customers_dot}, and you can upload this license to your instance."
msgstr "Conformément aux termes de votre contrat d'abonnement avec GitLab, vous devez communiquer vos données d'utilisation de licences à une fréquence mensuelle. GitLab utilise ces données pour maintenir votre abonnement à jour. Pour ce faire, exportez votre fichier d'utilisation de licence et envoyez-le par courriel à %{renewal_service_email}. Si une mise à jour de la licence est nécessaire, GitLab vous enverra celle-ci à l'adresse de courriel enregistrée dans le %{customers_dot} et vous pourrez la téléverser sur votre instance."
@@ -29420,7 +29798,7 @@ msgid "Perform code reviews and enhance collaboration with merge requests."
msgstr "Effectuez des revue de code et améliorez la collaboration avec des demandes de fusion."
msgid "Perform common operations on GitLab project"
-msgstr ""
+msgstr "Effectuer des opérations courantes sur le projet GitLab"
msgid "Performance optimization"
msgstr "Optimisation des performances"
@@ -29510,7 +29888,7 @@ msgid "Permissions Help"
msgstr "Aide sur les Autorisations"
msgid "Permissions and group features"
-msgstr ""
+msgstr "Permissions et fonctionnalités du groupe"
msgid "Personal Access Token"
msgstr "Jeton d’accès personnel"
@@ -29551,8 +29929,32 @@ msgstr "Tâches Phabricator"
msgid "Phone"
msgstr "Téléphone"
+msgid "PhoneVerification|Enter a valid code."
+msgstr "Entrez un code valide."
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr "Une erreur s'est produite. Veuillez réessayer."
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr "Le code a expiré. Demandez un nouveau code et réessayez."
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr "Une erreur s'est produite avec le numéro de téléphone que vous avez saisi. Entrez un numéro de téléphone différent et réessayez."
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr "Une erreur s'est produite avec le numéro de téléphone que vous avez saisi. Entrez un numéro de téléphone valide."
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr "Le code de vérification ne peut pas être vide."
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr "Vous avez atteint le nombre maximal d'essais. Demandez un nouveau code et réessayez."
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr "Vous avez atteint le nombre maximal d'essais. Patientez %{interval} et réessayez."
+
msgid "Pick a name"
-msgstr ""
+msgstr "Choisir un nom"
msgid "Pin code"
msgstr "Code Pin"
@@ -29594,7 +29996,7 @@ msgid "Pipeline ran in fork of project"
msgstr "Pipeline exécuté dans un fork du projet"
msgid "Pipeline status emails"
-msgstr ""
+msgstr "Courriels sur l'état du pipeline"
msgid "Pipeline subscriptions"
msgstr ""
@@ -29615,10 +30017,10 @@ msgid "PipelineCharts|An error has occurred when retrieving the pipelines data"
msgstr "Une erreur s’est produite lors de la récupération des données des pipelines"
msgid "PipelineCharts|An unknown error occurred while processing CI/CD analytics."
-msgstr "Une erreur inconnue s'est produite lors du traitement des analyses CI/CD."
+msgstr "Une erreur inconnue s'est produite lors du traitement de l'analytique CI/CD."
msgid "PipelineCharts|CI/CD Analytics"
-msgstr "Analyses CI/CD"
+msgstr "Analytique CI/CD"
msgid "PipelineCharts|Failed:"
msgstr "Échecs :"
@@ -29642,7 +30044,7 @@ msgid "PipelineEditorFileTree|Configuration files added with the include keyword
msgstr "Fichiers de configuration ajoutés avec le mot-clé include"
msgid "PipelineEditorFileTree|When you use the include keyword to add pipeline configuration from files in the project, those files will be listed here."
-msgstr ""
+msgstr "Lorsque vous utilisez le mot-clé include pour ajouter la configuration du pipeline à partir des fichiers du projet, ces fichiers sont listés ici."
msgid "PipelineEditorTutorial|Browse %{linkStart}CI/CD examples and templates%{linkEnd}"
msgstr "Parcourir des %{linkStart}exemples et des modèles de CI/CD%{linkEnd}"
@@ -29666,7 +30068,7 @@ msgid "PipelineEditorTutorial|Make your pipeline more efficient with the %{linkS
msgstr "Rendez votre pipeline plus efficace avec le %{linkStart}mot-clé Needs%{linkEnd}"
msgid "PipelineEditorTutorial|Resources to help with your CI/CD configuration:"
-msgstr ""
+msgstr "Ressources pour vous aider avec votre configuration CI/CD :"
msgid "PipelineEditorTutorial|Select the pipeline ID to view the full details about your first pipeline run."
msgstr "Sélectionnez l'ID du pipeline pour afficher les détails complets de votre première exécution de pipeline."
@@ -29687,13 +30089,13 @@ msgid "PipelineEditorTutorial|View %{linkStart}.gitlab-ci.yml syntax reference%{
msgstr "Voir la %{linkStart}référence de la syntaxe de .gitlab-ci.yml%{linkEnd}"
msgid "PipelineEditorTutorial|âš™ï¸ Pipeline configuration reference"
-msgstr ""
+msgstr "âš™ï¸ Référence de configuration du pipeline"
msgid "PipelineEditorTutorial|💡 Tip: Visualize and validate your pipeline"
-msgstr ""
+msgstr "💡 Astuce : Visualisez et validez votre pipeline"
msgid "PipelineEditorTutorial|🚀 Run your first pipeline"
-msgstr ""
+msgstr "🚀 Exécutez votre premier pipeline"
msgid "PipelineEditor|Configuration content has changed. Re-run validation for updated results."
msgstr "Le contenu de la configuration a changé. Relancez la validation pour des résultats actualisés."
@@ -29903,16 +30305,16 @@ msgid "PipelineWizardListWidget|remove step"
msgstr "supprimer l'étape"
msgid "PipelineWizard|Commit"
-msgstr ""
+msgstr "Valider"
msgid "PipelineWizard|Commit Message"
-msgstr ""
+msgstr "Message de validation"
msgid "PipelineWizard|Commit changes to your file"
msgstr "Valider les modifications sur votre fichier"
msgid "PipelineWizard|Commit file to Branch"
-msgstr ""
+msgstr "Valider le fichier dans la Branche"
msgid "PipelineWizard|Commit your new file"
msgstr "Validez votre nouveau fichier"
@@ -29927,7 +30329,7 @@ msgid "PipelineWizard|There was a problem while checking whether your file alrea
msgstr "Une erreur s'est produite lors de la vérification de l'existence de votre fichier dans la branche spécifiée."
msgid "PipelineWizard|There was an unexpected error trying to set up the template. The error has been logged."
-msgstr ""
+msgstr "Une erreur inattendue s'est produite en essayant de configurer le modèle. L’erreur a été consignée."
msgid "Pipelines"
msgstr "Pipelines"
@@ -29939,7 +30341,7 @@ msgid "Pipelines settings for '%{project_name}' were successfully updated."
msgstr "Les paramètres des pipelines pour « %{project_name} » ont été mis à jour avec succès."
msgid "Pipelines|\"Hello world\" with GitLab CI"
-msgstr ""
+msgstr "« Hello world » avec GitLab CI"
msgid "Pipelines|%{jobs} %{ref_text} in %{duration}"
msgstr "%{jobs} %{ref_text} en %{duration}"
@@ -29972,7 +30374,7 @@ msgid "Pipelines|Build with confidence"
msgstr "Construire en toute confiance"
msgid "Pipelines|Building for iOS?"
-msgstr ""
+msgstr "Construire pour iOS ?"
msgid "Pipelines|By revoking a trigger you will break any processes making use of it. Are you sure?"
msgstr "En révoquant un déclencheur, vous casserez tous les processus qui l'utilisent. Êtes-vous sûr ?"
@@ -30038,7 +30440,7 @@ msgid "Pipelines|If you are unsure, please ask a project maintainer to review it
msgstr "En cas de doute, veuillez demander à un mainteneur du projet de l'examiner pour vous."
msgid "Pipelines|Install GitLab Runner"
-msgstr ""
+msgstr "Installer GitLab Runner"
msgid "Pipelines|It is recommended the code is reviewed thoroughly before running this pipeline with the parent project's CI resource."
msgstr "Il est recommandé que le code soit examiné minutieusement avant d’exécuter ce pipeline avec la ressource CI du projet parent."
@@ -30095,10 +30497,10 @@ msgid "Pipelines|Ready to set up CI/CD for your project?"
msgstr ""
msgid "Pipelines|Revoke trigger"
-msgstr ""
+msgstr "Révoquer le déclencheur"
msgid "Pipelines|Runners are available to run your jobs now"
-msgstr ""
+msgstr "Des exécuteurs sont maintenant disponibles pour exécuter vos tâches"
msgid "Pipelines|Set up a runner"
msgstr "Configurer un exécuteur"
@@ -30161,7 +30563,7 @@ msgid "Pipelines|Token"
msgstr "Jeton"
msgid "Pipelines|Trigger user has insufficient permissions to project"
-msgstr ""
+msgstr "L’utilisateur du déclencheur n’a pas les permissions suffisantes pour le projet"
msgid "Pipelines|Try test template"
msgstr "Essayer le modèle de test"
@@ -30332,7 +30734,7 @@ msgid "Pipeline|Tag name"
msgstr "Nom de l'étiquette"
msgid "Pipeline|Test coverage"
-msgstr ""
+msgstr "Couverture des tests"
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr "Cette modification diminuera la couverture de test globale si elle est fusionnée."
@@ -30356,7 +30758,7 @@ msgid "Pipeline|Trigger author"
msgstr "Auteur du déclencheur"
msgid "Pipeline|Triggerer"
-msgstr ""
+msgstr "Déclenché par"
msgid "Pipeline|Variables"
msgstr "Variables"
@@ -30374,7 +30776,7 @@ msgid "Pipeline|You’re about to stop pipeline #%{pipelineId}."
msgstr "Vous êtes sur le point d’arrêter le pipeline n°%{pipelineId}."
msgid "Pipeline|for"
-msgstr ""
+msgstr "pour"
msgid "Pipeline|merge request"
msgstr "demande de fusion"
@@ -30383,7 +30785,7 @@ msgid "Pipeline|merge train"
msgstr "train de fusion"
msgid "Pipeline|on"
-msgstr ""
+msgstr "sur"
msgid "Pipeline|with stage"
msgstr "avec l’étape"
@@ -30434,13 +30836,13 @@ msgid "Please %{link_to_register} or %{link_to_sign_in} to comment"
msgstr "Veuillez vous %{link_to_register} ou vous %{link_to_sign_in} pour commenter"
msgid "Please %{registerLinkStart}register%{registerLinkEnd} or %{signInLinkStart}sign in%{signInLinkEnd} to reply."
-msgstr ""
+msgstr "Veuillez %{registerLinkStart}vous enregistrer%{registerLinkEnd} ou %{signInLinkStart}vous connecter%{signInLinkEnd} pour répondre."
msgid "Please %{registerLinkStart}register%{registerLinkEnd} or %{signInLinkStart}sign in%{signInLinkEnd} to start a new discussion."
msgstr "Veuillez vous %{registerLinkStart}inscrire%{registerLinkEnd} ou vous %{signInLinkStart}connecter%{signInLinkEnd} pour démarrer un nouvelle discussion."
msgid "Please %{startTagRegister}register%{endRegisterTag} or %{startTagSignIn}sign in%{endSignInTag} to reply"
-msgstr ""
+msgstr "Veuillez %{startTagRegister}vous enregistrer%{endRegisterTag} ou %{startTagSignIn}vous connecter%{endSignInTag} pour répondre"
msgid "Please accept the Terms of Service before continuing."
msgstr "Veuillez accepter les conditions générales d’utilisation avant de continuer."
@@ -30449,10 +30851,10 @@ msgid "Please add a comment in the text area above"
msgstr "Veuillez ajouter un commentaire dans la zone de texte ci-dessus"
msgid "Please check the configuration file for this chart"
-msgstr ""
+msgstr "Veuillez vérifier le fichier de configuration pour ce graphique"
msgid "Please check the configuration file to ensure that a collection of charts has been declared."
-msgstr ""
+msgstr "Veuillez vérifier le fichier de configuration pour vous assurer qu'une collection de graphiques a été déclarée."
msgid "Please check the configuration file to ensure that it is available and the YAML is valid"
msgstr "Veuillez vérifier le fichier de configuration pour vous assurer qu'il est disponible et que le YAML est valide"
@@ -30466,6 +30868,9 @@ msgstr "Veuillez vérifier votre courriel (%{email}) pour être sûr que vous ê
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr "Veuillez cliquer sur le lien présent dans le courriel de confirmation avant de continuer. Il a été envoyé à %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
+msgid "Please complete the incident creation form."
+msgstr "Veuillez remplir le formulaire de création d'incident."
+
msgid "Please complete your profile with email address"
msgstr "Veuillez compléter votre profil avec une adresse de courriel"
@@ -30509,7 +30914,7 @@ msgid "Please enter a non-negative number"
msgstr "Veuillez entrer un nombre non négatif"
msgid "Please enter a number greater than %{number} (from the project settings)"
-msgstr ""
+msgstr "Veuillez entrer un nombre supérieur à %{number} (depuis les paramètres du projet)"
msgid "Please enter a valid URL format, ex: http://www.example.com/home"
msgstr "Veuillez entrer un format d'URL valide, ex. : http://www.exemple.com/accueil"
@@ -30563,7 +30968,7 @@ msgid "Please provide a valid email address."
msgstr "Veuillez fournir une adresse de courriel valide."
msgid "Please provide attributes to update"
-msgstr ""
+msgstr "Veuillez fournir les attributs à mettre à jour"
msgid "Please reach out if you have any questions and we'll be happy to assist."
msgstr "N'hésitez pas à nous contacter si vous avez des questions et nous serons heureux de vous accompagner."
@@ -30605,7 +31010,7 @@ msgid "Please select what should be included in each exported requirement."
msgstr "Veuillez sélectionner ce qui doit être inclus dans chaque exigence exportée."
msgid "Please select..."
-msgstr ""
+msgstr "Veuillez sélectionner..."
msgid "Please set a new password before proceeding."
msgstr "Veuillez définir un nouveau mot de passe avant de continuer."
@@ -30620,10 +31025,7 @@ msgid "Please try and refresh the page. If the problem persists please contact s
msgstr "Veuillez essayer et actualiser la page. Si le problème persiste, veuillez contacter le support."
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
-msgstr ""
-
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
+msgstr "Veuillez taper %{phrase_code} pour continuer ou fermer ce dialogue pour annuler."
msgid "Please wait a few moments while we load the file history for this line."
msgstr "Veuillez patienter quelques instants pendant que nous chargeons l'historique des fichiers pour cette ligne."
@@ -30641,10 +31043,10 @@ msgid "Please wait while we prepare for verification."
msgstr "Veuillez patienter pendant que nous nous préparons pour la vérification."
msgid "Pods in use"
-msgstr ""
+msgstr "Pods utilisés"
msgid "Point to any links you like: documentation, built binaries, or other related materials. These can be internal or external links from your GitLab instance. Each URL and link title must be unique."
-msgstr ""
+msgstr "Pointez vers tous les liens que vous souhaitez : documentation, binaires produits ou tout autre ressource associée. Il peut s’agir de liens internes ou externes à partir de votre instance GitLab. Les URL et titre de lien doivent être uniques."
msgid "Policies"
msgstr "Politiques"
@@ -30698,19 +31100,19 @@ msgid "PreScanVerification|(optional)"
msgstr "(facultatif)"
msgid "PreScanVerification|Attempts to authenticate with the scan target"
-msgstr ""
+msgstr "Tente de s'authentifier auprès de la cible de l'analyse"
msgid "PreScanVerification|Attempts to find and connect to the scan target"
-msgstr ""
+msgstr "Tente de trouver et de se connecter à la cible de l'analyse"
msgid "PreScanVerification|Attempts to follow internal links and crawl 3 pages without errors"
-msgstr ""
+msgstr "Tente de suivre des liens internes et d'explorer 3 pages sans erreurs"
msgid "PreScanVerification|Authentication"
msgstr "Authentification"
msgid "PreScanVerification|Cancel pre-scan verification"
-msgstr ""
+msgstr "Annuler la vérification avant analyse"
msgid "PreScanVerification|Connection"
msgstr "Connexion"
@@ -30731,22 +31133,22 @@ msgid "PreScanVerification|Started %{timeAgo} in pipeline"
msgstr "A démarré %{timeAgo} en pipeline"
msgid "PreScanVerification|Target exploration"
-msgstr ""
+msgstr "Exploration de la cible"
msgid "PreScanVerification|Test your configuration and identify potential errors before running a full scan."
msgstr "Testez votre configuration et identifiez les erreurs potentielles avant de lancer une analyse complète."
msgid "PreScanVerification|The last pre-scan verification job is no longer valid because this scan’s configuration has changed."
-msgstr ""
+msgstr "La dernière tâche de vérification avant analyse n’est plus valide car la configuration de cette analyse a changé."
msgid "PreScanVerification|The pre-scan verification status was reset for this scan"
-msgstr ""
+msgstr "L'état de la vérification avant analyse a été réinitialisé pour cette analyse"
msgid "PreScanVerification|Verification checks"
-msgstr ""
+msgstr "Contrôles de vérification"
msgid "PreScanVerification|Verification checks are determined by a scan’s configuration details. Changing configuration details may alter or reset the verification checks and their status."
-msgstr ""
+msgstr "Les contrôles de vérification sont déterminés par les détails de configuration de l'analyse. La modification de ces derniers peut altérer ou réinitialiser les contrôles de vérification et leurs états."
msgid "PreScanVerification|Verify configuration"
msgstr "Vérifier la configuration"
@@ -30755,7 +31157,7 @@ msgid "PreScanVerification|View results"
msgstr "Voir les résultats"
msgid "PreScanVerification|You must complete the scan configuration form before running pre-scan verification"
-msgstr ""
+msgstr "Vous devez remplir le formulaire de configuration de l'analyse avant d'exécuter la vérification avant analyse"
msgid "Preferences"
msgstr "Préférences"
@@ -30778,6 +31180,9 @@ msgstr "Choisissez le contenu par défaut que vous souhaitez voir sur votre tabl
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "Choisissez le contenu que vous souhaitez voir sur la vue d'ensemble d'un projet."
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr "Choisissez quelle version de l'EDI Web vous souhaitez utiliser."
+
msgid "Preferences|Color for added lines"
msgstr "Couleur des lignes ajoutées"
@@ -30854,7 +31259,7 @@ msgid "Preferences|Sourcegraph"
msgstr "Sourcegraph"
msgid "Preferences|Surround text selection when typing quotes or brackets"
-msgstr ""
+msgstr "Encadrer la sélection de texte lors de la saisie de guillemets ou d'accolades"
msgid "Preferences|Syntax highlighting theme"
msgstr "Thème de coloration syntaxique"
@@ -30862,6 +31267,9 @@ msgstr "Thème de coloration syntaxique"
msgid "Preferences|Tab width"
msgstr "Largeur de tabulation"
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr "L'ancien EDI Web reste disponible tant que le nouveau est en Bêta."
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr "Cette fonctionnalité est expérimentale et les traductions ne sont pas encore terminées."
@@ -30874,8 +31282,14 @@ msgstr "Ces paramètres vous permettent de personnaliser le comportement de la m
msgid "Preferences|Time preferences"
msgstr "Préférences de l’heure"
+msgid "Preferences|Use legacy Web IDE"
+msgstr "Utiliser l'ancien EDI Web"
+
msgid "Preferences|Use relative times"
-msgstr ""
+msgstr "Dates et heures relatives à l'heure actuelle"
+
+msgid "Preferences|Web IDE"
+msgstr "EDI Web"
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr "Lorsque vous saisissez du texte dans une zone de commentaire ou de description, l'appui sur %{kbdOpen}Entrée%{kbdClose} dans une liste ajoute dessous un nouvel élément."
@@ -30965,7 +31379,7 @@ msgid "Priority"
msgstr "Priorité"
msgid "Private"
-msgstr ""
+msgstr "Privé"
msgid "Private - Guest users are not allowed to view detailed release information like title and source code."
msgstr "Privé - Les utilisateurs invités ne sont pas autorisés à voir les informations de version détaillées telles que la version ou le code source."
@@ -30977,7 +31391,7 @@ msgid "Private - The group and its projects can only be viewed by members."
msgstr "Privé — le groupe ainsi que ses projets ne sont accessibles qu’à ses membres."
msgid "Private group(s)"
-msgstr ""
+msgstr "Groupe(s) privé(s)"
msgid "Private profile"
msgstr "Profil privé"
@@ -30994,26 +31408,35 @@ msgstr "Problème avec la commande %{name} : %{message}."
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
+msgstr "Analytique des produits"
+
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr "Ajouter au tableau de bord"
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
msgstr ""
msgid "ProductAnalytics|Audience"
msgstr "Audience"
+msgid "ProductAnalytics|New Analytics Widget Title"
+msgstr ""
+
msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr "Il n’y a pas de données pour ce type de graphique actuellement. Veuillez consulter l’onglet Configuration si vous n’avez pas déjà configuré l’outil des données analytiques de produit."
-msgid "ProductAnalytics|Widgets content"
-msgstr "Contenu des widgets"
-
msgid "Productivity"
msgstr "Productivité"
msgid "Productivity Analytics"
-msgstr ""
+msgstr "Analytique de la Productivité"
msgid "Productivity analytics can help identify the problems that are delaying your team"
-msgstr ""
+msgstr "L'analytique de la productivité peut contribuer à identifier les problèmes qui retardent votre équipe"
msgid "ProductivityAanalytics|Merge requests"
msgstr "Demandes de fusion"
@@ -31022,16 +31445,16 @@ msgid "ProductivityAanalytics|is earlier than the allowed minimum date"
msgstr "est plus tôt que la date minimale autorisée"
msgid "ProductivityAnalytics|Ascending"
-msgstr ""
+msgstr "Croissant"
msgid "ProductivityAnalytics|Days"
msgstr "Jours"
msgid "ProductivityAnalytics|Days to merge"
-msgstr ""
+msgstr "Jours jusqu'à la fusion"
msgid "ProductivityAnalytics|Descending"
-msgstr ""
+msgstr "Décroissant"
msgid "ProductivityAnalytics|Hours"
msgstr "Heures"
@@ -31097,10 +31520,10 @@ msgid "Profiles|Account scheduled for removal."
msgstr "Compte programmé pour suppression."
msgid "Profiles|Active"
-msgstr ""
+msgstr "Actif"
msgid "Profiles|Add key"
-msgstr "Ajouter une clef"
+msgstr "Ajouter une clé"
msgid "Profiles|An error occurred while updating your username, please try again."
msgstr "Une erreur s'est produite lors de la mise à jour de votre nom d'utilisateur, veuillez réessayer."
@@ -31109,7 +31532,7 @@ msgid "Profiles|Avatar cropper"
msgstr "Rogneur d’avatar"
msgid "Profiles|Avatar will be removed. Are you sure?"
-msgstr "L’avatar sera supprimé. Êtesâ€vous sûr·e ?"
+msgstr "L’avatar sera supprimé. Êtesâ€vous sûr(e) ?"
msgid "Profiles|Begins with %{ssh_key_algorithms}."
msgstr "Commence par %{ssh_key_algorithms}."
@@ -31118,7 +31541,7 @@ msgid "Profiles|Bio"
msgstr ""
msgid "Profiles|Change username"
-msgstr "Changer le nom d’utilisateur·rice"
+msgstr "Changer le nom d’utilisateur"
msgid "Profiles|Changing your username can have unintended side effects."
msgstr "Changer votre nom d'utilisateur peut avoir des effets secondaires inattendus."
@@ -31202,7 +31625,7 @@ msgid "Profiles|Expired:"
msgstr "Expiré :"
msgid "Profiles|Expires:"
-msgstr ""
+msgstr "Expire :"
msgid "Profiles|Feed token was successfully reset"
msgstr "Le jeton de flux a été réinitialisé avec succès"
@@ -31244,7 +31667,7 @@ msgid "Profiles|Key titles are publicly visible."
msgstr "Le titre des clés est visible publiquement."
msgid "Profiles|Last used:"
-msgstr ""
+msgstr "Dernière utilisation :"
msgid "Profiles|Learn more"
msgstr "En savoir plus"
@@ -31366,6 +31789,12 @@ msgstr "Mettre à jour le nom d’utilisateur"
msgid "Profiles|Upload new avatar"
msgstr "Téléverser un nouvel avatar"
+msgid "Profiles|Usage type"
+msgstr "Type d'utilisation"
+
+msgid "Profiles|Usage type:"
+msgstr "Type d'utilisation :"
+
msgid "Profiles|Use a private email - %{email}"
msgstr "Utiliser une adresse de courriel privée — %{email}"
@@ -31400,7 +31829,7 @@ msgid "Profiles|You can upload your avatar here or change it at %{gravatar_link}
msgstr "Vous pouvez téléverser votre avatar ici ou le changer en %{gravatar_link}"
msgid "Profiles|You don't have access to delete this user."
-msgstr "Vous n’avez pas les autorisations suffisantes pour supprimer cet utilisateur ou cette utilisatrice."
+msgstr "Vous n’avez pas les autorisations suffisantes pour supprimer cet utilisateur."
msgid "Profiles|You must accept the Terms of Service in order to perform this action."
msgstr "Vous devez accepter les Conditions Générales d'Utilisation pour effectuer cette action."
@@ -31451,7 +31880,7 @@ msgid "Progress"
msgstr "Progression"
msgid "Progress tracking"
-msgstr ""
+msgstr "Suivi de la progression"
msgid "Project"
msgstr "Projet"
@@ -31571,7 +32000,7 @@ msgid "Project is required when cluster_type is :project"
msgstr "Le projet est requis lorsque cluster_type vaut :project"
msgid "Project members"
-msgstr ""
+msgstr "Membres du projet"
msgid "Project milestone"
msgstr "Jalon du projet"
@@ -31592,7 +32021,7 @@ msgid "Project path"
msgstr "Chemin du projet"
msgid "Project security status"
-msgstr ""
+msgstr "État de la sécurité des projets"
msgid "Project security status help page"
msgstr "Page d'aide sur l'état de la sécurité du projet"
@@ -31628,7 +32057,7 @@ msgid "ProjectActivityRSS|Subscribe"
msgstr "S’abonner"
msgid "ProjectCreationLevel|Allowed to create projects"
-msgstr "Autorisé·e à créer des projets"
+msgstr "Autorisé à créer des projets"
msgid "ProjectCreationLevel|Default project creation protection"
msgstr "Protection de création de projets par défaut"
@@ -31700,7 +32129,7 @@ msgid "ProjectQualitySummary|Failure"
msgstr "Échec"
msgid "ProjectQualitySummary|Get insight into the overall percentage of tests in your project that succeed, fail and are skipped."
-msgstr ""
+msgstr "Obtenez des statistiques sur le pourcentage global des tests de votre projet qui réussissent, échouent et sont ignorés."
msgid "ProjectQualitySummary|Help us improve this page"
msgstr "Aidez-nous à améliorer cette page"
@@ -31811,7 +32240,7 @@ msgid "ProjectService|Must have permission to trigger a manual build in TeamCity
msgstr "Doit avoir l'autorisation de déclencher une construction manuelle dans TeamCity."
msgid "ProjectService|Perform common operations on GitLab project: %{project_name}"
-msgstr ""
+msgstr "Effectuer des opérations courantes sur le projet GitLab : %{project_name}"
msgid "ProjectService|Run CI/CD pipelines with Buildkite."
msgstr "Exécuter des pipelines CI/CD avec Buildkite."
@@ -31892,20 +32321,26 @@ msgid "ProjectSettings|Additional settings that influence how and when merges ar
msgstr "Paramètres supplémentaires qui agissent sur comment et quand les fusions sont effectuées."
msgid "ProjectSettings|All merge requests and commits are made against this branch unless you specify a different one."
-msgstr ""
+msgstr "Toutes les demandes de fusion et les validations sont faites sur cette branche, sauf si vous en spécifiez une autre."
msgid "ProjectSettings|All threads must be resolved"
-msgstr "Tous les fil de conversation doivent être résolus"
+msgstr "Tous les fils de conversation doivent être résolus"
msgid "ProjectSettings|Allow"
msgstr "Autoriser"
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr "Autoriser tout le monde à télécharger depuis le Registre de Paquets"
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr "Toujours afficher les boutons des émojis de récompense « pouce vers le haut » et « pouce vers le bas » sur les tickets, les demandes de fusion et les extraits."
msgid "ProjectSettings|Analytics"
msgstr "Analytiques"
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr "Tout le monde peut extraire des paquets avec une API de gestionnaire de paquets."
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr "Fermer automatiquement les tickets référencés sur la branche par défaut"
@@ -31916,7 +32351,7 @@ msgid "ProjectSettings|Badges"
msgstr "Badges numériques"
msgid "ProjectSettings|Branches created from issues follow this pattern."
-msgstr ""
+msgstr "Les branches créées à partir de tickets suivent ce modèle."
msgid "ProjectSettings|Build, test, and deploy your changes."
msgstr "Construisez, testez et déployez vos modifications."
@@ -31937,10 +32372,10 @@ msgid "ProjectSettings|Choose your merge method, options, checks, and squash opt
msgstr "Choisissez votre méthode de fusion, vos options, vos vérifications et vos options d'écrasement."
msgid "ProjectSettings|Combine git tags with release notes, release evidence, and assets to create a release."
-msgstr ""
+msgstr "Combiner des étiquettes git avec les notes de version, les preuves de publication et les ressources pour créer une version."
msgid "ProjectSettings|Configure your infrastructure."
-msgstr ""
+msgstr "Configurer votre infrastructure."
msgid "ProjectSettings|Configure your project resources and monitor their health."
msgstr "Configurer les ressources de votre projet et surveiller leur état de santé."
@@ -31949,7 +32384,7 @@ msgid "ProjectSettings|Contact an admin to change this setting."
msgstr "Contactez un administrateur pour modifier ce paramètre."
msgid "ProjectSettings|Container registry"
-msgstr ""
+msgstr "Registre de conteneurs"
msgid "ProjectSettings|Customize this project's badges."
msgstr "Personnaliser les badges de ce groupe."
@@ -31958,7 +32393,7 @@ msgid "ProjectSettings|Determine what happens to the commit history when you mer
msgstr "Déterminer ce qu'il advient de l'historique des commits lors de la fusion d'une demande de fusion."
msgid "ProjectSettings|Disable email notifications"
-msgstr ""
+msgstr "Désactiver les notifications par courriel"
msgid "ProjectSettings|Do not allow"
msgstr "Ne pas autoriser"
@@ -31985,10 +32420,7 @@ msgid "ProjectSettings|Every merge creates a merge commit."
msgstr "Chaque fusion crée un commit de fusion."
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
-msgstr ""
-
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr "Chaque projet peut avoir son propre espace pour stocker ses paquets."
+msgstr "Chaque projet peut avoir son propre espace pour stocker ses images Docker"
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr "Chaque projet peut avoir son propre espace pour stocker ses paquets. Remarque : le Registre de Paquets est toujours visible lorsqu'un projet est public."
@@ -31997,7 +32429,7 @@ msgid "ProjectSettings|Every project can make deployments to environments either
msgstr "Tout projet peut effectuer des déploiements vers des environnements via CI/CD ou appel d'API. Les membres extérieurs au projet ont un accès en lecture seule."
msgid "ProjectSettings|Everyone"
-msgstr ""
+msgstr "Tout le monde"
msgid "ProjectSettings|Existing merge requests and protected branches are not affected."
msgstr "Les demandes de fusion et les branches protégées existantes ne sont pas affectées."
@@ -32009,7 +32441,7 @@ msgid "ProjectSettings|Failed to update tag!"
msgstr "Impossible de mettre à jour l’étiquette !"
msgid "ProjectSettings|Fast-forward merge"
-msgstr ""
+msgstr "Fusion en avance rapide"
msgid "ProjectSettings|Fast-forward merges only."
msgstr "Fusions en avance rapide uniquement."
@@ -32036,7 +32468,7 @@ msgid "ProjectSettings|Housekeeping, export, archive, change path, transfer, and
msgstr "Maintenance, export, archivage, modification de chemin, transfert et suppression."
msgid "ProjectSettings|How do they differ?"
-msgstr ""
+msgstr "Quelles sont les différences ?"
msgid "ProjectSettings|If merge trains are enabled, merging is only possible if the branch can be rebased without conflicts."
msgstr "Si les trains de fusion sont activés, la fusion n'est possible que si la branche peut être rebasée sans conflits."
@@ -32072,13 +32504,13 @@ msgid "ProjectSettings|Merge checks"
msgstr "Vérifications de fusion"
msgid "ProjectSettings|Merge commit"
-msgstr ""
+msgstr "Commit de fusion"
msgid "ProjectSettings|Merge commit message template"
msgstr "Modèle de message pour les validations de fusion"
msgid "ProjectSettings|Merge commit with semi-linear history"
-msgstr ""
+msgstr "Commit de fusion avec un historique semi-linéaire"
msgid "ProjectSettings|Merge method"
msgstr "Méthode de fusion"
@@ -32090,13 +32522,13 @@ msgid "ProjectSettings|Merge requests"
msgstr "Demandes de fusion"
msgid "ProjectSettings|Merge requests approved for merge are queued, and pipelines validate the combined results of the source and target branches before merge. %{link_start}What are merge trains?%{link_end}"
-msgstr ""
+msgstr "Les demandes de fusion approuvées pour être fusionnées sont mises en file d'attente, et les pipelines valident les résultats combinés des branches source et cible avant la fusion. %{link_start}Que sont les trains de fusion ?%{link_end}"
msgid "ProjectSettings|Merge requests can't be merged if the latest pipeline did not succeed or is still running."
msgstr "Les demandes de fusion ne peuvent pas être fusionnées si le dernier pipeline a échoué ou est toujours en cours d'exécution."
msgid "ProjectSettings|Merge suggestions"
-msgstr ""
+msgstr "Suggestions de fusion"
msgid "ProjectSettings|Merging is only allowed when the source branch is up-to-date with its target."
msgstr "La fusion n’est autorisée que lorsque la branche source est à jour avec sa cible."
@@ -32104,6 +32536,9 @@ msgstr "La fusion n’est autorisée que lorsque la branche source est à jour a
msgid "ProjectSettings|Monitor"
msgstr "Supervision"
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr "Surveiller la santé de votre projet et répondre aux incidents."
+
msgid "ProjectSettings|No merge commits are created."
msgstr "Aucun commit de fusion n'est créé."
@@ -32149,6 +32584,9 @@ msgstr "Visibilité du projet"
msgid "ProjectSettings|Public"
msgstr "Publique"
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr "Publiez, stockez et visualisez les paquets dans un projet."
+
msgid "ProjectSettings|Releases"
msgstr "Versions"
@@ -32183,16 +32621,16 @@ msgid "ProjectSettings|Security & Compliance for this project"
msgstr "Sécurité & Conformité de ce projet"
msgid "ProjectSettings|Select the default branch for this project, and configure the template for branch names."
-msgstr ""
+msgstr "Sélectionnez la branche par défaut pour ce projet et configurez le modèle pour les noms de branches."
msgid "ProjectSettings|Set the default behavior of this option in merge requests. Changes to this are also applied to existing merge requests."
-msgstr ""
+msgstr "Définissez le comportement par défaut de cette option dans les demandes de fusion. Les modifications apportées sont également appliquées aux demandes de fusion existantes."
msgid "ProjectSettings|Share code with others outside the project."
msgstr "Partagez du code avec d'autres personnes en dehors du projet."
msgid "ProjectSettings|Show default award emojis"
-msgstr ""
+msgstr "Afficher les émojis de récompense par défaut"
msgid "ProjectSettings|Show link to create or view a merge request when pushing from the command line"
msgstr ""
@@ -32270,7 +32708,7 @@ msgid "ProjectSettings|Users can only push commits to this repository if the com
msgstr ""
msgid "ProjectSettings|Users can only push commits to this repository if the committer name is consistent with their git config username."
-msgstr ""
+msgstr "Les utilisateurs ne peuvent pousser sur ce dépôt que si le nom du contributeur est cohérent avec son nom d'utilisateur dans la configuration git."
msgid "ProjectSettings|Users can request access"
msgstr "Les utilisateurs peuvent demander l'accès"
@@ -32278,11 +32716,11 @@ msgstr "Les utilisateurs peuvent demander l'accès"
msgid "ProjectSettings|View and edit files in this project."
msgstr "Afficher et modifier des fichiers dans ce projet."
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
-msgstr "Afficher et modifier les fichiers de ce projet. Les membres qui n'en font pas partie n'ont qu'un accès en lecture."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
+msgstr "Voir et modifier les fichiers de ce projet. Défini sur **Tout le monde ayant accès**, les membres qui ne font pas partie du projet n'ont qu'un accès en lecture seule."
msgid "ProjectSettings|View project analytics."
-msgstr ""
+msgstr "Voir les analytiques du projet."
msgid "ProjectSettings|Visibility options for this fork are limited by the current visibility of the source project."
msgstr "Les options de visibilité de ce fork sont limitées par la visibilité actuelle du projet source."
@@ -32306,7 +32744,7 @@ msgid "ProjectSettings|When merge request pipelines are enabled in the CI/CD con
msgstr "Quand les pipelines des demandes de fusion sont activés dans le fichier de configuration CI/CD, ceux-ci valident les résultats combinés des branches source et cible. %{link_start}Comment configurer les pipelines pour les demandes de fusion ?%{link_end}"
msgid "ProjectSettings|When semi-linear merge is not possible, the user is given the option to rebase."
-msgstr ""
+msgstr "Lorsque la fusion semi-linéaire n’est pas possible, l’utilisateur a la possibilité de rebaser."
msgid "ProjectSettings|When there is a merge conflict, the user is given the option to rebase."
msgstr "En cas de conflit de fusion, l’utilisateur a la possibilité de rebaser."
@@ -32324,7 +32762,7 @@ msgid "ProjectTemplates|Android"
msgstr "Android"
msgid "ProjectTemplates|GitLab Cluster Management"
-msgstr ""
+msgstr "Gestion de grappe de serveurs avec GitLab"
msgid "ProjectTemplates|Gitpod/Spring Petclinic"
msgstr "Gitpod/Spring Petclinic"
@@ -32354,10 +32792,13 @@ msgid "ProjectTemplates|Netlify/Jekyll"
msgstr "Netlify/Jekyll"
msgid "ProjectTemplates|Netlify/Plain HTML"
-msgstr ""
+msgstr "Netlify/HTML brut"
msgid "ProjectTemplates|NodeJS Express"
-msgstr ""
+msgstr "NodeJS Express"
+
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr "Pages/Bridgetown"
msgid "ProjectTemplates|Pages/Gatsby"
msgstr "Pages/Gatsby"
@@ -32381,7 +32822,7 @@ msgid "ProjectTemplates|Pages/Pelican"
msgstr "Pages/Pelican"
msgid "ProjectTemplates|Pages/Plain HTML"
-msgstr ""
+msgstr "Pages/HTML brut"
msgid "ProjectTemplates|Ruby on Rails"
msgstr "Ruby on Rails"
@@ -32453,7 +32894,7 @@ msgid "Projects that can be accessed"
msgstr "Projets accessibles"
msgid "Projects to index"
-msgstr ""
+msgstr "Projets à indexer"
msgid "Projects with critical vulnerabilities"
msgstr "Projets avec des vulnérabilités critiques"
@@ -32477,7 +32918,7 @@ msgid "Projects with write access"
msgstr ""
msgid "ProjectsDropdown|Frequently visited"
-msgstr "Les plus consultés"
+msgstr "Fréquemment consultés"
msgid "ProjectsDropdown|Loading projects"
msgstr "Chargement des projets"
@@ -32498,7 +32939,7 @@ msgid "ProjectsDropdown|This feature requires browser localStorage support"
msgstr "Cette fonctionnalité nécessite un navigateur prenant en charge localStorage"
msgid "ProjectsNew|Allows you to immediately clone this project’s repository. Skip this if you plan to push up an existing repository."
-msgstr ""
+msgstr "Vous permet de cloner immédiatement le dépôt de ce projet. Ignorez ceci si vous prévoyez de pousser un dépôt existant."
msgid "ProjectsNew|Analyze your source code for known security vulnerabilities."
msgstr "Analyser votre code source à la recherche de failles de sécurité connues."
@@ -32528,13 +32969,13 @@ msgid "ProjectsNew|Create new project"
msgstr "Créer un nouveau projet"
msgid "ProjectsNew|Description format"
-msgstr ""
+msgstr "Description formatée"
msgid "ProjectsNew|Enable Static Application Security Testing (SAST)"
msgstr "Activer les Tests Statiques de Sécurité des Applications (SAST)"
msgid "ProjectsNew|Import"
-msgstr ""
+msgstr "Importer"
msgid "ProjectsNew|Import project"
msgstr "Importer un projet"
@@ -32552,10 +32993,10 @@ msgid "ProjectsNew|No import options available"
msgstr "Aucune option d'importation n'est disponible"
msgid "ProjectsNew|Pick a group or namespace"
-msgstr ""
+msgstr "Choisissez un groupe ou un espace de noms"
msgid "ProjectsNew|Pick a group or namespace where you want to create this project."
-msgstr ""
+msgstr "Choisissez le groupe ou l'espace de noms où vous souhaitez créer ce projet."
msgid "ProjectsNew|Project Configuration"
msgstr "Configuration du Projet"
@@ -32567,7 +33008,7 @@ msgid "ProjectsNew|Recommended if you're new to GitLab"
msgstr ""
msgid "ProjectsNew|Run CI/CD for external repository"
-msgstr ""
+msgstr "Exécuter la CI/CD pour un dépôt externe"
msgid "ProjectsNew|Visibility Level"
msgstr "Niveau de visibilité"
@@ -32606,7 +33047,7 @@ msgid "PrometheusService|Custom metrics"
msgstr "Métriques personnalisées"
msgid "PrometheusService|Custom metrics require Prometheus installed on a cluster with environment scope \"*\" OR a manually configured Prometheus to be available."
-msgstr ""
+msgstr "Les métriques personnalisées nécessitent que Prometheus soit installé sur une grappe de serveurs avec la portée d'environnement « * » OU qu'une instance Prometheus configurée manuellement soit disponible."
msgid "PrometheusService|Enable Prometheus to define custom metrics, using either option above"
msgstr "Activez Prometheus pour définir des métriques personnalisées, en utilisant l'une des options ci-dessus"
@@ -32684,7 +33125,7 @@ msgid "Promote"
msgstr "Promouvoir"
msgid "Promote issue to an epic"
-msgstr ""
+msgstr "Promouvoir le ticket en épopée"
msgid "Promote issue to incident"
msgstr "Promouvoir le ticket en incident"
@@ -32705,7 +33146,7 @@ msgid "PromoteMilestone|Promotion failed - %{message}"
msgstr "La promotion a échoué - %{message}"
msgid "Promoted issue to an epic."
-msgstr ""
+msgstr "Le ticket a été promu en épopée."
msgid "Promotes issue to incident"
msgstr ""
@@ -32717,10 +33158,10 @@ msgid "Promotions|Add %{link_start} description templates %{link_end} to help yo
msgstr "Ajoutez des %{link_start} modèles de description %{link_end} pour aider vos contributeurs à communiquer efficacement !"
msgid "Promotions|Add Group Webhooks and GitLab Enterprise Edition."
-msgstr "Ajouter Points d'Ancrage Web de Groupe et GitLab Édition Entreprise."
+msgstr "Ajouter Crochets Web de Groupe et GitLab Édition Entreprise."
msgid "Promotions|Better Protected Branches"
-msgstr ""
+msgstr "Branches mieux protégées"
msgid "Promotions|Burndown Charts are visual representations of the progress of completing a milestone. At a glance, you see the current state for the completion a given milestone. Without them, you would have to organize the data from the milestone and plot it yourself to have the same sense of progress."
msgstr "Les graphiques d'avancement sont des représentations visuelles de la progression de la réalisation d’un jalon. En un coup d'œil, vous voyez l'état actuel de la réalisation d'un jalon donné. Sans eux, vous devrez organiser les données à partir du jalon et le tracer vous-même pour avoir la même impression de progrès."
@@ -32828,7 +33269,7 @@ msgid "Promotions|Upgrade your plan to activate Audit Events."
msgstr "Mettez à niveau votre forfait pour activer les Événements d'Audit."
msgid "Promotions|Upgrade your plan to activate Group Webhooks."
-msgstr "Mettez à niveau votre forfait pour activer les Points d'Ancrage Web de Groupe."
+msgstr "Mettez à niveau votre forfait pour activer les Crochets Web de Groupe."
msgid "Promotions|Upgrade your plan to improve merge requests."
msgstr "Mettez à niveau votre forfait pour améliorer les demandes de fusion."
@@ -32840,13 +33281,13 @@ msgid "Promotions|Upgrade your plan to improve repositories."
msgstr "Mettez à niveau votre forfait pour améliorer les dépôts."
msgid "Promotions|Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
-msgstr "Les points d'ancrage web vous permettent de déclencher une URL si, par exemple, du nouveau code est poussé ou un nouveau ticket est créé. Vous pouvez en configurer pour écouter des événements spécifiques tels que des poussées, des tickets ou des demandes de fusion. Les points d'ancrage web de groupe s’appliqueront à tous les projets d'un groupe, ce qui vous permet d'uniformiser leurs fonctionnalités dans la totalité de votre groupe."
+msgstr "Les crochets web vous permettent de déclencher une URL si, par exemple, du nouveau code est poussé ou un nouveau ticket est créé. Vous pouvez en configurer pour écouter des événements spécifiques tels que des poussées, des tickets ou des demandes de fusion. Les crochets web de groupe s’appliqueront à tous les projets d'un groupe, ce qui vous permet d'uniformiser leurs fonctionnalités dans la totalité de votre groupe."
msgid "Promotions|Weight"
msgstr "Poids"
msgid "Promotions|Weighting your issue"
-msgstr ""
+msgstr "Alourdissez votre ticket"
msgid "Promotions|When you have a lot of issues, it can be hard to get an overview. By adding a weight to your issues, you can get a better idea of the effort, cost, required time, or value of each, and so better manage them."
msgstr "Lorsque vous avez de nombreux tickets, il peut être difficile d'en avoir une vue d'ensemble. En leur ajoutant un poids, vous pouvez avoir une meilleure idée de l'effort, du coût, du temps nécessaire ou de la valeur de chacun, et ainsi mieux les gérer."
@@ -32881,14 +33322,11 @@ msgstr ""
msgid "Protected Branches"
msgstr "Branches protégées"
-msgid "Protected Environment"
-msgstr "Environnement protégé"
-
msgid "Protected Paths: requests"
msgstr "Chemins protégés : demandes"
msgid "Protected Tag"
-msgstr ""
+msgstr "Étiquette protégée"
msgid "Protected Tags"
msgstr "Étiquettes protégées"
@@ -32945,7 +33383,7 @@ msgid "ProtectedBranch|Branch:"
msgstr "Branche :"
msgid "ProtectedBranch|By default, protected branches restrict who can modify the branch."
-msgstr ""
+msgstr "Par défaut, les branches protégées restreignent la possibilité de modifier la branche."
msgid "ProtectedBranch|Code owner approval"
msgstr ""
@@ -33101,7 +33539,7 @@ msgid "ProtectedTags|default"
msgstr "par défaut"
msgid "ProtectedTag|By default, protected tags restrict who can modify the tag."
-msgstr ""
+msgstr "Par défaut, les étiquettes protégées restreignent la possibilité de modifier l'étiquette."
msgid "ProtectedTag|Learn more."
msgstr "En savoir plus."
@@ -33122,7 +33560,7 @@ msgid "Protip: %{linkStart}Auto DevOps%{linkEnd} uses Kubernetes clusters to dep
msgstr "Conseil d'expert : %{linkStart}Auto DevOps%{linkEnd} utilise des grappes de serveurs Kubernetes pour déployer votre code !"
msgid "Provide Feedback"
-msgstr ""
+msgstr "Donnez Votre Avis"
msgid "Provide a number our sales team can use to call you."
msgstr "Indiquez un numéro sur lequel notre équipe commerciale pourra vous appeler."
@@ -33140,7 +33578,7 @@ msgid "Proxy support for this API is not available currently"
msgstr "Le support du proxy pour cette API n'est pas disponible pour le moment"
msgid "Public"
-msgstr ""
+msgstr "Public"
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "Public — le groupe ainsi que n’importe quel projet public est accessible sans authentification."
@@ -33152,7 +33590,7 @@ msgid "Public Access Help"
msgstr "Aide sur l’Accès Public"
msgid "Public deploy keys"
-msgstr ""
+msgstr "Clés de déploiement publiques"
msgid "Public pipelines"
msgstr "Pipelines publics"
@@ -33161,7 +33599,7 @@ msgid "Public projects Minutes cost factor"
msgstr "Facteur de coût des Minutes des projets publics"
msgid "Public projects are an easy way to allow everyone to have read-only access."
-msgstr ""
+msgstr "Les projets publics sont un moyen simple de permettre à chacun d'avoir un accès en lecture seule."
msgid "Publish to status page"
msgstr "Publier sur la page d'état"
@@ -33179,7 +33617,7 @@ msgid "Pull"
msgstr "Récupérer"
msgid "Pull mirroring updated %{time}."
-msgstr ""
+msgstr "La mise en miroir de type pull a été mise à jour %{time}."
msgid "Pull requests from fork are not supported"
msgstr "Les demandes « pull » depuis un fork ne sont pas prises en charge"
@@ -33206,7 +33644,7 @@ msgid "Push"
msgstr "Push"
msgid "Push Rule updated successfully."
-msgstr ""
+msgstr "Règle de Poussée mise à jour avec succès."
msgid "Push Rules"
msgstr "Règles de poussage Git"
@@ -33215,10 +33653,10 @@ msgid "Push Rules updated successfully."
msgstr "Règles de Poussée mises à jour avec succès."
msgid "Push an existing Git repository"
-msgstr ""
+msgstr "Pousser un dépôt Git existant"
msgid "Push an existing folder"
-msgstr ""
+msgstr "Pousser un dossier existant"
msgid "Push code to the repository."
msgstr "Pousser du code vers le dépôt."
@@ -33311,7 +33749,7 @@ msgid "PushRule|Push rules"
msgstr "Règles de poussée"
msgid "PushRule|Reject inconsistent user name"
-msgstr ""
+msgstr "Rejeter les noms d'utilisateurs incohérents"
msgid "PushRule|Reject unverified users"
msgstr "Rejeter les utilisateurs non vérifiés"
@@ -33335,7 +33773,7 @@ msgid "PushoverService|Enter new user key"
msgstr "Entrez la nouvelle clé d'utilisateur"
msgid "PushoverService|Enter your application key."
-msgstr ""
+msgstr "Entrez la clé de votre application."
msgid "PushoverService|Enter your user key."
msgstr "Entrez votre clé d’utilisateur."
@@ -33362,10 +33800,10 @@ msgid "PushoverService|Normal priority"
msgstr "Priorité normale"
msgid "PushoverService|See project %{project_full_name}"
-msgstr ""
+msgstr "Voir le projet %{project_full_name}"
msgid "PushoverService|Total commits count: %{total_commits_count}"
-msgstr ""
+msgstr "Nombre total de validations : %{total_commits_count}"
msgid "QualitySummary|Project quality"
msgstr "Qualité du projet"
@@ -33404,7 +33842,7 @@ msgid "Rake Tasks Help"
msgstr "Aide sur les Tâches Rake"
msgid "Random"
-msgstr ""
+msgstr "Hasard"
msgid "Rate Limits"
msgstr "Limites de Fréquence"
@@ -33419,7 +33857,7 @@ msgid "Rate limits can help reduce request volume (like from crawlers or abusive
msgstr "Les limitations de fréquence contribuent à réduire le volume de requêtes (p. ex. celles de robots d'exploration ou de bots abusifs)."
msgid "Raw blob request rate limit per minute"
-msgstr ""
+msgstr "Limite du nombre de demandes de blob brut par minute"
msgid "Raw blob requests"
msgstr "Demandes de blobs bruts"
@@ -33452,7 +33890,7 @@ msgid "Ready to get started with GitLab? Follow these steps to set up your works
msgstr "Prêt à démarrer avec GitLab ? Suivez ces étapes pour configurer votre espace de travail, planifier et valider les modifications et déployer votre projet."
msgid "Ready to merge by members who can write to the target branch."
-msgstr ""
+msgstr "Prêt à fusionner par les membres qui peuvent écrire dans la branche cible."
msgid "Ready to merge!"
msgstr "Prêt pour la fusion !"
@@ -33491,16 +33929,16 @@ msgid "Receive notifications about your own activity"
msgstr "Recevoir des notifications sur votre propre activité"
msgid "Receive product marketing emails"
-msgstr ""
+msgstr "Recevoir des courriels commerciaux sur le produit"
msgid "Recent"
msgstr "Récent"
msgid "Recent Project Activity"
-msgstr ""
+msgstr "Activité récente du projet"
msgid "Recent Searches Service is unavailable"
-msgstr ""
+msgstr "Le Service des Recherches Récentes n'est pas disponible"
msgid "Recent events"
msgstr "Événements récents"
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] "L’affichage sera réactualisé dans une seconde avec le statut mis à jour..."
msgstr[1] "L’affichage sera réactualisé dans %d secondes avec le statut mis à jour…"
+msgid "Refreshing..."
+msgstr "Rafraîchissement en cours..."
+
msgid "Regenerate export"
msgstr ""
@@ -33583,7 +34024,7 @@ msgid "Register / Sign In"
msgstr "Inscription / Connexion"
msgid "Register Two-Factor Authenticator"
-msgstr ""
+msgstr "Enregistrer un Authentificateur A2F"
msgid "Register Universal Two-Factor (U2F) Device"
msgstr "Enregistrer un Appareil Universel à Deux Facteurs (U2F)"
@@ -33601,7 +34042,7 @@ msgid "Register the runner with this URL:"
msgstr "Enregistrez l'exécuteur avec cette URL :"
msgid "Register with two-factor app"
-msgstr ""
+msgstr "S'inscrire avec l'appli A2F"
msgid "Register with:"
msgstr "S'inscrire avec :"
@@ -33717,7 +34158,7 @@ msgid "Release does not exist"
msgstr "La version n'existe pas"
msgid "Release does not have the same project as the milestone"
-msgstr ""
+msgstr "La version n'a pas le même projet que le jalon"
msgid "Release notes"
msgstr "Notes de version"
@@ -33752,6 +34193,9 @@ msgstr "Dossier d'exploitation"
msgid "ReleaseAssetLinkType|Runbooks"
msgstr "Dossiers d'exploitation"
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr "Date de sortie"
@@ -33816,7 +34260,7 @@ msgid "Remove %{displayReference}"
msgstr "Supprimer %{displayReference}"
msgid "Remove Zoom meeting"
-msgstr ""
+msgstr "Supprimer la réunion Zoom"
msgid "Remove Zoom meeting."
msgstr "Supprimer la réunion Zoom."
@@ -33849,7 +34293,7 @@ msgid "Remove avatar"
msgstr "Supprimer l’avatar"
msgid "Remove card"
-msgstr ""
+msgstr "Supprimer la carte"
msgid "Remove child epic from an epic"
msgstr "Supprimer l'épopée enfant d'une épopée"
@@ -33879,10 +34323,10 @@ msgid "Remove fork relationship"
msgstr ""
msgid "Remove from batch"
-msgstr ""
+msgstr "Supprimer du lot"
msgid "Remove from epic"
-msgstr ""
+msgstr "Supprimer de l'épopée"
msgid "Remove group"
msgstr "Supprimer le groupe"
@@ -33894,13 +34338,13 @@ msgid "Remove iteration"
msgstr "Supprimer l'itération"
msgid "Remove license"
-msgstr ""
+msgstr "Supprimer la licence"
msgid "Remove limit"
msgstr ""
msgid "Remove link"
-msgstr ""
+msgstr "Supprimer le lien"
msgid "Remove list"
msgstr "Supprimer la liste"
@@ -33912,7 +34356,7 @@ msgid "Remove logo"
msgstr "Supprimer le logo"
msgid "Remove member"
-msgstr ""
+msgstr "Supprimer le membre"
msgid "Remove milestone"
msgstr "Supprimer le jalon"
@@ -33936,10 +34380,10 @@ msgid "Remove secondary email"
msgstr "Supprimer l'adresse de courriel secondaire"
msgid "Remove spent time"
-msgstr ""
+msgstr "Supprimer le temps passé"
msgid "Remove time estimate"
-msgstr ""
+msgstr "Supprimer l'estimation du temps"
msgid "Remove topic avatar"
msgstr "Supprimer l'avatar du sujet"
@@ -34005,7 +34449,7 @@ msgid "Removed upload with id %{id}"
msgstr ""
msgid "RemovedProjects|No projects pending deletion found"
-msgstr ""
+msgstr "Aucun projet en attente de suppression n'a été trouvé"
msgid "RemovedProjects|Projects that are pending deletion that you have access to are listed here."
msgstr "Les projets dont la suppression est en attente et auxquels vous avez accès sont listés ici."
@@ -34032,19 +34476,19 @@ msgid "Removes all labels."
msgstr "Supprime toutes les étiquettes."
msgid "Removes an issue from an epic."
-msgstr ""
+msgstr "Supprime un ticket d'une épopée."
msgid "Removes parent epic %{epic_ref}."
msgstr ""
msgid "Removes spent time."
-msgstr ""
+msgstr "Supprime le temps passé."
msgid "Removes the due date."
msgstr "Supprime la date d'échéance."
msgid "Removes time estimate."
-msgstr ""
+msgstr "Supprime l'estimation du temps."
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr "La suppression de ce groupe supprime également tous les projets enfants, y compris les projets archivés et leurs ressources."
@@ -34074,7 +34518,7 @@ msgid "Reopen %{issueType}"
msgstr "Rouvrir %{issueType}"
msgid "Reopen %{noteable}"
-msgstr ""
+msgstr "Rouvrir %{noteable}"
msgid "Reopen epic"
msgstr "Rouvrir l’épopée"
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr "Réouverture..."
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34101,7 +34548,7 @@ msgid "Replace %{name}"
msgstr "Remplacer %{name}"
msgid "Replace all label(s)"
-msgstr ""
+msgstr "Remplacer toutes les étiquettes"
msgid "Replace audio"
msgstr ""
@@ -34145,11 +34592,8 @@ msgstr "Répondre…"
msgid "Report Finding not found"
msgstr "Découverte de Rapport introuvable"
-msgid "Report abuse"
-msgstr "Signaler un abus"
-
-msgid "Report abuse to admin"
-msgstr "Signaler un abus à l'administrateur"
+msgid "Report abuse to administrator"
+msgstr ""
msgid "Report couldn't be prepared."
msgstr "Le rapport n'a pas pu être préparé."
@@ -34161,7 +34605,7 @@ msgid "Report version not provided, %{report_type} report type supports versions
msgstr "Version de rapport non fournie, le type rapport de %{report_type} prend en charge les versions : %{supported_schema_versions}. GitLab essaiera de valider ce rapport avec les versions les plus anciennes prises en charge pour ce type de rapport afin d'afficher toutes les erreurs mais ne l'ingérera pas"
msgid "Report your license usage data to GitLab"
-msgstr ""
+msgstr "Communiquer les données d'utilisation de votre licence à GitLab"
msgid "Reported %{timeAgo} by %{reportedBy}"
msgstr ""
@@ -34244,28 +34688,28 @@ msgid "Reports|Head report parsing error:"
msgstr ""
msgid "Reports|Identifier"
-msgstr ""
+msgstr "Identifiant"
msgid "Reports|Metrics report scanning detected no new changes"
msgstr "L’analyse des rapports métriques n’a détecté aucun nouveau changement"
msgid "Reports|Metrics reports are loading"
-msgstr ""
+msgstr "Les rapports des métriques sont en cours de chargement"
msgid "Reports|Metrics reports changed on %{numberOfChanges} %{pointsString}"
-msgstr ""
+msgstr "Les rapports des métriques ont changé sur %{numberOfChanges} %{pointsString}"
msgid "Reports|Metrics reports did not change"
-msgstr ""
+msgstr "Les rapports des métriques n'ont pas changé"
msgid "Reports|Metrics reports failed loading results"
-msgstr ""
+msgstr "Les rapports des métriques n'ont pas réussi à charger les résultats"
msgid "Reports|Metrics reports failed to load results"
-msgstr ""
+msgstr "Les rapports sur les métriques n’ont pas réussi à charger les résultats"
msgid "Reports|Metrics reports: %{strong_start}%{numberOfChanges}%{strong_end} %{changes}"
-msgstr ""
+msgstr "Rapports sur les métriques : %{strong_start}%{numberOfChanges}%{strong_end} %{changes}"
msgid "Reports|New"
msgstr "Nouveau"
@@ -34280,10 +34724,10 @@ msgid "Reports|Test summary"
msgstr "Synthèse des tests"
msgid "Reports|Test summary failed to load results"
-msgstr ""
+msgstr "Échec du chargement des résultats de la synthèse des tests"
msgid "Reports|Test summary results are loading"
-msgstr ""
+msgstr "Les résultats de la synthèse des tests sont en cours de chargement"
msgid "Reports|Tool"
msgstr "Outil"
@@ -34386,7 +34830,7 @@ msgid "RepositoriesAnalytics|Projects with Coverage: %{projectCount}"
msgstr "Projets avec Couverture : %{projectCount}"
msgid "RepositoriesAnalytics|Repositories Analytics"
-msgstr "Analyses des Dépôts"
+msgstr "Analytique des Dépôts"
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
@@ -34398,7 +34842,7 @@ msgid "Repository"
msgstr "Dépôt"
msgid "Repository Analytics"
-msgstr "Analyses du dépôt"
+msgstr "Analytique du dépôt"
msgid "Repository Graph"
msgstr "Graphe du dépôt"
@@ -34482,13 +34926,16 @@ msgid "RepositorySettingsAccessLevel|Select"
msgstr "Sélectionner"
msgid "Request"
-msgstr ""
+msgstr "Requête"
msgid "Request Access"
msgstr "Demander l’accès"
+msgid "Request Headers"
+msgstr "En-têtes de requête"
+
msgid "Request a new one"
-msgstr ""
+msgstr "Demandez-en un nouveau"
msgid "Request data is too large"
msgstr "Les données de la requête sont trop volumineuses"
@@ -34500,10 +34947,10 @@ msgid "Request parameter %{param} is missing."
msgstr "Le paramètre de requête %{param} est manquant."
msgid "Request review from"
-msgstr ""
+msgstr "Demander une revue de code à"
msgid "Request time"
-msgstr ""
+msgstr "Heure de la requête"
msgid "Request to link SAML account must be authorized"
msgstr ""
@@ -34548,7 +34995,7 @@ msgid "Required only if you are not using role instance credentials."
msgstr ""
msgid "Requirement"
-msgstr ""
+msgstr "Exigence"
msgid "Requirement %{reference} has been added"
msgstr "L'exigence %{reference} a été ajoutée"
@@ -34594,13 +35041,13 @@ msgid "Resend"
msgstr "Renvoyer"
msgid "Resend Request"
-msgstr "Renvoyer la Demande"
+msgstr "Renvoyer la Requête"
msgid "Resend confirmation e-mail"
msgstr "Renvoyer le courriel de confirmation"
msgid "Resend confirmation email"
-msgstr ""
+msgstr "Renvoyer un courriel de confirmation"
msgid "Resend invite"
msgstr "Renvoyer l'invitation"
@@ -34618,7 +35065,7 @@ msgid "Reset error tracking access token"
msgstr "Réinitialiser le jeton d'accès du suivi des erreurs"
msgid "Reset file"
-msgstr ""
+msgstr "Réinitialiser le fichier"
msgid "Reset filters"
msgstr "Réinitialiser les filtres"
@@ -34633,13 +35080,13 @@ msgid "Reset registration token"
msgstr "Réinitialiser le jeton d'inscription"
msgid "Reset template"
-msgstr ""
+msgstr "Réinitialiser le modèle"
msgid "Reset to project defaults"
msgstr "Réinitialiser aux valeurs par défaut du projet"
msgid "Resolve"
-msgstr ""
+msgstr "Résoudre"
msgid "Resolve conflicts"
msgstr "Résoudre les conflits"
@@ -34651,7 +35098,7 @@ msgid "Resolve these conflicts or ask someone with write access to this reposito
msgstr "Résolvez ces conflits ou demandez à une personne disposant d'un accès en écriture sur ce référentiel de faire la fusion localement."
msgid "Resolve thread"
-msgstr ""
+msgstr "Résoudre le fil de conversation"
msgid "Resolved"
msgstr ""
@@ -34672,10 +35119,10 @@ msgid "Resource link added"
msgstr "Lien de ressource ajouté"
msgid "Response"
-msgstr ""
+msgstr "Réponse"
msgid "Response didn't include `service_desk_address`"
-msgstr ""
+msgstr "La réponse n'incluait pas `service_desk_address`"
msgid "Response metrics (AWS ELB)"
msgstr "Métriques de réponse (AWS ELB)"
@@ -34687,7 +35134,7 @@ msgid "Response metrics (HA Proxy)"
msgstr "Métriques de réponse (HA Proxy)"
msgid "Response metrics (NGINX Ingress VTS)"
-msgstr ""
+msgstr "Métriques de réponse (NGINX Ingress avec VTS)"
msgid "Response metrics (NGINX Ingress)"
msgstr "Métriques de réponse (nginx Ingress)"
@@ -34696,7 +35143,7 @@ msgid "Response metrics (NGINX)"
msgstr "Métriques de réponse (nginx)"
msgid "Response text"
-msgstr ""
+msgstr "Texte de réponse"
msgid "Restart GitLab to apply changes."
msgstr "Redémarrez GitLab pour appliquer les modifications."
@@ -34708,10 +35155,10 @@ msgid "Restore"
msgstr "Restaurer"
msgid "Restore group"
-msgstr ""
+msgstr "Restaurer le groupe"
msgid "Restore project"
-msgstr ""
+msgstr "Restaurer le projet"
msgid "Restoring projects"
msgstr "Restauration des projets"
@@ -34734,6 +35181,9 @@ msgstr "Restreindre les projets de cet exécuteur"
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr "Reprendre"
@@ -34762,7 +35212,7 @@ msgid "Retry this job"
msgstr "Relancer cette tâche"
msgid "Retry this job in order to create the necessary resources."
-msgstr ""
+msgstr "Réessayez cette tâche afin de créer les ressources nécessaires."
msgid "Retry verification"
msgstr "Relancer la vérification"
@@ -34808,8 +35258,8 @@ msgstr "Examinez le projet cible avant d'y publier quelque chose pour éviter d'
msgid "Review time"
msgstr "Durée de revue"
-msgid "Review time is defined as the time it takes from first comment until merged."
-msgstr "La durée de revue est définie comme le temps passé depuis le premier commentaire jusqu'à la fusion."
+msgid "Review time is the amount of time since the first comment in a merge request."
+msgstr "La durée de revue est le temps écoulé à compter du premier commentaire ajouté à une demande de fusion."
msgid "ReviewApp|Enable Review App"
msgstr "Activer Review App"
@@ -34871,7 +35321,7 @@ msgid "Rollback"
msgstr ""
msgid "Rollout of free user limits within GitLab.com. Do not edit these values unless approval has been given via %{link_start}this issue%{link_end}."
-msgstr ""
+msgstr "Déploiement des limites d'utilisateurs gratuits sur GitLab.com. Ne modifiez pas ces valeurs à moins que l'approbation n'ait été donnée via %{link_start}ce ticket%{link_end}."
msgid "Ruby"
msgstr "Ruby"
@@ -34903,6 +35353,9 @@ msgstr "Démarrer la maintenance"
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr "Exécuter des tâches de maintenance pour optimiser automatiquement les dépôts Git. Désactiver cette option fera que les performances vont se dégrader au fil du temps."
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr "Exécuter des tâches manuelles ou différées"
@@ -34958,13 +35411,13 @@ msgstr[0] "%{strongStart}%{count}%{strongEnd} exécuteur sera définitivement su
msgstr[1] "%{strongStart}%{count}%{strongEnd} exécuteurs seront définitivement supprimés et ne seront plus disponibles pour les projets ou les groupes de l'instance. Êtes-vous sûr de vouloir continuer ?"
msgid "Runners|A capacity of 1 enables warm HA through Auto Scaling group re-spawn. A capacity of 2 enables hot HA because the service is available even when a node is lost. A capacity of 3 or more enables hot HA and manual scaling of runner fleet."
-msgstr ""
+msgstr "Une capacité de 1 active la haute disponibilité avec reprise semi-automatique via le redémarrage du groupe Auto Scaling. Une capacité de 2 active la haute disponibilité avec reprise automatique car le service reste disponible même quand un nœud est perdu. Une capacité de 3 ou plus active la haute disponibilité avec reprise automatique et mise à l'échelle manuelle de la flotte d'exécuteurs."
msgid "Runners|A new version is available"
msgstr "Une nouvelle version est disponible"
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
-msgstr "Une tâche périodique en arrière-plan supprime les exécuteurs qui n'ont pas contacté GitLab dans plus de %{elapsedTime}. %{linkStart}Puis-je voir combien d'exécuteurs ont été supprimés ?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgstr "Une tâche périodique en arrière-plan supprime les exécuteurs qui n'ont pas contacté GitLab depuis plus de %{elapsedTime}. Seuls les exécuteurs enregistrés sur ce groupe sont supprimés. Ceux dans les sous-groupes et dans les projets ne le sont pas. %{linkStart}Puis-je voir combien d'exécuteurs ont été supprimés ?%{linkEnd}"
msgid "Runners|Active"
msgstr "Actif"
@@ -34985,11 +35438,14 @@ msgid "Runners|Amazon Linux 2 Docker HA with manual scaling and optional schedul
msgstr "Docker HA Amazon Linux 2 avec mise à l'échelle manuelle et planification facultative. Spot %{percentage}."
msgid "Runners|Amazon Linux 2 Docker HA with manual scaling and optional scheduling. Non-spot."
-msgstr ""
+msgstr "Docker HA Amazon Linux 2 avec mise à l'échelle manuelle et planification facultative. Non Spot."
msgid "Runners|An error has occurred fetching instructions"
msgstr "Une erreur s'est produite lors de la récupération des instructions"
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr "Une erreur s'est produite lors de la suppression. Certains exécuteurs peuvent ne pas avoir été supprimés."
+
msgid "Runners|An upgrade is available for this runner"
msgstr "Une mise à jour est disponible pour cet exécuteur"
@@ -35011,6 +35467,9 @@ msgstr "Associé à un ou plusieurs projets"
msgid "Runners|Available"
msgstr "Disponible"
+msgid "Runners|Available shared runners: %{count}"
+msgstr "Exécuteurs partagés disponibles : %{count}"
+
msgid "Runners|Available to all projects"
msgstr "Disponible pour tous les projets"
@@ -35021,13 +35480,13 @@ msgid "Runners|Can run untagged jobs"
msgstr "Possibilité d'exécuter les tâches non étiquetées"
msgid "Runners|Capacity of 1 enables warm HA through Auto Scaling group re-spawn. Capacity of 2 enables hot HA because the service is available even when a node is lost. Capacity of 3 or more enables hot HA and manual scaling of runner fleet."
-msgstr ""
+msgstr "La capacité de 1 active la haute disponibilité avec reprise semi-automatique via le redémarrage du groupe Auto Scaling. La capacité de 2 active la haute disponibilité avec reprise automatique car le service reste disponible même quand un nœud est perdu. La capacité de 3 ou plus active la haute disponibilité avec reprise automatique et mise à l'échelle manuelle de la flotte d'exécuteurs."
msgid "Runners|Checkbox"
msgstr "Case à cocher de sélection"
msgid "Runners|Choose your preferred GitLab Runner"
-msgstr ""
+msgstr "Choisissez votre exécuteur GitLab préféré"
msgid "Runners|Clear selection"
msgstr "Désélectionner"
@@ -35094,6 +35553,9 @@ msgstr "Entrez le nombre de secondes. Ce délai d'expiration a la priorité sur
msgid "Runners|Executor"
msgstr "Exécuteur"
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr "Les exécuteurs existants ne sont pas affectés. Pour permettre l'enregistrement d'un exécuteur pour tous les groupes, activez ce réglage dans l'Espace d'Administration, dans Paramètres &gt Intégration et livraison continues."
+
msgid "Runners|Filter projects"
msgstr "Filtrer les projets"
@@ -35112,6 +35574,9 @@ msgstr "Comment mettons-nous à jour GitLab Runner ?"
msgid "Runners|IP Address"
msgstr "Adresse IP"
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr "Installer un exécuteur"
@@ -35146,25 +35611,28 @@ msgid "Runners|Never contacted"
msgstr "Jamais contacté"
msgid "Runners|Never contacted:"
-msgstr ""
+msgstr "Jamais contacté :"
msgid "Runners|Never expires"
msgstr "N'expire jamais"
-msgid "Runners|New group runners view"
-msgstr "Nouvelle vue des exécuteurs de groupe"
+msgid "Runners|New group runners can be registered"
+msgstr ""
msgid "Runners|New registration token generated!"
msgstr "Nouveau jeton d’inscription généré !"
+msgid "Runners|No description"
+msgstr "Aucune description"
+
msgid "Runners|No results found"
msgstr "Aucun résultat trouvé"
msgid "Runners|No spot. Default choice for Windows Shell executor."
-msgstr ""
+msgstr "Non Spot. Choix par défaut pour l'exécutant Shell pour Windows."
msgid "Runners|No spot. This is the default choice for Linux Docker executor."
-msgstr ""
+msgstr "Non Spot. Il s'agit du choix par défaut pour l'exécutant Docker pour Linux."
msgid "Runners|Not accepting jobs"
msgstr "Tâches non acceptées"
@@ -35249,6 +35717,9 @@ msgstr "Exécuteur #%{runner_id}"
msgid "Runners|Runner %{name} was deleted"
msgstr "L'exécuteur %{name} a été supprimé"
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr "Exécuteur assigné au projet."
@@ -35256,10 +35727,10 @@ msgid "Runners|Runner authentication token expiration"
msgstr "Expiration du jeton d'authentification de l'exécuteur"
msgid "Runners|Runner authentication tokens will expire based on a set interval. They will automatically rotate once expired."
-msgstr ""
+msgstr "Les jetons d'authentification des exécuteurs expireront selon un intervalle défini. Ils seront renouvelés automatiquement une fois expirés."
msgid "Runners|Runner has contacted GitLab within the last %{elapsedTime}"
-msgstr ""
+msgstr "L'exécuteur a contacté GitLab au cours des dernières %{elapsedTime}"
msgid "Runners|Runner has never contacted GitLab (when you register a runner, use %{codeStart}gitlab-runner run%{codeEnd} to bring it online)"
msgstr "L'exécuteur n'a jamais contacté GitLab (lorsque vous enregistrez un exécuteur, utilisez %{codeStart}gitlab-runner run%{codeEnd} pour le mettre en ligne)"
@@ -35268,10 +35739,10 @@ msgid "Runners|Runner has never contacted this instance"
msgstr "L'exécuteur n'a jamais contacté cette instance"
msgid "Runners|Runner has not contacted GitLab in more than %{elapsedTime}"
-msgstr ""
+msgstr "L'exécuteur n'a pas contacté GitLab depuis plus de %{elapsedTime}"
msgid "Runners|Runner is locked and available for currently assigned projects only. Only administrators can change the assigned projects."
-msgstr ""
+msgstr "L'exécuteur est verrouillé et n'est disponible que pour les projets qui lui sont actuellement assignés. Seuls les administrateurs peuvent modifier les projets assignés."
msgid "Runners|Runner is offline; last contact was %{runner_contact} ago"
msgstr "L'exécuteur est hors-ligne ; le dernier contact était il y a %{runner_contact}"
@@ -35286,13 +35757,13 @@ msgid "Runners|Runner is online; last contact was %{timeAgo}"
msgstr "L'exécuteur est en ligne ; le dernier contact était %{timeAgo}"
msgid "Runners|Runner is stale; it has never contacted this instance"
-msgstr ""
+msgstr "L'exécuteur est périmé ; il n'a jamais contacté cette instance"
msgid "Runners|Runner is stale; last contact was %{runner_contact} ago"
-msgstr ""
+msgstr "L'exécuteur est périmé ; le dernier contact a été établi il y a %{runner_contact}"
msgid "Runners|Runner is stale; last contact was %{timeAgo}"
-msgstr ""
+msgstr "L'exécuteur est périmé ; le dernier contact a été établi %{timeAgo}"
msgid "Runners|Runner registration"
msgstr "Enregistrement d'exécuteur"
@@ -35315,6 +35786,9 @@ msgstr "Les exécuteurs sont les agents qui exécutent vos tâches CI/CD. Suivez
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr "Les exécuteurs sont les agents qui exécutent vos tâches CI/CD. Pour enregistrer de nouveaux exécuteurs, veuillez contacter votre administrateur."
+msgid "Runners|Running"
+msgstr "En cours d'exécution"
+
msgid "Runners|Runs untagged jobs"
msgstr "Exécute des tâches non marquées"
@@ -35325,7 +35799,7 @@ msgid "Runners|Select projects to assign to this runner"
msgstr "Sélectionnez les projets à attribuer à cet exécuteur"
msgid "Runners|Select your preferred option here. In the next step, you can choose the capacity for your runner in the AWS CloudFormation console."
-msgstr ""
+msgstr "Sélectionnez ici votre option préférée. Lors de la prochaine étape, vous pourrez choisir la capacité pour votre exécuteur dans la console AWS CloudFormation."
msgid "Runners|Show only inherited"
msgstr "N'afficher que ceux hérités"
@@ -35346,10 +35820,10 @@ msgid "Runners|Something went wrong while fetching the tags suggestions"
msgstr "Une erreur s'est produite lors de la récupération des suggestions d'étiquettes"
msgid "Runners|Stale"
-msgstr ""
+msgstr "Périmé(s)"
msgid "Runners|Stale:"
-msgstr ""
+msgstr "Périmé :"
msgid "Runners|Status"
msgstr "État"
@@ -35363,12 +35837,6 @@ msgstr "Étiquettes"
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr "Les étiquettes contrôlent quels sont les types de tâches qu'un exécuteur peut gérer. En étiquetant un exécuteur, vous avez la garantie que les exécuteurs partagés ne gérerons que les tâches qu'ils sont capables d'exécuter."
-msgid "Runners|Take me there!"
-msgstr "Emmenez-moi là-bas !"
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr "La nouvelle vue vous donne plus d'espace et une meilleure visibilité sur votre flotte d'exécuteurs."
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr "Le projet, le groupe ou l'instance où l'exécuteur a été enregistré. Les exécuteurs d'instance sont toujours détenus par l'Administrateur."
@@ -35432,7 +35900,7 @@ msgid "Runners|Use the runner for jobs without tags, in addition to tagged jobs.
msgstr "Utiliser l'exécuteur pour les tâches sans étiquette, en plus des tâches étiquetées."
msgid "Runners|Use the runner for the currently assigned projects only. Only administrators can change the assigned projects."
-msgstr ""
+msgstr "Utiliser l'exécuteur uniquement sur les projets qui lui sont actuellement assignés. Seuls les administrateurs peuvent modifier les projets assignés."
msgid "Runners|Use the runner on pipelines for protected branches only."
msgstr "Utiliser l'exécuteur sur les pipelines pour les branches protégées uniquement."
@@ -35453,7 +35921,7 @@ msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %
msgstr "Shell Windows 2019 avec mise à l'échelle manuelle et planification facultative. Spot %{percentage}."
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. Non-spot."
-msgstr ""
+msgstr "Shell Windows 2019 avec mise à l'échelle manuelle et planification facultative. Non Spot."
msgid "Runners|Yes, start deleting stale runners"
msgstr "Oui, commencer à supprimer les exécuteurs périmés"
@@ -35482,6 +35950,9 @@ msgstr "spécifique"
msgid "Runner|Owner"
msgstr "Propriétaire"
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr "En cours d’exécution"
@@ -35501,7 +35972,7 @@ msgid "SAML discovery tokens"
msgstr "Jetons de découverte SAML"
msgid "SAML for %{group_name}"
-msgstr ""
+msgstr "SAML pour %{group_name}"
msgid "SAML single sign-on"
msgstr "Authentification unique SAML"
@@ -35509,14 +35980,17 @@ msgstr "Authentification unique SAML"
msgid "SAML single sign-on for %{group_name}"
msgstr "Authentification unique SAML pour %{group_name}"
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr "Connectez-vous à GitLab pour connecter le compte de votre organisation"
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr "Le groupe %{strongOpen}%{group_path}%{strongClose} vous autorise à vous connecter en utilisant l'authentification unique."
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
-msgstr "Pour accéder à %{strongOpen}%{group_name}%{strongClose}, vous devez vous connecter en utilisant l'authentification unique via une page de connexion externe."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
+msgstr "Pour accéder à %{groupName}, vous devez vous connecter en utilisant l'authentification unique via une page de connexion externe."
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
msgstr "Pour autoriser %{strongOpen}%{group_name}%{strongClose} à gérer votre compte GitLab %{strongOpen}%{username}%{strongClose} (%{email}) après vous être connecté avec succès en utilisant l'authentification unique, sélectionnez %{strongOpen}Autoriser%{strongClose}."
@@ -35537,7 +36011,7 @@ msgid "SSH Key"
msgstr "Clé SSH"
msgid "SSH Keys"
-msgstr "Clefs SSH"
+msgstr "Clés SSH"
msgid "SSH Keys Help"
msgstr "Aide sur les clés SSH"
@@ -35549,7 +36023,7 @@ msgid "SSH host key fingerprints"
msgstr "Empreintes de la clé SSH de l'hôte"
msgid "SSH host keys"
-msgstr "Clefs SSH de l’hôte"
+msgstr "Clés SSH de l’hôte"
msgid "SSH host keys are not available on this system. Please use %{ssh_keyscan} command or contact your GitLab administrator for more information."
msgstr "Les clés d'hôtes SSH ne sont pas disponibles sur ce système. Veuillez utiliser la commande %{ssh_keyscan} ou contacter votre administrateur GitLab pour plus d'informations."
@@ -35557,6 +36031,9 @@ msgstr "Les clés d'hôtes SSH ne sont pas disponibles sur ce système. Veuillez
msgid "SSH key"
msgstr "Clé SSH"
+msgid "SSH key fingerprint:"
+msgstr "Empreinte de la clé SSH :"
+
msgid "SSH keys"
msgstr "Clés SSH"
@@ -35570,7 +36047,16 @@ msgid "SSH keys with the following fingerprints have expired and can no longer b
msgstr "Les clés SSH avec les empreintes suivantes ont expiré et ne peuvent plus être utilisées :"
msgid "SSH public key"
-msgstr "Clef SSH publique"
+msgstr "Clé SSH publique"
+
+msgid "SSHKey|Authentication"
+msgstr "Authentification"
+
+msgid "SSHKey|Authentication & Signing"
+msgstr "Authentification et Signature"
+
+msgid "SSHKey|Signing"
+msgstr "Signature"
msgid "SSL Verification:"
msgstr "Vérification SSL :"
@@ -35591,7 +36077,7 @@ msgid "Save"
msgstr "Enregistrer"
msgid "Save %{name} size limits"
-msgstr ""
+msgstr "Enregistrer les limites de taille pour %{name}"
msgid "Save Changes"
msgstr "Enregistrer les modifications"
@@ -35636,6 +36122,12 @@ msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan
msgstr "%{thenLabelStart}Then%{thenLabelEnd} Nécessite une analyse %{scan} pour s'exécuter"
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr "%{thenLabelStart}Alors%{thenLabelEnd} Nécessite l'exécution d'une analyse %{scan} avec le profil de site %{siteProfile} et le profil de scanner %{scannerProfile}"
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
msgstr ""
msgid "ScanExecutionPolicy|A pipeline is run"
@@ -35665,9 +36157,15 @@ msgstr "Sélectionner un profil de scanner"
msgid "ScanExecutionPolicy|Select site profile"
msgstr "Sélectionner un profil de site"
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr "Profil de site"
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr "agent"
@@ -35678,7 +36176,10 @@ msgid "ScanExecutionPolicy|in namespaces"
msgstr "dans les espaces de noms"
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
-msgstr ""
+msgstr "%{ifLabelStart}si%{ifLabelEnd} %{scanners} trouve(nt) plus de %{vulnerabilitiesAllowed} vulnérabilités %{severities} %{vulnerabilityStates} dans une demande de fusion ouverte ciblant %{branches}"
+
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr "%{ifLabelStart}si%{ifLabelEnd} %{selector}"
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
@@ -35686,12 +36187,18 @@ msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr "ajouter un approbateur"
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr "analyseurs"
msgid "ScanResultPolicy|severity levels"
msgstr "niveaux de gravité"
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr "états de vulnérabilité"
@@ -35753,22 +36260,22 @@ msgid "Scopes: %{scope_list}"
msgstr "Portées : %{scope_list}"
msgid "Scroll down"
-msgstr ""
+msgstr "Faire défiler vers le bas"
msgid "Scroll left"
-msgstr ""
+msgstr "Faire défiler vers la gauche"
msgid "Scroll right"
-msgstr ""
+msgstr "Faire défiler vers la droite"
msgid "Scroll to bottom"
-msgstr "Faire défiler vers le bas"
+msgstr "Faire défiler jusqu'en bas"
msgid "Scroll to top"
-msgstr "Faire défiler vers le haut"
+msgstr "Faire défiler jusqu'en haut"
msgid "Scroll up"
-msgstr ""
+msgstr "Faire défiler vers le haut"
msgid "Search"
msgstr "Rechercher"
@@ -35776,6 +36283,9 @@ msgstr "Rechercher"
msgid "Search GitLab"
msgstr "Rechercher dans GitLab"
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr "Rechercher un groupe"
@@ -35804,7 +36314,7 @@ msgid "Search by Git revision"
msgstr "Rechercher par révision Git"
msgid "Search by author"
-msgstr "Recherche par auteur·e"
+msgstr "Recherche par auteur"
msgid "Search by commit title or SHA"
msgstr "Rechercher par titre de commit ou par SHA"
@@ -35885,7 +36395,7 @@ msgid "Search refs"
msgstr ""
msgid "Search requirements"
-msgstr ""
+msgstr "Rechercher des exigences"
msgid "Search settings"
msgstr "Paramètres de recherche"
@@ -35924,13 +36434,13 @@ msgid "SearchAutocomplete|in all GitLab"
msgstr "Dans tout GitLab"
msgid "SearchAutocomplete|in group %{groupName}"
-msgstr ""
+msgstr "dans le groupe %{groupName}"
msgid "SearchAutocomplete|in project %{projectName}"
msgstr "dans le projet %{projectName}"
msgid "SearchCodeResults|of %{link_to_project}"
-msgstr ""
+msgstr "de %{link_to_project}"
msgid "SearchResults|Showing %{count} %{scope} for %{term_element}"
msgstr "Affichage de %{count} %{scope} pour %{term_element}"
@@ -35956,8 +36466,8 @@ msgstr[1] "commentaires"
msgid "SearchResults|commit"
msgid_plural "SearchResults|commits"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "commit"
+msgstr[1] "commits"
msgid "SearchResults|epic"
msgid_plural "SearchResults|epics"
@@ -36006,13 +36516,13 @@ msgid "SearchToken|Reviewer"
msgstr "Relecteur"
msgid "Searching by both author and message is currently not supported."
-msgstr "La recherche simultanée par auteur·e et par message n'est pas prise en charge actuellement."
+msgstr "La recherche simultanée par auteur et par message n'est pas prise en charge actuellement."
msgid "Seats"
msgstr "Sièges"
msgid "Seats owed"
-msgstr ""
+msgstr "Sièges dus"
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr "Données d'utilisation des sièges depuis le %{last_enqueue_time} (Actualisées chaque jour)"
@@ -36138,7 +36648,7 @@ msgid "SecurityConfiguration|Copy code and open .gitlab-ci.yml file"
msgstr "Copier le code et ouvrir le fichier .gitlab-ci.yml"
msgid "SecurityConfiguration|Copy code only"
-msgstr ""
+msgstr "Copier uniquement le code"
msgid "SecurityConfiguration|Could not retrieve configuration data. Please refresh the page, or try again later."
msgstr "Impossible de récupérer les données de configuration. Veuillez actualiser la page ou réessayer plus tard."
@@ -36234,13 +36744,13 @@ msgid "SecurityOrchestration| or "
msgstr " ou "
msgid "SecurityOrchestration|%{agent} for %{namespaces}"
-msgstr ""
+msgstr "%{agent} pour %{namespaces}"
msgid "SecurityOrchestration|%{branches} and %{lastBranch} branches"
-msgstr ""
+msgstr "branches %{branches} et %{lastBranch}"
msgid "SecurityOrchestration|%{branches} branch"
-msgstr ""
+msgstr "branche %{branches}"
msgid "SecurityOrchestration|%{scanners}"
msgstr "%{scanners}"
@@ -36293,6 +36803,9 @@ msgstr "Êtes-vous sûr de vouloir supprimer cette politique ? Cette action ne p
msgid "SecurityOrchestration|Choose a project"
msgstr "Choisir un projet"
+msgid "SecurityOrchestration|Choose approver type"
+msgstr "Choisir le type d'approbateur"
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr "Créez des règles de vulnérabilité plus robustes et appliquez-les à tous vos projets."
@@ -36350,9 +36863,18 @@ msgstr "Échec du chargement des images."
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr "Échec du chargement des scanneurs de vulnérabilités."
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr "Pour les groupes importants, il peut y avoir un délai significatif dans l'application des modifications de la stratégie sur les demandes de fusion préexistantes. Les modifications de stratégies s'appliquent en général presque immédiatement pour les demandes de fusion nouvellement créées."
+
+msgid "SecurityOrchestration|Groups"
+msgstr "Groupes"
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr "Si un scanner quelconque trouve une vulnérabilité critique récemment détectée dans une demande de fusion ouverte ciblant la branche maître, alors exiger deux approbations de tout membre de l'Appli sécurité."
+msgid "SecurityOrchestration|Individual users"
+msgstr "Utilisateurs individuels"
+
msgid "SecurityOrchestration|Inherited"
msgstr "Hérité"
@@ -36360,7 +36882,7 @@ msgid "SecurityOrchestration|Inherited from %{namespace}"
msgstr "Héritée de %{namespace}"
msgid "SecurityOrchestration|Invalid policy"
-msgstr ""
+msgstr "Stratégie non valide"
msgid "SecurityOrchestration|Invalid policy type"
msgstr "Type de politique non valide"
@@ -36368,6 +36890,9 @@ msgstr "Type de politique non valide"
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr "Dernière analyse exécutée avec %{agent}"
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr "Nouvelle politique"
@@ -36404,6 +36929,9 @@ msgstr "La politique ne peut pas être activée pour les branches inexistantes (
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr "La stratégie ne peut pas être activée sans information de branche"
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr "Les modifications de stratégie peuvent prendre un certain temps pour être appliquées."
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr "Analyse à effectuer sur chaque pipeline des %{branches}"
msgid "SecurityOrchestration|Security Approvals"
msgstr "Approbations de sécurité"
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr "Le projet de politique de sécurité a été lié avec succès"
@@ -36485,12 +37016,21 @@ msgstr "Le projet de politique de sécurité a été dissocié avec succès"
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr "Sélectionnez un projet dans lequel stocker vos politiques de sécurité. %{linkStart}Plus d'informations.%{linkEnd}"
+msgid "SecurityOrchestration|Select groups"
+msgstr "Sélectionner des groupes"
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr "Sélectionnez le type d'analyse"
+
msgid "SecurityOrchestration|Select security project"
msgstr "Sélectionner un projet de sécurité"
+msgid "SecurityOrchestration|Select users"
+msgstr "Sélectionnez des utilisateurs"
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr "Une erreur s’est produite, impossible de récupérer les stratégies"
@@ -36558,7 +37098,10 @@ msgid "SecurityOrchestration|Update scan policies"
msgstr "Mettre à jour les politiques d'analyse"
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
-msgstr ""
+msgstr "Utilisez une stratégie d'exécution d'analyse pour créer des règles qui forcent les analyses de sécurité sur des branches particulières à un moment donné. Les types pris en charge sont SAST, DAST, Détection de Secret, analyse de Conteneur et analyse de Dépendance."
+
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr "Utilisez une stratégie de résultats d'analyse pour créer des règles qui vérifient les failles de sécurité et la conformité des licences avant de fusionner une demande de fusion."
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr "Utilisez une stratégie de résultats d'analyse pour créer des règles qui garantissent que les problèmes de sécurité sont vérifiés avant de fusionner une demande de fusion."
@@ -36576,7 +37119,7 @@ msgid "SecurityOrchestration|all branches"
msgstr "toutes les branches"
msgid "SecurityOrchestration|all namespaces"
-msgstr ""
+msgstr "tous les espaces de nom"
msgid "SecurityOrchestration|an"
msgstr "un(e)"
@@ -36587,6 +37130,9 @@ msgstr "branche"
msgid "SecurityOrchestration|branches"
msgstr "branches"
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr "scanner trouve"
@@ -36597,10 +37143,10 @@ msgid "SecurityOrchestration|the %{branches}"
msgstr "les %{branches}"
msgid "SecurityOrchestration|the %{namespaces} and %{lastNamespace} namespaces"
-msgstr ""
+msgstr "les espaces de noms %{namespaces} et %{lastNamespace}"
msgid "SecurityOrchestration|the %{namespaces} namespace"
-msgstr ""
+msgstr "l'espace de noms %{namespaces}"
msgid "SecurityOrchestration|vulnerabilities"
msgstr "vulnérabilités"
@@ -36609,7 +37155,7 @@ msgid "SecurityOrchestration|vulnerability"
msgstr "vulnérabilité"
msgid "SecurityPolicies|Invalid or empty policy"
-msgstr ""
+msgstr "Stratégie vide ou non valide"
msgid "SecurityReports|%{count} Selected"
msgstr "%{count} sélectionnés"
@@ -36623,6 +37169,9 @@ msgstr "%{firstProject} et %{secondProject}"
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr "%{firstProject}, %{secondProject}, et %{rest}"
+msgid "SecurityReports|Activity"
+msgstr "Activité"
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr "Ajouter ou supprimer des projets à surveiller dans la zone de sécurité. Les résultats des projets inclus dans cette liste seront présentés sur le tableau de bord de sécurité et dans le rapport de vulnérabilité."
@@ -36632,8 +37181,23 @@ msgstr "Ajouter des projets"
msgid "SecurityReports|All activity"
msgstr "Toute l'activité"
+msgid "SecurityReports|All clusters"
+msgstr "Toutes les grappes de serveurs"
+
+msgid "SecurityReports|All images"
+msgstr "Toutes les images"
+
+msgid "SecurityReports|All projects"
+msgstr "Tous les projets"
+
msgid "SecurityReports|All severities"
-msgstr ""
+msgstr "Tous les niveaux de gravité"
+
+msgid "SecurityReports|All statuses"
+msgstr "Tous les états"
+
+msgid "SecurityReports|All tools"
+msgstr "Tous les outils"
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr "Bien qu'il soit rare de ne pas avoir de vulnérabilités, cela peut arriver. Vérifiez vos paramètres afin de vous assurer que vous avez configuré votre tableau de bord correctement."
@@ -36645,16 +37209,16 @@ msgid "SecurityReports|Change status"
msgstr "Modifier l'état"
msgid "SecurityReports|Check the messages generated while parsing the following security reports, as they may prevent the results from being ingested by GitLab. Ensure the security report conforms to a supported %{helpPageLinkStart}JSON schema%{helpPageLinkEnd}."
-msgstr ""
+msgstr "Vérifiez les messages générés lors de l'analyse des rapports de sécurité suivants, car ils peuvent empêcher les résultats d'être ingérés par GitLab. Assurez-vous que le rapport de sécurité est conforme à un %{helpPageLinkStart}schéma JSON%{helpPageLinkEnd} pris en charge."
msgid "SecurityReports|Cluster"
msgstr "Grappe de serveurs"
msgid "SecurityReports|Comment added to '%{vulnerabilityName}'"
-msgstr ""
+msgstr "Commentaire ajouté à « %{vulnerabilityName} »"
msgid "SecurityReports|Comment deleted on '%{vulnerabilityName}'"
-msgstr ""
+msgstr "Commentaire supprimé sur « %{vulnerabilityName} »"
msgid "SecurityReports|Comment edited on '%{vulnerabilityName}'"
msgstr "Commentaire édité sur « %{vulnerabilityName} »"
@@ -36687,7 +37251,7 @@ msgid "SecurityReports|Does not have issue"
msgstr ""
msgid "SecurityReports|Download %{artifactName}"
-msgstr ""
+msgstr "Télécharger %{artifactName}"
msgid "SecurityReports|Download results"
msgstr "Télécharger les résultats"
@@ -36726,7 +37290,7 @@ msgid "SecurityReports|Image"
msgstr "Image"
msgid "SecurityReports|Issue"
-msgstr ""
+msgstr "Ticket"
msgid "SecurityReports|Issue Created"
msgstr "Ticket créé"
@@ -36765,7 +37329,7 @@ msgid "SecurityReports|More info"
msgstr "Plus d’informations"
msgid "SecurityReports|New vulnerabilities are vulnerabilities that the security scan detects in the merge request that are different to existing vulnerabilities in the default branch."
-msgstr ""
+msgstr "Les nouvelles vulnérabilités sont les vulnérabilités que l’analyse de sécurité a détecté dans la demande de fusion et qui sont différentes de celles existantes dans la branche par défaut."
msgid "SecurityReports|No activity"
msgstr "Aucune activité"
@@ -36779,6 +37343,9 @@ msgstr "Aucune vulnérabilité trouvée"
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr "Aucune vulnérabilité trouvée pour ce pipeline"
+msgid "SecurityReports|Not available"
+msgstr "Non disponible"
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr "Oups, quelque chose semble ne pas s'être passé correctement."
@@ -36855,7 +37422,7 @@ msgid "SecurityReports|Still detected"
msgstr "Toujours détecté"
msgid "SecurityReports|Submit vulnerability"
-msgstr ""
+msgstr "Soumettre une vulnérabilité"
msgid "SecurityReports|Take survey"
msgstr "Participer à l'enquête"
@@ -36948,10 +37515,10 @@ msgid "SecurityTraining|Enable security training to learn how to fix vulnerabili
msgstr "Activer la formation sur la sécurité pour apprendre comment corriger les vulnérabilités. Voir la formation de sécurité depuis des centres de formation sélectionnés en fonction de la vulnérabilité détectée."
msgid "SecurityTraining|Primary Training"
-msgstr "Formation primaire"
+msgstr "Formation de base"
msgid "SecurityTraining|Resolve with security training"
-msgstr ""
+msgstr "Résoudre avec une formation sur la sécurité"
msgid "SecurityTraining|Training from this partner takes precedence when more than one training partner is enabled."
msgstr "La formation provenant de ce partenaire est prioritaire lorsqu'il y a plus d'un partenaire de formation activé."
@@ -36984,7 +37551,7 @@ msgid "Select Archive Format"
msgstr "Sélectionnez le format de l’archive"
msgid "Select Git revision"
-msgstr ""
+msgstr "Sélectionnez une révision Git"
msgid "Select Profile"
msgstr ""
@@ -36999,11 +37566,14 @@ msgid "Select a color"
msgstr "Sélectionnez une couleur"
msgid "Select a compliance framework to apply to this project. %{linkStart}How are these added?%{linkEnd}"
-msgstr ""
+msgstr "Sélectionnez un cadre de conformité à appliquer à ce projet. %{linkStart}Comment sont-ils ajoutés ?%{linkEnd}"
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr "Sélectionnez un fichier dans la barre latérale de gauche pour commencer l'édition. Ensuite, vous pourrez valider vos modifications."
+msgid "Select a group"
+msgstr "Sélectionner un groupe"
+
msgid "Select a label"
msgstr "Sélectionnez une étiquette"
@@ -37014,7 +37584,7 @@ msgid "Select a new namespace"
msgstr "Sélectionnez un nouvel espace de noms"
msgid "Select a project"
-msgstr ""
+msgstr "Sélectionnez un projet"
msgid "Select a reason"
msgstr "Choisissez un motif"
@@ -37047,7 +37617,7 @@ msgid "Select assignee(s)"
msgstr "Sélectionner une personne assignée"
msgid "Select branch"
-msgstr ""
+msgstr "Sélectionnez une branche"
msgid "Select branch or create wildcard"
msgstr "Sélectionnez une branche ou créez un joker"
@@ -37065,13 +37635,13 @@ msgid "Select epic"
msgstr "Sélectionner une épopée"
msgid "Select group"
-msgstr ""
+msgstr "Sélectionner un groupe"
msgid "Select group or project"
-msgstr ""
+msgstr "Sélectionner un groupe ou un projet"
msgid "Select health status"
-msgstr ""
+msgstr "Sélectionnez l'état de santé"
msgid "Select iteration"
msgstr "Sélectionner une itération"
@@ -37089,7 +37659,7 @@ msgid "Select milestone"
msgstr "Sélectionner un jalon"
msgid "Select private project"
-msgstr ""
+msgstr "Sélectionner un projet privé"
msgid "Select project"
msgstr "Sélectionner un projet"
@@ -37109,6 +37679,9 @@ msgstr "Sélectionner un rapport"
msgid "Select reviewer(s)"
msgstr "Sélectionnez un ou plusieurs relecteurs"
+msgid "Select severity (optional)"
+msgstr "Sélectionnez la gravité (facultatif)"
+
msgid "Select source"
msgstr "Sélectionner la source"
@@ -37131,19 +37704,19 @@ msgid "Select subgroup"
msgstr "Sélectionner un sous-groupe"
msgid "Select subscription"
-msgstr ""
+msgstr "Sélectionner l'abonnement"
msgid "Select target branch"
msgstr "Sélectionner une branche cible"
msgid "Select target branch or tag"
-msgstr ""
+msgstr "Sélectionnez une branche ou une étiquette cible"
msgid "Select target project"
msgstr "Sélectionnez le projet cible"
msgid "Select timezone"
-msgstr ""
+msgstr "Sélectionnez le fuseau horaire"
msgid "Select type"
msgstr "Sélectionner le type"
@@ -37161,7 +37734,7 @@ msgid "Selected for some items."
msgstr ""
msgid "Selected levels cannot be used by non-admin users for groups, projects or snippets. If the public level is restricted, user profiles are only visible to logged in users."
-msgstr ""
+msgstr "Les niveaux sélectionnés ne peuvent pas être utilisés par des utilisateurs non administrateurs pour des groupes, des projets ou des extraits de code. Si le niveau public est limité, les profils des utilisateurs ne sont visibles que par les utilisateurs connectés."
msgid "Selected tag is already in use. Choose another option."
msgstr "L'étiquette sélectionnée est déjà utilisée. Faites un autre choix."
@@ -37257,7 +37830,7 @@ msgid "Sentry API URL"
msgstr "URL de l'API Sentry"
msgid "Sentry event"
-msgstr ""
+msgstr "Événement Sentry"
msgid "Sep"
msgstr "sept."
@@ -37365,7 +37938,7 @@ msgid "Set any rate limit to %{code_open}0%{code_close} to disable the limit."
msgstr "Définissez une limitation de fréquence sur %{code_open}0%{code_close} pour la désactiver."
msgid "Set due date"
-msgstr ""
+msgstr "Définir la date d'échéance"
msgid "Set health status"
msgstr "Définir l'état de santé"
@@ -37395,7 +37968,7 @@ msgid "Set per-user rate limits for imports and exports of projects and groups."
msgstr "Définir des limitations de fréquence d'importations et d'exportations de projets ou groupes par utilisateur."
msgid "Set projects and maximum size limits, session duration, user options, and check feature availability for namespace plan."
-msgstr ""
+msgstr "Définir les limites de projets et de taille maximale, la durée de session, les options des utilisateurs et vérifier la disponibilité des fonctionnalités pour le forfait de l'espace de noms."
msgid "Set rate limits for package registry API requests that supersede the general user and IP rate limits."
msgstr "Configurer des limitations de fréquence pour les requêtes à l'API du registre de paquets pour remplacer celles générales aux IP et utilisateurs."
@@ -37404,7 +37977,7 @@ msgid "Set rate limits for searches performed by web or API requests."
msgstr ""
msgid "Set severity"
-msgstr ""
+msgstr "Définir la gravité"
msgid "Set sign-in restrictions for all users."
msgstr "Définir des restrictions de connexion pour tous les utilisateurs."
@@ -37422,7 +37995,7 @@ msgid "Set the Draft status"
msgstr "Définir à l'état Brouillon"
msgid "Set the Ready status"
-msgstr ""
+msgstr "Définir à l'état Prête"
msgid "Set the default expiration time for job artifacts in all projects. Set to %{code_open}0%{code_close} to never expire artifacts by default. If no unit is written, it defaults to seconds. For example, these are all equivalent: %{code_open}3600%{code_close}, %{code_open}60 minutes%{code_close}, or %{code_open}one hour%{code_close}."
msgstr "Définir le délai d'expiration par défaut des artéfacts des tâches dans tous les projets. Mettre à %{code_open}0%{code_close} pour que les artéfacts n'expirent jamais par défaut. Si aucune unité n'est précisée, ce sera la seconde par défaut. Ces exemples sont tous équivalents : %{code_open}3600%{code_close}, %{code_open}60 minutes%{code_close} et %{code_open}une heure%{code_close}."
@@ -37452,10 +38025,10 @@ msgid "Set this number to 0 to disable the limit."
msgstr "Définissez ce nombre à 0 pour désactiver la limite."
msgid "Set time estimate"
-msgstr ""
+msgstr "Définir l'estimation du temps"
msgid "Set time estimate to %{time_estimate}."
-msgstr ""
+msgstr "Définir l'estimation du temps à %{time_estimate}."
msgid "Set to 0 for no size limit."
msgstr "Définir à 0 pour qu'il n'y ait pas de limite."
@@ -37464,7 +38037,7 @@ msgid "Set up CI/CD"
msgstr "Configuration CI/CD"
msgid "Set up Jira Integration"
-msgstr ""
+msgstr "Configurer l'intégration de Jira"
msgid "Set up a %{type} runner for a project"
msgstr "Mettre en place un exécuteur %{type} pour un projet"
@@ -37530,16 +38103,16 @@ msgid "SetStatusModal|Your status resets on %{date}."
msgstr "Votre état est réinitialisé le %{date}."
msgid "Sets %{epic_ref} as parent epic."
-msgstr ""
+msgstr "Définit %{epic_ref} comme épopée parente."
msgid "Sets health status to %{health_status}."
msgstr "Définit l'état de santé sur %{health_status}."
msgid "Sets target branch to %{branch_name}."
-msgstr ""
+msgstr "Définit %{branch_name} en tant que branche cible."
msgid "Sets the due date to %{due_date}."
-msgstr ""
+msgstr "Définit la date d'échéance au %{due_date}."
msgid "Sets the iteration to %{iteration_reference}."
msgstr "Définit l'itération à %{iteration_reference}."
@@ -37551,7 +38124,7 @@ msgid "Sets the severity"
msgstr "Définit la gravité"
msgid "Sets time estimate to %{time_estimate}."
-msgstr ""
+msgstr "Définit l'estimation du temps à %{time_estimate}."
msgid "Sets weight to %{weight}."
msgstr "Définit le poids à %{weight}."
@@ -37597,6 +38170,9 @@ msgstr "Partagez l'%{strong_open}URL d'authentification unique GitLab%{strong_cl
msgid "Shared Runners"
msgstr "Exécuteurs partagés"
+msgid "Shared Runners:"
+msgstr "Exécuteurs partagés :"
+
msgid "Shared projects"
msgstr "Projets partagés"
@@ -37658,7 +38234,7 @@ msgid "Show Pipeline IID"
msgstr "Afficher l'IID de Pipeline"
msgid "Show all %{issuable_type}."
-msgstr ""
+msgstr "Afficher l'ensemble des %{issuable_type}."
msgid "Show all activity"
msgstr "Afficher toute l'activité"
@@ -37691,13 +38267,13 @@ msgid "Show command"
msgstr "Afficher la commande"
msgid "Show comments"
-msgstr ""
+msgstr "Afficher les commentaires"
msgid "Show comments on this file"
msgstr "Afficher les commentaires sur ce fichier"
msgid "Show comments only"
-msgstr ""
+msgstr "Afficher uniquement les commentaires"
msgid "Show complete raw log"
msgstr "Afficher le journal brut complet"
@@ -37723,6 +38299,9 @@ msgstr "Afficher la dernière version"
msgid "Show list"
msgstr "Afficher la liste"
+msgid "Show more"
+msgstr "Afficher plus"
+
msgid "Show one file at a time"
msgstr "Afficher un fichier à la fois"
@@ -37742,7 +38321,7 @@ msgid "Show the Open list"
msgstr "Afficher la liste Ouvert(e)s"
msgid "Show thread"
-msgstr ""
+msgstr "Afficher le fil"
msgid "Show whitespace changes"
msgstr "Afficher les modifications des espaces"
@@ -37805,7 +38384,7 @@ msgid "ShowcaseSecurity|Vulnerability management"
msgstr "Gestion des vulnérabilités"
msgid "Showing %{conflict}"
-msgstr ""
+msgstr "Affichage de %{conflict}"
msgid "Showing %{count} of %{total} projects"
msgstr "Affichage de %{count} projets sur %{total}"
@@ -37828,7 +38407,7 @@ msgid "Showing all issues"
msgstr "Affichage de tous les tickets"
msgid "Showing data for workflow items completed in this date range. Date range limited to %{maxDateRange} days."
-msgstr ""
+msgstr "Affichage des données des éléments terminés du flux de travail sur cette plage de dates. Celle-ci est limitée à %{maxDateRange} jours."
msgid "Showing last %{size} of log -"
msgstr ""
@@ -37849,13 +38428,13 @@ msgid "Sidebar|%{name}: %{value}"
msgstr "%{name} : %{value}"
msgid "Sidebar|Assign health status"
-msgstr ""
+msgstr "Assigner l'état de santé"
msgid "Sidebar|Health status"
msgstr "État de santé"
msgid "Sidebar|No status"
-msgstr ""
+msgstr "Pas d'état"
msgid "Sidebar|None"
msgstr "Aucun"
@@ -37881,17 +38460,14 @@ msgstr "Connectez-vous comme utilisateur avec l'adresse de courriel correspondan
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr "Se connecter à %{group_name}"
-
msgid "Sign in to GitLab"
msgstr "Se connecter à GitLab"
msgid "Sign in using smart card"
-msgstr ""
+msgstr "Connexion à l'aide d'une carte à puce"
msgid "Sign in via 2FA code"
-msgstr ""
+msgstr "Connexion via un code A2F"
msgid "Sign in with"
msgstr "Connexion avec"
@@ -37942,7 +38518,7 @@ msgid "Sign-in using %{provider} auth failed"
msgstr "La connexion via l'authentification de %{provider} a échoué"
msgid "Sign-out page URL"
-msgstr ""
+msgstr "URL de la page de déconnexion"
msgid "Sign-up restrictions"
msgstr "Restrictions d’inscription"
@@ -37960,7 +38536,7 @@ msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted
msgstr "En cliquant sur %{button_text}, je reconnais avoir lu et accepté les %{link_start}Conditions Générales d'Utilisation et Politique de Confidentialité%{link_end} de GitLab"
msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
-msgstr ""
+msgstr "En vous connectant, vous acceptez les %{link_start} Conditions d'Utilisation et reconnaissez avoir pris connaissance de la Politique de Confidentialité et de la Politique de gestion des cookies%{link_end}."
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr "Le prénom est trop long (le maximum est de %{max_length} caractères)."
@@ -37978,25 +38554,25 @@ msgid "SignUp|Username is too short (minimum is %{min_length} characters)."
msgstr "Le nom d'utilisateur est trop court (le minimum est de %{min_length} caractères)."
msgid "Signed in"
-msgstr ""
+msgstr "Connecté(e)"
msgid "Signed in to GitLab"
-msgstr ""
+msgstr "Connecté à GitLab"
msgid "Signed in to GitLab as %{user_link}"
-msgstr ""
+msgstr "Connecté à GitLab en tant que %{user_link}"
msgid "Signed in with %{authentication} authentication"
msgstr "Connexion avec l'authentification %{authentication}"
msgid "Signing in using %{label} has been disabled"
-msgstr ""
+msgstr "La connexion avec %{label} a été désactivée"
msgid "Signing in using your %{label} account without a pre-existing GitLab account is not allowed."
-msgstr ""
+msgstr "La connexion via l'utilisation de votre compte %{label} sans un compte GitLab préexistant n'est pas autorisée."
msgid "Similar issues"
-msgstr ""
+msgstr "Tickets similaires"
msgid "Simulate a pipeline created for the default branch"
msgstr "Simuler un pipeline créé pour la branche par défaut"
@@ -38020,7 +38596,7 @@ msgid "Size limit per repository (MB)"
msgstr "Limite de taille par dépôt (Mo)"
msgid "Skipped"
-msgstr ""
+msgstr "Ignoré"
msgid "Skipped deployment to"
msgstr "Omission du déploiement vers"
@@ -38037,14 +38613,14 @@ msgstr "L’intégration de Slack permet d’interagir avec GitLab via des comma
msgid "Slack logo"
msgstr "Logo Slack"
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
-msgstr "Êtes-vous sûr de vouloir supprimer ce projet de l'application Slack ?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
+msgstr "Êtes-vous sûr(e) de vouloir supprimer ce projet de l'appli GitLab pour Slack ?"
msgid "SlackIntegration|Client ID"
msgstr "ID client"
msgid "SlackIntegration|Client secret"
-msgstr ""
+msgstr "Secret client"
msgid "SlackIntegration|GitLab for Slack"
msgstr "GitLab pour Slack"
@@ -38052,14 +38628,17 @@ msgstr "GitLab pour Slack"
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr "GitLab pour Slack a été installé avec succès."
-msgid "SlackIntegration|Install Slack app"
-msgstr "Installer l'application Slack"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr "Installer l'appli GitLab pour Slack"
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
+msgstr "Les notifications ne fonctionnent que si vous êtes sur la dernière version de l'appli GitLab pour Slack"
msgid "SlackIntegration|Project alias"
msgstr "Alias du projet"
-msgid "SlackIntegration|Reinstall Slack app"
-msgstr "Réinstaller l'application Slack"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
+msgstr "Réinstaller l'appli GitLab pour Slack"
msgid "SlackIntegration|Remove project"
msgstr "Supprimer le projet"
@@ -38082,14 +38661,17 @@ msgstr "Nom de l'équipe"
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr "Cette intégration permet aux utilisateurs d'effectuer des opérations courantes sur ce projet en entrant des commandes barre oblique dans Slack."
+msgid "SlackIntegration|Update to the latest version"
+msgstr "Mise à jour vers la dernière version"
+
msgid "SlackIntegration|Verification token"
msgstr "Jeton de vérification"
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr "Vous pouvez à présent fermer cette fenêtre et rejoindre votre espace de travail Slack."
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
-msgstr ""
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgstr "Vous aurez peut-être besoin de réinstaller l'appli GitLab pour Slack lorsque nous %{linkStart}effectuerons des mises à jour ou modifierons les permissions%{linkEnd}."
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
msgstr "1. %{slash_command_link_start}Ajoutez une commande barre oblique%{slash_command_link_end} dans votre équipe Slack avec cette information :"
@@ -38104,7 +38686,7 @@ msgid "SlackService|After setup, get a list of available Slack slash commands by
msgstr "Après la configuration, obtenez une liste des commandes barre oblique de Slack disponibles en entrant"
msgid "SlackService|Fill in the word that works best for your team."
-msgstr ""
+msgstr "À remplir avec le mot qui convient le mieux à votre équipe."
msgid "SlackService|Perform common operations in this project by entering slash commands in Slack."
msgstr "Effectuez des opérations courantes sur ce projet en entrant des commandes barre oblique dans Slack."
@@ -38226,12 +38808,12 @@ msgstr "Des épopées enfants peuvent être cachées en raison des filtres appli
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr "Certains domaines usuels ne sont pas autorisés. %{learn_more_link}."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
+msgstr ""
+
msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr "Quelqu'un a modifié le fichier en même temps que vous. Veuillez vérifier %{link_start}le fichier %{icon}%{link_end} et assurez-vous que ses modifications ne seront pas supprimées accidentellement par les vôtres."
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
-msgstr ""
-
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -38269,16 +38851,19 @@ msgid "Something went wrong trying to load issue contacts."
msgstr "Une erreur s'est produite lors de la tentative de chargement des contacts du ticket."
msgid "Something went wrong when reordering designs. Please try again"
-msgstr "Une erreur s'est produite lors de la réorganisation des designs. Veuillez réessayer"
+msgstr "Une erreur s'est produite lors de la réorganisation des esquisses. Veuillez réessayer"
+
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr "Une erreur s'est produite lors de l'envoi du lien d'incident vers Slack."
msgid "Something went wrong while adding timeline event."
msgstr "Une erreur s'est produite lors de l'ajout d'un événement de chronologie."
msgid "Something went wrong while adding your award. Please try again."
-msgstr ""
+msgstr "Une erreur s'est produite lors de l'ajout de votre récompense. Veuillez réessayer."
msgid "Something went wrong while applying the batch of suggestions. Please try again."
-msgstr ""
+msgstr "Une erreur s'est produite lors de l'application du lot de suggestions. Veuillez réessayer."
msgid "Something went wrong while applying the suggestion. Please try again."
msgstr "Une erreur s'est produite lors de l'application de la suggestion. Veuillez réessayer."
@@ -38289,6 +38874,9 @@ msgstr "Une erreur s'est produite lors de l'archivage d'une exigence."
msgid "Something went wrong while closing the epic. Please try again later."
msgstr "Une erreur s’est produite lors de la fermeture de l'épopée. Veuillez réessayer plus tard."
+msgid "Something went wrong while closing the incident form."
+msgstr "Une erreur s'est produite lors de la fermeture du formulaire d'incident."
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr "Une erreur s'est produite lors de la fermeture de la demande de fusion. Veuillez réessayer plus tard."
@@ -38302,7 +38890,7 @@ msgid "Something went wrong while deleting the source branch. Please try again."
msgstr "Une erreur s'est produite lors de la suppression de la branche source. Veuillez réessayer."
msgid "Something went wrong while deleting your note. Please try again."
-msgstr ""
+msgstr "Une erreur s'est produite lors de la suppression de votre note. Veuillez réessayer."
msgid "Something went wrong while deploying this environment. Please try again."
msgstr "Une erreur s'est produite lors du déploiement de cet environnement. Veuillez réessayer."
@@ -38358,6 +38946,9 @@ msgstr "Une erreur s'est produite lors de la récupération de la liste des paqu
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr "Une erreur s'est produite lors de l'obtention du certificat Let's Encrypt."
+msgid "Something went wrong while opening the incident form."
+msgstr "Une erreur s'est produite lors de l'ouverture du formulaire d'incident."
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr "Une erreur s'est produite lors de la promotion du ticket en épopée. Veuillez réessayer."
@@ -38389,7 +38980,7 @@ msgid "Something went wrong while setting %{issuableType} notifications."
msgstr "Une erreur s'est produite lors de la configuration des notifications des %{issuableType}."
msgid "Something went wrong while setting %{issuableType} to-do item."
-msgstr "Une erreur s'est produite lors de la configuration de l'élément à-faire de %{issuableType}."
+msgstr "Une erreur s'est produite lors de la configuration du pense-bête de %{issuableType}."
msgid "Something went wrong while setting %{issuableType} weight."
msgstr "Une erreur s'est produite lors du réglage du poids de %{issuableType}."
@@ -38434,7 +39025,7 @@ msgid "Sorry, no projects matched your search"
msgstr "Désolé, aucun projet ne correspond à votre recherche"
msgid "Sorry, you have exceeded the maximum browsable page number. Please use the API to explore further."
-msgstr ""
+msgstr "Désolé, vous avez dépassé le nombre maximum de pages consultables. Veuillez utiliser l'API pour en explorer davantage."
msgid "Sorry, your filter produced no results"
msgstr "Désolé, aucun résultat ne correspond à vos critères de recherche"
@@ -38677,7 +39268,7 @@ msgid "Sourcegraph"
msgstr "Sourcegraph"
msgid "SourcegraphAdmin|Block on private and internal projects"
-msgstr ""
+msgstr "Bloquer les projets privés et internes"
msgid "SourcegraphAdmin|Configure the URL to a Sourcegraph instance which can read your GitLab projects."
msgstr "Configurer l’URL d’une instance de Sourcegraph qui peut lire vos projets GitLab."
@@ -38731,7 +39322,7 @@ msgid "Spam and Anti-bot Protection"
msgstr "Protection antiâ€pourriel et antiâ€robot"
msgid "Spam log successfully submitted as ham."
-msgstr ""
+msgstr "Le journal d'indésirables a été soumis avec succès comme acceptable."
msgid "Specific runners"
msgstr "Exécuteurs spécifiques"
@@ -38749,10 +39340,13 @@ msgid "Speed up your pipelines with Needs relationships"
msgstr "Accélérez vos pipelines avec des relations de Besoins"
msgid "Spent at"
+msgstr "Passé le"
+
+msgid "Spent at can't be a future date and time."
msgstr ""
msgid "Squash commit message"
-msgstr ""
+msgstr "Message du squash des commits"
msgid "Squash commits"
msgstr "Combiner (squash) les commits"
@@ -38779,7 +39373,7 @@ msgid "Star labels to start sorting by priority"
msgstr "Mettez des étoiles sur les étiquettes pour les classer par priorité"
msgid "Star toggle failed. Try again later."
-msgstr ""
+msgstr "L'ajout/suppression de l'étoile a échoué. Réessayez plus tard."
msgid "StarProject|Star"
msgstr "Mettre en favori"
@@ -38794,10 +39388,10 @@ msgid "Starred projects"
msgstr "Projets favoris"
msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
-msgstr ""
+msgstr "Consultez la page d'un projet et cliquez sur l'icône étoile. Vous pourrez ensuite trouver le projet sur cette page."
msgid "StarredProjectsEmptyState|You don't have starred projects yet."
-msgstr ""
+msgstr "Vous n'avez pas encore de projets étoilés."
msgid "Starrers"
msgstr ""
@@ -38830,22 +39424,22 @@ msgid "Start a review"
msgstr "Démarrer une revue de code"
msgid "Start by choosing a group to start exploring the merge requests in that group. You can then proceed to filter by projects, labels, milestones and authors."
-msgstr ""
+msgstr "Commencez par choisir un groupe pour débuter l'exploration des demandes de fusion qu'il contient. Vous pouvez ensuite procéder au filtrage par projets, étiquettes, jalons et auteurs."
msgid "Start cleanup"
-msgstr ""
+msgstr "Lancer le nettoyage"
msgid "Start date"
msgstr "Date de début"
msgid "Start free trial"
-msgstr ""
+msgstr "Commencer l'essai gratuit"
msgid "Start inputting changes and we will generate a YAML-file for you to add to your repository"
msgstr "Commencez à saisir des modifications et nous régénérerons un fichier YAML à votre attention pour l'ajouter à votre dépôt"
msgid "Start internal thread"
-msgstr ""
+msgstr "Démarrer un fil de conversation interne"
msgid "Start merge train"
msgstr ""
@@ -38856,11 +39450,14 @@ msgstr "Démarrer un train de fusion lorsque le pipeline réussit"
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr "Commencer une recherche"
msgid "Start thread"
-msgstr ""
+msgstr "Démarrer un fil de conversation"
msgid "Start your Free Ultimate Trial"
msgstr "Commencez votre Essai Ultimate Gratuit"
@@ -38919,6 +39516,9 @@ msgstr "Statistiques"
msgid "Status"
msgstr "État"
+msgid "Status (optional)"
+msgstr "État (facultatif)"
+
msgid "Status was retried."
msgstr ""
@@ -38938,7 +39538,7 @@ msgid "StatusCheck|API to check"
msgstr "API à vérifier"
msgid "StatusCheck|Add status check"
-msgstr ""
+msgstr "Ajouter une vérification d'état"
msgid "StatusCheck|An error occurred deleting the %{name} status check."
msgstr "Une erreur s'est produite lors de la suppression de la vérification d'état %{name}."
@@ -38977,7 +39577,7 @@ msgid "StatusCheck|Service name"
msgstr "Nom du service"
msgid "StatusCheck|Status checks"
-msgstr ""
+msgstr "Vérifications d'état"
msgid "StatusCheck|Status checks all passed"
msgstr "Les vérifications d'état sont toutes passées"
@@ -39082,7 +39682,7 @@ msgid "Storage:"
msgstr "Stockage :"
msgid "StorageSize|Unknown"
-msgstr ""
+msgstr "Inconnu"
msgid "Store your files, plan your work, collaborate on code, and more."
msgstr "Stocker vos fichiers, planifiez votre travail, collaborez sur du code et bien plus encore."
@@ -39112,10 +39712,10 @@ msgid "SubgroupCreationLevel|Roles allowed to create subgroups"
msgstr "Rôles autorisés à créer des sous-groupes"
msgid "SubgroupCreationlevel|Allowed to create subgroups"
-msgstr ""
+msgstr "Autorisés à créer des sous-groupes"
msgid "SubgroupCreationlevel|Maintainers"
-msgstr ""
+msgstr "Mainteneurs"
msgid "SubgroupCreationlevel|Owners"
msgstr "Propriétaires"
@@ -39235,10 +39835,10 @@ msgid "SubscriptionTable|Billing"
msgstr "Facturation"
msgid "SubscriptionTable|Free"
-msgstr ""
+msgstr "Gratuit"
msgid "SubscriptionTable|GitLab allows you to continue using your subscription even if you exceed the number of seats you purchased. You will be required to pay for these seats upon renewal."
-msgstr ""
+msgstr "GitLab vous permet de continuer à utiliser votre abonnement même si vous dépassez le nombre de sièges que vous avez achetés. Il vous sera nécessaire de payer ces sièges lors du renouvellement."
msgid "SubscriptionTable|Last invoice"
msgstr "Dernière facture"
@@ -39250,10 +39850,10 @@ msgid "SubscriptionTable|Manage"
msgstr "Gérer"
msgid "SubscriptionTable|Max seats used"
-msgstr ""
+msgstr "Max de sièges utilisés"
msgid "SubscriptionTable|Next invoice"
-msgstr ""
+msgstr "Prochaine facture"
msgid "SubscriptionTable|Refresh Seats"
msgstr "Actualiser les Sièges"
@@ -39262,13 +39862,13 @@ msgid "SubscriptionTable|Renew"
msgstr "Renouveler"
msgid "SubscriptionTable|Seats currently in use"
-msgstr ""
+msgstr "Sièges actuellement utilisés"
msgid "SubscriptionTable|Seats in subscription"
-msgstr ""
+msgstr "Sièges dans l'abonnement"
msgid "SubscriptionTable|Seats owed"
-msgstr ""
+msgstr "Sièges dus"
msgid "SubscriptionTable|See usage"
msgstr "Voir l'utilisation"
@@ -39283,31 +39883,31 @@ msgid "SubscriptionTable|Subscription start date"
msgstr "Date de début de l'abonnement"
msgid "SubscriptionTable|This is the last time the GitLab.com team was in contact with you to settle any outstanding balances."
-msgstr ""
+msgstr "Il s'agit de la dernière date à laquelle l’équipe de GitLab.com vous a contacté pour une éventuelle régularisation."
msgid "SubscriptionTable|This is the maximum number of users that have existed at the same time since this subscription started."
-msgstr ""
+msgstr "Il s'agit du nombre maximum atteint d'utilisateurs présents simultanément depuis le début de cet abonnement."
msgid "SubscriptionTable|This is the next date when the GitLab.com team is scheduled to get in contact with you to settle any outstanding balances."
-msgstr ""
+msgstr "Il s'agit de la prochaine date à laquelle l’équipe de GitLab.com a prévu de vous contacter pour une éventuelle régularisation."
msgid "SubscriptionTable|This is the number of seats you will be required to purchase if you update to a paid plan."
-msgstr ""
+msgstr "Il s'agit du nombre de sièges que vous devrez acheter si vous effectuez une mise à jour vers un forfait payant."
msgid "SubscriptionTable|Trial"
-msgstr ""
+msgstr "Essai"
msgid "SubscriptionTable|Trial end date"
-msgstr ""
+msgstr "Date de fin de l'essai"
msgid "SubscriptionTable|Trial start date"
-msgstr ""
+msgstr "Date de début de l'essai"
msgid "SubscriptionTable|Usage"
msgstr "Utilisation"
msgid "SubscriptionTable|Usage count is performed once a day at 12:00 PM."
-msgstr ""
+msgstr "Le décompte d'utilisation est effectué une fois par jour à midi."
msgid "Subscriptions"
msgstr "Abonnements"
@@ -39331,10 +39931,10 @@ msgid "Subscription|Your subscription for %{strong}%{namespace_name}%{strong_clo
msgstr "Votre abonnement à %{strong}%{namespace_name}%{strong_close} a expiré et vous êtes maintenant sur l'%{pricing_link_start}offre GitLab Free%{pricing_link_end}. Ne vous inquiétez pas, vos données sont en sécurité. Contactez notre équipe d'assistance (%{support_email}). Ils se feront un plaisir de vous aider pour le renouvellement de votre abonnement."
msgid "Subtracted"
-msgstr ""
+msgstr "Soustraction de"
msgid "Subtracts"
-msgstr ""
+msgstr "Soustrait"
msgid "Succeeded"
msgstr ""
@@ -39391,7 +39991,7 @@ msgid "Suggest code changes which can be immediately applied in one click. Try i
msgstr "Suggérer des modifications de code qui peuvent être appliquées immédiatement en un seul clic. Essayez !"
msgid "Suggested change"
-msgstr ""
+msgstr "Modification suggérée"
msgid "SuggestedColors|Aztec Gold"
msgstr "Or aztèque"
@@ -39499,10 +40099,10 @@ msgid "Suggestion(s)"
msgstr "Suggestion(s)"
msgid "Suggestions are not applicable as one or more suggestions were not found."
-msgstr ""
+msgstr "Les suggestions ne sont pas applicables car une ou plusieurs d'entre elles n'ont pas été trouvées."
msgid "Suggestions are not applicable as their lines cannot overlap."
-msgstr ""
+msgstr "Les suggestions ne sont pas applicables car leurs lignes ne peuvent pas se chevaucher."
msgid "Suggestions must all be on the same branch."
msgstr "Les suggestions doivent toutes être sur la même branche."
@@ -39514,7 +40114,7 @@ msgid "Suite"
msgstr "Suite"
msgid "Summary"
-msgstr ""
+msgstr "Récapitulatif"
msgid "Summary / note"
msgstr "Résumé / note"
@@ -39535,7 +40135,7 @@ msgid "SuperSonics|Activation code"
msgstr "Code d'activation"
msgid "SuperSonics|Activation not possible due to seat mismatch"
-msgstr ""
+msgstr "Activation impossible en raison de sièges non concordants"
msgid "SuperSonics|Activation not possible due to true-up value mismatch"
msgstr "L'activation n'est pas possible à cause d'un ajustement incorrect"
@@ -39586,10 +40186,10 @@ msgid "SuperSonics|Maximum users"
msgstr "Nombre maximum d'utilisateurs"
msgid "SuperSonics|Offline license"
-msgstr ""
+msgstr "Licence hors ligne"
msgid "SuperSonics|Online license"
-msgstr ""
+msgstr "Licence en ligne"
msgid "SuperSonics|Paste your activation code"
msgstr "Collez votre code d'activation"
@@ -39656,6 +40256,9 @@ msgstr "Vous ne pouvez plus synchroniser les détails de votre abonnement avec G
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr "Vous pouvez commencer un essai gratuit de GitLab Ultimate sans aucune obligation ou détails de paiement."
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr "Vous n'avez pas d'abonnement actif"
@@ -39663,7 +40266,7 @@ msgid "SuperSonics|You have a future dated license"
msgstr ""
msgid "SuperSonics|You have added a license that activates on %{date}. Please see the subscription history table below for more details."
-msgstr ""
+msgstr "Vous avez ajouté une licence qui s’active le %{date}. Veuillez consulter l’historique des abonnements dans le tableau ci-dessous pour plus de détails."
msgid "SuperSonics|You have applied a true-up for %{trueUpQuantity} %{trueUpQuantityUsers} but you need one for %{expectedTrueUpQuantity} %{expectedTrueUpQuantityUsers}. To pay for seat overages, contact your sales representative. For further assistance, contact %{licenseSupportLinkStart}GitLab support%{licenseSupportLinkEnd}."
msgstr "Vous avez fait un ajustement pour %{trueUpQuantity} %{trueUpQuantityUsers} mais vous devez en faire un pour %{expectedTrueUpQuantity} %{expectedTrueUpQuantityUsers}. Pour vous acquitter des sièges supplémentaires, contactez votre représentant commercial. Pour une assistance complémentaire, contactez le %{licenseSupportLinkStart}support GitLab%{licenseSupportLinkEnd}."
@@ -39681,7 +40284,7 @@ msgid "SuperSonics|Your %{subscriptionEntryName} cannot be displayed at the mome
msgstr "Votre %{subscriptionEntryName} ne peut pas être affiché pour le moment. Veuillez actualiser la page pour réessayer."
msgid "SuperSonics|Your current GitLab installation has %{userCount} active %{userCountUsers}, which exceeds your new subscription seat count of %{licenseUserCount} by %{overageCount}. To activate your new subscription, %{purchaseLinkStart}purchase%{purchaseLinkEnd} an additional %{overageCount} %{overageCountSeats}, or %{deactivateLinkStart}deactivate%{deactivateLinkEnd} or %{blockLinkStart}block%{blockLinkEnd} %{overageCount} %{overageCountUsers}. For further assistance, contact %{licenseSupportLinkStart}GitLab support%{licenseSupportLinkEnd}."
-msgstr ""
+msgstr "Votre installation GitLab actuelle a %{userCount} %{userCountUsers} en activité, ce qui dépasse de %{overageCount} le nombre de sièges de votre nouvel abonnement qui est de %{licenseUserCount}. Pour activer votre nouvel abonnement, %{purchaseLinkStart}achetez%{purchaseLinkEnd} %{overageCount} %{overageCountSeats} de plus, ou %{deactivateLinkStart}désactivez%{deactivateLinkEnd} ou %{blockLinkStart}bloquez%{blockLinkEnd} %{overageCount} %{overageCountUsers}. Pour une assistance supplémentaire, contactez le %{licenseSupportLinkStart}support GitLab%{licenseSupportLinkEnd}."
msgid "SuperSonics|Your future dated license was successfully added"
msgstr "Votre licence postdatée a été ajoutée avec succès"
@@ -39746,9 +40349,6 @@ msgstr "Changer de branche"
msgid "Switch branch/tag"
msgstr "Changer de branche ou d’étiquette"
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr "Passer à GitLab Next"
@@ -39765,7 +40365,7 @@ msgid "Sync now"
msgstr "Synchroniser maintenant"
msgid "Synced"
-msgstr ""
+msgstr "Synchronisé"
msgid "Synchronize LDAP"
msgstr "Synchronisation LDAP"
@@ -39798,13 +40398,13 @@ msgid "SynthaxHighlightingTheme|Solarized Light"
msgstr "Clair solarisé"
msgid "System"
-msgstr ""
+msgstr "Système"
msgid "System Hooks"
-msgstr "« Hooks » système"
+msgstr "Crochets du système"
msgid "System Hooks Help"
-msgstr "Aide sur les « Hooks » système"
+msgstr "Aide sur les Crochets du système"
msgid "System Info"
msgstr "Informations système"
@@ -39852,7 +40452,7 @@ msgid "Tag name is required."
msgstr "Le nom de l'étiquette est requis."
msgid "Tag push"
-msgstr ""
+msgstr "Poussée d'étiquette"
msgid "Tag push events"
msgstr "Événements de poussée d'étiquette"
@@ -39948,7 +40548,7 @@ msgid "TagsPage|Only a project maintainer or owner can delete a protected tag"
msgstr "Seul un propriétaire ou un mainteneur du projet peut supprimer une étiquette protégée"
msgid "TagsPage|Optionally, add a message to the tag. Leaving this blank creates a %{link_start}lightweight tag.%{link_end}"
-msgstr ""
+msgstr "Éventuellement, ajoutez un message à l'étiquette. Laisser ce champ vide crée une %{link_start}étiquette légère.%{link_end}"
msgid "TagsPage|Please type the following to confirm:"
msgstr "Veuillez taper ce qui suit pour confirmer :"
@@ -40005,7 +40605,7 @@ msgid "Target branch"
msgstr "Branche cible"
msgid "Target branch or tag"
-msgstr ""
+msgstr "Branche ou étiquette cible"
msgid "Target roles"
msgstr "Rôles cibles"
@@ -40031,6 +40631,12 @@ msgstr "Créer/importer des tickets pour collaborer sur des idées et planifier
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr "Configurer des pipelines CI/CD pour construire, tester, déployer et surveiller le code"
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr "Équipe"
@@ -40150,7 +40756,7 @@ msgid "Terraform|Deletion in progress"
msgstr "Suppression en cours"
msgid "Terraform|Details"
-msgstr ""
+msgstr "Détails"
msgid "Terraform|Download JSON"
msgstr "Télécharger le JSON"
@@ -40262,7 +40868,7 @@ msgstr[0] "Couverture de test : %d atteint"
msgstr[1] "Couverture de test : %d atteints"
msgid "Test settings"
-msgstr "Paramètres de test"
+msgstr "Tester les paramètres"
msgid "TestCases|Move test case"
msgstr "Déplacer le cas de test"
@@ -40280,7 +40886,7 @@ msgid "TestCases|Search test cases"
msgstr "Rechercher des cas de test"
msgid "TestCases|Something went wrong while adding test case to a to-do item."
-msgstr "Une erreur s'est produite lors de l'ajout du scénario de test à un élément À faire."
+msgstr "Une erreur s'est produite lors de l'ajout du scénario de test à un pense-bête."
msgid "TestCases|Something went wrong while creating a test case."
msgstr "Une erreur s'est produite lors de la création d'un cas de test."
@@ -40292,7 +40898,7 @@ msgid "TestCases|Something went wrong while fetching test cases list."
msgstr "Une erreur s'est produite lors de la récupération de la liste des cas de test."
msgid "TestCases|Something went wrong while marking test case to-do item as done."
-msgstr "Une erreur s'est produite lors du marquage de l'élément À faire du scénario de test comme terminé."
+msgstr "Une erreur s'est produite lors du marquage du pense-bête du scénario de test comme fait."
msgid "TestCases|Something went wrong while moving test case."
msgstr "Une erreur s'est produite lors du déplacement du cas de test."
@@ -40313,7 +40919,7 @@ msgid "TestHooks|Ensure the project has CI jobs."
msgstr ""
msgid "TestHooks|Ensure the project has CI pipelines."
-msgstr ""
+msgstr "Assurez-vous que le projet dispose de pipelines CI."
msgid "TestHooks|Ensure the project has deployments."
msgstr ""
@@ -40343,7 +40949,7 @@ msgid "TestReports|%{count} tests"
msgstr "%{count} tests"
msgid "TestReports|%{rate}%{sign} success rate"
-msgstr ""
+msgstr "Taux de réussite %{rate}%{sign}"
msgid "TestReports|Attachment"
msgstr "Pièce jointe"
@@ -40474,10 +41080,10 @@ msgid "The URLs for connecting to Elasticsearch. For clustering, add the URLs se
msgstr "Les URLs pour se connecter à Elasticsearch. Pour les grappes de serveurs, ajoutez les URLs séparées par des virgules."
msgid "The `/merge` quick action requires the SHA of the head of the branch."
-msgstr ""
+msgstr "L'action rapide `/merge` nécessite l'empreinte SHA du pointeur HEAD de la branche."
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
-msgstr ""
+msgstr "L'application sera utilisée lorsque le secret du client peut être tenu confidentiel. Les applications mobiles natives et les applications web monopage sont considérées comme non confidentielles."
msgid "The associated issue #%{issueId} has been closed as the error is now resolved."
msgstr "Le ticket #%{issueId} associé a été fermé car l'erreur est maintenant résolue."
@@ -40507,7 +41113,7 @@ msgid "The complete DevOps platform. One application with endless possibilities.
msgstr ""
msgid "The compliance report shows the merge request violations merged in protected environments."
-msgstr ""
+msgstr "Le rapport de conformité montre les violations des demandes de fusion fusionnées dans les environnements protégés."
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr "La connexion expirera après %{timeout}. Pour les dépôts qui nécessitent plus de temps, utilisez une combinaison de clone et push."
@@ -40528,7 +41134,7 @@ msgid "The content of this page is not encoded in UTF-8. Edits can only be made
msgstr "Le contenu de cette page n'est pas codé en UTF-8. Les modifications ne peuvent être effectuées que via le dépôt Git."
msgid "The contents of this group, its subgroups and projects will be permanently removed after %{deletion_adjourned_period} days on %{date}. After this point, your data cannot be recovered."
-msgstr ""
+msgstr "Le contenu de ce groupe, de ses sous-groupes et de ses projets sera supprimé définitivement après %{deletion_adjourned_period} jours le %{date}. Après cette date, vos données ne pourront plus être récupérées."
msgid "The current epic"
msgstr "L'épopée actuelle"
@@ -40537,7 +41143,7 @@ msgid "The current incident"
msgstr "L'incident actuel"
msgid "The current issue"
-msgstr ""
+msgstr "Le ticket actuel"
msgid "The current user is not authorized to access the job log."
msgstr "L'utilisateur actuel n'est pas autorisé à accéder au journal des tâches."
@@ -40555,7 +41161,7 @@ msgid "The default CI/CD configuration file and path for new projects."
msgstr "Le fichier de configuration CI/CD et le chemin par défaut des nouveaux projets."
msgid "The default branch for this project has been changed. Please update your bookmarks."
-msgstr "La branche par défaut de ce projet a été modifiée. Veuillez mettre à jour vos favoris."
+msgstr "La branche par défaut de ce projet a été modifiée. Veuillez mettre à jour vos marque-pages."
msgid "The dependency list details information about the components used within your project."
msgstr "La liste des dépendances détaille les informations sur les composants utilisés dans votre projet."
@@ -40611,6 +41217,9 @@ msgstr "Le Jeton d'Accès Personnel suivant a été révoqué par un administrat
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr "La clé SSH suivante a été supprimée par un administrateur, %{username}."
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr "Les fichiers ou répertoires suivants ne peuvent être modifiés que par l'utilisateur qui les a verrouillés."
+
msgid "The following items will NOT be exported:"
msgstr "Les éléments suivants ne seront PAS exportés :"
@@ -40619,8 +41228,8 @@ msgstr "Les éléments suivants seront exportés :"
msgid "The following personal access token: %{token_names} was revoked, because a new policy to expire personal access tokens were set."
msgid_plural "The following personal access tokens: %{token_names} were revoked, because a new policy to expire personal access tokens were set."
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "Le jeton d'accès personnel suivant : %{token_names} a été révoqué car une nouvelle stratégie d'expiration des jetons d'accès personnels a été définie."
+msgstr[1] "Les jetons d'accès personnels suivants : %{token_names} ont été révoqués car une nouvelle stratégie d'expiration des jetons d'accès personnels a été définie."
msgid "The fork relationship has been removed."
msgstr "La relation de divergence a été supprimée."
@@ -40637,16 +41246,16 @@ msgid "The git server, Gitaly, is not available at this time. Please contact you
msgstr "Le serveur git, Gitaly, n'est pas disponible pour le moment. Veuillez contacter votre administrateur."
msgid "The global settings require you to enable Two-Factor Authentication for your account."
-msgstr ""
+msgstr "Les paramètres globaux nécessitent que vous activiez l'authentification à deux facteurs sur votre compte."
msgid "The group and any internal projects can be viewed by any logged in user except external users."
-msgstr ""
+msgstr "Le groupe et tous les projets internes peuvent être vus par tout utilisateur connecté, à l'exception des utilisateurs externes."
msgid "The group and any public projects can be viewed without any authentication."
msgstr "Le groupe et tous les projets publics peuvent être visualisés sans aucune authentification."
msgid "The group and its projects can only be viewed by members."
-msgstr ""
+msgstr "Le groupe ainsi que ses projets ne sont accessibles qu’à ses membres."
msgid "The group export can be downloaded from:"
msgstr "L'export du groupe peut être téléchargé à partir de :"
@@ -40655,7 +41264,7 @@ msgid "The group has already been shared with this group"
msgstr "Le groupe a déjà été partagé avec ce groupe"
msgid "The group settings for %{group_links} require you to enable Two-Factor Authentication for your account. You can %{leave_group_links}."
-msgstr ""
+msgstr "Les paramètres de groupe pour %{group_links} nécessitent l'activation de l'authentification à deux facteurs pour votre compte. Vous pouvez %{leave_group_links}."
msgid "The group_project_ids parameter is only allowed for a group"
msgstr "Le paramètre group_project_ids n'est autorisé que pour un groupe"
@@ -40667,7 +41276,7 @@ msgid "The hostname of your Snowplow collector."
msgstr "Le nom d'hôte de votre collecteur Snowplow."
msgid "The import cannot be canceled because it is %{project_status}"
-msgstr ""
+msgstr "L'importation ne peut pas être annulée car elle est %{project_status}"
msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr "L’importation expirera après %{timeout}. Pour les dépôts qui prennent plus de temps, utilisez une combinaison de clone et push."
@@ -40694,7 +41303,7 @@ msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
msgstr "Le ticket a été promu en épopée avec succès. Redirection vers l'épopée..."
msgid "The last owner cannot be set to awaiting"
-msgstr ""
+msgstr "Le dernier propriétaire ne peut pas être mis en attente"
msgid "The latest artifacts created by jobs in the most recent successful pipeline will be stored."
msgstr "Les derniers artéfacts créés par les tâches du pipeline réussi le plus récemment seront enregistrés."
@@ -40751,13 +41360,13 @@ msgid "The maximum number of tags that a single worker accepts for cleanup. If t
msgstr "Le nombre maximal d'étiquettes qu'un worker unique accepte de nettoyer. Si le nombre d'étiquettes vient à dépasser cette limite, la liste des étiquettes à supprimer est tronquée à cette valeur. Pour enlever cette limite, définissez-la à 0."
msgid "The merge conflicts for this merge request cannot be resolved through GitLab. Please try to resolve them locally."
-msgstr ""
+msgstr "Les conflits de fusion pour cette demande de fusion ne peuvent pas être résolus via GitLab. Veuillez essayer de les résoudre localement."
msgid "The merge conflicts for this merge request have already been resolved."
msgstr "Les conflits de fusion de cette demande de fusion ont déjà été résolus."
msgid "The merge conflicts for this merge request have already been resolved. Please return to the merge request."
-msgstr ""
+msgstr "Les conflits de fusion pour cette demande de fusion ont déjà été résolus. Veuillez retourner à la demande de fusion."
msgid "The metric must be one of %{metrics}."
msgstr ""
@@ -40778,7 +41387,7 @@ msgid "The number of merge requests merged by month."
msgstr "Le nombre de demandes de fusion fusionnées par mois."
msgid "The number of times an upload record could not find its file"
-msgstr ""
+msgstr "Le nombre de fois qu'un enregistrement téléversé n'a pas pu trouver son fichier"
msgid "The page could not be displayed because it timed out."
msgstr "La page n'a pas pu être affichée car le délai d'attente a expiré."
@@ -40826,19 +41435,19 @@ msgid "The project was successfully imported."
msgstr "Le projet a été importé avec succès."
msgid "The related CI build failed."
-msgstr ""
+msgstr "La construction CI associée a échoué."
msgid "The remote mirror URL is invalid."
msgstr "L'URL du miroir distant n'est pas valide."
msgid "The remote mirror took to long to complete."
-msgstr ""
+msgstr "Le miroir distant a pris trop de temps pour se terminer."
msgid "The remote repository is being updated..."
msgstr "Le dépôt distant est en cours de mise à jour …"
msgid "The report artifact provided by the CI build couldn't be parsed."
-msgstr ""
+msgstr "L'artéfact de rapport fourni par la construction CI n'a pas pu être analysé."
msgid "The report has been successfully prepared."
msgstr "Le rapport a été préparé avec succès."
@@ -40877,7 +41486,7 @@ msgid "The snippet is visible only to me."
msgstr "L'extrait de code est visible seulement pour moi."
msgid "The snippet is visible only to project members."
-msgstr ""
+msgstr "L'extrait de code n'est visible que par les membres du projet."
msgid "The snippet is visible to any logged in user except external users."
msgstr "Tous les utilisateurs connectés peuvent voir l'extrait, à l'exception des utilisateurs externes."
@@ -40892,7 +41501,7 @@ msgid "The source topic is not a topic."
msgstr "Le sujet source n'est pas un sujet."
msgid "The specified tab is invalid, please select another"
-msgstr ""
+msgstr "L'onglet spécifié n'est pas valide, veuillez en sélectionner un autre"
msgid "The start date must be earlier than the end date."
msgstr "La date de début doit être antérieure à la date de fin."
@@ -40919,7 +41528,7 @@ msgid "The user is being deleted."
msgstr "L'utilisateur est en cours de suppression."
msgid "The user map has been saved. Continue by selecting the projects you want to import."
-msgstr ""
+msgstr "La carte de correspondance des utilisateurs a été enregistrée. Continuez par la sélection des projets que vous souhaitez importer."
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr "La carte des utilisateurs met en correspondance les utilisateurs de FogBugz qui ont participé à vos projets en précisant la manière dont leurs adresses de courriel et leurs noms d’utilisateur sont importés dans GitLab. Vous pouvez y apporter des modifications en remplissant le tableau ciâ€dessous."
@@ -40975,9 +41584,6 @@ msgstr "Il n'y a pas de Journaux d'Indésirables"
msgid "There are no abuse reports!"
msgstr "Il n'y a aucun rapport d'abus !"
-msgid "There are no archived projects yet"
-msgstr "Il n’y a pas encore de projets archivés"
-
msgid "There are no archived requirements"
msgstr "Il n'y a aucune exigence archivée"
@@ -41006,7 +41612,7 @@ msgid "There are no commits yet."
msgstr "Il n'y a pas encore de commit."
msgid "There are no custom project templates set up for this GitLab instance. They are enabled from GitLab's Admin Area. Contact your GitLab instance administrator to setup custom project templates."
-msgstr ""
+msgstr "Aucun modèle de projet personnalisé n'a été configuré pour cette instance de GitLab. Ils sont activés à partir de l'Espace d'Administration de GitLab. Contactez votre administrateur d'instance GitLab pour configurer des modèles de projet personnalisés."
msgid "There are no issues to show"
msgstr "Il n’y a aucun ticket à afficher"
@@ -41035,9 +41641,6 @@ msgstr "Il n'y a aucun cas de test ouvert"
msgid "There are no packages yet"
msgstr "Il n'y a pas encore de paquets"
-msgid "There are no projects shared with this group yet"
-msgstr "Il n’y a pas encore de projets partagés avec ce groupe"
-
msgid "There are no secure files yet."
msgstr "Il n'y a pas encore de fichier sécurisé."
@@ -41063,7 +41666,7 @@ msgid "There is already a repository with that name on disk"
msgstr "Il existe déjà un dépôt avec ce nom sur le disque"
msgid "There is already a to-do item for this design."
-msgstr "Il y a déjà un élément « À faire » pour ce design."
+msgstr "Il y a déjà un pense-bête pour cette esquisse."
msgid "There is no chart data available."
msgstr "Il n'y a aucune donnée de graphique disponible."
@@ -41083,6 +41686,9 @@ msgstr "Il y a trop de données à calculer. Veuillez modifier votre sélection.
msgid "There was a problem communicating with your device."
msgstr "Une erreur s'est produite lors de la communication avec votre appareil."
+msgid "There was a problem creating the incident. Please try again."
+msgstr "Une erreur s'est produite lors de la création de l'incident. Veuillez réessayez."
+
msgid "There was a problem fetching CRM contacts."
msgstr "Une erreur s'est produite lors de la récupération des contacts du CRM."
@@ -41159,7 +41765,7 @@ msgid "There was a problem updating the keep latest artifacts setting."
msgstr "Une erreur s'est produite lors de la mise à jour du paramètre de conservation des derniers artéfacts."
msgid "There was an error adding a To Do."
-msgstr ""
+msgstr "Une erreur s'est produite lors de l'ajout d'un pense-bête."
msgid "There was an error creating the dashboard, branch name is invalid."
msgstr "Une erreur s'est produite lors de la création du tableau de bord, le nom de la branche n'est pas valide."
@@ -41171,7 +41777,7 @@ msgid "There was an error creating the issue"
msgstr "Une erreur s'est produite lors de la création du ticket"
msgid "There was an error deleting the To Do."
-msgstr ""
+msgstr "Une erreur s'est produite lors de la suppression du pense-bête."
msgid "There was an error fetching configuration for charts"
msgstr "Une erreur s'est produite lors de la récupération de la configuration des graphiques"
@@ -41180,10 +41786,10 @@ msgid "There was an error fetching data for the selected stage"
msgstr "Une erreur s'est produite lors de la récupération des données de l'étape sélectionnée"
msgid "There was an error fetching data for the tasks by type chart"
-msgstr ""
+msgstr "Une erreur s'est produite lors de la récupération des données pour le graphique des tâches par type"
msgid "There was an error fetching label data for the selected group"
-msgstr ""
+msgstr "Une erreur s'est produite lors de la récupération des données des étiquettes pour le groupe sélectionné"
msgid "There was an error fetching median data for stages"
msgstr "Une erreur s'est produite lors de la récupération des données médianes des étapes"
@@ -41195,7 +41801,7 @@ msgid "There was an error fetching stage total counts"
msgstr "Une erreur s'est produite lors de la récupération du nombre total d'étapes"
msgid "There was an error fetching the %{replicableType}"
-msgstr ""
+msgstr "Une erreur s'est produite lors de la récupération de %{replicableType}"
msgid "There was an error fetching the deploy freezes."
msgstr "Une erreur s'est produite lors de la récupération des gels de déploiement."
@@ -41203,6 +41809,9 @@ msgstr "Une erreur s'est produite lors de la récupération des gels de déploie
msgid "There was an error fetching the environments information."
msgstr "Une erreur s'est produite lors de la récupération des informations sur les environnements."
+msgid "There was an error fetching the job."
+msgstr "Une erreur s'est produite lors de la récupération de la tâche."
+
msgid "There was an error fetching the jobs for your project."
msgstr "Une erreur s'est produite lors de la récupération des tâches de votre projet."
@@ -41234,14 +41843,17 @@ msgid "There was an error parsing the data for this graph."
msgstr "Une erreur s'est produite lors de l'analyse des données de ce graphe."
msgid "There was an error removing the e-mail."
-msgstr ""
+msgstr "Une erreur s'est produite lors de la suppression du courriel."
msgid "There was an error resetting user pipeline minutes."
-msgstr ""
+msgstr "Une erreur s'est produite lors de la réinitialisation des minutes du pipeline utilisateur."
msgid "There was an error retrieving the Jira users."
msgstr "Une erreur s'est produite lors de la récupération des utilisateurs Jira."
+msgid "There was an error running the job. Please try again."
+msgstr "Une erreur s'est produite lors de l'exécution de la tâche. Veuillez réessayer."
+
msgid "There was an error saving your changes."
msgstr "Une erreur s'est produite lors de l'enregistrement de vos modifications."
@@ -41318,13 +41930,13 @@ msgid "Third Party Advisory Link"
msgstr "Liens des Annonces d'Offres Tierces"
msgid "This %{issuableDisplayName} is locked. Only project members can comment."
-msgstr ""
+msgstr "Ce %{issuableDisplayName} est verrouillé. Seuls les membres du projet peuvent commenter."
msgid "This %{issuable} is locked. Only %{strong_open}project members%{strong_close} can comment."
msgstr "Ce(tte) %{issuable} est verrouillé(e). Seuls les %{strong_open}membres du projet%{strong_close} peuvent apporter des commentaires."
msgid "This %{noteableTypeText} is %{confidentialLinkStart}confidential%{confidentialLinkEnd} and %{lockedLinkStart}locked%{lockedLinkEnd}."
-msgstr ""
+msgstr "Ce %{noteableTypeText} est %{confidentialLinkStart}confidentiel%{confidentialLinkEnd} et %{lockedLinkStart}verrouillé%{lockedLinkEnd}."
msgid "This %{noteableTypeText} is locked."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr "Ce motif Cron n'est pas valide"
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr "Cette instance de GitLab ne fournit aucun exécuteur partagé pour le moment. Les administrateurs de l’instance peuvent en enregistrer dans l'espace d’administration."
@@ -41351,7 +41960,7 @@ msgid "This URL already exists."
msgstr "Cette URL existe déjà."
msgid "This action can lead to data loss. To prevent accidental actions we ask you to confirm your intention."
-msgstr ""
+msgstr "Cette action peut entraîner une perte de données. Pour éviter les manœuvres accidentelles, nous vous demandons de confirmer que c'est à votre initiative."
msgid "This action cannot be undone, and will permanently delete the %{key} SSH key"
msgstr "Cette action ne pourra pas être annulée et supprimera définitivement la clé SSH %{key}"
@@ -41405,7 +42014,7 @@ msgid "This board's scope is reduced"
msgstr "La portée de ce tableau est réduite"
msgid "This branch has diverged from upstream."
-msgstr ""
+msgstr "Cette branche a divergé de l'amont."
msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
msgstr "Cette modification va supprimer %{strongOpen}TOUTES%{strongClose} les fonctionnalités Premium et Ultimate pour %{strongOpen}TOUS%{strongClose} les clients SaaS et faire échouer le démarrage des tests."
@@ -41423,7 +42032,7 @@ msgid "This comment changed after you started editing it. Review the %{startTag}
msgstr "Ce commentaire a changé depuis que vous avez commencé à le modifier. Examinez sa %{startTag}version actualisée%{endTag} pour vous assurer qu'il n'y a pas de perte d'information."
msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
-msgstr ""
+msgstr "Ce commit fait partie de la demande de fusion %{link_to_merge_request}. Les commentaires créés ici seront créés dans le contexte de cette demande de fusion."
msgid "This commit was signed with %{strong_open}multiple%{strong_close} signatures."
msgstr "Ce commit a été signé avec %{strong_open}plusieurs%{strong_close} signatures."
@@ -41456,7 +42065,7 @@ msgid "This deployment job does not run automatically and must be started manual
msgstr "Cette tâche de déploiement ne s'exécute pas automatiquement et doit être démarrée manuellement, mais elle est antérieure au déploiement le plus récent et ne peut donc pas s'exécuter."
msgid "This deployment job does not run automatically and must be started manually, but you do not have access to this job's protected environment. The job can only be started by a project member allowed to deploy to the environment."
-msgstr ""
+msgstr "Cette tâche de déploiement ne s'exécute pas automatiquement et doit être démarrée manuellement, mais vous n'avez pas accès à l'environnement protégé de cette tâche. Celle-ci ne peut être lancée que par un membre du projet autorisé à déployer sur l'environnement."
msgid "This device has already been registered with us."
msgstr "Cet appareil a déjà été enregistré auprès de nous."
@@ -41486,7 +42095,7 @@ msgid "This endpoint has been requested too many times. Try again later."
msgstr "Ce point de terminaison a été sollicité trop souvent. Réessayez plus tard."
msgid "This environment has no deployments yet."
-msgstr ""
+msgstr "Il n'y a pas encore de déploiement dans cet environnement."
msgid "This environment is being deployed"
msgstr "Cet environnement est en cours de déploiement"
@@ -41527,9 +42136,12 @@ msgstr "Cette épopée n'existe pas ou vous n'avez pas les permissions suffisant
msgid "This epic would exceed maximum number of related epics."
msgstr "Cette épopée dépasserait le nombre maximum d'épopées associées."
-msgid "This feature requires local storage to be enabled"
+msgid "This experiment has no logged candidates"
msgstr ""
+msgid "This feature requires local storage to be enabled"
+msgstr "Cette fonctionnalité nécessite que le stockage local soit activé"
+
msgid "This field is required"
msgstr "Ce champ est requis"
@@ -41545,8 +42157,8 @@ msgstr "Ce formulaire est désactivé dans l'aperçu"
msgid "This group"
msgstr "Ce groupe"
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
-msgstr "Ce groupe et ses sous-groupes et projets seront placés dans un état de « suppression en attente » pendant %{deletion_adjourned_period} jours, puis définitivement supprimés le %{date}. Le groupe peut être entièrement restauré avant cette date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgstr "Ce groupe et ses sous-groupes et projets seront placés dans un état de « suppression en attente » pendant %{deletion_delayed_period} jours, puis définitivement supprimés le %{date}. Le groupe peut être entièrement restauré avant cette date."
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
msgstr "Ce groupe ne peut pas être supprimé car il est lié à un abonnement. Pour supprimer ce groupe, %{linkStart}associez l'abonnement%{linkEnd} à un groupe différent."
@@ -41573,7 +42185,7 @@ msgid "This group is linked to a subscription"
msgstr "Ce groupe est lié à un abonnement"
msgid "This group is not permitted to create compliance violations"
-msgstr ""
+msgstr "Ce groupe n'est pas autorisé à créer des violations de conformité"
msgid "This group, its subgroups and projects has been scheduled for removal on %{date}."
msgstr "Ce groupe, ses sous-groupes et ses projets ont été programmés pour être supprimés le %{date}."
@@ -41645,13 +42257,13 @@ msgid "This issue is in a child epic of the filtered epic"
msgstr "Ce ticket est une épopée enfant de l'épopée filtrée"
msgid "This job could not start because it could not retrieve the needed artifacts%{punctuation}%{invalid_dependencies}"
-msgstr ""
+msgstr "Cette tâche n'a pas pu démarrer car elle n'a pas pu récupérer les artifacts%{punctuation}%{invalid_dependencies} requis"
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr "Cette tâche dépend des tâches en amont qui doivent réussir pour que celleâ€ci soit déclenchée"
msgid "This job deploys to the protected environment \"%{environment}\" which requires approvals."
-msgstr ""
+msgstr "Cette tâche effectue un déploiement vers l'environnement protégé « %{environment} » qui nécessite des approbations."
msgid "This job does not have a trace."
msgstr "Cette tâche n’a pas de trace."
@@ -41678,16 +42290,16 @@ msgid "This job is an out-of-date deployment to %{environmentLink} using cluster
msgstr "Cette tâche est un déploiement obsolète vers %{environmentLink} utilisant la grappe de serveurs %{clusterNameOrLink} et l'espace de noms %{kubernetesNamespace}. Voir le %{deploymentLink}."
msgid "This job is an out-of-date deployment to %{environmentLink} using cluster %{clusterNameOrLink}."
-msgstr ""
+msgstr "Cette tâche est un ancien déploiement vers %{environmentLink} effectué par la grappe de serveurs %{clusterNameOrLink}."
msgid "This job is an out-of-date deployment to %{environmentLink} using cluster %{clusterNameOrLink}. View the %{deploymentLink}."
-msgstr ""
+msgstr "Cette tâche est un ancien déploiement vers %{environmentLink} effectué par la grappe de serveurs %{clusterNameOrLink}. Voir le %{deploymentLink}."
msgid "This job is an out-of-date deployment to %{environmentLink}."
msgstr "Cette tâche est un déploiement obsolète sur %{environmentLink}."
msgid "This job is an out-of-date deployment to %{environmentLink}. View the %{deploymentLink}."
-msgstr ""
+msgstr "Cette tâche est un ancien déploiement vers %{environmentLink}. Voir le %{deploymentLink}."
msgid "This job is archived. Only the complete pipeline can be retried."
msgstr "Cette tâche est archivée. Seul le pipeline complet peut faire l’objet d’une nouvelle exécution."
@@ -41702,28 +42314,28 @@ msgid "This job is creating a deployment to %{environmentLink} using cluster %{c
msgstr "Cette tâche est en train de créer un déploiement sur %{environmentLink} en utilisant la grappe de serveurs %{clusterNameOrLink}."
msgid "This job is creating a deployment to %{environmentLink} using cluster %{clusterNameOrLink}. This will overwrite the %{deploymentLink}."
-msgstr ""
+msgstr "Cette tâche est en train de créer un déploiement sur %{environmentLink} en utilisant la grappe de serveurs %{clusterNameOrLink}. Ceci écrasera le %{deploymentLink}."
msgid "This job is creating a deployment to %{environmentLink}."
msgstr "Cette tâche va effectuer un déploiement sur %{environmentLink}."
msgid "This job is creating a deployment to %{environmentLink}. This will overwrite the %{deploymentLink}."
-msgstr ""
+msgstr "Cette tâche est en train de créer un déploiement sur %{environmentLink}. Ceci écrasera le %{deploymentLink}."
msgid "This job is deployed to %{environmentLink} using cluster %{clusterNameOrLink} and namespace %{kubernetesNamespace}."
msgstr "Cette tâche est déployée sur %{environmentLink} en utilisant la grappe de serveurs %{clusterNameOrLink} et l'espace de noms %{kubernetesNamespace}."
msgid "This job is deployed to %{environmentLink} using cluster %{clusterNameOrLink}."
-msgstr ""
+msgstr "Cette tâche est déployée vers %{environmentLink} par la grappe de serveurs %{clusterNameOrLink}."
msgid "This job is deployed to %{environmentLink}."
-msgstr ""
+msgstr "Cette tâche est déployée vers %{environmentLink}."
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr "Cette tâche est en attente d’être choisie par un exécuteur"
msgid "This job is performing tasks that must complete before it can start"
-msgstr ""
+msgstr "Cette tâche est en train d'effectuer des actions qui doivent se terminer avant qu'elle ne puisse démarrer"
msgid "This job is preparing to start"
msgstr ""
@@ -41792,7 +42404,7 @@ msgid "This only applies to repository indexing operations."
msgstr "Cela ne s'applique qu'aux opérations d'indexation des dépôts."
msgid "This page is hosted on GitLab pages but contains user-generated content and may contain malicious code. Do not accept unless you trust the author and source."
-msgstr ""
+msgstr "Cette page est hébergée sur GitLab Pages mais contient du contenu créé par des utilisateurs et peut contenir du code malveillant. N'acceptez pas à moins de faire confiance à l'auteur et à la source."
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr "Cette page n’est pas disponible car vous n’êtes pas autorisé à lire des informations à travers de multiples projets."
@@ -41837,13 +42449,13 @@ msgid "This project is %{strongStart}NOT%{strongEnd} a fork. This process delete
msgstr "Ce projet %{strongStart}N'EST PAS%{strongEnd} une divergence. Ce processus supprime le dépôt du projet et toutes ses ressources associées."
msgid "This project is archived and cannot be commented on."
-msgstr ""
+msgstr "Ce projet est archivé et ne peut pas être commenté."
msgid "This project is licensed under the %{strong_start}%{license_name}%{strong_end}."
msgstr "Ce projet est sous licence %{strong_start}%{license_name}%{strong_end}."
msgid "This project is mirrored from %{link}."
-msgstr ""
+msgstr "Ce projet est mis en miroir à partir de %{link}."
msgid "This project is not subscribed to any project pipelines."
msgstr "Ce projet n'est abonné à aucun pipeline de projets."
@@ -41903,7 +42515,7 @@ msgid "This setting is allowed for forked projects only"
msgstr "Ce paramètre n'est autorisé que pour les projets divergents"
msgid "This subscription is for"
-msgstr ""
+msgstr "Cet abonnement est pour"
msgid "This suggestion already matches its content."
msgstr "Cette suggestion correspond déjà à son contenu."
@@ -41942,7 +42554,7 @@ msgid "This variable can not be masked."
msgstr "Cette variable ne peut pas être masquée."
msgid "This vulnerability type has been deprecated from GitLab's default ruleset and automatically resolved."
-msgstr ""
+msgstr "Ce type de vulnérabilité a été déprécié du jeu de règles par défaut de GitLab et résolu automatiquement."
msgid "This will invalidate your registered applications and U2F / WebAuthn devices."
msgstr "Cela va invalider vos applications enregistrées et vos appareils U2F/WebAuthn."
@@ -41960,7 +42572,7 @@ msgid "Thread options"
msgstr "Options du fil de discussion"
msgid "Thread to reply to cannot be found"
-msgstr ""
+msgstr "Le fil de discussion auquel répondre est introuvable"
msgid "Threshold in bytes at which to compress Sidekiq job arguments."
msgstr "Seuil en octets à partir duquel compresser les arguments des tâches Sidekiq."
@@ -41969,7 +42581,7 @@ msgid "Threshold in bytes at which to reject Sidekiq jobs. Set this to 0 to if y
msgstr "Seuil en octets à partir duquel rejeter les tâches Sidekiq. Définissez-le sur 0 si vous ne souhaitez pas de limite sur les tâches Sidekiq."
msgid "Threshold number of changes (branches or tags) in a single push above which a bulk push event is created (default is 3)."
-msgstr ""
+msgstr "Seuil du nombre de modifications (branches ou étiquettes) au sein d'une poussée unique pour lequel un événement de poussée en masse est créé (3 par défaut)."
msgid "Throughput"
msgstr ""
@@ -41987,7 +42599,7 @@ msgid "Time (in hours) that users are allowed to skip forced configuration of tw
msgstr "Durée (en heures) pendant laquelle les utilisateurs sont autorisés à ignorer la configuration obligatoire de l’authentification à deux facteurs."
msgid "Time based: Yes"
-msgstr ""
+msgstr "Basé sur le temps : oui"
msgid "Time before an issue gets scheduled"
msgstr "Temps avant qu’un ticket ne soit planifié"
@@ -42019,8 +42631,14 @@ msgstr "Temps restant"
msgid "Time spent"
msgstr "Temps passé"
+msgid "Time spent can't be zero."
+msgstr "Le temps passé ne peut pas être nul."
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr "Le temps passé doit être formaté correctement. Par exemple : 1h 30m."
+
msgid "Time to Restore Service"
-msgstr ""
+msgstr "Délai de restauration de service"
msgid "Time to merge"
msgstr ""
@@ -42191,10 +42809,10 @@ msgid "Timeline event added successfully."
msgstr "Événement de chronologie ajouté avec succès."
msgid "Timeline|Turn recent updates view off"
-msgstr ""
+msgstr "Désactiver la vue des mises à jour récentes"
msgid "Timeline|Turn recent updates view on"
-msgstr ""
+msgstr "Activer la vue des mises à jour récentes"
msgid "Timelog doesn't exist or you don't have permission to delete it"
msgstr "L'entrée de temps passé n'existe pas ou vous n'avez pas la permission de la supprimer"
@@ -42269,17 +42887,20 @@ msgstr "Titre (obligatoire)"
msgid "Title:"
msgstr "Titre :"
+msgid "Titles"
+msgstr "Titres"
+
msgid "Titles and Descriptions"
msgstr "Titres et descriptions"
msgid "To"
-msgstr ""
+msgstr "Au"
msgid "To %{link_to_help} of your domain, add the above key to a TXT record within your DNS configuration."
msgstr "Pour %{link_to_help} de votre domaine, ajoutez la clé ci-dessus à un enregistrement TXT dans votre configuration DNS."
msgid "To Do"
-msgstr ""
+msgstr "À faire"
msgid "To GitLab"
msgstr "Vers GitLab"
@@ -42294,25 +42915,22 @@ msgid "To accept this invitation, sign in."
msgstr "Pour accepter cette invitation, connectez-vous."
msgid "To access this domain create a new DNS record"
-msgstr ""
+msgstr "Pour accéder à ce domaine, créez un nouvel enregistrement DNS"
msgid "To activate your trial, we need additional details from you."
-msgstr ""
+msgstr "Pour activer votre essai, nous avons besoin d'informations supplémentaires de votre part."
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr "Pour ajouter un suffixe personnalisé, configurez une adresse électronique pour le Service d'Assistance. %{linkStart}En savoir plus.%{linkEnd}"
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr "Pour ajouter le nom à afficher, configurez l'adresse de courriel du Service d'Assistance. %{linkStart}En savoir plus.%{linkEnd}"
-
msgid "To add the entry manually, provide the following details to the application on your phone."
-msgstr ""
+msgstr "Pour ajouter l'entrée manuellement, fournissez les détails suivants à l'application sur votre téléphone."
msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
msgstr "Pour approuver cette demande de fusion, veuillez saisir votre mot de passe. Ce projet nécessite que toutes les approbations soient authentifiées."
msgid "To complete registration, we need additional details from you."
-msgstr ""
+msgstr "Pour terminer votre inscription, nous avons besoin d'informations supplémentaires de votre part."
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the %{code_open}repo%{code_close} scope, so we can display a list of your public and private repositories which are available to connect."
msgstr "Pour connecter des dépôts GitHub, vous pouvez utiliser un %{personal_access_token_link}. Lorsque vous créez votre Jeton d'Accès Personnel, vous devez sélectionner la portée %{code_open}repo%{code_close} pour que nous puissions afficher une liste de vos dépôts publics et privés qui sont disponibles pour connexion."
@@ -42351,7 +42969,7 @@ msgid "To ensure no loss of personal content, this account should only be used f
msgstr "Pour éviter toute perte de contenu personnel, ce compte ne doit être utilisé qu'en rapport avec %{group_name}."
msgid "To find the state of this project's repository at the time of any of these versions, check out %{link_start}the tags%{link_end}"
-msgstr ""
+msgstr "Pour retrouver l'état du dépôt de ce projet au moment de chacune de ses versions, extrayez-en %{link_start}les étiquettes%{link_end}"
msgid "To further protect your account, consider configuring a %{mfa_link_start}two-factor authentication%{mfa_link_end} method."
msgstr "Pour protéger davantage votre compte, envisagez de configurer une méthode d'%{mfa_link_start}authentification à deux facteurs%{mfa_link_end}."
@@ -42378,10 +42996,10 @@ msgid "To import an SVN repository, check out %{svn_link}."
msgstr "Pour importer un dépôt SVN, consultez %{svn_link}."
msgid "To keep this project going, create a new issue"
-msgstr ""
+msgstr "Pour maintenir ce projet actif, créez un nouveau ticket"
msgid "To keep this project going, create a new merge request"
-msgstr ""
+msgstr "Pour maintenir ce projet actif, créez une nouvelle demande de fusion"
msgid "To learn more about this project, read %{link_to_wiki}"
msgstr "Pour en savoir plus sur ce projet, lisez %{link_to_wiki}"
@@ -42419,9 +43037,12 @@ msgstr "Pour réactiver votre compte, %{gitlab_link_start}connectez-vous à GitL
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr "Pour réactiver votre compte, connectez-vous à GitLab sur %{gitlab_url}."
-msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
+msgstr "Pour supprimer l'état de %{link_start}lecture seule%{link_end} et retrouver l'accès en écriture, vous pouvez réduire le nombre d'utilisateurs dans votre espace de noms à %{free_limit} utilisateurs ou moins. Vous pouvez également passer à une édition payante, sans limite d'utilisateurs. Si vous avez besoin d'un temps de réflexion, vous pouvez commencer un essai gratuit de 30 jours qui comprend un nombre illimité d'utilisateurs."
+
msgid "To resolve this, try to:"
msgstr "Pour résoudre cela, essayez de :"
@@ -42432,10 +43053,10 @@ msgid "To see all the user's personal access tokens you must impersonate them fi
msgstr "Pour voir tous les jetons d'accès personnel d'un utilisateur, vous devez d'abord emprunter son identité."
msgid "To see this project's operational details, %{linkStart}upgrade its group plan to Premium%{linkEnd}. You can also remove the project from the dashboard."
-msgstr ""
+msgstr "Pour voir les détails opérationnels de ce projet, %{linkStart}mettez à niveau le forfait de son groupe vers Premium%{linkEnd}. Vous pouvez également supprimer le projet du tableau de bord."
msgid "To see this project's operational details, contact an owner of group %{groupName} to upgrade the plan. You can also remove the project from the dashboard."
-msgstr ""
+msgstr "Pour voir les détails opérationnels de ce projet, contactez un propriétaire du groupe %{groupName} pour mettre à niveau le forfait. Vous pouvez également supprimer le projet du tableau de bord."
msgid "To see what's changed or create a merge request, choose a branch or tag (like %{branch}), or enter a commit (like %{sha})."
msgstr "Pour voir ce qui a été modifié ou pour créer une demande de fusion, choisissez une branche ou une étiquette (comme %{branch}) ou entrez un commit ( comme %{sha})."
@@ -42447,7 +43068,7 @@ msgid "To set up this integration:"
msgstr "Pour configurer cette intégration :"
msgid "To specify the notification level per project of a group you belong to, you need to visit project page and change notification level there."
-msgstr ""
+msgstr "Pour spécifier le niveau de notification pour le projet d'un groupe auquel vous appartenez, vous devez vous rendre sur la page du projet et y modifier le niveau de notification."
msgid "To start using GitLab Enterprise Edition, upload the %{codeOpen}.gitlab-license%{codeClose} file or enter the license key you have received from GitLab Inc."
msgstr "Pour commencer à utiliser GitLab Édition Entreprise, téléversez le fichier %{codeOpen}.gitlab-license%{codeClose} ou entrez la clé de licence que vous avez reçue de GitLab Inc."
@@ -42456,7 +43077,7 @@ msgid "To unsubscribe from this issue, please paste the following link into your
msgstr "Pour vous désabonner de ce ticket, veuillez coller le lien suivant dans votre navigateur :"
msgid "To update Snippets with multiple files, you must use the `files` parameter"
-msgstr "Pour mettre à jour les Extraits de code avec plusieurs fichiers, vous devez utiliser le paramètre « files »"
+msgstr "Pour mettre à jour les Extraits de code avec plusieurs fichiers, vous devez utiliser le paramètre `files`"
msgid "To use the additional formats, you must start the required %{container_link_start}companion containers%{container_link_end}."
msgstr "Pour utiliser des formats supplémentaires, vous devez démarrer les %{container_link_start}conteneurs complémentaires%{container_link_end} requis."
@@ -42477,10 +43098,10 @@ msgid "To widen your search, change or remove filters above."
msgstr "Pour élargir votre recherche, modifiez ou supprimez des filtres ci-dessus."
msgid "To-Do List"
-msgstr "Liste de tâches"
+msgstr "Pense-bêtes"
msgid "To-do item successfully marked as done."
-msgstr ""
+msgstr "Le pense-bête a été marqué avec succès comme terminé."
msgid "Today"
msgstr "Aujourd’hui"
@@ -42501,17 +43122,20 @@ msgid "Todos|Any Type"
msgstr "Tout Type"
msgid "Todos|Are you looking for things to do? Take a look at %{strongStart}%{openIssuesLinkStart}open issues%{openIssuesLinkEnd}%{strongEnd}, contribute to %{strongStart}%{mergeRequestLinkStart}a merge request%{mergeRequestLinkEnd}%{mergeRequestLinkEnd}%{strongEnd}, or mention someone in a comment to automatically assign them a new to-do item."
-msgstr "Cherchez-vous des choses à faire ? Jetez un coup d'œil aux %{strongStart}%{openIssuesLinkStart}tickets ouverts%{openIssuesLinkEnd}%{strongEnd}, contribuez à %{strongStart}%{mergeRequestLinkStart}une demande de fusion%{mergeRequestLinkEnd}%{mergeRequestLinkEnd}%{strongEnd} ou mentionnez quelqu'un dans un commentaire pour lui assigner automatiquement une nouvelle tâche."
+msgstr "Cherchez-vous des choses à faire ? Jetez un coup d'œil aux %{strongStart}%{openIssuesLinkStart}tickets ouverts%{openIssuesLinkEnd}%{strongEnd}, contribuez à %{strongStart}%{mergeRequestLinkStart}une demande de fusion%{mergeRequestLinkEnd}%{mergeRequestLinkEnd}%{strongEnd} ou mentionnez quelqu'un dans un commentaire pour lui assigner automatiquement un nouveau pense-bête."
msgid "Todos|Assigned"
msgstr "A assigné"
msgid "Todos|Could not merge"
-msgstr ""
+msgstr "Impossible de fusionner"
msgid "Todos|Design"
msgstr "Design"
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr "Épopée"
@@ -42531,7 +43155,7 @@ msgid "Todos|Good job! Looks like you don't have anything left on your To-Do Lis
msgstr "Bon travail ! Il semblerait qu'il ne reste plus rien sur votre Liste de Tâches"
msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
-msgstr ""
+msgstr "Désormais, vous serez connu comme le « Destructeur de Pense-Bêtes »"
msgid "Todos|Isn't an empty To-Do List beautiful?"
msgstr "Une Liste de Tâches vide, n'est-ce-pas magnifique ?"
@@ -42545,6 +43169,9 @@ msgstr "C'est ainsi que vous savez toujours sur quoi travailler ensuite."
msgid "Todos|Mark all as done"
msgstr "Tout marquer comme terminé"
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr "A mentionné"
@@ -42552,7 +43179,7 @@ msgid "Todos|Merge request"
msgstr "Demande de fusion"
msgid "Todos|Nothing is on your to-do list. Nice work!"
-msgstr "Il n'y a rien sur votre liste de tâches. Beau travail !"
+msgstr "Il n'y a plus aucun pense-bête. Beau travail !"
msgid "Todos|Nothing left to do. High five!"
msgstr "Plus rien à faire. Tope là !"
@@ -42560,45 +43187,45 @@ msgstr "Plus rien à faire. Tope là !"
msgid "Todos|Pipelines"
msgstr "Pipelines"
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr "Revue demandée"
-msgid "Todos|The pipeline failed in"
-msgstr ""
+msgid "Todos|The pipeline failed"
+msgstr "Le pipeline a échoué"
msgid "Todos|Undo mark all as done"
msgstr "Annuler Tout marquer comme terminé"
msgid "Todos|When an issue or merge request is assigned to you, or when you receive a %{strongStart}@mention%{strongEnd} in a comment, this automatically triggers a new item in your To-Do List."
-msgstr "Quand un ticket ou une demande de fusion vous est assigné, ou quand vous recevez une %{strongStart}@mention%{strongEnd} dans un commentaire, cela génère automatiquement un nouvel élément sur votre Liste de Tâches."
+msgstr "Quand un ticket ou une demande de fusion vous est assigné, ou quand vous recevez une %{strongStart}@mention%{strongEnd} dans un commentaire, cela génère automatiquement un nouveau pense-bête."
msgid "Todos|You're all done!"
msgstr "Vous avez terminé !"
msgid "Todos|Your To-Do List shows what to work on next"
-msgstr "Votre Liste de Tâches montre sur quoi travailler ensuite"
+msgstr "Vos pense-bêtes vous montrent sur quoi travailler ensuite"
-msgid "Todos|added a todo for"
-msgstr ""
+msgid "Todos|added a to-do item"
+msgstr "a ajouté un pense-bête"
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
+msgstr "a mentionné %{who}"
+
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|set %{who} as an approver"
msgstr ""
msgid "Todos|yourself"
msgstr "vous-même"
-msgid "Todo|at %{todo_parent_path}"
-msgstr ""
-
msgid "Toggle GitLab Next"
msgstr "Activer/désactiver GitLab Next"
@@ -42621,10 +43248,10 @@ msgid "Toggle commit description"
msgstr "Afficher ou masquer la description du commit"
msgid "Toggle commit list"
-msgstr ""
+msgstr "Afficher/masquer la liste des validations"
msgid "Toggle emoji award"
-msgstr ""
+msgstr "Afficher/masquer les émojis de récompense"
msgid "Toggle focus mode"
msgstr "Activer/désactiver le mode focus"
@@ -42747,13 +43374,13 @@ msgid "Total Contributions"
msgstr "Total des contributions"
msgid "Total Score"
-msgstr ""
+msgstr "Note totale"
msgid "Total cores (CPUs)"
msgstr "Nombre de cœurs (CPUs)"
msgid "Total issue weight"
-msgstr ""
+msgstr "Poids de ticket total"
msgid "Total memory (GB)"
msgstr "Mémoire totale (Go)"
@@ -42965,7 +43592,13 @@ msgid "Trigger was successfully updated."
msgstr "Le déclencheur a été mis à jour avec succès."
msgid "Triggerer"
-msgstr ""
+msgstr "Déclenché par"
+
+msgid "Trigger|Description"
+msgstr "Description"
+
+msgid "Trigger|Trigger description"
+msgstr "Description du déclencheur"
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr "L’utilisateur du déclencheur n’a pas les autorisations suffisantes sur ce projet"
@@ -42974,7 +43607,7 @@ msgid "Trigger|invalid"
msgstr "invalide"
msgid "Trusted"
-msgstr ""
+msgstr "De confiance"
msgid "Trusted applications are automatically authorized on GitLab OAuth flow. It's highly recommended for the security of users that trusted applications have the confidential setting set to true."
msgstr "Les applications de confiance sont automatiquement autorisées sur le flux GitLab OAuth. Il est fortement recommandé pour la sécurité des utilisateurs que les applications approuvées aient le paramètre confidentiel défini sur vrai."
@@ -43003,6 +43636,9 @@ msgstr "Essayez de vous connecter en utilisant votre nom d'utilisateur ou votre
msgid "Try out GitLab Pipelines"
msgstr "Essayez les Pipelines GitLab"
+msgid "Try out the new Web IDE"
+msgstr "Essayez le nouvel EDI Web"
+
msgid "Try the troubleshooting steps here."
msgstr "Essayez les étapes de dépannage ici."
@@ -43013,7 +43649,7 @@ msgid "Try to keep the first line under 52 characters and the others under 72."
msgstr "Essayez de garder la première ligne en dessous de 52 caractères et les autres en dessous de 72."
msgid "Try using a different search term to find the file you are looking for."
-msgstr ""
+msgstr "Essayez d’utiliser un terme de recherche différent pour trouver le fichier que vous recherchez."
msgid "Trying to communicate with your device. Plug it in (if needed) and press the button on the device now."
msgstr "Tentative de communication avec votre appareil. Branchez-le (si nécessaire) et appuyez sur le bouton de l'appareil maintenant."
@@ -43061,7 +43697,7 @@ msgid "Two-factor authentication disabled"
msgstr "Authentification à deux facteurs désactivée"
msgid "Two-factor authentication grace period"
-msgstr ""
+msgstr "Délai de grâce de l'authentification à deux facteurs"
msgid "Two-factor authentication has been disabled for this user"
msgstr "L'authentification à deux facteurs a été désactivée pour cet utilisateur"
@@ -43136,7 +43772,7 @@ msgid "URL of the external storage to serve the repository static objects."
msgstr "URL du stockage externe pour desservir les objets statiques du dépôt."
msgid "URL or request ID"
-msgstr ""
+msgstr "URL ou ID de requête"
msgid "USER %{user_name} WILL BE REMOVED! Are you sure?"
msgstr "L'UTILISATEUR %{user_name} SERA SUPPRIMÉ ! Êtes-vous sûr(e) ?"
@@ -43154,7 +43790,7 @@ msgid "Unable to apply suggestions to a deleted line."
msgstr "Impossible d'appliquer les suggestions à une ligne supprimée."
msgid "Unable to build Slack link."
-msgstr ""
+msgstr "Impossible de construire le lien Slack."
msgid "Unable to collect CPU info"
msgstr "Impossible de collecter les informations sur le CPU"
@@ -43180,8 +43816,14 @@ msgstr "Impossible de récupérer la liste des branches de ce projet."
msgid "Unable to fetch branches list, please close the form and try again"
msgstr "Impossible de récupérer la liste des branches, veuillez fermer le formulaire et réessayer"
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr "Impossible de récupérer le groupe. Rechargez la page pour réessayer."
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr "Impossible de récupérer les groupes. Rechargez la page pour réessayer."
+
msgid "Unable to fetch upstream and downstream pipelines."
-msgstr ""
+msgstr "Impossible de récupérer les pipelines en amont ni ceux en aval."
msgid "Unable to find Jira project to import data from."
msgstr "Impossible de trouver le projet Jira à partir duquel importer les données."
@@ -43256,7 +43898,7 @@ msgid "Unapproved the current merge request."
msgstr "Demande de fusion actuelle désapprouvée."
msgid "Unarchive project"
-msgstr ""
+msgstr "Désarchiver le projet"
msgid "Unarchiving the project restores its members' ability to make commits, and create issues, comments, and other entities. %{strong_start}After you unarchive the project, it displays in the search and on the dashboard.%{strong_end} %{link_start}Learn more.%{link_end}"
msgstr "Le désarchivage du projet redonnera à ses membres la possibilité d'y faire des commits et d'y créer des tickets, commentaires ou autres entités. %{strong_start}Une fois le projet désarchivé, il apparaîtra dans les recherches et sur le tableau de bord.%{strong_end} %{link_start}En savoir plus.%{link_end}"
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr "Non satisfait ?"
+msgid "Units|d"
+msgstr "j"
+
msgid "Units|ms"
msgstr "ms"
@@ -43313,16 +43958,16 @@ msgid "Unknown"
msgstr "Inconnu"
msgid "Unknown Error"
-msgstr ""
+msgstr "Erreur inconnue"
msgid "Unknown encryption strategy: %{encrypted_strategy}!"
msgstr "Stratégie de chiffrement inconnue : %{encrypted_strategy} !"
msgid "Unknown format"
-msgstr ""
+msgstr "Format inconnu"
msgid "Unknown response text"
-msgstr ""
+msgstr "Texte de réponse inconnu"
msgid "Unknown user"
msgstr "Utilisateur inconnu"
@@ -43346,7 +43991,7 @@ msgid "Unlock merge request"
msgstr "Déverrouiller la demande de fusion"
msgid "Unlock more features with GitLab Ultimate"
-msgstr ""
+msgstr "Déverrouiller plus de fonctionnalités avec GitLab Ultimate"
msgid "Unlock the discussion"
msgstr "Déverrouiller la discussion"
@@ -43367,7 +44012,7 @@ msgid "Unreachable"
msgstr "Injoignable"
msgid "Unrecognized approval status."
-msgstr ""
+msgstr "État d'approbation non reconnu."
msgid "Unrecognized cluster type"
msgstr "Type de grappe de serveurs non reconnu"
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr "Déprogrammer la tâche"
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr "Désélectionnez « Développer la référence de la variable » si vous voulez utiliser la valeur de la variable comme une chaîne brute."
+
msgid "Unstar"
msgstr "Supprimer des favoris"
@@ -43412,7 +44060,7 @@ msgid "Unsupported sort value."
msgstr "Valeur de tri non prise en charge."
msgid "Unsupported todo type passed. Supported todo types are: %{todo_types}"
-msgstr "Type « À faire » non pris en charge. Les types « À faire » supportés sont : %{todo_types}"
+msgstr "Type de pense-bête non pris en charge. Les types de pense-bêtes pris en charge sont : %{todo_types}"
msgid "Unused"
msgstr ""
@@ -43436,7 +44084,7 @@ msgid "Update"
msgstr "Mettre à jour"
msgid "Update %{sourcePath} file"
-msgstr ""
+msgstr "Mise à jour du fichier %{sourcePath}"
msgid "Update Now"
msgstr "Mettre à jour maintenant"
@@ -43460,7 +44108,7 @@ msgid "Update broadcast message"
msgstr "Mettre à jour le message de diffusion"
msgid "Update failed"
-msgstr ""
+msgstr "La mise à jour a échoué"
msgid "Update it"
msgstr "Mettez-la à jour"
@@ -43478,7 +44126,7 @@ msgid "Update variable"
msgstr "Mettre à jour la variable"
msgid "Update your bookmarked URLs as filtered/sorted branches URL has been changed."
-msgstr ""
+msgstr "Mettez à jour vos marque-pages car les URLs des branches filtrées/triées ont été modifiées."
msgid "Update your group name, description, avatar, and visibility."
msgstr "Modifiez le nom du groupe, sa description, son avatar et sa visibilité."
@@ -43487,7 +44135,7 @@ msgid "Update your project name, topics, description, and avatar."
msgstr "Mettre à jour le nom, les sujets, la description et l'avatar de votre projet."
msgid "UpdateProject|Cannot rename project because it contains container registry tags!"
-msgstr ""
+msgstr "Impossible de renommer le projet car il contient des étiquettes dans le registre de conteneurs !"
msgid "UpdateProject|Could not set the default branch"
msgstr "Impossible de définir la branche par défaut"
@@ -43538,10 +44186,10 @@ msgid "Upload New File"
msgstr "Téléverser un nouveau fichier"
msgid "Upload a certificate for your domain with all intermediates"
-msgstr ""
+msgstr "Téléversez un certificat pour votre domaine avec tous les intermédiaires"
msgid "Upload a private key for your certificate"
-msgstr ""
+msgstr "Téléversez une clé privée pour votre certificat"
msgid "Upload could not be deleted."
msgstr "Le téléversement n'a pas pu être supprimé."
@@ -43556,7 +44204,7 @@ msgid "Upload new file"
msgstr "Téléverser un nouveau fichier"
msgid "Upload object map"
-msgstr ""
+msgstr "Téléversez la liste des objets"
msgid "Uploaded date"
msgstr "Date de téléversement"
@@ -43567,6 +44215,9 @@ msgstr "Téléversement des modifications vers le terminal"
msgid "Uploading..."
msgstr "Téléversement en cours..."
+msgid "Uploads"
+msgstr "Téléversements"
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr "Les statistiques de stockage au niveau du projet pour le Registre de Con
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr "Cette statistique de stockage au niveau du projet n'inclut pas les économies réalisées par la déduplication à l'échelle du site et n'est pas utilisée pour calculer le stockage total de l'espace de noms."
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr "Les %{help_link_start}exécuteurs partagés%{help_link_end} sont désactivés. Aucune limite d'utilisation du pipeline n'est donc définie"
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr "Les %{linkStart}Exécuteurs Partagés%{linkEnd} sont désactivés. Aucune limite d'utilisation du pipeline n'est donc définie"
msgid "UsageQuota|%{linkTitle} help link"
msgstr "lien d'aide %{linkTitle}"
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr "%{storage_limit_link_start}Une limite de stockage d'espace de noms%{link_end} sera bientôt imposée pour l'espace de noms %{strong_start}%{namespace_name}%{strong_end}. %{extra_message}"
@@ -43624,9 +44269,6 @@ msgstr "Utilisation des minutes CI par projet"
msgid "UsageQuota|CI/CD minutes usage"
msgstr "Utilisation des minutes CI/CD"
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr "Utilisation des minutes CI/CD depuis le %{timeElapsed}"
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr "Utilisation des minutes CI/CD depuis %{usageSince}"
@@ -43636,9 +44278,6 @@ msgstr "Paquets de code et images de conteneur."
msgid "UsageQuota|Container Registry"
msgstr "Registre de Conteneur"
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr "Proxy de dépendance"
@@ -43682,7 +44321,7 @@ msgid "UsageQuota|Local proxy used for frequently-accessed upstream Docker image
msgstr "Proxy local utilisé pour les images Docker en amont auxquelles les accès sont fréquents. %{linkStart}En savoir plus%{linkEnd}"
msgid "UsageQuota|Namespace storage used"
-msgstr ""
+msgstr "Stockage utilisé par les espaces de noms"
msgid "UsageQuota|No CI minutes usage data available."
msgstr "Aucune donnée d'utilisation des minutes CI disponible."
@@ -43730,7 +44369,7 @@ msgid "UsageQuota|Shared bits of code and text."
msgstr "Bouts de code et de texte partagés."
msgid "UsageQuota|Shared runner duration"
-msgstr ""
+msgstr "Durée des exécuteurs partagés"
msgid "UsageQuota|Snippets"
msgstr "Extraits"
@@ -43765,11 +44404,8 @@ msgstr "L'espace de noms utilise actuellement %{strong_start}%{used_storage}%{st
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr "L'espace de noms utilise actuellement %{strong_start}%{used_storage}%{strong_end} du stockage de l'espace de noms. Affichez et gérez votre utilisation depuis les %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}En savoir plus%{link_end} sur comment diminuer votre stockage."
-msgid "UsageQuota|The table below shows current period usage"
-msgstr "Le tableau ci-dessous montre l'utilisation sur la période actuelle"
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
-msgstr "Le tableau ci-dessous montre l'utilisation depuis %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
+msgstr "Le tableau ci-dessous montre l'utilisation depuis %{usageSince}"
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
msgstr "Il s'agit de la quantité totale de stockage utilisée par tous vos projets dans cet espace de noms."
@@ -43777,12 +44413,6 @@ msgstr "Il s'agit de la quantité totale de stockage utilisée par tous vos proj
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr "Il s'agit de la quantité totale de stockage utilisée par les projets au-dessus de la limite de stockage gratuit de %{actualRepositorySizeLimit}."
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr "Cet espace de noms contient des projets verrouillés"
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr "Cet espace de noms n'a aucun projet utilisant des exécuteurs partagés"
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr "Aucun projet de cet espace de noms n'a utilisé d'exécuteurs partagés sur la période actuelle"
@@ -43795,9 +44425,6 @@ msgstr "Quantité totale de stockage excédentaire utilisée"
msgid "UsageQuota|Total namespace storage used"
msgstr "Quantité totale de stockage utilisé pour l'espace de noms"
-msgid "UsageQuota|Unlimited"
-msgstr "Illimité"
-
msgid "UsageQuota|Uploads"
msgstr "Téléversements"
@@ -43831,8 +44458,8 @@ msgstr "Lien d'aide sur les quotas d'utilisation"
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr "Paramètres utilisateur &gt; Quotas d'utilisation"
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
-msgstr "Dès le moment où vous achetez de l'espace de stockage supplémentaire, nous débloquons automatiquement les projets qui ont été verrouillés lorsque vous avez atteint la limite de %{actualRepositorySizeLimit}."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
+msgstr ""
msgid "UsageQuota|Wiki"
msgstr "Wiki"
@@ -43840,17 +44467,17 @@ msgstr "Wiki"
msgid "UsageQuota|Wiki content."
msgstr "Contenu Wiki."
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
-msgstr "Vous avez utilisé la totalité de votre stockage supplémentaire, veuillez en acheter davantage pour déverrouiller vos projets au-delà de la limite gratuite de %{actualRepositorySizeLimit}."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
+msgstr "Vous avez utilisé la totalité de votre stockage supplémentaire. Achetez-en davantage pour déverrouiller les projets dépassant la limite."
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
-msgstr "Vous avez atteint la limite de stockage gratuit de %{actualRepositorySizeLimit} sur %{projectsLockedText}. Pour les déverrouiller, veuillez acheter de l'espace de stockage supplémentaire."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
+msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr "Vous avez utilisé : %{usage} %{limit}"
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
-msgstr "Le stockage que vous avez acheté arrive bientôt à saturation. Pour éviter le verrouillage des projets, veuillez en acquérir davantage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
+msgstr "Le stockage que vous avez acheté est presque épuisé. Pour éviter le verrouillage des projets, achetez-en davantage."
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
msgstr "sur %{formattedLimit} du stockage de votre espace de noms"
@@ -43973,7 +44600,7 @@ msgid "Use cURL"
msgstr "Utiliser cURL"
msgid "Use custom color #FF0000"
-msgstr ""
+msgstr "Utilisez la couleur personnalisée #FF0000"
msgid "Use double quotes for multiple keywords, such as %{code_open}\"your search\"%{code_close}"
msgstr "Utilisez des guillemets doubles s'il y a plusieurs mots-clés, comme %{code_open}\"votre recherche\"%{code_close}"
@@ -44023,17 +44650,20 @@ msgstr "Utilisez l'URL de l'instance Cloud publique (%{kroki_public_url}) ou %{i
msgid "Use the search bar on the top of this page"
msgstr "Utilisez la barre de recherche en haut de cette page"
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr "Utilisez ce formulaire pour signaler à l'administrateur les utilisateurs qui créent des tickets ou des commentaires indésirables, ou qui se comportent de manière inappropriée."
+
msgid "Use this token to validate received payloads."
msgstr "Utiliser ce jeton pour valider les charges utiles reçues."
msgid "Use webhook"
-msgstr "Utiliser un point d'ancrage web"
+msgstr "Utiliser un crochet web"
msgid "Use your global notification setting"
msgstr "Utiliser vos paramètres de notification globaux"
msgid "Use your smart card to authenticate with the LDAP server."
-msgstr ""
+msgstr "Utilisez votre carte à puce pour vous authentifier auprès du serveur LDAP."
msgid "Used"
msgstr ""
@@ -44104,7 +44734,7 @@ msgid "User is blocked"
msgstr "L'utilisateur est bloqué"
msgid "User is not allowed to resolve thread"
-msgstr ""
+msgstr "L'utilisateur n'est pas autorisé à résoudre le fil de discussion"
msgid "User key"
msgstr "Clé d'utilisateur"
@@ -44260,7 +44890,7 @@ msgid "UserProfile|Explore public groups to find projects to contribute to."
msgstr "Explorez les groupes publics pour trouver des projets pour lesquels contribuer."
msgid "UserProfile|Followers"
-msgstr "Abonnés"
+msgstr "Suiveurs"
msgid "UserProfile|Following"
msgstr "Suivi"
@@ -44289,29 +44919,26 @@ msgstr "Projets personnels"
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr "Signaler un abus"
-
msgid "UserProfile|Retry"
msgstr "Réessayer"
msgid "UserProfile|Snippets"
-msgstr "Fragments de code"
+msgstr "Extraits de code"
msgid "UserProfile|Snippets in GitLab can either be private, internal, or public."
msgstr "Les extraits dans GitLab peuvent être soit privés, internes ou publics."
msgid "UserProfile|Star projects to track their progress and show your appreciation."
-msgstr ""
+msgstr "Mettez une étoile sur des projets pour suivre leur progression et montrer que vous les appréciez."
msgid "UserProfile|Starred projects"
-msgstr "Projets favoris"
+msgstr "Projets étoilés"
msgid "UserProfile|Subscribe"
msgstr "S’abonner"
msgid "UserProfile|This user doesn't have any followers."
-msgstr "Cet utilisateur n'a aucun abonné."
+msgstr "Cet utilisateur n'a aucun suiveur."
msgid "UserProfile|This user doesn't have any personal projects"
msgstr "Cet utilisateur ne dispose pas de projets personnels"
@@ -44323,7 +44950,7 @@ msgid "UserProfile|This user hasn't contributed to any projects"
msgstr "Cet utilisateur n’a contribué à aucun projet"
msgid "UserProfile|This user hasn't starred any projects"
-msgstr "Cet utilisateur n'a mis aucun projet en favoris"
+msgstr "Cet utilisateur n'a mis d'étoile à aucun projet"
msgid "UserProfile|This user is blocked"
msgstr "Cet utilisateur est bloqué"
@@ -44347,10 +44974,10 @@ msgid "UserProfile|You are not following other users."
msgstr "Vous ne suivez pas d'autres utilisateurs."
msgid "UserProfile|You can create a group for several dependent projects."
-msgstr ""
+msgstr "Vous pouvez créer un groupe pour plusieurs projets interdépendants."
msgid "UserProfile|You do not have any followers."
-msgstr "Vous n'avez aucun abonné."
+msgstr "Vous n'avez aucun suiveur."
msgid "UserProfile|You haven't created any personal projects."
msgstr "Vous n'avez créé aucun projet personnel."
@@ -44389,7 +45016,7 @@ msgid "Username: %{username}"
msgstr "Nom d'utilisateur : %{username}"
msgid "Users"
-msgstr "Utilisateurs et utilisatrices"
+msgstr "Utilisateurs"
msgid "Users API rate limit"
msgstr ""
@@ -44428,10 +45055,10 @@ msgid "Users with a Guest role or those who don't belong to a Project or Group w
msgstr "Les utilisateurs ayant un rôle d'Invité et ceux qui ne font pas partie d'un Projet ou d'un Groupe n'utiliseront pas de licence de siège."
msgid "UsersSelect|%{name} + %{length} more"
-msgstr ""
+msgstr "%{name} + %{length} de plus"
msgid "UsersSelect|Any User"
-msgstr ""
+msgstr "Tout utilisateur"
msgid "UsersSelect|Assignee"
msgstr "Assigné"
@@ -44476,13 +45103,16 @@ msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
-msgstr ""
+msgstr "Utilisation de la stratégie de chiffrement requise alors que le champ chiffré est manquant !"
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr "L'utilisation du mot-clé %{codeStart}needs%{codeEnd} (nécessité) permet aux tâches d'être exécutées avant que leur étape ne soit atteinte. Les tâches s'exécutent dès que leurs relations avec les %{codeStart}needs%{codeEnd} sont satisfaites, ce qui accélère vos pipelines."
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr "Code VS dans votre navigateur. Affichez le code et apportez des modifications à partir de la même interface utilisateur que celle de votre EDI local 🎉"
+
msgid "Valid From"
-msgstr ""
+msgstr "Valide du"
msgid "Validate"
msgstr "Valider"
@@ -44491,16 +45121,16 @@ msgid "Validate your GitLab CI configuration"
msgstr "Valider votre configuration GitLab CI"
msgid "Validate your GitLab CI configuration file"
-msgstr ""
+msgstr "Validez votre fichier de configuration GitLab CI"
msgid "Validated at"
-msgstr ""
+msgstr "Validé le"
msgid "Validated at:"
-msgstr ""
+msgstr "Validé le :"
msgid "Validated:"
-msgstr ""
+msgstr "Validé :"
msgid "Validations failed."
msgstr "Les validations ont échoué."
@@ -44553,6 +45183,9 @@ msgstr "Métriques DORA"
msgid "ValueStreamAnalytics|Dashboard"
msgstr "Tableau de bord"
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr "Modifier la Chaîne de Valeur : %{name}"
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr "Aller à la doc"
@@ -44571,6 +45204,9 @@ msgstr "Médiane de la durée de la création du ticket à sa fermeture."
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr "Nouvelle Chaîne de Valeur"
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr "Nombre de commits poussés vers la branche par défaut"
@@ -44619,12 +45255,12 @@ msgstr "Arrêter"
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr "La Chaîne de Valeur par Défaut ne peut pas être supprimée"
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
-msgstr "Les valeurs qui contiennent le caractère %{codeStart}$%{codeEnd} peuvent être considérées comme une référence à une variable et développées. %{docsLinkStart}En savoir plus.%{docsLinkEnd}"
-
msgid "Variable"
msgstr "Variable"
+msgid "Variable value will be evaluated as raw string."
+msgstr "La valeur de la variable sera évaluée comme une chaîne brute."
+
msgid "Variable will be masked in job logs."
msgstr "La variable sera masquée dans les journaux des tâches."
@@ -44634,8 +45270,8 @@ msgstr "Variables"
msgid "Variables can be:"
msgstr "Les variables peuvent être :"
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
-msgstr "Les variables stockent des informations, telles que les mots de passe et les clés secrètes, que vous pouvez utiliser dans les scripts de tâche."
+msgid "Variables can have several attributes."
+msgstr "Les variables peuvent avoir plusieurs attributs."
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
msgstr "Les variables stockent des informations, telles que les mots de passe et les clés secrètes, que vous pouvez utiliser dans les scripts de tâche. Tous les projets de l'instance peuvent les utiliser."
@@ -44650,7 +45286,7 @@ msgid "Various settings that affect GitLab performance."
msgstr "Divers paramètres qui affectent les performances de GitLab."
msgid "Verification status"
-msgstr ""
+msgstr "État de la vérification"
msgid "VerificationReminder|Pipeline failing? To keep GitLab spam and abuse free we ask that you verify your identity."
msgstr "Pipeline en échec ? Pour garder GitLab à l'abri des pourriels et des abus nous vous demandons de vérifier votre identité."
@@ -44691,6 +45327,21 @@ msgstr "Version %{versionNumber}"
msgid "Version %{versionNumber} (latest)"
msgstr "Version %{versionNumber} (dernière)"
+msgid "VersionCheck|%{details}"
+msgstr "%{details}"
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr "Mise à jour de sécurité critique disponible"
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr "En savoir plus sur cette version de sécurité critique."
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr "Me le rappeler dans 3 jours"
+
msgid "VersionCheck|Up to date"
msgstr "À jour"
@@ -44700,6 +45351,18 @@ msgstr "Mettre à jour dès que possible"
msgid "VersionCheck|Update available"
msgstr "Mise à jour disponible"
+msgid "VersionCheck|Upgrade now"
+msgstr "Mettre à niveau maintenant"
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr "Vous êtes actuellement sur la version %{currentVersion}. Nous vous recommandons fortement de mettre à jour votre installation de GitLab. %{link}"
+
msgid "VersionCheck|Your GitLab Version"
msgstr "Votre Version de GitLab"
@@ -44707,7 +45370,7 @@ msgid "View Stage: %{title}"
msgstr "Vue de l'Étape : %{title}"
msgid "View alert details at"
-msgstr ""
+msgstr "Voir les détails de l'alerte sur"
msgid "View alert details."
msgstr "Afficher les détails de l'alerte."
@@ -44725,10 +45388,10 @@ msgid "View all projects"
msgstr "Voir tous les projets"
msgid "View blame"
-msgstr ""
+msgstr "Voir les annotations"
msgid "View blame prior to this change"
-msgstr "Voir les informations « blame » antérieures à cette modification"
+msgstr "Voir les annotations antérieures à cette modification"
msgid "View card matches"
msgstr ""
@@ -44757,18 +45420,18 @@ msgid "View eligible approvers"
msgstr "Voir les approbateurs éligibles"
msgid "View entire blame"
-msgstr ""
+msgstr "Voir toutes les annotations"
msgid "View exposed artifact"
msgid_plural "View %d exposed artifacts"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "Voir l'artéfact exposé"
+msgstr[1] "Voir les %d artéfacts exposés"
msgid "View file @ "
msgstr "Voir le fichier @ "
msgid "View file @ %{commitSha}"
-msgstr ""
+msgstr "Voir le fichier @ %{commitSha}"
msgid "View full dashboard"
msgstr "Voir le tableau de bord complet"
@@ -44780,10 +45443,10 @@ msgid "View group labels"
msgstr "Afficher les labels de groupe"
msgid "View group pipeline usage quota"
-msgstr ""
+msgstr "Voir le quota d'utilisation des pipelines du groupe"
msgid "View incident details at"
-msgstr ""
+msgstr "Voir les détails de l'incident sur"
msgid "View incident details."
msgstr "Voir les détails de l'incident."
@@ -44792,16 +45455,16 @@ msgid "View incident issues."
msgstr "Voir les tickets d'incident."
msgid "View issue"
-msgstr ""
+msgstr "Voir le ticket"
msgid "View issues"
-msgstr ""
+msgstr "Voir les tickets"
msgid "View it on GitLab"
msgstr "Voir sur GitLab"
msgid "View job"
-msgstr ""
+msgstr "Voir la tâche"
msgid "View job log"
msgstr "Voir le journal des tâches"
@@ -44831,7 +45494,7 @@ msgid "View page @ "
msgstr "Voir la page @ "
msgid "View performance dashboard."
-msgstr ""
+msgstr "Voir le tableau de bord des performances."
msgid "View project"
msgstr "Voir le projet"
@@ -44866,7 +45529,7 @@ msgid "View the latest successful deployment to this environment"
msgstr "Voir le dernier déploiement réussi dans cet environnement"
msgid "View the performance dashboard at"
-msgstr ""
+msgstr "Voir le tableau de bord des performances sur"
msgid "View usage details"
msgstr "Afficher les détails d'utilisation"
@@ -44875,16 +45538,16 @@ msgid "View users statistics"
msgstr "Voir les statistiques des utilisateurs"
msgid "Viewed"
-msgstr ""
+msgstr "Vu"
msgid "Viewing commit"
-msgstr ""
+msgstr "Affichage du commit"
msgid "Viewing projects and designs data from a primary site is not possible when using a unified URL. Visit the secondary site directly. %{geo_help_url}"
-msgstr "L'affichage des projets et des données de conception depuis un site principal n'est pas possible quand une URL unifiée est utilisée. Consultez le site secondaire directement. %{geo_help_url}"
+msgstr "L'affichage des projets et des données des esquisses depuis un site principal n'est pas possible quand une URL unifiée est utilisée. Consultez le site secondaire directement. %{geo_help_url}"
msgid "Violation"
-msgstr ""
+msgstr "Violation"
msgid "Visibility"
msgstr "Visibilité"
@@ -44929,7 +45592,7 @@ msgid "Vulnerabilities"
msgstr "Vulnérabilités"
msgid "Vulnerabilities over time"
-msgstr ""
+msgstr "Vulnérabilités au cours du temps"
msgid "Vulnerability"
msgstr "Vulnérabilité"
@@ -45007,7 +45670,7 @@ msgid "VulnerabilityManagement|Fetching linked Jira issues"
msgstr "Récupération des tickets Jira liés"
msgid "VulnerabilityManagement|Identifier code and URL are required fields"
-msgstr ""
+msgstr "Le code identifiant et l'URL sont des champs requis"
msgid "VulnerabilityManagement|Manually add a vulnerability entry into the vulnerability report."
msgstr "Ajoutez manuellement une entrée de vulnérabilité dans le rapport de vulnérabilité."
@@ -45019,7 +45682,7 @@ msgid "VulnerabilityManagement|Needs triage"
msgstr ""
msgid "VulnerabilityManagement|Read more about related issues"
-msgstr ""
+msgstr "En savoir plus sur les tickets associés"
msgid "VulnerabilityManagement|Related Jira issues"
msgstr ""
@@ -45211,7 +45874,7 @@ msgid "Vulnerability|Method"
msgstr "Méthode"
msgid "Vulnerability|Namespace"
-msgstr ""
+msgstr "Espace de noms"
msgid "Vulnerability|Project"
msgstr "Projet"
@@ -45298,7 +45961,7 @@ msgid "Waiting for merge (open and assigned)"
msgstr "En attente de fusion (ouvertes et assignées)"
msgid "Waiting for performance data"
-msgstr ""
+msgstr "En attente des données de performance"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "Vous voulez voir les données ? Merci de contacter un administrateur pour en obtenir l’accès."
@@ -45337,10 +46000,10 @@ msgid "We couldn't find any %{scope} matching %{term}"
msgstr "Nous n'avons pas trouvé de %{scope} correspondant à %{term}"
msgid "We couldn't find any %{scope} matching %{term} in group %{group}"
-msgstr ""
+msgstr "Nous n'avons pas trouvé de %{scope} correspondant à %{term} dans le groupe %{group}"
msgid "We couldn't find any %{scope} matching %{term} in project %{project}"
-msgstr ""
+msgstr "Nous n'avons pas trouvé de %{scope} correspondant à %{term} dans le projet %{project}"
msgid "We couldn't reach the Prometheus server. Either the server no longer exists or the configuration details need updating."
msgstr "Nous n'avons pas pu atteindre le serveur Prometheus. Soit le serveur n'existe plus, soit les détails de la configuration doivent être mis à jour."
@@ -45360,6 +46023,9 @@ msgstr "Nous avons détecté un potentiel indésirable dans %{humanized_resource
msgid "We don't have enough data to show this stage."
msgstr "Nous n’avons pas suffisamment de données pour afficher cette étape."
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr "Nous avons trouvé votre jeton dans un projet public et l'avons automatiquement révoqué pour protéger votre compte."
+
msgid "We have found the following errors:"
msgstr "Nous avons trouvé les erreurs suivantes :"
@@ -45370,7 +46036,7 @@ msgid "We invite you to %{featureLinkStart}request a feature%{featureLinkEnd}, %
msgstr "Nous vous invitons à %{featureLinkStart}faire une demande de fonctionnalité%{featureLinkEnd}, %{bugLinkStart}signaler un bogue%{bugLinkEnd} ou %{feedbackLinkStart}partager vos réactions%{feedbackLinkEnd}"
msgid "We recommend a work email address."
-msgstr ""
+msgstr "Nous recommandons une adresse de courriel professionnelle."
msgid "We recommend leaving all SAST analyzers enabled"
msgstr "Nous vous recommandons de laisser tous les analyseurs SAST activés"
@@ -45435,9 +46101,6 @@ msgstr "Appareils WebAuthn (%{length})"
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr "WebAuthn ne fonctionne qu'avec les sites Web accessibles en HTTPS. Contactez votre administrateur pour plus de détails."
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr "Créer un projet divergent"
@@ -45453,24 +46116,12 @@ msgstr "Modifier rapidement et facilement plusieurs fichiers de votre projet."
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr "Modifier rapidement et facilement plusieurs fichiers de votre projet. Appuyez sur . pour ouvrir\n"
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr "Une erreur s’est produite lors de la mise à jour des préférences de l’utilisateur. Veuillez consulter la console développeur pour plus de détails."
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr "Ce projet n’accepte pas les commits non signés."
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr "Ce projet n'accepte pas les commits non signés. Vous ne pouvez pas valider de modifications dans l'EDI Web."
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr "Vous ne pouvez pas directement modifier les fichiers de ce projet. Créez une divergence, puis proposez une demande de fusion avec vos modifications."
@@ -45490,7 +46141,7 @@ msgid "WebexTeamsService|Webex Teams"
msgstr "Webex Teams"
msgid "Webhook"
-msgstr "Webhook"
+msgstr "Crochet web"
msgid "Webhook Logs"
msgstr "Journaux Webhook"
@@ -45501,14 +46152,26 @@ msgstr "Paramètres Webhook"
msgid "Webhook events will be displayed here."
msgstr "Les événements de crochet web seront affichés ici."
+msgid "Webhook was created"
+msgstr "Le crochet web a été créé"
+
+msgid "Webhook was deleted"
+msgstr "Le crochet web a été supprimé"
+
+msgid "Webhook was scheduled for deletion"
+msgstr "Le crochet web a été planifié pour suppression"
+
+msgid "Webhook was updated"
+msgstr "Le crochet web a été mis à jour"
+
msgid "Webhook:"
-msgstr "Point d'ancrage web :"
+msgstr "Crochet web :"
msgid "Webhooks"
msgstr "Crochets web"
msgid "Webhooks Help"
-msgstr "Aide sur les Webhooks"
+msgstr "Aide sur les crochets web"
msgid "Webhooks|+ Mask another portion of URL"
msgstr "+ Masquer une autre partie de l'URL"
@@ -45565,10 +46228,7 @@ msgid "Webhooks|Are you sure you want to delete this project hook?"
msgstr "Êtes-vous sûr(e) de vouloir supprimer ce crochet de projet ?"
msgid "Webhooks|Are you sure you want to delete this webhook?"
-msgstr "Êtes-vous sûr(e) de vouloir supprimer ce webhook ?"
-
-msgid "Webhooks|Comments"
-msgstr "Commentaires"
+msgstr "Êtes-vous sûr(e) de vouloir supprimer ce crochet web ?"
msgid "Webhooks|Confidential comments"
msgstr "Commentaires confidentiels"
@@ -45577,7 +46237,7 @@ msgid "Webhooks|Confidential issues events"
msgstr "Événements de tickets confidentiels"
msgid "Webhooks|Delete webhook"
-msgstr "Supprimer le webhook"
+msgstr "Supprimer le crochet web"
msgid "Webhooks|Deployment events"
msgstr "Événements de déploiement"
@@ -45592,7 +46252,7 @@ msgid "Webhooks|Failed to connect"
msgstr "Échec de la connexion"
msgid "Webhooks|Fails to connect"
-msgstr ""
+msgstr "Échoue à se connecter"
msgid "Webhooks|Feature flag events"
msgstr "Événements d'indicateur de fonctionnalité"
@@ -45618,20 +46278,17 @@ msgstr "Événements des membres"
msgid "Webhooks|Merge request events"
msgstr "Événements de demande de fusion"
+msgid "Webhooks|Must match part of URL"
+msgstr ""
+
msgid "Webhooks|Pipeline events"
msgstr "Événements des pipelines"
-msgid "Webhooks|Push events"
-msgstr "Événements de poussées"
-
-msgid "Webhooks|Push to the repository."
-msgstr "Poussée vers le dépôt."
-
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
msgstr "Les expressions rationnelles telles que %{REGEX_CODE} sont prises en charge."
msgid "Webhooks|Regular expression"
-msgstr ""
+msgstr "Expression rationnelle"
msgid "Webhooks|Releases events"
msgstr "Événements de Versions"
@@ -45655,17 +46312,14 @@ msgid "Webhooks|Tag push events"
msgstr "Événements de poussée d'étiquette"
msgid "Webhooks|The webhook %{help_link_start}failed to connect%{help_link_end}, and will retry in %{retry_time}. To re-enable it, check %{strong_start}Recent events%{strong_end} for error details, then test your settings below."
-msgstr "Le point d'ancrage Wab %{help_link_start}n'a pas réussi à se connecter%{help_link_end}, et réessayera dans %{retry_time}. Pour le réactiver, vérifiez les détails de l'erreur dans les %{strong_start}Événements récents%{strong_end} puis testez vos paramètres ci-dessous."
+msgstr "Le crochet Web %{help_link_start}n'a pas réussi à se connecter%{help_link_end}, et réessaiera dans %{retry_time}. Pour le réactiver, vérifiez les détails de l'erreur dans les %{strong_start}Événements récents%{strong_end} puis testez vos paramètres ci-dessous."
msgid "Webhooks|The webhook failed to connect, and is disabled. To re-enable it, check %{strong_start}Recent events%{strong_end} for error details, then test your settings below."
-msgstr "Le point d'ancrage Web n'a pas réussi à se connecter et est désactivé. Pour le réactiver, vérifiez les détails de l'erreur dans les %{strong_start}Événements récents%{strong_end} puis testez vos paramètres ci-dessous."
+msgstr "Le crochet Web n'a pas réussi à se connecter et est désactivé. Pour le réactiver, vérifiez les détails de l'erreur dans les %{strong_start}Événements récents%{strong_end} puis testez vos paramètres ci-dessous."
msgid "Webhooks|Trigger"
msgstr "Déclencheur"
-msgid "Webhooks|URL"
-msgstr "URL"
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr "L'URL doit être en encodage-pourcent si elle contient un ou plusieurs caractères spéciaux."
@@ -45679,13 +46333,13 @@ msgid "Webhooks|Webhook disabled"
msgstr "Webhook désactivé"
msgid "Webhooks|Webhook failed to connect"
-msgstr "Le point d'ancrage Web n'a pas réussi à se connecter"
+msgstr "Le crochet Web n'a pas réussi à se connecter"
msgid "Webhooks|Webhook fails to connect"
-msgstr "Le point d'ancrage Web n'arrive pas à se connecter"
+msgstr "Le crochet Web n'arrive pas à se connecter"
msgid "Webhooks|Webhook rate limit has been reached"
-msgstr ""
+msgstr "La limite de fréquence d'appel de crochet web a été atteinte"
msgid "Webhooks|Webhooks for %{root_namespace} are now disabled because they've been triggered more than %{limit} times per minute. They'll be automatically re-enabled in the next minute."
msgstr "Les webhooks de %{root_namespace} sont maintenant désactivés car ils ont été déclenchés plus de %{limit} fois par minute. Ils seront automatiquement réactivés la minute suivante."
@@ -45751,7 +46405,7 @@ msgid "What are project audit events?"
msgstr "Que sont les événements d'audit de projet ?"
msgid "What are some examples?"
-msgstr ""
+msgstr "Voir quelques exemples ?"
msgid "What does the setting affect?"
msgstr "Qu'est-ce que le réglage affecte ?"
@@ -45819,7 +46473,7 @@ msgid "When using the %{code_open}http://%{code_close} or %{code_open}https://%{
msgstr "Lors de l'utilisation des protocoles %{code_open}http://%{code_close} ou %{code_open}https://%{code_close}, veuillez fournir l'URL exacte du dépôt. Les redirections HTTP ne seront pas suivies."
msgid "When you transfer your project to a group, you can easily manage multiple projects, view usage quotas for storage, pipeline minutes, and users, and start a trial or upgrade to a paid tier."
-msgstr ""
+msgstr "Lorsque vous transférez votre projet vers un groupe, vous pouvez facilement gérer plusieurs projets, voir les quotas d'utilisation pour le stockage, les minutes de pipelines, les utilisateurs, et commencer un essai ou la mise à niveau vers une offre payante."
msgid "When your trial ends, you'll move to the Free tier, which has a limit of %{free_user_limit} seat. %{free_user_limit} seat will remain active, and members not occupying a seat will have the %{link_start}Over limit status%{link_end} and lose access to this group."
msgid_plural "When your trial ends, you'll move to the Free tier, which has a limit of %{free_user_limit} seats. %{free_user_limit} seats will remain active, and members not occupying a seat will have the %{link_start}Over limit status%{link_end} and lose access to this group."
@@ -45950,7 +46604,7 @@ msgid "WikiHistoricalPage|This is an old version of this page."
msgstr "Il s’agit d’une ancienne version de la page."
msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}."
-msgstr "Vous pouvez consulter le %{most_recent_link} ou parcourir l’%{history_link}."
+msgstr "Vous pouvez consulter la %{most_recent_link} ou parcourir l’%{history_link}."
msgid "WikiHistoricalPage|history"
msgstr "historique"
@@ -46057,18 +46711,18 @@ msgstr ""
msgid "Will be mapped to"
msgstr "Sera associé à"
-msgid "Will deploy to"
+msgid "Will be released"
msgstr ""
+msgid "Will deploy to"
+msgstr "Sera déployé sur"
+
msgid "Wireframe"
msgstr "Maquette"
msgid "With requirements, you can set criteria to check your products against."
msgstr "Avec les exigences, vous pouvez définir des critères auxquels confronter vos produits."
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr "Retirer la demande d’accès"
@@ -46087,6 +46741,12 @@ msgstr "%{workItemType} supprimé"
msgid "WorkItem|Add"
msgstr "Ajouter"
+msgid "WorkItem|Add %{workItemType}"
+msgstr "Ajouter %{workItemType}"
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr "Ajouter %{workItemType}s"
+
msgid "WorkItem|Add a title"
msgstr "Ajouter un titre"
@@ -46102,9 +46762,6 @@ msgstr "Ajouter une date d'échéance"
msgid "WorkItem|Add start date"
msgstr "Ajouter une date de début"
-msgid "WorkItem|Add task"
-msgstr "Ajouter une tâche"
-
msgid "WorkItem|Add to iteration"
msgstr "Ajouter à l'itération"
@@ -46125,6 +46782,9 @@ msgstr[1] "Assignés"
msgid "WorkItem|Cancel"
msgstr "Annuler"
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr "Enfant supprimé"
@@ -46134,6 +46794,12 @@ msgstr "Fermé"
msgid "WorkItem|Collapse tasks"
msgstr "Réduire les tâches"
+msgid "WorkItem|Create %{workItemType}"
+msgstr "Créer %{workItemType}"
+
+msgid "WorkItem|Create objective"
+msgstr "Créer un objectif"
+
msgid "WorkItem|Create task"
msgstr "Créer une tâche"
@@ -46144,32 +46810,41 @@ msgid "WorkItem|Dates"
msgstr "Dates"
msgid "WorkItem|Delete %{workItemType}"
-msgstr ""
+msgstr "Supprimer %{workItemType}"
+
+msgid "WorkItem|Discard changes"
+msgstr "Abandonner les modifications"
msgid "WorkItem|Due date"
msgstr "Date d'échéance"
+msgid "WorkItem|Existing task"
+msgstr "Tâche existante"
+
msgid "WorkItem|Expand tasks"
msgstr "Étendre les tâches"
msgid "WorkItem|Incident"
msgstr "Incident"
-msgid "WorkItem|Introducing tasks"
-msgstr "Introduction aux tâches"
-
msgid "WorkItem|Issue"
msgstr "Ticket"
msgid "WorkItem|Iteration"
msgstr "Itération"
-msgid "WorkItem|Learn about tasks."
-msgstr "En savoir plus sur les tâches."
+msgid "WorkItem|Key result"
+msgstr "Résultat clé"
msgid "WorkItem|Milestone"
msgstr "Jalon"
+msgid "WorkItem|New objective"
+msgstr "Nouvel objectif"
+
+msgid "WorkItem|New task"
+msgstr "Nouvelle tâche"
+
msgid "WorkItem|No iteration"
msgstr "Aucune itération"
@@ -46179,12 +46854,18 @@ msgstr "Aucun résultat correspondant"
msgid "WorkItem|No milestone"
msgstr "Aucun jalon"
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr "Aucun objectif ni résultat clé n’est actuellement assigné."
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr "Aucune tâche n’est actuellement assignée. Utilisez des tâches pour scinder ce ticket en parties plus petites."
msgid "WorkItem|None"
msgstr "Aucun"
+msgid "WorkItem|Objective"
+msgstr "Objectif"
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr "Seuls l'auteur, les assignés et les membres du projet ayant au moins le rôle de Rapporteur peuvent voir cette tâche ou recevoir des notifications la concernant."
@@ -46197,14 +46878,23 @@ msgstr "Supprimer"
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr "Enregistrer et écraser"
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr "Rechercher des %{workItemType}s existants"
+
msgid "WorkItem|Select type"
msgstr "Sélectionner le type"
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr "Quelqu’un a édité la description en même temps que vous. Si vous l'enregistrez, cela écrasera ses modifications. Veuillez confirmer que vous souhaitez enregistrer vos modifications."
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
-msgstr ""
+msgstr "Une erreur s'est produite lors de la création de %{workItemType}. Veuillez réessayer."
msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
-msgstr ""
+msgstr "Une erreur s'est produite lors de la suppression de %{workItemType}. Veuillez réessayer."
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr "Une erreur s’est produite lors de la suppression de la tâche. Veuillez réessayer."
@@ -46231,7 +46921,7 @@ msgid "WorkItem|Something went wrong while fetching milestones. Please try again
msgstr "Une erreur s’est produite lors de la récupération des jalons. Veuillez réessayer."
msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
-msgstr ""
+msgstr "Une erreur s'est produite lors de la mise à jour de %{workItemType}. Veuillez réessayer."
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr "Une erreur s’est produite lors de la mise à jour de l’élément de travail. Veuillez réessayer."
@@ -46263,8 +46953,8 @@ msgstr "Activer la confidentialité"
msgid "WorkItem|Undo"
msgstr "Annuler"
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
-msgstr "Utiliser des tâches pour scinder votre travail sur un ticket en parties plus petites. %{learnMoreLink}"
+msgid "WorkItem|View current version"
+msgstr "Voir la version actuelle"
msgid "WorkItem|Work Items"
msgstr "Éléments de Travail"
@@ -46293,6 +46983,9 @@ msgstr "Écrire un commentaire…"
msgid "Write a description or drag your files here…"
msgstr "Rédigez une description ou faites glisser vos fichiers ici…"
+msgid "Write a description..."
+msgstr "Écrivez une description..."
+
msgid "Write a description…"
msgstr "Écrivez une description…"
@@ -46336,15 +47029,15 @@ msgid "You"
msgstr "Vous"
msgid "You already have pending todo for this alert"
-msgstr "Vous avez déjà une tâche « À faire » en attente pour cette alerte"
+msgstr "Vous avez déjà un pense-bête en attente pour cette alerte"
msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
msgstr "Vous allez ajouter %{usersTag} personnes à la discussion. Elles recevront toutes une notification."
msgid "You are about to clear %{count} image from the cache. Once you confirm, the next time a pipeline runs it must pull an image or tag from Docker Hub. Are you sure?"
msgid_plural "You are about to clear %{count} images from the cache. Once you confirm, the next time a pipeline runs it must pull an image or tag from Docker Hub. Are you sure?"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "Vous êtes sur le point de supprimer %{count} image du cache. Après avoir confirmé, à la prochaine exécution d'un pipeline, ce dernier devra récupérer une image ou une étiquette depuis Docker Hub. Êtes-vous sûr(e) ?"
+msgstr[1] "Vous êtes sur le point de supprimer %{count} images du cache. Après avoir confirmé, à la prochaine exécution d'un pipeline, ce dernier devra récupérer une image ou une étiquette depuis Docker Hub. Êtes-vous sûr(e) ?"
msgid "You are about to delete this forked project containing:"
msgstr "Vous êtes sur le point de supprimer ce projet divergent qui contient :"
@@ -46371,7 +47064,7 @@ msgid "You are attempting to update a file that has changed since you started ed
msgstr "Vous tentez de mettre à jour un fichier qui a changé depuis que vous avez commencé à le modifier."
msgid "You are being redirected away from GitLab"
-msgstr ""
+msgstr "Vous êtes redirigé(e) en dehors de GitLab"
msgid "You are billed if you exceed this number. %{qsrOverageLinkStart}How does billing work?%{qsrOverageLinkEnd}"
msgstr "Vous serez facturé si ce nombre est dépassé. %{qsrOverageLinkStart}Comment fonctionne la facturation ?%{qsrOverageLinkEnd}"
@@ -46443,7 +47136,7 @@ msgid "You are not authorized to update this scanner profile"
msgstr "Vous n'êtes pas autorisé à mettre à jour ce profil de scanner"
msgid "You are not authorized to upload metric images"
-msgstr ""
+msgstr "Vous n'êtes pas autorisé(e) à téléverser des images de métriques"
msgid "You are now impersonating %{username}"
msgstr "Vous utilisez maintenant l'identité de %{username}"
@@ -46452,7 +47145,7 @@ msgid "You are on a read-only GitLab instance."
msgstr "Vous êtes sur une instance GitLab en lecture seule."
msgid "You are receiving this message because you are a GitLab administrator for %{url}."
-msgstr ""
+msgstr "Vous recevez ce message car vous êtes un administrateur GitLab pour %{url}."
msgid "You are signed in to GitLab as:"
msgstr "Vous êtes connecté à GitLab en tant que :"
@@ -46460,11 +47153,11 @@ msgstr "Vous êtes connecté à GitLab en tant que :"
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr "Vous essayez de téléverser autre chose qu'une image. Veuillez envoyer un .png, .jpg, .jpeg, .gif, .bmp, .tiff ou .ico."
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr "Vous pouvez %{gitlabLinkStart}résoudre les conflits sur GitLab%{gitlabLinkEnd} ou %{resolveLocallyStart}le faire localement%{resolveLocallyEnd}."
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
-msgstr "Vous pouvez %{resolveLocallyStart}effectuer la résolution localement%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
+msgstr "Vous pouvez %{resolveLocallyStart}les résoudre localement%{resolveLocallyEnd}."
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
msgstr "Vous pouvez ajuster les règles de bannissement automatique %{link_start}ici%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr "Vous pouvez toujours modifier votre URL plus tard"
msgid "You can always edit this later"
msgstr "Vous pouvez toujours modifier ceci plus tard"
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] "Vous pouvez maintenant commencer à déplacer des membres dans %{namespace_name}. Un membre perd l'accès au groupe lorsque vous désactivez %{strong_start}Dans un siège%{strong_end}. Si plus de %{free_user_limit} membre a l'option %{strong_start}Dans un siège%{strong_end} activée après le 19 octobre 2022, nous sélectionnerons le %{free_user_limit} membre dont l'accès sera conservé. Nous prendrons en compte en premier les membres ayant les rôles de Propriétaire et de Mainteneur, puis ceux actifs le plus récemment, jusqu'à atteindre %{free_user_limit} membre. Les membres restants passeront à l'état Hors limite et n'auront plus accès au groupe."
-msgstr[1] "Vous pouvez maintenant commencer à déplacer des membres dans %{namespace_name}. Un membre perd l'accès au groupe lorsque vous désactivez %{strong_start}Dans un siège%{strong_end}. Si plus de %{free_user_limit} membres ont l'option %{strong_start}Dans un siège%{strong_end} activée après le 19 octobre 2022, nous sélectionnerons les %{free_user_limit} membres dont l'accès sera conservé. Nous prendrons en compte en premier les membres ayant les rôles de Propriétaire et de Mainteneur, puis ceux actifs le plus récemment, jusqu'à atteindre %{free_user_limit} membres. Les membres restants passeront à l'état Hors limite et n'auront plus accès au groupe."
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46565,7 +47253,7 @@ msgid "You can enter up to 280 characters"
msgstr "Vous pouvez entrer jusqu'à 280 caractères"
msgid "You can filter by 'days to merge' by clicking on the columns in the chart."
-msgstr ""
+msgstr "Vous pouvez filtrer selon les « jours jusqu'à la fusion » en cliquant sur les colonnes du graphique."
msgid "You can find more information about GitLab subscriptions in %{subscriptions_doc_link}."
msgstr "Vous pouvez trouver plus d’informations sur les abonnements à GitLab dans %{subscriptions_doc_link}."
@@ -46573,9 +47261,6 @@ msgstr "Vous pouvez trouver plus d’informations sur les abonnements à GitLab
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr "Vous pouvez commencer par cloner le dépôt ou par y ajouter des fichiers avec l'une des options suivantes."
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr "Vous pouvez regrouper les cas de test avec des étiquettes. Pour en savoir plus sur la direction future de cette fonctionnalité, visitez %{linkStart}Gestion de la Qualité sur la page Direction%{linkEnd}."
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "Vous pouvez inviter un nouveau membre sur %{project_name} ou inviter un autre groupe."
@@ -46586,7 +47271,7 @@ msgid "You can invite another group to %{project_name}."
msgstr "Vous pouvez inviter un autre groupe sur %{project_name}."
msgid "You can move around the graph by using the arrow keys."
-msgstr "Vous pouvez vous déplacer dans le graphique en utilisant les touches fléchées."
+msgstr "Vous pouvez vous déplacer dans le graphe en utilisant les touches fléchées."
msgid "You can notify the app / group or a project by sending them an email notification"
msgstr "Vous pouvez notifier l'application / groupe ou un projet en leur envoyant une notification par courriel"
@@ -46594,17 +47279,11 @@ msgstr "Vous pouvez notifier l'application / groupe ou un projet en leur envoyan
msgid "You can now close this window."
msgstr "Vous pouvez maintenant fermer cette fenêtre."
-msgid "You can now export your security dashboard to a CSV report."
-msgstr "Vous pouvez maintenant exporter votre tableau de bord de sécurité vers un rapport CSV."
-
msgid "You can now submit a merge request to get this change into the original branch."
-msgstr ""
+msgstr "Vous pouvez maintenant soumettre une demande de fusion pour que cette modification soit appliquée sur la branche d'origine."
msgid "You can now submit a merge request to get this change into the original project."
-msgstr ""
-
-msgid "You can only %{action} files when you are on a branch"
-msgstr "Vous pouvez seulement %{action} des fichiers lorsque vous êtes sur une branche"
+msgstr "Vous pouvez maintenant soumettre une demande de fusion pour que cette modification soit appliquée dans le projet d'origine."
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr "Vous ne pouvez ajouter au maximum que %{max_contacts} contacts à la fois"
@@ -46613,7 +47292,7 @@ msgid "You can only edit files when you are on a branch"
msgstr "Vous ne pouvez modifier des fichiers que dans une branche"
msgid "You can only merge once the items above are resolved."
-msgstr ""
+msgstr "Vous ne pouvez fusionner qu'une fois les éléments ci-dessus résolus."
msgid "You can only transfer the project to namespaces you manage."
msgstr "Vous ne pouvez transférer le projet que vers des espaces de noms que vous gérez."
@@ -46628,7 +47307,7 @@ msgid "You can set up jobs to only use runners with specific tags. Separate tags
msgstr "Vous pouvez configurer des tâches pour utiliser uniquement des exécuteurs qui possèdent des étiquettes spécifiques. Utilisez des virgules pour séparer les étiquettes."
msgid "You can specify notification level per group or per project."
-msgstr ""
+msgstr "Vous pouvez spécifier le niveau de notification par groupe ou par projet."
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr "Vous pouvez tester votre fichier « .gitlab-ci.yml » avec %{linkStart}CI Lint%{linkEnd}."
@@ -46673,7 +47352,7 @@ msgid "You cannot rename an environment after it's created."
msgstr "Vous ne pouvez pas renommer un environnement après sa création."
msgid "You cannot set yourself to awaiting"
-msgstr ""
+msgstr "Vous ne pouvez pas vous mettre vous-même en attente"
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr "Vous ne pouvez pas écrire sur une instance GitLab Geo secondaire en lecture seule. Veuillez utiliser le %{link_to_primary_node} à la place."
@@ -46687,6 +47366,9 @@ msgstr "Vous ne pouvez pas modifier directement des fichiers dans ce projet. CrÃ
msgid "You could not create a new trigger."
msgstr "Vous ne pouvez pas créer un nouveau déclencheur."
+msgid "You do not have access to any projects for creating incidents."
+msgstr "Vous n'avez accès à aucun projet pour créer des incidents."
+
msgid "You do not have any subscriptions yet"
msgstr "Vous n’avez souscrit à aucun abonnement pour le moment"
@@ -46697,7 +47379,7 @@ msgid "You do not have permission to approve a member"
msgstr "Vous n'avez pas la permission d'approuver un membre"
msgid "You do not have permission to leave this %{namespaceType}."
-msgstr ""
+msgstr "Vous n'avez pas la permission de quitter ce %{namespaceType}."
msgid "You do not have permission to run a pipeline on this branch."
msgstr "Vous n'avez pas la permission d'exécuter un pipeline sur cette branche."
@@ -46730,7 +47412,7 @@ msgid "You don't have any authorized applications"
msgstr "Vous ne disposez d’aucune application autorisée"
msgid "You don't have any deployments right now."
-msgstr ""
+msgstr "Vous n'avez aucun déploiement pour le moment."
msgid "You don't have any open merge requests"
msgstr "Vous n'avez aucune demande de fusion ouverte"
@@ -46741,6 +47423,9 @@ msgstr "Vous n'avez aucune recherche récente"
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr "Vous n'avez pas la permission d'examiner ce déploiement. Contactez le propriétaire du projet ou du groupe pour obtenir de l'aide."
+msgid "You don't have permission to view this epic"
+msgstr "Vous n'avez pas la permission de voir cette épopée"
+
msgid "You don't have permissions to create this project"
msgstr "Vous n'avez pas la permission de créer ce projet"
@@ -46754,7 +47439,7 @@ msgid "You don't have write access to the source branch."
msgstr "Vous n'avez pas d'accès en écriture à la branche source."
msgid "You don’t have access to Productivity Analytics in this group"
-msgstr ""
+msgstr "Vous n'avez pas accès à l'Analytique de la Productivité dans ce groupe"
msgid "You don’t have access to Value Stream Analytics for this group"
msgstr "Vous n'avez pas accès à l'analyse des chaînes de valeur de ce groupe"
@@ -46795,7 +47480,7 @@ msgid "You have insufficient permissions to configure escalation policies for th
msgstr "Vous ne disposez pas des permissions suffisantes pour configurer des politiques d'escalade sur ce projet"
msgid "You have insufficient permissions to create a Todo for this alert"
-msgstr "Vous n'avez pas les autorisations suffisantes pour créer une tâche « À faire » pour cette alerte"
+msgstr "Vous n'avez pas les autorisations suffisantes pour créer un pense-bête pour cette alerte"
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr "Vous n'avez pas les permissions suffisantes pour créer une intégration HTTP pour ce projet"
@@ -46803,6 +47488,9 @@ msgstr "Vous n'avez pas les permissions suffisantes pour créer une intégration
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr "Vous n'avez pas les permissions suffisantes pour créer un calendrier des astreintes pour ce projet"
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr "Vous n'avez pas les permissions suffisantes pour gérer les alertes de ce projet"
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr "Vous n'avez pas les permissions suffisantes pour gérer les liens de ressources pour cet incident"
@@ -46852,16 +47540,13 @@ msgid "You have set up 2FA for your account! If you lose access to your 2FA devi
msgstr "Vous avez configuré l'A2F sur votre compte ! Si vous perdez l'accès à votre périphérique A2F, vous pouvez utiliser vos codes de récupération pour accéder à votre compte. Sinon, en envoyant une clé SSH, vous pourrez %{anchorOpen}utiliser celle-ci pour générer des codes de récupération supplémentaires%{anchorClose}."
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
+msgstr "Vous avez acheté avec succès %{product}. Vous allez recevoir un reçu par courriel. Votre achat pouvant prendre une minute pour être synchronisé, actualisez la page si vous ne le voyez pas encore."
msgid "You have unsaved changes"
msgstr "Vous avez des modifications non enregistrées"
msgid "You left the \"%{membershipable_human_name}\" %{source_type}."
-msgstr ""
+msgstr "Vous avez quitté le %{source_type} « %{membershipable_human_name} »."
msgid "You may close the milestone now."
msgstr "Vous pouvez maintenant fermer le jalon."
@@ -46873,7 +47558,7 @@ msgid "You must be logged in to search across all of GitLab"
msgstr "Vous devez être connecté(e) pour rechercher dans tout GitLab"
msgid "You must have developer or higher permissions in the associated project to view job logs when debug trace is enabled. To disable debug trace, set the 'CI_DEBUG_TRACE' and 'CI_DEBUG_SERVICES' variables to 'false' in your pipeline configuration or CI/CD settings. If you must view this job log, a project maintainer or owner must add you to the project with developer permissions or higher."
-msgstr ""
+msgstr "Vous devez disposer de permissions de niveau développeur ou supérieur dans le projet associé pour afficher les journaux des tâches lorsque les traces de débogage sont activées. Pour désactiver celles-ci, définissez les variables « CI_DEBUG_TRACE » et « CI_DEBUG_SERVICES » à « false » dans la configuration de votre pipeline ou dans les paramètres CI/CD. Si vous avez besoin de voir ce journal des tâches, un responsable ou propriétaire du projet doit vous ajouter au projet avec des permissions développeur ou supérieures."
msgid "You must have maintainer access to force delete a lock"
msgstr "Seul un responsable peut forcer la suppression d’un verrou"
@@ -46924,7 +47609,7 @@ msgid "You successfully declined the invitation"
msgstr "Vous avez refusé l'invitation avec succès"
msgid "You tried to fork %{link_to_the_project} but it failed for the following reason:"
-msgstr ""
+msgstr "Vous avez essayé de créer une divergence du projet %{link_to_the_project}, mais cela a échoué pour la raison suivante :"
msgid "You will be removed from existing projects/groups"
msgstr "Vous serez retiré des projets/groupes existants"
@@ -46984,10 +47669,10 @@ msgid "You're at the last commit"
msgstr "Vous êtes à la dernière validation"
msgid "You're not allowed to %{tag_start}edit%{tag_end} files in this project directly. Please fork this project, make your changes there, and submit a merge request."
-msgstr ""
+msgstr "Vous n'êtes pas autorisé à %{tag_start}modifier%{tag_end} directement les fichiers dans ce projet. Veuillez créer une divergence de ce projet, y apporter vos modifications et soumettre une demande de fusion."
msgid "You're not allowed to make changes to this project directly. A fork of this project has been created that you can make changes in, so you can submit a merge request."
-msgstr ""
+msgstr "Vous n'êtes pas autorisé à modifier directement ce projet. Une divergence de ce projet a été créée afin que vous puissiez y apporter des modifications, et ainsi vous pourrez soumettre une demande de fusion."
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
@@ -47002,13 +47687,13 @@ msgid "You're receiving this email because of your account on %{host}. %{manage_
msgstr "Vous recevez cet e-mail en raison de votre compte sur %{host}. %{manage_notifications_link_start}Gérez toutes les notifications%{manage_notifications_link_end} &middot; %{help_link_start}Aide%{help_link_end}"
msgid "You're receiving this email because of your account on %{host}. %{unsubscribe_link_start}Unsubscribe%{unsubscribe_link_end} from this thread &middot; %{manage_notifications_link_start}Manage all notifications%{manage_notifications_link_end} &middot; %{help_link_start}Help%{help_link_end}"
-msgstr ""
+msgstr "Vous recevez ce courriel en raison de votre compte sur %{host}. %{unsubscribe_link_start}Désabonnez-vous%{unsubscribe_link_end} de ce fil de conversation &middot; %{manage_notifications_link_start}Gérez toutes les notifications%{manage_notifications_link_end} &middot; %{help_link_start}Aide%{help_link_end}"
msgid "You're receiving this email because of your activity on %{host}."
msgstr "Vous recevez ce courriel en raison de votre activité sur %{host}."
msgid "You're receiving this email because of your activity on %{host}. %{unsubscribe_link_start}Unsubscribe%{unsubscribe_link_end} from this thread &middot; %{manage_notifications_link_start}Manage all notifications%{manage_notifications_link_end} &middot; %{help_link_start}Help%{help_link_end}"
-msgstr ""
+msgstr "Vous recevez ce courriel en raison de votre activité sur %{host}. %{unsubscribe_link_start}Désabonnez-vous%{unsubscribe_link_end} de ce fil de conversation &middot; %{manage_notifications_link_start}Gérez toutes les notifications%{manage_notifications_link_end} &middot; %{help_link_start}Aide%{help_link_end}"
msgid "You're receiving this email because you have been assigned an item on %{host}."
msgstr "Vous recevez ce courriel car un élément vous a été assigné sur %{host}."
@@ -47029,11 +47714,14 @@ msgid "You're viewing members of %{strong_start}%{group_name}%{strong_end}."
msgstr "Vous voyez actuellement les membres de %{strong_start}%{group_name}%{strong_end}."
msgid "You've already enabled two-factor authentication using one time password authenticators. In order to register a different device, you must first disable two-factor authentication."
-msgstr ""
+msgstr "Vous avez déjà activé l'authentification à deux facteurs à l'aide d'un authentificateur OTP. Pour enregistrer un autre appareil, vous devez d'abord désactiver l'authentification à deux facteurs."
msgid "You've rejected %{user}"
msgstr "Vous avez rejeté %{user}"
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr "Vous avez acheté avec succès l'abonnement au forfait %{plan} pour %{seats} et vous allez recevoir un reçu par courriel. Votre achat pouvant prendre une minute pour être synchronisé, actualisez la page si les détails de votre abonnement ne sont pas encore affichés."
+
msgid "YouTube"
msgstr "YouTube"
@@ -47050,10 +47738,10 @@ msgid "Your %{strong}%{plan_name}%{strong_close} subscription expires on %{stron
msgstr "Votre abonnement %{strong}%{plan_name}%{strong_close} arrive à échéance le %{strong}%{expires_on}%{strong_close}. Si vous ne le renouvelez pas, vous perdrez l'accès à vos fonctionnalités payantes le %{strong}%{downgrades_on}%{strong_close}. Après cette date, vous ne pourrez plus créer de tickets ni de demandes de fusion, ni utiliser d'autres fonctionnalités."
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
-msgstr ""
+msgstr "Votre abonnement %{strong}%{plan_name}%{strong_close} pour %{strong}%{namespace_name}%{strong_close} expirera le %{strong}%{expires_on}%{strong_close}."
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
-msgstr "La syntaxe de votre configuration CI/CD n'est pas valide. Voir l'onglet Lint pour plus de détails."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
+msgstr "La syntaxe de votre configuration CI/CD n'est pas valide. Sélectionnez l'onglet Valider pour plus de détails."
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
msgstr "Votre exportation CSV a commencé. Elle sera envoyée à %{email} une fois terminée."
@@ -47088,6 +47776,9 @@ msgstr "Votre demande de compte GitLab a été approuvée !"
msgid "Your GitLab group"
msgstr "Votre groupe GitLab"
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr "Votre instance GitLab permet à tout le monde de se créer un compte, ce qui représente un risque de sécurité pour les instances GitLab exposées publiquement. Vous devriez désactiver les nouvelles inscriptions si les utilisateurs publics ne sont pas censés s'enregistrer."
+
msgid "Your Groups"
msgstr "Vos groupes"
@@ -47113,7 +47804,7 @@ msgid "Your SSH keys (%{count})"
msgstr "Vos clés SSH (%{count})"
msgid "Your To-Do List"
-msgstr "Votre liste de tâches"
+msgstr "Vos pense-bêtes"
msgid "Your U2F device did not send a valid JSON response."
msgstr "Votre appareil U2F n'a pas envoyé de réponse JSON valide."
@@ -47149,7 +47840,7 @@ msgid "Your account is locked."
msgstr "Votre compte est verrouillé."
msgid "Your account uses dedicated credentials for the \"%{group_name}\" group and can only be updated through SSO."
-msgstr ""
+msgstr "Votre compte utilise des identifiants dédiés au groupe « %{group_name} » et ne peut être mis à jour que via SSO."
msgid "Your action has been rejected because the namespace storage limit has been reached. For more information, visit %{doc_url}."
msgstr "Votre action a été rejetée car la limite de stockage de l'espace de noms a été atteinte. Pour plus d'informations, consultez %{doc_url}."
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] "Votre groupe gratuit est maintenant limité à %d membre"
msgstr[1] "Votre groupe gratuit est maintenant limité à %d membres"
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] "Votre groupe %{strong_start}%{namespace_name}%{strong_end} contient plus de %{free_user_limit} membre. À partir du 19 octobre 2022, le %{free_user_limit} membre le plus récemment actif restera actif et ceux restants passeront à l'%{link_start}état Hors limite%{link_end} et perdront l'accès au groupe. Vous pouvez vous rendre sur la page Quotas d'Utilisation pour gérer lesquel des %{free_user_limit} membre restera dans votre groupe."
-msgstr[1] "Votre groupe %{strong_start}%{namespace_name}%{strong_end} contient plus de %{free_user_limit} membres. À partir du 19 octobre 2022, les %{free_user_limit} membres les plus récemment actifs resteront actifs et ceux restants passeront à l'%{link_start}état Hors limite%{link_end} et perdront l'accès au groupe. Vous pouvez vous rendre sur la page Quotas d'Utilisation pour gérer lesquels des %{free_user_limit} membres resteront dans votre groupe."
-
msgid "Your groups"
msgstr "Vos groupes"
@@ -47275,6 +47961,15 @@ msgstr "Votre nom"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr "Votre espace de noms %{namespace_name} dépasse la limite de %{free_limit} utilisateurs et a été placé en lecture seule."
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr "Votre espace de noms dépasse les limites d'utilisateurs et de stockage et a été placé en lecture seule."
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr "Votre espace de noms dépasse la limite d'utilisateurs et a été placé en lecture seule."
+
msgid "Your new %{accessTokenType}"
msgstr "Votre nouveau %{accessTokenType}"
@@ -47428,16 +48123,19 @@ msgid "[No reason]"
msgstr "[Sans raison]"
msgid "[REDACTED]"
-msgstr ""
+msgstr "[CAVIARDÉ]"
msgid "[Redacted]"
-msgstr ""
+msgstr "[Caviardé]"
+
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr "[Prend en charge le GitLab Flavored Markdown, y compris les actions rapides]"
msgid "`end_time` should not exceed one month after `start_time`"
-msgstr "« end_time » ne devrait pas dépasser « start_time » de plus d'un mois"
+msgstr "`end_time` ne devrait pas dépasser `start_time` de plus d'un mois"
msgid "`start_time` should precede `end_time`"
-msgstr "L'heure de début « start_time » doit être antérieure à celle de fin « end_time »"
+msgstr "L'heure de début `start_time` doit être antérieure à celle de fin `end_time`"
msgid "a deleted user"
msgstr "un utilisateur supprimé"
@@ -47457,16 +48155,16 @@ msgid "added a %{link_type} link"
msgstr ""
msgid "added a Zoom call to this issue"
-msgstr ""
+msgstr "a ajouté un appel Zoom à ce ticket"
msgid "ago"
-msgstr ""
+msgstr "plus tôt"
msgid "alert"
msgstr "alerte"
msgid "allowed to fail"
-msgstr ""
+msgstr "autorisé à échouer"
msgid "already banned from namespace"
msgstr ""
@@ -47487,10 +48185,10 @@ msgid "and"
msgstr "et"
msgid "any-approver for the merge request already exists"
-msgstr ""
+msgstr "tout-approbateur pour la demande de fusion existe déjà"
msgid "any-approver for the project already exists"
-msgstr ""
+msgstr "tout-approbateur pour le projet existe déjà"
msgid "approval"
msgid_plural "approvals"
@@ -47507,7 +48205,7 @@ msgid "artifacts"
msgstr "artéfacts"
msgid "assign yourself"
-msgstr ""
+msgstr "assigner à vous-même"
msgid "assigned"
msgstr ""
@@ -47522,7 +48220,7 @@ msgid "at least the Reporter role"
msgstr "au moins le rôle de Rapporteur"
msgid "at least the Reporter role, the author, and assignees"
-msgstr ""
+msgstr "au moins le rôle de Rapporteur, l'auteur et les personnes assignées"
msgid "attach a new file"
msgstr ""
@@ -47563,13 +48261,13 @@ msgid "can contain only lowercase letters, digits, and '_'."
msgstr "ne peut contenir que des minuscules, des chiffres et '_'."
msgid "can not be changed for existing notes"
-msgstr ""
+msgstr "ne peut pas être modifié pour les notes existantes"
msgid "can not be set for this resource"
-msgstr ""
+msgstr "ne peut pas être défini pour cette ressource"
msgid "can not be set for this type of note"
-msgstr ""
+msgstr "ne peut pas être défini pour ce type de note"
msgid "can only be changed by a group admin."
msgstr "ne peut être modifié que par un administrateur de groupe."
@@ -47578,10 +48276,10 @@ msgid "can only have one escalation policy"
msgstr "ne peut avoir qu'une seule politique d'escalade"
msgid "can't be enabled when delayed group deletion is disabled"
-msgstr ""
+msgstr "ne peut pas être activé quand la suppression de groupe différée est désactivée"
msgid "can't be nil"
-msgstr ""
+msgstr "ne peut pas être nul"
msgid "can't be solely blank"
msgstr ""
@@ -47610,14 +48308,17 @@ msgstr "ne peut pas être associé à un sous-groupe"
msgid "cannot be associated with both a Group and a Project"
msgstr "ne peut pas être associé simultanément à un Groupe et à un Projet"
+msgid "cannot be blank"
+msgstr "ne peut pas être vide"
+
msgid "cannot be changed"
-msgstr ""
+msgstr "ne peut pas être modifié"
msgid "cannot be changed if a personal project has container registry tags."
-msgstr ""
+msgstr "ne peut pas être modifié si un projet personnel contient des étiquettes dans le registre de conteneurs."
msgid "cannot be changed if shared runners are enabled"
-msgstr ""
+msgstr "ne peut pas être modifié si les exécuteurs partagés sont activés"
msgid "cannot be changed since member is associated with a custom role"
msgstr "ne peut pas être changé car le membre est associé à un rôle personnalisé"
@@ -47629,10 +48330,10 @@ msgid "cannot be enabled because parent group does not allow it"
msgstr ""
msgid "cannot be enabled because parent group has shared Runners disabled"
-msgstr ""
+msgstr "ne peut pas être activé car le groupe parent a désactivé les exécuteurs partagés"
msgid "cannot be enabled unless all domains have TLS certificates"
-msgstr ""
+msgstr "ne peut pas être activé à moins que tous les domaines n'aient des certificats TLS"
msgid "cannot be enabled until a valid credit card is on file"
msgstr "impossible à activer tant qu'une carte de crédit valide n'est pas enregistrée"
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] "modification"
msgstr[1] "modifications"
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr "%{criticalStart}critiques%{criticalEnd} %{highStart}hautes%{highEnd} et %{otherStart}autres%{otherEnd}"
@@ -47720,11 +48431,14 @@ msgstr "%{scanner} a détecté %{number} nouvelles %{vulnStr} potentielles"
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr "%{scanner} a détecté %{strong_start}%{number}%{strong_end} nouveau potentiel %{vulnStr}"
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
-msgstr "%{scanner} n'a détecté aucune %{boldStart}nouvelle%{boldEnd} vulnérabilité potentielle"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr "%{scanner} n'a détecté aucune nouvelle %{vulnStr}"
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
-msgstr "%{scanner} n'a détecté aucun %{strong_start}nouveau%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
+msgstr "%{scanner} n'a détecté aucune nouvelle vulnérabilité potentielle"
+
+msgid "ciReport|%{scanner}: Loading resulted in an error"
+msgstr "%{scanner} : Le chargement a généré une erreur"
msgid "ciReport|: Loading resulted in an error"
msgstr "Le chargement a entraîné une erreur"
@@ -47745,7 +48459,7 @@ msgid "ciReport|All projects"
msgstr "Tous les projets"
msgid "ciReport|All severities"
-msgstr ""
+msgstr "Tous les niveaux de gravité"
msgid "ciReport|All tools"
msgstr "Tous les outils"
@@ -47786,10 +48500,10 @@ msgid "ciReport|Code Quality"
msgstr "Qualité de Code"
msgid "ciReport|Code Quality failed loading results"
-msgstr ""
+msgstr "Échec du chargement des résultats de qualité du code"
msgid "ciReport|Code Quality test metrics results are being parsed"
-msgstr ""
+msgstr "Les résultats des métriques des tests de qualité du code sont en cours d’analyse"
msgid "ciReport|Code quality degraded due to 1 new issue"
msgid_plural "ciReport|Code quality degraded due to %d new issues"
@@ -47844,16 +48558,16 @@ msgid "ciReport|Dependency scanning"
msgstr "Analyse des dépendances"
msgid "ciReport|Detects known vulnerabilities in your source code's dependencies."
-msgstr ""
+msgstr "Détecte les vulnérabilités connues dans les dépendances de votre code source."
msgid "ciReport|Detects known vulnerabilities in your source code."
-msgstr ""
+msgstr "Détecte les vulnérabilités connues dans votre code source."
msgid "ciReport|Detects known vulnerabilities in your web application."
-msgstr ""
+msgstr "Détecte les vulnérabilités connues dans votre application web."
msgid "ciReport|Detects secrets and credentials vulnerabilities in your source code."
-msgstr ""
+msgstr "Détecte les vulnérabilités de secrets et d’identifiants dans votre code source."
msgid "ciReport|Download patch to resolve"
msgstr ""
@@ -47882,8 +48596,8 @@ msgstr "Corrigé :"
msgid "ciReport|Found %{issuesWithCount}"
msgstr "%{issuesWithCount} trouvés"
-msgid "ciReport|Full Report"
-msgstr "Rapport complet"
+msgid "ciReport|Full report"
+msgstr ""
msgid "ciReport|Generic Report"
msgstr "Rapport Générique"
@@ -47898,27 +48612,27 @@ msgid "ciReport|License Compliance"
msgstr "Conformité de Licence"
msgid "ciReport|License Compliance failed loading results"
-msgstr ""
+msgstr "Échec du chargement des résultats d'analyse de conformité de la licence"
msgid "ciReport|License Compliance test metrics results are being parsed"
msgstr "Les résultats des métriques des tests de Conformité de Licence sont en cours d’analyse"
msgid "ciReport|Load Performance"
-msgstr ""
+msgstr "Performance de charge"
msgid "ciReport|Load performance test metrics detected %{strong_start}%{changesFound}%{strong_end} change"
msgid_plural "ciReport|Load performance test metrics detected %{strong_start}%{changesFound}%{strong_end} changes"
-msgstr[0] "Les métriques du test de performances de la charge ont détecté %{strong_start}%{changesFound}%{strong_end} modification"
-msgstr[1] "Les métriques du test de performances de la charge ont détecté %{strong_start}%{changesFound}%{strong_end} modifications"
+msgstr[0] "Les métriques des tests de performance de charge ont détecté %{strong_start}%{changesFound}%{strong_end} modification"
+msgstr[1] "Les métriques des tests de performance de charge ont détecté %{strong_start}%{changesFound}%{strong_end} modifications"
msgid "ciReport|Load performance test metrics results are being parsed"
msgstr "Les résultats des métriques des tests de performance de charge sont en cours d’analyse"
msgid "ciReport|Load performance test metrics: "
-msgstr ""
+msgstr "Métriques des tests de performance de charge : "
msgid "ciReport|Load performance test metrics: No changes"
-msgstr ""
+msgstr "Métriques des tests de performance de charge : Aucune modification"
msgid "ciReport|Loading %{reportName} report"
msgstr "Chargement du rapport %{reportName}"
@@ -48060,6 +48774,11 @@ msgstr "a commenté"
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48070,7 +48789,7 @@ msgid "complete"
msgstr ""
msgid "compliance violation has already been recorded"
-msgstr ""
+msgstr "une violation de conformité a déjà été enregistrée"
msgid "contacts can only be added to root groups"
msgstr ""
@@ -48088,7 +48807,7 @@ msgid "contribute to this project."
msgstr "contribue à ce projet."
msgid "could not read private key, is the passphrase correct?"
-msgstr "impossible de lire la clef privée, la phrase secrète estâ€elle correcte ?"
+msgstr "impossible de lire la clé privée, la phrase secrète estâ€elle correcte ?"
msgid "created"
msgstr ""
@@ -48129,7 +48848,7 @@ msgid "days"
msgstr "jours"
msgid "default"
-msgstr ""
+msgstr "par défaut"
msgid "default branch"
msgstr "branche par défaut"
@@ -48144,7 +48863,7 @@ msgid "deploy"
msgstr ""
msgid "design"
-msgstr ""
+msgstr "esquisse"
msgid "disabled"
msgstr "désactivé"
@@ -48162,7 +48881,7 @@ msgid "does not match dast_site_validation.project"
msgstr "ne correspond pas à dast_site_validation.project"
msgid "download it"
-msgstr ""
+msgstr "le télécharger"
msgid "draft"
msgid_plural "drafts"
@@ -48173,16 +48892,16 @@ msgid "e.g. %{token}"
msgstr "p. ex. %{token}"
msgid "element is not a hierarchy"
-msgstr ""
+msgstr "l'élément n'est pas une hiérarchie"
msgid "eligible users"
msgstr "utilisateurs éligibles"
msgid "email '%{email}' is not a verified email."
-msgstr ""
+msgstr "l'adresse « %{email} » n'est pas une adresse de courriel vérifiée."
msgid "email address settings"
-msgstr ""
+msgstr "paramètres d'adresse de courriel"
msgid "enabled"
msgstr "activé"
@@ -48194,13 +48913,13 @@ msgid "ending with a reserved file extension is not allowed."
msgstr "se terminant par une extension de fichier réservée n'est pas autorisé."
msgid "entries cannot be larger than 255 characters"
-msgstr ""
+msgstr "les entrées ne peuvent pas dépasser 255 caractères"
msgid "entries cannot be nil"
msgstr ""
msgid "entries cannot contain HTML tags"
-msgstr ""
+msgstr "les entrées ne peuvent pas contenir de balises HTML"
msgid "epic"
msgstr "épopée"
@@ -48209,7 +48928,7 @@ msgid "error"
msgstr "erreur"
msgid "estimateCommand|%{slash_command} overwrites the total estimated time."
-msgstr "%{slash_command} écrase le temps passé total."
+msgstr "%{slash_command} écrase le temps estimé total."
msgid "example.com"
msgstr "exemple.com"
@@ -48217,9 +48936,6 @@ msgstr "exemple.com"
msgid "exceeds maximum length (100 usernames)"
msgstr "dépasse la longueur maximale (100 noms d'utilisateurs)"
-msgid "exceeds the %{max_value_length} character limit"
-msgstr "dépasse la limite fixée à %{max_value_length} caractères"
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr "dépasse la limite de %{bytes} octets"
@@ -48227,7 +48943,7 @@ msgid "exceeds the limit of %{bytes} bytes for directory name \"%{dirname}\""
msgstr "dépasse la limite de %{bytes} octets pour le nom du répertoire \"%{dirname}\""
msgid "expired on %{timebox_due_date}"
-msgstr ""
+msgstr "a expiré le %{timebox_due_date}"
msgid "expires on %{timebox_due_date}"
msgstr "arrive à échéance le %{timebox_due_date}"
@@ -48262,16 +48978,16 @@ msgid "for"
msgstr "pendant"
msgid "for %{link_to_merge_request} with %{link_to_merge_request_source_branch}"
-msgstr ""
+msgstr "pour %{link_to_merge_request} avec %{link_to_merge_request_source_branch}"
msgid "for %{link_to_merge_request} with %{link_to_merge_request_source_branch} into %{link_to_merge_request_target_branch}"
msgstr ""
msgid "for %{link_to_pipeline_ref}"
-msgstr ""
+msgstr "pour %{link_to_pipeline_ref}"
msgid "for %{ref}"
-msgstr ""
+msgstr "pour %{ref}"
msgid "for this project"
msgstr "pour ce projet"
@@ -48294,7 +49010,7 @@ msgid "frontmatter"
msgstr "frontmatter"
msgid "group"
-msgstr ""
+msgstr "groupe"
msgid "group access token"
msgstr "jeton d'accès de groupe"
@@ -48374,10 +49090,10 @@ msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
-msgstr ""
+msgstr "dans le groupe %{link_to_group}"
msgid "in project %{link_to_project}"
-msgstr ""
+msgstr "dans le projet %{link_to_project}"
msgid "instance completed"
msgid_plural "instances completed"
@@ -48394,12 +49110,15 @@ msgid "invalidated"
msgstr ""
msgid "is"
-msgstr ""
+msgstr "est"
msgid "is already associated to a GitLab Issue. New issue will not be associated."
-msgstr ""
+msgstr "est déjà associé à un ticket GitLab. Le nouveau ticket ne sera pas associé."
msgid "is already linked to this vulnerability"
+msgstr "est déjà lié à cette vulnérabilité"
+
+msgid "is already present in ancestors"
msgstr ""
msgid "is an invalid IP address range"
@@ -48409,7 +49128,7 @@ msgid "is blocked by"
msgstr "est bloqué par"
msgid "is forbidden by a top-level group"
-msgstr ""
+msgstr "est interdit par un groupe de premier niveau"
msgid "is invalid because there is downstream lock"
msgstr "n’est pas valide, car il y a un verrou en aval"
@@ -48418,7 +49137,7 @@ msgid "is invalid because there is upstream lock"
msgstr "n’est pas valide, car il y a un verrou en amont"
msgid "is not"
-msgstr ""
+msgstr "n'est pas"
msgid "is not a descendant of the Group owning the template"
msgstr ""
@@ -48430,7 +49149,7 @@ msgid "is not a valid X509 certificate."
msgstr "n’est pas un certificat X.509 valide."
msgid "is not allowed for sign-up. Please use your regular email address."
-msgstr ""
+msgstr "n'est pas autorisé pour l'inscription. Veuillez utiliser votre adresse de courriel habituelle."
msgid "is not allowed for this group."
msgstr "n'est pas autorisé pour ce groupe."
@@ -48441,8 +49160,14 @@ msgstr "n'est pas autorisé pour ce projet."
msgid "is not allowed since the group is not top-level group."
msgstr "n'est pas autorisé car le groupe n'est pas un groupe de premier niveau."
+msgid "is not allowed to add this type of parent"
+msgstr "n'est pas autorisé à ajouter ce type de parent"
+
+msgid "is not allowed to point to itself"
+msgstr "n'est pas autorisé à pointer sur lui-même"
+
msgid "is not allowed. Please use your regular email address."
-msgstr ""
+msgstr "n'est pas autorisé. Veuillez utiliser votre adresse de courriel habituelle."
msgid "is not in the group enforcing Group Managed Account"
msgstr "n'est pas dans le groupe qui impose le Compte Géré de Groupe"
@@ -48475,10 +49200,10 @@ msgid "is too long (maximum is %{count} characters)"
msgstr "est trop long (le maximum est de %{count} caractères)"
msgid "is too long (maximum is 100 entries)"
-msgstr ""
+msgstr "est trop long (le maximum est de 100 entrées)"
msgid "is too long (maximum is 1000 entries)"
-msgstr ""
+msgstr "est trop long (le maximum est de 1000 entrées)"
msgid "issue"
msgstr "ticket"
@@ -48490,25 +49215,25 @@ msgid "issues need attention"
msgstr "Les tickets nécessitent une attention"
msgid "issues on track"
-msgstr ""
+msgstr "tickets en bonne voie"
msgid "it is larger than %{limit}"
-msgstr ""
+msgstr "il fait plus de %{limit}"
msgid "it is stored as a job artifact"
-msgstr ""
+msgstr "il est stocké en tant qu'artéfact de tâche"
msgid "it is stored externally"
msgstr ""
msgid "it is stored in LFS"
-msgstr ""
+msgstr "il est stocké dans LFS"
msgid "it is too large"
-msgstr ""
+msgstr "il est trop volumineux"
msgid "jigsaw is not defined"
-msgstr ""
+msgstr "jigsaw n'est pas défini"
msgid "kuromoji custom analyzer"
msgstr "analyseur personnalisé kuromoji"
@@ -48546,7 +49271,7 @@ msgstr[0] "ligne"
msgstr[1] "lignes"
msgid "load it anyway"
-msgstr ""
+msgstr "le charger quand même"
msgid "loading"
msgstr ""
@@ -48577,7 +49302,7 @@ msgstr[0] "demande de fusion"
msgstr[1] "demandes de fusion"
msgid "mergedCommitsAdded| (commits were squashed)"
-msgstr ""
+msgstr "(les commits ont été écrasés)"
msgid "metric_id must be unique across a project"
msgstr "metric_id doit être unique au sein d'un projet"
@@ -48586,13 +49311,13 @@ msgid "milestone should belong either to a project or a group."
msgstr "le jalon doit appartenir à un projet ou à un groupe."
msgid "missing"
-msgstr ""
+msgstr "manquant"
msgid "months"
msgstr "mois"
msgid "most recent deployment"
-msgstr ""
+msgstr "déploiement le plus récent"
msgid "mrWidgetCommitsAdded|%{commitCount} and %{mergeCommitCount} will be added to %{targetBranch}%{squashedCommits}."
msgstr "%{commitCount} et %{mergeCommitCount} seront ajoutés à %{targetBranch}%{squashedCommits}."
@@ -48634,7 +49359,7 @@ msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasis
msgstr "%{metricsLinkStart}L’usage mémoire%{metricsLinkEnd} %{emphasisStart}est resté stable%{emphasisEnd} à %{memoryFrom}MO"
msgid "mrWidget|A merge train is a queued list of merge requests waiting to be merged into the target branch. The changes in each merge request are combined with the changes in earlier merge requests and tested before merge."
-msgstr ""
+msgstr "Un train de fusion est une file d'attente constituée de demandes de fusions attendant d'être fusionnées dans la branche cible. Les modifications de chaque demande de fusion sont combinées avec celles des demandes de fusion antérieures et testées avant la fusion."
msgid "mrWidget|A new merge train has started and this merge request is the first of the queue."
msgstr "Un nouveau train de fusion a démarré et cette demande de fusion est la première de la file d’attente."
@@ -48670,7 +49395,7 @@ msgid "mrWidget|Approve"
msgstr "Approuver"
msgid "mrWidget|Approve additionally"
-msgstr ""
+msgstr "Approuver en complément"
msgid "mrWidget|Approved by"
msgstr "Approuvée par"
@@ -48890,7 +49615,7 @@ msgid "mrWidget|To approve this merge request, please enter your password. This
msgstr "Pour approuver cette demande de fusion, veuillez saisir votre mot de passe. Ce projet nécessite que toutes les approbations soient authentifiées."
msgid "mrWidget|To change these default messages, edit the templates for both the merge and squash commit messages. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
+msgstr "Pour modifier ces messages par défaut, éditez les modèles pour les messages de commits de fusion et ceux des squash de commits. %{linkStart}En savoir plus.%{linkEnd}"
msgid "mrWidget|To change this default message, edit the template for merge commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr "Pour modifier ce message par défaut, éditez le modèle des messages pour les validations de fusion. %{linkStart}En savoir plus.%{linkEnd}"
@@ -48932,6 +49657,9 @@ msgid "must be associated with a Group or a Project"
msgstr "doit être associé à un Groupe ou à un Projet"
msgid "must be greater than start date"
+msgstr "doit être supérieure à la date de début"
+
+msgid "must be in same hierarchy as custom role's namespace"
msgstr ""
msgid "must be inside the fork network"
@@ -48943,12 +49671,12 @@ msgstr "doit être inférieur à la limite de %{tag_limit} étiquettes"
msgid "must be set for a project namespace"
msgstr "doit être défini pour un espace de noms de projet"
-msgid "must be top-level namespace"
-msgstr "doit être un espace de noms de niveau supérieur"
-
msgid "must be unique by status and elapsed time within a policy"
msgstr "doit être unique au niveau état et temps écoulé dans une politique"
+msgid "must belong to same project of its requirement object."
+msgstr ""
+
msgid "must belong to same project of the work item."
msgstr "doit appartenir au même projet que l'élément de travail."
@@ -48971,7 +49699,7 @@ msgid "my-topic"
msgstr "mon-sujet"
msgid "needs to be between 10 minutes and 1 month"
-msgstr ""
+msgstr "doit être entre 10 minutes et 1 mois"
msgid "never"
msgstr "jamais"
@@ -48989,7 +49717,7 @@ msgid "no name set"
msgstr "aucun nom défini"
msgid "no one can merge"
-msgstr ""
+msgstr "personne ne peut fusionner"
msgid "no scopes selected"
msgstr "aucune portée sélectionnée"
@@ -48998,13 +49726,13 @@ msgid "none"
msgstr "aucun"
msgid "not authorized to create member"
-msgstr ""
+msgstr "non autorisé à créer un membre"
msgid "not authorized to update member"
-msgstr ""
+msgstr "non autorisé à mettre à jour le membre"
msgid "not found"
-msgstr ""
+msgstr "introuvable"
msgid "nounSeries|%{firstItem} and %{lastItem}"
msgstr "%{firstItem} et %{lastItem}"
@@ -49031,7 +49759,7 @@ msgid "open issue"
msgstr "ouvrir un ticket"
msgid "or"
-msgstr ""
+msgstr "ou"
msgid "organizations can only be added to root groups"
msgstr ""
@@ -49060,7 +49788,7 @@ msgid "password"
msgstr "mot de passe"
msgid "pending comment"
-msgstr ""
+msgstr "commentaire en attente"
msgid "pending deletion"
msgstr "en attente de suppression"
@@ -49078,7 +49806,7 @@ msgid "pipeline schedules documentation"
msgstr "documentation des planifications de pipeline"
msgid "pipelineEditorWalkthrough|Let's do this!"
-msgstr ""
+msgstr "Allons-y !"
msgid "pipelineEditorWalkthrough|See how GitLab pipelines work"
msgstr "Voir comment fonctionnent les pipelines GitLab"
@@ -49104,7 +49832,7 @@ msgid "private"
msgstr "privé"
msgid "private key does not match certificate."
-msgstr "la clef privée ne correspond pas au certificat."
+msgstr "la clé privée ne correspond pas au certificat."
msgid "processing"
msgstr ""
@@ -49121,7 +49849,7 @@ msgid "project access tokens"
msgstr "jetons d'accès au projet"
msgid "project bots cannot be added to other groups / projects"
-msgstr ""
+msgstr "les robots du projet ne peuvent pas être ajoutés à d'autres groupes / projets"
msgid "project is read-only"
msgstr "le projet est en lecture seule"
@@ -49150,6 +49878,9 @@ msgstr "Clé privée reCAPTCHA"
msgid "reCAPTCHA site key"
msgstr "Clé du site reCAPTCHA"
+msgid "reached maximum depth"
+msgstr "a atteint la profondeur maximale"
+
msgid "recent activity"
msgstr "activité récente"
@@ -49178,7 +49909,7 @@ msgid "removed a %{link_type} link"
msgstr ""
msgid "removed a Zoom call from this issue"
-msgstr ""
+msgstr "a supprimé un appel Zoom de ce ticket"
msgid "rendered diff"
msgstr ""
@@ -49189,7 +49920,7 @@ msgstr[0] "réponse"
msgstr[1] "réponses"
msgid "reply should have same confidentiality as top-level note"
-msgstr ""
+msgstr "la réponse doit avoir la même confidentialité que la note de premier niveau"
msgid "repositories"
msgstr "dépôts"
@@ -49200,16 +49931,21 @@ msgstr[0] "dépôt"
msgstr[1] "dépôts"
msgid "repository:"
-msgstr ""
+msgstr "dépôt :"
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] "règle"
+msgstr[1] "règles"
+
msgid "running"
msgstr ""
msgid "satisfied"
-msgstr ""
+msgstr "satisfaite"
msgid "scan-execution-policy: policy not applied, %{policy_path} file is invalid"
msgstr "scan-execution-policy : politique non appliquée, le fichier %{policy_path} n'est pas valide"
@@ -49259,7 +49995,7 @@ msgid "severity|Unknown"
msgstr "Inconnue"
msgid "should be an array of %{object_name} objects"
-msgstr ""
+msgstr "doit être un tableau d'objets %{object_name}"
msgid "should be an array of existing usernames. %{invalid} does not exist"
msgstr "devrait être un tableau de noms d'utilisateurs existants. %{invalid} n'existe pas"
@@ -49298,13 +50034,13 @@ msgid "ssh:"
msgstr "ssh:"
msgid "started a discussion on %{design_link}"
-msgstr ""
+msgstr "a commencé une discussion sur %{design_link}"
msgid "started on %{timebox_start_date}"
-msgstr ""
+msgstr "a commencé le %{timebox_start_date}"
msgid "starts on %{timebox_start_date}"
-msgstr ""
+msgstr "commence le %{timebox_start_date}"
msgid "structure is too large"
msgstr "la structure est trop grande"
@@ -49376,19 +50112,19 @@ msgid "total must be less than or equal to %{size}"
msgstr "le total doit être inférieur ou égal à %{size}"
msgid "triggered"
-msgstr ""
+msgstr "déclenché"
msgid "two-factor authentication settings"
msgstr "paramètres d'authentification à deux facteurs"
msgid "type must be Debian"
-msgstr ""
+msgstr "le type doit être Debian"
msgid "type parameter is missing and is required"
msgstr "le paramètre de type est manquant et est requis"
msgid "unicode domains should use IDNA encoding"
-msgstr ""
+msgstr "les domaines unicode doivent utiliser le codage IDNA"
msgid "updated"
msgstr ""
@@ -49426,7 +50162,7 @@ msgid "value for '%{storage}' must be between 0 and 100"
msgstr "la valeur de « %{storage} » doit être comprise entre 0 et 100"
msgid "verify ownership"
-msgstr ""
+msgstr "vérifier la propriété"
msgid "version %{versionIndex}"
msgstr "version %{versionIndex}"
@@ -49441,7 +50177,7 @@ msgid "view it on GitLab"
msgstr "voir sur GitLab"
msgid "view the blob"
-msgstr ""
+msgstr "voir le blob"
msgid "view the source"
msgstr ""
@@ -49500,7 +50236,7 @@ msgid "with expiry remaining unchanged at %{old_expiry}"
msgstr ""
msgid "yaml invalid"
-msgstr ""
+msgstr "yaml non valide"
msgid "you"
msgstr "vous"
diff --git a/locale/fr/gitlab.po.time_stamp b/locale/fr/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/fr/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 64f787b32f3..5b6dd92358b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -511,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -711,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -744,6 +750,9 @@ msgstr ""
msgid "%{itemsCount} issues with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{key} is not a valid URL."
+msgstr ""
+
msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
msgstr ""
@@ -911,9 +920,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -950,6 +956,12 @@ msgstr ""
msgid "%{ref} cannot be added: %{error}"
msgstr ""
+msgid "%{relation_type} epic does not exist."
+msgstr ""
+
+msgid "%{relation_type} epic is not present."
+msgstr ""
+
msgid "%{releases} release"
msgid_plural "%{releases} releases"
msgstr[0] ""
@@ -1279,12 +1291,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -1532,7 +1550,7 @@ msgstr[0] ""
msgstr[1] ""
msgid "1 user"
-msgid_plural "%{num} users"
+msgid_plural "%d users"
msgstr[0] ""
msgstr[1] ""
@@ -1759,6 +1777,9 @@ msgstr ""
msgid "A sign-in to your account has been made from the following IP address: %{ip}"
msgstr ""
+msgid "A template for starting a new TYPO3 project"
+msgstr ""
+
msgid "A title is required"
msgstr ""
@@ -2116,7 +2137,7 @@ msgstr ""
msgid "Activate Service Desk"
msgstr ""
-msgid "Activated on"
+msgid "Activated"
msgstr ""
msgid "Active"
@@ -2377,6 +2398,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2464,6 +2488,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2740,6 +2767,9 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
+msgid "AdminSettings|By default, set a limit to 0 to have no limit."
+msgstr ""
+
msgid "AdminSettings|CI/CD limits"
msgstr ""
@@ -2809,6 +2839,9 @@ msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Search"
msgstr ""
+msgid "AdminSettings|Enable migrating GitLab groups and projects by direct transfer"
+msgstr ""
+
msgid "AdminSettings|Enable pipeline suggestion banner"
msgstr ""
@@ -2896,6 +2929,9 @@ msgstr ""
msgid "AdminSettings|Maximum number of custom domains per project"
msgstr ""
+msgid "AdminSettings|Maximum number of downstream pipelines in a pipeline's hierarchy tree"
+msgstr ""
+
msgid "AdminSettings|Maximum number of jobs in a single pipeline"
msgstr ""
@@ -2983,9 +3019,6 @@ msgstr ""
msgid "AdminSettings|Set a CI/CD template as the required pipeline configuration for all projects in the instance. Project CI/CD configuration merges into the required pipeline configuration when the pipeline runs. %{link_start}What is a required pipeline configuration?%{link_end}"
msgstr ""
-msgid "AdminSettings|Set limit to 0 to disable it."
-msgstr ""
-
msgid "AdminSettings|Set the expiration time of authentication tokens of newly registered group runners."
msgstr ""
@@ -3031,6 +3064,9 @@ msgstr ""
msgid "AdminSettings|There are Advanced Search migrations pending that require indexing to pause. Indexing must remain paused until GitLab completes the migrations."
msgstr ""
+msgid "AdminSettings|This limit cannot be disabled. Set to 0 to block all DAG dependencies."
+msgstr ""
+
msgid "AdminSettings|To enable Registration Features, first enable Service Ping."
msgstr ""
@@ -4393,15 +4429,6 @@ msgstr ""
msgid "An error occurred while loading projects."
msgstr ""
-msgid "An error occurred while loading the Jobs tab."
-msgstr ""
-
-msgid "An error occurred while loading the Needs tab."
-msgstr ""
-
-msgid "An error occurred while loading the Test Reports tab."
-msgstr ""
-
msgid "An error occurred while loading the blob controls."
msgstr ""
@@ -4435,9 +4462,6 @@ msgstr ""
msgid "An error occurred while loading the notification settings. Please try again."
msgstr ""
-msgid "An error occurred while loading the pipeline."
-msgstr ""
-
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -4551,6 +4575,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4668,9 +4695,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -4831,9 +4855,6 @@ msgstr ""
msgid "ApplicationSettings|Send a confirmation email during sign up. New users must confirm their email address before they can log in."
msgstr ""
-msgid "ApplicationSettings|Send confirmation email on sign-up"
-msgstr ""
-
msgid "ApplicationSettings|Sign-up enabled"
msgstr ""
@@ -5063,6 +5084,9 @@ msgstr ""
msgid "Approvals are optional."
msgstr ""
+msgid "Approvals required"
+msgstr ""
+
msgid "Approvals|Section: %section"
msgstr ""
@@ -5209,6 +5233,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5617,6 +5644,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5662,6 +5692,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5893,9 +5926,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6918,6 +6948,9 @@ msgstr ""
msgid "BranchRules|%{total} approval %{subject}"
msgstr ""
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
msgid "BranchRules|%{total} status %{subject}"
msgstr ""
@@ -7197,9 +7230,93 @@ msgstr ""
msgid "Broadcast Messages"
msgstr ""
+msgid "BroadcastMessages|Add broadcast message"
+msgstr ""
+
+msgid "BroadcastMessages|Allow users to dismiss the broadcast message"
+msgstr ""
+
+msgid "BroadcastMessages|Banner"
+msgstr ""
+
+msgid "BroadcastMessages|Blue"
+msgstr ""
+
+msgid "BroadcastMessages|Dark"
+msgstr ""
+
+msgid "BroadcastMessages|Dismissable"
+msgstr ""
+
+msgid "BroadcastMessages|Ends at"
+msgstr ""
+
+msgid "BroadcastMessages|Green"
+msgstr ""
+
+msgid "BroadcastMessages|Indigo"
+msgstr ""
+
+msgid "BroadcastMessages|Light"
+msgstr ""
+
+msgid "BroadcastMessages|Light Blue"
+msgstr ""
+
+msgid "BroadcastMessages|Light Green"
+msgstr ""
+
+msgid "BroadcastMessages|Light Indigo"
+msgstr ""
+
+msgid "BroadcastMessages|Light Red"
+msgstr ""
+
+msgid "BroadcastMessages|Message"
+msgstr ""
+
+msgid "BroadcastMessages|Notification"
+msgstr ""
+
+msgid "BroadcastMessages|Paths can contain wildcards, like */welcome"
+msgstr ""
+
+msgid "BroadcastMessages|Red"
+msgstr ""
+
+msgid "BroadcastMessages|Starts at"
+msgstr ""
+
+msgid "BroadcastMessages|Target Path"
+msgstr ""
+
+msgid "BroadcastMessages|Target roles"
+msgstr ""
+
+msgid "BroadcastMessages|The broadcast message displays only to users in projects and groups who have these roles."
+msgstr ""
+
+msgid "BroadcastMessages|Theme"
+msgstr ""
+
+msgid "BroadcastMessages|There was an error adding broadcast message."
+msgstr ""
+
+msgid "BroadcastMessages|There was an error updating broadcast message."
+msgstr ""
+
msgid "BroadcastMessages|There was an issue deleting this message, please try again later."
msgstr ""
+msgid "BroadcastMessages|Type"
+msgstr ""
+
+msgid "BroadcastMessages|Update broadcast message"
+msgstr ""
+
+msgid "BroadcastMessages|Your message here"
+msgstr ""
+
msgid "Browse Directory"
msgstr ""
@@ -8084,6 +8201,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8193,6 +8313,9 @@ msgstr ""
msgid "Checkout|Country"
msgstr ""
+msgid "Checkout|Coupon code (optional)"
+msgstr ""
+
msgid "Checkout|Create a new group"
msgstr ""
@@ -8205,9 +8328,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8223,7 +8343,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8247,6 +8367,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8352,12 +8475,6 @@ msgstr ""
msgid "Child epic"
msgstr ""
-msgid "Child epic does not exist."
-msgstr ""
-
-msgid "Child epic doesn't exist."
-msgstr ""
-
msgid "Child issues and epics"
msgstr ""
@@ -8490,9 +8607,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8505,6 +8628,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8517,6 +8643,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8526,6 +8655,12 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
+msgid "CiVariables|Trigger this manual action"
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8535,6 +8670,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -8645,6 +8786,9 @@ msgstr ""
msgid "Click the link below to confirm your email address."
msgstr ""
+msgid "Click to expand"
+msgstr ""
+
msgid "Click to expand it."
msgstr ""
@@ -8747,12 +8891,18 @@ msgstr ""
msgid "Closed this %{quick_action_target}."
msgstr ""
+msgid "Closed this issue. Marked as related to, and a duplicate of, %{duplicate_param}."
+msgstr ""
+
msgid "Closed: %{closed}"
msgstr ""
msgid "Closes this %{quick_action_target}."
msgstr ""
+msgid "Closes this issue. Marks as related to, and a duplicate of, %{duplicate_reference}."
+msgstr ""
+
msgid "Cloud Run"
msgstr ""
@@ -9226,6 +9376,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10047,6 +10200,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10071,6 +10227,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10086,15 +10245,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10260,7 +10428,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10380,6 +10548,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10419,12 +10590,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10689,9 +10854,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -11187,6 +11349,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11319,9 +11484,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11400,6 +11562,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11436,6 +11601,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -12083,7 +12284,13 @@ msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
-msgid "DORA4Metrics|DORA metrics for %{groupName} group"
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
+msgid "DORA4Metrics|DORA metrics for %{name} group"
+msgstr ""
+
+msgid "DORA4Metrics|DORA metrics for %{name} project"
msgstr ""
msgid "DORA4Metrics|Date"
@@ -12101,9 +12308,18 @@ msgstr ""
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Failed to load charts"
+msgstr ""
+
msgid "DORA4Metrics|Lead Time for Changes"
msgstr ""
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12122,6 +12338,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12134,6 +12353,9 @@ msgstr ""
msgid "DORA4Metrics|Number of incidents divided by the number of deployments to a production environment in the given time period."
msgstr ""
+msgid "DORA4Metrics|Past 6 Months"
+msgstr ""
+
msgid "DORA4Metrics|Percentage of failed deployments"
msgstr ""
@@ -12179,9 +12401,6 @@ msgstr ""
msgid "DashboardProjects|Personal"
msgstr ""
-msgid "DashboardProjects|Trending"
-msgstr ""
-
msgid "Dashboards"
msgstr ""
@@ -12218,6 +12437,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12365,6 +12587,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12736,6 +12961,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12793,6 +13021,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13084,11 +13318,10 @@ msgstr ""
msgid "DeletionSettings|Keep deleted"
msgstr ""
-msgid "DeletionSettings|Keep deleted projects for %{number} days"
-msgstr ""
-
-msgid "DeletionSettings|Keep deleted projects for 1 day"
-msgstr ""
+msgid "DeletionSettings|Keep deleted projects for %{number} day"
+msgid_plural "DeletionSettings|Keep deleted projects for %{number} days"
+msgstr[0] ""
+msgstr[1] ""
msgid "DeletionSettings|None, delete immediately"
msgstr ""
@@ -13534,6 +13767,9 @@ msgstr ""
msgid "Deployment Target|Select the deployment target"
msgstr ""
+msgid "Deployment approvals is not configured for this environment."
+msgstr ""
+
msgid "Deployment frequency"
msgstr ""
@@ -13585,6 +13821,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13621,6 +13860,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13725,9 +13967,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13892,6 +14140,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15664,6 +15915,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15979,6 +16233,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16140,6 +16403,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16170,9 +16436,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16191,9 +16469,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16248,10 +16523,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
+msgstr ""
+
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16284,9 +16565,6 @@ msgstr ""
msgid "Expires %{preposition} %{expires_at}"
msgstr ""
-msgid "Expires on"
-msgstr ""
-
msgid "Expires:"
msgstr ""
@@ -16320,9 +16598,6 @@ msgstr ""
msgid "Explore snippets"
msgstr ""
-msgid "Explore topics"
-msgstr ""
-
msgid "Export"
msgstr ""
@@ -16567,9 +16842,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16726,9 +16998,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16983,6 +17252,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17202,15 +17474,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17394,13 +17657,22 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Fork has diverged from upstream repository"
msgstr ""
-msgid "Found errors in your %{gitlab_ci_yml}:"
+msgid "ForksDivergence|Up to date with upstream repository"
msgstr ""
-msgid "Found errors in your .gitlab-ci.yml:"
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Framework successfully deleted"
@@ -17474,11 +17746,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17521,6 +17788,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18368,6 +18638,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18581,6 +18856,12 @@ msgstr ""
msgid "GlobalSearch|%{count} default results provided. Use the up and down arrow keys to navigate search results list."
msgstr ""
+msgid "GlobalSearch|Close"
+msgstr ""
+
+msgid "GlobalSearch|Group"
+msgstr ""
+
msgid "GlobalSearch|Groups"
msgstr ""
@@ -18605,6 +18886,9 @@ msgstr ""
msgid "GlobalSearch|Merge requests that I'm a reviewer"
msgstr ""
+msgid "GlobalSearch|Project"
+msgstr ""
+
msgid "GlobalSearch|Projects"
msgstr ""
@@ -18617,9 +18901,15 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
+msgid "GlobalSearch|Search"
+msgstr ""
+
msgid "GlobalSearch|Search GitLab"
msgstr ""
@@ -18632,9 +18922,15 @@ msgstr ""
msgid "GlobalSearch|Settings"
msgstr ""
+msgid "GlobalSearch|Syntax options"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
+msgid "GlobalSearch|There was an error fetching the \"Syntax Options\" document."
+msgstr ""
+
msgid "GlobalSearch|Type and press the enter key to submit search."
msgstr ""
@@ -18644,6 +18940,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18917,6 +19216,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19106,7 +19411,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19193,6 +19498,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19298,6 +19606,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19589,9 +19900,15 @@ msgstr ""
msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
msgstr ""
+msgid "GroupsEmptyState|No archived projects."
+msgstr ""
+
msgid "GroupsEmptyState|No groups found"
msgstr ""
+msgid "GroupsEmptyState|No shared projects."
+msgstr ""
+
msgid "GroupsEmptyState|No subgroups or projects."
msgstr ""
@@ -19619,6 +19936,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19628,9 +19948,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -19646,6 +19963,9 @@ msgstr ""
msgid "GroupsNew|Import groups from another instance of GitLab"
msgstr ""
+msgid "GroupsNew|Importing groups by direct transfer is currently disabled."
+msgstr ""
+
msgid "GroupsNew|No import options available"
msgstr ""
@@ -19655,6 +19975,12 @@ msgstr ""
msgid "GroupsNew|Personal access token"
msgstr ""
+msgid "GroupsNew|Please %{admin_link_start}enable it in the Admin settings%{admin_link_end}."
+msgstr ""
+
+msgid "GroupsNew|Please ask your Administrator to enable it in the Admin settings."
+msgstr ""
+
msgid "GroupsNew|Please fill in GitLab source URL."
msgstr ""
@@ -19664,6 +19990,9 @@ msgstr ""
msgid "GroupsNew|Provide credentials for another instance of GitLab to import your groups directly."
msgstr ""
+msgid "GroupsNew|Remember to enable it also on the instance you are migrating from."
+msgstr ""
+
msgid "GroupsNew|This feature is deprecated and replaced by %{docs_link_start}group migration%{docs_link_end}."
msgstr ""
@@ -20092,6 +20421,12 @@ msgstr ""
msgid "Hierarchy|Planning hierarchy"
msgstr ""
+msgid "Hierarchy|Something went wrong while fetching children."
+msgstr ""
+
+msgid "Hierarchy|Something went wrong while removing a child item."
+msgstr ""
+
msgid "Hierarchy|Take the work items survey"
msgstr ""
@@ -20149,9 +20484,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20320,6 +20652,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20344,6 +20679,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20383,7 +20721,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20425,6 +20763,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20437,9 +20781,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20732,12 +21085,24 @@ msgstr ""
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
+msgid "ImportProjects|Cancel import"
+msgstr ""
+
+msgid "ImportProjects|Cancelling project import failed"
+msgstr ""
+
+msgid "ImportProjects|Cancelling project import failed: %{reason}"
+msgstr ""
+
msgid "ImportProjects|Error importing repository %{project_safe_import_url} into %{project_full_path} - %{message}"
msgstr ""
msgid "ImportProjects|Import repositories"
msgstr ""
+msgid "ImportProjects|Imported files will be kept. You can import this repository again later."
+msgstr ""
+
msgid "ImportProjects|Importing the project failed"
msgstr ""
@@ -20785,6 +21150,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21682,6 +22050,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21790,7 +22161,7 @@ msgstr ""
msgid "Insights|Configure a custom report for insights into your group processes such as amount of issues, bugs, and merge requests per month. %{linkStart}How do I configure an insights report?%{linkEnd}"
msgstr ""
-msgid "Insights|Some items are not visible beacuse the project was filtered out in the insights.yml file (see the projects.only config for more information)."
+msgid "Insights|Some items are not visible beacuse the project was filtered out in the insights.yml file (see the projects.only config in the YAML file or the enabled project features (issues, merge requests) in the project settings)."
msgstr ""
msgid "Insights|This project is filtered out in the insights.yml file (see the projects.only config for more information)."
@@ -21954,6 +22325,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21987,6 +22361,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22335,6 +22712,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22383,7 +22763,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22988,9 +23368,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23201,6 +23578,9 @@ msgstr ""
msgid "JiraService|Change GitLab version"
msgstr ""
+msgid "JiraService|Continue setup in GitLab"
+msgstr ""
+
msgid "JiraService|Define the type of Jira issue to create from a vulnerability."
msgstr ""
@@ -23249,6 +23629,9 @@ msgstr ""
msgid "JiraService|If different from Web URL."
msgstr ""
+msgid "JiraService|In order to complete the set up, you’ll need to complete a few steps in GitLab."
+msgstr ""
+
msgid "JiraService|Issues created from vulnerabilities in this project will be Jira issues, even if GitLab issues are enabled."
msgstr ""
@@ -23393,9 +23776,6 @@ msgstr ""
msgid "Jobs|All"
msgstr ""
-msgid "Jobs|An error occurred while loading the Failed Jobs tab."
-msgstr ""
-
msgid "Jobs|Are you sure you want to proceed?"
msgstr ""
@@ -23423,9 +23803,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23576,6 +23953,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23657,6 +24037,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23726,9 +24109,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23803,15 +24183,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23871,9 +24254,6 @@ msgstr ""
msgid "Last Seen"
msgstr ""
-msgid "Last Sync"
-msgstr ""
-
msgid "Last Used"
msgstr ""
@@ -23898,6 +24278,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -24003,6 +24386,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -25011,9 +25397,6 @@ msgstr ""
msgid "Marked this %{noun} as ready."
msgstr ""
-msgid "Marked this issue as a duplicate of %{duplicate_param}."
-msgstr ""
-
msgid "Marked this issue as related to %{issue_ref}."
msgstr ""
@@ -25026,9 +25409,6 @@ msgstr ""
msgid "Marks this %{noun} as ready."
msgstr ""
-msgid "Marks this issue as a duplicate of %{duplicate_reference}."
-msgstr ""
-
msgid "Marks this issue as related to %{issue_ref}."
msgstr ""
@@ -25365,6 +25745,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -25783,6 +26169,9 @@ msgstr ""
msgid "MergeRequest|Error loading full diff. Please try again."
msgstr ""
+msgid "MergeRequest|Failed to load the page"
+msgstr ""
+
msgid "MergeRequest|No files found"
msgstr ""
@@ -26492,6 +26881,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26555,9 +26947,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26693,6 +27082,9 @@ msgstr ""
msgid "Name"
msgstr ""
+msgid "Name can contain only letters, digits, emojis, '_', '.', '+', dashes, or spaces"
+msgstr ""
+
msgid "Name can't be blank"
msgstr ""
@@ -26702,6 +27094,9 @@ msgstr ""
msgid "Name is already taken."
msgstr ""
+msgid "Name must start with a letter, digit, emoji, or '_'"
+msgstr ""
+
msgid "Name new label"
msgstr ""
@@ -26767,6 +27162,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26946,6 +27344,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26958,9 +27359,6 @@ msgstr ""
msgid "New directory"
msgstr ""
-msgid "New discussion"
-msgstr ""
-
msgid "New email address added"
msgstr ""
@@ -26991,6 +27389,9 @@ msgstr ""
msgid "New incident"
msgstr ""
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27264,9 +27665,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27276,9 +27674,6 @@ msgstr ""
msgid "No labels with such name or description"
msgstr ""
-msgid "No license. All rights reserved"
-msgstr ""
-
msgid "No matches found"
msgstr ""
@@ -27455,6 +27850,21 @@ msgstr ""
msgid "Normal text"
msgstr ""
+msgid "NorthstarNavigation|Alpha"
+msgstr ""
+
+msgid "NorthstarNavigation|Could not update the new navigation preference. Please try again later."
+msgstr ""
+
+msgid "NorthstarNavigation|Navigation redesign"
+msgstr ""
+
+msgid "NorthstarNavigation|New navigation"
+msgstr ""
+
+msgid "NorthstarNavigation|Toggle new navigation"
+msgstr ""
+
msgid "Not all browsers support U2F devices. Therefore, we require that you set up a two-factor authentication app first. That way you'll always be able to sign in - even when you're using an unsupported browser."
msgstr ""
@@ -27994,6 +28404,9 @@ msgstr ""
msgid "Number of Git pushes after which %{code_start}git gc%{code_end} is run."
msgstr ""
+msgid "Number of Git pushes after which Gitaly is asked to optimize a repository."
+msgstr ""
+
msgid "Number of Git pushes after which a full %{code_start}git repack%{code_end} is run."
msgstr ""
@@ -28027,9 +28440,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28410,6 +28838,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28487,9 +28918,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28595,9 +29023,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28628,6 +29053,9 @@ msgstr ""
msgid "Opstrace endpoint for Error Tracking integration"
msgstr ""
+msgid "Optimize repository period"
+msgstr ""
+
msgid "Optimize your workflow with CI/CD Pipelines"
msgstr ""
@@ -29107,6 +29535,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29241,9 +29672,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29284,6 +29712,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29353,13 +29784,10 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
-msgid "Parent"
-msgstr ""
-
-msgid "Parent epic doesn't exist."
+msgid "Parameters"
msgstr ""
-msgid "Parent epic is not present."
+msgid "Parent"
msgstr ""
msgid "Parsing error for param :embed_json. %{message}"
@@ -29416,6 +29844,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29674,9 +30111,15 @@ msgstr ""
msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
msgstr ""
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
msgstr ""
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -29893,6 +30336,9 @@ msgstr ""
msgid "PipelineSchedules|Are you sure you want to delete this pipeline schedule?"
msgstr ""
+msgid "PipelineSchedules|Cron timezone"
+msgstr ""
+
msgid "PipelineSchedules|Delete pipeline schedule"
msgstr ""
@@ -29905,6 +30351,9 @@ msgstr ""
msgid "PipelineSchedules|Inactive"
msgstr ""
+msgid "PipelineSchedules|Interval Pattern"
+msgstr ""
+
msgid "PipelineSchedules|Last Pipeline"
msgstr ""
@@ -29932,6 +30381,9 @@ msgstr ""
msgid "PipelineSchedules|Run pipeline schedule"
msgstr ""
+msgid "PipelineSchedules|Save pipeline schedule"
+msgstr ""
+
msgid "PipelineSchedules|Successfully taken ownership from %{owner}."
msgstr ""
@@ -30751,9 +31203,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30964,6 +31413,9 @@ msgstr ""
msgid "Preferences|Must be a number between %{min} and %{max}"
msgstr ""
+msgid "Preferences|Opt out of the Web IDE Beta"
+msgstr ""
+
msgid "Preferences|Preview"
msgstr ""
@@ -30991,6 +31443,12 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The Web IDE Beta is the default Web IDE experience."
+msgstr ""
+
+msgid "Preferences|The Web IDE remains available alongside the Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -31006,6 +31464,9 @@ msgstr ""
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -31123,16 +31584,40 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Analyze your product with Product Analytics"
+msgstr ""
+
+msgid "Product Analytics|Set up Product Analytics to track how your product is performing. Combine it with your GitLab data to better understand where you can improve your product and development processes."
+msgstr ""
+
+msgid "Product Analytics|Set up product analytics..."
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
+msgid "ProductAnalytics|An error occurred while fetching data. Refresh the page to try again."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|Dashboards are created by editing the projects dashboard files."
+msgstr ""
+
+msgid "ProductAnalytics|New Analytics Widget Title"
+msgstr ""
+
+msgid "ProductAnalytics|Product analytics dashboards"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31495,6 +31980,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31783,6 +32274,18 @@ msgstr ""
msgid "ProjectLastActivity|Never"
msgstr ""
+msgid "ProjectList|Explore"
+msgstr ""
+
+msgid "ProjectList|Starred"
+msgstr ""
+
+msgid "ProjectList|Topics"
+msgstr ""
+
+msgid "ProjectList|Yours"
+msgstr ""
+
msgid "ProjectOverview|Fork"
msgstr ""
@@ -32029,12 +32532,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -32071,9 +32580,6 @@ msgstr ""
msgid "ProjectSettings|Configure your infrastructure."
msgstr ""
-msgid "ProjectSettings|Configure your project resources and monitor their health."
-msgstr ""
-
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
@@ -32116,9 +32622,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32233,6 +32736,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32245,9 +32751,6 @@ msgstr ""
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
-msgid "ProjectSettings|Operations"
-msgstr ""
-
msgid "ProjectSettings|Override user notification preferences for all project members."
msgstr ""
@@ -32278,6 +32781,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32395,10 +32901,10 @@ msgstr ""
msgid "ProjectSettings|Users can copy the repository to a new project."
msgstr ""
-msgid "ProjectSettings|Users can only push commits to this repository if the committer email is one of their own verified emails."
+msgid "ProjectSettings|Users can only push commits to this repository if the commit author name is consistent with their GitLab account name."
msgstr ""
-msgid "ProjectSettings|Users can only push commits to this repository if the committer name is consistent with their git config username."
+msgid "ProjectSettings|Users can only push commits to this repository if the committer email is one of their own verified emails."
msgstr ""
msgid "ProjectSettings|Users can request access"
@@ -32407,7 +32913,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32488,6 +32994,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32527,6 +33036,9 @@ msgstr ""
msgid "ProjectTemplates|Spring"
msgstr ""
+msgid "ProjectTemplates|TYPO3 Distribution"
+msgstr ""
+
msgid "ProjectTemplates|Tencent Serverless Framework/NextjsSSR"
msgstr ""
@@ -33010,9 +33522,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33154,6 +33663,24 @@ msgstr ""
msgid "ProtectedBranch|default"
msgstr ""
+msgid "ProtectedEnvironments|Allowed to deploy"
+msgstr ""
+
+msgid "ProtectedEnvironments|An error occurred while fetching information on the selected approvers."
+msgstr ""
+
+msgid "ProtectedEnvironments|Approval rules"
+msgstr ""
+
+msgid "ProtectedEnvironments|Number of approvals must be between 1 and 5"
+msgstr ""
+
+msgid "ProtectedEnvironments|Set which groups, access levels or users are required to approve."
+msgstr ""
+
+msgid "ProtectedEnvironments|Set which groups, access levels or users that are allowed to deploy to this environment"
+msgstr ""
+
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
@@ -33166,6 +33693,9 @@ msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy to %{project} / %{environment}"
msgstr ""
+msgid "ProtectedEnvironment|Approvers"
+msgstr ""
+
msgid "ProtectedEnvironment|Environment"
msgstr ""
@@ -33202,6 +33732,9 @@ msgstr ""
msgid "ProtectedEnvironment|Select an environment"
msgstr ""
+msgid "ProtectedEnvironment|Select environment"
+msgstr ""
+
msgid "ProtectedEnvironment|Select groups"
msgstr ""
@@ -33684,6 +34217,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33881,6 +34417,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34193,9 +34732,6 @@ msgstr ""
msgid "Renew subscription"
msgstr ""
-msgid "Renews"
-msgstr ""
-
msgid "Reopen"
msgstr ""
@@ -34220,6 +34756,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34274,10 +34813,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34616,6 +35152,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34863,6 +35402,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -35032,6 +35574,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -35092,7 +35637,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -35143,6 +35688,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35226,6 +35774,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35244,6 +35795,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35283,12 +35837,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35381,6 +35938,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35447,6 +36007,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35495,12 +36058,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35644,13 +36201,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35692,6 +36252,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35707,6 +36270,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35773,9 +36345,21 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Ex, tag-name-1, tag-name-2"
+msgstr ""
+
+msgid "ScanExecutionPolicy|If the field is empty, the runner will be automatically selected"
+msgstr ""
+
msgid "ScanExecutionPolicy|Scanner profile"
msgstr ""
@@ -35803,6 +36387,9 @@ msgstr ""
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35812,21 +36399,57 @@ msgstr ""
msgid "ScanExecutionPolicy|in namespaces"
msgstr ""
+msgid "ScanResultPolicy|%{count} licenses"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
+msgid "ScanResultPolicy|Newly Detected"
+msgstr ""
+
+msgid "ScanResultPolicy|Pre-existing"
+msgstr ""
+
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|except"
+msgstr ""
+
+msgid "ScanResultPolicy|finds any license %{matchType} %{licenseType} and is %{licenseStates} in an open merge request targeting %{branches}"
+msgstr ""
+
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
+msgid "ScanResultPolicy|license states"
+msgstr ""
+
+msgid "ScanResultPolicy|license type"
+msgstr ""
+
+msgid "ScanResultPolicy|matching"
+msgstr ""
+
+msgid "ScanResultPolicy|matching type"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35911,6 +36534,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36007,9 +36633,6 @@ msgstr ""
msgid "Search projects"
msgstr ""
-msgid "Search projects..."
-msgstr ""
-
msgid "Search protected branches"
msgstr ""
@@ -36377,6 +37000,15 @@ msgstr ""
msgid "SecurityOrchestration|%{branches} branch"
msgstr ""
+msgid "SecurityOrchestration|%{scannerStart}%{scanner}%{scannerEnd}"
+msgstr ""
+
+msgid "SecurityOrchestration|%{scannerStart}%{scanner}%{scannerEnd} on runners with the %{tags} and %{lastTag} tags"
+msgstr ""
+
+msgid "SecurityOrchestration|%{scannerStart}%{scanner}%{scannerEnd} on runners with the %{tags} tag"
+msgstr ""
+
msgid "SecurityOrchestration|%{scanners}"
msgstr ""
@@ -36488,6 +37120,9 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
msgid "SecurityOrchestration|Groups"
msgstr ""
@@ -36512,6 +37147,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36548,6 +37186,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36620,6 +37261,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36629,9 +37273,15 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
@@ -36659,7 +37309,7 @@ msgstr ""
msgid "SecurityOrchestration|Summary"
msgstr ""
-msgid "SecurityOrchestration|The following branches do not exist on this development project: %{branches}. Please review all branches to ensure the values are accurate before updating this policy."
+msgid "SecurityOrchestration|The following branches do not exist on this development project: %{branches}. Please review all protected branches to ensure the values are accurate before updating this policy."
msgstr ""
msgid "SecurityOrchestration|There was a problem creating the new security policy"
@@ -36722,10 +37372,10 @@ msgstr ""
msgid "SecurityOrchestration|a"
msgstr ""
-msgid "SecurityOrchestration|all branches"
+msgid "SecurityOrchestration|all namespaces"
msgstr ""
-msgid "SecurityOrchestration|all namespaces"
+msgid "SecurityOrchestration|all protected branches"
msgstr ""
msgid "SecurityOrchestration|an"
@@ -36737,6 +37387,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36785,16 +37438,25 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
msgid "SecurityReports|All statuses"
msgstr ""
-msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
+msgid "SecurityReports|All tools"
msgstr ""
-msgid "SecurityReports|At GitLab, we're all about iteration and feedback. That's why we are reaching out to customers like you to help guide what we work on this year for Vulnerability Management. We have a lot of exciting ideas and ask that you assist us by taking a short survey %{boldStart}no longer than 10 minutes%{boldEnd} to evaluate a few of our potential features."
+msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
msgid "SecurityReports|Change status"
@@ -36935,6 +37597,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37013,9 +37678,6 @@ msgstr ""
msgid "SecurityReports|Submit vulnerability"
msgstr ""
-msgid "SecurityReports|Take survey"
-msgstr ""
-
msgid "SecurityReports|The Vulnerability Report shows results of successful scans on your project's default branch, manually added vulnerability records, and vulnerabilities found from scanning operational environments. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -37043,6 +37705,9 @@ msgstr ""
msgid "SecurityReports|There was an error dismissing the vulnerability."
msgstr ""
+msgid "SecurityReports|There was an error fetching the finding. Please try again."
+msgstr ""
+
msgid "SecurityReports|There was an error reverting the dismissal."
msgstr ""
@@ -37073,9 +37738,6 @@ msgstr ""
msgid "SecurityReports|Upgrade to manage vulnerabilities"
msgstr ""
-msgid "SecurityReports|Vulnerability Management feature survey"
-msgstr ""
-
msgid "SecurityReports|Vulnerability Report"
msgstr ""
@@ -37094,9 +37756,6 @@ msgstr ""
msgid "SecurityReports|You must sign in as an authorized user to see this report"
msgstr ""
-msgid "SecurityReports|Your feedback is important to us! We will ask again in a week."
-msgstr ""
-
msgid "SecurityReports|scanned resources"
msgstr ""
@@ -37759,6 +38418,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -38046,9 +38708,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38202,7 +38861,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38217,16 +38876,13 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
-msgstr ""
-
-msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38253,13 +38909,19 @@ msgstr ""
msgid "SlackIntegration|Update to the latest version"
msgstr ""
+msgid "SlackIntegration|Update to the latest version of GitLab for Slack to get notifications"
+msgstr ""
+
+msgid "SlackIntegration|Update to the latest version to receive notifications from GitLab."
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38280,10 +38942,10 @@ msgstr ""
msgid "SlackService|Perform common operations in this project by entering slash commands in Slack."
msgstr ""
-msgid "Slack|%{asterisk}Step 1.%{asterisk} Connect your GitLab account to get started."
+msgid "Slack|%{asterisk}Channel notifications%{asterisk}"
msgstr ""
-msgid "Slack|%{asterisk}Step 2.%{asterisk} Try it out!"
+msgid "Slack|%{asterisk}Slash commands%{asterisk}"
msgstr ""
msgid "Slack|%{emoji}Connected to GitLab account %{account}"
@@ -38295,22 +38957,16 @@ msgstr ""
msgid "Slack|Connect your GitLab account"
msgstr ""
-msgid "Slack|Create a new issue"
-msgstr ""
-
-msgid "Slack|Create new issues from Slack: %{command}"
+msgid "Slack|Control GitLab from Slack with %{startMarkup}slash commands%{endMarkup}. For a list of available commands, enter %{command}."
msgstr ""
-msgid "Slack|Run a CI/CD job"
+msgid "Slack|GitLab for Slack now supports channel-based notifications.Let your team know when new issues are created or new CI/CD jobs are run.%{startMarkup}Learn more%{endMarkup}."
msgstr ""
-msgid "Slack|See a list of available commands: %{command})"
+msgid "Slack|To start using notifications, %{startMarkup}enable the Slack integration%{endMarkup} in your project settings."
msgstr ""
-msgid "Slack|Streamline your GitLab deployments with ChatOps. Once you've configured your %{startMarkup}CI/CD pipelines%{endMarkup}, try: %{command}"
-msgstr ""
-
-msgid "Slack|View and control GitLab content while you're working in Slack. Type the command as a message in your chat client to activate it. %{startMarkup}Learn more%{endMarkup}."
+msgid "Slack|To start using slash commands, connect your GitLab account."
msgstr ""
msgid "Slice multiplier"
@@ -38397,10 +39053,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38442,6 +39098,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38928,6 +39587,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38997,15 +39659,15 @@ msgstr ""
msgid "Start a Free Ultimate Trial"
msgstr ""
-msgid "Start a new discussion…"
-msgstr ""
-
msgid "Start a new merge request with these changes"
msgstr ""
msgid "Start a review"
msgstr ""
+msgid "Start another thread"
+msgstr ""
+
msgid "Start by choosing a group to start exploring the merge requests in that group. You can then proceed to filter by projects, labels, milestones and authors."
msgstr ""
@@ -39033,6 +39695,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -39096,6 +39761,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39489,18 +40157,36 @@ msgstr ""
msgid "Subscriptions"
msgstr ""
+msgid "Subscriptions|Activation date"
+msgstr ""
+
msgid "Subscriptions|Chat with sales"
msgstr ""
msgid "Subscriptions|Close"
msgstr ""
+msgid "Subscriptions|End date"
+msgstr ""
+
+msgid "Subscriptions|End date:"
+msgstr ""
+
+msgid "Subscriptions|Last sync"
+msgstr ""
+
+msgid "Subscriptions|None"
+msgstr ""
+
msgid "Subscriptions|Not ready to buy yet?"
msgstr ""
msgid "Subscriptions|Start a free trial"
msgstr ""
+msgid "Subscriptions|Start date"
+msgstr ""
+
msgid "Subscriptions|We understand. Maybe you have some questions for our sales team, or maybe you'd like to try some of the paid features first. What would you like to do?"
msgstr ""
@@ -39717,6 +40403,9 @@ msgstr ""
msgid "SuperSonics|Activation not possible due to true-up value mismatch"
msgstr ""
+msgid "SuperSonics|Add activation code"
+msgstr ""
+
msgid "SuperSonics|An error occurred while adding your subscription"
msgstr ""
@@ -39735,7 +40424,7 @@ msgstr ""
msgid "SuperSonics|Cloud licensing is now available. It's an easier way to activate instances and manage subscriptions. Read more about it in our %{blogPostLinkStart}blog post%{blogPostLinkEnd}. Activation codes are available in the %{portalLinkStart}Customers Portal%{portalLinkEnd}."
msgstr ""
-msgid "SuperSonics|Enter activation code"
+msgid "SuperSonics|Customers Portal"
msgstr ""
msgid "SuperSonics|Export license usage file"
@@ -39756,9 +40445,6 @@ msgstr ""
msgid "SuperSonics|Licensed to"
msgstr ""
-msgid "SuperSonics|Manage"
-msgstr ""
-
msgid "SuperSonics|Maximum users"
msgstr ""
@@ -39833,6 +40519,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39923,9 +40612,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40208,6 +40894,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40241,15 +40933,6 @@ msgstr ""
msgid "TemporaryStorageIncrease|can only be set with more than %{percentage}%% usage"
msgstr ""
-msgid "TemporaryStorage|GitLab allows you a %{strongStart}free, one-time storage increase%{strongEnd}. For 30 days your storage will be unlimited. This gives you time to reduce your storage usage. After 30 days, your original storage limit of %{limit} applies. If you are at maximum storage capacity, your account will be read-only. To continue using GitLab you'll have to purchase additional storage or decrease storage usage."
-msgstr ""
-
-msgid "TemporaryStorage|Increase storage temporarily"
-msgstr ""
-
-msgid "TemporaryStorage|Temporarily increase storage now?"
-msgstr ""
-
msgid "Terminal"
msgstr ""
@@ -40280,6 +40963,21 @@ msgstr ""
msgid "TerraformBanner|Using Terraform? Try the GitLab Managed Terraform State"
msgstr ""
+msgid "TerraformLimits|Learn more about Terraform limits."
+msgstr ""
+
+msgid "TerraformLimits|Limits for Terraform features"
+msgstr ""
+
+msgid "TerraformLimits|Maximum file size (in bytes) of Terraform state files. Set to 0 for no limit."
+msgstr ""
+
+msgid "TerraformLimits|Terraform limits"
+msgstr ""
+
+msgid "TerraformLimits|Terraform state size limit (bytes)"
+msgstr ""
+
msgid "Terraform|%{name} successfully removed"
msgstr ""
@@ -40546,9 +41244,6 @@ msgstr ""
msgid "TestReports|Test reports require job artifacts but all artifacts are expired. %{linkStart}Learn more%{linkEnd}"
msgstr ""
-msgid "TestReports|Tests"
-msgstr ""
-
msgid "TestReports|There are no test cases to display."
msgstr ""
@@ -40951,6 +41646,9 @@ msgstr ""
msgid "The name of the Jenkins project. Copy the name from the end of the URL to the project."
msgstr ""
+msgid "The new Web IDE"
+msgstr ""
+
msgid "The number of changes to fetch from GitLab when cloning a repository. Lower values can speed up pipeline execution. Set to %{code_open}0%{code_close} or blank to fetch all branches and tags for each job"
msgstr ""
@@ -41155,6 +41853,9 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
+msgid "There are no approval rules for the given `represent_as` parameter. Use a valid User/Group/Role name instead."
+msgstr ""
+
msgid "There are no archived requirements"
msgstr ""
@@ -41257,6 +41958,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41377,6 +42081,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41416,6 +42123,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41509,9 +42219,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41701,6 +42408,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -42115,7 +42825,7 @@ msgstr ""
msgid "This variable can not be masked."
msgstr ""
-msgid "This vulnerability type has been deprecated from GitLab's default ruleset and automatically resolved."
+msgid "This vulnerability was automatically resolved because its vulnerability type was disabled in this project or removed from GitLab's default ruleset."
msgstr ""
msgid "This will invalidate your registered applications and U2F / WebAuthn devices."
@@ -42193,6 +42903,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42443,6 +43159,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42548,6 +43267,11 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To invite more users, you can reduce the number of users in your namespace to %{free_limit} user or less. You can also upgrade to a paid tier which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
+msgid_plural "To invite more users, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "To keep this project going, create a new issue"
msgstr ""
@@ -42686,6 +43410,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42719,6 +43446,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42734,13 +43464,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42755,22 +43485,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access to group %{which}"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -43055,24 +43785,12 @@ msgstr ""
msgid "Trials|Day %{daysUsed}/%{duration}"
msgstr ""
-msgid "Trials|Go back to GitLab"
-msgstr ""
-
-msgid "Trials|Skip Trial"
-msgstr ""
-
msgid "Trials|Upgrade %{groupName} to %{planName}"
msgstr ""
-msgid "Trials|You can always resume this process by selecting your avatar and choosing 'Start an Ultimate trial'"
-msgstr ""
-
msgid "Trials|You can apply your trial to a new group or an existing group."
msgstr ""
-msgid "Trials|You won't get a free trial right now but you can always resume this process by selecting your avatar and choosing 'Start an Ultimate trial'"
-msgstr ""
-
msgid "Trials|You've got %{daysRemaining} day remaining on GitLab %{planName}!"
msgid_plural "Trials|You've got %{daysRemaining} days remaining on GitLab %{planName}!"
msgstr[0] ""
@@ -43141,6 +43859,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43171,6 +43895,9 @@ msgstr ""
msgid "Try grouping with different labels"
msgstr ""
+msgid "Try it out now"
+msgstr ""
+
msgid "Try logging in using your username or email. If you have forgotten your password, try recovering it"
msgstr ""
@@ -43348,6 +44075,9 @@ msgstr ""
msgid "Unable to create link to vulnerability"
msgstr ""
+msgid "Unable to create pipeline"
+msgstr ""
+
msgid "Unable to fetch branch list for this project."
msgstr ""
@@ -43567,6 +44297,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43675,6 +44408,9 @@ msgstr ""
msgid "UpdateProject|Could not set the default branch"
msgstr ""
+msgid "UpdateProject|Could not set the default branch. Do you have a branch named 'HEAD' in your repository? (%{linkStart}How do I fix this?%{linkEnd})"
+msgstr ""
+
msgid "UpdateProject|New visibility level not allowed!"
msgstr ""
@@ -43750,6 +44486,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43768,18 +44507,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43807,9 +44540,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43819,9 +44549,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43846,9 +44573,6 @@ msgstr ""
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
-msgid "UsageQuota|Increase storage temporarily"
-msgstr ""
-
msgid "UsageQuota|LFS storage"
msgstr ""
@@ -43948,12 +44672,6 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
@@ -43963,9 +44681,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43978,9 +44693,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -44014,7 +44726,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -44023,16 +44735,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44206,6 +44918,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44247,6 +44962,12 @@ msgstr ""
msgid "User %{username} was successfully removed."
msgstr ""
+msgid "User %{user} SCIM identity is deactivated"
+msgstr ""
+
+msgid "User %{user} SCIM identity is reactivated"
+msgstr ""
+
msgid "User %{user} was removed from %{group}."
msgstr ""
@@ -44472,9 +45193,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44664,7 +45382,7 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
-msgid "Valid From"
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE."
msgstr ""
msgid "Validate"
@@ -44736,6 +45454,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44754,6 +45475,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44802,10 +45526,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44817,7 +45541,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44874,6 +45598,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44883,6 +45622,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45069,9 +45820,6 @@ msgstr ""
msgid "Violation"
msgstr ""
-msgid "Visibility"
-msgstr ""
-
msgid "Visibility and access controls"
msgstr ""
@@ -45543,6 +46291,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45618,9 +46369,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45636,24 +46384,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45684,6 +46420,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45750,9 +46498,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45807,16 +46552,10 @@ msgstr ""
msgid "Webhooks|Pipeline events"
msgstr ""
-msgid "Webhooks|Push events"
-msgstr ""
-
-msgid "Webhooks|Push to the repository."
-msgstr ""
-
-msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
+msgid "Webhooks|Regular expression"
msgstr ""
-msgid "Webhooks|Regular expression"
+msgid "Webhooks|Regular expressions such as %{REGEX_CODE} are supported."
msgstr ""
msgid "Webhooks|Releases events"
@@ -45849,9 +46588,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46243,6 +46979,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46252,9 +46991,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46267,12 +47003,24 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
+msgid "WorkItem|%{count} more assignees"
+msgstr ""
+
msgid "WorkItem|%{workItemType} deleted"
msgstr ""
+msgid "WorkItem|Activity"
+msgstr ""
+
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46288,12 +47036,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
-msgid "WorkItem|Add tasks"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46314,6 +47056,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46323,6 +47068,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46335,6 +47086,9 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
@@ -46344,10 +47098,10 @@ msgstr ""
msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Health status"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46356,12 +47110,15 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key Result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
msgid "WorkItem|New task"
msgstr ""
@@ -46374,12 +47131,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46392,12 +47155,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
-msgid "WorkItem|Search existing tasks"
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
msgstr ""
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46428,6 +47197,12 @@ msgstr ""
msgid "WorkItem|Something went wrong while fetching milestones. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while removing child."
+msgstr ""
+
+msgid "WorkItem|Something went wrong while undoing child removal."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
msgstr ""
@@ -46461,7 +47236,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46685,9 +47460,6 @@ msgstr ""
msgid "You can also star a label to make it a priority label."
msgstr ""
-msgid "You can also test your %{gitlab_ci_yml} in %{lint_link_start}CI Lint%{lint_link_end}"
-msgstr ""
-
msgid "You can also upload existing files from your computer using the instructions below."
msgstr ""
@@ -46769,9 +47541,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46790,9 +47559,6 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
@@ -46823,9 +47589,6 @@ msgstr ""
msgid "You can specify notification level per group or per project."
msgstr ""
-msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
-msgstr ""
-
msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
msgstr ""
@@ -46934,7 +47697,10 @@ msgstr ""
msgid "You don't have any recent searches"
msgstr ""
-msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
+msgid "You don't have permission to approve this deployment. Contact the project or group owner for help."
+msgstr ""
+
+msgid "You don't have permission to view this epic"
msgstr ""
msgid "You don't have permissions to create this project"
@@ -46999,6 +47765,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -47227,8 +47996,10 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
-msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
-msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for 1 user and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgid_plural "You've successfully purchased the %{plan} plan subscription for %{quantity} users and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr[0] ""
+msgstr[1] ""
msgid "YouTube"
msgstr ""
@@ -47248,7 +48019,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47284,6 +48055,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47427,11 +48201,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47468,9 +48237,21 @@ msgstr ""
msgid "Your name"
msgstr ""
+msgid "Your namespace %{namespace_name} has reached the %{free_limit} user limit"
+msgstr ""
+
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47614,9 +48395,6 @@ msgstr ""
msgid "ZentaoIntegration|ZenTao issues"
msgstr ""
-msgid "Zoom"
-msgstr ""
-
msgid "Zoom meeting added"
msgstr ""
@@ -47767,6 +48545,9 @@ msgstr ""
msgid "can not be changed for existing notes"
msgstr ""
+msgid "can not be changed to %{new_type}"
+msgstr ""
+
msgid "can not be set for this resource"
msgstr ""
@@ -47812,6 +48593,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47870,6 +48654,11 @@ msgid_plural "checks"
msgstr[0] ""
msgstr[1] ""
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47933,6 +48722,9 @@ msgstr ""
msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
+msgid "ciReport|%{scanner}: Loading resulted in an error"
+msgstr ""
+
msgid "ciReport|: Loading resulted in an error"
msgstr ""
@@ -47992,10 +48784,13 @@ msgstr ""
msgid "ciReport|Code Quality"
msgstr ""
-msgid "ciReport|Code Quality failed loading results"
+msgid "ciReport|Code Quality failed to load results"
+msgstr ""
+
+msgid "ciReport|Code Quality hasn't changed."
msgstr ""
-msgid "ciReport|Code Quality test metrics results are being parsed"
+msgid "ciReport|Code Quality is loading"
msgstr ""
msgid "ciReport|Code quality degraded due to 1 new issue"
@@ -48089,7 +48884,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48148,9 +48943,6 @@ msgstr ""
msgid "ciReport|New vulnerabilities are vulnerabilities that the security scan detects in the merge request that are different to existing vulnerabilities in the default branch."
msgstr ""
-msgid "ciReport|No changes to Code Quality."
-msgstr ""
-
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -48267,6 +49059,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48424,9 +49221,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48609,6 +49403,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48630,9 +49427,6 @@ msgstr ""
msgid "is not a descendant of the Group owning the template"
msgstr ""
-msgid "is not a valid URL."
-msgstr ""
-
msgid "is not a valid X509 certificate."
msgstr ""
@@ -48648,6 +49442,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -49141,6 +49941,12 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in ISO 8601 format"
+msgstr ""
+
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -49150,9 +49956,6 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
-msgstr ""
-
msgid "must be unique by status and elapsed time within a policy"
msgstr ""
@@ -49228,12 +50031,6 @@ msgstr ""
msgid "nounSeries|%{item}, and %{lastItem}"
msgstr ""
-msgid "only %{parent_types} can be parent of Task."
-msgstr ""
-
-msgid "only Task can be assigned as a child in hierarchy."
-msgstr ""
-
msgid "only available on top-level groups."
msgstr ""
@@ -49360,6 +50157,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
diff --git a/locale/gl_ES/gitlab.po b/locale/gl_ES/gitlab.po
index 245ebe51631..1d1fe60f062 100644
--- a/locale/gl_ES/gitlab.po
+++ b/locale/gl_ES/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: gl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:21\n"
+"PO-Revision-Date: 2022-12-10 06:42\n"
msgid " %{start} to %{end}"
msgstr " %{start} a %{end}"
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/he_IL/gitlab.po b/locale/he_IL/gitlab.po
index 815163032a0..bd3b4c44b48 100644
--- a/locale/he_IL/gitlab.po
+++ b/locale/he_IL/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: he\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:23\n"
+"PO-Revision-Date: 2022-12-10 06:40\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -451,6 +451,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -666,6 +673,12 @@ msgstr[3] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -886,13 +899,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -1088,9 +1101,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -1143,7 +1153,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1318,6 +1328,13 @@ msgstr ""
msgid "%{text} is available"
msgstr "%{text} זמין"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1357,6 +1374,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1478,12 +1498,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr "(×ין שינויי×)"
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2630,6 +2656,9 @@ msgstr "הוספת התליית מערכת"
msgid "Add text to the sign-in page. Markdown enabled."
msgstr "הוספת טקסט לעמוד הכניסה. ×פשר Markdown."
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr "הוספה ללוח"
@@ -2717,6 +2746,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "דקות נוספות"
@@ -2912,6 +2944,9 @@ msgstr "עצירת המשימות נכשלה"
msgid "AdminArea|Total users"
msgstr "סך כל המשתמשי×"
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr "משתמשי×"
@@ -3212,7 +3247,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3275,9 +3310,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4806,6 +4838,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4923,9 +4958,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5480,6 +5512,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5894,6 +5929,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5939,6 +5977,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -6170,9 +6211,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -7200,6 +7238,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7314,9 +7361,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7326,6 +7370,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7347,7 +7394,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7368,9 +7418,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7392,6 +7439,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7425,6 +7475,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7437,6 +7493,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7491,6 +7550,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -8299,6 +8361,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8347,6 +8412,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8472,9 +8540,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8490,7 +8555,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8514,6 +8579,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8757,9 +8825,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8772,6 +8846,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8784,6 +8861,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8793,6 +8873,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8802,6 +8885,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9497,6 +9586,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10320,6 +10412,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10344,6 +10439,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10359,15 +10457,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10533,7 +10640,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10653,6 +10760,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10692,12 +10802,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10968,9 +11072,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -11154,9 +11255,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11469,6 +11567,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11601,9 +11702,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11682,6 +11780,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11718,6 +11819,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11943,6 +12080,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12355,12 +12495,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12373,9 +12519,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12394,6 +12552,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12427,6 +12588,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12487,6 +12651,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12634,6 +12801,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -13007,6 +13177,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -13064,6 +13237,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13868,6 +14047,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13904,6 +14086,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -14010,9 +14195,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -14179,6 +14370,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15500,6 +15694,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15863,9 +16060,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15959,6 +16153,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -16274,6 +16471,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16437,6 +16643,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16467,9 +16676,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16488,9 +16709,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16545,13 +16763,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16871,9 +17092,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -17030,9 +17248,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -17289,6 +17504,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17508,15 +17726,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17553,6 +17762,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17697,10 +17909,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17782,13 +18003,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17831,6 +18045,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18682,6 +18899,13 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18931,6 +19155,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18958,6 +19185,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -19135,6 +19365,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -19228,6 +19461,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19417,7 +19656,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19504,6 +19743,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19609,6 +19851,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19780,7 +20025,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19801,9 +20046,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19933,6 +20175,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19942,9 +20187,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20426,12 +20668,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20465,9 +20713,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20636,6 +20881,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20660,6 +20908,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20699,7 +20950,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20741,6 +20992,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20753,9 +21010,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -21107,6 +21373,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21674,12 +21943,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21998,6 +22273,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -22272,6 +22550,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -22305,6 +22586,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22653,6 +22937,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22701,7 +22988,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -23170,9 +23457,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -23311,9 +23595,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23746,9 +24027,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23899,6 +24177,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23980,6 +24261,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -24049,9 +24333,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -24128,15 +24409,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -24225,6 +24509,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -24330,6 +24617,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -25017,10 +25307,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -25071,6 +25361,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -25176,6 +25469,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25674,6 +25970,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25695,6 +25994,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26830,6 +27135,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26893,9 +27201,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -27107,6 +27412,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -27288,6 +27596,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -27330,6 +27641,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27603,9 +27920,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27705,6 +28019,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -28373,9 +28690,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28758,6 +29090,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28837,9 +29172,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28945,9 +29277,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -29233,6 +29562,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -29324,6 +29656,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -29339,6 +29674,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -29363,6 +29704,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29435,6 +29788,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29450,6 +29806,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29460,6 +29819,9 @@ msgstr[3] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29514,6 +29876,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29562,9 +29927,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29587,6 +29949,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29602,6 +29971,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29671,6 +30043,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29734,6 +30109,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29977,6 +30361,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30892,6 +31300,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -31048,9 +31459,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -31204,6 +31612,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -31288,6 +31699,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -31300,9 +31714,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -31420,16 +31840,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31792,6 +32221,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -32326,12 +32761,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -32413,9 +32854,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32530,6 +32968,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32575,6 +33016,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32704,7 +33148,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32785,6 +33229,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -33307,9 +33754,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33983,6 +34427,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -34182,6 +34629,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34521,6 +34971,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34575,10 +35028,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34929,6 +35379,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -35180,6 +35633,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -35256,7 +35712,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -35353,6 +35809,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -35419,7 +35878,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -35446,6 +35905,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35467,6 +35929,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35552,6 +36017,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35570,6 +36038,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35609,12 +36080,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35709,6 +36183,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35775,6 +36252,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35823,12 +36303,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35944,6 +36418,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35971,13 +36448,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -36019,6 +36499,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -36034,6 +36517,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -36100,6 +36592,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -36127,9 +36625,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -36142,18 +36646,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -36238,6 +36751,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36777,6 +37293,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36834,9 +37353,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36852,6 +37380,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36888,6 +37419,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36960,6 +37494,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36969,12 +37506,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -37044,6 +37590,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -37071,6 +37620,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -37107,6 +37659,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -37116,9 +37671,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -37263,6 +37833,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37488,6 +38061,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37593,6 +38169,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -38083,6 +38662,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -38209,6 +38791,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -38369,9 +38954,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38525,7 +39107,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38540,13 +39122,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38570,13 +39155,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38714,10 +39302,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38759,6 +39347,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38777,6 +39368,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38846,6 +39440,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -39239,6 +39836,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -39344,6 +39944,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -39407,6 +40010,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -40146,6 +40752,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -40236,9 +40845,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40521,6 +41127,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -41111,6 +41723,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -41479,9 +42094,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41539,9 +42151,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41587,6 +42196,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41707,6 +42319,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41746,6 +42361,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41839,9 +42457,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -42031,6 +42646,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -42049,7 +42667,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42523,6 +43141,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42777,6 +43401,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42810,9 +43437,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42927,6 +43551,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -43020,6 +43647,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -43053,6 +43683,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -43068,13 +43701,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -43089,22 +43722,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -43477,6 +44110,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43513,6 +44152,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43690,6 +44332,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43813,6 +44461,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43894,6 +44545,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -44077,6 +44731,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -44095,18 +44752,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -44134,9 +44785,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -44146,9 +44794,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -44275,10 +44920,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -44287,12 +44929,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -44305,9 +44941,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -44341,7 +44974,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -44350,16 +44983,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44533,6 +45166,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44801,9 +45437,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44993,6 +45626,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -45065,6 +45701,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -45083,6 +45722,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -45131,10 +45773,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -45146,7 +45788,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -45203,6 +45845,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -45212,6 +45869,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45878,6 +46547,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45953,9 +46625,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45971,24 +46640,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -46019,6 +46676,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -46085,9 +46754,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -46136,13 +46802,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -46181,9 +46844,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46579,6 +47239,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46588,9 +47251,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46609,6 +47269,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46624,9 +47290,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46649,6 +47312,9 @@ msgstr[3] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46658,6 +47324,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46670,16 +47342,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46688,12 +47363,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46703,12 +47384,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46721,9 +47408,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46787,7 +47483,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46817,6 +47513,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46986,10 +47685,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -47028,13 +47727,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -47101,9 +47793,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -47122,18 +47811,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -47215,6 +47898,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -47269,6 +47955,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -47333,6 +48022,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -47384,9 +48076,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47564,6 +48253,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47582,7 +48274,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47618,6 +48310,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47763,13 +48458,6 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "Your groups"
msgstr ""
@@ -47809,6 +48497,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47969,6 +48666,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -48152,6 +48852,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -48207,6 +48910,20 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -48264,10 +48981,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -48432,7 +49152,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48614,6 +49334,13 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48775,9 +49502,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48968,6 +49692,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -49007,6 +49734,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -49510,6 +50243,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -49519,10 +50255,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49732,6 +50468,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49791,6 +50530,13 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "running"
msgstr ""
diff --git a/locale/hi_IN/gitlab.po b/locale/hi_IN/gitlab.po
index 118b32160e5..405a4f52574 100644
--- a/locale/hi_IN/gitlab.po
+++ b/locale/hi_IN/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: hi\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:23\n"
+"PO-Revision-Date: 2022-12-10 06:42\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/hr_HR/gitlab.po b/locale/hr_HR/gitlab.po
index 73d481002f7..723c0e26ce9 100644
--- a/locale/hr_HR/gitlab.po
+++ b/locale/hr_HR/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: hr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:21\n"
+"PO-Revision-Date: 2022-12-10 06:42\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -397,6 +397,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -586,6 +592,12 @@ msgstr[2] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -796,13 +808,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -997,9 +1009,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -1051,7 +1060,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1213,6 +1222,12 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1252,6 +1267,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1372,12 +1390,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2497,6 +2521,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2584,6 +2611,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2779,6 +2809,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -3079,7 +3112,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3142,9 +3175,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4672,6 +4702,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4789,9 +4822,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5338,6 +5368,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5749,6 +5782,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5794,6 +5830,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -6025,9 +6064,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -7051,6 +7087,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7165,9 +7210,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7177,6 +7219,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7198,7 +7243,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7219,9 +7267,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7243,6 +7288,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7276,6 +7324,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7288,6 +7342,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7342,6 +7399,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -8149,6 +8209,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8197,6 +8260,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8320,9 +8386,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8338,7 +8401,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8362,6 +8425,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8605,9 +8671,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8620,6 +8692,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8632,6 +8707,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8641,6 +8719,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8650,6 +8731,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9343,6 +9430,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10165,6 +10255,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10189,6 +10282,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10204,15 +10300,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10378,7 +10483,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10498,6 +10603,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10537,12 +10645,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10810,9 +10912,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10996,9 +11095,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11311,6 +11407,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11443,9 +11542,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11524,6 +11620,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11560,6 +11659,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11785,6 +11920,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12196,12 +12334,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12214,9 +12358,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12235,6 +12391,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12268,6 +12427,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12328,6 +12490,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12475,6 +12640,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12847,6 +13015,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12904,6 +13075,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13702,6 +13879,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13738,6 +13918,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13843,9 +14026,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -14011,6 +14200,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15328,6 +15520,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15691,9 +15886,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15787,6 +15979,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -16102,6 +16297,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16264,6 +16468,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16294,9 +16501,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16315,9 +16534,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16372,13 +16588,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16696,9 +16915,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16855,9 +17071,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -17113,6 +17326,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17332,15 +17548,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17377,6 +17584,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17521,10 +17731,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17605,12 +17824,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17653,6 +17866,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18502,6 +18718,12 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18751,6 +18973,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18778,6 +19003,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18955,6 +19183,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -19048,6 +19279,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19237,7 +19474,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19324,6 +19561,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19429,6 +19669,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19600,7 +19843,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19621,9 +19864,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19753,6 +19993,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19762,9 +20005,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20242,12 +20482,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20281,9 +20527,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20452,6 +20695,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20476,6 +20722,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20515,7 +20764,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20557,6 +20806,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20569,9 +20824,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20920,6 +21184,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21487,12 +21754,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21811,6 +22084,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -22084,6 +22360,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -22117,6 +22396,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22465,6 +22747,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22513,7 +22798,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22981,9 +23266,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -23122,9 +23404,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23557,9 +23836,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23710,6 +23986,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23791,6 +24070,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23860,9 +24142,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23938,15 +24217,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -24034,6 +24316,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -24139,6 +24424,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24820,10 +25108,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24874,6 +25162,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24979,6 +25270,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25477,6 +25771,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25498,6 +25795,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26629,6 +26932,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26692,9 +26998,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26905,6 +27208,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -27085,6 +27391,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -27127,6 +27436,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27400,9 +27715,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27502,6 +27814,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -28165,9 +28480,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28549,6 +28879,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28627,9 +28960,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28735,9 +29065,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -29023,6 +29350,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -29113,6 +29443,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -29128,6 +29461,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -29152,6 +29491,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29224,6 +29575,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29239,6 +29593,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29248,6 +29605,9 @@ msgstr[2] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29302,6 +29662,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29350,9 +29713,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29374,6 +29734,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29389,6 +29755,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29458,6 +29827,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29521,6 +29893,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29764,6 +30145,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30679,6 +31084,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30835,9 +31243,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30991,6 +31396,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -31075,6 +31483,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -31087,9 +31498,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -31207,16 +31624,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31579,6 +32005,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -32113,12 +32545,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -32200,9 +32638,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32317,6 +32752,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32362,6 +32800,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32491,7 +32932,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32572,6 +33013,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -33094,9 +33538,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33769,6 +34210,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33967,6 +34411,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34306,6 +34753,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34360,10 +34810,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34708,6 +35155,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34957,6 +35407,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -35032,7 +35485,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -35128,6 +35581,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -35191,7 +35647,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -35218,6 +35674,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35239,6 +35698,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35323,6 +35785,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35341,6 +35806,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35380,12 +35848,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35479,6 +35950,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35545,6 +36019,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35593,12 +36070,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35713,6 +36184,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35740,13 +36214,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35788,6 +36265,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35803,6 +36283,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35869,6 +36358,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35896,9 +36391,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35911,18 +36412,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -36007,6 +36517,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36535,6 +37048,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36592,9 +37108,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36610,6 +37135,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36646,6 +37174,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36718,6 +37249,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36727,12 +37261,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36802,6 +37345,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36829,6 +37375,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36865,6 +37414,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36874,9 +37426,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -37021,6 +37588,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37246,6 +37816,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37351,6 +37924,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37840,6 +38416,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37966,6 +38545,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -38125,9 +38707,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38281,7 +38860,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38296,13 +38875,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38326,13 +38908,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38470,10 +39055,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38515,6 +39100,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38533,6 +39121,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38602,6 +39193,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38995,6 +39589,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -39100,6 +39697,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -39163,6 +39763,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39901,6 +40504,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39991,9 +40597,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40276,6 +40879,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40861,6 +41470,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -41227,9 +41839,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41287,9 +41896,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41335,6 +41941,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41455,6 +42064,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41494,6 +42106,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41587,9 +42202,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41779,6 +42391,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41797,7 +42412,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42271,6 +42886,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42523,6 +43144,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42556,9 +43180,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42673,6 +43294,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42766,6 +43390,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42799,6 +43426,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42814,13 +43444,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42835,22 +43465,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -43222,6 +43852,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43258,6 +43894,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43435,6 +44074,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43558,6 +44203,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43639,6 +44287,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43822,6 +44473,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43840,18 +44494,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43879,9 +44527,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43891,9 +44536,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -44020,10 +44662,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -44032,12 +44671,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -44050,9 +44683,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -44086,7 +44716,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -44095,16 +44725,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44278,6 +44908,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44545,9 +45178,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44737,6 +45367,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44809,6 +45442,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44827,6 +45463,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44875,10 +45514,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44890,7 +45529,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44947,6 +45586,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44956,6 +45610,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45619,6 +46285,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45694,9 +46363,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45712,24 +46378,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45760,6 +46414,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45826,9 +46492,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45877,13 +46540,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45922,9 +46582,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46318,6 +46975,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46327,9 +46987,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46348,6 +47005,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46363,9 +47026,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46387,6 +47047,9 @@ msgstr[2] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46396,6 +47059,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46408,16 +47077,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46426,12 +47098,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46441,12 +47119,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46459,9 +47143,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46525,7 +47218,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46555,6 +47248,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46723,10 +47419,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46765,12 +47461,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46837,9 +47527,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46858,18 +47545,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46951,6 +47632,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -47005,6 +47689,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -47068,6 +47755,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -47119,9 +47809,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47299,6 +47986,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47317,7 +48007,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47353,6 +48043,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47497,12 +48190,6 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "Your groups"
msgstr ""
@@ -47542,6 +48229,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47701,6 +48397,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47881,6 +48580,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47935,6 +48637,18 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47992,10 +48706,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -48157,7 +48874,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48337,6 +49054,12 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48496,9 +49219,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48685,6 +49405,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48724,6 +49447,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -49222,6 +49951,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -49231,10 +49963,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49441,6 +50173,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49498,6 +50233,12 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "running"
msgstr ""
diff --git a/locale/hu_HU/gitlab.po b/locale/hu_HU/gitlab.po
index ccb247daedf..ca6b0ac074d 100644
--- a/locale/hu_HU/gitlab.po
+++ b/locale/hu_HU/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: hu\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:22\n"
+"PO-Revision-Date: 2022-12-10 06:40\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/hy_AM/gitlab.po b/locale/hy_AM/gitlab.po
index d5bebf0567f..db4136d878f 100644
--- a/locale/hy_AM/gitlab.po
+++ b/locale/hy_AM/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: hy-AM\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:23\n"
+"PO-Revision-Date: 2022-12-10 06:40\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/id_ID/gitlab.po b/locale/id_ID/gitlab.po
index f687bce2bee..6692a9fd2d2 100644
--- a/locale/id_ID/gitlab.po
+++ b/locale/id_ID/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: id\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:21\n"
+"PO-Revision-Date: 2022-12-10 06:42\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -289,6 +289,10 @@ msgid "%d more comment"
msgid_plural "%d more comments"
msgstr[0] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -426,6 +430,12 @@ msgstr[0] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -616,13 +626,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -815,9 +825,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -867,7 +874,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1003,6 +1010,10 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1042,6 +1053,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1160,12 +1174,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2231,6 +2251,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2318,6 +2341,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2513,6 +2539,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2813,7 +2842,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -2876,9 +2905,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4404,6 +4430,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4521,9 +4550,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5054,6 +5080,9 @@ msgid "Are you sure you want to import %d repository?"
msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5459,6 +5488,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5504,6 +5536,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5735,9 +5770,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6753,6 +6785,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -6867,9 +6908,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -6879,6 +6917,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -6900,7 +6941,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -6921,9 +6965,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -6945,6 +6986,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -6978,6 +7022,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -6990,6 +7040,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7044,6 +7097,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7849,6 +7905,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -7897,6 +7956,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8016,9 +8078,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8034,7 +8093,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8058,6 +8117,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8301,9 +8363,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8316,6 +8384,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8328,6 +8399,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8337,6 +8411,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8346,6 +8423,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9035,6 +9118,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -9855,6 +9941,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -9879,6 +9968,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -9894,15 +9986,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10068,7 +10169,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10188,6 +10289,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10227,12 +10331,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10494,9 +10592,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10680,9 +10775,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -10995,6 +11087,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11127,9 +11222,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11208,6 +11300,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11244,6 +11339,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11469,6 +11600,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -11878,12 +12012,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -11896,9 +12036,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -11917,6 +12069,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -11950,6 +12105,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12010,6 +12168,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12157,6 +12318,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12527,6 +12691,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12584,6 +12751,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13370,6 +13543,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13406,6 +13582,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13509,9 +13688,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13675,6 +13860,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -14984,6 +15172,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15347,9 +15538,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15443,6 +15631,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15758,6 +15949,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -15918,6 +16118,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -15948,9 +16151,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -15969,9 +16184,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16026,13 +16238,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16346,9 +16561,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16505,9 +16717,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16761,6 +16970,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -16980,15 +17192,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17025,6 +17228,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17169,10 +17375,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17251,10 +17466,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17297,6 +17508,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18142,6 +18356,10 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18391,6 +18609,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18418,6 +18639,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18595,6 +18819,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18688,6 +18915,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -18877,7 +19110,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -18964,6 +19197,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19069,6 +19305,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19240,7 +19479,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19261,9 +19500,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19393,6 +19629,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19402,9 +19641,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -19874,12 +20110,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -19913,9 +20155,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20084,6 +20323,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20108,6 +20350,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20147,7 +20392,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20189,6 +20434,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20201,9 +20452,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20546,6 +20806,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21113,12 +21376,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21437,6 +21706,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21708,6 +21980,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21741,6 +22016,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22089,6 +22367,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22137,7 +22418,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22603,9 +22884,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22744,9 +23022,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23179,9 +23454,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23332,6 +23604,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23413,6 +23688,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23482,9 +23760,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23558,15 +23833,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23652,6 +23930,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23757,6 +24038,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24426,10 +24710,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24480,6 +24764,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24585,6 +24872,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25083,6 +25373,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25104,6 +25397,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26227,6 +26526,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26290,9 +26592,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26501,6 +26800,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26679,6 +26981,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26721,6 +27026,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -26994,9 +27305,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27096,6 +27404,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27749,9 +28060,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28131,6 +28457,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28207,9 +28536,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28315,9 +28641,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28603,6 +28926,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28691,6 +29017,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28706,6 +29035,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28730,6 +29065,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -28802,6 +29149,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -28817,6 +29167,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28824,6 +29177,9 @@ msgstr[0] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -28878,6 +29234,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -28926,9 +29285,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -28948,6 +29304,10 @@ msgid "PackageRegistry|You are about to delete 1 asset. This operation is irreve
msgid_plural "PackageRegistry|You are about to delete %d assets. This operation is irreversible."
msgstr[0] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -28963,6 +29323,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29032,6 +29395,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29095,6 +29461,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29338,6 +29713,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30253,6 +30652,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30409,9 +30811,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30565,6 +30964,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30649,6 +31051,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30661,9 +31066,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30781,16 +31192,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31153,6 +31573,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31687,12 +32113,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31774,9 +32206,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -31891,6 +32320,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -31936,6 +32368,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32065,7 +32500,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32146,6 +32581,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32668,9 +33106,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33341,6 +33776,9 @@ msgid "Refreshing in a second to show the updated status..."
msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33537,6 +33975,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -33876,6 +34317,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -33930,10 +34374,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34266,6 +34707,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34511,6 +34955,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34584,7 +35031,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34678,6 +35125,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34735,7 +35185,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34762,6 +35212,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -34783,6 +35236,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -34865,6 +35321,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -34883,6 +35342,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -34922,12 +35384,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35019,6 +35484,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35085,6 +35553,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35133,12 +35604,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35251,6 +35716,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35278,13 +35746,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35326,6 +35797,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35341,6 +35815,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35407,6 +35890,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35434,9 +35923,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35449,18 +35944,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35545,6 +36049,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36051,6 +36558,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36108,9 +36618,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36126,6 +36645,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36162,6 +36684,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36234,6 +36759,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36243,12 +36771,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36318,6 +36855,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36345,6 +36885,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36381,6 +36924,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36390,9 +36936,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36537,6 +37098,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -36762,6 +37326,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -36867,6 +37434,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37354,6 +37924,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37480,6 +38053,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37637,9 +38213,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -37793,7 +38366,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -37808,13 +38381,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -37838,13 +38414,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -37982,10 +38561,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38027,6 +38606,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38045,6 +38627,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38114,6 +38699,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38507,6 +39095,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38612,6 +39203,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38675,6 +39269,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39411,6 +40008,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39501,9 +40101,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -39786,6 +40383,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40361,6 +40964,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40723,9 +41329,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -40783,9 +41386,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -40831,6 +41431,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -40951,6 +41554,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -40990,6 +41596,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41083,9 +41692,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41275,6 +41881,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41293,7 +41902,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -41767,6 +42376,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42015,6 +42630,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42048,9 +42666,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42165,6 +42780,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42258,6 +42876,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42291,6 +42912,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42306,13 +42930,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42327,22 +42951,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42712,6 +43336,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -42748,6 +43378,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -42925,6 +43558,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43048,6 +43687,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43129,6 +43771,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43312,6 +43957,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43330,18 +43978,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43369,9 +44011,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43381,9 +44020,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43510,10 +44146,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43522,12 +44155,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43540,9 +44167,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43576,7 +44200,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43585,16 +44209,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -43768,6 +44392,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44033,9 +44660,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44225,6 +44849,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44297,6 +44924,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44315,6 +44945,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44363,10 +44996,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44378,7 +45011,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44435,6 +45068,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44444,6 +45092,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45101,6 +45761,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45176,9 +45839,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45194,24 +45854,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45242,6 +45890,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45308,9 +45968,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45359,13 +46016,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push events"
-msgstr ""
-
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45404,9 +46058,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -45796,6 +46447,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -45805,9 +46459,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -45826,6 +46477,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -45841,9 +46498,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -45863,6 +46517,9 @@ msgstr[0] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -45872,6 +46529,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -45884,16 +46547,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -45902,12 +46568,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -45917,12 +46589,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -45935,9 +46613,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46001,7 +46688,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46031,6 +46718,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46197,10 +46887,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46239,10 +46929,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46309,9 +46995,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46330,18 +47013,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46423,6 +47100,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46477,6 +47157,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46538,6 +47221,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46589,9 +47275,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -46769,6 +47452,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -46787,7 +47473,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -46823,6 +47509,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -46965,10 +47654,6 @@ msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-
msgid "Your groups"
msgstr ""
@@ -47008,6 +47693,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47165,6 +47859,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47339,6 +48036,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47391,6 +48091,14 @@ msgid "change"
msgid_plural "changes"
msgstr[0] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47448,10 +48156,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47607,7 +48318,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -47783,6 +48494,10 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -47938,9 +48653,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48119,6 +48831,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48158,6 +48873,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48646,6 +49367,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48655,10 +49379,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -48859,6 +49583,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -48912,6 +49639,10 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+
msgid "running"
msgstr ""
diff --git a/locale/ig_NG/gitlab.po b/locale/ig_NG/gitlab.po
index 85bda778a68..eef691d8134 100644
--- a/locale/ig_NG/gitlab.po
+++ b/locale/ig_NG/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: ig\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:24\n"
+"PO-Revision-Date: 2022-12-10 06:44\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -289,6 +289,10 @@ msgid "%d more comment"
msgid_plural "%d more comments"
msgstr[0] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -426,6 +430,12 @@ msgstr[0] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -616,13 +626,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -815,9 +825,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -867,7 +874,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1003,6 +1010,10 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1042,6 +1053,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1160,12 +1174,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2231,6 +2251,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2318,6 +2341,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2513,6 +2539,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2813,7 +2842,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -2876,9 +2905,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4404,6 +4430,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4521,9 +4550,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5054,6 +5080,9 @@ msgid "Are you sure you want to import %d repository?"
msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5459,6 +5488,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5504,6 +5536,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5735,9 +5770,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6753,6 +6785,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -6867,9 +6908,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -6879,6 +6917,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -6900,7 +6941,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -6921,9 +6965,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -6945,6 +6986,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -6978,6 +7022,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -6990,6 +7040,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7044,6 +7097,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7849,6 +7905,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -7897,6 +7956,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8016,9 +8078,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8034,7 +8093,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8058,6 +8117,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8301,9 +8363,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8316,6 +8384,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8328,6 +8399,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8337,6 +8411,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8346,6 +8423,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9035,6 +9118,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -9855,6 +9941,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -9879,6 +9968,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -9894,15 +9986,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10068,7 +10169,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10188,6 +10289,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10227,12 +10331,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10494,9 +10592,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10680,9 +10775,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -10995,6 +11087,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11127,9 +11222,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11208,6 +11300,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11244,6 +11339,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11469,6 +11600,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -11878,12 +12012,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -11896,9 +12036,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -11917,6 +12069,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -11950,6 +12105,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12010,6 +12168,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12157,6 +12318,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12527,6 +12691,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12584,6 +12751,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13370,6 +13543,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13406,6 +13582,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13509,9 +13688,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13675,6 +13860,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -14984,6 +15172,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15347,9 +15538,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15443,6 +15631,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15758,6 +15949,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -15918,6 +16118,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -15948,9 +16151,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -15969,9 +16184,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16026,13 +16238,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16346,9 +16561,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16505,9 +16717,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16761,6 +16970,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -16980,15 +17192,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17025,6 +17228,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17169,10 +17375,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17251,10 +17466,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17297,6 +17508,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18142,6 +18356,10 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18391,6 +18609,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18418,6 +18639,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18595,6 +18819,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18688,6 +18915,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -18877,7 +19110,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -18964,6 +19197,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19069,6 +19305,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19240,7 +19479,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19261,9 +19500,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19393,6 +19629,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19402,9 +19641,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -19874,12 +20110,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -19913,9 +20155,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20084,6 +20323,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20108,6 +20350,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20147,7 +20392,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20189,6 +20434,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20201,9 +20452,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20546,6 +20806,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21113,12 +21376,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21437,6 +21706,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21708,6 +21980,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21741,6 +22016,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22089,6 +22367,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22137,7 +22418,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22603,9 +22884,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22744,9 +23022,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23179,9 +23454,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23332,6 +23604,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23413,6 +23688,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23482,9 +23760,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23558,15 +23833,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23652,6 +23930,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23757,6 +24038,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24426,10 +24710,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24480,6 +24764,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24585,6 +24872,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25083,6 +25373,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25104,6 +25397,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26227,6 +26526,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26290,9 +26592,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26501,6 +26800,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26679,6 +26981,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26721,6 +27026,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -26994,9 +27305,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27096,6 +27404,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27749,9 +28060,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28131,6 +28457,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28207,9 +28536,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28315,9 +28641,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28603,6 +28926,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28691,6 +29017,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28706,6 +29035,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28730,6 +29065,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -28802,6 +29149,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -28817,6 +29167,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28824,6 +29177,9 @@ msgstr[0] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -28878,6 +29234,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -28926,9 +29285,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -28948,6 +29304,10 @@ msgid "PackageRegistry|You are about to delete 1 asset. This operation is irreve
msgid_plural "PackageRegistry|You are about to delete %d assets. This operation is irreversible."
msgstr[0] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -28963,6 +29323,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29032,6 +29395,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29095,6 +29461,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29338,6 +29713,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30253,6 +30652,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30409,9 +30811,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30565,6 +30964,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30649,6 +31051,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30661,9 +31066,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30781,16 +31192,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31153,6 +31573,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31687,12 +32113,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31774,9 +32206,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -31891,6 +32320,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -31936,6 +32368,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32065,7 +32500,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32146,6 +32581,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32668,9 +33106,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33341,6 +33776,9 @@ msgid "Refreshing in a second to show the updated status..."
msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33537,6 +33975,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -33876,6 +34317,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -33930,10 +34374,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34266,6 +34707,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34511,6 +34955,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34584,7 +35031,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34678,6 +35125,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34735,7 +35185,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34762,6 +35212,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -34783,6 +35236,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -34865,6 +35321,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -34883,6 +35342,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -34922,12 +35384,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35019,6 +35484,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35085,6 +35553,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35133,12 +35604,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35251,6 +35716,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35278,13 +35746,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35326,6 +35797,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35341,6 +35815,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35407,6 +35890,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35434,9 +35923,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35449,18 +35944,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35545,6 +36049,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36051,6 +36558,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36108,9 +36618,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36126,6 +36645,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36162,6 +36684,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36234,6 +36759,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36243,12 +36771,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36318,6 +36855,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36345,6 +36885,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36381,6 +36924,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36390,9 +36936,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36537,6 +37098,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -36762,6 +37326,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -36867,6 +37434,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37354,6 +37924,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37480,6 +38053,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37637,9 +38213,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -37793,7 +38366,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -37808,13 +38381,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -37838,13 +38414,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -37982,10 +38561,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38027,6 +38606,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38045,6 +38627,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38114,6 +38699,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38507,6 +39095,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38612,6 +39203,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38675,6 +39269,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39411,6 +40008,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39501,9 +40101,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -39786,6 +40383,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40361,6 +40964,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40723,9 +41329,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -40783,9 +41386,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -40831,6 +41431,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -40951,6 +41554,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -40990,6 +41596,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41083,9 +41692,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41275,6 +41881,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41293,7 +41902,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -41767,6 +42376,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42015,6 +42630,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42048,9 +42666,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42165,6 +42780,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42258,6 +42876,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42291,6 +42912,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42306,13 +42930,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42327,22 +42951,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42712,6 +43336,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -42748,6 +43378,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -42925,6 +43558,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43048,6 +43687,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43129,6 +43771,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43312,6 +43957,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43330,18 +43978,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43369,9 +44011,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43381,9 +44020,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43510,10 +44146,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43522,12 +44155,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43540,9 +44167,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43576,7 +44200,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43585,16 +44209,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -43768,6 +44392,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44033,9 +44660,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44225,6 +44849,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44297,6 +44924,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44315,6 +44945,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44363,10 +44996,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44378,7 +45011,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44435,6 +45068,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44444,6 +45092,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45101,6 +45761,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45176,9 +45839,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45194,24 +45854,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45242,6 +45890,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45308,9 +45968,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45359,13 +46016,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push events"
-msgstr ""
-
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45404,9 +46058,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -45796,6 +46447,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -45805,9 +46459,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -45826,6 +46477,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -45841,9 +46498,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -45863,6 +46517,9 @@ msgstr[0] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -45872,6 +46529,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -45884,16 +46547,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -45902,12 +46568,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -45917,12 +46589,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -45935,9 +46613,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46001,7 +46688,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46031,6 +46718,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46197,10 +46887,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46239,10 +46929,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46309,9 +46995,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46330,18 +47013,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46423,6 +47100,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46477,6 +47157,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46538,6 +47221,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46589,9 +47275,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -46769,6 +47452,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -46787,7 +47473,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -46823,6 +47509,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -46965,10 +47654,6 @@ msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-
msgid "Your groups"
msgstr ""
@@ -47008,6 +47693,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47165,6 +47859,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47339,6 +48036,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47391,6 +48091,14 @@ msgid "change"
msgid_plural "changes"
msgstr[0] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47448,10 +48156,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47607,7 +48318,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -47783,6 +48494,10 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -47938,9 +48653,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48119,6 +48831,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48158,6 +48873,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48646,6 +49367,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48655,10 +49379,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -48859,6 +49583,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -48912,6 +49639,10 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+
msgid "running"
msgstr ""
diff --git a/locale/is_IS/gitlab.po b/locale/is_IS/gitlab.po
index d62a0263f40..96abee72767 100644
--- a/locale/is_IS/gitlab.po
+++ b/locale/is_IS/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: is\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:21\n"
+"PO-Revision-Date: 2022-12-10 06:42\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/it/gitlab.po b/locale/it/gitlab.po
index f41760b8066..bc7253c1c57 100644
--- a/locale/it/gitlab.po
+++ b/locale/it/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: it\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:23\n"
+"PO-Revision-Date: 2022-12-10 06:40\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] "%d altro commento"
msgstr[1] "Altri %d commenti"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr "%{text} è disponibile"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr "(verifica progresso)"
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr "Si è verificato un errore. Riprova."
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "Ago"
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "Branch"
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,8 +7092,11 @@ msgstr ""
msgid "Branches|Compare"
msgstr "Confronta"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "Elimina tutte le branches che sono state mergiate in '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
+msgstr ""
msgid "Branches|Delete branch"
msgstr "Elimina Branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "Eliminare le branch mergiate è un'operazione irreversibile. Sicuro di voler procedere?"
-
msgid "Branches|Filter by branch name"
msgstr "Filtra per nome branch"
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr "La branch predefinita non può esser eliminata"
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "Controllo disponibilità per %{text}…"
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr "in corso"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr "Tag"
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr "Feb"
msgid "February"
msgstr "Febbraio"
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr "Dalla creazione di un issue fino al rilascio in produzione"
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr "Membri"
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr "Nuova Branch"
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "Nuovo Issue"
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr "Si apre in una nuova finestra"
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr "Richiedi accesso"
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr "Cambia branch/tag"
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr "Ritira richiesta d'accesso"
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr "Il tuo nome"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/it/gitlab.po.time_stamp b/locale/it/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/it/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/ja/gitlab.po b/locale/ja/gitlab.po
index 3f5ba8e628e..86a519d4a64 100644
--- a/locale/ja/gitlab.po
+++ b/locale/ja/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: ja\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:23\n"
+"PO-Revision-Date: 2022-12-10 06:40\n"
msgid " %{start} to %{end}"
msgstr " %{start} ã‹ã‚‰ %{end} ã¾ã§"
@@ -289,6 +289,10 @@ msgid "%d more comment"
msgid_plural "%d more comments"
msgstr[0] "%d 件以上ã®ã‚³ãƒ¡ãƒ³ãƒˆ"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%d 件ã®ã‚³ãƒ¡ãƒ³ãƒˆãŒä¿ç•™ä¸­"
@@ -426,6 +430,12 @@ msgstr[0] "%{bold_start}%{count}%{bold_end} 件ã®ã‚ªãƒ¼ãƒ—ンã—ã¦ã„るマー
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr "%{code_open}マスク:%{code_close} ジョブログã§éžè¡¨ç¤ºã«ã—ã¾ã™ã€‚マスキングã®è¦ä»¶ã‚’満ãŸã™å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
@@ -616,15 +626,15 @@ msgstr "%{group_name} ã¯ã‚°ãƒ«ãƒ¼ãƒ—管ç†ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’使用ã—ã¾ã™ã€‚
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr "%{group_name}&%{epic_iid} &middot; %{author} ã«ã‚ˆã‚Š %{epic_created} 作æˆã•ã‚Œã¾ã—ãŸ"
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type} ã¯å‰Šé™¤ã•ã‚Œã¾ã—ãŸ"
-
-msgid "%{hook_type} was scheduled for deletion"
-msgstr "%{hook_type} ã®å‰Šé™¤ãŒã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«ã•ã‚Œã¾ã—ãŸ"
-
msgid "%{host} sign-in from new location"
msgstr "æ–°ã—ã„場所%{host} ã‹ã‚‰ã®ãƒ­ã‚°ã‚¤ãƒ³"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
+msgstr ""
+
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr ""
+
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr "%{integrations_link_start}インテグレーション%{link_end} を利用ã—ã¦ã€ã‚µãƒ¼ãƒ‰ãƒ‘ーティアプリケーションを GitLab ワークフローã«çµ±åˆã§ãã¾ã™ã€‚利用å¯èƒ½ãªã‚¤ãƒ³ãƒ†ã‚°ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãŒè¦ä»¶ã‚’満ãŸã•ãªã„å ´åˆã¯ã€ %{webhooks_link_start}ウェブフック%{link_end} ã®ä½¿ç”¨ã‚’検討ã—ã¦ãã ã•ã„。"
@@ -647,7 +657,7 @@ msgid "%{italic_start}What's new%{italic_end} is inactive and cannot be viewed."
msgstr "%{italic_start}新機能%{italic_end} ã¯éžã‚¢ã‚¯ãƒ†ã‚£ãƒ–ã§ã‚ã‚Šã€è¡¨ç¤ºã§ãã¾ã›ã‚“。"
msgid "%{itemsCount} issues with a limit of %{maxIssueCount}"
-msgstr ""
+msgstr "ä¸Šé™ %{maxIssueCount} 件ã®ã†ã¡ %{itemsCount} 件ã®ã‚¤ã‚·ãƒ¥ãƒ¼"
msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
msgstr "%{labelStart}実際ã®ãƒ¬ã‚¹ãƒãƒ³ã‚¹:%{labelEnd} %{headers}"
@@ -815,9 +825,6 @@ msgstr "%{openedEpics} オープン, %{closedEpics} 完了"
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr "%{openedIssues} オープン, %{closedIssues} 完了"
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -867,7 +874,7 @@ msgstr "%{reportType} %{status}"
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr "%{reportType} ã®æ½œåœ¨çš„㪠%{vulnMessage} ã‚’ %{totalStart}%{total}%{totalEnd} 件検出ã—ã¾ã—ãŸ"
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1003,6 +1010,10 @@ msgstr "%{template_project_id} ã¯ä¸æ˜Žã€ã¾ãŸã¯ç„¡åŠ¹ã§ã™"
msgid "%{text} is available"
msgstr "%{text} ãŒåˆ©ç”¨ã§ãã¾ã™ã€‚"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr "%{timebox_type} ã¯ãƒãƒ¼ãƒ³ã‚¢ãƒƒãƒ—ãƒãƒ£ãƒ¼ãƒˆã‚’サãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“"
@@ -1042,6 +1053,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr "%{type} %{name} åå‰ã®ã¿ã‚’サãƒãƒ¼ãƒˆã—ã¾ã™"
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr "%{userName} (マージã§ãã¾ã›ã‚“)"
@@ -1160,12 +1174,18 @@ msgstr "(+%{count}&nbsp;ルール)"
msgid "(Group Managed Account)"
msgstr "(グループ管ç†ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ)"
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr "(変更ãªã—)"
msgid "(UTC %{offset}) %{timezone}"
msgstr "(UTC %{offset}) %{timezone}"
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr "(進行状æ³ã‚’確èªã™ã‚‹)"
@@ -2231,6 +2251,9 @@ msgstr "システムフックã®è¿½åŠ "
msgid "Add text to the sign-in page. Markdown enabled."
msgstr "サインインページã«ãƒ†ã‚­ã‚¹ãƒˆã‚’追加ã—ã¾ã™ã€‚ マークダウンãŒæœ‰åŠ¹ã«ãªã£ã¦ã„ã¾ã™ã€‚"
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr "ボードã«è¿½åŠ "
@@ -2318,6 +2341,9 @@ msgstr "ã“ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®æ–°æ©Ÿèƒ½"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "ã‚ãªãŸã® GitLab インスタンスã§æ–°ã—ã„アプリケーションを追加ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。権é™ã‚’å–å¾—ã™ã‚‹ã«ã¯ã€GitLab 管ç†è€…ã«é€£çµ¡ã—ã¦ãã ã•ã„。"
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "追加分数"
@@ -2513,6 +2539,9 @@ msgstr "ジョブã®åœæ­¢ã«å¤±æ•—ã—ã¾ã—ãŸ"
msgid "AdminArea|Total users"
msgstr "全ユーザー"
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr "ユーザー"
@@ -2813,7 +2842,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -2876,9 +2905,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -2949,7 +2975,7 @@ msgid "AdminUsers|(Admin)"
msgstr "(管ç†è€…)"
msgid "AdminUsers|(Banned)"
-msgstr "(ブロック済ã¿)"
+msgstr "(BAN)"
msgid "AdminUsers|(Blocked)"
msgstr "(ブロック)"
@@ -3033,13 +3059,13 @@ msgid "AdminUsers|Avatar"
msgstr ""
msgid "AdminUsers|Ban user"
-msgstr ""
+msgstr "BANã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼"
msgid "AdminUsers|Ban user %{username}?"
-msgstr "ユーザー %{username} をブロックã—ã¾ã™ã‹?"
+msgstr "ユーザー %{username} ã‚’BANã—ã¾ã™ã‹?"
msgid "AdminUsers|Banned"
-msgstr "ブロック済ã¿"
+msgstr "BAN"
msgid "AdminUsers|Be added to groups and projects"
msgstr "グループã¨ãƒ—ロジェクトã«è¿½åŠ ã™ã‚‹"
@@ -3141,7 +3167,7 @@ msgid "AdminUsers|LDAP Blocked"
msgstr ""
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
-msgstr ""
+msgstr "BANã•ã‚ŒãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã¤ã„ã¦ã®è©³ç´°ã¯ %{link_start}ã“ã¡ã‚‰%{link_end}。"
msgid "AdminUsers|Limits"
msgstr ""
@@ -3252,10 +3278,10 @@ msgid "AdminUsers|To confirm, type %{username}."
msgstr ""
msgid "AdminUsers|Unban user"
-msgstr "ユーザーã®ãƒ–ロック解除"
+msgstr "ユーザーã®BAN解除"
msgid "AdminUsers|Unban user %{username}?"
-msgstr "ユーザー %{username} ã®ãƒ–ロックを解除ã—ã¾ã™ã‹?"
+msgstr "ユーザー %{username} ã®BANを解除ã—ã¾ã™ã‹?"
msgid "AdminUsers|Unblock"
msgstr "ブロック解除"
@@ -3297,7 +3323,7 @@ msgid "AdminUsers|What does this mean?"
msgstr ""
msgid "AdminUsers|When banned:"
-msgstr ""
+msgstr "BANã•ã‚ŒãŸå ´åˆ:"
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒå†ã³ãƒ­ã‚°ã‚¤ãƒ³ã™ã‚‹ã¨ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯æœ‰åŠ¹ã«ãªã‚Šã¾ã™"
@@ -3327,10 +3353,10 @@ msgid "AdminUsers|You can always unblock their account, their data will remain i
msgstr "アカウントã®ãƒ–ロックをã„ã¤ã§ã‚‚解除ã§ãã¾ã™ã€‚データã¯ãã®ã¾ã¾æ®‹ã‚Šã¾ã™ã€‚"
msgid "AdminUsers|You can ban their account in the future if necessary."
-msgstr ""
+msgstr "å¿…è¦ã«å¿œã˜ã¦ã€ä»Šå¾Œã“れらã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’BANã—ã¾ã™ã€‚"
msgid "AdminUsers|You can unban their account in the future. Their data remains intact."
-msgstr ""
+msgstr "今後ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®BANを解除ã§ãã¾ã™ã€‚データã¯ãã®ã¾ã¾æ®‹ã‚Šã¾ã™ã€‚"
msgid "AdminUsers|You cannot remove your own administrator access."
msgstr ""
@@ -4404,6 +4430,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚å†åº¦ãŠè©¦ã—ãã ã•ã„。"
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4521,9 +4550,6 @@ msgstr "ä»»æ„ã®ãƒžã‚¤ãƒ«ã‚¹ãƒˆãƒ¼ãƒ³"
msgid "Any namespace"
msgstr "ä»»æ„ã®ãƒãƒ¼ãƒ ã‚¹ãƒšãƒ¼ã‚¹"
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr "アプリID"
@@ -5054,6 +5080,9 @@ msgid "Are you sure you want to import %d repository?"
msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr "%{path} をロックã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
@@ -5459,6 +5488,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5504,6 +5536,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "8月"
@@ -5735,9 +5770,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr "利用å¯èƒ½ãª Specific Runner"
@@ -5907,7 +5939,7 @@ msgid "BambooService|The user with API access to the Bamboo server."
msgstr ""
msgid "Banned"
-msgstr ""
+msgstr "BAN"
msgid "Banner message"
msgstr ""
@@ -6195,7 +6227,7 @@ msgid "BillingPlans|Upgrade to Ultimate"
msgstr ""
msgid "BillingPlans|Value stream management"
-msgstr ""
+msgstr "ãƒãƒªãƒ¥ãƒ¼ã‚¹ãƒˆãƒªãƒ¼ãƒ ç®¡ç†"
msgid "BillingPlans|We're here to help."
msgstr ""
@@ -6753,6 +6785,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -6867,9 +6908,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "ブランãƒ"
@@ -6879,6 +6917,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr "アクティブ"
@@ -6900,8 +6941,11 @@ msgstr ""
msgid "Branches|Compare"
msgstr "比較"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "'%{default_branch}'ã«ãƒžãƒ¼ã‚¸ã•ã‚ŒãŸã™ã¹ã¦ã®ãƒ–ランãƒã‚’削除"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
+msgstr ""
msgid "Branches|Delete branch"
msgstr "ブランãƒã‚’削除"
@@ -6921,9 +6965,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "マージã•ã‚ŒãŸãƒ–ランãƒã‚’削除ã™ã‚‹ã¨å…ƒã«ã¯æˆ»ã›ã¾ã›ã‚“。よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
-
msgid "Branches|Filter by branch name"
msgstr "ブランãƒåã§ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼"
@@ -6945,6 +6986,9 @@ msgstr "概è¦"
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr "アクティブブランãƒã‚’表示"
@@ -6978,6 +7022,12 @@ msgstr "デフォルトブランãƒã¯å‰Šé™¤ã§ãã¾ã›ã‚“"
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr "ローカルã®å¤‰æ›´å†…容を破棄ã—ã¦ã€ã‚¢ãƒƒãƒ—ストリームã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã§ãƒ–ランãƒã‚’上書ãã™ã‚‹ã«ã¯ã€ã“ã“ã§ãれらを削除ã—ã€ä¸Šã®ã€Œä»Šã™ãæ›´æ–°ã€ã‚’クリックã—ã¦ãã ã•ã„。"
@@ -6990,6 +7040,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr "ブランム%{branchName} を永久ã«å‰Šé™¤ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚"
@@ -7044,6 +7097,9 @@ msgstr "アーティファクトã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "BuildArtifacts|Loading artifacts"
msgstr "アーティファクトã®ãƒ­ãƒ¼ãƒ‰"
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr "ビルトイン"
@@ -7849,6 +7905,9 @@ msgstr "パイプライン#%{pipeline_id} %{duration} 㮠%{humanized_status}"
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr "パイプライン %{pipeline_link} ã® %{ref_type} %{ref_link} ã«ã‚ˆã‚‹ %{user_combined_name} %{humanized_status}"
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr "ã‚¿ã‚°"
@@ -7897,6 +7956,9 @@ msgstr "既知ã®è„†å¼±æ€§ã«å¯¾ã—ã¦ã€Dockerイメージをãƒã‚§ãƒƒã‚¯ã™ã‚‹
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "%{text} ãŒåˆ©ç”¨å¯èƒ½ã‹ç¢ºèªã—ã¦ã„ã¾ã™â€¦"
@@ -8016,9 +8078,6 @@ msgstr "クレジットカードã®ãƒ•ã‚©ãƒ¼ãƒ èª­ã¿è¾¼ã¿å¤±æ•—ã—ã¾ã—ãŸã€‚
msgid "Checkout|Edit"
msgstr "編集"
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr "有効期é™: %{expirationMonth}/%{expirationYear}"
@@ -8034,7 +8093,7 @@ msgstr "国情報ã®èª­ã¿è¾¼ã¿ã«å¤±æ•—ã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—
msgid "Checkout|Failed to load states. Please try again."
msgstr "都é“府県や州ã®èª­ã¿è¾¼ã¿ã«å¤±æ•—ã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8058,6 +8117,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr "GitLab を使用ã—ã¦ã„る会社åã¾ãŸã¯çµ„ç¹”å"
@@ -8301,9 +8363,15 @@ msgstr "実行中"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr "ç¾åœ¨ã®å€¤ã§ã¯ãƒžã‚¹ã‚¯ã•ã‚ŒãŸå¤‰æ•°ã‚’使用ã§ãã¾ã›ã‚“"
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr "キーを入力"
@@ -8316,6 +8384,9 @@ msgstr "キー"
msgid "CiVariables|Masked"
msgstr "マスク"
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8328,6 +8399,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr "環境変数を削除"
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr "スコープ"
@@ -8337,6 +8411,9 @@ msgstr "ã“ã®å®Ÿè¡Œã§ä½¿ç”¨ã•ã‚Œã‚‹å¤‰æ•°ã®å€¤ã‚’指定ã—ã¾ã™ã€‚%{linkSta
msgid "CiVariables|State"
msgstr "状態"
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr "種類"
@@ -8346,6 +8423,12 @@ msgstr "値"
msgid "CiVariables|Variables"
msgstr "変数"
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr "* (ã™ã¹ã¦ã®ç’°å¢ƒ)"
@@ -9035,6 +9118,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -9495,7 +9581,7 @@ msgid "Code owners"
msgstr "コードオーナー"
msgid "Code review"
-msgstr ""
+msgstr "コードレビュー"
msgid "Code snippet"
msgstr ""
@@ -9585,7 +9671,7 @@ msgid "ColorWidget|Error fetching epic color."
msgstr "エピックカラーã®å–å¾—ã«å¤±æ•—ã—ã¾ã—ãŸã€‚"
msgid "Colorize messages"
-msgstr ""
+msgstr "メッセージã«è‰²ã‚’付ã‘ã‚‹"
msgid "ComboSearch is not defined"
msgstr "ComboSearch ãŒå®šç¾©ã•ã‚Œã¦ã„ã¾ã›ã‚“"
@@ -9855,6 +9941,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -9879,6 +9968,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -9894,15 +9986,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10068,7 +10169,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr "%{link} ã®çµ±åˆã‚’設定ã—ã¾ã™ 。"
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10188,6 +10289,9 @@ msgstr "接続中"
msgid "Connecting to terminal sync service"
msgstr "ターミナルåŒæœŸã‚µãƒ¼ãƒ“スã«æŽ¥ç¶š"
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr "接続ã—ã¦ã„ã¾ã™..."
@@ -10227,12 +10331,6 @@ msgstr "コンテナレジストリã®ã‚¤ãƒ¡ãƒ¼ã‚¸"
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr "コンテナレジストリã¯ã€ã“ã® GitLab インスタンスã§æœ‰åŠ¹ã«ãªã£ã¦ã„ã¾ã›ã‚“。 AutoDevOps ãŒæ©Ÿèƒ½ã™ã‚‹ãŸã‚ã«ã¯ã€ç®¡ç†è€…ã«æœ‰åŠ¹ã«ã™ã‚‹ã‚ˆã†ä¾é ¼ã—ã¦ãã ã•ã„。"
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr "%{linkStart}管ç†è¨­å®š%{linkEnd} ã«ã‚¢ã‚¯ã‚»ã‚¹ã—ã¦ã“ã®æ©Ÿèƒ½ã‚’有効ã«ã—ã¦ãã ã•ã„。"
@@ -10494,9 +10592,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10680,9 +10775,6 @@ msgstr "アカウントã«ç´ã¥ãメールã®ç®¡ç†"
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -10995,6 +11087,9 @@ msgstr "デザインをアップロードã§ãã¾ã›ã‚“ã§ã—ãŸã€‚アップロ
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11127,9 +11222,6 @@ msgstr "イシューã®ä½œæˆ"
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr "イテレーションã®ä½œæˆ"
-
msgid "Create label"
msgstr ""
@@ -11208,6 +11300,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11244,6 +11339,42 @@ msgstr "ã‚ãªãŸã¯ã€ã‚°ãƒ«ãƒ¼ãƒ—を作æˆã™ã‚‹æ¨©é™ã‚’ã‚‚ã£ã¦ã„ã¾ã›ã‚“
msgid "CreateTag|Tag"
msgstr "ã‚¿ã‚°"
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr "%{name} (デフォルト)"
@@ -11275,7 +11406,7 @@ msgid "CreateValueStreamForm|Create new Value Stream"
msgstr ""
msgid "CreateValueStreamForm|Create value stream"
-msgstr ""
+msgstr "ãƒãƒªãƒ¥ãƒ¼ã‚¹ãƒˆãƒªãƒ¼ãƒ ã®ä½œæˆ"
msgid "CreateValueStreamForm|Default stages"
msgstr "デフォルトステージ"
@@ -11284,7 +11415,7 @@ msgid "CreateValueStreamForm|Default stages can only be hidden or re-ordered"
msgstr ""
msgid "CreateValueStreamForm|Edit value stream"
-msgstr ""
+msgstr "ãƒãƒªãƒ¥ãƒ¼ã‚¹ãƒˆãƒªãƒ¼ãƒ ã®ç·¨é›†"
msgid "CreateValueStreamForm|Editing stage"
msgstr "ステージを編集"
@@ -11302,7 +11433,7 @@ msgid "CreateValueStreamForm|Enter stage name"
msgstr "ステージåを入力"
msgid "CreateValueStreamForm|Enter value stream name"
-msgstr ""
+msgstr "ãƒãƒªãƒ¥ãƒ¼ã‚¹ãƒˆãƒªãƒ¼ãƒ ã®åå‰ã‚’入力"
msgid "CreateValueStreamForm|Issue stage end"
msgstr ""
@@ -11335,7 +11466,7 @@ msgid "CreateValueStreamForm|Restore stage"
msgstr "ステージを復元"
msgid "CreateValueStreamForm|Save value stream"
-msgstr ""
+msgstr "ãƒãƒªãƒ¥ãƒ¼ã‚¹ãƒˆãƒªãƒ¼ãƒ ã®ä¿å­˜"
msgid "CreateValueStreamForm|Select end event"
msgstr "終了イベントをé¸æŠž"
@@ -11469,6 +11600,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr "é‡å¤§ãªè„†å¼±æ€§ã®å­˜åœ¨"
@@ -11878,12 +12012,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -11896,9 +12036,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -11917,6 +12069,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -11950,6 +12105,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12010,6 +12168,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12157,6 +12318,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12527,6 +12691,9 @@ msgstr ""
msgid "Days to merge"
msgstr "マージã¾ã§ã®æ—¥æ•°"
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12584,6 +12751,12 @@ msgstr "週ã®å§‹ã¾ã‚Šã®æ›œæ—¥ (デフォルト)"
msgid "Default first day of the week in calendars and date pickers."
msgstr "カレンダーã¨æ—¥ä»˜ã®è¨­å®šã§ä½¿ç”¨ã™ã‚‹ã€é€±ã®æœ€åˆã®æ—¥(デフォルト)"
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13370,6 +13543,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13406,6 +13582,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr "デプロイ"
@@ -13509,9 +13688,15 @@ msgstr ""
msgid "Description:"
msgstr "説明:"
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr "説明ラベル"
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr "デザインマãƒã‚¸ãƒ¡ãƒ³ãƒˆãƒ•ã‚¡ã‚¤ãƒ«ã¨ãƒ‡ãƒ¼ã‚¿"
@@ -13675,6 +13860,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -14984,6 +15172,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr "å°‘ãªãã¨ã‚‚3文字以上ã§æ¤œç´¢ã—ã¦ãã ã•ã„"
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr "ã‚ãªãŸã® Bitbucket Server ã® URL ã¨ä»¥ä¸‹ã®å€‹äººã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’入力ã—ã¦ãã ã•ã„"
@@ -15347,9 +15538,6 @@ msgstr "エピックを削除"
msgid "Epics|Remove issue"
msgstr "イシューã®å‰Šé™¤"
-msgid "Epics|Show more"
-msgstr "詳細を見る"
-
msgid "Epics|Something went wrong while creating child epics."
msgstr "å­ã‚¨ãƒ”ック作æˆä¸­ã«ä½•ã‹å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -15443,6 +15631,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr "å‚ç…§ã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿ"
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr "ä¾å­˜é–¢ä¿‚リストãŒå–å¾—ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯æŽ¥ç¶šçŠ¶æ³ã‚’確èªã—ã¦å†è©¦è¡Œã—ã¦ãã ã•ã„。"
@@ -15507,7 +15698,7 @@ msgid "Error occurred. A blocked user must be unblocked to be activated"
msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ブロックã•ã‚ŒãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã€æœ‰åŠ¹åŒ–ã™ã‚‹ãŸã‚ã«ãƒ–ロックを解除ã—ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“"
msgid "Error occurred. User was not banned"
-msgstr ""
+msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ユーザーã¯BANã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ"
msgid "Error occurred. User was not blocked"
msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ユーザーã¯ãƒ–ロックã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ"
@@ -15516,7 +15707,7 @@ msgid "Error occurred. User was not confirmed"
msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ユーザーã¯ç¢ºèªã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ"
msgid "Error occurred. User was not unbanned"
-msgstr ""
+msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ユーザーã¯BAN解除ã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ"
msgid "Error occurred. User was not unblocked"
msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ユーザーã¯ãƒ–ロック解除ã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ"
@@ -15758,6 +15949,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -15918,6 +16118,9 @@ msgstr "全員ãŒè²¢çŒ®ã§ãã¾ã™"
msgid "Everything on your to-do list is marked as done."
msgstr "ã‚ãªãŸã®To Doリストã®é …ç›®ã™ã¹ã¦ãŒå®Œäº†ã¨ã—ã¦ãƒžãƒ¼ã‚¯ã•ã‚Œã¦ã„ã¾ã™ã€‚"
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -15948,9 +16151,21 @@ msgstr "エビデンス一覧"
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr "例: @sub\\.company\\.com$"
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -15969,9 +16184,6 @@ msgstr "マージコミットを除外ã—ã¦ã„ã¾ã™ã€‚ 6,000件ã®ã‚³ãƒŸãƒƒãƒˆ
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16026,13 +16238,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr "サイドãƒãƒ¼ã‚’é–‹ã"
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16346,9 +16561,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr "ref ã‚’å–å¾—ã§ãã¾ã›ã‚“ã§ã—ãŸ"
-msgid "Failed to install."
-msgstr "インストールã«å¤±æ•—ã—ã¾ã—ãŸã€‚"
-
msgid "Failed to load"
msgstr ""
@@ -16505,9 +16717,6 @@ msgstr "イシューã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸ"
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr "アップグレードã«å¤±æ•—ã—ã¾ã—ãŸã€‚"
-
msgid "Failed to upload object map file"
msgstr "オブジェクトマップファイルをアップロードã§ãã¾ã›ã‚“ã§ã—ãŸã€‚"
@@ -16761,6 +16970,9 @@ msgstr "2月"
msgid "February"
msgstr "2月"
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -16980,15 +17192,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr "Flowdock トークンを入力ã—ã¾ã™ã€‚"
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17025,6 +17228,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr "フォントã®è‰²"
@@ -17169,12 +17375,21 @@ msgstr "フォーク中ã§ã™"
msgid "Forks"
msgstr "フォーク"
-msgid "Format: %{dateFormat}"
-msgstr "フォーマット:%{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
+msgstr "フォーマット:%{dateFormat}"
+
msgid "Found errors in your %{gitlab_ci_yml}:"
msgstr "%{gitlab_ci_yml} ã«ã‚¨ãƒ©ãƒ¼ãŒã‚ã‚Šã¾ã™"
@@ -17251,10 +17466,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr "%{providerTitle} ã‹ã‚‰"
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-
msgid "From issue creation until deploy to production"
msgstr "イシューãŒç™»éŒ²ã•ã‚Œã¦ã‹ã‚‰ãƒ—ロダクションã«ãƒ‡ãƒ—ロイã•ã‚Œã‚‹ã¾ã§"
@@ -17297,6 +17508,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr "åˆæœŸè¨­å®šãƒ©ãƒ™ãƒ«ã‚»ãƒƒãƒˆã‚’生æˆã™ã‚‹"
@@ -18142,6 +18356,10 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18391,6 +18609,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18418,6 +18639,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18595,6 +18819,9 @@ msgstr "マージリクエストã¸ç§»å‹•"
msgid "Go to your projects"
msgstr "ã‚ãªãŸã®ãƒ—ロジェクトã¸ç§»å‹•"
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr "ã‚ãªãŸã®ã‚¹ãƒ‹ãƒšãƒƒãƒˆã¸ç§»å‹•"
@@ -18688,6 +18915,12 @@ msgstr ""
msgid "Graph"
msgstr "グラフ"
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -18877,7 +19110,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -18964,6 +19197,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19069,6 +19305,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr "SAMLレスãƒãƒ³ã‚¹å‡ºåŠ›"
@@ -19240,7 +19479,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19261,9 +19500,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr "Auto DevOps パイプラインã®æ›´æ–°ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸï¼š %{error_messages} 。"
@@ -19393,6 +19629,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr "グループを作æˆ"
@@ -19402,9 +19641,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -19874,12 +20110,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr "最高ä½:"
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -19913,9 +20155,6 @@ msgstr "ホームページ"
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr "フックã®å®Ÿè¡Œã«å¤±æ•—ã—ã¾ã—ãŸã€‚グループã«ã‚³ãƒŸãƒƒãƒˆã®ã‚るプロジェクトãŒã‚ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。"
-msgid "Hook was successfully updated."
-msgstr "フックã¯æ­£å¸¸ã«æ›´æ–°ã—ã¾ã—ãŸã€‚"
-
msgid "Horizontal rule"
msgstr ""
@@ -20084,6 +20323,9 @@ msgstr "識別å­"
msgid "Identities"
msgstr "ID"
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20108,6 +20350,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20147,7 +20392,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20189,6 +20434,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20201,9 +20452,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20546,6 +20806,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21113,12 +21376,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr "インシデント管ç†åˆ¶é™"
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21437,6 +21706,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr "ã“ã®ãƒ©ãƒ³ãƒŠãƒ¼ãŒã‚¿ã‚°ã®ãªã„ジョブをé¸æŠžã§ãã‚‹ã‹ã©ã†ã‹ã‚’示ã—ã¾ã™ã€‚"
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr "SSHキーをアップロードã—ã¦ã„ãªã„ユーザーã«ã€è¿½åŠ ã•ã‚Œã‚‹ã¾ã§ SSH 経由ã§ãƒ—ッシュã§ããªã„ã“ã¨ã‚’通知"
@@ -21708,6 +21980,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21741,6 +22016,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr "GitLabã®ç®¡ç†è€…ã¯ã€ã‚°ãƒ«ãƒ¼ãƒ—内ã®ã™ã¹ã¦ã®ãƒ—ロジェクトãŒç¶™æ‰¿ã—ã€ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã§ä½¿ç”¨ã™ã‚‹ã‚¤ãƒ³ãƒ†ã‚°ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³æ©Ÿèƒ½ã‚’設定ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ã“れらã®ã‚¤ãƒ³ãƒ†ã‚°ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã¯ã€ã¾ã ã‚«ã‚¹ã‚¿ãƒ è¨­å®šã‚’使用ã—ã¦ã„ãªã„ã™ã¹ã¦ã®ãƒ—ロジェクトã«é©ç”¨ã•ã‚Œã¾ã™ã€‚プロジェクトã®ãƒ¬ãƒ™ãƒ«ã§è¨­å®šãŒå¿…è¦ãªå ´åˆã¯ã€ãƒ—ロジェクトã®ã‚«ã‚¹ã‚¿ãƒ è¨­å®šã‚’上書ãã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚詳細㯠%{integrations_link_start}グループレベルã®ã‚¤ãƒ³ãƒ†ã‚°ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ç®¡ç†%{link_end} ã‚’å‚ç…§ã—ã¦ãã ã•ã„。"
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr "グループレベルインテグレーション管ç†"
@@ -22089,6 +22367,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22137,7 +22418,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22603,9 +22884,6 @@ msgstr ""
msgid "Issue|Title"
msgstr "タイトル"
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22744,9 +23022,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23179,9 +23454,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23332,6 +23604,9 @@ msgstr "プロジェクトã«ã‚ªãƒ³ãƒ©ã‚¤ãƒ³ã® Runner ãŒå‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¦ã
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23413,6 +23688,9 @@ msgstr "キー"
msgid "Key (PEM)"
msgstr "キー (PEM)"
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23482,9 +23760,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr "リソース deployment ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚"
-msgid "Kubernetes error: %{error_code}"
-msgstr "Kubernetesエラー: %{error_code}"
-
msgid "LDAP"
msgstr "LDAP"
@@ -23558,15 +23833,18 @@ msgstr "ラベル"
msgid "Labels"
msgstr "ラベル"
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr "ラベルを%{features} ã«é©ç”¨ã§ãã¾ã™ã€‚グループラベルã¯ã‚°ãƒ«ãƒ¼ãƒ—内ã®ã™ã¹ã¦ã®ãƒ—ロジェクトã§ä½¿ç”¨ã§ãã¾ã™ã€‚"
-
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "ラベルを使用ã—ã¦ã€ã‚¤ã‚·ãƒ¥ãƒ¼ã‚„マージリクエストを分類ã§ãã¾ã™ã€‚"
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23652,6 +23930,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23757,6 +24038,9 @@ msgstr ""
msgid "Learn More."
msgstr "詳ã—ã見る。"
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr "%{link_start} ビルトインã®ãƒ†ãƒ³ãƒ—レートã«è²¢çŒ® %{link_end}ã™ã‚‹æ–¹æ³•ã‚’å­¦ã¶"
@@ -24426,12 +24710,12 @@ msgstr "ロックã•ã‚ŒãŸãƒ•ã‚¡ã‚¤ãƒ«"
msgid "Locked by %{fileLockUserName}"
msgstr "%{fileLockUserName} ã«ã‚ˆã£ã¦ ã«ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™ã€‚"
+msgid "Locked files"
+msgstr ""
+
msgid "Locked the discussion."
msgstr "ロックã—ãŸè­°è«–"
-msgid "Locks give the ability to lock specific file or folder."
-msgstr "ロックã¯ã€ç‰¹å®šã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚„フォルダをロックã™ã‚‹æ©Ÿèƒ½ã‚’æä¾›ã—ã¾ã™ã€‚"
-
msgid "Locks the discussion."
msgstr " ディスカッションをロックã™ã‚‹."
@@ -24480,6 +24764,9 @@ msgstr ""
msgid "Logs"
msgstr "ログ"
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr "å¼±ã„脆弱性ã®å­˜åœ¨"
@@ -24585,6 +24872,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25083,6 +25373,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr "測定ã—ãŸã‚³ãƒ¼ãƒ‰ã®ãƒã‚¤ãƒˆæ•°ã€‚自動生æˆã‚³ãƒ¼ãƒ‰ã¨ãƒ™ãƒ³ãƒ€ãƒ¼ã‚³ãƒ¼ãƒ‰ã‚’除外ã—ã¾ã™ã€‚"
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25104,6 +25397,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr "メンãƒãƒ¼"
@@ -26227,6 +26526,9 @@ msgstr ""
msgid "Modal|Close"
msgstr "é–‰ã˜ã‚‹"
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26290,9 +26592,6 @@ msgstr "詳細情報"
msgid "More information"
msgstr "詳ã—ã„情報"
-msgid "More information and share feedback"
-msgstr "より詳細ãªæƒ…å ±ã¨ãƒ•ã‚£ãƒ¼ãƒ‰ãƒãƒƒã‚¯ã®å…±æœ‰"
-
msgid "More information is available|here"
msgstr ""
@@ -26501,6 +26800,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26679,6 +26981,9 @@ msgstr "æ–°è¦ãƒ–ランãƒ"
msgid "New branch unavailable"
msgstr "æ–°ã—ã„ブランãƒã¯åˆ©ç”¨ã§ãã¾ã›ã‚“"
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr "æ–°ã—ã„éžå…¬é–‹ã®ã‚¨ãƒ”ックã®ã‚¿ã‚¤ãƒˆãƒ« "
@@ -26721,6 +27026,12 @@ msgstr "æ–°ã—ã„ヘルスãƒã‚§ãƒƒã‚¯ã®ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’生æˆã—ã¾
msgid "New identity"
msgstr "æ–°ã—ã„ ID"
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "æ–°è¦ã‚¤ã‚·ãƒ¥ãƒ¼"
@@ -26994,9 +27305,6 @@ msgstr ""
msgid "No iteration"
msgstr "イテレーションãŒã‚ã‚Šã¾ã›ã‚“"
-msgid "No iterations to show"
-msgstr "表示ã™ã‚‹ã‚¤ãƒ†ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãŒã‚ã‚Šã¾ã›ã‚“"
-
msgid "No job log"
msgstr "ジョブログãŒã‚ã‚Šã¾ã›ã‚“"
@@ -27096,6 +27404,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27749,9 +28060,24 @@ msgstr ""
msgid "OK"
msgstr "OK"
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr "オブジェクトãŒã‚µãƒ¼ãƒãƒ¼ã«å­˜åœ¨ã—ã¾ã›ã‚“ã€ã¾ãŸã¯ã€ãã‚Œã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹æ¨©é™ãŒã‚ã‚Šã¾ã›ã‚“"
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28131,6 +28457,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28207,9 +28536,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28315,9 +28641,6 @@ msgstr "æ–°è¦ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã§é–‹ã"
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr "æ“作ã«å¤±æ•—ã—ã¾ã—ãŸã€‚ pod log ã® %{pod_name} ã§è©³ç´°ã‚’確èªã—ã¦ãã ã•ã„。"
-
msgid "Operation not allowed"
msgstr ""
@@ -28511,7 +28834,7 @@ msgid "Package already exists"
msgstr "パッケージã¯ã™ã§ã«ã‚ã‚Šã¾ã™"
msgid "Package and registry settings"
-msgstr ""
+msgstr "パッケージã¨ãƒ¬ã‚¸ã‚¹ãƒˆãƒªã®è¨­å®š"
msgid "Package deleted successfully"
msgstr "パッケージã¯æ­£å¸¸ã«å‰Šé™¤ã•ã‚Œã¾ã—ãŸ"
@@ -28603,6 +28926,9 @@ msgstr "Conan"
msgid "PackageRegistry|Conan Command"
msgstr "Conan コマンド"
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ".pypirc ã®å†…容をコピー"
@@ -28691,6 +29017,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28706,6 +29035,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28730,6 +29065,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr "Pypiã®ãƒ¬ã‚¸ã‚¹ãƒˆãƒªã®è©³ç´°ã«ã¤ã„ã¦ã¯ã€ %{linkStart}ドキュメントをå‚ç…§%{linkEnd}。"
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -28802,6 +29149,9 @@ msgstr "NuGet コマンド"
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -28817,6 +29167,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28824,6 +29177,9 @@ msgstr[0] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -28878,6 +29234,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -28926,9 +29285,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr "より多ãを検索ã™ã‚‹ã«ã¯ã€ä¸Šã®ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼ã‚’変更ã¾ãŸã¯å‰Šé™¤ã—ã¾ã™."
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -28948,6 +29304,10 @@ msgid "PackageRegistry|You are about to delete 1 asset. This operation is irreve
msgid_plural "PackageRegistry|You are about to delete %d assets. This operation is irreversible."
msgstr[0] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -28963,9 +29323,12 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages and registries"
+msgid "Packages"
msgstr ""
+msgid "Packages and registries"
+msgstr "パッケージã¨ãƒ¬ã‚¸ã‚¹ãƒˆãƒª"
+
msgid "Page not found"
msgstr "ページãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
@@ -29032,6 +29395,9 @@ msgstr "パラメーター"
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr "パラメーター \"job_id\"㯠%{job_id_max_size} 長を超ãˆã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“"
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr "親"
@@ -29095,6 +29461,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29338,6 +29713,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr "åå‰ã‚’é¸æŠž"
@@ -29801,7 +30200,7 @@ msgid "Pipelines|Edit"
msgstr ""
msgid "Pipelines|Editor"
-msgstr ""
+msgstr "エディタ"
msgid "Pipelines|Follow these instructions to install GitLab Runner on macOS."
msgstr ""
@@ -29870,7 +30269,7 @@ msgid "Pipelines|Owner"
msgstr ""
msgid "Pipelines|Pipeline Editor"
-msgstr ""
+msgstr "パイプラインエディタ"
msgid "Pipelines|Pipeline syntax is correct."
msgstr ""
@@ -30253,6 +30652,9 @@ msgstr "é›»å­ãƒ¡ãƒ¼ãƒ«ï¼ˆ%{email})をãƒã‚§ãƒƒã‚¯ã—ã¦ã€ã“ã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr "ã‚ãªãŸã®ãƒ—ロフィールã«ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’記入ã—ã¦ã€ãƒ—ロファイルを完了ã—ã¦ãã ã•ã„"
@@ -30409,9 +30811,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr "続行ã™ã‚‹ã«ã¯ %{phrase_code} を入力ã—ã¦ãã ã•ã„。キャンセルã™ã‚‹å ´åˆã¯ã“ã®ãƒ¢ãƒ¼ãƒ€ãƒ«ã‚’é–‰ã˜ã¦ãã ã•ã„。"
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr "イシューやコメントã¸ã®ã‚¹ãƒ‘ム行為ã€ã¾ãŸä¸é©åˆ‡ãªæŒ™å‹•ã‚’ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã¤ã„ã¦ã¯ã€ã“ã¡ã‚‰ã®ãƒ•ã‚©ãƒ¼ãƒ ã‹ã‚‰ç®¡ç†è€…ã«å ±å‘Šã—ã¦ãã ã•ã„。"
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30565,6 +30964,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "プロジェクトã®æ¦‚è¦ãƒšãƒ¼ã‚¸ã«è¡¨ç¤ºã—ãŸã„コンテンツをé¸æŠžã—ã¾ã™ã€‚"
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30649,6 +31051,9 @@ msgstr "シンタックスãƒã‚¤ãƒ©ã‚¤ãƒˆã®ãƒ†ãƒ¼ãƒž"
msgid "Preferences|Tab width"
msgstr "タブ幅"
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30661,9 +31066,15 @@ msgstr "ã“ã®è¨­å®šã«ã‚ˆã‚Šã€ã‚·ã‚¹ãƒ†ãƒ ãƒ¬ã‚¤ã‚¢ã‚¦ãƒˆã¨ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆãƒ“
msgid "Preferences|Time preferences"
msgstr "時間設定"
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr "相対時間を使用"
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30781,18 +31192,27 @@ msgstr ""
msgid "Proceed"
msgstr "続行"
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
+msgid "ProductAnalytics|New Analytics Widget Title"
+msgstr ""
+
msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr "ç¾åœ¨ã€ã“ã®ã‚¿ã‚¤ãƒ—ã®ãƒãƒ£ãƒ¼ãƒˆã«ã¯ãƒ‡ãƒ¼ã‚¿ãŒã‚ã‚Šã¾ã›ã‚“。プロダクト分æžãƒ„ールを設定ã—ã¦ã„ãªã„å ´åˆã¯ã€Setup タブを開ã„ã¦ãã ã•ã„。"
-msgid "ProductAnalytics|Widgets content"
-msgstr ""
-
msgid "Productivity"
msgstr ""
@@ -31153,6 +31573,12 @@ msgstr "ユーザーåã‚’æ›´æ–°"
msgid "Profiles|Upload new avatar"
msgstr "æ–°ã—ã„ã‚¢ãƒã‚¿ãƒ¼ã‚’アップロード"
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr "プライベートメールを使用ã™ã‚‹ - %{email}"
@@ -31687,12 +32113,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31774,9 +32206,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr "ãã‚Œãžã‚Œã®ãƒ—ロジェクトã¯ã€Dockerイメージをä¿å­˜ã™ã‚‹ãŸã‚ã®å„自ã§ã‚¹ãƒšãƒ¼ã‚¹ã‚’æŒã¤ã“ã¨ãŒã§ãã¾ã™"
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -31891,6 +32320,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -31936,6 +32368,9 @@ msgstr "プロジェクトã®å¯è¦–性"
msgid "ProjectSettings|Public"
msgstr "公開"
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32065,7 +32500,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32146,6 +32581,9 @@ msgstr "Netlify/Plain HTML"
msgid "ProjectTemplates|NodeJS Express"
msgstr "NodeJS Express"
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32668,9 +33106,6 @@ msgstr "ä¿è­·ãƒ–ランãƒ"
msgid "Protected Branches"
msgstr "ä¿è­·ãƒ–ランãƒ"
-msgid "Protected Environment"
-msgstr "ä¿è­·ç’°å¢ƒ"
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33341,6 +33776,9 @@ msgid "Refreshing in a second to show the updated status..."
msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] "æ›´æ–°ã•ã‚ŒãŸã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã‚’表示ã™ã‚‹ãŸã‚ã«ã€ %d 秒間リフレッシュã—ã¦ã„ã¾ã™..."
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr "エクスãƒãƒ¼ãƒˆã‚’å†ç”Ÿæˆ"
@@ -33537,6 +33975,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -33876,6 +34317,9 @@ msgstr "ã“ã® %{quick_action_target} ã‚’å†ã³é–‹ã"
msgid "Reopened this %{quick_action_target}."
msgstr "ã“ã® %{quick_action_target} ã‚’å†ã³é–‹ã„ãŸã€‚"
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr "ã“ã® %{quick_action_target} ã‚’å†ã³é–‹ã。"
@@ -33930,11 +34374,8 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr "ä¸æ­£åˆ©ç”¨ã‚’報告"
-
-msgid "Report abuse to admin"
-msgstr "管ç†è€…ã«ä¸æ­£åˆ©ç”¨ã‚’報告"
+msgid "Report abuse to administrator"
+msgstr ""
msgid "Report couldn't be prepared."
msgstr ""
@@ -34266,6 +34707,9 @@ msgstr ""
msgid "Request Access"
msgstr "アクセス権é™ã‚’リクエストã™ã‚‹"
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34511,6 +34955,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr "å†é–‹"
@@ -34584,8 +35031,8 @@ msgstr ""
msgid "Review time"
msgstr "レビュー時間"
-msgid "Review time is defined as the time it takes from first comment until merged."
-msgstr "レビュー時間ã¯æœ€åˆã®ã‚³ãƒ¡ãƒ³ãƒˆã‹ã‚‰ãƒžãƒ¼ã‚¸ã•ã‚Œã‚‹ã¾ã§ã®æ™‚間を表ã—ã¾ã™ã€‚"
+msgid "Review time is the amount of time since the first comment in a merge request."
+msgstr ""
msgid "ReviewApp|Enable Review App"
msgstr "Review Appを有効ã«ã™ã‚‹"
@@ -34678,6 +35125,9 @@ msgstr "ãƒã‚¦ã‚¹ã‚­ãƒ¼ãƒ”ングを実行"
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34735,7 +35185,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr "æ–°ã—ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãŒåˆ©ç”¨å¯èƒ½ã§ã™"
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34762,6 +35212,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -34783,6 +35236,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -34865,6 +35321,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -34883,6 +35342,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -34922,12 +35384,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35019,6 +35484,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35085,6 +35553,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35133,12 +35604,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35251,6 +35716,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr "稼åƒä¸­"
@@ -35278,13 +35746,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35326,6 +35797,9 @@ msgstr ""
msgid "SSH key"
msgstr "SSH キー"
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr "SSH キー"
@@ -35341,6 +35815,15 @@ msgstr ""
msgid "SSH public key"
msgstr "SSH 公開éµ"
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr "SSL ã®æ¤œè¨¼:"
@@ -35407,6 +35890,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35434,9 +35923,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35449,18 +35944,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35545,6 +36049,9 @@ msgstr "検索"
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr "グループã®æ¤œç´¢"
@@ -36051,6 +36558,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36108,9 +36618,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36126,6 +36645,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36162,6 +36684,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36234,6 +36759,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36243,12 +36771,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36318,6 +36855,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36345,6 +36885,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36381,6 +36924,9 @@ msgstr "%{firstProject} 㨠%{secondProject}"
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr "%{firstProject}〠%{secondProject}ã€ãŠã‚ˆã³ %{rest}"
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr "セキュリティエリアã§ç›£è¦–ã™ã‚‹ãƒ—ロジェクトを追加ã¾ãŸã¯å‰Šé™¤ã—ã¾ã™ã€‚ ã“ã®ä¸€è¦§ã«å«ã¾ã‚Œã‚‹ãƒ—ロジェクトã§ã¯ã€ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ãƒ€ãƒƒã‚·ãƒ¥ãƒœãƒ¼ãƒ‰ã¨è„†å¼±æ€§ãƒ¬ãƒãƒ¼ãƒˆã«çµæžœãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚"
@@ -36390,9 +36936,24 @@ msgstr "プロジェクトを追加"
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36537,6 +37098,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -36762,6 +37326,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr "å·¦å´ã®ã‚µã‚¤ãƒ‰ãƒãƒ¼ã‹ã‚‰ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã—ã¦ç·¨é›†ã‚’開始ã—ã¾ã™ã€‚ãã®å¾Œã€å¤‰æ›´ã‚’コミットã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr "ラベルをé¸æŠž"
@@ -36853,10 +37420,10 @@ msgid "Select project"
msgstr "プロジェクトã®é¸æŠž"
msgid "Select project to create %{type}"
-msgstr ""
+msgstr "%{type} を作æˆã™ã‚‹ãƒ—ロジェクトをé¸æŠž"
msgid "Select project to create issue"
-msgstr ""
+msgstr "イシューを作æˆã™ã‚‹ãƒ—ロジェクトをé¸æŠž"
msgid "Select projects"
msgstr "プロジェクトã®é¸æŠž"
@@ -36867,6 +37434,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37354,6 +37924,9 @@ msgstr ""
msgid "Shared Runners"
msgstr "共有 Runner"
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr "共有プロジェクト"
@@ -37480,6 +38053,9 @@ msgstr "最新ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’表示"
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr "一度ã«1ã¤ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’表示"
@@ -37637,9 +38213,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -37793,7 +38366,7 @@ msgstr "Slackçµ±åˆã§ã¯ã€Slackã®ãƒãƒ£ãƒƒãƒˆã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã‹ã‚‰ slash コã
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -37808,13 +38381,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -37838,13 +38414,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -37982,11 +38561,11 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
-msgstr "誰ã‹ãŒåŒæ™‚ã«ã‚¤ã‚·ãƒ¥ãƒ¼ã‚’編集ã—ã¾ã—ãŸã€‚%{linkStart} イシュー %{linkEnd} を確èªã—ã¦ã€å¤‰æ›´ç‚¹ãŒæ„図ã›ãšå‰Šé™¤ã•ã‚Œãªã„よã†ã«æ°—ã‚’ã¤ã‘ã¦ãã ã•ã„。"
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr "ã‚ãªãŸã®ç·¨é›†ã¨åŒæ™‚ã«èª°ã‹ãŒã“ã® %{issueType} を編集ã—ãŸãŸã‚ã€èª¬æ˜ŽãŒæ›´æ–°ã•ã‚Œã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ç·¨é›†ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
@@ -38027,6 +38606,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38045,6 +38627,9 @@ msgstr "Requirement をアーカイブã™ã‚‹éš›ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38114,6 +38699,9 @@ msgstr "パッケージリストをå–å¾—ã™ã‚‹éš›ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr "Let's Encrypt ã®è¨¼æ˜Žæ›¸ã‚’å–å¾—ã™ã‚‹éš›ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38507,6 +39095,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr "コミットメッセージをスカッシュ"
@@ -38612,6 +39203,9 @@ msgstr "パイプラインãŒæˆåŠŸã—ãŸã¨ãã«ãƒžãƒ¼ã‚¸ãƒˆãƒ¬ã‚¤ãƒ³ã‚’開始
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr "検索開始"
@@ -38675,6 +39269,9 @@ msgstr "統計"
msgid "Status"
msgstr "状態"
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39102,7 +39699,7 @@ msgid "Successfully approved"
msgstr ""
msgid "Successfully banned"
-msgstr ""
+msgstr "正常ã«BANã—ã¾ã—ãŸ"
msgid "Successfully blocked"
msgstr "正常ã«ãƒ–ロックã•ã‚Œã¾ã—ãŸ"
@@ -39129,7 +39726,7 @@ msgid "Successfully synced %{synced_timeago}."
msgstr ""
msgid "Successfully unbanned"
-msgstr ""
+msgstr "正常ã«BANを解除ã—ã¾ã—ãŸ"
msgid "Successfully unblocked"
msgstr "正常ã«ãƒ–ロックãŒè§£é™¤ã•ã‚Œã¾ã—ãŸ"
@@ -39411,6 +40008,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39501,9 +40101,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr "ブランãƒãƒ»ã‚¿ã‚°åˆ‡ã‚Šæ›¿ãˆ"
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr "GitLab Nextã«åˆ‡ã‚Šæ›¿ãˆã‚‹"
@@ -39786,6 +40383,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr "ãƒãƒ¼ãƒ "
@@ -39999,7 +40602,7 @@ msgid "Test"
msgstr "テスト"
msgid "Test Cases"
-msgstr ""
+msgstr "テストケース"
msgid "Test case"
msgstr ""
@@ -40361,6 +40964,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr "次ã®ã‚¢ã‚¤ãƒ†ãƒ ã¯ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã§ãã¾ã›ã‚“。"
@@ -40723,9 +41329,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr "アーカイブã•ã‚ŒãŸãƒ—ロジェクトã¯ã‚ã‚Šã¾ã›ã‚“"
-
msgid "There are no archived requirements"
msgstr ""
@@ -40783,9 +41386,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr "ã¾ã ãƒ‘ッケージãŒã‚ã‚Šã¾ã›ã‚“。"
-msgid "There are no projects shared with this group yet"
-msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã¨å…±æœ‰ã—ã¦ã„るプロジェクトã¯ã‚ã‚Šã¾ã›ã‚“"
-
msgid "There are no secure files yet."
msgstr ""
@@ -40831,6 +41431,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr "ã‚ãªãŸã®ãƒ‡ãƒã‚¤ã‚¹ã¨ã®é–“ã«é€šä¿¡éšœå®³ãŒç™ºç”Ÿã—ã¦ã„ã¾ã™ã€‚"
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -40951,6 +41554,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -40990,6 +41596,9 @@ msgstr "ユーザーã®ãƒ‘イプライン時間ã®ãƒªã‚»ãƒƒãƒˆä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒ
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr "ã‚ãªãŸã®å¤‰æ›´ã®ä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -41083,9 +41692,6 @@ msgstr "%{reason} ã®ãŸã‚ã€ã“ã® %{viewer} ã¯è¡¨ç¤ºã§ãã¾ã›ã‚“ã§ã—ãŸ
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41275,6 +41881,9 @@ msgstr "ã“ã®ã‚¨ãƒ”ックã¯å­˜åœ¨ã—ãªã„ã‹ã€ã‚ãªãŸã«å分ãªæ¨©é™ãŒ
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr "ã“ã®æ©Ÿèƒ½ã¯ãƒ­ãƒ¼ã‚«ãƒ«ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã‚’有効ã«ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™"
@@ -41293,7 +41902,7 @@ msgstr ""
msgid "This group"
msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—"
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -41387,7 +41996,7 @@ msgid "This issue is currently blocked by the following issues:"
msgstr ""
msgid "This issue is hidden because its author has been banned"
-msgstr ""
+msgstr "ã“ã®ã‚¤ã‚·ãƒ¥ãƒ¼ã¯è‘—者ãŒBANã•ã‚Œã¦ã„ã‚‹ãŸã‚éžè¡¨ç¤ºã«ãªã£ã¦ã„ã¾ã™"
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -41767,6 +42376,12 @@ msgstr "残り時間"
msgid "Time spent"
msgstr "経éŽæ™‚é–“"
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42015,6 +42630,9 @@ msgstr ""
msgid "Title:"
msgstr "タイトル:"
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr "タイトルãŠã‚ˆã³èª¬æ˜Ž"
@@ -42048,9 +42666,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr "エントリーを手動ã§è¿½åŠ ã™ã‚‹ã«ã¯ã€ã‚¹ãƒžãƒ¼ãƒˆãƒ•ã‚©ãƒ³ã®ã‚¢ãƒ—リケーションã«æ¬¡ã®è©³ç´°ã‚’入力ã—ã¦ãã ã•ã„"
@@ -42165,6 +42780,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42258,6 +42876,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42291,6 +42912,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42306,13 +42930,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42327,22 +42951,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42436,16 +43060,16 @@ msgid "Too many users found. Quick actions are limited to at most %{max_count} u
msgstr ""
msgid "TopNav|Explore"
-msgstr ""
+msgstr "探索"
msgid "TopNav|Go back"
msgstr ""
msgid "TopNav|Switch to"
-msgstr ""
+msgstr "切り替ãˆ"
msgid "TopNav|Your dashboards"
-msgstr ""
+msgstr "ダッシュボード"
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -42712,6 +43336,12 @@ msgstr "トリガーã¯æ­£å¸¸ã«æ›´æ–°ã•ã‚Œã¾ã—ãŸã€‚"
msgid "Triggerer"
msgstr "トリガー"
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -42748,6 +43378,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -42925,6 +43558,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43048,6 +43687,9 @@ msgstr "残念ãªãŒã‚‰ã€GitLab ã¸ã®ãƒ¡ãƒ¼ãƒ«å‡¦ç†ã¯ã§ãã¾ã›ã‚“ã§ã—ã
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr "ms"
@@ -43129,6 +43771,9 @@ msgstr ""
msgid "Unschedule job"
msgstr "ジョブã®ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«ã‚’解除"
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr "スターを外ã™"
@@ -43312,6 +43957,9 @@ msgstr "変更をターミナルã«ã‚¢ãƒƒãƒ—ロードã—ã¦ã„ã¾ã™"
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr "Upstream"
@@ -43330,18 +43978,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr "%{help_link_start}共有 Runner%{help_link_end} ã¯ç„¡åŠ¹ã«ãªã£ã¦ã„ã‚‹ãŸã‚ã€ãƒ‘イプラインã®ä½¿ç”¨ã«åˆ¶é™ã¯ã‚ã‚Šã¾ã›ã‚“"
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43369,9 +44011,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43381,9 +44020,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr "ç¾åœ¨ã®ä½¿ç”¨çŠ¶æ³"
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43510,10 +44146,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43522,12 +44155,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr "ã“ã®ãƒãƒ¼ãƒ ã‚¹ãƒšãƒ¼ã‚¹ã«ã¯ã€å…±æœ‰ãƒ©ãƒ³ãƒŠãƒ¼ã‚’使用ã™ã‚‹ãƒ—ロジェクトã¯ã‚ã‚Šã¾ã›ã‚“。"
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43540,9 +44167,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr "無制é™"
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43576,7 +44200,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43585,16 +44209,16 @@ msgstr "Wiki"
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -43768,6 +44392,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44033,9 +44660,6 @@ msgstr "個人的ãªãƒ—ロジェクト"
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr "ä¸æ­£åˆ©ç”¨ã‚’報告ã™ã‚‹"
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44225,6 +44849,9 @@ msgstr "æš—å·åŒ–フィールドãŒãªã„å ´åˆã€å¿…è¦ãªæš—å·åŒ–戦略を使
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44262,7 +44889,7 @@ msgid "Value might contain a variable reference"
msgstr ""
msgid "Value stream"
-msgstr ""
+msgstr "ãƒãƒªãƒ¥ãƒ¼ã‚¹ãƒˆãƒªãƒ¼ãƒ "
msgid "ValueStreamAnalyticsStage|We don't have enough data to show this stage."
msgstr "ã“ã®ã‚¹ãƒ†ãƒ¼ã‚¸ã‚’表示ã™ã‚‹ã®ã«å分ãªãƒ‡ãƒ¼ã‚¿ãŒã‚ã‚Šã¾ã›ã‚“。"
@@ -44297,6 +44924,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44315,6 +44945,9 @@ msgstr "イシューã®ä½œæˆã‹ã‚‰ã‚¯ãƒ­ãƒ¼ã‚ºã¾ã§ã®æ™‚é–“ã®ä¸­å¤®å€¤ã€‚"
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44346,7 +44979,7 @@ msgid "ValueStreamAnalytics|Total number of deploys to production."
msgstr ""
msgid "ValueStreamAnalytics|Value stream"
-msgstr ""
+msgstr "ãƒãƒªãƒ¥ãƒ¼ã‚¹ãƒˆãƒªãƒ¼ãƒ "
msgid "ValueStreamEvent|Items in stage"
msgstr ""
@@ -44363,10 +44996,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr "デフォルトãƒãƒªãƒ¥ãƒ¼ã‚¹ãƒˆãƒªãƒ¼ãƒ ã¯å‰Šé™¤ã§ãã¾ã›ã‚“"
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44378,7 +45011,7 @@ msgstr "変数"
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44435,6 +45068,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44444,6 +45092,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -44460,13 +45120,13 @@ msgid "View all environments."
msgstr ""
msgid "View all groups"
-msgstr ""
+msgstr "ã™ã¹ã¦ã®ã‚°ãƒ«ãƒ¼ãƒ—を表示"
msgid "View all issues"
msgstr "ã™ã¹ã¦ã®ã‚¤ã‚·ãƒ¥ãƒ¼ã‚’表示"
msgid "View all projects"
-msgstr ""
+msgstr "å…¨ã¦ã®ãƒ—ロジェクトを表示"
msgid "View blame"
msgstr ""
@@ -44874,7 +45534,7 @@ msgid "Vulnerability|Cluster"
msgstr ""
msgid "Vulnerability|Code Review"
-msgstr ""
+msgstr "コードレビュー"
msgid "Vulnerability|Comments"
msgstr ""
@@ -45101,6 +45761,9 @@ msgstr "%{humanized_resource_name} ã«ã‚¹ãƒ‘ムãŒã‚ã‚‹å¯èƒ½æ€§ã‚’検出ã—ã¾
msgid "We don't have enough data to show this stage."
msgstr "データä¸è¶³ã®ãŸã‚ã€ã“ã®ã‚¹ãƒ†ãƒ¼ã‚¸ã®è¡¨ç¤ºã¯ã§ãã¾ã›ã‚“。"
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr "次ã®ã‚¨ãƒ©ãƒ¼ãŒã‚ã‚Šã¾ã—ãŸã€‚:"
@@ -45176,9 +45839,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45194,24 +45854,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45242,6 +45890,18 @@ msgstr "Webhook設定"
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45308,9 +45968,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr "コメント"
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45359,15 +46016,12 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr "マージリクエストイベント"
+msgid "Webhooks|Must match part of URL"
+msgstr ""
+
msgid "Webhooks|Pipeline events"
msgstr "パイプラインイベント"
-msgid "Webhooks|Push events"
-msgstr "プッシュイベント"
-
-msgid "Webhooks|Push to the repository."
-msgstr ""
-
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
msgstr ""
@@ -45404,9 +46058,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr "トリガー"
-msgid "Webhooks|URL"
-msgstr "URL"
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -45796,6 +46447,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr "次ã®å ´æ‰€ã«ãƒ‡ãƒ—ロイã—ã¾ã™"
@@ -45805,9 +46459,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr "アクセスリクエストをå–り消ã™"
@@ -45826,6 +46477,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -45841,9 +46498,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -45863,6 +46517,9 @@ msgstr[0] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -45872,6 +46529,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -45884,16 +46547,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -45902,12 +46568,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -45917,12 +46589,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -45935,9 +46613,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr "種類をé¸æŠž"
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46001,7 +46688,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46031,6 +46718,9 @@ msgstr "コメントを書ã..."
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46197,10 +46887,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46239,10 +46929,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46309,9 +46995,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "æ–°ã—ã„メンãƒãƒ¼ã‚’%{project_name} ã«æ‹›å¾…ã™ã‚‹ã‹ã€åˆ¥ã®ã‚°ãƒ«ãƒ¼ãƒ—を招待ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
@@ -46330,18 +47013,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr "セキュリティダッシュボードをCSVレãƒãƒ¼ãƒˆã«ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã§ãã¾ã™ã€‚"
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr "ã“ã®å¤‰æ›´ã‚’å…ƒã®ãƒ–ランãƒã«å映ã•ã›ã‚‹ãŸã‚ã«ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’é€ä¿¡ã§ãるよã†ã«ãªã‚Šã¾ã—ãŸã€‚"
msgid "You can now submit a merge request to get this change into the original project."
msgstr "ã“ã®å¤‰æ›´ã‚’å…ƒã®ãƒ—ロジェクトã«å映ã•ã›ã‚‹ãŸã‚ã«ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’é€ä¿¡ã§ãるよã†ã«ãªã‚Šã¾ã—ãŸã€‚"
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46423,6 +47100,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr "æ–°ã—ã„トリガーを作æˆã§ãã¾ã›ã‚“ã§ã—ãŸã€‚"
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr "ã‚ãªãŸã«ã¯ã‚µãƒ–スクリプションãŒã‚ã‚Šã¾ã›ã‚“"
@@ -46477,6 +47157,9 @@ msgstr "最近ã®æ¤œç´¢ãŒã‚ã‚Šã¾ã›ã‚“。"
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46538,6 +47221,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46589,9 +47275,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -46769,6 +47452,9 @@ msgstr "ワンタイムパスワードèªè¨¼ã‚’使用ã—ãŸ2è¦ç´ èªè¨¼ã¯æ—¢ã
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -46787,7 +47473,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -46823,6 +47509,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr "ã‚ãªãŸã® GitLab ã®ã‚°ãƒ«ãƒ¼ãƒ—"
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr "所属グループ"
@@ -46965,10 +47654,6 @@ msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-
msgid "Your groups"
msgstr "所属グループ"
@@ -47008,6 +47693,15 @@ msgstr "åå‰"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47165,6 +47859,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47339,6 +48036,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47391,6 +48091,14 @@ msgid "change"
msgid_plural "changes"
msgstr[0] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47448,10 +48156,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47607,7 +48318,7 @@ msgstr "修正済ã¿:"
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -47783,6 +48494,10 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr "%{link_to_project} ã«ã¤ã„ã¦ã‚³ãƒ¡ãƒ³ãƒˆã—ã¾ã—ãŸ"
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+
msgid "commit %{commit_id}"
msgstr "コミット %{commit_id}"
@@ -47938,9 +48653,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr "ãƒã‚¤ãƒˆæ•°ã®åˆ¶é™ (%{bytes}) を超ãˆã¦ã„ã¾ã™"
@@ -48119,6 +48831,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr "無効ãªIPアドレスã®ç¯„囲"
@@ -48158,6 +48873,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48646,6 +49367,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr "開始日より後ã«ã—ã¦ãã ã•ã„。"
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48655,10 +49379,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -48859,6 +49583,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -48912,6 +49639,10 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+
msgid "running"
msgstr ""
diff --git a/locale/ja/gitlab.po.time_stamp b/locale/ja/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/ja/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/ka_GE/gitlab.po b/locale/ka_GE/gitlab.po
index cddeca773cc..543d8a0e4f3 100644
--- a/locale/ka_GE/gitlab.po
+++ b/locale/ka_GE/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: ka\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:23\n"
+"PO-Revision-Date: 2022-12-10 06:40\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/kab/gitlab.po b/locale/kab/gitlab.po
index a10d66a0d72..52689583d43 100644
--- a/locale/kab/gitlab.po
+++ b/locale/kab/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: kab\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:25\n"
+"PO-Revision-Date: 2022-12-10 06:44\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/ko/gitlab.po b/locale/ko/gitlab.po
index 0a167e7e0ce..0f14d54f88e 100644
--- a/locale/ko/gitlab.po
+++ b/locale/ko/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: ko\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:23\n"
+"PO-Revision-Date: 2022-12-10 06:40\n"
msgid " %{start} to %{end}"
msgstr " %{start}부터 %{end}까지"
@@ -289,6 +289,10 @@ msgid "%d more comment"
msgid_plural "%d more comments"
msgstr[0] "%d 댓글 ë” ë³´ê¸°"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%dê°œì˜ ëŒ€ê¸° ì¤‘ì¸ ëŒ“ê¸€"
@@ -426,6 +430,12 @@ msgstr[0] ""
msgid "%{chartTitle} no data series"
msgstr "%{chartTitle} ë°ì´í„° 시리즈 ì—†ìŒ"
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -616,15 +626,15 @@ msgstr "%{group_name}ì€ ê·¸ë£¹ 관리 ê³„ì •ì„ ì‚¬ìš© 합니다. %{group_name}
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type} ì‚­ì œë¨"
-
-msgid "%{hook_type} was scheduled for deletion"
-msgstr "%{hook_type} 삭제 예정"
-
msgid "%{host} sign-in from new location"
msgstr "새로운 위치ì—ì„œ %{host}ì— ë¡œê·¸ì¸ë˜ì—ˆìŠµë‹ˆë‹¤."
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
+msgstr ""
+
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr ""
+
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
@@ -815,9 +825,6 @@ msgstr "%{openedEpics}개 열림, %{closedEpics}개 닫힘"
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr "%{openedIssues}개 열림, %{closedIssues}개 닫힘"
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr "%{over_limit_message} ë” ë§Žì€ íšŒì›ì„ 확보하기 위해 그룹 소유ìžëŠ” í‰ê°€íŒì„ 시작하거나 유료 등급으로 업그레ì´ë“œí•  수 있습니다."
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr "%{over_limit_message} ë” ë§Žì€ ì¢Œì„ì„ ì–»ìœ¼ë ¤ë©´ %{link_start}유료 요금제%{link_end}ë¡œ 업그레ì´ë“œí•˜ì‹­ì‹œì˜¤."
@@ -867,7 +874,7 @@ msgstr "%{reportType} %{status}"
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1003,6 +1010,10 @@ msgstr "%{template_project_id}(ì€)는 ì•Œ 수 없거나 잘못ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "%{text} is available"
msgstr "%{text} 사용 가능"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1042,6 +1053,9 @@ msgstr "%{type} ì€ %{help_link} ì´ì–´ì•¼ 합니다."
msgid "%{type} only supports %{name} name"
msgstr "%{type} ì€ %{name} ì´ë¦„만 지ì›í•©ë‹ˆë‹¤."
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr "%{userName} (머지할 수 ì—†ìŒ)"
@@ -1160,12 +1174,18 @@ msgstr "(+%{count}&nbsp;규칙)"
msgid "(Group Managed Account)"
msgstr "(그룹 관리 계정)"
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr "(변경사항 ì—†ìŒ)"
msgid "(UTC %{offset}) %{timezone}"
msgstr "(UTC %{offset}) %{timezone}"
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr "(진행 ìƒí™© 확ì¸)"
@@ -2231,6 +2251,9 @@ msgstr "시스템 í›„í¬ ì¶”ê°€"
msgid "Add text to the sign-in page. Markdown enabled."
msgstr "ë¡œê·¸ì¸ íŽ˜ì´ì§€ì— í…스트를 추가합니다. 마í¬ë‹¤ìš´ì´ 활성화ë˜ì—ˆìŠµë‹ˆë‹¤."
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr "ë³´ë“œì— ì¶”ê°€"
@@ -2318,6 +2341,9 @@ msgstr "ì´ ë²„ì „ì— ì¶”ê°€ë¨"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "GitLab ì¸ìŠ¤í„´ìŠ¤ì— 새 애플리케ì´ì…˜ì„ 추가 í•  수 없습니다. ê¶Œí•œì„ ì–»ìœ¼ë ¤ë©´ GitLab 관리ìžì—게 문ì˜í•˜ì‹­ì‹œì˜¤."
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "추가 시간(분)"
@@ -2513,6 +2539,9 @@ msgstr "ìž‘ì—… ì¤‘ì§€ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤."
msgid "AdminArea|Total users"
msgstr "ì´ ì‚¬ìš©ìž"
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr "사용ìž"
@@ -2813,7 +2842,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -2876,9 +2905,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4404,6 +4430,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr "오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„í•´ 주세요."
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr "GitLabê³¼ ì—°ë™í•˜ì—¬ 쿠버네티스(Kubernetes) í´ëŸ¬ìŠ¤í„°ë¥¼ 관리하는 프로ì íŠ¸ 예시"
@@ -4521,9 +4550,6 @@ msgstr ""
msgid "Any namespace"
msgstr "ìž„ì˜ì˜ 네임스페ì´ìŠ¤"
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr "앱 ID"
@@ -5054,6 +5080,9 @@ msgid "Are you sure you want to import %d repository?"
msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5459,6 +5488,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr "ëŒ€ìƒ URL"
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5504,6 +5536,9 @@ msgstr "ê°’"
msgid "AuditStreams|Verification token"
msgstr "ì¸ì¦ 토í°"
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "8ì›”"
@@ -5735,9 +5770,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr "사용 가능한 공유 러너:"
-
msgid "Available specific runners"
msgstr "사용 가능한 지정 러너"
@@ -6753,6 +6785,15 @@ msgstr "*-stable ë˜ëŠ” production/ 와 ê°™ì€ %{linkStart}와ì¼ë“œì¹´ë“œ%{link
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr "*-stable ë˜ëŠ” production/*ê³¼ ê°™ì€ %{linkStart}와ì¼ë“œì¹´ë“œ%{linkEnd}ê°€ 지ì›ë©ë‹ˆë‹¤."
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr "모든 브랜치"
@@ -6867,9 +6908,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr "기본값"
-msgid "BranchRules|protected"
-msgstr "보호ë¨"
-
msgid "Branches"
msgstr "브랜치"
@@ -6879,6 +6917,9 @@ msgstr "브랜치들: %{source_branch} ì—ì„œ %{target_branch} ë¡œ"
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr "브랜치들: %{source_branch} → %{target_branch} "
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr "활성"
@@ -6900,8 +6941,11 @@ msgstr ""
msgid "Branches|Compare"
msgstr "비êµ"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "'%{default_branch}'ì— ë¨¸ì§€ëœ ëª¨ë“  브랜치 지우기"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
+msgstr ""
msgid "Branches|Delete branch"
msgstr "브랜치 지우기"
@@ -6921,9 +6965,6 @@ msgstr "ë³´í˜¸ëœ ë¸Œëžœì¹˜ë¥¼ 삭제합니다. ì •ë§ í™•ì‹¤í•©ë‹ˆê¹Œ?"
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr "%{strongStart}%{branchName}%{strongEnd} 브랜치 삭제는 취소할 수 없습니다. 확실합니까?"
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "ë¨¸ì§€ëœ ë¸Œëžœì¹˜ë¥¼ 삭제하면 ë˜ëŒë¦´ 수 없습니다. 괜찮ì„까요?"
-
msgid "Branches|Filter by branch name"
msgstr "브랜치 ì´ë¦„으로 í•„í„°ë§"
@@ -6945,6 +6986,9 @@ msgstr "개요"
msgid "Branches|Please type the following to confirm:"
msgstr "다ìŒì— 있는 ë‚´ìš©ì„ ìž…ë ¥í•˜ì—¬ 확ì¸í•˜ì„¸ìš”."
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr "í™œì„±ëœ ë¸Œëžœì¹˜ 보기"
@@ -6978,6 +7022,12 @@ msgstr "Default 브랜치는 삭제할 수 없습니다."
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr "로컬 ë³€ê²½ì‚¬í•­ì„ ì·¨ì†Œí•˜ê³  업스트림 버전으로 브랜치를 ë®ì–´ì“°ë ¤ë©´ 여기서 삭제하고 '지금 ì—…ë°ì´íŠ¸'를 ì„ íƒí•˜ì„¸ìš”."
@@ -6990,6 +7040,9 @@ msgstr "예, 브랜치를 삭제합니다."
msgid "Branches|Yes, delete protected branch"
msgstr "예, ë³´í˜¸ëœ ë¸Œëžœì¹˜ë¥¼ 삭제합니다."
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr "%{branchName} 브랜치를 ì˜êµ¬ì ìœ¼ë¡œ 삭제하려고 합니다."
@@ -7044,6 +7097,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr "내장ëœ"
@@ -7849,6 +7905,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -7897,6 +7956,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "%{text}ì´(ê°€) 사용 가능한지 í™•ì¸ ì¤‘â€¦"
@@ -8016,9 +8078,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8034,7 +8093,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8058,6 +8117,9 @@ msgstr "%{minimumNumberOfUsers} (사용 ì¤‘ì¸ ì¢Œì„) ì´ìƒì´ì–´ì•¼ 합니다
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr "%{minimumNumberOfUsers} (사용 ì¤‘ì¸ ì¢Œì„ê³¼ 제한 초과 íšŒì› ëª¨ë‘) ì´ìƒì´ì–´ì•¼ 합니다. ì¢Œì„ ìˆ˜ë¥¼ 줄ì´ë ¤ë©´ 그룹ì—ì„œ 구성ì›ì„ 제거하세요."
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8301,9 +8363,15 @@ msgstr "실행 중"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr "입력 변수 키"
@@ -8316,6 +8384,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8328,6 +8399,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr "변수 행 제거"
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8337,6 +8411,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8346,6 +8423,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr "* (모든 환경)"
@@ -9035,6 +9118,9 @@ msgstr "%{userName}ì— ì˜í•´ 토í°ì´ 취소ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -9855,6 +9941,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -9879,6 +9968,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -9894,15 +9986,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr "요구 형ì‹: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}규정 준수 파ì´í”„ë¼ì¸ 구성ì´ëž€ 무엇입니까?%{linkEnd}"
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10068,7 +10169,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr "%{link} ì—°ë™ êµ¬ì„±"
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10188,6 +10289,9 @@ msgstr "연결 중"
msgid "Connecting to terminal sync service"
msgstr "í„°ë¯¸ë„ ë™ê¸°í™” ì„œë¹„ìŠ¤ì— ì—°ê²°ì¤‘"
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr "연결중..."
@@ -10227,12 +10331,6 @@ msgstr "컨테ì´ë„ˆ 레지스트리 ì´ë¯¸ì§€ë“¤"
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10494,9 +10592,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10680,9 +10775,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -10995,6 +11087,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr "êµ­ê°€"
@@ -11127,9 +11222,6 @@ msgstr "ì´ìŠˆ ìƒì„±"
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11208,6 +11300,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11244,6 +11339,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr "태그"
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11469,6 +11600,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -11878,12 +12012,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr "DORA4Metrics|실패율 변경"
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr "DORA4Metrics|실패율(í¼ì„¼íŠ¸) 변경"
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -11896,9 +12036,21 @@ msgstr "DORA4Metrics|ì¸ì‹œë˜íŠ¸ê°€ ì˜¤í”ˆëœ ê¸°ê°„ (ì¼)"
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -11917,6 +12069,9 @@ msgstr "DORA4Metrics|주어진 기간 ë™ì•ˆ 프로ë•ì…˜ 환경ì—ì„œ ì¸ì‹œë˜
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr "DORA4Metrics|ì´ ê¸°ê°„ ë™ì•ˆ ì¸ì‹œë˜íŠ¸ ì—†ìŒ"
@@ -11950,6 +12105,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr "DORA4Metrics|서비스 ë³µì› ì‹œê°„"
@@ -12010,6 +12168,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr "활성화ë˜ì§€ ì•ŠìŒ"
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12157,6 +12318,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12527,6 +12691,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12584,6 +12751,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13370,6 +13543,9 @@ msgstr "DeploymentApproval|ê±°ë¶€ë¨ %{time}"
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr "DeploymentApproval|사용ìžì—ì˜í•´ ê±°ë¶€ë¨ %{time}"
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr "GitLab 페ì´ì§€"
@@ -13406,6 +13582,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr "ë°°í¬"
@@ -13509,9 +13688,15 @@ msgstr ""
msgid "Description:"
msgstr "설명:"
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13675,6 +13860,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr "개발ìž"
@@ -14984,6 +15172,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr "아래 Bitbucket Server URL ë° ê°œì¸ ì•¡ì„¸ìŠ¤ 토í°ì„ 입력하십시오."
@@ -15347,9 +15538,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15443,6 +15631,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr "refs ë°˜ì˜ ì¤‘ 오류"
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15758,6 +15949,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -15918,6 +16118,9 @@ msgstr "모ë‘ê°€ 기여할 수 있ìŒ."
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -15948,9 +16151,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -15969,9 +16184,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16026,13 +16238,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr "사ì´ë“œë°” 확장"
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16346,9 +16561,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16505,9 +16717,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16761,6 +16970,9 @@ msgstr "2ì›”"
msgid "February"
msgstr "2ì›”"
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -16980,15 +17192,6 @@ msgstr "FLoC(Federated Learning of Cohort)"
msgid "FloC|Participate in FLoC"
msgstr "FLoCì— ì°¸ì—¬"
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17025,6 +17228,9 @@ msgstr "팔로우한 사용ìžì˜ 활ë™"
msgid "Followed users"
msgstr "팔로우한 사용ìž"
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr "글꼴 색"
@@ -17169,10 +17375,19 @@ msgstr "í¬í¬ 진행중"
msgid "Forks"
msgstr "í¬í¬"
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17251,10 +17466,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] "2022ë…„ 10ì›” 19ì¼ë¶€í„° 무료 비공개 ê·¸ë£¹ì€ %d 명으로 제한ë©ë‹ˆë‹¤."
-
msgid "From issue creation until deploy to production"
msgstr "ì´ìŠˆ ìƒì„±ì—ì„œ 프로ë•ì…˜ ë°°í¬ê¹Œì§€"
@@ -17297,6 +17508,9 @@ msgstr "ì¼ë°˜ 파ì´í”„ë¼ì¸"
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18142,6 +18356,10 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18391,6 +18609,9 @@ msgstr "GlobalSearch|최근 ì´ìŠˆ"
msgid "GlobalSearch|Recent merge requests"
msgstr "GlobalSearch|최근 머지 리퀘스트"
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr "결과가 ì—…ë°ì´íŠ¸ë˜ì—ˆìŠµë‹ˆë‹¤. %{count}ê°œì˜ ê²°ê³¼ë¥¼ 사용할 수 있습니다. 위/아래 화살표 키를 사용하여 검색 결과를 íƒìƒ‰í•˜ê±°ë‚˜ 엔터 키를 눌러 제출하세요."
@@ -18418,6 +18639,9 @@ msgstr "ì•„ëž˜ì— í‘œì‹œí•  새 ì œì•ˆì„ ìž…ë ¥í•˜ì„¸ìš”."
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr "단축키 %{kbdOpen}/%{kbdClose}ì„(를) ì´ìš©í•´ ê²€ìƒ‰ì„ ì‹œìž‘í•´ 보세요."
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr "ë¬´ì—‡ì„ ì°¾ê³  있나요?"
@@ -18595,6 +18819,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18688,6 +18915,12 @@ msgstr ""
msgid "Graph"
msgstr "그래프"
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -18877,7 +19110,7 @@ msgstr "최근 30ì¼"
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -18964,6 +19197,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19069,6 +19305,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19240,7 +19479,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr "GroupSettings|리í¬íŠ¸"
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19261,9 +19500,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19393,6 +19629,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19402,9 +19641,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr "하위 그룹 만들기"
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -19874,12 +20110,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -19913,9 +20155,6 @@ msgstr "홈페ì´ì§€"
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20084,6 +20323,9 @@ msgstr "ì‹ë³„ìž"
msgid "Identities"
msgstr "ID"
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20108,6 +20350,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20147,8 +20392,8 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr "전화 번호"
-msgid "IdentityVerification|Phone number can't be blank."
-msgstr "전화번호는 비워둘 수 없습니다."
+msgid "IdentityVerification|Phone number is required."
+msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
msgstr "ì „í™” 번호는 %{maxLength} ìžë¦¬ ì´í•˜ì—¬ì•¼ 합니다."
@@ -20189,6 +20434,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20201,9 +20452,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr "ê²°ì œ 수단 확ì¸"
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20546,6 +20806,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21113,12 +21376,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr "IncidentManagement|%{hours} 시간 %{minutes} 분 남ìŒ"
@@ -21437,6 +21706,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21708,6 +21980,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21741,6 +22016,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22089,6 +22367,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22137,8 +22418,8 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
-msgstr "InviteMembersModal|회ì›ì„ ì„ íƒí•˜ê±°ë‚˜ 초대할 ì´ë©”ì¼ ì£¼ì†Œë¥¼ 입력하십시오"
+msgid "InviteMembersModal|Please add members to invite"
+msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -22603,9 +22884,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22744,9 +23022,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23179,9 +23454,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23332,6 +23604,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23413,6 +23688,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr "키 (PEM)"
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr "Key:"
@@ -23482,9 +23760,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23558,15 +23833,18 @@ msgstr "ë¼ë²¨"
msgid "Labels"
msgstr "ë¼ë²¨"
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr "%{features} ì— ë ˆì´ë¸”ì„ ì ìš©í•  수 있습니다. 그룹 ë ˆì´ë¸”ì€ ê·¸ë£¹ë‚´ì˜ ëª¨ë“  프로ì íŠ¸ì— 사용할 수 있습니다."
-
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "ë¼ë²¨ì€ ì´ìŠˆì™€ 머지 리퀘스트(MR)ì— ì¹´í…Œê³ ë¼ì´ì¦ˆë¥¼ 위해 ì ìš©ê°€ëŠ¥í•©ë‹ˆë‹¤."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr "ë ˆì´ë¸”ì€ ë¬¸ì œ ë° ë¨¸ì§€ ë¦¬í€˜ìŠ¤íŠ¸ì— ì ìš©í•  수 있습니다. ë ˆì´ë¸”ì— ë³„í‘œë¥¼ 표시하여 ìš°ì„  순위 ë ˆì´ë¸”ë¡œ 만드십시오."
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23652,6 +23930,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23757,6 +24038,9 @@ msgstr ""
msgid "Learn More."
msgstr "ë” ì•Œì•„ë³´ê¸°."
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24426,10 +24710,10 @@ msgstr "잠긴 파ì¼"
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24480,6 +24764,9 @@ msgstr ""
msgid "Logs"
msgstr "로그"
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24585,6 +24872,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25083,6 +25373,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25104,6 +25397,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr "회ì›"
@@ -26227,6 +26526,9 @@ msgstr "프로ì íŠ¸ 추가"
msgid "Modal|Close"
msgstr "닫기"
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26290,9 +26592,6 @@ msgstr "ë” ë§Žì€ ì •ë³´"
msgid "More information"
msgstr "ë” ë§Žì€ ì •ë³´"
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26501,6 +26800,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26679,6 +26981,9 @@ msgstr "새 브랜치"
msgid "New branch unavailable"
msgstr "새로운 브랜치 사용 불가능"
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr "새로운 비공개 ì—픽 제목"
@@ -26721,6 +27026,12 @@ msgstr ""
msgid "New identity"
msgstr "신규 ID"
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "새 ì´ìŠˆ"
@@ -26994,9 +27305,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27096,6 +27404,9 @@ msgstr "ê²°ê³¼ ì—†ìŒ"
msgid "No results found"
msgstr "검색 결과가 없습니다"
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27749,9 +28060,24 @@ msgstr ""
msgid "OK"
msgstr "확ì¸"
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr "관찰 가능성"
@@ -28131,6 +28457,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28207,9 +28536,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28315,9 +28641,6 @@ msgstr "새 창으로 열기"
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28603,6 +28926,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28691,6 +29017,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr "패키지 버전 삭제"
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr "ì„ íƒí•œ 항목 ì‚­ì œ"
@@ -28706,6 +29035,12 @@ msgstr "마지막 패키지 ìžì‚°ì„ 삭제하면 %{name}ì˜ ë²„ì „ %{version}
msgid "PackageRegistry|Duplicate packages"
msgstr "중복 패키지"
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28730,6 +29065,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -28802,6 +29149,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -28817,6 +29167,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28824,6 +29177,9 @@ msgstr[0] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr "ì˜êµ¬ì ìœ¼ë¡œ ì‚­ì œ"
@@ -28878,6 +29234,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -28926,9 +29285,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -28948,6 +29304,10 @@ msgid "PackageRegistry|You are about to delete 1 asset. This operation is irreve
msgid_plural "PackageRegistry|You are about to delete %d assets. This operation is irreversible."
msgstr[0] "%d ìžì‚°ì„ 삭제하려고 합니다. ì´ ìž‘ì—…ì€ ë˜ëŒë¦´ 수 없습니다."
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -28963,6 +29323,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr "패키지 ë° ë ˆì§€ìŠ¤íŠ¸ë¦¬"
@@ -29032,6 +29395,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29095,6 +29461,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29338,6 +29713,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30253,6 +30652,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr "계ì†í•˜ê¸° ì „ì— í™•ì¸ ì´ë©”ì¼ì˜ ë§í¬ë¥¼ í´ë¦­í•˜ì‹­ì‹œì˜¤. %{html_tag_strong_start}%{email}%{html_tag_strong_end}ë¡œ 보냈습니다."
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30409,9 +30811,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30565,6 +30964,9 @@ msgstr "대시보드ì—ì„œ 기본ì ìœ¼ë¡œ 보여줄 콘í…츠를 ì„ íƒí•©ë‹ˆë‹
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30649,6 +31051,9 @@ msgstr "문법 하ì´ë¼ì´íŒ… 테마"
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30661,9 +31066,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr "시간 설정"
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr "설명ì´ë‚˜ 설명 ìƒìžì— 입력할 ë•Œ 목ë¡ì—ì„œ %{kbdOpen}Enter%{kbdClose} ì„ ëˆ„ë¥´ë©´ ì•„ëž˜ì— ìƒˆ í•­ëª©ì´ ì¶”ê°€ë©ë‹ˆë‹¤."
@@ -30781,16 +31192,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31153,6 +31573,12 @@ msgstr "ì‚¬ìš©ìž ì´ë¦„ ì—…ë°ì´íŠ¸"
msgid "Profiles|Upload new avatar"
msgstr "새 아바타 업로드"
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr "ê°œì¸ ì´ë©”ì¼ ì‚¬ìš© - %{email}"
@@ -31687,12 +32113,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31774,9 +32206,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -31891,6 +32320,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -31936,6 +32368,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr "릴리스"
@@ -32065,7 +32500,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32146,6 +32581,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32668,9 +33106,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33341,6 +33776,9 @@ msgid "Refreshing in a second to show the updated status..."
msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33537,6 +33975,9 @@ msgstr "런ë¶"
msgid "ReleaseAssetLinkType|Runbooks"
msgstr "런ë¶"
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr "릴리스 날짜"
@@ -33876,6 +34317,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -33930,10 +34374,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34266,6 +34707,9 @@ msgstr ""
msgid "Request Access"
msgstr "액세스 요청"
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34511,6 +34955,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr "재개"
@@ -34584,7 +35031,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34678,6 +35125,9 @@ msgstr "정리 실행"
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34735,7 +35185,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr "Runners|새 ë²„ì „ì„ ì‚¬ìš©í•  수 있습니다."
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34762,6 +35212,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -34783,6 +35236,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -34865,6 +35321,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr "프로ì íŠ¸ í•„í„°"
@@ -34883,6 +35342,9 @@ msgstr "GitLab 러너를 어떻게 업그레ì´ë“œí•©ë‹ˆê¹Œ?"
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -34922,12 +35384,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr "만료ë˜ì§€ ì•ŠìŒ"
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr "Runners|ê²€ìƒ‰ëœ ê²°ê³¼ê°€ 없습니다"
@@ -35019,6 +35484,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35085,6 +35553,9 @@ msgstr "Runners|러너는 CI/CD ìž‘ì—…ì„ ì‹¤í–‰í•˜ëŠ” ì—ì´ì „트입니다. %{
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr "러너는 CI/CD ìž‘ì—…ì„ ì‹¤í–‰í•˜ëŠ” ì—ì´ì „트입니다. 새 러너를 등ë¡í•˜ë ¤ë©´ 관리ìžì—게 문ì˜í•˜ì„¸ìš”."
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35133,12 +35604,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr "태그는 러너가 처리할 수 있는 ìž‘ì—… ìœ í˜•ì„ ì œì–´í•©ë‹ˆë‹¤. ëŸ¬ë„ˆì— íƒœê·¸ë¥¼ 지정하면 공유 러너가 실행할 수 있는 작업만 처리하ë„ë¡ í•  수 있습니다."
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr "러너가 등ë¡ëœ 프로ì íŠ¸, 그룹 ë˜ëŠ” ì¸ìŠ¤í„´ìŠ¤. ì¸ìŠ¤í„´ìŠ¤ 실행기는 í•­ìƒ ê´€ë¦¬ìžê°€ 소유합니다."
@@ -35251,6 +35716,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr "Runner|소유ìž"
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr "실행중"
@@ -35278,13 +35746,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr "GitLabì— ë¡œê·¸ì¸í•˜ì—¬ ì¡°ì§ì˜ ê³„ì •ì— ì—°ê²°"
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35326,6 +35797,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35341,6 +35815,15 @@ msgstr ""
msgid "SSH public key"
msgstr "SSH 공개키"
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35407,6 +35890,12 @@ msgstr "%{thenLabelStart}다ìŒ%{thenLabelEnd} 실행하려면 %{scan} 스캔 í•
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr "%{thenLabelStart}그런 다ìŒ%{thenLabelEnd} 사ì´íŠ¸ 프로필 %{siteProfile} ë° ìŠ¤ìºë„ˆ 프로필 %{scannerProfile}ë¡œ 실행하려면 %{scan} ìŠ¤ìº”ì´ í•„ìš”í•©ë‹ˆë‹¤."
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35434,9 +35923,15 @@ msgstr "스ìºë„ˆ 프로필 ì„ íƒ"
msgid "ScanExecutionPolicy|Select site profile"
msgstr "사ì´íŠ¸ 프로필 ì„ íƒ"
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr "사ì´íŠ¸ 프로필"
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr "ì—ì´ì „트"
@@ -35449,18 +35944,27 @@ msgstr "네임스페ì´ìŠ¤ ë‚´"
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35545,6 +36049,9 @@ msgstr "검색"
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36051,6 +36558,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36108,9 +36618,18 @@ msgstr "SecurityOrchestration|ì´ë¯¸ì§€ë¥¼ 로드하지 못했습니다."
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr "SecurityOrchestration|ì·¨ì•½ì  ìŠ¤ìºë„ˆë¥¼ 로드하지 못했습니다."
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36126,6 +36645,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36162,6 +36684,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36234,6 +36759,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36243,12 +36771,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36318,6 +36855,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36345,6 +36885,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36381,6 +36924,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr "보안 ì˜ì—­ì—ì„œ 모니터ë§í•  프로ì íŠ¸ë¥¼ 추가하거나 제거합니다. ì´ ëª©ë¡ì— í¬í•¨ëœ 프로ì íŠ¸ì˜ 결과는 보안 대시보드 ë° ì·¨ì•½ì  ë³´ê³ ì„œì— í‘œì‹œë©ë‹ˆë‹¤."
@@ -36390,9 +36936,24 @@ msgstr "프로ì íŠ¸ 추가"
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr "취약ì ì´ 없는 경우는 드물지만 ë°œìƒí•  수 있습니다. ì„¤ì •ì„ í™•ì¸í•˜ì—¬ 대시보드를 올바르게 설정했는지 확ì¸í•˜ì„¸ìš”."
@@ -36537,6 +37098,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -36762,6 +37326,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr "ë¼ë²¨ ì„ íƒ"
@@ -36867,6 +37434,9 @@ msgstr "ë³´ê³ ì„œ ì„ íƒ"
msgid "Select reviewer(s)"
msgstr "리뷰어 ì„ íƒí•˜ê¸°"
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37354,6 +37924,9 @@ msgstr ""
msgid "Shared Runners"
msgstr "공용 Runners"
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr "ê³µìœ ëœ í”„ë¡œì íŠ¸"
@@ -37480,6 +38053,9 @@ msgstr "최신 버전 보기"
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37637,9 +38213,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -37793,7 +38366,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -37808,13 +38381,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -37838,13 +38414,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -37982,10 +38561,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38027,6 +38606,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr "타임ë¼ì¸ ì´ë²¤íŠ¸ë¥¼ 추가하는 ë™ì•ˆ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
@@ -38045,6 +38627,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38114,6 +38699,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38507,6 +39095,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38612,6 +39203,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38675,6 +39269,9 @@ msgstr "통계"
msgid "Status"
msgstr "ìƒíƒœ"
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39411,6 +40008,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39501,9 +40101,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr "스위치 브랜치/태그"
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr "GitLab Next로 전환"
@@ -39786,6 +40383,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr "팀"
@@ -40361,6 +40964,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr "%{username}님, ë‹¤ìŒ SSH 키는 관리ìžì— ì˜í•´ ì‚­ì œ ë˜ì—ˆìŠµë‹ˆë‹¤."
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40723,9 +41329,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -40783,9 +41386,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr "ì•„ì§ íŒ¨í‚¤ì§€ê°€ 없습니다"
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr "ì•„ì§ ë³´ì•ˆ 파ì¼ì´ 없습니다."
@@ -40831,6 +41431,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr "CRM ì—°ë½ì²˜ë¥¼ 가져오는 ì¤‘ì— ë¬¸ì œê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
@@ -40951,6 +41554,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -40990,6 +41596,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr "변경 ì‚¬í•­ì„ ì €ìž¥í•˜ëŠ” ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
@@ -41083,9 +41692,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41275,6 +41881,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41293,8 +41902,8 @@ msgstr ""
msgid "This group"
msgstr "ì´ ê·¸ë£¹"
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
-msgstr "ì´ ê·¸ë£¹ê³¼ 서브 그룹, 그리고 프로ì íŠ¸ê°€ 'ì‚­ì œ 대기 중' ìƒíƒœë¡œ %{deletion_adjourned_period} ë™ì•ˆ 표시ë˜ê³ , %{date}ì— ì™„ì „ížˆ ì‚­ì œë©ë‹ˆë‹¤. 완전히 ì‚­ì œë˜ê¸° ì „ì— ê·¸ë£¹ì„ ì™„ì „ížˆ 복구할 수 있습니다."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
msgstr ""
@@ -41767,6 +42376,12 @@ msgstr "ë‚¨ì€ ì‹œê°„"
msgid "Time spent"
msgstr "소요 시간"
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42015,6 +42630,9 @@ msgstr ""
msgid "Title:"
msgstr "제목:"
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr "제목 ë° ì„¤ëª…"
@@ -42048,9 +42666,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr "표시 ì´ë¦„ì„ ì¶”ê°€í•˜ë ¤ë©´ 서비스 ë°ìŠ¤í¬ ì´ë©”ì¼ ì£¼ì†Œë¥¼ 설정하십시오. %{linkStart}ìžì„¸ížˆ 알아보기%{linkEnd}"
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42165,6 +42780,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42258,6 +42876,9 @@ msgstr ""
msgid "Todos|Design"
msgstr "ë””ìžì¸"
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr "ì—픽"
@@ -42291,6 +42912,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr "ëª¨ë‘ ì™„ë£Œë¡œ 표시"
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr "언급함"
@@ -42306,13 +42930,13 @@ msgstr "ë” ì´ìƒ í•  ì¼ì´ 없습니다. í•˜ì´ íŒŒì´ë¸Œ!"
msgid "Todos|Pipelines"
msgstr "파ì´í”„ë¼ì¸"
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr "검토 요청"
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42327,22 +42951,22 @@ msgstr "ëª¨ë‘ ì™„ë£Œí•˜ì˜€ìŠµë‹ˆë‹¤!"
msgid "Todos|Your To-Do List shows what to work on next"
msgstr "ë‹¹ì‹ ì˜ í•  ì¼ ëª©ë¡ì€ 다ìŒì— 해야 í•  ì¼ì„ ë³´ì—¬ì¤ë‹ˆë‹¤."
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42712,6 +43336,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -42748,6 +43378,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -42925,6 +43558,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43048,6 +43687,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43129,6 +43771,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr "별표 제거"
@@ -43312,6 +43957,9 @@ msgstr "터미ë„ì— ë³€ê²½ 사항 업로드 중"
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43330,18 +43978,12 @@ msgstr "Container Registryì— ëŒ€í•œ 프로ì íŠ¸ 수준 스토리지 통계는
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr "ì´ í”„ë¡œì íŠ¸ 수준 저장소 통계ì—는 사ì´íŠ¸ ì „ì²´ 중복 ì œê±°ì— ëŒ€í•œ ì ˆê°ì•¡ì´ í¬í•¨ë˜ì§€ 않으며 네임스페ì´ìŠ¤ 저장소를 합계를 계산하는 ë° ì‚¬ìš©ë˜ì§€ 않습니다."
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr "%{help_link_start}공유 러너%{help_link_end}ê°€ 비활성화ë˜ì—ˆê¸° 때문ì—, 파ì´í”„ë¼ì¸ ì‚¬ìš©ì— ëŒ€í•œ ì œí•œì´ ì„¤ì •ë˜ì§€ 않았습니다."
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr "구매한 저장 공간 중 %{percentageLeft} 사용 가능"
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43369,9 +44011,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr "UsageQuota| %{usageSince}ì´í›„ì˜ CI/CD 분 사용량"
@@ -43381,9 +44020,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr "ì´ë²ˆ ê²°ì œ 주기 사용량"
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43510,10 +44146,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr "네임스페ì´ìŠ¤ëŠ” 현재 %{strong_start}%{used_storage}%{strong_end} 네임스페ì´ìŠ¤ 저장소를 사용하고 있습니다. %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}ì—ì„œ ì‚¬ìš©ëŸ‰ì„ ë³´ê³  관리합니다. %{docs_link_start}스토리지를 줄ì´ëŠ” 방법%{link_end}."
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43522,12 +44155,6 @@ msgstr "ì´ ë„¤ìž„ìŠ¤íŽ˜ì´ìŠ¤ì˜ 프로ì íŠ¸ì—ì„œ ì´ ì‚¬ìš©ì¤‘ì¸ ì €ìž¥ ê³µ
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr "ì´ ë„¤ìž„ìŠ¤íŽ˜ì´ìŠ¤ì—는 잠긴 프로ì íŠ¸ê°€ í¬í•¨ë˜ì–´ 있습니다."
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr "ì´ ë„¤ìž„ìŠ¤íŽ˜ì´ìŠ¤ì—는 공유 러너를 사용하는 프로ì íŠ¸ê°€ 없습니다."
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43540,9 +44167,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr "ì´ ë„¤ìž„ìŠ¤íŽ˜ì´ìŠ¤ 저장 공간 사용량"
-msgid "UsageQuota|Unlimited"
-msgstr "무제한"
-
msgid "UsageQuota|Uploads"
msgstr "업로드"
@@ -43576,7 +44200,7 @@ msgstr "사용량 제한 ë„움 ë§í¬"
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr "ì‚¬ìš©ìž ì„¤ì • &gt; 사용 할당량"
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43585,17 +44209,17 @@ msgstr "위키"
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
-msgstr "구입한 저장 ê³µê°„ì´ ë¶€ì¡±í•©ë‹ˆë‹¤. 프로ì íŠ¸ê°€ ìž ê¸°ëŠ”ê²ƒì„ ë°©ì§€í•˜ë ¤ë©´, ë” ë§Žì€ ì¶”ê°€ 저장 ê³µê°„ì„ êµ¬ë§¤í•˜ì„¸ìš”."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
+msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
msgstr ""
@@ -43768,6 +44392,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44033,9 +44660,6 @@ msgstr "ê°œì¸ í”„ë¡œì íŠ¸"
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr "%{pronunciation} ë¡œ ë°œìŒ"
-msgid "UserProfile|Report abuse"
-msgstr "악용사례 신고"
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44225,6 +44849,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44297,6 +44924,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44315,6 +44945,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44363,10 +44996,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44378,7 +45011,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44435,6 +45068,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44444,6 +45092,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45101,6 +45761,9 @@ msgstr "%{humanized_resource_name}ì—ì„œ 잠재ì ì¸ ìŠ¤íŒ¸ì„ íƒì§€í–ˆìŠµë‹ˆë
msgid "We don't have enough data to show this stage."
msgstr "ì´ ë‹¨ê³„ë¥¼ ë³´ì—¬ì£¼ê¸°ì— ì¶©ë¶„í•œ ë°ì´í„°ê°€ 없습니다."
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr "ë‹¤ìŒ ì˜¤ë¥˜ë¥¼ 발견했습니다:"
@@ -45176,9 +45839,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45194,24 +45854,12 @@ msgstr "프로ì íŠ¸ì˜ 여러 파ì¼ì„ 빠르고 쉽게 편집합니다."
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr "프로ì íŠ¸ì˜ 여러 파ì¼ì„ 빠르고 쉽게 편집합니다. . ì„ ëˆŒëŸ¬ì„œ 열어보세요."
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45242,6 +45890,18 @@ msgstr "웹훅 설정"
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr "웹훅"
@@ -45308,9 +45968,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr "댓글"
-
msgid "Webhooks|Confidential comments"
msgstr "비공개 댓글"
@@ -45359,13 +46016,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr "머지 리퀘스트 ì´ë²¤íŠ¸"
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45404,9 +46058,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -45796,6 +46447,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -45805,9 +46459,6 @@ msgstr "와ì´ì–´í”„레임"
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr "액세스 요청 철회"
@@ -45826,6 +46477,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -45841,9 +46498,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr "ë°˜ë³µì— ì¶”ê°€"
@@ -45863,6 +46517,9 @@ msgstr[0] "담당ìž"
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -45872,6 +46529,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr "작업 축소"
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -45884,30 +46547,39 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr "%{workItemType} 삭제"
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
+msgid "WorkItem|Existing task"
+msgstr ""
+
msgid "WorkItem|Expand tasks"
msgstr "작업 펼침"
msgid "WorkItem|Incident"
msgstr "ì¸ì‹œë˜íŠ¸"
-msgid "WorkItem|Introducing tasks"
-msgstr ""
-
msgid "WorkItem|Issue"
msgstr "ì´ìŠˆ"
msgid "WorkItem|Iteration"
msgstr "반복"
-msgid "WorkItem|Learn about tasks."
-msgstr "태스í¬ì— 대해 알아보기"
+msgid "WorkItem|Key result"
+msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr "반복 ì—†ìŒ"
@@ -45917,12 +46589,18 @@ msgstr "ì¼ì¹˜í•˜ëŠ” 결과가 없습니다"
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr "현재 í• ë‹¹ëœ ìž‘ì—…ì´ ì—†ìŠµë‹ˆë‹¤. ìž‘ì—…ì„ ì‚¬ìš©í•˜ì—¬ ì´ ë¬¸ì œë¥¼ ë” ìž‘ì€ ë¶€ë¶„ìœ¼ë¡œ 나눕니다."
msgid "WorkItem|None"
msgstr "ì—†ìŒ"
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr "최소 리í¬í„° ì—­í•  ì´ìƒì˜ 프로ì íŠ¸ 구성ì›ê³¼ 작성ìž, 그리고 담당ìžë§Œ ì´ ìž‘ì—…ì„ ë³´ê±°ë‚˜ ì•Œë¦¼ì„ ë°›ì„ ìˆ˜ 있습니다."
@@ -45935,9 +46613,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr "요구 사항"
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr "%{workItemType} ìƒì„± ì‹œ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„í•´ 주세요."
@@ -46001,7 +46688,7 @@ msgstr "WorkItem|기밀성 활성화"
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46031,6 +46718,9 @@ msgstr "댓글 쓰기..."
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46197,10 +46887,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46239,10 +46929,6 @@ msgstr "ë‚˜ì¤‘ì— ì–¸ì œë“ ì§€ URLì„ ë³€ê²½í•  수 있습니다."
msgid "You can always edit this later"
msgstr "ë‚˜ì¤‘ì— ì–¸ì œë“ ì§€ 수정할 수 있습니다."
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] "ì´ì œ %{namespace_name} ì—ì„œ 멤버 ì´ë™ì„ 시작할 수 있습니다. %{strong_start}좌ì„ì—ì„œ%{strong_end}삭제하면 구성ì›ì´ ê·¸ë£¹ì— ì•¡ì„¸ìŠ¤í•  수 없습니다. 2022ë…„ 10ì›” 19ì¼ ì´í›„ì— %{strong_start}좌ì„ì—ì„œ%{strong_end} %{free_user_limit} ì´ìƒì˜ 구성ì›ì´ í™œì„±í™”ëœ ê²½ìš° 액세스를 유지할 %{free_user_limit} ëª…ì˜ êµ¬ì„±ì›ì„ ì„ íƒí•©ë‹ˆë‹¤. 먼저 ì†Œìœ ìž ë° ìœ ì§€ ê´€ë¦¬ìž ì—­í• ì´ ìžˆëŠ” 구성ì›ì„ 계산한 ë‹¤ìŒ êµ¬ì„±ì›ì´ %{free_user_limit} ëª…ì— ë„달할 때까지 가장 ìµœê·¼ì— í™œë™í•œ 구성ì›ì„ 계산합니다. 나머지 구성ì›ì€ í•œë„ ì´ˆê³¼ ìƒíƒœê°€ ë˜ê³  ê·¸ë£¹ì— ì•¡ì„¸ìŠ¤í•  수 없게 ë©ë‹ˆë‹¤."
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46309,9 +46995,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "%{project_name} ì— ìƒˆ 회ì›ì„ 초대하거나 다른 ê·¸ë£¹ì„ ì´ˆëŒ€í•  수 있습니다."
@@ -46330,18 +47013,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr "ì´ì œ 보안 대시보드를 CSV 보고서로 내보낼 수 있습니다."
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46423,6 +47100,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46477,6 +47157,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46538,6 +47221,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46589,9 +47275,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -46769,6 +47452,9 @@ msgstr "ì´ë¯¸ ì¼íšŒìš© ì¸ì¦ê¸°ë¥¼ ì´ìš©í•˜ì—¬ ì´ì¤‘ ì¸ì¦ì„ 활성화했
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -46787,7 +47473,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -46823,6 +47509,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr "ë‚˜ì˜ ê·¸ë£¹"
@@ -46965,10 +47654,6 @@ msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] "무료 ê·¸ë£¹ì€ ì´ì œ %d 명으로 제한ë©ë‹ˆë‹¤."
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] "ê·€í•˜ì˜ ê·¸ë£¹ì¸ %{strong_start}%{namespace_name}%{strong_end} ì—는 %{free_user_limit} 명 ì´ìƒì˜ 회ì›ì´ 있습니다. 2022ë…„ 10ì›” 19ì¼ë¶€í„° 가장 ìµœê·¼ì— í™œë™í•œ %{free_user_limit} ëª…ì˜ êµ¬ì„±ì›ì´ 활ë™ì„ 유지하고 나머지 구성ì›ì€ %{link_start}명 초과 ìƒíƒœ%{link_end} ì´ ë˜ì–´ ê·¸ë£¹ì— ì•¡ì„¸ìŠ¤í•  수 없습니다. 사용 할당량 페ì´ì§€ë¡œ ì´ë™í•˜ì—¬ ê·¸ë£¹ì— ë‚¨ì„ %{free_user_limit} ëª…ì˜ êµ¬ì„±ì›ì„ 관리할 수 있습니다."
-
msgid "Your groups"
msgstr "ë‹¹ì‹ ì˜ ê·¸ë£¹"
@@ -47008,6 +47693,15 @@ msgstr "ê·€í•˜ì˜ ì´ë¦„"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47165,6 +47859,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47339,6 +48036,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47391,6 +48091,14 @@ msgid "change"
msgid_plural "changes"
msgstr[0] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr "%{criticalStart}중요%{criticalEnd}, %{highStart}높ìŒ%{highEnd} ë° %{otherStart}기타%{otherEnd}"
@@ -47448,11 +48156,14 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr "%{scanner}가 %{vulnStr} 가능성 %{strong_start}%{number}%{strong_end} 개를 검출함"
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
-msgstr "ciReport|%{scanner}ê°€ no %{boldStart} 새로운 %{boldEnd} 잠재 보안 취약ì ë“¤ íƒì§€í•¨"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
+msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
-msgstr "%{scanner}ê°€ %{strong_start}새로운%{strong_end} %{vulnStr} ê°€ëŠ¥ì„±ì„ ê²€ì¶¯í•˜ì§€ 못함"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
+msgstr ""
msgid "ciReport|: Loading resulted in an error"
msgstr ""
@@ -47607,7 +48318,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -47783,6 +48494,10 @@ msgstr "코멘트"
msgid "commented on %{link_to_project}"
msgstr "%{link_to_project}ì— ëŒ“ê¸€ì„ ë‹¬ì•˜ìŠµë‹ˆë‹¤."
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -47938,9 +48653,6 @@ msgstr "example.com"
msgid "exceeds maximum length (100 usernames)"
msgstr "최대 ê¸¸ì´ ì´ˆê³¼ (ì‚¬ìš©ìž ì´ë¦„ 100ê°œ)"
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48119,6 +48831,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48158,6 +48873,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48646,6 +49367,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48655,12 +49379,12 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
-msgstr "최ìƒìœ„ 네임스페ì´ìŠ¤ì—¬ì•¼ 합니다."
-
msgid "must be unique by status and elapsed time within a policy"
msgstr ""
+msgid "must belong to same project of its requirement object."
+msgstr ""
+
msgid "must belong to same project of the work item."
msgstr ""
@@ -48859,6 +49583,9 @@ msgstr "reCAPTCHA ê°œì¸ í‚¤"
msgid "reCAPTCHA site key"
msgstr "reCAPTCHA 사ì´íŠ¸ 키"
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -48912,6 +49639,10 @@ msgstr "저장소:"
msgid "role's base access level does not match the access level of the membership"
msgstr "ì—­í• ì˜ ê¸°ë³¸ 액세스 ìˆ˜ì¤€ì´ ë©¤ë²„ì‹­ì˜ ì•¡ì„¸ìŠ¤ 수준과 ì¼ì¹˜í•˜ì§€ 않습니다."
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+
msgid "running"
msgstr ""
diff --git a/locale/ko/gitlab.po.time_stamp b/locale/ko/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/ko/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/ku_TR/gitlab.po b/locale/ku_TR/gitlab.po
index 8ec30aab0db..f804f1e7a55 100644
--- a/locale/ku_TR/gitlab.po
+++ b/locale/ku_TR/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: ku\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:23\n"
+"PO-Revision-Date: 2022-12-10 06:40\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/ky_KG/gitlab.po b/locale/ky_KG/gitlab.po
index 56c9ad12bc8..605f2c6e8fd 100644
--- a/locale/ky_KG/gitlab.po
+++ b/locale/ky_KG/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: ky\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:23\n"
+"PO-Revision-Date: 2022-12-10 06:42\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/lt_LT/gitlab.po b/locale/lt_LT/gitlab.po
index ef0f793bf9d..0a31de96f18 100644
--- a/locale/lt_LT/gitlab.po
+++ b/locale/lt_LT/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: lt\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:23\n"
+"PO-Revision-Date: 2022-12-10 06:40\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -451,6 +451,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -666,6 +673,12 @@ msgstr[3] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -886,13 +899,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -1088,9 +1101,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -1143,7 +1153,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1318,6 +1328,13 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1357,6 +1374,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1478,12 +1498,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2630,6 +2656,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2717,6 +2746,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2912,6 +2944,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -3212,7 +3247,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3275,9 +3310,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4806,6 +4838,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4923,9 +4958,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5480,6 +5512,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5894,6 +5929,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5939,6 +5977,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -6170,9 +6211,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -7200,6 +7238,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7314,9 +7361,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7326,6 +7370,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7347,7 +7394,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7368,9 +7418,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7392,6 +7439,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7425,6 +7475,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7437,6 +7493,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7491,6 +7550,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -8299,6 +8361,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8347,6 +8412,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8472,9 +8540,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8490,7 +8555,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8514,6 +8579,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8757,9 +8825,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8772,6 +8846,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8784,6 +8861,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8793,6 +8873,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8802,6 +8885,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9497,6 +9586,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10320,6 +10412,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10344,6 +10439,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10359,15 +10457,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10533,7 +10640,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10653,6 +10760,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10692,12 +10802,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10968,9 +11072,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -11154,9 +11255,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11469,6 +11567,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11601,9 +11702,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11682,6 +11780,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11718,6 +11819,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11943,6 +12080,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12355,12 +12495,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12373,9 +12519,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12394,6 +12552,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12427,6 +12588,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12487,6 +12651,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12634,6 +12801,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -13007,6 +13177,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -13064,6 +13237,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13868,6 +14047,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13904,6 +14086,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -14010,9 +14195,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -14179,6 +14370,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15500,6 +15694,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15863,9 +16060,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15959,6 +16153,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -16274,6 +16471,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16437,6 +16643,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16467,9 +16676,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16488,9 +16709,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16545,13 +16763,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16871,9 +17092,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -17030,9 +17248,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -17289,6 +17504,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17508,15 +17726,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17553,6 +17762,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17697,10 +17909,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17782,13 +18003,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17831,6 +18045,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18682,6 +18899,13 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18931,6 +19155,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18958,6 +19185,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -19135,6 +19365,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -19228,6 +19461,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19417,7 +19656,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19504,6 +19743,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19609,6 +19851,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19780,7 +20025,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19801,9 +20046,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19933,6 +20175,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19942,9 +20187,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20426,12 +20668,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20465,9 +20713,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20636,6 +20881,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20660,6 +20908,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20699,7 +20950,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20741,6 +20992,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20753,9 +21010,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -21107,6 +21373,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21674,12 +21943,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21998,6 +22273,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -22272,6 +22550,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -22305,6 +22586,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22653,6 +22937,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22701,7 +22988,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -23170,9 +23457,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -23311,9 +23595,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23746,9 +24027,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23899,6 +24177,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23980,6 +24261,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -24049,9 +24333,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -24128,15 +24409,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -24225,6 +24509,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -24330,6 +24617,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -25017,10 +25307,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -25071,6 +25361,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -25176,6 +25469,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25674,6 +25970,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25695,6 +25994,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26830,6 +27135,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26893,9 +27201,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -27107,6 +27412,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -27288,6 +27596,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -27330,6 +27641,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27603,9 +27920,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27705,6 +28019,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -28373,9 +28690,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28758,6 +29090,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28837,9 +29172,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28945,9 +29277,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -29233,6 +29562,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -29324,6 +29656,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -29339,6 +29674,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -29363,6 +29704,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29435,6 +29788,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29450,6 +29806,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29460,6 +29819,9 @@ msgstr[3] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29514,6 +29876,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29562,9 +29927,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29587,6 +29949,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29602,6 +29971,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29671,6 +30043,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29734,6 +30109,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29977,6 +30361,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30892,6 +31300,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -31048,9 +31459,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -31204,6 +31612,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -31288,6 +31699,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -31300,9 +31714,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -31420,16 +31840,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31792,6 +32221,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -32326,12 +32761,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -32413,9 +32854,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32530,6 +32968,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32575,6 +33016,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32704,7 +33148,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32785,6 +33229,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -33307,9 +33754,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33983,6 +34427,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -34182,6 +34629,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34521,6 +34971,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34575,10 +35028,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34929,6 +35379,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -35180,6 +35633,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -35256,7 +35712,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -35353,6 +35809,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -35419,7 +35878,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -35446,6 +35905,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35467,6 +35929,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35552,6 +36017,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35570,6 +36038,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35609,12 +36080,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35709,6 +36183,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35775,6 +36252,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35823,12 +36303,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35944,6 +36418,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35971,13 +36448,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -36019,6 +36499,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -36034,6 +36517,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -36100,6 +36592,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -36127,9 +36625,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -36142,18 +36646,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -36238,6 +36751,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36777,6 +37293,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36834,9 +37353,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36852,6 +37380,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36888,6 +37419,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36960,6 +37494,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36969,12 +37506,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -37044,6 +37590,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -37071,6 +37620,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -37107,6 +37659,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -37116,9 +37671,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -37263,6 +37833,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37488,6 +38061,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37593,6 +38169,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -38083,6 +38662,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -38209,6 +38791,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -38369,9 +38954,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38525,7 +39107,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38540,13 +39122,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38570,13 +39155,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38714,10 +39302,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38759,6 +39347,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38777,6 +39368,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38846,6 +39440,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -39239,6 +39836,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -39344,6 +39944,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -39407,6 +40010,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -40146,6 +40752,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -40236,9 +40845,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40521,6 +41127,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -41111,6 +41723,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -41479,9 +42094,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41539,9 +42151,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41587,6 +42196,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41707,6 +42319,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41746,6 +42361,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41839,9 +42457,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -42031,6 +42646,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -42049,7 +42667,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42523,6 +43141,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42777,6 +43401,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42810,9 +43437,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42927,6 +43551,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -43020,6 +43647,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -43053,6 +43683,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -43068,13 +43701,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -43089,22 +43722,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -43477,6 +44110,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43513,6 +44152,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43690,6 +44332,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43813,6 +44461,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43894,6 +44545,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -44077,6 +44731,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -44095,18 +44752,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -44134,9 +44785,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -44146,9 +44794,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -44275,10 +44920,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -44287,12 +44929,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -44305,9 +44941,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -44341,7 +44974,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -44350,16 +44983,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44533,6 +45166,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44801,9 +45437,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44993,6 +45626,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -45065,6 +45701,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -45083,6 +45722,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -45131,10 +45773,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -45146,7 +45788,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -45203,6 +45845,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -45212,6 +45869,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45878,6 +46547,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45953,9 +46625,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45971,24 +46640,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -46019,6 +46676,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -46085,9 +46754,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -46136,13 +46802,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -46181,9 +46844,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46579,6 +47239,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46588,9 +47251,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46609,6 +47269,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46624,9 +47290,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46649,6 +47312,9 @@ msgstr[3] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46658,6 +47324,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46670,16 +47342,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46688,12 +47363,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46703,12 +47384,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46721,9 +47408,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46787,7 +47483,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46817,6 +47513,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46986,10 +47685,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -47028,13 +47727,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -47101,9 +47793,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -47122,18 +47811,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -47215,6 +47898,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -47269,6 +47955,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -47333,6 +48022,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -47384,9 +48076,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47564,6 +48253,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47582,7 +48274,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47618,6 +48310,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47763,13 +48458,6 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "Your groups"
msgstr ""
@@ -47809,6 +48497,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47969,6 +48666,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -48152,6 +48852,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -48207,6 +48910,20 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -48264,10 +48981,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -48432,7 +49152,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48614,6 +49334,13 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48775,9 +49502,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48968,6 +49692,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -49007,6 +49734,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -49510,6 +50243,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -49519,10 +50255,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49732,6 +50468,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49791,6 +50530,13 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "running"
msgstr ""
diff --git a/locale/mk_MK/gitlab.po b/locale/mk_MK/gitlab.po
index 6505cd62af3..ee086614d57 100644
--- a/locale/mk_MK/gitlab.po
+++ b/locale/mk_MK/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: mk\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:23\n"
+"PO-Revision-Date: 2022-12-10 06:40\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/ml_IN/gitlab.po b/locale/ml_IN/gitlab.po
index c191d76d2ab..5b871f6dff1 100644
--- a/locale/ml_IN/gitlab.po
+++ b/locale/ml_IN/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: ml-IN\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:25\n"
+"PO-Revision-Date: 2022-12-10 06:43\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/mn_MN/gitlab.po b/locale/mn_MN/gitlab.po
index 5f25cea5f66..79b5e9f5a15 100644
--- a/locale/mn_MN/gitlab.po
+++ b/locale/mn_MN/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: mn\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:23\n"
+"PO-Revision-Date: 2022-12-10 06:40\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/nb_NO/gitlab.po b/locale/nb_NO/gitlab.po
index 0ee100f3d99..66aa03eb0c3 100644
--- a/locale/nb_NO/gitlab.po
+++ b/locale/nb_NO/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: nb\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:24\n"
+"PO-Revision-Date: 2022-12-10 06:43\n"
msgid " %{start} to %{end}"
msgstr " %{start} til %{end}"
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] "%d flere kommentarer"
msgstr[1] "%d flere kommentarer"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%d avventende kommentar"
@@ -506,6 +511,12 @@ msgstr[1] "%{bold_start}%{count}%{bold_end} åpnede fletteforespørsler"
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr "%{code_open}Maskert:%{code_close} Skjult i jobblogger. MÃ¥ oppfylle kravene til maskering."
@@ -706,15 +717,15 @@ msgstr "%{group_name} bruker gruppeadministrerte kontoer. Du må opprette en ny
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr "%{group_name}&%{epic_iid} &middot; opprettet %{epic_created} av %{author}"
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type} ble slettet"
-
-msgid "%{hook_type} was scheduled for deletion"
-msgstr "%{hook_type} ble planlagt for sletting"
-
msgid "%{host} sign-in from new location"
msgstr "%{host} pålogging fra nytt sted"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
+msgstr ""
+
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr ""
+
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr "%{integrations_link_start}Integrasjoner%{link_end} lar deg gjøre tredjepartsapplikasjoner til en del av GitLab-arbeidsflyten. Hvis de tilgjengelige integrasjonene ikke tilfredsstiller dine behov, kan du vurdere å bruke en %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr "%{openedEpics} åpne, %{closedEpics} lukket"
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr "%{openedIssues} åpne, %{closedIssues} lukket"
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,8 +967,8 @@ msgstr "%{reportType} %{status}"
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr "%{reportType} oppdaget %{totalStart}%{total}%{totalEnd} potensial %{vulnMessage}"
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
-msgstr "%{reportType} oppdaget ingen %{totalStart}nye%{totalEnd} sårbarheter."
+msgid "%{reportType} detected no new vulnerabilities."
+msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
msgstr "%{retryButtonStart}Prøv igjen%{retryButtonEnd} eller %{newFileButtonStart}legg ved en ny fil%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr "%{template_project_id} er ukjent eller ugyldig"
msgid "%{text} is available"
msgstr "%{text} er tilgjengelig"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr "%{timebox_type} støtter ikke utbrenningskart"
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr "%{type} støtter kun %{name} navn"
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr "%{userName} (kan ikke flette)"
@@ -1266,12 +1282,18 @@ msgstr "(+%{count}&nbsp;regler)"
msgid "(Group Managed Account)"
msgstr "(Gruppestyrt konto)"
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr "(Ingen endringer)"
msgid "(UTC %{offset}) %{timezone}"
msgstr "(UTC %{offset}) %{timezone}"
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr "(sjekk fremgang)"
@@ -2364,6 +2386,9 @@ msgstr "Legg til systemkrok"
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr "Legg til på bord"
@@ -2451,6 +2476,9 @@ msgstr "Lagt til i denne versjonen"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "Å legge til nye applikasjoner er deaktivert i GitLab-instansen din. Ta kontakt med GitLab-administratoren din for å få tillatelse til det"
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "Ytterligere minutter"
@@ -2646,6 +2674,9 @@ msgstr "Stopp jobber som mislyktes"
msgid "AdminArea|Total users"
msgstr "Totalt antall brukere"
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr "Brukere"
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr "Velg en CI/CD-mal"
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr "En feil oppstod. Vennligst logg inn på nytt."
msgid "An error occurred. Please try again."
msgstr "En feil oppstod. Vennligst prøv igjen."
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr "Enhver milepæl"
msgid "Any namespace"
msgstr "Ethvert navneområde"
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr "App-ID"
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] "Er du sikker på at du vil importere %d kodelager?"
msgstr[1] "Er du sikker på at du vil importere %d kodelagre?"
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "Aug"
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "Grener"
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr "Aktiv"
@@ -7049,8 +7092,11 @@ msgstr ""
msgid "Branches|Compare"
msgstr "Sammenlign"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "Slett alle grener som er flettet sammen til '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
+msgstr ""
msgid "Branches|Delete branch"
msgstr "Slett gren"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "Å slette de flettede grenene kan ikke angres på. Er du sikker?"
-
msgid "Branches|Filter by branch name"
msgstr "Filtrer etter grennavn"
@@ -7094,6 +7137,9 @@ msgstr "Oversikt"
msgid "Branches|Please type the following to confirm:"
msgstr "Vennligst skriv inn det følgende for å bekrefte:"
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr "Vis aktive grener"
@@ -7127,6 +7173,12 @@ msgstr "Standardgrenen kan ikke slettes"
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr "Ja, slett grenen"
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr "Laster inn artefakter"
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr "Innebygget"
@@ -7999,6 +8057,9 @@ msgstr "Rørledning #%{pipeline_id} %{humanized_status} på %{duration}"
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr "Rørledning %{pipeline_link} av %{ref_type} %{ref_link} av %{user_combined_name} %{humanized_status}"
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr "Etikett"
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "Kontrollerer %{text} tilgjengelighet …"
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr "Rediger"
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr "Mislyktes i å laste inn land. Vennligst prøv igjen."
msgid "Checkout|Failed to load states. Please try again."
msgstr "Mislyktes i å laste inn delstater. Vennligst prøv igjen."
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr "kjørende"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr "Miljøer"
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr "Nøkkel"
msgid "CiVariables|Masked"
msgstr "Maskert"
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr "Fjern variabel"
msgid "CiVariables|Remove variable row"
msgstr "Fjern variabel-rad"
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr "Omfang"
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr "Tilstand"
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr "Type"
@@ -8498,6 +8577,12 @@ msgstr "Verdi"
msgid "CiVariables|Variables"
msgstr "Variabler"
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr "* (Alle miljøer)"
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr "Ukjent bruker"
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr "Konfigurer %{link}-integrasjonen."
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr "Tilkobler"
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr "Kobler til..."
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr "Container-kodelagre"
-
-msgid "Container repository"
-msgstr "Container-kodelager"
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr "Etiketten ble vellykket merket for sletting."
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr "Etikettene ble vellykket merket for sletting."
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr "Land"
@@ -11285,9 +11382,6 @@ msgstr "Lag en rapport"
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr "Opprett iterasjon"
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr "Opprett utdrag"
msgid "Create tag %{tagName}"
msgstr "Opprett etiketten %{tagName}"
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr "Du har ikke tillatelse til å opprette grupper."
msgid "CreateTag|Tag"
msgstr "Etikett"
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr "%{name} (standard)"
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr "Kritiske sårbarheter til stede"
@@ -12037,12 +12173,18 @@ msgstr "%{startDate} - %{endDate}"
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr "Minimum = 1 sekund, Maks = 3600 sekunder"
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr "Ny skannerprofil"
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr "Dager igjen til sammenslåing"
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr "Standard første ukedag"
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr "Distribusjoner"
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr "Beskrivelse:"
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr "Beskrivende stempel"
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr "DevOps-adopsjon"
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr "Skriv inn minst tre tegn for å søke"
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr "Fjern epos"
msgid "Epics|Remove issue"
msgstr "Fjern saker"
-msgid "Epics|Show more"
-msgstr "Vis mer"
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr "min"
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr "Estimat"
@@ -16091,6 +16293,9 @@ msgstr "Alle kan bidra"
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr "Eksempel: @sub\\.firma\\.com$"
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr "Eksempler"
@@ -16142,9 +16359,6 @@ msgstr "Ekskludert innflettings-commiter. Begrenset til 6 000 commiter."
msgid "Execution time"
msgstr "Iverksettelsestid"
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr "Eksisterende greinnavn, etikett, eller commit-SHA"
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr "Utvid sidepanelet"
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr "Forventede dokumenter: %{expected_documents}"
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr "Mislyktes i å installere."
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr "Mislyktes i å oppdatere-sakerstatus"
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr "Mislyktes i å upgrade."
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr "Feb"
msgid "February"
msgstr "Februar"
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr "Fulgte brukere"
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr "Skriftfarge"
@@ -17345,12 +17553,21 @@ msgstr "Utgreining pågår"
msgid "Forks"
msgstr "Utgreininger"
-msgid "Format: %{dateFormat}"
-msgstr "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
+msgstr "Format: %{dateFormat}"
+
msgid "Found errors in your %{gitlab_ci_yml}:"
msgstr "Fant feil i din %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr "Fra %{code_open}%{source_title}%{code_close} til"
msgid "From %{providerTitle}"
msgstr "Fra %{providerTitle}"
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr "Generelle rørledninger"
msgid "General settings"
msgstr "Hovedinnstillinger"
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr "Generer et standardsett med stempler"
@@ -18322,6 +18537,11 @@ msgstr "GitLab-versjon"
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr "Resultatene er oppdatert. %{count} resultater tilgjengelig. Bruk ↑- og ↓-piltastene for å navigere i søkeresultatlisten, eller ENTER for å sende inn."
@@ -18598,6 +18821,9 @@ msgstr "Skriv inn for at nye forslag skal vises nedenfor."
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr "Hva søker du etter?"
@@ -18775,6 +19001,9 @@ msgstr "Gå til dine fletteforespørsler"
msgid "Go to your projects"
msgstr "GÃ¥ til dine prosjekter"
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr "GÃ¥ til utdragene dine"
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr "Diagram"
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr "GraphViewType|Jobbavhengigheter"
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr "Medlemmer lagt til"
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr "Innen 3 år"
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr "SAML-gruppenavn"
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr "SAML-gruppenavn: %{saml_group_name}"
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr "Koble til instans"
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr "Kontakt en administrator for å aktivere alternativer for å importere gruppen din."
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr "Opprett gruppe"
@@ -19582,9 +19823,6 @@ msgstr "Opprett ny gruppe"
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr "GitLab-kilde-URL"
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr "Høyeste rolle:"
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr "Alarmhendelser:"
@@ -20097,9 +20341,6 @@ msgstr "Hjemmeside"
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr "Kroken ble vellykket oppdatert."
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr "Identifikasjoner"
msgid "Identities"
msgstr "Identiteter"
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr "Hendelse"
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr "Skru på kommentarer"
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr "GitLab-administratorer kan sette opp integrasjoner som alle grupper og p
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr "GitLab-administratorer kan sette opp integrasjoner som alle prosjekter i en gruppe arver og benytter som standard. Disse integrasjonene gjelder for alle prosjekter som ikke allerede bruker tilpassede innstillinger. Du kan overstyre tilpassede innstillinger for et prosjekt dersom innstillingene er nødvendige på det nivået. Lær mer om %{integrations_link_start}integrasjonsbehandling på gruppenivå%{link_end}."
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr "Tittel"
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr "Ingen jobber å vise"
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr "Nøkkel"
msgid "Key (PEM)"
msgstr "Nøkkel (PEM)"
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr "Kubernetes-feil: %{error_code}"
-
msgid "LDAP"
msgstr "LDAP"
@@ -23748,15 +24025,18 @@ msgstr "Stempler"
msgid "Labels"
msgstr "Stempler"
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr "Stempler kan brukes på %{features}. Gruppestempler er tilgjengelige for ethvert prosjekt innenfor gruppen."
-
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr "Stempler uten saker i denne iterasjonen:"
@@ -23843,6 +24123,9 @@ msgstr "Senest redigert av %{link_start}%{avatar} %{name}%{link_end}"
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr "Senest endret"
@@ -23948,6 +24231,9 @@ msgstr "Lær mer"
msgid "Learn More."
msgstr "Les mer."
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,12 +24909,12 @@ msgstr "LÃ¥ste filer"
msgid "Locked by %{fileLockUserName}"
msgstr "LÃ¥st av %{fileLockUserName}"
+msgid "Locked files"
+msgstr ""
+
msgid "Locked the discussion."
msgstr "LÃ¥ste diskusjonen."
-msgid "Locks give the ability to lock specific file or folder."
-msgstr ""
-
msgid "Locks the discussion."
msgstr "LÃ¥ser diskusjonen."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr "Logger"
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr "Medlemmer"
@@ -26428,6 +26729,9 @@ msgstr "Legg til prosjekter"
msgid "Modal|Close"
msgstr "Lukk"
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr "Endret"
@@ -26491,9 +26795,6 @@ msgstr "Mer informasjon"
msgid "More information"
msgstr "Mer informasjon"
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr "Ny gren"
msgid "New branch unavailable"
msgstr "Ny gren utilgjengelig"
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr "Tittel på nytt konfidensielt epos "
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr "Ny identitet"
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "Ny sak"
@@ -27197,9 +27510,6 @@ msgstr "Ingen saker funnet"
msgid "No iteration"
msgstr "Ingen iterasjon"
-msgid "No iterations to show"
-msgstr "Ingen iterasjoner å vise"
-
msgid "No job log"
msgstr "Ingen jobblogg"
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr "OK"
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr "Ã…pnes i et nytt vindu"
msgid "Opens new window"
msgstr "Ã…pner et nytt vindu"
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr "Handlingen er ikke tillatt"
@@ -28813,6 +29138,9 @@ msgstr "Conan"
msgid "PackageRegistry|Conan Command"
msgstr "Conan-kommando"
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr "Kopier .pypirc-innhold"
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr "Generisk"
@@ -29013,6 +29362,9 @@ msgstr "NuGet-kommando"
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr "Pakkeregister"
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr "Vis PyPi-kommandoer"
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr "For å utvide søket, endre eller fjerne filtrene ovenfor."
-msgid "PackageRegistry|Type"
-msgstr "Type"
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr "npm"
msgid "PackageRegistry|published by %{author}"
msgstr "publisert av %{author}"
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr "Parameter"
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr "Parent"
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr "Telefon"
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr "Velg et navn"
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "Velg hvilket innhold du vil se på et prosjekts oversiktsside."
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr "Syntaksfremhevingstema"
msgid "Preferences|Tab width"
msgstr "Fanebredde"
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr "Denne innstillingen lar deg tilpasse oppførselen til systemoppsettet og
msgid "Preferences|Time preferences"
msgstr "Tidspreferanser"
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr "Bruk relative tidspunkter"
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr "Fortsett"
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr "Oppdater brukernavn"
msgid "Profiles|Upload new avatar"
msgstr "Last opp ny avatar"
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr "Bruk en privat e-post - %{email}"
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr "Tillat"
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr "Analyser"
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr "Prosjektsynlighet"
msgid "ProjectSettings|Public"
msgstr "Offentlig"
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr "NodeJS Express"
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr "Beskyttet gren"
msgid "Protected Branches"
msgstr "Beskyttede grener"
-msgid "Protected Environment"
-msgstr "Beskyttet miljø"
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr "Regenerer eksport"
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr "Gjenåpne denne %{quick_action_target}"
msgid "Reopened this %{quick_action_target}."
msgstr "Gjenåpnet denne %{quick_action_target}."
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr "Gjenåpner denne %{quick_action_target}."
@@ -34145,11 +34592,8 @@ msgstr "Svar …"
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr "Rapporter misbruk"
-
-msgid "Report abuse to admin"
-msgstr "Rapporter misbruk til admin"
+msgid "Report abuse to administrator"
+msgstr ""
msgid "Report couldn't be prepared."
msgstr ""
@@ -34487,6 +34931,9 @@ msgstr "Forespørsel"
msgid "Request Access"
msgstr "Be om tilgang"
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr "Be om en ny en"
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr "Fortsett"
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr "Gjennomgangstid"
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr "IP-adresse"
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr "Etiketter"
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr "spesifikk"
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr "Kjører"
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr "SSH-nøkkel"
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr "SSH-nøkler"
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr "Offentlig SSH-nøkkel"
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr "SSL-verifisering:"
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr "Søk"
msgid "Search GitLab"
msgstr "Søk på GitLab"
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr "Ny retningslinje"
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr "%{firstProject} og %{secondProject}"
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr "%{firstProject}, %{secondProject}, og %{rest}"
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr "Legg til eller fjern prosjekter som skal overvåkes i sikkerhetsområdet. Prosjekter som er inkludert i denne listen vil få sine resultater vist i sikkerhetskontrollpanelet og sårbarhetsrapporten."
@@ -36632,9 +37181,24 @@ msgstr "Legg til prosjekter"
msgid "SecurityReports|All activity"
msgstr "All aktivitet"
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr "Ingen sårbarheter funnet"
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr "Oi sann, noe ser ikke riktig ut."
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr "Velg et stempel"
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr "Velg anmelder(e)"
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr "Velg kilde"
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr "Delte prosjekter"
@@ -37723,6 +38299,9 @@ msgstr "Vis nyeste versjon"
msgid "Show list"
msgstr "Vis liste"
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr "Vis én fil om gangen"
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr "Forhåndsvisning av pålogging"
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr "Logg på GitLab"
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr "Noe gikk galt under arkivering av et krav."
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr "Noe gikk galt under innhenting av pakkelisten."
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr "Start søk"
@@ -38919,6 +39516,9 @@ msgstr "Statistikk"
msgid "Status"
msgstr "Status"
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr "Bytt gren"
msgid "Switch branch/tag"
msgstr "Bytt gren/etikett"
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr "Team"
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr "De følgende gjenstander vil IKKE bli eksportert:"
@@ -40975,9 +41584,6 @@ msgstr "Det er ingen spam-logger"
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr "Det er ingen arkiverte prosjekter ennå"
-
msgid "There are no archived requirements"
msgstr "Det er ingen arkiverte krav"
@@ -41035,9 +41641,6 @@ msgstr "Det er ingen åpne testtilfeller"
msgid "There are no packages yet"
msgstr "Det er ingen pakker ennå"
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr "En feil oppstod under lagring av endringene dine."
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr "Dette Cron-mønsteret er ugyldig"
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr "Denne gruppen"
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr "Gjenstående tid"
msgid "Time spent"
msgstr "Tid brukt"
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr "Tittel (påkrevd)"
msgid "Title:"
msgstr "Tittel:"
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr "Titler og beskrivelser"
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr "For å aktivere kontoen din på nytt, logg på GitLab på %{gitlab_url}."
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr "Triggeren ble vellykket oppdatert."
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr "Misfornøyd?"
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr "ms"
@@ -43384,6 +44029,9 @@ msgstr "Uoppklart"
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr "Fjern stjerne"
@@ -43567,6 +44215,9 @@ msgstr "Laster opp endringer til terminalen"
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr "Oppstrøms"
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr "CI-minuttbruk per prosjekt"
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr "Bruk i nåværende periode"
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr "Total mengde overflødig lagring brukt"
msgid "UsageQuota|Total namespace storage used"
msgstr "Total mengde navneområde-lagring brukt"
-msgid "UsageQuota|Unlimited"
-msgstr "Ubegrenset"
-
msgid "UsageQuota|Uploads"
msgstr "Opplastinger"
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr "Wiki"
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr "Du har brukt: %{usage} %{limit}"
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr "Personlige prosjekter"
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr "Uttalt som: %{pronunciation}"
-msgid "UserProfile|Report abuse"
-msgstr "Rapporter misbruk"
-
msgid "UserProfile|Retry"
msgstr "Prøv igjen"
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,12 +45255,12 @@ msgstr "Stopp"
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr "Standardverdistrømmen kan ikke bli slettet"
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
-msgstr ""
-
msgid "Variable"
msgstr "Variabel"
+msgid "Variable value will be evaluated as raw string."
+msgstr ""
+
msgid "Variable will be masked in job logs."
msgstr "Variabelen vil bli maskert i jobbloggføringene."
@@ -44634,7 +45270,7 @@ msgstr "Variabler"
msgid "Variables can be:"
msgstr "Variabler kan være:"
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr "Versjon %{versionNumber}"
msgid "Version %{versionNumber} (latest)"
msgstr "Versjon %{versionNumber} (nyeste)"
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr "Avgrein prosjekt"
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr "Webhook-innstillinger"
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr "Kommentarer"
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,15 +46278,12 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr "Fletteforespørselshendelser"
+msgid "Webhooks|Must match part of URL"
+msgstr ""
+
msgid "Webhooks|Pipeline events"
msgstr "Rørledningshendelser"
-msgid "Webhooks|Push events"
-msgstr "Pushhendelser"
-
-msgid "Webhooks|Push to the repository."
-msgstr ""
-
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
msgstr ""
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr "Trigger"
-msgid "Webhooks|URL"
-msgstr "URL"
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr "Vil bli opprettet"
msgid "Will be mapped to"
msgstr "Vil bli kartlagt til"
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr "Sak"
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr "Skriv en kommentar …"
msgid "Write a description or drag your files here…"
msgstr "Skriv en beskrivelse og/eller slipp filene dine hit …"
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr "Du kan alltid redigere dette senere"
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "Du kan invitere et nytt medlem til %{project_name} eller invitere en annen gruppe."
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr "Du har ikke noen abonnementer enda"
@@ -46741,6 +47423,9 @@ msgstr "Du har ingen nylige søk"
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr "Du har allerede aktivert 2-trinnsautentisering ved hjelp av éngangspass
msgid "You've rejected %{user}"
msgstr "Du har avvist %{user}"
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr "Ditt %{strong}%{plan_name}%{strong_close} abonnement på %{strong}%{namespace_name}%{strong_close} vil utløpe den %{strong}%{expires_on}%{strong_close}."
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr "Din GitLab-gruppe"
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr "Dine grupper"
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr "Dine grupper"
@@ -47275,6 +47961,15 @@ msgstr "Navnet ditt"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr "kan ikke endres"
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] "endre"
msgstr[1] "forandringer"
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr "Rettet opp i:"
msgid "ciReport|Found %{issuesWithCount}"
msgstr "Fant %{issuesWithCount}"
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr "kommenterte på %{link_to_project}"
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr "commit %{commit_id}"
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr "overgår grensen på %{bytes} bytes"
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr "er et ugyldig IP-adresseområde"
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr "må være høyere enn startdatoen"
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr "nylig aktivitet"
@@ -49205,6 +49936,11 @@ msgstr "kodelager:"
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/nl_NL/gitlab.po b/locale/nl_NL/gitlab.po
index 2be1fb9ff49..17347f1c48f 100644
--- a/locale/nl_NL/gitlab.po
+++ b/locale/nl_NL/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: nl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:23\n"
+"PO-Revision-Date: 2022-12-10 06:40\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr "%{text} is beschikbaar"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "Branches"
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/or_IN/gitlab.po b/locale/or_IN/gitlab.po
index 4f68008552f..c41a30843f6 100644
--- a/locale/or_IN/gitlab.po
+++ b/locale/or_IN/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: or\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:24\n"
+"PO-Revision-Date: 2022-12-10 06:43\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/pa_IN/gitlab.po b/locale/pa_IN/gitlab.po
index 594954e89de..4448314d43e 100644
--- a/locale/pa_IN/gitlab.po
+++ b/locale/pa_IN/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: pa-IN\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:23\n"
+"PO-Revision-Date: 2022-12-10 06:41\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/pa_PK/gitlab.po b/locale/pa_PK/gitlab.po
index 36b8bd4ca03..86131d357ec 100644
--- a/locale/pa_PK/gitlab.po
+++ b/locale/pa_PK/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: pa-PK\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:24\n"
+"PO-Revision-Date: 2022-12-10 06:44\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/pl_PL/gitlab.po b/locale/pl_PL/gitlab.po
index 413a80f75d8..b509364c1bb 100644
--- a/locale/pl_PL/gitlab.po
+++ b/locale/pl_PL/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: pl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:23\n"
+"PO-Revision-Date: 2022-12-10 06:41\n"
msgid " %{start} to %{end}"
msgstr " %{start} do %{end}"
@@ -451,6 +451,13 @@ msgstr[1] "%d dodatkowe komentarze"
msgstr[2] "%d dodatkowych komentarzy"
msgstr[3] "%d dodatkowego komentarza"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%d oczekujÄ…cy komentarz"
@@ -666,6 +673,12 @@ msgstr[3] "%{bold_start}%{count}%{bold_end} otwartego merge requesta"
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr "%{code_open} Maskowanie:%{code_close} Ukryte w logu zadań. Musi spełnić wymagania maskowania."
@@ -886,15 +899,15 @@ msgstr "Grupa %{group_name} używa konta zarządzane grupowo. Musisz utworzyć n
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type} zostało usunięte"
-
-msgid "%{hook_type} was scheduled for deletion"
-msgstr "%{hook_type} zostało zaplanowane do usunięcia"
-
msgid "%{host} sign-in from new location"
msgstr "%{host} zalogowanie z nowej lokalizacji"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
+msgstr ""
+
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr ""
+
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr "%{integrations_link_start}Integracje:%{link_end} pozwalają Ci użyć aplikacji firm trzecich jako część twojego workflow w GitLab. Jeśli dostępne integracje nie spełniają Twoich potrzeb, rozważ wykorzystanie %{webhooks_link_start}webhooka%{link_end}."
@@ -1088,9 +1101,6 @@ msgstr "%{openedEpics} otwarte, %{closedEpics} zamknięte"
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr "%{openedIssues} otwarte, %{closedIssues} zamknięte"
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -1143,7 +1153,7 @@ msgstr "%{status} %{reportType}"
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr "%{reportType} wykryto %{totalStart}%{total}%{totalEnd} potencjalnych %{vulnMessage}"
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1318,6 +1328,13 @@ msgstr "%{template_project_id} jest nieznany lub nieprawidłowy"
msgid "%{text} is available"
msgstr "%{text} jest dostępny"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr "%{timebox_type} nie obsługuje wykresów wypalania (burnup)"
@@ -1357,6 +1374,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr "%{userName} (nie może zmergować)"
@@ -1478,12 +1498,18 @@ msgstr "(+%{count}&nbsp;zasad)"
msgid "(Group Managed Account)"
msgstr "(Konto zarzÄ…dzane przez grupÄ™)"
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr "(Bez zmian)"
msgid "(UTC %{offset}) %{timezone}"
msgstr "(UTC %{offset}) %{timezone}"
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr "(sprawdź postęp)"
@@ -2630,6 +2656,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr "Dodaj do tablicy"
@@ -2717,6 +2746,9 @@ msgstr "Dodano w tej wersji"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "W Twojej instancji GitLaba wyłączono dodawanie nowych aplikacji. Aby uzyskać pozwolenie, skontaktuj się z administratorem swojego GitLaba"
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "Dodatkowe minuty"
@@ -2912,6 +2944,9 @@ msgstr "Zatrzymanie zadań nie powiodło się"
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -3212,7 +3247,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3275,9 +3310,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4806,6 +4838,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4923,9 +4958,6 @@ msgstr ""
msgid "Any namespace"
msgstr "Dowolna przestrzeń nazw"
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr "ID aplikacji"
@@ -5480,6 +5512,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5894,6 +5929,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5939,6 +5977,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -6170,9 +6211,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -7200,6 +7238,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7314,9 +7361,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7326,6 +7370,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7347,7 +7394,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7368,9 +7418,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7392,6 +7439,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7425,6 +7475,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7437,6 +7493,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7491,6 +7550,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -8299,6 +8361,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8347,6 +8412,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8472,9 +8540,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8490,7 +8555,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8514,6 +8579,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8757,9 +8825,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8772,6 +8846,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8784,6 +8861,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8793,6 +8873,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8802,6 +8885,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9497,6 +9586,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10320,6 +10412,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10344,6 +10439,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10359,15 +10457,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10533,7 +10640,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10653,6 +10760,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10692,12 +10802,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10968,9 +11072,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -11154,9 +11255,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11469,6 +11567,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11601,9 +11702,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11682,6 +11780,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11718,6 +11819,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11943,6 +12080,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12355,12 +12495,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12373,9 +12519,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12394,6 +12552,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12427,6 +12588,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12487,6 +12651,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12634,6 +12801,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -13007,6 +13177,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -13064,6 +13237,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13868,6 +14047,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13904,6 +14086,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -14010,9 +14195,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -14179,6 +14370,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15500,6 +15694,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15863,9 +16060,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15959,6 +16153,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -16274,6 +16471,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16437,6 +16643,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16467,9 +16676,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16488,9 +16709,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16545,13 +16763,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16871,9 +17092,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -17030,9 +17248,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -17289,6 +17504,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17508,15 +17726,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17553,6 +17762,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17697,10 +17909,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17782,13 +18003,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17831,6 +18045,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18682,6 +18899,13 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18931,6 +19155,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18958,6 +19185,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -19135,6 +19365,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -19228,6 +19461,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19417,7 +19656,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19504,6 +19743,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19609,6 +19851,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19780,7 +20025,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19801,9 +20046,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19933,6 +20175,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19942,9 +20187,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20426,12 +20668,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20465,9 +20713,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20636,6 +20881,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20660,6 +20908,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20699,7 +20950,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20741,6 +20992,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20753,9 +21010,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -21107,6 +21373,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21674,12 +21943,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21998,6 +22273,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -22272,6 +22550,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -22305,6 +22586,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22653,6 +22937,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22701,7 +22988,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -23170,9 +23457,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -23311,9 +23595,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23746,9 +24027,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23899,6 +24177,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23980,6 +24261,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -24049,9 +24333,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -24128,15 +24409,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -24225,6 +24509,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -24330,6 +24617,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -25017,10 +25307,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -25071,6 +25361,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -25176,6 +25469,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25674,6 +25970,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25695,6 +25994,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26830,6 +27135,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26893,9 +27201,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -27107,6 +27412,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -27288,6 +27596,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -27330,6 +27641,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27603,9 +27920,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27705,6 +28019,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -28373,9 +28690,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28758,6 +29090,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28837,9 +29172,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28945,9 +29277,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -29233,6 +29562,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -29324,6 +29656,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -29339,6 +29674,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -29363,6 +29704,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29435,6 +29788,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29450,6 +29806,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29460,6 +29819,9 @@ msgstr[3] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29514,6 +29876,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29562,9 +29927,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29587,6 +29949,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29602,6 +29971,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29671,6 +30043,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29734,6 +30109,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29977,6 +30361,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30892,6 +31300,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -31048,9 +31459,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -31204,6 +31612,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -31288,6 +31699,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -31300,9 +31714,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -31420,16 +31840,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31792,6 +32221,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -32326,12 +32761,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -32413,9 +32854,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32530,6 +32968,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32575,6 +33016,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32704,7 +33148,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32785,6 +33229,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -33307,9 +33754,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33983,6 +34427,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -34182,6 +34629,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34521,6 +34971,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34575,10 +35028,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34929,6 +35379,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -35180,6 +35633,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -35256,7 +35712,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -35353,6 +35809,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -35419,7 +35878,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -35446,6 +35905,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35467,6 +35929,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35552,6 +36017,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35570,6 +36038,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35609,12 +36080,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35709,6 +36183,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35775,6 +36252,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35823,12 +36303,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35944,6 +36418,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35971,13 +36448,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -36019,6 +36499,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -36034,6 +36517,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -36100,6 +36592,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -36127,9 +36625,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -36142,18 +36646,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -36238,6 +36751,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36777,6 +37293,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36834,9 +37353,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36852,6 +37380,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36888,6 +37419,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36960,6 +37494,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36969,12 +37506,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -37044,6 +37590,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -37071,6 +37620,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -37107,6 +37659,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -37116,9 +37671,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -37263,6 +37833,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37488,6 +38061,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37593,6 +38169,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -38083,6 +38662,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -38209,6 +38791,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -38369,9 +38954,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38525,7 +39107,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38540,13 +39122,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38570,13 +39155,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38714,10 +39302,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38759,6 +39347,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38777,6 +39368,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38846,6 +39440,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -39239,6 +39836,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -39344,6 +39944,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -39407,6 +40010,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -40146,6 +40752,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -40236,9 +40845,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40521,6 +41127,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -41111,6 +41723,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -41479,9 +42094,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr "Nie ma jeszcze zarchiwizowanych projektów"
-
msgid "There are no archived requirements"
msgstr ""
@@ -41539,9 +42151,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr "Nie ma jeszcze projektów udostępnionych w tej grupie"
-
msgid "There are no secure files yet."
msgstr ""
@@ -41587,6 +42196,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41707,6 +42319,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41746,6 +42361,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41839,9 +42457,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -42031,6 +42646,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -42049,7 +42667,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42523,6 +43141,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42777,6 +43401,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42810,9 +43437,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42927,6 +43551,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -43020,6 +43647,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -43053,6 +43683,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -43068,13 +43701,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -43089,22 +43722,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -43477,6 +44110,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43513,6 +44152,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43690,6 +44332,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43813,6 +44461,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43894,6 +44545,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -44077,6 +44731,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -44095,18 +44752,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -44134,9 +44785,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -44146,9 +44794,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -44275,10 +44920,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -44287,12 +44929,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -44305,9 +44941,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -44341,7 +44974,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -44350,16 +44983,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44533,6 +45166,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44801,9 +45437,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44993,6 +45626,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -45065,6 +45701,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -45083,6 +45722,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -45131,10 +45773,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -45146,7 +45788,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -45203,6 +45845,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -45212,6 +45869,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45878,6 +46547,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45953,9 +46625,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45971,24 +46640,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -46019,6 +46676,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -46085,9 +46754,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -46136,13 +46802,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -46181,9 +46844,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46579,6 +47239,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46588,9 +47251,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46609,6 +47269,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46624,9 +47290,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46649,6 +47312,9 @@ msgstr[3] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46658,6 +47324,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46670,16 +47342,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46688,12 +47363,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46703,12 +47384,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46721,9 +47408,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46787,7 +47483,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46817,6 +47513,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46986,10 +47685,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -47028,13 +47727,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -47101,9 +47793,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -47122,18 +47811,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -47215,6 +47898,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -47269,6 +47955,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -47333,6 +48022,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -47384,9 +48076,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47564,6 +48253,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47582,7 +48274,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47618,6 +48310,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47763,13 +48458,6 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "Your groups"
msgstr ""
@@ -47809,6 +48497,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47969,6 +48666,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -48152,6 +48852,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -48207,6 +48910,20 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -48264,10 +48981,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -48432,7 +49152,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48614,6 +49334,13 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48775,9 +49502,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48968,6 +49692,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -49007,6 +49734,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -49510,6 +50243,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -49519,10 +50255,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49732,6 +50468,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49791,6 +50530,13 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "running"
msgstr ""
diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po
index 503484c3701..6c72111e1c8 100644
--- a/locale/pt_BR/gitlab.po
+++ b/locale/pt_BR/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: pt-BR\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:21\n"
+"PO-Revision-Date: 2022-12-11 07:03\n"
msgid " %{start} to %{end}"
msgstr " %{start} até %{end}"
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] "mais %d comentário"
msgstr[1] "mais %d comentários"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] "%d pacote"
+msgstr[1] "%d pacotes"
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%d comentário pendente"
@@ -506,6 +511,12 @@ msgstr[1] "%{bold_start}%{count}%{bold_end} solicitações de mesclagem abertas"
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr "%{code_open}Mascarado:%{code_close} Escondidos nos logs de tarefas. Deve corresponder a requisitos de mascaramento."
@@ -706,15 +717,15 @@ msgstr "%{group_name} usa contas gerenciadas por grupo. Você precisa criar uma
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr "%{group_name}&%{epic_iid} &middot; criado %{epic_created} por %{author}"
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type} foi excluído"
-
-msgid "%{hook_type} was scheduled for deletion"
-msgstr "%{hook_type} foi agendado para exclusão"
-
msgid "%{host} sign-in from new location"
msgstr "%{host} entrou a partir de um novo local"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
+msgstr ""
+
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr ""
+
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr "%{integrations_link_start}Integrações%{link_end} permitem que aplicativos de terceiros façam parte do seu fluxo de trabalho do GitLab. Se as integrações disponíveis não atenderem às suas necessidades, considere usar um %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr "%{openedEpics} abertos, %{closedEpics} fechados"
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr "%{openedIssues} aberta, %{closedIssues} fechada"
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,8 +967,8 @@ msgstr "%{reportType} %{status}"
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
-msgstr "%{reportType} não detectou %{totalStart}novas%{totalEnd} vulnerabilidades."
+msgid "%{reportType} detected no new vulnerabilities."
+msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
msgstr "%{retryButtonStart}Tente novamente%{retryButtonEnd} ou %{newFileButtonStart}anexe um novo arquivo%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr "%{template_project_id} é desconhecido ou inválido"
msgid "%{text} is available"
msgstr "%{text} está disponível"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr "%{timebox_type} não suporta o gráfico de burnup"
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr "%{url} (opcional)"
+
msgid "%{userName} (cannot merge)"
msgstr "%{userName} (não pode mesclar)"
@@ -1266,12 +1282,18 @@ msgstr "(+%{count}&nbsp;regras)"
msgid "(Group Managed Account)"
msgstr "(Conta gerenciada por grupo)"
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr "(Nenhuma alteração)"
msgid "(UTC %{offset}) %{timezone}"
msgstr "(UTC %{offset}) %{timezone}"
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr "(verificar progresso)"
@@ -2364,6 +2386,9 @@ msgstr "Adicionar system hook"
msgid "Add text to the sign-in page. Markdown enabled."
msgstr "Adicione texto à página de entrada. Markdown ativado."
+msgid "Add time entry"
+msgstr "Adicionar entrada de tempo"
+
msgid "Add to board"
msgstr "Adicionar ao painel"
@@ -2392,7 +2417,7 @@ msgid "Add variable"
msgstr "Adicionar variável"
msgid "Add vulnerability finding"
-msgstr ""
+msgstr "Adicionar descoberta de vulnerabilidades"
msgid "Add webhook"
msgstr "Adicionar webhook"
@@ -2451,6 +2476,9 @@ msgstr "Adicionado nesta versão"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "A adição de novos aplicativos está desativada na sua instância do GitLab. Entre em contato com seu administrador do GitLab para obter a permissão"
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "Minutos adicionais"
@@ -2646,6 +2674,9 @@ msgstr "Erro ao parar tarefas"
msgid "AdminArea|Total users"
msgstr "Usuários totais"
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr "Usuários"
@@ -2946,8 +2977,8 @@ msgstr "Pesquisa com o Elasticsearch ativado"
msgid "AdminSettings|Select a CI/CD template"
msgstr "Selecione um modelo CI/CD"
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
-msgstr "Selecione um grupo para usar como fonte para modelos de projeto em nível de instância."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
+msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
msgstr "Selecione para desativar o acesso público aos sites Pages, o que exige que os usuários façam login para acessar os sites Pages em sua instância. %{link_start}Saiba mais.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr "Os artefatos mais recentes de todas as tarefas dos pipelines mais recentes em cada projeto são armazenados e não expiram."
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr "Os projetos neste grupo podem ser selecionados como modelos para novos projetos criados na instância. %{link_start}Saiba mais.%{link_end} "
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr "O modelo para a configuração de pipeline necessária pode ser um dos modelos fornecidos pelo GitLab ou um modelo personalizado adicionado a um repositório de modelos de instância. %{link_start}Como faço para criar um repositório de modelo de instância?%{link_end}"
@@ -4538,6 +4566,9 @@ msgstr "Ocorreu um erro. Por favor, entre novamente."
msgid "An error occurred. Please try again."
msgstr "Ocorreu um erro. Tente novamente."
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr "Um projeto de exemplo para gerenciar clusters Kubernetes integrados ao GitLab"
@@ -4655,9 +4686,6 @@ msgstr "Qualquer marco"
msgid "Any namespace"
msgstr "Qualquer espaço de nome"
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr "App ID"
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr "Tem certeza de que deseja bloquear %{path}?"
@@ -5604,6 +5635,9 @@ msgstr "Excluir %{link}"
msgid "AuditStreams|Destination URL"
msgstr "URL de destino"
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr "Destinatário recebeu todos os dados do eventos de auditoria"
@@ -5626,13 +5660,13 @@ msgid "AuditStreams|Setup streaming for audit events"
msgstr "Configurar streaming para eventos de auditoria"
msgid "AuditStreams|Stream added successfully"
-msgstr ""
+msgstr "Stream adicionado com sucesso"
msgid "AuditStreams|Stream deleted successfully"
-msgstr ""
+msgstr "Stream excluído com sucesso"
msgid "AuditStreams|Stream updated successfully"
-msgstr ""
+msgstr "Stream atualizado com sucesso"
msgid "AuditStreams|Streams"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr "Valor"
msgid "AuditStreams|Verification token"
msgstr "Token de verificação"
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "Ago"
@@ -5880,9 +5917,6 @@ msgstr "Executores de grupo disponíveis: %{runners}"
msgid "Available on-demand"
msgstr "Disponível por demanda"
-msgid "Available shared runners:"
-msgstr "Executores compartilhados disponíveis:"
-
msgid "Available specific runners"
msgstr "Executores específicos disponíveis"
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr "Todas as ramificações"
@@ -7002,10 +7045,10 @@ msgid "BranchRules|Roles"
msgstr "Cargos"
msgid "BranchRules|Status checks"
-msgstr ""
+msgstr "Verificações de status"
msgid "BranchRules|Status checks (%{total})"
-msgstr ""
+msgstr "Verificações de status (%{total})"
msgid "BranchRules|Target Branch"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr "Usuários"
msgid "BranchRules|default"
msgstr "padrão"
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "Ramificações"
@@ -7028,6 +7068,9 @@ msgstr "Ramificações: %{source_branch} para %{target_branch}"
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr "Ramificações: %{source_branch} → %{target_branch}"
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr "Ativos"
@@ -7049,8 +7092,11 @@ msgstr "Não foi possível encontrar o commit HEAD para essa ramificação"
msgid "Branches|Compare"
msgstr "Comparar"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "Apagar todos as ramificações que foram mesclados em '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
+msgstr ""
msgid "Branches|Delete branch"
msgstr "Apagar ramificação"
@@ -7070,9 +7116,6 @@ msgstr "Excluir ramificação protegida. Você tem CERTEZA ABSOLUTA disso?"
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr "Excluir a ramificação %{strongStart}%{branchName}%{strongEnd} não pode ser desfeito. Você tem certeza?"
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "Apagar ramificações mescladas não pode ser desfeita. Você tem certeza?"
-
msgid "Branches|Filter by branch name"
msgstr "Filtrar por nome de ramificação"
@@ -7094,6 +7137,9 @@ msgstr "Visão Geral"
msgid "Branches|Please type the following to confirm:"
msgstr "Por favor, digite o seguinte para confirmar"
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr "Mostrar branches ativas"
@@ -7127,6 +7173,12 @@ msgstr "A ramificação padrão não pode ser apagada"
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr "Para descartar as alterações locais e sobrescrever o branch com a versão upstream, exclua-o aqui e escolhe 'Atualize agora' acima."
@@ -7139,6 +7191,9 @@ msgstr "Sim, apagar o branch"
msgid "Branches|Yes, delete protected branch"
msgstr "Sim, apagar o branch protegido"
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr "Carregando artefatos"
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr "Embutido"
@@ -7999,6 +8057,9 @@ msgstr "Pipeline #%{pipeline_id} %{humanized_status} em %{duration}"
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr "Tag"
@@ -8047,6 +8108,9 @@ msgstr "Verifique suas imagens do Docker quanto a vulnerabilidades conhecidas."
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr "Verifique as imagens de cluster do Kubernetes em busca de vulnerabilidades conhecidas."
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "Verificando disponibilidade de %{text}…"
@@ -8168,9 +8232,6 @@ msgstr "Falha ao carregar o formulário de cartão de crédito: %{message}"
msgid "Checkout|Edit"
msgstr "Editar"
-msgid "Checkout|Enter a number greater than 0"
-msgstr "Insira um número maior que 0"
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr "Expira em: %{expirationMonth}/%{expirationYear}"
@@ -8186,8 +8247,8 @@ msgstr "Falha ao carregar países. Por favor, tente novamente."
msgid "Checkout|Failed to load states. Please try again."
msgstr "Falha ao carregar estados. Por favor, tente novamente."
-msgid "Checkout|Failed to load the payment form. Please try again."
-msgstr "Falha ao carregar o formulário de pagamento. Por favor, tente novamente."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
+msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
msgstr "Falha ao registrar o cartão de crédito. Por favor, tente novamente."
@@ -8210,6 +8271,9 @@ msgstr "Deve ser %{minimumNumberOfUsers} (seus assentos em uso) ou mais."
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr "Deve ser 1 ou mais. Não pode ser um decimal."
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr "Nome da empresa ou organização usando GitLab"
@@ -8453,9 +8517,15 @@ msgstr "executando"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr "Não é possível usar a variável mascarada com o valor atual"
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr "Ambientes"
+msgid "CiVariables|Expanded"
+msgstr "Expandido"
+
msgid "CiVariables|Input variable key"
msgstr "Digite o nome da variável"
@@ -8468,6 +8538,9 @@ msgstr "Chave"
msgid "CiVariables|Masked"
msgstr "Mascarada"
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr "Opções"
@@ -8480,6 +8553,9 @@ msgstr "Remover variável"
msgid "CiVariables|Remove variable row"
msgstr "Remover a variável"
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr "Escopo"
@@ -8489,6 +8565,9 @@ msgstr "Especifique os valores das variáveis a serem usadas nessa execução. O
msgid "CiVariables|State"
msgstr "Estado"
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr "Tipo"
@@ -8498,6 +8577,12 @@ msgstr "Valor"
msgid "CiVariables|Variables"
msgstr "Variáveis"
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr "* (Todos os ambientes)"
@@ -9189,6 +9274,9 @@ msgstr "Token revogado por %{userName}"
msgid "ClusterAgents|Unknown user"
msgstr "Usuário desconhecido"
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr "Token de acesso válido"
@@ -10010,6 +10098,9 @@ msgstr "Configuração do pipeline compliance (opcional)"
msgid "ComplianceFrameworks|Configuration not found"
msgstr "Configuração não encontrada"
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr "Excluir framework de conformidade %{framework}"
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr "Frameworks que foram adicionados aparecerão aqui."
@@ -10049,15 +10143,24 @@ msgstr "O nome é necessário"
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr "Nenhuma estrutura de conformidade está configurada ainda"
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr "Adicione um framework a %{linkStart}%{groupName}%{linkEnd} e ele aparecerá aqui."
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr "Configure a integração de %{link}."
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr "Conectando"
msgid "Connecting to terminal sync service"
msgstr "Conectando ao serviço de sincronização de terminal"
+msgid "Connecting to the remote environment..."
+msgstr "Conectando ao ambiente remoto..."
+
msgid "Connecting..."
msgstr "Conectando..."
@@ -10382,12 +10488,6 @@ msgstr "Imagens do registro de contêiner"
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr "O registro de contêiner não está ativado nesta instância do GitLab. Peça a um administrador para ativá-lo para que o Auto DevOps funcione."
-msgid "Container repositories"
-msgstr "Repositórios do container"
-
-msgid "Container repository"
-msgstr "Repositório do container"
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr "Visite as %{linkStart}configurações de administração%{linkEnd} para ativar este recurso."
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr "Tags que correspondem a estas regras são %{strongStart}mantidas%{strongEnd}, mesmo que elas correspondam a regra de exclusão abaixo. A %{secondStrongStart}última%{secondStrongEnd} é sempre mantida."
@@ -10838,9 +10935,6 @@ msgstr "Controle e-mails vinculados à sua conta"
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr "Controle como a variável CI_JOB_TOKEN CI/CD é usada para acesso à API entre projetos."
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr "Não foi possível atribuir política ao projeto ou grupo"
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr "País"
@@ -11285,9 +11382,6 @@ msgstr "Criar issue"
msgid "Create issue to resolve all threads"
msgstr "Crie uma issue para resolver todos os tópicos"
-msgid "Create iteration"
-msgstr "Criar interação"
-
msgid "Create label"
msgstr "Criar etiqueta"
@@ -11366,6 +11460,9 @@ msgstr "Criar snippet"
msgid "Create tag %{tagName}"
msgstr "Criar tag %{tagName}"
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr "Criar tópico"
@@ -11402,6 +11499,42 @@ msgstr "Você não tem permissão para criar grupos."
msgid "CreateTag|Tag"
msgstr "Tag"
+msgid "CreateTimelogForm|Add time entry"
+msgstr "Adicionar entrada de hora"
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr "Cancelar"
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr "Exemplo: 1h 30m"
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr "Salvar"
+
+msgid "CreateTimelogForm|Spent at"
+msgstr "Gasto em"
+
+msgid "CreateTimelogForm|Summary"
+msgstr "Resumo"
+
+msgid "CreateTimelogForm|Time spent"
+msgstr "Tempo gasto"
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr "issue"
+
+msgid "CreateTimelogForm|merge request"
+msgstr "solicitação de mesclagem"
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr "%{name} (padrão)"
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr "Cartão de crédito:"
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr "Vulnerabilidades críticas presentes"
@@ -12037,12 +12173,18 @@ msgstr "%{startDate} - %{endDate}"
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr "Taxa de falha de mudança"
+
msgid "DORA4Metrics|Change failure rate"
msgstr "Taxa de falha de mudança"
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr "Taxa de falha de mudança (porcentagem)"
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr "Dias para um incidente aberto"
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr "Frequência de implantação"
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr "Tempo de espera para mudanças"
@@ -12076,6 +12230,9 @@ msgstr "Tempo médio em que um incidente foi aberto em um ambiente de produção
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr "Nenhum incidente durante este período"
@@ -12109,6 +12266,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 "O gráfico exibe a mediana de tempo entre uma solicitação de mesclagem sendo realizada e implantada no(s) ambient(s) de produção baseados no valor %{linkStart}deployment_tier%{linkEnd} ."
+msgid "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr "Tempo para restaurar o serviço"
@@ -12169,6 +12329,9 @@ msgstr "Nenhuma verificação anterior encontrada para este projeto"
msgid "DastConfig|Not enabled"
msgstr "Não ativado"
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr "Uma verificação passiva monitora todas as mensagens HTTP (solicitações e respostas) enviadas para o destino. Uma varredura ativa ataca o alvo para encontrar potenciais vulnerabilidades."
@@ -12316,6 +12479,9 @@ msgstr "Mínimo = 1 segundo, Máximo = 3600 segundos"
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr "Monitora todas as solicitações HTTP enviadas ao destino para encontrar possíveis vulnerabilidades."
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr "Novo perfil de verificação"
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr "Dias para mesclar"
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr "Desativar usuários inativos após um período de inatividade"
@@ -12744,6 +12913,12 @@ msgstr "Primeiro dia da semana padrão"
msgid "Default first day of the week in calendars and date pickers."
msgstr "Primeiro dia da semana padrão em calendários e selecionadores de data."
+msgid "Default language"
+msgstr "Idioma padrão"
+
+msgid "Default language for users who are not logged in."
+msgstr "Idioma padrão para usuários que não estão entrados."
+
msgid "Default projects limit"
msgstr "Limite padrão de projetos"
@@ -13208,8 +13383,8 @@ msgstr "Quando ativado, as imagens com mais de 90 dias serão removidas do cache
msgid "Depends on %d merge request being merged"
msgid_plural "Depends on %d merge requests being merged"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "Depende %d solicitação de mesclagem que está sendo mesclada"
+msgstr[1] "Depende %d solicitações de mesclagem sendo mesclada"
msgid "Depends on %{strongStart}%{closedCount} closed%{strongEnd} merge request."
msgid_plural "Depends on %{strongStart}%{closedCount} closed%{strongEnd} merge requests."
@@ -13536,6 +13711,9 @@ msgstr "Rejeitado %{time}"
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr "Rejeitado por você %{time}"
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr "GitLab Pages"
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr "Máquina virtual (EC2, por exemplo)"
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr "Implementações"
@@ -13676,9 +13857,15 @@ msgstr "Descrição analisada com %{link_start}GitLab Flavored Markdown%{link_en
msgid "Description:"
msgstr "Descrição:"
+msgid "Descriptions"
+msgstr "Descrições"
+
msgid "Descriptive label"
msgstr "Etiqueta descritiva"
+msgid "Design"
+msgstr "Design"
+
msgid "Design Management files and data"
msgstr "Projeto de gerenciamento de arquivos e dados"
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr "Adoção de DevOps"
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr "Desenvolvedor"
@@ -15156,6 +15346,9 @@ msgstr "Digite qualquer cor."
msgid "Enter at least three characters to search"
msgstr "Digite pelo menos três caracteres para pesquisar"
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr "Digite o seu URL do servidor do Bitbucket e o token de acesso pessoal abaixo"
@@ -15519,9 +15712,6 @@ msgstr "Remover épico"
msgid "Epics|Remove issue"
msgstr "Remover issue"
-msgid "Epics|Show more"
-msgstr "Mostrar mais"
-
msgid "Epics|Something went wrong while creating child epics."
msgstr "Algo deu errado ao criar épicos filhos."
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr "Erro ao recuperar refs"
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr "Erro ao recuperar lista de dependências. Por favor, verifique sua conexão de rede e tente novamente."
@@ -15930,6 +16123,15 @@ msgstr "Esta política não tem regras de escalação."
msgid "EscalationPolicies|mins"
msgstr "mins"
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr "Estimativa"
@@ -16091,6 +16293,9 @@ msgstr "Todos podem contribuir"
msgid "Everything on your to-do list is marked as done."
msgstr "Tudo na sua lista de tarefas pendentes está marcado como concluído."
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr "Tudo o que você precisa para criar um site do GitLab Pages usando o Gatsby"
@@ -16121,9 +16326,21 @@ msgstr "Coleção de provas"
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr "Exemplo: @sub\\.company\\.com$"
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr "Exemplos"
@@ -16142,9 +16359,6 @@ msgstr "Excluindo commits de mesclar. Limitado a 6.000 commits."
msgid "Execution time"
msgstr "Tempo de execução"
-msgid "Executive Dashboard"
-msgstr "Painel executivo"
-
msgid "Existing branch name, tag, or commit SHA"
msgstr "Nome de ramificação, tag ou SHA do commit existente"
@@ -16199,13 +16413,16 @@ msgstr "Expandir seção de configurações"
msgid "Expand sidebar"
msgstr "Expandir barra lateral"
+msgid "Expand variable reference"
+msgstr "Expandir referência de variável"
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr "Falha ao obter a ref."
-msgid "Failed to install."
-msgstr "Falha ao instalar."
-
msgid "Failed to load"
msgstr "Falha ao carregar"
@@ -16680,9 +16894,6 @@ msgstr "Erro em atualizar o status da issue"
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr "Falha ao atualizar."
-
msgid "Failed to upload object map file"
msgstr "Falha ao enviar o arquivo de mapeamento de objeto"
@@ -16937,6 +17148,9 @@ msgstr "Fev"
msgid "February"
msgstr "Fevereiro"
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr "Participar do FLoC"
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr "Insira seu token do Flowdock."
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr "Enviar notificações de eventos do GitLab para fluxos do Flowdock."
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr "Enviar notificações de eventos do GitLab para fluxos do Flowdock. %{docs_link}"
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr "Atividade de usuários seguidos"
msgid "Followed users"
msgstr "Usuários seguidos"
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr "Cor da Fonte"
@@ -17345,12 +17553,21 @@ msgstr "Fork em andamento"
msgid "Forks"
msgstr "Forks"
-msgid "Format: %{dateFormat}"
-msgstr "Formato: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|Up to date with upstream repository"
msgstr ""
+msgid "Format: %{dateFormat}"
+msgstr "Formato: %{dateFormat}"
+
msgid "Found errors in your %{gitlab_ci_yml}:"
msgstr "Erros encontrados em seu %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr "De %{code_open}%{source_title}%{code_close} em"
msgid "From %{providerTitle}"
msgstr "Do %{providerTitle}"
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] "A partir de 19 de outubro de 2022, os grupos privados gratuitos serão limitados a %d membros"
-msgstr[1] "A partir de 19 de outubro de 2022, os grupos privados gratuitos serão limitados a %d membros"
-
msgid "From issue creation until deploy to production"
msgstr "Da abertura de tarefas até a implantação para a produção"
@@ -17475,6 +17687,9 @@ msgstr "Pipelines gerais"
msgid "General settings"
msgstr "Configurações gerais"
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr "Gerar etiquetas padrão"
@@ -18155,16 +18370,16 @@ msgid "GitAbuse|Excluded users"
msgstr "Usuários excluídos"
msgid "GitAbuse|Number of repositories"
-msgstr ""
+msgstr "Número de repositórios"
msgid "GitAbuse|Number of repositories can't be blank. Set to 0 for no limit."
-msgstr ""
+msgstr "Número de repositórios não pode ficar em branco. Defina como 0 para nenhum limite."
msgid "GitAbuse|Number of repositories must be a number."
-msgstr ""
+msgstr "O número de repositórios deve ser um número."
msgid "GitAbuse|Number of repositories should be between %{minNumRepos}-%{maxNumRepos}."
-msgstr ""
+msgstr "O número de repositórios deve estar entre %{minNumRepos}-%{maxNumRepos}."
msgid "GitAbuse|Reporting time period (seconds)"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr "Versão do GitLab"
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr "GitLab.com (SaaS)"
@@ -18571,6 +18791,9 @@ msgstr "Edições recentes"
msgid "GlobalSearch|Recent merge requests"
msgstr "Solicitações de mesclagem recentes"
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr "Digite para que novas sugestões apareçam abaixo."
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr "Use a tecla de atalho %{kbdOpen}/%{kbdClose} para iniciar uma pesquisa"
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr "O que você está procurando?"
@@ -18775,6 +19001,9 @@ msgstr "Ir para suas solicitações de mesclagem"
msgid "Go to your projects"
msgstr "Ir para seus projetos"
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr "Ir para seus snippets"
@@ -18868,6 +19097,12 @@ msgstr "Conceder permissões de escrita a esta chave"
msgid "Graph"
msgstr "Gráfico"
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr "Dependências de tarefa"
@@ -18929,7 +19164,7 @@ msgid "Group applications"
msgstr "Aplicativos de grupo"
msgid "Group audit events"
-msgstr ""
+msgstr "Eventos de auditoria de grupo"
msgid "Group avatar"
msgstr "Avatar do grupo"
@@ -19057,8 +19292,8 @@ msgstr "Últimos 30 dias"
msgid "GroupActivityMetrics|Members added"
msgstr "Membros adicionados"
-msgid "GroupActivityMetrics|Merge Requests created"
-msgstr "GroupActivityMetrics|Solicitações de mesclagem criadas"
+msgid "GroupActivityMetrics|Merge requests created"
+msgstr ""
msgid "GroupActivityMetrics|Recent activity"
msgstr "Atividade recente"
@@ -19144,6 +19379,9 @@ msgstr "Dentro de 3 anos"
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr "Nome do Grupo SAML"
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr "Nome do Grupo SAML: %{saml_group_name}"
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,8 +19661,8 @@ msgstr "Projetos em %{group} não podem ser compartilhados com outros grupos"
msgid "GroupSettings|Reporting"
msgstr "Relatórios"
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
-msgstr "Selecione um subgrupo para usar como fonte para modelos de projeto personalizados para este grupo."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
+msgstr ""
msgid "GroupSettings|Select parent group"
msgstr "Selecionar o grupo pai"
@@ -19441,9 +19682,6 @@ msgstr "Defina o nome inicial e as proteções para a ramificação padrão dos
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr "O pipeline de Auto DevOps é executado se nenhum arquivo de configuração de CI for encontrado."
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr "Os projetos neste subgrupo podem ser selecionados como modelos para novos projetos criados no grupo. %{link_start}Saiba mais.%{link_end}"
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr "Houve um problema ao atualizar o pipeline de Auto DevOps: %{error_messages}."
@@ -19573,6 +19811,9 @@ msgstr "Conectar instância"
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr "Contate um administrador para habilitar opções para importar seu grupo."
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr "Criar grupo"
@@ -19582,9 +19823,6 @@ msgstr "Criar novo grupo"
msgid "GroupsNew|Create subgroup"
msgstr "Criar subgrupo"
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr "Crie isso nas configurações de usuário %{pat_link_start}%{pat_link_end} da instância do GitLab de origem. Por %{short_living_link_start}motivos de segurança%{short_living_link_end}, use uma data de expiração curta ao criar o token."
-
msgid "GroupsNew|GitLab source URL"
msgstr "URL da fonte do GitLab"
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr "Vulnerabilidades altas ou desconhecidas encontradas"
msgid "Highest role:"
msgstr "Função mais alta:"
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr "Eventos de Alerta:"
@@ -20097,9 +20341,6 @@ msgstr "Página inicial"
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr "A execução do hook falhou. Certifique-se de que o grupo tenha um projeto com commits."
-msgid "Hook was successfully updated."
-msgstr "O Hook foi atualizado com sucesso."
-
msgid "Horizontal rule"
msgstr "Régua horizontal"
@@ -20268,6 +20509,9 @@ msgstr "Identificadores"
msgid "Identities"
msgstr "Identidades"
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr "Um novo código foi enviado."
@@ -20292,6 +20536,9 @@ msgstr "Criar um projeto"
msgid "IdentityVerification|Didn't receive a code?"
msgstr "Não recebeu um código?"
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr "Digitar um código."
@@ -20331,8 +20578,8 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr "Número de telefone"
-msgid "IdentityVerification|Phone number can't be blank."
-msgstr "O número de telefone não pode ficar em branco."
+msgid "IdentityVerification|Phone number is required."
+msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
msgstr ""
@@ -20373,6 +20620,12 @@ msgstr "O código está incorreto. Digite-o novamente ou envie um novo código."
msgid "IdentityVerification|Verification code"
msgstr "Código de verificação"
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr "Verificação bem-sucedida"
@@ -20385,9 +20638,18 @@ msgstr "Verificar endereço de e-mail"
msgid "IdentityVerification|Verify payment method"
msgstr "Verificar forma de pagamento"
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr "Verificar sua identidade"
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr "Você sempre pode verificar sua conta posteriormente para criar um grupo."
@@ -20733,6 +20995,9 @@ msgstr "Não há um repositório de Git válido neste URL. Se seu repositório H
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr "No caso de espelhamento de pull, seu usuário será o autor de todos os eventos no feed de atividades que são o resultado de uma atualização, como novas ramificações sendo criadas ou novos commits sendo enviados para ramificações existentes."
@@ -21019,10 +21284,10 @@ msgid "InProductMarketing|Invite your colleagues today"
msgstr "Convide seus colegas hoje mesmo"
msgid "InProductMarketing|Invite your team in less than 60 seconds"
-msgstr ""
+msgstr "Convide sua equipe em menos de 60 segundos"
msgid "InProductMarketing|Invite your team now"
-msgstr ""
+msgstr "Convide sua equipe agora"
msgid "InProductMarketing|Invite your team today to build better code (and processes) together"
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr "Incidente"
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr "Detalhes do incidente"
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr "%{hours} horas, %{minutes} minutos restantes"
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr "Indica se este executor pode escolher tarefas sem tags"
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr "Informar aos usuários sem chaves SSH configuradas que eles não podem realizar pushes por SSH até que uma seja adicionada"
@@ -21896,6 +22170,9 @@ msgstr "Ativar verificação de SSL"
msgid "Integrations|Enable comments"
msgstr "Ativar comentários"
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr "Os administradores do GitLab podem configurar integrações que todos os projetos herdam e usam por padrão. Essas integrações se aplicam a todos os projetos que ainda não usam configurações personalizadas. Você pode substituir configurações personalizadas para um projeto, se as configurações forem necessárias nesse nível. Saiba mais sobre o %{integrations_link_start}gerenciamento de integração em nível em grupo%{link_end}."
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr "Gerenciamento de integração em nível de grupo"
@@ -22277,6 +22557,9 @@ msgstr "Convide seus colegas"
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr "Percebemos que você não convidou ninguém para este grupo. Convide seus colegas para que você possa discutir problemas, colaborar em solicitações de mesclagem e compartilhar seu conhecimento."
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,8 +22608,8 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr "Membros foram adicionados com sucesso"
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
-msgstr "Por favor, selecione membros ou digite endereços de e-mail para convidar"
+msgid "InviteMembersModal|Please add members to invite"
+msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr "Revise os erros de convite e tente novamente:"
@@ -22406,7 +22689,7 @@ msgid "InviteMember|Invite members"
msgstr "Convidar membros"
msgid "InviteMember|Invite your team"
-msgstr ""
+msgstr "Convide sua equipe"
msgid "InviteMember|Invited users will be added with developer level permissions. %{linkStart}View the documentation%{linkEnd} to see how to change this later."
msgstr "Os usuários convidados serão adicionados com permissões de nível de desenvolvedor. %{linkStart}Veja a documentação%{linkEnd} para ver como alterar isso mais tarde."
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr "Título"
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr "Erro ao carregar cadências de iteração."
msgid "Iterations|Iteration cadences"
msgstr "Cadências de iteração"
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr "Iterações|As iterações são uma maneira de acompanhar issues durante um período de tempo, permitindo que as equipes também acompanhem as métricas de velocidade e volatilidade."
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr "Nenhuma tarefa para mostrar"
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr "Status"
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr "Essa tarefa travou porque o projeto não tem qualquer executor online at
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr "Aguardando recurso"
@@ -23602,6 +23879,9 @@ msgstr "Chave"
msgid "Key (PEM)"
msgstr "Chave (PEM)"
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr "Chave:"
@@ -23671,9 +23951,6 @@ msgstr "Clusters de Kubernetes"
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr "LDAP"
@@ -23748,15 +24025,18 @@ msgstr "Etiquetas"
msgid "Labels"
msgstr "Etiquetas"
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr "Etiquetas podem ser aplicadas à %{features}. Etiquetas de grupo estão disponíveis para qualquer projeto dentro do grupo."
-
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "Etiquetas podem ser aplicadas a issues e solicitações de mesclagem para categorizá-los."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr "As etiquetas podem ser aplicadas a issues e solicitações de mesclagem. Marque uma etiqueta com estrela para torná-la uma etiqueta de prioridade."
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr "Etiquetas sem issues nesta iteração:"
@@ -23843,6 +24123,9 @@ msgstr "Última edição por %{link_start}%{avatar} %{name}%{link_end}"
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr "Último acesso"
+
msgid "Last modified"
msgstr "Última modificação"
@@ -23948,6 +24231,9 @@ msgstr "Saiba mais"
msgid "Learn More."
msgstr "Saiba mais."
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr "Aprenda como %{link_start}contribuir para os modelos embutidos%{link_end}"
@@ -24226,8 +24512,8 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected %d license and policy violation for the source branch only"
msgid_plural "LicenseCompliance|License Compliance detected %d licenses and policy violations for the source branch only"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "Conformidade de licença detectou %d violação de licença e política apenas para a filial de origem"
+msgstr[1] "Conformidade de licença detectou %d licenças e violações de política apenas para a filial de origem"
msgid "LicenseCompliance|License Compliance detected %d license and policy violation for the source branch only; approval required"
msgid_plural "LicenseCompliance|License Compliance detected %d licenses and policy violations for the source branch only; approval required"
@@ -24623,12 +24909,12 @@ msgstr "Arquivos travados"
msgid "Locked by %{fileLockUserName}"
msgstr "Bloqueado por %{fileLockUserName}"
+msgid "Locked files"
+msgstr "Arquivos travados"
+
msgid "Locked the discussion."
msgstr "Bloqueou a discussão."
-msgid "Locks give the ability to lock specific file or folder."
-msgstr "Travas possibilitam travar um arquivo ou uma pasta específica."
-
msgid "Locks the discussion."
msgstr "Bloqueia a discussão."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr "Registros"
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr "Gerenciar etiquetas de %{workspace}"
+msgid "Manage Dashboards"
+msgstr "Gerenciar painéis"
+
msgid "Manage Web IDE features."
msgstr "Gerenciar recursos do IDE Web."
@@ -25280,6 +25572,9 @@ msgstr "Tempo médio para mesclar"
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr "Medido em bytes de código. Exclui código gerado e fornecido."
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr "Tempo limite médio"
@@ -25301,6 +25596,12 @@ msgstr "%{member_name} convidou você para participar do GitLab"
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr "não pode ser alterado"
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr "Membros"
@@ -26428,6 +26729,9 @@ msgstr "Adicionar projetos"
msgid "Modal|Close"
msgstr "Fechar"
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr "Modificado"
@@ -26491,9 +26795,6 @@ msgstr "Mais informações"
msgid "More information"
msgstr "Mais informações"
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr "Mais informações disponíveis|aqui"
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr "Comprar mais armazenamento"
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr "Carregamentos não são contados em cotas de armazenamento de espaço de nome."
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr "Recomendamos que você compre armazenamento adicional para garantir que seu serviço não seja interrompido."
@@ -26882,6 +27186,9 @@ msgstr "Nova ramificação"
msgid "New branch unavailable"
msgstr "Nova ramificação indisponível"
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr "Nova identidade"
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "Nova issue"
@@ -27197,9 +27510,6 @@ msgstr "Nenhuma issue encontrada"
msgid "No iteration"
msgstr "Sem iteração"
-msgid "No iterations to show"
-msgstr "Nenhuma iteração para mostrar"
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr "Nenhum resultado"
msgid "No results found"
msgstr "Nenhum resultado encontrado"
+msgid "No results found."
+msgstr "Nenhum resultado encontrado."
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr "OK"
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr "O objeto não existe no servidor ou você não tem permissão para acessá-lo"
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr "Fuso horário"
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr "Ver resultados"
@@ -28417,9 +28748,6 @@ msgstr "Acessível apenas por %{membersPageLinkStart}membros do projeto%{members
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr "Abrir em nova janela"
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr "A operação falhou. Verifique os registros do pod para %{pod_name} para mais detalhes."
-
msgid "Operation not allowed"
msgstr "Operação não permitida"
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr "Excluir ativos do pacote"
msgid "PackageRegistry|Delete package version"
msgstr "Excluir versão do pacote"
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr "Pacotes duplicados"
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr "Erro ao publicar"
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr "Genérico"
@@ -29013,6 +29362,9 @@ msgstr "Comando NuGet"
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr "Número de ativos duplicados a serem mantidos"
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr "Registro de pacote"
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr "Formatos de pacote"
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr "Excluir permanentemente"
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr "Para ampliar a sua pesquisa, altere ou remova os filtros acima."
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr "publicado por %{author}"
+msgid "Packages"
+msgstr "Pacotes"
+
msgid "Packages and registries"
msgstr "Pacotes e registros"
@@ -29245,6 +29611,9 @@ msgstr "Parâmetro"
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr "Parâmetros"
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr "Por favor, digite sua senha para confirmar"
msgid "Passwords should be unique and not used for any other sites or services."
msgstr "As senhas devem ser exclusivas e não devem ser usadas para nenhum outro site ou serviço."
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr "Telefone"
+msgid "PhoneVerification|Enter a valid code."
+msgstr "Digite um código válido."
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr "Escolha um nome"
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr "Por favor, complete seu perfil com endereço de e-mail"
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr "Por favor, digite %{phrase_code} para continuar ou feche este modal para cancelar."
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr "Por favor, use este formulário para denunciar aos administradores as contas que criam spam em issues, comentários ou se comportam de maneira inapropriada."
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr "Por favor, aguarde alguns instantes enquanto carregamos o histórico do arquivo para esta linha."
@@ -30778,6 +31180,9 @@ msgstr "Escolha o conteúdo que você deseja ver por padrão em seu painel."
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "Escolha o conteúdo que você quer ver na página de visão geral de um projeto."
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr "Escolha qual versão do IDE Web você deseja usar."
+
msgid "Preferences|Color for added lines"
msgstr "Cor para linhas adicionadas"
@@ -30862,6 +31267,9 @@ msgstr "Tema de realce de sintaxe"
msgid "Preferences|Tab width"
msgstr "Largura da guia"
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr "O Web IDE legado permanece disponível enquanto o novo IDE Web está em Beta."
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr "Este recurso é experimental e as traduções ainda não estão completas."
@@ -30874,9 +31282,15 @@ msgstr "Esta configuração permite que você personalize o comportamento do lay
msgid "Preferences|Time preferences"
msgstr "Preferências de hora"
+msgid "Preferences|Use legacy Web IDE"
+msgstr "Usar IDE Web legado"
+
msgid "Preferences|Use relative times"
msgstr "Usar tempos relativos"
+msgid "Preferences|Web IDE"
+msgstr "IDE Web"
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr "Quando você digita em uma descrição ou caixa de comentário, pressionar %{kbdOpen}Enter%{kbdClose} em uma lista adiciona um novo item abaixo."
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr "Prosseguir"
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr "Análise de produtos"
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr "Atualizar nome de usuário"
msgid "Profiles|Upload new avatar"
msgstr "Enviar novo avatar"
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr "Utilize um email privado - %{email}"
@@ -31892,7 +32321,7 @@ msgid "ProjectSettings|Additional settings that influence how and when merges ar
msgstr "Configurações adicionais que influenciam como e onde as mesclagem são feitas."
msgid "ProjectSettings|All merge requests and commits are made against this branch unless you specify a different one."
-msgstr "ProjectSettings|Todas as solicitações de mesclagem e commits são feitas nessa ramificação, a menos que você especifique uma diferente."
+msgstr "Todas as solicitações de mesclagem e commits são feitas nessa ramificação, a menos que você especifique uma diferente."
msgid "ProjectSettings|All threads must be resolved"
msgstr "Todos os tópicos devem ser resolvidos"
@@ -31900,12 +32329,18 @@ msgstr "Todos os tópicos devem ser resolvidos"
msgid "ProjectSettings|Allow"
msgstr "Permitir"
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr "Sempre mostrar os emojis de aprovação e desaprovação com polegar em issues, solicitações de mesclagem e snippets."
msgid "ProjectSettings|Analytics"
msgstr "Análises"
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr "Fechar automaticamente as issues referenciadas na ramificação padrão"
@@ -31987,9 +32422,6 @@ msgstr "Cada merge cria um commit de mesclagem."
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr "Cada projeto pode ter seu próprio espaço de armazenar suas imagens Docker"
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr "Cada projeto pode ter seu próprio espaço de armazenar seus pacotes."
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr "Cada projeto pode ter seu próprio espaço para armazenar seus pacotes. Nota: O registro do pacote está sempre visível quando um projeto é público."
@@ -32104,6 +32536,9 @@ msgstr "Mesclar só é permitido quando a ramificação de origem está atualiza
msgid "ProjectSettings|Monitor"
msgstr "Monitor"
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr "Nenhum commit de mesclagem foi criado."
@@ -32149,6 +32584,9 @@ msgstr "Visibilidade do projeto"
msgid "ProjectSettings|Public"
msgstr "Público"
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr "Versões"
@@ -32278,8 +32716,8 @@ msgstr "Usuários podem solicitar acesso"
msgid "ProjectSettings|View and edit files in this project."
msgstr "Visualize e edite arquivos neste projeto."
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
-msgstr "Veja e edite arquivos neste projeto. Não membros do projeto têm apenas acesso de leitura."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
+msgstr ""
msgid "ProjectSettings|View project analytics."
msgstr "Ver análise de projeto."
@@ -32359,6 +32797,9 @@ msgstr "Netlify/HTML simples"
msgid "ProjectTemplates|NodeJS Express"
msgstr "NodeJS Express"
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr "Pages/Gatsby"
@@ -32881,9 +33322,6 @@ msgstr "Ramificação protegida"
msgid "Protected Branches"
msgstr "Ramificações protegidas"
-msgid "Protected Environment"
-msgstr "Ambiente protegido"
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33215,7 +33653,7 @@ msgid "Push Rules updated successfully."
msgstr "Regras de push atualizadas com sucesso."
msgid "Push an existing Git repository"
-msgstr ""
+msgstr "Faça push de um repositório Git existente"
msgid "Push an existing folder"
msgstr "Faça push de uma pasta existente"
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] "Atualizando em um segundo para mostrar o status atualizado..."
msgstr[1] "Atualizando em %d segundos para mostrar o status atualizado..."
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr "Regenerar exportação"
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr "Data de lançamento"
@@ -34091,6 +34535,9 @@ msgstr "Reabrir este %{quick_action_target}"
msgid "Reopened this %{quick_action_target}."
msgstr "%{quick_action_target} foi reaberto."
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr "Reabre este %{quick_action_target}."
@@ -34145,11 +34592,8 @@ msgstr "Responder…"
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr "Denunciar abuso"
-
-msgid "Report abuse to admin"
-msgstr "Denunciar abuso ao administrador"
+msgid "Report abuse to administrator"
+msgstr "Reportar abuso ao administrador"
msgid "Report couldn't be prepared."
msgstr ""
@@ -34487,6 +34931,9 @@ msgstr "Requisição"
msgid "Request Access"
msgstr "Solicitar acesso"
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr "Continuar"
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr "Tempo de revisão"
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr "Executar manutenção"
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr "Uma nova versão está disponível"
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr "Uma atualização está disponível para este executor"
@@ -35003,7 +35459,7 @@ msgid "Runners|Assigned Group"
msgstr "Grupo Atribuído"
msgid "Runners|Assigned Projects (%{projectCount})"
-msgstr ""
+msgstr "Projetos atribuídos (%{projectCount})"
msgid "Runners|Associated with one or more projects"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr "Disponível"
+msgid "Runners|Available shared runners: %{count}"
+msgstr "Executores compartilhados disponíveis: %{count}"
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr "Executor"
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr "Filtrar projetos"
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr "Endereço de IP"
+msgid "Runners|Idle"
+msgstr "Ocioso"
+
msgid "Runners|Install a runner"
msgstr "Instalar um executor"
@@ -35151,12 +35616,15 @@ msgstr "Nunca contatados:"
msgid "Runners|Never expires"
msgstr "Nunca expira"
-msgid "Runners|New group runners view"
-msgstr "Visualização dos novos executores no grupo"
+msgid "Runners|New group runners can be registered"
+msgstr "Novos executores do grupo podem ser registrados"
msgid "Runners|New registration token generated!"
msgstr "Novo token de registro gerado!"
+msgid "Runners|No description"
+msgstr "Sem descrição"
+
msgid "Runners|No results found"
msgstr "Nenhum resultado encontrado"
@@ -35249,6 +35717,9 @@ msgstr "Executor #%{runner_id}"
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr "Executor atribuído ao projeto."
@@ -35315,6 +35786,9 @@ msgstr "Executores são os agentes que executam seus trabalhos de CI/CD. Siga as
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr "Executando"
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35349,7 +35823,7 @@ msgid "Runners|Stale"
msgstr "Obsoleto"
msgid "Runners|Stale:"
-msgstr ""
+msgstr "Obsoleto:"
msgid "Runners|Status"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr "Tags"
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr "Tags controlam que tipo de tarefas um executor pode manipular. Ao marcar um executor, você garante que os executores compartilhados lidem apenas com as tarefas para os quais estão equipados."
-msgid "Runners|Take me there!"
-msgstr "Leve-me lá!"
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr "A nova visualização oferece mais espaço e melhor visibilidade da sua frota de executores."
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr "O projeto, grupo ou instância onde o executor foi registrado. Os executores de instâncias são sempre de propriedade do Administrador."
@@ -35482,6 +35950,9 @@ msgstr "específicos"
msgid "Runner|Owner"
msgstr "Proprietário"
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr "Executando"
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr "Entrar em %{groupName}"
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr "Entrar no GitLab para conectar a conta da sua organização"
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr "Chaves SSH"
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr "Chave SSH pública"
+msgid "SSHKey|Authentication"
+msgstr "Autenticação"
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr "Verificação SSL:"
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr "%{thenLabelStart}Então%{thenLabelEnd} Exigir uma %{scan} verificação para executar com perfil do site %{siteProfile} e verificação de perfil %{scannerProfile}"
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr "Selecionar tags (se houver)"
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr "Tags"
+
msgid "ScanExecutionPolicy|agent"
msgstr "agente"
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr "%{ifLabelStart}se%{ifLabelEnd} %{scanners} encontrar mais de %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilidades em uma solicitação de mesclagem aberta visando %{branches}"
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr "%{ifLabelStart}se%{ifLabelEnd} %{selector}"
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr "%{thenLabelStart}Então%{thenLabelEnd} Requer aprovação de %{approvalsRequired} dos seguintes aprovadores:"
msgid "ScanResultPolicy|add an approver"
msgstr "adicionar um aprovador"
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr "verificações"
msgid "ScanResultPolicy|severity levels"
msgstr "níveis de gravidade"
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr "vulnerabilidades permitidas"
+
msgid "ScanResultPolicy|vulnerability states"
msgstr "estados de vulnerabilidade"
@@ -35776,6 +36283,9 @@ msgstr "Pesquisar"
msgid "Search GitLab"
msgstr "Pesquisar no GitLab"
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr "Pesquisar um grupo"
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr "Escolha um projeto"
+msgid "SecurityOrchestration|Choose approver type"
+msgstr "Escolha o tipo de aprovador"
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr "Crie regras de vulnerabilidade mais robustas e aplique-as a todos os seus projetos."
@@ -36350,9 +36863,18 @@ msgstr "Falha ao carregar imagens."
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr "Falha ao carregar as verificações de vulnerabilidade."
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr "Grupos"
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr "Usuários individuais"
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr "Nova política"
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr "Mudanças de política podem levar algum tempo para serem aplicadas."
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr "Aprovações de segurança"
+msgid "SecurityOrchestration|Security Scan"
+msgstr "Verificação de segurança"
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr "Selecione um projeto para armazenar suas políticas de segurança. %{linkStart}Mais informações.%{linkEnd}"
+msgid "SecurityOrchestration|Select groups"
+msgstr "Selecionar grupos"
+
msgid "SecurityOrchestration|Select policy"
msgstr "Selecionar política"
+msgid "SecurityOrchestration|Select scan type"
+msgstr "Selecionar tipo de verificação"
+
msgid "SecurityOrchestration|Select security project"
msgstr "Selecionar projeto de segurança"
+msgid "SecurityOrchestration|Select users"
+msgstr "Selecionar usuários"
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr "Use uma política de resultados de verificação para criar regras que garantam que as issues de segurança sejam verificados antes de mesclar uma solicitação de mesclagem."
@@ -36587,6 +37130,9 @@ msgstr "ramificação"
msgid "SecurityOrchestration|branches"
msgstr "ramificações"
+msgid "SecurityOrchestration|group level branch"
+msgstr "ramificação em nível de grupo"
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr "%{firstProject} e %{secondProject}"
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr "%{firstProject}, %{secondProject} e %{rest}"
+msgid "SecurityReports|Activity"
+msgstr "Atividade"
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr "Adicionar ou remover projetos para monitorar na área de segurança. Projetos incluídos nessa lista terão seus resultados exibidos no painel de segurança e relatório de vulnerabilidade."
@@ -36632,9 +37181,24 @@ msgstr "Adicionar projetos"
msgid "SecurityReports|All activity"
msgstr "Todas as atividades"
+msgid "SecurityReports|All clusters"
+msgstr "Todos os clusters"
+
+msgid "SecurityReports|All images"
+msgstr "Todas as imagens"
+
+msgid "SecurityReports|All projects"
+msgstr "Todos os projetos"
+
msgid "SecurityReports|All severities"
msgstr "Todas as severidades"
+msgid "SecurityReports|All statuses"
+msgstr "Todos os status"
+
+msgid "SecurityReports|All tools"
+msgstr "Todas as ferramentas"
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr "Não disponível"
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr "Selecione uma estrutura de conformidade para aplicar a este projeto. %{l
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr "Selecione um arquivo da barra lateral na esquerda para começar a editar. Depois, você poderá fazer commit das suas alterações."
+msgid "Select a group"
+msgstr "Selecionar grupo"
+
msgid "Select a label"
msgstr "Selecione uma etiqueta"
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr "Selecionar revisor(es)"
+msgid "Select severity (optional)"
+msgstr "Selecionar severidade (opcional)"
+
msgid "Select source"
msgstr "Selecionar fonte"
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr "Executores compartilhados"
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr "Projetos compartilhados"
@@ -37723,6 +38299,9 @@ msgstr "Mostrar a versão mais recente"
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr "Mostrar mais"
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr "Prévia de entrada"
-msgid "Sign in to %{group_name}"
-msgstr "Entre em %{group_name}"
-
msgid "Sign in to GitLab"
msgstr "Entrar no Gitlab"
@@ -38037,7 +38613,7 @@ msgstr "A integração com o Slack permite que você interaja com o GitLab por m
msgid "Slack logo"
msgstr "Logotipo do Slack"
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr "GitLab para Slack"
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr "GitLab for Slack foi instalado com sucesso."
-msgid "SlackIntegration|Install Slack app"
-msgstr "Instalar o aplicativo Slack"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
+msgstr ""
msgid "SlackIntegration|Project alias"
msgstr "Alias do projeto"
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr "Nome da equipe"
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr "Esta integração permite que os usuários realizem operações comuns neste projeto digitando comandos de barra no Slack."
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr "Token de verificação"
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr "Agora você pode fechar esta janela e acessar seu workspace do Slack."
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,11 +38808,11 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr "Alguns domínios comuns não são permitidos. %{learn_more_link}"
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
-msgstr "Alguém editou a issue ao mesmo tempo que você. Por favor, verifique %{linkStart}a issue%{linkEnd} e tenha certeza de que suas alterações não irão intencionalmente remover as alterações da outra pessoa."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr "Alguém editou este %{issueType} ao mesmo tempo que você. A descrição foi atualizada e você precisará fazer as alterações novamente."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr "Algo deu errado ao recuperar a lista de pacotes."
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr "Ocorreu um erro ao obter o certificado Let's Encrypt."
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr "Gasto em"
+msgid "Spent at can't be a future date and time."
+msgstr "O gasto não pode ser uma data e hora futuras."
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr "Iniciar conexão remota"
+
msgid "Start search"
msgstr "Iniciar pesquisa"
@@ -38919,6 +39516,9 @@ msgstr "Estatísticas"
msgid "Status"
msgstr "Status"
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -38998,7 +39598,7 @@ msgid "StatusCheck|You are about to remove the %{name} status check."
msgstr ""
msgid "StatusCheck|status checks"
-msgstr ""
+msgstr "Verificações de status"
msgid "StatusPage|AWS %{docsLink}"
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr "Você não tem uma assinatura ativa"
@@ -39746,9 +40349,6 @@ msgstr "Trocar ramificação"
msgid "Switch branch/tag"
msgstr "Trocar ramificação/tag"
-msgid "Switch editors"
-msgstr "Alternar editores"
-
msgid "Switch to GitLab Next"
msgstr "Mudar para GitLab Next"
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr "Configure pipelines de CI/CD para construir, testar, implantar e monitorar código"
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr "Equipe"
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr "Os seguintes itens NÃO serão exportados:"
@@ -40975,9 +41584,6 @@ msgstr "Não há registros de spam"
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr "Ainda não há projetos arquivados"
-
msgid "There are no archived requirements"
msgstr "Não há requisitos arquivados"
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr "Não há pacotes ainda"
-msgid "There are no projects shared with this group yet"
-msgstr "Não há projetos compartilhados com esse grupo ainda"
-
msgid "There are no secure files yet."
msgstr "Ainda não há arquivos seguros."
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr "Houve um problema de comunicação com o seu dispositivo."
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr "Houve um erro ao redefinir os minutos de pipeline do usuário."
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr "Ocorreu um erro ao salvar suas alterações."
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr "Esse épico não existe ou você não tem permissão suficiente."
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr "Esta funcionalidade requer que o armazenamento local esteja ativado"
@@ -41545,7 +42157,7 @@ msgstr "Este formulário está desativado na pré-visualização"
msgid "This group"
msgstr "Esse grupo"
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr "Tempo restante"
msgid "Time spent"
msgstr "Tempo gasto"
+msgid "Time spent can't be zero."
+msgstr "O tempo gasto não pode ser zero."
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr "O tempo gasto deve ser formatado corretamente. Por exemplo: 1h 30m."
+
msgid "Time to Restore Service"
msgstr "Tempo para restaurar o serviço"
@@ -42269,6 +42887,9 @@ msgstr "Título (obrigatório)"
msgid "Title:"
msgstr "Título:"
+msgid "Titles"
+msgstr "Títulos"
+
msgid "Titles and Descriptions"
msgstr "Títulos e descrições"
@@ -42302,9 +42923,6 @@ msgstr "Para ativar sua avaliação, precisamos de detalhes adicionais de você.
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr "Para adicionar a entrada manualmente, forneça os seguintes detalhes ao aplicativo em seu telefone."
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr "Não foi possível mesclar"
msgid "Todos|Design"
msgstr "Design"
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr "Épico"
@@ -42545,6 +43169,9 @@ msgstr "É assim que você sempre sabe no que trabalhar a seguir."
msgid "Todos|Mark all as done"
msgstr "Marcar tudo como concluído"
+msgid "Todos|Member access requested"
+msgstr "Acesso de membro solicitado"
+
msgid "Todos|Mentioned"
msgstr "Mencionado"
@@ -42560,14 +43187,14 @@ msgstr "Nada a fazer. Toca aqui!"
msgid "Todos|Pipelines"
msgstr "Pipelines"
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr "Revisão solicitada"
-msgid "Todos|The pipeline failed in"
-msgstr "O pipeline falhou em"
+msgid "Todos|The pipeline failed"
+msgstr "O pipeline falhou"
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -42581,24 +43208,24 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr "Sua lista de tarefas mostra o que fazer a seguir"
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
-msgstr "mencionados %{who} em"
+msgid "Todos|has requested access"
+msgstr "pediu acesso"
+
+msgid "Todos|mentioned %{who}"
+msgstr ""
-msgid "Todos|requested a review of"
-msgstr "solicitou uma revisão de"
+msgid "Todos|requested a review"
+msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|set %{who} as an approver"
msgstr ""
msgid "Todos|yourself"
msgstr "você mesmo"
-msgid "Todo|at %{todo_parent_path}"
-msgstr ""
-
msgid "Toggle GitLab Next"
msgstr "Alternar para o GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr "Gatilho"
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr "Experimente o novo IDE Web"
+
msgid "Try the troubleshooting steps here."
msgstr "Tente as etapas de solução de problemas aqui."
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr "Infelizmente, sua mensagem de e-mail para o GitLab não pôde ser proces
msgid "Unhappy?"
msgstr "Infeliz?"
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr "ms"
@@ -43384,6 +44029,9 @@ msgstr "Não resolvido"
msgid "Unschedule job"
msgstr "Desprogramar tarefa"
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr "Desmarcar como favorito"
@@ -43567,6 +44215,9 @@ msgstr "Enviando mudanças ao terminal"
msgid "Uploading..."
msgstr "Enviando..."
+msgid "Uploads"
+msgstr "Envios"
+
msgid "Upstream"
msgstr "Upstream"
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr "%{help_link_start}Executores compartilhados%{help_link_end} estão desativados, então não há limites definidos no uso de pipeline"
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr "%{linkStart}Executores compartilhados%{linkEnd} estão desativados, então não há limites definidos no uso de pipeline"
msgid "UsageQuota|%{linkTitle} help link"
msgstr "link de ajuda %{linkTitle}"
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr "%{percentageLeft} de armazenamento comprado está disponível"
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr "Uso de minutos de CI por projeto"
msgid "UsageQuota|CI/CD minutes usage"
msgstr "Uso de minutos de CI/CD"
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr "Uso de minutos de CI/CD desde %{timeElapsed}"
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr "Uso de minutos de CI/CD desde %{usageSince}"
@@ -43636,9 +44278,6 @@ msgstr "Pacotes de código e imagens de contêiner."
msgid "UsageQuota|Container Registry"
msgstr "Registro de contêiner"
-msgid "UsageQuota|Current period usage"
-msgstr "Uso do período atual"
-
msgid "UsageQuota|Dependency proxy"
msgstr "Proxy de dependência"
@@ -43765,11 +44404,8 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr "A tabela abaixo mostra o uso do período atual"
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
-msgstr "A tabela abaixo mostra o uso desde %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
+msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
msgstr ""
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr "Esta é a quantidade total de armazenamento usada por projetos acima do limite de %{actualRepositorySizeLimit} armazenamento gratuito."
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr "Este espaço de nome contém projetos bloqueados"
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr "Este espaço de nome não possui projetos que usam executores compartilhados"
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr "Este espaço de nome não tem projetos que usaram executores compartilhados no período atual"
@@ -43795,9 +44425,6 @@ msgstr "Total de armazenamento em excesso usado"
msgid "UsageQuota|Total namespace storage used"
msgstr "Total de armazenamento de espaço de nome usado"
-msgid "UsageQuota|Unlimited"
-msgstr "Ilimitado"
-
msgid "UsageQuota|Uploads"
msgstr "Envios"
@@ -43831,7 +44458,7 @@ msgstr "Link de ajuda de cotas de uso"
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr "Configurações do usuário &gt; Cotas de uso"
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,17 +44467,17 @@ msgstr "Wiki"
msgid "UsageQuota|Wiki content."
msgstr "Conteúdo da wiki."
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
-msgstr "Você consumiu todo o seu armazenamento adicional, compre mais para desbloquear seus projetos acima do limite gratuito de %{actualRepositorySizeLimit}."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
+msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
-msgstr "Você atingiu o limite de armazenamento gratuito de %{actualRepositorySizeLimit} em %{projectsLockedText}. Para desbloqueá-los, compre armazenamento adicional."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
+msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr "Você usou: %{usage} %{limit}"
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
-msgstr "O armazenamento adquirido está acabando. Para evitar projetos bloqueados, adquira mais armazenamento."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
+msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
msgstr "de %{formattedLimit} da sua memória de espaço de nome"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr "Projetos pessoais"
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr "Pronunciado como: %{pronunciation}"
-msgid "UserProfile|Report abuse"
-msgstr "Denunciar abuso"
-
msgid "UserProfile|Retry"
msgstr "Tentar novamente"
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr "Visual Studio Code no seu navegador. Veja código e faça alterações na mesma interface do usuário como em seu IDE local 🎉"
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr "Métricas DORA"
msgid "ValueStreamAnalytics|Dashboard"
msgstr "Painel"
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr "Ir para docs"
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr "Número de commits enviados para a ramificação padrão"
@@ -44619,12 +45255,12 @@ msgstr "Parar"
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
-msgstr ""
-
msgid "Variable"
msgstr "Variável"
+msgid "Variable value will be evaluated as raw string."
+msgstr ""
+
msgid "Variable will be masked in job logs."
msgstr "A variável será mascarada nos registros de tarefa."
@@ -44634,8 +45270,8 @@ msgstr "Variáveis"
msgid "Variables can be:"
msgstr "As variáveis podem ser:"
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
-msgstr "As variáveis armazenam informações, como senhas e chaves secretas, que podem ser usadas em scripts de tarefa."
+msgid "Variables can have several attributes."
+msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
msgstr "As variáveis armazenam informações, como senhas e chaves secretas, que podem ser usadas em scripts de tarefa. Todos os projetos na instância podem usar essas variáveis."
@@ -44691,6 +45327,21 @@ msgstr "Versão %{versionNumber}"
msgid "Version %{versionNumber} (latest)"
msgstr "Versão %{versionNumber} (última)"
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr "Lembrar-me novamente em 3 dias"
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -44980,7 +45643,7 @@ msgid "VulnerabilityManagement|A verified true-positive vulnerability"
msgstr ""
msgid "VulnerabilityManagement|Add vulnerability finding"
-msgstr ""
+msgstr "Adicionar descoberta de vulnerabilidade"
msgid "VulnerabilityManagement|An unverified non-confirmed finding"
msgstr ""
@@ -45010,7 +45673,7 @@ msgid "VulnerabilityManagement|Identifier code and URL are required fields"
msgstr ""
msgid "VulnerabilityManagement|Manually add a vulnerability entry into the vulnerability report."
-msgstr ""
+msgstr "Adicione manualmente uma entrada de vulnerabilidade no relatório de vulnerabilidade."
msgid "VulnerabilityManagement|Name is a required field"
msgstr ""
@@ -45073,13 +45736,13 @@ msgid "VulnerabilityManagement|Submit vulnerability"
msgstr "Enviar vulnerabilidade"
msgid "VulnerabilityManagement|Summary, detailed description, steps to reproduce, etc."
-msgstr ""
+msgstr "Resumo, descrição detalhada, passos para reproduzir, etc."
msgid "VulnerabilityManagement|Verified as fixed or mitigated"
msgstr ""
msgid "VulnerabilityManagement|Vulnerability name or type. Ex: Cross-site scripting"
-msgstr ""
+msgstr "Nome ou tipo de vulnerabilidade. Ex: script entre sites"
msgid "VulnerabilityManagement|Will not fix or a false-positive"
msgstr ""
@@ -45319,7 +45982,7 @@ msgid "Watch how"
msgstr ""
msgid "We also use email for avatar detection if no avatar is uploaded."
-msgstr ""
+msgstr "Também usamos e-mail para detecção de avatar se nenhum avatar for carregado."
msgid "We are currently unable to fetch data for the pipeline header."
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr "Detectamos spam potencial no %{humanized_resource_name}. Por favor, reso
msgid "We don't have enough data to show this stage."
msgstr "Esse estágio não possui dados suficientes para exibição."
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr "Dispositivos WebAuthn (%{length})"
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr "O WebAuthn só funciona com sites habilitados para HTTPS. Entre em contato com seu administrador para mais detalhes."
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr "Tem certeza de que deseja trocar de editor? Você perderá todas as alterações não salvas."
-
msgid "WebIDE|Fork project"
msgstr "Fork projeto"
@@ -45453,24 +46116,12 @@ msgstr "Edite rapidamente e facilmente vários arquivos em seu projeto."
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr "Edite de forma rápida e fácil vários arquivos em seu projeto. Imprensa . abrir"
-msgid "WebIDE|Ready for something new?"
-msgstr "Pronto para algo novo?"
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr "Mudar para o novo IDE Web"
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr "Você está convidado a experimentar o novo IDE Web."
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr "Você não pode editar arquivos diretamente nesse projeto. Faça um fork nesse projeto e envie uma solicitação de mesclagem com suas alterações."
@@ -45501,6 +46152,18 @@ msgstr "Configurações de webhook"
msgid "Webhook events will be displayed here."
msgstr "Os eventos do webhook serão exibidos aqui."
+msgid "Webhook was created"
+msgstr "Webhook foi criado"
+
+msgid "Webhook was deleted"
+msgstr "Webhook foi excluído"
+
+msgid "Webhook was scheduled for deletion"
+msgstr "Webhook foi agendado para exclusão"
+
+msgid "Webhook was updated"
+msgstr "Webhook foi atualizado"
+
msgid "Webhook:"
msgstr "Webhook:"
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr "Comentários"
-
msgid "Webhooks|Confidential comments"
msgstr "Comentários confidenciais"
@@ -45618,15 +46278,12 @@ msgstr "Eventos de membro"
msgid "Webhooks|Merge request events"
msgstr "Eventos de solicitação de mesclagem"
+msgid "Webhooks|Must match part of URL"
+msgstr ""
+
msgid "Webhooks|Pipeline events"
msgstr "Eventos de pipeline"
-msgid "Webhooks|Push events"
-msgstr "Eventos de push"
-
-msgid "Webhooks|Push to the repository."
-msgstr "Push para o repositório."
-
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
msgstr ""
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr "Gatilho"
-msgid "Webhooks|URL"
-msgstr "URL"
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr "URL deve ser codificado por porcentagem se contiver um ou mais caracteres especiais."
@@ -46057,6 +46711,9 @@ msgstr "Será criado"
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr "Será implantado para"
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr "Com os requisitos, pode estabelecer critérios para comprovar sus produtos."
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr "Com casos de teste, você pode definir condições para seu projeto atender na determinação da qualidade"
-
msgid "Withdraw Access Request"
msgstr "Remover requisição de acesso"
@@ -46087,6 +46741,12 @@ msgstr "%{workItemType} excluído"
msgid "WorkItem|Add"
msgstr "Adicionar"
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr "Adicionar %{workItemType}s"
+
msgid "WorkItem|Add a title"
msgstr "Adicionar um título"
@@ -46102,9 +46762,6 @@ msgstr "Adicionar data de vencimento"
msgid "WorkItem|Add start date"
msgstr "Adicionar data de início"
-msgid "WorkItem|Add task"
-msgstr "Adicionar tarefa"
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] "Responsáveis"
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr "Fechado"
msgid "WorkItem|Collapse tasks"
msgstr "Recolher tarefas"
+msgid "WorkItem|Create %{workItemType}"
+msgstr "Criar %{workItemType}"
+
+msgid "WorkItem|Create objective"
+msgstr "Criar objetivo"
+
msgid "WorkItem|Create task"
msgstr "Criar tarefa"
@@ -46146,30 +46812,39 @@ msgstr "Datas"
msgid "WorkItem|Delete %{workItemType}"
msgstr "Excluir %{workItemType}"
+msgid "WorkItem|Discard changes"
+msgstr "Descartar alterações"
+
msgid "WorkItem|Due date"
msgstr "Data de vencimento"
+msgid "WorkItem|Existing task"
+msgstr "Tarefa existente"
+
msgid "WorkItem|Expand tasks"
msgstr "Expandir tarefas"
msgid "WorkItem|Incident"
msgstr "Incidente"
-msgid "WorkItem|Introducing tasks"
-msgstr "Introdução de tarefas"
-
msgid "WorkItem|Issue"
msgstr "Issue"
msgid "WorkItem|Iteration"
msgstr "Iteração"
-msgid "WorkItem|Learn about tasks."
-msgstr "Saiba mais sobre tarefas."
+msgid "WorkItem|Key result"
+msgstr ""
msgid "WorkItem|Milestone"
msgstr "Marco"
+msgid "WorkItem|New objective"
+msgstr "Novo objetivo"
+
+msgid "WorkItem|New task"
+msgstr "Nova tarefa"
+
msgid "WorkItem|No iteration"
msgstr "Sem iteração"
@@ -46179,12 +46854,18 @@ msgstr "Nenhum resultado correspondente"
msgid "WorkItem|No milestone"
msgstr "Nenhum marco"
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr "Nenhum objetivo ou resultado principal está atribuído atualmente."
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr "Nenhuma tarefa está atribuída no momento. Use tarefas para dividir esse problema em partes menores."
msgid "WorkItem|None"
msgstr "Nenhum"
+msgid "WorkItem|Objective"
+msgstr "Objetivo"
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr "Apenas membros do projeto com pelo menos a função de Relator, o autor e os responsáveis podem visualizar ou serem notificados sobre esta tarefa."
@@ -46197,9 +46878,18 @@ msgstr "Remover"
msgid "WorkItem|Requirements"
msgstr "Requisitos"
+msgid "WorkItem|Save and overwrite"
+msgstr "Salvar e sobrescrever"
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr "Selecione o tipo"
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,8 +46953,8 @@ msgstr "Ativar confidencialidade"
msgid "WorkItem|Undo"
msgstr "Desfazer"
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
-msgstr "Use tarefas para dividir seu trabalho em uma issue em partes menores. %{learnMoreLink}"
+msgid "WorkItem|View current version"
+msgstr "Ver versão atual"
msgid "WorkItem|Work Items"
msgstr ""
@@ -46293,6 +46983,9 @@ msgstr "Escreva um comentário…"
msgid "Write a description or drag your files here…"
msgstr "Escreva uma descrição ou arraste seus arquivos para aqui…"
+msgid "Write a description..."
+msgstr "Escreva uma descrição..."
+
msgid "Write a description…"
msgstr "Escreva uma descrição…"
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46488,7 +47181,7 @@ msgid "You can also test your %{gitlab_ci_yml} in %{lint_link_start}CI Lint%{lin
msgstr ""
msgid "You can also upload existing files from your computer using the instructions below."
-msgstr ""
+msgstr "Você também enviar arquivos existentes do seu computador usando as instruções abaixo."
msgid "You can also use group access tokens with Git to authenticate over HTTP(S). %{link_start}Learn more.%{link_end}"
msgstr ""
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr "Você pode editar isto depois"
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr "Você pode agrupar casos de teste usando etiquetas. Para saber mais sobre a direção futura desse recurso, visite %{linkStart}direção de gerenciamento de qualidade página%{linkEnd}."
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "Você pode convidar um novo membro para %{project_name} ou convidar outro grupo."
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr "Agora você pode enviar uma solicitação de mesclagem para fazer esta mudança no projeto original."
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr "Você não pode editar arquivos diretamente nesse projeto. Faça um fork
msgid "You could not create a new trigger."
msgstr "Você não pôde criar um novo gatilho."
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr "Você ainda não tem nenhuma inscrição"
@@ -46741,6 +47423,9 @@ msgstr "Você não tem nenhuma pesquisa recente"
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr "Você não tem permissões suficientes para gerenciar alertas para este projeto"
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr "Você configurou o 2FA para sua conta! Se você perder o acesso ao seu d
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr "Você tem mudanças não salvas"
@@ -47034,6 +47719,9 @@ msgstr "Você já ativou a autenticação de dois fatores usando autenticadores
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr "Seus grupos"
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] "Seu grupo gratuito agora está limitado a %d membro"
msgstr[1] "Seu grupo gratuito agora está limitado a %d membros"
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr "Seus grupos"
@@ -47275,6 +47961,15 @@ msgstr "Seu nome"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr "[Redacted]"
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr "não pode estar em branco"
+
msgid "cannot be changed"
msgstr "não pode ser alterado"
@@ -47663,17 +48364,27 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
msgid "ciReport|%{danger_start}%{degradedNum} degraded%{danger_end}, %{same_start}%{sameNum} same%{same_end}, and %{success_start}%{improvedNum} improved%{success_end}"
-msgstr ""
+msgstr "%{danger_start}%{degradedNum} piorou%{danger_end}, %{same_start}%{sameNum} igual%{same_end}e %{success_start}%{improvedNum} melhorou%{success_end}"
msgid "ciReport|%{degradedNum} degraded"
msgstr ""
msgid "ciReport|%{improvedNum} improved"
-msgstr ""
+msgstr "%{improvedNum} melhorado"
msgid "ciReport|%{linkStartTag}Learn more about API Fuzzing%{linkEndTag}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47766,7 +48480,7 @@ msgid "ciReport|Browser performance test metrics results are being parsed"
msgstr ""
msgid "ciReport|Browser performance test metrics: "
-msgstr ""
+msgstr "Métricas de teste de desempenho do navegador: "
msgid "ciReport|Browser performance test metrics: %{strong_start}%{changesFound}%{strong_end} change"
msgid_plural "ciReport|Browser performance test metrics: %{strong_start}%{changesFound}%{strong_end} changes"
@@ -47882,7 +48596,7 @@ msgstr "Corrigido:"
msgid "ciReport|Found %{issuesWithCount}"
msgstr "Encontrado %{issuesWithCount}"
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr "Relatório completo"
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr "comentou em %{link_to_project}"
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr "commit %{commit_id}"
@@ -48217,9 +48936,6 @@ msgstr "example.com"
msgid "exceeds maximum length (100 usernames)"
msgstr "excede o comprimento máximo (100 nomes de usuários)"
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr "excede o limite de %{bytes} bytes"
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr "já está vinculado a esta vulnerabilidade"
+msgid "is already present in ancestors"
+msgstr "já está presente nos antepassados"
+
msgid "is an invalid IP address range"
msgstr "é um intervalo de endereços IP inválido"
@@ -48441,6 +49160,12 @@ msgstr "não é permitido para este projeto."
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr "não é permitido apontar para si mesmo"
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48773,7 +49498,7 @@ msgid "mrWidget|Merge blocked: all threads must be resolved."
msgstr "Mesclagem bloqueada: todos os tópicos devem ser resolvidos."
msgid "mrWidget|Merge blocked: denied licenses must be removed."
-msgstr ""
+msgstr "Mesclar bloqueado: as licenças negadas devem ser removidas."
msgid "mrWidget|Merge blocked: fast-forward merge is not possible. To merge this request, first rebase locally."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr "deve ser maior que a data de início"
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,12 +49671,12 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
-msgstr "deve ser um espaço de nome de nível superior"
-
msgid "must be unique by status and elapsed time within a policy"
msgstr ""
+msgid "must belong to same project of its requirement object."
+msgstr ""
+
msgid "must belong to same project of the work item."
msgstr ""
@@ -49150,6 +49878,9 @@ msgstr "Chave privada de reCAPTCHA"
msgid "reCAPTCHA site key"
msgstr "Chave de reCAPTCHA do site"
+msgid "reached maximum depth"
+msgstr "atingiu a profundidade máxima"
+
msgid "recent activity"
msgstr "atividade recente"
@@ -49205,6 +49936,11 @@ msgstr "repositório:"
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr "executando"
diff --git a/locale/pt_BR/gitlab.po.time_stamp b/locale/pt_BR/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/pt_BR/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/pt_PT/gitlab.po b/locale/pt_PT/gitlab.po
index 6a93ec2275d..885de7649e7 100644
--- a/locale/pt_PT/gitlab.po
+++ b/locale/pt_PT/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: pt-PT\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:22\n"
+"PO-Revision-Date: 2022-12-10 06:41\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] "mais %d comentário"
msgstr[1] "mais %d comentários"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr "%{group_name} usa contas de gestão de grupo. Precisas de criar uma nova
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr "%{text} está disponível"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr "(Sem alterações)"
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr "(verificar progresso)"
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "A adição de novas aplicações está desativada na tua instância do GitLab. Entra em contacto com o teu administrador do GitLab para obter a permissão"
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "Minutos adicionais"
@@ -2646,6 +2674,9 @@ msgstr "Erro ao parar trabalhos"
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr "Ocorreu um erro. Por favor, tenta novamente."
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr "Qualquer espaço de nome"
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "ago"
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr "Executadores específicos disponíveis"
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "Ramos"
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr "Ativo"
@@ -7049,8 +7092,11 @@ msgstr ""
msgid "Branches|Compare"
msgstr "Comparar"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "Apagar todos os ramos que foram mesclados em '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
+msgstr ""
msgid "Branches|Delete branch"
msgstr "Apagar ramo"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "Apagar ramos mesclados não pode ser desfeito. Tens a certeza?"
-
msgid "Branches|Filter by branch name"
msgstr "Filtrar por nome de ramo"
@@ -7094,6 +7137,9 @@ msgstr "Visão Geral"
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr "Mostrar ramos ativos"
@@ -7127,6 +7173,12 @@ msgstr "O ramo padrão não pode ser apagado"
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr "Para rejeitar as alterações locais e sobrescrever a ramificação com a versão upstream, apaga-o aqui e escolhe \"Atualizar Agora\" em cima."
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr "Incorporado"
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "A verificar a disponibilidade de %{text}…"
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr "em execução"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr "Não podes usar a Variável Mascarada com o valor atual"
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr "Chave da variável de entrada"
@@ -8468,6 +8538,9 @@ msgstr "Chave"
msgid "CiVariables|Masked"
msgstr "Mascarado"
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr "Remover linha da variável"
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr "Escopo"
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr "Estado"
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr "Tipo"
@@ -8498,6 +8577,12 @@ msgstr "Valor"
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr "* (Todos os ambientes)"
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr "Falha ao enviar o ficheiro de mapeamento de objeto"
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr "Houve um problema de atualização da pipeline Auto DevOps: %{error_messages}."
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr "Informar os utilizadores sem enviar as chaves SSH que não podem empurrar através de SSH até que um seja adicionado"
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr "Enviar novo avatar"
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr "Executadores Compartilhados"
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/ro_RO/gitlab.po b/locale/ro_RO/gitlab.po
index 9328769bce1..6f9507e3cf6 100644
--- a/locale/ro_RO/gitlab.po
+++ b/locale/ro_RO/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: ro\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:20\n"
+"PO-Revision-Date: 2022-12-10 06:39\n"
msgid " %{start} to %{end}"
msgstr " de la %{start} până la %{end}"
@@ -397,6 +397,12 @@ msgstr[0] "%d comentariu în plus"
msgstr[1] "%d comentarii în plus"
msgstr[2] "%d de comentarii în plus"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%d comentariu în așteptare"
@@ -586,6 +592,12 @@ msgstr[2] "%{bold_start}%{count}%{bold_end} de merge request-uri deschise"
msgid "%{chartTitle} no data series"
msgstr "%{chartTitle} fără nicio serie de date"
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr "%{code_open}Mascate:%{code_close} Ascunse în jurnalele jobului. Trebuie să se potrivească cerințelor de mascare."
@@ -796,15 +808,15 @@ msgstr "%{group_name} utilizează conturi gestionate de grup. Trebuie să creaț
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr "%{group_name}&%{epic_iid} &middot; a creat %{epic_created} de %{author}"
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type} a fost șters"
-
-msgid "%{hook_type} was scheduled for deletion"
-msgstr "%{hook_type} a fost programat pentru ștergere"
-
msgid "%{host} sign-in from new location"
msgstr "%{host} se conectează dintr-o nouă locație"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
+msgstr ""
+
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr ""
+
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr "%{integrations_link_start}Integrările%{link_end} vă permit să faceți aplicații terțe parte din fluxul de lucru GitLab. Dacă integrările disponibile nu vă satisfac nevoile, considerați posibilitatea de a utiliza un %{webhooks_link_start}webhook%{link_end}."
@@ -997,9 +1009,6 @@ msgstr "%{openedEpics} deschise, %{closedEpics} închise"
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr "%{openedIssues} deschise, %{closedIssues} închise"
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr "%{over_limit_message} Pentru obținerea mai multor membri, proprietarul grupului poate să înceapă o perioadă de probă sau să treacă la un nivel plătit."
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr "%{over_limit_message} Pentru a obține mai multe seat-uri, %{link_start}faceți upgrade la un nivel plătit%{link_end}."
@@ -1051,8 +1060,8 @@ msgstr "%{reportType} %{status}"
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr "%{reportType} a detectat %{totalStart}%{total}%{totalEnd} potențiale %{vulnMessage}"
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
-msgstr "%{reportType} nu a detectat %{totalStart}noi%{totalEnd} vulnerabilități."
+msgid "%{reportType} detected no new vulnerabilities."
+msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
msgstr "%{retryButtonStart}Încercați din nou%{retryButtonEnd} sau %{newFileButtonStart}atașați un nou fișier%{newFileButtonEnd}."
@@ -1213,6 +1222,12 @@ msgstr "%{template_project_id} este necunoscut sau invalid"
msgid "%{text} is available"
msgstr "%{text} este disponibil(ă)"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr "%{timebox_type} nu acceptă grafice burnup"
@@ -1252,6 +1267,9 @@ msgstr "%{type} trebuie să fie un/o %{help_link}"
msgid "%{type} only supports %{name} name"
msgstr "%{type} acceptă numai numele %{name}"
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr "%{userName} (nu poate îmbina)"
@@ -1372,12 +1390,18 @@ msgstr "(+%{count}&nbsp;reguli)"
msgid "(Group Managed Account)"
msgstr "(Cont gestionat de grup)"
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr "(Nicio schimbare)"
msgid "(UTC %{offset}) %{timezone}"
msgstr "(UTC %{offset}) %{timezone}"
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr "(verificați progresul)"
@@ -2497,6 +2521,9 @@ msgstr "Adăugare hook de sistem"
msgid "Add text to the sign-in page. Markdown enabled."
msgstr "Adăugați text la pagina de autentificare. Markdown activat."
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr "Adăugați la bord"
@@ -2584,6 +2611,9 @@ msgstr "S-a adăugat în această versiune"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "Adăugarea de aplicații noi este dezactivată în instanța dvs. GitLab. Vă rugăm să contactați administratorul dvs. GitLab pentru a obține permisiunea"
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "Minute suplimentare"
@@ -2779,6 +2809,9 @@ msgstr "Oprirea joburilor a eșuat"
msgid "AdminArea|Total users"
msgstr "Total utilizatori"
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr "Utilizatori"
@@ -3079,8 +3112,8 @@ msgstr "Căutați cu Elasticsearch activat"
msgid "AdminSettings|Select a CI/CD template"
msgstr "Selectați un șablon CI/CD"
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
-msgstr "Selectați un grup care să fie utilizat ca sursă pentru șabloanele de proiect la nivel de instanță."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
+msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
msgstr "Selectați această opțiune pentru a dezactiva accesul public pentru site-urile Pages, care necesită ca utilizatorii să se înregistreze pentru a avea acces la site-urile Pages din instanța dvs. %{link_start}Aflați mai multe.%{link_end}"
@@ -3142,9 +3175,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr "Cele mai recente artefacte pentru toate joburile din cele mai recente pipeline-uri reușite din fiecare proiect sunt stocate și nu expiră."
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr "Proiectele din acest grup pot fi selectate ca șabloane pentru proiectele noi create pe instanță. %{link_start}Aflați mai multe.%{link_end} "
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr "Șablonul pentru configurația pipeline necesară poate fi unul dintre șabloanele furnizate de GitLab sau un șablon personalizat adăugat la un repozitoriu de șabloane de instanță. %{link_start}Cum se creează un repozitoriu de șabloane de instanță?%{link_end}"
@@ -4672,6 +4702,9 @@ msgstr "S-a produs o eroare. Vă rugăm să vă autentificați din nou."
msgid "An error occurred. Please try again."
msgstr "A apărut o eroare. Vă rugăm să încercați din nou."
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr "Un exemplu de proiect pentru gestionarea clusterelor Kubernetes integrat cu GitLab"
@@ -4789,9 +4822,6 @@ msgstr "Orice obiectiv"
msgid "Any namespace"
msgstr "Orice spațiu de nume"
-msgid "Anyone can register for an account."
-msgstr "Oricine se poate înregistra pentru un cont."
-
msgid "App ID"
msgstr "ID aplicație"
@@ -5338,6 +5368,9 @@ msgstr[0] "Sunteți sigur că doriți să importați %d repozitoriu?"
msgstr[1] "Sunteți sigur că doriți să importați %d repozitorii?"
msgstr[2] "Sunteți sigur că doriți să importați %d de repozitorii?"
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr "Sunteți sigur că vreți să blocați %{path}?"
@@ -5749,6 +5782,9 @@ msgstr "Ștergeți %{link}"
msgid "AuditStreams|Destination URL"
msgstr "URL-ul de destinație"
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr "Destinațiile primesc toate datele evenimentelor de audit"
@@ -5794,6 +5830,9 @@ msgstr "Valoare"
msgid "AuditStreams|Verification token"
msgstr "Token de verificare"
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "Aug"
@@ -6025,9 +6064,6 @@ msgstr "Executori de grup disponibili: %{runners}"
msgid "Available on-demand"
msgstr "Disponibil la cerere"
-msgid "Available shared runners:"
-msgstr "Executori partajați disponibili:"
-
msgid "Available specific runners"
msgstr "Executori specifici disponibili"
@@ -7051,6 +7087,15 @@ msgstr "%{linkStart}Caracterele wildcard%{linkEnd}, cum ar fi *-stable sau produ
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr "Sunt acceptate wildcard-uri %{linkStart}%{linkEnd}, cum ar fi *-stable sau production/*."
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr "Toate ramurile"
@@ -7165,9 +7210,6 @@ msgstr "Utilizatori"
msgid "BranchRules|default"
msgstr "implicit"
-msgid "BranchRules|protected"
-msgstr "protejat"
-
msgid "Branches"
msgstr "Ramuri"
@@ -7177,6 +7219,9 @@ msgstr "Ramuri: %{source_branch} la %{target_branch}"
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr "Ramuri: %{source_branch} → %{target_branch}"
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr "Active"
@@ -7198,8 +7243,11 @@ msgstr "Nu se poate găsi commit-ul HEAD pentru această ramură"
msgid "Branches|Compare"
msgstr "Comparați"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "Șterge toate ramurile care sunt îmbinate în „%{default_branch}â€"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
+msgstr ""
msgid "Branches|Delete branch"
msgstr "Ștergeți ramura"
@@ -7219,9 +7267,6 @@ msgstr "Ștergeți ramura protejată. Sunteți ABSOLUT SIGUR?"
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr "Ștergerea ramurii %{strongStart}%{branchName}%{strongEnd} nu poate fi anulată. Sunteți sigur?"
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "Ștergerea ramurilor îmbinate nu poate fi anulată. Sunteți sigur?"
-
msgid "Branches|Filter by branch name"
msgstr "Filtrați după numele ramurii"
@@ -7243,6 +7288,9 @@ msgstr "Vedere de ansamblu"
msgid "Branches|Please type the following to confirm:"
msgstr "Vă rugăm să introduceți următoarele date pentru a confirma:"
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr "Afișați ramurile active"
@@ -7276,6 +7324,12 @@ msgstr "Ramura implicită nu poate fi ștearsă"
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr "Această ramură nu a fost îmbinată în %{defaultBranchName}. Pentru a evita pierderea de date, considerați îmbinarea acestei ramuri înainte de a o șterge."
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr "Pentru a renunța la modificările locale și a suprascrie ramura cu versiunea din amonte, ștergeți-o aici și alegeți „Actualizați acum†de mai sus."
@@ -7288,6 +7342,9 @@ msgstr "Da, ștergeți ramura"
msgid "Branches|Yes, delete protected branch"
msgstr "Da, ștergeți ramura protejată"
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr "Sunteți pe cale să ștergeți definitiv ramura %{branchName}."
@@ -7342,6 +7399,9 @@ msgstr "A apărut o eroare la preluarea artefactelor"
msgid "BuildArtifacts|Loading artifacts"
msgstr "Se încarcă artifactele"
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr "ÃŽncorporat"
@@ -8149,6 +8209,9 @@ msgstr "Pipeline #%{pipeline_id} %{humanized_status} în %{duration}"
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr "Pipeline %{pipeline_link} de %{ref_type} %{ref_link} de %{user_combined_name} %{humanized_status}"
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr "Etichetă"
@@ -8197,6 +8260,9 @@ msgstr "Verificați imaginile dvs. Docker pentru vulnerabilități cunoscute."
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr "Verificați imaginile clusterului dvs. Kubernetes pentru vulnerabilități cunoscute."
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "Verificarea disponibilității %{text}…"
@@ -8320,9 +8386,6 @@ msgstr "Formularul cardului de credit nu s-a încărcat: %{message}"
msgid "Checkout|Edit"
msgstr "Editați"
-msgid "Checkout|Enter a number greater than 0"
-msgstr "Introduceți un număr mai mare de 0"
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr "Exp %{expirationMonth}/%{expirationYear}"
@@ -8338,8 +8401,8 @@ msgstr "Nu s-a reușit încărcarea țărilor. Vă rugăm să încercați din no
msgid "Checkout|Failed to load states. Please try again."
msgstr "Nu s-a reușit încărcarea județelor. Vă rugăm să încercați din nou."
-msgid "Checkout|Failed to load the payment form. Please try again."
-msgstr "Nu s-a reușit încărcarea formularului de plată. Vă rugăm să încercați din nou."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
+msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
msgstr "Nu s-a reușit înregistrarea cardului de credit. Vă rugăm să încercați din nou."
@@ -8362,6 +8425,9 @@ msgstr "Trebuie să fie de cel puÈ›in %{minimumNumberOfUsers} (seat-urile dvs. Ã
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr "Trebuie să fie de minimum %{minimumNumberOfUsers} (seat-urile dvs. în uz, plus toți membrii care depășesc limita) sau mai mult. Pentru a cumpăra mai puține locuri, înlăturați membri din grup."
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr "Numele companiei sau organizației care utilizează GitLab"
@@ -8605,9 +8671,15 @@ msgstr "în execuție"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr "Nu se poate utiliza Variabilă mascată cu valoarea curentă"
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr "Medii"
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr "Cheia variabilei de intrare"
@@ -8620,6 +8692,9 @@ msgstr "Cheie"
msgid "CiVariables|Masked"
msgstr "Mascată"
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr "Opțiuni"
@@ -8632,6 +8707,9 @@ msgstr "Înlăturați variabila"
msgid "CiVariables|Remove variable row"
msgstr "Înlăturați rândul variabil"
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr "Domeniul"
@@ -8641,6 +8719,9 @@ msgstr "Specificați valorile variabilelor care vor fi utilizate în această ru
msgid "CiVariables|State"
msgstr "Stare"
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr "Tipul"
@@ -8650,6 +8731,12 @@ msgstr "Valoare"
msgid "CiVariables|Variables"
msgstr "Variabile"
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr "* (Toate mediile)"
@@ -9343,6 +9430,9 @@ msgstr "Token revocat de %{userName}"
msgid "ClusterAgents|Unknown user"
msgstr "Utilizator necunoscut"
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr "Token de acces valid"
@@ -10165,6 +10255,9 @@ msgstr "Configurarea pipeline-ului de conformitate (opțional)"
msgid "ComplianceFrameworks|Configuration not found"
msgstr "Configurația nu a fost găsită"
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr "Ștergeți framework-ul de conformitate %{framework}"
@@ -10189,6 +10282,9 @@ msgstr "Eroare la preluarea datelor framework-urilor de conformitate. Vă rugăm
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr "Eroare la preluarea datelor framework-urilor de conformitate. Vă rugăm să reîmprospătați pagina sau încercați un alt framework"
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr "Framework-urile care au fost adăugate vor apărea aici."
@@ -10204,15 +10300,24 @@ msgstr "Numele este obligatoriu"
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr "Încă nu sunt configurate framework-uri de conformitate"
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr "Format obligatoriu: %{codeStart}cale/fișier.y[a]ml@nume-grup/nume-proiect%{codeEnd}. %{linkStart}Ce este o configurație pipeline de conformitate?%{linkEnd}"
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr "Nu se poate salva acest framework de conformitate. Vă rugăm să încercați din nou"
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr "Sunteți pe cale să ștergeți definitiv framework-ul de conformitate %{framework} din toate proiectele care îl au aplicat în prezent, ceea ce poate elimina alte funcționalități. Acest lucru nu poate fi anulat."
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr "Adăugați un framework la %{linkStart}%{groupName}%{linkEnd} și acesta va apărea aici."
@@ -10378,8 +10483,8 @@ msgstr "ConfiguraÈ›i limite specifice pentru solicitările API depreciate care Ã
msgid "Configure the %{link} integration."
msgstr "Configurați integrarea %{link}."
-msgid "Configure the default first day of the week and time tracking units."
-msgstr "Configurați prima zi a săptămânii și unitățile de urmărire a timpului implicite."
+msgid "Configure the default first day of the week, time tracking units, and default language."
+msgstr ""
msgid "Configure the way a user creates a new account."
msgstr "Configurați modul în care un utilizator creează un cont nou."
@@ -10498,6 +10603,9 @@ msgstr "Conectarea"
msgid "Connecting to terminal sync service"
msgstr "Se conectează la serviciul de sincronizare a terminalului"
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr "Se conectează..."
@@ -10537,12 +10645,6 @@ msgstr "Imaginile registrului de containere"
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr "Registrul de containere nu este activat pe această instanță GitLab. Solicitați unui administrator să-l activeze pentru ca Auto DevOps să funcționeze."
-msgid "Container repositories"
-msgstr "Repozitorii de containere"
-
-msgid "Container repository"
-msgstr "Repozitoriu de containere"
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr " Vă rugăm să vizitați %{linkStart}setările de administrare%{linkEnd} pentru a activa această funcție."
@@ -10810,9 +10912,6 @@ msgstr "Etichetă marcată cu succes pentru ștergere."
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr "Etichete marcate cu succes pentru ștergere."
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr "Temporar, etichetele nu pot fi marcate pentru ștergere. Vă rugăm să încercați din nou în câteva minute. %{docLinkStart}Mai multe detalii%{docLinkEnd}."
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr "Etichetele care corespund acestor reguli sunt %{strongStart}păstrate%{strongEnd}, chiar dacă se potrivesc unei reguli de eliminare de mai jos. %{secondStrongStart}Cea mai recentă%{secondStrongEnd} etichetă este întotdeauna păstrată."
@@ -10996,9 +11095,6 @@ msgstr "Controlați e-mailurile legate de contul d-voastră"
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr "Controlați cum este utilizată variabila CI_JOB_TOKEN CI/CD pentru accesul API între proiecte."
-msgid "Control how the GitLab Package Registry functions."
-msgstr "Controlați modul în care funcționează Registrul de pachete GitLab."
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr "Controlați dacă doriți să afișați conținutul de îmbunătățire a experienței clienților și ofertele terților în GitLab."
@@ -11311,6 +11407,9 @@ msgstr "Nu s-au putut încărca design-urile, deoarece unul sau mai multe fișie
msgid "Couldn't assign policy to project or group"
msgstr "Nu s-a putut atribui politica unui proiect sau grup"
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr "Å¢ara"
@@ -11443,9 +11542,6 @@ msgstr "Creați problema"
msgid "Create issue to resolve all threads"
msgstr "Creați o problemă pentru a rezolva toate subiectele"
-msgid "Create iteration"
-msgstr "Creați iterația"
-
msgid "Create label"
msgstr "Creează eticheta"
@@ -11524,6 +11620,9 @@ msgstr "Creați un fragment de cod"
msgid "Create tag %{tagName}"
msgstr "Creați eticheta %{tagName}"
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr "Creați un subiect"
@@ -11560,6 +11659,42 @@ msgstr "Nu aveți permisiunea de a crea grupuri."
msgid "CreateTag|Tag"
msgstr "Etichetă"
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr "%{name} (implicit)"
@@ -11785,6 +11920,9 @@ msgstr "Cardul de credit trebuie să fie la dosar pentru a crea o conductă"
msgid "Credit card:"
msgstr "Card de credit:"
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr "Vulnerabilități critice prezente"
@@ -12196,12 +12334,18 @@ msgstr "%{startDate} - %{endDate}"
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr "Media (ultimele %{days}d)"
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr "Rata de eșec a modificărilor"
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr "Rata de eșec a modificării (procentaj)"
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr "Metrici DORA pentru grupul %{groupName}"
@@ -12214,9 +12358,21 @@ msgstr "Zile pentru un incident deschis"
msgid "DORA4Metrics|Days from merge to deploy"
msgstr "Zile de la îmbinare până la desfășurare"
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr "Frecvența de implementare"
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr "Timpul necesar pentru modificări"
@@ -12235,6 +12391,9 @@ msgstr "Timpul mediu în care un incident a fost deschis într-un mediu de produ
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr "Niciun incident în această perioadă"
@@ -12268,6 +12427,9 @@ msgstr "Graficul afișează frecvența implementărilor în mediul/mediile de pr
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 "Graficul afișează timpul mediu dintre îmbinarea și implementarea unui merge request în mediul/mediile de producție care se bazează pe valoarea %{linkStart}deployment_tier%{linkEnd}."
+msgid "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr "Timpul pentru restabilirea serviciului"
@@ -12328,6 +12490,9 @@ msgstr "Nu au fost găsite scanări anterioare pentru acest proiect"
msgid "DastConfig|Not enabled"
msgstr "Neactivat"
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr "O scanare pasivă monitorizează toate mesajele HTTP (solicitări și răspunsuri) trimise către țintă. O scanare activă atacă ținta pentru a găsi vulnerabilități potențiale."
@@ -12475,6 +12640,9 @@ msgstr "Minim = 1 secundă, Maxim = 3600 de secunde"
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr "Monitorizează toate solicitările HTTP trimise către țintă pentru a găsi potențiale vulnerabilități."
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr "Profil nou de scaner"
@@ -12847,6 +13015,9 @@ msgstr "Zile de inactivitate înainte de dezactivare"
msgid "Days to merge"
msgstr "Zile până la îmbinare"
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr "Dezactivați utilizatorii inactivi după o perioadă de inactivitate"
@@ -12904,6 +13075,12 @@ msgstr "Prima zi implicită a săptămânii"
msgid "Default first day of the week in calendars and date pickers."
msgstr "Prima zi implicită a săptămânii în calendare și selectori de date."
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr "Limita implicită a proiectelor"
@@ -13702,6 +13879,9 @@ msgstr "Respins %{time}"
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr "Respins de dvs. %{time}"
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr "GitLab Pages"
@@ -13738,6 +13918,9 @@ msgstr "Backend fără server (Lambda, funcții Cloud)"
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr "Mașină virtuală (de exemplu, EC2)"
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr "Implementări"
@@ -13843,9 +14026,15 @@ msgstr "Descriere analizată cu %{link_start}GitLab Flavored Markdown%{link_end}
msgid "Description:"
msgstr "Descriere:"
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr "Etichetă descriptivă"
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr "Fișierele și datele de Management al designului"
@@ -14011,6 +14200,9 @@ msgstr "Rapoarte DevOps"
msgid "DevOps adoption"
msgstr "Adoptarea DevOps"
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr "Dezvoltator"
@@ -15328,6 +15520,9 @@ msgstr "Introduceți orice culoare."
msgid "Enter at least three characters to search"
msgstr "Introduceți cel puțin trei caractere pentru a căuta"
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr "Introduceți mai jos URL-ul serverului Bitbucket și tokenul personal de acces"
@@ -15691,9 +15886,6 @@ msgstr "Înlăturați epica"
msgid "Epics|Remove issue"
msgstr "Înlăturați problema"
-msgid "Epics|Show more"
-msgstr "Afișați mai multe"
-
msgid "Epics|Something went wrong while creating child epics."
msgstr "Ceva nu a mers bine în timpul creării epicelor copil."
@@ -15787,6 +15979,9 @@ msgstr "Eroare la preluarea datelor payload"
msgid "Error fetching refs"
msgstr "Eroare la preluarea referințelor"
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr "Eroare la preluarea listei de dependențe. Vă rugăm să verificați conexiunea de rețea și să încercați din nou."
@@ -16102,6 +16297,15 @@ msgstr "Această politică nu are reguli de escaladare."
msgid "EscalationPolicies|mins"
msgstr "min."
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr "Estimare"
@@ -16264,6 +16468,9 @@ msgstr "Toată lumea poate contribui"
msgid "Everything on your to-do list is marked as done."
msgstr "Tot ceea ce aveți în lista de sarcini de-făcut sunt marcate ca terminate."
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr "Tot ce aveți nevoie pentru a crea un site GitLab Pages folosind Gatsby"
@@ -16294,9 +16501,21 @@ msgstr "Colectarea probelor"
msgid "Exactly one of %{attributes} is required"
msgstr "Exact unul dintre %{attributes} este necesar"
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr "Exemplu: @sub\\.company\\.com$"
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr "Exemple"
@@ -16315,9 +16534,6 @@ msgstr "Se exclud commit-urile de îmbinare. Limitat la 6000 de commit-uri."
msgid "Execution time"
msgstr "Durata de execuție"
-msgid "Executive Dashboard"
-msgstr "Tablou de bord executiv"
-
msgid "Existing branch name, tag, or commit SHA"
msgstr "Numele ramurii, eticheta sau SHA de commit existente"
@@ -16372,14 +16588,17 @@ msgstr "Extindeți secțiunea de setări"
msgid "Expand sidebar"
msgstr "Extindeți bara laterală"
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr "Documente așteptate: %{expected_documents}"
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
-msgstr "Trebuie să aibă exact una dintre următoarele: Utilizator, Spațiu de nume sau Proiect."
+msgid "Experiment candidates"
+msgstr ""
msgid "Experiments"
msgstr ""
@@ -16696,9 +16915,6 @@ msgstr "Nu s-a putut genera raportul, încercați din nou mai târziu"
msgid "Failed to get ref."
msgstr "Nu s-a reușit obținerea ref."
-msgid "Failed to install."
-msgstr "Nu s-a reușit instalarea."
-
msgid "Failed to load"
msgstr "Nu s-a reușit încărcarea"
@@ -16855,9 +17071,6 @@ msgstr "Nu s-a reușit actualizarea stării problemei"
msgid "Failed to update the Canary Ingress."
msgstr "Nu s-a reușit actualizarea Canary Ingress."
-msgid "Failed to upgrade."
-msgstr "Nu s-a reușit upgrade-ul."
-
msgid "Failed to upload object map file"
msgstr "Nu s-a reușit încărcarea fișierului de mapare a obiectului"
@@ -17113,6 +17326,9 @@ msgstr "Feb"
msgid "February"
msgstr "Februarie"
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17332,15 +17548,6 @@ msgstr "Federated Learning of Cohorts (FLoC)"
msgid "FloC|Participate in FLoC"
msgstr "Participați la FLoC"
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr "Introduceți tokenul Flowdock."
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr "Trimiteți notificări de evenimente din GitLab la fluxurile Flowdock."
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr "Trimiteți notificări de evenimente din GitLab la fluxurile Flowdock. %{docs_link}"
-
msgid "Focus filter bar"
msgstr "Focalizare bară filtru"
@@ -17377,6 +17584,9 @@ msgstr "Activitatea utilizatorilor urmăriți"
msgid "Followed users"
msgstr "Utilizatori urmăriți"
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr "Culoarea fontului"
@@ -17521,12 +17731,21 @@ msgstr "Forking în curs"
msgid "Forks"
msgstr "Forkuri"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
msgid "Format: %{dateFormat}"
msgstr "Format: %{dateFormat}"
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
-msgstr "Redirecționați solicitările de pachete %{package_type} către Registrul %{registry_type} dacă pachetele nu sunt găsite în Registrul de pachete GitLab."
-
msgid "Found errors in your %{gitlab_ci_yml}:"
msgstr "S-au găsit erori în %{gitlab_ci_yml} d-voastră:"
@@ -17605,12 +17824,6 @@ msgstr "De la %{code_open}%{source_title}%{code_close} în"
msgid "From %{providerTitle}"
msgstr "De la %{providerTitle}"
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] "Începând cu 19 octombrie 2022, grupurile private gratuite vor fi limitate la %d membru."
-msgstr[1] "Începând cu 19 octombrie 2022, grupurile private gratuite vor fi limitate la %d membri."
-msgstr[2] "Începând cu 19 octombrie 2022, grupurile private gratuite vor fi limitate la %d de membri."
-
msgid "From issue creation until deploy to production"
msgstr "De la crearea problemei până la implementarea în producție"
@@ -17653,6 +17866,9 @@ msgstr "Pipeline-uri generale"
msgid "General settings"
msgstr "Setări generale"
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr "Generați un set implicit de etichete"
@@ -18502,6 +18718,12 @@ msgstr "Versiunea GitLab"
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr "GitLab va crea o ramură în forkul dvs. și va iniția un merge request."
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "GitLab.com (SaaS)"
msgstr "GitLab.com (SaaS)"
@@ -18751,6 +18973,9 @@ msgstr "Probleme recente"
msgid "GlobalSearch|Recent merge requests"
msgstr "Merge request-uri recente"
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr "Rezultatele s-au actualizat. %{count} (de) rezultate disponibile. Utilizați tastele săgeată sus și jos pentru a naviga în lista de rezultate ale căutării sau tasta ENTER pentru a trimite."
@@ -18778,6 +19003,9 @@ msgstr "Tastați pentru ca noile sugestii să apară mai jos."
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr "Utilizați tasta rapidă %{kbdOpen}/%{kbdClose} pentru a iniția o căutare"
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr "Ce căutați?"
@@ -18955,6 +19183,9 @@ msgstr "Mergeți la merge request-urile dvs."
msgid "Go to your projects"
msgstr "Mergeți la proiectele dvs."
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr "Mergeți la fragmentele dvs. de cod"
@@ -19048,6 +19279,12 @@ msgstr "Acordați permisiuni în scriere pentru această cheie"
msgid "Graph"
msgstr "Grafic"
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr "Dependențe de job"
@@ -19237,8 +19474,8 @@ msgstr "Ultimele 30 de zile"
msgid "GroupActivityMetrics|Members added"
msgstr "Membri adăugați"
-msgid "GroupActivityMetrics|Merge Requests created"
-msgstr "Merge request-uri create"
+msgid "GroupActivityMetrics|Merge requests created"
+msgstr ""
msgid "GroupActivityMetrics|Recent activity"
msgstr "Activitatea recentă"
@@ -19324,6 +19561,9 @@ msgstr "ÃŽn decurs de 3 ani"
msgid "GroupSAML|\"persistent\" recommended"
msgstr "„persistent†este recomandat"
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr "%{strongOpen}Avertisment%{strongClose} - Activați %{linkStart}aplicarea SSO%{linkEnd} pentru a reduce riscurile de securitate."
@@ -19429,6 +19669,9 @@ msgstr "Numele grupului SAML"
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr "Numele grupului SAML: %{saml_group_name}"
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr "Ieșirea răspunsului SAML"
@@ -19600,8 +19843,8 @@ msgstr "Proiectele din %{group} nu pot fi partajate cu alte grupuri"
msgid "GroupSettings|Reporting"
msgstr "Raportare"
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
-msgstr "Selectați un subgrup care să fie utilizat ca sursă pentru șabloanele de proiect personalizate pentru acest grup."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
+msgstr ""
msgid "GroupSettings|Select parent group"
msgstr "Selectați grupul părinte"
@@ -19621,9 +19864,6 @@ msgstr "Setați numele și protecțiile inițiale pentru ramura implicită a noi
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr "Pipeline-ul Auto DevOps se execută dacă nu se găsește niciun fișier de configurare CI alternativ."
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr "Proiectele din acest subgrup pot fi selectate ca șabloane pentru proiecte noi create în grup. %{link_start}Aflați mai multe.%{link_end}"
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr "A apărut o problemă la actualizarea pipeline-ului Auto DevOps: %{error_messages}."
@@ -19753,6 +19993,9 @@ msgstr "Conectați instanța"
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr "Contactați un administrator pentru a activa opțiunile de import ale grupului dumneavoastră."
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr "Creați un grup"
@@ -19762,9 +20005,6 @@ msgstr "Creați un grup nou"
msgid "GroupsNew|Create subgroup"
msgstr "Creați un subgrup"
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr "Creați aceasta în %{pat_link_start}setările de utilizator%{pat_link_end} ale instanței GitLab sursă. Din %{short_living_link_start}motive de securitate%{short_living_link_end}, utilizați o dată de expirare scurtă atunci când creați tokenul."
-
msgid "GroupsNew|GitLab source URL"
msgstr "URL-ul sursei GitLab"
@@ -20242,12 +20482,18 @@ msgstr "Structură indisponibilă"
msgid "Hierarchy|You can start using these items now."
msgstr "Puteți începe să folosiți aceste elemente acum."
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr "Vulnerabilități ridicate sau necunoscute prezente"
msgid "Highest role:"
msgstr "Cel mai înalt rol:"
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr "Evenimente de alertă:"
@@ -20281,9 +20527,6 @@ msgstr "Pagina de start"
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr "Executarea hook-ului a eșuat. Asigurați-vă ca grupul să aibă un proiect cu commit-uri."
-msgid "Hook was successfully updated."
-msgstr "Hook-ul a fost actualizat cu succes."
-
msgid "Horizontal rule"
msgstr "Regula orizontală"
@@ -20452,6 +20695,9 @@ msgstr "Identificatori"
msgid "Identities"
msgstr "Identități"
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr "A fost trimis un nou cod."
@@ -20476,6 +20722,9 @@ msgstr "Creați un proiect"
msgid "IdentityVerification|Didn't receive a code?"
msgstr "Nu ați primit un cod?"
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr "Introduceți codul."
@@ -20515,8 +20764,8 @@ msgstr "Numărul maxim de încercări de autentificare a fost depășit. Aștept
msgid "IdentityVerification|Phone number"
msgstr "Număr de telefon"
-msgid "IdentityVerification|Phone number can't be blank."
-msgstr "Numărul de telefon trebuie completat."
+msgid "IdentityVerification|Phone number is required."
+msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
msgstr "Numărul de telefon trebuie să aibă cel mult %{maxLength} cifre."
@@ -20557,6 +20806,12 @@ msgstr "Codul este incorect. Introduceți-l din nou sau trimiteți-mi un nou cod
msgid "IdentityVerification|Verification code"
msgstr "Cod de verificare"
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr "Verificare reușită"
@@ -20569,9 +20824,18 @@ msgstr "Verificați adresa de e-mail"
msgid "IdentityVerification|Verify payment method"
msgstr "Metodă de verificare prin plată"
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr "Verificați-vă identitatea"
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr "Puteți oricând să vă verificați contul ulterior pentru a crea un grup."
@@ -20920,6 +21184,9 @@ msgstr "Nu există un repozitoriu Git valid la această adresă URL. Dacă depoz
msgid "Improve customer support with Service Desk"
msgstr "Îmbunătățiți asistența pentru clienți cu Service Desk"
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr "În cazul replicării pull, utilizatorul dvs. va fi autorul tuturor evenimentelor din fluxul de activitate care sunt rezultatul unei actualizări, cum ar fi crearea de ramuri noi sau împingerea de noi commit-uri în ramurile existente."
@@ -21487,12 +21754,18 @@ msgstr "Incident"
msgid "Incident Management Limits"
msgstr "Limite de gestionare a incidentelor"
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr "Detalii privind incidentul"
msgid "Incident template (optional)."
msgstr "Șablon de incident (opțional)."
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr "%{hours} ore, %{minutes} minute rămase"
@@ -21811,6 +22084,9 @@ msgstr "Ștergerea indexului este anulată"
msgid "Indicates whether this runner can pick jobs without tags"
msgstr "Indică dacă acest executor poate alege joburi fără etichete"
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr "Informați utilizatorii fără chei SSH încărcate că nu pot face push prin SSH până când nu adaugă una."
@@ -22084,6 +22360,9 @@ msgstr "Activați verificarea SSL"
msgid "Integrations|Enable comments"
msgstr "Activați comentariile"
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr "Verificați dacă URL-ul instanței dvs. este corect și dacă instanța este configurată corect. %{linkStart}Aflați mai multe%{linkEnd}."
@@ -22117,6 +22396,9 @@ msgstr "Administratorii GitLab pot configura integrări pe care toate grupurile
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr "Administratorii GitLab pot configura integrări pe care toate proiectele dintr-un grup le moștenesc și le utilizează în mod implicit. Aceste integrări se aplică tuturor proiectelor care nu utilizează deja setări personalizate. Puteți suprascrie setările personalizate pentru un proiect dacă setările sunt necesare la acel nivel. Aflați mai multe despre %{integrations_link_start}gestionarea integrărilor la nivel de grup%{link_end}."
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr "Managementul integrării la nivel de grup"
@@ -22465,6 +22747,9 @@ msgstr "Invitați-vă colegii"
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr "Am observat că nu ați invitat pe nimeni în acest grup. Invitați-vă colegii pentru a putea discuta probleme, colabora la merge request-uri și pentru a vă împărtăși cunoștințele."
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr "Pentru a avea mai mulți membri, proprietarul acestui spațiu de nume poate %{trialLinkStart}începe o perioadă de probă%{trialLinkEnd} sau poate %{upgradeLinkStart}face upgrade%{upgradeLinkEnd} la un nivel plătit."
@@ -22513,8 +22798,8 @@ msgstr "Gestionați membrii"
msgid "InviteMembersModal|Members were successfully added"
msgstr "Membrii au fost adăugați cu succes"
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
-msgstr "Selectați membrii sau introduceți adresele de e-mail pentru a invita"
+msgid "InviteMembersModal|Please add members to invite"
+msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr "Examinați erorile de invitație și încercați din nou:"
@@ -22981,9 +23266,6 @@ msgstr "S-a produs o eroare în timpul mutării problemelor."
msgid "Issue|Title"
msgstr "Titlu"
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr "Nu este posibilă %{action} fișierelor care sunt stocate în LFS utilizând interfața web."
-
msgid "It looks like you have some draft commits in this branch."
msgstr "Se pare că aveți câteva drafturi de commit-uri în această ramură."
@@ -23122,9 +23404,6 @@ msgstr "Eroare la încărcarea cadențelor de iterație."
msgid "Iterations|Iteration cadences"
msgstr "Cadențe de iterație"
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr "Iterațiile sunt o modalitate de a urmări problemele pe un interval de timp, permițând echipelor să urmărească, de asemenea, velocitatea și volatilitatea metricilor."
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr "Iterațiile sunt programate să înceapă în zilele de %{weekday}."
@@ -23557,9 +23836,6 @@ msgstr "Niciun job de afișat"
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr "Căutarea în text brut nu este acceptată în prezent pentru funcția de căutare filtrată a joburilor. Vă rugăm să utilizați tokenurile de căutare disponibile."
-msgid "Jobs|Status"
-msgstr "Status"
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr "A apărut o problemă la preluarea joburilor eșuate."
@@ -23710,6 +23986,9 @@ msgstr "Acest job este blocat, deoarece proiectul nu are niciun executor online
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr "Acest job este blocat pentru că nu aveți niciun executor activ care să poată rula acest job."
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr "În așteptare de resurse"
@@ -23791,6 +24070,9 @@ msgstr "Cheie"
msgid "Key (PEM)"
msgstr "Cheie (PEM)"
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr "Cheie:"
@@ -23860,9 +24142,6 @@ msgstr "Clusterele Kubernetes"
msgid "Kubernetes deployment not found"
msgstr "Implementarea Kubernetes nu a fost găsită"
-msgid "Kubernetes error: %{error_code}"
-msgstr "Eroare Kubernetes: %{error_code}"
-
msgid "LDAP"
msgstr "LDAP"
@@ -23938,15 +24217,18 @@ msgstr "Etichete"
msgid "Labels"
msgstr "Etichete"
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr "Etichetele pot fi aplicate la %{features}. Etichetele de grup sunt disponibile pentru orice proiect din cadrul grupului."
-
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "Etichetele pot fi aplicate problemelor și merge request-urilor pentru a le clasifica."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr "Etichetele se pot aplica la probleme și la merge request-uri. Marcați cu stea o etichetă pentru a o transforma într-o etichetă prioritară."
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr "Etichete fără probleme în această iterație:"
@@ -24034,6 +24316,9 @@ msgstr "Ultima editare de %{link_start}%{avatar} %{name}%{link_end}"
msgid "Last event"
msgstr "Ultimul eveniment"
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr "Ultima modificare"
@@ -24139,6 +24424,9 @@ msgstr "Aflați mai multe"
msgid "Learn More."
msgstr "Aflați mai multe."
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr "Aflați cum să %{link_start}contribuiți la șabloanele încorporate%{link_end}"
@@ -24820,12 +25108,12 @@ msgstr "Fișiere blocate"
msgid "Locked by %{fileLockUserName}"
msgstr "Blocat de %{fileLockUserName}"
+msgid "Locked files"
+msgstr ""
+
msgid "Locked the discussion."
msgstr "Discuția a fost închisă."
-msgid "Locks give the ability to lock specific file or folder."
-msgstr "Blocările oferă posibilitatea de a bloca un anumit fișier sau un dosar."
-
msgid "Locks the discussion."
msgstr "Închideți discuția."
@@ -24874,6 +25162,9 @@ msgstr "Logoul va fi înlăturat. Sunteți sigur?"
msgid "Logs"
msgstr "Jurnale"
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr "Prezintă vulnerabilități scăzute"
@@ -24979,6 +25270,9 @@ msgstr "Face ca acest/această %{type} să fie confidențial(ă)."
msgid "Manage %{workspace} labels"
msgstr "Gestionați etichetele pentru %{workspace}"
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr "Gestionați funcțiile Web IDE."
@@ -25477,6 +25771,9 @@ msgstr "Timpul mediu de îmbinare"
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr "Numărul de octeți de cod măsurat. Se exclud codurile generate automat și cele furnizate."
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr "Timeout mediu"
@@ -25498,6 +25795,12 @@ msgstr "%{member_name} v-a invitat să vă alăturați la GitLab"
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr "Invitație de aderare la %{project_or_group} %{project_or_group_name}"
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr "Membri"
@@ -26629,6 +26932,9 @@ msgstr "Adăugare proiecte"
msgid "Modal|Close"
msgstr "Închideți"
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr "Modificat"
@@ -26692,9 +26998,6 @@ msgstr "Mai multe informații"
msgid "More information"
msgstr "Mai multe informații"
-msgid "More information and share feedback"
-msgstr "Mai multe informații și împărtășiți feedback-ul"
-
msgid "More information is available|here"
msgstr "Mai multe informații sunt disponibile|aici"
@@ -26905,6 +27208,9 @@ msgstr "Acțiune necesară: Stocarea a fost depășită pentru %{namespace_name}
msgid "NamespaceStorage|Buy more storage"
msgstr "Cumpărați mai mult spațiu de stocare"
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr "Vă recomandăm să cumpărați spațiu de stocare suplimentar pentru a vă asigura că serviciul dvs. nu este întrerupt."
@@ -27085,6 +27391,9 @@ msgstr "Ramură nouă"
msgid "New branch unavailable"
msgstr "Noua ramură nu este disponibilă"
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr "Titlu nou pentru epica confidențială "
@@ -27127,6 +27436,12 @@ msgstr "A fost generat un nou token de acces pentru verificarea de sănătate!"
msgid "New identity"
msgstr "Noua identitate"
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "Problemă nouă"
@@ -27400,9 +27715,6 @@ msgstr "Nu s-au găsit probleme"
msgid "No iteration"
msgstr "Nicio iterație"
-msgid "No iterations to show"
-msgstr "Nicio iterație de afișat"
-
msgid "No job log"
msgstr "Niciun jurnal de job"
@@ -27502,6 +27814,9 @@ msgstr "Niciun rezultat"
msgid "No results found"
msgstr "Nu s-a găsit niciun rezultat"
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr "Niciun executor disponibil"
@@ -28165,9 +28480,24 @@ msgstr "Numărul de fragmente"
msgid "OK"
msgstr "OK"
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr "Obiectul nu există pe server sau nu aveți permisiuni pentru a-l accesa"
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr "Observabilitate"
@@ -28549,6 +28879,9 @@ msgstr "Nu există scanări programate."
msgid "OnDemandScans|Timezone"
msgstr "Fusul orar"
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr "Vizualizați rezultatul"
@@ -28627,9 +28960,6 @@ msgstr "Accesibil numai %{membersPageLinkStart}membrilor proiectului%{membersPag
msgid "Only active projects show up in the search and on the dashboard."
msgstr "Numai proiectele active apar în căutări și în tabloul de bord."
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr "Permiteți oricui să se înregistreze pentru conturi pe instanțele GitLab pe care intenționați să le folosească oricine. Permițând oricui să se înregistreze, face ca instanțele GitLab să fie mai vulnerabile."
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr "Are efect numai atunci când este activată stocarea remote. Setați la 0 pentru nicio limită de mărime."
@@ -28735,9 +29065,6 @@ msgstr "Se deschide într-o fereastră nouă"
msgid "Opens new window"
msgstr "Deschide o fereastră nouă"
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr "Operația a eșuat. Verificați jurnalele pod pentru %{pod_name} pentru mai multe detalii."
-
msgid "Operation not allowed"
msgstr "Operațiunea nu este permisă"
@@ -29023,6 +29350,9 @@ msgstr "Conan"
msgid "PackageRegistry|Conan Command"
msgstr "Comanda Conan"
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr "Copiați conținutul .pypirc"
@@ -29113,6 +29443,9 @@ msgstr "Ștergeți activul pachetului"
msgid "PackageRegistry|Delete package version"
msgstr "Ștergeți versiunea pachetului"
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr "Ștergeți selectat"
@@ -29128,6 +29461,12 @@ msgstr "Ștergerea ultimului activ al pachetului va înlătura versiunea %{versi
msgid "PackageRegistry|Duplicate packages"
msgstr "Pachete duplicate"
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr "Eroare de publicare"
@@ -29152,6 +29491,18 @@ msgstr "Pentru mai multe informații despre registrul NuGet, %{linkStart}consult
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr "Pentru mai multe informații despre registrul PyPi, %{linkStart}consultați documentația%{linkEnd}."
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr "Generic"
@@ -29224,6 +29575,9 @@ msgstr "Comanda NuGet"
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr "Numărul de active duplicate care trebuie păstrate"
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr "Registrul de pachete"
@@ -29239,6 +29593,9 @@ msgstr "Pachetul a fost șters cu succes"
msgid "PackageRegistry|Package formats"
msgstr "Formatele pachetelor"
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] "Pachetul are %{updatesCount} actualizare arhivată"
@@ -29248,6 +29605,9 @@ msgstr[2] "Pachetul are %{updatesCount} de actualizări arhivate"
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr "Pachet actualizat de commit-ul %{link} pe ramura %{branch}, construit de pipeline-ul %{pipeline} și publicat în registru la %{datetime}"
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr "Ștergeți permanent"
@@ -29302,6 +29662,9 @@ msgstr "Afișați comenzile PyPi"
msgid "PackageRegistry|Show Yarn commands"
msgstr "Afișați comenzile Yarn"
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr "Ceva nu a mers bine la ștergerea activului pachetului."
@@ -29350,9 +29713,6 @@ msgstr "Acest pachet NuGet nu are dependențe."
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr "Pentru a vă extinde căutarea, modificați sau eliminați filtrele de mai sus."
-msgid "PackageRegistry|Type"
-msgstr "Tipul"
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr "Nu se pot obține informații despre versiunea pachetului."
@@ -29374,6 +29734,12 @@ msgstr[0] "Sunteți pe punctul de a șterge 1 activ. Această operațiune este i
msgstr[1] "Sunteți pe cale să ștergeți %d active. Această operațiune este ireversibilă."
msgstr[2] "Sunteți pe cale să ștergeți %d de active. Această operațiune este ireversibilă."
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr "Sunteți pe cale să ștergeți versiunea %{version} a %{name} Sunteți sigur?"
@@ -29389,6 +29755,9 @@ msgstr "npm"
msgid "PackageRegistry|published by %{author}"
msgstr "publicat de %{author}"
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr "Pachete și registre"
@@ -29458,6 +29827,9 @@ msgstr "Parametru"
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr "Parametrul „job_id†nu poate depăși lungimea de %{job_id_max_size}"
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr "Părinte"
@@ -29521,6 +29893,15 @@ msgstr "Vă rugăm să introduceți parola pentru a confirma"
msgid "Passwords should be unique and not used for any other sites or services."
msgstr "Parolele ar trebui să fie unice și să nu fie utilizate pentru alte site-uri sau servicii."
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr "necesită cel puțin o literă minusculă"
@@ -29764,6 +30145,30 @@ msgstr ""
msgid "Phone"
msgstr "Telefon"
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr "Alegeți un nume"
@@ -30679,6 +31084,9 @@ msgstr "Vă rugăm să vă verificați e-mailul (%{email}) pentru a confirma că
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr "Înainte de a continua, faceți clic pe linkul din e-mailul de confirmare trimis la %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr "Vă rugăm să completați profilul dvs. cu adresa de e-mail"
@@ -30835,9 +31243,6 @@ msgstr "Vă rugăm să încercați să reîmprospătați pagina. Dacă problema
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr "Vă rugăm să introduceți %{phrase_code} pentru a continua sau să închideți acest mod pentru a anula."
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr "Vă rugăm să folosiți acest formular pentru a raporta administratorului utilizatorii care creează probleme de spam, comentarii sau se comportă necorespunzător."
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr "Vă rugăm să așteptați câteva momente în timp ce încărcăm istoricul fișierului pentru această linie."
@@ -30991,6 +31396,9 @@ msgstr "Alegeți conținutul implicit pe care doriți să-l vedeți în tabloul
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "Alegeți ce conținut doriți să vedeți în pagina de prezentare generală a unui proiect."
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr "Culoare pentru linii adăugate"
@@ -31075,6 +31483,9 @@ msgstr "Tema de evidențiere a sintaxei"
msgid "Preferences|Tab width"
msgstr "Lățimea tab-ului"
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr "Această funcție este experimentală, iar traducerile nu sunt completate încă."
@@ -31087,9 +31498,15 @@ msgstr "Această setare vă permite să personalizați comportamentul aspectului
msgid "Preferences|Time preferences"
msgstr "Preferințele de timp"
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr "Utilizați timpi relativi"
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr "Când introduceți o descriere sau un comentariu într-o casetă, apăsarea tastei %{kbdOpen}Enter%{kbdClose} într-o listă adaugă un nou element mai jos."
@@ -31207,18 +31624,27 @@ msgstr "Problemă cu comanda %{name}: %{message}."
msgid "Proceed"
msgstr "Continuați"
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr "Audiență"
+msgid "ProductAnalytics|New Analytics Widget Title"
+msgstr ""
+
msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr "În prezent, nu există date pentru acest tip de grafic. Vă rugăm să consultați fila „Configurare†dacă nu ați configurat deja instrumentul de analiză a produsului."
-msgid "ProductAnalytics|Widgets content"
-msgstr "Conținut widget-uri"
-
msgid "Productivity"
msgstr "Productivitate"
@@ -31579,6 +32005,12 @@ msgstr "Actualizați numele de utilizator"
msgid "Profiles|Upload new avatar"
msgstr "Încărcați un nou avatar"
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr "Utilizați un e-mail privat - %{email}"
@@ -32113,12 +32545,18 @@ msgstr "Toate subiectele trebuie să fie rezolvate"
msgid "ProjectSettings|Allow"
msgstr "Permiteți"
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr "Afișați întotdeauna butoanele emoji de recompensă cu degetul mare în sus și degetul mare în jos pe probleme, merge request-uri și fragmente de cod."
msgid "ProjectSettings|Analytics"
msgstr "Analize"
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr "Închiderea automată a problemelor referite pe ramura implicită"
@@ -32200,9 +32638,6 @@ msgstr "Fiecare îmbinare creează un commit de îmbinare."
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr "Fiecare proiect poate avea propriul spațiu pentru a-și stoca imaginile Docker"
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr "Fiecare proiect poate avea propriul spațiu de depozitare a pachetelor sale."
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr "Fiecare proiect poate avea propriul său spațiu pentru a-și depozita pachetele. Notă: Registrul de pachete este întotdeauna vizibil atunci când un proiect este public."
@@ -32317,6 +32752,9 @@ msgstr "Îmbinarea este permisă numai atunci când ramura sursă este actualiza
msgid "ProjectSettings|Monitor"
msgstr "Monitorizare"
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr "Nu se creează niciun commit de îmbinare"
@@ -32362,6 +32800,9 @@ msgstr "Vizibilitatea proiectului"
msgid "ProjectSettings|Public"
msgstr "Public"
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr "Versiuni"
@@ -32491,8 +32932,8 @@ msgstr "Utilizatorii pot solicita acces"
msgid "ProjectSettings|View and edit files in this project."
msgstr "Vizualizați și editați fișierele din acest proiect."
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
-msgstr "Vizualizați și editați fișierele din acest proiect. Cei care nu sunt membri ai proiectului au acces doar în citire."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
+msgstr ""
msgid "ProjectSettings|View project analytics."
msgstr "Vizualizați analizele proiectului."
@@ -32572,6 +33013,9 @@ msgstr "Netlify/Plain HTML"
msgid "ProjectTemplates|NodeJS Express"
msgstr "NodeJS Express"
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr "Pages/Gatsby"
@@ -33094,9 +33538,6 @@ msgstr "Ramură protejată"
msgid "Protected Branches"
msgstr "Ramuri protejate"
-msgid "Protected Environment"
-msgstr "Mediu protejat"
-
msgid "Protected Paths: requests"
msgstr "Căi protejate: solicitări"
@@ -33769,6 +34210,9 @@ msgstr[0] "Se reîmprospătează într-o secundă pentru a afișa starea actuali
msgstr[1] "Se reîmprospătează în %d secunde pentru a afișa starea actualizată..."
msgstr[2] "Se reîmprospătează în %d de secunde pentru a afișa starea actualizată..."
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr "Regenerați exportul"
@@ -33967,6 +34411,9 @@ msgstr "Runbook"
msgid "ReleaseAssetLinkType|Runbooks"
msgstr "Runbooks"
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr "Data lansării"
@@ -34306,6 +34753,9 @@ msgstr "Redeschideți acest / această %{quick_action_target}"
msgid "Reopened this %{quick_action_target}."
msgstr "S-a redeschis acest / această %{quick_action_target}."
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr "Redeschide acest / această %{quick_action_target}."
@@ -34360,11 +34810,8 @@ msgstr "Răspunde..."
msgid "Report Finding not found"
msgstr "Raportul de detectare nu a fost găsit"
-msgid "Report abuse"
-msgstr "Raportați un abuz"
-
-msgid "Report abuse to admin"
-msgstr "Raportați abuzul la un administrator"
+msgid "Report abuse to administrator"
+msgstr ""
msgid "Report couldn't be prepared."
msgstr "Raportul nu a putut fi pregătit."
@@ -34708,6 +35155,9 @@ msgstr "Solicitare"
msgid "Request Access"
msgstr "Solicitați acces"
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr "Solicitați una nouă"
@@ -34957,6 +35407,9 @@ msgstr "Limitați proiectele pentru acest executor"
msgid "Restricted shift times are not available for hourly shifts"
msgstr "Orele de tură restricționate nu sunt disponibile pentru turele orare"
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr "Reluare"
@@ -35032,8 +35485,8 @@ msgstr "Revizuiți proiectul țintă înainte de a-l trimite pentru a evita expu
msgid "Review time"
msgstr "Timpul de revizuire"
-msgid "Review time is defined as the time it takes from first comment until merged."
-msgstr "Timpul de revizuire este definit ca fiind timpul necesar de la primul comentariu până la integrare."
+msgid "Review time is the amount of time since the first comment in a merge request."
+msgstr ""
msgid "ReviewApp|Enable Review App"
msgstr "Activați aplicația de analiză"
@@ -35128,6 +35581,9 @@ msgstr "Executați menajul"
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr "Executați sarcini de menaj pentru a optimiza automat repozitoriile Git. Dezactivarea acestei opțiuni va duce la degradarea performanțelor în timp."
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr "Executați joburi manuale sau întârziate"
@@ -35191,8 +35647,8 @@ msgstr "O capacitate de 1 permite o HA caldă prin regenerarea grupului cu Scala
msgid "Runners|A new version is available"
msgstr "O nouă versiune este disponibilă"
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
-msgstr "O sarcină de fundal periodică șterge executorii care nu au contactat GitLab de mai mult de %{elapsedTime}. %{linkStart}Pot vedea câți executori au fost șterși?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgstr ""
msgid "Runners|Active"
msgstr "Activ"
@@ -35218,6 +35674,9 @@ msgstr "Amazon Linux 2 Docker HA cu scalare manuală și programare opțională.
msgid "Runners|An error has occurred fetching instructions"
msgstr "A apărut o eroare la preluarea instrucțiunilor"
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr "Este disponibil un upgrade pentru acest executor"
@@ -35239,6 +35698,9 @@ msgstr "Asociat cu unul sau mai multe proiecte"
msgid "Runners|Available"
msgstr "Disponibil"
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr "Disponibil pentru toate proiectele"
@@ -35323,6 +35785,9 @@ msgstr "Introduceți numărul de secunde. Acest timp de expirare are prioritate
msgid "Runners|Executor"
msgstr "Executor"
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr "Filtrare proiecte"
@@ -35341,6 +35806,9 @@ msgstr "Cum actualizăm executorul GitLab?"
msgid "Runners|IP Address"
msgstr "Adresa IP"
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr "Instalați un executor"
@@ -35380,12 +35848,15 @@ msgstr "Niciodată contactat:"
msgid "Runners|Never expires"
msgstr "Nu expiră niciodată"
-msgid "Runners|New group runners view"
-msgstr "Noua vizualizare a executorilor de grup"
+msgid "Runners|New group runners can be registered"
+msgstr ""
msgid "Runners|New registration token generated!"
msgstr "S-a generat un nou token de înregistrare!"
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr "Nu s-a găsit niciun rezultat"
@@ -35479,6 +35950,9 @@ msgstr "Executor #%{runner_id}"
msgid "Runners|Runner %{name} was deleted"
msgstr "Executorul %{name} a fost șters"
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr "Executorul a fost atribuit proiectului."
@@ -35545,6 +36019,9 @@ msgstr "Executorii sunt agenții care execută joburile CI/CD. Urmați %{linkSta
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr "Executorii sunt agenții care execută sarcinile CI/CD. Pentru a înregistra noi executori, contactați-vă administratorul."
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr "Rulează joburi neetichetate"
@@ -35593,12 +36070,6 @@ msgstr "Etichete"
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr "Etichetele controlează tipul de joburi pe care le poate gestiona un executor. Prin etichetarea unui executor, asigurați ca executorii partajați să se ocupe numai de joburile pentru care sunt echipați."
-msgid "Runners|Take me there!"
-msgstr "Duceți-mă acolo!"
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr "Noua vizualizare vă oferă mai mult spațiu și o mai bună vizibilitate asupra flotei de executori."
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr "Proiectul, grupul sau instanța în care a fost înregistrat executorul. Executorii de instanță sunt întotdeauna deținuți de Administrator."
@@ -35713,6 +36184,9 @@ msgstr "specific"
msgid "Runner|Owner"
msgstr "Proprietar"
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr "Rulează"
@@ -35740,14 +36214,17 @@ msgstr "Autentificare unică SAML"
msgid "SAML single sign-on for %{group_name}"
msgstr "Autentificare unică SAML pentru %{group_name}"
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr "Conectați-vă la GitLab pentru a conecta contul organizației dumneavoastră"
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr "Grupul %{strongOpen}%{group_path}%{strongClose} vă permite să vă conectați folosind autentificarea unică."
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
-msgstr "Pentru a accesa %{strongOpen}%{group_name}%{strongClose}, trebuie să vă autentificați folosind autentificarea unică printr-o pagină de autentificare externă."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
+msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
msgstr "Pentru a permite ca %{strongOpen}%{group_name}%{strongClose} să vă administreze contul GitLab %{strongOpen}%{username}%{strongClose} (%{email}) după ce vă autentificați folosind autentificarea unică, selectați %{strongOpen}Autorizați%{strongClose}."
@@ -35788,6 +36265,9 @@ msgstr "Cheile gazdă SSH nu sunt disponibile pe acest sistem. Vă rugăm să ut
msgid "SSH key"
msgstr "Cheia SSH"
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr "Chei SSH"
@@ -35803,6 +36283,15 @@ msgstr "Cheile SSH cu următoarele amprente au expirat și nu mai pot fi utiliza
msgid "SSH public key"
msgstr "Cheia publică SSH"
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr "Verificare SSL:"
@@ -35869,6 +36358,12 @@ msgstr "%{thenLabelStart}Atunci%{thenLabelEnd} necesită executarea unei scanăr
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr "%{thenLabelStart}Atunci%{thenLabelEnd} Solicitați executarea unei scanări %{scan} cu profilul de site %{siteProfile} și profilul de scaner %{scannerProfile}"
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr "Se execută un pipeline"
@@ -35896,9 +36391,15 @@ msgstr "Selectați profilul scanerului"
msgid "ScanExecutionPolicy|Select site profile"
msgstr "Selectați profilul site-ului"
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr "Profil de site"
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr "agent"
@@ -35911,18 +36412,27 @@ msgstr "în spațiile de nume"
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr "%{ifLabelStart}dacă%{ifLabelEnd} %{scanners} găsește mai mult de %{vulnerabilitiesAllowed} (de) vulnerabilitate/vulnerabilități %{severities} %{vulnerabilityStates} într-un merge request deschis care vizează %{branches}"
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr "%{thenLabelStart}Atunci%{thenLabelEnd} Necesită aprobare de la %{approvalsRequired} dintre următorii aprobatori:"
msgid "ScanResultPolicy|add an approver"
msgstr "adăugați un aprobator"
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr "scanere"
msgid "ScanResultPolicy|severity levels"
msgstr "nivele de severitate"
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr "stări de vulnerabilitate"
@@ -36007,6 +36517,9 @@ msgstr "Căutare"
msgid "Search GitLab"
msgstr "Căutați în GitLab"
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr "Căutați un grup"
@@ -36535,6 +37048,9 @@ msgstr "Sunteți sigur că doriți să ștergeți această politică? Această a
msgid "SecurityOrchestration|Choose a project"
msgstr "Alegeți un proiect"
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr "Creați reguli de vulnerabilitate mai solide și aplicați-le tuturor proiectelor dumneavoastră."
@@ -36592,9 +37108,18 @@ msgstr "Nu s-a reușit încărcarea imaginilor."
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr "Nu s-au putut încărca scanerele de vulnerabilitate."
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr "Dacă un scaner găsește o vulnerabilitate critică nou detectată într-un merge request deschis care vizează ramura principală, atunci solicitați două aprobări din partea oricărui membru al securității aplicației."
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr "Moștenit"
@@ -36610,6 +37135,9 @@ msgstr "Tip de politică invalid"
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr "Cea mai recentă scanare executată împotriva %{agent}"
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr "Politică nouă"
@@ -36646,6 +37174,9 @@ msgstr "Politica nu poate fi activată pentru ramuri inexistente (%{branches})"
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr "Politica nu poate fi activată fără informații despre ramură"
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr "Definiția politicii"
@@ -36718,6 +37249,9 @@ msgstr "Scanare care trebuie efectuată pe fiecare pipeline de pe %{branches}"
msgid "SecurityOrchestration|Security Approvals"
msgstr "Aprobări de securitate"
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr "Proiectul politicii de securitate a fost conectat cu succes"
@@ -36727,12 +37261,21 @@ msgstr "Proiectul politicii de securitate a fost deconectat cu succes"
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr "Selectați un proiect în care să stocați politicile de securitate. %{linkStart}Mai multe informații.%{linkEnd}"
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr "Selectați politica"
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr "Selectați proiectul de securitate"
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr "Ceva nu a mers bine, nu se pot prelua politicile"
@@ -36802,6 +37345,9 @@ msgstr "Actualizați politicile de scanare"
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr "Utilizați o politică de rezultate ale scanării pentru a crea reguli care asigură verificarea problemelor de securitate înainte de îmbinarea unui merge request."
@@ -36829,6 +37375,9 @@ msgstr "ramură"
msgid "SecurityOrchestration|branches"
msgstr "ramuri"
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr "scaner găsește"
@@ -36865,6 +37414,9 @@ msgstr "%{firstProject} și %{secondProject}"
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr "%{firstProject}, %{secondProject}, și %{rest}"
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr "Adăugați sau eliminați proiecte de monitorizat în zona de securitate. Proiectele incluse în această listă vor avea rezultatele lor afișate în tabloul de bord de securitate și în raportul de vulnerabilitate."
@@ -36874,9 +37426,24 @@ msgstr "Adăugare proiecte"
msgid "SecurityReports|All activity"
msgstr "Toată activitatea"
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr "Deși este rar să nu existe vulnerabilități, acest lucru se poate întâmpla. Verificați setările pentru a vă asigura că v-ați configurat corect tabloul de bord."
@@ -37021,6 +37588,9 @@ msgstr "Nu s-au găsit vulnerabilități"
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr "Nu s-au găsit vulnerabilități pentru acest pipeline"
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr "Oops, ceva nu pare să fie în regulă."
@@ -37246,6 +37816,9 @@ msgstr "Selectați un framework de conformitate care să se aplice acestui proie
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr "Selectați un fișier din bara laterală din stânga pentru a începe editarea. Ulterior, veți putea să comiteți modificările."
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr "Selectați o etichetă"
@@ -37351,6 +37924,9 @@ msgstr "Selectați raportul"
msgid "Select reviewer(s)"
msgstr "Selectare recenzent(i)"
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr "Selectați sursa"
@@ -37840,6 +38416,9 @@ msgstr "Partajați %{strong_open}URL-ul de autentificare unică GitLab%{strong_c
msgid "Shared Runners"
msgstr "Executori partajați"
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr "Proiecte partajate"
@@ -37966,6 +38545,9 @@ msgstr "Afișați ultima versiune"
msgid "Show list"
msgstr "Afișați lista"
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr "Afișați câte un fișier odată"
@@ -38125,9 +38707,6 @@ msgstr "Conectați-vă ca utilizator cu adresa de e-mail potrivită, adăugați
msgid "Sign in preview"
msgstr "Previzualizare autentificare"
-msgid "Sign in to %{group_name}"
-msgstr "Conectați-vă la %{group_name}"
-
msgid "Sign in to GitLab"
msgstr "Conectați-vă la GitLab"
@@ -38281,8 +38860,8 @@ msgstr "Integrarea Slack vă permite să interacționați cu GitLab prin comenzi
msgid "Slack logo"
msgstr "Logoul Slack"
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
-msgstr "Sunteți sigur că doriți să înlăturați acest proiect din aplicația Slack?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
+msgstr ""
msgid "SlackIntegration|Client ID"
msgstr "ID client"
@@ -38296,14 +38875,17 @@ msgstr "GitLab pentru Slack"
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr "GitLab pentru Slack a fost instalat cu succes."
-msgid "SlackIntegration|Install Slack app"
-msgstr "Instalați aplicația Slack"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
+msgstr ""
msgid "SlackIntegration|Project alias"
msgstr "Aliasul proiectului"
-msgid "SlackIntegration|Reinstall Slack app"
-msgstr "Reinstalați aplicația Slack"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
+msgstr ""
msgid "SlackIntegration|Remove project"
msgstr "Înlăturați proiectul"
@@ -38326,14 +38908,17 @@ msgstr "Numele echipei"
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr "Această integrare permite utilizatorilor să efectueze operații comune pe acest proiect prin introducerea de comenzi slash (/) în Slack."
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr "Token de verificare"
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr "Acum puteți închide această fereastră și puteți merge la spațiul de lucru Slack."
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
-msgstr "S-ar putea să fie nevoie să reinstalați aplicația Slack atunci când %{linkStart}facem actualizări sau modificăm permisiunile%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
msgstr "1. %{slash_command_link_start}Adăugați o comandă slash%{slash_command_link_end} în echipa Slack folosind aceste informații:"
@@ -38470,12 +39055,12 @@ msgstr "Unele epice copil pot fi ascunse din cauza filtrelor aplicate"
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr "Unele domenii comune nu sunt permise. %{learn_more_link}."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
+msgstr ""
+
msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr "Cineva a editat fișierul în același timp cu dumneavoastră. Vă rugăm să verificați %{link_start}fișierul %{icon}%{link_end} și să vă asigurați că modificările dvs. nu vor elimina neintenționat modificările lor."
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
-msgstr "Cineva a editat problema în același timp cu dvs. Vă rugăm să verificați %{linkStart}problema%{linkEnd} și să vă asigurați că modificările dvs. nu le vor elimina neintenționat pe ale lor."
-
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr "Cineva a editat acest %{issueType} în același timp. Descrierea a fost actualizată și va trebui să faceți modificările din nou."
@@ -38515,6 +39100,9 @@ msgstr "S-a produs o eroare în timpul încercării de încărcare a contactelor
msgid "Something went wrong when reordering designs. Please try again"
msgstr "Ceva nu a mers bine atunci când am reordonat design-urile. Vă rugăm să încercați din nou"
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr "Ceva nu a mers bine la adăugarea evenimentului cronologic."
@@ -38533,6 +39121,9 @@ msgstr "Ceva nu a mers bine la arhivarea unei cerințe."
msgid "Something went wrong while closing the epic. Please try again later."
msgstr "Ceva nu a mers bine în timpul închiderii epicei. Vă rugăm să încercați din nou mai târziu."
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr "Ceva nu a mers bine la închiderea merge request-ului. Vă rugăm să încercați din nou mai târziu."
@@ -38602,6 +39193,9 @@ msgstr "Ceva nu a mers bine în timpul preluării listei de pachete."
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr "Ceva nu a mers bine la obținerea certificatului Let's Encrypt."
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr "Ceva nu a mers bine în timpul promovării problemei într-o epică. Vă rugăm să încercați din nou."
@@ -38995,6 +39589,9 @@ msgstr "Accelerați-vă pipeline-urile cu relațiile Needs"
msgid "Spent at"
msgstr "Petrecut la"
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr "Mesajul commit-ului de squash"
@@ -39100,6 +39697,9 @@ msgstr "Porniți merge train-ul când conducta reușește"
msgid "Start merge train..."
msgstr "ÃŽncepe merge train-ul..."
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr "Începeți căutarea"
@@ -39163,6 +39763,9 @@ msgstr "Statistici"
msgid "Status"
msgstr "Stare"
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr "Starea a fost reîncercată."
@@ -39901,6 +40504,9 @@ msgstr "Nu vă mai puteți sincroniza detaliile abonamentului cu GitLab. Obține
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr "Puteți începe o perioadă de încercare gratuită a GitLab Ultimate fără nicio obligație sau detalii de plată."
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr "Nu aveți un abonament activ"
@@ -39991,9 +40597,6 @@ msgstr "Schimbați branșa"
msgid "Switch branch/tag"
msgstr "Schimbați ramura/eticheta"
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr "Treceți la GitLab Next"
@@ -40276,6 +40879,12 @@ msgstr "Creați/importați probleme (tichete) pentru a colabora la idei și a pl
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr "Configurați pipeline-uri CI/CD pentru a construi, testa, distribui și monitoriza codul"
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr "Echipa"
@@ -40861,6 +41470,9 @@ msgstr "Următorul token de acces personal a fost revocat de un administrator, %
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr "Următoarea cheie SSH a fost ștearsă de un administrator, %{username}."
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr "Următoarele elemente NU vor fi exportate:"
@@ -41227,9 +41839,6 @@ msgstr "Nu există jurnale de spam"
msgid "There are no abuse reports!"
msgstr "Nu există rapoarte de abuz!"
-msgid "There are no archived projects yet"
-msgstr "Nu există încă proiecte arhivate"
-
msgid "There are no archived requirements"
msgstr "Nu există cerințe arhivate"
@@ -41287,9 +41896,6 @@ msgstr "Nu există cazuri de testare deschise"
msgid "There are no packages yet"
msgstr "Nu există încă pachete"
-msgid "There are no projects shared with this group yet"
-msgstr "Nu există încă proiecte partajate cu acest grup"
-
msgid "There are no secure files yet."
msgstr "Nu există încă fișiere securizate."
@@ -41335,6 +41941,9 @@ msgstr "Există prea multe date de calculat. Vă rugăm să schimbați selecția
msgid "There was a problem communicating with your device."
msgstr "A existat o problemă de comunicare cu dispozitivul dumneavoastră."
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr "A apărut o problemă la preluarea contactelor CRM."
@@ -41455,6 +42064,9 @@ msgstr "A apărut o eroare la preluarea înghețurilor de implementare."
msgid "There was an error fetching the environments information."
msgstr "A apărut o eroare la preluarea informațiilor despre medii."
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr "A apărut o eroare la preluarea joburilor pentru proiectul dvs."
@@ -41494,6 +42106,9 @@ msgstr "A apărut o eroare la resetarea minutelor de pipeline ale utilizatorului
msgid "There was an error retrieving the Jira users."
msgstr "A apărut o eroare la recuperarea utilizatorilor Jira."
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr "A apărut o eroare la salvarea modificărilor."
@@ -41587,9 +42202,6 @@ msgstr "Acest %{viewer} nu a putut fi afișat deoarece %{reason}. Puteți %{opti
msgid "This Cron pattern is invalid"
msgstr "Acest model Cron nu este valid"
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr "Această instanță GitLab nu oferă încă niciun executor partajat. Administratorii instanței pot înregistra executorii partajați în zona admin."
@@ -41779,6 +42391,9 @@ msgstr "Această epică nu există sau nu aveți permisiunea suficientă."
msgid "This epic would exceed maximum number of related epics."
msgstr "Această epică ar depăși numărul maxim de epice conexe."
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr "Această caracteristică necesită ca stocarea locală să fie activată"
@@ -41797,8 +42412,8 @@ msgstr "Acest formular este dezactivat în previzualizare"
msgid "This group"
msgstr "Acest grup"
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
-msgstr "Acest grup împreună cu subgrupurile și proiectele sale vor fi plasate în starea „în așteptare de ștergere†timp de %{deletion_adjourned_period} zile, apoi vor fi șterse definitiv la %{date}. Grupul poate fi restaurat complet înainte de această dată."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
msgstr "Acest grup nu poate fi înlăturat deoarece este legat de un abonament. Pentru a înlătura acest grup, %{linkStart}conectați abonamentul%{linkEnd} cu un alt grup."
@@ -42271,6 +42886,12 @@ msgstr "Timp rămas"
msgid "Time spent"
msgstr "Timp petrecut"
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr "Timpul pentru restabilirea serviciului"
@@ -42523,6 +43144,9 @@ msgstr "Titlu (obligatoriu)"
msgid "Title:"
msgstr "Titlu:"
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr "Titluri și descrieri"
@@ -42556,9 +43180,6 @@ msgstr "Pentru a vă activa perioada de încercare, avem nevoie de detalii supli
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr "Pentru a adăuga un sufix personalizat, configurați o adresă de e-mail Service Desk. %{linkStart}Aflați mai multe.%{linkEnd}"
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr "Pentru a adăuga un nume de afișare, setați o adresă de e-mail Service Desk. %{linkStart}Aflați mai multe.%{linkEnd}"
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr "Pentru a adăuga manual intrarea, furnizați următoarele detalii aplicației de pe telefon."
@@ -42673,6 +43294,9 @@ msgstr "Pentru a vă reactiva contul, %{gitlab_link_start}conectați-vă la GitL
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr "Pentru a vă reactiva contul, conectați-vă la GitLab la %{gitlab_url}."
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42766,6 +43390,9 @@ msgstr "Nu s-a putut îmbina"
msgid "Todos|Design"
msgstr "Design"
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr "Epică"
@@ -42799,6 +43426,9 @@ msgstr "În acest fel știți totdeauna la ce să lucrați în continuare."
msgid "Todos|Mark all as done"
msgstr "Marcați totul ca terminat"
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr "Menționate"
@@ -42814,14 +43444,14 @@ msgstr "Nu mai e nimic de făcut. Bate palma!"
msgid "Todos|Pipelines"
msgstr "Pipeline-uri"
-msgid "Todos|Removed from Merge Train:"
-msgstr "Înlăturat din Merge Train:"
+msgid "Todos|Removed from Merge Train"
+msgstr ""
msgid "Todos|Review requested"
msgstr "Solicitări de revizuire"
-msgid "Todos|The pipeline failed in"
-msgstr "Pipeline-ul a eșuat în"
+msgid "Todos|The pipeline failed"
+msgstr ""
msgid "Todos|Undo mark all as done"
msgstr "Anulați marcarea tuturor ca fiind făcute"
@@ -42835,24 +43465,24 @@ msgstr "Ați terminat totul!"
msgid "Todos|Your To-Do List shows what to work on next"
msgstr "Lista de sarcini De-Făcut arată la ce să lucrați în continuare"
-msgid "Todos|added a todo for"
-msgstr "a adăugat o sarcină de făcut pentru"
+msgid "Todos|added a to-do item"
+msgstr ""
-msgid "Todos|mentioned %{who} on"
-msgstr "a menționat pe %{who} pe"
+msgid "Todos|has requested access"
+msgstr ""
-msgid "Todos|requested a review of"
-msgstr "a solicitat o revizuire a"
+msgid "Todos|mentioned %{who}"
+msgstr ""
-msgid "Todos|set %{who} as an approver for"
-msgstr "setați %{who} ca aprobator pentru"
+msgid "Todos|requested a review"
+msgstr ""
+
+msgid "Todos|set %{who} as an approver"
+msgstr ""
msgid "Todos|yourself"
msgstr "înșivă"
-msgid "Todo|at %{todo_parent_path}"
-msgstr "la %{todo_parent_path}"
-
msgid "Toggle GitLab Next"
msgstr "Comutați la GitLab Next"
@@ -43222,6 +43852,12 @@ msgstr "Declanșatorul a fost actualizat cu succes."
msgid "Triggerer"
msgstr "Declanșator"
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr "Utilizatorul declanșator nu are permisiuni suficiente pentru proiect"
@@ -43258,6 +43894,9 @@ msgstr "Încercați să vă conectați utilizând numele de utilizator sau e-mai
msgid "Try out GitLab Pipelines"
msgstr "Încercați pipeline-urile GitLab"
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr "Încercați etapele de depanare de aici."
@@ -43435,6 +44074,12 @@ msgstr "Nu se poate prelua lista ramurilor acestui proiect."
msgid "Unable to fetch branches list, please close the form and try again"
msgstr "Nu s-a putut prelua lista ramurilor, vă rugăm să închideți formularul și să încercați din nou"
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr "Imposibil de preluat pipeline-urile din amonte și din aval."
@@ -43558,6 +44203,9 @@ msgstr "Din păcate, mesajul dvs. de e-mail către GitLab nu a putut fi procesat
msgid "Unhappy?"
msgstr "Nemulțumit(ă)?"
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr "ms"
@@ -43639,6 +44287,9 @@ msgstr "Nerezolvat"
msgid "Unschedule job"
msgstr "Deprogramați jobul"
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr "Anulare stea"
@@ -43822,6 +44473,9 @@ msgstr "Încărcarea modificărilor în terminal"
msgid "Uploading..."
msgstr "Se încarcă..."
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr "ÃŽn amonte"
@@ -43840,18 +44494,12 @@ msgstr "Statisticile de stocare la nivel de proiect pentru registrul de containe
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr "Această statistică de stocare la nivel de proiect nu include economiile pentru deduplicarea la nivel de site și nu este utilizată pentru a calcula stocarea totală a spațiului de nume."
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr "%{help_link_start}Executorii partajați%{help_link_end} sunt dezactivați, așa că nu există limite stabilite pentru utilizarea de pipeline-uri."
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr "%{linkStart}Executorii partajați%{linkEnd} sunt dezactivați, așa că nu există limite stabilite pentru utilizarea pipeline-ului."
msgid "UsageQuota|%{linkTitle} help link"
msgstr "%{linkTitle} link de ajutor"
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr "%{percentageLeft} din stocarea achiziționată este disponibilă"
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr "%{storage_limit_link_start}O limită de stocare a spațiului de nume%{link_end} va fi în curând impusă pentru spațiul de nume %{strong_start}%{namespace_name}%{strong_end}. %{extra_message}"
@@ -43879,9 +44527,6 @@ msgstr "Utilizarea de minute CI pe proiect"
msgid "UsageQuota|CI/CD minutes usage"
msgstr "Utilizarea minutelor CI/CD"
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr "Utilizarea minutelor CI/CD începând cu %{timeElapsed}"
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr "Minute CI/CD utilizate de la %{usageSince}"
@@ -43891,9 +44536,6 @@ msgstr "Pachete de cod și imagini de containere."
msgid "UsageQuota|Container Registry"
msgstr "Registrul de containere"
-msgid "UsageQuota|Current period usage"
-msgstr "Utilizarea în perioada curentă"
-
msgid "UsageQuota|Dependency proxy"
msgstr "Proxy de dependență"
@@ -44020,11 +44662,8 @@ msgstr "Spațiul de nume utilizează în prezent %{strong_start}%{used_storage}%
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr "Spațiul de nume utilizează în prezent %{strong_start}%{used_storage}%{strong_end} de spațiul de stocare al spațiului de nume. Vizualizați și gestionați utilizarea din %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Aflați mai multe%{link_end} despre cum să reduceți spațiul de stocare"
-msgid "UsageQuota|The table below shows current period usage"
-msgstr "Tabelul de mai jos prezintă utilizarea din perioada curentă"
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
-msgstr "Tabelul de mai jos arată utilizarea de la %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
+msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
msgstr "Aceasta este stocarea totală utilizată de toate proiectele dvs. din acest spațiu de nume."
@@ -44032,12 +44671,6 @@ msgstr "Aceasta este stocarea totală utilizată de toate proiectele dvs. din ac
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr "Aceasta este cantitatea totală de spațiu de stocare utilizată de proiecte peste limita de stocare gratuită de %{actualRepositorySizeLimit}."
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr "Acest spațiu de nume conține proiecte blocate"
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr "Acest spațiu de nume nu are niciun proiect care să folosească executori partajați"
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr "Acest spațiu de nume nu are proiecte care au folosit executorii partajați în perioada curentă."
@@ -44050,9 +44683,6 @@ msgstr "Totalul stocării excesive utilizate"
msgid "UsageQuota|Total namespace storage used"
msgstr "Stocarea totală a spațiului de nume utilizată"
-msgid "UsageQuota|Unlimited"
-msgstr "Nelimitat"
-
msgid "UsageQuota|Uploads"
msgstr "Încărcări"
@@ -44086,8 +44716,8 @@ msgstr "Link de ajutor pentru cotele de utilizare"
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr "Setări utilizator &gt; Cote de utilizare"
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
-msgstr "Atunci când achiziționați spațiu de stocare suplimentar, deblocăm automat proiectele care au fost blocate atunci când ați atins limita de %{actualRepositorySizeLimit}."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
+msgstr ""
msgid "UsageQuota|Wiki"
msgstr "Wiki"
@@ -44095,17 +44725,17 @@ msgstr "Wiki"
msgid "UsageQuota|Wiki content."
msgstr "Conținutul Wiki."
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
-msgstr "Ați consumat tot spațiul de stocare suplimentar, vă rugăm să cumpărați mai mult pentru a debloca proiectele dvs. peste limita gratuită de %{actualRepositorySizeLimit}."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
+msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
-msgstr "Ați atins limita de stocare gratuită de %{actualRepositorySizeLimit} pe %{projectsLockedText}. Pentru a le debloca, vă rugăm să achiziționați spațiu de stocare suplimentar."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
+msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr "Ați folosit: %{usage} %{limit}"
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
-msgstr "Spațiul de stocare achiziționat se epuizează. Pentru a evita proiectele blocate, vă rugăm să achiziționați mai mult spațiu de stocare."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
+msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
msgstr "din %{formattedLimit} din spațiul de stocare al spațiului dvs. de nume"
@@ -44278,6 +44908,9 @@ msgstr "Folosiți URL-ul instanței cloud publice (%{kroki_public_url}) sau %{in
msgid "Use the search bar on the top of this page"
msgstr "Utilizați bara de căutare din partea de sus a acestei pagini"
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr "Utilizați acest token pentru a valida payload-urile primite."
@@ -44545,9 +45178,6 @@ msgstr "Proiecte personale"
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr "Pronunțat ca: %{pronunciation}"
-msgid "UserProfile|Report abuse"
-msgstr "Raportați un abuz"
-
msgid "UserProfile|Retry"
msgstr "Încercați din nou"
@@ -44737,6 +45367,9 @@ msgstr "Utilizarea strategiei de criptare necesară atunci când câmpul criptat
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr "Utilizând cuvântul cheie %{codeStart}needs%{codeEnd}, joburile unei etape ulterioare se execută înainte de finalizarea completă a etapei actuale. Joburile rulează imediat ce sunt îndeplinite relațiile %{codeStart}needs%{codeEnd}, ceea ce accelerează pipeline-urile."
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr "Valabil de la"
@@ -44809,6 +45442,9 @@ msgstr "Metrici DORA"
msgid "ValueStreamAnalytics|Dashboard"
msgstr "Tablou de bord"
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr "Mergeți la documente"
@@ -44827,6 +45463,9 @@ msgstr "Timpul mediu de la crearea problemei până la închiderea acesteia."
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr "Timpul mediu de la primul commit al merge request-ului unei probleme legate până la momentul în care problema respectivă este închisă."
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr "Numărul de commit-uri împinse în ramura implicită"
@@ -44875,12 +45514,12 @@ msgstr "Stop"
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr "Fluxul de valori implicit nu poate fi șters"
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
-msgstr "Valorile care conțin caracterul %{codeStart}$%{codeEnd} pot fi considerate o referință la o variabilă și pot fi extinse.%{docsLinkStart}Aflați mai multe.%{docsLinkEnd}"
-
msgid "Variable"
msgstr "Variabilă"
+msgid "Variable value will be evaluated as raw string."
+msgstr ""
+
msgid "Variable will be masked in job logs."
msgstr "Variabila va fi mascată în jurnalele de joburi."
@@ -44890,8 +45529,8 @@ msgstr "Variabile"
msgid "Variables can be:"
msgstr "Variabilele pot fi:"
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
-msgstr "Variabilele stochează informații, cum ar fi parolele și cheile secrete, pe care le puteți utiliza în scripturile jobului."
+msgid "Variables can have several attributes."
+msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
msgstr "Variabilele stochează informații, cum ar fi parolele și cheile secrete, pe care le puteți utiliza în scripturile de job. Toate proiectele din instanță pot utiliza aceste variabile."
@@ -44947,6 +45586,21 @@ msgstr "Versiunea %{versionNumber}"
msgid "Version %{versionNumber} (latest)"
msgstr "Versiunea %{versionNumber} (cea mai recentă)"
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr "La zi"
@@ -44956,6 +45610,18 @@ msgstr "Actualizați ASAP"
msgid "VersionCheck|Update available"
msgstr "Actualizare disponibilă"
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr "Versiunea dvs. GitLab"
@@ -45619,6 +46285,9 @@ msgstr "Am detectat un spam potențial în %{humanized_resource_name}. Vă rugă
msgid "We don't have enough data to show this stage."
msgstr "Nu avem suficiente date pentru a afișa această etapă."
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr "Am găsit următoarele erori:"
@@ -45694,9 +46363,6 @@ msgstr "Dispozitive WebAuthn (%{length})"
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr "WebAuthn funcționează numai cu site-uri web compatibile HTTPS. Contactați administratorul dumneavoastră pentru mai multe detalii."
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr "Creați fork de proiect"
@@ -45712,24 +46378,12 @@ msgstr "Editați cu rapiditate și ușurință mai multe fișiere din proiect."
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr "Editați cu rapiditate și ușurință mai multe fișiere din proiect. Apăsați . pentru a deschide"
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr "Acest proiect nu acceptă commit-uri nesemnate."
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr "Acest proiect nu acceptă commit-uri nesemnate. Nu puteți comite modificări prin intermediul Web IDE."
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr "Nu puteți edita fișiere direct în acest proiect. Creați un fork pentru acest proiect și trimiteți un merge request cu modificările dumneavoastră."
@@ -45760,6 +46414,18 @@ msgstr "Setări Webhook"
msgid "Webhook events will be displayed here."
msgstr "Evenimentele Webhook vor fi afișate aici."
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr "Webhook:"
@@ -45826,9 +46492,6 @@ msgstr "Sunteți sigur că doriți să ștergeți acest hook de proiect?"
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr "Sunteți sigur că doriți să ștergeți acest webhook?"
-msgid "Webhooks|Comments"
-msgstr "Comentarii"
-
msgid "Webhooks|Confidential comments"
msgstr "Comentarii confidențiale"
@@ -45877,15 +46540,12 @@ msgstr "Evenimente de membri"
msgid "Webhooks|Merge request events"
msgstr "Evenimente de merge request"
+msgid "Webhooks|Must match part of URL"
+msgstr ""
+
msgid "Webhooks|Pipeline events"
msgstr "Evenimente de pipeline"
-msgid "Webhooks|Push events"
-msgstr "Evenimente de push"
-
-msgid "Webhooks|Push to the repository."
-msgstr "Push către repozitoriu."
-
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
msgstr "Regex precum %{REGEX_CODE} este acceptat."
@@ -45922,9 +46582,6 @@ msgstr "Webhook-ul nu a reușit să se conecteze și este dezactivat. Pentru a-l
msgid "Webhooks|Trigger"
msgstr "Declanșator"
-msgid "Webhooks|URL"
-msgstr "URL"
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr "URL-ul trebuie să fie codificat-procentual atunci când conține unul sau mai multe caractere speciale."
@@ -46318,6 +46975,9 @@ msgstr "Va fi creat(ă)"
msgid "Will be mapped to"
msgstr "Va fi mapat la"
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr "Se va desfășura la"
@@ -46327,9 +46987,6 @@ msgstr "Structură de fire"
msgid "With requirements, you can set criteria to check your products against."
msgstr "Cu ajutorul cerințelor, puteți stabili criterii de verificare a produselor dumneavoastră."
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr "Cu ajutorul cazurilor de testare, puteți defini condițiile pe care trebuie să le îndeplinească proiectul dumneavoastră pentru a determina calitatea."
-
msgid "Withdraw Access Request"
msgstr "Retrageți solicitarea de acces"
@@ -46348,6 +47005,12 @@ msgstr "%{workItemType}șters"
msgid "WorkItem|Add"
msgstr "Adăugați"
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr "Adăugați un titlu"
@@ -46363,9 +47026,6 @@ msgstr "Adăugați data de scadență"
msgid "WorkItem|Add start date"
msgstr "Adăugați data de început"
-msgid "WorkItem|Add task"
-msgstr "Adăugați sarcina"
-
msgid "WorkItem|Add to iteration"
msgstr "Adăugați la iterație"
@@ -46387,6 +47047,9 @@ msgstr[2] "Responsabili"
msgid "WorkItem|Cancel"
msgstr "Anulare"
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr "Copilul a fost înlăturat"
@@ -46396,6 +47059,12 @@ msgstr "ÃŽnchis"
msgid "WorkItem|Collapse tasks"
msgstr "Restrângeți sarcinile"
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr "Creați o sarcină"
@@ -46408,30 +47077,39 @@ msgstr "Date"
msgid "WorkItem|Delete %{workItemType}"
msgstr "Ștergeți %{workItemType}"
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr "Data scadenței"
+msgid "WorkItem|Existing task"
+msgstr ""
+
msgid "WorkItem|Expand tasks"
msgstr "Extindeți sarcinile"
msgid "WorkItem|Incident"
msgstr "Incident"
-msgid "WorkItem|Introducing tasks"
-msgstr "Introducerea sarcinilor"
-
msgid "WorkItem|Issue"
msgstr "Problemă"
msgid "WorkItem|Iteration"
msgstr "Iterație"
-msgid "WorkItem|Learn about tasks."
-msgstr "Învățați despre sarcini."
+msgid "WorkItem|Key result"
+msgstr ""
msgid "WorkItem|Milestone"
msgstr "Obiectiv"
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr "Nicio iterație"
@@ -46441,12 +47119,18 @@ msgstr "Nicio potrivire de rezultate"
msgid "WorkItem|No milestone"
msgstr "Niciun obiectiv"
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr "În prezent, nu sunt alocate sarcini. Utilizați sarcinile pentru a împărți această problemă în părți mai mici."
msgid "WorkItem|None"
msgstr "Niciunul"
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr "Numai membrii proiectului cu cel puțin rolul de Reporter, autorul și responsabilii pot vizualiza sau fi notificați cu privire la această sarcină."
@@ -46459,9 +47143,18 @@ msgstr "Înlăturați"
msgid "WorkItem|Requirements"
msgstr "Cerințe"
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr "Selectați tipul"
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr "Ceva nu a mers bine la crearea %{workItemType}. Vă rugăm să încercați din nou."
@@ -46525,8 +47218,8 @@ msgstr "Porniți confidențialitatea"
msgid "WorkItem|Undo"
msgstr "Anulare"
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
-msgstr "Utilizați sarcinile pentru a împărți munca la o problemă în părți mai mici. %{learnMoreLink}"
+msgid "WorkItem|View current version"
+msgstr ""
msgid "WorkItem|Work Items"
msgstr "Elemente de lucru"
@@ -46555,6 +47248,9 @@ msgstr "Scrieți un comentariu..."
msgid "Write a description or drag your files here…"
msgstr "Scrieți o descriere sau glisați fișierele aici…"
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr "Scrieți o descriere..."
@@ -46723,11 +47419,11 @@ msgstr "Sunteți conectat la GitLab ca:"
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr "Încercați să încărcați altceva decât o imagine. Vă rugăm să încărcați un fișier .png, .jpg, .jpeg, .gif, .bmp, .tiff sau .ico."
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
-msgstr "Puteți să %{gitlabLinkStart}rezolvați conflictele pe GitLab%{gitlabLinkEnd} sau %{resolveLocallyStart}să le rezolvați local%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
+msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
-msgstr "Puteți %{resolveLocallyStart}rezolva la nivel local%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
+msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
msgstr "Puteți ajusta regulile de interzicere automată %{link_start}aici%{link_end}."
@@ -46765,12 +47461,6 @@ msgstr "Puteți schimba oricând URL-ul mai târziu"
msgid "You can always edit this later"
msgstr "Puteți edita oricând acest lucru mai târziu"
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] "Puteți să începeți mutarea membrilor în %{namespace_name} acum. Un membru își pierde accesul la grup atunci când dezactivați opțiunea %{strong_start}Într-un seat%{strong_end}. Dacă mai mult de %{free_user_limit} membru are activată opțiunea %{strong_start}Într-un seat%{strong_end} după 19 octombrie 2022, vom selecta %{free_user_limit} membru care își păstrează accesul. Vom număra mai întâi membrii care au rolurile de Proprietar și Întreținător, apoi cei mai recent activi membri până când vom ajunge la %{free_user_limit} membru. Membrii rămași vor primi statutul de „Peste limită†și vor pierde accesul la grup."
-msgstr[1] "Puteți să începeți mutarea membrilor în %{namespace_name} acum. Un membru își pierde accesul la grup atunci când dezactivați opțiunea %{strong_start}Într-un seat%{strong_end}. Dacă mai mult de %{free_user_limit} membri au activată opțiunea %{strong_start}Într-un seat%{strong_end} după 19 octombrie 2022, vom selecta cei %{free_user_limit} membri care își păstrează accesul. Vom număra mai întâi membrii care au rolurile de Proprietar și Întreținător, apoi cei mai recent activi membri până când vom ajunge la %{free_user_limit} membri. Membrii rămași vor primi statutul de „Peste limită†și vor pierde accesul la grup."
-msgstr[2] "Puteți să începeți mutarea membrilor în %{namespace_name} acum. Un membru își pierde accesul la grup atunci când dezactivați opțiunea %{strong_start}Într-un seat%{strong_end}. Dacă mai mult de %{free_user_limit} de membri au activată opțiunea %{strong_start}Într-un seat%{strong_end} după 19 octombrie 2022, vom selecta cei %{free_user_limit} de membri care își păstrează accesul. Vom număra mai întâi membrii care au rolurile de Proprietar și Întreținător, apoi cei mai recent activi membri până când vom ajunge la %{free_user_limit} de membri. Membrii rămași vor primi statutul de „Peste limită†și vor pierde accesul la grup."
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr "Îl puteți verifica în setările %{pat_link_start}tokenurilor de acces personale%{pat_link_end}."
@@ -46837,9 +47527,6 @@ msgstr "Puteți găsi mai multe informații despre abonamentele GitLab în %{sub
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr "Puteți începe prin a clona repozitoriul sau puteți începe să adăugați fișiere în acesta cu una dintre următoarele opțiuni."
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr "Puteți grupa cazurile de testare utilizând etichete. Pentru a afla despre direcția viitoare a acestei funcții, vizitați %{linkStart}pagina direcției Managementul Calității%{linkEnd}."
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "Puteți invita un nou membru la %{project_name} sau puteți invita un alt grup."
@@ -46858,18 +47545,12 @@ msgstr "Puteți notifica aplicația/grupul sau un proiect trimițându-le o noti
msgid "You can now close this window."
msgstr "Acum puteți închide această fereastră."
-msgid "You can now export your security dashboard to a CSV report."
-msgstr "Acum puteți să exportați tabloul de bord de securitate într-un raport CSV."
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr "Acum puteți trimite un merge request pentru a introduce această modificare în ramura originală."
msgid "You can now submit a merge request to get this change into the original project."
msgstr "Acum puteți trimite un merge request pentru a introduce această modificare în proiectul original."
-msgid "You can only %{action} files when you are on a branch"
-msgstr "Puteți %{action} fișiere numai atunci când vă aflați pe o ramură"
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr "Puteți adăuga doar până la %{max_contacts} (de) contacte în același timp"
@@ -46951,6 +47632,9 @@ msgstr "Nu puteÈ›i edita fiÈ™iere direct în acest proiect. Fork acest proiect È
msgid "You could not create a new trigger."
msgstr "Nu ați putut crea un nou declanșator."
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr "Nu aveți încă niciun abonament"
@@ -47005,6 +47689,9 @@ msgstr "Nu aveți nicio căutare recentă"
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr "Nu aveți permisiunea de a revizui această implementare. Contactați proprietarul proiectului sau al grupului pentru ajutor."
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr "Nu aveți permisiuni pentru a crea acest proiect"
@@ -47068,6 +47755,9 @@ msgstr "Nu aveți permisiuni suficiente pentru a crea o integrare HTTP pentru ac
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr "Nu aveți permisiuni suficiente pentru a crea un program de gardă pentru acest proiect"
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr "Nu aveți permisiuni suficiente pentru a gestiona linkurile resurselor acestui incident"
@@ -47119,9 +47809,6 @@ msgstr "Ați configurat 2FA pentru contul dvs.! Dacă pierdeți accesul la dispo
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr "Ați cumpărat cu succes %{product}. Veți primi o chitanță prin e-mail. Achiziția dvs. poate dura un minut pentru a se sincroniza, așa că reîmprospătați pagina dacă nu o vedeți încă."
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr "Achiziționarea abonamentului la planul %{plan} pentru %{seats} s-a efectuat. Veți primi o chitanță prin e-mail. Sincronizarea achiziției poate dura un minut, așa că reîmprospătați pagina dacă nu o vedeți încă."
-
msgid "You have unsaved changes"
msgstr "Aveți modificări nesalvate"
@@ -47299,6 +47986,9 @@ msgstr "Ați activat deja autentificarea cu doi factori folosind autentificatoar
msgid "You've rejected %{user}"
msgstr "Ați respins %{user}"
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -47317,8 +48007,8 @@ msgstr "Abonamentul %{strong}%{plan_name}%{strong_close} expiră la %{strong}%{e
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr "Abonamentul dvs. %{strong}%{plan_name}%{strong_close} pentru %{strong}%{namespace_name}%{strong_close} va expira pe %{strong}%{expires_on}%{strong_close}."
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
-msgstr "Sintaxa configurației dvs. CI/CD nu este valabilă. Vizualizați fila Lint pentru mai multe detalii."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
+msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
msgstr "Exportul dvs. CSV a început. Acesta va fi trimis prin e-mail la %{email} atunci când este finalizat."
@@ -47353,6 +48043,9 @@ msgstr "Solicitarea dvs. de creare a contului GitLab a fost aprobată!"
msgid "Your GitLab group"
msgstr "Grupul dvs. GitLab"
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr "Grupurile dvs."
@@ -47497,12 +48190,6 @@ msgstr[0] "Grupul dvs. gratuit este acum limitat la %d membru"
msgstr[1] "Grupul dvs. gratuit este acum limitat la %d membri"
msgstr[2] "Grupul dvs. gratuit este acum limitat la %d de membri"
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] "Grupul dvs., %{strong_start}%{namespace_name}%{strong_end} are mai mult de %{free_user_limit} membru. Începând cu 19 octombrie 2022, cel mai recent %{free_user_limit} membru activ va rămâne activ, iar restul membrilor vor avea statutul %{link_start}Peste limită%{link_end} și vor pierde accesul la grup. Pentru a gestiona care %{free_user_limit} membru va rămâne în grup, puteți accesa pagina Cotele de utilizare."
-msgstr[1] "Grupul dvs, %{strong_start}%{namespace_name}%{strong_end} are mai mult de %{free_user_limit} membri. Începând cu 19 octombrie 2022, cei mai recent %{free_user_limit} membri activi vor rămâne activi, iar ceilalți membri vor avea statutul %{link_start}Peste limită%{link_end} și vor pierde accesul la grup. Pentru a gestiona care dintre cei %{free_user_limit} membri vor rămâne în grup, puteți accesa pagina Cotele de utilizare."
-msgstr[2] "Grupul dvs., %{strong_start}%{namespace_name}%{strong_end} are mai mult de %{free_user_limit} de membri. Începând cu 19 octombrie 2022, cei mai recent activi %{free_user_limit} de membri vor rămâne activi, iar restul membrilor vor avea statutul %{link_start}Peste limită%{link_end} și vor pierde accesul la grup. Puteți accesa pagina Cotele de utilizare pentru a gestiona care dintre cei %{free_user_limit} de membri vor rămâne în grup."
-
msgid "Your groups"
msgstr "Grupurile dvs."
@@ -47542,6 +48229,15 @@ msgstr "Numele dvs."
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr "Noul dvs. %{accessTokenType}"
@@ -47701,6 +48397,9 @@ msgstr "[REDACTED]"
msgid "[Redacted]"
msgstr "[Redacted]"
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr "„end_time†nu trebuie să depășească o lună după „start_timeâ€."
@@ -47881,6 +48580,9 @@ msgstr "nu poate fi asociat cu un subgrup"
msgid "cannot be associated with both a Group and a Project"
msgstr "nu poate fi asociat atât cu un Grup, cât și cu un Proiect"
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr "nu se poate modifica"
@@ -47935,6 +48637,18 @@ msgstr[0] "modificare"
msgstr[1] "modificări"
msgstr[2] "de modificări"
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr "%{criticalStart}critice%{criticalEnd}, %{highStart}ridicate%{highEnd} și %{otherStart}altele%{otherEnd}"
@@ -47992,11 +48706,14 @@ msgstr "%{scanner} a detectat %{number} (de) nouă / noi potențială(e) %{vulnS
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr "%{scanner} a detectat %{strong_start}%{number}%{strong_end} potențiale noi %{vulnStr}"
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
-msgstr "%{scanner} nu a detectat nicio %{boldStart}nouă%{boldEnd} vulnerabilitate potențială"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr "%{scanner} nu a detectat nicio nouă %{vulnStr}"
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
+msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
-msgstr "%{scanner} nu a detectat %{strong_start}noi%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
+msgstr ""
msgid "ciReport|: Loading resulted in an error"
msgstr ": Încărcarea a avut ca rezultat o eroare"
@@ -48157,8 +48874,8 @@ msgstr "Corectat:"
msgid "ciReport|Found %{issuesWithCount}"
msgstr "S-a(u) găsit %{issuesWithCount}"
-msgid "ciReport|Full Report"
-msgstr "Raportul complet"
+msgid "ciReport|Full report"
+msgstr ""
msgid "ciReport|Generic Report"
msgstr "Raport generic"
@@ -48337,6 +49054,12 @@ msgstr "comentat"
msgid "commented on %{link_to_project}"
msgstr "a comentat pe %{link_to_project}"
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "commit %{commit_id}"
msgstr "commit %{commit_id}"
@@ -48496,9 +49219,6 @@ msgstr "example.com"
msgid "exceeds maximum length (100 usernames)"
msgstr "depășește lungimea maximă (100 de nume de utilizatori)"
-msgid "exceeds the %{max_value_length} character limit"
-msgstr "depășește limita de %{max_value_length} caractere"
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr "depășește limita de %{bytes} bytes"
@@ -48685,6 +49405,9 @@ msgstr "este deja asociat cu o problemă GitLab. Noua problemă nu va fi asociat
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr "este un interval de adrese IP invalid"
@@ -48724,6 +49447,12 @@ msgstr "nu este permis pentru acest proiect."
msgid "is not allowed since the group is not top-level group."
msgstr "nu este permis, deoarece grupul nu este un grup de nivel superior."
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr "nu este permisă. Vă rugăm să folosiți adresa dvs. obișnuită de e-mail."
@@ -49222,6 +49951,9 @@ msgstr "trebuie să fie asociat cu un Grup sau un Proiect"
msgid "must be greater than start date"
msgstr "trebuie să fie mai mare decât data de început"
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr "trebuie să fie în interiorul rețelei fork."
@@ -49231,12 +49963,12 @@ msgstr "trebuie să fie mai mic(ă) decât limita de %{tag_limit} (de) etichete"
msgid "must be set for a project namespace"
msgstr "trebuie să fie setat pentru un spațiu de nume de proiect"
-msgid "must be top-level namespace"
-msgstr "trebuie să fie un spațiu de nume de nivel superior"
-
msgid "must be unique by status and elapsed time within a policy"
msgstr "trebuie să fie unic(ă) prin statut și timp scurs în cadrul unei politici"
+msgid "must belong to same project of its requirement object."
+msgstr ""
+
msgid "must belong to same project of the work item."
msgstr ""
@@ -49441,6 +50173,9 @@ msgstr "cheie privată reCAPTCHA"
msgid "reCAPTCHA site key"
msgstr "cheia site-ului reCAPTCHA"
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr "activitate recentă"
@@ -49498,6 +50233,12 @@ msgstr "repozitoriu:"
msgid "role's base access level does not match the access level of the membership"
msgstr "nivelul de acces de bază al rolului nu corespunde nivelului de acces al calității de membru"
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "running"
msgstr "rulează"
diff --git a/locale/ru/gitlab.po b/locale/ru/gitlab.po
index f6c77617e22..753dac4b4ff 100644
--- a/locale/ru/gitlab.po
+++ b/locale/ru/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: ru\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:21\n"
+"PO-Revision-Date: 2022-12-10 06:41\n"
msgid " %{start} to %{end}"
msgstr " %{start} по %{end}"
@@ -54,7 +54,7 @@ msgid " or %{emphasisStart}!merge request id%{emphasisEnd}"
msgstr " или %{emphasisStart}!id запроÑа на ÑлиÑние%{emphasisEnd}"
msgid " or %{emphasisStart}#id%{emphasisEnd}"
-msgstr ""
+msgstr "или %{emphasisStart}#id%{emphasisEnd}"
msgid " or %{emphasisStart}#issue id%{emphasisEnd}"
msgstr " или %{emphasisStart}#id обÑуждениÑ%{emphasisEnd}"
@@ -145,10 +145,10 @@ msgstr[3] ""
msgid "%d additional assignee"
msgid_plural "%d additional assignees"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "%d дополнительные ответÑтвенные"
+msgstr[1] "%d дополнительных ответÑтвенных"
+msgstr[2] "%d дополнительных ответÑтвенных"
+msgstr[3] "%d дополнительных ответÑтвенных"
msgid "%d additional commenter"
msgid_plural "%d additional commenters"
@@ -451,6 +451,13 @@ msgstr[1] "еще %d комментариÑ"
msgstr[2] "еще %d комментариев"
msgstr[3] "еще %d комментариев"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%d ожидающий комментарий"
@@ -467,10 +474,10 @@ msgstr[3] "%d личных проектов будут удалены без вÐ
msgid "%d point"
msgid_plural "%d points"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "%d балл"
+msgstr[1] "%d баллов"
+msgstr[2] "%d баллов"
+msgstr[3] "%d баллов"
msgid "%d previously merged commit"
msgid_plural "%d previously merged commits"
@@ -579,10 +586,10 @@ msgstr[3] ""
msgid "%d vulnerability set to dismissed"
msgid_plural "%d vulnerabilities set to dismissed"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "%d уÑзвимоÑÑ‚ÑŒ отклонена"
+msgstr[1] "%d уÑзвимоÑти(ей) отклонены"
+msgstr[2] "%d уÑзвимоÑти(ей) отклонены"
+msgstr[3] "%d уÑзвимоÑти(ей) отклонены"
msgid "%d vulnerability set to needs triage"
msgid_plural "%d vulnerabilities set to needs triage"
@@ -666,6 +673,12 @@ msgstr[3] "%{bold_start}%{count}%{bold_end} открытых запроÑов н
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -886,14 +899,14 @@ msgstr "%{group_name} иÑпользует управлÑемые группов
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr "%{group_name}&%{epic_iid} &middot; Ñоздана пользователем %{author} %{epic_created}"
-msgid "%{hook_type} was deleted"
-msgstr ""
+msgid "%{host} sign-in from new location"
+msgstr "%{host} вход из нового меÑтоположениÑ"
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
-msgstr "%{host} вход из нового меÑтоположениÑ"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr "%{integrations_link_start}Интеграции%{link_end} позволÑÑŽÑ‚ вам Ñделать Ñторонние Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñ‡Ð°Ñтью вашего рабочего процеÑÑа GitLab. ЕÑли доÑтупные интеграции не ÑоответÑтвуют вашим потребноÑÑ‚Ñм, раÑÑмотрите возможноÑÑ‚ÑŒ иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ %{webhooks_link_start}веб-обработчика%{link_end}."
@@ -1037,7 +1050,7 @@ msgid "%{name_with_link} namespace has run out of Shared Runner Pipeline minutes
msgstr ""
msgid "%{name} (Busy)"
-msgstr ""
+msgstr "%{name} (ÐедоÑтупен)"
msgid "%{name} is already being used for another emoji"
msgstr ""
@@ -1088,9 +1101,6 @@ msgstr "%{openedEpics} открыто, %{closedEpics} закрыто"
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr "%{openedIssues} открыто, %{closedIssues} закрыто"
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -1143,7 +1153,7 @@ msgstr "%{reportType} %{status}"
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr "%{reportType} обнаружено %{totalStart}%{total}%{totalEnd} потенциальных %{vulnMessage}"
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1318,6 +1328,13 @@ msgstr "%{template_project_id}' неизвеÑтен или недейÑтвит
msgid "%{text} is available"
msgstr "%{text} доÑтупен"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr "%{timebox_type} не поддерживает диаграммы ÑгораниÑ"
@@ -1357,6 +1374,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr "%{userName}(не может выполнÑÑ‚ÑŒ ÑлиÑние)"
@@ -1478,12 +1498,18 @@ msgstr "(+%{count}&nbsp;правил)"
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr "(Без изменений)"
msgid "(UTC %{offset}) %{timezone}"
msgstr "(UTC %{offset}) %{timezone}"
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr "(прогреÑÑ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ¸)"
@@ -1546,7 +1572,7 @@ msgid "+%{more_assignees_count}"
msgstr "+%{more_assignees_count}"
msgid "+%{more_assignees_count} more assignees"
-msgstr ""
+msgstr "+ ещё %{more_assignees_count} ответÑтвенных"
msgid "+%{more_reviewers_count}"
msgstr "+%{more_reviewers_count}"
@@ -2630,6 +2656,9 @@ msgstr "Добавить ÑиÑтемный обработчик"
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr "Добавить на доÑку"
@@ -2649,7 +2678,7 @@ msgid "Add to tree"
msgstr "Добавить к дереву"
msgid "Add topics to projects to help users find them."
-msgstr ""
+msgstr "ДобавлÑйте теги в проекты, чтобы пользователÑм было легче их найти."
msgid "Add trigger"
msgstr "Добавить триггер"
@@ -2717,6 +2746,9 @@ msgstr "Добавлено в Ñтой верÑии"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "Добавление новых приложений отключено в вашем ÑкземплÑре GitLab. ОбратитеÑÑŒ к админиÑтратору GitLab, чтобы получить разрешение"
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "Дополнительные минуты"
@@ -2912,6 +2944,9 @@ msgstr "ОÑтановка заданий не удалаÑÑŒ"
msgid "AdminArea|Total users"
msgstr "Ð’Ñего пользователей"
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr "Пользователи"
@@ -3212,8 +3247,8 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr "Выберите шаблон CI/CD"
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
-msgstr "Выберите группу Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² качеÑтве иÑточника шаблонов проектов на уровне ÑкземплÑра."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
+msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
msgstr "Выберите, чтобы отключить общий доÑтуп к Ñайтам Pages. Это потребует от пользователей авторизации Ð´Ð»Ñ Ð´Ð¾Ñтупа к Ñайтам Pages вашего ÑкземплÑра. %{link_start}Подробнее.%{link_end}"
@@ -3275,9 +3310,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr "ПоÑледние артефакты Ð´Ð»Ñ Ð²Ñех заданий в недавних уÑпешных Ñборочных линиÑÑ… каждого проекта ÑохранÑÑŽÑ‚ÑÑ Ð±ÐµÑÑрочно."
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr "Проекты в Ñтой группе могут быть выбраны в качеÑтве шаблонов Ð´Ð»Ñ Ð½Ð¾Ð²Ñ‹Ñ… проектов, Ñозданных в ÑкземплÑре. %{link_start}Подробнее.%{link_end} "
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr "Шаблон обÑзательной конфигурации Ñборочной линии может быть как одним из предоÑтавленных GitLab, так и пользовательÑким, добавленным в репозиторий шаблонов ÑкземплÑра. %{link_start}Как мне Ñоздать репозиторий шаблонов ÑкземплÑра?%{link_end}"
@@ -3861,7 +3893,7 @@ msgid "AlertManagement|Alert"
msgstr "Оповещение"
msgid "AlertManagement|Alert assignee(s): %{assignees}"
-msgstr ""
+msgstr "ОповеÑтить отвеÑтвенных: %{assignees}"
msgid "AlertManagement|Alert detail"
msgstr "ПодробноÑти оповещениÑ"
@@ -3969,7 +4001,7 @@ msgid "AlertManagement|There was an error displaying the alerts. Confirm your en
msgstr "Произошла ошибка при отображении оповещений. Проверьте наÑтройки конечной точки, чтобы убедитьÑÑ, что Ð¾Ð¿Ð¾Ð²ÐµÑ‰ÐµÐ½Ð¸Ñ Ð¿Ð¾ÐºÐ°Ð·Ñ‹Ð²Ð°ÑŽÑ‚ÑÑ."
msgid "AlertManagement|There was an error while updating the assignee(s) list. Please try again."
-msgstr ""
+msgstr "Произошла ошибка при обновлении ÑпиÑка ответÑтвенных. ПожалуйÑта, попробуйте еще раз."
msgid "AlertManagement|There was an error while updating the assignee(s) of the alert. Please try again."
msgstr ""
@@ -4806,9 +4838,12 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr "Произошла ошибка. ПожалуйÑта, попробуйте Ñнова."
-msgid "An example project for managing Kubernetes clusters integrated with GitLab"
+msgid "An error occurred. Unable to reopen this merge request."
msgstr ""
+msgid "An example project for managing Kubernetes clusters integrated with GitLab"
+msgstr "Пример проекта Ð´Ð»Ñ ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÐºÐ»Ð°Ñтерами Kubernetes, интегрированными Ñ GitLab"
+
msgid "An example project that shows off the best practices for setting up GitLab for your own organization, including sample issues, merge requests, and milestones"
msgstr ""
@@ -4867,7 +4902,7 @@ msgid "Analyze your infrastructure as code configuration files for known vulnera
msgstr ""
msgid "Analyze your source code and git history for secrets."
-msgstr ""
+msgstr "Проанализируйте Ñодержит ли ваш иÑходный код и иÑÑ‚Ð¾Ñ€Ð¸Ñ git Ñекреты."
msgid "Analyze your source code for known vulnerabilities."
msgstr "Ðнализируйте ваш иÑходный код на предмет извеÑтных уÑзвимоÑтей."
@@ -4923,9 +4958,6 @@ msgstr ""
msgid "Any namespace"
msgstr "Любое проÑтранÑтво имен"
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr "ID приложениÑ"
@@ -5480,6 +5512,9 @@ msgstr[1] "Вы уверены, что хотите импортировать %
msgstr[2] "Вы уверены, что хотите импортировать %d репозиториев?"
msgstr[3] "Вы уверены, что хотите импортировать %d репозиториев?"
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr "Вы уверены, что хотите заблокировать %{path}?"
@@ -5508,7 +5543,7 @@ msgid "Are you sure you want to remove %{group_name}?"
msgstr "Вы уверены, что вы хотите удалить %{group_name}?"
msgid "Are you sure you want to remove %{topic_name}?"
-msgstr ""
+msgstr "Вы уверены, что хотите удалить %{topic_name}?"
msgid "Are you sure you want to remove the attachment?"
msgstr "Ð’Ñ‹ дейÑтвительно хотите удалить вложение?"
@@ -5703,7 +5738,7 @@ msgid "Assigned Issues"
msgstr "Ðазначенные обÑуждениÑ"
msgid "Assigned merge requests"
-msgstr ""
+msgstr "Ðазначенные запроÑÑ‹ на ÑлиÑние"
msgid "Assigned projects"
msgstr ""
@@ -5894,6 +5929,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5939,6 +5977,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "Ðвг."
@@ -6170,9 +6211,6 @@ msgstr "ДоÑтупные групповые runner'Ñ‹: %{runners}"
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr "ДоÑтупные общие runner'Ñ‹:"
-
msgid "Available specific runners"
msgstr "ДоÑтупные ÑпецифичеÑкие runner'Ñ‹"
@@ -6783,7 +6821,7 @@ msgid "Billing|Enter at least three characters to search."
msgstr "Введите минимум три Ñимвола Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка."
msgid "Billing|Explore paid plans"
-msgstr ""
+msgstr "Обзор платных планов"
msgid "Billing|Export list"
msgstr ""
@@ -6935,7 +6973,7 @@ msgid "BoardScope|Any Milestone"
msgstr "Любой Ñтап"
msgid "BoardScope|Any assignee"
-msgstr ""
+msgstr "Любой ответÑтвенный"
msgid "BoardScope|Any iteration"
msgstr ""
@@ -6944,7 +6982,7 @@ msgid "BoardScope|Any label"
msgstr ""
msgid "BoardScope|Assignee"
-msgstr ""
+msgstr "ОтветÑтвенный"
msgid "BoardScope|Choose labels"
msgstr ""
@@ -6980,7 +7018,7 @@ msgid "BoardScope|Search milestones"
msgstr ""
msgid "BoardScope|Select assignee"
-msgstr ""
+msgstr "Выберите ответÑтвенного"
msgid "BoardScope|Select iteration"
msgstr ""
@@ -7200,6 +7238,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7314,9 +7361,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "Ветки"
@@ -7326,6 +7370,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr "Ðктивные"
@@ -7347,8 +7394,11 @@ msgstr "Ðевозможно найти HEAD-коммит Ñтой ветки"
msgid "Branches|Compare"
msgstr "Сравнить"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "Удалить вÑе ветки, влитые в '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
+msgstr ""
msgid "Branches|Delete branch"
msgstr "Удалить ветку"
@@ -7368,9 +7418,6 @@ msgstr "Удаление защищённой ветки. Ð’Ñ‹ ÐБСОЛЮТÐÐ
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr "Удаление ветки %{strongStart}%{branchName}%{strongEnd} невозможно отменить. Вы уверены?"
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "Удаление влитых веток невозможно отменить. Вы уверены?"
-
msgid "Branches|Filter by branch name"
msgstr "Отфильтровать по имени ветки"
@@ -7392,6 +7439,9 @@ msgstr "Обзор"
msgid "Branches|Please type the following to confirm:"
msgstr "ПожалуйÑта, введите Ñледующее Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ:"
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr "Показать активные ветки"
@@ -7425,6 +7475,12 @@ msgstr "Ветка \"по умолчанию\" не может быть удал
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr "Чтобы отменить локальные Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸ перезапиÑать ветку верÑией из родительÑкого репозиториÑ, удалите её здеÑÑŒ и выберите \"Обновить ÑейчаÑ\" выше."
@@ -7437,6 +7493,9 @@ msgstr "Да, удалить ветку"
msgid "Branches|Yes, delete protected branch"
msgstr "Да, удалить защищённую ветку"
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7491,6 +7550,9 @@ msgstr "Произошла ошибка при получении артефак
msgid "BuildArtifacts|Loading artifacts"
msgstr "Загрузка артефактов"
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr "Ð’Ñтроенный"
@@ -7760,10 +7822,10 @@ msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
msgid "CICD|Add a %{base_domain_link_start}base domain%{link_end} to your %{kubernetes_cluster_link_start}Kubernetes cluster%{link_end} for your deployment strategy to work."
-msgstr ""
+msgstr "CICD | Добавьте %{base_domain_link_start}базовый домен%{link_end} к вашему %{kubernetes_cluster_link_start}клаÑтеру Kubernetes%{link_end} чтобы ваша ÑÑ‚Ñ€Ð°Ñ‚ÐµÐ³Ð¸Ñ Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ð°Ð»Ð°."
msgid "CICD|Add a %{kubernetes_cluster_link_start}Kubernetes cluster integration%{link_end} with a domain, or create an AUTO_DEVOPS_PLATFORM_TARGET CI variable."
-msgstr ""
+msgstr "CICD | Добавьте %{kubernetes_cluster_link_start}интеграции клаÑтера Kubernetes%{link_end} Ñ Ð´Ð¾Ð¼ÐµÐ½Ð¾Ð¼ или Ñоздайте переменную AUTO_DEVOPS_PLATFORM_TARGET CI."
msgid "CICD|Add an existing project to the scope"
msgstr ""
@@ -8051,7 +8113,7 @@ msgid "Cannot modify %{profile_name} referenced in security policy"
msgstr "Ðевозможно изменить %{profile_name}, упомÑнутый в политике безопаÑноÑти"
msgid "Cannot modify managed Kubernetes cluster"
-msgstr "Ðевозможно изменить управлÑемый клаÑтер Kubernetes"
+msgstr "Ðевозможно изменить клаÑтер Kubernetes"
msgid "Cannot modify provider during creation"
msgstr ""
@@ -8299,6 +8361,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr "Ð¡Ð±Ð¾Ñ€Ð¾Ñ‡Ð½Ð°Ñ Ð»Ð¸Ð½Ð¸Ñ %{pipeline_link} из %{ref_type} %{ref_link} Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %{user_combined_name} %{humanized_status}"
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr "Тег"
@@ -8345,6 +8410,9 @@ msgid "Check your Docker images for known vulnerabilities."
msgstr "Проверьте ваши образы Docker на наличие извеÑтных уÑзвимоÑтей."
msgid "Check your Kubernetes cluster images for known vulnerabilities."
+msgstr "Проверьте ваши образы клаÑтера Kubernetes на наличие извеÑтных уÑзвимоÑтей."
+
+msgid "Check your sign-up restrictions"
msgstr ""
msgid "Checking %{text} availability…"
@@ -8472,9 +8540,6 @@ msgstr "Ðе удалоÑÑŒ загрузить форму кредитной кÐ
msgid "Checkout|Edit"
msgstr "Правка"
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8490,7 +8555,7 @@ msgstr "Ðе удалоÑÑŒ загрузить Ñтраны. ПожалуйÑÑ‚Ð
msgid "Checkout|Failed to load states. Please try again."
msgstr "Ðе удалоÑÑŒ загрузить штаты. ПожалуйÑта, попробуйте ещё раз."
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8514,6 +8579,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr "Ð˜Ð¼Ñ ÐºÐ¾Ð¼Ð¿Ð°Ð½Ð¸Ð¸ или организации, иÑпользующей GitLab"
@@ -8757,9 +8825,15 @@ msgstr "выполнÑетÑÑ"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr "Ðевозможно иÑпользовать маÑкируемую переменную Ñ Ñ‚ÐµÐºÑƒÑ‰Ð¸Ð¼ значением"
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr "ОкружениÑ"
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr "Ключ входной переменной"
@@ -8772,6 +8846,9 @@ msgstr "Ключ"
msgid "CiVariables|Masked"
msgstr "МаÑкируемаÑ"
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8784,6 +8861,9 @@ msgstr "Удалить переменную"
msgid "CiVariables|Remove variable row"
msgstr "Удалить Ñтроку переменных"
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr "ОблаÑÑ‚ÑŒ видимоÑти"
@@ -8793,6 +8873,9 @@ msgstr "Укажите Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ñ‹Ñ…, которые бÑ
msgid "CiVariables|State"
msgstr "СоÑтоÑние"
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr "Тип"
@@ -8802,6 +8885,12 @@ msgstr "Значение"
msgid "CiVariables|Variables"
msgstr "Переменные"
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr "* (Ð’Ñе Ñреды)"
@@ -9293,7 +9382,7 @@ msgid "ClusterAgents|Configuration"
msgstr "КонфигурациÑ"
msgid "ClusterAgents|Connect a Kubernetes cluster"
-msgstr ""
+msgstr "Подключение клаÑтера Kubernetes"
msgid "ClusterAgents|Connect a cluster"
msgstr ""
@@ -9468,7 +9557,7 @@ msgid "ClusterAgents|Tell us what you think"
msgstr ""
msgid "ClusterAgents|The GitLab agent provides an increased level of security when connecting Kubernetes clusters to GitLab. %{linkStart}Learn more about the GitLab agent.%{linkEnd}"
-msgstr ""
+msgstr "Ðгент GitLab обеÑпечивает повышенный уровень безопаÑноÑти при подключении клаÑтеров Kubernetes к GitLab. %{linkStart}Узнайте больше об агенте GitLab.%{linkEnd}"
msgid "ClusterAgents|The agent has not been connected in a long time. There might be a connectivity issue. Last contact was %{timeAgo}."
msgstr ""
@@ -9497,6 +9586,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -9555,7 +9647,7 @@ msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr "Добавить клаÑтер Kubernetes"
msgid "ClusterIntegration|Adding a Kubernetes cluster to your group will automatically share the cluster across all your projects. Use review apps, deploy your applications, and easily run your pipelines for all projects using the same cluster."
-msgstr ""
+msgstr "Добавление клаÑтера Kubernetes в вашу группу автоматичеÑки разделит клаÑтер во вÑех ваших проектах. ИÑпользуйте Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра, развертывайте Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¸ легко запуÑкайте Ñвои Ñборочные линии Ð´Ð»Ñ Ð²Ñех проектов Ñ Ð¸Ñпользованием одного и того же клаÑтера."
msgid "ClusterIntegration|Adding a Kubernetes cluster will automatically share the cluster across all projects. Use review apps, deploy your applications, and easily run your pipelines for all projects using the same cluster."
msgstr "Добавление клаÑтера Kubernetes автоматичеÑки разделит клаÑтер между вÑеми проектами. ИÑпользуйте Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñ€ÐµÑ†ÐµÐ½Ð·Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ, развертывайте Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¸ легко запуÑкайте Ñборочные линии Ð´Ð»Ñ Ð²Ñех проектов, иÑпользующих один и тот же клаÑтер."
@@ -9630,7 +9722,7 @@ msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancesto
msgstr ""
msgid "ClusterIntegration|Connect a Kubernetes cluster"
-msgstr ""
+msgstr "ClusterIntegration|Подключение клаÑтера Kubernetes"
msgid "ClusterIntegration|Connect your cluster to GitLab through %{linkStart}cluster certificates%{linkEnd}."
msgstr ""
@@ -9648,7 +9740,7 @@ msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr "Скопировать Ð¸Ð¼Ñ ÐºÐ»Ð°Ñтера Kubernetes"
msgid "ClusterIntegration|Create a Kubernetes cluster"
-msgstr ""
+msgstr "Создать клаÑтер Kubernetes"
msgid "ClusterIntegration|Deploy each environment to its own namespace. Otherwise, environments within a project share a project-wide namespace. Note that anyone who can trigger a deployment of a namespace can read its secrets. If modified, existing environments will use their current namespaces until the cluster cache is cleared."
msgstr ""
@@ -9675,7 +9767,7 @@ msgid "ClusterIntegration|Enter new Service Token"
msgstr ""
msgid "ClusterIntegration|Enter your Kubernetes cluster certificate details"
-msgstr ""
+msgstr "Введите данные Ñертификата клаÑтера Kubernetes."
msgid "ClusterIntegration|Environment scope"
msgstr "Среда окружениÑ"
@@ -9744,19 +9836,19 @@ msgid "ClusterIntegration|Integrations allow you to use applications installed i
msgstr ""
msgid "ClusterIntegration|Kubernetes cluster is being created..."
-msgstr ""
+msgstr "КлаÑтер Kubernetes ÑоздаетÑÑ..."
msgid "ClusterIntegration|Kubernetes cluster name"
msgstr "Ð˜Ð¼Ñ ÐºÐ»Ð°Ñтера Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster was successfully created."
-msgstr ""
+msgstr "КлаÑтер Kubernetes уÑпешно Ñоздан."
msgid "ClusterIntegration|Learn more about Kubernetes."
msgstr ""
msgid "ClusterIntegration|Learn more about group Kubernetes clusters"
-msgstr ""
+msgstr "Узнайте больше о группах клаÑтеров Kubernetes"
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr "Узнайте больше о ÑкземплÑрах клаÑтеров Kubernetes"
@@ -9891,7 +9983,7 @@ msgid "ClusterIntegration|Use GitLab to deploy to your cluster, run jobs, use re
msgstr ""
msgid "ClusterIntegration|Use the %{linkStart}GitLab agent%{linkEnd} to safely connect your Kubernetes clusters to GitLab. You can deploy your applications, run your pipelines, use Review Apps, and much more."
-msgstr ""
+msgstr "ИÑпользуйте %{linkStart}GitLab Agent%{linkEnd} Ð´Ð»Ñ Ð±ÐµÐ·Ð¾Ð¿Ð°Ñного Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ ÐºÐ»Ð°Ñтеров Kubernetes к GitLab. Ð’Ñ‹ можете разворачивать приложениÑ, запуÑкать Ñборочные линии, иÑпользовать ÐŸÑ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñ€ÐµÐ²Ð¸Ð·Ð¸Ð¹ и многое другое."
msgid "ClusterIntegration|Using AutoDevOps with multiple clusters? %{help_link_start}Read this first.%{help_link_end}"
msgstr ""
@@ -10320,6 +10412,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10344,6 +10439,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10359,15 +10457,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10533,7 +10640,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10630,7 +10737,7 @@ msgid "Connect"
msgstr "Подключить"
msgid "Connect a Kubernetes Cluster"
-msgstr ""
+msgstr "Подключить клаÑтер Kubernetes"
msgid "Connect a cluster"
msgstr ""
@@ -10653,6 +10760,9 @@ msgstr "Подключение"
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr "Подключение..."
@@ -10692,12 +10802,6 @@ msgstr "Образы рееÑтра контейнеров"
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr "РееÑÑ‚Ñ€ контейнеров не включен в Ñтом ÑкземплÑре GitLab. ПопроÑите админиÑтратора включить его, чтобы Auto DevOps работал."
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10968,9 +11072,6 @@ msgstr "Тег уÑпешно помечен Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ."
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr "Теги уÑпешно помечены Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ."
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr "Теги, которые ÑоответÑтвуют Ñтим правилам будут %{strongStart}Ñохранены%{strongEnd}, даже, еÑли они ÑоответÑтвуют правилам ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð½Ð¸Ð¶Ðµ. %{secondStrongStart}ПоÑледний%{secondStrongEnd} тег вÑегда ÑохранÑетÑÑ."
@@ -11154,9 +11255,6 @@ msgstr "Управление адреÑами Ñлектронной почты,
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr "УправлÑйте работой РееÑтра пакетов GitLab."
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11469,6 +11567,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11506,7 +11607,7 @@ msgid "Create a GitLab account first, and then connect it to your %{label} accou
msgstr "Сначала Ñоздайте учетную запиÑÑŒ GitLab, а затем подключите ее к вашей учетной запиÑи %{label}."
msgid "Create a Kubernetes cluster"
-msgstr ""
+msgstr "Создать клаÑтер Kubernetes"
msgid "Create a Mattermost team for this group"
msgstr "Создать команду в Mattermost Ð´Ð»Ñ Ñтой группы"
@@ -11601,9 +11702,6 @@ msgstr "Создать обÑуждение"
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr "Создать итерацию"
-
msgid "Create label"
msgstr ""
@@ -11682,8 +11780,11 @@ msgstr "Создать Ñниппет"
msgid "Create tag %{tagName}"
msgstr "Создать тег %{tagName}"
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
-msgstr "Создать тему"
+msgstr "Создать тег"
msgid "Create user"
msgstr "Создать пользователÑ"
@@ -11718,6 +11819,42 @@ msgstr "У Ð²Ð°Ñ Ð½ÐµÑ‚ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ Ð½Ð° Ñоздание групп."
msgid "CreateTag|Tag"
msgstr "Тег"
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11943,6 +12080,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -11998,10 +12138,10 @@ msgid "Crm|Organization has been updated."
msgstr ""
msgid "Cron Timezone"
-msgstr "Ð’Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð·Ð¾Ð½Ð° Cron"
+msgstr "ЧаÑовой поÑÑ Cron"
msgid "Cron time zone"
-msgstr ""
+msgstr "ЧаÑовой поÑÑ Cron"
msgid "Crowd"
msgstr ""
@@ -12355,12 +12495,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12373,9 +12519,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12394,6 +12552,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12427,6 +12588,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12487,6 +12651,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12587,7 +12754,7 @@ msgid "DastProfiles|Enable Authentication"
msgstr ""
msgid "DastProfiles|Enable Basic Authentication"
-msgstr ""
+msgstr "Включить базовую аутентификацию"
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -12617,7 +12784,7 @@ msgid "DastProfiles|Include debug messages in the DAST console output."
msgstr ""
msgid "DastProfiles|Manage %{profileType} profiles"
-msgstr ""
+msgstr "Управление %{profileType} профилÑми"
msgid "DastProfiles|Manage profiles"
msgstr ""
@@ -12634,6 +12801,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12665,7 +12835,7 @@ msgid "DastProfiles|Password form field"
msgstr ""
msgid "DastProfiles|Profile in use and cannot be renamed"
-msgstr ""
+msgstr "Профиль иÑпользуетÑÑ Ð¸ не может быть переименован"
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -12689,7 +12859,7 @@ msgid "DastProfiles|Save profile"
msgstr ""
msgid "DastProfiles|Scan Method"
-msgstr ""
+msgstr "Метод ÑканированиÑ"
msgid "DastProfiles|Scan method"
msgstr ""
@@ -13007,6 +13177,9 @@ msgstr ""
msgid "Days to merge"
msgstr "Дней до ÑлиÑниÑ"
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -13064,6 +13237,12 @@ msgstr "Первый день недели по умолчанию"
msgid "Default first day of the week in calendars and date pickers."
msgstr "Первый день недели по умолчанию в календарÑÑ… и ÑредÑтвах выбора даты."
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13110,7 +13289,7 @@ msgid "Define custom rules for what constitutes spam, independent of Akismet"
msgstr ""
msgid "Define environments in the deploy stage(s) in %{code_open}.gitlab-ci.yml%{code_close} to track deployments here."
-msgstr ""
+msgstr "Укажите Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸Ñ Ð½Ð° Ñтапе(ах) Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð² %{code_open}.gitlab-ci.yml%{code_close} Ð´Ð»Ñ Ð¾Ñ‚ÑÐ»ÐµÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð·Ð´ÐµÑÑŒ."
msgid "Define how approval rules are applied to merge requests."
msgstr ""
@@ -13626,7 +13805,7 @@ msgid "DeployFreeze|Specify deploy freezes using %{cron_syntax_link_start}cron s
msgstr ""
msgid "DeployFreeze|Time zone"
-msgstr ""
+msgstr "ЧаÑовой поÑÑ"
msgid "DeployKeys|+%{count} others"
msgstr "+%{count} других"
@@ -13809,7 +13988,7 @@ msgid "Deployment Frequency"
msgstr ""
msgid "Deployment Target|%{linkStart}How to provision or deploy to Kubernetes clusters from GitLab?%{linkEnd}"
-msgstr ""
+msgstr "%{linkStart}Как подготовить или развернуть клаÑтеры Kubernetes из GitLab?%{linkEnd}"
msgid "Deployment Target|Project deployment target (optional)"
msgstr ""
@@ -13868,6 +14047,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13904,9 +14086,12 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
-msgid "Deployments"
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
msgstr ""
+msgid "Deployments"
+msgstr "РазвертываниÑ"
+
msgid "Deployments|%{deployments} environment impacted."
msgid_plural "Deployments|%{deployments} environments impacted."
msgstr[0] ""
@@ -14010,9 +14195,15 @@ msgstr ""
msgid "Description:"
msgstr "ОпиÑание:"
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr "ОпиÑÑ‹Ð²Ð°ÑŽÑ‰Ð°Ñ Ð¼ÐµÑ‚ÐºÐ°"
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr "Данные и файлы ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð´Ð¸Ð·Ð°Ð¹Ð½Ð°Ð¼Ð¸"
@@ -14179,6 +14370,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15036,7 +15230,7 @@ msgid "Edit title and description"
msgstr ""
msgid "Edit topic: %{topic_name}"
-msgstr ""
+msgstr "Редактировать тег: %{topic_name}"
msgid "Edit user: %{user_name}"
msgstr ""
@@ -15500,6 +15694,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr "Введите не менее трех Ñимволов Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка"
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr "Введите URL-Ð°Ð´Ñ€ÐµÑ Ð²Ð°ÑˆÐµÐ³Ð¾ Ñервера Bitbucket и ключ доÑтупа"
@@ -15597,7 +15794,7 @@ msgid "Environments Dashboard"
msgstr "Панель ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸Ñми"
msgid "Environments allow you to track deployments of your application. %{linkStart}More information%{linkEnd}."
-msgstr ""
+msgstr "ÐžÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð·Ð²Ð¾Ð»ÑÑŽÑ‚ отÑлеживать развертывание вашего приложениÑ. %{linkStart}Подробнее%{linkEnd}."
msgid "Environments in %{name}"
msgstr ""
@@ -15863,9 +16060,6 @@ msgstr "Удалить цель"
msgid "Epics|Remove issue"
msgstr "Удалить обÑуждение"
-msgid "Epics|Show more"
-msgstr "Показать еще"
-
msgid "Epics|Something went wrong while creating child epics."
msgstr "Что-то пошло не так при Ñоздании дочерней цели."
@@ -15959,6 +16153,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr "Ошибка Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ ÑÑылок"
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr "Ошибка при извлечении ÑпиÑка завиÑимоÑтей. ПожалуйÑта, проверьте подключение к Ñети и попробуйте ещё раз."
@@ -16274,6 +16471,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr "Оценить"
@@ -16374,13 +16580,13 @@ msgid "Every 3 months"
msgstr ""
msgid "Every 3 months on the %{day} at %{time} %{timezone}"
-msgstr ""
+msgstr "Каждые 3 меÑÑца по %{day} в %{time} %{timezone}"
msgid "Every 6 months"
msgstr ""
msgid "Every 6 months on the %{day} at %{time} %{timezone}"
-msgstr ""
+msgstr "Каждые 6 меÑÑцев по %{day} в %{time} %{timezone}"
msgid "Every day"
msgstr ""
@@ -16389,7 +16595,7 @@ msgid "Every day (at %{time})"
msgstr ""
msgid "Every day at %{time} %{timezone}"
-msgstr ""
+msgstr "Каждый день в %{time} %{timezone}"
msgid "Every month"
msgstr "Каждый меÑÑц"
@@ -16398,7 +16604,7 @@ msgid "Every month (Day %{day} at %{time})"
msgstr ""
msgid "Every month on the %{day} at %{time} %{timezone}"
-msgstr ""
+msgstr "Каждый меÑÑц по %{day} в %{time} %{timezone}"
msgid "Every three months"
msgstr "Каждые три меÑÑца"
@@ -16417,13 +16623,13 @@ msgid "Every week (%{weekday} at %{time})"
msgstr ""
msgid "Every week on %{day} at %{time} %{timezone}"
-msgstr ""
+msgstr "Каждую неделю по %{day} в %{time} %{timezone}"
msgid "Every year"
msgstr ""
msgid "Every year on %{day} at %{time} %{timezone}"
-msgstr ""
+msgstr "Каждый год по %{day} в %{time} %{timezone}"
msgid "Everyone With Access"
msgstr "Ð’Ñе, имеющие доÑтуп"
@@ -16437,6 +16643,9 @@ msgstr "Каждый может внеÑти Ñвой вклад"
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16467,9 +16676,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16488,9 +16709,6 @@ msgstr "ИÑÐºÐ»ÑŽÑ‡Ð°Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ñ‹-ÑлиÑниÑ. Ограничено дÐ
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16545,13 +16763,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr "Развернуть боковую панель"
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16621,7 +16842,7 @@ msgid "Explore snippets"
msgstr ""
msgid "Explore topics"
-msgstr ""
+msgstr "ИÑÑледуйте теги"
msgid "Export"
msgstr ""
@@ -16871,9 +17092,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr "Ðе удаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ ÑÑылку"
-msgid "Failed to install."
-msgstr "Ðе удалоÑÑŒ уÑтановить."
-
msgid "Failed to load"
msgstr ""
@@ -17030,9 +17248,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr "Ðе удалоÑÑŒ загрузить файл карты объектов"
@@ -17289,6 +17504,9 @@ msgstr "Фев."
msgid "February"
msgstr "Февраль"
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17508,15 +17726,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17553,6 +17762,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr "Цвет Шрифта"
@@ -17653,7 +17865,7 @@ msgid "ForkProject|Private"
msgstr "Приватный"
msgid "ForkProject|Project access must be granted explicitly to each user. If this project is part of a group, access will be granted to members of the group."
-msgstr ""
+msgstr "ДоÑтуп должен быть Ñвно предоÑтавлен каждому пользователю. ЕÑли проект ÑвлÑетÑÑ Ñ‡Ð°Ñтью группы, доÑтуп будет предоÑтавлен ее учаÑтникам."
msgid "ForkProject|Public"
msgstr "Публичный"
@@ -17674,7 +17886,7 @@ msgid "ForkProject|Visibility level"
msgstr ""
msgid "ForkProject|Want to organize several dependent projects under the same namespace?"
-msgstr ""
+msgstr "Хотите организовать неÑколько завиÑимых проектов в одном проÑтранÑтве имен?"
msgid "ForkSuggestion|Cancel"
msgstr ""
@@ -17697,10 +17909,19 @@ msgstr "ВыполнÑетÑÑ Ð¾Ñ‚Ð²ÐµÑ‚Ð²Ð»ÐµÐ½Ð¸Ðµ"
msgid "Forks"
msgstr "ОтветвлениÑ"
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17732,10 +17953,10 @@ msgid "FreeUserCap|Alternatively you can upgrade to GitLab Premium or GitLab Ult
msgstr ""
msgid "FreeUserCap|Explore paid plans"
-msgstr ""
+msgstr "Обзор платных планов"
msgid "FreeUserCap|Explore paid plans:"
-msgstr ""
+msgstr "Обзор платных планов"
msgid "FreeUserCap|Looks like you've reached your limit of %{free_user_limit} members for \"%{namespace_name}\". You can't add any more, but you can manage your existing members, for example, by removing inactive members and replacing them with new members."
msgstr ""
@@ -17782,13 +18003,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "From issue creation until deploy to production"
msgstr "От ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¾Ð±ÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð´Ð¾ Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ñ€ÐµÐ°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ð¸ в рабочей Ñреде"
@@ -17831,6 +18045,9 @@ msgstr "Сборочные линии"
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr "Создать Ñтандартный набор меток"
@@ -18682,6 +18899,13 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18905,19 +19129,19 @@ msgid "GlobalSearch|In this project"
msgstr ""
msgid "GlobalSearch|Issues I've created"
-msgstr ""
+msgstr "ОбÑуждениÑ, которые Ñ Ñоздал"
msgid "GlobalSearch|Issues assigned to me"
-msgstr ""
+msgstr "ОбÑуждениÑ, назначенные мне"
msgid "GlobalSearch|Merge requests I've created"
-msgstr ""
+msgstr "ЗапроÑÑ‹ на ÑлиÑние, которые Ñ Ñоздал"
msgid "GlobalSearch|Merge requests assigned to me"
-msgstr ""
+msgstr "ЗапроÑÑ‹ на ÑлиÑние, назначенные мне"
msgid "GlobalSearch|Merge requests that I'm a reviewer"
-msgstr ""
+msgstr "ЗапроÑÑ‹ на ÑлиÑние, где Ñ Ñ€ÐµÐ²ÑŒÑŽÐµÑ€"
msgid "GlobalSearch|Projects"
msgstr ""
@@ -18931,6 +19155,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18958,6 +19185,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -19135,6 +19365,9 @@ msgstr "Перейти к Ñвоим запроÑам на ÑлиÑние"
msgid "Go to your projects"
msgstr "Перейдити к Ñвоим проектам"
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr "Перейти к Ñвоим Ñниппетам"
@@ -19228,6 +19461,12 @@ msgstr ""
msgid "Graph"
msgstr "Диаграмма"
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19250,10 +19489,10 @@ msgid "Group %{group_name} and its Mattermost team were successfully created."
msgstr ""
msgid "Group %{group_name} couldn't be exported."
-msgstr ""
+msgstr "Группа %{group_name} не может быть ÑкÑпортирована."
msgid "Group %{group_name} was exported successfully."
-msgstr ""
+msgstr "Группа %{group_name} уÑпешно ÑкÑпортирована."
msgid "Group %{group_name} was scheduled for deletion."
msgstr "Группа %{group_name} была запланирована Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ."
@@ -19283,7 +19522,7 @@ msgid "Group access token creation is disabled in this group. You can still use
msgstr ""
msgid "Group application: %{name}"
-msgstr ""
+msgstr "Приложение группы: %{name}"
msgid "Group applications"
msgstr ""
@@ -19358,7 +19597,7 @@ msgid "Group name"
msgstr "Ðазвание группы"
msgid "Group name (your organization)"
-msgstr ""
+msgstr "Ðазвание группы (вашей организации)"
msgid "Group navigation"
msgstr ""
@@ -19376,7 +19615,7 @@ msgid "Group pipeline minutes were successfully reset."
msgstr "Минуты групповой Ñборочной линии уÑпешно Ñброшены."
msgid "Group project URLs are prefixed with the group namespace"
-msgstr ""
+msgstr "URL-адреÑа групповых проектов имеют Ð¿Ñ€ÐµÑ„Ð¸ÐºÑ Ð¿Ñ€Ð¾ÑтранÑтва имен группы."
msgid "Group requires separate account"
msgstr ""
@@ -19417,7 +19656,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19504,6 +19743,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19598,7 +19840,7 @@ msgid "GroupSAML|Reset SCIM token"
msgstr ""
msgid "GroupSAML|Role to assign members of this SAML group."
-msgstr ""
+msgstr "Роль Ð´Ð»Ñ Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñ‡Ð»ÐµÐ½Ð¾Ð² Ñтой группы SAML."
msgid "GroupSAML|SAML Group Links"
msgstr ""
@@ -19607,6 +19849,9 @@ msgid "GroupSAML|SAML Group Name"
msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
+msgstr "Ð˜Ð¼Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹ SAML: %{saml_group_name}"
+
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
msgstr ""
msgid "GroupSAML|SAML Response Output"
@@ -19634,7 +19879,7 @@ msgid "GroupSAML|The SCIM token is now hidden. To see the value of the token aga
msgstr ""
msgid "GroupSAML|The case-sensitive group name that will be sent by the SAML identity provider."
-msgstr ""
+msgstr "Ð˜Ð¼Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹, чувÑтвительное к региÑтру, которое будет отправлено SAML идентификатором провайдера."
msgid "GroupSAML|This will be set as the access level of users added to the group."
msgstr ""
@@ -19780,7 +20025,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19796,14 +20041,11 @@ msgid "GroupSettings|Set a size limit for all content in each Pages site in this
msgstr ""
msgid "GroupSettings|Set the initial name and protections for the default branch of new repositories created in the group."
-msgstr ""
+msgstr "УÑтановите Ð¸Ð¼Ñ Ð²ÐµÑ‚ÐºÐ¸ по умолчанию и права доÑтупа Ð´Ð»Ñ Ð½Ð¾Ð²Ñ‹Ñ… репозиториев, Ñозданных в группе."
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr "При обновлении Ñборочной линии Auto DevOps возникла проблема: %{error_messages}."
@@ -19895,7 +20137,7 @@ msgid "GroupsEmptyState|Create new project"
msgstr ""
msgid "GroupsEmptyState|Create new subgroup"
-msgstr ""
+msgstr "Создать новую подгруппу"
msgid "GroupsEmptyState|Groups are the best way to manage multiple projects and members."
msgstr ""
@@ -19919,13 +20161,13 @@ msgid "GroupsEmptyState|You do not have necessary permissions to create a subgro
msgstr ""
msgid "GroupsNew|%{groupsLinkStart}Groups%{groupsLinkEnd} and %{subgroupsLinkStart}subgroups%{subgroupsLinkEnd} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
-msgstr ""
+msgstr "%{groupsLinkStart}Группы%{groupsLinkEnd} и %{subgroupsLinkStart}подгруппы%{subgroupsLinkEnd} позволÑÑŽÑ‚ управлÑÑ‚ÑŒ и взаимодейÑтвовать между неÑколькими проектами. УчаÑтники группы имеют доÑтуп ко вÑем его проектам."
msgid "GroupsNew|%{linkStart}Groups%{linkEnd} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
-msgstr ""
+msgstr "%{linkStart}Группы%{linkEnd} позволÑÑŽÑ‚ вам управлÑÑ‚ÑŒ и Ñотрудничать в неÑкольких проектах. УчаÑтники группы имеют доÑтуп ко вÑем ее проектам."
msgid "GroupsNew|Assemble related projects together and grant members access to several projects at once."
-msgstr ""
+msgstr "Соберите ÑвÑзанные проекты вмеÑте и предоÑтавьте учаÑтникам доÑтуп к неÑкольким проектам одновременно."
msgid "GroupsNew|Connect instance"
msgstr ""
@@ -19933,26 +20175,26 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr "Создать группу"
msgid "GroupsNew|Create new group"
-msgstr ""
+msgstr "Создать новую группу"
msgid "GroupsNew|Create subgroup"
-msgstr ""
-
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
+msgstr "Создать подгруппу"
msgid "GroupsNew|GitLab source URL"
msgstr ""
msgid "GroupsNew|Groups can also be nested by creating %{linkStart}subgroups%{linkEnd}."
-msgstr ""
+msgstr "Группы также могут быть вложены путем ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ %{linkStart}подгрупп%{linkEnd}."
msgid "GroupsNew|Import a group and related data from another GitLab instance."
-msgstr ""
+msgstr "Импорт группы и ÑвÑзанных данных из другого ÑкземплÑра GitLab."
msgid "GroupsNew|Import group"
msgstr "Импорт группы"
@@ -20027,7 +20269,7 @@ msgid "Groups|Checking group URL availability..."
msgstr ""
msgid "Groups|Enter a descriptive name for your group."
-msgstr ""
+msgstr "Введите опиÑательное название Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ¹ группы."
msgid "Groups|Group ID"
msgstr ""
@@ -20042,7 +20284,7 @@ msgid "Groups|Group description (optional)"
msgstr ""
msgid "Groups|Group name"
-msgstr ""
+msgstr "Ðазвание группы"
msgid "Groups|Group path is available."
msgstr ""
@@ -20054,13 +20296,13 @@ msgid "Groups|Learn more"
msgstr ""
msgid "Groups|Learn more about subgroups"
-msgstr ""
+msgstr "Узнайте больше о подгруппах"
msgid "Groups|Members, projects, trials, and paid subscriptions are tied to a specific top-level group. If you are already a member of a top-level group, you can create a subgroup so your new work is part of your existing top-level group. Do you want to create a subgroup instead?"
-msgstr ""
+msgstr "УчаÑтники, проекты, пробные и платные подпиÑки привÑзаны к конкретной группе верхнего уровнÑ. ЕÑли вы уже ÑвлÑетеÑÑŒ учаÑтником группы верхнего уровнÑ, вы можете Ñоздать подгруппу, чтобы ваша Ð½Ð¾Ð²Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ð° была чаÑтью вашей ÑущеÑтвующей группы верхнего уровнÑ. Ð’Ñ‹ хотите Ñоздать подгруппу?"
msgid "Groups|Must start with letter, digit, emoji, or underscore. Can also contain periods, dashes, spaces, and parentheses."
-msgstr ""
+msgstr "Должно начинатьÑÑ Ñ Ð±ÑƒÐºÐ²Ñ‹, цифры, Ñмайлика или подчеркиваниÑ. Может также Ñодержать точки, тире, пробелы и Ñкобки."
msgid "Groups|Remove avatar"
msgstr ""
@@ -20075,7 +20317,7 @@ msgid "Groups|Subgroup name"
msgstr ""
msgid "Groups|Subgroup slug"
-msgstr ""
+msgstr "URL-Ð°Ð´Ñ€ÐµÑ Ð¿Ð¾Ð´Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹"
msgid "Groups|You're creating a new top-level group"
msgstr ""
@@ -20170,7 +20412,7 @@ msgid "HarborRegistry|Please try different search criteria"
msgstr ""
msgid "HarborRegistry|Published %{timeInfo}"
-msgstr ""
+msgstr "Опубликовано %{timeInfo}"
msgid "HarborRegistry|Root image"
msgstr ""
@@ -20185,7 +20427,7 @@ msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
-msgstr ""
+msgstr "Извините, ваш фильтр не дал результатов."
msgid "HarborRegistry|Tag"
msgstr ""
@@ -20200,7 +20442,7 @@ msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
-msgstr ""
+msgstr "Чтобы раÑширить поиÑк, измените или удалите указанные выше фильтры."
msgid "HarborRegistry|We are having trouble connecting to the Harbor Registry. Please try refreshing the page. If this error persists, please review %{docLinkStart}the documentation%{docLinkEnd}."
msgstr ""
@@ -20426,12 +20668,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr "ПриÑутÑтвуют уÑзвимоÑти выÑокого или неизвеÑтного уровнÑ"
msgid "Highest role:"
msgstr "ÐаивыÑÑˆÐ°Ñ Ñ€Ð¾Ð»ÑŒ:"
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20465,9 +20713,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr "Ðе удалоÑÑŒ выполнить обработчик. УбедитеÑÑŒ, что группа имеет проект Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð°Ð¼Ð¸."
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20532,19 +20777,19 @@ msgid "I forgot my password"
msgstr "Я забыл пароль"
msgid "I want to explore GitLab to see if it’s worth switching to"
-msgstr ""
+msgstr "Я хочу изучить GitLab, чтобы понÑÑ‚ÑŒ, Ñтоит ли переходить"
msgid "I want to learn the basics of Git"
-msgstr ""
+msgstr "Я хочу изучить оÑновы Git"
msgid "I want to move my repository to GitLab from somewhere else"
-msgstr ""
+msgstr "Я хочу перенеÑти Ñвой репозиторий в GitLab из другого меÑта"
msgid "I want to store my code"
-msgstr ""
+msgstr "Я хочу хранить Ñвой код"
msgid "I want to use GitLab CI with my existing repository"
-msgstr ""
+msgstr "Я хочу иÑпользовать GitLab CI Ñ Ð¼Ð¾Ð¸Ð¼ ÑущеÑтвующим репозиторием"
msgid "I'd like to receive updates about GitLab via email"
msgstr ""
@@ -20636,6 +20881,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20660,6 +20908,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20699,7 +20950,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20741,6 +20992,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20753,9 +21010,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -21107,6 +21373,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21674,12 +21943,18 @@ msgstr "Инцидент"
msgid "Incident Management Limits"
msgstr "Лимиты, ÑвÑзанные Ñ Ð£Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸ÐµÐ¼ инцидентами"
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21711,7 +21986,7 @@ msgid "IncidentManagement|Assign paging status"
msgstr ""
msgid "IncidentManagement|Assignees"
-msgstr ""
+msgstr "ОтветÑтвеные"
msgid "IncidentManagement|Closed"
msgstr ""
@@ -21765,10 +22040,10 @@ msgid "IncidentManagement|Paged"
msgstr ""
msgid "IncidentManagement|Published"
-msgstr ""
+msgstr "Опубликовано"
msgid "IncidentManagement|Published to status page"
-msgstr ""
+msgstr "Опубликовано на Ñтранице ÑоÑтоÑниÑ"
msgid "IncidentManagement|Resolved"
msgstr ""
@@ -21998,14 +22273,17 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr "Указывает, может ли Ñтот обработчик заданий выбирать Ð·Ð°Ð´Ð°Ð½Ð¸Ñ Ð±ÐµÐ· тегов"
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr "Сообщать пользователÑм без загруженных SSH ключей, что они не могут отправлÑÑ‚ÑŒ через SSH до тех пор, пока Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ один не будет добавлен"
msgid "Infrastructure"
-msgstr ""
+msgstr "ИнфраÑтруктура"
msgid "Infrastructure Registry"
-msgstr ""
+msgstr "РееÑÑ‚Ñ€ Terraform"
msgid "Infrastructure as Code (IaC) Scanning"
msgstr ""
@@ -22023,7 +22301,7 @@ msgid "InfrastructureRegistry|For more information on the Terraform registry, %{
msgstr ""
msgid "InfrastructureRegistry|Infrastructure Registry"
-msgstr ""
+msgstr "РееÑÑ‚Ñ€ Terraform"
msgid "InfrastructureRegistry|Publish and share your modules. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -22032,13 +22310,13 @@ msgid "InfrastructureRegistry|Terraform"
msgstr ""
msgid "InfrastructureRegistry|Terraform modules are the main way to package and reuse resource configurations with Terraform. Learn more about how to %{noPackagesLinkStart}create Terraform modules%{noPackagesLinkEnd} in GitLab."
-msgstr ""
+msgstr "Модули Terraform ÑвлÑÑŽÑ‚ÑÑ Ð¾Ñновным ÑпоÑобом упаковки и повторного иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¹ реÑурÑов Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Terraform. Узнайте больше о том, как %{noPackagesLinkStart}Ñоздавать модули Terraform%{noPackagesLinkEnd} в GitLab."
msgid "InfrastructureRegistry|To authorize access to the Terraform registry:"
msgstr ""
msgid "InfrastructureRegistry|You have no Terraform modules in your project"
-msgstr ""
+msgstr "В вашем проекте нет модулей Terraform"
msgid "Inherited"
msgstr ""
@@ -22272,6 +22550,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -22305,6 +22586,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22468,10 +22752,10 @@ msgid "Internal"
msgstr "Внутренний"
msgid "Internal - The group and any internal projects can be viewed by any logged in user except external users."
-msgstr ""
+msgstr "Внутренний - Группа и любые внутренние проекты будут доÑтупны любым зарегиÑтрированным пользователем, за иÑключением внешних пользователей."
msgid "Internal - The project can be accessed by any logged in user except external users."
-msgstr ""
+msgstr "Внутренний — ДоÑтуп к проекту может получить любой зарегиÑтрированный пользователь, кроме внешних пользователей."
msgid "Internal error occurred while delivering this webhook."
msgstr ""
@@ -22653,6 +22937,9 @@ msgstr "Приглашайте коллег"
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr "Мы заметили, что вы никого не приглаÑили в Ñту группу. ПриглаÑите коллег, чтобы вы могли учаÑтвовать в обÑуждениÑÑ…, вмеÑте работать над запроÑами на ÑлиÑние и делитьÑÑ Ð·Ð½Ð°Ð½Ð¸Ñми."
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22701,7 +22988,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr "УчаÑтники уÑпешно добавлены"
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22883,16 +23170,16 @@ msgid "Is using seat"
msgstr "ИÑпользует меÑто"
msgid "IssuableEvents|assigned to"
-msgstr ""
+msgstr "назначил(а)"
msgid "IssuableEvents|removed review request for"
-msgstr ""
+msgstr "удалил(а) Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ревью у"
msgid "IssuableEvents|requested review from"
-msgstr ""
+msgstr "запроÑил(а) ревью у"
msgid "IssuableEvents|unassigned"
-msgstr ""
+msgstr "убрал(а) ответÑтвенного"
msgid "IssuableStatus|%{wi_type} created %{created_at} by "
msgstr ""
@@ -23170,9 +23457,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -23311,9 +23595,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23746,9 +24027,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23899,6 +24177,9 @@ msgstr "Эта задание оÑтановлено, потому что про
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23980,6 +24261,9 @@ msgstr "Ключ"
msgid "Key (PEM)"
msgstr "Ключ (PEM)"
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -24026,10 +24310,10 @@ msgid "Kubernetes Cluster"
msgstr "КлаÑтер Kubernetes"
msgid "Kubernetes Clusters"
-msgstr ""
+msgstr "КлаÑтеры Kubernetes"
msgid "Kubernetes cluster"
-msgstr ""
+msgstr "КлаÑтер Kubernetes"
msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
msgstr "Ð’Ñ€ÐµÐ¼Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ ÐºÐ»Ð°Ñтера Kubernetes превышает Ð²Ñ€ÐµÐ¼Ñ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ; %{timeout}"
@@ -24044,14 +24328,11 @@ msgid "Kubernetes cluster was successfully updated."
msgstr "КлаÑтер Kubernetes был уÑпешно обновлён."
msgid "Kubernetes clusters"
-msgstr ""
+msgstr "КлаÑтеры Kubernetes"
msgid "Kubernetes deployment not found"
msgstr "Развёртывание Kubernetes не найдено"
-msgid "Kubernetes error: %{error_code}"
-msgstr "Ошибка Kubernetes: %{error_code}"
-
msgid "LDAP"
msgstr "LDAP"
@@ -24128,15 +24409,18 @@ msgstr "Метки"
msgid "Labels"
msgstr "Метки"
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr "Метки могут быть применены к %{features}. Групповые метки доÑтупны Ð´Ð»Ñ Ð»ÑŽÐ±Ð¾Ð³Ð¾ проекта внутри группы."
-
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "Метки могут применÑÑ‚ÑŒÑÑ Ðº обÑуждениÑм и запроÑам на ÑлиÑние Ð´Ð»Ñ Ð¸Ñ… категоризации."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -24225,6 +24509,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -24330,6 +24617,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr "Узнайте, как %{link_start}внеÑти Ñвой вклад во вÑтроенные шаблоны%{link_end}"
@@ -25017,12 +25307,12 @@ msgstr "Заблокированные файлы"
msgid "Locked by %{fileLockUserName}"
msgstr "Заблокировано %{fileLockUserName}"
+msgid "Locked files"
+msgstr ""
+
msgid "Locked the discussion."
msgstr "ДиÑкуÑÑÐ¸Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð°."
-msgid "Locks give the ability to lock specific file or folder."
-msgstr ""
-
msgid "Locks the discussion."
msgstr "Блокирует диÑкуÑÑию."
@@ -25071,6 +25361,9 @@ msgstr ""
msgid "Logs"
msgstr "Журналы"
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -25176,6 +25469,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25674,6 +25970,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr "ИзмерÑетÑÑ Ð² байтах кода. ИÑключает Ñгенерированный и вендорÑкий код."
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25695,6 +25994,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr "УчаÑтники"
@@ -25824,7 +26129,7 @@ msgid "Members|Reverted to LDAP group sync settings."
msgstr ""
msgid "Members|Role updated successfully."
-msgstr ""
+msgstr "Роль уÑпешно обновлена."
msgid "Members|Search groups"
msgstr ""
@@ -26004,7 +26309,7 @@ msgid "MergeConflict|origin//their changes"
msgstr ""
msgid "MergeRequestAnalytics|Assignees"
-msgstr ""
+msgstr "ОтветÑтвенные"
msgid "MergeRequestAnalytics|Date Merged"
msgstr ""
@@ -26124,28 +26429,28 @@ msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
msgid "MergeTopics|%{sourceTopic} will be removed"
-msgstr ""
+msgstr "%{sourceTopic} будет удален"
msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
-msgstr ""
+msgstr "Ð’Ñе выбранные проекты будут перемещены в %{targetTopic}"
msgid "MergeTopics|Merge topics"
-msgstr ""
+msgstr "Объединить теги"
msgid "MergeTopics|Merging topics will cause the following:"
-msgstr ""
+msgstr "Объединение тегов приведет к Ñледующему:"
msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
-msgstr ""
+msgstr "Переназначить тег Ð´Ð»Ñ Ð²Ñех выбранных проектов."
msgid "MergeTopics|Source topic"
-msgstr ""
+msgstr "ИÑходный тег"
msgid "MergeTopics|Target topic"
-msgstr ""
+msgstr "Конечный тег"
msgid "MergeTopics|This action cannot be undone."
-msgstr ""
+msgstr "Это дейÑтвие Ð½ÐµÐ»ÑŒÐ·Ñ Ð±ÑƒÐ´ÐµÑ‚ отменить."
msgid "Merged"
msgstr "Слито"
@@ -26280,7 +26585,7 @@ msgid "MetricsSettings|Choose whether to display dashboard metrics in UTC or the
msgstr ""
msgid "MetricsSettings|Dashboard timezone"
-msgstr ""
+msgstr "ЧаÑовой поÑÑ Ð¿Ð°Ð½ÐµÐ»Ð¸ управлениÑ"
msgid "MetricsSettings|External dashboard URL"
msgstr ""
@@ -26295,7 +26600,7 @@ msgid "MetricsSettings|UTC (Coordinated Universal Time)"
msgstr ""
msgid "MetricsSettings|User's local timezone"
-msgstr ""
+msgstr "ЧаÑовой поÑÑ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
msgid "MetricsUsersStarredDashboards|Dashboard with requested path can not be found"
msgstr ""
@@ -26830,6 +27135,9 @@ msgstr ""
msgid "Modal|Close"
msgstr "Закрыть"
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26893,9 +27201,6 @@ msgstr "Подробнее"
msgid "More information"
msgstr "Ð”Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ"
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26906,7 +27211,7 @@ msgid "More than %{number_commits_distance} commits different with %{default_bra
msgstr "Более %{number_commits_distance} коммитов отличаетÑÑ Ð¾Ñ‚ %{default_branch}"
msgid "More topics"
-msgstr ""
+msgstr "Больше тегов"
msgid "Most common"
msgstr ""
@@ -27020,7 +27325,7 @@ msgid "My company or team"
msgstr "ÐœÐ¾Ñ ÐºÐ¾Ð¼Ð¿Ð°Ð½Ð¸Ñ Ð¸Ð»Ð¸ команда"
msgid "My topic"
-msgstr ""
+msgstr "Мой тег"
msgid "My-Reaction"
msgstr "ÐœÐ¾Ñ Ñ€ÐµÐ°ÐºÑ†Ð¸Ñ"
@@ -27107,6 +27412,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -27288,6 +27596,9 @@ msgstr "ÐÐ¾Ð²Ð°Ñ Ð²ÐµÑ‚ÐºÐ°"
msgid "New branch unavailable"
msgstr "ÐÐ¾Ð²Ð°Ñ Ð²ÐµÑ‚ÐºÐ° недоÑтупна"
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -27330,6 +27641,12 @@ msgstr "Ðовый токен доÑтупа Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ¸ работÐ
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "Ðовое обÑуждение"
@@ -27409,7 +27726,7 @@ msgid "New test case"
msgstr ""
msgid "New topic"
-msgstr ""
+msgstr "Ðовый тег"
msgid "New users set to external"
msgstr ""
@@ -27496,7 +27813,7 @@ msgid "No artifacts found"
msgstr ""
msgid "No assignee"
-msgstr ""
+msgstr "Ðет ответÑтвенного"
msgid "No authentication methods configured."
msgstr "Методы аутентификации не наÑтроены."
@@ -27603,9 +27920,6 @@ msgstr ""
msgid "No iteration"
msgstr "Ðет итераций"
-msgid "No iterations to show"
-msgstr "Ðет итераций Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ"
-
msgid "No job log"
msgstr ""
@@ -27705,6 +28019,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27933,17 +28250,17 @@ msgstr "ÐаÑтройки уведомлений Ñохранены"
msgid "NotificationEmail|Assignee"
msgid_plural "NotificationEmail|Assignees"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "ОтветÑтвенный"
+msgstr[1] "ОтветÑтвенные"
+msgstr[2] "ОтветÑтвенные"
+msgstr[3] "ОтветÑтвенные"
msgid "NotificationEmail|Assignee: %{users}"
msgid_plural "NotificationEmail|Assignees: %{users}"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "ОтветÑтвенный: %{users}"
+msgstr[1] "ОтветÑтвенные: %{users}"
+msgstr[2] "ОтветÑтвенные: %{users}"
+msgstr[3] "ОтветÑтвенные: %{users}"
msgid "NotificationEmail|Reviewer"
msgid_plural "NotificationEmail|Reviewers"
@@ -28329,7 +28646,7 @@ msgid "November"
msgstr "ÐоÑбрь"
msgid "Now, personalize your GitLab experience"
-msgstr ""
+msgstr "Теперь перÑонализируйте Ñвой опыт работы в GitLab"
msgid "Nuget metadatum must have at least license_url, project_url or icon_url set"
msgstr ""
@@ -28373,9 +28690,24 @@ msgstr ""
msgid "OK"
msgstr "ОК"
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28546,10 +28878,10 @@ msgid "OnCallSchedules|Select participant"
msgstr ""
msgid "OnCallSchedules|Select timezone"
-msgstr ""
+msgstr "Выберите чаÑовой поÑÑ"
msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
-msgstr ""
+msgstr "УÑтанавливает чаÑовой поÑÑ Ð¿Ð¾ умолчанию Ð´Ð»Ñ Ñ€Ð°ÑпиÑаниÑ, Ð´Ð»Ñ Ð²Ñех учаÑтников"
msgid "OnCallSchedules|Successfully created a new rotation"
msgstr ""
@@ -28756,6 +29088,9 @@ msgid "OnDemandScans|There are no scheduled scans."
msgstr ""
msgid "OnDemandScans|Timezone"
+msgstr "ЧаÑовой поÑÑ"
+
+msgid "OnDemandScans|Verify configuration"
msgstr ""
msgid "OnDemandScans|View results"
@@ -28837,9 +29172,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28945,9 +29277,6 @@ msgstr "ОткроетÑÑ Ð² новом окне"
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr "ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð½Ðµ удалаÑÑŒ. Проверьте журналы пода Ð´Ð»Ñ %{pod_name} Ð´Ð»Ñ Ð±Ð¾Ð»ÐµÐµ подробной информации."
-
msgid "Operation not allowed"
msgstr ""
@@ -29233,6 +29562,9 @@ msgstr "Conan"
msgid "PackageRegistry|Conan Command"
msgstr "Команда Conan"
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr "Копировать Ñодержимое .pypirc"
@@ -29324,6 +29656,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -29339,6 +29674,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -29363,6 +29704,18 @@ msgstr "Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информацÐ
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr "Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации о рееÑтре PyPi %{linkStart}Ñмотрите документацию%{linkEnd}."
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29382,10 +29735,10 @@ msgid "PackageRegistry|Helm"
msgstr ""
msgid "PackageRegistry|Help us learn about your registry migration needs"
-msgstr ""
+msgstr "Помогите нам узнать о ваших потребноÑÑ‚ÑÑ…"
msgid "PackageRegistry|If you are interested in migrating packages from your private registry to the GitLab Package Registry, take our survey and tell us more about your needs."
-msgstr ""
+msgstr "ЕÑли вы заинтереÑованы в переноÑе пакетов из вашего чаÑтного Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Ð² рееÑÑ‚Ñ€ пакетов GitLab, пройдите наш Ð¾Ð¿Ñ€Ð¾Ñ Ð¸ раÑÑкажите нам больше о ваших потребноÑÑ‚ÑÑ…."
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr "ЕÑли вы еще не Ñделали Ñтого, вам нужно будет добавить нижераÑположенное в Ñвой файл %{codeStart}.pypirc%{codeEnd}."
@@ -29435,6 +29788,9 @@ msgstr "Команда NuGet"
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr "РееÑÑ‚Ñ€ пакетов"
@@ -29450,6 +29806,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29460,6 +29819,9 @@ msgstr[3] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29514,6 +29876,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29536,7 +29901,7 @@ msgid "PackageRegistry|Source project located at %{link}"
msgstr ""
msgid "PackageRegistry|Take survey"
-msgstr ""
+msgstr "Пройти опроÑ"
msgid "PackageRegistry|Target SHA: %{sha}"
msgstr ""
@@ -29562,9 +29927,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr "Чтобы раÑширить результаты поиÑка, измените или удалите фильтры выше."
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29587,6 +29949,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29602,6 +29971,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr "опубликовано %{author}"
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29671,6 +30043,9 @@ msgstr "Параметр"
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr "Параметр \"job_id\" не может превышать длину %{job_id_max_size}"
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29734,6 +30109,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29977,6 +30361,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr "Выберите имÑ"
@@ -30892,6 +31300,9 @@ msgstr "ПожалуйÑта, проверьте почту (%{email}), чтоб
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr "ПожалуйÑта, заполните Ð°Ð´Ñ€ÐµÑ Ñлектронной почты в Ñвоем профиле"
@@ -30956,10 +31367,10 @@ msgid "Please fill in a descriptive name for your group."
msgstr ""
msgid "Please fill in a name for your topic."
-msgstr ""
+msgstr "ПожалуйÑта, введите Ð¸Ð¼Ñ Ñ‚ÐµÐ³Ð°."
msgid "Please fill in a title for your topic."
-msgstr ""
+msgstr "ПожалуйÑта, введите заголовок тега."
msgid "Please fill out this field."
msgstr "ПожалуйÑта, заполните Ñто поле."
@@ -31048,9 +31459,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr "ПожалуйÑта, введите %{phrase_code} чтобы продолжить, или закройте Ñто окно Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹."
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr "ПожалуйÑта, иÑпользуйте Ñту форму Ð´Ð»Ñ Ð¾Ð¿Ð¾Ð²ÐµÑ‰ÐµÐ½Ð¸Ñ Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñтраторов о пользователÑÑ…, Ñоздающих задачи и комментарии, Ñодержащие Ñпам или ведущих ÑÐµÐ±Ñ Ð½ÐµÐ°Ð´ÐµÐºÐ²Ð°Ñ‚Ð½Ð¾."
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -31204,6 +31612,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "Выберите, какое Ñодержимое вы хотите видеть на Ñтранице обзора проекта."
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -31288,6 +31699,9 @@ msgstr "Тема подÑветки ÑинтакÑиÑа"
msgid "Preferences|Tab width"
msgstr "Ширина табулÑций"
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -31300,9 +31714,15 @@ msgstr "Этот параметр позволÑет наÑтроить шири
msgid "Preferences|Time preferences"
msgstr "ÐаÑтройки времени"
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr "ИÑпользовать отноÑительное времÑ"
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -31397,7 +31817,7 @@ msgid "Private - Guest users are not allowed to view detailed release informatio
msgstr ""
msgid "Private - Project access must be granted explicitly to each user. If this project is part of a group, access will be granted to members of the group."
-msgstr "Приватный - ДоÑтуп должен быть предоÑтавлен Ñвно каждому пользователю. ЕÑли проект ÑвлÑетÑÑ Ñ‡Ð°Ñтью группы, он наÑледует учаÑтников и их привилегии."
+msgstr "Приватный - ДоÑтуп должен быть Ñвно предоÑтавлен каждому пользователю. ЕÑли проект ÑвлÑетÑÑ Ñ‡Ð°Ñтью группы, доÑтуп будет предоÑтавлен ее учаÑтникам."
msgid "Private - The group and its projects can only be viewed by members."
msgstr "ÐŸÑ€Ð¸Ð²Ð°Ñ‚Ð½Ð°Ñ - Группу и включённые в неё проекты могут видеть только члены группы."
@@ -31420,16 +31840,25 @@ msgstr ""
msgid "Proceed"
msgstr "Продолжить"
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31517,7 +31946,7 @@ msgid "Profiles|@username"
msgstr "@username"
msgid "Profiles|Account could not be deleted. GitLab was unable to verify your identity."
-msgstr ""
+msgstr "Ð£Ñ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ не может быть удалена. GitLab не может подтвердить вашу личноÑÑ‚ÑŒ."
msgid "Profiles|Account scheduled for removal."
msgstr "Ð£Ñ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ запланирована к удалению."
@@ -31529,7 +31958,7 @@ msgid "Profiles|Add key"
msgstr "Добавить ключ"
msgid "Profiles|An error occurred while updating your username, please try again."
-msgstr ""
+msgstr "При обновлении имени Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð¾ÑˆÐ»Ð° ошибка, попробуйте ещё раз."
msgid "Profiles|Avatar cropper"
msgstr "Обрезать аватар"
@@ -31538,7 +31967,7 @@ msgid "Profiles|Avatar will be removed. Are you sure?"
msgstr "Ðватар будет удален. Ð’Ñ‹ уверены?"
msgid "Profiles|Begins with %{ssh_key_algorithms}."
-msgstr ""
+msgstr "ÐачинаетÑÑ Ñ %{ssh_key_algorithms}."
msgid "Profiles|Bio"
msgstr "О Ñебе"
@@ -31553,7 +31982,7 @@ msgid "Profiles|Choose file..."
msgstr "Выбрать файл..."
msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
-msgstr ""
+msgstr "Выберите, чтобы отобразить вклад в закрытых проектах в вашем профиле без какой-либо информации о проекте, репозитории или организации."
msgid "Profiles|City, country"
msgstr "Город, Ñтрана"
@@ -31562,10 +31991,10 @@ msgid "Profiles|Commit email"
msgstr "Email коммита"
msgid "Profiles|Connect %{provider}"
-msgstr ""
+msgstr "Подключить %{provider}"
msgid "Profiles|Connect a service for sign-in."
-msgstr ""
+msgstr "Выберите ÑÐµÑ€Ð²Ð¸Ñ Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ð°."
msgid "Profiles|Connected Accounts"
msgstr "Подключенные аккаунты"
@@ -31592,7 +32021,7 @@ msgid "Profiles|Disconnect"
msgstr "Отключить"
msgid "Profiles|Disconnect %{provider}"
-msgstr ""
+msgstr "Отключить %{provider}"
msgid "Profiles|Do not show on profile"
msgstr "Ðе показывать в профиле"
@@ -31604,31 +32033,31 @@ msgid "Profiles|Edit Profile"
msgstr "Изменить профиль"
msgid "Profiles|Ensure you have two-factor authentication recovery codes stored in a safe place."
-msgstr ""
+msgstr "УбедитеÑÑŒ, что у Ð²Ð°Ñ ÐµÑÑ‚ÑŒ коды воÑÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð´Ð²ÑƒÑ…Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð¾Ð¹ аутентификации, хранÑщиеÑÑ Ð² надежном меÑте."
msgid "Profiles|Enter how your name is pronounced to help people address you correctly."
-msgstr ""
+msgstr "Укажите, как произноÑитÑÑ Ð²Ð°ÑˆÐµ имÑ, чтобы люди обращалиÑÑŒ к вам правильно"
msgid "Profiles|Enter your name, so people you know can recognize you."
-msgstr ""
+msgstr "Введите ваше имÑ, чтобы люди могли раÑпознать ваÑ."
msgid "Profiles|Enter your password to confirm the email change"
-msgstr ""
+msgstr "Введите ваш пароль Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð°Ð´Ñ€ÐµÑа Ñлектронной почты"
msgid "Profiles|Enter your pronouns to let people know how to refer to you."
msgstr ""
msgid "Profiles|Example: MacBook key"
-msgstr ""
+msgstr "Пример: ключ MacBook"
msgid "Profiles|Expiration date"
-msgstr ""
+msgstr "Дата иÑÑ‚ÐµÑ‡ÐµÐ½Ð¸Ñ Ñрока дейÑтвиÑ"
msgid "Profiles|Expired:"
-msgstr ""
+msgstr "Срок дейÑÑ‚Ð²Ð¸Ñ Ð¸Ñтек:"
msgid "Profiles|Expires:"
-msgstr ""
+msgstr "ИÑтекает:"
msgid "Profiles|Feed token was successfully reset"
msgstr "Токен новоÑтной ленты уÑпешно Ñброшен"
@@ -31637,10 +32066,10 @@ msgid "Profiles|Full name"
msgstr "Полное имÑ"
msgid "Profiles|GitLab is unable to verify your identity automatically. For security purposes, you must set a password by %{openingTag}resetting your password%{closingTag} to delete your account."
-msgstr ""
+msgstr "GitLab не может автоматичеÑки проверить вашу личноÑÑ‚ÑŒ. Ð’ целÑÑ… безопаÑноÑти вы должны задать пароль путём %{openingTag}ÑброÑа%{closingTag} Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ ÑƒÑ‡Ñ‘Ñ‚Ð½Ð¾Ð¹ запиÑи."
msgid "Profiles|If after setting a password, the option to delete your account is still not available, please %{link_start}submit a request%{link_end} to begin the account deletion process."
-msgstr ""
+msgstr "ЕÑли поÑле уÑтановки Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑ‚ÑŒ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð²Ð°ÑˆÐµÐ¹ учетной запиÑи по-прежнему недоÑтупна, %{link_start}запроÑ%{link_end} , чтобы начать процеÑÑ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ ÑƒÑ‡ÐµÑ‚Ð½Ð¾Ð¹ запиÑи."
msgid "Profiles|Include private contributions on my profile"
msgstr "Включить вклад в приватные проекты в мой профиль"
@@ -31658,19 +32087,19 @@ msgid "Profiles|Invalid username"
msgstr "Ðеверное Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
msgid "Profiles|Job title"
-msgstr ""
+msgstr "ДолжноÑÑ‚ÑŒ"
msgid "Profiles|Key"
msgstr "Ключ"
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
-msgstr ""
+msgstr "С Ñтой даты ключ ÑтановитÑÑ Ð½ÐµÐ´ÐµÐ¹Ñтвительным. МакÑимальное Ð²Ñ€ÐµÐ¼Ñ Ð¶Ð¸Ð·Ð½Ð¸ ключей SSH – %{max_ssh_key_lifetime}  дней."
msgid "Profiles|Key titles are publicly visible."
-msgstr ""
+msgstr "Заголовки ключей общедоÑтупны."
msgid "Profiles|Last used:"
-msgstr ""
+msgstr "ПоÑледнее иÑпользование:"
msgid "Profiles|Learn more"
msgstr "Узнать больше"
@@ -31685,10 +32114,10 @@ msgid "Profiles|Main settings"
msgstr "ОÑновные наÑтройки"
msgid "Profiles|Manage two-factor authentication"
-msgstr ""
+msgstr "Управление двухфакторной аутентификацией"
msgid "Profiles|No file chosen."
-msgstr ""
+msgstr "Файл не выбран."
msgid "Profiles|Notification email"
msgstr "Ð­Ð»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð°Ñ Ð¿Ð¾Ñ‡Ñ‚Ð° уведомлений"
@@ -31715,70 +32144,70 @@ msgid "Profiles|Profile was successfully updated"
msgstr "Профиль уÑпешно обновлен"
msgid "Profiles|Pronouns"
-msgstr ""
+msgstr "МеÑтоимениÑ"
msgid "Profiles|Pronunciation"
-msgstr ""
+msgstr "Произношение"
msgid "Profiles|Public avatar"
-msgstr ""
+msgstr "Ð¤Ð¾Ñ‚Ð¾Ð³Ñ€Ð°Ñ„Ð¸Ñ Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ"
msgid "Profiles|Public email"
msgstr "Публичный email"
msgid "Profiles|Publicly visible private SSH keys can compromise your system."
-msgstr ""
+msgstr "ОбщедоÑтупные закрытые ключи SSH могут поÑтавить под угрозу вашу ÑиÑтему."
msgid "Profiles|Remove avatar"
msgstr "Удалить аватар"
msgid "Profiles|Select a service to sign in with."
-msgstr ""
+msgstr "Выберите ÑÐµÑ€Ð²Ð¸Ñ Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ð°."
msgid "Profiles|Service sign-in"
msgstr ""
msgid "Profiles|Set new profile picture"
-msgstr ""
+msgstr "УÑтановить новое изображение профилÑ"
msgid "Profiles|Set your local time zone."
-msgstr ""
+msgstr "УÑтановите ваш чаÑовой поÑÑ."
msgid "Profiles|Some options are unavailable for LDAP accounts"
msgstr "Ðекоторые параметры недоÑтупны Ð´Ð»Ñ ÑƒÑ‡ÐµÑ‚Ð½Ñ‹Ñ… запиÑей LDAP"
msgid "Profiles|Static object token was successfully reset"
-msgstr ""
+msgstr "Токен ÑтатичеÑкого объекта уÑпешно Ñброшена"
msgid "Profiles|Tell us about yourself in fewer than 250 characters."
msgstr ""
msgid "Profiles|The ability to update your name has been disabled by your administrator."
-msgstr ""
+msgstr "ВозможноÑÑ‚ÑŒ изменÑÑ‚ÑŒ ваше Ð¸Ð¼Ñ Ð±Ñ‹Ð»Ð° отключена админиÑтратором."
msgid "Profiles|The maximum file size allowed is 200KB."
msgstr "МакÑимально допуÑтимый размер файла ÑоÑтавлÑет 200 Кбайт."
msgid "Profiles|This email will be displayed on your public profile."
-msgstr ""
+msgstr "Этот Ð°Ð´Ñ€ÐµÑ Ð±ÑƒÐ´ÐµÑ‚ отображатьÑÑ Ð² вашем профиле."
msgid "Profiles|This email will be used for web based operations, such as edits and merges. %{commit_email_link_start}Learn more.%{commit_email_link_end}"
-msgstr ""
+msgstr "Этот Ð°Ð´Ñ€ÐµÑ Ð±ÑƒÐ´ÐµÑ‚ иÑпользоватьÑÑ Ð´Ð»Ñ Ð²ÐµÐ±-операций, таких как редактирование и ÑлиÑние. %{commit_email_link_start}Узнайте больше%{commit_email_link_end}"
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr "Этот Ñмайл и Ñообщение поÑвÑÑ‚ÑÑ Ð² вашем профиле и во вÑем интерфейÑе."
msgid "Profiles|This information will appear on your profile."
-msgstr ""
+msgstr "Эта Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¿Ð¾ÑвитÑÑ Ð² вашем профиле."
msgid "Profiles|Time settings"
msgstr "ÐаÑтройки времени"
msgid "Profiles|Title"
-msgstr ""
+msgstr "Заголовок"
msgid "Profiles|Two-factor authentication"
-msgstr ""
+msgstr "Ð”Ð²ÑƒÑ…Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð°Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ"
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "Введите значение %{confirmationValue} Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ:"
@@ -31792,9 +32221,15 @@ msgstr "Обновить Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
msgid "Profiles|Upload new avatar"
msgstr "Загрузить новый аватар"
-msgid "Profiles|Use a private email - %{email}"
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
msgstr ""
+msgid "Profiles|Use a private email - %{email}"
+msgstr "ИÑпользовать приватную Ñлектронную почту - %{email}"
+
msgid "Profiles|User ID"
msgstr "ID пользователÑ"
@@ -31808,7 +32243,7 @@ msgid "Profiles|Using emojis in names seems fun, but please try to set a status
msgstr "ИÑпользовать Ñмайлы в имени - веÑьма ÐºÑ€ÐµÐ°Ñ‚Ð¸Ð²Ð½Ð°Ñ Ð¼Ñ‹Ñль, но лучше попробуйте изменить ÑÑ‚Ð°Ñ‚ÑƒÑ Ð² профиле"
msgid "Profiles|Website url"
-msgstr ""
+msgstr "ÐÐ´Ñ€ÐµÑ Ñайта"
msgid "Profiles|Who you represent or work for."
msgstr ""
@@ -31829,10 +32264,10 @@ msgid "Profiles|You don't have access to delete this user."
msgstr "У Ð²Ð°Ñ Ð½ÐµÑ‚ прав на удаление Ñтого пользователÑ."
msgid "Profiles|You must accept the Terms of Service in order to perform this action."
-msgstr ""
+msgstr "Ð’Ñ‹ должны принÑÑ‚ÑŒ УÑÐ»Ð¾Ð²Ð¸Ñ Ð¿Ñ€ÐµÐ´Ð¾ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÑƒÑлуг Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ñтого дейÑтвиÑ."
msgid "Profiles|You must transfer ownership or delete groups you are an owner of before you can delete your account"
-msgstr ""
+msgstr "Ð’Ñ‹ должны передать право ÑобÑтвенноÑти или удалить группы, владельцем которых вы ÑвлÑетеÑÑŒ, прежде чем вы Ñможете удалить Ñвою учетную запиÑÑŒ"
msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
msgstr "Перед удалением учётной запиÑи, вам необходимо передать право Ð²Ð»Ð°Ð´ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ удалить Ñти группы."
@@ -31853,10 +32288,10 @@ msgid "Profiles|Your name was automatically set based on your %{provider_label}
msgstr "Ваше Ð¸Ð¼Ñ Ð±Ñ‹Ð»Ð¾ автоматичеÑки уÑтановлено на оÑнове вашей учетной запиÑи %{provider_label}, чтобы ваши знакомые могли Ð²Ð°Ñ ÑƒÐ·Ð½Ð°Ñ‚ÑŒ"
msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
-msgstr ""
+msgstr "Ваше Ð¸Ð¼Ñ Ð±Ñ‹Ð»Ð¾ автоматичеÑки наÑтроено на оÑнове вашей учётной запиÑи %{provider_label}, чтобы люди, которые вы знаете, могут Ð²Ð°Ñ Ñ€Ð°Ñпознать."
msgid "Profiles|https://website.com"
-msgstr ""
+msgstr "https://website.com"
msgid "Profiles|username"
msgstr "Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
@@ -31931,7 +32366,7 @@ msgid "Project URL"
msgstr "ÐÐ´Ñ€ÐµÑ ÐŸÑ€Ð¾ÐµÐºÑ‚Ð°"
msgid "Project access must be granted explicitly to each user. If this project is part of a group, access is granted to members of the group."
-msgstr ""
+msgstr "ДоÑтуп должен быть Ñвно предоÑтавлен каждому пользователю. ЕÑли проект ÑвлÑетÑÑ Ñ‡Ð°Ñтью группы, доÑтуп будет предоÑтавлен ее учаÑтникам."
msgid "Project access token creation is disabled in this group. You can still use and manage existing tokens. %{link_start}Learn more.%{link_end}"
msgstr ""
@@ -31991,7 +32426,7 @@ msgid "Project info:"
msgstr "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ проекте:"
msgid "Project information"
-msgstr ""
+msgstr "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ проекте"
msgid "Project is required when cluster_type is :project"
msgstr ""
@@ -32326,12 +32761,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr "Разрешать"
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr "Ð’Ñегда показывать большой палец вверх или вниз в обÑуждениÑÑ…, запроÑах на ÑлиÑние и Ñниппетах."
msgid "ProjectSettings|Analytics"
msgstr "Ðналитика"
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -32413,9 +32854,6 @@ msgstr "Каждое ÑлиÑние Ñоздает коммит ÑлиÑниÑ."
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr "Каждый проект может иметь Ñвоё ÑобÑтвенное проÑтранÑтво Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¾Ð±Ñ€Ð°Ð·Ð¾Ð² Docker"
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32530,6 +32968,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr "Коммиты ÑлиÑÐ½Ð¸Ñ Ð½Ðµ ÑоздаютÑÑ."
@@ -32575,6 +33016,9 @@ msgstr "ВидимоÑÑ‚ÑŒ проекта"
msgid "ProjectSettings|Public"
msgstr "ПубличнаÑ"
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32600,7 +33044,7 @@ msgid "ProjectSettings|Roll out new features without redeploying with feature fl
msgstr ""
msgid "ProjectSettings|Search for topic"
-msgstr ""
+msgstr "ПоиÑк по тегу"
msgid "ProjectSettings|Security & Compliance"
msgstr ""
@@ -32704,7 +33148,7 @@ msgstr "Пользователи могут запрашивать доÑтуп"
msgid "ProjectSettings|View and edit files in this project."
msgstr "ПроÑмотр и редактирование файлов в Ñтом проекте."
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32785,6 +33229,9 @@ msgstr "Netlify/ПроÑтой HTML"
msgid "ProjectTemplates|NodeJS Express"
msgstr "NodeJS Express"
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr "Pages/Gatsby"
@@ -32897,7 +33344,7 @@ msgid "Projects with no vulnerabilities and security scanning enabled"
msgstr ""
msgid "Projects with this topic"
-msgstr ""
+msgstr "Проекты Ñ Ñтим тегом"
msgid "Projects with write access"
msgstr "Проекты Ñ Ð´Ð¾Ñтупом Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи"
@@ -32927,7 +33374,7 @@ msgid "ProjectsNew|Allows you to immediately clone this project’s repository.
msgstr "ПозволÑет вам Ñразу клонировать репозиторий. ПропуÑтите Ñтот пункт, еÑли вы планируете загрузить ÑущеÑтвующий репозиторий."
msgid "ProjectsNew|Analyze your source code for known security vulnerabilities."
-msgstr ""
+msgstr "Проанализируйте иÑходный код на наличие извеÑтных уÑзвимоÑтей."
msgid "ProjectsNew|Connect your external repository to GitLab CI/CD."
msgstr "Подключить внешний репозиторий к GitLab CI/CD."
@@ -32939,10 +33386,10 @@ msgid "ProjectsNew|Create"
msgstr "Создать"
msgid "ProjectsNew|Create a blank project to store your files, plan your work, and collaborate on code, among other things."
-msgstr ""
+msgstr "Создайте пуÑтой проект Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð², Ð¿Ð»Ð°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ и ÑовмеÑтного напиÑÐ°Ð½Ð¸Ñ ÐºÐ¾Ð´Ð°."
msgid "ProjectsNew|Create a project pre-populated with the necessary files to get you started quickly."
-msgstr ""
+msgstr "Создайте проект из шаблона Ñ Ð¿Ñ€ÐµÐ´Ð²Ð°Ñ€Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾ заполненными файлами, чтобы быÑтро начать работу."
msgid "ProjectsNew|Create blank project"
msgstr "Создать пуÑтой проект"
@@ -32957,7 +33404,7 @@ msgid "ProjectsNew|Description format"
msgstr "Ðапишите неÑколько Ñлов про проект"
msgid "ProjectsNew|Enable Static Application Security Testing (SAST)"
-msgstr ""
+msgstr "Включить Static Application Security Testing (SAST)"
msgid "ProjectsNew|Import"
msgstr "Импорт"
@@ -32972,13 +33419,13 @@ msgid "ProjectsNew|Initialize repository with a README"
msgstr "Добавить README файл"
msgid "ProjectsNew|Migrate your data from an external source like GitHub, Bitbucket, or another instance of GitLab."
-msgstr ""
+msgstr "ПеренеÑите Ñвои данные из Ñторонних репозиториев, например GitHub, Bitbucket или другого ÑкземплÑра GitLab."
msgid "ProjectsNew|No import options available"
msgstr "Ðет доÑтупных опций импорта"
msgid "ProjectsNew|Pick a group or namespace"
-msgstr ""
+msgstr "Выберите группу или неймÑпейÑ"
msgid "ProjectsNew|Pick a group or namespace where you want to create this project."
msgstr ""
@@ -32999,7 +33446,7 @@ msgid "ProjectsNew|Visibility Level"
msgstr "Уровень доÑтупа"
msgid "ProjectsNew|Want to organize several dependent projects under the same namespace? %{link_start}Create a group.%{link_end}"
-msgstr ""
+msgstr "Хотите организовать неÑколько завиÑимых проектов в одном проÑтранÑтве имен? %{link_start}Создать группу.%{link_end}"
msgid "PrometheusAlerts|exceeded"
msgstr ""
@@ -33307,9 +33754,6 @@ msgstr ""
msgid "Protected Branches"
msgstr "Защищённые ветки"
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33545,7 +33989,7 @@ msgid "ProtectedTag|default"
msgstr ""
msgid "Protip: %{linkStart}Auto DevOps%{linkEnd} uses Kubernetes clusters to deploy your code!"
-msgstr ""
+msgstr "Совет: %{linkStart}Auto DevOps%{linkEnd} иÑпользует клаÑтеры Kubernetes Ð´Ð»Ñ Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ кода!"
msgid "Provide Feedback"
msgstr ""
@@ -33593,10 +34037,10 @@ msgid "Publish to status page"
msgstr ""
msgid "Published"
-msgstr ""
+msgstr "Опубликовано"
msgid "Published on status page"
-msgstr ""
+msgstr "Опубликовано на Ñтранице ÑоÑтоÑниÑ"
msgid "Publishes this issue to the associated status page."
msgstr ""
@@ -33932,7 +34376,7 @@ msgid "Recent events"
msgstr ""
msgid "Recent searches"
-msgstr ""
+msgstr "ПоÑледние запроÑÑ‹"
msgid "Recently used"
msgstr ""
@@ -33983,6 +34427,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -34182,6 +34629,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34372,7 +34822,7 @@ msgid "Remove time estimate"
msgstr "Удалить оценку времени"
msgid "Remove topic avatar"
-msgstr ""
+msgstr "Удалить логотип тега"
msgid "Remove user"
msgstr ""
@@ -34521,6 +34971,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34575,11 +35028,8 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr "ПожаловатьÑÑ"
-
-msgid "Report abuse to admin"
-msgstr "Сообщить о нарушении админиÑтратору"
+msgid "Report abuse to administrator"
+msgstr ""
msgid "Report couldn't be prepared."
msgstr ""
@@ -34929,6 +35379,9 @@ msgstr ""
msgid "Request Access"
msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾Ñтупа"
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -35180,6 +35633,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr "Продолжить"
@@ -35256,7 +35712,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -35315,7 +35771,7 @@ msgid "Roadmap view"
msgstr ""
msgid "Role"
-msgstr ""
+msgstr "Роль"
msgid "Rollback"
msgstr ""
@@ -35353,6 +35809,9 @@ msgstr "ЗапуÑтить очиÑтку"
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -35419,7 +35878,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -35446,6 +35905,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35467,6 +35929,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35552,6 +36017,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35570,6 +36038,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35609,12 +36080,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35709,6 +36183,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35775,6 +36252,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35823,12 +36303,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35944,6 +36418,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr "ВыполнÑетÑÑ"
@@ -35971,13 +36448,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -36019,6 +36499,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -36034,6 +36517,15 @@ msgstr ""
msgid "SSH public key"
msgstr "Открытый ключ SSH"
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr "Ð’ÐµÑ€Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ð¿Ð¾ SSL:"
@@ -36100,6 +36592,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -36127,9 +36625,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -36142,18 +36646,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -36238,6 +36751,9 @@ msgstr "ПоиÑк"
msgid "Search GitLab"
msgstr "ПоиÑк в GitLab"
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36245,7 +36761,7 @@ msgid "Search an environment spec"
msgstr ""
msgid "Search assignees"
-msgstr ""
+msgstr "ИÑкать ответÑтвенных"
msgid "Search authors"
msgstr ""
@@ -36371,13 +36887,13 @@ msgid "SearchAutocomplete|Issues I've created"
msgstr "ОбÑуждениÑ, которые Ñ Ñоздал"
msgid "SearchAutocomplete|Issues assigned to me"
-msgstr "ОбÑуждениÑ, на которые Ñ Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½"
+msgstr "ОбÑуждениÑ, назначенные мне"
msgid "SearchAutocomplete|Merge requests I've created"
msgstr "ЗапроÑÑ‹ на ÑлиÑние, которые Ñ Ñоздал"
msgid "SearchAutocomplete|Merge requests assigned to me"
-msgstr "ЗапроÑÑ‹ на ÑлиÑниÑ, на которые Ñ Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½"
+msgstr "ЗапроÑÑ‹ на ÑлиÑние, назначенные мне"
msgid "SearchAutocomplete|Merge requests that I'm a reviewer"
msgstr ""
@@ -36777,6 +37293,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36834,9 +37353,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36852,6 +37380,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36888,6 +37419,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36960,6 +37494,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36969,12 +37506,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -37044,6 +37590,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -37071,6 +37620,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -37107,6 +37659,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -37116,9 +37671,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -37263,6 +37833,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr "Ð”Ð»Ñ Ñтой Ñборочной линии не найдено никаких уÑзвимоÑтей"
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37342,7 +37915,7 @@ msgid "SecurityReports|Submit vulnerability"
msgstr ""
msgid "SecurityReports|Take survey"
-msgstr ""
+msgstr "Пройти опроÑ"
msgid "SecurityReports|The Vulnerability Report shows results of successful scans on your project's default branch, manually added vulnerability records, and vulnerabilities found from scanning operational environments. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -37488,6 +38061,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr "Выберите файл на левой боковой панели, чтобы начать редактирование. ПоÑле Ñтого вы Ñможете зафикÑировать Ñвои изменениÑ."
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr "Выберите метку"
@@ -37519,7 +38095,7 @@ msgid "Select all"
msgstr "Выбрать вÑе"
msgid "Select an assignee"
-msgstr ""
+msgstr "Выберите ответÑтвенного"
msgid "Select an iteration"
msgstr ""
@@ -37528,7 +38104,7 @@ msgid "Select assignee"
msgstr "Выбрать ответÑтвенного"
msgid "Select assignee(s)"
-msgstr ""
+msgstr "Выберите ответÑтвенного(Ñ‹Ñ…)"
msgid "Select branch"
msgstr ""
@@ -37593,6 +38169,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37627,7 +38206,7 @@ msgid "Select target project"
msgstr ""
msgid "Select timezone"
-msgstr ""
+msgstr "Выбрать чаÑовой поÑÑ"
msgid "Select type"
msgstr ""
@@ -37981,10 +38560,10 @@ msgid "SetStatusModal|Clear status"
msgstr "ОчиÑтить ÑтатуÑ"
msgid "SetStatusModal|Clear status after"
-msgstr ""
+msgstr "ОчиÑтить ÑÑ‚Ð°Ñ‚ÑƒÑ Ñ‡ÐµÑ€ÐµÐ·"
msgid "SetStatusModal|Displays that you are busy or not able to respond"
-msgstr ""
+msgstr "Показывает, что вы занÑÑ‚Ñ‹ или не можете ответить"
msgid "SetStatusModal|Edit status"
msgstr "Изменить ÑтатуÑ"
@@ -37999,7 +38578,7 @@ msgid "SetStatusModal|Set status"
msgstr "Изменить ÑтатуÑ"
msgid "SetStatusModal|Set yourself as busy"
-msgstr ""
+msgstr "ÐедоÑтупен"
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr "Извините, мы не Ñмогли изменить ваш ÑтатуÑ. ПожалуйÑта, попробуйте позже."
@@ -38011,7 +38590,7 @@ msgid "SetStatusModal|What's your status?"
msgstr "Как у Ð²Ð°Ñ Ð´ÐµÐ»Ð°?"
msgid "SetStatusModal|Your status resets on %{date}."
-msgstr ""
+msgstr "Ваш ÑÑ‚Ð°Ñ‚ÑƒÑ ÑброÑитÑÑ %{date}."
msgid "Sets %{epic_ref} as parent epic."
msgstr "УÑтанавливает %{epic_ref} как родительÑкую цель."
@@ -38083,6 +38662,9 @@ msgstr ""
msgid "Shared Runners"
msgstr "Общие Runner'ы"
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -38209,6 +38791,9 @@ msgstr "Показать поÑледнюю верÑию"
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -38258,7 +38843,7 @@ msgid "ShowcaseSecurity|Enable Secret Detection"
msgstr ""
msgid "ShowcaseSecurity|Enable Static Application Security Testing (SAST)"
-msgstr ""
+msgstr "Включить Static Application Security Testing (SAST)"
msgid "ShowcaseSecurity|Find out if your external libraries are safe. Run dependency scanning jobs that check for known vulnerabilities in your external libraries."
msgstr ""
@@ -38285,7 +38870,7 @@ msgid "ShowcaseSecurity|Upgrade now"
msgstr ""
msgid "ShowcaseSecurity|Use GitLab CI/CD to analyze your source code for known vulnerabilities. Compare the found vulnerabilities between your source and target branches."
-msgstr ""
+msgstr "ИÑпользуйте GitLab CI/CD Ð´Ð»Ñ Ð°Ð½Ð°Ð»Ð¸Ð·Ð° вашего иÑходного кода Ð´Ð»Ñ Ð¸Ð·Ð²ÐµÑтных уÑзвимоÑтей. Сравните найденные уÑзвимоÑти между вашим иÑточником и конечными ветками."
msgid "ShowcaseSecurity|Vulnerability management"
msgstr ""
@@ -38369,9 +38954,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38525,7 +39107,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38540,13 +39122,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38570,13 +39155,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38714,11 +39302,11 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
-msgstr "Кто-то отредактировал обÑуждение одновременно Ñ Ð²Ð°Ð¼Ð¸. ПожалуйÑта, проверьте %{linkStart}обÑуждение%{linkEnd} и убедитеÑÑŒ, что внеÑённые Вами Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñлучайно не затёрли чужие."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -38759,6 +39347,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38777,6 +39368,9 @@ msgstr "Что-то пошло не так при архивировании Ñ‚Ñ
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38846,6 +39440,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -39239,6 +39836,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr "ОпиÑание Ð´Ð»Ñ Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð½Ð¾Ð³Ð¾ (squash) коммита"
@@ -39344,6 +39944,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr "Ðачать поиÑк"
@@ -39407,6 +40010,9 @@ msgstr "СтатиÑтика"
msgid "Status"
msgstr "СтатуÑ"
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -40146,6 +40752,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -40236,9 +40845,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr "Переключить ветка/тег"
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40521,6 +41127,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr "Команда"
@@ -41111,6 +41723,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr "Следующие Ñлементы ÐЕ будут ÑкÑпортированы:"
@@ -41144,7 +41759,7 @@ msgid "The global settings require you to enable Two-Factor Authentication for y
msgstr "Глобальные наÑтройки требуют, чтобы вы включили двухфакторную аутентификацию Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ¹ учетной запиÑи."
msgid "The group and any internal projects can be viewed by any logged in user except external users."
-msgstr ""
+msgstr "Группа и любые внутренние проекты будут доÑтупны любым зарегиÑтрированным пользователем, за иÑключением внешних пользователей."
msgid "The group and any public projects can be viewed without any authentication."
msgstr "Группу и любые публичные проекты можно проÑматривать без какой-либо аутентификации."
@@ -41309,7 +41924,7 @@ msgid "The pipelines schedule runs pipelines in the future, repeatedly, for spec
msgstr "РаÑпиÑание Ñборочных линий может многократно запуÑкать Ñборочные линии Ð´Ð»Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð½Ñ‹Ñ… веток или тегов в будущем. Эти запланированные Ñборочные линии будут наÑледовать Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ñтупа к проектам от аÑÑоциированного пользователÑ."
msgid "The project can be accessed by any logged in user except external users."
-msgstr ""
+msgstr "ДоÑтуп к проекту может получить любой зарегиÑтрированный пользователь, кроме внешних пользователей."
msgid "The project can be accessed without any authentication."
msgstr "ДоÑтуп к проекту возможен без какой-либо проверки подлинноÑти."
@@ -41390,10 +42005,10 @@ msgid "The source project of this merge request has been removed."
msgstr ""
msgid "The source topic and the target topic are identical."
-msgstr ""
+msgstr "ИÑходный тег и конечный одинаковы."
msgid "The source topic is not a topic."
-msgstr ""
+msgstr "ИÑходный тег не ÑвлÑетÑÑ Ñ‚ÐµÐ³Ð¾Ð¼."
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -41408,7 +42023,7 @@ msgid "The tag name can't be changed for an existing release."
msgstr ""
msgid "The target topic is not a topic."
-msgstr ""
+msgstr "Конечный тег не ÑвлÑетÑÑ Ñ‚ÐµÐ³Ð¾Ð¼."
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -41479,9 +42094,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr "Ðет Ñообщений о нарушениÑÑ…!"
-msgid "There are no archived projects yet"
-msgstr "Ðрхивных проектов пока нет"
-
msgid "There are no archived requirements"
msgstr "Ðет архивных требований"
@@ -41539,20 +42151,17 @@ msgstr ""
msgid "There are no packages yet"
msgstr "Пакетов пока нет"
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
msgid "There are no topics to show."
-msgstr ""
+msgstr "Теги не найдены."
msgid "There are no variables yet."
msgstr ""
msgid "There are running deployments on the environment. Please retry later."
-msgstr ""
+msgstr "Ð’ Ñреде запущены развертываниÑ. Повторите попытку позже."
msgid "There are several file size limits in place for the Package Registry."
msgstr ""
@@ -41587,6 +42196,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr "Произошла ошибка ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ Ð²Ð°ÑˆÐ¸Ð¼ уÑтройÑтвом."
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41707,6 +42319,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41746,6 +42361,9 @@ msgstr "Произошла ошибка при ÑброÑе минут польÐ
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr "Произошла ошибка при Ñохранении ваших изменений."
@@ -41839,9 +42457,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -42031,6 +42646,9 @@ msgstr "Этой цели не ÑущеÑтвует, или у Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñ
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr "Эта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ñ‚Ñ€ÐµÐ±ÑƒÐµÑ‚, чтобы локальное хранилище было включено"
@@ -42049,7 +42667,7 @@ msgstr ""
msgid "This group"
msgstr "Эта группа"
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42523,6 +43141,12 @@ msgstr ""
msgid "Time spent"
msgstr "Времени затрачено"
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42542,7 +43166,7 @@ msgid "Time until first merge request"
msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð´Ð¾ первого запроÑа на ÑлиÑние"
msgid "Time zone"
-msgstr ""
+msgstr "ЧаÑовой поÑÑ"
msgid "TimeTrackingEstimated|Est"
msgstr "Оцен."
@@ -42719,7 +43343,7 @@ msgid "Timeout for the fastest Gitaly operations (in seconds)."
msgstr ""
msgid "Timezone"
-msgstr ""
+msgstr "ЧаÑовой поÑÑ"
msgid "Time|A"
msgstr ""
@@ -42777,6 +43401,9 @@ msgstr ""
msgid "Title:"
msgstr "Заголовок:"
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42787,7 +43414,7 @@ msgid "To %{link_to_help} of your domain, add the above key to a TXT record with
msgstr ""
msgid "To Do"
-msgstr ""
+msgstr "Выполнить"
msgid "To GitLab"
msgstr "Ð’ GitLab"
@@ -42810,9 +43437,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr "Чтобы добавить запиÑÑŒ вручную, предоÑтавьте Ñледующую информацию в приложении на Ñвоем телефоне."
@@ -42927,6 +43551,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -43020,6 +43647,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -43051,6 +43681,9 @@ msgid "Todos|It's how you always know what to work on next."
msgstr ""
msgid "Todos|Mark all as done"
+msgstr "Отметить вÑе как выполненное"
+
+msgid "Todos|Member access requested"
msgstr ""
msgid "Todos|Mentioned"
@@ -43068,13 +43701,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -43089,22 +43722,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -43198,55 +43831,55 @@ msgid "Too many users found. Quick actions are limited to at most %{max_count} u
msgstr ""
msgid "TopNav|Explore"
-msgstr ""
+msgstr "Обзор"
msgid "TopNav|Go back"
msgstr ""
msgid "TopNav|Switch to"
-msgstr ""
+msgstr "Перейти к"
msgid "TopNav|Your dashboards"
msgstr ""
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
-msgstr ""
+msgstr "Тег %{source_topic} был уÑпешно объединен Ñ Ñ‚ÐµÐ³Ð¾Ð¼ %{target_topic}."
msgid "Topic %{topic_name} was successfully created."
-msgstr ""
+msgstr "Тег %{topic_name} был уÑпешно Ñоздан."
msgid "Topic %{topic_name} was successfully removed."
-msgstr ""
+msgstr "Тег %{topic_name} был уÑпешно удален."
msgid "Topic avatar"
-msgstr ""
+msgstr "Логотип тега"
msgid "Topic avatar for %{name} will be removed. This cannot be undone."
-msgstr ""
+msgstr "Логотип Ð´Ð»Ñ %{name} будет удален. Это не может быть отменено."
msgid "Topic slug (name)"
-msgstr ""
+msgstr "Ð˜Ð¼Ñ Ñ‚ÐµÐ³Ð° (напр. в url)"
msgid "Topic title"
-msgstr ""
+msgstr "Заголовок тега (в интерфейÑе)"
msgid "Topic was successfully updated."
-msgstr ""
+msgstr "Тег уÑпешно обновлен."
msgid "TopicSelect|No matching results"
-msgstr ""
+msgstr "Ðет подходÑщих результатов"
msgid "TopicSelect|Search topics"
-msgstr ""
+msgstr "ПоиÑк по тегам"
msgid "TopicSelect|Select a topic"
-msgstr ""
+msgstr "Выберите тег"
msgid "Topics"
-msgstr ""
+msgstr "Теги"
msgid "Topics could not be merged!"
-msgstr ""
+msgstr "Теги не могут быть объединены!"
msgid "Total"
msgstr "Ð’Ñего"
@@ -43477,6 +44110,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43513,6 +44152,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43690,6 +44332,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43813,6 +44461,9 @@ msgstr "К Ñожалению, ваше пиÑьмо в GitLab не может Ð
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr "мÑ"
@@ -43894,6 +44545,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr "Убрать из избранного"
@@ -43994,7 +44648,7 @@ msgid "Update your group name, description, avatar, and visibility."
msgstr "Обновить наименование вашей группы, её опиÑание, аватар и видимоÑÑ‚ÑŒ."
msgid "Update your project name, topics, description, and avatar."
-msgstr ""
+msgstr "Обновите название Ñвоего проекта, теги, опиÑание и логотип."
msgid "UpdateProject|Cannot rename project because it contains container registry tags!"
msgstr "Ðе удаетÑÑ Ð¿ÐµÑ€ÐµÐ¸Ð¼ÐµÐ½Ð¾Ð²Ð°Ñ‚ÑŒ проект, потому что он Ñодержит теги рееÑтра контейнеров!"
@@ -44077,6 +44731,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -44095,18 +44752,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr "%{help_link_start}Общие Runner'Ñ‹%{help_link_end} отключены, поÑтому нет наÑтроенных ограничений в иÑпользовании Ñборочных линий"
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -44134,9 +44785,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -44146,9 +44794,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr "Текущий период иÑпользованиÑ"
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -44275,10 +44920,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -44287,12 +44929,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr "Ð’ Ñтом проÑтранÑтве имён нет проектов, которые иÑпользуют общие Runner'Ñ‹"
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -44305,9 +44941,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr "Ðе ограничена"
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -44341,7 +44974,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -44350,16 +44983,16 @@ msgstr "Wiki"
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44533,6 +45166,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44661,13 +45297,13 @@ msgid "User-based escalation rules must have a user with access to the project"
msgstr ""
msgid "UserAvailability|%{author} %{spanStart}(Busy)%{spanEnd}"
-msgstr ""
+msgstr "%{author} %{spanStart}(ÐедоÑтупен)%{spanEnd}"
msgid "UserAvailability|%{author} (Busy)"
-msgstr ""
+msgstr "%{author} (ÐедоÑтупен)"
msgid "UserAvailability|(Busy)"
-msgstr ""
+msgstr "(ÐедоÑтупен)"
msgid "UserLists|Add"
msgstr ""
@@ -44745,7 +45381,7 @@ msgid "UserList|created %{timeago}"
msgstr ""
msgid "UserProfile|(Busy)"
-msgstr ""
+msgstr "(ÐедоÑтупен)"
msgid "UserProfile|Activity"
msgstr "ÐктивноÑÑ‚ÑŒ"
@@ -44757,25 +45393,25 @@ msgid "UserProfile|Blocked user"
msgstr "Заблокированный пользователь"
msgid "UserProfile|Bot activity"
-msgstr ""
+msgstr "ÐктивноÑÑ‚ÑŒ бота"
msgid "UserProfile|Contributed projects"
msgstr "Вклад в проекты"
msgid "UserProfile|Copy user ID"
-msgstr ""
+msgstr "Копировать ID пользователÑ"
msgid "UserProfile|Edit profile"
msgstr "Изменить профиль"
msgid "UserProfile|Explore public groups to find projects to contribute to."
-msgstr ""
+msgstr "ПроÑмотрите публичные группы, чтобы найти проекты Ð´Ð»Ñ ÑƒÑ‡Ð°ÑтиÑ."
msgid "UserProfile|Followers"
-msgstr ""
+msgstr "ПодпиÑчики"
msgid "UserProfile|Following"
-msgstr ""
+msgstr "ПодпиÑки"
msgid "UserProfile|Groups"
msgstr "Группы"
@@ -44799,13 +45435,10 @@ msgid "UserProfile|Personal projects"
msgstr "Личные проекты"
msgid "UserProfile|Pronounced as: %{pronunciation}"
-msgstr ""
-
-msgid "UserProfile|Report abuse"
-msgstr "Сообщить о нарушении"
+msgstr "ПроизноÑитÑÑ ÐºÐ°Ðº: %{pronunciation}"
msgid "UserProfile|Retry"
-msgstr ""
+msgstr "Повторить"
msgid "UserProfile|Snippets"
msgstr "Сниппеты"
@@ -44814,7 +45447,7 @@ msgid "UserProfile|Snippets in GitLab can either be private, internal, or public
msgstr "Сниппеты в GitLab могут быть чаÑтными, внутренними или общедоÑтупными."
msgid "UserProfile|Star projects to track their progress and show your appreciation."
-msgstr ""
+msgstr "Добавьте проекты в избранное Ð´Ð»Ñ Ð¾Ñ‚ÑÐ»ÐµÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ð¸Ñ… прогреÑÑа и Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ñвоей благодарноÑти."
msgid "UserProfile|Starred projects"
msgstr "Избранные проекты"
@@ -44823,7 +45456,7 @@ msgid "UserProfile|Subscribe"
msgstr "ПодпиÑатьÑÑ"
msgid "UserProfile|This user doesn't have any followers."
-msgstr ""
+msgstr "У Ñтого Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð½ÐµÑ‚ подпиÑчиков."
msgid "UserProfile|This user doesn't have any personal projects"
msgstr "У Ñтого Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð½ÐµÑ‚ личных проектов"
@@ -44832,7 +45465,7 @@ msgid "UserProfile|This user has a private profile"
msgstr "У Ñтого Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ñ‹Ð¹ профиль"
msgid "UserProfile|This user hasn't contributed to any projects"
-msgstr ""
+msgstr "Этот пользователь не учаÑтвовал ни в каких проектах"
msgid "UserProfile|This user hasn't starred any projects"
msgstr "Это пользователь не добавил в избранное ни одного проекта"
@@ -44841,13 +45474,13 @@ msgid "UserProfile|This user is blocked"
msgstr "Этот пользователь заблокирован"
msgid "UserProfile|This user isn't following other users."
-msgstr ""
+msgstr "Этот пользователь не подпиÑан на других пользователей."
msgid "UserProfile|Unconfirmed user"
-msgstr ""
+msgstr "Ðеподтвержденный пользователь"
msgid "UserProfile|User ID: %{id}"
-msgstr ""
+msgstr "ID пользователÑ: %{id}"
msgid "UserProfile|View all"
msgstr "Показать вÑе"
@@ -44856,13 +45489,13 @@ msgid "UserProfile|View user in admin area"
msgstr "ПроÑмотр Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð² панели админиÑтрированиÑ"
msgid "UserProfile|You are not following other users."
-msgstr ""
+msgstr "Ð’Ñ‹ не подпиÑаны на других пользователей."
msgid "UserProfile|You can create a group for several dependent projects."
-msgstr ""
+msgstr "Ð’Ñ‹ можете Ñоздать группу Ð´Ð»Ñ Ð½ÐµÑкольких завиÑимых проектов."
msgid "UserProfile|You do not have any followers."
-msgstr ""
+msgstr "У Ð²Ð°Ñ Ð½ÐµÑ‚ подпиÑчиков."
msgid "UserProfile|You haven't created any personal projects."
msgstr "Ð’Ñ‹ не Ñоздали ни одного личного проекта."
@@ -44874,10 +45507,10 @@ msgid "UserProfile|Your projects can be available publicly, internally, or priva
msgstr "Ðа ваш выбор проекты могут быть публичными, внутренними или личными."
msgid "UserProfile|at"
-msgstr ""
+msgstr "в"
msgid "UserProfile|made a private contribution"
-msgstr ""
+msgstr "Ñделал личный вклад"
msgid "Username"
msgstr ""
@@ -44993,6 +45626,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -45057,7 +45693,7 @@ msgid "ValueStreamAnalytics|&lt;1m"
msgstr ""
msgid "ValueStreamAnalytics|Average number of deployments to production per day."
-msgstr ""
+msgstr "Среднее количеÑтво развертываний в производÑтвенной Ñреде за день."
msgid "ValueStreamAnalytics|DORA metrics"
msgstr ""
@@ -45065,6 +45701,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -45083,6 +45722,9 @@ msgstr "Медианное Ð²Ñ€ÐµÐ¼Ñ Ð¾Ñ‚ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¾Ð±ÑуждениÑ
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -45090,7 +45732,7 @@ msgid "ValueStreamAnalytics|Number of new issues created."
msgstr ""
msgid "ValueStreamAnalytics|Percentage of deployments that cause an incident in production."
-msgstr ""
+msgstr "Процент развертываний, вызвавших инцидент в рабочей Ñреде."
msgid "ValueStreamAnalytics|Shows %{selectedSubjectFilterText} and %{labelsCount} for group '%{groupName}' and %{projectsCount} from %{createdAfter} to %{createdBefore}"
msgstr ""
@@ -45131,10 +45773,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr "Поток ценноÑти по умолчанию не может быть удалён"
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -45146,7 +45788,7 @@ msgstr "Переменные"
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -45203,6 +45845,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr "ВерÑÐ¸Ñ %{versionNumber} (поÑледнÑÑ)"
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -45212,6 +45869,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45228,13 +45897,13 @@ msgid "View all environments."
msgstr ""
msgid "View all groups"
-msgstr ""
+msgstr "ПроÑмотреть вÑе группы"
msgid "View all issues"
msgstr ""
msgid "View all projects"
-msgstr ""
+msgstr "ПроÑмотреть вÑе проекты"
msgid "View blame"
msgstr ""
@@ -45878,6 +46547,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¿Ð¾ Ñтапу отÑутÑтвует."
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45924,7 +46596,7 @@ msgid "We'll continuously validate your pipeline configuration. The validation r
msgstr ""
msgid "We'll use this to help surface the right features and information to you."
-msgstr ""
+msgstr "Мы воÑпользуемÑÑ Ñтим, чтобы предоÑтавить вам нужные функции и информацию."
msgid "We're experiencing difficulties and this tab content is currently unavailable."
msgstr ""
@@ -45953,9 +46625,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45971,24 +46640,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -46019,6 +46676,18 @@ msgstr "ÐаÑтройки веб-обработчика"
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -46085,9 +46754,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -46136,14 +46802,11 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ñборочной линии"
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
-msgstr ""
+msgid "Webhooks|Pipeline events"
+msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ñборочной линии"
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
msgstr ""
@@ -46181,9 +46844,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46275,7 +46935,7 @@ msgid "What does the setting affect?"
msgstr ""
msgid "What does this command do?"
-msgstr ""
+msgstr "Что делает Ñта команда?"
msgid "What is GitLab Runner?"
msgstr ""
@@ -46299,7 +46959,7 @@ msgid "What variables can I use?"
msgstr ""
msgid "What will you use this group for?"
-msgstr ""
+msgstr "Ð”Ð»Ñ ÐºÐ°ÐºÐ¸Ñ… целей вы будете иÑпользовать группу?"
msgid "What would you like to do?"
msgstr ""
@@ -46376,7 +47036,7 @@ msgid "Who will be using this GitLab trial?"
msgstr ""
msgid "Who will be using this group?"
-msgstr ""
+msgstr "Кто будет иÑпользовать Ñту группу?"
msgid "Why are you signing up? (optional)"
msgstr ""
@@ -46579,6 +47239,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46588,9 +47251,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr "Отменить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾Ñтупа"
@@ -46609,6 +47269,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46624,9 +47290,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46649,6 +47312,9 @@ msgstr[3] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46658,6 +47324,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46670,16 +47342,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46688,12 +47363,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46703,12 +47384,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46721,9 +47408,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46787,7 +47483,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46817,6 +47513,9 @@ msgstr "Ðапишите комментарий…"
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46986,10 +47685,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -47028,13 +47727,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -47101,9 +47793,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr "Ð’Ñ‹ можете начать Ñ ÐºÐ»Ð¾Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Ð¸Ð»Ð¸ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² в него одним из Ñледующих ÑпоÑобов."
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "Ð’Ñ‹ можете приглаÑить нового учаÑтника в %{project_name} или приглаÑить другую группу."
@@ -47122,18 +47811,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr "Теперь вы можете отправить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние, чтобы внеÑти Ñто изменение в иÑходную ветку."
msgid "You can now submit a merge request to get this change into the original project."
msgstr "Теперь вы можете отправить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние, чтобы внеÑти Ñто изменение в иÑходный проект."
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -47215,6 +47898,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr "У Ð²Ð°Ñ Ð¿Ð¾ÐºÐ° нет подпиÑок"
@@ -47269,6 +47955,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -47333,6 +48022,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -47384,9 +48076,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47564,6 +48253,9 @@ msgstr "Вы уже включили двухфакторную аутентиф
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -47582,7 +48274,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47618,6 +48310,9 @@ msgstr "Ваш Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° Ñоздание учетной запиÑи Gi
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr "Ваши Группы"
@@ -47763,13 +48458,6 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "Your groups"
msgstr "Ваши группы"
@@ -47809,6 +48497,15 @@ msgstr "Ваше имÑ"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47969,6 +48666,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -48152,6 +48852,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -48207,6 +48910,20 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -48264,10 +48981,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -48432,7 +49152,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr "Ðайдено %{issuesWithCount}"
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48614,6 +49334,13 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "commit %{commit_id}"
msgstr "коммит %{commit_id}"
@@ -48775,9 +49502,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48816,7 +49540,7 @@ msgid "finding is not found or is already attached to a vulnerability"
msgstr ""
msgid "following"
-msgstr ""
+msgstr "подпиÑан"
msgid "for"
msgstr ""
@@ -48968,6 +49692,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr "диапазон недопуÑтимых IP адреÑов"
@@ -49007,6 +49734,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -49510,6 +50243,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -49519,10 +50255,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49732,6 +50468,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr "недавнÑÑ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð¾ÑÑ‚ÑŒ"
@@ -49791,6 +50530,13 @@ msgstr "репозиторий:"
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "running"
msgstr ""
diff --git a/locale/ru/gitlab.po.time_stamp b/locale/ru/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/ru/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/si_LK/gitlab.po b/locale/si_LK/gitlab.po
index 56b02abbb11..8dc29214e35 100644
--- a/locale/si_LK/gitlab.po
+++ b/locale/si_LK/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: si-LK\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:24\n"
+"PO-Revision-Date: 2022-12-11 07:27\n"
msgid " %{start} to %{end}"
msgstr " %{start} සිට %{end}"
@@ -310,8 +310,8 @@ msgstr[1] "ස්ථර %d"
msgid "%d merge request"
msgid_plural "%d merge requests"
-msgstr[0] "සංයුක්ත ඉල්ලීම් %d"
-msgstr[1] "සංයුක්ත ඉල්ලීම් %d"
+msgstr[0] "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් %d"
+msgstr[1] "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් %d"
msgid "%d merge request that you don't have access to."
msgid_plural "%d merge requests that you don't have access to."
@@ -320,8 +320,8 @@ msgstr[1] ""
msgid "%d merge requests"
msgid_plural "%d merge requests"
-msgstr[0] "සංයුක්ත ඉල්ලීම් %d"
-msgstr[1] "සංයුක්ත ඉල්ලීම් %d"
+msgstr[0] "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් %d"
+msgstr[1] "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් %d"
msgid "%d metric"
msgid_plural "%d metrics"
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] "තවත් අදහස් %d"
msgstr[1] "තවත් අදහස් %d"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -500,12 +505,18 @@ msgstr[1] "à·ƒà·à¶¸à·à¶¢à·’කයින් %{bold_start}%{count}%{bold_end}"
msgid "%{bold_start}%{count}%{bold_end} opened merge request"
msgid_plural "%{bold_start}%{count}%{bold_end} opened merge requests"
-msgstr[0] "විවෘත සංයුක්ත ඉල්ලීම් %{bold_start}%{count}%{bold_end}"
-msgstr[1] "විවෘත සංයුක්ත ඉල්ලීම් %{bold_start}%{count}%{bold_end}"
+msgstr[0] "විවෘත ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් %{bold_start}%{count}%{bold_end}"
+msgstr[1] "විවෘත ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් %{bold_start}%{count}%{bold_end}"
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,14 +717,14 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type} මක෠ඇත"
+msgid "%{host} sign-in from new location"
+msgstr "%{host} වෙත නව ස්ථà·à¶±à¶ºà¶šà·’න් ඇතුළු වී ඇත"
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
-msgstr "%{host} වෙත නව ස්ථà·à¶±à¶ºà¶šà·’න් ඇතුළු වී ඇත"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
@@ -827,7 +838,7 @@ msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr ""
msgid "%{mergeLength}/%{usersLength} can merge"
-msgstr "%{mergeLength}/%{usersLength} සංයුක්ත කළ à·„à·à¶šà·’ය"
+msgstr "%{mergeLength}/%{usersLength} ඒකà·à¶¶à¶¯à·Šà¶° කිරීමට à·„à·à¶šà·’ය"
msgid "%{message} showing first %{warnings_displayed}"
msgstr ""
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr "%{reportType} %{status}"
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr "%{text} තිබේ"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,8 +1160,11 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr "%{name} නමකට පමණක් %{type} සහà·à¶º දක්වයි"
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
-msgstr "%{userName} (සංයුක්ත කළ නොහà·à¶šà·’ය)"
+msgstr "%{userName} (ඒකà·à¶¶à¶¯à·Šà¶° කිරීමට නොහà·à¶šà·’ය)"
msgid "%{userName}'s avatar"
msgstr "%{userName}ගේ ප්â€à¶»à¶­à·’රුව"
@@ -1255,7 +1271,7 @@ msgstr[0] "(%d වස෠ඇත)"
msgstr[1] "(%d වස෠ඇත)"
msgid "(%{mrCount} merged)"
-msgstr "(%{mrCount} සංයුක්තයි)"
+msgstr "(%{mrCount} ඒකà·à¶¶à¶¯à·Šà¶°à¶ºà·’)"
msgid "(%{value}) has already been taken"
msgstr "(%{value}) දà·à¶±à¶§à¶¸à¶­à·Š ගෙන ඇත"
@@ -1266,12 +1282,18 @@ msgstr "(නීති&nbsp;+%{count})"
msgid "(Group Managed Account)"
msgstr "(à·ƒà·à¶¸à·–හික කළමනà·à¶šà¶»à¶« ගිණුම)"
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr "(වෙනස්කම් නà·à¶­)"
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -1435,8 +1457,8 @@ msgstr[1] ""
msgid "1 closed merge request"
msgid_plural "%{merge_requests} closed merge requests"
-msgstr[0] "à·€à·à·ƒà·– සංයුක්ත ඉල්ලීම් 1කි"
-msgstr[1] "à·€à·à·ƒà·– සංයුක්ත ඉල්ලීම් %{merge_requests}කි"
+msgstr[0] "à·€à·à·ƒà·– ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් 1කි"
+msgstr[1] "à·€à·à·ƒà·– ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් %{merge_requests}කි"
msgid "1 day"
msgid_plural "%d days"
@@ -1480,13 +1502,13 @@ msgstr[1] ""
msgid "1 merge request selected"
msgid_plural "%d merge requests selected"
-msgstr[0] "තේරූ සංයුක්ත ඉල්ලීම් 1 කි"
-msgstr[1] "තේරූ සංයුක්ත ඉල්ලීම් %d කි"
+msgstr[0] "තේරූ ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් 1 කි"
+msgstr[1] "තේරූ ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් %d කි"
msgid "1 merged merge request"
msgid_plural "%{merge_requests} merged merge requests"
-msgstr[0] "1 ක් සංයුක්ත කෙරිණි"
-msgstr[1] "%{merge_requests} ක් සංයුක්ත කෙරිණි"
+msgstr[0] "1 ක් ඒකà·à¶¶à¶¯à·Šà¶° කෙරිණි"
+msgstr[1] "%{merge_requests} ක් ඒකà·à¶¶à¶¯à·Šà¶° කෙරිණි"
msgid "1 minute"
msgid_plural "%d minutes"
@@ -1505,8 +1527,8 @@ msgstr[1] ""
msgid "1 open merge request"
msgid_plural "%{merge_requests} open merge requests"
-msgstr[0] "විවෘත සංයුක්ත ඉල්ලීම් 1කි"
-msgstr[1] "විවෘත සංයුක්ත ඉල්ලීම් %{merge_requests}කි"
+msgstr[0] "විවෘත ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් 1කි"
+msgstr[1] "විවෘත ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් %{merge_requests}කි"
msgid "1 pipeline"
msgid_plural "%d pipelines"
@@ -2143,7 +2165,7 @@ msgid "Add Kubernetes cluster"
msgstr ""
msgid "Add LICENSE"
-msgstr "බලපත්â€à¶»à¶ºà¶šà·Š එකතු කරන්න"
+msgstr "බලපත්â€à¶»à¶ºà¶šà·Š යොදන්න"
msgid "Add License"
msgstr "බලපත්â€à¶»à¶ºà¶šà·Š යොදන්න"
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2416,7 +2441,7 @@ msgid "AddMember|Invite limit of %{daily_invites} per day exceeded"
msgstr ""
msgid "AddMember|Invites cannot be blank"
-msgstr "ඇරයුම් හිස්විය නොහà·à¶šà·’ය"
+msgstr "ඇරයුම් හිස් නොවිය යුතුය"
msgid "AddMember|No invite source provided."
msgstr ""
@@ -2443,7 +2468,7 @@ msgid "Added an issue to an epic."
msgstr ""
msgid "Added for this merge request"
-msgstr "මෙම සංයුක්ත ඉල්ලීමෙහි ඇත"
+msgstr "මෙම ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීමෙහි ඇත"
msgid "Added in this version"
msgstr "මෙම අනුවà·à¶¯à¶ºà·™à·„à·’ ඇත"
@@ -2451,6 +2476,9 @@ msgstr "මෙම අනුවà·à¶¯à¶ºà·™à·„à·’ ඇත"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "අතිරේක විනà·à¶©à·’"
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr "මුළු පරිà·à·“ලකයින්"
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr "පරිà·à·“ලකයින්"
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr "CI/CD අච්චුවක් තà·à¶»à¶±à·Šà¶±"
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -3064,7 +3092,7 @@ msgid "AdminStatistics|Issues"
msgstr ""
msgid "AdminStatistics|Merge requests"
-msgstr "සංයුක්ත ඉල්ලීම්"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම්"
msgid "AdminStatistics|Milestones"
msgstr ""
@@ -3559,7 +3587,7 @@ msgid "After a successful password update, you will be redirected to the login p
msgstr ""
msgid "After it expires, you can't use merge approvals, code quality, or many other features."
-msgstr "එය ඉකුත් වූ පසු, ඔබට සංයුක්ත අනුමà·à¶­à·’, කේත ගුණත්â€à·€à¶º, හ෠අනෙකුත් විà·à·šà·‚à·à¶‚ග භà·à·€à·’ත කළ නොහà·à¶šà·’ය."
+msgstr "එය ඉකුත් වූ පසු, ඔබට ඒකà·à¶¶à¶¯à·Šà¶° අනුමà·à¶­à·’, කේත ගුණත්â€à·€à¶º, හ෠අනෙකුත් විà·à·šà·‚à·à¶‚ග භà·à·€à·’ත෠කිරීමට නොහà·à¶šà·’ය."
msgid "After it expires, you can't use merge approvals, epics, or many other features."
msgstr ""
@@ -3973,7 +4001,7 @@ msgid "All merge conflicts were resolved. The merge request can now be merged."
msgstr ""
msgid "All merge request dependencies have been merged"
-msgstr "සියළුම සංයුක්ත ඉල්ලීම් පරà·à¶ºà¶­à·Šà¶­ ඒකà·à¶¶à¶¯à·Šà¶° කර ඇත"
+msgstr "සියළුම ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් පරà·à¶ºà¶­à·Šà¶­ ඒකà·à¶¶à¶¯à·Šà¶° කර ඇත"
msgid "All paths are relative to the GitLab URL. Do not include %{relative_url_link_start}relative URLs%{relative_url_link_end}."
msgstr ""
@@ -4375,7 +4403,7 @@ msgid "An error occurred while loading issues"
msgstr ""
msgid "An error occurred while loading merge requests."
-msgstr "සංයුක්ත ඉල්ලීම් පූරණය කිරීමේදී දà·à·‚යක් ඇති විය."
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් පූරණය කිරීමේදී දà·à·‚යක් ඇති විය."
msgid "An error occurred while loading projects."
msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’ පූරණය කිරීමේදී දà·à·‚යක් ඇති විය."
@@ -4411,13 +4439,13 @@ msgid "An error occurred while loading the file. Please try again."
msgstr "ගොනුව පූරණයේදී දà·à·‚යකි. නà·à·€à¶­ උත්සà·à·„ කරන්න."
msgid "An error occurred while loading the merge request changes."
-msgstr "සංයුක්ත ඉල්ලීමෙහි වෙනස්කම් පූරණයේ දී දà·à·‚යකි."
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීමෙහි වෙනස්කම් පූරණයේ දී දà·à·‚යකි."
msgid "An error occurred while loading the merge request version data."
-msgstr "සංයුක්ත ඉල්ලීමෙහි අනුවà·à¶¯ දත්ත පූරණයේ දී දà·à·‚යකි."
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීමෙහි අනුවà·à¶¯ දත්ත පූරණයේ දී දà·à·‚යකි."
msgid "An error occurred while loading the merge request."
-msgstr "සංයුක්ත ඉල්ලීම පූරණයේ දී දà·à·‚යකි."
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම පූරණයේ දී දà·à·‚යකි."
msgid "An error occurred while loading the notification settings. Please try again."
msgstr "දà·à¶±à·”ම්දීමේ à·ƒà·à¶šà·ƒà·”ම් පූරණයේ දී දà·à·‚යකි. නà·à·€à¶­ උත්සà·à·„ කරන්න."
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr "ඕනෑම අයෙක්ට ගිණුමක් සෑදීමට à·„à·à¶šà·’ය."
-
msgid "App ID"
msgstr "යෙදුමේ à·„à·à¶³à·”."
@@ -5057,13 +5085,13 @@ msgid "Approve"
msgstr "අනුමත"
msgid "Approve a merge request"
-msgstr "සංයුක්ත ඉල්ලීම අනුමà·à¶­à·’ය"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම අනුමà·à¶­à·’ය"
msgid "Approve merge request"
msgstr ""
msgid "Approve the current merge request."
-msgstr "වත්මන් සංයුක්ත ඉල්ලීමට අනුමà·à¶­à·’ය."
+msgstr "වත්මන් ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීමට අනුමà·à¶­à·’ය."
msgid "Approved"
msgstr "අනුමතයි"
@@ -5147,7 +5175,7 @@ msgid "Are you sure you want to approve %{user}?"
msgstr ""
msgid "Are you sure you want to attempt to merge?"
-msgstr "ඔබට සංයුක්ත කිරීමට තà·à¶­à·Š කිරීමට වුවමනà·à¶¯?"
+msgstr "ඔබට ඒකà·à¶¶à¶¯à·Šà¶°à¶º සඳහ෠තà·à¶­à·Š කිරීමට වුවමනà·à¶¯?"
msgid "Are you sure you want to cancel editing this %{commentType}?"
msgstr "ඔබට මෙම %{commentType} සංස්කරණය අවලංගු කිරීමට වුවමනà·à¶¯?"
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr "ඔබට %{path} අගුළු දà·à¶¸à·“මට වුවමන෠ද?"
@@ -5209,7 +5240,7 @@ msgid "Are you sure you want to lose your issue information?"
msgstr ""
msgid "Are you sure you want to merge immediately?"
-msgstr "ඔබට වහà·à¶¸ සංයුක්ත කිරීමට වුවමන෠ද?"
+msgstr "ඔබට වහà·à¶¸ ඒකà·à¶¶à¶¯à·Šà¶° කිරීමට වුවමන෠ද?"
msgid "Are you sure you want to re-deploy this environment?"
msgstr ""
@@ -5419,7 +5450,7 @@ msgid "Assigned Issues"
msgstr ""
msgid "Assigned merge requests"
-msgstr "පà·à·€à¶»à·– සංයුක්ත ඉල්ලීම්"
+msgstr "පà·à·€à¶»à·– ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම්"
msgid "Assigned projects"
msgstr "පà·à·€à¶»à·– ව්â€à¶ºà·à¶´à·˜à¶­à·’"
@@ -5604,6 +5635,9 @@ msgstr "%{link} මකන්න"
msgid "AuditStreams|Destination URL"
msgstr "ගමනà·à¶±à·Šà¶­ ඒ.à·ƒ.නි."
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr "අගය"
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "නිකිණි"
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr "තිබෙන හවුල් ධà·à·€à¶š:"
-
msgid "Available specific runners"
msgstr ""
@@ -6460,7 +6494,7 @@ msgid "Billing|Add seats"
msgstr ""
msgid "Billing|An email address is only visible for users with public emails."
-msgstr ""
+msgstr "වි-තà·à¶´à·à¶½à·Š ලිපිනයක් දිස් වන්නේ ප්â€à¶»à·ƒà·’ද්ධ වි-තà·à¶´à·à¶½à·Š සතු පරිà·à·Šâ€à¶»à·“ලකයින්ට පමණි."
msgid "Billing|An error occurred while approving %{user}"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "à·à·à¶›à·"
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr "සසඳන්න"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7059,7 +7105,7 @@ msgid "Branches|Delete branch. Are you ABSOLUTELY SURE?"
msgstr ""
msgid "Branches|Delete merged branches"
-msgstr "සංයුක්ත à·à·à¶›à· මකන්න"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶°à·’ත à·à·à¶›à· මකන්න"
msgid "Branches|Delete protected branch"
msgstr ""
@@ -7070,11 +7116,8 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
-msgstr ""
+msgstr "à·à·à¶›à·à·€ අනුව පෙරන්න"
msgid "Branches|Merged into %{default_branch}"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr "විà·à·Šà¶½à·šà·‚ණය"
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr "සක්â€à¶»à·’ය à·à·à¶›à· පෙන්වන්න"
@@ -7127,6 +7173,12 @@ msgstr "පෙරනිමි à·à·à¶›à·à·€ මà·à¶šà·“මට නොහà·à¶š
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr "ඔව්, à·à·à¶›à·à·€ මකන්න"
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7149,7 +7204,7 @@ msgid "Branches|diverged from upstream"
msgstr ""
msgid "Branches|merged"
-msgstr "සංයුක්තයි"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶°à¶ºà·’"
msgid "Branches|protected"
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7619,7 +7677,7 @@ msgid "Can't apply this suggestion."
msgstr "යà·à¶¢à¶±à·à·€ යෙදීමට නොහà·à¶šà·’ය."
msgid "Can't be empty"
-msgstr "හිස් විය නොහà·à¶šà·’ය"
+msgstr "හිස් නොවිය යුතුය"
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -7712,7 +7770,7 @@ msgid "Cannot assign a confidential epic to a non-confidential issue. Make the i
msgstr ""
msgid "Cannot be merged automatically"
-msgstr "ස්වයංක්â€à¶»à·“යව සංයුක්ත කළ නොහà·à¶šà·’ය"
+msgstr "ස්වයංක්â€à¶»à·“යව ඒකà·à¶¶à¶¯à·Šà¶° කළ නොහà·à¶šà·’ය"
msgid "Cannot create the abuse report. The reported user was invalid. Please try again or contact support."
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr "අනන්â€à¶ºà¶±à¶º"
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr "ණය පතට ආකෘතිය පූරණයට අසමත් à
msgid "Checkout|Edit"
msgstr "සංස්කරණය"
-msgid "Checkout|Enter a number greater than 0"
-msgstr "0 ට වඩ෠වà·à¶©à·’ අංකයක් ඇතුල් කරන්න"
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr "ඉකුත්වීම %{expirationMonth}/%{expirationYear}"
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr "ගිට්ලà·à¶¶à·Š භà·à·€à·’ත෠කරන සමà·à¶œà¶¸à·š හ෠සංවිධà·à¶±à¶ºà·š නම"
@@ -8453,9 +8517,15 @@ msgstr "ධà·à·€à¶±à¶ºà·š"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr "පරිසර"
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr "යතුර"
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr "වර්ගය"
@@ -8498,6 +8577,12 @@ msgstr "අගය"
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr "* (සියළු පරිසර)"
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -9936,10 +10024,10 @@ msgid "CompareRevisions|Compare"
msgstr "සසඳන්න"
msgid "CompareRevisions|Create merge request"
-msgstr "සංයුක්ත ඉල්ලීමක් à·ƒà·à¶¯à¶±à·Šà¶±"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීමක් à·ƒà·à¶¯à¶±à·Šà¶±"
msgid "CompareRevisions|Filter by Git revision"
-msgstr ""
+msgstr "ගිට් ප්â€à¶»à¶­à·’à·à·à¶°à¶±à¶º අනුව පෙරන්න"
msgid "CompareRevisions|Select Git revision"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11217,7 +11314,7 @@ msgid "Create a new issue"
msgstr ""
msgid "Create a new project"
-msgstr ""
+msgstr "නව ව්â€à¶ºà·à¶´à·˜à¶­à·’යක් à·ƒà·à¶¯à¶±à·Šà¶±"
msgid "Create a new repository"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11343,7 +11437,7 @@ msgid "Create or close an issue."
msgstr ""
msgid "Create or import your first project"
-msgstr ""
+msgstr "පළමු ව්â€à¶ºà·à¶´à·˜à¶­à·’ය à·ƒà·à¶¯à¶±à·Šà¶± හ෠ආයà·à¶­à¶º"
msgid "Create project"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr "සේවà·à·€ ප්â€à¶»à¶­à·Šâ€à¶ºà¶»à·Šà¶´à¶«à¶ºà¶§ කà·à¶½à¶º"
@@ -12119,22 +12279,22 @@ msgid "DSN"
msgstr ""
msgid "Dashboard"
-msgstr ""
+msgstr "උපකරණ පුවරුව"
msgid "Dashboard uid not found"
msgstr ""
msgid "DashboardProjects|All"
-msgstr ""
+msgstr "සියල්ල"
msgid "DashboardProjects|Personal"
-msgstr ""
+msgstr "පෞද්ගලික"
msgid "DashboardProjects|Trending"
-msgstr ""
+msgstr "නà·à¶œà·“ එන"
msgid "Dashboards"
-msgstr ""
+msgstr "උපකරණ පුවරු"
msgid "Dashboard|%{firstProject} and %{secondProject}"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12215,7 +12378,7 @@ msgid "DastProfiles|Change site profile"
msgstr ""
msgid "DastProfiles|Choose a scan method"
-msgstr ""
+msgstr "සුපිරික්සන ක්â€à¶»à¶¸à¶ºà¶šà·Š තà·à¶»à¶±à·Šà¶±"
msgid "DastProfiles|Could not create the scanner profile. Please try again."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr "යෙදවීම්"
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -14707,10 +14897,10 @@ msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
msgid "Edit your search and try again"
-msgstr ""
+msgstr "සෙවුම සංà·à·à¶°à¶±à¶º කර නà·à·€à¶­ බලන්න"
msgid "Edit your search filter and try again."
-msgstr ""
+msgstr "සෙවුම් පෙරහන සංà·à·à¶°à¶±à¶º කර නà·à·€à¶­ බලන්න."
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr "සෙවීමට අවම à·€à·à¶ºà·™à¶±à·Š අකුරු තුනක් යොදන්න."
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15238,7 +15431,7 @@ msgid "Environment:"
msgstr ""
msgid "EnvironmentDashboard|API"
-msgstr ""
+msgstr "යෙ.ක්â€à¶».මු."
msgid "EnvironmentDashboard|Created through the Deployment API"
msgstr ""
@@ -15250,7 +15443,7 @@ msgid "Environments"
msgstr "පරිසර"
msgid "Environments Dashboard"
-msgstr ""
+msgstr "පරිසර උපකරණ පුවරුව"
msgid "Environments allow you to track deployments of your application. %{linkStart}More information%{linkEnd}."
msgstr ""
@@ -15265,19 +15458,19 @@ msgid "EnvironmentsDashboard|Add a project to the dashboard"
msgstr ""
msgid "EnvironmentsDashboard|Add projects"
-msgstr ""
+msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’ එක් කරන්න"
msgid "EnvironmentsDashboard|Environments Dashboard"
-msgstr ""
+msgstr "පරිසර උපකරණ පුවරුව"
msgid "EnvironmentsDashboard|Job: %{job}"
msgstr ""
msgid "EnvironmentsDashboard|More actions"
-msgstr ""
+msgstr "තව ක්â€à¶»à·’යà·à¶¸à·à¶»à·Šà¶œ"
msgid "EnvironmentsDashboard|Remove"
-msgstr ""
+msgstr "ඉවත් කරන්න"
msgid "EnvironmentsDashboard|The environments dashboard provides a summary of each project's environments' status, including pipeline and alert statuses."
msgstr ""
@@ -15343,7 +15536,7 @@ msgid "Environments|Deployment %{status}"
msgstr ""
msgid "Environments|Edit your search and try again"
-msgstr ""
+msgstr "සෙවුම සංà·à·à¶°à¶±à¶º කර නà·à·€à¶­ බලන්න"
msgid "Environments|Enable review app"
msgstr ""
@@ -15406,7 +15599,7 @@ msgid "Environments|Rollback environment %{name}?"
msgstr ""
msgid "Environments|Search by environment name"
-msgstr ""
+msgstr "පරිසරයේ නම යටතේ සොයන්න"
msgid "Environments|Show all"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -15940,13 +16142,13 @@ msgid "Even if you reach the number of seats in your subscription, you can conti
msgstr ""
msgid "EventFilterBy|Filter by all"
-msgstr ""
+msgstr "සියල්ල අනුව පෙරන්න"
msgid "EventFilterBy|Filter by comments"
-msgstr ""
+msgstr "අදහස් අනුව පෙරන්න"
msgid "EventFilterBy|Filter by designs"
-msgstr ""
+msgstr "නිර්මà·à¶« අනුව පෙරන්න"
msgid "EventFilterBy|Filter by epic events"
msgstr ""
@@ -15955,13 +16157,13 @@ msgid "EventFilterBy|Filter by issue events"
msgstr ""
msgid "EventFilterBy|Filter by merge events"
-msgstr ""
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° සිදුවීම් අනුව පෙරන්න"
msgid "EventFilterBy|Filter by push events"
msgstr ""
msgid "EventFilterBy|Filter by team"
-msgstr ""
+msgstr "කණ්ඩà·à¶ºà¶¸ අනුව පෙරන්න"
msgid "EventFilterBy|Filter by wiki"
msgstr ""
@@ -16086,11 +16288,14 @@ msgid "Everyone can access the wiki."
msgstr ""
msgid "Everyone can contribute"
-msgstr "à·ƒà·à¶¸à¶§ දà·à¶ºà¶š විය à·„à·à¶šà·’ය"
+msgstr "à·ƒà·à¶¸à¶§ දà·à¶ºà¶š වීමට à·„à·à¶šà·’ය"
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr "නවම්"
msgid "February"
msgstr "නවම්"
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17022,13 +17236,13 @@ msgid "Filter"
msgstr ""
msgid "Filter by"
-msgstr ""
+msgstr "අනුව පෙරන්න"
msgid "Filter by %{page_context_word} that are currently open."
msgstr ""
msgid "Filter by Git revision"
-msgstr ""
+msgstr "ගිට් ප්â€à¶»à¶­à·’à·à·à¶°à¶±à¶º අනුව පෙරන්න"
msgid "Filter by issues that are currently closed."
msgstr ""
@@ -17037,19 +17251,19 @@ msgid "Filter by issues that are currently opened."
msgstr ""
msgid "Filter by label"
-msgstr ""
+msgstr "නම්පත අනුව පෙරන්න"
msgid "Filter by merge requests that are currently closed and unmerged."
-msgstr ""
+msgstr "දà·à¶±à¶§ වස෠තිබෙන ඒකà·à¶¶à¶¯à·Šà¶° නොකළ ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් අනුව පෙරන්න."
msgid "Filter by merge requests that are currently merged."
-msgstr ""
+msgstr "දà·à¶±à¶§à¶¸à¶­à·Š ඒකà·à¶¶à¶¯à·Šà¶° කර තිබෙන ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් අනුව පෙරන්න."
msgid "Filter by milestone name"
-msgstr ""
+msgstr "සන්ධිස්ථà·à¶±à¶º අනුව පෙරන්න"
msgid "Filter by name"
-msgstr ""
+msgstr "නම අනුව පෙරන්න"
msgid "Filter by test cases that are currently archived."
msgstr ""
@@ -17058,7 +17272,7 @@ msgid "Filter by test cases that are currently open."
msgstr ""
msgid "Filter by user"
-msgstr ""
+msgstr "පරිà·à·Šâ€à¶»à·“ලකය෠අනුව පෙරන්න"
msgid "Filter parameters are not valid. Make sure that the end date is after the start date."
msgstr ""
@@ -17070,10 +17284,10 @@ msgid "Filter results"
msgstr ""
msgid "Filter results by group"
-msgstr ""
+msgstr "සමූහයට අනුව ප්â€à¶»à¶­à·’ඵල පෙරන්න"
msgid "Filter results by project"
-msgstr ""
+msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’යට අනුව ප්â€à¶»à¶­à·’ඵල පෙරන්න"
msgid "Filter results..."
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17464,10 +17676,10 @@ msgid "GPG signature (loading...)"
msgstr ""
msgid "General"
-msgstr ""
+msgstr "à·ƒà·à¶¸à·à¶±à·Šâ€à¶º"
msgid "General Settings"
-msgstr ""
+msgstr "à·ƒà·à¶¸à·à¶±à·Šâ€à¶º à·ƒà·à¶šà·ƒà·”ම්"
msgid "General pipelines"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -17675,10 +17890,10 @@ msgid "Geo|Filter Geo sites"
msgstr ""
msgid "Geo|Filter by name"
-msgstr ""
+msgstr "නම අනුව පෙරන්න"
msgid "Geo|Filter by status"
-msgstr ""
+msgstr "තත්â€à·€à¶º අනුව පෙරන්න"
msgid "Geo|Full details"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr "GitLab.com (SaaS)"
@@ -18569,6 +18789,9 @@ msgid "GlobalSearch|Recent issues"
msgstr ""
msgid "GlobalSearch|Recent merge requests"
+msgstr "මෑත ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම්"
+
+msgid "GlobalSearch|Result count is over limit."
msgstr ""
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr "සෙවීමක් ඇරඹීමට කෙටිමං යතුර %{kbdOpen}/%{kbdClose} භà·à·€à·’ත෠කරන්න"
+msgid "GlobalSearch|Users"
+msgstr "පරිà·à·Šâ€à¶»à·“ලකයින්"
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18608,7 +18834,7 @@ msgid "GlobalSearch|group"
msgstr "සමූහය"
msgid "GlobalSearch|in %{scope}"
-msgstr ""
+msgstr "%{scope} තුළ"
msgid "GlobalSearch|project"
msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’ය"
@@ -18722,7 +18948,7 @@ msgid "Go to repository charts"
msgstr ""
msgid "Go to repository graph"
-msgstr ""
+msgstr "කà·à·‚්ඨයේ ප්â€à¶»à·ƒà·Šà¶®à·à¶»à¶ºà¶§ යන්න"
msgid "Go to snippets"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18866,16 +19095,22 @@ msgid "Grant write permissions to this key"
msgstr ""
msgid "Graph"
+msgstr "ප්â€à¶»à·ƒà·Šà¶­à·à¶»à¶º"
+
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
msgstr ""
msgid "GraphViewType|Job dependencies"
msgstr ""
msgid "GraphViewType|Show dependencies"
-msgstr ""
+msgstr "පරà·à¶ºà¶­à·Šà¶­ පෙන්වන්න"
msgid "GraphViewType|Stage"
-msgstr ""
+msgstr "අදියර"
msgid "Gravatar"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr "පසුගිය දින 30"
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19319,7 +19560,7 @@ msgid "GroupSelect|No matching results"
msgstr ""
msgid "GroupSelect|Search groups"
-msgstr ""
+msgstr "සමූහ සොයන්න"
msgid "GroupSelect|Select a group"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr "%{group} à·„à·’ ඇති ව්â€à¶ºà·à¶´à·˜à¶­à·’ අන් සමà·
msgid "GroupSettings|Reporting"
msgstr "à·€à·à¶»à·Šà¶­à· කිරීම"
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19508,16 +19746,16 @@ msgid "Groups are the best way to manage projects and members."
msgstr ""
msgid "GroupsDropdown|Frequently visited"
-msgstr ""
+msgstr "නිතර ගොඩවà·à¶¯à·”ණු"
msgid "GroupsDropdown|Groups you visit often will appear here"
-msgstr ""
+msgstr "ඔබ නිතර ගොඩවදින සමූහ මෙහි දිස්වේ"
msgid "GroupsDropdown|Loading groups"
msgstr ""
msgid "GroupsDropdown|Search your groups"
-msgstr ""
+msgstr "ඔබගේ සමූහ සොයන්න"
msgid "GroupsDropdown|Something went wrong on our end."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -19655,7 +19893,7 @@ msgid "GroupsTree|Options"
msgstr ""
msgid "GroupsTree|Search by name"
-msgstr ""
+msgstr "නම අනුව සොයන්න"
msgid "Groups|Avatar will be removed. Are you sure?"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20164,7 +20405,7 @@ msgid "I forgot my password"
msgstr "මà·à¶œà·š මුරපදය අමතක වුණà·"
msgid "I want to explore GitLab to see if it’s worth switching to"
-msgstr ""
+msgstr "ගිට්ලà·à¶¶à·Š වෙත මà·à¶»à·”වීම à·€à·à¶¯à¶œà¶­à·Š දà·à¶ºà·’ බà·à¶½à·“මට එය ගවේෂණය කිරීමට අවà·à·Šâ€à¶ºà¶ºà·’"
msgid "I want to learn the basics of Git"
msgstr ""
@@ -20224,7 +20465,7 @@ msgid "IDE|Review"
msgstr ""
msgid "IDE|Start a new merge request"
-msgstr "නව සංයුක්ත ඉල්ලීමක් අරඹන්න"
+msgstr "නව ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීමක් අරඹන්න"
msgid "IDE|Successful commit"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr "තහවුරු කේතය"
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr "තහවුරුව à·ƒà·à¶»à·Šà¶®à¶šà¶ºà·’"
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20564,10 +20826,10 @@ msgid "Import CSV"
msgstr ""
msgid "Import Projects from Gitea"
-msgstr ""
+msgstr "ගිටිය෠වෙතින් ව්â€à¶ºà·à¶´à·˜à¶­à·’ ආයà·à¶­à¶º"
msgid "Import an exported GitLab project"
-msgstr ""
+msgstr "නිර්යà·à¶­ කළ ගිට්ලà·à¶¶à·Š ව්â€à¶ºà·à¶´à·˜à¶­à·’යක් ආයà·à¶­à¶º"
msgid "Import and export rate limits"
msgstr ""
@@ -20606,22 +20868,22 @@ msgid "Import multiple repositories by uploading a manifest file."
msgstr ""
msgid "Import project"
-msgstr ""
+msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’ය ආයà·à¶­à¶º"
msgid "Import project from"
-msgstr ""
+msgstr "වෙතින් ව්â€à¶ºà·à¶´à·˜à¶­à·’ය ආයà·à¶­à¶º"
msgid "Import projects from Bitbucket"
-msgstr ""
+msgstr "බිට්බකට් වෙතින් ව්â€à¶ºà·à¶´à·˜à¶­à·’ ආයà·à¶­à¶º"
msgid "Import projects from Bitbucket Server"
-msgstr ""
+msgstr "බිට්බකට් සේවà·à¶¯à·à¶ºà¶šà¶º වෙතින් ව්â€à¶ºà·à¶´à·˜à¶­à·’ ආයà·à¶­à¶º"
msgid "Import projects from FogBugz"
-msgstr ""
+msgstr "ෆොග්බග්ස් වෙතින් ව්â€à¶ºà·à¶´à·˜à¶­à·’ ආයà·à¶­à¶º"
msgid "Import projects from GitLab.com"
-msgstr ""
+msgstr "GitLab.com වෙතින් ව්â€à¶ºà·à¶´à·˜à¶­à·’ ආයà·à¶­à¶º"
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23045,7 +23322,7 @@ msgid "Jira project key is not configured."
msgstr ""
msgid "Jira project: %{importProject}"
-msgstr ""
+msgstr "ජිර෠ව්â€à¶ºà·à¶´à·˜à¶­à·’ය: %{importProject}"
msgid "Jira service not configured."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr "LDAP"
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr "නම්පත්"
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23826,7 +24106,7 @@ msgid "Last accessed on"
msgstr ""
msgid "Last activity"
-msgstr ""
+msgstr "අවසà·à¶± ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸"
msgid "Last commit"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr "අගුළු ලූ ගොනු"
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24747,7 +25036,7 @@ msgid "Mailgun events"
msgstr ""
msgid "Main menu"
-msgstr ""
+msgstr "ප්â€à¶»à¶°à·à¶± වට්ටà·à¶»à·”à·€"
msgid "Maintainer"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr "උපකරණ පුවරු කළමනà·à¶šà¶»à¶«à¶º"
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr "à·ƒà·à¶¸à·à¶¢à·’කයින්"
@@ -25429,7 +25730,7 @@ msgid "Members|Role updated successfully."
msgstr ""
msgid "Members|Search groups"
-msgstr ""
+msgstr "සමූහ සොයන්න"
msgid "Members|Search invited"
msgstr ""
@@ -25447,19 +25748,19 @@ msgid "Menu"
msgstr ""
msgid "Merge"
-msgstr "සංයුක්ත"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶°"
msgid "Merge Conflicts"
msgstr ""
msgid "Merge Request"
-msgstr "සංයුක්ත ඉල්ලීම"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම"
msgid "Merge Request Analytics"
msgstr ""
msgid "Merge Requests"
-msgstr "සංයුක්ත ඉල්ලීම්"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම්"
msgid "Merge Requests created"
msgstr ""
@@ -25513,7 +25814,7 @@ msgid "Merge options"
msgstr ""
msgid "Merge request"
-msgstr "සංයුක්ත ඉල්ලීම"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම"
msgid "Merge request %{mr_link} was reviewed by %{mr_author}"
msgstr ""
@@ -25537,7 +25838,7 @@ msgid "Merge request events"
msgstr ""
msgid "Merge request locked."
-msgstr "සංයුක්ත ඉල්ලීම අගුළු ල෠ඇත."
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම අගුළු ල෠ඇත."
msgid "Merge request not merged"
msgstr ""
@@ -25546,13 +25847,13 @@ msgid "Merge request reports"
msgstr ""
msgid "Merge request unlocked."
-msgstr "සංයුක්ත ඉල්ලීම අගුළු à·„à·à¶»à·’ණි."
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම අගුළු à·„à·à¶»à·’ණි."
msgid "Merge request was scheduled to merge after pipeline succeeds"
msgstr ""
msgid "Merge requests"
-msgstr "සංයුක්ත ඉල්ලීම්"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම්"
msgid "Merge requests and approvals settings have moved."
msgstr ""
@@ -25834,7 +26135,7 @@ msgid "Metrics - Prometheus"
msgstr ""
msgid "Metrics Dashboard"
-msgstr ""
+msgstr "ප්â€à¶»à¶¸à·’තික උපකරණ පුවරුව"
msgid "Metrics Dashboard YAML definition"
msgstr ""
@@ -25885,10 +26186,10 @@ msgid "MetricsSettings|Dashboard timezone"
msgstr ""
msgid "MetricsSettings|External dashboard URL"
-msgstr ""
+msgstr "බà·à·„ිර උපකරණ පුවරුවේ ඒ.à·ƒ.නි."
msgid "MetricsSettings|Manage metrics dashboard settings."
-msgstr ""
+msgstr "ප්â€à¶»à¶¸à·’තික පුවරුවේ à·ƒà·à¶šà·ƒà·”ම් කළමනà·à¶šà¶»à¶«à¶º"
msgid "MetricsSettings|Metrics"
msgstr ""
@@ -25921,7 +26222,7 @@ msgid "Metrics|Avg"
msgstr ""
msgid "Metrics|Back to dashboard"
-msgstr ""
+msgstr "උපකරණ පුවරුවට ආපසු"
msgid "Metrics|Cancel"
msgstr ""
@@ -25948,7 +26249,7 @@ msgid "Metrics|Create metric"
msgstr ""
msgid "Metrics|Create new dashboard"
-msgstr ""
+msgstr "නව උපකරණ පුවරුවක් à·ƒà·à¶¯à¶±à·Šà¶±"
msgid "Metrics|Create your dashboard configuration file"
msgstr ""
@@ -26192,7 +26493,7 @@ msgid "MilestoneCombobox|Group milestones"
msgstr ""
msgid "MilestoneCombobox|Milestone"
-msgstr ""
+msgstr "සන්ධිස්ථà·à¶±à¶º"
msgid "MilestoneCombobox|No matching results"
msgstr ""
@@ -26204,7 +26505,7 @@ msgid "MilestoneCombobox|Project milestones"
msgstr ""
msgid "MilestoneCombobox|Search Milestones"
-msgstr ""
+msgstr "සන්ධිස්ථà·à¶± සොයන්න"
msgid "MilestoneCombobox|Select milestone"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26856,7 +27160,7 @@ msgid "New Pipeline Schedule"
msgstr ""
msgid "New Project"
-msgstr ""
+msgstr "නව ව්â€à¶ºà·à¶´à·˜à¶­à·’ය"
msgid "New Protected Branch"
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -26943,7 +27256,7 @@ msgid "New list"
msgstr ""
msgid "New merge request"
-msgstr "නව සංයුක්ත ඉල්ලීම"
+msgstr "නව ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම"
msgid "New milestone"
msgstr ""
@@ -26958,13 +27271,13 @@ msgid "New pipelines cause older pending or running pipelines on the same branch
msgstr ""
msgid "New project"
-msgstr ""
+msgstr "නව ව්â€à¶ºà·à¶´à·˜à¶­à·’ය"
msgid "New project page"
-msgstr ""
+msgstr "නව ව්â€à¶ºà·à¶´à·˜à¶­à·’ පිටුව"
msgid "New project pages"
-msgstr ""
+msgstr "නව ව්â€à¶ºà·à¶´à·˜à¶­à·’ පිටු"
msgid "New project/repository"
msgstr "නව ව්â€à¶ºà·à¶´à·˜à¶­à·’ය/කà·à·‚්ඨය"
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr "ප්â€à¶»à¶­à·’ඵල නà·à¶­"
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27488,7 +27801,7 @@ msgid "Notes|Make this an internal note"
msgstr ""
msgid "Notes|Show all activity"
-msgstr ""
+msgstr "සියළු ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸à·Š පෙන්වන්න"
msgid "Notes|Show comments only"
msgstr ""
@@ -27805,7 +28118,7 @@ msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
msgid "Notify|Milestone removed"
-msgstr ""
+msgstr "සන්ධිස්ථà·à¶±à¶º ඉවත් කෙරිණි"
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,17 +28853,14 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
msgid "Operations"
-msgstr ""
+msgstr "මෙහෙයුම්"
msgid "Operations Dashboard"
-msgstr ""
+msgstr "මෙහෙයුම් පුවරුව"
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -28544,10 +28869,10 @@ msgid "OperationsDashboard|Add projects"
msgstr ""
msgid "OperationsDashboard|More information"
-msgstr ""
+msgstr "තව තොරතුරු"
msgid "OperationsDashboard|Operations Dashboard"
-msgstr ""
+msgstr "මෙහෙයුම් පුවරුව"
msgid "OperationsDashboard|The Operations and Environments dashboards share the same list of projects. When you add or remove a project from one, GitLab adds or removes the project from the other. %{linkStart}More information%{linkEnd}"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr "ඇසුරුම් අනුපිටපත්"
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr "නුගෙට් විධà·à¶±à¶º"
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr "උපකරණ පුවරුවට යොදන්න"
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31124,13 +31547,13 @@ msgid "Profiles|Changing your username can have unintended side effects."
msgstr ""
msgid "Profiles|Choose file..."
-msgstr ""
+msgstr "තà·à¶»à¶±à·Šà¶±..."
msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
msgstr "කිසිදු ව්â€à¶ºà·à¶´à·˜à¶­à·’යක්, කà·à·‚්ඨයක් හ෠සංවිධà·à¶±à¶ºà¶š තොරතුරක් රහිතව ඔබගේ ප්â€à¶»à·ƒà·’ද්ධ පà·à¶­à·’කඩෙහි පෞද්ගලික ව්â€à¶ºà·à¶´à·˜à¶­à·’වල දà·à¶ºà¶šà¶­à·Šà·€ පෙන්වීමට තà·à¶»à¶±à·Šà¶±."
msgid "Profiles|City, country"
-msgstr ""
+msgstr "නගරය, රට"
msgid "Profiles|Commit email"
msgstr ""
@@ -31169,7 +31592,7 @@ msgid "Profiles|Disconnect %{provider}"
msgstr ""
msgid "Profiles|Do not show on profile"
-msgstr ""
+msgstr "පà·à¶­à·’කඩෙහි නොපෙන්වන්න"
msgid "Profiles|Don't display activity-related personal information on your profile."
msgstr "ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸à·Š ආà·à·Šâ€à¶»à·’ත පෞද්ගලික තොරතුරු ඔබගේ පà·à¶­à·’කඩෙහි නොපෙන්වන්න."
@@ -31298,7 +31721,7 @@ msgid "Profiles|Public avatar"
msgstr "ප්â€à¶»à·ƒà·’ද්ධ ප්â€à¶»à¶­à·’රූපය"
msgid "Profiles|Public email"
-msgstr ""
+msgstr "ප්â€à¶»à·ƒà·’ද්ධ වි-තà·à¶´à·‘ල"
msgid "Profiles|Publicly visible private SSH keys can compromise your system."
msgstr ""
@@ -31331,16 +31754,16 @@ msgid "Profiles|The ability to update your name has been disabled by your admini
msgstr ""
msgid "Profiles|The maximum file size allowed is 200KB."
-msgstr ""
+msgstr "ඉඩ දෙන ගොනුවක උපරිම ප්â€à¶»à¶¸à·à¶«à¶º කි.බ. 200 වේ."
msgid "Profiles|This email will be displayed on your public profile."
-msgstr "මෙම වි-තà·à¶´à·‘ල ඔබගේ පොදු පà·à¶­à·’කඩෙහි පෙන්වයි."
+msgstr "මෙම වි-තà·à¶´à·‘ල ඔබගේ ප්â€à¶»à·ƒà·’ද්ධ පà·à¶­à·’කඩෙහි පෙන්වයි."
msgid "Profiles|This email will be used for web based operations, such as edits and merges. %{commit_email_link_start}Learn more.%{commit_email_link_end}"
-msgstr "සංස්කරණ හ෠සංයුක්ත කà·à¶»à·Šà¶ºà¶º à·€à·à¶±à·’ වියමන පà·à¶¯à¶š මෙහෙයුම් සඳහ෠භà·à·€à·’ත෠කෙරේ. %{commit_email_link_start}තව දà·à¶±à¶œà¶±à·Šà¶±.%{commit_email_link_end}"
+msgstr "සංස්කරණ හ෠ඒකà·à¶¶à¶¯à·Šà¶° කà·à¶»à·Šà¶ºà¶º à·€à·à¶±à·’ වියමන පà·à¶¯à¶š මෙහෙයුම් සඳහ෠භà·à·€à·’ත෠කෙරේ. %{commit_email_link_start}තව දà·à¶±à¶œà¶±à·Šà¶±.%{commit_email_link_end}"
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
-msgstr "මෙම ඉමොජිය සහ පණිවිඩය ඔබගේ පà·à¶­à·’කඩෙහි සහ අතුරු මුහුණත පුර෠දිස්වනු ඇත."
+msgstr "මෙම ඉමොජිය සහ පණිවිඩය ඔබගේ පà·à¶­à·’කඩෙහි සහ අතුරු මුහුණත පුර෠දිස්වේ."
msgid "Profiles|This information will appear on your profile."
msgstr "මෙම තොරතුරු ඔබගේ පà·à¶­à·’කඩෙහි දිස්වේ."
@@ -31358,7 +31781,7 @@ msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
msgid "Profiles|Update profile settings"
-msgstr ""
+msgstr "පà·à¶­à·’කඩ à·ƒà·à¶šà·ƒà·”ම් සුරකින්න"
msgid "Profiles|Update username"
msgstr ""
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr "නව ප්â€à¶»à¶­à·’රූපයක් යොදන්න"
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31430,7 +31859,7 @@ msgid "Profiles|Your name was automatically set based on your %{provider_label}
msgstr "%{provider_label} ගිණුම මත පදනම්ව ඔබගේ නම ස්වයංක්â€à¶»à·“යව සකස෠ඇත, එබà·à·€à·’න් ඔබ දන්න෠පුද්ගලයින්ට හඳුනà·à¶œà¶­ à·„à·à¶šà·’ය."
msgid "Profiles|https://website.com"
-msgstr ""
+msgstr "https://අඩවිය.ලංකà·"
msgid "Profiles|username"
msgstr "පරිà·à·“ලක නà·à¶¸à¶º"
@@ -31502,7 +31931,7 @@ msgid "Project Templates"
msgstr ""
msgid "Project URL"
-msgstr ""
+msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’යේ ඒ.à·ƒ.නි."
msgid "Project access must be granted explicitly to each user. If this project is part of a group, access is granted to members of the group."
msgstr ""
@@ -31577,7 +32006,7 @@ msgid "Project milestone"
msgstr ""
msgid "Project name"
-msgstr ""
+msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’යේ නම"
msgid "Project navigation"
msgstr ""
@@ -31616,7 +32045,7 @@ msgid "Project was not found or you do not have permission to add this project t
msgstr ""
msgid "Project: %{name}"
-msgstr ""
+msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’ය: %{name}"
msgid "Project:Branches: %{source_project_path}:%{source_branch} to %{target_project_path}:%{target_branch}"
msgstr ""
@@ -31646,7 +32075,7 @@ msgid "ProjectCreationLevel|Roles allowed to create projects"
msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’ සෑදීමට ඉඩ දී ඇති භූමිකà·"
msgid "ProjectFileTree|Name"
-msgstr ""
+msgstr "නම"
msgid "ProjectFileTree|Show more"
msgstr ""
@@ -31757,7 +32186,7 @@ msgid "ProjectSelect|No matching results"
msgstr ""
msgid "ProjectSelect|Search for project"
-msgstr ""
+msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’යක් සඳහ෠සොයන්න"
msgid "ProjectSelect|Search projects"
msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’ සොයන්න"
@@ -31772,34 +32201,34 @@ msgid "ProjectSelect|There was an error fetching the projects. Please try again.
msgstr ""
msgid "ProjectService|Drone server URL"
-msgstr ""
+msgstr "ඩ්රà·à¶±à·Š සේවà·à¶¯à·à¶ºà¶šà¶ºà·š ඒ.à·ƒ.නි."
msgid "ProjectService|Enter new API key"
-msgstr ""
+msgstr "නව යෙ.ක්â€à¶».මු. යතුර යොදන්න"
msgid "ProjectService|Enter new password"
-msgstr ""
+msgstr "නව මුරපදය යොදන්න"
msgid "ProjectService|Enter new password."
-msgstr ""
+msgstr "නව මුරපදය යොදන්න."
msgid "ProjectService|Enter new token"
msgstr ""
msgid "ProjectService|Jenkins server URL"
-msgstr ""
+msgstr "ජෙකින්ස් සේවà·à¶¯à·à¶ºà¶šà¶ºà·š ඒ.à·ƒ.නි."
msgid "ProjectService|Leave blank to use your current API key"
-msgstr ""
+msgstr "වත්මන් යෙ.ක්â€à¶».මු. යතුර භà·à·€à·’ත෠කිරීමට හිස්ව තබන්න"
msgid "ProjectService|Leave blank to use your current API key."
-msgstr ""
+msgstr "වත්මන් යෙ.ක්â€à¶».මු. යතුර භà·à·€à·’ත෠කිරීමට හිස්ව තබන්න."
msgid "ProjectService|Leave blank to use your current password"
-msgstr ""
+msgstr "වත්මන් මුරපදය භà·à·€à·’ත෠කිරීමට හිස්ව තබන්න"
msgid "ProjectService|Leave blank to use your current password."
-msgstr ""
+msgstr "වත්මන් මුරපදය භà·à·€à·’ත෠කිරීමට හිස්ව තබන්න."
msgid "ProjectService|Leave blank to use your current token."
msgstr ""
@@ -31823,7 +32252,7 @@ msgid "ProjectService|Run CI/CD pipelines with JetBrains TeamCity."
msgstr ""
msgid "ProjectService|TeamCity server URL"
-msgstr ""
+msgstr "ටීම්සිටි සේවà·à¶¯à·à¶ºà¶šà¶ºà·š ඒ.à·ƒ.නි."
msgid "ProjectService|The build configuration ID of the TeamCity project."
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32117,7 +32552,7 @@ msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
msgid "ProjectSettings|Operations"
-msgstr ""
+msgstr "මෙහෙයුම්"
msgid "ProjectSettings|Override user notification preferences for all project members."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32174,7 +32612,7 @@ msgid "ProjectSettings|Roll out new features without redeploying with feature fl
msgstr ""
msgid "ProjectSettings|Search for topic"
-msgstr ""
+msgstr "මà·à¶­à·˜à¶šà·à·€à¶šà·Š සඳහ෠සොයන්න"
msgid "ProjectSettings|Security & Compliance"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32477,16 +32918,16 @@ msgid "Projects with write access"
msgstr ""
msgid "ProjectsDropdown|Frequently visited"
-msgstr ""
+msgstr "නිතර ගොඩවà·à¶¯à·”ණු"
msgid "ProjectsDropdown|Loading projects"
msgstr ""
msgid "ProjectsDropdown|Projects you visit often will appear here"
-msgstr ""
+msgstr "ඔබ නිතර ගොඩවදින ව්â€à¶ºà·à¶´à·˜à¶­à·’ මෙහි දිස්වේ"
msgid "ProjectsDropdown|Search your projects"
-msgstr ""
+msgstr "ඔබගේ ව්â€à¶ºà·à¶´à·˜à¶­à·’ සොයන්න"
msgid "ProjectsDropdown|Something went wrong on our end."
msgstr ""
@@ -32510,34 +32951,34 @@ msgid "ProjectsNew|Contact an administrator to enable options for importing your
msgstr ""
msgid "ProjectsNew|Create"
-msgstr ""
+msgstr "à·ƒà·à¶¯à¶±à·Šà¶±"
msgid "ProjectsNew|Create a blank project to store your files, plan your work, and collaborate on code, among other things."
-msgstr ""
+msgstr "ඔබගේ ගොනු ගබඩ෠කිරීමට, à·€à·à¶© à·ƒà·à¶½à·ƒà·”ම් කිරීමට මෙන්ම කේත සහ වෙනත් දෑ අතර කටයුතු සඳහ෠හිස් ව්â€à¶ºà·à¶´à·˜à¶­à·’යක් à·ƒà·à¶¯à¶±à·Šà¶±."
msgid "ProjectsNew|Create a project pre-populated with the necessary files to get you started quickly."
msgstr ""
msgid "ProjectsNew|Create blank project"
-msgstr ""
+msgstr "හිස් ව්â€à¶ºà·à¶´à·˜à¶­à·’යක් à·ƒà·à¶¯à¶±à·Šà¶±"
msgid "ProjectsNew|Create from template"
-msgstr ""
+msgstr "අච්චුවකින් à·ƒà·à¶¯à¶±à·Šà¶±"
msgid "ProjectsNew|Create new project"
-msgstr ""
+msgstr "නව ව්â€à¶ºà·à¶´à·˜à¶­à·’යක් à·ƒà·à¶¯à¶±à·Šà¶±"
msgid "ProjectsNew|Description format"
-msgstr ""
+msgstr "සවිස්තරයේ ආකෘතිය"
msgid "ProjectsNew|Enable Static Application Security Testing (SAST)"
msgstr ""
msgid "ProjectsNew|Import"
-msgstr ""
+msgstr "ආයà·à¶­à¶º"
msgid "ProjectsNew|Import project"
-msgstr ""
+msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’ය ආයà·à¶­à¶º"
msgid "ProjectsNew|Include a Getting Started README"
msgstr ""
@@ -32546,7 +32987,7 @@ msgid "ProjectsNew|Initialize repository with a README"
msgstr ""
msgid "ProjectsNew|Migrate your data from an external source like GitHub, Bitbucket, or another instance of GitLab."
-msgstr ""
+msgstr "ගිට්හබ්, බිට්බකට් හ෠ගිට්ලà·à¶¶à·Š à·„à·’ වෙනත් සේවà·à¶¯à·à¶ºà¶š à·€à·à¶±à·’ බà·à·„ිර මූලà·à·à·Šâ€à¶»à¶ºà¶šà·’න් ඔබගේ දත්ත සංක්â€à¶»à¶¸à¶«à¶º කරන්න."
msgid "ProjectsNew|No import options available"
msgstr ""
@@ -32558,7 +32999,7 @@ msgid "ProjectsNew|Pick a group or namespace where you want to create this proje
msgstr ""
msgid "ProjectsNew|Project Configuration"
-msgstr ""
+msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’යේ වින්â€à¶ºà·à·ƒà¶º"
msgid "ProjectsNew|Project description %{tag_start}(optional)%{tag_end}"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33500,7 +33938,7 @@ msgid "Recent Project Activity"
msgstr ""
msgid "Recent Searches Service is unavailable"
-msgstr ""
+msgstr "මෑත සෙවුම් සේවà·à·€ නොතිබේ"
msgid "Recent events"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34401,7 +34845,7 @@ msgid "Repository Analytics"
msgstr ""
msgid "Repository Graph"
-msgstr ""
+msgstr "කà·à·‚්ඨයේ ප්â€à¶»à·ƒà·Šà¶®à·à¶»à¶º"
msgid "Repository Settings"
msgstr ""
@@ -34487,6 +34931,9 @@ msgstr "ඉල්ලීම"
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr "නව අනුවà·à¶¯à¶ºà¶šà·Š තිබේ"
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr "අ.ජà·.කෙ. ලිපිනය"
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr "ප්â€à¶»à¶­à·’ඵල හමු නොවිණි"
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr "SSH යතුරු"
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr "තීව්â€à¶»à¶­à· මට්ටම්"
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,8 +36283,11 @@ msgstr "සොයන්න"
msgid "Search GitLab"
msgstr "ගිට්ලà·à¶¶à·Š à·„à·’ සොයන්න"
+msgid "Search Within"
+msgstr "මෙතුළ සොයන්න"
+
msgid "Search a group"
-msgstr ""
+msgstr "සමූහයක් සොයන්න"
msgid "Search an environment spec"
msgstr ""
@@ -35786,88 +36296,88 @@ msgid "Search assignees"
msgstr ""
msgid "Search authors"
-msgstr ""
+msgstr "කතුවරුන් සොයන්න"
msgid "Search branch"
-msgstr ""
+msgstr "à·à·à¶›à·à·€ සොයන්න"
msgid "Search branches"
-msgstr ""
+msgstr "à·à·à¶›à· සොයන්න"
msgid "Search branches and tags"
-msgstr ""
+msgstr "à·à·à¶›à· හ෠අනන්â€à¶ºà¶± සොයන්න"
msgid "Search branches, tags, and commits"
msgstr ""
msgid "Search by Git revision"
-msgstr ""
+msgstr "ගිට් ප්â€à¶»à¶­à·’à·à·à¶°à¶±à¶º අනුව සොයන්න"
msgid "Search by author"
-msgstr ""
+msgstr "කර්තෘ අනුව සොයන්න"
msgid "Search by commit title or SHA"
msgstr ""
msgid "Search by message"
-msgstr ""
+msgstr "පණිවිඩය අනුව සොයන්න"
msgid "Search by name"
-msgstr ""
+msgstr "නම අනුව සොයන්න"
msgid "Search files"
-msgstr ""
+msgstr "ගොනු සොයන්න"
msgid "Search for Namespace"
msgstr ""
msgid "Search for a LDAP group"
-msgstr ""
+msgstr "LDAP සමූහයක් සඳහ෠සොයන්න"
msgid "Search for a group"
-msgstr ""
+msgstr "සමූහයක් සඳහ෠සොයන්න"
msgid "Search for an emoji"
-msgstr ""
+msgstr "ඉමොජියක් සඳහ෠සොයන්න"
msgid "Search for projects, issues, etc."
msgstr ""
msgid "Search for this text"
-msgstr ""
+msgstr "මෙම පෙළ සඳහ෠සොයන්න"
msgid "Search forks"
msgstr ""
msgid "Search groups"
-msgstr ""
+msgstr "සමූහ සොයන්න"
msgid "Search iterations"
-msgstr ""
+msgstr "පුනහ්කරණ සොයන්න"
msgid "Search labels"
-msgstr ""
+msgstr "නම්පත් සොයන්න"
msgid "Search merge requests"
-msgstr ""
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් සොයන්න"
msgid "Search milestones"
-msgstr ""
+msgstr "සන්ධිස්ථà·à¶± සොයන්න"
msgid "Search or create tag"
-msgstr ""
+msgstr "අනන්â€à¶ºà¶± සොයන්න à·„à· à·ƒà·à¶¯à¶±à·Šà¶±"
msgid "Search or filter results..."
-msgstr ""
+msgstr "ප්â€à¶»à¶­à·’ඵල සොයන්න හ෠පෙරන්න..."
msgid "Search or filter results…"
-msgstr ""
+msgstr "සොයන්න හ෠ප්â€à¶»à¶­à·’ඵල පෙරන්න…"
msgid "Search page"
-msgstr ""
+msgstr "පිටුව සොයන්න"
msgid "Search project"
-msgstr ""
+msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’ය සොයන්න"
msgid "Search projects"
msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’ සොයන්න"
@@ -35876,34 +36386,34 @@ msgid "Search projects..."
msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’ සොයන්න..."
msgid "Search protected branches"
-msgstr ""
+msgstr "රක්â€à·‚ිත à·à·à¶›à· සොයන්න"
msgid "Search rate limits"
-msgstr ""
+msgstr "අනුපà·à¶­ සීම෠සොයන්න"
msgid "Search refs"
msgstr ""
msgid "Search requirements"
-msgstr ""
+msgstr "අවà·à·Šâ€à¶ºà¶­à· සොයන්න"
msgid "Search settings"
-msgstr ""
+msgstr "à·ƒà·à¶šà·ƒà·”ම් සොයන්න"
msgid "Search users"
-msgstr ""
+msgstr "පරිà·à·Šâ€à¶»à·“ලකයින් සොයන්න"
msgid "Search users or groups"
-msgstr ""
+msgstr "පරිà·à·Šâ€à¶»à·“ලකයින් හ෠සමූහ සොයන්න"
msgid "Search your project dependencies for their licenses and apply policies."
-msgstr ""
+msgstr "ඔවුන්ගේ බලපත්â€à¶» සඳහ෠ඔබගේ ව්â€à¶ºà·à¶´à·˜à¶­à·’යේ පරà·à¶ºà¶­à·Šà¶­ සොයන්න හ෠ප්â€à¶»à¶­à·’පත්ති යොදන්න."
msgid "Search your projects"
msgstr "ඔබගේ ව්â€à¶ºà·à¶´à·˜à¶­à·’ සොයන්න"
msgid "SearchAutocomplete|All GitLab"
-msgstr ""
+msgstr "සමස්ත ගිට්ලà·à¶¶à·Š"
msgid "SearchAutocomplete|Issues I've created"
msgstr ""
@@ -35921,7 +36431,7 @@ msgid "SearchAutocomplete|Merge requests that I'm a reviewer"
msgstr ""
msgid "SearchAutocomplete|in all GitLab"
-msgstr ""
+msgstr "සමස්ත ගිට්ලà·à¶¶à·Šà·„à·’"
msgid "SearchAutocomplete|in group %{groupName}"
msgstr ""
@@ -35946,13 +36456,13 @@ msgstr ""
msgid "SearchResults|code result"
msgid_plural "SearchResults|code results"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "කේත ප්â€à¶»à¶­à·’ඵලය"
+msgstr[1] "කේත ප්â€à¶»à¶­à·’ඵල"
msgid "SearchResults|comment"
msgid_plural "SearchResults|comments"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "අදහස"
+msgstr[1] "අදහස්"
msgid "SearchResults|commit"
msgid_plural "SearchResults|commits"
@@ -35971,18 +36481,18 @@ msgstr[1] ""
msgid "SearchResults|merge request"
msgid_plural "SearchResults|merge requests"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම"
+msgstr[1] "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම්"
msgid "SearchResults|milestone"
msgid_plural "SearchResults|milestones"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "සන්ධිස්ථà·à¶±à¶º"
+msgstr[1] "සන්ධිස්ථà·à¶±"
msgid "SearchResults|project"
msgid_plural "SearchResults|projects"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "ව්â€à¶ºà·à¶´à·˜à¶­à·’ය"
+msgstr[1] "ව්â€à¶ºà·à¶´à·˜à¶­à·’"
msgid "SearchResults|snippet"
msgid_plural "SearchResults|snippets"
@@ -36063,13 +36573,13 @@ msgid "Security Configuration"
msgstr ""
msgid "Security Dashboard"
-msgstr ""
+msgstr "ආරක්â€à·‚ණ උපකරණ පුවරුව"
msgid "Security Finding not found"
msgstr ""
msgid "Security dashboard"
-msgstr ""
+msgstr "ආරක්â€à·‚ණ උපකරණ පුවරුව"
msgid "Security navigation"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36630,11 +37179,26 @@ msgid "SecurityReports|Add projects"
msgstr ""
msgid "SecurityReports|All activity"
+msgstr "සියළු ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸à·Š"
+
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
msgstr ""
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36768,7 +37332,7 @@ msgid "SecurityReports|New vulnerabilities are vulnerabilities that the security
msgstr ""
msgid "SecurityReports|No activity"
-msgstr ""
+msgstr "ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸à·Š නà·à¶­"
msgid "SecurityReports|No longer detected"
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37500,7 +38073,7 @@ msgid "SetStatusModal|Clear status after"
msgstr "තත්â€à·€à¶º මක෠දà·à¶¸à·“ම"
msgid "SetStatusModal|Displays that you are busy or not able to respond"
-msgstr ""
+msgstr "ඔබ කà·à¶»à·Šà¶ºà¶¶à·„ුල හ෠ප්â€à¶»à¶­à·’චà·à¶» දà·à¶šà·Šà·€à·“මට නොහà·à¶šà·’ බව පෙන්වයි"
msgid "SetStatusModal|Edit status"
msgstr "තත්â€à·€à¶º සංස්කරණය"
@@ -37515,7 +38088,7 @@ msgid "SetStatusModal|Set status"
msgstr "තත්â€à·€à¶º සකසන්න"
msgid "SetStatusModal|Set yourself as busy"
-msgstr ""
+msgstr "ඔබ කà·à¶»à·Šà¶ºà¶¶à·„ුල යà·à¶ºà·’ සකසන්න"
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37661,7 +38237,7 @@ msgid "Show all %{issuable_type}."
msgstr ""
msgid "Show all activity"
-msgstr ""
+msgstr "සියළු ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸à·Š පෙන්වන්න"
msgid "Show all breadcrumbs"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr "ගිට්ලà·à¶¶à·Š වෙත පිවිසෙන්න"
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38509,13 +39100,13 @@ msgid "SortOptions|Manual"
msgstr "අතින්"
msgid "SortOptions|Merged date"
-msgstr "සංයුක්ත දිනය"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶°à·’ත දිනය"
msgid "SortOptions|Merged earlier"
msgstr ""
msgid "SortOptions|Merged recently"
-msgstr "මෑතදී සංයුක්ත"
+msgstr "මෑතදී ඒකà·à¶¶à¶¯à·Šà¶°à·’ත"
msgid "SortOptions|Milestone due date"
msgstr ""
@@ -38548,7 +39139,7 @@ msgid "SortOptions|Oldest created"
msgstr ""
msgid "SortOptions|Oldest last activity"
-msgstr ""
+msgstr "පරණම අවසà·à¶± ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸"
msgid "SortOptions|Oldest sign in"
msgstr ""
@@ -38701,7 +39292,7 @@ msgid "SourcegraphAdmin|Sourcegraph URL"
msgstr ""
msgid "SourcegraphAdmin|https://sourcegraph.example.com"
-msgstr ""
+msgstr "https://sourcegraph.example.com"
msgid "SourcegraphPreferences|This feature is experimental and currently limited to certain projects."
msgstr "මෙම විà·à·šà·‚à·à¶‚ගය පරීක්â€à·‚à·à¶­à·Šà¶¸à¶š වන අතර දà·à¶±à¶§ ඇතà·à¶¸à·Š ව්â€à¶ºà·à¶´à·˜à¶­à·’ සඳහ෠සීම෠වේ."
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr "සෙවුම අරඹන්න"
@@ -38919,6 +39516,9 @@ msgstr "සංඛ්â€à¶ºà·à¶½à·šà¶›à¶±"
msgid "Status"
msgstr "තත්â€à·€à¶º"
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr "à·à·à¶›à·à·€ මà·à¶»à·”à·€"
msgid "Switch branch/tag"
msgstr "à·à·à¶›à·à·€/අනන්â€à¶ºà¶±à¶º මà·à¶»à·”à·€"
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr "ගිට්ලà·à¶¶à·Š නෙක්â€à·ƒà·Šà¶§à·Š මà·à¶»à·”à·€"
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr "කණ්ඩà·à¶ºà¶¸"
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr "පහත SSH යතුර පරිපà·à¶½à¶šà¶ºà·™à¶šà·” විසින් මක෠දà·à¶¸à·’ණි, %{username}."
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr "පහත අංග නිර්යà·à¶­ නොකෙරේ:"
@@ -40805,10 +41414,10 @@ msgid "The pipelines schedule runs pipelines in the future, repeatedly, for spec
msgstr ""
msgid "The project can be accessed by any logged in user except external users."
-msgstr "බà·à·„ිර පුද්ගලයින්ට à·„à·à¶» ඇතුළු වූ ඕනෑම අයෙකුට ව්â€à¶ºà·à¶´à·˜à¶­à·’යට ප්â€à¶»à·€à·šà· විය à·„à·à¶šà·’ය."
+msgstr "බà·à·„ිර පුද්ගලයින්ට à·„à·à¶» ඇතුළු වූ ඕනෑම අයෙකුට ව්â€à¶ºà·à¶´à·˜à¶­à·’යට ප්â€à¶»à·€à·šà· වීමට à·„à·à¶šà·’ය."
msgid "The project can be accessed without any authentication."
-msgstr "කිසිදු සත්â€à¶ºà·à¶´à¶±à¶ºà¶šà·’න් තොරව ව්â€à¶ºà·à¶´à·˜à¶­à·’යට ප්â€à¶»à·€à·šà· විය à·„à·à¶šà·’ය."
+msgstr "කිසිදු සත්â€à¶ºà·à¶´à¶±à¶ºà¶šà·’න් තොරව ව්â€à¶ºà·à¶´à·˜à¶­à·’යට ප්â€à¶»à·€à·šà· වීමට à·„à·à¶šà·’ය."
msgid "The project has already been added to your dashboard."
msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’ය දà·à¶±à¶§à¶¸à¶­à·Š ඔබගේ උපකරණ පුවරුවට එක් කර ඇත."
@@ -40883,7 +41492,7 @@ msgid "The snippet is visible to any logged in user except external users."
msgstr ""
msgid "The source project of this merge request has been removed."
-msgstr "මෙම සංයුක්ත ඉල්ලීමේ මූලà·à·à·Šâ€à¶» ව්â€à¶ºà·à¶´à·˜à¶­à·’ය ඉවත් කර ඇත."
+msgstr "මෙම ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීමේ මූලà·à·à·Šâ€à¶» ව්â€à¶ºà·à¶´à·˜à¶­à·’ය ඉවත් කර ඇත."
msgid "The source topic and the target topic are identical."
msgstr ""
@@ -40955,7 +41564,7 @@ msgid "There are currently no mirrored repositories."
msgstr ""
msgid "There are merge conflicts"
-msgstr "සංයුක්ත à¶à¶§à·Šà¶§à¶± තිබේ"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° à¶à¶§à·Šà¶§à¶± තිබේ"
msgid "There are no GPG keys associated with this account."
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr "අයà·à¶ à·’ත සටහන් නà·à¶­"
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr "තවම සංරක්â€à·‚ිත ව්â€à¶ºà·à¶´à·˜à¶­à·’ නà·à¶­"
-
msgid "There are no archived requirements"
msgstr ""
@@ -41000,7 +41606,7 @@ msgid "There are no closed issues"
msgstr ""
msgid "There are no closed merge requests"
-msgstr "à·€à·à·ƒà·– සංයුක්ත ඉල්ලීම් නà·à¶­"
+msgstr "à·€à·à·ƒà·– ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් නà·à¶­"
msgid "There are no commits yet."
msgstr ""
@@ -41024,7 +41630,7 @@ msgid "There are no open issues"
msgstr ""
msgid "There are no open merge requests"
-msgstr "විවෘත සංයුක්ත ඉල්ලීම් නà·à¶­"
+msgstr "විවෘත ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් නà·à¶­"
msgid "There are no open requirements"
msgstr "විවෘත අවà·à·Šâ€à¶ºà¶­à· නà·à¶­"
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr "තවම ඇසුරුම් නà·à¶­"
-msgid "There are no projects shared with this group yet"
-msgstr "මෙම සමූහය සමග තවම බෙද෠ගත් ව්â€à¶ºà·à¶´à·˜à¶­à·’ නà·à¶­"
-
msgid "There are no secure files yet."
msgstr "තවම ආරක්â€à·‚ිත ගොනු නà·à¶­."
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr "CRM සබඳත෠ගà·à¶±à·“මේ ගà·à¶§à¶½à·”වකි."
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr "පරිසරයේ තොරතුරු ගà·à¶±à·“මේ දී දà·à·‚යකි."
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr "ජිර෠පරිà·à·“ලකයින් ලබ෠ගà·à¶±à·“මේ දà·à·‚යකි."
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr "ඔබගේ වෙනස්කම් සුරà·à¶šà·“මේ දà·à·‚යකි."
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr "මෙම සමූහය"
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -41777,10 +42389,10 @@ msgid "This merge request is from an internal project to a public project."
msgstr ""
msgid "This merge request is locked."
-msgstr "සංයුක්ත ඉල්ලීම අගුලු ල෠ඇත."
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම අගුළු ල෠ඇත."
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
-msgstr "සංයුක්ත ඉල්ලීම ඒකà·à¶¶à¶¯à·Šà¶°à¶ºà·’. මෙම යà·à¶¢à¶±à·à·€ යෙදීමට, ගොනුව සෘජුව සංස්කරණය කරන්න."
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම ඒකà·à¶¶à¶¯à·Šà¶°à¶ºà·’. මෙම යà·à¶¢à¶±à·à·€ යෙදීමට, ගොනුව සෘජුව සංස්කරණය කරන්න."
msgid "This namespace has already been taken! Please choose another one."
msgstr ""
@@ -41849,7 +42461,7 @@ msgid "This project is not subscribed to any project pipelines."
msgstr ""
msgid "This project is public. Non-members can guess the Service Desk email address, because it contains the group and project name. %{linkStart}How do I create a custom email address?%{linkEnd}"
-msgstr ""
+msgstr "මෙය ප්â€à¶»à·ƒà·’ද්ධ ව්â€à¶ºà·à¶´à·˜à¶­à·’යකි. එහි සමූහය හ෠ව්â€à¶ºà·à¶´à·˜à¶­à·’යේ නම අඩංගු බà·à·€à·’න් à·ƒà·à¶¸à·à¶¢à·’කයින් නොවන අයට සේව෠මේසයේ වි-තà·à¶´à·à¶½à·Š ලිපිනය අනුමà·à¶± කිරීමට à·„à·à¶šà·’ය. %{linkStart}අභිරුචි වි-තà·à¶´à·à¶½à·Š ලිපිනයක් à·ƒà·à¶¯à· ගන්නේ කෙසේද?%{linkEnd}"
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr "සිරà·à·ƒà·’ය (අවà·à·Šâ€à¶ºà¶ºà·’)"
msgid "Title:"
msgstr "සිරà·à·ƒà·’ය:"
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr "සිරà·à·ƒà·’ සහ සවිස්තර"
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr "සියල්ල අහවර බව යොදන්න"
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr "සියල්ල අහවරයි!"
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42696,10 +43323,10 @@ msgid "TopNav|Go back"
msgstr "ආපසු යන්න"
msgid "TopNav|Switch to"
-msgstr ""
+msgstr "මà·à¶»à·” වන්න"
msgid "TopNav|Your dashboards"
-msgstr ""
+msgstr "ඔබගේ උපකරණ පුවරු"
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43100,7 +43736,7 @@ msgid "URL"
msgstr "ඒ.ස.නි."
msgid "URL cannot be blank"
-msgstr "ඒ.à·ƒ.නි. හිස්විය නොහà·à¶šà·’ය"
+msgstr "ඒ.ස.නි. හිස් නොවිය යුතුය"
msgid "URL is invalid"
msgstr "ඒ.à·ƒ.නි. à·€à·à¶»à¶¯à·’ය"
@@ -43151,7 +43787,7 @@ msgid "UTC"
msgstr ""
msgid "Unable to apply suggestions to a deleted line."
-msgstr "මක෠දà·à¶¸à·– රේඛà·à·€à¶šà¶§ යà·à¶¢à¶±à· යෙදිය නොහà·à¶šà·’ය."
+msgstr "මක෠දà·à¶¸à·– රේඛà·à·€à¶šà¶§ යà·à¶¢à¶±à· යෙදීමට නොහà·à¶šà·’ය."
msgid "Unable to build Slack link."
msgstr "ස්ලà·à¶šà·Š සබà·à¶³à·’ය තà·à¶±à·“මට නොහà·à¶šà·’ය."
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr "අසතුටුයිද?"
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr "මි.තත්."
@@ -43343,7 +43988,7 @@ msgid "Unlock account"
msgstr "ගිණුම අගුළු හරින්න"
msgid "Unlock merge request"
-msgstr "සංයුක්ත ඉල්ලීම අගුළු à·„à·à¶»à·“ම"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම අගුළු à·„à·à¶»à·“ම"
msgid "Unlock more features with GitLab Ultimate"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr "අග්â€à¶»à¶ºà¶§ වෙනස්කම් උඩුගත වෙම
msgid "Uploading..."
msgstr "උඩුගත වෙමින්..."
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr "උදව් සබà·à¶³à·’ය %{linkTitle}"
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’යකට CI විනà·à¶©à·’ භà·à·€à·’à
msgid "UsageQuota|CI/CD minutes usage"
msgstr "CI/CD විනà·à¶©à·’ භà·à·€à·’තය"
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr "පරà·à¶ºà¶­à·Šà¶­ ප්â€à¶»à¶­à·’යුක්තය"
@@ -43721,7 +44360,7 @@ msgid "UsageQuota|Repository"
msgstr "කà·à·‚්ඨය"
msgid "UsageQuota|Search"
-msgstr ""
+msgstr "සොයන්න"
msgid "UsageQuota|Seats"
msgstr "ආසන"
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr "අසීමිත"
-
msgid "UsageQuota|Uploads"
msgstr "උඩුගත කිරීම්"
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,17 +44467,17 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
-msgstr "%{projectsLockedText} à·„à·’ %{actualRepositorySizeLimit} නොමිලේ ආචයන සීමà·à·€à¶§ ඔබ ළඟ෠වී ඇත. ඒව෠අගුලු à·„à·à¶»à·“මට, අතිරේක ආචයනය මිලදී ගන්න."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
+msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr "ඔබගේ භà·à·€à·’තය: %{usage} %{limit}"
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
-msgstr "ඔබ මිලදී ගත් ආචයනය අඩු වෙමින් පවතී. ව්â€à¶ºà·à¶´à·˜à¶­à·’ අගුලු à·€à·à¶§à·“ම à·€à·à·…à·à¶šà·Šà·€à·“මට, තවත් ආචයනය මිලදී ගන්න."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
+msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
msgstr ""
@@ -43877,7 +44504,7 @@ msgid "UsageTrends|Items"
msgstr "අථක"
msgid "UsageTrends|Merge requests"
-msgstr "සංයුක්ත ඉල්ලීම්"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම්"
msgid "UsageTrends|Month"
msgstr "මà·à·ƒà¶º"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44272,7 +44902,7 @@ msgid "UserProfile|Groups are the best way to manage projects and members."
msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’ à·„à· à·ƒà·à¶¸à·à¶¢à·’කයින් කළමනà·à¶šà¶»à¶«à¶ºà¶§ සමූහ හොඳම ක්â€à¶»à¶¸à¶º වේ."
msgid "UserProfile|Join or create a group to start contributing by commenting on issues or submitting merge requests!"
-msgstr "ගà·à¶§à·…à·” සඳහ෠අදහස් දà·à¶šà·Šà·€à·“මෙන් හ෠සංයුක්ත ඉල්ලීම් යොමු කිරීමෙන් දà·à¶ºà¶šà¶­à·Šâ€à·€à¶º ඇරඹීමට සමූහයකට එක්වන්න à·„à· à·ƒà·à¶¯à¶±à·Šà¶±!"
+msgstr "ගà·à¶§à·…à·” සඳහ෠අදහස් දà·à¶šà·Šà·€à·“මෙන් හ෠ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් යොමු කිරීමෙන් දà·à¶ºà¶šà¶­à·Šâ€à·€à¶º ඇරඹීමට සමූහයකට එක්වන්න à·„à· à·ƒà·à¶¯à¶±à·Šà¶±!"
msgid "UserProfile|Most Recent Activity"
msgstr "වඩà·à¶­à·Šà¶¸ මෑත ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸"
@@ -44289,9 +44919,6 @@ msgstr "පෞද්ගලික ව්â€à¶ºà·à¶´à·˜à¶­à·’"
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr "උච්චà·à¶»à¶«à¶º: %{pronunciation}"
-msgid "UserProfile|Report abuse"
-msgstr "අයුතු à·€à·à¶»à·Šà¶­à·à·€"
-
msgid "UserProfile|Retry"
msgstr "යළි උත්සà·à·„ය"
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr "උපකරණ පුවරුව"
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr "ප්â€à¶»à¶½à·šà¶›à¶± වෙත යන්න"
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr "නවත්වන්න"
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr "අනුවà·à¶¯à¶º %{versionNumber}"
msgid "Version %{versionNumber} (latest)"
msgstr "අනුවà·à¶¯à¶º %{versionNumber} (නවතම)"
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr "යà·à·€à¶­à·Šà¶šà·à¶½à·“නයි"
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr "යà·à·€à¶­à·Šà¶šà·à¶½à¶ºà¶šà·Š තිබේ"
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr "ඔබගේ ගිට්ලà·à¶¶à·Š අනුවà·à¶¯à¶º"
@@ -44716,13 +45379,13 @@ msgid "View all environments."
msgstr "සියළු පරිසර බලන්න."
msgid "View all groups"
-msgstr ""
+msgstr "සියළුම සමූහ බලන්න"
msgid "View all issues"
msgstr ""
msgid "View all projects"
-msgstr ""
+msgstr "සියළුම ව්â€à¶ºà·à¶´à·˜à¶­à·’ බලන්න"
msgid "View blame"
msgstr ""
@@ -45295,7 +45958,7 @@ msgid "Waiting for approval"
msgstr "අනුමà·à¶­à·’යට රà·à¶³à·™à¶¸à·’න්"
msgid "Waiting for merge (open and assigned)"
-msgstr "සංයුක්තයට රà·à¶³à·™à¶¸à·’න් (විවෘත හ෠පවර෠ඇත)"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶°à¶ºà¶§ රà·à¶³à·™à¶¸à·’න් (විවෘත හ෠පවර෠ඇත)"
msgid "Waiting for performance data"
msgstr ""
@@ -45319,7 +45982,7 @@ msgid "Watch how"
msgstr "කෙසේදà·à¶ºà·’ නරඹන්න"
msgid "We also use email for avatar detection if no avatar is uploaded."
-msgstr ""
+msgstr "ප්â€à¶»à¶­à·’රූපයක් උඩුගත නොකළහොත් ප්â€à¶»à¶­à·’රූපය අනà·à·€à¶»à¶«à¶º උදෙස෠ද වි-තà·à¶´à·‘ල භà·à·€à·’ත෠කෙරේ."
msgid "We are currently unable to fetch data for the pipeline header."
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr "මෙම අදියර පෙන්වීමට අපට ප්â€à¶»à¶¸à·à¶«à·€à¶­à·Š දත්ත නà·à¶­."
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr "පහත සඳහන් දà·à·‚ අපට හමුව ඇත:"
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45445,7 +46108,7 @@ msgid "WebIDE|Go to fork"
msgstr ""
msgid "WebIDE|Merge request"
-msgstr "සංයුක්ත ඉල්ලීම"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම"
msgid "WebIDE|Quickly and easily edit multiple files in your project."
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr "අදහස්"
-
msgid "Webhooks|Confidential comments"
msgstr "විà·à·Šà·€à·ƒà¶±à·“ය අදහස්"
@@ -45616,15 +46276,12 @@ msgid "Webhooks|Member events"
msgstr "à·ƒà·à¶¸à·à¶¢à·’ක සිදුවීම්"
msgid "Webhooks|Merge request events"
-msgstr "සංයුක්ත ඉල්ලීම් සිදුවීම්"
-
-msgid "Webhooks|Pipeline events"
-msgstr ""
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් සිදුවීම්"
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr "ඒ.ස.නි."
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr "එකතු"
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr "අවලංගු"
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr "වස෠ඇත"
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr "කà·à¶»à·Šà¶ºà¶ºà¶šà·Š à·ƒà·à¶¯à¶±à·Šà¶±"
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,10 +46833,16 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
+msgstr "සන්ධිස්ථà·à¶±à¶º"
+
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
msgstr ""
msgid "WorkItem|No iteration"
@@ -46177,6 +46852,9 @@ msgid "WorkItem|No matching results"
msgstr ""
msgid "WorkItem|No milestone"
+msgstr "සන්ධිස්ථà·à¶± නà·à¶­"
+
+msgid "WorkItem|No objectives or key results are currently assigned."
msgstr ""
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
@@ -46185,6 +46863,9 @@ msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr "වර්ගය තà·à¶»à¶±à·Šà¶±"
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr "අදහසක් ලියන්න…"
msgid "Write a description or drag your files here…"
msgstr "සවිස්තරයක් ලියන්න හ෠ගොනු මෙතà·à¶±à¶§ අදින්න…"
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr "සවිස්තරයක් ලියන්න…"
@@ -46460,10 +47153,10 @@ msgstr "මෙලෙස ගිට්ලà·à¶¶à·Š වෙත ඇතුළු à·€à·
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr "à·ƒà·à¶¸à·€à·’ට ඔබගේ ඒ.à·ƒ.නි. පසුව වෙනà·
msgid "You can always edit this later"
msgstr "ඔබට මෙය පසුව සංà·à·à¶°à¶±à¶º කළ à·„à·à¶šà·’ය"
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr "ගිට්ලà·à¶¶à·Š දà·à¶ºà¶šà¶­à·Šâ€à·€ පිළිබඳ à·€
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,17 +47279,11 @@ msgstr "යෙදුමට / සමූහයට හ෠ව්â€à¶ºà·à¶´à·˜à¶­à
msgid "You can now close this window."
msgstr "දà·à¶±à·Š ඔබට මෙම කවුළුව වස෠දà·à¶¸à·’ය à·„à·à¶šà·’ය."
-msgid "You can now export your security dashboard to a CSV report."
-msgstr "දà·à¶±à·Š ඔබගේ ආරක්â€à·‚ණ උපකරණ පුවරුව CSV à·€à·à¶»à·Šà¶­à·à·€à¶šà¶§ නිර්යà·à¶­ කළ à·„à·à¶šà·’ය."
-
msgid "You can now submit a merge request to get this change into the original branch."
-msgstr "මෙම වෙනස මුල් à·à·à¶›à·à·€ වෙත ගà·à¶±à·“මට දà·à¶±à·Š සංයුක්ත ඉල්ලීමක් යොමු කළ à·„à·à¶šà·’ය."
+msgstr "මෙම වෙනස මුල් à·à·à¶›à·à·€ වෙත ගà·à¶±à·“මට දà·à¶±à·Š ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීමක් යොමු කිරීමට à·„à·à¶šà·’ය."
msgid "You can now submit a merge request to get this change into the original project."
-msgstr "මෙම වෙනස මුල් à·à·à¶›à·à·€ වෙත ගà·à¶±à·“මට දà·à¶±à·Š සංයුක්ත ඉල්ලීමක් යොමු කළ à·„à·à¶šà·’ය."
-
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
+msgstr "මෙම වෙනස මුල් à·à·à¶›à·à·€ වෙත ගà·à¶±à·“මට දà·à¶±à·Š ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීමක් යොමු කිරීමට à·„à·à¶šà·’ය."
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr "ඔබට එකවර සබඳත෠%{max_contacts} ක් එක් කළ à·„à·à¶šà·’ය"
@@ -46613,7 +47292,7 @@ msgid "You can only edit files when you are on a branch"
msgstr "ඔබ à·à·à¶›à·à·€à¶š සිටින විට පමණක් ගොනු සංස්කරණය කළ à·„à·à¶šà·’ය"
msgid "You can only merge once the items above are resolved."
-msgstr "ඉහත අංග විසඳූ පසු පමණක් සංයුක්ත කළ à·„à·à¶šà·’ය."
+msgstr "ඉහත අංග විසඳූ පසු පමණක් ඒකà·à¶¶à¶¯à·Šà¶° කිරීමට à·„à·à¶šà·’ය."
msgid "You can only transfer the project to namespaces you manage."
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46733,7 +47415,7 @@ msgid "You don't have any deployments right now."
msgstr ""
msgid "You don't have any open merge requests"
-msgstr "ඔබ සතුව විවෘත සංයුක්ත ඉල්ලීම් නà·à¶­"
+msgstr "ඔබ සතුව විවෘත ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම් නà·à¶­"
msgid "You don't have any recent searches"
msgstr "ඔබ සතුව මෑත සෙවීම් නà·à¶­"
@@ -46741,6 +47423,9 @@ msgstr "ඔබ සතුව මෑත සෙවීම් නà·à¶­"
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr "ඔබට මෙම ව්â€à¶ºà·à¶´à·˜à¶­à·’ය සෑදීමට අවසර නà·à¶­"
@@ -46803,6 +47488,9 @@ msgstr "ඔබට මෙම ව්â€à¶ºà·à¶´à·˜à¶­à·’ය සඳහ෠HTTP අ
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr "ඔබට නොසුරà·à¶šà·’ වෙනස්කම් ඇත"
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr "ඔබ %{user} ප්â€à¶»à¶­à·’ක්â€à·‚ේප කර ඇත"
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr "යූටියුබ්"
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr "ඔබගේ ගිට්ලà·à¶¶à·Š ගිණුම සඳහ෠ඉà¶
msgid "Your GitLab group"
msgstr "ඔබගේ ගිට්ලà·à¶¶à·Š සමූහය"
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr "ඔබගේ සමූහය"
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr "ඔබගේ සමූහ"
@@ -47275,6 +47961,15 @@ msgstr "ඔබගේ නම"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr "ඔබගේ නව %{accessTokenType}"
@@ -47315,7 +48010,7 @@ msgid "Your projects"
msgstr "ඔබගේ ව්â€à¶ºà·à¶´à·˜à¶­à·’"
msgid "Your public email will be displayed on your public profile."
-msgstr "ඔබගේ ප්â€à¶»à·ƒà·’ද්ධ වි-තà·à¶´à·‘ල ප්â€à¶»à·ƒà·’ද්ධ පà·à¶­à·’කඩෙහි දර්à·à¶±à¶º වනු ඇත."
+msgstr "ඔබගේ ප්â€à¶»à·ƒà·’ද්ධ වි-තà·à¶´à·‘ල ප්â€à¶»à·ƒà·’ද්ධ පà·à¶­à·’කඩෙහි දිස්වනු ඇත."
msgid "Your request for access could not be processed: %{error_message}"
msgstr "ප්â€à¶»à·€à·šà·à¶º සඳහ෠ඔබගේ ඉල්ලීම à·ƒà·à¶šà·ƒà·“මට නොහà·à¶šà·’ය: %{error_message}"
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr "වෙනස් කළ නොහà·à¶šà·’ය"
@@ -47656,13 +48357,23 @@ msgid "cannot itself be blocked"
msgstr ""
msgid "cannot merge"
-msgstr "සංයුක්ත කළ නොහà·à¶šà·’ය"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° කළ නොහà·à¶šà·’ය"
msgid "change"
msgid_plural "changes"
msgstr[0] "වෙනස"
msgstr[1] "වෙනස්කම්"
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47802,7 +48516,7 @@ msgstr[0] "විසඳූ ගà·à¶§à·…ුවක් නිස෠කේත ගà·
msgstr[1] "විසඳූ ගà·à¶§à·…à·” %d ක් නිස෠කේත ගුණත්â€à·€à¶º à·€à·à¶©à·’දියුණු විය"
msgid "ciReport|Code quality scanning detected %{issueCount} changes in merged results"
-msgstr "කේත ගුණත්â€à·€ සුපිරික්සීමට සංයුක්ත ප්â€à¶»à¶­à·’ඵලවල වෙනස්කම් %{issueCount} ක් අනà·à·€à¶»à¶«à¶º විය"
+msgstr "කේත ගුණත්â€à·€ සුපිරික්සීමට ඒකà·à¶¶à¶¯à·Šà¶° ප්â€à¶»à¶­à·’ඵලවල වෙනස්කම් %{issueCount} ක් අනà·à·€à¶»à¶«à¶º විය"
msgid "ciReport|Container Scanning"
msgstr ""
@@ -47882,8 +48596,8 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr "%{issuesWithCount} හමුවිය"
-msgid "ciReport|Full Report"
-msgstr "පූර්ණ à·€à·à¶»à·Šà¶­à·à·€"
+msgid "ciReport|Full report"
+msgstr ""
msgid "ciReport|Generic Report"
msgstr ""
@@ -47954,7 +48668,7 @@ msgid "ciReport|RPS"
msgstr "RPS"
msgid "ciReport|Resolve with merge request"
-msgstr "සංයුක්ත ඉල්ලීම සමඟ විසඳන්න"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම සමඟ විසඳන්න"
msgid "ciReport|SAST"
msgstr "SAST"
@@ -48005,7 +48719,7 @@ msgid "ciReport|There was an error creating the issue. Please try again."
msgstr ""
msgid "ciReport|There was an error creating the merge request. Please try again."
-msgstr "සංයුක්ත ඉල්ලීම සෑදීමේ දà·à·‚යකි. නà·à·€à¶­ උත්සà·à·„ කරන්න."
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම සෑදීමේ දà·à·‚යකි. නà·à·€à¶­ උත්සà·à·„ කරන්න."
msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
msgstr ""
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr "උදà·à·„රණය.ලංකà·"
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr "බයිට %{bytes} ක සීමà·à·€ ඉක්මවයි"
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr "වලංගු නොවන අ.ජà·.කෙ. ලිපින පරà·à·ƒà¶ºà¶šà·’"
@@ -48441,6 +49160,12 @@ msgstr "මෙම ව්â€à¶ºà·à¶´à·˜à¶­à·’ය සඳහ෠ඉඩ නොදà·
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr "ඉඩ නොදේ. ඔබගේ à·ƒà·à¶¸à·à¶±à·Šâ€à¶º වි-තà·à¶´à·‘ල භà·à·€à·’ත෠කරන්න."
@@ -48573,8 +49298,8 @@ msgstr[1] "à·ƒà·à¶¸à·à¶¢à·’කයින්"
msgid "merge request"
msgid_plural "merge requests"
-msgstr[0] "සංයුක්ත ඉල්ලීම"
-msgstr[1] "සංයුක්ත ඉල්ලීම්"
+msgstr[0] "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම"
+msgstr[1] "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම්"
msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
@@ -48604,13 +49329,13 @@ msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
-msgstr "වෙනස්කම් %{targetBranch} වෙත %{mergeCommitSha}%{squashedCommits} සමඟ සංයුක්තයි."
+msgstr "වෙනස්කම් %{targetBranch} වෙත %{mergeCommitSha}%{squashedCommits} සමඟ ඒකà·à¶¶à¶¯à·Šà¶°à¶ºà·’."
msgid "mrWidgetCommitsAdded|The changes were not merged into %{targetBranch}."
-msgstr "වෙනස්කම් %{targetBranch} වෙත සංයුක්ත කර නà·à¶­."
+msgstr "වෙනස්කම් %{targetBranch} වෙත ඒකà·à¶¶à¶¯à·Šà¶° කර නà·à¶­."
msgid "mrWidgetNothingToMerge|This merge request contains no changes."
-msgstr "මෙම සංයුක්ත ඉල්ලීමෙහි වෙනසක් අඩංගු නොවේ."
+msgstr "මෙම ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීමෙහි වෙනසක් අඩංගු නොවේ."
msgid "mrWidgetNothingToMerge|Use merge requests to propose changes to your project and discuss them with your team. To make changes, push a commit or edit this merge request to use a different branch. With %{linkStart}CI/CD%{linkEnd}, automatically test your changes before merging."
msgstr ""
@@ -48688,10 +49413,10 @@ msgid "mrWidget|Assign yourself to this issue"
msgstr ""
msgid "mrWidget|Cancel auto-merge"
-msgstr "ස්වයං සංයුක්තය අවලංගු කරන්න"
+msgstr "ස්වයං ඒකà·à¶¶à¶¯à·Šà¶°à¶º අවලංගු කරන්න"
msgid "mrWidget|Checking if merge request can be merged…"
-msgstr "සංයුක්ත ඉල්ලීම සංයුක්ත කළ à·„à·à¶šà·’දà·à¶ºà·’ පරීක්â€à·‚෠වෙමින්…"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම ඒකà·à¶¶à¶¯à·Šà¶° කිරීමට à·„à·à¶šà·’දà·à¶ºà·’ පරීක්â€à·‚෠වෙමින්…"
msgid "mrWidget|Cherry-pick"
msgstr ""
@@ -48791,7 +49516,7 @@ msgid "mrWidget|Merge blocked: you can only merge after the above items are reso
msgstr ""
msgid "mrWidget|Merge failed."
-msgstr "සංයුක්තය අසමත් විය."
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶°à¶ºà¶§ අසමත් විය."
msgid "mrWidget|Merge unavailable: merge requests are read-only on archived projects."
msgstr ""
@@ -48806,10 +49531,10 @@ msgid "mrWidget|Merging! Changes will land soon…"
msgstr ""
msgid "mrWidget|Merging! Drum roll, please…"
-msgstr "සංයුක්ත වෙමින්! කරුණà·à¶šà¶», බෙර වයන්න…"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° වෙමින්! කරුණà·à¶šà¶», බෙර වයන්න…"
msgid "mrWidget|Merging! Everything's good…"
-msgstr "සංයුක්ත වෙමින්! සියල්ල හොඳයි…"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° වෙමින්! සියල්ල හොඳයි…"
msgid "mrWidget|Merging! Lift-off in 5… 4… 3…"
msgstr ""
@@ -48824,7 +49549,7 @@ msgid "mrWidget|Merging! This is going to be great…"
msgstr ""
msgid "mrWidget|Merging! We're almost there…"
-msgstr "සංයුක්ත වෙමින්…"
+msgstr "ඒකà·à¶¶à¶¯à·Šà¶° වෙමින්…"
msgid "mrWidget|More information"
msgstr "තව තොරතුරු"
@@ -48836,7 +49561,7 @@ msgid "mrWidget|Please restore it or use a different %{type} branch."
msgstr ""
msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
-msgstr "ස්වයංක්â€à¶»à·“යව සංයුක්ත වීමට සූදà·à¶±à¶¸à·Š. ඉල්ලීම සංයුක්ත කිරීමට මෙම කà·à·‚්ඨයට ලිවීමේ ප්â€à¶»à·€à·šà·à¶º ඇති යමෙකුගෙන් අසන්න"
+msgstr "ස්වයංක්â€à¶»à·“යව ඒකà·à¶¶à¶¯à·Šà¶° වීමට සූදà·à¶±à¶¸à·Š. ඉල්ලීම ඒකà·à¶¶à¶¯à·Šà¶° කිරීමට මෙම කà·à·‚්ඨයට ලිවීමේ ප්â€à¶»à·€à·šà·à¶º ඇති යමෙකුගෙන් අසන්න"
msgid "mrWidget|Refresh"
msgstr "නà·à·€à·”ම් කරන්න"
@@ -48884,7 +49609,7 @@ msgid "mrWidget|The source branch is %{link} the target branch"
msgstr ""
msgid "mrWidget|This merge request failed to be merged automatically"
-msgstr "මෙම සංයුක්ත ඉල්ලීම ස්වයංක්â€à¶»à·“යව සංයුක්ත වීමට අසමත් විය"
+msgstr "මෙම ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම ස්වයංක්â€à¶»à·“යව ඒකà·à¶¶à¶¯à·Šà¶° වීමට අසමත් විය"
msgid "mrWidget|To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr "ආරම්භක දිනයට පසු විය යුතුය"
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -48980,7 +49708,7 @@ msgid "never expires"
msgstr "කිසිද෠කල් ඉකුත් නොවේ"
msgid "new merge request"
-msgstr "නව සංයුක්ත ඉල්ලීම"
+msgstr "නව ඒකà·à¶¶à¶¯à·Šà¶° ඉල්ලීම"
msgid "no expiration"
msgstr "කල් ඉකුත්වීමක් නà·à¶­"
@@ -48989,7 +49717,7 @@ msgid "no name set"
msgstr "නමක් සකස෠නà·à¶­"
msgid "no one can merge"
-msgstr "කිසිවෙකුට සංයුක්ත කළ නොහà·à¶šà·’ය"
+msgstr "කිසිවෙකුට ඒකà·à¶¶à¶¯à·Šà¶° කළ නොහà·à¶šà·’ය"
msgid "no scopes selected"
msgstr ""
@@ -49150,6 +49878,9 @@ msgstr "අභියà·à¶œà¶ºà·™à·„à·’ පුද්ගලික යතුර"
msgid "reCAPTCHA site key"
msgstr "අභියà·à¶œà¶ºà·™à·„à·’ අඩවි යතුර"
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr "මෑත ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸"
@@ -49205,6 +49936,11 @@ msgstr "කà·à·‚්ඨය:"
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/sk_SK/gitlab.po b/locale/sk_SK/gitlab.po
index c23c35973cd..c4c2c7d2621 100644
--- a/locale/sk_SK/gitlab.po
+++ b/locale/sk_SK/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: sk\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:22\n"
+"PO-Revision-Date: 2022-12-10 06:41\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -451,6 +451,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -666,6 +673,12 @@ msgstr[3] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -886,13 +899,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -1088,9 +1101,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -1143,7 +1153,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1318,6 +1328,13 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1357,6 +1374,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1478,12 +1498,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2630,6 +2656,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2717,6 +2746,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2912,6 +2944,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -3212,7 +3247,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3275,9 +3310,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4806,6 +4838,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4923,9 +4958,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5480,6 +5512,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5894,6 +5929,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5939,6 +5977,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -6170,9 +6211,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -7200,6 +7238,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7314,9 +7361,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7326,6 +7370,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7347,7 +7394,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7368,9 +7418,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7392,6 +7439,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7425,6 +7475,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7437,6 +7493,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7491,6 +7550,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -8299,6 +8361,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8347,6 +8412,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8472,9 +8540,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8490,7 +8555,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8514,6 +8579,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8757,9 +8825,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8772,6 +8846,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8784,6 +8861,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8793,6 +8873,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8802,6 +8885,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9497,6 +9586,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10320,6 +10412,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10344,6 +10439,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10359,15 +10457,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10533,7 +10640,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10653,6 +10760,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10692,12 +10802,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10968,9 +11072,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -11154,9 +11255,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11469,6 +11567,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11601,9 +11702,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11682,6 +11780,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11718,6 +11819,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11943,6 +12080,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12355,12 +12495,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12373,9 +12519,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12394,6 +12552,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12427,6 +12588,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12487,6 +12651,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12634,6 +12801,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -13007,6 +13177,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -13064,6 +13237,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13868,6 +14047,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13904,6 +14086,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -14010,9 +14195,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -14179,6 +14370,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15500,6 +15694,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15863,9 +16060,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15959,6 +16153,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -16274,6 +16471,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16437,6 +16643,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16467,9 +16676,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16488,9 +16709,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16545,13 +16763,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16871,9 +17092,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -17030,9 +17248,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -17289,6 +17504,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17508,15 +17726,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17553,6 +17762,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17697,10 +17909,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17782,13 +18003,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17831,6 +18045,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18682,6 +18899,13 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18931,6 +19155,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18958,6 +19185,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -19135,6 +19365,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -19228,6 +19461,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19417,7 +19656,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19504,6 +19743,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19609,6 +19851,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19780,7 +20025,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19801,9 +20046,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19933,6 +20175,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19942,9 +20187,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20426,12 +20668,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20465,9 +20713,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20636,6 +20881,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20660,6 +20908,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20699,7 +20950,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20741,6 +20992,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20753,9 +21010,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -21107,6 +21373,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21674,12 +21943,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21998,6 +22273,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -22272,6 +22550,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -22305,6 +22586,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22653,6 +22937,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22701,7 +22988,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -23170,9 +23457,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -23311,9 +23595,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23746,9 +24027,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23899,6 +24177,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23980,6 +24261,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -24049,9 +24333,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -24128,15 +24409,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -24225,6 +24509,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -24330,6 +24617,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -25017,10 +25307,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -25071,6 +25361,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -25176,6 +25469,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25674,6 +25970,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25695,6 +25994,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26830,6 +27135,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26893,9 +27201,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -27107,6 +27412,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -27288,6 +27596,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -27330,6 +27641,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27603,9 +27920,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27705,6 +28019,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -28373,9 +28690,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28758,6 +29090,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28837,9 +29172,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28945,9 +29277,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -29233,6 +29562,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -29324,6 +29656,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -29339,6 +29674,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -29363,6 +29704,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29435,6 +29788,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29450,6 +29806,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29460,6 +29819,9 @@ msgstr[3] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29514,6 +29876,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29562,9 +29927,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29587,6 +29949,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29602,6 +29971,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29671,6 +30043,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29734,6 +30109,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29977,6 +30361,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30892,6 +31300,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -31048,9 +31459,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -31204,6 +31612,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -31288,6 +31699,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -31300,9 +31714,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -31420,16 +31840,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31792,6 +32221,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -32326,12 +32761,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -32413,9 +32854,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32530,6 +32968,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32575,6 +33016,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32704,7 +33148,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32785,6 +33229,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -33307,9 +33754,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33983,6 +34427,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -34182,6 +34629,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34521,6 +34971,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34575,10 +35028,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34929,6 +35379,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -35180,6 +35633,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -35256,7 +35712,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -35353,6 +35809,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -35419,7 +35878,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -35446,6 +35905,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35467,6 +35929,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35552,6 +36017,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35570,6 +36038,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35609,12 +36080,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35709,6 +36183,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35775,6 +36252,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35823,12 +36303,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35944,6 +36418,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35971,13 +36448,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -36019,6 +36499,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -36034,6 +36517,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -36100,6 +36592,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -36127,9 +36625,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -36142,18 +36646,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -36238,6 +36751,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36777,6 +37293,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36834,9 +37353,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36852,6 +37380,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36888,6 +37419,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36960,6 +37494,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36969,12 +37506,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -37044,6 +37590,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -37071,6 +37620,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -37107,6 +37659,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -37116,9 +37671,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -37263,6 +37833,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37488,6 +38061,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37593,6 +38169,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -38083,6 +38662,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -38209,6 +38791,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -38369,9 +38954,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38525,7 +39107,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38540,13 +39122,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38570,13 +39155,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38714,10 +39302,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38759,6 +39347,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38777,6 +39368,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38846,6 +39440,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -39239,6 +39836,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -39344,6 +39944,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -39407,6 +40010,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -40146,6 +40752,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -40236,9 +40845,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40521,6 +41127,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -41111,6 +41723,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -41479,9 +42094,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41539,9 +42151,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41587,6 +42196,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41707,6 +42319,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41746,6 +42361,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41839,9 +42457,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -42031,6 +42646,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -42049,7 +42667,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42523,6 +43141,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42777,6 +43401,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42810,9 +43437,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42927,6 +43551,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -43020,6 +43647,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -43053,6 +43683,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -43068,13 +43701,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -43089,22 +43722,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -43477,6 +44110,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43513,6 +44152,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43690,6 +44332,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43813,6 +44461,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43894,6 +44545,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -44077,6 +44731,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -44095,18 +44752,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -44134,9 +44785,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -44146,9 +44794,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -44275,10 +44920,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -44287,12 +44929,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -44305,9 +44941,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -44341,7 +44974,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -44350,16 +44983,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44533,6 +45166,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44801,9 +45437,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44993,6 +45626,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -45065,6 +45701,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -45083,6 +45722,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -45131,10 +45773,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -45146,7 +45788,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -45203,6 +45845,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -45212,6 +45869,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45878,6 +46547,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45953,9 +46625,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45971,24 +46640,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -46019,6 +46676,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -46085,9 +46754,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -46136,13 +46802,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -46181,9 +46844,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46579,6 +47239,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46588,9 +47251,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46609,6 +47269,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46624,9 +47290,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46649,6 +47312,9 @@ msgstr[3] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46658,6 +47324,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46670,16 +47342,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46688,12 +47363,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46703,12 +47384,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46721,9 +47408,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46787,7 +47483,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46817,6 +47513,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46986,10 +47685,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -47028,13 +47727,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -47101,9 +47793,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -47122,18 +47811,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -47215,6 +47898,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -47269,6 +47955,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -47333,6 +48022,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -47384,9 +48076,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47564,6 +48253,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47582,7 +48274,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47618,6 +48310,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47763,13 +48458,6 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "Your groups"
msgstr ""
@@ -47809,6 +48497,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47969,6 +48666,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -48152,6 +48852,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -48207,6 +48910,20 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -48264,10 +48981,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -48432,7 +49152,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48614,6 +49334,13 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48775,9 +49502,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48968,6 +49692,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -49007,6 +49734,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -49510,6 +50243,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -49519,10 +50255,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49732,6 +50468,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49791,6 +50530,13 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "running"
msgstr ""
diff --git a/locale/sl_SI/gitlab.po b/locale/sl_SI/gitlab.po
index 8193dd0b596..09014c8fec7 100644
--- a/locale/sl_SI/gitlab.po
+++ b/locale/sl_SI/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: sl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:20\n"
+"PO-Revision-Date: 2022-12-10 06:41\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -451,6 +451,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -666,6 +673,12 @@ msgstr[3] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -886,13 +899,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -1088,9 +1101,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -1143,7 +1153,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1318,6 +1328,13 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1357,6 +1374,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1478,12 +1498,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2630,6 +2656,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2717,6 +2746,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2912,6 +2944,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -3212,7 +3247,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3275,9 +3310,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4806,6 +4838,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4923,9 +4958,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5480,6 +5512,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5894,6 +5929,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5939,6 +5977,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -6170,9 +6211,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -7200,6 +7238,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7314,9 +7361,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7326,6 +7370,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7347,7 +7394,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7368,9 +7418,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7392,6 +7439,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7425,6 +7475,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7437,6 +7493,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7491,6 +7550,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -8299,6 +8361,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8347,6 +8412,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8472,9 +8540,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8490,7 +8555,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8514,6 +8579,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8757,9 +8825,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8772,6 +8846,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8784,6 +8861,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8793,6 +8873,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8802,6 +8885,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9497,6 +9586,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10320,6 +10412,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10344,6 +10439,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10359,15 +10457,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10533,7 +10640,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10653,6 +10760,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10692,12 +10802,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10968,9 +11072,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -11154,9 +11255,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11469,6 +11567,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11601,9 +11702,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11682,6 +11780,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11718,6 +11819,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11943,6 +12080,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12355,12 +12495,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12373,9 +12519,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12394,6 +12552,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12427,6 +12588,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12487,6 +12651,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12634,6 +12801,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -13007,6 +13177,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -13064,6 +13237,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13868,6 +14047,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13904,6 +14086,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -14010,9 +14195,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -14179,6 +14370,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15500,6 +15694,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15863,9 +16060,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15959,6 +16153,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -16274,6 +16471,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16437,6 +16643,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16467,9 +16676,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16488,9 +16709,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16545,13 +16763,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16871,9 +17092,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -17030,9 +17248,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -17289,6 +17504,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17508,15 +17726,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17553,6 +17762,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17697,10 +17909,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17782,13 +18003,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17831,6 +18045,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18682,6 +18899,13 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18931,6 +19155,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18958,6 +19185,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -19135,6 +19365,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -19228,6 +19461,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19417,7 +19656,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19504,6 +19743,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19609,6 +19851,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19780,7 +20025,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19801,9 +20046,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19933,6 +20175,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19942,9 +20187,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20426,12 +20668,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20465,9 +20713,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20636,6 +20881,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20660,6 +20908,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20699,7 +20950,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20741,6 +20992,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20753,9 +21010,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -21107,6 +21373,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21674,12 +21943,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21998,6 +22273,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -22272,6 +22550,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -22305,6 +22586,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22653,6 +22937,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22701,7 +22988,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -23170,9 +23457,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -23311,9 +23595,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23746,9 +24027,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23899,6 +24177,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23980,6 +24261,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -24049,9 +24333,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -24128,15 +24409,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -24225,6 +24509,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -24330,6 +24617,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -25017,10 +25307,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -25071,6 +25361,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -25176,6 +25469,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25674,6 +25970,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25695,6 +25994,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26830,6 +27135,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26893,9 +27201,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -27107,6 +27412,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -27288,6 +27596,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -27330,6 +27641,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27603,9 +27920,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27705,6 +28019,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -28373,9 +28690,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28758,6 +29090,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28837,9 +29172,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28945,9 +29277,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -29233,6 +29562,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -29324,6 +29656,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -29339,6 +29674,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -29363,6 +29704,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29435,6 +29788,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29450,6 +29806,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29460,6 +29819,9 @@ msgstr[3] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29514,6 +29876,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29562,9 +29927,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29587,6 +29949,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29602,6 +29971,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29671,6 +30043,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29734,6 +30109,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29977,6 +30361,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30892,6 +31300,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -31048,9 +31459,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -31204,6 +31612,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -31288,6 +31699,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -31300,9 +31714,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -31420,16 +31840,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31792,6 +32221,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -32326,12 +32761,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -32413,9 +32854,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32530,6 +32968,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32575,6 +33016,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32704,7 +33148,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32785,6 +33229,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -33307,9 +33754,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33983,6 +34427,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -34182,6 +34629,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34521,6 +34971,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34575,10 +35028,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34929,6 +35379,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -35180,6 +35633,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -35256,7 +35712,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -35353,6 +35809,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -35419,7 +35878,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -35446,6 +35905,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35467,6 +35929,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35552,6 +36017,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35570,6 +36038,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35609,12 +36080,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35709,6 +36183,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35775,6 +36252,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35823,12 +36303,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35944,6 +36418,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35971,13 +36448,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -36019,6 +36499,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -36034,6 +36517,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -36100,6 +36592,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -36127,9 +36625,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -36142,18 +36646,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -36238,6 +36751,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36777,6 +37293,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36834,9 +37353,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36852,6 +37380,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36888,6 +37419,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36960,6 +37494,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36969,12 +37506,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -37044,6 +37590,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -37071,6 +37620,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -37107,6 +37659,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -37116,9 +37671,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -37263,6 +37833,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37488,6 +38061,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37593,6 +38169,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -38083,6 +38662,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -38209,6 +38791,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -38369,9 +38954,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38525,7 +39107,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38540,13 +39122,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38570,13 +39155,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38714,10 +39302,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38759,6 +39347,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38777,6 +39368,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38846,6 +39440,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -39239,6 +39836,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -39344,6 +39944,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -39407,6 +40010,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -40146,6 +40752,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -40236,9 +40845,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40521,6 +41127,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -41111,6 +41723,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -41479,9 +42094,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41539,9 +42151,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41587,6 +42196,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41707,6 +42319,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41746,6 +42361,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41839,9 +42457,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -42031,6 +42646,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -42049,7 +42667,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42523,6 +43141,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42777,6 +43401,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42810,9 +43437,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42927,6 +43551,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -43020,6 +43647,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -43053,6 +43683,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -43068,13 +43701,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -43089,22 +43722,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -43477,6 +44110,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43513,6 +44152,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43690,6 +44332,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43813,6 +44461,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43894,6 +44545,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -44077,6 +44731,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -44095,18 +44752,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -44134,9 +44785,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -44146,9 +44794,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -44275,10 +44920,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -44287,12 +44929,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -44305,9 +44941,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -44341,7 +44974,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -44350,16 +44983,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44533,6 +45166,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44801,9 +45437,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44993,6 +45626,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -45065,6 +45701,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -45083,6 +45722,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -45131,10 +45773,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -45146,7 +45788,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -45203,6 +45845,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -45212,6 +45869,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45878,6 +46547,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45953,9 +46625,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45971,24 +46640,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -46019,6 +46676,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -46085,9 +46754,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -46136,13 +46802,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -46181,9 +46844,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46579,6 +47239,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46588,9 +47251,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46609,6 +47269,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46624,9 +47290,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46649,6 +47312,9 @@ msgstr[3] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46658,6 +47324,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46670,16 +47342,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46688,12 +47363,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46703,12 +47384,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46721,9 +47408,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46787,7 +47483,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46817,6 +47513,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46986,10 +47685,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -47028,13 +47727,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -47101,9 +47793,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -47122,18 +47811,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -47215,6 +47898,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -47269,6 +47955,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -47333,6 +48022,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -47384,9 +48076,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47564,6 +48253,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47582,7 +48274,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47618,6 +48310,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47763,13 +48458,6 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "Your groups"
msgstr ""
@@ -47809,6 +48497,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47969,6 +48666,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -48152,6 +48852,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -48207,6 +48910,20 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -48264,10 +48981,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -48432,7 +49152,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48614,6 +49334,13 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48775,9 +49502,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48968,6 +49692,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -49007,6 +49734,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -49510,6 +50243,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -49519,10 +50255,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49732,6 +50468,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49791,6 +50530,13 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "running"
msgstr ""
diff --git a/locale/sq_AL/gitlab.po b/locale/sq_AL/gitlab.po
index 2f240a42aa8..9060126622c 100644
--- a/locale/sq_AL/gitlab.po
+++ b/locale/sq_AL/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: sq\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:20\n"
+"PO-Revision-Date: 2022-12-10 06:41\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/sr_CS/gitlab.po b/locale/sr_CS/gitlab.po
index f4e29b1a805..030707a6656 100644
--- a/locale/sr_CS/gitlab.po
+++ b/locale/sr_CS/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: sr-CS\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:24\n"
+"PO-Revision-Date: 2022-12-10 06:44\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -397,6 +397,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -586,6 +592,12 @@ msgstr[2] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -796,13 +808,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -997,9 +1009,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -1051,7 +1060,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1213,6 +1222,12 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1252,6 +1267,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1372,12 +1390,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2497,6 +2521,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2584,6 +2611,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2779,6 +2809,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -3079,7 +3112,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3142,9 +3175,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4672,6 +4702,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4789,9 +4822,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5338,6 +5368,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5749,6 +5782,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5794,6 +5830,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -6025,9 +6064,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -7051,6 +7087,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7165,9 +7210,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7177,6 +7219,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7198,7 +7243,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7219,9 +7267,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7243,6 +7288,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7276,6 +7324,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7288,6 +7342,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7342,6 +7399,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -8149,6 +8209,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8197,6 +8260,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8320,9 +8386,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8338,7 +8401,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8362,6 +8425,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8605,9 +8671,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8620,6 +8692,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8632,6 +8707,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8641,6 +8719,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8650,6 +8731,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9343,6 +9430,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10165,6 +10255,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10189,6 +10282,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10204,15 +10300,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10378,7 +10483,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10498,6 +10603,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10537,12 +10645,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10810,9 +10912,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10996,9 +11095,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11311,6 +11407,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11443,9 +11542,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11524,6 +11620,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11560,6 +11659,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11785,6 +11920,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12196,12 +12334,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12214,9 +12358,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12235,6 +12391,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12268,6 +12427,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12328,6 +12490,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12475,6 +12640,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12847,6 +13015,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12904,6 +13075,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13702,6 +13879,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13738,6 +13918,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13843,9 +14026,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -14011,6 +14200,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15328,6 +15520,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15691,9 +15886,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15787,6 +15979,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -16102,6 +16297,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16264,6 +16468,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16294,9 +16501,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16315,9 +16534,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16372,13 +16588,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16696,9 +16915,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16855,9 +17071,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -17113,6 +17326,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17332,15 +17548,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17377,6 +17584,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17521,10 +17731,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17605,12 +17824,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17653,6 +17866,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18502,6 +18718,12 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18751,6 +18973,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18778,6 +19003,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18955,6 +19183,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -19048,6 +19279,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19237,7 +19474,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19324,6 +19561,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19429,6 +19669,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19600,7 +19843,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19621,9 +19864,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19753,6 +19993,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19762,9 +20005,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20242,12 +20482,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20281,9 +20527,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20452,6 +20695,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20476,6 +20722,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20515,7 +20764,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20557,6 +20806,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20569,9 +20824,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20920,6 +21184,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21487,12 +21754,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21811,6 +22084,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -22084,6 +22360,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -22117,6 +22396,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22465,6 +22747,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22513,7 +22798,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22981,9 +23266,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -23122,9 +23404,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23557,9 +23836,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23710,6 +23986,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23791,6 +24070,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23860,9 +24142,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23938,15 +24217,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -24034,6 +24316,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -24139,6 +24424,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24820,10 +25108,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24874,6 +25162,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24979,6 +25270,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25477,6 +25771,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25498,6 +25795,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26629,6 +26932,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26692,9 +26998,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26905,6 +27208,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -27085,6 +27391,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -27127,6 +27436,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27400,9 +27715,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27502,6 +27814,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -28165,9 +28480,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28549,6 +28879,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28627,9 +28960,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28735,9 +29065,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -29023,6 +29350,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -29113,6 +29443,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -29128,6 +29461,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -29152,6 +29491,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29224,6 +29575,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29239,6 +29593,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29248,6 +29605,9 @@ msgstr[2] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29302,6 +29662,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29350,9 +29713,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29374,6 +29734,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29389,6 +29755,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29458,6 +29827,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29521,6 +29893,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29764,6 +30145,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30679,6 +31084,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30835,9 +31243,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30991,6 +31396,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -31075,6 +31483,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -31087,9 +31498,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -31207,16 +31624,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31579,6 +32005,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -32113,12 +32545,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -32200,9 +32638,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32317,6 +32752,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32362,6 +32800,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32491,7 +32932,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32572,6 +33013,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -33094,9 +33538,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33769,6 +34210,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33967,6 +34411,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34306,6 +34753,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34360,10 +34810,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34708,6 +35155,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34957,6 +35407,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -35032,7 +35485,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -35128,6 +35581,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -35191,7 +35647,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -35218,6 +35674,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35239,6 +35698,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35323,6 +35785,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35341,6 +35806,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35380,12 +35848,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35479,6 +35950,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35545,6 +36019,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35593,12 +36070,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35713,6 +36184,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35740,13 +36214,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35788,6 +36265,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35803,6 +36283,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35869,6 +36358,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35896,9 +36391,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35911,18 +36412,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -36007,6 +36517,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36535,6 +37048,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36592,9 +37108,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36610,6 +37135,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36646,6 +37174,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36718,6 +37249,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36727,12 +37261,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36802,6 +37345,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36829,6 +37375,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36865,6 +37414,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36874,9 +37426,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -37021,6 +37588,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37246,6 +37816,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37351,6 +37924,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37840,6 +38416,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37966,6 +38545,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -38125,9 +38707,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38281,7 +38860,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38296,13 +38875,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38326,13 +38908,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38470,10 +39055,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38515,6 +39100,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38533,6 +39121,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38602,6 +39193,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38995,6 +39589,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -39100,6 +39697,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -39163,6 +39763,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39901,6 +40504,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39991,9 +40597,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40276,6 +40879,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40861,6 +41470,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -41227,9 +41839,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41287,9 +41896,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41335,6 +41941,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41455,6 +42064,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41494,6 +42106,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41587,9 +42202,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41779,6 +42391,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41797,7 +42412,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42271,6 +42886,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42523,6 +43144,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42556,9 +43180,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42673,6 +43294,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42766,6 +43390,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42799,6 +43426,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42814,13 +43444,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42835,22 +43465,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -43222,6 +43852,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43258,6 +43894,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43435,6 +44074,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43558,6 +44203,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43639,6 +44287,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43822,6 +44473,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43840,18 +44494,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43879,9 +44527,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43891,9 +44536,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -44020,10 +44662,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -44032,12 +44671,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -44050,9 +44683,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -44086,7 +44716,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -44095,16 +44725,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44278,6 +44908,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44545,9 +45178,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44737,6 +45367,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44809,6 +45442,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44827,6 +45463,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44875,10 +45514,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44890,7 +45529,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44947,6 +45586,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44956,6 +45610,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45619,6 +46285,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45694,9 +46363,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45712,24 +46378,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45760,6 +46414,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45826,9 +46492,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45877,13 +46540,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45922,9 +46582,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46318,6 +46975,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46327,9 +46987,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46348,6 +47005,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46363,9 +47026,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46387,6 +47047,9 @@ msgstr[2] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46396,6 +47059,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46408,16 +47077,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46426,12 +47098,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46441,12 +47119,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46459,9 +47143,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46525,7 +47218,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46555,6 +47248,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46723,10 +47419,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46765,12 +47461,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46837,9 +47527,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46858,18 +47545,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46951,6 +47632,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -47005,6 +47689,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -47068,6 +47755,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -47119,9 +47809,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47299,6 +47986,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47317,7 +48007,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47353,6 +48043,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47497,12 +48190,6 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "Your groups"
msgstr ""
@@ -47542,6 +48229,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47701,6 +48397,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47881,6 +48580,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47935,6 +48637,18 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47992,10 +48706,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -48157,7 +48874,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48337,6 +49054,12 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48496,9 +49219,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48685,6 +49405,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48724,6 +49447,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -49222,6 +49951,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -49231,10 +49963,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49441,6 +50173,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49498,6 +50233,12 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "running"
msgstr ""
diff --git a/locale/sr_SP/gitlab.po b/locale/sr_SP/gitlab.po
index 8ede4d795dd..80caf05c125 100644
--- a/locale/sr_SP/gitlab.po
+++ b/locale/sr_SP/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: sr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:20\n"
+"PO-Revision-Date: 2022-12-10 06:41\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -397,6 +397,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -586,6 +592,12 @@ msgstr[2] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -796,13 +808,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -997,9 +1009,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -1051,7 +1060,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1213,6 +1222,12 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1252,6 +1267,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1372,12 +1390,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2497,6 +2521,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2584,6 +2611,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2779,6 +2809,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -3079,7 +3112,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3142,9 +3175,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4672,6 +4702,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4789,9 +4822,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5338,6 +5368,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5749,6 +5782,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5794,6 +5830,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -6025,9 +6064,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -7051,6 +7087,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7165,9 +7210,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7177,6 +7219,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7198,7 +7243,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7219,9 +7267,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7243,6 +7288,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7276,6 +7324,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7288,6 +7342,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7342,6 +7399,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -8149,6 +8209,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8197,6 +8260,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8320,9 +8386,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8338,7 +8401,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8362,6 +8425,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8605,9 +8671,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8620,6 +8692,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8632,6 +8707,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8641,6 +8719,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8650,6 +8731,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9343,6 +9430,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10165,6 +10255,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10189,6 +10282,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10204,15 +10300,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10378,7 +10483,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10498,6 +10603,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10537,12 +10645,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10810,9 +10912,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10996,9 +11095,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11311,6 +11407,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11443,9 +11542,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11524,6 +11620,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11560,6 +11659,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11785,6 +11920,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12196,12 +12334,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12214,9 +12358,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12235,6 +12391,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12268,6 +12427,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12328,6 +12490,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12475,6 +12640,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12847,6 +13015,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12904,6 +13075,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13702,6 +13879,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13738,6 +13918,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13843,9 +14026,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -14011,6 +14200,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15328,6 +15520,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15691,9 +15886,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15787,6 +15979,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -16102,6 +16297,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16264,6 +16468,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16294,9 +16501,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16315,9 +16534,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16372,13 +16588,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16696,9 +16915,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16855,9 +17071,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -17113,6 +17326,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17332,15 +17548,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17377,6 +17584,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17521,10 +17731,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17605,12 +17824,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17653,6 +17866,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18502,6 +18718,12 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18751,6 +18973,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18778,6 +19003,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18955,6 +19183,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -19048,6 +19279,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19237,7 +19474,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19324,6 +19561,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19429,6 +19669,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19600,7 +19843,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19621,9 +19864,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19753,6 +19993,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19762,9 +20005,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20242,12 +20482,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20281,9 +20527,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20452,6 +20695,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20476,6 +20722,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20515,7 +20764,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20557,6 +20806,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20569,9 +20824,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20920,6 +21184,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21487,12 +21754,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21811,6 +22084,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -22084,6 +22360,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -22117,6 +22396,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22465,6 +22747,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22513,7 +22798,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22981,9 +23266,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -23122,9 +23404,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23557,9 +23836,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23710,6 +23986,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23791,6 +24070,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23860,9 +24142,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23938,15 +24217,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -24034,6 +24316,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -24139,6 +24424,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24820,10 +25108,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24874,6 +25162,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24979,6 +25270,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25477,6 +25771,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25498,6 +25795,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26629,6 +26932,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26692,9 +26998,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26905,6 +27208,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -27085,6 +27391,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -27127,6 +27436,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27400,9 +27715,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27502,6 +27814,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -28165,9 +28480,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28549,6 +28879,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28627,9 +28960,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28735,9 +29065,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -29023,6 +29350,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -29113,6 +29443,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -29128,6 +29461,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -29152,6 +29491,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29224,6 +29575,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29239,6 +29593,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29248,6 +29605,9 @@ msgstr[2] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29302,6 +29662,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29350,9 +29713,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29374,6 +29734,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29389,6 +29755,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29458,6 +29827,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29521,6 +29893,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29764,6 +30145,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30679,6 +31084,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30835,9 +31243,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30991,6 +31396,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -31075,6 +31483,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -31087,9 +31498,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -31207,16 +31624,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31579,6 +32005,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -32113,12 +32545,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -32200,9 +32638,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32317,6 +32752,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32362,6 +32800,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32491,7 +32932,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32572,6 +33013,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -33094,9 +33538,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33769,6 +34210,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33967,6 +34411,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34306,6 +34753,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34360,10 +34810,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34708,6 +35155,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34957,6 +35407,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -35032,7 +35485,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -35128,6 +35581,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -35191,7 +35647,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -35218,6 +35674,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35239,6 +35698,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35323,6 +35785,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35341,6 +35806,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35380,12 +35848,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35479,6 +35950,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35545,6 +36019,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35593,12 +36070,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35713,6 +36184,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35740,13 +36214,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35788,6 +36265,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35803,6 +36283,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35869,6 +36358,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35896,9 +36391,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35911,18 +36412,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -36007,6 +36517,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36535,6 +37048,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36592,9 +37108,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36610,6 +37135,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36646,6 +37174,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36718,6 +37249,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36727,12 +37261,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36802,6 +37345,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36829,6 +37375,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36865,6 +37414,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36874,9 +37426,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -37021,6 +37588,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37246,6 +37816,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37351,6 +37924,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37840,6 +38416,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37966,6 +38545,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -38125,9 +38707,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38281,7 +38860,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38296,13 +38875,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38326,13 +38908,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38470,10 +39055,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38515,6 +39100,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38533,6 +39121,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38602,6 +39193,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38995,6 +39589,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -39100,6 +39697,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -39163,6 +39763,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39901,6 +40504,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39991,9 +40597,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40276,6 +40879,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40861,6 +41470,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -41227,9 +41839,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41287,9 +41896,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41335,6 +41941,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41455,6 +42064,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41494,6 +42106,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41587,9 +42202,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41779,6 +42391,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41797,7 +42412,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42271,6 +42886,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42523,6 +43144,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42556,9 +43180,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42673,6 +43294,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42766,6 +43390,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42799,6 +43426,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42814,13 +43444,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42835,22 +43465,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -43222,6 +43852,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43258,6 +43894,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43435,6 +44074,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43558,6 +44203,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43639,6 +44287,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43822,6 +44473,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43840,18 +44494,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43879,9 +44527,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43891,9 +44536,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -44020,10 +44662,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -44032,12 +44671,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -44050,9 +44683,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -44086,7 +44716,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -44095,16 +44725,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44278,6 +44908,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44545,9 +45178,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44737,6 +45367,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44809,6 +45442,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44827,6 +45463,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44875,10 +45514,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44890,7 +45529,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44947,6 +45586,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44956,6 +45610,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45619,6 +46285,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45694,9 +46363,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45712,24 +46378,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45760,6 +46414,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45826,9 +46492,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45877,13 +46540,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45922,9 +46582,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46318,6 +46975,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46327,9 +46987,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46348,6 +47005,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46363,9 +47026,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46387,6 +47047,9 @@ msgstr[2] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46396,6 +47059,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46408,16 +47077,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46426,12 +47098,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46441,12 +47119,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46459,9 +47143,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46525,7 +47218,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46555,6 +47248,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46723,10 +47419,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46765,12 +47461,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46837,9 +47527,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46858,18 +47545,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46951,6 +47632,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -47005,6 +47689,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -47068,6 +47755,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -47119,9 +47809,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47299,6 +47986,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47317,7 +48007,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47353,6 +48043,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47497,12 +48190,6 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "Your groups"
msgstr ""
@@ -47542,6 +48229,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47701,6 +48397,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47881,6 +48580,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47935,6 +48637,18 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47992,10 +48706,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -48157,7 +48874,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48337,6 +49054,12 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48496,9 +49219,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48685,6 +49405,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48724,6 +49447,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -49222,6 +49951,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -49231,10 +49963,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49441,6 +50173,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49498,6 +50233,12 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "running"
msgstr ""
diff --git a/locale/sv_SE/gitlab.po b/locale/sv_SE/gitlab.po
index 076a7df4700..332f4585170 100644
--- a/locale/sv_SE/gitlab.po
+++ b/locale/sv_SE/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: sv-SE\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:20\n"
+"PO-Revision-Date: 2022-12-10 06:41\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] "%d kommentar till"
msgstr[1] "%d kommentarer till"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%d väntande kommentar"
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type} raderades"
+msgid "%{host} sign-in from new location"
+msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/sw_KE/gitlab.po b/locale/sw_KE/gitlab.po
index 3e905fac9a6..2460bdb94a1 100644
--- a/locale/sw_KE/gitlab.po
+++ b/locale/sw_KE/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: sw\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:24\n"
+"PO-Revision-Date: 2022-12-10 06:43\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/ta_IN/gitlab.po b/locale/ta_IN/gitlab.po
index b59208a7626..e7f084b442a 100644
--- a/locale/ta_IN/gitlab.po
+++ b/locale/ta_IN/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: ta\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:21\n"
+"PO-Revision-Date: 2022-12-10 06:42\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/th_TH/gitlab.po b/locale/th_TH/gitlab.po
index 971845cdf03..5f61f80b932 100644
--- a/locale/th_TH/gitlab.po
+++ b/locale/th_TH/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: th\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:21\n"
+"PO-Revision-Date: 2022-12-10 06:42\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -289,6 +289,10 @@ msgid "%d more comment"
msgid_plural "%d more comments"
msgstr[0] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -426,6 +430,12 @@ msgstr[0] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -616,13 +626,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -815,9 +825,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -867,7 +874,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1003,6 +1010,10 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1042,6 +1053,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1160,12 +1174,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2231,6 +2251,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2318,6 +2341,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2513,6 +2539,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2813,7 +2842,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -2876,9 +2905,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4404,6 +4430,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4521,9 +4550,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5054,6 +5080,9 @@ msgid "Are you sure you want to import %d repository?"
msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5459,6 +5488,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5504,6 +5536,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5735,9 +5770,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6753,6 +6785,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -6867,9 +6908,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -6879,6 +6917,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -6900,7 +6941,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -6921,9 +6965,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -6945,6 +6986,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -6978,6 +7022,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -6990,6 +7040,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7044,6 +7097,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7849,6 +7905,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -7897,6 +7956,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8016,9 +8078,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8034,7 +8093,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8058,6 +8117,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8301,9 +8363,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8316,6 +8384,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8328,6 +8399,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8337,6 +8411,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8346,6 +8423,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9035,6 +9118,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -9855,6 +9941,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -9879,6 +9968,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -9894,15 +9986,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10068,7 +10169,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10188,6 +10289,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10227,12 +10331,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10494,9 +10592,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10680,9 +10775,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -10995,6 +11087,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11127,9 +11222,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11208,6 +11300,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11244,6 +11339,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11469,6 +11600,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -11878,12 +12012,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -11896,9 +12036,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -11917,6 +12069,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -11950,6 +12105,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12010,6 +12168,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12157,6 +12318,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12527,6 +12691,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12584,6 +12751,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13370,6 +13543,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13406,6 +13582,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13509,9 +13688,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13675,6 +13860,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -14984,6 +15172,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15347,9 +15538,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15443,6 +15631,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15758,6 +15949,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -15918,6 +16118,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -15948,9 +16151,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -15969,9 +16184,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16026,13 +16238,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16346,9 +16561,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16505,9 +16717,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16761,6 +16970,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -16980,15 +17192,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17025,6 +17228,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17169,10 +17375,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17251,10 +17466,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17297,6 +17508,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18142,6 +18356,10 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18391,6 +18609,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18418,6 +18639,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18595,6 +18819,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18688,6 +18915,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -18877,7 +19110,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -18964,6 +19197,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19069,6 +19305,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19240,7 +19479,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19261,9 +19500,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19393,6 +19629,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19402,9 +19641,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -19874,12 +20110,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -19913,9 +20155,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20084,6 +20323,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20108,6 +20350,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20147,7 +20392,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20189,6 +20434,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20201,9 +20452,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20546,6 +20806,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21113,12 +21376,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21437,6 +21706,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21708,6 +21980,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21741,6 +22016,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22089,6 +22367,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22137,7 +22418,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22603,9 +22884,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22744,9 +23022,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23179,9 +23454,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23332,6 +23604,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23413,6 +23688,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23482,9 +23760,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23558,15 +23833,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23652,6 +23930,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23757,6 +24038,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24426,10 +24710,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24480,6 +24764,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24585,6 +24872,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25083,6 +25373,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25104,6 +25397,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26227,6 +26526,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26290,9 +26592,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26501,6 +26800,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26679,6 +26981,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26721,6 +27026,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -26994,9 +27305,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27096,6 +27404,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27749,9 +28060,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28131,6 +28457,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28207,9 +28536,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28315,9 +28641,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28603,6 +28926,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28691,6 +29017,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28706,6 +29035,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28730,6 +29065,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -28802,6 +29149,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -28817,6 +29167,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28824,6 +29177,9 @@ msgstr[0] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -28878,6 +29234,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -28926,9 +29285,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -28948,6 +29304,10 @@ msgid "PackageRegistry|You are about to delete 1 asset. This operation is irreve
msgid_plural "PackageRegistry|You are about to delete %d assets. This operation is irreversible."
msgstr[0] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -28963,6 +29323,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29032,6 +29395,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29095,6 +29461,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29338,6 +29713,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30253,6 +30652,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30409,9 +30811,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30565,6 +30964,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30649,6 +31051,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30661,9 +31066,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30781,16 +31192,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31153,6 +31573,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31687,12 +32113,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31774,9 +32206,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -31891,6 +32320,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -31936,6 +32368,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32065,7 +32500,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32146,6 +32581,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32668,9 +33106,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33341,6 +33776,9 @@ msgid "Refreshing in a second to show the updated status..."
msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33537,6 +33975,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -33876,6 +34317,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -33930,10 +34374,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34266,6 +34707,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34511,6 +34955,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34584,7 +35031,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34678,6 +35125,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34735,7 +35185,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34762,6 +35212,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -34783,6 +35236,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -34865,6 +35321,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -34883,6 +35342,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -34922,12 +35384,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35019,6 +35484,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35085,6 +35553,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35133,12 +35604,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35251,6 +35716,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35278,13 +35746,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35326,6 +35797,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35341,6 +35815,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35407,6 +35890,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35434,9 +35923,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35449,18 +35944,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35545,6 +36049,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36051,6 +36558,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36108,9 +36618,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36126,6 +36645,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36162,6 +36684,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36234,6 +36759,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36243,12 +36771,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36318,6 +36855,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36345,6 +36885,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36381,6 +36924,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36390,9 +36936,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36537,6 +37098,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -36762,6 +37326,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -36867,6 +37434,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37354,6 +37924,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37480,6 +38053,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37637,9 +38213,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -37793,7 +38366,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -37808,13 +38381,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -37838,13 +38414,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -37982,10 +38561,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38027,6 +38606,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38045,6 +38627,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38114,6 +38699,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38507,6 +39095,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38612,6 +39203,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38675,6 +39269,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39411,6 +40008,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39501,9 +40101,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -39786,6 +40383,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40361,6 +40964,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40723,9 +41329,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -40783,9 +41386,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -40831,6 +41431,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -40951,6 +41554,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -40990,6 +41596,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41083,9 +41692,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41275,6 +41881,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41293,7 +41902,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -41767,6 +42376,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42015,6 +42630,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42048,9 +42666,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42165,6 +42780,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42258,6 +42876,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42291,6 +42912,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42306,13 +42930,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42327,22 +42951,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42712,6 +43336,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -42748,6 +43378,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -42925,6 +43558,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43048,6 +43687,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43129,6 +43771,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43312,6 +43957,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43330,18 +43978,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43369,9 +44011,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43381,9 +44020,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43510,10 +44146,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43522,12 +44155,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43540,9 +44167,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43576,7 +44200,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43585,16 +44209,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -43768,6 +44392,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44033,9 +44660,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44225,6 +44849,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44297,6 +44924,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44315,6 +44945,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44363,10 +44996,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44378,7 +45011,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44435,6 +45068,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44444,6 +45092,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45101,6 +45761,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45176,9 +45839,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45194,24 +45854,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45242,6 +45890,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45308,9 +45968,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45359,13 +46016,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push events"
-msgstr ""
-
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45404,9 +46058,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -45796,6 +46447,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -45805,9 +46459,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -45826,6 +46477,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -45841,9 +46498,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -45863,6 +46517,9 @@ msgstr[0] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -45872,6 +46529,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -45884,16 +46547,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -45902,12 +46568,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -45917,12 +46589,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -45935,9 +46613,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46001,7 +46688,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46031,6 +46718,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46197,10 +46887,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46239,10 +46929,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46309,9 +46995,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46330,18 +47013,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46423,6 +47100,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46477,6 +47157,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46538,6 +47221,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46589,9 +47275,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -46769,6 +47452,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -46787,7 +47473,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -46823,6 +47509,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -46965,10 +47654,6 @@ msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-
msgid "Your groups"
msgstr ""
@@ -47008,6 +47693,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47165,6 +47859,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47339,6 +48036,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47391,6 +48091,14 @@ msgid "change"
msgid_plural "changes"
msgstr[0] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47448,10 +48156,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47607,7 +48318,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -47783,6 +48494,10 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -47938,9 +48653,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48119,6 +48831,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48158,6 +48873,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48646,6 +49367,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48655,10 +49379,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -48859,6 +49583,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -48912,6 +49639,10 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+
msgid "running"
msgstr ""
diff --git a/locale/tr_TR/gitlab.po b/locale/tr_TR/gitlab.po
index cf8c0d4e562..1f02a24921e 100644
--- a/locale/tr_TR/gitlab.po
+++ b/locale/tr_TR/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: tr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:20\n"
+"PO-Revision-Date: 2022-12-10 06:41\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] "%d yorum daha"
msgstr[1] "%d yorum daha"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%d yorum bekliyor"
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,15 +717,15 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type} silindi"
-
-msgid "%{hook_type} was scheduled for deletion"
-msgstr "%{hook_type} silme işlemi için zamanlandı"
-
msgid "%{host} sign-in from new location"
msgstr "%{host} yeni konumdan oturum açıldı"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
+msgstr ""
+
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr ""
+
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
@@ -906,9 +917,6 @@ msgstr "%{openedEpics} açık, %{closedEpics} kapalı"
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr "%{openedIssues} açık, %{closedIssues} kapalı"
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr "%{reportType} %{status}"
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr "%{template_project_id} bilinmiyor ya da geçersiz"
msgid "%{text} is available"
msgstr "%{text} kullanılabilir"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr "%{userName} (birleÅŸtiremez)"
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr "(DeÄŸiÅŸiklik yok)"
msgid "(UTC %{offset}) %{timezone}"
msgstr "(UTC %{offset}) %{timezone}"
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr "(ilerlemeyi kontrol et)"
@@ -2364,6 +2386,9 @@ msgstr "Sistem kancası ekle"
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr "Panoya ekle"
@@ -2451,6 +2476,9 @@ msgstr "Bu sürümde eklendi"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "GitLab örneğinizde yeni uygulamalar eklemek devre dışı bırakıldı. İzin almak için lütfen GitLab yöneticinize başvurun"
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "Ek dakika"
@@ -2646,6 +2674,9 @@ msgstr "İşleri durdurma başarısız oldu"
msgid "AdminArea|Total users"
msgstr "Toplam kullanıcı"
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr "Bir hata oluştu. Lütfen tekrar deneyin."
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr "Herhangi bir dönüm noktası"
msgid "Any namespace"
msgstr "Herhangi isim alanı"
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr "Uygulama KimliÄŸi"
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "AÄŸustos"
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "Dallar"
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr "Etkin"
@@ -7049,8 +7092,11 @@ msgstr ""
msgid "Branches|Compare"
msgstr "Karşılaştır"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "'%{default_branch}' ile birleştirilmiş tüm dalları sil"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
+msgstr ""
msgid "Branches|Delete branch"
msgstr "Dalı sil"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "Birleştirilmiş dalı siliyorsunuz ve bu geri alınamaz. Emin misiniz?"
-
msgid "Branches|Filter by branch name"
msgstr "Dal adına göre filtrele"
@@ -7094,6 +7137,9 @@ msgstr "Genel Bakış"
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr "Etkin dalları göster"
@@ -7127,6 +7173,12 @@ msgstr "Öntanımlı dal silinemez"
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr "Yerel değişikliklerden vazgeçip akış sürümüyle dalın üzerine yazmak için, buradan silin ve yukarıdan 'Şimdi Güncelle'yi seçin."
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr "Dahili"
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr "Etiket"
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "%{text} varlığı kontrol ediliyor…"
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr "Düzenle"
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr "Son Erme %{expirationMonth}/%{expirationYear}"
@@ -8186,7 +8247,7 @@ msgstr "Ülkeler yüklenemedi. Lütfen tekrar deneyin."
msgid "Checkout|Failed to load states. Please try again."
msgstr "İller yüklenemedi. Lütfen tekrar deneyin."
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr "GitLab kullanan şirketin veya kuruluşun adı"
@@ -8453,9 +8517,15 @@ msgstr "çalışıyor"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr "Ortamlar"
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr "Değişken anahtarını girin"
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr "Değişken satırı kaldırın"
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr "Tür"
@@ -8498,6 +8577,12 @@ msgstr "DeÄŸer"
msgid "CiVariables|Variables"
msgstr "DeÄŸiÅŸkenler"
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr "* (Tüm ortamlar)"
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr "Bağlanılıyor"
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr "Bağlanıyor…"
@@ -10382,12 +10488,6 @@ msgstr "Kapsayıcı kaydı resimleri"
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr "Hesabınıza bağlı e-postaları kontrol edin"
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr "Sorun kaydı oluştur"
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr "Etiket"
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr "Haftanın varsayılan ilk günü"
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr "Varsayılan proje limiti"
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr "Açıklama:"
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr "Tasarım Yönetimi dosya ve verileri"
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr "Aramak için en az üç karakter girin"
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr "EpiÄŸi sil"
msgid "Epics|Remove issue"
msgstr "Sorunu sil"
-msgid "Epics|Show more"
-msgstr "Daha fazlasını göster"
-
msgid "Epics|Something went wrong while creating child epics."
msgstr "Alt epikler oluÅŸturulurken bir ÅŸeyler ters gitti."
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr "Tahmini"
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr "Yapılacaklar listenizdeki her şey bitti olarak işaretlendi."
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr "Birleştirme işlemleri hariç tutuluyor. 6,000 işlem ile sınırlı."
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr "Mevcut dal adı, etiketi veya işlem SHA"
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr "Kenar çubuğunu genişlet"
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr "Åžub"
msgid "February"
msgstr "Åžubat"
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr "Yazı Tipi Rengi"
@@ -17345,10 +17553,19 @@ msgstr "Çatallama devam ediyor"
msgid "Forks"
msgstr "Çatallar"
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr "Genel iş hatları"
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr "Varsayılan etiket kümesi oluştur"
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr "BirleÅŸtirme isteklerinize gidin"
msgid "Go to your projects"
msgstr "Projelerinize gidin"
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr "Parçacıklarınıza gidin"
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr "Çizelge"
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr "Grup oluÅŸtur"
@@ -19582,9 +19823,6 @@ msgstr "Yeni grup oluÅŸtur"
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr "En yüksek rol:"
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr "Kanca başarıyla güncellendi."
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr "%{hours} saat, %{minutes} dakika kaldı"
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr "Yorumları etkinleştir"
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr "Başlık"
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr "Kubernetes dağıtımı bulunamadı"
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr "Etiketler"
msgid "Labels"
msgstr "Etiketler"
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr ""
-
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "Etiketler sorunları ve birleştirme taleplerini kategorize etmek için kullanılabilir."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr "Daha fazlasını öğrenin."
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,12 +24909,12 @@ msgstr "Kilitli Dosyalar"
msgid "Locked by %{fileLockUserName}"
msgstr "%{fileLockUserName} tarafından kilitlendi"
+msgid "Locked files"
+msgstr ""
+
msgid "Locked the discussion."
msgstr "Tartışma kilitlendi."
-msgid "Locks give the ability to lock specific file or folder."
-msgstr "Kilitler, belirli bir dosyayı veya klasörü kilitleme yeteneği sağlar."
-
msgid "Locks the discussion."
msgstr "Tartışmayı kilitler."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr "Ãœyeler"
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr "Kapat"
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr "Daha fazla bilgi"
msgid "More information"
msgstr "Daha fazla bilgi"
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr "Yeni dal"
msgid "New branch unavailable"
msgstr "Yeni dal kullanılamaz"
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr "Yeni kimlik"
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "Yeni sorun"
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr "Gösterilecek yineleme yok"
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr "Yeni bir pencerede açılır"
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr "Şifreler benzersiz olmalı ve başka hiçbir site veya hizmet için kullanılmamalıdır."
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr "Lütfen devam etmek için %{phrase_code} yazın ya da iptal etmek için bu pencereyi kapatın."
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "Bir projenin genel bakış sayfasında hangi içeriği görmek istediğinizi seçin."
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr "Sözdizimi vurgu teması"
msgid "Preferences|Tab width"
msgstr "Sekme geniÅŸliÄŸi"
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr "Bu ayar, sistem düzeni ve varsayılan görünümlerin davranışını Ã
msgid "Preferences|Time preferences"
msgstr "Zaman tercihleri"
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr "Göreceli zamanları kullan"
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr "Kullanıcı adını güncelle"
msgid "Profiles|Upload new avatar"
msgstr "Yeni profil resmi yükle"
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr "Özel bir e-posta kullan - %{email}"
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr "Proje görünürlüğü"
msgid "ProjectSettings|Public"
msgstr "Herkese açık"
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] "Güncellenmiş durumu göstermek için bir saniye içinde yenilenecek..."
msgstr[1] "Güncellenmiş durumu göstermek için %d saniye içinde yenilenecek..."
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr "Bu %{quick_action_target} yeniden açıldı."
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,12 +34592,9 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
+msgid "Report abuse to administrator"
msgstr ""
-msgid "Report abuse to admin"
-msgstr "Kötüye kullanımı yöneticiye bildir"
-
msgid "Report couldn't be prepared."
msgstr ""
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr "EriÅŸim Talebi"
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,8 +35258,8 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
-msgstr "İnceleme süresi, ilk yorumdan birleştirilene kadar geçen süre olarak tanımlanır."
+msgid "Review time is the amount of time since the first comment in a merge request."
+msgstr ""
msgid "ReviewApp|Enable Review App"
msgstr ""
@@ -34903,6 +35353,9 @@ msgstr "Temizlik hizmetini çalıştır"
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr "Çalışıyor"
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr "Ara"
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr "Paylaşılan projeler"
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr "Aramayı başlat"
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr "Durum"
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr "Dal/etiketi deÄŸiÅŸtir"
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr "GitLab Next'e geçin"
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr "Ekip"
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr "Henüz arşivlenmiş proje yok"
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr "Henüz bu grupla paylaşılan proje yok."
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr "Kullanıcı iş hattı dakikaları sıfırlanırken bir hata oluştu."
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr "Bu grup"
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr "Kalan süre"
msgid "Time spent"
msgstr "Harcanan zaman"
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr "Tetikleyici başarıyla güncellendi."
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr "Yıldızı kaldır"
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr "Mevcut dönem kullanımı"
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr "Bu isim alanının paylaşılan çalıştırıcıları kullanan projeleri yok"
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr "Sınırsız"
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr "Viki"
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr "Kullandığınız: %{usage} %{limit}"
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr "KiÅŸisel projeler"
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr "Kötüye kullanımı bildir"
-
msgid "UserProfile|Retry"
msgstr "Yeniden dene"
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr "Bu aşamayı göstermek için yeterli veriye sahip değiliz."
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr "Erişim İsteğini Geri Çek"
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr "Gitlab grubunuz"
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr "Gruplarınız"
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr "Gruplarınız"
@@ -47275,6 +47961,15 @@ msgstr "Adınız"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr "Düzeltildi:"
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr "Yorumladı: %{link_to_project}"
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/uk/gitlab.po b/locale/uk/gitlab.po
index ba4a6611f00..a08df80d036 100644
--- a/locale/uk/gitlab.po
+++ b/locale/uk/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: uk\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:20\n"
+"PO-Revision-Date: 2022-12-10 06:41\n"
msgid " %{start} to %{end}"
msgstr " %{start} до %{end}"
@@ -451,6 +451,13 @@ msgstr[1] "ще %d коментарі"
msgstr[2] "ще %d коментарів"
msgstr[3] "ще %d коментарів"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] "%d пакет"
+msgstr[1] "%d пакета"
+msgstr[2] "%d пакетів"
+msgstr[3] "%d пакетів"
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%d коментар в очікуванні"
@@ -666,6 +673,12 @@ msgstr[3] "%{bold_start}%{count}%{bold_end} відкритих запитів н
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr "%{code_open}ЗамаÑковано:%{code_close} Сховано в журналах завдань. Повинні відповідати вимогам до маÑкуваннÑ."
@@ -886,15 +899,15 @@ msgstr "%{group_name} викориÑтовує облікові запиÑи кÐ
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr "%{group_name}Ñ–%{epic_iid} &middot; Ñтворили %{epic_created} від %{author}"
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type} було видалено"
-
-msgid "%{hook_type} was scheduled for deletion"
-msgstr "%{hook_type} було призначено Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ"
-
msgid "%{host} sign-in from new location"
msgstr "вхід на %{host} з нового розташуваннÑ"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
+msgstr ""
+
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr ""
+
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr "%{integrations_link_start}Інтеграції%{link_end} дозволÑÑŽÑ‚ÑŒ зробити Ñторонні програми чаÑтиною вашого робочого процеÑу GitLab. Якщо наÑвні інтеграції не відповідають Вашим потребам, розглÑньте можливіÑÑ‚ÑŒ викориÑÑ‚Ð°Ð½Ð½Ñ %{webhooks_link_start}вебхуків (webhooks)%{link_end}."
@@ -1088,9 +1101,6 @@ msgstr "%{openedEpics} відкрито, %{closedEpics} закрито"
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr "%{openedIssues} відкрито, %{closedIssues} закрито"
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr "%{over_limit_message} Щоб отримати більше учаÑників, влаÑник групи може почати пробну верÑÑ–ÑŽ або перейти на платний рівень."
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr "%{over_limit_message}, Щоб отримати більше міÑць, %{link_start}перейдіть на платний рівень%{link_end}."
@@ -1143,8 +1153,8 @@ msgstr "%{reportType} %{status}"
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr "%{reportType} виÑвив %{totalStart}%{total}%{totalEnd} потенціал %{vulnMessage}"
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
-msgstr "%{reportType} не виÑвив %{totalStart}нових%{totalEnd} вразливоÑтей."
+msgid "%{reportType} detected no new vulnerabilities."
+msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
msgstr "%{retryButtonStart}Спробуйте знову%{retryButtonEnd} або %{newFileButtonStart}прикріпити новий файл%{newFileButtonEnd}."
@@ -1318,6 +1328,13 @@ msgstr "%{template_project_id} невідомий або неправильниÐ
msgid "%{text} is available"
msgstr "%{text} доÑтупний"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr "%{timebox_type} не підтримує графік згорÑннÑ"
@@ -1357,6 +1374,9 @@ msgstr "%{type} має бути %{help_link}"
msgid "%{type} only supports %{name} name"
msgstr "%{type} підтримує лише %{name} ім'Ñ"
+msgid "%{url} (optional)"
+msgstr "%{url} (необов'Ñзково)"
+
msgid "%{userName} (cannot merge)"
msgstr "%{userName} (не може виконувати злиттÑ)"
@@ -1478,12 +1498,18 @@ msgstr "(+%{count}&nbsp;правил)"
msgid "(Group Managed Account)"
msgstr "(Обліковий Ð·Ð°Ð¿Ð¸Ñ ÐºÐµÑ€Ð¾Ð²Ð°Ð½Ð¾Ñ— групи)"
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr "(Ðемає змін)"
msgid "(UTC %{offset}) %{timezone}"
msgstr "(UTC %{offset}) %{timezone}"
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr "(перевірити прогреÑ)"
@@ -2442,7 +2468,7 @@ msgid "Add a bullet list"
msgstr "Додати ненумерований ÑпиÑок"
msgid "Add a checklist"
-msgstr ""
+msgstr "Додати ÑпиÑок"
msgid "Add a collapsible section"
msgstr "Додати згорнуту Ñекцію"
@@ -2630,6 +2656,9 @@ msgstr "Додати ÑиÑтемний хук"
msgid "Add text to the sign-in page. Markdown enabled."
msgstr "Додати текÑÑ‚ до Ñторінки входу. Markdown увімкнено."
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr "Додати на дошку"
@@ -2717,6 +2746,9 @@ msgstr "Додано в цій верÑÑ–Ñ—"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð½Ð¾Ð²Ð¸Ñ… проєктів Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ інÑтанÑу GitLab заборонено. ЗвернітьÑÑ Ð´Ð¾ Ñвого адмініÑтратора GitLab, щоб отримати дозвіл"
+msgid "Additional diagram formats"
+msgstr "Додаткові формати діаграм"
+
msgid "Additional minutes"
msgstr "Додаткові хвилини"
@@ -2912,6 +2944,9 @@ msgstr "Зупинка завдань пройшла невдало"
msgid "AdminArea|Total users"
msgstr "Загальна кількіÑÑ‚ÑŒ кориÑтувачів"
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr "КориÑтувачі"
@@ -2940,7 +2975,7 @@ msgid "AdminDashboard|Error loading the statistics. Please try again"
msgstr "Помилка при завантаженні ÑтатиÑтики. Будь лаÑка, Ñпробуйте знову"
msgid "AdminEmail|Body"
-msgstr ""
+msgstr "Тіло"
msgid "AdminEmail|Body is required."
msgstr ""
@@ -2955,7 +2990,7 @@ msgid "AdminEmail|Subject"
msgstr "Тема"
msgid "AdminEmail|Subject is required."
-msgstr ""
+msgstr "Тема є необхідною."
msgid "AdminLabels|Define your default set of project labels"
msgstr "Визначте ваш набір проєктних міток за замовчуваннÑм"
@@ -3003,7 +3038,7 @@ msgid "AdminSettings|Configure limits on the number of repositories users can do
msgstr ""
msgid "AdminSettings|Configure product analytics to track events within your project applications."
-msgstr ""
+msgstr "Ðалаштувати аналітику продукту Ð´Ð»Ñ Ð²Ñ–Ð´ÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿Ð¾Ð´Ñ–Ð¹ у ваших проєктних заÑтоÑунках."
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -3021,7 +3056,7 @@ msgid "AdminSettings|Delete inactive projects that exceed"
msgstr "Видалити неактивні проєкти, що перевищують"
msgid "AdminSettings|Delete project after"
-msgstr ""
+msgstr "Видалити проєкт через"
msgid "AdminSettings|Disable Elasticsearch until indexing completes."
msgstr "Вимкніть Elasticsearch до Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ñ–Ð½Ð´ÐµÐºÑуваннÑ."
@@ -3120,7 +3155,7 @@ msgid "AdminSettings|Jitsu host"
msgstr "ХоÑÑ‚ Jitsu"
msgid "AdminSettings|Jitsu project ID"
-msgstr ""
+msgstr "ID проєкту в Jitsu"
msgid "AdminSettings|Keep the latest artifacts for all jobs in the latest successful pipelines"
msgstr "Зберегти оÑтанні артефакти Ð´Ð»Ñ Ð²ÑÑ–Ñ… завдань в оÑтанніх уÑпішних конвеєрах"
@@ -3212,8 +3247,8 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr "Виберіть шаблон CI/CD"
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
-msgstr "Виберіть групу Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð°Ð½Ð½Ñ Ñк джерело Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ð½Ð¸Ñ… шаблонів на рівні інÑтанÑа."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
+msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
msgstr ""
@@ -3275,9 +3310,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr "ОÑтанні артефакти Ð´Ð»Ñ Ð²ÑÑ–Ñ… завдань у оÑтанніх уÑпішних конвеєрах у кожному проєкті зберігаютьÑÑ Ñ‚Ð° не мають терміну дії."
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr "Проєкти цієї групи можна вибрати Ñк шаблони Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ… проєктів, Ñтворених в інÑтанÑÑ–. %{link_start}ДізнайтеÑÑŒ більше.%{link_end} "
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr "Шаблон Ð´Ð»Ñ Ð½ÐµÐ¾Ð±Ñ…Ñ–Ð´Ð½Ð¾Ñ— конфігурації конвеєра може бути одним із шаблонів, наданих GitLab, або інший шаблон доданий до репозиторію шаблонів інÑтанÑу. %{link_start}Як Ñтворити шаблон інÑтанÑу?%{link_end}"
@@ -3603,7 +3635,7 @@ msgid "AdminUsers|Restore user access to the account, including web, Git and API
msgstr "Відновити доÑтуп кориÑтувача до облікового запиÑу, включаючи веб, Git та API."
msgid "AdminUsers|Search by name, email, or username"
-msgstr ""
+msgstr "Пошук за іменем, електронною поштою або іменем кориÑтувача"
msgid "AdminUsers|Search users"
msgstr "Пошук кориÑтувачів"
@@ -4806,6 +4838,9 @@ msgstr "Виникла помилка. Будь лаÑка, увійдіть щÐ
msgid "An error occurred. Please try again."
msgstr "СталаÑÑŒ помилка. Спробуйте ще раз."
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr "Приклад проєкту Ð´Ð»Ñ ÑƒÐ¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ ÐºÐ»Ð°Ñтерами Kubernetes, інтегрованими з GitLab"
@@ -4923,9 +4958,6 @@ msgstr "Будь-Ñкий етап"
msgid "Any namespace"
msgstr "Будь-Ñкий проÑÑ‚Ñ–Ñ€ імен"
-msgid "Anyone can register for an account."
-msgstr "Будь-хто може зареєÑтрувати обліковий запиÑ."
-
msgid "App ID"
msgstr "Ідентифікатор заÑтоÑунку"
@@ -5100,7 +5132,7 @@ msgid "ApplicationSettings|Text shown after a user signs up. Markdown enabled."
msgstr "ТекÑÑ‚, Ñкий відображаєтьÑÑ Ð¿Ñ–ÑÐ»Ñ Ñ€ÐµÑ”Ñтрації кориÑтувача. Markdown увімкнено."
msgid "ApplicationSettings|This feature is only available on GitLab.com"
-msgstr ""
+msgstr "Ð¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð´Ð¾Ñтупна лише на GitLab.com"
msgid "ApplicationSettings|This option is only available on GitLab.com"
msgstr "Цей параметр доÑтупний лише на GitLab.com"
@@ -5405,7 +5437,7 @@ msgid "Archived projects"
msgstr "Заархівовані проєкти"
msgid "Archiving the project makes it entirely read-only. It is hidden from the dashboard and doesn't display in searches. %{strong_start}The repository cannot be committed to, and no issues, comments, or other entities can be created.%{strong_end} %{link_start}Learn more.%{link_end}"
-msgstr ""
+msgstr "ÐÑ€Ñ…Ñ–Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ñƒ зробить його доÑтупним лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ. Він буде прихований з панелі ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñ– не відображатиметьÑÑ Ð² пошуках. %{strong_start}Ð’ репозиторій не можна буде комітити, Ñ– не можливо буде Ñтворити жодних задач, коментарів чи інших об'єктів.%{strong_end} %{link_start}Докладніше.%{link_end}"
msgid "Are you ABSOLUTELY SURE you wish to remove this group?"
msgstr "Ви ÐБСОЛЮТÐО ВПЕВÐЕÐІ що бажаєте видалити цю групу?"
@@ -5480,6 +5512,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr "Ви впевнені, що хочете заблокувати %{path}?"
@@ -5595,28 +5630,28 @@ msgid "Artifacts"
msgstr "Ðртефакти"
msgid "Artifacts|An error occurred while deleting the artifact"
-msgstr ""
+msgstr "Помилка при видаленні артефакту"
msgid "Artifacts|An error occurred while retrieving job artifacts"
msgstr ""
msgid "Artifacts|Artifacts"
-msgstr ""
+msgstr "Ðртефакти"
msgid "Artifacts|Browse"
-msgstr ""
+msgstr "ПереглÑнути"
msgid "Artifacts|Delete %{name}?"
-msgstr ""
+msgstr "Видалити %{name}?"
msgid "Artifacts|Delete artifact"
-msgstr ""
+msgstr "Видалити артефакт"
msgid "Artifacts|This artifact will be permanently deleted. Any reports generated from this artifact will be empty."
msgstr ""
msgid "Artifacts|Total artifacts size"
-msgstr ""
+msgstr "Загальний розмір артефактів"
msgid "As we continue to build more features for SAST, we'd love your feedback on the SAST configuration feature in %{linkStart}this issue%{linkEnd}."
msgstr "ОÑкільки ми продовжуємо Ñтворювати більше функцій Ð´Ð»Ñ SAST, ми хотіли б отримати ваші відгуки про функцію конфігурації SAST у %{linkStart}цій задачі%{linkEnd}."
@@ -5859,7 +5894,7 @@ msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party sys
msgstr ""
msgid "AuditStreams|Add another custom header"
-msgstr ""
+msgstr "Додайте ще один Ñпеціальний заголовок"
msgid "AuditStreams|Add external stream destination"
msgstr ""
@@ -5886,7 +5921,7 @@ msgid "AuditStreams|Cancel editing"
msgstr "СкаÑувати редагуваннÑ"
msgid "AuditStreams|Custom HTTP headers (optional)"
-msgstr ""
+msgstr "КориÑтувацькі HTTP-заголовки (опціонально)"
msgid "AuditStreams|Delete %{link}"
msgstr "Видалити %{link}"
@@ -5894,6 +5929,9 @@ msgstr "Видалити %{link}"
msgid "AuditStreams|Destination URL"
msgstr "URL-адреÑа призначеннÑ"
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5939,6 +5977,9 @@ msgstr "ЗначеннÑ"
msgid "AuditStreams|Verification token"
msgstr "Токен перевірки"
+msgid "AuditStreams|filtered"
+msgstr "відфільтровано"
+
msgid "Aug"
msgstr "Ñерп."
@@ -6170,9 +6211,6 @@ msgstr "ДоÑтупна група раннерів: %{runners}"
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr "ДоÑтупні Ñпільні раннери:"
-
msgid "Available specific runners"
msgstr "ДоÑтупні Ñпеціальні раннери"
@@ -6297,7 +6335,7 @@ msgid "Badges|Saving the badge failed, please check the entered URLs and try aga
msgstr "Ð—Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐºÐ° не вдалоÑÑ, будь лаÑка перевірте введений URL Ñ– Ñпробуйте знову."
msgid "Badges|Supported %{docsLinkStart}variables%{docsLinkEnd}: %{placeholders}"
-msgstr "ПідтримуєтьÑÑ %{docsLinkStart}змінних%{docsLinkEnd}: %{placeholders}"
+msgstr "ПідтримуєтьÑÑ %{docsLinkStart}змінні%{docsLinkEnd}: %{placeholders}"
msgid "Badges|The badge was deleted."
msgstr "Значок був видалений."
@@ -6477,7 +6515,7 @@ msgid "BillingPlans|10,000 CI/CD minutes per month"
msgstr "10,000 хвилин CI/CD в міÑÑць"
msgid "BillingPlans|10GB transfer per month"
-msgstr ""
+msgstr "10Гб передачі на міÑÑць"
msgid "BillingPlans|400 CI/CD minutes per month"
msgstr "400 хвилин CI/CD в міÑÑць"
@@ -6783,7 +6821,7 @@ msgid "Billing|Enter at least three characters to search."
msgstr "Введіть щонайменше три Ñимволи Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ."
msgid "Billing|Explore paid plans"
-msgstr ""
+msgstr "ОзнайомтеÑÑ Ð· платними планами"
msgid "Billing|Export list"
msgstr "ЕкÑпортувати ÑпиÑок"
@@ -7200,6 +7238,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr "ПідтримуютьÑÑ%{linkStart}шаблони%{linkEnd}, такі Ñк *-stable або production/*."
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr "Ð’ÑÑ– гілки"
@@ -7314,9 +7361,6 @@ msgstr "КориÑтувачі"
msgid "BranchRules|default"
msgstr "за умовчаннÑм"
-msgid "BranchRules|protected"
-msgstr "захищена"
-
msgid "Branches"
msgstr "Гілки"
@@ -7326,6 +7370,9 @@ msgstr "Гілки: %{source_branch} до %{target_branch}"
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr "Гілки: %{source_branch} → %{target_branch}"
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr "Ðктивні"
@@ -7347,8 +7394,11 @@ msgstr "Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ HEAD-коміт Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— гілÐ
msgid "Branches|Compare"
msgstr "ПорівнÑти"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "Видалити вÑÑ– гілки Ñкі злиті в '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
+msgstr ""
msgid "Branches|Delete branch"
msgstr "Видалити гілку"
@@ -7368,9 +7418,6 @@ msgstr "Видалити захищену гілку. Ви ÐБСОЛЮТÐО Ð’
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr "Ðе можна ÑкаÑувати Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð³Ñ–Ð»ÐºÐ¸ %{strongStart}%{branchName}%{strongEnd}. Ви впевнені?"
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð·Ð»Ð¸Ñ‚Ð¸Ñ… гілок неможливо буде ÑкаÑувати. Ви впевнені?"
-
msgid "Branches|Filter by branch name"
msgstr "Фільтрувати за назвою гілки"
@@ -7392,6 +7439,9 @@ msgstr "ОглÑд"
msgid "Branches|Please type the following to confirm:"
msgstr "Будь лаÑка, введіть наÑтупні дії Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ:"
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr "Показувати активні гілки"
@@ -7425,6 +7475,12 @@ msgstr "Гілка \"за замовчуваннÑм\" не може бути в
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr "Щоб відхилити локальні зміни Ñ– перезапиÑати гілку верÑією з upstream, видаліть Ñ—Ñ— тут Ñ– виберіть \"Оновити зараз\" вище."
@@ -7437,6 +7493,9 @@ msgstr "Так, видалити гілку"
msgid "Branches|Yes, delete protected branch"
msgstr "Так, видалити захищену гілку"
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr "Ви збираєтеÑÑŒ назавжди видалити гілку %{branchName}."
@@ -7491,6 +7550,9 @@ msgstr "Помилка при отриманні артефактів"
msgid "BuildArtifacts|Loading artifacts"
msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð°Ñ€Ñ‚ÐµÑ„Ð°ÐºÑ‚Ñ–Ð²"
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr "Вбудований"
@@ -8299,6 +8361,9 @@ msgstr "Конвеєр #%{pipeline_id} %{humanized_status} в %{duration}"
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr "Конвеєр %{pipeline_link} з %{ref_type} %{ref_link} запущений %{user_combined_name} %{humanized_status}"
+msgid "ChatMessage|Pipeline name"
+msgstr "Ðазва конвеєра"
+
msgid "ChatMessage|Tag"
msgstr "Тег"
@@ -8347,6 +8412,9 @@ msgstr "Перевірте Ñвої образи Docker на наÑвніÑÑ‚ÑŒ
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr "Перевірте образи клаÑтера Kubernetes на наÑвніÑÑ‚ÑŒ відомих вразливоÑтей."
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "Перевірка доÑтупноÑÑ‚Ñ– %{text}…"
@@ -8472,9 +8540,6 @@ msgstr "Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ форму кредитної
msgid "Checkout|Edit"
msgstr "Редагувати"
-msgid "Checkout|Enter a number greater than 0"
-msgstr "Введіть чиÑло, що більше 0"
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8490,8 +8555,8 @@ msgstr "Помилка при завантаженні країн. Будь ла
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
-msgstr "Помилка при завантаженні форми оплати. Будь лаÑка, Ñпробуйте знову."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
+msgstr "Помилка при завантаженні платіжної форми. Оновіть Ñторінку Ñ– Ñпробуйте знову."
msgid "Checkout|Failed to register credit card. Please try again."
msgstr "Ðе вдалоÑÑ Ð·Ð°Ñ€ÐµÑ”Ñтрувати кредитну картку. Будь лаÑка, Ñпробуйте ще раз."
@@ -8514,6 +8579,9 @@ msgstr "Має бути %{minimumNumberOfUsers} (ваші вільні міÑцÑ
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr "Ðазва компанії або організації, що викориÑтовує GitLab"
@@ -8757,9 +8825,15 @@ msgstr "виконуєтьÑÑ"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr "Ðеможливо викориÑтовувати приховану змінну із поточним значеннÑм"
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr "Середовища"
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr "Ключ вхідної змінної"
@@ -8772,6 +8846,9 @@ msgstr "Ключ"
msgid "CiVariables|Masked"
msgstr "Приховано"
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8784,6 +8861,9 @@ msgstr "Видалити змінну"
msgid "CiVariables|Remove variable row"
msgstr "Видалити Ñ€Ñдок змінних"
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr "ОблаÑÑ‚ÑŒ видимоÑÑ‚Ñ–"
@@ -8793,6 +8873,9 @@ msgstr "Вкажіть Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð¼Ñ–Ð½Ð½Ð¸Ñ…, Ñкі будуть ви
msgid "CiVariables|State"
msgstr "Стан"
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr "Тип"
@@ -8802,6 +8885,12 @@ msgstr "ЗначеннÑ"
msgid "CiVariables|Variables"
msgstr "Змінні"
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr "* (Ð’ÑÑ– Ñередовища)"
@@ -9497,9 +9586,12 @@ msgstr "Токен відкликано %{userName}"
msgid "ClusterAgents|Unknown user"
msgstr "Ðевідомий кориÑтувач"
-msgid "ClusterAgents|Valid access token"
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
msgstr ""
+msgid "ClusterAgents|Valid access token"
+msgstr "Коректний токен доÑтупу"
+
msgid "ClusterAgents|View all %{number} agents"
msgstr "ПереглÑнути вÑÑ– %{number} агентів"
@@ -10008,7 +10100,7 @@ msgid "Collapse issues"
msgstr "Згорнути задачі"
msgid "Collapse jobs"
-msgstr ""
+msgstr "Згорнути завданнÑ"
msgid "Collapse merge details"
msgstr "Приховати деталі злиттÑ"
@@ -10320,6 +10412,9 @@ msgstr "ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ð²Ñ–Ð´Ð¿Ð¾Ð²Ñ–Ð´Ð½Ð¾ÑÑ‚Ñ– конвеєру (н
msgid "ComplianceFrameworks|Configuration not found"
msgstr "ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ð½Ðµ знайдена"
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr "Видалити фреймворк відповідноÑÑ‚Ñ– %{framework}"
@@ -10344,6 +10439,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr "Додані фреймворки будуть з'ÑвлÑтиÑÑŒ тут."
@@ -10359,15 +10457,24 @@ msgstr "Потрібна назва"
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr "Ð’Ñтановити за замовчуваннÑм"
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr "Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ цей фреймворк відповідноÑÑ‚Ñ–. Будь лаÑка, Ñпробуйте ще раз"
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr "за умовчаннÑм"
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10533,8 +10640,8 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ— з %{link}."
-msgid "Configure the default first day of the week and time tracking units."
-msgstr "Ðалаштуйте перший день Ñ‚Ð¸Ð¶Ð½Ñ Ð·Ð° замовчуваннÑм Ñ– одиниці відÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ñ‡Ð°Ñу."
+msgid "Configure the default first day of the week, time tracking units, and default language."
+msgstr ""
msgid "Configure the way a user creates a new account."
msgstr "Ðалаштувати ÑпоÑіб ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувачем нового облікового запиÑу."
@@ -10653,6 +10760,9 @@ msgstr "З'єднаннÑ"
msgid "Connecting to terminal sync service"
msgstr "Ð—â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ñ–Ð· ÑервіÑом Ñинхронізації терміналу"
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr "З'єднаннÑ..."
@@ -10692,12 +10802,6 @@ msgstr "Образи в реєÑтрі контейнерів"
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr "РеєÑÑ‚Ñ€ контейнерів не включений у даний інÑÑ‚Ð°Ð½Ñ GitLab. ПопроÑÑ–Ñ‚ÑŒ адмініÑтратора включити його Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб Auto DevOps працював."
-msgid "Container repositories"
-msgstr "Репозиторії контейнерів"
-
-msgid "Container repository"
-msgstr "Репозиторій контейнера"
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr "Будь лаÑка, перейдіть до %{linkStart}налаштувань адмініÑтрації%{linkEnd} , щоб увімкнути цю функцію."
@@ -10968,9 +11072,6 @@ msgstr "Тег уÑпішно позначений Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ."
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr "Теги уÑпішно позначені Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ."
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr "Теги, Ñкі відповідають цим правилам, %{strongStart}зберігаютьÑÑ%{strongEnd}, навіть Ñкщо вони відповідають правилу Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð½Ð¸Ð¶Ñ‡Ðµ. %{secondStrongStart}оÑтанній%{secondStrongEnd} тег завжди зберігаєтьÑÑ."
@@ -11154,9 +11255,6 @@ msgstr "Керувати адреÑами електронної пошти, пÐ
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr "Контролює, чи показувати контент Ð¿Ð¾ÐºÑ€Ð°Ñ‰ÐµÐ½Ð½Ñ ÐºÐ»Ñ–Ñ”Ð½Ñ‚Ñького доÑвіду та Ñторонні пропозиції в GitLab."
@@ -11469,6 +11567,9 @@ msgstr "Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ ваші дизайни, оÑ
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr "Країна"
@@ -11601,9 +11702,6 @@ msgstr "Створити задачу"
msgid "Create issue to resolve all threads"
msgstr "Створити задачу Ð´Ð»Ñ Ð²Ð¸Ñ€Ñ–ÑˆÐµÐ½Ð½Ñ Ð²ÑÑ–Ñ… обговорень"
-msgid "Create iteration"
-msgstr "Створити ітерацію"
-
msgid "Create label"
msgstr "Створити мітку"
@@ -11682,6 +11780,9 @@ msgstr "Створити Ñніпет"
msgid "Create tag %{tagName}"
msgstr "Створити тег %{tagName}"
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr "Створити тему"
@@ -11701,13 +11802,13 @@ msgid "Create your group"
msgstr "Створити групи"
msgid "Create, update, or delete a merge request."
-msgstr ""
+msgstr "СтвореннÑ, Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð°Ð±Ð¾ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на злиттÑ."
msgid "CreateGitTag|Add a message to the tag. Leaving this blank creates a %{linkStart}lightweight tag%{linkEnd}."
msgstr ""
msgid "CreateGitTag|Set tag message"
-msgstr ""
+msgstr "УÑтановити Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñ‚ÐµÐ³Ñƒ"
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” дозволу Ñтворювати підгрупу в цій групі."
@@ -11718,6 +11819,42 @@ msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” дозволу Ñтворювати групи."
msgid "CreateTag|Tag"
msgstr "Тег"
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr "задача"
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr "%{name} (за замовчуваннÑм)"
@@ -11943,6 +12080,9 @@ msgstr ""
msgid "Credit card:"
msgstr "Кредитна картка:"
+msgid "Critical - S1"
+msgstr "Критичний - S1"
+
msgid "Critical vulnerabilities present"
msgstr "ПриÑутні критичні вразливоÑÑ‚Ñ–"
@@ -12355,12 +12495,18 @@ msgstr "%{startDate} - %{endDate}"
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr "Ð’ Ñередньому (оÑтанні %{days}d)"
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr "Змінити відÑоток невдач (відÑотків)"
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12373,9 +12519,21 @@ msgstr "Дні Ð´Ð»Ñ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¾Ð³Ð¾ інциденту"
msgid "DORA4Metrics|Days from merge to deploy"
msgstr "Днів від Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð´Ð¾ розгортаннÑ"
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr "ЧаÑтота розгортаннÑ"
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr "Ð§Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ"
+
msgid "DORA4Metrics|Lead time for changes"
msgstr "Ð§Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð¼Ñ–Ð½"
@@ -12394,6 +12552,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr "Ðові задачі"
+
msgid "DORA4Metrics|No incidents during this period"
msgstr "Жодних інцидентів за цей період"
@@ -12427,6 +12588,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 "Графік відображає Ñередній Ñ‡Ð°Ñ Ð¼Ñ–Ð¶ об’єднаннÑм запиту на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð° його розгортаннÑм у production Ñередовищі, що базуютьÑÑ Ð½Ð° значенні %{linkStart}deployment_tier%{linkEnd}."
+msgid "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr "Ð§Ð°Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÑервіÑу"
@@ -12487,6 +12651,9 @@ msgstr "Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проєкту не знайдено попереднÑ
msgid "DastConfig|Not enabled"
msgstr "Ðе увімкнено"
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr "ПаÑивне ÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ñ–Ð´Ñтежує вÑÑ– HTTP-Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ (запити та відповіді), надіÑлані цільовому об’єкту. Ðктивне ÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ñ‚Ð°ÐºÑƒÑ” цільовий об’єкт, щоб знайти потенційні вразливоÑÑ‚Ñ–."
@@ -12634,6 +12801,9 @@ msgstr "Мінімум = 1 Ñекунда, МакÑимум = 3600 Ñекунд"
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr "Ðовий профіль Ñканера"
@@ -13007,6 +13177,9 @@ msgstr "Днів бездіÑльноÑÑ‚Ñ– перед деактивацією"
msgid "Days to merge"
msgstr "Днів до злиттÑ"
+msgid "Deactivate"
+msgstr "Деактивувати"
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -13038,7 +13211,7 @@ msgid "Decrease"
msgstr ""
msgid "Default - Never run"
-msgstr ""
+msgstr "За замовчуваннÑм - ніколи не запуÑкати"
msgid "Default CI/CD configuration file"
msgstr "Типовий файл конфігурації CI/CD"
@@ -13064,6 +13237,12 @@ msgstr "Перший день Ñ‚Ð¸Ð¶Ð½Ñ Ð·Ð° замовчуваннÑм"
msgid "Default first day of the week in calendars and date pickers."
msgstr "Перший день Ñ‚Ð¸Ð¶Ð½Ñ Ð·Ð° замовчуваннÑм в календарÑÑ… та при виборі дати."
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr "Ліміт проєктів за замовчуваннÑм"
@@ -13868,6 +14047,9 @@ msgstr "Відхилено %{time}"
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr "Відхилено вами %{time}"
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr "GitLab Pages"
@@ -13904,6 +14086,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr "РозгортаннÑ"
@@ -14010,9 +14195,15 @@ msgstr ""
msgid "Description:"
msgstr "ОпиÑ:"
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr "Мітка Ð´Ð»Ñ Ð¾Ð¿Ð¸Ñу"
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr "Файли та дані ÑƒÐ¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ Ð´Ð¸Ð·Ð°Ð¹Ð½Ð¾Ð¼"
@@ -14063,7 +14254,7 @@ msgid "DesignManagement|Comment"
msgstr "Коментар"
msgid "DesignManagement|Continue creating"
-msgstr ""
+msgstr "Продовжити ÑтвореннÑ"
msgid "DesignManagement|Continue editing"
msgstr ""
@@ -14179,6 +14370,9 @@ msgstr "Звіти DevOps"
msgid "DevOps adoption"
msgstr "ÐŸÑ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ DevOps"
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr "Розробник"
@@ -15500,6 +15694,9 @@ msgstr "Введіть будь-Ñкий колір."
msgid "Enter at least three characters to search"
msgstr "Введіть щонайменше 3 Ñимволи Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ"
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr "Введіть URL-адреÑу вашого Bitbucket Server Ñ– ключ доÑтупу"
@@ -15863,9 +16060,6 @@ msgstr "Видалити епік"
msgid "Epics|Remove issue"
msgstr "Видалити задачу"
-msgid "Epics|Show more"
-msgstr "Показати більше"
-
msgid "Epics|Something went wrong while creating child epics."
msgstr "ЩоÑÑŒ пішло не так при Ñтворенні дочірніх епіків."
@@ -15959,6 +16153,9 @@ msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при отриманні даних."
msgid "Error fetching refs"
msgstr "Помилка Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ refs"
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr "Помилка при отриманні ÑпиÑку залежноÑтей. Будь лаÑка, перевірте ваше Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· мережею."
@@ -16274,6 +16471,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr "Хвилини"
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr "Оцінити"
@@ -16437,6 +16643,9 @@ msgstr "Кожен може зробити Ñвій внеÑок"
msgid "Everything on your to-do list is marked as done."
msgstr "Ð’Ñе у вашому ÑпиÑку нагадувань відмічено виконаним."
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16467,9 +16676,21 @@ msgstr "Збір даних"
msgid "Exactly one of %{attributes} is required"
msgstr "Потрібен лише один з %{attributes}"
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr "Приклад: @sub\\.company\\.com$"
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr "Приклад: ssh\\:\\/\\/"
+
msgid "Examples"
msgstr "Приклади"
@@ -16488,9 +16709,6 @@ msgstr "Без комітів злиттÑ. Обмежено 6000 комітів
msgid "Execution time"
msgstr "Ð§Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ"
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr "Ім'Ñ Ñ–Ñнуючої гілки, тега або SHA коміта"
@@ -16545,17 +16763,20 @@ msgstr "Розгорнути розділ налаштувань"
msgid "Expand sidebar"
msgstr "Розгорніть бічну панель"
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
-msgstr "Повинен мати точно один із таких параметрів: КориÑтувач, проÑÑ‚Ñ–Ñ€ імен або проєкт."
+msgid "Experiment candidates"
+msgstr ""
msgid "Experiments"
-msgstr ""
+msgstr "ЕкÑперименти"
msgid "Expiration"
msgstr "Термін дії"
@@ -16871,9 +17092,6 @@ msgstr "Ðе вдалоÑÑ Ð·Ð³ÐµÐ½ÐµÑ€ÑƒÐ²Ð°Ñ‚Ð¸ звіт, будь лаÑка
msgid "Failed to get ref."
msgstr "Ðе вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ ref."
-msgid "Failed to install."
-msgstr "Ðе вдалоÑÑ Ð²Ñтановити."
-
msgid "Failed to load"
msgstr "Ðеможливо завантажити"
@@ -17030,9 +17248,6 @@ msgstr "Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ ÑÑ‚Ð°Ñ‚ÑƒÑ Ð·Ð°Ð´Ð°Ñ‡Ñ–"
msgid "Failed to update the Canary Ingress."
msgstr "Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ Canary Ingress."
-msgid "Failed to upgrade."
-msgstr "Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸."
-
msgid "Failed to upload object map file"
msgstr "Ðе вдалоÑÑ Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ файл відповідноÑÑ‚Ñ– об’єктів"
@@ -17289,9 +17504,12 @@ msgstr "лют."
msgid "February"
msgstr "лютий"
-msgid "Feedback and Updates"
+msgid "Feedback"
msgstr ""
+msgid "Feedback and Updates"
+msgstr "Зворотний зв'Ñзок та оновленнÑ"
+
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -17508,15 +17726,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17553,6 +17762,9 @@ msgstr "ÐктивніÑÑ‚ÑŒ підпиÑаних кориÑтувачів"
msgid "Followed users"
msgstr "ПідпиÑки"
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr "Колір шрифту"
@@ -17697,12 +17909,21 @@ msgstr "ВідбуваєтьÑÑ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ„Ð¾Ñ€ÐºÑƒ"
msgid "Forks"
msgstr "Форки"
-msgid "Format: %{dateFormat}"
-msgstr "Формат: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|Up to date with upstream repository"
msgstr ""
+msgid "Format: %{dateFormat}"
+msgstr "Формат: %{dateFormat}"
+
msgid "Found errors in your %{gitlab_ci_yml}:"
msgstr "Знайдено помилки у вашому %{gitlab_ci_yml}:"
@@ -17782,13 +18003,6 @@ msgstr "З %{code_open}%{source_title}%{code_close} в"
msgid "From %{providerTitle}"
msgstr "З %{providerTitle}"
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] "З 19 Ð¶Ð¾Ð²Ñ‚Ð½Ñ 2022 Ñ€. безкоштовні приватні групи будуть обмежені %d учаÑником"
-msgstr[1] "З 19 Ð¶Ð¾Ð²Ñ‚Ð½Ñ 2022 Ñ€. безкоштовні приватні групи будуть обмежені %d учаÑниками"
-msgstr[2] "З 19 Ð¶Ð¾Ð²Ñ‚Ð½Ñ 2022 Ñ€. безкоштовні приватні групи будуть обмежені %d учаÑниками"
-msgstr[3] "З 19 Ð¶Ð¾Ð²Ñ‚Ð½Ñ 2022 Ñ€. безкоштовні приватні групи будуть обмежені %d учаÑниками"
-
msgid "From issue creation until deploy to production"
msgstr "З моменту ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡Ñ– до Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° production"
@@ -17831,6 +18045,9 @@ msgstr "Загальні конвеєри"
msgid "General settings"
msgstr "Загальні налаштуваннÑ"
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr "Створити Ñтандартний набір міток"
@@ -18682,6 +18899,13 @@ msgstr "ВерÑÑ–Ñ GitLab"
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr "GitLab Ñтворить гілку у вашому форку та розпочне запит на злиттÑ."
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18884,7 +19108,7 @@ msgid "Global Shortcuts"
msgstr "Глобальні комбінації клавіш"
msgid "Global notification level"
-msgstr ""
+msgstr "Глобальний рівень ÑповіщеннÑ"
msgid "Global notification settings"
msgstr "Загальні Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñповіщень"
@@ -18931,6 +19155,9 @@ msgstr "Свіжі задачі"
msgid "GlobalSearch|Recent merge requests"
msgstr "ОÑтанні запити на злиттÑ"
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18958,6 +19185,9 @@ msgstr "Введіть, щоб нижче з'ÑвилиÑÑ Ð½Ð¾Ð²Ñ– пропоÐ
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr "ВикориÑтовуйте клавішу швидкого доÑтупу %{kbdOpen}/%{kbdClose}, щоб почати пошук"
+msgid "GlobalSearch|Users"
+msgstr "КориÑтувачі"
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -19135,6 +19365,9 @@ msgstr "Перейти до ваших запитів на злиттÑ"
msgid "Go to your projects"
msgstr "Перейти до ваших проєктів"
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr "Перейти до ваших Ñніпетів"
@@ -19228,6 +19461,12 @@ msgstr "Ðадайте дозволи на Ð·Ð°Ð¿Ð¸Ñ Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ ключ
msgid "Graph"
msgstr "Граф"
+msgid "GraphQL"
+msgstr "GraphQL"
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr "ЗалежноÑÑ‚Ñ– завдань"
@@ -19417,7 +19656,7 @@ msgstr "За оÑтанні 30 днів"
msgid "GroupActivityMetrics|Members added"
msgstr "УчаÑники додані"
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19504,6 +19743,9 @@ msgstr "ПротÑгом 3-Ñ… років"
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19609,6 +19851,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19780,8 +20025,8 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
-msgstr "Виберіть підгрупу Ñк джерело Ð´Ð»Ñ Ð²Ð»Ð°Ñних шаблонів проєктів Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— групи."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
+msgstr ""
msgid "GroupSettings|Select parent group"
msgstr "Вибери батьківÑьку групу"
@@ -19801,9 +20046,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr "Конвеєр Auto DevOps запуÑкаєтьÑÑ, Ñкщо не знайдено жодного альтернативного файлу конфігурації CI."
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr "Проєкти у цій підгрупі можна вибрати Ñк шаблони Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ… проєктів, Ñтворених у цій групі. %{link_start}ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ.%{link_end}"
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr "Проблема при оновленні конвеєра Auto DevOps: %{error_messages}."
@@ -19933,6 +20175,9 @@ msgstr "Підключити інÑтанÑ"
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr "ЗвернітьÑÑ Ð´Ð¾ адмініÑтратора, щоб увімкнути параметри Ð´Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ вашої групи."
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr "Створити групу"
@@ -19942,9 +20187,6 @@ msgstr "Створити нову групу"
msgid "GroupsNew|Create subgroup"
msgstr "Створити підгрупу"
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr "Вихідна URL-адреÑа GitLab"
@@ -20426,12 +20668,18 @@ msgstr "ÐедоÑтупна Ñтруктура"
msgid "Hierarchy|You can start using these items now."
msgstr "Ви можете почати викориÑтовувати ці елементи зараз."
+msgid "High - S2"
+msgstr "ВиÑокий - S2"
+
msgid "High or unknown vulnerabilities present"
msgstr "ПриÑутні вразливоÑÑ‚Ñ– виÑокого або невідомого рівнÑ"
msgid "Highest role:"
msgstr "Ðайвища роль:"
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr "Події попереджень:"
@@ -20465,9 +20713,6 @@ msgstr "Ð”Ð¾Ð¼Ð°ÑˆÐ½Ñ Ñторінка"
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr "Ðевдале Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ñ…ÑƒÐºÐ°. ПереконайтеÑÑ, що Ñ†Ñ Ð³Ñ€ÑƒÐ¿Ð° має проєкт із комітами."
-msgid "Hook was successfully updated."
-msgstr "Хук уÑпішно оновлено."
-
msgid "Horizontal rule"
msgstr "Горизонтальна лінійка"
@@ -20514,7 +20759,7 @@ msgid "How do I use file templates?"
msgstr "Як викориÑтовувати шаблони файлів?"
msgid "How does pull mirroring work?"
-msgstr ""
+msgstr "Як працює віддзеркаленнÑ?"
msgid "How many seconds an IP counts toward the IP address limit."
msgstr ""
@@ -20532,7 +20777,7 @@ msgid "I forgot my password"
msgstr "Я забув пароль"
msgid "I want to explore GitLab to see if it’s worth switching to"
-msgstr ""
+msgstr "Я хочу доÑлідити GitLab чи варто перейти на нього"
msgid "I want to learn the basics of Git"
msgstr "Я хочу вивчити оÑнови Git"
@@ -20568,7 +20813,7 @@ msgid "IDE|Commit"
msgstr "Коміт"
msgid "IDE|Commit to %{branchName} branch"
-msgstr ""
+msgstr "Закомітити в %{branchName} гілку"
msgid "IDE|Edit"
msgstr "Редагувати"
@@ -20616,7 +20861,7 @@ msgid "IP address expiration time"
msgstr ""
msgid "IP address restrictions"
-msgstr ""
+msgstr "ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ IP-адреÑ"
msgid "IP addresses per user"
msgstr ""
@@ -20636,6 +20881,9 @@ msgstr "Ідентифікатори"
msgid "Identities"
msgstr "ІдентифікаціÑ"
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr "Ðовий код надіÑлано."
@@ -20660,6 +20908,9 @@ msgstr "Створити проєкт"
msgid "IdentityVerification|Didn't receive a code?"
msgstr "Ðе отримали код?"
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr "Введіть код."
@@ -20699,8 +20950,8 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr "Ðомер телефону"
-msgid "IdentityVerification|Phone number can't be blank."
-msgstr "Ðомер телефону не може бути порожнім."
+msgid "IdentityVerification|Phone number is required."
+msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
msgstr "Ðомер телефону має бути %{maxLength} цифр або менше."
@@ -20741,6 +20992,12 @@ msgstr "Код хибний. Введіть його ще раз або наді
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20753,9 +21010,18 @@ msgstr "Підтвердити адреÑу електронної пошти"
msgid "IdentityVerification|Verify payment method"
msgstr "Підтвердьте ÑпоÑіб оплати"
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr "Підтвердити Ñвою оÑобу"
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -21107,6 +21373,9 @@ msgstr "За цією URL-адреÑою немає дійÑного репозÐ
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21645,7 +21914,7 @@ msgid "InProductMarketing|repository mirroring"
msgstr "Ð²Ñ–Ð´Ð´Ð·ÐµÑ€ÐºÐ°Ð»ÐµÐ½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–ÑŽ"
msgid "InProductMarketing|set up a repo"
-msgstr ""
+msgstr "налаштувати репо"
msgid "InProductMarketing|test and deploy"
msgstr "теÑтуйте та розгортайте"
@@ -21674,12 +21943,18 @@ msgstr "Інцидент"
msgid "Incident Management Limits"
msgstr "Ліміти, пов’Ñзані із УправліннÑм Інцидентами"
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr "Деталі інциденту"
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr "Ðазва інциденту"
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr "%{hours} годин, %{minutes} хвилин залишилоÑÑ"
@@ -21998,6 +22273,9 @@ msgstr "Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñ–Ð½Ð´ÐµÐºÑу ÑкаÑовуєтьÑÑ"
msgid "Indicates whether this runner can pick jobs without tags"
msgstr "Вказує на те, чи може даний раннер виконувати Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÐµÐ· тегів"
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr "ПовідомлÑти кориÑтувачам без клічів SSH, що без них вони не зможуть відправлÑти код через SSH"
@@ -22113,7 +22391,7 @@ msgid "Insights|This project is filtered out in the insights.yml file (see the p
msgstr ""
msgid "Install GitLab Runner and ensure it's running."
-msgstr ""
+msgstr "Ð’Ñтановіть GitLab раннер Ñ– переконайтеÑÑ, що він працює."
msgid "Install on clusters"
msgstr "Ð’Ñтановити на клаÑтери"
@@ -22272,6 +22550,9 @@ msgstr "Увімкнути перевірку SSL"
msgid "Integrations|Enable comments"
msgstr "Увімкнути коментарі"
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -22305,6 +22586,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ”ÑŽ на рівні групи"
@@ -22576,7 +22860,7 @@ msgid "Invalid yaml"
msgstr "ÐедійÑний yaml"
msgid "Invalidated"
-msgstr ""
+msgstr "Визнано недійÑним"
msgid "Investigate vulnerability: %{title}"
msgstr "ДоÑлідити вразливіÑÑ‚ÑŒ: %{title}"
@@ -22653,6 +22937,9 @@ msgstr "ЗапроÑити Ñвоїх колег"
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr "Ми помітили, що ви нікого не запроÑили до цієї групи. ЗапроÑÑ–Ñ‚ÑŒ Ñвоїх колег, щоб ви могли обговорювати задачі, Ñпівпрацювати над запитами на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð° ділитиÑÑ Ñвоїми знаннÑми."
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22701,8 +22988,8 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr "УчаÑники були уÑпішно додані"
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
-msgstr "Будь лаÑка, виберіть учаÑників або введіть адреÑи електронної пошти Ð´Ð»Ñ Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ"
+msgid "InviteMembersModal|Please add members to invite"
+msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -23153,13 +23440,13 @@ msgid "IssuesAnalytics|Total:"
msgstr "Ð’Ñього:"
msgid "Issues|Move selected"
-msgstr ""
+msgstr "ПереміÑтити вибрані"
msgid "Issues|Tasks and test cases can not be moved."
-msgstr ""
+msgstr "ÐŸÑ–Ð´Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ñ‚Ð° теÑтові кейÑи не можуть бути переміщені."
msgid "Issues|Tasks can not be moved."
-msgstr ""
+msgstr "ÐŸÑ–Ð´Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð½Ðµ можуть бути переміщені."
msgid "Issues|Test cases can not be moved."
msgstr ""
@@ -23170,9 +23457,6 @@ msgstr ""
msgid "Issue|Title"
msgstr "Ðазва"
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr "Ðеможливо %{action} файли, Ñкі зберігаютьÑÑ Ñƒ LFS за допомогою веб-інтерфейÑу"
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -23219,7 +23503,7 @@ msgid "Iterations"
msgstr "Ітерації"
msgid "Iterations cadence not found"
-msgstr ""
+msgstr "ЧаÑтота ітерацій не знайдена"
msgid "Iterations cannot be manually added to cadences that use automatic scheduling"
msgstr ""
@@ -23311,9 +23595,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr "Ітерації починаютьÑÑ Ð·Ð° розкладом по %{weekday}"
@@ -23735,7 +24016,7 @@ msgid "Jobs|Finished"
msgstr "Завершено"
msgid "Jobs|Job is stuck. Check runners."
-msgstr ""
+msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ðµ. Перевірте раннери."
msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
msgstr ""
@@ -23746,9 +24027,6 @@ msgstr "Ðемає завдань Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ"
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr "СтатуÑ"
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23899,6 +24177,9 @@ msgstr "Це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ðµ, тому що цей прÐ
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23915,7 +24196,7 @@ msgid "Job|manual"
msgstr "вручну"
msgid "Job|triggered"
-msgstr ""
+msgstr "запущено"
msgid "Join GitLab today! You and your team can plan, build, and ship secure code all in one application. Get started here for free!"
msgstr ""
@@ -23980,6 +24261,9 @@ msgstr "Ключ"
msgid "Key (PEM)"
msgstr "Ключ (PEM)"
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr "Ключ:"
@@ -24049,9 +24333,6 @@ msgstr "Kubernetes-клаÑтери"
msgid "Kubernetes deployment not found"
msgstr "Ð Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Kubernetes не знайдено"
-msgid "Kubernetes error: %{error_code}"
-msgstr "Помилка Kubernetes: %{error_code}"
-
msgid "LDAP"
msgstr "LDAP"
@@ -24128,15 +24409,18 @@ msgstr "Мітки"
msgid "Labels"
msgstr "Мітки"
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr "Мітки можуть бути заÑтоÑовані до %{features}. Групові мітки доÑтупні Ð´Ð»Ñ Ð±ÑƒÐ´ÑŒ-Ñкого проєкту в межах групи."
-
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "Мітки можуть бути заÑтоÑовані до задач та запитів на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð´Ð»Ñ Ñ—Ñ… категоризації."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr "Мітки можуть бути заÑтоÑовані до задач та запитів на злиттÑ. Позначте мітку, щоб зробити Ñ—Ñ— пріоритетною."
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr "Мітки без задач у цій ітерації:"
@@ -24225,6 +24509,9 @@ msgstr "ВоÑтаннє редаговано %{link_start}%{avatar} %{name}%{li
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr "ОÑÑ‚Ð°Ð½Ð½Ñ Ð·Ð¼Ñ–Ð½Ð°"
@@ -24330,6 +24617,9 @@ msgstr "ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ"
msgid "Learn More."
msgstr "Прочитати більше."
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr "ДізнайтеÑÑ Ñк %{link_start}зробити внеÑок до вбудованих шаблонів%{link_end}"
@@ -24703,7 +24993,7 @@ msgid "Licensed Features"
msgstr "ФункціональніÑÑ‚ÑŒ, доÑтупна в ліцензії"
msgid "Licensed to:"
-msgstr ""
+msgstr "Ð›Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñ Ð½Ð°Ð»ÐµÐ¶Ð¸Ñ‚ÑŒ:"
msgid "Licenses"
msgstr "Ліцензії"
@@ -25017,12 +25307,12 @@ msgstr "Заблоковані файли"
msgid "Locked by %{fileLockUserName}"
msgstr "Заблоковано %{fileLockUserName}"
+msgid "Locked files"
+msgstr ""
+
msgid "Locked the discussion."
msgstr "ДиÑкуÑÑ–ÑŽ закрито."
-msgid "Locks give the ability to lock specific file or folder."
-msgstr "Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¼Ð¾Ð¶Ðµ бути заÑтоÑоване до конкретного файлу або директорії."
-
msgid "Locks the discussion."
msgstr "Блокує обговореннÑ."
@@ -25071,6 +25361,9 @@ msgstr ""
msgid "Logs"
msgstr "Логи"
+msgid "Low - S4"
+msgstr "Ðизький - S4"
+
msgid "Low vulnerabilities present"
msgstr "ПриÑутні вразливоÑÑ‚Ñ– низького рівнÑ"
@@ -25176,6 +25469,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr "Керувати функціÑми веб-IDE."
@@ -25228,13 +25524,13 @@ msgid "Manifest"
msgstr "МаніфеÑÑ‚"
msgid "Manifest file"
-msgstr ""
+msgstr "Файл маніфеÑту"
msgid "Manifest file import"
msgstr "Імпортувати файл маніфеÑту"
msgid "Manifest import"
-msgstr ""
+msgstr "Імпорт маніфеÑту"
msgid "Manual"
msgstr "Вручну"
@@ -25354,7 +25650,7 @@ msgid "Marked to do as done."
msgstr ""
msgid "Marks"
-msgstr ""
+msgstr "Помітити"
msgid "Marks this %{noun} as ready."
msgstr ""
@@ -25674,6 +25970,9 @@ msgstr "Середній Ñ‡Ð°Ñ Ð·Ð»Ð¸Ñ‚Ñ‚Ñ"
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr "Середній - S3"
+
msgid "Medium timeout"
msgstr "Середній Ñ‡Ð°Ñ Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ"
@@ -25695,6 +25994,12 @@ msgstr "%{member_name} запроÑив Ð²Ð°Ñ Ð¿Ñ€Ð¸Ñ”Ð´Ð½Ð°Ñ‚Ð¸ÑÑ Ð´Ð¾ GitLa
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr "Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð¿Ñ€Ð¸Ñ”Ð´Ð½Ð°Ñ‚Ð¸ÑÑ Ð´Ð¾ %{project_or_group} %{project_or_group_name}"
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr "КориÑтувачі"
@@ -26043,7 +26348,7 @@ msgid "MergeRequests|An error occurred while saving the draft comment."
msgstr "Виникла помилка під Ñ‡Ð°Ñ Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ñ‡ÐµÑ€Ð½ÐµÑ‚ÐºÐ¸ коментарÑ."
msgid "MergeRequests|Create issue to resolve thread"
-msgstr ""
+msgstr "Створити задачу, щоб вирішити обговореннÑ"
msgid "MergeRequests|Reference copied"
msgstr "ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ñкопійовано"
@@ -26830,6 +27135,9 @@ msgstr "Додати проєкти"
msgid "Modal|Close"
msgstr "Закрити"
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr "Змінено"
@@ -26893,9 +27201,6 @@ msgstr "Детальніше"
msgid "More information"
msgstr "Детальніше"
-msgid "More information and share feedback"
-msgstr "Ðадати більше інформації Ñ– поділитиÑÑ Ð²Ñ–Ð´Ð³ÑƒÐºÐ¾Ð¼"
-
msgid "More information is available|here"
msgstr "тут"
@@ -27107,6 +27412,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -27265,7 +27573,7 @@ msgid "New Project"
msgstr "Ðовий Проєкт"
msgid "New Protected Branch"
-msgstr ""
+msgstr "Ðова захищена гілка"
msgid "New Requirement"
msgstr "Ðова вимога"
@@ -27288,6 +27596,9 @@ msgstr "Ðова гілка"
msgid "New branch unavailable"
msgstr "Ðова гілка недоÑтупна"
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -27330,6 +27641,12 @@ msgstr "Згенеровано новий токен Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ Ð
msgid "New identity"
msgstr "Ðова ідентифікаціÑ"
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "Ðова задача"
@@ -27603,9 +27920,6 @@ msgstr "Проблем не знайдено"
msgid "No iteration"
msgstr "Ðемає ітерації"
-msgid "No iterations to show"
-msgstr "Ðемає ітерацій Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ñƒ"
-
msgid "No job log"
msgstr "Ðемає журналу завданнÑ"
@@ -27705,6 +28019,9 @@ msgstr "Жодних результатів"
msgid "No results found"
msgstr "Ðічого не знайдено"
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -28373,9 +28690,24 @@ msgstr ""
msgid "OK"
msgstr "OK"
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr "Об’єкт не Ñ–Ñнує на Ñервері або у Ð²Ð°Ñ Ð½Ðµ має до нього доÑтупу"
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr "СпоÑтережливіÑÑ‚ÑŒ"
@@ -28758,6 +29090,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28832,14 +29167,11 @@ msgid "Only SSH"
msgstr "Тільки SSH"
msgid "Only accessible by %{membersPageLinkStart}project members%{membersPageLinkEnd}. Membership must be explicitly granted to each user."
-msgstr ""
+msgstr "ДоÑтупно лише Ð´Ð»Ñ %{membersPageLinkStart}учаÑників проєкту%{membersPageLinkEnd}. ЧленÑтво має бути надано кожному кориÑтувачеві."
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr "Дозволити будь-кому реєÑтрувати облікові запиÑи на інÑтанÑах GitLab, Ñкі ви маєте намір кориÑтуватиÑÑ Ð½Ð¸Ð¼Ð¸. Дозвіл будь-кому зареєÑтруватиÑÑ Ñ€Ð¾Ð±Ð¸Ñ‚ÑŒ GitLab інÑтанÑи більш вразливими."
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28945,9 +29277,6 @@ msgstr "ВідкриваєтьÑÑ Ñƒ новому вікні"
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr "ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐ¸Ð»Ð°ÑÑ Ð½ÐµÐ²Ð´Ð°Ð»Ð¾. Перевірте журнал pod'а %{pod_name} Ð´Ð»Ñ Ð±Ñ–Ð»ÑŒÑˆ детальної інформації."
-
msgid "Operation not allowed"
msgstr "ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð·Ð°Ð±Ð¾Ñ€Ð¾Ð½ÐµÐ½Ð°"
@@ -29141,7 +29470,7 @@ msgid "Package already exists"
msgstr "Пакет вже Ñ–Ñнує"
msgid "Package and registry settings"
-msgstr ""
+msgstr "Параметри пакета та реєÑтру"
msgid "Package deleted successfully"
msgstr "Пакет уÑпішно видалено"
@@ -29233,6 +29562,9 @@ msgstr "Conan"
msgid "PackageRegistry|Conan Command"
msgstr "Команда Conan"
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -29319,11 +29651,14 @@ msgid "PackageRegistry|Delete package"
msgstr "Видалити пакет"
msgid "PackageRegistry|Delete package asset"
-msgstr ""
+msgstr "Видалити верÑÑ–ÑŽ пакета"
msgid "PackageRegistry|Delete package version"
msgstr "Видалити верÑÑ–ÑŽ пакету"
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr "Видалити вибране"
@@ -29339,6 +29674,12 @@ msgstr "Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¾Ñтаннього пакета призведе д
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -29363,6 +29704,18 @@ msgstr "Щоб дізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про реєÑÑ‚Ñ€ NuGet, %{link
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr "Щоб дізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про реєÑÑ‚Ñ€ PyPi, %{linkStart}переглÑньте документацію%{linkEnd}."
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29435,11 +29788,14 @@ msgstr "Команда NNGet"
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr "КількіÑÑ‚ÑŒ дублікатів, Ñкі потрібно зберегти"
+msgid "PackageRegistry|Other versions"
+msgstr "Інші верÑÑ–Ñ—"
+
msgid "PackageRegistry|Package Registry"
msgstr "РеєÑÑ‚Ñ€ пакетів"
msgid "PackageRegistry|Package asset deleted successfully"
-msgstr ""
+msgstr "Пакет уÑпішно видалено"
msgid "PackageRegistry|Package assets deleted successfully"
msgstr "РеÑурÑи пакета уÑпішно видалено"
@@ -29450,6 +29806,9 @@ msgstr "Пакет уÑпішно видалено"
msgid "PackageRegistry|Package formats"
msgstr "Формати пакетів"
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29460,6 +29819,9 @@ msgstr[3] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr "Пакет оновлено через коміт %{link} в гілці %{branch}, побудований через конвеєр %{pipeline}та опублікований в реєÑÑ‚Ñ€ %{datetime}"
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr "Видалити назавжди"
@@ -29514,6 +29876,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29562,9 +29927,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29587,6 +29949,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr "Ви збираєтеÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ верÑÑ–ÑŽ %{version} з %{name}. Ви впевнені?"
@@ -29602,6 +29971,9 @@ msgstr "npm"
msgid "PackageRegistry|published by %{author}"
msgstr "опубліковано %{author}"
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr "Пакети та реєÑтри"
@@ -29671,6 +30043,9 @@ msgstr "Параметр"
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr "Параметр \"job_id\" не може перевищувати довжину %{job_id_max_size}"
+msgid "Parameters"
+msgstr "Параметри"
+
msgid "Parent"
msgstr "Джерело"
@@ -29734,6 +30109,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr "Ðе задовільний"
+
+msgid "Password|Satisfied"
+msgstr "Задовільний"
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29977,6 +30361,30 @@ msgstr ""
msgid "Phone"
msgstr "Телефон"
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr "Виберіть ім'Ñ"
@@ -30164,7 +30572,7 @@ msgid "PipelineEditor|This tab will be usable when the CI/CD configuration file
msgstr ""
msgid "PipelineEditor|Validate pipeline"
-msgstr ""
+msgstr "Перевірити конвеєр"
msgid "PipelineEditor|Validate pipeline under selected conditions"
msgstr ""
@@ -30209,7 +30617,7 @@ msgid "PipelineSchedules|Last Pipeline"
msgstr "ОÑтанній Конвеєр"
msgid "PipelineSchedules|New schedule"
-msgstr ""
+msgstr "Ðовий розклад"
msgid "PipelineSchedules|Next Run"
msgstr "ÐаÑтупний запуÑк"
@@ -30602,7 +31010,7 @@ msgid "Pipelines|Use template"
msgstr "ВикориÑтовувати шаблон"
msgid "Pipelines|Validate"
-msgstr ""
+msgstr "Перевірити"
msgid "Pipelines|Validating GitLab CI configuration…"
msgstr "Перевірка конфігурації GitLab CI…"
@@ -30686,7 +31094,7 @@ msgid "Pipeline|Manual"
msgstr "Вручну"
msgid "Pipeline|Merge request pipeline"
-msgstr ""
+msgstr "Конвеєр запиту на злиттÑ"
msgid "Pipeline|Merge train pipeline"
msgstr "Конвеєр ланцюжка змін"
@@ -30892,6 +31300,9 @@ msgstr "Будь лаÑка, перевірте Ñвою поштову Ñкри
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr "Перш ніж продовжити, натиÑніть поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð² електронному лиÑÑ‚Ñ– з підтвердженнÑм. Його було надіÑлано на %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr "Будь лаÑка, доповніть Ñвій профіль адреÑою електронної пошти"
@@ -31048,9 +31459,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr "Будь лаÑка, введіть %{phrase_code} Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ Ð°Ð±Ð¾ закрийте це модальне вікно Ð´Ð»Ñ Ð²Ñ–Ð´Ð¼Ñ–Ð½Ð¸."
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr "Будь лаÑка, викориÑтовуйте цю форму щоб повідомити адміну Ñкі кориÑтувачі Ñтворюють Ñпам (задачі або коментарі) або поводÑÑ‚ÑŒÑÑ Ð½ÐµÐ½Ð°Ð»ÐµÐ¶Ð½Ð¸Ð¼ чином."
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -31204,6 +31612,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "Вибрати вміÑÑ‚ оглÑдової Ñторінки проєкту."
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr "Колір Ð´Ð»Ñ Ð´Ð¾Ð´Ð°Ð½Ð¸Ñ… Ñ€Ñдків"
@@ -31288,6 +31699,9 @@ msgstr "Тема Ð´Ð»Ñ Ð¿Ñ–Ð´Ñвітки ÑинтакÑиÑу"
msgid "Preferences|Tab width"
msgstr "Ширина табулÑції"
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr "Ð¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ñ” екÑпериментальною Ñ– переклади ще не завершені."
@@ -31300,9 +31714,15 @@ msgstr "Цей параметр дозволÑÑ” вам налаштувати Ð
msgid "Preferences|Time preferences"
msgstr "Параметри чаÑу"
+msgid "Preferences|Use legacy Web IDE"
+msgstr "ВикориÑтовувати Ñтарий Веб IDE"
+
msgid "Preferences|Use relative times"
msgstr "ВикориÑтовувати відноÑний чаÑ"
+msgid "Preferences|Web IDE"
+msgstr "Веб IDE"
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr "Коли ви вводите Ð¾Ð¿Ð¸Ñ Ð°Ð±Ð¾ коментар, натиÑÐºÐ°Ð½Ð½Ñ ÐºÐ»Ð°Ð²Ñ–Ñˆ %{kbdOpen}Enter%{kbdClose} у ÑпиÑку додає новий елемент нижче."
@@ -31420,17 +31840,26 @@ msgstr ""
msgid "Proceed"
msgstr "Продовжити"
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr "Ðналітика продукту"
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr "ÐудиторіÑ"
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
-msgstr "ВміÑÑ‚ віджетів"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgstr ""
msgid "Productivity"
msgstr "ПродуктивніÑÑ‚ÑŒ"
@@ -31792,6 +32221,12 @@ msgstr "Оновити ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
msgid "Profiles|Upload new avatar"
msgstr "Завантажити новий аватар"
+msgid "Profiles|Usage type"
+msgstr "Тип викориÑтаннÑ"
+
+msgid "Profiles|Usage type:"
+msgstr "Тип викориÑтаннÑ:"
+
msgid "Profiles|Use a private email - %{email}"
msgstr "ВикориÑтовувати приватну адреÑу електронної пошти - %{email}"
@@ -32326,12 +32761,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr "Дозволити"
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr "Ðналітика"
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -32413,9 +32854,6 @@ msgstr "Кожне Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñтворить окремий коміт."
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr "Кожен проєкт може мати влаÑний проÑÑ‚Ñ–Ñ€ Ð´Ð»Ñ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Ñвоїх образів Docker"
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr "Кожен проєкт може мати влаÑний проÑÑ‚Ñ–Ñ€ Ð´Ð»Ñ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Ð¹Ð¾Ð³Ð¾ пакетів. Примітка: РеєÑÑ‚Ñ€ пакунків завжди видно, коли проєкт Ñ” публічним."
@@ -32459,7 +32897,7 @@ msgid "ProjectSettings|Highlight the usage of hidden unicode characters. These h
msgstr ""
msgid "ProjectSettings|Housekeeping, export, archive, change path, transfer, and delete."
-msgstr ""
+msgstr "ОчищеннÑ, екÑпорт, архівуваннÑ, зміна шлÑху, перенеÑÐµÐ½Ð½Ñ Ñ‚Ð° видаленнÑ."
msgid "ProjectSettings|How do they differ?"
msgstr "Чим вони відрізнÑÑŽÑ‚ÑŒÑÑ?"
@@ -32468,7 +32906,7 @@ msgid "ProjectSettings|If merge trains are enabled, merging is only possible if
msgstr ""
msgid "ProjectSettings|Infrastructure"
-msgstr ""
+msgstr "ІнфраÑтруктура"
msgid "ProjectSettings|Internal"
msgstr "Внутрішній"
@@ -32486,7 +32924,7 @@ msgid "ProjectSettings|Leave empty to use default template."
msgstr ""
msgid "ProjectSettings|Manage who can see the project in the public access directory."
-msgstr ""
+msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð¸Ð¼, хто може бачити проєкт в каталозі публічного доÑтупу"
msgid "ProjectSettings|Manages large files such as audio, video, and graphics files."
msgstr "Керує великими файлами такими Ñк аудіо, відео та графіка."
@@ -32530,6 +32968,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32564,7 +33005,7 @@ msgid "ProjectSettings|Pipelines must succeed"
msgstr "Конвеєри мають бути уÑпішними"
msgid "ProjectSettings|Prevents direct linking to potentially sensitive media files"
-msgstr ""
+msgstr "Запобігає прÑмому підключенню до потенційно конфіденційних медіафайлів"
msgid "ProjectSettings|Private"
msgstr "Приватний"
@@ -32575,6 +33016,9 @@ msgstr "ВидиміÑÑ‚ÑŒ проєкту"
msgid "ProjectSettings|Public"
msgstr "Публічний"
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr "Релізи"
@@ -32588,7 +33032,7 @@ msgid "ProjectSettings|Require an associated issue from Jira"
msgstr ""
msgid "ProjectSettings|Require authentication to view media files"
-msgstr ""
+msgstr "Вимагати автентифікацію Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду медіафайлів"
msgid "ProjectSettings|Requirements"
msgstr "Вимоги"
@@ -32704,8 +33148,8 @@ msgstr "КориÑтувачі можуть запитувати доÑтуп"
msgid "ProjectSettings|View and edit files in this project."
msgstr "ПереглÑдати та редагувати файли в цьому проєкті."
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
-msgstr "ПереглÑд Ñ– Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñ–Ð² у цьому проєкті. Ðе учаÑники проєкту мають доÑтуп лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
+msgstr ""
msgid "ProjectSettings|View project analytics."
msgstr "ПереглÑнути аналітику проєкту."
@@ -32720,7 +33164,7 @@ msgid "ProjectSettings|Warn about Potentially Unwanted Characters"
msgstr ""
msgid "ProjectSettings|What are badges?"
-msgstr ""
+msgstr "Що таке значки?"
msgid "ProjectSettings|What are merge trains?"
msgstr ""
@@ -32785,6 +33229,9 @@ msgstr "Netrify/Plain HTML"
msgid "ProjectTemplates|NodeJS Express"
msgstr "NodeJS Express"
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32831,7 +33278,7 @@ msgid "ProjectTemplates|iOS (Swift)"
msgstr "iOS (Swift)"
msgid "ProjectTransfer|An error occurred fetching the transfer locations, please refresh the page and try again."
-msgstr ""
+msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð¼Ñ–Ñць перенеÑеннÑ, будь лаÑка, оновіть Ñторінку та повторіть Ñпробу."
msgid "ProjectView|Activity"
msgstr "ÐктивніÑÑ‚ÑŒ"
@@ -33307,9 +33754,6 @@ msgstr "Захищена гілка"
msgid "Protected Branches"
msgstr "Захищені гілки"
-msgid "Protected Environment"
-msgstr "Захищене Ñередовище"
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33338,7 +33782,7 @@ msgid "ProtectedBranch|Allow all users with push access to force push."
msgstr ""
msgid "ProtectedBranch|Allowed to create"
-msgstr ""
+msgstr "Дозволено Ñтворювати"
msgid "ProtectedBranch|Allowed to force push"
msgstr ""
@@ -33392,7 +33836,7 @@ msgid "ProtectedBranch|Learn more."
msgstr "ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ."
msgid "ProtectedBranch|New Protected Tag"
-msgstr ""
+msgstr "Ðовий захищений тег"
msgid "ProtectedBranch|No tags are protected."
msgstr ""
@@ -33410,7 +33854,7 @@ msgid "ProtectedBranch|Protected branches"
msgstr "Захищені гілки"
msgid "ProtectedBranch|Protected branches, merge request approvals, and status checks will appear here once configured."
-msgstr ""
+msgstr "Захищені гілки, Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð° перевірки Ñтану з'ÑвлÑÑ‚ÑŒÑÑ Ñ‚ÑƒÑ‚ піÑÐ»Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ."
msgid "ProtectedBranch|Protected tags (%{tags_count})"
msgstr ""
@@ -33983,6 +34427,9 @@ msgstr[1] "ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‡ÐµÑ€ÐµÐ· %d Ñекунди Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°
msgstr[2] "ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‡ÐµÑ€ÐµÐ· %d Ñекунд Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð°ÐºÑ‚ÑƒÐ°Ð»ÑŒÐ½Ð¾Ð³Ð¾ Ñтану..."
msgstr[3] "ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‡ÐµÑ€ÐµÐ· %d Ñекунд Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð°ÐºÑ‚ÑƒÐ°Ð»ÑŒÐ½Ð¾Ð³Ð¾ Ñтану..."
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -34182,6 +34629,9 @@ msgstr "Runbooks"
msgid "ReleaseAssetLinkType|Runbooks"
msgstr "Runbooks"
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34492,7 +34942,7 @@ msgid "Render diagrams in your documents using PlantUML."
msgstr ""
msgid "Renew subscription"
-msgstr ""
+msgstr "Подовжити підпиÑку"
msgid "Renews"
msgstr ""
@@ -34521,6 +34971,9 @@ msgstr "Повторно відкрити %{quick_action_target}"
msgid "Reopened this %{quick_action_target}."
msgstr "Повторно відкрито %{quick_action_target}."
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr "Повторно відкриває %{quick_action_target}."
@@ -34575,11 +35028,8 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr "Повідомити про порушеннÑ"
-
-msgid "Report abuse to admin"
-msgstr "Повідомити адмініÑтратора про порушеннÑ"
+msgid "Report abuse to administrator"
+msgstr ""
msgid "Report couldn't be prepared."
msgstr ""
@@ -34929,6 +35379,9 @@ msgstr "Запит"
msgid "Request Access"
msgstr "Запит доÑтупу"
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -35180,6 +35633,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr "Продовжити"
@@ -35256,7 +35712,7 @@ msgstr ""
msgid "Review time"
msgstr "Ð§Ð°Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду"
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -35353,6 +35809,9 @@ msgstr "ЗапуÑтити очищеннÑ"
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -35419,7 +35878,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr "ДоÑтупна нова верÑÑ–Ñ"
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -35446,6 +35905,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35467,6 +35929,9 @@ msgstr ""
msgid "Runners|Available"
msgstr "ДоÑтупні"
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr "ДоÑтупно Ð´Ð»Ñ Ð²ÑÑ–Ñ… проєктів"
@@ -35552,6 +36017,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr "Фільтр проєктів"
@@ -35570,6 +36038,9 @@ msgstr "Як оновити GitLab раннер?"
msgid "Runners|IP Address"
msgstr "IP-адреÑа"
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr "Ð’Ñтановити раннер"
@@ -35609,12 +36080,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr "Ðіколи не закінчуєтьÑÑ"
-msgid "Runners|New group runners view"
-msgstr ""
+msgid "Runners|New group runners can be registered"
+msgstr "Можна реєÑтрувати нову групу ранерів"
msgid "Runners|New registration token generated!"
msgstr "Ðовий реєÑтраційний токен, Ñтворений!"
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr "Ðе знайдено результатів"
@@ -35643,7 +36117,7 @@ msgid "Runners|Owner"
msgstr "ВлаÑник"
msgid "Runners|Pause from accepting jobs"
-msgstr ""
+msgstr "Призупинити прийнÑÑ‚Ñ‚Ñ Ð·Ð°Ð²Ð´Ð°Ð½ÑŒ"
msgid "Runners|Paused"
msgstr "Призупинено"
@@ -35709,6 +36183,9 @@ msgstr "Раннер #%{runner_id}"
msgid "Runners|Runner %{name} was deleted"
msgstr "Раннер %{name} було видалено"
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr "Раннер призначено Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ñƒ."
@@ -35775,6 +36252,9 @@ msgstr "Раннери — це агенти, Ñкі виконують ваші
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr "Раннери — це агенти, Ñкі керують вашою роботою CI/CD. Щоб зареєÑтрувати нові раннери, будь лаÑка, звернітьÑÑ Ð´Ð¾ адмініÑтратора."
+msgid "Runners|Running"
+msgstr "Запущено"
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35815,7 +36295,7 @@ msgid "Runners|Status"
msgstr "СтатуÑ"
msgid "Runners|Stop the runner from accepting new jobs."
-msgstr ""
+msgstr "Заборонити ранеру приймати нові завданнÑ."
msgid "Runners|Tags"
msgstr "Теги"
@@ -35823,12 +36303,6 @@ msgstr "Теги"
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr "Теги визначають, Ñкі типи завдань може виконати раннер. Позначаючи раннер, ви гарантуєте, що Ñпільні раннери виконують лише Ñ‚Ñ– завданнÑ, Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ñких вони обладнані."
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr "Проєкт, група або інÑтанÑ, де зареєÑтровано раннер. ІнÑÑ‚Ð°Ð½Ñ Ñ€Ð°Ð½Ð½ÐµÑ€Ð¸ завжди належать адмініÑтратору."
@@ -35897,7 +36371,7 @@ msgid "Runners|Use the runner for the currently assigned projects only. Only adm
msgstr ""
msgid "Runners|Use the runner on pipelines for protected branches only."
-msgstr ""
+msgstr "ВикориÑтовуйте ранер в конвеєрах лише Ð´Ð»Ñ Ð·Ð°Ñ…Ð¸Ñ‰ÐµÐ½Ð¸Ñ… гілок."
msgid "Runners|Value"
msgstr "ЗначеннÑ"
@@ -35944,6 +36418,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr "ВлаÑник"
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr "ВиконуєтьÑÑ"
@@ -35971,14 +36448,17 @@ msgstr "Єдиний вхід SAML"
msgid "SAML single sign-on for %{group_name}"
msgstr "Єдиний вхід SAML Ð´Ð»Ñ %{group_name}"
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr "Група %{strongOpen}%{group_path}%{strongClose} дозволÑÑ” входити за допомогою єдиного входу."
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
-msgstr "Щоб отримати доÑтуп до %{strongOpen}%{group_name}%{strongClose}, ви повинні ввійти за допомогою ÑиÑтеми єдиного входу через зовнішню Ñторінку входу."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
+msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
msgstr "Щоб дозволити %{strongOpen}%{group_name}%{strongClose} керувати вашим обліковим запиÑом GitLab %{strongOpen}%{username}%{strongClose} (%{email}) піÑÐ»Ñ ÑƒÑпішного входу за допомогою єдиного входу, виберіть %{strongOpen}Ðвторизувати%{strongClose}."
@@ -36019,6 +36499,9 @@ msgstr ""
msgid "SSH key"
msgstr "SSH ключ"
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -36034,6 +36517,15 @@ msgstr ""
msgid "SSH public key"
msgstr "Відкритий SSH-ключ"
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr "Перевірка SSL:"
@@ -36100,6 +36592,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr "Конвеєр запущений"
@@ -36127,9 +36625,15 @@ msgstr "Виберіть профіль Ñканера"
msgid "ScanExecutionPolicy|Select site profile"
msgstr "Вибрати профіль Ñайту"
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr "Профіль Ñайту"
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr "агент"
@@ -36142,18 +36646,27 @@ msgstr "у проÑторі імен"
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -36238,6 +36751,9 @@ msgstr "Пошук"
msgid "Search GitLab"
msgstr "Пошук в GitLab"
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36338,7 +36854,7 @@ msgid "Search projects..."
msgstr "Пошук проєктів..."
msgid "Search protected branches"
-msgstr ""
+msgstr "Пошук захищених гілок"
msgid "Search rate limits"
msgstr ""
@@ -36777,6 +37293,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36834,9 +37353,18 @@ msgstr "Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ образи."
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr "Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ Ñканери вразливоÑÑ‚Ñ–."
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36852,6 +37380,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr "Ðова політика"
@@ -36888,6 +37419,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36960,6 +37494,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36969,12 +37506,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -37044,6 +37590,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -37071,6 +37620,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr "гілки"
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -37107,6 +37659,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -37116,9 +37671,24 @@ msgstr "Додати проєкти"
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr "Ð’ÑÑ– клаÑтери"
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr "Ð’ÑÑ– рівні"
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -37263,6 +37833,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37488,6 +38061,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr "Ð”Ð»Ñ Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÑƒ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ виберіть його в лівій бічній панелі. Пізніше ви будете мати можливіÑÑ‚ÑŒ закомітити Ñвої зміни."
+msgid "Select a group"
+msgstr "Вибрати групу"
+
msgid "Select a label"
msgstr "Вибрати мітку"
@@ -37534,7 +38110,7 @@ msgid "Select branch"
msgstr "Виберіть гілку"
msgid "Select branch or create wildcard"
-msgstr ""
+msgstr "Оберіть гілку або Ñтворіть шаблон"
msgid "Select branches"
msgstr ""
@@ -37593,6 +38169,9 @@ msgstr "Виберіть звіт"
msgid "Select reviewer(s)"
msgstr "Вибрати оглÑдача(ів)"
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr "Виберіть джерело"
@@ -38083,6 +38662,9 @@ msgstr ""
msgid "Shared Runners"
msgstr "Загальні раннери"
+msgid "Shared Runners:"
+msgstr "Загальні раннери:"
+
msgid "Shared projects"
msgstr "Спільні проєкти"
@@ -38209,6 +38791,9 @@ msgstr "Показати оÑтанню верÑÑ–ÑŽ"
msgid "Show list"
msgstr "Показати ÑпиÑок"
+msgid "Show more"
+msgstr "Показати більше"
+
msgid "Show one file at a time"
msgstr ""
@@ -38369,9 +38954,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr "Увійти в %{group_name}"
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38525,7 +39107,7 @@ msgstr "Slack Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ð´Ð¾Ð·Ð²Ð¾Ð»Ð¸Ñ‚ÑŒ вам взаємодіÑÑ
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38540,13 +39122,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38570,13 +39155,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38714,11 +39302,11 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
-msgstr "ХтоÑÑŒ редагував цю задачу в одночаÑно із вами. Будь лаÑка, ознайомтеÑÑ Ñ–Ð· %{linkStart}нею%{linkEnd} Ñ– переконайтеÑÑ, ваші зміни не затруть зміни інших."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr "ХтоÑÑŒ відредагував цю %{issueType} одночаÑно з вами. ÐžÐ¿Ð¸Ñ Ð±ÑƒÐ»Ð¾ оновлено, тому вам знову треба внеÑти Ñвої зміни."
@@ -38759,6 +39347,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38777,6 +39368,9 @@ msgstr "ЩоÑÑŒ пішло не так під Ñ‡Ð°Ñ Ð°Ñ€Ñ…Ñ–Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38846,6 +39440,9 @@ msgstr "Помилка при отриманні ÑпиÑку пакетів."
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -39239,6 +39836,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð¾Ð±'єднаного (squash) коміту"
@@ -39344,6 +39944,9 @@ msgstr "Створити ланцюжок змін піÑÐ»Ñ ÑƒÑпішного
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr "Розпочати пошук"
@@ -39407,6 +40010,9 @@ msgstr "СтатиÑтика"
msgid "Status"
msgstr "СтатуÑ"
+msgid "Status (optional)"
+msgstr "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ (необов'Ñзково)"
+
msgid "Status was retried."
msgstr ""
@@ -40065,7 +40671,7 @@ msgid "SuperSonics|Learn more about %{activationLinkStart}activating your subscr
msgstr ""
msgid "SuperSonics|Licensed to"
-msgstr ""
+msgstr "Ð›Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñ Ð½Ð°Ð»ÐµÐ¶Ð¸Ñ‚ÑŒ:"
msgid "SuperSonics|Manage"
msgstr ""
@@ -40146,6 +40752,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” активної підпиÑки"
@@ -40171,7 +40780,7 @@ msgid "SuperSonics|Your %{subscriptionEntryName} cannot be displayed at the mome
msgstr ""
msgid "SuperSonics|Your current GitLab installation has %{userCount} active %{userCountUsers}, which exceeds your new subscription seat count of %{licenseUserCount} by %{overageCount}. To activate your new subscription, %{purchaseLinkStart}purchase%{purchaseLinkEnd} an additional %{overageCount} %{overageCountSeats}, or %{deactivateLinkStart}deactivate%{deactivateLinkEnd} or %{blockLinkStart}block%{blockLinkEnd} %{overageCount} %{overageCountUsers}. For further assistance, contact %{licenseSupportLinkStart}GitLab support%{licenseSupportLinkEnd}."
-msgstr ""
+msgstr "Ваша поточна інÑталÑÑ†Ñ–Ñ GitLab має %{userCount} активних %{userCountUsers}, що перевищує вашу нову кількіÑÑ‚ÑŒ міÑць у підпиÑці %{licenseUserCount} на %{overageCount}. Щоб активувати нову підпиÑку, %{purchaseLinkStart}придбайте%{purchaseLinkEnd} додаткову %{overageCount} %{overageCountSeats}або %{deactivateLinkStart}деактивуйте%{deactivateLinkEnd} або %{blockLinkStart}заблокуйте%{blockLinkEnd} %{overageCount} %{overageCountUsers}. Щоб отримати додаткову допомогу, звернітьÑÑ Ð´Ð¾ %{licenseSupportLinkStart}Ñлужби підтримки GitLab%{licenseSupportLinkEnd}."
msgid "SuperSonics|Your future dated license was successfully added"
msgstr ""
@@ -40236,9 +40845,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr "Перейти в гілку/тег"
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr "Перейти на GitLab Next"
@@ -40521,6 +41127,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr "Команда"
@@ -41111,6 +41723,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr "ÐаÑтупні елементи ÐЕ будуть екÑпортовані:"
@@ -41479,9 +42094,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr "Повідомлень про Ð·Ð»Ð¾Ð²Ð¶Ð¸Ð²Ð°Ð½Ð½Ñ Ð½ÐµÐ¼Ð°Ñ”!"
-msgid "There are no archived projects yet"
-msgstr "Ðаразі немає жодного архівованого проєкту"
-
msgid "There are no archived requirements"
msgstr "Ðемає жодних архівних вимог"
@@ -41539,9 +42151,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr "Ðаразі пакети відÑутні"
-msgid "There are no projects shared with this group yet"
-msgstr "Ще немає Ñпільних проєктів з цією групою"
-
msgid "There are no secure files yet."
msgstr "Ðемає захищених файлів."
@@ -41587,6 +42196,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr "Проблема Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾ вашого приÑтрою."
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41707,6 +42319,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41746,6 +42361,9 @@ msgstr "Помилка при Ñкиданні хвилин Ð´Ð»Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr "Помилка при збереженні ваших змін."
@@ -41816,7 +42434,7 @@ msgid "These will be sent to %{email} in an attachment once finished."
msgstr ""
msgid "Things to be aware of before transferring:"
-msgstr ""
+msgstr "Що потрібно знати перед перенеÑеннÑм:"
msgid "Third Party Advisory Link"
msgstr ""
@@ -41839,9 +42457,6 @@ msgstr "Цей %{viewer} не може бути відображено череÐ
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41861,7 +42476,7 @@ msgid "This action cannot be undone, and will permanently delete the %{key} SSH
msgstr ""
msgid "This action deletes %{codeOpen}%{project_path_with_namespace}%{codeClose} and everything this project contains. %{strongOpen}There is no going back.%{strongClose}"
-msgstr ""
+msgstr "Ð¦Ñ Ð´Ñ–Ñ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚ÑŒ %{codeOpen}%{project_path_with_namespace}%{codeClose} Ñ– вÑе, що міÑтитьÑÑ Ñƒ цьому проєкті. %{strongOpen}Зворотного шлÑху немає.%{strongClose}"
msgid "This action deletes %{codeOpen}%{project_path_with_namespace}%{codeClose} on %{date} and everything this project contains."
msgstr ""
@@ -42031,6 +42646,9 @@ msgstr "Цей епік не Ñ–Ñнує або у Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” доÑтат
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr "Ð¦Ñ Ñ„ÑƒÐ½Ñ†Ñ–Ð¾Ð½Ð°Ð»ÑŒÐ½Ñ–ÑÑ‚ÑŒ вимагає ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¾Ð³Ð¾ Ñховища"
@@ -42049,7 +42667,7 @@ msgstr ""
msgid "This group"
msgstr "Ð¦Ñ Ð³Ñ€ÑƒÐ¿Ð°"
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42523,6 +43141,12 @@ msgstr "ЗалишилоÑÑ Ñ‡Ð°Ñу"
msgid "Time spent"
msgstr "Витрачено чаÑу"
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42777,6 +43401,9 @@ msgstr ""
msgid "Title:"
msgstr "Заголовок:"
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42810,9 +43437,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr "Щоб додати відображуване ім'Ñ, налаштуйте адреÑу електронної пошти Service Desk. %{linkStart}Докладніше%{linkEnd}."
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr "Щоб додати Ð·Ð°Ð¿Ð¸Ñ Ð²Ñ€ÑƒÑ‡Ð½Ñƒ, надайте наÑтупні відомоÑÑ‚Ñ– заÑтоÑунку у вашому телефоні."
@@ -42927,6 +43551,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -43015,11 +43642,14 @@ msgid "Todos|Assigned"
msgstr "Призначено"
msgid "Todos|Could not merge"
-msgstr ""
+msgstr "Ðе вдалоÑÑ Ð¾Ð±'єднати"
msgid "Todos|Design"
msgstr "Дизайн"
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr "Епік"
@@ -43053,6 +43683,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr "Позначити вÑе Ñк виконане"
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr "Згадувано"
@@ -43068,13 +43701,13 @@ msgstr "Ðе залишилоÑÑ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸. Дай п'ÑÑ‚ÑŒ!"
msgid "Todos|Pipelines"
msgstr "Конвеєр"
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -43089,24 +43722,24 @@ msgstr "Ð’Ñе готово!"
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
+msgstr ""
+
+msgid "Todos|set %{who} as an approver"
msgstr ""
msgid "Todos|yourself"
msgstr "Ñебе"
-msgid "Todo|at %{todo_parent_path}"
-msgstr "о %{todo_parent_path}"
-
msgid "Toggle GitLab Next"
msgstr ""
@@ -43315,7 +43948,7 @@ msgid "Transfer project"
msgstr "ПеренеÑти проєкт"
msgid "Transfer your project into another namespace. %{link_start}Learn more.%{link_end}"
-msgstr ""
+msgstr "ПеренеÑÑ–Ñ‚ÑŒ Ñвій проєкт в інший проÑÑ‚Ñ–Ñ€ імен. %{link_start}ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ.%{link_end}"
msgid "TransferGroup|Cannot transfer group to one of its subgroup."
msgstr ""
@@ -43477,6 +44110,12 @@ msgstr "Тригер уÑпішно оновлено."
msgid "Triggerer"
msgstr "Запущено"
+msgid "Trigger|Description"
+msgstr "ОпиÑ"
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43513,6 +44152,9 @@ msgstr "Спробуйте увійти за допомогою Ñвого імÐ
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr "Спробуйте нову веб-IDE"
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43690,6 +44332,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43813,6 +44461,9 @@ msgstr "Ðа жаль, ваше Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ—
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43894,6 +44545,9 @@ msgstr "Ðе вирішено"
msgid "Unschedule job"
msgstr "СкаÑувати заплановане завданнÑ"
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr "Видалити із обраних"
@@ -44077,6 +44731,9 @@ msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð¼Ñ–Ð½ до терміналу"
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr "Попередній"
@@ -44095,18 +44752,12 @@ msgstr "СтатиÑтичні дані щодо Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Ð ÐµÑ”ÑÑ‚
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr "Ð¦Ñ ÑтатиÑтика Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Ð½Ð° рівні проєкту не включає економію за рахунок дедуплікації на рівні вÑього Ñайту Ñ– не викориÑтовуєтьÑÑ Ð´Ð»Ñ Ñ€Ð¾Ð·Ñ€Ð°Ñ…ÑƒÐ½ÐºÑƒ загального обÑÑгу Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ñтору імен."
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr "%{help_link_start}Загальні раннери%{help_link_end} вимкнено, тому ліміти на викориÑÑ‚Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ñ–Ð² відÑутні"
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -44134,9 +44785,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ Ñ…Ð²Ð¸Ð»Ð¸Ð½ CI/CD із %{usageSince}"
@@ -44146,9 +44794,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr "РеєÑÑ‚Ñ€ контейнерів"
-msgid "UsageQuota|Current period usage"
-msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ Ð·Ð° поточний період"
-
msgid "UsageQuota|Dependency proxy"
msgstr "ПрокÑÑ– залежноÑтей"
@@ -44198,7 +44843,7 @@ msgid "UsageQuota|No CI minutes usage data available."
msgstr ""
msgid "UsageQuota|No projects to display."
-msgstr ""
+msgstr "Ðемає проєктів Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ."
msgid "UsageQuota|Packages"
msgstr "Пакети"
@@ -44275,10 +44920,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr "ПроÑÑ‚Ñ–Ñ€ імен наразі викориÑтовує %{strong_start}%{used_storage}%{strong_end} пам’ÑÑ‚Ñ– проÑтору імен. ПереглÑдайте та керуйте Ñвоїм викориÑтаннÑм із %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ%{link_end} про те, Ñк зменшити пам’ÑÑ‚ÑŒ."
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -44287,12 +44929,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr "Цей проÑÑ‚Ñ–Ñ€ імен не міÑтить проєктів, що викориÑтовують загальні раннери"
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -44305,9 +44941,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr "Без обмежень"
-
msgid "UsageQuota|Uploads"
msgstr "ЗавантаженнÑ"
@@ -44341,7 +44974,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувача &gt; Квоти викориÑтаннÑ"
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -44350,16 +44983,16 @@ msgstr "Вікі"
msgid "UsageQuota|Wiki content."
msgstr "ВміÑÑ‚ Вікі."
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr "Ви викориÑтали: %{usage} %{limit}"
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44533,6 +45166,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44801,9 +45437,6 @@ msgstr "ОÑобиÑÑ‚Ñ– проєкти"
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr "Повідомити про зловживаннÑ"
-
msgid "UserProfile|Retry"
msgstr "Повторити"
@@ -44922,13 +45555,13 @@ msgid "Users cannot be added to projects in this group"
msgstr ""
msgid "Users in License"
-msgstr ""
+msgstr "КориÑтувачі в ліцензії"
msgid "Users or groups set as approvers in the project's or merge request's settings."
msgstr ""
msgid "Users over License"
-msgstr ""
+msgstr "КориÑтувачі перевищили ліцензію"
msgid "Users requesting access to"
msgstr "КориÑтувачі, Ñкі запитують доÑтуп до"
@@ -44993,6 +45626,9 @@ msgstr "ВикориÑтовуєтьÑÑ Ð¾Ð±Ð¾Ð²â€™Ñзкова ÑтратегÑ
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr "VS Code у вашому браузері. ПереглÑдайте код Ñ– вноÑьте зміни з того Ñамого інтерфейÑу, що й у вашій локальній IDE 🎉"
+
msgid "Valid From"
msgstr ""
@@ -45065,6 +45701,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -45083,6 +45722,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -45131,12 +45773,12 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
-msgstr ""
-
msgid "Variable"
msgstr "Змінна"
+msgid "Variable value will be evaluated as raw string."
+msgstr ""
+
msgid "Variable will be masked in job logs."
msgstr "Змінна буде замаÑкована в журналі завдань."
@@ -45146,7 +45788,7 @@ msgstr "Змінні"
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -45203,6 +45845,21 @@ msgstr "ВерÑÑ–Ñ %{versionNumber}"
msgid "Version %{versionNumber} (latest)"
msgstr "ВерÑÑ–Ñ %{versionNumber} (оÑтаннÑ)"
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr "Ðайновіша верÑÑ–Ñ"
@@ -45212,6 +45869,18 @@ msgstr "Оновити Ñкомога швидше"
msgid "VersionCheck|Update available"
msgstr "ДоÑтупне оновленнÑ"
+msgid "VersionCheck|Upgrade now"
+msgstr "Оновити зараз"
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr "Ваша верÑÑ–Ñ GitLab"
@@ -45753,7 +46422,7 @@ msgid "Vulnerability|Scanner Provider"
msgstr ""
msgid "Vulnerability|Scanner:"
-msgstr ""
+msgstr "Сканер:"
msgid "Vulnerability|Security Audit"
msgstr ""
@@ -45878,6 +46547,9 @@ msgstr "Ми виÑвили потенційний Ñпам у %{humanized_resou
msgid "We don't have enough data to show this stage."
msgstr "Ми не маємо доÑтатньо даних Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ñ†Ñ–Ñ”Ñ— Ñтадії."
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr "Ми виÑвили наÑтупні помилки:"
@@ -45953,9 +46625,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr "Форк проєкту"
@@ -45969,16 +46638,7 @@ msgid "WebIDE|Quickly and easily edit multiple files in your project."
msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
-msgstr ""
-
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
+msgstr "Швидко і легко редагувати декілька файлів у вашому проєкті."
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
@@ -45986,9 +46646,6 @@ msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -46019,6 +46676,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr "Вебхук Ñтворено"
+
+msgid "Webhook was deleted"
+msgstr "Вебхук видалено"
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr "Вебхук оновлено"
+
msgid "Webhook:"
msgstr ""
@@ -46085,9 +46754,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -46136,14 +46802,11 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr "Події конвеєрів"
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
-msgstr ""
+msgid "Webhooks|Pipeline events"
+msgstr "Події конвеєрів"
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
msgstr "РегулÑрний вираз, такий Ñк %{REGEX_CODE}."
@@ -46181,9 +46844,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr "URL-адреÑа"
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46579,6 +47239,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr "Буде випущено"
+
msgid "Will deploy to"
msgstr "Розгорне на"
@@ -46588,9 +47251,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr "За допомогою вимог, ви можете вÑтановити Ñтандарти Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ Ñвоєї продукції."
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr "СкаÑувати запит доÑтупу"
@@ -46609,6 +47269,12 @@ msgstr "%{workItemType} видалено"
msgid "WorkItem|Add"
msgstr "Додати"
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr "Додати заголовок"
@@ -46624,9 +47290,6 @@ msgstr "Додати термін виконаннÑ"
msgid "WorkItem|Add start date"
msgstr "Додати дату початку"
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr "Додати до ітерації"
@@ -46649,6 +47312,9 @@ msgstr[3] "Виконавців"
msgid "WorkItem|Cancel"
msgstr "СкаÑувати"
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr "Дочірній елемент видалено"
@@ -46658,6 +47324,12 @@ msgstr "Закрито"
msgid "WorkItem|Collapse tasks"
msgstr "Згорнути завданнÑ"
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46670,30 +47342,39 @@ msgstr "Дати"
msgid "WorkItem|Delete %{workItemType}"
msgstr "Видалити %{workItemType}"
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr "Дата виконаннÑ"
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
+msgid "WorkItem|Expand tasks"
+msgstr "Розгорнути підзавданнÑ"
+
msgid "WorkItem|Incident"
msgstr "Інцидент"
-msgid "WorkItem|Introducing tasks"
-msgstr ""
-
msgid "WorkItem|Issue"
msgstr "Задача"
msgid "WorkItem|Iteration"
msgstr "ІтераціÑ"
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr "Етап"
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr "Ðемає ітерації"
@@ -46703,12 +47384,18 @@ msgstr "Ðемає відповідних результатів"
msgid "WorkItem|No milestone"
msgstr "Ðемає етапу"
-msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
+msgid "WorkItem|No objectives or key results are currently assigned."
msgstr ""
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
+msgstr "ÐŸÑ–Ð´Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð½Ð°Ñ€Ð°Ð·Ñ– не призначені. ВикориÑтовуйте підзавданнÑ, щоб розбити цю задачу на менші чаÑтини."
+
msgid "WorkItem|None"
msgstr "Ðемає"
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr "Лише учаÑники проєкту з принаймні роллю Репортера, автор Ñ– виконавці можуть переглÑдати це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð°Ð±Ð¾ отримувати ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ нього."
@@ -46721,9 +47408,18 @@ msgstr "Видалити"
msgid "WorkItem|Requirements"
msgstr "Вимоги"
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr "Вибрати тип"
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr "ЩоÑÑŒ пішло не так під Ñ‡Ð°Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ %{workItemType}. Будь лаÑка Ñпробуйте ще раз."
@@ -46764,13 +47460,13 @@ msgid "WorkItem|Start date"
msgstr "Дата початку"
msgid "WorkItem|Task"
-msgstr ""
+msgstr "підзавданнÑ"
msgid "WorkItem|Task deleted"
-msgstr ""
+msgstr "ÐŸÑ–Ð´Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð¾"
msgid "WorkItem|Tasks"
-msgstr ""
+msgstr "ПідзавданнÑ"
msgid "WorkItem|Test case"
msgstr ""
@@ -46787,7 +47483,7 @@ msgstr "Увімкнути конфіденційніÑÑ‚ÑŒ"
msgid "WorkItem|Undo"
msgstr "СкаÑувати"
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46817,6 +47513,9 @@ msgstr "ÐапиÑати коментар…"
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46986,10 +47685,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr "Ви намагаєтеÑÑŒ завантажити щоÑÑŒ, що не Ñ” зображеннÑм. Будь лаÑка, завантажте файл .png, .jpg, .jpeg, .gif, .bmp, .tiff або .ico."
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -47028,13 +47727,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr "Ви завжди можете відредагувати це пізніше"
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -47101,9 +47793,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "Ви можете запроÑити нового учаÑника до %{project_name} або запроÑити іншу групу."
@@ -47122,18 +47811,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr "Ви можете тепер відправити запит на злиттÑ, щоб Ñ†Ñ Ð·Ð¼Ñ–Ð½Ð° попала у вихідну гілку."
msgid "You can now submit a merge request to get this change into the original project."
msgstr "Ви можете тепер відправити запит на злиттÑ, щоб Ñ†Ñ Ð·Ð¼Ñ–Ð½Ð° потрапила у вихідний проєкт."
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -47215,6 +47898,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr "Ви не змогли Ñтворити новий тригер."
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr "У Ð²Ð°Ñ Ñ‰Ðµ немає підпиÑок"
@@ -47269,6 +47955,9 @@ msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” недавніх пошуків"
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -47333,6 +48022,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -47384,9 +48076,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr "Ви уÑпішно придбали тарифний план %{plan} Ð´Ð»Ñ %{seats}. Ви отримаєте чек на електронну пошту. Ваша покупка може зайнÑти хвилину Ð´Ð»Ñ Ñинхронізації, тому оновіть Ñторінку, Ñкщо ви ще цього не бачили."
-
msgid "You have unsaved changes"
msgstr ""
@@ -47564,6 +48253,9 @@ msgstr "Ви уже увімкнули двофакторну автентифі
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -47582,7 +48274,7 @@ msgstr "Ваша підпиÑка на %{strong}%{plan_name}%{strong_close} за
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47618,6 +48310,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr "Ваша група GitLab"
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr "Ваші групи"
@@ -47763,13 +48458,6 @@ msgstr[1] "Ваша безкоштовна група тепер обмеженÐ
msgstr[2] "Ваша безкоштовна група тепер обмежена %d учаÑниками"
msgstr[3] "Ваша безкоштовна група тепер обмежена %d учаÑниками"
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "Your groups"
msgstr "Ваші групи"
@@ -47795,7 +48483,7 @@ msgid "Your license does not support on-call schedules"
msgstr ""
msgid "Your license is valid from"
-msgstr ""
+msgstr "Ваша Ð»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñ Ð´Ñ–Ð¹Ñна з"
msgid "Your membership in %{group} no longer expires."
msgstr ""
@@ -47809,6 +48497,15 @@ msgstr "Ваше ім'Ñ"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47969,6 +48666,9 @@ msgstr "[REDACTED]"
msgid "[Redacted]"
msgstr "[Redacted]"
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -48152,6 +48852,9 @@ msgstr "не можна аÑоціювати з підгрупою"
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -48207,6 +48910,20 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr "%{criticalStart}критичний%{criticalEnd}, %{highStart}виÑокий%{highEnd} та %{otherStart}інші%{otherEnd}"
@@ -48264,10 +48981,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -48432,7 +49152,7 @@ msgstr "Виправлено:"
msgid "ciReport|Found %{issuesWithCount}"
msgstr "Знайдено %{issuesWithCount}"
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr "Повний звіт"
msgid "ciReport|Generic Report"
@@ -48614,6 +49334,13 @@ msgstr "прокоментовано"
msgid "commented on %{link_to_project}"
msgstr "прокоментовано в %{link_to_project}"
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "commit %{commit_id}"
msgstr "коміт %{commit_id}"
@@ -48775,9 +49502,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr "перевищує макÑимальну довжину (100 кориÑтувачів)"
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48968,6 +49692,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr "вже пов'Ñзана з цією вразливіÑÑ‚ÑŽ"
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr "Ñ” недопуÑтимим діапазоном IP-адреÑ"
@@ -49007,6 +49734,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -49510,6 +50243,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr "повинна бути пізніша за дату початку"
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -49519,12 +50255,12 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
-msgstr "має бути проÑÑ‚Ñ–Ñ€ імен верхнього рівнÑ"
-
msgid "must be unique by status and elapsed time within a policy"
msgstr ""
+msgid "must belong to same project of its requirement object."
+msgstr ""
+
msgid "must belong to same project of the work item."
msgstr ""
@@ -49598,7 +50334,7 @@ msgid "only %{parent_types} can be parent of Task."
msgstr "лише %{parent_types} може бути батьківÑьким завданнÑм."
msgid "only Task can be assigned as a child in hierarchy."
-msgstr ""
+msgstr "тільки ÐŸÑ–Ð´Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð¼Ð¾Ð¶Ðµ бути призначене Ñк дочірнє в ієрархії."
msgid "only available on top-level groups."
msgstr ""
@@ -49732,6 +50468,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49791,6 +50530,13 @@ msgstr "репозиторій:"
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "running"
msgstr ""
diff --git a/locale/uk/gitlab.po.time_stamp b/locale/uk/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/uk/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/ur_PK/gitlab.po b/locale/ur_PK/gitlab.po
index b7ae00cd0e7..0c2967eb1a5 100644
--- a/locale/ur_PK/gitlab.po
+++ b/locale/ur_PK/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: ur-PK\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:21\n"
+"PO-Revision-Date: 2022-12-10 06:41\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/uz_UZ/gitlab.po b/locale/uz_UZ/gitlab.po
index f236b145e88..b3a62cc0b06 100644
--- a/locale/uz_UZ/gitlab.po
+++ b/locale/uz_UZ/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: uz\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:24\n"
+"PO-Revision-Date: 2022-12-10 06:43\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -506,6 +511,12 @@ msgstr[1] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -706,13 +717,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -906,9 +917,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -959,7 +967,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1108,6 +1116,11 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1147,6 +1160,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1266,12 +1282,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2364,6 +2386,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2451,6 +2476,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2646,6 +2674,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2946,7 +2977,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -3009,9 +3040,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4538,6 +4566,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4655,9 +4686,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5196,6 +5224,9 @@ msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
msgstr[1] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5604,6 +5635,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5649,6 +5683,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5880,9 +5917,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6902,6 +6936,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -7016,9 +7059,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -7028,6 +7068,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -7049,7 +7092,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -7070,9 +7116,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -7094,6 +7137,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -7127,6 +7173,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -7139,6 +7191,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7193,6 +7248,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7999,6 +8057,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -8047,6 +8108,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8168,9 +8232,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8186,7 +8247,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8210,6 +8271,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8453,9 +8517,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8468,6 +8538,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8480,6 +8553,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8489,6 +8565,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8498,6 +8577,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9189,6 +9274,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -10010,6 +10098,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -10034,6 +10125,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -10049,15 +10143,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10223,7 +10326,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10343,6 +10446,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10382,12 +10488,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10652,9 +10752,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10838,9 +10935,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -11153,6 +11247,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11285,9 +11382,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11366,6 +11460,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11402,6 +11499,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11627,6 +11760,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -12037,12 +12173,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -12055,9 +12197,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -12076,6 +12230,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -12109,6 +12266,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12169,6 +12329,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12316,6 +12479,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12687,6 +12853,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12744,6 +12913,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13536,6 +13711,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13572,6 +13750,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13676,9 +13857,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13843,6 +14030,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -15156,6 +15346,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15519,9 +15712,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15615,6 +15805,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15930,6 +16123,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -16091,6 +16293,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -16121,9 +16326,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -16142,9 +16359,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16199,13 +16413,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16521,9 +16738,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16680,9 +16894,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16937,6 +17148,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -17156,15 +17370,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17201,6 +17406,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17345,10 +17553,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17428,11 +17645,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17475,6 +17687,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18322,6 +18537,11 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18571,6 +18791,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18598,6 +18821,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18775,6 +19001,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18868,6 +19097,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -19057,7 +19292,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -19144,6 +19379,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19249,6 +19487,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19420,7 +19661,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19441,9 +19682,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19573,6 +19811,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19582,9 +19823,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -20058,12 +20296,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -20097,9 +20341,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20268,6 +20509,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20292,6 +20536,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20331,7 +20578,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20373,6 +20620,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20385,9 +20638,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20733,6 +20995,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21300,12 +21565,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21624,6 +21895,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21896,6 +22170,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21929,6 +22206,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22277,6 +22557,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22325,7 +22608,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22792,9 +23075,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22933,9 +23213,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23368,9 +23645,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23521,6 +23795,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23602,6 +23879,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23671,9 +23951,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23748,15 +24025,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23843,6 +24123,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23948,6 +24231,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24623,10 +24909,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24677,6 +24963,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24782,6 +25071,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25280,6 +25572,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25301,6 +25596,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26428,6 +26729,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26491,9 +26795,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26703,6 +27004,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26882,6 +27186,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26924,6 +27231,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -27197,9 +27510,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27299,6 +27609,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27957,9 +28270,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28340,6 +28668,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28417,9 +28748,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28525,9 +28853,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28813,6 +29138,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28902,6 +29230,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28917,6 +29248,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28941,6 +29278,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -29013,6 +29362,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -29028,6 +29380,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -29036,6 +29391,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29090,6 +29448,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29138,9 +29499,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -29161,6 +29519,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -29176,6 +29539,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29245,6 +29611,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29308,6 +29677,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29551,6 +29929,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30466,6 +30868,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30622,9 +31027,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30778,6 +31180,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30862,6 +31267,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30874,9 +31282,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30994,16 +31408,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31366,6 +31789,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31900,12 +32329,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31987,9 +32422,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -32104,6 +32536,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -32149,6 +32584,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32278,7 +32716,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32359,6 +32797,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32881,9 +33322,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33555,6 +33993,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
msgstr[1] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33752,6 +34193,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -34091,6 +34535,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -34145,10 +34592,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34487,6 +34931,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34734,6 +35181,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34808,7 +35258,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34903,6 +35353,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34963,7 +35416,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34990,6 +35443,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -35011,6 +35467,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -35094,6 +35553,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35112,6 +35574,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -35151,12 +35616,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35249,6 +35717,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35315,6 +35786,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35363,12 +35837,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35482,6 +35950,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35509,13 +35980,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35557,6 +36031,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35572,6 +36049,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35638,6 +36124,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35665,9 +36157,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35680,18 +36178,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35776,6 +36283,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36293,6 +36803,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36350,9 +36863,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36368,6 +36890,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36404,6 +36929,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36476,6 +37004,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36485,12 +37016,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36560,6 +37100,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36587,6 +37130,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36623,6 +37169,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36632,9 +37181,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36779,6 +37343,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -37004,6 +37571,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -37109,6 +37679,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37597,6 +38170,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37723,6 +38299,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37881,9 +38460,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -38037,7 +38613,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -38052,13 +38628,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -38082,13 +38661,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -38226,10 +38808,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38271,6 +38853,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38289,6 +38874,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38358,6 +38946,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38751,6 +39342,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38856,6 +39450,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38919,6 +39516,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39656,6 +40256,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39746,9 +40349,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -40031,6 +40631,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40611,6 +41217,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40975,9 +41584,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -41035,9 +41641,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -41083,6 +41686,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -41203,6 +41809,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -41242,6 +41851,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41335,9 +41947,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41527,6 +42136,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41545,7 +42157,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -42019,6 +42631,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42269,6 +42887,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42302,9 +42923,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42419,6 +43037,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42512,6 +43133,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42545,6 +43169,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42560,13 +43187,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42581,22 +43208,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42967,6 +43594,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -43003,6 +43636,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -43180,6 +43816,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43303,6 +43945,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43384,6 +44029,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43567,6 +44215,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43585,18 +44236,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43624,9 +44269,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43636,9 +44278,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43765,10 +44404,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43777,12 +44413,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43795,9 +44425,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43831,7 +44458,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43840,16 +44467,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -44023,6 +44650,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44289,9 +44919,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44481,6 +45108,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44553,6 +45183,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44571,6 +45204,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44619,10 +45255,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44634,7 +45270,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44691,6 +45327,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44700,6 +45351,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45360,6 +46023,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45435,9 +46101,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45453,24 +46116,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45501,6 +46152,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45567,9 +46230,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45618,13 +46278,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
-msgstr ""
-
-msgid "Webhooks|Push events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45663,9 +46320,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -46057,6 +46711,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -46066,9 +46723,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -46087,6 +46741,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -46102,9 +46762,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -46125,6 +46782,9 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -46134,6 +46794,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -46146,16 +46812,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -46164,12 +46833,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -46179,12 +46854,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -46197,9 +46878,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46263,7 +46953,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46293,6 +46983,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46460,10 +47153,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46502,11 +47195,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46573,9 +47261,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46594,18 +47279,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46687,6 +47366,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46741,6 +47423,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46803,6 +47488,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46854,9 +47542,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -47034,6 +47719,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -47052,7 +47740,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -47088,6 +47776,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -47231,11 +47922,6 @@ msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
msgstr[1] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Your groups"
msgstr ""
@@ -47275,6 +47961,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47433,6 +48128,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47610,6 +48308,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47663,6 +48364,16 @@ msgid_plural "changes"
msgstr[0] ""
msgstr[1] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47720,10 +48431,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47882,7 +48596,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -48060,6 +48774,11 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -48217,9 +48936,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48402,6 +49118,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48441,6 +49160,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48934,6 +49659,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48943,10 +49671,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -49150,6 +49878,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -49205,6 +49936,11 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "running"
msgstr ""
diff --git a/locale/vi_VN/gitlab.po b/locale/vi_VN/gitlab.po
index 6f0d31d5c05..17923ed6614 100644
--- a/locale/vi_VN/gitlab.po
+++ b/locale/vi_VN/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: vi\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:20\n"
+"PO-Revision-Date: 2022-12-10 06:41\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -289,6 +289,10 @@ msgid "%d more comment"
msgid_plural "%d more comments"
msgstr[0] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -426,6 +430,12 @@ msgstr[0] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -616,13 +626,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -815,9 +825,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -867,7 +874,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1003,6 +1010,10 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1042,6 +1053,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1160,12 +1174,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2231,6 +2251,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2318,6 +2341,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr ""
@@ -2513,6 +2539,9 @@ msgstr ""
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2813,7 +2842,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -2876,9 +2905,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4404,6 +4430,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4521,9 +4550,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5054,6 +5080,9 @@ msgid "Are you sure you want to import %d repository?"
msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5459,6 +5488,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5504,6 +5536,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -5735,9 +5770,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6753,6 +6785,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -6867,9 +6908,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr ""
@@ -6879,6 +6917,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr ""
@@ -6900,7 +6941,10 @@ msgstr ""
msgid "Branches|Compare"
msgstr ""
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
msgstr ""
msgid "Branches|Delete branch"
@@ -6921,9 +6965,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -6945,6 +6986,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -6978,6 +7022,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -6990,6 +7040,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7044,6 +7097,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7849,6 +7905,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -7897,6 +7956,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8016,9 +8078,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8034,7 +8093,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8058,6 +8117,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8301,9 +8363,15 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8316,6 +8384,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8328,6 +8399,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8337,6 +8411,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8346,6 +8423,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9035,6 +9118,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -9855,6 +9941,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -9879,6 +9968,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -9894,15 +9986,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10068,7 +10169,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10188,6 +10289,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10227,12 +10331,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10494,9 +10592,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10680,9 +10775,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -10995,6 +11087,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11127,9 +11222,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11208,6 +11300,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11244,6 +11339,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11469,6 +11600,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -11878,12 +12012,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -11896,9 +12036,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -11917,6 +12069,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -11950,6 +12105,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12010,6 +12168,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12157,6 +12318,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12527,6 +12691,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12584,6 +12751,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13370,6 +13543,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13406,6 +13582,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13509,9 +13688,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13675,6 +13860,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -14984,6 +15172,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15347,9 +15538,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15443,6 +15631,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15758,6 +15949,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -15918,6 +16118,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -15948,9 +16151,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -15969,9 +16184,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16026,13 +16238,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16346,9 +16561,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16505,9 +16717,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16761,6 +16970,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -16980,15 +17192,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17025,6 +17228,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17169,10 +17375,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17251,10 +17466,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -17297,6 +17508,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -18142,6 +18356,10 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18391,6 +18609,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18418,6 +18639,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18595,6 +18819,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18688,6 +18915,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -18877,7 +19110,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -18964,6 +19197,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19069,6 +19305,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19240,7 +19479,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19261,9 +19500,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19393,6 +19629,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19402,9 +19641,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -19874,12 +20110,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -19913,9 +20155,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20084,6 +20323,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20108,6 +20350,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20147,7 +20392,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20189,6 +20434,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20201,9 +20452,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20546,6 +20806,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21113,12 +21376,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21437,6 +21706,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21708,6 +21980,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21741,6 +22016,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22089,6 +22367,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22137,7 +22418,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22603,9 +22884,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22744,9 +23022,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23179,9 +23454,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23332,6 +23604,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23413,6 +23688,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23482,9 +23760,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23558,15 +23833,18 @@ msgstr ""
msgid "Labels"
msgstr ""
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests to categorize them."
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23652,6 +23930,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23757,6 +24038,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24426,10 +24710,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24480,6 +24764,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24585,6 +24872,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25083,6 +25373,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25104,6 +25397,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26227,6 +26526,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26290,9 +26592,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26501,6 +26800,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26679,6 +26981,9 @@ msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26721,6 +27026,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -26994,9 +27305,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27096,6 +27404,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27749,9 +28060,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28131,6 +28457,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28207,9 +28536,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28315,9 +28641,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28603,6 +28926,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28691,6 +29017,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28706,6 +29035,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28730,6 +29065,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -28802,6 +29149,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -28817,6 +29167,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28824,6 +29177,9 @@ msgstr[0] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -28878,6 +29234,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -28926,9 +29285,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -28948,6 +29304,10 @@ msgid "PackageRegistry|You are about to delete 1 asset. This operation is irreve
msgid_plural "PackageRegistry|You are about to delete %d assets. This operation is irreversible."
msgstr[0] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -28963,6 +29323,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29032,6 +29395,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29095,6 +29461,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29338,6 +29713,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30253,6 +30652,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30409,9 +30811,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30565,6 +30964,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30649,6 +31051,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30661,9 +31066,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30781,16 +31192,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31153,6 +31573,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31687,12 +32113,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31774,9 +32206,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -31891,6 +32320,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -31936,6 +32368,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32065,7 +32500,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32146,6 +32581,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32668,9 +33106,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33341,6 +33776,9 @@ msgid "Refreshing in a second to show the updated status..."
msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33537,6 +33975,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -33876,6 +34317,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -33930,10 +34374,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34266,6 +34707,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34511,6 +34955,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34584,7 +35031,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34678,6 +35125,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34735,7 +35185,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34762,6 +35212,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -34783,6 +35236,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -34865,6 +35321,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -34883,6 +35342,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -34922,12 +35384,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35019,6 +35484,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35085,6 +35553,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35133,12 +35604,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35251,6 +35716,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35278,13 +35746,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35326,6 +35797,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35341,6 +35815,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35407,6 +35890,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35434,9 +35923,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35449,18 +35944,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35545,6 +36049,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36051,6 +36558,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36108,9 +36618,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36126,6 +36645,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36162,6 +36684,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36234,6 +36759,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36243,12 +36771,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36318,6 +36855,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36345,6 +36885,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36381,6 +36924,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36390,9 +36936,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36537,6 +37098,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -36762,6 +37326,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -36867,6 +37434,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37354,6 +37924,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37480,6 +38053,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37637,9 +38213,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -37793,7 +38366,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -37808,13 +38381,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -37838,13 +38414,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -37982,10 +38561,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38027,6 +38606,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38045,6 +38627,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38114,6 +38699,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38507,6 +39095,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38612,6 +39203,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38675,6 +39269,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39411,6 +40008,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39501,9 +40101,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -39786,6 +40383,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr ""
@@ -40361,6 +40964,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40723,9 +41329,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -40783,9 +41386,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -40831,6 +41431,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -40951,6 +41554,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -40990,6 +41596,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41083,9 +41692,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41275,6 +41881,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41293,7 +41902,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -41767,6 +42376,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42015,6 +42630,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42048,9 +42666,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42165,6 +42780,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42258,6 +42876,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42291,6 +42912,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42306,13 +42930,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42327,22 +42951,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42712,6 +43336,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -42748,6 +43378,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -42925,6 +43558,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43048,6 +43687,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43129,6 +43771,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr ""
@@ -43312,6 +43957,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43330,18 +43978,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43369,9 +44011,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43381,9 +44020,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43510,10 +44146,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43522,12 +44155,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43540,9 +44167,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43576,7 +44200,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43585,16 +44209,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -43768,6 +44392,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44033,9 +44660,6 @@ msgstr ""
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44225,6 +44849,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44297,6 +44924,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44315,6 +44945,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44363,10 +44996,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44378,7 +45011,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44435,6 +45068,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44444,6 +45092,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45101,6 +45761,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45176,9 +45839,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45194,24 +45854,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45242,6 +45890,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45308,9 +45968,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45359,13 +46016,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push events"
-msgstr ""
-
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45404,9 +46058,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -45796,6 +46447,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -45805,9 +46459,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr ""
@@ -45826,6 +46477,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -45841,9 +46498,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -45863,6 +46517,9 @@ msgstr[0] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -45872,6 +46529,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -45884,16 +46547,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -45902,12 +46568,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -45917,12 +46589,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -45935,9 +46613,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46001,7 +46688,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46031,6 +46718,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46197,10 +46887,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46239,10 +46929,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46309,9 +46995,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46330,18 +47013,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46423,6 +47100,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46477,6 +47157,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46538,6 +47221,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46589,9 +47275,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -46769,6 +47452,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -46787,7 +47473,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -46823,6 +47509,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -46965,10 +47654,6 @@ msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-
msgid "Your groups"
msgstr ""
@@ -47008,6 +47693,15 @@ msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47165,6 +47859,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47339,6 +48036,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47391,6 +48091,14 @@ msgid "change"
msgid_plural "changes"
msgstr[0] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47448,10 +48156,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47607,7 +48318,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -47783,6 +48494,10 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -47938,9 +48653,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48119,6 +48831,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48158,6 +48873,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48646,6 +49367,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48655,10 +49379,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -48859,6 +49583,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -48912,6 +49639,10 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+
msgid "running"
msgstr ""
diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po
index 3edcd3fe207..c805cd23694 100644
--- a/locale/zh_CN/gitlab.po
+++ b/locale/zh_CN/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: zh-CN\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:21\n"
+"PO-Revision-Date: 2022-12-10 06:41\n"
msgid " %{start} to %{end}"
msgstr "从%{start}到%{end}"
@@ -289,6 +289,10 @@ msgid "%d more comment"
msgid_plural "%d more comments"
msgstr[0] "%d个更多评论"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%dæ¡å¾…处ç†çš„评论"
@@ -426,6 +430,12 @@ msgstr[0] "%{bold_start}%{count}%{bold_end} 个开放中的åˆå¹¶è¯·æ±‚"
msgid "%{chartTitle} no data series"
msgstr "%{chartTitle} 没有数æ®ï¼"
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr "%{code_open}éšè—:%{code_close} éšè—在作业日志中。必须符åˆéšè—è¦æ±‚。"
@@ -616,15 +626,15 @@ msgstr "%{group_name}使用由群组托管å¸æˆ·ã€‚您需è¦åˆ›å»ºä¸€ä¸ªæ–°çš„Gi
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr "%{group_name}&%{epic_iid} &middot; %{epic_created}由%{author}创建"
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type}已删除"
-
-msgid "%{hook_type} was scheduled for deletion"
-msgstr "%{hook_type}已安排删除"
-
msgid "%{host} sign-in from new location"
msgstr "%{host} 从新ä½ç½®ç™»å½•"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
+msgstr ""
+
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr ""
+
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr "%{integrations_link_start}集æˆ%{link_end}使得将第三方应用程åºæˆä¸ºGitLab工作æµç¨‹çš„一部分。如果当å‰å¯ç”¨çš„集æˆä¸èƒ½æ»¡è¶³æ‚¨çš„需求,å¯ä»¥è€ƒè™‘使用 %{webhooks_link_start}webhook%{link_end}。"
@@ -815,9 +825,6 @@ msgstr "%{openedEpics}个开å¯ä¸­ï¼Œ %{closedEpics}个已关闭"
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr "%{openedIssues}个开å¯ä¸­ï¼Œ %{closedIssues}个已关闭"
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr "%{over_limit_message} è¦èŽ·å¾—更多æˆå‘˜ï¼Œç¾¤ç»„的所有者å¯ä»¥å¼€å§‹è¯•ç”¨æˆ–å‡çº§åˆ°ä»˜è´¹ç­‰çº§ã€‚"
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr "%{over_limit_message} è¦èŽ·å¾—更多席ä½ï¼Œè¯·%{link_start}å‡çº§åˆ°ä»˜è´¹ç‰ˆ%{link_end}。"
@@ -867,8 +874,8 @@ msgstr "%{reportType} %{status}"
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr "%{reportType}检测到%{totalStart}%{total}%{totalEnd}个潜在的%{vulnMessage}"
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
-msgstr "%{reportType}未检测到%{totalStart}æ–°çš„%{totalEnd}安全æ¼æ´žã€‚"
+msgid "%{reportType} detected no new vulnerabilities."
+msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
msgstr "%{retryButtonStart}é‡è¯•%{retryButtonEnd}或%{newFileButtonStart}添加新文件%{newFileButtonEnd}。"
@@ -1003,6 +1010,10 @@ msgstr "%{template_project_id}为未知或无效"
msgid "%{text} is available"
msgstr "%{text}å¯ç”¨"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr "%{timebox_type}ä¸æ”¯æŒç‡ƒçƒ§å›¾"
@@ -1042,6 +1053,9 @@ msgstr "%{type} 必须是一个%{help_link}"
msgid "%{type} only supports %{name} name"
msgstr "%{type} åªæ”¯æŒ %{name} å称"
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr "%{userName} (æ— æƒåˆå¹¶)"
@@ -1160,12 +1174,18 @@ msgstr "(+%{count}&nbsp;æ¡è§„则)"
msgid "(Group Managed Account)"
msgstr "(由群组管ç†çš„å¸æˆ·)"
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr "(æ— å˜æ›´å†…容)"
msgid "(UTC %{offset}) %{timezone}"
msgstr "(UTC %{offset}) %{timezone}"
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr "(检查进度)"
@@ -2231,6 +2251,9 @@ msgstr "添加系统钩å­"
msgid "Add text to the sign-in page. Markdown enabled."
msgstr "将文本添加到登录页é¢ã€‚Markdownå·²å¯ç”¨ã€‚"
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr "添加到é¢æ¿"
@@ -2318,6 +2341,9 @@ msgstr "此版本新增"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "当å‰GitLab实例ç¦æ­¢æ·»åŠ æ–°åº”用程åºã€‚请è”系您的GitLab管ç†å‘˜ä»¥èŽ·å¾—相关æƒé™ã€‚"
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "更多的分钟数"
@@ -2513,6 +2539,9 @@ msgstr "åœæ­¢ä½œä¸šå¤±è´¥"
msgid "AdminArea|Total users"
msgstr "所有用户"
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr "用户"
@@ -2604,16 +2633,16 @@ msgid "AdminSettings|Configure limits on the number of repositories users can do
msgstr "é…置用户在给定时间内å¯ä»¥ä¸‹è½½çš„仓库数é‡é™åˆ¶ã€‚"
msgid "AdminSettings|Configure product analytics to track events within your project applications."
-msgstr ""
+msgstr "设定生产力分æžï¼Œè¿½è¸ªé¡¹ç›®åº”用程åºå†…的事件"
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr "é…置何时自动删除ä¸æ´»è·ƒçš„项目。%{linkStart}什么是ä¸æ´»è·ƒçš„项目?%{linkEnd}"
msgid "AdminSettings|Cube API URL"
-msgstr ""
+msgstr "Cube API URL"
msgid "AdminSettings|Cube API key"
-msgstr ""
+msgstr "Cube API 密钥"
msgid "AdminSettings|Delete inactive projects"
msgstr "删除ä¸æ´»è·ƒçš„项目"
@@ -2813,8 +2842,8 @@ msgstr "å¯ç”¨ Elasticsearch çš„æœç´¢"
msgid "AdminSettings|Select a CI/CD template"
msgstr "选择一个 CI/CD 模æ¿"
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
-msgstr "选择一个群组作为实例级项目模æ¿çš„æ¥æºã€‚"
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
+msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
msgstr "选择ç¦ç”¨Pages站点的公开访问æƒé™ï¼Œè¿™éœ€è¦ç”¨æˆ·ç™»å½•æ‰èƒ½è®¿é—®æ‚¨çš„Pages站点。 %{link_start}了解更多。%{link_end}"
@@ -2865,20 +2894,17 @@ msgid "AdminSettings|The ID of the project in Jitsu. The project contains all an
msgstr "Jitsu中的项目ID,此项目包å«æ‰€æœ‰åˆ†æžå®žä¾‹ã€‚"
msgid "AdminSettings|The URL of your Cube instance."
-msgstr ""
+msgstr "Cube 实例的 URL"
msgid "AdminSettings|The default domain to use for Auto Review Apps and Auto Deploy stages in all projects."
msgstr "在所有项目中,自动评审应用和自动部署阶段使用的默认域å。"
msgid "AdminSettings|The host of your Jitsu instance."
-msgstr ""
+msgstr "您的 Jitsu 实例主机"
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr "存储了æ¯ä¸ªé¡¹ç›®ä¸­æœ€è¿‘æˆåŠŸå®Œæˆçš„æµæ°´çº¿ä¸­æ‰€æœ‰ä½œä¸šçš„最新产物,并且ä¸ä¼šè¿‡æœŸã€‚"
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr "å¯ä»¥é€‰æ‹©è¯¥ç¾¤ç»„中的项目作为在实例中创建新项目的模æ¿ã€‚ %{link_start}了解更多。%{link_end} "
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr "所需æµæ°´çº¿é…置的模æ¿å¯ä»¥æ˜¯GitLabæ供的模æ¿ä¹‹ä¸€ï¼Œä¹Ÿå¯ä»¥æ˜¯æ·»åŠ åˆ°å®žä¾‹æ¨¡æ¿ä»“库的自定义模æ¿ã€‚%{link_start}如何创建实例模æ¿ä»“库?%{link_end}"
@@ -2898,13 +2924,13 @@ msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
msgstr "以 IMA 凭æ®ä½¿ç”¨ AWS çš„ OpenSearch æœåŠ¡"
msgid "AdminSettings|Used to connect Jitsu to the Clickhouse instance."
-msgstr ""
+msgstr "用于连接 Jitsu 到 Clickhouse 实例。"
msgid "AdminSettings|Used to generate short-lived API access tokens."
-msgstr ""
+msgstr "用于生æˆçŸ­æœŸçš„ API 访问令牌。"
msgid "AdminSettings|Used to retrieve dashboard data from the Cube instance."
-msgstr ""
+msgstr "用于从 Cube 实例中获得仪表盘数æ®ã€‚"
msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr "用户和群组在被添加到群组或项目之å‰å¿…须确认接å—邀请。"
@@ -3009,7 +3035,7 @@ msgid "AdminUsers|Admins"
msgstr "管ç†å‘˜"
msgid "AdminUsers|An error occurred while fetching this user's contributions, and the request cannot return the number of issues, merge requests, groups, and projects linked to this user. If you proceed with deleting the user, all their contributions will still be deleted."
-msgstr ""
+msgstr "获å–此用户的贡献数æ®æ—¶å‡ºé”™ï¼Œè¯·æ±‚无法返回和该用户相关的议题ã€åˆå¹¶è¯·æ±‚ã€ç¾¤ç»„和项目的数é‡ã€‚如果您继续删除该用户,他们的所有贡献数æ®ä»å°†è¢«åˆ é™¤ã€‚"
msgid "AdminUsers|Approve"
msgstr "批准"
@@ -3312,7 +3338,7 @@ msgid "AdminUsers|You are about to permanently delete the user %{username}. Issu
msgstr "您å³å°†æ°¸ä¹…删除用户 %{username}。该用户的议题ã€åˆå¹¶è¯·æ±‚以åŠç›¸å…³çš„群组将被转移到系统的“Ghost用户â€ã€‚为é¿å…æ•°æ®ä¸¢å¤±ï¼Œå»ºè®®æ‚¨ä½¿ç”¨ %{strongStart}ç¦ç”¨ç”¨æˆ·%{strongEnd} 功能。一旦您 %{strongStart}删除用户%{strongEnd},将无法撤消或æ¢å¤ã€‚"
msgid "AdminUsers|You are about to permanently delete the user %{username}. This will delete all issues, merge requests, groups, and projects linked to them. To avoid data loss, consider using the %{strongStart}Block user%{strongEnd} feature instead. After you %{strongStart}Delete user%{strongEnd}, you cannot undo this action or recover the data."
-msgstr ""
+msgstr "您å³å°†æ°¸ä¹…删除用户 %{username}。此æ“作会删除该用户的所有议题ã€åˆå¹¶è¯·æ±‚以åŠç›¸å…³çš„群组。为é¿å…æ•°æ®ä¸¢å¤±ï¼Œå»ºè®®æ‚¨ä½¿ç”¨ %{strongStart}ç¦ç”¨ç”¨æˆ·%{strongEnd} 功能。一旦您 %{strongStart}删除用户%{strongEnd},将无法撤消此æ“作或æ¢å¤æ•°æ®ã€‚"
msgid "AdminUsers|You can always block their account again if needed."
msgstr "如果需è¦ï¼Œæ‚¨éšæ—¶å¯ä»¥ç¦ç”¨ä»–们的å¸æˆ·ã€‚"
@@ -3360,7 +3386,7 @@ msgid "Administrators"
msgstr "管ç†å‘˜"
msgid "Administrators are not permitted to connect applications with these scopes: %{code_open}api%{code_close}, %{code_open}read_api%{code_close}, %{code_open}read_repository%{code_close}, %{code_open}write_repository%{code_close}, %{code_open}write_registry%{code_close}, %{code_open}read_registry%{code_close}, and %{code_open}sudo%{code_close}. To permit this, change the %{code_open}disable_admin_oauth_scopes%{code_close} setting using the API."
-msgstr ""
+msgstr "ä¸å…许系统管ç†å‘˜è¿žæŽ¥å…·æœ‰ä»¥ä¸‹èŒƒå›´çš„应用程åºï¼š%{code_open}api%{code_close}ã€%{code_open}read_api%{code_close}ã€%{code_open}read_repository%{code_close}ã€%{code_open}write_repository%{code_close}ã€%{code_open}write_registry%{code_close}ã€%{code_open}read_registry%{code_close} å’Œ %{code_open}sudo%{code_close}。è¦å…许使用,请利用 API 更改 %{code_open}disable_admin_oauth_scopes%{code_close} 的设定。"
msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
msgstr "新加入的用户必须ç»è¿‡ç³»ç»Ÿç®¡ç†å‘˜å®¡æ ¸ä¸Žæ‰¹å‡†ã€‚了解更多关于%{help_link_start}用户数å°é¡¶%{help_link_end}çš„ä¿¡æ¯ã€‚"
@@ -4404,6 +4430,9 @@ msgstr "出现错误。请é‡æ–°ç™»å½•ã€‚"
msgid "An error occurred. Please try again."
msgstr "å‘生了错误,请å†è¯•ä¸€æ¬¡ã€‚"
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr "一个与GitLab集æˆçš„Kubernetes集群管ç†çš„示例项目。"
@@ -4521,9 +4550,6 @@ msgstr "任何里程碑"
msgid "Any namespace"
msgstr "任何命å空间"
-msgid "Anyone can register for an account."
-msgstr "任何人都å¯ä»¥æ³¨å†Œä¸€ä¸ªå¸æˆ·ã€‚"
-
msgid "App ID"
msgstr "应用ID"
@@ -4647,7 +4673,7 @@ msgid "ApplicationSettings|Minimum password length (number of characters)"
msgstr "最å°å¯†ç é•¿åº¦ï¼ˆå­—符数)"
msgid "ApplicationSettings|New users can sign up without confirming their email address."
-msgstr ""
+msgstr "新用户无需确认其电å­é‚®ä»¶åœ°å€å³å¯æ³¨å†Œã€‚"
msgid "ApplicationSettings|Off"
msgstr "关闭"
@@ -5054,6 +5080,9 @@ msgid "Are you sure you want to import %d repository?"
msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] "您确定è¦å¯¼å…¥%d个仓库å—?"
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr "您确定è¦é”定 %{path} å—?"
@@ -5118,7 +5147,7 @@ msgid "Are you sure you want to revoke this %{accessTokenType}? This action cann
msgstr "您确定è¦æ’¤é”€%{accessTokenType}嘛?此æ“作ä¸èƒ½æ’¤é”€ã€‚"
msgid "Are you sure you want to revoke this group access token? This action cannot be undone."
-msgstr ""
+msgstr "您确定è¦æ’¤é”€è¯¥ç¾¤ç»„的访问令牌å—?此æ“作无法撤消。"
msgid "Are you sure you want to revoke this personal access token? This action cannot be undone."
msgstr "您确定è¦æ’¤æ¶ˆæ­¤ä¸ªäººè®¿é—®ä»¤ç‰Œå—?此æ“作无法撤消。"
@@ -5459,6 +5488,9 @@ msgstr "删除 %{link}"
msgid "AuditStreams|Destination URL"
msgstr "目的地 URL"
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr "目的地接收所有审计事件数æ®"
@@ -5504,6 +5536,9 @@ msgstr "值"
msgid "AuditStreams|Verification token"
msgstr "验è¯ä»¤ç‰Œ"
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "8月"
@@ -5735,9 +5770,6 @@ msgstr "å¯ç”¨çš„群组 Runner:%{runners}"
msgid "Available on-demand"
msgstr "å¯æŒ‰ç”¨æˆ·è¦æ±‚"
-msgid "Available shared runners:"
-msgstr "å¯ç”¨çš„共享 Runner:"
-
msgid "Available specific runners"
msgstr "å¯ç”¨çš„指定Runner"
@@ -5997,10 +6029,10 @@ msgid "Be careful. Renaming a project's repository can have unintended side effe
msgstr "请注æ„,é‡å‘½å项目的仓库å¯èƒ½ä¼šäº§ç”Ÿéžé¢„期的副作用。"
msgid "Because you enabled auto-banning, we have also automatically banned this user from %{scope}. If this is a mistake, you can %{link_start}unban them%{link_end}."
-msgstr ""
+msgstr "因为您å¯ç”¨äº†è‡ªåŠ¨å°ç¦ï¼Œæ‰€ä»¥æˆ‘们åŒæ—¶ä¹Ÿåœ¨ %{scope} 中自动å°ç¦æ­¤ç”¨æˆ·ã€‚如果这是错误的,您å¯ä»¥ %{link_start}å–消å°ç¦ä»–们%{link_end}。"
msgid "Because you enabled auto-banning, we have also automatically banned this user from %{scope}. If this is a mistake, you can unban them: %{url}."
-msgstr ""
+msgstr "因为您å¯ç”¨äº†è‡ªåŠ¨å°ç¦ï¼Œæ‰€ä»¥æˆ‘们åŒæ—¶ä¹Ÿåœ¨ %{scope} 自动ç¦æ­¢æ­¤ç”¨æˆ·ã€‚如果这是错误的,您å¯ä»¥å–消å°ç¦ä»–们: %{url}。"
msgid "Before enabling this integration, create a webhook for the room in Google Chat where you want to receive notifications from this project. %{docs_link}"
msgstr "在å¯ç”¨æ­¤é›†æˆä¹‹å‰ï¼Œè¯·åœ¨ Google Chat 中为您希望从该项目接收通知的房间创建一个 webhook。 %{docs_link}"
@@ -6753,6 +6785,15 @@ msgstr "%{linkStart}支æŒé€šé…符%{linkEnd} ,例如 *-stable 或 production/
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr "%{linkStart}支æŒé€šé…符%{linkEnd} ,例如 *-stable 或 production/*。"
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr "所有分支"
@@ -6784,7 +6825,7 @@ msgid "BranchRules|Approvals"
msgstr "批准"
msgid "BranchRules|Approvals to ensure separation of duties for new merge requests. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
+msgstr "批准以确ä¿æ–°åˆå¹¶è¯·æ±‚的任务分离。 %{linkStart}了解更多。%{linkEnd}"
msgid "BranchRules|Branch"
msgstr "分支"
@@ -6796,7 +6837,7 @@ msgid "BranchRules|Branch rules details"
msgstr "分支规则详情"
msgid "BranchRules|Check for a status response in merge requests. Failures do not block merges. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
+msgstr "检查åˆå¹¶è¯·æ±‚中的状æ€å“应,失败ä¸ä¼šé˜»æ­¢åˆå¹¶ã€‚%{linkStart} 了解更多信æ¯ã€‚%{linkEnd}"
msgid "BranchRules|Create wildcard: %{searchTerm}"
msgstr "创建通é…符:%{searchTerm}"
@@ -6867,9 +6908,6 @@ msgstr "用户"
msgid "BranchRules|default"
msgstr "默认"
-msgid "BranchRules|protected"
-msgstr "å—ä¿æŠ¤çš„"
-
msgid "Branches"
msgstr "分支"
@@ -6879,6 +6917,9 @@ msgstr "分支:%{source_branch} 到 %{target_branch}"
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr "分支:%{source_branch} → %{target_branch}"
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr "活跃"
@@ -6900,8 +6941,11 @@ msgstr "ä¸èƒ½æ‰¾åˆ°è¿™ä¸ªåˆ†æ”¯çš„ HEAD æ交"
msgid "Branches|Compare"
msgstr "比较"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "删除所有已åˆå¹¶åˆ° %{default_branch} 的分支。"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
+msgstr ""
msgid "Branches|Delete branch"
msgstr "删除分支"
@@ -6921,9 +6965,6 @@ msgstr "删除å—ä¿æŠ¤çš„分支。确定继续å—?"
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr "删除 %{strongStart}%{branchName}%{strongEnd} 分支æ“作无法撤销。确定继续å—?"
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "删除已åˆå¹¶çš„分支åŽå°†æ— æ³•æ¢å¤ï¼Œç¡®å®šç»§ç»­å—?"
-
msgid "Branches|Filter by branch name"
msgstr "按分支å称筛选"
@@ -6945,6 +6986,9 @@ msgstr "概览"
msgid "Branches|Please type the following to confirm:"
msgstr "请输入以下内容以确认:"
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr "查看活跃分支"
@@ -6978,6 +7022,12 @@ msgstr "无法删除默认分支"
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr "此分支尚未åˆå¹¶åˆ° %{defaultBranchName} 中。为é¿å…æ•°æ®ä¸¢å¤±ï¼Œè¯·è€ƒè™‘在删除之å‰åˆå¹¶æ­¤åˆ†æ”¯ã€‚"
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr "è¦æ”¾å¼ƒæœ¬åœ°æ›´æ”¹å¹¶è¦†ç›–上游版本的分支,请在此处将其删除,然åŽé€‰æ‹©ä¸Šé¢çš„“立å³æ›´æ–°â€ã€‚"
@@ -6990,6 +7040,9 @@ msgstr "是的,删除分支"
msgid "Branches|Yes, delete protected branch"
msgstr "是的,删除å—ä¿æŠ¤çš„分支"
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr "您将è¦æ°¸ä¹…删除分支 %{branchName}。"
@@ -7044,6 +7097,9 @@ msgstr "获å–产物时出错"
msgid "BuildArtifacts|Loading artifacts"
msgstr "加载产物中"
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr "内置"
@@ -7733,7 +7789,7 @@ msgid "ChangeReviewer|Unassigned"
msgstr "未分é…"
msgid "ChangeTypeAction|Cherry-pick"
-msgstr "优选"
+msgstr "拣选"
msgid "ChangeTypeAction|GitLab will create a branch in your fork and start a merge request."
msgstr "GitLab 将在您的派生项目中创建分支并开始åˆå¹¶è¯·æ±‚。"
@@ -7849,6 +7905,9 @@ msgstr "æµæ°´çº¿#%{pipeline_id}%{humanized_status}耗时%{duration}"
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr "ç”±%{user_combined_name}%{humanized_status}触å‘çš„%{ref_type}%{ref_link}æµæ°´çº¿%{pipeline_link}"
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr "标签"
@@ -7897,6 +7956,9 @@ msgstr "检查您的Dockeré•œåƒæ˜¯å¦å­˜åœ¨å·²çŸ¥æ¼æ´ž."
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr "检查您的 Kubernetes 集群镜åƒæ˜¯å¦å­˜åœ¨å·²çŸ¥æ¼æ´žã€‚"
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "正在检查%{text}çš„å¯ç”¨æ€§..."
@@ -8016,9 +8078,6 @@ msgstr "信用å¡è¡¨å•åŠ è½½å¤±è´¥ï¼š %{message}"
msgid "Checkout|Edit"
msgstr "编辑"
-msgid "Checkout|Enter a number greater than 0"
-msgstr "请输入大于0的数字"
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr "有效期至%{expirationMonth}/%{expirationYear}"
@@ -8034,8 +8093,8 @@ msgstr "加载国家失败。请å†è¯•ä¸€æ¬¡ã€‚"
msgid "Checkout|Failed to load states. Please try again."
msgstr "加载州失败。请é‡è¯•ã€‚"
-msgid "Checkout|Failed to load the payment form. Please try again."
-msgstr "加载付款表å•å¤±è´¥ã€‚请å†è¯•ä¸€æ¬¡ã€‚"
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
+msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
msgstr "注册信用å¡å¤±è´¥ã€‚请å†è¯•ä¸€æ¬¡ã€‚"
@@ -8058,6 +8117,9 @@ msgstr "必须为 %{minimumNumberOfUsers} (使用中的席ä½æ•°ï¼‰æˆ–更多ã€
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr "必须为 %{minimumNumberOfUsers} (使用中的席ä½æ•°ï¼‰æˆ–更多。è¦è´­ä¹°æ›´å°‘的席ä½ï¼Œè¯·ä»Žç¾¤ç»„中移除æˆå‘˜ã€‚"
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr "使用GitLabçš„å…¬å¸æˆ–组织的å称"
@@ -8301,9 +8363,15 @@ msgstr "è¿è¡Œä¸­"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr "当å‰å€¼æ— æ³•ä½¿ç”¨éšè—å˜é‡"
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr "环境"
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr "输入å˜é‡çš„å称"
@@ -8316,6 +8384,9 @@ msgstr "é”®"
msgid "CiVariables|Masked"
msgstr "éšè—"
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr "选项"
@@ -8328,6 +8399,9 @@ msgstr "删除å˜é‡"
msgid "CiVariables|Remove variable row"
msgstr "删除å˜é‡è¡Œ"
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr "范围"
@@ -8337,6 +8411,9 @@ msgstr "指定è¦åœ¨æ­¤è¿è¡Œä¸­ä½¿ç”¨çš„å˜é‡å€¼ã€‚ %{linkStart}CI/CD设置%{l
msgid "CiVariables|State"
msgstr "状æ€"
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr "类型"
@@ -8346,6 +8423,12 @@ msgstr "值"
msgid "CiVariables|Variables"
msgstr "å˜é‡"
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr "* (所有环境)"
@@ -9035,6 +9118,9 @@ msgstr "由 %{userName} 撤销的令牌"
msgid "ClusterAgents|Unknown user"
msgstr "未知用户"
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr "有效的访问令牌"
@@ -9633,13 +9719,13 @@ msgid "Comment/Reply (quoting selected text)"
msgstr "评论/å›žå¤ (引用选定的文本)"
msgid "Commenting on files that are only moved or renamed is not supported"
-msgstr ""
+msgstr "ä¸æ”¯æŒå¯¹å·²ç§»åŠ¨æˆ–é‡å‘½å的文件进行评论"
msgid "Commenting on files that replace or are replaced by symbolic links is not supported"
-msgstr ""
+msgstr "ä¸æ”¯æŒå¯¹æ›¿æ¢æˆ–被符å·é“¾æŽ¥æ›¿æ¢çš„文件进行评论"
msgid "Commenting on symbolic links that replace or are replaced by files is not supported"
-msgstr ""
+msgstr "ä¸æ”¯æŒå¯¹æ›¿æ¢æ–‡ä»¶æˆ–被文件替æ¢çš„符å·é“¾æŽ¥è¿›è¡Œè¯„论"
msgid "Commenting on this line is not supported"
msgstr "ä¸æ”¯æŒè¯„论此行"
@@ -9855,6 +9941,9 @@ msgstr "åˆè§„æµæ°´çº¿é…置(å¯é€‰ï¼‰"
msgid "ComplianceFrameworks|Configuration not found"
msgstr "找ä¸åˆ°é…ç½®"
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr "删除åˆè§„框架%{framework}"
@@ -9879,6 +9968,9 @@ msgstr "获å–åˆè§„框架数æ®æ—¶å‡ºé”™ã€‚请刷新页é¢"
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr "获å–åˆè§„框架数æ®æ—¶å‡ºé”™ã€‚请刷新页é¢æˆ–å°è¯•å…¶å®ƒçš„框架"
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr "已添加的框架将出现在此处。"
@@ -9894,15 +9986,24 @@ msgstr "å称是必需的"
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr "尚未设置åˆè§„框架"
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr "所需格å¼ï¼š%{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}。%{linkStart}什么是åˆè§„æµæ°´çº¿é…置?%{linkEnd}"
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr "无法ä¿å­˜æ­¤åˆè§„性框架。请é‡è¯•"
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr "您å³å°†ä»Žæ‰€æœ‰å½“å‰åº”用的项目中永久删除åˆè§„框架 %{framework} ,这å¯èƒ½ä¼šåˆ é™¤å…¶å®ƒåŠŸèƒ½ã€‚æ­¤æ“作无法撤消。"
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr "添加一个框架到 %{linkStart}%{groupName}%{linkEnd} 并将出现在这里。"
@@ -10068,8 +10169,8 @@ msgstr "为已废弃的 API 请求é…置特定é™åˆ¶ä»¥å–代一般用户和 IP
msgid "Configure the %{link} integration."
msgstr "é…ç½® %{link} 集æˆã€‚"
-msgid "Configure the default first day of the week and time tracking units."
-msgstr "é…置默认的æ¯å‘¨ç¬¬ä¸€å¤©å’Œæ—¶é—´è·Ÿè¸ªå•ä½ã€‚"
+msgid "Configure the default first day of the week, time tracking units, and default language."
+msgstr ""
msgid "Configure the way a user creates a new account."
msgstr "é…置用户创建新å¸æˆ·çš„æ–¹å¼ã€‚"
@@ -10188,6 +10289,9 @@ msgstr "正在连接"
msgid "Connecting to terminal sync service"
msgstr "连接到终端åŒæ­¥æœåŠ¡"
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr "正在连接..."
@@ -10227,12 +10331,6 @@ msgstr "容器镜åƒåº“é•œåƒ"
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr "æ­¤GitLab实例上尚未å¯ç”¨å®¹å™¨é•œåƒåº“。请通知管ç†å‘˜å¯ç”¨ä»¥ä¾¿Auto DevOps能够正常工作。"
-msgid "Container repositories"
-msgstr "容器仓库"
-
-msgid "Container repository"
-msgstr "容器仓库"
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr "请访问%{linkStart}管ç†è®¾ç½®%{linkEnd}以å¯ç”¨æ­¤åŠŸèƒ½ã€‚"
@@ -10494,9 +10592,6 @@ msgstr "标签已æˆåŠŸæ ‡è®°ä¸ºå¾…删除。"
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr "标签已æˆåŠŸæ ‡è®°ä¸ºå¾…删除。"
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr "标签暂时ä¸èƒ½æ ‡è®°ä¸ºåˆ é™¤ã€‚请几分钟åŽå†è¯•ã€‚%{docLinkStart}更多详情%{docLinkEnd}。"
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr "匹é…这些规则的标签会被%{strongStart}ä¿ç•™%{strongEnd},å³ä½¿å®ƒä»¬ä¸Žä¸‹é¢çš„删除规则相匹é…。 %{secondStrongStart}最新的%{secondStrongEnd}标签总是会被ä¿ç•™ã€‚"
@@ -10657,10 +10752,10 @@ msgid "ContributionAnalytics|The given date range is larger than 31 days"
msgstr "给定的日期范围大于 31 天"
msgid "ContributionAnalytics|The to date is earlier than the given from date"
-msgstr ""
+msgstr "结æŸæ—¥æœŸæ—©äºŽç»™å®šçš„开始日期"
msgid "ContributionAnalytics|There is too much data to calculate. Try lowering the period_limit setting in the insights configuration file."
-msgstr ""
+msgstr "æ•°æ®å¤ªå¤šï¼Œæ— æ³•è®¡ç®—。å°è¯•é™ä½Žæ´žå¯Ÿé…置文件中的 period_limit 设置。"
msgid "Contributions for %{calendar_date}"
msgstr "%{calendar_date}的贡献"
@@ -10680,9 +10775,6 @@ msgstr "控制与您å¸æˆ·å…³è”的电å­é‚®ä»¶"
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr "控制 CI_JOB_TOKEN CI/CD å˜é‡å¦‚何用于项目之间的 API 访问。"
-msgid "Control how the GitLab Package Registry functions."
-msgstr "控制 GitLab 软件包仓库是如何è¿ä½œçš„。"
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr "控制是å¦åœ¨ GitLab 中显示客户体验改进内容和第三方优惠。"
@@ -10995,6 +11087,9 @@ msgstr "无法上传您的设计,因为ä¸æ”¯æŒå·²ä¸Šä¼ ä¸€ä¸ªæˆ–多个的文
msgid "Couldn't assign policy to project or group"
msgstr "无法将策略分é…给项目或群组"
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr "国家/地区"
@@ -11127,9 +11222,6 @@ msgstr "创建议题"
msgid "Create issue to resolve all threads"
msgstr "创建议题æ¥è§£å†³æ‰€æœ‰ä¸»é¢˜"
-msgid "Create iteration"
-msgstr "创建迭代"
-
msgid "Create label"
msgstr "创建标记"
@@ -11208,6 +11300,9 @@ msgstr "创建代ç ç‰‡æ®µ"
msgid "Create tag %{tagName}"
msgstr "创建标签 %{tagName}"
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr "创建主题"
@@ -11244,6 +11339,42 @@ msgstr "您无æƒåˆ›å»ºç¾¤ç»„。"
msgid "CreateTag|Tag"
msgstr "标签"
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr "%{name} (默认)"
@@ -11458,7 +11589,7 @@ msgid "CredentialsInventory|Personal Access Tokens"
msgstr "个人访问令牌"
msgid "CredentialsInventory|Project and Group Access Tokens"
-msgstr ""
+msgstr "项目和群组访问令牌"
msgid "CredentialsInventory|SSH Keys"
msgstr "SSH密钥"
@@ -11469,6 +11600,9 @@ msgstr "信用å¡éœ€è¦å­˜æ¡£æ‰èƒ½åˆ›å»ºæµæ°´çº¿"
msgid "Credit card:"
msgstr "信用å¡ï¼š"
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr "存在严é‡æ¼æ´ž"
@@ -11878,12 +12012,18 @@ msgstr "%{startDate} - %{endDate}"
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr "å¹³å‡ï¼ˆæœ€è¿‘%{days}天)"
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr "更改失败率"
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr "å˜æ›´å¤±è´¥çŽ‡ï¼ˆç™¾åˆ†æ¯”)"
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr "%{groupName} 群组的 DORA指标"
@@ -11896,9 +12036,21 @@ msgstr "事件开放的天数"
msgid "DORA4Metrics|Days from merge to deploy"
msgstr "从åˆå¹¶åˆ°éƒ¨ç½²çš„天数"
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr "部署频率"
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr "å˜æ›´çš„å‰ç½®æ—¶é—´"
@@ -11915,6 +12067,9 @@ msgid "DORA4Metrics|Median time an incident was open in a production environment
msgstr "在给定时间段内,事件在生产环境中的中ä½æ—¶é—´ã€‚"
msgid "DORA4Metrics|Month to date"
+msgstr "本月至今"
+
+msgid "DORA4Metrics|New issues"
msgstr ""
msgid "DORA4Metrics|No incidents during this period"
@@ -11950,6 +12105,9 @@ msgstr "该图表显示基于 %{linkStart}deployment_tier%{linkEnd} 值的生产
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 "该图表显示åˆå¹¶è¯·æ±‚被åˆå¹¶å’Œéƒ¨ç½²åˆ°ç”Ÿäº§çŽ¯å¢ƒä¹‹é—´çš„中间值时间,这些时间基于 %{linkStart}deployment_tier%{linkEnd} 值。"
+msgid "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr "æ¢å¤æœåŠ¡çš„时间"
@@ -12010,6 +12168,9 @@ msgstr "未找到此项目以å‰çš„扫æ"
msgid "DastConfig|Not enabled"
msgstr "未å¯ç”¨"
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr "被动扫æ监控å‘é€åˆ°ç›®æ ‡çš„所有HTTP消æ¯(请求和å“应)。主动扫æ会对目标进行攻击以å‘现潜在æ¼æ´žã€‚"
@@ -12157,6 +12318,9 @@ msgstr "æœ€å° = 1秒,最大 = 3600秒"
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr "监控å‘é€åˆ°ç›®æ ‡çš„所有 HTTP 请求æ¥æŸ¥æ‰¾æ½œåœ¨æ¼æ´žã€‚"
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr "新建扫æ工具é…ç½®"
@@ -12432,7 +12596,7 @@ msgid "Data type"
msgstr "æ•°æ®ç±»åž‹"
msgid "Database '%{database_name}' is using PostgreSQL %{pg_version_current}, but PostgreSQL %{pg_version_minimum} is required for this version of GitLab. Please upgrade your environment to a supported PostgreSQL version, see %{pg_requirements_url} for details."
-msgstr ""
+msgstr "æ•°æ®åº““%{database_name}â€æ­£åœ¨ä½¿ç”¨ PostgreSQL %{pg_version_current},但此版本的 GitLab éœ€è¦ PostgreSQL %{pg_version_minimum} 。请将您的环境å‡çº§åˆ°æ”¯æŒçš„ PostgreSQL 版本,详情请å‚è§ %{pg_requirements_url}。"
msgid "Database update failed"
msgstr "æ•°æ®åº“更新失败"
@@ -12527,6 +12691,9 @@ msgstr "åœç”¨å‰çš„ä¸æ´»åŠ¨å¤©æ•°"
msgid "Days to merge"
msgstr "åˆå¹¶æ‰€éœ€å¤©æ•°"
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr "无活动一段时间冻结休眠用户"
@@ -12584,6 +12751,12 @@ msgstr "æ¯å‘¨çš„默认起始日"
msgid "Default first day of the week in calendars and date pickers."
msgstr "在日期选择器中显示æ¯å‘¨é»˜è®¤çš„起始日。"
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr "默认项目é™åˆ¶"
@@ -13370,6 +13543,9 @@ msgstr "于 %{time} æ‹’ç»"
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr "由您于 %{time} æ‹’ç»"
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr "GitLab Pages"
@@ -13406,6 +13582,9 @@ msgstr "æ— æœåŠ¡å™¨åŽç«¯ï¼ˆLambdaã€äº‘函数)"
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr "虚拟机(例如 EC2)"
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr "部署"
@@ -13509,9 +13688,15 @@ msgstr "通过%{link_start}GitLab风格的Markdown%{link_end}解æžçš„æè¿°."
msgid "Description:"
msgstr "æè¿°:"
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr "æ述性标签"
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr "设计管ç†æ–‡ä»¶å’Œæ•°æ®"
@@ -13675,6 +13860,9 @@ msgstr "DevOps 报告"
msgid "DevOps adoption"
msgstr "DevOps adoption"
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr "å¼€å‘者"
@@ -14984,6 +15172,9 @@ msgstr "输入任何颜色。"
msgid "Enter at least three characters to search"
msgstr "请至少输入三个字符æ‰å¯æœç´¢"
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr "输入您的BitbucketæœåŠ¡å™¨URL和个人访问令牌"
@@ -15347,9 +15538,6 @@ msgstr "删除å²è¯—"
msgid "Epics|Remove issue"
msgstr "删除议题"
-msgid "Epics|Show more"
-msgstr "显示更多"
-
msgid "Epics|Something went wrong while creating child epics."
msgstr "创建å­å²è¯—时出错。"
@@ -15443,6 +15631,9 @@ msgstr "获å–有效数æ®æ—¶å‡ºé”™ã€‚"
msgid "Error fetching refs"
msgstr "获å–refs时出错。"
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr "获å–ä¾èµ–列表时出错。请检查您的网络连接,然åŽé‡è¯•ã€‚"
@@ -15758,6 +15949,15 @@ msgstr "此策略没有å‡çº§è§„则。"
msgid "EscalationPolicies|mins"
msgstr "最å°å€¼"
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr "预计"
@@ -15918,6 +16118,9 @@ msgstr "人人皆å¯è´¡çŒ®"
msgid "Everything on your to-do list is marked as done."
msgstr "您的待办事项列表中的所有内容都标记为已完æˆã€‚"
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr "使用Gatsby创建GitLab Pages网站所需的所有信æ¯"
@@ -15948,9 +16151,21 @@ msgstr "凭è¯é›†"
msgid "Exactly one of %{attributes} is required"
msgstr "其中的一个%{attributes}是必需的"
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr "示例: @sub\\.company\\.com$"
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr "例å­"
@@ -15969,9 +16184,6 @@ msgstr "ä¸åŒ…括åˆå¹¶æ交。仅é™6,000次æ交。"
msgid "Execution time"
msgstr "执行时间"
-msgid "Executive Dashboard"
-msgstr "管ç†å±•ç¤ºä»ªè¡¨ç›˜"
-
msgid "Existing branch name, tag, or commit SHA"
msgstr "已存在分支å称,标记或æ交SHA"
@@ -16026,14 +16238,17 @@ msgstr "展开设置部分"
msgid "Expand sidebar"
msgstr "展开侧边æ "
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr "需è¦çš„文档: %{expected_documents}"
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
-msgstr "必须有一个用户ã€å‘½å空间或项目。"
+msgid "Experiment candidates"
+msgstr ""
msgid "Experiments"
msgstr "实验"
@@ -16138,7 +16353,7 @@ msgid "Export this project with all its related data in order to move it to a ne
msgstr "导出此项目åŠå…¶æ‰€æœ‰ç›¸å…³æ•°æ®ï¼Œä»¥ä¾¿å°†å…¶ç§»åŠ¨åˆ°æ–°çš„ GitLab 实例。导出的文件准备就绪åŽï¼Œæ‚¨å¯ä»¥ä»Žæ­¤é¡µé¢æˆ–从您将收到的电å­é‚®ä»¶é€šçŸ¥ä¸­çš„下载链接下载它。然åŽï¼Œæ‚¨å¯ä»¥åœ¨åˆ›å»ºæ–°é¡¹ç›®æ—¶å°†å…¶å¯¼å…¥ã€‚ %{link_start}了解更多。%{link_end}"
msgid "Export variable to pipelines running on protected branches and tags only."
-msgstr "仅导出å˜é‡åˆ°ä¿æŠ¤åˆ†æ”¯å’Œæ ‡ç­¾ä¸Šè¿è¡Œçš„æµæ°´çº¿ã€‚"
+msgstr "仅导出å˜é‡åˆ°ä¿æŠ¤åˆ†æ”¯å’Œä¿æŠ¤æ ‡ç­¾ä¸Šè¿è¡Œçš„æµæ°´çº¿ã€‚"
msgid "Exported requirements"
msgstr "导出的è¦æ±‚"
@@ -16346,9 +16561,6 @@ msgstr "生æˆæŠ¥å‘Šå¤±è´¥ï¼Œè¯·ç¨åŽå†è¯•"
msgid "Failed to get ref."
msgstr "获å–ref失败。"
-msgid "Failed to install."
-msgstr "安装失败。"
-
msgid "Failed to load"
msgstr "加载失败"
@@ -16505,9 +16717,6 @@ msgstr "更新议题状æ€å¤±è´¥"
msgid "Failed to update the Canary Ingress."
msgstr "无法更新Canary Ingress。"
-msgid "Failed to upgrade."
-msgstr "å‡çº§å¤±è´¥ã€‚"
-
msgid "Failed to upload object map file"
msgstr "上传对象映射文件失败"
@@ -16761,6 +16970,9 @@ msgstr "2月"
msgid "February"
msgstr "2月"
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr "å馈和更新"
@@ -16980,15 +17192,6 @@ msgstr "Federated Learning of Cohorts (FLoC)"
msgid "FloC|Participate in FLoC"
msgstr "å‚加 FLoC"
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr "输入您的 Flowdock 令牌。"
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr "从 GitLab å‘é€äº‹ä»¶é€šçŸ¥åˆ° Flowdock æµã€‚"
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr "从 GitLab å‘ Flowdock æµå‘é€äº‹ä»¶é€šçŸ¥ã€‚ %{docs_link}"
-
msgid "Focus filter bar"
msgstr "èšç„¦è¿‡æ»¤æ "
@@ -17025,6 +17228,9 @@ msgstr "关注用户的动æ€"
msgid "Followed users"
msgstr "关注的用户"
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr "字体颜色"
@@ -17169,12 +17375,21 @@ msgstr "派生(Fork)中"
msgid "Forks"
msgstr "派生"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr ""
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
msgid "Format: %{dateFormat}"
msgstr "æ ¼å¼ï¼š%{dateFormat}"
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
-msgstr "如果在 GitLab 软件包库中找ä¸åˆ°è½¯ä»¶åŒ…,则将 %{package_type} 软件包请求转å‘到 %{registry_type} 库"
-
msgid "Found errors in your %{gitlab_ci_yml}:"
msgstr "在您的 %{gitlab_ci_yml} 中找到错误:"
@@ -17251,10 +17466,6 @@ msgstr "从%{code_open}%{source_title}%{code_close}到"
msgid "From %{providerTitle}"
msgstr "%{providerTitle}æºåœ°å€"
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] "自 2022 å¹´ 10 月 19 日起,å…è´¹ç§æœ‰ç¾¤ç»„å°†é™åˆ¶æœ€å¤š %d åæˆå‘˜åŠ å…¥"
-
msgid "From issue creation until deploy to production"
msgstr "从创建议题到部署至生产环境"
@@ -17297,6 +17508,9 @@ msgstr "æµæ°´çº¿é€šç”¨è®¾ç½®"
msgid "General settings"
msgstr "通用设置"
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr "生æˆä¸€ç»„默认的标记"
@@ -18142,6 +18356,10 @@ msgstr "GitLab 版本"
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr "GitLab 将在你的派生项目中创建分支并开始åˆå¹¶è¯·æ±‚。"
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+
msgid "GitLab.com (SaaS)"
msgstr "GitLab.com (SaaS)"
@@ -18391,6 +18609,9 @@ msgstr "最近的议题"
msgid "GlobalSearch|Recent merge requests"
msgstr "最近的åˆå¹¶è¯·æ±‚"
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr "结果已更新, %{count} 个结果å¯ç”¨ï¼Œä½¿ç”¨ä¸Šä¸‹ç®­å¤´é”®æµè§ˆæœç´¢ç»“果列表,或使用 ENTER é”®æ交。"
@@ -18418,6 +18639,9 @@ msgstr "新建议在下é¢æ˜¾ç¤ºã€‚"
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr "使用快æ·é”® %{kbdOpen}/%{kbdClose} 开始æœç´¢"
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr "您想è¦æœç´¢ä»€ä¹ˆï¼Ÿ"
@@ -18595,6 +18819,9 @@ msgstr "转到您的åˆå¹¶è¯·æ±‚"
msgid "Go to your projects"
msgstr "转到您的项目"
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr "转到您的代ç ç‰‡æ®µ"
@@ -18688,6 +18915,12 @@ msgstr "授予此密钥写入æƒé™"
msgid "Graph"
msgstr "分支图"
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr "作业ä¾èµ–项"
@@ -18707,7 +18940,7 @@ msgid "Group"
msgstr "群组"
msgid "Group %{group_name} and its Mattermost team were successfully created."
-msgstr ""
+msgstr " %{group_name} 群组åŠå…¶ Mattermost 团队已æˆåŠŸåˆ›å»ºã€‚"
msgid "Group %{group_name} couldn't be exported."
msgstr "群组%{group_name}无法导出。"
@@ -18877,8 +19110,8 @@ msgstr "过去 30 天"
msgid "GroupActivityMetrics|Members added"
msgstr "添加新æˆå‘˜æ•°"
-msgid "GroupActivityMetrics|Merge Requests created"
-msgstr "åˆå¹¶è¯·æ±‚已创建"
+msgid "GroupActivityMetrics|Merge requests created"
+msgstr ""
msgid "GroupActivityMetrics|Recent activity"
msgstr "最近活动"
@@ -18964,6 +19197,9 @@ msgstr "3年内"
msgid "GroupSAML|\"persistent\" recommended"
msgstr "推è“æŒä¹…化â€"
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr "%{strongOpen}警告%{strongClose} - å¯ç”¨ %{linkStart}SSO 实施%{linkEnd} 以é™ä½Žå®‰å…¨é£Žé™©ã€‚"
@@ -19069,6 +19305,9 @@ msgstr "SAML群组å称"
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr "SAML群组å称: %{saml_group_name}"
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr "SAMLå“应输出"
@@ -19240,8 +19479,8 @@ msgstr "%{group} 中的项目ä¸èƒ½ä¸Žå…¶ä»–群组共享"
msgid "GroupSettings|Reporting"
msgstr "报告"
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
-msgstr "选择一个å­ç»„用作该组的自定义项目模æ¿çš„æºã€‚"
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
+msgstr ""
msgid "GroupSettings|Select parent group"
msgstr "选择父组"
@@ -19261,9 +19500,6 @@ msgstr "设置群组中创建的新仓库的默认分支的åˆå§‹å称和ä¿æŠ¤
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr "如果找ä¸åˆ°æ›¿ä»£ CI é…置文件,则Auto DevOps æµæ°´çº¿å°†è¿è¡Œã€‚"
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr "å¯ä»¥é€‰æ‹©è¯¥å­ç»„中的项目作为该组中创建的新项目的模æ¿ã€‚ %{link_start}了解更多。%{link_end}"
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr "æ›´æ–° Auto DevOps æµæ°´çº¿æ—¶å‡ºçŽ°é—®é¢˜ï¼š %{error_messages}。"
@@ -19393,6 +19629,9 @@ msgstr "连接实例"
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr "è”系管ç†å‘˜å¯ç”¨å¯¼å…¥ç¾¤ç»„的选项。"
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr "创建群组"
@@ -19402,9 +19641,6 @@ msgstr "创建新的群组"
msgid "GroupsNew|Create subgroup"
msgstr "创建å­ç»„"
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr "在æºå®žä¾‹çš„ %{pat_link_start}用户设置%{pat_link_end} 中创建。出于 %{short_living_link_start}安全原因%{short_living_link_end},在创建令牌时使用较短的到期日期。"
-
msgid "GroupsNew|GitLab source URL"
msgstr "GitLabæºURL"
@@ -19874,12 +20110,18 @@ msgstr "ä¸å¯ç”¨çš„结构"
msgid "Hierarchy|You can start using these items now."
msgstr "您现在å¯ä»¥å¼€å§‹ä½¿ç”¨è¿™äº›äº‹é¡¹ã€‚"
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr "存在高å±æˆ–未知æ¼æ´ž"
msgid "Highest role:"
msgstr "顶级角色:"
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr "警报事件:"
@@ -19913,9 +20155,6 @@ msgstr "主页"
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr "Hook 执行失败。确ä¿ç¾¤ç»„有一个包å«æ交的项目。"
-msgid "Hook was successfully updated."
-msgstr "é’©å­å·²æˆåŠŸæ›´æ–°ã€‚"
-
msgid "Horizontal rule"
msgstr "水平规则"
@@ -20084,6 +20323,9 @@ msgstr "标识符"
msgid "Identities"
msgstr "身份标识"
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr "新验è¯ç å·²å‘é€ã€‚"
@@ -20108,6 +20350,9 @@ msgstr "创建项目"
msgid "IdentityVerification|Didn't receive a code?"
msgstr "未收到验è¯ç ï¼Ÿ"
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr "输入验è¯ç ã€‚"
@@ -20147,8 +20392,8 @@ msgstr "超过最大登录å°è¯•æ¬¡æ•°ã€‚等待 %{interval} åŽé‡è¯•ã€‚"
msgid "IdentityVerification|Phone number"
msgstr "电è¯å·ç "
-msgid "IdentityVerification|Phone number can't be blank."
-msgstr "电è¯å·ç ä¸èƒ½ä¸ºç©ºã€‚"
+msgid "IdentityVerification|Phone number is required."
+msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
msgstr "电è¯å·ç å¿…须是 %{maxLength} ä½æˆ–更少。"
@@ -20189,6 +20434,12 @@ msgstr "验è¯ç ä¸æ­£ç¡®ã€‚å†æ¬¡è¾“入或é‡æ–°å‘é€æ–°éªŒè¯ç ã€‚"
msgid "IdentityVerification|Verification code"
msgstr "验è¯ç "
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr "验è¯æˆåŠŸ"
@@ -20201,9 +20452,18 @@ msgstr "验è¯æ‚¨çš„电å­é‚®ä»¶åœ°å€"
msgid "IdentityVerification|Verify payment method"
msgstr "验è¯ä»˜æ¬¾æ–¹å¼"
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr "验è¯æ‚¨çš„身份"
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr "您å¯ä»¥ç¨åŽéšæ—¶éªŒè¯æ‚¨çš„å¸æˆ·ï¼Œæ¥åˆ›å»ºä¸€ä¸ªç¾¤ç»„。"
@@ -20546,6 +20806,9 @@ msgstr "在此 URL 上没有有效的 Git 仓库。如果您的 HTTP 仓库ä¸èƒ
msgid "Improve customer support with Service Desk"
msgstr "通过æœåŠ¡å°æ”¹å–„客户支æŒ"
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr "在拉å–é•œåƒçš„情况下,您的用户将æˆä¸ºæ´»åŠ¨æè¦ä¸­æ‰€æœ‰ä½œä¸ºæ›´æ–°ç»“果的事件的作者,例如创建新分支或将新æ交推é€åˆ°çŽ°æœ‰åˆ†æ”¯ã€‚"
@@ -21113,12 +21376,18 @@ msgstr "事件"
msgid "Incident Management Limits"
msgstr "事件管ç†é™åˆ¶"
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr "事件详情"
msgid "Incident template (optional)."
msgstr "事件模æ¿ï¼ˆå¯é€‰ï¼‰ã€‚"
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr "剩余%{hours}å°æ—¶%{minutes} 分钟"
@@ -21437,6 +21706,9 @@ msgstr "索引删除已å–消"
msgid "Indicates whether this runner can pick jobs without tags"
msgstr "指示此runner是å¦å¯ä»¥é€‰æ‹©æ— æ ‡è®°çš„作业"
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr "通知用户没有上传SSH密钥,如果没有SSH秘钥,将无法通过SSH推é€ã€‚"
@@ -21708,6 +21980,9 @@ msgstr "å¯ç”¨ SSL 验è¯"
msgid "Integrations|Enable comments"
msgstr "å¯ç”¨è¯„论"
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr "ç¡®ä¿æ‚¨çš„实例 URL 正确且您的实例é…置正确。%{linkStart}了解更多%{linkEnd}。"
@@ -21741,6 +22016,9 @@ msgstr "GitLab 管ç†å‘˜å¯ä»¥è®¾ç½®æ‰€æœ‰ç¾¤ç»„和项目默认继承和使用çš
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr "GitLab 管ç†å‘˜å¯ä»¥è®¾ç½®ä¸€ä¸ªç¾¤ç»„中所有项目默认继承和使用的集æˆï¼Œè¿™äº›é›†æˆåº”用于所有尚未使用自定义设置的项目, 如果项目需è¦è®¾ç½®ï¼Œæ‚¨å¯ä»¥è¦†ç›–自定义设置。 了解更多关于 %{integrations_link_start}群组级集æˆç®¡ç†%{link_end}。"
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr "群组级集æˆç®¡ç†"
@@ -22089,9 +22367,12 @@ msgstr "邀请您的åŒäº‹"
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr "ç›®å‰æ‚¨è¿˜æ²¡æœ‰é‚€è¯·ä»»ä½•äººåŠ å…¥è¿™ä¸ªç¾¤ç»„。 您å¯ä»¥é‚€è¯·æ‚¨çš„åŒäº‹åˆ°ç¾¤ç»„ç§ï¼Œä»¥æ–¹ä¾¿è®¨è®ºé—®é¢˜ï¼Œåœ¨åˆå¹¶è¯·æ±‚中进行å作,以åŠåˆ†äº«æ‚¨çš„知识。"
-msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
msgstr ""
+msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
+msgstr "è¦èŽ·å¾—更多æˆå‘˜ï¼Œè¯¥å‘½å空间的所有者å¯ä»¥%{trialLinkStart}开始试用%{trialLinkEnd},或%{upgradeLinkStart}å‡çº§%{upgradeLinkEnd}到付费级别。"
+
msgid "InviteMembersModal|%{linkStart}Read more%{linkEnd} about role permissions"
msgstr "%{linkStart}了解更多%{linkEnd}关于角色æƒé™çš„ä¿¡æ¯"
@@ -22137,8 +22418,8 @@ msgstr "管ç†æˆå‘˜"
msgid "InviteMembersModal|Members were successfully added"
msgstr "æˆå‘˜å·²æˆåŠŸæ·»åŠ "
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
-msgstr "请选择è¦é‚€è¯·çš„æˆå‘˜æˆ–输入电å­é‚®ä»¶åœ°å€"
+msgid "InviteMembersModal|Please add members to invite"
+msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr "请检查邀请错误并é‡è¯•ï¼š"
@@ -22175,7 +22456,7 @@ msgid "InviteMembersModal|To get more members an owner of the group can %{trialL
msgstr "è¦èŽ·å¾—更多æˆå‘˜ï¼Œç¾¤ç»„的所有者å¯ä»¥ %{trialLinkStart}开始试用%{trialLinkEnd} 或 %{upgradeLinkStart}å‡çº§%{upgradeLinkEnd} 到付费等级。"
msgid "InviteMembersModal|To invite new users to this namespace, you must remove existing users. You can still add existing namespace users."
-msgstr ""
+msgstr "è¦é‚€è¯·æ–°ç”¨æˆ·åŠ å…¥æ­¤å‘½å空间,您必须删除现有用户。您ä»ç„¶å¯ä»¥æ·»åŠ çŽ°æœ‰å‘½å空间的用户。"
msgid "InviteMembersModal|Username or email address"
msgstr "用户å或电å­é‚®ä»¶åœ°å€"
@@ -22328,7 +22609,7 @@ msgid "IssuableEvents|unassigned"
msgstr "未分é…"
msgid "IssuableStatus|%{wi_type} created %{created_at} by "
-msgstr "%{wi_type} 由 %{created_at} 创建"
+msgstr "%{wi_type} 创建于 %{created_at} "
msgid "IssuableStatus|Closed"
msgstr "已关闭"
@@ -22603,9 +22884,6 @@ msgstr "移动议题时出错。"
msgid "Issue|Title"
msgstr "标题"
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr "无法使用 Web ç•Œé¢ %{action} 存储在 LFS 中的文件"
-
msgid "It looks like you have some draft commits in this branch."
msgstr "看起æ¥ä½ åœ¨è¿™ä¸ªåˆ†æ”¯ä¸Šæœ‰ä¸€äº›è‰ç¨¿æ交。"
@@ -22744,9 +23022,6 @@ msgstr "加载迭代周期时出错。"
msgid "Iterations|Iteration cadences"
msgstr "迭代周期"
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr "迭代是在一段时间内跟踪议题的一ç§æ–¹æ³•ï¼Œä½¿å°ç»„也能够跟踪速度和波动指标。"
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr "迭代计划在%{weekday}开始。"
@@ -23179,9 +23454,6 @@ msgstr "没有å¯æ˜¾ç¤ºçš„作业"
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr "对任务过滤åŽçš„æœç´¢åŠŸèƒ½ï¼Œå½“å‰ä¸æ”¯æŒæœç´¢åŽŸå§‹æ–‡æœ¬ã€‚请使用å¯ç”¨çš„æœç´¢ä»¤ç‰Œã€‚"
-msgid "Jobs|Status"
-msgstr "状æ€"
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr "获å–失败的任务时出错。"
@@ -23332,6 +23604,9 @@ msgstr "此作业已阻塞,因为该项目没有分é…任何å¯ç”¨Runner。"
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr "此作业已阻塞,因为没有å¯ç”¨çš„Runner处ç†æ­¤é¡¹ä½œä¸šã€‚"
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr "等待资æº"
@@ -23413,6 +23688,9 @@ msgstr "é”®"
msgid "Key (PEM)"
msgstr "秘钥 (PEM)"
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr "Key:"
@@ -23482,9 +23760,6 @@ msgstr "Kubernetes 集群"
msgid "Kubernetes deployment not found"
msgstr "找ä¸åˆ°Kubernetes部署"
-msgid "Kubernetes error: %{error_code}"
-msgstr "Kubinentes 错误: %{error_code}"
-
msgid "LDAP"
msgstr "LDAP"
@@ -23558,15 +23833,18 @@ msgstr "标记"
msgid "Labels"
msgstr "标记"
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr "标记å¯ä»¥åº”用于 %{features}。群组标记å¯ç”¨äºŽç¾¤ç»„中的所有项目。"
-
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "标记å¯ç”¨äºŽå¯¹è®®é¢˜å’Œåˆå¹¶è¯·æ±‚进行分类。"
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr "标记å¯ä»¥åˆ†é…给议题和åˆå¹¶è¯·æ±‚。为标记加星标å¯ä»¥ä½¿ä¹‹æˆä¸ºä¼˜å…ˆæ ‡è®°ã€‚"
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr "此迭代中没有议题的标记:"
@@ -23652,6 +23930,9 @@ msgstr "最åŽä¸€æ¬¡ç”± %{link_start}%{avatar} %{name}%{link_end} 编辑"
msgid "Last event"
msgstr "最近活动"
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr "最近修改"
@@ -23757,6 +24038,9 @@ msgstr "了解更多"
msgid "Learn More."
msgstr "了解更多。"
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr "了解如何 %{link_start}贡献到内置的模æ¿%{link_end}"
@@ -24205,10 +24489,10 @@ msgid "Link"
msgstr "链接"
msgid "Link %{issuableType}s together to show that they're related or that one is blocking others."
-msgstr ""
+msgstr "将 %{issuableType}链接在一起,表明它们是相关的,或其中一个阻塞了其它的。"
msgid "Link %{issuableType}s together to show that they're related."
-msgstr ""
+msgstr "将 %{issuableType} 链接在一起,表明它们是相关的。"
msgid "Link (optional)"
msgstr "链接(å¯é€‰ï¼‰"
@@ -24426,12 +24710,12 @@ msgstr "å·²é”定文件"
msgid "Locked by %{fileLockUserName}"
msgstr "被%{fileLockUserName}é”定"
+msgid "Locked files"
+msgstr ""
+
msgid "Locked the discussion."
msgstr "é”定讨论."
-msgid "Locks give the ability to lock specific file or folder."
-msgstr "加é”å¯ä»¥é”定特定的文件或文件夹。"
-
msgid "Locks the discussion."
msgstr "é”定讨论."
@@ -24480,6 +24764,9 @@ msgstr "Logo 将被删除,您确定å—?"
msgid "Logs"
msgstr "日志"
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr "存在低å±æ¼æ´ž"
@@ -24585,6 +24872,9 @@ msgstr "将此 %{type} 设置为ç§å¯†ã€‚"
msgid "Manage %{workspace} labels"
msgstr "ç®¡ç† %{workspace} 标记"
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr "ç®¡ç† Web IDE 功能。"
@@ -25083,6 +25373,9 @@ msgstr "å¹³å‡åˆå¹¶æ—¶é—´"
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr "以代ç å­—节为å•ä½ã€‚排除生æˆçš„代ç å’Œä¾›åº”商代ç ã€‚"
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr "中等超时"
@@ -25104,6 +25397,12 @@ msgstr "%{member_name}邀请您使用GitLab"
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr "邀请加入%{project_or_group}%{project_or_group_name}"
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr "æˆå‘˜"
@@ -26227,6 +26526,9 @@ msgstr "添加项目"
msgid "Modal|Close"
msgstr "关闭"
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr "已修改"
@@ -26290,9 +26592,6 @@ msgstr "更多信æ¯"
msgid "More information"
msgstr "更多信æ¯"
-msgid "More information and share feedback"
-msgstr "更多信æ¯å’Œå…±äº«å馈"
-
msgid "More information is available|here"
msgstr "这里"
@@ -26501,6 +26800,9 @@ msgstr "需è¦é‡‡å–措施:%{namespace_name} 的存储空间已超出"
msgid "NamespaceStorage|Buy more storage"
msgstr "购买更多存储"
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr "我们建议您购买é¢å¤–存储,确ä¿æ‚¨çš„æœåŠ¡ä¸ä¸­æ–­ã€‚"
@@ -26679,6 +26981,9 @@ msgstr "新建分支"
msgid "New branch unavailable"
msgstr "新分支ä¸å¯ç”¨"
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr "新建ç§å¯†å²è¯—标题 "
@@ -26721,6 +27026,12 @@ msgstr "已生æˆæ–°çš„è¿è¡ŒçŠ¶å†µæ£€æŸ¥è®¿é—®ä»¤ç‰Œï¼"
msgid "New identity"
msgstr "新建身份标识"
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "新建议题"
@@ -26994,9 +27305,6 @@ msgstr "未å‘现议题"
msgid "No iteration"
msgstr "无迭代"
-msgid "No iterations to show"
-msgstr "没有å¯æ˜¾ç¤ºè¿­ä»£"
-
msgid "No job log"
msgstr "没有作业日志"
@@ -27096,6 +27404,9 @@ msgstr "没有结果"
msgid "No results found"
msgstr "未找到结果"
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr "æ— runnerå¯æ‰§è¡Œæ–‡ä»¶"
@@ -27749,9 +28060,24 @@ msgstr "碎片数é‡"
msgid "OK"
msgstr "确定"
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr "对象在æœåŠ¡å™¨ä¸Šä¸å­˜åœ¨, 或者您没有访问它的æƒé™"
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr "å¯è§‚测性"
@@ -28131,6 +28457,9 @@ msgstr "没有计划中的扫æ。"
msgid "OnDemandScans|Timezone"
msgstr "时区"
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr "查看结果"
@@ -28207,9 +28536,6 @@ msgstr "åªæœ‰%{membersPageLinkStart}项目æˆå‘˜%{membersPageLinkEnd}å¯ä»¥è®¿é
msgid "Only active projects show up in the search and on the dashboard."
msgstr "仅活动的项目显示在æœç´¢å’Œä»ªè¡¨æ¿ä¸Šã€‚"
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr "åªå…许任何人在您打算被任何人使用的 GitLab 实例上注册å¸æˆ·ã€‚å…许任何人注册会使 GitLab 实例更容易å—到攻击。"
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr "仅在å¯ç”¨è¿œç¨‹å­˜å‚¨æ—¶æœ‰æ•ˆã€‚设置为 0 表示没有大å°é™åˆ¶ã€‚"
@@ -28315,9 +28641,6 @@ msgstr "打开一个新窗å£"
msgid "Opens new window"
msgstr "打开新窗å£"
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr "æ“作失败。请检查 Pod 日志 %{pod_name} 了解更多信æ¯ã€‚"
-
msgid "Operation not allowed"
msgstr "ä¸å…许此æ“作"
@@ -28603,6 +28926,9 @@ msgstr "Conan"
msgid "PackageRegistry|Conan Command"
msgstr "Conan命令"
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr "å¤åˆ¶.pypirc内容"
@@ -28691,6 +29017,9 @@ msgstr "删除软件包 asset"
msgid "PackageRegistry|Delete package version"
msgstr "删除软件包版本"
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr "删除选中"
@@ -28706,6 +29035,12 @@ msgstr "删除最åŽä¸€ä¸ªè½¯ä»¶åŒ… assets 将删除 %{name} çš„ %{version} 版æ
msgid "PackageRegistry|Duplicate packages"
msgstr "é‡å¤çš„软件包"
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr "å‘布错误"
@@ -28730,6 +29065,18 @@ msgstr "关于Nuget注册表的更多信æ¯ï¼Œ%{linkStart}请è§æ–‡æ¡£%{linkEnd}
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr "关于PyPi注册表的更多信æ¯ï¼Œ%{linkStart}请è§æ–‡æ¡£%{linkEnd}。"
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr "通用"
@@ -28802,6 +29149,9 @@ msgstr "NuGet命令"
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr "è¦ä¿ç•™çš„é‡å¤ assets æ•°é‡"
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr "软件包注册表"
@@ -28817,6 +29167,9 @@ msgstr "æˆåŠŸåˆ é™¤è½¯ä»¶åŒ…"
msgid "PackageRegistry|Package formats"
msgstr "软件包格å¼"
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] "软件包有 %{updatesCount} 个存档更新"
@@ -28824,6 +29177,9 @@ msgstr[0] "软件包有 %{updatesCount} 个存档更新"
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr "软件包由分支%{branch}上的%{link}æ交所更新,由æµæ°´çº¿%{pipeline}构建并于%{datetime}å‘布到库"
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr "永久删除"
@@ -28878,6 +29234,9 @@ msgstr "显示 PyPi 命令"
msgid "PackageRegistry|Show Yarn commands"
msgstr "显示 Yarn 命令"
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr "删除软件包 asset 时出错。"
@@ -28926,9 +29285,6 @@ msgstr "这个Nuget包没有ä¾èµ–项目。"
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr "è¦æ‰©å¤§æœç´¢èŒƒå›´ï¼Œè¯·æ›´æ”¹æˆ–删除上é¢çš„筛选器。"
-msgid "PackageRegistry|Type"
-msgstr "类型"
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr "无法获å–软件包版本信æ¯ã€‚"
@@ -28948,6 +29304,10 @@ msgid "PackageRegistry|You are about to delete 1 asset. This operation is irreve
msgid_plural "PackageRegistry|You are about to delete %d assets. This operation is irreversible."
msgstr[0] "您将è¦åˆ é™¤ %d 项 assets,此æ“作是ä¸å¯é€†çš„。"
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr "å³å°†åˆ é™¤%{name}çš„%{version}版本。确定继续å—?"
@@ -28963,6 +29323,9 @@ msgstr "npm"
msgid "PackageRegistry|published by %{author}"
msgstr "ç”±%{author}å‘布"
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr "软件包与镜åƒåº“"
@@ -29032,6 +29395,9 @@ msgstr "å‚æ•°"
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr "å‚æ•°\"job_id\"ä¸èƒ½è¶…过%{job_id_max_size}的长度"
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr "父级"
@@ -29095,6 +29461,15 @@ msgstr "请输入您的密ç ä»¥ç¡®è®¤"
msgid "Passwords should be unique and not used for any other sites or services."
msgstr "密ç åº”唯一并未用于任何其他网站或æœåŠ¡ã€‚"
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr "需包å«è‡³å°‘一个å°å†™å­—æ¯"
@@ -29338,6 +29713,30 @@ msgstr "Phabricator 任务"
msgid "Phone"
msgstr "电è¯"
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr "选择一个å称"
@@ -30253,6 +30652,9 @@ msgstr "请检查您的电å­é‚®ä»¶(%{email})以确认您拥有此电å­é‚®ç®±å¹
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr "请在继续å‰ç‚¹å‡»ç”µå­é‚®ä»¶ä¸­çš„确认链接。它被å‘é€åˆ°äº†%{html_tag_strong_start}%{email}%{html_tag_strong_end}"
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr "请在您的个人资料中填写电å­é‚®ä»¶åœ°å€"
@@ -30409,9 +30811,6 @@ msgstr "请å°è¯•åˆ·æ–°é¡µé¢ã€‚如果问题ä»ç„¶å­˜åœ¨ï¼Œè¯·è”系支æŒäººå‘˜
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr "请输入%{phrase_code}以继续或关闭此对è¯æ¡†ä»¥å–消。"
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr "请使用此表å•å‘管ç†å‘˜æŠ¥å‘Šåˆ›å»ºåžƒåœ¾è®®é¢˜ã€è¯„论或行为ä¸å½“的用户。"
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr "请ç¨ç­‰ç‰‡åˆ»ï¼Œæˆ‘们正在加载此行的文件历å²è®°å½•ã€‚"
@@ -30518,16 +30917,16 @@ msgid "PreScanVerification|Started %{timeAgo} in pipeline"
msgstr "在æµæ°´çº¿ä¸­å¼€å§‹äºŽ %{timeAgo}"
msgid "PreScanVerification|Target exploration"
-msgstr ""
+msgstr "目标探索"
msgid "PreScanVerification|Test your configuration and identify potential errors before running a full scan."
msgstr "在è¿è¡Œå®Œæ•´æ‰«æå‰æµ‹è¯•æ‚¨çš„é…置并识别潜在错误。"
msgid "PreScanVerification|The last pre-scan verification job is no longer valid because this scan’s configuration has changed."
-msgstr ""
+msgstr "最åŽä¸€æ¬¡çš„预扫æ验è¯ä½œä¸šä¸å†æœ‰æ•ˆï¼Œå› ä¸ºæ­¤æ‰«æçš„é…置已更改。"
msgid "PreScanVerification|The pre-scan verification status was reset for this scan"
-msgstr ""
+msgstr "此扫æ的扫æå‰éªŒè¯çŠ¶æ€å·²é‡ç½®"
msgid "PreScanVerification|Verification checks"
msgstr "验è¯æ£€æŸ¥"
@@ -30565,6 +30964,9 @@ msgstr "选择您希望在仪表盘上默认看到的内容。"
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "选择项目概览页é¢ä¸­æ‚¨æƒ³çœ‹åˆ°çš„内容。"
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr "添加行的颜色"
@@ -30649,6 +31051,9 @@ msgstr "语法高亮主题"
msgid "Preferences|Tab width"
msgstr "制表符宽度"
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr "此功能是实验性的,翻译尚未完æˆã€‚"
@@ -30661,9 +31066,15 @@ msgstr "此设置å…许您自定义系统布局和默认视图的呈现。"
msgid "Preferences|Time preferences"
msgstr "时间å好"
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr "使用相对时间"
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr "当您在æ述或评论框中键入时,按列表中的 %{kbdOpen}Enter%{kbdClose},会在下é¢æ·»åŠ ä¸€ä¸ªæ–°é¡¹ã€‚"
@@ -30781,18 +31192,27 @@ msgstr "%{name} 命令出错: %{message}。"
msgid "Proceed"
msgstr "继续"
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr "生产力分æž"
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr "å—ä¼—"
+msgid "ProductAnalytics|New Analytics Widget Title"
+msgstr ""
+
msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr "此类型的图表目å‰æ²¡æœ‰æ•°æ®ã€‚如果您尚未é…置产å“分æžå·¥å…·ï¼Œè¯·æŸ¥çœ‹è®¾ç½®æ ‡ç­¾ã€‚"
-msgid "ProductAnalytics|Widgets content"
-msgstr "部件内容"
-
msgid "Productivity"
msgstr "效率"
@@ -31153,6 +31573,12 @@ msgstr "更新用户å"
msgid "Profiles|Upload new avatar"
msgstr "上传新头åƒ"
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr "使用ç§äººç”µå­é‚®ä»¶ - %{email}"
@@ -31687,12 +32113,18 @@ msgstr "必须解决所有主题"
msgid "ProjectSettings|Allow"
msgstr "å…许"
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr "始终在议题ã€åˆå¹¶è¯·æ±‚和代ç ç‰‡æ®µä¸Šæ˜¾ç¤ºèµžè®¸å’Œä¸èµžè®¸è¡¨æƒ…符å·æŒ‰é’®ã€‚"
msgid "ProjectSettings|Analytics"
msgstr "分æž"
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr "自动关闭默认分支上的引用议题"
@@ -31774,9 +32206,6 @@ msgstr "æ¯æ¬¡åˆå¹¶éƒ½ä¼šåˆ›å»ºä¸€ä¸ªåˆå¹¶æ交。"
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr "æ¯ä¸ªé¡¹ç›®éƒ½å¯ä»¥æœ‰è‡ªå·±çš„空间æ¥å­˜å‚¨å…¶Dockeré•œåƒ"
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr "æ¯ä¸ªé¡¹ç›®éƒ½å¯ä»¥æœ‰è‡ªå·±çš„空间æ¥å­˜å‚¨å®ƒçš„软件包。"
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr "æ¯ä¸ªé¡¹ç›®éƒ½å¯ä»¥æœ‰è‡ªå·±çš„空间æ¥å­˜å‚¨å®ƒçš„软件包。注æ„:当一个项目是公开时,软件包库总是å¯è§çš„。"
@@ -31891,6 +32320,9 @@ msgstr "åªæœ‰å½“æºåˆ†æ”¯ä¸Žå…¶ç›®æ ‡æ˜¯æœ€æ–°çš„时候,æ‰å…许åˆå¹¶ã€‚"
msgid "ProjectSettings|Monitor"
msgstr "监控"
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr "没有创建åˆå¹¶æ交。"
@@ -31936,6 +32368,9 @@ msgstr "项目å¯è§æ€§"
msgid "ProjectSettings|Public"
msgstr "公开"
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr "å‘布"
@@ -32057,7 +32492,7 @@ msgid "ProjectSettings|Users can only push commits to this repository if the com
msgstr "如果æ交者电å­é‚®ä»¶æ˜¯ä»–们自己的已验è¯ç”µå­é‚®ä»¶ä¹‹ä¸€ï¼Œç”¨æˆ·åªèƒ½å°†æ交推é€åˆ°æ­¤ä»“库。"
msgid "ProjectSettings|Users can only push commits to this repository if the committer name is consistent with their git config username."
-msgstr ""
+msgstr "如果æ交者å称与他们的 git config 用户å一致,用户åªèƒ½æŽ¨é€æ交到此仓库。"
msgid "ProjectSettings|Users can request access"
msgstr "用户å¯ä»¥è¯·æ±‚访问"
@@ -32065,8 +32500,8 @@ msgstr "用户å¯ä»¥è¯·æ±‚访问"
msgid "ProjectSettings|View and edit files in this project."
msgstr "查看和编辑此项目中的文件。"
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
-msgstr "查看和编辑此项目中的文件。éžé¡¹ç›®æˆå‘˜åªæœ‰è¯»å–æƒé™ã€‚"
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
+msgstr ""
msgid "ProjectSettings|View project analytics."
msgstr "查看项目分æžã€‚"
@@ -32146,6 +32581,9 @@ msgstr "Netrify/Plain HTML"
msgid "ProjectTemplates|NodeJS Express"
msgstr "NodeJS Express"
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr "Pages/Gatsby"
@@ -32668,9 +33106,6 @@ msgstr "ä¿æŠ¤åˆ†æ”¯"
msgid "Protected Branches"
msgstr "ä¿æŠ¤åˆ†æ”¯"
-msgid "Protected Environment"
-msgstr "å—ä¿æŠ¤çš„环境"
-
msgid "Protected Paths: requests"
msgstr "å—ä¿æŠ¤çš„路径:请求"
@@ -33341,6 +33776,9 @@ msgid "Refreshing in a second to show the updated status..."
msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] "%d 秒åŽåˆ·æ–°ä»¥æ˜¾ç¤ºæ›´æ–°çŠ¶æ€..."
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr "é‡æ–°ç”Ÿæˆå¯¼å‡º"
@@ -33537,6 +33975,9 @@ msgstr "Runbook"
msgid "ReleaseAssetLinkType|Runbooks"
msgstr "Runbook"
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr "å‘布日期"
@@ -33876,6 +34317,9 @@ msgstr "é‡æ–°æ‰“å¼€%{quick_action_target}"
msgid "Reopened this %{quick_action_target}."
msgstr "é‡æ–°æ‰“å¼€%{quick_action_target}。"
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr "é‡æ–°æ‰“å¼€%{quick_action_target}。"
@@ -33930,11 +34374,8 @@ msgstr "回å¤â€¦"
msgid "Report Finding not found"
msgstr "未找到报告结果"
-msgid "Report abuse"
-msgstr "报告滥用"
-
-msgid "Report abuse to admin"
-msgstr "å‘管ç†å‘˜æŠ¥å‘Šæ»¥ç”¨è¡Œä¸º"
+msgid "Report abuse to administrator"
+msgstr ""
msgid "Report couldn't be prepared."
msgstr "无法准备报告。"
@@ -34266,6 +34707,9 @@ msgstr "请求"
msgid "Request Access"
msgstr "申请æƒé™"
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr "请求一个新的"
@@ -34511,6 +34955,9 @@ msgstr "é™åˆ¶é€‚用于该 runner 的项目"
msgid "Restricted shift times are not available for hourly shifts"
msgstr "é™åˆ¶è½®ç­æ—¶é—´ä¸é€‚用于æ¯å°æ—¶è½®ç­"
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr "æ¢å¤"
@@ -34584,8 +35031,8 @@ msgstr "在æ交å‰å®¡æŸ¥ç›®æ ‡é¡¹ç›®ï¼Œä»¥é¿å…暴露 %{source} 的更改。"
msgid "Review time"
msgstr "审核时间"
-msgid "Review time is defined as the time it takes from first comment until merged."
-msgstr "评审时间定义为从首次评论到åˆå¹¶æ‰€éœ€æ—¶é—´ã€‚"
+msgid "Review time is the amount of time since the first comment in a merge request."
+msgstr ""
msgid "ReviewApp|Enable Review App"
msgstr "å¯ç”¨å®¡é˜…应用"
@@ -34678,6 +35125,9 @@ msgstr "è¿è¡Œä¾‹è¡Œç»´æŠ¤"
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr "è¿è¡Œä¾‹è¡Œç»´æŠ¤ä»»åŠ¡æ¥è‡ªåŠ¨ä¼˜åŒ– Git 仓库。ç¦ç”¨æ­¤é€‰é¡¹å°†å¯¼è‡´æ€§èƒ½éšç€æ—¶é—´çš„推移而下é™ã€‚"
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr "è¿è¡Œæ‰‹åŠ¨æˆ–延迟的作业"
@@ -34735,8 +35185,8 @@ msgstr "容é‡ä¸º 1,通过自动扩展组é‡æ–°ç”Ÿæˆå¯ç”¨æš– HA。容é‡ä¸º
msgid "Runners|A new version is available"
msgstr "有新版本å¯ç”¨"
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
-msgstr "一个周期性的åŽå°ä»»åŠ¡åˆ é™¤ runner,这些 runner 在超过 %{elapsedTime} åŽæœªè”ç³» GitLab 。%{linkStart}我å¯ä»¥æŸ¥çœ‹æœ‰å¤šå°‘ runner 被删除了?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgstr ""
msgid "Runners|Active"
msgstr "å¯ç”¨"
@@ -34762,6 +35212,9 @@ msgstr "具有手动缩放和å¯é€‰è°ƒåº¦åŠŸèƒ½çš„ Amazon Linux 2 Docker HA。No
msgid "Runners|An error has occurred fetching instructions"
msgstr "获å–指令时å‘生错误"
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr "æ­¤ runner 有å¯ç”¨çš„å‡çº§"
@@ -34783,6 +35236,9 @@ msgstr "与一个或多个项目关è”"
msgid "Runners|Available"
msgstr "å¯ç”¨"
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr "适用于所有项目"
@@ -34865,6 +35321,9 @@ msgstr "输入秒数。此超时时间优先级高于为项目设定的较低超
msgid "Runners|Executor"
msgstr "执行器"
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr "过滤项目"
@@ -34883,6 +35342,9 @@ msgstr "我们如何å‡çº§ GitLab runner?"
msgid "Runners|IP Address"
msgstr "IP地å€"
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr "安装Runner"
@@ -34922,12 +35384,15 @@ msgstr "从未连接过:"
msgid "Runners|Never expires"
msgstr "æ°¸ä¸è¿‡æœŸ"
-msgid "Runners|New group runners view"
-msgstr "新建群组 runner 视图"
+msgid "Runners|New group runners can be registered"
+msgstr ""
msgid "Runners|New registration token generated!"
msgstr "已生æˆæ–°çš„注册令牌ï¼"
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr "未找到结果"
@@ -35019,6 +35484,9 @@ msgstr "Runner #%{runner_id}"
msgid "Runners|Runner %{name} was deleted"
msgstr "Runner %{name} 已删除"
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr "分é…给项目的runner"
@@ -35085,6 +35553,9 @@ msgstr "Runners 是è¿è¡Œ CI/CD 作业的代ç†ã€‚按照 %{linkStart}安装和æ³
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr "Runners 是è¿è¡Œ CI/CD 作业的代ç†ã€‚è¦æ³¨å†Œæ–°çš„ runners,请è”系您的管ç†å‘˜ã€‚"
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr "è¿è¡Œæœªæ ‡è®°çš„作业"
@@ -35133,12 +35604,6 @@ msgstr "标签"
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr "标签控制 runner å¯ä»¥å¤„ç†çš„作业类型。通过为 runner 打标签,您å¯ä»¥ç¡®ä¿å…±äº« runners åªå¤„ç†ä»–们准备è¿è¡Œçš„工作。"
-msgid "Runners|Take me there!"
-msgstr "带我去那里ï¼"
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr "新视图为您æ供更多空间并更好地了解 runner 队ä¼ã€‚"
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr "注册了 runner 的项目ã€ç¾¤ç»„或实例。实例 runners 始终归管ç†å‘˜æ‰€æœ‰ã€‚"
@@ -35251,6 +35716,9 @@ msgstr "特定"
msgid "Runner|Owner"
msgstr "所有者"
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr "è¿è¡Œä¸­"
@@ -35278,14 +35746,17 @@ msgstr "SAML å•ç‚¹ç™»å½•"
msgid "SAML single sign-on for %{group_name}"
msgstr "%{group_name} çš„ SAML å•ç‚¹ç™»å½•"
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr "登录 GitLab 以连接您组织的å¸æˆ·"
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr "%{strongOpen}%{group_path}%{strongClose} 群组å…许您使用å•ç‚¹ç™»å½•è¿›è¡Œç™»å½•ã€‚"
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
-msgstr "è¦è®¿é—® %{strongOpen}%{group_name}%{strongClose},您必须通过外部登录页é¢ä½¿ç”¨å•ç‚¹ç™»å½•è¿›è¡Œç™»å½•ã€‚"
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
+msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
msgstr ""
@@ -35326,6 +35797,9 @@ msgstr "SSH主机密钥在此系统上ä¸å¯ç”¨ã€‚请使用%{ssh_keyscan}命令æ
msgid "SSH key"
msgstr "SSH密钥"
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr "SSH 密钥"
@@ -35341,6 +35815,15 @@ msgstr "具有以下指纹的 SSH 密钥已过期,无法å†ä½¿ç”¨ï¼š"
msgid "SSH public key"
msgstr "SSH公钥"
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr "SSL验è¯ï¼š"
@@ -35407,6 +35890,12 @@ msgstr "%{thenLabelStart}然åŽ%{thenLabelEnd} éœ€è¦ %{scan} 扫ææ‰èƒ½è¿è¡Œ
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr "%{thenLabelStart}然åŽ%{thenLabelEnd} éœ€è¦ %{scan} 扫ææ‰èƒ½ä½¿ç”¨ç«™ç‚¹é…置文件 %{siteProfile} 和扫æ器é…置文件 %{scannerProfile} æ¥è¿è¡Œ"
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr "æµæ°´çº¿è¿è¡Œä¸­"
@@ -35434,9 +35923,15 @@ msgstr "选择扫æ器é…置文件"
msgid "ScanExecutionPolicy|Select site profile"
msgstr "选择站点é…置文件"
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr "站点é…置文件"
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr "代ç†"
@@ -35449,18 +35944,27 @@ msgstr "在命å空间中"
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr "%{ifLabelStart}如果%{ifLabelEnd} %{scanners} 在针对 %{branches} 的开放åˆå¹¶è¯·æ±‚中å‘现超过 %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} çš„æ¼æ´ž"
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr "%{thenLabelStart}然åŽ%{thenLabelEnd} 需è¦ä»¥ä¸‹ %{approvalsRequired} 个核准人的批准:"
msgid "ScanResultPolicy|add an approver"
msgstr "添加核准人"
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr "扫æ器"
msgid "ScanResultPolicy|severity levels"
msgstr "严é‡ç¨‹åº¦"
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr "æ¼æ´žçŠ¶æ€"
@@ -35545,6 +36049,9 @@ msgstr "æœç´¢"
msgid "Search GitLab"
msgstr "æœç´¢ GitLab"
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr "æœç´¢ç¾¤ç»„"
@@ -35992,13 +36499,13 @@ msgid "SecurityOrchestration| or "
msgstr "或 "
msgid "SecurityOrchestration|%{agent} for %{namespaces}"
-msgstr ""
+msgstr "%{namespaces} 使用的 %{agent}"
msgid "SecurityOrchestration|%{branches} and %{lastBranch} branches"
-msgstr ""
+msgstr "%{branches} 和 %{lastBranch} 分支"
msgid "SecurityOrchestration|%{branches} branch"
-msgstr ""
+msgstr "%{branches} 分支"
msgid "SecurityOrchestration|%{scanners}"
msgstr "%{scanners}"
@@ -36051,6 +36558,9 @@ msgstr "确定è¦åˆ é™¤æ­¤ç­–ç•¥å—?此æ“作无法撤消。"
msgid "SecurityOrchestration|Choose a project"
msgstr "选择项目"
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr "创建更强大的æ¼æ´žè§„则并将其应用于您的所有项目。"
@@ -36108,9 +36618,18 @@ msgstr "加载镜åƒå¤±è´¥ã€‚"
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr "加载æ¼æ´žæ‰«æ器失败。"
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr "如果任何扫æ器在针对主分支的开放åˆå¹¶è¯·æ±‚中å‘现新检测的严é‡æ¼æ´žï¼Œç„¶åŽéœ€è¦ä»»ä½• App security æˆå‘˜çš„两次批准。"
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr "继承"
@@ -36126,6 +36645,9 @@ msgstr "无效的策略类型"
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr "针对 %{agent} è¿è¡Œçš„最新扫æ"
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr "新建策略"
@@ -36162,6 +36684,9 @@ msgstr "无法为ä¸å­˜åœ¨çš„分支å¯ç”¨ç­–ç•¥ (%{branches})"
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr "没有分支信æ¯å°±æ— æ³•å¯ç”¨ç­–ç•¥"
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr "策略定义"
@@ -36234,6 +36759,9 @@ msgstr "在%{branches}上扫ææ¯ä¸ªæµæ°´çº¿"
msgid "SecurityOrchestration|Security Approvals"
msgstr "安全批准"
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr "安全策略项目已æˆåŠŸè¿žæŽ¥"
@@ -36243,12 +36771,21 @@ msgstr "安全策略项目已æˆåŠŸå–消关è”"
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr "选择一个项目æ¥å­˜å‚¨æ‚¨çš„安全策略。 %{linkStart}更多信æ¯ã€‚%{linkEnd}"
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr "选择策略"
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr "选择安全项目"
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr "出现错误,无法获å–ç­–ç•¥"
@@ -36316,6 +36853,9 @@ msgid "SecurityOrchestration|Update scan policies"
msgstr "更新扫æç­–ç•¥"
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
+msgstr "使用扫æ执行策略æ¥åˆ›å»ºè§„则,在特定的时间对特定的分支强制进行安全扫æ。支æŒçš„类型有 SASTã€DASTã€Secret 检测ã€å®¹å™¨æ‰«æå’Œä¾èµ–扫æ。"
+
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
msgstr ""
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
@@ -36345,6 +36885,9 @@ msgstr "分支"
msgid "SecurityOrchestration|branches"
msgstr "分支"
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr "扫æ器å‘现"
@@ -36355,10 +36898,10 @@ msgid "SecurityOrchestration|the %{branches}"
msgstr "%{branches}"
msgid "SecurityOrchestration|the %{namespaces} and %{lastNamespace} namespaces"
-msgstr ""
+msgstr "%{namespaces} å’Œ %{lastNamespace} 命å空间"
msgid "SecurityOrchestration|the %{namespaces} namespace"
-msgstr ""
+msgstr "%{namespaces} 命å空间"
msgid "SecurityOrchestration|vulnerabilities"
msgstr "æ¼æ´ž"
@@ -36381,6 +36924,9 @@ msgstr "%{firstProject}和%{secondProject}"
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr "%{firstProject},%{secondProject},åŠ%{rest}"
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr "添加或删除安全区内的项目。 此列表中项目的扫æ结果将显示在安全仪表盘和æ¼æ´žæŠ¥å‘Šä¸­ã€‚"
@@ -36390,7 +36936,22 @@ msgstr "添加项目"
msgid "SecurityReports|All activity"
msgstr "所有活动"
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
+msgstr "全部严é‡çº§åˆ«"
+
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
msgstr ""
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
@@ -36523,7 +37084,7 @@ msgid "SecurityReports|More info"
msgstr "更多信æ¯"
msgid "SecurityReports|New vulnerabilities are vulnerabilities that the security scan detects in the merge request that are different to existing vulnerabilities in the default branch."
-msgstr ""
+msgstr "æ–°æ¼æ´žæ˜¯å®‰å…¨æ‰«æ在åˆå¹¶è¯·æ±‚中检测到的,与默认分支中的现有æ¼æ´žä¸åŒçš„æ¼æ´žã€‚"
msgid "SecurityReports|No activity"
msgstr "无活动"
@@ -36537,6 +37098,9 @@ msgstr "未å‘现æ¼æ´ž"
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr "æ­¤æµæ°´çº¿ä¸­æœªå‘现æ¼æ´ž"
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr "看起æ¥ä¸æ­£ç¡®ã€‚"
@@ -36724,7 +37288,7 @@ msgid "See our website for help"
msgstr "查看我们的网站以获å–帮助"
msgid "See the Geo troubleshooting documentation to learn more: %{docs_url}"
-msgstr ""
+msgstr "查看Geo故障排除文档以了解更多: %{docs_url}"
msgid "See the affected projects in the GitLab admin panel"
msgstr "查看 GitLab 管ç†é¢æ¿ä¸­çš„å—å½±å“项目"
@@ -36762,6 +37326,9 @@ msgstr "选择适用于该项目的åˆè§„框架。 %{linkStart}这些是如何æ·
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr "请先从左侧边æ é€‰æ‹©ä¸€ä¸ªæ–‡ä»¶å¼€å§‹ç¼–辑,然åŽå°±å¯ä»¥æ交您的更改了。"
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr "选择一个标记"
@@ -36867,6 +37434,9 @@ msgstr "选择报告"
msgid "Select reviewer(s)"
msgstr "选择审核者"
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr "选择æº"
@@ -36946,19 +37516,19 @@ msgid "SelfMonitoring|Activate or deactivate instance self-monitoring."
msgstr "å¯ç”¨æˆ–ç¦ç”¨å®žä¾‹è‡ªç›‘控。"
msgid "SelfMonitoring|Activate self-monitoring to create a project to use to monitor the health of your instance."
-msgstr ""
+msgstr "å¯ç”¨è‡ªç›‘控,创建一个用于监控您实例è¿è¡ŒçŠ¶å†µçš„项目。"
msgid "SelfMonitoring|Deactivate self-monitoring?"
msgstr "åœç”¨è‡ªç›‘控?"
msgid "SelfMonitoring|Deactivating self-monitoring deletes the self-monitoring project. Are you sure you want to deactivate self-monitoring and delete the project?"
-msgstr ""
+msgstr "åœç”¨è‡ªç›‘控将会删除自监控项目。您确定è¦åœç”¨è‡ªç›‘控并删除项目å—?"
msgid "SelfMonitoring|Self-monitoring"
msgstr "自监控"
msgid "SelfMonitoring|Self-monitoring is active. Use the %{projectLinkStart}self-monitoring project%{projectLinkEnd} to monitor the health of your instance."
-msgstr ""
+msgstr "自监控已å¯ç”¨ã€‚使用%{projectLinkStart}自监控项目%{projectLinkEnd}æ¥ç›‘控您的实例的è¿è¡ŒçŠ¶å†µã€‚"
msgid "SelfMonitoring|Self-monitoring project successfully created."
msgstr "自监控项目已æˆåŠŸåˆ›å»ºã€‚"
@@ -37354,6 +37924,9 @@ msgstr "分享%{strong_open}GitLabå•ç‚¹ç™»å½•ç½‘å€%{strong_close}给群组æˆå
msgid "Shared Runners"
msgstr "共享Runner"
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr "共享项目"
@@ -37480,6 +38053,9 @@ msgstr "显示最新版本"
msgid "Show list"
msgstr "显示列表"
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr "一次显示一个文件"
@@ -37584,7 +38160,7 @@ msgid "Showing all issues"
msgstr "已显示全部议题"
msgid "Showing data for workflow items completed in this date range. Date range limited to %{maxDateRange} days."
-msgstr ""
+msgstr "显示在此日期范围内完æˆçš„工作æµé¡¹ç›®çš„æ•°æ®ã€‚日期范围最多 %{maxDateRange} 天。"
msgid "Showing last %{size} of log -"
msgstr "显示日志的最åŽ%{size} -"
@@ -37637,9 +38213,6 @@ msgstr "以具有匹é…电å­é‚®ä»¶åœ°å€çš„用户身份登录,将电å­é‚®ä»¶
msgid "Sign in preview"
msgstr "登录预览"
-msgid "Sign in to %{group_name}"
-msgstr "登录到 %{group_name}"
-
msgid "Sign in to GitLab"
msgstr "登录到GitLab"
@@ -37793,8 +38366,8 @@ msgstr "Slack集æˆå…许您通过èŠå¤©çª—å£ä¸­çš„指令与GitLab交互。"
msgid "Slack logo"
msgstr "Slack logo"
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
-msgstr "您确定è¦ä»Ž Slack 应用程åºä¸­åˆ é™¤æ­¤é¡¹ç›®å—?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
+msgstr ""
msgid "SlackIntegration|Client ID"
msgstr "客户端 ID"
@@ -37808,14 +38381,17 @@ msgstr "GitLab for Slack"
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr "GitLab for Slack å·²æˆåŠŸå®‰è£…。"
-msgid "SlackIntegration|Install Slack app"
-msgstr "安装 Slack 应用程åº"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
+msgstr ""
msgid "SlackIntegration|Project alias"
msgstr "项目别å"
-msgid "SlackIntegration|Reinstall Slack app"
-msgstr "é‡æ–°å®‰è£… Slack 应用程åº"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
+msgstr ""
msgid "SlackIntegration|Remove project"
msgstr "删除项目"
@@ -37838,14 +38414,17 @@ msgstr "团队å称"
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr "此集æˆå…许用户通过在 Slack 中输入指令æ¥å¯¹è¯¥é¡¹ç›®æ‰§è¡Œå¸¸ç”¨æ“作。"
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr "验è¯ä»¤ç‰Œ"
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr "您现在å¯ä»¥å…³é—­æ­¤çª—å£å¹¶è½¬åˆ°æ‚¨çš„ Slack 工作区。"
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
-msgstr "%{linkStart}更新或更改æƒé™%{linkEnd}时,您å¯èƒ½éœ€è¦é‡æ–°å®‰è£… Slack 应用程åºã€‚"
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
msgstr "1. %{slash_command_link_start}在您的Slack团队中使用此信æ¯æ·»åŠ ä¸€ä¸ªæ–œæ å‘½ä»¤%{slash_command_link_end}:"
@@ -37982,12 +38561,12 @@ msgstr "æŸäº›å­å²è¯—å¯èƒ½ç”±äºŽåº”用筛选器而被éšè—"
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr "æŸäº›å¸¸è§åŸŸåä¸è¢«å…许。%{learn_more_link}"
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
+msgstr ""
+
msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr "有人在您编辑文件的åŒæ—¶ç¼–辑了该文件。请检查 %{link_start}文件 %{icon}%{link_end} 并确ä¿æ‚¨çš„更改ä¸ä¼šæ— æ„中删除他们的更改。"
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
-msgstr "其他用户åŒæ—¶ç¼–辑了这个议题。请检查%{linkStart}议题%{linkEnd}并确ä¿æ‚¨çš„更改ä¸ä¼šæ— æ„中移除他们的更改。"
-
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr "有人与您åŒæ—¶ç¼–辑了%{issueType} 。æ述已被更新,您需è¦é‡æ–°ä¿®æ”¹ã€‚"
@@ -38001,7 +38580,7 @@ msgid "Someone signed in to your %{host} account from a new location"
msgstr "有人从新ä½ç½®ç™»å½•åˆ°æ‚¨çš„ %{host} å¸æˆ·"
msgid "Someone, hopefully you, has requested to reset the password for your GitLab account on %{link_to_gitlab}."
-msgstr "有人(希望是您)è¦æ±‚在 %{link_to_gitlab}上é‡ç½®æ‚¨çš„ GitLab è´¦å·çš„密ç ã€‚"
+msgstr "有人(希望是您)è¦æ±‚在 %{link_to_gitlab} 上é‡ç½®æ‚¨çš„ GitLab è´¦å·çš„密ç ã€‚"
msgid "Something went wrong"
msgstr "出错了"
@@ -38027,6 +38606,9 @@ msgstr "å°è¯•åŠ è½½è®®é¢˜è”系人时出错。"
msgid "Something went wrong when reordering designs. Please try again"
msgstr "é‡æ–°æŽ’åºè®¾è®¡æ—¶å‡ºäº†ç‚¹é—®é¢˜ã€‚请å†è¯•ä¸€æ¬¡"
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr "添加时间线事件时出错。"
@@ -38045,6 +38627,9 @@ msgstr "归档需求时出了错。"
msgid "Something went wrong while closing the epic. Please try again later."
msgstr "关闭å²è¯—时出了错。请ç¨åŽå†è¯•ã€‚"
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr "关闭åˆå¹¶è¯·æ±‚时出错。请ç¨åŽå†è¯•ã€‚"
@@ -38114,6 +38699,9 @@ msgstr "获å–软件包列表时出错。"
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr "获å–Let's Encryptè¯ä¹¦æ—¶å‡ºäº†é”™ã€‚"
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr "将议题å‡çº§åˆ°å²è¯—时出错。请å†è¯•ä¸€æ¬¡ã€‚"
@@ -38507,6 +39095,9 @@ msgstr "通过Needs关系加速您的æµæ°´çº¿"
msgid "Spent at"
msgstr "花费在"
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr "压缩æ交消æ¯"
@@ -38612,6 +39203,9 @@ msgstr "æµæ°´çº¿æˆåŠŸæ—¶å¯åŠ¨åˆå¹¶é˜Ÿåˆ—"
msgid "Start merge train..."
msgstr "å¯åŠ¨åˆå¹¶é˜Ÿåˆ—..."
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr "开始æœç´¢"
@@ -38675,6 +39269,9 @@ msgstr "统计"
msgid "Status"
msgstr "状æ€"
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr "状æ€å·²é‡è¯•ã€‚"
@@ -39411,6 +40008,9 @@ msgstr "您无法å†å°†è®¢é˜…详细信æ¯ä¸Ž GitLab åŒæ­¥ã€‚ %{connectivityHelp
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr "您å¯ä»¥å¼€å§‹å…费试用 GitLab 旗舰版,无需任何承诺或付款信æ¯ã€‚"
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr "您没有有效订阅"
@@ -39501,9 +40101,6 @@ msgstr "切æ¢åˆ†æ”¯"
msgid "Switch branch/tag"
msgstr "切æ¢åˆ†æ”¯/标签"
-msgid "Switch editors"
-msgstr "切æ¢ç¼–辑器"
-
msgid "Switch to GitLab Next"
msgstr "切æ¢åˆ°GitLab 预览版"
@@ -39786,6 +40383,12 @@ msgstr "创建/导入议题(工å•ï¼‰ï¼Œå作想法和规划工作"
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr "设置 CI/CD æµæ°´çº¿æ¥æž„建ã€æµ‹è¯•ã€éƒ¨ç½²å’Œç›‘视代ç "
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr "团队"
@@ -40361,6 +40964,9 @@ msgstr "管ç†å‘˜ %{username}撤消了以下个人访问令牌。"
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr "以下SSH密钥已由管ç†å‘˜ %{username}删除。"
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr "以下项目将ä¸ä¼šè¢«å¯¼å‡ºï¼š"
@@ -40723,9 +41329,6 @@ msgstr "没有垃圾邮件日志"
msgid "There are no abuse reports!"
msgstr "没有滥用报告ï¼"
-msgid "There are no archived projects yet"
-msgstr "当å‰å°šæ— å·²å½’档的项目"
-
msgid "There are no archived requirements"
msgstr "没有已归档的需求"
@@ -40783,9 +41386,6 @@ msgstr "没有开放的测试用例"
msgid "There are no packages yet"
msgstr "还没有包"
-msgid "There are no projects shared with this group yet"
-msgstr "当å‰å°šæ— åˆ†äº«ç»™è¯¥ç¾¤ç»„的项目"
-
msgid "There are no secure files yet."
msgstr "尚无安全文件。"
@@ -40831,6 +41431,9 @@ msgstr "æ•°æ®å¤ªå¤šæ— æ³•è®¡ç®—。请更改您的选择。"
msgid "There was a problem communicating with your device."
msgstr "与您的设备通信时出现问题。"
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr "èŽ·å– CRM è”系人时出错。"
@@ -40951,6 +41554,9 @@ msgstr "获å–部署冻结时出错。"
msgid "There was an error fetching the environments information."
msgstr "获å–环境信æ¯æ—¶å‡ºé”™ã€‚"
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr "为您的项目获å–作业时出错。"
@@ -40990,6 +41596,9 @@ msgstr "é‡ç½®ç”¨æˆ·æµæ°´çº¿åˆ†é’Ÿæ•°æ—¶å‡ºé”™ã€‚"
msgid "There was an error retrieving the Jira users."
msgstr "获å–Jira用户时出错。"
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr "ä¿å­˜å˜æ›´æ—¶å‡ºé”™ã€‚"
@@ -41083,9 +41692,6 @@ msgstr "因为 %{reason}无法显示 %{viewer} 。您å¯ä»¥æ”¹ä¸º %{options}。"
msgid "This Cron pattern is invalid"
msgstr "æ­¤Cronæ ¼å¼æ— æ•ˆ"
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr "æ­¤ GitLab 实例尚未æ供任何共享 Runner,实例管ç†å‘˜å¯ä»¥åœ¨ç®¡ç†ä¸­å¿ƒæ³¨å†Œå…±äº«Runner。"
@@ -41275,6 +41881,9 @@ msgstr "æ­¤å²è¯—ä¸å­˜åœ¨æˆ–者您没有足够的æƒé™ã€‚"
msgid "This epic would exceed maximum number of related epics."
msgstr "æ­¤å²è¯—将超过相关å²è¯—的最大数é‡ã€‚"
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr "此功能需è¦æœ¬åœ°å­˜å‚¨ä»¥å¯ç”¨"
@@ -41293,8 +41902,8 @@ msgstr "此表å•åœ¨é¢„览中被ç¦ç”¨"
msgid "This group"
msgstr "当å‰ç¾¤ç»„"
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
-msgstr "该组åŠå…¶å­ç»„和项目将在 %{deletion_adjourned_period} 天内处于“待删除â€çŠ¶æ€ï¼Œç„¶åŽåœ¨ %{date} 被永久删除。该群组å¯ä»¥åœ¨è¯¥æ—¥æœŸä¹‹å‰å®Œå…¨æ¢å¤ã€‚"
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
msgstr "此群组ä¸èƒ½è¢«åˆ é™¤ï¼Œå› ä¸ºå®ƒå·²é“¾æŽ¥åˆ°ä¸€ä¸ªè®¢é˜…,è¦åˆ é™¤æ­¤ç¾¤ç»„, %{linkStart}将订阅%{linkEnd} 链接到å¦ä¸€ä¸ªç¾¤ç»„。"
@@ -41540,7 +42149,7 @@ msgid "This only applies to repository indexing operations."
msgstr "è¿™åªé€‚用于仓库索引æ“作。"
msgid "This page is hosted on GitLab pages but contains user-generated content and may contain malicious code. Do not accept unless you trust the author and source."
-msgstr ""
+msgstr "此页é¢æ‰˜ç®¡åœ¨ GitLab Pages上,但其包å«ç”±ä½¿ç”¨è€…编写的内容,并且å¯èƒ½åŒ…å«æ¶æ„代ç ã€‚除éžæ‚¨ä¿¡ä»»ä½œè€…å’Œæ¥æºï¼Œå¦åˆ™è¯·å‹¿æŽ¥å—。"
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr "此页é¢ä¸å¯ç”¨ï¼Œæ‚¨æ— æƒè·¨é¡¹ç›®é˜…读相关信æ¯ã€‚"
@@ -41690,7 +42299,7 @@ msgid "This variable can not be masked."
msgstr "æ­¤å˜é‡æ— æ³•è¢«éšè—。"
msgid "This vulnerability type has been deprecated from GitLab's default ruleset and automatically resolved."
-msgstr ""
+msgstr "该æ¼æ´žç±»åž‹å·²ä»Ž GitLab 的默认规则集中弃用并自动解决。"
msgid "This will invalidate your registered applications and U2F / WebAuthn devices."
msgstr "这会使您注册的应用程åºå’Œ U2F / WebAuthn 设备无效。"
@@ -41767,6 +42376,12 @@ msgstr "剩余时间:"
msgid "Time spent"
msgstr "花费的时间"
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr "æ¢å¤æœåŠ¡æ—¶é—´"
@@ -42015,6 +42630,9 @@ msgstr "标题(必填)"
msgid "Title:"
msgstr "标题:"
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr "标题和æè¿°"
@@ -42048,9 +42666,6 @@ msgstr "è¦æ¿€æ´»æ‚¨çš„试用,我们需è¦æ‚¨æ供更多的详细信æ¯ã€‚"
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr "è¦æ·»åŠ è‡ªå®šä¹‰åŽç¼€ï¼Œè¯·è®¾ç½®æœåŠ¡å°ç”µå­é‚®ä»¶åœ°å€ã€‚%{linkStart}了解更多。%{linkEnd}"
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr "è¦æ·»åŠ æ˜¾ç¤ºå称,请设置æœåŠ¡å°ç”µå­é‚®ä»¶åœ°å€ã€‚%{linkStart}了解更多。%{linkEnd}"
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr "如需手动添加æ¡ç›®ï¼Œè¯·åœ¨æ‰‹æœºåº”用中æ供以下信æ¯ã€‚"
@@ -42165,6 +42780,9 @@ msgstr "è¦é‡æ–°æ¿€æ´»æ‚¨çš„å¸æˆ·ï¼Œ %{gitlab_link_start}登录到 GitLab。%{
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr "è¦é‡æ–°æ¿€æ´»æ‚¨çš„å¸æˆ·ï¼Œè¯·åœ¨ %{gitlab_url}登录 GitLab。"
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42258,6 +42876,9 @@ msgstr "无法åˆå¹¶"
msgid "Todos|Design"
msgstr "设计"
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr "å²è¯—"
@@ -42291,6 +42912,9 @@ msgstr "这是您始终知é“下一步该åšä»€ä¹ˆçš„æ–¹å¼ã€‚"
msgid "Todos|Mark all as done"
msgstr "标记全部完æˆ"
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr "å·²æåŠ"
@@ -42306,14 +42930,14 @@ msgstr "没有需è¦åšçš„事情。真棒ï¼"
msgid "Todos|Pipelines"
msgstr "æµæ°´çº¿"
-msgid "Todos|Removed from Merge Train:"
-msgstr "已从åˆå¹¶é˜Ÿåˆ—中删除:"
+msgid "Todos|Removed from Merge Train"
+msgstr ""
msgid "Todos|Review requested"
msgstr "已请求审核"
-msgid "Todos|The pipeline failed in"
-msgstr "æµæ°´çº¿å¤±è´¥"
+msgid "Todos|The pipeline failed"
+msgstr ""
msgid "Todos|Undo mark all as done"
msgstr "撤销标记全部完æˆ"
@@ -42327,24 +42951,24 @@ msgstr "å·²ç»å…¨éƒ¨å®Œæˆäº†ï¼"
msgid "Todos|Your To-Do List shows what to work on next"
msgstr "您的待办事项列表显示下一步è¦åšä»€ä¹ˆ"
-msgid "Todos|added a todo for"
-msgstr "新增一个待办事项"
+msgid "Todos|added a to-do item"
+msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
-msgstr "请求审核"
+msgid "Todos|mentioned %{who}"
+msgstr ""
+
+msgid "Todos|requested a review"
+msgstr ""
-msgid "Todos|set %{who} as an approver for"
-msgstr "设置 %{who} 为核准人"
+msgid "Todos|set %{who} as an approver"
+msgstr ""
msgid "Todos|yourself"
msgstr "您自己"
-msgid "Todo|at %{todo_parent_path}"
-msgstr "在 %{todo_parent_path}"
-
msgid "Toggle GitLab Next"
msgstr "切æ¢GitLab Next"
@@ -42712,6 +43336,12 @@ msgstr "触å‘器已æˆåŠŸæ›´æ–°ã€‚"
msgid "Triggerer"
msgstr "触å‘者"
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr "触å‘用户没有足够的项目æƒé™"
@@ -42748,6 +43378,9 @@ msgstr "å°è¯•ä½¿ç”¨æ‚¨çš„用户å或电å­é‚®ä»¶ç™»å½•ã€‚如果您忘记了密
msgid "Try out GitLab Pipelines"
msgstr "试用 GitLab æµæ°´çº¿"
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr "å°è¯•æ­¤å¤„的故障排除步骤。"
@@ -42925,6 +43558,12 @@ msgstr "无法获å–此项目的分支列表。"
msgid "Unable to fetch branches list, please close the form and try again"
msgstr "无法获å–分支列表,请关闭表å•å¹¶é‡è¯•"
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr "无法获å–上游和下游æµæ°´çº¿ã€‚"
@@ -43048,6 +43687,9 @@ msgstr "很é—憾,您å‘é€ç»™GitLab的电å­é‚®ä»¶æ— æ³•å¤„ç†ã€‚"
msgid "Unhappy?"
msgstr "ä¸å–œæ¬¢ï¼Ÿ"
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr "毫秒"
@@ -43129,6 +43771,9 @@ msgstr "未解决的"
msgid "Unschedule job"
msgstr "å–消作业计划"
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr "å–消星标"
@@ -43312,6 +43957,9 @@ msgstr "正在将å˜æ›´å†…容上传到终端"
msgid "Uploading..."
msgstr "正在上传..."
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr "上游"
@@ -43330,18 +43978,12 @@ msgstr "Container Registry 的项目级存储统计信æ¯ä»…具有方å‘性,ä¸
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr "此项目级存储统计ä¸åŒ…括站点范围é‡å¤æ•°æ®çš„删除,ä¸ç”¨äºŽè®¡ç®—总命å空间存储。"
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr "%{help_link_start}共享Runner%{help_link_end}å·²ç¦ç”¨ï¼Œæ‰€ä»¥æµæ°´çº¿ä½¿ç”¨æ²¡æœ‰è®¾ç½®é™åˆ¶"
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr "%{linkStart}共享 runner%{linkEnd} å·²ç¦ç”¨ï¼Œå› æ­¤å¯¹æµæ°´çº¿ä½¿ç”¨æ²¡æœ‰é™åˆ¶"
msgid "UsageQuota|%{linkTitle} help link"
msgstr "%{linkTitle} 帮助链接"
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr "%{percentageLeft}购买的存储空间å¯ç”¨"
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr "%{storage_limit_link_start}命å空间存储é™åˆ¶%{link_end}将很快在 %{strong_start}%{namespace_name}%{strong_end} 命å空间中执行。%{extra_message}"
@@ -43369,9 +44011,6 @@ msgstr "CI 分钟使用é‡ï¼ˆæŒ‰é¡¹ç›®ï¼‰"
msgid "UsageQuota|CI/CD minutes usage"
msgstr "CI/CD 分钟数使用é‡"
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr "%{timeElapsed} 以æ¥çš„ CI/CD 分钟数使用é‡"
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr "%{usageSince} 以æ¥çš„ CI/CD 分钟数使用é‡"
@@ -43381,9 +44020,6 @@ msgstr "代ç åŒ…和容器镜åƒã€‚"
msgid "UsageQuota|Container Registry"
msgstr "容器镜åƒåº“"
-msgid "UsageQuota|Current period usage"
-msgstr "当å‰å‘¨æœŸä½¿ç”¨é‡"
-
msgid "UsageQuota|Dependency proxy"
msgstr "ä¾èµ–代ç†"
@@ -43510,11 +44146,8 @@ msgstr "命å空间目å‰æ­£åœ¨ä½¿ç”¨ %{strong_start}%{used_storage}%{strong_en
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr "命å空间当å‰æ­£åœ¨ä½¿ç”¨ %{strong_start}%{used_storage}%{strong_end} 的命å空间存储。从 %{strong_start}%{usage_quotas_nav_instruction}%{strong_end} 查看和管ç†æ‚¨çš„使用情况。%{docs_link_start}了解更多%{link_end}有关如何å‡å°‘存储空间的信æ¯ã€‚"
-msgid "UsageQuota|The table below shows current period usage"
-msgstr "下表显示当å‰æ—¶é—´æ®µä½¿ç”¨æƒ…况"
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
-msgstr "下表显示 %{timeElapsed} 以æ¥çš„使用情况"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
+msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
msgstr "这是此å称空间中所有项目使用的存储空间总和。"
@@ -43522,12 +44155,6 @@ msgstr "这是此å称空间中所有项目使用的存储空间总和。"
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr "这是超出项目å…è´¹%{actualRepositorySizeLimit}存储é™åˆ¶çš„存储空间。"
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr "此命å空间包å«å·²é”定的项目"
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr "此命å空间没有使用共享Runner的项目"
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr "这个命å空间没有在当å‰é˜¶æ®µä½¿ç”¨å…±äº« runner 的项目"
@@ -43540,9 +44167,6 @@ msgstr "已使用超é‡å­˜å‚¨ç©ºé—´æ€»è®¡"
msgid "UsageQuota|Total namespace storage used"
msgstr "已使用命å空间存储总计"
-msgid "UsageQuota|Unlimited"
-msgstr "æ— é™"
-
msgid "UsageQuota|Uploads"
msgstr "上传"
@@ -43576,8 +44200,8 @@ msgstr "使用é…é¢å¸®åŠ©é“¾æŽ¥"
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr "用户设置 &gt; 使用é‡é…é¢"
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
-msgstr "当您购买更多的存储空间时,我们会自动解é”达到%{actualRepositorySizeLimit}é™åˆ¶æ—¶è¢«é”定的项目。"
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
+msgstr ""
msgid "UsageQuota|Wiki"
msgstr "Wiki"
@@ -43585,17 +44209,17 @@ msgstr "Wiki"
msgid "UsageQuota|Wiki content."
msgstr "Wiki 内容。"
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
-msgstr "您已ç»ç”¨å°½æ‰€æœ‰æ›´å¤šçš„存储空间,请购买更多空间以解é”超过å…è´¹%{actualRepositorySizeLimit}é™åˆ¶çš„项目。"
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
+msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
-msgstr "您已ç»åœ¨%{projectsLockedText}达到存储空间é™åˆ¶%{actualRepositorySizeLimit}。如需解é”,请购买更多存储空间。"
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
+msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr "您已使用: %{usage} %{limit}"
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
-msgstr "您购买的存储空间已在低ä½ã€‚为了é¿å…项目被é”定,请购买更多存储空间。"
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
+msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
msgstr ",您命å空间存储上é™æ€»è®¡%{formattedLimit}"
@@ -43768,6 +44392,9 @@ msgstr "使用公共云实例 URL (%{kroki_public_url}) 或 %{install_link_start
msgid "Use the search bar on the top of this page"
msgstr "使用本页顶部的æœç´¢æ "
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr "使用此令牌æ¥éªŒè¯æ”¶åˆ°çš„有效数æ®ã€‚"
@@ -44033,9 +44660,6 @@ msgstr "个人项目"
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr "读作: %{pronunciation}"
-msgid "UserProfile|Report abuse"
-msgstr "举报滥用行为"
-
msgid "UserProfile|Retry"
msgstr "é‡è¯•"
@@ -44225,6 +44849,9 @@ msgstr "当缺少加密字段时,使用必è¦çš„加密策略ï¼"
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr "使用%{codeStart}needs%{codeEnd}关键字会使作业比其所在阶段更早è¿è¡Œã€‚åªè¦å®ƒä»¬çš„%{codeStart}needs%{codeEnd}关系得到满足,作业将尽快执行,从而实现æµæ°´çº¿åŠ é€Ÿã€‚"
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr "有效期自"
@@ -44297,6 +44924,9 @@ msgstr "DORA 指标"
msgid "ValueStreamAnalytics|Dashboard"
msgstr "仪表盘"
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr "转到文档"
@@ -44315,6 +44945,9 @@ msgstr "从议题创建到关闭的中ä½æ•°æ—¶é—´ã€‚"
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr "从一个关è”议题的åˆå¹¶è¯·æ±‚最早æ交到该议题结æŸæ—¶çš„中ä½æ—¶é—´ã€‚"
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr "推é€åˆ°é»˜è®¤åˆ†æ”¯çš„æ交数é‡"
@@ -44363,12 +44996,12 @@ msgstr "åœæ­¢"
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr "默认价值æµä¸å¯åˆ é™¤"
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
-msgstr "åŒ…å« %{codeStart}$%{codeEnd} 字符的值å¯ä»¥è¢«è§†ä¸ºå˜é‡å¼•ç”¨å¹¶è¿›è¡Œæ‰©å±•ã€‚%{docsLinkStart}了解更多。%{docsLinkEnd}"
-
msgid "Variable"
msgstr "å˜é‡"
+msgid "Variable value will be evaluated as raw string."
+msgstr ""
+
msgid "Variable will be masked in job logs."
msgstr "å˜é‡å€¼å°†åœ¨ä½œä¸šæ—¥å¿—中被éšè—。"
@@ -44378,8 +45011,8 @@ msgstr "å˜é‡"
msgid "Variables can be:"
msgstr "å˜é‡å¯ä»¥æ˜¯ï¼š"
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
-msgstr "å˜é‡å­˜å‚¨ä¿¡æ¯ï¼Œå¦‚密ç å’Œå¯†é’¥ï¼Œæ‚¨å¯ä»¥åœ¨ä½œä¸šè„šæœ¬ä¸­ä½¿ç”¨ã€‚"
+msgid "Variables can have several attributes."
+msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
msgstr "å˜é‡å­˜å‚¨ä¿¡æ¯ï¼Œå¦‚密ç å’Œå¯†é’¥ï¼Œæ‚¨å¯ä»¥åœ¨ä½œä¸šè„šæœ¬ä¸­ä½¿ç”¨ã€‚实例上的所有项目都å¯ä»¥ä½¿ç”¨è¿™äº›å˜é‡ã€‚"
@@ -44435,6 +45068,21 @@ msgstr "版本 %{versionNumber}"
msgid "Version %{versionNumber} (latest)"
msgstr "版本 %{versionNumber} (最新)"
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr "最新"
@@ -44444,6 +45092,18 @@ msgstr "尽快更新"
msgid "VersionCheck|Update available"
msgstr "æ›´æ–°å¯ç”¨"
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr "您的 GitLab 版本"
@@ -44976,7 +45636,7 @@ msgid "Vulnerability|Scanner Provider"
msgstr "扫æ工具æ供者"
msgid "Vulnerability|Scanner:"
-msgstr ""
+msgstr "扫æ器:"
msgid "Vulnerability|Security Audit"
msgstr "安全审计"
@@ -45101,6 +45761,9 @@ msgstr "我们在%{humanized_resource_name}检测到潜在滥用行为。请输å
msgid "We don't have enough data to show this stage."
msgstr "该阶段的数æ®ä¸è¶³ï¼Œæ— æ³•æ˜¾ç¤ºã€‚"
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr "我们å‘现以下错误:"
@@ -45135,7 +45798,7 @@ msgid "We want to be sure it is you, please confirm you are not a robot."
msgstr "我们è¦ç¡®å®šæ‚¨æ˜¯ä¸æ˜¯æœºå™¨äººã€‚"
msgid "We want to let you know %{username} has exceeded the Git rate limit due to them downloading more than %{max_project_downloads} project %{repositories_text} within %{within_text}."
-msgstr ""
+msgstr "我们想è¦è®©æ‚¨çŸ¥é“ %{username} å·²ç»è¶…过了 Git 速率é™åˆ¶ï¼Œå› ä¸ºä»–们在 %{within_text} 内下载了项目 %{repositories_text} 超过 %{max_project_downloads} 次。"
msgid "We will notify %{inviter} that you declined their invitation to join GitLab. You will stop receiving reminders."
msgstr "我们会通知%{inviter} ,您拒ç»äº†æ³¨å†ŒGitLab的邀请。您将ä¸ä¼šç»§ç»­æ”¶åˆ°æ醒。"
@@ -45176,9 +45839,6 @@ msgstr "WebAuthn设备 (%{length})"
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr "WebAuthnåªæ”¯æŒå¯ç”¨äº†HTTPS的网站。您å¯ä»¥è”系管ç†å‘˜èŽ·å¾—更多信æ¯"
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr "您确定è¦åˆ‡æ¢ç¼–辑器å—?您将丢失任何未ä¿å­˜çš„更改。"
-
msgid "WebIDE|Fork project"
msgstr "Fork 项目"
@@ -45194,24 +45854,12 @@ msgstr "快速轻æ¾åœ°ç¼–辑您项目中的多个文件。"
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr "轻快地编辑您项目中的多个文件。按 . 打开"
-msgid "WebIDE|Ready for something new?"
-msgstr "准备好迎接新功能了å—?"
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr "更新用户å好设置时出错。请查看开å‘者控制å°äº†è§£è¯¦æƒ…。"
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr "切æ¢åˆ°æ–°çš„ Web IDE"
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr "此项目ä¸æŽ¥å—未签åçš„æ交。"
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr "此项目ä¸æŽ¥å—未签åçš„æ交,您无法通过 Web IDE æ交更改。"
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr "您被邀请体验新的 Web IDE。"
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr "您ä¸èƒ½åœ¨è¿™ä¸ªé¡¹ç›®ä¸­ç›´æŽ¥ç¼–辑文件,请派生(Fork)这个项目并æ交åˆå¹¶è¯·æ±‚。"
@@ -45242,6 +45890,18 @@ msgstr "Webhook设置"
msgid "Webhook events will be displayed here."
msgstr "Webhook 事件将在这里显示。"
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr "Webhook:"
@@ -45308,9 +45968,6 @@ msgstr "您确实需è¦åˆ é™¤è¿™ä¸ªé¡¹ç›® hook å—?"
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr "您确实需è¦åˆ é™¤è¿™ä¸ª webhook å—?"
-msgid "Webhooks|Comments"
-msgstr "评论"
-
msgid "Webhooks|Confidential comments"
msgstr "ç§å¯†è¯„论"
@@ -45359,15 +46016,12 @@ msgstr "æˆå‘˜äº‹ä»¶"
msgid "Webhooks|Merge request events"
msgstr "åˆå¹¶è¯·æ±‚事件"
+msgid "Webhooks|Must match part of URL"
+msgstr ""
+
msgid "Webhooks|Pipeline events"
msgstr "æµæ°´çº¿äº‹ä»¶"
-msgid "Webhooks|Push events"
-msgstr "推é€äº‹ä»¶"
-
-msgid "Webhooks|Push to the repository."
-msgstr "推é€åˆ°ä»“库。"
-
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
msgstr "支æŒä¾‹å¦‚ %{REGEX_CODE} 的正则表达å¼ã€‚"
@@ -45404,9 +46058,6 @@ msgstr "Webhook 连接失败,已ç¦ç”¨ã€‚è¦é‡æ–°å¯ç”¨å®ƒï¼Œè¯·æ£€æŸ¥ %{stro
msgid "Webhooks|Trigger"
msgstr "触å‘æ¥æº"
-msgid "Webhooks|URL"
-msgstr "网å€"
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr "如果 URL 包å«ä¸€ä¸ªæˆ–多个特殊字符,则必须进行百分å·ç¼–ç ã€‚"
@@ -45796,6 +46447,9 @@ msgstr "将被创建"
msgid "Will be mapped to"
msgstr "将被映射到"
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr "将部署到"
@@ -45805,9 +46459,6 @@ msgstr "线框图"
msgid "With requirements, you can set criteria to check your products against."
msgstr "æ过需求,您å¯ä»¥è®¾ç½®æ ‡å‡†æ¥æ£€æŸ¥æ‚¨çš„产å“。"
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr "使用测试用例,您å¯ä»¥å®šä¹‰é¡¹ç›®çš„æ¡ä»¶ä»¥å†³å®šè´¨é‡"
-
msgid "Withdraw Access Request"
msgstr "å–消æƒé™ç”³è¯·"
@@ -45826,6 +46477,12 @@ msgstr "%{workItemType} 已删除"
msgid "WorkItem|Add"
msgstr "添加"
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr "添加标题"
@@ -45841,9 +46498,6 @@ msgstr "添加截止日期"
msgid "WorkItem|Add start date"
msgstr "添加起始日期"
-msgid "WorkItem|Add task"
-msgstr "添加任务"
-
msgid "WorkItem|Add to iteration"
msgstr "添加到迭代"
@@ -45863,6 +46517,9 @@ msgstr[0] "指派人"
msgid "WorkItem|Cancel"
msgstr "å–消"
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr "已删除å­é¡¹"
@@ -45872,6 +46529,12 @@ msgstr "已关闭"
msgid "WorkItem|Collapse tasks"
msgstr "折å ä»»åŠ¡"
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr "创建任务"
@@ -45884,30 +46547,39 @@ msgstr "日期"
msgid "WorkItem|Delete %{workItemType}"
msgstr "删除 %{workItemType}"
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr "截止日期"
+msgid "WorkItem|Existing task"
+msgstr ""
+
msgid "WorkItem|Expand tasks"
msgstr "展开任务"
msgid "WorkItem|Incident"
msgstr "事件"
-msgid "WorkItem|Introducing tasks"
-msgstr "介ç»ä»»åŠ¡"
-
msgid "WorkItem|Issue"
msgstr "议题"
msgid "WorkItem|Iteration"
msgstr "迭代"
-msgid "WorkItem|Learn about tasks."
-msgstr "了解任务详情。"
+msgid "WorkItem|Key result"
+msgstr ""
msgid "WorkItem|Milestone"
msgstr "里程碑"
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr "无迭代"
@@ -45917,12 +46589,18 @@ msgstr "没有匹é…的结果"
msgid "WorkItem|No milestone"
msgstr "无里程碑"
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr "当å‰æœªè¢«åˆ†é…任何任务。使用任务功能将这个议题分解æˆæ›´å°çš„部分。"
msgid "WorkItem|None"
msgstr "æ— "
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr "åªæœ‰ä½œè€…ã€æŒ‡æ´¾äººå’Œè‡³å°‘是报告者的项目æˆå‘˜ï¼Œå¯ä»¥æŸ¥çœ‹æˆ–收到有关此任务的通知。"
@@ -45935,9 +46613,18 @@ msgstr "删除"
msgid "WorkItem|Requirements"
msgstr "è¦æ±‚"
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr "选择类型"
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr "创建 %{workItemType} 时出现问题。请å†è¯•ä¸€æ¬¡ã€‚"
@@ -46001,8 +46688,8 @@ msgstr "å¼€å¯ç§å¯†"
msgid "WorkItem|Undo"
msgstr "撤消"
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
-msgstr "使用任务功能,将您在一个议题中的工作划分æˆæ›´å°çš„部分。%{learnMoreLink}"
+msgid "WorkItem|View current version"
+msgstr ""
msgid "WorkItem|Work Items"
msgstr "工作事项"
@@ -46031,6 +46718,9 @@ msgstr "å‘表评论..."
msgid "Write a description or drag your files here…"
msgstr "写下æ述或将您的文件拖到此处…"
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr "写说明..."
@@ -46197,11 +46887,11 @@ msgstr "您登录的身份:"
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr "您正在å°è¯•ä¸Šä¼ éžå›¾ç‰‡æ–‡ä»¶ã€‚请上传.pngã€.jpgã€.jpegã€.gifã€.bmpã€.tiff或.ico。"
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
-msgstr "您å¯ä»¥%{gitlabLinkStart}在 GitLab 上解决冲çª%{gitlabLinkEnd} 或 %{resolveLocallyStart}本地解决%{resolveLocallyEnd}。"
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
+msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
-msgstr "您å¯ä»¥ %{resolveLocallyStart}在本地解决它%{resolveLocallyEnd}。"
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
+msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
msgstr "您å¯ä»¥%{link_start}在此处%{link_end}调整自动å°ç¦çš„规则。"
@@ -46239,10 +46929,6 @@ msgstr "您å¯ä»¥ç¨åŽæ›´æ”¹æ‚¨çš„ URL"
msgid "You can always edit this later"
msgstr "您也å¯ä»¥ç¨åŽç¼–辑此选项。"
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr "您å¯ä»¥åœ¨ %{pat_link_start}个人访问令牌%{pat_link_end} 中查看它。"
@@ -46309,9 +46995,6 @@ msgstr "您å¯ä»¥åœ¨%{subscriptions_doc_link}中找到更多关于GitLab订阅çš
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr "您å¯ä»¥é€šè¿‡å…‹éš†ä»“库开始或开始以以下方å¼ä¹‹ä¸€æ·»åŠ æ–‡ä»¶ã€‚"
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr "您å¯ä»¥ä½¿ç”¨æ ‡ç­¾å¯¹æµ‹è¯•ç”¨ä¾‹è¿›è¡Œåˆ†ç»„。è¦äº†è§£æ­¤åŠŸèƒ½çš„未æ¥å‘展方å‘,请访问 %{linkStart}è´¨é‡ç®¡ç†æ–¹å‘页%{linkEnd}。"
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "您å¯ä»¥é‚€è¯·ä¸€ä¸ªæ–°æˆå‘˜æˆ–å¦ä¸€ä¸ªç¾¤ç»„加入%{project_name}。"
@@ -46330,18 +47013,12 @@ msgstr "您å¯ä»¥é€šè¿‡ç”µå­é‚®ä»¶æ¥é€šçŸ¥åº”用程åº/群组或项目"
msgid "You can now close this window."
msgstr "您现在å¯ä»¥å…³é—­æ­¤çª—å£ã€‚"
-msgid "You can now export your security dashboard to a CSV report."
-msgstr "现在你å¯ä»¥å¯¼å‡ºå®‰å…¨ä»ªè¡¨ç›˜åˆ° CSV 报告。"
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr "您现在å¯ä»¥æ交åˆå¹¶è¯·æ±‚以将此更改å‘é€åˆ°æºåˆ†æ”¯ã€‚"
msgid "You can now submit a merge request to get this change into the original project."
msgstr "您现在å¯ä»¥æ交åˆå¹¶è¯·æ±‚以将此更改添加到æºé¡¹ç›®ä¸­ã€‚"
-msgid "You can only %{action} files when you are on a branch"
-msgstr "当处于分支时,您åªèƒ½ %{action} 文件"
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr "您一次最多åªèƒ½æ·»åŠ  %{max_contacts} 个è”系人"
@@ -46397,7 +47074,7 @@ msgid "You cannot impersonate a user who cannot log in"
msgstr "您无法仿使用无法登录用户的身份"
msgid "You cannot impersonate a user with an expired password"
-msgstr ""
+msgstr "您无法模拟密ç è¿‡æœŸçš„用户"
msgid "You cannot impersonate an internal user"
msgstr "您无法使用内部用户的身份"
@@ -46423,6 +47100,9 @@ msgstr "您ä¸èƒ½åœ¨è¿™ä¸ªé¡¹ç›®ä¸­ç›´æŽ¥ç¼–辑文件,请派生(Fork)这ä¸
msgid "You could not create a new trigger."
msgstr "您无法创建新的触å‘器。"
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr "您当å‰å°šæœªè®¢é˜…任何计划"
@@ -46477,6 +47157,9 @@ msgstr "您没有任何近期的æœç´¢"
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr "您无æƒæŸ¥çœ‹æ­¤éƒ¨ç½²ã€‚è”系项目或群组所有者寻求帮助。"
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr "您无æƒåˆ›å»ºé¡¹ç›®"
@@ -46538,11 +47221,14 @@ msgstr "您没有足够的æƒé™ä¸ºæ­¤é¡¹ç›®åˆ›å»ºHTTP集æˆ"
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr "您没有足够的æƒé™æ¥åˆ›å»ºæ­¤é¡¹ç›®çš„待命计划"
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr "您没有足够的æƒé™æ¥ç®¡ç†æ­¤äº‹ä»¶çš„资æºé“¾æŽ¥"
msgid "You have insufficient permissions to manage timeline event tags for this project"
-msgstr ""
+msgstr "您没有足够的æƒé™ç®¡ç†æ­¤é¡¹ç›®çš„时间线事件标签"
msgid "You have insufficient permissions to manage timeline events for this incident"
msgstr "您没有足够的æƒé™ç®¡ç†æ­¤äº‹ä»¶çš„时间线事件"
@@ -46589,9 +47275,6 @@ msgstr "您已ç»ä¸ºæ‚¨çš„å¸æˆ·è®¾ç½®äº†ä¸¤æ­¥éªŒè¯ï¼ 如果您无法访问æ‚
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr "您已æˆåŠŸè´­ä¹° %{product}。您将通过电å­é‚®ä»¶æ”¶åˆ°æ”¶æ®ã€‚您的购买å¯èƒ½éœ€è¦ä¸€åˆ†é’Ÿæ‰èƒ½åŒæ­¥ï¼Œå¦‚果您还没有看到,请刷新页é¢ã€‚"
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr "您已æˆåŠŸè´­ä¹° %{seats} çš„ %{plan} 计划订阅。您将通过电å­é‚®ä»¶æ”¶åˆ°æ”¶æ®ã€‚您的购买å¯èƒ½éœ€è¦ä¸€åˆ†é’Ÿæ‰èƒ½åŒæ­¥ï¼Œå¦‚果您还没有看到,请刷新页é¢ã€‚"
-
msgid "You have unsaved changes"
msgstr "您有未ä¿å­˜çš„更改"
@@ -46769,6 +47452,9 @@ msgstr "您已ç»ä½¿ç”¨ä¸€æ¬¡å¯†ç éªŒè¯å™¨å¯ç”¨äº†åŒé‡è®¤è¯ã€‚如果您è¦
msgid "You've rejected %{user}"
msgstr "您拒ç»äº† %{user}"
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -46787,8 +47473,8 @@ msgstr "您的 %{strong}%{plan_name}%{strong_close} 订阅将于 %{strong}%{expi
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr "您为%{strong}%{namespace_name}%{strong_close}的%{strong}%{plan_name}%{strong_close}订阅将于%{strong}%{expires_on}%{strong_close}到期。"
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
-msgstr "您的 CI/CD é…置语法无效。查看 Lint 选项å¡ä»¥èŽ·å–更多详细信æ¯ã€‚"
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
+msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
msgstr "CSV导出已ç»å¼€å§‹ã€‚完æˆåŽå°†å‘é€ç”µå­é‚®ä»¶è‡³%{email}。"
@@ -46823,6 +47509,9 @@ msgstr "您的GitLabå¸æˆ·åˆ›å»ºè¯·æ±‚已被批准ï¼"
msgid "Your GitLab group"
msgstr "您的GitLab群组"
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr "您的群组"
@@ -46887,7 +47576,7 @@ msgid "Your account uses dedicated credentials for the \"%{group_name}\" group a
msgstr "您的å¸æˆ·ä½¿ç”¨ç¾¤ç»„“%{group_name}â€çš„专用凭æ®ï¼Œå¹¶ä¸”åªèƒ½é€šè¿‡SSO进行更新。"
msgid "Your action has been rejected because the namespace storage limit has been reached. For more information, visit %{doc_url}."
-msgstr ""
+msgstr "您的æ“作已被拒ç»ï¼Œå› ä¸ºå…¶å·²è¾¾åˆ°å‘½å空间的存储é™åˆ¶ã€‚如需了解更多信æ¯ï¼Œè¯·è®¿é—® %{doc_url}。"
msgid "Your action succeeded."
msgstr "您的æ“作æˆåŠŸã€‚"
@@ -46965,10 +47654,6 @@ msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] "您的å…费群组现在é™åˆ¶ä¸º %d åæˆå‘˜"
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] "您的群组 %{strong_start}%{namespace_name}%{strong_end} 有超过 %{free_user_limit} åæˆå‘˜ã€‚从 2022 å¹´ 10 月 19 日起,最近活跃的 %{free_user_limit} ä½æˆå‘˜å°†ä¿æŒæ´»è·ƒçŠ¶æ€ï¼Œå…¶ä½™æˆå‘˜å°†å¤„于%{link_start}超é™çŠ¶æ€%{link_end}并无法访问群组。您å¯ä»¥å‰å¾€â€œä½¿ç”¨é…é¢â€é¡µé¢æ¥ç®¡ç†æ‚¨çš„群组中将ä¿ç•™çš„ %{free_user_limit} åæˆå‘˜ã€‚"
-
msgid "Your groups"
msgstr "您的群组"
@@ -47006,6 +47691,15 @@ msgid "Your name"
msgstr "您的åå­—"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
+msgstr "您的命å空间 %{namespace_name} 超过了 %{free_limit} 用户é™åˆ¶ï¼Œå·²è¢«ç½®äºŽåªè¯»çŠ¶æ€ã€‚"
+
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
msgstr ""
msgid "Your new %{accessTokenType}"
@@ -47165,6 +47859,9 @@ msgstr "[REDACTED]"
msgid "[Redacted]"
msgstr "[Redacted]"
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr "`end_time` ä¸åº”超过 `start_time` åŽçš„一个月"
@@ -47339,6 +48036,9 @@ msgstr "ä¸èƒ½ä¸Žå­ç»„å…³è”"
msgid "cannot be associated with both a Group and a Project"
msgstr "ä¸èƒ½åŒæ—¶ä¸Žç¾¤ç»„和项目相关è”"
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr "无法更改"
@@ -47391,6 +48091,14 @@ msgid "change"
msgid_plural "changes"
msgstr[0] "å˜æ›´"
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr "%{criticalStart}critical%{criticalEnd}ã€%{highStart}high%{highEnd} å’Œ%{otherStart}其他%{otherEnd}"
@@ -47448,11 +48156,14 @@ msgstr "%{scanner} 检测到 %{number} 新的潜在 %{vulnStr}"
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr "%{scanner} 检测到 %{strong_start}%{number} 个%{strong_end}新的潜在%{vulnStr}"
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
-msgstr "%{scanner} 未检测到%{boldStart}æ–°çš„%{boldEnd}潜在æ¼æ´ž"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr "%{scanner} 未检测到新的 %{vulnStr}"
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
-msgstr "%{scanner} 没有检测到%{strong_start}新的%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{scanner}: Loading resulted in an error"
+msgstr ""
msgid "ciReport|: Loading resulted in an error"
msgstr ": 加载导致错误"
@@ -47607,8 +48318,8 @@ msgstr "已修å¤:"
msgid "ciReport|Found %{issuesWithCount}"
msgstr "找到%{issuesWithCount}"
-msgid "ciReport|Full Report"
-msgstr "完整报告"
+msgid "ciReport|Full report"
+msgstr ""
msgid "ciReport|Generic Report"
msgstr "通用报告"
@@ -47663,7 +48374,7 @@ msgid "ciReport|New"
msgstr "新增"
msgid "ciReport|New vulnerabilities are vulnerabilities that the security scan detects in the merge request that are different to existing vulnerabilities in the default branch."
-msgstr ""
+msgstr "æ–°æ¼æ´žæ˜¯å®‰å…¨æ‰«æ在åˆå¹¶è¯·æ±‚中检测到的,与默认分支中的现有æ¼æ´žä¸åŒçš„æ¼æ´žã€‚"
msgid "ciReport|No changes to Code Quality."
msgstr "Code Quality æ— å˜åŒ–。"
@@ -47783,6 +48494,10 @@ msgstr "已评论"
msgid "commented on %{link_to_project}"
msgstr "评论 %{link_to_project}"
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+
msgid "commit %{commit_id}"
msgstr "æ交 %{commit_id}"
@@ -47938,9 +48653,6 @@ msgstr "example.com"
msgid "exceeds maximum length (100 usernames)"
msgstr "超过最大长度(100 个用户å)"
-msgid "exceeds the %{max_value_length} character limit"
-msgstr "超过 %{max_value_length} 个字符的é™åˆ¶"
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr "超过%{bytes}字节的é™åˆ¶"
@@ -48117,6 +48829,9 @@ msgid "is already associated to a GitLab Issue. New issue will not be associated
msgstr "å·²ç»ä¸Žä¸€ä¸ªGitLab议题关è”。新的议题将ä¸ä¼šè¢«å…³è”。"
msgid "is already linked to this vulnerability"
+msgstr "å·²ç»ä¸Žæ­¤æ¼æ´žå…³è”"
+
+msgid "is already present in ancestors"
msgstr ""
msgid "is an invalid IP address range"
@@ -48158,6 +48873,12 @@ msgstr "在此项目中ä¸è¢«å…许。"
msgid "is not allowed since the group is not top-level group."
msgstr "ä¸å…许,因为该群组ä¸æ˜¯é¡¶çº§ç¾¤ç»„。"
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr "ä¸å…许。请使用您的常规电å­é‚®ä»¶åœ°å€ã€‚"
@@ -48646,6 +49367,9 @@ msgstr "必须与群组或项目相关è”"
msgid "must be greater than start date"
msgstr "必须大于开始日期"
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr "必须在派生(fork)网络内"
@@ -48655,12 +49379,12 @@ msgstr "å¿…é¡»å°äºŽ %{tag_limit} 个标签的数é‡é™åˆ¶"
msgid "must be set for a project namespace"
msgstr "必须为项目命å空间设置"
-msgid "must be top-level namespace"
-msgstr "必须是顶级命å空间"
-
msgid "must be unique by status and elapsed time within a policy"
msgstr "必须在策略中的状æ€å’Œç»è¿‡æ—¶é—´ä¸Šæ˜¯å”¯ä¸€çš„"
+msgid "must belong to same project of its requirement object."
+msgstr ""
+
msgid "must belong to same project of the work item."
msgstr "必须属于工作项所在的åŒä¸€é¡¹ç›®ã€‚"
@@ -48859,6 +49583,9 @@ msgstr "reCAPTCHA ç§é’¥"
msgid "reCAPTCHA site key"
msgstr "reCAPTCHA 公钥"
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr "最近活动"
@@ -48912,6 +49639,10 @@ msgstr "仓库:"
msgid "role's base access level does not match the access level of the membership"
msgstr "基于角色的访问级别ä¸åŒ¹é…æˆå‘˜çš„访问级别"
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+
msgid "running"
msgstr "è¿è¡Œä¸­"
diff --git a/locale/zh_CN/gitlab.po.time_stamp b/locale/zh_CN/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/zh_CN/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po
index d3818eb6001..97afc4b28f0 100644
--- a/locale/zh_HK/gitlab.po
+++ b/locale/zh_HK/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: zh-HK\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:25\n"
+"PO-Revision-Date: 2022-12-10 06:43\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -289,6 +289,10 @@ msgid "%d more comment"
msgid_plural "%d more comments"
msgstr[0] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -426,6 +430,12 @@ msgstr[0] ""
msgid "%{chartTitle} no data series"
msgstr ""
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr ""
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr ""
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
@@ -616,13 +626,13 @@ msgstr ""
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr ""
-msgid "%{hook_type} was deleted"
+msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{hook_type} was scheduled for deletion"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
msgstr ""
-msgid "%{host} sign-in from new location"
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
msgstr ""
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
@@ -815,9 +825,6 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
@@ -867,7 +874,7 @@ msgstr ""
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
+msgid "%{reportType} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1003,6 +1010,10 @@ msgstr ""
msgid "%{text} is available"
msgstr "%{text} å¯ç”¨"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] ""
+
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -1042,6 +1053,9 @@ msgstr ""
msgid "%{type} only supports %{name} name"
msgstr ""
+msgid "%{url} (optional)"
+msgstr ""
+
msgid "%{userName} (cannot merge)"
msgstr ""
@@ -1160,12 +1174,18 @@ msgstr ""
msgid "(Group Managed Account)"
msgstr ""
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
msgid "(UTC %{offset}) %{timezone}"
msgstr ""
+msgid "(Unlimited pipeline minutes)"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -2231,6 +2251,9 @@ msgstr ""
msgid "Add text to the sign-in page. Markdown enabled."
msgstr ""
+msgid "Add time entry"
+msgstr ""
+
msgid "Add to board"
msgstr ""
@@ -2318,6 +2341,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
+msgid "Additional diagram formats"
+msgstr ""
+
msgid "Additional minutes"
msgstr "é¡å¤–的筆記"
@@ -2513,6 +2539,9 @@ msgstr "åœæ­¢ä»»å‹™å¤±æ•—"
msgid "AdminArea|Total users"
msgstr ""
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr ""
+
msgid "AdminArea|Users"
msgstr ""
@@ -2813,7 +2842,7 @@ msgstr ""
msgid "AdminSettings|Select a CI/CD template"
msgstr ""
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
@@ -2876,9 +2905,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr ""
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr ""
@@ -4404,6 +4430,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr ""
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
@@ -4521,9 +4550,6 @@ msgstr ""
msgid "Any namespace"
msgstr ""
-msgid "Anyone can register for an account."
-msgstr ""
-
msgid "App ID"
msgstr ""
@@ -5054,6 +5080,9 @@ msgid "Are you sure you want to import %d repository?"
msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] ""
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr ""
+
msgid "Are you sure you want to lock %{path}?"
msgstr ""
@@ -5459,6 +5488,9 @@ msgstr ""
msgid "AuditStreams|Destination URL"
msgstr ""
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr ""
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
@@ -5504,6 +5536,9 @@ msgstr ""
msgid "AuditStreams|Verification token"
msgstr ""
+msgid "AuditStreams|filtered"
+msgstr ""
+
msgid "Aug"
msgstr "八月"
@@ -5735,9 +5770,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
-msgid "Available shared runners:"
-msgstr ""
-
msgid "Available specific runners"
msgstr ""
@@ -6753,6 +6785,15 @@ msgstr ""
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr ""
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr ""
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr ""
+
msgid "BranchRules|All branches"
msgstr ""
@@ -6867,9 +6908,6 @@ msgstr ""
msgid "BranchRules|default"
msgstr ""
-msgid "BranchRules|protected"
-msgstr ""
-
msgid "Branches"
msgstr "分支"
@@ -6879,6 +6917,9 @@ msgstr ""
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr ""
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr ""
+
msgid "Branches|Active"
msgstr "æ´»èºçš„"
@@ -6900,8 +6941,11 @@ msgstr ""
msgid "Branches|Compare"
msgstr "比較"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "移除所有已經åˆä½µåˆ° %{default_branch} 的分支。"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr ""
+
+msgid "Branches|Delete all merged branches?"
+msgstr ""
msgid "Branches|Delete branch"
msgstr "移除分支"
@@ -6921,9 +6965,6 @@ msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
-
msgid "Branches|Filter by branch name"
msgstr ""
@@ -6945,6 +6986,9 @@ msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr ""
+
msgid "Branches|Show active branches"
msgstr ""
@@ -6978,6 +7022,12 @@ msgstr ""
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr ""
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr ""
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
@@ -6990,6 +7040,9 @@ msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr ""
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr ""
@@ -7044,6 +7097,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -7849,6 +7905,9 @@ msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
+msgid "ChatMessage|Pipeline name"
+msgstr ""
+
msgid "ChatMessage|Tag"
msgstr ""
@@ -7897,6 +7956,9 @@ msgstr ""
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr ""
+msgid "Check your sign-up restrictions"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -8016,9 +8078,6 @@ msgstr ""
msgid "Checkout|Edit"
msgstr ""
-msgid "Checkout|Enter a number greater than 0"
-msgstr ""
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr ""
@@ -8034,7 +8093,7 @@ msgstr ""
msgid "Checkout|Failed to load states. Please try again."
msgstr ""
-msgid "Checkout|Failed to load the payment form. Please try again."
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
@@ -8058,6 +8117,9 @@ msgstr ""
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr ""
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr ""
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
@@ -8301,9 +8363,15 @@ msgstr "é‹è¡Œä¸­"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr ""
+msgid "CiVariables|Clear inputs"
+msgstr ""
+
msgid "CiVariables|Environments"
msgstr ""
+msgid "CiVariables|Expanded"
+msgstr ""
+
msgid "CiVariables|Input variable key"
msgstr ""
@@ -8316,6 +8384,9 @@ msgstr ""
msgid "CiVariables|Masked"
msgstr ""
+msgid "CiVariables|Maximum number of variables reached."
+msgstr ""
+
msgid "CiVariables|Options"
msgstr ""
@@ -8328,6 +8399,9 @@ msgstr ""
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|Run job again"
+msgstr ""
+
msgid "CiVariables|Scope"
msgstr ""
@@ -8337,6 +8411,9 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariables|Type"
msgstr ""
@@ -8346,6 +8423,12 @@ msgstr ""
msgid "CiVariables|Variables"
msgstr ""
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr ""
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -9035,6 +9118,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr ""
+
msgid "ClusterAgents|Valid access token"
msgstr ""
@@ -9855,6 +9941,9 @@ msgstr ""
msgid "ComplianceFrameworks|Configuration not found"
msgstr ""
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr ""
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr ""
@@ -9879,6 +9968,9 @@ msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr ""
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr ""
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr ""
@@ -9894,15 +9986,24 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
+msgid "ComplianceFrameworks|Remove default"
+msgstr ""
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
+msgid "ComplianceFrameworks|Set default"
+msgstr ""
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr ""
+msgid "ComplianceFrameworks|default"
+msgstr ""
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr ""
@@ -10068,7 +10169,7 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
-msgid "Configure the default first day of the week and time tracking units."
+msgid "Configure the default first day of the week, time tracking units, and default language."
msgstr ""
msgid "Configure the way a user creates a new account."
@@ -10188,6 +10289,9 @@ msgstr ""
msgid "Connecting to terminal sync service"
msgstr ""
+msgid "Connecting to the remote environment..."
+msgstr ""
+
msgid "Connecting..."
msgstr ""
@@ -10227,12 +10331,6 @@ msgstr ""
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr ""
-msgid "Container repositories"
-msgstr ""
-
-msgid "Container repository"
-msgstr ""
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
@@ -10494,9 +10592,6 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr ""
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -10680,9 +10775,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
-msgstr ""
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
@@ -10995,6 +11087,9 @@ msgstr ""
msgid "Couldn't assign policy to project or group"
msgstr ""
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -11127,9 +11222,6 @@ msgstr ""
msgid "Create issue to resolve all threads"
msgstr ""
-msgid "Create iteration"
-msgstr ""
-
msgid "Create label"
msgstr ""
@@ -11208,6 +11300,9 @@ msgstr ""
msgid "Create tag %{tagName}"
msgstr ""
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr ""
+
msgid "Create topic"
msgstr ""
@@ -11244,6 +11339,42 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr "標籤"
+msgid "CreateTimelogForm|Add time entry"
+msgstr ""
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr ""
+
+msgid "CreateTimelogForm|Cancel"
+msgstr ""
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr ""
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr ""
+
+msgid "CreateTimelogForm|Save"
+msgstr ""
+
+msgid "CreateTimelogForm|Spent at"
+msgstr ""
+
+msgid "CreateTimelogForm|Summary"
+msgstr ""
+
+msgid "CreateTimelogForm|Time spent"
+msgstr ""
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr ""
+
+msgid "CreateTimelogForm|issue"
+msgstr ""
+
+msgid "CreateTimelogForm|merge request"
+msgstr ""
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11469,6 +11600,9 @@ msgstr ""
msgid "Credit card:"
msgstr ""
+msgid "Critical - S1"
+msgstr ""
+
msgid "Critical vulnerabilities present"
msgstr ""
@@ -11878,12 +12012,18 @@ msgstr ""
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr ""
+
msgid "DORA4Metrics|Change failure rate"
msgstr ""
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr ""
+msgid "DORA4Metrics|Cycle time"
+msgstr ""
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr ""
@@ -11896,9 +12036,21 @@ msgstr ""
msgid "DORA4Metrics|Days from merge to deploy"
msgstr ""
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr ""
+
msgid "DORA4Metrics|Deployment frequency"
msgstr ""
+msgid "DORA4Metrics|Deploys"
+msgstr ""
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr ""
+
+msgid "DORA4Metrics|Lead time"
+msgstr ""
+
msgid "DORA4Metrics|Lead time for changes"
msgstr ""
@@ -11917,6 +12069,9 @@ msgstr ""
msgid "DORA4Metrics|Month to date"
msgstr ""
+msgid "DORA4Metrics|New issues"
+msgstr ""
+
msgid "DORA4Metrics|No incidents during this period"
msgstr ""
@@ -11950,6 +12105,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 "DORA4Metrics|Time to Restore Service"
+msgstr ""
+
msgid "DORA4Metrics|Time to restore service"
msgstr ""
@@ -12010,6 +12168,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12157,6 +12318,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -12527,6 +12691,9 @@ msgstr ""
msgid "Days to merge"
msgstr ""
+msgid "Deactivate"
+msgstr ""
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
@@ -12584,6 +12751,12 @@ msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
+msgid "Default language"
+msgstr ""
+
+msgid "Default language for users who are not logged in."
+msgstr ""
+
msgid "Default projects limit"
msgstr ""
@@ -13370,6 +13543,9 @@ msgstr ""
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr ""
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13406,6 +13582,9 @@ msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr ""
+
msgid "Deployments"
msgstr ""
@@ -13509,9 +13688,15 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Descriptions"
+msgstr ""
+
msgid "Descriptive label"
msgstr ""
+msgid "Design"
+msgstr ""
+
msgid "Design Management files and data"
msgstr ""
@@ -13675,6 +13860,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "DevOps metrics comparison (Alpha)"
+msgstr ""
+
msgid "Developer"
msgstr ""
@@ -14984,6 +15172,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter at least three characters to search."
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -15347,9 +15538,6 @@ msgstr ""
msgid "Epics|Remove issue"
msgstr ""
-msgid "Epics|Show more"
-msgstr ""
-
msgid "Epics|Something went wrong while creating child epics."
msgstr ""
@@ -15443,6 +15631,9 @@ msgstr ""
msgid "Error fetching refs"
msgstr ""
+msgid "Error fetching target projects. Please try again."
+msgstr ""
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr ""
@@ -15758,6 +15949,15 @@ msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
+msgid "EscalationStatus|Acknowledged"
+msgstr ""
+
+msgid "EscalationStatus|Resolved"
+msgstr ""
+
+msgid "EscalationStatus|Triggered"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -15918,6 +16118,9 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
@@ -15948,9 +16151,21 @@ msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
+msgid "Example: (feature|hotfix)\\/*"
+msgstr ""
+
+msgid "Example: (jar|exe)$"
+msgstr ""
+
msgid "Example: @sub\\.company\\.com$"
msgstr ""
+msgid "Example: Fixes \\d+\\..*"
+msgstr ""
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr ""
+
msgid "Examples"
msgstr ""
@@ -15969,9 +16184,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
-msgid "Executive Dashboard"
-msgstr ""
-
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@@ -16026,13 +16238,16 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand variable reference"
+msgstr ""
+
msgid "Expected documents: %{expected_documents}"
msgstr ""
-msgid "Experiment Candidates"
+msgid "Experiment"
msgstr ""
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
+msgid "Experiment candidates"
msgstr ""
msgid "Experiments"
@@ -16346,9 +16561,6 @@ msgstr ""
msgid "Failed to get ref."
msgstr ""
-msgid "Failed to install."
-msgstr ""
-
msgid "Failed to load"
msgstr ""
@@ -16505,9 +16717,6 @@ msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
-msgid "Failed to upgrade."
-msgstr ""
-
msgid "Failed to upload object map file"
msgstr ""
@@ -16761,6 +16970,9 @@ msgstr "二月"
msgid "February"
msgstr "二月"
+msgid "Feedback"
+msgstr ""
+
msgid "Feedback and Updates"
msgstr ""
@@ -16980,15 +17192,6 @@ msgstr ""
msgid "FloC|Participate in FLoC"
msgstr ""
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr ""
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr ""
-
msgid "Focus filter bar"
msgstr ""
@@ -17025,6 +17228,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -17169,10 +17375,19 @@ msgstr ""
msgid "Forks"
msgstr ""
-msgid "Format: %{dateFormat}"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr ""
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr ""
+
+msgid "ForksDivergence|%{messages} upstream repository"
msgstr ""
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr ""
+
+msgid "Format: %{dateFormat}"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
@@ -17251,10 +17466,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] ""
-
msgid "From issue creation until deploy to production"
msgstr "從創建議題到部署到生產環境"
@@ -17297,6 +17508,9 @@ msgstr ""
msgid "General settings"
msgstr ""
+msgid "Generate API key at %{site}"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr "產生é è¨­çš„標籤"
@@ -18142,6 +18356,10 @@ msgstr ""
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr ""
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] ""
+
msgid "GitLab.com (SaaS)"
msgstr ""
@@ -18391,6 +18609,9 @@ msgstr ""
msgid "GlobalSearch|Recent merge requests"
msgstr ""
+msgid "GlobalSearch|Result count is over limit."
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -18418,6 +18639,9 @@ msgstr ""
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr ""
+msgid "GlobalSearch|Users"
+msgstr ""
+
msgid "GlobalSearch|What are you searching for?"
msgstr ""
@@ -18595,6 +18819,9 @@ msgstr ""
msgid "Go to your projects"
msgstr ""
+msgid "Go to your review requests"
+msgstr ""
+
msgid "Go to your snippets"
msgstr ""
@@ -18688,6 +18915,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -18877,7 +19110,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added"
msgstr ""
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr ""
msgid "GroupActivityMetrics|Recent activity"
@@ -18964,6 +19197,9 @@ msgstr ""
msgid "GroupSAML|\"persistent\" recommended"
msgstr ""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr ""
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr ""
@@ -19069,6 +19305,9 @@ msgstr ""
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr ""
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr ""
+
msgid "GroupSAML|SAML Response Output"
msgstr ""
@@ -19240,7 +19479,7 @@ msgstr ""
msgid "GroupSettings|Reporting"
msgstr ""
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "GroupSettings|Select parent group"
@@ -19261,9 +19500,6 @@ msgstr ""
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr ""
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr ""
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
@@ -19393,6 +19629,9 @@ msgstr ""
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr ""
+
msgid "GroupsNew|Create group"
msgstr ""
@@ -19402,9 +19641,6 @@ msgstr ""
msgid "GroupsNew|Create subgroup"
msgstr ""
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr ""
-
msgid "GroupsNew|GitLab source URL"
msgstr ""
@@ -19874,12 +20110,18 @@ msgstr ""
msgid "Hierarchy|You can start using these items now."
msgstr ""
+msgid "High - S2"
+msgstr ""
+
msgid "High or unknown vulnerabilities present"
msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "Highlight"
+msgstr ""
+
msgid "HighlightBar|Alert events:"
msgstr ""
@@ -19913,9 +20155,6 @@ msgstr ""
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr ""
-msgid "Hook was successfully updated."
-msgstr ""
-
msgid "Horizontal rule"
msgstr ""
@@ -20084,6 +20323,9 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|A new code has been sent."
msgstr ""
@@ -20108,6 +20350,9 @@ msgstr ""
msgid "IdentityVerification|Didn't receive a code?"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr ""
+
msgid "IdentityVerification|Enter a code."
msgstr ""
@@ -20147,7 +20392,7 @@ msgstr ""
msgid "IdentityVerification|Phone number"
msgstr ""
-msgid "IdentityVerification|Phone number can't be blank."
+msgid "IdentityVerification|Phone number is required."
msgstr ""
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
@@ -20189,6 +20434,12 @@ msgstr ""
msgid "IdentityVerification|Verification code"
msgstr ""
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr ""
+
msgid "IdentityVerification|Verification successful"
msgstr ""
@@ -20201,9 +20452,18 @@ msgstr ""
msgid "IdentityVerification|Verify payment method"
msgstr ""
+msgid "IdentityVerification|Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr ""
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr ""
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
@@ -20546,6 +20806,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
+msgid "Improve quality with test cases"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21113,12 +21376,18 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident creation cancelled."
+msgstr ""
+
msgid "Incident details"
msgstr ""
msgid "Incident template (optional)."
msgstr ""
+msgid "Incident title"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -21437,6 +21706,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Info"
+msgstr ""
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr ""
@@ -21708,6 +21980,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr ""
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -21741,6 +22016,9 @@ msgstr ""
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr ""
+msgid "Integrations|GitLab for Slack app"
+msgstr ""
+
msgid "Integrations|Group-level integration management"
msgstr ""
@@ -22089,6 +22367,9 @@ msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr ""
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
@@ -22137,7 +22418,7 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgid "InviteMembersModal|Please add members to invite"
msgstr ""
msgid "InviteMembersModal|Review the invite errors and try again:"
@@ -22603,9 +22884,6 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr ""
-
msgid "It looks like you have some draft commits in this branch."
msgstr ""
@@ -22744,9 +23022,6 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr ""
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
@@ -23179,9 +23454,6 @@ msgstr ""
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr ""
-msgid "Jobs|Status"
-msgstr ""
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
@@ -23332,6 +23604,9 @@ msgstr ""
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr ""
+msgid "Job|Update CI/CD variables"
+msgstr ""
+
msgid "Job|Waiting for resource"
msgstr ""
@@ -23413,6 +23688,9 @@ msgstr ""
msgid "Key (PEM)"
msgstr ""
+msgid "Key result"
+msgstr ""
+
msgid "Key:"
msgstr ""
@@ -23482,9 +23760,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -23558,15 +23833,18 @@ msgstr ""
msgid "Labels"
msgstr "標籤"
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr "標籤å¯ä»¥æ‡‰ç”¨æ–¼ %{features}。群組標籤å¯ä»¥ç”¨æ–¼ä»»ä½•ç¾¤çµ„內的專案。"
-
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "標籤å¯ä»¥ç”¨æ–¼è­°é¡Œå’Œåˆä½µè«‹æ±‚以å°å®ƒå€‘進行分類。"
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23652,6 +23930,9 @@ msgstr ""
msgid "Last event"
msgstr ""
+msgid "Last login"
+msgstr ""
+
msgid "Last modified"
msgstr ""
@@ -23757,6 +24038,9 @@ msgstr ""
msgid "Learn More."
msgstr ""
+msgid "Learn about signing commits with SSH keys."
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -24426,10 +24710,10 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion."
+msgid "Locked files"
msgstr ""
-msgid "Locks give the ability to lock specific file or folder."
+msgid "Locked the discussion."
msgstr ""
msgid "Locks the discussion."
@@ -24480,6 +24764,9 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "Low - S4"
+msgstr ""
+
msgid "Low vulnerabilities present"
msgstr ""
@@ -24585,6 +24872,9 @@ msgstr ""
msgid "Manage %{workspace} labels"
msgstr ""
+msgid "Manage Dashboards"
+msgstr ""
+
msgid "Manage Web IDE features."
msgstr ""
@@ -25083,6 +25373,9 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
+msgid "Medium - S3"
+msgstr ""
+
msgid "Medium timeout"
msgstr ""
@@ -25104,6 +25397,12 @@ msgstr ""
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr ""
+msgid "MemberRole|can't be changed"
+msgstr ""
+
+msgid "MemberRole|must be top-level namespace"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -26227,6 +26526,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model candidate details"
+msgstr ""
+
msgid "Modified"
msgstr ""
@@ -26290,9 +26592,6 @@ msgstr ""
msgid "More information"
msgstr ""
-msgid "More information and share feedback"
-msgstr ""
-
msgid "More information is available|here"
msgstr ""
@@ -26501,6 +26800,9 @@ msgstr ""
msgid "NamespaceStorage|Buy more storage"
msgstr ""
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr ""
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr ""
@@ -26679,6 +26981,9 @@ msgstr "新增分支"
msgid "New branch unavailable"
msgstr ""
+msgid "New code quality findings"
+msgstr ""
+
msgid "New confidential epic title "
msgstr ""
@@ -26721,6 +27026,12 @@ msgstr ""
msgid "New identity"
msgstr ""
+msgid "New incident"
+msgstr ""
+
+msgid "New incident has been created"
+msgstr ""
+
msgid "New issue"
msgstr "新議題"
@@ -26994,9 +27305,6 @@ msgstr ""
msgid "No iteration"
msgstr ""
-msgid "No iterations to show"
-msgstr ""
-
msgid "No job log"
msgstr ""
@@ -27096,6 +27404,9 @@ msgstr ""
msgid "No results found"
msgstr ""
+msgid "No results found."
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27749,9 +28060,24 @@ msgstr ""
msgid "OK"
msgstr ""
+msgid "OKR|Existing key result"
+msgstr ""
+
+msgid "OKR|Existing objective"
+msgstr ""
+
+msgid "OKR|New key result"
+msgstr ""
+
+msgid "OKR|New objective"
+msgstr ""
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Objective"
+msgstr ""
+
msgid "Observability"
msgstr ""
@@ -28131,6 +28457,9 @@ msgstr ""
msgid "OnDemandScans|Timezone"
msgstr ""
+msgid "OnDemandScans|Verify configuration"
+msgstr ""
+
msgid "OnDemandScans|View results"
msgstr ""
@@ -28207,9 +28536,6 @@ msgstr ""
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr ""
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
@@ -28315,9 +28641,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
@@ -28603,6 +28926,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr ""
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
@@ -28691,6 +29017,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -28706,6 +29035,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr ""
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -28730,6 +29065,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests"
+msgstr ""
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr ""
+
msgid "PackageRegistry|Generic"
msgstr ""
@@ -28802,6 +29149,9 @@ msgstr ""
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr ""
+msgid "PackageRegistry|Other versions"
+msgstr ""
+
msgid "PackageRegistry|Package Registry"
msgstr ""
@@ -28817,6 +29167,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
+msgid "PackageRegistry|Package forwarding"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28824,6 +29177,9 @@ msgstr[0] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -28878,6 +29234,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -28926,9 +29285,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "PackageRegistry|Type"
-msgstr ""
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
@@ -28948,6 +29304,10 @@ msgid "PackageRegistry|You are about to delete 1 asset. This operation is irreve
msgid_plural "PackageRegistry|You are about to delete %d assets. This operation is irreversible."
msgstr[0] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
@@ -28963,6 +29323,9 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
+msgid "Packages"
+msgstr ""
+
msgid "Packages and registries"
msgstr ""
@@ -29032,6 +29395,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
+msgid "Parameters"
+msgstr ""
+
msgid "Parent"
msgstr ""
@@ -29095,6 +29461,15 @@ msgstr ""
msgid "Passwords should be unique and not used for any other sites or services."
msgstr ""
+msgid "Password|Not satisfied"
+msgstr ""
+
+msgid "Password|Satisfied"
+msgstr ""
+
+msgid "Password|To be satisfied"
+msgstr ""
+
msgid "Password|requires at least one lowercase letter"
msgstr ""
@@ -29338,6 +29713,30 @@ msgstr ""
msgid "Phone"
msgstr ""
+msgid "PhoneVerification|Enter a valid code."
+msgstr ""
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr ""
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr ""
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr ""
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr ""
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr ""
+
msgid "Pick a name"
msgstr ""
@@ -30253,6 +30652,9 @@ msgstr ""
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr ""
+msgid "Please complete the incident creation form."
+msgstr ""
+
msgid "Please complete your profile with email address"
msgstr ""
@@ -30409,9 +30811,6 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr ""
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr ""
@@ -30565,6 +30964,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr ""
+
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30649,6 +31051,9 @@ msgstr ""
msgid "Preferences|Tab width"
msgstr ""
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr ""
@@ -30661,9 +31066,15 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Use legacy Web IDE"
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
+msgid "Preferences|Web IDE"
+msgstr ""
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr ""
@@ -30781,16 +31192,25 @@ msgstr ""
msgid "Proceed"
msgstr ""
+msgid "Product Analytics|Onboarding view"
+msgstr ""
+
msgid "Product analytics"
msgstr ""
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr ""
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr ""
+
msgid "ProductAnalytics|Audience"
msgstr ""
-msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
+msgid "ProductAnalytics|New Analytics Widget Title"
msgstr ""
-msgid "ProductAnalytics|Widgets content"
+msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr ""
msgid "Productivity"
@@ -31153,6 +31573,12 @@ msgstr ""
msgid "Profiles|Upload new avatar"
msgstr ""
+msgid "Profiles|Usage type"
+msgstr ""
+
+msgid "Profiles|Usage type:"
+msgstr ""
+
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -31687,12 +32113,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr ""
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr ""
@@ -31774,9 +32206,6 @@ msgstr ""
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr ""
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr ""
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr ""
@@ -31891,6 +32320,9 @@ msgstr ""
msgid "ProjectSettings|Monitor"
msgstr ""
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -31936,6 +32368,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr ""
+
msgid "ProjectSettings|Releases"
msgstr ""
@@ -32065,7 +32500,7 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project."
msgstr ""
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
msgstr ""
msgid "ProjectSettings|View project analytics."
@@ -32146,6 +32581,9 @@ msgstr ""
msgid "ProjectTemplates|NodeJS Express"
msgstr ""
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr ""
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr ""
@@ -32668,9 +33106,6 @@ msgstr ""
msgid "Protected Branches"
msgstr ""
-msgid "Protected Environment"
-msgstr ""
-
msgid "Protected Paths: requests"
msgstr ""
@@ -33341,6 +33776,9 @@ msgid "Refreshing in a second to show the updated status..."
msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] ""
+msgid "Refreshing..."
+msgstr ""
+
msgid "Regenerate export"
msgstr ""
@@ -33537,6 +33975,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -33876,6 +34317,9 @@ msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
+msgid "Reopening..."
+msgstr ""
+
msgid "Reopens this %{quick_action_target}."
msgstr ""
@@ -33930,10 +34374,7 @@ msgstr ""
msgid "Report Finding not found"
msgstr ""
-msgid "Report abuse"
-msgstr ""
-
-msgid "Report abuse to admin"
+msgid "Report abuse to administrator"
msgstr ""
msgid "Report couldn't be prepared."
@@ -34266,6 +34707,9 @@ msgstr ""
msgid "Request Access"
msgstr "申請權é™"
+msgid "Request Headers"
+msgstr ""
+
msgid "Request a new one"
msgstr ""
@@ -34511,6 +34955,9 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts"
msgstr ""
+msgid "Results limit reached"
+msgstr ""
+
msgid "Resume"
msgstr ""
@@ -34584,7 +35031,7 @@ msgstr ""
msgid "Review time"
msgstr ""
-msgid "Review time is defined as the time it takes from first comment until merged."
+msgid "Review time is the amount of time since the first comment in a merge request."
msgstr ""
msgid "ReviewApp|Enable Review App"
@@ -34678,6 +35125,9 @@ msgstr ""
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr ""
+msgid "Run manual job again"
+msgstr ""
+
msgid "Run manual or delayed jobs"
msgstr ""
@@ -34735,7 +35185,7 @@ msgstr ""
msgid "Runners|A new version is available"
msgstr ""
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
msgstr ""
msgid "Runners|Active"
@@ -34762,6 +35212,9 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr ""
+
msgid "Runners|An upgrade is available for this runner"
msgstr ""
@@ -34783,6 +35236,9 @@ msgstr ""
msgid "Runners|Available"
msgstr ""
+msgid "Runners|Available shared runners: %{count}"
+msgstr ""
+
msgid "Runners|Available to all projects"
msgstr ""
@@ -34865,6 +35321,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -34883,6 +35342,9 @@ msgstr ""
msgid "Runners|IP Address"
msgstr ""
+msgid "Runners|Idle"
+msgstr ""
+
msgid "Runners|Install a runner"
msgstr ""
@@ -34922,12 +35384,15 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
-msgid "Runners|New group runners view"
+msgid "Runners|New group runners can be registered"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
+msgid "Runners|No description"
+msgstr ""
+
msgid "Runners|No results found"
msgstr ""
@@ -35019,6 +35484,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -35085,6 +35553,9 @@ msgstr ""
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr ""
+msgid "Runners|Running"
+msgstr ""
+
msgid "Runners|Runs untagged jobs"
msgstr ""
@@ -35133,12 +35604,6 @@ msgstr ""
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr ""
-msgid "Runners|Take me there!"
-msgstr ""
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr ""
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr ""
@@ -35251,6 +35716,9 @@ msgstr ""
msgid "Runner|Owner"
msgstr ""
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -35278,13 +35746,16 @@ msgstr ""
msgid "SAML single sign-on for %{group_name}"
msgstr ""
+msgid "SAML|Sign in to %{groupName}"
+msgstr ""
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr ""
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr ""
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
msgstr ""
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
@@ -35326,6 +35797,9 @@ msgstr ""
msgid "SSH key"
msgstr ""
+msgid "SSH key fingerprint:"
+msgstr ""
+
msgid "SSH keys"
msgstr ""
@@ -35341,6 +35815,15 @@ msgstr ""
msgid "SSH public key"
msgstr ""
+msgid "SSHKey|Authentication"
+msgstr ""
+
+msgid "SSHKey|Authentication & Signing"
+msgstr ""
+
+msgid "SSHKey|Signing"
+msgstr ""
+
msgid "SSL Verification:"
msgstr ""
@@ -35407,6 +35890,12 @@ msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
@@ -35434,9 +35923,15 @@ msgstr ""
msgid "ScanExecutionPolicy|Select site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr ""
+
msgid "ScanExecutionPolicy|Site profile"
msgstr ""
+msgid "ScanExecutionPolicy|Tags"
+msgstr ""
+
msgid "ScanExecutionPolicy|agent"
msgstr ""
@@ -35449,18 +35944,27 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr ""
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
msgstr ""
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr ""
+
msgid "ScanResultPolicy|scanners"
msgstr ""
msgid "ScanResultPolicy|severity levels"
msgstr ""
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr ""
+
msgid "ScanResultPolicy|vulnerability states"
msgstr ""
@@ -35545,6 +36049,9 @@ msgstr ""
msgid "Search GitLab"
msgstr ""
+msgid "Search Within"
+msgstr ""
+
msgid "Search a group"
msgstr ""
@@ -36051,6 +36558,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
+msgid "SecurityOrchestration|Choose approver type"
+msgstr ""
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr ""
@@ -36108,9 +36618,18 @@ msgstr ""
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr ""
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr ""
+
+msgid "SecurityOrchestration|Groups"
+msgstr ""
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
+msgid "SecurityOrchestration|Individual users"
+msgstr ""
+
msgid "SecurityOrchestration|Inherited"
msgstr ""
@@ -36126,6 +36645,9 @@ msgstr ""
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr ""
+msgid "SecurityOrchestration|License Scan"
+msgstr ""
+
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -36162,6 +36684,9 @@ msgstr ""
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr ""
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr ""
+
msgid "SecurityOrchestration|Policy definition"
msgstr ""
@@ -36234,6 +36759,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Approvals"
msgstr ""
+msgid "SecurityOrchestration|Security Scan"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -36243,12 +36771,21 @@ msgstr ""
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr ""
+msgid "SecurityOrchestration|Select groups"
+msgstr ""
+
msgid "SecurityOrchestration|Select policy"
msgstr ""
+msgid "SecurityOrchestration|Select scan type"
+msgstr ""
+
msgid "SecurityOrchestration|Select security project"
msgstr ""
+msgid "SecurityOrchestration|Select users"
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -36318,6 +36855,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36345,6 +36885,9 @@ msgstr ""
msgid "SecurityOrchestration|branches"
msgstr ""
+msgid "SecurityOrchestration|group level branch"
+msgstr ""
+
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -36381,6 +36924,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -36390,9 +36936,24 @@ msgstr ""
msgid "SecurityReports|All activity"
msgstr ""
+msgid "SecurityReports|All clusters"
+msgstr ""
+
+msgid "SecurityReports|All images"
+msgstr ""
+
+msgid "SecurityReports|All projects"
+msgstr ""
+
msgid "SecurityReports|All severities"
msgstr ""
+msgid "SecurityReports|All statuses"
+msgstr ""
+
+msgid "SecurityReports|All tools"
+msgstr ""
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr ""
@@ -36537,6 +37098,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
+msgid "SecurityReports|Not available"
+msgstr ""
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
@@ -36762,6 +37326,9 @@ msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
+msgid "Select a group"
+msgstr ""
+
msgid "Select a label"
msgstr ""
@@ -36867,6 +37434,9 @@ msgstr ""
msgid "Select reviewer(s)"
msgstr ""
+msgid "Select severity (optional)"
+msgstr ""
+
msgid "Select source"
msgstr ""
@@ -37354,6 +37924,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Shared Runners:"
+msgstr ""
+
msgid "Shared projects"
msgstr ""
@@ -37480,6 +38053,9 @@ msgstr ""
msgid "Show list"
msgstr ""
+msgid "Show more"
+msgstr ""
+
msgid "Show one file at a time"
msgstr ""
@@ -37637,9 +38213,6 @@ msgstr ""
msgid "Sign in preview"
msgstr ""
-msgid "Sign in to %{group_name}"
-msgstr ""
-
msgid "Sign in to GitLab"
msgstr ""
@@ -37793,7 +38366,7 @@ msgstr ""
msgid "Slack logo"
msgstr ""
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
msgid "SlackIntegration|Client ID"
@@ -37808,13 +38381,16 @@ msgstr ""
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr ""
-msgid "SlackIntegration|Install Slack app"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr ""
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Project alias"
msgstr ""
-msgid "SlackIntegration|Reinstall Slack app"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
msgstr ""
msgid "SlackIntegration|Remove project"
@@ -37838,13 +38414,16 @@ msgstr ""
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr ""
+msgid "SlackIntegration|Update to the latest version"
+msgstr ""
+
msgid "SlackIntegration|Verification token"
msgstr ""
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr ""
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
@@ -37982,10 +38561,10 @@ msgstr ""
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr ""
-msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
msgstr ""
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
+msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -38027,6 +38606,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr ""
+
msgid "Something went wrong while adding timeline event."
msgstr ""
@@ -38045,6 +38627,9 @@ msgstr ""
msgid "Something went wrong while closing the epic. Please try again later."
msgstr ""
+msgid "Something went wrong while closing the incident form."
+msgstr ""
+
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
@@ -38114,6 +38699,9 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
+msgid "Something went wrong while opening the incident form."
+msgstr ""
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
@@ -38507,6 +39095,9 @@ msgstr ""
msgid "Spent at"
msgstr ""
+msgid "Spent at can't be a future date and time."
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -38612,6 +39203,9 @@ msgstr ""
msgid "Start merge train..."
msgstr ""
+msgid "Start remote connection"
+msgstr ""
+
msgid "Start search"
msgstr ""
@@ -38675,6 +39269,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Status (optional)"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -39411,6 +40008,9 @@ msgstr ""
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr ""
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr ""
+
msgid "SuperSonics|You do not have an active subscription"
msgstr ""
@@ -39501,9 +40101,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr "切æ›åˆ†æ”¯/標籤"
-msgid "Switch editors"
-msgstr ""
-
msgid "Switch to GitLab Next"
msgstr ""
@@ -39786,6 +40383,12 @@ msgstr ""
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr ""
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr ""
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr ""
+
msgid "Team"
msgstr "團隊"
@@ -40361,6 +40964,9 @@ msgstr ""
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr ""
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr ""
+
msgid "The following items will NOT be exported:"
msgstr ""
@@ -40723,9 +41329,6 @@ msgstr ""
msgid "There are no abuse reports!"
msgstr ""
-msgid "There are no archived projects yet"
-msgstr ""
-
msgid "There are no archived requirements"
msgstr ""
@@ -40783,9 +41386,6 @@ msgstr ""
msgid "There are no packages yet"
msgstr ""
-msgid "There are no projects shared with this group yet"
-msgstr ""
-
msgid "There are no secure files yet."
msgstr ""
@@ -40831,6 +41431,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem creating the incident. Please try again."
+msgstr ""
+
msgid "There was a problem fetching CRM contacts."
msgstr ""
@@ -40951,6 +41554,9 @@ msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
+msgid "There was an error fetching the job."
+msgstr ""
+
msgid "There was an error fetching the jobs for your project."
msgstr ""
@@ -40990,6 +41596,9 @@ msgstr ""
msgid "There was an error retrieving the Jira users."
msgstr ""
+msgid "There was an error running the job. Please try again."
+msgstr ""
+
msgid "There was an error saving your changes."
msgstr ""
@@ -41083,9 +41692,6 @@ msgstr ""
msgid "This Cron pattern is invalid"
msgstr ""
-msgid "This Experiment has no logged Candidates"
-msgstr ""
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr ""
@@ -41275,6 +41881,9 @@ msgstr ""
msgid "This epic would exceed maximum number of related epics."
msgstr ""
+msgid "This experiment has no logged candidates"
+msgstr ""
+
msgid "This feature requires local storage to be enabled"
msgstr ""
@@ -41293,7 +41902,7 @@ msgstr ""
msgid "This group"
msgstr ""
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
msgstr ""
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
@@ -41767,6 +42376,12 @@ msgstr ""
msgid "Time spent"
msgstr ""
+msgid "Time spent can't be zero."
+msgstr ""
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr ""
+
msgid "Time to Restore Service"
msgstr ""
@@ -42015,6 +42630,9 @@ msgstr ""
msgid "Title:"
msgstr ""
+msgid "Titles"
+msgstr ""
+
msgid "Titles and Descriptions"
msgstr ""
@@ -42048,9 +42666,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
@@ -42165,6 +42780,9 @@ msgstr ""
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr ""
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr ""
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr ""
@@ -42258,6 +42876,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Due %{due_date}"
+msgstr ""
+
msgid "Todos|Epic"
msgstr ""
@@ -42291,6 +42912,9 @@ msgstr ""
msgid "Todos|Mark all as done"
msgstr ""
+msgid "Todos|Member access requested"
+msgstr ""
+
msgid "Todos|Mentioned"
msgstr ""
@@ -42306,13 +42930,13 @@ msgstr ""
msgid "Todos|Pipelines"
msgstr ""
-msgid "Todos|Removed from Merge Train:"
+msgid "Todos|Removed from Merge Train"
msgstr ""
msgid "Todos|Review requested"
msgstr ""
-msgid "Todos|The pipeline failed in"
+msgid "Todos|The pipeline failed"
msgstr ""
msgid "Todos|Undo mark all as done"
@@ -42327,22 +42951,22 @@ msgstr ""
msgid "Todos|Your To-Do List shows what to work on next"
msgstr ""
-msgid "Todos|added a todo for"
+msgid "Todos|added a to-do item"
msgstr ""
-msgid "Todos|mentioned %{who} on"
+msgid "Todos|has requested access"
msgstr ""
-msgid "Todos|requested a review of"
+msgid "Todos|mentioned %{who}"
msgstr ""
-msgid "Todos|set %{who} as an approver for"
+msgid "Todos|requested a review"
msgstr ""
-msgid "Todos|yourself"
+msgid "Todos|set %{who} as an approver"
msgstr ""
-msgid "Todo|at %{todo_parent_path}"
+msgid "Todos|yourself"
msgstr ""
msgid "Toggle GitLab Next"
@@ -42712,6 +43336,12 @@ msgstr ""
msgid "Triggerer"
msgstr ""
+msgid "Trigger|Description"
+msgstr ""
+
+msgid "Trigger|Trigger description"
+msgstr ""
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr ""
@@ -42748,6 +43378,9 @@ msgstr ""
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try out the new Web IDE"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -42925,6 +43558,12 @@ msgstr ""
msgid "Unable to fetch branches list, please close the form and try again"
msgstr ""
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr ""
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr ""
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr ""
@@ -43048,6 +43687,9 @@ msgstr ""
msgid "Unhappy?"
msgstr ""
+msgid "Units|d"
+msgstr ""
+
msgid "Units|ms"
msgstr ""
@@ -43129,6 +43771,9 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr ""
+
msgid "Unstar"
msgstr "å–消星標"
@@ -43312,6 +43957,9 @@ msgstr ""
msgid "Uploading..."
msgstr ""
+msgid "Uploads"
+msgstr ""
+
msgid "Upstream"
msgstr ""
@@ -43330,18 +43978,12 @@ msgstr ""
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr ""
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr ""
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr ""
msgid "UsageQuota|%{linkTitle} help link"
msgstr ""
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr ""
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr ""
@@ -43369,9 +44011,6 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage"
msgstr ""
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr ""
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr ""
@@ -43381,9 +44020,6 @@ msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
-msgid "UsageQuota|Current period usage"
-msgstr ""
-
msgid "UsageQuota|Dependency proxy"
msgstr ""
@@ -43510,10 +44146,7 @@ msgstr ""
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr ""
-msgid "UsageQuota|The table below shows current period usage"
-msgstr ""
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
@@ -43522,12 +44155,6 @@ msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr ""
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr ""
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr ""
@@ -43540,9 +44167,6 @@ msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
-msgid "UsageQuota|Unlimited"
-msgstr ""
-
msgid "UsageQuota|Uploads"
msgstr ""
@@ -43576,7 +44200,7 @@ msgstr ""
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr ""
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
msgstr ""
msgid "UsageQuota|Wiki"
@@ -43585,16 +44209,16 @@ msgstr ""
msgid "UsageQuota|Wiki content."
msgstr ""
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
msgstr ""
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr ""
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
msgstr ""
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
@@ -43768,6 +44392,9 @@ msgstr ""
msgid "Use the search bar on the top of this page"
msgstr ""
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr ""
+
msgid "Use this token to validate received payloads."
msgstr ""
@@ -44033,9 +44660,6 @@ msgstr "個人專案"
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr ""
-msgid "UserProfile|Report abuse"
-msgstr ""
-
msgid "UserProfile|Retry"
msgstr ""
@@ -44225,6 +44849,9 @@ msgstr ""
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr ""
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr ""
+
msgid "Valid From"
msgstr ""
@@ -44297,6 +44924,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Dashboard"
msgstr ""
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr ""
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""
@@ -44315,6 +44945,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr ""
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr ""
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr ""
@@ -44363,10 +44996,10 @@ msgstr ""
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr ""
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
+msgid "Variable"
msgstr ""
-msgid "Variable"
+msgid "Variable value will be evaluated as raw string."
msgstr ""
msgid "Variable will be masked in job logs."
@@ -44378,7 +45011,7 @@ msgstr ""
msgid "Variables can be:"
msgstr ""
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
+msgid "Variables can have several attributes."
msgstr ""
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
@@ -44435,6 +45068,21 @@ msgstr ""
msgid "Version %{versionNumber} (latest)"
msgstr ""
+msgid "VersionCheck|%{details}"
+msgstr ""
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr ""
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr ""
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr ""
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr ""
+
msgid "VersionCheck|Up to date"
msgstr ""
@@ -44444,6 +45092,18 @@ msgstr ""
msgid "VersionCheck|Update available"
msgstr ""
+msgid "VersionCheck|Upgrade now"
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr ""
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr ""
+
msgid "VersionCheck|Your GitLab Version"
msgstr ""
@@ -45101,6 +45761,9 @@ msgstr ""
msgid "We don't have enough data to show this stage."
msgstr "該階段的數據ä¸è¶³ï¼Œç„¡æ³•é¡¯ç¤ºã€‚"
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr ""
+
msgid "We have found the following errors:"
msgstr ""
@@ -45176,9 +45839,6 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr ""
-
msgid "WebIDE|Fork project"
msgstr ""
@@ -45194,24 +45854,12 @@ msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|Ready for something new?"
-msgstr ""
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr ""
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr ""
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr ""
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr ""
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr ""
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr ""
@@ -45242,6 +45890,18 @@ msgstr ""
msgid "Webhook events will be displayed here."
msgstr ""
+msgid "Webhook was created"
+msgstr ""
+
+msgid "Webhook was deleted"
+msgstr ""
+
+msgid "Webhook was scheduled for deletion"
+msgstr ""
+
+msgid "Webhook was updated"
+msgstr ""
+
msgid "Webhook:"
msgstr ""
@@ -45308,9 +45968,6 @@ msgstr ""
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
-msgid "Webhooks|Comments"
-msgstr ""
-
msgid "Webhooks|Confidential comments"
msgstr ""
@@ -45359,13 +46016,10 @@ msgstr ""
msgid "Webhooks|Merge request events"
msgstr ""
-msgid "Webhooks|Pipeline events"
+msgid "Webhooks|Must match part of URL"
msgstr ""
-msgid "Webhooks|Push events"
-msgstr ""
-
-msgid "Webhooks|Push to the repository."
+msgid "Webhooks|Pipeline events"
msgstr ""
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
@@ -45404,9 +46058,6 @@ msgstr ""
msgid "Webhooks|Trigger"
msgstr ""
-msgid "Webhooks|URL"
-msgstr ""
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr ""
@@ -45796,6 +46447,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
@@ -45805,9 +46459,6 @@ msgstr ""
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr ""
-
msgid "Withdraw Access Request"
msgstr "å–消權é™ç”³è¯·"
@@ -45826,6 +46477,12 @@ msgstr ""
msgid "WorkItem|Add"
msgstr ""
+msgid "WorkItem|Add %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Add a title"
msgstr ""
@@ -45841,9 +46498,6 @@ msgstr ""
msgid "WorkItem|Add start date"
msgstr ""
-msgid "WorkItem|Add task"
-msgstr ""
-
msgid "WorkItem|Add to iteration"
msgstr ""
@@ -45863,6 +46517,9 @@ msgstr[0] ""
msgid "WorkItem|Cancel"
msgstr ""
+msgid "WorkItem|Child objectives and key results"
+msgstr ""
+
msgid "WorkItem|Child removed"
msgstr ""
@@ -45872,6 +46529,12 @@ msgstr ""
msgid "WorkItem|Collapse tasks"
msgstr ""
+msgid "WorkItem|Create %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Create objective"
+msgstr ""
+
msgid "WorkItem|Create task"
msgstr ""
@@ -45884,16 +46547,19 @@ msgstr ""
msgid "WorkItem|Delete %{workItemType}"
msgstr ""
+msgid "WorkItem|Discard changes"
+msgstr ""
+
msgid "WorkItem|Due date"
msgstr ""
-msgid "WorkItem|Expand tasks"
+msgid "WorkItem|Existing task"
msgstr ""
-msgid "WorkItem|Incident"
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Introducing tasks"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Issue"
@@ -45902,12 +46568,18 @@ msgstr ""
msgid "WorkItem|Iteration"
msgstr ""
-msgid "WorkItem|Learn about tasks."
+msgid "WorkItem|Key result"
msgstr ""
msgid "WorkItem|Milestone"
msgstr ""
+msgid "WorkItem|New objective"
+msgstr ""
+
+msgid "WorkItem|New task"
+msgstr ""
+
msgid "WorkItem|No iteration"
msgstr ""
@@ -45917,12 +46589,18 @@ msgstr ""
msgid "WorkItem|No milestone"
msgstr ""
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr ""
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
msgstr ""
+msgid "WorkItem|Objective"
+msgstr ""
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
@@ -45935,9 +46613,18 @@ msgstr ""
msgid "WorkItem|Requirements"
msgstr ""
+msgid "WorkItem|Save and overwrite"
+msgstr ""
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr ""
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
@@ -46001,7 +46688,7 @@ msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
@@ -46031,6 +46718,9 @@ msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
+msgid "Write a description..."
+msgstr ""
+
msgid "Write a description…"
msgstr ""
@@ -46197,10 +46887,10 @@ msgstr ""
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr ""
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
@@ -46239,10 +46929,6 @@ msgstr ""
msgid "You can always edit this later"
msgstr ""
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] ""
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr ""
@@ -46309,9 +46995,6 @@ msgstr ""
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr ""
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -46330,18 +47013,12 @@ msgstr ""
msgid "You can now close this window."
msgstr ""
-msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
msgid "You can now submit a merge request to get this change into the original project."
msgstr ""
-msgid "You can only %{action} files when you are on a branch"
-msgstr ""
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr ""
@@ -46423,6 +47100,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You do not have access to any projects for creating incidents."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46477,6 +47157,9 @@ msgstr ""
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr ""
+msgid "You don't have permission to view this epic"
+msgstr ""
+
msgid "You don't have permissions to create this project"
msgstr ""
@@ -46538,6 +47221,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr ""
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr ""
@@ -46589,9 +47275,6 @@ msgstr ""
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr ""
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr ""
-
msgid "You have unsaved changes"
msgstr ""
@@ -46769,6 +47452,9 @@ msgstr ""
msgid "You've rejected %{user}"
msgstr ""
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -46787,7 +47473,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
@@ -46823,6 +47509,9 @@ msgstr ""
msgid "Your GitLab group"
msgstr ""
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -46965,10 +47654,6 @@ msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] ""
-
msgid "Your groups"
msgstr ""
@@ -47008,6 +47693,15 @@ msgstr "您的åå­—"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr ""
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr ""
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr ""
+
msgid "Your new %{accessTokenType}"
msgstr ""
@@ -47165,6 +47859,9 @@ msgstr ""
msgid "[Redacted]"
msgstr ""
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr ""
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr ""
@@ -47339,6 +48036,9 @@ msgstr ""
msgid "cannot be associated with both a Group and a Project"
msgstr ""
+msgid "cannot be blank"
+msgstr ""
+
msgid "cannot be changed"
msgstr ""
@@ -47391,6 +48091,14 @@ msgid "change"
msgid_plural "changes"
msgstr[0] ""
+msgid "check"
+msgid_plural "checks"
+msgstr[0] ""
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] ""
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr ""
@@ -47448,10 +48156,13 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr ""
+
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner}: Loading resulted in an error"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
@@ -47607,7 +48318,7 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr ""
msgid "ciReport|Generic Report"
@@ -47783,6 +48494,10 @@ msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] ""
+
msgid "commit %{commit_id}"
msgstr ""
@@ -47938,9 +48653,6 @@ msgstr ""
msgid "exceeds maximum length (100 usernames)"
msgstr ""
-msgid "exceeds the %{max_value_length} character limit"
-msgstr ""
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr ""
@@ -48119,6 +48831,9 @@ msgstr ""
msgid "is already linked to this vulnerability"
msgstr ""
+msgid "is already present in ancestors"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
@@ -48158,6 +48873,12 @@ msgstr ""
msgid "is not allowed since the group is not top-level group."
msgstr ""
+msgid "is not allowed to add this type of parent"
+msgstr ""
+
+msgid "is not allowed to point to itself"
+msgstr ""
+
msgid "is not allowed. Please use your regular email address."
msgstr ""
@@ -48646,6 +49367,9 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr ""
+
msgid "must be inside the fork network"
msgstr ""
@@ -48655,10 +49379,10 @@ msgstr ""
msgid "must be set for a project namespace"
msgstr ""
-msgid "must be top-level namespace"
+msgid "must be unique by status and elapsed time within a policy"
msgstr ""
-msgid "must be unique by status and elapsed time within a policy"
+msgid "must belong to same project of its requirement object."
msgstr ""
msgid "must belong to same project of the work item."
@@ -48859,6 +49583,9 @@ msgstr ""
msgid "reCAPTCHA site key"
msgstr ""
+msgid "reached maximum depth"
+msgstr ""
+
msgid "recent activity"
msgstr ""
@@ -48912,6 +49639,10 @@ msgstr ""
msgid "role's base access level does not match the access level of the membership"
msgstr ""
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] ""
+
msgid "running"
msgstr ""
diff --git a/locale/zh_HK/gitlab.po.time_stamp b/locale/zh_HK/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/zh_HK/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po
index 490493fc116..72f9a69b312 100644
--- a/locale/zh_TW/gitlab.po
+++ b/locale/zh_TW/gitlab.po
@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-Language: zh-TW\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 16\n"
-"PO-Revision-Date: 2022-11-13 09:21\n"
+"PO-Revision-Date: 2022-12-11 07:26\n"
msgid " %{start} to %{end}"
msgstr " %{start} 到 %{end}"
@@ -159,7 +159,7 @@ msgstr[0] "剩下 %d 個字元"
msgid "%d child epic"
msgid_plural "%d child epics"
-msgstr[0] "%d 個å­å²è©©"
+msgstr[0] "%d 個å­å²è©© (epic) "
msgid "%d code quality issue"
msgid_plural "%d code quality issues"
@@ -211,7 +211,7 @@ msgstr[0] "%d 天"
msgid "%d epic"
msgid_plural "%d epics"
-msgstr[0] "%d 個å²è©©"
+msgstr[0] "%d 個å²è©© (epic) "
msgid "%d exporter"
msgid_plural "%d exporters"
@@ -289,6 +289,10 @@ msgid "%d more comment"
msgid_plural "%d more comments"
msgstr[0] "還有 %d 則留言"
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] "%d 個軟體套件"
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] "%d 待處ç†è©•è«–"
@@ -426,6 +430,12 @@ msgstr[0] "%{bold_start}%{count}%{bold_end} 個已開啟的åˆä½µè«‹æ±‚"
msgid "%{chartTitle} no data series"
msgstr "%{chartTitle} 沒有資料系列"
+msgid "%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable."
+msgstr "%{codeStart}$%{codeEnd} 將被視是為å°å¦ä¸€å€‹è®Šæ•¸å¼•ç”¨çš„啟始。"
+
+msgid "%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
+msgstr "%{code_open}Expanded:%{code_close} 帶有 %{code_open}$%{code_close} 的變數將被視為是å°å¦ä¸€å€‹è®Šæ•¸å¼•ç”¨çš„啟始。"
+
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr "%{code_open}éš±è—:%{code_close} éš±è—於作業日誌之中。必須符åˆéš±è—è¦å®šã€‚"
@@ -616,15 +626,15 @@ msgstr "%{group_name} 使用群組管ç†å¸³è™Ÿã€‚您需è¦å»ºç«‹ä¸€å€‹æ–°çš„ Git
msgid "%{group_name}&%{epic_iid} &middot; created %{epic_created} by %{author}"
msgstr "%{group_name}&%{epic_iid} &middot; %{epic_created} 由%{author}創建"
-msgid "%{hook_type} was deleted"
-msgstr "%{hook_type}已被刪除"
-
-msgid "%{hook_type} was scheduled for deletion"
-msgstr "%{hook_type}已安排刪除"
-
msgid "%{host} sign-in from new location"
msgstr "%{host} 從新ä½ç½®ç™»å…¥"
+msgid "%{human_readable_key} exceeds %{max_value_length} characters"
+msgstr "%{human_readable_key} è¶…éŽ %{max_value_length} 個字元"
+
+msgid "%{human_readable_key} is less than %{min_value_length} characters"
+msgstr "%{human_readable_key} 少於 %{min_value_length} 個字元"
+
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr "%{integrations_link_start}æ•´åˆ%{link_end}能讓你將第三方應用程å¼æˆç‚º GitLab工作æµç¨‹çš„一部分。若當å‰å¯ç”¨çš„æ•´åˆç„¡æ³•æ»¿è¶³ä½ çš„需求,å¯ä»¥è€ƒæ…®ä½¿ç”¨ %{webhooks_link_start}webhook%{link_end}。"
@@ -815,9 +825,6 @@ msgstr "%{openedEpics} 個開放,%{closedEpics} 個關閉"
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr "%{openedIssues} 個開放,%{closedIssues} 個關閉"
-msgid "%{over_limit_message} To get more members, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr "%{over_limit_message} è¦å–得更多æˆå“¡ï¼Œç¾¤çµ„çš„æ“有者å¯ä»¥é–‹å§‹è©¦ç”¨æˆ–å‡ç´šåˆ°ä»˜è²»å±¤ç´šã€‚"
-
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr "%{over_limit_message} %{link_start}å‡ç´šåˆ°ä»˜è²»æ–¹æ¡ˆ%{link_end}以ç²å¾—更多席次。"
@@ -856,7 +863,7 @@ msgstr "%{ref} 無法添加: %{error}"
msgid "%{releases} release"
msgid_plural "%{releases} releases"
-msgstr[0] "%{releases} 個發布版"
+msgstr[0] "%{releases} 個發布版本"
msgid "%{remaining_approvals} left"
msgstr "還剩 %{remaining_approvals} 個"
@@ -867,8 +874,8 @@ msgstr "%{reportType} %{status}"
msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr "%{reportType}檢測到%{totalStart}%{total}%{totalEnd}個潛在的%{vulnMessage}"
-msgid "%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities."
-msgstr "%{reportType} 未檢測到 %{totalStart}æ–°%{totalEnd} æ¼æ´žã€‚"
+msgid "%{reportType} detected no new vulnerabilities."
+msgstr "%{reportType} 未檢測到新的æ¼æ´žã€‚"
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
msgstr "%{retryButtonStart}è«‹é‡è©¦%{retryButtonEnd}或%{newFileButtonStart}附加一個新的檔案%{newFileButtonEnd}。"
@@ -1003,6 +1010,10 @@ msgstr "%{template_project_id} 為未知或無效"
msgid "%{text} is available"
msgstr "%{text} å¯ç”¨"
+msgid "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approval from %{approverType}%{approvers}"
+msgid_plural "%{thenLabelStart}Then%{thenLabelEnd} Require %{approvalsRequired} approvals from %{approverType}%{approvers}"
+msgstr[0] "%{thenLabelStart} 接下來 %{thenLabelEnd} 需è¦ä¾†è‡ª %{approverType}%{approvers} çš„ %{approvalsRequired} 核准"
+
msgid "%{timebox_type} does not support burnup charts"
msgstr "%{timebox_type}ä¸æ”¯æ´ç‡ƒç›¡åœ–"
@@ -1042,6 +1053,9 @@ msgstr "%{type} 必須為 %{help_link}"
msgid "%{type} only supports %{name} name"
msgstr "%{type} åªæ”¯æŒ %{name} åå­—"
+msgid "%{url} (optional)"
+msgstr "%{url} (é¸å¡«)"
+
msgid "%{userName} (cannot merge)"
msgstr "%{userName} (無法åˆä½µ)"
@@ -1076,7 +1090,7 @@ msgid "%{user} created a merge request: %{mr_link}"
msgstr "%{user} 建立了åˆä½µè«‹æ±‚: %{mr_link}"
msgid "%{user} created an epic: %{epic_link}"
-msgstr "%{user} 建立了å²è©©ï¼š %{epic_link}"
+msgstr "%{user} 建立了å²è©© (epic): %{epic_link}"
msgid "%{user} created an issue: %{issue_link}"
msgstr "%{user} 建立了一個議題: %{issue_link}"
@@ -1160,12 +1174,18 @@ msgstr "(+%{count}&nbsp;æ¢è¦å‰‡)"
msgid "(Group Managed Account)"
msgstr "(由群組管ç†çš„帳號)"
+msgid "(Limited to %{quota} pipeline minutes per month)"
+msgstr "(æ¯å€‹æœˆæµæ°´ç·šé™åˆ¶åˆ†é˜æ•¸ç‚º %{quota})"
+
msgid "(No changes)"
msgstr "(無變更)"
msgid "(UTC %{offset}) %{timezone}"
msgstr "(UTC %{offset}) %{timezone}"
+msgid "(Unlimited pipeline minutes)"
+msgstr "(無é™åˆ¶çš„æµæ°´ç·šåˆ†é˜æ•¸ï¼‰"
+
msgid "(check progress)"
msgstr "(檢查進度)"
@@ -1560,10 +1580,10 @@ msgid "A new personal access token has been created"
msgstr "已建立一個新的個人存å–權æ–(令牌)"
msgid "A new personal access token, named %{token_name}, has been created."
-msgstr "已建立一個新的個人存å–權æ–,命å為%{token_name}。"
+msgstr "已建立一個新的個人存å–權æ–(令牌),命å為%{token_name}。"
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
-msgstr "éžç§äººå²è©©ä¸å¯è¢«æŒ‡æ´¾åˆ°ç§äººçˆ¶å²è©©"
+msgstr "éžç§äººå²è©© (epic) ä¸å¯è¢«æŒ‡æ´¾åˆ°ç§äººçˆ¶å²è©© (epic) "
msgid "A non-confidential issue cannot have a confidential parent."
msgstr "éžæ©Ÿå¯†æ€§è­°é¡Œä¸èƒ½æœ‰æ©Ÿå¯†æ€§çš„父級。"
@@ -1851,7 +1871,7 @@ msgid "AccessTokens|Are you sure? Any issue email addresses currently in use wil
msgstr "你確定嗎?目å‰æ­£åœ¨ä½¿ç”¨çš„所有議題電å­éƒµä»¶ä½å€éƒ½å°‡åœæ­¢é‹ä½œã€‚"
msgid "AccessTokens|Copy feed token"
-msgstr "訪å•ä»¤ç‰Œ(權æ–)|複製æè¦ä»¤ç‰Œ(權æ–)"
+msgstr "複製æè¦æ¬Šæ–(令牌)"
msgid "AccessTokens|Copy incoming email token"
msgstr "複製傳入的電å­éƒµä»¶å­˜å–權æ–"
@@ -1863,7 +1883,7 @@ msgid "AccessTokens|Created"
msgstr "已建立"
msgid "AccessTokens|Feed token"
-msgstr "Feed 權æ–"
+msgstr "Feed 權æ–(令牌)"
msgid "AccessTokens|Incoming email token"
msgstr "傳入電å­éƒµä»¶æ¬Šæ–"
@@ -1878,10 +1898,10 @@ msgid "AccessTokens|Keep this token secret. Anyone who has it can create issues
msgstr "ä¿æŒè©²ä»¤ç‰Œ(權æ–)的機密。任何æ“有它的人都å¯ä»¥åƒæ‚¨ä¸€æ¨£å»ºç«‹è­°é¡Œã€‚如果發生這種情æ³ï¼Œè«‹ %{linkStart} é‡ç½®æ­¤ä»¤ç‰Œ(權æ–) %{linkEnd}。"
msgid "AccessTokens|Keep this token secret. Anyone who has it can read activity and issue RSS feeds or your calendar feed as if they were you. If that happens, %{linkStart}reset this token%{linkEnd}."
-msgstr "ä¿å¯†æ­¤æ¬Šæ–,å¦å‰‡ä»–人å¯ä»¥æ“有您查閱活動ã€ç™¼å¸ƒ RSS 關注或日曆關注的權é™ï¼Œå¦‚該情æ³ç™¼ç”Ÿï¼Œ %{linkStart}é‡ç½®æ­¤æ¬Šæ–%{linkEnd}。"
+msgstr "ä¿å¯†æ­¤æ¬Šæ–(令牌),å¦å‰‡ä»–人å¯ä»¥æ“有您查閱活動ã€ç™¼å¸ƒ RSS 關注或日曆關注的權é™ï¼Œå¦‚該情æ³ç™¼ç”Ÿï¼Œ %{linkStart}é‡ç½®æ­¤æ¬Šæ–%{linkEnd}。"
msgid "AccessTokens|Personal Access Tokens"
-msgstr "個人存å–權æ–"
+msgstr "個人存å–權æ–(令牌)"
msgid "AccessTokens|Static object token"
msgstr "éœæ…‹ç‰©ä»¶æ¬Šæ–"
@@ -1893,10 +1913,10 @@ msgid "AccessTokens|They are the only accepted password when you have Two-Factor
msgstr "當您啟用兩步驟èªè­‰ (2FA) 時,它們將是唯一能接å—的密碼。"
msgid "AccessTokens|You can also use personal access tokens to authenticate against Git over HTTP."
-msgstr "您還å¯ä»¥ä½¿ç”¨å€‹äººå­˜å–權æ–é€éŽ HTTP 進行 Git 驗證。"
+msgstr "您還å¯ä»¥ä½¿ç”¨å€‹äººå­˜å–權æ–(令牌)é€éŽ HTTP 進行 Git 驗證。"
msgid "AccessTokens|You can generate a personal access token for each application you use that needs access to the GitLab API."
-msgstr "您å¯ä»¥ç‚ºæ¯å€‹éœ€è¦å­˜å– GitLab API 的應用程å¼ç”Ÿæˆå€‹äººå­˜å–權æ–。"
+msgstr "您å¯ä»¥ç‚ºæ¯å€‹éœ€è¦å­˜å– GitLab API 的應用程å¼ç”Ÿæˆå€‹äººå­˜å–權æ–(令牌)。"
msgid "AccessTokens|Your feed token authenticates you when your RSS reader loads a personalized RSS feed or when your calendar application loads a personalized calendar. It is visible in those feed URLs."
msgstr "當您的 RSS 閱讀器加載個性化 RSS 訂閱æºæˆ–日曆應用程åºåŠ è¼‰å€‹æ€§åŒ–日曆時,您的訂閱æºæ¬Šæ–å°æ‚¨é€²è¡Œèº«ä»½é©—證。它在這些關注 URL 中å¯è¦‹ã€‚"
@@ -2073,7 +2093,7 @@ msgid "Add a numbered list"
msgstr "加入編號列表"
msgid "Add a related epic"
-msgstr "新增相關å²è©©"
+msgstr "新增相關å²è©© (epic) "
msgid "Add a related issue"
msgstr "增加一個相關議題"
@@ -2118,7 +2138,7 @@ msgid "Add broadcast message"
msgstr "添加廣播消æ¯"
msgid "Add child epic to an epic"
-msgstr "加入å­å²è©©åˆ°å²è©©"
+msgstr "加入å­å²è©© (epic) 到å²è©© (epic) "
msgid "Add comment now"
msgstr "ç«‹å³åŠ å…¥ç•™è¨€"
@@ -2231,11 +2251,14 @@ msgstr "加入系統掛鉤"
msgid "Add text to the sign-in page. Markdown enabled."
msgstr "加入文字到登入é é¢ã€‚Markdown 已啟用。"
+msgid "Add time entry"
+msgstr "加入時間項"
+
msgid "Add to board"
msgstr "新增到看æ¿"
msgid "Add to epic"
-msgstr "加到å²è©©"
+msgstr "加到å²è©© (epic) "
msgid "Add to merge train"
msgstr "加到åˆä½µéˆ"
@@ -2298,7 +2321,7 @@ msgid "Added"
msgstr "已加入"
msgid "Added %{epic_ref} as a child epic."
-msgstr "已加入 %{epic_ref} 為å­å²è©©ã€‚"
+msgstr "已加入 %{epic_ref} 為å­å²è©© (epic)。"
msgid "Added %{label_references} %{label_text}."
msgstr "已加入 %{label_references} %{label_text}。"
@@ -2307,7 +2330,7 @@ msgid "Added a to do."
msgstr "已添加一個代辦事項。"
msgid "Added an issue to an epic."
-msgstr "å·²å‘å²è©©åŠ å…¥è­°é¡Œã€‚"
+msgstr "å·²å‘å²è©© (epic) 加入議題。"
msgid "Added for this merge request"
msgstr "添加此åˆä½µè«‹æ±‚"
@@ -2318,6 +2341,9 @@ msgstr "此版本新增"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "ç›®å‰çš„ GitLab 實體ä¸å…許加入新應用程å¼ã€‚è«‹è¯çµ¡æ‚¨çš„ GitLab 管ç†å“¡ä»¥ç²å¾—相關權é™ã€‚"
+msgid "Additional diagram formats"
+msgstr "其他圖表格å¼"
+
msgid "Additional minutes"
msgstr "é¡å¤–分é˜"
@@ -2343,7 +2369,7 @@ msgid "Adds"
msgstr "加入"
msgid "Adds %{epic_ref} as child epic."
-msgstr "加入 %{epic_ref} 作為å­å²è©©ã€‚"
+msgstr "加入 %{epic_ref} 作為å­å²è©© (epic)。"
msgid "Adds %{labels} %{label_text}."
msgstr "加入 %{labels}%{label_text}。"
@@ -2364,7 +2390,7 @@ msgid "Adds a to do."
msgstr "新增待辦事項。"
msgid "Adds an issue to an epic."
-msgstr "å‘å²è©©åŠ å…¥è­°é¡Œã€‚"
+msgstr "å‘å²è©© (epic) 加入議題。"
msgid "Adds email participant(s)."
msgstr "新增電å­éƒµä»¶åƒèˆ‡è€…。"
@@ -2513,6 +2539,9 @@ msgstr "åœæ­¢ä½œæ¥­å¤±æ•—"
msgid "AdminArea|Total users"
msgstr "總使用者"
+msgid "AdminArea|Updated %{last_update_time}"
+msgstr "已更新於 %{last_update_time}"
+
msgid "AdminArea|Users"
msgstr "使用者"
@@ -2628,7 +2657,7 @@ msgid "AdminSettings|Disable Elasticsearch until indexing completes."
msgstr "åœç”¨ Elasticsearch 一直到索引建立完æˆã€‚"
msgid "AdminSettings|Disable feed token"
-msgstr "åœç”¨å‹•æ…‹æ¬Šæ–"
+msgstr "åœç”¨ feed 權æ–(令牌)"
msgid "AdminSettings|Disable public access to Pages sites"
msgstr "åœç”¨å…¬é–‹å­˜å–é é¢ç¶²é "
@@ -2682,7 +2711,7 @@ msgid "AdminSettings|Enforce invitation flow for groups and projects"
msgstr "強制執行群組和專案的邀請æµç¨‹"
msgid "AdminSettings|Feed token"
-msgstr "動態權æ–"
+msgstr "Feed 權æ–(令牌)"
msgid "AdminSettings|Git abuse rate limit"
msgstr "Git 濫用率é™åˆ¶"
@@ -2813,8 +2842,8 @@ msgstr "啟用 Elasticsearch 進行æœå°‹"
msgid "AdminSettings|Select a CI/CD template"
msgstr "é¸æ“‡ä¸€å€‹ CI/CD 範本"
-msgid "AdminSettings|Select a group to use as the source for instance-level project templates."
-msgstr "é¸å–一個群組作為實例級別專案模æ¿çš„來æº"
+msgid "AdminSettings|Select a group to use as a source of custom templates for new projects. %{link_start}Learn more%{link_end}."
+msgstr "é¸æ“‡ä¸€å€‹ç¾¤çµ„作為新專案的自定義範本來æºï¼Œ%{link_start}了解更多%{link_end}。"
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
msgstr "é¸æ“‡åœç”¨å…¬é–‹å­˜å–é é¢ç¶²ç«™ï¼Œè¡¨ç¤ºç”¨æˆ¶éœ€è¦ç™»å…¥æ‰èƒ½å­˜å–您實例的é é¢ç¶²ç«™ã€‚%{link_start}了解更多。%{link_end}"
@@ -2876,9 +2905,6 @@ msgstr "您 Jitsu 實例的主機。"
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr "æ¯å€‹å°ˆæ¡ˆæœ€æ–°æˆåŠŸçš„æµæ°´ç·šï¼Œå…¶æ‰€æœ‰ä»»å‹™çš„最新產物已被儲存,並且ä¸æœƒéŽæœŸã€‚"
-msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
-msgstr "å¯é¸æ“‡æ­¤ç¾¤çµ„的專案作為實例中新建立專案的模æ¿ã€‚%{link_start}了解更多。%{link_end} "
-
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr "å¿…è¦æµæ°´ç·šé…置的範本å¯ä»¥æ˜¯ GitLab æ供的範本之一,或者是新增到實例範本檔案庫的自訂範本。%{link_start}如何建立實例範本檔案庫?%{link_end}"
@@ -3150,7 +3176,7 @@ msgid "AdminUsers|Linkedin"
msgstr "Linkedin"
msgid "AdminUsers|Locked"
-msgstr "AdminUsers | 已鎖定"
+msgstr "已鎖定"
msgid "AdminUsers|Log in"
msgstr "登入"
@@ -3429,10 +3455,10 @@ msgid "After it expires, you can't use merge approvals, code quality, or many ot
msgstr "éŽæœŸå¾Œï¼Œæ‚¨å°‡ç„¡æ³•æ‰¹å‡†åˆä½µè«‹æ±‚ã€æª¢è¦–代碼å“質或許多其他功能。"
msgid "After it expires, you can't use merge approvals, epics, or many other features."
-msgstr "éŽæœŸå¾Œï¼Œæ‚¨å°‡ç„¡æ³•æ‰¹å‡†åˆä½µè«‹æ±‚ã€å²è©©æˆ–許多其他功能。"
+msgstr "éŽæœŸå¾Œï¼Œæ‚¨å°‡ç„¡æ³•æ‰¹å‡†åˆä½µè«‹æ±‚ã€å²è©© (epics) 或許多其他功能。"
msgid "After it expires, you can't use merge approvals, epics, or many security features."
-msgstr "éŽæœŸå¾Œï¼Œæ‚¨å°‡ç„¡æ³•æ‰¹å‡†åˆä½µè«‹æ±‚ã€å²è©©æˆ–多安全功能。"
+msgstr "éŽæœŸå¾Œï¼Œæ‚¨å°‡ç„¡æ³•æ‰¹å‡†åˆä½µè«‹æ±‚ã€å²è©© (epics) 或多安全功能。"
msgid "After the export is complete, download the data file from a notification email or from this page. You can then import the data file from the %{strong_text_start}Create new group%{strong_text_end} page of another GitLab instance."
msgstr "匯出完æˆå¾Œï¼Œå¾žé€šçŸ¥é›»å­éƒµä»¶æˆ–æ­¤é é¢ä¸‹è¼‰æ•¸æ“šæ–‡ä»¶ã€‚然後,您å¯ä»¥å¾žå¦ä¸€å€‹ GitLab 實例的 %{strong_text_start}建立新群組%{strong_text_end} é é¢åŒ¯å…¥æ•¸æ“šæ–‡ä»¶ã€‚"
@@ -3531,7 +3557,7 @@ msgid "AlertManagement|None"
msgstr "ç„¡"
msgid "AlertManagement|Open"
-msgstr "打開"
+msgstr "é–‹å•Ÿ"
msgid "AlertManagement|Please try again."
msgstr "è«‹å†è©¦ä¸€æ¬¡ã€‚"
@@ -3561,7 +3587,7 @@ msgid "AlertManagement|Status"
msgstr "狀態"
msgid "AlertManagement|Surface alerts in GitLab"
-msgstr "GitLab中的表é¢è­¦å ±"
+msgstr "GitLab中的警示"
msgid "AlertManagement|There was an error displaying the alert. Please refresh the page to try again."
msgstr "顯示警報時出ç¾éŒ¯èª¤ã€‚è«‹é‡æ–°æ•´ç†é é¢ï¼Œç„¶å¾Œé‡è©¦ã€‚"
@@ -4071,7 +4097,7 @@ msgid "An error occurred while adding approvers"
msgstr "新增核准者時發生錯誤"
msgid "An error occurred while adding formatted title for epic"
-msgstr "為å²è©©(epic)添加格å¼åŒ–標題時發生錯誤"
+msgstr "為å²è©© (epic) 添加格å¼åŒ–標題時發生錯誤"
msgid "An error occurred while approving, please try again."
msgstr "核准時發生錯誤,請é‡è©¦ã€‚"
@@ -4314,7 +4340,7 @@ msgid "An error occurred while performing this action."
msgstr "執行該æ“作時發生錯誤"
msgid "An error occurred while removing epics."
-msgstr "移除å²è©©æ™‚發生錯誤。"
+msgstr "移除å²è©© (epic) 時發生錯誤。"
msgid "An error occurred while removing issues."
msgstr "移除議題時發生錯誤。"
@@ -4404,6 +4430,9 @@ msgstr "發生錯誤,請é‡æ–°ç™»å…¥ã€‚"
msgid "An error occurred. Please try again."
msgstr "發生了錯誤,請å†è©¦ä¸€æ¬¡ã€‚"
+msgid "An error occurred. Unable to reopen this merge request."
+msgstr "發生錯誤,無法é‡æ–°é–‹å•Ÿè©²åˆä½µè«‹æ±‚。"
+
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr "ç®¡ç† Kubernetes å¢é›†èˆ‡ GitLab æ•´åˆåœ¨ä¸€èµ·çš„範例專案"
@@ -4521,9 +4550,6 @@ msgstr "任何里程碑"
msgid "Any namespace"
msgstr "任何命å空間"
-msgid "Anyone can register for an account."
-msgstr "任何人都å¯ä»¥è¨»å†Šä¸€å€‹å¸³è™Ÿã€‚"
-
msgid "App ID"
msgstr "應用程å¼ID"
@@ -5054,6 +5080,9 @@ msgid "Are you sure you want to import %d repository?"
msgid_plural "Are you sure you want to import %d repositories?"
msgstr[0] "您確定è¦åŒ¯å…¥%d 檔案庫嗎?"
+msgid "Are you sure you want to leave the Web IDE? All unsaved changes will be lost."
+msgstr "您確定è¦é€€å‡ºè©² Web IDE å—Ž? 所有未儲存的更動都將éºå¤±ã€‚"
+
msgid "Are you sure you want to lock %{path}?"
msgstr "您確定è¦éŽ–ä½ %{path}?"
@@ -5121,7 +5150,7 @@ msgid "Are you sure you want to revoke this group access token? This action cann
msgstr "您確定è¦æ’¤å›žæ­¤ç¾¤çµ„å­˜å–權æ–嗎?此動作無法復原。"
msgid "Are you sure you want to revoke this personal access token? This action cannot be undone."
-msgstr "您確定è¦æ’¤å›žæ­¤å€‹äººå­˜å–憑證 (access token)嗎?此動作無法復原。"
+msgstr "您確定è¦æ’¤å›žæ­¤å€‹äººå­˜å–權æ–(令牌)嗎?此動作無法復原。"
msgid "Are you sure you want to revoke this project access token? This action cannot be undone."
msgstr "您確定è¦æ’¤å›žæ­¤å°ˆæ¡ˆå­˜å–憑證 (access token)嗎?此動作無法復原。"
@@ -5205,7 +5234,7 @@ msgid "AsanaService|Comma-separated list of branches to be automatically inspect
msgstr "è¦è‡ªå‹•æª¢æŸ¥çš„以逗號分隔的分支列表。留空以包括所有分支。"
msgid "AsanaService|User Personal Access Token. User must have access to the task. All comments are attributed to this user."
-msgstr "使用者個人存å–憑證。使用者必須æ“有存å–任務的權é™ã€‚將會以使用者的身分進行所有留言。"
+msgstr "使用者個人存å–權æ–(令牌)。使用者必須æ“有存å–任務的權é™ã€‚將會以使用者的身分進行所有留言。"
msgid "Ascending"
msgstr "å‡åºæŽ’列"
@@ -5326,10 +5355,10 @@ msgid "At least one of group_id or project_id must be specified"
msgstr "必須指定至少一個group_id或 project_id"
msgid "At least one of your Personal Access Tokens is expired. %{generate_new}"
-msgstr "您的個人存å–令牌(權æ–)中至少有一個已éŽæœŸã€‚ %{generate_new}"
+msgstr "您的個人存å–權æ–(令牌)中至少有一個已éŽæœŸã€‚ %{generate_new}"
msgid "At least one of your Personal Access Tokens will expire soon. %{generate_new}"
-msgstr "您的個人存å–令牌(權æ–)中至少有一個å³å°‡éŽæœŸã€‚ %{generate_new}"
+msgstr "您的個人存å–權æ–(令牌)中至少有一個å³å°‡éŽæœŸã€‚ %{generate_new}"
msgid "At risk"
msgstr "存在風險"
@@ -5459,6 +5488,9 @@ msgstr "刪除 %{link}"
msgid "AuditStreams|Destination URL"
msgstr "目的地 URL"
+msgid "AuditStreams|Destination has filters applied. %{linkStart}What are filters?%{linkEnd}"
+msgstr "目的地套用了éŽæ¿¾å™¨ï¼Œ%{linkStart}什麼是éŽæ¿¾å™¨ï¼Ÿ%{linkEnd}"
+
msgid "AuditStreams|Destinations receive all audit event data"
msgstr "目的地接收所有審計事件數據"
@@ -5504,6 +5536,9 @@ msgstr "價值"
msgid "AuditStreams|Verification token"
msgstr "驗證權æ–"
+msgid "AuditStreams|filtered"
+msgstr "éŽæ¿¾"
+
msgid "Aug"
msgstr "8月"
@@ -5735,9 +5770,6 @@ msgstr "å¯ä½¿ç”¨çš„群組執行器:%{runners}"
msgid "Available on-demand"
msgstr "按需æä¾›"
-msgid "Available shared runners:"
-msgstr "å¯å…±ç”¨çš„執行器:"
-
msgid "Available specific runners"
msgstr "å¯æŒ‡å®šçš„執行器(Runners)"
@@ -6431,7 +6463,7 @@ msgid "Blocking"
msgstr "å°éŽ–中"
msgid "Blocking epics"
-msgstr "å°éŽ–å²è©©"
+msgstr "å°éŽ–å²è©© (epics) "
msgid "Blocking issues"
msgstr "å°éŽ–中議題"
@@ -6443,25 +6475,25 @@ msgid "Blog"
msgstr "部è½æ ¼"
msgid "Board scope affects which epics are displayed for anyone who visits this board"
-msgstr "看æ¿ç¯„åœæœƒå½±éŸ¿ä»»ä½•å­˜å–此看æ¿çš„人å¯ä»¥çœ‹åˆ°å“ªäº›å²è©©é¡¯ç¤º"
+msgstr "看æ¿ç¯„åœæœƒå½±éŸ¿ä»»ä½•å­˜å–此看æ¿çš„人å¯ä»¥çœ‹åˆ°å“ªäº›å²è©© (epics) 顯示"
msgid "Board scope affects which issues are displayed for anyone who visits this board"
msgstr "看æ¿ç¯„åœæœƒå½±éŸ¿å­˜å–此看æ¿çš„人å¯ä»¥é¡¯ç¤ºå“ªäº›è­°é¡Œ"
msgid "BoardNewEpic|Groups"
-msgstr "BoardNewEpic|群組"
+msgstr "群組"
msgid "BoardNewEpic|Loading groups"
-msgstr "BoardNewEpic|加載群組"
+msgstr "加載群組"
msgid "BoardNewEpic|No matching results"
-msgstr "BoardNewEpic|沒有符åˆçš„çµæžœ"
+msgstr "沒有符åˆçš„çµæžœ"
msgid "BoardNewEpic|Search groups"
-msgstr "BoardNewEpic|æœå°‹ç¾¤çµ„"
+msgstr "æœå°‹ç¾¤çµ„"
msgid "BoardNewEpic|Select a group"
-msgstr "BoardNewEpic|é¸æ“‡ä¸€å€‹ç¾¤çµ„"
+msgstr "é¸æ“‡ä¸€å€‹ç¾¤çµ„"
msgid "BoardNewIssue|No matching results"
msgstr "沒有符åˆçš„çµæžœ"
@@ -6573,7 +6605,7 @@ msgid_plural "Boards|+ %{displayedIssuablesCount} more %{issuableType}s"
msgstr[0] "+ %{displayedIssuablesCount} 更多 %{issuableType}"
msgid "Boards|An error occurred while creating the epic. Please try again."
-msgstr "建立epic時發生錯誤,請é‡è©¦ã€‚"
+msgstr "建立å²è©© (epic) 時發生錯誤,請é‡è©¦ã€‚"
msgid "Boards|An error occurred while creating the issue. Please try again."
msgstr "建立議題時發生錯誤。請é‡è©¦ã€‚"
@@ -6594,7 +6626,7 @@ msgid "Boards|An error occurred while fetching labels. Please reload the page."
msgstr "抓å–標籤時發生錯誤。請é‡æ–°è¼‰å…¥é é¢ã€‚"
msgid "Boards|An error occurred while fetching the board epics. Please reload the page."
-msgstr "抓å–看æ¿å²è©© (epic) 時發生錯誤。請é‡æ–°è¼‰å…¥é é¢ã€‚"
+msgstr "æ“·å–看æ¿å²è©© (epic) 時發生錯誤,請é‡æ–°è¼‰å…¥é é¢ã€‚"
msgid "Boards|An error occurred while fetching the board issues. Please reload the page."
msgstr "抓å–看æ¿è­°é¡Œæ™‚發生錯誤。請é‡æ–°è¼‰å…¥é é¢ã€‚"
@@ -6612,7 +6644,7 @@ msgid "Boards|An error occurred while generating lists. Please reload the page."
msgstr "產生清單時發生錯誤。請é‡æ–°åŠ è¼‰é é¢ã€‚"
msgid "Boards|An error occurred while moving the epic. Please try again."
-msgstr "移動 epic 時發生錯誤。請é‡è©¦ã€‚"
+msgstr "移動å²è©© (epic) 時發生錯誤。請é‡è©¦ã€‚"
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr "移動å•é¡Œ (issue)時發生錯誤。請é‡è©¦ã€‚"
@@ -6652,7 +6684,7 @@ msgid "Boards|New board"
msgstr "新建看æ¿"
msgid "Boards|New epic"
-msgstr "新建å²è©©"
+msgstr "新建å²è©© (epic) "
msgid "Boards|Retrieving blocking %{issuableType}s"
msgstr "正在å–回å°éŽ–中的%{issuableType}"
@@ -6691,13 +6723,13 @@ msgid "Board|Failed to delete board. Please try again."
msgstr "刪除看æ¿å¤±æ•—,請é‡è©¦ã€‚"
msgid "Board|Load more epics"
-msgstr "加載更多å²è©©"
+msgstr "加載更多å²è©© (epics) "
msgid "Board|Load more issues"
msgstr "載入更多議題"
msgid "Board|Loading epics"
-msgstr "正在加載å²è©©"
+msgstr "正在加載å²è©© (epics) "
msgid "Bold text"
msgstr "粗體字"
@@ -6753,6 +6785,15 @@ msgstr "%{linkStart}è¬ç”¨å­—å…ƒ%{linkEnd} å¯æ”¯æ´å¦‚ *-stable 或 production/
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
msgstr "%{linkStart}è¬ç”¨å­—å…ƒ%{linkEnd} å¯æ”¯æ´å½¢å¦‚ *-stable or production/* 。"
+msgid "BranchRules|%{total} approval %{subject}"
+msgstr "%{total} 核准 %{subject}"
+
+msgid "BranchRules|%{total} matching %{subject}"
+msgstr "%{total} ç¬¦åˆ %{subject}"
+
+msgid "BranchRules|%{total} status %{subject}"
+msgstr "%{total} 狀態 %{subject}"
+
msgid "BranchRules|All branches"
msgstr "全部分支"
@@ -6867,9 +6908,6 @@ msgstr "使用者"
msgid "BranchRules|default"
msgstr "é è¨­"
-msgid "BranchRules|protected"
-msgstr "å—ä¿è­·çš„"
-
msgid "Branches"
msgstr "分支"
@@ -6879,6 +6917,9 @@ msgstr "分支:%{source_branch} 到 %{target_branch}"
msgid "Branches: %{source_branch} → %{target_branch}"
msgstr "分支:%{source_branch} → %{target_branch}"
+msgid "Branches|A branch won't be deleted if it is protected or associated with an open merge request."
+msgstr "如果分支å—到ä¿è­·æˆ–與開啟的åˆä½µè«‹æ±‚相關è¯ï¼Œå‰‡ä¸æœƒè¢«åˆªé™¤ã€‚"
+
msgid "Branches|Active"
msgstr "æ´»èº"
@@ -6900,8 +6941,11 @@ msgstr "分支|找ä¸åˆ°è©²åˆ†æ”¯çš„ HEAD æ交"
msgid "Branches|Compare"
msgstr "比較"
-msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "刪除所有已åˆä½µåˆ° %{default_branch} 的分支。"
+msgid "Branches|Delete all branches that are merged into '%{defaultBranch}'"
+msgstr "刪除所有已åˆä½µåˆ°â€œ%{defaultBranch}â€çš„分支"
+
+msgid "Branches|Delete all merged branches?"
+msgstr "刪除所有已åˆä½µçš„分支?"
msgid "Branches|Delete branch"
msgstr "刪除分支"
@@ -6921,9 +6965,6 @@ msgstr "分支|刪除å—ä¿è­·çš„分支,您確定繼續嗎?"
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr "分支|刪除 %{strongStart}%{branchName}%{strongEnd} 分支æ“作無法撤銷。您確定繼續嗎?"
-msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "刪除已åˆä½µçš„分支後將無法回復,您確定繼續嗎?"
-
msgid "Branches|Filter by branch name"
msgstr "僅篩é¸åˆ†æ”¯å稱"
@@ -6945,6 +6986,9 @@ msgstr "概覽"
msgid "Branches|Please type the following to confirm:"
msgstr "分支|請輸入以下內容進行確èªï¼š"
+msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}."
+msgstr "請輸入以下內容進行確èªï¼š%{codeStart}delete%{codeEnd}。"
+
msgid "Branches|Show active branches"
msgstr "查看活èºåˆ†æ”¯"
@@ -6978,6 +7022,12 @@ msgstr "無法刪除é è¨­åˆ†æ”¯"
msgid "Branches|This branch hasn't been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr "此分支尚未åˆä½µåˆ° %{defaultBranchName}。為é¿å…資料éºå¤±ï¼Œè«‹è€ƒæ…®åœ¨åˆªé™¤ä¹‹å‰åˆä½µæ­¤åˆ†æ”¯ã€‚"
+msgid "Branches|This bulk action is %{strongStart}permanent and cannot be undone or recovered%{strongEnd}."
+msgstr "該批次æ“作是%{strongStart}永久性且無法撤消或復原%{strongEnd}。"
+
+msgid "Branches|This may include merged branches that are not visible on the current screen."
+msgstr "這å¯èƒ½åŒ…括在當å‰èž¢å¹•ä¸Šä¸å¯è¦‹çš„åˆä½µåˆ†æ”¯ã€‚"
+
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr "è¦æ”¾æ£„本機變更並覆蓋上游版本的分支,請在此處將其刪除,然後é¸æ“‡ä¸Šé¢çš„「立å³æ›´æ–°ã€ã€‚"
@@ -6990,6 +7040,9 @@ msgstr "分支|是的,刪除分支"
msgid "Branches|Yes, delete protected branch"
msgstr "是的,刪除å—ä¿è­·çš„分支"
+msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}."
+msgstr "您å³å°‡ %{strongStart} 刪除已åˆä½µåˆ° %{codeStart}%{defaultBranch}%{codeEnd} 的所有分支 %{strongEnd}。"
+
msgid "Branches|You're about to permanently delete the branch %{branchName}."
msgstr "您將永久刪除 %{branchName} 分支。"
@@ -7044,6 +7097,9 @@ msgstr "抓å–產物時發生錯誤"
msgid "BuildArtifacts|Loading artifacts"
msgstr "正在載入產物"
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr "正在建構您的åˆä½µè«‹æ±‚,請ç¨ç­‰ç‰‡åˆ»å¾Œåˆ·æ–°è©²é é¢ã€‚"
+
msgid "Built-in"
msgstr "內建"
@@ -7210,7 +7266,7 @@ msgid "By authenticating with an account tied to an Enterprise e-mail address, i
msgstr "通éŽä½¿ç”¨èˆ‡ä¼æ¥­é›»å­éƒµä»¶åœ°å€ç¶å®šçš„帳號進行身份驗證,å¯ä»¥å¾—知該帳號是ä¼æ¥­ä½¿ç”¨è€…。 "
msgid "By default, all projects and groups will use the global notifications setting."
-msgstr "é è¨­æƒ…æ³ä¸‹ï¼Œæ‰€æœ‰é …目和群組將使用全域通知設定。"
+msgstr "é è¨­æƒ…æ³ä¸‹ï¼Œæ‰€æœ‰å°ˆæ¡ˆå’Œç¾¤çµ„將使用全域通知設定。"
msgid "By month"
msgstr "按月"
@@ -7553,13 +7609,13 @@ msgid "Canceled deployment to"
msgstr "å·²å–消佈署至"
msgid "Cancelled"
-msgstr "å·²å–消"
+msgstr "å–消"
msgid "Cancelling Preview"
msgstr "å–消é è¦½"
msgid "Cannot assign a confidential epic to a non-confidential issue. Make the issue confidential and try again"
-msgstr "無法將機密å²è©©åˆ†é…給éžæ©Ÿå¯†è­°é¡Œã€‚請將此議題設置機密並é‡è©¦"
+msgstr "無法將機密å²è©© (epic) 分é…給éžæ©Ÿå¯†è­°é¡Œï¼Œè«‹å°‡æ­¤è­°é¡Œè¨­ç½®æ©Ÿå¯†ä¸¦é‡è©¦"
msgid "Cannot be merged automatically"
msgstr "無法自動åˆä½µ"
@@ -7589,10 +7645,10 @@ msgid "Cannot import because issues are not available in this project."
msgstr "無法匯入,因為議題在此專案中ä¸å¯ç”¨ã€‚"
msgid "Cannot make the epic confidential if it contains non-confidential child epics"
-msgstr "如果å²è©©åŒ…å«éžç§å¯†å­å²è©©ï¼Œå‰‡ç„¡æ³•å°‡è©²å²è©©è¨­ç½®ç‚ºç§å¯†ã€‚"
+msgstr "如果å²è©© (epic) 包å«éžç§å¯†å­å²è©© (epic) ,則無法將該å²è©© (epic) 設置為ç§å¯†ã€‚"
msgid "Cannot make the epic confidential if it contains non-confidential issues"
-msgstr "如果å²è©©åŒ…å«éžç§å¯†è­°é¡Œï¼Œå‰‡ç„¡æ³•å°‡è©²å²è©©è¨­ç½®ç‚ºç§å¯†ã€‚"
+msgstr "如果å²è©© (epic) 包å«éžç§å¯†è­°é¡Œï¼Œå‰‡ç„¡æ³•å°‡è©²å²è©© (epic) 設置為ç§å¯†ã€‚"
msgid "Cannot merge"
msgstr "無法åˆä½µ"
@@ -7849,6 +7905,9 @@ msgstr "æµæ°´ç·š#%{pipeline_id}%{humanized_status}耗時%{duration}"
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}"
msgstr "ç”±%{user_combined_name}%{humanized_status}觸發的%{ref_type}%{ref_link}æµæ°´ç·š%{pipeline_link}"
+msgid "ChatMessage|Pipeline name"
+msgstr "æµæ°´ç·šå稱"
+
msgid "ChatMessage|Tag"
msgstr "標籤"
@@ -7897,6 +7956,9 @@ msgstr "檢查您的Docker 映åƒæª”是å¦å­˜åœ¨å·²çŸ¥æ¼æ´žã€‚"
msgid "Check your Kubernetes cluster images for known vulnerabilities."
msgstr "檢查您的Kuberneteså¢é›†æ˜ åƒæª”是å¦å­˜åœ¨å·²çŸ¥æ¼æ´žã€‚"
+msgid "Check your sign-up restrictions"
+msgstr "檢查您的註冊é™åˆ¶"
+
msgid "Checking %{text} availability…"
msgstr "正在檢查%{text}çš„å¯ç”¨æ€§..."
@@ -8016,9 +8078,6 @@ msgstr "信用å¡è¡¨å–®è¼‰å…¥å¤±æ•—:%{message}"
msgid "Checkout|Edit"
msgstr "編輯"
-msgid "Checkout|Enter a number greater than 0"
-msgstr "請輸入一個大於0的數字"
-
msgid "Checkout|Exp %{expirationMonth}/%{expirationYear}"
msgstr "有效期至 %{expirationMonth}/%{expirationYear}"
@@ -8034,8 +8093,8 @@ msgstr "加載國家失敗。請é‡è©¦ã€‚"
msgid "Checkout|Failed to load states. Please try again."
msgstr "加載州失敗。請é‡è©¦ã€‚"
-msgid "Checkout|Failed to load the payment form. Please try again."
-msgstr "加載付款表格失敗。請é‡è©¦ã€‚"
+msgid "Checkout|Failed to load the payment form. Refresh the page and try again."
+msgstr "付款表格載入失敗,請刷新é é¢é‡è©¦ã€‚"
msgid "Checkout|Failed to register credit card. Please try again."
msgstr "信用å¡è¨»å†Šå¤±æ•—。請é‡è©¦ã€‚"
@@ -8058,6 +8117,9 @@ msgstr "此數字必須為 %{minimumNumberOfUsers} (使用中的席ä½æ•¸ï¼‰æˆ
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use, plus all over limit members) or more. To buy fewer seats, remove members from the group."
msgstr "必須是 %{minimumNumberOfUsers}(您正在使用的席次,加上所有超é™æˆå“¡ï¼‰æˆ–更多。è¦æ¸›å°‘購買的席ä½ï¼Œè«‹å¾žç¾¤çµ„中多除æˆå“¡ã€‚"
+msgid "Checkout|Must be 1 or more. Cannot be a decimal."
+msgstr "必須是 1 或以上,ä¸èƒ½æœ‰å°æ•¸ã€‚"
+
msgid "Checkout|Name of company or organization using GitLab"
msgstr "使用 GitLab çš„å…¬å¸æˆ–組織å稱"
@@ -8161,16 +8223,16 @@ msgid "Child"
msgstr "å­ç´š"
msgid "Child epic"
-msgstr "å­å²è©©"
+msgstr "å­å²è©© (epic) "
msgid "Child epic does not exist."
-msgstr "å­å²è©©ä¸å­˜åœ¨ã€‚"
+msgstr "å­å²è©© (epic) ä¸å­˜åœ¨ã€‚"
msgid "Child epic doesn't exist."
-msgstr "å­å²è©©ä¸å­˜åœ¨ã€‚"
+msgstr "å­å²è©© (epic) ä¸å­˜åœ¨ã€‚"
msgid "Child issues and epics"
-msgstr "å­è­°é¡Œèˆ‡å²è©©"
+msgstr "å­è­°é¡Œèˆ‡å²è©© (epic) "
msgid "Chinese language support using"
msgstr "支æŒä½¿ç”¨ä¸­æ–‡"
@@ -8224,7 +8286,7 @@ msgid "CiCdAnalytics|Date range: %{range}"
msgstr "日期範åœï¼š%{range}"
msgid "CiStatusLabel|canceled"
-msgstr "å·²å–消"
+msgstr "å–消"
msgid "CiStatusLabel|created"
msgstr "已建立"
@@ -8233,13 +8295,13 @@ msgid "CiStatusLabel|delayed"
msgstr "已延é²"
msgid "CiStatusLabel|failed"
-msgstr "已失敗"
+msgstr "失敗"
msgid "CiStatusLabel|manual action"
msgstr "手動æ“作"
msgid "CiStatusLabel|passed"
-msgstr "已通éŽ"
+msgstr "通éŽ"
msgid "CiStatusLabel|passed with warnings"
msgstr "已通éŽä½†æœ‰è­¦å‘Š"
@@ -8251,7 +8313,7 @@ msgid "CiStatusLabel|preparing"
msgstr "準備"
msgid "CiStatusLabel|skipped"
-msgstr "已跳éŽ"
+msgstr "ç•¥éŽ"
msgid "CiStatusLabel|waiting for delayed job"
msgstr "等待已延é²çš„作業"
@@ -8263,10 +8325,10 @@ msgid "CiStatusLabel|waiting for resource"
msgstr "等待資æº"
msgid "CiStatusText|blocked"
-msgstr "å·²å°ç¦"
+msgstr "鎖定"
msgid "CiStatusText|canceled"
-msgstr "å·²å–消"
+msgstr "å–消"
msgid "CiStatusText|created"
msgstr "已建立"
@@ -8275,13 +8337,13 @@ msgid "CiStatusText|delayed"
msgstr "已延é²"
msgid "CiStatusText|failed"
-msgstr "已失敗"
+msgstr "失敗"
msgid "CiStatusText|manual"
msgstr "手動æ“作"
msgid "CiStatusText|passed"
-msgstr "已通éŽ"
+msgstr "通éŽ"
msgid "CiStatusText|pending"
msgstr "等待中"
@@ -8290,7 +8352,7 @@ msgid "CiStatusText|preparing"
msgstr "準備"
msgid "CiStatusText|skipped"
-msgstr "已跳éŽ"
+msgstr "ç•¥éŽ"
msgid "CiStatusText|waiting"
msgstr "等待"
@@ -8301,9 +8363,15 @@ msgstr "執行中"
msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr "ç›®å‰æ•¸å€¼ç„¡æ³•ä½¿ç”¨è®Šæ•¸é®ç½©"
+msgid "CiVariables|Clear inputs"
+msgstr "清除輸入"
+
msgid "CiVariables|Environments"
msgstr "環境"
+msgid "CiVariables|Expanded"
+msgstr "展開"
+
msgid "CiVariables|Input variable key"
msgstr "輸入變數的å稱"
@@ -8316,6 +8384,9 @@ msgstr "éµ"
msgid "CiVariables|Masked"
msgstr "é®ç½©"
+msgid "CiVariables|Maximum number of variables reached."
+msgstr "å·²é”到最大變數數é‡ã€‚"
+
msgid "CiVariables|Options"
msgstr "é¸é …"
@@ -8328,6 +8399,9 @@ msgstr "移除變數"
msgid "CiVariables|Remove variable row"
msgstr "移除變數行"
+msgid "CiVariables|Run job again"
+msgstr "å†æ¬¡åŸ·è¡Œä½œæ¥­"
+
msgid "CiVariables|Scope"
msgstr "範åœ"
@@ -8337,6 +8411,9 @@ msgstr "指定è¦åœ¨æ­¤åŸ·è¡Œä¸­ä½¿ç”¨çš„變數值。%{linkStart}CI/CD設定%{li
msgid "CiVariables|State"
msgstr "狀態"
+msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
+msgstr "該 %{entity} 具有 %{currentVariableCount} 已定義的 CI/CD 變數,æ¯å€‹ %{entity} 的最大變數數é‡ç‚º %{maxVariableLimit},è¦å¢žåŠ æ–°çš„變數,您必須先減少已定義變數的數é‡ã€‚"
+
msgid "CiVariables|Type"
msgstr "é¡žåž‹"
@@ -8346,6 +8423,12 @@ msgstr "值"
msgid "CiVariables|Variables"
msgstr "變數"
+msgid "CiVariables|Variables store information, like passwords and secret keys, that you can use in job scripts. Each %{entity} can define a maximum of %{limit} variables."
+msgstr "變數儲存您å¯ä»¥åœ¨ä½œæ¥­è…³æœ¬ä¸­ä½¿ç”¨çš„資訊,例如密碼和密鑰,æ¯å€‹ %{entity} 最多å¯ä»¥å®šç¾© %{limit} 個變數。"
+
+msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables."
+msgstr "您已é”到å¯ä½¿ç”¨è®Šæ•¸çš„最大數é‡ï¼Œè¦å¢žåŠ æ–°çš„變數,您必須先減少已定義變數的數é‡ã€‚"
+
msgid "CiVariable|* (All environments)"
msgstr "* (所有環境)"
@@ -8420,7 +8503,7 @@ msgid "Clear templates search input"
msgstr "清除範本æœå°‹è¼¸å…¥"
msgid "Clear this checkbox to use a personal access token instead."
-msgstr "清除此勾é¸æ¡†ä»¥æ”¹ç”¨å€‹äººå­˜å–憑證 (access token)。"
+msgstr "清除此勾é¸æ¡†ä»¥æ”¹ç”¨å€‹äººå­˜å–權æ–(令牌)。"
msgid "Clear this checkbox to use a personal access token or LDAP password instead."
msgstr "清除此勾é¸æ¡†ï¼Œä½¿ç”¨å€‹äººè¨ªå•ä»¤ç‰Œ(權æ–)或 LDAP 密碼代替。"
@@ -8495,7 +8578,7 @@ msgid "Clone with KRB5"
msgstr "以 KRB5 複製"
msgid "Clone with SSH"
-msgstr "以 SSH 克隆 (Clone)"
+msgstr "使用 SSH 克隆 (Clone)"
msgid "CloneIssue|Cannot clone issue due to insufficient permissions!"
msgstr "於權é™ä¸è¶³ï¼Œç„¡æ³•è¤‡è£½ç‰ˆæœ¬åº«è­°é¡Œ (issue)ï¼"
@@ -8528,7 +8611,7 @@ msgid "Close design"
msgstr "關閉設計"
msgid "Close epic"
-msgstr "關閉å²è©©"
+msgstr "關閉å²è©© (epic)"
msgid "Close milestone"
msgstr "關閉里程碑"
@@ -9035,6 +9118,9 @@ msgstr "ClusterAgents|令牌(權æ–)被 %{userName} 撤回"
msgid "ClusterAgents|Unknown user"
msgstr "ClusterAgents|未知使用者"
+msgid "ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd})."
+msgstr "使用與您 Kubernetes 版本相容的 Helm 版本(請åƒé–± %{linkStart}Helm 版本支æŒæ”¿ç­–%{linkEnd})。"
+
msgid "ClusterAgents|Valid access token"
msgstr "有效的存å–權æ–"
@@ -9576,13 +9662,13 @@ msgid "ColorWidget|An error occurred while updating color."
msgstr "æ›´æ–°é¡è‰²æ™‚發生錯誤。"
msgid "ColorWidget|Assign epic color"
-msgstr "設定å²è©©é¡è‰²"
+msgstr "設定å²è©© (epic) é¡è‰²"
msgid "ColorWidget|Color"
msgstr "é¡è‰²"
msgid "ColorWidget|Error fetching epic color."
-msgstr "讀å–å²è©©é¡è‰²æ™‚發生錯誤"
+msgstr "讀å–å²è©© (epic) é¡è‰²æ™‚發生錯誤"
msgid "Colorize messages"
msgstr "為消æ¯è‘—色"
@@ -9811,7 +9897,7 @@ msgid "CompareRevisions|There was an error while updating the branch/tag list. P
msgstr "比較修訂|更新分支/標籤列表時發生錯誤。請é‡è©¦ã€‚"
msgid "CompareRevisions|View open merge request"
-msgstr "比較修訂|檢視開啟的åˆä½µè«‹æ±‚"
+msgstr "檢視開啟的åˆä½µè«‹æ±‚"
msgid "Complete"
msgstr "完æˆ"
@@ -9855,6 +9941,9 @@ msgstr "åˆè¦æµæ°´ç·šçµ„態設定(å¯é¸ï¼‰"
msgid "ComplianceFrameworks|Configuration not found"
msgstr "找ä¸åˆ°çµ„態設定"
+msgid "ComplianceFrameworks|Default compliance framework successfully updated"
+msgstr "é è¨­åˆè¦æ€§æ¡†æž¶å·²æˆåŠŸæ›´æ–°"
+
msgid "ComplianceFrameworks|Delete compliance framework %{framework}"
msgstr "刪除%{framework}åˆè¦æ¡†æž¶"
@@ -9879,6 +9968,9 @@ msgstr "å–å¾—åˆè¦æ¡†æž¶è³‡æ–™éŒ¯èª¤ï¼Œè«‹é‡æ–°è¼‰å…¥é é¢"
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page or try a different framework"
msgstr "å–å¾—åˆè¦æ¡†æž¶è³‡æ–™éŒ¯èª¤ï¼Œè«‹é‡æ–°è¼‰å…¥é é¢æˆ–嘗試其它的框架"
+msgid "ComplianceFrameworks|Error setting the default compliance frameworks"
+msgstr "é è¨­åˆè¦æ€§æ¡†æž¶è¨­å®šéŒ¯èª¤"
+
msgid "ComplianceFrameworks|Frameworks that have been added will appear here."
msgstr "已添加的框架將出ç¾åœ¨æ­¤è™•ã€‚"
@@ -9894,15 +9986,24 @@ msgstr "å稱是必è¦çš„"
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr "尚未設置åˆè¦æ¡†æž¶"
+msgid "ComplianceFrameworks|Remove default"
+msgstr "移除é è¨­"
+
msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr "è¦æ±‚çš„æ ¼å¼ï¼š%{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}。 %{linkStart}什麼是åˆè¦çš„æµæ°´ç·šé…置?%{linkEnd}"
+msgid "ComplianceFrameworks|Set default"
+msgstr "設定é è¨­"
+
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr "無法ä¿å­˜æ­¤åˆè¦æ€§æ¡†æž¶ï¼Œè«‹é‡è©¦"
msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone."
msgstr "您å³å°‡å¾žæ‰€æœ‰ç•¶å‰æ‡‰ç”¨çš„專案中永久刪除åˆè¦æ¡†æž¶ %{framework} ,這å¯èƒ½æœƒåˆªé™¤å…¶å®ƒåŠŸèƒ½ã€‚æ­¤æ“作無法撤消。"
+msgid "ComplianceFrameworks|default"
+msgstr "é è¨­"
+
msgid "ComplianceFramework|Add a framework to %{linkStart}%{groupName}%{linkEnd} and it will appear here."
msgstr "添加一個框架到 %{linkStart}%{groupName}%{linkEnd} 並將出ç¾åœ¨é€™è£¡ã€‚"
@@ -10068,8 +10169,8 @@ msgstr "為已廢棄的 API 請求é…置特定é™åˆ¶ä»¥å–代一般用戶和 IP
msgid "Configure the %{link} integration."
msgstr "%{link} æ•´åˆè¨­å®šã€‚"
-msgid "Configure the default first day of the week and time tracking units."
-msgstr "設定é è¨­çš„æ¯é€±ç¬¬ä¸€å¤©å’Œæ™‚間追蹤單ä½ã€‚"
+msgid "Configure the default first day of the week, time tracking units, and default language."
+msgstr "設置é è¨­ä¸€å‘¨çš„第一天和時間追踪單ä½ä»¥åŠé è¨­èªžè¨€ã€‚"
msgid "Configure the way a user creates a new account."
msgstr "é…置使用者建立新帳號的方å¼ã€‚"
@@ -10188,6 +10289,9 @@ msgstr "正在連線"
msgid "Connecting to terminal sync service"
msgstr "連接到終端åŒæ­¥æœå‹™"
+msgid "Connecting to the remote environment..."
+msgstr "正在連線到é ç«¯ç’°å¢ƒ..."
+
msgid "Connecting..."
msgstr "正在連線..."
@@ -10227,12 +10331,6 @@ msgstr "容器映åƒåº«æ˜ åƒ"
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
msgstr "æ­¤ GitLab 實體未啟用容器映åƒåº«ã€‚為了讓自動 DevOps 能é‹ä½œï¼Œè«‹è©¢å•ç®¡ç†å“¡å•Ÿç”¨ã€‚"
-msgid "Container repositories"
-msgstr "容器映åƒåº«"
-
-msgid "Container repository"
-msgstr "容器映åƒåº«"
-
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr "請訪å•%{linkStart}管ç†è¨­ç½®%{linkEnd}以啟用此功能。"
@@ -10350,7 +10448,7 @@ msgid "ContainerRegistry|Expiration policy will run in %{time}"
msgstr "ContainerRegistry|逾期策略將在 %{time} å¾®é‹è¡Œ"
msgid "ContainerRegistry|If you are not already logged in, you need to authenticate to the Container Registry by using your GitLab username and password. If you have %{twofaDocLinkStart}Two-Factor Authentication%{twofaDocLinkEnd} enabled, use a %{personalAccessTokensDocLinkStart}Personal Access Token%{personalAccessTokensDocLinkEnd} instead of a password."
-msgstr "如果您尚未登入,您需è¦ä½¿ç”¨æ‚¨çš„GitLab使用者å稱和密碼來進行身份èªè­‰ã€‚如果您啟用 %{twofaDocLinkStart}é›™é‡èº«ä»½é©—è­‰%{twofaDocLinkEnd} ,請使用%{personalAccessTokensDocLinkStart}個人存å–令牌(權æ–)%{personalAccessTokensDocLinkEnd}而ä¸æ˜¯å¯†ç¢¼ã€‚"
+msgstr "如果您尚未登入,您需è¦ä½¿ç”¨æ‚¨çš„GitLab使用者å稱和密碼來進行身份èªè­‰ã€‚如果您啟用 %{twofaDocLinkStart}é›™é‡èº«ä»½é©—è­‰%{twofaDocLinkEnd} ,請使用%{personalAccessTokensDocLinkStart}個人存å–權æ–(令牌)%{personalAccessTokensDocLinkEnd}而ä¸æ˜¯å¯†ç¢¼ã€‚"
msgid "ContainerRegistry|Image repository deletion failed"
msgstr "ContainerRegistry|映åƒæª”版本庫刪除失敗"
@@ -10494,9 +10592,6 @@ msgstr "標籤已æˆåŠŸè¢«æ¨™ç¤ºç‚ºå¾…刪除。"
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr "標籤已æˆåŠŸè¢«æ¨™ç¤ºç‚ºå¾…刪除。"
-msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr "標籤暫時ä¸èƒ½è¢«æ¨™ç¤ºç‚ºåˆªé™¤ã€‚請幾分é˜å¾Œå†è©¦ã€‚%{docLinkStart}更多詳情%{docLinkEnd}。"
-
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr "符åˆé€™äº›è¦å‰‡çš„標籤會被%{strongStart}ä¿ç•™%{strongEnd},å³ä½¿å®ƒå€‘與下é¢çš„刪除è¦å‰‡ç›¸åŒ¹é…。 %{secondStrongStart}最新的%{secondStrongEnd}標籤總是會被ä¿ç•™ã€‚"
@@ -10680,9 +10775,6 @@ msgstr "控制與您帳號關è¯çš„é›»å­éƒµä»¶"
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr "控制 CI_JOB_TOKEN CI/CD 變é‡å¦‚何用於專案之間的 API å­˜å–。"
-msgid "Control how the GitLab Package Registry functions."
-msgstr "控制 GitLab 軟體套件倉庫如何é‹ä½œã€‚"
-
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr "控制是å¦åœ¨ GitLab 中顯示客戶體驗改進內容和第三方優惠。"
@@ -10969,7 +11061,7 @@ msgid "Could not revoke impersonation token %{token_name}."
msgstr "無法復原身份模擬令牌(權æ–) %{token_name}。"
msgid "Could not revoke personal access token %{personal_access_token_name}."
-msgstr "無法復原個人存å–令牌(權æ–) %{personal_access_token_name}。"
+msgstr "無法復原個人存å–權æ–(令牌) %{personal_access_token_name}。"
msgid "Could not save configuration. Please refresh the page, or try again later."
msgstr "無法ä¿å­˜çµ„態,請é‡æ–°è¼‰å…¥æ­¤é ï¼Œæˆ–ç¨å¾Œå†è©¦ã€‚"
@@ -10995,6 +11087,9 @@ msgstr "無法上傳您的設計,一個或數個已上傳的檔案ä¸è¢«æ”¯æ´
msgid "Couldn't assign policy to project or group"
msgstr "無法將策略分é…給專案或群組"
+msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
+msgstr "無法找到事件審核類型: %{missing_filters} éŽæ¿¾å™¨"
+
msgid "Country"
msgstr "國家"
@@ -11065,7 +11160,7 @@ msgid "Create a new repository"
msgstr "建立一個新版本庫"
msgid "Create a personal access token on your account to pull or push via %{protocol}."
-msgstr "在帳號上建立個人存å–令牌(權æ–),並以此 %{protocol} 來 pull 或 push。"
+msgstr "在帳號上建立個人存å–權æ–(令牌),並以此 %{protocol} 來 pull 或 push。"
msgid "Create a project"
msgstr "建立一個專案"
@@ -11077,7 +11172,7 @@ msgid "Create an incident. Incidents are created for each alert triggered."
msgstr "建立一個事件(incident),æ¯å€‹è§¸ç™¼çš„警報都會被建立一個事件(Incidents)。"
msgid "Create and provide your GitHub %{link_start}Personal Access Token%{link_end}. You will need to select the %{code_open}repo%{code_close} scope, so we can display a list of your public and private repositories which are available to import."
-msgstr "請建立並æ供您GitHub%{link_start}的個人訪å•ä»¤ç‰Œ(權æ–)%{link_end},您需è¦é¸æ“‡%{code_open}repo%{code_close}範åœï¼Œé€™æ¨£æˆ‘們æ‰å¯ä»¥å‘您顯示å¯å°Žå…¥çš„公共和ç§æœ‰ç‰ˆæœ¬åº«çš„列表。"
+msgstr "請建立並æ供您GitHub%{link_start}的個人訪å•æ¬Šæ–(令牌)%{link_end},您需è¦é¸æ“‡%{code_open}repo%{code_close}範åœï¼Œé€™æ¨£æˆ‘們æ‰å¯ä»¥å‘您顯示å¯å°Žå…¥çš„公共和ç§æœ‰ç‰ˆæœ¬åº«çš„列表。"
msgid "Create branch"
msgstr "建立分支"
@@ -11107,7 +11202,7 @@ msgid "Create empty repository"
msgstr "建立空的版本庫"
msgid "Create epic"
-msgstr "建立å²è©©"
+msgstr "建立å²è©© (epic)"
msgid "Create file"
msgstr "建立文件"
@@ -11127,9 +11222,6 @@ msgstr "建立議題"
msgid "Create issue to resolve all threads"
msgstr "建立議題(issue)來解決所有主題"
-msgid "Create iteration"
-msgstr "建立迭代"
-
msgid "Create label"
msgstr "建立標記 (label)"
@@ -11176,7 +11268,7 @@ msgid "Create new label"
msgstr "建立新標記 (label)"
msgid "Create new..."
-msgstr "建立新…"
+msgstr "新增…"
msgid "Create one"
msgstr "建立一個"
@@ -11194,7 +11286,7 @@ msgid "Create project label"
msgstr "建立專案標記 (label)"
msgid "Create release"
-msgstr "建立版本發布"
+msgstr "建立發布版本"
msgid "Create requirement"
msgstr "建立需求"
@@ -11208,6 +11300,9 @@ msgstr "創建程å¼ç¢¼ç‰‡æ®µ"
msgid "Create tag %{tagName}"
msgstr "建立標籤%{tagName}"
+msgid "Create testing scenarios by defining project conditions in your development platform."
+msgstr "在您的開發平å°ä¸­é€éŽå®šç¾©å°ˆæ¡ˆæ¢ä»¶ä¾†å»ºç«‹æ¸¬è©¦æƒ…境。"
+
msgid "Create topic"
msgstr "建立主題"
@@ -11244,6 +11339,42 @@ msgstr "您無權建立群組。"
msgid "CreateTag|Tag"
msgstr "標籤"
+msgid "CreateTimelogForm|Add time entry"
+msgstr "加入時間項"
+
+msgid "CreateTimelogForm|An error occurred while saving the time entry."
+msgstr "儲存時間項時發生錯誤。"
+
+msgid "CreateTimelogForm|Cancel"
+msgstr "å–消"
+
+msgid "CreateTimelogForm|Example: 1h 30m"
+msgstr "範例:1h 30m"
+
+msgid "CreateTimelogForm|How do I track and estimate time?"
+msgstr "如何追踪與估算時間?"
+
+msgid "CreateTimelogForm|Save"
+msgstr "儲存"
+
+msgid "CreateTimelogForm|Spent at"
+msgstr "用於"
+
+msgid "CreateTimelogForm|Summary"
+msgstr "摘è¦"
+
+msgid "CreateTimelogForm|Time spent"
+msgstr "花費的時間"
+
+msgid "CreateTimelogForm|Track time spent on this %{issuableTypeNameStart}%{issuableTypeNameEnd}. %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+msgstr "追踪在此 %{issuableTypeNameStart}%{issuableTypeNameEnd} 上花費的時間。 %{timeTrackingDocsLinkStart}%{timeTrackingDocsLinkEnd}"
+
+msgid "CreateTimelogForm|issue"
+msgstr "議題"
+
+msgid "CreateTimelogForm|merge request"
+msgstr "åˆä½µè«‹æ±‚"
+
msgid "CreateValueStreamForm|%{name} (default)"
msgstr "CreateValueStreamForm|%{name}(é è¨­å€¼ï¼‰"
@@ -11437,7 +11568,7 @@ msgid "Creating"
msgstr "建立"
msgid "Creating epic"
-msgstr "建立å²è©©ä¸­"
+msgstr "正在建​立å²è©© (epic)"
msgid "Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available."
msgstr "正在使用Prometheus伺æœå™¨ä¸­çš„資料建立圖表。如果這需è¦å¾ˆé•·æ™‚間,請確ä¿è³‡æ–™å¯ç”¨ã€‚"
@@ -11455,7 +11586,7 @@ msgid "CredentialsInventory|No credentials found"
msgstr "找ä¸åˆ°æ†‘è­‰"
msgid "CredentialsInventory|Personal Access Tokens"
-msgstr "個人存å–令牌(權æ–)"
+msgstr "個人存å–權æ–(令牌)"
msgid "CredentialsInventory|Project and Group Access Tokens"
msgstr "專案與群組存å–權æ–"
@@ -11469,6 +11600,9 @@ msgstr "信用å¡éœ€è¦å­˜æª”æ‰èƒ½å»ºç«‹æµæ°´ç·š"
msgid "Credit card:"
msgstr "信用å¡ï¼š"
+msgid "Critical - S1"
+msgstr "åš´é‡ - S1"
+
msgid "Critical vulnerabilities present"
msgstr "存在嚴é‡æ¼æ´ž"
@@ -11749,7 +11883,7 @@ msgid "CycleAnalyticsStage|Review"
msgstr "評審"
msgid "CycleAnalyticsStage|Staging"
-msgstr "é ç™¼ä½ˆ"
+msgstr "é ç™¼å¸ƒ"
msgid "CycleAnalyticsStage|Test"
msgstr "測試"
@@ -11878,12 +12012,18 @@ msgstr "%{startDate} - %{endDate}"
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr "å¹³å‡ï¼ˆæœ€è¿‘%{days}天)"
+msgid "DORA4Metrics|Change Failure Rate"
+msgstr "變更失敗率"
+
msgid "DORA4Metrics|Change failure rate"
msgstr "變更失敗率"
msgid "DORA4Metrics|Change failure rate (percentage)"
msgstr "變更失敗率(%%)"
+msgid "DORA4Metrics|Cycle time"
+msgstr "週期時間"
+
msgid "DORA4Metrics|DORA metrics for %{groupName} group"
msgstr "%{groupName} 群組的 DORA 指標"
@@ -11896,9 +12036,21 @@ msgstr "事故開啟的天數"
msgid "DORA4Metrics|Days from merge to deploy"
msgstr "從åˆä½µåˆ°éƒ¨ç½²çš„天數"
+msgid "DORA4Metrics|Deployment Frequency"
+msgstr "部署頻率"
+
msgid "DORA4Metrics|Deployment frequency"
msgstr "部署頻率"
+msgid "DORA4Metrics|Deploys"
+msgstr "部署"
+
+msgid "DORA4Metrics|Lead Time for Changes"
+msgstr "變更的å‰ç½®æ™‚é–“"
+
+msgid "DORA4Metrics|Lead time"
+msgstr "交付時間"
+
msgid "DORA4Metrics|Lead time for changes"
msgstr "變更的å‰ç½®æ™‚é–“"
@@ -11917,6 +12069,9 @@ msgstr "在給定時間內,於正å¼ç’°å¢ƒä¸­æ‰€é–‹å•Ÿçš„事故中ä½æ•¸æ™‚é–“
msgid "DORA4Metrics|Month to date"
msgstr "本月至今"
+msgid "DORA4Metrics|New issues"
+msgstr "新議題"
+
msgid "DORA4Metrics|No incidents during this period"
msgstr "該期間無事故"
@@ -11950,6 +12105,9 @@ msgstr "該圖表根據 %{linkStart}deployment_tier%{linkEnd} 值顯示部署到
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 "該圖表根據 %{linkStart}deployment_tier%{linkEnd} 值顯示åˆä½µè«‹æ±‚與部署到生產環境之間的中間時間。"
+msgid "DORA4Metrics|Time to Restore Service"
+msgstr "æœå‹™æ¢å¾©æ™‚é–“"
+
msgid "DORA4Metrics|Time to restore service"
msgstr "æœå‹™æ¢å¾©æ™‚é–“"
@@ -12010,6 +12168,9 @@ msgstr "未找到該專案之å‰çš„掃æ"
msgid "DastConfig|Not enabled"
msgstr "未啟用"
+msgid "DastProfiles|/graphql"
+msgstr "/graphql"
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr "被動掃æ監控發é€åˆ°ç›®æ¨™çš„所有 HTTP 消æ¯ï¼ˆè«‹æ±‚和回應),主動掃æ則攻擊目標以發ç¾æ½›åœ¨æ¼æ´žã€‚"
@@ -12157,6 +12318,9 @@ msgstr "最å°å€¼ = 1 秒,最大值 = 3600 秒"
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr "監控發é€åˆ°ç›®æ¨™çš„所有 HTTP 請求以發ç¾æ½›åœ¨æ¼æ´žã€‚"
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr "å¿…é ˆå…許自çœæŸ¥è©¢è«‹æ±‚ API 架構,%{linkStart}如何啟用自çœ%{linkEnd}?"
+
msgid "DastProfiles|New scanner profile"
msgstr "新掃æ器設定檔"
@@ -12527,6 +12691,9 @@ msgstr "åœç”¨å‰çš„未活èºå¤©æ•¸"
msgid "Days to merge"
msgstr "åˆä½µæ‰€éœ€å¤©æ•¸"
+msgid "Deactivate"
+msgstr "åœç”¨"
+
msgid "Deactivate dormant users after a period of inactivity"
msgstr "在一段時間未活動後åœç”¨ä¼‘眠使用者"
@@ -12584,6 +12751,12 @@ msgstr "æ¯é€±çš„é è¨­èµ·å§‹æ—¥"
msgid "Default first day of the week in calendars and date pickers."
msgstr "在日期é¸æ“‡å™¨ä¸­é¡¯ç¤ºæ¯é€±é è¨­çš„起始日。"
+msgid "Default language"
+msgstr "é è¨­èªžè¨€"
+
+msgid "Default language for users who are not logged in."
+msgstr "未登入使用者的é è¨­èªžè¨€ã€‚"
+
msgid "Default projects limit"
msgstr "é è¨­å°ˆæ¡ˆé™åˆ¶"
@@ -12720,7 +12893,7 @@ msgid "Delete deploy key"
msgstr "刪除部署金鑰"
msgid "Delete epic"
-msgstr "刪除å²è©©"
+msgstr "刪除å²è©© (epic) "
msgid "Delete file"
msgstr "刪除檔案"
@@ -12753,10 +12926,10 @@ msgid "Delete project"
msgstr "刪除專案"
msgid "Delete release"
-msgstr "刪除版本發佈"
+msgstr "刪除發布版本"
msgid "Delete release %{release}?"
-msgstr "刪除 %{release} 版本發佈?"
+msgstr "刪除 %{release} 發布版本?"
msgid "Delete row"
msgstr "刪除行"
@@ -12786,7 +12959,7 @@ msgid "Delete this attachment"
msgstr "刪除該附件"
msgid "Delete this epic and all descendants?"
-msgstr "刪除此å²è©©å’Œæ‰€æœ‰ä¸‹ç´šï¼Ÿ"
+msgstr "刪除此å²è©© (epic) 和所有下級?"
msgid "Delete this project"
msgstr "刪除此專案"
@@ -12819,13 +12992,13 @@ msgid "DeleteProject|Failed to remove wiki repository. Please try again or conta
msgstr "刪除wiki版本庫失敗。請é‡è©¦æˆ–è¯çµ¡ç®¡ç†å“¡ã€‚"
msgid "DeleteRelease|Are you sure you want to delete this release?"
-msgstr "您確定è¦åˆªé™¤æ­¤ç‰ˆæœ¬ç™¼ä½ˆå—Žï¼Ÿ"
+msgstr "您確定è¦åˆªé™¤æ­¤ç™¼å¸ƒç‰ˆæœ¬å—Žï¼Ÿ"
msgid "DeleteRelease|For more details, see %{docsPathStart}Deleting a release%{docsPathEnd}."
-msgstr "有關更多詳細信æ¯ï¼Œè«‹åƒé–± %{docsPathStart}刪除版本發佈%{docsPathEnd}。"
+msgstr "有關更多詳細信æ¯ï¼Œè«‹åƒé–± %{docsPathStart}刪除發布版本%{docsPathEnd}。"
msgid "DeleteRelease|You are about to delete release %{release} and its assets. The Git tag %{tag} will not be deleted."
-msgstr "您å³å°‡åˆªé™¤ %{release} 版本發佈åŠå…¶è³‡ç”¢ã€‚ Git 標籤 %{tag} ä¸æœƒè¢«åˆªé™¤ã€‚"
+msgstr "您å³å°‡åˆªé™¤ %{release} 發布版本åŠå…¶è³‡ç”¢ã€‚ Git 標籤 %{tag} ä¸æœƒè¢«åˆªé™¤ã€‚"
msgid "DeleteValueStream|'%{name}' Value Stream deleted"
msgstr "「%{name}〠價值æµå·²åˆªé™¤"
@@ -13370,6 +13543,9 @@ msgstr "已拒絕於 %{time} "
msgid "DeploymentApproval|Rejected by you %{time}"
msgstr "被您拒絕於 %{time}"
+msgid "DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)"
+msgstr "邊緣計算 (e.g. Cloudflare Workers)"
+
msgid "DeploymentTarget|GitLab Pages"
msgstr "GitLab Pages"
@@ -13406,6 +13582,9 @@ msgstr "ç„¡æœå‹™å™¨å¾Œç«¯ï¼ˆLambdaã€é›²å‡½æ•¸ï¼‰"
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr "虛擬主機(例如 EC2)"
+msgid "DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)"
+msgstr "Web éƒ¨ç½²å¹³å° (Netlifyã€Vercelã€Gatsby)"
+
msgid "Deployments"
msgstr "部署"
@@ -13462,7 +13641,7 @@ msgid "Deployment|created"
msgstr "已建立"
msgid "Deployment|failed"
-msgstr "已失敗"
+msgstr "失敗"
msgid "Deployment|running"
msgstr "執行中"
@@ -13509,9 +13688,15 @@ msgstr "使用 %{link_start}GitLab Flavored Markdown%{link_end} 解æžçš„æè¿°ã
msgid "Description:"
msgstr "æè¿°:"
+msgid "Descriptions"
+msgstr "æè¿°"
+
msgid "Descriptive label"
msgstr "æ述性標記"
+msgid "Design"
+msgstr "設計"
+
msgid "Design Management files and data"
msgstr "設計管ç†æ–‡ä»¶å’Œè³‡æ–™"
@@ -13640,7 +13825,7 @@ msgid "DesignManagement|Upload designs"
msgstr "上傳設計"
msgid "DesignManagement|Upload skipped. %{reason}"
-msgstr "已跳éŽä¸Šå‚³ã€‚%{reason}"
+msgstr "已略éŽä¸Šå‚³ã€‚%{reason}"
msgid "DesignManagement|Your designs are being copied and are on their way… Please refresh to update."
msgstr "您的設計正在複製éŽç¨‹ä¸­â€¦ 請刷新以ç²å¾—更新。"
@@ -13675,6 +13860,9 @@ msgstr "DevOps 報告"
msgid "DevOps adoption"
msgstr "DevOps adoption"
+msgid "DevOps metrics comparison (Alpha)"
+msgstr "DevOps 指標比較 (Alpha)"
+
msgid "Developer"
msgstr "開發者"
@@ -14024,7 +14212,7 @@ msgid "Discover"
msgstr "Discover"
msgid "Discover GitLab Geo"
-msgstr "探索GitLab Geo"
+msgstr "探索 GitLab Geo"
msgid "Discover projects, groups and snippets. Share your projects with others"
msgstr "ç€è¦½å°ˆæ¡ˆï¼Œç¾¤çµ„和程å¼ç¢¼ç‰‡æ®µã€‚與他人分享您的專案"
@@ -14424,7 +14612,7 @@ msgid "Edit Pipeline Schedule"
msgstr "編輯æµæ°´ç·šæŽ’程"
msgid "Edit Release"
-msgstr "編輯發佈"
+msgstr "編輯發布"
msgid "Edit Requirement"
msgstr "編輯需求"
@@ -14463,7 +14651,7 @@ msgid "Edit environment"
msgstr "編輯環境"
msgid "Edit epics"
-msgstr "編輯å²è©©"
+msgstr "編輯å²è©© (epic) "
msgid "Edit files in the editor and commit changes here"
msgstr "在編輯器中編輯文件,並在這裡​​æ交變更內容"
@@ -14514,7 +14702,7 @@ msgid "Edit this file only."
msgstr "僅編輯此檔案。"
msgid "Edit this release"
-msgstr "編輯此發佈"
+msgstr "編輯此發布"
msgid "Edit title and description"
msgstr "編輯標題和æè¿°"
@@ -14984,8 +15172,11 @@ msgstr "輸入任何é¡è‰²ã€‚"
msgid "Enter at least three characters to search"
msgstr "請至少輸入三個字元æ‰å¯æœå°‹"
+msgid "Enter at least three characters to search."
+msgstr "至少輸入三個字元進行æœç´¢ã€‚"
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
-msgstr "輸入您的Bitbucket伺æœå™¨URL和個人存å–令牌(權æ–)"
+msgstr "輸入您的Bitbucket伺æœå™¨URL和個人存å–權æ–(令牌)"
msgid "Enter in your Phabricator Server URL and personal access token below"
msgstr "在下é¢è¼¸å…¥æ‚¨çš„ Phabricator Server URL 和個人存å–令牌(權æ–)"
@@ -15216,7 +15407,7 @@ msgid "Environments|Open"
msgstr "é–‹å•Ÿ"
msgid "Environments|Open live environment"
-msgstr "打開執行中的環境"
+msgstr "開啟執行中的環境"
msgid "Environments|Re-deploy environment"
msgstr "é‡æ–°éƒ¨ç½²ç’°å¢ƒ"
@@ -15285,37 +15476,37 @@ msgid "Environment|Deployment tier"
msgstr "部署層級"
msgid "Epic"
-msgstr "å²è©©"
+msgstr "å²è©© (Epic)"
msgid "Epic Boards"
-msgstr "å²è©©çœ‹æ¿"
+msgstr "å²è©© (Epic) 看æ¿"
msgid "Epic actions"
-msgstr "å²è©©å‹•ä½œ"
+msgstr "å²è©© (Epic) 動作"
msgid "Epic cannot be found."
-msgstr "找ä¸åˆ°å²è©©ã€‚"
+msgstr "找ä¸åˆ°å²è©© (epic) 。"
msgid "Epic details"
-msgstr "å²è©©è©³æƒ…"
+msgstr "å²è©© (Epic) 詳情"
msgid "Epic events"
-msgstr "å²è©©äº‹ä»¶"
+msgstr "å²è©© (Epic) 事件"
msgid "Epic not found for given params"
-msgstr "未找到給定åƒæ•¸çš„å²è©©"
+msgstr "未找到給定åƒæ•¸çš„å²è©© (epic) "
msgid "Epics"
-msgstr "å²è©©"
+msgstr "å²è©© (Epics)"
msgid "Epics Roadmap"
-msgstr "å²è©©è·¯ç·šåœ–"
+msgstr "å²è©© (Epics) 路線圖"
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
-msgstr "有效利用å²è©©ï¼Œæ‚¨çš„產å“線管ç†æœƒè®Šå¾—更輕鬆高效"
+msgstr "有效利用å²è©© (epics) ,您的產å“線管ç†æœƒè®Šå¾—更輕鬆高效"
msgid "Epics, issues, and merge requests"
-msgstr "å²è©©ã€è­°é¡Œå’Œåˆä½µè«‹æ±‚"
+msgstr "å²è©© (Epics)ã€è­°é¡Œå’Œåˆä½µè«‹æ±‚"
msgid "Epics|%{startDate} – %{dueDate}"
msgstr "%{startDate} – %{dueDate}"
@@ -15324,10 +15515,10 @@ msgid "Epics|%{startDate} – No due date"
msgstr "%{startDate} – 無截止日期"
msgid "Epics|Add a new epic"
-msgstr "加入一個新的å²è©©ã€‚"
+msgstr "加入一個新的å²è©© (epic) 。"
msgid "Epics|Add an existing epic"
-msgstr "加入一個ç¾æœ‰çš„å²è©©ã€‚"
+msgstr "加入一個ç¾æœ‰çš„å²è©© (epic) 。"
msgid "Epics|Are you sure you want to remove %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr "您確定è¦å¾ž %{bStart}%{parentEpicTitle}%{bEnd} 移除 %{bStart}%{targetEpicTitle}%{bEnd} 嗎?"
@@ -15342,25 +15533,22 @@ msgid "Epics|No start date – %{dueDate}"
msgstr "無開始日期 — %{dueDate}"
msgid "Epics|Remove epic"
-msgstr "刪除å²è©©"
+msgstr "刪除å²è©© (epic) "
msgid "Epics|Remove issue"
msgstr "刪除議題"
-msgid "Epics|Show more"
-msgstr "顯示更多"
-
msgid "Epics|Something went wrong while creating child epics."
-msgstr "建立å­å²è©©æ™‚發生錯誤。"
+msgstr "建立å­å²è©© (epics) 時發生錯誤。"
msgid "Epics|Something went wrong while creating issue."
msgstr "建立議題時發生錯誤。"
msgid "Epics|Something went wrong while fetching child epics."
-msgstr "å–å¾—å­å²è©©æ™‚發生錯誤。"
+msgstr "å–å¾—å­å²è©© (epics) 時發生錯誤。"
msgid "Epics|Something went wrong while fetching epics list."
-msgstr "å–å¾—å²è©©åˆ—表時發生錯誤。"
+msgstr "å–å¾—å²è©© (epics) 列表時發生錯誤。"
msgid "Epics|Something went wrong while moving item."
msgstr "移動專案時出了錯。"
@@ -15369,19 +15557,19 @@ msgid "Epics|Something went wrong while ordering item."
msgstr "排åºæ™‚發生錯誤。"
msgid "Epics|Something went wrong while updating epics."
-msgstr "æ›´æ–°å²è©©æ™‚發生錯誤。"
+msgstr "æ›´æ–°å²è©© (epics) 時發生錯誤。"
msgid "Epics|The color for the epic when it's visualized, such as on roadmap timeline bars."
-msgstr "å²è©©åœ¨å¯è¦–覺化時的é¡è‰²ï¼Œä¾‹å¦‚在路線圖的時間軸線上。"
+msgstr "å²è©© (Epic) 在å¯è¦–覺化時的é¡è‰²ï¼Œä¾‹å¦‚在路線圖的時間軸線上。"
msgid "Epics|This epic and any containing child epics are confidential and should only be visible to team members with at least Reporter access."
-msgstr "這個å²è©©å’Œä»»ä½•åŒ…å«å­å²è©©çš„訊æ¯å‡ç‚ºç§å¯†ï¼Œåªèƒ½å°æ“有至少報告者訪å•æ¬Šé™çš„團隊æˆå“¡å¯è¦‹ã€‚"
+msgstr "這個å²è©© (epics) 和任何包å«å­å²è©© (epic) 的訊æ¯å‡ç‚ºç§å¯†ï¼Œåªèƒ½å°æ“有至少報告者訪å•æ¬Šé™çš„團隊æˆå“¡å¯è¦‹ã€‚"
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
-msgstr "該æ“作也會從%{bStart}%{parentEpicTitle}%{bEnd}中移除%{bStart}%{targetEpicTitle}%{bEnd}的所有級別å­å²è©©ã€‚確定繼續嗎?"
+msgstr "該æ“作也會從%{bStart}%{parentEpicTitle}%{bEnd}中移除%{bStart}%{targetEpicTitle}%{bEnd}的所有級別å­å²è©© (epic)。確定繼續嗎?"
msgid "Epics|Unable to save epic. Please try again"
-msgstr "無法ä¿å­˜å²è©©ã€‚è«‹é‡è©¦"
+msgstr "無法ä¿å­˜å²è©© (epic) ,請é‡è©¦"
msgid "Erased"
msgstr "已刪除"
@@ -15396,7 +15584,7 @@ msgid "Error Tracking"
msgstr "錯誤追蹤"
msgid "Error creating epic"
-msgstr "建立 å²è©© 時發生錯誤"
+msgstr "建立å²è©© (Epic) 時發生錯誤"
msgid "Error creating label."
msgstr "建立標記時發生錯誤。"
@@ -15443,6 +15631,9 @@ msgstr "å–得有效資料時發生錯誤。"
msgid "Error fetching refs"
msgstr "å–å¾—refs時發生錯誤。"
+msgid "Error fetching target projects. Please try again."
+msgstr "讀å–目標專案時發生錯誤,請å†è©¦ä¸€æ¬¡ã€‚"
+
msgid "Error fetching the dependency list. Please check your network connection and try again."
msgstr "å–å¾—ä¾è³´åˆ—表時發生錯誤。請檢查您的網路連接,然後é‡è©¦ã€‚"
@@ -15621,10 +15812,10 @@ msgid "ErrorTracking|If you self-host Sentry, enter your Sentry instance's full
msgstr "å¦‚æžœæ‚¨è‡ªç®¡ç† Sentry,請輸入您的 Sentry 實例完整網å€ã€‚如果您使用的是 Sentry 的託管解決方案,請輸入 https://sentry.io"
msgid "ErrorTracking|Integrated error tracking is %{epicLinkStart}turned off by default%{epicLinkEnd} and no longer active for this project. To re-enable error tracking on self-hosted instances, you can either %{flagLinkStart}turn on the feature flag%{flagLinkEnd} for integrated error tracking, or provide a %{settingsLinkStart}Sentry API URL and Auth Token%{settingsLinkEnd} on your project settings page. However, error tracking is not ready for production use and cannot be enabled on GitLab.com."
-msgstr "æ•´åˆéŒ¯èª¤è¿½è¹¤ %{epicLinkStart}é è¨­é—œé–‰%{epicLinkEnd}並且ä¸å†å°æ­¤å°ˆæ¡ˆç”Ÿæ•ˆã€‚為了é‡æ–°å•Ÿç”¨ç§æœ‰åŒ–部署實例的錯誤跟蹤,您å¯ä»¥%{flagLinkStart}打開功能標誌%{flagLinkEnd} 用於整åˆéŒ¯èª¤è¿½è¹¤ï¼Œ 或者在您的專案設置é é¢ä¸Šæ供一個 %{settingsLinkStart}Sentry API URL和身份驗證令牌%{settingsLinkEnd} 。然而,錯誤追蹤尚未準備就緒在使用者正å¼ç’°å¢ƒï¼Œç„¡æ³•åœ¨ GitLab.com 上啟用。"
+msgstr "æ•´åˆéŒ¯èª¤è¿½è¹¤ %{epicLinkStart}é è¨­é—œé–‰%{epicLinkEnd}並且ä¸å†å°æ­¤å°ˆæ¡ˆç”Ÿæ•ˆã€‚為了é‡æ–°å•Ÿç”¨ç§æœ‰åŒ–部署實例的錯誤跟蹤,您å¯ä»¥%{flagLinkStart}打開特性標籤%{flagLinkEnd} 用於整åˆéŒ¯èª¤è¿½è¹¤ï¼Œ 或者在您的專案設置é é¢ä¸Šæ供一個 %{settingsLinkStart}Sentry API URL和身份驗證令牌%{settingsLinkEnd} 。然而,錯誤追蹤尚未準備就緒在使用者正å¼ç’°å¢ƒï¼Œç„¡æ³•åœ¨ GitLab.com 上啟用。"
msgid "ErrorTracking|Integrated error tracking is %{epicLinkStart}turned off by default%{epicLinkEnd} and no longer active for this project. To re-enable error tracking on self-hosted instances, you can either %{flagLinkStart}turn on the feature flag%{flagLinkEnd} for integrated error tracking, or provide a Sentry API URL and Auth Token below. However, error tracking is not ready for production use and cannot be enabled on GitLab.com."
-msgstr "æ•´åˆéŒ¯èª¤è¿½è¹¤ %{epicLinkStart}é è¨­é—œé–‰%{epicLinkEnd} 並且ä¸å†å°æ­¤å°ˆæ¡ˆç”Ÿæ•ˆã€‚為了é‡æ–°å•Ÿç”¨ç§æœ‰åŒ–部署實例的錯誤追蹤,您å¯ä»¥%{flagLinkStart}打開功能標誌%{flagLinkEnd} 用於集æˆéŒ¯èª¤è·Ÿè¹¤ï¼Œ 或者在下é¢æ供一個 Sentry API URL和身份驗證令牌 。然而,錯誤跟蹤尚未準備就緒在使用者正å¼ç’°å¢ƒï¼Œç„¡æ³•åœ¨ GitLab.com 上啟用。"
+msgstr "æ•´åˆéŒ¯èª¤è¿½è¹¤ %{epicLinkStart}é è¨­é—œé–‰%{epicLinkEnd} 並且ä¸å†å°æ­¤å°ˆæ¡ˆç”Ÿæ•ˆã€‚為了é‡æ–°å•Ÿç”¨ç§æœ‰åŒ–部署實例的錯誤追蹤,您å¯ä»¥%{flagLinkStart}打開特性標籤%{flagLinkEnd} 用於集æˆéŒ¯èª¤è·Ÿè¹¤ï¼Œ 或者在下é¢æ供一個 Sentry API URL和身份驗證令牌 。然而,錯誤跟蹤尚未準備就緒在使用者正å¼ç’°å¢ƒï¼Œç„¡æ³•åœ¨ GitLab.com 上啟用。"
msgid "ErrorTracking|No projects available"
msgstr "ç„¡å¯ç”¨çš„é …ç›®"
@@ -15758,6 +15949,15 @@ msgstr "該政策沒有å‡ç´šè¦å‰‡ã€‚"
msgid "EscalationPolicies|mins"
msgstr "最å°å€¼"
+msgid "EscalationStatus|Acknowledged"
+msgstr "已確èª"
+
+msgid "EscalationStatus|Resolved"
+msgstr "已解決"
+
+msgid "EscalationStatus|Triggered"
+msgstr "已觸發"
+
msgid "Estimate"
msgstr "é ä¼°"
@@ -15777,7 +15977,7 @@ msgid "EventFilterBy|Filter by designs"
msgstr "ä¾è¨­è¨ˆç¯©é¸"
msgid "EventFilterBy|Filter by epic events"
-msgstr "ä¾å²è©©äº‹ä»¶ç¯©é¸"
+msgstr "ä¾å²è©© (epic) 事件篩é¸"
msgid "EventFilterBy|Filter by issue events"
msgstr "åªé¡¯ç¤ºè­°é¡Œäº‹ä»¶"
@@ -15918,6 +16118,9 @@ msgstr "人人皆å¯è²¢ç»"
msgid "Everything on your to-do list is marked as done."
msgstr "您的待辦事項列表中的所有內容都標記為已完æˆã€‚"
+msgid "Everything you need to create a GitLab Pages site using Bridgetown"
+msgstr "使用 Bridgetown 來建立 GitLab Pages 網站所需的一切"
+
msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr "使用Gatsby建立GitLab Pages網站所需的所有訊æ¯"
@@ -15948,9 +16151,21 @@ msgstr "憑證集"
msgid "Exactly one of %{attributes} is required"
msgstr "其中的一個%{attributes}是必需的"
+msgid "Example: (feature|hotfix)\\/*"
+msgstr "範例: (feature|hotfix)\\/*"
+
+msgid "Example: (jar|exe)$"
+msgstr "範例: (jar|exe)$"
+
msgid "Example: @sub\\.company\\.com$"
msgstr "範例: @sub\\.company\\.com$"
+msgid "Example: Fixes \\d+\\..*"
+msgstr "範例: Fixes \\d+\\..*"
+
+msgid "Example: ssh\\:\\/\\/"
+msgstr "範例: ssh\\:\\/\\/"
+
msgid "Examples"
msgstr "範例"
@@ -15969,9 +16184,6 @@ msgstr "ä¸åŒ…括åˆä½µæ交。僅é™6,000次æ交。"
msgid "Execution time"
msgstr "執行時間"
-msgid "Executive Dashboard"
-msgstr "執行儀表æ¿"
-
msgid "Existing branch name, tag, or commit SHA"
msgstr "ç¾æœ‰åˆ†æ”¯å稱ã€æ¨™ç±¤æˆ–æ交 SHA"
@@ -16026,14 +16238,17 @@ msgstr "展開設定部份"
msgid "Expand sidebar"
msgstr "展開å´é‚Šæ¬„"
+msgid "Expand variable reference"
+msgstr "擴展變數引用"
+
msgid "Expected documents: %{expected_documents}"
msgstr "需è¦çš„文件: %{expected_documents}"
-msgid "Experiment Candidates"
-msgstr "實驗候é¸äºº"
+msgid "Experiment"
+msgstr "實驗"
-msgid "ExperimentSubject|Must have exactly one of User, Namespace, or Project."
-msgstr "必須有一個使用者ã€å‘½å空間或專案。"
+msgid "Experiment candidates"
+msgstr "實驗å°è±¡"
msgid "Experiments"
msgstr "實驗"
@@ -16075,10 +16290,10 @@ msgid "Explain the problem. If appropriate, provide a link to the relevant issue
msgstr "請解釋此å•é¡Œã€‚如é©ç”¨ï¼Œå¯æ供相關議題或留言的連çµã€‚"
msgid "Explore"
-msgstr "探索"
+msgstr "ç€è¦½"
msgid "Explore GitLab"
-msgstr "ç€è¦½GitLab"
+msgstr "ç€è¦½ GitLab"
msgid "Explore Groups"
msgstr "ç€è¦½ç¾¤çµ„"
@@ -16246,7 +16461,7 @@ msgid "Facebook"
msgstr "Facebook"
msgid "Failed"
-msgstr "已失敗"
+msgstr "失敗"
msgid "Failed Jobs"
msgstr "失敗的作業"
@@ -16346,9 +16561,6 @@ msgstr "生æˆå ±å‘Šå¤±æ•—,請ç¨å¾Œå†è©¦"
msgid "Failed to get ref."
msgstr "å–å¾—ref失敗。"
-msgid "Failed to install."
-msgstr "安è£å¤±æ•—。"
-
msgid "Failed to load"
msgstr "載入失敗"
@@ -16505,9 +16717,6 @@ msgstr "無法更新議題狀態"
msgid "Failed to update the Canary Ingress."
msgstr "無法更新 Canary Ingress。"
-msgid "Failed to upgrade."
-msgstr "å‡ç´šå¤±æ•—。"
-
msgid "Failed to upload object map file"
msgstr "上傳物件映射文件失敗"
@@ -16533,35 +16742,35 @@ msgid "Favicon will be removed. Are you sure?"
msgstr "圖標將被移除。您確定嗎?"
msgid "Feature Flags"
-msgstr "功能標誌"
+msgstr "特性標籤"
msgid "Feature deprecation"
msgstr "功能æè¿°"
msgid "Feature flag status"
-msgstr "功能標籤狀態"
+msgstr "特性標籤狀態"
msgid "Feature flag was not removed."
-msgstr "功能標籤未被移除。"
+msgstr "特性標籤未被移除。"
msgid "Feature flag was successfully removed."
-msgstr "功能標籤已æˆåŠŸç§»é™¤ã€‚"
+msgstr "特性標籤已æˆåŠŸç§»é™¤ã€‚"
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
-msgstr[0] "%d個使用者"
+msgstr[0] "%d 個使用者"
msgid "FeatureFlags|%{percent} by available ID"
-msgstr "ä¾å¯ç”¨ID%{percent}"
+msgstr "ä¾å¯ç”¨ ID %{percent}"
msgid "FeatureFlags|%{percent} by session ID"
-msgstr "ä¾æœƒè©±ID%{percent}"
+msgstr "ä¾æœƒè©± ID %{percent}"
msgid "FeatureFlags|%{percent} by user ID"
-msgstr "ä¾ä½¿ç”¨è€…ID%{percent}"
+msgstr "ä¾ä½¿ç”¨è€… ID %{percent}"
msgid "FeatureFlags|%{percent} randomly"
-msgstr "隨機%{percent}"
+msgstr "隨機 %{percent}"
msgid "FeatureFlags|* (All Environments)"
msgstr "*(全部環境)"
@@ -16588,64 +16797,64 @@ msgid "FeatureFlags|Configure"
msgstr "é…ç½®"
msgid "FeatureFlags|Configure feature flags"
-msgstr "é…置功能標誌"
+msgstr "設定特性標籤"
msgid "FeatureFlags|Consider using the more flexible \"Percent rollout\" strategy instead."
msgstr "請考慮使用更éˆæ´»çš„\"Percent rollout\"策略。"
msgid "FeatureFlags|Create feature flag"
-msgstr "建立功能標誌"
+msgstr "建立特性標籤"
msgid "FeatureFlags|Delete %{name}?"
msgstr "刪除 %{name}?"
msgid "FeatureFlags|Delete feature flag"
-msgstr "刪除功能標誌"
+msgstr "刪除特性標籤"
msgid "FeatureFlags|Description"
msgstr "æè¿°"
msgid "FeatureFlags|Edit Feature Flag"
-msgstr "編輯功能標誌"
+msgstr "編輯特性標籤"
msgid "FeatureFlags|Edit User List"
msgstr "編輯使用者列表"
msgid "FeatureFlags|Enable features for specific users and environments by configuring feature flag strategies."
-msgstr "通éŽè¨­ç½®åŠŸèƒ½æ¨™èªŒç­–略,為特定使用者和環境啟用功能。"
+msgstr "通éŽè¨­å®šç‰¹æ€§æ¨™ç±¤ç­–略,為特定使用者和環境啟用功能。"
msgid "FeatureFlags|Environment Specs"
msgstr "環境è¦æ ¼"
msgid "FeatureFlags|Feature Flag"
-msgstr "功能標誌"
+msgstr "特性標籤"
msgid "FeatureFlags|Feature Flag User List Details"
-msgstr "功能標誌使用者列表詳細訊æ¯"
+msgstr "特性標籤使用者列表詳細訊æ¯"
msgid "FeatureFlags|Feature Flag User Lists"
-msgstr "功能標誌使用者列表"
+msgstr "特性標籤使用者列表"
msgid "FeatureFlags|Feature Flag behavior is built up by creating a set of rules to define the status of target environments. A default wildcard rule %{codeStart}*%{codeEnd} for %{boldStart}All Environments%{boldEnd} is set, and you are able to add as many rules as you need by choosing environment specs below. You can toggle the behavior for each of your rules to set them %{boldStart}Active%{boldEnd} or %{boldStart}Inactive%{boldEnd}."
-msgstr "功能標誌是é€éŽå»ºç«‹ä¸€çµ„è¦å‰‡ä¾†å®šç¾©ç›®æ¨™ç’°å¢ƒç‹€æ…‹çš„構建行為。é è¨­çš„è¦å‰‡%{codeStart} *%{codeEnd}用於%{boldStart}所有環境%{boldEnd},您å¯ä»¥é€éŽä¸‹é¢çš„環境è¦ç¯„加入任æ„數é‡çš„è¦å‰‡ã€‚您å¯ä»¥åˆ‡æ›æ¯å€‹è¦å‰‡çš„行為以設定它們%{boldStart}啟用%{boldEnd}或%{boldStart}åœç”¨%{boldEnd}。"
+msgstr "特性標籤是é€éŽå»ºç«‹ä¸€çµ„è¦å‰‡ä¾†å®šç¾©ç›®æ¨™ç’°å¢ƒç‹€æ…‹çš„構建行為。é è¨­çš„è¦å‰‡%{codeStart} *%{codeEnd}用於%{boldStart}所有環境%{boldEnd},您å¯ä»¥é€éŽä¸‹é¢çš„環境è¦ç¯„加入任æ„數é‡çš„è¦å‰‡ã€‚您å¯ä»¥åˆ‡æ›æ¯å€‹è¦å‰‡çš„行為以設定它們%{boldStart}啟用%{boldEnd}或%{boldStart}åœç”¨%{boldEnd}。"
msgid "FeatureFlags|Feature Flag has no strategies"
-msgstr "功能標誌無策略"
+msgstr "無策略特性標籤"
msgid "FeatureFlags|Feature Flags"
-msgstr "功能標誌"
+msgstr "特性標籤"
msgid "FeatureFlags|Feature flag %{name} will be removed. Are you sure?"
-msgstr "功能標誌 %{name} 將被刪除。您確定嗎?"
+msgstr "%{name} 特性標籤將被刪除,您確定嗎?"
msgid "FeatureFlags|Feature flags allow you to configure your code into different flavors by dynamically toggling certain functionality."
-msgstr "功能標誌å…許é€éŽå‹•æ…‹åˆ‡æ›æŸäº›åŠŸèƒ½ä½¿æ‡‰ç”¨å‘ˆç¾ä¸åŒç‰¹æ€§ã€‚"
+msgstr "特性標籤å…許é€éŽå‹•æ…‹åˆ‡æ›æŸäº›åŠŸèƒ½ä½¿æ‡‰ç”¨å‘ˆç¾ä¸åŒç‰¹æ€§ã€‚"
msgid "FeatureFlags|Feature flags limit reached (%{featureFlagsLimit}). Delete one or more feature flags before adding new ones."
-msgstr "功能標誌已é”到上é™(%{featureFlagsLimit})。在加入新功能標誌之å‰åˆªé™¤ä¸€å€‹æˆ–多個ç¾æœ‰çš„功能標誌。"
+msgstr "特性標籤已é”到上é™(%{featureFlagsLimit})。在加入新特性標籤之å‰åˆªé™¤ä¸€å€‹æˆ–多個ç¾æœ‰çš„功能標誌。"
msgid "FeatureFlags|Get started with feature flags"
-msgstr "功能標誌入門"
+msgstr "特性標籤入門"
msgid "FeatureFlags|ID"
msgstr "ID"
@@ -16666,7 +16875,7 @@ msgid "FeatureFlags|List details"
msgstr "列表詳細訊æ¯"
msgid "FeatureFlags|Loading feature flags"
-msgstr "載入功能標誌"
+msgstr "載入特性標籤"
msgid "FeatureFlags|More information"
msgstr "更多訊æ¯"
@@ -16678,13 +16887,13 @@ msgid "FeatureFlags|New"
msgstr "新建"
msgid "FeatureFlags|New Feature Flag"
-msgstr "新建功能標誌"
+msgstr "新建特性標籤"
msgid "FeatureFlags|New User List"
msgstr "新建使用者列表"
msgid "FeatureFlags|New feature flag"
-msgstr "新建功能標誌"
+msgstr "新建特性標籤"
msgid "FeatureFlags|No user list selected"
msgstr "未é¸æ“‡ä½¿ç”¨è€…列表"
@@ -16714,7 +16923,7 @@ msgid "FeatureFlags|Strategies"
msgstr "ç­–ç•¥"
msgid "FeatureFlags|There was an error fetching the feature flags."
-msgstr "å–得功能標誌時發生錯誤。"
+msgstr "å–得特性標籤時發生錯誤。"
msgid "FeatureFlags|To prevent accidental actions we ask you to confirm your intention. Please type %{projectName} to proceed or close this modal to cancel."
msgstr "為了防止誤æ“作,我們需è¦æ‚¨å†æ¬¡ç¢ºèªã€‚請輸入%{projectName}繼續或關閉此å°è©±æ¡†ä»¥å–消。"
@@ -16761,6 +16970,9 @@ msgstr "2月"
msgid "February"
msgstr "2月"
+msgid "Feedback"
+msgstr "回饋 "
+
msgid "Feedback and Updates"
msgstr "回饋與更新"
@@ -16980,15 +17192,6 @@ msgstr "基於è¯é‚¦å¼å­¸ç¿’架構的廣告投放 (FLoC)"
msgid "FloC|Participate in FLoC"
msgstr "åƒåŠ  FLoC"
-msgid "FlowdockService|Enter your Flowdock token."
-msgstr "輸入您的 Flowdock 令牌(權æ–)"
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
-msgstr "從 GitLab 發é€äº‹ä»¶é€šçŸ¥åˆ° Flowdock æµã€‚"
-
-msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
-msgstr "從 GitLab å‘ Flowdock æµç™¼é€äº‹ä»¶é€šçŸ¥ã€‚ %{docs_link}"
-
msgid "Focus filter bar"
msgstr "èšç„¦éŽæ¿¾åˆ—"
@@ -17025,6 +17228,9 @@ msgstr "關注使用者的動態"
msgid "Followed users"
msgstr "關注的使用者"
+msgid "Following tags don't exist"
+msgstr "下列標籤ä¸å­˜åœ¨"
+
msgid "Font Color"
msgstr "å­—é«”é¡è‰²"
@@ -17047,7 +17253,7 @@ msgid "For each job, re-use the project workspace. If the workspace doesn't exis
msgstr "é‡å°æ¯å€‹ä½œæ¥­ï¼Œé‡è¤‡ä½¿ç”¨å°ˆæ¡ˆå·¥ä½œç©ºé–“。如果工作空間ä¸å­˜åœ¨ï¼Œè«‹ä½¿ç”¨ %{code_open} git clone %{code_close}。"
msgid "For example, the application using the token or the purpose of the token. Do not give sensitive information for the name of the token, as it will be visible to all %{resource_type} members."
-msgstr "例如,使用令牌的應用程å¼æˆ–令牌的用途。ä¸è¦æ供令牌å稱的æ•æ„Ÿè¨Šæ¯ï¼Œå› ç‚ºå®ƒå°‡å°æ‰€æœ‰ %{resource_type} æˆå“¡å¯è¦‹ã€‚"
+msgstr "例如,使用權æ–(令牌)的應用程å¼æˆ–權æ–(令牌)的用途。ä¸è¦æ供權æ–(令牌)å稱的æ•æ„Ÿè¨Šæ¯ï¼Œå› ç‚ºå®ƒå°‡å°æ‰€æœ‰ %{resource_type} æˆå“¡å¯è¦‹ã€‚"
msgid "For faster browsing, not all history is shown."
msgstr "為了ç€è¦½çš„æµæš¢åº¦ï¼Œä¸æœƒé¡¯ç¤ºæ‰€æœ‰æ­·å²è¨˜éŒ„。"
@@ -17169,12 +17375,21 @@ msgstr "分å‰è™•ç†ä¸­"
msgid "Forks"
msgstr "分å‰ï¼ˆFork)"
+msgid "ForksDivergence|%{ahead} %{commit_word} ahead of"
+msgstr "%{ahead} %{commit_word} 領先"
+
+msgid "ForksDivergence|%{behind} %{commit_word} behind"
+msgstr "%{behind} %{commit_word} è½å¾Œ"
+
+msgid "ForksDivergence|%{messages} upstream repository"
+msgstr "%{messages} 上游版本庫"
+
+msgid "ForksDivergence|Up to date with upstream repository"
+msgstr "與上游版本庫ä¿æŒåŒæ­¥"
+
msgid "Format: %{dateFormat}"
msgstr "æ ¼å¼ï¼š%{dateFormat}"
-msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
-msgstr "如果在 GitLab 軟體套件註冊庫中找ä¸åˆ°è»Ÿé«”套件,則將 %{package_type} 軟體套件請求轉發到 %{registry_type} 註冊庫"
-
msgid "Found errors in your %{gitlab_ci_yml}:"
msgstr "在您的 %{gitlab_ci_yml} 中找到錯誤:"
@@ -17251,10 +17466,6 @@ msgstr "從%{code_open}%{source_title}%{code_close}到"
msgid "From %{providerTitle}"
msgstr "%{providerTitle}來æºåœ°å€"
-msgid "From October 19, 2022, free private groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free private groups will be limited to %d members"
-msgstr[0] "從 2022 å¹´ 10 月 19 日起,å…è²»ç§äººç¾¤çµ„會é™åˆ¶æœ€å¤šåªèƒ½æœ‰ %d åæˆå“¡"
-
msgid "From issue creation until deploy to production"
msgstr "從建立議題到部署至正å¼ç’°å¢ƒ"
@@ -17297,6 +17508,9 @@ msgstr "æµæ°´ç·šä¸€èˆ¬è¨­å®š"
msgid "General settings"
msgstr "一般設定"
+msgid "Generate API key at %{site}"
+msgstr "在 %{site} ç”Ÿæˆ API 密鑰"
+
msgid "Generate a default set of labels"
msgstr "生æˆä¸€çµ„é è¨­çš„標記"
@@ -17927,7 +18141,7 @@ msgid "Get started!"
msgstr "開始å§ï¼"
msgid "Getting started with releases"
-msgstr "開始使用發佈"
+msgstr "開始使用發布"
msgid "Git"
msgstr "Git"
@@ -18142,6 +18356,10 @@ msgstr "GitLab版本"
msgid "GitLab will create a branch in your fork and start a merge request."
msgstr "GitLab 將在你的分å‰ï¼ˆFork)中建立一個分支並啟動一個åˆä½µè«‹æ±‚。"
+msgid "GitLab will enforce this limit in the future. If you are over %{free_user_limit} user when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} user or less or purchase a paid tier."
+msgid_plural "GitLab will enforce this limit in the future. If you are over %{free_user_limit} users when enforcement begins, your namespace will be placed in a %{link_start}read-only%{link_end} state. To avoid being placed in a read-only state, reduce your namespace to %{free_user_limit} users or less or purchase a paid tier."
+msgstr[0] "GitLab 將在未來強制執行該é™åˆ¶ï¼Œå¦‚æžœæ‚¨åœ¨å¼·åˆ¶åŸ·è¡Œé–‹å§‹æ™‚è¶…éŽ %{free_user_limit} 個使用者,您的命å空間將處於 %{link_start}read-only%{link_end} 狀態。為é¿å…處於 read-only 狀態,請將您的命å空間減少到 %{free_user_limit} 個使用者或更少,亦或是購買付費級別。"
+
msgid "GitLab.com (SaaS)"
msgstr "GitLab.com (SaaS)"
@@ -18335,7 +18553,7 @@ msgid "Given access %{time_ago}"
msgstr "%{time_ago}授權存å–"
msgid "Given epic is already related to this epic."
-msgstr "給定å²è©©å·²ç¶“與此å²è©©é—œè¯ã€‚"
+msgstr "給定å²è©© (epic) 已經與此å²è©© (epic) é—œè¯ã€‚"
msgid "Global Search is disabled for this scope"
msgstr "此範åœçš„全域æœå°‹å·²ç¦ç”¨"
@@ -18383,7 +18601,7 @@ msgid "GlobalSearch|Projects"
msgstr "專案"
msgid "GlobalSearch|Recent epics"
-msgstr "近期的å²è©©"
+msgstr "近期的å²è©© (epics) "
msgid "GlobalSearch|Recent issues"
msgstr "近期的議題"
@@ -18391,6 +18609,9 @@ msgstr "近期的議題"
msgid "GlobalSearch|Recent merge requests"
msgstr "近期的åˆä½µæ交"
+msgid "GlobalSearch|Result count is over limit."
+msgstr "çµæžœæ•¸ç›®è¶…éŽé™åˆ¶"
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr "çµæžœå·²æ›´æ–°ï¼Œ %{count} 個çµæžœå¯ç”¨ï¼Œä½¿ç”¨ä¸Šä¸‹ç®­é ­éµç€è¦½æœå°‹çµæžœåˆ—表,或使用 ENTER éµæ交。"
@@ -18418,6 +18639,9 @@ msgstr "新建議在下é¢é¡¯ç¤ºã€‚"
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
msgstr "使用快æ·éµ %{kbdOpen}/%{kbdClose} 開始æœå°‹"
+msgid "GlobalSearch|Users"
+msgstr "使用者"
+
msgid "GlobalSearch|What are you searching for?"
msgstr "您正在æœå°‹ä»€éº¼ï¼Ÿ"
@@ -18476,7 +18700,7 @@ msgid "Go to environments page to approve or reject"
msgstr "å‰å¾€ç’°å¢ƒé é¢ä»¥æ ¸å‡†æˆ–é§å›ž"
msgid "Go to epic"
-msgstr "å‰å¾€å²è©©ï¼ˆepic)"
+msgstr "å‰å¾€å²è©© (epic)"
msgid "Go to file"
msgstr "å‰å¾€æ–‡ä»¶"
@@ -18536,7 +18760,7 @@ msgid "Go to project"
msgstr "è·³å‰å¾€é …ç›®"
msgid "Go to releases"
-msgstr "å‰å¾€ç™¼ä½ˆ"
+msgstr "å‰å¾€ç™¼å¸ƒ"
msgid "Go to repository charts"
msgstr "å‰å¾€ç‰ˆæœ¬åº«åœ–表"
@@ -18595,6 +18819,9 @@ msgstr "å‰å¾€æ‚¨çš„åˆä½µè«‹æ±‚"
msgid "Go to your projects"
msgstr "å‰å¾€æ‚¨çš„é …ç›®"
+msgid "Go to your review requests"
+msgstr "å‰å¾€æ‚¨çš„審閱請求"
+
msgid "Go to your snippets"
msgstr "å‰å¾€æ‚¨çš„程å¼ç¢¼ç‰‡æ®µ"
@@ -18688,6 +18915,12 @@ msgstr "授予此金鑰的寫入權é™"
msgid "Graph"
msgstr "分支圖"
+msgid "GraphQL"
+msgstr "GraphQL"
+
+msgid "GraphQL endpoint path"
+msgstr "GraphQL 端點路徑"
+
msgid "GraphViewType|Job dependencies"
msgstr "GraphViewType|作業ä¾è³´"
@@ -18877,7 +19110,7 @@ msgstr "éŽåŽ» 30 天"
msgid "GroupActivityMetrics|Members added"
msgstr "加入新æˆå“¡æ•¸"
-msgid "GroupActivityMetrics|Merge Requests created"
+msgid "GroupActivityMetrics|Merge requests created"
msgstr "åˆä½µè«‹æ±‚已建立"
msgid "GroupActivityMetrics|Recent activity"
@@ -18914,10 +19147,10 @@ msgid "GroupRoadmap|%{startDateInWords} – %{endDateInWords}"
msgstr "%{startDateInWords} – %{endDateInWords}"
msgid "GroupRoadmap|Loading epics"
-msgstr "正在載入å²è©©"
+msgstr "正在載入å²è©© (epics) "
msgid "GroupRoadmap|New epic"
-msgstr "æ–°å²è©©"
+msgstr "æ–°å²è©© (epic) "
msgid "GroupRoadmap|No start and end date"
msgstr "無開始和çµæŸæ—¥æœŸ"
@@ -18926,16 +19159,16 @@ msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr "無開始日期 - %{dateWord}"
msgid "GroupRoadmap|Something went wrong while fetching epics"
-msgstr "è®€å– å²è©© 時發生錯誤"
+msgstr "讀å–å²è©© (epics) 時發生錯誤"
msgid "GroupRoadmap|Something went wrong while fetching milestones"
msgstr "å–得里程碑時出ç¾éŒ¯èª¤"
msgid "GroupRoadmap|Sorry, no epics matched your search"
-msgstr "å°ä¸èµ·ï¼Œæœªæœå°‹åˆ°ä»»ä½•ç¬¦åˆæ¢ä»¶çš„å²è©©"
+msgstr "抱歉,未æœå°‹åˆ°ä»»ä½•ç¬¦åˆæ¢ä»¶çš„å²è©© (epics)"
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
-msgstr "開發è—圖顯示了å²è©©æ²¿è‘—時間軸的進展情æ³"
+msgstr "開發è—圖顯示了å²è©© (epics) 沿著時間軸的進展情æ³"
msgid "GroupRoadmap|This quarter"
msgstr "本季度"
@@ -18944,19 +19177,19 @@ msgid "GroupRoadmap|This year"
msgstr "今年"
msgid "GroupRoadmap|To make your epics appear in the roadmap, add start or due dates to them."
-msgstr "è¦ä½¿æ‚¨çš„å²è©©å‡ºç¾åœ¨è·¯ç·šåœ–中,請為其加入開始日期或截止日期。"
+msgstr "è¦ä½¿æ‚¨çš„å²è©© (epics) 出ç¾åœ¨è·¯ç·šåœ–中,請為其加入開始日期或截止日期。"
msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of the %{linkStart}child epics%{linkEnd}."
-msgstr "è¦æŸ¥çœ‹é–‹ç™¼è—圖,請至少為一個%{linkStart}å­å²è©©%{linkEnd}加入開始或截止日期。"
+msgstr "è¦æŸ¥çœ‹é–‹ç™¼è—圖,請至少為一個 %{linkStart}å­å²è©© (epic) %{linkEnd} 加入開始或截止日期。"
msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups; from %{startDate} to %{endDate}."
-msgstr "è¦æŸ¥çœ‹é–‹ç™¼è—圖,請在此群組或其å­ç¾¤çµ„中的一個 å²è©© 中加入開始日期或截止日期;從 %{startDate} 到 %{endDate}。"
+msgstr "è¦æŸ¥çœ‹é–‹ç™¼è—圖,請在此群組或其å­ç¾¤çµ„中的一個å²è©© (epics) 中加入開始日期或截止日期;從 %{startDate} 到 %{endDate}。"
msgid "GroupRoadmap|To widen your search, change or remove filters; from %{startDate} to %{endDate}."
msgstr "è¦æ“´å¤§æ‚¨çš„æœå°‹ï¼Œè«‹è®Šæ›´æˆ–移除éŽæ¿¾å™¨ï¼Œå¾ž %{startDate} 到 %{endDate}。"
msgid "GroupRoadmap|View epics list"
-msgstr "查看å²è©©åˆ—表"
+msgstr "查看å²è©© (epics) 列表"
msgid "GroupRoadmap|Within 3 years"
msgstr "3 年之內"
@@ -18964,6 +19197,9 @@ msgstr "3 年之內"
msgid "GroupSAML|\"persistent\" recommended"
msgstr "推薦的\"æŒä¹…化\""
+msgid "GroupSAML|%{group_name} SAML authentication failed: %{message}"
+msgstr "%{group_name} SAML 身份驗證失敗:%{message}"
+
msgid "GroupSAML|%{strongOpen}Warning%{strongClose} - Enable %{linkStart}SSO enforcement%{linkEnd} to reduce security risks."
msgstr "%{strongOpen}警告%{strongClose} - 啟用 %{linkStart}SSO 實施%{linkEnd} 以é™ä½Žå®‰å…¨é¢¨éšªã€‚"
@@ -19069,6 +19305,9 @@ msgstr "SAML群組å稱"
msgid "GroupSAML|SAML Group Name: %{saml_group_name}"
msgstr "SAML群組å稱: %{saml_group_name}"
+msgid "GroupSAML|SAML Name ID and email address do not match your user account. Contact an administrator."
+msgstr "SAML å稱 ID 和電å­éƒµä»¶åœ°å€èˆ‡æ‚¨çš„使用者帳號ä¸ç›¸ç¬¦ï¼Œè«‹è¯ç¹«ç®¡ç†å“¡ã€‚"
+
msgid "GroupSAML|SAML Response Output"
msgstr "SAML回應輸出"
@@ -19240,8 +19479,8 @@ msgstr "%{group} 內的專案無法與其他群組共享"
msgid "GroupSettings|Reporting"
msgstr "報告"
-msgid "GroupSettings|Select a subgroup to use as the source for custom project templates for this group."
-msgstr "é¸æ“‡ä¸€å€‹å­ç¾¤çµ„用作該群組的自訂專案範本來æºã€‚"
+msgid "GroupSettings|Select a subgroup to use as a source of custom templates for new projects in this group. %{link_start}Learn more%{link_end}."
+msgstr "é¸æ“‡ä¸€å€‹å­ç¾¤çµ„以作為該群組中新專案的自定義模æ¿ä¾†æºï¼Œ%{link_start}了解更多%{link_end}。"
msgid "GroupSettings|Select parent group"
msgstr "é¸æ“‡ä¸Šå±¤ç¾¤çµ„"
@@ -19261,9 +19500,6 @@ msgstr "GroupSettings|為群組中建立的新檔案庫的é è¨­åˆ†æ”¯è¨­å®šåˆå
msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found."
msgstr "GroupSettings|如果未找到替代 CI 設定檔案,執行 Auto DevOps æµæ°´ç·šã€‚"
-msgid "GroupSettings|The projects in this subgroup can be selected as templates for new projects created in the group. %{link_start}Learn more.%{link_end}"
-msgstr "å¯ä»¥é¸æ“‡è©²å­ç¾¤çµ„中的專案作為該組中建立的新專案的模æ¿ã€‚ %{link_start}瞭解更多。%{link_end}"
-
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr "æ›´æ–° Auto DevOps æµæ°´ç·šæ™‚出ç¾å•é¡Œï¼š %{error_messages}。"
@@ -19393,6 +19629,9 @@ msgstr "GroupsNew|連接實例"
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr "GroupsNew|è¯ç¹«ç®¡ç†å“¡ä»¥å•Ÿç”¨åŒ¯å…¥ç¾¤çµ„çš„é¸é …。"
+msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time."
+msgstr "åœ¨ä¾†æº GitLab æœå‹™å¯¦ä¾‹çš„ %{pat_link_start}使用者設定%{pat_link_end} 範åœå…§ä½¿ç”¨ %{code_start}api%{code_end} å’Œ %{code_start}read_repository%{code_end} 建立權æ–(令牌)。基於 %{short_living_link_start}安全原因%{short_living_link_end},請為權æ–(令牌)設定一個較短的到期日期。請記ä½ï¼Œå¤§è¦ç¯„é·ç§»éœ€è¦æ›´å¤šæ™‚間。"
+
msgid "GroupsNew|Create group"
msgstr "建立群組"
@@ -19402,9 +19641,6 @@ msgstr "建立新群組"
msgid "GroupsNew|Create subgroup"
msgstr "建立å­ç¾¤çµ„"
-msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token."
-msgstr "在 GitLab 實例的 %{pat_link_start}user settings%{pat_link_end} 中建立。 出於%{short_living_link_start}安全原因%{short_living_link_end},在建立令牌時使用較短的到期日期。"
-
msgid "GroupsNew|GitLab source URL"
msgstr "GroupsNew|GitLab ä¾†æº URL"
@@ -19427,13 +19663,13 @@ msgid "GroupsNew|Not all related objects are migrated. %{docs_link_start}More in
msgstr "並éžæ‰€æœ‰ç›¸é—œç‰©ä»¶éƒ½å·²é·ç§»ã€‚%{docs_link_start}更多訊æ¯%{docs_link_end}。"
msgid "GroupsNew|Personal access token"
-msgstr "GroupsNew|個人存å–令牌(權æ–)(access token)"
+msgstr "GroupsNew|個人存å–權æ–(令牌)(access token)"
msgid "GroupsNew|Please fill in GitLab source URL."
msgstr "GroupsNew|請填寫 GitLab 來æºç¶²å€ã€‚"
msgid "GroupsNew|Please fill in your personal access token."
-msgstr "GroupsNew|請填寫您的個人存å–令牌(權æ–)(access token)。"
+msgstr "GroupsNew|請填寫您的個人存å–權æ–(令牌)。"
msgid "GroupsNew|Provide credentials for another instance of GitLab to import your groups directly."
msgstr "為å¦ä¸€å€‹ GitLab 實例æ供憑據以直接匯入您的群組。"
@@ -19874,12 +20110,18 @@ msgstr "ä¸å¯ç”¨çš„çµæ§‹"
msgid "Hierarchy|You can start using these items now."
msgstr "您ç¾åœ¨å¯ä»¥é–‹å§‹ä½¿ç”¨é€™äº›äº‹é …。"
+msgid "High - S2"
+msgstr "高 - S2"
+
msgid "High or unknown vulnerabilities present"
msgstr "存在高å±éšªæˆ–未知æ¼æ´ž"
msgid "Highest role:"
msgstr "最上層角色:"
+msgid "Highlight"
+msgstr "高亮"
+
msgid "HighlightBar|Alert events:"
msgstr "警報事件:"
@@ -19893,7 +20135,7 @@ msgid "HighlightBar|Time to SLA:"
msgstr "è·SLA時間: "
msgid "Historical release"
-msgstr "æ­·å²ç™¼ä½ˆç‰ˆæœ¬"
+msgstr "æ­·å²ç‰ˆæœ¬"
msgid "History"
msgstr "æ­·å²è¨˜éŒ„"
@@ -19913,9 +20155,6 @@ msgstr "主é "
msgid "Hook execution failed. Ensure the group has a project with commits."
msgstr "Hook 執行失敗。確ä¿ç¾¤çµ„有一個包å«æ交的專案。"
-msgid "Hook was successfully updated."
-msgstr "掛鉤已æˆåŠŸæ›´æ–°ã€‚"
-
msgid "Horizontal rule"
msgstr "æ°´å¹³è¦å‰‡"
@@ -20084,6 +20323,9 @@ msgstr "識別碼"
msgid "Identities"
msgstr "身份標識"
+msgid "IdentityVerification|%{linkStart}Enter a new phone number%{linkEnd}"
+msgstr "%{linkStart}輸入一個新的電話號碼%{linkEnd}"
+
msgid "IdentityVerification|A new code has been sent."
msgstr "已發é€æ–°é©—證碼。"
@@ -20108,6 +20350,9 @@ msgstr "建立專案"
msgid "IdentityVerification|Didn't receive a code?"
msgstr "沒有收到驗證碼?"
+msgid "IdentityVerification|Didn't receive a code? %{linkStart}Send a new code%{linkEnd}"
+msgstr "尚未收到代碼? %{linkStart}發é€æ–°çš„代碼%{linkEnd}"
+
msgid "IdentityVerification|Enter a code."
msgstr "輸入驗證碼。"
@@ -20147,8 +20392,8 @@ msgstr "已超éŽæœ€å¤§ç™»å…¥å˜—試次數。等待 %{interval} 後å†é‡è©¦ã€‚"
msgid "IdentityVerification|Phone number"
msgstr "電話號碼"
-msgid "IdentityVerification|Phone number can't be blank."
-msgstr "電話號碼ä¸èƒ½ç‚ºç©ºã€‚"
+msgid "IdentityVerification|Phone number is required."
+msgstr "電話號碼為必è¦ã€‚"
msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
msgstr "電話號碼最多åªèƒ½ç‚º %{maxLength} ä½æ•¸å­—。"
@@ -20189,6 +20434,12 @@ msgstr "驗證碼ä¸æ­£ç¢ºï¼Œå†æ¬¡è¼¸å…¥æˆ–é‡æ–°ç™¼é€æ–°é©—證碼。"
msgid "IdentityVerification|Verification code"
msgstr "驗證碼"
+msgid "IdentityVerification|Verification code can't be blank."
+msgstr "驗證碼ä¸å¾—為空。"
+
+msgid "IdentityVerification|Verification code must be a number."
+msgstr "驗證碼必須是數字。"
+
msgid "IdentityVerification|Verification successful"
msgstr "é©—è­‰æˆåŠŸ"
@@ -20201,9 +20452,18 @@ msgstr "驗證您的電å­éƒµä»¶åœ°å€"
msgid "IdentityVerification|Verify payment method"
msgstr "確èªæ‚¨çš„付款方å¼"
+msgid "IdentityVerification|Verify phone number"
+msgstr "驗證電話號碼"
+
msgid "IdentityVerification|Verify your identity"
msgstr "驗證您的身份"
+msgid "IdentityVerification|We sent a new code to +%{phoneNumber}"
+msgstr "æˆ‘å€‘å‘ +%{phoneNumber} 發é€äº†ä¸€å€‹æ–°çš„代碼"
+
+msgid "IdentityVerification|We've sent a verification code to +%{phoneNumber}"
+msgstr "我們已將驗證碼發é€è‡³ +%{phoneNumber}"
+
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr "您å¯ä»¥ç¨å¾Œéš¨æ™‚驗證您的帳號,來建立一個群組。"
@@ -20546,6 +20806,9 @@ msgstr "在此 URL 上沒有有效的 Git 倉庫。如果您的 HTTP 倉庫ä¸èƒ
msgid "Improve customer support with Service Desk"
msgstr "通éŽæœå‹™å°æ”¹å–„客戶支æ´"
+msgid "Improve quality with test cases"
+msgstr "通éŽæ¸¬è©¦æ¡ˆä¾‹æ高質é‡"
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr "在拉å–映åƒçš„情æ³ä¸‹ï¼Œæ‚¨çš„使用者將æˆç‚ºæ´»å‹•æè¦ä¸­æ‰€æœ‰ä½œç‚ºæ›´æ–°çµæžœçš„事件作者,例如建立新分支或將新æ交推é€åˆ°ç¾æœ‰åˆ†æ”¯ã€‚"
@@ -20571,7 +20834,7 @@ msgid "InProductMarketing|%{strong_start}Advanced application security%{strong_e
msgstr "%{strong_start}進階應用程å¼å®‰å…¨%{strong_end} — 包括 SASTã€DAST 掃æã€FUZZ 測試ã€ä¾è³´æ€§æŽƒæã€è¨±å¯è­‰åˆè¦æ€§ã€ç§˜å¯†æª¢æ¸¬"
msgid "InProductMarketing|%{strong_start}Company wide portfolio management%{strong_end} — including multi-level epics, scoped labels"
-msgstr "%{strong_start}å…¬å¸ç¯„åœå…§çš„組åˆç®¡ç†%{strong_end} — 包括多層級å²è©©ã€ç¯„åœæ¨™ç±¤"
+msgstr "%{strong_start}å…¬å¸ç¯„åœå…§çš„組åˆç®¡ç†%{strong_end} — 包括多層級å²è©© (epics) ã€ç¯„åœæ¨™ç±¤"
msgid "InProductMarketing|%{strong_start}Executive level insights%{strong_end} — including reporting on productivity, tasks by type, days to completion, value stream"
msgstr "%{strong_start}執行級別的見解%{strong_end} — 包括生產力報告ã€ä»»å‹™é¡žåž‹ã€å®Œæˆå¤©æ•¸ã€åƒ¹å€¼æµ"
@@ -20664,7 +20927,7 @@ msgid "InProductMarketing|Create a project in GitLab in 5 minutes"
msgstr "5分é˜å…§åœ¨ GitLab 中建立一個專案"
msgid "InProductMarketing|Create well-defined workflows by using scoped labels on issues, merge requests, and epics. Labels with the same scope cannot be used together, which prevents conflicts."
-msgstr "通éŽåœ¨è­°é¡Œã€åˆä½µè«‹æ±‚å’Œå²è©©ä¸Šä½¿ç”¨ç¯„åœæ¨™ç±¤ï¼Œå»ºç«‹å®šç¾©æ˜Žç¢ºçš„工作æµã€‚無法將相åŒç¯„åœçš„標籤一起使用,從而防止è¡çªã€‚"
+msgstr "通éŽåœ¨è­°é¡Œã€åˆä½µè«‹æ±‚å’Œå²è©© (epic) 上使用範åœæ¨™ç±¤ï¼Œå»ºç«‹å®šç¾©æ˜Žç¢ºçš„工作æµã€‚無法將相åŒç¯„åœçš„標籤一起使用,從而防止è¡çªã€‚"
msgid "InProductMarketing|Create your first project!"
msgstr "建立您的第一個專案ï¼"
@@ -20697,16 +20960,16 @@ msgid "InProductMarketing|Dynamic application security testing"
msgstr "動態應用程å¼å®‰å…¨æ¸¬è©¦"
msgid "InProductMarketing|Epics"
-msgstr "å²è©©"
+msgstr "å²è©© (Epics)"
msgid "InProductMarketing|Expand your DevOps journey with a free GitLab trial"
msgstr "通éŽå…費的 GitLab 試用擴展您的 DevOps 之旅"
msgid "InProductMarketing|Explore GitLab CI/CD"
-msgstr "探索GitLab CI/CD"
+msgstr "探索 GitLab CI/CD"
msgid "InProductMarketing|Explore the options"
-msgstr "探索é¸é …"
+msgstr "ç€è¦½é¸é …"
msgid "InProductMarketing|Explore the power of GitLab CI/CD"
msgstr "探索 GitLab CI/CD 的力é‡"
@@ -20859,7 +21122,7 @@ msgid "InProductMarketing|Lower cost of development"
msgstr "é™ä½Žé–‹ç™¼æˆæœ¬"
msgid "InProductMarketing|Make it easier to collaborate on high-level ideas by grouping related issues in an epic."
-msgstr "通éŽå°‡ç›¸é—œè­°é¡Œåˆ†çµ„到å²è©©ä¸­ï¼Œæ›´å®¹æ˜“就高層次想法進行å”åŒåˆä½œã€‚"
+msgstr "通éŽå°‡ç›¸é—œè­°é¡Œåˆ†çµ„到å²è©© (epic) 中,更容易就高層次想法進行å”åŒåˆä½œã€‚"
msgid "InProductMarketing|Making the switch? It's easier than you think to import your projects into GitLab. Move %{github_link}, or import something %{bitbucket_link}."
msgstr "進行轉æ›ï¼Ÿå°‡å°ˆæ¡ˆåŒ¯å…¥ GitLab 比您想åƒçš„è¦å®¹æ˜“,移動 %{github_link},或者匯入一些æ±è¥¿ %{bitbucket_link}。"
@@ -21027,7 +21290,7 @@ msgid "InProductMarketing|Used by more than 100,000 organizations from around th
msgstr "å…¨çƒè¶…éŽ10è¬å€‹çµ„織使用:"
msgid "InProductMarketing|Visualize your epics and milestones in a timeline."
-msgstr "在時間線上å¯è¦–化您的å²è©©å’Œé‡Œç¨‹ç¢‘。"
+msgstr "在時間線上å¯è¦–化您的å²è©© (epics) 和里程碑。"
msgid "InProductMarketing|Want to get your iOS app up and running, including publishing all the way to TestFlight? Follow our guide to set up GitLab and fastlane to publish iOS apps to the App Store."
msgstr "想è¦å•Ÿå‹•ä¸¦é‹è¡Œæ‚¨çš„ iOS 應用程åºï¼ŒåŒ…括一直發布到 TestFlight? 按照我們的指å—設置 GitLab å’Œ fastlane 以將 iOS 應用程å¼ç™¼å¸ƒåˆ° App Store。"
@@ -21113,12 +21376,18 @@ msgstr "事件"
msgid "Incident Management Limits"
msgstr "事件管ç†é™åˆ¶"
+msgid "Incident creation cancelled."
+msgstr "事故建立已å–消。"
+
msgid "Incident details"
msgstr "事件詳情"
msgid "Incident template (optional)."
msgstr "事件範本(å¯é¸ï¼‰ã€‚"
+msgid "Incident title"
+msgstr "事故å稱"
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr "剩餘%{hours}å°æ™‚%{minutes} 分é˜"
@@ -21195,7 +21464,7 @@ msgid "IncidentManagement|None"
msgstr "ç„¡"
msgid "IncidentManagement|Open"
-msgstr "打開"
+msgstr "é–‹å•Ÿ"
msgid "IncidentManagement|Page your team with escalation policies"
msgstr "通知您的團隊使用å‡ç´šç­–ç•¥"
@@ -21437,6 +21706,9 @@ msgstr "索引刪除已å–消"
msgid "Indicates whether this runner can pick jobs without tags"
msgstr "指示此runner是å¦å¯ä»¥é¸æ“‡ç„¡æ¨™è¨˜çš„作業"
+msgid "Info"
+msgstr "資訊"
+
msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
msgstr "通知使用者沒有上傳 SSH 金鑰,如果沒有 SSH 金鑰,將無法é€éŽ SSH 推é€ã€‚"
@@ -21708,6 +21980,9 @@ msgstr "啟用 SSL 驗證"
msgid "Integrations|Enable comments"
msgstr "啟用留言評論"
+msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
+msgstr "為 Slack 工作å€å•Ÿç”¨æ–œç·šå‘½ä»¤å’Œé€šçŸ¥ã€‚"
+
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr "確ä¿æ‚¨çš„æœå‹™å¯¦ä¾‹ URL 正確並且您的實例組態é…置是正確的。 %{linkStart}了解更多%{linkEnd}。"
@@ -21741,6 +22016,9 @@ msgstr "GitLab 管ç†å“¡å¯ä»¥è¨­å®šæ‰€æœ‰ç¾¤çµ„和專案é è¨­ç¹¼æ‰¿å’Œä½¿ç”¨çš
msgid "Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}."
msgstr "GitLab 管ç†å“¡å¯ä»¥è¨­å®šä¸€å€‹ç¾¤çµ„中所有專案é è¨­ç¹¼æ‰¿å’Œä½¿ç”¨çš„æ•´åˆï¼Œé€™äº›æ•´åˆæ‡‰ç”¨æ–¼æ‰€æœ‰å°šæœªä½¿ç”¨è‡ªè¨‚設定的專案, 如果專案需è¦è¨­å®šï¼Œæ‚¨å¯ä»¥è¦†è“‹è‡ªè¨‚設定。 瞭解更多關於 %{integrations_link_start}群組級整åˆç®¡ç†%{link_end}。"
+msgid "Integrations|GitLab for Slack app"
+msgstr "GitLab çš„ Slack æ‡‰ç”¨ç¨‹å¼ "
+
msgid "Integrations|Group-level integration management"
msgstr "群組層級整åˆç®¡ç†"
@@ -22089,6 +22367,9 @@ msgstr "邀請您的åŒäº‹"
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr "ç›®å‰æ‚¨é‚„沒有邀請任何人加入這個群組。 您å¯ä»¥é‚€è«‹æ‚¨çš„åŒäº‹åˆ°ç¾¤çµ„,以方便討論å•é¡Œï¼Œåœ¨åˆä½µè«‹æ±‚中進行å”作,以åŠåˆ†äº«æ‚¨çš„知識。"
+msgid "InviteMembersModal| Inviting a group %{linkStart}adds its members to your group%{linkEnd}, including members who join after the invite. This might put your group over the free %{count} user limit."
+msgstr "邀請群組 %{linkStart} 會將其æˆå“¡æ·»åŠ åˆ°æ‚¨çš„群組 %{linkEnd},包括在å—邀後加入的æˆå“¡ã€‚這å¯èƒ½æœƒä½¿æ‚¨çš„ç¾¤çµ„è¶…éŽ %{count} 個å…費的使用者é™åˆ¶ã€‚"
+
msgid "InviteMembersModal| To get more members, the owner of this namespace can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr "è¦å–得更多æˆå“¡ï¼Œè©²å‘½å空間的æ“有者å¯ä»¥ %{trialLinkStart}開始試用%{trialLinkEnd} 或 %{upgradeLinkStart} å‡ç´š %{upgradeLinkEnd} 到付費等級。"
@@ -22137,8 +22418,8 @@ msgstr "管ç†æˆå“¡"
msgid "InviteMembersModal|Members were successfully added"
msgstr "æˆå“¡å·²æˆåŠŸåŠ å…¥"
-msgid "InviteMembersModal|Please select members or type email addresses to invite"
-msgstr "è«‹é¸æ“‡è¦é‚€è«‹çš„æˆå“¡æˆ–輸入電å­éƒµä»¶åœ°å€"
+msgid "InviteMembersModal|Please add members to invite"
+msgstr "請加入è¦é‚€è«‹çš„æˆå“¡"
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr "查看邀請錯誤並é‡è©¦ï¼š"
@@ -22349,7 +22630,7 @@ msgid "IssuableStatus|promoted"
msgstr "å·²å‡ç´š"
msgid "Issuable|epic"
-msgstr "å²è©©"
+msgstr "å²è©© (epic)"
msgid "Issuable|escalation policy"
msgstr "å‡ç´šæ”¿ç­–"
@@ -22364,7 +22645,7 @@ msgid "Issue"
msgstr "議題"
msgid "Issue %{issue_reference} has already been added to epic %{epic_reference}."
-msgstr "è­°é¡Œ%{issue_reference}已被加入到å²è©©%{epic_reference}。"
+msgstr "è­°é¡Œ%{issue_reference}已被加入到å²è©© (epic) %{epic_reference}。"
msgid "Issue Analytics"
msgstr "議題分æž"
@@ -22376,7 +22657,7 @@ msgid "Issue Type"
msgstr "議題類型"
msgid "Issue already promoted to epic."
-msgstr "議題已å‡ç´šç‚ºå²è©©ã€‚"
+msgstr "議題已å‡ç´šç‚ºå²è©© (epic) 。"
msgid "Issue cannot be found."
msgstr "議題無法找到"
@@ -22553,7 +22834,7 @@ msgid "Issues with label %{label}"
msgstr "帶標記 %{label} 的議題"
msgid "Issues with no epic assigned"
-msgstr "未分é…å²è©©çš„è­°é¡Œ"
+msgstr "未分é…å²è©© (epic) çš„è­°é¡Œ"
msgid "Issues, merge requests, pushes, and comments."
msgstr "議題,åˆä½µè«‹æ±‚,推é€åŠç•™è¨€ã€‚"
@@ -22603,9 +22884,6 @@ msgstr "移動議題時發生錯誤。"
msgid "Issue|Title"
msgstr "標題"
-msgid "It is not possible to %{action} files that are stored in LFS using the web interface"
-msgstr "無法使用 Web ç•Œé¢ %{action} 儲存在 LFS 中的文件"
-
msgid "It looks like you have some draft commits in this branch."
msgstr "看起來你在這個分支上有一些è‰ç¨¿æ交。"
@@ -22744,9 +23022,6 @@ msgstr "載入迭代週期時發生錯誤。"
msgid "Iterations|Iteration cadences"
msgstr "迭代週期"
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
-msgstr "迭代是在一段時間內追蹤議題的一種方法,使å°çµ„也能夠追蹤速度和波動指標。"
-
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr "迭代排程從 %{weekday} 開始。"
@@ -23030,7 +23305,7 @@ msgid "JiraService|Move to Done"
msgstr "移動到完æˆ"
msgid "JiraService|Open Jira"
-msgstr "打開Jira"
+msgstr "é–‹å•Ÿ Jira"
msgid "JiraService|Password or API token"
msgstr "密碼或 API 令牌"
@@ -23111,7 +23386,7 @@ msgid "Job %{jobName}"
msgstr "作業 %{jobName}"
msgid "Job Failed #%{build_id}"
-msgstr "作業 #%{build_id} 已失敗 "
+msgstr "#%{build_id} 作業失敗 "
msgid "Job has been erased"
msgstr "作業已被刪除"
@@ -23179,9 +23454,6 @@ msgstr "沒有å¯é¡¯ç¤ºçš„作業"
msgid "Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens."
msgstr "å°ä»»å‹™éŽæ¿¾å¾Œçš„æœå°‹åŠŸèƒ½ï¼Œç•¶å‰ä¸æ”¯æ´æœå°‹åŽŸå§‹æ–‡æœ¬ã€‚請使用å¯ç”¨çš„æœå°‹ä»¤ç‰Œã€‚"
-msgid "Jobs|Status"
-msgstr "狀態"
-
msgid "Jobs|There was a problem fetching the failed jobs."
msgstr "讀å–失敗的作業時出ç¾å•é¡Œã€‚"
@@ -23303,7 +23575,7 @@ msgid "Job|Show complete raw"
msgstr "顯示完整æº"
msgid "Job|Skipped"
-msgstr "已跳éŽ"
+msgstr "ç•¥éŽ"
msgid "Job|Status"
msgstr "狀態"
@@ -23332,6 +23604,9 @@ msgstr "此作業已阻塞,因為該項目沒有分é…任何å¯ç”¨Runner。"
msgid "Job|This job is stuck because you don't have any active runners that can run this job."
msgstr "作業|此作業被å¡ä½äº†ï¼Œå› ç‚ºæ‚¨æ²’有任何啟用的執行器å¯ä»¥é‹è¡Œæ­¤ä½œæ¥­ã€‚"
+msgid "Job|Update CI/CD variables"
+msgstr "更新 CI/CD 變數"
+
msgid "Job|Waiting for resource"
msgstr "等待資æº"
@@ -23363,10 +23638,10 @@ msgid "Join your team on GitLab and contribute to an existing project"
msgstr "加入您的 GitLab 團隊,並為ç¾æœ‰å°ˆæ¡ˆåšå‡ºè²¢ç»"
msgid "Joined %{time_ago}"
-msgstr "加入於%{time_ago}"
+msgstr "於 %{time_ago} 加入"
msgid "Joined %{user_created_time}"
-msgstr "加入於%{user_created_time}"
+msgstr "於 %{user_created_time} 加入"
msgid "Joined projects (%{projects_count})"
msgstr "已加入的專案 (%{projects_count})"
@@ -23413,6 +23688,9 @@ msgstr "金鑰"
msgid "Key (PEM)"
msgstr "金鑰 (PEM)"
+msgid "Key result"
+msgstr "é—œéµçµæžœ"
+
msgid "Key:"
msgstr "金鑰:"
@@ -23482,9 +23760,6 @@ msgstr "Kubernetes å¢é›†"
msgid "Kubernetes deployment not found"
msgstr "找ä¸åˆ°Kubernetes部署"
-msgid "Kubernetes error: %{error_code}"
-msgstr "Kubinentes 錯誤: %{error_code}"
-
msgid "LDAP"
msgstr "LDAP"
@@ -23558,15 +23833,18 @@ msgstr "標記"
msgid "Labels"
msgstr "標記"
-msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr "標記å¯ä»¥æ‡‰ç”¨æ–¼ %{features}。群組標記å¯ç”¨æ–¼ç¾¤çµ„中的所有專案。"
-
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "標記å¯ç”¨æ–¼å°è­°é¡Œå’Œåˆä½µè«‹æ±‚進行分類。"
+msgid "Labels can be applied to issues and merge requests. Group labels are available for any project within the group."
+msgstr "標記å¯ä»¥æ‡‰ç”¨æ–¼è­°é¡Œèˆ‡åˆä½µè«‹æ±‚,群組標記å¯ç”¨æ–¼ç¾¤çµ„內的任何專案。"
+
msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr "標籤å¯ä»¥æ‡‰ç”¨åœ¨è­°é¡Œå’Œåˆä½µè«‹æ±‚,為標籤加上星標å¯ä»¥ä½¿å…¶æˆç‚ºå„ªå…ˆæ¨™ç±¤ã€‚"
+msgid "Labels can be applied to issues, merge requests, and epics. Group labels are available for any project within the group."
+msgstr "標記å¯ä»¥æ‡‰ç”¨æ–¼è­°é¡Œã€åˆä½µè«‹æ±‚å’Œå²è©© (epics) ,群組標記å¯ç”¨æ–¼ç¾¤çµ„內的任何專案。"
+
msgid "Labels with no issues in this iteration:"
msgstr "此迭代中沒有議題的標記:"
@@ -23652,6 +23930,9 @@ msgstr "最後一次由 %{link_start}%{avatar} %{name}%{link_end} 編輯"
msgid "Last event"
msgstr "最近一次事件"
+msgid "Last login"
+msgstr "上次登錄"
+
msgid "Last modified"
msgstr "最近修改"
@@ -23757,6 +24038,9 @@ msgstr "瞭解更多"
msgid "Learn More."
msgstr "瞭解更多。"
+msgid "Learn about signing commits with SSH keys."
+msgstr "了解如何使用 SSH 密鑰簽署æ交。"
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr "了解如何 %{link_start}è²¢ç»åˆ°å…§å»ºçš„範本%{link_end}"
@@ -23806,7 +24090,7 @@ msgid "Learn more about issues."
msgstr "了解更多關於議題的資訊。"
msgid "Learn more about linking epics"
-msgstr "å–得更多關於連çµå²è©©çš„資訊"
+msgstr "å–得更多關於連çµå²è©© (epics) 的資訊"
msgid "Learn more about linking issues"
msgstr "å–得更多關於連çµè­°é¡Œçš„資訊"
@@ -24187,7 +24471,7 @@ msgid "Limit the number of inbound incident management alerts that can be sent t
msgstr "é™åˆ¶å¯ä»¥ç™¼é€åˆ°å°ˆæ¡ˆçš„入站事件管ç†è­¦å ±æ•¸é‡ã€‚"
msgid "Limit the number of issues and epics per minute a user can create through web and API requests."
-msgstr "é™åˆ¶ä½¿ç”¨è€…æ¯åˆ†é˜å¯ä»¥é€šéŽ Web å’Œ API 請求建立的議題和å²è©©æ•¸é‡ã€‚"
+msgstr "é™åˆ¶ä½¿ç”¨è€…æ¯åˆ†é˜å¯ä»¥é€šéŽ Web å’Œ API 請求建立的議題和å²è©© (epics) 數é‡ã€‚"
msgid "Limit the number of pipeline creation requests per minute. This limit includes pipelines created through the UI, the API, and by background processing."
msgstr "é™åˆ¶æ¯åˆ†é˜çš„æµæ°´ç·šå»ºç«‹è«‹æ±‚數。此é™åˆ¶åŒ…æ‹¬é€šéŽ UIã€API 和後å°è™•ç†å»ºç«‹çš„æµæ°´ç·šã€‚"
@@ -24244,7 +24528,7 @@ msgid "Linked emails (%{email_count})"
msgstr "已連çµçš„é›»å­éƒµä»¶ (%{email_count})"
msgid "Linked epics"
-msgstr "已連çµå²è©©"
+msgstr "已連çµå²è©© (epics) "
msgid "Linked incidents or issues"
msgstr "é—œè¯çš„事故或議題"
@@ -24426,12 +24710,12 @@ msgstr "已鎖定文件"
msgid "Locked by %{fileLockUserName}"
msgstr "被%{fileLockUserName}鎖定"
+msgid "Locked files"
+msgstr "已鎖定的文件"
+
msgid "Locked the discussion."
msgstr "鎖定討論."
-msgid "Locks give the ability to lock specific file or folder."
-msgstr "加鎖å¯ä»¥éŽ–定特定的文件或資料夾。"
-
msgid "Locks the discussion."
msgstr "鎖定討論."
@@ -24480,6 +24764,9 @@ msgstr "Logo 將被刪除,您確定嗎?"
msgid "Logs"
msgstr "日誌"
+msgid "Low - S4"
+msgstr "低 - S4"
+
msgid "Low vulnerabilities present"
msgstr "存在低風險æ¼æ´ž"
@@ -24585,6 +24872,9 @@ msgstr "將該 %{type} 設為機密。"
msgid "Manage %{workspace} labels"
msgstr "ç®¡ç† %{workspace} 標記"
+msgid "Manage Dashboards"
+msgstr "管ç†å„€è¡¨æ¿"
+
msgid "Manage Web IDE features."
msgstr "ç®¡ç† Web IDE 功能。"
@@ -24592,7 +24882,7 @@ msgid "Manage access"
msgstr "管ç†æ¬Šé™"
msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
-msgstr "管ç†å¯ä»¥å°‡ GitLab 用作 OAuth æ供程å¼çš„應用程å¼ï¼Œä»¥åŠæ‚¨å·²æŽˆæ¬Šä½¿ç”¨æ‚¨çš„帳號的應用程å¼ã€‚"
+msgstr "管ç†å¯ä»¥ä½¿ç”¨ GitLab 作為 OAuth æ供者的應用程åºï¼Œä»¥åŠæ‚¨å·²æŽˆæ¬Šä½¿ç”¨æ‚¨çš„帳號的應用程å¼ã€‚"
msgid "Manage applications that use GitLab as an OAuth provider."
msgstr "管ç†ä½¿ç”¨ GitLab 作為 OAuth æ供程å¼çš„應用程å¼ã€‚"
@@ -25083,6 +25373,9 @@ msgstr "å¹³å‡åˆä½µæ™‚é–“"
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr "ä¾ç¨‹å¼ç¢¼çš„ä½å…ƒçµ„數測é‡ã€‚排除已產生的åŠä¾›æ‡‰å•†çš„程å¼ç¢¼ã€‚"
+msgid "Medium - S3"
+msgstr "中 - S3"
+
msgid "Medium timeout"
msgstr "中等逾時"
@@ -25093,7 +25386,7 @@ msgid "Member since"
msgstr "æˆå“¡è‡ª"
msgid "Member since %{date}"
-msgstr "加入於 %{date}"
+msgstr "於 %{date} 加入"
msgid "Member since:"
msgstr "æˆå“¡è‡ªï¼š"
@@ -25104,6 +25397,12 @@ msgstr "%{member_name}邀請您使用GitLab"
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
msgstr "邀請加入 %{project_or_group} %{project_or_group_name}"
+msgid "MemberRole|can't be changed"
+msgstr "無法被變更"
+
+msgid "MemberRole|must be top-level namespace"
+msgstr "必須是最上層的命å空間"
+
msgid "Members"
msgstr "æˆå“¡"
@@ -26227,6 +26526,9 @@ msgstr "加入專案"
msgid "Modal|Close"
msgstr "關閉"
+msgid "Model candidate details"
+msgstr "模型候é¸è©³ç´°è³‡è¨Š"
+
msgid "Modified"
msgstr "已修改"
@@ -26290,9 +26592,6 @@ msgstr "更多訊æ¯"
msgid "More information"
msgstr "更多訊æ¯"
-msgid "More information and share feedback"
-msgstr "更多訊æ¯å’Œå…±äº«å›žé¥‹"
-
msgid "More information is available|here"
msgstr "這裡"
@@ -26501,6 +26800,9 @@ msgstr "需è¦æŽ¡å–的措施:已超出 %{namespace_name} 的儲存空間"
msgid "NamespaceStorage|Buy more storage"
msgstr "購買更多儲存"
+msgid "NamespaceStorage|Uploads are not counted in namespace storage quotas."
+msgstr "上傳ä¸è¨ˆå…¥å‘½å空間的儲存é¡åº¦ã€‚"
+
msgid "NamespaceStorage|We recommend that you buy additional storage to ensure your service is not interrupted."
msgstr "我們建議您購買é¡å¤–儲存,確ä¿æ‚¨çš„æœå‹™ä¸ä¸­æ–·ã€‚"
@@ -26616,13 +26918,13 @@ msgid "New Environment"
msgstr "新建環境"
msgid "New Epic"
-msgstr "新增å²è©©"
+msgstr "新增å²è©© (Epic) "
msgid "New File"
msgstr "新增文件"
msgid "New Group"
-msgstr "新建群組"
+msgstr "新增群組"
msgid "New Group Name"
msgstr "新增群組å稱"
@@ -26647,7 +26949,7 @@ msgid "New Pages Domain"
msgstr "新增Pages網域"
msgid "New Password"
-msgstr "新增密碼"
+msgstr "新密碼"
msgid "New Pipeline Schedule"
msgstr "新增æµæ°´ç·šæŽ’程"
@@ -26679,8 +26981,11 @@ msgstr "新增分支"
msgid "New branch unavailable"
msgstr "新增的分支ä¸å¯ç”¨"
+msgid "New code quality findings"
+msgstr "新的程å¼ç¢¼è³ªé‡æª¢æ¸¬"
+
msgid "New confidential epic title "
-msgstr "新增機密å²è©©æ¨™é¡Œ "
+msgstr "新增機密å²è©© (epic) 標題 "
msgid "New confidential issue title"
msgstr "新增機密議題標題"
@@ -26701,10 +27006,10 @@ msgid "New environment"
msgstr "新增環境"
msgid "New epic"
-msgstr "建立å²è©©"
+msgstr "建立å²è©© (epic) "
msgid "New epic title"
-msgstr "新增å²è©©æ¨™é¡Œ"
+msgstr "新增å²è©© (epic) 標題"
msgid "New error tracking access token has been generated!"
msgstr "已產生新的錯誤追踪存å–權æ–ï¼"
@@ -26713,7 +27018,7 @@ msgid "New file"
msgstr "新增文件"
msgid "New group"
-msgstr "新建群組"
+msgstr "新增群組"
msgid "New health check access token has been generated!"
msgstr "已生æˆæ–°çš„執行狀æ³æª¢æŸ¥å­˜å–令牌(權æ–)ï¼"
@@ -26721,6 +27026,12 @@ msgstr "已生æˆæ–°çš„執行狀æ³æª¢æŸ¥å­˜å–令牌(權æ–)ï¼"
msgid "New identity"
msgstr "新建身份標識"
+msgid "New incident"
+msgstr "新增事故"
+
+msgid "New incident has been created"
+msgstr "已建立新的事故"
+
msgid "New issue"
msgstr "新建議題"
@@ -26749,7 +27060,7 @@ msgid "New name"
msgstr "新增å稱"
msgid "New password"
-msgstr "新增密碼"
+msgstr "新密碼"
msgid "New pipelines cause older pending or running pipelines on the same branch to be cancelled."
msgstr "æ–°æµæ°´ç·šæœƒå°Žè‡´åŒä¸€åˆ†æ”¯ä¸Šè¼ƒèˆŠçš„待處ç†æˆ–正在é‹è¡Œçš„æµæ°´ç·šè¢«å–消。"
@@ -26764,7 +27075,7 @@ msgid "New project pages"
msgstr "新建專案é é¢"
msgid "New project/repository"
-msgstr "新建專案/版本庫"
+msgstr "新增專案/版本庫"
msgid "New public deploy key"
msgstr "新建公共部署金鑰"
@@ -26788,7 +27099,7 @@ msgid "New schedule"
msgstr "新增排程"
msgid "New snippet"
-msgstr "新建程å¼ç¢¼ç‰‡æ®µ"
+msgstr "新增程å¼ç¢¼ç‰‡æ®µ"
msgid "New subgroup"
msgstr "新建å­ç¾¤çµ„"
@@ -26851,7 +27162,7 @@ msgid "No CSV data to display."
msgstr "沒有è¦é¡¯ç¤ºçš„ CSV 資料。"
msgid "No Epic"
-msgstr "ç„¡å²è©©"
+msgstr "ç„¡å²è©© (Epic) "
msgid "No Google Cloud projects - You need at least one Google Cloud project"
msgstr "沒有 Google Cloud 專案 - 您至少需è¦ä¸€å€‹ Google Cloud 專案"
@@ -26908,7 +27219,7 @@ msgid "No changes between %{source} and %{target}"
msgstr "%{source}和%{target} 之間沒有變更內容"
msgid "No child epics match applied filters"
-msgstr "沒有符åˆç•¶å‰éŽæ¿¾å™¨çš„å­å²è©©"
+msgstr "沒有符åˆç•¶å‰éŽæ¿¾å™¨çš„å­å²è©© (epics) "
msgid "No commenters"
msgstr "沒有留言評論者"
@@ -26994,9 +27305,6 @@ msgstr "未發ç¾è­°é¡Œ"
msgid "No iteration"
msgstr "無迭代"
-msgid "No iterations to show"
-msgstr "沒有å¯é¡¯ç¤ºè¿­ä»£"
-
msgid "No job log"
msgstr "沒有作業日誌"
@@ -27096,6 +27404,9 @@ msgstr "沒有çµæžœ"
msgid "No results found"
msgstr "沒有çµæžœ"
+msgid "No results found."
+msgstr "æœå°‹ä¸åˆ°è³‡æ–™"
+
msgid "No runner executable"
msgstr "沒有執行器å¯åŸ·è¡Œæ–‡ä»¶"
@@ -27236,16 +27547,16 @@ msgid "Note that pushing to GitLab requires write access to this repository."
msgstr "請注æ„,推é€åˆ°GitLab需è¦å°æ­¤ç‰ˆæœ¬åº«æœ‰å¯«å…¥æ¬Šé™ã€‚"
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
-msgstr "æ示:作為GitLab管ç†å“¡ï¼Œå¯ä»¥é…ç½® %{github_integration_link},這將å…許é€éŽGitHub登入並å…許連接Github程å¼ç¢¼ç‰ˆæœ¬åº«è€Œä¸éœ€è¦å€‹äººå­˜å–令牌(權æ–)。"
+msgstr "æ示:作為GitLab管ç†å“¡ï¼Œå¯ä»¥é…ç½® %{github_integration_link},這將å…許é€éŽGitHub登入並å…許連接Github程å¼ç¢¼ç‰ˆæœ¬åº«è€Œä¸éœ€è¦å€‹äººå­˜å–權æ–(令牌)。"
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
-msgstr "æ示:作為GitLab管ç†å“¡ï¼Œå¯ä»¥é…ç½® %{github_integration_link},這將å…許é€éŽGitHub登入並å…許匯入Github程å¼ç¢¼ç‰ˆæœ¬åº«è€Œä¸éœ€è¦å€‹äººå­˜å–令牌(權æ–)。"
+msgstr "æ示:作為GitLab管ç†å“¡ï¼Œå¯ä»¥é…ç½® %{github_integration_link},這將å…許é€éŽGitHub登入並å…許匯入Github程å¼ç¢¼ç‰ˆæœ¬åº«è€Œä¸éœ€è¦å€‹äººå­˜å–權æ–(令牌)。"
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
-msgstr "æ示:如GitLab管ç†å“¡é…ç½® %{github_integration_link},將å…許é€éŽGitHub登入並å…許連接Github程å¼ç¢¼ç‰ˆæœ¬åº«è€Œä¸éœ€è¦å€‹äººå­˜å–令牌(權æ–)。"
+msgstr "æ示:如GitLab管ç†å“¡é…ç½® %{github_integration_link},將å…許é€éŽGitHub登入並å…許連接Github程å¼ç¢¼ç‰ˆæœ¬åº«è€Œä¸éœ€è¦å€‹äººå­˜å–權æ–(令牌)。"
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
-msgstr "æ示:如GitLab管ç†å“¡é…ç½® %{github_integration_link},將å…許é€éŽGitHub登入並å…許匯入Github程å¼ç¢¼ç‰ˆæœ¬åº«è€Œä¸éœ€è¦å€‹äººå­˜å–令牌(權æ–)。"
+msgstr "æ示:如GitLab管ç†å“¡é…ç½® %{github_integration_link},將å…許é€éŽGitHub登入並å…許匯入Github程å¼ç¢¼ç‰ˆæœ¬åº«è€Œä¸éœ€è¦å€‹äººå­˜å–權æ–(令牌)。"
msgid "Note: current forks will keep their visibility level."
msgstr "注æ„:當å‰çš„分å‰ï¼ˆFork)將ä¿æŒå…¶å¯è¦‹ç­‰ç´šã€‚"
@@ -27308,7 +27619,7 @@ msgid "Nothing to preview."
msgstr "沒有å¯é è¦½çš„內容。"
msgid "Notification Email"
-msgstr "é›»å­éƒµä»¶é€šçŸ¥"
+msgstr "通知用電å­éƒµä»¶"
msgid "Notification events"
msgstr "通知事件"
@@ -27363,7 +27674,7 @@ msgid "NotificationEvent|Moved project"
msgstr "移動專案"
msgid "NotificationEvent|New epic"
-msgstr "æ–°å²è©©"
+msgstr "æ–°å²è©© (epic) "
msgid "NotificationEvent|New issue"
msgstr "新建議題"
@@ -27375,7 +27686,7 @@ msgid "NotificationEvent|New note"
msgstr "新建通知"
msgid "NotificationEvent|New release"
-msgstr "新發佈"
+msgstr "新發布"
msgid "NotificationEvent|Push to merge request"
msgstr "推é€åˆ°åˆä½µè«‹æ±‚"
@@ -27749,9 +28060,24 @@ msgstr "碎片數é‡"
msgid "OK"
msgstr "確定"
+msgid "OKR|Existing key result"
+msgstr "ç¾å­˜çš„é—œéµçµæžœ"
+
+msgid "OKR|Existing objective"
+msgstr "ç¾å­˜ç›®æ¨™"
+
+msgid "OKR|New key result"
+msgstr "æ–°çš„é—œéµçµæžœ"
+
+msgid "OKR|New objective"
+msgstr "新的目標"
+
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr "物件在伺æœå™¨ä¸Šä¸å­˜åœ¨, 或者您沒有存å–它的權é™"
+msgid "Objective"
+msgstr "目標 "
+
msgid "Observability"
msgstr "å¯è§€å¯Ÿæ€§"
@@ -28131,6 +28457,9 @@ msgstr "沒有排程中的掃æ。"
msgid "OnDemandScans|Timezone"
msgstr "時å€"
+msgid "OnDemandScans|Verify configuration"
+msgstr "組態確èª"
+
msgid "OnDemandScans|View results"
msgstr "查看çµæžœ"
@@ -28166,7 +28495,7 @@ msgid "One or more groups that you don't have access to."
msgstr "您沒有存å–的一個或多個群組的權é™ã€‚"
msgid "One or more of you personal access tokens were revoked"
-msgstr "您的一個或多個個人存å–令牌(權æ–)已被å–消"
+msgstr "您的一個或多個個人存å–權æ–(令牌)已被å–消"
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr "您的一個或多個 %{provider} 專案無法直接匯入 GitLab,因為它們使用 Subversion 或 Mercurial 進行版本控制,而ä¸æ˜¯ Git。"
@@ -28207,14 +28536,11 @@ msgstr "僅 %{membersPageLinkStart} 專案æˆå“¡ %{membersPageLinkEnd} å¯ä»¥å­˜
msgid "Only active projects show up in the search and on the dashboard."
msgstr "僅啟用的專案顯示在æœå°‹å’Œå„€è¡¨æ¿ä¸Šã€‚"
-msgid "Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable."
-msgstr "å…許任何人在您打算被任何人使用的 GitLab 實例上註冊帳號。å…許任何人註冊會使 GitLab 實例更容易å—到攻擊。"
-
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr "僅在啟用é ç¨‹å„²å­˜æ™‚有效。設定為 0 表示沒有大å°é™åˆ¶ã€‚"
msgid "Only group members with at least the Reporter role can view or be notified about this epic"
-msgstr "åªæœ‰è‡³å°‘具有報告者角色的群組æˆå“¡æ‰èƒ½æŸ¥çœ‹æˆ–收到有關此å²è©©çš„通知"
+msgstr "åªæœ‰è‡³å°‘具有報告者角色的群組æˆå“¡æ‰èƒ½æŸ¥çœ‹æˆ–收到有關此å²è©© (epic) 的通知"
msgid "Only include features new to your current subscription tier."
msgstr "僅包括您當å‰è¨‚閱級別的新功能。"
@@ -28253,16 +28579,16 @@ msgid "Oops, are you sure?"
msgstr "您確定嗎?"
msgid "Open"
-msgstr "打開"
+msgstr "é–‹å•Ÿ"
msgid "Open Selection"
-msgstr "開啟所é¸é …"
+msgstr "開啟所é¸"
msgid "Open errors"
msgstr "開啟錯誤"
msgid "Open in Gitpod"
-msgstr "在 Gitpod 中打開"
+msgstr "在 Gitpod 中開啟"
msgid "Open in Web IDE"
msgstr "在 Web IDE 中打開"
@@ -28315,9 +28641,6 @@ msgstr "開啟一個新視窗"
msgid "Opens new window"
msgstr "開啟新視窗"
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr "æ“作失敗。請檢查 Pod 日誌 %{pod_name} 了解更多資訊。"
-
msgid "Operation not allowed"
msgstr "ä¸å…許該æ“作"
@@ -28603,6 +28926,9 @@ msgstr "Conan"
msgid "PackageRegistry|Conan Command"
msgstr "Conan 指令"
+msgid "PackageRegistry|Configure package forwarding and package file size limits."
+msgstr "設定軟體套件發é€å’Œæ–‡ä»¶å¤§å°é™åˆ¶ã€‚"
+
msgid "PackageRegistry|Copy .pypirc content"
msgstr "複製 .pypirc 內容"
@@ -28691,6 +29017,9 @@ msgstr "刪除軟體套件資產"
msgid "PackageRegistry|Delete package version"
msgstr "刪除軟體套件版本"
+msgid "PackageRegistry|Delete packages"
+msgstr "刪除軟體套件"
+
msgid "PackageRegistry|Delete selected"
msgstr "刪除é¸å–"
@@ -28706,6 +29035,12 @@ msgstr "刪除最後一個軟體套件資產將åŒæ™‚移除 %{name} çš„ %{versio
msgid "PackageRegistry|Duplicate packages"
msgstr "é‡è¤‡çš„套件"
+msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
+msgstr "å°æ‰€æœ‰å­ç¾¤çµ„執行 %{packageType} 設定"
+
+msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
+msgstr "å°æ‰€æœ‰å­ç¾¤çµ„執行 %{package_type} 設定"
+
msgid "PackageRegistry|Error publishing"
msgstr "錯誤發布"
@@ -28730,6 +29065,18 @@ msgstr "關於Nuget註冊表的更多訊æ¯ï¼Œ%{linkStart}請見文件%{linkEnd}
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr "關於PyPi註冊表的更多訊æ¯ï¼Œ%{linkStart}請見文件%{linkEnd}。"
+msgid "PackageRegistry|Forward %{packageType} package requests"
+msgstr "ç™¼é€ %{packageType} 軟體套件請求"
+
+msgid "PackageRegistry|Forward %{package_type} package requests"
+msgstr "ç™¼é€ %{package_type} 軟體套件請求"
+
+msgid "PackageRegistry|Forward package requests"
+msgstr "發é€è»Ÿé«”套件請求"
+
+msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
+msgstr "如果在 GitLab 軟體套件註冊庫中找ä¸åˆ°è©²å¥—件,則將軟體套件請求發é€åˆ°å…¬é–‹çš„註冊庫。"
+
msgid "PackageRegistry|Generic"
msgstr "一般"
@@ -28802,6 +29149,9 @@ msgstr "NuGet指令"
msgid "PackageRegistry|Number of duplicate assets to keep"
msgstr "è¦ä¿ç•™çš„é‡è¤‡è³‡ç”¢æ•¸é‡"
+msgid "PackageRegistry|Other versions"
+msgstr "其他版本"
+
msgid "PackageRegistry|Package Registry"
msgstr "軟體套件註冊表"
@@ -28817,6 +29167,9 @@ msgstr "æˆåŠŸåˆªé™¤è»Ÿé«”套件"
msgid "PackageRegistry|Package formats"
msgstr "軟體套件格å¼"
+msgid "PackageRegistry|Package forwarding"
+msgstr "軟體套件發é€"
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] "軟體套件有 %{updatesCount} 個存檔更新"
@@ -28824,6 +29177,9 @@ msgstr[0] "軟體套件有 %{updatesCount} 個存檔更新"
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr "軟體套件由分支%{branch}上的%{link}æ交所更新,由æµæ°´ç·š%{pipeline}構建並於%{datetime}發布到庫"
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr "æˆåŠŸåˆªé™¤è»Ÿé«”套件"
+
msgid "PackageRegistry|Permanently delete"
msgstr "永久刪除"
@@ -28878,6 +29234,9 @@ msgstr "顯示 PyPi 指令"
msgid "PackageRegistry|Show Yarn commands"
msgstr "顯示 Yarn 指令"
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr "刪除軟體套件時發生錯誤。"
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr "刪除軟體套件資產時發生錯誤。"
@@ -28926,9 +29285,6 @@ msgstr "這個Nuget套件沒有ä¾è³´å°ˆæ¡ˆã€‚"
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr "è¦æ“´å¤§æœå°‹ç¯„åœï¼Œè«‹æ›´æ”¹æˆ–刪除上é¢çš„éŽæ¿¾å™¨ã€‚"
-msgid "PackageRegistry|Type"
-msgstr "é¡žåž‹"
-
msgid "PackageRegistry|Unable to fetch package version information."
msgstr "無法å–得軟體套件版本訊æ¯ã€‚"
@@ -28948,6 +29304,10 @@ msgid "PackageRegistry|You are about to delete 1 asset. This operation is irreve
msgid_plural "PackageRegistry|You are about to delete %d assets. This operation is irreversible."
msgstr[0] "您å³å°‡åˆªé™¤ %d 項資產,該æ“作是ä¸å¯é€†çš„。"
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] "您å³å°‡åˆªé™¤ %d 個軟體套件,該æ“作為ä¸å¯é€†ã€‚"
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr "å³å°‡åˆªé™¤%{name}çš„%{version}版本。確定繼續嗎?"
@@ -28963,6 +29323,9 @@ msgstr "npm"
msgid "PackageRegistry|published by %{author}"
msgstr "由%{author}發布"
+msgid "Packages"
+msgstr "軟體套件"
+
msgid "Packages and registries"
msgstr "軟體套件與註冊表"
@@ -29032,14 +29395,17 @@ msgstr "åƒæ•¸"
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr "\"job_id\" åƒæ•¸çš„長度ä¸èƒ½è¶…éŽ %{job_id_max_size}"
+msgid "Parameters"
+msgstr "å‚æ•°"
+
msgid "Parent"
msgstr "上層"
msgid "Parent epic doesn't exist."
-msgstr "父å²è©©ä¸å­˜åœ¨ã€‚"
+msgstr "父å²è©© (epic) ä¸å­˜åœ¨ã€‚"
msgid "Parent epic is not present."
-msgstr "父å²è©©ä¸å­˜åœ¨ã€‚"
+msgstr "父å²è©© (epic) ä¸å­˜åœ¨ã€‚"
msgid "Parsing error for param :embed_json. %{message}"
msgstr "åƒæ•¸:embed_json解æžéŒ¯èª¤ã€‚%{message}"
@@ -29057,7 +29423,7 @@ msgid "Pass job variables"
msgstr "傳éžä½œæ¥­è®Šæ•¸"
msgid "Passed"
-msgstr "已通éŽ"
+msgstr "通éŽ"
msgid "Passed on"
msgstr "已通éŽæ–¼"
@@ -29095,6 +29461,15 @@ msgstr "請輸入您的密碼以確èª"
msgid "Passwords should be unique and not used for any other sites or services."
msgstr "密碼應唯一,並且未用於任何其他網站或æœå‹™ã€‚"
+msgid "Password|Not satisfied"
+msgstr "未符åˆè¦ç¯„"
+
+msgid "Password|Satisfied"
+msgstr "符åˆè¦ç¯„"
+
+msgid "Password|To be satisfied"
+msgstr "須符åˆè¦ç¯„"
+
msgid "Password|requires at least one lowercase letter"
msgstr "至少需è¦ä¸€å€‹å°å¯«å­—æ¯"
@@ -29117,13 +29492,13 @@ msgid "Paste a public key here. %{link_start}How do I generate it?%{link_end}"
msgstr "在這裡貼上一個公鑰。%{link_start}我如何生æˆå®ƒï¼Ÿ%{link_end}"
msgid "Paste confidential epic link"
-msgstr "貼上機密å²è©©é€£çµ"
+msgstr "貼上機密å²è©© (epic) 連çµ"
msgid "Paste confidential issue link"
msgstr "貼上機密議題連çµ"
msgid "Paste epic link"
-msgstr "貼上å²è©©é€£çµ"
+msgstr "貼上å²è©© (epic) 連çµ"
msgid "Paste issue link"
msgstr "貼上議題連çµ"
@@ -29300,7 +29675,7 @@ msgid "Permissions and group features"
msgstr "權é™å’Œç¾¤çµ„功能"
msgid "Personal Access Token"
-msgstr "個人存å–憑證"
+msgstr "個人存å–權æ–(令牌)"
msgid "Personal Access Token prefix"
msgstr "個人存å–令牌å‰ç¶´"
@@ -29338,6 +29713,30 @@ msgstr "Phabricator 任務"
msgid "Phone"
msgstr "電話"
+msgid "PhoneVerification|Enter a valid code."
+msgstr "輸入有效的驗證碼。"
+
+msgid "PhoneVerification|Something went wrong. Please try again."
+msgstr "發生錯誤,請å†è©¦ä¸€æ¬¡ã€‚"
+
+msgid "PhoneVerification|The code has expired. Request a new code and try again."
+msgstr "該驗證碼已éŽæœŸï¼Œè«‹é‡æ–°ç”³è«‹æ–°é©—證碼並é‡è©¦ã€‚"
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a different phone number and try again."
+msgstr "您輸入的電話號碼有å•é¡Œï¼Œè«‹è¼¸å…¥ä¸åŒçš„電話號碼並é‡è©¦ã€‚"
+
+msgid "PhoneVerification|There was a problem with the phone number you entered. Enter a valid phone number."
+msgstr "您輸入的電話號碼有å•é¡Œï¼Œè«‹è¼¸å…¥æœ‰æ•ˆçš„電話號碼。"
+
+msgid "PhoneVerification|Verification code can't be blank."
+msgstr "驗證碼ä¸èƒ½ç©ºç™½ã€‚"
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Request a new code and try again."
+msgstr "您已é”到最大嘗試次數,請申請新驗證碼,然後é‡è©¦ã€‚"
+
+msgid "PhoneVerification|You've reached the maximum number of tries. Wait %{interval} and try again."
+msgstr "您已é”到最大嘗試次數,請等待 %{interval} 後é‡è©¦ã€‚"
+
msgid "Pick a name"
msgstr "é¸æ“‡ä¸€å€‹å稱"
@@ -30017,7 +30416,7 @@ msgid "Pipeline|Branches or tags could not be loaded."
msgstr "無法載入分支或標籤。"
msgid "Pipeline|Canceled"
-msgstr "å·²å–消"
+msgstr "å–消"
msgid "Pipeline|Checking pipeline status"
msgstr "檢查æµæ°´ç·šç‹€æ…‹"
@@ -30059,7 +30458,7 @@ msgid "Pipeline|Merged result pipeline"
msgstr "åˆä½µçµæžœæµæ°´ç·š"
msgid "Pipeline|Passed"
-msgstr "已通éŽ"
+msgstr "通éŽ"
msgid "Pipeline|Pending"
msgstr "等待中"
@@ -30092,7 +30491,7 @@ msgid "Pipeline|Running"
msgstr "執行中"
msgid "Pipeline|Skipped"
-msgstr "已跳éŽ"
+msgstr "ç•¥éŽ"
msgid "Pipeline|Source"
msgstr "來æº"
@@ -30253,6 +30652,9 @@ msgstr "請檢查您的電å­éƒµä»¶(%{email})以確èªæ‚¨æ“有此電å­ä¿¡ç®±ä¸
msgid "Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}."
msgstr "請先點擊èªè­‰é›»å­éƒµä»¶ä¸­çš„連çµï¼Œä»¥ç¹¼çºŒæŽ¥ä¸‹ä¾†çš„步驟。連çµå·²è¢«å‚³é€è‡³ %{html_tag_strong_start}%{email}%{html_tag_strong_end}。"
+msgid "Please complete the incident creation form."
+msgstr "請填寫事故建立表格。"
+
msgid "Please complete your profile with email address"
msgstr "請在您的個人資料中填寫電å­éƒµä»¶åœ°å€"
@@ -30409,9 +30811,6 @@ msgstr "請嘗試é‡æ–°æ•´ç†é é¢ã€‚如果å•é¡Œä»ç„¶å­˜åœ¨ï¼Œè«‹è¯çµ¡æ”¯æ´
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr "請輸入%{phrase_code}以繼續或關閉此å°è©±æ¡†ä»¥å–消。"
-msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
-msgstr "請使用此表單å‘管ç†å“¡å ±å‘Šå»ºç«‹åžƒåœ¾è­°é¡Œã€ç•™è¨€æˆ–行為ä¸ç•¶çš„使用者。"
-
msgid "Please wait a few moments while we load the file history for this line."
msgstr "è«‹ç¨å€™ï¼Œæˆ‘們正在載入此行的檔案歷å²ç´€éŒ„。"
@@ -30565,11 +30964,14 @@ msgstr "é¸æ“‡æ‚¨å¸Œæœ›åœ¨å„€è¡¨æ¿ä¸Šé è¨­çœ‹åˆ°çš„內容。"
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "é¸æ“‡å°ˆæ¡ˆæ¦‚覽é é¢ä¸­æ‚¨æƒ³çœ‹åˆ°çš„內容。"
+msgid "Preferences|Choose which Web IDE version you want to use."
+msgstr "é¸æ“‡æ‚¨è¦ä½¿ç”¨çš„ Web IDE 版本。"
+
msgid "Preferences|Color for added lines"
-msgstr "為已加入行著色"
+msgstr "已加入行的é¡è‰²"
msgid "Preferences|Color for removed lines"
-msgstr "為已移除行著色"
+msgstr "已移除行的é¡è‰²"
msgid "Preferences|Color theme"
msgstr "佈景主題å好設定"
@@ -30611,7 +31013,7 @@ msgid "Preferences|Gitpod"
msgstr "Gitpod"
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
-msgstr "一次僅顯示一個文件,而ä¸æ˜¯æ‰€æœ‰æ›´æ”¹çš„文件。è¦åœ¨æ–‡ä»¶ä¹‹é–“切æ›ï¼Œè«‹ä½¿ç”¨æ–‡ä»¶ç€è¦½å™¨ã€‚"
+msgstr "一次僅顯示一個文件,而ä¸æ˜¯æ‰€æœ‰æ›´æ”¹çš„文件;è¦åœ¨æ–‡ä»¶ä¹‹é–“切æ›ï¼Œè«‹ä½¿ç”¨æ–‡ä»¶ç€è¦½å™¨ã€‚"
msgid "Preferences|Integrations"
msgstr "æ•´åˆ"
@@ -30649,21 +31051,30 @@ msgstr "語法醒目標示主題"
msgid "Preferences|Tab width"
msgstr "Tab 寬度"
+msgid "Preferences|The legacy Web IDE remains available while the new Web IDE is in Beta."
+msgstr "舊版 Web IDE ä»ç„¶å¯ç”¨ï¼Œè€Œæ–°ç‰ˆ Web IDE 處於 Beta 階段。"
+
msgid "Preferences|This feature is experimental and translations are not yet complete."
msgstr "此功能是實驗性的,翻譯尚未完æˆã€‚"
msgid "Preferences|This setting allows you to customize the appearance of the syntax."
-msgstr "此設定å…許您自訂基於語法的外觀。"
+msgstr "此設定å…許您自定義基於語法的外觀。"
msgid "Preferences|This setting allows you to customize the behavior of the system layout and default views."
-msgstr "此設定å…許您自訂系統佈局和é è¨­æª¢è¦–的呈ç¾ã€‚"
+msgstr "此設定å…許您自定義系統佈局和é è¨­æª¢è¦–的呈ç¾ã€‚"
msgid "Preferences|Time preferences"
msgstr "時å€è¨­å®š"
+msgid "Preferences|Use legacy Web IDE"
+msgstr "使用舊版 Web IDE"
+
msgid "Preferences|Use relative times"
msgstr "使用相å°æ™‚é–“"
+msgid "Preferences|Web IDE"
+msgstr "Web IDE"
+
msgid "Preferences|When you type in a description or comment box, pressing %{kbdOpen}Enter%{kbdClose} in a list adds a new item below."
msgstr "當您在æ述或留言框中輸入時,在列表中按 %{kbdOpen}Enter%{kbdClose} 會在下é¢å¢žåŠ ä¸€å€‹æ–°é …目。"
@@ -30781,18 +31192,27 @@ msgstr "%{name} 指令發生錯誤: %{message}。"
msgid "Proceed"
msgstr "繼續"
+msgid "Product Analytics|Onboarding view"
+msgstr "åˆå§‹è§€"
+
msgid "Product analytics"
msgstr "產å“分æž"
+msgid "ProductAnalytics|Add to Dashboard"
+msgstr "加入到儀表æ¿"
+
+msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
+msgstr "載入 %{widgetTitle} å°éƒ¨ä»¶æ™‚發生錯誤。"
+
msgid "ProductAnalytics|Audience"
msgstr "觀眾"
+msgid "ProductAnalytics|New Analytics Widget Title"
+msgstr "新的分æžå°éƒ¨ä»¶æ¨™é¡Œ"
+
msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already."
msgstr "此類型的圖表目å‰æ²’有資料。如果您尚未設定產å“分æžå·¥å…·ï¼Œè«‹æŸ¥çœ‹è¨­å®šæ¨™ç±¤ã€‚"
-msgid "ProductAnalytics|Widgets content"
-msgstr "å°çµ„件內容"
-
msgid "Productivity"
msgstr "生產率"
@@ -30914,7 +31334,7 @@ msgid "Profiles|Choose file..."
msgstr "é¸æ“‡æ–‡ä»¶..."
msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information."
-msgstr "é¸æ“‡åœ¨å…¬é–‹å€‹äººè³‡æ–™ä¸­é¡¯ç¤ºç§æœ‰å°ˆæ¡ˆçš„è²¢ç»ï¼Œä½†ä¸é¡¯ç¤ºä»»ä½•å°ˆæ¡ˆï¼Œç‰ˆæœ¬åº«æˆ–組織訊æ¯."
+msgstr "é¸æ“‡åœ¨å…¬é–‹å€‹äººè³‡æ–™ä¸­é¡¯ç¤ºç§æœ‰å°ˆæ¡ˆçš„è²¢ç»ï¼Œä½†ä¸é¡¯ç¤ºä»»ä½•å°ˆæ¡ˆã€ç‰ˆæœ¬åº«æˆ–組織訊æ¯ã€‚"
msgid "Profiles|City, country"
msgstr "城市,國家"
@@ -30971,13 +31391,13 @@ msgid "Profiles|Enter how your name is pronounced to help people address you cor
msgstr "輸入您姓å的發音以幫助人們正確稱呼您."
msgid "Profiles|Enter your name, so people you know can recognize you."
-msgstr "輸入您的姓å,以便大家èªè­˜æ‚¨."
+msgstr "輸入您的姓å,這樣大家就å¯ä»¥èªè­˜æ‚¨."
msgid "Profiles|Enter your password to confirm the email change"
msgstr "輸入您的密碼以確èªé›»å­éƒµä»¶æ›´æ”¹"
msgid "Profiles|Enter your pronouns to let people know how to refer to you."
-msgstr "輸入您的代稱,讓人們知é“如何稱呼您."
+msgstr "輸入您的稱謂,讓人們知é“如何稱呼您."
msgid "Profiles|Example: MacBook key"
msgstr "例如:MacBook Key"
@@ -30992,7 +31412,7 @@ msgid "Profiles|Expires:"
msgstr "éŽæœŸï¼š"
msgid "Profiles|Feed token was successfully reset"
-msgstr "訊æ¯å‹•æ…‹ä»¤ç‰Œ(權æ–)å·²æˆåŠŸé‡è¨­"
+msgstr "訊æ¯å‹• Feed 權æ–(令牌)å·²æˆåŠŸé‡è¨­"
msgid "Profiles|Full name"
msgstr "å…¨å"
@@ -31055,7 +31475,7 @@ msgid "Profiles|Notification email"
msgstr "通知郵件"
msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
-msgstr "å¯é¸çš„,但推薦。 如果設置,密鑰將在指定日期失效。"
+msgstr "éžå¿…è¦ï¼Œä½†å»ºè­°è¨­å®šï¼› 如果設定了,則金鑰將在指定日期失效。"
msgid "Profiles|Organization"
msgstr "組織"
@@ -31076,7 +31496,7 @@ msgid "Profiles|Profile was successfully updated"
msgstr "個人資料已æˆåŠŸæ›´æ–°"
msgid "Profiles|Pronouns"
-msgstr "稱呼"
+msgstr "稱謂"
msgid "Profiles|Pronunciation"
msgstr "讀音"
@@ -31153,6 +31573,12 @@ msgstr "更新使用者å稱"
msgid "Profiles|Upload new avatar"
msgstr "上傳新頭åƒ"
+msgid "Profiles|Usage type"
+msgstr "使用類型"
+
+msgid "Profiles|Usage type:"
+msgstr "使用類型:"
+
msgid "Profiles|Use a private email - %{email}"
msgstr "使用ç§äººé›»å­éƒµä»¶ - %{email}"
@@ -31687,12 +32113,18 @@ msgstr "必須解決所有線程"
msgid "ProjectSettings|Allow"
msgstr "å…許"
+msgid "ProjectSettings|Allow anyone to pull from Package Registry"
+msgstr "å…許任何人從軟體套件註冊庫中拉å–"
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down award emoji buttons on issues, merge requests, and snippets."
msgstr "始終在議題ã€åˆä½µè«‹æ±‚和程å¼ç¢¼ç‰‡æ®µä¸Šé¡¯ç¤ºè®šåŒå’Œä¸è®šåŒè¡¨æƒ…符號按鈕。"
msgid "ProjectSettings|Analytics"
msgstr "分æž"
+msgid "ProjectSettings|Anyone can pull packages with a package manager API."
+msgstr "任何人都å¯ä»¥ä½¿ç”¨è»Ÿé«”å¥—ä»¶ç®¡ç† API 拉å–套件。"
+
msgid "ProjectSettings|Auto-close referenced issues on default branch"
msgstr "自動關閉é è¨­åˆ†æ”¯ä¸Šæ‰€åƒç…§çš„議題。"
@@ -31774,9 +32206,6 @@ msgstr "æ¯æ¬¡åˆä½µéƒ½æœƒå»ºç«‹ä¸€å€‹åˆä½µæ交。"
msgid "ProjectSettings|Every project can have its own space to store its Docker images"
msgstr "æ¯å€‹å°ˆæ¡ˆéƒ½å¯ä»¥æœ‰è‡ªå·±çš„空間來儲存其Docker映åƒ"
-msgid "ProjectSettings|Every project can have its own space to store its packages."
-msgstr "æ¯å€‹å°ˆæ¡ˆéƒ½å¯ä»¥æœ‰è‡ªå·±çš„空間來存儲其軟體套件。"
-
msgid "ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public."
msgstr "æ¯å€‹å°ˆæ¡ˆéƒ½å¯ä»¥æœ‰è‡ªå·±çš„空間來儲存它的軟體套件。注æ„:當一個專案是公開時,軟體套件庫總是å¯è¦‹çš„。"
@@ -31802,7 +32231,7 @@ msgid "ProjectSettings|Fast-forward merges only."
msgstr "僅快進å¼ï¼ˆFast-forward)åˆä½µã€‚"
msgid "ProjectSettings|Feature flags"
-msgstr "功能標誌"
+msgstr "特性標籤"
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr "用於在此專案中å”作開發想法和計劃工作的éˆæ´»å·¥å…·ã€‚"
@@ -31891,6 +32320,9 @@ msgstr "åªæœ‰ç•¶ä¾†æºåˆ†æ”¯èˆ‡å…¶ç›®æ¨™æ˜¯æœ€æ–°çš„時候,æ‰å…許åˆä½µã€‚
msgid "ProjectSettings|Monitor"
msgstr "監控"
+msgid "ProjectSettings|Monitor the health of your project and respond to incidents."
+msgstr "監控您專案的é‹è¡Œç‹€æ³ä¸¦å°äº‹æ•…åšå‡ºå›žæ‡‰ã€‚"
+
msgid "ProjectSettings|No merge commits are created."
msgstr "沒有建立åˆä½µæ交。"
@@ -31936,6 +32368,9 @@ msgstr "專案å¯è¦‹æ€§"
msgid "ProjectSettings|Public"
msgstr "公開的"
+msgid "ProjectSettings|Publish, store, and view packages in a project."
+msgstr "在專案內發布ã€å„²å­˜å’ŒæŸ¥çœ‹è»Ÿé«”套件。"
+
msgid "ProjectSettings|Releases"
msgstr "發布"
@@ -31958,7 +32393,7 @@ msgid "ProjectSettings|Requirements management system."
msgstr "需求管ç†ç³»çµ±ã€‚"
msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
-msgstr "無需使用功能標誌é‡æ–°éƒ¨ç½²å³å¯æŽ¨å‡ºæ–°åŠŸèƒ½ã€‚"
+msgstr "無需使用特性標籤é‡æ–°éƒ¨ç½²å³å¯æŽ¨å‡ºæ–°åŠŸèƒ½ã€‚"
msgid "ProjectSettings|Search for topic"
msgstr "æœå°‹ä¸»é¡Œ"
@@ -32065,8 +32500,8 @@ msgstr "使用者å¯ä»¥è«‹æ±‚å­˜å–"
msgid "ProjectSettings|View and edit files in this project."
msgstr "查看和編輯此專案中的文件。"
-msgid "ProjectSettings|View and edit files in this project. Non-project members have only read access."
-msgstr "查看和編輯此專案中的文件。éžå°ˆæ¡ˆæˆå“¡åªæœ‰è®€å–權é™ã€‚"
+msgid "ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access."
+msgstr "查看和編輯該專案的文件,當設定為 **Everyone With Access** 時,éžå°ˆæ¡ˆæˆå“¡åªæœ‰å”¯è®€æ¬Šé™ã€‚"
msgid "ProjectSettings|View project analytics."
msgstr "查看專案分æžã€‚"
@@ -32146,6 +32581,9 @@ msgstr "Netrify/Plain HTML"
msgid "ProjectTemplates|NodeJS Express"
msgstr "NodeJS Express"
+msgid "ProjectTemplates|Pages/Bridgetown"
+msgstr "Pages/Bridgetown"
+
msgid "ProjectTemplates|Pages/Gatsby"
msgstr "Pages/Gatsby"
@@ -32471,13 +32909,13 @@ msgid "Promote"
msgstr "æå‡"
msgid "Promote issue to an epic"
-msgstr "將議題æå‡ç‚ºå²è©©"
+msgstr "將議題æå‡ç‚ºå²è©© (epic) "
msgid "Promote issue to incident"
msgstr "將議題æå‡ç‚ºäº‹ä»¶"
msgid "Promote to epic"
-msgstr "æå‡åˆ°å²è©©"
+msgstr "æå‡åˆ°å²è©© (epic) "
msgid "Promote to group label"
msgstr "å‡ç´šåˆ°ç¾¤çµ„標記"
@@ -32492,7 +32930,7 @@ msgid "PromoteMilestone|Promotion failed - %{message}"
msgstr "å‡ç´šå¤±æ•— - %{message}"
msgid "Promoted issue to an epic."
-msgstr "將議題å‡ç´šç‚ºå²è©©."
+msgstr "將議題å‡ç´šç‚ºå²è©© (epic) 。"
msgid "Promotes issue to incident"
msgstr "將議題æå‡ç‚ºäº‹ä»¶"
@@ -32546,7 +32984,7 @@ msgid "Promotions|Don't show me this again"
msgstr "ä¸å†é¡¯ç¤º"
msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
-msgstr "Epic 讓您å¯ä»¥é€šéŽè¿½è¹¤è·¨å°ˆæ¡ˆå’Œé‡Œç¨‹ç¢‘共享主題的å•é¡Œçµ„,更有效地管ç†å°ˆæ¡ˆçµ„åˆã€‚"
+msgstr "å²è©© (Epic) 讓您å¯ä»¥é€šéŽè¿½è¹¤è·¨å°ˆæ¡ˆå’Œé‡Œç¨‹ç¢‘共享主題的å•é¡Œçµ„,更有效地管ç†å°ˆæ¡ˆçµ„åˆã€‚"
msgid "Promotions|Improve issues management with Issue weight and GitLab Enterprise Edition."
msgstr "使用GitLabä¼æ¥­ç‰ˆä¸­è­°é¡Œæ¬Šé‡å¸¶ä¾†çš„增強議題管ç†åŠŸèƒ½ã€‚"
@@ -32668,9 +33106,6 @@ msgstr "å—ä¿è­·çš„分支"
msgid "Protected Branches"
msgstr "å—ä¿è­·çš„分支"
-msgid "Protected Environment"
-msgstr "å—ä¿è­·çš„環境"
-
msgid "Protected Paths: requests"
msgstr "å—ä¿è­·çš„路徑: 請求"
@@ -33341,6 +33776,9 @@ msgid "Refreshing in a second to show the updated status..."
msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] "%d 秒後é‡æ–°æ•´ç†ä»¥é¡¯ç¤ºæ›´æ–°ç‹€æ…‹..."
+msgid "Refreshing..."
+msgstr "é‡æ–°æ•´ç†ä¸­â€¦"
+
msgid "Regenerate export"
msgstr "é‡æ–°åŒ¯å‡º"
@@ -33468,7 +33906,7 @@ msgid "Relate to %{issuable_type} %{add_related_issue_link}"
msgstr "é—œè¯åˆ° %{issuable_type} %{add_related_issue_link}"
msgid "Related feature flags"
-msgstr "相關的功能標誌"
+msgstr "相關的特性標籤"
msgid "Related issues"
msgstr "相關議題"
@@ -33484,7 +33922,7 @@ msgid_plural "Releases"
msgstr[0] "發行"
msgid "Release %{deletedRelease} has been successfully deleted."
-msgstr "發佈版本 %{deletedRelease} å·²æˆåŠŸåˆªé™¤ã€‚"
+msgstr "%{deletedRelease} 版本已æˆåŠŸåˆªé™¤ã€‚"
msgid "Release already exists"
msgstr "版本已存在"
@@ -33537,11 +33975,14 @@ msgstr "Runbook"
msgid "ReleaseAssetLinkType|Runbooks"
msgstr "Runbook"
+msgid "Released"
+msgstr "發布版本"
+
msgid "Released date"
msgstr "發布日期"
msgid "Releases"
-msgstr "發佈版本"
+msgstr "發布"
msgid "Releases are based on Git tags and mark specific points in a project's development history. They can contain information about the type of changes and can also deliver binaries, like compiled versions of your software."
msgstr "發布基於Git標籤,並標記專案開發歷å²ä¸­çš„特定點。它們å¯ä»¥åŒ…å«æœ‰é—œæ›´æ”¹é¡žåž‹çš„訊æ¯ï¼Œé‚„å¯ä»¥æ供二進制文件,例如軟體的編譯版本。"
@@ -33565,7 +34006,7 @@ msgid "Release|Something went wrong while creating a new release."
msgstr "建立新發布時發生錯誤。"
msgid "Release|Something went wrong while deleting the release."
-msgstr "刪除發佈版本時發生錯誤。"
+msgstr "刪除發布版本時發生錯誤。"
msgid "Release|Something went wrong while getting the release details."
msgstr "å–得發布詳情時出ç¾å•é¡Œã€‚"
@@ -33637,7 +34078,7 @@ msgid "Remove card"
msgstr "刪除å¡ç‰‡"
msgid "Remove child epic from an epic"
-msgstr "從å²è©©ä¸­åˆªé™¤å­å²è©©"
+msgstr "從å²è©©ä¸­åˆªé™¤å­å²è©© (epic) "
msgid "Remove customer relation contact(s)."
msgstr "移除客戶關係è¯çµ¡äºº(s)。"
@@ -33667,7 +34108,7 @@ msgid "Remove from batch"
msgstr "從批次中移除"
msgid "Remove from epic"
-msgstr "從å²è©©ä¸­ç§»é™¤"
+msgstr "從å²è©© (epic) 中移除"
msgid "Remove group"
msgstr "移除群組"
@@ -33703,7 +34144,7 @@ msgid "Remove milestone"
msgstr "移除里程碑"
msgid "Remove parent epic from an epic"
-msgstr "從å²è©©ä¸­ç§»é™¤çˆ¶å²è©©"
+msgstr "從å²è©©ä¸­ç§»é™¤çˆ¶å²è©©å²è©© (epic) "
msgid "Remove priority"
msgstr "移除優先級別"
@@ -33751,7 +34192,7 @@ msgid "Removed %{assignee_text} %{assignee_references}."
msgstr "已移除%{assignee_text}%{assignee_references}。"
msgid "Removed %{epic_ref} from child epics."
-msgstr "已從å­å²è©©ä¸­ç§»é™¤%{epic_ref}。"
+msgstr "已從å­å²è©© (epics) 中移除%{epic_ref}。"
msgid "Removed %{iteration_reference} iteration."
msgstr "移除了迭代%{iteration_reference}。"
@@ -33769,13 +34210,13 @@ msgid "Removed all labels."
msgstr "已移除所有標籤。"
msgid "Removed an issue from an epic."
-msgstr "從å²è©©ä¸­ç§»é™¤äº†ä¸€å€‹è­°é¡Œã€‚"
+msgstr "從å²è©© (epic) 中移除了一個議題。"
msgid "Removed group can not be restored!"
msgstr "已移除的群組無法復原ï¼"
msgid "Removed parent epic %{epic_ref}."
-msgstr "已移除父å²è©©%{epic_ref}。"
+msgstr "已移除父å²è©© (epic) %{epic_ref}。"
msgid "Removed spent time."
msgstr "已移除消耗時間."
@@ -33799,7 +34240,7 @@ msgid "Removes %{assignee_text} %{assignee_references}."
msgstr "移除%{assignee_text} %{assignee_references}。"
msgid "Removes %{epic_ref} from child epics."
-msgstr "從å­å²è©©ä¸­ç§»é™¤%{epic_ref}。"
+msgstr "從å­å²è©© (epic) 中移除%{epic_ref}。"
msgid "Removes %{iteration_reference} iteration."
msgstr "移除迭代%{iteration_reference}。"
@@ -33817,10 +34258,10 @@ msgid "Removes all labels."
msgstr "移除所有標記。"
msgid "Removes an issue from an epic."
-msgstr "從å²è©©ä¸­ç§»é™¤ä¸€å€‹è­°é¡Œã€‚"
+msgstr "從å²è©© (epic) 中移除一個議題。"
msgid "Removes parent epic %{epic_ref}."
-msgstr "移除父å²è©©%{epic_ref}。"
+msgstr "移除父å²è©© (epic) %{epic_ref}。"
msgid "Removes spent time."
msgstr "移除消耗時間."
@@ -33862,7 +34303,7 @@ msgid "Reopen %{noteable}"
msgstr "é‡æ–°é–‹å•Ÿ %{noteable}"
msgid "Reopen epic"
-msgstr "é‡æ–°é–‹å•Ÿå²è©©"
+msgstr "é‡æ–°é–‹å•Ÿå²è©© (epic) "
msgid "Reopen milestone"
msgstr "é‡æ–°é–‹å•Ÿé‡Œç¨‹ç¢‘"
@@ -33876,6 +34317,9 @@ msgstr "é‡æ–°é–‹å•Ÿ%{quick_action_target}"
msgid "Reopened this %{quick_action_target}."
msgstr "é‡æ–°é–‹å•Ÿ%{quick_action_target}。"
+msgid "Reopening..."
+msgstr "é‡æ–°é–‹å•Ÿ..."
+
msgid "Reopens this %{quick_action_target}."
msgstr "é‡æ–°é–‹å•Ÿ%{quick_action_target}。"
@@ -33930,11 +34374,8 @@ msgstr "回復…"
msgid "Report Finding not found"
msgstr "未發ç¾æœå°‹å ±å‘Š"
-msgid "Report abuse"
-msgstr "報告濫用"
-
-msgid "Report abuse to admin"
-msgstr "å‘管ç†å“¡å ±å‘Šæ¿«ç”¨è¡Œç‚º"
+msgid "Report abuse to administrator"
+msgstr "å‘管ç†è€…æ報濫用行為"
msgid "Report couldn't be prepared."
msgstr "無法準備報告。"
@@ -34266,6 +34707,9 @@ msgstr "請求"
msgid "Request Access"
msgstr "申請權é™"
+msgid "Request Headers"
+msgstr "請求標頭"
+
msgid "Request a new one"
msgstr "請求一個新的"
@@ -34511,6 +34955,9 @@ msgstr "é™åˆ¶ç”¨æ–¼é€™å€‹åŸ·è¡Œå™¨çš„專案"
msgid "Restricted shift times are not available for hourly shifts"
msgstr "é™åˆ¶è¼ªç­æ™‚é–“ä¸é©ç”¨æ–¼æ¯å°æ™‚輪ç­"
+msgid "Results limit reached"
+msgstr "é”到çµæžœé™åˆ¶"
+
msgid "Resume"
msgstr "æ¢å¾©"
@@ -34584,8 +35031,8 @@ msgstr "在æ交å‰å¯©æŸ¥ç›®æ¨™å°ˆæ¡ˆï¼Œä»¥é¿å…暴露 %{source} 的更改。"
msgid "Review time"
msgstr "檢視時間"
-msgid "Review time is defined as the time it takes from first comment until merged."
-msgstr "檢視時間的定義是自第一次留言到åˆä½µæ‰€èŠ±çš„時間。"
+msgid "Review time is the amount of time since the first comment in a merge request."
+msgstr "審查時間是自第一次å°åˆä½µè«‹æ±‚發表評論以來的時間。"
msgid "ReviewApp|Enable Review App"
msgstr "啟用審閱應用程å¼"
@@ -34678,6 +35125,9 @@ msgstr "執行例行維護"
msgid "Run housekeeping tasks to automatically optimize Git repositories. Disabling this option will cause performance to degenerate over time."
msgstr "執行管ç†ä»»å‹™ä¾†è‡ªå‹•å„ªåŒ– Git 版本庫,åœç”¨è©²é¸é …將導致性能隨著時間的推移而下é™ã€‚"
+msgid "Run manual job again"
+msgstr "å†æ¬¡åŸ·è¡Œæ‰‹å‹•ä½œæ¥­"
+
msgid "Run manual or delayed jobs"
msgstr "執行手動或延é²çš„作業"
@@ -34735,8 +35185,8 @@ msgstr "1的容é‡å¯ä»¥é€šéŽè‡ªå‹•ç¸®æ”¾ç¾¤çµ„é‡æ–°ç”¢ç”Ÿ warm HA。容é‡ç‚º
msgid "Runners|A new version is available"
msgstr "有新版本å¯ç”¨ï¼"
-msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
-msgstr "定期後å°ä»»å‹™æœƒåˆªé™¤è¶…éŽ %{elapsedTime} 未è¯ç¹« GitLab 的執行器。 %{linkStart}我å¯ä»¥æŸ¥çœ‹åˆªé™¤äº†å¤šå°‘執行器嗎?%{linkEnd}"
+msgid "Runners|A periodic background task deletes runners that haven't contacted GitLab in more than %{elapsedTime}. Only runners registered in this group are deleted. Runners in subgroups and projects are not. %{linkStart}Can I view how many runners were deleted?%{linkEnd}"
+msgstr "定期的後å°ä»»å‹™æœƒåˆªé™¤æœªè¯ç¹« GitLab è¶…éŽ %{elapsedTime} 的執行器 (Runner),åªæœ‰è¨»å†Šåˆ°è©²ç¾¤çµ„的執行器被刪除,ä¸å«å­ç¾¤çµ„和專案中執行器。 %{linkStart}我å¯ä»¥çœ‹åˆ°æœ‰å¤šå°‘執行器被刪除了嗎?%{linkEnd}"
msgid "Runners|Active"
msgstr "啟用"
@@ -34762,6 +35212,9 @@ msgstr "Amazon Linux 2 Docker HA 具有手動縮放和å¯é¸çš„排程。Non-spot
msgid "Runners|An error has occurred fetching instructions"
msgstr "å–得指令時發生錯誤"
+msgid "Runners|An error occurred while deleting. Some runners may not have been deleted."
+msgstr "刪除éŽç¨‹ç™¼ç”ŸéŒ¯èª¤ã€‚一些執行器å¯èƒ½æ²’有被刪除。"
+
msgid "Runners|An upgrade is available for this runner"
msgstr "該執行器有å¯ç”¨çš„æ›´æ–°"
@@ -34783,6 +35236,9 @@ msgstr "與一個或多個專案有關è¯"
msgid "Runners|Available"
msgstr "å¯ç”¨æ€§"
+msgid "Runners|Available shared runners: %{count}"
+msgstr "å¯ç”¨çš„共享執行器:%{count}"
+
msgid "Runners|Available to all projects"
msgstr "é©ç”¨æ–¼æ‰€æœ‰å°ˆæ¡ˆ"
@@ -34865,6 +35321,9 @@ msgstr "輸入秒數。此逾時優先於為專案設定的較低逾時。"
msgid "Runners|Executor"
msgstr "執行者"
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr "éŽæ¿¾å°ˆæ¡ˆ"
@@ -34883,6 +35342,9 @@ msgstr "如何å‡ç´š GitLab 執行器?"
msgid "Runners|IP Address"
msgstr "IP ä½å€"
+msgid "Runners|Idle"
+msgstr "é–’ç½®"
+
msgid "Runners|Install a runner"
msgstr "安è£åŸ·è¡Œå™¨"
@@ -34922,12 +35384,15 @@ msgstr "從未連接éŽ:"
msgid "Runners|Never expires"
msgstr "無期é™"
-msgid "Runners|New group runners view"
-msgstr "新的群組執行器視圖"
+msgid "Runners|New group runners can be registered"
+msgstr "å¯ä»¥è¨»å†Šæ–°çš„群組執行器(Runner)"
msgid "Runners|New registration token generated!"
msgstr "已產生新的註冊令牌(權æ–)ï¼"
+msgid "Runners|No description"
+msgstr "沒有æè¿°"
+
msgid "Runners|No results found"
msgstr "未找到çµæžœ"
@@ -35019,6 +35484,9 @@ msgstr "執行器 #%{runner_id}"
msgid "Runners|Runner %{name} was deleted"
msgstr "%{name} 執行器已刪除"
+msgid "Runners|Runner Registration"
+msgstr "註冊執行器(Runner)"
+
msgid "Runners|Runner assigned to project."
msgstr "指派給專案的執行器。"
@@ -35085,6 +35553,9 @@ msgstr "執行器是é‹è¡Œ CI/CD 作業的代ç†ã€‚按照%{linkStart}安è£å’Œè¨
msgid "Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator."
msgstr "執行器(runner)是執行您 CI/CD 作業的代ç†ï¼Œè¦è¨»å†Šæ–°çš„執行器(runner),請è¯ç¹«æ‚¨çš„管ç†å“¡ã€‚"
+msgid "Runners|Running"
+msgstr "執行中"
+
msgid "Runners|Runs untagged jobs"
msgstr "執行未被標籤的作業"
@@ -35133,12 +35604,6 @@ msgstr "標籤"
msgid "Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run."
msgstr "標籤å¯ä»¥æŽ§åˆ¶åŸ·è¡Œå™¨è™•ç†çš„作業類型, 通éŽæ¨™ç±¤åŸ·è¡Œå™¨ï¼Œæ‚¨å¯ä»¥ç¢ºä¿å…±äº«åŸ·è¡Œå™¨åƒ…處ç†å®ƒå€‘è£é…執行的作業。"
-msgid "Runners|Take me there!"
-msgstr "帶我到那裡ï¼"
-
-msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
-msgstr "新檢視表為您在執行器佇列æ供更多空間和更好的å¯è¦‹åº¦ã€‚"
-
msgid "Runners|The project, group or instance where the runner was registered. Instance runners are always owned by Administrator."
msgstr "執行器(runner)所註冊的專案ã€ç¾¤çµ„或實例。實例執行器(runner)始終歸管ç†å“¡æ‰€æœ‰ã€‚"
@@ -35251,6 +35716,9 @@ msgstr "特定"
msgid "Runner|Owner"
msgstr "所有者"
+msgid "Runner|Runner %{runnerName} failed to delete"
+msgstr "執行器 %{runnerName} 刪除失敗"
+
msgid "Running"
msgstr "執行中"
@@ -35278,14 +35746,17 @@ msgstr "SAML 單一登入"
msgid "SAML single sign-on for %{group_name}"
msgstr "%{group_name} 的 SAML 單一登入"
+msgid "SAML|Sign in to %{groupName}"
+msgstr "登入 %{groupName}"
+
msgid "SAML|Sign in to GitLab to connect your organization's account"
msgstr "登入 GitLab 以連接您組織的帳號"
msgid "SAML|The %{strongOpen}%{group_path}%{strongClose} group allows you to sign in using single sign-on."
msgstr "%{strongOpen}%{group_path}%{strongClose} 群組å…許您使用單點登入進行登入。"
-msgid "SAML|To access %{strongOpen}%{group_name}%{strongClose}, you must sign in using single sign-on through an external sign-in page."
-msgstr "è¦å­˜å– %{strongOpen}%{group_name}%{strongClose},您必須從外部登入é é¢ä½¿ç”¨å–®é»žç™»å…¥é€²è¡Œç™»å…¥ã€‚"
+msgid "SAML|To access %{groupName}, you must sign in using single sign-on through an external sign-in page."
+msgstr "è¦å­˜å– %{groupName},您必須通éŽå¤–部的登入é é¢ä½¿ç”¨å–®é»žç™»å…¥é€²è¡Œç™»å…¥ã€‚"
msgid "SAML|To allow %{strongOpen}%{group_name}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}."
msgstr "è¦åœ¨æ‚¨ä½¿ç”¨å–®é»žç™»å…¥æˆåŠŸç™»å…¥å¾Œå…許 %{strongOpen}%{group_name}%{strongClose} 管ç†æ‚¨çš„ GitLab %{strongOpen}%{username}%{strongClose} 帳號 (%{email}) ,請é¸æ“‡ %{strongOpen}授權 %{strongClose}。"
@@ -35326,6 +35797,9 @@ msgstr "SSH主機金鑰在此系統上ä¸å¯ç”¨ã€‚請使用%{ssh_keyscan}指令æ
msgid "SSH key"
msgstr "SSH金鑰"
+msgid "SSH key fingerprint:"
+msgstr "SSH 密鑰指紋:"
+
msgid "SSH keys"
msgstr "SSH 金鑰"
@@ -35341,6 +35815,15 @@ msgstr "具有以下指紋的 SSH 金鑰已éŽæœŸï¼Œç„¡æ³•å†ä½¿ç”¨ï¼š"
msgid "SSH public key"
msgstr "SSH公鑰"
+msgid "SSHKey|Authentication"
+msgstr "身份驗證"
+
+msgid "SSHKey|Authentication & Signing"
+msgstr "é©—è­‰ & ç°½å"
+
+msgid "SSHKey|Signing"
+msgstr "ç°½å"
+
msgid "SSL Verification:"
msgstr "SSL驗證:"
@@ -35366,7 +35849,7 @@ msgid "Save Changes"
msgstr "儲存變更"
msgid "Save application"
-msgstr "儲存應用"
+msgstr "儲存應用程å¼"
msgid "Save changes"
msgstr "儲存變更"
@@ -35407,6 +35890,12 @@ msgstr "%{thenLabelStart}然後%{thenLabelEnd} éœ€è¦ %{scan} 掃ææ‰èƒ½é‹è¡Œ
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr "%{thenLabelStart}Then%{thenLabelEnd} è¦æ±‚使用 %{siteProfile} 站點é…置和 %{scannerProfile} 掃æ器é…置執行 %{scan} 掃æ"
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgstr "%{thenLabelStart}下一步%{thenLabelEnd} 需è¦ä½¿ç”¨ç«™é»žè¨­å®šæ–‡ä»¶ %{siteProfile} 和帶有標籤 %{tags} 的掃æ設定文件 %{scannerProfile} 執行 %{scan} 掃æ"
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgstr "%{thenLabelStart}下一步%{thenLabelEnd} 需è¦ä½¿ç”¨æ¨™ç±¤ %{tags} 執行 %{scan} 掃æ"
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr "æµæ°´ç·šåŸ·è¡Œä¸­"
@@ -35434,9 +35923,15 @@ msgstr "é¸æ“‡æŽƒæ器é…ç½®"
msgid "ScanExecutionPolicy|Select site profile"
msgstr "é¸æ“‡ç«™é»žé…ç½®"
+msgid "ScanExecutionPolicy|Select tags (if any)"
+msgstr "é¸æ“‡æ¨™ç±¤ (如果有)"
+
msgid "ScanExecutionPolicy|Site profile"
msgstr "站點é…ç½®"
+msgid "ScanExecutionPolicy|Tags"
+msgstr "標籤"
+
msgid "ScanExecutionPolicy|agent"
msgstr "代ç†"
@@ -35449,18 +35944,27 @@ msgstr "在命å空間中"
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr "%{ifLabelStart}如果%{ifLabelEnd} %{scanners} 在é‡å° %{branches} 的開放åˆä½µè«‹æ±‚中發ç¾è¶…éŽ %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} çš„æ¼æ´ž"
+msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+msgstr "%{ifLabelStart}if%{ifLabelEnd} %{selector}"
+
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr "%{thenLabelStart}然後%{thenLabelEnd} 需è¦ä»¥ä¸‹ %{approvalsRequired} 個核准人的核准:"
msgid "ScanResultPolicy|add an approver"
msgstr "加入核准者"
+msgid "ScanResultPolicy|from %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
+msgstr "在é‡å° %{branches} 的開放åˆä½µè«‹æ±‚中 %{scanners} æ‰¾åˆ°è¶…éŽ %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} 個æ¼æ´ž"
+
msgid "ScanResultPolicy|scanners"
msgstr "掃æ器"
msgid "ScanResultPolicy|severity levels"
msgstr "åš´é‡ç¨‹åº¦"
+msgid "ScanResultPolicy|vulnerabilities allowed"
+msgstr "å…許的æ¼æ´ž"
+
msgid "ScanResultPolicy|vulnerability states"
msgstr "æ¼æ´žç‹€æ…‹"
@@ -35545,6 +36049,9 @@ msgstr "æœå°‹"
msgid "Search GitLab"
msgstr "æœå°‹ GitLab"
+msgid "Search Within"
+msgstr "æœç´¢ç¯„åœ"
+
msgid "Search a group"
msgstr "æœå°‹ç¾¤çµ„"
@@ -35727,7 +36234,7 @@ msgstr[0] "æ交"
msgid "SearchResults|epic"
msgid_plural "SearchResults|epics"
-msgstr[0] "å²è©©"
+msgstr[0] "å²è©© (epics)"
msgid "SearchResults|issue"
msgid_plural "SearchResults|issues"
@@ -36051,6 +36558,9 @@ msgstr "您確定è¦åˆªé™¤æ­¤æ”¿ç­–嗎? æ­¤æ“作無法撤消。"
msgid "SecurityOrchestration|Choose a project"
msgstr "é¸æ“‡å°ˆæ¡ˆ"
+msgid "SecurityOrchestration|Choose approver type"
+msgstr "é¸æ“‡å¯©æ ¸è€…é¡žåž‹"
+
msgid "SecurityOrchestration|Create more robust vulnerability rules and apply them to all your projects."
msgstr "建立更å¥å…¨çš„æ¼æ´žè¦å‰‡ä¸¦å°‡å…¶å¥—用於您的所有專案。"
@@ -36108,9 +36618,18 @@ msgstr "無法載入圖åƒã€‚"
msgid "SecurityOrchestration|Failed to load vulnerability scanners."
msgstr "æ¼æ´žæŽƒæ程å¼è¼‰å…¥å¤±æ•—。"
+msgid "SecurityOrchestration|For large groups, there may be a significant delay in applying policy changes to pre-existing merge requests. Policy changes typically apply almost immediately for newly created merge requests."
+msgstr "å°æ–¼å¤§åž‹ç¾¤çµ„,將政策更改套用於é å…ˆå­˜åœ¨çš„åˆä½µè«‹æ±‚å¯èƒ½æœƒæœ‰æ˜Žé¡¯çš„延é²ï¼›ç­–略更改通常幾乎立å³é©ç”¨æ–¼æ–°å»ºç«‹çš„åˆä½µè«‹æ±‚。"
+
+msgid "SecurityOrchestration|Groups"
+msgstr "群組"
+
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr "如果任何掃æ器在é‡å°ä¸»åˆ†æ”¯çš„開放åˆä½µè«‹æ±‚中發ç¾æ–°æª¢æ¸¬çš„åš´é‡æ¼æ´žï¼Œå‰‡éœ€è¦å–得應用程å¼å®‰å…¨çš„任何æˆå“¡çš„兩次核准。"
+msgid "SecurityOrchestration|Individual users"
+msgstr "個人使用者"
+
msgid "SecurityOrchestration|Inherited"
msgstr "繼承"
@@ -36126,6 +36645,9 @@ msgstr "無效的政策類型"
msgid "SecurityOrchestration|Latest scan run against %{agent}"
msgstr "é‡å° %{agent} 執行的最新掃æ"
+msgid "SecurityOrchestration|License Scan"
+msgstr "許å¯è­‰æŽƒæ"
+
msgid "SecurityOrchestration|New policy"
msgstr "新政策"
@@ -36162,6 +36684,9 @@ msgstr "無法為ä¸å­˜åœ¨çš„分支啟用政略 (%{branches})"
msgid "SecurityOrchestration|Policy cannot be enabled without branch information"
msgstr "沒有分支訊æ¯å°±ç„¡æ³•å•Ÿç”¨æ”¿ç•¥"
+msgid "SecurityOrchestration|Policy changes may take some time to be applied."
+msgstr "政策變更å¯èƒ½éœ€è¦ä¸€äº›æ™‚é–“æ‰èƒ½å¥—用。"
+
msgid "SecurityOrchestration|Policy definition"
msgstr "政策定義"
@@ -36234,6 +36759,9 @@ msgstr "在%{branches}上掃ææ¯å€‹æµæ°´ç·š"
msgid "SecurityOrchestration|Security Approvals"
msgstr "安全èªè­‰"
+msgid "SecurityOrchestration|Security Scan"
+msgstr "安全掃æ"
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr "安全政策專案已æˆåŠŸé€£æŽ¥"
@@ -36243,12 +36771,21 @@ msgstr "安全政策專案已æˆåŠŸå–消關è¯"
msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr "é¸æ“‡ä¸€å€‹å°ˆæ¡ˆä¾†å„²å­˜æ‚¨çš„安全政策。 %{linkStart}更多訊æ¯ã€‚%{linkEnd}"
+msgid "SecurityOrchestration|Select groups"
+msgstr "é¸æ“‡ç¾¤çµ„"
+
msgid "SecurityOrchestration|Select policy"
msgstr "é¸æ“‡æ”¿ç­–"
+msgid "SecurityOrchestration|Select scan type"
+msgstr "é¸æ“‡æŽƒæé¡žåž‹"
+
msgid "SecurityOrchestration|Select security project"
msgstr "é¸æ“‡å®‰å…¨å°ˆæ¡ˆ"
+msgid "SecurityOrchestration|Select users"
+msgstr "é¸æ“‡ä½¿ç”¨è€…"
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr "出了點å•é¡Œï¼Œç„¡æ³•å–得政策"
@@ -36318,6 +36855,9 @@ msgstr "更新掃æ政策"
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr "掃æ執行政策å…許建立在特定時間強制å°ç‰¹å®šåˆ†æ”¯é€²è¡Œå®‰å…¨æŽƒæçš„è¦å‰‡ã€‚支æ´çš„類型是 SASTã€DASTã€Secret 檢測ã€å®¹å™¨æŽƒæ與ä¾è³´é—œä¿‚掃æ。"
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr "使用掃æçµæžœæ”¿ç­–建立è¦å‰‡ï¼Œåœ¨åˆä½µåˆä½µè«‹æ±‚之å‰æª¢æŸ¥å®‰å…¨æ¼æ´žå’Œè¨±å¯è­‰åˆè¦æ€§ã€‚"
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr "使用掃æçµæžœæ”¿ç­–建立è¦å‰‡ï¼Œç¢ºä¿åœ¨åˆä½µåˆä½µè«‹æ±‚之å‰æª¢æŸ¥å®‰å…¨å•é¡Œã€‚"
@@ -36345,6 +36885,9 @@ msgstr "分支"
msgid "SecurityOrchestration|branches"
msgstr "分支"
+msgid "SecurityOrchestration|group level branch"
+msgstr "群組層級分支"
+
msgid "SecurityOrchestration|scanner finds"
msgstr "掃æ器發ç¾"
@@ -36381,6 +36924,9 @@ msgstr "%{firstProject}和%{secondProject}"
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr "%{firstProject},%{secondProject},åŠ%{rest}"
+msgid "SecurityReports|Activity"
+msgstr "活動"
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr "加入或刪除安全å€å…§çš„專案。 此列表中專案的掃æçµæžœå°‡é¡¯ç¤ºåœ¨å®‰å…¨å„€è¡¨æ¿å’Œæ¼æ´žå ±å‘Šä¸­ã€‚"
@@ -36390,9 +36936,24 @@ msgstr "加入專案"
msgid "SecurityReports|All activity"
msgstr "全部活動"
+msgid "SecurityReports|All clusters"
+msgstr "全部å¢é›†"
+
+msgid "SecurityReports|All images"
+msgstr "全部映åƒæª”"
+
+msgid "SecurityReports|All projects"
+msgstr "全部專案"
+
msgid "SecurityReports|All severities"
msgstr "全部嚴é‡ç´šåˆ¥"
+msgid "SecurityReports|All statuses"
+msgstr "全部狀態"
+
+msgid "SecurityReports|All tools"
+msgstr "全部工具"
+
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgstr "雖然沒有æ¼æ´žçš„情æ³å¾ˆå°‘見,但它å¯èƒ½æœƒç™¼ç”Ÿã€‚檢查您的設定以確ä¿æ‚¨å·²æ­£ç¢ºè¨­å®šå„€è¡¨æ¿ã€‚"
@@ -36537,6 +37098,9 @@ msgstr "未發ç¾æ¼æ´ž"
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr "æ­¤æµæ°´ç·šä¸­æœªç™¼ç¾æ¼æ´ž"
+msgid "SecurityReports|Not available"
+msgstr "無法使用"
+
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr "看起來ä¸æ­£ç¢ºã€‚"
@@ -36762,6 +37326,9 @@ msgstr "é¸æ“‡é©ç”¨æ–¼è©²å°ˆæ¡ˆçš„åˆè¦æ¡†æž¶ã€‚ %{linkStart}這些是如何åŠ
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr "請先從左å´é‚Šæ¬„é¸æ“‡ä¸€å€‹æ–‡ä»¶é–‹å§‹ç·¨è¼¯ï¼Œç„¶å¾Œå°±å¯ä»¥æ交您的變更了。"
+msgid "Select a group"
+msgstr "é¸æ“‡ä¸€å€‹ç¾¤çµ„"
+
msgid "Select a label"
msgstr "é¸æ“‡ä¸€å€‹æ¨™è¨˜"
@@ -36820,7 +37387,7 @@ msgid "Select due date"
msgstr "設定截止日期"
msgid "Select epic"
-msgstr "é¸æ“‡å²è©©"
+msgstr "é¸æ“‡å²è©© (epic) "
msgid "Select group"
msgstr "é¸æ“‡ç¾¤çµ„"
@@ -36867,6 +37434,9 @@ msgstr "é¸æ“‡å ±å‘Š"
msgid "Select reviewer(s)"
msgstr "é¸æ“‡å¯©æ ¸è€…(s)"
+msgid "Select severity (optional)"
+msgstr "é¸æ“‡åš´é‡ç¨‹åº¦ (é¸å¡«)"
+
msgid "Select source"
msgstr "é¸æ“‡ä¾†æº"
@@ -37111,7 +37681,7 @@ msgid "Session duration (minutes)"
msgstr "會話(session)æŒçºŒæ™‚é–“(分é˜)"
msgid "Set %{epic_ref} as the parent epic."
-msgstr "å°‡%{epic_ref}設定為父å²è©©ã€‚"
+msgstr "å°‡%{epic_ref}設定為父å²è©© (epic) 。"
msgid "Set a default description template to be used for new issues. %{link_start}What are description templates?%{link_end}"
msgstr "設定用於新議題的é è¨­æ述範本。 %{link_start}什麼是æ述範本?%{link_end}"
@@ -37147,7 +37717,7 @@ msgid "Set new password"
msgstr "設定新密碼"
msgid "Set parent epic to an epic"
-msgstr "為å²è©©è¨­å®šä¸Šç´šå²è©©"
+msgstr "為å²è©©è¨­å®šä¸Šç´šå²è©© (epic) "
msgid "Set per-user rate limits for imports and exports of projects and groups."
msgstr "為專案和群組的匯入和匯出設定æ¯å€‹ä½¿ç”¨è€…的速率é™åˆ¶ã€‚"
@@ -37255,7 +37825,7 @@ msgid "SetStatusModal|Clear status"
msgstr "清除狀態"
msgid "SetStatusModal|Clear status after"
-msgstr "外少時間之後清除狀態"
+msgstr "多少時間之後清除狀態"
msgid "SetStatusModal|Displays that you are busy or not able to respond"
msgstr "顯示您忙碌中或無法回應"
@@ -37288,7 +37858,7 @@ msgid "SetStatusModal|Your status resets on %{date}."
msgstr "您的狀態在 %{date} 時é‡è¨­ã€‚"
msgid "Sets %{epic_ref} as parent epic."
-msgstr "å°‡%{epic_ref}設定為父å²è©©ã€‚"
+msgstr "å°‡%{epic_ref}設定為父å²è©© (epic) 。"
msgid "Sets health status to %{health_status}."
msgstr "å°‡å¥åº·ç‹€æ…‹è¨­å®šç‚º %{health_status}。"
@@ -37354,6 +37924,9 @@ msgstr "分享%{strong_open}GitLab單一登入網å€%{strong_close}給群組æˆå
msgid "Shared Runners"
msgstr "共用執行器(Runners)"
+msgid "Shared Runners:"
+msgstr "已共用的執行器(Runners):"
+
msgid "Shared projects"
msgstr "共用專案"
@@ -37424,7 +37997,7 @@ msgid "Show all breadcrumbs"
msgstr "顯示所有麵包屑導覽"
msgid "Show all epics"
-msgstr "顯示所有å²è©©"
+msgstr "顯示所有å²è©© (epics) "
msgid "Show all issues."
msgstr "顯示所有議題。"
@@ -37442,7 +38015,7 @@ msgid "Show archived projects only"
msgstr "僅顯示已歸檔專案"
msgid "Show closed epics"
-msgstr "顯示已關閉的å²è©©"
+msgstr "顯示已關閉的å²è©© (epics) "
msgid "Show command"
msgstr "顯示相關指令"
@@ -37480,11 +38053,14 @@ msgstr "顯示最新版本"
msgid "Show list"
msgstr "顯示列表"
+msgid "Show more"
+msgstr "顯示更多"
+
msgid "Show one file at a time"
msgstr "一次顯示一個文件"
msgid "Show open epics"
-msgstr "顯示開放中的å²è©©"
+msgstr "顯示開放中的å²è©© (epics) "
msgid "Show project milestones"
msgstr "顯示專案里程碑"
@@ -37578,7 +38154,7 @@ msgid "Showing %{pageSize} of %{total} %{issuableType}"
msgstr "顯示 %{pageSize} / %{total} %{issuableType}"
msgid "Showing all epics"
-msgstr "顯示所有å²è©©"
+msgstr "顯示所有å²è©© (epics) "
msgid "Showing all issues"
msgstr "顯示所有議題"
@@ -37637,9 +38213,6 @@ msgstr "以具有符åˆé›»å­éƒµä»¶åœ°å€çš„使用者身份登入,將電å­éƒµ
msgid "Sign in preview"
msgstr "登入é è¦½"
-msgid "Sign in to %{group_name}"
-msgstr "登入至 %{group_name}"
-
msgid "Sign in to GitLab"
msgstr "登入到GitLab"
@@ -37776,7 +38349,7 @@ msgid "Size limit per repository (MB)"
msgstr "æ¯å€‹ç‰ˆæœ¬åº«çš„大å°é™åˆ¶ (MB)"
msgid "Skipped"
-msgstr "已跳éŽ"
+msgstr "ç•¥éŽ"
msgid "Skipped deployment to"
msgstr "è·³éŽéƒ¨ç½²åˆ°"
@@ -37793,8 +38366,8 @@ msgstr "Slackæ•´åˆå…許您é€éŽèŠå¤©è¦–窗中的shash指令與GitLab互動ã€
msgid "Slack logo"
msgstr "Slack logo"
-msgid "SlackIntegration|Are you sure you want to remove this project from the Slack application?"
-msgstr "您確定è¦å¾ž Slack 應用程å¼ä¸­åˆªé™¤æ­¤å°ˆæ¡ˆå—Žï¼Ÿ"
+msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
+msgstr "您確定è¦å¾ž GitLab for Slack 應用程å¼ä¸­ç§»é™¤è©²å°ˆæ¡ˆå—Žï¼Ÿ"
msgid "SlackIntegration|Client ID"
msgstr "客戶端 ID"
@@ -37808,14 +38381,17 @@ msgstr "GitLab for Slack"
msgid "SlackIntegration|GitLab for Slack was successfully installed."
msgstr "GitLab for Slack å·²æˆåŠŸå®‰è£ã€‚"
-msgid "SlackIntegration|Install Slack app"
-msgstr "å®‰è£ Slack 應用程å¼"
+msgid "SlackIntegration|Install GitLab for Slack app"
+msgstr "å®‰è£ GitLab for Slack 應用程å¼"
+
+msgid "SlackIntegration|Notifications only work if you're on the latest version of the GitLab for Slack app"
+msgstr "通知僅在您使用最新版本的 GitLab for Slack 應用程å¼æ™‚æ‰æœ‰æ•ˆ"
msgid "SlackIntegration|Project alias"
msgstr "專案別å"
-msgid "SlackIntegration|Reinstall Slack app"
-msgstr "é‡æ–°å®‰è£ Slack 應用程å¼"
+msgid "SlackIntegration|Reinstall GitLab for Slack app"
+msgstr "GitLab for Slack"
msgid "SlackIntegration|Remove project"
msgstr "移除專案"
@@ -37838,14 +38414,17 @@ msgstr "團隊å稱"
msgid "SlackIntegration|This integration allows users to perform common operations on this project by entering slash commands in Slack."
msgstr "該整åˆå…許使用者通éŽåœ¨ Slack 中輸入斜æ å‘½ä»¤ä¾†å°è©²å°ˆæ¡ˆåŸ·è¡Œå¸¸è¦‹æ“作。"
+msgid "SlackIntegration|Update to the latest version"
+msgstr "更新到最新版本"
+
msgid "SlackIntegration|Verification token"
msgstr "驗證令牌(權æ–)"
msgid "SlackIntegration|You can now close this window and go to your Slack workspace."
msgstr "您ç¾åœ¨å¯ä»¥é—œé–‰æ­¤çª—å£ä¸¦è½‰åˆ°æ‚¨çš„ Slack 工作å€ã€‚"
-msgid "SlackIntegration|You may need to reinstall the Slack application when we %{linkStart}make updates or change permissions%{linkEnd}."
-msgstr "當我們%{linkStart}進行更新或更改權é™%{linkEnd}時,您å¯èƒ½éœ€è¦é‡æ–°å®‰è£ Slack 應用程å¼ã€‚"
+msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
+msgstr "當我們%{linkStart}進行更新或更改權é™%{linkEnd}時,您å¯èƒ½éœ€è¦é‡æ–°å®‰è£ GitLab for Slack 應用程å¼ã€‚"
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
msgstr "1. %{slash_command_link_start}在您的Slack團隊中使用此訊æ¯åŠ å…¥ä¸€å€‹æ–œæ§“指令%{slash_command_link_end}:"
@@ -37923,7 +38502,7 @@ msgid "SnippetsEmptyState|Documentation"
msgstr "文件"
msgid "SnippetsEmptyState|New snippet"
-msgstr "新建程å¼ç¢¼ç‰‡æ®µ"
+msgstr "新增程å¼ç¢¼ç‰‡æ®µ"
msgid "SnippetsEmptyState|No snippets found"
msgstr "當å‰ç„¡ç¨‹å¼ç¢¼ç‰‡æ®µ"
@@ -37977,17 +38556,17 @@ msgid "Some changes are not shown"
msgstr "一些變更未顯示"
msgid "Some child epics may be hidden due to applied filters"
-msgstr "æŸäº›å­å²è©©å¯èƒ½ç”±æ–¼å¥—用éŽæ¿¾å™¨è€Œè¢«éš±è—"
+msgstr "æŸäº›å­å²è©© (epics) å¯èƒ½ç”±æ–¼å¥—用éŽæ¿¾å™¨è€Œè¢«éš±è—"
msgid "Some common domains are not allowed. %{learn_more_link}."
msgstr "æŸäº›å¸¸è¦‹ç¶²åŸŸä¸è¢«å…許。%{learn_more_link}"
+msgid "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes."
+msgstr "有人與您åŒæ™‚編輯了 %{issuableType},查看 %{linkStart}該 %{issuableType}%{linkEnd} 並確ä¿æ‚¨ä¸æœƒç„¡æ„中覆蓋他們的更動。"
+
msgid "Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs."
msgstr "有人在您編輯文件的åŒæ™‚編輯了該文件。請檢查 %{link_start}文件 %{icon}%{link_end} 並確ä¿æ‚¨çš„更改ä¸æœƒç„¡æ„中刪除他們的更改。"
-msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
-msgstr "其他使用者åŒæ™‚編輯了這個議題。請檢查%{linkStart}è­°é¡Œ%{linkEnd}並確ä¿æ‚¨çš„變更ä¸æœƒç„¡æ„中移除他們的變更。"
-
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr "有人與您åŒæ™‚編輯了%{issueType} 。æ述已被更新,您需è¦é‡æ–°ä¿®æ”¹ã€‚"
@@ -38027,6 +38606,9 @@ msgstr "嘗試載入議題è¯çµ¡äººæ™‚發生錯誤。"
msgid "Something went wrong when reordering designs. Please try again"
msgstr "é‡æ–°æŽ’åºè¨­è¨ˆæ™‚出了點å•é¡Œã€‚è«‹å†è©¦ä¸€æ¬¡"
+msgid "Something went wrong when sending the incident link to Slack."
+msgstr "將事故éˆçµç™¼é€åˆ° Slack 時發生錯誤。"
+
msgid "Something went wrong while adding timeline event."
msgstr "加入時間線事件時發生錯誤。"
@@ -38043,7 +38625,10 @@ msgid "Something went wrong while archiving a requirement."
msgstr "歸檔需求時出了錯。"
msgid "Something went wrong while closing the epic. Please try again later."
-msgstr "關閉å²è©©æ™‚出了錯。請ç¨å¾Œå†è©¦ã€‚"
+msgstr "關閉å²è©© (epic) 時出了錯,請ç¨å¾Œå†è©¦ã€‚"
+
+msgid "Something went wrong while closing the incident form."
+msgstr "關閉事故表單時發生錯誤。"
msgid "Something went wrong while closing the merge request. Please try again later."
msgstr "關閉åˆä½µè«‹æ±‚時發生錯誤。請ç¨å¾Œå†è©¦ã€‚"
@@ -38114,8 +38699,11 @@ msgstr "å–得軟體套件列表時發生錯誤。"
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr "å–å¾—Let's Encrypt憑證時發生錯誤。"
+msgid "Something went wrong while opening the incident form."
+msgstr "開啟事故表單時發生錯誤。"
+
msgid "Something went wrong while promoting the issue to an epic. Please try again."
-msgstr "將議題å‡ç´šåˆ°å²è©©æ™‚發生錯誤。請å†è©¦ä¸€æ¬¡ã€‚"
+msgstr "將議題å‡ç´šåˆ°å²è©© (epic) 時發生錯誤,請å†è©¦ä¸€æ¬¡ã€‚"
msgid "Something went wrong while promoting the note to timeline event."
msgstr "將備註æå‡åˆ°æ™‚間線事件時發生錯誤。"
@@ -38124,7 +38712,7 @@ msgid "Something went wrong while reopening a requirement."
msgstr "é‡æ–°æ‰“開需求時發生錯誤。"
msgid "Something went wrong while reopening the epic. Please try again later."
-msgstr "é‡æ–°æ‰“é–‹å²è©©æ™‚發生錯誤。請ç¨å¾Œå†è©¦ã€‚"
+msgstr "é‡æ–°æ‰“é–‹å²è©© (epic) 時發生錯誤,請ç¨å¾Œå†è©¦ã€‚"
msgid "Something went wrong while reopening the merge request. Please try again later."
msgstr "é‡æ–°é–‹å•Ÿåˆä½µè«‹æ±‚時發生錯誤。請ç¨å¾Œå†è©¦."
@@ -38507,6 +39095,9 @@ msgstr "通éŽNeeds關係加速您的æµæ°´ç·š"
msgid "Spent at"
msgstr "花費在"
+msgid "Spent at can't be a future date and time."
+msgstr "花費的時間ä¸èƒ½æ˜¯æœªä¾†çš„日期和時間。"
+
msgid "Squash commit message"
msgstr "壓縮 (Squash) æ交訊æ¯"
@@ -38612,6 +39203,9 @@ msgstr "æµæ°´ç·šæˆåŠŸæ™‚å•Ÿå‹•åˆä½µä½‡åˆ—"
msgid "Start merge train..."
msgstr "å•Ÿå‹•åˆä½µä½‡åˆ—..."
+msgid "Start remote connection"
+msgstr "開始é ç«¯é€£ç·š"
+
msgid "Start search"
msgstr "開始æœå°‹"
@@ -38675,6 +39269,9 @@ msgstr "統計"
msgid "Status"
msgstr "狀態"
+msgid "Status (optional)"
+msgstr "狀態 (é¸å¡«)"
+
msgid "Status was retried."
msgstr "狀態已é‡è©¦ã€‚"
@@ -39411,6 +40008,9 @@ msgstr "您無法å†å°‡è¨‚閱詳細訊æ¯èˆ‡ GitLab åŒæ­¥ã€‚ %{connectivityHelp
msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details."
msgstr "您å¯ä»¥é–‹å§‹å…費試用 GitLab 旗艦版,無需任何承諾或付款訊æ¯ã€‚"
+msgid "SuperSonics|You can sync your subscription data to ensure your details are up to date."
+msgstr "您å¯ä»¥åŒæ­¥æ‚¨çš„訂閱資料以確ä¿æ‚¨çš„詳細資訊是最新的。"
+
msgid "SuperSonics|You do not have an active subscription"
msgstr "您沒有有效訂閱"
@@ -39501,9 +40101,6 @@ msgstr "切æ›åˆ†æ”¯"
msgid "Switch branch/tag"
msgstr "切æ›åˆ†æ”¯/標籤"
-msgid "Switch editors"
-msgstr "切æ›ç·¨è¼¯å™¨"
-
msgid "Switch to GitLab Next"
msgstr "切æ›åˆ°GitLab é è¦½ç‰ˆ"
@@ -39786,6 +40383,12 @@ msgstr "建立/匯入 議題(工單)以就想法和è¦åŠƒå·¥ä½œé€²è¡Œå”åŒå
msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code"
msgstr "設定 CI/CD æµæ°´ç·šä¾†æ§‹å»ºã€æ¸¬è©¦ã€éƒ¨ç½²å’Œç›£æŽ§ç¨‹å¼ç¢¼"
+msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed"
+msgstr "%{complete_count} / %{total_count} %{checklist_item_noun} 已完æˆ"
+
+msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}"
+msgstr "%{complete_count}/%{total_count} %{checklist_item_noun}"
+
msgid "Team"
msgstr "團隊"
@@ -40281,7 +40884,7 @@ msgid "The contents of this group, its subgroups and projects will be permanentl
msgstr "該群組,其å­ç¾¤çµ„和專案的內容將於%{deletion_adjourned_period}天後在%{date}永久移除。在此之後,您的資料將無法æ¢å¾©ã€‚"
msgid "The current epic"
-msgstr "當å‰å²è©©"
+msgstr "當å‰å²è©© (epic) "
msgid "The current incident"
msgstr "ç›®å‰äº‹ä»¶"
@@ -40361,6 +40964,9 @@ msgstr "管ç†å“¡ %{username} 撤消了以下個人存å–令牌。"
msgid "The following SSH key was deleted by an administrator, %{username}."
msgstr "以下SSH金鑰已由管ç†å“¡ %{username} 刪除。"
+msgid "The following files or directories can only be modified by the user who locked them."
+msgstr "以下文件或目錄åªèƒ½ç”±éŽ–定它們的使用者修改。"
+
msgid "The following items will NOT be exported:"
msgstr "以下項目將ä¸æœƒè¢«åŒ¯å‡ºï¼š"
@@ -40439,7 +41045,7 @@ msgid "The invitation was successfully resent."
msgstr "邀請已é‡æ–°ç™¼é€ã€‚"
msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
-msgstr "該å•é¡Œè¢«æˆåŠŸæå‡ç‚ºå²è©©ã€‚é‡å°Žå‘到å²è©©..."
+msgstr "該å•é¡Œè¢«æˆåŠŸæå‡ç‚ºå²è©© (epic) ,é‡å°Žå‘到å²è©© (epic) ..."
msgid "The last owner cannot be set to awaiting"
msgstr "最後一個æ“有者ä¸èƒ½è¨­å®šç‚ºç­‰å¾…"
@@ -40532,7 +41138,7 @@ msgid "The page could not be displayed because it timed out."
msgstr "é é¢å·²é€¾æ™‚,無法顯示。"
msgid "The parent epic is confidential and can only contain confidential epics and issues"
-msgstr "父å²è©©æ˜¯æ©Ÿå¯†çš„,åªèƒ½åŒ…å«æ©Ÿå¯†å²è©©å’Œè­°é¡Œ"
+msgstr "父å²è©© (epic) 是機密的,åªèƒ½åŒ…å«æ©Ÿå¯†å²è©© (epics) 和議題"
msgid "The parsed YAML is too big"
msgstr "被解æžçš„ YAML 文件太大"
@@ -40723,9 +41329,6 @@ msgstr "沒有垃圾郵件日誌"
msgid "There are no abuse reports!"
msgstr "沒有濫用報告ï¼"
-msgid "There are no archived projects yet"
-msgstr "當å‰å°šç„¡å·²æ­¸æª”的專案"
-
msgid "There are no archived requirements"
msgstr "沒有已歸檔的需求"
@@ -40742,7 +41345,7 @@ msgid "There are no charts configured for this page"
msgstr "沒有為此é é¢è¨­å®šåœ–表"
msgid "There are no closed epics"
-msgstr "沒有已關閉的å²è©©"
+msgstr "沒有已關閉的å²è©© (epics) "
msgid "There are no closed issues"
msgstr "沒有已關閉的議題"
@@ -40766,7 +41369,7 @@ msgid "There are no matching files"
msgstr "沒有符åˆçš„文件"
msgid "There are no open epics"
-msgstr "沒有開啟中的å²è©©"
+msgstr "沒有開啟中的å²è©© (epics) "
msgid "There are no open issues"
msgstr "沒有未關閉的議題"
@@ -40783,9 +41386,6 @@ msgstr "沒有開啟的測試案例"
msgid "There are no packages yet"
msgstr "還沒有軟體套件"
-msgid "There are no projects shared with this group yet"
-msgstr "ç›®å‰å°šç„¡åˆ†äº«çµ¦è©²ç¾¤çµ„的專案"
-
msgid "There are no secure files yet."
msgstr "還沒有安全文件。"
@@ -40831,6 +41431,9 @@ msgstr "資料太多無法計算。請更改您的é¸æ“‡ã€‚"
msgid "There was a problem communicating with your device."
msgstr "與您的設備通信時出ç¾å•é¡Œã€‚"
+msgid "There was a problem creating the incident. Please try again."
+msgstr "建立事故時發生å•é¡Œï¼Œè«‹å†è©¦ä¸€æ¬¡ã€‚"
+
msgid "There was a problem fetching CRM contacts."
msgstr "è®€å– CRM è¯çµ¡äººæ™‚發生錯誤。"
@@ -40844,7 +41447,7 @@ msgid "There was a problem fetching emojis."
msgstr "å–得表情符號時出ç¾å•é¡Œã€‚"
msgid "There was a problem fetching epics."
-msgstr "å–å¾—å²è©©æ™‚出ç¾å•é¡Œã€‚"
+msgstr "å–å¾—å²è©© (epics) 時出ç¾å•é¡Œã€‚"
msgid "There was a problem fetching groups."
msgstr "å–得群組時出ç¾å•é¡Œã€‚"
@@ -40951,6 +41554,9 @@ msgstr "å–å¾—å‡çµéƒ¨ç½²æ™‚發生錯誤。"
msgid "There was an error fetching the environments information."
msgstr "å–得環境訊æ¯æ™‚發生錯誤。"
+msgid "There was an error fetching the job."
+msgstr "æå–作業時發生錯誤。"
+
msgid "There was an error fetching the jobs for your project."
msgstr "為您的專案å–得作業時發生錯誤。"
@@ -40967,13 +41573,13 @@ msgid "There was an error gathering the chart data"
msgstr "收集圖表資料時發生錯誤"
msgid "There was an error getting the epic participants."
-msgstr "å–å¾—å²è©©åƒèˆ‡è€…時發生錯誤。"
+msgstr "å–å¾—å²è©© (epic) åƒèˆ‡è€…時發生錯誤。"
msgid "There was an error importing the Jira project."
msgstr "匯入Jira專案時發生錯誤。"
msgid "There was an error loading related feature flags"
-msgstr "載入相關功能標誌時發生錯誤"
+msgstr "載入相關特性標籤時發生錯誤"
msgid "There was an error loading users activity calendar."
msgstr "載入使用者活動日曆時發生錯誤。"
@@ -40990,6 +41596,9 @@ msgstr "é‡è¨­ä½¿ç”¨è€…æµæ°´ç·šåˆ†é˜æ•¸æ™‚發生錯誤。"
msgid "There was an error retrieving the Jira users."
msgstr "å–å¾—Jira使用者時發生錯誤。"
+msgid "There was an error running the job. Please try again."
+msgstr "執行作業時發生錯誤,請å†è©¦ä¸€æ¬¡ã€‚"
+
msgid "There was an error saving your changes."
msgstr "ä¿å­˜è®Šæ›´æ™‚發生錯誤。"
@@ -41039,7 +41648,7 @@ msgid "There was an error with the reCAPTCHA. Please solve the reCAPTCHA again."
msgstr "reCAPTCHA 驗證錯誤。請å†æ¬¡é©—è­‰ reCAPTCHA。"
msgid "These dates affect how your epics appear in the roadmap. Set a fixed date or one inherited from the milestones assigned to issues in this epic."
-msgstr "這些日期會影響您的å²è©©åœ¨è·¯ç·šåœ–中的顯示方å¼ã€‚設定一個固定日期或從分é…給此å²è©©ä¸­è­°é¡Œçš„里程碑繼承的日期。"
+msgstr "這些日期會影響您的å²è©© (epics) 在路線圖中的顯示方å¼ã€‚設定一個固定日期或從分é…給此å²è©© (epic) 中議題的里程碑繼承的日期。"
msgid "These examples show how to trigger this project's pipeline for a branch or tag."
msgstr "這些範例顯示了如何為分支或標記觸發此專案的æµæ°´ç·šã€‚"
@@ -41083,9 +41692,6 @@ msgstr "因為 %{reason}無法顯示 %{viewer} 。您å¯ä»¥æ”¹ç‚º %{options}。"
msgid "This Cron pattern is invalid"
msgstr "æ­¤Cronæ ¼å¼ç„¡æ•ˆ"
-msgid "This Experiment has no logged Candidates"
-msgstr "該實驗沒有登記的候é¸äºº"
-
msgid "This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area."
msgstr "æ­¤ GitLab 實例尚未æ供任何共用執行器(runners)。實例管ç†å“¡å¯ä»¥åœ¨ç®¡ç†å€è¨»å†Šå…±ç”¨åŸ·è¡Œå™¨(runners)。"
@@ -41249,31 +41855,34 @@ msgid "This environment's canary ingress has been updated recently. Please retry
msgstr "此環境的Canary Ingress最近有更新。請ç¨å¾Œå†è©¦ã€‚"
msgid "This epic already has the maximum number of child epics."
-msgstr "æ­¤å²è©©çš„å­å²è©©æ•¸ç›®å·²é”最大值。"
+msgstr "æ­¤å²è©© (epic) çš„å­å²è©© (epics) 數目已é”最大值。"
msgid "This epic cannot be added. An epic cannot be added to itself."
-msgstr "無法增加該å²è©©ï¼Œå²è©©ä¸èƒ½åŠ å…¥åˆ°è‡ªèº«ã€‚"
+msgstr "無法增加該å²è©© (epic) ,å²è©© (epic) ä¸èƒ½åŠ å…¥åˆ°è‡ªèº«ã€‚"
msgid "This epic cannot be added. An epic must belong to the same group or subgroup as its parent epic."
-msgstr "無法增加該å²è©©ï¼Œå²è©©å¿…須與其父å²è©©å±¬æ–¼åŒä¸€ç¾¤çµ„或å­ç¾¤çµ„。"
+msgstr "無法增加該å²è©© (epic),å²è©© (epic) 必須與其父å²è©© (epic) 屬於åŒä¸€ç¾¤çµ„或å­ç¾¤çµ„。"
msgid "This epic cannot be added. It is already an ancestor of the parent epic."
-msgstr "無法增加該å²è©©ï¼Œå®ƒå·²ç¶“是其父å²è©©çš„上一層。"
+msgstr "無法增加該å²è©© (epic) ,它已經是其父å²è©© (epic) 的上一層。"
msgid "This epic cannot be added. It is already assigned to the parent epic."
-msgstr "無法增加該å²è©©ï¼Œå®ƒå·²åˆ†é…給父å²è©©ã€‚"
+msgstr "無法增加該å²è©© (epic) ,它已分é…給父å²è©© (epic) 。"
msgid "This epic cannot be added. One or more epics would exceed the maximum depth (%{max_depth}) from its most distant ancestor."
-msgstr "無法增加該å²è©©ï¼Œä¸€å€‹æˆ–多個å²è©©å°‡è¶…éŽå…¶æœ€ä¸Šå±¤ç¥–先的最大深度 (%{max_depth})。"
+msgstr "無法增加該å²è©© (epic) ,一個或多個å²è©© (epic) 將超éŽå…¶æœ€ä¸Šå±¤ç¥–先的最大深度 (%{max_depth})。"
msgid "This epic cannot be added. You don't have access to perform this action."
-msgstr "無法增加該å²è©©ï¼Œæ‚¨ç„¡æ¬ŠåŸ·è¡Œæ­¤å‹•ä½œã€‚"
+msgstr "無法增加該å²è©© (epic) ,您無權執行此動作。"
msgid "This epic does not exist or you don't have sufficient permission."
-msgstr "æ­¤å²è©©ä¸å­˜åœ¨æˆ–者您沒有足夠的權é™ã€‚"
+msgstr "æ­¤å²è©© (epic) ä¸å­˜åœ¨æˆ–者您沒有足夠的權é™ã€‚"
msgid "This epic would exceed maximum number of related epics."
-msgstr "æ­¤å²è©©å°‡è¶…éŽç›¸é—œå²è©©çš„最大數é‡ã€‚"
+msgstr "æ­¤å²è©© (epic) 將超éŽç›¸é—œå²è©© (epics) 的最大數é‡ã€‚"
+
+msgid "This experiment has no logged candidates"
+msgstr "該實驗沒有已記錄的候é¸è€…"
msgid "This feature requires local storage to be enabled"
msgstr "此功能需è¦å•Ÿç”¨æœ¬åœ°å„²å­˜"
@@ -41293,8 +41902,8 @@ msgstr "此表單在é è¦½ä¸­è¢«ç¦ç”¨"
msgid "This group"
msgstr "該群組"
-msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_adjourned_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
-msgstr "該群組åŠå…¶å­ç¾¤çµ„和專案將在 %{deletion_adjourned_period} 天內處於「待刪除ã€ç‹€æ…‹ï¼Œç„¶å¾Œåœ¨ %{date} 被永久刪除。該群組å¯ä»¥åœ¨è©²æ—¥æœŸä¹‹å‰å®Œå…¨æ¢å¾©ã€‚"
+msgid "This group and its subgroups and projects will be placed in a 'pending deletion' state for %{deletion_delayed_period} days, then permanently deleted on %{date}. The group can be fully restored before that date."
+msgstr "該群組åŠå…¶å­ç¾¤çµ„和專案將在 %{deletion_delayed_period} 天內處於“待刪除â€ç‹€æ…‹ï¼Œè€Œå¾Œåœ¨ %{date} 永久刪除,該群組å¯ä»¥åœ¨è©²æ—¥æœŸä¹‹å‰å®Œæ•´æ¢å¾©ã€‚"
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
msgstr "此群組ä¸èƒ½è¢«åˆªé™¤ï¼Œå› ç‚ºå®ƒå·²é€£çµåˆ°ä¸€å€‹è¨‚閱,è¦åˆªé™¤æ­¤ç¾¤çµ„, %{linkStart}將訂閱%{linkEnd} 連çµåˆ°å¦ä¸€å€‹ç¾¤çµ„。"
@@ -41354,7 +41963,7 @@ msgid "This is a private email address %{helpIcon} generated just for you. Anyon
msgstr "這是專為您產生的ç§äººé›»å­éƒµä»¶åœ°å€ %{helpIcon} 任何æ“有它的人都å¯ä»¥åƒæ‚¨ä¸€æ¨£å»ºç«‹å•é¡Œæˆ–åˆä½µè«‹æ±‚。如果發生這種情æ³ï¼Œ %{resetLinkStart}é‡è¨­é€™å€‹ä»¤ç‰Œ%{resetLinkEnd}。"
msgid "This is a security log of authentication events involving your account."
-msgstr "這是涉åŠæ‚¨å¸³è™Ÿçš„身份驗證事件的安全日誌。"
+msgstr "這是涉åŠæ‚¨å¸³è™Ÿçš„身份驗證事件安全日誌。"
msgid "This is a self-managed instance of GitLab."
msgstr "這是GitLab的自管ç†å¯¦ä¾‹ã€‚"
@@ -41375,10 +41984,10 @@ msgid "This is your current session"
msgstr "這是您目å‰çš„工作階段"
msgid "This issue cannot be assigned to a confidential epic because it is public."
-msgstr "此議題ä¸èƒ½åˆ†é…給機密å²è©©ï¼Œå› ç‚ºå®ƒæ˜¯å…¬é–‹çš„。"
+msgstr "此議題ä¸èƒ½åˆ†é…給機密å²è©© (epic) ,因為它是公開的。"
msgid "This issue cannot be made public because it belongs to a confidential epic."
-msgstr "此議題ä¸èƒ½å…¬é–‹ï¼Œå› ç‚ºå®ƒå±¬æ–¼æ©Ÿå¯†å²è©©ã€‚"
+msgstr "此議題ä¸èƒ½å…¬é–‹ï¼Œå› ç‚ºå®ƒå±¬æ–¼æ©Ÿå¯†å²è©© (epic) 。"
msgid "This issue is confidential and should only be visible to team members with at least Reporter access."
msgstr "此議題是機密的,åªèƒ½é¡¯ç¤ºçµ¦æ“有至少「報告人ã€å­˜å–權é™çš„團隊æˆå“¡ã€‚"
@@ -41390,7 +41999,7 @@ msgid "This issue is hidden because its author has been banned"
msgstr "此議題已隱è—,因為其作者已被ç¦æ­¢"
msgid "This issue is in a child epic of the filtered epic"
-msgstr "此議題在éŽæ¿¾å²è©©çš„å­å²è©©ä¸­"
+msgstr "此議題在éŽæ¿¾å²è©© (epic) çš„å­å²è©© (epic) 中"
msgid "This job could not start because it could not retrieve the needed artifacts%{punctuation}%{invalid_dependencies}"
msgstr "此作業無法啟動,因為它無法檢索所需的工件%{punctuation}%{invalid_dependencies}"
@@ -41767,6 +42376,12 @@ msgstr "剩餘時間:"
msgid "Time spent"
msgstr "花費的時間"
+msgid "Time spent can't be zero."
+msgstr "花費的時間ä¸èƒ½ç‚ºé›¶ã€‚"
+
+msgid "Time spent must be formatted correctly. For example: 1h 30m."
+msgstr "花費的時間必須是正確的格å¼ï¼Œä¾‹å¦‚:1h 30m。"
+
msgid "Time to Restore Service"
msgstr "是時候æ¢å¾©æœå‹™äº†"
@@ -42015,6 +42630,9 @@ msgstr "標題 (å¿…è¦)"
msgid "Title:"
msgstr "標題:"
+msgid "Titles"
+msgstr "標題"
+
msgid "Titles and Descriptions"
msgstr "標題和æè¿°"
@@ -42048,9 +42666,6 @@ msgstr "è¦å•Ÿç”¨æ‚¨çš„試用版,我們需è¦æ‚¨æ供更多詳細信æ¯ã€‚"
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr "è¦åŠ å…¥è‡ªè¨‚後綴,請設定æœå‹™å°é›»å­éƒµä»¶åœ°å€ã€‚ %{linkStart}了解更多。%{linkEnd}"
-msgid "To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
-msgstr "è¦å¢žåŠ é¡¯ç¤ºå稱,請設定æœå‹™å°é›»å­éƒµä»¶åœ°å€ã€‚ %{linkStart}了解更多。%{linkEnd}"
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr "如需手動加入æ¢ç›®ï¼Œè«‹åœ¨æ‰‹æ©Ÿæ‡‰ç”¨ä¸­æ供以下訊æ¯ã€‚"
@@ -42165,6 +42780,9 @@ msgstr "è¦é‡æ–°å•Ÿç”¨æ‚¨çš„帳號, %{gitlab_link_start}登入到 GitLab。%{
msgid "To reactivate your account, sign in to GitLab at %{gitlab_url}."
msgstr "è¦é‡æ–°å•Ÿç”¨æ‚¨çš„帳號,請在 %{gitlab_url}登入 GitLab。"
+msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your namespace owner to reduce the number of users in your namespace to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
+msgstr "è¦ç§»é™¤ %{link_start}read-only%{link_end} 狀態並é‡æ–°å–得寫入權é™ï¼Œè«‹è¦æ±‚您的命å空間æ“有者將您å空間中的使用者數é‡æ¸›å°‘到 %{free_limit} 使用者或更少,或者å‡ç´šåˆ°æ²’有使用者é™åˆ¶çš„付費級別。"
+
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr "è¦ç§»é™¤%{link_start}唯讀%{link_end}狀態並é‡æ–°å–得寫入權é™ï¼Œæ‚¨å¯ä»¥å°‡å‘½å空間中的使用者數é‡æ¸›å°‘到 %{free_limit} 個使用者或更少。您也å¯ä»¥å‡ç´šåˆ°æ²’有使用者é™åˆ¶çš„付費層級。若您需è¦æ›´å¤šæ™‚間,å¯ä»¥é–‹å§‹30天的å…費試用,其中包å«äº†ç„¡é™ä½¿ç”¨è€…。"
@@ -42175,7 +42793,7 @@ msgid "To run CI/CD pipelines with JetBrains TeamCity, input the GitLab project
msgstr "è¦ä½¿ç”¨ JetBrains TeamCity 執行 CI/CD æµæ°´ç·šï¼Œè«‹åœ¨ TeamCity 專案版本控制設定中輸入 GitLab 專案詳細訊æ¯ã€‚"
msgid "To see all the user's personal access tokens you must impersonate them first."
-msgstr "è¦æŸ¥çœ‹ä½¿ç”¨è€…的所有個人存å–令牌(權æ–),您必須先模擬其身份。"
+msgstr "è¦æŸ¥çœ‹ä½¿ç”¨è€…的所有個人存å–權æ–(令牌),您必須先模擬其身份。"
msgid "To see this project's operational details, %{linkStart}upgrade its group plan to Premium%{linkEnd}. You can also remove the project from the dashboard."
msgstr "è¦æŸ¥çœ‹æ­¤å°ˆæ¡ˆçš„æ“作詳情, %{linkStart}將群組方案å‡ç´šåˆ°å°ˆæ¥­ç‰ˆ%{linkEnd}。您也å¯ä»¥å¾žå„€è¡¨æ¿ä¸­ç§»é™¤è©²å°ˆæ¡ˆã€‚"
@@ -42193,7 +42811,7 @@ msgid "To set up this integration:"
msgstr "設定該整åˆï¼š"
msgid "To specify the notification level per project of a group you belong to, you need to visit project page and change notification level there."
-msgstr "è‹¥è¦æŒ‡å®šæ‚¨æ‰€å±¬çš„群組的æ¯å€‹å°ˆæ¡ˆçš„通知級別,您需è¦å­˜å–專案é é¢ï¼Œè®Šæ›´é€šçŸ¥ç´šåˆ¥ã€‚"
+msgstr "è‹¥è¦æŒ‡å®šæ‚¨æ‰€å±¬ç¾¤çµ„çš„æ¯å€‹å°ˆæ¡ˆé€šçŸ¥ç´šåˆ¥ï¼Œæ‚¨éœ€è¦è¨ªå•å°ˆæ¡ˆé é¢ï¼Œä¸¦è®Šæ›´é€šçŸ¥ç´šåˆ¥ã€‚"
msgid "To start using GitLab Enterprise Edition, upload the %{codeOpen}.gitlab-license%{codeClose} file or enter the license key you have received from GitLab Inc."
msgstr "è¦é–‹å§‹ä½¿ç”¨ä¼æ¥­ç‰ˆï¼Œè«‹ä¸Šå‚³ %{codeOpen}.gitlab-license%{codeClose} 文件或輸入您從 GitLab Inc. 收到的授權許å¯é‡‘é‘°"
@@ -42258,8 +42876,11 @@ msgstr "無法åˆä½µ"
msgid "Todos|Design"
msgstr "設計"
+msgid "Todos|Due %{due_date}"
+msgstr "截止日期 %{due_date}"
+
msgid "Todos|Epic"
-msgstr "å²è©©"
+msgstr "å²è©© (Epic)"
msgid "Todos|Filter by author"
msgstr "ä¾ä½œè€…éŽæ¿¾"
@@ -42291,6 +42912,9 @@ msgstr "這是您總是知é“下一步è¦åšä»€éº¼çš„æ–¹å¼ã€‚"
msgid "Todos|Mark all as done"
msgstr "標示為全部完æˆ"
+msgid "Todos|Member access requested"
+msgstr "已請求會員存å–權é™"
+
msgid "Todos|Mentioned"
msgstr "æåŠ"
@@ -42306,14 +42930,14 @@ msgstr "沒有待辦事項,擊掌慶ç¥ï¼"
msgid "Todos|Pipelines"
msgstr "æµæ°´ç·š"
-msgid "Todos|Removed from Merge Train:"
-msgstr "已從åˆä½µåˆ—隊中移除:"
+msgid "Todos|Removed from Merge Train"
+msgstr "已從åˆä½µä½‡åˆ—中移除"
msgid "Todos|Review requested"
msgstr "已請求審核"
-msgid "Todos|The pipeline failed in"
-msgstr "æµæ°´ç·šå¤±æ•—æ–¼"
+msgid "Todos|The pipeline failed"
+msgstr "æµæ°´ç·šå¤±æ•—"
msgid "Todos|Undo mark all as done"
msgstr "撤銷標示為全部完æˆ"
@@ -42327,24 +42951,24 @@ msgstr "已經全部完æˆäº†ï¼"
msgid "Todos|Your To-Do List shows what to work on next"
msgstr "您的待辦事項列表顯示下一步è¦åšä»€éº¼"
-msgid "Todos|added a todo for"
-msgstr "已加入待辦事項"
+msgid "Todos|added a to-do item"
+msgstr "已加入一項待辦項目"
-msgid "Todos|mentioned %{who} on"
-msgstr "æåŠ %{who} æ–¼"
+msgid "Todos|has requested access"
+msgstr "已請求存å–"
-msgid "Todos|requested a review of"
-msgstr "審查è¦æ±‚"
+msgid "Todos|mentioned %{who}"
+msgstr "æåŠ %{who}"
-msgid "Todos|set %{who} as an approver for"
-msgstr "將 %{who} 設置為核准者"
+msgid "Todos|requested a review"
+msgstr "å·²è¦æ±‚審閱"
+
+msgid "Todos|set %{who} as an approver"
+msgstr "設定 %{who} 為核准者"
msgid "Todos|yourself"
msgstr "您自己"
-msgid "Todo|at %{todo_parent_path}"
-msgstr "在 %{todo_parent_path}"
-
msgid "Toggle GitLab Next"
msgstr "切æ›GitLabé è¦½ç‰ˆ"
@@ -42406,13 +43030,13 @@ msgid "Token Access"
msgstr "å­˜å–令牌(權æ–)"
msgid "Token name"
-msgstr "令牌å稱"
+msgstr "權æ–(令牌)å稱"
msgid "Token valid until revoked"
msgstr "令牌在撤銷å‰æœ‰æ•ˆ"
msgid "Tokens|Scopes set the permission levels granted to the token."
-msgstr "設定授予令牌的權é™ç´šåˆ¥ç¯„åœã€‚"
+msgstr "設定授予權æ–(令牌)的權é™ç´šåˆ¥ç¯„åœã€‚"
msgid "Tokens|Select scopes"
msgstr "é¸æ“‡ç¯„åœ"
@@ -42712,6 +43336,12 @@ msgstr "觸發器已æˆåŠŸæ›´æ–°ã€‚"
msgid "Triggerer"
msgstr "觸發者"
+msgid "Trigger|Description"
+msgstr "æè¿°"
+
+msgid "Trigger|Trigger description"
+msgstr "觸發器說明"
+
msgid "Trigger|Trigger user has insufficient permissions to project"
msgstr "觸發使用者沒有足夠的專案權é™"
@@ -42748,6 +43378,9 @@ msgstr "嘗試使用您的使用者å稱或電å­éƒµä»¶ç™»å…¥ï¼Œ 如果您忘è¨
msgid "Try out GitLab Pipelines"
msgstr "試用 GitLab æµæ°´ç·š"
+msgid "Try out the new Web IDE"
+msgstr "試用新的 Web IDE"
+
msgid "Try the troubleshooting steps here."
msgstr "嘗試此處的故障排除步驟。"
@@ -42925,6 +43558,12 @@ msgstr "無法å–得此專案的分支列表。"
msgid "Unable to fetch branches list, please close the form and try again"
msgstr "無法å–得分支列表,請關閉表單並é‡è©¦"
+msgid "Unable to fetch group. Reload the page to try again."
+msgstr "無法擷å–群組,請é‡æ–°è¼‰å…¥é é¢é‡è©¦ã€‚"
+
+msgid "Unable to fetch groups. Reload the page to try again."
+msgstr "無法擷å–群組,請é‡æ–°è¼‰å…¥é é¢é‡è©¦ã€‚"
+
msgid "Unable to fetch upstream and downstream pipelines."
msgstr "無法å–得上游和下游æµæ°´ç·šã€‚"
@@ -42983,7 +43622,7 @@ msgid "Unable to update label prioritization at this time"
msgstr "ç›®å‰ç„¡æ³•æ›´æ–°æ¨™è¨˜å„ªå…ˆåº¦"
msgid "Unable to update this epic at this time."
-msgstr "ç›®å‰ç„¡æ³•æ›´æ–°è©²å²è©©ã€‚"
+msgstr "ç›®å‰ç„¡æ³•æ›´æ–°è©²å²è©© (epic) 。"
msgid "Unable to update this issue at this time."
msgstr "ç›®å‰ç„¡æ³•æ›´æ–°è©²è­°é¡Œã€‚"
@@ -43048,6 +43687,9 @@ msgstr "很éºæ†¾ï¼Œæ‚¨ç™¼é€çµ¦GitLabçš„é›»å­éƒµä»¶ç„¡æ³•è™•ç†ã€‚"
msgid "Unhappy?"
msgstr "ä¸æ»¿æ„?"
+msgid "Units|d"
+msgstr "天"
+
msgid "Units|ms"
msgstr "毫秒"
@@ -43129,6 +43771,9 @@ msgstr "未解決的"
msgid "Unschedule job"
msgstr "å–消作業排程"
+msgid "Unselect \"Expand variable reference\" if you want to use the variable value as a raw string."
+msgstr "如果您è¦ä½¿ç”¨è®Šæ•¸å€¼ä½œç‚ºåŽŸå§‹å­—串,請å–消é¸æ“‡â€œæ“´å±•è®Šæ•¸å¼•ç”¨â€ã€‚"
+
msgid "Unstar"
msgstr "å–消收è—(星號)"
@@ -43312,6 +43957,9 @@ msgstr "正在將變更內容上傳到終端"
msgid "Uploading..."
msgstr "上傳中..."
+msgid "Uploads"
+msgstr "上傳"
+
msgid "Upstream"
msgstr "上游"
@@ -43330,18 +43978,12 @@ msgstr "容器註冊表的專案級儲存統計訊æ¯åƒ…是定å‘的,ä¸åŒ…括
msgid "UsageQuotas|This project-level storage statistic does not include savings for site-wide deduplication and is not used to calculate total namespace storage."
msgstr "這些專案級儲存統計資料ä¸åŒ…括站點範åœé‡è¤‡è³‡æ–™åˆªé™¤æ‰€ä¿ç•™çš„,並且ä¸ç”¨æ–¼ç¸½å‘½å空間儲存。"
-msgid "UsageQuota|%{help_link_start}Shared runners%{help_link_end} are disabled, so there are no limits set on pipeline usage"
-msgstr "%{help_link_start}共用執行器(Runner)%{help_link_end}å·²åœç”¨ï¼Œæ‰€ä»¥æµæ°´ç·šä½¿ç”¨æ²’有設定é™åˆ¶"
-
msgid "UsageQuota|%{linkStart}Shared runners%{linkEnd} are disabled, so there are no limits set on pipeline usage"
msgstr "%{linkStart}共享執行器%{linkEnd}被åœç”¨ï¼Œå› æ­¤å°æµæ°´ç·šçš„使用沒有設置é™åˆ¶"
msgid "UsageQuota|%{linkTitle} help link"
msgstr "%{linkTitle} 幫助連çµ"
-msgid "UsageQuota|%{percentageLeft} of purchased storage is available"
-msgstr "%{percentageLeft} 購買的儲存空間å¯ç”¨"
-
msgid "UsageQuota|%{storage_limit_link_start}A namespace storage limit%{link_end} will soon be enforced for the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}"
msgstr "%{storage_limit_link_start}命å空間儲存é™åˆ¶%{link_end} å°‡å¾ˆå¿«å° %{strong_start}%{namespace_name}%{strong_end} 命å空間實施。 %{extra_message}"
@@ -43369,9 +44011,6 @@ msgstr "CI 分é˜ä½¿ç”¨é‡ï¼ˆæŒ‰å°ˆæ¡ˆï¼‰"
msgid "UsageQuota|CI/CD minutes usage"
msgstr "CI/CD 使用分é˜æ•¸"
-msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
-msgstr "從 %{timeElapsed} 以來的 CI 分é˜ä½¿ç”¨é‡"
-
msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
msgstr "從 %{usageSince} 以來的 CI/CD 分é˜ä½¿ç”¨é‡"
@@ -43381,9 +44020,6 @@ msgstr "程å¼ç¢¼å¥—件和容器映åƒã€‚"
msgid "UsageQuota|Container Registry"
msgstr "容器註冊表"
-msgid "UsageQuota|Current period usage"
-msgstr "當å‰é€±æœŸä½¿ç”¨é‡"
-
msgid "UsageQuota|Dependency proxy"
msgstr "ä¾è³´ä»£ç†"
@@ -43510,11 +44146,8 @@ msgstr "命å空間目å‰æ­£åœ¨ä½¿ç”¨ %{strong_start}%{used_storage}%{strong_en
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
msgstr "該命å空間目å‰æ­£åœ¨ä½¿ç”¨ %{strong_start}%{used_storage}%{strong_end} 的命å空間儲存。從 %{strong_start}%{usage_quotas_nav_instruction}%{strong_end} 查看和管ç†æ‚¨çš„使用情æ³ã€‚ %{docs_link_start}了解更多%{link_end}關於如何減少您的儲存空間。"
-msgid "UsageQuota|The table below shows current period usage"
-msgstr "下表顯示了目å‰å€é–“的使用情æ³"
-
-msgid "UsageQuota|The table below shows usage since %{timeElapsed}"
-msgstr "下表顯示了自 %{timeElapsed} 以來的使用情æ³"
+msgid "UsageQuota|The table below shows usage since %{usageSince}"
+msgstr "下方表格顯示自 %{usageSince} 以來的使用情æ³"
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
msgstr "這是此命å空間中所有專案使用的儲存空間總和。"
@@ -43522,12 +44155,6 @@ msgstr "這是此命å空間中所有專案使用的儲存空間總和。"
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr "這是超出專案å…è²»%{actualRepositorySizeLimit}儲存é™åˆ¶çš„儲存空間。"
-msgid "UsageQuota|This namespace contains locked projects"
-msgstr "此命å空間包å«å·²éŽ–定的專案"
-
-msgid "UsageQuota|This namespace has no projects which use shared runners"
-msgstr "此命å空間沒有使用共用執行器(runners)的專案"
-
msgid "UsageQuota|This namespace has no projects which used shared runners in the current period"
msgstr "此命å空間目å‰æ²’有使用共享執行器的專案"
@@ -43540,9 +44167,6 @@ msgstr "已使用超é‡å„²å­˜ç©ºé–“總計"
msgid "UsageQuota|Total namespace storage used"
msgstr "已使用命å空間儲存總計"
-msgid "UsageQuota|Unlimited"
-msgstr "ç„¡é™"
-
msgid "UsageQuota|Uploads"
msgstr "上傳"
@@ -43576,8 +44200,8 @@ msgstr "使用é…é¡å¹«åŠ©é€£çµ"
msgid "UsageQuota|User settings &gt; Usage quotas"
msgstr "使用者設定 &gt; 使用é¡åº¦"
-msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
-msgstr "當您購買更多的儲存空間時,我們會自動解鎖é”到%{actualRepositorySizeLimit}é™åˆ¶æ™‚被鎖定的專案。"
+msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked if the storage limit was reached."
+msgstr "當您購買é¡å¤–的儲存空間時,我們會自動解鎖之å‰å·²é”到儲存é™åˆ¶è€ŒéŽ–定的專案。"
msgid "UsageQuota|Wiki"
msgstr "Wiki"
@@ -43585,17 +44209,17 @@ msgstr "Wiki"
msgid "UsageQuota|Wiki content."
msgstr "Wiki 內容。"
-msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
-msgstr "您已經用盡所有更多的儲存空間,請購買更多空間以解鎖超éŽå…è²»%{actualRepositorySizeLimit}é™åˆ¶çš„專案。"
+msgid "UsageQuota|You have consumed all of your additional storage. Purchase more to unlock projects over the limit."
+msgstr "您已用完所有é¡å¤–儲存空間,請購買更多空間以解鎖超éŽé™åˆ¶çš„專案。"
-msgid "UsageQuota|You have reached the free storage limit of %{actualRepositorySizeLimit} on %{projectsLockedText}. To unlock them, please purchase additional storage."
-msgstr "您已經在%{projectsLockedText}é”到儲存空間é™åˆ¶%{actualRepositorySizeLimit}。如需解鎖,請購買更多儲存空間。"
+msgid "UsageQuota|You have reached the free storage limit on %{projectsLockedText}. To unlock them, purchase additional storage."
+msgstr "您已é”到 %{projectsLockedText} çš„å…費儲存é™åˆ¶ï¼Œè¦è§£éŽ–它們,請購買é¡å¤–的儲存空間。"
msgid "UsageQuota|You used: %{usage} %{limit}"
msgstr "您已使用: %{usage} %{limit}"
-msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
-msgstr "您購買的儲存空間已ä¸è¶³ã€‚為了é¿å…專案被鎖定,請購買更多儲存空間。"
+msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, purchase more storage."
+msgstr "您購買的儲存空間ä¸è¶³ï¼Œç‚ºé¿å…專案被鎖定,請購買更多的儲存空間。"
msgid "UsageQuota|out of %{formattedLimit} of your namespace storage"
msgstr "超éŽæ‚¨å‘½å空間儲存的%{formattedLimit}"
@@ -43640,7 +44264,7 @@ msgid "UsageTrends|Pipelines failed"
msgstr "æµæ°´ç·šå¤±æ•—"
msgid "UsageTrends|Pipelines skipped"
-msgstr "æµæ°´ç·šå·²è·³éŽ"
+msgstr "æµæ°´ç·šå·²ç•¥éŽ"
msgid "UsageTrends|Pipelines succeeded"
msgstr "æµæ°´ç·šæˆåŠŸ"
@@ -43768,6 +44392,9 @@ msgstr "使用公共雲實例 URL (%{kroki_public_url}) 或 %{install_link_start
msgid "Use the search bar on the top of this page"
msgstr "使用本é é ‚部的æœå°‹æ¬„"
+msgid "Use this form to report to the administrator users who create spam issues, comments or behave inappropriately."
+msgstr "使用該表單å‘å¯ä»¥å»ºç«‹åžƒåœ¾éƒµä»¶è­°é¡Œã€ç•™è¨€è©•è«–或行為ä¸ç•¶çš„系統管ç†ä½¿ç”¨è€…æ報。"
+
msgid "Use this token to validate received payloads."
msgstr "使用此令牌來驗證收到的有效資料。"
@@ -43917,7 +44544,7 @@ msgid "UserLists|Create"
msgstr "建立"
msgid "UserLists|Define a set of users to be used within feature flag strategies"
-msgstr "定義在功能標誌策略中使用的一組使用者"
+msgstr "定義在特性標籤策略中使用的一組使用者"
msgid "UserLists|Edit"
msgstr "編輯"
@@ -43926,16 +44553,16 @@ msgid "UserLists|Edit %{name}"
msgstr "編輯%{name}"
msgid "UserLists|Enter a comma separated list of user IDs. These IDs should be the users of the system in which the feature flag is set, not GitLab IDs"
-msgstr "輸入一個逗號分隔的使用者ID列表。這些ID應該是設定使用功能標誌的系統的使用者,而ä¸æ˜¯GitLab ID"
+msgstr "輸入一個逗號分隔的使用者ID列表。這些ID應該是設定使用特性標籤的系統的使用者,而ä¸æ˜¯GitLab ID"
msgid "UserLists|Feature flag user list"
-msgstr "功能標誌使用者列表"
+msgstr "特性標籤使用者列表"
msgid "UserLists|Get started with user lists"
msgstr "開始使用使用者列表"
msgid "UserLists|Lists allow you to define a set of users to be used with feature flags. %{linkStart}Read more about feature flag lists.%{linkEnd}"
-msgstr "列表å…許您定義一組用於功能標誌的使用者。 %{linkStart}閱讀更多關於功能標誌列表的訊æ¯ã€‚%{linkEnd}"
+msgstr "列表å…許您定義一組用於特性標籤的使用者。 %{linkStart}閱讀更多關於特性標籤列表的訊æ¯ã€‚%{linkEnd}"
msgid "UserLists|Loading user lists"
msgstr "載入使用者列表"
@@ -43968,7 +44595,7 @@ msgid "UserLists|User Lists"
msgstr "使用者列表"
msgid "UserLists|User lists allow you to define a set of users to use with Feature Flags."
-msgstr "使用者列表å…許您定義一組與功能標誌一起使用的使用者。"
+msgstr "使用者列表å…許您定義一組與特性標籤一起使用的使用者。"
msgid "UserList|Delete %{name}?"
msgstr "刪除%{name}嗎?"
@@ -44033,9 +44660,6 @@ msgstr "個人專案"
msgid "UserProfile|Pronounced as: %{pronunciation}"
msgstr "讀作: %{pronunciation}"
-msgid "UserProfile|Report abuse"
-msgstr "舉報濫用行為"
-
msgid "UserProfile|Retry"
msgstr "é‡è©¦"
@@ -44225,6 +44849,9 @@ msgstr "當缺少加密欄ä½æ™‚,使用必è¦çš„加密策略ï¼"
msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
msgstr "使用%{codeStart}needs%{codeEnd}é—œéµå­—讓作業項比其所在階段更早執行。åªè¦å®ƒå€‘çš„%{codeStart}needs%{codeEnd}關係得到滿足,作業將盡快執行,從而實ç¾æµæ°´ç·šåŠ é€Ÿã€‚"
+msgid "VS Code in your browser. View code and make changes from the same UI as in your local IDE 🎉"
+msgstr "ç€è¦½å™¨ä¸­çš„ VS 程å¼ç¢¼ï¼Œå¾žèˆ‡æœ¬æ©Ÿ IDE 相åŒçš„ UI 查看程å¼ç¢¼ä¸¦é€²è¡Œä¿®æ”¹ 🎉"
+
msgid "Valid From"
msgstr "有效期自"
@@ -44297,6 +44924,9 @@ msgstr "DORA(DevOps Research and Assessment) 指標"
msgid "ValueStreamAnalytics|Dashboard"
msgstr "儀表æ¿"
+msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
+msgstr "編輯 Value Stream:%{name}"
+
msgid "ValueStreamAnalytics|Go to docs"
msgstr "å‰å¾€æ–‡ä»¶"
@@ -44315,6 +44945,9 @@ msgstr "從議題建立到關閉的時間中ä½æ•¸ã€‚"
msgid "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed."
msgstr "從一個關è¯è­°é¡Œçš„åˆä½µè«‹æ±‚最早æ交到該議題çµæŸçš„時間中ä½æ•¸ã€‚"
+msgid "ValueStreamAnalytics|New Value Stream"
+msgstr "新建 Value Stream"
+
msgid "ValueStreamAnalytics|Number of commits pushed to the default branch"
msgstr "推é€åˆ°é è¨­åˆ†æ”¯çš„æ交數é‡"
@@ -44363,12 +44996,12 @@ msgstr "åœæ­¢"
msgid "ValueStream|The Default Value Stream cannot be deleted"
msgstr "é è¨­åƒ¹å€¼æµä¸å¯åˆªé™¤"
-msgid "Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}"
-msgstr "åŒ…å« %{codeStart}$%{codeEnd} 字元的值å¯ä»¥è¢«è¦–為變數引用並進行擴展。%{docsLinkStart}了解更多。%{docsLinkEnd}"
-
msgid "Variable"
msgstr "變數"
+msgid "Variable value will be evaluated as raw string."
+msgstr "變數值將被評估為原始字串。"
+
msgid "Variable will be masked in job logs."
msgstr "變數值將在作業日誌中被隱è—。"
@@ -44378,8 +45011,8 @@ msgstr "變數"
msgid "Variables can be:"
msgstr "變數å¯ä»¥æ˜¯ï¼š"
-msgid "Variables store information, like passwords and secret keys, that you can use in job scripts."
-msgstr "變數儲存訊æ¯ï¼Œå¦‚密碼和金鑰,您å¯ä»¥åœ¨ä½œæ¥­è…³æœ¬ä¸­ä½¿ç”¨ã€‚"
+msgid "Variables can have several attributes."
+msgstr "變數å¯ä»¥æœ‰å¤šå€‹å±¬æ€§ã€‚"
msgid "Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables."
msgstr "變數儲存訊æ¯ï¼Œå¦‚密碼和金鑰,您å¯ä»¥åœ¨ä½œæ¥­è…³æœ¬ä¸­ä½¿ç”¨ã€‚實例上的所有專案都å¯ä»¥ä½¿ç”¨é€™äº›è®Šæ•¸ã€‚"
@@ -44435,6 +45068,21 @@ msgstr "版本 %{versionNumber}"
msgid "Version %{versionNumber} (latest)"
msgstr "版本 %{versionNumber} (最新)"
+msgid "VersionCheck|%{details}"
+msgstr "%{details}"
+
+msgid "VersionCheck|Critical security upgrade available"
+msgstr "å¯ç”¨çš„é—œéµå®‰å…¨æ›´æ–°"
+
+msgid "VersionCheck|Important notice - Critical security release"
+msgstr "é‡è¦é€šçŸ¥ - é—œéµå®‰å…¨ç™¼å¸ƒ"
+
+msgid "VersionCheck|Learn more about this critical security release."
+msgstr "了解關於該é‡è¦å®‰å…¨ç‰ˆæœ¬çš„更多資訊。"
+
+msgid "VersionCheck|Remind me again in 3 days"
+msgstr "3 天後å†æ¬¡æ醒我"
+
msgid "VersionCheck|Up to date"
msgstr "最新"
@@ -44444,6 +45092,18 @@ msgstr "盡快更新"
msgid "VersionCheck|Update available"
msgstr "å¯ç”¨æ›´æ–°"
+msgid "VersionCheck|Upgrade now"
+msgstr "ç«‹å³å‡ç´š"
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation immediately."
+msgstr "您當å‰ä½¿ç”¨çš„是 %{currentVersion} 版本ï¼æˆ‘們強烈建議立å³å‡ç´šæ‚¨å®‰è£çš„ GitLab 。"
+
+msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}."
+msgstr "您當å‰ä½¿ç”¨çš„是 %{currentVersion} 版本ï¼æˆ‘們強烈建議您立å³å°‡å®‰è£çš„ GitLab å‡ç´šåˆ°ä¸‹åˆ—版本之一:%{latestStableVersions}。"
+
+msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}"
+msgstr "您當å‰ä½¿ç”¨çš„版本是 %{currentVersion},我們強烈建議å‡ç´šæ‚¨å®‰è£çš„ GitLab 。 %{link}"
+
msgid "VersionCheck|Your GitLab Version"
msgstr "您的 GitLab 版本"
@@ -45060,7 +45720,7 @@ msgid "Watch how"
msgstr "觀看如何使用"
msgid "We also use email for avatar detection if no avatar is uploaded."
-msgstr "若未上傳頭貼,我們也會利用電å­ä¿¡ç®±æ‰¾å‡ºé ­è²¼ã€‚"
+msgstr "若未上傳頭åƒï¼Œæˆ‘們也會利用電å­ä¿¡ç®±æ‰¾å‡ºé ­åƒã€‚"
msgid "We are currently unable to fetch data for the pipeline header."
msgstr "我們目å‰ç„¡æ³•å–å¾—æ­¤æµæ°´ç·šæ¨™é ­(header)的資料。"
@@ -45069,7 +45729,7 @@ msgid "We are currently unable to fetch data for this graph."
msgstr "我們目å‰ç„¡æ³•å–得此圖表的資料。"
msgid "We could not determine the path to remove the epic"
-msgstr "我們無法確定移除å²è©©çš„路徑"
+msgstr "我們無法確定移除å²è©© (epic) 的路徑"
msgid "We could not determine the path to remove the issue"
msgstr "我們無法確定移除議題的路徑"
@@ -45101,6 +45761,9 @@ msgstr "我們在%{humanized_resource_name}檢測到潛在濫用行為。請輸å
msgid "We don't have enough data to show this stage."
msgstr "該階段的資料ä¸è¶³ï¼Œç„¡æ³•é¡¯ç¤ºã€‚"
+msgid "We found your token in a public project and have automatically revoked it to protect your account."
+msgstr "我們在公開專案中發ç¾äº†æ‚¨çš„權æ–(令牌),並且已自動將其撤銷以ä¿è­·æ‚¨çš„帳號。"
+
msgid "We have found the following errors:"
msgstr "我們發ç¾ä»¥ä¸‹éŒ¯èª¤ï¼š"
@@ -45176,9 +45839,6 @@ msgstr "WebAuthn設備 (%{length})"
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr "WebAuthnåªæ”¯æ´å•Ÿç”¨äº†HTTPS的網站。您å¯ä»¥è¯çµ¡ç®¡ç†å“¡ç²å¾—更多訊æ¯"
-msgid "WebIDE|Are you sure you want to switch editors? You will lose any unsaved changes."
-msgstr "您確定è¦åˆ‡æ›ç·¨è¼¯å™¨å—Žï¼Ÿæ‚¨å°‡æœƒå¤±åŽ»æ‰€æœ‰æœªå„²å­˜çš„變更。"
-
msgid "WebIDE|Fork project"
msgstr "分å‰ï¼ˆFork)專案"
@@ -45194,24 +45854,12 @@ msgstr "快速且輕鬆地編輯專案中的多個文件。"
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr "快速且輕鬆地編輯專案中的多個文件,按 . 來打開"
-msgid "WebIDE|Ready for something new?"
-msgstr "準備好迎接新事物了嗎?"
-
-msgid "WebIDE|Something went wrong while updating the user preferences. Please see developer console for details."
-msgstr "更新使用者å好設定時發生錯誤,詳情請查看開發者主控å°ã€‚"
-
-msgid "WebIDE|Switch to new Web IDE"
-msgstr "切æ›åˆ°æ–°çš„ Web IDE"
-
msgid "WebIDE|This project does not accept unsigned commits."
msgstr "此專案ä¸æŽ¥å—未簽åçš„æ交。"
msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
msgstr "此專案ä¸æŽ¥å—未簽åçš„æäº¤ï¼Œæ‚¨ç„¡æ³•é€šéŽ Web IDE æ交更改。"
-msgid "WebIDE|You are invited to experience the new Web IDE."
-msgstr "誠摯邀請您體驗新的 Web IDE。"
-
msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
msgstr "您ä¸èƒ½åœ¨é€™å€‹å°ˆæ¡ˆä¸­ç›´æŽ¥ç·¨è¼¯æ–‡ä»¶ï¼Œè«‹åˆ†å‰ï¼ˆFork)這個專案並æ交åˆä½µè«‹æ±‚。"
@@ -45242,6 +45890,18 @@ msgstr "Webhook設定"
msgid "Webhook events will be displayed here."
msgstr "Webhook事件將在這裡顯示。"
+msgid "Webhook was created"
+msgstr "已建立 Webhook"
+
+msgid "Webhook was deleted"
+msgstr "已刪除 Webhook"
+
+msgid "Webhook was scheduled for deletion"
+msgstr "Webhook 已列入刪除排程"
+
+msgid "Webhook was updated"
+msgstr "已更新 Webhook"
+
msgid "Webhook:"
msgstr "Webhook:"
@@ -45267,7 +45927,7 @@ msgid "Webhooks|A deployment starts, finishes, fails, or is canceled."
msgstr "部署開始ã€å®Œæˆã€å¤±æ•—或被å–消。"
msgid "Webhooks|A feature flag is turned on or off."
-msgstr "一個功能標誌已打開或關閉。"
+msgstr "一個特性標籤已打開或關閉。"
msgid "Webhooks|A group member is created, updated, or removed."
msgstr "已建立ã€æ›´æ–°æˆ–移除群組æˆå“¡ã€‚"
@@ -45308,9 +45968,6 @@ msgstr "您確實需è¦åˆªé™¤é€™å€‹å°ˆæ¡ˆ hook 嗎?"
msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr "您確實需è¦åˆªé™¤é€™å€‹ webhook 嗎?"
-msgid "Webhooks|Comments"
-msgstr "留言"
-
msgid "Webhooks|Confidential comments"
msgstr "機密留言"
@@ -45336,7 +45993,7 @@ msgid "Webhooks|Fails to connect"
msgstr "連接失敗"
msgid "Webhooks|Feature flag events"
-msgstr "功能標誌事件"
+msgstr "特性標籤事件"
msgid "Webhooks|Go to webhooks"
msgstr "到 webhooks"
@@ -45359,15 +46016,12 @@ msgstr "æˆå“¡äº‹ä»¶"
msgid "Webhooks|Merge request events"
msgstr "åˆä½µè«‹æ±‚事件"
+msgid "Webhooks|Must match part of URL"
+msgstr "å¿…é ˆåŒ¹é… URL 的一部分"
+
msgid "Webhooks|Pipeline events"
msgstr "æµæ°´ç·šäº‹ä»¶"
-msgid "Webhooks|Push events"
-msgstr "推é€äº‹ä»¶"
-
-msgid "Webhooks|Push to the repository."
-msgstr "推é€åˆ°ç‰ˆæœ¬åº«ã€‚"
-
msgid "Webhooks|Regex such as %{REGEX_CODE} is supported."
msgstr "æ”¯æ´ %{REGEX_CODE} 等正則表é”å¼ã€‚"
@@ -45404,9 +46058,6 @@ msgstr "Webhook 連接失敗,已åœç”¨ã€‚è¦é‡æ–°å•Ÿç”¨å®ƒï¼Œè«‹æª¢æŸ¥ %{stro
msgid "Webhooks|Trigger"
msgstr "觸發器"
-msgid "Webhooks|URL"
-msgstr "網å€"
-
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
msgstr "如果 URL 包å«ä¸€å€‹æˆ–多個特殊字元,則必須進行百分號編碼。"
@@ -45683,7 +46334,7 @@ msgid "WikiEmpty|You must be a project member in order to add wiki pages."
msgstr "åªæœ‰å°ˆæ¡ˆæˆå“¡æ‰å¯ä»¥åŠ å…¥ Wiki é é¢ã€‚"
msgid "WikiEmpty|You've enabled the Confluence Workspace integration. Your wiki will be viewable directly within Confluence. We are hard at work integrating Confluence more seamlessly into GitLab. If you'd like to stay up to date, follow our %{wiki_confluence_epic_link_start}Confluence epic%{wiki_confluence_epic_link_end}."
-msgstr "您已啟用Confluence Workspaceæ•´åˆã€‚您的Wikiå°‡å¯ç›´æŽ¥åœ¨Confluence中查看。我們正在努力將Confluence更無縫地整åˆåˆ°GitLab中。如果您想了解最新訊æ¯ï¼Œè«‹é—œæ³¨æˆ‘們的%{wiki_confluence_epic_link_start}Confluenceå²è©©%{wiki_confluence_epic_link_end}。"
+msgstr "您已啟用Confluence Workspaceæ•´åˆã€‚您的Wikiå°‡å¯ç›´æŽ¥åœ¨Confluence中查看。我們正在努力將Confluence更無縫地整åˆåˆ°GitLab中。如果您想了解最新訊æ¯ï¼Œè«‹é—œæ³¨æˆ‘們的%{wiki_confluence_epic_link_start}Confluence epic%{wiki_confluence_epic_link_end}。"
msgid "WikiHistoricalPage|This is an old version of this page."
msgstr "這是此é é¢çš„éŽæœŸç‰ˆæœ¬ã€‚"
@@ -45796,6 +46447,9 @@ msgstr "將被建立"
msgid "Will be mapped to"
msgstr "將被映射到"
+msgid "Will be released"
+msgstr "å³å°‡è¢«ç™¼å¸ƒ"
+
msgid "Will deploy to"
msgstr "將部署到"
@@ -45805,9 +46459,6 @@ msgstr "線框稿"
msgid "With requirements, you can set criteria to check your products against."
msgstr "根據è¦æ±‚,您å¯ä»¥è¨­å®šæ¨™æº–來檢查您的產å“。"
-msgid "With test cases, you can define conditions for your project to meet in determining quality"
-msgstr "使用測試案例,您å¯ä»¥å®šç¾©å°ˆæ¡ˆçš„æ¢ä»¶ä»¥æ±ºå®šè³ªé‡"
-
msgid "Withdraw Access Request"
msgstr "å–消權é™ç”³è«‹"
@@ -45826,6 +46477,12 @@ msgstr "%{workItemType} 已刪除"
msgid "WorkItem|Add"
msgstr "新增"
+msgid "WorkItem|Add %{workItemType}"
+msgstr "加入 %{workItemType}"
+
+msgid "WorkItem|Add %{workItemType}s"
+msgstr "加入 %{workItemType}"
+
msgid "WorkItem|Add a title"
msgstr "增加標題"
@@ -45841,9 +46498,6 @@ msgstr "加入截止日期"
msgid "WorkItem|Add start date"
msgstr "加入開始日期"
-msgid "WorkItem|Add task"
-msgstr "增加任務"
-
msgid "WorkItem|Add to iteration"
msgstr "加入到迭代"
@@ -45863,6 +46517,9 @@ msgstr[0] "指派者"
msgid "WorkItem|Cancel"
msgstr "å–消"
+msgid "WorkItem|Child objectives and key results"
+msgstr "次目標和關éµçµæžœ"
+
msgid "WorkItem|Child removed"
msgstr "已移除å­é …"
@@ -45872,6 +46529,12 @@ msgstr "已關閉"
msgid "WorkItem|Collapse tasks"
msgstr "收折任務"
+msgid "WorkItem|Create %{workItemType}"
+msgstr "建立 %{workItemType}"
+
+msgid "WorkItem|Create objective"
+msgstr "建立目標"
+
msgid "WorkItem|Create task"
msgstr "建立任務"
@@ -45884,30 +46547,39 @@ msgstr "日期"
msgid "WorkItem|Delete %{workItemType}"
msgstr "刪除 %{workItemType}"
+msgid "WorkItem|Discard changes"
+msgstr "放棄變更"
+
msgid "WorkItem|Due date"
msgstr "截止日期"
+msgid "WorkItem|Existing task"
+msgstr "ç¾æœ‰ä»»å‹™"
+
msgid "WorkItem|Expand tasks"
msgstr "展開任務"
msgid "WorkItem|Incident"
msgstr "事故"
-msgid "WorkItem|Introducing tasks"
-msgstr "任務介紹"
-
msgid "WorkItem|Issue"
msgstr "議題"
msgid "WorkItem|Iteration"
msgstr "迭代"
-msgid "WorkItem|Learn about tasks."
-msgstr "了解任務。"
+msgid "WorkItem|Key result"
+msgstr "é—œéµçµæžœ"
msgid "WorkItem|Milestone"
msgstr "里程碑"
+msgid "WorkItem|New objective"
+msgstr "新增目標"
+
+msgid "WorkItem|New task"
+msgstr "新增任務"
+
msgid "WorkItem|No iteration"
msgstr "沒有迭代"
@@ -45917,12 +46589,18 @@ msgstr "沒有符åˆçš„çµæžœ"
msgid "WorkItem|No milestone"
msgstr "沒有里程碑"
+msgid "WorkItem|No objectives or key results are currently assigned."
+msgstr "ç›®å‰æ²’有被指派的目標或關éµçµæžœã€‚"
+
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr "ç›®å‰æœªè¢«æŒ‡æ´¾ä»»å‹™ï¼Œä½¿ç”¨ä»»å‹™å°‡è©²è­°é¡Œæ‹†åˆ†æˆæ›´å°çš„部分。"
msgid "WorkItem|None"
msgstr "ç„¡"
+msgid "WorkItem|Objective"
+msgstr "目標"
+
msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr "åªæœ‰è‡³å°‘具有報告者角色的專案æˆå“¡ã€ä½œè€…å’Œå—指派者å¯ä»¥æŸ¥çœ‹æˆ–收到有關此任務的通知。"
@@ -45935,9 +46613,18 @@ msgstr "移除"
msgid "WorkItem|Requirements"
msgstr "需求"
+msgid "WorkItem|Save and overwrite"
+msgstr "儲存並覆蓋"
+
+msgid "WorkItem|Search existing %{workItemType}s"
+msgstr "查找ç¾æœ‰çš„ %{workItemType}"
+
msgid "WorkItem|Select type"
msgstr "é¸æ“‡é¡žåž‹"
+msgid "WorkItem|Someone edited the description at the same time you did. If you save it will overwrite their changes. Please confirm you'd like to save your edits."
+msgstr "有人與您åŒæ™‚編輯了該æ述,如果您儲存它將覆蓋他們的更改,請確èªæ‚¨è¦å„²å­˜æ‚¨çš„編輯。"
+
msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr "建立 %{workItemType} 時發生錯誤,請é‡è©¦ã€‚"
@@ -46001,8 +46688,8 @@ msgstr "開啟機密性"
msgid "WorkItem|Undo"
msgstr "復原"
-msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
-msgstr "使用任務將您議題中的工作分解æˆæ›´å°çš„部分, %{learnMoreLink}"
+msgid "WorkItem|View current version"
+msgstr "查看當å‰ç‰ˆæœ¬"
msgid "WorkItem|Work Items"
msgstr "工作事項"
@@ -46031,6 +46718,9 @@ msgstr "發表留言..."
msgid "Write a description or drag your files here…"
msgstr "撰寫æ述或將您的文件拖到此處…"
+msgid "Write a description..."
+msgstr "撰寫æ述說明..."
+
msgid "Write a description…"
msgstr "撰寫æè¿°..."
@@ -46197,11 +46887,11 @@ msgstr "您以該身份æ›å…¥GitLab:"
msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
msgstr "您正在嘗試上傳éžåœ–片文件。請上傳.pngã€.jpgã€.jpegã€.gifã€.bmpã€.tiff或.ico。"
-msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
-msgstr "您å¯ä»¥%{gitlabLinkStart}在 GitLab 上解決è¡çª%{gitlabLinkEnd} 或 %{resolveLocallyStart}本機解決%{resolveLocallyEnd}。"
+msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
+msgstr "您å¯ä»¥ %{gitlabLinkStart} 在 GitLab 上解決è¡çª %{gitlabLinkEnd} 或 %{resolveLocallyStart} 在本機解決 %{resolveLocallyEnd}。"
-msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
-msgstr "您å¯ä»¥ %{resolveLocallyStart}在本機解決它%{resolveLocallyEnd}。"
+msgid "You can %{resolveLocallyStart}resolve them locally%{resolveLocallyEnd}."
+msgstr "您å¯ä»¥ %{resolveLocallyStart}在本機解決它們 %{resolveLocallyEnd}。"
msgid "You can adjust rules on auto-banning %{link_start}here%{link_end}."
msgstr "您å¯ä»¥åœ¨ %{link_start}此處%{link_end}調整自動ç¦æ­¢è¦å‰‡ã€‚"
@@ -46239,10 +46929,6 @@ msgstr "您å¯ä»¥ç¨å¾Œæ›´æ”¹æ‚¨çš„ URL"
msgid "You can always edit this later"
msgstr "您也å¯ä»¥ç¨å¾Œç·¨è¼¯æ­¤é¸é …。"
-msgid "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} member has %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} member who maintains access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} member. The remaining members will get a status of Over limit and lose access to the group."
-msgid_plural "You can begin moving members in %{namespace_name} now. A member loses access to the group when you turn off %{strong_start}In a seat%{strong_end}. If over %{free_user_limit} members have %{strong_start}In a seat%{strong_end} enabled after October 19, 2022, we'll select the %{free_user_limit} members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach %{free_user_limit} members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr[0] "您ç¾åœ¨å¯ä»¥é–‹å§‹å°‡æˆå“¡ç§»å‹•åˆ° %{namespace_name},當您關閉%{strong_start}席次內%{strong_end}é¸é …時,æˆå“¡å°‡å¤±åŽ»å°ç¾¤çµ„çš„å­˜å–權é™ã€‚ 如果在 2022 å¹´ 10 月 19 æ—¥ä¹‹å¾Œè¶…éŽ %{free_user_limit} 個æˆå“¡å•Ÿç”¨äº†%{strong_start}席次內%{strong_end}é¸é …,我們將é¸æ“‡ç¶­æŒå­˜å–權é™çš„%{free_user_limit}個æˆå“¡ã€‚我們將首先計算具有æ“有者和維護者角色的æˆå“¡ï¼Œç„¶å¾Œæ˜¯è¿‘期較為活èºçš„æˆå“¡ï¼Œç›´åˆ°é”到 %{free_user_limit} 個æˆå“¡ï¼Œå…¶é¤˜æˆå“¡å°‡è¢«è¨­å®šç‚ºâ€œè¶…出é™åˆ¶â€ç‹€æ…‹ï¼Œä¸¦ä¸”將無法存å–該群組。"
-
msgid "You can check it in your %{pat_link_start}personal access tokens%{pat_link_end} settings."
msgstr "您å¯ä»¥åœ¨ %{pat_link_start}個人存å–令牌%{pat_link_end} 中查看它。"
@@ -46280,7 +46966,7 @@ msgid "You can create a new one or check them in your personal access tokens set
msgstr "您å¯ä»¥åœ¨æ‚¨çš„個人存å–令牌設定%{pat_link}中建立一個新的令牌或檢查它們。"
msgid "You can create new ones at your %{pat_link_start}Personal Access Tokens%{pat_link_end} settings"
-msgstr "您å¯ä»¥åœ¨%{pat_link_start}個人存å–令牌(權æ–)%{pat_link_end}設定中建立新的令牌(權æ–)"
+msgstr "您å¯ä»¥åœ¨%{pat_link_start}個人存å–權æ–(令牌)%{pat_link_end}設定中建立新的權æ–(令牌)"
msgid "You can create new ones at your Personal Access Tokens settings %{pat_link}"
msgstr "您å¯ä»¥åœ¨å€‹äººå­˜å–令牌(權æ–)設定%{pat_link}中建立新的存å–令牌(權æ–)。"
@@ -46309,9 +46995,6 @@ msgstr "您å¯ä»¥åœ¨ %{subscriptions_doc_link} 找到更多關於 GitLab 訂閱ç
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
msgstr "您å¯ä»¥é€šéŽè¤‡è£½ (Clone) 版本庫開始,或使用以下方å¼ä¹‹ä¸€åŠ å…¥æ–‡ä»¶ã€‚"
-msgid "You can group test cases using labels. To learn about the future direction of this feature, visit %{linkStart}Quality Management direction page%{linkEnd}."
-msgstr "您å¯ä»¥ä½¿ç”¨æ¨™ç±¤å°æ¸¬è©¦æ¡ˆä¾‹é€²è¡Œåˆ†çµ„。è¦äº†è§£æ­¤åŠŸèƒ½çš„未來發展方å‘ï¼Œè«‹å­˜å– %{linkStart}質é‡ç®¡ç†æ–¹å‘é %{linkEnd}。"
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "您å¯ä»¥é‚€è«‹æ–°æˆå“¡æˆ–å¦ä¸€å€‹ç¾¤çµ„加入%{project_name} 。"
@@ -46330,18 +47013,12 @@ msgstr "您å¯ä»¥é€šéŽé›»å­éƒµä»¶ä¾†é€šçŸ¥æ‡‰ç”¨ç¨‹å¼/群組或專案"
msgid "You can now close this window."
msgstr "您ç¾åœ¨å¯ä»¥é—œé–‰æ­¤çª—å£ã€‚"
-msgid "You can now export your security dashboard to a CSV report."
-msgstr "ç¾åœ¨æ‚¨å¯ä»¥åŒ¯å‡ºå®‰å…¨å„€è¡¨æ¿åˆ° CSV 報告。"
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr "您ç¾åœ¨å¯ä»¥æ交åˆä½µè«‹æ±‚以將此更改發é€åˆ°ä¾†æºåˆ†æ”¯ã€‚"
msgid "You can now submit a merge request to get this change into the original project."
msgstr "您ç¾åœ¨å¯ä»¥æ交åˆä½µè«‹æ±‚以將此更改加入到來æºå°ˆæ¡ˆä¸­ã€‚"
-msgid "You can only %{action} files when you are on a branch"
-msgstr "當處於分支時,您åªèƒ½ %{action} 文件"
-
msgid "You can only add up to %{max_contacts} contacts at one time"
msgstr "您一次最多åªèƒ½åŠ å…¥ %{max_contacts} 個è¯çµ¡äºº"
@@ -46382,7 +47059,7 @@ msgid "You cannot access the raw file. Please wait a minute."
msgstr "您ä¸èƒ½å­˜å–原始文件。請ç¨å€™ã€‚"
msgid "You cannot add any more epics. This epic already has maximum number of child epics."
-msgstr "您ä¸èƒ½å†å¢žåŠ ä»»ä½•å²è©©ï¼Œè©²å²è©©å·²ç¶“æ“有最大數é‡çš„å­å²è©©ã€‚"
+msgstr "您ä¸èƒ½å†å¢žåŠ ä»»ä½•å²è©© (epics),該å²è©© (epic) 已經æ“有最大數é‡çš„å­å²è©© (epics)。"
msgid "You cannot approve your own deployment."
msgstr "您ä¸èƒ½æ ¸å‡†æ‚¨è‡ªå·±çš„部署。"
@@ -46423,6 +47100,9 @@ msgstr "您ä¸èƒ½åœ¨é€™å€‹å°ˆæ¡ˆä¸­ç›´æŽ¥ç·¨è¼¯æ–‡ä»¶ï¼Œè«‹åˆ†å‰ï¼ˆFork)這å€
msgid "You could not create a new trigger."
msgstr "您無法建立新的觸發器。"
+msgid "You do not have access to any projects for creating incidents."
+msgstr "您無權存å–任何用於建立事故的專案。"
+
msgid "You do not have any subscriptions yet"
msgstr "您目å‰å°šæœªæœ‰ä»»ä½•è¨‚é–±"
@@ -46477,6 +47157,9 @@ msgstr "您沒有任何近期的æœå°‹"
msgid "You don't have permission to review this deployment. Contact the project or group owner for help."
msgstr "您無權查看此部署。è¯çµ¡å°ˆæ¡ˆæˆ–群組所有者尋求幫助。"
+msgid "You don't have permission to view this epic"
+msgstr "您無權查看該å²è©© (epic) "
+
msgid "You don't have permissions to create this project"
msgstr "您無權建立此專案"
@@ -46538,6 +47221,9 @@ msgstr "您沒有足夠的權é™ç‚ºæ­¤å°ˆæ¡ˆå»ºç«‹HTTPæ•´åˆ"
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr "您沒有足夠的權é™ä¾†å»ºç«‹æ­¤å°ˆæ¡ˆçš„ on-call 排程"
+msgid "You have insufficient permissions to manage alerts for this project"
+msgstr "您的權é™ä¸è¶³ä»¥ç®¡ç†è©²å°ˆæ¡ˆçš„警示"
+
msgid "You have insufficient permissions to manage resource links for this incident"
msgstr "您沒有足夠的權é™ç®¡ç†æ­¤äº‹æ•…的資æºéˆçµ"
@@ -46589,9 +47275,6 @@ msgstr "您已經為您的帳號設定了雙因å­(2FA)é©—è­‰ï¼ å¦‚æžœæ‚¨ç„¡æ³•
msgid "You have successfully purchased %{product}. You'll receive a receipt by email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
msgstr "您已æˆåŠŸè³¼è²· %{product}。您將通éŽé›»å­éƒµä»¶æ”¶åˆ°æ”¶æ“šã€‚您的購買å¯èƒ½éœ€è¦ä¸€åˆ†é˜æ‰èƒ½åŒæ­¥ï¼Œå¦‚果您還沒有看到,請é‡æ–°æ•´ç†é é¢ã€‚"
-msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. Your purchase may take a minute to sync, so refresh the page if you don't see it yet."
-msgstr "您已æˆåŠŸè³¼è²· %{seats} çš„ %{plan} 方案訂閱。您將會é€éŽé›»å­éƒµä»¶æ”¶åˆ°æ”¶æ“šã€‚您的購買å¯èƒ½éœ€è¦ä¸€åˆ†é˜åŒæ­¥ï¼Œè‹¥æ‚¨é‚„沒看到,請é‡æ–°æ•´ç†é é¢ã€‚"
-
msgid "You have unsaved changes"
msgstr "您有未儲存的變更"
@@ -46769,6 +47452,9 @@ msgstr "您已經使用一次密碼驗證器來啟用了雙因å­èªè­‰ã€‚如果
msgid "You've rejected %{user}"
msgstr "您拒絕了 %{user}"
+msgid "You've successfully purchased the %{plan} plan subscription for %{seats} and you'll receive a receipt by email. Your purchase may take a minute to sync, refresh the page if your subscription details haven't displayed yet."
+msgstr "您已æˆåŠŸè³¼è²· %{seats} çš„ %{plan} 訂閱計劃,您將通éŽé›»å­éƒµä»¶æ”¶åˆ°æ”¶æ“šã€‚您的購買å¯èƒ½éœ€è¦ä¸€åˆ†é˜æ‰èƒ½åŒæ­¥ï¼Œå¦‚果您的訂閱詳細資訊尚未顯示,請刷新該é é¢ã€‚"
+
msgid "YouTube"
msgstr "YouTube"
@@ -46787,8 +47473,8 @@ msgstr "您的 %{strong}%{plan_name}%{strong_close} 訂閱將於 %{strong}%{expi
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr "您為%{strong}%{namespace_name}%{strong_close}的%{strong}%{plan_name}%{strong_close}訂閱將於%{strong}%{expires_on}%{strong_close}到期。"
-msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details."
-msgstr "您的 CI/CD 設定語法無效。查看 Lint é ç±¤ä»¥å–得更多詳細訊æ¯ã€‚"
+msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
+msgstr "您的 CI/CD 設定語法無效,請é¸å–é©—è­‰é ç°½äº†è§£æ›´å¤šè©³ç´°è³‡è¨Šã€‚"
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
msgstr "CSV匯出已經開始。完æˆå¾Œå°‡ç™¼é€é›»å­éƒµä»¶è‡³%{email}。"
@@ -46823,6 +47509,9 @@ msgstr "您的GitLab帳號建立請求已被核准ï¼"
msgid "Your GitLab group"
msgstr "您的GitLab群組"
+msgid "Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account."
+msgstr "您的 GitLab æœå‹™å¯¦ä¾‹å…許任何人註冊帳號,這å°æ–¼å…¬é–‹çš„ GitLab æœå‹™å¯¦ä¾‹å­˜åœ¨å®‰å…¨é¢¨éšªã€‚如果ä¸å¸Œæœ›å°å¤–開放使用者註冊帳號,您應該åœç”¨è¨»å†Šæ–°å¸³è™ŸåŠŸèƒ½ã€‚"
+
msgid "Your Groups"
msgstr "您的群組"
@@ -46878,7 +47567,7 @@ msgid "Your account is authenticated with SSO or SAML. To %{push_pull_link_start
msgstr "æ‚¨çš„å¸³è™Ÿå·²é€šéŽ SSO 或 SAML 進行身份驗證。 è¦ä½¿ç”¨æ­¤å¸³è™Ÿä¾† %{push_pull_link_start}推é€å’Œæ‹‰å–%{link_end} çš„ %{protocol} ,您必須設定 %{set_password_link_start}密碼%{link_end} 或 %{set_up_pat_link_start}設定個人存å–令牌%{link_end} 以代替密碼。 欲了解更多訊æ¯ï¼Œè«‹å­˜å– %{clone_with_https_link_start}使用 HTTPS 克隆 (clone)%{link_end}。"
msgid "Your account is authenticated with SSO or SAML. To %{push_pull_link_start}push and pull%{link_end} over %{protocol} with Git using this account, you must %{set_up_pat_link_start}set up a Personal Access Token%{link_end} to use instead of a password. For more information, see %{clone_with_https_link_start}Clone with HTTPS%{link_end}."
-msgstr "æ‚¨çš„å¸³è™Ÿå·²é€šéŽ SSO 或 SAML 進行身份驗證。è¦ä½¿ç”¨æ­¤å¸³è™Ÿä¾† %{push_pull_link_start}推é€å’Œæ‹‰å–%{link_end} è¶…éŽ %{protocol} %{set_up_pat_link_start}設定個人存å–令牌%{link_end} 以代替密碼使用。有關詳細訊æ¯ï¼Œè«‹åƒé–± %{clone_with_https_link_start}使用 HTTPS 進行複製 (Clone) %{link_end}。"
+msgstr "æ‚¨çš„å¸³è™Ÿå·²é€šéŽ SSO 或 SAML 進行身份驗證。è¦ä½¿ç”¨æ­¤å¸³è™Ÿä¾† %{push_pull_link_start}推é€å’Œæ‹‰å–%{link_end} è¶…éŽ %{protocol} %{set_up_pat_link_start}設定個人存å–令牌%{link_end} 以代替密碼使用。有關詳細訊æ¯ï¼Œè«‹åƒé–± %{clone_with_https_link_start}使用 HTTPS 進行克隆 (Clone) %{link_end}。"
msgid "Your account is locked."
msgstr "您的帳號已被鎖定。"
@@ -46965,10 +47654,6 @@ msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] "您的å…費群組目å‰åƒ…é™æ–¼ %d åæˆå“¡"
-msgid "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} member. From October 19, 2022, the %{free_user_limit} most recently active member will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} member will remain in your group."
-msgid_plural "Your group, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_user_limit} members. From October 19, 2022, the %{free_user_limit} most recently active members will remain active, and the remaining members will have the %{link_start}Over limit status%{link_end} and lose access to the group. You can go to the Usage Quotas page to manage which %{free_user_limit} members will remain in your group."
-msgstr[0] "您的群組 %{strong_start}%{namespace_name}%{strong_end} æ“æœ‰è¶…éŽ %{free_user_limit} ä½æˆå“¡ã€‚從 2022 å¹´ 10 月 19 日起,最近活èºçš„ %{free_user_limit} æˆå“¡å°‡ä¿æŒæ´»èºç‹€æ…‹ï¼Œå…¶é¤˜æˆå“¡å°‡è™•æ–¼ %{link_start}超é™ç‹€æ…‹%{link_end} 並失去å°è©²ç¾¤çµ„çš„å­˜å–權é™ã€‚您å¯ä»¥å‰å¾€ä½¿ç”¨é…é¡é é¢ä¾†ç®¡ç†å°‡å“ª %{free_user_limit} ä½æˆå“¡ä¿ç•™åœ¨æ‚¨çš„群組內。"
-
msgid "Your groups"
msgstr "您的群組"
@@ -47008,6 +47693,15 @@ msgstr "您的åå­—"
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr "您的命å空間 %{namespace_name} 超éŽäº† %{free_limit} 個使用者的é™åˆ¶ï¼Œä¸”被置於唯讀狀態。"
+msgid "Your namespace %{namespace_name} is over the %{free_user_limit} user limit"
+msgstr "您的 %{namespace_name} 命åç©ºé–“å·²è¶…éŽ %{free_user_limit} 使用者é™åˆ¶"
+
+msgid "Your namespace is over the user and storage limits and has been placed in a read-only state."
+msgstr "您的命å空間超出了使用者和儲存é™åˆ¶ï¼Œå·²è¢«ç½®æ–¼å”¯è®€ç‹€æ…‹ã€‚"
+
+msgid "Your namespace is over the user limit and has been placed in a read-only state."
+msgstr "您的命å空間超出了使用者é™åˆ¶ï¼Œå·²è¢«ç½®æ–¼å”¯è®€ç‹€æ…‹ã€‚"
+
msgid "Your new %{accessTokenType}"
msgstr "您的新 %{accessTokenType}"
@@ -47127,7 +47821,7 @@ msgid "ZentaoIntegration|If different from Web URL."
msgstr "如果與 Web URL ä¸åŒã€‚"
msgid "ZentaoIntegration|Open ZenTao"
-msgstr "打開禪é“(ZenTao)"
+msgstr "開啟禪é“(ZenTao)"
msgid "ZentaoIntegration|Use ZenTao as this project's issue tracker."
msgstr "使用禪é“(ZenTao)作為此專案的議題追蹤器。"
@@ -47165,6 +47859,9 @@ msgstr "[REDACTED]"
msgid "[Redacted]"
msgstr "[Redacted]"
+msgid "[Supports GitLab-flavored markdown, including quick actions]"
+msgstr "[æ”¯æ´ GitLab 風格的 markdown,包括快速æ“作]"
+
msgid "`end_time` should not exceed one month after `start_time`"
msgstr "`end_time` ä¸æ‡‰è¶…éŽ `start_time` 後的一個月"
@@ -47339,6 +48036,9 @@ msgstr "ä¸èƒ½èˆ‡å­ç¾¤çµ„é—œè¯"
msgid "cannot be associated with both a Group and a Project"
msgstr "ä¸èƒ½åŒæ™‚與群組和專案相關è¯"
+msgid "cannot be blank"
+msgstr "ä¸èƒ½ç‚ºç©º"
+
msgid "cannot be changed"
msgstr "無法被更改"
@@ -47391,6 +48091,14 @@ msgid "change"
msgid_plural "changes"
msgstr[0] "變更"
+msgid "check"
+msgid_plural "checks"
+msgstr[0] "檢查"
+
+msgid "checklist item"
+msgid_plural "checklist items"
+msgstr[0] "項目清單"
+
msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
msgstr "%{criticalStart}åš´é‡%{criticalEnd}ã€%{highStart}高%{highEnd} å’Œ %{otherStart}其他%{otherEnd}"
@@ -47448,11 +48156,14 @@ msgstr "%{scanner} 檢測到 %{number} 個新的潛在 %{vulnStr}"
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr "%{scanner} 檢測到 %{strong_start} %{number} %{strong_end} 個新的潛在 %{vulnStr}"
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
-msgstr "%{scanner} 未檢測到 %{boldStart}æ–°çš„%{boldEnd} 潛在æ¼æ´ž"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgstr "%{scanner} 沒有檢測到新的 %{vulnStr}"
-msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
-msgstr "%{scanner} 未檢測到 %{strong_start}新的%{strong_end} %{vulnStr}"
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
+msgstr "%{scanner} 未檢測到新的潛在æ¼æ´ž"
+
+msgid "ciReport|%{scanner}: Loading resulted in an error"
+msgstr "%{scanner}: 載入導致錯誤"
msgid "ciReport|: Loading resulted in an error"
msgstr ": 載入導致錯誤"
@@ -47607,7 +48318,7 @@ msgstr "已修復:"
msgid "ciReport|Found %{issuesWithCount}"
msgstr "找到%{issuesWithCount}"
-msgid "ciReport|Full Report"
+msgid "ciReport|Full report"
msgstr "完整報告"
msgid "ciReport|Generic Report"
@@ -47783,6 +48494,10 @@ msgstr "評論留言"
msgid "commented on %{link_to_project}"
msgstr "留言 %{link_to_project}"
+msgid "commit"
+msgid_plural "commits"
+msgstr[0] "æ交"
+
msgid "commit %{commit_id}"
msgstr "æ交 %{commit_id}"
@@ -47924,7 +48639,7 @@ msgid "entries cannot contain HTML tags"
msgstr "輸入ä¸èƒ½åŒ…å«HTML標籤"
msgid "epic"
-msgstr "å²è©©"
+msgstr "å²è©© (epic) "
msgid "error"
msgstr "錯誤"
@@ -47938,9 +48653,6 @@ msgstr "example.com"
msgid "exceeds maximum length (100 usernames)"
msgstr "超éŽæœ€å¤§é•·åº¦ï¼ˆ100 個使用者å)"
-msgid "exceeds the %{max_value_length} character limit"
-msgstr "è¶…éŽ %{max_value_length} 個字元的é™åˆ¶"
-
msgid "exceeds the limit of %{bytes} bytes"
msgstr "超éŽ%{bytes}ä½å…ƒçµ„çš„é™åˆ¶"
@@ -47954,7 +48666,7 @@ msgid "expires on %{timebox_due_date}"
msgstr "æ–¼%{timebox_due_date}éŽæœŸ"
msgid "failed"
-msgstr "已失敗"
+msgstr "失敗"
msgid "failed to dismiss associated finding(id=%{finding_id}): %{message}"
msgstr "無法忽略關è¯çš„發ç¾(id=%{finding_id}): %{message}"
@@ -48119,6 +48831,9 @@ msgstr "已經與一個GitLab議題關è¯ã€‚新的議題將ä¸æœƒè¢«é—œè¯ã€‚"
msgid "is already linked to this vulnerability"
msgstr "已與該æ¼æ´žç›¸é—œè¯"
+msgid "is already present in ancestors"
+msgstr "已經存在於先輩中"
+
msgid "is an invalid IP address range"
msgstr "是無效的 IP ä½å€ç¯„åœ"
@@ -48158,6 +48873,12 @@ msgstr "在此專案中ä¸è¢«å…許。"
msgid "is not allowed since the group is not top-level group."
msgstr "ä¸è¢«å…許,因為該群組ä¸æ˜¯æœ€ä¸Šå±¤ç¾¤çµ„。"
+msgid "is not allowed to add this type of parent"
+msgstr "ä¸å…許加入該類型的父級"
+
+msgid "is not allowed to point to itself"
+msgstr "ä¸å…許指å‘自身"
+
msgid "is not allowed. Please use your regular email address."
msgstr "ä¸è¢«å…許。請使用您的常è¦é›»å­éƒµä»¶åœ°å€ã€‚"
@@ -48646,6 +49367,9 @@ msgstr "必須與群組或專案相關è¯"
msgid "must be greater than start date"
msgstr "必須大於開始日期"
+msgid "must be in same hierarchy as custom role's namespace"
+msgstr "必須與自定義角色的命å空間處於åŒä¸€å±¤çµæ§‹ä¸­"
+
msgid "must be inside the fork network"
msgstr "必須在分å‰ï¼ˆfork)網路內"
@@ -48655,12 +49379,12 @@ msgstr "å¿…é ˆå°æ–¼ %{tag_limit} 個標籤的數é‡é™åˆ¶"
msgid "must be set for a project namespace"
msgstr "必須為專案命å空間設定"
-msgid "must be top-level namespace"
-msgstr "必須是最高層級命å空間"
-
msgid "must be unique by status and elapsed time within a policy"
msgstr "必須在政策中的狀態和經éŽæ™‚間上是唯一的"
+msgid "must belong to same project of its requirement object."
+msgstr "必須屬於其需求å°è±¡çš„åŒä¸€å°ˆæ¡ˆã€‚"
+
msgid "must belong to same project of the work item."
msgstr "必須屬於該工作項目的åŒä¸€å°ˆæ¡ˆã€‚"
@@ -48777,10 +49501,10 @@ msgid "pending deletion"
msgstr "待刪除"
msgid "personal access token"
-msgstr "個人存å–令牌(權æ–)"
+msgstr "個人存å–權æ–(令牌)"
msgid "personal access tokens"
-msgstr "個人存å–憑證"
+msgstr "個人存å–權æ–(令牌)"
msgid "pipeline"
msgstr "æµæ°´ç·š"
@@ -48859,6 +49583,9 @@ msgstr "reCAPTCHA ç§é‘°"
msgid "reCAPTCHA site key"
msgstr "reCAPTCHA 公鑰"
+msgid "reached maximum depth"
+msgstr "é”到最大深度"
+
msgid "recent activity"
msgstr "最近活動"
@@ -48912,6 +49639,10 @@ msgstr "版本庫:"
msgid "role's base access level does not match the access level of the membership"
msgstr "基於角色的存å–等級ä¸åŒ¹é…æˆå“¡çš„å­˜å–等級"
+msgid "rule"
+msgid_plural "rules"
+msgstr[0] "è¦å‰‡"
+
msgid "running"
msgstr "執行中"
@@ -49049,7 +49780,7 @@ msgid "the correct format."
msgstr "正確的格å¼ã€‚"
msgid "the following epics"
-msgstr "下列å²è©©"
+msgstr "下列å²è©© (epics)"
msgid "the following incidents or issues"
msgstr "如下事件或議題"
diff --git a/locale/zh_TW/gitlab.po.time_stamp b/locale/zh_TW/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/zh_TW/gitlab.po.time_stamp
+++ /dev/null
diff --git a/metrics_server/metrics_server.rb b/metrics_server/metrics_server.rb
index d1a64aa5b79..0516d711ffb 100644
--- a/metrics_server/metrics_server.rb
+++ b/metrics_server/metrics_server.rb
@@ -43,6 +43,7 @@ class MetricsServer # rubocop:disable Gitlab/NamespacedClass
path = options[:path]&.then { |p| Pathname.new(p) } || Pathname.new('')
cmd = path.join('gitlab-metrics-exporter').to_path
env = {
+ 'GOGC' => '10', # Set Go GC heap goal to 10% to curb memory growth.
'GME_MMAP_METRICS_DIR' => metrics_dir.to_s,
'GME_PROBES' => 'self,mmap',
'GME_SERVER_HOST' => settings['address'],
diff --git a/package.json b/package.json
index 7b28a3d557b..ef7eac995ad 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"jest-debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
"jest:ci": "jest --config jest.config.js --ci --coverage --testSequencer ./scripts/frontend/parallel_ci_sequencer.js",
"jest:ci:minimal": "jest --config jest.config.js --ci --coverage --findRelatedTests $(cat $RSPEC_CHANGED_FILES_PATH) --passWithNoTests --testSequencer ./scripts/frontend/parallel_ci_sequencer.js",
+ "jest:contract": "PACT_DO_NOT_TRACK=true jest --config jest.config.contract.js --runInBand",
"jest:integration": "jest --config jest.config.integration.js",
"lint:eslint": "node scripts/frontend/eslint.js",
"lint:eslint:fix": "node scripts/frontend/eslint.js --fix",
@@ -51,15 +52,16 @@
"@babel/core": "^7.18.5",
"@babel/preset-env": "^7.18.2",
"@codesandbox/sandpack-client": "^1.2.2",
+ "@cubejs-client/core": "^0.31.0",
"@gitlab/at.js": "1.5.7",
"@gitlab/favicon-overlay": "2.0.0",
- "@gitlab/svgs": "3.8.0",
- "@gitlab/ui": "49.10.0",
+ "@gitlab/fonts": "^1.0.1",
+ "@gitlab/svgs": "3.14.0",
+ "@gitlab/ui": "52.6.0",
"@gitlab/visual-review-tools": "1.7.3",
- "@gitlab/web-ide": "0.0.1-dev-20221114183058",
+ "@gitlab/web-ide": "0.0.1-dev-20221217175648",
"@rails/actioncable": "6.1.4-7",
"@rails/ujs": "6.1.4-7",
- "@sentry/browser": "5.30.0",
"@sourcegraph/code-host-integration": "0.0.84",
"@tiptap/core": "^2.0.0-beta.182",
"@tiptap/extension-blockquote": "^2.0.0-beta.29",
@@ -122,7 +124,7 @@
"dropzone": "^4.2.0",
"editorconfig": "^0.15.3",
"emoji-regex": "^10.0.0",
- "esbuild": "0.15.9",
+ "esbuild": "0.15.18",
"esbuild-loader": "^2.20.0",
"fast-mersenne-twister": "1.0.2",
"file-loader": "^6.2.0",
@@ -172,6 +174,8 @@
"remark-rehype": "^10.1.0",
"scrollparent": "^2.0.1",
"select2": "3.5.2-browserify",
+ "sentrybrowser5": "npm:@sentry/browser@5.30.0",
+ "sentrybrowser7": "npm:@sentry/browser@^7.21.1",
"sortablejs": "^1.10.2",
"string-hash": "1.1.3",
"style-loader": "^2.0.0",
@@ -209,36 +213,36 @@
"@gitlab/stylelint-config": "4.1.0",
"@graphql-eslint/eslint-plugin": "3.12.0",
"@testing-library/dom": "^7.16.2",
- "@types/jest": "^27.5.1",
+ "@types/jest": "^28.1.3",
"@vue/test-utils": "1.3.0",
- "@vue/vue2-jest": "^27.0.0",
+ "@vue/vue2-jest": "^28.1.0",
"ajv": "^8.10.0",
"ajv-formats": "^2.1.1",
"axios-mock-adapter": "^1.15.0",
- "babel-jest": "^27.5.1",
+ "babel-jest": "^28.1.3",
"chalk": "^2.4.1",
"cheerio": "^1.0.0-rc.9",
"commander": "^2.20.3",
"custom-jquery-matchers": "^2.1.0",
- "eslint": "8.26.0",
+ "eslint": "8.28.0",
"eslint-import-resolver-jest": "3.0.2",
"eslint-import-resolver-webpack": "0.13.2",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-no-jquery": "2.7.0",
- "eslint-plugin-no-unsanitized": "^4.0.1",
+ "eslint-plugin-no-unsanitized": "^4.0.2",
"gettext-extractor": "^3.5.3",
"gettext-extractor-vue": "^5.0.0",
"glob": "^7.1.6",
"istanbul-lib-coverage": "^3.0.0",
"istanbul-lib-report": "^3.0.0",
"istanbul-reports": "^3.0.0",
- "jest": "^27.5.1",
- "jest-canvas-mock": "^2.1.2",
- "jest-diff": "^27.5.1",
- "jest-environment-jsdom": "^27.5.1",
- "jest-jasmine2": "^27.5.1",
- "jest-junit": "^12.0.0",
- "jest-util": "^27.5.1",
+ "jest": "^28.1.3",
+ "jest-canvas-mock": "^2.4.0",
+ "jest-diff": "^28.1.3",
+ "jest-environment-jsdom": "^28.1.3",
+ "jest-jasmine2": "^28.1.3",
+ "jest-junit": "^12.3.0",
+ "jest-util": "^28.1.3",
"jsonlint": "^1.6.3",
"markdownlint-cli": "0.32.2",
"miragejs": "^0.1.40",
@@ -270,4 +274,4 @@
"node": ">=12.22.1",
"yarn": "^1.10.0"
}
-} \ No newline at end of file
+}
diff --git a/qa/Dockerfile b/qa/Dockerfile
index 7f236a25288..71fc615ac13 100644
--- a/qa/Dockerfile
+++ b/qa/Dockerfile
@@ -1,9 +1,9 @@
ARG DOCKER_VERSION=20.10.14
ARG CHROME_VERSION=106
-ARG QA_BUILD_TARGET=qa
+ARG QA_BUILD_TARGET=ee
ARG RUBY_VERSION=2.7
-FROM registry.gitlab.com/gitlab-org/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3-git-2.36-lfs-2.9-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}-gcloud-383-kubectl-1.23 AS qa
+FROM registry.gitlab.com/gitlab-org/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3-git-2.36-lfs-2.9-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}-gcloud-383-kubectl-1.23 AS foss
LABEL maintainer="GitLab Quality Department <quality@gitlab.com>"
ENV DEBIAN_FRONTEND="noninteractive"
@@ -39,10 +39,6 @@ RUN bundle config set --local without development \
&& bundle install --retry=3
COPY ./config/initializers/0_inject_enterprise_edition_module.rb /home/gitlab/config/initializers/
-# Copy VERSION to ensure the COPY succeeds to copy at least one file since ee/app/models/license.rb isn't present in FOSS
-# The [b] part makes ./ee/app/models/license.r[b] a pattern that is allowed to return no files (which is the case in FOSS)
-COPY VERSION ./ee/app/models/license.r[b] /home/gitlab/ee/app/models/
-COPY VERSION ./ee/config/feature_flag[s] /home/gitlab/ee/config/feature_flags/
COPY ./config/feature_flags /home/gitlab/config/feature_flags
COPY ./config/bundler_setup.rb /home/gitlab/config/
COPY ./lib/gitlab_edition.rb /home/gitlab/lib/
@@ -53,8 +49,15 @@ COPY ./qa /home/gitlab/qa
ENTRYPOINT ["bin/test"]
-# Add JH files when pass the parameter: `--build-arg QA_BUILD_TARGET=jhqa`
-FROM qa AS jhqa
+# Add ee files when passing the parameter: `--build-arg QA_BUILD_TARGET=ee`
+FROM foss as ee
+# Copy VERSION to ensure the COPY succeeds to copy at least one file since ee/app/models/license.rb isn't present in FOSS
+# The [b] part makes ./ee/app/models/license.r[b] a pattern that is allowed to return no files (which is the case in FOSS)
+ONBUILD COPY VERSION ./ee/app/models/license.r[b] /home/gitlab/ee/app/models/
+ONBUILD COPY VERSION ./ee/config/feature_flag[s] /home/gitlab/ee/config/feature_flags/
+
+# Add JH files when passing the parameter: `--build-arg QA_BUILD_TARGET=jhqa`
+FROM ee AS jhqa
ONBUILD COPY ./jh/qa /home/gitlab/jh/qa
ONBUILD COPY ./jh/lib /home/gitlab/jh/lib
ONBUILD COPY ./jh/config/feature_flags /home/gitlab/jh/config/feature_flags
diff --git a/qa/Gemfile b/qa/Gemfile
index b84a22883d1..224fced35dc 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -2,14 +2,14 @@
source 'https://rubygems.org'
-gem 'gitlab-qa', '~> 8', '>= 8.11.0', require: 'gitlab/qa'
+gem 'gitlab-qa', '~> 8', '>= 8.14.0', require: 'gitlab/qa'
gem 'activesupport', '~> 6.1.4.7' # This should stay in sync with the root's Gemfile
-gem 'allure-rspec', '~> 2.19.0'
+gem 'allure-rspec', '~> 2.20.0'
gem 'capybara', '~> 3.38.0'
gem 'capybara-screenshot', '~> 1.0.26'
gem 'rake', '~> 13', '>= 13.0.6'
gem 'rspec', '~> 3.12'
-gem 'selenium-webdriver', '~> 4.6', '>= 4.6.1'
+gem 'selenium-webdriver', '~> 4.7', '>= 4.7.1'
gem 'airborne', '~> 0.3.7', require: false # airborne is messing with rspec sandboxed mode so not requiring by default
gem 'rest-client', '~> 2.1.0'
gem 'rspec-retry', '~> 0.6.2', require: 'rspec/retry'
@@ -17,14 +17,14 @@ gem 'rspec_junit_formatter', '~> 0.6.0'
gem 'faker', '~> 3.0'
gem 'knapsack', '~> 4.0'
gem 'parallel_tests', '~> 4.0'
-gem 'rotp', '~> 6.2.1'
+gem 'rotp', '~> 6.2.2'
gem 'parallel', '~> 1.22', '>= 1.22.1'
gem 'rainbow', '~> 3.1.1'
gem 'rspec-parameterized', '~> 0.5.2'
-gem 'octokit', '~> 6.0.0'
+gem 'octokit', '~> 6.0.1'
gem "faraday-retry", "~> 2.0"
gem 'webdrivers', '~> 5.2'
-gem 'zeitwerk', '~> 2.4'
+gem 'zeitwerk', '~> 2.6', '>= 2.6.6'
gem 'influxdb-client', '~> 2.8'
gem 'terminal-table', '~> 3.0.2', require: false
gem 'slack-notifier', '~> 2.4', require: false
@@ -32,15 +32,15 @@ gem 'fog-google', '~> 1.19', require: false
gem 'fog-core', '2.1.0', require: false # fog-google generates a ton of warnings with latest core
gem "warning", "~> 1.3"
-gem 'confiner', '~> 0.3'
+gem 'confiner', '~> 0.4'
gem 'chemlab', '~> 0.10'
gem 'chemlab-library-www-gitlab-com', '~> 0.1', '>= 0.1.1'
# dependencies for jenkins client
-gem 'nokogiri', '~> 1.13', '>= 1.13.9'
+gem 'nokogiri', '~> 1.13', '>= 1.13.10'
-gem 'deprecation_toolkit', '~> 2.0.0', require: false
+gem 'deprecation_toolkit', '~> 2.0.1', require: false
group :development do
gem 'pry-byebug', '~> 3.10.1', platform: :mri
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 3ccc5fe9be3..243389e6e4a 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -15,13 +15,14 @@ GEM
rack-test (>= 1.1.0, < 2.0)
rest-client (>= 2.0.2, < 3.0)
rspec (~> 3.8)
- allure-rspec (2.19.0)
- allure-ruby-commons (= 2.19.0)
+ allure-rspec (2.20.0)
+ allure-ruby-commons (= 2.20.0)
rspec-core (>= 3.8, < 4)
- allure-ruby-commons (2.19.0)
+ allure-ruby-commons (2.20.0)
mime-types (>= 3.3, < 4)
oj (>= 3.10, < 4)
require_all (>= 2, < 4)
+ rspec-expectations (~> 3.12)
uuid (>= 2.3, < 3)
ast (2.4.2)
binding_ninja (0.2.3)
@@ -47,15 +48,14 @@ GEM
watir (>= 6, < 8)
chemlab-library-www-gitlab-com (0.1.1)
chemlab (~> 0.4)
- childprocess (4.1.0)
coderay (1.1.2)
colorize (0.8.1)
concurrent-ruby (1.1.10)
- confiner (0.3.0)
+ confiner (0.4.0)
gitlab (>= 4.17)
- zeitwerk (~> 2.5.1)
+ zeitwerk (>= 2.5, < 3)
declarative (0.0.20)
- deprecation_toolkit (2.0.0)
+ deprecation_toolkit (2.0.1)
activesupport (>= 5.2)
diff-lcs (1.3)
domain_name (0.5.20190701)
@@ -100,7 +100,7 @@ GEM
gitlab (4.18.0)
httparty (~> 0.18)
terminal-table (>= 1.5.1)
- gitlab-qa (8.11.0)
+ gitlab-qa (8.14.0)
activesupport (~> 6.1)
gitlab (~> 4.18.0)
http (~> 5.0)
@@ -155,7 +155,7 @@ GEM
httpclient (2.8.3)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
- influxdb-client (2.8.0)
+ influxdb-client (2.9.0)
jwt (2.5.0)
knapsack (4.0.0)
rake
@@ -178,10 +178,10 @@ GEM
multi_json (1.15.0)
multi_xml (0.6.0)
netrc (0.11.0)
- nokogiri (1.13.9)
+ nokogiri (1.13.10)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
- octokit (6.0.0)
+ octokit (6.0.1)
faraday (>= 1, < 3)
sawyer (~> 0.9)
oj (3.13.23)
@@ -202,7 +202,7 @@ GEM
byebug (~> 11.0)
pry (>= 0.13, < 0.15)
public_suffix (5.0.0)
- racc (1.6.0)
+ racc (1.6.1)
rack (2.2.3.1)
rack-test (1.1.0)
rack (>= 1.0, < 3)
@@ -221,7 +221,7 @@ GEM
netrc (~> 0.8)
retriable (3.1.2)
rexml (3.2.5)
- rotp (6.2.1)
+ rotp (6.2.2)
rspec (3.12.0)
rspec-core (~> 3.12.0)
rspec-expectations (~> 3.12.0)
@@ -252,8 +252,7 @@ GEM
sawyer (0.9.2)
addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3)
- selenium-webdriver (4.6.1)
- childprocess (>= 0.5, < 5.0)
+ selenium-webdriver (4.7.1)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
@@ -292,7 +291,7 @@ GEM
websocket (1.2.9)
xpath (3.2.0)
nokogiri (~> 1.8)
- zeitwerk (2.5.4)
+ zeitwerk (2.6.6)
PLATFORMS
ruby
@@ -300,40 +299,40 @@ PLATFORMS
DEPENDENCIES
activesupport (~> 6.1.4.7)
airborne (~> 0.3.7)
- allure-rspec (~> 2.19.0)
+ allure-rspec (~> 2.20.0)
capybara (~> 3.38.0)
capybara-screenshot (~> 1.0.26)
chemlab (~> 0.10)
chemlab-library-www-gitlab-com (~> 0.1, >= 0.1.1)
- confiner (~> 0.3)
- deprecation_toolkit (~> 2.0.0)
+ confiner (~> 0.4)
+ deprecation_toolkit (~> 2.0.1)
faker (~> 3.0)
faraday-retry (~> 2.0)
fog-core (= 2.1.0)
fog-google (~> 1.19)
- gitlab-qa (~> 8, >= 8.11.0)
+ gitlab-qa (~> 8, >= 8.14.0)
influxdb-client (~> 2.8)
knapsack (~> 4.0)
- nokogiri (~> 1.13, >= 1.13.9)
- octokit (~> 6.0.0)
+ nokogiri (~> 1.13, >= 1.13.10)
+ octokit (~> 6.0.1)
parallel (~> 1.22, >= 1.22.1)
parallel_tests (~> 4.0)
pry-byebug (~> 3.10.1)
rainbow (~> 3.1.1)
rake (~> 13, >= 13.0.6)
rest-client (~> 2.1.0)
- rotp (~> 6.2.1)
+ rotp (~> 6.2.2)
rspec (~> 3.12)
rspec-parameterized (~> 0.5.2)
rspec-retry (~> 0.6.2)
rspec_junit_formatter (~> 0.6.0)
ruby-debug-ide (~> 0.7.3)
- selenium-webdriver (~> 4.6, >= 4.6.1)
+ selenium-webdriver (~> 4.7, >= 4.7.1)
slack-notifier (~> 2.4)
terminal-table (~> 3.0.2)
warning (~> 1.3)
webdrivers (~> 5.2)
- zeitwerk (~> 2.4)
+ zeitwerk (~> 2.6, >= 2.6.6)
BUNDLED WITH
- 2.3.25
+ 2.3.26
diff --git a/qa/README.md b/qa/README.md
index 564beb4c6e8..4e2d688aa54 100644
--- a/qa/README.md
+++ b/qa/README.md
@@ -88,6 +88,43 @@ bundle exec bin/qa Test::Instance::All {GDK IP ADDRESS}
- Note: If you want to run tests requiring SSH against GDK, you will need to [modify your GDK setup](https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/run_qa_against_gdk.md).
- Note: If this is your first time running GDK, you can use the password pre-set for `root`. [See supported GitLab environment variables](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/docs/what_tests_can_be_run.md#supported-gitlab-environment-variables). If you have changed your `root` password, use that when exporting `GITLAB_INITIAL_ROOT_PASSWORD`.
+#### Run the end-to-end tests on GitLab in Docker
+
+1. [GitLab can be installed in Docker](https://docs.gitlab.com/ee/install/docker.html). You can use the following command to start an instance that you can visit at `http://127.0.0.1`:
+
+ ```
+ docker run \
+ --hostname 127.0.0.1 \
+ --publish 80:80 --publish 22:22 \
+ --name gitlab \
+ --shm-size 256m \
+ --env GITLAB_OMNIBUS_CONFIG="gitlab_rails['initial_root_password']='5iveL\!fe';" \
+ gitlab/gitlab-ee:nightly
+ ```
+
+ Notes:
+ - If you are on a Mac with [Apple Silicon](https://support.apple.com/en-us/HT211814), you will also need to add: `--platform=linux/amd64`
+ - If you are on Windows, please be aware that [Docker Desktop must be set to use Linux containers](https://learn.microsoft.com/en-us/virtualization/windowscontainers/quick-start/quick-start-windows-10-linux#run-your-first-linux-container).
+
+
+2. Navigate to the QA folder and run the following commands.
+
+ ```bash
+ cd gitlab/qa
+ bundle install
+ export WEBDRIVER_HEADLESS=false
+ export GITLAB_INITIAL_ROOT_PASSWORD=5iveL\!fe
+ export QA_GITLAB_URL="http://127.0.0.1"
+ ```
+
+3. Most tests that do not require special setup could then be run with the following command.
+
+ ```bash
+ bundle exec rspec <path/to/spec.rb>
+ ```
+
+- Note: See the section above for situations that might require adjustment to the commands or to the configuration of the GitLab instance. [You can find more information in the documentation](https://docs.gitlab.com/ee/install/docker.html).
+
#### Running EE tests
When running EE tests you'll need to have a license available. GitLab engineers can [request a license](https://about.gitlab.com/handbook/developer-onboarding/#working-on-gitlab-ee).
@@ -241,9 +278,8 @@ feature flag ([via the API](https://docs.gitlab.com/ee/api/features.html)) if no
run all the tests in the `Test::Instance::All` scenario, and then enable the
feature flag again if it was enabled earlier.
-Note: the QA framework doesn't currently allow you to easily toggle a feature
-flag during a single test, [as you can in unit tests](https://docs.gitlab.com/ee/development/feature_flags/index.html),
-but [that capability is planned](https://gitlab.com/gitlab-org/quality/team-tasks/issues/77).
+Note: You can also [toggle feature
+flags in the tests themselves](https://docs.gitlab.com/ee/development/testing_guide/end_to_end/feature_flags.html).
Note also that the `--` separator isn't used because `--enable-feature` and `--disable-feature`
are QA framework options, not `rspec` options.
diff --git a/qa/lib/gitlab/page/group/settings/usage_quotas.rb b/qa/lib/gitlab/page/group/settings/usage_quotas.rb
index 8540bce3da8..04aa1b779ce 100644
--- a/qa/lib/gitlab/page/group/settings/usage_quotas.rb
+++ b/qa/lib/gitlab/page/group/settings/usage_quotas.rb
@@ -35,21 +35,21 @@ module Gitlab
div :purchased_usage_total_free # Different UI for free namespace
span :purchased_usage_total
div :storage_purchase_successful_alert, text: /You have successfully purchased a storage/
- h2 :storage_available_alert, text: /purchased storage is available/
+ div :additional_storage_alert, text: /purchase additional storage/
def plan_ci_limits
- plan_ci_minutes_element.span.text[%r{([^/ ]+)$}]
+ plan_ci_minutes[/(\d+){2}/]
end
def additional_ci_limits
- additional_ci_minutes_element.span.text[%r{([^/ ]+)$}]
+ additional_ci_minutes[/(\d+){2}/]
end
# Waits and Checks if storage available alert presents on the page
#
# @return [Boolean] True if the alert presents, false if not after 5 second wait
- def purchased_storage_available?
- storage_available_alert_element.wait_until(timeout: 5, &:present?)
+ def additional_storage_available?
+ additional_storage_alert_element.wait_until(timeout: 5, &:present?)
rescue Watir::Wait::TimeoutError
false
end
@@ -67,7 +67,7 @@ module Gitlab
#
# @return [Float] Total purchased storage value in GiB
def total_purchased_storage(free_name_space = true)
- storage_available_alert_element.wait_until(&:present?)
+ additional_storage_alert_element.wait_until(&:present?)
if free_name_space
purchased_usage_total_free.split('/').last.match(/\d+\.\d+/)[0].to_f
diff --git a/qa/qa.rb b/qa/qa.rb
index bf6b75a1278..fe0c19e0818 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -13,9 +13,10 @@ Bundler.require(:default)
require 'securerandom'
require 'pathname'
+require 'rainbow/refinement'
require 'active_support/core_ext/hash'
require 'active_support/core_ext/object/blank'
-require 'rainbow/refinement'
+require 'active_support/core_ext/module/delegation'
module QA
root = "#{__dir__}/qa"
diff --git a/qa/qa/ce/strategy.rb b/qa/qa/ce/strategy.rb
index 981b60d1920..8143595a18b 100644
--- a/qa/qa/ce/strategy.rb
+++ b/qa/qa/ce/strategy.rb
@@ -8,12 +8,12 @@ module QA
def perform_before_hooks
if QA::Runtime::Env.admin_personal_access_token.present?
QA::Resource::PersonalAccessTokenCache.set_token_for_username(QA::Runtime::User.admin_username,
- QA::Runtime::Env.admin_personal_access_token)
+ QA::Runtime::Env.admin_personal_access_token)
end
if QA::Runtime::Env.personal_access_token.present? && QA::Runtime::Env.user_username.present?
QA::Resource::PersonalAccessTokenCache.set_token_for_username(QA::Runtime::Env.user_username,
- QA::Runtime::Env.personal_access_token)
+ QA::Runtime::Env.personal_access_token)
end
# The login page could take some time to load the first time it is visited.
@@ -22,6 +22,9 @@ module QA
QA::Support::Retrier.retry_on_exception do
QA::Runtime::Browser.visit(:gitlab, QA::Page::Main::Login)
end
+ return unless QA::Runtime::Env.allow_local_requests?
+
+ Runtime::ApplicationSettings.set_application_settings(allow_local_requests_from_web_hooks_and_services: true)
end
end
end
diff --git a/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb b/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb
index 8eac8419022..e6ec4528d0d 100644
--- a/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb
+++ b/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb
@@ -25,9 +25,26 @@ spec:
- --token-file=/config/token
- --kas-address
- "<%= kas_wss_address %>"
+ <% if QA::Runtime::Env.qa_cookies.to_s.include?("gitlab_canary=true") %>
+ - --kas-header
+ - "Cookie: gitlab_canary=true"
+ <% end %>
volumeMounts:
- name: token-volume
mountPath: /config
+ env:
+ - name: POD_NAMESPACE
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ - name: POD_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
+ - name: SERVICE_ACCOUNT_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: spec.serviceAccountName
volumes:
- name: token-volume
secret:
diff --git a/qa/qa/fixtures/package_managers/maven/group/settings_with_pat.xml.erb b/qa/qa/fixtures/package_managers/maven/group/settings_with_pat.xml.erb
index 611c232819f..a2d3e7493ef 100644
--- a/qa/qa/fixtures/package_managers/maven/group/settings_with_pat.xml.erb
+++ b/qa/qa/fixtures/package_managers/maven/group/settings_with_pat.xml.erb
@@ -7,7 +7,7 @@
<httpHeaders>
<property>
<name>Private-Token</name>
- <value><%= personal_access_token %></value>
+ <value>${PERSONAL_ACCESS_TOKEN}</value>
</property>
</httpHeaders>
</configuration>
diff --git a/qa/qa/flow/alert_settings.rb b/qa/qa/flow/alert_settings.rb
new file mode 100644
index 00000000000..0e884f58773
--- /dev/null
+++ b/qa/qa/flow/alert_settings.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module QA
+ module Flow
+ module AlertSettings
+ extend self
+
+ def setup_http_endpoint_and_send_alert(integration_name: nil, payload: nil)
+ integration_name ||= random_word
+ payload ||= { title: random_word, description: random_word }
+ Page::Project::Menu.perform(&:go_to_monitor_settings)
+ Page::Project::Settings::Monitor.perform do |setting|
+ setting.expand_alerts do |alert|
+ alert.add_new_integration
+ alert.select_http_endpoint
+ alert.enter_integration_name(integration_name)
+ alert.activate_integration
+ alert.save_and_create_alert
+ alert.fill_in_test_payload(payload.to_json)
+ alert.send_test_alert
+ end
+ end
+ end
+
+ private
+
+ def random_word
+ Faker::Lorem.word
+ end
+ end
+ end
+end
diff --git a/qa/qa/flow/login.rb b/qa/qa/flow/login.rb
index ec205e0aa86..564a51ee483 100644
--- a/qa/qa/flow/login.rb
+++ b/qa/qa/flow/login.rb
@@ -14,10 +14,8 @@ module QA
result
end
- def while_signed_in_as_admin(address: :gitlab)
- while_signed_in(address: address, admin: true) do
- yield
- end
+ def while_signed_in_as_admin(address: :gitlab, &block)
+ while_signed_in(address: address, admin: true, &block)
end
def sign_in(as: nil, address: :gitlab, skip_page_validation: false, admin: false)
diff --git a/qa/qa/git/location.rb b/qa/qa/git/location.rb
index c3733572e70..9ac97a66e53 100644
--- a/qa/qa/git/location.rb
+++ b/qa/qa/git/location.rb
@@ -1,16 +1,13 @@
# frozen_string_literal: true
require 'uri'
-require 'forwardable'
module QA
module Git
class Location
- extend Forwardable
-
attr_reader :git_uri, :uri
- def_delegators :@uri, :user, :host, :path
+ delegate :user, :host, :path, to: :@uri
# See: config/initializers/1_settings.rb
# Settings#build_gitlab_shell_ssh_path_prefix
diff --git a/qa/qa/page/admin/menu.rb b/qa/qa/page/admin/menu.rb
index 3164676f8e4..42dd1083bbe 100644
--- a/qa/qa/page/admin/menu.rb
+++ b/qa/qa/page/admin/menu.rb
@@ -92,16 +92,12 @@ module QA
end
end
- def within_sidebar
- within_element(:admin_sidebar_content) do
- yield
- end
+ def within_sidebar(&block)
+ within_element(:admin_sidebar_content, &block)
end
- def within_submenu(element)
- within_element(element) do
- yield
- end
+ def within_submenu(element, &block)
+ within_element(element, &block)
end
end
end
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index f59b06b4e75..ab83da7dacf 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -13,7 +13,6 @@ module QA
include Support::WaitForRequests
extend Validatable
- extend SingleForwardable
ElementNotFound = Class.new(RuntimeError)
@@ -31,8 +30,6 @@ module QA
end
end
- def_delegators :evaluator, :view, :views
-
def initialize
@retry_later_backoff = QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME
end
@@ -276,7 +273,7 @@ module QA
visible = kwargs.delete(:visible)
visible = visible.nil? && true
- try_find_element = lambda do |wait|
+ try_find_element = ->(wait) do
if disabled.nil?
has_css?(element_selector_css(name, kwargs), text: text, wait: wait, class: klass, visible: visible)
else
@@ -422,26 +419,30 @@ module QA
URI(page.current_url).host
end
- def self.path
- raise NotImplementedError
- end
+ class << self
+ def path
+ raise NotImplementedError
+ end
- def self.evaluator
- @evaluator ||= Page::Base::DSL.new
- end
+ def evaluator
+ @evaluator ||= Page::Base::DSL.new
+ end
- def self.errors
- return ["Page class does not have views / elements defined!"] if views.empty?
+ def errors
+ return ["Page class does not have views / elements defined!"] if views.empty?
- views.flat_map(&:errors)
- end
+ views.flat_map(&:errors)
+ end
- def self.elements
- views.flat_map(&:elements)
- end
+ def elements
+ views.flat_map(&:elements)
+ end
+
+ def required_elements
+ elements.select(&:required?)
+ end
- def self.required_elements
- elements.select(&:required?)
+ delegate :view, :views, to: :evaluator
end
def send_keys_to_element(name, keys)
diff --git a/qa/qa/page/component/blob_content.rb b/qa/qa/page/component/blob_content.rb
index b6001cf39b5..a57ef38f768 100644
--- a/qa/qa/page/component/blob_content.rb
+++ b/qa/qa/page/component/blob_content.rb
@@ -72,11 +72,11 @@ module QA
private
- def within_file_by_number(element, file_number)
+ def within_file_by_number(element, file_number, &block)
if file_number
- within_element_by_index(element, file_number - 1) { yield }
+ within_element_by_index(element, file_number - 1, &block)
else
- within_element(element) { yield }
+ within_element(element, &block)
end
end
end
diff --git a/qa/qa/page/component/custom_metric.rb b/qa/qa/page/component/custom_metric.rb
deleted file mode 100644
index 094979f5e18..00000000000
--- a/qa/qa/page/component/custom_metric.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Page
- module Component
- module CustomMetric
- extend QA::Page::PageConcern
-
- def self.included(base)
- super
-
- base.view 'app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue' do
- element :custom_metric_prometheus_title_field
- element :custom_metric_prometheus_query_field
- element :custom_metric_prometheus_y_label_field
- element :custom_metric_prometheus_unit_label_field
- element :custom_metric_prometheus_legend_label_field
- end
- end
-
- def add_custom_metric
- fill_element :custom_metric_prometheus_title_field, 'HTTP Requests Total'
- fill_element :custom_metric_prometheus_query_field, 'rate(http_requests_total[5m])'
- fill_element :custom_metric_prometheus_y_label_field, 'Requests/second'
- fill_element :custom_metric_prometheus_unit_label_field, 'req/sec'
- fill_element :custom_metric_prometheus_legend_label_field, 'HTTP requests'
-
- save_changes
- end
-
- def save_changes
- click_button(class: 'btn-success')
- end
-
- def delete_custom_metric
- click_button(class: 'btn-danger')
- within('.modal-content') { click_button(class: 'btn-danger') }
- end
-
- def edit_custom_metric
- fill_element :custom_metric_prometheus_title_field, ''
- fill_element :custom_metric_prometheus_title_field, 'Throughput'
-
- save_changes
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/component/dropdown.rb b/qa/qa/page/component/dropdown.rb
new file mode 100644
index 00000000000..e6204fb5332
--- /dev/null
+++ b/qa/qa/page/component/dropdown.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module Dropdown
+ include Select2
+
+ def select_item(item_text)
+ return super if use_select2?
+
+ find('li.gl-dropdown-item', text: item_text, match: :prefer_exact).click
+ end
+
+ def has_item?(item_text)
+ return super if use_select2?
+
+ has_css?('li.gl-dropdown-item', text: item_text, match: :prefer_exact)
+ end
+
+ def current_selection
+ return super if use_select2?
+
+ expand_select_list unless dropdown_open?
+ find('span.gl-dropdown-button-text').text
+ end
+
+ def clear_current_selection_if_present
+ return super if use_select2?
+
+ expand_select_list unless dropdown_open?
+
+ if has_css?('button[data-testid="listbox-reset-button"]')
+ find('button[data-testid="listbox-reset-button"]').click
+ elsif dropdown_open?
+ expand_select_list
+ end
+ end
+
+ def search_item(item_text)
+ return super if use_select2?
+
+ find('div.gl-search-box-by-type input[type="Search"]').set(item_text)
+ wait_for_search_to_complete
+ end
+
+ def search_and_select(item_text)
+ return super if use_select2?
+
+ QA::Runtime::Logger.info "Searching and selecting: #{item_text}"
+
+ search_item(item_text)
+
+ unless has_item?(item_text)
+ raise QA::Page::Base::ElementNotFound, %(Couldn't find option named "#{item_text}")
+ end
+
+ select_item(item_text)
+ end
+
+ def search_and_select_exact(item_text)
+ return super if use_select2?
+
+ QA::Runtime::Logger.info "Searching and selecting: #{item_text}"
+
+ search_item(item_text)
+
+ unless has_item?(item_text)
+ raise QA::Page::Base::ElementNotFound, %(Couldn't find option named "#{item_text}")
+ end
+
+ find('li.gl-dropdown-item span:nth-child(2)', text: item_text, exact_text: true).click
+ end
+
+ def expand_select_list
+ return super if use_select2?
+
+ find('svg.dropdown-chevron').click
+ end
+
+ def wait_for_search_to_complete
+ return super if use_select2?
+
+ Support::WaitForRequests.wait_for_requests
+
+ has_css?('div[data-testid="listbox-search-loader"]', wait: 1)
+ has_no_css?('div[data-testid="listbox-search-loader"]')
+ end
+
+ def dropdown_open?
+ return super if use_select2?
+
+ has_css?('ul.gl-dropdown-contents', wait: 1)
+ end
+
+ def find_input_by_prefix_and_set(element_prefix, item_text)
+ find("input[id^=\"#{element_prefix}\"]").set(item_text)
+ end
+
+ private
+
+ # rubocop:disable Gitlab/PredicateMemoization
+ def use_select2?
+ @use_select2 ||= has_css?('.select2-container', wait: 1)
+ end
+ # rubocop:enable Gitlab/PredicateMemoization
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/component/invite_members_modal.rb b/qa/qa/page/component/invite_members_modal.rb
index 27dce152367..295b5134bd5 100644
--- a/qa/qa/page/component/invite_members_modal.rb
+++ b/qa/qa/page/component/invite_members_modal.rb
@@ -76,10 +76,7 @@ module QA
def set_access_level(access_level)
# Guest option is selected by default, skipping these steps if desired option is 'Guest'
- unless access_level == 'Guest'
- click_element :access_level_dropdown
- click_button access_level
- end
+ select_element(:access_level_dropdown, access_level) unless access_level == 'Guest'
end
def send_invite
diff --git a/qa/qa/page/component/issuable/sidebar.rb b/qa/qa/page/component/issuable/sidebar.rb
index fb2e7478684..0a31dee2b4f 100644
--- a/qa/qa/page/component/issuable/sidebar.rb
+++ b/qa/qa/page/component/issuable/sidebar.rb
@@ -22,19 +22,19 @@ module QA
element :reviewers_edit_button
end
- base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue' do
+ base.view 'app/assets/javascripts/sidebar/components/labels/labels_select_widget/labels_select_root.vue' do
element :labels_block
end
- base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue' do
+ base.view 'app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view.vue' do
element :dropdown_input_field
end
- base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue' do
+ base.view 'app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents.vue' do
element :labels_dropdown_content
end
- base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue' do
+ base.view 'app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_value.vue' do
element :selected_label_content
end
diff --git a/qa/qa/page/component/snippet.rb b/qa/qa/page/component/snippet.rb
index 47ed1a9616b..4e1c7f3e2bb 100644
--- a/qa/qa/page/component/snippet.rb
+++ b/qa/qa/page/component/snippet.rb
@@ -170,7 +170,7 @@ module QA
# wait for the page to reload after deletion
wait_until(reload: false) do
has_no_element?(:delete_snippet_button) &&
- has_no_element?(:snippet_action_button, action: 'Delete')
+ has_no_element?(:snippet_action_button, action: 'Delete')
end
end
diff --git a/qa/qa/page/file/edit.rb b/qa/qa/page/file/edit.rb
index b9b676ee3c4..e66019279ce 100644
--- a/qa/qa/page/file/edit.rb
+++ b/qa/qa/page/file/edit.rb
@@ -8,8 +8,8 @@ module QA
include Shared::CommitButton
include Shared::Editor
- view 'app/assets/javascripts/editor/components/source_editor_toolbar_button.vue' do
- element :editor_toolbar_button
+ view 'app/assets/javascripts/editor/extensions/source_editor_markdown_livepreview_ext.js' do
+ element :editor_toolbar_button, "qaSelector: 'editor_toolbar_button'" # rubocop:disable QA/ElementWithPattern
end
def has_markdown_preview?(component, content)
diff --git a/qa/qa/page/group/settings/general.rb b/qa/qa/page/group/settings/general.rb
index 86585eee121..bb5a3485531 100644
--- a/qa/qa/page/group/settings/general.rb
+++ b/qa/qa/page/group/settings/general.rb
@@ -40,6 +40,14 @@ module QA
element :project_creation_level_dropdown
end
+ view 'app/views/groups/settings/_transfer.html.haml' do
+ element :transfer_group_content
+ end
+
+ view 'app/assets/javascripts/groups/components/transfer_group_form.vue' do
+ element :transfer_group_button
+ end
+
def set_group_name(name)
find_element(:group_name_field).send_keys([:command, 'a'], :backspace)
find_element(:group_name_field).set name
@@ -102,6 +110,38 @@ module QA
click_element(:save_permissions_changes_button)
end
+
+ def transfer_group(source_group, target_group)
+ QA::Runtime::Logger.info "Transferring group: #{source_group.path} to target group: #{target_group.path}"
+
+ expand_content(:advanced_settings_content)
+
+ scroll_to_transfer_group_content
+
+ select_namespace(target_group.path)
+
+ wait_for_enabled_transfer_group_button
+ click_element(:transfer_group_button)
+
+ fill_confirmation_text(source_group.path)
+ confirm_transfer
+ end
+
+ private
+
+ def scroll_to_transfer_group_content
+ retry_until(sleep_interval: 1, message: 'Waiting for transfer group content to display') do
+ has_element?(:transfer_group_content, wait: 3)
+ end
+
+ scroll_to_element :transfer_group_content
+ end
+
+ def wait_for_enabled_transfer_group_button
+ retry_until(sleep_interval: 1, message: 'Waiting for transfer group button to be enabled') do
+ has_element?(:transfer_group_button, disabled: false, wait: 3)
+ end
+ end
end
end
end
diff --git a/qa/qa/page/group/settings/group_deploy_tokens.rb b/qa/qa/page/group/settings/group_deploy_tokens.rb
index 7d908f473de..c1c3303113b 100644
--- a/qa/qa/page/group/settings/group_deploy_tokens.rb
+++ b/qa/qa/page/group/settings/group_deploy_tokens.rb
@@ -54,12 +54,10 @@ module QA
private
- def within_new_project_deploy_token
+ def within_new_project_deploy_token(&block)
has_element?(:created_deploy_token_container, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
- within_element(:created_deploy_token_container) do
- yield
- end
+ within_element(:created_deploy_token_container, &block)
end
end
end
diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb
index a30d489e6ff..46ab1e35510 100644
--- a/qa/qa/page/group/show.rb
+++ b/qa/qa/page/group/show.rb
@@ -39,7 +39,7 @@ module QA
end
def group_id
- find_element(:group_id_content).text.delete('Group ID: ')
+ find_element(:group_id_content).text.delete('Group ID: ').sub(/\n.*/, '')
end
def leave_group
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index d7ca8223862..8af78bb86c6 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -12,6 +12,10 @@ module QA
element :change_password_button
end
+ view 'app/views/devise/sessions/new.html.haml' do
+ element :register_link
+ end
+
view 'app/views/devise/sessions/_new_base.html.haml' do
element :login_field
element :password_field
diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb
index ecd71e7c2f4..1e050d79e23 100644
--- a/qa/qa/page/main/menu.rb
+++ b/qa/qa/page/main/menu.rb
@@ -9,6 +9,7 @@ module QA
view 'app/views/layouts/header/_current_user_dropdown.html.haml' do
element :sign_out_link
element :edit_profile_link
+ element :user_profile_link
end
view 'app/views/layouts/header/_default.html.haml' do
@@ -39,6 +40,7 @@ module QA
element :projects_dropdown
element :groups_dropdown
element :snippets_link
+ element :menu_item_link
end
view 'app/views/layouts/_search.html.haml' do
diff --git a/qa/qa/page/merge_request/new.rb b/qa/qa/page/merge_request/new.rb
index 909b37943ff..dc2f908a906 100644
--- a/qa/qa/page/merge_request/new.rb
+++ b/qa/qa/page/merge_request/new.rb
@@ -13,7 +13,7 @@ module QA
element :source_branch_dropdown
end
- view 'app/views/projects/merge_requests/show.html.haml' do
+ view 'app/views/projects/merge_requests/_page.html.haml' do
element :diffs_tab
end
diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb
index e1add9ad434..aacff7c4172 100644
--- a/qa/qa/page/merge_request/show.rb
+++ b/qa/qa/page/merge_request/show.rb
@@ -126,7 +126,7 @@ module QA
element :title_content, required: true
end
- view 'app/views/projects/merge_requests/show.html.haml' do
+ view 'app/views/projects/merge_requests/_page.html.haml' do
element :notes_tab, required: true
element :commits_tab, required: true
element :diffs_tab, required: true
@@ -366,7 +366,7 @@ module QA
# Revisit after merge page re-architect is done https://gitlab.com/gitlab-org/gitlab/-/issues/300042
# To remove page refresh logic if possible
wait_until_ready_to_merge
- wait_until { !find_element(:merge_button).text.include?('when pipeline succeeds') }
+ wait_until { !find_element(:merge_button).text.include?('when pipeline succeeds') } # rubocop:disable Rails/NegateInclude
click_element(:merge_button)
end
@@ -390,6 +390,7 @@ module QA
def click_open_in_web_ide
click_element(:mr_code_dropdown)
click_element(:open_in_web_ide_button)
+ page.driver.browser.switch_to.window(page.driver.browser.window_handles.last)
wait_for_requests
end
@@ -433,7 +434,11 @@ module QA
end
def revert_change!
- click_element(:revert_button, Page::Component::CommitModal)
+ # retry when the modal doesn't appear for large MRs as the onClick listener is initialized after the click
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/366336
+ retry_on_exception do
+ click_element(:revert_button, Page::Component::CommitModal)
+ end
click_element(:submit_commit_button)
end
diff --git a/qa/qa/page/profile/two_factor_auth.rb b/qa/qa/page/profile/two_factor_auth.rb
index 16aa60262d8..2add02b5c48 100644
--- a/qa/qa/page/profile/two_factor_auth.rb
+++ b/qa/qa/page/profile/two_factor_auth.rb
@@ -25,7 +25,8 @@ module QA
def click_configure_it_later_button
# TO DO: Investigate why button does not appear sometimes:
# https://gitlab.com/gitlab-org/gitlab/-/issues/382698
- return unless has_element?(:configure_it_later_button)
+ page.refresh
+ return unless has_element?(:configure_it_later_button, wait: 60)
click_element :configure_it_later_button
wait_until(max_duration: 10, message: "Waiting for create a group page") do
diff --git a/qa/qa/page/project/import/github.rb b/qa/qa/page/project/import/github.rb
index 75468c74814..c48b1a67d90 100644
--- a/qa/qa/page/project/import/github.rb
+++ b/qa/qa/page/project/import/github.rb
@@ -81,7 +81,19 @@ module QA
reload: false,
skip_finished_loading_check_on_refresh: true
) do
- has_element?(:import_status_indicator, text: "Complete")
+ status_selector = 'import_status_indicator'
+ is_partial_import = has_css?(status_selector, text: "Partial import")
+
+ # Temporarily adding this for investigation purposes. This makes sure that the details section is
+ # expanded when the screenshot is taken when the test fails. This can be removed or repurposed later
+ # after investigation. Related: https://gitlab.com/gitlab-org/gitlab/-/issues/385252#note_1211218434
+ if is_partial_import
+ within_element_by_index(:import_status_indicator, 0) do
+ find('button').click
+ end
+ end
+
+ has_element?(status_selector, text: "Complete")
end
end
end
diff --git a/qa/qa/page/project/infrastructure/kubernetes/show.rb b/qa/qa/page/project/infrastructure/kubernetes/show.rb
index 6de5024e525..8725f64fe32 100644
--- a/qa/qa/page/project/infrastructure/kubernetes/show.rb
+++ b/qa/qa/page/project/infrastructure/kubernetes/show.rb
@@ -9,6 +9,9 @@ module QA
view 'app/assets/javascripts/clusters/forms/components/integration_form.vue' do
element :integration_status_toggle
element :base_domain_field
+ end
+
+ view 'app/assets/javascripts/integrations/edit/components/integration_form_actions.vue' do
element :save_changes_button
end
diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb
index b1417d9b9db..2f8ffc634ac 100644
--- a/qa/qa/page/project/issue/show.rb
+++ b/qa/qa/page/project/issue/show.rb
@@ -67,10 +67,6 @@ module QA
click_element :close_issue_button
end
- def has_metrics_unfurled?
- has_element?(:prometheus_graph_widgets, wait: 30)
- end
-
def has_reopen_issue_button?
has_element?(:reopen_issue_button)
end
diff --git a/qa/qa/page/project/job/show.rb b/qa/qa/page/project/job/show.rb
index 5506c5ed4d9..24fd34b4d22 100644
--- a/qa/qa/page/project/job/show.rb
+++ b/qa/qa/page/project/job/show.rb
@@ -15,7 +15,7 @@ module QA
element :pipeline_path, required: true
end
- view 'app/assets/javascripts/jobs/components/job/sidebar/legacy_sidebar_header.vue' do
+ view 'app/assets/javascripts/jobs/components/job/sidebar/sidebar_header.vue' do
element :retry_button
end
diff --git a/qa/qa/page/project/monitor/alerts/index.rb b/qa/qa/page/project/monitor/alerts/index.rb
new file mode 100644
index 00000000000..50b69d59db7
--- /dev/null
+++ b/qa/qa/page/project/monitor/alerts/index.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Project
+ module Monitor
+ module Alerts
+ class Index < Page::Base
+ view 'app/assets/javascripts/alert_management/components/alert_management_table.vue' do
+ element :alert_table_container, required: true
+ end
+
+ def has_alert_with_title?(title)
+ has_link?(title)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/monitor/metrics/show.rb b/qa/qa/page/project/monitor/metrics/show.rb
deleted file mode 100644
index 59602d0fcf7..00000000000
--- a/qa/qa/page/project/monitor/metrics/show.rb
+++ /dev/null
@@ -1,134 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Page
- module Project
- module Monitor
- module Metrics
- class Show < Page::Base
- EXPECTED_TITLE = 'Memory Usage (Total)'
- LOADING_MESSAGE = 'Waiting for performance data'
-
- view 'app/assets/javascripts/monitoring/components/dashboard.vue' do
- element :prometheus_graphs_content
- end
-
- view 'app/assets/javascripts/monitoring/components/dashboard_header.vue' do
- element :dashboards_filter_dropdown
- element :environments_dropdown
- element :range_picker_dropdown
- end
-
- view 'app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue' do
- element :actions_menu_dropdown
- element :edit_dashboard_button_enabled
- end
-
- view 'app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue' do
- element :duplicate_dashboard_filename_field
- end
-
- view 'app/assets/javascripts/monitoring/components/dashboard_panel.vue' do
- element :prometheus_graph_widgets
- element :prometheus_widgets_dropdown
- element :generate_chart_link_menu_item
- end
-
- view 'app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue' do
- element :quick_range_item
- end
-
- view 'app/assets/javascripts/monitoring/components/variables_section.vue' do
- element :variables_content
- element :variable_item
- end
-
- def wait_for_metrics
- wait_for_data
- return if has_metrics?
-
- wait_until(max_duration: 180) do
- wait_for_data
- has_metrics?
- end
- end
-
- def has_metrics?
- within_element :prometheus_graphs_content do
- has_text?(EXPECTED_TITLE)
- end
- end
-
- def has_edit_dashboard_enabled?
- click_element :actions_menu_dropdown
-
- within_element :actions_menu_dropdown do
- has_element? :edit_dashboard_button_enabled
- end
- end
-
- def duplicate_dashboard(save_as = 'test_duplication.yml', commit_option = 'Commit to default branch')
- click_element :actions_menu_dropdown
- click_on 'Duplicate current dashboard'
- fill_element :duplicate_dashboard_filename_field, "#{SecureRandom.hex(8)}-#{save_as}"
- choose commit_option
- within('.modal-content') { click_button(class: 'btn-success') }
- end
-
- def select_dashboard(dashboard_name)
- click_element :dashboards_filter_dropdown
-
- within_element :dashboards_filter_dropdown do
- click_on dashboard_name
- end
- end
-
- def filter_environment(environment = 'production')
- click_element :environments_dropdown
-
- within_element :environments_dropdown do
- click_link_with_text environment
- end
- end
-
- def show_last(range = '8 hours')
- all_elements(:range_picker_dropdown, minimum: 1).first.click
- click_element :quick_range_item, text: range
- end
-
- def copy_link_to_first_chart
- all_elements(:prometheus_widgets_dropdown, minimum: 1).first.click
- find_element(:generate_chart_link_menu_item)['data-clipboard-text']
- end
-
- def has_custom_metric?(metric)
- within_element :prometheus_graphs_content do
- has_text?(metric)
- end
- end
-
- def has_templating_variable?(variable)
- within_element :variables_content do
- has_element?(:variable_item, text: variable)
- end
- end
-
- def has_template_metric?(metric)
- within_element :prometheus_graphs_content do
- has_text?(metric)
- end
- end
-
- private
-
- def wait_for_data
- wait_until(reload: false) { !has_text?(LOADING_MESSAGE) } if has_text?(LOADING_MESSAGE)
- end
- end
- end
- end
- end
- end
-end
-
-QA::Page::Project::Monitor::Metrics::Show.prepend_mod_with('Page::Project::Monitor::Metrics::Show', namespace: QA)
diff --git a/qa/qa/page/project/pipeline/new.rb b/qa/qa/page/project/pipeline/new.rb
index 742fcad5c07..1d85d072e34 100644
--- a/qa/qa/page/project/pipeline/new.rb
+++ b/qa/qa/page/project/pipeline/new.rb
@@ -5,23 +5,49 @@ module QA
module Project
module Pipeline
class New < QA::Page::Base
- view 'app/assets/javascripts/pipeline_new/components/legacy_pipeline_new_form.vue' do
+ view 'app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue' do
element :run_pipeline_button, required: true
element :ci_variable_row_container
element :ci_variable_key_field
element :ci_variable_value_field
+ element :ci_variable_value_dropdown
+ element :ci_variable_value_dropdown_item
end
def click_run_pipeline_button
click_element(:run_pipeline_button, Page::Project::Pipeline::Show)
end
+ def click_variable_dropdown
+ return unless has_variable_dropdown?
+
+ click_element(:ci_variable_value_dropdown)
+ end
+
def configure_variable(key: nil, value: 'foo', row_index: 0)
within_element_by_index(:ci_variable_row_container, row_index) do
fill_element(:ci_variable_key_field, key) unless key.nil?
fill_element(:ci_variable_value_field, value)
end
end
+
+ def has_variable_dropdown?
+ has_element?(:ci_variable_value_dropdown)
+ end
+
+ def variable_dropdown
+ return unless has_variable_dropdown?
+
+ find_element(:ci_variable_value_dropdown)
+ end
+
+ def variable_dropdown_item_with_index(index)
+ return unless has_variable_dropdown?
+
+ within_element_by_index(:ci_variable_value_dropdown_item, index) do
+ find('p')
+ end
+ end
end
end
end
diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb
index 33ba27a788a..e4511ababfd 100644
--- a/qa/qa/page/project/pipeline/show.rb
+++ b/qa/qa/page/project/pipeline/show.rb
@@ -11,6 +11,10 @@ module QA
element :pipeline_header, required: true
end
+ view 'app/views/projects/pipelines/_info.html.haml' do
+ element :merge_request_badge_tag
+ end
+
view 'app/assets/javascripts/pipelines/components/graph/graph_component.vue' do
element :pipeline_graph, /class.*pipeline-graph.*/ # rubocop:disable QA/ElementWithPattern
end
@@ -27,7 +31,7 @@ module QA
element :downstream_title_content
end
- view 'app/assets/javascripts/reports/components/report_section.vue' do
+ view 'app/assets/javascripts/ci/reports/components/report_section.vue' do
element :expand_report_button
end
@@ -46,6 +50,10 @@ module QA
end
end
+ def has_merge_request_badge_tag?
+ has_element?(:merge_request_badge_tag)
+ end
+
def has_build?(name, status: :success, wait: nil)
if status
within_element(:job_item_container, text: name) do
diff --git a/qa/qa/page/project/pipeline_editor/new.rb b/qa/qa/page/project/pipeline_editor/new.rb
index 5d79dd86f2a..da3e772b11f 100644
--- a/qa/qa/page/project/pipeline_editor/new.rb
+++ b/qa/qa/page/project/pipeline_editor/new.rb
@@ -5,7 +5,7 @@ module QA
module Project
module PipelineEditor
class New < QA::Page::Base
- view 'app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_empty_state.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/ui/pipeline_editor_empty_state.vue' do
element :create_new_ci_button, required: true
end
diff --git a/qa/qa/page/project/pipeline_editor/show.rb b/qa/qa/page/project/pipeline_editor/show.rb
index 8fa20aa57cf..0a7a4460d18 100644
--- a/qa/qa/page/project/pipeline_editor/show.rb
+++ b/qa/qa/page/project/pipeline_editor/show.rb
@@ -5,21 +5,21 @@ module QA
module Project
module PipelineEditor
class Show < QA::Page::Base
- view 'app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/pipeline_editor_app.vue' do
element :pipeline_editor_app, required: true
end
- view 'app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/file_nav/branch_switcher.vue' do
element :branch_selector_button, required: true
element :branch_menu_item_button
element :branch_menu_container
end
- view 'app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/commit/commit_form.vue' do
element :source_branch_field, required: true
end
- view 'app/assets/javascripts/pipeline_editor/components/editor/ci_editor_header.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/editor/ci_editor_header.vue' do
element :drawer_toggle, required: true
element :template_repo_link, required: true
end
@@ -28,16 +28,16 @@ module QA
element :source_editor_container, required: true
end
- view 'app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/header/pipeline_status.vue' do
element :pipeline_id_content
end
- view 'app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/commit/commit_form.vue' do
element :commit_changes_button
element :new_mr_checkbox
end
- view 'app/assets/javascripts/pipeline_editor/components/header/validation_segment.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/header/validation_segment.vue' do
element :validation_message_content
end
@@ -46,15 +46,15 @@ module QA
element :job_container
end
- view 'app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/pipeline_editor_tabs.vue' do
element :file_editor_container
end
- view 'app/assets/javascripts/pipeline_editor/components/popovers/file_tree_popover.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/popovers/file_tree_popover.vue' do
element :file_tree_popover
end
- view 'app/assets/javascripts/pipeline_editor/components/validate/ci_validate.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/validate/ci_validate.vue' do
element :simulate_pipeline_button
end
diff --git a/qa/qa/page/project/settings/alerts.rb b/qa/qa/page/project/settings/alerts.rb
index be9b61ded80..a74a227d697 100644
--- a/qa/qa/page/project/settings/alerts.rb
+++ b/qa/qa/page/project/settings/alerts.rb
@@ -6,14 +6,27 @@ module QA
module Settings
class Alerts < Page::Base
view 'app/assets/javascripts/alerts_settings/components/alerts_form.vue' do
- element :create_issue_checkbox
+ element :create_incident_checkbox
element :incident_templates_dropdown
element :save_changes_button
element :incident_templates_item
end
- def enable_issues_for_incidents
- check_element(:create_issue_checkbox)
+ view 'app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue' do
+ element :add_integration_button
+ end
+
+ view 'app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue' do
+ element :integration_type_dropdown
+ element :integration_name_field
+ element :active_toggle_container
+ element :save_and_create_alert_button
+ element :test_payload_field
+ element :send_test_alert_button
+ end
+
+ def enable_incident_for_alert
+ check_element(:create_incident_checkbox)
end
def select_issue_template(template)
@@ -32,6 +45,43 @@ module QA
has_text?(template)
end
end
+
+ def add_new_integration
+ wait_for_requests
+ click_element(:add_integration_button)
+ end
+
+ def select_http_endpoint
+ click_element(:integration_type_dropdown)
+ find("option[value='HTTP']").click
+
+ # Click outside of the list to close it
+ click_element(:integration_name_field)
+ end
+
+ def enter_integration_name(name)
+ fill_element(:integration_name_field, name)
+ end
+
+ def activate_integration
+ within_element(:active_toggle_container) do
+ find('.gl-toggle').click
+ end
+
+ wait_for_requests
+ end
+
+ def save_and_create_alert
+ click_element(:save_and_create_alert_button)
+ end
+
+ def fill_in_test_payload(payload)
+ fill_element(:test_payload_field, payload)
+ end
+
+ def send_test_alert
+ click_element(:send_test_alert_button)
+ end
end
end
end
diff --git a/qa/qa/page/project/settings/merge_request.rb b/qa/qa/page/project/settings/merge_request.rb
index d862979aeec..ae6a04028c9 100644
--- a/qa/qa/page/project/settings/merge_request.rb
+++ b/qa/qa/page/project/settings/merge_request.rb
@@ -15,7 +15,7 @@ module QA
element :merge_ff_radio
end
- view 'app/views/projects/_merge_request_merge_checks_settings.html.haml' do
+ view 'app/views/projects/_merge_request_pipelines_and_threads_options.html.haml' do
element :allow_merge_if_all_discussions_are_resolved_checkbox
end
diff --git a/qa/qa/page/project/settings/monitor.rb b/qa/qa/page/project/settings/monitor.rb
index 87fb0698897..8170ae31a13 100644
--- a/qa/qa/page/project/settings/monitor.rb
+++ b/qa/qa/page/project/settings/monitor.rb
@@ -11,8 +11,18 @@ module QA
element :incidents_settings_content
end
+ view 'app/views/projects/settings/operations/_alert_management.html.haml' do
+ element :alerts_settings_content
+ end
+
def expand_incidents(&block)
expand_content(:incidents_settings_content) do
+ # Fill in with incidents settings
+ end
+ end
+
+ def expand_alerts(&block)
+ expand_content(:alerts_settings_content) do
Settings::Alerts.perform(&block)
end
end
diff --git a/qa/qa/page/project/settings/protected_branches.rb b/qa/qa/page/project/settings/protected_branches.rb
index 4fbf656210f..659fe198d49 100644
--- a/qa/qa/page/project/settings/protected_branches.rb
+++ b/qa/qa/page/project/settings/protected_branches.rb
@@ -5,19 +5,19 @@ module QA
module Project
module Settings
class ProtectedBranches < Page::Base
- view 'app/views/projects/protected_branches/shared/_dropdown.html.haml' do
+ view 'app/views/protected_branches/shared/_dropdown.html.haml' do
element :protected_branch_dropdown
element :protected_branch_dropdown_content
end
- view 'app/views/projects/protected_branches/_create_protected_branch.html.haml' do
+ view 'app/views/protected_branches/_create_protected_branch.html.haml' do
element :allowed_to_push_dropdown
element :allowed_to_push_dropdown_content
element :allowed_to_merge_dropdown
element :allowed_to_merge_dropdown_content
end
- view 'app/views/projects/protected_branches/shared/_create_protected_branch.html.haml' do
+ view 'app/views/protected_branches/shared/_create_protected_branch.html.haml' do
element :protect_button
end
diff --git a/qa/qa/page/project/settings/repository.rb b/qa/qa/page/project/settings/repository.rb
index bf1c3130485..6931d26b259 100644
--- a/qa/qa/page/project/settings/repository.rb
+++ b/qa/qa/page/project/settings/repository.rb
@@ -7,7 +7,7 @@ module QA
class Repository < Page::Base
include QA::Page::Settings::Common
- view 'app/views/projects/protected_branches/shared/_index.html.haml' do
+ view 'app/views/protected_branches/shared/_index.html.haml' do
element :protected_branches_settings_content
end
diff --git a/qa/qa/page/project/settings/services/jenkins.rb b/qa/qa/page/project/settings/services/jenkins.rb
index 39403995ce8..a9b5c84f9ee 100644
--- a/qa/qa/page/project/settings/services/jenkins.rb
+++ b/qa/qa/page/project/settings/services/jenkins.rb
@@ -13,7 +13,7 @@ module QA
element :service_password_field, ':data-qa-selector="`${fieldId}_field`"' # rubocop:disable QA/ElementWithPattern
end
- view 'app/assets/javascripts/integrations/edit/components/integration_form.vue' do
+ view 'app/assets/javascripts/integrations/edit/components/integration_form_actions.vue' do
element :save_changes_button
end
diff --git a/qa/qa/page/project/settings/services/jira.rb b/qa/qa/page/project/settings/services/jira.rb
index 41034bbd897..7a62b111f98 100644
--- a/qa/qa/page/project/settings/services/jira.rb
+++ b/qa/qa/page/project/settings/services/jira.rb
@@ -19,7 +19,7 @@ module QA
element :service_jira_issue_transition_id_field
end
- view 'app/assets/javascripts/integrations/edit/components/integration_form.vue' do
+ view 'app/assets/javascripts/integrations/edit/components/integration_form_actions.vue' do
element :save_changes_button
end
diff --git a/qa/qa/page/project/settings/services/pipeline_status_emails.rb b/qa/qa/page/project/settings/services/pipeline_status_emails.rb
index 2f78577e3d5..3edd1d61d76 100644
--- a/qa/qa/page/project/settings/services/pipeline_status_emails.rb
+++ b/qa/qa/page/project/settings/services/pipeline_status_emails.rb
@@ -9,6 +9,9 @@ module QA
view 'app/assets/javascripts/integrations/edit/components/integration_form.vue' do
element :recipients_div, %q(:data-qa-selector="`${field.name}_div`") # rubocop:disable QA/ElementWithPattern
element :notify_only_broken_pipelines_div, %q(:data-qa-selector="`${field.name}_div`") # rubocop:disable QA/ElementWithPattern
+ end
+
+ view 'app/assets/javascripts/integrations/edit/components/integration_form_actions.vue' do
element :save_changes_button
end
diff --git a/qa/qa/page/project/settings/services/prometheus.rb b/qa/qa/page/project/settings/services/prometheus.rb
deleted file mode 100644
index 2e3c385a27d..00000000000
--- a/qa/qa/page/project/settings/services/prometheus.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Page
- module Project
- module Settings
- module Services
- class Prometheus < Page::Base
- include Page::Component::CustomMetric
-
- view 'app/views/shared/integrations/prometheus/_custom_metrics.html.haml' do
- element :custom_metrics_container
- element :new_metric_button
- end
-
- def click_on_custom_metric(custom_metric)
- within_element :custom_metrics_container do
- click_on custom_metric
- end
- end
-
- def click_on_new_metric
- click_element :new_metric_button
- end
-
- def has_custom_metric?(custom_metric)
- within_element :custom_metrics_container do
- has_text? custom_metric
- end
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index a82fa7f5cf3..168bfd6aa0a 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -36,7 +36,6 @@ module QA
end
view 'app/views/projects/_home_panel.html.haml' do
- element :forked_from_link
element :project_name_content
element :project_id_content
element :project_badges_content
@@ -48,6 +47,10 @@ module QA
element :tree_holder, '.tree-holder' # rubocop:disable QA/ElementWithPattern
end
+ view 'app/views/projects/_fork_info.html.haml' do
+ element :forked_from_link
+ end
+
view 'app/views/projects/buttons/_fork.html.haml' do
element :fork_label, "%span= s_('ProjectOverview|Fork')" # rubocop:disable QA/ElementWithPattern
element :fork_link, "link_to new_project_fork_path(@project)" # rubocop:disable QA/ElementWithPattern
diff --git a/qa/qa/page/project/sub_menus/monitor.rb b/qa/qa/page/project/sub_menus/monitor.rb
index e3593e0a257..27fb58fb146 100644
--- a/qa/qa/page/project/sub_menus/monitor.rb
+++ b/qa/qa/page/project/sub_menus/monitor.rb
@@ -15,18 +15,18 @@ module QA
end
end
- def go_to_monitor_metrics
+ def go_to_monitor_incidents
hover_monitor do
within_submenu do
- click_element(:sidebar_menu_item_link, menu_item: 'Metrics')
+ click_element(:sidebar_menu_item_link, menu_item: 'Incidents')
end
end
end
- def go_to_monitor_incidents
+ def go_to_monitor_alerts
hover_monitor do
within_submenu do
- click_element(:sidebar_menu_item_link, menu_item: 'Incidents')
+ click_element(:sidebar_menu_item_link, menu_item: 'Alerts')
end
end
end
diff --git a/qa/qa/page/project/web_ide/edit.rb b/qa/qa/page/project/web_ide/edit.rb
index 293fcd1e676..975d3c8ea14 100644
--- a/qa/qa/page/project/web_ide/edit.rb
+++ b/qa/qa/page/project/web_ide/edit.rb
@@ -109,6 +109,14 @@ module QA
element :file_to_commit_content
end
+ # Used for stablility, due to feature_caching of vscode_web_ide
+ def wait_until_ide_loads
+ Support::Waiter.wait_until(sleep_interval: 2, max_duration: 60, reload_page: page,
+ retry_on_exception: true) do
+ has_element?(:commit_mode_tab)
+ end
+ end
+
def has_file?(file_name)
within_element(:file_list_container) do
has_element?(:file_name_content, file_name: file_name)
diff --git a/qa/qa/resource/api_fabricator.rb b/qa/qa/resource/api_fabricator.rb
index d1cfdfbc16c..d82109c1d54 100644
--- a/qa/qa/resource/api_fabricator.rb
+++ b/qa/qa/resource/api_fabricator.rb
@@ -170,9 +170,7 @@ module QA
end
def api_client
- @api_client ||= begin
- Runtime::API::Client.new(:gitlab, is_new_session: !current_url.start_with?('http'), user: api_user)
- end
+ @api_client ||= Runtime::API::Client.new(:gitlab, is_new_session: !current_url.start_with?('http'), user: api_user)
end
def process_api_response(parsed_response)
diff --git a/qa/qa/resource/base.rb b/qa/qa/resource/base.rb
index 4a1a60f4da1..00c002cae9c 100644
--- a/qa/qa/resource/base.rb
+++ b/qa/qa/resource/base.rb
@@ -23,7 +23,7 @@ module QA
end
def fabricate_via_api_unless_fips!
- if QA::Support::FIPS.enabled?
+ if Runtime::Env.personal_access_tokens_disabled?
fabricate!
else
fabricate_via_api!
@@ -31,7 +31,7 @@ module QA
end
def fabricate!(*args, &prepare_block)
- if QA::Support::FIPS.enabled?
+ if Runtime::Env.personal_access_tokens_disabled?
fabricate_via_browser_ui!(*args, &prepare_block)
else
fabricate_via_api!(*args, &prepare_block)
@@ -107,7 +107,7 @@ module QA
Support::FabricationTracker.save_fabrication(:"#{fabrication_method}_fabrication", fabrication_time)
- unless resource.retrieved_from_cache || QA::Support::FIPS.enabled?
+ unless resource.retrieved_from_cache || Runtime::Env.personal_access_tokens_disabled?
Tools::TestResourceDataProcessor.collect(
resource: resource,
info: resource.identifier,
diff --git a/qa/qa/resource/bulk_import_group.rb b/qa/qa/resource/bulk_import_group.rb
index 31db8ae4cc6..19ad5f1faf2 100644
--- a/qa/qa/resource/bulk_import_group.rb
+++ b/qa/qa/resource/bulk_import_group.rb
@@ -11,7 +11,7 @@ module QA
api_client.personal_access_token
end
- attribute :gitlab_address do
+ attribute :source_gitlab_address do
QA::Runtime::Scenario.gitlab_address
end
@@ -28,7 +28,7 @@ module QA
Page::Group::New.perform do |group|
group.switch_to_import_tab
- group.connect_gitlab_instance(gitlab_address, import_access_token)
+ group.connect_gitlab_instance(source_gitlab_address, import_access_token)
end
Page::Group::BulkImport.perform do |import_page|
@@ -50,7 +50,7 @@ module QA
def api_post_body
{
configuration: {
- url: gitlab_address,
+ url: source_gitlab_address,
access_token: import_access_token
},
entities: [
@@ -93,7 +93,7 @@ module QA
# override transformation only for /bulk_imports endpoint which doesn't have web_url in response and
# ignore others so import_id is not overwritten incorrectly
- api_resource[:web_url] = "#{gitlab_address}/#{full_path}"
+ api_resource[:web_url] = "#{QA::Runtime::Scenario.gitlab_address}/#{full_path}"
api_resource[:import_id] = api_resource[:id]
api_resource
end
diff --git a/qa/qa/resource/group.rb b/qa/qa/resource/group.rb
index 9d1a6868562..f53bb531d9a 100644
--- a/qa/qa/resource/group.rb
+++ b/qa/qa/resource/group.rb
@@ -18,7 +18,7 @@ module QA
end
attribute :sandbox do
- if QA::Support::FIPS.enabled?
+ if Runtime::Env.personal_access_tokens_disabled?
Resource::Sandbox.fabricate! do |sandbox|
sandbox.path = Runtime::Namespace.sandbox_name
end
@@ -40,9 +40,7 @@ module QA
sandbox.visit!
Page::Group::Show.perform do |group_show|
- if group_show.has_subgroup?(path)
- group_show.click_subgroup(path)
- else
+ unless group_show.has_subgroup?(path)
group_show.go_to_new_subgroup
Page::Group::New.perform do |group_new|
@@ -56,7 +54,11 @@ module QA
group_show.has_text?(path) &&
group_show.has_new_project_and_new_subgroup_buttons?
end
+ sandbox.visit!
end
+
+ group_show.click_subgroup(path)
+ @id = group_show.group_id
end
end
diff --git a/qa/qa/resource/group_base.rb b/qa/qa/resource/group_base.rb
index f6d1aacca0a..c5b1a4ecea0 100644
--- a/qa/qa/resource/group_base.rb
+++ b/qa/qa/resource/group_base.rb
@@ -24,6 +24,7 @@ module QA
def projects
parse_body(api_get_from("#{api_get_path}/projects")).map do |project|
Project.init do |resource|
+ resource.add_name_uuid = false
resource.api_client = api_client
resource.group = self
resource.id = project[:id]
diff --git a/qa/qa/resource/issuable.rb b/qa/qa/resource/issuable.rb
index 6ebdaac8298..5aee27b46d4 100644
--- a/qa/qa/resource/issuable.rb
+++ b/qa/qa/resource/issuable.rb
@@ -3,6 +3,8 @@
module QA
module Resource
class Issuable < Base
+ using Rainbow
+
# Commentes (notes) path
#
# @return [String]
@@ -14,6 +16,7 @@ module QA
#
# @return [Array]
def comments(auto_paginate: false, attempts: 0)
+ Runtime::Logger.debug("Fetching comments for #{self.class.name.black.bg(:white)} with path '#{api_get_path}'")
return parse_body(api_get_from(api_comments_path)) unless auto_paginate
auto_paginated_response(
diff --git a/qa/qa/resource/merge_request.rb b/qa/qa/resource/merge_request.rb
index fcfda106523..d1d99393ca2 100644
--- a/qa/qa/resource/merge_request.rb
+++ b/qa/qa/resource/merge_request.rb
@@ -199,6 +199,10 @@ module QA
:source_project_id,
:target_project_id,
:merge_status,
+ # we consider mr to still be the same even if users changed
+ :author,
+ :reviewers,
+ :assignees,
# these can differ depending on user fetching mr
:user,
:subscribed,
diff --git a/qa/qa/resource/protected_branch.rb b/qa/qa/resource/protected_branch.rb
index 7b6a3d296c4..879c3a4282c 100644
--- a/qa/qa/resource/protected_branch.rb
+++ b/qa/qa/resource/protected_branch.rb
@@ -51,8 +51,8 @@ module QA
page.select_allowed_to_merge(allowed_to_merge)
page.select_allowed_to_push(allowed_to_push)
page.protect_branch
- else
- page.require_code_owner_approval(branch_name) if require_code_owner_approval
+ elsif require_code_owner_approval
+ page.require_code_owner_approval(branch_name)
end
end
end
diff --git a/qa/qa/resource/reusable.rb b/qa/qa/resource/reusable.rb
index 6a9d0392ba2..536f70b50b4 100644
--- a/qa/qa/resource/reusable.rb
+++ b/qa/qa/resource/reusable.rb
@@ -37,8 +37,8 @@ module QA
resource: self
}
- self.class.resources[reuse_as][:attributes] ||= all_attributes.each_with_object({}) do |attribute_name, attributes|
- attributes[attribute_name] = instance_variable_get("@#{attribute_name}")
+ self.class.resources[reuse_as][:attributes] ||= all_attributes.index_with do |attribute_name|
+ instance_variable_get("@#{attribute_name}")
end
self.class.resources[reuse_as][:tests] << Runtime::Example.location
end
diff --git a/qa/qa/resource/runner.rb b/qa/qa/resource/runner.rb
index da4021f89b7..3c74d8de21a 100644
--- a/qa/qa/resource/runner.rb
+++ b/qa/qa/resource/runner.rb
@@ -3,10 +3,22 @@
module QA
module Resource
class Runner < Base
- attr_writer :name, :tags, :image, :executor, :executor_image
- attr_accessor :config, :token, :run_untagged
+ attributes :id,
+ :active,
+ :paused,
+ :runner_type,
+ :online,
+ :status,
+ :ip_address,
+ :token,
+ :tags,
+ :config,
+ :run_untagged,
+ :name, # This attribute == runner[:description]
+ :image,
+ :executor,
+ :executor_image
- attribute :id
attribute :project do
Project.fabricate_via_api! do |resource|
resource.name = 'project-with-ci-cd'
@@ -14,81 +26,47 @@ module QA
end
end
- def name
- @name || "qa-runner-#{SecureRandom.hex(4)}"
+ def initialize
+ @tags = nil
+ @config = nil
+ @run_untagged = nil
+ @name = "qa-runner-#{SecureRandom.hex(4)}"
+ @image = 'registry.gitlab.com/gitlab-org/gitlab-runner:alpine'
+ @executor = :shell
+ @executor_image = 'registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.7'
end
- def image
- @image || 'registry.gitlab.com/gitlab-org/gitlab-runner:alpine'
- end
-
- def executor
- @executor || :shell
- end
-
- def executor_image
- @executor_image || 'registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.7'
+ # Initially we only support fabricate
+ # via API
+ def fabricate!
+ fabricate_via_api!
end
+ # Start container and register runner
+ # Fetch via API and populate attributes
+ #
def fabricate_via_api!
- @docker_container = Service::DockerRun::GitlabRunner.new(name).tap do |runner|
- QA::Support::Retrier.retry_on_exception(sleep_interval: 5) do
- runner.pull
- end
-
- runner.token = @token ||= project.runners_token
- runner.address = Runtime::Scenario.gitlab_address
- runner.tags = @tags if @tags
- runner.image = image
- runner.config = config if config
- runner.executor = executor
- runner.executor_image = executor_image if executor == :docker
- runner.run_untagged = run_untagged if run_untagged
- runner.register!
- end
+ start_container_and_register
+ populate_runner_attributes
end
def remove_via_api!
- runners = list_of_runners(tag_list: @tags)
-
- # If we have no runners, print the logs from the runner docker container in case they show why it isn't running.
- if runners.blank?
- dump_logs
-
- return
- end
-
- this_runner = runners.find { |runner| runner[:description] == name }
-
- # As above, but now we should have a specific runner. If not, print the logs from the runner docker container
- # to see if we can find out why the runner isn't running.
- unless this_runner
- dump_logs
-
- raise "Project #{project.path_with_namespace} does not have a runner with a description matching #{name} #{"or tags #{@tags}" if @tags&.any?}. Runners available: #{runners}"
- end
-
- @id = this_runner[:id]
-
super
ensure
- Service::DockerRun::GitlabRunner.new(name).remove!
- end
-
- def list_of_runners(tag_list: nil)
- url = tag_list ? "#{api_post_path}?tag_list=#{tag_list.compact.join(',')}" : api_post_path
- auto_paginated_response(request_url(url))
+ @docker_container.remove!
end
def reload!
- super if method(:running?).super_method.call
+ populate_runner_attributes
end
def api_delete_path
"/runners/#{id}"
end
- def api_get_path; end
+ def api_get_path
+ "/runners"
+ end
def api_post_path
"/runners"
@@ -96,15 +74,75 @@ module QA
def api_post_body; end
+ def not_found_by_tags?
+ url = "#{api_get_path}?tag_list=#{tags.compact.join(',')}"
+ auto_paginated_response(request_url(url)).empty?
+ end
+
+ def runners_list
+ runners_list = nil
+ url = tags ? "#{api_get_path}?tag_list=#{tags.compact.join(',')}" : api_get_path
+ Runtime::Logger.info('Looking for list of runners via API...')
+ Support::Retrier.retry_until(max_duration: 60, sleep_interval: 1) do
+ runners_list = auto_paginated_response(request_url(url))
+ runners_list.present?
+ end
+
+ runners_list
+ end
+
+ def wait_until_online
+ Runtime::Logger.info('Waiting for runner to come online...')
+ Support::Retrier.retry_until(max_duration: 60, sleep_interval: 1) do
+ this_runner[:status] == 'online'
+ end
+ end
+
+ def restart
+ Runtime::Logger.info("Restarting runner container #{name}...")
+ @docker_container.restart
+ wait_until_online
+ end
+
private
- def dump_logs
- if @docker_container.running?
- @docker_container.logs
- else
- QA::Runtime::Logger.debug("No runner container found named #{name}")
+ def start_container_and_register
+ @docker_container = Service::DockerRun::GitlabRunner.new(name).tap do |runner|
+ Support::Retrier.retry_on_exception(sleep_interval: 5) do
+ runner.pull
+ end
+
+ runner.token = @token ||= project.runners_token
+ runner.address = Runtime::Scenario.gitlab_address
+ runner.tags = tags if tags
+ runner.image = image
+ runner.config = config if config
+ runner.executor = executor
+ runner.executor_image = executor_image if executor == :docker
+ runner.run_untagged = run_untagged if run_untagged
+ runner.register!
end
end
+
+ def this_runner
+ runner = nil
+ Support::Retrier.retry_until(max_duration: 60, sleep_interval: 1) do
+ runner = runners_list.find { |runner| runner[:description] == name }
+ !runner.nil?
+ end
+ runner
+ end
+
+ def populate_runner_attributes
+ runner = this_runner
+ @id = runner[:id]
+ @active = runner[:active]
+ @paused = runner[:paused]
+ @runner_type = runner[:typed]
+ @online = runner[:online]
+ @status = runner[:status]
+ @ip_address = runner[:ip_address]
+ end
end
end
end
diff --git a/qa/qa/resource/sandbox.rb b/qa/qa/resource/sandbox.rb
index 18526448b00..f5cd51bf9cf 100644
--- a/qa/qa/resource/sandbox.rb
+++ b/qa/qa/resource/sandbox.rb
@@ -10,7 +10,7 @@ module QA
class << self
# Force top level group creation via UI if test is executed on dot_com environment
def fabricate!(*args, &prepare_block)
- if Specs::Helpers::ContextSelector.dot_com? || QA::Support::FIPS.enabled?
+ if Specs::Helpers::ContextSelector.dot_com? || Runtime::Env.personal_access_tokens_disabled?
return fabricate_via_browser_ui!(*args, &prepare_block)
end
diff --git a/qa/qa/resource/ssh_key.rb b/qa/qa/resource/ssh_key.rb
index dd475d7fa66..1c142058908 100644
--- a/qa/qa/resource/ssh_key.rb
+++ b/qa/qa/resource/ssh_key.rb
@@ -3,14 +3,12 @@
module QA
module Resource
class SSHKey < Base
- extend Forwardable
-
attr_reader :title
attr_accessor :expires_at
attribute :id
- def_delegators :key, :private_key, :public_key, :md5_fingerprint, :sha256_fingerprint
+ delegate :private_key, :public_key, :md5_fingerprint, :sha256_fingerprint, to: :key
def initialize
self.title = Time.now.to_f
diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb
index c8babbc0b16..0398509396f 100644
--- a/qa/qa/resource/user.rb
+++ b/qa/qa/resource/user.rb
@@ -155,7 +155,7 @@ module QA
end
def self.fabricate_or_use(username = nil, password = nil)
- if Runtime::Env.signup_disabled? || !QA::Support::FIPS.enabled?
+ if Runtime::Env.signup_disabled? && !Runtime::Env.personal_access_tokens_disabled?
fabricate_via_api! do |user|
user.username = username
user.password = password
diff --git a/qa/qa/runtime/api/client.rb b/qa/qa/runtime/api/client.rb
index 5ca3b0c51f8..213388ca264 100644
--- a/qa/qa/runtime/api/client.rb
+++ b/qa/qa/runtime/api/client.rb
@@ -35,9 +35,12 @@ module QA
end
def self.as_admin
- @admin_client ||= begin
+ @admin_client ||=
if Runtime::Env.admin_personal_access_token
- Runtime::API::Client.new(:gitlab, personal_access_token: Runtime::Env.admin_personal_access_token)
+ Runtime::API::Client.new(
+ :gitlab,
+ personal_access_token: Runtime::Env.admin_personal_access_token
+ )
else
# To return an API client that has admin access, we need a user with admin access to confirm that
# the API client user has admin access.
@@ -62,7 +65,6 @@ module QA
client
end
- end
end
private
diff --git a/qa/qa/runtime/api/repository_storage_moves.rb b/qa/qa/runtime/api/repository_storage_moves.rb
index fb8d70c0836..450b7cd5712 100644
--- a/qa/qa/runtime/api/repository_storage_moves.rb
+++ b/qa/qa/runtime/api/repository_storage_moves.rb
@@ -16,7 +16,7 @@ module QA
QA::Runtime::Logger.debug("Move data: #{move}")
move[:state] == status &&
- move[:destination_storage_name] == destination_storage
+ move[:destination_storage_name] == destination_storage
end
end
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index d2ddaf86353..af1a4e06473 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -160,15 +160,14 @@ module QA
# From https://github.com/mattheworiordan/capybara-screenshot/issues/84#issuecomment-41219326
Capybara::Screenshot.register_driver(QA::Runtime::Env.browser) do |driver, path|
+ QA::Runtime::Logger.info("Saving screenshot..")
driver.browser.save_screenshot(path)
end
- Capybara::Screenshot.append_timestamp = false
-
Capybara::Screenshot.register_filename_prefix_formatter(:rspec) do |example|
::File.join(
QA::Runtime::Namespace.name(reset_cache: false),
- example.full_description.downcase.parameterize(separator: "_")[0..99]
+ example.full_description.downcase.parameterize(separator: "_")[0..79]
)
end
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index 7cb7625118e..d4d9ffe62e6 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -133,6 +133,11 @@ module QA
enabled?(ENV['SIGNUP_DISABLED'], default: false)
end
+ # PATs are disabled for FedRamp
+ def personal_access_tokens_disabled?
+ enabled?(ENV['PERSONAL_ACCESS_TOKENS_DISABLED'], default: false)
+ end
+
def admin_password
ENV['GITLAB_ADMIN_PASSWORD']
end
@@ -431,7 +436,7 @@ module QA
end
def gitlab_agentk_version
- ENV.fetch('GITLAB_AGENTK_VERSION', 'v14.5.0')
+ ENV.fetch('GITLAB_AGENTK_VERSION', 'fe716ea')
end
def transient_trials
@@ -493,6 +498,10 @@ module QA
enabled?(ENV['QA_USE_PUBLIC_IP_API'], default: false)
end
+ def allow_local_requests?
+ enabled?(ENV['QA_ALLOW_LOCAL_REQUESTS'], default: false)
+ end
+
def chrome_default_download_path
ENV['DEFAULT_CHROME_DOWNLOAD_PATH']
end
diff --git a/qa/qa/runtime/ip_address.rb b/qa/qa/runtime/ip_address.rb
index fcb6db750bb..ae83d10ffb5 100644
--- a/qa/qa/runtime/ip_address.rb
+++ b/qa/qa/runtime/ip_address.rb
@@ -8,16 +8,17 @@ module QA
HostUnreachableError = Class.new(StandardError)
LOOPBACK_ADDRESS = '127.0.0.1'
- PUBLIC_IP_ADDRESS_API = "https://api.ipify.org"
+ PUBLIC_IP_ADDRESS_API = 'https://api.ipify.org'
def fetch_current_ip_address
# When running on CI against a live environment such as staging.gitlab.com,
# we use the public facing IP address
- non_test_host = !URI.parse(Scenario.gitlab_address).host.include?('.test')
+ non_test_host = !URI.parse(Scenario.gitlab_address).host.include?('.test') # rubocop:disable Rails/NegateInclude
has_no_public_ip = Env.running_in_ci? || Env.use_public_ip_api?
ip_address = if has_no_public_ip && non_test_host
- response = get(PUBLIC_IP_ADDRESS_API)
+ response = get_public_ip_address
+
raise HostUnreachableError, "#{PUBLIC_IP_ADDRESS_API} is unreachable" unless response.code == Support::API::HTTP_STATUS_OK
response.body
@@ -31,6 +32,12 @@ module QA
ip_address
end
+
+ def get_public_ip_address
+ Support::Retrier.retry_on_exception(sleep_interval: 1) do
+ get(PUBLIC_IP_ADDRESS_API)
+ end
+ end
end
end
end
diff --git a/qa/qa/runtime/logger.rb b/qa/qa/runtime/logger.rb
index e0e7385d6d4..7e78ba470d8 100644
--- a/qa/qa/runtime/logger.rb
+++ b/qa/qa/runtime/logger.rb
@@ -1,23 +1,21 @@
# frozen_string_literal: true
-require 'forwardable'
-
module QA
module Runtime
class Logger
- extend SingleForwardable
-
- def_delegators :logger, :debug, :info, :warn, :error, :fatal, :unknown
+ class << self
+ # Global logger instance
+ #
+ # @return [ActiveSupport::Logger]
+ def logger
+ @logger ||= Gitlab::QA::TestLogger.logger(
+ level: Gitlab::QA::Runtime::Env.log_level,
+ source: 'QA Tests',
+ path: File.expand_path('../../tmp', __dir__)
+ )
+ end
- # Global logger instance
- #
- # @return [ActiveSupport::Logger]
- def self.logger
- @logger ||= Gitlab::QA::TestLogger.logger(
- level: Gitlab::QA::Runtime::Env.log_level,
- source: 'QA Tests',
- path: File.expand_path('../../tmp', __dir__)
- )
+ delegate :debug, :info, :warn, :error, :fatal, :unknown, to: :logger
end
end
end
diff --git a/qa/qa/runtime/script_extensions/interceptor.js b/qa/qa/runtime/script_extensions/interceptor.js
index 9e98b0421b4..cde94e98774 100644
--- a/qa/qa/runtime/script_extensions/interceptor.js
+++ b/qa/qa/runtime/script_extensions/interceptor.js
@@ -101,6 +101,34 @@
}
/**
+ * @param url - the URL
+ * @param method - the REST method
+ * @param clonedResponse - a cloned fetch response
+ * @return {Promise<void>}
+ */
+ async function checkForGraphQLErrors(url, method, clonedResponse) {
+ if (/api\/graphql/.test(url)) {
+ const body = await clonedResponse.json();
+ if (body.errors && body.errors instanceof Array) {
+ const errorMessages = body.errors.map((error) => error.message);
+
+ commitToCache((cache) => {
+ // eslint-disable-next-line no-param-reassign
+ cache.errors ||= [];
+ cache.errors.push({
+ status: clonedResponse.status,
+ url,
+ method,
+ errorData: `error-messages: ${errorMessages.join(', ')}`,
+ headers: { 'x-request-id': clonedResponse.headers.get('x-request-id') },
+ });
+ return cache;
+ });
+ }
+ }
+ }
+
+ /**
* Replacement for fetch implementation
* tracks active requests, and commits metadata to the cache
* if the response is not ok or was cancelled.
@@ -115,7 +143,6 @@
window.Interceptor.activeFetchRequests += 1;
try {
const response = await pureFetch(url, opts, ...args);
- window.Interceptor.activeFetchRequests += -1;
const clone = response.clone();
if (!clone.ok) {
@@ -131,6 +158,9 @@
return cache;
});
}
+
+ await checkForGraphQLErrors(url, method, clone);
+
return response;
} catch (error) {
commitToCache((cache) => {
@@ -144,14 +174,24 @@
return cache;
});
- window.Interceptor.activeFetchRequests += -1;
throw error;
+ } finally {
+ window.Interceptor.activeFetchRequests += -1;
}
}
- if (checkCache()) {
- saveCache({});
- }
+ /**
+ * Initializes the cache
+ * if the cache doesn't already exist.
+ */
+ const initCache = () => {
+ if (checkCache() && getCache() == null) {
+ saveCache({});
+ }
+ };
+
+ // Initialize cache on page load.
+ initCache();
window.fetch = interceptedFetch;
window.XMLHttpRequest.prototype.open = interceptXhr;
diff --git a/qa/qa/scenario/test/integration/import.rb b/qa/qa/scenario/test/integration/import.rb
new file mode 100644
index 00000000000..4b0966998cd
--- /dev/null
+++ b/qa/qa/scenario/test/integration/import.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Integration
+ class Import < Test::Instance::All
+ tags :import
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/docker_run/base.rb b/qa/qa/service/docker_run/base.rb
index c91f68d31a0..65ebe09eeea 100644
--- a/qa/qa/service/docker_run/base.rb
+++ b/qa/qa/service/docker_run/base.rb
@@ -77,6 +77,12 @@ module QA
def read_file(file_path)
`docker exec #{@name} /bin/cat #{file_path}`
end
+
+ def restart
+ return "Container #{@name} is not running, cannot restart." unless running?
+
+ shell "docker restart #{@name}"
+ end
end
end
end
diff --git a/qa/qa/service/docker_run/gitlab_runner.rb b/qa/qa/service/docker_run/gitlab_runner.rb
index 45ab4ceff99..7a62951a2f6 100644
--- a/qa/qa/service/docker_run/gitlab_runner.rb
+++ b/qa/qa/service/docker_run/gitlab_runner.rb
@@ -56,6 +56,12 @@ module QA
@run_untagged = false
end
+ def restart
+ super
+
+ wait_until_shell_command_matches("docker logs #{@name}", /Configuration loaded/)
+ end
+
private
def register_command
diff --git a/qa/qa/service/praefect_manager.rb b/qa/qa/service/praefect_manager.rb
index c332e7a6198..57f5310901b 100644
--- a/qa/qa/service/praefect_manager.rb
+++ b/qa/qa/service/praefect_manager.rb
@@ -9,6 +9,8 @@ module QA
attr_accessor :gitlab
+ attr_reader :primary_node, :secondary_node, :tertiary_node, :postgres
+
PrometheusQueryError = Class.new(StandardError)
def initialize
@@ -21,7 +23,9 @@ module QA
@virtual_storage = 'default'
end
- attr_reader :primary_node, :secondary_node, :tertiary_node, :postgres
+ def gitaly_nodes
+ [primary_node, secondary_node, tertiary_node]
+ end
# Executes the praefect `dataloss` command.
#
@@ -50,42 +54,22 @@ module QA
end
end
- def stop_primary_node
- stop_node(@primary_node)
- wait_until_node_is_removed_from_healthy_storages(@primary_node)
- end
-
- def start_primary_node
- start_node(@primary_node)
- end
-
def start_praefect
start_node(@praefect)
- wait_for_praefect
+ QA::Runtime::Logger.info("Waiting for health check on praefect")
+ Support::Waiter.wait_until(max_duration: 120, sleep_interval: 1, raise_on_failure: true) do
+ wait_until_shell_command("docker exec #{@praefect} gitlab-ctl status praefect") do |line|
+ break true if line.include?('run: praefect: ')
+
+ QA::Runtime::Logger.debug(line.chomp)
+ end
+ end
end
def stop_praefect
stop_node(@praefect)
end
- def stop_secondary_node
- stop_node(@secondary_node)
- wait_until_node_is_removed_from_healthy_storages(@secondary_node)
- end
-
- def start_secondary_node
- start_node(@secondary_node)
- end
-
- def stop_tertiary_node
- stop_node(@tertiary_node)
- wait_until_node_is_removed_from_healthy_storages(@tertiary_node)
- end
-
- def start_tertiary_node
- start_node(@tertiary_node)
- end
-
def start_node(name)
state = node_state(name)
return if state == "running"
@@ -111,6 +95,8 @@ module QA
return if node_state(name) == 'paused'
shell "docker pause #{name}"
+
+ wait_until_node_is_removed_from_healthy_storages(name) if gitaly_nodes.include?(name)
end
def node_state(name)
@@ -126,9 +112,9 @@ module QA
QA::Runtime::Logger.info("Clearing the replication queue")
shell sql_to_docker_exec_cmd(
<<~SQL
- delete from replication_queue_job_lock;
- delete from replication_queue_lock;
- delete from replication_queue;
+ delete from replication_queue_job_lock;
+ delete from replication_queue_lock;
+ delete from replication_queue;
SQL
)
end
@@ -137,32 +123,16 @@ module QA
QA::Runtime::Logger.info("Setting jobs in replication queue to `in_progress` and acquiring locks")
shell sql_to_docker_exec_cmd(
<<~SQL
- update replication_queue set state = 'in_progress';
- insert into replication_queue_job_lock (job_id, lock_id, triggered_at)
- select id, rq.lock_id, created_at from replication_queue rq
- left join replication_queue_job_lock rqjl on rq.id = rqjl.job_id
- where state = 'in_progress' and rqjl.job_id is null;
- update replication_queue_lock set acquired = 't';
+ update replication_queue set state = 'in_progress';
+ insert into replication_queue_job_lock (job_id, lock_id, triggered_at)
+ select id, rq.lock_id, created_at from replication_queue rq
+ left join replication_queue_job_lock rqjl on rq.id = rqjl.job_id
+ where state = 'in_progress' and rqjl.job_id is null;
+ update replication_queue_lock set acquired = 't';
SQL
)
end
- # Reconciles the previous primary node with the current one
- # I.e., it brings the previous primary node up-to-date
- def reconcile_nodes
- reconcile_node_with_node(@primary_node, current_primary_node)
- end
-
- def reconcile_node_with_node(target, reference)
- QA::Runtime::Logger.info("Reconcile #{target} with #{reference} on #{@virtual_storage}")
- wait_until_shell_command_matches(
- "docker exec #{@praefect} bash -c '/opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml reconcile -virtual #{@virtual_storage} -target #{target} -reference #{reference} -f'",
- /FINISHED: \d+ repos were checked for consistency/,
- sleep_interval: 5,
- retry_on_exception: true
- )
- end
-
def query_read_distribution
cmd = "docker exec #{@gitlab} bash -c 'curl -s http://localhost:9090/api/v1/query?query=gitaly_praefect_read_distribution'"
output = shell(cmd, stream_progress: false) do |line|
@@ -173,6 +143,8 @@ module QA
raise PrometheusQueryError, "Unable to query read distribution metrics" unless result['status'] == 'success'
+ raise PrometheusQueryError, "No read distribution metrics found" if result['data']['result'].empty?
+
result['data']['result'].map { |result| { node: result['metric']['storage'], value: result['value'][1].to_i } }
end
@@ -202,9 +174,7 @@ module QA
def start_all_nodes
start_postgres
- start_node(@primary_node)
- start_node(@secondary_node)
- start_node(@tertiary_node)
+ gitaly_nodes.each { |node| start_node(node) }
start_praefect
wait_for_health_check_all_nodes
@@ -228,17 +198,6 @@ module QA
destination_storage[:type] == :praefect ? verify_storage_move_to_praefect(repo_path, destination_storage[:name]) : verify_storage_move_to_gitaly(repo_path, destination_storage[:name])
end
- def wait_for_praefect
- QA::Runtime::Logger.info("Waiting for health check on praefect")
- Support::Waiter.wait_until(max_duration: 120, sleep_interval: 1, raise_on_failure: true) do
- wait_until_shell_command("docker exec #{@praefect} gitlab-ctl status praefect") do |line|
- break true if line.include?('run: praefect: ')
-
- QA::Runtime::Logger.debug(line.chomp)
- end
- end
- end
-
def praefect_sql_ping_healthy?
cmd = "docker exec #{@praefect} bash -c '/opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-ping'"
wait_until_shell_command(cmd) do |line|
@@ -247,17 +206,6 @@ module QA
end
end
- def wait_for_sql_ping
- wait_until_shell_command_matches(
- "docker exec #{@praefect} bash -c '/opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-ping'",
- /praefect sql-ping: OK/
- )
- end
-
- def health_check_failure_message?(msg)
- ['error when pinging healthcheck', 'failed checking node health'].include?(msg)
- end
-
def wait_for_dial_nodes_successful
Support::Waiter.repeat_until(max_attempts: 3, max_duration: 120, sleep_interval: 1) do
nodes_confirmed = {
@@ -314,14 +262,6 @@ module QA
dataloss_info
end
- def praefect_dataloss_info_for_project(project_id)
- dataloss_info = []
- Support::Retrier.retry_until(max_duration: 60) do
- dataloss_info = praefect_dataloss_information(project_id)
- dataloss_info.include?("#{Digest::SHA256.hexdigest(project_id.to_s)}.git")
- end
- end
-
def wait_for_project_synced_across_all_storages(project_id)
Support::Retrier.retry_until(max_duration: 60) do
praefect_dataloss_information(project_id).include?('All repositories are fully available on all assigned storages!')
@@ -345,9 +285,7 @@ module QA
end
def wait_for_health_check_all_nodes
- wait_for_gitaly_health_check(@primary_node)
- wait_for_gitaly_health_check(@secondary_node)
- wait_for_gitaly_health_check(@tertiary_node)
+ gitaly_nodes.each { |node| wait_for_gitaly_health_check(node) }
end
def wait_for_gitaly_health_check(node)
@@ -362,35 +300,11 @@ module QA
wait_until_node_is_marked_as_healthy_storage(node)
end
- def wait_for_primary_node_health_check
- wait_for_gitaly_health_check(@primary_node)
- end
-
- def wait_for_secondary_node_health_check
- wait_for_gitaly_health_check(@secondary_node)
- end
-
- def wait_for_tertiary_node_health_check
- wait_for_gitaly_health_check(@tertiary_node)
- end
-
def wait_for_health_check_failure(node)
QA::Runtime::Logger.info("Waiting for health check failure on #{node}")
wait_until_node_is_removed_from_healthy_storages(node)
end
- def wait_for_primary_node_health_check_failure
- wait_for_health_check_failure(@primary_node)
- end
-
- def wait_for_secondary_node_health_check_failure
- wait_for_health_check_failure(@secondary_node)
- end
-
- def wait_for_tertiary_node_health_check_failure
- wait_for_health_check_failure(@tertiary_node)
- end
-
def wait_until_node_is_removed_from_healthy_storages(node)
Support::Waiter.wait_until(max_duration: 120, sleep_interval: 1, raise_on_failure: true) do
result = []
@@ -457,10 +371,10 @@ module QA
result = []
shell sql_to_docker_exec_cmd(
<<~SQL
- select job from replication_queue
- where state = 'ready'
- and job ->> 'change' = 'update'
- and job ->> 'target_node_storage' = '#{@primary_node}';
+ select job from replication_queue
+ where state = 'ready'
+ and job ->> 'change' = 'update'
+ and job ->> 'target_node_storage' = '#{@primary_node}';
SQL
) do |line|
result << line
@@ -599,20 +513,6 @@ module QA
private
- def current_primary_node
- result = []
- shell sql_to_docker_exec_cmd("select node_name from shard_primaries where shard_name = '#{@virtual_storage}';") do |line|
- result << line
- end
- # The result looks like:
- # node_name
- # -----------
- # gitaly1
- # (1 row)
-
- result[2].strip
- end
-
def dataloss_command
"docker exec #{@praefect} bash -c '/opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml dataloss'"
end
@@ -655,13 +555,6 @@ module QA
end
end
- def with_praefect_log(**kwargs)
- wait_until_shell_command("docker exec #{@praefect} bash -c 'tail -n 1 /var/log/gitlab/praefect/current'", **kwargs) do |line|
- QA::Runtime::Logger.debug(line.chomp)
- yield JSON.parse(line)
- end
- end
-
def repo_type(repo_path)
return :snippet if repo_path.start_with?('@snippets')
return :design if repo_path.end_with?('.design.git')
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb
index 8bbef4ae429..9ffca8d54c9 100644
--- a/qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb
+++ b/qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb
@@ -25,8 +25,8 @@ module QA
it 'automatically fails over', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347830' do
# stop other nodes, so we can control which node the commit is sent to
- praefect_manager.stop_secondary_node
- praefect_manager.stop_tertiary_node
+ praefect_manager.stop_node(praefect_manager.secondary_node)
+ praefect_manager.stop_node(praefect_manager.tertiary_node)
Resource::Repository::ProjectPush.fabricate! do |push|
push.project = project
@@ -40,8 +40,8 @@ module QA
# Stop the primary node to trigger failover, and then wait
# for Gitaly to be ready for writes again
- praefect_manager.stop_primary_node
- praefect_manager.wait_for_primary_node_health_check_failure
+ praefect_manager.stop_node(praefect_manager.primary_node)
+ praefect_manager.wait_for_health_check_failure(praefect_manager.primary_node)
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
@@ -65,8 +65,8 @@ module QA
context 'when recovering from dataloss after failover' do
it 'automatically reconciles', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347831' do
# Start the old primary node again
- praefect_manager.start_primary_node
- praefect_manager.wait_for_primary_node_health_check
+ praefect_manager.start_node(praefect_manager.primary_node)
+ praefect_manager.wait_for_gitaly_health_check(praefect_manager.primary_node)
# Confirm automatic reconciliation
expect(praefect_manager.replicated?(project.id)).to be true
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb
index 1abc7b8a912..022f51205f0 100644
--- a/qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb
+++ b/qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb
@@ -27,8 +27,8 @@ module QA
# Stop the primary node to trigger failover, and then wait
# for Gitaly to be ready for writes again
- praefect_manager.stop_primary_node
- praefect_manager.wait_for_primary_node_health_check_failure
+ praefect_manager.stop_node(praefect_manager.primary_node)
+ praefect_manager.wait_for_health_check_failure(praefect_manager.primary_node)
# Push a commit to the new primary
Resource::Repository::ProjectPush.fabricate! do |push|
@@ -43,7 +43,7 @@ module QA
expect(praefect_manager).to be_replication_pending
# Start the old primary node again
- praefect_manager.start_primary_node
+ praefect_manager.start_node(praefect_manager.primary_node)
praefect_manager.wait_for_health_check_all_nodes
# Wait for automatic replication
@@ -51,8 +51,8 @@ module QA
# Force switch to the old primary node
# This ensures that the commit was replicated
- praefect_manager.stop_secondary_node
- praefect_manager.stop_tertiary_node
+ praefect_manager.stop_node(praefect_manager.secondary_node)
+ praefect_manager.stop_node(praefect_manager.tertiary_node)
# Confirm that both commits are available
expect(project.commits.map { |commit| commit[:message].chomp })
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb
index 397fdb909ac..60ce2a65fd1 100644
--- a/qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb
+++ b/qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb
@@ -36,20 +36,16 @@ module QA
context 'when a node is unhealthy' do
before do
- praefect_manager.stop_secondary_node
+ praefect_manager.stop_node(praefect_manager.secondary_node)
end
after do
# Leave the cluster in a suitable state for subsequent tests
- praefect_manager.start_secondary_node
+ praefect_manager.start_node(praefect_manager.secondary_node)
end
it 'does not read from the unhealthy node',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347834',
- quarantine: {
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/378174',
- type: :flaky
- } do
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347834' do
pre_read_data = praefect_manager.query_read_distribution
read_from_project(project, number_of_reads_per_loop * 10)
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb
index 6ba192a9dd6..cf387c14006 100644
--- a/qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb
+++ b/qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb
@@ -29,7 +29,7 @@ module QA
praefect_manager.wait_for_project_synced_across_all_storages(project.id)
# testing for gitaly2 'out of sync'
- praefect_manager.stop_secondary_node
+ praefect_manager.stop_node(praefect_manager.secondary_node)
number_of_changes = 3
1.upto(number_of_changes) do |i|
@@ -47,7 +47,7 @@ module QA
end
# testing for gitaly3 'in sync' but marked unhealthy
- praefect_manager.stop_tertiary_node
+ praefect_manager.stop_node(praefect_manager.tertiary_node)
project_data_loss = praefect_manager.praefect_dataloss_information(project.id)
aggregate_failures "validate dataloss identified" do
@@ -74,7 +74,7 @@ module QA
end
praefect_manager.wait_for_replication_to_node(project.id, praefect_manager.primary_node)
- praefect_manager.stop_primary_node
+ praefect_manager.stop_node(praefect_manager.primary_node)
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
commit.commit_message = 'accept-dataloss-2'
@@ -86,7 +86,7 @@ module QA
end
praefect_manager.wait_for_replication_to_node(project.id, praefect_manager.secondary_node)
- praefect_manager.stop_secondary_node
+ praefect_manager.stop_node(praefect_manager.secondary_node)
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
commit.commit_message = 'accept-dataloss-3'
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb
index 94bae38c5c8..f88372c0b59 100644
--- a/qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb
+++ b/qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb
@@ -35,7 +35,7 @@ module QA
# During normal operations we avoid create a replication event
# https://gitlab.com/groups/gitlab-org/-/epics/7741
- praefect_manager.stop_secondary_node
+ praefect_manager.stop_node(praefect_manager.secondary_node)
Git::Repository.perform do |repository|
repository.uri = project.repository_http_location.uri
repository.use_default_credentials
@@ -47,7 +47,7 @@ module QA
end
repository.push_all_branches
end
- praefect_manager.start_secondary_node
+ praefect_manager.start_node(praefect_manager.secondary_node)
Support::Retrier.retry_until(max_duration: 60) do
count = praefect_manager.replication_queue_lock_count
diff --git a/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb b/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb
index ab50e02c790..27f9bcc9675 100644
--- a/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb
@@ -4,7 +4,7 @@ module QA
# https://github.com/gitlab-qa-github/import-test <- project under test
#
RSpec.describe 'Manage', product_group: :import do
- describe 'GitHub import', :reliable do
+ describe 'GitHub import' do
include_context 'with github import'
context 'when imported via api' do
diff --git a/qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb b/qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb
index 5acf15dd2b4..64ab8d8fc43 100644
--- a/qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb
@@ -207,6 +207,8 @@ module QA
after do |example|
next unless defined?(@import_time)
+ # add additional import time metric
+ example.metadata[:custom_test_metrics] = { fields: { import_time: @import_time } }
# save data for comparison notification creation
save_json(
"data",
@@ -269,7 +271,7 @@ module QA
# fetch all objects right after import has started
fetch_github_objects
- import_status = lambda do
+ import_status = -> {
imported_project.project_import_status.yield_self do |status|
@stats = status.dig(:stats, :imported)
@@ -278,7 +280,7 @@ module QA
status[:import_status]
end
- end
+ }
logger.info("== Waiting for import to be finished ==")
expect(import_status).to eventually_eq('finished').within(max_duration: import_max_duration, sleep_interval: 30)
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb
index e17e12cdaf3..1f0c37df101 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb
@@ -1,70 +1,14 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Manage', :reliable, :requires_admin, product_group: :import do
- describe 'Gitlab migration' do
- let(:import_wait_duration) { { max_duration: 300, sleep_interval: 2 } }
- let(:admin_api_client) { Runtime::API::Client.as_admin }
- let(:user) do
- Resource::User.fabricate_via_api! do |usr|
- usr.api_client = admin_api_client
- usr.hard_delete_on_api_removal = true
- end
- end
-
- let(:api_client) { Runtime::API::Client.new(user: user) }
-
- let(:sandbox) do
- Resource::Sandbox.fabricate_via_api! do |group|
- group.api_client = admin_api_client
- end
- end
-
- let(:destination_group) do
- Resource::Group.fabricate_via_api! do |group|
- group.api_client = api_client
- group.sandbox = sandbox
- group.path = "destination-group-for-import-#{SecureRandom.hex(4)}"
- end
- end
-
- let(:source_group) do
- Resource::Group.fabricate_via_api! do |group|
- group.api_client = api_client
- group.sandbox = sandbox
- group.path = "source-group-for-import-#{SecureRandom.hex(4)}"
- group.avatar = File.new('qa/fixtures/designs/tanuki.jpg', 'r')
- end
- end
-
- let(:imported_group) do
- Resource::BulkImportGroup.fabricate_via_api! do |group|
- group.api_client = api_client
- group.sandbox = destination_group
- group.source_group = source_group
- end
- end
-
- let(:import_failures) do
- imported_group.import_details.sum([]) { |details| details[:failures] }
- end
-
- before do
- sandbox.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
- end
-
- after do |example|
- # Checking for failures in the test currently makes test very flaky due to catching unrelated failures
- # Log failures for easier debugging
- Runtime::Logger.warn("Import failures: #{import_failures}") if example.exception && !import_failures.empty?
- ensure
- user.remove_via_api!
- end
+ RSpec.describe "Manage", :reliable, product_group: :import do
+ include_context "with gitlab group migration"
+ describe "Gitlab migration" do
context 'with subgroups and labels' do
let(:subgroup) do
Resource::Group.fabricate_via_api! do |group|
- group.api_client = api_client
+ group.api_client = source_admin_api_client
group.sandbox = source_group
group.path = "subgroup-for-import-#{SecureRandom.hex(4)}"
end
@@ -80,12 +24,12 @@ module QA
before do
Resource::GroupLabel.fabricate_via_api! do |label|
- label.api_client = api_client
+ label.api_client = source_admin_api_client
label.group = source_group
label.title = "source-group-#{SecureRandom.hex(4)}"
end
Resource::GroupLabel.fabricate_via_api! do |label|
- label.api_client = api_client
+ label.api_client = source_admin_api_client
label.group = subgroup
label.title = "subgroup-#{SecureRandom.hex(4)}"
end
@@ -97,7 +41,7 @@ module QA
'successfully imports groups and labels',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347674'
) do
- expect { imported_group.import_status }.to eventually_eq('finished').within(import_wait_duration)
+ expect_group_import_finished_successfully
aggregate_failures do
expect(imported_group.reload!).to eq(source_group)
@@ -112,7 +56,7 @@ module QA
context 'with milestones and badges' do
let(:source_milestone) do
Resource::GroupMilestone.fabricate_via_api! do |milestone|
- milestone.api_client = api_client
+ milestone.api_client = source_admin_api_client
milestone.group = source_group
end
end
@@ -121,7 +65,7 @@ module QA
source_milestone
Resource::GroupBadge.fabricate_via_api! do |badge|
- badge.api_client = api_client
+ badge.api_client = source_admin_api_client
badge.group = source_group
badge.link_url = "http://example.com/badge"
badge.image_url = "http://shields.io/badge"
@@ -134,7 +78,7 @@ module QA
'successfully imports group milestones and badges',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347628'
) do
- expect { imported_group.import_status }.to eventually_eq('finished').within(import_wait_duration)
+ expect_group_import_finished_successfully
imported_milestone = imported_group.reload!.milestones.find { |ml| ml.title == source_milestone.title }
aggregate_failures do
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb
index 887eeda51e3..dd2e7f06995 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb
@@ -7,12 +7,18 @@ module QA
let!(:source_issue) do
Resource::Issue.fabricate_via_api! do |issue|
- issue.api_client = api_client
+ issue.api_client = source_admin_api_client
issue.project = source_project
issue.labels = %w[label_one label_two]
end
end
+ let(:source_issue_comments) do
+ source_issue.comments.map do |note|
+ { **note.except(:id, :noteable_id), author: note[:author].except(:web_url) }
+ end
+ end
+
let(:imported_issues) { imported_projects.first.issues }
let(:imported_issue) do
@@ -24,6 +30,12 @@ module QA
end
end
+ let(:imported_issue_comments) do
+ imported_issue.comments.map do |note|
+ { **note.except(:id, :noteable_id), author: note[:author].except(:web_url) }
+ end
+ end
+
context 'with project issues' do
let!(:source_comment) { source_issue.add_comment(body: 'This is a test comment!') }
@@ -33,19 +45,18 @@ module QA
'successfully imports issue',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347608'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
expect(imported_issues.count).to eq(1)
-
- aggregate_failures do
- expect(imported_issue).to eq(source_issue.reload!)
-
- expect(imported_comments.count).to eq(1)
- expect(imported_comments.first&.fetch(:body)).to include(source_comment[:body])
- end
+ expect(imported_issue).to eq(source_issue.reload!)
+ expect(imported_issue_comments).to match_array(source_issue_comments)
end
end
- context "with designs" do
+ # we can't fabricate things in source instance via UI
+ context "with designs", quarantine: {
+ type: :broken,
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/366592'
+ } do
let!(:source_design) do
Flow::Login.sign_in(as: user)
@@ -66,7 +77,7 @@ module QA
'successfully imports design',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/366449'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
expect(imported_issues.count).to eq(1)
expect(imported_design.full_path).to eq(source_design.full_path)
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb
index 116a00f8385..5fc170435e3 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb
@@ -4,113 +4,76 @@
# rubocop:disable Rails/Pluck, Layout/LineLength, RSpec/MultipleMemoizedHelpers
module QA
- RSpec.describe "Manage", requires_admin: 'creates users', only: { job: 'large-gitlab-import' } do
- describe "Gitlab migration", product_group: :import do
- let(:logger) { Runtime::Logger.logger }
- let(:differ) { RSpec::Support::Differ.new(color: true) }
- let(:gitlab_group) { ENV['QA_LARGE_IMPORT_GROUP'] || 'gitlab-migration' }
- let(:gitlab_project) { ENV['QA_LARGE_IMPORT_REPO'] || 'dri' }
- let(:gitlab_source_address) { ENV['QA_LARGE_IMPORT_SOURCE_URL'] || 'https://staging.gitlab.com' }
-
- let(:import_wait_duration) do
- {
- max_duration: (ENV['QA_LARGE_IMPORT_DURATION'] || 3600).to_i,
- sleep_interval: 30
- }
- end
-
- let(:admin_api_client) { Runtime::API::Client.as_admin }
-
- # explicitly create PAT via api to not create it via UI in environments where admin token env var is not present
- let(:target_api_client) do
- Runtime::API::Client.new(
- user: user,
- personal_access_token: Resource::PersonalAccessToken.fabricate_via_api! do |pat|
- pat.api_client = admin_api_client
- end.token
- )
- end
-
- let(:user) do
- Resource::User.fabricate_via_api! do |usr|
- usr.api_client = admin_api_client
- end
- end
-
- let(:source_api_client) do
+ RSpec.describe "Manage", :skip_live_env, only: { job: "large-gitlab-import" } do
+ describe "Gitlab migration", orchestrated: false, product_group: :import do
+ include_context "with gitlab group migration"
+
+ let!(:logger) { Runtime::Logger.logger }
+ let!(:differ) { RSpec::Support::Differ.new(color: true) }
+ let!(:source_gitlab_address) { ENV["QA_LARGE_IMPORT_SOURCE_URL"] || "https://gitlab.com" }
+ let!(:gitlab_source_group) { ENV["QA_LARGE_IMPORT_GROUP"] || "gitlab-migration-large-import-test" }
+ let!(:gitlab_source_project) { ENV["QA_LARGE_IMPORT_REPO"] || "migration-test-project" }
+ let!(:import_wait_duration) { { max_duration: (ENV["QA_LARGE_IMPORT_DURATION"] || 3600).to_i, sleep_interval: 30 } }
+
+ let!(:source_admin_user) { "no-op" }
+ let!(:source_admin_api_client) do
Runtime::API::Client.new(
- gitlab_source_address,
- personal_access_token: ENV["QA_LARGE_IMPORT_GL_TOKEN"],
+ source_gitlab_address,
+ personal_access_token: ENV["QA_LARGE_IMPORT_GL_TOKEN"] || raise("missing QA_LARGE_IMPORT_GL_TOKEN variable"),
is_new_session: false
)
end
- let(:sandbox) do
+ # alias api client because for large import test it's not an actual admin user
+ let!(:source_api_client) { source_admin_api_client }
+
+ let!(:source_group) do
Resource::Sandbox.fabricate_via_api! do |group|
- group.api_client = admin_api_client
+ group.api_client = source_api_client
+ group.path = gitlab_source_group
end
end
- let(:destination_group) do
- Resource::Group.fabricate_via_api! do |group|
+ # generate unique target group because source group has a static name
+ let!(:target_sandbox) do
+ Resource::Sandbox.fabricate_via_api! do |group|
group.api_client = admin_api_client
- group.sandbox = sandbox
- group.path = "imported-group-destination-#{SecureRandom.hex(4)}"
+ group.path = "qa-sandbox-#{SecureRandom.hex(4)}"
end
end
- # Source group and it's objects
+ # Source objects
#
- let(:source_group) do
- Resource::Sandbox.fabricate_via_api! do |group|
- group.api_client = source_api_client
- group.path = gitlab_group
- end
- end
-
- let(:source_project) { source_group.projects.find { |project| project.name.include?(gitlab_project) }.reload! }
+ let(:source_project) { source_group.projects.find { |project| project.name == gitlab_source_project }.reload! }
let(:source_branches) { source_project.repository_branches(auto_paginate: true).map { |b| b[:name] } }
let(:source_commits) { source_project.commits(auto_paginate: true).map { |c| c[:id] } }
let(:source_labels) { source_project.labels(auto_paginate: true).map { |l| l.except(:id) } }
let(:source_milestones) { source_project.milestones(auto_paginate: true).map { |ms| ms.except(:id, :web_url, :project_id) } }
let(:source_pipelines) { source_project.pipelines(auto_paginate: true).map { |pp| pp.except(:id, :web_url, :project_id) } }
- let(:source_mrs) { fetch_mrs(source_project, source_api_client) }
- let(:source_issues) { fetch_issues(source_project, source_api_client) }
+ let(:source_mrs) { fetch_mrs(source_project, source_api_client, transform_urls: true) }
+ let(:source_issues) { fetch_issues(source_project, source_api_client, transform_urls: true) }
- # Imported group and it's objects
+ # Imported objects
#
- let(:imported_group) do
- Resource::BulkImportGroup.fabricate_via_api! do |group|
- group.import_access_token = source_api_client.personal_access_token # token for importing on source instance
- group.api_client = target_api_client # token used by qa framework to access resources in destination instance
- group.gitlab_address = gitlab_source_address
- group.source_group = source_group
- group.sandbox = destination_group
- end
- end
-
- let(:imported_project) { imported_group.projects.find { |project| project.name.include?(gitlab_project) }.reload! }
+ let(:imported_project) { imported_group.projects.find { |project| project.name == gitlab_source_project }.reload! }
let(:branches) { imported_project.repository_branches(auto_paginate: true).map { |b| b[:name] } }
let(:commits) { imported_project.commits(auto_paginate: true).map { |c| c[:id] } }
let(:labels) { imported_project.labels(auto_paginate: true).map { |l| l.except(:id) } }
let(:milestones) { imported_project.milestones(auto_paginate: true).map { |ms| ms.except(:id, :web_url, :project_id) } }
- let(:pipelines) { imported_project.pipelines.map { |pp| pp.except(:id, :web_url, :project_id) } }
- let(:mrs) { fetch_mrs(imported_project, target_api_client) }
- let(:issues) { fetch_issues(imported_project, target_api_client) }
-
- let(:import_failures) { imported_group.import_details.sum([]) { |details| details[:failures] } }
+ let(:pipelines) { imported_project.pipelines(auto_paginate: true).map { |pp| pp.except(:id, :web_url, :project_id) } }
+ let(:mrs) { fetch_mrs(imported_project, api_client) }
+ let(:issues) { fetch_issues(imported_project, api_client) }
before do
- destination_group.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
+ Runtime::Feature.enable(:bulk_import_projects) unless Runtime::Feature.enabled?(:bulk_import_projects)
end
# rubocop:disable RSpec/InstanceVariable
after do |example|
- # Log failures for easier debugging
- Runtime::Logger.error("Import failures: #{import_failures}") if example.exception && !import_failures.empty?
-
next unless defined?(@import_time)
+ # add additional import time metric
+ example.metadata[:custom_test_metrics] = { fields: { import_time: @import_time } }
# save data for comparison notification creation
save_json(
"data",
@@ -121,7 +84,7 @@ module QA
source: {
name: "GitLab Source",
project_name: source_project.path_with_namespace,
- address: gitlab_source_address,
+ address: source_gitlab_address,
data: {
branches: source_branches.length,
commits: source_commits.length,
@@ -163,17 +126,14 @@ module QA
start = Time.now
# trigger import and log imported group path
- logger.info("== Importing group '#{gitlab_group}' in to '#{imported_group.full_path}' ==")
+ logger.info("== Importing group '#{gitlab_source_group}' in to '#{imported_group.full_path}' ==")
# fetch all objects right after import has started
fetch_source_gitlab_objects
# wait for import to finish and save import time
logger.info("== Waiting for import to be finished ==")
- expect { imported_group.import_status }.not_to eventually_eq("started").within(import_wait_duration)
- # finished status actually means success, don't wait for finished status explicitly
- # because test would wait full duration if returned status is "failed"
- expect(imported_group.import_status).to eq("finished")
+ expect_group_import_finished_successfully
@import_time = Time.now - start
@@ -267,8 +227,8 @@ module QA
comment_diff = verify_comments(type, actual, expected)
{
- "missing_#{type}s": (expected.keys - actual.keys).map { |it| actual[it]&.slice(:title, :url) }.compact,
- "extra_#{type}s": (actual.keys - expected.keys).map { |it| expected[it]&.slice(:title, :url) }.compact,
+ "missing_#{type}s": (expected.keys - actual.keys).map { |it| expected[it]&.slice(:title, :url) }.compact,
+ "extra_#{type}s": (actual.keys - expected.keys).map { |it| actual[it]&.slice(:title, :url) }.compact,
"#{type}_comments": comment_diff
}
end
@@ -336,11 +296,12 @@ module QA
#
# @param [QA::Resource::Project]
# @param [Runtime::API::Client] client
+ # @param [Boolean] transform_urls
# @return [Hash]
- def fetch_mrs(project, client)
+ def fetch_mrs(project, client, transform_urls: false)
imported_mrs = project.merge_requests(auto_paginate: true, attempts: 2)
- Parallel.map(imported_mrs, in_threads: 4) do |mr|
+ Parallel.map(imported_mrs, in_threads: 6) do |mr|
resource = Resource::MergeRequest.init do |resource|
resource.project = project
resource.iid = mr[:iid]
@@ -350,11 +311,11 @@ module QA
[mr[:iid], {
url: mr[:web_url],
title: mr[:title],
- body: sanitize_description(mr[:description]) || '',
+ body: sanitize_description(mr[:description], transform_urls) || '',
state: mr[:state],
comments: resource
.comments(auto_paginate: true, attempts: 2)
- .map { |c| sanitize_comment(c[:body]) }
+ .map { |c| sanitize_comment(c[:body], transform_urls) }
}]
end.to_h
end
@@ -363,11 +324,12 @@ module QA
#
# @param [QA::Resource::Project]
# @param [Runtime::API::Client] client
+ # @param [Boolean] transform_urls
# @return [Hash]
- def fetch_issues(project, client)
+ def fetch_issues(project, client, transform_urls: false)
imported_issues = project.issues(auto_paginate: true, attempts: 2)
- Parallel.map(imported_issues, in_threads: 4) do |issue|
+ Parallel.map(imported_issues, in_threads: 6) do |issue|
resource = Resource::Issue.init do |issue_resource|
issue_resource.project = project
issue_resource.iid = issue[:iid]
@@ -378,42 +340,66 @@ module QA
url: issue[:web_url],
title: issue[:title],
state: issue[:state],
- body: sanitize_description(issue[:description]) || '',
+ body: sanitize_description(issue[:description], transform_urls) || '',
comments: resource
.comments(auto_paginate: true, attempts: 2)
- .map { |c| sanitize_comment(c[:body]) }
+ .map { |c| sanitize_comment(c[:body], transform_urls) }
}]
end.to_h
end
- # Importer user mention pattern
+ # Remove added postfixes and transform urls
#
- # @return [Regex]
- def created_by_pattern
- @created_by_pattern ||= /\n\n \*By #{importer_username_pattern} on \S+ \(imported from GitLab\)\*/
+ # Source urls need to be replaced with target urls for comparison to work
+ #
+ # @param [String] body
+ # @param [Boolean] transform_urls
+ # @return [String]
+ def sanitize_comment(body, transform_urls)
+ comment = body&.gsub(created_by_pattern, "")
+ return comment unless transform_urls
+
+ comment&.gsub(source_project_url, imported_project_url)
end
- # Username of importer user for removal from comments and descriptions
+ # Remove added postfixes and transform urls
+ #
+ # Source urls need to be replaced with target urls for comparison to work
#
+ # @param [String] body
+ # @param [Boolean] transform_urls
# @return [String]
- def importer_username_pattern
- @importer_username_pattern ||= ENV['QA_LARGE_IMPORT_USER_PATTERN'] || "(gitlab-migration|GitLab QA Bot)"
+ def sanitize_description(body, transform_urls)
+ description = body&.gsub(created_by_pattern, "")
+ return description unless transform_urls
+
+ description&.gsub(source_project_url, imported_project_url)
end
- # Remove added prefixes from comments
+ # Following objects are memoized via instance variables due to Parallel having some type of issue calling
+ # helpers defined via rspec let method
+
+ # Importer user mention pattern
+ #
+ # @return [Regex]
+ def created_by_pattern
+ @created_by_pattern ||= /\n\n \*By .+ on \S+ \(imported from GitLab\)\*/
+ end
+
+ # Source project url
#
- # @param [String] body
# @return [String]
- def sanitize_comment(body)
- body&.gsub(created_by_pattern, "")
+ def source_project_url
+ @source_group_url ||= "#{source_gitlab_address}/#{source_project.full_path}"
end
- # Remove created by prefix from descripion
+ # Imported project url
+ #
+ # This needs to be constructed manually because it is called before project import finishes
#
- # @param [String] body
# @return [String]
- def sanitize_description(body)
- body&.gsub(created_by_pattern, "")
+ def imported_project_url
+ @imported_group_url ||= "#{Runtime::Scenario.gitlab_address}/#{imported_group.full_path}/#{source_project.path}"
end
# Save json as file
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_members_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_members_spec.rb
index 07e54ead9c8..7fe11c3bafe 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_members_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_members_spec.rb
@@ -5,39 +5,37 @@ module QA
describe 'Gitlab migration', product_group: :import do
include_context 'with gitlab project migration'
- let(:member) do
+ let!(:source_member) do
+ Resource::User.fabricate_via_api! do |usr|
+ usr.api_client = source_admin_api_client
+ end.tap(&:set_public_email)
+ end
+
+ let!(:target_member) do
Resource::User.fabricate_via_api! do |usr|
usr.api_client = admin_api_client
- usr.hard_delete_on_api_removal = true
- end
+ usr.email = source_member.email
+ end.tap(&:set_public_email)
end
let(:imported_group_member) do
- imported_group.reload!.list_members.find { |usr| usr['username'] == member.username }
+ imported_group.reload!.list_members.find { |usr| usr['username'] == target_member.username }
end
let(:imported_project_member) do
- imported_project.reload!.list_members.find { |usr| usr['username'] == member.username }
- end
-
- before do
- member.set_public_email
- end
-
- after do
- member.remove_via_api!
+ imported_project.reload!.list_members.find { |usr| usr['username'] == target_member.username }
end
context 'with group member' do
before do
- source_group.add_member(member, Resource::Members::AccessLevel::DEVELOPER)
+ source_group.add_member(source_member, Resource::Members::AccessLevel::DEVELOPER)
end
it(
'member retains indirect membership in imported project',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/354416'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
aggregate_failures do
expect(imported_project_member).to be_nil
@@ -50,14 +48,14 @@ module QA
context 'with project member' do
before do
- source_project.add_member(member, Resource::Members::AccessLevel::DEVELOPER)
+ source_project.add_member(source_member, Resource::Members::AccessLevel::DEVELOPER)
end
it(
'member retains direct membership in imported project',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/354417'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
aggregate_failures do
expect(imported_group_member).to be_nil
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb
index f44786939dc..127db36052f 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb
@@ -7,57 +7,64 @@ module QA
let!(:source_project_with_readme) { true }
- # We create additional user so that object being migrated is not owned by the user doing migration
- let!(:other_user) do
- Resource::User
- .fabricate_via_api! { |usr| usr.api_client = admin_api_client }
- .tap do |usr|
- usr.set_public_email
- source_project.add_member(usr, Resource::Members::AccessLevel::MAINTAINER)
- end
+ let!(:source_mr_reviewer) do
+ reviewer = Resource::User.fabricate_via_api! do |usr|
+ usr.api_client = source_admin_api_client
+ usr.username = "source-reviewer-#{SecureRandom.hex(6)}"
+ end
+ reviewer.tap do |usr|
+ usr.set_public_email
+ source_project.add_member(usr, Resource::Members::AccessLevel::MAINTAINER)
+ end
end
let!(:source_mr) do
Resource::MergeRequest.fabricate_via_api! do |mr|
mr.project = source_project
- mr.api_client = Runtime::API::Client.new(user: other_user)
- mr.reviewer_ids = [other_user.id]
+ mr.api_client = source_admin_api_client
+ mr.reviewer_ids = [source_mr_reviewer.id]
end
end
- let!(:source_comment) { source_mr.add_comment(body: 'This is a test comment!') }
+ let!(:mr_reviewer) do
+ Resource::User.fabricate_via_api! do |usr|
+ usr.api_client = admin_api_client
+ usr.email = source_mr_reviewer.email
+ end.tap(&:set_public_email)
+ end
- let(:imported_mrs) { imported_project.merge_requests }
- let(:imported_mr_comments) { imported_mr.comments.map { |note| note.except(:id, :noteable_id) } }
- let(:source_mr_comments) { source_mr.comments.map { |note| note.except(:id, :noteable_id) } }
+ let!(:source_mr_reviewers) { [source_mr_reviewer.email] }
+ let!(:source_mr_approvers) { [source_admin_user.email] }
+ let(:source_mr_comments) do
+ source_mr.comments.map do |note|
+ { **note.except(:id, :noteable_id), author: note[:author].except(:web_url) }
+ end
+ end
+ let(:imported_mrs) { imported_project.merge_requests }
let(:imported_mr) do
Resource::MergeRequest.init do |mr|
mr.project = imported_project
- mr.iid = imported_mrs.first[:iid]
+ mr.iid = imported_project.merge_requests.first[:iid]
mr.api_client = api_client
end
end
- let(:imported_mr_reviewers) { imported_mr.reviewers.map { |r| r.slice(:name, :username) } }
- let(:source_mr_reviewers) { [{ name: other_user.name, username: other_user.username }] }
+ let(:imported_mr_comments) do
+ imported_mr.comments.map do |note|
+ { **note.except(:id, :noteable_id), author: note[:author].except(:web_url) }
+ end
+ end
+ let(:imported_mr_reviewers) { imported_mr.reviewers.map { |reviewer| reviewer[:username] } }
let(:imported_mr_approvers) do
- imported_mr.approval_configuration[:approved_by].map do |usr|
- { username: usr.dig(:user, :username), name: usr.dig(:user, :name) }
- end
+ imported_mr.approval_configuration[:approved_by].map { |usr| usr.dig(:user, :username) }
end
before do
- source_project.update_approval_configuration(
- merge_requests_author_approval: true,
- approvals_before_merge: 1
- )
+ source_project.update_approval_configuration(merge_requests_author_approval: true, approvals_before_merge: 1)
source_mr.approve
- end
-
- after do
- other_user.remove_via_api!
+ source_mr.add_comment(body: 'This is a test comment!')
end
context 'with merge request' do
@@ -65,15 +72,15 @@ module QA
'successfully imports merge request',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348478'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
expect(imported_mrs.count).to eq(1)
aggregate_failures do
expect(imported_mr).to eq(source_mr.reload!)
expect(imported_mr_comments).to match_array(source_mr_comments)
- expect(imported_mr_reviewers).to eq(source_mr_reviewers)
- expect(imported_mr_approvers).to eq([{ username: other_user.username, name: other_user.name }])
+ expect(imported_mr_reviewers).to eq([mr_reviewer.username])
+ expect(imported_mr_approvers).to eq([source_admin_user.username])
end
end
end
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb
index 7b79e6967c7..8d631808d17 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb
@@ -22,7 +22,7 @@ module QA
before do
Resource::Repository::Commit.fabricate_via_api! do |commit|
- commit.api_client = api_client
+ commit.api_client = source_admin_api_client
commit.project = source_project
commit.commit_message = 'Add .gitlab-ci.yml'
commit.add_files(
@@ -47,7 +47,7 @@ module QA
'successfully imports ci pipeline',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/354650'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
expect(imported_pipelines).to eq(source_pipelines)
end
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb
index 2b7818d1ed2..83691cdf143 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb
@@ -5,14 +5,54 @@ module QA
describe 'Gitlab migration', product_group: :import do
include_context 'with gitlab project migration'
+ # this spec is used as a sanity test for gitlab migration because it can run outside of orchestrated setup
+ context 'with import within same instance', orchestrated: false, import: false, quarantine: {
+ type: :test_environment,
+ issue: "https://gitlab.com/gitlab-org/gitlab/-/issues/383605",
+ only: { job: "review-qa" }
+ } do
+ let!(:source_project_with_readme) { true }
+ let!(:source_gitlab_address) { Runtime::Scenario.gitlab_address }
+ let!(:source_admin_api_client) { admin_api_client }
+
+ let!(:source_sandbox) do
+ Resource::Sandbox.fabricate_via_api! do |group|
+ group.api_client = admin_api_client
+ end
+ end
+
+ let!(:target_sandbox) { source_sandbox }
+
+ let!(:source_group) do
+ Resource::Group.fabricate_via_api! do |group|
+ group.api_client = admin_api_client
+ group.sandbox = source_sandbox
+ group.path = "source-group-for-import-#{SecureRandom.hex(4)}"
+ group.avatar = File.new('qa/fixtures/designs/tanuki.jpg', 'r')
+ end
+ end
+
+ let(:destination_group_path) { "target-group-for-import-#{SecureRandom.hex(4)}" }
+ let(:cleanup!) { user.remove_via_api! }
+
+ it(
+ 'successfully imports project',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/383351'
+ ) do
+ expect_project_import_finished_successfully
+
+ expect(imported_project).to eq(source_project)
+ end
+ end
+
context 'with uninitialized project' do
it(
'successfully imports project',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347610'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
- expect(imported_projects.first).to eq(source_project)
+ expect(imported_project).to eq(source_project)
end
end
@@ -59,7 +99,7 @@ module QA
'successfully imports repository',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347570'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
aggregate_failures do
expect(imported_commits).to match_array(source_commits)
@@ -78,9 +118,9 @@ module QA
'successfully imports project wiki',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347567'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
- expect(imported_projects.first.wikis).to eq(source_project.wikis)
+ expect(imported_project.wikis).to eq(source_project.wikis)
end
end
end
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb
index 36036a2321e..b3510cef3e9 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb
@@ -6,13 +6,13 @@ module QA
include_context 'with gitlab project migration'
context 'with release' do
- let(:tag) { 'v0.0.1' }
- let(:source_project_with_readme) { true }
+ let!(:tag) { 'v0.0.1' }
+ let!(:source_project_with_readme) { true }
- let(:milestone) do
+ let!(:milestone) do
Resource::ProjectMilestone.fabricate_via_api! do |resource|
resource.project = source_project
- resource.api_client = api_client
+ resource.api_client = source_admin_api_client
end
end
@@ -60,7 +60,7 @@ module QA
'successfully imports project release',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/360243'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
expect(imported_releases.size).to eq(1), "Expected to have 1 migrated release"
expect(imported_release).to eq(source_release)
diff --git a/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb b/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb
index 16d4fd35b69..3df6e988bfa 100644
--- a/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb
@@ -70,13 +70,7 @@ module QA
it(
'is allowed to commit to sub-group project via the API',
- :reliable,
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/363349',
- quarantine: {
- only: { subdomain: %i[staging staging-ref] },
- type: :investigating,
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/370282'
- }
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/363349'
) do
expect do
Resource::Repository::Commit.fabricate_via_api! do |commit|
@@ -87,6 +81,9 @@ module QA
commit.commit_message = 'Add new file'
commit.add_files([{ file_path: 'test.txt', content: 'new file' }])
end
+ rescue StandardError => e
+ QA::Runtime::Logger.error("Full failure message: #{e.message}")
+ raise
end.not_to raise_error
end
diff --git a/qa/qa/specs/features/api/4_verify/file_variable_spec.rb b/qa/qa/specs/features/api/4_verify/file_variable_spec.rb
index 4ae97f589cf..6d375341c1b 100644
--- a/qa/qa/specs/features/api/4_verify/file_variable_spec.rb
+++ b/qa/qa/specs/features/api/4_verify/file_variable_spec.rb
@@ -1,7 +1,10 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do
+ RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring, quarantine: {
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/383324',
+ type: :stale
+ } do
describe 'Pipeline with project file variables' do
let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" }
diff --git a/qa/qa/specs/features/api/4_verify/remove_runner_spec.rb b/qa/qa/specs/features/api/4_verify/remove_runner_spec.rb
index eb1b085c35c..7aaaa7137ed 100644
--- a/qa/qa/specs/features/api/4_verify/remove_runner_spec.rb
+++ b/qa/qa/specs/features/api/4_verify/remove_runner_spec.rb
@@ -17,17 +17,16 @@ module QA
# Removing a runner via the UI is covered by `spec/features/runners_spec.rb``
it 'removes the runner', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/354828' do
- runners = nil
- expect { (runners = runner.list_of_runners(tag_list: runner_tags)).size }
- .to eventually_eq(1).within(max_duration: 10, sleep_interval: 1)
- expect(runners.first[:description]).to eq(executor)
+ runners_list = runner.runners_list
+ expect(runners_list.size).to eq(1)
+ expect(runners_list.first[:description]).to eq(executor)
- request = Runtime::API::Request.new(api_client, "runners/#{runners.first[:id]}")
+ request = Runtime::API::Request.new(api_client, "runners/#{runner.id}")
response = delete(request.url)
expect(response.code).to eq(Support::API::HTTP_STATUS_NO_CONTENT)
expect(response.body).to be_empty
- expect(runner.list_of_runners(tag_list: runner_tags)).to be_empty
+ expect(runner).to be_not_found_by_tags
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/group/gitlab_migration_group_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/group/gitlab_migration_group_spec.rb
deleted file mode 100644
index c690202f091..00000000000
--- a/qa/qa/specs/features/browser_ui/1_manage/group/gitlab_migration_group_spec.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- describe 'Manage', :requires_admin, :reliable, product_group: :import do
- describe 'Gitlab migration' do
- let!(:admin_api_client) { Runtime::API::Client.as_admin }
- let!(:user) do
- Resource::User.fabricate_via_api! do |usr|
- usr.api_client = admin_api_client
- usr.hard_delete_on_api_removal = true
- end
- end
-
- let!(:api_client) { Runtime::API::Client.new(user: user) }
- let!(:personal_access_token) { api_client.personal_access_token }
-
- let(:sandbox) do
- Resource::Sandbox.fabricate_via_api! do |group|
- group.api_client = admin_api_client
- end
- end
-
- let(:source_group) do
- Resource::Sandbox.fabricate! do |group|
- group.api_client = api_client
- group.path = "source-group-for-import-#{SecureRandom.hex(4)}"
- end
- end
-
- let(:imported_group) do
- Resource::BulkImportGroup.init do |group|
- group.api_client = api_client
- group.sandbox = sandbox
- group.source_group = source_group
- end
- end
-
- before do
- sandbox.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
-
- Flow::Login.sign_in(as: user)
-
- source_group
-
- Page::Main::Menu.perform(&:go_to_create_group)
- Page::Group::New.perform do |group|
- group.switch_to_import_tab
- group.connect_gitlab_instance(Runtime::Scenario.gitlab_address, personal_access_token)
- end
- end
-
- after do
- user.remove_via_api!
- end
-
- it(
- 'imports group from UI',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347862',
- issue_1: 'https://gitlab.com/gitlab-org/gitlab/-/issues/331252',
- issue_2: 'https://gitlab.com/gitlab-org/gitlab/-/issues/333678',
- issue_3: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332351',
- except: { job: 'instance-image-slow-network' }
- ) do
- Page::Group::BulkImport.perform do |import_page|
- import_page.import_group(imported_group.path, imported_group.sandbox.path)
-
- expect(import_page).to have_imported_group(imported_group.path, wait: 300)
-
- imported_group.reload!.visit!
- Page::Group::Show.perform do |group|
- expect(group).to have_content(imported_group.path)
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_group_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_group_spec.rb
new file mode 100644
index 00000000000..8fe4dc192bd
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_group_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Manage' do
+ describe 'Subgroup transfer', product_group: :workspace do
+ let(:source_group) do
+ Resource::Group.fabricate_via_api! do |group|
+ group.path = "source-group-for-transfer_#{SecureRandom.hex(8)}"
+ end
+ end
+
+ let!(:target_group) do
+ Resource::Group.fabricate_via_api! do |group|
+ group.path = "target-group-for-transfer_#{SecureRandom.hex(8)}"
+ end
+ end
+
+ let(:sub_group_for_transfer) do
+ Resource::Group.fabricate_via_api! do |group|
+ group.path = "subgroup-for-transfer_#{SecureRandom.hex(8)}"
+ group.sandbox = source_group
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+ sub_group_for_transfer.visit!
+ end
+
+ it 'transfers a subgroup to another group',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347692' do
+ Page::Group::Menu.perform(&:click_group_general_settings_item)
+ Page::Group::Settings::General.perform do |general|
+ general.transfer_group(sub_group_for_transfer, target_group)
+
+ sub_group_for_transfer.sandbox = target_group
+ sub_group_for_transfer.reload!
+ end
+
+ expect(page).to have_text("Group '#{sub_group_for_transfer.path}' was successfully transferred.")
+ expect(page.driver.current_url).to include(sub_group_for_transfer.full_path)
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb
new file mode 100644
index 00000000000..43a8af93e27
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Manage', product_group: :import do
+ describe 'GitHub import' do
+ include_context 'with github import'
+
+ context 'when imported via UI' do
+ let(:imported_project) do
+ Resource::ProjectImportedFromGithub.init do |project|
+ project.import = true
+ project.group = group
+ project.github_personal_access_token = Runtime::Env.github_access_token
+ project.github_repository_path = github_repo
+ project.api_client = api_client
+ end
+ end
+
+ let(:imported_issue) do
+ Resource::Issue.init do |resource|
+ resource.project = imported_project
+ resource.iid = imported_project.issues.first[:iid]
+ resource.api_client = api_client
+ end.reload!
+ end
+
+ let(:imported_issue_events) do
+ imported_issue.label_events.map { |e| { name: "#{e[:action]}_label", label: e.dig(:label, :name) } }
+ end
+
+ before do
+ Flow::Login.sign_in(as: user)
+ Page::Main::Menu.perform(&:go_to_create_project)
+ Page::Project::New.perform do |project_page|
+ project_page.click_import_project
+ project_page.click_github_link
+ end
+ end
+
+ it 'imports a project', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347877' do
+ Page::Project::Import::Github.perform do |import_page|
+ import_page.add_personal_access_token(Runtime::Env.github_access_token)
+
+ import_page.select_advanced_option(:single_endpoint_issue_events_import)
+ import_page.select_advanced_option(:single_endpoint_notes_import)
+ import_page.select_advanced_option(:attachments_import)
+
+ import_page.import!(github_repo, group.full_path, imported_project.name)
+
+ aggregate_failures do
+ expect(import_page).to have_imported_project(github_repo, wait: 240)
+ # validate button is present instead of navigating to avoid dealing with multiple tabs
+ # which makes the test more complicated
+ expect(import_page).to have_go_to_project_button(github_repo)
+ end
+ end
+
+ imported_project.reload!.visit!
+ Page::Project::Show.perform do |project|
+ aggregate_failures do
+ expect(project).to have_content(imported_project.name)
+ expect(project).to have_content('Project for github import test')
+ end
+ end
+
+ # Validate :single_endpoint_issue_events_import option was triggered correctly and imported the events
+ expect(imported_issue_events).to match_array(
+ [
+ { name: "add_label", label: "question" },
+ { name: "add_label", label: "good first issue" },
+ { name: "add_label", label: "help wanted" }
+ ]
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/migration/gitlab_migration_group_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/migration/gitlab_migration_group_spec.rb
new file mode 100644
index 00000000000..4bcd2c44617
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/migration/gitlab_migration_group_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module QA
+ describe 'Manage', :reliable, product_group: :import do
+ describe 'Gitlab migration' do
+ include_context "with gitlab group migration"
+
+ let!(:imported_group) do
+ Resource::BulkImportGroup.init do |group|
+ group.api_client = api_client
+ group.sandbox = target_sandbox
+ group.source_group = source_group
+ end
+ end
+
+ before do
+ Flow::Login.sign_in(as: user)
+
+ Page::Main::Menu.perform(&:go_to_create_group)
+ Page::Group::New.perform do |group|
+ group.switch_to_import_tab
+ group.connect_gitlab_instance(source_gitlab_address, source_admin_api_client.personal_access_token)
+ end
+ end
+
+ it 'imports group from UI', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347862' do
+ Page::Group::BulkImport.perform do |import_page|
+ import_page.import_group(source_group.path, target_sandbox.path)
+
+ expect(import_page).to have_imported_group(imported_group.path, wait: 300)
+
+ imported_group.reload!.visit!
+ Page::Group::Show.perform do |group|
+ expect(group).to have_content(imported_group.path)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
deleted file mode 100644
index 15563e3aa2a..00000000000
--- a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- # Spec uses real github.com, which means outage of github can actually block deployment
- # Keep spec in reliable bucket but don't run in blocking pipelines
- RSpec.describe 'Manage', :github, :reliable, :skip_live_env, :requires_admin, product_group: :import do
- describe 'GitHub import' do
- context 'when imported via UI' do
- let(:github_repo) { 'gitlab-qa-github/import-test' }
- let(:api_client) { Runtime::API::Client.as_admin }
- let(:group) { Resource::Group.fabricate_via_api! { |resource| resource.api_client = api_client } }
- let(:user) do
- Resource::User.fabricate_via_api! do |resource|
- resource.api_client = api_client
- resource.hard_delete_on_api_removal = true
- end
- end
-
- let(:imported_project) do
- Resource::ProjectImportedFromGithub.init do |project|
- project.import = true
- project.group = group
- project.github_personal_access_token = Runtime::Env.github_access_token
- project.github_repository_path = github_repo
- project.api_client = api_client
- end
- end
-
- let(:imported_issue) do
- Resource::Issue.init do |resource|
- resource.project = imported_project
- resource.iid = imported_project.issues.first[:iid]
- resource.api_client = api_client
- end.reload!
- end
-
- let(:imported_issue_events) do
- imported_issue.label_events.map { |e| { name: "#{e[:action]}_label", label: e.dig(:label, :name) } }
- end
-
- before do
- group.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
-
- Flow::Login.sign_in(as: user)
- Page::Main::Menu.perform(&:go_to_create_project)
- Page::Project::New.perform do |project_page|
- project_page.click_import_project
- project_page.click_github_link
- end
- end
-
- after do
- user.remove_via_api!
- end
-
- it 'imports a project', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347877' do
- Page::Project::Import::Github.perform do |import_page|
- import_page.add_personal_access_token(Runtime::Env.github_access_token)
-
- import_page.select_advanced_option(:single_endpoint_issue_events_import)
- import_page.select_advanced_option(:single_endpoint_notes_import)
- import_page.select_advanced_option(:attachments_import)
-
- import_page.import!(github_repo, group.full_path, imported_project.name)
-
- aggregate_failures do
- expect(import_page).to have_imported_project(github_repo, wait: 240)
- # validate button is present instead of navigating to avoid dealing with multiple tabs
- # which makes the test more complicated
- expect(import_page).to have_go_to_project_button(github_repo)
- end
- end
-
- imported_project.reload!.visit!
- Page::Project::Show.perform do |project|
- aggregate_failures do
- expect(project).to have_content(imported_project.name)
- expect(project).to have_content('Project for github import test')
- end
- end
-
- # Validate :single_endpoint_issue_events_import option was triggered correctly and imported the events
- expect(imported_issue_events).to match_array(
- [
- { name: "add_label", label: "question" },
- { name: "add_label", label: "good first issue" },
- { name: "add_label", label: "help wanted" }
- ]
- )
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb
index 66208921f0e..eaf43f04c4b 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb
@@ -1,11 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Plan', quarantine: {
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/366839',
- type: :test_environment,
- only: { job: 'review-qa-*' }
- } do
+ RSpec.describe 'Plan', product_group: :product_planning do
describe 'Design Management' do
let(:issue) { Resource::Issue.fabricate_via_api! }
let(:design_filename) { 'banana_sample.gif' }
diff --git a/qa/qa/specs/features/browser_ui/2_plan/design_management/archive_design_content_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/design_management/archive_design_content_spec.rb
index 8cbc6d7209c..03b2bc6823a 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/design_management/archive_design_content_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/design_management/archive_design_content_spec.rb
@@ -1,11 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Plan', quarantine: {
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/366839',
- type: :test_environment,
- only: { job: 'review-qa-*' }
- } do
+ RSpec.describe 'Plan', product_group: :product_planning do
describe 'Design Management' do
let(:first_design) { Resource::Design.fabricate! }
diff --git a/qa/qa/specs/features/browser_ui/2_plan/design_management/modify_design_content_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/design_management/modify_design_content_spec.rb
index 8f4902026d2..61b67441ebb 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/design_management/modify_design_content_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/design_management/modify_design_content_spec.rb
@@ -1,11 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Plan', quarantine: {
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/366839',
- type: :test_environment,
- only: { job: 'review-qa-*' }
- } do
+ RSpec.describe 'Plan', product_group: :product_planning do
describe 'Design Management' do
let(:design) do
Resource::Design.fabricate_via_browser_ui! do |design|
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb
index 36cfb9dfb6e..fd818c3797b 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb
@@ -17,7 +17,7 @@ module QA
before do
Flow::Login.sign_in
- if QA::Support::FIPS.enabled?
+ if Runtime::Env.personal_access_tokens_disabled?
# Ensure user exists
user
Flow::Login.sign_in_as_admin
@@ -31,7 +31,7 @@ module QA
project.add_member(user)
end
- if QA::Support::FIPS.enabled?
+ if Runtime::Env.personal_access_tokens_disabled?
Resource::Issue.fabricate_via_browser_ui! do |issue|
issue.project = project
end.visit!
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
index 330cae575e4..236af93716f 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
@@ -27,7 +27,7 @@ module QA
merge_request.visit!
Page::MergeRequest::Show.perform do |mr_page|
- expect(mr_page).to have_content('Merge blocked: the source branch must be rebased onto the target branch.')
+ expect(mr_page).to have_content('Merge blocked: the source branch must be rebased onto the target branch.', wait: 20)
expect(mr_page).to be_fast_forward_not_possible
expect(mr_page).not_to have_merge_button
expect(merge_request.project.commits.size).to eq(2)
diff --git a/qa/qa/specs/features/browser_ui/3_create/pages/new_static_page_spec.rb b/qa/qa/specs/features/browser_ui/3_create/pages/new_static_page_spec.rb
index c35aa403bfa..449bffe61e0 100644
--- a/qa/qa/specs/features/browser_ui/3_create/pages/new_static_page_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/pages/new_static_page_spec.rb
@@ -1,13 +1,21 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', :gitlab_pages, :orchestrated, except: { job: 'review-qa-*', subdomain: :production } do
+ RSpec.describe 'Create',
+ :gitlab_pages,
+ :orchestrated,
+ except: { job: 'review-qa-*' },
+ quarantine: {
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/383215',
+ type: :test_environment,
+ only: { subdomain: 'staging-ref' }
+ } do
# TODO: Convert back to :smoke once proved to be stable. Related issue: https://gitlab.com/gitlab-org/gitlab/-/issues/300906
describe 'Pages', product_group: :editor do
let!(:project) do
Resource::Project.fabricate_via_api! do |project|
- project.name = 'jekyll-pages-project'
- project.template_name = :jekyll
+ project.name = 'gitlab-pages-project'
+ project.template_name = :plainhtml
end
end
@@ -45,7 +53,8 @@ module QA
pages.go_to_access_page
Support::Waiter.wait_until(sleep_interval: 2, max_duration: 60, reload_page: page,
retry_on_exception: true) do
- expect(page).to have_content('Write an awesome description for your new site here.')
+ expect(page).to have_content(
+ 'This is a simple plain-HTML website on GitLab Pages, without any fancy static site generator.')
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb
index 0503b1b3761..b98bb8592d3 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb
@@ -2,7 +2,10 @@
module QA
RSpec.describe 'Create' do
- describe 'Branch with unusual name', product_group: :source_code do
+ describe 'Branch with unusual name', product_group: :source_code, quarantine: {
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/364565',
+ type: :bug
+ } do
let(:branch_name) { 'unUsually/named#br--anch' }
let(:project) do
Resource::Project.fabricate_via_api! do |resource|
@@ -29,6 +32,16 @@ module QA
Page::Project::Show.perform do |show|
show.switch_to_branch(branch_name)
+
+ # It takes a few seconds for console errors to appear
+ sleep 3
+
+ errors = page.driver.browser.logs.get(:browser)
+ .select { |e| e.level == "SEVERE" }
+ .to_a
+
+ raise("Console error(s):\n#{errors.join("\n\n")}") if errors.present?
+
show.click_file('test-folder')
expect(show).to have_file('test-file.md')
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb
index 0e5fcea438d..aeb8e7d27bf 100644
--- a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb
@@ -18,7 +18,7 @@ module QA
end
after do
- if QA::Support::FIPS.enabled?
+ if Runtime::Env.personal_access_tokens_disabled?
snippet.visit!
Page::Dashboard::Snippet::Show.perform(&:click_delete_button)
else
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb
index 8ea65e17e13..93f804f1e39 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', product_group: :editor do
+ RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :project }, product_group: :editor do
describe 'Web IDE file templates' do
include Runtime::Fixtures
@@ -11,6 +11,11 @@ module QA
project.description = 'Add file templates via the Web IDE'
project.initialize_with_readme = true
end
+ Runtime::Feature.disable(:vscode_web_ide, project: @project)
+ end
+
+ after(:all) do
+ Runtime::Feature.enable(:vscode_web_ide, project: @project)
end
templates = [
@@ -54,6 +59,7 @@ module QA
Page::Project::Show.perform(&:open_web_ide!)
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
ide.create_new_file_from_template template[:file_name], template[:name]
expect(ide.has_file?(template[:file_name])).to be_truthy
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb
index 1da9ed652fe..a001dee891a 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', product_group: :editor do
+ RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :project }, product_group: :editor do
describe 'Add a directory in Web IDE' do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
@@ -11,11 +11,15 @@ module QA
end
before do
+ Runtime::Feature.disable(:vscode_web_ide, project: project)
Flow::Login.sign_in
-
project.visit!
end
+ after do
+ Runtime::Feature.enable(:vscode_web_ide, project: project)
+ end
+
context 'when a directory with the same name already exists' do
let(:directory_name) { 'first_directory' }
@@ -38,6 +42,11 @@ module QA
it 'throws an error', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347733' do
Page::Project::WebIDE::Edit.perform do |ide|
+ # Support::Waiter.wait_until(sleep_interval: 2, max_duration: 60, reload_page: page,
+ # retry_on_exception: true) do
+ # expect(ide).to have_element(:commit_mode_tab)
+ # end
+ ide.wait_until_ide_loads
ide.add_directory(directory_name)
end
@@ -54,6 +63,7 @@ module QA
it 'shows in the tree view but cannot be committed', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347732' do
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
ide.add_directory(directory_name)
expect(ide).to have_file(directory_name)
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb
index 1dfda1608f4..cb0da601a88 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', product_group: :editor do
+ RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :project }, product_group: :editor do
describe 'First file using Web IDE' do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
@@ -13,14 +13,20 @@ module QA
let(:file_name) { 'the very first file.txt' }
before do
+ Runtime::Feature.disable(:vscode_web_ide, project: project)
Flow::Login.sign_in
end
+ after do
+ Runtime::Feature.enable(:vscode_web_ide, project: project)
+ end
+
it "creates the first file in an empty project via Web IDE", testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347803' do
project.visit!
Page::Project::Show.perform(&:create_first_new_file!)
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
ide.create_first_file(file_name)
ide.commit_changes
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb
index 56cf2a08bd9..2007fe4a667 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', product_group: :editor do
+ RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :project }, product_group: :editor do
describe 'Link to line in Web IDE' do
let(:user) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) }
let(:project) do
@@ -11,10 +11,12 @@ module QA
end
before do
+ Runtime::Feature.disable(:vscode_web_ide, project: project)
Flow::Login.sign_in
end
after do
+ Runtime::Feature.enable(:vscode_web_ide, project: project)
project.remove_via_api!
end
@@ -25,6 +27,7 @@ module QA
Page::Project::Show.perform(&:open_web_ide_via_shortcut)
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
ide.select_file('app.js')
@link = ide.link_line('26')
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb
index 820b47a3175..dc9f68c5c73 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', product_group: :editor do
+ RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :project }, product_group: :editor do
describe 'Open a fork in Web IDE',
skip: {
issue: "https://gitlab.com/gitlab-org/gitlab/-/issues/351696",
@@ -14,6 +14,14 @@ module QA
end
end
+ before do
+ Runtime::Feature.disable(:vscode_web_ide, project: parent_project)
+ end
+
+ after do
+ Runtime::Feature.enable(:vscode_web_ide, project: parent_project)
+ end
+
context 'when a user does not have permissions to commit to the project' do
let(:user) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_2, Runtime::Env.gitlab_qa_password_2) }
@@ -57,6 +65,7 @@ module QA
def submit_merge_request_upstream
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
expect(ide).to have_project_path("#{user.username}/#{parent_project.name}")
ide.add_file('new file', 'some random text')
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb
index 685cd2d4ad6..039d25477bf 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', product_group: :editor do
+ RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :project }, product_group: :editor do
describe 'Open Web IDE from Diff Tab' do
files = [
{
@@ -44,11 +44,15 @@ module QA
end
before do
+ Runtime::Feature.disable(:vscode_web_ide, project: project)
Flow::Login.sign_in
-
merge_request.visit!
end
+ after do
+ Runtime::Feature.enable(:vscode_web_ide, project: project)
+ end
+
it 'opens and edits a multi-file merge request in Web IDE from Diff Tab', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347724' do
Page::MergeRequest::Show.perform do |show|
show.click_diffs_tab
@@ -56,6 +60,7 @@ module QA
end
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
files.each do |files|
expect(ide).to have_file(files[:file_path])
expect(ide).to have_file_content(files[:file_path], files[:content])
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb
index e4f29952f99..fe0060e9bbc 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb
@@ -1,31 +1,44 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', product_group: :editor, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/381530', type: :stale } do
+ RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :project }, product_group: :editor do
describe 'Review a merge request in Web IDE' do
let(:new_file) { 'awesome_new_file.txt' }
let(:original_text) { 'Text' }
let(:review_text) { 'Reviewed ' }
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'review-merge-request-spec-project'
+ project.initialize_with_readme = true
+ end
+ end
+
let(:merge_request) do
Resource::MergeRequest.fabricate_via_api! do |mr|
mr.file_name = new_file
mr.file_content = original_text
+ mr.project = project
end
end
before do
+ Runtime::Feature.disable(:vscode_web_ide, project: project)
Flow::Login.sign_in
-
merge_request.visit!
end
+ after do
+ Runtime::Feature.enable(:vscode_web_ide, project: project)
+ end
+
it 'opens and edits a merge request in Web IDE', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347786' do
Page::MergeRequest::Show.perform do |show|
show.click_open_in_web_ide
end
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
ide.has_file?(new_file)
ide.add_to_modified_content(review_text)
ide.commit_changes
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/server_hooks_custom_error_message_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/server_hooks_custom_error_message_spec.rb
index 0972e4f3e3d..3cd14ecd799 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/server_hooks_custom_error_message_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/server_hooks_custom_error_message_spec.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', :skip_live_env, except: { job: 'review-qa-*' }, product_group: :editor do
+ RSpec.describe 'Create', :skip_live_env, except: { job: 'review-qa-*' },
+ feature_flag: { name: 'vscode_web_ide', scope: :project },
+ product_group: :editor do
describe 'Git Server Hooks' do
let(:file_path) { File.absolute_path(File.join('qa', 'fixtures', 'web_ide', 'README.md')) }
@@ -14,15 +16,21 @@ module QA
end
before do
+ Runtime::Feature.disable(:vscode_web_ide, project: project)
Flow::Login.sign_in
project.visit!
end
+ after do
+ Runtime::Feature.enable(:vscode_web_ide, project: project)
+ end
+
context 'Custom error messages' do
it 'renders preconfigured error message when user hook failed on commit in WebIDE',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/364751' do
Page::Project::Show.perform(&:open_web_ide_via_shortcut)
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
ide.upload_file(file_path)
ide.commit_changes(wait_for_success: false)
expect(ide).to have_text('Custom error message rejecting prereceive hook for projects with GL_PROJECT_PATH')
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb
index c0f65416a1c..c6e283f67e0 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', product_group: :editor do
+ RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :project }, product_group: :editor do
describe 'Upload a file in Web IDE' do
let(:file_path) { File.absolute_path(File.join('qa', 'fixtures', 'web_ide', file_name)) }
@@ -13,17 +13,23 @@ module QA
end
before do
+ Runtime::Feature.disable(:vscode_web_ide, project: project)
Flow::Login.sign_in
project.visit!
Page::Project::Show.perform(&:open_web_ide!)
end
+ after do
+ Runtime::Feature.enable(:vscode_web_ide, project: project)
+ end
+
context 'when a file with the same name already exists' do
let(:file_name) { 'README.md' }
it 'throws an error', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347850' do
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
ide.upload_file(file_path)
end
@@ -36,6 +42,7 @@ module QA
it 'shows the Edit tab with the text', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347852' do
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
ide.upload_file(file_path)
expect(ide).to have_file(file_name)
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/web_terminal_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/web_terminal_spec.rb
index f90676ee15a..695b295bd86 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/web_terminal_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/web_terminal_spec.rb
@@ -10,10 +10,12 @@ module QA
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/338179',
type: :bug
},
+ feature_flag: { name: 'vscode_web_ide', scope: :project },
product_group: :editor
) do
describe 'Web IDE web terminal' do
before do
+ Runtime::Feature.disable(:vscode_web_ide, project: project)
project = Resource::Project.fabricate_via_api! do |project|
project.name = 'web-terminal-project'
end
@@ -56,6 +58,7 @@ module QA
end
after do
+ Runtime::Feature.enable(:vscode_web_ide, project: project)
@runner.remove_via_api! if @runner
end
@@ -76,6 +79,7 @@ module QA
# There are also FE specs
# * spec/frontend/ide/components/terminal/terminal_controls_spec.js
Page::Project::WebIDE::Edit.perform do |edit|
+ edit.wait_until_ide_loads
edit.start_web_terminal
expect(edit).to have_no_alert
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb
index 4a0a8be3659..5dda8b04805 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb
@@ -2,10 +2,7 @@
module QA
RSpec.describe 'Verify', :runner do
- describe 'Pipeline with customizable variable', feature_flag: {
- name: :run_pipeline_graphql,
- scope: :project
- } do
+ describe 'Pipeline with customizable variable' do
let(:executor) { "qa-runner-#{Time.now.to_i}" }
let(:pipeline_job_name) { 'customizable-variable' }
let(:variable_custom_value) { 'Custom Foo' }
@@ -48,74 +45,45 @@ module QA
end
end
- shared_examples 'pipeline with custom variable' do
- before do
- Flow::Login.sign_in
+ before do
+ Flow::Login.sign_in
- project.visit!
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
+ project.visit!
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
- # Sometimes the variables will not be prefilled because of reactive cache so we revisit the page again.
- # TODO: Investigate alternatives to deal with cache implementation
- # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/381233
- page.refresh
- end
-
- after do
- runner&.remove_via_api!
- end
-
- it 'manually creates a pipeline and uses the defined custom variable value' do
- Page::Project::Pipeline::New.perform do |new|
- new.configure_variable(value: variable_custom_value)
- new.click_run_pipeline_button
- end
-
- Page::Project::Pipeline::Show.perform do |show|
- Support::Waiter.wait_until { show.passed? }
- end
-
- job = Resource::Job.fabricate_via_api! do |job|
- job.id = project.job_by_name(pipeline_job_name)[:id]
- job.name = pipeline_job_name
- job.project = project
- end
-
- job.visit!
+ # Sometimes the variables will not be prefilled because of reactive cache so we revisit the page again.
+ # TODO: Investigate alternatives to deal with cache implementation
+ # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/381233
+ page.refresh
+ end
- Page::Project::Job::Show.perform do |show|
- expect(show.output).to have_content(variable_custom_value)
- end
- end
+ after do
+ runner&.remove_via_api!
end
- # TODO: Clean up tests when run_pipeline_graphql is enabled
- # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/372310
- context(
- 'with feature flag disabled',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/361814'
- ) do
- before do
- Runtime::Feature.disable(:run_pipeline_graphql, project: project)
+ it 'manually creates a pipeline and uses the defined custom variable value',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/378975' do
+ Page::Project::Pipeline::New.perform do |new|
+ new.configure_variable(value: variable_custom_value)
+ new.click_run_pipeline_button
end
- it_behaves_like 'pipeline with custom variable'
- end
-
- context(
- 'with feature flag enabled',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/378975'
- ) do
- before do
- Runtime::Feature.enable(:run_pipeline_graphql, project: project)
+ Page::Project::Pipeline::Show.perform do |show|
+ Support::Waiter.wait_until { show.passed? }
end
- after do
- Runtime::Feature.disable(:run_pipeline_graphql, project: project)
+ job = Resource::Job.fabricate_via_api! do |job|
+ job.id = project.job_by_name(pipeline_job_name)[:id]
+ job.name = pipeline_job_name
+ job.project = project
end
- it_behaves_like 'pipeline with custom variable'
+ job.visit!
+
+ Page::Project::Job::Show.perform do |show|
+ expect(show.output).to have_content(variable_custom_value)
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb
index c4ce916d47d..1878292015e 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb
@@ -2,13 +2,12 @@
module QA
RSpec.describe 'Verify' do
- describe 'Pipeline with prefill variables', feature_flag: {
- name: :run_pipeline_graphql,
- scope: :project
- } do
+ describe 'Pipeline with prefill variables' do
let(:prefill_variable_description1) { Faker::Lorem.sentence }
let(:prefill_variable_value1) { Faker::Lorem.word }
+ let(:prefill_variable_value5) { Faker::Lorem.word }
let(:prefill_variable_description2) { Faker::Lorem.sentence }
+ let(:prefill_variable_description5) { Faker::Lorem.sentence }
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'project-with-prefill-variables'
@@ -33,7 +32,12 @@ module QA
TEST3:
value: test 3 value
TEST4: test 4 value
-
+ TEST5:
+ value: "FOO"
+ options:
+ - #{prefill_variable_value5}
+ - "FOO"
+ description: #{prefill_variable_description5}
test:
script: echo "$FOO"
YAML
@@ -43,62 +47,53 @@ module QA
end
end
- shared_examples 'pre-filled variables form' do
- before do
- Flow::Login.sign_in
+ before do
+ Flow::Login.sign_in
+ project.visit!
- project.visit!
- # Navigate to Run Pipeline page
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
+ # Navigate to Run Pipeline page
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
- # Sometimes the variables will not be prefilled because of reactive cache so we revisit the page again.
- # TODO: Investigate alternatives to deal with cache implementation
- # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/381233
- page.refresh
- end
+ # Sometimes the variables will not be prefilled because of reactive cache so we revisit the page again.
+ # TODO: Investigate alternatives to deal with cache implementation
+ # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/381233
+ page.refresh
+ end
- it 'shows only variables with description as prefill variables on the run pipeline page' do
- Page::Project::Pipeline::New.perform do |new|
- aggregate_failures do
- expect(new).to have_field('Input variable key', with: 'TEST1')
- expect(new).to have_field('Input variable value', with: prefill_variable_value1)
- expect(new).to have_content(prefill_variable_description1)
+ it 'shows only variables with description as prefill variables on the run pipeline page',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/378977' do
+ Page::Project::Pipeline::New.perform do |new|
+ aggregate_failures do
+ expect(new).to have_field('Input variable key', with: 'TEST1')
+ expect(new).to have_field('Input variable value', with: prefill_variable_value1)
+ expect(new).to have_content(prefill_variable_description1)
+
+ expect(new).to have_field('Input variable key', with: 'TEST2')
+ expect(new).to have_field('Input variable value', with: '')
+ expect(new).to have_content(prefill_variable_description2)
- expect(new).to have_field('Input variable key', with: 'TEST2')
- expect(new).to have_content(prefill_variable_description2)
+ expect(new).not_to have_field('Input variable key', with: 'TEST3')
+ expect(new).not_to have_field('Input variable key', with: 'TEST4')
- expect(new).not_to have_field('Input variable key', with: 'TEST3')
- expect(new).not_to have_field('Input variable key', with: 'TEST4')
- end
+ expect(new).to have_field('Input variable key', with: 'TEST5')
+ expect(new).to have_content(prefill_variable_description5)
end
end
end
- # TODO: Clean up tests when run_pipeline_graphql is enabled
- # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/372310
- context(
- 'with feature flag disabled',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/371204'
- ) do
- before do
- Runtime::Feature.disable(:run_pipeline_graphql, project: project)
- end
-
- it_behaves_like 'pre-filled variables form'
- end
+ it 'shows dropdown for variables with description, value, and options defined',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/383820' do
+ Page::Project::Pipeline::New.perform do |new|
+ aggregate_failures do
+ expect(new.variable_dropdown).to have_text('FOO')
- context 'with feature flag enabled',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/378977' do
- before do
- Runtime::Feature.enable(:run_pipeline_graphql, project: project)
- end
+ new.click_variable_dropdown
- after do
- Runtime::Feature.disable(:run_pipeline_graphql, project: project)
+ expect(new.variable_dropdown_item_with_index(0)).to have_text(prefill_variable_value5)
+ expect(new.variable_dropdown_item_with_index(1)).to have_text('FOO')
+ end
end
-
- it_behaves_like 'pre-filled variables form'
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb
index fe934e8c60f..a4849d47183 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb
@@ -120,7 +120,7 @@ module QA
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/381486',
quarantine: {
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/381806',
- only: { pipeline: %w[staging staging-canary] },
+ only: { pipeline: %w[staging staging-canary staging-ref] },
type: :waiting_on
} do
before do
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/mr_event_rule_pipeline_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/mr_event_rule_pipeline_spec.rb
index e876bf3ab8b..448da6d9d87 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/mr_event_rule_pipeline_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/mr_event_rule_pipeline_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do
- context 'When job is configured to only run on merge_request_events' do
+ context 'when job is configured to only run on merge_request_events' do
let(:mr_only_job_name) { 'mr_only_job' }
let(:non_mr_only_job_name) { 'non_mr_only_job' }
let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" }
@@ -31,10 +31,12 @@ module QA
file_path: '.gitlab-ci.yml',
content: <<~YAML
#{mr_only_job_name}:
+ tags: ["#{executor}"]
script: echo 'OK'
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
#{non_mr_only_job_name}:
+ tags: ["#{executor}"]
script: echo 'OK'
rules:
- if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
@@ -57,13 +59,17 @@ module QA
before do
Flow::Login.sign_in
- merge_request.visit!
- Page::MergeRequest::Show.perform(&:click_pipeline_link)
+ # TODO: We should remove (wait) revisiting logic when
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/385332 is resolved
+ Support::Waiter.wait_until do
+ merge_request.visit!
+ Page::MergeRequest::Show.perform(&:click_pipeline_link)
+ Page::Project::Pipeline::Show.perform(&:has_merge_request_badge_tag?)
+ end
end
after do
runner.remove_via_api!
- project.remove_via_api!
end
it 'only runs the job configured to run on merge requests', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347662' do
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb
index b1ecce297c9..d30d5b43568 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb
@@ -33,10 +33,7 @@ module QA
runner.remove_via_api!
end
- context(
- 'when policy is allowed',
- quarantine: { type: :flaky, issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/369397' }
- ) do
+ context 'when policy is allowed' do
let(:allowed_policies) { %w[if-not-present always never] }
where do
@@ -87,20 +84,19 @@ module QA
let(:allowed_policies) { %w[never] }
let(:pull_policies) { %w[always] }
- let(:message) do
- 'ERROR: Preparation failed: the configured PullPolicies ([always])'\
- ' are not allowed by AllowedPullPolicies ([never])'
- end
+ # The sentence seems differ from time to time,
+ # only checking portions of the sentence that matter
+ let(:text1) { 'pull_policy ([always])' }
+ let(:text2) { 'is not one of the allowed_pull_policies ([never])' }
it(
'fails job with policy not allowed message',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/368853',
- quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/371420', type: :stale }
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/368853'
) do
visit_job
- expect(job_log).to have_content(message),
- "Expected to find #{message} in #{job_log}, but didn't."
+ expect(job_log).to include(text1, text2),
+ "Expected to find contents #{text1} and #{text2} in #{job_log}, but didn't."
end
end
@@ -123,8 +119,7 @@ module QA
tempdir.close!
- # Give runner some time to pick up new configuration
- sleep(30)
+ runner.restart
end
def add_ci_file
@@ -154,7 +149,7 @@ module QA
def visit_job
Page::Project::Pipeline::Show.perform do |show|
- Support::Waiter.wait_until { show.completed? }
+ Support::Waiter.wait_until(max_duration: 90) { show.completed? }
show.click_job(job_name)
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb
index aac8893ff2c..cda45efd828 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :skip_live_env, :orchestrated, :packages, :object_storage, :reliable, product_group: :package_registry do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage, :reliable, product_group: :package_registry do
describe 'Maven group level endpoint' do
include Runtime::Fixtures
+ include Support::Helpers::MaskToken
include_context 'packages registry qa scenario'
let(:group_id) { 'com.gitlab.qa' }
@@ -12,6 +13,18 @@ module QA
let(:package_version) { '1.3.7' }
let(:package_type) { 'maven' }
+ let(:group_deploy_token) do
+ Resource::GroupDeployToken.fabricate_via_api! do |deploy_token|
+ deploy_token.name = 'maven-group-deploy-token'
+ deploy_token.group = package_project.group
+ deploy_token.scopes = %w[
+ read_repository
+ read_package_registry
+ write_package_registry
+ ]
+ end
+ end
+
context 'via maven' do
where do
{
@@ -37,11 +50,13 @@ module QA
let(:token) do
case authentication_token_type
when :personal_access_token
- personal_access_token
+ use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: package_project)
+ use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: client_project)
when :ci_job_token
- '${env.CI_JOB_TOKEN}'
+ '${CI_JOB_TOKEN}'
when :project_deploy_token
- project_deploy_token.token
+ use_ci_variable(name: 'GROUP_DEPLOY_TOKEN', value: group_deploy_token.token, project: package_project)
+ use_ci_variable(name: 'GROUP_DEPLOY_TOKEN', value: group_deploy_token.token, project: client_project)
end
end
@@ -121,8 +136,9 @@ module QA
context 'duplication setting' do
before do
+ use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: package_project)
+ use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: client_project)
package_project.group.visit!
-
Page::Group::Menu.perform(&:go_to_package_settings)
end
@@ -132,16 +148,19 @@ module QA
end
it 'prevents users from publishing duplicates', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/377491' do
- create_duplicated_package
+ create_package(package_project)
+ show_latest_deploy_job
- push_duplicated_package
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 400)
+ end
- client_project.visit!
+ Page::Project::Job::Show.perform(&:retry!)
show_latest_deploy_job
Page::Project::Job::Show.perform do |job|
- expect(job).not_to be_successful(timeout: 800)
+ expect(job).not_to be_successful(timeout: 400)
end
end
end
@@ -152,52 +171,32 @@ module QA
end
it 'allows users to publish duplicates', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/377492' do
- create_duplicated_package
-
- push_duplicated_package
+ create_package(package_project)
show_latest_deploy_job
Page::Project::Job::Show.perform do |job|
- expect(job).to be_successful(timeout: 800)
+ expect(job).to be_successful(timeout: 400)
end
- end
- end
- def create_duplicated_package
- settings_xml_with_pat = ERB.new(read_fixture('package_managers/maven/group', 'settings_with_pat.xml.erb')).result(binding)
- pom_xml = ERB.new(read_fixture('package_managers/maven/group/producer', 'pom.xml.erb')).result(binding)
-
- with_fixtures([
- {
- file_path: 'pom.xml',
- content: pom_xml
- },
- {
- file_path: 'settings.xml',
- content: settings_xml_with_pat
- }
- ]) do |dir|
- Service::DockerRun::Maven.new(dir).publish!
- end
-
- package_project.visit!
+ Page::Project::Job::Show.perform(&:retry!)
- Page::Project::Menu.perform(&:click_packages_link)
+ show_latest_deploy_job
- Page::Project::Packages::Index.perform do |index|
- expect(index).to have_package(package_name)
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 400)
+ end
end
end
- def push_duplicated_package
+ def create_package(project)
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
Resource::Repository::Commit.fabricate_via_api! do |commit|
gitlab_ci_yaml = ERB.new(read_fixture('package_managers/maven/group/producer', 'gitlab_ci.yaml.erb')).result(binding)
pom_xml = ERB.new(read_fixture('package_managers/maven/group/producer', 'pom.xml.erb')).result(binding)
settings_xml_with_pat = ERB.new(read_fixture('package_managers/maven/group', 'settings_with_pat.xml.erb')).result(binding)
- commit.project = client_project
+ commit.project = project
commit.commit_message = 'Add .gitlab-ci.yml'
commit.add_files(
[
@@ -210,7 +209,7 @@ module QA
end
def show_latest_deploy_job
- client_project.visit!
+ package_project.visit!
Flow::Pipeline.visit_latest_pipeline
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb
index 8e1b0176f35..46c165ed806 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb
@@ -1,13 +1,14 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :skip_live_env, :orchestrated, :packages, :object_storage, :reliable,
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage, :reliable,
feature_flag: {
name: 'maven_central_request_forwarding',
scope: :global
} do
describe 'Maven project level endpoint', product_group: :package_registry do
include Runtime::Fixtures
+ include Support::Helpers::MaskToken
let(:group_id) { 'com.gitlab.qa' }
let(:artifact_id) { "maven-#{SecureRandom.hex(8)}" }
@@ -92,11 +93,11 @@ module QA
let(:token) do
case authentication_token_type
when :personal_access_token
- personal_access_token
+ use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: package_project)
when :ci_job_token
- '${env.CI_JOB_TOKEN}'
+ '${CI_JOB_TOKEN}'
when :project_deploy_token
- project_deploy_token.token
+ use_ci_variable(name: 'PROJECT_DEPLOY_TOKEN', value: project_deploy_token.token, project: package_project)
end
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/rubygems_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/rubygems_registry_spec.rb
index 63ab826e57b..284130fa92b 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/rubygems_registry_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/rubygems_registry_spec.rb
@@ -40,9 +40,6 @@ module QA
after do
Runtime::Feature.disable(:rubygem_packages, project: project)
- runner.remove_via_api!
- package.remove_via_api!
- project.remove_via_api!
end
it 'publishes a Ruby gem', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347649' do
diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
index 057b4c15db1..d6446c9725d 100644
--- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
+++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Configure', only: { subdomain: %i[staging staging-canary] }, product_group: :configure do
+ RSpec.describe 'Configure',
+ quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/381454', type: :flaky },
+ only: { subdomain: %i[staging staging-canary] }, product_group: :configure do
describe 'Auto DevOps with a Kubernetes Agent' do
let!(:app_project) do
Resource::Project.fabricate_via_api! do |project|
diff --git a/qa/qa/specs/features/browser_ui/8_monitor/.gitkeep b/qa/qa/specs/features/browser_ui/8_monitor/.gitkeep
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/qa/qa/specs/features/browser_ui/8_monitor/.gitkeep
+++ /dev/null
diff --git a/qa/qa/specs/features/browser_ui/8_monitor/incident_management/http_endpoint_integration_creates_alert_spec.rb b/qa/qa/specs/features/browser_ui/8_monitor/incident_management/http_endpoint_integration_creates_alert_spec.rb
new file mode 100644
index 00000000000..8ea728ca94c
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/8_monitor/incident_management/http_endpoint_integration_creates_alert_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Monitor', product_group: :respond do
+ describe 'Http endpoint integration' do
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-for-alerts'
+ project.description = 'Project for alerts'
+ end
+ end
+
+ let(:random_word) { Faker::Lorem.word }
+
+ let(:payload) do
+ { title: random_word, description: random_word }
+ end
+
+ before do
+ Flow::Login.sign_in
+ project.visit!
+ Flow::AlertSettings.setup_http_endpoint_and_send_alert(payload: payload)
+ end
+
+ it(
+ 'can send test alert that creates new alert',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/382803'
+ ) do
+ Page::Project::Menu.perform(&:go_to_monitor_alerts)
+ Page::Project::Monitor::Alerts::Index.perform do |alerts|
+ expect(alerts).to have_alert_with_title(random_word)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/sanity/interception_spec.rb b/qa/qa/specs/features/sanity/interception_spec.rb
new file mode 100644
index 00000000000..f8930db3aa5
--- /dev/null
+++ b/qa/qa/specs/features/sanity/interception_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Browser request interception', :orchestrated, :framework do
+ before(:context) do
+ skip 'Only can test for chrome' unless QA::Runtime::Env.can_intercept?
+ end
+
+ before do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ end
+
+ let(:page) { Capybara.current_session }
+ let(:logger) { class_double('QA::Runtime::Logger') }
+
+ it 'intercepts failed graphql calls' do
+ page.execute_script <<~JS
+ fetch('/api/graphql', {
+ method: 'POST',
+ body: JSON.stringify({ query: 'query {}'}),
+ headers: { 'Content-Type': 'application/json' }
+ })
+ JS
+
+ Support::Waiter.wait_until do
+ !get_cached_error.nil?
+ end
+ expect(**get_cached_error).to include({ 'method' => 'POST', 'status' => 200, 'url' => '/api/graphql' })
+ end
+
+ def get_cached_error
+ cache = page.execute_script <<~JS
+ return Interceptor.getCache()
+ JS
+
+ cache['errors']&.first
+ end
+ end
+end
diff --git a/qa/qa/specs/features/shared_contexts/import/github_import_shared_context.rb b/qa/qa/specs/features/shared_contexts/import/github_import_shared_context.rb
index 0a0c2a4a6df..27d94b04cde 100644
--- a/qa/qa/specs/features/shared_contexts/import/github_import_shared_context.rb
+++ b/qa/qa/specs/features/shared_contexts/import/github_import_shared_context.rb
@@ -1,10 +1,10 @@
# frozen_string_literal: true
module QA
- RSpec.shared_context "with github import", :github, :skip_live_env, :requires_admin, quarantine: {
- type: :broken,
- issue: "https://gitlab.com/gitlab-org/gitlab/-/issues/382166"
- } do
+ RSpec.shared_context "with github import", :github, :import, :requires_admin, :orchestrated do
+ include QA::Support::Data::Github
+
+ let!(:github_repo) { "#{github_username}/import-test" }
let!(:api_client) { Runtime::API::Client.as_admin }
let!(:group) do
@@ -28,7 +28,7 @@ module QA
project.name = 'imported-project'
project.group = group
project.github_personal_access_token = Runtime::Env.github_access_token
- project.github_repository_path = 'gitlab-qa-github/import-test'
+ project.github_repository_path = github_repo
project.api_client = user_api_client
project.issue_events_import = true
project.full_notes_import = true
@@ -38,9 +38,5 @@ module QA
before do
group.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
end
-
- after do
- user.remove_via_api!
- end
end
end
diff --git a/qa/qa/specs/features/shared_contexts/import/gitlab_group_migration_common.rb b/qa/qa/specs/features/shared_contexts/import/gitlab_group_migration_common.rb
new file mode 100644
index 00000000000..e1d762f41cb
--- /dev/null
+++ b/qa/qa/specs/features/shared_contexts/import/gitlab_group_migration_common.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.shared_context(
+ 'with gitlab group migration',
+ :import,
+ :orchestrated,
+ requires_admin: 'creates a user via API'
+ ) do
+ let!(:import_wait_duration) { { max_duration: 120, sleep_interval: 2 } }
+
+ # source instance objects
+ #
+ let!(:source_gitlab_address) { ENV["QA_IMPORT_SOURCE_URL"] || raise("QA_IMPORT_SOURCE_URL is required!") }
+ let!(:source_admin_api_client) do
+ Runtime::API::Client.new(
+ source_gitlab_address,
+ personal_access_token: Runtime::Env.admin_personal_access_token || raise("Admin access token missing!"),
+ is_new_session: false
+ )
+ end
+ let!(:source_admin_user) do
+ Resource::User.fabricate_via_api! do |usr|
+ usr.api_client = source_admin_api_client
+ usr.username = Runtime::Env.admin_username || "root"
+ end.tap(&:set_public_email)
+ end
+ let!(:source_group) do
+ Resource::Sandbox.fabricate_via_api! do |group|
+ group.api_client = source_admin_api_client
+ group.path = "source-group-for-import-#{SecureRandom.hex(4)}"
+ group.avatar = File.new("qa/fixtures/designs/tanuki.jpg", "r")
+ end
+ end
+
+ # target instance objects
+ #
+ let!(:admin_api_client) { Runtime::API::Client.as_admin }
+ let!(:admin_user) do
+ Resource::User.fabricate_via_api! do |usr|
+ usr.api_client = admin_api_client
+ usr.username = Runtime::Env.admin_username || "root"
+ end.tap(&:set_public_email)
+ end
+ let!(:user) do
+ Resource::User.fabricate_via_api! do |usr|
+ usr.api_client = admin_api_client
+ usr.username = "target-user-#{SecureRandom.hex(6)}"
+ end
+ end
+ let!(:api_client) { Runtime::API::Client.new(user: user) }
+ let!(:target_sandbox) do
+ Resource::Sandbox.fabricate_via_api! do |group|
+ group.api_client = admin_api_client
+ end
+ end
+
+ let(:destination_group_path) { source_group.path }
+ let(:imported_group) do
+ Resource::BulkImportGroup.fabricate_via_api! do |group|
+ group.api_client = api_client
+ group.sandbox = target_sandbox
+ group.source_group = source_group
+ group.source_gitlab_address = source_gitlab_address
+ group.destination_group_path = destination_group_path
+ group.import_access_token = source_admin_api_client.personal_access_token
+ end
+ end
+
+ let(:import_failures) do
+ imported_group.import_details.sum([]) { |details| details[:failures] }
+ end
+
+ let(:cleanup!) {}
+
+ def expect_group_import_finished_successfully
+ imported_group # trigger import
+
+ status = nil
+ Support::Retrier.retry_until(**import_wait_duration, raise_on_failure: false) do
+ status = imported_group.import_status
+ %w[finished failed].include?(status)
+ end
+
+ # finished status means success, all other statuses are considered to fail the test
+ expect(status).to eq('finished'), "Expected import to finish successfully, but status was: #{status}"
+ end
+
+ before do
+ Runtime::ApplicationSettings.set_application_settings(bulk_import_enabled: true)
+
+ target_sandbox.add_member(user, Resource::Members::AccessLevel::OWNER)
+ end
+
+ after do |example|
+ # Checking for failures in the test currently makes test very flaky due to catching unrelated failures
+ # Log failures for easier debugging
+ Runtime::Logger.error("Import failures: #{import_failures}") if example.exception && !import_failures.empty?
+ rescue StandardError
+ # rescue when import did not happen at all and checking import failues will raise an error
+ ensure
+ # make sure cleanup runs last
+ cleanup!
+ end
+ end
+end
diff --git a/qa/qa/specs/features/shared_contexts/import/gitlab_project_migration_common.rb b/qa/qa/specs/features/shared_contexts/import/gitlab_project_migration_common.rb
index 9c80c088917..728907c708f 100644
--- a/qa/qa/specs/features/shared_contexts/import/gitlab_project_migration_common.rb
+++ b/qa/qa/specs/features/shared_contexts/import/gitlab_project_migration_common.rb
@@ -1,91 +1,32 @@
# frozen_string_literal: true
module QA
- RSpec.shared_context 'with gitlab project migration', requires_admin: 'creates a user via API',
- quarantine: {
- type: :flaky,
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/364839'
- },
- feature_flag: {
- name: 'bulk_import_projects',
- scope: :global
- } do
- let(:source_project_with_readme) { false }
- let(:import_wait_duration) { { max_duration: 300, sleep_interval: 2 } }
- let(:admin_api_client) { Runtime::API::Client.as_admin }
- let(:user) do
- Resource::User.fabricate_via_api! do |usr|
- usr.api_client = admin_api_client
- usr.hard_delete_on_api_removal = true
- end
- end
+ RSpec.shared_context 'with gitlab project migration' do
+ # gitlab project migration doesn't work on just the projects
+ # so all project migration tests will always require setup for gitlab group migration
+ include_context "with gitlab group migration"
- let(:api_client) { Runtime::API::Client.new(user: user) }
-
- let(:sandbox) do
- Resource::Sandbox.fabricate_via_api! do |group|
- group.api_client = admin_api_client
- end
- end
-
- let(:destination_group) do
- Resource::Group.fabricate_via_api! do |group|
- group.api_client = api_client
- group.sandbox = sandbox
- group.path = "destination-group-for-import-#{SecureRandom.hex(4)}"
- end
- end
-
- let(:source_group) do
- Resource::Group.fabricate_via_api! do |group|
- group.api_client = api_client
- group.path = "source-group-for-import-#{SecureRandom.hex(4)}"
- end
- end
+ let(:source_project_with_readme) { false }
let(:source_project) do
Resource::Project.fabricate_via_api! do |project|
- project.api_client = api_client
+ project.api_client = source_admin_api_client
project.group = source_group
project.initialize_with_readme = source_project_with_readme
end
end
- let(:imported_group) do
- Resource::BulkImportGroup.fabricate_via_api! do |group|
- group.api_client = api_client
- group.sandbox = destination_group
- group.source_group = source_group
- end
- end
-
let(:imported_projects) { imported_group.reload!.projects }
let(:imported_project) { imported_projects.first }
- let(:import_failures) do
- imported_group.import_details.sum([]) { |details| details[:failures] }
- end
-
- def expect_import_finished
- imported_group # trigger import
-
- expect { imported_group.import_status }.to eventually_eq('finished').within(import_wait_duration)
+ def expect_project_import_finished_successfully
+ expect_group_import_finished_successfully
expect(imported_projects.count).to eq(1), "Expected to have 1 imported project. Found: #{imported_projects.count}"
end
before do
- Runtime::Feature.enable(:bulk_import_projects)
-
- sandbox.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
+ Runtime::Feature.enable(:bulk_import_projects) unless Runtime::Feature.enabled?(:bulk_import_projects)
source_project # fabricate source group and project
end
-
- after do |example|
- # Checking for failures in the test currently makes test very flaky due to catching unrelated failures
- # Log failures for easier debugging
- Runtime::Logger.warn("Import failures: #{import_failures}") if example.exception && !import_failures.empty?
- ensure
- user.remove_via_api!
- end
end
end
diff --git a/qa/qa/specs/helpers/feature_flag.rb b/qa/qa/specs/helpers/feature_flag.rb
index 7e618f19ed5..2b0f9e67a41 100644
--- a/qa/qa/specs/helpers/feature_flag.rb
+++ b/qa/qa/specs/helpers/feature_flag.rb
@@ -20,11 +20,11 @@ module QA
# This is to avoid flakiness with other tests running in parallel on the same environment
# as well as interfering with feature flag experimentation done by development groups.
example.metadata[:skip] = global_feature_flag_message if ContextSelector.dot_com?
- else
+ elsif skip_env_for_scoped_feature_flag
# Tests using a feature flag scoped to an actor (ex: :project, :user, :group), or
# with no scope defined (such as in the case of a low risk global feature flag),
# will only be skipped on environments without an admin account
- example.metadata[:skip] = feature_flag_message if skip_env_for_scoped_feature_flag
+ example.metadata[:skip] = feature_flag_message
end
end
end
diff --git a/qa/qa/specs/spec_helper.rb b/qa/qa/specs/spec_helper.rb
index a4721040683..003e8aa74c1 100644
--- a/qa/qa/specs/spec_helper.rb
+++ b/qa/qa/specs/spec_helper.rb
@@ -52,12 +52,10 @@ RSpec.configure do |config|
end
config.prepend_after do |example|
- if example.exception
- page = Capybara.page
+ page = Capybara.page
+ QA::Support::PageErrorChecker.log_request_errors(page)
- QA::Support::PageErrorChecker.log_request_errors(page)
- QA::Support::PageErrorChecker.check_page_for_error_code(page)
- end
+ QA::Support::PageErrorChecker.check_page_for_error_code(page) if example.exception
end
# Add fabrication time to spec metadata
diff --git a/qa/qa/support/data/github.rb b/qa/qa/support/data/github.rb
new file mode 100644
index 00000000000..b0fe1baeff8
--- /dev/null
+++ b/qa/qa/support/data/github.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module QA
+ module Support
+ module Data
+ module Github
+ def github_username
+ 'gitlab-qa-github'
+ end
+ end
+ end
+ end
+end
+
+QA::Support::Data::Github.prepend_mod_with('Support::Data::Github', namespace: QA)
diff --git a/qa/qa/support/data/license.rb b/qa/qa/support/data/license.rb
new file mode 100644
index 00000000000..cd4745fefcd
--- /dev/null
+++ b/qa/qa/support/data/license.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module QA
+ module Support
+ module Data
+ module License
+ def license_user
+ 'GitLab QA'
+ end
+
+ def license_company
+ 'QA User'
+ end
+
+ def license_user_count
+ 10_000
+ end
+
+ def license_plan
+ QA::ULTIMATE_SELF_MANAGED
+ end
+ end
+ end
+ end
+end
+
+QA::Support::Data::License.prepend_mod_with('Support::Data::License', namespace: QA)
diff --git a/qa/qa/support/formatters/allure_metadata_formatter.rb b/qa/qa/support/formatters/allure_metadata_formatter.rb
index 02719536b17..c8ddbeb4536 100644
--- a/qa/qa/support/formatters/allure_metadata_formatter.rb
+++ b/qa/qa/support/formatters/allure_metadata_formatter.rb
@@ -116,8 +116,7 @@ module QA
# @return [Array]
def flaky_specs
@flaky_specs ||= influx_data.lazy.each_with_object({}) do |data, result|
- # Do not consider failures in same merge request
- records = data.records.reject { |r| r.values["_value"] == merge_request_iid }
+ records = data.records
runs = records.count
failed = records.count { |r| r.values["status"] == "failed" }
@@ -136,14 +135,14 @@ module QA
def influx_data
return [] unless run_type
- query_api.query(query: <<~QUERY).values
- from(bucket: "#{Support::InfluxdbTools::INFLUX_TEST_METRICS_BUCKET}")
- |> range(start: -14d)
+ query_api.query(query: <<~QUERY)
+ from(bucket: "#{Support::InfluxdbTools::INFLUX_MAIN_TEST_METRICS_BUCKET}")
+ |> range(start: -30d)
|> filter(fn: (r) => r._measurement == "test-stats")
|> filter(fn: (r) => r.run_type == "#{run_type}" and
r.status != "pending" and
r.quarantined == "false" and
- r._field == "merge_request_iid"
+ r._field == "id"
)
|> group(columns: ["testcase"])
QUERY
diff --git a/qa/qa/support/formatters/test_metrics_formatter.rb b/qa/qa/support/formatters/test_metrics_formatter.rb
index e84373a487d..6e6cdc35af5 100644
--- a/qa/qa/support/formatters/test_metrics_formatter.rb
+++ b/qa/qa/support/formatters/test_metrics_formatter.rb
@@ -8,6 +8,8 @@ module QA
class TestMetricsFormatter < RSpec::Core::Formatters::BaseFormatter
include Support::InfluxdbTools
+ CUSTOM_METRICS_KEY = :custom_test_metrics
+
RSpec::Core::Formatters.register(self, :stop)
# Finish test execution
@@ -19,16 +21,15 @@ module QA
parse_execution_data(notification.examples)
- if Runtime::Env.export_metrics?
- push_test_metrics
- push_fabrication_metrics
- end
-
- save_test_metrics if Runtime::Env.save_metrics_json?
+ push_test_metrics
+ push_fabrication_metrics
+ save_test_metrics
end
private
+ delegate :export_metrics?, :save_metrics_json?, :ci_job_url, :ci_job_name, to: "QA::Runtime::Env"
+
# Save execution data for the run
#
# @param [Array<RSpec::Core::Example>] examples
@@ -42,6 +43,8 @@ module QA
#
# @return [void]
def push_test_metrics
+ return log(:debug, "Metrics export not enabled, skipping test metrics export") unless export_metrics?
+
write_api.write(data: execution_data)
log(:debug, "Pushed #{execution_data.length} test execution entries to influxdb")
rescue StandardError => e
@@ -52,6 +55,8 @@ module QA
#
# @return [void]
def push_fabrication_metrics
+ return log(:debug, "Metrics export not enabled, skipping fabrication metrics export") unless export_metrics?
+
data = Tools::TestResourceDataProcessor.resources.flat_map do |resource, values|
values.map { |v| fabrication_stats(resource: resource, **v) }
end
@@ -67,6 +72,8 @@ module QA
#
# @return [void]
def save_test_metrics
+ return log(:debug, "Saving test metrics json not enabled, skipping") unless save_metrics_json?
+
File.write("tmp/test-metrics-#{env('CI_JOB_NAME_SLUG') || 'local'}.json", execution_data.to_json)
rescue StandardError => e
log(:error, "Failed to save test execution metrics, error: #{e}")
@@ -101,7 +108,8 @@ module QA
run_type: run_type,
stage: devops_stage(file_path),
product_group: example.metadata[:product_group],
- testcase: example.metadata[:testcase]
+ testcase: example.metadata[:testcase],
+ **custom_metrics_tags(example.metadata)
},
fields: {
id: example.id,
@@ -110,11 +118,12 @@ module QA
ui_fabrication: ui_fabrication,
total_fabrication: api_fabrication + ui_fabrication,
retry_attempts: retry_attempts(example.metadata),
- job_url: QA::Runtime::Env.ci_job_url,
+ job_url: ci_job_url,
pipeline_url: env('CI_PIPELINE_URL'),
pipeline_id: env('CI_PIPELINE_ID'),
job_id: env('CI_JOB_ID'),
- merge_request_iid: merge_request_iid
+ merge_request_iid: merge_request_iid,
+ **custom_metrics_fields(example.metadata)
}
}
rescue StandardError => e
@@ -139,13 +148,13 @@ module QA
resource: resource,
fabrication_method: fabrication_method,
http_method: http_method,
- run_type: env('QA_RUN_TYPE') || run_type,
+ run_type: run_type,
merge_request: merge_request
},
fields: {
fabrication_time: fabrication_time,
info: info,
- job_url: QA::Runtime::Env.ci_job_url,
+ job_url: ci_job_url,
timestamp: timestamp
}
}
@@ -155,7 +164,7 @@ module QA
#
# @return [String]
def job_name
- @job_name ||= QA::Runtime::Env.ci_job_name&.gsub(%r{ \d{1,2}/\d{1,2}}, '')
+ @job_name ||= ci_job_name&.gsub(%r{ \d{1,2}/\d{1,2}}, '')
end
# Single common timestamp for all exported example metrics to keep data points consistently grouped
@@ -220,6 +229,40 @@ module QA
metadata[:retry_attempts] || 0
end
+ # Additional custom metrics tags
+ #
+ # @param [Hash] metadata
+ # @return [Hash]
+ def custom_metrics_tags(metadata)
+ custom_metrics(metadata, :tags)
+ end
+
+ # Additional custom metrics fields
+ #
+ # @param [Hash] metadata
+ # @return [Hash]
+ def custom_metrics_fields(metadata)
+ custom_metrics(metadata, :fields)
+ end
+
+ # Custom test metrics
+ #
+ # @param [Hash] metadata
+ # @param [Symbol] type type of metric, :fields or :tags
+ # @return [Hash]
+ def custom_metrics(metadata, type)
+ custom_metrics = metadata[CUSTOM_METRICS_KEY]
+ return {} unless custom_metrics
+ return {} unless custom_metrics.is_a?(Hash) && custom_metrics[type].is_a?(Hash)
+
+ custom_metrics[type].to_h do |key, value|
+ k = key.to_sym
+ v = value.is_a?(Numeric) || value.nil? ? value : value.to_s
+
+ [k, v]
+ end
+ end
+
# Print log message
#
# @param [Symbol] level
diff --git a/qa/qa/support/helpers/mask_token.rb b/qa/qa/support/helpers/mask_token.rb
index 0c0af524c97..3aea77779ad 100644
--- a/qa/qa/support/helpers/mask_token.rb
+++ b/qa/qa/support/helpers/mask_token.rb
@@ -9,9 +9,8 @@ module QA
ci_variable.project = project
ci_variable.key = name
ci_variable.value = value
- ci_variable.protected = true
end
- "$#{name}"
+ "${#{name}}"
end
def use_group_ci_variable(name:, value:, group:)
@@ -19,9 +18,8 @@ module QA
ci_variable.group = group
ci_variable.key = name
ci_variable.value = value
- ci_variable.protected = true
end
- "$#{name}"
+ "${#{name}}"
end
end
end
diff --git a/qa/qa/support/influxdb_tools.rb b/qa/qa/support/influxdb_tools.rb
index e817b096864..efdbe1cd129 100644
--- a/qa/qa/support/influxdb_tools.rb
+++ b/qa/qa/support/influxdb_tools.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require "active_support/core_ext/module/delegation"
-
module QA
module Support
# Common tools for use with influxdb metrics setup
diff --git a/qa/qa/support/knapsack_report.rb b/qa/qa/support/knapsack_report.rb
index 659b8f10e0a..27d8b144f3b 100644
--- a/qa/qa/support/knapsack_report.rb
+++ b/qa/qa/support/knapsack_report.rb
@@ -5,13 +5,13 @@ require "fog/google"
module QA
module Support
class KnapsackReport
- extend SingleForwardable
-
PROJECT = "gitlab-qa-resources"
BUCKET = "knapsack-reports"
FALLBACK_REPORT = "knapsack/master_report.json"
- def_delegators :new, :configure!, :move_regenerated_report, :download_report, :upload_report
+ class << self
+ delegate :configure!, :move_regenerated_report, :download_report, :upload_report, to: :new
+ end
def initialize(report_name = nil)
@report_name = report_name
diff --git a/qa/qa/support/loglinking.rb b/qa/qa/support/loglinking.rb
index f24577ff313..5a1aad3c7eb 100644
--- a/qa/qa/support/loglinking.rb
+++ b/qa/qa/support/loglinking.rb
@@ -1,4 +1,7 @@
# frozen_string_literal: true
+
+require 'active_support/core_ext/integer/time'
+
module QA
module Support
module Loglinking
@@ -7,63 +10,90 @@ module QA
STAGING_REF_ADDRESS = 'https://staging-ref.gitlab.com'
PRODUCTION_ADDRESS = 'https://gitlab.com'
PRE_PROD_ADDRESS = 'https://pre.gitlab.com'
- SENTRY_ENVIRONMENTS = {
+ SENTRY_BASE_URLS = {
staging: 'https://sentry.gitlab.net/gitlab/staginggitlabcom/?environment=gstg',
staging_ref: 'https://sentry.gitlab.net/gitlab/staging-ref/?environment=all',
pre: 'https://sentry.gitlab.net/gitlab/pregitlabcom/?environment=all',
production: 'https://sentry.gitlab.net/gitlab/gitlabcom/?environment=gprd'
}.freeze
- KIBANA_ENVIRONMENTS = {
+ KIBANA_BASE_URLS = {
staging: 'https://nonprod-log.gitlab.net/',
- canary: 'https://log.gprd.gitlab.net/',
- production: 'https://log.gprd.gitlab.net/'
+ production: 'https://log.gprd.gitlab.net/',
+ pre: 'https://nonprod-log.gitlab.net/'
+ }.freeze
+ KIBANA_INDICES = {
+ staging: 'ed942d00-5186-11ea-ad8a-f3610a492295',
+ production: '7092c4e2-4eb5-46f2-8305-a7da2edad090',
+ pre: 'pubsub-rails-inf-pre'
}.freeze
- def self.failure_metadata(correlation_id)
- return if correlation_id.blank?
+ class << self
+ def failure_metadata(correlation_id)
+ return if correlation_id.blank?
- sentry_uri = sentry_url
- kibana_uri = kibana_url
+ errors = ["Correlation Id: #{correlation_id}"]
- errors = ["Correlation Id: #{correlation_id}"]
- errors << "Sentry Url: #{sentry_uri}&query=correlation_id%3A%22#{correlation_id}%22" if sentry_uri
- errors << "Kibana Url: #{kibana_uri}app/discover#/?_a=%28query%3A%28language%3Akuery%2Cquery%3A%27json.correlation_id%20%3A%20#{correlation_id}%27%29%29&_g=%28time%3A%28from%3Anow-24h%2Cto%3Anow%29%29" if kibana_uri
+ env = get_logging_environment
- errors.join("\n")
- end
+ unless env.nil?
+ sentry_base_url = get_sentry_base_url(env)
+ kibana_base_url = get_kibana_base_url(env)
+ kibana_index = get_kibana_index(env)
- def self.sentry_url
- return unless logging_environment?
+ errors << "Sentry Url: #{get_sentry_url(sentry_base_url, correlation_id)}" if sentry_base_url
+ errors << "Kibana Url: #{get_kibana_url(kibana_base_url, kibana_index, correlation_id)}" if kibana_base_url
+ end
- SENTRY_ENVIRONMENTS[logging_environment]
- end
+ errors.join("\n")
+ end
- def self.kibana_url
- return unless logging_environment?
+ def get_sentry_base_url(env)
+ SENTRY_BASE_URLS[env]
+ end
- KIBANA_ENVIRONMENTS[logging_environment]
- end
+ def get_sentry_url(base_url, correlation_id)
+ "#{base_url}&query=correlation_id%3A%22#{correlation_id}%22"
+ end
- def self.logging_environment
- address = QA::Runtime::Scenario.attributes[:gitlab_address]
- return if address.nil?
-
- case address
- when STAGING_ADDRESS
- :staging
- when STAGING_REF_ADDRESS
- :staging_ref
- when PRODUCTION_ADDRESS
- :production
- when PRE_PROD_ADDRESS
- :pre
- else
- nil
+ def get_kibana_base_url(env)
+ KIBANA_BASE_URLS[env]
end
- end
- def self.logging_environment?
- !logging_environment.nil?
+ def get_kibana_index(env)
+ KIBANA_INDICES[env]
+ end
+
+ def get_kibana_url(base_url, index, correlation_id)
+ "#{base_url}app/discover#/?_a=%28index:%27#{index}%27%2Cquery%3A%28language%3Akuery%2C" \
+ "query%3A%27json.correlation_id%20%3A%20#{correlation_id}%27%29%29" \
+ "&_g=%28time%3A%28from%3A%27#{start_time}%27%2Cto%3A%27#{end_time}%27%29%29"
+ end
+
+ def get_logging_environment
+ address = QA::Runtime::Scenario.attributes[:gitlab_address]
+ return if address.nil?
+
+ case address
+ when STAGING_ADDRESS
+ :staging
+ when STAGING_REF_ADDRESS
+ :staging_ref
+ when PRODUCTION_ADDRESS
+ :production
+ when PRE_PROD_ADDRESS
+ :pre
+ else
+ nil
+ end
+ end
+
+ def start_time
+ (Time.now.utc - 24.hours).iso8601(3)
+ end
+
+ def end_time
+ Time.now.utc.iso8601(3)
+ end
end
end
end
diff --git a/qa/qa/support/page/logging.rb b/qa/qa/support/page/logging.rb
index 79ea4a8d001..2e97325aff0 100644
--- a/qa/qa/support/page/logging.rb
+++ b/qa/qa/support/page/logging.rb
@@ -79,9 +79,12 @@ module QA
super
end
+ # @param name [Symbol] name of the data_qa_selector element
+ # @param page [Class] a target page class to check existence of (class must inherit from QA::Page::Base)
+ # @param kwargs [Hash] keyword arguments to pass to Capybara finder
def click_element(name, page = nil, **kwargs)
msg = ["clicking :#{highlight_element(name)}"]
- msg << ", expecting to be at #{page.class}" if page
+ msg << "and ensuring #{page} is present" if page
log(msg.join(' '), :info)
log("with args #{kwargs}")
diff --git a/qa/qa/support/page_error_checker.rb b/qa/qa/support/page_error_checker.rb
index acba25643ae..1d791a83037 100644
--- a/qa/qa/support/page_error_checker.rb
+++ b/qa/qa/support/page_error_checker.rb
@@ -88,8 +88,8 @@ module QA
grouped_errors = group_errors(cache['errors'])
- errors = grouped_errors.map do |error_metadata, request_id_string|
- "#{error_metadata} -- #{request_id_string}"
+ errors = grouped_errors.map do |error_metadata, error_body|
+ "#{error_metadata} -- #{error_body[:request_id_string]}\n#{error_body[:error_body]}"
end
QA::Runtime::Logger.error "Interceptor Api Errors\n#{errors.join("\n")}" unless errors.nil? || errors.empty?
@@ -107,7 +107,7 @@ module QA
end
def logs(page)
- page.driver.browser.manage.logs.get(:browser)
+ page.driver.browser.logs.get(:browser)
end
private
@@ -120,7 +120,11 @@ module QA
errors.each_with_object({}) do |error, memo|
url = error['url']&.split('?')&.first || 'Unknown url'
key = "[#{error['status']}] #{error['method']} #{url}"
- memo[key] = "Correlation Id: #{error.dig('headers', 'x-request-id') || 'Correlation Id not found'}"
+ request_id_string = "Correlation Id: #{error.dig('headers', 'x-request-id') || 'Correlation Id not found'}"
+ memo[key] = {
+ request_id_string: request_id_string,
+ error_body: error['errorData']
+ }
end
end
end
diff --git a/qa/qa/support/run.rb b/qa/qa/support/run.rb
index 242293f9eef..da82c09462d 100644
--- a/qa/qa/support/run.rb
+++ b/qa/qa/support/run.rb
@@ -13,7 +13,7 @@ module QA
alias_method :to_s, :response
def success?
- exitstatus == 0 && !response.include?('Error encountered')
+ exitstatus == 0 && !response.include?('Error encountered') # rubocop:disable Rails/NegateInclude
end
def to_i
@@ -45,3 +45,5 @@ module QA
end
end
end
+
+QA::Support::Run.prepend_mod_with("Support::Run", namespace: QA)
diff --git a/qa/qa/support/ssh.rb b/qa/qa/support/ssh.rb
index 1b53244d1e4..eebe5e65504 100644
--- a/qa/qa/support/ssh.rb
+++ b/qa/qa/support/ssh.rb
@@ -70,3 +70,5 @@ module QA
end
end
end
+
+QA::Support::SSH.prepend_mod_with("Support::SSH", namespace: QA)
diff --git a/qa/qa/tools/long_running_spec_reporter.rb b/qa/qa/tools/long_running_spec_reporter.rb
index ce035248baa..865b16f1d41 100644
--- a/qa/qa/tools/long_running_spec_reporter.rb
+++ b/qa/qa/tools/long_running_spec_reporter.rb
@@ -6,15 +6,15 @@ require "slack-notifier"
module QA
module Tools
class LongRunningSpecReporter
- extend SingleForwardable
-
SLACK_CHANNEL = "#quality-reports"
PROJECT = "gitlab-qa-resources"
BUCKET = "knapsack-reports"
REPORT_NAME = "ee-instance-parallel.json"
RUNTIME_THRESHOLD = 300
- def_delegator :new, :execute
+ class << self
+ delegate :execute, to: :new
+ end
# Find and report specs exceeding runtime threshold
#
diff --git a/qa/qa/tools/reliable_report.rb b/qa/qa/tools/reliable_report.rb
index b3df6de3d54..fd39b637f83 100644
--- a/qa/qa/tools/reliable_report.rb
+++ b/qa/qa/tools/reliable_report.rb
@@ -326,7 +326,7 @@ module QA
def test_runs(reliable:)
puts("Fetching data on #{reliable ? 'reliable ' : ''}test execution for past #{range} days\n".colorize(:green))
- all_runs = query_api.query(query: query(reliable)).values
+ all_runs = query_api.query(query: query(reliable))
all_runs.each_with_object(Hash.new { |hsh, key| hsh[key] = {} }) do |table, result|
records = table.records.sort_by { |record| record.values["_time"] }
# skip specs that executed less time than defined by range or stopped executing before report date
@@ -341,7 +341,7 @@ module QA
runs = records.count
failed = records.count { |r| r.values["status"] == "failed" }
- failure_rate = (failed.to_f / runs.to_f) * 100
+ failure_rate = (failed.to_f / runs) * 100
result[stage][name] = {
file: file,
@@ -358,7 +358,7 @@ module QA
# @return [String]
def query(reliable)
<<~QUERY
- from(bucket: "#{Support::InfluxdbTools::INFLUX_TEST_METRICS_BUCKET}")
+ from(bucket: "#{Support::InfluxdbTools::INFLUX_MAIN_TEST_METRICS_BUCKET}")
|> range(start: -#{range}d)
|> filter(fn: (r) => r._measurement == "test-stats")
|> filter(fn: (r) => r.run_type == "staging-full" or
diff --git a/qa/qa/tools/test_resources_handler.rb b/qa/qa/tools/test_resources_handler.rb
index 068fe37a37b..2aa8845605e 100644
--- a/qa/qa/tools/test_resources_handler.rb
+++ b/qa/qa/tools/test_resources_handler.rb
@@ -60,7 +60,9 @@ module QA
end
delete_resources(filtered_resources)
- delete_groups_permanently(filtered_resources['QA::Resource::Group'])
+
+ filtered_groups = filtered_resources['QA::Resource::Group']
+ delete_groups_permanently(filtered_groups) unless filtered_groups.nil?
end
return puts "\nDone" if failures.empty?
diff --git a/qa/qa/vendor/smocker/event_payload.rb b/qa/qa/vendor/smocker/event_payload.rb
index 4bf154b76c2..e7287c741ce 100644
--- a/qa/qa/vendor/smocker/event_payload.rb
+++ b/qa/qa/vendor/smocker/event_payload.rb
@@ -16,6 +16,10 @@ module QA
raw[:object_kind]&.to_sym
end
+ def event_name
+ raw[:event_name]&.to_sym
+ end
+
def project_name
raw.dig(:project, :name)
end
@@ -43,6 +47,14 @@ module QA
def wiki?
event == :wiki_page
end
+
+ def subgroup_create?
+ event_name == :subgroup_create
+ end
+
+ def subgroup_destroy?
+ event_name == :subgroup_destroy
+ end
end
end
end
diff --git a/qa/qa/vendor/smocker/smocker_api.rb b/qa/qa/vendor/smocker/smocker_api.rb
index 3f595b58886..230656776b7 100644
--- a/qa/qa/vendor/smocker/smocker_api.rb
+++ b/qa/qa/vendor/smocker/smocker_api.rb
@@ -31,6 +31,7 @@ module QA
def self.teardown!
@container&.remove!
+ @container = nil
end
def initialize(container, **wait_args)
diff --git a/qa/spec/page/logging_spec.rb b/qa/spec/page/logging_spec.rb
index 93a08108787..1a82cda2585 100644
--- a/qa/spec/page/logging_spec.rb
+++ b/qa/spec/page/logging_spec.rb
@@ -5,6 +5,7 @@ require 'capybara/dsl'
RSpec.describe QA::Support::Page::Logging do
let(:page) { double.as_null_object }
let(:logger) { Gitlab::QA::TestLogger.logger(level: ::Logger::DEBUG, source: 'QA Tests') }
+ let(:page_class) { class_double('QA::Page::TestPage') }
before do
allow(QA::Runtime::Logger).to receive(:logger).and_return(logger)
@@ -66,6 +67,14 @@ RSpec.describe QA::Support::Page::Logging do
.to output(/clicking :element/).to_stdout_from_any_process
end
+ it 'logs click_element with a page' do
+ allow(page_class).to receive(:validate_elements_present!).and_return(true)
+ allow(page_class).to receive(:to_s).and_return('QA::Page::TestPage')
+
+ expect { subject.click_element(:element, page_class) }
+ .to output(/clicking :element and ensuring QA::Page::TestPage is present/).to_stdout_from_any_process
+ end
+
it 'logs fill_element' do
expect { subject.fill_element(:element, 'foo') }
.to output(/filling :element with "foo"/).to_stdout_from_any_process
diff --git a/qa/spec/resource/api_fabricator_spec.rb b/qa/spec/resource/api_fabricator_spec.rb
index 4cb6ef3c9b5..76cc8e0e303 100644
--- a/qa/spec/resource/api_fabricator_spec.rb
+++ b/qa/spec/resource/api_fabricator_spec.rb
@@ -113,7 +113,6 @@ RSpec.describe QA::Resource::ApiFabricator do
it 'raises a ResourceFabricationFailedError exception' do
expect(api_request).to receive(:new).with(api_client_instance, subject.api_post_path).and_return(double(url: resource_web_url))
expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post)
- allow(QA::Support::Loglinking).to receive(:logging_environment).and_return(nil)
expect { subject.fabricate_via_api! }.to raise_error do |error|
expect(error.class).to eql(described_class::ResourceFabricationFailedError)
@@ -126,7 +125,7 @@ RSpec.describe QA::Resource::ApiFabricator do
it 'logs a correlation id' do
response = double('Raw POST response', code: 400, body: post_response.to_json, headers: { x_request_id: 'foobar' })
- allow(QA::Support::Loglinking).to receive(:logging_environment).and_return(nil)
+ allow(QA::Support::Loglinking).to receive(:get_logging_environment).and_return(nil)
expect(api_request).to receive(:new).with(api_client_instance, subject.api_post_path).and_return(double(url: resource_web_url))
expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(response)
@@ -140,12 +139,14 @@ RSpec.describe QA::Resource::ApiFabricator do
end
end
- it 'logs a sentry url from staging' do
+ it 'logs Sentry and Kibana URLs from staging' do
response = double('Raw POST response', code: 400, body: post_response.to_json, headers: { x_request_id: 'foobar' })
cookies = [{ name: 'Foo', value: 'Bar' }, { name: 'gitlab_canary', value: 'true' }]
+ time = Time.new(2022, 11, 14, 0, 0, 0, '+00:00')
allow(Capybara.current_session).to receive_message_chain(:driver, :browser, :manage, :all_cookies).and_return(cookies)
allow(QA::Runtime::Scenario).to receive(:attributes).and_return({ gitlab_address: 'https://staging.gitlab.com' })
+ allow(Time).to receive(:now).and_return(time)
expect(api_request).to receive(:new).with(api_client_instance, subject.api_post_path).and_return(double(url: resource_web_url))
expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(response)
@@ -156,7 +157,7 @@ RSpec.describe QA::Resource::ApiFabricator do
Fabrication of FooBarResource using the API failed (400) with `#{raw_post}`.
Correlation Id: foobar
Sentry Url: https://sentry.gitlab.net/gitlab/staginggitlabcom/?environment=gstg&query=correlation_id%3A%22foobar%22
- Kibana Url: https://nonprod-log.gitlab.net/app/discover#/?_a=%28query%3A%28language%3Akuery%2Cquery%3A%27json.correlation_id%20%3A%20foobar%27%29%29&_g=%28time%3A%28from%3Anow-24h%2Cto%3Anow%29%29
+ Kibana Url: https://nonprod-log.gitlab.net/app/discover#/?_a=%28index:%27ed942d00-5186-11ea-ad8a-f3610a492295%27%2Cquery%3A%28language%3Akuery%2Cquery%3A%27json.correlation_id%20%3A%20foobar%27%29%29&_g=%28time%3A%28from%3A%272022-11-13T00:00:00.000Z%27%2Cto%3A%272022-11-14T00:00:00.000Z%27%29%29
ERROR
end
end
diff --git a/qa/spec/resource/base_spec.rb b/qa/spec/resource/base_spec.rb
index 0ec27da7277..e0bfccf5e78 100644
--- a/qa/spec/resource/base_spec.rb
+++ b/qa/spec/resource/base_spec.rb
@@ -94,9 +94,9 @@ RSpec.describe QA::Resource::Base do
end
end
- context 'when FIPS mode is enabled' do
+ context 'when personal_access_tokens_disabled returns true' do
before do
- stub_env('FIPS', '1')
+ stub_env('PERSONAL_ACCESS_TOKENS_DISABLED', true)
end
it 'calls .fabricate_via_browser_ui!' do
@@ -108,7 +108,7 @@ RSpec.describe QA::Resource::Base do
end
describe '.fabricate_via_api_unless_fips!' do
- context 'when FIPS mode is not enabled' do
+ context 'when personal_access_tokens_disabled returns false' do
it 'calls .fabricate_via_api!!' do
expect(described_class).to receive(:fabricate_via_api!)
@@ -116,9 +116,9 @@ RSpec.describe QA::Resource::Base do
end
end
- context 'when FIPS mode is enabled' do
+ context 'when personal_access_tokens_disabled returns true' do
before do
- stub_env('FIPS', '1')
+ stub_env('PERSONAL_ACCESS_TOKENS_DISABLED', true)
end
it 'calls .fabricate_via_browser_ui!' do
diff --git a/qa/spec/resource/user_spec.rb b/qa/spec/resource/user_spec.rb
index d1fc02ff033..547c27dc2ff 100644
--- a/qa/spec/resource/user_spec.rb
+++ b/qa/spec/resource/user_spec.rb
@@ -143,4 +143,27 @@ RSpec.describe QA::Resource::User do
end
end
end
+
+ describe '#fabricate_or_use' do
+ # Signup Disabled, Personal Access Tokens disabled, method used, method that is not used
+ [
+ [true, false, :fabricate_via_api!, :fabricate!],
+ [false, false, :fabricate!, :fabricate_via_api!],
+ [false, true, :fabricate!, :fabricate_via_api!],
+ [true, true, :fabricate!, :fabricate_via_api!]
+ ].each do |signup_disabled, personal_access_tokens_disabled, method_used, method_not_used|
+ it "when signup_disabled is #{signup_disabled}, "\
+ "personal_access_tokens_disabled is #{personal_access_tokens_disabled}, "\
+ "calls #{method_used}, does not call #{method_not_used}" do
+ allow(QA::Runtime::Env).to receive(:signup_disabled?).and_return(signup_disabled)
+ allow(QA::Runtime::Env).to receive(:personal_access_tokens_disabled?)
+ .and_return(personal_access_tokens_disabled)
+
+ expect(described_class).to receive(method_used)
+ expect(described_class).not_to receive(method_not_used)
+
+ described_class.fabricate_or_use
+ end
+ end
+ end
end
diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb
index 61d91da8738..e9c2000681b 100644
--- a/qa/spec/runtime/env_spec.rb
+++ b/qa/spec/runtime/env_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe QA::Runtime::Env do
it_behaves_like 'boolean method with parameter', kwargs
end
- shared_examples 'boolean method with parameter' do |method:, param: nil, env_key:, default:|
+ shared_examples 'boolean method with parameter' do |method:, env_key:, default:, param: nil|
context 'when there is an env variable set' do
it 'returns false when falsey values specified' do
stub_env(env_key, 'false')
diff --git a/qa/spec/support/formatters/test_metrics_formatter_spec.rb b/qa/spec/support/formatters/test_metrics_formatter_spec.rb
index 76bde98cc33..2b8a0791dc5 100644
--- a/qa/spec/support/formatters/test_metrics_formatter_spec.rb
+++ b/qa/spec/support/formatters/test_metrics_formatter_spec.rb
@@ -253,6 +253,27 @@ describe QA::Support::Formatters::TestMetricsFormatter do
end
end
+ context 'with additional custom metrics' do
+ it 'exports data to influxdb with additional metrics' do
+ run_spec do
+ it(
+ 'spec',
+ custom_test_metrics: { tags: { custom_tag: "tag" }, fields: { custom_field: 1 } },
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/1234'
+ ) {}
+ end
+
+ custom_data = data.merge({
+ **data,
+ tags: data[:tags].merge({ custom_tag: "tag" }),
+ fields: data[:fields].merge({ custom_field: 1 })
+ })
+
+ expect(influx_write_api).to have_received(:write).once
+ expect(influx_write_api).to have_received(:write).with(data: [custom_data])
+ end
+ end
+
context 'with fabrication runtimes' do
let(:api_fabrication) { 4 }
let(:ui_fabrication) { 10 }
diff --git a/qa/spec/support/loglinking_spec.rb b/qa/spec/support/loglinking_spec.rb
index 10865068e3d..3955d266ef6 100644
--- a/qa/spec/support/loglinking_spec.rb
+++ b/qa/spec/support/loglinking_spec.rb
@@ -2,40 +2,57 @@
RSpec.describe QA::Support::Loglinking do
describe '.failure_metadata' do
- context 'return nil string' do
- it 'if correlation_id is empty' do
+ context 'when correlation_id does not exist' do
+ it 'returns nil when correlation_id is empty' do
expect(QA::Support::Loglinking.failure_metadata('')).to eq(nil)
end
- it 'if correlation_id is nil' do
+ it 'returns nil when correlation_id is nil' do
expect(QA::Support::Loglinking.failure_metadata(nil)).to eq(nil)
end
end
- context 'return error string' do
- it 'with sentry URL' do
- allow(QA::Support::Loglinking).to receive(:sentry_url).and_return('https://sentry.address/?environment=bar')
- allow(QA::Support::Loglinking).to receive(:kibana_url).and_return(nil)
+ context 'when correlation_id exists' do
+ context 'and logging environment exists' do
+ it 'returns Sentry URL' do
+ allow(QA::Support::Loglinking).to receive(:get_logging_environment).and_return(:foo)
+ allow(QA::Support::Loglinking).to receive(:get_sentry_base_url).and_return('https://sentry.address/?environment=bar')
+ allow(QA::Support::Loglinking).to receive(:get_kibana_base_url).and_return(nil)
+ allow(QA::Support::Loglinking).to receive(:get_kibana_index).and_return(nil)
- expect(QA::Support::Loglinking.failure_metadata('foo123')).to eql(<<~ERROR.chomp)
+ expect(QA::Support::Loglinking.failure_metadata('foo123')).to eql(<<~ERROR.chomp)
Correlation Id: foo123
Sentry Url: https://sentry.address/?environment=bar&query=correlation_id%3A%22foo123%22
- ERROR
- end
+ ERROR
+ end
+
+ it 'returns Kibana URL' do
+ time = Time.new(2022, 11, 14, 0, 0, 0, '+00:00')
- it 'with kibana URL' do
- allow(QA::Support::Loglinking).to receive(:sentry_url).and_return(nil)
- allow(QA::Support::Loglinking).to receive(:kibana_url).and_return('https://kibana.address/')
+ allow(QA::Support::Loglinking).to receive(:get_logging_environment).and_return(:foo)
+ allow(QA::Support::Loglinking).to receive(:get_sentry_base_url).and_return(nil)
+ allow(QA::Support::Loglinking).to receive(:get_kibana_base_url).and_return('https://kibana.address/')
+ allow(QA::Support::Loglinking).to receive(:get_kibana_index).and_return('pubsub-rails-inf-foo')
+ allow(Time).to receive(:now).and_return(time)
- expect(QA::Support::Loglinking.failure_metadata('foo123')).to eql(<<~ERROR.chomp)
+ expect(QA::Support::Loglinking.failure_metadata('foo123')).to eql(<<~ERROR.chomp)
Correlation Id: foo123
- Kibana Url: https://kibana.address/app/discover#/?_a=%28query%3A%28language%3Akuery%2Cquery%3A%27json.correlation_id%20%3A%20foo123%27%29%29&_g=%28time%3A%28from%3Anow-24h%2Cto%3Anow%29%29
- ERROR
+ Kibana Url: https://kibana.address/app/discover#/?_a=%28index:%27pubsub-rails-inf-foo%27%2Cquery%3A%28language%3Akuery%2Cquery%3A%27json.correlation_id%20%3A%20foo123%27%29%29&_g=%28time%3A%28from%3A%272022-11-13T00:00:00.000Z%27%2Cto%3A%272022-11-14T00:00:00.000Z%27%29%29
+ ERROR
+ end
+ end
+
+ context 'and logging environment does not exist' do
+ it 'returns only the correlation ID' do
+ allow(QA::Support::Loglinking).to receive(:get_logging_environment).and_return(nil)
+
+ expect(QA::Support::Loglinking.failure_metadata('foo123')).to eql('Correlation Id: foo123')
+ end
end
end
end
- describe '.sentry_url' do
+ describe '.get_sentry_base_url' do
let(:url_hash) do
{
:staging => 'https://sentry.gitlab.net/gitlab/staginggitlabcom/?environment=gstg',
@@ -47,36 +64,52 @@ RSpec.describe QA::Support::Loglinking do
}
end
- it 'returns sentry URL if environment found' do
+ it 'returns Sentry base URL based on environment' do
url_hash.each do |environment, url|
- allow(QA::Support::Loglinking).to receive(:logging_environment).and_return(environment)
-
- expect(QA::Support::Loglinking.sentry_url).to eq(url)
+ expect(QA::Support::Loglinking.get_sentry_base_url(environment)).to eq(url)
end
end
end
- describe '.kibana_url' do
+ describe '.get_kibana_base_url' do
let(:url_hash) do
{
:staging => 'https://nonprod-log.gitlab.net/',
:staging_ref => nil,
:production => 'https://log.gprd.gitlab.net/',
+ :pre => 'https://nonprod-log.gitlab.net/',
:foo => nil,
nil => nil
}
end
- it 'returns kibana URL if environment found' do
+ it 'returns Kibana URL based on environment' do
url_hash.each do |environment, url|
- allow(QA::Support::Loglinking).to receive(:logging_environment).and_return(environment)
+ expect(QA::Support::Loglinking.get_kibana_base_url(environment)).to eq(url)
+ end
+ end
+ end
+
+ describe '.get_kibana_index' do
+ let(:index_hash) do
+ {
+ :staging => 'ed942d00-5186-11ea-ad8a-f3610a492295',
+ :staging_ref => nil,
+ :production => '7092c4e2-4eb5-46f2-8305-a7da2edad090',
+ :pre => 'pubsub-rails-inf-pre',
+ :foo => nil,
+ nil => nil
+ }
+ end
- expect(QA::Support::Loglinking.kibana_url).to eq(url)
+ it 'returns Kibana index based on environment' do
+ index_hash.each do |environment, index|
+ expect(QA::Support::Loglinking.get_kibana_index(environment)).to eq(index)
end
end
end
- describe '.logging_environment' do
+ describe '.get_logging_environment' do
let(:staging_address) { 'https://staging.gitlab.com' }
let(:staging_ref_address) { 'https://staging-ref.gitlab.com' }
let(:production_address) { 'https://gitlab.com' }
@@ -110,23 +143,7 @@ RSpec.describe QA::Support::Loglinking do
logging_env_array.each do |logging_env_hash|
allow(QA::Runtime::Scenario).to receive(:attributes).and_return({ gitlab_address: logging_env_hash[:address] })
- expect(QA::Support::Loglinking.logging_environment).to eq(logging_env_hash[:expected_env])
- end
- end
- end
-
- describe '.logging_environment?' do
- context 'returns boolean' do
- it 'returns true if logging_environment is not nil' do
- allow(QA::Support::Loglinking).to receive(:logging_environment).and_return(:staging)
-
- expect(QA::Support::Loglinking.logging_environment?).to eq(true)
- end
-
- it 'returns false if logging_environment is nil' do
- allow(QA::Support::Loglinking).to receive(:logging_environment).and_return(nil)
-
- expect(QA::Support::Loglinking.logging_environment?).to eq(false)
+ expect(QA::Support::Loglinking.get_logging_environment).to eq(logging_env_hash[:expected_env])
end
end
end
diff --git a/qa/spec/support/page_error_checker_spec.rb b/qa/spec/support/page_error_checker_spec.rb
index 735c0f83ecd..5ccbe869dfd 100644
--- a/qa/spec/support/page_error_checker_spec.rb
+++ b/qa/spec/support/page_error_checker_spec.rb
@@ -298,7 +298,7 @@ RSpec.describe QA::Support::PageErrorChecker do
expect(page).to receive(:execute_script)
expect(QA::Runtime::Logger).to receive(:debug).with("Fetching API error cache for #{page_url}")
- expect(QA::Runtime::Logger).to receive(:error).with(<<~ERROR.chomp)
+ expect(QA::Runtime::Logger).to receive(:error).with(<<~ERROR)
Interceptor Api Errors
[500] GET https://foo.bar -- Correlation Id: 12345
ERROR
@@ -318,7 +318,7 @@ RSpec.describe QA::Support::PageErrorChecker do
expect(page).to receive(:execute_script)
expect(QA::Runtime::Logger).to receive(:debug).with("Fetching API error cache for #{page_url}")
- expect(QA::Runtime::Logger).to receive(:error).with(<<~ERROR.chomp).exactly(1).time
+ expect(QA::Runtime::Logger).to receive(:error).with(<<~ERROR).exactly(1).time
Interceptor Api Errors
[500] GET https://foo.bar -- Correlation Id: 12345
ERROR
@@ -338,7 +338,7 @@ RSpec.describe QA::Support::PageErrorChecker do
expect(page).to receive(:execute_script)
expect(QA::Runtime::Logger).to receive(:debug).with("Fetching API error cache for #{page_url}")
- expect(QA::Runtime::Logger).to receive(:error).with(<<~ERROR.chomp)
+ expect(QA::Runtime::Logger).to receive(:error).with(<<~ERROR)
Interceptor Api Errors
[500] GET https://foo.bar -- Correlation Id: 12345
ERROR
@@ -346,6 +346,28 @@ RSpec.describe QA::Support::PageErrorChecker do
QA::Support::PageErrorChecker.log_request_errors(page)
end
+ it 'logs graphql errors if any exist' do
+ error = {
+ 'url' => 'https://foo.bar?query={ sensitive-data: 12345 }',
+ 'status' => 200,
+ 'method' => 'POST',
+ 'errorData' => 'error-messages: Something bad happened',
+ 'headers' => { 'x-request-id' => '12345' }
+ }
+ expect(page).to receive(:driver).and_return(driver)
+ expect(page).to receive(:execute_script).and_return({ 'errors' => [error] })
+ expect(page).to receive(:execute_script)
+
+ expect(QA::Runtime::Logger).to receive(:debug).with("Fetching API error cache for #{page_url}")
+ expect(QA::Runtime::Logger).to receive(:error).with(<<~ERROR.chomp)
+ Interceptor Api Errors
+ [200] POST https://foo.bar -- Correlation Id: 12345
+ error-messages: Something bad happened
+ ERROR
+
+ QA::Support::PageErrorChecker.log_request_errors(page)
+ end
+
it 'returns if cache is nil' do
expect(page).to receive(:driver).and_return(driver)
expect(page).to receive(:execute_script).and_return(nil)
@@ -365,17 +387,11 @@ RSpec.describe QA::Support::PageErrorChecker do
end
end
stub_const('Logs', logs_class)
- manage_class = Class.new do
+ browser_class = Class.new do
def self.logs
Logs
end
end
- stub_const('Manage', manage_class)
- browser_class = Class.new do
- def self.manage
- Manage
- end
- end
stub_const('Browser', browser_class)
driver_class = Class.new do
def self.browser
diff --git a/qa/spec/tools/reliable_report_spec.rb b/qa/spec/tools/reliable_report_spec.rb
index f08af8a717a..9786d247d5e 100644
--- a/qa/spec/tools/reliable_report_spec.rb
+++ b/qa/spec/tools/reliable_report_spec.rb
@@ -22,8 +22,8 @@ describe QA::Tools::ReliableReport do
"stage" => "manage",
"_time" => time
}
- {
- 0 => instance_double(
+ [
+ instance_double(
"InfluxDB2::FluxTable",
records: [
instance_double("InfluxDB2::FluxRecord", values: values),
@@ -31,7 +31,7 @@ describe QA::Tools::ReliableReport do
instance_double("InfluxDB2::FluxRecord", values: values.merge({ "_time" => Time.now.to_s }))
]
)
- }
+ ]
end
let(:reliable_runs) do
@@ -42,8 +42,8 @@ describe QA::Tools::ReliableReport do
"stage" => "create",
"_time" => time
}
- {
- 0 => instance_double(
+ [
+ instance_double(
"InfluxDB2::FluxTable",
records: [
instance_double("InfluxDB2::FluxRecord", values: { **values, "status" => "passed" }),
@@ -51,12 +51,12 @@ describe QA::Tools::ReliableReport do
instance_double("InfluxDB2::FluxRecord", values: values.merge({ "_time" => Time.now.to_s }))
]
)
- }
+ ]
end
def flux_query(reliable:)
<<~QUERY
- from(bucket: "e2e-test-stats")
+ from(bucket: "e2e-test-stats-main")
|> range(start: -#{range}d)
|> filter(fn: (r) => r._measurement == "test-stats")
|> filter(fn: (r) => r.run_type == "staging-full" or
diff --git a/results.txt b/results.txt
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/results.txt
+++ /dev/null
diff --git a/rubocop/cop/database/multiple_databases.rb b/rubocop/cop/database/multiple_databases.rb
index 33ff8acd4d8..26beecdeba7 100644
--- a/rubocop/cop/database/multiple_databases.rb
+++ b/rubocop/cop/database/multiple_databases.rb
@@ -20,6 +20,7 @@ module RuboCop
ALLOWED_METHODS = %i[
no_touching
configurations
+ logger
].freeze
def_node_matcher :active_record_base_method_is_used?, <<~PATTERN
diff --git a/rubocop/cop/feature_flag_usage.rb b/rubocop/cop/feature_flag_usage.rb
new file mode 100644
index 00000000000..60e82633466
--- /dev/null
+++ b/rubocop/cop/feature_flag_usage.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module Cop
+ class FeatureFlagUsage < RuboCop::Cop::Base
+ MSG = 'Do not use Feature Flags for monkey-patches or Redis code. Use environment variables instead.'
+
+ def_node_matcher :using_feature_flag?, <<~PATTERN
+ (send (const {nil? | cbase} :Feature) {:enabled? | :disabled?} ...)
+ PATTERN
+
+ def on_send(node)
+ return unless using_feature_flag?(node)
+
+ add_offense(node, message: MSG)
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/gitlab/strong_memoize_attr.rb b/rubocop/cop/gitlab/strong_memoize_attr.rb
new file mode 100644
index 00000000000..c98aa4765fa
--- /dev/null
+++ b/rubocop/cop/gitlab/strong_memoize_attr.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module Cop
+ module Gitlab
+ # Prefer using `.strong_memoize_attr()` over `#strong_memoize()`. See
+ # https://docs.gitlab.com/ee/development/utilities.html/#strongmemoize.
+ #
+ # Good:
+ #
+ # def memoized_method
+ # 'This is a memoized method'
+ # end
+ # strong_memoize_attr :memoized_method
+ #
+ # Bad, can be autocorrected:
+ #
+ # def memoized_method
+ # strong_memoize(:memoized_method) do
+ # 'This is a memoized method'
+ # end
+ # end
+ #
+ # Very bad, can't be autocorrected:
+ #
+ # def memoized_method
+ # return unless enabled?
+ #
+ # strong_memoize(:memoized_method) do
+ # 'This is a memoized method'
+ # end
+ # end
+ #
+ class StrongMemoizeAttr < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
+ MSG = 'Use `strong_memoize_attr`, instead of using `strong_memoize` directly'
+
+ def_node_matcher :strong_memoize?, <<~PATTERN
+ (block
+ $(send nil? :strong_memoize
+ (sym $_)
+ )
+ (args)
+ $_
+ )
+ PATTERN
+
+ def on_block(node)
+ send_node, attr_name, body = strong_memoize?(node)
+ return unless send_node
+
+ corrector = autocorrect_pure_definitions(node.parent, attr_name, body) if node.parent.def_type?
+
+ add_offense(send_node, &corrector)
+ end
+
+ private
+
+ def autocorrect_pure_definitions(def_node, attr_name, body)
+ proc do |corrector|
+ method_name = def_node.method_name
+ attr_suffix = ", :#{attr_name}" if attr_name != method_name
+ replacement = "\n#{indent(def_node)}strong_memoize_attr :#{method_name}#{attr_suffix}"
+
+ corrector.insert_after(def_node, replacement)
+ corrector.replace(def_node.body, body.source)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/graphql/descriptions.rb b/rubocop/cop/graphql/descriptions.rb
index 3c945507699..239f5b966a4 100644
--- a/rubocop/cop/graphql/descriptions.rb
+++ b/rubocop/cop/graphql/descriptions.rb
@@ -3,6 +3,11 @@
# This cop checks for missing GraphQL descriptions and enforces the description style guide:
# https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#description-style-guide
#
+# @safety
+# This cop is unsafe because not all cases of "this" can be substituted with
+# "the". This will require a technical writer to assist with the alternative,
+# proper grammar that can be used for that particular GraphQL descriptions.
+#
# @examples
#
# # bad
@@ -49,6 +54,8 @@ module RuboCop
MSG_NO_DESCRIPTION = "Please add a `description` property. #{MSG_STYLE_GUIDE_LINK}"
MSG_NO_PERIOD = "`description` strings must end with a `.`. #{MSG_STYLE_GUIDE_LINK}"
MSG_BAD_START = "`description` strings should not start with \"A...\" or \"The...\". #{MSG_STYLE_GUIDE_LINK}"
+ MSG_CONTAINS_THIS = "`description` strings should not contain the demonstrative \"this\"."\
+ " #{MSG_STYLE_GUIDE_LINK}"
def_node_matcher :graphql_describable?, <<~PATTERN
(send nil? {:field :argument :value} ...)
@@ -82,15 +89,17 @@ module RuboCop
MSG_NO_PERIOD
elsif bad_start?(description)
MSG_BAD_START
+ elsif contains_demonstrative_this?(description)
+ MSG_CONTAINS_THIS
end
return unless message
add_offense(node, message: message) do |corrector|
- description = locate_description(node)
next unless description
- corrector.insert_after(before_end_quote(description), '.')
+ corrector.insert_after(before_end_quote(description), '.') if no_period?(description)
+ corrector.replace(locate_this(description), 'the') if contains_demonstrative_this?(description)
end
end
@@ -114,6 +123,10 @@ module RuboCop
string?(description) && description.value.strip.downcase.start_with?('a ', 'the ')
end
+ def contains_demonstrative_this?(description)
+ string?(description) && /\bthis\b/.match?(description.value.strip)
+ end
+
# Returns true if `description` node is a `:str` (as opposed to a `#copy_field_description` call)
def string?(description)
description.type == :str
@@ -127,6 +140,14 @@ module RuboCop
adjust = heredoc_source.index(/\s+\Z/) - heredoc_source.length
string.location.heredoc_body.adjust(end_pos: adjust)
end
+
+ # Returns a `Parser::Source::Range` of the first `this` encountered
+ def locate_this(string)
+ target = 'this'
+ range = string.heredoc? ? string.location.heredoc_body : string.location.expression
+ index = range.source.index(target)
+ range.adjust(begin_pos: index, end_pos: (index + target.length) - range.length)
+ end
end
end
end
diff --git a/rubocop/cop/migration/add_column_with_default.rb b/rubocop/cop/migration/add_column_with_default.rb
deleted file mode 100644
index 36603e09263..00000000000
--- a/rubocop/cop/migration/add_column_with_default.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../../migration_helpers'
-
-module RuboCop
- module Cop
- module Migration
- class AddColumnWithDefault < RuboCop::Cop::Base
- include MigrationHelpers
-
- MSG = '`add_column_with_default` is deprecated, use `add_column` instead'
-
- def on_send(node)
- return unless in_migration?(node)
-
- name = node.children[1]
-
- add_offense(node.loc.selector) if name == :add_column_with_default
- end
- end
- end
- end
-end
diff --git a/rubocop/cop/migration/add_limit_to_text_columns.rb b/rubocop/cop/migration/add_limit_to_text_columns.rb
index 5c71fbbfaa2..2d3b5d5094f 100644
--- a/rubocop/cop/migration/add_limit_to_text_columns.rb
+++ b/rubocop/cop/migration/add_limit_to_text_columns.rb
@@ -44,11 +44,9 @@ module RuboCop
if text_operation_with_limit?(send_node)
add_offense(send_node.loc.selector, message: TEXT_LIMIT_ATTRIBUTE_NOT_ALLOWED) if version(node) < TEXT_LIMIT_ATTRIBUTE_ALLOWED_SINCE
- else
+ elsif text_limit_missing?(node, *table_and_attribute_name(send_node))
# We require a limit for the same table and attribute name
- if text_limit_missing?(node, *table_and_attribute_name(send_node))
- add_offense(send_node.loc.selector)
- end
+ add_offense(send_node.loc.selector)
end
end
end
diff --git a/rubocop/cop/migration/batch_migrations_post_only.rb b/rubocop/cop/migration/batch_migrations_post_only.rb
new file mode 100644
index 00000000000..3a1f3b64d75
--- /dev/null
+++ b/rubocop/cop/migration/batch_migrations_post_only.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require_relative '../../migration_helpers'
+
+module RuboCop
+ module Cop
+ module Migration
+ # Cop that checks that no background batched migration helpers are called by regular migrations.
+ class BatchMigrationsPostOnly < RuboCop::Cop::Base
+ include MigrationHelpers
+
+ MSG = "This method must only be used in post-deployment migrations."
+
+ FORBIDDEN_METHODS = %w[
+ ensure_batched_background_migration_is_finished
+ queue_batched_background_migration
+ delete_batched_background_migration
+ finalize_batched_background_migration
+ ].freeze
+
+ SYMBOLIZED_MATCHER = FORBIDDEN_METHODS.map { |w| ":#{w}" }.join(' | ')
+
+ def_node_matcher :on_forbidden_method, <<~PATTERN
+ (send nil? {#{SYMBOLIZED_MATCHER}} ...)
+ PATTERN
+
+ def on_send(node)
+ on_forbidden_method(node) do
+ break if in_post_deployment_migration?(node)
+
+ add_offense(node, message: MSG)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/migration/safer_boolean_column.rb b/rubocop/cop/migration/safer_boolean_column.rb
index d3d77b16357..c5df21dc94a 100644
--- a/rubocop/cop/migration/safer_boolean_column.rb
+++ b/rubocop/cop/migration/safer_boolean_column.rb
@@ -21,9 +21,9 @@ module RuboCop
class SaferBooleanColumn < RuboCop::Cop::Base
include MigrationHelpers
- DEFAULT_OFFENSE = 'Boolean columns on the `%s` table should have a default. You may wish to use `add_column_with_default`.'
+ DEFAULT_OFFENSE = 'Boolean columns on the `%s` table should have a default.'
NULL_OFFENSE = 'Boolean columns on the `%s` table should disallow nulls.'
- DEFAULT_AND_NULL_OFFENSE = 'Boolean columns on the `%s` table should have a default and should disallow nulls. You may wish to use `add_column_with_default`.'
+ DEFAULT_AND_NULL_OFFENSE = 'Boolean columns on the `%s` table should have a default and should disallow nulls.'
def_node_matcher :add_column?, <<~PATTERN
(send nil? :add_column $...)
diff --git a/rubocop/cop/migration/versioned_migration_class.rb b/rubocop/cop/migration/versioned_migration_class.rb
index 648782f1735..572ddcd1b12 100644
--- a/rubocop/cop/migration/versioned_migration_class.rb
+++ b/rubocop/cop/migration/versioned_migration_class.rb
@@ -9,9 +9,10 @@ module RuboCop
include MigrationHelpers
ENFORCED_SINCE = 2021_09_02_00_00_00
+ CURRENT_DATABASE_MIGRATION_CLASS = 'Gitlab::Database::Migration[2.1]'
- MSG_INHERIT = 'Don\'t inherit from ActiveRecord::Migration but use Gitlab::Database::Migration[1.0] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.'
- MSG_INCLUDE = 'Don\'t include migration helper modules directly. Inherit from Gitlab::Database::Migration[1.0] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.'
+ MSG_INHERIT = 'Don\'t inherit from ActiveRecord::Migration but use Gitlab::Database::Migration[2.1] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.'
+ MSG_INCLUDE = 'Don\'t include migration helper modules directly. Inherit from Gitlab::Database::Migration[2.1] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.'
ACTIVERECORD_MIGRATION_CLASS = 'ActiveRecord::Migration'
diff --git a/rubocop/cop/rspec/avoid_test_prof.rb b/rubocop/cop/rspec/avoid_test_prof.rb
new file mode 100644
index 00000000000..fb4b162f125
--- /dev/null
+++ b/rubocop/cop/rspec/avoid_test_prof.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'rubocop-rspec'
+
+module RuboCop
+ module Cop
+ module RSpec
+ # This cop checks for the usage of TestProf methods in migration specs.
+ #
+ # @example
+ #
+ # # bad
+ # let_it_be(:user) { table(:users).create(username: 'test') }
+ # let_it_be_with_reload(:user) { table(:users).create(username: 'test') }
+ # let_it_be_with_refind(:user) { table(:users).create(username: 'test') }
+ #
+ # before_all do
+ # do_something
+ # end
+ #
+ # # good
+ # let(:user) { table(:users).create(username: 'test') }
+ # let!(:user) { table(:users).create(username: 'test') }
+ #
+ # before(:all) do
+ # do_something
+ # end
+ #
+ # before do
+ # do_something
+ # end
+ class AvoidTestProf < RuboCop::Cop::Base
+ MESSAGE = "Prefer %{alternatives} over `%{method}` in migration specs. " \
+ 'See ' \
+ 'https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#testprof-in-migration-specs'
+
+ LET_ALTERNATIVES = %w[`let` `let!`].freeze
+ ALTERNATIVES = {
+ let_it_be: LET_ALTERNATIVES,
+ let_it_be_with_reload: LET_ALTERNATIVES,
+ let_it_be_with_refind: LET_ALTERNATIVES,
+ before_all: %w[`before` `before(:all)`]
+ }.freeze
+
+ FORBIDDEN_METHODS = ALTERNATIVES.keys.map(&:inspect).join(' ')
+ RESTRICT_ON_SEND = ALTERNATIVES.keys
+
+ def_node_matcher :forbidden_method_usage, <<~PATTERN
+ (send nil? ${#{FORBIDDEN_METHODS}} ...)
+ PATTERN
+
+ def on_send(node)
+ method = forbidden_method_usage(node)
+ return unless method
+
+ alternatives = ALTERNATIVES.fetch(method).join(' or ')
+
+ add_offense(
+ node,
+ message: format(MESSAGE, method: method, alternatives: alternatives)
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/rspec/timecop_freeze.rb b/rubocop/cop/rspec/timecop_freeze.rb
deleted file mode 100644
index b13f5050040..00000000000
--- a/rubocop/cop/rspec/timecop_freeze.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# frozen_string_literal: true
-
-require 'rubocop-rspec'
-
-module RuboCop
- module Cop
- module RSpec
- # This cop checks for `Timecop.freeze` usage in specs.
- #
- # @example
- #
- # # bad
- # Timecop.freeze(Time.current) { example.run }
- #
- # # good
- # freeze_time(Time.current) { example.run }
- #
- class TimecopFreeze < RuboCop::Cop::Base
- extend RuboCop::Cop::AutoCorrector
-
- include MatchRange
- MESSAGE = 'Do not use `Timecop.freeze`, use `freeze_time` instead. ' \
- 'See https://gitlab.com/gitlab-org/gitlab/-/issues/214432 for more info.'
-
- def_node_matcher :timecop_freeze?, <<~PATTERN
- (send (const nil? :Timecop) :freeze ?_)
- PATTERN
-
- def on_send(node)
- return unless timecop_freeze?(node)
-
- add_offense(node, message: MESSAGE) do |corrector|
- each_match_range(node.source_range, /^(Timecop\.freeze)/) do |match_range|
- corrector.replace(match_range, 'freeze_time')
- end
- end
- end
- end
- end
- end
-end
diff --git a/rubocop/cop/rspec/timecop_travel.rb b/rubocop/cop/rspec/timecop_travel.rb
deleted file mode 100644
index 03f978be349..00000000000
--- a/rubocop/cop/rspec/timecop_travel.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# frozen_string_literal: true
-
-require 'rubocop-rspec'
-
-module RuboCop
- module Cop
- module RSpec
- # This cop checks for `Timecop.travel` usage in specs.
- #
- # @example
- #
- # # bad
- # Timecop.travel(1.day.ago) { create(:issue) }
- #
- # # good
- # travel_to(1.day.ago) { create(:issue) }
- #
- class TimecopTravel < RuboCop::Cop::Base
- extend RuboCop::Cop::AutoCorrector
-
- include MatchRange
- MESSAGE = 'Do not use `Timecop.travel`, use `travel_to` instead. ' \
- 'See https://gitlab.com/gitlab-org/gitlab/-/issues/214432 for more info.'
-
- def_node_matcher :timecop_travel?, <<~PATTERN
- (send (const nil? :Timecop) :travel _)
- PATTERN
-
- def on_send(node)
- return unless timecop_travel?(node)
-
- add_offense(node, message: MESSAGE) do |corrector|
- each_match_range(node.source_range, /^(Timecop\.travel)/) do |match_range|
- corrector.replace(match_range, 'travel_to')
- end
- end
- end
- end
- end
- end
-end
diff --git a/rubocop/migration_helpers.rb b/rubocop/migration_helpers.rb
index 22f3931be73..50d7b198931 100644
--- a/rubocop/migration_helpers.rb
+++ b/rubocop/migration_helpers.rb
@@ -19,7 +19,7 @@ module RuboCop
# List of helpers that add new columns, either directly (ADD_COLUMN_METHODS)
# or through a create/alter table (TABLE_METHODS)
- ADD_COLUMN_METHODS = %i(add_column add_column_with_default change_column_type_concurrently).freeze
+ ADD_COLUMN_METHODS = %i(add_column change_column_type_concurrently).freeze
TABLE_METHODS = %i(create_table create_table_if_not_exists change_table create_table_with_constraints).freeze
@@ -66,7 +66,7 @@ module RuboCop
def array_column?(node)
node.each_descendant(:pair).any? do |pair_node|
pair_node.child_nodes[0].value == :array && # Searching for a (pair (sym :array) (true)) node
- pair_node.child_nodes[1].type == :true # RuboCop::AST::Node uses symbols for types, even when that is a :true
+ pair_node.child_nodes[1].type == :true # RuboCop::AST::Node uses symbols for types, even when that is a :true
end
end
# rubocop:enable Lint/BooleanSymbol
diff --git a/rubocop/rubocop-code_reuse.yml b/rubocop/rubocop-code_reuse.yml
index a3b75117621..6d54f880759 100644
--- a/rubocop/rubocop-code_reuse.yml
+++ b/rubocop/rubocop-code_reuse.yml
@@ -26,6 +26,7 @@ CodeReuse/ActiveRecord:
- lib/banzai/**/*.rb
- lib/gitlab/background_migration/**/*.rb
- lib/gitlab/cycle_analytics/**/*.rb
+ - lib/gitlab/counters/**/*.rb
- lib/gitlab/database/**/*.rb
- lib/gitlab/database_importers/common_metrics/importer.rb
- lib/gitlab/import_export/**/*.rb
diff --git a/scripts/api/create_issue_discussion.rb b/scripts/api/create_issue_discussion.rb
new file mode 100644
index 00000000000..74a9f3ae378
--- /dev/null
+++ b/scripts/api/create_issue_discussion.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'gitlab'
+require_relative 'default_options'
+
+class CreateIssueDiscussion
+ def initialize(options)
+ @project = options.fetch(:project)
+
+ # Force the token to be a string so that if api_token is nil, it's set to '',
+ # allowing unauthenticated requests (for forks).
+ api_token = options.delete(:api_token).to_s
+
+ warn "No API token given." if api_token.empty?
+
+ @client = Gitlab.client(
+ endpoint: options.delete(:endpoint) || API::DEFAULT_OPTIONS[:endpoint],
+ private_token: api_token
+ )
+ end
+
+ def execute(discussion_data)
+ client.post(
+ "/projects/#{client.url_encode project}/issues/#{discussion_data.delete(:issue_iid)}/discussions",
+ body: discussion_data
+ )
+ end
+
+ private
+
+ attr_reader :project, :client
+end
diff --git a/scripts/api/download_job_artifact.rb b/scripts/api/download_job_artifact.rb
deleted file mode 100755
index 394ad8f3a3d..00000000000
--- a/scripts/api/download_job_artifact.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-require 'optparse'
-require 'fileutils'
-require 'uri'
-require 'cgi'
-require 'net/http'
-require_relative 'default_options'
-
-class ArtifactFinder
- def initialize(options)
- @project = options.delete(:project)
- @job_id = options.delete(:job_id)
- @api_token = options.delete(:api_token)
- @endpoint = options.delete(:endpoint) || API::DEFAULT_OPTIONS[:endpoint]
- @artifact_path = options.delete(:artifact_path)
-
- warn "No API token given." unless api_token
- end
-
- def execute
- url = "#{endpoint}/projects/#{CGI.escape(project)}/jobs/#{job_id}/artifacts"
-
- if artifact_path
- FileUtils.mkdir_p(File.dirname(artifact_path))
- url += "/#{artifact_path}"
- end
-
- fetch(url)
- end
-
- private
-
- attr_reader :project, :job_id, :api_token, :endpoint, :artifact_path
-
- def fetch(uri_str, limit = 10)
- raise 'Too many HTTP redirects' if limit == 0
-
- uri = URI(uri_str)
- request = Net::HTTP::Get.new(uri)
- request['Private-Token'] = api_token if api_token
-
- Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
- http.request(request) do |response|
- case response
- when Net::HTTPSuccess then
- File.open(artifact_path || 'artifacts.zip', 'w') do |file|
- response.read_body(&file.method(:write))
- end
- when Net::HTTPRedirection then
- location = response['location']
- warn "Redirected (#{limit - 1} redirections remaining)."
- fetch(location, limit - 1)
- else
- raise "Unexpected response: #{response.value}"
- end
- end
- end
- end
-end
-
-if $PROGRAM_NAME == __FILE__
- options = API::DEFAULT_OPTIONS.dup
-
- OptionParser.new do |opts|
- opts.on("-p", "--project PROJECT", String, "Project where to find the job (defaults to $CI_PROJECT_ID)") do |value|
- options[:project] = value
- end
-
- opts.on("-j", "--job-id JOB_ID", String, "A job ID") do |value|
- options[:job_id] = value
- end
-
- opts.on("-a", "--artifact-path ARTIFACT_PATH", String, "A valid artifact path") do |value|
- options[:artifact_path] = value
- end
-
- opts.on("-t", "--api-token API_TOKEN", String, "A value API token with the `read_api` scope") do |value|
- options[:api_token] = value
- end
-
- opts.on("-E", "--endpoint ENDPOINT", String, "The API endpoint for the API token. (defaults to $CI_API_V4_URL and fallback to https://gitlab.com/api/v4)") do |value|
- options[:endpoint] = value
- end
-
- opts.on("-h", "--help", "Prints this help") do
- puts opts
- exit
- end
- end.parse!
-
- ArtifactFinder.new(options).execute
-end
diff --git a/scripts/build_assets_image b/scripts/build_assets_image
index 8aa6526061a..ee8623c826e 100755
--- a/scripts/build_assets_image
+++ b/scripts/build_assets_image
@@ -1,36 +1,75 @@
+#!/bin/sh
+
+set -e
+
+# This script builds an image that contains assets, that's then used by:
+# - The `CNG` downstream pipelines (triggered from `gitlab-org/gitlab` via the `review-build-cng` job):
+# https://gitlab.com/gitlab-org/gitlab/-/blob/c34e0834b01cd45c1f69a01b5e38dd6bc505f903/.gitlab/ci/review-apps/main.gitlab-ci.yml#L69.
+# - The `omnibus-gitlab` downstream pipelines (triggered from `gitlab-org/gitlab` via the `e2e:package-and-test` job):
+# https://gitlab.com/gitlab-org/omnibus-gitlab/-/blob/dfd1ad475868fc84e91ab7b5706aa03e46dc3a86/.gitlab-ci.yml#L130.
+# - The `gitlab-org/charts/gitlab` `master` pipelines via `gitlab-org/build/CNG`,
+# which pull `registry.gitlab.com/gitlab-org/gitlab/gitlab-assets-ee:master`
+# - The `omnibus-gitlab` and CNG `master`/stable-branch pipelines, for both gitlab.com and dev.gitlab.org,
+# which pull `registry.gitlab.com/gitlab-org/gitlab/gitlab-assets-ee:${CI_COMMIT_REF_SLUG}`.
+# - The `omnibus-gitlab` tag pipelines, for both gitlab.com and dev.gitlab.org,
+# which pull `registry.gitlab.com/gitlab-org/gitlab/gitlab-assets-ee:${CI_COMMIT_REF_SLUG}`.
+# - The CNG tag pipelines, for both gitlab.com and dev.gitlab.org,
+# which pull `registry.gitlab.com/gitlab-org/gitlab/gitlab-assets-ee:${CI_COMMIT_REF_NAME}`.
+# - The auto-deploy pipelines, which pull `registry.gitlab.com/gitlab-org/gitlab/gitlab-assets-ee:${CI_COMMIT_SHA}`.
+
+. scripts/utils.sh
+
# Exit early if we don't want to build the image
-if [[ "${BUILD_ASSETS_IMAGE}" != "true" ]]
+if [ "${BUILD_ASSETS_IMAGE}" != "true" ]
then
exit 0
fi
# Generate the image name based on the project this is being run in
ASSETS_IMAGE_NAME="gitlab-assets-ce"
+
# `dev.gitlab-org` still has gitlab-ee.
-if [[ "${CI_PROJECT_NAME}" == "gitlab" ]] || [[ "${CI_PROJECT_NAME}" == "gitlab-ee" ]]
-then
+if [ "${CI_PROJECT_NAME}" = "gitlab" ] || [ "${CI_PROJECT_NAME}" = "gitlab-ee" ]; then
ASSETS_IMAGE_NAME="gitlab-assets-ee"
fi
-ASSETS_IMAGE_PATH=${CI_REGISTRY}/${CI_PROJECT_PATH}/${ASSETS_IMAGE_NAME}
+ASSETS_IMAGE_PATH="${CI_REGISTRY}/${CI_PROJECT_PATH}/${ASSETS_IMAGE_NAME}"
-mkdir -p assets_container.build/public
-cp -r public/assets assets_container.build/public/
-cp Dockerfile.assets assets_container.build/
+# Used in MR pipelines
+COMMIT_ASSETS_HASH_DESTINATION="${ASSETS_IMAGE_PATH}:$(assets_image_tag)"
+# Used by other projects's master pipelines
+COMMIT_REF_SLUG_DESTINATION="${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_SLUG}"
+# Used by auto-deploy pipelines: https://gitlab.com/gitlab-org/release/docs/blob/master/general/deploy/auto-deploy.md
+COMMIT_SHA_DESTINATION=${ASSETS_IMAGE_PATH}:${CI_COMMIT_SHA}
+# Used for CNG tag pipelines
+COMMIT_REF_NAME_DESTINATION="${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_NAME}"
-COMMIT_REF_SLUG_DESTINATION=${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_SLUG}
+if skopeo inspect "docker://${COMMIT_ASSETS_HASH_DESTINATION}" > /dev/null; then
+ echosuccess "Image ${COMMIT_ASSETS_HASH_DESTINATION} already exists, no need to rebuild it."
-COMMIT_SHA_DESTINATION=${ASSETS_IMAGE_PATH}:${CI_COMMIT_SHA}
-COMMIT_REF_NAME_DESTINATION=${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_NAME}
+ skopeo copy "docker://${COMMIT_ASSETS_HASH_DESTINATION}" "docker://${COMMIT_REF_SLUG_DESTINATION}"
+ skopeo copy "docker://${COMMIT_ASSETS_HASH_DESTINATION}" "docker://${COMMIT_SHA_DESTINATION}"
-DESTINATIONS="--destination=$COMMIT_REF_SLUG_DESTINATION --destination=$COMMIT_SHA_DESTINATION"
+ if [ -n "${CI_COMMIT_TAG}" ]; then
+ skopeo copy "docker://${COMMIT_ASSETS_HASH_DESTINATION}" "docker://${COMMIT_REF_NAME_DESTINATION}"
+ fi
+else
+ echoinfo "Image ${COMMIT_ASSETS_HASH_DESTINATION} doesn't exist, we'll need to build it."
-# Also tag the image with GitLab version, if running on a tag pipeline, so
-# other projects can simply use that instead of computing the slug.
-if [ -n "$CI_COMMIT_TAG" ]; then
- DESTINATIONS="$DESTINATIONS --destination=$COMMIT_REF_NAME_DESTINATION"
-fi
+ DESTINATIONS="--destination=${COMMIT_ASSETS_HASH_DESTINATION} --destination=${COMMIT_REF_SLUG_DESTINATION} --destination=${COMMIT_SHA_DESTINATION}"
-echo "building assets image for destinations: $DESTINATIONS"
+ if [ -n "${CI_COMMIT_TAG}" ]; then
+ DESTINATIONS="$DESTINATIONS --destination=${COMMIT_REF_NAME_DESTINATION}"
+ fi
-/kaniko/executor --context=assets_container.build --dockerfile=assets_container.build/Dockerfile.assets $DESTINATIONS
+ mkdir -p assets_container.build/public
+ cp -r public/assets assets_container.build/public/
+ cp Dockerfile.assets assets_container.build/
+
+ echo "Building assets image for destinations: ${DESTINATIONS}"
+
+ /kaniko/executor \
+ --context="assets_container.build" \
+ --dockerfile="assets_container.build/Dockerfile.assets" \
+ ${DESTINATIONS}
+fi
diff --git a/scripts/build_qa_image b/scripts/build_qa_image
index 477bec29ba7..3728608e32c 100755
--- a/scripts/build_qa_image
+++ b/scripts/build_qa_image
@@ -1,9 +1,11 @@
#!/bin/bash
QA_IMAGE_NAME="gitlab-ee-qa"
+QA_BUILD_TARGET="ee"
if [[ "${CI_PROJECT_NAME}" == "gitlabhq" || "${CI_PROJECT_NAME}" == "gitlab-foss" ]]; then
QA_IMAGE_NAME="gitlab-ce-qa"
+ QA_BUILD_TARGET="foss"
fi
# Tag with commit SHA by default
@@ -36,7 +38,7 @@ docker buildx build \
--build-arg=CHROME_VERSION="${CHROME_VERSION}" \
--build-arg=DOCKER_VERSION="${DOCKER_VERSION}" \
--build-arg=RUBY_VERSION="${RUBY_VERSION}" \
- --build-arg=QA_BUILD_TARGET="${QA_BUILD_TARGET:-qa}" \
+ --build-arg=QA_BUILD_TARGET="${QA_BUILD_TARGET}" \
--file="${CI_PROJECT_DIR}/qa/Dockerfile" \
--push \
${DESTINATIONS} \
diff --git a/scripts/check-template-changes b/scripts/check-template-changes
new file mode 100755
index 00000000000..1a3060fe1bb
--- /dev/null
+++ b/scripts/check-template-changes
@@ -0,0 +1,105 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require 'tmpdir'
+
+@template = ARGV.first
+
+if @template.nil?
+ puts "Usage: #{__FILE__} <path_to_project_template>"
+ exit 1
+end
+
+@name = File.basename(@template).delete_suffix('.tar.gz')
+@extracted_template_dir = Dir.mktmpdir(@name)
+@master_template_dir = Dir.mktmpdir(@name)
+
+def extract(dest)
+ system('tar', 'xf', @template, '-C', dest, exception: true)
+end
+
+def cleanup
+ FileUtils.rm_rf(@extracted_template_dir)
+ FileUtils.rm_rf(@master_template_dir)
+end
+
+def repo_details
+ Dir.chdir(@extracted_template_dir) do
+ system('git', 'clone', 'project.bundle', @name, exception: true)
+ end
+
+ Dir.chdir(File.join(@extracted_template_dir, @name)) do
+ head_commit = `git cat-file -p HEAD`
+ lines = head_commit.split("\n")
+
+ repository = lines
+ .find { |line| line.start_with?('Template repository: ') }
+ .rpartition(' ').last
+
+ commit_sha = lines
+ .find { |line| line.start_with?('Commit SHA: ') }
+ .rpartition(' ').last
+
+ [repository, commit_sha]
+ end
+end
+
+puts "Extracting template to: #{@extracted_template_dir}"
+
+extract(@extracted_template_dir)
+branch = `git rev-parse --abbrev-ref HEAD`.chomp
+system('git', 'checkout', 'master', exception: true)
+extract(@master_template_dir)
+system('git', 'checkout', branch, exception: true)
+
+puts
+puts '🧠Comparing new template with master'
+puts
+
+system('git', '--no-pager', 'diff', '--no-index', @master_template_dir, @extracted_template_dir)
+
+puts
+puts '--- end diff ---'
+
+repository, commit_sha = repo_details
+
+puts
+puts "📠Template is created from #{repository} at commit #{commit_sha}"
+
+unless repository.start_with?('https://gitlab.com/gitlab-org/project-templates/')
+ puts '⌠This template does not have the correct origin'
+ cleanup
+ exit 1
+end
+
+puts '🧠Verifying that template repo matches remote'
+puts
+
+remote_repo_dir = Dir.mktmpdir(@name)
+
+system('git', 'clone', repository, remote_repo_dir, exception: true)
+
+Dir.chdir(remote_repo_dir) do
+ system('git', 'checkout', commit_sha, exception: true)
+ system('git', '--no-pager', 'show')
+end
+
+extracted_template_repo_dir = File.join(@extracted_template_dir, @name)
+
+FileUtils.rm_rf(File.join(extracted_template_repo_dir, '.git'))
+FileUtils.cp_r(File.join(remote_repo_dir, '.git'), extracted_template_repo_dir)
+
+Dir.chdir(extracted_template_repo_dir) do
+ status = `git status`
+ puts status
+ puts
+
+ if status.include?('nothing to commit, working tree clean')
+ puts "✅ Template is up to date with remote commit #{commit_sha}"
+ else
+ puts '⌠Template is not synced with remote'
+ end
+end
+
+FileUtils.rm_rf(remote_repo_dir)
+cleanup
diff --git a/scripts/create-pipeline-failure-incident.rb b/scripts/create-pipeline-failure-incident.rb
index c38f80699e6..1035a680291 100755
--- a/scripts/create-pipeline-failure-incident.rb
+++ b/scripts/create-pipeline-failure-incident.rb
@@ -7,13 +7,14 @@ require 'json'
require_relative 'api/pipeline_failed_jobs'
require_relative 'api/create_issue'
+require_relative 'api/create_issue_discussion'
class CreatePipelineFailureIncident
DEFAULT_OPTIONS = {
project: nil,
incident_json_file: 'incident.json'
}.freeze
- DEFAULT_LABELS = ['Engineering Productivity', 'master-broken:undetermined'].freeze
+ DEFAULT_LABELS = ['Engineering Productivity', 'master-broken::undetermined'].freeze
def initialize(options)
@project = options.delete(:project)
@@ -28,7 +29,12 @@ class CreatePipelineFailureIncident
labels: incident_labels
}
- CreateIssue.new(project: project, api_token: api_token).execute(payload)
+ CreateIssue.new(project: project, api_token: api_token).execute(payload).tap do |incident|
+ CreateIssueDiscussion.new(project: project, api_token: api_token)
+ .execute(issue_iid: incident.iid, body: "## Root Cause Analysis")
+ CreateIssueDiscussion.new(project: project, api_token: api_token)
+ .execute(issue_iid: incident.iid, body: "## Investigation Steps")
+ end
end
private
@@ -44,8 +50,16 @@ class CreatePipelineFailureIncident
end
def title
- "#{now.strftime('%A %F %R UTC')} - `#{ENV['CI_PROJECT_PATH']}` broken `#{ENV['CI_COMMIT_REF_NAME']}` " \
- "with #{failed_jobs.size} failed jobs"
+ @title ||= begin
+ full_title = "#{now.strftime('%A %F %R UTC')} - `#{ENV['CI_PROJECT_PATH']}` " \
+ "broken `#{ENV['CI_COMMIT_REF_NAME']}` with #{failed_jobs.map(&:name).join(', ')}"
+
+ if full_title.size >= 255
+ "#{full_title[...252]}..." # max title length is 255, and we add an elipsis
+ else
+ full_title
+ end
+ end
end
def description
@@ -85,7 +99,7 @@ class CreatePipelineFailureIncident
You can create a merge request, assign to any available maintainer, and ping people that were involved/related to the introduction of the failure.
Additionally, a message can be posted in `#backend_maintainers` or `#frontend_maintainers` to get a maintainer take a look at the fix ASAP.
- In both cases, make sure to add the ~"pipeline:expedite-master-fixing" label, and `master:broken` or `master:foss-broken` label, to speed up the `master`-fixing pipelines.
+ In both cases, make sure to add the ~"pipeline:expedite" label, and `master:broken` or `master:foss-broken` label, to speed up the `master`-fixing pipelines.
### Resolution
diff --git a/scripts/lib/gitlab.rb b/scripts/lib/gitlab.rb
index 556e2037edf..a7ca6b7c5df 100644
--- a/scripts/lib/gitlab.rb
+++ b/scripts/lib/gitlab.rb
@@ -4,10 +4,10 @@ module Gitlab
module_function
def ee?
- File.exist?(File.expand_path('../../ee/app/models/license.rb', __dir__)) && !%w[true 1].include?(ENV['FOSS_ONLY'].to_s)
+ File.exist?(File.expand_path('../../ee/app/models/license.rb', __dir__)) && !%w[true 1].include?(ENV['FOSS_ONLY'].to_s) # rubocop:disable Rails/NegateInclude
end
def jh?
- ee? && Dir.exist?(File.expand_path('../../jh', __dir__)) && !%w[true 1].include?(ENV['EE_ONLY'].to_s)
+ ee? && Dir.exist?(File.expand_path('../../jh', __dir__)) && !%w[true 1].include?(ENV['EE_ONLY'].to_s) # rubocop:disable Rails/NegateInclude
end
end
diff --git a/scripts/lib/glfm/constants.rb b/scripts/lib/glfm/constants.rb
index c432e5495dd..16ffb12db57 100644
--- a/scripts/lib/glfm/constants.rb
+++ b/scripts/lib/glfm/constants.rb
@@ -4,6 +4,11 @@ require 'pathname'
module Glfm
module Constants
+ # Version and titles for rendering
+ GLFM_SPEC_VERSION = 'alpha'
+ GLFM_SPEC_TXT_TITLE = 'GitLab Flavored Markdown Official Specification'
+ ES_SNAPSHOT_SPEC_TITLE = 'GitLab Flavored Markdown Internal Extensions'
+
# Root dir containing all specification files
specification_path = Pathname.new(File.expand_path("../../../glfm_specification", __dir__))
@@ -25,6 +30,12 @@ module Glfm
GLFM_OUTPUT_SPEC_PATH = specification_path.join('output_spec')
GLFM_SPEC_TXT_PATH = GLFM_OUTPUT_SPEC_PATH.join('spec.txt')
GLFM_SPEC_HTML_PATH = GLFM_OUTPUT_SPEC_PATH.join('spec.html')
+ GLFM_SPEC_TXT_HEADER = <<~MARKDOWN
+ ---
+ title: #{GLFM_SPEC_TXT_TITLE}
+ version: #{GLFM_SPEC_VERSION}
+ ...
+ MARKDOWN
# Example Snapshot (ES) files
ES_OUTPUT_EXAMPLE_SNAPSHOTS_PATH = specification_path.join('output_example_snapshots')
@@ -34,14 +45,14 @@ module Glfm
ES_MARKDOWN_YML_PATH = ES_OUTPUT_EXAMPLE_SNAPSHOTS_PATH.join('markdown.yml')
ES_HTML_YML_PATH = ES_OUTPUT_EXAMPLE_SNAPSHOTS_PATH.join('html.yml')
ES_PROSEMIRROR_JSON_YML_PATH = ES_OUTPUT_EXAMPLE_SNAPSHOTS_PATH.join('prosemirror_json.yml')
-
- # Other constants used for processing files
- GLFM_SPEC_TXT_HEADER = <<~MARKDOWN
+ ES_SNAPSHOT_SPEC_MD_HEADER = <<~MARKDOWN
---
- title: GitLab Flavored Markdown (GLFM) Spec
- version: alpha
+ title: #{ES_SNAPSHOT_SPEC_TITLE}
+ version: #{GLFM_SPEC_VERSION}
...
MARKDOWN
+
+ # Other constants used for processing files
EXAMPLE_BACKTICKS_LENGTH = 32
EXAMPLE_BACKTICKS_STRING = '`' * EXAMPLE_BACKTICKS_LENGTH
EXAMPLE_BEGIN_STRING = "#{EXAMPLE_BACKTICKS_STRING} example"
diff --git a/scripts/lib/glfm/render_static_html.rb b/scripts/lib/glfm/render_static_html.rb
index 6af73cd845d..bf7865c4d95 100644
--- a/scripts/lib/glfm/render_static_html.rb
+++ b/scripts/lib/glfm/render_static_html.rb
@@ -23,8 +23,8 @@ require_relative 'shared'
# the API and obtain the response.
#
# It is intended to be invoked as a helper subprocess from the `update_example_snapshots.rb`
-# script class. It's not intended to be run or used directly. This usage is also reinforced
-# by not naming the file with a `_spec.rb` ending.
+# and `update_specification.rb` script classes. It's not intended to be run or used directly.
+# This usage is also reinforced by not naming the file with a `_spec.rb` ending.
RSpec.describe 'Render Static HTML', :api, type: :request do
include Glfm::Constants
include Glfm::Shared
diff --git a/scripts/lib/glfm/render_wysiwyg_html_and_json.js b/scripts/lib/glfm/render_wysiwyg_html_and_json.js
index 8f94f50d62b..f9c4c417a6f 100644
--- a/scripts/lib/glfm/render_wysiwyg_html_and_json.js
+++ b/scripts/lib/glfm/render_wysiwyg_html_and_json.js
@@ -19,7 +19,6 @@ jest.mock('~/emoji');
// Jest because that is the simplest environment in which to execute the
// relevant Content Editor logic.
//
-//
// This script should be invoked via jest with the a command similar to the following:
// yarn jest --testMatch '**/render_wysiwyg_html_and_json.js' ./scripts/lib/glfm/render_wysiwyg_html_and_json.js
it('serializes html to prosemirror json', async () => {
diff --git a/scripts/lib/glfm/shared.rb b/scripts/lib/glfm/shared.rb
index b529d9ba94f..56cb2f95d6a 100644
--- a/scripts/lib/glfm/shared.rb
+++ b/scripts/lib/glfm/shared.rb
@@ -3,6 +3,7 @@ require 'fileutils'
require 'open3'
require 'active_support/core_ext/hash/keys'
+# This module contains shared methods used by other GLFM scripts and modules.
module Glfm
module Shared
def write_file(file_path, file_content_string)
diff --git a/scripts/lib/glfm/specification_html_template.erb b/scripts/lib/glfm/specification_html_template.erb
new file mode 100644
index 00000000000..f75cb400229
--- /dev/null
+++ b/scripts/lib/glfm/specification_html_template.erb
@@ -0,0 +1,244 @@
+<!DOCTYPE html>
+<!-- NOTE: Styling is based on the CommonMark specification template: -->
+<!-- - https://github.com/commonmark/commonmark-spec/blob/master/tools/make_spec.lua -->
+<!-- - https://github.com/commonmark/commonmark-spec/blob/master/tools/template.html -->
+<!-- -->
+<!-- NOTE: 'TODO:' comments will be followed up as task(s) on this issue: -->
+<!-- - https://gitlab.com/gitlab-org/gitlab/-/issues/361241 -->
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title><%= title %></title>
+ <style type="text/css">
+ body {
+ font-family: Helvetica, arial, freesans, clean, sans-serif;
+ line-height: 1.4;
+ max-width: 48em;
+ margin: auto;
+ padding: 0 0.5em 4em;
+ color: #333333;
+ background-color: #ffffff;
+ font-size: 13pt;
+ }
+
+ div#TOC ul { list-style: none; }
+ h1 {
+ font-size: 140%;
+ font-weight: bold;
+ border-top: 1px solid gray;
+ padding-top: 0.5em;
+ }
+
+ h2 {
+ font-size: 120%;
+ font-weight: bold;
+ }
+
+ h3 {
+ font-size: 110%;
+ font-weight: bold;
+ }
+
+ h4 {
+ font-size: 100%;
+ font-weight: bold;
+ }
+
+ /* NOTE: "font-weight: bold" was applied to "a.definition" class in original CommonMark */
+ /* template, but in practice it was applied to all anchors */
+ a {
+ font-weight: bold;
+ }
+
+
+ /* TODO: Format whitespace in examples. This will require preprocessing to insert spans around them. */
+ /*span.space { position: relative; }*/
+ /*span.space:after {*/
+ /* content: "·";*/
+ /* position: absolute;*/
+ /* !* create a mark that indicates a space (trick from D. Greenspan) *!*/
+ /* top: 0; bottom: 7px; left: 1px; right: 1px;*/
+ /* color: #aaaaaa;*/
+ /*}*/
+ /*@media print {*/
+ /* a.dingus { display: none; }*/
+ /*}*/
+
+ div.example {
+ overflow: hidden;
+ }
+
+ p {
+ text-align: justify;
+ }
+
+ pre {
+ padding: 0.5em;
+ margin: 0.2em 0 0.5em;
+ font-size: 88%;
+ }
+
+ pre {
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+ }
+
+ code {
+ font-family: monospace;
+ background-color: #d3e1e4;
+ }
+
+ pre > code {
+ background-color: transparent;
+ }
+
+ .example {
+ font-size: 0; /* hack to get width: 50% to work on inline-block */
+ padding-bottom: 6pt;
+ }
+
+ .column pre {
+ font-size: 11pt;
+ padding: 2pt 6pt;
+ }
+
+ div.examplenum {
+ font-size: 11pt;
+ text-align: left;
+ margin-bottom: 10px;
+ }
+
+ div.column {
+ display: inline-block;
+ width: 50%;
+ vertical-align: top;
+ }
+
+ div.example > div:nth-child(2) {
+ clear: left;
+ background-color: #d3e1e4;
+ }
+
+ div.example > div:nth-child(3) {
+ clear: right;
+ background-color: #c9cace;
+ }
+
+ @media print {
+ @page {
+ size: auto;
+ margin: 1.2in 1.2in 1.2in 1.2in;
+ }
+
+ body {
+ margin: 0;
+ line-height: 1.2;
+ font-size: 10pt;
+ }
+
+ .column pre {
+ font-size: 9pt;
+ }
+
+ div.examplenum {
+ font-size: 9pt;
+ }
+ }
+ </style>
+ <!-- TODO: Extract this javascript out to a separate file and unit test it -->
+ <script type="text/javascript">
+ /* NOTE: The following code performs many of the pre-processing steps originally handled */
+ /* in https://github.com/commonmark/commonmark-spec/blob/master/tools/make_spec.lua */
+
+ /* Adds a div.example wrapper around each pair of example code blocks. */
+ function addAttributesToExampleWrapperDivs() {
+ const exampleAnchorTags = document.querySelectorAll("a[href^=\"#example-\"]");
+ for (const exampleAnchorTag of exampleAnchorTags) {
+ const examplenumDiv = exampleAnchorTag.parentElement;
+ examplenumDiv.classList.add("examplenum");
+ const exampleDiv = examplenumDiv.parentElement;
+ exampleDiv.classList.add("example");
+ exampleDiv.id = exampleAnchorTag.getAttribute("href").substring(1);
+ }
+ }
+
+ function addColumnClassToMarkdownDivs() {
+ const markdownCodeBlockDivs = document.querySelectorAll("div.markdown-code-block");
+ for (const markdownCodeBlockDiv of markdownCodeBlockDivs) {
+ markdownCodeBlockDiv.classList.add("column");
+ }
+ }
+
+ function addNumbersToHeaders() {
+ const headers = document.querySelectorAll('h1,h2,h3');
+ let h1Index = -1; // NOTE: -1 because we don't assign a number to the title
+ let h2Index = 0;
+ let h3Index = 0;
+ const tocEntries = [];
+ for (const header of headers) {
+ if (h1Index === -1) {
+ h1Index++;
+ continue;
+ }
+
+ const originalHeaderTextContent = header.textContent.trim();
+ const headerAnchor = originalHeaderTextContent.toLowerCase().replaceAll(' ', '-');
+ header.id = headerAnchor;
+ let indent;
+ let headerTextContent;
+ if (header.tagName === 'H1') {
+ h1Index++;
+ h2Index = 0;
+ h3Index = 0;
+ header.textContent = headerTextContent = h1Index + ' ' + originalHeaderTextContent;
+ indent = 0;
+ } else if (header.tagName === 'H2') {
+ h2Index++;
+ h3Index = 0;
+ header.textContent =
+ headerTextContent = h1Index + '.' + h2Index + ' ' + originalHeaderTextContent;
+ indent = 1;
+ } else if (header.tagName === 'H3') {
+ h3Index++;
+ header.textContent = headerTextContent =
+ h1Index + '.' + h2Index + '.' + h3Index + ' ' + originalHeaderTextContent;
+ indent = 2;
+ }
+ tocEntries.push({headerAnchor, headerTextContent, indent});
+ }
+ }
+
+ document.addEventListener("DOMContentLoaded", function(_event) {
+ addAttributesToExampleWrapperDivs();
+ addColumnClassToMarkdownDivs();
+ const tocEntries = addNumbersToHeaders();
+ addToc(tocEntries);
+ });
+
+ /* NOTE: The following code is to support the "Try it" interactive "dingus", which */
+ /* we do not yet support. But it is being left here for comparison context with the */
+ /* original CommonMark template. */
+ // $$(document).ready(function() {
+ // $$("div.example").each(function(e) {
+ // var t = $$(this).find('code.language-markdown').text();
+ // $$(this).find('a.dingus').click(function(f) {
+ // window.open('/dingus/?text=' +
+ // encodeURIComponent(t.replace(/→/g,"\t")));
+ // });
+ // });
+ // $$("code.language-markdown").dblclick(function(e) { window.open('/dingus/?text=' +
+ // encodeURIComponent($$(this).text()));
+ // });
+ // });
+ </script>
+</head>
+<body>
+<h1 class="title"><%= title %></h1>
+<div class="version">Version <%= version %></div>
+
+<%= body %>
+
+</body>
+</html>
diff --git a/scripts/lib/glfm/update_specification.rb b/scripts/lib/glfm/update_specification.rb
index b87005bdb90..ef6f24d5a77 100644
--- a/scripts/lib/glfm/update_specification.rb
+++ b/scripts/lib/glfm/update_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+require 'erb'
require 'fileutils'
require 'open-uri'
require 'pathname'
@@ -29,19 +30,21 @@ module Glfm
# create `output_spec/spec.txt`
glfm_spec_txt_header_lines = GLFM_SPEC_TXT_HEADER.split("\n").map { |line| "#{line}\n" }
official_spec_lines = readlines_from_path!(GLFM_OFFICIAL_SPECIFICATION_MD_PATH)
-
glfm_spec_txt_string = (glfm_spec_txt_header_lines + official_spec_lines).join('')
write_glfm_spec_txt(glfm_spec_txt_string)
# create `output_example_snapshots/snapshot_spec.md`
+ snapshot_spec_md_header_lines = ES_SNAPSHOT_SPEC_MD_HEADER.split("\n").map { |line| "#{line}\n" }
ghfm_spec_example_lines = extract_ghfm_spec_example_lines(ghfm_spec_lines)
official_spec_example_lines =
extract_glfm_spec_example_lines(official_spec_lines, GLFM_OFFICIAL_SPECIFICATION_MD_PATH)
internal_extension_lines = readlines_from_path!(GLFM_INTERNAL_EXTENSIONS_MD_PATH)
+ validate_internal_extensions_md(internal_extension_lines)
internal_extension_example_lines =
extract_glfm_spec_example_lines(internal_extension_lines, GLFM_INTERNAL_EXTENSIONS_MD_PATH)
+
snapshot_spec_md_string = (
- glfm_spec_txt_header_lines +
+ snapshot_spec_md_header_lines +
ghfm_spec_example_lines +
official_spec_example_lines +
["\n"] +
@@ -49,16 +52,37 @@ module Glfm
).join('')
write_snapshot_spec_md(snapshot_spec_md_string)
+ # Some unit tests can skip HTML generation if they don't need it, so they run faster
if skip_spec_html_generation
output("Skipping GLFM spec.html and snapshot_spec.html generation...")
return
end
- # create `output_spec/spec.html` and `output_snapshot_examples/snapshot_spec.html`
- spec_html_string, snapshot_spec_html_string =
- generate_spec_html_files(glfm_spec_txt_string, snapshot_spec_md_string)
- write_spec_html(spec_html_string)
- write_snapshot_spec_html(snapshot_spec_html_string)
+ # Use the backend markdown processing to render un-styled GLFM specification HTML files from the markdown
+ # We strip off the frontmatter headers before rendering.
+ spec_html_unstyled_string, snapshot_spec_html_unstyled_string =
+ generate_spec_html_files(
+ glfm_spec_txt_string.gsub!(GLFM_SPEC_TXT_HEADER, "[TOC]\n\n"),
+ snapshot_spec_md_string.gsub!(ES_SNAPSHOT_SPEC_MD_HEADER, "[TOC]\n\n"),
+ ghfm_spec_example_lines.join('')
+ )
+
+ # Add styling to the rendered HTML files, to make them look like the CommonMark and
+ # GitHub Flavored Markdown HTML-rendered specifications
+ spec_html_styled_string = add_styling_to_specification_html(
+ body: spec_html_unstyled_string,
+ title: GLFM_SPEC_TXT_TITLE,
+ version: GLFM_SPEC_VERSION
+ )
+ snapshot_spec_html_styled_string = add_styling_to_specification_html(
+ body: snapshot_spec_html_unstyled_string,
+ title: ES_SNAPSHOT_SPEC_TITLE,
+ version: GLFM_SPEC_VERSION
+ )
+
+ # Write out the styled HTML GLFM specification HTML files
+ write_spec_html(spec_html_styled_string)
+ write_snapshot_spec_html(snapshot_spec_html_styled_string)
end
private
@@ -156,6 +180,16 @@ module Glfm
spec_lines[(begin_tests_comment_line_index + 1)..(end_tests_comment_index - 1)]
end
+ def validate_internal_extensions_md(internal_extension_lines)
+ first_line = internal_extension_lines[0].strip
+ last_line = internal_extension_lines[-1].strip
+ return unless first_line != BEGIN_TESTS_COMMENT_LINE_TEXT || last_line != END_TESTS_COMMENT_LINE_TEXT
+
+ raise "Error: No content is allowed outside of the " \
+ "'#{BEGIN_TESTS_COMMENT_LINE_TEXT}' and '#{END_TESTS_COMMENT_LINE_TEXT}' comments " \
+ "in '#{GLFM_INTERNAL_EXTENSIONS_MD_PATH}'."
+ end
+
def write_glfm_spec_txt(glfm_spec_txt_string)
output("Writing #{GLFM_SPEC_TXT_PATH}...")
FileUtils.mkdir_p(Pathname.new(GLFM_SPEC_TXT_PATH).dirname)
@@ -168,11 +202,32 @@ module Glfm
write_file(ES_SNAPSHOT_SPEC_MD_PATH, snapshot_spec_md_string)
end
- def generate_spec_html_files(spec_txt_string, snapshot_spec_md_string)
+ def generate_spec_html_files(spec_txt_string, snapshot_spec_md_string, ghfm_spec_examples_string)
output("Generating spec.html and snapshot_spec.html from spec.txt and snapshot_spec.md markdown...")
- spec_txt_string_split_examples = split_examples_into_html_and_md(spec_txt_string)
- snapshot_spec_md_string_split_examples = split_examples_into_html_and_md(snapshot_spec_md_string)
+ # NOTE: spec.txt only contains official GLFM examples, but snapshot_spec.md contains ALL examples, with the
+ # official GLFM examples coming _after_ the GHFM (which contains CommonMark + GHFM) examples, and the
+ # internal extension examples coming last. In the snapshot_spec.md, The CommonMark and GLFM examples come
+ # first, in order for the example numbers to match tne numbers in those separate specifications [1]. But, we
+ # also need for the numbering of the official examples in spec.txt to match the numbering of the official
+ # examples in snapshot_spec.md. Here's the ordering:
+ #
+ # spec.txt:
+ # 1. GLFM Official
+ #
+ # snapshot_spec.md:
+ # 1. GHFM (contains CommonMark + GHFM)
+ # 2. GLFM Official
+ # 3. GLFM Internal
+ #
+ # [1] Note that the example numbering in the GLFM spec.html is currently out of sync with its corresponding
+ # spec.txt because its rendering is out of date. This has been reported in the following issue:
+ # https://github.com/github/cmark-gfm/issues/288
+ ghfm_spec_examples_count = ghfm_spec_examples_string.scan(EXAMPLE_BEGIN_STRING).length
+
+ spec_txt_string_split_examples =
+ transform_examples_for_rendering(spec_txt_string, starting_example_number: ghfm_spec_examples_count + 1)
+ snapshot_spec_md_string_split_examples = transform_examples_for_rendering(snapshot_spec_md_string)
input_markdown_yml_string = <<~MARKDOWN
---
@@ -212,15 +267,46 @@ module Glfm
[rendered_html_hash.fetch(:spec_txt), rendered_html_hash.fetch(:snapshot_spec_md)]
end
- def split_examples_into_html_and_md(spec_md_string)
- spec_md_string.gsub(
- /(^#{EXAMPLE_BEGIN_STRING}.*?$(?:.|\n)*?)^\.$(\n(?:.|\n)*?^#{EXAMPLE_END_STRING}$)/mo,
- "\\1#{EXAMPLE_BACKTICKS_STRING}\n\n#{EXAMPLE_BACKTICKS_STRING}\\2"
- )
+ # NOTE: body, title, and version are used by the ERB binding.
+ # noinspection RubyUnusedLocalVariable
+ def add_styling_to_specification_html(body:, title:, version:)
+ # noinspection RubyMismatchedArgumentType
+ ERB.new(File.read(File.expand_path('specification_html_template.erb', __dir__))).result(binding)
+ end
+
+ def transform_examples_for_rendering(spec_md_string, starting_example_number: 1)
+ # This method:
+ # 1. Splits the single example code block which has a period between the markdown and HTML into two code blocks
+ # 2. Adds a wrapper div for use in styling and target for the example number named anchor. This will get the
+ # 'class="example" id="example-n"' attributes applied via javascript (since markdown rendering does not
+ # preserve classes or IDs)
+ # 3. Adds a div which includes the example number named anchor and text. This will get the 'class="examplenum"'
+ # attribute applied via javascript.
+ #
+ # NOTE: Even though they will get stripped durning markdown rendering, we will go ahead and add the class and id
+ # attributes here, for easier debugging and comparison to the source markdown.
+ example_replacement_regex = /(^#{EXAMPLE_BEGIN_STRING}.*?$(?:.|\n)*?)^\.$(\n(?:.|\n)*?^#{EXAMPLE_END_STRING}$)/mo
+ example_num = starting_example_number
+ spec_md_string.gsub(example_replacement_regex) do |_example_string|
+ markdown_part = ::Regexp.last_match(1)
+ html_part = ::Regexp.last_match(2)
+ example_anchor_name = "example-#{example_num}"
+ examplenum_div = %(<div class="examplenum"><a href="##{example_anchor_name}">Example #{example_num}</a></div>\n)
+ example_num += 1
+ # NOTE: We need blank lines before the markdown code blocks so they will be rendered properly
+ %(<div class="example" id="#{example_anchor_name}">\n) +
+ "#{examplenum_div}\n" \
+ "#{markdown_part}" \
+ "#{EXAMPLE_BACKTICKS_STRING}" \
+ "\n\n" \
+ "#{EXAMPLE_BACKTICKS_STRING}" \
+ "#{html_part}\n" \
+ '</div>'
+ end
end
def write_spec_html(spec_html_string)
- output("Writing #{GLFM_SPEC_TXT_PATH}...")
+ output("Writing #{GLFM_SPEC_HTML_PATH}...")
FileUtils.mkdir_p(Pathname.new(GLFM_SPEC_HTML_PATH).dirname)
write_file(GLFM_SPEC_HTML_PATH, "#{spec_html_string}\n")
end
diff --git a/scripts/lint-doc-quality.sh b/scripts/lint-doc-quality.sh
new file mode 100755
index 00000000000..9d8409a7c80
--- /dev/null
+++ b/scripts/lint-doc-quality.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+echo '=> Generating code quality artifact...'
+echo
+
+# Generate code quality artifact for Vale warnings only on changed files.
+# Only works on merged results pipelines, so first checks if a merged results CI variable is present.
+# If not present, runs on all files.
+
+if [ -z "${CI_MERGE_REQUEST_TARGET_BRANCH_SHA}" ]
+then
+ MD_DOC_PATH=${MD_DOC_PATH:-doc}
+ echo "Merge request pipeline (detached) detected. Testing all files."
+else
+ MERGE_BASE=$(git merge-base "${CI_MERGE_REQUEST_TARGET_BRANCH_SHA}" "${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}")
+ MD_DOC_PATH=$(git diff --diff-filter=d --name-only "${MERGE_BASE}..${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" -- 'doc/*.md')
+ if [ -n "${MD_DOC_PATH}" ]
+ then
+ echo -e "Merged results pipeline detected. Testing only the following files: ${MD_DOC_PATH}"
+ fi
+fi
+
+echo "vale --output=doc/.vale/vale-json.tmpl --minAlertLevel warning ${MD_DOC_PATH} > gl-code-quality-report-docs.json"
+vale --output=doc/.vale/vale-json.tmpl --minAlertLevel warning ${MD_DOC_PATH} > gl-code-quality-report-docs.json
diff --git a/scripts/review_apps/automated_cleanup.rb b/scripts/review_apps/automated_cleanup.rb
index f020283de52..2d87c18d7d2 100755
--- a/scripts/review_apps/automated_cleanup.rb
+++ b/scripts/review_apps/automated_cleanup.rb
@@ -22,6 +22,7 @@ module ReviewApps
IGNORED_KUBERNETES_ERRORS = [
'NotFound'
].freeze
+ ENVIRONMENTS_NOT_FOUND_THRESHOLD = 3
# $GITLAB_PROJECT_REVIEW_APP_CLEANUP_API_TOKEN => `Automated Review App Cleanup` project token
def initialize(
@@ -30,10 +31,11 @@ module ReviewApps
api_endpoint: ENV['CI_API_V4_URL'],
options: {}
)
- @project_path = project_path
- @gitlab_token = gitlab_token
- @api_endpoint = api_endpoint
- @dry_run = options[:dry_run]
+ @project_path = project_path
+ @gitlab_token = gitlab_token
+ @api_endpoint = api_endpoint
+ @dry_run = options[:dry_run]
+ @environments_not_found_count = 0
puts "Dry-run mode." if dry_run
end
@@ -91,13 +93,11 @@ module ReviewApps
release = Tooling::Helm3Client::Release.new(environment.slug, 1, deployed_at.to_s, nil, nil, environment.slug)
releases_to_delete << release
end
+ elsif deployed_at >= stop_threshold
+ print_release_state(subject: 'Review App', release_name: environment.slug, release_date: last_deploy, action: 'leaving')
else
- if deployed_at >= stop_threshold
- print_release_state(subject: 'Review App', release_name: environment.slug, release_date: last_deploy, action: 'leaving')
- else
- environment_state = fetch_environment(environment)&.state
- stop_environment(environment, deployment) if environment_state && environment_state != 'stopped'
- end
+ environment_state = fetch_environment(environment)&.state
+ stop_environment(environment, deployment) if environment_state && environment_state != 'stopped'
end
checked_environments << environment.slug
@@ -174,7 +174,7 @@ module ReviewApps
private
- attr_reader :project_path, :gitlab_token, :api_endpoint, :dry_run
+ attr_reader :api_endpoint, :dry_run, :gitlab_token, :project_path
def fetch_environment(environment)
gitlab.environment(project_path, environment.id)
@@ -188,10 +188,17 @@ module ReviewApps
print_release_state(subject: 'Review app', release_name: environment.slug, release_date: release_date, action: 'deleting')
gitlab.delete_environment(project_path, environment.id) unless dry_run
+ rescue Gitlab::Error::NotFound
+ puts "Review app '#{environment.name}' / '#{environment.slug}' (##{environment.id}) was not found: ignoring it"
+ @environments_not_found_count += 1
+
+ if @environments_not_found_count >= ENVIRONMENTS_NOT_FOUND_THRESHOLD
+ raise "At least #{ENVIRONMENTS_NOT_FOUND_THRESHOLD} environments were missing when we tried to delete them. Please investigate"
+ end
rescue Gitlab::Error::Forbidden
puts "Review app '#{environment.name}' / '#{environment.slug}' (##{environment.id}) is forbidden: skipping it"
rescue Gitlab::Error::InternalServerError
- puts "Review app '#{environment.name}' / '#{environment.slug}' (##{environment.id}) 500 error - ignoring it"
+ puts "Review app '#{environment.name}' / '#{environment.slug}' (##{environment.id}) 500 error: ignoring it"
end
def stop_environment(environment, deployment)
diff --git a/scripts/review_apps/base-config.yaml b/scripts/review_apps/base-config.yaml
index f845dd04e8f..0981aafec22 100644
--- a/scripts/review_apps/base-config.yaml
+++ b/scripts/review_apps/base-config.yaml
@@ -69,8 +69,8 @@ gitlab:
cpu: 400m
memory: 920Mi
limits:
- cpu: 600m
- memory: 1100Mi
+ cpu: 800m
+ memory: 1380Mi
sidekiq:
resources:
@@ -99,7 +99,7 @@ gitlab:
cpu: 746m
memory: 2809Mi
limits:
- cpu: 1119m
+ cpu: 1300m
memory: 4214Mi
minReplicas: 1
maxReplicas: 1
diff --git a/scripts/review_apps/gcp-quotas-checks.rb b/scripts/review_apps/gcp-quotas-checks.rb
new file mode 100755
index 00000000000..187277f87ea
--- /dev/null
+++ b/scripts/review_apps/gcp-quotas-checks.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+# We created this because we could not monitor k8s resource count directly in GCP monitoring (see
+# https://gitlab.com/gitlab-org/quality/engineering-productivity-infrastructure/-/issues/37)
+#
+# If this functionality ever becomes available, please replace this script with GCP monitoring!
+
+require 'json'
+
+class QuotaChecker
+ def initialize
+ @exit_with_error = false
+ end
+
+ def check_quotas(quotas, threshold: 0.8)
+ quotas.each do |quota|
+ print "Checking quota #{quota['metric']}..."
+ quota_percent_usage = quota['usage'].to_f / quota['limit']
+ if quota_percent_usage > threshold
+ puts "⌠#{quota['metric']} is above the #{threshold * 100}% threshold! (current value: #{quota_percent_usage})"
+ @exit_with_error = true
+ else
+ puts "✅"
+ end
+ end
+ end
+
+ def failed?
+ @exit_with_error
+ end
+end
+
+quota_checker = QuotaChecker.new
+
+puts "Checking regional quotas:"
+gcloud_command_output = `gcloud compute regions describe us-central1 --format=json`
+quotas = JSON.parse(gcloud_command_output)['quotas']
+quota_checker.check_quotas(quotas)
+puts
+
+puts "Checking project-wide quotas:"
+gcloud_command_output = `gcloud compute project-info describe --format=json`
+quotas = JSON.parse(gcloud_command_output)['quotas']
+quota_checker.check_quotas(quotas)
+
+exit 1 if quota_checker.failed?
diff --git a/scripts/review_apps/gcp_cleanup.sh b/scripts/review_apps/gcp_cleanup.sh
deleted file mode 100755
index 114ac6f7ec0..00000000000
--- a/scripts/review_apps/gcp_cleanup.sh
+++ /dev/null
@@ -1,160 +0,0 @@
-#!/usr/bin/env bash
-
-source scripts/utils.sh
-
-function setup_gcp_dependencies() {
- apt-get update && apt-get install -y jq
-
- gcloud auth activate-service-account --key-file="${REVIEW_APPS_GCP_CREDENTIALS}"
- gcloud config set project "${REVIEW_APPS_GCP_PROJECT}"
-}
-
-# These scripts require the following environment variables:
-# - REVIEW_APPS_GCP_REGION - e.g `us-central1`
-# - KUBE_NAMESPACE - e.g `review-apps`
-
-function delete_firewall_rules() {
- if [[ ${#@} -eq 0 ]]; then
- echoinfo "No firewall rules to be deleted" true
- return
- fi
-
- echoinfo "Deleting firewall rules:" true
- echo "${@}"
-
- if [[ ${DRY_RUN} = 1 ]]; then
- echo "[DRY RUN] gcloud compute firewall-rules delete -q" "${@}"
- else
- gcloud compute firewall-rules delete -q "${@}"
- fi
-}
-
-function delete_forwarding_rules() {
- if [[ ${#@} -eq 0 ]]; then
- echoinfo "No forwarding rules to be deleted" true
- return
- fi
-
- echoinfo "Deleting forwarding rules:" true
- echo "${@}"
-
- if [[ ${DRY_RUN} = 1 ]]; then
- echo "[DRY RUN] gcloud compute forwarding-rules delete -q" "${@}" "--region ${REVIEW_APPS_GCP_REGION}"
- else
- gcloud compute forwarding-rules delete -q "${@}" --region "${REVIEW_APPS_GCP_REGION}"
- fi
-}
-
-function delete_target_pools() {
- if [[ ${#@} -eq 0 ]]; then
- echoinfo "No target pools to be deleted" true
- return
- fi
-
- echoinfo "Deleting target pools:" true
- echo "${@}"
-
- if [[ ${DRY_RUN} = 1 ]]; then
- echo "[DRY RUN] gcloud compute target-pools delete -q" "${@}" "--region ${REVIEW_APPS_GCP_REGION}"
- else
- gcloud compute target-pools delete -q "${@}" --region "${REVIEW_APPS_GCP_REGION}"
- fi
-}
-
-function delete_http_health_checks() {
- if [[ ${#@} -eq 0 ]]; then
- echoinfo "No http health checks to be deleted" true
- return
- fi
-
- echoinfo "Deleting http health checks:" true
- echo "${@}"
-
- if [[ ${DRY_RUN} = 1 ]]; then
- echo "[DRY RUN] gcloud compute http-health-checks delete -q" "${@}"
- else
- gcloud compute http-health-checks delete -q "${@}"
- fi
-}
-
-function get_related_firewall_rules() {
- local forwarding_rule=${1}
-
- gcloud compute firewall-rules list --filter "name~${forwarding_rule}" --format "value(name)"
-}
-
-function get_service_name_in_forwarding_rule() {
- local forwarding_rule=${1}
-
- gcloud compute forwarding-rules describe "${forwarding_rule}" --region "${REVIEW_APPS_GCP_REGION}" --format "value(description)" | jq -r '.["kubernetes.io/service-name"]'
-}
-
-function forwarding_rule_k8s_service_exists() {
- local namespace="${KUBE_NAMESPACE}"
- local namespaced_service_name=$(get_service_name_in_forwarding_rule "$forwarding_rule")
-
- if [[ ! $namespaced_service_name =~ ^"${namespace}" ]]; then
- return 0 # this prevents `review-apps-ee` pipeline from deleting `review-apps-ce` resources and vice versa
- fi
-
- local service_name=$(echo "${namespaced_service_name}" | sed -e "s/${namespace}\///g")
-
- kubectl get svc "${service_name}" -n "${namespace}" >/dev/null 2>&1
- local status=$?
-
- return $status
-}
-
-function gcp_cleanup() {
- if [[ ! $(command -v kubectl) ]]; then
- echoerr "kubectl executable not found"
- return 1
- fi
-
- if [[ -z "${REVIEW_APPS_GCP_REGION}" ]]; then
- echoerr "REVIEW_APPS_GCP_REGION is not set."
- return 1
- fi
-
- if [[ -z "${KUBE_NAMESPACE}" ]]; then
- echoerr "KUBE_NAMESPACE is not set."
- return 1
- fi
-
- if [[ -n "${DRY_RUN}" ]]; then
- echoinfo "Running in DRY_RUN"
- fi
-
- local target_pools_to_delete=()
- local firewall_rules_to_delete=()
- local forwarding_rules_to_delete=()
- local http_health_checks_to_delete=()
-
- for forwarding_rule in $(gcloud compute forwarding-rules list --filter="region:(${REVIEW_APPS_GCP_REGION})" --format "value(name)"); do
- echoinfo "Inspecting forwarding rule ${forwarding_rule}" true
-
- # We perform clean up when there is no more kubernetes service that require the resources.
- # To identify the kubernetes service using the resources,
- # we find the service name indicated in the forwarding rule description, e.g:
- #
- # $ gcloud compute forwarding-rules describe aff68b997da1211e984a042010af0019
- # # ...
- # description: '{"kubernetes.io/service-name":"review-apps-ee/review-winh-eslin-809vqz-nginx-ingress-controller"}'
- # # ...
- if forwarding_rule_k8s_service_exists "${forwarding_rule}"; then
- echoinfo "Skip clean up for ${forwarding_rule}"
- else
- echoinfo "Queuing forwarding rule, firewall rule, target pool and health check for ${forwarding_rule} to be cleaned up"
-
- firewall_rules_to_delete+=($(get_related_firewall_rules "${forwarding_rule}"))
- forwarding_rules_to_delete+=(${forwarding_rule})
- target_pools_to_delete+=(${forwarding_rule})
- http_health_checks_to_delete+=(${forwarding_rule})
- fi
- done
-
- delete_firewall_rules "${firewall_rules_to_delete[@]}"
- delete_forwarding_rules "${forwarding_rules_to_delete[@]}"
- delete_target_pools "${target_pools_to_delete[@]}"
- delete_http_health_checks "${http_health_checks_to_delete[@]}"
-}
diff --git a/scripts/review_apps/k8s-resources-count-checks.sh b/scripts/review_apps/k8s-resources-count-checks.sh
new file mode 100755
index 00000000000..b63fa043065
--- /dev/null
+++ b/scripts/review_apps/k8s-resources-count-checks.sh
@@ -0,0 +1,90 @@
+#!/usr/bin/env bash
+
+# We created this because we could not monitor quotas easily in GCP monitoring (see
+# https://gitlab.com/gitlab-org/quality/engineering-productivity-infrastructure/-/issues/37)
+#
+# If this functionality ever becomes available, please replace this script with GCP monitoring!
+
+function k8s_resource_count() {
+ local resource_name="${1}"
+
+ kubectl get -A "${resource_name}" 2> /dev/null | wc -l | xargs
+}
+
+# ~13 services per review-app - ~230 review apps
+SERVICES_COUNT_THRESHOLD=3000
+REVIEW_APPS_COUNT_THRESHOLD=200
+
+exit_with_error=false
+
+# In the current GKE cluster configuration, we should never go higher than 4096 services per cluster.
+services_count=$(kubectl get services -A | wc -l | xargs)
+if [ "${services_count}" -gt "${SERVICES_COUNT_THRESHOLD}" ]; then
+ >&2 echo "⌠[ERROR] Services are above ${SERVICES_COUNT_THRESHOLD} (currently at ${services_count})"
+ exit_with_error=true
+fi
+
+review_apps_count=$(helm ls -A | wc -l | xargs)
+if [ "${review_apps_count}" -gt "${REVIEW_APPS_COUNT_THRESHOLD}" ]; then
+ >&2 echo "⌠[ERROR] Review apps count are above ${REVIEW_APPS_COUNT_THRESHOLD} (currently at ${review_apps_count})"
+ exit_with_error=true
+fi
+
+namespaces_count=$(kubectl get namespaces -A | wc -l | xargs)
+if [ "$(echo $(($namespaces_count - $review_apps_count)) | sed 's/-//')" -gt 30 ]; then
+ >&2 echo "⌠[ERROR] Difference between namespaces and deployed review-apps is above 30 (${namespaces_count} namespaces and ${review_apps_count} review-apps)"
+ exit_with_error=true
+fi
+
+if [ "${exit_with_error}" = true ] ; then
+ exit 1
+fi
+
+echo -e "\nShow k8s resources count: "
+cat > k8s-resources-count.out <<COMMANDS
+ $(k8s_resource_count backendconfigs.cloud.google.com) backendconfigs.cloud.google.com
+ $(k8s_resource_count capacityrequests.internal.autoscaling.gke.io) capacityrequests.internal.autoscaling.gke.io
+ $(k8s_resource_count capacityrequests.internal.autoscaling.k8s.io) capacityrequests.internal.autoscaling.k8s.io
+ $(k8s_resource_count certificaterequests.cert-manager.io) certificaterequests.cert-manager.io
+ $(k8s_resource_count certificates.cert-manager.io) certificates.cert-manager.io
+ $(k8s_resource_count challenges.acme.cert-manager.io) challenges.acme.cert-manager.io
+ $(k8s_resource_count configmaps) configmaps
+ $(k8s_resource_count containerwatcherstatuses.containerthreatdetection.googleapis.com) containerwatcherstatuses.containerthreatdetection.googleapis.com
+ $(k8s_resource_count controllerrevisions.apps) controllerrevisions.apps
+ $(k8s_resource_count cronjobs.batch) cronjobs.batch
+ $(k8s_resource_count csistoragecapacities.storage.k8s.io) csistoragecapacities.storage.k8s.io
+ $(k8s_resource_count daemonsets.apps) daemonsets.apps
+ $(k8s_resource_count deployments.apps) deployments.apps
+ $(k8s_resource_count endpoints) endpoints
+ $(k8s_resource_count frontendconfigs.networking.gke.io) frontendconfigs.networking.gke.io
+ $(k8s_resource_count horizontalpodautoscalers.autoscaling) horizontalpodautoscalers.autoscaling
+ $(k8s_resource_count ingressclasses) ingressclasses
+ $(k8s_resource_count ingresses.networking.k8s.io) ingresses.networking.k8s.io
+ $(k8s_resource_count issuers.cert-manager.io) issuers.cert-manager.io
+ $(k8s_resource_count jobs.batch) jobs.batch
+ $(k8s_resource_count leases.coordination.k8s.io) leases.coordination.k8s.io
+ $(k8s_resource_count limitranges) limitranges
+ $(k8s_resource_count managedcertificates.networking.gke.io) managedcertificates.networking.gke.io
+ $(k8s_resource_count networkpolicies.networking.k8s.io) networkpolicies.networking.k8s.io
+ $(k8s_resource_count orders.acme.cert-manager.io) orders.acme.cert-manager.io
+ $(k8s_resource_count persistentvolumeclaims) persistentvolumeclaims
+ $(k8s_resource_count poddisruptionbudgets.policy) poddisruptionbudgets.policy
+ $(k8s_resource_count pods) pods
+ $(k8s_resource_count podtemplates) podtemplates
+ $(k8s_resource_count replicasets.apps) replicasets.apps
+ $(k8s_resource_count replicationcontrollers) replicationcontrollers
+ $(k8s_resource_count resourcequotas) resourcequotas
+ $(k8s_resource_count rolebindings.rbac.authorization.k8s.io) rolebindings.rbac.authorization.k8s.io
+ $(k8s_resource_count roles.rbac.authorization.k8s.io) roles.rbac.authorization.k8s.io
+ $(k8s_resource_count scalingpolicies.scalingpolicy.kope.io) scalingpolicies.scalingpolicy.kope.io
+ $(k8s_resource_count secrets) secrets
+ $(k8s_resource_count serviceaccounts) serviceaccounts
+ $(k8s_resource_count serviceattachments.networking.gke.io) serviceattachments.networking.gke.io
+ $(k8s_resource_count servicenetworkendpointgroups.networking.gke.io) servicenetworkendpointgroups.networking.gke.io
+ $(k8s_resource_count services) services
+ $(k8s_resource_count statefulsets.apps) statefulsets.apps
+ $(k8s_resource_count updateinfos.nodemanagement.gke.io) updateinfos.nodemanagement.gke.io
+ $(k8s_resource_count volumesnapshots.snapshot.storage.k8s.io) volumesnapshots.snapshot.storage.k8s.io
+COMMANDS
+
+sort --reverse --numeric-sort < k8s-resources-count.out
diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh
index 5883141a943..e185ed43e38 100755
--- a/scripts/review_apps/review-apps.sh
+++ b/scripts/review_apps/review-apps.sh
@@ -196,9 +196,9 @@ function create_application_secret() {
if [ -z "${REVIEW_APPS_EE_LICENSE_FILE}" ]; then echo "License not found" && return; fi
- gitlab_license_shared_secret=$(kubectl get secret --namespace ${namespace} --no-headers -o=custom-columns=NAME:.metadata.name shared-gitlab-license 2> /dev/null | tail -n 1)
+ gitlab_license_shared_secret=$(kubectl get secret --namespace "${namespace}" --no-headers -o=custom-columns=NAME:.metadata.name shared-gitlab-license 2> /dev/null | tail -n 1)
if [[ "${gitlab_license_shared_secret}" == "" ]]; then
- echoinfo "Creating the 'shared-gitlab-license' secret in the ${namespace} namespace..." true
+ echoinfo "Creating the 'shared-gitlab-license' secret in the "${namespace}" namespace..." true
kubectl create secret generic --namespace "${namespace}" \
"shared-gitlab-license" \
--from-file=license="${REVIEW_APPS_EE_LICENSE_FILE}" \
@@ -262,7 +262,7 @@ function deploy() {
gitlab_workhorse_image_repository="${IMAGE_REPOSITORY}/gitlab-workhorse-ee"
sentry_enabled="false"
- if [ -n ${REVIEW_APPS_SENTRY_DSN} ]; then
+ if [ -n "${REVIEW_APPS_SENTRY_DSN}" ]; then
echo "REVIEW_APPS_SENTRY_DSN detected, enabling Sentry"
sentry_enabled="true"
fi
@@ -342,11 +342,25 @@ EOF
}
function verify_deploy() {
- local namespace="${CI_ENVIRONMENT_SLUG}"
+ local deployed="false"
+
+ mkdir -p curl-logs/
- echoinfo "[$(date '+%H:%M:%S')] Verifying deployment at ${CI_ENVIRONMENT_URL}"
+ for i in {1..60}; do # try for 5 minutes
+ local now=$(date '+%H:%M:%S')
+ echo "[${now}] Verifying deployment at ${CI_ENVIRONMENT_URL}/users/sign_in"
+ log_name="curl-logs/${now}.log"
+ curl --connect-timeout 3 -o "${log_name}" -s "${CI_ENVIRONMENT_URL}/users/sign_in"
+
+ if grep "Remember me" "${log_name}" &> /dev/null; then
+ deployed="true"
+ break
+ fi
+
+ sleep 5
+ done
- if retry "test_url \"${CI_ENVIRONMENT_URL}\""; then
+ if [[ "${deployed}" == "true" ]]; then
echoinfo "[$(date '+%H:%M:%S')] Review app is deployed to ${CI_ENVIRONMENT_URL}"
return 0
else
@@ -358,6 +372,26 @@ function verify_deploy() {
function display_deployment_debug() {
local namespace="${CI_ENVIRONMENT_SLUG}"
- echoinfo "Environment debugging data:"
- kubectl get svc,pods,jobs --namespace "${namespace}"
+ # Install dig to inspect DNS entries
+ #
+ # Silent install: see https://stackoverflow.com/a/52642167/1620195
+ apt-get -qq update && apt-get -qq install -y dnsutils < /dev/null > /dev/null
+
+ echoinfo "[debugging data] Check review-app webservice DNS entry:"
+ dig +short $(echo "${CI_ENVIRONMENT_URL}" | sed 's~http[s]*://~~g')
+
+ echoinfo "[debugging data] Check external IP for nginx-ingress-controller service (should be THE SAME AS the DNS entry IP above):"
+ kubectl -n "${namespace}" get svc "${namespace}-nginx-ingress-controller" -o jsonpath='{.status.loadBalancer.ingress[].ip}'
+
+ echoinfo "[debugging data] k8s resources:"
+ kubectl -n "${namespace}" get pods
+
+ echoinfo "[debugging data] PostgreSQL logs:"
+ kubectl -n "${namespace}" logs -l app=postgresql --all-containers
+
+ echoinfo "[debugging data] DB migrations logs:"
+ kubectl -n "${namespace}" logs -l app=migrations --all-containers
+
+ echoinfo "[debugging data] Webservice logs:"
+ kubectl -n "${namespace}" logs -l app=webservice -c webservice
}
diff --git a/scripts/rspec_helpers.sh b/scripts/rspec_helpers.sh
index 14c5b94e921..923b633fcc9 100644
--- a/scripts/rspec_helpers.sh
+++ b/scripts/rspec_helpers.sh
@@ -3,44 +3,14 @@
function retrieve_tests_metadata() {
mkdir -p $(dirname "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}") $(dirname "${FLAKY_RSPEC_SUITE_REPORT_PATH}") "${RSPEC_PROFILING_FOLDER_PATH}"
- if [[ -n "${RETRIEVE_TESTS_METADATA_FROM_PAGES}" ]]; then
- if [[ ! -f "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" ]]; then
- curl --location -o "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" "https://gitlab-org.gitlab.io/gitlab/${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" ||
- echo "{}" > "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}"
- fi
-
- if [[ ! -f "${FLAKY_RSPEC_SUITE_REPORT_PATH}" ]]; then
- curl --location -o "${FLAKY_RSPEC_SUITE_REPORT_PATH}" "https://gitlab-org.gitlab.io/gitlab/${FLAKY_RSPEC_SUITE_REPORT_PATH}" ||
- echo "{}" > "${FLAKY_RSPEC_SUITE_REPORT_PATH}"
- fi
- else
- # ${CI_DEFAULT_BRANCH} might not be master in other forks but we want to
- # always target the canonical project here, so the branch must be hardcoded
- local project_path="gitlab-org/gitlab"
- local artifact_branch="master"
- local username="gitlab-bot"
- local job_name="update-tests-metadata"
- local test_metadata_job_id
-
- # Ruby
- test_metadata_job_id=$(scripts/api/get_job_id.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" -q "status=success" -q "ref=${artifact_branch}" -q "username=${username}" -Q "scope=success" --job-name "${job_name}")
-
- if [[ -n "${test_metadata_job_id}" ]]; then
- echo "test_metadata_job_id: ${test_metadata_job_id}"
-
- if [[ ! -f "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" ]]; then
- scripts/api/download_job_artifact.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" --job-id "${test_metadata_job_id}" --artifact-path "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" || echo "{}" > "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}"
- fi
-
- if [[ ! -f "${FLAKY_RSPEC_SUITE_REPORT_PATH}" ]]; then
- scripts/api/download_job_artifact.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" --job-id "${test_metadata_job_id}" --artifact-path "${FLAKY_RSPEC_SUITE_REPORT_PATH}" ||
- echo "{}" > "${FLAKY_RSPEC_SUITE_REPORT_PATH}"
- fi
- else
- echo "test_metadata_job_id couldn't be found!"
+ if [[ ! -f "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" ]]; then
+ curl --location -o "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" "https://gitlab-org.gitlab.io/gitlab/${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" ||
echo "{}" > "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}"
+ fi
+
+ if [[ ! -f "${FLAKY_RSPEC_SUITE_REPORT_PATH}" ]]; then
+ curl --location -o "${FLAKY_RSPEC_SUITE_REPORT_PATH}" "https://gitlab-org.gitlab.io/gitlab/${FLAKY_RSPEC_SUITE_REPORT_PATH}" ||
echo "{}" > "${FLAKY_RSPEC_SUITE_REPORT_PATH}"
- fi
fi
}
@@ -74,31 +44,8 @@ function update_tests_metadata() {
function retrieve_tests_mapping() {
mkdir -p $(dirname "$RSPEC_PACKED_TESTS_MAPPING_PATH")
- if [[ -n "${RETRIEVE_TESTS_METADATA_FROM_PAGES}" ]]; then
- if [[ ! -f "${RSPEC_PACKED_TESTS_MAPPING_PATH}" ]]; then
- (curl --location -o "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz" "https://gitlab-org.gitlab.io/gitlab/${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz" && gzip -d "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz") || echo "{}" > "${RSPEC_PACKED_TESTS_MAPPING_PATH}"
- fi
- else
- # ${CI_DEFAULT_BRANCH} might not be master in other forks but we want to
- # always target the canonical project here, so the branch must be hardcoded
- local project_path="gitlab-org/gitlab"
- local artifact_branch="master"
- local username="gitlab-bot"
- local job_name="update-tests-metadata"
- local test_metadata_with_mapping_job_id
-
- test_metadata_with_mapping_job_id=$(scripts/api/get_job_id.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" -q "status=success" -q "ref=${artifact_branch}" -q "username=${username}" -Q "scope=success" --job-name "${job_name}")
-
- if [[ -n "${test_metadata_with_mapping_job_id}" ]]; then
- echo "test_metadata_with_mapping_job_id: ${test_metadata_with_mapping_job_id}"
-
- if [[ ! -f "${RSPEC_PACKED_TESTS_MAPPING_PATH}" ]]; then
- (scripts/api/download_job_artifact.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" --job-id "${test_metadata_with_mapping_job_id}" --artifact-path "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz" && gzip -d "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz") || echo "{}" > "${RSPEC_PACKED_TESTS_MAPPING_PATH}"
- fi
- else
- echo "test_metadata_with_mapping_job_id couldn't be found!"
- echo "{}" > "${RSPEC_PACKED_TESTS_MAPPING_PATH}"
- fi
+ if [[ ! -f "${RSPEC_PACKED_TESTS_MAPPING_PATH}" ]]; then
+ (curl --location -o "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz" "https://gitlab-org.gitlab.io/gitlab/${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz" && gzip -d "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz") || echo "{}" > "${RSPEC_PACKED_TESTS_MAPPING_PATH}"
fi
scripts/unpack-test-mapping "${RSPEC_PACKED_TESTS_MAPPING_PATH}" "${RSPEC_TESTS_MAPPING_PATH}"
@@ -107,31 +54,8 @@ function retrieve_tests_mapping() {
function retrieve_frontend_fixtures_mapping() {
mkdir -p $(dirname "$FRONTEND_FIXTURES_MAPPING_PATH")
- if [[ -n "${RETRIEVE_TESTS_METADATA_FROM_PAGES}" ]]; then
- if [[ ! -f "${FRONTEND_FIXTURES_MAPPING_PATH}" ]]; then
- (curl --location -o "${FRONTEND_FIXTURES_MAPPING_PATH}" "https://gitlab-org.gitlab.io/gitlab/${FRONTEND_FIXTURES_MAPPING_PATH}") || echo "{}" > "${FRONTEND_FIXTURES_MAPPING_PATH}"
- fi
- else
- # ${CI_DEFAULT_BRANCH} might not be master in other forks but we want to
- # always target the canonical project here, so the branch must be hardcoded
- local project_path="gitlab-org/gitlab"
- local artifact_branch="master"
- local username="gitlab-bot"
- local job_name="generate-frontend-fixtures-mapping"
- local test_metadata_with_mapping_job_id
-
- test_metadata_with_mapping_job_id=$(scripts/api/get_job_id.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" -q "ref=${artifact_branch}" -q "username=${username}" -Q "scope=success" --job-name "${job_name}")
-
- if [[ $? -eq 0 ]] && [[ -n "${test_metadata_with_mapping_job_id}" ]]; then
- echo "test_metadata_with_mapping_job_id: ${test_metadata_with_mapping_job_id}"
-
- if [[ ! -f "${FRONTEND_FIXTURES_MAPPING_PATH}" ]]; then
- (scripts/api/download_job_artifact.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" --job-id "${test_metadata_with_mapping_job_id}" --artifact-path "${FRONTEND_FIXTURES_MAPPING_PATH}") || echo "{}" > "${FRONTEND_FIXTURES_MAPPING_PATH}"
- fi
- else
- echo "test_metadata_with_mapping_job_id couldn't be found!"
- echo "{}" > "${FRONTEND_FIXTURES_MAPPING_PATH}"
- fi
+ if [[ ! -f "${FRONTEND_FIXTURES_MAPPING_PATH}" ]]; then
+ (curl --location -o "${FRONTEND_FIXTURES_MAPPING_PATH}" "https://gitlab-org.gitlab.io/gitlab/${FRONTEND_FIXTURES_MAPPING_PATH}") || echo "{}" > "${FRONTEND_FIXTURES_MAPPING_PATH}"
fi
}
@@ -324,9 +248,40 @@ function retry_failed_rspec_examples() {
# Merge the JUnit report from retry into the first-try report
junit_merge "${JUNIT_RETRY_FILE}" "${JUNIT_RESULT_FILE}" --update-only
+ if [[ $rspec_run_status -eq 0 ]]; then
+ # The test is flaky because it succeeded after being retried.
+ # Make the pipeline "pass with warnings" if the flaky test is part of this MR.
+ warn_on_successfully_retried_test
+ fi
+
exit $rspec_run_status
}
+# Exit with an allowed_failure exit code if the flaky test was part of the MR that triggered this pipeline
+function warn_on_successfully_retried_test {
+ local changed_files=$(git diff --name-only $CI_MERGE_REQUEST_TARGET_BRANCH_SHA | grep spec)
+ echoinfo "A test was flaky and succeeded after being retried. Checking to see if flaky test is part of this MR..."
+
+ if [[ "$changed_files" == "" ]]; then
+ echoinfo "Flaky test was not part of this MR."
+ return
+ fi
+
+ while read changed_file
+ do
+ # include the root path in the regexp to eliminate false positives
+ changed_file="^\./$changed_file"
+
+ if grep -q "$changed_file" "$RETRIED_TESTS_REPORT_PATH"; then
+ echoinfo "Flaky test '$changed_file' was found in the list of files changed by this MR."
+ echoinfo "Exiting with code $SUCCESSFULLY_RETRIED_TEST_EXIT_CODE."
+ exit $SUCCESSFULLY_RETRIED_TEST_EXIT_CODE
+ fi
+ done <<< "$changed_files"
+
+ echoinfo "Flaky test was not part of this MR."
+}
+
function rspec_rerun_previous_failed_tests() {
local test_file_count_threshold=${RSPEC_PREVIOUS_FAILED_TEST_FILE_COUNT_THRESHOLD:-10}
local matching_tests_file=${1}
diff --git a/scripts/rubocop-max-files-in-cache-check b/scripts/rubocop-max-files-in-cache-check
deleted file mode 100755
index 34caa0e197c..00000000000
--- a/scripts/rubocop-max-files-in-cache-check
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-require_relative '../config/bundler_setup'
-require 'rubocop'
-
-MINIMUM_MAX_FILES_IN_CACHE_MARGIN = 1.05
-RECOMMENDED_MAX_FILES_IN_CACHE_MARGIN = 1.25
-RUBOCOP_LIST_TARGET_FILES_COMMAND = 'bundle exec rubocop --list-target-files | wc -l'
-
-RuboCopMaxFilesInCacheIsTooSmall = Class.new(StandardError)
-
-rubocop_target_files_count = `#{RUBOCOP_LIST_TARGET_FILES_COMMAND}`.strip.to_i
-
-raise Error, "#{RUBOCOP_LIST_TARGET_FILES_COMMAND} failed with status #{$?}!" if rubocop_target_files_count == 0
-
-rubocop_target_files_count = rubocop_target_files_count.to_i
-rubocop_current_max_files_in_cache = RuboCop::ConfigLoader.load_yaml_configuration(File.expand_path('../.rubocop.yml', __dir__)).dig('AllCops', 'MaxFilesInCache').to_i
-minimum_max_files_in_cache = (rubocop_target_files_count * MINIMUM_MAX_FILES_IN_CACHE_MARGIN).round(-3)
-
-# We want AllCops.MaxFilesInCache to be at least 5% above the actual files count at any time to give us enough time to increase it accordingly
-if rubocop_current_max_files_in_cache <= minimum_max_files_in_cache
- recommended_max_files_in_cache = (rubocop_target_files_count * RECOMMENDED_MAX_FILES_IN_CACHE_MARGIN).round(-3)
- raise RuboCopMaxFilesInCacheIsTooSmall, "Current count of RuboCop target file is #{rubocop_target_files_count} but AllCops.MaxFilesInCache is set to #{rubocop_current_max_files_in_cache}. We recommend to increase it to #{recommended_max_files_in_cache}."
-else
- puts "Current count of RuboCop target file is #{rubocop_target_files_count} and AllCops.MaxFilesInCache is set to #{rubocop_current_max_files_in_cache}. All good."
- exit(0)
-end
diff --git a/scripts/static-analysis b/scripts/static-analysis
index c6cf09e056b..9a0057d8f4d 100755
--- a/scripts/static-analysis
+++ b/scripts/static-analysis
@@ -50,7 +50,6 @@ class StaticAnalysis
Task.new(%w[bin/rake gettext:lint], 105),
Task.new(%W[scripts/license-check.sh #{project_path}], 200),
Task.new(%w[bin/rake lint:static_verification], 40),
- Task.new(%w[scripts/rubocop-max-files-in-cache-check], 25),
Task.new(%w[bin/rake config_lint], 10),
Task.new(%w[bin/rake gitlab:sidekiq:all_queues_yml:check], 15),
(Gitlab.ee? ? Task.new(%w[bin/rake gitlab:sidekiq:sidekiq_queues_yml:check], 11) : nil),
@@ -134,7 +133,7 @@ class StaticAnalysis
def warning_count(static_analysis)
static_analysis.warned_results
- .count { |result| !ALLOWED_WARNINGS.include?(result.stderr.strip) }
+ .count { |result| !ALLOWED_WARNINGS.include?(result.stderr.strip) } # rubocop:disable Rails/NegateInclude
end
def tasks_to_run(node_total)
diff --git a/scripts/trigger-build.rb b/scripts/trigger-build.rb
index 897ca9f473e..411e5ed13c6 100755
--- a/scripts/trigger-build.rb
+++ b/scripts/trigger-build.rb
@@ -153,13 +153,15 @@ module Trigger
# Read version files from all components
def version_file_variables
- Dir.glob("*_VERSION").each_with_object({}) do |version_file, params|
+ Dir.glob("*_VERSION").each_with_object({}) do |version_file, params| # rubocop:disable Rails/IndexWith
params[version_file] = version_param_value(version_file)
end
end
end
class CNG < Base
+ ASSETS_HASH = "cached-assets-hash.txt"
+
def variables
# Delete variables that aren't useful when using native triggers.
super.tap do |hash|
@@ -187,7 +189,6 @@ module Trigger
"TRIGGER_BRANCH" => ref,
"GITLAB_VERSION" => ENV['CI_COMMIT_SHA'],
"GITLAB_TAG" => ENV['CI_COMMIT_TAG'], # Always set a value, even an empty string, so that the downstream pipeline can correctly check it.
- "GITLAB_ASSETS_TAG" => ENV['CI_COMMIT_TAG'] ? ENV['CI_COMMIT_REF_NAME'] : ENV['CI_COMMIT_SHA'],
"FORCE_RAILS_IMAGE_BUILDS" => 'true',
"CE_PIPELINE" => Trigger.ee? ? nil : "true", # Always set a value, even an empty string, so that the downstream pipeline can correctly check it.
"EE_PIPELINE" => Trigger.ee? ? "true" : nil # Always set a value, even an empty string, so that the downstream pipeline can correctly check it.
diff --git a/scripts/undercoverage b/scripts/undercoverage
index 86153671d6a..348f421c0d5 100755
--- a/scripts/undercoverage
+++ b/scripts/undercoverage
@@ -21,6 +21,13 @@ end
compare_base = ARGV[0]
compare_base ||= IO.popen(%w(git merge-base origin/master HEAD)) { |p| p.read.chomp }
+coverage_file_path = 'coverage/lcov/gitlab.lcov'
+
+result = if File.exist?(coverage_file_path)
+ Undercover::CLI.run(%W(-c #{compare_base}))
+ else
+ warn "#{coverage_file_path} doesn't exist"
+ 0
+ end
-result = Undercover::CLI.run(%W(-c #{compare_base}))
exit result
diff --git a/scripts/used-feature-flags b/scripts/used-feature-flags
index eb7e85be229..74180d02a91 100755
--- a/scripts/used-feature-flags
+++ b/scripts/used-feature-flags
@@ -114,6 +114,9 @@ if unused_flags.count > 0
puts
puts "If they are really no longer needed REMOVE their .yml definition".red
puts "If they are needed you need to ENSURE that their usage is covered with specs to continue.".red
+ puts "Feature flag usage is detected via Rubocop, which is unable to resolve dynamic feature flag usage,".red.bold
+ puts "interpolated strings however are optimistically matched. For more details consult test suite:".red
+ puts "https://gitlab.com/gitlab-org/gitlab/-/blob/69cb5d36db95881b495966c95655672cfb816f62/spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb".red
puts
unused_flags.keys.sort.each do |name|
puts "- #{name}".yellow
diff --git a/scripts/utils.sh b/scripts/utils.sh
index 50ca7f558f6..92f647958fe 100644
--- a/scripts/utils.sh
+++ b/scripts/utils.sh
@@ -15,9 +15,11 @@ function retry() {
function test_url() {
local url="${1}"
+ local curl_args="${2}"
local status
+ local cmd="curl ${curl_args} --output /dev/null -L -s -w ''%{http_code}'' \"${url}\""
- status=$(curl --output /dev/null -L -s -w ''%{http_code}'' "${url}")
+ status=$(eval "${cmd}")
if [[ $status == "200" ]]; then
return 0
@@ -105,6 +107,27 @@ function install_junit_merge_gem() {
run_timed_command "gem install junit_merge --no-document --version 0.1.2"
}
+function fail_on_warnings() {
+ local cmd="$*"
+ local warnings
+ warnings="$(mktemp)"
+
+ eval "$cmd 2>$warnings"
+ local ret=$?
+
+ if test -s "$warnings";
+ then
+ echoerr "There were warnings:"
+ cat "$warnings"
+ rm "$warnings"
+ return 1
+ fi
+
+ rm "$warnings"
+
+ return $ret
+}
+
function run_timed_command() {
local cmd="${1}"
local metric_name="${2:-no}"
@@ -203,3 +226,21 @@ function danger_as_local() {
# We need to base SHA to help danger determine the base commit for this shallow clone.
bundle exec danger dry_run --fail-on-errors=true --verbose --base="${CI_MERGE_REQUEST_DIFF_BASE_SHA}" --head="${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA:-$CI_COMMIT_SHA}" --dangerfile="${DANGER_DANGERFILE:-Dangerfile}"
}
+
+# We're inlining this function in `.gitlab/ci/package-and-test/main.gitlab-ci.yml` so make sure to reflect any changes there
+function assets_image_tag() {
+ local cache_assets_hash_file="cached-assets-hash.txt"
+
+ if [[ -n "${CI_COMMIT_TAG}" ]]; then
+ echo -n "${CI_COMMIT_REF_NAME}"
+ elif [[ -f "${cache_assets_hash_file}" ]]; then
+ echo -n "assets-hash-$(cat ${cache_assets_hash_file} | cut -c1-10)"
+ else
+ echo -n "${CI_COMMIT_SHA}"
+ fi
+}
+
+function setup_gcloud() {
+ gcloud auth activate-service-account --key-file="${REVIEW_APPS_GCP_CREDENTIALS}"
+ gcloud config set project "${REVIEW_APPS_GCP_PROJECT}"
+}
diff --git a/scripts/verify-tff-mapping b/scripts/verify-tff-mapping
index b4974f71ebf..302e50bf34f 100755
--- a/scripts/verify-tff-mapping
+++ b/scripts/verify-tff-mapping
@@ -35,6 +35,30 @@ tests = [
},
{
+ explanation: 'EE lib should map to respective spec.',
+ source: 'ee/lib/world.rb',
+ expected: ['ee/spec/lib/world_spec.rb']
+ },
+
+ {
+ explanation: 'https://gitlab.com/gitlab-org/gitlab/-/issues/368628',
+ source: 'lib/gitlab/usage_data_counters/foo.rb',
+ expected: ['spec/lib/gitlab/usage_data_spec.rb']
+ },
+
+ {
+ explanation: 'https://gitlab.com/gitlab-org/quality/engineering-productivity/master-broken-incidents/-/issues/54#note_1160811638',
+ source: 'lib/gitlab/ci/config/base.rb',
+ expected: ['spec/lib/gitlab/ci/yaml_processor_spec.rb']
+ },
+
+ {
+ explanation: 'https://gitlab.com/gitlab-org/quality/engineering-productivity/master-broken-incidents/-/issues/54#note_1160811638',
+ source: 'ee/lib/gitlab/ci/config/base.rb',
+ expected: ['spec/lib/gitlab/ci/yaml_processor_spec.rb', 'ee/spec/lib/gitlab/ci/yaml_processor_spec.rb']
+ },
+
+ {
explanation: 'FOSS lib should map to respective spec',
source: 'lib/gitaly/server.rb',
expected: ['spec/lib/gitaly/server_spec.rb']
@@ -47,6 +71,42 @@ tests = [
},
{
+ explanation: 'Initializers should map to respective spec',
+ source: 'config/initializers/action_mailer_hooks.rb',
+ expected: ['spec/initializers/action_mailer_hooks_spec.rb']
+ },
+
+ {
+ explanation: 'DB structure should map to schema spec',
+ source: 'db/structure.sql',
+ expected: ['spec/db/schema_spec.rb']
+ },
+
+ {
+ explanation: 'Migration should map to its non-timestamped spec',
+ source: 'db/migrate/20210818220234_add_default_project_approval_rules_vuln_allowed.rb',
+ expected: ['spec/migrations/add_default_project_approval_rules_vuln_allowed_spec.rb']
+ },
+
+ {
+ explanation: 'Migration should map to its timestamped spec',
+ source: 'db/post_migrate/20210915022415_cleanup_bigint_conversion_for_ci_builds.rb',
+ expected: ['spec/migrations/20210915022415_cleanup_bigint_conversion_for_ci_builds_spec.rb']
+ },
+
+ {
+ explanation: 'FOSS views should map to respective spec',
+ source: 'app/views/admin/dashboard/index.html.haml',
+ expected: ['spec/views/admin/dashboard/index.html.haml_spec.rb']
+ },
+
+ {
+ explanation: 'EE views should map to respective spec',
+ source: 'ee/app/views/subscriptions/new.html.haml',
+ expected: ['ee/spec/views/subscriptions/new.html.haml_spec.rb']
+ },
+
+ {
explanation: 'FOSS spec code should map to itself',
source: 'spec/models/issue_spec.rb',
expected: ['spec/models/issue_spec.rb']
@@ -77,57 +137,57 @@ tests = [
},
{
- explanation: 'Initializers should map to respective spec',
- source: 'config/initializers/action_mailer_hooks.rb',
- expected: ['spec/initializers/action_mailer_hooks_spec.rb']
+ explanation: 'Whats New should map to its respective spec',
+ source: 'data/whats_new/202101140001_13_08.yml',
+ expected: ['spec/lib/release_highlights/validator_spec.rb']
},
{
- explanation: 'FOSS views should map to respective spec',
- source: 'app/views/admin/dashboard/index.html.haml',
- expected: ['spec/views/admin/dashboard/index.html.haml_spec.rb']
+ explanation: 'The documentation index page is used in this haml_lint spec',
+ source: 'doc/index.md',
+ expected: ['spec/haml_lint/linter/documentation_links_spec.rb']
},
{
- explanation: 'EE views should map to respective spec',
- source: 'ee/app/views/subscriptions/new.html.haml',
- expected: ['ee/spec/views/subscriptions/new.html.haml_spec.rb']
+ explanation: 'Spec for FOSS sidekiq worker',
+ source: 'app/workers/new_worker.rb',
+ expected: ['spec/workers/every_sidekiq_worker_spec.rb']
},
{
- explanation: 'DB structure should map to schema spec',
- source: 'db/structure.sql',
- expected: ['spec/db/schema_spec.rb']
+ explanation: 'Spec for EE sidekiq worker',
+ source: 'ee/app/workers/new_worker.rb',
+ expected: ['spec/workers/every_sidekiq_worker_spec.rb']
},
{
- explanation: 'Migration should map to its non-timestamped spec',
- source: 'db/migrate/20210818220234_add_default_project_approval_rules_vuln_allowed.rb',
- expected: ['spec/migrations/add_default_project_approval_rules_vuln_allowed_spec.rb']
+ explanation: 'Known events',
+ source: 'lib/gitlab/usage_data_counters/known_events/common.yml',
+ expected: ['spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb', 'spec/lib/gitlab/usage_data_spec.rb']
},
{
- explanation: 'Migration should map to its timestamped spec',
- source: 'db/post_migrate/20210915022415_cleanup_bigint_conversion_for_ci_builds.rb',
- expected: ['spec/migrations/20210915022415_cleanup_bigint_conversion_for_ci_builds_spec.rb']
+ explanation: 'FOSS mailer previews',
+ source: 'app/mailers/previews/foo.rb',
+ expected: ['spec/mailers/previews_spec.rb']
},
{
- explanation: 'Whats New should map to its respective spec',
- source: 'data/whats_new/202101140001_13_08.yml',
- expected: ['spec/lib/release_highlights/validator_spec.rb']
+ explanation: 'EE mailer previews',
+ source: 'ee/app/mailers/previews/foo.rb',
+ expected: ['spec/mailers/previews_spec.rb']
},
{
- explanation: 'Spec for every sidekiq worker',
- source: 'app/workers/new_worker.rb',
- expected: ['spec/workers/every_sidekiq_worker_spec.rb']
+ explanation: 'EE mailer extension previews',
+ source: 'ee/app/mailers/previews/license_mailer_preview.rb',
+ expected: ['spec/mailers/previews_spec.rb']
},
{
- explanation: 'Known events',
- source: 'lib/gitlab/usage_data_counters/known_events/common.yml',
- expected: ['spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb', 'spec/lib/gitlab/usage_data_spec.rb']
+ explanation: 'GLFM spec and config files for CE and EE should map to respective markdown snapshot specs',
+ source: 'glfm_specification/foo',
+ expected: ['spec/requests/api/markdown_snapshot_spec.rb', 'ee/spec/requests/api/markdown_snapshot_spec.rb']
}
]
diff --git a/sidekiq_cluster/cli.rb b/sidekiq_cluster/cli.rb
index 52dc14130fb..760a5f14c2d 100644
--- a/sidekiq_cluster/cli.rb
+++ b/sidekiq_cluster/cli.rb
@@ -31,8 +31,9 @@ module Gitlab
CommandError = Class.new(StandardError)
def initialize(log_output = $stderr)
- # As recommended by https://github.com/mperham/sidekiq/wiki/Advanced-Options#concurrency
- @max_concurrency = 50
+ # https://github.com/mperham/sidekiq/wiki/Advanced-Options#concurrency
+ # https://ruby.social/@getajobmike/109326475545816363
+ @max_concurrency = 20
@min_concurrency = 0
@environment = ENV['RAILS_ENV'] || 'development'
@metrics_dir = ENV["prometheus_multiproc_dir"] || File.absolute_path("tmp/prometheus_multiproc_dir/sidekiq")
@@ -111,7 +112,7 @@ module Gitlab
end
def start_and_supervise_workers(queue_groups)
- worker_pids = SidekiqCluster.start(
+ wait_threads = SidekiqCluster.start(
queue_groups,
env: @environment,
directory: @rails_path,
@@ -134,6 +135,7 @@ module Gitlab
)
metrics_server_pid = start_metrics_server
+ worker_pids = wait_threads.map(&:pid)
supervisor.supervise(worker_pids + Array(metrics_server_pid)) do |dead_pids|
# If we're not in the process of shutting down the cluster,
# and the metrics server died, restart it.
@@ -148,6 +150,13 @@ module Gitlab
[]
end
end
+
+ exit_statuses = wait_threads.map do |thread|
+ thread.join
+ thread.value
+ end
+
+ exit 1 unless exit_statuses.compact.all?(&:success?)
end
def start_metrics_server
diff --git a/sidekiq_cluster/sidekiq_cluster.rb b/sidekiq_cluster/sidekiq_cluster.rb
index c68cbe7c163..1ed08e7e839 100644
--- a/sidekiq_cluster/sidekiq_cluster.rb
+++ b/sidekiq_cluster/sidekiq_cluster.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require_relative '../lib/gitlab/process_management'
+require_relative '../lib/gitlab/process_supervisor'
module Gitlab
module SidekiqCluster
@@ -33,8 +34,9 @@ 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: DEFAULT_SOFT_TIMEOUT_SECONDS, dryrun: false)
+ # Returns an Array containing the waiter threads (from Process.detach) of
+ # the started processes.
+ def self.start(queues, env: :development, directory: Dir.pwd, max_concurrency: 20, min_concurrency: 0, timeout: DEFAULT_SOFT_TIMEOUT_SECONDS, dryrun: false)
queues.map.with_index do |pair, index|
start_sidekiq(pair, env: env,
directory: directory,
@@ -82,9 +84,7 @@ module Gitlab
)
end
- ProcessManagement.wait_async(pid)
-
- pid
+ Process.detach(pid)
end
def self.count_by_queue(queues)
diff --git a/spec/bin/audit_event_type_spec.rb b/spec/bin/audit_event_type_spec.rb
index d4b1ebf08de..e23d365f68f 100644
--- a/spec/bin/audit_event_type_spec.rb
+++ b/spec/bin/audit_event_type_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'bin/audit-event-type' do
using RSpec::Parameterized::TableSyntax
describe AuditEventTypeCreator do
- let(:argv) { %w[test_audit_event -d test -g govern::compliance -s -t -i https://url -m http://url] }
+ let(:argv) { %w[test_audit_event -d test -c compliance_management -s -t -i https://url -m http://url] }
let(:options) { AuditEventTypeOptionParser.parse(argv) }
let(:creator) { described_class.new(options) }
let(:existing_audit_event_types) do
@@ -71,8 +71,8 @@ RSpec.describe 'bin/audit-event-type' do
:force | %w[foo --force] | true
:description | %w[foo -d desc] | 'desc'
:description | %w[foo --description desc] | 'desc'
- :group | %w[foo -g govern::compliance] | 'govern::compliance'
- :group | %w[foo --group govern::compliance] | 'govern::compliance'
+ :feature_category | %w[foo -c audit_events] | 'audit_events'
+ :feature_category | %w[foo --feature-category audit_events] | 'audit_events'
:milestone | %w[foo -M 15.6] | '15.6'
:milestone | %w[foo --milestone 15.6] | '15.6'
:saved_to_database | %w[foo -s] | true
@@ -141,27 +141,27 @@ RSpec.describe 'bin/audit-event-type' do
end
end
- describe '.read_group' do
- let(:group) { 'govern::compliance' }
+ describe '.read_feature_category' do
+ let(:feature_category) { 'compliance_management' }
- it 'reads group from stdin' do
- expect(Readline).to receive(:readline).and_return(group)
+ it 'reads feature_category from stdin' do
+ expect(Readline).to receive(:readline).and_return(feature_category)
expect do
- expect(described_class.read_group).to eq('govern::compliance')
- end.to output(/Specify the group introducing the audit event type, like `govern::compliance`:/).to_stdout
+ expect(described_class.read_feature_category).to eq('compliance_management')
+ end.to output(/Specify the feature category of this audit event, like `compliance_management`:/).to_stdout
end
- context 'when group is empty' do
- let(:group) { '' }
+ context 'when feature category is empty' do
+ let(:feature_category) { '' }
it 'shows error message and retries' do
- expect(Readline).to receive(:readline).and_return(group)
+ expect(Readline).to receive(:readline).and_return(feature_category)
expect(Readline).to receive(:readline).and_raise('EOF')
expect do
- expect { described_class.read_group }.to raise_error(/EOF/)
- end.to output(/Specify the group introducing the audit event type, like `govern::compliance`:/)
- .to_stdout.and output(/group is a required field/).to_stderr
+ expect { described_class.read_feature_category }.to raise_error(/EOF/)
+ end.to output(/Specify the feature category of this audit event, like `compliance_management`:/)
+ .to_stdout.and output(/feature_category is a required field/).to_stderr
end
end
end
@@ -281,11 +281,11 @@ RSpec.describe 'bin/audit-event-type' do
describe '.read_milestone' do
before do
- allow(File).to receive(:read).with('VERSION').and_return('15.6.0-pre')
allow(File).to receive(:read).and_call_original
end
it 'returns the correct milestone from the VERSION file' do
+ expect(File).to receive(:read).with('VERSION').and_return('15.6.0-pre')
expect(described_class.read_milestone).to eq('15.6')
end
end
diff --git a/spec/bin/feature_flag_spec.rb b/spec/bin/feature_flag_spec.rb
index 03f5ac135f7..cce103965d3 100644
--- a/spec/bin/feature_flag_spec.rb
+++ b/spec/bin/feature_flag_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'bin/feature-flag' do
using RSpec::Parameterized::TableSyntax
describe FeatureFlagCreator do
- let(:argv) { %w[feature-flag-name -t development -g group::memory -i https://url -m http://url] }
+ let(:argv) { %w[feature-flag-name -t development -g group::geo -i https://url -m http://url] }
let(:options) { FeatureFlagOptionParser.parse(argv) }
let(:creator) { described_class.new(options) }
let(:existing_flags) do
@@ -81,8 +81,8 @@ RSpec.describe 'bin/feature-flag' do
:type | %w[foo --type development] | :development
:type | %w[foo -t invalid] | nil
:type | %w[foo --type invalid] | nil
- :group | %w[foo -g group::memory] | 'group::memory'
- :group | %w[foo --group group::memory] | 'group::memory'
+ :group | %w[foo -g group::geo] | 'group::geo'
+ :group | %w[foo --group group::geo] | 'group::geo'
:group | %w[foo -g invalid] | nil
:group | %w[foo --group invalid] | nil
end
@@ -178,12 +178,12 @@ RSpec.describe 'bin/feature-flag' do
end
describe '.read_group' do
- let(:group) { 'group::memory' }
+ let(:group) { 'group::geo' }
it 'reads type from stdin' do
expect(Readline).to receive(:readline).and_return(group)
expect do
- expect(described_class.read_group).to eq('group::memory')
+ expect(described_class.read_group).to eq('group::geo')
end.to output(/Specify the group introducing the feature flag/).to_stdout
end
diff --git a/spec/commands/sidekiq_cluster/cli_spec.rb b/spec/commands/sidekiq_cluster/cli_spec.rb
index 4d1a07a6a75..c2ea9455de6 100644
--- a/spec/commands/sidekiq_cluster/cli_spec.rb
+++ b/spec/commands/sidekiq_cluster/cli_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo
let(:cli) { described_class.new('/dev/null') }
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 }
+ { env: 'test', directory: Dir.pwd, max_concurrency: 20, min_concurrency: 0, dryrun: false, timeout: timeout }
end
let(:sidekiq_exporter_enabled) { false }
@@ -245,9 +245,9 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo
it 'expands multiple queue groups correctly' do
expected_workers =
if Gitlab.ee?
- [%w[chat_notification], %w[project_export projects_import_export_relation_export project_template_export]]
+ [%w[chat_notification], %w[project_export projects_import_export_parallel_project_export projects_import_export_relation_export project_template_export]]
else
- [%w[chat_notification], %w[project_export projects_import_export_relation_export]]
+ [%w[chat_notification], %w[project_export projects_import_export_parallel_project_export projects_import_export_relation_export]]
end
expect(Gitlab::SidekiqCluster)
@@ -299,11 +299,11 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo
end
context 'starting the server' do
- context 'without --dryrun' do
- before do
- allow(Gitlab::SidekiqCluster).to receive(:start).and_return([])
- end
+ before do
+ allow(Gitlab::SidekiqCluster).to receive(:start).and_return([])
+ end
+ context 'without --dryrun' do
it 'wipes the metrics directory before starting workers' do
expect(metrics_cleanup_service).to receive(:execute).ordered
expect(Gitlab::SidekiqCluster).to receive(:start).ordered.and_return([])
@@ -403,9 +403,42 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo
let(:sidekiq_exporter_enabled) { true }
let(:metrics_server_pid) { 99 }
let(:sidekiq_worker_pids) { [2, 42] }
+ let(:waiter_threads) { [instance_double('Process::Waiter'), instance_double('Process::Waiter')] }
+ let(:process_status) { instance_double('Process::Status') }
before do
- allow(Gitlab::SidekiqCluster).to receive(:start).and_return(sidekiq_worker_pids)
+ allow(Gitlab::SidekiqCluster).to receive(:start).and_return(waiter_threads)
+ allow(process_status).to receive(:success?).and_return(true)
+ allow(cli).to receive(:exit)
+
+ waiter_threads.each.with_index do |thread, i|
+ allow(thread).to receive(:join)
+ allow(thread).to receive(:pid).and_return(sidekiq_worker_pids[i])
+ allow(thread).to receive(:value).and_return(process_status)
+ end
+ end
+
+ context 'when one of the workers has been terminated gracefully' do
+ it 'stops the entire process cluster' do
+ expect(MetricsServer).to receive(:start_for_sidekiq).once.and_return(metrics_server_pid)
+ expect(supervisor).to receive(:supervise).and_yield([2, 99])
+ expect(supervisor).to receive(:shutdown)
+ expect(cli).not_to receive(:exit).with(1)
+
+ cli.run(%w(foo))
+ end
+ end
+
+ context 'when one of the workers has failed' do
+ it 'stops the entire process cluster and exits with a non-zero code' do
+ expect(MetricsServer).to receive(:start_for_sidekiq).once.and_return(metrics_server_pid)
+ expect(supervisor).to receive(:supervise).and_yield([2, 99])
+ expect(supervisor).to receive(:shutdown)
+ expect(process_status).to receive(:success?).and_return(false)
+ expect(cli).to receive(:exit).with(1)
+
+ cli.run(%w(foo))
+ end
end
it 'stops the entire process cluster if one of the workers has been terminated' do
diff --git a/spec/config/application_spec.rb b/spec/config/application_spec.rb
index 94fecc26e7f..7b64ad4a9b9 100644
--- a/spec/config/application_spec.rb
+++ b/spec/config/application_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Application do # rubocop:disable RSpec/FilePath
+RSpec.describe Gitlab::Application, feature_category: :scalability do # rubocop:disable RSpec/FilePath
using RSpec::Parameterized::TableSyntax
filtered_param = ActiveSupport::ParameterFilter::FILTERED
diff --git a/spec/config/inject_enterprise_edition_module_spec.rb b/spec/config/inject_enterprise_edition_module_spec.rb
index 96fc26fc80a..47cb36c569e 100644
--- a/spec/config/inject_enterprise_edition_module_spec.rb
+++ b/spec/config/inject_enterprise_edition_module_spec.rb
@@ -2,7 +2,7 @@
require 'fast_spec_helper'
-RSpec.describe InjectEnterpriseEditionModule do
+RSpec.describe InjectEnterpriseEditionModule, feature_category: :fulfillment_developer_productivity do
let(:extension_name) { 'FF' }
let(:extension_namespace) { Module.new }
let(:fish_name) { 'Fish' }
diff --git a/spec/config/mail_room_spec.rb b/spec/config/mail_room_spec.rb
index ec306837361..cf2146bdf77 100644
--- a/spec/config/mail_room_spec.rb
+++ b/spec/config/mail_room_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'mail_room.yml' do
+RSpec.describe 'mail_room.yml', feature_category: :service_desk do
include StubENV
let(:mailroom_config_path) { 'config/mail_room.yml' }
diff --git a/spec/config/object_store_settings_spec.rb b/spec/config/object_store_settings_spec.rb
index 9275c809550..7b4fa495288 100644
--- a/spec/config/object_store_settings_spec.rb
+++ b/spec/config/object_store_settings_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require Rails.root.join('config', 'object_store_settings.rb')
-RSpec.describe ObjectStoreSettings do
+RSpec.describe ObjectStoreSettings, feature_category: :not_owned do
describe '#parse!' do
let(:settings) { Settingslogic.new(config) }
@@ -70,7 +70,6 @@ RSpec.describe ObjectStoreSettings do
expect(settings.artifacts['object_store']['enabled']).to be true
expect(settings.artifacts['object_store']['connection']).to eq(connection)
expect(settings.artifacts['object_store']['direct_upload']).to be true
- expect(settings.artifacts['object_store']['background_upload']).to be false
expect(settings.artifacts['object_store']['proxy_download']).to be false
expect(settings.artifacts['object_store']['remote_directory']).to eq('artifacts')
expect(settings.artifacts['object_store']['bucket_prefix']).to eq(nil)
@@ -81,7 +80,6 @@ RSpec.describe ObjectStoreSettings do
expect(settings.lfs['object_store']['enabled']).to be true
expect(settings.lfs['object_store']['connection']).to eq(connection)
expect(settings.lfs['object_store']['direct_upload']).to be true
- expect(settings.lfs['object_store']['background_upload']).to be false
expect(settings.lfs['object_store']['proxy_download']).to be true
expect(settings.lfs['object_store']['remote_directory']).to eq('lfs-objects')
expect(settings.lfs['object_store']['bucket_prefix']).to eq(nil)
@@ -200,7 +198,6 @@ RSpec.describe ObjectStoreSettings do
'enabled' => true,
'remote_directory' => 'some-bucket',
'direct_upload' => false,
- 'background_upload' => true,
'proxy_download' => false
}
end
@@ -215,9 +212,7 @@ RSpec.describe ObjectStoreSettings do
expect(settings.artifacts['object_store']).to be_nil
expect(settings.lfs['object_store']['remote_directory']).to eq('some-bucket')
expect(settings.lfs['object_store']['bucket_prefix']).to eq(nil)
- # Disable background_upload, regardless of the input config
expect(settings.lfs['object_store']['direct_upload']).to eq(true)
- expect(settings.lfs['object_store']['background_upload']).to eq(false)
expect(settings.external_diffs['object_store']).to be_nil
end
end
@@ -230,7 +225,6 @@ RSpec.describe ObjectStoreSettings do
expect(settings['enabled']).to be false
expect(settings['direct_upload']).to be true
- expect(settings['background_upload']).to be false
expect(settings['remote_directory']).to be nil
expect(settings['bucket_prefix']).to be nil
end
@@ -245,7 +239,6 @@ RSpec.describe ObjectStoreSettings do
expect(settings['enabled']).to be true
expect(settings['direct_upload']).to be true
- expect(settings['background_upload']).to be false
expect(settings['remote_directory']).to eq 'artifacts'
expect(settings['bucket_prefix']).to be nil
end
diff --git a/spec/config/settings_spec.rb b/spec/config/settings_spec.rb
index fe2081fa5de..4917b043812 100644
--- a/spec/config/settings_spec.rb
+++ b/spec/config/settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Settings do
+RSpec.describe Settings, feature_category: :authentication_and_authorization do
describe 'omniauth' do
it 'defaults to enabled' do
expect(described_class.omniauth.enabled).to be true
diff --git a/spec/config/smime_signature_settings_spec.rb b/spec/config/smime_signature_settings_spec.rb
index 5ce6fdd975b..477ad4a74ed 100644
--- a/spec/config/smime_signature_settings_spec.rb
+++ b/spec/config/smime_signature_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe SmimeSignatureSettings do
+RSpec.describe SmimeSignatureSettings, feature_category: :not_owned do
describe '.parse' do
let(:default_smime_key) { Rails.root.join('.gitlab_smime_key') }
let(:default_smime_cert) { Rails.root.join('.gitlab_smime_cert') }
diff --git a/spec/contracts/consumer/.node-version b/spec/contracts/consumer/.node-version
deleted file mode 100644
index 18711d290ea..00000000000
--- a/spec/contracts/consumer/.node-version
+++ /dev/null
@@ -1 +0,0 @@
-14.17.5
diff --git a/spec/contracts/consumer/fixtures/project/merge_request/diffs_batch.fixture.js b/spec/contracts/consumer/fixtures/project/merge_requests/diffs_batch.fixture.js
index 673aad721b3..673aad721b3 100644
--- a/spec/contracts/consumer/fixtures/project/merge_request/diffs_batch.fixture.js
+++ b/spec/contracts/consumer/fixtures/project/merge_requests/diffs_batch.fixture.js
diff --git a/spec/contracts/consumer/fixtures/project/merge_request/diffs_metadata.fixture.js b/spec/contracts/consumer/fixtures/project/merge_requests/diffs_metadata.fixture.js
index 2fee4a02023..2fee4a02023 100644
--- a/spec/contracts/consumer/fixtures/project/merge_request/diffs_metadata.fixture.js
+++ b/spec/contracts/consumer/fixtures/project/merge_requests/diffs_metadata.fixture.js
diff --git a/spec/contracts/consumer/fixtures/project/merge_request/discussions.fixture.js b/spec/contracts/consumer/fixtures/project/merge_requests/discussions.fixture.js
index 8c392395e1c..8c392395e1c 100644
--- a/spec/contracts/consumer/fixtures/project/merge_request/discussions.fixture.js
+++ b/spec/contracts/consumer/fixtures/project/merge_requests/discussions.fixture.js
diff --git a/spec/contracts/consumer/fixtures/project/pipeline_schedule/update_pipeline_schedule.fixture.js b/spec/contracts/consumer/fixtures/project/pipeline_schedules/update_pipeline_schedule.fixture.js
index acfab14851a..acfab14851a 100644
--- a/spec/contracts/consumer/fixtures/project/pipeline_schedule/update_pipeline_schedule.fixture.js
+++ b/spec/contracts/consumer/fixtures/project/pipeline_schedules/update_pipeline_schedule.fixture.js
diff --git a/spec/contracts/consumer/fixtures/project/pipeline/create_a_new_pipeline.fixture.js b/spec/contracts/consumer/fixtures/project/pipelines/create_a_new_pipeline.fixture.js
index 68063d2fb0c..68063d2fb0c 100644
--- a/spec/contracts/consumer/fixtures/project/pipeline/create_a_new_pipeline.fixture.js
+++ b/spec/contracts/consumer/fixtures/project/pipelines/create_a_new_pipeline.fixture.js
diff --git a/spec/contracts/consumer/fixtures/project/pipeline/delete_pipeline.fixture.js b/spec/contracts/consumer/fixtures/project/pipelines/delete_pipeline.fixture.js
index 2e3e7355b99..2e3e7355b99 100644
--- a/spec/contracts/consumer/fixtures/project/pipeline/delete_pipeline.fixture.js
+++ b/spec/contracts/consumer/fixtures/project/pipelines/delete_pipeline.fixture.js
diff --git a/spec/contracts/consumer/fixtures/project/pipeline/get_list_project_pipelines.fixture.js b/spec/contracts/consumer/fixtures/project/pipelines/get_list_project_pipelines.fixture.js
index a982e927572..a982e927572 100644
--- a/spec/contracts/consumer/fixtures/project/pipeline/get_list_project_pipelines.fixture.js
+++ b/spec/contracts/consumer/fixtures/project/pipelines/get_list_project_pipelines.fixture.js
diff --git a/spec/contracts/consumer/fixtures/project/pipeline/get_pipeline_header_data.fixture.js b/spec/contracts/consumer/fixtures/project/pipelines/get_pipeline_header_data.fixture.js
index b14a230d2e0..b14a230d2e0 100644
--- a/spec/contracts/consumer/fixtures/project/pipeline/get_pipeline_header_data.fixture.js
+++ b/spec/contracts/consumer/fixtures/project/pipelines/get_pipeline_header_data.fixture.js
diff --git a/spec/contracts/consumer/package.json b/spec/contracts/consumer/package.json
index 6d3feaa6d4c..60f268806de 100644
--- a/spec/contracts/consumer/package.json
+++ b/spec/contracts/consumer/package.json
@@ -22,5 +22,8 @@
"devDependencies": {
"@babel/preset-env": "^7.18.2",
"babel-jest": "^28.1.1"
+ },
+ "config": {
+ "pact_do_not_track": true
}
}
diff --git a/spec/contracts/consumer/specs/project/merge_request/show.spec.js b/spec/contracts/consumer/specs/project/merge_request/show.spec.js
deleted file mode 100644
index 4183e19435a..00000000000
--- a/spec/contracts/consumer/specs/project/merge_request/show.spec.js
+++ /dev/null
@@ -1,108 +0,0 @@
-import { pactWith } from 'jest-pact';
-
-import { DiffsBatch } from '../../../fixtures/project/merge_request/diffs_batch.fixture';
-import { Discussions } from '../../../fixtures/project/merge_request/discussions.fixture';
-import { DiffsMetadata } from '../../../fixtures/project/merge_request/diffs_metadata.fixture';
-import {
- getDiffsBatch,
- getDiffsMetadata,
- getDiscussions,
-} from '../../../resources/api/project/merge_requests';
-
-const CONSUMER_NAME = 'MergeRequest#show';
-const CONSUMER_LOG = '../logs/consumer.log';
-const CONTRACT_DIR = '../contracts/project/merge_request/show';
-const DIFFS_BATCH_PROVIDER_NAME = 'Merge Request Diffs Batch Endpoint';
-const DISCUSSIONS_PROVIDER_NAME = 'Merge Request Discussions Endpoint';
-const DIFFS_METADATA_PROVIDER_NAME = 'Merge Request Diffs Metadata Endpoint';
-
-// API endpoint: /merge_requests/:id/diffs_batch.json
-pactWith(
- {
- consumer: CONSUMER_NAME,
- provider: DIFFS_BATCH_PROVIDER_NAME,
- log: CONSUMER_LOG,
- dir: CONTRACT_DIR,
- },
-
- (provider) => {
- describe(DIFFS_BATCH_PROVIDER_NAME, () => {
- beforeEach(() => {
- const interaction = {
- ...DiffsBatch.scenario,
- ...DiffsBatch.request,
- willRespondWith: DiffsBatch.success,
- };
- provider.addInteraction(interaction);
- });
-
- it('returns a successful body', async () => {
- const diffsBatch = await getDiffsBatch({
- url: provider.mockService.baseUrl,
- });
-
- expect(diffsBatch).toEqual(DiffsBatch.body);
- });
- });
- },
-);
-
-pactWith(
- {
- consumer: CONSUMER_NAME,
- provider: DISCUSSIONS_PROVIDER_NAME,
- log: CONSUMER_LOG,
- dir: CONTRACT_DIR,
- },
-
- (provider) => {
- describe(DISCUSSIONS_PROVIDER_NAME, () => {
- beforeEach(() => {
- const interaction = {
- ...Discussions.scenario,
- ...Discussions.request,
- willRespondWith: Discussions.success,
- };
- provider.addInteraction(interaction);
- });
-
- it('return a successful body', async () => {
- const discussions = await getDiscussions({
- url: provider.mockService.baseUrl,
- });
-
- expect(discussions).toEqual(Discussions.body);
- });
- });
- },
-);
-
-pactWith(
- {
- consumer: CONSUMER_NAME,
- provider: DIFFS_METADATA_PROVIDER_NAME,
- log: CONSUMER_LOG,
- dir: CONTRACT_DIR,
- },
-
- (provider) => {
- describe(DIFFS_METADATA_PROVIDER_NAME, () => {
- beforeEach(() => {
- const interaction = {
- ...DiffsMetadata.scenario,
- ...DiffsMetadata.request,
- willRespondWith: DiffsMetadata.success,
- };
- provider.addInteraction(interaction);
- });
-
- it('return a successful body', async () => {
- const diffsMetadata = await getDiffsMetadata({
- url: provider.mockService.baseUrl,
- });
-
- expect(diffsMetadata).toEqual(DiffsMetadata.body);
- });
- });
- },
-);
diff --git a/spec/contracts/consumer/specs/project/merge_requests/show.spec.js b/spec/contracts/consumer/specs/project/merge_requests/show.spec.js
new file mode 100644
index 00000000000..fcc0e117e2d
--- /dev/null
+++ b/spec/contracts/consumer/specs/project/merge_requests/show.spec.js
@@ -0,0 +1,108 @@
+import { pactWith } from 'jest-pact';
+
+import { DiffsBatch } from '../../../fixtures/project/merge_requests/diffs_batch.fixture';
+import { Discussions } from '../../../fixtures/project/merge_requests/discussions.fixture';
+import { DiffsMetadata } from '../../../fixtures/project/merge_requests/diffs_metadata.fixture';
+import {
+ getDiffsBatch,
+ getDiffsMetadata,
+ getDiscussions,
+} from '../../../resources/api/project/merge_requests';
+
+const CONSUMER_NAME = 'MergeRequests#show';
+const CONSUMER_LOG = '../logs/consumer.log';
+const CONTRACT_DIR = '../contracts/project/merge_requests/show';
+const GET_DIFFS_BATCH_PROVIDER_NAME = 'GET diffs batch';
+const GET_DISCUSSIONS_PROVIDER_NAME = 'GET discussions';
+const GET_DIFFS_METADATA_PROVIDER_NAME = 'GET diffs metadata';
+
+// API endpoint: /merge_requests/:id/diffs_batch.json
+pactWith(
+ {
+ consumer: CONSUMER_NAME,
+ provider: GET_DIFFS_BATCH_PROVIDER_NAME,
+ log: CONSUMER_LOG,
+ dir: CONTRACT_DIR,
+ },
+
+ (provider) => {
+ describe(GET_DIFFS_BATCH_PROVIDER_NAME, () => {
+ beforeEach(() => {
+ const interaction = {
+ ...DiffsBatch.scenario,
+ ...DiffsBatch.request,
+ willRespondWith: DiffsBatch.success,
+ };
+ provider.addInteraction(interaction);
+ });
+
+ it('returns a successful body', async () => {
+ const diffsBatch = await getDiffsBatch({
+ url: provider.mockService.baseUrl,
+ });
+
+ expect(diffsBatch).toEqual(DiffsBatch.body);
+ });
+ });
+ },
+);
+
+pactWith(
+ {
+ consumer: CONSUMER_NAME,
+ provider: GET_DISCUSSIONS_PROVIDER_NAME,
+ log: CONSUMER_LOG,
+ dir: CONTRACT_DIR,
+ },
+
+ (provider) => {
+ describe(GET_DISCUSSIONS_PROVIDER_NAME, () => {
+ beforeEach(() => {
+ const interaction = {
+ ...Discussions.scenario,
+ ...Discussions.request,
+ willRespondWith: Discussions.success,
+ };
+ provider.addInteraction(interaction);
+ });
+
+ it('return a successful body', async () => {
+ const discussions = await getDiscussions({
+ url: provider.mockService.baseUrl,
+ });
+
+ expect(discussions).toEqual(Discussions.body);
+ });
+ });
+ },
+);
+
+pactWith(
+ {
+ consumer: CONSUMER_NAME,
+ provider: GET_DIFFS_METADATA_PROVIDER_NAME,
+ log: CONSUMER_LOG,
+ dir: CONTRACT_DIR,
+ },
+
+ (provider) => {
+ describe(GET_DIFFS_METADATA_PROVIDER_NAME, () => {
+ beforeEach(() => {
+ const interaction = {
+ ...DiffsMetadata.scenario,
+ ...DiffsMetadata.request,
+ willRespondWith: DiffsMetadata.success,
+ };
+ provider.addInteraction(interaction);
+ });
+
+ it('return a successful body', async () => {
+ const diffsMetadata = await getDiffsMetadata({
+ url: provider.mockService.baseUrl,
+ });
+
+ expect(diffsMetadata).toEqual(DiffsMetadata.body);
+ });
+ });
+ },
+);
diff --git a/spec/contracts/consumer/specs/project/pipeline/index.spec.js b/spec/contracts/consumer/specs/project/pipeline/index.spec.js
deleted file mode 100644
index 1453435d637..00000000000
--- a/spec/contracts/consumer/specs/project/pipeline/index.spec.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import { pactWith } from 'jest-pact';
-
-import { ProjectPipelines } from '../../../fixtures/project/pipeline/get_list_project_pipelines.fixture';
-import { getProjectPipelines } from '../../../resources/api/project/pipelines';
-
-const CONSUMER_NAME = 'Pipelines#index';
-const CONSUMER_LOG = '../logs/consumer.log';
-const CONTRACT_DIR = '../contracts/project/pipeline/index';
-const PROVIDER_NAME = 'GET List project pipelines';
-
-// API endpoint: /pipelines.json
-pactWith(
- {
- consumer: CONSUMER_NAME,
- provider: PROVIDER_NAME,
- log: CONSUMER_LOG,
- dir: CONTRACT_DIR,
- },
-
- (provider) => {
- describe(PROVIDER_NAME, () => {
- beforeEach(() => {
- const interaction = {
- ...ProjectPipelines.scenario,
- ...ProjectPipelines.request,
- willRespondWith: ProjectPipelines.success,
- };
- provider.addInteraction(interaction);
- });
-
- it('returns a successful body', async () => {
- const pipelines = await getProjectPipelines({
- url: provider.mockService.baseUrl,
- });
-
- expect(pipelines).toEqual(ProjectPipelines.body);
- });
- });
- },
-);
diff --git a/spec/contracts/consumer/specs/project/pipeline/new.spec.js b/spec/contracts/consumer/specs/project/pipeline/new.spec.js
deleted file mode 100644
index c3824d5979e..00000000000
--- a/spec/contracts/consumer/specs/project/pipeline/new.spec.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import { pactWith } from 'jest-pact';
-
-import { NewProjectPipeline } from '../../../fixtures/project/pipeline/create_a_new_pipeline.fixture';
-import { postProjectPipelines } from '../../../resources/api/project/pipelines';
-
-const CONSUMER_NAME = 'Pipelines#new';
-const CONSUMER_LOG = '../logs/consumer.log';
-const CONTRACT_DIR = '../contracts/project/pipeline/new';
-const PROVIDER_NAME = 'POST Create a new pipeline';
-
-// API endpoint: /pipelines.json
-pactWith(
- {
- consumer: CONSUMER_NAME,
- provider: PROVIDER_NAME,
- log: CONSUMER_LOG,
- dir: CONTRACT_DIR,
- },
-
- (provider) => {
- describe(PROVIDER_NAME, () => {
- beforeEach(async () => {
- const interaction = {
- ...NewProjectPipeline.scenario,
- ...NewProjectPipeline.request,
- willRespondWith: NewProjectPipeline.success,
- };
-
- provider.addInteraction(interaction);
- });
-
- it('returns a successful body', async () => {
- const newPipeline = await postProjectPipelines({
- url: provider.mockService.baseUrl,
- });
-
- expect(newPipeline.status).toEqual(NewProjectPipeline.success.status);
- });
- });
- },
-);
diff --git a/spec/contracts/consumer/specs/project/pipeline/show.spec.js b/spec/contracts/consumer/specs/project/pipeline/show.spec.js
deleted file mode 100644
index be6abb78eb5..00000000000
--- a/spec/contracts/consumer/specs/project/pipeline/show.spec.js
+++ /dev/null
@@ -1,89 +0,0 @@
-import { pactWith } from 'jest-pact';
-import { GraphQLInteraction } from '@pact-foundation/pact';
-
-import { extractGraphQLQuery } from '../../../helpers/graphql_query_extractor';
-
-import { PipelineHeaderData } from '../../../fixtures/project/pipeline/get_pipeline_header_data.fixture';
-import { DeletePipeline } from '../../../fixtures/project/pipeline/delete_pipeline.fixture';
-
-import { getPipelineHeaderDataRequest, deletePipeline } from '../../../resources/graphql/pipelines';
-
-const CONSUMER_NAME = 'Pipelines#show';
-const CONSUMER_LOG = '../logs/consumer.log';
-const CONTRACT_DIR = '../contracts/project/pipeline/show';
-const GET_PIPELINE_HEADER_DATA_PROVIDER_NAME = 'GET pipeline header data';
-const DELETE_PIPELINE_PROVIDER_NAME = 'DELETE pipeline';
-
-// GraphQL query: getPipelineHeaderData
-pactWith(
- {
- consumer: CONSUMER_NAME,
- provider: GET_PIPELINE_HEADER_DATA_PROVIDER_NAME,
- log: CONSUMER_LOG,
- dir: CONTRACT_DIR,
- },
-
- (provider) => {
- describe(GET_PIPELINE_HEADER_DATA_PROVIDER_NAME, () => {
- beforeEach(async () => {
- const query = await extractGraphQLQuery(
- 'app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql',
- );
- const graphqlQuery = new GraphQLInteraction()
- .given(PipelineHeaderData.scenario.state)
- .uponReceiving(PipelineHeaderData.scenario.uponReceiving)
- .withQuery(query)
- .withRequest(PipelineHeaderData.request)
- .withVariables(PipelineHeaderData.variables)
- .willRespondWith(PipelineHeaderData.success);
-
- provider.addInteraction(graphqlQuery);
- });
-
- it('returns a successful body', async () => {
- const pipelineHeaderData = await getPipelineHeaderDataRequest({
- url: provider.mockService.baseUrl,
- });
-
- expect(pipelineHeaderData.data).toEqual(PipelineHeaderData.body);
- });
- });
- },
-);
-
-// GraphQL query: deletePipeline
-pactWith(
- {
- consumer: CONSUMER_NAME,
- provider: DELETE_PIPELINE_PROVIDER_NAME,
- log: CONSUMER_LOG,
- dir: CONTRACT_DIR,
- },
-
- (provider) => {
- describe(DELETE_PIPELINE_PROVIDER_NAME, () => {
- beforeEach(async () => {
- const query = await extractGraphQLQuery(
- 'app/assets/javascripts/pipelines/graphql/mutations/delete_pipeline.mutation.graphql',
- );
- const graphqlQuery = new GraphQLInteraction()
- .given(DeletePipeline.scenario.state)
- .uponReceiving(DeletePipeline.scenario.uponReceiving)
- .withQuery(query)
- .withRequest(DeletePipeline.request)
- .withVariables(DeletePipeline.variables)
- .willRespondWith(DeletePipeline.success);
-
- provider.addInteraction(graphqlQuery);
- });
-
- it('returns a successful body', async () => {
- const deletePipelineResponse = await deletePipeline({
- url: provider.mockService.baseUrl,
- });
-
- expect(deletePipelineResponse.status).toEqual(DeletePipeline.success.status);
- });
- });
- },
-);
diff --git a/spec/contracts/consumer/specs/project/pipeline_schedule/edit.spec.js b/spec/contracts/consumer/specs/project/pipeline_schedule/edit.spec.js
deleted file mode 100644
index 117e6754255..00000000000
--- a/spec/contracts/consumer/specs/project/pipeline_schedule/edit.spec.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import { pactWith } from 'jest-pact';
-
-import { UpdatePipelineSchedule } from '../../../fixtures/project/pipeline_schedule/update_pipeline_schedule.fixture';
-import { updatePipelineSchedule } from '../../../resources/api/pipeline_schedules';
-
-const CONSUMER_NAME = 'PipelineSchedules#edit';
-const CONSUMER_LOG = '../logs/consumer.log';
-const CONTRACT_DIR = '../contracts/project/pipeline_schedule/edit';
-const PROVIDER_NAME = 'PUT Edit a pipeline schedule';
-
-// API endpoint: /pipelines.json
-pactWith(
- {
- consumer: CONSUMER_NAME,
- provider: PROVIDER_NAME,
- log: CONSUMER_LOG,
- dir: CONTRACT_DIR,
- },
-
- (provider) => {
- describe(PROVIDER_NAME, () => {
- beforeEach(() => {
- const interaction = {
- ...UpdatePipelineSchedule.scenario,
- ...UpdatePipelineSchedule.request,
- willRespondWith: UpdatePipelineSchedule.success,
- };
-
- provider.addInteraction(interaction);
- });
-
- it('returns a successful body', async () => {
- const pipelineSchedule = await updatePipelineSchedule({
- url: provider.mockService.baseUrl,
- });
-
- expect(pipelineSchedule.status).toEqual(UpdatePipelineSchedule.success.status);
- });
- });
- },
-);
diff --git a/spec/contracts/consumer/specs/project/pipeline_schedules/edit.spec.js b/spec/contracts/consumer/specs/project/pipeline_schedules/edit.spec.js
new file mode 100644
index 00000000000..0924b1b3b3d
--- /dev/null
+++ b/spec/contracts/consumer/specs/project/pipeline_schedules/edit.spec.js
@@ -0,0 +1,41 @@
+import { pactWith } from 'jest-pact';
+
+import { UpdatePipelineSchedule } from '../../../fixtures/project/pipeline_schedules/update_pipeline_schedule.fixture';
+import { updatePipelineSchedule } from '../../../resources/api/pipeline_schedules';
+
+const CONSUMER_NAME = 'PipelineSchedules#edit';
+const CONSUMER_LOG = '../logs/consumer.log';
+const CONTRACT_DIR = '../contracts/project/pipeline_schedules/edit';
+const PROVIDER_NAME = 'PUT edit a pipeline schedule';
+
+// API endpoint: /pipelines.json
+pactWith(
+ {
+ consumer: CONSUMER_NAME,
+ provider: PROVIDER_NAME,
+ log: CONSUMER_LOG,
+ dir: CONTRACT_DIR,
+ },
+
+ (provider) => {
+ describe(PROVIDER_NAME, () => {
+ beforeEach(() => {
+ const interaction = {
+ ...UpdatePipelineSchedule.scenario,
+ ...UpdatePipelineSchedule.request,
+ willRespondWith: UpdatePipelineSchedule.success,
+ };
+
+ provider.addInteraction(interaction);
+ });
+
+ it('returns a successful body', async () => {
+ const pipelineSchedule = await updatePipelineSchedule({
+ url: provider.mockService.baseUrl,
+ });
+
+ expect(pipelineSchedule.status).toEqual(UpdatePipelineSchedule.success.status);
+ });
+ });
+ },
+);
diff --git a/spec/contracts/consumer/specs/project/pipelines/index.spec.js b/spec/contracts/consumer/specs/project/pipelines/index.spec.js
new file mode 100644
index 00000000000..14bad31a763
--- /dev/null
+++ b/spec/contracts/consumer/specs/project/pipelines/index.spec.js
@@ -0,0 +1,40 @@
+import { pactWith } from 'jest-pact';
+
+import { ProjectPipelines } from '../../../fixtures/project/pipelines/get_list_project_pipelines.fixture';
+import { getProjectPipelines } from '../../../resources/api/project/pipelines';
+
+const CONSUMER_NAME = 'Pipelines#index';
+const CONSUMER_LOG = '../logs/consumer.log';
+const CONTRACT_DIR = '../contracts/project/pipelines/index';
+const PROVIDER_NAME = 'GET list project pipelines';
+
+// API endpoint: /pipelines.json
+pactWith(
+ {
+ consumer: CONSUMER_NAME,
+ provider: PROVIDER_NAME,
+ log: CONSUMER_LOG,
+ dir: CONTRACT_DIR,
+ },
+
+ (provider) => {
+ describe(PROVIDER_NAME, () => {
+ beforeEach(() => {
+ const interaction = {
+ ...ProjectPipelines.scenario,
+ ...ProjectPipelines.request,
+ willRespondWith: ProjectPipelines.success,
+ };
+ provider.addInteraction(interaction);
+ });
+
+ it('returns a successful body', async () => {
+ const pipelines = await getProjectPipelines({
+ url: provider.mockService.baseUrl,
+ });
+
+ expect(pipelines).toEqual(ProjectPipelines.body);
+ });
+ });
+ },
+);
diff --git a/spec/contracts/consumer/specs/project/pipelines/new.spec.js b/spec/contracts/consumer/specs/project/pipelines/new.spec.js
new file mode 100644
index 00000000000..9e381a61670
--- /dev/null
+++ b/spec/contracts/consumer/specs/project/pipelines/new.spec.js
@@ -0,0 +1,41 @@
+import { pactWith } from 'jest-pact';
+
+import { NewProjectPipeline } from '../../../fixtures/project/pipelines/create_a_new_pipeline.fixture';
+import { postProjectPipelines } from '../../../resources/api/project/pipelines';
+
+const CONSUMER_NAME = 'Pipelines#new';
+const CONSUMER_LOG = '../logs/consumer.log';
+const CONTRACT_DIR = '../contracts/project/pipelines/new';
+const PROVIDER_NAME = 'POST create a new pipeline';
+
+// API endpoint: /pipelines.json
+pactWith(
+ {
+ consumer: CONSUMER_NAME,
+ provider: PROVIDER_NAME,
+ log: CONSUMER_LOG,
+ dir: CONTRACT_DIR,
+ },
+
+ (provider) => {
+ describe(PROVIDER_NAME, () => {
+ beforeEach(async () => {
+ const interaction = {
+ ...NewProjectPipeline.scenario,
+ ...NewProjectPipeline.request,
+ willRespondWith: NewProjectPipeline.success,
+ };
+
+ provider.addInteraction(interaction);
+ });
+
+ it('returns a successful body', async () => {
+ const newPipeline = await postProjectPipelines({
+ url: provider.mockService.baseUrl,
+ });
+
+ expect(newPipeline.status).toEqual(NewProjectPipeline.success.status);
+ });
+ });
+ },
+);
diff --git a/spec/contracts/consumer/specs/project/pipelines/show.spec.js b/spec/contracts/consumer/specs/project/pipelines/show.spec.js
new file mode 100644
index 00000000000..97ad9dbbc9d
--- /dev/null
+++ b/spec/contracts/consumer/specs/project/pipelines/show.spec.js
@@ -0,0 +1,89 @@
+import { pactWith } from 'jest-pact';
+import { GraphQLInteraction } from '@pact-foundation/pact';
+
+import { extractGraphQLQuery } from '../../../helpers/graphql_query_extractor';
+
+import { PipelineHeaderData } from '../../../fixtures/project/pipelines/get_pipeline_header_data.fixture';
+import { DeletePipeline } from '../../../fixtures/project/pipelines/delete_pipeline.fixture';
+
+import { getPipelineHeaderDataRequest, deletePipeline } from '../../../resources/graphql/pipelines';
+
+const CONSUMER_NAME = 'Pipelines#show';
+const CONSUMER_LOG = '../logs/consumer.log';
+const CONTRACT_DIR = '../contracts/project/pipelines/show';
+const GET_PIPELINE_HEADER_DATA_PROVIDER_NAME = 'GET pipeline header data';
+const DELETE_PIPELINE_PROVIDER_NAME = 'DELETE pipeline';
+
+// GraphQL query: getPipelineHeaderData
+pactWith(
+ {
+ consumer: CONSUMER_NAME,
+ provider: GET_PIPELINE_HEADER_DATA_PROVIDER_NAME,
+ log: CONSUMER_LOG,
+ dir: CONTRACT_DIR,
+ },
+
+ (provider) => {
+ describe(GET_PIPELINE_HEADER_DATA_PROVIDER_NAME, () => {
+ beforeEach(async () => {
+ const query = await extractGraphQLQuery(
+ 'app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql',
+ );
+ const graphqlQuery = new GraphQLInteraction()
+ .given(PipelineHeaderData.scenario.state)
+ .uponReceiving(PipelineHeaderData.scenario.uponReceiving)
+ .withQuery(query)
+ .withRequest(PipelineHeaderData.request)
+ .withVariables(PipelineHeaderData.variables)
+ .willRespondWith(PipelineHeaderData.success);
+
+ provider.addInteraction(graphqlQuery);
+ });
+
+ it('returns a successful body', async () => {
+ const pipelineHeaderData = await getPipelineHeaderDataRequest({
+ url: provider.mockService.baseUrl,
+ });
+
+ expect(pipelineHeaderData.data).toEqual(PipelineHeaderData.body);
+ });
+ });
+ },
+);
+
+// GraphQL query: deletePipeline
+pactWith(
+ {
+ consumer: CONSUMER_NAME,
+ provider: DELETE_PIPELINE_PROVIDER_NAME,
+ log: CONSUMER_LOG,
+ dir: CONTRACT_DIR,
+ },
+
+ (provider) => {
+ describe(DELETE_PIPELINE_PROVIDER_NAME, () => {
+ beforeEach(async () => {
+ const query = await extractGraphQLQuery(
+ 'app/assets/javascripts/pipelines/graphql/mutations/delete_pipeline.mutation.graphql',
+ );
+ const graphqlQuery = new GraphQLInteraction()
+ .given(DeletePipeline.scenario.state)
+ .uponReceiving(DeletePipeline.scenario.uponReceiving)
+ .withQuery(query)
+ .withRequest(DeletePipeline.request)
+ .withVariables(DeletePipeline.variables)
+ .willRespondWith(DeletePipeline.success);
+
+ provider.addInteraction(graphqlQuery);
+ });
+
+ it('returns a successful body', async () => {
+ const deletePipelineResponse = await deletePipeline({
+ url: provider.mockService.baseUrl,
+ });
+
+ expect(deletePipelineResponse.status).toEqual(DeletePipeline.success.status);
+ });
+ });
+ },
+);
diff --git a/spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_batch_endpoint.json b/spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_batch_endpoint.json
deleted file mode 100644
index 3fa13766766..00000000000
--- a/spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_batch_endpoint.json
+++ /dev/null
@@ -1,229 +0,0 @@
-{
- "consumer": {
- "name": "MergeRequest#show"
- },
- "provider": {
- "name": "Merge Request Diffs Batch Endpoint"
- },
- "interactions": [
- {
- "description": "a request for diff lines",
- "providerState": "a merge request with diffs exists",
- "request": {
- "method": "GET",
- "path": "/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_batch.json",
- "query": "page=0",
- "headers": {
- "Accept": "*/*"
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "diff_files": [
- {
- "content_sha": "b0c94059db75b2473d616d4b1fde1a77533355a3",
- "submodule": false,
- "edit_path": "/gitlab-qa-bot/...",
- "ide_edit_path": "/gitlab-qa-bot/...",
- "old_path_html": "Gemfile",
- "new_path_html": "Gemfile",
- "blob": {
- "id": "855071bb3928d140764885964f7be1bb3e582495",
- "path": "Gemfile",
- "name": "Gemfile",
- "mode": "1234567",
- "readable_text": true,
- "icon": "doc-text"
- },
- "can_modify_blob": false,
- "file_identifier_hash": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587",
- "file_hash": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587",
- "file_path": "Gemfile",
- "old_path": "Gemfile",
- "new_path": "Gemfile",
- "new_file": false,
- "renamed_file": false,
- "deleted_file": false,
- "diff_refs": {
- "base_sha": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587",
- "start_sha": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587",
- "head_sha": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587"
- },
- "mode_changed": false,
- "a_mode": "123456",
- "b_mode": "123456",
- "viewer": {
- "name": "text",
- "collapsed": false
- },
- "old_size": 2288,
- "new_size": 2288,
- "added_lines": 1,
- "removed_lines": 1,
- "load_collapsed_diff_url": "/gitlab-qa-bot/...",
- "view_path": "/gitlab-qa-bot/...",
- "context_lines_path": "/gitlab-qa-bot/...",
- "highlighted_diff_lines": [
- {
- "text": "source",
- "rich_text": "<span></span>",
- "can_receive_suggestion": true
- }
- ],
- "is_fully_expanded": false
- }
- ],
- "pagination": {
- "total_pages": 1
- }
- },
- "matchingRules": {
- "$.body.diff_files": {
- "min": 1
- },
- "$.body.diff_files[*].*": {
- "match": "type"
- },
- "$.body.diff_files[*].content_sha": {
- "match": "type"
- },
- "$.body.diff_files[*].submodule": {
- "match": "type"
- },
- "$.body.diff_files[*].edit_path": {
- "match": "type"
- },
- "$.body.diff_files[*].ide_edit_path": {
- "match": "type"
- },
- "$.body.diff_files[*].old_path_html": {
- "match": "type"
- },
- "$.body.diff_files[*].new_path_html": {
- "match": "type"
- },
- "$.body.diff_files[*].blob.id": {
- "match": "type"
- },
- "$.body.diff_files[*].blob.path": {
- "match": "type"
- },
- "$.body.diff_files[*].blob.name": {
- "match": "type"
- },
- "$.body.diff_files[*].blob.mode": {
- "match": "type"
- },
- "$.body.diff_files[*].blob.readable_text": {
- "match": "type"
- },
- "$.body.diff_files[*].blob.icon": {
- "match": "type"
- },
- "$.body.diff_files[*].can_modify_blob": {
- "match": "type"
- },
- "$.body.diff_files[*].file_identifier_hash": {
- "match": "type"
- },
- "$.body.diff_files[*].file_hash": {
- "match": "type"
- },
- "$.body.diff_files[*].file_path": {
- "match": "type"
- },
- "$.body.diff_files[*].old_path": {
- "match": "type"
- },
- "$.body.diff_files[*].new_path": {
- "match": "type"
- },
- "$.body.diff_files[*].new_file": {
- "match": "type"
- },
- "$.body.diff_files[*].renamed_file": {
- "match": "type"
- },
- "$.body.diff_files[*].deleted_file": {
- "match": "type"
- },
- "$.body.diff_files[*].diff_refs.base_sha": {
- "match": "type"
- },
- "$.body.diff_files[*].diff_refs.start_sha": {
- "match": "type"
- },
- "$.body.diff_files[*].diff_refs.head_sha": {
- "match": "type"
- },
- "$.body.diff_files[*].mode_changed": {
- "match": "type"
- },
- "$.body.diff_files[*].a_mode": {
- "match": "type"
- },
- "$.body.diff_files[*].b_mode": {
- "match": "type"
- },
- "$.body.diff_files[*].viewer.name": {
- "match": "type"
- },
- "$.body.diff_files[*].viewer.collapsed": {
- "match": "type"
- },
- "$.body.diff_files[*].old_size": {
- "match": "type"
- },
- "$.body.diff_files[*].new_size": {
- "match": "type"
- },
- "$.body.diff_files[*].added_lines": {
- "match": "type"
- },
- "$.body.diff_files[*].removed_lines": {
- "match": "type"
- },
- "$.body.diff_files[*].load_collapsed_diff_url": {
- "match": "type"
- },
- "$.body.diff_files[*].view_path": {
- "match": "type"
- },
- "$.body.diff_files[*].context_lines_path": {
- "match": "type"
- },
- "$.body.diff_files[*].highlighted_diff_lines": {
- "min": 1
- },
- "$.body.diff_files[*].highlighted_diff_lines[*].*": {
- "match": "type"
- },
- "$.body.diff_files[*].highlighted_diff_lines[*].text": {
- "match": "type"
- },
- "$.body.diff_files[*].highlighted_diff_lines[*].rich_text": {
- "match": "type"
- },
- "$.body.diff_files[*].highlighted_diff_lines[*].can_receive_suggestion": {
- "match": "type"
- },
- "$.body.diff_files[*].is_fully_expanded": {
- "match": "type"
- },
- "$.body.pagination.total_pages": {
- "match": "type"
- }
- }
- }
- }
- ],
- "metadata": {
- "pactSpecification": {
- "version": "2.0.0"
- }
- }
-} \ No newline at end of file
diff --git a/spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_metadata_endpoint.json b/spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_metadata_endpoint.json
deleted file mode 100644
index c59a3d55f43..00000000000
--- a/spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_metadata_endpoint.json
+++ /dev/null
@@ -1,223 +0,0 @@
-{
- "consumer": {
- "name": "MergeRequest#show"
- },
- "provider": {
- "name": "Merge Request Diffs Metadata Endpoint"
- },
- "interactions": [
- {
- "description": "a request for diffs metadata",
- "providerState": "a merge request exists",
- "request": {
- "method": "GET",
- "path": "/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_metadata.json",
- "headers": {
- "Accept": "*/*"
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "real_size": "1",
- "size": 1,
- "branch_name": "testing-branch-1",
- "source_branch_exists": true,
- "target_branch_name": "master",
- "merge_request_diff": {
- "created_at": "2022-02-17T11:47:08.804Z",
- "commits_count": 1,
- "latest": true,
- "short_commit_sha": "aee1ffec",
- "base_version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773",
- "head_version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_head=true",
- "version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773",
- "compare_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773&start_sha=aee1ffec2299c0cfb17c8821e931339b73a3759f"
- },
- "latest_diff": true,
- "latest_version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs",
- "added_lines": 1,
- "removed_lines": 1,
- "render_overflow_warning": false,
- "email_patch_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1.patch",
- "plain_diff_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1.diff",
- "merge_request_diffs": [
- {
- "commits_count": 1,
- "latest": true,
- "short_commit_sha": "aee1ffec",
- "base_version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773",
- "head_version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_head=true",
- "version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773",
- "compare_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773&start_sha=aee1ffec2299c0cfb17c8821e931339b73a3759f"
- }
- ],
- "definition_path_prefix": "/gitlab-qa-bot/contract-testing/-/blob/aee1ffec2299c0cfb17c8821e931339b73a3759f",
- "diff_files": [
- {
- "added_lines": 1,
- "removed_lines": 1,
- "new_path": "Gemfile",
- "old_path": "Gemfile",
- "new_file": false,
- "deleted_file": false,
- "submodule": false,
- "file_identifier_hash": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587",
- "file_hash": "de3150c01c3a946a6168173c4116741379fe3579"
- }
- ],
- "has_conflicts": false,
- "can_merge": false,
- "project_path": "gitlab-qa-bot/contract-testing",
- "project_name": "contract-testing"
- },
- "matchingRules": {
- "$.body.real_size": {
- "match": "type"
- },
- "$.body.size": {
- "match": "type"
- },
- "$.body.branch_name": {
- "match": "type"
- },
- "$.body.source_branch_exists": {
- "match": "type"
- },
- "$.body.target_branch_name": {
- "match": "type"
- },
- "$.body.merge_request_diff.created_at": {
- "match": "regex",
- "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
- },
- "$.body.merge_request_diff.commits_count": {
- "match": "type"
- },
- "$.body.merge_request_diff.latest": {
- "match": "type"
- },
- "$.body.merge_request_diff.short_commit_sha": {
- "match": "type"
- },
- "$.body.merge_request_diff.base_version_path": {
- "match": "type"
- },
- "$.body.merge_request_diff.head_version_path": {
- "match": "type"
- },
- "$.body.merge_request_diff.version_path": {
- "match": "type"
- },
- "$.body.merge_request_diff.compare_path": {
- "match": "type"
- },
- "$.body.latest_diff": {
- "match": "type"
- },
- "$.body.latest_version_path": {
- "match": "type"
- },
- "$.body.added_lines": {
- "match": "type"
- },
- "$.body.removed_lines": {
- "match": "type"
- },
- "$.body.render_overflow_warning": {
- "match": "type"
- },
- "$.body.email_patch_path": {
- "match": "type"
- },
- "$.body.plain_diff_path": {
- "match": "type"
- },
- "$.body.merge_request_diffs": {
- "min": 1
- },
- "$.body.merge_request_diffs[*].*": {
- "match": "type"
- },
- "$.body.merge_request_diffs[*].commits_count": {
- "match": "type"
- },
- "$.body.merge_request_diffs[*].latest": {
- "match": "type"
- },
- "$.body.merge_request_diffs[*].short_commit_sha": {
- "match": "type"
- },
- "$.body.merge_request_diffs[*].base_version_path": {
- "match": "type"
- },
- "$.body.merge_request_diffs[*].head_version_path": {
- "match": "type"
- },
- "$.body.merge_request_diffs[*].version_path": {
- "match": "type"
- },
- "$.body.merge_request_diffs[*].compare_path": {
- "match": "type"
- },
- "$.body.definition_path_prefix": {
- "match": "type"
- },
- "$.body.diff_files": {
- "min": 1
- },
- "$.body.diff_files[*].*": {
- "match": "type"
- },
- "$.body.diff_files[*].added_lines": {
- "match": "type"
- },
- "$.body.diff_files[*].removed_lines": {
- "match": "type"
- },
- "$.body.diff_files[*].new_path": {
- "match": "type"
- },
- "$.body.diff_files[*].old_path": {
- "match": "type"
- },
- "$.body.diff_files[*].new_file": {
- "match": "type"
- },
- "$.body.diff_files[*].deleted_file": {
- "match": "type"
- },
- "$.body.diff_files[*].submodule": {
- "match": "type"
- },
- "$.body.diff_files[*].file_identifier_hash": {
- "match": "type"
- },
- "$.body.diff_files[*].file_hash": {
- "match": "type"
- },
- "$.body.has_conflicts": {
- "match": "type"
- },
- "$.body.can_merge": {
- "match": "type"
- },
- "$.body.project_path": {
- "match": "type"
- },
- "$.body.project_name": {
- "match": "type"
- }
- }
- }
- }
- ],
- "metadata": {
- "pactSpecification": {
- "version": "2.0.0"
- }
- }
-}
diff --git a/spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_discussions_endpoint.json b/spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_discussions_endpoint.json
deleted file mode 100644
index ecaf9c123af..00000000000
--- a/spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_discussions_endpoint.json
+++ /dev/null
@@ -1,236 +0,0 @@
-{
- "consumer": {
- "name": "MergeRequest#show"
- },
- "provider": {
- "name": "Merge Request Discussions Endpoint"
- },
- "interactions": [
- {
- "description": "a request for discussions",
- "providerState": "a merge request with discussions exists",
- "request": {
- "method": "GET",
- "path": "/gitlab-org/gitlab-qa/-/merge_requests/1/discussions.json",
- "headers": {
- "Accept": "*/*"
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": [
- {
- "id": "fd73763cbcbf7b29eb8765d969a38f7d735e222a",
- "reply_id": "fd73763cbcbf7b29eb8765d969a38f7d735e222a",
- "project_id": 6954442,
- "confidential": false,
- "diff_discussion": false,
- "expanded": false,
- "for_commit": false,
- "individual_note": true,
- "resolvable": false,
- "resolved_by_push": false,
- "notes": [
- {
- "id": "76489845",
- "author": {
- "id": 1675733,
- "username": "gitlab-qa-bot",
- "name": "gitlab-qa-bot",
- "state": "active",
- "avatar_url": "https://secure.gravatar.com/avatar/8355ad0f2761367fae6b9c4fe80994b9?s=80&d=identicon",
- "show_status": false,
- "path": "/gitlab-qa-bot"
- },
- "created_at": "2022-02-22T07:06:55.038Z",
- "updated_at": "2022-02-22T07:06:55.038Z",
- "system": false,
- "noteable_id": 8333422,
- "noteable_type": "MergeRequest",
- "resolvable": false,
- "resolved": true,
- "confidential": false,
- "noteable_iid": 1,
- "note": "This is a test comment",
- "note_html": "<p data-sourcepos=\"1:1-1:22\" dir=\"auto\">This is a test comment</p>",
- "current_user": {
- "can_edit": true,
- "can_award_emoji": true,
- "can_resolve": false,
- "can_resolve_discussion": false
- },
- "is_noteable_author": true,
- "discussion_id": "fd73763cbcbf7b29eb8765d969a38f7d735e222a",
- "emoji_awardable": true,
- "report_abuse_path": "/gitlab-qa-bot/...",
- "noteable_note_url": "https://staging.gitlab.com/gitlab-qa-bot/...",
- "cached_markdown_version": 1900552,
- "human_access": "Maintainer",
- "is_contributor": false,
- "project_name": "contract-testing",
- "path": "/gitlab-qa-bot/..."
- }
- ],
- "resolved": true
- }
- ],
- "matchingRules": {
- "$.body": {
- "min": 1
- },
- "$.body[*].*": {
- "match": "type"
- },
- "$.body[*].id": {
- "match": "type"
- },
- "$.body[*].reply_id": {
- "match": "type"
- },
- "$.body[*].project_id": {
- "match": "type"
- },
- "$.body[*].confidential": {
- "match": "type"
- },
- "$.body[*].diff_discussion": {
- "match": "type"
- },
- "$.body[*].expanded": {
- "match": "type"
- },
- "$.body[*].for_commit": {
- "match": "type"
- },
- "$.body[*].individual_note": {
- "match": "type"
- },
- "$.body[*].resolvable": {
- "match": "type"
- },
- "$.body[*].resolved_by_push": {
- "match": "type"
- },
- "$.body[*].notes": {
- "min": 1
- },
- "$.body[*].notes[*].*": {
- "match": "type"
- },
- "$.body[*].notes[*].id": {
- "match": "type"
- },
- "$.body[*].notes[*].author.id": {
- "match": "type"
- },
- "$.body[*].notes[*].author.username": {
- "match": "type"
- },
- "$.body[*].notes[*].author.name": {
- "match": "type"
- },
- "$.body[*].notes[*].author.state": {
- "match": "type"
- },
- "$.body[*].notes[*].author.avatar_url": {
- "match": "type"
- },
- "$.body[*].notes[*].author.show_status": {
- "match": "type"
- },
- "$.body[*].notes[*].author.path": {
- "match": "type"
- },
- "$.body[*].notes[*].created_at": {
- "match": "regex",
- "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
- },
- "$.body[*].notes[*].updated_at": {
- "match": "regex",
- "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
- },
- "$.body[*].notes[*].system": {
- "match": "type"
- },
- "$.body[*].notes[*].noteable_id": {
- "match": "type"
- },
- "$.body[*].notes[*].noteable_type": {
- "match": "type"
- },
- "$.body[*].notes[*].resolvable": {
- "match": "type"
- },
- "$.body[*].notes[*].resolved": {
- "match": "type"
- },
- "$.body[*].notes[*].confidential": {
- "match": "type"
- },
- "$.body[*].notes[*].noteable_iid": {
- "match": "type"
- },
- "$.body[*].notes[*].note": {
- "match": "type"
- },
- "$.body[*].notes[*].note_html": {
- "match": "type"
- },
- "$.body[*].notes[*].current_user.can_edit": {
- "match": "type"
- },
- "$.body[*].notes[*].current_user.can_award_emoji": {
- "match": "type"
- },
- "$.body[*].notes[*].current_user.can_resolve": {
- "match": "type"
- },
- "$.body[*].notes[*].current_user.can_resolve_discussion": {
- "match": "type"
- },
- "$.body[*].notes[*].is_noteable_author": {
- "match": "type"
- },
- "$.body[*].notes[*].discussion_id": {
- "match": "type"
- },
- "$.body[*].notes[*].emoji_awardable": {
- "match": "type"
- },
- "$.body[*].notes[*].report_abuse_path": {
- "match": "type"
- },
- "$.body[*].notes[*].noteable_note_url": {
- "match": "type"
- },
- "$.body[*].notes[*].cached_markdown_version": {
- "match": "type"
- },
- "$.body[*].notes[*].human_access": {
- "match": "type"
- },
- "$.body[*].notes[*].is_contributor": {
- "match": "type"
- },
- "$.body[*].notes[*].project_name": {
- "match": "type"
- },
- "$.body[*].notes[*].path": {
- "match": "type"
- },
- "$.body[*].resolved": {
- "match": "type"
- }
- }
- }
- }
- ],
- "metadata": {
- "pactSpecification": {
- "version": "2.0.0"
- }
- }
-} \ No newline at end of file
diff --git a/spec/contracts/contracts/project/merge_requests/show/mergerequests#show-get_diffs_batch.json b/spec/contracts/contracts/project/merge_requests/show/mergerequests#show-get_diffs_batch.json
new file mode 100644
index 00000000000..809a3aeec13
--- /dev/null
+++ b/spec/contracts/contracts/project/merge_requests/show/mergerequests#show-get_diffs_batch.json
@@ -0,0 +1,229 @@
+{
+ "consumer": {
+ "name": "MergeRequests#show"
+ },
+ "provider": {
+ "name": "GET diffs batch"
+ },
+ "interactions": [
+ {
+ "description": "a request for diff lines",
+ "providerState": "a merge request with diffs exists",
+ "request": {
+ "method": "GET",
+ "path": "/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_batch.json",
+ "query": "page=0",
+ "headers": {
+ "Accept": "*/*"
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "diff_files": [
+ {
+ "content_sha": "b0c94059db75b2473d616d4b1fde1a77533355a3",
+ "submodule": false,
+ "edit_path": "/gitlab-qa-bot/...",
+ "ide_edit_path": "/gitlab-qa-bot/...",
+ "old_path_html": "Gemfile",
+ "new_path_html": "Gemfile",
+ "blob": {
+ "id": "855071bb3928d140764885964f7be1bb3e582495",
+ "path": "Gemfile",
+ "name": "Gemfile",
+ "mode": "1234567",
+ "readable_text": true,
+ "icon": "doc-text"
+ },
+ "can_modify_blob": false,
+ "file_identifier_hash": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587",
+ "file_hash": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587",
+ "file_path": "Gemfile",
+ "old_path": "Gemfile",
+ "new_path": "Gemfile",
+ "new_file": false,
+ "renamed_file": false,
+ "deleted_file": false,
+ "diff_refs": {
+ "base_sha": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587",
+ "start_sha": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587",
+ "head_sha": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587"
+ },
+ "mode_changed": false,
+ "a_mode": "123456",
+ "b_mode": "123456",
+ "viewer": {
+ "name": "text",
+ "collapsed": false
+ },
+ "old_size": 2288,
+ "new_size": 2288,
+ "added_lines": 1,
+ "removed_lines": 1,
+ "load_collapsed_diff_url": "/gitlab-qa-bot/...",
+ "view_path": "/gitlab-qa-bot/...",
+ "context_lines_path": "/gitlab-qa-bot/...",
+ "highlighted_diff_lines": [
+ {
+ "text": "source",
+ "rich_text": "<span></span>",
+ "can_receive_suggestion": true
+ }
+ ],
+ "is_fully_expanded": false
+ }
+ ],
+ "pagination": {
+ "total_pages": 1
+ }
+ },
+ "matchingRules": {
+ "$.body.diff_files": {
+ "min": 1
+ },
+ "$.body.diff_files[*].*": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].content_sha": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].submodule": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].edit_path": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].ide_edit_path": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].old_path_html": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].new_path_html": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].blob.id": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].blob.path": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].blob.name": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].blob.mode": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].blob.readable_text": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].blob.icon": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].can_modify_blob": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].file_identifier_hash": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].file_hash": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].file_path": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].old_path": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].new_path": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].new_file": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].renamed_file": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].deleted_file": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].diff_refs.base_sha": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].diff_refs.start_sha": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].diff_refs.head_sha": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].mode_changed": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].a_mode": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].b_mode": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].viewer.name": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].viewer.collapsed": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].old_size": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].new_size": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].added_lines": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].removed_lines": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].load_collapsed_diff_url": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].view_path": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].context_lines_path": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].highlighted_diff_lines": {
+ "min": 1
+ },
+ "$.body.diff_files[*].highlighted_diff_lines[*].*": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].highlighted_diff_lines[*].text": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].highlighted_diff_lines[*].rich_text": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].highlighted_diff_lines[*].can_receive_suggestion": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].is_fully_expanded": {
+ "match": "type"
+ },
+ "$.body.pagination.total_pages": {
+ "match": "type"
+ }
+ }
+ }
+ }
+ ],
+ "metadata": {
+ "pactSpecification": {
+ "version": "2.0.0"
+ }
+ }
+} \ No newline at end of file
diff --git a/spec/contracts/contracts/project/merge_requests/show/mergerequests#show-get_diffs_metadata.json b/spec/contracts/contracts/project/merge_requests/show/mergerequests#show-get_diffs_metadata.json
new file mode 100644
index 00000000000..3a7b0707445
--- /dev/null
+++ b/spec/contracts/contracts/project/merge_requests/show/mergerequests#show-get_diffs_metadata.json
@@ -0,0 +1,223 @@
+{
+ "consumer": {
+ "name": "MergeRequests#show"
+ },
+ "provider": {
+ "name": "GET diffs metadata"
+ },
+ "interactions": [
+ {
+ "description": "a request for diffs metadata",
+ "providerState": "a merge request exists",
+ "request": {
+ "method": "GET",
+ "path": "/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_metadata.json",
+ "headers": {
+ "Accept": "*/*"
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "real_size": "1",
+ "size": 1,
+ "branch_name": "testing-branch-1",
+ "source_branch_exists": true,
+ "target_branch_name": "master",
+ "merge_request_diff": {
+ "created_at": "2022-02-17T11:47:08.804Z",
+ "commits_count": 1,
+ "latest": true,
+ "short_commit_sha": "aee1ffec",
+ "base_version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773",
+ "head_version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_head=true",
+ "version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773",
+ "compare_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773&start_sha=aee1ffec2299c0cfb17c8821e931339b73a3759f"
+ },
+ "latest_diff": true,
+ "latest_version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs",
+ "added_lines": 1,
+ "removed_lines": 1,
+ "render_overflow_warning": false,
+ "email_patch_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1.patch",
+ "plain_diff_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1.diff",
+ "merge_request_diffs": [
+ {
+ "commits_count": 1,
+ "latest": true,
+ "short_commit_sha": "aee1ffec",
+ "base_version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773",
+ "head_version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_head=true",
+ "version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773",
+ "compare_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773&start_sha=aee1ffec2299c0cfb17c8821e931339b73a3759f"
+ }
+ ],
+ "definition_path_prefix": "/gitlab-qa-bot/contract-testing/-/blob/aee1ffec2299c0cfb17c8821e931339b73a3759f",
+ "diff_files": [
+ {
+ "added_lines": 1,
+ "removed_lines": 1,
+ "new_path": "Gemfile",
+ "old_path": "Gemfile",
+ "new_file": false,
+ "deleted_file": false,
+ "submodule": false,
+ "file_identifier_hash": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587",
+ "file_hash": "de3150c01c3a946a6168173c4116741379fe3579"
+ }
+ ],
+ "has_conflicts": false,
+ "can_merge": false,
+ "project_path": "gitlab-qa-bot/contract-testing",
+ "project_name": "contract-testing"
+ },
+ "matchingRules": {
+ "$.body.real_size": {
+ "match": "type"
+ },
+ "$.body.size": {
+ "match": "type"
+ },
+ "$.body.branch_name": {
+ "match": "type"
+ },
+ "$.body.source_branch_exists": {
+ "match": "type"
+ },
+ "$.body.target_branch_name": {
+ "match": "type"
+ },
+ "$.body.merge_request_diff.created_at": {
+ "match": "regex",
+ "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
+ },
+ "$.body.merge_request_diff.commits_count": {
+ "match": "type"
+ },
+ "$.body.merge_request_diff.latest": {
+ "match": "type"
+ },
+ "$.body.merge_request_diff.short_commit_sha": {
+ "match": "type"
+ },
+ "$.body.merge_request_diff.base_version_path": {
+ "match": "type"
+ },
+ "$.body.merge_request_diff.head_version_path": {
+ "match": "type"
+ },
+ "$.body.merge_request_diff.version_path": {
+ "match": "type"
+ },
+ "$.body.merge_request_diff.compare_path": {
+ "match": "type"
+ },
+ "$.body.latest_diff": {
+ "match": "type"
+ },
+ "$.body.latest_version_path": {
+ "match": "type"
+ },
+ "$.body.added_lines": {
+ "match": "type"
+ },
+ "$.body.removed_lines": {
+ "match": "type"
+ },
+ "$.body.render_overflow_warning": {
+ "match": "type"
+ },
+ "$.body.email_patch_path": {
+ "match": "type"
+ },
+ "$.body.plain_diff_path": {
+ "match": "type"
+ },
+ "$.body.merge_request_diffs": {
+ "min": 1
+ },
+ "$.body.merge_request_diffs[*].*": {
+ "match": "type"
+ },
+ "$.body.merge_request_diffs[*].commits_count": {
+ "match": "type"
+ },
+ "$.body.merge_request_diffs[*].latest": {
+ "match": "type"
+ },
+ "$.body.merge_request_diffs[*].short_commit_sha": {
+ "match": "type"
+ },
+ "$.body.merge_request_diffs[*].base_version_path": {
+ "match": "type"
+ },
+ "$.body.merge_request_diffs[*].head_version_path": {
+ "match": "type"
+ },
+ "$.body.merge_request_diffs[*].version_path": {
+ "match": "type"
+ },
+ "$.body.merge_request_diffs[*].compare_path": {
+ "match": "type"
+ },
+ "$.body.definition_path_prefix": {
+ "match": "type"
+ },
+ "$.body.diff_files": {
+ "min": 1
+ },
+ "$.body.diff_files[*].*": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].added_lines": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].removed_lines": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].new_path": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].old_path": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].new_file": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].deleted_file": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].submodule": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].file_identifier_hash": {
+ "match": "type"
+ },
+ "$.body.diff_files[*].file_hash": {
+ "match": "type"
+ },
+ "$.body.has_conflicts": {
+ "match": "type"
+ },
+ "$.body.can_merge": {
+ "match": "type"
+ },
+ "$.body.project_path": {
+ "match": "type"
+ },
+ "$.body.project_name": {
+ "match": "type"
+ }
+ }
+ }
+ }
+ ],
+ "metadata": {
+ "pactSpecification": {
+ "version": "2.0.0"
+ }
+ }
+} \ No newline at end of file
diff --git a/spec/contracts/contracts/project/merge_requests/show/mergerequests#show-get_discussions.json b/spec/contracts/contracts/project/merge_requests/show/mergerequests#show-get_discussions.json
new file mode 100644
index 00000000000..74f75e7eb7b
--- /dev/null
+++ b/spec/contracts/contracts/project/merge_requests/show/mergerequests#show-get_discussions.json
@@ -0,0 +1,236 @@
+{
+ "consumer": {
+ "name": "MergeRequests#show"
+ },
+ "provider": {
+ "name": "GET discussions"
+ },
+ "interactions": [
+ {
+ "description": "a request for discussions",
+ "providerState": "a merge request with discussions exists",
+ "request": {
+ "method": "GET",
+ "path": "/gitlab-org/gitlab-qa/-/merge_requests/1/discussions.json",
+ "headers": {
+ "Accept": "*/*"
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": [
+ {
+ "id": "fd73763cbcbf7b29eb8765d969a38f7d735e222a",
+ "reply_id": "fd73763cbcbf7b29eb8765d969a38f7d735e222a",
+ "project_id": 6954442,
+ "confidential": false,
+ "diff_discussion": false,
+ "expanded": false,
+ "for_commit": false,
+ "individual_note": true,
+ "resolvable": false,
+ "resolved_by_push": false,
+ "notes": [
+ {
+ "id": "76489845",
+ "author": {
+ "id": 1675733,
+ "username": "gitlab-qa-bot",
+ "name": "gitlab-qa-bot",
+ "state": "active",
+ "avatar_url": "https://secure.gravatar.com/avatar/8355ad0f2761367fae6b9c4fe80994b9?s=80&d=identicon",
+ "show_status": false,
+ "path": "/gitlab-qa-bot"
+ },
+ "created_at": "2022-02-22T07:06:55.038Z",
+ "updated_at": "2022-02-22T07:06:55.038Z",
+ "system": false,
+ "noteable_id": 8333422,
+ "noteable_type": "MergeRequest",
+ "resolvable": false,
+ "resolved": true,
+ "confidential": false,
+ "noteable_iid": 1,
+ "note": "This is a test comment",
+ "note_html": "<p data-sourcepos=\"1:1-1:22\" dir=\"auto\">This is a test comment</p>",
+ "current_user": {
+ "can_edit": true,
+ "can_award_emoji": true,
+ "can_resolve": false,
+ "can_resolve_discussion": false
+ },
+ "is_noteable_author": true,
+ "discussion_id": "fd73763cbcbf7b29eb8765d969a38f7d735e222a",
+ "emoji_awardable": true,
+ "report_abuse_path": "/gitlab-qa-bot/...",
+ "noteable_note_url": "https://staging.gitlab.com/gitlab-qa-bot/...",
+ "cached_markdown_version": 1900552,
+ "human_access": "Maintainer",
+ "is_contributor": false,
+ "project_name": "contract-testing",
+ "path": "/gitlab-qa-bot/..."
+ }
+ ],
+ "resolved": true
+ }
+ ],
+ "matchingRules": {
+ "$.body": {
+ "min": 1
+ },
+ "$.body[*].*": {
+ "match": "type"
+ },
+ "$.body[*].id": {
+ "match": "type"
+ },
+ "$.body[*].reply_id": {
+ "match": "type"
+ },
+ "$.body[*].project_id": {
+ "match": "type"
+ },
+ "$.body[*].confidential": {
+ "match": "type"
+ },
+ "$.body[*].diff_discussion": {
+ "match": "type"
+ },
+ "$.body[*].expanded": {
+ "match": "type"
+ },
+ "$.body[*].for_commit": {
+ "match": "type"
+ },
+ "$.body[*].individual_note": {
+ "match": "type"
+ },
+ "$.body[*].resolvable": {
+ "match": "type"
+ },
+ "$.body[*].resolved_by_push": {
+ "match": "type"
+ },
+ "$.body[*].notes": {
+ "min": 1
+ },
+ "$.body[*].notes[*].*": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].id": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].author.id": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].author.username": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].author.name": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].author.state": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].author.avatar_url": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].author.show_status": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].author.path": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].created_at": {
+ "match": "regex",
+ "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
+ },
+ "$.body[*].notes[*].updated_at": {
+ "match": "regex",
+ "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
+ },
+ "$.body[*].notes[*].system": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].noteable_id": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].noteable_type": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].resolvable": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].resolved": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].confidential": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].noteable_iid": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].note": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].note_html": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].current_user.can_edit": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].current_user.can_award_emoji": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].current_user.can_resolve": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].current_user.can_resolve_discussion": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].is_noteable_author": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].discussion_id": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].emoji_awardable": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].report_abuse_path": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].noteable_note_url": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].cached_markdown_version": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].human_access": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].is_contributor": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].project_name": {
+ "match": "type"
+ },
+ "$.body[*].notes[*].path": {
+ "match": "type"
+ },
+ "$.body[*].resolved": {
+ "match": "type"
+ }
+ }
+ }
+ }
+ ],
+ "metadata": {
+ "pactSpecification": {
+ "version": "2.0.0"
+ }
+ }
+} \ No newline at end of file
diff --git a/spec/contracts/contracts/project/pipeline/index/pipelines#index-get_list_project_pipelines.json b/spec/contracts/contracts/project/pipeline/index/pipelines#index-get_list_project_pipelines.json
deleted file mode 100644
index 2ebfd1bfdf2..00000000000
--- a/spec/contracts/contracts/project/pipeline/index/pipelines#index-get_list_project_pipelines.json
+++ /dev/null
@@ -1,478 +0,0 @@
-{
- "consumer": {
- "name": "Pipelines#index"
- },
- "provider": {
- "name": "GET List project pipelines"
- },
- "interactions": [
- {
- "description": "a request for a list of project pipelines",
- "providerState": "a few pipelines for a project exists",
- "request": {
- "method": "GET",
- "path": "/gitlab-org/gitlab-qa/-/pipelines.json",
- "query": "scope=all&page=1",
- "headers": {
- "Accept": "*/*"
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "pipelines": [
- {
- "id": 564173401,
- "iid": 8197225,
- "user": {
- "id": 1781152,
- "username": "gitlab-bot",
- "name": "🤖 GitLab Bot 🤖",
- "state": "active",
- "avatar_url": "https://gitlab.com/uploads/-/system/user/avatar/1516152/avatar.png",
- "web_url": "https://gitlab.com/gitlab-bot",
- "show_status": false,
- "path": "/gitlab-bot"
- },
- "active": true,
- "source": "schedule",
- "created_at": "2022-06-11T00:05:21.558Z",
- "updated_at": "2022-06-11T00:05:34.258Z",
- "path": "/gitlab-org/gitlab/-/pipelines/561224401",
- "flags": {
- "stuck": false,
- "auto_devops": false,
- "merge_request": false,
- "yaml_errors": false,
- "retryable": false,
- "cancelable": false,
- "failure_reason": false,
- "detached_merge_request_pipeline": false,
- "merge_request_pipeline": false,
- "merge_train_pipeline": false,
- "latest": true
- },
- "details": {
- "status": {
- "icon": "status_running",
- "text": "running",
- "label": "running",
- "group": "running",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab/-/pipelines/566374401",
- "illustration": null,
- "favicon": "/assets/ci_favicons/favicon_status_running.png"
- },
- "stages": [
- {
- "name": "sync",
- "title": "sync: passed",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab/-/pipelines/561174401#sync",
- "illustration": null,
- "favicon": "/assets/ci_favicons/favicon_status_success.png"
- },
- "path": "/gitlab-org/gitlab/-/pipelines/561124401#sync",
- "dropdown_path": "/gitlab-org/gitlab/-/pipelines/561174401/stage.json?stage=sync"
- }
- ],
- "duration": 25,
- "finished_at": "2022-06-11T00:55:21.558Z",
- "name": "Pipeline",
- "manual_actions": [
- {
- "name": "review-docs-deploy",
- "playable": true,
- "scheduled": false
- }
- ],
- "scheduled_actions": [
- {
- "name": "review-docs-schedule",
- "playable": true,
- "scheduled": false
- }
- ]
- },
- "ref": {
- "name": "master",
- "path": "/gitlab-org/gitlab/-/commits/master",
- "tag": false,
- "branch": true,
- "merge_request": false
- },
- "commit": {
- "id": "e6d797385144b955c6d4ecfa00e9656dc33efd2b",
- "short_id": "e6d79738",
- "created_at": "2022-06-10T22:02:10.000+00:00",
- "parent_ids": [
- "3b0e053a24958174eaa7e3b183c7263432890d1c"
- ],
- "title": "Merge branch 'ee-test' into 'master'",
- "message": "Merge branch 'ee-test' into 'master'\nThis is a test.",
- "author_name": "John Doe",
- "author_email": "jdoe@gitlab.com",
- "authored_date": "2022-06-10T22:02:10.000+00:00",
- "committer_name": "John Doe",
- "committer_email": "jdoe@gitlab.com",
- "committed_date": "2022-06-10T22:02:10.000+00:00",
- "trailers": {
- },
- "web_url": "https://gitlab.com/gitlab-org/gitlab/-/commit/f559253c514d9ab707c66e",
- "author": null,
- "author_gravatar_url": "https://secure.gravatar.com/avatar/d85e45af29611ac2c1395e3c3d6ec5d6?s=80&d=identicon",
- "commit_url": "https://gitlab.com/gitlab-org/gitlab/-/commit/dc7522f559253c514d9ab707c66e7a1026abca5a",
- "commit_path": "/gitlab-org/gitlab/-/commit/dc7522f559253c514d9ab707c66e7a1026abca5a"
- },
- "project": {
- "id": 253964,
- "name": "GitLab",
- "full_path": "/gitlab-org/gitlab",
- "full_name": "GitLab.org / GitLab"
- },
- "triggered_by": null,
- "triggered": [
-
- ]
- }
- ],
- "count": {
- "all": "1,000+"
- }
- },
- "matchingRules": {
- "$.body.pipelines": {
- "min": 1
- },
- "$.body.pipelines[*].*": {
- "match": "type"
- },
- "$.body.pipelines[*].id": {
- "match": "type"
- },
- "$.body.pipelines[*].iid": {
- "match": "type"
- },
- "$.body.pipelines[*].user.id": {
- "match": "type"
- },
- "$.body.pipelines[*].user.username": {
- "match": "type"
- },
- "$.body.pipelines[*].user.name": {
- "match": "type"
- },
- "$.body.pipelines[*].user.state": {
- "match": "regex",
- "regex": "^(active|blocked)$"
- },
- "$.body.pipelines[*].user.avatar_url": {
- "match": "regex",
- "regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
- },
- "$.body.pipelines[*].user.web_url": {
- "match": "regex",
- "regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
- },
- "$.body.pipelines[*].user.show_status": {
- "match": "type"
- },
- "$.body.pipelines[*].user.path": {
- "match": "regex",
- "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
- },
- "$.body.pipelines[*].active": {
- "match": "type"
- },
- "$.body.pipelines[*].source": {
- "match": "regex",
- "regex": "^(push|web|trigger|schedule|api|external|pipeline|chat|webide|merge_request_event|external_pull_request_event|parent_pipeline|ondemand_dast_scan|ondemand_dast_validation)$"
- },
- "$.body.pipelines[*].name": {
- "match": "type"
- },
- "$.body.pipelines[*].created_at": {
- "match": "regex",
- "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
- },
- "$.body.pipelines[*].updated_at": {
- "match": "regex",
- "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
- },
- "$.body.pipelines[*].path": {
- "match": "regex",
- "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
- },
- "$.body.pipelines[*].flags.stuck": {
- "match": "type"
- },
- "$.body.pipelines[*].flags.auto_devops": {
- "match": "type"
- },
- "$.body.pipelines[*].flags.merge_request": {
- "match": "type"
- },
- "$.body.pipelines[*].flags.yaml_errors": {
- "match": "type"
- },
- "$.body.pipelines[*].flags.retryable": {
- "match": "type"
- },
- "$.body.pipelines[*].flags.cancelable": {
- "match": "type"
- },
- "$.body.pipelines[*].flags.failure_reason": {
- "match": "type"
- },
- "$.body.pipelines[*].flags.detached_merge_request_pipeline": {
- "match": "type"
- },
- "$.body.pipelines[*].flags.merge_request_pipeline": {
- "match": "type"
- },
- "$.body.pipelines[*].flags.merge_train_pipeline": {
- "match": "type"
- },
- "$.body.pipelines[*].flags.latest": {
- "match": "type"
- },
- "$.body.pipelines[*].details.status.icon": {
- "match": "regex",
- "regex": "^status_(canceled|created|failed|manual|pending|preparing|running|scheduled|skipped|success|warning)$"
- },
- "$.body.pipelines[*].details.status.text": {
- "match": "regex",
- "regex": "^(canceled|created|delayed|failed|manual|passed|pending|preparing|running|skipped|waiting)$"
- },
- "$.body.pipelines[*].details.status.label": {
- "match": "regex",
- "regex": "^(canceled|created|delayed|failed|manual action|passed|pending|preparing|running|skipped|passed with warnings|waiting for resource)$"
- },
- "$.body.pipelines[*].details.status.group": {
- "match": "regex",
- "regex": "^(canceled|created|failed|manual|pending|preparing|running|scheduled|skipped|success|success_warning|waiting-for-resource)$"
- },
- "$.body.pipelines[*].details.status.tooltip": {
- "match": "regex",
- "regex": "^(canceled|created|delayed|failed|manual action|passed|pending|preparing|running|skipped|passed with warnings|waiting for resource)$"
- },
- "$.body.pipelines[*].details.status.has_details": {
- "match": "type"
- },
- "$.body.pipelines[*].details.status.details_path": {
- "match": "regex",
- "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
- },
- "$.body.pipelines[*].details.status.favicon": {
- "match": "regex",
- "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
- },
- "$.body.pipelines[*].details.stages": {
- "min": 1
- },
- "$.body.pipelines[*].details.stages[*].*": {
- "match": "type"
- },
- "$.body.pipelines[*].details.stages[*].name": {
- "match": "type"
- },
- "$.body.pipelines[*].details.stages[*].title": {
- "match": "type"
- },
- "$.body.pipelines[*].details.stages[*].status.icon": {
- "match": "regex",
- "regex": "^status_(canceled|created|failed|manual|pending|preparing|running|scheduled|skipped|success|warning)$"
- },
- "$.body.pipelines[*].details.stages[*].status.text": {
- "match": "regex",
- "regex": "^(canceled|created|delayed|failed|manual|passed|pending|preparing|running|skipped|waiting)$"
- },
- "$.body.pipelines[*].details.stages[*].status.label": {
- "match": "regex",
- "regex": "^(canceled|created|delayed|failed|manual action|passed|pending|preparing|running|skipped|passed with warnings|waiting for resource)$"
- },
- "$.body.pipelines[*].details.stages[*].status.group": {
- "match": "regex",
- "regex": "^(canceled|created|failed|manual|pending|preparing|running|scheduled|skipped|success|success_warning|waiting-for-resource)$"
- },
- "$.body.pipelines[*].details.stages[*].status.tooltip": {
- "match": "regex",
- "regex": "^(canceled|created|delayed|failed|manual action|passed|pending|preparing|running|skipped|passed with warnings|waiting for resource)$"
- },
- "$.body.pipelines[*].details.stages[*].status.has_details": {
- "match": "type"
- },
- "$.body.pipelines[*].details.stages[*].status.details_path": {
- "match": "regex",
- "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
- },
- "$.body.pipelines[*].details.stages[*].status.favicon": {
- "match": "regex",
- "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
- },
- "$.body.pipelines[*].details.stages[*].path": {
- "match": "regex",
- "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
- },
- "$.body.pipelines[*].details.stages[*].dropdown_path": {
- "match": "regex",
- "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
- },
- "$.body.pipelines[*].details.duration": {
- "match": "type"
- },
- "$.body.pipelines[*].details.finished_at": {
- "match": "regex",
- "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
- },
- "$.body.pipelines[*].details.name": {
- "match": "type"
- },
- "$.body.pipelines[*].details.event_type_name": {
- "match": "type"
- },
- "$.body.pipelines[*].details.manual_actions": {
- "min": 1
- },
- "$.body.pipelines[*].details.manual_actions[*].*": {
- "match": "type"
- },
- "$.body.pipelines[*].details.manual_actions[*].name": {
- "match": "type"
- },
- "$.body.pipelines[*].details.manual_actions[*].playable": {
- "match": "type"
- },
- "$.body.pipelines[*].details.manual_actions[*].scheduled": {
- "match": "type"
- },
- "$.body.pipelines[*].details.scheduled_actions": {
- "min": 1
- },
- "$.body.pipelines[*].details.scheduled_actions[*].*": {
- "match": "type"
- },
- "$.body.pipelines[*].details.scheduled_actions[*].name": {
- "match": "type"
- },
- "$.body.pipelines[*].details.scheduled_actions[*].playable": {
- "match": "type"
- },
- "$.body.pipelines[*].details.scheduled_actions[*].scheduled": {
- "match": "type"
- },
- "$.body.pipelines[*].ref.name": {
- "match": "type"
- },
- "$.body.pipelines[*].ref.path": {
- "match": "regex",
- "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
- },
- "$.body.pipelines[*].ref.tag": {
- "match": "type"
- },
- "$.body.pipelines[*].ref.branch": {
- "match": "type"
- },
- "$.body.pipelines[*].ref.merge_request": {
- "match": "type"
- },
- "$.body.pipelines[*].commit.id": {
- "match": "type"
- },
- "$.body.pipelines[*].commit.short_id": {
- "match": "type"
- },
- "$.body.pipelines[*].commit.created_at": {
- "match": "regex",
- "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
- },
- "$.body.pipelines[*].commit.parent_ids": {
- "min": 1
- },
- "$.body.pipelines[*].commit.parent_ids[*].*": {
- "match": "type"
- },
- "$.body.pipelines[*].commit.parent_ids[*]": {
- "match": "type"
- },
- "$.body.pipelines[*].commit.title": {
- "match": "type"
- },
- "$.body.pipelines[*].commit.message": {
- "match": "type"
- },
- "$.body.pipelines[*].commit.author_name": {
- "match": "type"
- },
- "$.body.pipelines[*].commit.author_email": {
- "match": "regex",
- "regex": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$"
- },
- "$.body.pipelines[*].commit.authored_date": {
- "match": "regex",
- "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
- },
- "$.body.pipelines[*].commit.committer_name": {
- "match": "type"
- },
- "$.body.pipelines[*].commit.committer_email": {
- "match": "regex",
- "regex": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$"
- },
- "$.body.pipelines[*].commit.committed_date": {
- "match": "regex",
- "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
- },
- "$.body.pipelines[*].commit.web_url": {
- "match": "regex",
- "regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
- },
- "$.body.pipelines[*].commit.author_gravatar_url": {
- "match": "regex",
- "regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
- },
- "$.body.pipelines[*].commit.commit_url": {
- "match": "regex",
- "regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
- },
- "$.body.pipelines[*].commit.commit_path": {
- "match": "regex",
- "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
- },
- "$.body.pipelines[*].project.id": {
- "match": "type"
- },
- "$.body.pipelines[*].project.name": {
- "match": "type"
- },
- "$.body.pipelines[*].project.full_path": {
- "match": "regex",
- "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
- },
- "$.body.pipelines[*].project.full_name": {
- "match": "type"
- },
- "$.body.count.all": {
- "match": "type"
- }
- }
- }
- }
- ],
- "metadata": {
- "pactSpecification": {
- "version": "2.0.0"
- }
- }
-} \ No newline at end of file
diff --git a/spec/contracts/contracts/project/pipeline/new/pipelines#new-post_create_a_new_pipeline.json b/spec/contracts/contracts/project/pipeline/new/pipelines#new-post_create_a_new_pipeline.json
deleted file mode 100644
index 4627f0cb0bf..00000000000
--- a/spec/contracts/contracts/project/pipeline/new/pipelines#new-post_create_a_new_pipeline.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- "consumer": {
- "name": "Pipelines#new"
- },
- "provider": {
- "name": "POST Create a new pipeline"
- },
- "interactions": [
- {
- "description": "a request to create a new pipeline",
- "providerState": "a project with a valid .gitlab-ci.yml configuration exists",
- "request": {
- "method": "POST",
- "path": "/gitlab-org/gitlab-qa/-/pipelines",
- "headers": {
- "Accept": "*/*",
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "ref": "master"
- }
- },
- "response": {
- "status": 302,
- "headers": {
- "Content-Type": "text/html; charset=utf-8"
- },
- "body": "<html><body>You are being <a href=\"http://example.org/gitlab-org/gitlab-qa/-/pipelines/5\">redirected</a>.</body></html>",
- "matchingRules": {
- "$.body": {
- "match": "regex",
- "regex": "You are being <a href=\\\"(.)+\\/pipelines\\/[0-9]+\\\">redirected<\\/a>."
- }
- }
- }
- }
- ],
- "metadata": {
- "pactSpecification": {
- "version": "2.0.0"
- }
- }
-} \ No newline at end of file
diff --git a/spec/contracts/contracts/project/pipeline_schedule/edit/pipelineschedules#edit-put_edit_a_pipeline_schedule.json b/spec/contracts/contracts/project/pipeline_schedule/edit/pipelineschedules#edit-put_edit_a_pipeline_schedule.json
deleted file mode 100644
index e0dd68dc230..00000000000
--- a/spec/contracts/contracts/project/pipeline_schedule/edit/pipelineschedules#edit-put_edit_a_pipeline_schedule.json
+++ /dev/null
@@ -1,48 +0,0 @@
-{
- "consumer": {
- "name": "PipelineSchedules#edit"
- },
- "provider": {
- "name": "PUT Edit a pipeline schedule"
- },
- "interactions": [
- {
- "description": "a request to edit a pipeline schedule",
- "providerState": "a project with a pipeline schedule exists",
- "request": {
- "method": "PUT",
- "path": "/gitlab-org/gitlab-qa/-/pipeline_schedules/25",
- "headers": {
- "Accept": "*/*",
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "schedule": {
- "description": "bar",
- "cron": "0 1 * * *",
- "cron_timezone": "UTC",
- "active": true
- }
- }
- },
- "response": {
- "status": 302,
- "headers": {
- "Content-Type": "text/html; charset=utf-8"
- },
- "body": "<html><body>You are being <a href=\"http://example.org/gitlab-org/gitlab-qa/-/pipelines/5\">redirected</a>.</body></html>",
- "matchingRules": {
- "$.body": {
- "match": "regex",
- "regex": "You are being <a href=\\\"(.)+\\\">redirected<\\/a>."
- }
- }
- }
- }
- ],
- "metadata": {
- "pactSpecification": {
- "version": "2.0.0"
- }
- }
-} \ No newline at end of file
diff --git a/spec/contracts/contracts/project/pipeline_schedules/edit/pipelineschedules#edit-put_edit_a_pipeline_schedule.json b/spec/contracts/contracts/project/pipeline_schedules/edit/pipelineschedules#edit-put_edit_a_pipeline_schedule.json
new file mode 100644
index 00000000000..59feebcfc64
--- /dev/null
+++ b/spec/contracts/contracts/project/pipeline_schedules/edit/pipelineschedules#edit-put_edit_a_pipeline_schedule.json
@@ -0,0 +1,48 @@
+{
+ "consumer": {
+ "name": "PipelineSchedules#edit"
+ },
+ "provider": {
+ "name": "PUT edit a pipeline schedule"
+ },
+ "interactions": [
+ {
+ "description": "a request to edit a pipeline schedule",
+ "providerState": "a project with a pipeline schedule exists",
+ "request": {
+ "method": "PUT",
+ "path": "/gitlab-org/gitlab-qa/-/pipeline_schedules/25",
+ "headers": {
+ "Accept": "*/*",
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "schedule": {
+ "description": "bar",
+ "cron": "0 1 * * *",
+ "cron_timezone": "UTC",
+ "active": true
+ }
+ }
+ },
+ "response": {
+ "status": 302,
+ "headers": {
+ "Content-Type": "text/html; charset=utf-8"
+ },
+ "body": "<html><body>You are being <a href=\"http://example.org/gitlab-org/gitlab-qa/-/pipelines/5\">redirected</a>.</body></html>",
+ "matchingRules": {
+ "$.body": {
+ "match": "regex",
+ "regex": "You are being <a href=\\\"(.)+\\\">redirected<\\/a>."
+ }
+ }
+ }
+ }
+ ],
+ "metadata": {
+ "pactSpecification": {
+ "version": "2.0.0"
+ }
+ }
+} \ No newline at end of file
diff --git a/spec/contracts/contracts/project/pipelines/index/pipelines#index-get_list_project_pipelines.json b/spec/contracts/contracts/project/pipelines/index/pipelines#index-get_list_project_pipelines.json
new file mode 100644
index 00000000000..01c6563f76a
--- /dev/null
+++ b/spec/contracts/contracts/project/pipelines/index/pipelines#index-get_list_project_pipelines.json
@@ -0,0 +1,469 @@
+{
+ "consumer": {
+ "name": "Pipelines#index"
+ },
+ "provider": {
+ "name": "GET list project pipelines"
+ },
+ "interactions": [
+ {
+ "description": "a request for a list of project pipelines",
+ "providerState": "a few pipelines for a project exists",
+ "request": {
+ "method": "GET",
+ "path": "/gitlab-org/gitlab-qa/-/pipelines.json",
+ "query": "scope=all&page=1",
+ "headers": {
+ "Accept": "*/*"
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "pipelines": [
+ {
+ "id": 564173401,
+ "iid": 8197225,
+ "user": {
+ "id": 1781152,
+ "username": "gitlab-bot",
+ "name": "🤖 GitLab Bot 🤖",
+ "state": "active",
+ "avatar_url": "https://gitlab.com/uploads/-/system/user/avatar/1516152/avatar.png",
+ "web_url": "https://gitlab.com/gitlab-bot",
+ "show_status": false,
+ "path": "/gitlab-bot"
+ },
+ "active": true,
+ "source": "schedule",
+ "created_at": "2022-06-11T00:05:21.558Z",
+ "updated_at": "2022-06-11T00:05:34.258Z",
+ "path": "/gitlab-org/gitlab/-/pipelines/561224401",
+ "flags": {
+ "stuck": false,
+ "auto_devops": false,
+ "merge_request": false,
+ "yaml_errors": false,
+ "retryable": false,
+ "cancelable": false,
+ "failure_reason": false,
+ "detached_merge_request_pipeline": false,
+ "merge_request_pipeline": false,
+ "merge_train_pipeline": false,
+ "latest": true
+ },
+ "details": {
+ "status": {
+ "icon": "status_running",
+ "text": "running",
+ "label": "running",
+ "group": "running",
+ "tooltip": "passed",
+ "has_details": true,
+ "details_path": "/gitlab-org/gitlab/-/pipelines/566374401",
+ "illustration": null,
+ "favicon": "/assets/ci_favicons/favicon_status_running.png"
+ },
+ "stages": [
+ {
+ "name": "sync",
+ "title": "sync: passed",
+ "status": {
+ "icon": "status_success",
+ "text": "passed",
+ "label": "passed",
+ "group": "success",
+ "tooltip": "passed",
+ "has_details": true,
+ "details_path": "/gitlab-org/gitlab/-/pipelines/561174401#sync",
+ "illustration": null,
+ "favicon": "/assets/ci_favicons/favicon_status_success.png"
+ },
+ "path": "/gitlab-org/gitlab/-/pipelines/561124401#sync",
+ "dropdown_path": "/gitlab-org/gitlab/-/pipelines/561174401/stage.json?stage=sync"
+ }
+ ],
+ "duration": 25,
+ "finished_at": "2022-06-11T00:55:21.558Z",
+ "name": "Pipeline",
+ "manual_actions": [
+ {
+ "name": "review-docs-deploy",
+ "playable": true,
+ "scheduled": false
+ }
+ ],
+ "scheduled_actions": [
+ {
+ "name": "review-docs-schedule",
+ "playable": true,
+ "scheduled": false
+ }
+ ]
+ },
+ "ref": {
+ "name": "master",
+ "path": "/gitlab-org/gitlab/-/commits/master",
+ "tag": false,
+ "branch": true,
+ "merge_request": false
+ },
+ "commit": {
+ "id": "e6d797385144b955c6d4ecfa00e9656dc33efd2b",
+ "short_id": "e6d79738",
+ "created_at": "2022-06-10T22:02:10.000+00:00",
+ "parent_ids": [
+ "3b0e053a24958174eaa7e3b183c7263432890d1c"
+ ],
+ "title": "Merge branch 'ee-test' into 'master'",
+ "message": "Merge branch 'ee-test' into 'master'\nThis is a test.",
+ "author_name": "John Doe",
+ "author_email": "jdoe@gitlab.com",
+ "authored_date": "2022-06-10T22:02:10.000+00:00",
+ "committer_name": "John Doe",
+ "committer_email": "jdoe@gitlab.com",
+ "committed_date": "2022-06-10T22:02:10.000+00:00",
+ "trailers": {},
+ "web_url": "https://gitlab.com/gitlab-org/gitlab/-/commit/f559253c514d9ab707c66e",
+ "author": null,
+ "author_gravatar_url": "https://secure.gravatar.com/avatar/d85e45af29611ac2c1395e3c3d6ec5d6?s=80&d=identicon",
+ "commit_url": "https://gitlab.com/gitlab-org/gitlab/-/commit/dc7522f559253c514d9ab707c66e7a1026abca5a",
+ "commit_path": "/gitlab-org/gitlab/-/commit/dc7522f559253c514d9ab707c66e7a1026abca5a"
+ },
+ "project": {
+ "id": 253964,
+ "name": "GitLab",
+ "full_path": "/gitlab-org/gitlab",
+ "full_name": "GitLab.org / GitLab"
+ },
+ "triggered_by": null,
+ "triggered": []
+ }
+ ],
+ "count": {
+ "all": "1,000+"
+ }
+ },
+ "matchingRules": {
+ "$.body.pipelines": {
+ "min": 1
+ },
+ "$.body.pipelines[*].*": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].id": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].iid": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].user.id": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].user.username": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].user.name": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].user.state": {
+ "match": "regex",
+ "regex": "^(active|blocked)$"
+ },
+ "$.body.pipelines[*].user.avatar_url": {
+ "match": "regex",
+ "regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
+ },
+ "$.body.pipelines[*].user.web_url": {
+ "match": "regex",
+ "regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
+ },
+ "$.body.pipelines[*].user.show_status": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].user.path": {
+ "match": "regex",
+ "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
+ },
+ "$.body.pipelines[*].active": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].source": {
+ "match": "regex",
+ "regex": "^(push|web|trigger|schedule|api|external|pipeline|chat|webide|merge_request_event|external_pull_request_event|parent_pipeline|ondemand_dast_scan|ondemand_dast_validation)$"
+ },
+ "$.body.pipelines[*].created_at": {
+ "match": "regex",
+ "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
+ },
+ "$.body.pipelines[*].updated_at": {
+ "match": "regex",
+ "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
+ },
+ "$.body.pipelines[*].path": {
+ "match": "regex",
+ "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
+ },
+ "$.body.pipelines[*].flags.stuck": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].flags.auto_devops": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].flags.merge_request": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].flags.yaml_errors": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].flags.retryable": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].flags.cancelable": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].flags.failure_reason": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].flags.detached_merge_request_pipeline": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].flags.merge_request_pipeline": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].flags.merge_train_pipeline": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].flags.latest": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].details.status.icon": {
+ "match": "regex",
+ "regex": "^status_(canceled|created|failed|manual|pending|preparing|running|scheduled|skipped|success|warning)$"
+ },
+ "$.body.pipelines[*].details.status.text": {
+ "match": "regex",
+ "regex": "^(canceled|created|delayed|failed|manual|passed|pending|preparing|running|skipped|waiting)$"
+ },
+ "$.body.pipelines[*].details.status.label": {
+ "match": "regex",
+ "regex": "^(canceled|created|delayed|failed|manual action|passed|pending|preparing|running|skipped|passed with warnings|waiting for resource)$"
+ },
+ "$.body.pipelines[*].details.status.group": {
+ "match": "regex",
+ "regex": "^(canceled|created|failed|manual|pending|preparing|running|scheduled|skipped|success|success_warning|waiting-for-resource)$"
+ },
+ "$.body.pipelines[*].details.status.tooltip": {
+ "match": "regex",
+ "regex": "^(canceled|created|delayed|failed|manual action|passed|pending|preparing|running|skipped|passed with warnings|waiting for resource)$"
+ },
+ "$.body.pipelines[*].details.status.has_details": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].details.status.details_path": {
+ "match": "regex",
+ "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
+ },
+ "$.body.pipelines[*].details.status.favicon": {
+ "match": "regex",
+ "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
+ },
+ "$.body.pipelines[*].details.stages": {
+ "min": 1
+ },
+ "$.body.pipelines[*].details.stages[*].*": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].details.stages[*].name": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].details.stages[*].title": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].details.stages[*].status.icon": {
+ "match": "regex",
+ "regex": "^status_(canceled|created|failed|manual|pending|preparing|running|scheduled|skipped|success|warning)$"
+ },
+ "$.body.pipelines[*].details.stages[*].status.text": {
+ "match": "regex",
+ "regex": "^(canceled|created|delayed|failed|manual|passed|pending|preparing|running|skipped|waiting)$"
+ },
+ "$.body.pipelines[*].details.stages[*].status.label": {
+ "match": "regex",
+ "regex": "^(canceled|created|delayed|failed|manual action|passed|pending|preparing|running|skipped|passed with warnings|waiting for resource)$"
+ },
+ "$.body.pipelines[*].details.stages[*].status.group": {
+ "match": "regex",
+ "regex": "^(canceled|created|failed|manual|pending|preparing|running|scheduled|skipped|success|success_warning|waiting-for-resource)$"
+ },
+ "$.body.pipelines[*].details.stages[*].status.tooltip": {
+ "match": "regex",
+ "regex": "^(canceled|created|delayed|failed|manual action|passed|pending|preparing|running|skipped|passed with warnings|waiting for resource)$"
+ },
+ "$.body.pipelines[*].details.stages[*].status.has_details": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].details.stages[*].status.details_path": {
+ "match": "regex",
+ "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
+ },
+ "$.body.pipelines[*].details.stages[*].status.favicon": {
+ "match": "regex",
+ "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
+ },
+ "$.body.pipelines[*].details.stages[*].path": {
+ "match": "regex",
+ "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
+ },
+ "$.body.pipelines[*].details.stages[*].dropdown_path": {
+ "match": "regex",
+ "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
+ },
+ "$.body.pipelines[*].details.duration": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].details.finished_at": {
+ "match": "regex",
+ "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
+ },
+ "$.body.pipelines[*].details.name": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].details.manual_actions": {
+ "min": 1
+ },
+ "$.body.pipelines[*].details.manual_actions[*].*": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].details.manual_actions[*].name": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].details.manual_actions[*].playable": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].details.manual_actions[*].scheduled": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].details.scheduled_actions": {
+ "min": 1
+ },
+ "$.body.pipelines[*].details.scheduled_actions[*].*": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].details.scheduled_actions[*].name": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].details.scheduled_actions[*].playable": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].details.scheduled_actions[*].scheduled": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].ref.name": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].ref.path": {
+ "match": "regex",
+ "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
+ },
+ "$.body.pipelines[*].ref.tag": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].ref.branch": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].ref.merge_request": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].commit.id": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].commit.short_id": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].commit.created_at": {
+ "match": "regex",
+ "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
+ },
+ "$.body.pipelines[*].commit.parent_ids": {
+ "min": 1
+ },
+ "$.body.pipelines[*].commit.parent_ids[*].*": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].commit.parent_ids[*]": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].commit.title": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].commit.message": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].commit.author_name": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].commit.author_email": {
+ "match": "regex",
+ "regex": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$"
+ },
+ "$.body.pipelines[*].commit.authored_date": {
+ "match": "regex",
+ "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
+ },
+ "$.body.pipelines[*].commit.committer_name": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].commit.committer_email": {
+ "match": "regex",
+ "regex": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$"
+ },
+ "$.body.pipelines[*].commit.committed_date": {
+ "match": "regex",
+ "regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
+ },
+ "$.body.pipelines[*].commit.web_url": {
+ "match": "regex",
+ "regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
+ },
+ "$.body.pipelines[*].commit.author_gravatar_url": {
+ "match": "regex",
+ "regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
+ },
+ "$.body.pipelines[*].commit.commit_url": {
+ "match": "regex",
+ "regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
+ },
+ "$.body.pipelines[*].commit.commit_path": {
+ "match": "regex",
+ "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
+ },
+ "$.body.pipelines[*].project.id": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].project.name": {
+ "match": "type"
+ },
+ "$.body.pipelines[*].project.full_path": {
+ "match": "regex",
+ "regex": "^\\/[a-zA-Z0-9#-=?_]+$"
+ },
+ "$.body.pipelines[*].project.full_name": {
+ "match": "type"
+ },
+ "$.body.count.all": {
+ "match": "type"
+ }
+ }
+ }
+ }
+ ],
+ "metadata": {
+ "pactSpecification": {
+ "version": "2.0.0"
+ }
+ }
+} \ No newline at end of file
diff --git a/spec/contracts/contracts/project/pipelines/new/pipelines#new-post_create_a_new_pipeline.json b/spec/contracts/contracts/project/pipelines/new/pipelines#new-post_create_a_new_pipeline.json
new file mode 100644
index 00000000000..4be6b1e3cab
--- /dev/null
+++ b/spec/contracts/contracts/project/pipelines/new/pipelines#new-post_create_a_new_pipeline.json
@@ -0,0 +1,43 @@
+{
+ "consumer": {
+ "name": "Pipelines#new"
+ },
+ "provider": {
+ "name": "POST create a new pipeline"
+ },
+ "interactions": [
+ {
+ "description": "a request to create a new pipeline",
+ "providerState": "a project with a valid .gitlab-ci.yml configuration exists",
+ "request": {
+ "method": "POST",
+ "path": "/gitlab-org/gitlab-qa/-/pipelines",
+ "headers": {
+ "Accept": "*/*",
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "ref": "master"
+ }
+ },
+ "response": {
+ "status": 302,
+ "headers": {
+ "Content-Type": "text/html; charset=utf-8"
+ },
+ "body": "<html><body>You are being <a href=\"http://example.org/gitlab-org/gitlab-qa/-/pipelines/5\">redirected</a>.</body></html>",
+ "matchingRules": {
+ "$.body": {
+ "match": "regex",
+ "regex": "You are being <a href=\\\"(.)+\\\">redirected<\\/a>."
+ }
+ }
+ }
+ }
+ ],
+ "metadata": {
+ "pactSpecification": {
+ "version": "2.0.0"
+ }
+ }
+} \ No newline at end of file
diff --git a/spec/contracts/contracts/project/pipeline/show/pipelines#show-delete_pipeline.json b/spec/contracts/contracts/project/pipelines/show/pipelines#show-delete_pipeline.json
index 795c8a6e197..795c8a6e197 100644
--- a/spec/contracts/contracts/project/pipeline/show/pipelines#show-delete_pipeline.json
+++ b/spec/contracts/contracts/project/pipelines/show/pipelines#show-delete_pipeline.json
diff --git a/spec/contracts/contracts/project/pipeline/show/pipelines#show-get_pipeline_header_data.json b/spec/contracts/contracts/project/pipelines/show/pipelines#show-get_pipeline_header_data.json
index 2d775dc0f61..2d775dc0f61 100644
--- a/spec/contracts/contracts/project/pipeline/show/pipelines#show-get_pipeline_header_data.json
+++ b/spec/contracts/contracts/project/pipelines/show/pipelines#show-get_pipeline_header_data.json
diff --git a/spec/contracts/provider/helpers/contract_source_helper.rb b/spec/contracts/provider/helpers/contract_source_helper.rb
new file mode 100644
index 00000000000..5fc2e3ffc0d
--- /dev/null
+++ b/spec/contracts/provider/helpers/contract_source_helper.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module Provider
+ module ContractSourceHelper
+ QA_PACT_BROKER_HOST = "http://localhost:9292/pacts"
+ PREFIX_PATHS = {
+ rake: "../../../contracts/contracts/project",
+ spec: "../contracts/project"
+ }.freeze
+ SUB_PATH_REGEX = %r{project/(?<file_path>.*?)_helper.rb}.freeze
+
+ class << self
+ def contract_location(requester, file_path)
+ raise ArgumentError, 'requester must be :rake or :spec' unless [:rake, :spec].include? requester
+
+ relevant_path = file_path.match(SUB_PATH_REGEX)[:file_path].split('/')
+
+ ENV["PACT_BROKER"] ? pact_broker_url(relevant_path) : local_contract_location(requester, relevant_path)
+ end
+
+ def pact_broker_url(file_path)
+ provider_url = "provider/#{construct_provider_url_path(file_path)}"
+ consumer_url = "consumer/#{construct_consumer_url_path(file_path)}"
+
+ "#{QA_PACT_BROKER_HOST}/#{provider_url}/#{consumer_url}/latest"
+ end
+
+ def construct_provider_url_path(file_path)
+ split_name = file_path[2].split('_')
+
+ split_name[0] = split_name[0].upcase
+ split_name.join("%20")
+ end
+
+ def construct_consumer_url_path(file_path)
+ "#{file_path[0].split('_').map(&:capitalize).join}%23#{file_path[1]}"
+ end
+
+ def local_contract_location(requester, file_path)
+ contract_path = construct_local_contract_path(file_path)
+ prefix_path = requester == :rake ? File.expand_path(PREFIX_PATHS[requester], __dir__) : PREFIX_PATHS[requester]
+
+ "#{prefix_path}#{contract_path}"
+ end
+
+ def construct_local_contract_path(file_path)
+ contract_file_name = "#{file_path[0].tr('_', '')}##{file_path[1]}-#{file_path[2]}.json"
+
+ "/#{file_path[0]}/#{file_path[1]}/#{contract_file_name}"
+ end
+ end
+ end
+end
diff --git a/spec/contracts/provider/helpers/publish_contract_helper.rb b/spec/contracts/provider/helpers/publish_contract_helper.rb
index 102a73d87ee..ddf1e1fd0ed 100644
--- a/spec/contracts/provider/helpers/publish_contract_helper.rb
+++ b/spec/contracts/provider/helpers/publish_contract_helper.rb
@@ -2,15 +2,15 @@
module Provider
module PublishContractHelper
- PROVIDER_VERSION = ENV['GIT_COMMIT'] || `git rev-parse --verify HEAD`.strip
- PROVIDER_BRANCH = ENV['GIT_BRANCH'] || `git name-rev --name-only HEAD`.strip
+ PROVIDER_VERSION = ENV["GIT_COMMIT"] || `git rev-parse --verify HEAD`.strip
+ PROVIDER_BRANCH = ENV["GIT_BRANCH"] || `git name-rev --name-only HEAD`.strip
PUBLISH_FLAG = true
def self.publish_contract_setup
- @setup ||= -> {
- app_version PROVIDER_VERSION
- app_version_branch PROVIDER_BRANCH
- publish_verification_results PUBLISH_FLAG
+ ->(app_version, app_version_branch, publish_verification_results) {
+ app_version.call(PROVIDER_VERSION)
+ app_version_branch.call(PROVIDER_BRANCH)
+ publish_verification_results.call(PUBLISH_FLAG)
}
end
end
diff --git a/spec/contracts/provider/pact_helpers/project/merge_request/show/diffs_batch_helper.rb b/spec/contracts/provider/pact_helpers/project/merge_request/show/diffs_batch_helper.rb
deleted file mode 100644
index 71f302f2c44..00000000000
--- a/spec/contracts/provider/pact_helpers/project/merge_request/show/diffs_batch_helper.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../../../../spec_helper'
-require_relative '../../../../states/project/merge_request/show_state'
-
-module Provider
- module DiffsBatchHelper
- Pact.service_provider "Merge Request Diffs Batch Endpoint" do
- app { Environments::Test.app }
-
- honours_pact_with 'MergeRequest#show' do
- pact_uri '../contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_batch_endpoint.json'
- end
-
- Provider::PublishContractHelper.publish_contract_setup.call
- end
- end
-end
diff --git a/spec/contracts/provider/pact_helpers/project/merge_request/show/diffs_metadata_helper.rb b/spec/contracts/provider/pact_helpers/project/merge_request/show/diffs_metadata_helper.rb
deleted file mode 100644
index 60a3abea5ae..00000000000
--- a/spec/contracts/provider/pact_helpers/project/merge_request/show/diffs_metadata_helper.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../../../../spec_helper'
-require_relative '../../../../states/project/merge_request/show_state'
-
-module Provider
- module DiffsMetadataHelper
- Pact.service_provider "Merge Request Diffs Metadata Endpoint" do
- app { Environments::Test.app }
-
- honours_pact_with 'MergeRequest#show' do
- pact_uri '../contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_metadata_endpoint.json'
- end
-
- app_version Provider::PublishContractHelper::PROVIDER_VERSION
- app_version_branch Provider::PublishContractHelper::PROVIDER_BRANCH
- publish_verification_results Provider::PublishContractHelper::PUBLISH_FLAG
- end
- end
-end
diff --git a/spec/contracts/provider/pact_helpers/project/merge_request/show/discussions_helper.rb b/spec/contracts/provider/pact_helpers/project/merge_request/show/discussions_helper.rb
deleted file mode 100644
index b9308af0a1a..00000000000
--- a/spec/contracts/provider/pact_helpers/project/merge_request/show/discussions_helper.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../../../../spec_helper'
-require_relative '../../../../states/project/merge_request/show_state'
-
-module Provider
- module DiscussionsHelper
- Pact.service_provider "Merge Request Discussions Endpoint" do
- app { Environments::Test.app }
-
- honours_pact_with 'MergeRequest#show' do
- pact_uri '../contracts/project/merge_request/show/mergerequest#show-merge_request_discussions_endpoint.json'
- end
-
- app_version Provider::PublishContractHelper::PROVIDER_VERSION
- app_version_branch Provider::PublishContractHelper::PROVIDER_BRANCH
- publish_verification_results Provider::PublishContractHelper::PUBLISH_FLAG
- end
- end
-end
diff --git a/spec/contracts/provider/pact_helpers/project/merge_requests/show/get_diffs_batch_helper.rb b/spec/contracts/provider/pact_helpers/project/merge_requests/show/get_diffs_batch_helper.rb
new file mode 100644
index 00000000000..aa97a07c07b
--- /dev/null
+++ b/spec/contracts/provider/pact_helpers/project/merge_requests/show/get_diffs_batch_helper.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require_relative "../../../../spec_helper"
+require_relative "../../../../helpers/contract_source_helper"
+require_relative "../../../../helpers/publish_contract_helper"
+require_relative "../../../../states/project/merge_requests/show_state"
+
+module Provider
+ module DiffsBatchHelper
+ Pact.service_provider "GET diffs batch" do
+ app { Environments::Test.app }
+
+ honours_pact_with "MergeRequests#show" do
+ pact_uri Provider::ContractSourceHelper.contract_location(:spec, __FILE__)
+ end
+
+ Provider::PublishContractHelper.publish_contract_setup.call(
+ method(:app_version),
+ method(:app_version_branch),
+ method(:publish_verification_results)
+ )
+ end
+ end
+end
diff --git a/spec/contracts/provider/pact_helpers/project/merge_requests/show/get_diffs_metadata_helper.rb b/spec/contracts/provider/pact_helpers/project/merge_requests/show/get_diffs_metadata_helper.rb
new file mode 100644
index 00000000000..891585b0066
--- /dev/null
+++ b/spec/contracts/provider/pact_helpers/project/merge_requests/show/get_diffs_metadata_helper.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require_relative "../../../../spec_helper"
+require_relative "../../../../helpers/contract_source_helper"
+require_relative "../../../../helpers/publish_contract_helper"
+require_relative "../../../../states/project/merge_requests/show_state"
+
+module Provider
+ module DiffsMetadataHelper
+ Pact.service_provider "GET diffs metadata" do
+ app { Environments::Test.app }
+
+ honours_pact_with "MergeRequests#show" do
+ pact_uri Provider::ContractSourceHelper.contract_location(:spec, __FILE__)
+ end
+
+ Provider::PublishContractHelper.publish_contract_setup.call(
+ method(:app_version),
+ method(:app_version_branch),
+ method(:publish_verification_results)
+ )
+ end
+ end
+end
diff --git a/spec/contracts/provider/pact_helpers/project/merge_requests/show/get_discussions_helper.rb b/spec/contracts/provider/pact_helpers/project/merge_requests/show/get_discussions_helper.rb
new file mode 100644
index 00000000000..229818366ca
--- /dev/null
+++ b/spec/contracts/provider/pact_helpers/project/merge_requests/show/get_discussions_helper.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require_relative "../../../../spec_helper"
+require_relative "../../../../helpers/contract_source_helper"
+require_relative "../../../../helpers/publish_contract_helper"
+require_relative "../../../../states/project/merge_requests/show_state"
+
+module Provider
+ module DiscussionsHelper
+ Pact.service_provider "GET discussions" do
+ app { Environments::Test.app }
+
+ honours_pact_with "MergeRequests#show" do
+ pact_uri Provider::ContractSourceHelper.contract_location(:spec, __FILE__)
+ end
+
+ Provider::PublishContractHelper.publish_contract_setup.call(
+ method(:app_version),
+ method(:app_version_branch),
+ method(:publish_verification_results)
+ )
+ end
+ end
+end
diff --git a/spec/contracts/provider/pact_helpers/project/pipeline/index/create_a_new_pipeline_helper.rb b/spec/contracts/provider/pact_helpers/project/pipeline/index/create_a_new_pipeline_helper.rb
deleted file mode 100644
index 2af960bc9fd..00000000000
--- a/spec/contracts/provider/pact_helpers/project/pipeline/index/create_a_new_pipeline_helper.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../../../../spec_helper'
-require_relative '../../../../states/project/pipeline/new_state'
-
-module Provider
- module CreateNewPipelineHelper
- Pact.service_provider "POST Create a new pipeline" do
- app { Environments::Test.app }
-
- honours_pact_with 'Pipelines#new' do
- pact_uri '../contracts/project/pipeline/new/pipelines#new-post_create_a_new_pipeline.json'
- end
-
- app_version Provider::PublishContractHelper::PROVIDER_VERSION
- app_version_branch Provider::PublishContractHelper::PROVIDER_BRANCH
- publish_verification_results Provider::PublishContractHelper::PUBLISH_FLAG
- end
- end
-end
diff --git a/spec/contracts/provider/pact_helpers/project/pipeline/index/get_list_project_pipelines_helper.rb b/spec/contracts/provider/pact_helpers/project/pipeline/index/get_list_project_pipelines_helper.rb
deleted file mode 100644
index 37cddd1b80e..00000000000
--- a/spec/contracts/provider/pact_helpers/project/pipeline/index/get_list_project_pipelines_helper.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../../../../spec_helper'
-require_relative '../../../../states/project/pipeline/index_state'
-
-module Provider
- module GetListProjectPipelinesHelper
- Pact.service_provider "GET List project pipelines" do
- app { Environments::Test.app }
-
- honours_pact_with 'Pipelines#index' do
- pact_uri '../contracts/project/project/pipeline/index/pipelines#index-get_list_project_pipelines.json'
- end
-
- app_version Provider::PublishContractHelper::PROVIDER_VERSION
- app_version_branch Provider::PublishContractHelper::PROVIDER_BRANCH
- publish_verification_results Provider::PublishContractHelper::PUBLISH_FLAG
- end
- end
-end
diff --git a/spec/contracts/provider/pact_helpers/project/pipeline/show/delete_pipeline_helper.rb b/spec/contracts/provider/pact_helpers/project/pipeline/show/delete_pipeline_helper.rb
deleted file mode 100644
index 0455281fcd7..00000000000
--- a/spec/contracts/provider/pact_helpers/project/pipeline/show/delete_pipeline_helper.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../../../../spec_helper'
-require_relative '../../../../helpers/publish_contract_helper'
-require_relative '../../../../states/project/pipeline/show_state'
-
-module Provider
- module DeletePipelineHelper
- Pact.service_provider "DELETE pipeline" do
- app { Environments::Test.app }
-
- honours_pact_with 'Pipelines#show' do
- pact_uri '../contracts/project/pipeline/show/pipelines#show-delete_pipeline.json'
- end
-
- app_version Provider::PublishContractHelper::PROVIDER_VERSION
- app_version_branch Provider::PublishContractHelper::PROVIDER_BRANCH
- publish_verification_results Provider::PublishContractHelper::PUBLISH_FLAG
- end
- end
-end
diff --git a/spec/contracts/provider/pact_helpers/project/pipeline/show/get_pipeline_header_data_helper.rb b/spec/contracts/provider/pact_helpers/project/pipeline/show/get_pipeline_header_data_helper.rb
deleted file mode 100644
index bce1c4ab3f4..00000000000
--- a/spec/contracts/provider/pact_helpers/project/pipeline/show/get_pipeline_header_data_helper.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../../../../spec_helper'
-require_relative '../../../../helpers/publish_contract_helper'
-require_relative '../../../../states/project/pipeline/show_state'
-
-module Provider
- module GetPipelinesHeaderDataHelper
- Pact.service_provider "GET pipeline header data" do
- app { Environments::Test.app }
-
- honours_pact_with 'Pipelines#show' do
- pact_uri '../contracts/project/pipeline/show/pipelines#show-get_project_pipeline_header_data.json'
- # pact_uri 'http://localhost:9292/pacts/provider/GET%20pipeline%20header%20data/consumer/Pipelines%23show/latest'
- end
-
- app_version Provider::PublishContractHelper::PROVIDER_VERSION
- app_version_branch Provider::PublishContractHelper::PROVIDER_BRANCH
- publish_verification_results Provider::PublishContractHelper::PUBLISH_FLAG
- end
- end
-end
diff --git a/spec/contracts/provider/pact_helpers/project/pipeline_schedule/update_pipeline_schedule_helper.rb b/spec/contracts/provider/pact_helpers/project/pipeline_schedule/update_pipeline_schedule_helper.rb
deleted file mode 100644
index d95a09abd8d..00000000000
--- a/spec/contracts/provider/pact_helpers/project/pipeline_schedule/update_pipeline_schedule_helper.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../../../spec_helper'
-require_relative '../../../states/project/pipeline_schedule/edit_state'
-
-module Provider
- module CreateNewPipelineHelper
- Pact.service_provider "PUT Edit a pipeline schedule" do
- app { Environments::Test.app }
-
- honours_pact_with 'PipelineSchedule#edit' do
- pact_uri '../contracts/project/pipeline_schedule/edit/pipelineschedules#edit-put_edit_a_pipeline_schedule.json'
- end
-
- app_version Provider::PublishContractHelper::PROVIDER_VERSION
- app_version_branch Provider::PublishContractHelper::PROVIDER_BRANCH
- publish_verification_results Provider::PublishContractHelper::PUBLISH_FLAG
- end
- end
-end
diff --git a/spec/contracts/provider/pact_helpers/project/pipeline_schedules/edit/put_edit_a_pipeline_schedule_helper.rb b/spec/contracts/provider/pact_helpers/project/pipeline_schedules/edit/put_edit_a_pipeline_schedule_helper.rb
new file mode 100644
index 00000000000..62702fd5f92
--- /dev/null
+++ b/spec/contracts/provider/pact_helpers/project/pipeline_schedules/edit/put_edit_a_pipeline_schedule_helper.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require_relative "../../../../spec_helper"
+require_relative "../../../../helpers/contract_source_helper"
+require_relative "../../../../helpers/publish_contract_helper"
+require_relative "../../../../states/project/pipeline_schedules/edit_state"
+
+module Provider
+ module CreateNewPipelineHelper
+ Pact.service_provider "PUT edit a pipeline schedule" do
+ app { Environments::Test.app }
+
+ honours_pact_with "PipelineSchedules#edit" do
+ pact_uri Provider::ContractSourceHelper.contract_location(:spec, __FILE__)
+ end
+
+ Provider::PublishContractHelper.publish_contract_setup.call(
+ method(:app_version),
+ method(:app_version_branch),
+ method(:publish_verification_results)
+ )
+ end
+ end
+end
diff --git a/spec/contracts/provider/pact_helpers/project/pipelines/index/get_list_project_pipelines_helper.rb b/spec/contracts/provider/pact_helpers/project/pipelines/index/get_list_project_pipelines_helper.rb
new file mode 100644
index 00000000000..03708db2eb2
--- /dev/null
+++ b/spec/contracts/provider/pact_helpers/project/pipelines/index/get_list_project_pipelines_helper.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require_relative "../../../../spec_helper"
+require_relative "../../../../helpers/contract_source_helper"
+require_relative "../../../../helpers/publish_contract_helper"
+require_relative "../../../../states/project/pipelines/index_state"
+
+module Provider
+ module GetListProjectPipelinesHelper
+ Pact.service_provider "GET list project pipelines" do
+ app { Environments::Test.app }
+
+ honours_pact_with "Pipelines#index" do
+ pact_uri Provider::ContractSourceHelper.contract_location(:spec, __FILE__)
+ end
+
+ Provider::PublishContractHelper.publish_contract_setup.call(
+ method(:app_version),
+ method(:app_version_branch),
+ method(:publish_verification_results)
+ )
+ end
+ end
+end
diff --git a/spec/contracts/provider/pact_helpers/project/pipelines/new/post_create_a_new_pipeline_helper.rb b/spec/contracts/provider/pact_helpers/project/pipelines/new/post_create_a_new_pipeline_helper.rb
new file mode 100644
index 00000000000..53e5ab61a20
--- /dev/null
+++ b/spec/contracts/provider/pact_helpers/project/pipelines/new/post_create_a_new_pipeline_helper.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require_relative "../../../../spec_helper"
+require_relative "../../../../helpers/contract_source_helper"
+require_relative "../../../../helpers/publish_contract_helper"
+require_relative "../../../../states/project/pipelines/new_state"
+
+module Provider
+ module CreateNewPipelineHelper
+ Pact.service_provider "POST create a new pipeline" do
+ app { Environments::Test.app }
+
+ honours_pact_with "Pipelines#new" do
+ pact_uri Provider::ContractSourceHelper.contract_location(:spec, __FILE__)
+ end
+
+ Provider::PublishContractHelper.publish_contract_setup.call(
+ method(:app_version),
+ method(:app_version_branch),
+ method(:publish_verification_results)
+ )
+ end
+ end
+end
diff --git a/spec/contracts/provider/pact_helpers/project/pipelines/show/delete_pipeline_helper.rb b/spec/contracts/provider/pact_helpers/project/pipelines/show/delete_pipeline_helper.rb
new file mode 100644
index 00000000000..1801e989c99
--- /dev/null
+++ b/spec/contracts/provider/pact_helpers/project/pipelines/show/delete_pipeline_helper.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require_relative "../../../../spec_helper"
+require_relative "../../../../helpers/contract_source_helper"
+require_relative "../../../../helpers/publish_contract_helper"
+require_relative "../../../../states/project/pipelines/show_state"
+
+module Provider
+ module DeletePipelineHelper
+ Pact.service_provider "DELETE pipeline" do
+ app { Environments::Test.app }
+
+ honours_pact_with "Pipelines#show" do
+ pact_uri Provider::ContractSourceHelper.contract_location(:spec, __FILE__)
+ end
+
+ Provider::PublishContractHelper.publish_contract_setup.call(
+ method(:app_version),
+ method(:app_version_branch),
+ method(:publish_verification_results)
+ )
+ end
+ end
+end
diff --git a/spec/contracts/provider/pact_helpers/project/pipelines/show/get_pipeline_header_data_helper.rb b/spec/contracts/provider/pact_helpers/project/pipelines/show/get_pipeline_header_data_helper.rb
new file mode 100644
index 00000000000..1f3ba9dd007
--- /dev/null
+++ b/spec/contracts/provider/pact_helpers/project/pipelines/show/get_pipeline_header_data_helper.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require_relative "../../../../spec_helper"
+require_relative "../../../../helpers/contract_source_helper"
+require_relative "../../../../helpers/publish_contract_helper"
+require_relative "../../../../states/project/pipelines/show_state"
+
+module Provider
+ module GetPipelinesHeaderDataHelper
+ Pact.service_provider "GET pipeline header data" do
+ app { Environments::Test.app }
+
+ honours_pact_with "Pipelines#show" do
+ pact_uri Provider::ContractSourceHelper.contract_location(:spec, __FILE__)
+ end
+
+ Provider::PublishContractHelper.publish_contract_setup.call(
+ method(:app_version),
+ method(:app_version_branch),
+ method(:publish_verification_results)
+ )
+ end
+ end
+end
diff --git a/spec/contracts/provider/spec_helper.rb b/spec/contracts/provider/spec_helper.rb
index 6009d6524e1..44e4d29c18e 100644
--- a/spec/contracts/provider/spec_helper.rb
+++ b/spec/contracts/provider/spec_helper.rb
@@ -3,6 +3,13 @@
require 'spec_helper'
require 'zeitwerk'
require_relative 'helpers/users_helper'
+require_relative('../../../ee/spec/contracts/provider/spec_helper') if Gitlab.ee?
+require Rails.root.join("spec/support/helpers/rails_helpers.rb")
+require Rails.root.join("spec/support/helpers/stub_env.rb")
+
+# Opt out of telemetry collection. We can't allow all engineers, and users who install GitLab from source, to be
+# automatically enrolled in sending data on their usage without their knowledge.
+ENV['PACT_DO_NOT_TRACK'] = 'true'
RSpec.configure do |config|
config.include Devise::Test::IntegrationHelpers
@@ -19,6 +26,8 @@ end
Pact.configure do |config|
config.include FactoryBot::Syntax::Methods
+ config.include RailsHelpers
+ config.include StubENV
end
module SpecHelper
diff --git a/spec/contracts/provider/states/project/merge_request/show_state.rb b/spec/contracts/provider/states/project/merge_request/show_state.rb
deleted file mode 100644
index 46f322f723a..00000000000
--- a/spec/contracts/provider/states/project/merge_request/show_state.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-
-Pact.provider_states_for "MergeRequest#show" do
- provider_state "a merge request with diffs exists" do
- set_up do
- user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
- namespace = create(:namespace, name: 'gitlab-org')
- project = create(:project, :custom_repo, name: 'gitlab-qa', namespace: namespace, files: {})
-
- project.add_maintainer(user)
-
- merge_request = create(:merge_request_with_multiple_diffs, source_project: project)
- merge_request_diff = create(:merge_request_diff, merge_request: merge_request)
-
- create(:merge_request_diff_file, :new_file, merge_request_diff: merge_request_diff)
- end
- end
-
- provider_state "a merge request exists" do
- set_up do
- user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
- namespace = create(:namespace, name: 'gitlab-org')
- project = create(:project, :custom_repo, name: 'gitlab-qa', namespace: namespace, files: {})
-
- project.add_maintainer(user)
-
- merge_request = create(:merge_request, source_project: project)
- merge_request_diff = create(:merge_request_diff, merge_request: merge_request)
-
- create(:merge_request_diff_file, :new_file, merge_request_diff: merge_request_diff)
- end
- end
-
- provider_state "a merge request with discussions exists" do
- set_up do
- user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
- namespace = create(:namespace, name: 'gitlab-org')
- project = create(:project, name: 'gitlab-qa', namespace: namespace)
-
- project.add_maintainer(user)
-
- merge_request = create(:merge_request_with_diffs, source_project: project, author: user)
-
- create(:discussion_note_on_merge_request, noteable: merge_request, project: project, author: user)
- end
- end
-end
diff --git a/spec/contracts/provider/states/project/merge_requests/show_state.rb b/spec/contracts/provider/states/project/merge_requests/show_state.rb
new file mode 100644
index 00000000000..ecb6784db1e
--- /dev/null
+++ b/spec/contracts/provider/states/project/merge_requests/show_state.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+Pact.provider_states_for "MergeRequests#show" do
+ provider_state "a merge request with diffs exists" do
+ set_up do
+ user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
+ namespace = create(:namespace, name: 'gitlab-org')
+ project = create(:project, :custom_repo, name: 'gitlab-qa', namespace: namespace, files: {})
+
+ project.add_maintainer(user)
+
+ merge_request = create(:merge_request_with_multiple_diffs, source_project: project)
+ merge_request_diff = create(:merge_request_diff, merge_request: merge_request)
+
+ create(:merge_request_diff_file, :new_file, merge_request_diff: merge_request_diff)
+ end
+ end
+
+ provider_state "a merge request exists" do
+ set_up do
+ user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
+ namespace = create(:namespace, name: 'gitlab-org')
+ project = create(:project, :custom_repo, name: 'gitlab-qa', namespace: namespace, files: {})
+
+ project.add_maintainer(user)
+
+ merge_request = create(:merge_request, source_project: project)
+ merge_request_diff = create(:merge_request_diff, merge_request: merge_request)
+
+ create(:merge_request_diff_file, :new_file, merge_request_diff: merge_request_diff)
+ end
+ end
+
+ provider_state "a merge request with discussions exists" do
+ set_up do
+ user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
+ namespace = create(:namespace, name: 'gitlab-org')
+ project = create(:project, name: 'gitlab-qa', namespace: namespace)
+
+ project.add_maintainer(user)
+
+ merge_request = create(:merge_request_with_diffs, source_project: project, author: user)
+
+ create(:discussion_note_on_merge_request, noteable: merge_request, project: project, author: user)
+ end
+ end
+end
diff --git a/spec/contracts/provider/states/project/pipeline_schedule/edit_state.rb b/spec/contracts/provider/states/project/pipeline_schedules/edit_state.rb
index 4ee714f15f3..4ee714f15f3 100644
--- a/spec/contracts/provider/states/project/pipeline_schedule/edit_state.rb
+++ b/spec/contracts/provider/states/project/pipeline_schedules/edit_state.rb
diff --git a/spec/contracts/provider/states/project/pipeline/index_state.rb b/spec/contracts/provider/states/project/pipelines/index_state.rb
index 639c25e9894..639c25e9894 100644
--- a/spec/contracts/provider/states/project/pipeline/index_state.rb
+++ b/spec/contracts/provider/states/project/pipelines/index_state.rb
diff --git a/spec/contracts/provider/states/project/pipeline/new_state.rb b/spec/contracts/provider/states/project/pipelines/new_state.rb
index 95914180bec..95914180bec 100644
--- a/spec/contracts/provider/states/project/pipeline/new_state.rb
+++ b/spec/contracts/provider/states/project/pipelines/new_state.rb
diff --git a/spec/contracts/provider/states/project/pipeline/show_state.rb b/spec/contracts/provider/states/project/pipelines/show_state.rb
index 3365647cd13..3365647cd13 100644
--- a/spec/contracts/provider/states/project/pipeline/show_state.rb
+++ b/spec/contracts/provider/states/project/pipelines/show_state.rb
diff --git a/spec/contracts/provider_specs/helpers/provider/contract_source_helper_spec.rb b/spec/contracts/provider_specs/helpers/provider/contract_source_helper_spec.rb
new file mode 100644
index 00000000000..8bb3b577135
--- /dev/null
+++ b/spec/contracts/provider_specs/helpers/provider/contract_source_helper_spec.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_relative '../../../provider/helpers/contract_source_helper'
+
+RSpec.describe Provider::ContractSourceHelper, feature_category: :not_owned do
+ let(:pact_helper_path) { 'pact_helpers/project/pipelines/new/post_create_pipeline_helper.rb' }
+ let(:split_pact_helper_path) { %w[pipelines new post_create_pipeline] }
+ let(:provider_url_path) { 'POST%20create%20pipeline' }
+ let(:consumer_url_path) { 'Pipelines%23new' }
+
+ describe '#contract_location' do
+ it 'raises an error when an invalid requester is given' do
+ expect { subject.contract_location(:foo, pact_helper_path) }
+ .to raise_error(ArgumentError, 'requester must be :rake or :spec')
+ end
+
+ context 'when the PACT_BROKER environment variable is not set' do
+ it 'extracts the relevant path from the pact_helper path' do
+ expect(subject).to receive(:local_contract_location).with(:rake, split_pact_helper_path)
+
+ subject.contract_location(:rake, pact_helper_path)
+ end
+
+ it 'does not construct the pact broker url' do
+ expect(subject).not_to receive(:pact_broker_url)
+
+ subject.contract_location(:rake, pact_helper_path)
+ end
+ end
+
+ context 'when the PACT_BROKER environment variable is set' do
+ before do
+ stub_env('PACT_BROKER', true)
+ end
+
+ it 'extracts the relevant path from the pact_helper path' do
+ expect(subject).to receive(:pact_broker_url).with(split_pact_helper_path)
+
+ subject.contract_location(:spec, pact_helper_path)
+ end
+
+ it 'does not construct the pact broker url' do
+ expect(subject).not_to receive(:local_contract_location)
+
+ subject.contract_location(:spec, pact_helper_path)
+ end
+ end
+ end
+
+ describe '#pact_broker_url' do
+ it 'returns the full url to the contract that the provider test is verifying' do
+ contract_url_path = "http://localhost:9292/pacts/provider/" \
+ "#{provider_url_path}/consumer/#{consumer_url_path}/latest"
+
+ expect(subject.pact_broker_url(split_pact_helper_path)).to eq(contract_url_path)
+ end
+ end
+
+ describe '#construct_provider_url_path' do
+ it 'returns the provider url path' do
+ expect(subject.construct_provider_url_path(split_pact_helper_path)).to eq(provider_url_path)
+ end
+ end
+
+ describe '#construct_consumer_url_path' do
+ it 'returns the consumer url path' do
+ expect(subject.construct_consumer_url_path(split_pact_helper_path)).to eq(consumer_url_path)
+ end
+ end
+
+ describe '#local_contract_location' do
+ it 'returns the contract file path with the prefix path for a rake task' do
+ rake_task_relative_path = '/spec/contracts/contracts/project'
+
+ rake_task_path = subject.local_contract_location(:rake, split_pact_helper_path)
+
+ expect(rake_task_path).to include(rake_task_relative_path)
+ expect(rake_task_path).not_to include('../')
+ end
+
+ it 'returns the contract file path with the prefix path for a spec' do
+ spec_relative_path = '../contracts/project'
+
+ expect(subject.local_contract_location(:spec, split_pact_helper_path)).to include(spec_relative_path)
+ end
+ end
+
+ describe '#construct_local_contract_path' do
+ it 'returns the local contract path' do
+ contract_path = '/pipelines/new/pipelines#new-post_create_pipeline.json'
+
+ expect(subject.construct_local_contract_path(split_pact_helper_path)).to eq(contract_path)
+ end
+ end
+end
diff --git a/spec/controllers/admin/application_settings/appearances_controller_spec.rb b/spec/controllers/admin/application_settings/appearances_controller_spec.rb
index cc914f3c9b8..5978381a926 100644
--- a/spec/controllers/admin/application_settings/appearances_controller_spec.rb
+++ b/spec/controllers/admin/application_settings/appearances_controller_spec.rb
@@ -11,6 +11,7 @@ RSpec.describe Admin::ApplicationSettings::AppearancesController do
let(:create_params) do
{
title: 'Foo',
+ short_title: 'F',
description: 'Bar',
header_message: header_message,
footer_message: footer_message
diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb
index 0ad0a111156..49c40ecee8b 100644
--- a/spec/controllers/admin/application_settings_controller_spec.rb
+++ b/spec/controllers/admin/application_settings_controller_spec.rb
@@ -162,6 +162,13 @@ RSpec.describe Admin::ApplicationSettingsController, :do_not_mock_admin_mode_set
expect(ApplicationSetting.current.receive_max_input_size).to eq(1024)
end
+ it 'updates the default_preferred_language for string value' do
+ put :update, params: { application_setting: { default_preferred_language: 'zh_CN' } }
+
+ expect(response).to redirect_to(general_admin_application_settings_path)
+ expect(ApplicationSetting.current.default_preferred_language).to eq('zh_CN')
+ end
+
it 'updates the default_project_creation for string value' do
put :update, params: { application_setting: { default_project_creation: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS } }
@@ -319,6 +326,19 @@ RSpec.describe Admin::ApplicationSettingsController, :do_not_mock_admin_mode_set
end
end
+ describe 'Terraform settings' do
+ let(:application_setting) { ApplicationSetting.current }
+
+ context 'max_terraform_state_size_bytes' do
+ it 'updates the receive_max_input_size setting' do
+ put :update, params: { application_setting: { max_terraform_state_size_bytes: '123' } }
+
+ expect(response).to redirect_to(general_admin_application_settings_path)
+ expect(application_setting.max_terraform_state_size_bytes).to eq(123)
+ end
+ end
+ end
+
describe 'user_email_lookup_limit aliasing' do
let(:application_setting) { ApplicationSetting.current }
let(:user_email_lookup_limit) { 8675 }
diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb
index 6085f0e1239..c534cf14327 100644
--- a/spec/controllers/admin/groups_controller_spec.rb
+++ b/spec/controllers/admin/groups_controller_spec.rb
@@ -52,4 +52,48 @@ RSpec.describe Admin::GroupsController do
post :create, params: { group: { path: 'test', name: 'test' } }
end
end
+
+ describe 'PUT #update' do
+ subject(:update!) do
+ put :update, params: { id: group.to_param, group: { runner_registration_enabled: new_value } }
+ end
+
+ context 'with runner registration disabled' do
+ let(:runner_registration_enabled) { false }
+ let(:new_value) { '1' }
+
+ it 'updates the setting successfully' do
+ update!
+
+ expect(response).to have_gitlab_http_status(:found)
+ expect(group.reload.runner_registration_enabled).to eq(true)
+ end
+
+ it 'does not change the registration token' do
+ expect do
+ update!
+ group.reload
+ end.not_to change(group, :runners_token)
+ end
+ end
+
+ context 'with runner registration enabled' do
+ let(:runner_registration_enabled) { true }
+ let(:new_value) { '0' }
+
+ it 'updates the setting successfully' do
+ update!
+
+ expect(response).to have_gitlab_http_status(:found)
+ expect(group.reload.runner_registration_enabled).to eq(false)
+ end
+
+ it 'changes the registration token' do
+ expect do
+ update!
+ group.reload
+ end.to change(group, :runners_token)
+ end
+ end
+ end
end
diff --git a/spec/controllers/admin/hooks_controller_spec.rb b/spec/controllers/admin/hooks_controller_spec.rb
index 82e4b873bf6..4101bd7f658 100644
--- a/spec/controllers/admin/hooks_controller_spec.rb
+++ b/spec/controllers/admin/hooks_controller_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe Admin::HooksController do
hook.reload
expect(response).to have_gitlab_http_status(:found)
- expect(flash[:notice]).to include('successfully updated')
+ expect(flash[:notice]).to include('was updated')
expect(hook).to have_attributes(hook_params.except(:url_variables))
expect(hook).to have_attributes(
url_variables: { 'token' => 'some secret value', 'baz' => 'woo' }
diff --git a/spec/controllers/admin/plan_limits_controller_spec.rb b/spec/controllers/admin/plan_limits_controller_spec.rb
index 2666925c2b7..99795de51d8 100644
--- a/spec/controllers/admin/plan_limits_controller_spec.rb
+++ b/spec/controllers/admin/plan_limits_controller_spec.rb
@@ -29,6 +29,26 @@ RSpec.describe Admin::PlanLimitsController do
end
end
+ context "when pipeline_hierarchy_size is passed in params" do
+ let(:params) do
+ {
+ plan_limits: {
+ plan_id: plan.id,
+ pipeline_hierarchy_size: 200, id: plan_limits.id
+ }
+ }
+ end
+
+ it "updates the pipeline_hierarchy_size plan limit" do
+ sign_in(create(:admin))
+
+ post :create, params: params
+
+ expect(response).to redirect_to(general_admin_application_settings_path)
+ expect(plan_limits.reload.pipeline_hierarchy_size).to eq(params[:plan_limits][:pipeline_hierarchy_size])
+ end
+ end
+
context 'without admin access' do
let(:file_size) { 1.megabytes }
diff --git a/spec/controllers/admin/runner_projects_controller_spec.rb b/spec/controllers/admin/runner_projects_controller_spec.rb
index 98f961f66bb..38cc2d171ac 100644
--- a/spec/controllers/admin/runner_projects_controller_spec.rb
+++ b/spec/controllers/admin/runner_projects_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Admin::RunnerProjectsController do
+RSpec.describe Admin::RunnerProjectsController, feature_category: :runner_fleet do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
diff --git a/spec/controllers/admin/runners_controller_spec.rb b/spec/controllers/admin/runners_controller_spec.rb
index 9e852cb28dd..6d58abb9d4d 100644
--- a/spec/controllers/admin/runners_controller_spec.rb
+++ b/spec/controllers/admin/runners_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Admin::RunnersController do
+RSpec.describe Admin::RunnersController, feature_category: :runner_fleet do
let_it_be(:runner) { create(:ci_runner) }
let_it_be(:user) { create(:admin) }
diff --git a/spec/controllers/concerns/check_rate_limit_spec.rb b/spec/controllers/concerns/check_rate_limit_spec.rb
index 34ececfe639..75776acd520 100644
--- a/spec/controllers/concerns/check_rate_limit_spec.rb
+++ b/spec/controllers/concerns/check_rate_limit_spec.rb
@@ -19,11 +19,9 @@ RSpec.describe CheckRateLimit do
@current_user = current_user
end
- def redirect_back_or_default(**args)
- end
+ def redirect_back_or_default(**args); end
- def render(**args)
- end
+ def render(**args); end
end
end
diff --git a/spec/controllers/concerns/issuable_actions_spec.rb b/spec/controllers/concerns/issuable_actions_spec.rb
index 37d9dc080e1..34f47ed16f2 100644
--- a/spec/controllers/concerns/issuable_actions_spec.rb
+++ b/spec/controllers/concerns/issuable_actions_spec.rb
@@ -14,8 +14,7 @@ RSpec.describe IssuableActions do
klass = Class.new do
attr_reader :current_user, :project, :issuable
- def self.before_action(action = nil, params = nil)
- end
+ def self.before_action(action = nil, params = nil); end
include IssuableActions
@@ -40,8 +39,7 @@ RSpec.describe IssuableActions do
[]
end
- def render(options)
- end
+ def render(options); end
end
klass.new(issuable, project, user, finder_params_for_issuable)
diff --git a/spec/controllers/dashboard/todos_controller_spec.rb b/spec/controllers/dashboard/todos_controller_spec.rb
index efa00877142..4f01839fec4 100644
--- a/spec/controllers/dashboard/todos_controller_spec.rb
+++ b/spec/controllers/dashboard/todos_controller_spec.rb
@@ -3,14 +3,12 @@
require 'spec_helper'
RSpec.describe Dashboard::TodosController do
- let(:user) { create(:user) }
- let(:author) { create(:user) }
- let(:project) { create(:project) }
- let(:todo_service) { TodoService.new }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project).tap { |project| project.add_developer(user) } }
+ let_it_be(:author) { create(:user) }
before do
sign_in(user)
- project.add_developer(user)
end
describe 'GET #index' do
@@ -83,11 +81,14 @@ RSpec.describe Dashboard::TodosController do
end
it_behaves_like 'paginated collection' do
- let!(:issues) { create_list(:issue, 3, project: project, assignees: [user]) }
+ let_it_be(:issues) { create_list(:issue, 3, project: project, assignees: [user]) }
let(:collection) { user.todos }
+ before_all do
+ issues.each { |issue| TodoService.new.new_issue(issue, user) }
+ end
+
before do
- issues.each { |issue| todo_service.new_issue(issue, user) }
allow(Kaminari.config).to receive(:default_per_page).and_return(2)
end
@@ -126,6 +127,26 @@ RSpec.describe Dashboard::TodosController do
expect(assigns(:todos)).to match_array(mentioned_todos)
end
+
+ context 'when filtering by type Issue' do
+ it 'also includes work item todos' do
+ mentioned_issue_todos = [
+ create(:todo, :mentioned, project: project, user: user, target: issues.first),
+ create(
+ :todo,
+ :mentioned,
+ project: project,
+ user: user,
+ target_id: issues.last.id,
+ target_type: 'WorkItem'
+ )
+ ]
+
+ get :index, params: { action_id: ::Todo::MENTIONED, type: 'Issue', project_id: project.id }
+
+ expect(assigns(:todos)).to match_array(mentioned_issue_todos)
+ end
+ end
end
end
diff --git a/spec/controllers/explore/projects_controller_spec.rb b/spec/controllers/explore/projects_controller_spec.rb
index 5c977439af4..a79d9fa1276 100644
--- a/spec/controllers/explore/projects_controller_spec.rb
+++ b/spec/controllers/explore/projects_controller_spec.rb
@@ -101,6 +101,7 @@ RSpec.describe Explore::ProjectsController do
expect(response).to have_gitlab_http_status(:not_found)
end
end
+
context 'when topic exists' do
before do
create(:topic, name: 'topic1')
diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb
index fe8b0291733..75f281caa90 100644
--- a/spec/controllers/graphql_controller_spec.rb
+++ b/spec/controllers/graphql_controller_spec.rb
@@ -47,6 +47,23 @@ RSpec.describe GraphqlController do
'raisedAt' => /graphql_controller_spec.rb/))
)
end
+
+ it 'handles Gitlab::Auth::TooManyIps', :aggregate_failures do
+ allow(controller).to receive(:execute) do
+ raise Gitlab::Auth::TooManyIps.new(150, '123.123.123.123', 10)
+ end
+
+ expect(controller).to receive(:log_exception).and_call_original
+
+ post :execute
+
+ expect(json_response).to include(
+ 'errors' => include(
+ a_hash_including('message' => 'User 150 from IP: 123.123.123.123 tried logging from too many ips: 10')
+ )
+ )
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
end
describe 'POST #execute' do
@@ -191,7 +208,7 @@ RSpec.describe GraphqlController do
expected_message = "Authentication error: " \
"enable 2FA in your profile settings to continue using GitLab: %{mfa_help_page}" %
- { mfa_help_page: EnforcesTwoFactorAuthentication::MFA_HELP_PAGE }
+ { mfa_help_page: controller.mfa_help_page_url }
expect(json_response).to eq({ 'errors' => [{ 'message' => expected_message }] })
end
diff --git a/spec/controllers/groups/application_controller_spec.rb b/spec/controllers/groups/application_controller_spec.rb
new file mode 100644
index 00000000000..46908fdb26a
--- /dev/null
+++ b/spec/controllers/groups/application_controller_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Groups::ApplicationController do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+
+ describe '#respond_to_missing?' do
+ it 'returns true if the method matches the name structure' do
+ expect(controller.respond_to?(:authorize_read_usage_quotas!)).to eq(true)
+ end
+
+ it 'returns false if the method does not match the name structure' do
+ expect(controller.respond_to?(:does_not_exist)).to eq(false)
+ end
+ end
+
+ describe '#method_missing' do
+ controller do
+ before_action :authorize_read_usage_quotas!
+
+ def index
+ head :ok
+ end
+ end
+
+ it 'calls authorize_action! with the policy and renders not_found when user not authorized' do
+ group.add_maintainer(user)
+ sign_in(user)
+ get :index, params: { group_id: group.to_param }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(response.headers['X-GitLab-Custom-Error']).to eq '1'
+ end
+
+ it 'calls authorize_action! with the policy and renders OK when user is authorized' do
+ group.add_owner(user)
+ sign_in(user)
+ get :index, params: { group_id: group.to_param }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+end
diff --git a/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb b/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb
index 5b4b00106cb..f1ca9e11a1a 100644
--- a/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb
+++ b/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb
@@ -233,7 +233,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
end
it_behaves_like 'a successful manifest pull'
- it_behaves_like 'a package tracking event', described_class.name, 'pull_manifest'
+ it_behaves_like 'a package tracking event', described_class.name, 'pull_manifest', false
context 'with workhorse response' do
let(:pull_response) { { status: :success, manifest: nil, from_cache: false } }
@@ -303,7 +303,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
end
it_behaves_like 'a successful blob pull'
- it_behaves_like 'a package tracking event', described_class.name, 'pull_blob_from_cache'
+ it_behaves_like 'a package tracking event', described_class.name, 'pull_blob_from_cache', false
context 'when cache entry does not exist' do
let(:blob_sha) { 'a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4' }
@@ -387,7 +387,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
group.add_guest(user)
end
- it_behaves_like 'a package tracking event', described_class.name, 'pull_blob'
+ it_behaves_like 'a package tracking event', described_class.name, 'pull_blob', false
it 'creates a blob' do
expect { subject }.to change { group.dependency_proxy_blobs.count }.by(1)
@@ -445,7 +445,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
group.add_guest(user)
end
- it_behaves_like 'a package tracking event', described_class.name, 'pull_manifest'
+ it_behaves_like 'a package tracking event', described_class.name, 'pull_manifest', false
it_behaves_like 'with invalid path'
context 'with no existing manifest' do
diff --git a/spec/controllers/groups/labels_controller_spec.rb b/spec/controllers/groups/labels_controller_spec.rb
index 37db26096d3..0521c5e02a8 100644
--- a/spec/controllers/groups/labels_controller_spec.rb
+++ b/spec/controllers/groups/labels_controller_spec.rb
@@ -54,7 +54,7 @@ RSpec.describe Groups::LabelsController do
get :index, params: { group_id: group.to_param }
end
- it 'avoids N+1 queries' do
+ it 'avoids N+1 queries', :use_clean_rails_redis_caching do
control = ActiveRecord::QueryRecorder.new(skip_cached: false) { get :index, params: { group_id: group.to_param } }
create_list(:group_label, 3, group: group)
diff --git a/spec/controllers/groups/registry/repositories_controller_spec.rb b/spec/controllers/groups/registry/repositories_controller_spec.rb
index 62c15201a95..efdafcd2657 100644
--- a/spec/controllers/groups/registry/repositories_controller_spec.rb
+++ b/spec/controllers/groups/registry/repositories_controller_spec.rb
@@ -114,7 +114,7 @@ RSpec.describe Groups::Registry::RepositoriesController do
it_behaves_like 'with name parameter'
- it_behaves_like 'a package tracking event', described_class.name, 'list_repositories'
+ it_behaves_like 'a package tracking event', described_class.name, 'list_repositories', false
context 'with project in subgroup' do
let_it_be(:test_group) { create(:group, parent: group) }
diff --git a/spec/controllers/groups/runners_controller_spec.rb b/spec/controllers/groups/runners_controller_spec.rb
index 2add3cd3b18..93c1571bb6c 100644
--- a/spec/controllers/groups/runners_controller_spec.rb
+++ b/spec/controllers/groups/runners_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::RunnersController do
+RSpec.describe Groups::RunnersController, feature_category: :runner_fleet do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
diff --git a/spec/controllers/groups/settings/repository_controller_spec.rb b/spec/controllers/groups/settings/repository_controller_spec.rb
index 73a205069f5..ab1bb134b03 100644
--- a/spec/controllers/groups/settings/repository_controller_spec.rb
+++ b/spec/controllers/groups/settings/repository_controller_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe Groups::Settings::RepositoryController do
let(:good_deploy_token_params) do
{
name: 'name',
- expires_at: 1.day.from_now.to_s,
+ expires_at: 1.day.from_now.to_datetime.to_s,
username: 'deployer',
read_repository: '1',
deploy_token_type: DeployToken.deploy_token_types[:group_type]
diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb
index e73e61b6ec5..35f712dc50d 100644
--- a/spec/controllers/import/bitbucket_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_controller_spec.rb
@@ -185,6 +185,14 @@ RSpec.describe Import::BitbucketController do
post :create, format: :json
+ expect_snowplow_event(
+ category: 'Import::BitbucketController',
+ action: 'create',
+ label: 'import_access_level',
+ user: user,
+ extra: { user_role: 'Owner', import_type: 'bitbucket' }
+ )
+
expect(response).to have_gitlab_http_status(:ok)
end
@@ -297,6 +305,14 @@ RSpec.describe Import::BitbucketController do
.to receive(:new).and_return(double(execute: project))
expect { post :create, format: :json }.not_to change(Namespace, :count)
+
+ expect_snowplow_event(
+ category: 'Import::BitbucketController',
+ action: 'create',
+ label: 'import_access_level',
+ user: user,
+ extra: { user_role: 'Owner', import_type: 'bitbucket' }
+ )
end
it "takes the current user's namespace" do
@@ -417,6 +433,14 @@ RSpec.describe Import::BitbucketController do
post :create, params: { target_namespace: other_namespace.name }, format: :json
expect(response).to have_gitlab_http_status(:unprocessable_entity)
+
+ expect_snowplow_event(
+ category: 'Import::BitbucketController',
+ action: 'create',
+ label: 'import_access_level',
+ user: user,
+ extra: { user_role: 'Not a member', import_type: 'bitbucket' }
+ )
end
end
end
diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb
index f3632e7370c..a85af89b262 100644
--- a/spec/controllers/import/github_controller_spec.rb
+++ b/spec/controllers/import/github_controller_spec.rb
@@ -41,6 +41,16 @@ RSpec.describe Import::GithubController do
expect(response).to render_template(:new)
end
end
+
+ it 'gets authorization url using oauth client' do
+ allow(controller).to receive(:logged_in_with_provider?).and_return(true)
+ expect(controller).to receive(:go_to_provider_for_permissions).and_call_original
+ expect_next_instance_of(OAuth2::Client) do |client|
+ expect(client.auth_code).to receive(:authorize_url).and_call_original
+ end
+
+ get :new
+ end
end
describe "GET callback" do
@@ -124,7 +134,48 @@ RSpec.describe Import::GithubController do
end
describe "GET status" do
- context 'when using OAuth' do
+ shared_examples 'calls repos through Clients::Proxy with expected args' do
+ it 'calls repos list from provider with expected args' do
+ expect_next_instance_of(Gitlab::GithubImport::Clients::Proxy) do |client|
+ expect(client).to receive(:repos)
+ .with(expected_filter, expected_pagination_options)
+ .and_return({ repos: [], page_info: {} })
+ end
+
+ get :status, params: params, format: :json
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['imported_projects'].size).to eq 0
+ expect(json_response['provider_repos'].size).to eq 0
+ expect(json_response['incompatible_repos'].size).to eq 0
+ expect(json_response['page_info']).to eq({})
+ end
+ end
+
+ let(:provider_token) { 'asdasd12345' }
+ let(:client_auth_success) { true }
+ let(:client_stub) { instance_double(Gitlab::GithubImport::Client, user: { login: 'user' }) }
+ let(:expected_pagination_options) { pagination_params.merge(first: 25, page: 1, per_page: 25) }
+ let(:expected_filter) { nil }
+ let(:params) { nil }
+ let(:pagination_params) { { before: nil, after: nil } }
+ let(:provider_repos) { [] }
+
+ before do
+ allow_next_instance_of(Gitlab::GithubImport::Clients::Proxy) do |proxy|
+ if client_auth_success
+ allow(proxy).to receive(:repos).and_return({ repos: provider_repos })
+ allow(proxy).to receive(:client).and_return(client_stub)
+ else
+ allow(proxy).to receive(:repos).and_raise(Octokit::Unauthorized)
+ end
+ end
+ session[:"#{provider}_access_token"] = provider_token
+ end
+
+ context 'with OAuth' do
+ let(:provider_token) { nil }
+
before do
allow(controller).to receive(:logged_in_with_provider?).and_return(true)
end
@@ -146,178 +197,133 @@ RSpec.describe Import::GithubController do
end
end
- context 'when feature remove_legacy_github_client is disabled' do
- before do
- stub_feature_flags(remove_legacy_github_client: false)
- session[:"#{provider}_access_token"] = 'asdasd12345'
- end
+ context 'with invalid access token' do
+ let(:client_auth_success) { false }
- it_behaves_like 'a GitHub-ish import controller: GET status'
+ it "handles an invalid token" do
+ get :status, format: :json
- it 'uses Gitlab::LegacyGitHubImport::Client' do
- expect(controller.send(:client)).to be_instance_of(Gitlab::LegacyGithubImport::Client)
+ expect(session[:"#{provider}_access_token"]).to be_nil
+ expect(controller).to redirect_to(new_import_url)
+ expect(flash[:alert]).to eq("Access denied to your #{Gitlab::ImportSources.title(provider.to_s)} account.")
end
+ end
- it 'fetches repos using legacy client' do
- expect_next_instance_of(Gitlab::LegacyGithubImport::Client) do |client|
- expect(client).to receive(:repos).and_return([])
- end
+ context 'when user has few different repos' do
+ let(:repo_struct) { Struct.new(:id, :login, :full_name, :name, :owner, keyword_init: true) }
+ let(:provider_repos) do
+ [repo_struct.new(login: 'vim', full_name: 'asd/vim', name: 'vim', owner: { login: 'owner' })]
+ end
- get :status
+ let!(:imported_project) do
+ create(
+ :project,
+ import_type: provider, namespace: user.namespace,
+ import_status: :finished, import_source: 'example/repo'
+ )
end
- it 'gets authorization url using legacy client' do
- allow(controller).to receive(:logged_in_with_provider?).and_return(true)
- expect(controller).to receive(:go_to_provider_for_permissions).and_call_original
- expect_next_instance_of(Gitlab::LegacyGithubImport::Client) do |client|
- expect(client).to receive(:authorize_url).and_call_original
- end
+ it 'responds with expected high-level structure' do
+ get :status, format: :json
- get :new
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.dig("imported_projects", 0, "id")).to eq(imported_project.id)
+ expect(json_response.dig("provider_repos", 0, "id")).to eq(provider_repos[0].id)
end
end
- context 'when feature remove_legacy_github_client is enabled' do
+ it_behaves_like 'calls repos through Clients::Proxy with expected args'
+
+ context 'with namespace_id param' do
+ let_it_be(:user) { create(:user) }
+
before do
- stub_feature_flags(remove_legacy_github_client: true)
- session[:"#{provider}_access_token"] = 'asdasd12345'
+ sign_in(user)
end
- it_behaves_like 'a GitHub-ish import controller: GET status'
-
- it 'uses Gitlab::GithubImport::Client' do
- expect(controller.send(:client)).to be_instance_of(Gitlab::GithubImport::Client)
+ after do
+ sign_out(user)
end
- it 'fetches repos using latest github client' do
- expect_next_instance_of(Gitlab::GithubImport::Client) do |client|
- expect(client).to receive(:repos).and_return([])
- end
+ context 'when user is allowed to create projects in this namespace' do
+ let(:namespace) { create(:namespace, owner: user) }
- get :status
- end
+ it 'provides namespace to the template' do
+ get :status, params: { namespace_id: namespace.id }, format: :html
- it 'gets authorization url using oauth client' do
- allow(controller).to receive(:logged_in_with_provider?).and_return(true)
- expect(controller).to receive(:go_to_provider_for_permissions).and_call_original
- expect_next_instance_of(OAuth2::Client) do |client|
- expect(client.auth_code).to receive(:authorize_url).and_call_original
+ expect(response).to have_gitlab_http_status :ok
+ expect(assigns(:namespace)).to eq(namespace)
end
-
- get :new
end
- context 'pagination' do
- context 'when no page is specified' do
- it 'requests first page' do
- expect_next_instance_of(Gitlab::GithubImport::Client) do |client|
- expect(client).to receive(:repos).with({ page: 1, per_page: 25 }).and_return([])
- end
-
- get :status
- end
- end
+ context 'when user is not allowed to create projects in this namespace' do
+ let(:namespace) { create(:namespace) }
- context 'when page is specified' do
- it 'requests repos with specified page' do
- expect_next_instance_of(Octokit::Client) do |client|
- expect(client).to receive(:repos).with(nil, { page: 2, per_page: 25 }).and_return([].to_enum)
- end
+ it 'renders 404' do
+ get :status, params: { namespace_id: namespace.id }, format: :html
- get :status, params: { page: 2 }
- end
+ expect(response).to have_gitlab_http_status :not_found
end
end
+ end
- context 'when filtering' do
- let(:filter) { 'test' }
- let(:user_login) { 'user' }
- let(:collaborations_subquery) { 'repo:repo1 repo:repo2' }
- let(:organizations_subquery) { 'org:org1 org:org2' }
- let(:search_query) { "test in:name is:public,private user:#{user_login} #{collaborations_subquery} #{organizations_subquery}" }
-
- before do
- allow_next_instance_of(Octokit::Client) do |client|
- allow(client).to receive(:user).and_return(double(login: user_login))
- end
- end
+ context 'pagination' do
+ context 'when cursor is specified' do
+ let(:pagination_params) { { before: nil, after: 'CURSOR' } }
+ let(:params) { pagination_params }
- it 'makes request to github search api' do
- expect_next_instance_of(Octokit::Client) do |client|
- expect(client).to receive(:user).and_return({ login: user_login })
- expect(client).to receive(:search_repositories).with(search_query, { page: 1, per_page: 25 }).and_return({ items: [].to_enum })
- end
+ it_behaves_like 'calls repos through Clients::Proxy with expected args'
+ end
- expect_next_instance_of(Gitlab::GithubImport::Client) do |client|
- expect(client).to receive(:collaborations_subquery).and_return(collaborations_subquery)
- expect(client).to receive(:organizations_subquery).and_return(organizations_subquery)
- end
+ context 'when page is specified' do
+ let(:pagination_params) { { before: nil, after: nil, page: 2 } }
+ let(:expected_pagination_options) { pagination_params.merge(first: 25, page: 2, per_page: 25) }
+ let(:params) { pagination_params }
- get :status, params: { filter: filter }, format: :json
- end
+ it_behaves_like 'calls repos through Clients::Proxy with expected args'
+ end
+ end
- context 'pagination' do
- context 'when no page is specified' do
- it 'requests first page' do
- expect_next_instance_of(Octokit::Client) do |client|
- expect(client).to receive(:user).and_return({ login: user_login })
- expect(client).to receive(:search_repositories).with(search_query, { page: 1, per_page: 25 }).and_return({ items: [].to_enum })
- end
-
- expect_next_instance_of(Gitlab::GithubImport::Client) do |client|
- expect(client).to receive(:collaborations_subquery).and_return(collaborations_subquery)
- expect(client).to receive(:organizations_subquery).and_return(organizations_subquery)
- end
-
- get :status, params: { filter: filter }, format: :json
- end
- end
+ context 'when filtering' do
+ let(:filter_param) { FFaker::Lorem.word }
+ let(:params) { { filter: filter_param } }
+ let(:expected_filter) { filter_param }
- context 'when page is specified' do
- it 'requests repos with specified page' do
- expect_next_instance_of(Octokit::Client) do |client|
- expect(client).to receive(:user).and_return({ login: user_login })
- expect(client).to receive(:search_repositories).with(search_query, { page: 2, per_page: 25 }).and_return({ items: [].to_enum })
- end
+ it_behaves_like 'calls repos through Clients::Proxy with expected args'
- expect_next_instance_of(Gitlab::GithubImport::Client) do |client|
- expect(client).to receive(:collaborations_subquery).and_return(collaborations_subquery)
- expect(client).to receive(:organizations_subquery).and_return(organizations_subquery)
- end
+ context 'with pagination' do
+ context 'when before cursor present' do
+ let(:pagination_params) { { before: 'before-cursor', after: nil } }
+ let(:params) { { filter: filter_param }.merge(pagination_params) }
- get :status, params: { filter: filter, page: 2 }, format: :json
- end
- end
+ it_behaves_like 'calls repos through Clients::Proxy with expected args'
end
- context 'when user input contains colons and spaces' do
- before do
- allow_next_instance_of(Gitlab::GithubImport::Client) do |client|
- allow(client).to receive(:search_repos_by_name).and_return(items: [])
- end
- end
+ context 'when after cursor present' do
+ let(:pagination_params) { { before: nil, after: 'after-cursor' } }
+ let(:params) { { filter: filter_param }.merge(pagination_params) }
- it 'sanitizes user input' do
- filter = ' test1:test2 test3 : test4 '
- expected_filter = 'test1test2test3test4'
+ it_behaves_like 'calls repos through Clients::Proxy with expected args'
+ end
+ end
- get :status, params: { filter: filter }, format: :json
+ context 'when user input contains colons and spaces' do
+ let(:filter_param) { ' test1:test2 test3 : test4 ' }
+ let(:expected_filter) { 'test1test2test3test4' }
- expect(assigns(:filter)).to eq(expected_filter)
- end
- end
+ it_behaves_like 'calls repos through Clients::Proxy with expected args'
+ end
+ end
- context 'when rate limit threshold is exceeded' do
- before do
- allow(controller).to receive(:status).and_raise(Gitlab::GithubImport::RateLimitError)
- end
+ context 'when rate limit threshold is exceeded' do
+ before do
+ allow(controller).to receive(:status).and_raise(Gitlab::GithubImport::RateLimitError)
+ end
- it 'returns 429' do
- get :status, params: { filter: 'test' }, format: :json
+ it 'returns 429' do
+ get :status, format: :json
- expect(response).to have_gitlab_http_status(:too_many_requests)
- end
- end
+ expect(response).to have_gitlab_http_status(:too_many_requests)
end
end
end
@@ -331,12 +337,12 @@ RSpec.describe Import::GithubController do
describe "GET realtime_changes" do
let(:user) { create(:user) }
- it_behaves_like 'a GitHub-ish import controller: GET realtime_changes'
-
before do
assign_session_token(provider)
end
+ it_behaves_like 'a GitHub-ish import controller: GET realtime_changes'
+
it 'includes stats in response' do
create(:project, import_type: provider, namespace: user.namespace, import_status: :finished, import_source: 'example/repo')
diff --git a/spec/controllers/jira_connect/events_controller_spec.rb b/spec/controllers/jira_connect/events_controller_spec.rb
index 80375a02b33..7da9eb7ac16 100644
--- a/spec/controllers/jira_connect/events_controller_spec.rb
+++ b/spec/controllers/jira_connect/events_controller_spec.rb
@@ -101,6 +101,14 @@ RSpec.describe JiraConnect::EventsController do
expect(response).to have_gitlab_http_status(:ok)
end
+ it 'uses the JiraConnectInstallations::UpdateService' do
+ expect_next_instance_of(JiraConnectInstallations::UpdateService, installation, anything) do |update_service|
+ expect(update_service).to receive(:execute).and_call_original
+ end
+
+ subject
+ end
+
context 'when parameters include a new shared secret and base_url' do
let(:shared_secret) { 'new_secret' }
let(:base_url) { 'https://new_test.atlassian.net' }
@@ -125,6 +133,36 @@ RSpec.describe JiraConnect::EventsController do
end
end
end
+
+ shared_examples 'generates JWT validation claims' do
+ specify do
+ expect_next_instance_of(Atlassian::JiraConnect::Jwt::Asymmetric, anything, expected_claims) do |asymmetric_jwt|
+ allow(asymmetric_jwt).to receive(:valid?).and_return(true)
+ end
+
+ subject
+ end
+ end
+
+ context 'when enforce_jira_base_url_https' do
+ before do
+ allow(Gitlab.config.jira_connect).to receive(:enforce_jira_base_url_https).and_return(true)
+ end
+
+ let(:expected_claims) { { aud: "https://test.host/-/jira_connect", iss: anything, qsh: anything } }
+
+ it_behaves_like 'generates JWT validation claims'
+ end
+
+ context 'when not enforce_jira_base_url_https' do
+ before do
+ allow(Gitlab.config.jira_connect).to receive(:enforce_jira_base_url_https).and_return(false)
+ end
+
+ let(:expected_claims) { { aud: "http://test.host/-/jira_connect", iss: anything, qsh: anything } }
+
+ it_behaves_like 'generates JWT validation claims'
+ end
end
describe '#uninstalled' do
diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb
index 0560ccb25dd..ab3f3fd397d 100644
--- a/spec/controllers/omniauth_callbacks_controller_spec.rb
+++ b/spec/controllers/omniauth_callbacks_controller_spec.rb
@@ -391,6 +391,32 @@ RSpec.describe OmniauthCallbacksController, type: :controller do
end
end
end
+
+ context 'with snowplow tracking', :snowplow do
+ let(:provider) { 'google_oauth2' }
+ let(:extern_uid) { 'my-uid' }
+
+ context 'when sign_in' do
+ it 'does not track the event' do
+ post provider
+ expect_no_snowplow_event
+ end
+ end
+
+ context 'when sign_up' do
+ let(:user) { double(email: generate(:email)) }
+
+ it 'tracks the event' do
+ post provider
+
+ expect_snowplow_event(
+ category: described_class.name,
+ action: "#{provider}_sso",
+ user: User.find_by(email: user.email)
+ )
+ end
+ end
+ end
end
describe '#saml' do
diff --git a/spec/controllers/profiles/keys_controller_spec.rb b/spec/controllers/profiles/keys_controller_spec.rb
index 63818337722..ed9022faf1b 100644
--- a/spec/controllers/profiles/keys_controller_spec.rb
+++ b/spec/controllers/profiles/keys_controller_spec.rb
@@ -14,13 +14,14 @@ RSpec.describe Profiles::KeysController do
expires_at = 3.days.from_now
expect do
- post :create, params: { key: build(:key, expires_at: expires_at).attributes }
+ post :create, params: { key: build(:key, usage_type: :signing, expires_at: expires_at).attributes }
end.to change { Key.count }.by(1)
key = Key.last
expect(key.expires_at).to be_like_time(expires_at)
expect(key.fingerprint_md5).to be_present
expect(key.fingerprint_sha256).to be_present
+ expect(key.usage_type).to eq('signing')
end
context 'with FIPS mode', :fips_mode do
diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb
index 5927f20df97..2334521b8a8 100644
--- a/spec/controllers/projects/environments_controller_spec.rb
+++ b/spec/controllers/projects/environments_controller_spec.rb
@@ -91,6 +91,34 @@ RSpec.describe Projects::EnvironmentsController do
expect(json_response['stopped_count']).to eq 1
end
+ it 'supports search within environment folder name' do
+ create(:environment, project: project, name: 'review-app', state: :available)
+
+ get :index, params: environment_params(format: :json, search: 'review')
+
+ expect(environments.map { |env| env['name'] }).to contain_exactly('review-app',
+ 'staging/review-1',
+ 'staging/review-2')
+ expect(json_response['available_count']).to eq 3
+ expect(json_response['stopped_count']).to eq 1
+ end
+
+ context 'when enable_environments_search_within_folder FF is disabled' do
+ before do
+ stub_feature_flags(enable_environments_search_within_folder: false)
+ end
+
+ it 'ignores name inside folder' do
+ create(:environment, project: project, name: 'review-app', state: :available)
+
+ get :index, params: environment_params(format: :json, search: 'review')
+
+ expect(environments.map { |env| env['name'] }).to contain_exactly('review-app')
+ expect(json_response['available_count']).to eq 1
+ expect(json_response['stopped_count']).to eq 0
+ end
+ end
+
it 'sets the polling interval header' do
subject
diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb
index 3dfc22927cf..1e9d999311a 100644
--- a/spec/controllers/projects/graphs_controller_spec.rb
+++ b/spec/controllers/projects/graphs_controller_spec.rb
@@ -11,6 +11,50 @@ RSpec.describe Projects::GraphsController do
project.add_maintainer(user)
end
+ describe '#show' do
+ subject { get(:show, params: params) }
+
+ let(:params) { { namespace_id: project.namespace.path, project_id: project.path, id: 'master' } }
+
+ describe 'ref_type' do
+ it 'assigns ref_type' do
+ subject
+
+ expect(assigns[:languages]).to be_nil
+ end
+
+ context 'when ref_type is provided' do
+ before do
+ params[:ref_type] = 'heads'
+ end
+
+ it 'assigns ref_type' do
+ subject
+
+ expect(assigns[:ref_type]).to eq('heads')
+ end
+ end
+ end
+
+ describe 'when format is json' do
+ let(:stubbed_limit) { 1 }
+
+ before do
+ params[:format] = 'json'
+ stub_const('Projects::GraphsController::MAX_COMMITS', stubbed_limit)
+ end
+
+ it 'renders json' do
+ subject
+
+ expect(json_response.size).to eq(stubbed_limit)
+ %w[author_name author_email date].each do |key|
+ expect(json_response[0]).to have_key(key)
+ end
+ end
+ end
+ end
+
describe 'GET languages' do
it "redirects_to action charts" do
get(:commits, params: { namespace_id: project.namespace.path, project_id: project.path, id: 'master' })
diff --git a/spec/controllers/projects/hooks_controller_spec.rb b/spec/controllers/projects/hooks_controller_spec.rb
index 18f16937505..815370d428d 100644
--- a/spec/controllers/projects/hooks_controller_spec.rb
+++ b/spec/controllers/projects/hooks_controller_spec.rb
@@ -59,7 +59,7 @@ RSpec.describe Projects::HooksController do
put :update, params: params
expect(response).to have_gitlab_http_status(:found)
- expect(flash[:notice]).to include('successfully updated')
+ expect(flash[:notice]).to include('was updated')
expect(hook.reload.url_variables).to eq(
'a' => 'updated',
@@ -154,28 +154,6 @@ RSpec.describe Projects::HooksController do
expect(flash[:alert]).to be_blank
end
- it 'ignores branch_filter_strategy when flag is disabled' do
- stub_feature_flags(enhanced_webhook_support_regex: false)
- hook_params = {
- url: 'http://example.com',
- branch_filter_strategy: 'regex',
- push_events: true
- }
- params = { namespace_id: project.namespace, project_id: project, hook: hook_params }
-
- expect { post :create, params: params }.to change(ProjectHook, :count).by(1)
-
- project_hook = ProjectHook.order_id_desc.take
-
- expect(project_hook).to have_attributes(
- url: 'http://example.com',
- branch_filter_strategy: 'wildcard'
- )
-
- expect(response).to have_gitlab_http_status(:found)
- expect(flash[:alert]).to be_blank
- end
-
it 'alerts the user if the new hook is invalid' do
hook_params = {
token: "TEST\nTOKEN",
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 8f26be442a7..31e297e5773 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -141,18 +141,32 @@ RSpec.describe Projects::IssuesController do
project.add_developer(user)
end
- it "returns issue attributes" do
- participants = create_list(:issue_email_participant, 2, issue: issue)
+ context 'issue email participants' do
+ context 'when issue is confidential' do
+ let(:issue) { create(:issue, project: project, confidential: true) }
+ let!(:participants) { create_list(:issue_email_participant, 2, issue: issue) }
- get :show, params: { namespace_id: project.namespace, project_id: project, id: issue.iid }, format: :json
+ it "returns issue email participants" do
+ get :show, params: { namespace_id: project.namespace, project_id: project, id: issue.iid }, format: :json
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response).to include(
- 'issue_email_participants' => contain_exactly(
- { "email" => participants[0].email }, { "email" => participants[1].email }
- ),
- 'type' => 'ISSUE'
- )
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to include(
+ 'issue_email_participants' => contain_exactly(
+ { "email" => participants[0].email }, { "email" => participants[1].email }
+ ),
+ 'type' => 'ISSUE'
+ )
+ end
+ end
+
+ context 'when issue is not confidential' do
+ it "returns empty email participants" do
+ get :show, params: { namespace_id: project.namespace, project_id: project, id: issue.iid }, format: :json
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to include('issue_email_participants' => [])
+ end
+ end
end
context 'when issue is not a task and work items feature flag is enabled' do
@@ -366,10 +380,10 @@ RSpec.describe Projects::IssuesController do
}
end
- context 'the current user cannot download code' do
+ context 'the current user cannot read code' do
it 'prevents access' do
allow(controller).to receive(:can?).with(any_args).and_return(true)
- allow(controller).to receive(:can?).with(user, :download_code, project).and_return(false)
+ allow(controller).to receive(:can?).with(user, :read_code, project).and_return(false)
subject
diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb
index a5259522fe2..dfa6ed639b6 100644
--- a/spec/controllers/projects/labels_controller_spec.rb
+++ b/spec/controllers/projects/labels_controller_spec.rb
@@ -100,7 +100,7 @@ RSpec.describe Projects::LabelsController do
list_labels
end
- it 'avoids N+1 queries' do
+ it 'avoids N+1 queries', :use_clean_rails_redis_caching do
control = ActiveRecord::QueryRecorder.new(skip_cached: false) { list_labels }
create_list(:label, 3, project: project)
diff --git a/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb b/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb
index 366a1e587ab..311af26abf6 100644
--- a/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb
@@ -105,15 +105,13 @@ RSpec.describe Projects::MergeRequests::ConflictsController do
if section['conflict']
expect(line['type']).to be_in(%w(old new))
expect(line.values_at('old_line', 'new_line')).to contain_exactly(nil, a_kind_of(Integer))
+ elsif line['type'].nil?
+ expect(line['old_line']).not_to eq(nil)
+ expect(line['new_line']).not_to eq(nil)
else
- if line['type'].nil?
- expect(line['old_line']).not_to eq(nil)
- expect(line['new_line']).not_to eq(nil)
- else
- expect(line['type']).to eq('match')
- expect(line['old_line']).to eq(nil)
- expect(line['new_line']).to eq(nil)
- end
+ expect(line['type']).to eq('match')
+ expect(line['old_line']).to eq(nil)
+ expect(line['new_line']).to eq(nil)
end
end
end
diff --git a/spec/controllers/projects/merge_requests/creations_controller_spec.rb b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
index a061a14c7b1..ace8c04b819 100644
--- a/spec/controllers/projects/merge_requests/creations_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
@@ -306,4 +306,18 @@ RSpec.describe Projects::MergeRequests::CreationsController do
post :create, params: merge_request_params
end
end
+
+ describe 'GET target_projects', feature_category: :code_review do
+ it 'returns target projects JSON' do
+ get :target_projects, params: { namespace_id: project.namespace.to_param, project_id: project }
+
+ expect(json_response.size).to be(2)
+
+ forked_project = json_response.first
+ expect(forked_project).to have_key('id')
+ expect(forked_project).to have_key('name')
+ expect(forked_project).to have_key('full_path')
+ expect(forked_project).to have_key('refs_url')
+ end
+ end
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 026cf19bde5..a93dc806283 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::MergeRequestsController do
+RSpec.describe Projects::MergeRequestsController, feature_category: :code_review do
include ProjectForksHelper
include Gitlab::Routing
using RSpec::Parameterized::TableSyntax
diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb
index 1f8e96258ca..0afd2e10ea2 100644
--- a/spec/controllers/projects/notes_controller_spec.rb
+++ b/spec/controllers/projects/notes_controller_spec.rb
@@ -150,7 +150,7 @@ RSpec.describe Projects::NotesController do
context 'when user cannot read commit' do
before do
allow(Ability).to receive(:allowed?).and_call_original
- allow(Ability).to receive(:allowed?).with(user, :download_code, project).and_return(false)
+ allow(Ability).to receive(:allowed?).with(user, :read_code, project).and_return(false)
end
it 'renders 404' do
@@ -757,6 +757,7 @@ RSpec.describe Projects::NotesController do
expect { put :update, params: request_params }.to change { note.reload.note }
end
end
+
context "doesnt update the note" do
let(:issue) { create(:issue, :confidential, project: project) }
let(:note) { create(:note, noteable: issue, project: project) }
diff --git a/spec/controllers/projects/pipeline_schedules_controller_spec.rb b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
index 5bcfae4227c..358b98621a5 100644
--- a/spec/controllers/projects/pipeline_schedules_controller_spec.rb
+++ b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
@@ -259,7 +259,7 @@ RSpec.describe Projects::PipelineSchedulesController do
context 'when adds a new duplicated variable' do
let(:schedule) do
basic_param.merge({
- variables_attributes: [{ key: 'CCC', secret_value: 'AAA123' }]
+ variables_attributes: [{ key: 'dup_key', secret_value: 'value_one' }, { key: 'dup_key', secret_value: 'value_two' }]
})
end
@@ -302,7 +302,7 @@ RSpec.describe Projects::PipelineSchedulesController do
let(:schedule) do
basic_param.merge({
variables_attributes: [{ id: pipeline_schedule_variable.id, _destroy: true },
- { key: 'CCC', secret_value: 'CCC123' }]
+ { key: 'AAA', secret_value: 'AAA123' }]
})
end
@@ -310,8 +310,8 @@ RSpec.describe Projects::PipelineSchedulesController do
expect { go }.not_to change { Ci::PipelineScheduleVariable.count }
pipeline_schedule.reload
- expect(pipeline_schedule.variables.last.key).to eq('CCC')
- expect(pipeline_schedule.variables.last.value).to eq('CCC123')
+ expect(pipeline_schedule.variables.last.key).to eq('AAA')
+ expect(pipeline_schedule.variables.last.value).to eq('AAA123')
end
end
end
diff --git a/spec/controllers/projects/refs_controller_spec.rb b/spec/controllers/projects/refs_controller_spec.rb
index 56415663109..a7a8361ae20 100644
--- a/spec/controllers/projects/refs_controller_spec.rb
+++ b/spec/controllers/projects/refs_controller_spec.rb
@@ -2,15 +2,89 @@
require 'spec_helper'
-RSpec.describe Projects::RefsController do
- let(:project) { create(:project, :repository) }
- let(:user) { create(:user) }
+RSpec.describe Projects::RefsController, feature_category: :source_code_management do
+ let_it_be(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
before do
sign_in(user)
project.add_developer(user)
end
+ describe 'GET #switch' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:id) { 'master' }
+ let(:params) do
+ { destination: destination, namespace_id: project.namespace.to_param, project_id: project, id: id,
+ ref_type: ref_type }
+ end
+
+ subject { get :switch, params: params }
+
+ context 'when the use_ref_type_parameter feature flag is not enabled' do
+ before do
+ stub_feature_flags(use_ref_type_parameter: false)
+ end
+
+ where(:destination, :ref_type, :redirected_to) do
+ 'tree' | nil | lazy { project_tree_path(project, id) }
+ 'tree' | 'heads' | lazy { project_tree_path(project, id) }
+ 'blob' | nil | lazy { project_blob_path(project, id) }
+ 'blob' | 'heads' | lazy { project_blob_path(project, id) }
+ 'graph' | nil | lazy { project_network_path(project, id) }
+ 'graph' | 'heads' | lazy { project_network_path(project, id) }
+ 'graphs' | nil | lazy { project_graph_path(project, id) }
+ 'graphs' | 'heads' | lazy { project_graph_path(project, id) }
+ 'find_file' | nil | lazy { project_find_file_path(project, id) }
+ 'find_file' | 'heads' | lazy { project_find_file_path(project, id) }
+ 'graphs_commits' | nil | lazy { commits_project_graph_path(project, id) }
+ 'graphs_commits' | 'heads' | lazy { commits_project_graph_path(project, id) }
+ 'badges' | nil | lazy { project_settings_ci_cd_path(project, ref: id) }
+ 'badges' | 'heads' | lazy { project_settings_ci_cd_path(project, ref: id) }
+ 'commits' | nil | lazy { project_commits_path(project, id) }
+ 'commits' | 'heads' | lazy { project_commits_path(project, id) }
+ 'somethingelse' | nil | lazy { project_commits_path(project, id) }
+ 'somethingelse' | 'heads' | lazy { project_commits_path(project, id) }
+ end
+
+ with_them do
+ it 'redirects to destination' do
+ expect(subject).to redirect_to(redirected_to)
+ end
+ end
+ end
+
+ context 'when the use_ref_type_parameter feature flag is enabled' do
+ where(:destination, :ref_type, :redirected_to) do
+ 'tree' | nil | lazy { project_tree_path(project, id) }
+ 'tree' | 'heads' | lazy { project_tree_path(project, id) }
+ 'blob' | nil | lazy { project_blob_path(project, id) }
+ 'blob' | 'heads' | lazy { project_blob_path(project, id) }
+ 'graph' | nil | lazy { project_network_path(project, id) }
+ 'graph' | 'heads' | lazy { project_network_path(project, id, ref_type: 'heads') }
+ 'graphs' | nil | lazy { project_graph_path(project, id) }
+ 'graphs' | 'heads' | lazy { project_graph_path(project, id, ref_type: 'heads') }
+ 'find_file' | nil | lazy { project_find_file_path(project, id) }
+ 'find_file' | 'heads' | lazy { project_find_file_path(project, id) }
+ 'graphs_commits' | nil | lazy { commits_project_graph_path(project, id) }
+ 'graphs_commits' | 'heads' | lazy { commits_project_graph_path(project, id) }
+ 'badges' | nil | lazy { project_settings_ci_cd_path(project, ref: id) }
+ 'badges' | 'heads' | lazy { project_settings_ci_cd_path(project, ref: id) }
+ 'commits' | nil | lazy { project_commits_path(project, id) }
+ 'commits' | 'heads' | lazy { project_commits_path(project, id, ref_type: 'heads') }
+ nil | nil | lazy { project_commits_path(project, id) }
+ nil | 'heads' | lazy { project_commits_path(project, id, ref_type: 'heads') }
+ end
+
+ with_them do
+ it 'redirects to destination' do
+ expect(subject).to redirect_to(redirected_to)
+ end
+ end
+ end
+ end
+
describe 'GET #logs_tree' do
let(:path) { 'foo/bar/baz.html' }
diff --git a/spec/controllers/projects/registry/repositories_controller_spec.rb b/spec/controllers/projects/registry/repositories_controller_spec.rb
index f4f5c182850..59bc1ba04e7 100644
--- a/spec/controllers/projects/registry/repositories_controller_spec.rb
+++ b/spec/controllers/projects/registry/repositories_controller_spec.rb
@@ -120,22 +120,6 @@ RSpec.describe Projects::Registry::RepositoriesController do
expect_snowplow_event(category: anything, action: 'delete_repository')
end
-
- context 'with container_registry_delete_repository_with_cron_worker disabled' do
- before do
- stub_feature_flags(container_registry_delete_repository_with_cron_worker: false)
- end
-
- it 'schedules a job to delete a repository' do
- expect(DeleteContainerRepositoryWorker).to receive(:perform_async).with(user.id, repository.id)
-
- expect { delete_repository(repository) }
- .to change { repository.reload.status }.from(nil).to('delete_scheduled')
-
- expect(repository.reload).to be_delete_scheduled
- expect(response).to have_gitlab_http_status(:no_content)
- end
- end
end
end
end
diff --git a/spec/controllers/projects/runners_controller_spec.rb b/spec/controllers/projects/runners_controller_spec.rb
index 1066c4ec9f6..5733b8114d4 100644
--- a/spec/controllers/projects/runners_controller_spec.rb
+++ b/spec/controllers/projects/runners_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::RunnersController do
+RSpec.describe Projects::RunnersController, feature_category: :runner_fleet do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:runner) { create(:ci_runner, :project, projects: [project]) }
diff --git a/spec/controllers/projects/service_ping_controller_spec.rb b/spec/controllers/projects/service_ping_controller_spec.rb
index 22fb18edc80..10d4b897564 100644
--- a/spec/controllers/projects/service_ping_controller_spec.rb
+++ b/spec/controllers/projects/service_ping_controller_spec.rb
@@ -91,11 +91,14 @@ RSpec.describe Projects::ServicePingController do
expect(response).to have_gitlab_http_status(:ok)
end
- it_behaves_like 'Snowplow event tracking' do
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:project) { create(:project) }
- let(:category) { 'ide_edit' }
- let(:action) { 'g_edit_by_live_preview' }
let(:namespace) { project.namespace }
+ let(:category) { 'Gitlab::UsageDataCounters::EditorUniqueCounter' }
+ let(:action) { 'ide_edit' }
+ let(:property) { 'g_edit_by_live_preview' }
+ let(:label) { 'usage_activity_by_stage_monthly.create.action_monthly_active_users_ide_edit' }
+ let(:context) { [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event_name).to_context] }
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
end
end
diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
index e5ae1b04a86..dcd1072612a 100644
--- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
@@ -19,8 +19,10 @@ RSpec.describe Projects::Settings::CiCdController do
let_it_be(:group) { create(:group, parent: parent_group) }
let_it_be(:other_project) { create(:project, group: group) }
+ subject { get :show, params: { namespace_id: project.namespace, project_id: project } }
+
it 'renders show with 200 status code' do
- get :show, params: { namespace_id: project.namespace, project_id: project }
+ subject
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:show)
@@ -32,25 +34,60 @@ RSpec.describe Projects::Settings::CiCdController do
end
it 'renders show with 404 status code' do
- get :show, params: { namespace_id: project.namespace, project_id: project }
+ subject
+
expect(response).to have_gitlab_http_status(:not_found)
end
end
- context 'with group runners' do
- let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group]) }
- let_it_be(:project_runner) { create(:ci_runner, :project, projects: [other_project]) }
- let_it_be(:shared_runner) { create(:ci_runner, :instance) }
+ context 'with assignable project runners' do
+ let(:project_runner) { create(:ci_runner, :project, projects: [other_project]) }
- it 'sets assignable project runners only' do
+ before do
group.add_maintainer(user)
+ end
- get :show, params: { namespace_id: project.namespace, project_id: project }
+ it 'sets assignable project runners' do
+ subject
expect(assigns(:assignable_runners)).to contain_exactly(project_runner)
end
end
+ context 'with project runners' do
+ let(:project_runner) { create(:ci_runner, :project, projects: [project]) }
+
+ it 'sets project runners' do
+ subject
+
+ expect(assigns(:project_runners)).to contain_exactly(project_runner)
+ end
+ end
+
+ context 'with group runners' do
+ let_it_be(:group) { create :group }
+ let_it_be(:project) { create :project, group: group }
+ let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group]) }
+
+ it 'sets group runners' do
+ subject
+
+ expect(assigns(:group_runners_count)).to be(1)
+ expect(assigns(:group_runners)).to contain_exactly(group_runner)
+ end
+ end
+
+ context 'with instance runners' do
+ let_it_be(:shared_runner) { create(:ci_runner, :instance) }
+
+ it 'sets shared runners' do
+ subject
+
+ expect(assigns(:shared_runners_count)).to be(1)
+ expect(assigns(:shared_runners)).to contain_exactly(shared_runner)
+ end
+ end
+
context 'prevents N+1 queries for tags' do
render_views
diff --git a/spec/controllers/projects/settings/integrations_controller_spec.rb b/spec/controllers/projects/settings/integrations_controller_spec.rb
index 2b23f177a9d..2ce58a77d94 100644
--- a/spec/controllers/projects/settings/integrations_controller_spec.rb
+++ b/spec/controllers/projects/settings/integrations_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::Settings::IntegrationsController do
+RSpec.describe Projects::Settings::IntegrationsController, feature_category: :integrations do
include JiraIntegrationHelpers
include AfterNextHelpers
@@ -39,168 +39,174 @@ RSpec.describe Projects::Settings::IntegrationsController do
end
end
- describe '#test' do
- context 'when the integration is not testable' do
- it 'renders 404' do
- allow_any_instance_of(Integration).to receive(:testable?).and_return(false)
+ describe '#test', :clean_gitlab_redis_rate_limiting do
+ let_it_be(:integration) { create(:external_wiki_integration, project: project) }
- put :test, params: project_params
+ let(:integration_params) { { external_wiki_url: 'https://example.net/wiki' } }
- expect(response).to have_gitlab_http_status(:not_found)
+ it 'renders 404 when the integration is not testable' do
+ allow_next_found_instance_of(integration.class) do |integration|
+ allow(integration).to receive(:testable?).and_return(false)
end
- end
-
- context 'when validations fail', :clean_gitlab_redis_rate_limiting do
- let(:integration_params) { { active: 'true', url: '' } }
- it 'returns error messages in JSON response' do
- put :test, params: project_params(service: integration_params)
+ put :test, params: project_params(service: integration_params)
- expect(json_response['message']).to eq 'Validations failed.'
- expect(json_response['service_response']).to include "Url can't be blank"
- expect(response).to be_successful
- end
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response).to eq({})
end
- context 'when successful', :clean_gitlab_redis_rate_limiting do
- context 'with empty project' do
- let_it_be(:project) { create(:project) }
+ it 'returns success if test is successful' do
+ allow_next(Integrations::Test::ProjectService).to receive(:execute).and_return({ success: true })
- context 'with chat notification integration' do
- let_it_be(:teams_integration) { project.create_microsoft_teams_integration(webhook: 'http://webhook.com') }
+ put :test, params: project_params(service: integration_params)
- let(:integration) { teams_integration }
+ expect(response).to be_successful
+ expect(json_response).to eq({})
+ end
- it 'returns success' do
- allow_next(::MicrosoftTeams::Notifier).to receive(:ping).and_return(true)
+ it 'returns extra given data if test is successful' do
+ allow_next(Integrations::Test::ProjectService).to receive(:execute)
+ .and_return({ success: true, data: { my_payload: true } })
- put :test, params: project_params
+ put :test, params: project_params(service: integration_params)
- expect(response).to be_successful
- end
- end
-
- it 'returns success' do
- stub_jira_integration_test
-
- expect(Gitlab::HTTP).to receive(:get).with('/rest/api/2/serverInfo', any_args).and_call_original
+ expect(response).to be_successful
+ expect(json_response).to eq({ 'my_payload' => true })
+ end
- put :test, params: project_params(service: integration_params)
+ it 'returns an error response if the test is not successful' do
+ allow_next(Integrations::Test::ProjectService).to receive(:execute).and_return({ success: false })
- expect(response).to be_successful
- end
- end
+ put :test, params: project_params(service: integration_params)
- it 'returns success' do
- stub_jira_integration_test
+ expect(response).to be_successful
+ expect(json_response).to eq(
+ 'error' => true,
+ 'message' => 'Connection failed. Check your integration settings.',
+ 'service_response' => '',
+ 'test_failed' => true
+ )
+ end
- expect(Gitlab::HTTP).to receive(:get).with('/rest/api/2/serverInfo', any_args).and_call_original
+ it 'returns extra given message if the test is not successful' do
+ allow_next(Integrations::Test::ProjectService).to receive(:execute)
+ .and_return({ success: false, result: 'Result of test' })
- put :test, params: project_params(service: integration_params)
+ put :test, params: project_params(service: integration_params)
- expect(response).to be_successful
- end
-
- context 'when service is configured for the first time' do
- let(:integration_params) do
- {
- 'active' => '1',
- 'push_events' => '1',
- 'token' => 'token',
- 'project_url' => 'https://buildkite.com/organization/pipeline'
- }
- end
+ expect(response).to be_successful
+ expect(json_response).to eq(
+ 'error' => true,
+ 'message' => 'Connection failed. Check your integration settings.',
+ 'service_response' => 'Result of test',
+ 'test_failed' => true
+ )
+ end
- before do
- allow_next(ServiceHook).to receive(:execute).and_return(true)
- end
+ it 'returns an error response if a network exception is raised' do
+ allow_next(Integrations::Test::ProjectService).to receive(:execute).and_raise(Errno::ECONNREFUSED)
- it 'persist the object' do
- do_put
+ put :test, params: project_params(service: integration_params)
- expect(response).to be_successful
- expect(json_response).to be_empty
- expect(Integrations::Buildkite.first).to be_present
- end
+ expect(response).to be_successful
+ expect(json_response).to eq(
+ 'error' => true,
+ 'message' => 'Connection failed. Check your integration settings.',
+ 'service_response' => 'Connection refused',
+ 'test_failed' => true
+ )
+ end
- it 'creates the ServiceHook object' do
- do_put
+ it 'returns error messages in JSON response if validations fail' do
+ integration_params = { active: 'true', external_wiki_url: '' }
- expect(response).to be_successful
- expect(json_response).to be_empty
- expect(Integrations::Buildkite.first.service_hook).to be_present
- end
+ put :test, params: project_params(service: integration_params)
- def do_put
- put :test, params: project_params(id: 'buildkite',
- service: integration_params)
- end
- end
+ expect(json_response['message']).to eq 'Validations failed.'
+ expect(json_response['service_response']).to eq(
+ "External wiki url can't be blank, External wiki url must be a valid URL"
+ )
+ expect(response).to be_successful
end
- context 'when unsuccessful', :clean_gitlab_redis_rate_limiting do
- it 'returns an error response when the integration test fails' do
- stub_request(:get, 'http://example.com/rest/api/2/serverInfo')
- .to_return(status: 404)
+ context 'when integration has a webhook' do
+ let_it_be(:integration) { create(:integrations_slack, project: project) }
+
+ it 'returns an error response if the webhook URL is changed to one that is blocked' do
+ integration_params = { webhook: 'http://127.0.0.1' }
put :test, params: project_params(service: integration_params)
expect(response).to be_successful
expect(json_response).to eq(
'error' => true,
- 'message' => 'Connection failed. Check your integration settings.',
- 'service_response' => '',
- 'test_failed' => true
+ 'message' => 'Validations failed.',
+ 'service_response' => "Webhook is blocked: Requests to localhost are not allowed",
+ 'test_failed' => false
)
end
- context 'with the Slack integration' do
- let_it_be(:integration) { build(:integrations_slack) }
+ it 'ignores masked webhook param' do
+ integration_params = { active: 'true', webhook: '************' }
+ allow_next(Integrations::Test::ProjectService).to receive(:execute).and_return({ success: true })
- it 'returns an error response when the URL is blocked' do
- put :test, params: project_params(service: { webhook: 'http://127.0.0.1' })
+ expect do
+ put :test, params: project_params(service: integration_params)
+ end.not_to change { integration.reload.webhook }
- expect(response).to be_successful
- expect(json_response).to eq(
- 'error' => true,
- 'message' => 'Connection failed. Check your integration settings.',
- 'service_response' => "URL 'http://127.0.0.1' is blocked: Requests to localhost are not allowed",
- 'test_failed' => true
- )
- end
+ expect(response).to be_successful
+ expect(json_response).to eq({})
+ end
- it 'returns an error response when a network exception is raised' do
- expect_next(Integrations::Slack).to receive(:test).and_raise(Errno::ECONNREFUSED)
+ it 'creates an associated web hook record if web hook integration is configured for the first time' do
+ integration_params = {
+ 'active' => '1',
+ 'issues_events' => '1',
+ 'push_events' => '0',
+ 'token' => 'my-token',
+ 'project_url' => 'https://buildkite.com/organization/pipeline'
+ }
+ allow_next(ServiceHook).to receive(:execute).and_return(true)
- put :test, params: project_params
+ expect do
+ put :test, params: project_params(id: 'buildkite', service: integration_params)
+ end.to change { Integrations::Buildkite.count }.from(0).to(1)
- expect(response).to be_successful
- expect(json_response).to eq(
- 'error' => true,
- 'message' => 'Connection failed. Check your integration settings.',
- 'service_response' => 'Connection refused',
- 'test_failed' => true
- )
- end
+ integration = Integrations::Buildkite.take
+
+ expect(response).to be_successful
+ expect(json_response).to eq({})
+ expect(integration).to have_attributes(
+ project_url: 'https://buildkite.com/organization/pipeline',
+ issues_events: true,
+ push_events: false
+ )
+ expect(integration.service_hook).to have_attributes(
+ url: 'https://webhook.buildkite.com/deliver/{webhook_token}',
+ interpolated_url: 'https://webhook.buildkite.com/deliver/my-token'
+ )
end
end
- context 'when the endpoint receives requests above the limit', :freeze_time, :clean_gitlab_redis_rate_limiting do
+ context 'when the endpoint receives requests above the rate limit', :freeze_time do
before do
allow(Gitlab::ApplicationRateLimiter).to receive(:rate_limits)
.and_return(project_testing_integration: { threshold: 1, interval: 1.minute })
end
it 'prevents making test requests' do
- stub_jira_integration_test
-
expect_next_instance_of(::Integrations::Test::ProjectService) do |service|
expect(service).to receive(:execute).and_return(http_status: 200)
end
2.times { post :test, params: project_params(service: integration_params) }
- expect(response.body).to include(_('This endpoint has been requested too many times. Try again later.'))
+ expect(json_response).to eq(
+ {
+ 'error' => true,
+ 'message' => 'This endpoint has been requested too many times. Try again later.'
+ }
+ )
expect(response).to have_gitlab_http_status(:ok)
end
end
diff --git a/spec/controllers/projects/settings/repository_controller_spec.rb b/spec/controllers/projects/settings/repository_controller_spec.rb
index ea50ff6caa0..51ea2e5d7c6 100644
--- a/spec/controllers/projects/settings/repository_controller_spec.rb
+++ b/spec/controllers/projects/settings/repository_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::Settings::RepositoryController do
+RSpec.describe Projects::Settings::RepositoryController, feature_category: :source_code_management do
let(:project) { create(:project_empty_repo, :public) }
let(:user) { create(:user) }
let(:base_params) { { namespace_id: project.namespace, project_id: project } }
@@ -19,6 +19,40 @@ RSpec.describe Projects::Settings::RepositoryController do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:show)
end
+
+ context 'when feature flag `group_protected_branches` disabled' do
+ before do
+ stub_feature_flags(group_protected_branches: false)
+ end
+
+ it 'does not assign instance variable `protected_group_branches`' do
+ get :show, params: base_params
+
+ expect(assigns).not_to include(:protected_group_branches)
+ end
+ end
+
+ context 'when feature flag `group_protected_branches` enabled' do
+ context 'when the root namespace is a user' do
+ it 'assigns empty instance variable `protected_group_branches`' do
+ get :show, params: base_params
+
+ expect(assigns[:protected_group_branches]).to eq([])
+ end
+ end
+
+ context 'when the root namespace is a group' do
+ let_it_be(:project) { create(:project_empty_repo, :public, :in_group) }
+
+ let(:protected_group_branch) { create(:protected_branch, group: project.root_namespace, project: nil) }
+
+ it 'assigns instance variable `protected_group_branches`' do
+ get :show, params: base_params
+
+ expect(assigns[:protected_group_branches]).to include(protected_group_branch)
+ end
+ end
+ end
end
describe 'PUT cleanup' do
@@ -54,7 +88,7 @@ RSpec.describe Projects::Settings::RepositoryController do
let(:good_deploy_token_params) do
{
name: 'name',
- expires_at: 1.day.from_now.to_s,
+ expires_at: 1.day.from_now.to_datetime.to_s,
username: 'deployer',
read_repository: '1',
deploy_token_type: DeployToken.deploy_token_types[:project_type]
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 446e5e38865..bc58eaa1d6f 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -702,16 +702,12 @@ RSpec.describe ProjectsController do
skip unless project.hashed_storage?(:repository)
hashed_storage_path = ::Storage::Hashed.new(project).disk_path
- original_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.repository.path
- end
+ original_repository_path = project.repository.relative_path
expect { update_project path: 'renamed_path' }.to change { project.reload.path }
expect(project.path).to include 'renamed_path'
- assign_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- assigns(:repository).path
- end
+ assign_repository_path = assigns(:repository).relative_path
expect(original_repository_path).to include(hashed_storage_path)
expect(assign_repository_path).to include(hashed_storage_path)
@@ -721,16 +717,12 @@ RSpec.describe ProjectsController do
skip if project.hashed_storage?(:repository)
hashed_storage_path = Storage::Hashed.new(project).disk_path
- original_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.repository.path
- end
+ original_repository_path = project.repository.relative_path
expect { update_project path: 'renamed_path' }.to change { project.reload.path }
expect(project.path).to include 'renamed_path'
- assign_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- assigns(:repository).path
- end
+ assign_repository_path = assigns(:repository).relative_path
expect(original_repository_path).not_to include(hashed_storage_path)
expect(assign_repository_path).to include(hashed_storage_path)
@@ -928,35 +920,6 @@ RSpec.describe ProjectsController do
with_them do
it_behaves_like 'feature update success'
end
-
- context 'for feature_access_level operations_access_level' do
- let(:feature_access_level) { :operations_access_level }
-
- include_examples 'feature update failure'
- end
-
- context 'with feature flag split_operations_visibility_permissions disabled' do
- before do
- stub_feature_flags(split_operations_visibility_permissions: false)
- end
-
- context 'for feature_access_level operations_access_level' do
- let(:feature_access_level) { :operations_access_level }
-
- include_examples 'feature update success'
- end
-
- where(:feature_access_level) do
- %i[
- environments_access_level feature_flags_access_level
- monitor_access_level
- ]
- end
-
- with_them do
- it_behaves_like 'feature update failure'
- end
- end
end
end
@@ -1334,7 +1297,7 @@ RSpec.describe ProjectsController do
text: merge_request.to_reference
}
- expect(json_response['body']).to match(/\!#{merge_request.iid} \(closed\)/)
+ expect(json_response['body']).to match(/!#{merge_request.iid} \(closed\)/)
end
end
@@ -1635,6 +1598,12 @@ RSpec.describe ProjectsController do
context 'applies correct scope when throttling', :clean_gitlab_redis_rate_limiting do
before do
stub_application_setting(project_download_export_limit: 1)
+
+ travel_to Date.current.beginning_of_day
+ end
+
+ after do
+ travel_back
end
it 'applies throttle per namespace' do
diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb
index 8775f68a5de..699052fe37a 100644
--- a/spec/controllers/registrations_controller_spec.rb
+++ b/spec/controllers/registrations_controller_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe RegistrationsController do
let(:session_params) { {} }
- subject { post(:create, params: user_params, session: session_params) }
+ subject(:post_create) { post(:create, params: user_params, session: session_params) }
context '`blocked_pending_approval` state' do
context 'when the `require_admin_approval_after_user_signup` setting is turned on' do
@@ -75,9 +75,9 @@ RSpec.describe RegistrationsController do
end
context 'email confirmation' do
- context 'when `send_user_confirmation_email` is true' do
+ context 'when `email_confirmation_setting` is set to `hard`' do
before do
- stub_application_setting(send_user_confirmation_email: true)
+ stub_application_setting_enum('email_confirmation_setting', 'hard')
end
it 'does not send a confirmation email' do
@@ -122,9 +122,9 @@ RSpec.describe RegistrationsController do
end
context 'email confirmation' do
- context 'when `send_user_confirmation_email` is true' do
+ context 'when `email_confirmation_setting` is set to `hard`' do
before do
- stub_application_setting(send_user_confirmation_email: true)
+ stub_application_setting_enum('email_confirmation_setting', 'hard')
stub_feature_flags(identity_verification: false)
end
@@ -142,18 +142,18 @@ RSpec.describe RegistrationsController do
stub_feature_flags(identity_verification: false)
end
- context 'when send_user_confirmation_email is false' do
+ context 'when `email_confirmation_setting` is set to `off`' do
it 'signs the user in' do
- stub_application_setting(send_user_confirmation_email: false)
+ stub_application_setting_enum('email_confirmation_setting', 'off')
expect { subject }.not_to have_enqueued_mail(DeviseMailer, :confirmation_instructions)
expect(controller.current_user).not_to be_nil
end
end
- context 'when send_user_confirmation_email is true' do
+ context 'when `email_confirmation_setting` is set to `hard`' do
before do
- stub_application_setting(send_user_confirmation_email: true)
+ stub_application_setting_enum('email_confirmation_setting', 'hard')
end
context 'when soft email confirmation is not enabled' do
@@ -167,6 +167,16 @@ RSpec.describe RegistrationsController do
expect(controller.current_user).to be_nil
end
+ it 'tracks an almost there redirect' do
+ post_create
+
+ expect_snowplow_event(
+ category: described_class.name,
+ action: 'render',
+ user: User.find_by(email: base_user_params[:email])
+ )
+ end
+
context 'when registration is triggered from an accepted invite' do
context 'when it is part from the initial invite email', :snowplow do
let_it_be(:member) { create(:project_member, :invited, invite_email: user_params.dig(:user, :email)) }
@@ -260,6 +270,16 @@ RSpec.describe RegistrationsController do
expect(response).to redirect_to(users_sign_up_welcome_path)
end
+ it 'does not track an almost there redirect' do
+ post_create
+
+ expect_no_snowplow_event(
+ category: described_class.name,
+ action: 'render',
+ user: User.find_by(email: base_user_params[:email])
+ )
+ end
+
context 'when invite email matches email used on registration' do
let(:session_params) { { invite_email: user_params.dig(:user, :email) } }
@@ -484,47 +504,56 @@ RSpec.describe RegistrationsController do
render_views
let_it_be(:new_user_params) { { new_user: base_user_params.merge({ password: "password" }) } }
- subject { post(:create, params: new_user_params) }
+ subject(:post_create) { post(:create, params: new_user_params) }
- context 'when block_weak_passwords is enabled (default)' do
- it 'renders the form with errors' do
- expect { subject }.not_to change(User, :count)
+ it 'renders the form with errors' do
+ expect { post_create }.not_to change(User, :count)
- expect(controller.current_user).to be_nil
- expect(response).to render_template(:new)
- expect(response.body).to include(_('Password must not contain commonly used combinations of words and letters'))
- end
+ expect(controller.current_user).to be_nil
+ expect(response).to render_template(:new)
+ expect(response.body).to include(_('Password must not contain commonly used combinations of words and letters'))
+ end
- it 'tracks the error' do
- subject
- expect_snowplow_event(
- category: 'Gitlab::Tracking::Helpers::WeakPasswordErrorEvent',
- action: 'track_weak_password_error',
- controller: 'RegistrationsController',
- method: 'create'
- )
- end
+ it 'tracks a weak password error' do
+ post_create
+
+ expect_snowplow_event(
+ category: 'Gitlab::Tracking::Helpers::WeakPasswordErrorEvent',
+ action: 'track_weak_password_error',
+ controller: 'RegistrationsController',
+ method: 'create'
+ )
end
- context 'when block_weak_passwords is disabled' do
- before do
- stub_feature_flags(block_weak_passwords: false)
- end
+ it 'does not track failed form submission' do
+ post_create
- it 'permits weak passwords' do
- expect { subject }.to change(User, :count).by(1)
- end
+ expect_no_snowplow_event(
+ category: described_class.name,
+ action: 'successfully_submitted_form'
+ )
end
end
context 'when the password is not weak' do
it 'does not track a weak password error' do
- subject
+ post_create
+
expect_no_snowplow_event(
category: 'Gitlab::Tracking::Helpers::WeakPasswordErrorEvent',
action: 'track_weak_password_error'
)
end
+
+ it 'tracks successful form submission' do
+ post_create
+
+ expect_snowplow_event(
+ category: described_class.name,
+ action: 'successfully_submitted_form',
+ user: User.find_by(email: base_user_params[:email])
+ )
+ end
end
context 'with preferred language' do
@@ -552,6 +581,39 @@ RSpec.describe RegistrationsController do
end
end
end
+
+ context 'when the first or last name is not "present?"' do
+ using RSpec::Parameterized::TableSyntax
+
+ render_views
+
+ shared_examples 'a user without present first name or last name' do
+ it 'renders the form with errors' do
+ subject
+ expect(controller.current_user).to be_nil
+ expect(response).to render_template(:new)
+ expect(response.body).to include(_('name cannot be blank')) # include 'First name' or 'Last name' or both
+ end
+ end
+
+ where(:first_name, :last_name) do
+ nil | 'last'
+ '' | 'last'
+ ' ' | 'last'
+ 'first' | nil
+ 'first' | ''
+ 'first' | ' '
+ '' | ''
+ end
+
+ with_them do
+ before do
+ base_user_params.merge!({ first_name: first_name, last_name: last_name })
+ end
+
+ it_behaves_like 'a user without present first name or last name'
+ end
+ end
end
describe '#destroy' do
diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb
index 21df53fb074..37fc5a033ba 100644
--- a/spec/controllers/search_controller_spec.rb
+++ b/spec/controllers/search_controller_spec.rb
@@ -421,6 +421,12 @@ RSpec.describe SearchController do
expect(json_response.count).to eq(1)
expect(json_response.first['label']).to match(/User settings/)
end
+
+ it 'makes a call to search_autocomplete_opts' do
+ expect(controller).to receive(:search_autocomplete_opts).once
+
+ get :autocomplete, params: { term: 'setting', filter: 'generic' }
+ end
end
describe '#append_info_to_payload' do
diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb
index 69282f951f9..78b3cc63b08 100644
--- a/spec/controllers/sessions_controller_spec.rb
+++ b/spec/controllers/sessions_controller_spec.rb
@@ -477,6 +477,22 @@ RSpec.describe SessionsController do
expect { authenticate_2fa(login: user.username, otp_attempt: user.current_otp) }.to change { AuthenticationEvent.count }.by(1)
expect(AuthenticationEvent.last.provider).to eq("two-factor")
end
+
+ context 'when rendering devise two factor' do
+ render_views
+
+ before do
+ Gon.clear
+ end
+
+ it "adds gon variables" do
+ authenticate_2fa(login: user.username, password: user.password)
+
+ expect(response).to render_template('devise/sessions/two_factor')
+ expect(Gon.all_variables).not_to be_empty
+ expect(response.body).to match('gon.api_version')
+ end
+ end
end
context 'when using two-factor authentication via U2F device' do
diff --git a/spec/db/development/create_work_item_hierarchy_restrictions_spec.rb b/spec/db/development/create_work_item_hierarchy_restrictions_spec.rb
new file mode 100644
index 00000000000..0e60ecd08c0
--- /dev/null
+++ b/spec/db/development/create_work_item_hierarchy_restrictions_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Create work item hierarchy restrictions in development', feature_category: :portfolio_management do
+ subject { load Rails.root.join('db/fixtures/development/50_create_work_item_hierarchy_restrictions.rb') }
+
+ it_behaves_like 'work item hierarchy restrictions importer'
+end
diff --git a/spec/db/docs_spec.rb b/spec/db/docs_spec.rb
index ad3705c3dbe..6cfff725988 100644
--- a/spec/db/docs_spec.rb
+++ b/spec/db/docs_spec.rb
@@ -2,108 +2,95 @@
require 'spec_helper'
-RSpec.describe 'Database Documentation' do
- context 'for each table' do
- # TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/366834
- let(:database_base_models) { Gitlab::Database.database_base_models.select { |k, _| k != 'geo' } }
-
- let(:all_tables) do
- database_base_models.flat_map { |_, m| m.connection.tables }.sort.uniq
- end
-
- let(:metadata_required_fields) do
- %i(
- feature_categories
- table_name
- )
- end
+RSpec.shared_examples 'validate dictionary' do |objects, directory_path, required_fields|
+ context 'for each object' do
+ let(:directory_path) { directory_path }
let(:metadata_allowed_fields) do
- metadata_required_fields + %i(
+ required_fields + %i[
classes
description
introduced_by_url
milestone
- )
+ gitlab_schema
+ ]
end
let(:metadata) do
- all_tables.each_with_object({}) do |table_name, hash|
- next unless File.exist?(table_metadata_file_path(table_name))
+ objects.each_with_object({}) do |object_name, hash|
+ next unless File.exist?(object_metadata_file_path(object_name))
- hash[table_name] ||= load_table_metadata(table_name)
+ hash[object_name] ||= load_object_metadata(required_fields, object_name)
end
end
- let(:tables_without_metadata) do
- all_tables.reject { |t| metadata.has_key?(t) }
+ let(:objects_without_metadata) do
+ objects.reject { |t| metadata.has_key?(t) }
end
- let(:tables_without_valid_metadata) do
+ let(:objects_without_valid_metadata) do
metadata.select { |_, t| t.has_key?(:error) }.keys
end
- let(:tables_with_disallowed_fields) do
+ let(:objects_with_disallowed_fields) do
metadata.select { |_, t| t.has_key?(:disallowed_fields) }.keys
end
- let(:tables_with_missing_required_fields) do
+ let(:objects_with_missing_required_fields) do
metadata.select { |_, t| t.has_key?(:missing_required_fields) }.keys
end
it 'has a metadata file' do
- expect(tables_without_metadata).to be_empty, multiline_error(
+ expect(objects_without_metadata).to be_empty, multiline_error(
'Missing metadata files',
- tables_without_metadata.map { |t| " #{table_metadata_file(t)}" }
+ objects_without_metadata.map { |t| " #{object_metadata_file(t)}" }
)
end
it 'has a valid metadata file' do
- expect(tables_without_valid_metadata).to be_empty, table_metadata_errors(
+ expect(objects_without_valid_metadata).to be_empty, object_metadata_errors(
'Table metadata files with errors',
:error,
- tables_without_valid_metadata
+ objects_without_valid_metadata
)
end
it 'has a valid metadata file with allowed fields' do
- expect(tables_with_disallowed_fields).to be_empty, table_metadata_errors(
+ expect(objects_with_disallowed_fields).to be_empty, object_metadata_errors(
'Table metadata files with disallowed fields',
:disallowed_fields,
- tables_with_disallowed_fields
+ objects_with_disallowed_fields
)
end
it 'has a valid metadata file without missing fields' do
- expect(tables_with_missing_required_fields).to be_empty, table_metadata_errors(
+ expect(objects_with_missing_required_fields).to be_empty, object_metadata_errors(
'Table metadata files with missing fields',
:missing_required_fields,
- tables_with_missing_required_fields
+ objects_with_missing_required_fields
)
end
end
private
- def table_metadata_file(table_name)
- File.join('db', 'docs', "#{table_name}.yml")
+ def object_metadata_file(object_name)
+ File.join(directory_path, "#{object_name}.yml")
end
- def table_metadata_file_path(table_name)
- Rails.root.join(table_metadata_file(table_name))
+ def object_metadata_file_path(object_name)
+ Rails.root.join(object_metadata_file(object_name))
end
- def load_table_metadata(table_name)
+ def load_object_metadata(required_fields, object_name)
result = {}
begin
- result[:metadata] = YAML.safe_load(File.read(table_metadata_file_path(table_name))).deep_symbolize_keys
+ result[:metadata] = YAML.safe_load(File.read(object_metadata_file_path(object_name))).deep_symbolize_keys
disallowed_fields = (result[:metadata].keys - metadata_allowed_fields)
- unless disallowed_fields.empty?
- result[:disallowed_fields] = "fields not allowed: #{disallowed_fields.join(', ')}"
- end
+ result[:disallowed_fields] = "fields not allowed: #{disallowed_fields.join(', ')}" unless disallowed_fields.empty?
- missing_required_fields = (metadata_required_fields - result[:metadata].reject { |_, v| v.blank? }.keys)
+ missing_required_fields = (required_fields - result[:metadata].reject { |_, v| v.blank? }.keys)
unless missing_required_fields.empty?
result[:missing_required_fields] = "missing required fields: #{missing_required_fields.join(', ')}"
end
@@ -113,11 +100,12 @@ RSpec.describe 'Database Documentation' do
result
end
- def table_metadata_errors(title, field, tables)
- lines = tables.map do |table_name|
+ # rubocop:disable Naming/HeredocDelimiterNaming
+ def object_metadata_errors(title, field, objects)
+ lines = objects.map do |object_name|
<<~EOM
- #{table_metadata_file(table_name)}
- #{metadata[table_name][field]}
+ #{object_metadata_file(object_name)}
+ #{metadata[object_name][field]}
EOM
end
@@ -131,4 +119,23 @@ RSpec.describe 'Database Documentation' do
#{lines.join("\n")}
EOM
end
+ # rubocop:enable Naming/HeredocDelimiterNaming
+end
+
+RSpec.describe 'Views documentation', feature_category: :database do
+ database_base_models = Gitlab::Database.database_base_models.select { |k, _| k != 'geo' }
+ views = database_base_models.flat_map { |_, m| m.connection.views }.sort.uniq
+ directory_path = File.join('db', 'docs', 'views')
+ required_fields = %i[feature_categories view_name gitlab_schema]
+
+ include_examples 'validate dictionary', views, directory_path, required_fields
+end
+
+RSpec.describe 'Tables documentation', feature_category: :database do
+ database_base_models = Gitlab::Database.database_base_models.select { |k, _| k != 'geo' }
+ tables = database_base_models.flat_map { |_, m| m.connection.tables }.sort.uniq
+ directory_path = File.join('db', 'docs')
+ required_fields = %i[feature_categories table_name gitlab_schema]
+
+ include_examples 'validate dictionary', tables, directory_path, required_fields
end
diff --git a/spec/db/migration_spec.rb b/spec/db/migration_spec.rb
index 7751bfd989d..b5f6192233f 100644
--- a/spec/db/migration_spec.rb
+++ b/spec/db/migration_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe 'Migrations Validation' do
# The range describes the timestamps that given migration helper can be used
let(:all_migration_classes) do
{
+ 2022_12_01_02_15_00.. => Gitlab::Database::Migration[2.1],
2022_01_26_21_06_58.. => Gitlab::Database::Migration[2.0],
2021_09_01_15_33_24..2022_04_25_12_06_03 => Gitlab::Database::Migration[1.0],
2021_05_31_05_39_16..2021_09_01_15_33_24 => ActiveRecord::Migration[6.1],
diff --git a/spec/db/production/create_work_item_hierarchy_restrictions_spec.rb b/spec/db/production/create_work_item_hierarchy_restrictions_spec.rb
new file mode 100644
index 00000000000..5b47d88d71a
--- /dev/null
+++ b/spec/db/production/create_work_item_hierarchy_restrictions_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Create work item hierarchy restrictions in production', feature_category: :portfolio_management do
+ subject { load Rails.root.join('db/fixtures/production/020_create_work_item_hierarchy_restrictions.rb') }
+
+ it_behaves_like 'work item hierarchy restrictions importer'
+end
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index ad49a763361..9e23cca7c3f 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -6,12 +6,11 @@ require Rails.root.join('ee', 'spec', 'db', 'schema_support') if Gitlab.ee?
RSpec.describe 'Database schema' do
prepend_mod_with('DB::SchemaSupport')
- let(:connection) { ActiveRecord::Base.connection }
let(:tables) { connection.tables }
let(:columns_name_with_jsonb) { retrieve_columns_name_with_jsonb }
IGNORED_INDEXES_ON_FKS = {
- issues: %w[work_item_type_id]
+ slack_integrations_scopes: %w[slack_api_scope_id]
}.with_indifferent_access.freeze
TABLE_PARTITIONS = %w[ci_builds_metadata].freeze
@@ -33,15 +32,27 @@ RSpec.describe 'Database schema' do
boards: %w[milestone_id iteration_id],
chat_names: %w[chat_id team_id user_id],
chat_teams: %w[team_id],
+ ci_build_needs: %w[partition_id],
+ ci_build_pending_states: %w[partition_id],
+ ci_build_report_results: %w[partition_id],
+ ci_build_trace_chunks: %w[partition_id],
+ ci_build_trace_metadata: %w[partition_id],
ci_builds: %w[erased_by_id trigger_request_id partition_id],
+ ci_builds_runner_session: %w[partition_id],
p_ci_builds_metadata: %w[partition_id],
ci_job_artifacts: %w[partition_id],
+ ci_job_variables: %w[partition_id],
ci_namespace_monthly_usages: %w[namespace_id],
+ ci_pending_builds: %w[partition_id],
ci_pipeline_variables: %w[partition_id],
ci_pipelines: %w[partition_id],
+ ci_resources: %w[partition_id],
ci_runner_projects: %w[runner_id],
+ ci_running_builds: %w[partition_id],
+ ci_sources_pipelines: %w[partition_id source_partition_id],
ci_stages: %w[partition_id],
ci_trigger_requests: %w[commit_id],
+ ci_unit_test_failures: %w[partition_id],
cluster_providers_aws: %w[security_group_id vpc_id access_key_id],
cluster_providers_gcp: %w[gcp_project_id operation_id],
compliance_management_frameworks: %w[group_id],
@@ -109,56 +120,62 @@ RSpec.describe 'Database schema' do
}.with_indifferent_access.freeze
context 'for table' do
- (ActiveRecord::Base.connection.tables - TABLE_PARTITIONS).sort.each do |table|
- describe table do
- let(:indexes) { connection.indexes(table) }
- let(:columns) { connection.columns(table) }
- let(:foreign_keys) { connection.foreign_keys(table) }
- let(:loose_foreign_keys) { Gitlab::Database::LooseForeignKeys.definitions.group_by(&:from_table).fetch(table, []) }
- let(:all_foreign_keys) { foreign_keys + loose_foreign_keys }
- # take the first column in case we're using a composite primary key
- let(:primary_key_column) { Array(connection.primary_key(table)).first }
-
- context 'all foreign keys' do
- # for index to be effective, the FK constraint has to be at first place
- it 'are indexed' do
- first_indexed_column = indexes.filter_map do |index|
- columns = index.columns
-
- # In cases of complex composite indexes, a string is returned eg:
- # "lower((extern_uid)::text), group_id"
- columns = columns.split(',') if columns.is_a?(String)
- column = columns.first.chomp
-
- # A partial index is not suitable for a foreign key column, unless
- # the only condition is for the presence of the foreign key itself
- column if index.where.nil? || index.where == "(#{column} IS NOT NULL)"
+ Gitlab::Database::EachDatabase.each_database_connection do |connection, _|
+ schemas_for_connection = Gitlab::Database.gitlab_schemas_for_connection(connection)
+ (connection.tables - TABLE_PARTITIONS).sort.each do |table|
+ table_schema = Gitlab::Database::GitlabSchema.table_schema(table)
+ next unless schemas_for_connection.include?(table_schema)
+
+ describe table do
+ let(:indexes) { connection.indexes(table) }
+ let(:columns) { connection.columns(table) }
+ let(:foreign_keys) { connection.foreign_keys(table) }
+ let(:loose_foreign_keys) { Gitlab::Database::LooseForeignKeys.definitions.group_by(&:from_table).fetch(table, []) }
+ let(:all_foreign_keys) { foreign_keys + loose_foreign_keys }
+ # take the first column in case we're using a composite primary key
+ let(:primary_key_column) { Array(connection.primary_key(table)).first }
+
+ context 'all foreign keys' do
+ # for index to be effective, the FK constraint has to be at first place
+ it 'are indexed' do
+ first_indexed_column = indexes.filter_map do |index|
+ columns = index.columns
+
+ # In cases of complex composite indexes, a string is returned eg:
+ # "lower((extern_uid)::text), group_id"
+ columns = columns.split(',') if columns.is_a?(String)
+ column = columns.first.chomp
+
+ # A partial index is not suitable for a foreign key column, unless
+ # the only condition is for the presence of the foreign key itself
+ column if index.where.nil? || index.where == "(#{column} IS NOT NULL)"
+ end
+ foreign_keys_columns = all_foreign_keys.map(&:column)
+ required_indexed_columns = foreign_keys_columns - ignored_index_columns(table)
+
+ # Add the primary key column to the list of indexed columns because
+ # postgres and mysql both automatically create an index on the primary
+ # key. Also, the rails connection.indexes() method does not return
+ # automatically generated indexes (like the primary key index).
+ first_indexed_column.push(primary_key_column)
+
+ expect(first_indexed_column.uniq).to include(*required_indexed_columns)
end
- foreign_keys_columns = all_foreign_keys.map(&:column)
- required_indexed_columns = foreign_keys_columns - ignored_index_columns(table)
-
- # Add the primary key column to the list of indexed columns because
- # postgres and mysql both automatically create an index on the primary
- # key. Also, the rails connection.indexes() method does not return
- # automatically generated indexes (like the primary key index).
- first_indexed_column.push(primary_key_column)
-
- expect(first_indexed_column.uniq).to include(*required_indexed_columns)
end
- end
- context 'columns ending with _id' do
- let(:column_names) { columns.map(&:name) }
- let(:column_names_with_id) { column_names.select { |column_name| column_name.ends_with?('_id') } }
- let(:foreign_keys_columns) { all_foreign_keys.map(&:column).uniq } # we can have FK and loose FK present at the same time
- let(:ignored_columns) { ignored_fk_columns(table) }
+ context 'columns ending with _id' do
+ let(:column_names) { columns.map(&:name) }
+ let(:column_names_with_id) { column_names.select { |column_name| column_name.ends_with?('_id') } }
+ let(:foreign_keys_columns) { all_foreign_keys.map(&:column).uniq } # we can have FK and loose FK present at the same time
+ let(:ignored_columns) { ignored_fk_columns(table) }
- it 'do have the foreign keys' do
- expect(column_names_with_id - ignored_columns).to match_array(foreign_keys_columns)
- end
+ it 'do have the foreign keys' do
+ expect(column_names_with_id - ignored_columns).to match_array(foreign_keys_columns)
+ end
- it 'and having foreign key are not in the ignore list' do
- expect(ignored_columns).to match_array(ignored_columns - foreign_keys)
+ it 'and having foreign key are not in the ignore list' do
+ expect(ignored_columns).to match_array(ignored_columns - foreign_keys)
+ end
end
end
end
@@ -225,7 +242,8 @@ RSpec.describe 'Database schema' do
"Packages::Composer::Metadatum" => %w[composer_json],
"RawUsageData" => %w[payload], # Usage data payload changes often, we cannot use one schema
"Releases::Evidence" => %w[summary],
- "Vulnerabilities::Finding::Evidence" => %w[data] # Validation work in progress
+ "Vulnerabilities::Finding::Evidence" => %w[data], # Validation work in progress
+ "EE::Gitlab::BackgroundMigration::FixSecurityScanStatuses::SecurityScan" => %w[info] # This is a migration model
}.freeze
# We are skipping GEO models for now as it adds up complexity
@@ -275,13 +293,16 @@ RSpec.describe 'Database schema' do
context 'primary keys' do
it 'expects every table to have a primary key defined' do
- connection = ActiveRecord::Base.connection
+ Gitlab::Database::EachDatabase.each_database_connection do |connection, _|
+ schemas_for_connection = Gitlab::Database.gitlab_schemas_for_connection(connection)
- problematic_tables = connection.tables.select do |table|
- !connection.primary_key(table).present?
- end.map(&:to_sym)
+ problematic_tables = connection.tables.select do |table|
+ table_schema = Gitlab::Database::GitlabSchema.table_schema(table)
+ schemas_for_connection.include?(table_schema) && !connection.primary_key(table).present?
+ end.map(&:to_sym)
- expect(problematic_tables).to be_empty
+ expect(problematic_tables).to be_empty
+ end
end
end
diff --git a/spec/factories/achievements/achievements.rb b/spec/factories/achievements/achievements.rb
new file mode 100644
index 00000000000..080a0376999
--- /dev/null
+++ b/spec/factories/achievements/achievements.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :achievement, class: 'Achievements::Achievement' do
+ namespace
+
+ name { generate(:name) }
+ end
+end
diff --git a/spec/factories/bulk_import.rb b/spec/factories/bulk_import.rb
index 748afc0c67c..54d05264269 100644
--- a/spec/factories/bulk_import.rb
+++ b/spec/factories/bulk_import.rb
@@ -5,6 +5,7 @@ FactoryBot.define do
user
source_type { :gitlab }
source_version { BulkImport.min_gl_version_for_project_migration.to_s }
+ source_enterprise { false }
trait :created do
status { 0 }
diff --git a/spec/factories/bulk_import/trackers.rb b/spec/factories/bulk_import/trackers.rb
index 22e0aa096fc..3e69ab26801 100644
--- a/spec/factories/bulk_import/trackers.rb
+++ b/spec/factories/bulk_import/trackers.rb
@@ -7,23 +7,22 @@ FactoryBot.define do
stage { 0 }
has_next_page { false }
sequence(:pipeline_name) { |n| "pipeline_name_#{n}" }
+ sequence(:jid) { |n| "bulk_import_entity_#{n}" }
trait :started do
status { 1 }
-
- sequence(:jid) { |n| "bulk_import_entity_#{n}" }
end
trait :finished do
status { 2 }
-
- sequence(:jid) { |n| "bulk_import_entity_#{n}" }
end
trait :failed do
status { -1 }
+ end
- sequence(:jid) { |n| "bulk_import_entity_#{n}" }
+ trait :skipped do
+ status { -2 }
end
end
end
diff --git a/spec/factories/ci/build_runner_sessions.rb b/spec/factories/ci/build_runner_sessions.rb
new file mode 100644
index 00000000000..f78eaa6a5f1
--- /dev/null
+++ b/spec/factories/ci/build_runner_sessions.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :ci_build_runner_session, class: 'Ci::BuildRunnerSession' do
+ build factory: :ci_build
+ url { 'https://gitlab.example.com' }
+ end
+end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index b88d6b5fda4..15a88955e05 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -7,6 +7,7 @@ FactoryBot.define do
created_at { 'Di 29. Okt 09:50:00 CET 2013' }
scheduling_type { 'stage' }
pending
+ partition_id { pipeline.partition_id }
options do
{
@@ -24,6 +25,8 @@ FactoryBot.define do
project { pipeline.project }
+ ref { pipeline.ref }
+
trait :with_token do
transient do
generate_token { true }
@@ -545,9 +548,12 @@ FactoryBot.define do
options do
{
image: { name: 'image:1.0', entrypoint: '/bin/sh' },
- services: ['postgres', { name: 'docker:stable-dind', entrypoint: '/bin/sh', command: 'sleep 30', alias: 'docker' }, { name: 'mysql:latest', variables: { MYSQL_ROOT_PASSWORD: 'root123.' } }],
+ services: ['postgres',
+ { name: 'docker:stable-dind', entrypoint: '/bin/sh', command: 'sleep 30', alias: 'docker' },
+ { name: 'mysql:latest', variables: { MYSQL_ROOT_PASSWORD: 'root123.' } }],
script: %w(echo),
after_script: %w(ls date),
+ hooks: { pre_get_sources_script: ["echo 'hello pre_get_sources_script'"] },
artifacts: {
name: 'artifacts_file',
untracked: false,
diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb
index 891628a0fc2..eef5c593e0f 100644
--- a/spec/factories/ci/pipelines.rb
+++ b/spec/factories/ci/pipelines.rb
@@ -8,7 +8,7 @@ FactoryBot.define do
sha { 'b83d6e391c22777fca1ed3012fce84f633d7fed0' }
status { 'pending' }
add_attribute(:protected) { false }
- partition_id { 100 }
+ partition_id { Ci::Pipeline.current_partition_value }
project
@@ -54,7 +54,6 @@ FactoryBot.define do
end
factory :ci_pipeline do
- partition_id { 100 }
transient { ci_ref_presence { true } }
before(:create) do |pipeline, evaluator|
@@ -84,6 +83,7 @@ FactoryBot.define do
end
trait :running do
+ started_at { Time.current }
status { :running }
end
diff --git a/spec/factories/ci/resource.rb b/spec/factories/ci/resource.rb
index dec26013a25..946cf9c17a7 100644
--- a/spec/factories/ci/resource.rb
+++ b/spec/factories/ci/resource.rb
@@ -6,6 +6,7 @@ FactoryBot.define do
trait(:retained) do
processable factory: :ci_build
+ partition_id { processable.partition_id }
end
end
end
diff --git a/spec/factories/ci/sources/pipelines.rb b/spec/factories/ci/sources/pipelines.rb
index 93d35097eac..bfe487eb6bb 100644
--- a/spec/factories/ci/sources/pipelines.rb
+++ b/spec/factories/ci/sources/pipelines.rb
@@ -4,8 +4,8 @@ FactoryBot.define do
factory :ci_sources_pipeline, class: 'Ci::Sources::Pipeline' do
after(:build) do |source|
source.project ||= source.pipeline.project
- source.source_pipeline ||= source.source_job.pipeline
- source.source_project ||= source.source_pipeline.project
+ source.source_pipeline ||= source.source_job&.pipeline
+ source.source_project ||= source.source_pipeline&.project
end
source_job factory: :ci_build
diff --git a/spec/factories/ci/unit_test_failure.rb b/spec/factories/ci/unit_test_failures.rb
index 07cd3419754..07cd3419754 100644
--- a/spec/factories/ci/unit_test_failure.rb
+++ b/spec/factories/ci/unit_test_failures.rb
diff --git a/spec/factories/ci/unit_test.rb b/spec/factories/ci/unit_tests.rb
index 480724f260a..480724f260a 100644
--- a/spec/factories/ci/unit_test.rb
+++ b/spec/factories/ci/unit_tests.rb
diff --git a/spec/factories/clusters/agents/group_authorizations.rb b/spec/factories/clusters/agents/group_authorizations.rb
index 6ea3668dc66..abe25794234 100644
--- a/spec/factories/clusters/agents/group_authorizations.rb
+++ b/spec/factories/clusters/agents/group_authorizations.rb
@@ -5,6 +5,14 @@ FactoryBot.define do
association :agent, factory: :cluster_agent
group
- config { { default_namespace: 'production' } }
+ transient do
+ environments { nil }
+ end
+
+ config do
+ { default_namespace: 'production' }.tap do |c|
+ c[:environments] = environments if environments
+ end
+ end
end
end
diff --git a/spec/factories/clusters/agents/project_authorizations.rb b/spec/factories/clusters/agents/project_authorizations.rb
index 176ecc3b517..eecbfe95bfc 100644
--- a/spec/factories/clusters/agents/project_authorizations.rb
+++ b/spec/factories/clusters/agents/project_authorizations.rb
@@ -5,6 +5,14 @@ FactoryBot.define do
association :agent, factory: :cluster_agent
project
- config { { default_namespace: 'production' } }
+ transient do
+ environments { nil }
+ end
+
+ config do
+ { default_namespace: 'production' }.tap do |c|
+ c[:environments] = environments if environments
+ end
+ end
end
end
diff --git a/spec/factories/dependency_proxy.rb b/spec/factories/dependency_proxy.rb
index 33356a701df..43cc923a4c5 100644
--- a/spec/factories/dependency_proxy.rb
+++ b/spec/factories/dependency_proxy.rb
@@ -23,14 +23,21 @@ FactoryBot.define do
factory :dependency_proxy_manifest, class: 'DependencyProxy::Manifest' do
group
size { 1234 }
- file { fixture_file_upload('spec/fixtures/dependency_proxy/manifest') }
digest { 'sha256:d0710affa17fad5f466a70159cc458227bd25d4afb39514ef662ead3e6c99515' }
sequence(:file_name) { |n| "alpine:latest#{n}.json" }
content_type { 'application/vnd.docker.distribution.manifest.v2+json' }
status { :default }
+ after(:build) do |manifest, _evaluator|
+ manifest.file = fixture_file_upload('spec/fixtures/dependency_proxy/manifest')
+ end
+
trait :pending_destruction do
status { :pending_destruction }
end
+
+ trait :remote_store do
+ file_store { DependencyProxy::FileUploader::Store::REMOTE }
+ end
end
end
diff --git a/spec/factories/deploy_tokens.rb b/spec/factories/deploy_tokens.rb
index a2116b738fd..45e92869e22 100644
--- a/spec/factories/deploy_tokens.rb
+++ b/spec/factories/deploy_tokens.rb
@@ -10,7 +10,7 @@ FactoryBot.define do
read_package_registry { false }
write_package_registry { false }
revoked { false }
- expires_at { 5.days.from_now }
+ expires_at { 5.days.from_now.to_datetime }
deploy_token_type { DeployToken.deploy_token_types[:project_type] }
trait :revoked do
diff --git a/spec/factories/events.rb b/spec/factories/events.rb
index a4f06a48621..0f564afe822 100644
--- a/spec/factories/events.rb
+++ b/spec/factories/events.rb
@@ -64,6 +64,11 @@ FactoryBot.define do
target_type { 'WorkItem' }
end
+ trait :for_merge_request do
+ target { association(:merge_request) }
+ target_type { 'MergeRequest' }
+ end
+
factory :design_event, traits: [:has_design] do
action { :created }
target { design }
diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb
index 6f9cf0ef895..f4d47b9ff8c 100644
--- a/spec/factories/groups.rb
+++ b/spec/factories/groups.rb
@@ -118,5 +118,9 @@ FactoryBot.define do
create(:crm_settings, group: group, enabled: true)
end
end
+
+ trait :with_root_storage_statistics do
+ association :root_storage_statistics, factory: :namespace_root_storage_statistics
+ end
end
end
diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb
index 88522737e06..70a4a3ec822 100644
--- a/spec/factories/issues.rb
+++ b/spec/factories/issues.rb
@@ -4,6 +4,7 @@ FactoryBot.define do
factory :issue, traits: [:has_internal_id] do
title { generate(:title) }
project
+ namespace { project.project_namespace }
author { project.creator }
updated_by { author }
relative_position { RelativePositioning::START_POSITION }
@@ -70,6 +71,16 @@ FactoryBot.define do
association :work_item_type, :default, :task
end
+ trait :objective do
+ issue_type { :objective }
+ association :work_item_type, :default, :objective
+ end
+
+ trait :key_result do
+ issue_type { :key_result }
+ association :work_item_type, :default, :key_result
+ end
+
factory :incident do
issue_type { :incident }
association :work_item_type, :default, :incident
diff --git a/spec/factories/ml/candidate_metadata.rb b/spec/factories/ml/candidate_metadata.rb
new file mode 100644
index 00000000000..e941ae4deb8
--- /dev/null
+++ b/spec/factories/ml/candidate_metadata.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :ml_candidate_metadata, class: '::Ml::CandidateMetadata' do
+ association :candidate, factory: :ml_candidates
+
+ sequence(:name) { |n| "metadata_#{n}" }
+ sequence(:value) { |n| "value#{n}" }
+ end
+end
diff --git a/spec/factories/ml/candidates.rb b/spec/factories/ml/candidates.rb
index 4fbcdc46103..2daed36d777 100644
--- a/spec/factories/ml/candidates.rb
+++ b/spec/factories/ml/candidates.rb
@@ -10,5 +10,11 @@ FactoryBot.define do
candidate.params = FactoryBot.create_list(:ml_candidate_params, 2, candidate: candidate )
end
end
+
+ trait :with_metadata do
+ after(:create) do |candidate|
+ candidate.metadata = FactoryBot.create_list(:ml_candidate_metadata, 2, candidate: candidate )
+ end
+ end
end
end
diff --git a/spec/factories/ml/experiment_metadata.rb b/spec/factories/ml/experiment_metadata.rb
new file mode 100644
index 00000000000..d3ece9630a4
--- /dev/null
+++ b/spec/factories/ml/experiment_metadata.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :ml_experiment_metadata, class: '::Ml::ExperimentMetadata' do
+ association :experiment, factory: :ml_experiments
+
+ sequence(:name) { |n| "metadata_#{n}" }
+ sequence(:value) { |n| "value#{n}" }
+ end
+end
diff --git a/spec/factories/ml/experiments.rb b/spec/factories/ml/experiments.rb
index e4f5a0da6cf..0acb4c5c5fc 100644
--- a/spec/factories/ml/experiments.rb
+++ b/spec/factories/ml/experiments.rb
@@ -4,6 +4,12 @@ FactoryBot.define do
sequence(:name) { |n| "experiment#{n}" }
project
- user
+ user { project&.creator }
+
+ trait :with_metadata do
+ after(:create) do |e|
+ e.metadata = FactoryBot.create_list(:ml_experiment_metadata, 2, experiment: e) # rubocop:disable StrategyInCallback
+ end
+ end
end
end
diff --git a/spec/factories/packages/rpm/rpm_repository_files.rb b/spec/factories/packages/rpm/rpm_repository_files.rb
index 00755f49d98..7b86c593627 100644
--- a/spec/factories/packages/rpm/rpm_repository_files.rb
+++ b/spec/factories/packages/rpm/rpm_repository_files.rb
@@ -34,5 +34,9 @@ FactoryBot.define do
trait :pending_destruction do
status { :pending_destruction }
end
+
+ trait :filelists do
+ file_name { 'filelists.xml' }
+ end
end
end
diff --git a/spec/factories/project_export_jobs.rb b/spec/factories/project_export_jobs.rb
index b2666555ea8..bf8cfd863ec 100644
--- a/spec/factories/project_export_jobs.rb
+++ b/spec/factories/project_export_jobs.rb
@@ -4,5 +4,21 @@ FactoryBot.define do
factory :project_export_job do
project
jid { SecureRandom.hex(8) }
+
+ trait :queued do
+ status { ProjectExportJob::STATUS[:queued] }
+ end
+
+ trait :started do
+ status { ProjectExportJob::STATUS[:started] }
+ end
+
+ trait :finished do
+ status { ProjectExportJob::STATUS[:finished] }
+ end
+
+ trait :failed do
+ status { ProjectExportJob::STATUS[:failed] }
+ end
end
end
diff --git a/spec/factories/projects/ci_feature_usages.rb b/spec/factories/projects/ci_feature_usages.rb
index 1ab1d82ef4b..48e5331afcc 100644
--- a/spec/factories/projects/ci_feature_usages.rb
+++ b/spec/factories/projects/ci_feature_usages.rb
@@ -4,6 +4,7 @@ FactoryBot.define do
factory :project_ci_feature_usage, class: 'Projects::CiFeatureUsage' do
project factory: :project
feature { :code_coverage } # rubocop: disable RSpec/EmptyExampleGroup
+
default_branch { false }
end
end
diff --git a/spec/factories/projects/import_export/relation_export_upload.rb b/spec/factories/projects/import_export/relation_export_upload.rb
index eaa57d6ee59..4bd6a586720 100644
--- a/spec/factories/projects/import_export/relation_export_upload.rb
+++ b/spec/factories/projects/import_export/relation_export_upload.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
FactoryBot.define do
- factory :project_relation_export_upload, class: 'Projects::ImportExport::RelationExportUpload' do
+ factory :relation_export_upload, class: 'Projects::ImportExport::RelationExportUpload' do
relation_export factory: :project_relation_export
export_file { fixture_file_upload("spec/fixtures/gitlab/import_export/labels.tar.gz") }
end
diff --git a/spec/factories/resource_milestone_event.rb b/spec/factories/resource_milestone_events.rb
index a3944e013da..a3944e013da 100644
--- a/spec/factories/resource_milestone_event.rb
+++ b/spec/factories/resource_milestone_events.rb
diff --git a/spec/factories/resource_state_event.rb b/spec/factories/resource_state_events.rb
index 926c6dd8cbc..926c6dd8cbc 100644
--- a/spec/factories/resource_state_event.rb
+++ b/spec/factories/resource_state_events.rb
diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb
index 97a1265c46a..760367539fc 100644
--- a/spec/factories/todos.rb
+++ b/spec/factories/todos.rb
@@ -41,6 +41,10 @@ FactoryBot.define do
action { Todo::UNMERGEABLE }
end
+ trait :member_access_requested do
+ action { Todo::MEMBER_ACCESS_REQUESTED }
+ end
+
trait :pending do
state { :pending }
end
diff --git a/spec/factories/work_items.rb b/spec/factories/work_items.rb
index 205b071a5d4..cff246d4071 100644
--- a/spec/factories/work_items.rb
+++ b/spec/factories/work_items.rb
@@ -27,5 +27,15 @@ FactoryBot.define do
trait :last_edited_by_user do
association :last_edited_by, factory: :user
end
+
+ trait :objective do
+ issue_type { :objective }
+ association :work_item_type, :default, :objective
+ end
+
+ trait :key_result do
+ issue_type { :key_result }
+ association :work_item_type, :default, :key_result
+ end
end
end
diff --git a/spec/factories/work_items/hierarchy_restrictions.rb b/spec/factories/work_items/hierarchy_restrictions.rb
new file mode 100644
index 00000000000..09a10b633ba
--- /dev/null
+++ b/spec/factories/work_items/hierarchy_restrictions.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :hierarchy_restriction, class: 'WorkItems::HierarchyRestriction' do
+ parent_type { association :work_item_type, :default }
+ child_type { association :work_item_type, :default }
+ end
+end
diff --git a/spec/factories/work_items/work_item_types.rb b/spec/factories/work_items/work_item_types.rb
index 1b6137503d3..d36cb6260c6 100644
--- a/spec/factories/work_items/work_item_types.rb
+++ b/spec/factories/work_items/work_item_types.rb
@@ -13,7 +13,7 @@ FactoryBot.define do
# Expect base_types to exist on the DB
if type_base_attributes.slice(:namespace, :namespace_id).compact.empty?
- WorkItems::Type.find_or_initialize_by(type_base_attributes).tap { |type| type.assign_attributes(attributes) }
+ WorkItems::Type.find_or_initialize_by(type_base_attributes)
else
WorkItems::Type.new(attributes)
end
diff --git a/spec/features/abuse_report_spec.rb b/spec/features/abuse_report_spec.rb
index 5fdd0816006..fdd11b59938 100644
--- a/spec/features/abuse_report_spec.rb
+++ b/spec/features/abuse_report_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Abuse reports' do
+RSpec.describe 'Abuse reports', feature_category: :not_owned do
let(:another_user) { create(:user) }
before do
diff --git a/spec/features/action_cable_logging_spec.rb b/spec/features/action_cable_logging_spec.rb
index cf20b204cc5..c02a41c4c59 100644
--- a/spec/features/action_cable_logging_spec.rb
+++ b/spec/features/action_cable_logging_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'ActionCable logging', :js do
+RSpec.describe 'ActionCable logging', :js, feature_category: :not_owned do
let_it_be(:project) { create(:project, :public) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:user) { create(:user) }
diff --git a/spec/features/admin/admin_abuse_reports_spec.rb b/spec/features/admin/admin_abuse_reports_spec.rb
index 3a02ce89aa9..10f12d7116f 100644
--- a/spec/features/admin/admin_abuse_reports_spec.rb
+++ b/spec/features/admin/admin_abuse_reports_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Admin::AbuseReports", :js do
+RSpec.describe "Admin::AbuseReports", :js, feature_category: :not_owned do
let(:user) { create(:user) }
context 'as an admin' do
diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb
index b297d92b2fa..5fbe7039c1d 100644
--- a/spec/features/admin/admin_appearance_spec.rb
+++ b/spec/features/admin/admin_appearance_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin Appearance' do
+RSpec.describe 'Admin Appearance', feature_category: :not_owned do
let!(:appearance) { create(:appearance) }
let(:admin) { create(:admin) }
diff --git a/spec/features/admin/admin_broadcast_messages_spec.rb b/spec/features/admin/admin_broadcast_messages_spec.rb
index b5416f539f1..a6bbdd70fc3 100644
--- a/spec/features/admin/admin_broadcast_messages_spec.rb
+++ b/spec/features/admin/admin_broadcast_messages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin Broadcast Messages' do
+RSpec.describe 'Admin Broadcast Messages', feature_category: :onboarding do
before do
admin = create(:admin)
sign_in(admin)
diff --git a/spec/features/admin/admin_browse_spam_logs_spec.rb b/spec/features/admin/admin_browse_spam_logs_spec.rb
index 471a7e8f0ab..461c9d08273 100644
--- a/spec/features/admin/admin_browse_spam_logs_spec.rb
+++ b/spec/features/admin/admin_browse_spam_logs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin browse spam logs' do
+RSpec.describe 'Admin browse spam logs', feature_category: :not_owned do
let!(:spam_log) { create(:spam_log, description: 'abcde ' * 20) }
before do
@@ -23,4 +23,11 @@ RSpec.describe 'Admin browse spam logs' do
expect(page).to have_link('Remove user')
expect(page).to have_link('Block user')
end
+
+ it 'does not perform N+1 queries' do
+ control_queries = ActiveRecord::QueryRecorder.new { visit admin_spam_logs_path }
+ create(:spam_log)
+
+ expect { visit admin_spam_logs_path }.not_to exceed_query_limit(control_queries)
+ end
end
diff --git a/spec/features/admin/admin_deploy_keys_spec.rb b/spec/features/admin/admin_deploy_keys_spec.rb
index 56b8c7fce14..e55e1cce6b9 100644
--- a/spec/features/admin/admin_deploy_keys_spec.rb
+++ b/spec/features/admin/admin_deploy_keys_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'admin deploy keys', :js do
+RSpec.describe 'admin deploy keys', :js, feature_category: :authentication_and_authorization do
include Spec::Support::Helpers::ModalHelpers
let_it_be(:admin) { create(:admin) }
diff --git a/spec/features/admin/admin_dev_ops_reports_spec.rb b/spec/features/admin/admin_dev_ops_reports_spec.rb
index f65862c568f..f290464b043 100644
--- a/spec/features/admin/admin_dev_ops_reports_spec.rb
+++ b/spec/features/admin/admin_dev_ops_reports_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'DevOps Report page', :js do
+RSpec.describe 'DevOps Report page', :js, feature_category: :devops_reports do
before do
admin = create(:admin)
sign_in(admin)
diff --git a/spec/features/admin/admin_disables_git_access_protocol_spec.rb b/spec/features/admin/admin_disables_git_access_protocol_spec.rb
index b370b779afe..76620b93557 100644
--- a/spec/features/admin/admin_disables_git_access_protocol_spec.rb
+++ b/spec/features/admin/admin_disables_git_access_protocol_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin disables Git access protocol', :js do
+RSpec.describe 'Admin disables Git access protocol', :js, feature_category: :source_code_management do
include StubENV
include MobileHelpers
diff --git a/spec/features/admin/admin_disables_two_factor_spec.rb b/spec/features/admin/admin_disables_two_factor_spec.rb
index 4463dbb1eb0..eed20d449cd 100644
--- a/spec/features/admin/admin_disables_two_factor_spec.rb
+++ b/spec/features/admin/admin_disables_two_factor_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin disables 2FA for a user' do
+RSpec.describe 'Admin disables 2FA for a user', feature_category: :system_access do
include Spec::Support::Helpers::ModalHelpers
it 'successfully', :js do
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index 657dd52228e..c36a742af6b 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin Groups' do
+RSpec.describe 'Admin Groups', feature_category: :subgroups do
include Select2Helper
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::Features::InviteMembersModalHelper
diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb
index 0f6cba6c105..de71a48d2dc 100644
--- a/spec/features/admin/admin_health_check_spec.rb
+++ b/spec/features/admin/admin_health_check_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Admin Health Check", :feature do
+RSpec.describe "Admin Health Check", feature_category: :continuous_verification do
include StubENV
let_it_be(:admin) { create(:admin) }
diff --git a/spec/features/admin/admin_hook_logs_spec.rb b/spec/features/admin/admin_hook_logs_spec.rb
index a2ee6343886..d6507e68692 100644
--- a/spec/features/admin/admin_hook_logs_spec.rb
+++ b/spec/features/admin/admin_hook_logs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin::HookLogs' do
+RSpec.describe 'Admin::HookLogs', feature_category: :continuous_verification do
let_it_be(:system_hook) { create(:system_hook) }
let_it_be(:hook_log) { create(:web_hook_log, web_hook: system_hook, internal_error_message: 'some error') }
let_it_be(:admin) { create(:admin) }
diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb
index dc5b0ae009e..e6630e40147 100644
--- a/spec/features/admin/admin_hooks_spec.rb
+++ b/spec/features/admin/admin_hooks_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin::Hooks' do
+RSpec.describe 'Admin::Hooks', feature_category: :integrations do
include Spec::Support::Helpers::ModalHelpers
let_it_be(:user) { create(:admin) }
@@ -58,10 +58,7 @@ RSpec.describe 'Admin::Hooks' do
describe 'Update existing hook' do
let(:new_url) { generate(:url) }
-
- before do
- create(:system_hook)
- end
+ let_it_be(:hook) { create(:system_hook) }
it 'updates existing hook' do
visit admin_hooks_path
@@ -71,9 +68,9 @@ RSpec.describe 'Admin::Hooks' do
check 'Enable SSL verification'
click_button 'Save changes'
- expect(page).to have_content 'SSL Verification: enabled'
- expect(page).to have_current_path(admin_hooks_path, ignore_query: true)
- expect(page).to have_content(new_url)
+ expect(page).to have_content('Enable SSL verification')
+ expect(page).to have_current_path(edit_admin_hook_path(hook), ignore_query: true)
+ expect(page).to have_content('Recent events')
end
end
@@ -145,7 +142,7 @@ RSpec.describe 'Admin::Hooks' do
visit admin_hooks_path
find('.hook-test-button.dropdown').click
- click_link 'Merge requests events'
+ click_link 'Merge request events'
expect(page).to have_content 'Hook executed successfully'
end
diff --git a/spec/features/admin/admin_jobs_spec.rb b/spec/features/admin/admin_jobs_spec.rb
index 36822f89c12..f0eaa83f05e 100644
--- a/spec/features/admin/admin_jobs_spec.rb
+++ b/spec/features/admin/admin_jobs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin Jobs' do
+RSpec.describe 'Admin Jobs', feature_category: :continuous_integration do
before do
admin = create(:admin)
sign_in(admin)
diff --git a/spec/features/admin/admin_labels_spec.rb b/spec/features/admin/admin_labels_spec.rb
index fa5c94aa66e..8d2813d26f7 100644
--- a/spec/features/admin/admin_labels_spec.rb
+++ b/spec/features/admin/admin_labels_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'admin issues labels' do
+RSpec.describe 'admin issues labels', feature_category: :team_planning do
include Spec::Support::Helpers::ModalHelpers
let!(:bug_label) { Label.create!(title: 'bug', template: true) }
diff --git a/spec/features/admin/admin_manage_applications_spec.rb b/spec/features/admin/admin_manage_applications_spec.rb
index 4cf290293bd..b4c77e802a8 100644
--- a/spec/features/admin/admin_manage_applications_spec.rb
+++ b/spec/features/admin/admin_manage_applications_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'admin manage applications' do
+RSpec.describe 'admin manage applications', feature_category: :system_access do
let_it_be(:new_application_path) { new_admin_application_path }
let_it_be(:applications_path) { admin_applications_path }
let_it_be(:index_path) { admin_applications_path }
diff --git a/spec/features/admin/admin_mode/login_spec.rb b/spec/features/admin/admin_mode/login_spec.rb
index 6b4c9adb096..393721fe451 100644
--- a/spec/features/admin/admin_mode/login_spec.rb
+++ b/spec/features/admin/admin_mode/login_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin Mode Login' do
+RSpec.describe 'Admin Mode Login', feature_category: :authentication_and_authorization do
include TermsHelper
include UserLoginHelper
include LdapHelpers
diff --git a/spec/features/admin/admin_mode/logout_spec.rb b/spec/features/admin/admin_mode/logout_spec.rb
index 3ca66ef0d6a..f4e8941d25a 100644
--- a/spec/features/admin/admin_mode/logout_spec.rb
+++ b/spec/features/admin/admin_mode/logout_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin Mode Logout', :js do
+RSpec.describe 'Admin Mode Logout', :js, feature_category: :authentication_and_authorization do
include TermsHelper
include UserLoginHelper
include Spec::Support::Helpers::Features::TopNavSpecHelpers
diff --git a/spec/features/admin/admin_mode/workers_spec.rb b/spec/features/admin/admin_mode/workers_spec.rb
index 8405e9132b6..f3639fd0800 100644
--- a/spec/features/admin/admin_mode/workers_spec.rb
+++ b/spec/features/admin/admin_mode/workers_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
# Test an operation that triggers background jobs requiring administrative rights
-RSpec.describe 'Admin mode for workers', :request_store do
+RSpec.describe 'Admin mode for workers', :request_store, feature_category: :authentication_and_authorization do
include Spec::Support::Helpers::Features::AdminUsersHelpers
let(:user) { create(:user) }
diff --git a/spec/features/admin/admin_mode_spec.rb b/spec/features/admin/admin_mode_spec.rb
index 33cf0e8c4f8..769ff75b5a2 100644
--- a/spec/features/admin/admin_mode_spec.rb
+++ b/spec/features/admin/admin_mode_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin mode', :js do
+RSpec.describe 'Admin mode', :js, feature_category: :not_owned do
include MobileHelpers
include Spec::Support::Helpers::Features::TopNavSpecHelpers
include StubENV
diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb
index 6b147b01991..0cb813c40f4 100644
--- a/spec/features/admin/admin_projects_spec.rb
+++ b/spec/features/admin/admin_projects_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Admin::Projects" do
+RSpec.describe "Admin::Projects", feature_category: :projects do
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::Features::InviteMembersModalHelper
include Spec::Support::Helpers::ModalHelpers
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index 92a3b388994..30fd04b1c3e 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Admin Runners" do
+RSpec.describe "Admin Runners", feature_category: :runner_fleet do
include Spec::Support::Helpers::Features::RunnersHelpers
include Spec::Support::Helpers::ModalHelpers
@@ -90,6 +90,22 @@ RSpec.describe "Admin Runners" do
end
end
+ it 'shows a running status badge that links to jobs tab' do
+ runner = create(:ci_runner, :project, projects: [project])
+ job = create(:ci_build, :running, runner: runner)
+
+ visit admin_runners_path
+
+ within_runner_row(runner.id) do
+ click_on(s_('Runners|Running'))
+ end
+
+ expect(current_url).to match(admin_runner_path(runner))
+
+ expect(find("[data-testid='td-status']")).to have_content "running"
+ expect(find("[data-testid='td-job']")).to have_content "##{job.id}"
+ end
+
describe 'search' do
before_all do
create(:ci_runner, :instance, description: 'runner-foo')
diff --git a/spec/features/admin/admin_search_settings_spec.rb b/spec/features/admin/admin_search_settings_spec.rb
index 989cb7cc787..3254bf75738 100644
--- a/spec/features/admin/admin_search_settings_spec.rb
+++ b/spec/features/admin/admin_search_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin searches application settings', :js do
+RSpec.describe 'Admin searches application settings', :js, feature_category: :global_search do
let_it_be(:admin) { create(:admin) }
let_it_be(:application_settings) { create(:application_setting) }
diff --git a/spec/features/admin/admin_sees_background_migrations_spec.rb b/spec/features/admin/admin_sees_background_migrations_spec.rb
index d72259d91b3..e1746dad196 100644
--- a/spec/features/admin/admin_sees_background_migrations_spec.rb
+++ b/spec/features/admin/admin_sees_background_migrations_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Admin > Admin sees background migrations" do
+RSpec.describe "Admin > Admin sees background migrations", feature_category: :database do
let_it_be(:admin) { create(:admin) }
let(:job_class) { Gitlab::BackgroundMigration::CopyColumnUsingBackgroundMigrationJob }
diff --git a/spec/features/admin/admin_sees_project_statistics_spec.rb b/spec/features/admin/admin_sees_project_statistics_spec.rb
index 9d9217c4574..d3d0625ac43 100644
--- a/spec/features/admin/admin_sees_project_statistics_spec.rb
+++ b/spec/features/admin/admin_sees_project_statistics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Admin > Admin sees project statistics" do
+RSpec.describe "Admin > Admin sees project statistics", feature_category: :projects do
let(:current_user) { create(:admin) }
before do
diff --git a/spec/features/admin/admin_sees_projects_statistics_spec.rb b/spec/features/admin/admin_sees_projects_statistics_spec.rb
index d340eb47f34..82361a985ae 100644
--- a/spec/features/admin/admin_sees_projects_statistics_spec.rb
+++ b/spec/features/admin/admin_sees_projects_statistics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Admin > Admin sees projects statistics" do
+RSpec.describe "Admin > Admin sees projects statistics", feature_category: :projects do
let(:current_user) { create(:admin) }
before do
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 72c9053ba49..2ac86ab9f49 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin updates settings' do
+RSpec.describe 'Admin updates settings', feature_category: :not_owned do
include StubENV
include TermsHelper
include UsageDataHelpers
@@ -71,11 +71,19 @@ RSpec.describe 'Admin updates settings' do
it 'change Visibility and Access Controls' do
page.within('.as-visibility-access') do
- uncheck 'Enabled'
+ page.within('[data-testid="project-export"]') do
+ uncheck 'Enabled'
+ end
+
+ page.within('[data-testid="bulk-import"]') do
+ check 'Enabled'
+ end
+
click_button 'Save changes'
end
expect(current_settings.project_export_enabled).to be_falsey
+ expect(current_settings.bulk_import_enabled).to be(true)
expect(page).to have_content "Application settings saved successfully"
end
diff --git a/spec/features/admin/admin_system_info_spec.rb b/spec/features/admin/admin_system_info_spec.rb
index 8ff31dfded7..6c4a316ae77 100644
--- a/spec/features/admin/admin_system_info_spec.rb
+++ b/spec/features/admin/admin_system_info_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin System Info' do
+RSpec.describe 'Admin System Info', feature_category: :not_owned do
before do
admin = create(:admin)
sign_in(admin)
diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
index d93dac4834e..5e6cc206883 100644
--- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb
+++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin > Users > Impersonation Tokens', :js do
+RSpec.describe 'Admin > Users > Impersonation Tokens', :js, feature_category: :authentication_and_authorization do
include Spec::Support::Helpers::ModalHelpers
include Spec::Support::Helpers::AccessTokenHelpers
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index f4b7fa45e4f..1f40f1f1bce 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Admin::Users" do
+RSpec.describe "Admin::Users", feature_category: :user_management do
let(:current_user) { create(:admin) }
before do
diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb
index 2dffef93600..318572a7664 100644
--- a/spec/features/admin/admin_uses_repository_checks_spec.rb
+++ b/spec/features/admin/admin_uses_repository_checks_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin uses repository checks', :request_store do
+RSpec.describe 'Admin uses repository checks', :request_store, feature_category: :user_management do
include StubENV
include Spec::Support::Helpers::ModalHelpers
diff --git a/spec/features/admin/dashboard_spec.rb b/spec/features/admin/dashboard_spec.rb
index e7ff8c23a8c..baca60134b9 100644
--- a/spec/features/admin/dashboard_spec.rb
+++ b/spec/features/admin/dashboard_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe 'admin visits dashboard' do
gitlab_enable_admin_mode_sign_in(admin)
end
- context 'counting forks', :js do
+ context 'counting forks', :js, feature_category: :source_code_management do
it 'correctly counts 2 forks of a project' do
project = create(:project)
project_fork = fork_project(project)
@@ -28,7 +28,7 @@ RSpec.describe 'admin visits dashboard' do
end
end
- describe 'Users statistic' do
+ describe 'Users statistic', feature_category: :user_management do
let_it_be(:users_statistics) { create(:users_statistics) }
it 'shows correct amounts of users', :aggregate_failures do
@@ -54,7 +54,7 @@ RSpec.describe 'admin visits dashboard' do
end
end
- describe 'Version check', :js do
+ describe 'Version check', :js, feature_category: :deployment_management do
it 'shows badge on CE' do
visit admin_root_path
diff --git a/spec/features/admin/integrations/instance_integrations_spec.rb b/spec/features/admin/integrations/instance_integrations_spec.rb
index 7b326ec161c..3b2ed1d9810 100644
--- a/spec/features/admin/integrations/instance_integrations_spec.rb
+++ b/spec/features/admin/integrations/instance_integrations_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Instance integrations', :js do
+RSpec.describe 'Instance integrations', :js, feature_category: :integrations do
include_context 'instance integration activation'
it_behaves_like 'integration settings form' do
diff --git a/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb b/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb
index 22a27b33671..d0ca5d76cc7 100644
--- a/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb
+++ b/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'User activates the instance-level Mattermost Slash Command integration', :js do
+RSpec.describe 'User activates the instance-level Mattermost Slash Command integration', :js,
+feature_category: :integrations do
include_context 'instance integration activation'
before do
diff --git a/spec/features/admin/users/user_spec.rb b/spec/features/admin/users/user_spec.rb
index 35b5c755b66..1552d4e6187 100644
--- a/spec/features/admin/users/user_spec.rb
+++ b/spec/features/admin/users/user_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin::Users::User' do
+RSpec.describe 'Admin::Users::User', feature_category: :user_management do
include Spec::Support::Helpers::Features::AdminUsersHelpers
include Spec::Support::Helpers::ModalHelpers
diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb
index 9c59f0226e0..4b49e8f4bc6 100644
--- a/spec/features/admin/users/users_spec.rb
+++ b/spec/features/admin/users/users_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin::Users' do
+RSpec.describe 'Admin::Users', feature_category: :user_management do
include Spec::Support::Helpers::Features::AdminUsersHelpers
include Spec::Support::Helpers::ModalHelpers
@@ -604,8 +604,8 @@ RSpec.describe 'Admin::Users' do
def sort_by(option)
page.within('.filtered-search-block') do
- find('.gl-new-dropdown').click
- find('.gl-new-dropdown-item', text: option).click
+ find('.gl-dropdown').click
+ find('.gl-dropdown-item', text: option).click
end
end
end
diff --git a/spec/features/admin_variables_spec.rb b/spec/features/admin_variables_spec.rb
index 9ec22bbe948..d1adbf59984 100644
--- a/spec/features/admin_variables_spec.rb
+++ b/spec/features/admin_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Instance variables', :js do
+RSpec.describe 'Instance variables', :js, feature_category: :pipeline_authoring do
let(:admin) { create(:admin) }
let(:page_path) { ci_cd_admin_application_settings_path }
diff --git a/spec/features/alert_management/alert_details_spec.rb b/spec/features/alert_management/alert_details_spec.rb
index 579b8221041..45fa4d810aa 100644
--- a/spec/features/alert_management/alert_details_spec.rb
+++ b/spec/features/alert_management/alert_details_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Alert details', :js do
+RSpec.describe 'Alert details', :js, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
let_it_be(:alert) { create(:alert_management_alert, project: project, status: 'triggered', title: 'Alert') }
@@ -30,6 +30,8 @@ RSpec.describe 'Alert details', :js do
alert_tabs = find('[data-testid="alertDetailsTabs"]')
expect(alert_tabs).to have_content('Alert details')
+ expect(alert_tabs).to have_content('Metrics')
+ expect(alert_tabs).to have_content('Activity feed')
end
end
@@ -61,7 +63,7 @@ RSpec.describe 'Alert details', :js do
expect(alert_status).to have_content('Triggered')
find('.gl-button').click
- find('.gl-new-dropdown-item', text: 'Acknowledged').click
+ find('.gl-dropdown-item', text: 'Acknowledged').click
wait_for_requests
diff --git a/spec/features/alert_management/alert_management_list_spec.rb b/spec/features/alert_management/alert_management_list_spec.rb
index 2fbce27033e..6ed3bdec5f5 100644
--- a/spec/features/alert_management/alert_management_list_spec.rb
+++ b/spec/features/alert_management/alert_management_list_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Alert Management index', :js do
+RSpec.describe 'Alert Management index', :js, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
diff --git a/spec/features/alert_management/user_filters_alerts_by_status_spec.rb b/spec/features/alert_management/user_filters_alerts_by_status_spec.rb
index bebbbcbf5f7..c3dab05550e 100644
--- a/spec/features/alert_management/user_filters_alerts_by_status_spec.rb
+++ b/spec/features/alert_management/user_filters_alerts_by_status_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User filters Alert Management table by status', :js do
+RSpec.describe 'User filters Alert Management table by status', :js, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
let_it_be(:alert1, reload: true) { create(:alert_management_alert, :triggered, project: project) }
diff --git a/spec/features/alert_management/user_searches_alerts_spec.rb b/spec/features/alert_management/user_searches_alerts_spec.rb
index 3bb1b260f36..d1e400f4145 100644
--- a/spec/features/alert_management/user_searches_alerts_spec.rb
+++ b/spec/features/alert_management/user_searches_alerts_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches Alert Management alerts', :js do
+RSpec.describe 'User searches Alert Management alerts', :js, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
let_it_be(:alert) { create(:alert_management_alert, project: project, status: 'triggered') }
diff --git a/spec/features/alert_management/user_updates_alert_status_spec.rb b/spec/features/alert_management/user_updates_alert_status_spec.rb
index 2d7be3a0022..98fd7449c4f 100644
--- a/spec/features/alert_management/user_updates_alert_status_spec.rb
+++ b/spec/features/alert_management/user_updates_alert_status_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User updates Alert Management status', :js do
+RSpec.describe 'User updates Alert Management status', :js, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
let_it_be(:alert) { create(:alert_management_alert, project: project, status: 'triggered') }
diff --git a/spec/features/alert_management_spec.rb b/spec/features/alert_management_spec.rb
index 3322c9c574f..de6b385b4cd 100644
--- a/spec/features/alert_management_spec.rb
+++ b/spec/features/alert_management_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Alert management', :js do
+RSpec.describe 'Alert management', :js, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
diff --git a/spec/features/alerts_settings/user_views_alerts_settings_spec.rb b/spec/features/alerts_settings/user_views_alerts_settings_spec.rb
index 60f2f776595..70223b2c0d4 100644
--- a/spec/features/alerts_settings/user_views_alerts_settings_spec.rb
+++ b/spec/features/alerts_settings/user_views_alerts_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Alert integrations settings form', :js do
+RSpec.describe 'Alert integrations settings form', :js, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:developer) { create(:user) }
diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb
index 855c91f70d7..4e204224773 100644
--- a/spec/features/atom/dashboard_issues_spec.rb
+++ b/spec/features/atom/dashboard_issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Dashboard Issues Feed" do
+RSpec.describe "Dashboard Issues Feed", feature_category: :devops_reports do
describe "GET /issues" do
let!(:user) do
user = create(:user, email: 'private1@example.com')
diff --git a/spec/features/atom/dashboard_spec.rb b/spec/features/atom/dashboard_spec.rb
index 851ae7b02a0..2e9005712bb 100644
--- a/spec/features/atom/dashboard_spec.rb
+++ b/spec/features/atom/dashboard_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Dashboard Feed" do
+RSpec.describe "Dashboard Feed", feature_category: :devops_reports do
describe "GET /" do
let!(:user) { create(:user, name: "Jonh") }
diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb
index 913f5a7bcf3..89db70c6680 100644
--- a/spec/features/atom/issues_spec.rb
+++ b/spec/features/atom/issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issues Feed' do
+RSpec.describe 'Issues Feed', feature_category: :devops_reports do
describe 'GET /issues' do
let_it_be_with_reload(:user) do
user = create(:user, email: 'private1@example.com')
diff --git a/spec/features/atom/merge_requests_spec.rb b/spec/features/atom/merge_requests_spec.rb
index 48db8fcbf1e..b9e1c7042b2 100644
--- a/spec/features/atom/merge_requests_spec.rb
+++ b/spec/features/atom/merge_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge Requests Feed' do
+RSpec.describe 'Merge Requests Feed', feature_category: :devops_reports do
describe 'GET /merge_requests' do
let_it_be_with_reload(:user) { create(:user, email: 'private1@example.com') }
let_it_be(:assignee) { create(:user, email: 'private2@example.com') }
diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb
index ab874408e55..b743f900ae7 100644
--- a/spec/features/atom/users_spec.rb
+++ b/spec/features/atom/users_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "User Feed" do
+RSpec.describe "User Feed", feature_category: :devops_reports do
describe "GET /" do
let!(:user) { create(:user) }
diff --git a/spec/features/boards/board_filters_spec.rb b/spec/features/boards/board_filters_spec.rb
index eab92de7e8a..dee63be8119 100644
--- a/spec/features/boards/board_filters_spec.rb
+++ b/spec/features/boards/board_filters_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issue board filters', :js do
+RSpec.describe 'Issue board filters', :js, feature_category: :team_planning do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
let_it_be(:board) { create(:board, project: project) }
@@ -191,7 +191,7 @@ RSpec.describe 'Issue board filters', :js do
end
def expect_filtered_search_dropdown_results(filter_dropdown, count)
- expect(filter_dropdown).to have_selector('.gl-new-dropdown-item', count: count)
+ expect(filter_dropdown).to have_selector('.gl-dropdown-item', count: count)
end
def visit_project_board
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index fee9b5b378e..3e2e391d060 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -2,16 +2,31 @@
require 'spec_helper'
-RSpec.describe 'Project issue boards', :js do
+# Flaky spec warning: the queries in this file routinely exceed the defined GraphQL query limit of 100.
+# Until those queries are optimized, we need to disable query limit checking in order for these tests
+# to pass consistently. Note that removing the disabling code can lead to flaky failures locally and in CI.
+#
+# In addition, it seems as though the use of `let_it_be` might be causing some of the
+# flakiness, as discussed in https://github.com/test-prof/test-prof/blob/master/docs/recipes/let_it_be.md#modifiers.
+# `reload: true` has been added to all `let_it_be` statements.
+#
+# See:
+# - https://gitlab.com/gitlab-org/gitlab/-/issues/323426
+# - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56458#note_535900110
+# - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102719
+# - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105849
+# - https://gitlab.com/gitlab-org/gitlab/-/issues/383970
+#
+RSpec.describe 'Project issue boards', :js, feature_category: :team_planning do
include DragTo
include MobileHelpers
include BoardHelpers
- let_it_be(:group) { create(:group, :nested) }
- let_it_be(:project) { create(:project, :public, namespace: group) }
- let_it_be(:board) { create(:board, project: project) }
- let_it_be(:user) { create(:user) }
- let_it_be(:user2) { create(:user) }
+ let_it_be(:group, reload: true) { create(:group, :nested) }
+ let_it_be(:project, reload: true) { create(:project, :public, namespace: group) }
+ let_it_be(:board, reload: true) { create(:board, project: project) }
+ let_it_be(:user, reload: true) { create(:user) }
+ let_it_be(:user2, reload: true) { create(:user) }
let(:filtered_search) { find('[data-testid="issue-board-filtered-search"]') }
let(:filter_input) { find('.gl-filtered-search-term-input') }
@@ -47,34 +62,34 @@ RSpec.describe 'Project issue boards', :js do
end
context 'with lists' do
- let_it_be(:milestone) { create(:milestone, project: project) }
-
- let_it_be(:planning) { create(:label, project: project, name: 'Planning', description: 'Test') }
- let_it_be(:development) { create(:label, project: project, name: 'Development') }
- let_it_be(:testing) { create(:label, project: project, name: 'Testing') }
- let_it_be(:bug) { create(:label, project: project, name: 'Bug') }
- let_it_be(:backlog) { create(:label, project: project, name: 'Backlog') }
- let_it_be(:closed) { create(:label, project: project, name: 'Closed') }
- let_it_be(:accepting) { create(:label, project: project, name: 'Accepting Merge Requests') }
- let_it_be(:a_plus) { create(:label, project: project, name: 'A+') }
- let_it_be(:list1) { create(:list, board: board, label: planning, position: 0) }
- let_it_be(:list2) { create(:list, board: board, label: development, position: 1) }
- let_it_be(:backlog_list) { create(:backlog_list, board: board) }
-
- let_it_be(:confidential_issue) { create(:labeled_issue, :confidential, project: project, author: user, labels: [planning], relative_position: 9) }
- let_it_be(:issue1) { create(:labeled_issue, project: project, title: 'aaa', description: '111', assignees: [user], labels: [planning], relative_position: 8) }
- let_it_be(:issue2) { create(:labeled_issue, project: project, title: 'bbb', description: '222', author: user2, labels: [planning], relative_position: 7) }
- let_it_be(:issue3) { create(:labeled_issue, project: project, title: 'ccc', description: '333', labels: [planning], relative_position: 6) }
- let_it_be(:issue4) { create(:labeled_issue, project: project, title: 'ddd', description: '444', labels: [planning], relative_position: 5) }
- let_it_be(:issue5) { create(:labeled_issue, project: project, title: 'eee', description: '555', labels: [planning], milestone: milestone, relative_position: 4) }
- let_it_be(:issue6) { create(:labeled_issue, project: project, title: 'fff', description: '666', labels: [planning, development], relative_position: 3) }
- let_it_be(:issue7) { create(:labeled_issue, project: project, title: 'ggg', description: '777', labels: [development], relative_position: 2) }
- let_it_be(:issue8) { create(:closed_issue, project: project, title: 'hhh', description: '888') }
- let_it_be(:issue9) { create(:labeled_issue, project: project, title: 'iii', description: '999', labels: [planning, testing, bug, accepting], relative_position: 1) }
- let_it_be(:issue10) { create(:labeled_issue, project: project, title: 'issue +', description: 'A+ great issue', labels: [a_plus]) }
+ let_it_be(:milestone, reload: true) { create(:milestone, project: project) }
+
+ let_it_be(:planning, reload: true) { create(:label, project: project, name: 'Planning', description: 'Test') }
+ let_it_be(:development, reload: true) { create(:label, project: project, name: 'Development') }
+ let_it_be(:testing, reload: true) { create(:label, project: project, name: 'Testing') }
+ let_it_be(:bug, reload: true) { create(:label, project: project, name: 'Bug') }
+ let_it_be(:backlog, reload: true) { create(:label, project: project, name: 'Backlog') }
+ let_it_be(:closed, reload: true) { create(:label, project: project, name: 'Closed') }
+ let_it_be(:accepting, reload: true) { create(:label, project: project, name: 'Accepting Merge Requests') }
+ let_it_be(:a_plus, reload: true) { create(:label, project: project, name: 'A+') }
+ let_it_be(:list1, reload: true) { create(:list, board: board, label: planning, position: 0) }
+ let_it_be(:list2, reload: true) { create(:list, board: board, label: development, position: 1) }
+ let_it_be(:backlog_list, reload: true) { create(:backlog_list, board: board) }
+
+ let_it_be(:confidential_issue, reload: true) { create(:labeled_issue, :confidential, project: project, author: user, labels: [planning], relative_position: 9) }
+ let_it_be(:issue1, reload: true) { create(:labeled_issue, project: project, title: 'aaa', description: '111', assignees: [user], labels: [planning], relative_position: 8) }
+ let_it_be(:issue2, reload: true) { create(:labeled_issue, project: project, title: 'bbb', description: '222', author: user2, labels: [planning], relative_position: 7) }
+ let_it_be(:issue3, reload: true) { create(:labeled_issue, project: project, title: 'ccc', description: '333', labels: [planning], relative_position: 6) }
+ let_it_be(:issue4, reload: true) { create(:labeled_issue, project: project, title: 'ddd', description: '444', labels: [planning], relative_position: 5) }
+ let_it_be(:issue5, reload: true) { create(:labeled_issue, project: project, title: 'eee', description: '555', labels: [planning], milestone: milestone, relative_position: 4) }
+ let_it_be(:issue6, reload: true) { create(:labeled_issue, project: project, title: 'fff', description: '666', labels: [planning, development], relative_position: 3) }
+ let_it_be(:issue7, reload: true) { create(:labeled_issue, project: project, title: 'ggg', description: '777', labels: [development], relative_position: 2) }
+ let_it_be(:issue8, reload: true) { create(:closed_issue, project: project, title: 'hhh', description: '888') }
+ let_it_be(:issue9, reload: true) { create(:labeled_issue, project: project, title: 'iii', description: '999', labels: [planning, testing, bug, accepting], relative_position: 1) }
+ let_it_be(:issue10, reload: true) { create(:labeled_issue, project: project, title: 'issue +', description: 'A+ great issue', labels: [a_plus]) }
before do
- visit_project_board(project, board)
+ visit_project_board_path_without_query_limit(project, board)
end
it 'shows description tooltip on list title', :quarantine do
@@ -88,7 +103,7 @@ RSpec.describe 'Project issue boards', :js do
wait_for_board_cards(3, 2)
end
- it 'shows confidential issues with icon' do
+ it 'shows confidential issues with icon', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/383970' do
page.within(find('.board:nth-child(2)')) do
expect(page).to have_selector('.confidential-icon', count: 1)
end
@@ -125,7 +140,7 @@ RSpec.describe 'Project issue boards', :js do
it 'infinite scrolls list' do
create_list(:labeled_issue, 30, project: project, labels: [planning])
- visit_project_board(project, board)
+ visit_project_board_path_without_query_limit(project, board)
page.within(find('.board:nth-child(2)')) do
expect(page.find('.board-header')).to have_content('38')
@@ -164,7 +179,7 @@ RSpec.describe 'Project issue boards', :js do
end
end
- context 'closed' do
+ context 'closed', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/383970' do
it 'shows list of closed issues' do
wait_for_board_cards(4, 1)
wait_for_requests
@@ -204,31 +219,26 @@ RSpec.describe 'Project issue boards', :js do
expect(find('.board:nth-child(3) [data-testid="board-list-header"]')).to have_content(planning.title)
# Make sure list positions are preserved after a reload
- visit_project_board(project, board)
+ visit_project_board_path_without_query_limit(project, board)
expect(find('.board:nth-child(2) [data-testid="board-list-header"]')).to have_content(development.title)
expect(find('.board:nth-child(3) [data-testid="board-list-header"]')).to have_content(planning.title)
end
context 'without backlog and closed lists' do
- let_it_be(:board) { create(:board, project: project, hide_backlog_list: true, hide_closed_list: true) }
- let_it_be(:list1) { create(:list, board: board, label: planning, position: 0) }
- let_it_be(:list2) { create(:list, board: board, label: development, position: 1) }
+ let_it_be(:board, reload: true) { create(:board, project: project, hide_backlog_list: true, hide_closed_list: true) }
+ let_it_be(:list1, reload: true) { create(:list, board: board, label: planning, position: 0) }
+ let_it_be(:list2, reload: true) { create(:list, board: board, label: development, position: 1) }
it 'changes position of list' do
- inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
- visit_project_board(project, board)
- end
+ visit_project_board_path_without_query_limit(project, board)
drag(list_from_index: 0, list_to_index: 1, selector: '.board-header')
expect(find('.board:nth-child(1) [data-testid="board-list-header"]')).to have_content(development.title)
expect(find('.board:nth-child(2) [data-testid="board-list-header"]')).to have_content(planning.title)
- inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
- # Make sure list positions are preserved after a reload
- visit_project_board(project, board)
- end
+ visit_project_board_path_without_query_limit(project, board)
expect(find('.board:nth-child(1) [data-testid="board-list-header"]')).to have_content(development.title)
expect(find('.board:nth-child(2) [data-testid="board-list-header"]')).to have_content(planning.title)
@@ -246,7 +256,7 @@ RSpec.describe 'Project issue boards', :js do
expect(page).to have_selector(selector, text: development.title, count: 1)
end
- it 'issue moves between lists and does not show the "Development" label since the card is in the "Development" list label' do
+ it 'issue moves between lists and does not show the "Development" label since the card is in the "Development" list label', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/383970' do
drag(list_from_index: 1, from_index: 1, list_to_index: 2)
wait_for_board_cards(2, 7)
@@ -257,7 +267,7 @@ RSpec.describe 'Project issue boards', :js do
expect(find('.board:nth-child(3)').all('.board-card').last).not_to have_content(development.title)
end
- it 'issue moves between lists and does not show the "Planning" label since the card is in the "Planning" list label' do
+ it 'issue moves between lists and does not show the "Planning" label since the card is in the "Planning" list label', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/383970' do
drag(list_from_index: 2, list_to_index: 1)
wait_for_board_cards(2, 9)
@@ -268,7 +278,7 @@ RSpec.describe 'Project issue boards', :js do
expect(find('.board:nth-child(2)').all('.board-card').first).not_to have_content(planning.title)
end
- it 'issue moves from closed' do
+ it 'issue moves from closed', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/383970' do
drag(list_from_index: 2, list_to_index: 3)
wait_for_board_cards(2, 8)
@@ -285,7 +295,7 @@ RSpec.describe 'Project issue boards', :js do
end
end
- context 'list header' do
+ context 'list header', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/383970' do
let(:total_planning_issues) { "8" }
it 'shows issue count on the list' do
@@ -309,7 +319,7 @@ RSpec.describe 'Project issue boards', :js do
wait_for_empty_boards((3..4))
end
- it 'filters by assignee' do
+ it 'filters by assignee', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/383970' do
set_filter("assignee", user.username)
click_on user.username
filter_submit.click
@@ -331,7 +341,7 @@ RSpec.describe 'Project issue boards', :js do
wait_for_board_cards(4, 0)
end
- it 'filters by label' do
+ it 'filters by label', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/383970' do
set_filter("label", testing.title)
click_on testing.title
filter_submit.click
@@ -390,7 +400,7 @@ RSpec.describe 'Project issue boards', :js do
wait_for_board_cards(2, 8)
end
- it 'infinite scrolls list with label filter' do
+ it 'infinite scrolls list with label filter', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/383970' do
create_list(:labeled_issue, 30, project: project, labels: [planning, testing])
set_filter("label", testing.title)
@@ -531,7 +541,7 @@ RSpec.describe 'Project issue boards', :js do
end
context 'as guest user' do
- let_it_be(:user_guest) { create(:user) }
+ let_it_be(:user_guest, reload: true) { create(:user) }
before do
stub_feature_flags(apollo_boards: false)
@@ -601,4 +611,10 @@ RSpec.describe 'Project issue boards', :js do
wait_for_requests
end
+
+ def visit_project_board_path_without_query_limit(project, board)
+ inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
+ visit_project_board(project, board)
+ end
+ end
end
diff --git a/spec/features/boards/focus_mode_spec.rb b/spec/features/boards/focus_mode_spec.rb
index 453a8d8870b..8f3ce25b583 100644
--- a/spec/features/boards/focus_mode_spec.rb
+++ b/spec/features/boards/focus_mode_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issue Boards focus mode', :js do
+RSpec.describe 'Issue Boards focus mode', :js, feature_category: :team_planning do
let(:project) { create(:project, :public) }
before do
diff --git a/spec/features/boards/issue_ordering_spec.rb b/spec/features/boards/issue_ordering_spec.rb
index a3dda3b9d2f..f1ee7a8fde7 100644
--- a/spec/features/boards/issue_ordering_spec.rb
+++ b/spec/features/boards/issue_ordering_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issue Boards', :js do
+RSpec.describe 'Issue Boards', :js, feature_category: :team_planning do
include DragTo
let(:project) { create(:project, :public) }
diff --git a/spec/features/boards/keyboard_shortcut_spec.rb b/spec/features/boards/keyboard_shortcut_spec.rb
index cefb486349d..6f03f6db3ab 100644
--- a/spec/features/boards/keyboard_shortcut_spec.rb
+++ b/spec/features/boards/keyboard_shortcut_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issue Boards shortcut', :js do
+RSpec.describe 'Issue Boards shortcut', :js, feature_category: :team_planning do
context 'issues are enabled' do
let(:project) { create(:project) }
diff --git a/spec/features/boards/multi_select_spec.rb b/spec/features/boards/multi_select_spec.rb
index cad303a14e5..7afe34be3d8 100644
--- a/spec/features/boards/multi_select_spec.rb
+++ b/spec/features/boards/multi_select_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Multi Select Issue', :js do
+RSpec.describe 'Multi Select Issue', :js, feature_category: :team_planning do
include DragTo
let(:group) { create(:group, :nested) }
diff --git a/spec/features/boards/multiple_boards_spec.rb b/spec/features/boards/multiple_boards_spec.rb
index 219f24f60d7..e9d34c6f87f 100644
--- a/spec/features/boards/multiple_boards_spec.rb
+++ b/spec/features/boards/multiple_boards_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Multiple Issue Boards', :js do
+RSpec.describe 'Multiple Issue Boards', :js, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public) }
let_it_be(:planning) { create(:label, project: project, name: 'Planning') }
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index 5f4517d47ee..1b0695e4e60 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issue Boards new issue', :js do
+RSpec.describe 'Issue Boards new issue', :js, feature_category: :team_planning do
let_it_be(:project) { create(:project, :public) }
let_it_be(:board) { create(:board, project: project) }
let_it_be(:backlog_list) { create(:backlog_list, board: board) }
diff --git a/spec/features/boards/reload_boards_on_browser_back_spec.rb b/spec/features/boards/reload_boards_on_browser_back_spec.rb
index 7fa440befc1..0ca680c5ed5 100644
--- a/spec/features/boards/reload_boards_on_browser_back_spec.rb
+++ b/spec/features/boards/reload_boards_on_browser_back_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Ensure Boards do not show stale data on browser back', :js do
+RSpec.describe 'Ensure Boards do not show stale data on browser back', :js, feature_category: :team_planning do
let(:project) { create(:project, :public) }
let(:board) { create(:board, project: project) }
let(:user) { create(:user) }
diff --git a/spec/features/boards/sidebar_assignee_spec.rb b/spec/features/boards/sidebar_assignee_spec.rb
index 63553cec89b..e3de594f856 100644
--- a/spec/features/boards/sidebar_assignee_spec.rb
+++ b/spec/features/boards/sidebar_assignee_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Project issue boards sidebar assignee', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332230' do
+RSpec.describe 'Project issue boards sidebar assignee', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332230',
+ feature_category: :team_planning do
include BoardHelpers
let_it_be(:user) { create(:user) }
@@ -112,7 +113,7 @@ RSpec.describe 'Project issue boards sidebar assignee', :js, quarantine: 'https:
page.within(assignees_widget) do
click_button('Edit')
- expect(find('.dropdown-menu')).to have_selector('.gl-new-dropdown-item-check-icon')
+ expect(find('.dropdown-menu')).to have_selector('.gl-dropdown-item-check-icon')
end
end
end
diff --git a/spec/features/boards/sidebar_labels_in_namespaces_spec.rb b/spec/features/boards/sidebar_labels_in_namespaces_spec.rb
index 8395a0b33c0..c3bb58df797 100644
--- a/spec/features/boards/sidebar_labels_in_namespaces_spec.rb
+++ b/spec/features/boards/sidebar_labels_in_namespaces_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issue boards sidebar labels select', :js do
+RSpec.describe 'Issue boards sidebar labels select', :js, feature_category: :team_planning do
include BoardHelpers
include_context 'labels from nested groups and projects'
diff --git a/spec/features/boards/sidebar_labels_spec.rb b/spec/features/boards/sidebar_labels_spec.rb
index 12d91e9c5a8..460d0d232b3 100644
--- a/spec/features/boards/sidebar_labels_spec.rb
+++ b/spec/features/boards/sidebar_labels_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project issue boards sidebar labels', :js do
+RSpec.describe 'Project issue boards sidebar labels', :js, feature_category: :team_planning do
include BoardHelpers
let_it_be(:group) { create(:group, :public) }
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index 2b2a412194a..0a16e95c0bf 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project issue boards sidebar', :js do
+RSpec.describe 'Project issue boards sidebar', :js, feature_category: :team_planning do
include BoardHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/boards/user_adds_lists_to_board_spec.rb b/spec/features/boards/user_adds_lists_to_board_spec.rb
index 480a88a6b84..a936e14168c 100644
--- a/spec/features/boards/user_adds_lists_to_board_spec.rb
+++ b/spec/features/boards/user_adds_lists_to_board_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User adds lists', :js do
+RSpec.describe 'User adds lists', :js, feature_category: :team_planning do
let_it_be(:group) { create(:group, :nested) }
let_it_be(:project) { create(:project, :public, namespace: group) }
let_it_be(:group_board) { create(:board, group: group) }
diff --git a/spec/features/boards/user_visits_board_spec.rb b/spec/features/boards/user_visits_board_spec.rb
index c386477fa9d..44c691435df 100644
--- a/spec/features/boards/user_visits_board_spec.rb
+++ b/spec/features/boards/user_visits_board_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User visits issue boards', :js do
+RSpec.describe 'User visits issue boards', :js, feature_category: :team_planning do
using RSpec::Parameterized::TableSyntax
let_it_be(:group) { create_default(:group, :public) }
diff --git a/spec/features/breadcrumbs_schema_markup_spec.rb b/spec/features/breadcrumbs_schema_markup_spec.rb
index f86ad5cd2ae..d924423c9a9 100644
--- a/spec/features/breadcrumbs_schema_markup_spec.rb
+++ b/spec/features/breadcrumbs_schema_markup_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Breadcrumbs schema markup', :aggregate_failures do
+RSpec.describe 'Breadcrumbs schema markup', :aggregate_failures, feature_category: :not_owned do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public, namespace: user.namespace) }
let_it_be(:issue) { create(:issue, project: project) }
diff --git a/spec/features/broadcast_messages_spec.rb b/spec/features/broadcast_messages_spec.rb
index 1fec68a1d98..8300cfce539 100644
--- a/spec/features/broadcast_messages_spec.rb
+++ b/spec/features/broadcast_messages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Broadcast Messages' do
+RSpec.describe 'Broadcast Messages', feature_category: :onboarding do
let_it_be(:user) { create(:user) }
shared_examples 'a Broadcast Messages' do |type|
@@ -31,11 +31,13 @@ RSpec.describe 'Broadcast Messages' do
expect(page).not_to have_content 'SampleMessage'
end
- it 'broadcast message is still hidden after refresh', :js,
- quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/347118' do
+ it 'broadcast message is still hidden after refresh', :js do
visit root_path
find('.js-dismiss-current-broadcast-notification').click
+
+ wait_for_cookie_set("hide_broadcast_message_#{broadcast_message.id}")
+
visit root_path
expect(page).not_to have_content 'SampleMessage'
diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb
index a8aa3f0b36a..2c5b7d66e2f 100644
--- a/spec/features/calendar_spec.rb
+++ b/spec/features/calendar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Contributions Calendar', :js do
+RSpec.describe 'Contributions Calendar', :js, feature_category: :users do
include MobileHelpers
let(:user) { create(:user) }
@@ -143,18 +143,27 @@ RSpec.describe 'Contributions Calendar', :js do
end
end
- describe '1 issue creation calendar activity' do
+ describe '1 issue and 1 work item creation calendar activity' do
before do
Issues::CreateService.new(project: contributed_project, current_user: user, params: issue_params, spam_params: nil).execute
+ WorkItems::CreateService.new(
+ project: contributed_project,
+ current_user: user,
+ params: { title: 'new task' },
+ spam_params: nil
+ ).execute
end
- it_behaves_like 'a day with activity', contribution_count: 1
+ it_behaves_like 'a day with activity', contribution_count: 2
describe 'issue title is shown on activity page' do
include_context 'visit user page'
- it 'displays calendar activity log', :sidekiq_might_not_need_inline do
- expect(find('#js-overview .overview-content-list .event-target-title')).to have_content issue_title
+ it 'displays calendar activity log', :sidekiq_inline do
+ expect(all('#js-overview .overview-content-list .event-target-title').map(&:text)).to contain_exactly(
+ match(/#{issue_title}/),
+ match(/new task/)
+ )
end
end
end
diff --git a/spec/features/callouts/registration_enabled_spec.rb b/spec/features/callouts/registration_enabled_spec.rb
index 79e99712183..1ea52dbf12a 100644
--- a/spec/features/callouts/registration_enabled_spec.rb
+++ b/spec/features/callouts/registration_enabled_spec.rb
@@ -2,11 +2,11 @@
require 'spec_helper'
-RSpec.describe 'Registration enabled callout' do
+RSpec.describe 'Registration enabled callout', feature_category: :authentication_and_authorization do
let_it_be(:admin) { create(:admin) }
let_it_be(:non_admin) { create(:user) }
let_it_be(:project) { create(:project) }
- let_it_be(:callout_title) { _('Anyone can register for an account.') }
+ let_it_be(:callout_title) { _('Check your sign-up restrictions') }
context 'when "Sign-up enabled" setting is `true`' do
before do
@@ -22,7 +22,7 @@ RSpec.describe 'Registration enabled callout' do
visit root_path
expect(page).to have_content callout_title
- expect(page).to have_link _('Turn off'), href: general_admin_application_settings_path(anchor: 'js-signup-settings')
+ expect(page).to have_link _('Deactivate'), href: general_admin_application_settings_path(anchor: 'js-signup-settings')
visit root_dashboard_path
diff --git a/spec/features/canonical_link_spec.rb b/spec/features/canonical_link_spec.rb
index 8b64e9a5b9d..d8f9a7584e7 100644
--- a/spec/features/canonical_link_spec.rb
+++ b/spec/features/canonical_link_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Canonical link' do
+RSpec.describe 'Canonical link', feature_category: :remote_development do
include Spec::Support::Helpers::Features::CanonicalLinkHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/clusters/cluster_detail_page_spec.rb b/spec/features/clusters/cluster_detail_page_spec.rb
index 06e3e00db7d..e8fb5f4105d 100644
--- a/spec/features/clusters/cluster_detail_page_spec.rb
+++ b/spec/features/clusters/cluster_detail_page_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Clusterable > Show page' do
+RSpec.describe 'Clusterable > Show page', feature_category: :kubernetes_management do
include KubernetesHelpers
let(:current_user) { create(:user) }
diff --git a/spec/features/clusters/cluster_health_dashboard_spec.rb b/spec/features/clusters/cluster_health_dashboard_spec.rb
index 88d6976c2be..b557f803a99 100644
--- a/spec/features/clusters/cluster_health_dashboard_spec.rb
+++ b/spec/features/clusters/cluster_health_dashboard_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Cluster Health board', :js, :kubeclient, :use_clean_rails_memory_store_caching, :sidekiq_inline do
+RSpec.describe 'Cluster Health board', :js, :kubeclient, :use_clean_rails_memory_store_caching, :sidekiq_inline,
+feature_category: :kubernetes_management do
include KubernetesHelpers
include PrometheusHelpers
diff --git a/spec/features/clusters/create_agent_spec.rb b/spec/features/clusters/create_agent_spec.rb
index b19e57c550c..01902c36e99 100644
--- a/spec/features/clusters/create_agent_spec.rb
+++ b/spec/features/clusters/create_agent_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Cluster agent registration', :js do
+RSpec.describe 'Cluster agent registration', :js, feature_category: :kubernetes_management do
let_it_be(:project) { create(:project, :custom_repo, files: { '.gitlab/agents/example-agent-1/config.yaml' => '' }) }
let_it_be(:current_user) { create(:user, maintainer_projects: [project]) }
let_it_be(:token) { Devise.friendly_token }
@@ -30,8 +30,8 @@ RSpec.describe 'Cluster agent registration', :js do
click_button('Connect a cluster')
expect(page).to have_content('Register')
- click_button('Select an agent')
- click_button('example-agent-2')
+ click_button('Select an agent or enter a name to create new')
+ page.find('li', text: 'example-agent-2').click
click_button('Register')
expect(page).to have_content('You cannot see this token again after you close this window.')
diff --git a/spec/features/commit_spec.rb b/spec/features/commit_spec.rb
index c9fa10d58e6..649b67e7fd0 100644
--- a/spec/features/commit_spec.rb
+++ b/spec/features/commit_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Commit' do
+RSpec.describe 'Commit', feature_category: :source_code_management do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
diff --git a/spec/features/commits/user_uses_quick_actions_spec.rb b/spec/features/commits/user_uses_quick_actions_spec.rb
index 12e7865e490..6d043a0bb2f 100644
--- a/spec/features/commits/user_uses_quick_actions_spec.rb
+++ b/spec/features/commits/user_uses_quick_actions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Commit > User uses quick actions', :js do
+RSpec.describe 'Commit > User uses quick actions', :js, feature_category: :source_code_management do
include Spec::Support::Helpers::Features::NotesHelpers
include RepoHelpers
diff --git a/spec/features/commits/user_view_commits_spec.rb b/spec/features/commits/user_view_commits_spec.rb
index f7fd3a6e209..b58d7cf3741 100644
--- a/spec/features/commits/user_view_commits_spec.rb
+++ b/spec/features/commits/user_view_commits_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Commit > User view commits' do
+RSpec.describe 'Commit > User view commits', feature_category: :source_code_management do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, :public) }
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 97f820c1518..e4d4375a138 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Commits' do
+RSpec.describe 'Commits', feature_category: :source_code_management do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
@@ -183,7 +183,7 @@ RSpec.describe 'Commits' do
set_cookie('new_repo', 'true')
visit project_commits_path(project, branch_name)
- expect(find('.js-project-refs-dropdown')).to have_content branch_name
+ expect(find('.ref-selector')).to have_content branch_name
end
end
diff --git a/spec/features/contextual_sidebar_spec.rb b/spec/features/contextual_sidebar_spec.rb
index cc4a0471d4e..2b671d4b3f1 100644
--- a/spec/features/contextual_sidebar_spec.rb
+++ b/spec/features/contextual_sidebar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Contextual sidebar', :js do
+RSpec.describe 'Contextual sidebar', :js, feature_category: :remote_development do
context 'when context is a project' do
let_it_be(:project) { create(:project) }
diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb
index 8de4c66c62f..55bf77d00b1 100644
--- a/spec/features/cycle_analytics_spec.rb
+++ b/spec/features/cycle_analytics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Value Stream Analytics', :js do
+RSpec.describe 'Value Stream Analytics', :js, feature_category: :value_stream_management do
include CycleAnalyticsHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/dashboard/activity_spec.rb b/spec/features/dashboard/activity_spec.rb
index 7390edc3c47..b1734cb353b 100644
--- a/spec/features/dashboard/activity_spec.rb
+++ b/spec/features/dashboard/activity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard > Activity' do
+RSpec.describe 'Dashboard > Activity', feature_category: :users do
let(:user) { create(:user) }
before do
diff --git a/spec/features/dashboard/archived_projects_spec.rb b/spec/features/dashboard/archived_projects_spec.rb
index d157d44bab7..d3992d34506 100644
--- a/spec/features/dashboard/archived_projects_spec.rb
+++ b/spec/features/dashboard/archived_projects_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard Archived Project' do
+RSpec.describe 'Dashboard Archived Project', feature_category: :projects do
let(:user) { create :user }
let(:project) { create :project }
let(:archived_project) { create(:project, :archived) }
diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb
index de8858fa8fa..34f99765c29 100644
--- a/spec/features/dashboard/datetime_on_tooltips_spec.rb
+++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Tooltips on .timeago dates', :js do
+RSpec.describe 'Tooltips on .timeago dates', :js, feature_category: :users do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, name: 'test', namespace: user.namespace) }
diff --git a/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb b/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb
index 6861fac3cc2..f5b02a87758 100644
--- a/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb
+++ b/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'The group dashboard', :js do
+RSpec.describe 'The group dashboard', :js, feature_category: :subgroups do
include ExternalAuthorizationServiceHelpers
include Spec::Support::Helpers::Features::TopNavSpecHelpers
diff --git a/spec/features/dashboard/group_spec.rb b/spec/features/dashboard/group_spec.rb
index f1283d29f4c..f363007f0d7 100644
--- a/spec/features/dashboard/group_spec.rb
+++ b/spec/features/dashboard/group_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard Group' do
+RSpec.describe 'Dashboard Group', feature_category: :subgroups do
before do
sign_in(create(:user))
end
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index 3a4296836bd..b28e2ccf787 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard Groups page', :js do
+RSpec.describe 'Dashboard Groups page', :js, feature_category: :subgroups do
let(:user) { create :user }
let(:group) { create(:group) }
let(:nested_group) { create(:group, :nested) }
diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb
index 91901414dde..5c7285f0491 100644
--- a/spec/features/dashboard/issuables_counter_spec.rb
+++ b/spec/features/dashboard/issuables_counter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching do
+RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
let(:issue) { create(:issue, project: project) }
@@ -12,6 +12,7 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d
issue.assignees = [user]
merge_request.update!(assignees: [user])
sign_in(user)
+ stub_feature_flags(limit_assigned_issues_count: false)
end
it 'reflects dashboard issues count' do
@@ -30,6 +31,28 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d
end
end
+ context 'when :limit_assigned_issues_count FF is used' do
+ before do
+ stub_feature_flags(limit_assigned_issues_count: true)
+ end
+
+ it 'reflects dashboard issues count' do
+ visit issues_path
+
+ expect_counters('issues', '1', n_("%d assigned issue", "%d assigned issues", 1) % 1)
+
+ issue.update!(assignees: [])
+
+ Users::AssignedIssuesCountService.new(current_user: user).delete_cache
+
+ travel_to(3.minutes.from_now) do
+ visit issues_path
+
+ expect_counters('issues', '0', n_("%d assigned issue", "%d assigned issues", 0) % 0)
+ end
+ end
+ end
+
it 'reflects dashboard merge requests count', :js do
visit merge_requests_path
diff --git a/spec/features/dashboard/issues_filter_spec.rb b/spec/features/dashboard/issues_filter_spec.rb
index 0d10aed955a..d5f362d8449 100644
--- a/spec/features/dashboard/issues_filter_spec.rb
+++ b/spec/features/dashboard/issues_filter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard Issues filtering', :js do
+RSpec.describe 'Dashboard Issues filtering', :js, feature_category: :team_planning do
include Spec::Support::Helpers::Features::SortingHelpers
include FilteredSearchHelpers
diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb
index 64181041be5..d74965f58fa 100644
--- a/spec/features/dashboard/issues_spec.rb
+++ b/spec/features/dashboard/issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard Issues' do
+RSpec.describe 'Dashboard Issues', feature_category: :team_planning do
include FilteredSearchHelpers
let(:current_user) { create :user }
diff --git a/spec/features/dashboard/label_filter_spec.rb b/spec/features/dashboard/label_filter_spec.rb
index ebe5c3e1091..f116c84ff40 100644
--- a/spec/features/dashboard/label_filter_spec.rb
+++ b/spec/features/dashboard/label_filter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard > label filter', :js do
+RSpec.describe 'Dashboard > label filter', :js, feature_category: :team_planning do
include FilteredSearchHelpers
let(:filtered_search) { find('.filtered-search') }
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
index 70f614cdcef..56d7c45de5d 100644
--- a/spec/features/dashboard/merge_requests_spec.rb
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard Merge Requests' do
+RSpec.describe 'Dashboard Merge Requests', feature_category: :code_review do
include Spec::Support::Helpers::Features::SortingHelpers
include FilteredSearchHelpers
include ProjectForksHelper
diff --git a/spec/features/dashboard/milestones_spec.rb b/spec/features/dashboard/milestones_spec.rb
index 08cb95979ac..b4d0d9c5812 100644
--- a/spec/features/dashboard/milestones_spec.rb
+++ b/spec/features/dashboard/milestones_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard > Milestones' do
+RSpec.describe 'Dashboard > Milestones', feature_category: :team_planning do
describe 'as anonymous user' do
before do
visit dashboard_milestones_path
diff --git a/spec/features/dashboard/project_member_activity_index_spec.rb b/spec/features/dashboard/project_member_activity_index_spec.rb
index c26a1a0b486..5bf1566fa31 100644
--- a/spec/features/dashboard/project_member_activity_index_spec.rb
+++ b/spec/features/dashboard/project_member_activity_index_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project member activity', :js do
+RSpec.describe 'Project member activity', :js, feature_category: :users do
let(:user) { create(:user) }
let(:project) { create(:project, :public, name: 'x', namespace: user.namespace) }
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
index c132caa88c8..2b89f16bbff 100644
--- a/spec/features/dashboard/projects_spec.rb
+++ b/spec/features/dashboard/projects_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard Projects' do
+RSpec.describe 'Dashboard Projects', feature_category: :projects do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, :repository) }
let_it_be(:project2) { create(:project, :public) }
@@ -101,14 +101,6 @@ RSpec.describe 'Dashboard Projects' do
expect(first('.project-row')).to have_content(project_with_most_stars.title)
end
-
- it 'shows tabs to filter by all projects or personal' do
- visit dashboard_projects_path
- segmented_button = page.find('.filtered-search-nav .button-filter-group')
-
- expect(segmented_button).to have_content 'All'
- expect(segmented_button).to have_content 'Personal'
- end
end
context 'when on Starred projects tab', :js do
diff --git a/spec/features/dashboard/root_explore_spec.rb b/spec/features/dashboard/root_explore_spec.rb
index a3c346ffe2a..c0d1f0de1f5 100644
--- a/spec/features/dashboard/root_explore_spec.rb
+++ b/spec/features/dashboard/root_explore_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Root explore' do
+RSpec.describe 'Root explore', feature_category: :not_owned do
let_it_be(:public_project) { create(:project, :public) }
let_it_be(:archived_project) { create(:project, :archived) }
let_it_be(:internal_project) { create(:project, :internal) }
@@ -30,4 +30,26 @@ RSpec.describe 'Root explore' do
include_examples 'shows public projects'
end
+
+ describe 'project language dropdown' do
+ let(:has_language_dropdown?) { page.has_selector?('[data-testid="project-language-dropdown"]') }
+
+ it 'is conditionally rendered' do
+ visit explore_projects_path
+
+ expect(has_language_dropdown?).to eq(true)
+ end
+
+ context 'with project_language_search ff disabled' do
+ before do
+ stub_feature_flags(project_language_search: false)
+ end
+
+ it 'is conditionally rendered' do
+ visit explore_projects_path
+
+ expect(has_language_dropdown?).to eq(false)
+ end
+ end
+ end
end
diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb
index 3f3ab4218f2..30587756505 100644
--- a/spec/features/dashboard/shortcuts_spec.rb
+++ b/spec/features/dashboard/shortcuts_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard shortcuts', :js do
+RSpec.describe 'Dashboard shortcuts', :js, feature_category: :not_owned do
context 'logged in' do
let(:user) { create(:user) }
let(:project) { create(:project) }
diff --git a/spec/features/dashboard/snippets_spec.rb b/spec/features/dashboard/snippets_spec.rb
index f891950eeb8..ab2cfc0573e 100644
--- a/spec/features/dashboard/snippets_spec.rb
+++ b/spec/features/dashboard/snippets_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard snippets' do
+RSpec.describe 'Dashboard snippets', feature_category: :source_code_management do
let_it_be(:user) { create(:user) }
context 'when the project has snippets' do
diff --git a/spec/features/dashboard/todos/target_state_spec.rb b/spec/features/dashboard/todos/target_state_spec.rb
index b0aafdda59a..f8b525a63f1 100644
--- a/spec/features/dashboard/todos/target_state_spec.rb
+++ b/spec/features/dashboard/todos/target_state_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard > Todo target states' do
+RSpec.describe 'Dashboard > Todo target states', feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:author) { create(:user) }
let_it_be(:project) { create(:project, :public) }
diff --git a/spec/features/dashboard/todos/todos_filtering_spec.rb b/spec/features/dashboard/todos/todos_filtering_spec.rb
index 938e42623f6..ea8c7e800c5 100644
--- a/spec/features/dashboard/todos/todos_filtering_spec.rb
+++ b/spec/features/dashboard/todos/todos_filtering_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard > User filters todos', :js do
+RSpec.describe 'Dashboard > User filters todos', :js, feature_category: :team_planning do
let(:user_1) { create(:user, username: 'user_1', name: 'user_1') }
let(:user_2) { create(:user, username: 'user_2', name: 'user_2') }
@@ -58,9 +58,9 @@ RSpec.describe 'Dashboard > User filters todos', :js do
wait_for_requests
- expect(page).to have_content "issue #{issue1.to_reference} \"issue\" at #{group1.name} / project_1"
- expect(page).to have_content "merge request #{merge_request.to_reference}"
- expect(page).not_to have_content "issue #{issue2.to_reference} \"issue\" at #{group2.name} / project_3"
+ expect(page).to have_content "issue · #{group1.name} / project_1 #{issue1.to_reference}"
+ expect(page).to have_content merge_request.to_reference.to_s
+ expect(page).not_to have_content "issue · #{group2.name} / project_3 #{issue2.to_reference}"
end
context 'Author filter' do
@@ -74,8 +74,8 @@ RSpec.describe 'Dashboard > User filters todos', :js do
wait_for_requests
- expect(find('.todos-list')).to have_content 'merge request'
- expect(find('.todos-list')).not_to have_content 'issue'
+ expect(find('.todos-list')).to have_content '!'
+ expect(find('.todos-list')).not_to have_content '#'
end
it 'shows only authors of existing todos' do
@@ -174,11 +174,11 @@ RSpec.describe 'Dashboard > User filters todos', :js do
def expect_to_see_action(action_name)
action_names = {
- assigned: ' assigned you ',
- review_requested: ' requested a review of ',
- mentioned: ' mentioned ',
- marked: ' added a todo for ',
- build_failed: ' pipeline failed in '
+ assigned: ' assigned you',
+ review_requested: ' requested a review',
+ mentioned: ' mentioned',
+ marked: ' added a to-do item',
+ build_failed: ' pipeline failed'
}
action_name_text = action_names.delete(action_name)
diff --git a/spec/features/dashboard/todos/todos_sorting_spec.rb b/spec/features/dashboard/todos/todos_sorting_spec.rb
index a0fa53b761b..e449f71878b 100644
--- a/spec/features/dashboard/todos/todos_sorting_spec.rb
+++ b/spec/features/dashboard/todos/todos_sorting_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard > User sorts todos' do
+RSpec.describe 'Dashboard > User sorts todos', feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project) }
diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb
index e02cd182b2c..606bc82a7bb 100644
--- a/spec/features/dashboard/todos/todos_spec.rb
+++ b/spec/features/dashboard/todos/todos_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard Todos' do
+RSpec.describe 'Dashboard Todos', feature_category: :team_planning do
include DesignManagementTestHelpers
let_it_be(:user) { create(:user, username: 'john') }
@@ -49,29 +49,8 @@ RSpec.describe 'Dashboard Todos' do
visit dashboard_todos_path
end
- it 'renders the mr link with the extra attributes' do
- link = page.find_link(referenced_mr.to_reference)
-
- expect(link).not_to be_nil
- expect(link['data-iid']).to eq(referenced_mr.iid.to_s)
- expect(link['data-project-path']).to eq(referenced_mr.project.full_path)
- expect(link['title']).to eq(referenced_mr.title)
- expect(link['data-reference-type']).to eq('merge_request')
- end
- end
-
- context 'when todo references an issue of type task' do
- let(:task) { create(:issue, :task, project: project) }
- let!(:task_todo) { create(:todo, :mentioned, user: user, project: project, target: task, author: author) }
-
- before do
- sign_in(user)
-
- visit dashboard_todos_path
- end
-
- it 'displays the correct issue type name' do
- expect(page).to have_content('mentioned you on task')
+ it 'renders the mr reference' do
+ expect(page).to have_content(referenced_mr.to_reference)
end
end
@@ -100,10 +79,6 @@ RSpec.describe 'Dashboard Todos' do
visit dashboard_todos_path
end
- it 'displays the correct issue type name' do
- expect(page).to have_content('mentioned you on issue')
- end
-
it 'has todo present' do
expect(page).to have_selector('.todos-list .todo', count: 1)
end
@@ -117,7 +92,7 @@ RSpec.describe 'Dashboard Todos' do
shared_examples 'deleting the todo' do
before do
within first('.todo') do
- click_link 'Done'
+ find('[data-testid="check-icon"]').click
end
end
@@ -143,9 +118,9 @@ RSpec.describe 'Dashboard Todos' do
shared_examples 'deleting and restoring the todo' do
before do
within first('.todo') do
- click_link 'Done'
+ find('[data-testid="check-icon"]').click
wait_for_requests
- click_link 'Undo'
+ find('[data-testid="redo-icon"]').click
end
end
@@ -192,7 +167,8 @@ RSpec.describe 'Dashboard Todos' do
it 'shows issue assigned to yourself message' do
page.within('.js-todos-all') do
- expect(page).to have_content("You assigned issue #{issue.to_reference} \"Fix bug\" at #{project.namespace.owner_name} / #{project.name} to yourself")
+ expect(page).to have_content("Fix bug · #{project.namespace.owner_name} / #{project.name} #{issue.to_reference}")
+ expect(page).to have_content("You assigned to yourself.")
end
end
end
@@ -203,10 +179,10 @@ RSpec.describe 'Dashboard Todos' do
visit dashboard_todos_path
end
- it 'shows you added a todo message' do
+ it 'shows you added a to-do item message' do
page.within('.js-todos-all') do
- expect(page).to have_content("You added a todo for issue #{issue.to_reference} \"Fix bug\" at #{project.namespace.owner_name} / #{project.name}")
- expect(page).not_to have_content('to yourself')
+ expect(page).to have_content("Fix bug · #{project.namespace.owner_name} / #{project.name} #{issue.to_reference}")
+ expect(page).to have_content("You added a to-do item.")
end
end
end
@@ -219,8 +195,8 @@ RSpec.describe 'Dashboard Todos' do
it 'shows you mentioned yourself message' do
page.within('.js-todos-all') do
- expect(page).to have_content("You mentioned yourself on issue #{issue.to_reference} \"Fix bug\" at #{project.namespace.owner_name} / #{project.name}")
- expect(page).not_to have_content('to yourself')
+ expect(page).to have_content("Fix bug · #{project.namespace.owner_name} / #{project.name} #{issue.to_reference}")
+ expect(page).to have_content("You mentioned yourself.")
end
end
end
@@ -233,8 +209,8 @@ RSpec.describe 'Dashboard Todos' do
it 'shows you directly addressed yourself message being displayed as mentioned yourself' do
page.within('.js-todos-all') do
- expect(page).to have_content("You mentioned yourself on issue #{issue.to_reference} \"Fix bug\" at #{project.namespace.owner_name} / #{project.name}")
- expect(page).not_to have_content('to yourself')
+ expect(page).to have_content("Fix bug · #{project.namespace.owner_name} / #{project.name} #{issue.to_reference}")
+ expect(page).to have_content("You mentioned yourself.")
end
end
end
@@ -249,8 +225,8 @@ RSpec.describe 'Dashboard Todos' do
it 'shows you set yourself as an approver message' do
page.within('.js-todos-all') do
- expect(page).to have_content("You set yourself as an approver for merge request #{merge_request.to_reference} \"Fixes issue\" at #{project.namespace.owner_name} / #{project.name}")
- expect(page).not_to have_content('to yourself')
+ expect(page).to have_content("Fixes issue · #{project.namespace.owner_name} / #{project.name} #{merge_request.to_reference}")
+ expect(page).to have_content("You set yourself as an approver.")
end
end
end
@@ -265,7 +241,28 @@ RSpec.describe 'Dashboard Todos' do
it 'shows you set yourself as an reviewer message' do
page.within('.js-todos-all') do
- expect(page).to have_content("You requested a review of merge request #{merge_request.to_reference} \"Fixes issue\" at #{project.namespace.owner_name} / #{project.name} from yourself")
+ expect(page).to have_content("Fixes issue · #{project.namespace.owner_name} / #{project.name} #{merge_request.to_reference}")
+ expect(page).to have_content("You requested a review from yourself.")
+ end
+ end
+ end
+ end
+
+ context 'User has automatically created todos' do
+ before do
+ sign_in(user)
+ end
+
+ context 'unmergeable todo' do
+ before do
+ create(:todo, :unmergeable, user: user, project: project, target: issue, author: user)
+ visit dashboard_todos_path
+ end
+
+ it 'shows unmergeable message' do
+ page.within('.js-todos-all') do
+ expect(page).to have_content("Fix bug · #{project.namespace.owner_name} / #{project.name} #{issue.to_reference}")
+ expect(page).to have_content("Could not merge.")
end
end
end
@@ -285,7 +282,7 @@ RSpec.describe 'Dashboard Todos' do
describe 'restoring the todo' do
before do
within first('.todo') do
- click_link 'Add a to do'
+ find('[data-testid="todo-add-icon"]').click
end
end
@@ -391,7 +388,7 @@ RSpec.describe 'Dashboard Todos' do
context 'User has deleted a todo' do
before do
within first('.todo') do
- click_link 'Done'
+ find('[data-testid="check-icon"]').click
end
end
@@ -420,13 +417,7 @@ RSpec.describe 'Dashboard Todos' do
end
it 'shows the todo' do
- expect(page).to have_content 'The pipeline failed in merge request'
- end
-
- it 'links to the pipelines for the merge request' do
- href = pipelines_project_merge_request_path(project, todo.target)
-
- expect(page).to have_link "merge request #{todo.target.to_reference}", href: href
+ expect(page).to have_content 'The pipeline failed.'
end
end
@@ -453,15 +444,29 @@ RSpec.describe 'Dashboard Todos' do
it 'has todo present' do
expect(page).to have_selector('.todos-list .todo', count: 1)
end
+ end
- it 'has a link that will take me to the design page' do
- click_link "design #{target.to_reference}"
+ context 'User has a todo for an access requested raised for group membership' do
+ let_it_be(:group) { create(:group, :public) }
- expectation = Gitlab::Routing.url_helpers.designs_project_issue_path(
- target.project, target.issue, target.filename
- )
+ let_it_be(:todo) do
+ create(:todo, :member_access_requested,
+ user: user,
+ target: group,
+ author: author,
+ group: group)
+ end
+
+ before do
+ group.add_owner(user)
+ sign_in(user)
- expect(page).to have_current_path(expectation, ignore_query: true)
+ visit dashboard_todos_path
+ end
+
+ it 'has todo present with access request content' do
+ expect(page).to have_selector('.todos-list .todo', count: 1)
+ expect(page).to have_content "#{author.name} has requested access to group #{group.name}"
end
end
end
diff --git a/spec/features/dashboard/user_filters_projects_spec.rb b/spec/features/dashboard/user_filters_projects_spec.rb
index e25da5854ab..1168a6827fd 100644
--- a/spec/features/dashboard/user_filters_projects_spec.rb
+++ b/spec/features/dashboard/user_filters_projects_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard > User filters projects' do
+RSpec.describe 'Dashboard > User filters projects', feature_category: :projects do
let(:user) { create(:user) }
let(:project) { create(:project, name: 'Victorialand', namespace: user.namespace, created_at: 2.seconds.ago, updated_at: 2.seconds.ago) }
let(:user2) { create(:user) }
@@ -16,7 +16,6 @@ RSpec.describe 'Dashboard > User filters projects' do
describe 'filtering personal projects' do
before do
- stub_feature_flags(project_list_filter_bar: false)
project2.add_developer(user)
visit dashboard_projects_path
@@ -33,7 +32,6 @@ RSpec.describe 'Dashboard > User filters projects' do
describe 'filtering starred projects', :js do
before do
- stub_feature_flags(project_list_filter_bar: false)
user.toggle_star(project)
visit dashboard_projects_path
@@ -49,8 +47,6 @@ RSpec.describe 'Dashboard > User filters projects' do
describe 'without search bar', :js do
before do
- stub_feature_flags(project_list_filter_bar: false)
-
project2.add_developer(user)
visit dashboard_projects_path
end
@@ -65,175 +61,4 @@ RSpec.describe 'Dashboard > User filters projects' do
expect(page).not_to have_content 'Treasure'
end
end
-
- describe 'with search bar', :js do
- before do
- stub_feature_flags(project_list_filter_bar: true)
-
- project2.add_developer(user)
- visit dashboard_projects_path
- end
-
- # TODO: move these helpers somewhere more useful
- def click_sort_direction
- page.find('.filtered-search-block #filtered-search-sorting-dropdown .reverse-sort-btn').click
- end
-
- def select_dropdown_option(selector, label, option_selector = '.dropdown-menu a')
- dropdown = page.find(selector)
- dropdown.click
-
- dropdown.find(option_selector, text: label, match: :first).click
- end
-
- def expect_to_see_projects(sorted_projects)
- list = page.all('.projects-list .project-name').map(&:text)
- expect(list).to match(sorted_projects)
- end
-
- describe 'Search' do
- it 'executes when the search button is clicked' do
- expect(page).to have_content 'Victorialand'
- expect(page).to have_content 'Treasure'
-
- fill_in 'project-filter-form-field', with: 'Lord vegeta\n'
- find('.filtered-search .btn').click
-
- expect(page).not_to have_content 'Victorialand'
- expect(page).not_to have_content 'Treasure'
- end
-
- it 'will execute when i press enter' do
- expect(page).to have_content 'Victorialand'
- expect(page).to have_content 'Treasure'
-
- fill_in 'project-filter-form-field', with: 'Lord frieza\n'
- find('#project-filter-form-field').native.send_keys :enter
-
- expect(page).not_to have_content 'Victorialand'
- expect(page).not_to have_content 'Treasure'
- end
- end
-
- describe 'Filter' do
- before do
- private_project = create(:project, :private, name: 'Private project', namespace: user.namespace)
- internal_project = create(:project, :internal, name: 'Internal project', namespace: user.namespace)
-
- private_project.add_maintainer(user)
- internal_project.add_maintainer(user)
- end
-
- it 'filters private projects only' do
- select_dropdown_option '#filtered-search-visibility-dropdown > .dropdown', 'Private', '.dropdown-item'
-
- expect(current_url).to match(/visibility_level=0/)
-
- list = page.all('.projects-list .project-name').map(&:text)
-
- expect(list).to contain_exactly("Private project", "Treasure", "Victorialand")
- end
-
- it 'filters internal projects only' do
- select_dropdown_option '#filtered-search-visibility-dropdown > .dropdown', 'Internal', '.dropdown-item'
-
- expect(current_url).to match(/visibility_level=10/)
-
- list = page.all('.projects-list .project-name').map(&:text)
-
- expect(list).to contain_exactly('Internal project')
- end
-
- it 'filters any project' do
- # Selecting the same option in the `GlListbox` does not emit `select` event
- # and that is why URL update won't be triggered. Given that `Any` is a default option
- # we need to explicitly switch from some other option (e.g. `Internal`) to `Any`
- # to trigger the page update
- select_dropdown_option '#filtered-search-visibility-dropdown > .dropdown', 'Internal', '.dropdown-item'
-
- select_dropdown_option '#filtered-search-visibility-dropdown > .dropdown', 'Any', '.dropdown-item'
-
- list = page.all('.projects-list .project-name').map(&:text)
-
- expect(list).to contain_exactly("Internal project", "Private project", "Treasure", "Victorialand")
- end
- end
-
- describe 'Sorting' do
- let(:desc_sorted_project_names) { %w[Treasure Victorialand] }
-
- before do
- user.toggle_star(project)
- user.toggle_star(project2)
- user2.toggle_star(project2)
- end
-
- it 'has all sorting options', :js do
- sorting_dropdown = page.find('.filtered-search-block #filtered-search-sorting-dropdown')
-
- expect(sorting_dropdown).to have_css '.reverse-sort-btn'
-
- sorting_dropdown.click
-
- ['Updated date', 'Created date', 'Name', 'Stars'].each do |label|
- expect(sorting_dropdown).to have_content(label)
- end
- end
-
- it 'defaults to "Name"', :js do
- page.find('.filtered-search-block #filtered-search-sorting-dropdown').click
- active_sorting_option = page.first('.filtered-search-block #filtered-search-sorting-dropdown .is-active')
-
- expect(active_sorting_option).to have_content 'Name'
- end
-
- context 'Sorting by name' do
- it 'sorts the project list' do
- select_dropdown_option '#filtered-search-sorting-dropdown', 'Name'
-
- expect_to_see_projects(desc_sorted_project_names)
-
- click_sort_direction
-
- expect_to_see_projects(desc_sorted_project_names.reverse)
- end
- end
-
- context 'Sorting by Updated date' do
- it 'sorts the project list' do
- select_dropdown_option '#filtered-search-sorting-dropdown', 'Updated date'
-
- expect_to_see_projects(desc_sorted_project_names)
-
- click_sort_direction
-
- expect_to_see_projects(desc_sorted_project_names.reverse)
- end
- end
-
- context 'Sorting by Created date' do
- it 'sorts the project list' do
- select_dropdown_option '#filtered-search-sorting-dropdown', 'Created date'
-
- expect_to_see_projects(desc_sorted_project_names)
-
- click_sort_direction
-
- expect_to_see_projects(desc_sorted_project_names.reverse)
- end
- end
-
- context 'Sorting by Stars' do
- it 'sorts the project list' do
- select_dropdown_option '#filtered-search-sorting-dropdown', 'Stars'
-
- expect_to_see_projects(desc_sorted_project_names)
-
- click_sort_direction
-
- expect_to_see_projects(desc_sorted_project_names.reverse)
- end
- end
- end
- end
end
diff --git a/spec/features/discussion_comments/commit_spec.rb b/spec/features/discussion_comments/commit_spec.rb
index 261e9fb9f3b..5407542dfc6 100644
--- a/spec/features/discussion_comments/commit_spec.rb
+++ b/spec/features/discussion_comments/commit_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Thread Comments Commit', :js do
+RSpec.describe 'Thread Comments Commit', :js, feature_category: :source_code_management do
include RepoHelpers
let(:user) { create(:user) }
diff --git a/spec/features/discussion_comments/issue_spec.rb b/spec/features/discussion_comments/issue_spec.rb
index ebb57b37918..90be3f0760d 100644
--- a/spec/features/discussion_comments/issue_spec.rb
+++ b/spec/features/discussion_comments/issue_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Thread Comments Issue', :js do
+RSpec.describe 'Thread Comments Issue', :js, feature_category: :source_code_management do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:issue) { create(:issue, project: project) }
diff --git a/spec/features/discussion_comments/merge_request_spec.rb b/spec/features/discussion_comments/merge_request_spec.rb
index a90ff3721d3..64395a44e57 100644
--- a/spec/features/discussion_comments/merge_request_spec.rb
+++ b/spec/features/discussion_comments/merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Thread Comments Merge Request', :js do
+RSpec.describe 'Thread Comments Merge Request', :js, feature_category: :source_code_management do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/features/discussion_comments/snippets_spec.rb b/spec/features/discussion_comments/snippets_spec.rb
index ca0a6d6e1c5..a703c880737 100644
--- a/spec/features/discussion_comments/snippets_spec.rb
+++ b/spec/features/discussion_comments/snippets_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Thread Comments Snippet', :js do
+RSpec.describe 'Thread Comments Snippet', :js, feature_category: :source_code_management do
let_it_be(:user) { create(:user) }
before do
diff --git a/spec/features/display_system_header_and_footer_bar_spec.rb b/spec/features/display_system_header_and_footer_bar_spec.rb
index 0979371a574..22fd0987418 100644
--- a/spec/features/display_system_header_and_footer_bar_spec.rb
+++ b/spec/features/display_system_header_and_footer_bar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Display system header and footer bar' do
+RSpec.describe 'Display system header and footer bar', feature_category: :not_owned do
let(:header_message) { "Foo" }
let(:footer_message) { "Bar" }
diff --git a/spec/features/error_pages_spec.rb b/spec/features/error_pages_spec.rb
index 8dc9e5ade46..6a322fd53d4 100644
--- a/spec/features/error_pages_spec.rb
+++ b/spec/features/error_pages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Error Pages', :js do
+RSpec.describe 'Error Pages', :js, feature_category: :error_tracking do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
diff --git a/spec/features/error_tracking/user_filters_errors_by_status_spec.rb b/spec/features/error_tracking/user_filters_errors_by_status_spec.rb
index 2ac43f67f64..168c4f330ca 100644
--- a/spec/features/error_tracking/user_filters_errors_by_status_spec.rb
+++ b/spec/features/error_tracking/user_filters_errors_by_status_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'When a user filters Sentry errors by status', :js, :use_clean_rails_memory_store_caching, :sidekiq_inline do
+RSpec.describe 'When a user filters Sentry errors by status', :js, :use_clean_rails_memory_store_caching, :sidekiq_inline,
+feature_category: :error_tracking do
include_context 'sentry error tracking context feature'
let_it_be(:issues_response_body) { fixture_file('sentry/issues_sample_response.json') }
diff --git a/spec/features/error_tracking/user_searches_sentry_errors_spec.rb b/spec/features/error_tracking/user_searches_sentry_errors_spec.rb
index 40718deed75..6026b42f7de 100644
--- a/spec/features/error_tracking/user_searches_sentry_errors_spec.rb
+++ b/spec/features/error_tracking/user_searches_sentry_errors_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'When a user searches for Sentry errors', :js, :use_clean_rails_memory_store_caching, :sidekiq_inline do
+RSpec.describe 'When a user searches for Sentry errors', :js, :use_clean_rails_memory_store_caching, :sidekiq_inline,
+feature_category: :error_tracking do
include_context 'sentry error tracking context feature'
let_it_be(:issues_response_body) { fixture_file('sentry/issues_sample_response.json') }
diff --git a/spec/features/error_tracking/user_sees_error_details_spec.rb b/spec/features/error_tracking/user_sees_error_details_spec.rb
index ecbb3fe0412..d7676d90d21 100644
--- a/spec/features/error_tracking/user_sees_error_details_spec.rb
+++ b/spec/features/error_tracking/user_sees_error_details_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'View error details page', :js, :use_clean_rails_memory_store_caching, :sidekiq_inline do
+RSpec.describe 'View error details page', :js, :use_clean_rails_memory_store_caching, :sidekiq_inline,
+feature_category: :error_tracking do
include_context 'sentry error tracking context feature'
context 'with current user as project owner' do
diff --git a/spec/features/error_tracking/user_sees_error_index_spec.rb b/spec/features/error_tracking/user_sees_error_index_spec.rb
index 21f9e688e3f..b7dfb6afc18 100644
--- a/spec/features/error_tracking/user_sees_error_index_spec.rb
+++ b/spec/features/error_tracking/user_sees_error_index_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'View error index page', :js, :use_clean_rails_memory_store_caching, :sidekiq_inline do
+RSpec.describe 'View error index page', :js, :use_clean_rails_memory_store_caching, :sidekiq_inline,
+feature_category: :error_tracking do
include_context 'sentry error tracking context feature'
let_it_be(:issues_response_body) { fixture_file('sentry/issues_sample_response.json') }
diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb
index c3096677a73..1f09b01ddec 100644
--- a/spec/features/expand_collapse_diffs_spec.rb
+++ b/spec/features/expand_collapse_diffs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Expand and collapse diffs', :js do
+RSpec.describe 'Expand and collapse diffs', :js, feature_category: :source_code_management do
let(:branch) { 'expand-collapse-diffs' }
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/features/explore/groups_list_spec.rb b/spec/features/explore/groups_list_spec.rb
index ba09cc20154..3ffa0dc5b64 100644
--- a/spec/features/explore/groups_list_spec.rb
+++ b/spec/features/explore/groups_list_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Explore Groups page', :js do
+RSpec.describe 'Explore Groups page', :js, feature_category: :subgroups do
let!(:user) { create :user }
let!(:group) { create(:group) }
let!(:public_group) { create(:group, :public) }
diff --git a/spec/features/explore/groups_spec.rb b/spec/features/explore/groups_spec.rb
index 201dc24b359..458f83dffb4 100644
--- a/spec/features/explore/groups_spec.rb
+++ b/spec/features/explore/groups_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Explore Groups', :js do
+RSpec.describe 'Explore Groups', :js, feature_category: :subgroups do
let(:user) { create :user }
let(:group) { create :group }
let!(:private_project) do
diff --git a/spec/features/explore/topics_spec.rb b/spec/features/explore/topics_spec.rb
index f0c57c2417a..b5787a2dba8 100644
--- a/spec/features/explore/topics_spec.rb
+++ b/spec/features/explore/topics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Explore Topics' do
+RSpec.describe 'Explore Topics', feature_category: :users do
context 'when no topics exist' do
it 'renders empty message', :aggregate_failures do
visit topics_explore_projects_path
diff --git a/spec/features/explore/user_explores_projects_spec.rb b/spec/features/explore/user_explores_projects_spec.rb
index c082ff1fb0c..f54a51c9ac9 100644
--- a/spec/features/explore/user_explores_projects_spec.rb
+++ b/spec/features/explore/user_explores_projects_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User explores projects' do
+RSpec.describe 'User explores projects', feature_category: :users do
context 'when some projects exist' do
let_it_be(:archived_project) { create(:project, :archived) }
let_it_be(:internal_project) { create(:project, :internal) }
@@ -35,8 +35,6 @@ RSpec.describe 'User explores projects' do
before do
sign_in(user)
-
- stub_feature_flags(project_list_filter_bar: false)
end
shared_examples 'empty search results' do
diff --git a/spec/features/file_uploads/attachment_spec.rb b/spec/features/file_uploads/attachment_spec.rb
index 41da0e9fbe0..cff0c0b52b4 100644
--- a/spec/features/file_uploads/attachment_spec.rb
+++ b/spec/features/file_uploads/attachment_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Upload an attachment', :api, :js do
+RSpec.describe 'Upload an attachment', :api, :js, feature_category: :projects do
include_context 'file upload requests helpers'
let_it_be(:project) { create(:project) }
diff --git a/spec/features/file_uploads/ci_artifact_spec.rb b/spec/features/file_uploads/ci_artifact_spec.rb
index 4f3b6c90ad4..420329cc952 100644
--- a/spec/features/file_uploads/ci_artifact_spec.rb
+++ b/spec/features/file_uploads/ci_artifact_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Upload ci artifact', :api, :js do
+RSpec.describe 'Upload ci artifact', :api, :js, feature_category: :build_artifacts do
include_context 'file upload requests helpers'
let_it_be(:user) { create(:user, :admin) }
diff --git a/spec/features/file_uploads/git_lfs_spec.rb b/spec/features/file_uploads/git_lfs_spec.rb
index 8d15c5c33f7..6af4bef2ec4 100644
--- a/spec/features/file_uploads/git_lfs_spec.rb
+++ b/spec/features/file_uploads/git_lfs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Upload a git lfs object', :js do
+RSpec.describe 'Upload a git lfs object', :js, feature_category: :source_code_management do
include_context 'file upload requests helpers'
let_it_be(:project) { create(:project) }
diff --git a/spec/features/file_uploads/graphql_add_design_spec.rb b/spec/features/file_uploads/graphql_add_design_spec.rb
index 17fbf5f6838..0b61c952b55 100644
--- a/spec/features/file_uploads/graphql_add_design_spec.rb
+++ b/spec/features/file_uploads/graphql_add_design_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Upload a design through graphQL', :js do
+RSpec.describe 'Upload a design through graphQL', :js, feature_category: :design_management do
include_context 'file upload requests helpers'
let_it_be(:query) do
diff --git a/spec/features/file_uploads/group_import_spec.rb b/spec/features/file_uploads/group_import_spec.rb
index a8592f99bd6..f5082e31c06 100644
--- a/spec/features/file_uploads/group_import_spec.rb
+++ b/spec/features/file_uploads/group_import_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Upload a group export archive', :api, :js do
+RSpec.describe 'Upload a group export archive', :api, :js, feature_category: :subgroups do
include_context 'file upload requests helpers'
let_it_be(:user) { create(:user, :admin) }
diff --git a/spec/features/file_uploads/maven_package_spec.rb b/spec/features/file_uploads/maven_package_spec.rb
index 70302142fa2..8a8bac53613 100644
--- a/spec/features/file_uploads/maven_package_spec.rb
+++ b/spec/features/file_uploads/maven_package_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Upload a maven package', :api, :js do
+RSpec.describe 'Upload a maven package', :api, :js, feature_category: :package_registry do
include_context 'file upload requests helpers'
let_it_be(:project) { create(:project) }
diff --git a/spec/features/file_uploads/multipart_invalid_uploads_spec.rb b/spec/features/file_uploads/multipart_invalid_uploads_spec.rb
index cff8b4e61a5..c4c5eb6b74b 100644
--- a/spec/features/file_uploads/multipart_invalid_uploads_spec.rb
+++ b/spec/features/file_uploads/multipart_invalid_uploads_spec.rb
@@ -2,11 +2,11 @@
require 'spec_helper'
-RSpec.describe 'Invalid uploads that must be rejected', :api, :js do
+RSpec.describe 'Invalid uploads that must be rejected', :api, :js, feature_category: :package_registry do
include_context 'file upload requests helpers'
let_it_be(:project) { create(:project) }
- let_it_be(:user) { create(:user, :admin) }
+ let_it_be(:user) { project.owner }
let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
context 'invalid upload key', :capybara_ignore_server_errors do
diff --git a/spec/features/file_uploads/nuget_package_spec.rb b/spec/features/file_uploads/nuget_package_spec.rb
index cbffd34d4ab..0dc48114e9a 100644
--- a/spec/features/file_uploads/nuget_package_spec.rb
+++ b/spec/features/file_uploads/nuget_package_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Upload a nuget package', :api, :js do
+RSpec.describe 'Upload a nuget package', :api, :js, feature_category: :package_registry do
include_context 'file upload requests helpers'
let_it_be(:project) { create(:project) }
diff --git a/spec/features/file_uploads/project_import_spec.rb b/spec/features/file_uploads/project_import_spec.rb
index 82b6f490d2a..c261834206d 100644
--- a/spec/features/file_uploads/project_import_spec.rb
+++ b/spec/features/file_uploads/project_import_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Upload a project export archive', :api, :js do
+RSpec.describe 'Upload a project export archive', :api, :js, feature_category: :projects do
include_context 'file upload requests helpers'
let_it_be(:user) { create(:user, :admin) }
diff --git a/spec/features/file_uploads/rubygem_package_spec.rb b/spec/features/file_uploads/rubygem_package_spec.rb
index f91fb407b28..9c4512e4eab 100644
--- a/spec/features/file_uploads/rubygem_package_spec.rb
+++ b/spec/features/file_uploads/rubygem_package_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Upload a RubyGems package', :api, :js do
+RSpec.describe 'Upload a RubyGems package', :api, :js, feature_category: :package_registry do
include_context 'file upload requests helpers'
let_it_be(:project) { create(:project) }
diff --git a/spec/features/file_uploads/user_avatar_spec.rb b/spec/features/file_uploads/user_avatar_spec.rb
index 34cfb4a4128..06501e09866 100644
--- a/spec/features/file_uploads/user_avatar_spec.rb
+++ b/spec/features/file_uploads/user_avatar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Upload a user avatar', :js do
+RSpec.describe 'Upload a user avatar', :js, feature_category: :users do
let_it_be(:user, reload: true) { create(:user) }
let(:file) { fixture_file_upload('spec/fixtures/banana_sample.gif') }
diff --git a/spec/features/frequently_visited_projects_and_groups_spec.rb b/spec/features/frequently_visited_projects_and_groups_spec.rb
index 7fbbc4dfc85..50e20910e16 100644
--- a/spec/features/frequently_visited_projects_and_groups_spec.rb
+++ b/spec/features/frequently_visited_projects_and_groups_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Frequently visited items', :js do
+RSpec.describe 'Frequently visited items', :js, feature_category: :not_owned do
include Spec::Support::Helpers::Features::TopNavSpecHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/gitlab_experiments_spec.rb b/spec/features/gitlab_experiments_spec.rb
index af14b6e2e95..c1417f6f7c5 100644
--- a/spec/features/gitlab_experiments_spec.rb
+++ b/spec/features/gitlab_experiments_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Gitlab::Experiment", :js do
+RSpec.describe "Gitlab::Experiment", :js, feature_category: :experimentation_activation do
# This is part of a set of tests that ensure that tracking remains
# consistent at the front end layer. Since we don't want to actually
# introduce an experiment in real code, we're going to simulate it
diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb
index 2e63ec2d4f2..7c55551e9c3 100644
--- a/spec/features/global_search_spec.rb
+++ b/spec/features/global_search_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Global search', :js do
+RSpec.describe 'Global search', :js, feature_category: :global_search do
include AfterNextHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/graphiql_spec.rb b/spec/features/graphiql_spec.rb
index 7729cdaa362..34c1797b6ba 100644
--- a/spec/features/graphiql_spec.rb
+++ b/spec/features/graphiql_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'GraphiQL' do
+RSpec.describe 'GraphiQL', feature_category: :integrations do
context 'without relative_url_root' do
before do
visit '/-/graphql-explorer'
diff --git a/spec/features/graphql_known_operations_spec.rb b/spec/features/graphql_known_operations_spec.rb
index 80214307be3..5b2205a4440 100644
--- a/spec/features/graphql_known_operations_spec.rb
+++ b/spec/features/graphql_known_operations_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
# We need to distinguish between known and unknown GraphQL operations. This spec
# tests that we set up Gitlab::Graphql::KnownOperations.default which requires
# integration of FE queries, webpack plugin, and BE.
-RSpec.describe 'Graphql known operations', :js do
+RSpec.describe 'Graphql known operations', :js, feature_category: :integrations do
around do |example|
# Let's make sure we aren't receiving or leaving behind any side-effects
# https://gitlab.com/gitlab-org/gitlab/-/jobs/1743294100
diff --git a/spec/features/group_variables_spec.rb b/spec/features/group_variables_spec.rb
index e2c659d7dfe..117f50aefc6 100644
--- a/spec/features/group_variables_spec.rb
+++ b/spec/features/group_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group variables', :js do
+RSpec.describe 'Group variables', :js, feature_category: :pipeline_authoring do
let(:user) { create(:user) }
let(:group) { create(:group) }
let!(:variable) { create(:ci_group_variable, key: 'test_key', value: 'test_value', masked: true, group: group) }
diff --git a/spec/features/groups/activity_spec.rb b/spec/features/groups/activity_spec.rb
index 5bac80959b1..7e592b3f48b 100644
--- a/spec/features/groups/activity_spec.rb
+++ b/spec/features/groups/activity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group activity page' do
+RSpec.describe 'Group activity page', feature_category: :subgroups do
let(:user) { create(:group_member, :developer, user: create(:user), group: group).user }
let(:group) { create(:group) }
let(:path) { activity_group_path(group) }
diff --git a/spec/features/groups/board_sidebar_spec.rb b/spec/features/groups/board_sidebar_spec.rb
index 10ef28f3fbc..8216bc3249d 100644
--- a/spec/features/groups/board_sidebar_spec.rb
+++ b/spec/features/groups/board_sidebar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group Issue Boards', :js do
+RSpec.describe 'Group Issue Boards', :js, feature_category: :subgroups do
include BoardHelpers
let(:group) { create(:group) }
diff --git a/spec/features/groups/board_spec.rb b/spec/features/groups/board_spec.rb
index aece6d790b5..11ec38f637b 100644
--- a/spec/features/groups/board_spec.rb
+++ b/spec/features/groups/board_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group Boards' do
+RSpec.describe 'Group Boards', feature_category: :team_planning do
include DragTo
include MobileHelpers
include BoardHelpers
@@ -35,7 +35,7 @@ RSpec.describe 'Group Boards' do
page.within("[data-testid='project-select-dropdown']") do
find('button.gl-dropdown-toggle').click
- find('.gl-new-dropdown-item button').click
+ find('.gl-dropdown-item button').click
end
click_button 'Create issue'
diff --git a/spec/features/groups/clusters/user_spec.rb b/spec/features/groups/clusters/user_spec.rb
index 6b512323d4d..3e565dd8eab 100644
--- a/spec/features/groups/clusters/user_spec.rb
+++ b/spec/features/groups/clusters/user_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User Cluster', :js do
+RSpec.describe 'User Cluster', :js, feature_category: :users do
include GoogleApi::CloudPlatformHelpers
let(:group) { create(:group) }
diff --git a/spec/features/groups/container_registry_spec.rb b/spec/features/groups/container_registry_spec.rb
index 7bef2dc9416..11f94967aaf 100644
--- a/spec/features/groups/container_registry_spec.rb
+++ b/spec/features/groups/container_registry_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Container Registry', :js do
+RSpec.describe 'Container Registry', :js, feature_category: :container_registry do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
@@ -97,8 +97,6 @@ RSpec.describe 'Container Registry', :js do
expect(find('.modal .modal-title')).to have_content _('Remove tag')
find('.modal .modal-footer .btn-danger').click
end
-
- it_behaves_like 'rejecting tags destruction for an importing repository on', tags: ['latest']
end
end
diff --git a/spec/features/groups/crm/contacts/create_spec.rb b/spec/features/groups/crm/contacts/create_spec.rb
index b10b2afe35c..860cadd322d 100644
--- a/spec/features/groups/crm/contacts/create_spec.rb
+++ b/spec/features/groups/crm/contacts/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Create a CRM contact', :js do
+RSpec.describe 'Create a CRM contact', :js, feature_category: :service_desk do
let(:user) { create(:user) }
let(:group) { create(:group, :crm_enabled) }
let!(:organization) { create(:organization, group: group, name: 'GitLab') }
diff --git a/spec/features/groups/dependency_proxy_for_containers_spec.rb b/spec/features/groups/dependency_proxy_for_containers_spec.rb
index ae721e7b91f..c0456140291 100644
--- a/spec/features/groups/dependency_proxy_for_containers_spec.rb
+++ b/spec/features/groups/dependency_proxy_for_containers_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group Dependency Proxy for containers', :js do
+RSpec.describe 'Group Dependency Proxy for containers', :js, feature_category: :dependency_proxy do
include DependencyProxyHelpers
include_context 'file upload requests helpers'
diff --git a/spec/features/groups/dependency_proxy_spec.rb b/spec/features/groups/dependency_proxy_spec.rb
index af9c4a40729..05984d40ea6 100644
--- a/spec/features/groups/dependency_proxy_spec.rb
+++ b/spec/features/groups/dependency_proxy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group Dependency Proxy' do
+RSpec.describe 'Group Dependency Proxy', feature_category: :dependency_proxy do
let(:owner) { create(:user) }
let(:reporter) { create(:user) }
let(:group) { create(:group) }
diff --git a/spec/features/groups/empty_states_spec.rb b/spec/features/groups/empty_states_spec.rb
index f1a8f97461a..a37c40f50e0 100644
--- a/spec/features/groups/empty_states_spec.rb
+++ b/spec/features/groups/empty_states_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group empty states' do
+RSpec.describe 'Group empty states', feature_category: :subgroups do
let(:group) { create(:group) }
let(:user) { create(:group_member, :developer, user: create(:user), group: group).user }
diff --git a/spec/features/groups/group_page_with_external_authorization_service_spec.rb b/spec/features/groups/group_page_with_external_authorization_service_spec.rb
index 59a7feb813b..dce5b67d694 100644
--- a/spec/features/groups/group_page_with_external_authorization_service_spec.rb
+++ b/spec/features/groups/group_page_with_external_authorization_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'The group page' do
+RSpec.describe 'The group page', feature_category: :subgroups do
include ExternalAuthorizationServiceHelpers
let(:user) { create(:user) }
diff --git a/spec/features/groups/group_runners_spec.rb b/spec/features/groups/group_runners_spec.rb
index c9d1c69e9e1..ab53ef7c470 100644
--- a/spec/features/groups/group_runners_spec.rb
+++ b/spec/features/groups/group_runners_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Group Runners" do
+RSpec.describe "Group Runners", feature_category: :runner_fleet do
include Spec::Support::Helpers::Features::RunnersHelpers
include Spec::Support::Helpers::ModalHelpers
diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb
index 81ff0088e1e..fe1b0909c06 100644
--- a/spec/features/groups/group_settings_spec.rb
+++ b/spec/features/groups/group_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Edit group settings' do
+RSpec.describe 'Edit group settings', feature_category: :subgroups do
let(:user) { create(:user) }
let(:group) { create(:group, path: 'foo') }
@@ -147,7 +147,7 @@ RSpec.describe 'Edit group settings' do
selected_group.add_owner(user)
end
- it 'can successfully transfer the group' do
+ it 'can successfully transfer the group', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/384966' do
visit edit_group_path(selected_group)
page.within('[data-testid="transfer-locations-dropdown"]') do
diff --git a/spec/features/groups/import_export/connect_instance_spec.rb b/spec/features/groups/import_export/connect_instance_spec.rb
index ae03e64cf59..11cc4bb9b37 100644
--- a/spec/features/groups/import_export/connect_instance_spec.rb
+++ b/spec/features/groups/import_export/connect_instance_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Import/Export - Connect to another instance', :js do
+RSpec.describe 'Import/Export - Connect to another instance', :js, feature_category: :importers do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
@@ -10,74 +10,96 @@ RSpec.describe 'Import/Export - Connect to another instance', :js do
group.add_owner(user)
end
- before do
- gitlab_sign_in(user)
+ context 'when importing group by direct transfer is enabled' do
+ before do
+ stub_application_setting(bulk_import_enabled: true)
- visit new_group_path
+ open_import_group
+ end
- click_link 'Import group'
- end
+ context 'when the user provides valid credentials' do
+ source_url = 'https://gitlab.com'
- context 'when the user provides valid credentials' do
- source_url = 'https://gitlab.com'
+ include_context 'bulk imports requests context', source_url
- include_context 'bulk imports requests context', source_url
+ it 'successfully connects to remote instance' do
+ pat = 'demo-pat'
- it 'successfully connects to remote instance' do
- pat = 'demo-pat'
+ expect(page).to have_content 'Import groups from another instance of GitLab'
+ expect(page).to have_content 'Not all related objects are migrated'
- expect(page).to have_content 'Import groups from another instance of GitLab'
- expect(page).to have_content 'Not all related objects are migrated'
+ fill_in :bulk_import_gitlab_url, with: source_url
+ fill_in :bulk_import_gitlab_access_token, with: pat
- fill_in :bulk_import_gitlab_url, with: source_url
- fill_in :bulk_import_gitlab_access_token, with: pat
+ click_on 'Connect instance'
- click_on 'Connect instance'
+ expect(page).to have_content 'Showing 1-1 of 42 groups that you own from %{url}' % { url: source_url }
+ expect(page).to have_content 'stub-group'
- expect(page).to have_content 'Showing 1-1 of 42 groups that you own from %{url}' % { url: source_url }
- expect(page).to have_content 'stub-group'
+ visit '/'
- visit '/'
+ wait_for_all_requests
+ end
+ end
+
+ context 'when the user provides invalid url' do
+ it 'reports an error' do
+ source_url = 'invalid-url'
+ pat = 'demo-pat'
+
+ fill_in :bulk_import_gitlab_url, with: source_url
+ fill_in :bulk_import_gitlab_access_token, with: pat
- wait_for_all_requests
+ click_on 'Connect instance'
+
+ expect(page).to have_content 'Specified URL cannot be used'
+ end
end
- end
- context 'when the user provides invalid url' do
- it 'reports an error' do
- source_url = 'invalid-url'
- pat = 'demo-pat'
+ context 'when the user does not fill in source URL' do
+ it 'reports an error' do
+ pat = 'demo-pat'
- fill_in :bulk_import_gitlab_url, with: source_url
- fill_in :bulk_import_gitlab_access_token, with: pat
+ fill_in :bulk_import_gitlab_access_token, with: pat
- click_on 'Connect instance'
+ click_on 'Connect instance'
- expect(page).to have_content 'Specified URL cannot be used'
+ expect(page).to have_content 'Please fill in GitLab source URL'
+ end
end
- end
- context 'when the user does not fill in source URL' do
- it 'reports an error' do
- pat = 'demo-pat'
+ context 'when the user does not fill in access token' do
+ it 'reports an error' do
+ source_url = 'https://gitlab.com'
- fill_in :bulk_import_gitlab_access_token, with: pat
+ fill_in :bulk_import_gitlab_url, with: source_url
- click_on 'Connect instance'
+ click_on 'Connect instance'
- expect(page).to have_content 'Please fill in GitLab source URL'
+ expect(page).to have_content 'Please fill in your personal access token'
+ end
end
end
- context 'when the user does not fill in access token' do
- it 'reports an error' do
- source_url = 'https://gitlab.com'
-
- fill_in :bulk_import_gitlab_url, with: source_url
+ context 'when importing group by direct transfer is disabled' do
+ before do
+ stub_application_setting(bulk_import_enabled: false)
- click_on 'Connect instance'
+ open_import_group
+ end
- expect(page).to have_content 'Please fill in your personal access token'
+ it 'renders fields and button disabled' do
+ expect(page).to have_field('GitLab source URL', disabled: true)
+ expect(page).to have_field('Personal access token', disabled: true)
+ expect(page).to have_button('Connect instance', disabled: true)
end
end
+
+ def open_import_group
+ gitlab_sign_in(user)
+
+ visit new_group_path
+
+ click_link 'Import group'
+ end
end
diff --git a/spec/features/groups/import_export/export_file_spec.rb b/spec/features/groups/import_export/export_file_spec.rb
index e3cb1ad77a7..885cfa0f595 100644
--- a/spec/features/groups/import_export/export_file_spec.rb
+++ b/spec/features/groups/import_export/export_file_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group Export', :js do
+RSpec.describe 'Group Export', :js, feature_category: :importers do
include ExportFileHelper
let_it_be(:user) { create(:user) }
diff --git a/spec/features/groups/import_export/import_file_spec.rb b/spec/features/groups/import_export/import_file_spec.rb
index b69b8bf2c19..f66062b9ac3 100644
--- a/spec/features/groups/import_export/import_file_spec.rb
+++ b/spec/features/groups/import_export/import_file_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Import/Export - Group Import', :js do
+RSpec.describe 'Import/Export - Group Import', :js, feature_category: :importers do
let_it_be(:user) { create(:user) }
let_it_be(:import_path) { "#{Dir.tmpdir}/group_import_spec" }
diff --git a/spec/features/groups/import_export/migration_history_spec.rb b/spec/features/groups/import_export/migration_history_spec.rb
index 243bdcc13a9..f851c5e2ec5 100644
--- a/spec/features/groups/import_export/migration_history_spec.rb
+++ b/spec/features/groups/import_export/migration_history_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Import/Export - GitLab migration history', :js do
+RSpec.describe 'Import/Export - GitLab migration history', :js, feature_category: :importers do
let_it_be(:user) { create(:user) }
let_it_be(:user_import_1) { create(:bulk_import, user: user) }
diff --git a/spec/features/groups/integrations/user_activates_mattermost_slash_command_spec.rb b/spec/features/groups/integrations/user_activates_mattermost_slash_command_spec.rb
index 02aa418cd73..fbdd760f7fb 100644
--- a/spec/features/groups/integrations/user_activates_mattermost_slash_command_spec.rb
+++ b/spec/features/groups/integrations/user_activates_mattermost_slash_command_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates the group-level Mattermost Slash Command integration', :js do
+RSpec.describe 'User activates the group-level Mattermost Slash Command integration', :js, feature_category: :build do
include_context 'group integration activation'
before do
diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb
index d4e88505118..00c0d4c3ebe 100644
--- a/spec/features/groups/issues_spec.rb
+++ b/spec/features/groups/issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group issues page' do
+RSpec.describe 'Group issues page', feature_category: :subgroups do
include FilteredSearchHelpers
include DragTo
@@ -11,6 +11,10 @@ RSpec.describe 'Group issues page' do
let(:project_with_issues_disabled) { create(:project, :issues_disabled, group: group) }
let(:path) { issues_group_path(group) }
+ before do
+ stub_feature_flags(or_issuable_queries: false)
+ end
+
context 'with shared examples', :js do
let(:issuable) { create(:issue, project: project, title: "this is my created issuable") }
diff --git a/spec/features/groups/labels/create_spec.rb b/spec/features/groups/labels/create_spec.rb
index 19433e612ff..5b57e670c1d 100644
--- a/spec/features/groups/labels/create_spec.rb
+++ b/spec/features/groups/labels/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Create a group label' do
+RSpec.describe 'Create a group label', feature_category: :team_planning do
let(:user) { create(:user) }
let(:group) { create(:group) }
diff --git a/spec/features/groups/labels/edit_spec.rb b/spec/features/groups/labels/edit_spec.rb
index cf1729af97d..2cbe44e11bf 100644
--- a/spec/features/groups/labels/edit_spec.rb
+++ b/spec/features/groups/labels/edit_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Edit group label' do
+RSpec.describe 'Edit group label', feature_category: :team_planning do
include Spec::Support::Helpers::ModalHelpers
let(:user) { create(:user) }
diff --git a/spec/features/groups/labels/index_spec.rb b/spec/features/groups/labels/index_spec.rb
index 68f03368989..ea27fa2c5d9 100644
--- a/spec/features/groups/labels/index_spec.rb
+++ b/spec/features/groups/labels/index_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group labels' do
+RSpec.describe 'Group labels', feature_category: :team_planning do
let(:user) { create(:user) }
let(:group) { create(:group) }
let!(:label) { create(:group_label, group: group) }
diff --git a/spec/features/groups/labels/search_labels_spec.rb b/spec/features/groups/labels/search_labels_spec.rb
index fbb0acfb923..478d35951f9 100644
--- a/spec/features/groups/labels/search_labels_spec.rb
+++ b/spec/features/groups/labels/search_labels_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Search for labels', :js do
+RSpec.describe 'Search for labels', :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:group) { create(:group) }
let!(:label1) { create(:group_label, title: 'Foo', description: 'Lorem ipsum', group: group) }
diff --git a/spec/features/groups/labels/sort_labels_spec.rb b/spec/features/groups/labels/sort_labels_spec.rb
index 9d05703aae6..c2410246fe1 100644
--- a/spec/features/groups/labels/sort_labels_spec.rb
+++ b/spec/features/groups/labels/sort_labels_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Sort labels', :js do
+RSpec.describe 'Sort labels', :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:group) { create(:group) }
let!(:label1) { create(:group_label, title: 'Foo', description: 'Lorem ipsum', group: group) }
diff --git a/spec/features/groups/labels/subscription_spec.rb b/spec/features/groups/labels/subscription_spec.rb
index 231c4b33bee..4d391074e62 100644
--- a/spec/features/groups/labels/subscription_spec.rb
+++ b/spec/features/groups/labels/subscription_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Labels subscription' do
+RSpec.describe 'Labels subscription', feature_category: :team_planning do
let(:user) { create(:user) }
let(:group) { create(:group) }
let!(:label1) { create(:group_label, group: group, title: 'foo') }
diff --git a/spec/features/groups/labels/user_sees_links_to_issuables_spec.rb b/spec/features/groups/labels/user_sees_links_to_issuables_spec.rb
index b0508633065..4caf5ba5314 100644
--- a/spec/features/groups/labels/user_sees_links_to_issuables_spec.rb
+++ b/spec/features/groups/labels/user_sees_links_to_issuables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Groups > Labels > User sees links to issuables' do
+RSpec.describe 'Groups > Labels > User sees links to issuables', feature_category: :team_planning do
let_it_be(:group) { create(:group, :public) }
before do
diff --git a/spec/features/groups/members/filter_members_spec.rb b/spec/features/groups/members/filter_members_spec.rb
index 917b35659a6..dc33bb11bea 100644
--- a/spec/features/groups/members/filter_members_spec.rb
+++ b/spec/features/groups/members/filter_members_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Groups > Members > Filter members', :js do
+RSpec.describe 'Groups > Members > Filter members', :js, feature_category: :subgroups do
include Spec::Support::Helpers::Features::MembersHelpers
let(:user) { create(:user) }
diff --git a/spec/features/groups/members/leave_group_spec.rb b/spec/features/groups/members/leave_group_spec.rb
index 66f251c859a..cfb1b24bccb 100644
--- a/spec/features/groups/members/leave_group_spec.rb
+++ b/spec/features/groups/members/leave_group_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Groups > Members > Leave group' do
+RSpec.describe 'Groups > Members > Leave group', feature_category: :subgroups do
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::ModalHelpers
diff --git a/spec/features/groups/members/list_members_spec.rb b/spec/features/groups/members/list_members_spec.rb
index b81949da85d..1aea5a76b41 100644
--- a/spec/features/groups/members/list_members_spec.rb
+++ b/spec/features/groups/members/list_members_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Groups > Members > List members', :js do
+RSpec.describe 'Groups > Members > List members', :js, feature_category: :subgroups do
include Spec::Support::Helpers::Features::MembersHelpers
let(:user1) { create(:user, name: 'John Doe') }
diff --git a/spec/features/groups/members/manage_groups_spec.rb b/spec/features/groups/members/manage_groups_spec.rb
index e4252e2f3aa..ee8786a2e36 100644
--- a/spec/features/groups/members/manage_groups_spec.rb
+++ b/spec/features/groups/members/manage_groups_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Groups > Members > Manage groups', :js do
+RSpec.describe 'Groups > Members > Manage groups', :js, feature_category: :subgroups do
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::Features::InviteMembersModalHelper
include Spec::Support::Helpers::ModalHelpers
diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb
index 5f28afc23f1..4211f2b6265 100644
--- a/spec/features/groups/members/manage_members_spec.rb
+++ b/spec/features/groups/members/manage_members_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Groups > Members > Manage members' do
+RSpec.describe 'Groups > Members > Manage members', feature_category: :subgroups do
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::Features::InviteMembersModalHelper
include Spec::Support::Helpers::ModalHelpers
@@ -72,7 +72,7 @@ RSpec.describe 'Groups > Members > Manage members' do
visit group_group_members_path(group)
- invite_member(user1.name, role: 'Reporter', refresh: false)
+ invite_member(user1.name, role: 'Reporter')
invite_modal = page.find(invite_modal_selector)
expect(invite_modal).to have_content("not authorized to update member")
diff --git a/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb
index 86185b8dd32..e9f80b05fa7 100644
--- a/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb
+++ b/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js do
+RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js, feature_category: :subgroups do
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::Features::InviteMembersModalHelper
diff --git a/spec/features/groups/members/master_manages_access_requests_spec.rb b/spec/features/groups/members/master_manages_access_requests_spec.rb
index 2a17e7d2a5c..951dc59feca 100644
--- a/spec/features/groups/members/master_manages_access_requests_spec.rb
+++ b/spec/features/groups/members/master_manages_access_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Groups > Members > Maintainer manages access requests' do
+RSpec.describe 'Groups > Members > Maintainer manages access requests', feature_category: :subgroups do
it_behaves_like 'Maintainer manages access requests' do
let(:entity) { create(:group, :public) }
let(:members_page_path) { group_group_members_path(entity) }
diff --git a/spec/features/groups/members/request_access_spec.rb b/spec/features/groups/members/request_access_spec.rb
index f806c7d3704..35eb085a195 100644
--- a/spec/features/groups/members/request_access_spec.rb
+++ b/spec/features/groups/members/request_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Groups > Members > Request access' do
+RSpec.describe 'Groups > Members > Request access', feature_category: :subgroups do
let(:user) { create(:user) }
let(:owner) { create(:user) }
let(:group) { create(:group, :public) }
diff --git a/spec/features/groups/members/search_members_spec.rb b/spec/features/groups/members/search_members_spec.rb
index fe5fed307d7..6b2896b194c 100644
--- a/spec/features/groups/members/search_members_spec.rb
+++ b/spec/features/groups/members/search_members_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Search group member', :js do
+RSpec.describe 'Search group member', :js, feature_category: :subgroups do
include Spec::Support::Helpers::Features::MembersHelpers
let(:user) { create :user }
diff --git a/spec/features/groups/members/sort_members_spec.rb b/spec/features/groups/members/sort_members_spec.rb
index bf8e64fa1e2..4e9adda5f2b 100644
--- a/spec/features/groups/members/sort_members_spec.rb
+++ b/spec/features/groups/members/sort_members_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Groups > Members > Sort members', :js do
+RSpec.describe 'Groups > Members > Sort members', :js, feature_category: :subgroups do
include Spec::Support::Helpers::Features::MembersHelpers
let(:owner) { create(:user, name: 'John Doe', created_at: 5.days.ago, last_activity_on: Date.today) }
diff --git a/spec/features/groups/members/tabs_spec.rb b/spec/features/groups/members/tabs_spec.rb
index 2e9f332c0d6..2dc116842b3 100644
--- a/spec/features/groups/members/tabs_spec.rb
+++ b/spec/features/groups/members/tabs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Groups > Members > Tabs', :js do
+RSpec.describe 'Groups > Members > Tabs', :js, feature_category: :subgroups do
using RSpec::Parameterized::TableSyntax
shared_examples 'active "Members" tab' do
diff --git a/spec/features/groups/merge_requests_spec.rb b/spec/features/groups/merge_requests_spec.rb
index 296b839c8fc..87f1f422e90 100644
--- a/spec/features/groups/merge_requests_spec.rb
+++ b/spec/features/groups/merge_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group merge requests page' do
+RSpec.describe 'Group merge requests page', feature_category: :code_review do
include FilteredSearchHelpers
let(:path) { merge_requests_group_path(group) }
diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb
index 92a40459737..a70a1e2e70b 100644
--- a/spec/features/groups/milestone_spec.rb
+++ b/spec/features/groups/milestone_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group milestones' do
+RSpec.describe 'Group milestones', feature_category: :subgroups do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project_empty_repo, group: group) }
let_it_be(:user) { create(:group_member, :maintainer, user: create(:user), group: group).user }
diff --git a/spec/features/groups/milestones/gfm_autocomplete_spec.rb b/spec/features/groups/milestones/gfm_autocomplete_spec.rb
index 1fec6091f1e..8df097dde88 100644
--- a/spec/features/groups/milestones/gfm_autocomplete_spec.rb
+++ b/spec/features/groups/milestones/gfm_autocomplete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'GFM autocomplete', :js do
+RSpec.describe 'GFM autocomplete', :js, feature_category: :team_planning do
let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
let_it_be(:group) { create(:group, name: 'Ancestor') }
let_it_be(:project) { create(:project, :repository, group: group) }
diff --git a/spec/features/groups/milestones_sorting_spec.rb b/spec/features/groups/milestones_sorting_spec.rb
index 6f3fc72775f..5543938957a 100644
--- a/spec/features/groups/milestones_sorting_spec.rb
+++ b/spec/features/groups/milestones_sorting_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Milestones sorting', :js do
+RSpec.describe 'Milestones sorting', :js, feature_category: :team_planning do
let(:group) { create(:group) }
let!(:project) { create(:project_empty_repo, group: group) }
let!(:other_project) { create(:project_empty_repo, group: group) }
diff --git a/spec/features/groups/navbar_spec.rb b/spec/features/groups/navbar_spec.rb
index b3fb563a202..180ccab78bc 100644
--- a/spec/features/groups/navbar_spec.rb
+++ b/spec/features/groups/navbar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group navbar' do
+RSpec.describe 'Group navbar', feature_category: :navigation do
include NavbarStructureHelper
include WikiHelpers
diff --git a/spec/features/groups/new_group_page_spec.rb b/spec/features/groups/new_group_page_spec.rb
index 6a8af9c31fd..662ef734299 100644
--- a/spec/features/groups/new_group_page_spec.rb
+++ b/spec/features/groups/new_group_page_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'New group page', :js do
+RSpec.describe 'New group page', :js, feature_category: :subgroups do
let(:user) { create(:user) }
let(:group) { create(:group) }
diff --git a/spec/features/groups/packages_spec.rb b/spec/features/groups/packages_spec.rb
index 26338b03349..dd238657fbc 100644
--- a/spec/features/groups/packages_spec.rb
+++ b/spec/features/groups/packages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group Packages' do
+RSpec.describe 'Group Packages', feature_category: :package_registry do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
diff --git a/spec/features/groups/settings/access_tokens_spec.rb b/spec/features/groups/settings/access_tokens_spec.rb
index 198d3a40df2..1bee3be1ddb 100644
--- a/spec/features/groups/settings/access_tokens_spec.rb
+++ b/spec/features/groups/settings/access_tokens_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group > Settings > Access Tokens', :js do
+RSpec.describe 'Group > Settings > Access Tokens', :js, feature_category: :authentication_and_authorization do
include Spec::Support::Helpers::ModalHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/groups/settings/ci_cd_spec.rb b/spec/features/groups/settings/ci_cd_spec.rb
index 50c481c115c..a1acb73178b 100644
--- a/spec/features/groups/settings/ci_cd_spec.rb
+++ b/spec/features/groups/settings/ci_cd_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group CI/CD settings' do
+RSpec.describe 'Group CI/CD settings', feature_category: :continuous_integration do
include WaitForRequests
let_it_be(:user) { create(:user) }
@@ -23,11 +23,6 @@ RSpec.describe 'Group CI/CD settings' do
visit group_settings_ci_cd_path(group)
end
- it 'displays the new group runners view banner' do
- expect(page).to have_content(s_('Runners|New group runners view'))
- expect(page).to have_link(href: group_runners_path(group))
- end
-
it 'has "Enable shared runners for this group" toggle', :js do
expect(shared_runners_toggle).to have_content(_('Enable shared runners for this group'))
end
diff --git a/spec/features/groups/settings/group_badges_spec.rb b/spec/features/groups/settings/group_badges_spec.rb
index 5bf736cc7ce..07c8451f8fb 100644
--- a/spec/features/groups/settings/group_badges_spec.rb
+++ b/spec/features/groups/settings/group_badges_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group Badges' do
+RSpec.describe 'Group Badges', feature_category: :subgroups do
include WaitForRequests
let(:user) { create(:user) }
diff --git a/spec/features/groups/settings/manage_applications_spec.rb b/spec/features/groups/settings/manage_applications_spec.rb
index 277471cb304..e7b87cda506 100644
--- a/spec/features/groups/settings/manage_applications_spec.rb
+++ b/spec/features/groups/settings/manage_applications_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User manages applications' do
+RSpec.describe 'User manages applications', feature_category: :subgroups do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:new_application_path) { group_settings_applications_path(group) }
diff --git a/spec/features/groups/settings/packages_and_registries_spec.rb b/spec/features/groups/settings/packages_and_registries_spec.rb
index 7f3f5775559..60aad8452ce 100644
--- a/spec/features/groups/settings/packages_and_registries_spec.rb
+++ b/spec/features/groups/settings/packages_and_registries_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group Package and registry settings' do
+RSpec.describe 'Group Package and registry settings', feature_category: :package_registry do
include WaitForRequests
let(:user) { create(:user) }
diff --git a/spec/features/groups/settings/repository_spec.rb b/spec/features/groups/settings/repository_spec.rb
index cd7dcbdb28d..c65a8268a18 100644
--- a/spec/features/groups/settings/repository_spec.rb
+++ b/spec/features/groups/settings/repository_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group Repository settings', :js do
+RSpec.describe 'Group Repository settings', :js, feature_category: :source_code_management do
include WaitForRequests
let_it_be(:user) { create(:user) }
diff --git a/spec/features/groups/settings/user_searches_in_settings_spec.rb b/spec/features/groups/settings/user_searches_in_settings_spec.rb
index fe0dd7cec9a..374ac236e20 100644
--- a/spec/features/groups/settings/user_searches_in_settings_spec.rb
+++ b/spec/features/groups/settings/user_searches_in_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches group settings', :js do
+RSpec.describe 'User searches group settings', :js, feature_category: :subgroups do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
diff --git a/spec/features/groups/share_lock_spec.rb b/spec/features/groups/share_lock_spec.rb
index d8207899e24..2f5a5e6ba16 100644
--- a/spec/features/groups/share_lock_spec.rb
+++ b/spec/features/groups/share_lock_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group share with group lock' do
+RSpec.describe 'Group share with group lock', feature_category: :subgroups do
let(:root_owner) { create(:user) }
let(:root_group) { create(:group) }
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
index d814906a274..c0af6080d0f 100644
--- a/spec/features/groups/show_spec.rb
+++ b/spec/features/groups/show_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group show page' do
+RSpec.describe 'Group show page', feature_category: :subgroups do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
diff --git a/spec/features/groups/user_browse_projects_group_page_spec.rb b/spec/features/groups/user_browse_projects_group_page_spec.rb
index 73fde7cafe5..38b879bb5b2 100644
--- a/spec/features/groups/user_browse_projects_group_page_spec.rb
+++ b/spec/features/groups/user_browse_projects_group_page_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User browse group projects page' do
+RSpec.describe 'User browse group projects page', feature_category: :subgroups do
let(:user) { create :user }
let(:group) { create :group }
diff --git a/spec/features/groups/user_sees_package_sidebar_spec.rb b/spec/features/groups/user_sees_package_sidebar_spec.rb
index ee216488232..64422f5cca5 100644
--- a/spec/features/groups/user_sees_package_sidebar_spec.rb
+++ b/spec/features/groups/user_sees_package_sidebar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Groups > sidebar' do
+RSpec.describe 'Groups > sidebar', feature_category: :subgroups do
let(:user) { create(:user) }
let(:group) { create(:group) }
diff --git a/spec/features/groups/user_sees_users_dropdowns_in_issuables_list_spec.rb b/spec/features/groups/user_sees_users_dropdowns_in_issuables_list_spec.rb
index 4e4c0e509b0..e5e30ed1a55 100644
--- a/spec/features/groups/user_sees_users_dropdowns_in_issuables_list_spec.rb
+++ b/spec/features/groups/user_sees_users_dropdowns_in_issuables_list_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Groups > User sees users dropdowns in issuables list', :js do
+RSpec.describe 'Groups > User sees users dropdowns in issuables list', :js, feature_category: :subgroups do
include FilteredSearchHelpers
let(:group) { create(:group) }
@@ -11,6 +11,7 @@ RSpec.describe 'Groups > User sees users dropdowns in issuables list', :js do
let!(:project) { create(:project, group: group) }
before do
+ stub_feature_flags(or_issuable_queries: false)
group.add_developer(user_in_dropdown)
sign_in(user_in_dropdown)
end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 4e02f6f7ca2..8806d1c2219 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group' do
+RSpec.describe 'Group', feature_category: :subgroups do
let(:user) { create(:user) }
before do
@@ -350,10 +350,10 @@ RSpec.describe 'Group' do
visit path
end
- it_behaves_like 'dirty submit form', [{ form: '.js-general-settings-form', input: 'input[name="group[name]"]' },
- { form: '.js-general-settings-form', input: '#group_visibility_level_0' },
- { form: '.js-general-permissions-form', input: '#group_request_access_enabled' },
- { form: '.js-general-permissions-form', input: 'input[name="group[two_factor_grace_period]"]' }]
+ it_behaves_like 'dirty submit form', [{ form: '.js-general-settings-form', input: 'input[name="group[name]"]', submit: 'button[type="submit"]' },
+ { form: '.js-general-settings-form', input: '#group_visibility_level_0', submit: 'button[type="submit"]' },
+ { form: '.js-general-permissions-form', input: '#group_request_access_enabled', submit: 'button[type="submit"]' },
+ { form: '.js-general-permissions-form', input: 'input[name="group[two_factor_grace_period]"]', submit: 'button[type="submit"]' }]
it 'saves new settings' do
page.within('.gs-general') do
diff --git a/spec/features/help_dropdown_spec.rb b/spec/features/help_dropdown_spec.rb
index a9c014a9408..a5c9221ad26 100644
--- a/spec/features/help_dropdown_spec.rb
+++ b/spec/features/help_dropdown_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Help Dropdown", :js do
+RSpec.describe "Help Dropdown", :js, feature_category: :not_owned do
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb
index eef48d09f32..6c0901d6169 100644
--- a/spec/features/help_pages_spec.rb
+++ b/spec/features/help_pages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Help Pages' do
+RSpec.describe 'Help Pages', feature_category: :not_owned do
describe 'Get the main help page' do
before do
allow(File).to receive(:read).and_call_original
diff --git a/spec/features/ics/dashboard_issues_spec.rb b/spec/features/ics/dashboard_issues_spec.rb
index 1d0ea495757..7115bd7dff7 100644
--- a/spec/features/ics/dashboard_issues_spec.rb
+++ b/spec/features/ics/dashboard_issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dashboard Issues Calendar Feed' do
+RSpec.describe 'Dashboard Issues Calendar Feed', feature_category: :team_planning do
describe 'GET /issues' do
let!(:user) do
user = create(:user, email: 'private1@example.com')
diff --git a/spec/features/ics/group_issues_spec.rb b/spec/features/ics/group_issues_spec.rb
index f29c39ad4ef..70ec156a7b0 100644
--- a/spec/features/ics/group_issues_spec.rb
+++ b/spec/features/ics/group_issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group Issues Calendar Feed' do
+RSpec.describe 'Group Issues Calendar Feed', feature_category: :subgroups do
describe 'GET /issues' do
let!(:user) do
user = create(:user, email: 'private1@example.com')
diff --git a/spec/features/ics/project_issues_spec.rb b/spec/features/ics/project_issues_spec.rb
index 771748060bb..4bbd966d72a 100644
--- a/spec/features/ics/project_issues_spec.rb
+++ b/spec/features/ics/project_issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project Issues Calendar Feed' do
+RSpec.describe 'Project Issues Calendar Feed', feature_category: :projects do
describe 'GET /issues' do
let!(:user) do
user = create(:user, email: 'private1@example.com')
diff --git a/spec/features/ide/clientside_preview_csp_spec.rb b/spec/features/ide/clientside_preview_csp_spec.rb
index 849fdb0a44c..04427a5c294 100644
--- a/spec/features/ide/clientside_preview_csp_spec.rb
+++ b/spec/features/ide/clientside_preview_csp_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'IDE Clientside Preview CSP' do
+RSpec.describe 'IDE Clientside Preview CSP', feature_category: :web_ide do
let_it_be(:user) { create(:user) }
shared_context 'disable feature' do
diff --git a/spec/features/ide/static_object_external_storage_csp_spec.rb b/spec/features/ide/static_object_external_storage_csp_spec.rb
index 421b5db0dbb..701be2626e5 100644
--- a/spec/features/ide/static_object_external_storage_csp_spec.rb
+++ b/spec/features/ide/static_object_external_storage_csp_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Static Object External Storage Content Security Policy' do
+RSpec.describe 'Static Object External Storage Content Security Policy', feature_category: :web_ide do
let_it_be(:user) { create(:user) }
shared_context 'disable feature' do
diff --git a/spec/features/ide/user_opens_merge_request_spec.rb b/spec/features/ide/user_opens_merge_request_spec.rb
index 4ffa5212970..0074b4b1eb0 100644
--- a/spec/features/ide/user_opens_merge_request_spec.rb
+++ b/spec/features/ide/user_opens_merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'IDE merge request', :js do
+RSpec.describe 'IDE merge request', :js, feature_category: :web_ide do
let(:merge_request) { create(:merge_request, :simple, source_project: project) }
let(:project) { create(:project, :public, :repository) }
let(:user) { project.first_owner }
diff --git a/spec/features/ide_spec.rb b/spec/features/ide_spec.rb
index 1f6d34efc0f..2ca8d3f7156 100644
--- a/spec/features/ide_spec.rb
+++ b/spec/features/ide_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'IDE', :js do
+RSpec.describe 'IDE', :js, feature_category: :web_ide do
include WebIdeSpecHelpers
let_it_be(:ide_iframe_selector) { '#ide iframe' }
@@ -46,10 +46,6 @@ RSpec.describe 'IDE', :js do
end
it_behaves_like 'legacy Web IDE'
-
- it 'does not show switch button' do
- expect(page).not_to have_button('Switch to new Web IDE')
- end
end
context 'with vscode feature flag on and use_legacy_web_ide=true' do
@@ -61,19 +57,6 @@ RSpec.describe 'IDE', :js do
end
it_behaves_like 'legacy Web IDE'
-
- describe 'when user switches to new Web IDE' do
- before do
- click_button('Switch to new Web IDE')
-
- # Confirm modal
- page.within('#confirmationModal') do
- click_button('Switch editors')
- end
- end
-
- it_behaves_like 'new Web IDE'
- end
end
describe 'sub-groups' do
diff --git a/spec/features/import/manifest_import_spec.rb b/spec/features/import/manifest_import_spec.rb
index 520cf850da2..bb3eb34637b 100644
--- a/spec/features/import/manifest_import_spec.rb
+++ b/spec/features/import/manifest_import_spec.rb
@@ -2,9 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Import multiple repositories by uploading a manifest file', :js do
- include Select2Helper
-
+RSpec.describe 'Import multiple repositories by uploading a manifest file', :js, feature_category: :importers do
let(:user) { create(:admin) }
let(:group) { create(:group) }
diff --git a/spec/features/incidents/incident_details_spec.rb b/spec/features/incidents/incident_details_spec.rb
index 7c24943eb6f..e1167285464 100644
--- a/spec/features/incidents/incident_details_spec.rb
+++ b/spec/features/incidents/incident_details_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Incident details', :js do
+RSpec.describe 'Incident details', :js, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
let_it_be(:incident) { create(:incident, project: project, author: developer, description: 'description') }
diff --git a/spec/features/incidents/incident_timeline_events_spec.rb b/spec/features/incidents/incident_timeline_events_spec.rb
index ef0eb27d310..3a73ea50247 100644
--- a/spec/features/incidents/incident_timeline_events_spec.rb
+++ b/spec/features/incidents/incident_timeline_events_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Incident timeline events', :js do
+RSpec.describe 'Incident timeline events', :js, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
let_it_be(:incident) { create(:incident, project: project) }
@@ -50,9 +50,9 @@ RSpec.describe 'Incident timeline events', :js do
it 'shows the confirmation modal and edits the event' do
click_button _('More actions')
- page.within '.gl-new-dropdown-contents' do
+ page.within '.gl-dropdown-contents' do
expect(page).to have_content(_('Edit'))
- page.find('.gl-new-dropdown-item-text-primary', text: _('Edit')).click
+ page.find('.gl-dropdown-item-text-primary', text: _('Edit')).click
end
expect(page).to have_selector('.common-note-form')
@@ -82,9 +82,9 @@ RSpec.describe 'Incident timeline events', :js do
it 'shows the confirmation modal and deletes the event' do
click_button _('More actions')
- page.within '.gl-new-dropdown-contents' do
+ page.within '.gl-dropdown-contents' do
expect(page).to have_content(_('Delete'))
- page.find('.gl-new-dropdown-item-text-primary', text: 'Delete').click
+ page.find('.gl-dropdown-item-text-primary', text: 'Delete').click
end
page.within '.modal' do
diff --git a/spec/features/incidents/incidents_list_spec.rb b/spec/features/incidents/incidents_list_spec.rb
index 3241e71f537..61983f9290c 100644
--- a/spec/features/incidents/incidents_list_spec.rb
+++ b/spec/features/incidents/incidents_list_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Incident Management index', :js do
+RSpec.describe 'Incident Management index', :js, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
let_it_be(:incident) { create(:incident, project: project) }
diff --git a/spec/features/incidents/user_creates_new_incident_spec.rb b/spec/features/incidents/user_creates_new_incident_spec.rb
index 685f6ab791a..bf5d7dac587 100644
--- a/spec/features/incidents/user_creates_new_incident_spec.rb
+++ b/spec/features/incidents/user_creates_new_incident_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Incident Management index', :js do
+RSpec.describe 'Incident Management index', :js, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:reporter) { create(:user) }
let_it_be(:guest) { create(:user) }
diff --git a/spec/features/incidents/user_filters_incidents_by_status_spec.rb b/spec/features/incidents/user_filters_incidents_by_status_spec.rb
index 661c737141b..f1f9dd45ff7 100644
--- a/spec/features/incidents/user_filters_incidents_by_status_spec.rb
+++ b/spec/features/incidents/user_filters_incidents_by_status_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User filters Incident Management table by status', :js do
+RSpec.describe 'User filters Incident Management table by status', :js, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
diff --git a/spec/features/incidents/user_searches_incidents_spec.rb b/spec/features/incidents/user_searches_incidents_spec.rb
index b8e3ff534c3..31a485801c4 100644
--- a/spec/features/incidents/user_searches_incidents_spec.rb
+++ b/spec/features/incidents/user_searches_incidents_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches Incident Management incidents', :js do
+RSpec.describe 'User searches Incident Management incidents', :js, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
let_it_be(:incident) { create(:incident, project: project) }
diff --git a/spec/features/incidents/user_uses_quick_actions_spec.rb b/spec/features/incidents/user_uses_quick_actions_spec.rb
index fce9eadd42f..3740f2fca47 100644
--- a/spec/features/incidents/user_uses_quick_actions_spec.rb
+++ b/spec/features/incidents/user_uses_quick_actions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Incidents > User uses quick actions', :js do
+RSpec.describe 'Incidents > User uses quick actions', :js, feature_category: :incident_management do
include Spec::Support::Helpers::Features::NotesHelpers
describe 'incident-only commands' do
diff --git a/spec/features/incidents/user_views_incident_spec.rb b/spec/features/incidents/user_views_incident_spec.rb
index 054a084ea9c..8216aca787a 100644
--- a/spec/features/incidents/user_views_incident_spec.rb
+++ b/spec/features/incidents/user_views_incident_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User views incident" do
+RSpec.describe "User views incident", feature_category: :incident_management do
let_it_be(:project) { create(:project_empty_repo, :public) }
let_it_be(:guest) { create(:user) }
let_it_be(:developer) { create(:user) }
@@ -57,7 +57,7 @@ RSpec.describe "User views incident" do
it 'shows incident actions', :js do
click_button 'Incident actions'
- expect(page).to have_link 'Report abuse'
+ expect(page).to have_link 'Report abuse to administrator'
end
end
end
diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb
index 34990a53b51..1091bea1ce3 100644
--- a/spec/features/invites_spec.rb
+++ b/spec/features/invites_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group or Project invitations', :aggregate_failures do
+RSpec.describe 'Group or Project invitations', :aggregate_failures, feature_category: :experimentation_expansion do
let_it_be(:owner) { create(:user, name: 'John Doe') }
let_it_be(:group) { create(:group, name: 'Owned') }
let_it_be(:project) { create(:project, :repository, namespace: group) }
@@ -155,11 +155,10 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
let(:new_user) { build_stubbed(:user) }
let(:invite_email) { new_user.email }
let(:group_invite) { create(:group_member, :invited, group: group, invite_email: invite_email, created_by: owner) }
- let(:send_email_confirmation) { true }
let(:extra_params) { { invite_type: Emails::Members::INITIAL_INVITE } }
before do
- stub_application_setting(send_user_confirmation_email: send_email_confirmation)
+ stub_application_setting_enum('email_confirmation_setting', 'hard')
end
context 'when registering using invitation email' do
@@ -181,7 +180,9 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
end
context 'email confirmation disabled' do
- let(:send_email_confirmation) { false }
+ before do
+ stub_application_setting_enum('email_confirmation_setting', 'off')
+ end
context 'the user signs up for an account with the invitation email address' do
it 'redirects to the most recent membership activity page with all the projects/groups invitations automatically accepted' do
@@ -213,6 +214,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
expect { fill_in_sign_up_form(new_user) }.not_to change { User.count }
expect(page).to have_content('prohibited this user from being saved')
expect(page).to have_current_path(user_registration_path, ignore_query: true)
+ expect(find_field('Email').value).to eq(group_invite.invite_email)
end
end
diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb
index a1e80586c05..350b0582565 100644
--- a/spec/features/issuables/issuable_list_spec.rb
+++ b/spec/features/issuables/issuable_list_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'issuable list', :js do
+RSpec.describe 'issuable list', :js, feature_category: :team_planning do
let(:project) { create(:project) }
let(:user) { create(:user) }
@@ -26,9 +26,9 @@ RSpec.describe 'issuable list', :js do
it "counts upvotes, downvotes and notes count for each #{issuable_type.to_s.humanize}" do
visit_issuable_list(issuable_type)
- expect(first('.issuable-upvotes')).to have_content(1)
- expect(first('.issuable-downvotes')).to have_content(1)
- expect(first('.issuable-comments')).to have_content(2)
+ expect(first('[data-testid="issuable-upvotes"]')).to have_content(1)
+ expect(first('[data-testid="issuable-downvotes"]')).to have_content(1)
+ expect(first('[data-testid="issuable-comments"]')).to have_content(2)
end
it 'sorts labels alphabetically' do
diff --git a/spec/features/issuables/markdown_references/internal_references_spec.rb b/spec/features/issuables/markdown_references/internal_references_spec.rb
index c5a8e9f367c..aeae76b1b77 100644
--- a/spec/features/issuables/markdown_references/internal_references_spec.rb
+++ b/spec/features/issuables/markdown_references/internal_references_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Internal references", :js do
+RSpec.describe "Internal references", :js, feature_category: :team_planning do
include Spec::Support::Helpers::Features::NotesHelpers
let(:private_project_user) { private_project.first_owner }
diff --git a/spec/features/issuables/markdown_references/jira_spec.rb b/spec/features/issuables/markdown_references/jira_spec.rb
index 66d0022f7e9..52464c6be8b 100644
--- a/spec/features/issuables/markdown_references/jira_spec.rb
+++ b/spec/features/issuables/markdown_references/jira_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Jira", :js do
+RSpec.describe "Jira", :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:actual_project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, target_project: actual_project, source_project: actual_project) }
diff --git a/spec/features/issuables/shortcuts_issuable_spec.rb b/spec/features/issuables/shortcuts_issuable_spec.rb
index 528420062dd..0190111b2f0 100644
--- a/spec/features/issuables/shortcuts_issuable_spec.rb
+++ b/spec/features/issuables/shortcuts_issuable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Blob shortcuts', :js do
+RSpec.describe 'Blob shortcuts', :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
let(:issue) { create(:issue, project: project, author: user) }
@@ -87,7 +87,7 @@ RSpec.describe 'Blob shortcuts', :js do
it "opens milestones dropdown for editing" do
find('body').native.send_key('m')
- expect(find('[data-testid="milestone-edit"]')).to have_selector('.gl-new-dropdown-inner')
+ expect(find('[data-testid="milestone-edit"]')).to have_selector('.gl-dropdown-inner')
end
end
diff --git a/spec/features/issuables/sorting_list_spec.rb b/spec/features/issuables/sorting_list_spec.rb
index 53723b39d5b..b5362267309 100644
--- a/spec/features/issuables/sorting_list_spec.rb
+++ b/spec/features/issuables/sorting_list_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'Sort Issuable List' do
+RSpec.describe 'Sort Issuable List', feature_category: :team_planning do
let(:project) { create(:project, :public) }
let(:first_created_issuable) { issuables.order_created_asc.first }
diff --git a/spec/features/issuables/user_sees_sidebar_spec.rb b/spec/features/issuables/user_sees_sidebar_spec.rb
index 66ed6044de6..2c0157ea9e0 100644
--- a/spec/features/issuables/user_sees_sidebar_spec.rb
+++ b/spec/features/issuables/user_sees_sidebar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issue Sidebar on Mobile' do
+RSpec.describe 'Issue Sidebar on Mobile', feature_category: :team_planning do
include MobileHelpers
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/issue_rebalancing_spec.rb b/spec/features/issue_rebalancing_spec.rb
index 686aa5eb1b6..3933f0ffcf8 100644
--- a/spec/features/issue_rebalancing_spec.rb
+++ b/spec/features/issue_rebalancing_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issue rebalancing' do
+RSpec.describe 'Issue rebalancing', feature_category: :team_planning do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:user) { create(:user) }
@@ -41,13 +41,13 @@ RSpec.describe 'Issue rebalancing' do
it 'shows an alert in project issues list with manual sort', :js do
visit project_issues_path(project, sort: 'relative_position')
- expect(page).to have_selector('.flash-notice', text: alert_message_regex, count: 1)
+ expect(page).to have_selector('.gl-alert-info', text: alert_message_regex, count: 1)
end
it 'shows an alert in group issues list with manual sort', :js do
visit issues_group_path(group, sort: 'relative_position')
- expect(page).to have_selector('.flash-notice', text: alert_message_regex, count: 1)
+ expect(page).to have_selector('.gl-alert-info', text: alert_message_regex, count: 1)
end
it 'does not show an alert in project issues list with other sorts' do
diff --git a/spec/features/issues/confidential_notes_spec.rb b/spec/features/issues/confidential_notes_spec.rb
index 858c054c803..d87c73da44d 100644
--- a/spec/features/issues/confidential_notes_spec.rb
+++ b/spec/features/issues/confidential_notes_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "Confidential notes on issues", :js do
+RSpec.describe "Confidential notes on issues", :js, feature_category: :team_planning do
it_behaves_like 'confidential notes on issuables' do
let_it_be(:issuable_parent) { create(:project) }
let_it_be(:issuable) { create(:issue, project: issuable_parent) }
diff --git a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
index a385e8a5fd0..0bdb5930f30 100644
--- a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
+++ b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Resolving all open threads in a merge request from an issue', :js do
+RSpec.describe 'Resolving all open threads in a merge request from an issue', :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
index 5ff61a52b21..3a32bd34af8 100644
--- a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
+++ b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Resolve an open thread in a merge request by creating an issue', :js do
+RSpec.describe 'Resolve an open thread in a merge request by creating an issue', :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, only_allow_merge_if_all_discussions_are_resolved: true) }
let(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/features/issues/csv_spec.rb b/spec/features/issues/csv_spec.rb
index 9fd171bf44b..8629201459f 100644
--- a/spec/features/issues/csv_spec.rb
+++ b/spec/features/issues/csv_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issues csv', :js do
+RSpec.describe 'Issues csv', :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:milestone) { create(:milestone, title: 'v1.0', project: project) }
diff --git a/spec/features/issues/discussion_lock_spec.rb b/spec/features/issues/discussion_lock_spec.rb
index 13f1742fbf6..33fc9a6fd96 100644
--- a/spec/features/issues/discussion_lock_spec.rb
+++ b/spec/features/issues/discussion_lock_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Discussion Lock', :js do
+RSpec.describe 'Discussion Lock', :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:issue) { create(:issue, project: project, author: user) }
let(:project) { create(:project, :public) }
diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
index 40b0bfd9aa4..a89c36a2b78 100644
--- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dropdown assignee', :js do
+RSpec.describe 'Dropdown assignee', :js, feature_category: :team_planning do
include FilteredSearchHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/features/issues/filtered_search/dropdown_author_spec.rb b/spec/features/issues/filtered_search/dropdown_author_spec.rb
index a67d114c6d1..b5d389b3bee 100644
--- a/spec/features/issues/filtered_search/dropdown_author_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_author_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dropdown author', :js do
+RSpec.describe 'Dropdown author', :js, feature_category: :team_planning do
include FilteredSearchHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/features/issues/filtered_search/dropdown_base_spec.rb b/spec/features/issues/filtered_search/dropdown_base_spec.rb
index 9e3e3d394cd..866b83a6319 100644
--- a/spec/features/issues/filtered_search/dropdown_base_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_base_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dropdown base', :js do
+RSpec.describe 'Dropdown base', :js, feature_category: :team_planning do
include FilteredSearchHelpers
let_it_be(:project) { create(:project) }
@@ -10,6 +10,7 @@ RSpec.describe 'Dropdown base', :js do
let_it_be(:issue) { create(:issue, project: project) }
before do
+ stub_feature_flags(or_issuable_queries: false)
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
index 78450a9c3f7..52c85942a7c 100644
--- a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dropdown emoji', :js do
+RSpec.describe 'Dropdown emoji', :js, feature_category: :team_planning do
include FilteredSearchHelpers
let_it_be(:project) { create(:project, :public) }
@@ -11,6 +11,7 @@ RSpec.describe 'Dropdown emoji', :js do
let_it_be(:award_emoji_star) { create(:award_emoji, name: 'star', user: user, awardable: issue) }
before do
+ stub_feature_flags(or_issuable_queries: false)
project.add_maintainer(user)
create_list(:award_emoji, 2, user: user, name: 'thumbsup')
create_list(:award_emoji, 1, user: user, name: 'thumbsdown')
diff --git a/spec/features/issues/filtered_search/dropdown_hint_spec.rb b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
index cbe917931aa..39034a40b1f 100644
--- a/spec/features/issues/filtered_search/dropdown_hint_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dropdown hint', :js do
+RSpec.describe 'Dropdown hint', :js, feature_category: :team_planning do
include FilteredSearchHelpers
let_it_be(:project) { create(:project, :public) }
diff --git a/spec/features/issues/filtered_search/dropdown_label_spec.rb b/spec/features/issues/filtered_search/dropdown_label_spec.rb
index 0ff56909ad1..a2eceb67841 100644
--- a/spec/features/issues/filtered_search/dropdown_label_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_label_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dropdown label', :js do
+RSpec.describe 'Dropdown label', :js, feature_category: :team_planning do
include FilteredSearchHelpers
let_it_be(:project) { create(:project) }
@@ -11,6 +11,7 @@ RSpec.describe 'Dropdown label', :js do
let_it_be(:label) { create(:label, project: project, title: 'bug-label') }
before do
+ stub_feature_flags(or_issuable_queries: false)
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
index 37d604106f1..d08eb29b5c0 100644
--- a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dropdown milestone', :js do
+RSpec.describe 'Dropdown milestone', :js, feature_category: :team_planning do
include FilteredSearchHelpers
let_it_be(:project) { create(:project) }
@@ -12,6 +12,7 @@ RSpec.describe 'Dropdown milestone', :js do
let_it_be(:issue) { create(:issue, project: project) }
before do
+ stub_feature_flags(or_issuable_queries: false)
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/issues/filtered_search/dropdown_release_spec.rb b/spec/features/issues/filtered_search/dropdown_release_spec.rb
index 08e20563c8e..5d9b8b04012 100644
--- a/spec/features/issues/filtered_search/dropdown_release_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_release_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Dropdown release', :js do
+RSpec.describe 'Dropdown release', :js, feature_category: :team_planning do
include FilteredSearchHelpers
let_it_be(:project) { create(:project) }
@@ -12,6 +12,7 @@ RSpec.describe 'Dropdown release', :js do
let_it_be(:issue) { create(:issue, project: project) }
before do
+ stub_feature_flags(or_issuable_queries: false)
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index e48df1b1c53..f67d5c40efd 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Filter issues', :js do
+RSpec.describe 'Filter issues', :js, feature_category: :team_planning do
include FilteredSearchHelpers
let(:project) { create(:project) }
diff --git a/spec/features/issues/filtered_search/recent_searches_spec.rb b/spec/features/issues/filtered_search/recent_searches_spec.rb
index cb17349dd43..2d9c73f2756 100644
--- a/spec/features/issues/filtered_search/recent_searches_spec.rb
+++ b/spec/features/issues/filtered_search/recent_searches_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Recent searches', :js do
+RSpec.describe 'Recent searches', :js, feature_category: :team_planning do
include FilteredSearchHelpers
let_it_be(:project_1) { create(:project, :public) }
diff --git a/spec/features/issues/filtered_search/search_bar_spec.rb b/spec/features/issues/filtered_search/search_bar_spec.rb
index e075547e326..c975df2a531 100644
--- a/spec/features/issues/filtered_search/search_bar_spec.rb
+++ b/spec/features/issues/filtered_search/search_bar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Search bar', :js do
+RSpec.describe 'Search bar', :js, feature_category: :team_planning do
include FilteredSearchHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/features/issues/filtered_search/visual_tokens_spec.rb b/spec/features/issues/filtered_search/visual_tokens_spec.rb
index 854b88c3f81..f25925ed33d 100644
--- a/spec/features/issues/filtered_search/visual_tokens_spec.rb
+++ b/spec/features/issues/filtered_search/visual_tokens_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Visual tokens', :js do
+RSpec.describe 'Visual tokens', :js, feature_category: :team_planning do
include FilteredSearchHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index fe591d7fe3a..2898c97c2e9 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'New/edit issue', :js do
+RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do
include ActionView::Helpers::JavaScriptHelper
let_it_be(:project) { create(:project, :repository) }
@@ -43,7 +43,7 @@ RSpec.describe 'New/edit issue', :js do
# To work around this, we have to hold on to and call to the original implementation manually.
original_issue_dropdown_options = FormHelper.instance_method(:assignees_dropdown_options)
allow_any_instance_of(FormHelper).to receive(:assignees_dropdown_options).and_wrap_original do |original, *args|
- options = original_issue_dropdown_options.bind(original.receiver).call(*args)
+ options = original_issue_dropdown_options.bind_call(original.receiver, *args)
options[:data][:per_page] = 2
options
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index fa4ce6fe1c1..2bd5373b715 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'GFM autocomplete', :js do
+RSpec.describe 'GFM autocomplete', :js, feature_category: :team_planning do
let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
let_it_be(:user2) { create(:user, name: 'Marge Simpson', username: 'msimpson') }
diff --git a/spec/features/issues/group_label_sidebar_spec.rb b/spec/features/issues/group_label_sidebar_spec.rb
index 8150f9c6faf..b26030fe8d0 100644
--- a/spec/features/issues/group_label_sidebar_spec.rb
+++ b/spec/features/issues/group_label_sidebar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group label on issue' do
+RSpec.describe 'Group label on issue', feature_category: :team_planning do
it 'renders link to the project issues page', :js do
group = create(:group)
project = create(:project, :public, namespace: group)
diff --git a/spec/features/issues/incident_issue_spec.rb b/spec/features/issues/incident_issue_spec.rb
index d6cde466d1b..2fba1ca9141 100644
--- a/spec/features/issues/incident_issue_spec.rb
+++ b/spec/features/issues/incident_issue_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Incident Detail', :js do
+RSpec.describe 'Incident Detail', :js, feature_category: :team_planning do
let_it_be(:project) { create(:project, :public) }
let_it_be(:payload) do
{
diff --git a/spec/features/issues/issue_detail_spec.rb b/spec/features/issues/issue_detail_spec.rb
index a253e6f4c86..44e9bbad1ba 100644
--- a/spec/features/issues/issue_detail_spec.rb
+++ b/spec/features/issues/issue_detail_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issue Detail', :js do
+RSpec.describe 'Issue Detail', :js, feature_category: :team_planning do
let_it_be_with_refind(:project) { create(:project, :public) }
let(:user) { create(:user) }
diff --git a/spec/features/issues/issue_header_spec.rb b/spec/features/issues/issue_header_spec.rb
index 165015013dd..090067fc4ac 100644
--- a/spec/features/issues/issue_header_spec.rb
+++ b/spec/features/issues/issue_header_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'issue header', :js do
+RSpec.describe 'issue header', :js, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
@@ -27,7 +27,7 @@ RSpec.describe 'issue header', :js do
it 'shows the "New related issue", "Report abuse", and "Delete issue" items', :aggregate_failures do
expect(page).to have_link 'New related issue'
- expect(page).to have_link 'Report abuse'
+ expect(page).to have_link 'Report abuse to administrator'
expect(page).to have_button 'Delete issue'
expect(page).not_to have_link 'Submit as spam'
end
@@ -71,7 +71,7 @@ RSpec.describe 'issue header', :js do
it 'does not show "Report abuse" link in dropdown' do
click_button 'Issue actions'
- expect(page).not_to have_link 'Report abuse'
+ expect(page).not_to have_link 'Report abuse to administrator'
end
end
end
@@ -116,7 +116,7 @@ RSpec.describe 'issue header', :js do
it 'only shows the "New related issue" and "Report abuse" items', :aggregate_failures do
expect(page).to have_link 'New related issue'
- expect(page).to have_link 'Report abuse'
+ expect(page).to have_link 'Report abuse to administrator'
expect(page).not_to have_link 'Submit as spam'
expect(page).not_to have_button 'Delete issue'
end
@@ -160,7 +160,7 @@ RSpec.describe 'issue header', :js do
it 'does not show "Report abuse" link in dropdown' do
click_button 'Issue actions'
- expect(page).not_to have_link 'Report abuse'
+ expect(page).not_to have_link 'Report abuse to administrator'
end
end
end
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index 6fa8a52a9c5..fa72acad8c6 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issue Sidebar' do
+RSpec.describe 'Issue Sidebar', feature_category: :team_planning do
include MobileHelpers
let_it_be(:group) { create(:group, :nested) }
diff --git a/spec/features/issues/issue_state_spec.rb b/spec/features/issues/issue_state_spec.rb
index d5a115433aa..758dafccb86 100644
--- a/spec/features/issues/issue_state_spec.rb
+++ b/spec/features/issues/issue_state_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'issue state', :js do
+RSpec.describe 'issue state', :js, feature_category: :team_planning do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
diff --git a/spec/features/issues/keyboard_shortcut_spec.rb b/spec/features/issues/keyboard_shortcut_spec.rb
index 4dbc5d8e01c..f91a0d4b057 100644
--- a/spec/features/issues/keyboard_shortcut_spec.rb
+++ b/spec/features/issues/keyboard_shortcut_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issues shortcut', :js do
+RSpec.describe 'Issues shortcut', :js, feature_category: :team_planning do
context 'New Issue shortcut' do
context 'issues are enabled' do
let(:project) { create(:project) }
diff --git a/spec/features/issues/markdown_toolbar_spec.rb b/spec/features/issues/markdown_toolbar_spec.rb
index aad5d319bc4..5cabaf16960 100644
--- a/spec/features/issues/markdown_toolbar_spec.rb
+++ b/spec/features/issues/markdown_toolbar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issue markdown toolbar', :js do
+RSpec.describe 'Issue markdown toolbar', :js, feature_category: :team_planning do
let_it_be(:project) { create(:project, :public) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:user) { create(:user) }
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
index 054b7b3855b..72c6e288168 100644
--- a/spec/features/issues/move_spec.rb
+++ b/spec/features/issues/move_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'issue move to another project' do
+RSpec.describe 'issue move to another project', feature_category: :team_planning do
let(:user) { create(:user) }
let(:old_project) { create(:project, :repository) }
let(:text) { 'Some issue description' }
diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb
index 5e02d5ad038..dae71481352 100644
--- a/spec/features/issues/note_polling_spec.rb
+++ b/spec/features/issues/note_polling_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issue notes polling', :js do
+RSpec.describe 'Issue notes polling', :js, feature_category: :team_planning do
include NoteInteractionHelpers
let(:project) { create(:project, :public) }
diff --git a/spec/features/issues/notes_on_issues_spec.rb b/spec/features/issues/notes_on_issues_spec.rb
index 4e98062e8b2..8d6262efa53 100644
--- a/spec/features/issues/notes_on_issues_spec.rb
+++ b/spec/features/issues/notes_on_issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Create notes on issues', :js do
+RSpec.describe 'Create notes on issues', :js, feature_category: :team_planning do
let(:user) { create(:user) }
def submit_comment(text)
diff --git a/spec/features/issues/related_issues_spec.rb b/spec/features/issues/related_issues_spec.rb
index 62127295a7c..f460b4b1c7f 100644
--- a/spec/features/issues/related_issues_spec.rb
+++ b/spec/features/issues/related_issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Related issues', :js do
+RSpec.describe 'Related issues', :js, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project_empty_repo, :public) }
diff --git a/spec/features/issues/resource_label_events_spec.rb b/spec/features/issues/resource_label_events_spec.rb
index e8158b3e2aa..531361b19af 100644
--- a/spec/features/issues/resource_label_events_spec.rb
+++ b/spec/features/issues/resource_label_events_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'List issue resource label events', :js do
+RSpec.describe 'List issue resource label events', :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project, author: user) }
diff --git a/spec/features/issues/rss_spec.rb b/spec/features/issues/rss_spec.rb
index e3faed81c73..36dffeded50 100644
--- a/spec/features/issues/rss_spec.rb
+++ b/spec/features/issues/rss_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project Issues RSS', :js do
+RSpec.describe 'Project Issues RSS', :js, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
diff --git a/spec/features/issues/service_desk_spec.rb b/spec/features/issues/service_desk_spec.rb
index 87cd00fac6b..922ab95538b 100644
--- a/spec/features/issues/service_desk_spec.rb
+++ b/spec/features/issues/service_desk_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Service Desk Issue Tracker', :js do
+RSpec.describe 'Service Desk Issue Tracker', :js, feature_category: :team_planning do
let(:project) { create(:project, :private, service_desk_enabled: true) }
let_it_be(:user) { create(:user) }
diff --git a/spec/features/issues/spam_akismet_issue_creation_spec.rb b/spec/features/issues/spam_akismet_issue_creation_spec.rb
index 4cc4c4cf607..7c62f141105 100644
--- a/spec/features/issues/spam_akismet_issue_creation_spec.rb
+++ b/spec/features/issues/spam_akismet_issue_creation_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Spam detection on issue creation', :js do
+RSpec.describe 'Spam detection on issue creation', :js, feature_category: :team_planning do
include StubENV
let(:project) { create(:project, :public) }
diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb
index 6a53c12eda3..2c537cefa5e 100644
--- a/spec/features/issues/todo_spec.rb
+++ b/spec/features/issues/todo_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Manually create a todo item from issue', :js do
+RSpec.describe 'Manually create a todo item from issue', :js, feature_category: :team_planning do
let!(:project) { create(:project) }
let!(:issue) { create(:issue, project: project) }
let!(:user) { create(:user) }
diff --git a/spec/features/issues/user_bulk_edits_issues_labels_spec.rb b/spec/features/issues/user_bulk_edits_issues_labels_spec.rb
index 2a201e0bc23..1fc6609d1f5 100644
--- a/spec/features/issues/user_bulk_edits_issues_labels_spec.rb
+++ b/spec/features/issues/user_bulk_edits_issues_labels_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issues > Labels bulk assignment' do
+RSpec.describe 'Issues > Labels bulk assignment', feature_category: :team_planning do
let(:user) { create(:user) }
let!(:project) { create(:project) }
let!(:bug) { create(:label, project: project, title: 'bug') }
diff --git a/spec/features/issues/user_bulk_edits_issues_spec.rb b/spec/features/issues/user_bulk_edits_issues_spec.rb
index d7fad355cb4..fc48bc4baf9 100644
--- a/spec/features/issues/user_bulk_edits_issues_spec.rb
+++ b/spec/features/issues/user_bulk_edits_issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Multiple issue updating from issues#index', :js do
+RSpec.describe 'Multiple issue updating from issues#index', :js, feature_category: :team_planning do
let!(:project) { create(:project) }
let!(:issue) { create(:issue, project: project) }
let!(:user) { create(:user) }
diff --git a/spec/features/issues/user_comments_on_issue_spec.rb b/spec/features/issues/user_comments_on_issue_spec.rb
index ef00e66af7e..59e1413fc97 100644
--- a/spec/features/issues/user_comments_on_issue_spec.rb
+++ b/spec/features/issues/user_comments_on_issue_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User comments on issue", :js do
+RSpec.describe "User comments on issue", :js, feature_category: :team_planning do
include Spec::Support::Helpers::Features::NotesHelpers
let_it_be(:project) { create(:project, :public) }
diff --git a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
index 5ba09703852..bbc14368d82 100644
--- a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
+++ b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User creates branch and merge request on issue page', :js do
+RSpec.describe 'User creates branch and merge request on issue page', :js, feature_category: :team_planning do
let(:membership_level) { :developer }
let(:user) { create(:user) }
let!(:project) { create(:project, :repository, :public) }
@@ -84,7 +84,7 @@ RSpec.describe 'User creates branch and merge request on issue page', :js do
wait_for_requests
- expect(page).to have_selector('.dropdown-toggle-text ', text: '1-cherry-coloured-funk')
+ expect(page).to have_selector('.ref-selector ', text: '1-cherry-coloured-funk')
expect(page).to have_current_path project_tree_path(project, '1-cherry-coloured-funk'), ignore_query: true
end
end
@@ -109,7 +109,7 @@ RSpec.describe 'User creates branch and merge request on issue page', :js do
wait_for_requests
- expect(page).to have_selector('.dropdown-toggle-text ', text: branch_name)
+ expect(page).to have_selector('.ref-selector', text: branch_name)
expect(page).to have_current_path project_tree_path(project, branch_name), ignore_query: true
end
end
diff --git a/spec/features/issues/user_creates_confidential_merge_request_spec.rb b/spec/features/issues/user_creates_confidential_merge_request_spec.rb
index 6b4526cd624..23fef5fa46e 100644
--- a/spec/features/issues/user_creates_confidential_merge_request_spec.rb
+++ b/spec/features/issues/user_creates_confidential_merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User creates confidential merge request on issue page', :js do
+RSpec.describe 'User creates confidential merge request on issue page', :js, feature_category: :team_planning do
include ProjectForksHelper
let(:user) { create(:user) }
diff --git a/spec/features/issues/user_creates_issue_by_email_spec.rb b/spec/features/issues/user_creates_issue_by_email_spec.rb
index c47f24ab836..d6d2b2a50f8 100644
--- a/spec/features/issues/user_creates_issue_by_email_spec.rb
+++ b/spec/features/issues/user_creates_issue_by_email_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issues > User creates issue by email' do
+RSpec.describe 'Issues > User creates issue by email', feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public) }
diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb
index 1d023a15159..a4b8cb91999 100644
--- a/spec/features/issues/user_creates_issue_spec.rb
+++ b/spec/features/issues/user_creates_issue_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User creates issue" do
+RSpec.describe "User creates issue", feature_category: :team_planning do
include DropzoneHelper
let_it_be(:project) { create(:project_empty_repo, :public) }
diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb
index 75df85f362f..223832a6ede 100644
--- a/spec/features/issues/user_edits_issue_spec.rb
+++ b/spec/features/issues/user_edits_issue_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "Issues > User edits issue", :js do
+RSpec.describe "Issues > User edits issue", :js, feature_category: :team_planning do
let_it_be(:project) { create(:project_empty_repo, :public) }
let_it_be(:project_with_milestones) { create(:project_empty_repo, :public) }
let_it_be(:user) { create(:user) }
@@ -416,7 +416,7 @@ RSpec.describe "Issues > User edits issue", :js do
find('.gl-form-input', visible: true).send_keys "\"#{milestones[0].title}\""
wait_for_requests
- page.within '.gl-new-dropdown-contents' do
+ page.within '.gl-dropdown-contents' do
expect(page).to have_content milestones[0].title
end
end
diff --git a/spec/features/issues/user_filters_issues_spec.rb b/spec/features/issues/user_filters_issues_spec.rb
index 2941ea6ec36..9f69e94b86c 100644
--- a/spec/features/issues/user_filters_issues_spec.rb
+++ b/spec/features/issues/user_filters_issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User filters issues', :js do
+RSpec.describe 'User filters issues', :js, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project_empty_repo, :public) }
diff --git a/spec/features/issues/user_interacts_with_awards_spec.rb b/spec/features/issues/user_interacts_with_awards_spec.rb
index a2dea7f048b..539e429534e 100644
--- a/spec/features/issues/user_interacts_with_awards_spec.rb
+++ b/spec/features/issues/user_interacts_with_awards_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User interacts with awards' do
+RSpec.describe 'User interacts with awards', feature_category: :team_planning do
include MobileHelpers
let(:user) { create(:user) }
@@ -136,6 +136,10 @@ RSpec.describe 'User interacts with awards' do
page.within('.note-actions') do
find('.note-emoji-button').click
end
+
+ # make sure emoji popup is visible
+ execute_script("window.scrollBy(0, 200)")
+
find('gl-emoji[data-name="8ball"]').click
wait_for_requests
diff --git a/spec/features/issues/user_resets_their_incoming_email_token_spec.rb b/spec/features/issues/user_resets_their_incoming_email_token_spec.rb
index 4580378dc8a..55c66eb8a39 100644
--- a/spec/features/issues/user_resets_their_incoming_email_token_spec.rb
+++ b/spec/features/issues/user_resets_their_incoming_email_token_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issues > User resets their incoming email token' do
+RSpec.describe 'Issues > User resets their incoming email token', feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public, namespace: user.namespace) }
let_it_be(:issue) { create(:issue, project: project) }
diff --git a/spec/features/issues/user_scrolls_to_deeplinked_note_spec.rb b/spec/features/issues/user_scrolls_to_deeplinked_note_spec.rb
index 1fa8f533869..f93fbd06964 100644
--- a/spec/features/issues/user_scrolls_to_deeplinked_note_spec.rb
+++ b/spec/features/issues/user_scrolls_to_deeplinked_note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User scrolls to deep-linked note' do
+RSpec.describe 'User scrolls to deep-linked note', feature_category: :team_planning do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:comment_1) { create(:note_on_issue, noteable: issue, project: project, note: 'written first') }
diff --git a/spec/features/issues/user_sees_breadcrumb_links_spec.rb b/spec/features/issues/user_sees_breadcrumb_links_spec.rb
index 4ec13533a8d..632999c5d49 100644
--- a/spec/features/issues/user_sees_breadcrumb_links_spec.rb
+++ b/spec/features/issues/user_sees_breadcrumb_links_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'New issue breadcrumb' do
+RSpec.describe 'New issue breadcrumb', feature_category: :team_planning do
let_it_be(:project, reload: true) { create(:project) }
let(:user) { project.creator }
diff --git a/spec/features/issues/user_sees_empty_state_spec.rb b/spec/features/issues/user_sees_empty_state_spec.rb
index b4c5a57de4f..5b95eb96e3b 100644
--- a/spec/features/issues/user_sees_empty_state_spec.rb
+++ b/spec/features/issues/user_sees_empty_state_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issues > User sees empty state', :js do
+RSpec.describe 'Issues > User sees empty state', :js, feature_category: :team_planning do
let_it_be(:project) { create(:project, :public) }
let_it_be(:user) { project.creator }
diff --git a/spec/features/issues/user_sees_live_update_spec.rb b/spec/features/issues/user_sees_live_update_spec.rb
index 7e4880f209e..860603ad546 100644
--- a/spec/features/issues/user_sees_live_update_spec.rb
+++ b/spec/features/issues/user_sees_live_update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issues > User sees live update', :js do
+RSpec.describe 'Issues > User sees live update', :js, feature_category: :team_planning do
let_it_be(:project) { create(:project, :public) }
let_it_be(:user) { project.creator }
diff --git a/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb b/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb
index 311818d2d15..b9a25f47da9 100644
--- a/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb
+++ b/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issues > Real-time sidebar', :js do
+RSpec.describe 'Issues > Real-time sidebar', :js, feature_category: :team_planning do
let_it_be(:project) { create(:project, :public) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:user) { create(:user) }
diff --git a/spec/features/issues/user_sorts_issue_comments_spec.rb b/spec/features/issues/user_sorts_issue_comments_spec.rb
index 4b38ce329b8..ca52e620ea7 100644
--- a/spec/features/issues/user_sorts_issue_comments_spec.rb
+++ b/spec/features/issues/user_sorts_issue_comments_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Comment sort direction' do
+RSpec.describe 'Comment sort direction', feature_category: :team_planning do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:comment_1) { create(:note_on_issue, noteable: issue, project: project, note: 'written first') }
diff --git a/spec/features/issues/user_sorts_issues_spec.rb b/spec/features/issues/user_sorts_issues_spec.rb
index 2716d742be3..206544b32a4 100644
--- a/spec/features/issues/user_sorts_issues_spec.rb
+++ b/spec/features/issues/user_sorts_issues_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User sorts issues" do
+RSpec.describe "User sorts issues", feature_category: :team_planning do
include SortingHelper
include IssueHelpers
diff --git a/spec/features/issues/user_toggles_subscription_spec.rb b/spec/features/issues/user_toggles_subscription_spec.rb
index 541bbc8a8e7..904fafdf56a 100644
--- a/spec/features/issues/user_toggles_subscription_spec.rb
+++ b/spec/features/issues/user_toggles_subscription_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User toggles subscription", :js do
+RSpec.describe "User toggles subscription", :js, feature_category: :team_planning do
let(:project) { create(:project_empty_repo, :public) }
let(:user) { create(:user) }
let(:user2) { create(:user) }
diff --git a/spec/features/issues/user_uses_quick_actions_spec.rb b/spec/features/issues/user_uses_quick_actions_spec.rb
index d458c991668..963f1c56fef 100644
--- a/spec/features/issues/user_uses_quick_actions_spec.rb
+++ b/spec/features/issues/user_uses_quick_actions_spec.rb
@@ -7,7 +7,7 @@ require 'spec_helper'
# for example, adding quick actions when creating the issue and checking DateTime formats on UI.
# Because this kind of spec takes more time to run there is no need to add new ones
# for each existing quick action unless they test something not tested by existing tests.
-RSpec.describe 'Issues > User uses quick actions', :js do
+RSpec.describe 'Issues > User uses quick actions', :js, feature_category: :team_planning do
include Spec::Support::Helpers::Features::NotesHelpers
context "issuable common quick actions" do
diff --git a/spec/features/issues/user_views_issue_spec.rb b/spec/features/issues/user_views_issue_spec.rb
index eca698bb2f4..17ff3e0c702 100644
--- a/spec/features/issues/user_views_issue_spec.rb
+++ b/spec/features/issues/user_views_issue_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User views issue" do
+RSpec.describe "User views issue", feature_category: :team_planning do
let_it_be(:project) { create(:project_empty_repo, :public) }
let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, project: project, description: "# Description header\n\n**Lorem** _ipsum_ dolor sit [amet](https://example.com)", author: user) }
diff --git a/spec/features/issues/user_views_issues_spec.rb b/spec/features/issues/user_views_issues_spec.rb
index 56afa7eb6ba..39d3dfbd487 100644
--- a/spec/features/issues/user_views_issues_spec.rb
+++ b/spec/features/issues/user_views_issues_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User views issues" do
+RSpec.describe "User views issues", feature_category: :team_planning do
let!(:closed_issue) { create(:closed_issue, project: project) }
let!(:open_issue1) { create(:issue, project: project) }
let!(:open_issue2) { create(:issue, project: project) }
diff --git a/spec/features/jira_connect/branches_spec.rb b/spec/features/jira_connect/branches_spec.rb
index c334a425849..489d3743a2a 100644
--- a/spec/features/jira_connect/branches_spec.rb
+++ b/spec/features/jira_connect/branches_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Create GitLab branches from Jira', :js do
+RSpec.describe 'Create GitLab branches from Jira', :js, feature_category: :integrations do
let_it_be(:alice) { create(:user, name: 'Alice') }
let_it_be(:bob) { create(:user, name: 'Bob') }
@@ -49,16 +49,11 @@ RSpec.describe 'Create GitLab branches from Jira', :js do
expect(page).to have_button('Create branch', disabled: false)
click_on 'master'
+ fill_in 'Search', with: source_branch
+ expect(page).not_to have_text(source_branch)
- within_dropdown do
- fill_in 'Search', with: source_branch
-
- expect(page).not_to have_text(source_branch)
-
- fill_in 'Search', with: 'master'
-
- expect(page).to have_text('master')
- end
+ fill_in 'Search', with: 'master'
+ expect(page).to have_text('master')
# Switch to project2
@@ -70,10 +65,13 @@ RSpec.describe 'Create GitLab branches from Jira', :js do
end
click_on 'master'
+ wait_for_requests
- within_dropdown do
- fill_in 'Search', with: source_branch
- click_on source_branch
+ fill_in 'Search', with: source_branch
+ wait_for_requests
+
+ within '[role="listbox"]' do
+ find('li', text: source_branch).click
end
fill_in 'Branch name', with: new_branch
diff --git a/spec/features/jira_connect/subscriptions_spec.rb b/spec/features/jira_connect/subscriptions_spec.rb
index 0468cfd70fc..8686234df01 100644
--- a/spec/features/jira_connect/subscriptions_spec.rb
+++ b/spec/features/jira_connect/subscriptions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Subscriptions Content Security Policy' do
+RSpec.describe 'Subscriptions Content Security Policy', feature_category: :integrations do
include ContentSecurityPolicyHelpers
let(:installation) { create(:jira_connect_installation) }
diff --git a/spec/features/jira_oauth_provider_authorize_spec.rb b/spec/features/jira_oauth_provider_authorize_spec.rb
index eb26440aff9..a542aaa7619 100644
--- a/spec/features/jira_oauth_provider_authorize_spec.rb
+++ b/spec/features/jira_oauth_provider_authorize_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'JIRA OAuth Provider' do
+RSpec.describe 'JIRA OAuth Provider', feature_category: :integrations do
describe 'JIRA DVCS OAuth Authorization' do
let(:application) { create(:oauth_application, redirect_uri: oauth_jira_dvcs_callback_url, scopes: 'read_user') }
diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb
index 2f22ac8b395..d6e607e80df 100644
--- a/spec/features/labels_hierarchy_spec.rb
+++ b/spec/features/labels_hierarchy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Labels Hierarchy', :js do
+RSpec.describe 'Labels Hierarchy', :js, feature_category: :team_planning do
include FilteredSearchHelpers
let!(:user) { create(:user) }
@@ -17,6 +17,7 @@ RSpec.describe 'Labels Hierarchy', :js do
let!(:project_label_1) { create(:label, project: project_1, title: 'Label_4') }
before do
+ stub_feature_flags(or_issuable_queries: false)
grandparent.add_owner(user)
sign_in(user)
diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb
index b5bf9279371..8073e7e9556 100644
--- a/spec/features/markdown/copy_as_gfm_spec.rb
+++ b/spec/features/markdown/copy_as_gfm_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Copy as GFM', :js do
+RSpec.describe 'Copy as GFM', :js, feature_category: :team_planning do
include MarkupHelper
include RepoHelpers
include ActionView::Helpers::JavaScriptHelper
@@ -26,7 +26,7 @@ RSpec.describe 'Copy as GFM', :js do
# by verifying (`html_to_gfm(gfm_to_html(gfm)) == gfm`) for a number of examples of GFM for every filter, using the `verify` helper.
# These are all in a single `it` for performance reasons.
- it 'works', :aggregate_failures do
+ it 'transforms HTML to GFM', :aggregate_failures do
verify(
'nesting',
'> 1. [x] **[$`2 + 2`$ {-=-}{+=+} 2^2 ~~:thumbsup:~~](http://google.com)**'
@@ -459,25 +459,25 @@ RSpec.describe 'Copy as GFM', :js do
</a>
</div>
<!---->
- <button type="button" class="btn qa-apply-btn js-apply-btn">Apply suggestion</button>
+ <button type="button" class="btn js-apply-btn">Apply suggestion</button>
</div>
<table class="mb-3 md-suggestion-diff js-syntax-highlight code white">
<tbody>
<tr class="line_holder old">
- <td class="diff-line-num old_line qa-old-diff-line-number old">9</td>
+ <td class="diff-line-num old_line old">9</td>
<td class="diff-line-num new_line old"></td>
<td class="line_content old"><span>Old
</span></td>
</tr>
<tr class="line_holder new">
<td class="diff-line-num old_line new"></td>
- <td class="diff-line-num new_line qa-new-diff-line-number new">9</td>
+ <td class="diff-line-num new_line new">9</td>
<td class="line_content new"><span>New
</span></td>
</tr>
<tr class="line_holder new">
<td class="diff-line-num old_line new"></td>
- <td class="diff-line-num new_line qa-new-diff-line-number new">10</td>
+ <td class="diff-line-num new_line new">10</td>
<td class="line_content new"><span> And newer
</span></td>
</tr>
diff --git a/spec/features/markdown/gitlab_flavored_markdown_spec.rb b/spec/features/markdown/gitlab_flavored_markdown_spec.rb
index 17fe2dab8f7..36b02b17924 100644
--- a/spec/features/markdown/gitlab_flavored_markdown_spec.rb
+++ b/spec/features/markdown/gitlab_flavored_markdown_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "GitLab Flavored Markdown" do
+RSpec.describe "GitLab Flavored Markdown", feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:issue) { create(:issue, project: project) }
diff --git a/spec/features/markdown/json_table_spec.rb b/spec/features/markdown/json_table_spec.rb
index 6b74dbac255..a9afbe1fc57 100644
--- a/spec/features/markdown/json_table_spec.rb
+++ b/spec/features/markdown/json_table_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Rendering json:table code block in markdown', :js do
+RSpec.describe 'Rendering json:table code block in markdown', :js, feature_category: :team_planning do
let_it_be(:project) { create(:project, :public) }
it 'creates table correctly' do
diff --git a/spec/features/markdown/keyboard_shortcuts_spec.rb b/spec/features/markdown/keyboard_shortcuts_spec.rb
index 82288af1f9f..cfb8e61689f 100644
--- a/spec/features/markdown/keyboard_shortcuts_spec.rb
+++ b/spec/features/markdown/keyboard_shortcuts_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Markdown keyboard shortcuts', :js do
+RSpec.describe 'Markdown keyboard shortcuts', :js, feature_category: :team_planning do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
diff --git a/spec/features/markdown/kroki_spec.rb b/spec/features/markdown/kroki_spec.rb
index f02f5d44244..dca00c5f2fa 100644
--- a/spec/features/markdown/kroki_spec.rb
+++ b/spec/features/markdown/kroki_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'kroki rendering', :js do
+RSpec.describe 'kroki rendering', :js, feature_category: :team_planning do
let_it_be(:project) { create(:project, :public) }
before do
diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb
index 08f9b8eda13..132a03877f8 100644
--- a/spec/features/markdown/markdown_spec.rb
+++ b/spec/features/markdown/markdown_spec.rb
@@ -26,7 +26,7 @@ require 'erb'
#
# See the MarkdownFeature class for setup details.
-RSpec.describe 'GitLab Markdown', :aggregate_failures do
+RSpec.describe 'GitLab Markdown', :aggregate_failures, feature_category: :team_planning do
include Capybara::Node::Matchers
include MarkupHelper
include MarkdownMatchers
@@ -290,6 +290,13 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do
aggregate_failures 'KrokiFilter' do
expect(doc).to parse_kroki
end
+
+ aggregate_failures 'AttributeFilter' do
+ img = doc.at_css('img[alt="Sized Image"]')
+
+ expect(img.attr('width')).to eq('75%')
+ expect(img.attr('height')).to eq('100')
+ end
end
end
diff --git a/spec/features/markdown/math_spec.rb b/spec/features/markdown/math_spec.rb
index 1f219886818..0c77bd2a8ff 100644
--- a/spec/features/markdown/math_spec.rb
+++ b/spec/features/markdown/math_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Math rendering', :js do
+RSpec.describe 'Math rendering', :js, feature_category: :team_planning do
let!(:project) { create(:project, :public) }
it 'renders inline and display math correctly' do
diff --git a/spec/features/markdown/metrics_spec.rb b/spec/features/markdown/metrics_spec.rb
index 61dd41204f8..b5e42b16f87 100644
--- a/spec/features/markdown/metrics_spec.rb
+++ b/spec/features/markdown/metrics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Metrics rendering', :js, :kubeclient, :use_clean_rails_memory_store_caching, :sidekiq_inline do
+RSpec.describe 'Metrics rendering', :js, :kubeclient, :use_clean_rails_memory_store_caching, :sidekiq_inline, feature_category: :team_planning do
include PrometheusHelpers
include KubernetesHelpers
include GrafanaApiHelpers
diff --git a/spec/features/markdown/observability_spec.rb b/spec/features/markdown/observability_spec.rb
new file mode 100644
index 00000000000..0c7d8cc006b
--- /dev/null
+++ b/spec/features/markdown/observability_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Observability rendering', :js do
+ let_it_be(:group) { create(:group, :public) }
+ let_it_be(:project) { create(:project, :repository, group: group) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:observable_url) { "https://observe.gitlab.com/" }
+
+ let_it_be(:expected) do
+ %(<iframe src="#{observable_url}?theme=light&amp;kiosk" frameborder="0")
+ end
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ context 'when embedding in an issue' do
+ let(:issue) do
+ create(:issue, project: project, description: observable_url)
+ end
+
+ before do
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ it 'renders iframe in description' do
+ page.within('.description') do
+ expect(page.html).to include(expected)
+ end
+ end
+
+ it 'renders iframe in comment' do
+ expect(page).not_to have_css('.note-text')
+
+ page.within('.js-main-target-form') do
+ fill_in('note[note]', with: observable_url)
+ click_button('Comment')
+ end
+
+ wait_for_requests
+
+ page.within('.note-text') do
+ expect(page.html).to include(expected)
+ end
+ end
+ end
+
+ context 'when embedding in an MR' do
+ let(:merge_request) do
+ create(:merge_request, source_project: project, target_project: project, description: observable_url)
+ end
+
+ before do
+ visit merge_request_path(merge_request)
+ wait_for_requests
+ end
+
+ it 'renders iframe in description' do
+ page.within('.description') do
+ expect(page.html).to include(expected)
+ end
+ end
+
+ it 'renders iframe in comment' do
+ expect(page).not_to have_css('.note-text')
+
+ page.within('.js-main-target-form') do
+ fill_in('note[note]', with: observable_url)
+ click_button('Comment')
+ end
+
+ wait_for_requests
+
+ page.within('.note-text') do
+ expect(page.html).to include(expected)
+ end
+ end
+ end
+end
diff --git a/spec/features/markdown/sandboxed_mermaid_spec.rb b/spec/features/markdown/sandboxed_mermaid_spec.rb
index 2bf88d7882d..26b397a1fd5 100644
--- a/spec/features/markdown/sandboxed_mermaid_spec.rb
+++ b/spec/features/markdown/sandboxed_mermaid_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Sandboxed Mermaid rendering', :js do
+RSpec.describe 'Sandboxed Mermaid rendering', :js, feature_category: :team_planning do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:description) do
<<~MERMAID
diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb
index f01217df8c5..e16c1ae094b 100644
--- a/spec/features/merge_request/batch_comments_spec.rb
+++ b/spec/features/merge_request/batch_comments_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > Batch comments', :js do
+RSpec.describe 'Merge request > Batch comments', :js, feature_category: :code_review do
include MergeRequestDiffHelpers
include RepoHelpers
diff --git a/spec/features/merge_request/close_reopen_report_toggle_spec.rb b/spec/features/merge_request/close_reopen_report_toggle_spec.rb
index 5e9400935c3..63ed355b16e 100644
--- a/spec/features/merge_request/close_reopen_report_toggle_spec.rb
+++ b/spec/features/merge_request/close_reopen_report_toggle_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issuables Close/Reopen/Report toggle' do
+RSpec.describe 'Issuables Close/Reopen/Report toggle', feature_category: :code_review do
include IssuablesHelper
let(:user) { create(:user) }
@@ -27,14 +27,14 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
find('[data-testid="merge-request-actions"]').click
expect(container).to have_link("Close merge request")
- expect(container).to have_link('Report abuse')
+ expect(container).to have_link('Report abuse to administrator')
end
it 'links to Report Abuse' do
find('[data-testid="merge-request-actions"]').click
- click_link 'Report abuse'
+ click_link 'Report abuse to administrator'
- expect(page).to have_content('Report abuse to admin')
+ expect(page).to have_content('Report abuse to administrator')
end
end
@@ -47,7 +47,7 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
expect(container).to have_link('Edit')
expect(container).to have_link('Mark as draft')
expect(container).to have_link('Close merge request')
- expect(container).to have_link('Report abuse')
+ expect(container).to have_link('Report abuse to administrator')
expect(container).not_to have_link('Reopen merge request')
end
end
@@ -59,7 +59,7 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
find('[data-testid="merge-request-actions"]').click
expect(container).to have_link('Edit')
- expect(container).to have_link('Report abuse')
+ expect(container).to have_link('Report abuse to administrator')
expect(container).to have_link('Reopen merge request')
expect(container).not_to have_link('Close merge request')
end
@@ -73,7 +73,7 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
expect(container).to have_link('Edit')
expect(container).to have_link('Reopen merge request')
expect(container).not_to have_link('Close merge request')
- expect(container).not_to have_link('Report abuse')
+ expect(container).not_to have_link('Report abuse to administrator')
end
end
end
@@ -83,7 +83,7 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
it 'shows only the `Edit` button' do
expect(container).to have_link(exact_text: 'Edit')
- expect(container).not_to have_link('Report abuse')
+ expect(container).not_to have_link('Report abuse to administrator')
expect(container).not_to have_button('Close merge request')
expect(container).not_to have_button('Reopen merge request')
end
@@ -93,7 +93,7 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
it 'shows only the `Edit` button' do
expect(container).to have_link(exact_text: 'Edit')
- expect(container).not_to have_link('Report abuse')
+ expect(container).not_to have_link('Report abuse to administrator')
expect(container).not_to have_button('Close merge request')
expect(container).not_to have_button('Reopen merge request')
end
@@ -112,7 +112,7 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
end
it 'only shows a `Report abuse` button' do
- expect(container).to have_link('Report abuse')
+ expect(container).to have_link('Report abuse to administrator')
expect(container).not_to have_button('Close merge request')
expect(container).not_to have_button('Reopen merge request')
expect(container).not_to have_link(exact_text: 'Edit')
diff --git a/spec/features/merge_request/maintainer_edits_fork_spec.rb b/spec/features/merge_request/maintainer_edits_fork_spec.rb
index 39d948bb6fb..bd040a5b894 100644
--- a/spec/features/merge_request/maintainer_edits_fork_spec.rb
+++ b/spec/features/merge_request/maintainer_edits_fork_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'a maintainer edits files on a source-branch of an MR from a fork', :js, :sidekiq_might_not_need_inline do
+RSpec.describe 'a maintainer edits files on a source-branch of an MR from a fork', :js, :sidekiq_might_not_need_inline,
+feature_category: :code_review do
include Spec::Support::Helpers::Features::SourceEditorSpecHelpers
include ProjectForksHelper
let(:user) { create(:user, username: 'the-maintainer') }
diff --git a/spec/features/merge_request/merge_request_discussion_lock_spec.rb b/spec/features/merge_request/merge_request_discussion_lock_spec.rb
index d69295744f7..b48d4d80647 100644
--- a/spec/features/merge_request/merge_request_discussion_lock_spec.rb
+++ b/spec/features/merge_request/merge_request_discussion_lock_spec.rb
@@ -4,7 +4,7 @@
require 'spec_helper'
-RSpec.describe 'Merge Request Discussion Lock', :js do
+RSpec.describe 'Merge Request Discussion Lock', :js, feature_category: :code_review do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project, author: user) }
diff --git a/spec/features/merge_request/user_accepts_merge_request_spec.rb b/spec/features/merge_request/user_accepts_merge_request_spec.rb
index b50e6779e07..dda22abada0 100644
--- a/spec/features/merge_request/user_accepts_merge_request_spec.rb
+++ b/spec/features/merge_request/user_accepts_merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inline do
+RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inline, feature_category: :code_review do
let(:merge_request) { create(:merge_request, :simple, source_project: project) }
let(:project) { create(:project, :public, :repository) }
let(:user) { create(:user) }
diff --git a/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb b/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb
index 75912238501..cf6836b544b 100644
--- a/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb
+++ b/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'create a merge request, allowing commits from members who can merge to the target branch', :js do
+RSpec.describe 'create a merge request, allowing commits from members who can merge to the target branch', :js,
+feature_category: :code_review do
include ProjectForksHelper
let(:user) { create(:user) }
let(:target_project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_request/user_approves_spec.rb b/spec/features/merge_request/user_approves_spec.rb
index 9670012803e..bfb6a3ec8de 100644
--- a/spec/features/merge_request/user_approves_spec.rb
+++ b/spec/features/merge_request/user_approves_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User approves', :js do
+RSpec.describe 'Merge request > User approves', :js, feature_category: :code_review do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/features/merge_request/user_assigns_themselves_reviewer_spec.rb b/spec/features/merge_request/user_assigns_themselves_reviewer_spec.rb
new file mode 100644
index 00000000000..2b93f88e96b
--- /dev/null
+++ b/spec/features/merge_request/user_assigns_themselves_reviewer_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Merge request > User assigns themselves as a reviewer', feature_category: :code_review do
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project, author: user, description: "test mr") }
+
+ context 'when logged in as a member of the project' do
+ before do
+ sign_in(user)
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it 'updates updated_by', :js do
+ wait_for_requests
+
+ expect do
+ page.within('.reviewer') do
+ click_button 'assign yourself'
+ end
+
+ expect(find('.reviewer')).to have_content(user.name)
+ wait_for_all_requests
+ end.to change { merge_request.reload.updated_at }
+ end
+
+ context 'when logged in as a non-member of the project' do
+ before do
+ sign_in(create(:user))
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it 'does not show link to assign self as Reviewer' do
+ page.within('.reviewer') do
+ expect(page).not_to have_content 'Assign yourself'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/merge_request/user_assigns_themselves_spec.rb b/spec/features/merge_request/user_assigns_themselves_spec.rb
index 2aaddc7791b..826904bd165 100644
--- a/spec/features/merge_request/user_assigns_themselves_spec.rb
+++ b/spec/features/merge_request/user_assigns_themselves_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User assigns themselves' do
+RSpec.describe 'Merge request > User assigns themselves', feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:issue1) { create(:issue, project: project) }
@@ -22,8 +22,12 @@ RSpec.describe 'Merge request > User assigns themselves' do
end
it 'updates updated_by', :js do
+ wait_for_requests
+
expect do
- click_button 'assign yourself'
+ page.within('[data-testid="assignee-block-container"]') do
+ click_button 'assign yourself'
+ end
expect(find('.assignee')).to have_content(user.name)
wait_for_all_requests
@@ -36,7 +40,9 @@ RSpec.describe 'Merge request > User assigns themselves' do
end
it 'does not display if related issues are already assigned' do
- expect(page).not_to have_content 'Assign yourself'
+ page.within('[data-testid="assignee-block-container"]') do
+ expect(page).not_to have_content 'Assign yourself'
+ end
end
end
end
@@ -48,7 +54,9 @@ RSpec.describe 'Merge request > User assigns themselves' do
end
it 'does not show assignment link' do
- expect(page).not_to have_content 'Assign yourself'
+ page.within('[data-testid="assignee-block-container"]') do
+ expect(page).not_to have_content 'Assign yourself'
+ end
end
end
end
diff --git a/spec/features/merge_request/user_awards_emoji_spec.rb b/spec/features/merge_request/user_awards_emoji_spec.rb
index 6fdc1a29174..dceac8d6a69 100644
--- a/spec/features/merge_request/user_awards_emoji_spec.rb
+++ b/spec/features/merge_request/user_awards_emoji_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User awards emoji', :js do
+RSpec.describe 'Merge request > User awards emoji', :js, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project, author: create(:user)) }
diff --git a/spec/features/merge_request/user_clicks_merge_request_tabs_spec.rb b/spec/features/merge_request/user_clicks_merge_request_tabs_spec.rb
index f3cbc1ea1f5..3e3ff91ad19 100644
--- a/spec/features/merge_request/user_clicks_merge_request_tabs_spec.rb
+++ b/spec/features/merge_request/user_clicks_merge_request_tabs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User clicks on merge request tabs', :js do
+RSpec.describe 'User clicks on merge request tabs', :js, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
diff --git a/spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb b/spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb
index 70951982c22..c5ef6b912fe 100644
--- a/spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb
+++ b/spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'User closes/reopens a merge request', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297500' do
+RSpec.describe 'User closes/reopens a merge request', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297500',
+ feature_category: :code_review do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
diff --git a/spec/features/merge_request/user_comments_on_commit_spec.rb b/spec/features/merge_request/user_comments_on_commit_spec.rb
index 8fa1fe3812d..64fe144cd0d 100644
--- a/spec/features/merge_request/user_comments_on_commit_spec.rb
+++ b/spec/features/merge_request/user_comments_on_commit_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User comments on a commit', :js do
+RSpec.describe 'User comments on a commit', :js, feature_category: :code_review do
include MergeRequestDiffHelpers
include RepoHelpers
diff --git a/spec/features/merge_request/user_comments_on_diff_spec.rb b/spec/features/merge_request/user_comments_on_diff_spec.rb
index ffaf403e873..f1a942d5708 100644
--- a/spec/features/merge_request/user_comments_on_diff_spec.rb
+++ b/spec/features/merge_request/user_comments_on_diff_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User comments on a diff', :js do
+RSpec.describe 'User comments on a diff', :js, feature_category: :code_review do
include MergeRequestDiffHelpers
include RepoHelpers
diff --git a/spec/features/merge_request/user_comments_on_merge_request_spec.rb b/spec/features/merge_request/user_comments_on_merge_request_spec.rb
index dbcfc2b968f..d5ad78746f4 100644
--- a/spec/features/merge_request/user_comments_on_merge_request_spec.rb
+++ b/spec/features/merge_request/user_comments_on_merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User comments on a merge request', :js do
+RSpec.describe 'User comments on a merge request', :js, feature_category: :code_review do
include RepoHelpers
let(:project) { create(:project, :repository) }
diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
index bd5048374d5..eb7894f4ef7 100644
--- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User creates image diff notes', :js do
+RSpec.describe 'Merge request > User creates image diff notes', :js, feature_category: :code_review do
include NoteInteractionHelpers
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_request/user_creates_merge_request_spec.rb b/spec/features/merge_request/user_creates_merge_request_spec.rb
index 0ae4ef18649..50629f11959 100644
--- a/spec/features/merge_request/user_creates_merge_request_spec.rb
+++ b/spec/features/merge_request/user_creates_merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User creates a merge request', :js do
+RSpec.describe 'User creates a merge request', :js, feature_category: :code_review do
include ProjectForksHelper
shared_examples 'creates a merge request' do
@@ -77,7 +77,7 @@ RSpec.describe 'User creates a merge request', :js do
first('.dropdown-source-project a', text: forked_project.full_path)
first('.js-target-project').click
- first('.dropdown-target-project a', text: project.full_path)
+ first('.dropdown-target-project li', text: project.full_path)
first('.js-source-branch').click
diff --git a/spec/features/merge_request/user_creates_mr_spec.rb b/spec/features/merge_request/user_creates_mr_spec.rb
index 9d97e57fe3a..5effde234cd 100644
--- a/spec/features/merge_request/user_creates_mr_spec.rb
+++ b/spec/features/merge_request/user_creates_mr_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User creates MR' do
+RSpec.describe 'Merge request > User creates MR', feature_category: :code_review do
include ProjectForksHelper
before do
diff --git a/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb b/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
index f0c0142a6cc..4f1119d6c33 100644
--- a/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
+++ b/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request < User customizes merge commit message', :js do
+RSpec.describe 'Merge request < User customizes merge commit message', :js, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:issue_1) { create(:issue, project: project) }
diff --git a/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb b/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb
index 59b5923b2a1..c04040dd6fd 100644
--- a/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb
+++ b/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User edits assignees sidebar', :js do
+RSpec.describe 'Merge request > User edits assignees sidebar', :js, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:protected_branch) { create(:protected_branch, :maintainers_can_push, name: 'master', project: project) }
let(:merge_request) { create(:merge_request, :simple, source_project: project, target_branch: protected_branch.name) }
diff --git a/spec/features/merge_request/user_edits_merge_request_spec.rb b/spec/features/merge_request/user_edits_merge_request_spec.rb
index 4ac25ea7ae0..6701c7d91ae 100644
--- a/spec/features/merge_request/user_edits_merge_request_spec.rb
+++ b/spec/features/merge_request/user_edits_merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User edits a merge request', :js do
+RSpec.describe 'User edits a merge request', :js, feature_category: :code_review do
let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:user) { create(:user) }
diff --git a/spec/features/merge_request/user_edits_mr_spec.rb b/spec/features/merge_request/user_edits_mr_spec.rb
index 2c949ed84f4..18e6827a872 100644
--- a/spec/features/merge_request/user_edits_mr_spec.rb
+++ b/spec/features/merge_request/user_edits_mr_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User edits MR' do
+RSpec.describe 'Merge request > User edits MR', feature_category: :code_review do
include ProjectForksHelper
before do
diff --git a/spec/features/merge_request/user_edits_reviewers_sidebar_spec.rb b/spec/features/merge_request/user_edits_reviewers_sidebar_spec.rb
index caf0c609f64..38c76314b9e 100644
--- a/spec/features/merge_request/user_edits_reviewers_sidebar_spec.rb
+++ b/spec/features/merge_request/user_edits_reviewers_sidebar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User edits reviewers sidebar', :js do
+RSpec.describe 'Merge request > User edits reviewers sidebar', :js, feature_category: :code_review do
context 'with invite members considerations' do
let_it_be(:merge_request) { create(:merge_request) }
let_it_be(:project) { merge_request.project }
diff --git a/spec/features/merge_request/user_expands_diff_spec.rb b/spec/features/merge_request/user_expands_diff_spec.rb
index 25c9584350d..8adbdcd310c 100644
--- a/spec/features/merge_request/user_expands_diff_spec.rb
+++ b/spec/features/merge_request/user_expands_diff_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User expands diff', :js do
+RSpec.describe 'User expands diff', :js, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_branch: 'expand-collapse-files', source_project: project, target_project: project) }
diff --git a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
index 07d99a786ba..1b9b3941714 100644
--- a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
+++ b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Batch diffs', :js do
+RSpec.describe 'Batch diffs', :js, feature_category: :code_review do
include MergeRequestDiffHelpers
include RepoHelpers
diff --git a/spec/features/merge_request/user_locks_discussion_spec.rb b/spec/features/merge_request/user_locks_discussion_spec.rb
index c8a6fdd4007..1bfd52d49e8 100644
--- a/spec/features/merge_request/user_locks_discussion_spec.rb
+++ b/spec/features/merge_request/user_locks_discussion_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User locks discussion', :js do
+RSpec.describe 'Merge request > User locks discussion', :js, feature_category: :code_review do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/features/merge_request/user_manages_subscription_spec.rb b/spec/features/merge_request/user_manages_subscription_spec.rb
index a8d59a6ffb5..16d869fc5a1 100644
--- a/spec/features/merge_request/user_manages_subscription_spec.rb
+++ b/spec/features/merge_request/user_manages_subscription_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User manages subscription', :js do
+RSpec.describe 'User manages subscription', :js, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:user) { create(:user) }
diff --git a/spec/features/merge_request/user_marks_merge_request_as_draft_spec.rb b/spec/features/merge_request/user_marks_merge_request_as_draft_spec.rb
index d85f275b724..201cdc94b56 100644
--- a/spec/features/merge_request/user_marks_merge_request_as_draft_spec.rb
+++ b/spec/features/merge_request/user_marks_merge_request_as_draft_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User marks merge request as draft', :js do
+RSpec.describe 'Merge request > User marks merge request as draft', :js, feature_category: :code_review do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/features/merge_request/user_merges_immediately_spec.rb b/spec/features/merge_request/user_merges_immediately_spec.rb
index 91327059e0f..b0aeea997f0 100644
--- a/spec/features/merge_request/user_merges_immediately_spec.rb
+++ b/spec/features/merge_request/user_merges_immediately_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge requests > User merges immediately', :js do
+RSpec.describe 'Merge requests > User merges immediately', :js, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let!(:merge_request) do
diff --git a/spec/features/merge_request/user_merges_merge_request_spec.rb b/spec/features/merge_request/user_merges_merge_request_spec.rb
index c91dc7b1c00..4196fdd5dac 100644
--- a/spec/features/merge_request/user_merges_merge_request_spec.rb
+++ b/spec/features/merge_request/user_merges_merge_request_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User merges a merge request", :js do
+RSpec.describe "User merges a merge request", :js, feature_category: :code_review do
let(:user) { project.first_owner }
before do
diff --git a/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
index d6b132b18da..447418b5a4b 100644
--- a/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User merges only if pipeline succeeds', :js do
+RSpec.describe 'Merge request > User merges only if pipeline succeeds', :js, feature_category: :code_review do
let(:merge_request) { create(:merge_request_with_diffs) }
let(:project) { merge_request.target_project }
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 abf916c72b3..78a21527794 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
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do
+RSpec.describe 'Merge request > User merges when pipeline succeeds', :js, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:merge_request) do
diff --git a/spec/features/merge_request/user_opens_checkout_branch_modal_spec.rb b/spec/features/merge_request/user_opens_checkout_branch_modal_spec.rb
index 4d2c59665bb..116de50f2a2 100644
--- a/spec/features/merge_request/user_opens_checkout_branch_modal_spec.rb
+++ b/spec/features/merge_request/user_opens_checkout_branch_modal_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User opens checkout branch modal', :js do
+RSpec.describe 'Merge request > User opens checkout branch modal', :js, feature_category: :code_review do
include ProjectForksHelper
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_request/user_opens_context_commits_modal_spec.rb b/spec/features/merge_request/user_opens_context_commits_modal_spec.rb
index 2d574e57fe9..f32a51cfcd4 100644
--- a/spec/features/merge_request/user_opens_context_commits_modal_spec.rb
+++ b/spec/features/merge_request/user_opens_context_commits_modal_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > Context commits', :js do
+RSpec.describe 'Merge request > Context commits', :js, feature_category: :code_review do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/features/merge_request/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb
index 8af0e957c14..f2ec0e2df6d 100644
--- a/spec/features/merge_request/user_posts_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User posts diff notes', :js do
+RSpec.describe 'Merge request > User posts diff notes', :js, feature_category: :code_review do
include MergeRequestDiffHelpers
include Spec::Support::Helpers::ModalHelpers
diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb
index 844ef6133c8..194e04a9544 100644
--- a/spec/features/merge_request/user_posts_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_notes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User posts notes', :js do
+RSpec.describe 'Merge request > User posts notes', :js, feature_category: :code_review do
include NoteInteractionHelpers
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/features/merge_request/user_rebases_merge_request_spec.rb b/spec/features/merge_request/user_rebases_merge_request_spec.rb
index d42864200ec..c3ee5ddc3b1 100644
--- a/spec/features/merge_request/user_rebases_merge_request_spec.rb
+++ b/spec/features/merge_request/user_rebases_merge_request_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User rebases a merge request", :js do
+RSpec.describe "User rebases a merge request", :js, feature_category: :code_review do
let(:merge_request) { create(:merge_request, :simple, source_project: project) }
let(:user) { project.first_owner }
diff --git a/spec/features/merge_request/user_resolves_conflicts_spec.rb b/spec/features/merge_request/user_resolves_conflicts_spec.rb
index a04ca4e789c..d4c80c1e9e2 100644
--- a/spec/features/merge_request/user_resolves_conflicts_spec.rb
+++ b/spec/features/merge_request/user_resolves_conflicts_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User resolves conflicts', :js do
+RSpec.describe 'Merge request > User resolves conflicts', :js, feature_category: :code_review do
include Spec::Support::Helpers::Features::SourceEditorSpecHelpers
let(:project) { create(:project, :repository) }
diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
index 99f1b1ab1ad..f0507e94424 100644
--- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
+++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
+RSpec.describe 'Merge request > User resolves diff notes and threads', :js, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:guest) { create(:user) }
diff --git a/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb b/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb
index f8f3467f6fb..a7508ede1a1 100644
--- a/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb
+++ b/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User resolves outdated diff discussions', :js do
+RSpec.describe 'Merge request > User resolves outdated diff discussions', :js, feature_category: :code_review do
let(:project) { create(:project, :repository, :public) }
let(:merge_request) do
diff --git a/spec/features/merge_request/user_resolves_wip_mr_spec.rb b/spec/features/merge_request/user_resolves_wip_mr_spec.rb
index 92927b713f1..b7f20a16a3f 100644
--- a/spec/features/merge_request/user_resolves_wip_mr_spec.rb
+++ b/spec/features/merge_request/user_resolves_wip_mr_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User resolves Draft', :js do
+RSpec.describe 'Merge request > User resolves Draft', :js, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:merge_request) do
diff --git a/spec/features/merge_request/user_reverts_merge_request_spec.rb b/spec/features/merge_request/user_reverts_merge_request_spec.rb
index 9cbba6c470f..edfa9267871 100644
--- a/spec/features/merge_request/user_reverts_merge_request_spec.rb
+++ b/spec/features/merge_request/user_reverts_merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User reverts a merge request', :js do
+RSpec.describe 'User reverts a merge request', :js, feature_category: :code_review do
let(:merge_request) { create(:merge_request, :simple, source_project: project) }
let(:project) { create(:project, :public, :repository) }
let(:user) { create(:user) }
diff --git a/spec/features/merge_request/user_reviews_image_spec.rb b/spec/features/merge_request/user_reviews_image_spec.rb
index bd490294829..5814dc6b58c 100644
--- a/spec/features/merge_request/user_reviews_image_spec.rb
+++ b/spec/features/merge_request/user_reviews_image_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > image review', :js do
+RSpec.describe 'Merge request > image review', :js, feature_category: :code_review do
include MergeRequestDiffHelpers
include RepoHelpers
diff --git a/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb b/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb
index cf4875a7a25..fdd2aeec274 100644
--- a/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb
+++ b/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User scrolls to note on load', :js do
+RSpec.describe 'Merge request > User scrolls to note on load', :js, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project, author: user) }
diff --git a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
index a6c024be698..8c2fc62d16f 100644
--- a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
include Spec::Support::Helpers::ModalHelpers # rubocop:disable Style/MixinUsage
-RSpec.describe 'Merge request > User sees avatars on diff notes', :js do
+RSpec.describe 'Merge request > User sees avatars on diff notes', :js, feature_category: :code_review do
include NoteInteractionHelpers
include Spec::Support::Helpers::ModalHelpers
include MergeRequestDiffHelpers
diff --git a/spec/features/merge_request/user_sees_breadcrumb_links_spec.rb b/spec/features/merge_request/user_sees_breadcrumb_links_spec.rb
index d8b258bac47..0b6aefcdab6 100644
--- a/spec/features/merge_request/user_sees_breadcrumb_links_spec.rb
+++ b/spec/features/merge_request/user_sees_breadcrumb_links_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'New merge request breadcrumb' do
+RSpec.describe 'New merge request breadcrumb', feature_category: :code_review do
let(:project) { create(:project, :repository) }
let(:user) { project.creator }
diff --git a/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb b/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
index 5827266d4b7..bbfa2be47cc 100644
--- a/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
+++ b/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees check out branch modal', :js do
+RSpec.describe 'Merge request > User sees check out branch modal', :js, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb b/spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb
index 35be21a646e..07b7cb1e8d8 100644
--- a/spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb
+++ b/spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User cherry-picks', :js do
+RSpec.describe 'Merge request > User cherry-picks', :js, feature_category: :code_review do
let(:group) { create(:group) }
let(:project) { create(:project, :repository, namespace: group) }
let(:user) { project.creator }
diff --git a/spec/features/merge_request/user_sees_closing_issues_message_spec.rb b/spec/features/merge_request/user_sees_closing_issues_message_spec.rb
index f56db3d3dbe..9a1d47a13b5 100644
--- a/spec/features/merge_request/user_sees_closing_issues_message_spec.rb
+++ b/spec/features/merge_request/user_sees_closing_issues_message_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees closing issues message', :js do
+RSpec.describe 'Merge request > User sees closing issues message', :js, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:issue_1) { create(:issue, project: project) }
diff --git a/spec/features/merge_request/user_sees_deleted_target_branch_spec.rb b/spec/features/merge_request/user_sees_deleted_target_branch_spec.rb
index dc50c3bc8db..16ae8b4304b 100644
--- a/spec/features/merge_request/user_sees_deleted_target_branch_spec.rb
+++ b/spec/features/merge_request/user_sees_deleted_target_branch_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees deleted target branch', :js do
+RSpec.describe 'Merge request > User sees deleted target branch', :js, feature_category: :code_review do
let(:merge_request) { create(:merge_request) }
let(:project) { merge_request.project }
let(:user) { project.creator }
diff --git a/spec/features/merge_request/user_sees_deployment_widget_spec.rb b/spec/features/merge_request/user_sees_deployment_widget_spec.rb
index 6f8ecf5f5c2..40ab06937ff 100644
--- a/spec/features/merge_request/user_sees_deployment_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_deployment_widget_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees deployment widget', :js do
+RSpec.describe 'Merge request > User sees deployment widget', :js, feature_category: :continuous_delivery do
include Spec::Support::Helpers::ModalHelpers
describe 'when merge request has associated environments' do
diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb
index 0bae019793c..101ff8fc152 100644
--- a/spec/features/merge_request/user_sees_diff_spec.rb
+++ b/spec/features/merge_request/user_sees_diff_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees diff', :js do
+RSpec.describe 'Merge request > User sees diff', :js, feature_category: :code_review do
include ProjectForksHelper
include RepoHelpers
include MergeRequestDiffHelpers
diff --git a/spec/features/merge_request/user_sees_discussions_navigation_spec.rb b/spec/features/merge_request/user_sees_discussions_navigation_spec.rb
index 9fbe7662fc0..a22fb2cff00 100644
--- a/spec/features/merge_request/user_sees_discussions_navigation_spec.rb
+++ b/spec/features/merge_request/user_sees_discussions_navigation_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees discussions navigation', :js do
+RSpec.describe 'Merge request > User sees discussions navigation', :js, feature_category: :code_review do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:user) { project.creator }
let_it_be(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/features/merge_request/user_sees_discussions_spec.rb b/spec/features/merge_request/user_sees_discussions_spec.rb
index cc477e363a4..0eae6e39eec 100644
--- a/spec/features/merge_request/user_sees_discussions_spec.rb
+++ b/spec/features/merge_request/user_sees_discussions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees threads', :js do
+RSpec.describe 'Merge request > User sees threads', :js, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb b/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb
index e250837f398..6db5480abb4 100644
--- a/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees merge button depending on unresolved threads', :js do
+RSpec.describe 'Merge request > User sees merge button depending on unresolved threads', :js,
+feature_category: :code_review do
let(:project) { create(:project, :repository) }
let(:user) { project.creator }
let!(:merge_request) { create(:merge_request_with_diff_notes, source_project: project, author: user) }
diff --git a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
index 2a1b9ea6009..f7594c717d1 100644
--- a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees pipelines triggered by merge request', :js do
+RSpec.describe 'Merge request > User sees pipelines triggered by merge request', :js, feature_category: :code_review do
include ProjectForksHelper
include TestReportsHelper
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 c4a29c1fb07..e5aa0f6e64d 100644
--- a/spec/features/merge_request/user_sees_merge_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees merge widget', :js do
+RSpec.describe 'Merge request > User sees merge widget', :js, feature_category: :code_review do
include ProjectForksHelper
include TestReportsHelper
include ReactiveCachingHelpers
diff --git a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
index 03f9f6ef565..5756218d20f 100644
--- a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
+++ b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request < User sees mini pipeline graph', :js do
+RSpec.describe 'Merge request < User sees mini pipeline graph', :js, feature_category: :continuous_integration do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project, head_pipeline: pipeline) }
diff --git a/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb b/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb
index a764dd97878..4bfdce29c6a 100644
--- a/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb
+++ b/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees MR from deleted forked project', :js do
+RSpec.describe 'Merge request > User sees MR from deleted forked project', :js, feature_category: :code_review do
include ProjectForksHelper
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb b/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb
index 39bba3f2f73..8e6f6d04676 100644
--- a/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb
+++ b/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
# This test serves as a regression test for a bug that caused an error
# message to be shown by JavaScript when the source branch was deleted.
# Please do not remove ":js".
-RSpec.describe 'Merge request > User sees MR with deleted source branch', :js do
+RSpec.describe 'Merge request > User sees MR with deleted source branch', :js, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
let(:user) { project.creator }
diff --git a/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb b/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb
index b8b7fc2009f..8f011f5616b 100644
--- a/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb
+++ b/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees notes from forked project', :js do
+RSpec.describe 'Merge request > User sees notes from forked project', :js, feature_category: :code_review do
include ProjectForksHelper
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_request/user_sees_page_metadata_spec.rb b/spec/features/merge_request/user_sees_page_metadata_spec.rb
index 7b3e07152a0..f97732f91a7 100644
--- a/spec/features/merge_request/user_sees_page_metadata_spec.rb
+++ b/spec/features/merge_request/user_sees_page_metadata_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees page metadata' do
+RSpec.describe 'Merge request > User sees page metadata', feature_category: :code_review do
let(:merge_request) { create(:merge_request, description: '**Lorem** _ipsum_ dolor sit [amet](https://example.com)') }
let(:project) { merge_request.target_project }
let(:user) { project.creator }
diff --git a/spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb b/spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb
index a9fefc89d6c..0816b14f9a5 100644
--- a/spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb
+++ b/spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees pipelines from forked project', :js do
+RSpec.describe 'Merge request > User sees pipelines from forked project', :js,
+feature_category: :continuous_integration do
include ProjectForksHelper
let(:target_project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_request/user_sees_pipelines_spec.rb b/spec/features/merge_request/user_sees_pipelines_spec.rb
index 11e542916f9..8faaf6bf39b 100644
--- a/spec/features/merge_request/user_sees_pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_pipelines_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees pipelines', :js do
+RSpec.describe 'Merge request > User sees pipelines', :js, feature_category: :code_review do
describe 'pipeline tab' do
let(:merge_request) { create(:merge_request) }
let(:project) { merge_request.target_project }
@@ -35,7 +35,7 @@ RSpec.describe 'Merge request > User sees pipelines', :js do
end
wait_for_requests
- expect(page).to have_selector('.stage-cell')
+ expect(page).to have_css('[data-testid="pipeline-mini-graph"]')
end
context 'with a detached merge request pipeline' do
diff --git a/spec/features/merge_request/user_sees_suggest_pipeline_spec.rb b/spec/features/merge_request/user_sees_suggest_pipeline_spec.rb
index 448ef750508..3a2e382fe99 100644
--- a/spec/features/merge_request/user_sees_suggest_pipeline_spec.rb
+++ b/spec/features/merge_request/user_sees_suggest_pipeline_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees suggest pipeline', :js do
+RSpec.describe 'Merge request > User sees suggest pipeline', :js, feature_category: :continuous_integration do
let(:merge_request) { create(:merge_request) }
let(:project) { merge_request.source_project }
let(:user) { project.creator }
diff --git a/spec/features/merge_request/user_sees_system_notes_spec.rb b/spec/features/merge_request/user_sees_system_notes_spec.rb
index 9f8d4c6d63f..40402c95d6f 100644
--- a/spec/features/merge_request/user_sees_system_notes_spec.rb
+++ b/spec/features/merge_request/user_sees_system_notes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees system notes', :js do
+RSpec.describe 'Merge request > User sees system notes', :js, feature_category: :code_review do
let(:public_project) { create(:project, :public, :repository) }
let(:private_project) { create(:project, :private, :repository) }
let(:user) { private_project.creator }
diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb
index 0e86e970f46..f0ff6e1769a 100644
--- a/spec/features/merge_request/user_sees_versions_spec.rb
+++ b/spec/features/merge_request/user_sees_versions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees versions', :js do
+RSpec.describe 'Merge request > User sees versions', :js, feature_category: :code_review do
include MergeRequestDiffHelpers
let(:merge_request) do
diff --git a/spec/features/merge_request/user_sees_wip_help_message_spec.rb b/spec/features/merge_request/user_sees_wip_help_message_spec.rb
index d33e54f2e3d..1a751af6ded 100644
--- a/spec/features/merge_request/user_sees_wip_help_message_spec.rb
+++ b/spec/features/merge_request/user_sees_wip_help_message_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees draft help message' do
+RSpec.describe 'Merge request > User sees draft help message', feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
diff --git a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
index bcc6abd4f65..8b6c9dc18f6 100644
--- a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
+++ b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User selects branches for new MR', :js do
+RSpec.describe 'Merge request > User selects branches for new MR', :js, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
diff --git a/spec/features/merge_request/user_squashes_merge_request_spec.rb b/spec/features/merge_request/user_squashes_merge_request_spec.rb
index da0d4ca23d1..43590aed3cc 100644
--- a/spec/features/merge_request/user_squashes_merge_request_spec.rb
+++ b/spec/features/merge_request/user_squashes_merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User squashes a merge request', :js do
+RSpec.describe 'User squashes a merge request', :js, feature_category: :code_review do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:source_branch) { 'csv' }
diff --git a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
index f77a42ee506..5a5494a2fe9 100644
--- a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
+++ b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User comments on a diff', :js do
+RSpec.describe 'User comments on a diff', :js, feature_category: :code_review do
include MergeRequestDiffHelpers
include RepoHelpers
diff --git a/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb b/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb
index 19774accaaf..993eb59cb74 100644
--- a/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb
+++ b/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User toggles whitespace changes', :js do
+RSpec.describe 'Merge request > User toggles whitespace changes', :js, feature_category: :code_review do
let(:merge_request) { create(:merge_request) }
let(:project) { merge_request.project }
let(:user) { project.creator }
diff --git a/spec/features/merge_request/user_tries_to_access_private_project_info_through_new_mr_spec.rb b/spec/features/merge_request/user_tries_to_access_private_project_info_through_new_mr_spec.rb
index 96a1cd81c93..5095457509a 100644
--- a/spec/features/merge_request/user_tries_to_access_private_project_info_through_new_mr_spec.rb
+++ b/spec/features/merge_request/user_tries_to_access_private_project_info_through_new_mr_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Merge Request > User tries to access private project information through the new mr page' do
+RSpec.describe 'Merge Request > User tries to access private project information through the new mr page',
+feature_category: :code_review do
let(:current_user) { create(:user) }
let(:private_project) do
create(:project, :public, :repository,
diff --git a/spec/features/merge_request/user_uses_quick_actions_spec.rb b/spec/features/merge_request/user_uses_quick_actions_spec.rb
index ca102913369..99befbace74 100644
--- a/spec/features/merge_request/user_uses_quick_actions_spec.rb
+++ b/spec/features/merge_request/user_uses_quick_actions_spec.rb
@@ -7,7 +7,8 @@ require 'spec_helper'
# for example, adding quick actions when creating the issue and checking DateTime formats on UI.
# Because this kind of spec takes more time to run there is no need to add new ones
# for each existing quick action unless they test something not tested by existing tests.
-RSpec.describe 'Merge request > User uses quick actions', :js, :use_clean_rails_redis_caching do
+RSpec.describe 'Merge request > User uses quick actions', :js, :use_clean_rails_redis_caching,
+feature_category: :code_review do
include Spec::Support::Helpers::Features::NotesHelpers
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_request/user_views_auto_expanding_diff_spec.rb b/spec/features/merge_request/user_views_auto_expanding_diff_spec.rb
index 1748f66c934..19a77a9192c 100644
--- a/spec/features/merge_request/user_views_auto_expanding_diff_spec.rb
+++ b/spec/features/merge_request/user_views_auto_expanding_diff_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views diffs file-by-file', :js do
+RSpec.describe 'User views diffs file-by-file', :js, feature_category: :code_review do
let(:merge_request) do
create(:merge_request, source_branch: 'squash-large-files', source_project: project, target_project: project)
end
diff --git a/spec/features/merge_request/user_views_diffs_commit_spec.rb b/spec/features/merge_request/user_views_diffs_commit_spec.rb
index cf92603972e..84cbfb35539 100644
--- a/spec/features/merge_request/user_views_diffs_commit_spec.rb
+++ b/spec/features/merge_request/user_views_diffs_commit_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views diff by commit', :js do
+RSpec.describe 'User views diff by commit', :js, feature_category: :code_review do
let(:merge_request) do
create(:merge_request_with_diffs, source_project: project, target_project: project, source_branch: 'merge-test')
end
diff --git a/spec/features/merge_request/user_views_diffs_file_by_file_spec.rb b/spec/features/merge_request/user_views_diffs_file_by_file_spec.rb
index ad9c342df3e..9db6f86e14d 100644
--- a/spec/features/merge_request/user_views_diffs_file_by_file_spec.rb
+++ b/spec/features/merge_request/user_views_diffs_file_by_file_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views diffs file-by-file', :js do
+RSpec.describe 'User views diffs file-by-file', :js, feature_category: :code_review do
let(:merge_request) do
create(:merge_request_with_diffs, source_project: project, target_project: project, source_branch: 'merge-test')
end
diff --git a/spec/features/merge_request/user_views_diffs_spec.rb b/spec/features/merge_request/user_views_diffs_spec.rb
index 894292c99eb..7363f6dfb32 100644
--- a/spec/features/merge_request/user_views_diffs_spec.rb
+++ b/spec/features/merge_request/user_views_diffs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views diffs', :js do
+RSpec.describe 'User views diffs', :js, feature_category: :code_review do
let(:merge_request) do
create(:merge_request_with_diffs, source_project: project, target_project: project, source_branch: 'merge-test')
end
diff --git a/spec/features/merge_request/user_views_merge_request_from_deleted_fork_spec.rb b/spec/features/merge_request/user_views_merge_request_from_deleted_fork_spec.rb
index e3272a6e280..2a9275adfcf 100644
--- a/spec/features/merge_request/user_views_merge_request_from_deleted_fork_spec.rb
+++ b/spec/features/merge_request/user_views_merge_request_from_deleted_fork_spec.rb
@@ -6,7 +6,7 @@ require 'spec_helper'
# updated.
# This can occur when the fork a merge request is created from is in the process
# of being destroyed.
-RSpec.describe 'User views merged merge request from deleted fork' do
+RSpec.describe 'User views merged merge request from deleted fork', feature_category: :code_review do
include ProjectForksHelper
let(:project) { create(:project, :repository) }
diff --git a/spec/features/merge_request/user_views_open_merge_request_spec.rb b/spec/features/merge_request/user_views_open_merge_request_spec.rb
index 1f4682b4a46..8b9e973217d 100644
--- a/spec/features/merge_request/user_views_open_merge_request_spec.rb
+++ b/spec/features/merge_request/user_views_open_merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views an open merge request' do
+RSpec.describe 'User views an open merge request', feature_category: :code_review do
let(:merge_request) do
create(:merge_request, source_project: project, target_project: project, description: '# Description header')
end
diff --git a/spec/features/merge_requests/filters_generic_behavior_spec.rb b/spec/features/merge_requests/filters_generic_behavior_spec.rb
index 80009cca2fb..0d6b5edcbab 100644
--- a/spec/features/merge_requests/filters_generic_behavior_spec.rb
+++ b/spec/features/merge_requests/filters_generic_behavior_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge Requests > Filters generic behavior', :js do
+RSpec.describe 'Merge Requests > Filters generic behavior', :js, feature_category: :code_review do
include FilteredSearchHelpers
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_requests/rss_spec.rb b/spec/features/merge_requests/rss_spec.rb
index 9fc3d3d6ae1..4c73ce3b684 100644
--- a/spec/features/merge_requests/rss_spec.rb
+++ b/spec/features/merge_requests/rss_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project Merge Requests RSS' do
+RSpec.describe 'Project Merge Requests RSS', feature_category: :code_review do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
diff --git a/spec/features/merge_requests/user_exports_as_csv_spec.rb b/spec/features/merge_requests/user_exports_as_csv_spec.rb
index 351e714b612..aedd7ef4d79 100644
--- a/spec/features/merge_requests/user_exports_as_csv_spec.rb
+++ b/spec/features/merge_requests/user_exports_as_csv_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge Requests > Exports as CSV', :js do
+RSpec.describe 'Merge Requests > Exports as CSV', :js, feature_category: :code_review do
let!(:project) { create(:project, :public, :repository) }
let!(:user) { project.creator }
let!(:open_mr) { create(:merge_request, title: 'Bugfix1', source_project: project, target_project: project, source_branch: 'bugfix1') }
diff --git a/spec/features/merge_requests/user_filters_by_approvals_spec.rb b/spec/features/merge_requests/user_filters_by_approvals_spec.rb
index 6dda9ca7952..56c8a65385c 100644
--- a/spec/features/merge_requests/user_filters_by_approvals_spec.rb
+++ b/spec/features/merge_requests/user_filters_by_approvals_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge Requests > User filters', :js do
+RSpec.describe 'Merge Requests > User filters', :js, feature_category: :code_review do
include FilteredSearchHelpers
let_it_be(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_requests/user_filters_by_assignees_spec.rb b/spec/features/merge_requests/user_filters_by_assignees_spec.rb
index 9827b067649..818cf6f076f 100644
--- a/spec/features/merge_requests/user_filters_by_assignees_spec.rb
+++ b/spec/features/merge_requests/user_filters_by_assignees_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge Requests > User filters by assignees', :js do
+RSpec.describe 'Merge Requests > User filters by assignees', :js, feature_category: :code_review do
include FilteredSearchHelpers
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_requests/user_filters_by_deployments_spec.rb b/spec/features/merge_requests/user_filters_by_deployments_spec.rb
index 157454d4e10..5f7d2fa9f9a 100644
--- a/spec/features/merge_requests/user_filters_by_deployments_spec.rb
+++ b/spec/features/merge_requests/user_filters_by_deployments_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge Requests > User filters by deployments', :js do
+RSpec.describe 'Merge Requests > User filters by deployments', :js, feature_category: :code_review do
include FilteredSearchHelpers
let!(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_requests/user_filters_by_draft_spec.rb b/spec/features/merge_requests/user_filters_by_draft_spec.rb
index de070805d96..d50d7edaefb 100644
--- a/spec/features/merge_requests/user_filters_by_draft_spec.rb
+++ b/spec/features/merge_requests/user_filters_by_draft_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge Requests > User filters by draft', :js do
+RSpec.describe 'Merge Requests > User filters by draft', :js, feature_category: :code_review do
include FilteredSearchHelpers
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_requests/user_filters_by_labels_spec.rb b/spec/features/merge_requests/user_filters_by_labels_spec.rb
index 980046ccd71..030eb1b6431 100644
--- a/spec/features/merge_requests/user_filters_by_labels_spec.rb
+++ b/spec/features/merge_requests/user_filters_by_labels_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge Requests > User filters by labels', :js do
+RSpec.describe 'Merge Requests > User filters by labels', :js, feature_category: :code_review do
include FilteredSearchHelpers
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_requests/user_filters_by_milestones_spec.rb b/spec/features/merge_requests/user_filters_by_milestones_spec.rb
index 877d5e6a4ee..abdb6c7787b 100644
--- a/spec/features/merge_requests/user_filters_by_milestones_spec.rb
+++ b/spec/features/merge_requests/user_filters_by_milestones_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge Requests > User filters by milestones', :js do
+RSpec.describe 'Merge Requests > User filters by milestones', :js, feature_category: :code_review do
include FilteredSearchHelpers
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_requests/user_filters_by_multiple_criteria_spec.rb b/spec/features/merge_requests/user_filters_by_multiple_criteria_spec.rb
index 3aba023b077..ae171f47ec3 100644
--- a/spec/features/merge_requests/user_filters_by_multiple_criteria_spec.rb
+++ b/spec/features/merge_requests/user_filters_by_multiple_criteria_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge requests > User filters by multiple criteria', :js do
+RSpec.describe 'Merge requests > User filters by multiple criteria', :js, feature_category: :code_review do
include FilteredSearchHelpers
let!(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_requests/user_filters_by_target_branch_spec.rb b/spec/features/merge_requests/user_filters_by_target_branch_spec.rb
index 1d9c80238f5..e0755695f5c 100644
--- a/spec/features/merge_requests/user_filters_by_target_branch_spec.rb
+++ b/spec/features/merge_requests/user_filters_by_target_branch_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge Requests > User filters by target branch', :js do
+RSpec.describe 'Merge Requests > User filters by target branch', :js, feature_category: :code_review do
include FilteredSearchHelpers
let!(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_requests/user_lists_merge_requests_spec.rb b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
index 2743f7e8ed4..d9c3bcda0d3 100644
--- a/spec/features/merge_requests/user_lists_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge requests > User lists merge requests' do
+RSpec.describe 'Merge requests > User lists merge requests', feature_category: :code_review do
include MergeRequestHelpers
include SortingHelper
diff --git a/spec/features/merge_requests/user_mass_updates_spec.rb b/spec/features/merge_requests/user_mass_updates_spec.rb
index 5c3cb098e28..133017d5b25 100644
--- a/spec/features/merge_requests/user_mass_updates_spec.rb
+++ b/spec/features/merge_requests/user_mass_updates_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge requests > User mass updates', :js do
+RSpec.describe 'Merge requests > User mass updates', :js, feature_category: :code_review do
let(:project) { create(:project, :repository) }
let(:user) { project.creator }
let(:user2) { create(:user) }
diff --git a/spec/features/merge_requests/user_sees_empty_state_spec.rb b/spec/features/merge_requests/user_sees_empty_state_spec.rb
index 056da53c47b..a50ea300249 100644
--- a/spec/features/merge_requests/user_sees_empty_state_spec.rb
+++ b/spec/features/merge_requests/user_sees_empty_state_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees empty state' do
+RSpec.describe 'Merge request > User sees empty state', feature_category: :code_review do
include ProjectForksHelper
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_requests/user_sorts_merge_requests_spec.rb b/spec/features/merge_requests/user_sorts_merge_requests_spec.rb
index 4a124299c61..d268cfc59f3 100644
--- a/spec/features/merge_requests/user_sorts_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_sorts_merge_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User sorts merge requests', :js do
+RSpec.describe 'User sorts merge requests', :js, feature_category: :code_review do
include CookieHelper
include Spec::Support::Helpers::Features::SortingHelpers
diff --git a/spec/features/merge_requests/user_views_all_merge_requests_spec.rb b/spec/features/merge_requests/user_views_all_merge_requests_spec.rb
index f8fe2c5ebe2..b55e4bd153f 100644
--- a/spec/features/merge_requests/user_views_all_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_views_all_merge_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views all merge requests' do
+RSpec.describe 'User views all merge requests', feature_category: :code_review do
let!(:closed_merge_request) { create(:closed_merge_request, source_project: project, target_project: project) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:project) { create(:project, :public) }
diff --git a/spec/features/merge_requests/user_views_closed_merge_requests_spec.rb b/spec/features/merge_requests/user_views_closed_merge_requests_spec.rb
index abc652c3bbd..4c2598dcc9c 100644
--- a/spec/features/merge_requests/user_views_closed_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_views_closed_merge_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views closed merge requests' do
+RSpec.describe 'User views closed merge requests', feature_category: :code_review do
let!(:closed_merge_request) { create(:closed_merge_request, source_project: project, target_project: project) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:project) { create(:project, :public) }
diff --git a/spec/features/merge_requests/user_views_merged_merge_requests_spec.rb b/spec/features/merge_requests/user_views_merged_merge_requests_spec.rb
index 3b93fb7e4bf..2526f1a855b 100644
--- a/spec/features/merge_requests/user_views_merged_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_views_merged_merge_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views merged merge requests' do
+RSpec.describe 'User views merged merge requests', feature_category: :code_review do
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let!(:merged_merge_request) { create(:merged_merge_request, source_project: project, target_project: project) }
let(:project) { create(:project, :public) }
diff --git a/spec/features/merge_requests/user_views_open_merge_requests_spec.rb b/spec/features/merge_requests/user_views_open_merge_requests_spec.rb
index 49509f89a8d..3c53bc5e283 100644
--- a/spec/features/merge_requests/user_views_open_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_views_open_merge_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views open merge requests' do
+RSpec.describe 'User views open merge requests', feature_category: :code_review do
let_it_be(:user) { create(:user) }
shared_examples_for 'shows merge requests' do
diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb
index 98d623902a5..50cd6b9e801 100644
--- a/spec/features/milestone_spec.rb
+++ b/spec/features/milestone_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Milestone' do
+RSpec.describe 'Milestone', feature_category: :team_planning do
let(:group) { create(:group, :public) }
let(:project) { create(:project, :public, namespace: group) }
let(:user) { create(:user) }
diff --git a/spec/features/milestones/user_creates_milestone_spec.rb b/spec/features/milestones/user_creates_milestone_spec.rb
index 1ab231632fb..b750f035e36 100644
--- a/spec/features/milestones/user_creates_milestone_spec.rb
+++ b/spec/features/milestones/user_creates_milestone_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "User creates milestone", :js do
+RSpec.describe "User creates milestone", :js, feature_category: :team_planning do
let_it_be(:developer) { create(:user) }
let_it_be(:inherited_guest) { create(:user) }
let_it_be(:inherited_developer) { create(:user) }
diff --git a/spec/features/milestones/user_deletes_milestone_spec.rb b/spec/features/milestones/user_deletes_milestone_spec.rb
index 40626407642..141e626c6f3 100644
--- a/spec/features/milestones/user_deletes_milestone_spec.rb
+++ b/spec/features/milestones/user_deletes_milestone_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "User deletes milestone", :js do
+RSpec.describe "User deletes milestone", :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
diff --git a/spec/features/milestones/user_edits_milestone_spec.rb b/spec/features/milestones/user_edits_milestone_spec.rb
index 3edd50922b6..438ca6a2518 100644
--- a/spec/features/milestones/user_edits_milestone_spec.rb
+++ b/spec/features/milestones/user_edits_milestone_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "User edits milestone", :js do
+RSpec.describe "User edits milestone", :js, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:milestone) { create(:milestone, project: project, start_date: Date.today, due_date: 5.days.from_now) }
diff --git a/spec/features/milestones/user_promotes_milestone_spec.rb b/spec/features/milestones/user_promotes_milestone_spec.rb
index a9c3c9706a0..0eacf36cdde 100644
--- a/spec/features/milestones/user_promotes_milestone_spec.rb
+++ b/spec/features/milestones/user_promotes_milestone_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User promotes milestone' do
+RSpec.describe 'User promotes milestone', feature_category: :team_planning do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, namespace: group) }
diff --git a/spec/features/milestones/user_sees_breadcrumb_links_spec.rb b/spec/features/milestones/user_sees_breadcrumb_links_spec.rb
index e9cfa9b20dc..febe803426a 100644
--- a/spec/features/milestones/user_sees_breadcrumb_links_spec.rb
+++ b/spec/features/milestones/user_sees_breadcrumb_links_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'New project milestone breadcrumb' do
+RSpec.describe 'New project milestone breadcrumb', feature_category: :team_planning do
let(:project) { create(:project) }
let(:milestone) { create(:milestone, project: project) }
let(:user) { project.creator }
diff --git a/spec/features/milestones/user_views_milestone_spec.rb b/spec/features/milestones/user_views_milestone_spec.rb
index 8674d59afdf..6c8dc47cce1 100644
--- a/spec/features/milestones/user_views_milestone_spec.rb
+++ b/spec/features/milestones/user_views_milestone_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "User views milestone" do
+RSpec.describe "User views milestone", feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, group: group) }
diff --git a/spec/features/milestones/user_views_milestones_spec.rb b/spec/features/milestones/user_views_milestones_spec.rb
index 752cc63486f..81823d6ba30 100644
--- a/spec/features/milestones/user_views_milestones_spec.rb
+++ b/spec/features/milestones/user_views_milestones_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "User views milestones" do
+RSpec.describe "User views milestones", feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:milestone) { create(:milestone, project: project) }
diff --git a/spec/features/monitor_sidebar_link_spec.rb b/spec/features/monitor_sidebar_link_spec.rb
index 4f529179522..d5f987d15c2 100644
--- a/spec/features/monitor_sidebar_link_spec.rb
+++ b/spec/features/monitor_sidebar_link_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
+RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures, feature_category: :not_owned do
let_it_be_with_reload(:project) { create(:project, :internal, :repository) }
let_it_be(:user) { create(:user) }
@@ -19,21 +19,13 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
let(:enabled) { Featurable::PRIVATE }
let(:disabled) { Featurable::DISABLED }
- where(:flag_enabled, :operations_access_level, :monitor_level, :render) do
- true | ref(:disabled) | ref(:enabled) | true
- true | ref(:disabled) | ref(:disabled) | false
- true | ref(:enabled) | ref(:enabled) | true
- true | ref(:enabled) | ref(:disabled) | false
- false | ref(:disabled) | ref(:enabled) | false
- false | ref(:disabled) | ref(:disabled) | false
- false | ref(:enabled) | ref(:enabled) | true
- false | ref(:enabled) | ref(:disabled) | true
+ where(:monitor_level, :render) do
+ ref(:enabled) | true
+ ref(:disabled) | false
end
with_them do
it 'renders when expected to' do
- stub_feature_flags(split_operations_visibility_permissions: flag_enabled)
- project.project_feature.update_attribute(:operations_access_level, operations_access_level)
project.project_feature.update_attribute(:monitor_access_level, monitor_level)
visit project_issues_path(project)
@@ -51,7 +43,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
let(:access_level) { ProjectFeature::PUBLIC }
before do
- project.project_feature.update_attribute(:operations_access_level, access_level)
project.project_feature.update_attribute(:monitor_access_level, access_level)
end
@@ -67,41 +58,19 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project))
end
- context 'with new monitor visiblity flag disabled' do
- stub_feature_flags(split_operations_visibility_permissions: false)
+ context 'when monitor project feature is PRIVATE' do
+ let(:access_level) { ProjectFeature::PRIVATE }
- context 'when operations project feature is PRIVATE' do
- let(:access_level) { ProjectFeature::PRIVATE }
-
- it 'does not show the `Monitor` menu' do
- expect(page).not_to have_selector('a.shortcuts-monitor')
- end
- end
-
- context 'when operations project feature is DISABLED' do
- let(:access_level) { ProjectFeature::DISABLED }
-
- it 'does not show the `Operations` menu' do
- expect(page).not_to have_selector('a.shortcuts-monitor')
- end
+ it 'does not show the `Monitor` menu' do
+ expect(page).not_to have_selector('a.shortcuts-monitor')
end
end
- context 'with new monitor visiblity flag enabled' do
- context 'when monitor project feature is PRIVATE' do
- let(:access_level) { ProjectFeature::PRIVATE }
+ context 'when monitor project feature is DISABLED' do
+ let(:access_level) { ProjectFeature::DISABLED }
- it 'does not show the `Monitor` menu' do
- expect(page).not_to have_selector('a.shortcuts-monitor')
- end
- end
-
- context 'when operations project feature is DISABLED' do
- let(:access_level) { ProjectFeature::DISABLED }
-
- it 'does not show the `Operations` menu' do
- expect(page).not_to have_selector('a.shortcuts-monitor')
- end
+ it 'does not show the `Monitor` menu' do
+ expect(page).not_to have_selector('a.shortcuts-monitor')
end
end
end
diff --git a/spec/features/nav/new_nav_toggle_spec.rb b/spec/features/nav/new_nav_toggle_spec.rb
new file mode 100644
index 00000000000..f040d801cfb
--- /dev/null
+++ b/spec/features/nav/new_nav_toggle_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'new navigation toggle', :js, feature_category: :navigation do
+ let_it_be(:user) { create(:user) }
+
+ before do
+ user.update!(use_new_navigation: user_preference)
+ stub_feature_flags(super_sidebar_nav: new_nav_ff)
+ sign_in(user)
+ visit explore_projects_path
+ end
+
+ context 'with feature flag off' do
+ let(:new_nav_ff) { false }
+
+ where(:user_preference) do
+ [true, false]
+ end
+
+ with_them do
+ it 'shows old topbar user dropdown with no way to toggle to new nav' do
+ within '.js-header-content .js-nav-user-dropdown' do
+ find('a[data-toggle="dropdown"]').click
+ expect(page).not_to have_content('Navigation redesign')
+ end
+ end
+ end
+ end
+
+ context 'with feature flag on' do
+ let(:new_nav_ff) { true }
+
+ context 'when user has new nav disabled' do
+ let(:user_preference) { false }
+
+ it 'allows to enable new nav', :aggregate_failures do
+ within '.js-nav-user-dropdown' do
+ find('a[data-toggle="dropdown"]').click
+ expect(page).to have_content('Navigation redesign')
+
+ toggle = page.find('.gl-toggle:not(.is-checked)')
+ toggle.click # reloads the page
+ end
+
+ wait_for_requests
+
+ expect(user.reload.use_new_navigation).to eq true
+ end
+ end
+
+ context 'when user has new nav enabled' do
+ let(:user_preference) { true }
+
+ it 'allows to disable new nav', :aggregate_failures do
+ within '.js-nav-user-dropdown' do
+ find('a[data-toggle="dropdown"]').click
+ expect(page).to have_content('Navigation redesign')
+
+ toggle = page.find('.gl-toggle.is-checked')
+ toggle.click # reloads the page
+ end
+
+ wait_for_requests
+
+ expect(user.reload.use_new_navigation).to eq false
+ end
+ end
+ end
+end
diff --git a/spec/features/nav/top_nav_responsive_spec.rb b/spec/features/nav/top_nav_responsive_spec.rb
index 4f8e47b5068..d038c5d9e32 100644
--- a/spec/features/nav/top_nav_responsive_spec.rb
+++ b/spec/features/nav/top_nav_responsive_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'top nav responsive', :js do
+RSpec.describe 'top nav responsive', :js, feature_category: :navigation do
include MobileHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/nav/top_nav_tooltip_spec.rb b/spec/features/nav/top_nav_tooltip_spec.rb
index a110c6cfecf..17828778112 100644
--- a/spec/features/nav/top_nav_tooltip_spec.rb
+++ b/spec/features/nav/top_nav_tooltip_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'top nav tooltips', :js do
+RSpec.describe 'top nav tooltips', :js, feature_category: :navigation do
let_it_be(:user) { create(:user) }
before do
diff --git a/spec/features/oauth_login_spec.rb b/spec/features/oauth_login_spec.rb
index fca8972b56c..07d0fca0139 100644
--- a/spec/features/oauth_login_spec.rb
+++ b/spec/features/oauth_login_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'OAuth Login', :allow_forgery_protection do
+RSpec.describe 'OAuth Login', :allow_forgery_protection, feature_category: :system_access do
include DeviseHelpers
def enter_code(code)
diff --git a/spec/features/oauth_provider_authorize_spec.rb b/spec/features/oauth_provider_authorize_spec.rb
index f5a1a35b66f..7638563b4a3 100644
--- a/spec/features/oauth_provider_authorize_spec.rb
+++ b/spec/features/oauth_provider_authorize_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'OAuth Provider' do
+RSpec.describe 'OAuth Provider', feature_category: :system_access do
describe 'Standard OAuth Authorization' do
let(:application) { create(:oauth_application, scopes: 'read_user') }
diff --git a/spec/features/oauth_registration_spec.rb b/spec/features/oauth_registration_spec.rb
index 0a35b5a7e42..48996164bd3 100644
--- a/spec/features/oauth_registration_spec.rb
+++ b/spec/features/oauth_registration_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'OAuth Registration', :js, :allow_forgery_protection do
+RSpec.describe 'OAuth Registration', :js, :allow_forgery_protection, feature_category: :system_access do
include LoginHelpers
include TermsHelper
using RSpec::Parameterized::TableSyntax
diff --git a/spec/features/one_trust_spec.rb b/spec/features/one_trust_spec.rb
index a7dfbfd6bdf..2aeb722886e 100644
--- a/spec/features/one_trust_spec.rb
+++ b/spec/features/one_trust_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'OneTrust' do
+RSpec.describe 'OneTrust', feature_category: :system_access do
context 'almost there page' do
context 'when OneTrust is enabled' do
let_it_be(:onetrust_url) { 'https://*.onetrust.com' }
diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb
index b2739454b52..272365ac7ee 100644
--- a/spec/features/participants_autocomplete_spec.rb
+++ b/spec/features/participants_autocomplete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Member autocomplete', :js do
+RSpec.describe 'Member autocomplete', :js, feature_category: :team_planning do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:user) { create(:user) }
let_it_be(:author) { create(:user) }
diff --git a/spec/features/password_reset_spec.rb b/spec/features/password_reset_spec.rb
index f89e19f5361..7d36e905327 100644
--- a/spec/features/password_reset_spec.rb
+++ b/spec/features/password_reset_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Password reset' do
+RSpec.describe 'Password reset', feature_category: :system_access do
describe 'throttling' do
it 'sends reset instructions when not previously sent' do
user = create(:user)
diff --git a/spec/features/populate_new_pipeline_vars_with_params_spec.rb b/spec/features/populate_new_pipeline_vars_with_params_spec.rb
index 75fa8561235..a83b5a81a41 100644
--- a/spec/features/populate_new_pipeline_vars_with_params_spec.rb
+++ b/spec/features/populate_new_pipeline_vars_with_params_spec.rb
@@ -2,47 +2,29 @@
require 'spec_helper'
-RSpec.describe "Populate new pipeline CI variables with url params", :js do
+RSpec.describe "Populate new pipeline CI variables with url params", :js, feature_category: :pipeline_authoring do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:page_path) { new_project_pipeline_path(project) }
- shared_examples 'form pre-filled with URL params' do
- before do
- sign_in(user)
- project.add_maintainer(user)
+ before do
+ sign_in(user)
+ project.add_maintainer(user)
- visit "#{page_path}?var[key1]=value1&file_var[key2]=value2"
- end
-
- it "var[key1]=value1 populates env_var variable correctly" do
- page.within(all("[data-testid='ci-variable-row']")[0]) do
- expect(find("[data-testid='pipeline-form-ci-variable-key']").value).to eq('key1')
- expect(find("[data-testid='pipeline-form-ci-variable-value']").value).to eq('value1')
- end
- end
-
- it "file_var[key2]=value2 populates file variable correctly" do
- page.within(all("[data-testid='ci-variable-row']")[1]) do
- expect(find("[data-testid='pipeline-form-ci-variable-key']").value).to eq('key2')
- expect(find("[data-testid='pipeline-form-ci-variable-value']").value).to eq('value2')
- end
- end
+ visit "#{page_path}?var[key1]=value1&file_var[key2]=value2"
end
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(run_pipeline_graphql: false)
+ it "var[key1]=value1 populates env_var variable correctly" do
+ page.within(all("[data-testid='ci-variable-row']")[0]) do
+ expect(find("[data-testid='pipeline-form-ci-variable-key']").value).to eq('key1')
+ expect(find("[data-testid='pipeline-form-ci-variable-value']").value).to eq('value1')
end
-
- it_behaves_like 'form pre-filled with URL params'
end
- context 'when feature flag is enabled' do
- before do
- stub_feature_flags(run_pipeline_graphql: true)
+ it "file_var[key2]=value2 populates file variable correctly" do
+ page.within(all("[data-testid='ci-variable-row']")[1]) do
+ expect(find("[data-testid='pipeline-form-ci-variable-key']").value).to eq('key2')
+ expect(find("[data-testid='pipeline-form-ci-variable-value']").value).to eq('value2')
end
-
- it_behaves_like 'form pre-filled with URL params'
end
end
diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb
index ca156642bc8..87a65438768 100644
--- a/spec/features/profile_spec.rb
+++ b/spec/features/profile_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Profile account page', :js do
+RSpec.describe 'Profile account page', :js, feature_category: :users do
include Spec::Support::Helpers::ModalHelpers
let(:user) { create(:user) }
diff --git a/spec/features/profiles/account_spec.rb b/spec/features/profiles/account_spec.rb
index 4fe0c3d035e..82c45862e07 100644
--- a/spec/features/profiles/account_spec.rb
+++ b/spec/features/profiles/account_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Profile > Account', :js do
+RSpec.describe 'Profile > Account', :js, feature_category: :users do
let(:user) { create(:user, username: 'foo') }
before do
diff --git a/spec/features/profiles/active_sessions_spec.rb b/spec/features/profiles/active_sessions_spec.rb
index d0819bb5363..5c20735cf35 100644
--- a/spec/features/profiles/active_sessions_spec.rb
+++ b/spec/features/profiles/active_sessions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
+RSpec.describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state, feature_category: :users do
include Spec::Support::Helpers::ModalHelpers
let(:user) do
diff --git a/spec/features/profiles/chat_names_spec.rb b/spec/features/profiles/chat_names_spec.rb
index 82134de582a..b3d65ab3a3c 100644
--- a/spec/features/profiles/chat_names_spec.rb
+++ b/spec/features/profiles/chat_names_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Profile > Chat' do
+RSpec.describe 'Profile > Chat', feature_category: :users do
let(:user) { create(:user) }
let(:integration) { create(:integration) }
diff --git a/spec/features/profiles/emails_spec.rb b/spec/features/profiles/emails_spec.rb
index 24917412826..e8ea227c072 100644
--- a/spec/features/profiles/emails_spec.rb
+++ b/spec/features/profiles/emails_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Profile > Emails' do
+RSpec.describe 'Profile > Emails', feature_category: :users do
let(:user) { create(:user) }
let(:other_user) { create(:user) }
diff --git a/spec/features/profiles/gpg_keys_spec.rb b/spec/features/profiles/gpg_keys_spec.rb
index 4eedeeac262..1d014f983e7 100644
--- a/spec/features/profiles/gpg_keys_spec.rb
+++ b/spec/features/profiles/gpg_keys_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Profile > GPG Keys' do
+RSpec.describe 'Profile > GPG Keys', feature_category: :users do
let(:user) { create(:user, email: GpgHelpers::User2.emails.first) }
before do
diff --git a/spec/features/profiles/keys_spec.rb b/spec/features/profiles/keys_spec.rb
index 65944f5a537..7a2a12d8dca 100644
--- a/spec/features/profiles/keys_spec.rb
+++ b/spec/features/profiles/keys_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Profile > SSH Keys' do
+RSpec.describe 'Profile > SSH Keys', feature_category: :users do
let(:user) { create(:user) }
before do
@@ -80,7 +80,7 @@ RSpec.describe 'Profile > SSH Keys' do
shared_examples 'removes key' do
it 'removes key' do
visit path
- click_button('Delete')
+ find('[data-testid=remove-icon]').click
page.within('.modal') do
page.click_button('Delete')
diff --git a/spec/features/profiles/oauth_applications_spec.rb b/spec/features/profiles/oauth_applications_spec.rb
index 7d8cd2dc6ca..80d05fd5cc7 100644
--- a/spec/features/profiles/oauth_applications_spec.rb
+++ b/spec/features/profiles/oauth_applications_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Profile > Applications' do
+RSpec.describe 'Profile > Applications', feature_category: :users do
include Spec::Support::Helpers::ModalHelpers
let(:user) { create(:user) }
diff --git a/spec/features/profiles/password_spec.rb b/spec/features/profiles/password_spec.rb
index 8887ff1746d..b324ee17873 100644
--- a/spec/features/profiles/password_spec.rb
+++ b/spec/features/profiles/password_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Profile > Password' do
+RSpec.describe 'Profile > Password', feature_category: :users do
let(:user) { create(:user) }
def fill_passwords(password, confirmation)
diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb
index 3ae88da06f6..3087d7ff296 100644
--- a/spec/features/profiles/personal_access_tokens_spec.rb
+++ b/spec/features/profiles/personal_access_tokens_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Profile > Personal Access Tokens', :js do
+RSpec.describe 'Profile > Personal Access Tokens', :js, feature_category: :users do
include Spec::Support::Helpers::ModalHelpers
include Spec::Support::Helpers::AccessTokenHelpers
diff --git a/spec/features/profiles/two_factor_auths_spec.rb b/spec/features/profiles/two_factor_auths_spec.rb
index decc2904b6e..8dddaad11c3 100644
--- a/spec/features/profiles/two_factor_auths_spec.rb
+++ b/spec/features/profiles/two_factor_auths_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Two factor auths' do
+RSpec.describe 'Two factor auths', feature_category: :users do
include Spec::Support::Helpers::ModalHelpers
context 'when signed in' do
diff --git a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb
index 1b6215c1308..197a33c355d 100644
--- a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb
+++ b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Profile > Notifications > User changes notified_of_own_activity setting', :js do
+RSpec.describe 'Profile > Notifications > User changes notified_of_own_activity setting', :js,
+feature_category: :users do
let(:user) { create(:user) }
before do
diff --git a/spec/features/profiles/user_edit_preferences_spec.rb b/spec/features/profiles/user_edit_preferences_spec.rb
index c724de04043..1a231f1d269 100644
--- a/spec/features/profiles/user_edit_preferences_spec.rb
+++ b/spec/features/profiles/user_edit_preferences_spec.rb
@@ -2,16 +2,18 @@
require 'spec_helper'
-RSpec.describe 'User edit preferences profile', :js do
+RSpec.describe 'User edit preferences profile', :js, feature_category: :users do
include StubLanguagesTranslationPercentage
# Empty value doesn't change the levels
let(:language_percentage_levels) { nil }
let(:user) { create(:user) }
+ let(:vscode_web_ide) { true }
before do
stub_languages_translation_percentage(language_percentage_levels)
stub_feature_flags(user_time_settings: true)
+ stub_feature_flags(vscode_web_ide: vscode_web_ide)
sign_in(user)
visit(profile_preferences_path)
end
@@ -36,6 +38,24 @@ RSpec.describe 'User edit preferences profile', :js do
expect(field).not_to be_checked
end
+ it 'allows the user to toggle using the legacy web ide' do
+ field = page.find_field("user[use_legacy_web_ide]")
+
+ expect(field).not_to be_checked
+
+ field.click
+
+ expect(field).to be_checked
+ end
+
+ describe 'when vscode_web_ide feature flag is disabled' do
+ let(:vscode_web_ide) { false }
+
+ it 'does not display the legacy web ide user preference' do
+ expect(page).not_to have_field("user[use_legacy_web_ide]")
+ end
+ end
+
describe 'User changes tab width to acceptable value' do
it 'shows success message' do
fill_in 'Tab width', with: 9
diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb
index 1d99f7a8511..67604292090 100644
--- a/spec/features/profiles/user_edit_profile_spec.rb
+++ b/spec/features/profiles/user_edit_profile_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User edit profile' do
+RSpec.describe 'User edit profile', feature_category: :users do
include Spec::Support::Helpers::Features::NotesHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/profiles/user_manages_applications_spec.rb b/spec/features/profiles/user_manages_applications_spec.rb
index ea7a6b4b6ba..179da61b8ed 100644
--- a/spec/features/profiles/user_manages_applications_spec.rb
+++ b/spec/features/profiles/user_manages_applications_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User manages applications' do
+RSpec.describe 'User manages applications', feature_category: :users do
let_it_be(:user) { create(:user) }
let_it_be(:new_application_path) { applications_profile_path }
let_it_be(:index_path) { oauth_applications_path }
diff --git a/spec/features/profiles/user_manages_emails_spec.rb b/spec/features/profiles/user_manages_emails_spec.rb
index b037d5048aa..16a9fbc2f47 100644
--- a/spec/features/profiles/user_manages_emails_spec.rb
+++ b/spec/features/profiles/user_manages_emails_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User manages emails' do
+RSpec.describe 'User manages emails', feature_category: :users do
let(:user) { create(:user) }
let(:other_user) { create(:user) }
diff --git a/spec/features/profiles/user_search_settings_spec.rb b/spec/features/profiles/user_search_settings_spec.rb
index 0b05e6c9489..09ee8ddeaab 100644
--- a/spec/features/profiles/user_search_settings_spec.rb
+++ b/spec/features/profiles/user_search_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches their settings', :js do
+RSpec.describe 'User searches their settings', :js, feature_category: :users do
let_it_be(:user) { create(:user) }
before do
diff --git a/spec/features/profiles/user_visits_notifications_tab_spec.rb b/spec/features/profiles/user_visits_notifications_tab_spec.rb
index e960cc76219..d212982f4e3 100644
--- a/spec/features/profiles/user_visits_notifications_tab_spec.rb
+++ b/spec/features/profiles/user_visits_notifications_tab_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User visits the notifications tab', :js do
+RSpec.describe 'User visits the notifications tab', :js, feature_category: :users do
let(:project) { create(:project) }
let(:user) { create(:user) }
diff --git a/spec/features/profiles/user_visits_profile_account_page_spec.rb b/spec/features/profiles/user_visits_profile_account_page_spec.rb
index b4d1185412b..1cf34478ecf 100644
--- a/spec/features/profiles/user_visits_profile_account_page_spec.rb
+++ b/spec/features/profiles/user_visits_profile_account_page_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User visits the profile account page' do
+RSpec.describe 'User visits the profile account page', feature_category: :users do
let(:user) { create(:user) }
before do
diff --git a/spec/features/profiles/user_visits_profile_authentication_log_spec.rb b/spec/features/profiles/user_visits_profile_authentication_log_spec.rb
index 22292eff9a3..726cca4a4bd 100644
--- a/spec/features/profiles/user_visits_profile_authentication_log_spec.rb
+++ b/spec/features/profiles/user_visits_profile_authentication_log_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User visits the authentication log' do
+RSpec.describe 'User visits the authentication log', feature_category: :users do
let(:user) { create(:user) }
context 'when user signed in' do
diff --git a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
index 4c61e8d45e4..9eee1b85e5e 100644
--- a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
+++ b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User visits the profile preferences page', :js do
+RSpec.describe 'User visits the profile preferences page', :js, feature_category: :users do
include Select2Helper
let(:user) { create(:user) }
diff --git a/spec/features/profiles/user_visits_profile_spec.rb b/spec/features/profiles/user_visits_profile_spec.rb
index df096c2f151..7fca0f24deb 100644
--- a/spec/features/profiles/user_visits_profile_spec.rb
+++ b/spec/features/profiles/user_visits_profile_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User visits their profile' do
+RSpec.describe 'User visits their profile', feature_category: :users do
let_it_be_with_refind(:user) { create(:user) }
before do
diff --git a/spec/features/profiles/user_visits_profile_ssh_keys_page_spec.rb b/spec/features/profiles/user_visits_profile_ssh_keys_page_spec.rb
index 0531434f00c..8467e9abeaf 100644
--- a/spec/features/profiles/user_visits_profile_ssh_keys_page_spec.rb
+++ b/spec/features/profiles/user_visits_profile_ssh_keys_page_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User visits the profile SSH keys page' do
+RSpec.describe 'User visits the profile SSH keys page', feature_category: :users do
let(:user) { create(:user) }
before do
diff --git a/spec/features/project_group_variables_spec.rb b/spec/features/project_group_variables_spec.rb
index fc482261fb1..0e1e6e49c6d 100644
--- a/spec/features/project_group_variables_spec.rb
+++ b/spec/features/project_group_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project group variables', :js do
+RSpec.describe 'Project group variables', :js, feature_category: :pipeline_authoring do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:subgroup) { create(:group, parent: group) }
diff --git a/spec/features/project_variables_spec.rb b/spec/features/project_variables_spec.rb
index 33b4af3b5aa..d1258937ce6 100644
--- a/spec/features/project_variables_spec.rb
+++ b/spec/features/project_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project variables', :js do
+RSpec.describe 'Project variables', :js, feature_category: :pipeline_authoring do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:variable) { create(:ci_variable, key: 'test_key', value: 'test_value', masked: true) }
diff --git a/spec/features/projects/active_tabs_spec.rb b/spec/features/projects/active_tabs_spec.rb
index ff97d3c9503..c27c9530f61 100644
--- a/spec/features/projects/active_tabs_spec.rb
+++ b/spec/features/projects/active_tabs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project active tab' do
+RSpec.describe 'Project active tab', feature_category: :projects do
let_it_be(:project) { create(:project, :repository, :with_namespace_settings) }
let(:user) { project.first_owner }
diff --git a/spec/features/projects/activity/rss_spec.rb b/spec/features/projects/activity/rss_spec.rb
index a3e511b5c22..5297f30220d 100644
--- a/spec/features/projects/activity/rss_spec.rb
+++ b/spec/features/projects/activity/rss_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project Activity RSS' do
+RSpec.describe 'Project Activity RSS', feature_category: :projects do
let(:project) { create(:project, :public) }
let(:user) { project.first_owner }
let(:path) { activity_project_path(project) }
diff --git a/spec/features/projects/activity/user_sees_activity_spec.rb b/spec/features/projects/activity/user_sees_activity_spec.rb
index a9cdbd5c342..cfa62415c49 100644
--- a/spec/features/projects/activity/user_sees_activity_spec.rb
+++ b/spec/features/projects/activity/user_sees_activity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Activity > User sees activity' do
+RSpec.describe 'Projects > Activity > User sees activity', feature_category: :projects do
let(:project) { create(:project, :repository, :public) }
let(:user) { project.creator }
let(:issue) { create(:issue, project: project) }
diff --git a/spec/features/projects/activity/user_sees_design_activity_spec.rb b/spec/features/projects/activity/user_sees_design_activity_spec.rb
index 70153921b82..6a51e548700 100644
--- a/spec/features/projects/activity/user_sees_design_activity_spec.rb
+++ b/spec/features/projects/activity/user_sees_design_activity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Activity > User sees design Activity', :js do
+RSpec.describe 'Projects > Activity > User sees design Activity', :js, feature_category: :design_management do
include DesignManagementTestHelpers
let_it_be(:uploader) { create(:user) }
diff --git a/spec/features/projects/activity/user_sees_design_comment_spec.rb b/spec/features/projects/activity/user_sees_design_comment_spec.rb
index 3a8e2790858..2d333e55b13 100644
--- a/spec/features/projects/activity/user_sees_design_comment_spec.rb
+++ b/spec/features/projects/activity/user_sees_design_comment_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Activity > User sees design comment', :js do
+RSpec.describe 'Projects > Activity > User sees design comment', :js, feature_category: :design_management do
include DesignManagementTestHelpers
let_it_be(:project) { create(:project, :repository, :public) }
diff --git a/spec/features/projects/activity/user_sees_private_activity_spec.rb b/spec/features/projects/activity/user_sees_private_activity_spec.rb
index 86692bc6b4c..e0aaf1dbbc3 100644
--- a/spec/features/projects/activity/user_sees_private_activity_spec.rb
+++ b/spec/features/projects/activity/user_sees_private_activity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project > Activity > User sees private activity', :js do
+RSpec.describe 'Project > Activity > User sees private activity', :js, feature_category: :projects do
let(:project) { create(:project, :public) }
let(:author) { create(:user) }
let(:user) { create(:user) }
diff --git a/spec/features/projects/artifacts/file_spec.rb b/spec/features/projects/artifacts/file_spec.rb
index f97c1b0e543..fe38cbc70f1 100644
--- a/spec/features/projects/artifacts/file_spec.rb
+++ b/spec/features/projects/artifacts/file_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Artifact file', :js do
+RSpec.describe 'Artifact file', :js, feature_category: :build_artifacts do
let(:project) { create(:project, :public) }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
diff --git a/spec/features/projects/artifacts/raw_spec.rb b/spec/features/projects/artifacts/raw_spec.rb
index c10cb56a44b..544875d36e3 100644
--- a/spec/features/projects/artifacts/raw_spec.rb
+++ b/spec/features/projects/artifacts/raw_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Raw artifact' do
+RSpec.describe 'Raw artifact', feature_category: :build_artifacts do
let(:project) { create(:project, :public) }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) }
diff --git a/spec/features/projects/artifacts/user_browses_artifacts_spec.rb b/spec/features/projects/artifacts/user_browses_artifacts_spec.rb
index c0d710fe186..6948a26196b 100644
--- a/spec/features/projects/artifacts/user_browses_artifacts_spec.rb
+++ b/spec/features/projects/artifacts/user_browses_artifacts_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User browses artifacts" do
+RSpec.describe "User browses artifacts", feature_category: :build_artifacts do
let(:project) { create(:project, :public) }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) }
diff --git a/spec/features/projects/artifacts/user_downloads_artifacts_spec.rb b/spec/features/projects/artifacts/user_downloads_artifacts_spec.rb
index 7d6ae03e08e..48dcb95e09b 100644
--- a/spec/features/projects/artifacts/user_downloads_artifacts_spec.rb
+++ b/spec/features/projects/artifacts/user_downloads_artifacts_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User downloads artifacts" do
+RSpec.describe "User downloads artifacts", feature_category: :build_artifacts do
let_it_be(:project) { create(:project, :repository, :public) }
let_it_be(:pipeline) { create(:ci_empty_pipeline, status: :success, sha: project.commit.id, project: project) }
let_it_be(:job) { create(:ci_build, :artifacts, :success, pipeline: pipeline) }
diff --git a/spec/features/projects/badges/coverage_spec.rb b/spec/features/projects/badges/coverage_spec.rb
index 7555e567c37..3c8b17607ca 100644
--- a/spec/features/projects/badges/coverage_spec.rb
+++ b/spec/features/projects/badges/coverage_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'test coverage badge' do
+RSpec.describe 'test coverage badge', feature_category: :code_testing do
let!(:user) { create(:user) }
let!(:project) { create(:project, :private) }
diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb
index d1e635f11c0..e6bd4b22b0a 100644
--- a/spec/features/projects/badges/list_spec.rb
+++ b/spec/features/projects/badges/list_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'list of badges' do
+RSpec.describe 'list of badges', feature_category: :continuous_integration do
before do
user = create(:user)
project = create(:project, :repository)
diff --git a/spec/features/projects/badges/pipeline_badge_spec.rb b/spec/features/projects/badges/pipeline_badge_spec.rb
index e3a01ab6fa2..c0f5d0ffead 100644
--- a/spec/features/projects/badges/pipeline_badge_spec.rb
+++ b/spec/features/projects/badges/pipeline_badge_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Pipeline Badge' do
+RSpec.describe 'Pipeline Badge', feature_category: :continuous_integration do
let_it_be(:project) { create(:project, :repository, :public) }
let(:ref) { project.default_branch }
diff --git a/spec/features/projects/blobs/blame_spec.rb b/spec/features/projects/blobs/blame_spec.rb
index 5287d5e4f7d..27b7c6ef2d5 100644
--- a/spec/features/projects/blobs/blame_spec.rb
+++ b/spec/features/projects/blobs/blame_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'File blame', :js do
+RSpec.describe 'File blame', :js, feature_category: :projects do
include TreeHelper
let_it_be(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb b/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb
index 9b0edcd09d2..48ee39dad19 100644
--- a/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb
+++ b/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Blob button line permalinks (BlobLinePermalinkUpdater)', :js do
+RSpec.describe 'Blob button line permalinks (BlobLinePermalinkUpdater)', :js, feature_category: :projects do
include TreeHelper
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb
index e01382cf31f..b7e0e3fd590 100644
--- a/spec/features/projects/blobs/blob_show_spec.rb
+++ b/spec/features/projects/blobs/blob_show_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'File blob', :js do
+RSpec.describe 'File blob', :js, feature_category: :projects do
include MobileHelpers
let(:project) { create(:project, :public, :repository) }
@@ -589,76 +589,35 @@ RSpec.describe 'File blob', :js do
file_path: '.gitlab/dashboards/custom-dashboard.yml',
file_content: file_content
).execute
- end
-
- context 'with metrics_dashboard_exhaustive_validations feature flag off' do
- before do
- stub_feature_flags(metrics_dashboard_exhaustive_validations: false)
- visit_blob('.gitlab/dashboards/custom-dashboard.yml')
- end
-
- context 'valid dashboard file' do
- let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) }
-
- it 'displays an auxiliary viewer' do
- aggregate_failures do
- # shows that dashboard yaml is valid
- expect(page).to have_content('Metrics Dashboard YAML definition is valid.')
-
- # shows a learn more link
- expect(page).to have_link('Learn more')
- end
- end
- end
-
- context 'invalid dashboard file' do
- let(:file_content) { "dashboard: 'invalid'" }
-
- it 'displays an auxiliary viewer' do
- aggregate_failures do
- # shows that dashboard yaml is invalid
- expect(page).to have_content('Metrics Dashboard YAML definition is invalid:')
- expect(page).to have_content("panel_groups: should be an array of panel_groups objects")
- # shows a learn more link
- expect(page).to have_link('Learn more')
- end
- end
- end
+ visit_blob('.gitlab/dashboards/custom-dashboard.yml')
end
- context 'with metrics_dashboard_exhaustive_validations feature flag on' do
- before do
- stub_feature_flags(metrics_dashboard_exhaustive_validations: true)
- visit_blob('.gitlab/dashboards/custom-dashboard.yml')
- end
-
- context 'valid dashboard file' do
- let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) }
+ context 'valid dashboard file' do
+ let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) }
- it 'displays an auxiliary viewer' do
- aggregate_failures do
- # shows that dashboard yaml is valid
- expect(page).to have_content('Metrics Dashboard YAML definition is valid.')
+ it 'displays an auxiliary viewer' do
+ aggregate_failures do
+ # shows that dashboard yaml is valid
+ expect(page).to have_content('Metrics Dashboard YAML definition is valid.')
- # shows a learn more link
- expect(page).to have_link('Learn more')
- end
+ # shows a learn more link
+ expect(page).to have_link('Learn more')
end
end
+ end
- context 'invalid dashboard file' do
- let(:file_content) { "dashboard: 'invalid'" }
+ context 'invalid dashboard file' do
+ let(:file_content) { "dashboard: 'invalid'" }
- it 'displays an auxiliary viewer' do
- aggregate_failures do
- # shows that dashboard yaml is invalid
- expect(page).to have_content('Metrics Dashboard YAML definition is invalid:')
- expect(page).to have_content("root is missing required keys: panel_groups")
+ it 'displays an auxiliary viewer' do
+ aggregate_failures do
+ # shows that dashboard yaml is invalid
+ expect(page).to have_content('Metrics Dashboard YAML definition is invalid:')
+ expect(page).to have_content("panel_groups: should be an array of panel_groups objects")
- # shows a learn more link
- expect(page).to have_link('Learn more')
- end
+ # shows a learn more link
+ expect(page).to have_link('Learn more')
end
end
end
@@ -1002,7 +961,7 @@ RSpec.describe 'File blob', :js do
end
it 'renders sandboxed iframe' do
- expected = %(<iframe src="/-/sandbox/swagger" sandbox="allow-scripts allow-popups" frameborder="0" width="100%" height="1000">)
+ expected = %(<iframe src="/-/sandbox/swagger" sandbox="allow-scripts allow-popups allow-forms" frameborder="0" width="100%" height="1000">)
expect(page.html).to include(expected)
end
end
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
index 5587b8abab3..144b4ed85cd 100644
--- a/spec/features/projects/blobs/edit_spec.rb
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Editing file blob', :js do
+RSpec.describe 'Editing file blob', :js, feature_category: :projects do
include Spec::Support::Helpers::Features::SourceEditorSpecHelpers
include TreeHelper
include BlobSpecHelpers
@@ -97,11 +97,16 @@ RSpec.describe 'Editing file blob', :js do
"Add a table"
]
- before do
+ it "does not have any buttons" do
+ stub_feature_flags(source_editor_toolbar: true)
visit project_edit_blob_path(project, tree_join(branch, readme_file_path))
+ buttons = page.all('.file-buttons .md-header-toolbar button[type="button"]')
+ expect(buttons.length).to eq(0)
end
- it "has defined set of toolbar buttons" do
+ it "has defined set of toolbar buttons when the flag is off" do
+ stub_feature_flags(source_editor_toolbar: false)
+ visit project_edit_blob_path(project, tree_join(branch, readme_file_path))
buttons = page.all('.file-buttons .md-header-toolbar button[type="button"]')
expect(buttons.length).to eq(toolbar_buttons.length)
toolbar_buttons.each_with_index do |button_title, i|
diff --git a/spec/features/projects/blobs/shortcuts_blob_spec.rb b/spec/features/projects/blobs/shortcuts_blob_spec.rb
index 64d643aa102..03276a737da 100644
--- a/spec/features/projects/blobs/shortcuts_blob_spec.rb
+++ b/spec/features/projects/blobs/shortcuts_blob_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Blob shortcuts', :js do
+RSpec.describe 'Blob shortcuts', :js, feature_category: :projects do
include TreeHelper
let(:project) { create(:project, :public, :repository) }
let(:path) { project.repository.ls_files(project.repository.root_ref)[0] }
diff --git a/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb b/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb
index a2db5e11c7c..a497be4cbc3 100644
--- a/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb
+++ b/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User follows pipeline suggest nudge spec when feature is enabled', :js do
+RSpec.describe 'User follows pipeline suggest nudge spec when feature is enabled', :js, feature_category: :projects do
include CookieHelper
let(:project) { create(:project, :empty_repo) }
diff --git a/spec/features/projects/blobs/user_views_pipeline_editor_button_spec.rb b/spec/features/projects/blobs/user_views_pipeline_editor_button_spec.rb
index 15e7a495e60..2f67e909543 100644
--- a/spec/features/projects/blobs/user_views_pipeline_editor_button_spec.rb
+++ b/spec/features/projects/blobs/user_views_pipeline_editor_button_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views pipeline editor button on root ci config file', :js do
+RSpec.describe 'User views pipeline editor button on root ci config file', :js, feature_category: :projects do
include BlobSpecHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/projects/branches/download_buttons_spec.rb b/spec/features/projects/branches/download_buttons_spec.rb
index 569a93a55fc..80ccd9c1417 100644
--- a/spec/features/projects/branches/download_buttons_spec.rb
+++ b/spec/features/projects/branches/download_buttons_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Download buttons in branches page' do
+RSpec.describe 'Download buttons in branches page', feature_category: :projects do
let(:user) { create(:user) }
let(:role) { :developer }
let(:status) { 'success' }
diff --git a/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb b/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
index 5f58e446ed9..eb370cfc1fc 100644
--- a/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
+++ b/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
@@ -2,10 +2,11 @@
require 'spec_helper'
-RSpec.describe 'New Branch Ref Dropdown', :js do
+RSpec.describe 'New Branch Ref Dropdown', :js, feature_category: :projects do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
- let(:toggle) { find('.create-from .dropdown-menu-toggle') }
+ let(:sha) { project.commit.sha }
+ let(:toggle) { find('.ref-selector') }
before do
project.add_maintainer(user)
@@ -14,37 +15,75 @@ RSpec.describe 'New Branch Ref Dropdown', :js do
visit new_project_branch_path(project)
end
- it 'filters a list of branches and tags' do
+ it 'finds a tag in a list' do
+ tag_name = 'v1.0.0'
+
toggle.click
- filter_by('v1.0.0')
+ filter_by(tag_name)
+
+ wait_for_requests
+
+ expect(items_count(tag_name)).to be(1)
- expect(items_count).to be(1)
+ item(tag_name).click
- filter_by('video')
+ expect(toggle).to have_content tag_name
+ end
+
+ it 'finds a branch in a list' do
+ branch_name = 'audio'
- expect(items_count).to be(1)
+ toggle.click
- find('.create-from .dropdown-content li').click
+ filter_by(branch_name)
- expect(toggle).to have_content 'video'
+ wait_for_requests
+
+ expect(items_count(branch_name)).to be(1)
+
+ item(branch_name).click
+
+ expect(toggle).to have_content branch_name
end
- it 'accepts a manually entered commit SHA' do
+ it 'finds a commit in a list' do
toggle.click
- filter_by('somecommitsha')
+ filter_by(sha)
+
+ wait_for_requests
+
+ sha_short = sha[0, 7]
- find('.create-from input[type=search]').send_keys(:enter)
+ expect(items_count(sha_short)).to be(1)
+
+ item(sha_short).click
+
+ expect(toggle).to have_content sha_short
+ end
+
+ it 'shows no results when there is no branch, tag or commit sha found' do
+ non_existing_ref = 'non_existing_branch_name'
+
+ toggle.click
+
+ filter_by(non_existing_ref)
+
+ wait_for_requests
+
+ expect(find('.gl-dropdown-contents')).not_to have_content(non_existing_ref)
+ end
- expect(toggle).to have_content 'somecommitsha'
+ def item(ref_name)
+ find('li', text: ref_name, match: :prefer_exact)
end
- def items_count
- all('.create-from .dropdown-content li').length
+ def items_count(ref_name)
+ all('li', text: ref_name, match: :prefer_exact).length
end
def filter_by(filter_text)
- fill_in 'Filter by Git revision', with: filter_text
+ fill_in _('Search by Git revision'), with: filter_text
end
end
diff --git a/spec/features/projects/branches/user_creates_branch_spec.rb b/spec/features/projects/branches/user_creates_branch_spec.rb
index be236b7ca7e..60bd77393e9 100644
--- a/spec/features/projects/branches/user_creates_branch_spec.rb
+++ b/spec/features/projects/branches/user_creates_branch_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User creates branch', :js do
+RSpec.describe 'User creates branch', :js, feature_category: :projects do
include Spec::Support::Helpers::Features::BranchesHelpers
let_it_be(:group) { create(:group, :public) }
@@ -81,11 +81,7 @@ RSpec.describe 'User creates branch', :js do
it 'does not create new branch' do
invalid_branch_name = '1.0 stable'
- fill_in('branch_name', with: invalid_branch_name)
- page.find('body').click # defocus the branch_name input
-
- select_branch('master')
- click_button('Create branch')
+ create_branch(invalid_branch_name)
expect(page).to have_content('Branch name is invalid')
expect(page).to have_content("can't contain spaces")
diff --git a/spec/features/projects/branches/user_deletes_branch_spec.rb b/spec/features/projects/branches/user_deletes_branch_spec.rb
index a89fed3a78a..92b5f176d2d 100644
--- a/spec/features/projects/branches/user_deletes_branch_spec.rb
+++ b/spec/features/projects/branches/user_deletes_branch_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User deletes branch", :js do
+RSpec.describe "User deletes branch", :js, feature_category: :projects do
include Spec::Support::Helpers::ModalHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/projects/branches/user_views_branches_spec.rb b/spec/features/projects/branches/user_views_branches_spec.rb
index 3f0614532f1..f0a1ba84ec6 100644
--- a/spec/features/projects/branches/user_views_branches_spec.rb
+++ b/spec/features/projects/branches/user_views_branches_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User views branches", :js do
+RSpec.describe "User views branches", :js, feature_category: :projects do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb
index ecf6349e431..fc7833809b3 100644
--- a/spec/features/projects/branches_spec.rb
+++ b/spec/features/projects/branches_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Branches' do
+RSpec.describe 'Branches', feature_category: :projects do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public, :repository) }
let(:repository) { project.repository }
@@ -143,7 +143,7 @@ RSpec.describe 'Branches' do
click_button "Updated date" # Open sorting dropdown
within '[data-testid="branches-dropdown"]' do
- find('p', text: 'Name').click
+ first('span', text: 'Name').click
end
expect(page).to have_content(sorted_branches(repository, count: 20, sort_by: :name))
@@ -154,7 +154,7 @@ RSpec.describe 'Branches' do
click_button "Updated date" # Open sorting dropdown
within '[data-testid="branches-dropdown"]' do
- find('p', text: 'Oldest updated').click
+ first('span', text: 'Oldest updated').click
end
expect(page).to have_content(sorted_branches(repository, count: 20, sort_by: :updated_asc))
diff --git a/spec/features/projects/ci/editor_spec.rb b/spec/features/projects/ci/editor_spec.rb
index c96d5f5823f..536152626af 100644
--- a/spec/features/projects/ci/editor_spec.rb
+++ b/spec/features/projects/ci/editor_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Pipeline Editor', :js do
+RSpec.describe 'Pipeline Editor', :js, feature_category: :pipeline_authoring do
include Spec::Support::Helpers::Features::SourceEditorSpecHelpers
let(:project) { create(:project_empty_repo, :public) }
diff --git a/spec/features/projects/ci/lint_spec.rb b/spec/features/projects/ci/lint_spec.rb
index 8d5f62d8a06..4fea07b18bc 100644
--- a/spec/features/projects/ci/lint_spec.rb
+++ b/spec/features/projects/ci/lint_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'CI Lint', :js do
+RSpec.describe 'CI Lint', :js, feature_category: :pipeline_authoring do
include Spec::Support::Helpers::Features::SourceEditorSpecHelpers
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/features/projects/classification_label_on_project_pages_spec.rb b/spec/features/projects/classification_label_on_project_pages_spec.rb
index 9522e5ce2cf..662b2296234 100644
--- a/spec/features/projects/classification_label_on_project_pages_spec.rb
+++ b/spec/features/projects/classification_label_on_project_pages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Classification label on project pages' do
+RSpec.describe 'Classification label on project pages', feature_category: :projects do
let(:project) do
create(:project, external_authorization_classification_label: 'authorized label')
end
diff --git a/spec/features/projects/cluster_agents_spec.rb b/spec/features/projects/cluster_agents_spec.rb
index 8c557a9c37a..43046db2b6c 100644
--- a/spec/features/projects/cluster_agents_spec.rb
+++ b/spec/features/projects/cluster_agents_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'ClusterAgents', :js do
+RSpec.describe 'ClusterAgents', :js, feature_category: :projects do
let_it_be(:token) { create(:cluster_agent_token, description: 'feature test token') }
let(:agent) { token.agent }
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index 5c54b7fda7c..114182982e2 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Gcp Cluster', :js do
+RSpec.describe 'Gcp Cluster', :js, feature_category: :kubernetes_management do
include GoogleApi::CloudPlatformHelpers
let(:project) { create(:project) }
diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb
index 527d038f975..34fc0a76c7f 100644
--- a/spec/features/projects/clusters/user_spec.rb
+++ b/spec/features/projects/clusters/user_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User Cluster', :js do
+RSpec.describe 'User Cluster', :js, feature_category: :kubernetes_management do
include GoogleApi::CloudPlatformHelpers
let(:project) { create(:project) }
diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb
index 9e1d66bc73e..3fb586bd143 100644
--- a/spec/features/projects/clusters_spec.rb
+++ b/spec/features/projects/clusters_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Clusters', :js do
+RSpec.describe 'Clusters', :js, feature_category: :projects do
include GoogleApi::CloudPlatformHelpers
let(:project) { create(:project) }
diff --git a/spec/features/projects/commit/builds_spec.rb b/spec/features/projects/commit/builds_spec.rb
index 7b10f72006f..dfd58a99953 100644
--- a/spec/features/projects/commit/builds_spec.rb
+++ b/spec/features/projects/commit/builds_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'project commit pipelines', :js do
+RSpec.describe 'project commit pipelines', :js, feature_category: :continuous_integration do
let(:project) { create(:project, :repository) }
before do
diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb
index fce9fa4fb62..dc8b84283a1 100644
--- a/spec/features/projects/commit/cherry_pick_spec.rb
+++ b/spec/features/projects/commit/cherry_pick_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Cherry-pick Commits', :js do
+RSpec.describe 'Cherry-pick Commits', :js, feature_category: :source_code_management do
let_it_be(:user) { create(:user) }
let_it_be(:sha) { '7d3b0f7cff5f37573aea97cebfd5692ea1689924' }
diff --git a/spec/features/projects/commit/comments/user_adds_comment_spec.rb b/spec/features/projects/commit/comments/user_adds_comment_spec.rb
index a470215186b..c53ac27bb5f 100644
--- a/spec/features/projects/commit/comments/user_adds_comment_spec.rb
+++ b/spec/features/projects/commit/comments/user_adds_comment_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User adds a comment on a commit", :js do
+RSpec.describe "User adds a comment on a commit", :js, feature_category: :source_code_management do
include Spec::Support::Helpers::Features::NotesHelpers
include RepoHelpers
diff --git a/spec/features/projects/commit/comments/user_deletes_comments_spec.rb b/spec/features/projects/commit/comments/user_deletes_comments_spec.rb
index 9059f9e4857..a1e7ddb4d6e 100644
--- a/spec/features/projects/commit/comments/user_deletes_comments_spec.rb
+++ b/spec/features/projects/commit/comments/user_deletes_comments_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User deletes comments on a commit", :js do
+RSpec.describe "User deletes comments on a commit", :js, feature_category: :source_code_management do
include Spec::Support::Helpers::Features::NotesHelpers
include Spec::Support::Helpers::ModalHelpers
include RepoHelpers
diff --git a/spec/features/projects/commit/comments/user_edits_comments_spec.rb b/spec/features/projects/commit/comments/user_edits_comments_spec.rb
index 8ac15c9cb7f..9019a981a18 100644
--- a/spec/features/projects/commit/comments/user_edits_comments_spec.rb
+++ b/spec/features/projects/commit/comments/user_edits_comments_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User edits a comment on a commit", :js do
+RSpec.describe "User edits a comment on a commit", :js, feature_category: :source_code_management do
include Spec::Support::Helpers::Features::NotesHelpers
include RepoHelpers
diff --git a/spec/features/projects/commit/diff_notes_spec.rb b/spec/features/projects/commit/diff_notes_spec.rb
index 6cebff1cc9a..f29e0803f61 100644
--- a/spec/features/projects/commit/diff_notes_spec.rb
+++ b/spec/features/projects/commit/diff_notes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Commit diff', :js do
+RSpec.describe 'Commit diff', :js, feature_category: :source_code_management do
include RepoHelpers
let(:user) { create(:user) }
diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
index 417e14e2376..3611efd1477 100644
--- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb
+++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Mini Pipeline Graph in Commit View', :js do
+RSpec.describe 'Mini Pipeline Graph in Commit View', :js, feature_category: :source_code_management do
let(:project) { create(:project, :public, :repository) }
context 'when commit has pipelines' do
diff --git a/spec/features/projects/commit/user_comments_on_commit_spec.rb b/spec/features/projects/commit/user_comments_on_commit_spec.rb
index a7f23f093a3..66a407b5ff6 100644
--- a/spec/features/projects/commit/user_comments_on_commit_spec.rb
+++ b/spec/features/projects/commit/user_comments_on_commit_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User comments on commit", :js do
+RSpec.describe "User comments on commit", :js, feature_category: :source_code_management do
include Spec::Support::Helpers::Features::NotesHelpers
include Spec::Support::Helpers::ModalHelpers
include RepoHelpers
diff --git a/spec/features/projects/commit/user_reverts_commit_spec.rb b/spec/features/projects/commit/user_reverts_commit_spec.rb
index 1c6cf5eb258..8c7b8e6ba32 100644
--- a/spec/features/projects/commit/user_reverts_commit_spec.rb
+++ b/spec/features/projects/commit/user_reverts_commit_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User reverts a commit', :js do
+RSpec.describe 'User reverts a commit', :js, feature_category: :source_code_management do
include RepoHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/projects/commit/user_views_user_status_on_commit_spec.rb b/spec/features/projects/commit/user_views_user_status_on_commit_spec.rb
index cc3c70e66ce..5670ed17eba 100644
--- a/spec/features/projects/commit/user_views_user_status_on_commit_spec.rb
+++ b/spec/features/projects/commit/user_views_user_status_on_commit_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project > Commit > View user status' do
+RSpec.describe 'Project > Commit > View user status', feature_category: :source_code_management do
include RepoHelpers
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/features/projects/commits/multi_view_diff_spec.rb b/spec/features/projects/commits/multi_view_diff_spec.rb
index c0e48b7b86c..b178a1c2171 100644
--- a/spec/features/projects/commits/multi_view_diff_spec.rb
+++ b/spec/features/projects/commits/multi_view_diff_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.shared_examples "no multiple viewers" do |commit_ref|
+RSpec.shared_examples "no multiple viewers", feature_category: :source_code_management do |commit_ref|
let(:ref) { commit_ref }
it "does not display multiple diff viewers" do
diff --git a/spec/features/projects/commits/rss_spec.rb b/spec/features/projects/commits/rss_spec.rb
index b521bb865ae..49da0727fbd 100644
--- a/spec/features/projects/commits/rss_spec.rb
+++ b/spec/features/projects/commits/rss_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project Commits RSS' do
+RSpec.describe 'Project Commits RSS', feature_category: :source_code_management do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
let(:path) { project_commits_path(project, :master) }
diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb
index 2719316c5dc..791f626b8d9 100644
--- a/spec/features/projects/commits/user_browses_commits_spec.rb
+++ b/spec/features/projects/commits/user_browses_commits_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User browses commits' do
+RSpec.describe 'User browses commits', feature_category: :source_code_management do
include RepoHelpers
let(:user) { create(:user) }
@@ -208,6 +208,10 @@ RSpec.describe 'User browses commits' do
expect(page).not_to have_link 'Create merge request'
end
+ it 'shows ref switcher with correct text', :js do
+ expect(find('.ref-selector')).to have_text('master')
+ end
+
context 'when click the compare tab' do
before do
wait_for_requests
@@ -220,9 +224,18 @@ RSpec.describe 'User browses commits' do
end
end
- context 'feature branch' do
+ context 'feature branch', :js do
let(:visit_commits_page) do
- visit project_commits_path(project, 'feature')
+ visit project_commits_path(project)
+
+ find('.ref-selector').click
+ wait_for_requests
+
+ page.within('.ref-selector') do
+ fill_in 'Search by Git revision', with: 'feature'
+ wait_for_requests
+ find('li', text: 'feature', match: :prefer_exact).click
+ end
end
context 'when project does not have open merge requests' do
@@ -230,6 +243,10 @@ RSpec.describe 'User browses commits' do
visit_commits_page
end
+ it 'shows ref switcher with correct text' do
+ expect(find('.ref-selector')).to have_text('feature')
+ end
+
it 'renders project commits' do
commit = project.repository.commit('0b4bc9a')
diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb
index 22b0f344606..8284299443f 100644
--- a/spec/features/projects/compare_spec.rb
+++ b/spec/features/projects/compare_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "Compare", :js do
+RSpec.describe "Compare", :js, feature_category: :projects do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
@@ -17,10 +17,10 @@ RSpec.describe "Compare", :js do
visit project_compare_index_path(project, from: 'master', to: 'master')
select_using_dropdown 'from', 'feature'
- expect(find('.js-compare-from-dropdown .gl-new-dropdown-button-text')).to have_content('feature')
+ expect(find('.js-compare-from-dropdown .gl-dropdown-button-text')).to have_content('feature')
select_using_dropdown 'to', 'binary-encoding'
- expect(find('.js-compare-to-dropdown .gl-new-dropdown-button-text')).to have_content('binary-encoding')
+ expect(find('.js-compare-to-dropdown .gl-dropdown-button-text')).to have_content('binary-encoding')
click_button 'Compare'
@@ -32,8 +32,8 @@ RSpec.describe "Compare", :js do
it "pre-populates fields" do
visit project_compare_index_path(project, from: "master", to: "master")
- expect(find(".js-compare-from-dropdown .gl-new-dropdown-button-text")).to have_content("master")
- expect(find(".js-compare-to-dropdown .gl-new-dropdown-button-text")).to have_content("master")
+ expect(find(".js-compare-from-dropdown .gl-dropdown-button-text")).to have_content("master")
+ expect(find(".js-compare-to-dropdown .gl-dropdown-button-text")).to have_content("master")
end
it_behaves_like 'compares branches'
@@ -99,7 +99,7 @@ RSpec.describe "Compare", :js do
find(".js-compare-from-dropdown .compare-dropdown-toggle").click
- expect(find(".js-compare-from-dropdown .gl-new-dropdown-contents")).to have_selector('li.gl-new-dropdown-item', count: 1)
+ expect(find(".js-compare-from-dropdown .gl-dropdown-contents")).to have_selector('li.gl-dropdown-item', count: 1)
end
context 'when commit has overflow', :js do
@@ -153,10 +153,10 @@ RSpec.describe "Compare", :js do
visit project_compare_index_path(project, from: "master", to: "master")
select_using_dropdown "from", "v1.0.0"
- expect(find(".js-compare-from-dropdown .gl-new-dropdown-button-text")).to have_content("v1.0.0")
+ expect(find(".js-compare-from-dropdown .gl-dropdown-button-text")).to have_content("v1.0.0")
select_using_dropdown "to", "v1.1.0"
- expect(find(".js-compare-to-dropdown .gl-new-dropdown-button-text")).to have_content("v1.1.0")
+ expect(find(".js-compare-to-dropdown .gl-dropdown-button-text")).to have_content("v1.1.0")
click_button "Compare"
expect(page).to have_content "Commits"
diff --git a/spec/features/projects/confluence/user_views_confluence_page_spec.rb b/spec/features/projects/confluence/user_views_confluence_page_spec.rb
index 49e7839f16c..c1ce6ea4536 100644
--- a/spec/features/projects/confluence/user_views_confluence_page_spec.rb
+++ b/spec/features/projects/confluence/user_views_confluence_page_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views the Confluence page' do
+RSpec.describe 'User views the Confluence page', feature_category: :integrations do
let_it_be(:user) { create(:user) }
let(:project) { create(:project, :public) }
diff --git a/spec/features/projects/container_registry_spec.rb b/spec/features/projects/container_registry_spec.rb
index e99af734c43..98cf024afa8 100644
--- a/spec/features/projects/container_registry_spec.rb
+++ b/spec/features/projects/container_registry_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Container Registry', :js do
+RSpec.describe 'Container Registry', :js, feature_category: :projects do
include_context 'container registry tags'
let(:user) { create(:user) }
@@ -104,8 +104,6 @@ RSpec.describe 'Container Registry', :js do
find('.modal .modal-footer .btn-danger').click
end
- it_behaves_like 'rejecting tags destruction for an importing repository on', tags: ['1']
-
it('pagination navigate to the second page') do
visit_next_page
diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb
index 06462263f5a..bd48fb68304 100644
--- a/spec/features/projects/deploy_keys_spec.rb
+++ b/spec/features/projects/deploy_keys_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project deploy keys', :js do
+RSpec.describe 'Project deploy keys', :js, feature_category: :projects do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project_empty_repo) }
let_it_be(:deploy_keys_project) { create(:deploy_keys_project, project: project) }
diff --git a/spec/features/projects/diffs/diff_show_spec.rb b/spec/features/projects/diffs/diff_show_spec.rb
index dcd6f1239bb..973c61de31d 100644
--- a/spec/features/projects/diffs/diff_show_spec.rb
+++ b/spec/features/projects/diffs/diff_show_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Diff file viewer', :js, :with_clean_rails_cache do
+RSpec.describe 'Diff file viewer', :js, :with_clean_rails_cache, feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
def visit_commit(sha, anchor: nil)
diff --git a/spec/features/projects/environments/environment_metrics_spec.rb b/spec/features/projects/environments/environment_metrics_spec.rb
index d486d8cf551..4a112445ab9 100644
--- a/spec/features/projects/environments/environment_metrics_spec.rb
+++ b/spec/features/projects/environments/environment_metrics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Environment > Metrics' do
+RSpec.describe 'Environment > Metrics', feature_category: :projects do
include PrometheusHelpers
let(:user) { create(:user) }
diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb
index 706c880d097..75913082803 100644
--- a/spec/features/projects/environments/environment_spec.rb
+++ b/spec/features/projects/environments/environment_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Environment' do
+RSpec.describe 'Environment', feature_category: :projects do
let_it_be(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:role) { :developer }
@@ -10,12 +10,83 @@ RSpec.describe 'Environment' do
before do
sign_in(user)
project.add_role(user, role)
+ stub_feature_flags(environment_details_vue: false)
end
def auto_stop_button_selector
%q{button[title="Prevent environment from auto-stopping"]}
end
+ describe 'environment details page vue' do
+ let_it_be(:environment) { create(:environment, project: project) }
+ let!(:permissions) {}
+ let!(:deployment) {}
+ let!(:action) {}
+ let!(:cluster) {}
+
+ before do
+ stub_feature_flags(environment_details_vue: true)
+ end
+
+ context 'with auto-stop' do
+ let_it_be(:environment) { create(:environment, :will_auto_stop, name: 'staging', project: project) }
+
+ before do
+ visit_environment(environment)
+ end
+
+ it 'shows auto stop info', :js do
+ expect(page).to have_content('Auto stops')
+ end
+
+ it 'shows auto stop button', :js do
+ expect(page).to have_selector(auto_stop_button_selector)
+ expect(page.find(auto_stop_button_selector).find(:xpath, '..')['action']).to have_content(cancel_auto_stop_project_environment_path(environment.project, environment))
+ end
+
+ it 'allows user to cancel auto stop', :js do
+ page.find(auto_stop_button_selector).click
+ wait_for_all_requests
+ expect(page).to have_content('Auto stop successfully canceled.')
+ expect(page).not_to have_selector(auto_stop_button_selector)
+ end
+ end
+
+ context 'with deployments' do
+ before do
+ visit_environment(environment)
+ end
+
+ context 'when there is a successful deployment' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, :success, pipeline: pipeline) }
+
+ let(:deployment) do
+ create(:deployment, :success, environment: environment, deployable: build)
+ end
+
+ it 'does show deployments', :js do
+ wait_for_requests
+ expect(page).to have_link("#{build.name} (##{build.id})")
+ end
+ end
+
+ context 'when there is a failed deployment' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+
+ let(:deployment) do
+ create(:deployment, :failed, environment: environment, deployable: build)
+ end
+
+ it 'does show deployments', :js do
+ wait_for_requests
+ expect(page).to have_link("#{build.name} (##{build.id})")
+ end
+ end
+ end
+ end
+
describe 'environment details page' do
let_it_be(:environment) { create(:environment, project: project) }
let!(:permissions) {}
diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb
index b445b0da901..788bf6477b1 100644
--- a/spec/features/projects/environments/environments_spec.rb
+++ b/spec/features/projects/environments/environments_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Environments page', :js do
+RSpec.describe 'Environments page', :js, feature_category: :projects do
include Spec::Support::Helpers::ModalHelpers
let(:project) { create(:project) }
diff --git a/spec/features/projects/feature_flag_user_lists/user_deletes_feature_flag_user_list_spec.rb b/spec/features/projects/feature_flag_user_lists/user_deletes_feature_flag_user_list_spec.rb
index 37d6f299883..6383c3196c4 100644
--- a/spec/features/projects/feature_flag_user_lists/user_deletes_feature_flag_user_list_spec.rb
+++ b/spec/features/projects/feature_flag_user_lists/user_deletes_feature_flag_user_list_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User deletes feature flag user list', :js do
+RSpec.describe 'User deletes feature flag user list', :js, feature_category: :projects do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
diff --git a/spec/features/projects/feature_flag_user_lists/user_edits_feature_flag_user_list_spec.rb b/spec/features/projects/feature_flag_user_lists/user_edits_feature_flag_user_list_spec.rb
index b37c2780827..8ab9e9baab9 100644
--- a/spec/features/projects/feature_flag_user_lists/user_edits_feature_flag_user_list_spec.rb
+++ b/spec/features/projects/feature_flag_user_lists/user_edits_feature_flag_user_list_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User edits feature flag user list', :js do
+RSpec.describe 'User edits feature flag user list', :js, feature_category: :projects do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
diff --git a/spec/features/projects/feature_flag_user_lists/user_sees_feature_flag_user_list_details_spec.rb b/spec/features/projects/feature_flag_user_lists/user_sees_feature_flag_user_list_details_spec.rb
index dfebe6408bd..7614349c5a4 100644
--- a/spec/features/projects/feature_flag_user_lists/user_sees_feature_flag_user_list_details_spec.rb
+++ b/spec/features/projects/feature_flag_user_lists/user_sees_feature_flag_user_list_details_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User sees feature flag user list details', :js do
+RSpec.describe 'User sees feature flag user list details', :js, feature_category: :projects do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
diff --git a/spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb b/spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb
index 43540dc4522..852d7bca96a 100644
--- a/spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb
+++ b/spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User deletes feature flag', :js do
+RSpec.describe 'User deletes feature flag', :js, feature_category: :feature_flags do
include FeatureFlagHelpers
let(:user) { create(:user) }
diff --git a/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb b/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb
index 949e530f86d..e2448887531 100644
--- a/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb
+++ b/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User sees feature flag list', :js do
+RSpec.describe 'User sees feature flag list', :js, feature_category: :feature_flags do
include FeatureFlagHelpers
let_it_be(:user) { create(:user) }
@@ -42,7 +42,7 @@ RSpec.describe 'User sees feature flag list', :js do
expect_status_toggle_button_not_to_be_checked
within_feature_flag_scopes do
- expect(page.find('[data-testid="strategy-badge"]')).to have_content('All Users: All Environments, review/*')
+ expect(page.find('[data-testid="strategy-label"]')).to have_content('All Users: All Environments, review/*')
end
end
end
@@ -66,7 +66,7 @@ RSpec.describe 'User sees feature flag list', :js do
expect_status_toggle_button_to_be_checked
within_feature_flag_scopes do
- expect(page.find('[data-testid="strategy-badge"]')).to have_content('All Users: production')
+ expect(page.find('[data-testid="strategy-label"]')).to have_content('All Users: production')
end
end
end
diff --git a/spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb b/spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb
index eb9ac078662..ce99ae92d63 100644
--- a/spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb
+++ b/spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User updates feature flag', :js do
+RSpec.describe 'User updates feature flag', :js, feature_category: :feature_flags do
include FeatureFlagHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index 649c21d4459..5e0998412ed 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Edit Project Settings' do
+RSpec.describe 'Edit Project Settings', feature_category: :projects do
let(:member) { create(:user) }
let!(:project) { create(:project, :public, :repository) }
let!(:issue) { create(:issue, project: project) }
diff --git a/spec/features/projects/files/dockerfile_dropdown_spec.rb b/spec/features/projects/files/dockerfile_dropdown_spec.rb
index dd1635c900e..1e05bdae204 100644
--- a/spec/features/projects/files/dockerfile_dropdown_spec.rb
+++ b/spec/features/projects/files/dockerfile_dropdown_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User wants to add a Dockerfile file', :js do
+RSpec.describe 'Projects > Files > User wants to add a Dockerfile file', :js, feature_category: :projects do
include Spec::Support::Helpers::Features::SourceEditorSpecHelpers
before do
diff --git a/spec/features/projects/files/download_buttons_spec.rb b/spec/features/projects/files/download_buttons_spec.rb
index a486d7517ac..2710e2efa94 100644
--- a/spec/features/projects/files/download_buttons_spec.rb
+++ b/spec/features/projects/files/download_buttons_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > Download buttons in files tree' do
+RSpec.describe 'Projects > Files > Download buttons in files tree', feature_category: :projects do
let(:project) { create(:project, :repository) }
let(:user) { project.creator }
diff --git a/spec/features/projects/files/edit_file_soft_wrap_spec.rb b/spec/features/projects/files/edit_file_soft_wrap_spec.rb
index e08c53a67dd..f6342257847 100644
--- a/spec/features/projects/files/edit_file_soft_wrap_spec.rb
+++ b/spec/features/projects/files/edit_file_soft_wrap_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User uses soft wrap while editing file', :js do
+RSpec.describe 'Projects > Files > User uses soft wrap while editing file', :js, feature_category: :projects do
before do
project = create(:project, :repository)
user = project.first_owner
diff --git a/spec/features/projects/files/editing_a_file_spec.rb b/spec/features/projects/files/editing_a_file_spec.rb
index e256bec2a1c..04f45de42cc 100644
--- a/spec/features/projects/files/editing_a_file_spec.rb
+++ b/spec/features/projects/files/editing_a_file_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User wants to edit a file' do
+RSpec.describe 'Projects > Files > User wants to edit a file', feature_category: :projects do
let(:project) { create(:project, :repository) }
let(:user) { project.first_owner }
let(:commit_params) do
diff --git a/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb b/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb
index a283f7d128c..d791e22e4f8 100644
--- a/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb
+++ b/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User views files page' do
+RSpec.describe 'Projects > Files > User views files page', feature_category: :projects do
let(:project) { create(:forked_project_with_submodules) }
let(:user) { project.first_owner }
diff --git a/spec/features/projects/files/find_file_keyboard_spec.rb b/spec/features/projects/files/find_file_keyboard_spec.rb
index 9ae3be4993b..19813396435 100644
--- a/spec/features/projects/files/find_file_keyboard_spec.rb
+++ b/spec/features/projects/files/find_file_keyboard_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > Find file keyboard shortcuts', :js do
+RSpec.describe 'Projects > Files > Find file keyboard shortcuts', :js, feature_category: :projects do
let(:project) { create(:project, :repository) }
let(:user) { project.first_owner }
diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb
index a86adf951d8..5e11a94e65b 100644
--- a/spec/features/projects/files/gitignore_dropdown_spec.rb
+++ b/spec/features/projects/files/gitignore_dropdown_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User wants to add a .gitignore file', :js do
+RSpec.describe 'Projects > Files > User wants to add a .gitignore file', :js, feature_category: :projects do
include Spec::Support::Helpers::Features::SourceEditorSpecHelpers
before do
diff --git a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
index 46ac0dee7eb..67678a937e5 100644
--- a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
+++ b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file', :js do
+RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file', :js, feature_category: :projects do
include Spec::Support::Helpers::Features::SourceEditorSpecHelpers
let(:params) { {} }
diff --git a/spec/features/projects/files/project_owner_creates_license_file_spec.rb b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
index c9ba8cbd2bb..7ac9cb33060 100644
--- a/spec/features/projects/files/project_owner_creates_license_file_spec.rb
+++ b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > Project owner creates a license file', :js do
+RSpec.describe 'Projects > Files > Project owner creates a license file', :js, feature_category: :projects do
let(:project) { create(:project, :repository) }
let(:project_maintainer) { project.first_owner }
diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
index 52686469243..8d64151e680 100644
--- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > Project owner sees a link to create a license file in empty project', :js do
+RSpec.describe 'Projects > Files > Project owner sees a link to create a license file in empty project', :js,
+feature_category: :projects do
include WebIdeSpecHelpers
let(:project) { create(:project_empty_repo) }
diff --git a/spec/features/projects/files/template_selector_menu_spec.rb b/spec/features/projects/files/template_selector_menu_spec.rb
index 51ae6616d4a..8dbfa3afb0b 100644
--- a/spec/features/projects/files/template_selector_menu_spec.rb
+++ b/spec/features/projects/files/template_selector_menu_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Template selector menu', :js do
+RSpec.describe 'Template selector menu', :js, feature_category: :projects do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
diff --git a/spec/features/projects/files/template_type_dropdown_spec.rb b/spec/features/projects/files/template_type_dropdown_spec.rb
index 9cdb5eeb076..990b118d172 100644
--- a/spec/features/projects/files/template_type_dropdown_spec.rb
+++ b/spec/features/projects/files/template_type_dropdown_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > Template type dropdown selector', :js do
+RSpec.describe 'Projects > Files > Template type dropdown selector', :js, feature_category: :projects do
let(:project) { create(:project, :repository) }
let(:user) { project.first_owner }
diff --git a/spec/features/projects/files/undo_template_spec.rb b/spec/features/projects/files/undo_template_spec.rb
index 0b2daf12063..afc9a5fd232 100644
--- a/spec/features/projects/files/undo_template_spec.rb
+++ b/spec/features/projects/files/undo_template_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > Template Undo Button', :js do
+RSpec.describe 'Projects > Files > Template Undo Button', :js, feature_category: :projects do
let(:project) { create(:project, :repository) }
let(:user) { project.first_owner }
diff --git a/spec/features/projects/files/user_browses_a_tree_with_a_folder_containing_only_a_folder_spec.rb b/spec/features/projects/files/user_browses_a_tree_with_a_folder_containing_only_a_folder_spec.rb
index 220572c6a6d..8b60d21a77e 100644
--- a/spec/features/projects/files/user_browses_a_tree_with_a_folder_containing_only_a_folder_spec.rb
+++ b/spec/features/projects/files/user_browses_a_tree_with_a_folder_containing_only_a_folder_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
# This is a regression test for https://gitlab.com/gitlab-org/gitlab-foss/issues/37569
-RSpec.describe 'Projects > Files > User browses a tree with a folder containing only a folder', :js do
+RSpec.describe 'Projects > Files > User browses a tree with a folder containing only a folder', :js,
+feature_category: :projects do
let(:project) { create(:project, :empty_repo) }
let(:user) { project.first_owner }
diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb
index 0f3ce5a2bad..125f7209ab4 100644
--- a/spec/features/projects/files/user_browses_files_spec.rb
+++ b/spec/features/projects/files/user_browses_files_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User browses files", :js do
+RSpec.describe "User browses files", :js, feature_category: :projects do
include RepoHelpers
let(:fork_message) do
@@ -86,6 +86,15 @@ RSpec.describe "User browses files", :js do
visit(project_tree_path(project, "markdown"))
end
+ it "redirects to the permalink URL" do
+ click_link(".gitignore")
+ click_link("Permalink")
+
+ permalink_path = project_blob_path(project, "#{project.repository.commit('markdown').sha}/.gitignore")
+
+ expect(page).to have_current_path(permalink_path, ignore_query: true)
+ end
+
it "shows correct files and links" do
expect(page).to have_current_path(project_tree_path(project, "markdown"), ignore_query: true)
expect(page).to have_content("README.md")
@@ -262,6 +271,8 @@ RSpec.describe "User browses files", :js do
context "when browsing a specific ref", :js do
let(:ref) { project_tree_path(project, "6d39438") }
+ ref_selector = '.ref-selector'
+
before do
visit(ref)
end
@@ -272,24 +283,34 @@ RSpec.describe "User browses files", :js do
end
it "shows files from a repository with apostroph in its name" do
- first(".js-project-refs-dropdown").click
+ ref_name = 'test'
- page.within(".project-refs-form") do
- click_link("'test'")
+ find(ref_selector).click
+ wait_for_requests
+
+ page.within(ref_selector) do
+ fill_in 'Search by Git revision', with: ref_name
+ wait_for_requests
+ find('li', text: ref_name, match: :prefer_exact).click
end
- expect(page).to have_selector(".dropdown-toggle-text", text: "'test'")
+ expect(find(ref_selector)).to have_text(ref_name)
- visit(project_tree_path(project, "'test'"))
+ visit(project_tree_path(project, ref_name))
expect(page).not_to have_selector(".tree-commit .animation-container")
end
it "shows the code with a leading dot in the directory" do
- first(".js-project-refs-dropdown").click
+ ref_name = 'fix'
+
+ find(ref_selector).click
+ wait_for_requests
- page.within(".project-refs-form") do
- click_link("fix")
+ page.within(ref_selector) do
+ fill_in 'Search by Git revision', with: ref_name
+ wait_for_requests
+ find('li', text: ref_name, match: :prefer_exact).click
end
visit(project_tree_path(project, "fix/.testdir"))
diff --git a/spec/features/projects/files/user_browses_lfs_files_spec.rb b/spec/features/projects/files/user_browses_lfs_files_spec.rb
index 56e18871810..6b401d6d789 100644
--- a/spec/features/projects/files/user_browses_lfs_files_spec.rb
+++ b/spec/features/projects/files/user_browses_lfs_files_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User browses LFS files' do
+RSpec.describe 'Projects > Files > User browses LFS files', feature_category: :projects do
let(:project) { create(:project, :repository) }
let(:user) { project.first_owner }
diff --git a/spec/features/projects/files/user_creates_directory_spec.rb b/spec/features/projects/files/user_creates_directory_spec.rb
index 9e0168d7ef3..4dd579ba8e9 100644
--- a/spec/features/projects/files/user_creates_directory_spec.rb
+++ b/spec/features/projects/files/user_creates_directory_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User creates a directory', :js do
+RSpec.describe 'Projects > Files > User creates a directory', :js, feature_category: :projects do
let(:fork_message) do
"You're not allowed to make changes to this project directly. "\
"A fork of this project has been created that you can make changes in, so you can submit a merge request."
diff --git a/spec/features/projects/files/user_creates_files_spec.rb b/spec/features/projects/files/user_creates_files_spec.rb
index a81f31d663e..97ccb45dfc6 100644
--- a/spec/features/projects/files/user_creates_files_spec.rb
+++ b/spec/features/projects/files/user_creates_files_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User creates files', :js do
+RSpec.describe 'Projects > Files > User creates files', :js, feature_category: :projects do
include Spec::Support::Helpers::Features::SourceEditorSpecHelpers
include BlobSpecHelpers
diff --git a/spec/features/projects/files/user_deletes_files_spec.rb b/spec/features/projects/files/user_deletes_files_spec.rb
index 806f1e8e9ed..61152a8badc 100644
--- a/spec/features/projects/files/user_deletes_files_spec.rb
+++ b/spec/features/projects/files/user_deletes_files_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User deletes files', :js do
+RSpec.describe 'Projects > Files > User deletes files', :js, feature_category: :projects do
let(:fork_message) do
"You're not allowed to make changes to this project directly. "\
"A fork of this project has been created that you can make changes in, so you can submit a merge request."
diff --git a/spec/features/projects/files/user_edits_files_spec.rb b/spec/features/projects/files/user_edits_files_spec.rb
index 1a9c5483218..5a61aa146a2 100644
--- a/spec/features/projects/files/user_edits_files_spec.rb
+++ b/spec/features/projects/files/user_edits_files_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User edits files', :js do
+RSpec.describe 'Projects > Files > User edits files', :js, feature_category: :projects do
include Spec::Support::Helpers::Features::SourceEditorSpecHelpers
include ProjectForksHelper
include BlobSpecHelpers
diff --git a/spec/features/projects/files/user_find_file_spec.rb b/spec/features/projects/files/user_find_file_spec.rb
index 69ea8b0eb5f..1b53189da83 100644
--- a/spec/features/projects/files/user_find_file_spec.rb
+++ b/spec/features/projects/files/user_find_file_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User find project file' do
+RSpec.describe 'User find project file', feature_category: :projects do
let(:user) { create :user }
let(:project) { create :project, :repository }
diff --git a/spec/features/projects/files/user_reads_pipeline_status_spec.rb b/spec/features/projects/files/user_reads_pipeline_status_spec.rb
index 294a03813cd..18a5fb71b10 100644
--- a/spec/features/projects/files/user_reads_pipeline_status_spec.rb
+++ b/spec/features/projects/files/user_reads_pipeline_status_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'user reads pipeline status', :js do
+RSpec.describe 'user reads pipeline status', :js, feature_category: :projects do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:v110_pipeline) { create_pipeline('v1.1.0', 'success') }
diff --git a/spec/features/projects/files/user_replaces_files_spec.rb b/spec/features/projects/files/user_replaces_files_spec.rb
index 1ecd50b6463..9fa3ddf92c6 100644
--- a/spec/features/projects/files/user_replaces_files_spec.rb
+++ b/spec/features/projects/files/user_replaces_files_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User replaces files', :js do
+RSpec.describe 'Projects > Files > User replaces files', :js, feature_category: :projects do
include DropzoneHelper
let(:fork_message) do
diff --git a/spec/features/projects/files/user_searches_for_files_spec.rb b/spec/features/projects/files/user_searches_for_files_spec.rb
index cce73d06f94..b438b203141 100644
--- a/spec/features/projects/files/user_searches_for_files_spec.rb
+++ b/spec/features/projects/files/user_searches_for_files_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User searches for files' do
+RSpec.describe 'Projects > Files > User searches for files', feature_category: :projects do
let(:user) { project.first_owner }
before do
diff --git a/spec/features/projects/files/user_uploads_files_spec.rb b/spec/features/projects/files/user_uploads_files_spec.rb
index cc621dfd9f8..575a6290a32 100644
--- a/spec/features/projects/files/user_uploads_files_spec.rb
+++ b/spec/features/projects/files/user_uploads_files_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User uploads files' do
+RSpec.describe 'Projects > Files > User uploads files', feature_category: :projects do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, name: 'Shop', creator: user) }
let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') }
diff --git a/spec/features/projects/fork_spec.rb b/spec/features/projects/fork_spec.rb
index 9ceadb63178..3867f7fd086 100644
--- a/spec/features/projects/fork_spec.rb
+++ b/spec/features/projects/fork_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project fork' do
+RSpec.describe 'Project fork', feature_category: :projects do
include ProjectForksHelper
let(:user) { create(:user) }
diff --git a/spec/features/projects/forks/fork_list_spec.rb b/spec/features/projects/forks/fork_list_spec.rb
index b48c46ef8cb..18424c18cbc 100644
--- a/spec/features/projects/forks/fork_list_spec.rb
+++ b/spec/features/projects/forks/fork_list_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'listing forks of a project' do
+RSpec.describe 'listing forks of a project', feature_category: :projects do
include ProjectForksHelper
include ExternalAuthorizationServiceHelpers
diff --git a/spec/features/projects/gfm_autocomplete_load_spec.rb b/spec/features/projects/gfm_autocomplete_load_spec.rb
index a7d68b07dd3..bb9f4e121d8 100644
--- a/spec/features/projects/gfm_autocomplete_load_spec.rb
+++ b/spec/features/projects/gfm_autocomplete_load_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'GFM autocomplete loading', :js do
+RSpec.describe 'GFM autocomplete loading', :js, feature_category: :projects do
let(:project) { create(:project) }
before do
diff --git a/spec/features/projects/graph_spec.rb b/spec/features/projects/graph_spec.rb
index 0b628ad1e9a..f96356b11c9 100644
--- a/spec/features/projects/graph_spec.rb
+++ b/spec/features/projects/graph_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project Graph', :js do
+RSpec.describe 'Project Graph', :js, feature_category: :projects do
let(:user) { create :user }
let(:project) { create(:project, :repository, namespace: user.namespace) }
let(:branch_name) { 'master' }
diff --git a/spec/features/projects/hook_logs/user_reads_log_spec.rb b/spec/features/projects/hook_logs/user_reads_log_spec.rb
index 9b7ec14c36f..92ddc559cf4 100644
--- a/spec/features/projects/hook_logs/user_reads_log_spec.rb
+++ b/spec/features/projects/hook_logs/user_reads_log_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Hook logs' do
+RSpec.describe 'Hook logs', feature_category: :projects do
let(:project) { create(:project) }
let(:project_hook) { create(:project_hook, project: project) }
let(:web_hook_log) { create(:web_hook_log, web_hook: project_hook, response_body: 'Hello World') }
diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb
index ccf3ccc6a96..8986ce91ae3 100644
--- a/spec/features/projects/import_export/export_file_spec.rb
+++ b/spec/features/projects/import_export/export_file_spec.rb
@@ -6,7 +6,7 @@ require 'spec_helper'
# It looks up for any sensitive word inside the JSON, so if a sensitive word is found
# we'll have to either include it adding the model that includes it to the +safe_list+
# or make sure the attribute is blacklisted in the +import_export.yml+ configuration
-RSpec.describe 'Import/Export - project export integration test', :js do
+RSpec.describe 'Import/Export - project export integration test', :js, feature_category: :importers do
include Select2Helper
include ExportFileHelper
@@ -53,7 +53,7 @@ RSpec.describe 'Import/Export - project export integration test', :js do
project_json_path = File.join(tmpdir, 'project.json')
expect(File).to exist(project_json_path)
- project_hash = Gitlab::Json.parse(IO.read(project_json_path))
+ project_hash = Gitlab::Json.parse(File.read(project_json_path))
sensitive_words.each do |sensitive_word|
found = find_sensitive_attributes(sensitive_word, project_hash)
@@ -79,7 +79,7 @@ RSpec.describe 'Import/Export - project export integration test', :js do
expect(File).to exist(project_json_path)
relations = []
- relations << Gitlab::Json.parse(IO.read(project_json_path))
+ relations << Gitlab::Json.parse(File.read(project_json_path))
Dir.glob(File.join(tmpdir, 'tree/project', '*.ndjson')) do |rb_filename|
File.foreach(rb_filename) do |line|
relations << Gitlab::Json.parse(line)
diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb
index 6f015f9cd22..8fb11f06cdd 100644
--- a/spec/features/projects/import_export/import_file_spec.rb
+++ b/spec/features/projects/import_export/import_file_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Import/Export - project import integration test', :js do
+RSpec.describe 'Import/Export - project import integration test', :js, feature_category: :importers do
let(:user) { create(:user) }
let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') }
let(:export_path) { "#{Dir.tmpdir}/import_file_spec" }
diff --git a/spec/features/projects/infrastructure_registry_spec.rb b/spec/features/projects/infrastructure_registry_spec.rb
index aab1cec8762..e1619726c8d 100644
--- a/spec/features/projects/infrastructure_registry_spec.rb
+++ b/spec/features/projects/infrastructure_registry_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Infrastructure Registry' do
+RSpec.describe 'Infrastructure Registry', feature_category: :projects do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
diff --git a/spec/features/projects/integrations/disable_triggers_spec.rb b/spec/features/projects/integrations/disable_triggers_spec.rb
index b039d610ecb..f7afce6d87c 100644
--- a/spec/features/projects/integrations/disable_triggers_spec.rb
+++ b/spec/features/projects/integrations/disable_triggers_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Disable individual triggers', :js do
+RSpec.describe 'Disable individual triggers', :js, feature_category: :integrations do
include_context 'project integration activation'
let(:checkbox_selector) { 'input[name$="_events]"]' }
diff --git a/spec/features/projects/integrations/project_integrations_spec.rb b/spec/features/projects/integrations/project_integrations_spec.rb
index 708a5bca8c1..d99b6ca9092 100644
--- a/spec/features/projects/integrations/project_integrations_spec.rb
+++ b/spec/features/projects/integrations/project_integrations_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project integrations', :js do
+RSpec.describe 'Project integrations', :js, feature_category: :integrations do
include_context 'project integration activation'
it_behaves_like 'integration settings form' do
diff --git a/spec/features/projects/integrations/user_activates_asana_spec.rb b/spec/features/projects/integrations/user_activates_asana_spec.rb
index 9ec9f00529a..b99ca2ebc1c 100644
--- a/spec/features/projects/integrations/user_activates_asana_spec.rb
+++ b/spec/features/projects/integrations/user_activates_asana_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Asana' do
+RSpec.describe 'User activates Asana', feature_category: :integrations do
include_context 'project integration activation'
it 'activates integration', :js do
diff --git a/spec/features/projects/integrations/user_activates_assembla_spec.rb b/spec/features/projects/integrations/user_activates_assembla_spec.rb
index be9034ec5ba..db5774e4514 100644
--- a/spec/features/projects/integrations/user_activates_assembla_spec.rb
+++ b/spec/features/projects/integrations/user_activates_assembla_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Assembla' do
+RSpec.describe 'User activates Assembla', feature_category: :integrations do
include_context 'project integration activation'
before do
diff --git a/spec/features/projects/integrations/user_activates_atlassian_bamboo_ci_spec.rb b/spec/features/projects/integrations/user_activates_atlassian_bamboo_ci_spec.rb
index 49f62a34bd2..a532c1b8644 100644
--- a/spec/features/projects/integrations/user_activates_atlassian_bamboo_ci_spec.rb
+++ b/spec/features/projects/integrations/user_activates_atlassian_bamboo_ci_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Atlassian Bamboo CI' do
+RSpec.describe 'User activates Atlassian Bamboo CI', feature_category: :integrations do
include_context 'project integration activation'
before do
diff --git a/spec/features/projects/integrations/user_activates_emails_on_push_spec.rb b/spec/features/projects/integrations/user_activates_emails_on_push_spec.rb
index 168779aad07..9a2d693a9f0 100644
--- a/spec/features/projects/integrations/user_activates_emails_on_push_spec.rb
+++ b/spec/features/projects/integrations/user_activates_emails_on_push_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Emails on push' do
+RSpec.describe 'User activates Emails on push', feature_category: :integrations do
include_context 'project integration activation'
it 'activates integration', :js do
diff --git a/spec/features/projects/integrations/user_activates_flowdock_spec.rb b/spec/features/projects/integrations/user_activates_flowdock_spec.rb
deleted file mode 100644
index df1a4feddfb..00000000000
--- a/spec/features/projects/integrations/user_activates_flowdock_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'User activates Flowdock' do
- include_context 'project integration activation' do
- let(:project) { create(:project, :repository) }
- end
-
- before do
- stub_request(:post, /.*api.flowdock.com.*/)
- end
-
- it 'activates integration', :js do
- visit_project_integration('Flowdock')
- fill_in('Token', with: 'verySecret')
-
- click_test_then_save_integration(expect_test_to_fail: false)
-
- expect(page).to have_content('Flowdock settings saved and active.')
- end
-end
diff --git a/spec/features/projects/integrations/user_activates_irker_spec.rb b/spec/features/projects/integrations/user_activates_irker_spec.rb
index 23b5f2a5c47..17c46bfaff7 100644
--- a/spec/features/projects/integrations/user_activates_irker_spec.rb
+++ b/spec/features/projects/integrations/user_activates_irker_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates irker (IRC gateway)' do
+RSpec.describe 'User activates irker (IRC gateway)', feature_category: :integrations do
include_context 'project integration activation'
it 'activates integration', :js do
diff --git a/spec/features/projects/integrations/user_activates_jetbrains_teamcity_ci_spec.rb b/spec/features/projects/integrations/user_activates_jetbrains_teamcity_ci_spec.rb
index f86a1b8a0a4..a18c052beb9 100644
--- a/spec/features/projects/integrations/user_activates_jetbrains_teamcity_ci_spec.rb
+++ b/spec/features/projects/integrations/user_activates_jetbrains_teamcity_ci_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates JetBrains TeamCity CI' do
+RSpec.describe 'User activates JetBrains TeamCity CI', feature_category: :integrations do
include_context 'project integration activation'
before do
diff --git a/spec/features/projects/integrations/user_activates_jira_spec.rb b/spec/features/projects/integrations/user_activates_jira_spec.rb
index dad201ffbb6..e4b10aeb340 100644
--- a/spec/features/projects/integrations/user_activates_jira_spec.rb
+++ b/spec/features/projects/integrations/user_activates_jira_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Jira', :js do
+RSpec.describe 'User activates Jira', :js, feature_category: :integrations do
include_context 'project integration activation'
include_context 'project integration Jira context'
diff --git a/spec/features/projects/integrations/user_activates_mattermost_slash_command_spec.rb b/spec/features/projects/integrations/user_activates_mattermost_slash_command_spec.rb
index 54c9ec0f62e..16c7a3ff226 100644
--- a/spec/features/projects/integrations/user_activates_mattermost_slash_command_spec.rb
+++ b/spec/features/projects/integrations/user_activates_mattermost_slash_command_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Set up Mattermost slash commands', :js do
+RSpec.describe 'Set up Mattermost slash commands', :js, feature_category: :integrations do
describe 'user visits the mattermost slash command config page' do
include_context 'project integration activation'
diff --git a/spec/features/projects/integrations/user_activates_packagist_spec.rb b/spec/features/projects/integrations/user_activates_packagist_spec.rb
index 0892843e840..2d77abfea7c 100644
--- a/spec/features/projects/integrations/user_activates_packagist_spec.rb
+++ b/spec/features/projects/integrations/user_activates_packagist_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Packagist' do
+RSpec.describe 'User activates Packagist', feature_category: :integrations do
include_context 'project integration activation'
before do
diff --git a/spec/features/projects/integrations/user_activates_pivotaltracker_spec.rb b/spec/features/projects/integrations/user_activates_pivotaltracker_spec.rb
index fe6ed786ace..b4dec8ffdb5 100644
--- a/spec/features/projects/integrations/user_activates_pivotaltracker_spec.rb
+++ b/spec/features/projects/integrations/user_activates_pivotaltracker_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates PivotalTracker' do
+RSpec.describe 'User activates PivotalTracker', feature_category: :integrations do
include_context 'project integration activation'
before do
diff --git a/spec/features/projects/integrations/user_activates_prometheus_spec.rb b/spec/features/projects/integrations/user_activates_prometheus_spec.rb
index 56b895919b8..5b2d885410f 100644
--- a/spec/features/projects/integrations/user_activates_prometheus_spec.rb
+++ b/spec/features/projects/integrations/user_activates_prometheus_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Prometheus' do
+RSpec.describe 'User activates Prometheus', feature_category: :integrations do
include_context 'project integration activation'
before do
diff --git a/spec/features/projects/integrations/user_activates_pushover_spec.rb b/spec/features/projects/integrations/user_activates_pushover_spec.rb
index 616efdc836f..a705c354a1e 100644
--- a/spec/features/projects/integrations/user_activates_pushover_spec.rb
+++ b/spec/features/projects/integrations/user_activates_pushover_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Pushover' do
+RSpec.describe 'User activates Pushover', feature_category: :integrations do
include_context 'project integration activation'
before do
diff --git a/spec/features/projects/integrations/user_activates_slack_notifications_spec.rb b/spec/features/projects/integrations/user_activates_slack_notifications_spec.rb
index e89f6e309ea..01c202baf70 100644
--- a/spec/features/projects/integrations/user_activates_slack_notifications_spec.rb
+++ b/spec/features/projects/integrations/user_activates_slack_notifications_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Slack notifications', :js do
+RSpec.describe 'User activates Slack notifications', :js, feature_category: :integrations do
include_context 'project integration activation'
context 'when integration is not configured yet' do
diff --git a/spec/features/projects/integrations/user_activates_slack_slash_command_spec.rb b/spec/features/projects/integrations/user_activates_slack_slash_command_spec.rb
index df8cd84ffdb..0f6d721565e 100644
--- a/spec/features/projects/integrations/user_activates_slack_slash_command_spec.rb
+++ b/spec/features/projects/integrations/user_activates_slack_slash_command_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Slack slash commands', :js do
+RSpec.describe 'Slack slash commands', :js, feature_category: :integrations do
include_context 'project integration activation'
before do
diff --git a/spec/features/projects/integrations/user_uses_inherited_settings_spec.rb b/spec/features/projects/integrations/user_uses_inherited_settings_spec.rb
index 8a2881c95dc..e0063a9c733 100644
--- a/spec/features/projects/integrations/user_uses_inherited_settings_spec.rb
+++ b/spec/features/projects/integrations/user_uses_inherited_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User uses inherited settings', :js do
+RSpec.describe 'User uses inherited settings', :js, feature_category: :integrations do
include JiraIntegrationHelpers
include_context 'project integration activation'
diff --git a/spec/features/projects/integrations/user_views_services_spec.rb b/spec/features/projects/integrations/user_views_services_spec.rb
index 559461f911f..e6be300c0a9 100644
--- a/spec/features/projects/integrations/user_views_services_spec.rb
+++ b/spec/features/projects/integrations/user_views_services_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views integrations', :js do
+RSpec.describe 'User views integrations', :js, feature_category: :integrations do
include_context 'project integration activation'
it 'shows the list of available integrations' do
diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb
index ac83de3e765..adf410ce6e8 100644
--- a/spec/features/projects/issuable_templates_spec.rb
+++ b/spec/features/projects/issuable_templates_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'issuable templates', :js do
+RSpec.describe 'issuable templates', :js, feature_category: :projects do
include ProjectForksHelper
let(:user) { create(:user) }
diff --git a/spec/features/projects/issues/design_management/user_links_to_designs_in_issue_spec.rb b/spec/features/projects/issues/design_management/user_links_to_designs_in_issue_spec.rb
index 78fb470d4ea..ef7022dcda8 100644
--- a/spec/features/projects/issues/design_management/user_links_to_designs_in_issue_spec.rb
+++ b/spec/features/projects/issues/design_management/user_links_to_designs_in_issue_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'viewing issues with design references' do
+RSpec.describe 'viewing issues with design references', feature_category: :design_management do
include DesignManagementTestHelpers
let_it_be(:public_project) { create(:project_empty_repo, :public) }
diff --git a/spec/features/projects/issues/design_management/user_paginates_designs_spec.rb b/spec/features/projects/issues/design_management/user_paginates_designs_spec.rb
index 908e30478b2..1490702a964 100644
--- a/spec/features/projects/issues/design_management/user_paginates_designs_spec.rb
+++ b/spec/features/projects/issues/design_management/user_paginates_designs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User paginates issue designs', :js do
+RSpec.describe 'User paginates issue designs', :js, feature_category: :design_management do
include DesignManagementTestHelpers
let(:project) { create(:project_empty_repo, :public) }
diff --git a/spec/features/projects/issues/design_management/user_permissions_upload_spec.rb b/spec/features/projects/issues/design_management/user_permissions_upload_spec.rb
index cfd8a4540ee..094bc9218ed 100644
--- a/spec/features/projects/issues/design_management/user_permissions_upload_spec.rb
+++ b/spec/features/projects/issues/design_management/user_permissions_upload_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User design permissions', :js do
+RSpec.describe 'User design permissions', :js, feature_category: :design_management do
include DesignManagementTestHelpers
let(:project) { create(:project_empty_repo, :public) }
diff --git a/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb b/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb
index 27d0be23aec..858d6751afa 100644
--- a/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb
+++ b/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User uploads new design', :js do
+RSpec.describe 'User uploads new design', :js, feature_category: :design_management do
include DesignManagementTestHelpers
let(:project) { create(:project_empty_repo, :public) }
diff --git a/spec/features/projects/issues/design_management/user_views_design_images_spec.rb b/spec/features/projects/issues/design_management/user_views_design_images_spec.rb
index c3aefe05f75..c5fc11222c2 100644
--- a/spec/features/projects/issues/design_management/user_views_design_images_spec.rb
+++ b/spec/features/projects/issues/design_management/user_views_design_images_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Users views raw design image files' do
+RSpec.describe 'Users views raw design image files', feature_category: :design_management do
include DesignManagementTestHelpers
let_it_be(:project) { create(:project, :public) }
diff --git a/spec/features/projects/issues/design_management/user_views_design_spec.rb b/spec/features/projects/issues/design_management/user_views_design_spec.rb
index b513a4fe3fa..11c8bdda3ac 100644
--- a/spec/features/projects/issues/design_management/user_views_design_spec.rb
+++ b/spec/features/projects/issues/design_management/user_views_design_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views issue designs', :js do
+RSpec.describe 'User views issue designs', :js, feature_category: :design_management do
include DesignManagementTestHelpers
let_it_be(:project) { create(:project_empty_repo, :public) }
diff --git a/spec/features/projects/issues/design_management/user_views_designs_spec.rb b/spec/features/projects/issues/design_management/user_views_designs_spec.rb
index 46c772027ad..995ed66df98 100644
--- a/spec/features/projects/issues/design_management/user_views_designs_spec.rb
+++ b/spec/features/projects/issues/design_management/user_views_designs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views issue designs', :js do
+RSpec.describe 'User views issue designs', :js, feature_category: :design_management do
include DesignManagementTestHelpers
let_it_be(:project) { create(:project_empty_repo, :public) }
diff --git a/spec/features/projects/issues/design_management/user_views_designs_with_svg_xss_spec.rb b/spec/features/projects/issues/design_management/user_views_designs_with_svg_xss_spec.rb
index 682a45cf592..a45b9b718c3 100644
--- a/spec/features/projects/issues/design_management/user_views_designs_with_svg_xss_spec.rb
+++ b/spec/features/projects/issues/design_management/user_views_designs_with_svg_xss_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views an SVG design that contains XSS', :js do
+RSpec.describe 'User views an SVG design that contains XSS', :js, feature_category: :design_management do
include DesignManagementTestHelpers
let(:project) { create(:project_empty_repo, :public) }
diff --git a/spec/features/projects/issues/email_participants_spec.rb b/spec/features/projects/issues/email_participants_spec.rb
index 3ffe0a5ced8..4dedbff608e 100644
--- a/spec/features/projects/issues/email_participants_spec.rb
+++ b/spec/features/projects/issues/email_participants_spec.rb
@@ -2,16 +2,15 @@
require 'spec_helper'
-RSpec.describe 'viewing an issue', :js do
+RSpec.describe 'viewing an issue', :js, feature_category: :issue_email_participants do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public) }
- let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be_with_refind(:issue) { create(:issue, project: project) }
let_it_be(:note) { create(:note_on_issue, project: project, noteable: issue) }
let_it_be(:participants) { create_list(:issue_email_participant, 4, issue: issue) }
before do
- sign_in(user)
- visit project_issue_path(project, issue)
+ project.add_reporter(user)
end
shared_examples 'email participants warning' do |selector|
@@ -20,15 +19,48 @@ RSpec.describe 'viewing an issue', :js do
end
end
- context 'for a new note' do
- it_behaves_like 'email participants warning', '.new-note'
+ shared_examples 'no email participants warning' do |selector|
+ it 'does not show email participants warning' do
+ expect(find(selector)).not_to have_content(", and 1 more will be notified of your comment")
+ end
+ end
+
+ context 'when issue is confidential' do
+ before do
+ issue.update!(confidential: true)
+ sign_in(user)
+ visit project_issue_path(project, issue)
+ end
+
+ context 'for a new note' do
+ it_behaves_like 'email participants warning', '.new-note'
+ end
+
+ context 'for a reply form' do
+ before do
+ find('.js-reply-button').click
+ end
+
+ it_behaves_like 'email participants warning', '.note-edit-form'
+ end
end
- context 'for a reply form' do
+ context 'when issue is not confidential' do
before do
- find('.js-reply-button').click
+ sign_in(user)
+ visit project_issue_path(project, issue)
end
- it_behaves_like 'email participants warning', '.note-edit-form'
+ context 'for a new note' do
+ it_behaves_like 'no email participants warning', '.new-note'
+ end
+
+ context 'for a reply form' do
+ before do
+ find('.js-reply-button').click
+ end
+
+ it_behaves_like 'no email participants warning', '.note-edit-form'
+ end
end
end
diff --git a/spec/features/projects/issues/viewing_relocated_issues_spec.rb b/spec/features/projects/issues/viewing_relocated_issues_spec.rb
index 10d5ad1747c..abd36b3ceef 100644
--- a/spec/features/projects/issues/viewing_relocated_issues_spec.rb
+++ b/spec/features/projects/issues/viewing_relocated_issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'issues canonical link' do
+RSpec.describe 'issues canonical link', feature_category: :team_planning do
include Spec::Support::Helpers::Features::CanonicalLinkHelpers
let_it_be(:original_project) { create(:project, :public) }
diff --git a/spec/features/projects/jobs/permissions_spec.rb b/spec/features/projects/jobs/permissions_spec.rb
index 740d009d6b8..c3c0043a6ef 100644
--- a/spec/features/projects/jobs/permissions_spec.rb
+++ b/spec/features/projects/jobs/permissions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project Jobs Permissions' do
+RSpec.describe 'Project Jobs Permissions', feature_category: :projects do
using RSpec::Parameterized::TableSyntax
let_it_be_with_reload(:group) { create(:group, name: 'some group') }
diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb
index 6a0cfcde812..78fb72ad2df 100644
--- a/spec/features/projects/jobs/user_browses_job_spec.rb
+++ b/spec/features/projects/jobs/user_browses_job_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User browses a job', :js do
+RSpec.describe 'User browses a job', :js, feature_category: :projects do
include Spec::Support::Helpers::ModalHelpers
let(:user) { create(:user) }
diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb
index cb3c1594868..1634f6dee74 100644
--- a/spec/features/projects/jobs/user_browses_jobs_spec.rb
+++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb
@@ -8,7 +8,7 @@ def visit_jobs_page
wait_for_requests
end
-RSpec.describe 'User browses jobs' do
+RSpec.describe 'User browses jobs', feature_category: :projects do
describe 'Jobs', :js do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
diff --git a/spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb b/spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb
index eea7e070a35..a9e0fce1a1c 100644
--- a/spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb
+++ b/spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User triggers manual job with variables', :js do
+RSpec.describe 'User triggers manual job with variables', :js, feature_category: :projects do
let(:user) { create(:user) }
let(:user_access_level) { :developer }
let(:project) { create(:project, :repository, namespace: user.namespace) }
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 96a8168e708..557a20ff2d6 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require 'tempfile'
-RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
+RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state, feature_category: :projects do
include Gitlab::Routing
include ProjectForksHelper
@@ -215,10 +215,6 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
visit project_job_path(project, job)
end
- it 'shows retry button' do
- expect(page).to have_link('Retry')
- end
-
context 'if job passed' do
it 'does not show New issue button' do
expect(page).not_to have_link('New issue')
diff --git a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
index 91a30004fc3..846a0a25891 100644
--- a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
+++ b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Issue prioritization' do
+RSpec.describe 'Issue prioritization', feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
diff --git a/spec/features/projects/labels/search_labels_spec.rb b/spec/features/projects/labels/search_labels_spec.rb
index 04dfd4ca5f1..d058565925e 100644
--- a/spec/features/projects/labels/search_labels_spec.rb
+++ b/spec/features/projects/labels/search_labels_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Search for labels', :js do
+RSpec.describe 'Search for labels', :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project) }
let!(:label1) { create(:label, title: 'Foo', description: 'Lorem ipsum', project: project) }
diff --git a/spec/features/projects/labels/sort_labels_spec.rb b/spec/features/projects/labels/sort_labels_spec.rb
index f2f1acd2348..378a575348e 100644
--- a/spec/features/projects/labels/sort_labels_spec.rb
+++ b/spec/features/projects/labels/sort_labels_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Sort labels', :js do
+RSpec.describe 'Sort labels', :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project) }
let!(:label1) { create(:label, title: 'Foo', description: 'Lorem ipsum', project: project) }
diff --git a/spec/features/projects/labels/subscription_spec.rb b/spec/features/projects/labels/subscription_spec.rb
index 7ca8a542c21..f1537458a18 100644
--- a/spec/features/projects/labels/subscription_spec.rb
+++ b/spec/features/projects/labels/subscription_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Labels subscription' do
+RSpec.describe 'Labels subscription', feature_category: :team_planning do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, :public, namespace: group) }
diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb
index 706ea92c086..b527b8926a0 100644
--- a/spec/features/projects/labels/update_prioritization_spec.rb
+++ b/spec/features/projects/labels/update_prioritization_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Prioritize labels' do
+RSpec.describe 'Prioritize labels', feature_category: :team_planning do
include DragTo
let(:user) { create(:user) }
diff --git a/spec/features/projects/labels/user_creates_labels_spec.rb b/spec/features/projects/labels/user_creates_labels_spec.rb
index 001d23cd2c9..46729048fe7 100644
--- a/spec/features/projects/labels/user_creates_labels_spec.rb
+++ b/spec/features/projects/labels/user_creates_labels_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User creates labels" do
+RSpec.describe "User creates labels", feature_category: :team_planning do
let_it_be(:project) { create(:project_empty_repo, :public) }
let_it_be(:user) { create(:user) }
diff --git a/spec/features/projects/labels/user_edits_labels_spec.rb b/spec/features/projects/labels/user_edits_labels_spec.rb
index 999c238c7b3..f90f215f9fc 100644
--- a/spec/features/projects/labels/user_edits_labels_spec.rb
+++ b/spec/features/projects/labels/user_edits_labels_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User edits labels" do
+RSpec.describe "User edits labels", feature_category: :team_planning do
include Spec::Support::Helpers::ModalHelpers
let_it_be(:project) { create(:project_empty_repo, :public) }
diff --git a/spec/features/projects/labels/user_promotes_label_spec.rb b/spec/features/projects/labels/user_promotes_label_spec.rb
index 4cb22c2e48c..e130dc561da 100644
--- a/spec/features/projects/labels/user_promotes_label_spec.rb
+++ b/spec/features/projects/labels/user_promotes_label_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User promotes label' do
+RSpec.describe 'User promotes label', feature_category: :team_planning do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, namespace: group) }
diff --git a/spec/features/projects/labels/user_removes_labels_spec.rb b/spec/features/projects/labels/user_removes_labels_spec.rb
index 11d73a56965..55dc52b8ccf 100644
--- a/spec/features/projects/labels/user_removes_labels_spec.rb
+++ b/spec/features/projects/labels/user_removes_labels_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User removes labels" do
+RSpec.describe "User removes labels", feature_category: :team_planning do
let(:project) { create(:project_empty_repo, :public) }
let(:user) { create(:user) }
diff --git a/spec/features/projects/labels/user_sees_breadcrumb_links_spec.rb b/spec/features/projects/labels/user_sees_breadcrumb_links_spec.rb
index f9c65c08ec0..117371e6904 100644
--- a/spec/features/projects/labels/user_sees_breadcrumb_links_spec.rb
+++ b/spec/features/projects/labels/user_sees_breadcrumb_links_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'New project label breadcrumb' do
+RSpec.describe 'New project label breadcrumb', feature_category: :team_planning do
let(:project) { create(:project) }
let(:user) { project.creator }
diff --git a/spec/features/projects/labels/user_sees_links_to_issuables_spec.rb b/spec/features/projects/labels/user_sees_links_to_issuables_spec.rb
index 6f98883a412..d8c673a2ce5 100644
--- a/spec/features/projects/labels/user_sees_links_to_issuables_spec.rb
+++ b/spec/features/projects/labels/user_sees_links_to_issuables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Labels > User sees links to issuables' do
+RSpec.describe 'Projects > Labels > User sees links to issuables', feature_category: :team_planning do
let_it_be(:user) { create(:user) }
before do
diff --git a/spec/features/projects/labels/user_views_labels_spec.rb b/spec/features/projects/labels/user_views_labels_spec.rb
index 7a6942b6259..b5a9a735af9 100644
--- a/spec/features/projects/labels/user_views_labels_spec.rb
+++ b/spec/features/projects/labels/user_views_labels_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User views labels" do
+RSpec.describe "User views labels", feature_category: :team_planning do
let_it_be(:project) { create(:project_empty_repo, :public) }
let_it_be(:user) { create(:user) }
diff --git a/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb b/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb
index c9fee9bee7a..63dc99efc8f 100644
--- a/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb
+++ b/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Members > Group member cannot leave group project' do
+RSpec.describe 'Projects > Members > Group member cannot leave group project', feature_category: :subgroups do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
diff --git a/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb b/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb
index 34c870b8a96..07886950b95 100644
--- a/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb
+++ b/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Projects > Members > Group member cannot request access to their group project' do
+RSpec.describe 'Projects > Members > Group member cannot request access to their group project',
+feature_category: :subgroups do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
diff --git a/spec/features/projects/members/group_members_spec.rb b/spec/features/projects/members/group_members_spec.rb
index 6aa6acbdae4..416b96ab668 100644
--- a/spec/features/projects/members/group_members_spec.rb
+++ b/spec/features/projects/members/group_members_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects members', :js do
+RSpec.describe 'Projects members', :js, feature_category: :subgroups do
include Spec::Support::Helpers::Features::MembersHelpers
let(:user) { create(:user) }
diff --git a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb
index ec86b7db4fa..7a11ee61c5f 100644
--- a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb
+++ b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Projects > Members > Group requester cannot request access to project', :js do
+RSpec.describe 'Projects > Members > Group requester cannot request access to project', :js,
+feature_category: :subgroups do
let(:user) { create(:user) }
let(:owner) { create(:user) }
let(:group) { create(:group, :public) }
diff --git a/spec/features/projects/members/groups_with_access_list_spec.rb b/spec/features/projects/members/groups_with_access_list_spec.rb
index 821b9249aa8..51acba246c5 100644
--- a/spec/features/projects/members/groups_with_access_list_spec.rb
+++ b/spec/features/projects/members/groups_with_access_list_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Members > Groups with access list', :js do
+RSpec.describe 'Projects > Members > Groups with access list', :js, feature_category: :subgroups do
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::ModalHelpers
include Spec::Support::Helpers::Features::InviteMembersModalHelper
diff --git a/spec/features/projects/members/manage_groups_spec.rb b/spec/features/projects/members/manage_groups_spec.rb
index e86affbbca1..b78bfacf171 100644
--- a/spec/features/projects/members/manage_groups_spec.rb
+++ b/spec/features/projects/members/manage_groups_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project > Members > Manage groups', :js do
+RSpec.describe 'Project > Members > Manage groups', :js, feature_category: :subgroups do
include ActionView::Helpers::DateHelper
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::Features::InviteMembersModalHelper
diff --git a/spec/features/projects/members/manage_members_spec.rb b/spec/features/projects/members/manage_members_spec.rb
index 1f317c55256..3ffa402dc2c 100644
--- a/spec/features/projects/members/manage_members_spec.rb
+++ b/spec/features/projects/members/manage_members_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Members > Manage members', :js, product_group: :onboarding do
+RSpec.describe 'Projects > Members > Manage members', :js, feature_category: :onboarding do
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::Features::InviteMembersModalHelper
include Spec::Support::Helpers::ModalHelpers
@@ -104,7 +104,6 @@ RSpec.describe 'Projects > Members > Manage members', :js, product_group: :onboa
click_on 'Invite members'
- click_on 'Guest'
wait_for_requests
end
@@ -112,13 +111,7 @@ RSpec.describe 'Projects > Members > Manage members', :js, product_group: :onboa
let(:current_user) { project_owner }
it 'shows Owner in the dropdown' do
- page.within '.dropdown-menu' do
- expect(page).to have_button('Guest')
- expect(page).to have_button('Reporter')
- expect(page).to have_button('Developer')
- expect(page).to have_button('Maintainer')
- expect(page).to have_button('Owner')
- end
+ expect(page).to have_select('Select a role', options: %w[Guest Reporter Developer Maintainer Owner])
end
end
@@ -126,13 +119,8 @@ RSpec.describe 'Projects > Members > Manage members', :js, product_group: :onboa
let(:current_user) { project_maintainer }
it 'does not show the Owner option' do
- page.within '.dropdown-menu' do
- expect(page).to have_button('Guest')
- expect(page).to have_button('Reporter')
- expect(page).to have_button('Developer')
- expect(page).to have_button('Maintainer')
- expect(page).not_to have_button('Owner')
- end
+ expect(page).to have_select('Select a role', options: %w[Guest Reporter Developer Maintainer])
+ expect(page).not_to have_select('Select a role', options: %w[Owner])
end
end
end
diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
index c92e8bc2954..31c8237aacc 100644
--- a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
+++ b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Members > Maintainer adds member with expiration date', :js do
+RSpec.describe 'Projects > Members > Maintainer adds member with expiration date', :js, feature_category: :subgroups do
include ActiveSupport::Testing::TimeHelpers
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::Features::InviteMembersModalHelper
diff --git a/spec/features/projects/members/master_manages_access_requests_spec.rb b/spec/features/projects/members/master_manages_access_requests_spec.rb
index f4e8c55e3cc..cea59679226 100644
--- a/spec/features/projects/members/master_manages_access_requests_spec.rb
+++ b/spec/features/projects/members/master_manages_access_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Members > Maintainer manages access requests' do
+RSpec.describe 'Projects > Members > Maintainer manages access requests', feature_category: :subgroups do
it_behaves_like 'Maintainer manages access requests' do
let(:entity) { create(:project, :public, :with_namespace_settings) }
let(:members_page_path) { project_project_members_path(entity) }
diff --git a/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb b/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb
index fa02e815867..dc18ca88c36 100644
--- a/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb
+++ b/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Members > Member cannot request access to their project' do
+RSpec.describe 'Projects > Members > Member cannot request access to their project', feature_category: :subgroups do
let(:member) { create(:user) }
let(:project) { create(:project) }
diff --git a/spec/features/projects/members/member_leaves_project_spec.rb b/spec/features/projects/members/member_leaves_project_spec.rb
index db227f3701d..2632bc2f5bd 100644
--- a/spec/features/projects/members/member_leaves_project_spec.rb
+++ b/spec/features/projects/members/member_leaves_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Members > Member leaves project' do
+RSpec.describe 'Projects > Members > Member leaves project', feature_category: :subgroups do
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::ModalHelpers
diff --git a/spec/features/projects/members/owner_cannot_leave_project_spec.rb b/spec/features/projects/members/owner_cannot_leave_project_spec.rb
index 45a8f979b87..7908fd3a98f 100644
--- a/spec/features/projects/members/owner_cannot_leave_project_spec.rb
+++ b/spec/features/projects/members/owner_cannot_leave_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Members > Owner cannot leave project' do
+RSpec.describe 'Projects > Members > Owner cannot leave project', feature_category: :subgroups do
let(:project) { create(:project) }
before do
diff --git a/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb b/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb
index fad5d831c19..b5a862578d3 100644
--- a/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb
+++ b/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Members > Owner cannot request access to their own project' do
+RSpec.describe 'Projects > Members > Owner cannot request access to their own project', feature_category: :subgroups do
let(:project) { create(:project) }
before do
diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb
index 8aadd6302d0..5c72d9efeb3 100644
--- a/spec/features/projects/members/sorting_spec.rb
+++ b/spec/features/projects/members/sorting_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Members > Sorting', :js do
+RSpec.describe 'Projects > Members > Sorting', :js, feature_category: :subgroups do
include Spec::Support::Helpers::Features::MembersHelpers
let(:maintainer) { create(:user, name: 'John Doe', created_at: 5.days.ago, last_activity_on: Date.today) }
diff --git a/spec/features/projects/members/tabs_spec.rb b/spec/features/projects/members/tabs_spec.rb
index 5611e7ee810..232420224fc 100644
--- a/spec/features/projects/members/tabs_spec.rb
+++ b/spec/features/projects/members/tabs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Members > Tabs', :js do
+RSpec.describe 'Projects > Members > Tabs', :js, feature_category: :subgroups do
include Spec::Support::Helpers::Features::MembersHelpers
using RSpec::Parameterized::TableSyntax
diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb
index be124502c32..11d162fabd4 100644
--- a/spec/features/projects/members/user_requests_access_spec.rb
+++ b/spec/features/projects/members/user_requests_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Members > User requests access', :js do
+RSpec.describe 'Projects > Members > User requests access', :js, feature_category: :subgroups do
include Spec::Support::Helpers::ModalHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/projects/merge_request_button_spec.rb b/spec/features/projects/merge_request_button_spec.rb
index eb52a7821f9..56aee469252 100644
--- a/spec/features/projects/merge_request_button_spec.rb
+++ b/spec/features/projects/merge_request_button_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge Request button' do
+RSpec.describe 'Merge Request button', feature_category: :projects do
include ProjectForksHelper
let_it_be(:user) { create(:user) }
diff --git a/spec/features/projects/milestones/gfm_autocomplete_spec.rb b/spec/features/projects/milestones/gfm_autocomplete_spec.rb
index 547a5d11dec..d4ce10b5cb5 100644
--- a/spec/features/projects/milestones/gfm_autocomplete_spec.rb
+++ b/spec/features/projects/milestones/gfm_autocomplete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'GFM autocomplete', :js do
+RSpec.describe 'GFM autocomplete', :js, feature_category: :team_planning do
let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
let_it_be(:group) { create(:group, name: 'Ancestor') }
let_it_be(:project) { create(:project, :repository, group: group) }
diff --git a/spec/features/projects/milestones/milestone_spec.rb b/spec/features/projects/milestones/milestone_spec.rb
index 6bd139c0ebe..73d46d3764e 100644
--- a/spec/features/projects/milestones/milestone_spec.rb
+++ b/spec/features/projects/milestones/milestone_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project milestone', :js do
+RSpec.describe 'Project milestone', :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
let(:milestone) { create(:milestone, project: project) }
diff --git a/spec/features/projects/milestones/milestones_sorting_spec.rb b/spec/features/projects/milestones/milestones_sorting_spec.rb
index 5ba4289fd11..8a8e7d07435 100644
--- a/spec/features/projects/milestones/milestones_sorting_spec.rb
+++ b/spec/features/projects/milestones/milestones_sorting_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Milestones sorting', :js do
+RSpec.describe 'Milestones sorting', :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
let(:milestones_for_sort_by) do
diff --git a/spec/features/projects/milestones/new_spec.rb b/spec/features/projects/milestones/new_spec.rb
index 170268297cd..ef9325a1627 100644
--- a/spec/features/projects/milestones/new_spec.rb
+++ b/spec/features/projects/milestones/new_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Creating a new project milestone', :js do
+RSpec.describe 'Creating a new project milestone', :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
diff --git a/spec/features/projects/milestones/user_interacts_with_labels_spec.rb b/spec/features/projects/milestones/user_interacts_with_labels_spec.rb
index d658599c52b..36dfee7811d 100644
--- a/spec/features/projects/milestones/user_interacts_with_labels_spec.rb
+++ b/spec/features/projects/milestones/user_interacts_with_labels_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User interacts with labels' do
+RSpec.describe 'User interacts with labels', feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
let(:milestone) { create(:milestone, project: project, title: 'v2.2', description: '# Description header') }
diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb
index 5b5f7860e43..4d85b5cfb2e 100644
--- a/spec/features/projects/navbar_spec.rb
+++ b/spec/features/projects/navbar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project navbar' do
+RSpec.describe 'Project navbar', feature_category: :projects do
include NavbarStructureHelper
include WaitForRequests
diff --git a/spec/features/projects/network_graph_spec.rb b/spec/features/projects/network_graph_spec.rb
index 97b743b4d73..b36fde8a2bf 100644
--- a/spec/features/projects/network_graph_spec.rb
+++ b/spec/features/projects/network_graph_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project Network Graph', :js do
+RSpec.describe 'Project Network Graph', :js, feature_category: :projects do
let(:user) { create :user }
let(:project) { create :project, :repository, namespace: user.namespace }
diff --git a/spec/features/projects/new_project_from_template_spec.rb b/spec/features/projects/new_project_from_template_spec.rb
index 1c8647d859a..97304840010 100644
--- a/spec/features/projects/new_project_from_template_spec.rb
+++ b/spec/features/projects/new_project_from_template_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'New project from template', :js do
+RSpec.describe 'New project from template', :js, feature_category: :projects do
let(:user) { create(:user) }
before do
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index 7cf05242a23..769ad5bf61a 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'New project', :js do
+RSpec.describe 'New project', :js, feature_category: :projects do
include Spec::Support::Helpers::Features::TopNavSpecHelpers
context 'as a user' do
diff --git a/spec/features/projects/package_files_spec.rb b/spec/features/projects/package_files_spec.rb
index 6dc0294bb9e..824b57db7ad 100644
--- a/spec/features/projects/package_files_spec.rb
+++ b/spec/features/projects/package_files_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'PackageFiles' do
+RSpec.describe 'PackageFiles', feature_category: :projects do
let(:user) { create(:user) }
let(:project) { create(:project) }
let!(:package) { create(:maven_package, project: project) }
diff --git a/spec/features/projects/packages_spec.rb b/spec/features/projects/packages_spec.rb
index bbe913cf1e5..31ff455d0df 100644
--- a/spec/features/projects/packages_spec.rb
+++ b/spec/features/projects/packages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Packages' do
+RSpec.describe 'Packages', feature_category: :projects do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
diff --git a/spec/features/projects/pages/user_adds_domain_spec.rb b/spec/features/projects/pages/user_adds_domain_spec.rb
index 5cb4fa163c8..708210e669c 100644
--- a/spec/features/projects/pages/user_adds_domain_spec.rb
+++ b/spec/features/projects/pages/user_adds_domain_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'User adds pages domain', :js do
+RSpec.describe 'User adds pages domain', :js, feature_category: :pages do
include LetsEncryptHelpers
include Spec::Support::Helpers::ModalHelpers
diff --git a/spec/features/projects/pages/user_configures_pages_pipeline_spec.rb b/spec/features/projects/pages/user_configures_pages_pipeline_spec.rb
index 029479d6b95..baef75ca303 100644
--- a/spec/features/projects/pages/user_configures_pages_pipeline_spec.rb
+++ b/spec/features/projects/pages/user_configures_pages_pipeline_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'Pages edits pages settings', :js do
+RSpec.describe 'Pages edits pages settings', :js, feature_category: :pages do
include Spec::Support::Helpers::ModalHelpers
let_it_be(:project) { create(:project, pages_https_only: false) }
@@ -46,14 +46,14 @@ RSpec.describe 'Pages edits pages settings', :js do
Feature.disable(:use_pipeline_wizard_for_pages)
end
+ after do
+ Feature.enable(:use_pipeline_wizard_for_pages)
+ end
+
it 'shows configure pages instructions' do
visit project_pages_path(project)
expect(page).to have_content('Configure pages')
end
-
- after do
- Feature.enable(:use_pipeline_wizard_for_pages)
- end
end
end
diff --git a/spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb b/spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb
index 2e28fa20b90..a7da59200e9 100644
--- a/spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb
+++ b/spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe "Pages with Let's Encrypt", :https_pages_enabled do
+RSpec.describe "Pages with Let's Encrypt", :https_pages_enabled, feature_category: :pages do
include LetsEncryptHelpers
include Spec::Support::Helpers::ModalHelpers
diff --git a/spec/features/projects/pages/user_edits_settings_spec.rb b/spec/features/projects/pages/user_edits_settings_spec.rb
index 88c27a6adf2..7ceefdecbae 100644
--- a/spec/features/projects/pages/user_edits_settings_spec.rb
+++ b/spec/features/projects/pages/user_edits_settings_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'Pages edits pages settings', :js do
+RSpec.describe 'Pages edits pages settings', :js, feature_category: :pages do
include Spec::Support::Helpers::ModalHelpers
let_it_be_with_reload(:project) { create(:project, :pages_published, pages_https_only: false) }
diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb
index e569fef76f8..8beb8af1a8e 100644
--- a/spec/features/projects/pipeline_schedules_spec.rb
+++ b/spec/features/projects/pipeline_schedules_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Pipeline Schedules', :js do
+RSpec.describe 'Pipeline Schedules', :js, feature_category: :projects do
include Spec::Support::Helpers::ModalHelpers
let!(:project) { create(:project, :repository) }
@@ -64,7 +64,7 @@ RSpec.describe 'Pipeline Schedules', :js do
it 'shows the pipeline schedule with default ref' do
page.within('[data-testid="schedule-target-ref"]') do
- expect(first('.gl-new-dropdown-button-text').text).to eq('master')
+ expect(first('.gl-dropdown-button-text').text).to eq('master')
end
end
end
@@ -77,7 +77,7 @@ RSpec.describe 'Pipeline Schedules', :js do
it 'shows the pipeline schedule with default ref' do
page.within('[data-testid="schedule-target-ref"]') do
- expect(first('.gl-new-dropdown-button-text').text).to eq('master')
+ expect(first('.gl-dropdown-button-text').text).to eq('master')
end
end
end
diff --git a/spec/features/projects/pipelines/legacy_pipeline_spec.rb b/spec/features/projects/pipelines/legacy_pipeline_spec.rb
deleted file mode 100644
index c4fc194f0cd..00000000000
--- a/spec/features/projects/pipelines/legacy_pipeline_spec.rb
+++ /dev/null
@@ -1,1315 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Pipeline', :js do
- include RoutesHelpers
- include ProjectForksHelper
- include ::ExclusiveLeaseHelpers
-
- let_it_be(:project) { create(:project) }
-
- let(:user) { create(:user) }
- let(:role) { :developer }
-
- before do
- sign_in(user)
- project.add_role(user, role)
- stub_feature_flags(pipeline_tabs_vue: false)
- end
-
- shared_context 'pipeline builds' do
- let!(:build_passed) do
- create(:ci_build, :success,
- pipeline: pipeline, stage: 'build', stage_idx: 0, name: 'build')
- end
-
- let!(:build_failed) do
- create(:ci_build, :failed,
- pipeline: pipeline, stage: 'test', stage_idx: 1, name: 'test')
- end
-
- let!(:build_preparing) do
- create(:ci_build, :preparing,
- pipeline: pipeline, stage: 'deploy', stage_idx: 2, name: 'prepare')
- end
-
- let!(:build_running) do
- create(:ci_build, :running,
- pipeline: pipeline, stage: 'deploy', stage_idx: 3, name: 'deploy')
- end
-
- let!(:build_manual) do
- create(:ci_build, :manual,
- pipeline: pipeline, stage: 'deploy', stage_idx: 3, name: 'manual-build')
- end
-
- let!(:build_scheduled) do
- create(:ci_build, :scheduled,
- pipeline: pipeline, stage: 'deploy', stage_idx: 3, name: 'delayed-job')
- end
-
- let!(:build_external) do
- create(:generic_commit_status, status: 'success',
- pipeline: pipeline,
- name: 'jenkins',
- stage: 'external',
- ref: 'master',
- target_url: 'http://gitlab.com/status')
- end
- end
-
- describe 'GET /:project/-/pipelines/:id' do
- include_context 'pipeline builds'
-
- let_it_be(:group) { create(:group) }
- let_it_be(:project, reload: true) { create(:project, :repository, group: group) }
-
- let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id, user: user) }
-
- subject(:visit_pipeline) { visit project_pipeline_path(project, pipeline) }
-
- it 'shows the pipeline graph' do
- visit_pipeline
-
- expect(page).to have_selector('.js-pipeline-graph')
- expect(page).to have_content('build')
- expect(page).to have_content('test')
- expect(page).to have_content('deploy')
- expect(page).to have_content('Retry')
- expect(page).to have_content('Cancel running')
- end
-
- it 'shows Pipeline tab pane as active' do
- visit_pipeline
-
- expect(page).to have_css('#js-tab-pipeline.active')
- end
-
- it 'shows link to the pipeline ref' do
- visit_pipeline
-
- expect(page).to have_link(pipeline.ref)
- end
-
- it 'shows the pipeline information' do
- visit_pipeline
-
- within '.pipeline-info' do
- expect(page).to have_content("#{pipeline.statuses.count} jobs " \
- "for #{pipeline.ref}")
- expect(page).to have_link(pipeline.ref,
- href: project_commits_path(pipeline.project, pipeline.ref))
- end
- end
-
- describe 'related merge requests' do
- context 'when there are no related merge requests' do
- it 'shows a "no related merge requests" message' do
- visit_pipeline
-
- within '.related-merge-request-info' do
- expect(page).to have_content('No related merge requests found.')
- end
- end
- end
-
- context 'when there is one related merge request' do
- let!(:merge_request) do
- create(:merge_request,
- source_project: project,
- source_branch: pipeline.ref)
- end
-
- it 'shows a link to the merge request' do
- visit_pipeline
-
- within '.related-merge-requests' do
- expect(page).to have_content('1 related merge request: ')
- expect(page).to have_selector('.js-truncated-mr-list')
- expect(page).to have_link("#{merge_request.to_reference} #{merge_request.title}")
-
- expect(page).not_to have_selector('.js-full-mr-list')
- expect(page).not_to have_selector('.text-expander')
- end
- end
- end
-
- context 'when there are two related merge requests' do
- let!(:merge_request1) do
- create(:merge_request,
- source_project: project,
- source_branch: pipeline.ref)
- end
-
- let!(:merge_request2) do
- create(:merge_request,
- source_project: project,
- source_branch: pipeline.ref,
- target_branch: 'fix')
- end
-
- it 'links to the most recent related merge request' do
- visit_pipeline
-
- within '.related-merge-requests' do
- expect(page).to have_content('2 related merge requests: ')
- expect(page).to have_link("#{merge_request2.to_reference} #{merge_request2.title}")
- expect(page).to have_selector('.text-expander')
- expect(page).to have_selector('.js-full-mr-list', visible: false)
- end
- end
-
- it 'expands to show links to all related merge requests' do
- visit_pipeline
-
- within '.related-merge-requests' do
- find('.text-expander').click
-
- expect(page).to have_selector('.js-full-mr-list', visible: true)
-
- pipeline.all_merge_requests.map do |merge_request|
- expect(page).to have_link(href: project_merge_request_path(project, merge_request))
- end
- end
- end
- end
- end
-
- describe 'pipelines details view' do
- let!(:status) { create(:user_status, user: pipeline.user, emoji: 'smirk', message: 'Authoring this object') }
-
- it 'pipeline header shows the user status and emoji' do
- visit project_pipeline_path(project, pipeline)
-
- within '[data-testid="ci-header-content"]' do
- expect(page).to have_selector("[data-testid='#{status.message}']")
- expect(page).to have_selector("[data-name='#{status.emoji}']")
- end
- end
- end
-
- describe 'pipeline graph' do
- before do
- visit_pipeline
- end
-
- context 'when pipeline has running builds' do
- it 'shows a running icon and a cancel action for the running build' do
- page.within('#ci-badge-deploy') do
- expect(page).to have_selector('.js-ci-status-icon-running')
- expect(page).to have_selector('.js-icon-cancel')
- expect(page).to have_content('deploy')
- end
- end
-
- it 'cancels the running build and shows retry button', :sidekiq_might_not_need_inline do
- find('#ci-badge-deploy .ci-action-icon-container').click
-
- page.within('#ci-badge-deploy') do
- expect(page).to have_css('.js-icon-retry')
- end
- end
- end
-
- context 'when pipeline has preparing builds' do
- it 'shows a preparing icon and a cancel action' do
- page.within('#ci-badge-prepare') do
- expect(page).to have_selector('.js-ci-status-icon-preparing')
- expect(page).to have_selector('.js-icon-cancel')
- expect(page).to have_content('prepare')
- end
- end
-
- it 'cancels the preparing build and shows retry button', :sidekiq_might_not_need_inline do
- find('#ci-badge-deploy .ci-action-icon-container').click
-
- page.within('#ci-badge-deploy') do
- expect(page).to have_css('.js-icon-retry')
- end
- end
- end
-
- context 'when pipeline has successful builds' do
- it 'shows the success icon and a retry action for the successful build' do
- page.within('#ci-badge-build') do
- expect(page).to have_selector('.js-ci-status-icon-success')
- expect(page).to have_content('build')
- end
-
- page.within('#ci-badge-build .ci-action-icon-container.js-icon-retry') do
- expect(page).to have_selector('svg')
- end
- end
-
- it 'is possible to retry the success job', :sidekiq_might_not_need_inline do
- find('#ci-badge-build .ci-action-icon-container').click
- wait_for_requests
-
- expect(page).not_to have_content('Retry job')
- within('.js-pipeline-header-container') do
- expect(page).to have_selector('.js-ci-status-icon-running')
- end
- end
- end
-
- context 'when pipeline has a delayed job' do
- let(:project) { create(:project, :repository, group: group) }
-
- it 'shows the scheduled icon and an unschedule action for the delayed job' do
- page.within('#ci-badge-delayed-job') do
- expect(page).to have_selector('.js-ci-status-icon-scheduled')
- expect(page).to have_content('delayed-job')
- end
-
- page.within('#ci-badge-delayed-job .ci-action-icon-container.js-icon-time-out') do
- expect(page).to have_selector('svg')
- end
- end
-
- it 'unschedules the delayed job and shows play button as a manual job', :sidekiq_might_not_need_inline do
- find('#ci-badge-delayed-job .ci-action-icon-container').click
-
- page.within('#ci-badge-delayed-job') do
- expect(page).to have_css('.js-icon-play')
- end
- end
- end
-
- context 'when pipeline has failed builds' do
- it 'shows the failed icon and a retry action for the failed build' do
- page.within('#ci-badge-test') do
- expect(page).to have_selector('.js-ci-status-icon-failed')
- expect(page).to have_content('test')
- end
-
- page.within('#ci-badge-test .ci-action-icon-container.js-icon-retry') do
- expect(page).to have_selector('svg')
- end
- end
-
- it 'is possible to retry the failed build', :sidekiq_might_not_need_inline do
- find('#ci-badge-test .ci-action-icon-container').click
- wait_for_requests
-
- expect(page).not_to have_content('Retry job')
- within('.js-pipeline-header-container') do
- expect(page).to have_selector('.js-ci-status-icon-running')
- end
- end
-
- it 'includes the failure reason' do
- page.within('#ci-badge-test') do
- build_link = page.find('.js-pipeline-graph-job-link')
- expect(build_link['title']).to eq('test - failed - (unknown failure)')
- end
- end
- end
-
- context 'when pipeline has manual jobs' do
- it 'shows the skipped icon and a play action for the manual build' do
- page.within('#ci-badge-manual-build') do
- expect(page).to have_selector('.js-ci-status-icon-manual')
- expect(page).to have_content('manual')
- end
-
- page.within('#ci-badge-manual-build .ci-action-icon-container.js-icon-play') do
- expect(page).to have_selector('svg')
- end
- end
-
- it 'is possible to play the manual job', :sidekiq_might_not_need_inline do
- find('#ci-badge-manual-build .ci-action-icon-container').click
- wait_for_requests
-
- expect(page).not_to have_content('Play job')
- within('.js-pipeline-header-container') do
- expect(page).to have_selector('.js-ci-status-icon-running')
- end
- end
- end
-
- context 'when pipeline has external job' do
- it 'shows the success icon and the generic comit status build' do
- expect(page).to have_selector('.js-ci-status-icon-success')
- expect(page).to have_content('jenkins')
- expect(page).to have_link('jenkins', href: 'http://gitlab.com/status')
- end
- end
- end
-
- context 'when the pipeline has manual stage' do
- before do
- create(:ci_build, :manual, pipeline: pipeline, stage_idx: 10, stage: 'publish', name: 'CentOS')
- create(:ci_build, :manual, pipeline: pipeline, stage_idx: 10, stage: 'publish', name: 'Debian')
- create(:ci_build, :manual, pipeline: pipeline, stage_idx: 10, stage: 'publish', name: 'OpenSUDE')
-
- # force to update stages statuses
- Ci::ProcessPipelineService.new(pipeline).execute
-
- visit_pipeline
- end
-
- it 'displays play all button' do
- expect(page).to have_selector('.js-stage-action')
- end
- end
-
- context 'page tabs' do
- before do
- visit_pipeline
- end
-
- it 'shows Pipeline, Jobs, DAG and Failed Jobs tabs with link' do
- expect(page).to have_link('Pipeline')
- expect(page).to have_link('Jobs')
- expect(page).to have_link('Needs')
- expect(page).to have_link('Failed Jobs')
- end
-
- it 'shows counter in Jobs tab' do
- expect(page.find('.js-builds-counter').text).to eq(pipeline.total_size.to_s)
- end
-
- it 'shows Pipeline tab as active' do
- expect(page).to have_css('.js-pipeline-tab-link .active')
- end
-
- context 'without permission to access builds' do
- let(:project) { create(:project, :public, :repository, public_builds: false) }
- let(:role) { :guest }
-
- it 'does not show the pipeline details page' do
- expect(page).to have_content('Not Found')
- end
- end
- end
-
- describe 'test tabs' do
- let(:pipeline) { create(:ci_pipeline, :with_test_reports, :with_report_results, project: project) }
-
- before do
- stub_feature_flags(pipeline_tabs_vue: false)
- visit_pipeline
- wait_for_requests
- end
-
- context 'with test reports' do
- it 'shows badge counter in Tests tab' do
- expect(page.find('.js-test-report-badge-counter').text).to eq(pipeline.test_report_summary.total[:count].to_s)
- end
-
- it 'calls summary.json endpoint', :js do
- find('.js-tests-tab-link').click
-
- expect(page).to have_content('Jobs')
- expect(page).to have_selector('[data-testid="tests-detail"]', visible: :all)
- end
- end
-
- context 'without test reports' do
- let(:pipeline) { create(:ci_pipeline, project: project) }
-
- it 'shows zero' do
- expect(page.find('.js-test-report-badge-counter', visible: :all).text).to eq("0")
- end
- end
- end
-
- context 'retrying jobs' do
- before do
- visit_pipeline
- end
-
- it { expect(page).not_to have_content('retried') }
-
- context 'when retrying' do
- before do
- find('[data-testid="retryPipeline"]').click
- wait_for_requests
- end
-
- it 'does not show a "Retry" button', :sidekiq_might_not_need_inline do
- expect(page).not_to have_content('Retry')
- end
-
- it 'shows running status in pipeline header', :sidekiq_might_not_need_inline do
- within('.js-pipeline-header-container') do
- expect(page).to have_selector('.js-ci-status-icon-running')
- end
- end
- end
- end
-
- context 'canceling jobs' do
- before do
- visit_pipeline
- end
-
- it { expect(page).not_to have_selector('.ci-canceled') }
-
- context 'when canceling' do
- before do
- click_on 'Cancel running'
- end
-
- it 'does not show a "Cancel running" button', :sidekiq_might_not_need_inline do
- expect(page).not_to have_content('Cancel running')
- end
- end
- end
-
- context 'when user can not delete' do
- before do
- visit_pipeline
- end
-
- it { expect(page).not_to have_button('Delete') }
- end
-
- context 'when deleting' do
- before do
- group.add_owner(user)
-
- visit_pipeline
-
- click_button 'Delete'
- click_button 'Delete pipeline'
- end
-
- it 'redirects to pipeline overview page', :sidekiq_inline do
- expect(page).to have_content('The pipeline has been deleted')
- expect(page).to have_current_path(project_pipelines_path(project), ignore_query: true)
- end
- end
-
- context 'when pipeline ref does not exist in repository anymore' do
- let(:pipeline) do
- create(:ci_empty_pipeline, project: project,
- ref: 'non-existent',
- sha: project.commit.id,
- user: user)
- end
-
- before do
- visit_pipeline
- end
-
- it 'does not render link to the pipeline ref' do
- expect(page).not_to have_link(pipeline.ref)
- expect(page).to have_content(pipeline.ref)
- end
-
- it 'does not render render raw HTML to the pipeline ref' do
- page.within '.pipeline-info' do
- expect(page).not_to have_content('<span class="ref-name"')
- end
- end
- end
-
- context 'when pipeline is detached merge request pipeline' do
- let(:source_project) { project }
- let(:target_project) { project }
-
- let(:merge_request) do
- create(:merge_request,
- :with_detached_merge_request_pipeline,
- source_project: source_project,
- target_project: target_project)
- end
-
- let(:pipeline) do
- merge_request.all_pipelines.last
- end
-
- it 'shows the pipeline information' do
- visit_pipeline
-
- within '.pipeline-info' do
- expect(page).to have_content("#{pipeline.statuses.count} jobs " \
- "for !#{merge_request.iid} " \
- "with #{merge_request.source_branch}")
- expect(page).to have_link("!#{merge_request.iid}",
- href: project_merge_request_path(project, merge_request))
- expect(page).to have_link(merge_request.source_branch,
- href: project_commits_path(merge_request.source_project, merge_request.source_branch))
- end
- end
-
- context 'when source branch does not exist' do
- before do
- project.repository.rm_branch(user, merge_request.source_branch)
- end
-
- it 'does not link to the source branch commit path' do
- visit_pipeline
-
- within '.pipeline-info' do
- expect(page).not_to have_link(merge_request.source_branch)
- expect(page).to have_content(merge_request.source_branch)
- end
- end
- end
-
- context 'when source project is a forked project' do
- let(:source_project) { fork_project(project, user, repository: true) }
-
- before do
- visit project_pipeline_path(source_project, pipeline)
- end
-
- it 'shows the pipeline information', :sidekiq_might_not_need_inline do
- within '.pipeline-info' do
- expect(page).to have_content("#{pipeline.statuses.count} jobs " \
- "for !#{merge_request.iid} " \
- "with #{merge_request.source_branch}")
- expect(page).to have_link("!#{merge_request.iid}",
- href: project_merge_request_path(project, merge_request))
- expect(page).to have_link(merge_request.source_branch,
- href: project_commits_path(merge_request.source_project, merge_request.source_branch))
- end
- end
- end
- end
-
- context 'when pipeline is merge request pipeline' do
- let(:project) { create(:project, :repository, group: group) }
- let(:source_project) { project }
- let(:target_project) { project }
-
- let(:merge_request) do
- create(:merge_request,
- :with_merge_request_pipeline,
- source_project: source_project,
- target_project: target_project,
- merge_sha: project.commit.id)
- end
-
- let(:pipeline) do
- merge_request.all_pipelines.last
- end
-
- before do
- pipeline.update!(user: user)
- end
-
- it 'shows the pipeline information' do
- visit_pipeline
-
- within '.pipeline-info' do
- expect(page).to have_content("#{pipeline.statuses.count} jobs " \
- "for !#{merge_request.iid} " \
- "with #{merge_request.source_branch} " \
- "into #{merge_request.target_branch}")
- expect(page).to have_link("!#{merge_request.iid}",
- href: project_merge_request_path(project, merge_request))
- expect(page).to have_link(merge_request.source_branch,
- href: project_commits_path(merge_request.source_project, merge_request.source_branch))
- expect(page).to have_link(merge_request.target_branch,
- href: project_commits_path(merge_request.target_project, merge_request.target_branch))
- end
- end
-
- context 'when target branch does not exist' do
- before do
- project.repository.rm_branch(user, merge_request.target_branch)
- end
-
- it 'does not link to the target branch commit path' do
- visit_pipeline
-
- within '.pipeline-info' do
- expect(page).not_to have_link(merge_request.target_branch)
- expect(page).to have_content(merge_request.target_branch)
- end
- end
- end
-
- context 'when source project is a forked project' do
- let(:source_project) { fork_project(project, user, repository: true) }
-
- before do
- visit project_pipeline_path(source_project, pipeline)
- end
-
- it 'shows the pipeline information', :sidekiq_might_not_need_inline do
- within '.pipeline-info' do
- expect(page).to have_content("#{pipeline.statuses.count} jobs " \
- "for !#{merge_request.iid} " \
- "with #{merge_request.source_branch} " \
- "into #{merge_request.target_branch}")
- expect(page).to have_link("!#{merge_request.iid}",
- href: project_merge_request_path(project, merge_request))
- expect(page).to have_link(merge_request.source_branch,
- href: project_commits_path(merge_request.source_project, merge_request.source_branch))
- expect(page).to have_link(merge_request.target_branch,
- href: project_commits_path(merge_request.target_project, merge_request.target_branch))
- end
- end
- end
- end
- end
-
- context 'when user does not have access to read jobs' do
- before do
- project.update!(public_builds: false)
- end
-
- describe 'GET /:project/-/pipelines/:id' do
- include_context 'pipeline builds'
-
- let_it_be(:project) { create(:project, :repository) }
-
- let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id, user: user) }
-
- before do
- visit project_pipeline_path(project, pipeline)
- end
-
- it 'shows the pipeline graph' do
- expect(page).to have_selector('.js-pipeline-graph')
- expect(page).to have_content('build')
- expect(page).to have_content('test')
- expect(page).to have_content('deploy')
- expect(page).to have_content('Retry')
- expect(page).to have_content('Cancel running')
- end
-
- it 'does not link to job' do
- expect(page).not_to have_selector('.js-pipeline-graph-job-link')
- end
- end
- end
-
- context 'when a bridge job exists' do
- include_context 'pipeline builds'
-
- let(:project) { create(:project, :repository) }
- let(:downstream) { create(:project, :repository) }
-
- let(:pipeline) do
- create(:ci_pipeline, project: project,
- ref: 'master',
- sha: project.commit.id,
- user: user)
- end
-
- let!(:bridge) do
- create(:ci_bridge, pipeline: pipeline,
- name: 'cross-build',
- user: user,
- downstream: downstream)
- end
-
- describe 'GET /:project/-/pipelines/:id' do
- before do
- visit project_pipeline_path(project, pipeline)
- end
-
- it 'shows the pipeline with a bridge job' do
- expect(page).to have_selector('.js-pipeline-graph')
- expect(page).to have_content('cross-build')
- end
-
- context 'when a scheduled pipeline is created by a blocked user' do
- let(:project) { create(:project, :repository) }
-
- let(:schedule) do
- create(:ci_pipeline_schedule,
- project: project,
- owner: project.first_owner,
- description: 'blocked user schedule'
- ).tap do |schedule|
- schedule.update_column(:next_run_at, 1.minute.ago)
- end
- end
-
- before do
- schedule.owner.block!
- PipelineScheduleWorker.new.perform
- end
-
- it 'displays the PipelineSchedule in an inactive state' do
- stub_feature_flags(pipeline_schedules_vue: false)
-
- visit project_pipeline_schedules_path(project)
- page.click_link('Inactive')
-
- expect(page).to have_selector('table.ci-table > tbody > tr > td', text: 'blocked user schedule')
- end
-
- it 'does not create a new Pipeline' do
- visit project_pipelines_path(project)
-
- expect(page).not_to have_selector('.ci-table')
- expect(schedule.last_pipeline).to be_nil
- end
- end
- end
-
- describe 'GET /:project/-/pipelines/:id/builds' do
- before do
- visit builds_project_pipeline_path(project, pipeline)
- end
-
- it 'shows a bridge job on a list' do
- expect(page).to have_content('cross-build')
- expect(page).to have_content(bridge.id)
- end
- end
- end
-
- context 'when build requires resource', :sidekiq_inline do
- let_it_be(:project) { create(:project, :repository) }
-
- let(:pipeline) { create(:ci_pipeline, project: project) }
- let(:resource_group) { create(:ci_resource_group, project: project) }
-
- let!(:test_job) do
- create(:ci_build, :pending, stage: 'test', name: 'test', stage_idx: 1, pipeline: pipeline, project: project)
- end
-
- let!(:deploy_job) do
- create(:ci_build, :created,
- stage: 'deploy',
- name: 'deploy',
- stage_idx: 2,
- pipeline: pipeline,
- project: project,
- resource_group: resource_group)
- end
-
- describe 'GET /:project/-/pipelines/:id' do
- subject { visit project_pipeline_path(project, pipeline) }
-
- it 'shows deploy job as created' do
- subject
-
- within('.js-pipeline-header-container') do
- expect(page).to have_content('pending')
- end
-
- within('.js-pipeline-graph') do
- within(all('[data-testid="stage-column"]')[0]) do
- expect(page).to have_content('test')
- expect(page).to have_css('.ci-status-icon-pending')
- end
-
- within(all('[data-testid="stage-column"]')[1]) do
- expect(page).to have_content('deploy')
- expect(page).to have_css('.ci-status-icon-created')
- end
- end
- end
-
- context 'when test job succeeded' do
- before do
- test_job.success!
- end
-
- it 'shows deploy job as pending' do
- subject
-
- within('.js-pipeline-header-container') do
- expect(page).to have_content('running')
- end
-
- within('.js-pipeline-graph') do
- within(all('[data-testid="stage-column"]')[0]) do
- expect(page).to have_content('test')
- expect(page).to have_css('.ci-status-icon-success')
- end
-
- within(all('[data-testid="stage-column"]')[1]) do
- expect(page).to have_content('deploy')
- expect(page).to have_css('.ci-status-icon-pending')
- end
- end
- end
- end
-
- context 'when test job succeeded but there are no available resources' do
- let(:another_job) { create(:ci_build, :running, project: project, resource_group: resource_group) }
-
- before do
- resource_group.assign_resource_to(another_job)
- test_job.success!
- end
-
- it 'shows deploy job as waiting for resource' do
- subject
-
- within('.js-pipeline-header-container') do
- expect(page).to have_content('waiting')
- end
-
- within('.js-pipeline-graph') do
- within(all('[data-testid="stage-column"]')[1]) do
- expect(page).to have_content('deploy')
- expect(page).to have_css('.ci-status-icon-waiting-for-resource')
- end
- end
- end
-
- context 'when resource is released from another job' do
- before do
- another_job.success!
- end
-
- it 'shows deploy job as pending' do
- subject
-
- within('.js-pipeline-header-container') do
- expect(page).to have_content('running')
- end
-
- within('.js-pipeline-graph') do
- within(all('[data-testid="stage-column"]')[1]) do
- expect(page).to have_content('deploy')
- expect(page).to have_css('.ci-status-icon-pending')
- end
- end
- end
- end
-
- context 'when deploy job is a bridge to trigger a downstream pipeline' do
- let!(:deploy_job) do
- create(:ci_bridge, :created,
- stage: 'deploy',
- name: 'deploy',
- stage_idx: 2,
- pipeline: pipeline,
- project: project,
- resource_group: resource_group
- )
- end
-
- it 'shows deploy job as waiting for resource' do
- subject
-
- within('.js-pipeline-header-container') do
- expect(page).to have_content('waiting')
- end
-
- within('.js-pipeline-graph') do
- within(all('[data-testid="stage-column"]')[1]) do
- expect(page).to have_content('deploy')
- expect(page).to have_css('.ci-status-icon-waiting-for-resource')
- end
- end
- end
- end
-
- context 'when deploy job is a bridge to trigger a downstream pipeline' do
- let!(:deploy_job) do
- create(:ci_bridge, :created,
- stage: 'deploy',
- name: 'deploy',
- stage_idx: 2,
- pipeline: pipeline,
- project: project,
- resource_group: resource_group
- )
- end
-
- it 'shows deploy job as waiting for resource' do
- subject
-
- within('.js-pipeline-header-container') do
- expect(page).to have_content('waiting')
- end
-
- within('.js-pipeline-graph') do
- within(all('[data-testid="stage-column"]')[1]) do
- expect(page).to have_content('deploy')
- expect(page).to have_css('.ci-status-icon-waiting-for-resource')
- end
- end
- end
- end
- end
- end
- end
-
- describe 'GET /:project/-/pipelines/:id/dag' do
- include_context 'pipeline builds'
-
- let_it_be(:project) { create(:project, :repository) }
-
- let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
-
- before do
- visit dag_project_pipeline_path(project, pipeline)
- end
-
- it 'shows DAG tab pane as active' do
- expect(page).to have_css('#js-tab-dag.active', visible: false)
- end
-
- context 'page tabs' do
- it 'shows Pipeline, Jobs and DAG tabs with link' do
- expect(page).to have_link('Pipeline')
- expect(page).to have_link('Jobs')
- expect(page).to have_link('DAG')
- end
-
- it 'shows counter in Jobs tab' do
- expect(page.find('.js-builds-counter').text).to eq(pipeline.total_size.to_s)
- end
-
- it 'shows DAG tab as active' do
- expect(page).to have_css('li.js-dag-tab-link .active')
- end
- end
- end
-
- context 'when user sees pipeline flags in a pipeline detail page' do
- let_it_be(:project) { create(:project, :repository) }
-
- context 'when pipeline is latest' do
- include_context 'pipeline builds'
-
- let(:pipeline) do
- create(:ci_pipeline,
- project: project,
- ref: 'master',
- sha: project.commit.id,
- user: user)
- end
-
- before do
- visit project_pipeline_path(project, pipeline)
- end
-
- it 'contains badge that indicates it is the latest build' do
- page.within(all('.well-segment')[1]) do
- expect(page).to have_content 'latest'
- end
- end
- end
-
- context 'when pipeline has configuration errors' do
- let(:pipeline) do
- create(:ci_pipeline,
- :invalid,
- project: project,
- ref: 'master',
- sha: project.commit.id,
- user: user)
- end
-
- before do
- visit project_pipeline_path(project, pipeline)
- end
-
- it 'contains badge that indicates errors' do
- page.within(all('.well-segment')[1]) do
- expect(page).to have_content 'yaml invalid'
- end
- end
-
- it 'contains badge with tooltip which contains error' do
- expect(pipeline).to have_yaml_errors
-
- page.within(all('.well-segment')[1]) do
- expect(page).to have_selector(
- %Q{span[title="#{pipeline.yaml_errors}"]})
- end
- end
-
- it 'contains badge that indicates failure reason' do
- expect(page).to have_content 'error'
- end
-
- it 'contains badge with tooltip which contains failure reason' do
- expect(pipeline.failure_reason?).to eq true
-
- page.within(all('.well-segment')[1]) do
- expect(page).to have_selector(
- %Q{span[title="#{pipeline.present.failure_reason}"]})
- end
- end
-
- it 'contains a pipeline header with title' do
- expect(page).to have_content "Pipeline ##{pipeline.id}"
- end
- end
-
- context 'when pipeline is stuck' do
- include_context 'pipeline builds'
-
- let(:pipeline) do
- create(:ci_pipeline,
- project: project,
- ref: 'master',
- sha: project.commit.id,
- user: user)
- end
-
- before do
- create(:ci_build, :pending, pipeline: pipeline)
- visit project_pipeline_path(project, pipeline)
- end
-
- it 'contains badge that indicates being stuck' do
- page.within(all('.well-segment')[1]) do
- expect(page).to have_content 'stuck'
- end
- end
- end
-
- context 'when pipeline uses auto devops' do
- include_context 'pipeline builds'
-
- let(:project) { create(:project, :repository, auto_devops_attributes: { enabled: true }) }
- let(:pipeline) do
- create(:ci_pipeline,
- :auto_devops_source,
- project: project,
- ref: 'master',
- sha: project.commit.id,
- user: user)
- end
-
- before do
- visit project_pipeline_path(project, pipeline)
- end
-
- it 'contains badge that indicates using auto devops' do
- page.within(all('.well-segment')[1]) do
- expect(page).to have_content 'Auto DevOps'
- end
- end
- end
-
- context 'when pipeline runs in a merge request context' do
- include_context 'pipeline builds'
-
- let(:pipeline) do
- create(:ci_pipeline,
- source: :merge_request_event,
- project: merge_request.source_project,
- ref: 'feature',
- sha: merge_request.diff_head_sha,
- user: user,
- merge_request: merge_request)
- end
-
- let(:merge_request) do
- create(:merge_request,
- source_project: project,
- source_branch: 'feature',
- target_project: project,
- target_branch: 'master')
- end
-
- before do
- visit project_pipeline_path(project, pipeline)
- end
-
- it 'contains badge that indicates detached merge request pipeline' do
- page.within(all('.well-segment')[1]) do
- expect(page).to have_content 'merge request'
- end
- end
- end
- end
-
- describe 'GET /:project/-/pipelines/:id/builds' do
- include_context 'pipeline builds'
-
- let_it_be(:project) { create(:project, :repository) }
-
- let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
-
- before do
- visit builds_project_pipeline_path(project, pipeline)
- end
-
- it 'shows a list of jobs' do
- expect(page).to have_content('Test')
- expect(page).to have_content(build_passed.id)
- expect(page).to have_content('Deploy')
- expect(page).to have_content(build_failed.id)
- expect(page).to have_content(build_running.id)
- expect(page).to have_content(build_external.id)
- expect(page).to have_content('Retry')
- expect(page).to have_content('Cancel running')
- expect(page).to have_button('Play')
- end
-
- context 'page tabs' do
- it 'shows Pipeline, Jobs and DAG tabs with link' do
- expect(page).to have_link('Pipeline')
- expect(page).to have_link('Jobs')
- expect(page).to have_link('Needs')
- end
-
- it 'shows counter in Jobs tab' do
- expect(page.find('.js-builds-counter').text).to eq(pipeline.total_size.to_s)
- end
- end
-
- context 'retrying jobs' do
- it { expect(page).not_to have_content('retried') }
-
- context 'when retrying' do
- before do
- find('[data-testid="retry"]', match: :first).click
- end
-
- it 'does not show a "Retry" button', :sidekiq_might_not_need_inline do
- expect(page).not_to have_content('Retry')
- end
- end
- end
-
- context 'canceling jobs' do
- it { expect(page).not_to have_selector('.ci-canceled') }
-
- context 'when canceling' do
- before do
- click_on 'Cancel running'
- end
-
- it 'does not show a "Cancel running" button', :sidekiq_might_not_need_inline do
- expect(page).not_to have_content('Cancel running')
- end
- end
- end
-
- context 'playing manual job' do
- before do
- within '[data-testid="jobs-tab-table"]' do
- click_button('Play')
-
- wait_for_requests
- end
- end
-
- it { expect(build_manual.reload).to be_pending }
- end
-
- context 'when user unschedules a delayed job' do
- before do
- within '[data-testid="jobs-tab-table"]' do
- click_button('Unschedule')
- end
- end
-
- it 'unschedules the delayed job and shows play button as a manual job' do
- expect(page).to have_button('Play')
- expect(page).not_to have_button('Unschedule')
- end
- end
- end
-
- describe 'GET /:project/-/pipelines/:id/failures' do
- let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: '1234') }
- let(:pipeline_failures_page) { failures_project_pipeline_path(project, pipeline) }
- let!(:failed_build) { create(:ci_build, :failed, pipeline: pipeline) }
-
- subject { visit pipeline_failures_page }
-
- context 'with failed build' do
- before do
- failed_build.trace.set('4 examples, 1 failure')
- end
-
- it 'lists failed builds' do
- subject
-
- expect(page).to have_content(failed_build.name)
- expect(page).to have_content(failed_build.stage_name)
- end
-
- it 'shows build failure logs' do
- subject
-
- expect(page).to have_content('4 examples, 1 failure')
- end
-
- it 'shows the failure reason' do
- subject
-
- expect(page).to have_content('There is an unknown failure, please try again')
- end
-
- context 'when user does not have permission to retry build' do
- it 'shows retry button for failed build' do
- subject
-
- page.within(find('#js-tab-failures', match: :first)) do
- expect(page).not_to have_button('Retry')
- end
- end
- end
-
- context 'when user does have permission to retry build' do
- before do
- create(:protected_branch, :developers_can_merge,
- name: pipeline.ref, project: project)
- end
-
- it 'shows retry button for failed build' do
- subject
-
- page.within(find('#js-tab-failures', match: :first)) do
- expect(page).to have_button('Retry')
- end
- end
- end
- end
-
- context 'when missing build logs' do
- it 'lists failed builds' do
- subject
-
- expect(page).to have_content(failed_build.name)
- expect(page).to have_content(failed_build.stage_name)
- end
-
- it 'does not show log' do
- subject
-
- expect(page).to have_content('No job log')
- end
- end
-
- context 'without permission to access builds' do
- let(:role) { :guest }
-
- before do
- project.update!(public_builds: false)
- end
-
- context 'when accessing failed jobs page' do
- it 'renders a 404 page' do
- requests = inspect_requests { subject }
-
- expect(page).to have_title('Not Found')
- expect(requests.first.status_code).to eq(404)
- end
- end
- end
-
- context 'without failures' do
- before do
- failed_build.update!(status: :success)
- end
-
- it 'does not show the failure tab' do
- subject
-
- expect(page).not_to have_content('Failed Jobs')
- end
-
- it 'displays the pipeline graph' do
- subject
-
- expect(page).to have_current_path(pipeline_path(pipeline), ignore_query: true)
- expect(page).to have_selector('.js-pipeline-graph')
- end
- end
- end
-end
diff --git a/spec/features/projects/pipelines/legacy_pipelines_spec.rb b/spec/features/projects/pipelines/legacy_pipelines_spec.rb
deleted file mode 100644
index 9d3ac71a875..00000000000
--- a/spec/features/projects/pipelines/legacy_pipelines_spec.rb
+++ /dev/null
@@ -1,852 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Pipelines', :js do
- include ProjectForksHelper
- include Spec::Support::Helpers::ModalHelpers
-
- let(:project) { create(:project) }
- let(:expected_detached_mr_tag) { 'merge request' }
-
- context 'when user is logged in' do
- let(:user) { create(:user) }
-
- before do
- sign_in(user)
-
- project.add_developer(user)
- project.update!(auto_devops_attributes: { enabled: false })
-
- stub_feature_flags(pipeline_tabs_vue: false)
- end
-
- describe 'GET /:project/-/pipelines' do
- let(:project) { create(:project, :repository) }
-
- let!(:pipeline) do
- create(
- :ci_empty_pipeline,
- project: project,
- ref: 'master',
- status: 'running',
- sha: project.commit.id
- )
- end
-
- context 'scope' do
- before do
- create(:ci_empty_pipeline, status: 'pending', project: project, sha: project.commit.id, ref: 'master')
- create(:ci_empty_pipeline, status: 'running', project: project, sha: project.commit.id, ref: 'master')
- create(:ci_empty_pipeline, status: 'created', project: project, sha: project.commit.id, ref: 'master')
- create(:ci_empty_pipeline, status: 'success', project: project, sha: project.commit.id, ref: 'master')
- end
-
- [:all, :running, :pending, :finished, :branches].each do |scope|
- context "when displaying #{scope}" do
- before do
- visit_project_pipelines(scope: scope)
- end
-
- it 'contains pipeline commit short SHA' do
- expect(page).to have_content(pipeline.short_sha)
- end
-
- it 'contains branch name' do
- expect(page).to have_content(pipeline.ref)
- end
- end
- end
- end
-
- context 'header tabs' do
- before do
- visit project_pipelines_path(project)
- wait_for_requests
- end
-
- it 'shows a tab for All pipelines and count' do
- expect(page.find('.js-pipelines-tab-all').text).to include('All')
- expect(page.find('.js-pipelines-tab-all .badge').text).to include('1')
- end
-
- it 'shows a tab for Finished pipelines and count' do
- expect(page.find('.js-pipelines-tab-finished').text).to include('Finished')
- end
-
- it 'shows a tab for Branches' do
- expect(page.find('.js-pipelines-tab-branches').text).to include('Branches')
- end
-
- it 'shows a tab for Tags' do
- expect(page.find('.js-pipelines-tab-tags').text).to include('Tags')
- end
-
- it 'updates content when tab is clicked' do
- page.find('.js-pipelines-tab-finished').click
- wait_for_requests
- expect(page).to have_content('There are currently no finished pipelines.')
- end
- end
-
- context 'navigation links' do
- before do
- visit project_pipelines_path(project)
- wait_for_requests
- end
-
- it 'renders "CI lint" link' do
- expect(page).to have_link('CI lint')
- end
-
- it 'renders "Run pipeline" link' do
- expect(page).to have_link('Run pipeline')
- end
- end
-
- context 'when pipeline is cancelable' do
- let!(:build) do
- create(:ci_build, pipeline: pipeline,
- stage: 'test')
- end
-
- before do
- build.run
- visit_project_pipelines
- end
-
- it 'indicates that pipeline can be canceled' do
- expect(page).to have_selector('.js-pipelines-cancel-button')
- expect(page).to have_selector('.ci-running')
- end
-
- context 'when canceling' do
- before do
- find('.js-pipelines-cancel-button').click
- click_button 'Stop pipeline'
- wait_for_requests
- end
-
- it 'indicated that pipelines was canceled', :sidekiq_might_not_need_inline do
- expect(page).not_to have_selector('.js-pipelines-cancel-button')
- expect(page).to have_selector('.ci-canceled')
- end
- end
- end
-
- context 'when pipeline is retryable', :sidekiq_might_not_need_inline do
- let!(:build) do
- create(:ci_build, pipeline: pipeline,
- stage: 'test')
- end
-
- before do
- build.drop
- visit_project_pipelines
- end
-
- it 'indicates that pipeline can be retried' do
- expect(page).to have_selector('.js-pipelines-retry-button')
- expect(page).to have_selector('.ci-failed')
- end
-
- context 'when retrying' do
- before do
- find('.js-pipelines-retry-button').click
- wait_for_requests
- end
-
- it 'shows running pipeline that is not retryable' do
- expect(page).not_to have_selector('.js-pipelines-retry-button')
- expect(page).to have_selector('.ci-running')
- end
- end
- end
-
- context 'when pipeline is detached merge request pipeline' do
- let(:merge_request) do
- create(:merge_request,
- :with_detached_merge_request_pipeline,
- source_project: source_project,
- target_project: target_project)
- end
-
- let!(:pipeline) { merge_request.all_pipelines.first }
- let(:source_project) { project }
- let(:target_project) { project }
-
- before do
- visit project_pipelines_path(source_project)
- end
-
- shared_examples_for 'detached merge request pipeline' do
- it 'shows pipeline information without pipeline ref', :sidekiq_might_not_need_inline do
- within '.pipeline-tags' do
- expect(page).to have_content(expected_detached_mr_tag)
-
- expect(page).to have_link(merge_request.iid,
- href: project_merge_request_path(project, merge_request))
-
- expect(page).not_to have_link(pipeline.ref)
- end
- end
- end
-
- it_behaves_like 'detached merge request pipeline'
-
- context 'when source project is a forked project' do
- let(:source_project) { fork_project(project, user, repository: true) }
-
- it_behaves_like 'detached merge request pipeline'
- end
- end
-
- context 'when pipeline is merge request pipeline' do
- let(:merge_request) do
- create(:merge_request,
- :with_merge_request_pipeline,
- source_project: source_project,
- target_project: target_project,
- merge_sha: target_project.commit.sha)
- end
-
- let!(:pipeline) { merge_request.all_pipelines.first }
- let(:source_project) { project }
- let(:target_project) { project }
-
- before do
- visit project_pipelines_path(source_project)
- end
-
- shared_examples_for 'Correct merge request pipeline information' do
- it 'does not show detached tag for the pipeline, and shows the link of the merge request' \
- 'and does not show the ref of the pipeline', :sidekiq_might_not_need_inline do
- within '.pipeline-tags' do
- expect(page).not_to have_content(expected_detached_mr_tag)
-
- expect(page).to have_link(merge_request.iid,
- href: project_merge_request_path(project, merge_request))
-
- expect(page).not_to have_link(pipeline.ref)
- end
- end
- end
-
- it_behaves_like 'Correct merge request pipeline information'
-
- context 'when source project is a forked project' do
- let(:source_project) { fork_project(project, user, repository: true) }
-
- it_behaves_like 'Correct merge request pipeline information'
- end
- end
-
- context 'when pipeline has configuration errors' do
- let(:pipeline) do
- create(:ci_pipeline, :invalid, project: project)
- end
-
- before do
- visit_project_pipelines
- end
-
- it 'contains badge that indicates errors' do
- expect(page).to have_content 'yaml invalid'
- end
-
- it 'contains badge with tooltip which contains error' do
- expect(pipeline).to have_yaml_errors
- expect(page).to have_selector(
- %Q{span[title="#{pipeline.yaml_errors}"]})
- end
-
- it 'contains badge that indicates failure reason' do
- expect(page).to have_content 'error'
- end
-
- it 'contains badge with tooltip which contains failure reason' do
- expect(pipeline.failure_reason?).to eq true
- expect(page).to have_selector(
- %Q{span[title="#{pipeline.present.failure_reason}"]})
- end
- end
-
- context 'with manual actions' do
- let!(:manual) do
- create(:ci_build, :manual,
- pipeline: pipeline,
- name: 'manual build',
- stage: 'test')
- end
-
- before do
- visit_project_pipelines
- end
-
- it 'has a dropdown with play button' do
- expect(page).to have_selector('[data-testid="pipelines-manual-actions-dropdown"] [data-testid="play-icon"]')
- end
-
- it 'has link to the manual action' do
- find('[data-testid="pipelines-manual-actions-dropdown"]').click
-
- expect(page).to have_button('manual build')
- end
-
- context 'when manual action was played' do
- before do
- find('[data-testid="pipelines-manual-actions-dropdown"]').click
- click_button('manual build')
- end
-
- it 'enqueues manual action job' do
- expect(page).to have_selector(
- '[data-testid="pipelines-manual-actions-dropdown"] .gl-dropdown-toggle:disabled'
- )
- end
- end
- end
-
- context 'when there is a delayed job' do
- let!(:delayed_job) do
- create(:ci_build, :scheduled,
- pipeline: pipeline,
- name: 'delayed job 1',
- stage: 'test')
- end
-
- before do
- visit_project_pipelines
- end
-
- it 'has a dropdown for actionable jobs' do
- expect(page).to have_selector('[data-testid="pipelines-manual-actions-dropdown"] [data-testid="play-icon"]')
- end
-
- it "has link to the delayed job's action" do
- find('[data-testid="pipelines-manual-actions-dropdown"]').click
-
- time_diff = [0, delayed_job.scheduled_at - Time.zone.now].max
- expect(page).to have_button('delayed job 1')
- expect(page).to have_content(Time.at(time_diff).utc.strftime("%H:%M:%S"))
- end
-
- context 'when delayed job is expired already' do
- let!(:delayed_job) do
- create(:ci_build, :expired_scheduled,
- pipeline: pipeline,
- name: 'delayed job 1',
- stage: 'test')
- end
-
- it "shows 00:00:00 as the remaining time" do
- find('[data-testid="pipelines-manual-actions-dropdown"]').click
-
- expect(page).to have_content("00:00:00")
- end
- end
-
- context 'when user played a delayed job immediately' do
- before do
- find('[data-testid="pipelines-manual-actions-dropdown"]').click
- accept_gl_confirm do
- click_button 'delayed job 1'
- end
- wait_for_requests
- end
-
- it 'enqueues the delayed job', :js do
- find('[data-testid="mini-pipeline-graph-dropdown"]').click
-
- within('[data-testid="mini-pipeline-graph-dropdown"]') { find('.ci-status-icon-pending') }
-
- expect(delayed_job.reload).to be_pending
- end
- end
- end
-
- context 'for generic statuses' do
- context 'when preparing' do
- let!(:pipeline) do
- create(:ci_empty_pipeline,
- status: 'preparing', project: project)
- end
-
- let!(:status) do
- create(:generic_commit_status,
- :preparing, pipeline: pipeline)
- end
-
- before do
- visit_project_pipelines
- end
-
- it 'is cancelable' do
- expect(page).to have_selector('.js-pipelines-cancel-button')
- end
-
- it 'shows the pipeline as preparing' do
- expect(page).to have_selector('.ci-preparing')
- end
- end
-
- context 'when running' do
- let!(:running) do
- create(:generic_commit_status,
- status: 'running',
- pipeline: pipeline,
- stage: 'test')
- end
-
- before do
- visit_project_pipelines
- end
-
- it 'is cancelable' do
- expect(page).to have_selector('.js-pipelines-cancel-button')
- end
-
- it 'has pipeline running' do
- expect(page).to have_selector('.ci-running')
- end
-
- context 'when canceling' do
- before do
- find('.js-pipelines-cancel-button').click
- click_button 'Stop pipeline'
- end
-
- it 'indicates that pipeline was canceled', :sidekiq_might_not_need_inline do
- expect(page).not_to have_selector('.js-pipelines-cancel-button')
- expect(page).to have_selector('.ci-canceled')
- end
- end
- end
-
- context 'when failed' do
- let!(:status) do
- create(:generic_commit_status, :pending,
- pipeline: pipeline,
- stage: 'test')
- end
-
- before do
- status.drop
- visit_project_pipelines
- end
-
- it 'is not retryable' do
- expect(page).not_to have_selector('.js-pipelines-retry-button')
- end
-
- it 'has failed pipeline', :sidekiq_might_not_need_inline do
- expect(page).to have_selector('.ci-failed')
- end
- end
- end
-
- context 'downloadable pipelines' do
- context 'with artifacts' do
- let!(:with_artifacts) do
- build = create(:ci_build, :success,
- pipeline: pipeline,
- name: 'rspec tests',
- stage: 'test')
-
- create(:ci_job_artifact, :codequality, job: build)
- end
-
- before do
- visit_project_pipelines
- end
-
- it 'has artifacts dropdown' do
- expect(page).to have_selector('[data-testid="pipeline-multi-actions-dropdown"]')
- end
- end
-
- context 'with artifacts expired' do
- let!(:with_artifacts_expired) do
- create(:ci_build, :expired, :success,
- pipeline: pipeline,
- name: 'rspec',
- stage: 'test')
- end
-
- before do
- visit_project_pipelines
- end
-
- it { expect(page).not_to have_selector('[data-testid="artifact-item"]') }
- end
-
- context 'without artifacts' do
- let!(:without_artifacts) do
- create(:ci_build, :success,
- pipeline: pipeline,
- name: 'rspec',
- stage: 'test')
- end
-
- before do
- visit_project_pipelines
- end
-
- it { expect(page).not_to have_selector('[data-testid="artifact-item"]') }
- end
-
- context 'with trace artifact' do
- before do
- create(:ci_build, :success, :trace_artifact, pipeline: pipeline)
-
- visit_project_pipelines
- end
-
- it 'does not show trace artifact as artifacts' do
- expect(page).not_to have_selector('[data-testid="artifact-item"]')
- end
- end
- end
-
- context 'mini pipeline graph' do
- let!(:build) do
- create(:ci_build, :pending, pipeline: pipeline,
- stage: 'build',
- name: 'build')
- end
-
- dropdown_selector = '[data-testid="mini-pipeline-graph-dropdown"]'
-
- before do
- visit_project_pipelines
- end
-
- it 'renders a mini pipeline graph' do
- expect(page).to have_selector('[data-testid="pipeline-mini-graph"]')
- expect(page).to have_selector(dropdown_selector)
- end
-
- context 'when clicking a stage badge' do
- it 'opens a dropdown' do
- find(dropdown_selector).click
-
- expect(page).to have_link build.name
- end
-
- it 'is possible to cancel pending build' do
- find(dropdown_selector).click
- find('.js-ci-action').click
- wait_for_requests
-
- expect(build.reload).to be_canceled
- end
- end
-
- context 'for a failed pipeline' do
- let!(:build) do
- create(:ci_build, :failed, pipeline: pipeline,
- stage: 'build',
- name: 'build')
- end
-
- it 'displays the failure reason' do
- find(dropdown_selector).click
-
- within('.js-builds-dropdown-list') do
- build_element = page.find('.mini-pipeline-graph-dropdown-item')
- expect(build_element['title']).to eq('build - failed - (unknown failure)')
- end
- end
- end
- end
-
- context 'with pagination' do
- before do
- allow(Ci::Pipeline).to receive(:default_per_page).and_return(1)
- create(:ci_empty_pipeline, project: project)
- end
-
- it 'renders pagination' do
- visit project_pipelines_path(project)
- wait_for_requests
-
- expect(page).to have_selector('.gl-pagination')
- end
-
- it 'renders second page of pipelines' do
- visit project_pipelines_path(project, page: '2')
- wait_for_requests
-
- expect(page).to have_selector('.gl-pagination .page-link', count: 4)
- end
-
- it 'shows updated content' do
- visit project_pipelines_path(project)
- wait_for_requests
- page.find('.page-link.next-page-item').click
-
- expect(page).to have_selector('.gl-pagination .page-link', count: 4)
- end
- end
-
- context 'with pipeline key selection' do
- before do
- visit project_pipelines_path(project)
- wait_for_requests
- end
-
- it 'changes the Pipeline ID column for Pipeline IID' do
- page.find('[data-testid="pipeline-key-dropdown"]').click
-
- within '.gl-new-dropdown-contents' do
- dropdown_options = page.find_all '.gl-new-dropdown-item'
-
- dropdown_options[1].click
- end
-
- expect(page.find('[data-testid="pipeline-th"]')).to have_content 'Pipeline'
- expect(page.find('[data-testid="pipeline-url-link"]')).to have_content "##{pipeline.iid}"
- end
- end
- end
-
- describe 'GET /:project/-/pipelines/show' do
- let(:project) { create(:project, :repository) }
-
- let(:pipeline) do
- create(:ci_empty_pipeline,
- project: project,
- sha: project.commit.id,
- user: user)
- end
-
- before do
- create_build('build', 0, 'build', :success)
- create_build('test', 1, 'rspec 0:2', :pending)
- create_build('test', 1, 'rspec 1:2', :running)
- create_build('test', 1, 'spinach 0:2', :created)
- create_build('test', 1, 'spinach 1:2', :created)
- create_build('test', 1, 'audit', :created)
- create_build('deploy', 2, 'production', :created)
-
- create(
- :generic_commit_status,
- pipeline: pipeline,
- stage: 'external',
- name: 'jenkins',
- stage_idx: 3,
- ref: 'master'
- )
-
- visit project_pipeline_path(project, pipeline)
- wait_for_requests
- end
-
- it 'shows a graph with grouped stages' do
- expect(page).to have_css('.js-pipeline-graph')
-
- # header
- expect(page).to have_text("##{pipeline.id}")
- expect(page).to have_selector(%Q(img[src="#{pipeline.user.avatar_url}"]))
- expect(page).to have_link(pipeline.user.name, href: user_path(pipeline.user))
-
- # stages
- expect(page).to have_text('build')
- expect(page).to have_text('test')
- expect(page).to have_text('deploy')
- expect(page).to have_text('external')
-
- # builds
- expect(page).to have_text('rspec')
- expect(page).to have_text('spinach')
- expect(page).to have_text('rspec')
- expect(page).to have_text('production')
- expect(page).to have_text('jenkins')
- end
-
- def create_build(stage, stage_idx, name, status)
- create(:ci_build, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name, status: status)
- end
- end
-
- describe 'POST /:project/-/pipelines' do
- let(:project) { create(:project, :repository) }
-
- before do
- stub_feature_flags(run_pipeline_graphql: false)
- visit new_project_pipeline_path(project)
- end
-
- context 'for valid commit', :js do
- before do
- click_button project.default_branch
- wait_for_requests
-
- find('p', text: 'master').click
- wait_for_requests
- end
-
- context 'with gitlab-ci.yml', :js do
- before do
- stub_ci_pipeline_to_return_yaml_file
- end
-
- it 'creates a new pipeline' do
- expect do
- click_on 'Run pipeline'
- wait_for_requests
- end
- .to change { Ci::Pipeline.count }.by(1)
-
- expect(Ci::Pipeline.last).to be_web
- end
-
- context 'when variables are specified' do
- it 'creates a new pipeline with variables' do
- page.within(find("[data-testid='ci-variable-row']")) do
- find("[data-testid='pipeline-form-ci-variable-key']").set('key_name')
- find("[data-testid='pipeline-form-ci-variable-value']").set('value')
- end
-
- expect do
- click_on 'Run pipeline'
- wait_for_requests
- end
- .to change { Ci::Pipeline.count }.by(1)
-
- expect(Ci::Pipeline.last.variables.map { |var| var.slice(:key, :secret_value) })
- .to eq [{ key: "key_name", secret_value: "value" }.with_indifferent_access]
- end
- end
- end
-
- context 'without gitlab-ci.yml' do
- before do
- click_on 'Run pipeline'
- wait_for_requests
- end
-
- it { expect(page).to have_content('Missing CI config file') }
-
- it 'creates a pipeline after first request failed and a valid gitlab-ci.yml file' \
- 'is available when trying again' do
- stub_ci_pipeline_to_return_yaml_file
-
- expect do
- click_on 'Run pipeline'
- wait_for_requests
- end
- .to change { Ci::Pipeline.count }.by(1)
- end
- end
- end
- end
-
- describe 'Reset runner caches' do
- let(:project) { create(:project, :repository) }
-
- before do
- create(:ci_empty_pipeline, status: 'success', project: project, sha: project.commit.id, ref: 'master')
- project.add_maintainer(user)
- visit project_pipelines_path(project)
- end
-
- it 'has a clear caches button' do
- expect(page).to have_button 'Clear runner caches'
- end
-
- describe 'user clicks the button' do
- context 'when project already has jobs_cache_index' do
- before do
- project.update!(jobs_cache_index: 1)
- end
-
- it 'increments jobs_cache_index' do
- click_button 'Clear runner caches'
- wait_for_requests
- expect(page.find('[data-testid="alert-info"]')).to have_content 'Project cache successfully reset.'
- end
- end
-
- context 'when project does not have jobs_cache_index' do
- it 'sets jobs_cache_index to 1' do
- click_button 'Clear runner caches'
- wait_for_requests
- expect(page.find('[data-testid="alert-info"]')).to have_content 'Project cache successfully reset.'
- end
- end
- end
- end
-
- describe 'Run Pipelines' do
- let(:project) { create(:project, :repository) }
-
- before do
- visit new_project_pipeline_path(project)
- end
-
- describe 'new pipeline page' do
- it 'has field to add a new pipeline' do
- expect(page).to have_selector('[data-testid="ref-select"]')
- expect(find('[data-testid="ref-select"]')).to have_content project.default_branch
- expect(page).to have_content('Run for')
- end
- end
-
- describe 'find pipelines' do
- it 'shows filtered pipelines', :js do
- click_button project.default_branch
-
- page.within '[data-testid="ref-select"]' do
- find('[data-testid="search-refs"]').native.send_keys('fix')
-
- page.within '.gl-new-dropdown-contents' do
- expect(page).to have_content('fix')
- end
- end
- end
- end
- end
-
- describe 'Empty State' do
- let(:project) { create(:project, :repository) }
-
- before do
- visit project_pipelines_path(project)
- end
-
- it 'renders empty state' do
- expect(page).to have_content 'Try test template'
- end
- end
- end
-
- context 'when user is not logged in' do
- before do
- project.update!(auto_devops_attributes: { enabled: false })
- visit project_pipelines_path(project)
- end
-
- context 'when project is public' do
- let(:project) { create(:project, :public, :repository) }
-
- context 'without pipelines' do
- it { expect(page).to have_content 'This project is not currently set up to run pipelines.' }
- end
- end
-
- context 'when project is private' do
- let(:project) { create(:project, :private, :repository) }
-
- it 'redirects the user to sign_in and displays the flash alert' do
- expect(page).to have_content 'You need to sign in'
- expect(page).to have_current_path("/users/sign_in")
- end
- end
- end
-
- def visit_project_pipelines(**query)
- visit project_pipelines_path(project, query)
- wait_for_requests
- end
-end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 2d729af513a..d6067e22952 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Pipeline', :js do
+RSpec.describe 'Pipeline', :js, feature_category: :projects do
include RoutesHelpers
include ProjectForksHelper
include ::ExclusiveLeaseHelpers
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index b7b715cb6db..3bdabd672c7 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Pipelines', :js do
+RSpec.describe 'Pipelines', :js, feature_category: :projects do
include ProjectForksHelper
include Spec::Support::Helpers::ModalHelpers
@@ -596,8 +596,8 @@ RSpec.describe 'Pipelines', :js do
it 'changes the Pipeline ID column for Pipeline IID' do
page.find('[data-testid="pipeline-key-dropdown"]').click
- within '.gl-new-dropdown-contents' do
- dropdown_options = page.find_all '.gl-new-dropdown-item'
+ within '.gl-dropdown-contents' do
+ dropdown_options = page.find_all '.gl-dropdown-item'
dropdown_options[1].click
end
@@ -663,7 +663,19 @@ RSpec.describe 'Pipelines', :js do
describe 'POST /:project/-/pipelines' do
let(:project) { create(:project, :repository) }
- shared_examples 'run pipeline form with gitlab-ci.yml' do
+ before do
+ visit new_project_pipeline_path(project)
+ end
+
+ context 'for valid commit', :js do
+ before do
+ click_button project.default_branch
+ wait_for_requests
+
+ find('p', text: 'master').click
+ wait_for_requests
+ end
+
context 'with gitlab-ci.yml', :js do
before do
stub_ci_pipeline_to_return_yaml_file
@@ -680,7 +692,7 @@ RSpec.describe 'Pipelines', :js do
end
context 'when variables are specified' do
- it 'creates a new pipeline with variables' do
+ it 'creates a new pipeline with variables', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/375552' do
page.within(find("[data-testid='ci-variable-row']")) do
find("[data-testid='pipeline-form-ci-variable-key']").set('key_name')
find("[data-testid='pipeline-form-ci-variable-value']").set('value')
@@ -697,9 +709,7 @@ RSpec.describe 'Pipelines', :js do
end
end
end
- end
- shared_examples 'run pipeline form without gitlab-ci.yml' do
context 'without gitlab-ci.yml' do
before do
click_on 'Run pipeline'
@@ -708,7 +718,7 @@ RSpec.describe 'Pipelines', :js do
it { expect(page).to have_content('Missing CI config file') }
- it 'creates a pipeline after first request failed and a valid gitlab-ci.yml file is available when trying again' do
+ it 'creates a pipeline after first request failed and a valid gitlab-ci.yml file is available when trying again', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/375552' do
stub_ci_pipeline_to_return_yaml_file
expect do
@@ -719,52 +729,6 @@ RSpec.describe 'Pipelines', :js do
end
end
end
-
- # Run Pipeline form with REST endpoints
- # TODO: Clean up tests when run_pipeline_graphql is enabled
- # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/372310
- context 'with feature flag disabled' do
- before do
- stub_feature_flags(run_pipeline_graphql: false)
- visit new_project_pipeline_path(project)
- end
-
- context 'for valid commit', :js do
- before do
- click_button project.default_branch
- wait_for_requests
-
- find('p', text: 'master').click
- wait_for_requests
- end
-
- it_behaves_like 'run pipeline form with gitlab-ci.yml'
-
- it_behaves_like 'run pipeline form without gitlab-ci.yml'
- end
- end
-
- # Run Pipeline form with GraphQL
- context 'with feature flag enabled' do
- before do
- stub_feature_flags(run_pipeline_graphql: true)
- visit new_project_pipeline_path(project)
- end
-
- context 'for valid commit', :js do
- before do
- click_button project.default_branch
- wait_for_requests
-
- find('p', text: 'master').click
- wait_for_requests
- end
-
- it_behaves_like 'run pipeline form with gitlab-ci.yml'
-
- it_behaves_like 'run pipeline form without gitlab-ci.yml'
- end
- end
end
describe 'Reset runner caches' do
@@ -825,7 +789,7 @@ RSpec.describe 'Pipelines', :js do
page.within '[data-testid="ref-select"]' do
find('[data-testid="search-refs"]').native.send_keys('fix')
- page.within '.gl-new-dropdown-contents' do
+ page.within '.gl-dropdown-contents' do
expect(page).to have_content('fix')
end
end
diff --git a/spec/features/projects/raw/user_interacts_with_raw_endpoint_spec.rb b/spec/features/projects/raw/user_interacts_with_raw_endpoint_spec.rb
index 6745eb1a3fb..fb7814285b8 100644
--- a/spec/features/projects/raw/user_interacts_with_raw_endpoint_spec.rb
+++ b/spec/features/projects/raw/user_interacts_with_raw_endpoint_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Raw > User interacts with raw endpoint' do
+RSpec.describe 'Projects > Raw > User interacts with raw endpoint', feature_category: :projects do
include RepoHelpers
let(:user) { create(:user) }
diff --git a/spec/features/projects/releases/user_creates_release_spec.rb b/spec/features/projects/releases/user_creates_release_spec.rb
index 4eb7581222e..f678d77b002 100644
--- a/spec/features/projects/releases/user_creates_release_spec.rb
+++ b/spec/features/projects/releases/user_creates_release_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User creates release', :js do
+RSpec.describe 'User creates release', :js, feature_category: :continuous_delivery do
include Spec::Support::Helpers::Features::ReleasesHelpers
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/features/projects/releases/user_views_edit_release_spec.rb b/spec/features/projects/releases/user_views_edit_release_spec.rb
index 78b9798941a..ef3b35837ff 100644
--- a/spec/features/projects/releases/user_views_edit_release_spec.rb
+++ b/spec/features/projects/releases/user_views_edit_release_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User edits Release', :js do
+RSpec.describe 'User edits Release', :js, feature_category: :continuous_delivery do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
diff --git a/spec/features/projects/releases/user_views_release_spec.rb b/spec/features/projects/releases/user_views_release_spec.rb
index 4410f345e56..efa0ebd761d 100644
--- a/spec/features/projects/releases/user_views_release_spec.rb
+++ b/spec/features/projects/releases/user_views_release_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views Release', :js do
+RSpec.describe 'User views Release', :js, feature_category: :continuous_delivery do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
diff --git a/spec/features/projects/releases/user_views_releases_spec.rb b/spec/features/projects/releases/user_views_releases_spec.rb
index 10418e8072d..13dde57d885 100644
--- a/spec/features/projects/releases/user_views_releases_spec.rb
+++ b/spec/features/projects/releases/user_views_releases_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views releases', :js do
+RSpec.describe 'User views releases', :js, feature_category: :continuous_delivery do
let_it_be(:today) { Time.zone.now }
let_it_be(:yesterday) { today - 1.day }
let_it_be(:tomorrow) { today + 1.day }
diff --git a/spec/features/projects/remote_mirror_spec.rb b/spec/features/projects/remote_mirror_spec.rb
index 2c8e895d43d..aa0c1ead4c0 100644
--- a/spec/features/projects/remote_mirror_spec.rb
+++ b/spec/features/projects/remote_mirror_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project remote mirror', :feature do
+RSpec.describe 'Project remote mirror', :feature, feature_category: :projects do
let(:project) { create(:project, :repository, :remote_mirror) }
let(:remote_mirror) { project.remote_mirrors.first }
let(:user) { create(:user) }
diff --git a/spec/features/projects/settings/access_tokens_spec.rb b/spec/features/projects/settings/access_tokens_spec.rb
index 88f9a50b093..12e14f5193f 100644
--- a/spec/features/projects/settings/access_tokens_spec.rb
+++ b/spec/features/projects/settings/access_tokens_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project > Settings > Access Tokens', :js do
+RSpec.describe 'Project > Settings > Access Tokens', :js, feature_category: :credential_management do
include Spec::Support::Helpers::ModalHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/projects/settings/branch_names_settings_spec.rb b/spec/features/projects/settings/branch_names_settings_spec.rb
index fdd883bc2b6..5d82dff1efd 100644
--- a/spec/features/projects/settings/branch_names_settings_spec.rb
+++ b/spec/features/projects/settings/branch_names_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project settings > repositories > Branch names', :js do
+RSpec.describe 'Project settings > repositories > Branch names', :js, feature_category: :projects do
let_it_be(:project) { create(:project, :public) }
let(:user) { create(:user) }
diff --git a/spec/features/projects/settings/branch_rules_settings_spec.rb b/spec/features/projects/settings/branch_rules_settings_spec.rb
index 5cc35f108b5..71d9c559b77 100644
--- a/spec/features/projects/settings/branch_rules_settings_spec.rb
+++ b/spec/features/projects/settings/branch_rules_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > Repository > Branch rules settings' do
+RSpec.describe 'Projects > Settings > Repository > Branch rules settings', feature_category: :projects do
let(:project) { create(:project_empty_repo) }
let(:user) { create(:user) }
let(:role) { :developer }
diff --git a/spec/features/projects/settings/external_authorization_service_settings_spec.rb b/spec/features/projects/settings/external_authorization_service_settings_spec.rb
index c236c85b773..a99fd5f9788 100644
--- a/spec/features/projects/settings/external_authorization_service_settings_spec.rb
+++ b/spec/features/projects/settings/external_authorization_service_settings_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > External Authorization Classification Label setting' do
+RSpec.describe 'Projects > Settings > External Authorization Classification Label setting',
+feature_category: :projects do
let(:user) { create(:user) }
let(:project) { create(:project_empty_repo) }
diff --git a/spec/features/projects/settings/forked_project_settings_spec.rb b/spec/features/projects/settings/forked_project_settings_spec.rb
index 04fb6953b51..28d5c080db9 100644
--- a/spec/features/projects/settings/forked_project_settings_spec.rb
+++ b/spec/features/projects/settings/forked_project_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > For a forked project', :js do
+RSpec.describe 'Projects > Settings > For a forked project', :js, feature_category: :projects do
include ProjectForksHelper
let(:user) { create(:user) }
let(:original_project) { create(:project) }
diff --git a/spec/features/projects/settings/lfs_settings_spec.rb b/spec/features/projects/settings/lfs_settings_spec.rb
index 6e1be3c7e51..1695b49830d 100644
--- a/spec/features/projects/settings/lfs_settings_spec.rb
+++ b/spec/features/projects/settings/lfs_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > LFS settings' do
+RSpec.describe 'Projects > Settings > LFS settings', feature_category: :projects do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:role) { :maintainer }
diff --git a/spec/features/projects/settings/merge_requests_settings_spec.rb b/spec/features/projects/settings/merge_requests_settings_spec.rb
index ba84d8b6d1a..ca90817b0a4 100644
--- a/spec/features/projects/settings/merge_requests_settings_spec.rb
+++ b/spec/features/projects/settings/merge_requests_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > Merge requests' do
+RSpec.describe 'Projects > Settings > Merge requests', feature_category: :projects do
include ProjectForksHelper
let(:user) { create(:user) }
diff --git a/spec/features/projects/settings/monitor_settings_spec.rb b/spec/features/projects/settings/monitor_settings_spec.rb
index 871391fbe9c..2cdcf86757e 100644
--- a/spec/features/projects/settings/monitor_settings_spec.rb
+++ b/spec/features/projects/settings/monitor_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > For a forked project', :js do
+RSpec.describe 'Projects > Settings > For a forked project', :js, feature_category: :projects do
let_it_be(:project) { create(:project, :repository, create_templates: :issue) }
let(:user) { project.first_owner }
diff --git a/spec/features/projects/settings/packages_settings_spec.rb b/spec/features/projects/settings/packages_settings_spec.rb
index 1c2b0faa215..4ef17830f81 100644
--- a/spec/features/projects/settings/packages_settings_spec.rb
+++ b/spec/features/projects/settings/packages_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > Packages', :js do
+RSpec.describe 'Projects > Settings > Packages', :js, feature_category: :projects do
let_it_be(:project) { create(:project) }
let(:user) { project.first_owner }
@@ -33,6 +33,10 @@ RSpec.describe 'Projects > Settings > Packages', :js do
it 'displays the packages access level setting' do
expect(page).to have_selector('[data-testid="package-registry-access-level"] > label', text: 'Package registry')
+ expect(page).to have_selector('input[name="package_registry_enabled"]', visible: false)
+ expect(page).to have_selector('input[name="package_registry_enabled"] + button', visible: true)
+ expect(page).to have_selector('input[name="package_registry_api_for_everyone_enabled"]', visible: false)
+ expect(page).to have_selector('input[name="package_registry_api_for_everyone_enabled"] + button', visible: true)
expect(page).to have_selector(
'input[name="project[project_feature_attributes][package_registry_access_level]"]',
visible: false
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index a64f81430d1..37973c9b8d6 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Projects > Settings > Pipelines settings" do
+RSpec.describe "Projects > Settings > Pipelines settings", feature_category: :projects do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:role) { :developer }
@@ -165,7 +165,7 @@ RSpec.describe "Projects > Settings > Pipelines settings" do
let(:page_token) { find('#registration_token').text }
before do
- click_button 'Reset registration token'
+ click_link 'Reset registration token'
end
it 'changes registration token' do
diff --git a/spec/features/projects/settings/project_badges_spec.rb b/spec/features/projects/settings/project_badges_spec.rb
index 2c26168e3c0..f4c2265c2c2 100644
--- a/spec/features/projects/settings/project_badges_spec.rb
+++ b/spec/features/projects/settings/project_badges_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project Badges' do
+RSpec.describe 'Project Badges', feature_category: :projects do
include WaitForRequests
let(:user) { create(:user) }
diff --git a/spec/features/projects/settings/project_settings_spec.rb b/spec/features/projects/settings/project_settings_spec.rb
index a0d44b579a8..46a41cfc6f1 100644
--- a/spec/features/projects/settings/project_settings_spec.rb
+++ b/spec/features/projects/settings/project_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects settings' do
+RSpec.describe 'Projects settings', feature_category: :projects do
let_it_be(:project) { create(:project) }
let(:user) { project.first_owner }
diff --git a/spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb b/spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb
index 477c4c2e1ba..d4c1fe4d43e 100644
--- a/spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb
+++ b/spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Project > Settings > Packages and registries > Container registry tag expiration policy' do
+RSpec.describe 'Project > Settings > Packages and registries > Container registry tag expiration policy',
+feature_category: :projects do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, namespace: user.namespace) }
diff --git a/spec/features/projects/settings/registry_settings_spec.rb b/spec/features/projects/settings/registry_settings_spec.rb
index d64570cd5cc..072b5f7f3b0 100644
--- a/spec/features/projects/settings/registry_settings_spec.rb
+++ b/spec/features/projects/settings/registry_settings_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Project > Settings > Packages and registries > Container registry tag expiration policy' do
+RSpec.describe 'Project > Settings > Packages and registries > Container registry tag expiration policy',
+feature_category: :projects do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, namespace: user.namespace) }
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index d73ff0284cd..6f0a3094849 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -2,12 +2,13 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > Repository settings' do
+RSpec.describe 'Projects > Settings > Repository settings', feature_category: :projects do
let(:project) { create(:project_empty_repo) }
let(:user) { create(:user) }
let(:role) { :developer }
before do
+ stub_feature_flags(branch_rules: false)
project.add_role(user, role)
sign_in(user)
end
@@ -39,19 +40,18 @@ RSpec.describe 'Projects > Settings > Repository settings' do
end
context 'Branch rules', :js do
- it 'renders branch rules settings' do
- visit project_settings_repository_path(project)
- expect(page).to have_content('Branch rules')
- end
-
context 'branch_rules feature flag disabled', :js do
it 'does not render branch rules settings' do
- stub_feature_flags(branch_rules: false)
visit project_settings_repository_path(project)
-
expect(page).not_to have_content('Branch rules')
end
end
+
+ it 'renders branch rules settings' do
+ stub_feature_flags(branch_rules: true)
+ visit project_settings_repository_path(project)
+ expect(page).to have_content('Branch rules')
+ end
end
context 'Deploy Keys', :js do
@@ -164,13 +164,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
end
project.reload
-
- # TODO: The following line is skipped because a toast with
- # "An error occurred while loading branch rules. Please try again."
- # shows up right after which hides the below message. It is causing flakiness.
- # https://gitlab.com/gitlab-org/gitlab/-/issues/383717#note_1185091998
-
- # expect(page).to have_content('Mirroring settings were successfully updated')
+ expect(page).to have_content('Mirroring settings were successfully updated')
expect(project.remote_mirrors.first.only_protected_branches).to eq(false)
end
@@ -190,13 +184,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
end
project.reload
-
- # TODO: The following line is skipped because a toast with
- # "An error occurred while loading branch rules. Please try again."
- # shows up right after which hides the below message. It is causing flakiness.
- # https://gitlab.com/gitlab-org/gitlab/-/issues/383717#note_1185091998
-
- # expect(page).to have_content('Mirroring settings were successfully updated')
+ expect(page).to have_content('Mirroring settings were successfully updated')
expect(project.remote_mirrors.first.only_protected_branches).to eq(true)
end
@@ -213,7 +201,12 @@ RSpec.describe 'Projects > Settings > Repository settings' do
click_button 'Mirror repository'
end
- expect(page).to have_content('Mirroring settings were successfully updated')
+ # TODO: The following line is skipped because a toast with
+ # "An error occurred while loading branch rules. Please try again."
+ # shows up right after which hides the below message. It is causing flakiness.
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/383717#note_1185091998
+
+ # expect(page).to have_content('Mirroring settings were successfully updated')
expect(project.reload.remote_mirrors.first.keep_divergent_refs).to eq(true)
end
@@ -229,7 +222,12 @@ RSpec.describe 'Projects > Settings > Repository settings' do
click_button 'Mirror repository'
end
- expect(page).to have_content('Mirroring settings were successfully updated')
+ # TODO: The following line is skipped because a toast with
+ # "An error occurred while loading branch rules. Please try again."
+ # shows up right after which hides the below message. It is causing flakiness.
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/383717#note_1185091998
+
+ # expect(page).to have_content('Mirroring settings were successfully updated')
expect(page).to have_selector('[title="Copy SSH public key"]')
end
@@ -272,7 +270,6 @@ RSpec.describe 'Projects > Settings > Repository settings' do
click_button 'Start cleanup'
end
end
-
expect(page).to have_content('Repository cleanup has started')
expect(RepositoryCleanupWorker.jobs.count).to eq(1)
end
diff --git a/spec/features/projects/settings/secure_files_spec.rb b/spec/features/projects/settings/secure_files_spec.rb
index ee38acf1953..9afe1f4de54 100644
--- a/spec/features/projects/settings/secure_files_spec.rb
+++ b/spec/features/projects/settings/secure_files_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Secure Files', :js do
+RSpec.describe 'Secure Files', :js, feature_category: :projects do
let(:project) { create(:project) }
let(:user) { create(:user) }
@@ -12,31 +12,10 @@ RSpec.describe 'Secure Files', :js do
sign_in(user)
end
- context 'when the :ci_secure_files feature flag is enabled' do
- before do
- stub_feature_flags(ci_secure_files: true)
-
- visit project_settings_ci_cd_path(project)
- end
-
- context 'authenticated user with admin permissions' do
- it 'shows the secure files settings' do
- expect(page).to have_content('Secure Files')
- end
- end
- end
-
- context 'when the :ci_secure_files feature flag is disabled' do
- before do
- stub_feature_flags(ci_secure_files: false)
-
+ context 'authenticated user with admin permissions' do
+ it 'shows the secure files settings' do
visit project_settings_ci_cd_path(project)
- end
-
- context 'authenticated user with admin permissions' do
- it 'does not shows the secure files settings' do
- expect(page).not_to have_content('Secure Files')
- end
+ expect(page).to have_content('Secure Files')
end
end
diff --git a/spec/features/projects/settings/service_desk_setting_spec.rb b/spec/features/projects/settings/service_desk_setting_spec.rb
index 86c5c3d2d8c..859c738731b 100644
--- a/spec/features/projects/settings/service_desk_setting_spec.rb
+++ b/spec/features/projects/settings/service_desk_setting_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Service Desk Setting', :js, :clean_gitlab_redis_cache do
+RSpec.describe 'Service Desk Setting', :js, :clean_gitlab_redis_cache, feature_category: :projects do
let(:project) { create(:project_empty_repo, :private, service_desk_enabled: false) }
let(:presenter) { project.present(current_user: user) }
let(:user) { create(:user) }
diff --git a/spec/features/projects/settings/user_archives_project_spec.rb b/spec/features/projects/settings/user_archives_project_spec.rb
index 03ea9e7c580..a6aac02d272 100644
--- a/spec/features/projects/settings/user_archives_project_spec.rb
+++ b/spec/features/projects/settings/user_archives_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > User archives a project' do
+RSpec.describe 'Projects > Settings > User archives a project', feature_category: :projects do
let(:user) { create(:user) }
before do
diff --git a/spec/features/projects/settings/user_changes_avatar_spec.rb b/spec/features/projects/settings/user_changes_avatar_spec.rb
index 92d5b4c1fcd..87043aec9b6 100644
--- a/spec/features/projects/settings/user_changes_avatar_spec.rb
+++ b/spec/features/projects/settings/user_changes_avatar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > User changes avatar' do
+RSpec.describe 'Projects > Settings > User changes avatar', feature_category: :projects do
let(:project) { create(:project, :repository) }
let(:user) { project.creator }
diff --git a/spec/features/projects/settings/user_changes_default_branch_spec.rb b/spec/features/projects/settings/user_changes_default_branch_spec.rb
index bf064839bd7..39704fdbbb2 100644
--- a/spec/features/projects/settings/user_changes_default_branch_spec.rb
+++ b/spec/features/projects/settings/user_changes_default_branch_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > User changes default branch' do
+RSpec.describe 'Projects > Settings > User changes default branch', feature_category: :projects do
let(:user) { create(:user) }
before do
diff --git a/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb b/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb
index 0fc12f93850..3a58de9aa7d 100644
--- a/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb
+++ b/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "User interacts with deploy keys", :js do
+RSpec.describe "User interacts with deploy keys", :js, feature_category: :projects do
let(:project) { create(:project, :repository) }
let(:user) { project.first_owner }
diff --git a/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb b/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
index c76b4d0af88..cfefdd54c23 100644
--- a/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
+++ b/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'Projects > Settings > User manages merge request settings' do
+RSpec.describe 'Projects > Settings > User manages merge request settings', feature_category: :projects do
include ProjectForksHelper
let(:user) { create(:user) }
diff --git a/spec/features/projects/settings/user_manages_project_members_spec.rb b/spec/features/projects/settings/user_manages_project_members_spec.rb
index 1d258582b3a..ee832da48d9 100644
--- a/spec/features/projects/settings/user_manages_project_members_spec.rb
+++ b/spec/features/projects/settings/user_manages_project_members_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > User manages project members' do
+RSpec.describe 'Projects > Settings > User manages project members', feature_category: :projects do
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::ModalHelpers
@@ -51,8 +51,6 @@ RSpec.describe 'Projects > Settings > User manages project members' do
click_button 'Import project members'
wait_for_requests
- page.refresh
-
expect(find_member_row(user_mike)).to have_content('Reporter')
end
diff --git a/spec/features/projects/settings/user_renames_a_project_spec.rb b/spec/features/projects/settings/user_renames_a_project_spec.rb
index 2e2d7119e2e..2da6e760fbf 100644
--- a/spec/features/projects/settings/user_renames_a_project_spec.rb
+++ b/spec/features/projects/settings/user_renames_a_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > User renames a project' do
+RSpec.describe 'Projects > Settings > User renames a project', feature_category: :projects do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace, path: 'gitlab', name: 'sample') }
diff --git a/spec/features/projects/settings/user_searches_in_settings_spec.rb b/spec/features/projects/settings/user_searches_in_settings_spec.rb
index 7ed96d01189..8a11507d064 100644
--- a/spec/features/projects/settings/user_searches_in_settings_spec.rb
+++ b/spec/features/projects/settings/user_searches_in_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches project settings', :js do
+RSpec.describe 'User searches project settings', :js, feature_category: :projects do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository, namespace: user.namespace, pages_https_only: false) }
diff --git a/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb b/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb
index 47383be1ba1..65aed4fd06f 100644
--- a/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb
+++ b/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Repository Settings > User sees revoke deploy token modal', :js do
+RSpec.describe 'Repository Settings > User sees revoke deploy token modal', :js, feature_category: :projects do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:role) { :developer }
diff --git a/spec/features/projects/settings/user_tags_project_spec.rb b/spec/features/projects/settings/user_tags_project_spec.rb
index e9a2aa29352..43e8e5a2d38 100644
--- a/spec/features/projects/settings/user_tags_project_spec.rb
+++ b/spec/features/projects/settings/user_tags_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > User tags a project', :js do
+RSpec.describe 'Projects > Settings > User tags a project', :js, feature_category: :projects do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
let!(:topic) { create(:topic, name: 'topic1') }
diff --git a/spec/features/projects/settings/user_transfers_a_project_spec.rb b/spec/features/projects/settings/user_transfers_a_project_spec.rb
index 23e10a36cee..53b4ee881f9 100644
--- a/spec/features/projects/settings/user_transfers_a_project_spec.rb
+++ b/spec/features/projects/settings/user_transfers_a_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > User transfers a project', :js do
+RSpec.describe 'Projects > Settings > User transfers a project', :js, feature_category: :projects do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, namespace: user.namespace) }
let(:group) { create(:group) }
diff --git a/spec/features/projects/settings/visibility_settings_spec.rb b/spec/features/projects/settings/visibility_settings_spec.rb
index 5cb12544066..5246eda976b 100644
--- a/spec/features/projects/settings/visibility_settings_spec.rb
+++ b/spec/features/projects/settings/visibility_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > Visibility settings', :js do
+RSpec.describe 'Projects > Settings > Visibility settings', :js, feature_category: :projects do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace, visibility_level: 20) }
@@ -28,26 +28,6 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js do
expect(visibility_select_container).to have_content 'Only accessible by project members. Membership must be explicitly granted to each user.'
end
- context 'builds select' do
- it 'hides builds select section' do
- find('.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .gl-toggle').click
-
- visit project_settings_merge_requests_path(project)
-
- expect(page).to have_selector('.builds-feature', visible: false)
- end
-
- context 'given project with builds_disabled access level' do
- let(:project) { create(:project, :builds_disabled, namespace: user.namespace) }
-
- it 'hides builds select section' do
- visit project_settings_merge_requests_path(project)
-
- expect(page).to have_selector('.builds-feature', visible: false)
- end
- end
- end
-
context 'disable email notifications' do
it 'is visible' do
expect(page).to have_selector('.js-emails-disabled', visible: true)
diff --git a/spec/features/projects/settings/webhooks_settings_spec.rb b/spec/features/projects/settings/webhooks_settings_spec.rb
index adbf2f6ee5c..8d22d84b9c9 100644
--- a/spec/features/projects/settings/webhooks_settings_spec.rb
+++ b/spec/features/projects/settings/webhooks_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Settings > Webhook Settings' do
+RSpec.describe 'Projects > Settings > Webhook Settings', feature_category: :projects do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:webhooks_path) { project_hooks_path(project) }
@@ -41,54 +41,28 @@ RSpec.describe 'Projects > Settings > Webhook Settings' do
expect(page).to have_content('Tag push events')
expect(page).to have_content('Issues events')
expect(page).to have_content('Confidential issues events')
- expect(page).to have_content('Note events')
- expect(page).to have_content('Merge requests events')
+ expect(page).to have_content('Comment')
+ expect(page).to have_content('Merge request events')
expect(page).to have_content('Pipeline events')
expect(page).to have_content('Wiki page events')
expect(page).to have_content('Releases events')
end
- context 'when feature flag "enhanced_webhook_support_regex" is disabled' do
- before do
- stub_feature_flags(enhanced_webhook_support_regex: false)
- end
-
- it 'create webhook', :js do
- visit webhooks_path
-
- fill_in 'URL', with: url
- check 'Tag push events'
- fill_in 'hook_push_events_branch_filter', with: 'master'
- check 'Enable SSL verification'
- check 'Job events'
-
- click_button 'Add webhook'
-
- expect(page).to have_content(url)
- expect(page).to have_content('SSL Verification: enabled')
- expect(page).to have_content('Tag push events')
- expect(page).to have_content('Job events')
- expect(page).to have_content('Push events')
- end
- end
-
- context 'when feature flag "enhanced_webhook_support_regex" is enabled' do
- it 'create webhook', :js do
- visit webhooks_path
+ it 'create webhook', :js do
+ visit webhooks_path
- fill_in 'URL', with: url
- check 'Tag push events'
- check 'Enable SSL verification'
- check 'Job events'
+ fill_in 'URL', with: url
+ check 'Tag push events'
+ check 'Enable SSL verification'
+ check 'Job events'
- click_button 'Add webhook'
+ click_button 'Add webhook'
- expect(page).to have_content(url)
- expect(page).to have_content('SSL Verification: enabled')
- expect(page).to have_content('Tag push events')
- expect(page).to have_content('Job events')
- expect(page).to have_content('Push events')
- end
+ expect(page).to have_content(url)
+ expect(page).to have_content('SSL Verification: enabled')
+ expect(page).to have_content('Tag push events')
+ expect(page).to have_content('Job events')
+ expect(page).to have_content('Push events')
end
it 'edit existing webhook', :js do
@@ -100,8 +74,8 @@ RSpec.describe 'Projects > Settings > Webhook Settings' do
check 'Enable SSL verification'
click_button 'Save changes'
- expect(page).to have_content 'SSL Verification: enabled'
- expect(page).to have_content(url)
+ expect(page).to have_content('Enable SSL verification')
+ expect(page).to have_current_path(edit_project_hook_path(project, hook), ignore_query: true)
end
it 'test existing webhook', :js do
diff --git a/spec/features/projects/show/download_buttons_spec.rb b/spec/features/projects/show/download_buttons_spec.rb
index e73bb3198e6..e4d50daa6f4 100644
--- a/spec/features/projects/show/download_buttons_spec.rb
+++ b/spec/features/projects/show/download_buttons_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Show > Download buttons' do
+RSpec.describe 'Projects > Show > Download buttons', feature_category: :projects do
let(:user) { create(:user) }
let(:role) { :developer }
let(:status) { 'success' }
diff --git a/spec/features/projects/show/no_password_spec.rb b/spec/features/projects/show/no_password_spec.rb
index ed06f4e14d3..9ead729af83 100644
--- a/spec/features/projects/show/no_password_spec.rb
+++ b/spec/features/projects/show/no_password_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'No Password Alert' do
+RSpec.describe 'No Password Alert', feature_category: :projects do
let_it_be(:message_password_auth_enabled) { 'Your account is authenticated with SSO or SAML. To push and pull over HTTP with Git using this account, you must set a password or set up a Personal Access Token to use instead of a password. For more information, see Clone with HTTPS.' }
let_it_be(:message_password_auth_disabled) { 'Your account is authenticated with SSO or SAML. To push and pull over HTTP with Git using this account, you must set up a Personal Access Token to use instead of a password. For more information, see Clone with HTTPS.' }
diff --git a/spec/features/projects/show/redirects_spec.rb b/spec/features/projects/show/redirects_spec.rb
index 55069cdd6c5..d1cb896450f 100644
--- a/spec/features/projects/show/redirects_spec.rb
+++ b/spec/features/projects/show/redirects_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Show > Redirects' do
+RSpec.describe 'Projects > Show > Redirects', feature_category: :projects do
let(:user) { create :user }
let(:public_project) { create :project, :public }
let(:private_project) { create :project, :private }
diff --git a/spec/features/projects/show/rss_spec.rb b/spec/features/projects/show/rss_spec.rb
index 0bd6e9cbe3b..c2e8a844094 100644
--- a/spec/features/projects/show/rss_spec.rb
+++ b/spec/features/projects/show/rss_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Show > RSS' do
+RSpec.describe 'Projects > Show > RSS', feature_category: :projects do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
let(:path) { project_path(project) }
diff --git a/spec/features/projects/show/schema_markup_spec.rb b/spec/features/projects/show/schema_markup_spec.rb
index 8adbdb64f1b..8262245c5cb 100644
--- a/spec/features/projects/show/schema_markup_spec.rb
+++ b/spec/features/projects/show/schema_markup_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Show > Schema Markup' do
+RSpec.describe 'Projects > Show > Schema Markup', feature_category: :projects do
let_it_be(:project) { create(:project, :repository, :public, :with_avatar, description: 'foobar', topic_list: 'topic1, topic2') }
it 'shows SoftwareSourceCode structured markup', :js do
diff --git a/spec/features/projects/show/user_interacts_with_auto_devops_banner_spec.rb b/spec/features/projects/show/user_interacts_with_auto_devops_banner_spec.rb
index 262885e09b3..2f33622d218 100644
--- a/spec/features/projects/show/user_interacts_with_auto_devops_banner_spec.rb
+++ b/spec/features/projects/show/user_interacts_with_auto_devops_banner_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Project > Show > User interacts with auto devops implicitly enabled banner' do
+RSpec.describe 'Project > Show > User interacts with auto devops implicitly enabled banner',
+feature_category: :projects do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
diff --git a/spec/features/projects/show/user_interacts_with_stars_spec.rb b/spec/features/projects/show/user_interacts_with_stars_spec.rb
index 158b6aa9b46..e2166854ba3 100644
--- a/spec/features/projects/show/user_interacts_with_stars_spec.rb
+++ b/spec/features/projects/show/user_interacts_with_stars_spec.rb
@@ -2,13 +2,14 @@
require 'spec_helper'
-RSpec.describe 'Projects > Show > User interacts with project stars' do
+RSpec.describe 'Projects > Show > User interacts with project stars', feature_category: :projects do
let(:project) { create(:project, :public, :repository) }
context 'when user is signed in', :js do
let(:user) { create(:user) }
before do
+ stub_feature_flags(vscode_web_ide: false)
sign_in(user)
visit(project_path(project))
end
diff --git a/spec/features/projects/show/user_manages_notifications_spec.rb b/spec/features/projects/show/user_manages_notifications_spec.rb
index 1df37eef6a9..8f6535fd4f0 100644
--- a/spec/features/projects/show/user_manages_notifications_spec.rb
+++ b/spec/features/projects/show/user_manages_notifications_spec.rb
@@ -2,10 +2,11 @@
require 'spec_helper'
-RSpec.describe 'Projects > Show > User manages notifications', :js do
+RSpec.describe 'Projects > Show > User manages notifications', :js, feature_category: :projects do
let(:project) { create(:project, :public, :repository) }
before do
+ stub_feature_flags(vscode_web_ide: false)
sign_in(project.first_owner)
end
@@ -23,7 +24,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do
click_notifications_button
page.within first('[data-testid="notification-dropdown"]') do
- expect(page.find('.gl-new-dropdown-item.is-active')).to have_content('On mention')
+ expect(page.find('.gl-dropdown-item.is-active')).to have_content('On mention')
expect(page).to have_css('[data-testid="notifications-icon"]')
end
end
diff --git a/spec/features/projects/show/user_sees_collaboration_links_spec.rb b/spec/features/projects/show/user_sees_collaboration_links_spec.rb
index c63427e56e6..145500a4c63 100644
--- a/spec/features/projects/show/user_sees_collaboration_links_spec.rb
+++ b/spec/features/projects/show/user_sees_collaboration_links_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Show > Collaboration links', :js do
+RSpec.describe 'Projects > Show > Collaboration links', :js, feature_category: :projects do
using RSpec::Parameterized::TableSyntax
let_it_be(:project) { create(:project, :repository, :public) }
diff --git a/spec/features/projects/show/user_sees_deletion_failure_message_spec.rb b/spec/features/projects/show/user_sees_deletion_failure_message_spec.rb
index 47e010dcf89..876eecfe559 100644
--- a/spec/features/projects/show/user_sees_deletion_failure_message_spec.rb
+++ b/spec/features/projects/show/user_sees_deletion_failure_message_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Show > User sees a deletion failure message' do
+RSpec.describe 'Projects > Show > User sees a deletion failure message', feature_category: :projects do
let(:project) { create(:project, :empty_repo, pending_delete: true) }
before do
diff --git a/spec/features/projects/show/user_sees_git_instructions_spec.rb b/spec/features/projects/show/user_sees_git_instructions_spec.rb
index 608bb4c5997..022f21f198d 100644
--- a/spec/features/projects/show/user_sees_git_instructions_spec.rb
+++ b/spec/features/projects/show/user_sees_git_instructions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Show > User sees Git instructions' do
+RSpec.describe 'Projects > Show > User sees Git instructions', feature_category: :projects do
let_it_be(:user) { create(:user) }
before do
diff --git a/spec/features/projects/show/user_sees_last_commit_ci_status_spec.rb b/spec/features/projects/show/user_sees_last_commit_ci_status_spec.rb
index 0aa0f7754c6..25d241f004e 100644
--- a/spec/features/projects/show/user_sees_last_commit_ci_status_spec.rb
+++ b/spec/features/projects/show/user_sees_last_commit_ci_status_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Show > User sees last commit CI status' do
+RSpec.describe 'Projects > Show > User sees last commit CI status', feature_category: :projects do
let_it_be(:project) { create(:project, :repository, :public) }
it 'shows the project README', :js do
diff --git a/spec/features/projects/show/user_sees_readme_spec.rb b/spec/features/projects/show/user_sees_readme_spec.rb
index 6a5b9472be8..a8c91b30f25 100644
--- a/spec/features/projects/show/user_sees_readme_spec.rb
+++ b/spec/features/projects/show/user_sees_readme_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Show > User sees README' do
+RSpec.describe 'Projects > Show > User sees README', feature_category: :projects do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository, :public) }
diff --git a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
index 5056e245fed..9eb2d109829 100644
--- a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
+++ b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
+RSpec.describe 'Projects > Show > User sees setup shortcut buttons', feature_category: :projects do
# For "New file", "Add license" functionality,
# see spec/features/projects/files/project_owner_creates_license_file_spec.rb
# see spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
diff --git a/spec/features/projects/show/user_uploads_files_spec.rb b/spec/features/projects/show/user_uploads_files_spec.rb
index a222d6b42ab..ed378040ce9 100644
--- a/spec/features/projects/show/user_uploads_files_spec.rb
+++ b/spec/features/projects/show/user_uploads_files_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Show > User uploads files' do
+RSpec.describe 'Projects > Show > User uploads files', feature_category: :projects do
include DropzoneHelper
let(:user) { create(:user) }
diff --git a/spec/features/projects/snippets/create_snippet_spec.rb b/spec/features/projects/snippets/create_snippet_spec.rb
index cbdf6d6852e..f2c575231ad 100644
--- a/spec/features/projects/snippets/create_snippet_spec.rb
+++ b/spec/features/projects/snippets/create_snippet_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Snippets > Create Snippet', :js do
+RSpec.describe 'Projects > Snippets > Create Snippet', :js, feature_category: :snippets do
include DropzoneHelper
include Spec::Support::Helpers::Features::SnippetSpecHelpers
diff --git a/spec/features/projects/snippets/show_spec.rb b/spec/features/projects/snippets/show_spec.rb
index 5937ff75457..1a480696b4e 100644
--- a/spec/features/projects/snippets/show_spec.rb
+++ b/spec/features/projects/snippets/show_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Snippets > Project snippet', :js do
+RSpec.describe 'Projects > Snippets > Project snippet', :js, feature_category: :snippets do
let_it_be(:user) { create(:user) }
let_it_be(:project) do
create(:project, creator: user).tap do |p|
diff --git a/spec/features/projects/snippets/user_comments_on_snippet_spec.rb b/spec/features/projects/snippets/user_comments_on_snippet_spec.rb
index 3ccb73c88ef..556f549f86c 100644
--- a/spec/features/projects/snippets/user_comments_on_snippet_spec.rb
+++ b/spec/features/projects/snippets/user_comments_on_snippet_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Snippets > User comments on a snippet', :js do
+RSpec.describe 'Projects > Snippets > User comments on a snippet', :js, feature_category: :snippets do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:snippet) { create(:project_snippet, :repository, project: project, author: user) }
diff --git a/spec/features/projects/snippets/user_deletes_snippet_spec.rb b/spec/features/projects/snippets/user_deletes_snippet_spec.rb
index ca49e6a36b7..c9d1afb7a4e 100644
--- a/spec/features/projects/snippets/user_deletes_snippet_spec.rb
+++ b/spec/features/projects/snippets/user_deletes_snippet_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Snippets > User deletes a snippet', :js do
+RSpec.describe 'Projects > Snippets > User deletes a snippet', :js, feature_category: :snippets do
let(:project) { create(:project) }
let!(:snippet) { create(:project_snippet, :repository, project: project, author: user) }
let(:user) { create(:user) }
diff --git a/spec/features/projects/snippets/user_updates_snippet_spec.rb b/spec/features/projects/snippets/user_updates_snippet_spec.rb
index aa498163f52..205db6c08b1 100644
--- a/spec/features/projects/snippets/user_updates_snippet_spec.rb
+++ b/spec/features/projects/snippets/user_updates_snippet_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Snippets > User updates a snippet', :js do
+RSpec.describe 'Projects > Snippets > User updates a snippet', :js, feature_category: :snippets do
include Spec::Support::Helpers::Features::SnippetSpecHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/projects/snippets/user_views_snippets_spec.rb b/spec/features/projects/snippets/user_views_snippets_spec.rb
index 40539b43ed5..ece65763ea5 100644
--- a/spec/features/projects/snippets/user_views_snippets_spec.rb
+++ b/spec/features/projects/snippets/user_views_snippets_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Snippets > User views snippets' do
+RSpec.describe 'Projects > Snippets > User views snippets', feature_category: :snippets do
let_it_be(:project) { create(:project) }
let(:user) { create(:user) }
diff --git a/spec/features/projects/sourcegraph_csp_spec.rb b/spec/features/projects/sourcegraph_csp_spec.rb
index 10dd050e8cc..4c8dd0a7df0 100644
--- a/spec/features/projects/sourcegraph_csp_spec.rb
+++ b/spec/features/projects/sourcegraph_csp_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Sourcegraph Content Security Policy' do
+RSpec.describe 'Sourcegraph Content Security Policy', feature_category: :projects do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository, namespace: user.namespace) }
diff --git a/spec/features/projects/sub_group_issuables_spec.rb b/spec/features/projects/sub_group_issuables_spec.rb
index d7614201740..2502d969305 100644
--- a/spec/features/projects/sub_group_issuables_spec.rb
+++ b/spec/features/projects/sub_group_issuables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Subgroup Issuables', :js do
+RSpec.describe 'Subgroup Issuables', :js, feature_category: :projects do
let!(:group) { create(:group, name: 'group') }
let!(:subgroup) { create(:group, parent: group, name: 'subgroup') }
let!(:project) { create(:project, namespace: subgroup, name: 'project') }
diff --git a/spec/features/projects/tags/download_buttons_spec.rb b/spec/features/projects/tags/download_buttons_spec.rb
index 0f1f72fd039..570721fc951 100644
--- a/spec/features/projects/tags/download_buttons_spec.rb
+++ b/spec/features/projects/tags/download_buttons_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Download buttons in tags page' do
+RSpec.describe 'Download buttons in tags page', feature_category: :source_code_management do
let(:user) { create(:user) }
let(:role) { :developer }
let(:status) { 'success' }
diff --git a/spec/features/projects/tags/user_edits_tags_spec.rb b/spec/features/projects/tags/user_edits_tags_spec.rb
index 857d0696659..e0efe3b465f 100644
--- a/spec/features/projects/tags/user_edits_tags_spec.rb
+++ b/spec/features/projects/tags/user_edits_tags_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project > Tags', :js do
+RSpec.describe 'Project > Tags', :js, feature_category: :source_code_management do
include DropzoneHelper
let_it_be(:user) { create(:user) }
diff --git a/spec/features/projects/tags/user_views_tag_spec.rb b/spec/features/projects/tags/user_views_tag_spec.rb
index 3978c5b7b78..0816b3240c9 100644
--- a/spec/features/projects/tags/user_views_tag_spec.rb
+++ b/spec/features/projects/tags/user_views_tag_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'User views tag', :feature do
+RSpec.describe 'User views tag', :feature, feature_category: :source_code_management do
include_examples 'user views tag' do
let(:tag_page) { project_tag_path(project, id: tag_name) }
end
diff --git a/spec/features/projects/tags/user_views_tags_spec.rb b/spec/features/projects/tags/user_views_tags_spec.rb
index d3849df023e..26f2e81e3df 100644
--- a/spec/features/projects/tags/user_views_tags_spec.rb
+++ b/spec/features/projects/tags/user_views_tags_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'User views tags', :feature do
+RSpec.describe 'User views tags', :feature, feature_category: :source_code_management do
include_examples 'user views tag' do
let(:tag_page) { project_tags_path(project) }
end
diff --git a/spec/features/projects/terraform_spec.rb b/spec/features/projects/terraform_spec.rb
index d9e45b5e78e..bbc7f675c55 100644
--- a/spec/features/projects/terraform_spec.rb
+++ b/spec/features/projects/terraform_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Terraform', :js do
+RSpec.describe 'Terraform', :js, feature_category: :projects do
let_it_be(:project) { create(:project) }
let_it_be(:terraform_state) { create(:terraform_state, :locked, :with_version, project: project) }
diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb
index 9c950cfee6e..3a0160c42fb 100644
--- a/spec/features/projects/tree/create_directory_spec.rb
+++ b/spec/features/projects/tree/create_directory_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Multi-file editor new directory', :js do
+RSpec.describe 'Multi-file editor new directory', :js, feature_category: :web_ide do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb
index c0567ed4580..61240150658 100644
--- a/spec/features/projects/tree/create_file_spec.rb
+++ b/spec/features/projects/tree/create_file_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Multi-file editor new file', :js do
+RSpec.describe 'Multi-file editor new file', :js, feature_category: :web_ide do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
diff --git a/spec/features/projects/tree/rss_spec.rb b/spec/features/projects/tree/rss_spec.rb
index efbfc329c9f..0b016ee3dd9 100644
--- a/spec/features/projects/tree/rss_spec.rb
+++ b/spec/features/projects/tree/rss_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project Tree RSS' do
+RSpec.describe 'Project Tree RSS', feature_category: :projects do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
let(:path) { project_tree_path(project, :master) }
diff --git a/spec/features/projects/tree/tree_show_spec.rb b/spec/features/projects/tree/tree_show_spec.rb
index eb0ef756b30..21932cae58b 100644
--- a/spec/features/projects/tree/tree_show_spec.rb
+++ b/spec/features/projects/tree/tree_show_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects tree', :js do
+RSpec.describe 'Projects tree', :js, feature_category: :web_ide do
include RepoHelpers
let(:user) { create(:user) }
@@ -157,17 +157,22 @@ RSpec.describe 'Projects tree', :js do
end
end
- context 'ref switcher' do
+ context 'ref switcher', :js do
it 'switches ref to branch' do
+ ref_selector = '.ref-selector'
ref_name = 'feature'
visit project_tree_path(project, 'master')
- first('.js-project-refs-dropdown').click
- page.within '.project-refs-form' do
- click_link ref_name
+ find(ref_selector).click
+ wait_for_requests
+
+ page.within(ref_selector) do
+ fill_in 'Search by Git revision', with: ref_name
+ wait_for_requests
+ find('li', text: ref_name, match: :prefer_exact).click
end
- expect(page).to have_selector '.dropdown-menu-toggle', text: ref_name
+ expect(find(ref_selector)).to have_text(ref_name)
end
end
end
diff --git a/spec/features/projects/tree/upload_file_spec.rb b/spec/features/projects/tree/upload_file_spec.rb
index f32141d6051..1e4abc789c2 100644
--- a/spec/features/projects/tree/upload_file_spec.rb
+++ b/spec/features/projects/tree/upload_file_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Multi-file editor upload file', :js do
+RSpec.describe 'Multi-file editor upload file', :js, feature_category: :web_ide do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:txt_file) { File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt') }
diff --git a/spec/features/projects/user_changes_project_visibility_spec.rb b/spec/features/projects/user_changes_project_visibility_spec.rb
index df13bb55c6d..5daa5b98b6e 100644
--- a/spec/features/projects/user_changes_project_visibility_spec.rb
+++ b/spec/features/projects/user_changes_project_visibility_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User changes public project visibility', :js do
+RSpec.describe 'User changes public project visibility', :js, feature_category: :projects do
include ProjectForksHelper
shared_examples 'changing visibility to private' do
diff --git a/spec/features/projects/user_creates_project_spec.rb b/spec/features/projects/user_creates_project_spec.rb
index b07f2d12660..af0bd932095 100644
--- a/spec/features/projects/user_creates_project_spec.rb
+++ b/spec/features/projects/user_creates_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User creates a project', :js do
+RSpec.describe 'User creates a project', :js, feature_category: :projects do
let(:user) { create(:user) }
before do
diff --git a/spec/features/projects/user_sees_sidebar_spec.rb b/spec/features/projects/user_sees_sidebar_spec.rb
index e2498928fa0..3a6e11356a2 100644
--- a/spec/features/projects/user_sees_sidebar_spec.rb
+++ b/spec/features/projects/user_sees_sidebar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > User sees sidebar' do
+RSpec.describe 'Projects > User sees sidebar', feature_category: :projects do
let(:user) { create(:user) }
let(:project) { create(:project, :private, public_builds: false, namespace: user.namespace) }
@@ -220,7 +220,7 @@ RSpec.describe 'Projects > User sees sidebar' do
it 'does not show fork button' do
visit project_path(project)
- within('.count-buttons') do
+ within('.project-repo-buttons') do
expect(page).not_to have_link 'Fork'
end
end
diff --git a/spec/features/projects/user_sees_user_popover_spec.rb b/spec/features/projects/user_sees_user_popover_spec.rb
index 0bbe7f26cd4..5badcd99dff 100644
--- a/spec/features/projects/user_sees_user_popover_spec.rb
+++ b/spec/features/projects/user_sees_user_popover_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User sees user popover', :js do
+RSpec.describe 'User sees user popover', :js, feature_category: :projects do
include Spec::Support::Helpers::Features::NotesHelpers
let_it_be(:user) { create(:user, pronouns: 'they/them') }
diff --git a/spec/features/projects/user_sorts_projects_spec.rb b/spec/features/projects/user_sorts_projects_spec.rb
index c40f01f3aa1..6a18d95c840 100644
--- a/spec/features/projects/user_sorts_projects_spec.rb
+++ b/spec/features/projects/user_sorts_projects_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User sorts projects and order persists' do
+RSpec.describe 'User sorts projects and order persists', feature_category: :projects do
include CookieHelper
let_it_be(:user) { create(:user) }
@@ -73,7 +73,7 @@ RSpec.describe 'User sorts projects and order persists' do
end
end
- it_behaves_like "sort order persists across all views", "Created date", "Created"
+ it_behaves_like "sort order persists across all views", "Oldest created", "Created"
end
context 'from group details', :js do
@@ -82,11 +82,11 @@ RSpec.describe 'User sorts projects and order persists' do
visit(details_group_path(group))
within '[data-testid=group_sort_by_dropdown]' do
find('button.gl-dropdown-toggle').click
- first(:button, 'Stars').click
+ first(:button, 'Updated').click
wait_for_requests
end
end
- it_behaves_like "sort order persists across all views", "Stars", "Stars"
+ it_behaves_like "sort order persists across all views", "Oldest updated", "Updated"
end
end
diff --git a/spec/features/projects/user_uses_shortcuts_spec.rb b/spec/features/projects/user_uses_shortcuts_spec.rb
index cd6f09ce275..05d79ea3b1b 100644
--- a/spec/features/projects/user_uses_shortcuts_spec.rb
+++ b/spec/features/projects/user_uses_shortcuts_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User uses shortcuts', :js do
+RSpec.describe 'User uses shortcuts', :js, feature_category: :projects do
let_it_be(:project) { create(:project, :repository) }
let(:user) { project.first_owner }
diff --git a/spec/features/projects/user_views_empty_project_spec.rb b/spec/features/projects/user_views_empty_project_spec.rb
index 696a7f4ee8a..352fa73bd05 100644
--- a/spec/features/projects/user_views_empty_project_spec.rb
+++ b/spec/features/projects/user_views_empty_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views an empty project' do
+RSpec.describe 'User views an empty project', feature_category: :projects do
let_it_be(:project) { create(:project, :empty_repo) }
let_it_be(:user) { create(:user) }
diff --git a/spec/features/projects/view_on_env_spec.rb b/spec/features/projects/view_on_env_spec.rb
index 5dd30f59e3d..bf32431fc88 100644
--- a/spec/features/projects/view_on_env_spec.rb
+++ b/spec/features/projects/view_on_env_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'View on environment', :js do
+RSpec.describe 'View on environment', :js, feature_category: :projects do
let(:branch_name) { 'feature' }
let(:file_path) { 'files/ruby/feature.rb' }
let(:project) { create(:project, :repository) }
diff --git a/spec/features/projects/wiki/user_views_wiki_empty_spec.rb b/spec/features/projects/wiki/user_views_wiki_empty_spec.rb
index ea045ddb6a1..1f3ba7a5ca2 100644
--- a/spec/features/projects/wiki/user_views_wiki_empty_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_empty_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project > User views empty wiki' do
+RSpec.describe 'Project > User views empty wiki', feature_category: :wiki do
let_it_be(:user) { create(:user) }
let(:wiki) { create(:project_wiki, project: project) }
diff --git a/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
index db2b3fc2f4b..79744633d0c 100644
--- a/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects > Wiki > User views wiki in project page' do
+RSpec.describe 'Projects > Wiki > User views wiki in project page', feature_category: :wiki do
before do
sign_in(project.first_owner)
end
diff --git a/spec/features/projects/wikis_spec.rb b/spec/features/projects/wikis_spec.rb
index 879ffd2932b..5d950da6674 100644
--- a/spec/features/projects/wikis_spec.rb
+++ b/spec/features/projects/wikis_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe 'Project wikis', :js do
+RSpec.describe 'Project wikis', :js, feature_category: :wiki do
let_it_be(:user) { create(:user) }
let(:wiki) { create(:project_wiki, user: user, project: project) }
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index cd9c173a4ff..ec0b3f9d81b 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project' do
+RSpec.describe 'Project', feature_category: :projects do
include ProjectForksHelper
include MobileHelpers
@@ -327,9 +327,9 @@ RSpec.describe 'Project' do
end
it 'has working links to submodules' do
- click_link('645f6c4c')
+ submodule = find_link('645f6c4c')
- expect(page).to have_selector('.ref-selector', text: '645f6c4c82fd3f5e06f67134450a570b795e55a6')
+ expect(submodule[:href]).to eq('https://gitlab.com/gitlab-org/gitlab-grack/-/tree/645f6c4c82fd3f5e06f67134450a570b795e55a6')
end
context 'for signed commit on default branch', :js do
@@ -418,7 +418,7 @@ RSpec.describe 'Project' do
visit path
end
- it_behaves_like 'dirty submit form', [{ form: '.js-general-settings-form', input: 'input[name="project[name]"]' }]
+ it_behaves_like 'dirty submit form', [{ form: '.js-general-settings-form', input: 'input[name="project[name]"]', submit: 'button[type="submit"]' }]
end
describe 'view for a user without an access to a repo' do
diff --git a/spec/features/promotion_spec.rb b/spec/features/promotion_spec.rb
index 903d6244a4c..b2ab718321f 100644
--- a/spec/features/promotion_spec.rb
+++ b/spec/features/promotion_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Promotions', :js do
+RSpec.describe 'Promotions', :js, feature_category: :service_desk do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project_empty_repo) }
diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb
index 174716d646d..c549d99a51f 100644
--- a/spec/features/protected_branches_spec.rb
+++ b/spec/features/protected_branches_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Protected Branches', :js do
+RSpec.describe 'Protected Branches', :js, feature_category: :source_code_management do
include ProtectedBranchHelpers
let(:user) { create(:user) }
diff --git a/spec/features/protected_tags_spec.rb b/spec/features/protected_tags_spec.rb
index c89dd4d1f90..1aadc7ce90a 100644
--- a/spec/features/protected_tags_spec.rb
+++ b/spec/features/protected_tags_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Protected Tags', :js do
+RSpec.describe 'Protected Tags', :js, feature_category: :source_code_management do
include ProtectedTagHelpers
let(:project) { create(:project, :repository) }
diff --git a/spec/features/read_only_spec.rb b/spec/features/read_only_spec.rb
index 11686552062..e65727c05e3 100644
--- a/spec/features/read_only_spec.rb
+++ b/spec/features/read_only_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'read-only message' do
+RSpec.describe 'read-only message', feature_category: :database do
let_it_be(:user) { create(:user) }
before do
@@ -14,7 +14,7 @@ RSpec.describe 'read-only message' do
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
end
- it_behaves_like 'Read-only instance', /You are on a read\-only GitLab instance./
+ it_behaves_like 'Read-only instance', /You are on a read-only GitLab instance./
end
context 'when database is in read-write mode' do
@@ -22,6 +22,6 @@ RSpec.describe 'read-only message' do
allow(Gitlab::Database).to receive(:read_only?).and_return(false)
end
- it_behaves_like 'Read-write instance', /You are on a read\-only GitLab instance./
+ it_behaves_like 'Read-write instance', /You are on a read-only GitLab instance./
end
end
diff --git a/spec/features/reportable_note/issue_spec.rb b/spec/features/reportable_note/issue_spec.rb
index 80c321d0f5a..55e7f5897bc 100644
--- a/spec/features/reportable_note/issue_spec.rb
+++ b/spec/features/reportable_note/issue_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Reportable note on issue', :js do
+RSpec.describe 'Reportable note on issue', :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:issue) { create(:issue, project: project) }
diff --git a/spec/features/reportable_note/merge_request_spec.rb b/spec/features/reportable_note/merge_request_spec.rb
index 58a39bac707..39048495e5d 100644
--- a/spec/features/reportable_note/merge_request_spec.rb
+++ b/spec/features/reportable_note/merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Reportable note on merge request', :js do
+RSpec.describe 'Reportable note on merge request', :js, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/features/reportable_note/snippets_spec.rb b/spec/features/reportable_note/snippets_spec.rb
index 92bf304ac86..7e8c2c2f989 100644
--- a/spec/features/reportable_note/snippets_spec.rb
+++ b/spec/features/reportable_note/snippets_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Reportable note on snippets', :js do
+RSpec.describe 'Reportable note on snippets', :js, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index cee0910aef7..40ba0fa9ebb 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Runners' do
+RSpec.describe 'Runners', feature_category: :runner_fleet do
let_it_be(:user) { create(:user) }
before do
@@ -117,11 +117,35 @@ RSpec.describe 'Runners' do
it 'user sees CI/CD setting page' do
visit project_runners_path(project)
- expect(page.find('.available-shared-runners')).to have_content(shared_runner.display_name)
+ within '[data-testid="available-shared-runners"]' do
+ expect(page).to have_content(shared_runner.display_name)
+ end
+ end
+
+ context 'when multiple shared runners are configured' do
+ let_it_be(:shared_runner_2) { create(:ci_runner, :instance) }
+
+ it 'shows the runner count' do
+ visit project_runners_path(project)
+
+ within '[data-testid="available-shared-runners"]' do
+ expect(page).to have_content format(_('Available shared runners: %{count}'), { count: 2 })
+ end
+ end
+
+ it 'adds pagination to the shared runner list' do
+ stub_const('Projects::Settings::CiCdController::NUMBER_OF_RUNNERS_PER_PAGE', 1)
+
+ visit project_runners_path(project)
+
+ within '[data-testid="available-shared-runners"]' do
+ expect(find('.pagination')).not_to be_nil
+ end
+ end
end
end
- context 'when multiple runners are configured' do
+ context 'when multiple project runners are configured' do
let!(:project_runner_2) { create(:ci_runner, :project, projects: [project]) }
it 'adds pagination to the runner list' do
@@ -306,7 +330,7 @@ RSpec.describe 'Runners' do
end
context 'project with a group and a group runner' do
- let_it_be(:ci_runner) do
+ let_it_be(:group_runner) do
create(:ci_runner, :group, groups: [group], description: 'group-runner')
end
@@ -330,6 +354,28 @@ RSpec.describe 'Runners' do
expect(page).to have_content 'Disable group runners'
expect(project.reload.group_runners_enabled).to be true
end
+
+ context 'when multiple group runners are configured' do
+ let_it_be(:group_runner_2) { create(:ci_runner, :group, groups: [group]) }
+
+ it 'shows the runner count' do
+ visit project_runners_path(project)
+
+ within '[data-testid="group-runners"]' do
+ expect(page).to have_content format(_('Available group runners: %{runners}'), { runners: 2 })
+ end
+ end
+
+ it 'adds pagination to the group runner list' do
+ stub_const('Projects::Settings::CiCdController::NUMBER_OF_RUNNERS_PER_PAGE', 1)
+
+ visit project_runners_path(project)
+
+ within '[data-testid="group-runners"]' do
+ expect(find('.pagination')).not_to be_nil
+ end
+ end
+ end
end
end
end
diff --git a/spec/features/search/user_searches_for_code_spec.rb b/spec/features/search/user_searches_for_code_spec.rb
index ee74ac84a73..14d67bac85f 100644
--- a/spec/features/search/user_searches_for_code_spec.rb
+++ b/spec/features/search/user_searches_for_code_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches for code', :js, :disable_rate_limiter do
+RSpec.describe 'User searches for code', :js, :disable_rate_limiter, feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
diff --git a/spec/features/search/user_searches_for_comments_spec.rb b/spec/features/search/user_searches_for_comments_spec.rb
index 3c39e9f41d4..d7f6143d173 100644
--- a/spec/features/search/user_searches_for_comments_spec.rb
+++ b/spec/features/search/user_searches_for_comments_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches for comments', :js, :disable_rate_limiter do
+RSpec.describe 'User searches for comments', :js, :disable_rate_limiter, feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/features/search/user_searches_for_commits_spec.rb b/spec/features/search/user_searches_for_commits_spec.rb
index e5d86c27942..1fd62a01c78 100644
--- a/spec/features/search/user_searches_for_commits_spec.rb
+++ b/spec/features/search/user_searches_for_commits_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches for commits', :js, :clean_gitlab_redis_rate_limiting do
+RSpec.describe 'User searches for commits', :js, :clean_gitlab_redis_rate_limiting, feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
diff --git a/spec/features/search/user_searches_for_issues_spec.rb b/spec/features/search/user_searches_for_issues_spec.rb
index 22d48bd38f2..6ebbe86d1a9 100644
--- a/spec/features/search/user_searches_for_issues_spec.rb
+++ b/spec/features/search/user_searches_for_issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches for issues', :js, :clean_gitlab_redis_rate_limiting do
+RSpec.describe 'User searches for issues', :js, :clean_gitlab_redis_rate_limiting, feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
diff --git a/spec/features/search/user_searches_for_merge_requests_spec.rb b/spec/features/search/user_searches_for_merge_requests_spec.rb
index 9bbf2cf16d8..69f62a4c1e2 100644
--- a/spec/features/search/user_searches_for_merge_requests_spec.rb
+++ b/spec/features/search/user_searches_for_merge_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches for merge requests', :js, :clean_gitlab_redis_rate_limiting do
+RSpec.describe 'User searches for merge requests', :js, :clean_gitlab_redis_rate_limiting, feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
let(:user) { create(:user) }
diff --git a/spec/features/search/user_searches_for_milestones_spec.rb b/spec/features/search/user_searches_for_milestones_spec.rb
index 702d4e60022..e87c2176380 100644
--- a/spec/features/search/user_searches_for_milestones_spec.rb
+++ b/spec/features/search/user_searches_for_milestones_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'User searches for milestones', :js, :clean_gitlab_redis_rate_limiting do
+RSpec.describe 'User searches for milestones', :js, :clean_gitlab_redis_rate_limiting,
+feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
diff --git a/spec/features/search/user_searches_for_projects_spec.rb b/spec/features/search/user_searches_for_projects_spec.rb
index 15c6224b61b..48a94161927 100644
--- a/spec/features/search/user_searches_for_projects_spec.rb
+++ b/spec/features/search/user_searches_for_projects_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches for projects', :js, :disable_rate_limiter do
+RSpec.describe 'User searches for projects', :js, :disable_rate_limiter, feature_category: :global_search do
let!(:project) { create(:project, :public, name: 'Shop') }
context 'when signed out' do
diff --git a/spec/features/search/user_searches_for_users_spec.rb b/spec/features/search/user_searches_for_users_spec.rb
index 1d649b42c8c..4737cef98c7 100644
--- a/spec/features/search/user_searches_for_users_spec.rb
+++ b/spec/features/search/user_searches_for_users_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches for users', :js, :clean_gitlab_redis_rate_limiting do
+RSpec.describe 'User searches for users', :js, :clean_gitlab_redis_rate_limiting, feature_category: :global_search do
let_it_be(:user1) { create(:user, username: 'gob_bluth', name: 'Gob Bluth') }
let_it_be(:user2) { create(:user, username: 'michael_bluth', name: 'Michael Bluth') }
let_it_be(:user3) { create(:user, username: 'gob_2018', name: 'George Oscar Bluth') }
diff --git a/spec/features/search/user_searches_for_wiki_pages_spec.rb b/spec/features/search/user_searches_for_wiki_pages_spec.rb
index 0f20ad0aa07..c7dc3e34bb7 100644
--- a/spec/features/search/user_searches_for_wiki_pages_spec.rb
+++ b/spec/features/search/user_searches_for_wiki_pages_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'User searches for wiki pages', :js, :clean_gitlab_redis_rate_limiting do
+RSpec.describe 'User searches for wiki pages', :js, :clean_gitlab_redis_rate_limiting,
+feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
diff --git a/spec/features/search/user_uses_header_search_field_spec.rb b/spec/features/search/user_uses_header_search_field_spec.rb
index 04f22cd2a31..334a192bec4 100644
--- a/spec/features/search/user_uses_header_search_field_spec.rb
+++ b/spec/features/search/user_uses_header_search_field_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User uses header search field', :js, :disable_rate_limiter do
+RSpec.describe 'User uses header search field', :js, :disable_rate_limiter, feature_category: :global_search do
include FilteredSearchHelpers
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/features/search/user_uses_search_filters_spec.rb b/spec/features/search/user_uses_search_filters_spec.rb
index 24f6c70e64c..2e3aaab563d 100644
--- a/spec/features/search/user_uses_search_filters_spec.rb
+++ b/spec/features/search/user_uses_search_filters_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User uses search filters', :js do
+RSpec.describe 'User uses search filters', :js, feature_category: :global_search do
let(:group) { create(:group) }
let!(:group_project) { create(:project, group: group) }
let(:project) { create(:project, namespace: user.namespace) }
diff --git a/spec/features/security/admin_access_spec.rb b/spec/features/security/admin_access_spec.rb
index 8070ae066e7..de81444ed71 100644
--- a/spec/features/security/admin_access_spec.rb
+++ b/spec/features/security/admin_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Admin::Projects" do
+RSpec.describe "Admin::Projects", feature_category: :permissions do
include AccessMatchers
describe "GET /admin/projects" do
diff --git a/spec/features/security/dashboard_access_spec.rb b/spec/features/security/dashboard_access_spec.rb
index 5430329d47d..948a4567624 100644
--- a/spec/features/security/dashboard_access_spec.rb
+++ b/spec/features/security/dashboard_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Dashboard access" do
+RSpec.describe "Dashboard access", feature_category: :permissions do
include AccessMatchers
describe "GET /dashboard" do
diff --git a/spec/features/security/group/internal_access_spec.rb b/spec/features/security/group/internal_access_spec.rb
index 755f170a93e..ad2df4a1882 100644
--- a/spec/features/security/group/internal_access_spec.rb
+++ b/spec/features/security/group/internal_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Internal Group access' do
+RSpec.describe 'Internal Group access', feature_category: :permissions do
include AccessMatchers
let(:group) { create(:group, :internal) }
@@ -27,9 +27,11 @@ RSpec.describe 'Internal Group access' do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
+
context 'when admin mode is disabled' do
it { is_expected.to be_allowed_for(:admin) }
end
+
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@@ -47,9 +49,11 @@ RSpec.describe 'Internal Group access' do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
+
context 'when admin mode is disabled' do
it { is_expected.to be_allowed_for(:admin) }
end
+
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@@ -69,9 +73,11 @@ RSpec.describe 'Internal Group access' do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
+
context 'when admin mode is disabled' do
it { is_expected.to be_allowed_for(:admin) }
end
+
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@@ -89,9 +95,11 @@ RSpec.describe 'Internal Group access' do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
+
context 'when admin mode is disabled' do
it { is_expected.to be_allowed_for(:admin) }
end
+
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@@ -109,9 +117,11 @@ RSpec.describe 'Internal Group access' do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
+
context 'when admin mode is disabled' do
it { is_expected.to be_denied_for(:admin) }
end
+
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_denied_for(:maintainer).of(group) }
it { is_expected.to be_denied_for(:developer).of(group) }
diff --git a/spec/features/security/group/private_access_spec.rb b/spec/features/security/group/private_access_spec.rb
index f733145b5e3..2e7b7512b45 100644
--- a/spec/features/security/group/private_access_spec.rb
+++ b/spec/features/security/group/private_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Private Group access' do
+RSpec.describe 'Private Group access', feature_category: :permissions do
include AccessMatchers
let(:group) { create(:group, :private) }
@@ -27,9 +27,11 @@ RSpec.describe 'Private Group access' do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
+
context 'when admin mode is disabled' do
it { is_expected.to be_denied_for(:admin) }
end
+
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@@ -47,9 +49,11 @@ RSpec.describe 'Private Group access' do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
+
context 'when admin mode is disabled' do
it { is_expected.to be_denied_for(:admin) }
end
+
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@@ -69,9 +73,11 @@ RSpec.describe 'Private Group access' do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
+
context 'when admin mode is disabled' do
it { is_expected.to be_denied_for(:admin) }
end
+
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@@ -89,9 +95,11 @@ RSpec.describe 'Private Group access' do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
+
context 'when admin mode is disabled' do
it { is_expected.to be_denied_for(:admin) }
end
+
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@@ -109,9 +117,11 @@ RSpec.describe 'Private Group access' do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
+
context 'when admin mode is disabled' do
it { is_expected.to be_denied_for(:admin) }
end
+
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_denied_for(:maintainer).of(group) }
it { is_expected.to be_denied_for(:developer).of(group) }
@@ -135,9 +145,11 @@ RSpec.describe 'Private Group access' do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
+
context 'when admin mode is disabled' do
it { is_expected.to be_denied_for(:admin) }
end
+
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
diff --git a/spec/features/security/group/public_access_spec.rb b/spec/features/security/group/public_access_spec.rb
index 90de2b58044..513c5710c8f 100644
--- a/spec/features/security/group/public_access_spec.rb
+++ b/spec/features/security/group/public_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Public Group access' do
+RSpec.describe 'Public Group access', feature_category: :permissions do
include AccessMatchers
let(:group) { create(:group, :public) }
@@ -27,9 +27,11 @@ RSpec.describe 'Public Group access' do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
+
context 'when admin mode is disabled' do
it { is_expected.to be_allowed_for(:admin) }
end
+
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@@ -47,9 +49,11 @@ RSpec.describe 'Public Group access' do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
+
context 'when admin mode is disabled' do
it { is_expected.to be_allowed_for(:admin) }
end
+
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@@ -69,9 +73,11 @@ RSpec.describe 'Public Group access' do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
+
context 'when admin mode is disabled' do
it { is_expected.to be_allowed_for(:admin) }
end
+
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@@ -89,9 +95,11 @@ RSpec.describe 'Public Group access' do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
+
context 'when admin mode is disabled' do
it { is_expected.to be_allowed_for(:admin) }
end
+
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@@ -109,9 +117,11 @@ RSpec.describe 'Public Group access' do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
+
context 'when admin mode is disabled' do
it { is_expected.to be_denied_for(:admin) }
end
+
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_denied_for(:maintainer).of(group) }
it { is_expected.to be_denied_for(:developer).of(group) }
diff --git a/spec/features/security/profile_access_spec.rb b/spec/features/security/profile_access_spec.rb
index 301efd2d99b..991ff115d3d 100644
--- a/spec/features/security/profile_access_spec.rb
+++ b/spec/features/security/profile_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Profile access" do
+RSpec.describe "Profile access", feature_category: :user_management do
include AccessMatchers
describe "GET /-/profile/keys" do
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index 48cee4b1f19..e35e7ed742b 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Internal Project Access" do
+RSpec.describe "Internal Project Access", feature_category: :permissions do
include AccessMatchers
let_it_be(:project, reload: true) { create(:project, :internal, :repository, :with_namespace_settings) }
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index c06b1e5da54..59ddb18ae8a 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Private Project Access" do
+RSpec.describe "Private Project Access", feature_category: :permissions do
include AccessMatchers
let_it_be(:project, reload: true) do
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index d2112430638..425691001f2 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Public Project Access" do
+RSpec.describe "Public Project Access", feature_category: :permissions do
include AccessMatchers
let_it_be(:project, reload: true) do
diff --git a/spec/features/security/project/snippet/internal_access_spec.rb b/spec/features/security/project/snippet/internal_access_spec.rb
index ab080f0a460..b7dcc5f31d3 100644
--- a/spec/features/security/project/snippet/internal_access_spec.rb
+++ b/spec/features/security/project/snippet/internal_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Internal Project Snippets Access" do
+RSpec.describe "Internal Project Snippets Access", feature_category: :permissions do
include AccessMatchers
let_it_be(:project) { create(:project, :internal) }
diff --git a/spec/features/security/project/snippet/private_access_spec.rb b/spec/features/security/project/snippet/private_access_spec.rb
index 1e0afc09b74..0ae45abb7ec 100644
--- a/spec/features/security/project/snippet/private_access_spec.rb
+++ b/spec/features/security/project/snippet/private_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Private Project Snippets Access" do
+RSpec.describe "Private Project Snippets Access", feature_category: :permissions do
include AccessMatchers
let_it_be(:project) { create(:project, :private) }
diff --git a/spec/features/security/project/snippet/public_access_spec.rb b/spec/features/security/project/snippet/public_access_spec.rb
index f734f7ba9e2..b98f665c0dc 100644
--- a/spec/features/security/project/snippet/public_access_spec.rb
+++ b/spec/features/security/project/snippet/public_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Public Project Snippets Access" do
+RSpec.describe "Public Project Snippets Access", feature_category: :permissions do
include AccessMatchers
let_it_be(:project) { create(:project, :public) }
diff --git a/spec/features/sentry_js_spec.rb b/spec/features/sentry_js_spec.rb
index 1d277ba7b3c..d3880011914 100644
--- a/spec/features/sentry_js_spec.rb
+++ b/spec/features/sentry_js_spec.rb
@@ -2,27 +2,62 @@
require 'spec_helper'
-RSpec.describe 'Sentry' do
- let(:sentry_regex_path) { '\/sentry.*\.chunk\.js' }
+RSpec.describe 'Sentry', feature_category: :error_tracking do
+ context 'when enable_new_sentry_clientside_integration is disabled' do
+ before do
+ stub_feature_flags(enable_new_sentry_clientside_integration: false)
+ end
+
+ it 'does not load sentry if sentry is disabled' do
+ allow(Gitlab.config.sentry).to receive(:enabled).and_return(false)
+
+ visit new_user_session_path
+
+ expect(has_requested_legacy_sentry).to eq(false)
+ end
- it 'does not load sentry if sentry is disabled' do
- allow(Gitlab.config.sentry).to receive(:enabled).and_return(false)
- visit new_user_session_path
+ it 'loads legacy sentry if sentry config is enabled', :js do
+ allow(Gitlab.config.sentry).to receive(:enabled).and_return(true)
- expect(has_requested_sentry).to eq(false)
+ visit new_user_session_path
+
+ expect(has_requested_legacy_sentry).to eq(true)
+ expect(evaluate_script('window._Sentry.SDK_VERSION')).to match(%r{^5\.})
+ end
end
- it 'loads sentry if sentry is enabled' do
- stub_sentry_settings
+ context 'when enable_new_sentry_clientside_integration is enabled' do
+ before do
+ stub_feature_flags(enable_new_sentry_clientside_integration: true)
+ end
+
+ it 'does not load sentry if sentry settings are disabled' do
+ allow(Gitlab::CurrentSettings).to receive(:sentry_enabled).and_return(false)
- visit new_user_session_path
+ visit new_user_session_path
- expect(has_requested_sentry).to eq(true)
+ expect(has_requested_sentry).to eq(false)
+ end
+
+ it 'loads sentry if sentry settings are enabled', :js do
+ allow(Gitlab::CurrentSettings).to receive(:sentry_enabled).and_return(true)
+
+ visit new_user_session_path
+
+ expect(has_requested_sentry).to eq(true)
+ expect(evaluate_script('window._Sentry.SDK_VERSION')).to match(%r{^7\.})
+ end
+ end
+
+ def has_requested_legacy_sentry
+ page.all('script', visible: false).one? do |elm|
+ elm[:src] =~ %r{/legacy_sentry.*\.chunk\.js\z}
+ end
end
def has_requested_sentry
page.all('script', visible: false).one? do |elm|
- elm[:src] =~ /#{sentry_regex_path}$/
+ elm[:src] =~ %r{/sentry.*\.chunk\.js\z}
end
end
end
diff --git a/spec/features/signed_commits_spec.rb b/spec/features/signed_commits_spec.rb
index 8725dbcafe8..34127787e47 100644
--- a/spec/features/signed_commits_spec.rb
+++ b/spec/features/signed_commits_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'GPG signed commits' do
+RSpec.describe 'GPG signed commits', feature_category: :source_code_management do
let(:project) { create(:project, :public, :repository) }
it 'changes from unverified to verified when the user changes their email to match the gpg key', :sidekiq_might_not_need_inline do
diff --git a/spec/features/snippets/embedded_snippet_spec.rb b/spec/features/snippets/embedded_snippet_spec.rb
index 90d877d29b7..73b29ffd575 100644
--- a/spec/features/snippets/embedded_snippet_spec.rb
+++ b/spec/features/snippets/embedded_snippet_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Embedded Snippets' do
+RSpec.describe 'Embedded Snippets', feature_category: :source_code_management do
let_it_be(:snippet) { create(:personal_snippet, :public, :repository) }
let(:blobs) { snippet.blobs.first(3) }
diff --git a/spec/features/snippets/explore_spec.rb b/spec/features/snippets/explore_spec.rb
index b62c35bf96e..ef4b75ac3b4 100644
--- a/spec/features/snippets/explore_spec.rb
+++ b/spec/features/snippets/explore_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Explore Snippets' do
+RSpec.describe 'Explore Snippets', feature_category: :source_code_management do
let!(:public_snippet) { create(:personal_snippet, :public) }
let!(:internal_snippet) { create(:personal_snippet, :internal) }
let!(:private_snippet) { create(:personal_snippet, :private) }
diff --git a/spec/features/snippets/internal_snippet_spec.rb b/spec/features/snippets/internal_snippet_spec.rb
index 2fcd11c2a47..9645c9c110d 100644
--- a/spec/features/snippets/internal_snippet_spec.rb
+++ b/spec/features/snippets/internal_snippet_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Internal Snippets', :js do
+RSpec.describe 'Internal Snippets', :js, feature_category: :source_code_management do
let(:internal_snippet) { create(:personal_snippet, :internal, :repository) }
let(:content) { internal_snippet.blobs.first.data.strip! }
diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb
index 8d55a7a64f4..c281e5906ad 100644
--- a/spec/features/snippets/notes_on_personal_snippets_spec.rb
+++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Comments on personal snippets', :js do
+RSpec.describe 'Comments on personal snippets', :js, feature_category: :source_code_management do
include NoteInteractionHelpers
include Spec::Support::Helpers::ModalHelpers
diff --git a/spec/features/snippets/private_snippets_spec.rb b/spec/features/snippets/private_snippets_spec.rb
index 7ff27419cf7..0620a50ea72 100644
--- a/spec/features/snippets/private_snippets_spec.rb
+++ b/spec/features/snippets/private_snippets_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Private Snippets', :js do
+RSpec.describe 'Private Snippets', :js, feature_category: :source_code_management do
let(:user) { create(:user) }
let(:private_snippet) { create(:personal_snippet, :repository, :private, author: user) }
let(:content) { private_snippet.blobs.first.data.strip! }
diff --git a/spec/features/snippets/public_snippets_spec.rb b/spec/features/snippets/public_snippets_spec.rb
index 0f27d96d8e9..be6d6b2c0fa 100644
--- a/spec/features/snippets/public_snippets_spec.rb
+++ b/spec/features/snippets/public_snippets_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Public Snippets', :js do
+RSpec.describe 'Public Snippets', :js, feature_category: :source_code_management do
let(:public_snippet) { create(:personal_snippet, :public, :repository) }
let(:content) { public_snippet.blobs.first.data.strip! }
diff --git a/spec/features/snippets/search_snippets_spec.rb b/spec/features/snippets/search_snippets_spec.rb
index d18729d080a..98842f54015 100644
--- a/spec/features/snippets/search_snippets_spec.rb
+++ b/spec/features/snippets/search_snippets_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Search Snippets', :js do
+RSpec.describe 'Search Snippets', :js, feature_category: :source_code_management do
it 'user searches for snippets by title' do
public_snippet = create(:personal_snippet, :public, title: 'Beginning and Middle')
private_snippet = create(:personal_snippet, :private, title: 'Middle and End')
diff --git a/spec/features/snippets/show_spec.rb b/spec/features/snippets/show_spec.rb
index 2103d362f94..a6e0bc32d42 100644
--- a/spec/features/snippets/show_spec.rb
+++ b/spec/features/snippets/show_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Snippet', :js do
+RSpec.describe 'Snippet', :js, feature_category: :source_code_management do
let_it_be(:user) { create(:user) }
let_it_be(:snippet) { create(:personal_snippet, :public, :repository, author: user) }
diff --git a/spec/features/snippets/spam_snippets_spec.rb b/spec/features/snippets/spam_snippets_spec.rb
index 3748a916780..5d49b36f4fe 100644
--- a/spec/features/snippets/spam_snippets_spec.rb
+++ b/spec/features/snippets/spam_snippets_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'snippet editor with spam', skip: "Will be handled in https://gitlab.com/gitlab-org/gitlab/-/issues/217722" do
+RSpec.describe 'snippet editor with spam', skip: "Will be handled in https://gitlab.com/gitlab-org/gitlab/-/issues/217722",
+ feature_category: :source_code_management do
include_context 'includes Spam constants'
let_it_be(:user) { create(:user) }
diff --git a/spec/features/snippets/user_creates_snippet_spec.rb b/spec/features/snippets/user_creates_snippet_spec.rb
index fd95516090a..064250c5673 100644
--- a/spec/features/snippets/user_creates_snippet_spec.rb
+++ b/spec/features/snippets/user_creates_snippet_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User creates snippet', :js do
+RSpec.describe 'User creates snippet', :js, feature_category: :source_code_management do
include DropzoneHelper
include Spec::Support::Helpers::Features::SnippetSpecHelpers
diff --git a/spec/features/snippets/user_deletes_snippet_spec.rb b/spec/features/snippets/user_deletes_snippet_spec.rb
index e896f7eb25b..3c4c41b0181 100644
--- a/spec/features/snippets/user_deletes_snippet_spec.rb
+++ b/spec/features/snippets/user_deletes_snippet_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User deletes snippet', :js do
+RSpec.describe 'User deletes snippet', :js, feature_category: :source_code_management do
let(:user) { create(:user) }
let(:content) { 'puts "test"' }
let(:snippet) { create(:personal_snippet, :repository, :public, content: content, author: user) }
diff --git a/spec/features/snippets/user_edits_snippet_spec.rb b/spec/features/snippets/user_edits_snippet_spec.rb
index a04c59b53d2..5096472ebe1 100644
--- a/spec/features/snippets/user_edits_snippet_spec.rb
+++ b/spec/features/snippets/user_edits_snippet_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User edits snippet', :js do
+RSpec.describe 'User edits snippet', :js, feature_category: :source_code_management do
include DropzoneHelper
include Spec::Support::Helpers::Features::SnippetSpecHelpers
diff --git a/spec/features/snippets/user_snippets_spec.rb b/spec/features/snippets/user_snippets_spec.rb
index bb733431b22..09e0e30666d 100644
--- a/spec/features/snippets/user_snippets_spec.rb
+++ b/spec/features/snippets/user_snippets_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User Snippets' do
+RSpec.describe 'User Snippets', feature_category: :source_code_management do
let(:author) { create(:user) }
let!(:public_snippet) { create(:personal_snippet, :public, author: author, title: "This is a public snippet") }
let!(:internal_snippet) { create(:personal_snippet, :internal, author: author, title: "This is an internal snippet") }
diff --git a/spec/features/snippets_spec.rb b/spec/features/snippets_spec.rb
index 35eb5c2e193..2ccdb68e844 100644
--- a/spec/features/snippets_spec.rb
+++ b/spec/features/snippets_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Snippets' do
+RSpec.describe 'Snippets', feature_category: :snippets do
context 'when the project has snippets' do
let(:project) { create(:project, :public) }
let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.first_owner, project: project) }
diff --git a/spec/features/tags/developer_creates_tag_spec.rb b/spec/features/tags/developer_creates_tag_spec.rb
index 5657115fb3c..111710ba325 100644
--- a/spec/features/tags/developer_creates_tag_spec.rb
+++ b/spec/features/tags/developer_creates_tag_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Developer creates tag' do
+RSpec.describe 'Developer creates tag', :js, feature_category: :source_code_management do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, :repository, namespace: group) }
@@ -15,6 +15,8 @@ RSpec.describe 'Developer creates tag' do
context 'from tag list' do
before do
visit project_tags_path(project)
+ click_link 'New tag'
+ wait_for_requests
end
it 'with an invalid name displays an error' do
@@ -23,10 +25,17 @@ RSpec.describe 'Developer creates tag' do
expect(page).to have_content 'Tag name invalid'
end
- it 'with an invalid reference displays an error' do
- create_tag_in_form(tag: 'v2.0', ref: 'foo')
-
- expect(page).to have_content 'Target foo is invalid'
+ it "doesn't allow to select invalid ref" do
+ ref_name = 'foo'
+ fill_in 'tag_name', with: 'v2.0'
+ ref_selector = '.ref-selector'
+ find(ref_selector).click
+ wait_for_requests
+ page.within(ref_selector) do
+ fill_in _('Search by Git revision'), with: ref_name
+ wait_for_requests
+ expect(find('.gl-dropdown-contents')).not_to have_content(ref_name)
+ end
end
it 'that already exists displays an error' do
@@ -46,27 +55,34 @@ RSpec.describe 'Developer creates tag' do
end
end
- it 'opens dropdown for ref', :js do
- click_link 'New tag'
- ref_row = find('.form-group:nth-of-type(2) .col-sm-12')
+ it 'opens dropdown for ref' do
+ ref_row = find('.form-group:nth-of-type(2) .col-sm-auto')
page.within ref_row do
ref_input = find('[name="ref"]', visible: false)
expect(ref_input.value).to eq 'master'
- expect(find('.dropdown-toggle-text')).to have_content 'master'
-
- find('.js-branch-select').click
-
- expect(find('.dropdown-menu')).to have_content 'empty-branch'
+ expect(find('.gl-dropdown-button-text')).to have_content 'master'
+ find('.ref-selector').click
+ expect(find('.dropdown-menu')).to have_content 'test'
end
end
end
def create_tag_in_form(tag:, ref:, message: nil, desc: nil)
- click_link 'New tag'
fill_in 'tag_name', with: tag
- find('#ref', visible: false).set(ref)
+ select_ref(ref: ref)
fill_in 'message', with: message unless message.nil?
fill_in 'release_description', with: desc unless desc.nil?
click_button 'Create tag'
end
+
+ def select_ref(ref:)
+ ref_selector = '.ref-selector'
+ find(ref_selector).click
+ wait_for_requests
+ page.within(ref_selector) do
+ fill_in _('Search by Git revision'), with: ref
+ wait_for_requests
+ find('li', text: ref, match: :prefer_exact).click
+ end
+ end
end
diff --git a/spec/features/tags/developer_deletes_tag_spec.rb b/spec/features/tags/developer_deletes_tag_spec.rb
index efd4b42c136..76cf3aa691d 100644
--- a/spec/features/tags/developer_deletes_tag_spec.rb
+++ b/spec/features/tags/developer_deletes_tag_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Developer deletes tag', :js do
+RSpec.describe 'Developer deletes tag', :js, feature_category: :source_code_management do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, :repository, namespace: group) }
diff --git a/spec/features/tags/developer_views_tags_spec.rb b/spec/features/tags/developer_views_tags_spec.rb
index e2399dd9978..dc9f38f1d83 100644
--- a/spec/features/tags/developer_views_tags_spec.rb
+++ b/spec/features/tags/developer_views_tags_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Developer views tags' do
+RSpec.describe 'Developer views tags', feature_category: :source_code_management do
include RepoHelpers
let(:user) { create(:user) }
diff --git a/spec/features/tags/maintainer_deletes_protected_tag_spec.rb b/spec/features/tags/maintainer_deletes_protected_tag_spec.rb
index 0bf9645c2fb..ce518b962cd 100644
--- a/spec/features/tags/maintainer_deletes_protected_tag_spec.rb
+++ b/spec/features/tags/maintainer_deletes_protected_tag_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Maintainer deletes protected tag', :js do
+RSpec.describe 'Maintainer deletes protected tag', :js, feature_category: :source_code_management do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, :repository, namespace: group) }
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index 07de3789c08..d35726fe125 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Task Lists', :js do
+RSpec.describe 'Task Lists', :js, feature_category: :team_planning do
include Warden::Test::Helpers
let_it_be(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/topic_show_spec.rb b/spec/features/topic_show_spec.rb
index 196fc34e3ea..d640e4e4edb 100644
--- a/spec/features/topic_show_spec.rb
+++ b/spec/features/topic_show_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Topic show page' do
+RSpec.describe 'Topic show page', feature_category: :projects do
let_it_be(:topic) { create(:topic, name: 'my-topic', title: 'My Topic', description: 'This is **my** topic https://google.com/ :poop: ```\ncode\n```', avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) }
context 'when topic does not exist' do
diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb
index eb497715df7..3616fdb2e8e 100644
--- a/spec/features/triggers_spec.rb
+++ b/spec/features/triggers_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Triggers', :js do
+RSpec.describe 'Triggers', :js, feature_category: :continuous_integration do
include Spec::Support::Helpers::ModalHelpers
let(:trigger_title) { 'trigger desc' }
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index eed67e3ac78..9ef0626b2b2 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
+RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js,
+feature_category: :authentication_and_authorization do
include Spec::Support::Helpers::Features::TwoFactorHelpers
before do
diff --git a/spec/features/unsubscribe_links_spec.rb b/spec/features/unsubscribe_links_spec.rb
index 12d2f0a9bb6..bcab35335cb 100644
--- a/spec/features/unsubscribe_links_spec.rb
+++ b/spec/features/unsubscribe_links_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Unsubscribe links', :sidekiq_inline do
+RSpec.describe 'Unsubscribe links', :sidekiq_inline, feature_category: :not_owned do
include Warden::Test::Helpers
let_it_be(:project) { create(:project, :public) }
diff --git a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
index 8daa869a6e3..78cede77fea 100644
--- a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User uploads avatar to group' do
+RSpec.describe 'User uploads avatar to group', feature_category: :users do
it 'they see the new avatar' do
user = create(:user)
group = create(:group)
diff --git a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
index 02f9d57fcfe..fb62b5eadc5 100644
--- a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User uploads avatar to profile' do
+RSpec.describe 'User uploads avatar to profile', feature_category: :users do
let!(:user) { create(:user) }
let(:avatar_file_path) { Rails.root.join('spec', 'fixtures', 'dk.png') }
diff --git a/spec/features/uploads/user_uploads_file_to_note_spec.rb b/spec/features/uploads/user_uploads_file_to_note_spec.rb
index 2547e2d274c..e5ad62592ae 100644
--- a/spec/features/uploads/user_uploads_file_to_note_spec.rb
+++ b/spec/features/uploads/user_uploads_file_to_note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User uploads file to note' do
+RSpec.describe 'User uploads file to note', feature_category: :team_planning do
include DropzoneHelper
let(:user) { create(:user) }
diff --git a/spec/features/usage_stats_consent_spec.rb b/spec/features/usage_stats_consent_spec.rb
index 69bd6f35558..c446fe1531b 100644
--- a/spec/features/usage_stats_consent_spec.rb
+++ b/spec/features/usage_stats_consent_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Usage stats consent' do
+RSpec.describe 'Usage stats consent', feature_category: :service_ping do
context 'when signed in' do
let(:user) { create(:admin, created_at: 8.days.ago) }
let(:message) { 'To help improve GitLab, we would like to periodically collect usage information.' }
diff --git a/spec/features/user_can_display_performance_bar_spec.rb b/spec/features/user_can_display_performance_bar_spec.rb
index 14b5964686f..4f6ce6e8f71 100644
--- a/spec/features/user_can_display_performance_bar_spec.rb
+++ b/spec/features/user_can_display_performance_bar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User can display performance bar', :js do
+RSpec.describe 'User can display performance bar', :js, feature_category: :continuous_verification do
shared_examples 'performance bar cannot be displayed' do
it 'does not show the performance bar by default' do
expect(page).not_to have_css('#js-peek')
diff --git a/spec/features/user_opens_link_to_comment_spec.rb b/spec/features/user_opens_link_to_comment_spec.rb
index 59dea91c666..fb8f312c44b 100644
--- a/spec/features/user_opens_link_to_comment_spec.rb
+++ b/spec/features/user_opens_link_to_comment_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User opens link to comment', :js do
+RSpec.describe 'User opens link to comment', :js, feature_category: :team_planning do
let(:project) { create(:project, :public) }
let(:note) { create(:note_on_issue, project: project) }
diff --git a/spec/features/user_sees_revert_modal_spec.rb b/spec/features/user_sees_revert_modal_spec.rb
index 5edf8358244..ea5fd537c5b 100644
--- a/spec/features/user_sees_revert_modal_spec.rb
+++ b/spec/features/user_sees_revert_modal_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Merge request > User sees revert modal', :js, :sidekiq_might_not_need_inline do
+RSpec.describe 'Merge request > User sees revert modal', :js, :sidekiq_might_not_need_inline,
+feature_category: :code_review do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/features/user_sorts_things_spec.rb b/spec/features/user_sorts_things_spec.rb
index c6a1cfdc146..708caf79090 100644
--- a/spec/features/user_sorts_things_spec.rb
+++ b/spec/features/user_sorts_things_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe "User sorts things", :js do
sign_in(current_user)
end
- it "issues -> project home page -> issues" do
+ it "issues -> project home page -> issues", feature_category: :team_planning do
sort_option = s_('SortOptions|Updated date')
visit(project_issues_path(project))
@@ -34,7 +34,7 @@ RSpec.describe "User sorts things", :js do
expect(page).to have_button(sort_option)
end
- it "merge requests -> dashboard merge requests" do
+ it "merge requests -> dashboard merge requests", feature_category: :code_review do
sort_option = s_('SortOptions|Updated date')
visit(project_merge_requests_path(project))
diff --git a/spec/features/users/active_sessions_spec.rb b/spec/features/users/active_sessions_spec.rb
index e2ee78a7cc5..53a4c8a91e9 100644
--- a/spec/features/users/active_sessions_spec.rb
+++ b/spec/features/users/active_sessions_spec.rb
@@ -2,27 +2,31 @@
require 'spec_helper'
-RSpec.describe 'Active user sessions', :clean_gitlab_redis_sessions do
+RSpec.describe 'Active user sessions', :clean_gitlab_redis_sessions, feature_category: :system_access do
it 'successful login adds a new active user login' do
+ user = create(:user)
+
now = Time.zone.parse('2018-03-12 09:06')
- Timecop.freeze(now) do
- user = create(:user)
+ travel_to(now) do
gitlab_sign_in(user)
expect(page).to have_current_path root_path, ignore_query: true
sessions = ActiveSession.list(user)
expect(sessions.count).to eq 1
+ gitlab_sign_out
+ end
- # refresh the current page updates the updated_at
- Timecop.freeze(now + 1.minute) do
- visit current_path
+ # refresh the current page updates the updated_at
+ travel_to(now + 1.minute) do
+ gitlab_sign_in(user)
+
+ visit current_path
- sessions = ActiveSession.list(user)
- expect(sessions.first).to have_attributes(
- created_at: Time.zone.parse('2018-03-12 09:06'),
- updated_at: Time.zone.parse('2018-03-12 09:07')
- )
- end
+ sessions = ActiveSession.list(user)
+ expect(sessions.first).to have_attributes(
+ created_at: Time.zone.parse('2018-03-12 09:06'),
+ updated_at: Time.zone.parse('2018-03-12 09:07')
+ )
end
end
diff --git a/spec/features/users/add_email_to_existing_account_spec.rb b/spec/features/users/add_email_to_existing_account_spec.rb
index cf78fc4587f..8c4e68c454f 100644
--- a/spec/features/users/add_email_to_existing_account_spec.rb
+++ b/spec/features/users/add_email_to_existing_account_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'AdditionalEmailToExistingAccount' do
+RSpec.describe 'AdditionalEmailToExistingAccount', feature_category: :users do
describe 'add secondary email associated with account' do
let_it_be(:user) { create(:user) }
let_it_be(:email) { create(:email, user: user) }
diff --git a/spec/features/users/anonymous_sessions_spec.rb b/spec/features/users/anonymous_sessions_spec.rb
index 6b21412ae3d..83473964d6b 100644
--- a/spec/features/users/anonymous_sessions_spec.rb
+++ b/spec/features/users/anonymous_sessions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Session TTLs', :clean_gitlab_redis_shared_state do
+RSpec.describe 'Session TTLs', :clean_gitlab_redis_shared_state, feature_category: :system_access do
include SessionHelpers
it 'creates a session with a short TTL when login fails' do
diff --git a/spec/features/users/bizible_csp_spec.rb b/spec/features/users/bizible_csp_spec.rb
index af0b42050b3..6c62cf9e0a2 100644
--- a/spec/features/users/bizible_csp_spec.rb
+++ b/spec/features/users/bizible_csp_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Bizible content security policy' do
+RSpec.describe 'Bizible content security policy', feature_category: :purchase do
before do
stub_config(extra: { one_trust_id: SecureRandom.uuid })
end
diff --git a/spec/features/users/confirmation_spec.rb b/spec/features/users/confirmation_spec.rb
index aaa49c75223..cf8d0c4dbd4 100644
--- a/spec/features/users/confirmation_spec.rb
+++ b/spec/features/users/confirmation_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User confirmation' do
+RSpec.describe 'User confirmation', feature_category: :system_access do
describe 'resend confirmation instructions' do
context 'when recaptcha is enabled' do
before do
diff --git a/spec/features/users/email_verification_on_login_spec.rb b/spec/features/users/email_verification_on_login_spec.rb
index f7102eaf9b7..de52f0b517e 100644
--- a/spec/features/users/email_verification_on_login_spec.rb
+++ b/spec/features/users/email_verification_on_login_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting do
+RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting, feature_category: :system_access do
include EmailHelpers
let_it_be(:user) { create(:user) }
@@ -223,6 +223,14 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
it_behaves_like 'email verification required'
it_behaves_like 'no email verification required when 2fa enabled or ff disabled'
+
+ context 'when the check_ip_address_for_email_verification feature flag is disabled' do
+ before do
+ stub_feature_flags(check_ip_address_for_email_verification: false)
+ end
+
+ it_behaves_like 'no email verification required'
+ end
end
describe 'when a previous authentication event exists for the same ip address' do
diff --git a/spec/features/users/google_analytics_csp_spec.rb b/spec/features/users/google_analytics_csp_spec.rb
index 46a9b3be22f..45cc6c5f39d 100644
--- a/spec/features/users/google_analytics_csp_spec.rb
+++ b/spec/features/users/google_analytics_csp_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Google Analytics 4 content security policy' do
+RSpec.describe 'Google Analytics 4 content security policy', feature_category: :purchase do
it 'includes the GA4 content security policy headers' do
visit root_path
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index 5ca5bd72b79..105e9f97989 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Login', :clean_gitlab_redis_sessions do
+RSpec.describe 'Login', :clean_gitlab_redis_sessions, feature_category: :system_access do
include TermsHelper
include UserLoginHelper
include SessionHelpers
@@ -103,7 +103,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
let(:alert_message) { "To continue, you need to select the link in the confirmation email we sent to verify your email address. If you didn't get our email, select Resend confirmation email" }
before do
- stub_application_setting(send_user_confirmation_email: true)
+ stub_application_setting_enum('email_confirmation_setting', 'hard')
allow(User).to receive(:allow_unconfirmed_access_for).and_return grace_period
stub_feature_flags(identity_verification: false)
end
@@ -953,7 +953,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
let(:alert_message) { "To continue, you need to select the link in the confirmation email we sent to verify your email address. If you didn't get our email, select Resend confirmation email" }
before do
- stub_application_setting(send_user_confirmation_email: true)
+ stub_application_setting_enum('email_confirmation_setting', 'hard')
stub_feature_flags(soft_email_confirmation: true)
stub_feature_flags(identity_verification: false)
allow(User).to receive(:allow_unconfirmed_access_for).and_return grace_period
diff --git a/spec/features/users/logout_spec.rb b/spec/features/users/logout_spec.rb
index 596f0dd5a94..c9839247e7d 100644
--- a/spec/features/users/logout_spec.rb
+++ b/spec/features/users/logout_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Logout/Sign out', :js do
+RSpec.describe 'Logout/Sign out', :js, feature_category: :system_access do
let(:user) { create(:user) }
before do
diff --git a/spec/features/users/one_trust_csp_spec.rb b/spec/features/users/one_trust_csp_spec.rb
index 382a0b4be6c..c22fd26f2e8 100644
--- a/spec/features/users/one_trust_csp_spec.rb
+++ b/spec/features/users/one_trust_csp_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'OneTrust content security policy' do
+RSpec.describe 'OneTrust content security policy', feature_category: :application_instrumentation do
let(:user) { create(:user) }
before do
diff --git a/spec/features/users/overview_spec.rb b/spec/features/users/overview_spec.rb
index 902079b7b93..489e7d61ff9 100644
--- a/spec/features/users/overview_spec.rb
+++ b/spec/features/users/overview_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Overview tab on a user profile', :js do
+RSpec.describe 'Overview tab on a user profile', :js, feature_category: :users do
let(:user) { create(:user) }
let(:contributed_project) { create(:project, :public, :repository) }
diff --git a/spec/features/users/password_spec.rb b/spec/features/users/password_spec.rb
index 793a11c616e..ccd383c8a15 100644
--- a/spec/features/users/password_spec.rb
+++ b/spec/features/users/password_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User password' do
+RSpec.describe 'User password', feature_category: :system_access do
describe 'send password reset' do
context 'when recaptcha is enabled' do
before do
diff --git a/spec/features/users/rss_spec.rb b/spec/features/users/rss_spec.rb
index aba1ff63fab..a2604cd298a 100644
--- a/spec/features/users/rss_spec.rb
+++ b/spec/features/users/rss_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User RSS' do
+RSpec.describe 'User RSS', feature_category: :users do
let(:user) { create(:user) }
let(:path) { user_path(create(:user)) }
diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb
index bbf5882f89f..318dd688fa4 100644
--- a/spec/features/users/show_spec.rb
+++ b/spec/features/users/show_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User page' do
+RSpec.describe 'User page', feature_category: :users do
include ExternalAuthorizationServiceHelpers
let_it_be(:user) { create(:user, bio: '<b>Lorem</b> <i>ipsum</i> dolor sit <a href="https://example.com">amet</a>') }
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
index 9b1a102f07b..1057ae48c7d 100644
--- a/spec/features/users/signup_spec.rb
+++ b/spec/features/users/signup_spec.rb
@@ -44,7 +44,7 @@ RSpec.shared_examples 'Signup name validation' do |field, max_length, label|
end
end
-RSpec.describe 'Signup' do
+RSpec.describe 'Signup', feature_category: :users do
include TermsHelper
let(:new_user) { build_stubbed(:user) }
@@ -197,7 +197,7 @@ RSpec.describe 'Signup' do
context 'with no errors' do
context 'when sending confirmation email' do
before do
- stub_application_setting(send_user_confirmation_email: true)
+ stub_application_setting_enum('email_confirmation_setting', 'hard')
end
context 'when soft email confirmation is not enabled' do
@@ -239,7 +239,7 @@ RSpec.describe 'Signup' do
context "when not sending confirmation email" do
before do
- stub_application_setting(send_user_confirmation_email: false)
+ stub_application_setting_enum('email_confirmation_setting', 'off')
end
it 'creates the user account and goes to dashboard' do
@@ -282,7 +282,7 @@ RSpec.describe 'Signup' do
expect(page).to have_content("Email has already been taken")
end
- it 'does not redisplay the password' do
+ it 'redisplays all fields except password' do
create(:user, email: new_user.email)
visit new_user_registration_path
@@ -291,6 +291,11 @@ RSpec.describe 'Signup' do
expect(page).to have_current_path user_registration_path, ignore_query: true
expect(page.body).not_to match(/#{new_user.password}/)
+
+ expect(find_field('First name').value).to eq(new_user.first_name)
+ expect(find_field('Last name').value).to eq(new_user.last_name)
+ expect(find_field('Username').value).to eq(new_user.username)
+ expect(find_field('Email').value).to eq(new_user.email)
end
end
diff --git a/spec/features/users/snippets_spec.rb b/spec/features/users/snippets_spec.rb
index ce19e491a7c..20fc2981418 100644
--- a/spec/features/users/snippets_spec.rb
+++ b/spec/features/users/snippets_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Snippets tab on a user profile', :js do
+RSpec.describe 'Snippets tab on a user profile', :js, feature_category: :snippets do
context 'when the user has snippets' do
let(:user) { create(:user) }
diff --git a/spec/features/users/terms_spec.rb b/spec/features/users/terms_spec.rb
index 7a662d24d60..7d2137b81b8 100644
--- a/spec/features/users/terms_spec.rb
+++ b/spec/features/users/terms_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Users > Terms', :js do
+RSpec.describe 'Users > Terms', :js, feature_category: :users do
include TermsHelper
let!(:term) { create(:term, terms: 'By accepting, you promise to be nice!') }
diff --git a/spec/features/users/user_browses_projects_on_user_page_spec.rb b/spec/features/users/user_browses_projects_on_user_page_spec.rb
index 5e7d7b76843..841b324fba4 100644
--- a/spec/features/users/user_browses_projects_on_user_page_spec.rb
+++ b/spec/features/users/user_browses_projects_on_user_page_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Users > User browses projects on user page', :js do
+RSpec.describe 'Users > User browses projects on user page', :js, feature_category: :projects do
let!(:user) { create :user }
let!(:private_project) do
create :project, :private, name: 'private', namespace: user.namespace do |project|
diff --git a/spec/features/users/zuora_csp_spec.rb b/spec/features/users/zuora_csp_spec.rb
index f3fd27d6495..b07c923fa54 100644
--- a/spec/features/users/zuora_csp_spec.rb
+++ b/spec/features/users/zuora_csp_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Zuora content security policy' do
+RSpec.describe 'Zuora content security policy', feature_category: :purchase do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:pipeline) { create(:ci_pipeline, project: project) }
diff --git a/spec/features/webauthn_spec.rb b/spec/features/webauthn_spec.rb
index 215d1ff1cb6..e2f16f4a017 100644
--- a/spec/features/webauthn_spec.rb
+++ b/spec/features/webauthn_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Using WebAuthn Devices for Authentication', :js do
+RSpec.describe 'Using WebAuthn Devices for Authentication', :js, feature_category: :authentication_and_authorization do
include Spec::Support::Helpers::Features::TwoFactorHelpers
let(:app_id) { "http://#{Capybara.current_session.server.host}:#{Capybara.current_session.server.port}" }
diff --git a/spec/features/whats_new_spec.rb b/spec/features/whats_new_spec.rb
index 2938ea1b1e8..6b19ab28b44 100644
--- a/spec/features/whats_new_spec.rb
+++ b/spec/features/whats_new_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "renders a `whats new` dropdown item" do
+RSpec.describe "renders a `whats new` dropdown item", feature_category: :not_owned do
let_it_be(:user) { create(:user) }
context 'when not logged in' do
diff --git a/spec/features/work_items/work_item_children_spec.rb b/spec/features/work_items/work_item_children_spec.rb
index 10a1bf7541e..4403ca60d11 100644
--- a/spec/features/work_items/work_item_children_spec.rb
+++ b/spec/features/work_items/work_item_children_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Work item children', :js do
+RSpec.describe 'Work item children', :js, feature_category: :team_planning do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :public, namespace: group) }
let_it_be(:user) { create(:user) }
diff --git a/spec/features/work_items/work_item_spec.rb b/spec/features/work_items/work_item_spec.rb
index 686b82de868..577ec060020 100644
--- a/spec/features/work_items/work_item_spec.rb
+++ b/spec/features/work_items/work_item_spec.rb
@@ -2,14 +2,16 @@
require 'spec_helper'
-RSpec.describe 'Work item', :js do
+RSpec.describe 'Work item', :js, feature_category: :team_planning do
let_it_be(:project) { create(:project, :public) }
let_it_be(:user) { create(:user) }
+ let_it_be(:other_user) { create(:user) }
let_it_be(:work_item) { create(:work_item, project: project) }
context 'for signed in user' do
before do
project.add_developer(user)
+ project.add_developer(other_user)
sign_in(user)
@@ -28,6 +30,31 @@ RSpec.describe 'Work item', :js do
expect(page).to have_text(user.name)
end
end
+
+ it 'shows conflict message when description changes', :aggregate_failures do
+ click_button "Edit description"
+ scroll_to(find('[aria-label="Description"]'))
+
+ # without this for some reason the test fails when running locally
+ sleep 1
+
+ ::WorkItems::UpdateService.new(
+ project: work_item.project,
+ current_user: other_user,
+ params: { description: "oh no!" }
+ ).execute(work_item)
+
+ work_item.reload
+
+ find('[aria-label="Description"]').send_keys("oh yeah!")
+
+ warning = 'Someone edited the description at the same time you did.'
+ expect(page.find('[data-testid="work-item-description-conflicts"]')).to have_text(warning)
+
+ click_button "Save and overwrite"
+
+ expect(page.find('[data-testid="work-item-description"]')).to have_text("oh yeah!")
+ end
end
end
end
diff --git a/spec/finders/autocomplete/routes_finder_spec.rb b/spec/finders/autocomplete/routes_finder_spec.rb
index c5b040a5640..f37e8e8de7b 100644
--- a/spec/finders/autocomplete/routes_finder_spec.rb
+++ b/spec/finders/autocomplete/routes_finder_spec.rb
@@ -32,8 +32,24 @@ RSpec.describe Autocomplete::RoutesFinder do
context 'when user is admin' do
let(:current_user) { admin }
- it 'finds all namespaces matching the search excluding project namespaces' do
- is_expected.to match_array([group.route, group2.route, user_route])
+ context 'when admin mode setting is disabled', :do_not_mock_admin_mode_setting do
+ it 'finds all namespaces matching the search excluding project namespaces' do
+ is_expected.to match_array([group.route, group2.route, user_route])
+ end
+ end
+
+ context 'when admin mode setting is enabled' do
+ context 'when in admin mode', :enable_admin_mode do
+ it 'finds all namespaces matching the search excluding project namespaces' do
+ is_expected.to match_array([group.route, group2.route, user_route])
+ end
+ end
+
+ context 'when not in admin mode' do
+ it 'does not find all namespaces' do
+ is_expected.to match_array([])
+ end
+ end
end
end
end
@@ -48,8 +64,24 @@ RSpec.describe Autocomplete::RoutesFinder do
context 'when user is admin' do
let(:current_user) { admin }
- it 'finds all projects matching the search' do
- is_expected.to match_array([project.route, project2.route])
+ context 'when admin mode setting is disabled', :do_not_mock_admin_mode_setting do
+ it 'finds all projects matching the search' do
+ is_expected.to match_array([project.route, project2.route])
+ end
+ end
+
+ context 'when admin mode setting is enabled' do
+ context 'when in admin mode', :enable_admin_mode do
+ it 'finds all projects matching the search' do
+ is_expected.to match_array([project.route, project2.route])
+ end
+ end
+
+ context 'when not in admin mode' do
+ it 'does not find all projects' do
+ is_expected.to match_array([])
+ end
+ end
end
end
end
diff --git a/spec/finders/branches_finder_spec.rb b/spec/finders/branches_finder_spec.rb
index f14c60c4b8f..18f8d1adecc 100644
--- a/spec/finders/branches_finder_spec.rb
+++ b/spec/finders/branches_finder_spec.rb
@@ -72,16 +72,6 @@ RSpec.describe BranchesFinder do
end
end
- context 'with an unknown name' do
- let(:params) { { search: 'random' } }
-
- it 'does not find any branch' do
- result = subject
-
- expect(result.count).to eq(0)
- end
- end
-
context 'by provided names' do
let(:params) { { names: %w[fix csv lfs does-not-exist] } }
@@ -115,6 +105,49 @@ RSpec.describe BranchesFinder do
end
end
+ context 'by name with wildcard' do
+ let(:params) { { search: 'f*e' } }
+
+ it 'filters branches' do
+ result = subject
+
+ expect(result.first.name).to eq('2-mb-file')
+ expect(result.count).to eq(30)
+ end
+ end
+
+ context 'by mixed regex operators' do
+ let(:params) { { search: '^f*e$' } }
+
+ it 'filters branches' do
+ result = subject
+
+ expect(result.first.name).to eq('feature')
+ expect(result.count).to eq(1)
+ end
+ end
+
+ context 'by name with multiple wildcards' do
+ let(:params) { { search: 'f*a*e' } }
+
+ it 'filters branches' do
+ result = subject
+
+ expect(result.first.name).to eq('after-create-delete-modify-move')
+ expect(result.count).to eq(11)
+ end
+ end
+
+ context 'with an unknown name' do
+ let(:params) { { search: 'random' } }
+
+ it 'does not find any branch' do
+ result = subject
+
+ expect(result.count).to eq(0)
+ end
+ end
+
context 'by nonexistent name that begins with' do
let(:params) { { search: '^nope' } }
@@ -134,6 +167,16 @@ RSpec.describe BranchesFinder do
expect(result.count).to eq(0)
end
end
+
+ context 'by nonexistent name with wildcard' do
+ let(:params) { { search: 'zz*asdf' } }
+
+ it 'filters branches' do
+ result = subject
+
+ expect(result.count).to eq(0)
+ end
+ end
end
context 'filter and sort' do
diff --git a/spec/finders/ci/auth_job_finder_spec.rb b/spec/finders/ci/auth_job_finder_spec.rb
index 0a326699875..73a65d0c5af 100644
--- a/spec/finders/ci/auth_job_finder_spec.rb
+++ b/spec/finders/ci/auth_job_finder_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::AuthJobFinder do
+RSpec.describe Ci::AuthJobFinder, feature_category: :continuous_integration do
let_it_be(:user, reload: true) { create(:user) }
let_it_be(:job, reload: true) { create(:ci_build, status: :running, user: user) }
@@ -68,7 +68,7 @@ RSpec.describe Ci::AuthJobFinder do
it 'sets ci_job_token_scope on the job user', :aggregate_failures do
expect(subject).to eq(job)
expect(subject.user).to be_from_ci_job_token
- expect(subject.user.ci_job_token_scope.source_project).to eq(job.project)
+ expect(subject.user.ci_job_token_scope.current_project).to eq(job.project)
end
end
end
diff --git a/spec/finders/ci/freeze_periods_finder_spec.rb b/spec/finders/ci/freeze_periods_finder_spec.rb
new file mode 100644
index 00000000000..6c58028a221
--- /dev/null
+++ b/spec/finders/ci/freeze_periods_finder_spec.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::FreezePeriodsFinder, feature_category: :release_orchestration do
+ subject(:finder) { described_class.new(project, user).execute }
+
+ let(:project) { create(:project, :private) }
+ let(:user) { create(:user) }
+ let!(:freeze_period_1) { create(:ci_freeze_period, project: project, created_at: 2.days.ago) }
+ let!(:freeze_period_2) { create(:ci_freeze_period, project: project, created_at: 1.day.ago) }
+
+ shared_examples_for 'returns nothing' do
+ specify do
+ is_expected.to be_empty
+ end
+ end
+
+ shared_examples_for 'returns freeze_periods ordered by created_at asc' do
+ it 'returns freeze_periods ordered by created_at' do
+ expect(subject.count).to eq(2)
+ expect(subject.pluck('id')).to eq([freeze_period_1.id, freeze_period_2.id])
+ end
+ end
+
+ context 'when user is a maintainer' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ it_behaves_like 'returns freeze_periods ordered by created_at asc'
+ end
+
+ context 'when user is a guest' do
+ before do
+ project.add_guest(user)
+ end
+
+ it_behaves_like 'returns nothing'
+ end
+
+ context 'when user is a developer' do
+ before do
+ project.add_developer(user)
+ end
+
+ it_behaves_like 'returns nothing'
+ end
+
+ context 'when user is not a project member' do
+ it_behaves_like 'returns nothing'
+
+ context 'when project is public' do
+ let(:project) { create(:project, :public) }
+
+ it_behaves_like 'returns nothing'
+ end
+ end
+end
diff --git a/spec/finders/ci/jobs_finder_spec.rb b/spec/finders/ci/jobs_finder_spec.rb
index dd3ba9721e4..0b3777a2fe8 100644
--- a/spec/finders/ci/jobs_finder_spec.rb
+++ b/spec/finders/ci/jobs_finder_spec.rb
@@ -14,52 +14,55 @@ RSpec.describe Ci::JobsFinder, '#execute' do
let(:params) { {} }
context 'no project' do
- subject { described_class.new(current_user: admin, params: params).execute }
+ subject { described_class.new(current_user: current_user, params: params).execute }
- it 'returns all jobs' do
- expect(subject).to match_array([pending_job, running_job, successful_job])
- end
+ context 'with admin' do
+ let(:current_user) { admin }
- context 'non admin user' do
- let(:admin) { user }
+ context 'when admin mode setting is disabled', :do_not_mock_admin_mode_setting do
+ it { is_expected.to match_array([pending_job, running_job, successful_job]) }
+ end
- it 'returns no jobs' do
- expect(subject).to be_empty
+ context 'when admin mode setting is enabled' do
+ context 'when in admin mode', :enable_admin_mode do
+ it { is_expected.to match_array([pending_job, running_job, successful_job]) }
+ end
+
+ context 'when not in admin mode' do
+ it { is_expected.to be_empty }
+ end
end
end
+ context 'with normal user' do
+ let(:current_user) { user }
+
+ it { is_expected.to be_empty }
+ end
+
context 'without user' do
- let(:admin) { nil }
+ let(:current_user) { nil }
- it 'returns no jobs' do
- expect(subject).to be_empty
- end
+ it { is_expected.to be_empty }
end
- context 'scope is present' do
+ context 'with scope', :enable_admin_mode do
+ let(:current_user) { admin }
let(:jobs) { [pending_job, running_job, successful_job] }
- where(:scope, :index) do
- [
- ['pending', 0],
- ['running', 1],
- ['finished', 2]
- ]
+ using RSpec::Parameterized::TableSyntax
+
+ where(:scope, :expected_jobs) do
+ 'pending' | lazy { [pending_job] }
+ 'running' | lazy { [running_job] }
+ 'finished' | lazy { [successful_job] }
+ %w[running success] | lazy { [running_job, successful_job] }
end
with_them do
let(:params) { { scope: scope } }
- it { expect(subject).to match_array([jobs[index]]) }
- end
- end
-
- context 'scope is an array' do
- let(:jobs) { [pending_job, running_job, successful_job, canceled_job] }
- let(:params) { { scope: %w'running success' } }
-
- it 'filters by the job statuses in the scope' do
- expect(subject).to contain_exactly(running_job, successful_job)
+ it { is_expected.to match_array(expected_jobs) }
end
end
end
@@ -96,6 +99,33 @@ RSpec.describe Ci::JobsFinder, '#execute' do
end
end
+ context 'when artifacts are present for some jobs' do
+ let_it_be(:job_with_artifacts) { create(:ci_build, :success, pipeline: pipeline, name: 'test') }
+ let_it_be(:artifact) { create(:ci_job_artifact, job: job_with_artifacts) }
+
+ subject { described_class.new(current_user: user, project: project, params: params).execute }
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ context 'when with_artifacts is true' do
+ let(:params) { { with_artifacts: true } }
+
+ it 'returns only jobs with artifacts' do
+ expect(subject).to match_array([job_with_artifacts])
+ end
+ end
+
+ context 'when with_artifacts is false' do
+ let(:params) { { with_artifacts: false } }
+
+ it 'returns all jobs' do
+ expect(subject).to match_array([successful_job, job_with_artifacts])
+ end
+ end
+ end
+
context 'when pipeline is present' do
before_all do
project.add_maintainer(user)
diff --git a/spec/finders/ci/pipelines_finder_spec.rb b/spec/finders/ci/pipelines_finder_spec.rb
index 908210e0296..a2e8fe8df5a 100644
--- a/spec/finders/ci/pipelines_finder_spec.rb
+++ b/spec/finders/ci/pipelines_finder_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Ci::PipelinesFinder do
- let(:project) { create(:project, :public, :repository) }
+ let_it_be(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
let(:params) { {} }
@@ -242,6 +242,45 @@ RSpec.describe Ci::PipelinesFinder do
end
end
+ context 'when name is specified' do
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project, name: 'Build pipeline') }
+ let_it_be(:pipeline_other) { create(:ci_pipeline, project: project, name: 'Some other pipeline') }
+
+ let(:params) { { name: 'build Pipeline' } }
+
+ it 'performs case insensitive compare' do
+ is_expected.to contain_exactly(pipeline)
+ end
+
+ context 'when name does not exist' do
+ let(:params) { { name: 'absent-name' } }
+
+ it 'returns empty' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when pipeline_name feature flag is off' do
+ before do
+ stub_feature_flags(pipeline_name: false)
+ end
+
+ it 'ignores name parameter' do
+ is_expected.to contain_exactly(pipeline, pipeline_other)
+ end
+ end
+
+ context 'when pipeline_name_search feature flag is off' do
+ before do
+ stub_feature_flags(pipeline_name_search: false)
+ end
+
+ it 'ignores name parameter' do
+ is_expected.to contain_exactly(pipeline, pipeline_other)
+ end
+ end
+ end
+
describe 'ordering' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/finders/ci/runners_finder_spec.rb b/spec/finders/ci/runners_finder_spec.rb
index 18eecd0f073..a8ef99eeaec 100644
--- a/spec/finders/ci/runners_finder_spec.rb
+++ b/spec/finders/ci/runners_finder_spec.rb
@@ -7,219 +7,221 @@ RSpec.describe Ci::RunnersFinder do
let_it_be(:admin) { create(:user, :admin) }
describe '#execute' do
- context 'with 2 runners' do
- let_it_be(:runner1) { create(:ci_runner, active: true) }
- let_it_be(:runner2) { create(:ci_runner, active: false) }
-
- context 'with empty params' do
- it 'returns all runners' do
- expect(Ci::Runner).to receive(:with_tags).and_call_original
- expect(described_class.new(current_user: admin, params: {}).execute).to match_array [runner1, runner2]
+ shared_examples 'executes as admin' do
+ context 'with 2 runners' do
+ let_it_be(:runner1) { create(:ci_runner, active: true) }
+ let_it_be(:runner2) { create(:ci_runner, active: false) }
+
+ context 'with empty params' do
+ it 'returns all runners' do
+ expect(Ci::Runner).to receive(:with_tags).and_call_original
+ expect(described_class.new(current_user: admin, params: {}).execute).to match_array [runner1, runner2]
+ end
end
- end
- context 'with nil group' do
- it 'returns all runners' do
- expect(Ci::Runner).to receive(:with_tags).and_call_original
- expect(described_class.new(current_user: admin, params: { group: nil }).execute).to match_array [runner1, runner2]
+ context 'with nil group' do
+ it 'returns all runners' do
+ expect(Ci::Runner).to receive(:with_tags).and_call_original
+ expect(described_class.new(current_user: admin, params: { group: nil }).execute).to match_array [runner1, runner2]
+ end
end
- end
- context 'with preload param set to :tag_name true' do
- it 'requests tags' do
- expect(Ci::Runner).to receive(:with_tags).and_call_original
- expect(described_class.new(current_user: admin, params: { preload: { tag_name: true } }).execute).to match_array [runner1, runner2]
+ context 'with preload param set to :tag_name true' do
+ it 'requests tags' do
+ expect(Ci::Runner).to receive(:with_tags).and_call_original
+ expect(described_class.new(current_user: admin, params: { preload: { tag_name: true } }).execute).to match_array [runner1, runner2]
+ end
end
- end
- context 'with preload param set to :tag_name false' do
- it 'does not request tags' do
- expect(Ci::Runner).not_to receive(:with_tags)
- expect(described_class.new(current_user: admin, params: { preload: { tag_name: false } }).execute).to match_array [runner1, runner2]
+ context 'with preload param set to :tag_name false' do
+ it 'does not request tags' do
+ expect(Ci::Runner).not_to receive(:with_tags)
+ expect(described_class.new(current_user: admin, params: { preload: { tag_name: false } }).execute).to match_array [runner1, runner2]
+ end
end
end
- end
- context 'filtering' do
- context 'by search term' do
- it 'calls Ci::Runner.search' do
- expect(Ci::Runner).to receive(:search).with('term').and_call_original
+ context 'filtering' do
+ context 'by search term' do
+ it 'calls Ci::Runner.search' do
+ expect(Ci::Runner).to receive(:search).with('term').and_call_original
- described_class.new(current_user: admin, params: { search: 'term' }).execute
+ described_class.new(current_user: admin, params: { search: 'term' }).execute
+ end
end
- end
- context 'by upgrade status' do
- let(:upgrade_status) {}
+ context 'by upgrade status' do
+ let(:upgrade_status) {}
- let_it_be(:runner1) { create(:ci_runner, version: 'a') }
- let_it_be(:runner2) { create(:ci_runner, version: 'b') }
- let_it_be(:runner3) { create(:ci_runner, version: 'c') }
- let_it_be(:runner_version_recommended) do
- create(:ci_runner_version, version: 'a', status: :recommended)
- end
+ let_it_be(:runner1) { create(:ci_runner, version: 'a') }
+ let_it_be(:runner2) { create(:ci_runner, version: 'b') }
+ let_it_be(:runner3) { create(:ci_runner, version: 'c') }
+ let_it_be(:runner_version_recommended) do
+ create(:ci_runner_version, version: 'a', status: :recommended)
+ end
- let_it_be(:runner_version_not_available) do
- create(:ci_runner_version, version: 'b', status: :not_available)
- end
+ let_it_be(:runner_version_not_available) do
+ create(:ci_runner_version, version: 'b', status: :not_available)
+ end
- let_it_be(:runner_version_available) do
- create(:ci_runner_version, version: 'c', status: :available)
- end
+ let_it_be(:runner_version_available) do
+ create(:ci_runner_version, version: 'c', status: :available)
+ end
- def execute
- described_class.new(current_user: admin, params: { upgrade_status: upgrade_status }).execute
- end
+ def execute
+ described_class.new(current_user: admin, params: { upgrade_status: upgrade_status }).execute
+ end
- Ci::RunnerVersion.statuses.keys.map(&:to_sym).each do |status|
- context "set to :#{status}" do
- let(:upgrade_status) { status }
+ Ci::RunnerVersion.statuses.keys.map(&:to_sym).each do |status|
+ context "set to :#{status}" do
+ let(:upgrade_status) { status }
- it "calls with_upgrade_status scope with corresponding :#{status} status" do
- if [:available, :not_available, :recommended].include?(status)
- expected_result = Ci::Runner.with_upgrade_status(status)
- end
+ it "calls with_upgrade_status scope with corresponding :#{status} status" do
+ if [:available, :not_available, :recommended].include?(status)
+ expected_result = Ci::Runner.with_upgrade_status(status)
+ end
- expect(Ci::Runner).to receive(:with_upgrade_status).with(status).and_call_original
+ expect(Ci::Runner).to receive(:with_upgrade_status).with(status).and_call_original
- result = execute
+ result = execute
- expect(result).to match_array(expected_result) if expected_result
+ expect(result).to match_array(expected_result) if expected_result
+ end
end
end
- end
- context 'set to an invalid value' do
- let(:upgrade_status) { :some_invalid_status }
+ context 'set to an invalid value' do
+ let(:upgrade_status) { :some_invalid_status }
- it 'raises ArgumentError' do
- expect { execute }.to raise_error(ArgumentError)
+ it 'raises ArgumentError' do
+ expect { execute }.to raise_error(ArgumentError)
+ end
end
- end
- context 'set to nil' do
- let(:upgrade_status) { nil }
+ context 'set to nil' do
+ let(:upgrade_status) { nil }
- it 'does not call with_upgrade_status' do
- expect(Ci::Runner).not_to receive(:with_upgrade_status)
+ it 'does not call with_upgrade_status' do
+ expect(Ci::Runner).not_to receive(:with_upgrade_status)
- expect(execute).to match_array(Ci::Runner.all)
+ expect(execute).to match_array(Ci::Runner.all)
+ end
end
end
- end
- context 'by status' do
- Ci::Runner::AVAILABLE_STATUSES.each do |status|
- it "calls the corresponding :#{status} scope on Ci::Runner" do
- expect(Ci::Runner).to receive(status.to_sym).and_call_original
+ context 'by status' do
+ Ci::Runner::AVAILABLE_STATUSES.each do |status|
+ it "calls the corresponding :#{status} scope on Ci::Runner" do
+ expect(Ci::Runner).to receive(status.to_sym).and_call_original
- described_class.new(current_user: admin, params: { status_status: status }).execute
+ described_class.new(current_user: admin, params: { status_status: status }).execute
+ end
end
end
- end
- context 'by active status' do
- it 'with active set as false calls the corresponding scope on Ci::Runner with false' do
- expect(Ci::Runner).to receive(:active).with(false).and_call_original
+ context 'by active status' do
+ it 'with active set as false calls the corresponding scope on Ci::Runner with false' do
+ expect(Ci::Runner).to receive(:active).with(false).and_call_original
- described_class.new(current_user: admin, params: { active: false }).execute
- end
+ described_class.new(current_user: admin, params: { active: false }).execute
+ end
- it 'with active set as true calls the corresponding scope on Ci::Runner with true' do
- expect(Ci::Runner).to receive(:active).with(true).and_call_original
+ it 'with active set as true calls the corresponding scope on Ci::Runner with true' do
+ expect(Ci::Runner).to receive(:active).with(true).and_call_original
- described_class.new(current_user: admin, params: { active: true }).execute
+ described_class.new(current_user: admin, params: { active: true }).execute
+ end
end
- end
- context 'by runner type' do
- it 'calls the corresponding scope on Ci::Runner' do
- expect(Ci::Runner).to receive(:project_type).and_call_original
+ context 'by runner type' do
+ it 'calls the corresponding scope on Ci::Runner' do
+ expect(Ci::Runner).to receive(:project_type).and_call_original
- described_class.new(current_user: admin, params: { type_type: 'project_type' }).execute
+ described_class.new(current_user: admin, params: { type_type: 'project_type' }).execute
+ end
end
- end
- context 'by tag_name' do
- it 'calls the corresponding scope on Ci::Runner' do
- expect(Ci::Runner).to receive(:tagged_with).with(%w[tag1 tag2]).and_call_original
+ context 'by tag_name' do
+ it 'calls the corresponding scope on Ci::Runner' do
+ expect(Ci::Runner).to receive(:tagged_with).with(%w[tag1 tag2]).and_call_original
- described_class.new(current_user: admin, params: { tag_name: %w[tag1 tag2] }).execute
+ described_class.new(current_user: admin, params: { tag_name: %w[tag1 tag2] }).execute
+ end
end
end
- end
- context 'sorting' do
- let_it_be(:runner1) { create :ci_runner, created_at: '2018-07-12 07:00', contacted_at: 1.minute.ago, token_expires_at: '2022-02-15 07:00' }
- let_it_be(:runner2) { create :ci_runner, created_at: '2018-07-12 08:00', contacted_at: 3.minutes.ago, token_expires_at: '2022-02-15 06:00' }
- let_it_be(:runner3) { create :ci_runner, created_at: '2018-07-12 09:00', contacted_at: 2.minutes.ago }
+ context 'sorting' do
+ let_it_be(:runner1) { create :ci_runner, created_at: '2018-07-12 07:00', contacted_at: 1.minute.ago, token_expires_at: '2022-02-15 07:00' }
+ let_it_be(:runner2) { create :ci_runner, created_at: '2018-07-12 08:00', contacted_at: 3.minutes.ago, token_expires_at: '2022-02-15 06:00' }
+ let_it_be(:runner3) { create :ci_runner, created_at: '2018-07-12 09:00', contacted_at: 2.minutes.ago }
- subject do
- described_class.new(current_user: admin, params: params).execute
- end
+ subject do
+ described_class.new(current_user: admin, params: params).execute
+ end
- shared_examples 'sorts by created_at descending' do
- it 'sorts by created_at descending' do
- is_expected.to eq [runner3, runner2, runner1]
+ shared_examples 'sorts by created_at descending' do
+ it 'sorts by created_at descending' do
+ is_expected.to eq [runner3, runner2, runner1]
+ end
end
- end
- context 'without sort param' do
- let(:params) { {} }
+ context 'without sort param' do
+ let(:params) { {} }
- it_behaves_like 'sorts by created_at descending'
- end
+ it_behaves_like 'sorts by created_at descending'
+ end
- %w(created_date created_at_desc).each do |sort|
- context "with sort param equal to #{sort}" do
- let(:params) { { sort: sort } }
+ %w(created_date created_at_desc).each do |sort|
+ context "with sort param equal to #{sort}" do
+ let(:params) { { sort: sort } }
- it_behaves_like 'sorts by created_at descending'
+ it_behaves_like 'sorts by created_at descending'
+ end
end
- end
- context 'with sort param equal to created_at_asc' do
- let(:params) { { sort: 'created_at_asc' } }
+ context 'with sort param equal to created_at_asc' do
+ let(:params) { { sort: 'created_at_asc' } }
- it 'sorts by created_at ascending' do
- is_expected.to eq [runner1, runner2, runner3]
+ it 'sorts by created_at ascending' do
+ is_expected.to eq [runner1, runner2, runner3]
+ end
end
- end
- context 'with sort param equal to contacted_asc' do
- let(:params) { { sort: 'contacted_asc' } }
+ context 'with sort param equal to contacted_asc' do
+ let(:params) { { sort: 'contacted_asc' } }
- it 'sorts by contacted_at ascending' do
- is_expected.to eq [runner2, runner3, runner1]
+ it 'sorts by contacted_at ascending' do
+ is_expected.to eq [runner2, runner3, runner1]
+ end
end
- end
- context 'with sort param equal to contacted_desc' do
- let(:params) { { sort: 'contacted_desc' } }
+ context 'with sort param equal to contacted_desc' do
+ let(:params) { { sort: 'contacted_desc' } }
- it 'sorts by contacted_at descending' do
- is_expected.to eq [runner1, runner3, runner2]
+ it 'sorts by contacted_at descending' do
+ is_expected.to eq [runner1, runner3, runner2]
+ end
end
- end
- context 'with sort param equal to token_expires_at_asc' do
- let(:params) { { sort: 'token_expires_at_asc' } }
+ context 'with sort param equal to token_expires_at_asc' do
+ let(:params) { { sort: 'token_expires_at_asc' } }
- it 'sorts by contacted_at ascending' do
- is_expected.to eq [runner2, runner1, runner3]
+ it 'sorts by contacted_at ascending' do
+ is_expected.to eq [runner2, runner1, runner3]
+ end
end
- end
- context 'with sort param equal to token_expires_at_desc' do
- let(:params) { { sort: 'token_expires_at_desc' } }
+ context 'with sort param equal to token_expires_at_desc' do
+ let(:params) { { sort: 'token_expires_at_desc' } }
- it 'sorts by contacted_at descending' do
- is_expected.to eq [runner3, runner1, runner2]
+ it 'sorts by contacted_at descending' do
+ is_expected.to eq [runner3, runner1, runner2]
+ end
end
end
end
- context 'by non admin user' do
+ shared_examples 'executes as normal user' do
it 'returns no runners' do
user = create :user
create :ci_runner, active: true
@@ -229,6 +231,24 @@ RSpec.describe Ci::RunnersFinder do
end
end
+ context 'when admin mode setting is disabled', :do_not_mock_admin_mode_setting do
+ it_behaves_like 'executes as admin'
+ end
+
+ context 'when admin mode setting is enabled' do
+ context 'when in admin mode', :enable_admin_mode do
+ it_behaves_like 'executes as admin'
+ end
+
+ context 'when not in admin mode' do
+ it_behaves_like 'executes as normal user'
+ end
+ end
+
+ context 'by non admin user' do
+ it_behaves_like 'executes as normal user'
+ end
+
context 'when user is nil' do
it 'returns no runners' do
user = nil
@@ -473,4 +493,153 @@ RSpec.describe Ci::RunnersFinder do
end
end
end
+
+ context 'project' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:other_project) { create(:project) }
+
+ let(:extra_params) { {} }
+ let(:params) { { project: project }.merge(extra_params).reject { |_, v| v.nil? } }
+
+ describe '#execute' do
+ subject { described_class.new(current_user: user, params: params).execute }
+
+ context 'with user as project admin' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ context 'with project runners' do
+ let_it_be(:runner_project) { create(:ci_runner, :project, contacted_at: 7.minutes.ago, projects: [project]) }
+
+ it 'returns runners available to project' do
+ expect(subject).to match_array([runner_project])
+ end
+ end
+
+ context 'with ancestor group runners' do
+ let_it_be(:runner_instance) { create(:ci_runner, contacted_at: 13.minutes.ago) }
+ let_it_be(:runner_group) { create(:ci_runner, :group, contacted_at: 12.minutes.ago, groups: [group]) }
+
+ it 'returns runners available to project' do
+ expect(subject).to match_array([runner_instance, runner_group])
+ end
+ end
+
+ context 'with allowed shared runners' do
+ let_it_be(:runner_instance) { create(:ci_runner, :instance, contacted_at: 13.minutes.ago) }
+
+ it 'returns runners available to project' do
+ expect(subject).to match_array([runner_instance])
+ end
+ end
+
+ context 'with project, ancestor group, and allowed shared runners' do
+ let_it_be(:runner_project) { create(:ci_runner, :project, contacted_at: 7.minutes.ago, projects: [project]) }
+ let_it_be(:runner_group) { create(:ci_runner, :group, contacted_at: 12.minutes.ago, groups: [group]) }
+ let_it_be(:runner_instance) { create(:ci_runner, :instance, contacted_at: 13.minutes.ago) }
+
+ it 'returns runners available to project' do
+ expect(subject).to match_array([runner_project, runner_group, runner_instance])
+ end
+ end
+
+ context 'filtering' do
+ let_it_be(:runner_instance_inactive) { create(:ci_runner, :instance, active: false, contacted_at: 13.minutes.ago) }
+ let_it_be(:runner_instance_active) { create(:ci_runner, :instance, active: true, contacted_at: 13.minutes.ago) }
+ let_it_be(:runner_project_active) { create(:ci_runner, :project, contacted_at: 5.minutes.ago, active: true, projects: [project]) }
+ let_it_be(:runner_project_inactive) { create(:ci_runner, :project, contacted_at: 5.minutes.ago, active: false, projects: [project]) }
+ let_it_be(:runner_other_project_inactive) { create(:ci_runner, :project, contacted_at: 5.minutes.ago, active: false, projects: [other_project]) }
+
+ context 'by search term' do
+ let_it_be(:runner_project_1) { create(:ci_runner, :project, contacted_at: 5.minutes.ago, description: 'runner_project_search', projects: [project]) }
+ let_it_be(:runner_project_2) { create(:ci_runner, :project, contacted_at: 5.minutes.ago, description: 'runner_project', projects: [project]) }
+ let_it_be(:runner_another_project) { create(:ci_runner, :project, contacted_at: 5.minutes.ago, description: 'runner_project_search', projects: [other_project]) }
+
+ let(:extra_params) { { search: 'runner_project_search' } }
+
+ it 'returns the correct runner' do
+ expect(subject).to match_array([runner_project_1])
+ end
+ end
+
+ context 'by active status' do
+ let(:extra_params) { { active: false } }
+
+ it 'returns the correct runners' do
+ expect(subject).to match_array([runner_instance_inactive, runner_project_inactive])
+ end
+ end
+
+ context 'by status' do
+ let(:extra_params) { { status_status: 'paused' } }
+
+ it 'returns correct runner' do
+ expect(subject).to match_array([runner_instance_inactive, runner_project_inactive])
+ end
+ end
+
+ context 'by tag_name' do
+ let_it_be(:runner_project_1) { create(:ci_runner, :project, contacted_at: 3.minutes.ago, tag_list: %w[runner_tag], projects: [project]) }
+ let_it_be(:runner_project_2) { create(:ci_runner, :project, contacted_at: 3.minutes.ago, tag_list: %w[other_tag], projects: [project]) }
+ let_it_be(:runner_other_project) { create(:ci_runner, :project, contacted_at: 3.minutes.ago, tag_list: %w[runner_tag], projects: [other_project]) }
+
+ let(:extra_params) { { tag_name: %w[runner_tag] } }
+
+ it 'returns correct runner' do
+ expect(subject).to match_array([runner_project_1])
+ end
+ end
+
+ context 'by runner type' do
+ let(:extra_params) { { type_type: 'project_type' } }
+
+ it 'returns correct runners' do
+ expect(subject).to match_array([runner_project_active, runner_project_inactive])
+ end
+ end
+ end
+ end
+
+ context 'with user as project developer' do
+ let(:user) { create(:user) }
+
+ before do
+ project.add_developer(user)
+ end
+
+ it 'returns no runners' do
+ expect(subject).to be_empty
+ end
+ end
+
+ context 'when user is nil' do
+ let_it_be(:user) { nil }
+
+ it 'returns no runners' do
+ expect(subject).to be_empty
+ end
+ end
+
+ context 'with nil project_full_path' do
+ let(:project_full_path) { nil }
+
+ it 'returns no runners' do
+ expect(subject).to be_empty
+ end
+ end
+
+ context 'when on_demand_scans_runner_tags feature flag is disabled' do
+ before do
+ stub_feature_flags(on_demand_scans_runner_tags: false)
+ end
+
+ it 'returns no runners' do
+ expect(subject).to be_empty
+ end
+ end
+ end
+ end
end
diff --git a/spec/finders/clusters/agent_tokens_finder_spec.rb b/spec/finders/clusters/agent_tokens_finder_spec.rb
index 619aca891c1..024e567a16e 100644
--- a/spec/finders/clusters/agent_tokens_finder_spec.rb
+++ b/spec/finders/clusters/agent_tokens_finder_spec.rb
@@ -5,24 +5,43 @@ require 'spec_helper'
RSpec.describe Clusters::AgentTokensFinder do
describe '#execute' do
let_it_be(:project) { create(:project) }
+ let_it_be(:agent) { create(:cluster_agent, project: project) }
let(:user) { create(:user, maintainer_projects: [project]) }
- let(:agent) { create(:cluster_agent, project: project) }
- let(:agent_id) { agent.id }
- let!(:matching_agent_tokens) do
+ let_it_be(:active_agent_tokens) do
[
create(:cluster_agent_token, agent: agent),
- create(:cluster_agent_token, :revoked, agent: agent)
+ create(:cluster_agent_token, agent: agent)
]
end
- subject(:execute) { described_class.new(project, user, agent_id).execute }
+ let_it_be(:revoked_agent_tokens) do
+ [
+ create(:cluster_agent_token, :revoked, agent: agent),
+ create(:cluster_agent_token, :revoked, agent: agent)
+ ]
+ end
- it 'returns the tokens of the specified agent' do
- # creating a token in a different agent to make sure it will not be included in the result
+ before_all do
+ # set up a token under a different agent as a way to verify
+ # that only tokens of a given agent are included in the result
create(:cluster_agent_token, agent: create(:cluster_agent))
+ end
+
+ subject(:execute) { described_class.new(agent, user).execute }
+
+ it { is_expected.to match_array(active_agent_tokens + revoked_agent_tokens) }
- expect(execute).to match_array(matching_agent_tokens)
+ context 'when filtering by status=active' do
+ subject(:execute) { described_class.new(agent, user, status: 'active').execute }
+
+ it { is_expected.to match_array(active_agent_tokens) }
+ end
+
+ context 'when filtering by status=revoked' do
+ subject(:execute) { described_class.new(agent, user, status: 'revoked').execute }
+
+ it { is_expected.to match_array(revoked_agent_tokens) }
end
context 'when user does not have permission' do
@@ -32,16 +51,20 @@ RSpec.describe Clusters::AgentTokensFinder do
project.add_reporter(user)
end
- it 'raises an error' do
- expect { execute }.to raise_error(ActiveRecord::RecordNotFound)
- end
+ it { is_expected.to eq ::Clusters::AgentToken.none }
end
- context 'when agent does not exist' do
- let(:agent_id) { non_existing_record_id }
+ context 'when current_user is nil' do
+ it 'returns an empty list' do
+ result = described_class.new(agent, nil).execute
+ expect(result).to eq ::Clusters::AgentToken.none
+ end
+ end
- it 'raises an error' do
- expect { execute }.to raise_error(ActiveRecord::RecordNotFound)
+ context 'when agent is nil' do
+ it 'returns an empty list' do
+ result = described_class.new(nil, user).execute
+ expect(result).to eq ::Clusters::AgentToken.none
end
end
end
diff --git a/spec/finders/environments/environments_finder_spec.rb b/spec/finders/environments/environments_finder_spec.rb
index 04fbd4067b4..df66bbdc235 100644
--- a/spec/finders/environments/environments_finder_spec.rb
+++ b/spec/finders/environments/environments_finder_spec.rb
@@ -51,15 +51,35 @@ RSpec.describe Environments::EnvironmentsFinder do
end
context 'with search and states' do
+ let_it_be(:environment_available_b) { create(:environment, :available, name: 'test/foldered-env', project: project) }
+
it 'searches environments by name and state' do
result = described_class.new(project, user, search: 'test', states: :available).execute
- expect(result).to contain_exactly(environment_available)
+ expect(result).to contain_exactly(environment_available, environment_available_b)
+ end
+
+ it 'searches environments by name inside folder and state' do
+ result = described_class.new(project, user, search: 'folder', states: :available).execute
+
+ expect(result).to contain_exactly(environment_available_b)
+ end
+
+ context 'when enable_environments_search_within_folder FF is disabled' do
+ before do
+ stub_feature_flags(enable_environments_search_within_folder: false)
+ end
+
+ it 'ignores name inside folder' do
+ result = described_class.new(project, user, search: 'folder', states: :available).execute
+
+ expect(result).to be_empty
+ end
end
end
context 'with id' do
- it 'searches environments by name and state' do
+ it 'searches environments by name and id' do
result = described_class.new(project, user, search: 'test', environment_ids: [environment_available.id]).execute
expect(result).to contain_exactly(environment_available)
diff --git a/spec/finders/freeze_periods_finder_spec.rb b/spec/finders/freeze_periods_finder_spec.rb
deleted file mode 100644
index 53cc07d91b0..00000000000
--- a/spec/finders/freeze_periods_finder_spec.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe FreezePeriodsFinder do
- subject(:finder) { described_class.new(project, user).execute }
-
- let(:project) { create(:project, :private) }
- let(:user) { create(:user) }
- let!(:freeze_period_1) { create(:ci_freeze_period, project: project, created_at: 2.days.ago) }
- let!(:freeze_period_2) { create(:ci_freeze_period, project: project, created_at: 1.day.ago) }
-
- shared_examples_for 'returns nothing' do
- specify do
- is_expected.to be_empty
- end
- end
-
- shared_examples_for 'returns freeze_periods ordered by created_at asc' do
- it 'returns freeze_periods ordered by created_at' do
- expect(subject.count).to eq(2)
- expect(subject.pluck('id')).to eq([freeze_period_1.id, freeze_period_2.id])
- end
- end
-
- context 'when user is a maintainer' do
- before do
- project.add_maintainer(user)
- end
-
- it_behaves_like 'returns freeze_periods ordered by created_at asc'
- end
-
- context 'when user is a guest' do
- before do
- project.add_guest(user)
- end
-
- it_behaves_like 'returns nothing'
- end
-
- context 'when user is a developer' do
- before do
- project.add_developer(user)
- end
-
- it_behaves_like 'returns nothing'
- end
-
- context 'when user is not a project member' do
- it_behaves_like 'returns nothing'
-
- context 'when project is public' do
- let(:project) { create(:project, :public) }
-
- it_behaves_like 'returns nothing'
- end
- end
-end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 704171a737b..43d66d285fa 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe IssuesFinder do
+RSpec.describe IssuesFinder, feature_category: :team_planning do
include_context 'IssuesFinder context'
it_behaves_like 'issues or work items finder', :issue, 'IssuesFinder#execute context'
diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb
index 11de19cfdbc..61be90b267a 100644
--- a/spec/finders/notes_finder_spec.rb
+++ b/spec/finders/notes_finder_spec.rb
@@ -328,6 +328,16 @@ RSpec.describe NotesFinder do
it 'returns the commit' do
expect(subject.target).to eq(commit)
end
+
+ context 'user does not have permission to read_code' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :read_code, project).and_return false
+ end
+
+ it 'returns nil' do
+ expect(subject.target).to be_nil
+ end
+ end
end
context 'target_iid' do
diff --git a/spec/finders/personal_access_tokens_finder_spec.rb b/spec/finders/personal_access_tokens_finder_spec.rb
index 21380cb6632..bcd5aef84f9 100644
--- a/spec/finders/personal_access_tokens_finder_spec.rb
+++ b/spec/finders/personal_access_tokens_finder_spec.rb
@@ -2,359 +2,320 @@
require 'spec_helper'
-RSpec.describe PersonalAccessTokensFinder do
- def finder(options = {}, current_user = nil)
- described_class.new(options, current_user)
- end
-
- describe '# searches PATs' do
- using RSpec::Parameterized::TableSyntax
+RSpec.describe PersonalAccessTokensFinder, :enable_admin_mode do
+ using RSpec::Parameterized::TableSyntax
- let_it_be(:time_token) do
- create(:personal_access_token, created_at: DateTime.new(2022, 01, 02),
- last_used_at: DateTime.new(2022, 01, 02))
+ describe '#execute' do
+ let(:admin) { create(:admin) }
+ let(:user) { create(:user) }
+ let(:other_user) { create(:user) }
+ let(:project_bot) { create(:user, :project_bot) }
+
+ let!(:tokens) do
+ {
+ active: create(:personal_access_token, user: user, name: 'my_pat_1'),
+ active_other: create(:personal_access_token, user: other_user, name: 'my_pat_2'),
+ expired: create(:personal_access_token, :expired, user: user),
+ revoked: create(:personal_access_token, :revoked, user: user),
+ active_impersonation: create(:personal_access_token, :impersonation, user: user),
+ expired_impersonation: create(:personal_access_token, :expired, :impersonation, user: user),
+ revoked_impersonation: create(:personal_access_token, :revoked, :impersonation, user: user),
+ bot: create(:personal_access_token, user: project_bot)
+ }
end
- let_it_be(:name_token) { create(:personal_access_token, name: 'test_1') }
-
- let_it_be(:impersonated_token) do
- create(:personal_access_token, :impersonation,
- created_at: DateTime.new(2022, 01, 02),
- last_used_at: DateTime.new(2022, 01, 02),
- name: 'imp_token'
- )
- end
+ let(:params) { {} }
+ let(:current_user) { admin }
- shared_examples 'finding tokens by user and options' do
- subject { finder(option, user).execute }
+ subject { described_class.new(params, current_user).execute }
- it 'finds exactly' do
- subject
+ describe 'by current user' do
+ context 'with no user' do
+ let(:current_user) { nil }
- is_expected.to contain_exactly(*result)
+ it 'returns all tokens' do
+ is_expected.to match_array(tokens.values)
+ end
end
- end
- context 'by' do
- where(:option, :user, :result) do
- { created_before: DateTime.new(2022, 01, 03) } | create(:admin) | lazy { [time_token, impersonated_token] }
- { created_after: DateTime.new(2022, 01, 01) } | create(:admin) | lazy { [time_token, name_token, impersonated_token] }
- { last_used_before: DateTime.new(2022, 01, 03) } | create(:admin) | lazy { [time_token, impersonated_token] }
- { last_used_before: DateTime.new(2022, 01, 03) } | create(:admin) | lazy { [time_token, impersonated_token] }
- { impersonation: true } | create(:admin) | lazy { [impersonated_token] }
- { search: 'test' } | create(:admin) | lazy { [name_token] }
- end
+ context 'with admin' do
+ let(:current_user) { admin }
- with_them do
- it_behaves_like 'finding tokens by user and options'
- end
- end
- end
-
- describe '#execute' do
- let(:user) { create(:user) }
- let(:params) { {} }
- let(:current_user) { nil }
- let!(:active_personal_access_token) { create(:personal_access_token, user: user) }
- let!(:expired_personal_access_token) { create(:personal_access_token, :expired, user: user) }
- let!(:revoked_personal_access_token) { create(:personal_access_token, :revoked, user: user) }
- let!(:active_impersonation_token) { create(:personal_access_token, :impersonation, user: user) }
- let!(:expired_impersonation_token) { create(:personal_access_token, :expired, :impersonation, user: user) }
- let!(:revoked_impersonation_token) { create(:personal_access_token, :revoked, :impersonation, user: user) }
- let!(:project_bot) { create(:user, :project_bot) }
- let!(:project_member) { create(:project_member, user: project_bot) }
- let!(:project_access_token) { create(:personal_access_token, user: project_bot) }
-
- subject { finder(params, current_user).execute }
-
- context 'when current_user is defined' do
- let(:current_user) { create(:admin) }
- let(:params) { { user: user } }
-
- context 'current_user is allowed to read PATs' do
- it do
- is_expected.to contain_exactly(active_personal_access_token, active_impersonation_token,
- revoked_personal_access_token, expired_personal_access_token,
- revoked_impersonation_token, expired_impersonation_token)
+ context 'when admin mode setting is disabled', :do_not_mock_admin_mode_setting do
+ it 'returns all tokens' do
+ is_expected.to match_array(tokens.values)
+ end
end
- end
- context 'current_user is not allowed to read PATs' do
- let(:current_user) { create(:user) }
+ context 'when admin mode setting is enabled' do
+ context 'when in admin mode', :enable_admin_mode do
+ it 'returns all tokens' do
+ is_expected.to match_array(tokens.values)
+ end
+ end
+
+ context 'when not in admin mode' do
+ before do
+ allow_next_instance_of(Gitlab::Auth::CurrentUserMode) do |current_user_mode|
+ allow(current_user_mode).to receive(:admin_mode?).and_return(false)
+ end
+ end
- it { is_expected.to be_empty }
+ it 'returns no tokens' do
+ is_expected.to be_empty
+ end
+ end
+ end
end
- context 'when user param is not set' do
- let(:params) { {} }
+ context 'when user can read user personal access tokens' do
+ let(:params) { { user: user } }
+ let(:current_user) { user }
- it do
- is_expected.to contain_exactly(active_personal_access_token, active_impersonation_token,
- revoked_personal_access_token, expired_personal_access_token,
- revoked_impersonation_token, expired_impersonation_token, project_access_token)
+ it 'returns tokens of user' do
+ is_expected.to contain_exactly(*user.personal_access_tokens)
end
+ end
- context 'when current_user is not an administrator' do
- let(:current_user) { create(:user) }
+ context 'when user can not read user personal access tokens' do
+ let(:params) { { user: other_user } }
+ let(:current_user) { user }
- it { is_expected.to be_empty }
+ it 'returns no tokens' do
+ is_expected.to be_empty
end
end
end
- describe 'without user' do
- it do
- is_expected.to contain_exactly(active_personal_access_token, active_impersonation_token,
- revoked_personal_access_token, expired_personal_access_token,
- revoked_impersonation_token, expired_impersonation_token, project_access_token)
+ describe 'by user' do
+ where(:by_user, :expected_tokens) do
+ nil | tokens.keys
+ ref(:user) | [:active, :expired, :revoked, :active_impersonation, :expired_impersonation, :revoked_impersonation]
+ ref(:other_user) | [:active_other]
+ ref(:admin) | []
end
- describe 'with users' do
- let(:user2) { create(:user) }
-
- before do
- create(:personal_access_token, user: user2)
- create(:personal_access_token, :expired, user: user2)
- create(:personal_access_token, :revoked, user: user2)
- create(:personal_access_token, :impersonation, user: user2)
- create(:personal_access_token, :expired, :impersonation, user: user2)
- create(:personal_access_token, :revoked, :impersonation, user: user2)
+ with_them do
+ let(:params) { { user: by_user } }
- params[:users] = [user]
+ it 'returns tokens by user' do
+ is_expected.to match_array(tokens.values_at(*expected_tokens))
end
-
- it {
- is_expected.to contain_exactly(active_personal_access_token, active_impersonation_token,
- revoked_personal_access_token, expired_personal_access_token,
- revoked_impersonation_token, expired_impersonation_token)
- }
end
+ end
- describe 'with sort order' do
- before do
- params[:sort] = 'id_asc'
- end
-
- it 'sorts records as per the specified sort order' do
- expect(subject).to match_array(PersonalAccessToken.all.order(id: :asc))
- end
+ describe 'by users' do
+ where(:by_users, :expected_tokens) do
+ nil | tokens.keys
+ lazy { [user] } | [:active, :expired, :revoked, :active_impersonation, :expired_impersonation, :revoked_impersonation]
+ lazy { [other_user] } | [:active_other]
+ lazy { [user, other_user] } | [:active, :active_other, :expired, :revoked, :active_impersonation, :expired_impersonation, :revoked_impersonation]
+ [] | [] # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
end
- describe 'without impersonation' do
- before do
- params[:impersonation] = false
- end
-
- it { is_expected.to contain_exactly(active_personal_access_token, revoked_personal_access_token, expired_personal_access_token, project_access_token) }
-
- describe 'with active state' do
- before do
- params[:state] = 'active'
- end
-
- it { is_expected.to contain_exactly(active_personal_access_token, project_access_token) }
- end
-
- describe 'with inactive state' do
- before do
- params[:state] = 'inactive'
- end
+ with_them do
+ let(:params) { { users: by_users } }
- it { is_expected.to contain_exactly(revoked_personal_access_token, expired_personal_access_token) }
+ it 'returns tokens by users' do
+ is_expected.to match_array(tokens.values_at(*expected_tokens))
end
end
+ end
- describe 'with impersonation' do
- before do
- params[:impersonation] = true
- end
-
- it { is_expected.to contain_exactly(active_impersonation_token, revoked_impersonation_token, expired_impersonation_token) }
-
- describe 'with active state' do
- before do
- params[:state] = 'active'
- end
-
- it { is_expected.to contain_exactly(active_impersonation_token) }
- end
+ describe 'by impersonation' do
+ where(:by_impersonation, :expected_tokens) do
+ nil | tokens.keys
+ true | [:active_impersonation, :expired_impersonation, :revoked_impersonation]
+ false | [:active, :active_other, :expired, :revoked, :bot]
+ 'other' | tokens.keys
+ end
- describe 'with inactive state' do
- before do
- params[:state] = 'inactive'
- end
+ with_them do
+ let(:params) { { impersonation: by_impersonation } }
- it { is_expected.to contain_exactly(revoked_impersonation_token, expired_impersonation_token) }
+ it 'returns tokens by impersonation' do
+ is_expected.to match_array(tokens.values_at(*expected_tokens))
end
end
+ end
- describe 'with active state' do
- before do
- params[:state] = 'active'
- end
-
- it { is_expected.to contain_exactly(active_personal_access_token, active_impersonation_token, project_access_token) }
+ describe 'by state' do
+ where(:by_state, :expected_tokens) do
+ nil | tokens.keys
+ 'active' | [:active, :active_other, :active_impersonation, :bot]
+ 'inactive' | [:expired, :revoked, :expired_impersonation, :revoked_impersonation]
+ 'other' | tokens.keys
end
- describe 'with inactive state' do
- before do
- params[:state] = 'inactive'
- end
+ with_them do
+ let(:params) { { state: by_state } }
- it do
- is_expected.to contain_exactly(expired_personal_access_token, revoked_personal_access_token,
- expired_impersonation_token, revoked_impersonation_token)
+ it 'returns tokens by state' do
+ is_expected.to match_array(tokens.values_at(*expected_tokens))
end
end
+ end
- describe 'with id' do
- subject { finder(params).find_by_id(active_personal_access_token.id) }
-
- it { is_expected.to eq(active_personal_access_token) }
+ describe 'by owner type' do
+ where(:by_owner_type, :expected_tokens) do
+ nil | tokens.keys
+ 'human' | [:active, :active_other, :expired, :revoked, :active_impersonation, :expired_impersonation, :revoked_impersonation]
+ 'other' | tokens.keys
+ end
- describe 'with impersonation' do
- before do
- params[:impersonation] = true
- end
+ with_them do
+ let(:params) { { owner_type: by_owner_type } }
- it { is_expected.to be_nil }
+ it 'returns tokens by owner type' do
+ is_expected.to match_array(tokens.values_at(*expected_tokens))
end
end
+ end
- describe 'with token' do
- subject { finder(params).find_by_token(active_personal_access_token.token) }
-
- it { is_expected.to eq(active_personal_access_token) }
+ describe 'by revoked state' do
+ where(:by_revoked_state, :expected_tokens) do
+ nil | [:active, :active_other, :expired, :active_impersonation, :expired_impersonation, :bot]
+ true | [:revoked, :revoked_impersonation]
+ false | [:active, :active_other, :expired, :active_impersonation, :expired_impersonation, :bot]
+ end
- describe 'with impersonation' do
- before do
- params[:impersonation] = true
- end
+ with_them do
+ let(:params) { { revoked: by_revoked_state } }
- it { is_expected.to be_nil }
+ it 'returns tokens by revoked state' do
+ is_expected.to match_array(tokens.values_at(*expected_tokens))
end
end
end
- describe 'with user' do
- let(:user2) { create(:user) }
- let!(:other_user_active_personal_access_token) { create(:personal_access_token, user: user2) }
- let!(:other_user_expired_personal_access_token) { create(:personal_access_token, :expired, user: user2) }
- let!(:other_user_revoked_personal_access_token) { create(:personal_access_token, :revoked, user: user2) }
- let!(:other_user_active_impersonation_token) { create(:personal_access_token, :impersonation, user: user2) }
- let!(:other_user_expired_impersonation_token) { create(:personal_access_token, :expired, :impersonation, user: user2) }
- let!(:other_user_revoked_impersonation_token) { create(:personal_access_token, :revoked, :impersonation, user: user2) }
-
+ describe 'by created date' do
before do
- params[:user] = user
- end
-
- it do
- is_expected.to contain_exactly(active_personal_access_token, active_impersonation_token,
- revoked_personal_access_token, expired_personal_access_token,
- revoked_impersonation_token, expired_impersonation_token)
+ tokens[:active_other].update!(created_at: 5.days.ago)
end
- describe 'filtering human tokens' do
- before do
- params[:owner_type] = 'human'
+ describe 'by created before' do
+ where(:by_created_before, :expected_tokens) do
+ 6.days.ago | []
+ 2.days.ago | [:active_other]
+ 2.days.from_now | tokens.keys
end
- it { is_expected.not_to include(project_access_token) }
+ with_them do
+ let(:params) { { created_before: by_created_before } }
+
+ it 'returns tokens by created before' do
+ is_expected.to match_array(tokens.values_at(*expected_tokens))
+ end
+ end
end
- describe 'without impersonation' do
- before do
- params[:impersonation] = false
+ describe 'by created after' do
+ where(:by_created_after, :expected_tokens) do
+ 6.days.ago | tokens.keys
+ 2.days.ago | [:active, :expired, :revoked, :active_impersonation, :expired_impersonation, :revoked_impersonation, :bot]
+ 2.days.from_now | []
end
- it { is_expected.to contain_exactly(active_personal_access_token, revoked_personal_access_token, expired_personal_access_token) }
+ with_them do
+ let(:params) { { created_after: by_created_after } }
- describe 'with active state' do
- before do
- params[:state] = 'active'
+ it 'returns tokens by created before' do
+ is_expected.to match_array(tokens.values_at(*expected_tokens))
end
-
- it { is_expected.to contain_exactly(active_personal_access_token) }
end
+ end
+ end
- describe 'with inactive state' do
- before do
- params[:state] = 'inactive'
- end
-
- it { is_expected.to contain_exactly(revoked_personal_access_token, expired_personal_access_token) }
- end
+ describe 'by last used date' do
+ before do
+ PersonalAccessToken.update_all(last_used_at: Time.now)
+ tokens[:active_other].update!(last_used_at: 5.days.ago)
end
- describe 'with impersonation' do
- before do
- params[:impersonation] = true
+ describe 'by last used before' do
+ where(:by_last_used_before, :expected_tokens) do
+ 6.days.ago | []
+ 2.days.ago | [:active_other]
+ 2.days.from_now | tokens.keys
end
- it { is_expected.to contain_exactly(active_impersonation_token, revoked_impersonation_token, expired_impersonation_token) }
+ with_them do
+ let(:params) { { last_used_before: by_last_used_before } }
- describe 'with active state' do
- before do
- params[:state] = 'active'
+ it 'returns tokens by last used before' do
+ is_expected.to match_array(tokens.values_at(*expected_tokens))
end
+ end
+ end
- it { is_expected.to contain_exactly(active_impersonation_token) }
+ describe 'by last used after' do
+ where(:by_last_used_after, :expected_tokens) do
+ 6.days.ago | tokens.keys
+ 2.days.ago | [:active, :expired, :revoked, :active_impersonation, :expired_impersonation, :revoked_impersonation, :bot]
+ 2.days.from_now | []
end
- describe 'with inactive state' do
- before do
- params[:state] = 'inactive'
- end
+ with_them do
+ let(:params) { { last_used_after: by_last_used_after } }
- it { is_expected.to contain_exactly(revoked_impersonation_token, expired_impersonation_token) }
+ it 'returns tokens by last used after' do
+ is_expected.to match_array(tokens.values_at(*expected_tokens))
+ end
end
end
+ end
- describe 'with active state' do
- before do
- params[:state] = 'active'
- end
-
- it { is_expected.to contain_exactly(active_personal_access_token, active_impersonation_token) }
+ describe 'by search' do
+ where(:by_search, :expected_tokens) do
+ nil | tokens.keys
+ 'my_pat' | [:active, :active_other]
+ 'other' | []
end
- describe 'with inactive state' do
- before do
- params[:state] = 'inactive'
- end
+ with_them do
+ let(:params) { { search: by_search } }
- it do
- is_expected.to contain_exactly(expired_personal_access_token, revoked_personal_access_token,
- expired_impersonation_token, revoked_impersonation_token)
+ it 'returns tokens by search' do
+ is_expected.to match_array(tokens.values_at(*expected_tokens))
end
end
+ end
- describe 'with id' do
- subject { finder(params).find_by_id(active_personal_access_token.id) }
-
- it { is_expected.to eq(active_personal_access_token) }
+ describe 'sort' do
+ where(:sort, :expected_tokens) do
+ nil | tokens.keys
+ 'id_asc' | [:active, :active_other, :expired, :revoked, :active_impersonation, :expired_impersonation, :revoked_impersonation, :bot]
+ 'id_desc' | [:bot, :revoked_impersonation, :expired_impersonation, :active_impersonation, :revoked, :expired, :active_other, :active]
+ 'other' | tokens.keys
+ end
- describe 'with impersonation' do
- before do
- params[:impersonation] = true
- end
+ with_them do
+ let(:params) { { sort: sort } }
- it { is_expected.to be_nil }
+ it 'returns ordered tokens' do
+ expect(subject.map(&:id)).to eq(tokens.values_at(*expected_tokens).map(&:id))
end
end
+ end
- describe 'with token' do
- subject { finder(params).find_by_token(active_personal_access_token.token) }
+ describe 'delegates' do
+ subject { described_class.new(params, current_user) }
- it { is_expected.to eq(active_personal_access_token) }
+ describe '#find_by_id' do
+ it 'returns token by id' do
+ expect(subject.find_by_id(tokens[:active].id)).to eq(tokens[:active])
+ end
+ end
- describe 'with impersonation' do
- before do
- params[:impersonation] = true
- end
+ describe '#find_by_token' do
+ it 'returns token by token' do
+ expect(subject.find_by_token(tokens[:active].token)).to eq(tokens[:active])
+ end
+ end
- it { is_expected.to be_nil }
+ describe '#find' do
+ it 'returns token by id' do
+ expect(subject.find(tokens[:active].id)).to eq(tokens[:active])
end
end
end
diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb
index 02153715eac..9fecbfb71fc 100644
--- a/spec/finders/projects_finder_spec.rb
+++ b/spec/finders/projects_finder_spec.rb
@@ -392,6 +392,23 @@ RSpec.describe ProjectsFinder do
it { is_expected.to match_array([project]) }
end
+ describe 'filter by language' do
+ let_it_be(:ruby) { create(:programming_language, name: 'Ruby') }
+ let_it_be(:repository_language) { create(:repository_language, project: internal_project, programming_language: ruby) }
+
+ let(:params) { { language: ruby.id } }
+
+ it { is_expected.to match_array([internal_project]) }
+
+ context 'when project_language_search feature flag disabled' do
+ before do
+ stub_feature_flags(project_language_search: false)
+ end
+
+ it { is_expected.to match_array([internal_project, public_project]) }
+ end
+ end
+
describe 'sorting' do
let_it_be(:more_projects) do
[
diff --git a/spec/finders/tags_finder_spec.rb b/spec/finders/tags_finder_spec.rb
index 0bf9b228c8a..2af23c466fb 100644
--- a/spec/finders/tags_finder_spec.rb
+++ b/spec/finders/tags_finder_spec.rb
@@ -68,6 +68,14 @@ RSpec.describe TagsFinder do
expect(result.count).to eq(1)
end
+ it 'filters tags by name with wildcard' do
+ result = load_tags({ search: 'v1.*.0' })
+
+ expect(result.first.name).to eq('v1.0.0')
+ expect(result.second.name).to eq('v1.1.0')
+ expect(result.count).to eq(2)
+ end
+
it 'filters tags by nonexistent name that begins with' do
result = load_tags({ search: '^nope' })
@@ -79,6 +87,11 @@ RSpec.describe TagsFinder do
expect(result.count).to eq(0)
end
+ it 'filters tags by nonexistent name with wildcard' do
+ result = load_tags({ search: 'n*e' })
+ expect(result.count).to eq(0)
+ end
+
context 'when search is not a string' do
it 'returns no matches' do
result = load_tags({ search: { 'a' => 'b' } })
diff --git a/spec/finders/todos_finder_spec.rb b/spec/finders/todos_finder_spec.rb
index 5611a67e977..bcead6b0170 100644
--- a/spec/finders/todos_finder_spec.rb
+++ b/spec/finders/todos_finder_spec.rb
@@ -327,9 +327,9 @@ RSpec.describe TodosFinder do
it 'returns the expected types' do
expected_result =
if Gitlab.ee?
- %w[Epic Issue MergeRequest DesignManagement::Design AlertManagement::Alert]
+ %w[Epic Issue WorkItem MergeRequest DesignManagement::Design AlertManagement::Alert]
else
- %w[Issue MergeRequest DesignManagement::Design AlertManagement::Alert]
+ %w[Issue WorkItem MergeRequest DesignManagement::Design AlertManagement::Alert]
end
expect(described_class.todo_types).to contain_exactly(*expected_result)
diff --git a/spec/finders/users_finder_spec.rb b/spec/finders/users_finder_spec.rb
index 271dce44db7..5cf845a87b2 100644
--- a/spec/finders/users_finder_spec.rb
+++ b/spec/finders/users_finder_spec.rb
@@ -8,9 +8,7 @@ RSpec.describe UsersFinder do
let_it_be(:project_bot) { create(:user, :project_bot) }
- context 'with a normal user' do
- let_it_be(:user) { create(:user) }
-
+ shared_examples 'executes users finder as normal user' do
it 'returns searchable users' do
users = described_class.new(user).execute
@@ -97,37 +95,35 @@ RSpec.describe UsersFinder do
end
end
- context 'with an admin user', :enable_admin_mode do
- let_it_be(:admin) { create(:admin) }
-
+ shared_examples 'executes users finder as admin' do
it 'filters by external users' do
- users = described_class.new(admin, external: true).execute
+ users = described_class.new(user, external: true).execute
expect(users).to contain_exactly(external_user)
end
it 'returns all users' do
- users = described_class.new(admin).execute
+ users = described_class.new(user).execute
- expect(users).to contain_exactly(admin, normal_user, blocked_user, unconfirmed_user, banned_user, external_user, omniauth_user, internal_user, admin_user, project_bot)
+ expect(users).to contain_exactly(user, normal_user, blocked_user, unconfirmed_user, banned_user, external_user, omniauth_user, internal_user, admin_user, project_bot)
end
it 'filters by blocked users' do
- users = described_class.new(admin, blocked: true).execute
+ users = described_class.new(user, blocked: true).execute
expect(users).to contain_exactly(blocked_user)
end
it 'filters by active users' do
- users = described_class.new(admin, active: true).execute
+ users = described_class.new(user, active: true).execute
- expect(users).to contain_exactly(admin, normal_user, unconfirmed_user, external_user, omniauth_user, admin_user, project_bot)
+ expect(users).to contain_exactly(user, normal_user, unconfirmed_user, external_user, omniauth_user, admin_user, project_bot)
end
it 'returns only admins' do
- users = described_class.new(admin, admins: true).execute
+ users = described_class.new(user, admins: true).execute
- expect(users).to contain_exactly(admin, admin_user)
+ expect(users).to contain_exactly(user, admin_user)
end
it 'filters by custom attributes' do
@@ -137,7 +133,7 @@ RSpec.describe UsersFinder do
create :user_custom_attribute, user: internal_user, key: 'foo', value: 'foo'
users = described_class.new(
- admin,
+ user,
custom_attributes: { foo: 'foo', bar: 'bar' }
).execute
@@ -145,10 +141,34 @@ RSpec.describe UsersFinder do
end
it 'filters by private emails search' do
- users = described_class.new(admin, search: normal_user.email).execute
+ users = described_class.new(user, search: normal_user.email).execute
expect(users).to contain_exactly(normal_user)
end
end
+
+ context 'with a normal user' do
+ let_it_be(:user) { create(:user) }
+
+ it_behaves_like 'executes users finder as normal user'
+ end
+
+ context 'with an admin user' do
+ let_it_be(:user) { create(:admin) }
+
+ context 'when admin mode setting is disabled', :do_not_mock_admin_mode_setting do
+ it_behaves_like 'executes users finder as admin'
+ end
+
+ context 'when admin mode setting is enabled' do
+ context 'when in admin mode', :enable_admin_mode do
+ it_behaves_like 'executes users finder as admin'
+ end
+
+ context 'when not in admin mode' do
+ it_behaves_like 'executes users finder as normal user'
+ end
+ end
+ end
end
end
diff --git a/spec/finders/work_items/work_items_finder_spec.rb b/spec/finders/work_items/work_items_finder_spec.rb
index fe400688a23..ab8a9ba9204 100644
--- a/spec/finders/work_items/work_items_finder_spec.rb
+++ b/spec/finders/work_items/work_items_finder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItems::WorkItemsFinder do
+RSpec.describe WorkItems::WorkItemsFinder, feature_category: :team_planning do
using RSpec::Parameterized::TableSyntax
include_context 'WorkItemsFinder context'
diff --git a/spec/fixtures/api/schemas/branch.json b/spec/fixtures/api/schemas/branch.json
index 0bb74577010..02389a1b979 100644
--- a/spec/fixtures/api/schemas/branch.json
+++ b/spec/fixtures/api/schemas/branch.json
@@ -1,12 +1,17 @@
{
"type": "object",
- "required" : [
+ "required": [
"name",
"url"
],
- "properties" : {
- "name": { "type": "string" },
- "url": { "type": "uri" }
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string",
+ "format": "uri"
+ }
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/cluster_status.json b/spec/fixtures/api/schemas/cluster_status.json
index 6f9535286ed..efc609b3c3f 100644
--- a/spec/fixtures/api/schemas/cluster_status.json
+++ b/spec/fixtures/api/schemas/cluster_status.json
@@ -1,55 +1,118 @@
{
"type": "object",
- "required" : [
+ "required": [
"status",
"applications"
],
- "properties" : {
- "status": { "type": "string" },
- "status_reason": { "type": ["string", "null"] },
+ "properties": {
+ "status": {
+ "type": "string"
+ },
+ "status_reason": {
+ "$ref": "types/nullable_string.json"
+ },
"applications": {
"type": "array",
- "items": { "$ref": "#/definitions/application_status" }
+ "items": {
+ "$ref": "#/definitions/application_status"
+ }
}
},
"additionalProperties": false,
"definitions": {
"application_status": {
"type": "object",
+ "required": [
+ "name",
+ "status"
+ ],
"additionalProperties": false,
- "properties" : {
- "name": { "type": "string" },
+ "properties": {
+ "name": {
+ "type": "string"
+ },
"status": {
- "type": {
- "enum": [
- "installable",
- "scheduled",
- "installing",
- "installed",
- "errored"
- ]
- }
+ "type": "string",
+ "enum": [
+ "installable",
+ "scheduled",
+ "installing",
+ "installed",
+ "errored",
+ "not_installable"
+ ]
+ },
+ "version": {
+ "type": "string"
+ },
+ "status_reason": {
+ "$ref": "types/nullable_string.json"
+ },
+ "external_ip": {
+ "$ref": "types/nullable_string.json"
+ },
+ "external_hostname": {
+ "$ref": "types/nullable_string.json"
+ },
+ "hostname": {
+ "$ref": "types/nullable_string.json"
+ },
+ "email": {
+ "$ref": "types/nullable_string.json"
+ },
+ "stack": {
+ "$ref": "types/nullable_string.json"
+ },
+ "host": {
+ "$ref": "types/nullable_string.json"
+ },
+ "port": {
+ "type": "integer"
+ },
+ "protocol": {
+ "type": "integer"
+ },
+ "update_available": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "can_uninstall": {
+ "type": "boolean"
},
- "version": { "type": "string" },
- "status_reason": { "type": ["string", "null"] },
- "external_ip": { "type": ["string", "null"] },
- "external_hostname": { "type": ["string", "null"] },
- "hostname": { "type": ["string", "null"] },
- "email": { "type": ["string", "null"] },
- "stack": { "type": ["string", "null"] },
- "host": {"type": ["string", "null"]},
- "port": {"type": ["integer", "514"]},
- "protocol": {"type": ["integer", "0"]},
- "update_available": { "type": ["boolean", "null"] },
- "can_uninstall": { "type": "boolean" },
"available_domains": {
"type": "array",
- "items": { "$ref": "#/definitions/domain" }
+ "items": {
+ "$ref": "#/definitions/domain"
+ }
},
- "pages_domain": { "type": [ { "$ref": "#/definitions/domain" }, "null"] }
- },
- "required" : [ "name", "status" ]
+ "pages_domain": {
+ "oneOf": [
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "#/definitions/domain"
+ }
+ ]
+ }
+ }
},
- "domain": { "id": "integer", "domain": "string" }
+ "domain": {
+ "type": "object",
+ "required": [
+ "id",
+ "domain"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "domain": {
+ "type": "string"
+ }
+ }
+ }
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/conflicts.json b/spec/fixtures/api/schemas/conflicts.json
index a947783d505..f8acac9f074 100644
--- a/spec/fixtures/api/schemas/conflicts.json
+++ b/spec/fixtures/api/schemas/conflicts.json
@@ -8,16 +8,29 @@
"files"
],
"properties": {
- "commit_message": {"type": "string"},
- "commit_sha": {"type": "string", "pattern": "^[0-9a-f]{40}$"},
- "source_branch": {"type": "string"},
- "target_branch": {"type": "string"},
+ "commit_message": {
+ "type": "string"
+ },
+ "commit_sha": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{40}$"
+ },
+ "source_branch": {
+ "type": "string"
+ },
+ "target_branch": {
+ "type": "string"
+ },
"files": {
"type": "array",
"items": {
"oneOf": [
- { "$ref": "#/definitions/conflict-text-with-sections" },
- { "$ref": "#/definitions/conflict-text-for-editor" }
+ {
+ "$ref": "#/definitions/conflict-text-with-sections"
+ },
+ {
+ "$ref": "#/definitions/conflict-text-for-editor"
+ }
]
}
}
@@ -32,15 +45,25 @@
"blob_path"
],
"properties": {
- "old_path": {"type": "string"},
- "new_path": {"type": "string"},
- "blob_icon": {"type": "string"},
- "blob_path": {"type": "string"}
+ "old_path": {
+ "type": "string"
+ },
+ "new_path": {
+ "type": "string"
+ },
+ "blob_icon": {
+ "type": "string"
+ },
+ "blob_path": {
+ "type": "string"
+ }
}
},
"conflict-text-for-editor": {
"allOf": [
- {"$ref": "#/definitions/conflict-base"},
+ {
+ "$ref": "#/definitions/conflict-base"
+ },
{
"type": "object",
"required": [
@@ -48,15 +71,25 @@
"content_path"
],
"properties": {
- "type": {"type": {"enum": ["text-editor"]}},
- "content_path": {"type": "string"}
+ "type": {
+ "type": "string",
+ "enum": [
+ "text",
+ "text-editor"
+ ]
+ },
+ "content_path": {
+ "type": "string"
+ }
}
}
]
},
"conflict-text-with-sections": {
"allOf": [
- {"$ref": "#/definitions/conflict-base"},
+ {
+ "$ref": "#/definitions/conflict-base"
+ },
{
"type": "object",
"required": [
@@ -65,14 +98,25 @@
"sections"
],
"properties": {
- "type": {"type": {"enum": ["text"]}},
- "content_path": {"type": "string"},
+ "type": {
+ "type": "string",
+ "enum": [
+ "text"
+ ]
+ },
+ "content_path": {
+ "type": "string"
+ },
"sections": {
"type": "array",
"items": {
"oneOf": [
- { "$ref": "#/definitions/section-context" },
- { "$ref": "#/definitions/section-conflict" }
+ {
+ "$ref": "#/definitions/section-context"
+ },
+ {
+ "$ref": "#/definitions/section-conflict"
+ }
]
}
}
@@ -87,7 +131,9 @@
"lines"
],
"properties": {
- "conflict": {"type": "boolean"},
+ "conflict": {
+ "type": "boolean"
+ },
"lines": {
"type": "array",
"items": {
@@ -99,11 +145,21 @@
"rich_text"
],
"properties": {
- "type": {"type": "string"},
- "old_line": {"type": "string"},
- "new_line": {"type": "string"},
- "text": {"type": "string"},
- "rich_text": {"type": "string"}
+ "type": {
+ "type": "string"
+ },
+ "old_line": {
+ "type": "string"
+ },
+ "new_line": {
+ "type": "string"
+ },
+ "text": {
+ "type": "string"
+ },
+ "rich_text": {
+ "type": "string"
+ }
}
}
}
@@ -111,27 +167,39 @@
},
"section-context": {
"allOf": [
- {"$ref": "#/definitions/section-base"},
+ {
+ "$ref": "#/definitions/section-base"
+ },
{
"type": "object",
"properties": {
- "conflict": {"enum": [false]}
+ "conflict": {
+ "type": "boolean"
+ }
}
}
]
},
"section-conflict": {
"allOf": [
- {"$ref": "#/definitions/section-base"},
+ {
+ "$ref": "#/definitions/section-base"
+ },
{
"type": "object",
- "required": ["id"],
+ "required": [
+ "id"
+ ],
"properties": {
- "conflict": {"enum": [true]},
- "id": {"type": "string"}
+ "conflict": {
+ "type": "boolean"
+ },
+ "id": {
+ "type": "string"
+ }
}
}
]
}
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/entities/discussion.json b/spec/fixtures/api/schemas/entities/discussion.json
index da2d2a83a8d..45271926547 100644
--- a/spec/fixtures/api/schemas/entities/discussion.json
+++ b/spec/fixtures/api/schemas/entities/discussion.json
@@ -1,34 +1,75 @@
{
"type": "object",
- "required" : [
+ "required": [
"id",
"notes",
"individual_note"
],
- "properties" : {
- "id": { "type": "string" },
- "individual_note": { "type": "boolean" },
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "individual_note": {
+ "type": "boolean"
+ },
"notes": {
"type": "array",
"items": {
"type": "object",
- "properties" : {
- "id": { "type": "string" },
- "type": { "type": ["string", "null"] },
- "body": { "type": "string" },
- "attachment": { "type": ["string", "null"]},
- "award_emoji": { "type": "array" },
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "type": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "body": {
+ "type": "string"
+ },
+ "attachment": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "award_emoji": {
+ "type": "array"
+ },
"author": {
"type": "object",
"properties": {
- "name": { "type": "string" },
- "username": { "type": "string" },
- "id": { "type": "integer" },
- "state": { "type": "string" },
- "avatar_url": { "type": "uri" },
- "web_url": { "type": "uri" },
- "status_tooltip_html": { "type": ["string", "null"] },
- "path": { "type": "string" }
+ "name": {
+ "type": "string"
+ },
+ "username": {
+ "type": "string"
+ },
+ "id": {
+ "type": "integer"
+ },
+ "state": {
+ "type": "string"
+ },
+ "avatar_url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "web_url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "status_tooltip_html": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "path": {
+ "type": "string"
+ }
},
"required": [
"id",
@@ -39,43 +80,131 @@
"username"
]
},
- "created_at": { "type": "string", "format": "date-time" },
- "updated_at": { "type": "string", "format": "date-time" },
- "system": { "type": "boolean" },
- "noteable_id": { "type": "integer" },
- "noteable_iid": { "type": ["integer", "null"] },
- "noteable_type": { "type": "string" },
- "resolved": { "type": "boolean" },
- "resolvable": { "type": "boolean" },
- "resolved_by": { "type": ["string", "null"] },
- "resolved_at": { "type": ["string", "null"], "format": "date-time" },
- "note": { "type": "string" },
- "note_html": { "type": "string" },
- "current_user": { "type": "object" },
- "suggestions": { "type": "array" },
- "discussion_id": { "type": "string" },
- "emoji_awardable": { "type": "boolean" },
- "report_abuse_path": { "type": "string" },
- "noteable_note_url": { "type": "string" },
- "resolve_path": { "type": "string" },
- "resolve_with_issue_path": { "type": "string" },
- "cached_markdown_version": { "type": "integer" },
- "human_access": { "type": ["string", "null"] },
- "is_noteable_author": { "type": "boolean" },
- "is_contributor": { "type": "boolean" },
- "project_name": { "type": "string" },
- "toggle_award_path": { "type": "string" },
- "path": { "type": "string" },
- "commands_changes": { "type": "object", "additionalProperties": true },
- "confidential": { "type": ["boolean", "null"] },
- "internal": { "type": ["boolean", "null"] }
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "system": {
+ "type": "boolean"
+ },
+ "noteable_id": {
+ "type": "integer"
+ },
+ "noteable_iid": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "noteable_type": {
+ "type": "string"
+ },
+ "resolved": {
+ "type": "boolean"
+ },
+ "resolvable": {
+ "type": "boolean"
+ },
+ "resolved_by": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "resolved_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "note": {
+ "type": "string"
+ },
+ "note_html": {
+ "type": "string"
+ },
+ "current_user": {
+ "type": "object"
+ },
+ "suggestions": {
+ "type": "array"
+ },
+ "discussion_id": {
+ "type": "string"
+ },
+ "emoji_awardable": {
+ "type": "boolean"
+ },
+ "report_abuse_path": {
+ "type": "string"
+ },
+ "noteable_note_url": {
+ "type": "string"
+ },
+ "resolve_path": {
+ "type": "string"
+ },
+ "resolve_with_issue_path": {
+ "type": "string"
+ },
+ "cached_markdown_version": {
+ "type": "integer"
+ },
+ "human_access": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "is_noteable_author": {
+ "type": "boolean"
+ },
+ "is_contributor": {
+ "type": "boolean"
+ },
+ "project_name": {
+ "type": "string"
+ },
+ "toggle_award_path": {
+ "type": "string"
+ },
+ "path": {
+ "type": "string"
+ },
+ "commands_changes": {
+ "type": "object",
+ "additionalProperties": true
+ },
+ "confidential": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "internal": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
},
"required": [
- "id", "attachment", "author", "created_at", "updated_at",
- "system", "noteable_id", "noteable_type"
+ "id",
+ "attachment",
+ "author",
+ "created_at",
+ "updated_at",
+ "system",
+ "noteable_id",
+ "noteable_type"
],
"additionalProperties": false
}
}
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/entities/issue.json b/spec/fixtures/api/schemas/entities/issue.json
index b4a076780d9..08937b5e68b 100644
--- a/spec/fixtures/api/schemas/entities/issue.json
+++ b/spec/fixtures/api/schemas/entities/issue.json
@@ -1,43 +1,142 @@
{
"type": "object",
- "properties" : {
- "id": { "type": "integer" },
- "iid": { "type": "integer" },
- "type": { "type": "string" },
- "author_id": { "type": "integer" },
- "description": { "type": ["string", "null"] },
- "lock_version": { "type": ["integer", "null"] },
- "milestone_id": { "type": ["string", "null"] },
- "title": { "type": "string" },
- "moved_to_id": { "type": ["integer", "null"] },
- "project_id": { "type": "integer" },
- "web_url": { "type": "string" },
- "state": { "type": "string" },
- "create_note_path": { "type": "string" },
- "preview_note_path": { "type": "string" },
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "iid": {
+ "type": "integer"
+ },
+ "type": {
+ "type": "string"
+ },
+ "author_id": {
+ "type": "integer"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "lock_version": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "milestone_id": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "title": {
+ "type": "string"
+ },
+ "moved_to_id": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "project_id": {
+ "type": "integer"
+ },
+ "web_url": {
+ "type": "string"
+ },
+ "state": {
+ "type": "string"
+ },
+ "create_note_path": {
+ "type": "string"
+ },
+ "preview_note_path": {
+ "type": "string"
+ },
"current_user": {
"type": "object",
"properties": {
- "can_create_note": { "type": "boolean" },
- "can_update": { "type": "boolean" }
+ "can_create_note": {
+ "type": "boolean"
+ },
+ "can_update": {
+ "type": "boolean"
+ }
}
},
- "created_at": { "type": "date-time" },
- "updated_at": { "type": "date-time" },
- "branch_name": { "type": ["string", "null"] },
- "due_date": { "type": ["string", "null"], "format": "date-time" },
- "confidential": { "type": "boolean" },
- "discussion_locked": { "type": ["boolean", "null"] },
- "updated_by_id": { "type": ["integer", "null"] },
- "time_estimate": { "type": "integer" },
- "total_time_spent": { "type": "integer" },
- "human_time_estimate": { "type": ["integer", "null"] },
- "human_total_time_spent": { "type": ["integer", "null"] },
- "milestone": { "type": ["object", "null"] },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "branch_name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "due_date": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "confidential": {
+ "type": "boolean"
+ },
+ "discussion_locked": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "updated_by_id": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "time_estimate": {
+ "type": "integer"
+ },
+ "total_time_spent": {
+ "type": "integer"
+ },
+ "human_time_estimate": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "human_total_time_spent": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "milestone": {
+ "type": [
+ "object",
+ "null"
+ ]
+ },
"labels": {
"type": "array",
- "items": { "$ref": "label.json" }
+ "items": {
+ "$ref": "label.json"
+ }
},
- "assignees": { "type": ["array", "null"] }
+ "assignees": {
+ "type": [
+ "array",
+ "null"
+ ]
+ }
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/entities/member.json b/spec/fixtures/api/schemas/entities/member.json
index 24a4863df9b..cd8a4e0519b 100644
--- a/spec/fixtures/api/schemas/entities/member.json
+++ b/spec/fixtures/api/schemas/entities/member.json
@@ -14,59 +14,130 @@
"is_direct_member"
],
"properties": {
- "id": { "type": "integer" },
- "created_at": { "type": "date-time" },
- "expires_at": { "type": ["date-time", "null"] },
- "requested_at": { "type": ["date-time", "null"] },
- "can_update": { "type": "boolean" },
- "can_remove": { "type": "boolean" },
- "is_direct_member": { "type": "boolean" },
+ "id": {
+ "type": "integer"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "expires_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "requested_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "can_update": {
+ "type": "boolean"
+ },
+ "can_remove": {
+ "type": "boolean"
+ },
+ "is_direct_member": {
+ "type": "boolean"
+ },
"access_level": {
"type": "object",
- "required": ["integer_value", "string_value"],
+ "required": [
+ "integer_value",
+ "string_value"
+ ],
"properties": {
- "integer_value": { "type": "integer" },
- "string_value": { "type": "string" }
+ "integer_value": {
+ "type": "integer"
+ },
+ "string_value": {
+ "type": "string"
+ }
},
"additionalProperties": false
},
"source": {
"type": "object",
- "required": ["id", "full_name", "web_url"],
+ "required": [
+ "id",
+ "full_name",
+ "web_url"
+ ],
"properties": {
- "id": { "type": "integer" },
- "full_name": { "type": "string" },
- "web_url": { "type": "string" }
+ "id": {
+ "type": "integer"
+ },
+ "full_name": {
+ "type": "string"
+ },
+ "web_url": {
+ "type": "string"
+ }
},
"additionalProperties": false
},
- "valid_roles": { "type": "object" },
- "type": { "type": "string" },
+ "valid_roles": {
+ "type": "object"
+ },
+ "type": {
+ "type": "string"
+ },
"created_by": {
"type": "object",
- "required": ["name", "web_url"],
+ "required": [
+ "name",
+ "web_url"
+ ],
"properties": {
- "name": { "type": "string" },
- "web_url": { "type": "string" }
+ "name": {
+ "type": "string"
+ },
+ "web_url": {
+ "type": "string"
+ }
},
"additionalProperties": false
},
"user": {
"allOf": [
- { "$ref": "member_user_default.json" }
+ {
+ "$ref": "member_user_default.json"
+ }
]
},
- "state": { "type": "integer" },
+ "state": {
+ "type": "integer"
+ },
"invite": {
"type": "object",
- "required": ["email", "avatar_url", "can_resend", "user_state"],
+ "required": [
+ "email",
+ "avatar_url",
+ "can_resend",
+ "user_state"
+ ],
"properties": {
- "email": { "type": "string" },
- "avatar_url": { "type": [ "string", "null" ] },
- "can_resend": { "type": "boolean" },
- "user_state": { "type": "string" }
+ "email": {
+ "type": "string"
+ },
+ "avatar_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "can_resend": {
+ "type": "boolean"
+ },
+ "user_state": {
+ "type": "string"
+ }
},
"additionalProperties": false
}
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/entities/merge_request_metrics.json b/spec/fixtures/api/schemas/entities/merge_request_metrics.json
index 3fa767f85df..591c5919b19 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_metrics.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_metrics.json
@@ -1,21 +1,46 @@
{
"type": "object",
- "required": ["closed_at", "merged_at", "closed_by", "merged_by"],
- "properties" : {
- "closed_at": { "type": ["datetime", "null"] },
- "merged_at": { "type": ["datetime", "null"] },
+ "required": [
+ "closed_at",
+ "merged_at",
+ "closed_by",
+ "merged_by"
+ ],
+ "properties": {
+ "closed_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "merged_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
"closed_by": {
"oneOf": [
- { "type": "null" },
- { "$ref": "user.json" }
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "user.json"
+ }
]
},
"merged_by": {
"oneOf": [
- { "type": "null" },
- { "$ref": "user.json" }
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "user.json"
+ }
]
}
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/entities/merge_request_poll_widget.json b/spec/fixtures/api/schemas/entities/merge_request_poll_widget.json
index be2fe19b067..f0509f7a76f 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_poll_widget.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_poll_widget.json
@@ -1,38 +1,128 @@
{
"type": "object",
- "properties" : {
- "id": { "type": "integer" },
- "iid": { "type": "integer" },
- "description": { "type": ["string", "null"] },
- "title": { "type": "string" },
- "auto_merge_strategy": { "type": ["string", "null"] },
- "available_auto_merge_strategies": { "type": "array" },
- "source_branch_protected": { "type": "boolean" },
- "allow_collaboration": { "type": "boolean"},
- "should_be_rebased": { "type": "boolean" },
- "ff_only_enabled": { "type": ["boolean", false] },
- "merge_user": { "type": ["object", "null"] },
- "pipeline": { "type": ["object", "null"] },
- "merge_pipeline": { "type": ["object", "null"] },
- "default_merge_commit_message": { "type": ["string", "null"] },
- "mergeable": { "type": "boolean" },
- "default_merge_commit_message_with_description": { "type": "string" },
- "mergeable_discussions_state": { "type": "boolean" },
- "project_archived": { "type": "boolean" },
- "only_allow_merge_if_pipeline_succeeds": { "type": "boolean" },
- "has_ci": { "type": "boolean" },
- "ci_status": { "type": ["string", "null"] },
- "pipeline_coverage_delta": { "type": ["float", "null"] },
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "iid": {
+ "type": "integer"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "title": {
+ "type": "string"
+ },
+ "auto_merge_strategy": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "available_auto_merge_strategies": {
+ "type": "array"
+ },
+ "source_branch_protected": {
+ "type": "boolean"
+ },
+ "allow_collaboration": {
+ "type": "boolean"
+ },
+ "should_be_rebased": {
+ "type": "boolean"
+ },
+ "ff_only_enabled": {
+ "type": "boolean"
+ },
+ "merge_user": {
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "pipeline": {
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "merge_pipeline": {
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "default_merge_commit_message": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "mergeable": {
+ "type": "boolean"
+ },
+ "default_merge_commit_message_with_description": {
+ "type": "string"
+ },
+ "mergeable_discussions_state": {
+ "type": "boolean"
+ },
+ "project_archived": {
+ "type": "boolean"
+ },
+ "only_allow_merge_if_pipeline_succeeds": {
+ "type": "boolean"
+ },
+ "has_ci": {
+ "type": "boolean"
+ },
+ "ci_status": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "pipeline_coverage_delta": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "float"
+ },
"builds_with_coverage": {
- "type": ["array", "null"],
+ "type": [
+ "array",
+ "null"
+ ],
"items": {
"type": "object",
- "required": ["name", "coverage"]
+ "required": [
+ "name",
+ "coverage"
+ ]
}
},
- "cancel_auto_merge_path": { "type": ["string", "null"] },
- "test_reports_path": { "type": ["string", "null"] },
- "create_issue_to_resolve_discussions_path": { "type": ["string", "null"] },
+ "cancel_auto_merge_path": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "test_reports_path": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "create_issue_to_resolve_discussions_path": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
"current_user": {
"type": "object",
"required": [
@@ -42,20 +132,71 @@
"can_create_issue"
],
"properties": {
- "can_remove_source_branch": { "type": "boolean" },
- "can_revert_on_current_merge_request": { "type": ["boolean", "null"] },
- "can_cherry_pick_on_current_merge_request": { "type": ["boolean", "null"] },
- "can_create_issue": { "type": "boolean" }
+ "can_remove_source_branch": {
+ "type": "boolean"
+ },
+ "can_revert_on_current_merge_request": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "can_cherry_pick_on_current_merge_request": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "can_create_issue": {
+ "type": "boolean"
+ }
},
"additionalProperties": false
},
- "can_push_to_source_branch": { "type": "boolean" },
- "new_blob_path": { "type": ["string", "null"] },
- "rebase_path": { "type": ["string", "null"] },
- "conflict_resolution_path": { "type": ["string", "null"] },
- "remove_wip_path": { "type": ["string", "null"] },
- "merge_path": { "type": ["string", "null"] },
- "cherry_pick_in_fork_path": { "type": ["string", "null"] },
- "revert_in_fork_path": { "type": ["string", "null"] }
+ "can_push_to_source_branch": {
+ "type": "boolean"
+ },
+ "new_blob_path": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "rebase_path": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "conflict_resolution_path": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "remove_wip_path": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "merge_path": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "cherry_pick_in_fork_path": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "revert_in_fork_path": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/entities/test_case.json b/spec/fixtures/api/schemas/entities/test_case.json
index 483fa881f8c..660d40c4a9f 100644
--- a/spec/fixtures/api/schemas/entities/test_case.json
+++ b/spec/fixtures/api/schemas/entities/test_case.json
@@ -1,24 +1,57 @@
{
"type": "object",
- "required" : [
+ "required": [
"status",
"name"
],
"properties": {
- "status": { "type": "string" },
- "name": { "type": "string" },
- "classname": { "type": "string" },
- "file": { "type": ["string", "null"] },
- "execution_time": { "type": "float" },
- "system_output": { "type": ["string", "null"] },
- "stack_trace": { "type": ["string", "null"] },
- "attachment_url": { "type": ["string", "null"] },
+ "status": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "classname": {
+ "type": "string"
+ },
+ "file": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "execution_time": {
+ "type": "number",
+ "format": "float"
+ },
+ "system_output": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "stack_trace": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "attachment_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
"recent_failures": {
"oneOf": [
- { "type": "null" },
- { "$ref": "test_case/recent_failures.json" }
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "test_case/recent_failures.json"
+ }
]
}
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/entities/trigger.json b/spec/fixtures/api/schemas/entities/trigger.json
index 5c46142673f..c6ccd84dcdd 100644
--- a/spec/fixtures/api/schemas/entities/trigger.json
+++ b/spec/fixtures/api/schemas/entities/trigger.json
@@ -10,14 +10,21 @@
],
"properties": {
"description": {
- "type": ["string", "null"]
+ "type": [
+ "string",
+ "null"
+ ]
},
"owner": {
"type": "object",
"$ref": "user.json"
},
"last_used": {
- "type": ["datetime", "null"]
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
},
"token": {
"type": "string"
@@ -36,4 +43,4 @@
}
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/environment.json b/spec/fixtures/api/schemas/environment.json
index 87b6e5da370..fe842844223 100644
--- a/spec/fixtures/api/schemas/environment.json
+++ b/spec/fixtures/api/schemas/environment.json
@@ -15,52 +15,120 @@
"can_stop"
],
"properties": {
- "id": { "type": "integer" },
- "global_id": { "type": "string" },
- "name": { "type": "string" },
- "state": { "type": "string" },
- "external_url": { "$ref": "types/nullable_string.json" },
- "environment_type": { "$ref": "types/nullable_string.json" },
- "name_without_type": { "type": "string" },
- "has_stop_action": { "type": "boolean" },
- "environment_path": { "type": "string" },
- "stop_path": { "type": "string" },
- "cancel_auto_stop_path": { "type": "string" },
- "folder_path": { "type": "string" },
- "logs_path": { "type": "string" },
- "logs_api_path": { "type": "string" },
- "enable_advanced_logs_querying": { "type": "boolean" },
- "created_at": { "type": "string", "format": "date-time" },
- "updated_at": { "type": "string", "format": "date-time" },
- "auto_stop_at": { "type": "string", "format": "date-time" },
- "can_stop": { "type": "boolean" },
- "has_opened_alert": { "type": "boolean" },
- "tier": { "type": "string" },
- "required_approval_count": { "type": "integer" },
- "cluster_type": { "type": "types/nullable_string.json" },
- "terminal_path": { "type": "types/nullable_string.json" },
+ "id": {
+ "type": "integer"
+ },
+ "global_id": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "state": {
+ "type": "string"
+ },
+ "external_url": {
+ "$ref": "types/nullable_string.json",
+ "format": "uri"
+ },
+ "environment_type": {
+ "$ref": "types/nullable_string.json",
+ "format": "uri"
+ },
+ "name_without_type": {
+ "type": "string"
+ },
+ "has_stop_action": {
+ "type": "boolean"
+ },
+ "environment_path": {
+ "type": "string"
+ },
+ "stop_path": {
+ "type": "string"
+ },
+ "cancel_auto_stop_path": {
+ "type": "string"
+ },
+ "folder_path": {
+ "type": "string"
+ },
+ "logs_path": {
+ "type": "string"
+ },
+ "logs_api_path": {
+ "type": "string"
+ },
+ "enable_advanced_logs_querying": {
+ "type": "boolean"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "auto_stop_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "can_stop": {
+ "type": "boolean"
+ },
+ "has_opened_alert": {
+ "type": "boolean"
+ },
+ "tier": {
+ "type": "string"
+ },
+ "required_approval_count": {
+ "type": "integer"
+ },
+ "cluster_type": {
+ "ref": "types/nullable_string.json"
+ },
+ "terminal_path": {
+ "ref": "types/nullable_string.json"
+ },
"rollout_status": {
"oneOf": [
- { "type": "null" },
- { "$ref": "rollout_status.json" }
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "rollout_status.json"
+ }
]
},
"last_deployment": {
"oneOf": [
- { "type": "null" },
- { "$ref": "deployment.json" },
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "deployment.json"
+ },
{
"type": "object",
- "properties" : {
- "name": { "type": "string" },
- "build_path": { "type": "string" }
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "build_path": {
+ "type": "string"
+ }
}
}
]
},
- "can_delete": { "type": "boolean" }
- ,
- "delete_path": { "type": "string" }
+ "can_delete": {
+ "type": "boolean"
+ },
+ "delete_path": {
+ "type": "string"
+ }
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/group_link/group_link.json b/spec/fixtures/api/schemas/group_link/group_link.json
index 3c2195df11e..885ed6d18e0 100644
--- a/spec/fixtures/api/schemas/group_link/group_link.json
+++ b/spec/fixtures/api/schemas/group_link/group_link.json
@@ -11,34 +11,82 @@
"is_direct_member"
],
"properties": {
- "id": { "type": "integer" },
- "created_at": { "type": "date-time" },
- "expires_at": { "type": ["date-time", "null"] },
+ "id": {
+ "type": "integer"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "expires_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
"access_level": {
"type": "object",
- "required": ["integer_value", "string_value"],
+ "required": [
+ "integer_value",
+ "string_value"
+ ],
"properties": {
- "integer_value": { "type": "integer" },
- "string_value": { "type": "string" }
+ "integer_value": {
+ "type": "integer"
+ },
+ "string_value": {
+ "type": "string"
+ }
},
"additionalProperties": false
},
- "valid_roles": { "type": "object" },
+ "valid_roles": {
+ "type": "object"
+ },
"shared_with_group": {
"type": "object",
- "required": ["id", "name", "full_name", "full_path", "avatar_url", "web_url"],
+ "required": [
+ "id",
+ "name",
+ "full_name",
+ "full_path",
+ "avatar_url",
+ "web_url"
+ ],
"properties": {
- "id": { "type": "integer" },
- "name": { "type": "string" },
- "full_name": { "type": "string" },
- "full_path": { "type": "string" },
- "avatar_url": { "type": ["string", "null"] },
- "web_url": { "type": "string" }
+ "id": {
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ },
+ "full_name": {
+ "type": "string"
+ },
+ "full_path": {
+ "type": "string"
+ },
+ "avatar_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "web_url": {
+ "type": "string"
+ }
},
"additionalProperties": false
},
- "can_update": { "type": "boolean" },
- "can_remove": { "type": "boolean" },
- "is_direct_member": { "type": "boolean" }
+ "can_update": {
+ "type": "boolean"
+ },
+ "can_remove": {
+ "type": "boolean"
+ },
+ "is_direct_member": {
+ "type": "boolean"
+ }
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/issue.json b/spec/fixtures/api/schemas/issue.json
index aefba89d9e2..ac8c05eb377 100644
--- a/spec/fixtures/api/schemas/issue.json
+++ b/spec/fixtures/api/schemas/issue.json
@@ -1,43 +1,104 @@
{
"type": "object",
- "required" : [
+ "required": [
"iid",
"title",
"confidential"
],
- "properties" : {
- "id": { "type": "integer" },
- "iid": { "type": "integer" },
- "project_id": { "type": ["integer", "null"] },
- "title": { "type": "string" },
- "confidential": { "type": "boolean" },
- "due_date": { "type": ["string", "null"] },
- "relative_position": { "type": ["integer", "null"] },
- "time_estimate": { "type": "integer" },
- "type": { "type": "string", "enum": ["ISSUE", "INCIDENT", "TEST_CASE", "REQUIREMENT"] },
- "issue_sidebar_endpoint": { "type": "string" },
- "toggle_subscription_endpoint": { "type": "string" },
- "assignable_labels_endpoint": { "type": "string" },
- "reference_path": { "type": "string" },
- "real_path": { "type": "string" },
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "iid": {
+ "type": "integer"
+ },
+ "project_id": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "title": {
+ "type": "string"
+ },
+ "confidential": {
+ "type": "boolean"
+ },
+ "due_date": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "relative_position": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "time_estimate": {
+ "type": "integer"
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "ISSUE",
+ "INCIDENT",
+ "TEST_CASE",
+ "REQUIREMENT"
+ ]
+ },
+ "issue_sidebar_endpoint": {
+ "type": "string"
+ },
+ "toggle_subscription_endpoint": {
+ "type": "string"
+ },
+ "assignable_labels_endpoint": {
+ "type": "string"
+ },
+ "reference_path": {
+ "type": "string"
+ },
+ "real_path": {
+ "type": "string"
+ },
"project": {
- "id": { "type": "integer" },
- "path": { "type": "string" }
+ "id": {
+ "type": "integer"
+ },
+ "path": {
+ "type": "string"
+ }
},
"labels": {
"type": "array",
- "items": { "$ref": "entities/label.json" }
+ "items": {
+ "$ref": "entities/label.json"
+ }
},
"assignee": {
- "id": { "type": "integer" },
- "name": { "type": "string" },
- "username": { "type": "string" },
- "avatar_url": { "type": "uri" }
+ "id": {
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ },
+ "username": {
+ "type": "string"
+ },
+ "avatar_url": {
+ "type": "string",
+ "format": "uri"
+ }
},
"assignees": {
"type": "array",
"items": {
- "type": ["object", "null"],
+ "type": [
+ "object",
+ "null"
+ ],
"required": [
"id",
"name",
@@ -45,13 +106,27 @@
"avatar_url"
],
"properties": {
- "id": { "type": "integer" },
- "name": { "type": "string" },
- "username": { "type": "string" },
- "avatar_url": { "type": "uri" }
+ "id": {
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ },
+ "username": {
+ "type": "string"
+ },
+ "avatar_url": {
+ "type": "string",
+ "format": "uri"
+ }
}
}
},
- "subscribed": { "type": ["boolean", "null"] }
+ "subscribed": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/jira_connect/author.json b/spec/fixtures/api/schemas/jira_connect/author.json
index bd2cff96d99..e653db9a97d 100644
--- a/spec/fixtures/api/schemas/jira_connect/author.json
+++ b/spec/fixtures/api/schemas/jira_connect/author.json
@@ -1,12 +1,27 @@
{
"type": "object",
"properties": {
- "name": { "type": "string" },
- "email": { "type": "string" },
- "username": { "type": "string" },
- "url": { "type": "uri" },
- "avatar": { "type": "uri" }
+ "name": {
+ "type": "string"
+ },
+ "email": {
+ "type": "string"
+ },
+ "username": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "avatar": {
+ "type": "string",
+ "format": "uri"
+ }
},
- "required": [ "name", "email" ],
+ "required": [
+ "name",
+ "email"
+ ],
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/jira_connect/branch.json b/spec/fixtures/api/schemas/jira_connect/branch.json
index c397d88fa91..237dc8abbc0 100644
--- a/spec/fixtures/api/schemas/jira_connect/branch.json
+++ b/spec/fixtures/api/schemas/jira_connect/branch.json
@@ -1,19 +1,38 @@
{
"type": "object",
"properties": {
- "id": { "type": "string" },
- "issueKeys": { "type": "array" },
- "name": { "type": "string" },
+ "id": {
+ "type": "string"
+ },
+ "issueKeys": {
+ "type": "array"
+ },
+ "name": {
+ "type": "string"
+ },
"lastCommit": {
"$ref": "./commit.json"
},
- "url": { "type": "uri" },
- "createPullRequestUrl": { "type": "uri" },
- "updateSequenceId": { "type": "integer" }
+ "url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "createPullRequestUrl": {
+ "type": "string",
+ "format": "uri"
+ },
+ "updateSequenceId": {
+ "type": "integer"
+ }
},
"required": [
- "id", "issueKeys", "name", "lastCommit",
- "url", "createPullRequestUrl", "updateSequenceId"
+ "id",
+ "issueKeys",
+ "name",
+ "lastCommit",
+ "url",
+ "createPullRequestUrl",
+ "updateSequenceId"
],
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/jira_connect/commit.json b/spec/fixtures/api/schemas/jira_connect/commit.json
index 794cf9ef365..0c35d650559 100644
--- a/spec/fixtures/api/schemas/jira_connect/commit.json
+++ b/spec/fixtures/api/schemas/jira_connect/commit.json
@@ -1,29 +1,61 @@
{
"type": "object",
"properties": {
- "id": { "type": "string" },
- "issueKeys": { "type": "array" },
- "hash": { "type": "string" },
- "displayId": { "type": "string" },
- "message": { "type": "string" },
- "flags": { "type": "array" },
+ "id": {
+ "type": "string"
+ },
+ "issueKeys": {
+ "type": "array"
+ },
+ "hash": {
+ "type": "string"
+ },
+ "displayId": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ },
+ "flags": {
+ "type": "array"
+ },
"author": {
"$ref": "./author.json"
},
- "fileCount": { "type": "integer" },
+ "fileCount": {
+ "type": "integer"
+ },
"files": {
"type": "array",
"items": {
"$ref": "./file.json"
}
},
- "authorTimestamp": { "type": "timestamp" },
- "url": { "type": "uri" },
- "updateSequenceId": { "type": "integer" }
+ "authorTimestamp": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "updateSequenceId": {
+ "type": "integer"
+ }
},
"required": [
- "id", "issueKeys", "hash", "displayId", "message", "flags", "author",
- "fileCount", "files", "authorTimestamp", "url", "updateSequenceId"
+ "id",
+ "issueKeys",
+ "hash",
+ "displayId",
+ "message",
+ "flags",
+ "author",
+ "fileCount",
+ "files",
+ "authorTimestamp",
+ "url",
+ "updateSequenceId"
],
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/jira_connect/file.json b/spec/fixtures/api/schemas/jira_connect/file.json
index 34718991237..3e93911ece4 100644
--- a/spec/fixtures/api/schemas/jira_connect/file.json
+++ b/spec/fixtures/api/schemas/jira_connect/file.json
@@ -1,14 +1,29 @@
{
"type": "object",
"properties": {
- "path": { "type": "string" },
- "changeType": { "type": "string" },
- "linesAdded": { "type": "integer" },
- "linesRemoved": { "type": "integer" },
- "url": { "type": "uri" }
+ "path": {
+ "type": "string"
+ },
+ "changeType": {
+ "type": "string"
+ },
+ "linesAdded": {
+ "type": "integer"
+ },
+ "linesRemoved": {
+ "type": "integer"
+ },
+ "url": {
+ "type": "string",
+ "format": "uri"
+ }
},
"required": [
- "path", "changeType", "linesAdded", "linesRemoved", "url"
+ "path",
+ "changeType",
+ "linesAdded",
+ "linesRemoved",
+ "url"
],
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/jira_connect/pull_request.json b/spec/fixtures/api/schemas/jira_connect/pull_request.json
index 56ce6faf498..430752335be 100644
--- a/spec/fixtures/api/schemas/jira_connect/pull_request.json
+++ b/spec/fixtures/api/schemas/jira_connect/pull_request.json
@@ -1,26 +1,63 @@
{
"type": "object",
"properties": {
- "id": { "type": "string" },
- "issueKeys": { "type": "array" },
- "displayId": { "type": "string" },
- "title": { "type": "string" },
+ "id": {
+ "type": "string"
+ },
+ "issueKeys": {
+ "type": "array"
+ },
+ "displayId": {
+ "type": "string"
+ },
+ "title": {
+ "type": "string"
+ },
"author": {
"$ref": "./author.json"
},
- "commentCount": { "type": "integer" },
- "sourceBranch": { "type": "string" },
- "destinationBranch": { "type": "string" },
- "lastUpdate": { "type": "timestamp" },
- "status": { "type": "string" },
- "sourceBranchUrl": { "type": "uri" },
- "url": { "type": "uri" },
- "updateSequenceId": { "type": "integer" }
+ "commentCount": {
+ "type": "integer"
+ },
+ "sourceBranch": {
+ "type": "string"
+ },
+ "destinationBranch": {
+ "type": "string"
+ },
+ "lastUpdate": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "status": {
+ "type": "string"
+ },
+ "sourceBranchUrl": {
+ "type": "string",
+ "format": "uri"
+ },
+ "url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "updateSequenceId": {
+ "type": "integer"
+ }
},
"required": [
- "id", "issueKeys", "displayId", "title", "author", "commentCount",
- "sourceBranch", "destinationBranch", "lastUpdate", "status",
- "sourceBranchUrl", "url", "updateSequenceId"
+ "id",
+ "issueKeys",
+ "displayId",
+ "title",
+ "author",
+ "commentCount",
+ "sourceBranch",
+ "destinationBranch",
+ "lastUpdate",
+ "status",
+ "sourceBranchUrl",
+ "url",
+ "updateSequenceId"
],
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/jira_connect/repository.json b/spec/fixtures/api/schemas/jira_connect/repository.json
index 9e81d77bc6a..5a45bf7cc77 100644
--- a/spec/fixtures/api/schemas/jira_connect/repository.json
+++ b/spec/fixtures/api/schemas/jira_connect/repository.json
@@ -1,11 +1,29 @@
{
"type": "object",
"properties": {
- "id": { "type": "string" },
- "name": { "type": "string" },
- "description": { "type": ["string", "null"] },
- "url": { "type": "uri" },
- "avatar": { "type": "uri" },
+ "id": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "avatar": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "uri"
+ },
"commits": {
"type": "array",
"items": {
@@ -24,11 +42,20 @@
"$ref": "./pull_request.json"
}
},
- "updateSequenceId": { "type": "integer" }
+ "updateSequenceId": {
+ "type": "integer"
+ }
},
"required": [
- "id", "name", "description", "url", "avatar",
- "commits", "branches", "pullRequests", "updateSequenceId"
+ "id",
+ "name",
+ "description",
+ "url",
+ "avatar",
+ "commits",
+ "branches",
+ "pullRequests",
+ "updateSequenceId"
],
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/job/build_trace_line.json b/spec/fixtures/api/schemas/job/build_trace_line.json
index 18726dff2bb..ada2b4edb7d 100644
--- a/spec/fixtures/api/schemas/job/build_trace_line.json
+++ b/spec/fixtures/api/schemas/job/build_trace_line.json
@@ -6,13 +6,23 @@
"content"
],
"properties": {
- "offset": { "type": "integer" },
+ "offset": {
+ "type": "integer"
+ },
"content": {
"type": "array",
- "items": { "$ref": "./build_trace_line_content.json" }
+ "items": {
+ "$ref": "./build_trace_line_content.json"
+ }
+ },
+ "section": {
+ "type": "string"
+ },
+ "section_header": {
+ "type": "boolean"
},
- "section": "string",
- "section_header": "boolean",
- "section_duration": "string"
+ "section_duration": {
+ "type": "string"
+ }
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/merge_request.json b/spec/fixtures/api/schemas/merge_request.json
index 36962660cd9..416391e454f 100644
--- a/spec/fixtures/api/schemas/merge_request.json
+++ b/spec/fixtures/api/schemas/merge_request.json
@@ -1,12 +1,17 @@
{
"type": "object",
- "required" : [
+ "required": [
"iid",
"url"
],
- "properties" : {
- "iid": { "type": "integer" },
- "url": { "type": "uri" }
+ "properties": {
+ "iid": {
+ "type": "integer"
+ },
+ "url": {
+ "type": "string",
+ "format": "uri"
+ }
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/ml/get_experiment.json b/spec/fixtures/api/schemas/ml/get_experiment.json
index 482455a89e1..13630ec92e9 100644
--- a/spec/fixtures/api/schemas/ml/get_experiment.json
+++ b/spec/fixtures/api/schemas/ml/get_experiment.json
@@ -23,11 +23,28 @@
"type": "string"
},
"lifecycle_stage": {
- "type": {
- "enum": [
- "active",
- "deleted"
- ]
+ "type": "string",
+ "enum": [
+ "active",
+ "deleted"
+ ]
+ },
+ "tags": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "key",
+ "value"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ }
+ }
}
}
}
diff --git a/spec/fixtures/api/schemas/ml/list_experiments.json b/spec/fixtures/api/schemas/ml/list_experiments.json
index 4c3e834abc6..10f512cd1eb 100644
--- a/spec/fixtures/api/schemas/ml/list_experiments.json
+++ b/spec/fixtures/api/schemas/ml/list_experiments.json
@@ -25,12 +25,11 @@
"type": "string"
},
"lifecycle_stage": {
- "type": {
- "enum": [
- "active",
- "deleted"
- ]
- }
+ "type": "string",
+ "enum": [
+ "active",
+ "deleted"
+ ]
}
}
}
diff --git a/spec/fixtures/api/schemas/ml/run.json b/spec/fixtures/api/schemas/ml/run.json
index 48d0ed25ce4..5591eee0800 100644
--- a/spec/fixtures/api/schemas/ml/run.json
+++ b/spec/fixtures/api/schemas/ml/run.json
@@ -45,24 +45,24 @@
"end_time": {
"type": "integer"
},
- "user_id": "",
+ "user_id": {
+ "type": "string"
+ },
"status": {
- "type": {
- "enum": [
- "RUNNING",
- "SCHEDULED",
- "FINISHED",
- "FAILED",
- "KILLED"
- ]
- }
+ "type": "string",
+ "enum": [
+ "RUNNING",
+ "SCHEDULED",
+ "FINISHED",
+ "FAILED",
+ "KILLED"
+ ]
},
"lifecycle_stage": {
- "type": {
- "enum": [
- "active"
- ]
- }
+ "type": "string",
+ "enum": [
+ "active"
+ ]
}
}
},
diff --git a/spec/fixtures/api/schemas/ml/update_run.json b/spec/fixtures/api/schemas/ml/update_run.json
index b429444120f..fb1291a9bb9 100644
--- a/spec/fixtures/api/schemas/ml/update_run.json
+++ b/spec/fixtures/api/schemas/ml/update_run.json
@@ -20,16 +20,44 @@
"end_time"
],
"properties": {
- "run_id": { "type": "string" },
- "run_uuid": { "type": "string" },
- "experiment_id": { "type": "string" },
- "artifact_location": { "type": "string" },
- "start_time": { "type": "integer" },
- "end_time": { "type": "integer" },
- "user_id": { "type": "string" },
- "status": { "type": { "enum" : ["RUNNING", "SCHEDULED", "FINISHED", "FAILED", "KILLED"] } },
- "lifecycle_stage": { "type": { "enum" : ["active"] } }
+ "run_id": {
+ "type": "string"
+ },
+ "run_uuid": {
+ "type": "string"
+ },
+ "experiment_id": {
+ "type": "string"
+ },
+ "artifact_location": {
+ "type": "string"
+ },
+ "start_time": {
+ "type": "integer"
+ },
+ "end_time": {
+ "type": "integer"
+ },
+ "user_id": {
+ "type": "string"
+ },
+ "status": {
+ "type": "string",
+ "enum": [
+ "RUNNING",
+ "SCHEDULED",
+ "FINISHED",
+ "FAILED",
+ "KILLED"
+ ]
+ },
+ "lifecycle_stage": {
+ "type": "string",
+ "enum": [
+ "active"
+ ]
+ }
}
}
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/pipeline_schedule.json b/spec/fixtures/api/schemas/pipeline_schedule.json
index ef5942b7eb3..ade5a76d934 100644
--- a/spec/fixtures/api/schemas/pipeline_schedule.json
+++ b/spec/fixtures/api/schemas/pipeline_schedule.json
@@ -1,53 +1,142 @@
{
"type": "object",
- "properties" : {
- "id": { "type": "integer" },
- "description": { "type": "string" },
- "ref": { "type": "string" },
- "cron": { "type": "string" },
- "cron_timezone": { "type": "string" },
- "next_run_at": { "type": "string" },
- "active": { "type": "boolean" },
- "created_at": { "type": ["string", "null"], "format": "date-time" },
- "updated_at": { "type": ["string", "null"], "format": "date-time" },
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "description": {
+ "type": "string"
+ },
+ "ref": {
+ "type": "string"
+ },
+ "cron": {
+ "type": "string"
+ },
+ "cron_timezone": {
+ "type": "string"
+ },
+ "next_run_at": {
+ "type": "string"
+ },
+ "active": {
+ "type": "boolean"
+ },
+ "created_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
"last_pipeline": {
- "type": ["object", "null"],
+ "type": [
+ "object",
+ "null"
+ ],
"properties": {
- "id": { "type": "integer" },
- "iid": { "type": "integer" },
- "project_id": { "type": "integer" },
- "sha": { "type": "string" },
- "ref": { "type": "string" },
- "status": { "type": "string" },
- "source": { "type": "string" },
- "web_url": { "type": ["string", "null"] },
- "created_at": { "type": ["string", "null"], "format": "date-time" },
- "updated_at": { "type": ["string", "null"], "format": "date-time" }
+ "id": {
+ "type": "integer"
+ },
+ "iid": {
+ "type": "integer"
+ },
+ "project_id": {
+ "type": "integer"
+ },
+ "sha": {
+ "type": "string"
+ },
+ "ref": {
+ "type": "string"
+ },
+ "status": {
+ "type": "string"
+ },
+ "source": {
+ "type": "string"
+ },
+ "web_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "created_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
},
"additionalProperties": false
},
"owner": {
"type": "object",
"properties": {
- "name": { "type": "string" },
- "username": { "type": "string" },
- "id": { "type": "integer" },
- "state": { "type": "string" },
- "avatar_url": { "type": "uri" },
- "web_url": { "type": "uri" }
+ "name": {
+ "type": "string"
+ },
+ "username": {
+ "type": "string"
+ },
+ "id": {
+ "type": "integer"
+ },
+ "state": {
+ "type": "string"
+ },
+ "avatar_url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "web_url": {
+ "type": "string",
+ "format": "uri"
+ }
},
"required": [
- "id", "name", "username", "state", "avatar_url", "web_url"
+ "id",
+ "name",
+ "username",
+ "state",
+ "avatar_url",
+ "web_url"
]
},
"variables": {
"type": "array",
- "items": { "$ref": "pipeline_schedule_variable.json" }
+ "items": {
+ "$ref": "pipeline_schedule_variable.json"
+ }
}
},
"required": [
- "id", "description", "ref", "cron", "cron_timezone", "next_run_at",
- "active", "created_at", "updated_at", "owner"
+ "id",
+ "description",
+ "ref",
+ "cron",
+ "cron_timezone",
+ "next_run_at",
+ "active",
+ "created_at",
+ "updated_at",
+ "owner"
],
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/prometheus/additional_metrics_query_result.json b/spec/fixtures/api/schemas/prometheus/additional_metrics_query_result.json
index 47b5d283b8c..f32ae3d334c 100644
--- a/spec/fixtures/api/schemas/prometheus/additional_metrics_query_result.json
+++ b/spec/fixtures/api/schemas/prometheus/additional_metrics_query_result.json
@@ -1,13 +1,29 @@
{
+ "type": "array",
"items": {
+ "type": "object",
+ "required": [
+ "group",
+ "metrics",
+ "priority"
+ ],
"properties": {
"group": {
"type": "string"
},
"metrics": {
+ "type": "array",
"items": {
+ "type": "object",
+ "required": [
+ "queries",
+ "title",
+ "weight"
+ ],
"properties": {
"queries": {
+ "type": "array",
+ "required": [],
"items": {
"properties": {
"query_range": {
@@ -16,13 +32,33 @@
"query": {
"type": "string"
},
+ "label": {
+ "type": "string"
+ },
+ "unit": {
+ "type": "string"
+ },
"result": {
- "type": "any"
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "metric",
+ "values"
+ ],
+ "properties": {
+ "metric": {
+ "type": "object"
+ },
+ "values": {
+ "type": "array"
+ }
+ }
+ }
}
},
"type": "object"
- },
- "type": "array"
+ }
},
"title": {
"type": "string"
@@ -33,26 +69,12 @@
"y_label": {
"type": "string"
}
- },
- "type": "object"
- },
- "required": [
- "metrics",
- "title",
- "weight"
- ],
- "type": "array"
+ }
+ }
},
"priority": {
"type": "integer"
}
- },
- "type": "object"
- },
- "required": [
- "group",
- "priority",
- "metrics"
- ],
- "type": "array"
+ }
+ }
} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/basic_environment.json b/spec/fixtures/api/schemas/public_api/v4/basic_environment.json
new file mode 100644
index 00000000000..f22f73f573b
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/basic_environment.json
@@ -0,0 +1,34 @@
+{
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "slug",
+ "external_url",
+ "created_at",
+ "updated_at"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ },
+ "slug": {
+ "type": "string"
+ },
+ "external_url": {
+ "$ref": "../../types/nullable_string.json"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "additionalProperties": false
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/basic_environments.json b/spec/fixtures/api/schemas/public_api/v4/basic_environments.json
new file mode 100644
index 00000000000..6620e423c96
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/basic_environments.json
@@ -0,0 +1,6 @@
+{
+ "type": "array",
+ "items": {
+ "$ref": "./basic_environment.json"
+ }
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/branch.json b/spec/fixtures/api/schemas/public_api/v4/branch.json
index 0073a6d89fc..ed65f3c2e5c 100644
--- a/spec/fixtures/api/schemas/public_api/v4/branch.json
+++ b/spec/fixtures/api/schemas/public_api/v4/branch.json
@@ -1,6 +1,6 @@
{
"type": "object",
- "required" : [
+ "required": [
"name",
"commit",
"merged",
@@ -10,16 +10,35 @@
"developers_can_merge",
"web_url"
],
- "properties" : {
- "name": { "type": "string" },
- "commit": { "$ref": "commit/basic.json" },
- "merged": { "type": "boolean" },
- "protected": { "type": "boolean" },
- "default": { "type": "boolean" },
- "developers_can_push": { "type": "boolean" },
- "developers_can_merge": { "type": "boolean" },
- "can_push": { "type": "boolean" },
- "web_url": { "type": "uri" }
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "commit": {
+ "$ref": "commit/basic.json"
+ },
+ "merged": {
+ "type": "boolean"
+ },
+ "protected": {
+ "type": "boolean"
+ },
+ "default": {
+ "type": "boolean"
+ },
+ "developers_can_push": {
+ "type": "boolean"
+ },
+ "developers_can_merge": {
+ "type": "boolean"
+ },
+ "can_push": {
+ "type": "boolean"
+ },
+ "web_url": {
+ "type": "string",
+ "format": "uri"
+ }
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/deploy_key.json b/spec/fixtures/api/schemas/public_api/v4/deploy_key.json
index 99e57a4c218..4f8b5c8422e 100644
--- a/spec/fixtures/api/schemas/public_api/v4/deploy_key.json
+++ b/spec/fixtures/api/schemas/public_api/v4/deploy_key.json
@@ -7,20 +7,50 @@
"expires_at",
"key",
"fingerprint_sha256",
+ "usage_type",
"projects_with_write_access"
],
"properties": {
- "id": { "type": "integer" },
- "title": { "type": "string" },
- "created_at": { "type": "string", "format": "date-time" },
- "expires_at": { "type": ["string", "null"], "format": "date-time" },
- "key": { "type": "string" },
- "fingerprint": { "type": "string" },
- "fingerprint_sha256": { "type": "string" },
+ "id": {
+ "type": "integer"
+ },
+ "title": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "expires_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "key": {
+ "type": "string"
+ },
+ "fingerprint": {
+ "type": "string"
+ },
+ "fingerprint_sha256": {
+ "type": "string"
+ },
+ "usage_type": {
+ "type": "string",
+ "enum": [
+ "auth",
+ "signing",
+ "auth_and_signing"
+ ]
+ },
"projects_with_write_access": {
"type": "array",
- "items": { "$ref": "project/identity.json" }
+ "items": {
+ "$ref": "project/identity.json"
+ }
}
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/deploy_token.json b/spec/fixtures/api/schemas/public_api/v4/deploy_token.json
index 102ab95a4ee..664740c2a3c 100644
--- a/spec/fixtures/api/schemas/public_api/v4/deploy_token.json
+++ b/spec/fixtures/api/schemas/public_api/v4/deploy_token.json
@@ -19,7 +19,9 @@
"username": {
"type": "string"
},
- "expires_at": { "type": "string" },
+ "expires_at": {
+ "type": "string"
+ },
"scopes": {
"type": "array",
"items": {
diff --git a/spec/fixtures/api/schemas/public_api/v4/environments.json b/spec/fixtures/api/schemas/public_api/v4/environments.json
index f739c06f604..1697da0f231 100644
--- a/spec/fixtures/api/schemas/public_api/v4/environments.json
+++ b/spec/fixtures/api/schemas/public_api/v4/environments.json
@@ -1,9 +1,6 @@
{
"type": "array",
"items": {
- "type": "object",
- "properties": {
- "$ref": "./environment.json"
- }
+ "$ref": "./environment.json"
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/feature_flag_scopes.json b/spec/fixtures/api/schemas/public_api/v4/feature_flag_scopes.json
index b1a7021db8b..1df46780a03 100644
--- a/spec/fixtures/api/schemas/public_api/v4/feature_flag_scopes.json
+++ b/spec/fixtures/api/schemas/public_api/v4/feature_flag_scopes.json
@@ -1,9 +1,6 @@
{
"type": "array",
"items": {
- "type": "object",
- "properties": {
- "$ref": "./feature_flag_scope.json"
- }
+ "$ref": "./feature_flag_scope.json"
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/feature_flags.json b/spec/fixtures/api/schemas/public_api/v4/feature_flags.json
index c19df0443d9..f381adc3c8b 100644
--- a/spec/fixtures/api/schemas/public_api/v4/feature_flags.json
+++ b/spec/fixtures/api/schemas/public_api/v4/feature_flags.json
@@ -1,9 +1,6 @@
{
"type": "array",
"items": {
- "type": "object",
- "properties": {
- "$ref": "./feature_flag.json"
- }
+ "$ref": "./feature_flag.json"
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/integration.json b/spec/fixtures/api/schemas/public_api/v4/integration.json
index b6f13d1cfe7..d1538db7de4 100644
--- a/spec/fixtures/api/schemas/public_api/v4/integration.json
+++ b/spec/fixtures/api/schemas/public_api/v4/integration.json
@@ -1,24 +1,62 @@
{
"type": "object",
"properties": {
- "id": { "type": "integer" },
- "title": { "type": "string" },
- "slug": { "type": "string" },
- "created_at": { "type": "date-time" },
- "updated_at": { "type": "date-time" },
- "active": { "type": "boolean" },
- "commit_events": { "type": "boolean" },
- "push_events": { "type": "boolean" },
- "issues_events": { "type": "boolean" },
- "confidential_issues_events": { "type": "boolean" },
- "merge_requests_events": { "type": "boolean" },
- "tag_push_events": { "type": "boolean" },
- "note_events": { "type": "boolean" },
- "confidential_note_events": { "type": "boolean" },
- "pipeline_events": { "type": "boolean" },
- "wiki_page_events": { "type": "boolean" },
- "job_events": { "type": "boolean" },
- "comment_on_event_enabled": { "type": "boolean" }
+ "id": {
+ "type": "integer"
+ },
+ "title": {
+ "type": "string"
+ },
+ "slug": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "active": {
+ "type": "boolean"
+ },
+ "commit_events": {
+ "type": "boolean"
+ },
+ "push_events": {
+ "type": "boolean"
+ },
+ "issues_events": {
+ "type": "boolean"
+ },
+ "confidential_issues_events": {
+ "type": "boolean"
+ },
+ "merge_requests_events": {
+ "type": "boolean"
+ },
+ "tag_push_events": {
+ "type": "boolean"
+ },
+ "note_events": {
+ "type": "boolean"
+ },
+ "confidential_note_events": {
+ "type": "boolean"
+ },
+ "pipeline_events": {
+ "type": "boolean"
+ },
+ "wiki_page_events": {
+ "type": "boolean"
+ },
+ "job_events": {
+ "type": "boolean"
+ },
+ "comment_on_event_enabled": {
+ "type": "boolean"
+ }
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/issue.json b/spec/fixtures/api/schemas/public_api/v4/issue.json
index 90b368b5226..c2b096a922f 100644
--- a/spec/fixtures/api/schemas/public_api/v4/issue.json
+++ b/spec/fixtures/api/schemas/public_api/v4/issue.json
@@ -1,16 +1,47 @@
{
"type": "object",
- "properties" : {
- "id": { "type": "integer" },
- "iid": { "type": "integer" },
- "project_id": { "type": "integer" },
- "title": { "type": "string" },
- "description": { "type": ["string", "null"] },
- "state": { "type": "string" },
- "discussion_locked": { "type": ["boolean", "null"] },
- "closed_at": { "type": ["string", "null"] },
- "created_at": { "type": "string", "format": "date-time" },
- "updated_at": { "type": "string", "format": "date-time" },
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "iid": {
+ "type": "integer"
+ },
+ "project_id": {
+ "type": "integer"
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "state": {
+ "type": "string"
+ },
+ "discussion_locked": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "closed_at": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
"labels": {
"type": "array",
"items": {
@@ -18,58 +49,171 @@
}
},
"milestone": {
- "type": ["object", "null"],
+ "type": [
+ "object",
+ "null"
+ ],
"properties": {
- "id": { "type": "integer" },
- "iid": { "type": "integer" },
- "project_id": { "type": ["integer", "null"] },
- "group_id": { "type": ["integer", "null"] },
- "title": { "type": "string" },
- "description": { "type": ["string", "null"] },
- "state": { "type": "string" },
- "created_at": { "type": "string", "format": "date-time" },
- "updated_at": { "type": "string", "format": "date-time" },
- "due_date": { "type": "string" , "format": "date-time" },
- "start_date": { "type": "string", "format": "date-time" }
+ "id": {
+ "type": "integer"
+ },
+ "iid": {
+ "type": "integer"
+ },
+ "project_id": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "group_id": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "state": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "due_date": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "anyOf": [
+ {
+ "format": "date-time"
+ },
+ {
+ "format": "date"
+ }
+ ]
+ },
+ "start_date": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "anyOf": [
+ {
+ "format": "date-time"
+ },
+ {
+ "format": "date"
+ }
+ ]
+ },
+ "expired": {
+ "type": "boolean"
+ },
+ "web_url": {
+ "type": "string",
+ "format": "uri"
+ }
},
"additionalProperties": false
},
"assignees": {
"type": "array",
"items": {
- "type": ["object", "null"],
+ "type": [
+ "object",
+ "null"
+ ],
"properties": {
- "name": { "type": "string" },
- "username": { "type": "string" },
- "id": { "type": "integer" },
- "state": { "type": "string" },
- "avatar_url": { "type": "uri" },
- "web_url": { "type": "uri" }
+ "name": {
+ "type": "string"
+ },
+ "username": {
+ "type": "string"
+ },
+ "id": {
+ "type": "integer"
+ },
+ "state": {
+ "type": "string"
+ },
+ "avatar_url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "web_url": {
+ "type": "string",
+ "format": "uri"
+ }
},
"additionalProperties": false
}
},
"assignee": {
- "type": ["object", "null"],
+ "type": [
+ "object",
+ "null"
+ ],
"properties": {
- "name": { "type": "string" },
- "username": { "type": "string" },
- "id": { "type": "integer" },
- "state": { "type": "string" },
- "avatar_url": { "type": "uri" },
- "web_url": { "type": "uri" }
+ "name": {
+ "type": "string"
+ },
+ "username": {
+ "type": "string"
+ },
+ "id": {
+ "type": "integer"
+ },
+ "state": {
+ "type": "string"
+ },
+ "avatar_url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "web_url": {
+ "type": "string",
+ "format": "uri"
+ }
},
"additionalProperties": false
},
"author": {
"type": "object",
"properties": {
- "name": { "type": "string" },
- "username": { "type": "string" },
- "id": { "type": "integer" },
- "state": { "type": "string" },
- "avatar_url": { "type": "uri" },
- "web_url": { "type": "uri" }
+ "name": {
+ "type": "string"
+ },
+ "username": {
+ "type": "string"
+ },
+ "id": {
+ "type": "integer"
+ },
+ "state": {
+ "type": "string"
+ },
+ "avatar_url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "web_url": {
+ "type": "string",
+ "format": "uri"
+ }
},
"required": [
"id",
@@ -80,30 +224,88 @@
"web_url"
]
},
- "user_notes_count": { "type": "integer" },
- "upvotes": { "type": "integer" },
- "downvotes": { "type": "integer" },
- "due_date": { "type": ["string", "null"] },
- "confidential": { "type": "boolean" },
- "web_url": { "type": "uri" },
- "severity": { "type": "string", "enum": ["UNKNOWN", "LOW", "MEDIUM", "HIGH", "CRITICAL"] },
+ "user_notes_count": {
+ "type": "integer"
+ },
+ "upvotes": {
+ "type": "integer"
+ },
+ "downvotes": {
+ "type": "integer"
+ },
+ "due_date": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "confidential": {
+ "type": "boolean"
+ },
+ "web_url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "severity": {
+ "type": "string",
+ "enum": [
+ "UNKNOWN",
+ "LOW",
+ "MEDIUM",
+ "HIGH",
+ "CRITICAL"
+ ]
+ },
"time_stats": {
- "time_estimate": { "type": "integer" },
- "total_time_spent": { "type": "integer" },
- "human_time_estimate": { "type": ["string", "null"] },
- "human_total_time_spent": { "type": ["string", "null"] }
+ "time_estimate": {
+ "type": "integer"
+ },
+ "total_time_spent": {
+ "type": "integer"
+ },
+ "human_time_estimate": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "human_total_time_spent": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
},
"references": {
- "short": {"type": "string"},
- "relative": {"type": "string"},
- "full": {"type": "string"}
+ "short": {
+ "type": "string"
+ },
+ "relative": {
+ "type": "string"
+ },
+ "full": {
+ "type": "string"
+ }
}
},
"required": [
- "id", "iid", "project_id", "title", "description",
- "state", "created_at", "updated_at", "labels",
- "milestone", "assignees", "author", "user_notes_count",
- "upvotes", "downvotes", "due_date", "confidential",
+ "id",
+ "iid",
+ "project_id",
+ "title",
+ "description",
+ "state",
+ "created_at",
+ "updated_at",
+ "labels",
+ "milestone",
+ "assignees",
+ "author",
+ "user_notes_count",
+ "upvotes",
+ "downvotes",
+ "due_date",
+ "confidential",
"web_url"
]
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/issues.json b/spec/fixtures/api/schemas/public_api/v4/issues.json
index c76806705e8..5ef97b020f9 100644
--- a/spec/fixtures/api/schemas/public_api/v4/issues.json
+++ b/spec/fixtures/api/schemas/public_api/v4/issues.json
@@ -1,9 +1,6 @@
{
"type": "array",
"items": {
- "type": "object",
- "properties" : {
- "$ref": "./issue.json"
- }
+ "$ref": "./issue.json"
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/labels/label_with_counts.json b/spec/fixtures/api/schemas/public_api/v4/labels/label_with_counts.json
index 2331932e07d..4aa3199919b 100644
--- a/spec/fixtures/api/schemas/public_api/v4/labels/label_with_counts.json
+++ b/spec/fixtures/api/schemas/public_api/v4/labels/label_with_counts.json
@@ -1,16 +1,22 @@
{
"type": "object",
- "properties": {
- "allOf": [
- { "$ref": "label.json" },
- {
- "type": "object",
- "properties": {
- "open_issues_count": { "type": "integer" },
- "closed_issues_count": { "type": "integer" },
- "open_merge_requests_count": { "type": "integer" }
+ "allOf": [
+ {
+ "$ref": "label.json"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "open_issues_count": {
+ "type": "integer"
+ },
+ "closed_issues_count": {
+ "type": "integer"
+ },
+ "open_merge_requests_count": {
+ "type": "integer"
}
}
- ]
- }
-}
+ }
+ ]
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/labels/project_label.json b/spec/fixtures/api/schemas/public_api/v4/labels/project_label.json
index a9a065ee71f..ec4f64b6772 100644
--- a/spec/fixtures/api/schemas/public_api/v4/labels/project_label.json
+++ b/spec/fixtures/api/schemas/public_api/v4/labels/project_label.json
@@ -1,15 +1,22 @@
{
"type": "object",
- "properties": {
- "allOf": [
- { "$ref": "label.json" },
- {
- "type": "object",
- "properties": {
- "priority": { "type": ["integer", "null"] },
- "is_project_label": { "type": "boolean" }
+ "allOf": [
+ {
+ "$ref": "label.json"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "priority": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "is_project_label": {
+ "type": "boolean"
}
}
- ]
- }
-}
+ }
+ ]
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/labels/project_label_with_counts.json b/spec/fixtures/api/schemas/public_api/v4/labels/project_label_with_counts.json
index 87b90b2b3b5..cc0dc4024fe 100644
--- a/spec/fixtures/api/schemas/public_api/v4/labels/project_label_with_counts.json
+++ b/spec/fixtures/api/schemas/public_api/v4/labels/project_label_with_counts.json
@@ -1,9 +1,11 @@
{
"type": "object",
- "properties": {
- "allOf": [
- { "$ref": "project_label.json" },
- { "$ref": "label_with_counts.json" }
- ]
- }
-}
+ "allOf": [
+ {
+ "$ref": "project_label.json"
+ },
+ {
+ "$ref": "label_with_counts.json"
+ }
+ ]
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/merge_request.json b/spec/fixtures/api/schemas/public_api/v4/merge_request.json
index 1ef2f9f9534..9a6c1757eea 100644
--- a/spec/fixtures/api/schemas/public_api/v4/merge_request.json
+++ b/spec/fixtures/api/schemas/public_api/v4/merge_request.json
@@ -1,25 +1,75 @@
{
"type": "object",
- "properties" : {
- "id": { "type": "integer" },
- "iid": { "type": "integer" },
- "project_id": { "type": "integer" },
- "title": { "type": "string" },
- "description": { "type": ["string", "null"] },
- "state": { "type": "string" },
- "merged_by": { "$ref": "user/basic.json" },
- "merge_user": { "$ref": "user/basic.json" },
- "merged_at": { "type": ["string", "null"] },
- "closed_by": { "$ref": "user/basic.json" },
- "closed_at": { "type": ["string", "null"], "format": "date-time" },
- "created_at": { "type": "string", "format": "date-time" },
- "updated_at": { "type": "string", "format": "date-time" },
- "target_branch": { "type": "string" },
- "source_branch": { "type": "string" },
- "upvotes": { "type": "integer" },
- "downvotes": { "type": "integer" },
- "author": { "$ref": "user/basic.json" },
- "assignee": { "$ref": "user/basic.json" },
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "iid": {
+ "type": "integer"
+ },
+ "project_id": {
+ "type": "integer"
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "state": {
+ "type": "string"
+ },
+ "merged_by": {
+ "$ref": "user/basic.json"
+ },
+ "merge_user": {
+ "$ref": "user/basic.json"
+ },
+ "merged_at": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "closed_by": {
+ "$ref": "user/basic.json"
+ },
+ "closed_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "target_branch": {
+ "type": "string"
+ },
+ "source_branch": {
+ "type": "string"
+ },
+ "upvotes": {
+ "type": "integer"
+ },
+ "downvotes": {
+ "type": "integer"
+ },
+ "author": {
+ "$ref": "user/basic.json"
+ },
+ "assignee": {
+ "$ref": "user/basic.json"
+ },
"assignees": {
"type": "array",
"items": {
@@ -32,60 +82,159 @@
"$ref": "user/basic.json"
}
},
- "source_project_id": { "type": "integer" },
- "target_project_id": { "type": "integer" },
+ "source_project_id": {
+ "type": "integer"
+ },
+ "target_project_id": {
+ "type": "integer"
+ },
"labels": {
"type": "array",
"items": {
"type": "string"
}
},
- "work_in_progress": { "type": "boolean" },
+ "work_in_progress": {
+ "type": "boolean"
+ },
"milestone": {
"oneOf": [
- { "type": "null" },
- { "$ref": "milestone.json" }
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "milestone.json"
+ }
]
},
- "merge_when_pipeline_succeeds": { "type": "boolean" },
- "merge_status": { "type": "string" },
- "sha": { "type": "string" },
- "merge_commit_sha": { "type": ["string", "null"] },
- "user_notes_count": { "type": "integer" },
- "changes_count": { "type": "string" },
- "should_remove_source_branch": { "type": ["boolean", "null"] },
- "force_remove_source_branch": { "type": ["boolean", "null"] },
- "discussion_locked": { "type": ["boolean", "null"] },
- "web_url": { "type": "uri" },
- "squash": { "type": "boolean" },
+ "merge_when_pipeline_succeeds": {
+ "type": "boolean"
+ },
+ "merge_status": {
+ "type": "string"
+ },
+ "sha": {
+ "type": "string"
+ },
+ "merge_commit_sha": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "user_notes_count": {
+ "type": "integer"
+ },
+ "changes_count": {
+ "type": "string"
+ },
+ "should_remove_source_branch": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "force_remove_source_branch": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "discussion_locked": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "web_url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "squash": {
+ "type": "boolean"
+ },
"time_stats": {
- "time_estimate": { "type": "integer" },
- "total_time_spent": { "type": "integer" },
- "human_time_estimate": { "type": ["string", "null"] },
- "human_total_time_spent": { "type": ["string", "null"] }
+ "time_estimate": {
+ "type": "integer"
+ },
+ "total_time_spent": {
+ "type": "integer"
+ },
+ "human_time_estimate": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "human_total_time_spent": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "allow_collaboration": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "allow_maintainer_to_push": {
+ "type": [
+ "boolean",
+ "null"
+ ]
},
- "allow_collaboration": { "type": ["boolean", "null"] },
- "allow_maintainer_to_push": { "type": ["boolean", "null"] },
"references": {
- "short": {"type": "string"},
- "relative": {"type": "string"},
- "full": {"type": "string"}
+ "short": {
+ "type": "string"
+ },
+ "relative": {
+ "type": "string"
+ },
+ "full": {
+ "type": "string"
+ }
}
},
"required": [
- "id", "iid", "project_id", "title", "description",
- "state", "created_at", "updated_at", "target_branch",
- "source_branch", "upvotes", "downvotes", "author",
- "assignee", "source_project_id", "target_project_id",
- "labels", "work_in_progress", "milestone", "merge_when_pipeline_succeeds",
- "merge_status", "sha", "merge_commit_sha", "user_notes_count",
- "should_remove_source_branch", "force_remove_source_branch",
- "web_url", "squash"
+ "id",
+ "iid",
+ "project_id",
+ "title",
+ "description",
+ "state",
+ "created_at",
+ "updated_at",
+ "target_branch",
+ "source_branch",
+ "upvotes",
+ "downvotes",
+ "author",
+ "assignee",
+ "source_project_id",
+ "target_project_id",
+ "labels",
+ "work_in_progress",
+ "milestone",
+ "merge_when_pipeline_succeeds",
+ "merge_status",
+ "sha",
+ "merge_commit_sha",
+ "user_notes_count",
+ "should_remove_source_branch",
+ "force_remove_source_branch",
+ "web_url",
+ "squash"
],
"head_pipeline": {
"oneOf": [
- { "type": "null" },
- { "$ref": "pipeline/detail.json" }
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "pipeline/detail.json"
+ }
]
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/merge_request_simple.json b/spec/fixtures/api/schemas/public_api/v4/merge_request_simple.json
index f176e5ee261..48de8f13547 100644
--- a/spec/fixtures/api/schemas/public_api/v4/merge_request_simple.json
+++ b/spec/fixtures/api/schemas/public_api/v4/merge_request_simple.json
@@ -1,26 +1,59 @@
{
"type": "object",
- "properties" : {
- "properties" : {
- "id": { "type": "integer" },
- "iid": { "type": "integer" },
- "project_id": { "type": "integer" },
- "title": { "type": "string" },
- "description": { "type": ["string", "null"] },
- "state": { "type": "string" },
- "created_at": { "type": "string", "format": "date-time" },
- "updated_at": { "type": "string", "format": "date-time" },
- "web_url": { "type": "uri" }
- },
- "required": [
- "id", "iid", "project_id", "title", "description",
- "state", "created_at", "updated_at", "web_url"
- ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "iid": {
+ "type": "integer"
+ },
+ "project_id": {
+ "type": "integer"
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "state": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "web_url": {
+ "type": "string",
+ "format": "uri"
+ },
"head_pipeline": {
"oneOf": [
- { "type": "null" },
- { "$ref": "pipeline/detail.json" }
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "pipeline/detail.json"
+ }
]
}
- }
-}
+ },
+ "required": [
+ "id",
+ "iid",
+ "project_id",
+ "title",
+ "description",
+ "state",
+ "created_at",
+ "updated_at",
+ "web_url"
+ ]
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/milestone.json b/spec/fixtures/api/schemas/public_api/v4/milestone.json
index e7e0e57f02f..c33c4044a62 100644
--- a/spec/fixtures/api/schemas/public_api/v4/milestone.json
+++ b/spec/fixtures/api/schemas/public_api/v4/milestone.json
@@ -1,24 +1,77 @@
{
"type": "object",
- "properties" : {
- "id": { "type": "integer" },
- "iid": { "type": "integer" },
- "project_id": { "type": ["integer", "null"] },
- "group_id": { "type": ["integer", "null"] },
- "title": { "type": "string" },
- "description": { "type": ["string", "null"] },
- "state": { "type": "string" },
- "created_at": { "type": "string" },
- "updated_at": { "type": "string" },
- "start_date": { "type": ["string", "null"], "format": "date-time" },
- "due_date": { "type": ["string", "null"], "format": "date-time" },
- "expired": { "type": ["boolean", "null"] },
- "web_url": { "type": "string" }
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "iid": {
+ "type": "integer"
+ },
+ "project_id": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "group_id": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "state": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "string"
+ },
+ "updated_at": {
+ "type": "string"
+ },
+ "start_date": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "due_date": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "expired": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "web_url": {
+ "type": "string"
+ }
},
"required": [
- "id", "iid", "title", "description", "state",
- "state", "created_at", "updated_at", "start_date",
- "due_date", "expired"
+ "id",
+ "iid",
+ "title",
+ "description",
+ "state",
+ "created_at",
+ "updated_at",
+ "start_date",
+ "due_date",
+ "expired"
],
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/milestone_with_stats.json b/spec/fixtures/api/schemas/public_api/v4/milestone_with_stats.json
index d09d1634eb9..b5fa161ec2a 100644
--- a/spec/fixtures/api/schemas/public_api/v4/milestone_with_stats.json
+++ b/spec/fixtures/api/schemas/public_api/v4/milestone_with_stats.json
@@ -1,32 +1,95 @@
{
"type": "object",
- "properties" : {
- "id": { "type": "integer" },
- "iid": { "type": "integer" },
- "project_id": { "type": ["integer", "null"] },
- "group_id": { "type": ["integer", "null"] },
- "title": { "type": "string" },
- "description": { "type": ["string", "null"] },
- "state": { "type": "string" },
- "created_at": { "type": "string", "format": "date-time" },
- "updated_at": { "type": "string", "format": "date-time" },
- "start_date": { "type": ["string", "null"], "format": "date-time" },
- "due_date": { "type": ["string", "null"], "format": "date-time" },
- "expired": { "type": ["boolean", "null"] },
- "web_url": { "type": "string" },
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "iid": {
+ "type": "integer"
+ },
+ "project_id": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "group_id": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "state": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "start_date": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "due_date": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "expired": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "web_url": {
+ "type": "string"
+ },
"issue_stats": {
- "required": ["total", "closed"],
+ "required": [
+ "total",
+ "closed"
+ ],
"properties": {
- "total": { "type": "integer" },
- "closed": { "type": "integer" }
+ "total": {
+ "type": "integer"
+ },
+ "closed": {
+ "type": "integer"
+ }
},
"additionalProperties": false
}
},
"required": [
- "id", "iid", "title", "description", "state",
- "state", "created_at", "updated_at", "start_date",
- "due_date", "expired", "issue_stats"
+ "id",
+ "iid",
+ "title",
+ "description",
+ "state",
+ "created_at",
+ "updated_at",
+ "start_date",
+ "due_date",
+ "expired",
+ "issue_stats"
],
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/notes.json b/spec/fixtures/api/schemas/public_api/v4/notes.json
index d6d0300a64f..1987a0f2f71 100644
--- a/spec/fixtures/api/schemas/public_api/v4/notes.json
+++ b/spec/fixtures/api/schemas/public_api/v4/notes.json
@@ -2,43 +2,124 @@
"type": "array",
"items": {
"type": "object",
- "properties" : {
- "id": { "type": "integer" },
- "type": { "type": ["string", "null"] },
- "body": { "type": "string" },
- "attachment": { "type": ["string", "null"] },
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "type": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "body": {
+ "type": "string"
+ },
+ "attachment": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
"author": {
"type": "object",
"properties": {
- "name": { "type": "string" },
- "username": { "type": "string" },
- "id": { "type": "integer" },
- "state": { "type": "string" },
- "avatar_url": { "type": "uri" },
- "web_url": { "type": "uri" }
+ "name": {
+ "type": "string"
+ },
+ "username": {
+ "type": "string"
+ },
+ "id": {
+ "type": "integer"
+ },
+ "state": {
+ "type": "string"
+ },
+ "avatar_url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "web_url": {
+ "type": "string",
+ "format": "uri"
+ }
},
- "required" : [
- "id", "name", "username", "state", "avatar_url", "web_url"
+ "required": [
+ "id",
+ "name",
+ "username",
+ "state",
+ "avatar_url",
+ "web_url"
]
},
- "commands_changes": { "type": "object", "additionalProperties": true },
- "created_at": { "type": "string", "format": "date-time" },
- "updated_at": { "type": "string", "format": "date-time" },
- "system": { "type": "boolean" },
- "noteable_id": { "type": "integer" },
- "noteable_iid": { "type": "integer" },
- "noteable_type": { "type": "string" },
- "resolved": { "type": "boolean" },
- "resolvable": { "type": "boolean" },
- "resolved_by": { "type": ["string", "null"] },
- "resolved_at": { "type": ["string", "null"] },
- "confidential": { "type": ["boolean", "null"] },
- "internal": { "type": ["boolean", "null"] }
+ "commands_changes": {
+ "type": "object",
+ "additionalProperties": true
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "system": {
+ "type": "boolean"
+ },
+ "noteable_id": {
+ "type": "integer"
+ },
+ "noteable_iid": {
+ "type": "integer"
+ },
+ "noteable_type": {
+ "type": "string"
+ },
+ "resolved": {
+ "type": "boolean"
+ },
+ "resolvable": {
+ "type": "boolean"
+ },
+ "resolved_by": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "resolved_at": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "confidential": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "internal": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
},
"required": [
- "id", "body", "attachment", "author", "created_at", "updated_at",
- "system", "noteable_id", "noteable_type"
+ "id",
+ "body",
+ "attachment",
+ "author",
+ "created_at",
+ "updated_at",
+ "system",
+ "noteable_id",
+ "noteable_type"
],
"additionalProperties": false
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/packages/package.json b/spec/fixtures/api/schemas/public_api/v4/packages/package.json
index 5d0d5f63aa9..c1b4fc09079 100644
--- a/spec/fixtures/api/schemas/public_api/v4/packages/package.json
+++ b/spec/fixtures/api/schemas/public_api/v4/packages/package.json
@@ -28,12 +28,9 @@
},
"_links": {
"type": "object",
- "required": [
- "web_path"
- ],
"properties": {
- "details": {
- "type": "string"
+ "web_path": {
+ "type": ["string", "null"]
}
}
},
diff --git a/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json b/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
index 66d4be529b1..9bcd191741f 100644
--- a/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
+++ b/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
@@ -1,23 +1,60 @@
{
"type": "object",
"properties": {
- "domain": { "type": "string" },
- "url": { "type": "uri" },
- "project_id": { "type": "integer" },
- "verified": { "type": "boolean" },
- "verification_code": { "type": ["string", "null"] },
- "enabled_until": { "type": ["string", "null"], "format": "date-time" },
- "auto_ssl_enabled": { "type": "boolean" },
+ "domain": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "project_id": {
+ "type": "integer"
+ },
+ "verified": {
+ "type": "boolean"
+ },
+ "verification_code": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "enabled_until": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "auto_ssl_enabled": {
+ "type": "boolean"
+ },
"certificate_expiration": {
"type": "object",
"properties": {
- "expired": { "type": "boolean" },
- "expiration": { "type": "string" }
+ "expired": {
+ "type": "boolean"
+ },
+ "expiration": {
+ "type": "string"
+ }
},
- "required": ["expired", "expiration"],
+ "required": [
+ "expired",
+ "expiration"
+ ],
"additionalProperties": false
}
},
- "required": ["domain", "url", "project_id", "verified", "verification_code", "enabled_until", "auto_ssl_enabled"],
+ "required": [
+ "domain",
+ "url",
+ "project_id",
+ "verified",
+ "verification_code",
+ "enabled_until",
+ "auto_ssl_enabled"
+ ],
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json b/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json
index bbbc610eb27..6ce431a274a 100644
--- a/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json
+++ b/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json
@@ -1,24 +1,61 @@
{
"type": "object",
"properties": {
- "domain": { "type": "string" },
- "url": { "type": "uri" },
- "verified": { "type": "boolean" },
- "verification_code": { "type": ["string", "null"] },
- "enabled_until": { "type": ["string", "null"] },
- "auto_ssl_enabled": { "type": "boolean" },
+ "domain": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "verified": {
+ "type": "boolean"
+ },
+ "verification_code": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "enabled_until": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "auto_ssl_enabled": {
+ "type": "boolean"
+ },
"certificate": {
"type": "object",
"properties": {
- "subject": { "type": "string" },
- "expired": { "type": "boolean" },
- "certificate": { "type": "string" },
- "certificate_text": { "type": "string" }
+ "subject": {
+ "type": "string"
+ },
+ "expired": {
+ "type": "boolean"
+ },
+ "certificate": {
+ "type": "string"
+ },
+ "certificate_text": {
+ "type": "string"
+ }
},
- "required": ["subject", "expired"],
+ "required": [
+ "subject",
+ "expired"
+ ],
"additionalProperties": false
}
},
- "required": ["domain", "url", "verified", "verification_code", "enabled_until", "auto_ssl_enabled"],
+ "required": [
+ "domain",
+ "url",
+ "verified",
+ "verification_code",
+ "enabled_until",
+ "auto_ssl_enabled"
+ ],
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/project_hooks.json b/spec/fixtures/api/schemas/public_api/v4/project_hooks.json
index 8c542ebe3ad..8557e5cae40 100644
--- a/spec/fixtures/api/schemas/public_api/v4/project_hooks.json
+++ b/spec/fixtures/api/schemas/public_api/v4/project_hooks.json
@@ -1,10 +1,6 @@
{
"type": "array",
"items": {
- "type": "object",
- "properties" : {
- "$ref": "./project_hook.json"
- }
+ "$ref": "./project_hook.json"
}
-}
-
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/snippets.json b/spec/fixtures/api/schemas/public_api/v4/snippets.json
index 65299901128..dab6b69f1c1 100644
--- a/spec/fixtures/api/schemas/public_api/v4/snippets.json
+++ b/spec/fixtures/api/schemas/public_api/v4/snippets.json
@@ -2,46 +2,107 @@
"type": "array",
"items": {
"type": "object",
- "properties" : {
- "id": { "type": "integer" },
- "project_id": { "type": ["integer", "null"] },
- "title": { "type": "string" },
- "file_name": { "type": ["string", "null"] },
- "files" : {
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "project_id": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "title": {
+ "type": "string"
+ },
+ "file_name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "files": {
"type": "array",
"items": {
"type": "object",
"properties": {
- "path": { "type": "string" },
- "raw_url": { "type": "string" }
+ "path": {
+ "type": "string"
+ },
+ "raw_url": {
+ "type": "string"
+ }
}
}
},
- "description": { "type": ["string", "null"] },
- "visibility": { "type": "string" },
- "web_url": { "type": "string" },
- "raw_url": { "type": "string" },
- "created_at": { "type": "string", "format": "date-time" },
- "updated_at": { "type": "string", "format": "date-time" },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "visibility": {
+ "type": "string"
+ },
+ "web_url": {
+ "type": "string"
+ },
+ "raw_url": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
"author": {
"type": "object",
"properties": {
- "name": { "type": "string" },
- "username": { "type": "string" },
- "id": { "type": "integer" },
- "state": { "type": "string" },
- "avatar_url": { "type": "uri" },
- "web_url": { "type": "uri" }
+ "name": {
+ "type": "string"
+ },
+ "username": {
+ "type": "string"
+ },
+ "id": {
+ "type": "integer"
+ },
+ "state": {
+ "type": "string"
+ },
+ "avatar_url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "web_url": {
+ "type": "string",
+ "format": "uri"
+ }
},
- "required" : [
- "id", "name", "username", "state", "avatar_url", "web_url"
+ "required": [
+ "id",
+ "name",
+ "username",
+ "state",
+ "avatar_url",
+ "web_url"
]
}
},
"required": [
- "id", "title", "file_name", "description", "web_url",
- "created_at", "updated_at", "author", "raw_url"
+ "id",
+ "title",
+ "file_name",
+ "description",
+ "web_url",
+ "created_at",
+ "updated_at",
+ "author",
+ "raw_url"
],
"additionalProperties": false
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/system_hooks.json b/spec/fixtures/api/schemas/public_api/v4/system_hooks.json
index a56542a8b99..a0800e4bc97 100644
--- a/spec/fixtures/api/schemas/public_api/v4/system_hooks.json
+++ b/spec/fixtures/api/schemas/public_api/v4/system_hooks.json
@@ -1,9 +1,6 @@
{
"type": "array",
"items": {
- "type": "object",
- "properties" : {
- "$ref": "./system_hook.json"
- }
+ "$ref": "./system_hook.json"
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/user/admin.json b/spec/fixtures/api/schemas/public_api/v4/user/admin.json
index f0d3cf3ba0e..7b46cbf5a36 100644
--- a/spec/fixtures/api/schemas/public_api/v4/user/admin.json
+++ b/spec/fixtures/api/schemas/public_api/v4/user/admin.json
@@ -32,6 +32,148 @@
"namespace_id"
],
"properties": {
- "$ref": "full.json"
+ "id": {
+ "type": "integer"
+ },
+ "username": {
+ "type": "string"
+ },
+ "email": {
+ "type": "string",
+ "pattern": "^[^@]+@[^@]+$"
+ },
+ "commit_email": {
+ "type": "string",
+ "pattern": "^[^@]+@[^@]+$"
+ },
+ "name": {
+ "type": "string"
+ },
+ "state": {
+ "type": "string",
+ "enum": [
+ "active",
+ "blocked"
+ ]
+ },
+ "avatar_url": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "uri"
+ },
+ "web_url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "is_admin": {
+ "type": "boolean"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "bio": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "location": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "pronouns": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "skype": {
+ "type": "string"
+ },
+ "linkedin": {
+ "type": "string"
+ },
+ "twitter": {
+ "type": "string"
+ },
+ "website_url": {
+ "type": "string"
+ },
+ "organization": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "last_sign_in_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "confirmed_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "color_scheme_id": {
+ "type": "integer"
+ },
+ "projects_limit": {
+ "type": "integer"
+ },
+ "current_sign_in_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "identities": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "provider": {
+ "type": "string",
+ "enum": [
+ "github",
+ "bitbucket",
+ "google_oauth2",
+ "twitter"
+ ]
+ },
+ "extern_uid": {
+ "type": [
+ "number",
+ "string"
+ ]
+ }
+ }
+ }
+ },
+ "can_create_group": {
+ "type": "boolean"
+ },
+ "can_create_project": {
+ "type": "boolean"
+ },
+ "two_factor_enabled": {
+ "type": "boolean"
+ },
+ "external": {
+ "type": "boolean"
+ },
+ "namespace_id": {
+ "type": "integer"
+ }
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/public_api/v4/user/login.json b/spec/fixtures/api/schemas/public_api/v4/user/login.json
deleted file mode 100644
index aa066883c47..00000000000
--- a/spec/fixtures/api/schemas/public_api/v4/user/login.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "type": "object",
- "required": [
- "id",
- "username",
- "email",
- "name",
- "state",
- "avatar_url",
- "web_url",
- "created_at",
- "is_admin",
- "bio",
- "location",
- "skype",
- "linkedin",
- "twitter",
- "website_url",
- "organization",
- "last_sign_in_at",
- "confirmed_at",
- "theme_id",
- "color_scheme_id",
- "projects_limit",
- "current_sign_in_at",
- "identities",
- "can_create_group",
- "can_create_project",
- "two_factor_enabled",
- "external"
- ],
- "properties": {
- "$ref": "full.json"
- }
-}
diff --git a/spec/fixtures/api/schemas/public_api/v4/user/public.json b/spec/fixtures/api/schemas/public_api/v4/user/public.json
index c4549e3ef63..53d67e041a1 100644
--- a/spec/fixtures/api/schemas/public_api/v4/user/public.json
+++ b/spec/fixtures/api/schemas/public_api/v4/user/public.json
@@ -28,32 +28,95 @@
"external"
],
"properties": {
- "id": { "type": "integer" },
- "username": { "type": "string" },
+ "id": {
+ "type": "integer"
+ },
+ "username": {
+ "type": "string"
+ },
"email": {
"type": "string",
"pattern": "^[^@]+@[^@]+$"
},
- "name": { "type": "string" },
+ "name": {
+ "type": "string"
+ },
"state": {
"type": "string",
- "enum": ["active", "blocked"]
- },
- "avatar_url": { "type": [ "string", "null" ] },
- "web_url": { "type": "string" },
- "created_at": { "type": "string", "format": "date-time" },
- "bio": { "type": ["string", "null"] },
- "location": { "type": ["string", "null"] },
- "skype": { "type": "string" },
- "linkedin": { "type": "string" },
- "twitter": { "type": "string "},
- "website_url": { "type": "string" },
- "organization": { "type": ["string", "null"] },
- "last_sign_in_at": { "type": ["string", "null"], "format": "date-time" },
- "confirmed_at": { "type": ["string", "null"] },
- "color_scheme_id": { "type": "integer" },
- "projects_limit": { "type": "integer" },
- "current_sign_in_at": { "type": ["string", "null"], "format": "date-time" },
+ "enum": [
+ "active",
+ "blocked"
+ ]
+ },
+ "avatar_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "web_url": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "bio": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "location": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "skype": {
+ "type": "string"
+ },
+ "linkedin": {
+ "type": "string"
+ },
+ "twitter": {
+ "type": "string"
+ },
+ "website_url": {
+ "type": "string"
+ },
+ "organization": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "last_sign_in_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "confirmed_at": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "color_scheme_id": {
+ "type": "integer"
+ },
+ "projects_limit": {
+ "type": "integer"
+ },
+ "current_sign_in_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
"identities": {
"type": "array",
"items": {
@@ -61,16 +124,35 @@
"properties": {
"provider": {
"type": "string",
- "enum": ["github", "bitbucket", "google_oauth2"]
+ "enum": [
+ "github",
+ "bitbucket",
+ "google_oauth2"
+ ]
},
- "extern_uid": { "type": ["number", "string"] }
+ "extern_uid": {
+ "type": [
+ "number",
+ "string"
+ ]
+ }
}
}
},
- "can_create_group": { "type": "boolean" },
- "can_create_project": { "type": "boolean" },
- "two_factor_enabled": { "type": "boolean" },
- "external": { "type": "boolean" },
- "commit_email": { "type": "string" }
+ "can_create_group": {
+ "type": "boolean"
+ },
+ "can_create_project": {
+ "type": "boolean"
+ },
+ "two_factor_enabled": {
+ "type": "boolean"
+ },
+ "external": {
+ "type": "boolean"
+ },
+ "commit_email": {
+ "type": "string"
+ }
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/registry/repository.json b/spec/fixtures/api/schemas/registry/repository.json
index 18d2c68ac2f..078fba7a34a 100644
--- a/spec/fixtures/api/schemas/registry/repository.json
+++ b/spec/fixtures/api/schemas/registry/repository.json
@@ -1,6 +1,12 @@
{
"type": "object",
- "required": ["id", "name", "path", "location", "created_at"],
+ "required": [
+ "id",
+ "name",
+ "path",
+ "location",
+ "created_at"
+ ],
"properties": {
"id": {
"type": "integer"
@@ -18,10 +24,15 @@
"type": "string"
},
"created_at": {
- "type": "date-time"
+ "type": "string",
+ "format": "date-time"
},
"cleanup_policy_started_at": {
- "type": "date-time"
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
},
"tags_path": {
"type": "string"
@@ -31,14 +42,24 @@
},
"status": {
"oneOf": [
- { "type": "null" },
- { "type": "string", "enum": ["delete_scheduled", "delete_failed"] }
+ {
+ "type": "null"
+ },
+ {
+ "type": "string",
+ "enum": [
+ "delete_scheduled",
+ "delete_failed"
+ ]
+ }
]
},
- "tags": { "$ref": "tags.json" },
+ "tags": {
+ "$ref": "tags.json"
+ },
"tags_count": {
"type": "integer"
}
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/release.json b/spec/fixtures/api/schemas/release.json
index fe4f8cd2157..11f238ffc92 100644
--- a/spec/fixtures/api/schemas/release.json
+++ b/spec/fixtures/api/schemas/release.json
@@ -1,22 +1,56 @@
{
"type": "object",
- "required": ["tag_name", "description"],
+ "required": [
+ "tag_name",
+ "description"
+ ],
"properties": {
- "name": { "type": "string" },
- "tag_name": { "type": "string" },
- "ref": { "type": "string "},
- "description": { "type": "string" },
- "description_html": { "type": "string" },
- "created_at": { "type": "string", "format": "date-time" },
+ "name": {
+ "type": "string"
+ },
+ "tag_name": {
+ "type": "string"
+ },
+ "ref": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "description_html": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
"commit": {
- "oneOf": [{ "type": "null" }, { "$ref": "public_api/v4/commit/basic.json" }]
+ "oneOf": [
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "public_api/v4/commit/basic.json"
+ }
+ ]
},
"author": {
- "oneOf": [{ "type": "null" }, { "$ref": "public_api/v4/user/basic.json" }]
+ "oneOf": [
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "public_api/v4/user/basic.json"
+ }
+ ]
},
"assets": {
- "count": { "type": "integer" },
- "links": { "$ref": "release/links.json" },
+ "count": {
+ "type": "integer"
+ },
+ "links": {
+ "$ref": "release/links.json"
+ },
"sources": {
"type": "array",
"items": {
@@ -27,4 +61,4 @@
}
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/api/schemas/variable.json b/spec/fixtures/api/schemas/variable.json
index c49d7d8c5ea..440e812d95b 100644
--- a/spec/fixtures/api/schemas/variable.json
+++ b/spec/fixtures/api/schemas/variable.json
@@ -8,13 +8,31 @@
"protected"
],
"properties": {
- "id": { "type": "integer" },
- "key": { "type": "string" },
- "value": { "type": "string" },
- "masked": { "type": "boolean" },
- "protected": { "type": "boolean" },
- "variable_type": { "type": "string" },
- "environment_scope": { "type": "string", "optional": true }
+ "id": {
+ "type": "integer"
+ },
+ "key": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ },
+ "masked": {
+ "type": "boolean"
+ },
+ "protected": {
+ "type": "boolean"
+ },
+ "raw": {
+ "type": "boolean"
+ },
+ "variable_type": {
+ "type": "string"
+ },
+ "environment_scope": {
+ "type": "string",
+ "optional": true
+ }
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/ce_sample_schema.json b/spec/fixtures/ce_sample_schema.json
index e69de29bb2d..9e26dfeeb6e 100644
--- a/spec/fixtures/ce_sample_schema.json
+++ b/spec/fixtures/ce_sample_schema.json
@@ -0,0 +1 @@
+{} \ No newline at end of file
diff --git a/spec/fixtures/clusters/chain_certificates.pem b/spec/fixtures/clusters/chain_certificates.pem
index 63f0b812b66..98ee90046f3 100644
--- a/spec/fixtures/clusters/chain_certificates.pem
+++ b/spec/fixtures/clusters/chain_certificates.pem
@@ -1,85 +1,63 @@
-----BEGIN CERTIFICATE-----
-MIIGYjCCBUqgAwIBAgIQATfkha/xTr3pbLHVWlPq4jANBgkqhkiG9w0BAQsFADBY
-MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEuMCwGA1UE
-AxMlR2xvYmFsU2lnbiBBdGxhcyBSMyBEViBUTFMgQ0EgMjAyMiBRMzAeFw0yMjA3
-MjIxOTQyMTFaFw0yMzA4MjMxOTQyMTBaMBsxGTAXBgNVBAMMEGFib3V0LmdpdGxh
-Yi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFFFQs8EITaWo5
-0U18/mPTDLencU/7siJT/4P8oeDkemyx98wzK6vuNj/2JEZ3v1psKun5n8Pb/fHa
-somKd/4icHgC4rnxrO6zayfb+cKzVghQe12Nj75lx6RtppqTgAmSOa3Tai5niICT
-I8s3d2wsHtfEgqAavcD0/zdPIk25Ji7yfquldSthnlhQqI4Pm3OxTiyFj/V5ZhFl
-IWZLvQaENjBSDVZQDcaPdWwodfXNA8fJmqk7cTLQ9P9NgjWvva7acl+Yd6hOFzV0
-EllBl/WF1KB+YzGuHI0CQHT7sv3GW1lXeE2EqrWoSdLTOSAqm6y02DyE79d1xvG6
-XXfX5ILlAgMBAAGjggNjMIIDXzAbBgNVHREEFDASghBhYm91dC5naXRsYWIuY29t
-MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
-HQYDVR0OBBYEFHK7MnjGDptQWjfmJ2fr3IrjxEopMFcGA1UdIARQME4wCAYGZ4EM
-AQIBMEIGCisGAQQBoDIKAQMwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xv
-YmFsc2lnbi5jb20vcmVwb3NpdG9yeS8wDAYDVR0TAQH/BAIwADCBngYIKwYBBQUH
-AQEEgZEwgY4wQAYIKwYBBQUHMAGGNGh0dHA6Ly9vY3NwLmdsb2JhbHNpZ24uY29t
-L2NhL2dzYXRsYXNyM2R2dGxzY2EyMDIycTMwSgYIKwYBBQUHMAKGPmh0dHA6Ly9z
-ZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2dzYXRsYXNyM2R2dGxzY2EyMDIy
-cTMuY3J0MB8GA1UdIwQYMBaAFPqROWOa+60QJOW+tbnaq9nERmmrMEgGA1UdHwRB
-MD8wPaA7oDmGN2h0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vY2EvZ3NhdGxhc3Iz
-ZHZ0bHNjYTIwMjJxMy5jcmwwggF9BgorBgEEAdZ5AgQCBIIBbQSCAWkBZwB1AG9T
-dqwx8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kTAAABgiduiHEAAAQDAEYwRAIg
-SYQrru/KAKfe+hUqpJmk7Fc8drkgtY3IcAurTOwbM68CIBYO9sbDspd5p7v17RQi
-QQkjdRwSjHiIgvlX0Y1JqmXjAHYArfe++nz/EMiLnT2cHj4YarRnKV3PsQwkyoWG
-NOvcgooAAAGCJ26IcQAABAMARzBFAiBc5a10annqMEH69bdEFy/Vo1gb3S3GQ993
-BCRV7ZXG4gIhAMqnsoKkU6ITwRXwE9KGjHnijJ8QrBrnK0i+JFaGe1ffAHYAs3N3
-B+GEUPhjhtYFqdwRCUp5LbFnDAuH3PADDnk2pZoAAAGCJ26I6QAABAMARzBFAiAf
-lW8Agd0DB68YA8XAbnlq7QNHw3uRMzNdS8gtRUe75gIhANTe+mt2p1ryW83P31OW
-jH3cEGJxdUNT/oDM3Fzesx94MA0GCSqGSIb3DQEBCwUAA4IBAQAfivKEmjqqOFFh
-VsX2XYkoDtreghpqMwHMCLwNk852Alr/Seyv9Ilng8cunU4NmhvEtsYVXkfE4XvB
-0QIVxkg1w7A+p7ejMjh6doLJ0aWNWIVW/DwOeP0qstF9lqvLdLDABoVn0BtYCDTH
-gjG80e2xpvPiKHGvBL+hlOIJwUuIAT3jN23sS1GoiYQGKsz0lovB09/6MGG0Qj8C
-3i9a59T9XBpwSKdpKd4u/CB6koBXD3atbBNBACuAMcFckTEtmkCFtSpqBuocJGKf
-LB4MFVaEwrd7Lc1ACC1et5FDtEI4I3/CerkRZTV+mRz5n6tB91AK3dRvjElfhiuh
-XXYRULvB
+MIIDgDCCAmigAwIBAgIJALI40QyGMz+AMA0GCSqGSIb3DQEBCwUAMHExCzAJBgNV
+BAYTAlVTMRYwFAYDVQQIDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQHDApDYWxpZm9y
+bmlhMRYwFAYDVQQKDA1HaXRMYWIgKFRlc3QpMR0wGwYDVQQDDBRUZXN0IEludGVy
+bWVkaWF0ZSBDQTAgFw0yMjExMjEwMDAzMTZaGA8zMDIyMDMyNDAwMDMxNlowazEL
+MAkGA1UEBhMCVVMxFjAUBgNVBAgMDVNhbiBGcmFuY2lzY28xEzARBgNVBAcMCkNh
+bGlmb3JuaWExFjAUBgNVBAoMDUdpdExhYiAoVGVzdCkxFzAVBgNVBAMMDlRlc3Qg
+TGVhZiBDZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxPHRlMks
+Auba0UxFoL248jfCQNWpEbRoImj/B9W5Zm2sdmJpaD01NVfR+O7eovbWozN6J+gs
+MKS5SUVDIu0E5iVBsEsQlDAF+ckPo3kvoWDAF2cLgNjpRApIiS9YCy9dZLxUl34x
+F4nXy19+53mouw/tX7kPLwnHxTVvBEpDfIttOCTlXnLu62v2LRP5lt5vFXE0B/00
+bD6t+lVl7eamvjDRAz2HoFMY+m1Popts7j4Pj3rWVWfo1RRu88NvjQLnqccFQFnd
+tDsv3YqtiWDdCbcnkIKcgZ3ckpDhTKwRjnKmub4un/OpEgn/WVR1iLCOCDS1RX1u
+j5HKhyiEzRJt1QIDAQABox8wHTAbBgNVHREEFDASghB0ZXN0LmV4YW1wbGUuY29t
+MA0GCSqGSIb3DQEBCwUAA4IBAQA31V60ppRL+/YD1qlGu2uDXKn2VaK41LQxoz7R
+LCQp6qf50XhQFMBu2Mdf5cAbMdgIcAE6BQ7r7kaxO4dSXn9J3cy5Qb4msRvGy63P
+rm0G71TmAK3ihYLP19eT5g9oOHarRc3gXVtUxCAd/XVrxp1GW2vxKTGo46rKKVeU
+myU3Yn9hlu6VszvyaseaDo4Tvd6+vfdpGRo6lcJIdjcQ3Ie2bH6kF1RWXT2sgBW/
+XsDrF7O15DUE9na/7ak8tCOnxFaag7f7SLEHC+NSiMkrRry7Yc3WEFMeJTzHCXGn
+5Uwl4FKujmFkTwOPOeKyWIkDJg+PQVXnVrTvZlx+6k+Kpv2j
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIIEjzCCA3egAwIBAgIQfCoMIT/GVVNFyR8ZH7hO+jANBgkqhkiG9w0BAQsFADBM
-MSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xv
-YmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0yMjA0MjAxMjAwMDBaFw0y
-NTA0MjAwMDAwMDBaMFgxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWdu
-IG52LXNhMS4wLAYDVQQDEyVHbG9iYWxTaWduIEF0bGFzIFIzIERWIFRMUyBDQSAy
-MDIyIFEzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuKh6ZjxOZpzO
-N6VUNU02x5nTqCc28i/G1Rg+6QndBdbXLDQyfAhjSdEQN+V4XRFizm37Lz83lNuP
-ezDpXizZVT+y27mgtWA3i6QGMjVQpAmvCkX/qB+bZY7dSuBAoeNjN1iQ3XU7/A4c
-gkCYvXCxwUgUFDwES2nd1JwBpukh44IK/uSqvzSgjMvJeW4+XGpSnsTtK8Vp/lA8
-k521/y0oqGwGbJ3Fr7JZ+1l3DXR6iISk1B3UuiAGzLUeSE50IRWGdcDMWtEFz1cW
-ehMX7MJKrtUecqoiWoycgjLEEOZCbiGGaHyAIzA1072wXgopK/AUsRg32Vklw+c4
-2enULTY1ZQIDAQABo4IBXzCCAVswDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG
-CCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQW
-BBT6kTljmvutECTlvrW52qvZxEZpqzAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpj
-move4t0bvDB7BggrBgEFBQcBAQRvMG0wLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3Nw
-Mi5nbG9iYWxzaWduLmNvbS9yb290cjMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9zZWN1
-cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L3Jvb3QtcjMuY3J0MDYGA1UdHwQvMC0w
-K6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC1yMy5jcmwwIQYD
-VR0gBBowGDAIBgZngQwBAgEwDAYKKwYBBAGgMgoBAzANBgkqhkiG9w0BAQsFAAOC
-AQEAFDMseeU/gsZwP9pZOKe7onasYRgFaFfZDfuKRrzxqOgMcAIdxi+X7TY+nlKG
-L1xi2NVHQ5pz0Sslh59EtBTrJrwhR3QgvZ+kv7OAHU01fc25tdpV8pBQyLIXTg60
-YYgpX0RdA39XkYHQ6zCu1SrsgiDOTtKwi5UCYXPYaTT0rWMOXOQgH6l97Y7lHAS7
-Ip/HqSLKmT0Cp2foBi36BGu7SdJsmVdjbC3CYXjhILH79r/hgjk5PHvvfRqVSrJy
-2lWQru3d4nCQfBrutTJaXc/W+kXyngEMMS+JhP4xYA/97qZbhNXHGOak+UAwKRge
-/vxBtbkpBXWLYhpbIi6/5FlssA==
+MIIDgjCCAmqgAwIBAgIJAMTV0ieHng2/MA0GCSqGSIb3DQEBCwUAMGkxCzAJBgNV
+BAYTAlVTMRYwFAYDVQQIDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQHDApDYWxpZm9y
+bmlhMRYwFAYDVQQKDA1HaXRMYWIgKFRlc3QpMRUwEwYDVQQDDAxUZXN0IFJvb3Qg
+Q0EwIBcNMjIxMTIwMjMzNzQyWhgPMzAyMjAzMjMyMzM3NDJaMHExCzAJBgNVBAYT
+AlVTMRYwFAYDVQQIDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQHDApDYWxpZm9ybmlh
+MRYwFAYDVQQKDA1HaXRMYWIgKFRlc3QpMR0wGwYDVQQDDBRUZXN0IEludGVybWVk
+aWF0ZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJfazJfCLjY9
+wGUfI5MxWtyX6H/DAAiYNSfRw28M4ZBAwJcgClvwEHeRFGVOw1TwDyopMV3JIDtk
++87AGvF1Jyu+5k+XkJtOmb6HtotjelUJS1gCgzbBoqlfHTfFCYNRD/rWV1zBFkq5
+wTMeRQvy7Q5MocBzr2VHwFvBr1YkT5P8nSsQ2iFA3r7Lsv5csgj1D7yFuI6LQcWN
+3IpCORuU7144ZNuK3ChhcmrV9S2770qAgqdmSUpIYQO4xCOFJB3Zs7kHJkKXn1cz
+jDXFsbQ//ouKG8zBDzeCawNsHMVei2oXBySZYSVXaHI1fRB9MYIAdheCn5QQSyqG
+zN7mP03iQq8CAwEAAaMjMCEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AYYwDQYJKoZIhvcNAQELBQADggEBAJaXhgFrG4ijA/TinY2ApARY5lsk0ol8DKq7
+4hVDDudRo1ET5gW3VhbYIkN9r+hZeRnzf3WghC75si7WROpv2DeNKKv2FzFg4OqF
+nw154EVvEWJ8tyCTZ/8tqIBErEJUPfzuO8bZjVpN+2eSVDbo3UBLVFbsE37is7li
+eqjic+kmS6onvlG7xN9xCAwo7zGsyfceTLC9n3D8kfJd1YYV8I/l0jU1Gxf6S7g4
+2DnqNaOGTBwdioIAhaUB4amXw4NZ/NZhG3bVsNyWt99cvtLeMFTSl9lRRMT3xxCI
++TPaT30R9qRuI1Sd0IxdybSx5QG6AuZAe/5v+2+C4+8u6onpuXE=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
-A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
-Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
-MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
-A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
-RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
-gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
-KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
-QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
-XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
-DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
-LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
-RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
-jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
-6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
-mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
-Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
-WD9f
+MIIDejCCAmKgAwIBAgIJAMMTWTnLVjZwMA0GCSqGSIb3DQEBCwUAMGkxCzAJBgNV
+BAYTAlVTMRYwFAYDVQQIDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQHDApDYWxpZm9y
+bmlhMRYwFAYDVQQKDA1HaXRMYWIgKFRlc3QpMRUwEwYDVQQDDAxUZXN0IFJvb3Qg
+Q0EwIBcNMjIxMTIwMjMyMzA4WhgPMzAyMjAzMjMyMzIzMDhaMGkxCzAJBgNVBAYT
+AlVTMRYwFAYDVQQIDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQHDApDYWxpZm9ybmlh
+MRYwFAYDVQQKDA1HaXRMYWIgKFRlc3QpMRUwEwYDVQQDDAxUZXN0IFJvb3QgQ0Ew
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFe5BkdGSwAL1w7kEMgMVu
+quanndVPI7Mz5YthrmJP1Qgv5TY7440PrKKbtSqx4aS2KdNKq9th7sHZ6Qe6NKbq
+G6/HZPCVO+X7ORI8yGbWD1iYBLCXmMOkXY5UsDcN5z/oKRXzHfLD0uYSQ5Fp0D9J
+O9Y0I2iz1ywsGm784xt0n7Yzz3HgjvYwU2VdLJPSzwSBPphj64E2cfM+5rGoaJd7
+Er3FodqR0YegrsMmggpK/QPIylt+TCkTgBL/+paM4KynUYusB+LzqXm0u1mYuZmt
++9Y4sKVZiNKlQFfg9XfZRpldn8pnFBMnLaUU757+mPJOuy5mLVlwAntw9qgi6UfX
+AgMBAAGjIzAhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqG
+SIb3DQEBCwUAA4IBAQCT8zbJ1nFfpTjPETpbQ4V2BR41Y4BZXFPcnfWY/byC8sRr
+EvbwiDWIehY/dxIHia+8dYhoXER/gWC1gc+rrW8Bil+n5119VEX9Ij6oFoQJgNrw
+rcP+W6Bjl9VTS15+A39Bo8x8ElNXRt6b91fBd77wzDamaB7UXq7eDIPu6Ov56tui
+RiYi9xXIwplWNH/XPl2HcwBw9hS5qG608qK9t/dJIpTIdWJmqg0gCxR8qmubrg58
+uvy8AhbYamLHTUa9Kf4CMxfl6QjrIMFCQiB7g6aaMZxRTQo9eRTMjoywNh8wd63g
+VYFagDTZZUGliXyIHkN6AVKvI7IIF21r7c5qarbX
-----END CERTIFICATE-----
diff --git a/spec/fixtures/clusters/intermediate_certificate.pem b/spec/fixtures/clusters/intermediate_certificate.pem
index b287201af16..142e73b99c2 100644
--- a/spec/fixtures/clusters/intermediate_certificate.pem
+++ b/spec/fixtures/clusters/intermediate_certificate.pem
@@ -1,27 +1,21 @@
-----BEGIN CERTIFICATE-----
-MIIEjzCCA3egAwIBAgIQfCoMIT/GVVNFyR8ZH7hO+jANBgkqhkiG9w0BAQsFADBM
-MSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xv
-YmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0yMjA0MjAxMjAwMDBaFw0y
-NTA0MjAwMDAwMDBaMFgxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWdu
-IG52LXNhMS4wLAYDVQQDEyVHbG9iYWxTaWduIEF0bGFzIFIzIERWIFRMUyBDQSAy
-MDIyIFEzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuKh6ZjxOZpzO
-N6VUNU02x5nTqCc28i/G1Rg+6QndBdbXLDQyfAhjSdEQN+V4XRFizm37Lz83lNuP
-ezDpXizZVT+y27mgtWA3i6QGMjVQpAmvCkX/qB+bZY7dSuBAoeNjN1iQ3XU7/A4c
-gkCYvXCxwUgUFDwES2nd1JwBpukh44IK/uSqvzSgjMvJeW4+XGpSnsTtK8Vp/lA8
-k521/y0oqGwGbJ3Fr7JZ+1l3DXR6iISk1B3UuiAGzLUeSE50IRWGdcDMWtEFz1cW
-ehMX7MJKrtUecqoiWoycgjLEEOZCbiGGaHyAIzA1072wXgopK/AUsRg32Vklw+c4
-2enULTY1ZQIDAQABo4IBXzCCAVswDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG
-CCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQW
-BBT6kTljmvutECTlvrW52qvZxEZpqzAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpj
-move4t0bvDB7BggrBgEFBQcBAQRvMG0wLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3Nw
-Mi5nbG9iYWxzaWduLmNvbS9yb290cjMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9zZWN1
-cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L3Jvb3QtcjMuY3J0MDYGA1UdHwQvMC0w
-K6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC1yMy5jcmwwIQYD
-VR0gBBowGDAIBgZngQwBAgEwDAYKKwYBBAGgMgoBAzANBgkqhkiG9w0BAQsFAAOC
-AQEAFDMseeU/gsZwP9pZOKe7onasYRgFaFfZDfuKRrzxqOgMcAIdxi+X7TY+nlKG
-L1xi2NVHQ5pz0Sslh59EtBTrJrwhR3QgvZ+kv7OAHU01fc25tdpV8pBQyLIXTg60
-YYgpX0RdA39XkYHQ6zCu1SrsgiDOTtKwi5UCYXPYaTT0rWMOXOQgH6l97Y7lHAS7
-Ip/HqSLKmT0Cp2foBi36BGu7SdJsmVdjbC3CYXjhILH79r/hgjk5PHvvfRqVSrJy
-2lWQru3d4nCQfBrutTJaXc/W+kXyngEMMS+JhP4xYA/97qZbhNXHGOak+UAwKRge
-/vxBtbkpBXWLYhpbIi6/5FlssA==
+MIIDgjCCAmqgAwIBAgIJAMTV0ieHng2/MA0GCSqGSIb3DQEBCwUAMGkxCzAJBgNV
+BAYTAlVTMRYwFAYDVQQIDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQHDApDYWxpZm9y
+bmlhMRYwFAYDVQQKDA1HaXRMYWIgKFRlc3QpMRUwEwYDVQQDDAxUZXN0IFJvb3Qg
+Q0EwIBcNMjIxMTIwMjMzNzQyWhgPMzAyMjAzMjMyMzM3NDJaMHExCzAJBgNVBAYT
+AlVTMRYwFAYDVQQIDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQHDApDYWxpZm9ybmlh
+MRYwFAYDVQQKDA1HaXRMYWIgKFRlc3QpMR0wGwYDVQQDDBRUZXN0IEludGVybWVk
+aWF0ZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJfazJfCLjY9
+wGUfI5MxWtyX6H/DAAiYNSfRw28M4ZBAwJcgClvwEHeRFGVOw1TwDyopMV3JIDtk
++87AGvF1Jyu+5k+XkJtOmb6HtotjelUJS1gCgzbBoqlfHTfFCYNRD/rWV1zBFkq5
+wTMeRQvy7Q5MocBzr2VHwFvBr1YkT5P8nSsQ2iFA3r7Lsv5csgj1D7yFuI6LQcWN
+3IpCORuU7144ZNuK3ChhcmrV9S2770qAgqdmSUpIYQO4xCOFJB3Zs7kHJkKXn1cz
+jDXFsbQ//ouKG8zBDzeCawNsHMVei2oXBySZYSVXaHI1fRB9MYIAdheCn5QQSyqG
+zN7mP03iQq8CAwEAAaMjMCEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AYYwDQYJKoZIhvcNAQELBQADggEBAJaXhgFrG4ijA/TinY2ApARY5lsk0ol8DKq7
+4hVDDudRo1ET5gW3VhbYIkN9r+hZeRnzf3WghC75si7WROpv2DeNKKv2FzFg4OqF
+nw154EVvEWJ8tyCTZ/8tqIBErEJUPfzuO8bZjVpN+2eSVDbo3UBLVFbsE37is7li
+eqjic+kmS6onvlG7xN9xCAwo7zGsyfceTLC9n3D8kfJd1YYV8I/l0jU1Gxf6S7g4
+2DnqNaOGTBwdioIAhaUB4amXw4NZ/NZhG3bVsNyWt99cvtLeMFTSl9lRRMT3xxCI
++TPaT30R9qRuI1Sd0IxdybSx5QG6AuZAe/5v+2+C4+8u6onpuXE=
-----END CERTIFICATE-----
diff --git a/spec/fixtures/clusters/leaf_certificate.pem b/spec/fixtures/clusters/leaf_certificate.pem
index b7b2f78a977..ff390a84967 100644
--- a/spec/fixtures/clusters/leaf_certificate.pem
+++ b/spec/fixtures/clusters/leaf_certificate.pem
@@ -1,37 +1,21 @@
-----BEGIN CERTIFICATE-----
-MIIGYjCCBUqgAwIBAgIQATfkha/xTr3pbLHVWlPq4jANBgkqhkiG9w0BAQsFADBY
-MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEuMCwGA1UE
-AxMlR2xvYmFsU2lnbiBBdGxhcyBSMyBEViBUTFMgQ0EgMjAyMiBRMzAeFw0yMjA3
-MjIxOTQyMTFaFw0yMzA4MjMxOTQyMTBaMBsxGTAXBgNVBAMMEGFib3V0LmdpdGxh
-Yi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFFFQs8EITaWo5
-0U18/mPTDLencU/7siJT/4P8oeDkemyx98wzK6vuNj/2JEZ3v1psKun5n8Pb/fHa
-somKd/4icHgC4rnxrO6zayfb+cKzVghQe12Nj75lx6RtppqTgAmSOa3Tai5niICT
-I8s3d2wsHtfEgqAavcD0/zdPIk25Ji7yfquldSthnlhQqI4Pm3OxTiyFj/V5ZhFl
-IWZLvQaENjBSDVZQDcaPdWwodfXNA8fJmqk7cTLQ9P9NgjWvva7acl+Yd6hOFzV0
-EllBl/WF1KB+YzGuHI0CQHT7sv3GW1lXeE2EqrWoSdLTOSAqm6y02DyE79d1xvG6
-XXfX5ILlAgMBAAGjggNjMIIDXzAbBgNVHREEFDASghBhYm91dC5naXRsYWIuY29t
-MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
-HQYDVR0OBBYEFHK7MnjGDptQWjfmJ2fr3IrjxEopMFcGA1UdIARQME4wCAYGZ4EM
-AQIBMEIGCisGAQQBoDIKAQMwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xv
-YmFsc2lnbi5jb20vcmVwb3NpdG9yeS8wDAYDVR0TAQH/BAIwADCBngYIKwYBBQUH
-AQEEgZEwgY4wQAYIKwYBBQUHMAGGNGh0dHA6Ly9vY3NwLmdsb2JhbHNpZ24uY29t
-L2NhL2dzYXRsYXNyM2R2dGxzY2EyMDIycTMwSgYIKwYBBQUHMAKGPmh0dHA6Ly9z
-ZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2dzYXRsYXNyM2R2dGxzY2EyMDIy
-cTMuY3J0MB8GA1UdIwQYMBaAFPqROWOa+60QJOW+tbnaq9nERmmrMEgGA1UdHwRB
-MD8wPaA7oDmGN2h0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vY2EvZ3NhdGxhc3Iz
-ZHZ0bHNjYTIwMjJxMy5jcmwwggF9BgorBgEEAdZ5AgQCBIIBbQSCAWkBZwB1AG9T
-dqwx8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kTAAABgiduiHEAAAQDAEYwRAIg
-SYQrru/KAKfe+hUqpJmk7Fc8drkgtY3IcAurTOwbM68CIBYO9sbDspd5p7v17RQi
-QQkjdRwSjHiIgvlX0Y1JqmXjAHYArfe++nz/EMiLnT2cHj4YarRnKV3PsQwkyoWG
-NOvcgooAAAGCJ26IcQAABAMARzBFAiBc5a10annqMEH69bdEFy/Vo1gb3S3GQ993
-BCRV7ZXG4gIhAMqnsoKkU6ITwRXwE9KGjHnijJ8QrBrnK0i+JFaGe1ffAHYAs3N3
-B+GEUPhjhtYFqdwRCUp5LbFnDAuH3PADDnk2pZoAAAGCJ26I6QAABAMARzBFAiAf
-lW8Agd0DB68YA8XAbnlq7QNHw3uRMzNdS8gtRUe75gIhANTe+mt2p1ryW83P31OW
-jH3cEGJxdUNT/oDM3Fzesx94MA0GCSqGSIb3DQEBCwUAA4IBAQAfivKEmjqqOFFh
-VsX2XYkoDtreghpqMwHMCLwNk852Alr/Seyv9Ilng8cunU4NmhvEtsYVXkfE4XvB
-0QIVxkg1w7A+p7ejMjh6doLJ0aWNWIVW/DwOeP0qstF9lqvLdLDABoVn0BtYCDTH
-gjG80e2xpvPiKHGvBL+hlOIJwUuIAT3jN23sS1GoiYQGKsz0lovB09/6MGG0Qj8C
-3i9a59T9XBpwSKdpKd4u/CB6koBXD3atbBNBACuAMcFckTEtmkCFtSpqBuocJGKf
-LB4MFVaEwrd7Lc1ACC1et5FDtEI4I3/CerkRZTV+mRz5n6tB91AK3dRvjElfhiuh
-XXYRULvB
+MIIDgDCCAmigAwIBAgIJALI40QyGMz+AMA0GCSqGSIb3DQEBCwUAMHExCzAJBgNV
+BAYTAlVTMRYwFAYDVQQIDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQHDApDYWxpZm9y
+bmlhMRYwFAYDVQQKDA1HaXRMYWIgKFRlc3QpMR0wGwYDVQQDDBRUZXN0IEludGVy
+bWVkaWF0ZSBDQTAgFw0yMjExMjEwMDAzMTZaGA8zMDIyMDMyNDAwMDMxNlowazEL
+MAkGA1UEBhMCVVMxFjAUBgNVBAgMDVNhbiBGcmFuY2lzY28xEzARBgNVBAcMCkNh
+bGlmb3JuaWExFjAUBgNVBAoMDUdpdExhYiAoVGVzdCkxFzAVBgNVBAMMDlRlc3Qg
+TGVhZiBDZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxPHRlMks
+Auba0UxFoL248jfCQNWpEbRoImj/B9W5Zm2sdmJpaD01NVfR+O7eovbWozN6J+gs
+MKS5SUVDIu0E5iVBsEsQlDAF+ckPo3kvoWDAF2cLgNjpRApIiS9YCy9dZLxUl34x
+F4nXy19+53mouw/tX7kPLwnHxTVvBEpDfIttOCTlXnLu62v2LRP5lt5vFXE0B/00
+bD6t+lVl7eamvjDRAz2HoFMY+m1Popts7j4Pj3rWVWfo1RRu88NvjQLnqccFQFnd
+tDsv3YqtiWDdCbcnkIKcgZ3ckpDhTKwRjnKmub4un/OpEgn/WVR1iLCOCDS1RX1u
+j5HKhyiEzRJt1QIDAQABox8wHTAbBgNVHREEFDASghB0ZXN0LmV4YW1wbGUuY29t
+MA0GCSqGSIb3DQEBCwUAA4IBAQA31V60ppRL+/YD1qlGu2uDXKn2VaK41LQxoz7R
+LCQp6qf50XhQFMBu2Mdf5cAbMdgIcAE6BQ7r7kaxO4dSXn9J3cy5Qb4msRvGy63P
+rm0G71TmAK3ihYLP19eT5g9oOHarRc3gXVtUxCAd/XVrxp1GW2vxKTGo46rKKVeU
+myU3Yn9hlu6VszvyaseaDo4Tvd6+vfdpGRo6lcJIdjcQ3Ie2bH6kF1RWXT2sgBW/
+XsDrF7O15DUE9na/7ak8tCOnxFaag7f7SLEHC+NSiMkrRry7Yc3WEFMeJTzHCXGn
+5Uwl4FKujmFkTwOPOeKyWIkDJg+PQVXnVrTvZlx+6k+Kpv2j
-----END CERTIFICATE-----
diff --git a/spec/fixtures/clusters/root_certificate.pem b/spec/fixtures/clusters/root_certificate.pem
index 8afb219058f..6d97302b2b3 100644
--- a/spec/fixtures/clusters/root_certificate.pem
+++ b/spec/fixtures/clusters/root_certificate.pem
@@ -1,21 +1,21 @@
-----BEGIN CERTIFICATE-----
-MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
-A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
-Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
-MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
-A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
-RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
-gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
-KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
-QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
-XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
-DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
-LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
-RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
-jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
-6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
-mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
-Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
-WD9f
+MIIDejCCAmKgAwIBAgIJAMMTWTnLVjZwMA0GCSqGSIb3DQEBCwUAMGkxCzAJBgNV
+BAYTAlVTMRYwFAYDVQQIDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQHDApDYWxpZm9y
+bmlhMRYwFAYDVQQKDA1HaXRMYWIgKFRlc3QpMRUwEwYDVQQDDAxUZXN0IFJvb3Qg
+Q0EwIBcNMjIxMTIwMjMyMzA4WhgPMzAyMjAzMjMyMzIzMDhaMGkxCzAJBgNVBAYT
+AlVTMRYwFAYDVQQIDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQHDApDYWxpZm9ybmlh
+MRYwFAYDVQQKDA1HaXRMYWIgKFRlc3QpMRUwEwYDVQQDDAxUZXN0IFJvb3QgQ0Ew
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFe5BkdGSwAL1w7kEMgMVu
+quanndVPI7Mz5YthrmJP1Qgv5TY7440PrKKbtSqx4aS2KdNKq9th7sHZ6Qe6NKbq
+G6/HZPCVO+X7ORI8yGbWD1iYBLCXmMOkXY5UsDcN5z/oKRXzHfLD0uYSQ5Fp0D9J
+O9Y0I2iz1ywsGm784xt0n7Yzz3HgjvYwU2VdLJPSzwSBPphj64E2cfM+5rGoaJd7
+Er3FodqR0YegrsMmggpK/QPIylt+TCkTgBL/+paM4KynUYusB+LzqXm0u1mYuZmt
++9Y4sKVZiNKlQFfg9XfZRpldn8pnFBMnLaUU757+mPJOuy5mLVlwAntw9qgi6UfX
+AgMBAAGjIzAhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqG
+SIb3DQEBCwUAA4IBAQCT8zbJ1nFfpTjPETpbQ4V2BR41Y4BZXFPcnfWY/byC8sRr
+EvbwiDWIehY/dxIHia+8dYhoXER/gWC1gc+rrW8Bil+n5119VEX9Ij6oFoQJgNrw
+rcP+W6Bjl9VTS15+A39Bo8x8ElNXRt6b91fBd77wzDamaB7UXq7eDIPu6Ov56tui
+RiYi9xXIwplWNH/XPl2HcwBw9hS5qG608qK9t/dJIpTIdWJmqg0gCxR8qmubrg58
+uvy8AhbYamLHTUa9Kf4CMxfl6QjrIMFCQiB7g6aaMZxRTQo9eRTMjoywNh8wd63g
+VYFagDTZZUGliXyIHkN6AVKvI7IIF21r7c5qarbX
-----END CERTIFICATE-----
diff --git a/spec/fixtures/config/redis_cluster_format_host.yml b/spec/fixtures/config/redis_cluster_format_host.yml
new file mode 100644
index 00000000000..7303db72c4e
--- /dev/null
+++ b/spec/fixtures/config/redis_cluster_format_host.yml
@@ -0,0 +1,29 @@
+# redis://[:password@]host[:port][/db-number][?option=value]
+# more details: http://www.iana.org/assignments/uri-schemes/prov/redis
+development:
+ password: myclusterpassword
+ cluster:
+ -
+ host: development-master1
+ port: 6379
+ -
+ host: development-master2
+ port: 6379
+test:
+ password: myclusterpassword
+ cluster:
+ -
+ host: test-master1
+ port: 6379
+ -
+ host: test-master2
+ port: 6379
+production:
+ password: myclusterpassword
+ cluster:
+ -
+ host: production-master1
+ port: 6379
+ -
+ host: production-master2
+ port: 6379
diff --git a/spec/fixtures/gitlab/database/structure_example.sql b/spec/fixtures/gitlab/database/structure_example.sql
index 1ad78adea53..feebf475d2e 100644
--- a/spec/fixtures/gitlab/database/structure_example.sql
+++ b/spec/fixtures/gitlab/database/structure_example.sql
@@ -77,3 +77,17 @@ ALTER TABLE ONLY public.abuse_reports
CREATE INDEX index_abuse_reports_on_user_id ON public.abuse_reports USING btree (user_id);
+CREATE TRIGGER gitlab_schema_write_trigger_for_users BEFORE INSERT OR DELETE OR UPDATE OR TRUNCATE ON users FOR EACH STATEMENT EXECUTE FUNCTION gitlab_schema_prevent_write();
+
+CREATE FUNCTION gitlab_schema_prevent_write() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ IF COALESCE(NULLIF(current_setting(CONCAT('lock_writes.', TG_TABLE_NAME), true), ''), 'true') THEN
+ RAISE EXCEPTION 'Table: "%" is write protected within this Gitlab database.', TG_TABLE_NAME
+ USING ERRCODE = 'modifying_sql_data_not_permitted',
+ HINT = 'Make sure you are using the right database connection';
+END IF;
+RETURN NEW;
+END
+$$;
diff --git a/spec/fixtures/gitlab/database/structure_example_cleaned.sql b/spec/fixtures/gitlab/database/structure_example_cleaned.sql
index ab6af34dda7..642a310e024 100644
--- a/spec/fixtures/gitlab/database/structure_example_cleaned.sql
+++ b/spec/fixtures/gitlab/database/structure_example_cleaned.sql
@@ -24,3 +24,16 @@ ALTER TABLE ONLY abuse_reports
ADD CONSTRAINT abuse_reports_pkey PRIMARY KEY (id);
CREATE INDEX index_abuse_reports_on_user_id ON abuse_reports USING btree (user_id);
+
+CREATE FUNCTION gitlab_schema_prevent_write() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ IF COALESCE(NULLIF(current_setting(CONCAT('lock_writes.', TG_TABLE_NAME), true), ''), 'true') THEN
+ RAISE EXCEPTION 'Table: "%" is write protected within this Gitlab database.', TG_TABLE_NAME
+ USING ERRCODE = 'modifying_sql_data_not_permitted',
+ HINT = 'Make sure you are using the right database connection';
+END IF;
+RETURN NEW;
+END
+$$;
diff --git a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/services.ndjson b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/services.ndjson
index e5d39512255..2d092d4e916 100644
--- a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/services.ndjson
+++ b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/services.ndjson
@@ -7,7 +7,6 @@
{"id":95,"project_id":5,"created_at":"2016-06-14T15:01:51.255Z","updated_at":"2016-06-14T15:01:51.255Z","active":false,"properties":{"api_url":"","jira_issue_transition_id":"2"},"template":false,"push_events":true,"issues_events":true,"merge_requests_events":true,"tag_push_events":true,"note_events":true,"job_events":true,"type":"JiraService","category":"issue_tracker","default":false,"wiki_page_events":true}
{"id":94,"project_id":5,"created_at":"2016-06-14T15:01:51.232Z","updated_at":"2016-06-14T15:01:51.232Z","active":true,"properties":{},"template":false,"push_events":true,"issues_events":true,"merge_requests_events":true,"tag_push_events":true,"note_events":true,"job_events":true,"type":"IrkerService","category":"common","default":false,"wiki_page_events":true}
{"id":93,"project_id":5,"created_at":"2016-06-14T15:01:51.219Z","updated_at":"2016-06-14T15:01:51.219Z","active":false,"properties":{"notify_only_broken_pipelines":true},"template":false,"push_events":true,"issues_events":true,"merge_requests_events":true,"tag_push_events":true,"note_events":true,"pipeline_events":true,"type":"HipchatService","category":"common","default":false,"wiki_page_events":true}
-{"id":91,"project_id":5,"created_at":"2016-06-14T15:01:51.182Z","updated_at":"2016-06-14T15:01:51.182Z","active":false,"properties":{},"template":false,"push_events":true,"issues_events":true,"merge_requests_events":true,"tag_push_events":true,"note_events":true,"job_events":true,"type":"FlowdockService","category":"common","default":false,"wiki_page_events":true}
{"id":90,"project_id":5,"created_at":"2016-06-14T15:01:51.166Z","updated_at":"2016-06-14T15:01:51.166Z","active":false,"properties":{},"template":false,"push_events":true,"issues_events":true,"merge_requests_events":true,"tag_push_events":true,"note_events":true,"job_events":true,"type":"ExternalWikiService","category":"common","default":false,"wiki_page_events":true}
{"id":89,"project_id":5,"created_at":"2016-06-14T15:01:51.153Z","updated_at":"2016-06-14T15:01:51.153Z","active":false,"properties":{},"template":false,"push_events":true,"issues_events":true,"merge_requests_events":true,"tag_push_events":true,"note_events":true,"job_events":true,"type":"EmailsOnPushService","category":"common","default":false,"wiki_page_events":true}
{"id":88,"project_id":5,"created_at":"2016-06-14T15:01:51.139Z","updated_at":"2016-06-14T15:01:51.139Z","active":false,"properties":{},"template":false,"push_events":true,"issues_events":true,"merge_requests_events":true,"tag_push_events":true,"note_events":true,"job_events":true,"type":"DroneCiService","category":"ci","default":false,"wiki_page_events":true}
diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metric_label_values_variable_full_syntax.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metric_label_values_variable_full_syntax.json
index a74b557dabe..145cc476d64 100644
--- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metric_label_values_variable_full_syntax.json
+++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metric_label_values_variable_full_syntax.json
@@ -2,12 +2,21 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
- "type", "options"
+ "type",
+ "options"
],
"properties": {
- "type": { "enum": "metric_label_values" },
- "label": { "type": "string" },
- "options": { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metric_label_values_variable_options.json" }
+ "type": {
+ "enum": [
+ "metric_label_values"
+ ]
+ },
+ "label": {
+ "type": "string"
+ },
+ "options": {
+ "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metric_label_values_variable_options.json"
+ }
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
index 14885813d93..38b2a8381bb 100644
--- a/spec/fixtures/markdown.md.erb
+++ b/spec/fixtures/markdown.md.erb
@@ -399,3 +399,7 @@ Bob -> Sara : Hello
[beard]-:>[foul mouth]
]
```
+
+### Image Attributes
+
+![Sized Image](app/assets/images/touch-icon-ipad.png){width=75% height=100}
diff --git a/spec/fixtures/markdown/markdown_golden_master_examples.yml b/spec/fixtures/markdown/markdown_golden_master_examples.yml
index 0c7e6ab5cd2..9e7de030a29 100644
--- a/spec/fixtures/markdown/markdown_golden_master_examples.yml
+++ b/spec/fixtures/markdown/markdown_golden_master_examples.yml
@@ -347,6 +347,16 @@
<li data-sourcepos="9:1-9:25"><code>HSLA(540,70%,50%,0.3)<span class="gfm-color_chip"><span style="background-color: HSLA(540,70%,50%,0.3);"></span></span></code></li>
</ul>
+- name: comment
+ markdown: |-
+ <!-- this is a
+ multiline markdown
+ comment -->
+ html: |-
+ <!-- this is a
+ multiline markdown
+ comment -->
+
- name: description_list
markdown: |-
<dl>
diff --git a/spec/fixtures/pager_duty/webhook_incident_trigger.json b/spec/fixtures/pager_duty/webhook_incident_trigger.json
index 872297adcf6..9994c8c843b 100644
--- a/spec/fixtures/pager_duty/webhook_incident_trigger.json
+++ b/spec/fixtures/pager_duty/webhook_incident_trigger.json
@@ -1,239 +1,61 @@
{
- "messages": [
- {
- "event": "incident.trigger",
- "log_entries": [
+ "event": {
+ "id": "01DDZJG3TC199M1GJQ7LO67JYS",
+ "event_type": "incident.triggered",
+ "resource_type": "incident",
+ "occurred_at": "2022-11-30T08:46:19.079Z",
+ "agent": {
+ "html_url": "https://gitlab-1.pagerduty.com/users/PIN0B5C",
+ "id": "PIN0B5C",
+ "self": "https://api.pagerduty.com/users/PIN0B5C",
+ "summary": "Rajendra Kadam",
+ "type": "user_reference"
+ },
+ "client": "nil",
+ "data": {
+ "id": "Q1XZUF87W1HB5A",
+ "type": "incident",
+ "self": "https://api.pagerduty.com/incidents/Q1XZUF87W1HB5A",
+ "html_url": "https://gitlab-1.pagerduty.com/incidents/Q1XZUF87W1HB5A",
+ "number": 2,
+ "status": "triggered",
+ "incident_key": "[FILTERED]",
+ "created_at": "2022-11-30T08:46:19Z",
+ "title": "[FILTERED]",
+ "service": {
+ "html_url": "https://gitlab-1.pagerduty.com/services/PK6IKMT",
+ "id": "PK6IKMT",
+ "self": "https://api.pagerduty.com/services/PK6IKMT",
+ "summary": "Test service",
+ "type": "service_reference"
+ },
+ "assignees": [
{
- "id": "R2XGXEI3W0FHMSDXHDIBQGBQ5E",
- "type": "trigger_log_entry",
- "summary": "Triggered through the website",
- "self": "https://api.pagerduty.com/log_entries/R2XGXEI3W0FHMSDXHDIBQGBQ5E",
- "html_url": "https://webdemo.pagerduty.com/incidents/PRORDTY/log_entries/R2XGXEI3W0FHMSDXHDIBQGBQ5E",
- "created_at": "2017-09-26T15:14:36Z",
- "agent": {
- "id": "P553OPV",
- "type": "user_reference",
- "summary": "Laura Haley",
- "self": "https://api.pagerduty.com/users/P553OPV",
- "html_url": "https://webdemo.pagerduty.com/users/P553OPV"
- },
- "channel": {
- "type": "web_trigger",
- "summary": "My new incident",
- "subject": "My new incident",
- "details": "Oh my gosh",
- "details_omitted": false
- },
- "service": {
- "id": "PN49J75",
- "type": "service_reference",
- "summary": "Production XDB Cluster",
- "self": "https://api.pagerduty.com/services/PN49J75",
- "html_url": "https://webdemo.pagerduty.com/services/PN49J75"
- },
- "incident": {
- "id": "PRORDTY",
- "type": "incident_reference",
- "summary": "[#33] My new incident",
- "self": "https://api.pagerduty.com/incidents/PRORDTY",
- "html_url": "https://webdemo.pagerduty.com/incidents/PRORDTY"
- },
- "teams": [
- {
- "id": "P4SI59S",
- "type": "team_reference",
- "summary": "Engineering",
- "self": "https://api.pagerduty.com/teams/P4SI59S",
- "html_url": "https://webdemo.pagerduty.com/teams/P4SI59S"
- }
- ],
- "contexts": [],
- "event_details": {
- "description": "My new incident"
- }
+ "html_url": "https://gitlab-1.pagerduty.com/users/PIN0B5C",
+ "id": "PIN0B5C",
+ "self": "https://api.pagerduty.com/users/PIN0B5C",
+ "summary": "Rajendra Kadam",
+ "type": "user_reference"
}
],
- "webhook": {
- "endpoint_url": "https://requestb.in/18ao6fs1",
- "name": "V2 wabhook",
- "description": null,
- "webhook_object": {
- "id": "PN49J75",
- "type": "service_reference",
- "summary": "Production XDB Cluster",
- "self": "https://api.pagerduty.com/services/PN49J75",
- "html_url": "https://webdemo.pagerduty.com/services/PN49J75"
- },
- "config": {},
- "outbound_integration": {
- "id": "PJFWPEP",
- "type": "outbound_integration_reference",
- "summary": "Generic V2 Webhook",
- "self": "https://api.pagerduty.com/outbound_integrations/PJFWPEP",
- "html_url": null
- },
- "accounts_addon": null,
- "id": "PKT9NNX",
- "type": "webhook",
- "summary": "V2 wabhook",
- "self": "https://api.pagerduty.com/webhooks/PKT9NNX",
- "html_url": null
+ "escalation_policy": {
+ "html_url": "https://gitlab-1.pagerduty.com/escalation_policies/PWP6XTY",
+ "id": "PWP6XTY",
+ "self": "https://api.pagerduty.com/escalation_policies/PWP6XTY",
+ "summary": "Default",
+ "type": "escalation_policy_reference"
},
- "incident": {
- "incident_number": 33,
- "title": "My new incident",
- "description": "My new incident",
- "created_at": "2017-09-26T15:14:36Z",
- "status": "triggered",
- "pending_actions": [
- {
- "type": "escalate",
- "at": "2017-09-26T15:44:36Z"
- },
- {
- "type": "resolve",
- "at": "2017-09-26T19:14:36Z"
- }
- ],
- "incident_key": null,
- "service": {
- "id": "PN49J75",
- "name": "Production XDB Cluster",
- "description": "This service was created during onboarding on July 5, 2017.",
- "auto_resolve_timeout": 14400,
- "acknowledgement_timeout": 1800,
- "created_at": "2017-07-05T17:33:09Z",
- "status": "critical",
- "last_incident_timestamp": "2017-09-26T15:14:36Z",
- "teams": [
- {
- "id": "P4SI59S",
- "type": "team_reference",
- "summary": "Engineering",
- "self": "https://api.pagerduty.com/teams/P4SI59S",
- "html_url": "https://webdemo.pagerduty.com/teams/P4SI59S"
- }
- ],
- "incident_urgency_rule": {
- "type": "constant",
- "urgency": "high"
- },
- "scheduled_actions": [],
- "support_hours": null,
- "escalation_policy": {
- "id": "PINYWEF",
- "type": "escalation_policy_reference",
- "summary": "Default",
- "self": "https://api.pagerduty.com/escalation_policies/PINYWEF",
- "html_url": "https://webdemo.pagerduty.com/escalation_policies/PINYWEF"
- },
- "addons": [],
- "privilege": null,
- "alert_creation": "create_alerts_and_incidents",
- "integrations": [
- {
- "id": "PUAYF96",
- "type": "generic_events_api_inbound_integration_reference",
- "summary": "API",
- "self": "https://api.pagerduty.com/services/PN49J75/integrations/PUAYF96",
- "html_url": "https://webdemo.pagerduty.com/services/PN49J75/integrations/PUAYF96"
- },
- {
- "id": "P90GZUH",
- "type": "generic_email_inbound_integration_reference",
- "summary": "Email",
- "self": "https://api.pagerduty.com/services/PN49J75/integrations/P90GZUH",
- "html_url": "https://webdemo.pagerduty.com/services/PN49J75/integrations/P90GZUH"
- }
- ],
- "metadata": {},
- "type": "service",
- "summary": "Production XDB Cluster",
- "self": "https://api.pagerduty.com/services/PN49J75",
- "html_url": "https://webdemo.pagerduty.com/services/PN49J75"
- },
- "assignments": [
- {
- "at": "2017-09-26T15:14:36Z",
- "assignee": {
- "id": "P553OPV",
- "type": "user_reference",
- "summary": "Laura Haley",
- "self": "https://api.pagerduty.com/users/P553OPV",
- "html_url": "https://webdemo.pagerduty.com/users/P553OPV"
- }
- }
- ],
- "acknowledgements": [],
- "last_status_change_at": "2017-09-26T15:14:36Z",
- "last_status_change_by": {
- "id": "PN49J75",
- "type": "service_reference",
- "summary": "Production XDB Cluster",
- "self": "https://api.pagerduty.com/services/PN49J75",
- "html_url": "https://webdemo.pagerduty.com/services/PN49J75"
- },
- "first_trigger_log_entry": {
- "id": "R2XGXEI3W0FHMSDXHDIBQGBQ5E",
- "type": "trigger_log_entry_reference",
- "summary": "Triggered through the website",
- "self": "https://api.pagerduty.com/log_entries/R2XGXEI3W0FHMSDXHDIBQGBQ5E",
- "html_url": "https://webdemo.pagerduty.com/incidents/PRORDTY/log_entries/R2XGXEI3W0FHMSDXHDIBQGBQ5E"
- },
- "escalation_policy": {
- "id": "PINYWEF",
- "type": "escalation_policy_reference",
- "summary": "Default",
- "self": "https://api.pagerduty.com/escalation_policies/PINYWEF",
- "html_url": "https://webdemo.pagerduty.com/escalation_policies/PINYWEF"
- },
- "privilege": null,
- "teams": [
- {
- "id": "P4SI59S",
- "type": "team_reference",
- "summary": "Engineering",
- "self": "https://api.pagerduty.com/teams/P4SI59S",
- "html_url": "https://webdemo.pagerduty.com/teams/P4SI59S"
- }
- ],
- "alert_counts": {
- "all": 0,
- "triggered": 0,
- "resolved": 0
- },
- "impacted_services": [
- {
- "id": "PN49J75",
- "type": "service_reference",
- "summary": "Production XDB Cluster",
- "self": "https://api.pagerduty.com/services/PN49J75",
- "html_url": "https://webdemo.pagerduty.com/services/PN49J75"
- }
- ],
- "is_mergeable": true,
- "basic_alert_grouping": null,
- "alert_grouping": null,
- "metadata": {},
- "external_references": [],
- "importance": null,
- "incidents_responders": [],
- "responder_requests": [],
- "subscriber_requests": [],
- "urgency": "high",
- "id": "PRORDTY",
- "type": "incident",
- "summary": "[#33] My new incident",
- "self": "https://api.pagerduty.com/incidents/PRORDTY",
- "html_url": "https://webdemo.pagerduty.com/incidents/PRORDTY",
- "alerts": [
- {
- "alert_key": "c24117fc42e44b44b4d6876190583378"
- }
- ]
+ "teams": [],
+ "priority": {
+ "html_url": "https://gitlab-1.pagerduty.com/account/incident_priorities",
+ "id": "PKWBGFQ",
+ "self": "https://api.pagerduty.com/priorities/PKWBGFQ",
+ "summary": "P2",
+ "type": "priority_reference"
},
- "id": "69a7ced0-a2cd-11e7-a799-22000a15839c",
- "created_on": "2017-09-26T15:14:36Z"
+ "urgency": "high",
+ "conference_bridge": "nil",
+ "resolve_reason": "nil"
}
- ]
+ }
}
diff --git a/spec/frontend/__helpers__/dom_events_helper.js b/spec/frontend/__helpers__/dom_events_helper.js
deleted file mode 100644
index 865ea97903f..00000000000
--- a/spec/frontend/__helpers__/dom_events_helper.js
+++ /dev/null
@@ -1,8 +0,0 @@
-export const triggerDOMEvent = (type) => {
- window.document.dispatchEvent(
- new Event(type, {
- bubbles: true,
- cancelable: true,
- }),
- );
-};
diff --git a/spec/frontend/__helpers__/filtered_search_spec_helper.js b/spec/frontend/__helpers__/filtered_search_spec_helper.js
index ecf10694a16..f76fdfca229 100644
--- a/spec/frontend/__helpers__/filtered_search_spec_helper.js
+++ b/spec/frontend/__helpers__/filtered_search_spec_helper.js
@@ -1,3 +1,5 @@
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
+
export default class FilteredSearchSpecHelper {
static createFilterVisualTokenHTML(name, operator, value, isSelected) {
return FilteredSearchSpecHelper.createFilterVisualToken(name, operator, value, isSelected)
@@ -43,7 +45,7 @@ export default class FilteredSearchSpecHelper {
static createSearchVisualToken(name) {
const li = document.createElement('li');
- li.classList.add('js-visual-token', 'filtered-search-term');
+ li.classList.add('js-visual-token', FILTERED_SEARCH_TERM);
li.innerHTML = `<div class="name">${name}</div>`;
return li;
}
diff --git a/spec/frontend/__helpers__/graphql_helpers.js b/spec/frontend/__helpers__/graphql_helpers.js
deleted file mode 100644
index 63123aa046f..00000000000
--- a/spec/frontend/__helpers__/graphql_helpers.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * Returns a clone of the given object with all __typename keys omitted,
- * including deeply nested ones.
- *
- * Only works with JSON-serializable objects.
- *
- * @param {object} An object with __typename keys (e.g., a GraphQL response)
- * @returns {object} A new object with no __typename keys
- */
-export const stripTypenames = (object) => {
- return JSON.parse(
- JSON.stringify(object, (key, value) => (key === '__typename' ? undefined : value)),
- );
-};
diff --git a/spec/frontend/__helpers__/graphql_helpers_spec.js b/spec/frontend/__helpers__/graphql_helpers_spec.js
deleted file mode 100644
index dd23fbbf4e9..00000000000
--- a/spec/frontend/__helpers__/graphql_helpers_spec.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import { stripTypenames } from './graphql_helpers';
-
-describe('stripTypenames', () => {
- it.each`
- input | expected
- ${{}} | ${{}}
- ${{ __typename: 'Foo' }} | ${{}}
- ${{ bar: 'bar', __typename: 'Foo' }} | ${{ bar: 'bar' }}
- ${{ bar: { __typename: 'Bar' }, __typename: 'Foo' }} | ${{ bar: {} }}
- ${{ bar: [{ __typename: 'Bar' }], __typename: 'Foo' }} | ${{ bar: [{}] }}
- ${[]} | ${[]}
- ${[{ __typename: 'Foo' }]} | ${[{}]}
- ${[{ bar: [{ a: 1, __typename: 'Bar' }] }]} | ${[{ bar: [{ a: 1 }] }]}
- `('given $input returns $expected, with all __typename keys removed', ({ input, expected }) => {
- const actual = stripTypenames(input);
- expect(actual).toEqual(expected);
- expect(input).not.toBe(actual);
- });
-
- it('given null returns null', () => {
- expect(stripTypenames(null)).toEqual(null);
- });
-});
diff --git a/spec/frontend/__helpers__/graphql_transformer.js b/spec/frontend/__helpers__/graphql_transformer.js
index e776e2ea6ac..f26b63dadfd 100644
--- a/spec/frontend/__helpers__/graphql_transformer.js
+++ b/spec/frontend/__helpers__/graphql_transformer.js
@@ -3,6 +3,8 @@ const loader = require('graphql-tag/loader');
module.exports = {
process(src) {
- return loader.call({ cacheable() {} }, src);
+ return {
+ code: loader.call({ cacheable() {} }, src),
+ };
},
};
diff --git a/spec/frontend/__helpers__/jest_helpers.js b/spec/frontend/__helpers__/jest_helpers.js
deleted file mode 100644
index 273d2c91966..00000000000
--- a/spec/frontend/__helpers__/jest_helpers.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
-@module
-
-This method provides convenience functions to help migrating from Karma/Jasmine to Jest.
-
-Try not to use these in new tests - this module is provided primarily for convenience of migrating tests.
- */
-
-/**
- * Creates a plain JS object pre-populated with Jest spy functions. Useful for making simple mocks classes.
- *
- * @see https://jasmine.github.io/2.0/introduction.html#section-Spies:_%3Ccode%3EcreateSpyObj%3C/code%3E
- * @param {string} baseName Human-readable name of the object. This is used for reporting purposes.
- * @param methods {string[]} List of method names that will be added to the spy object.
- */
-export function createSpyObj(baseName, methods) {
- const obj = {};
- methods.forEach((method) => {
- obj[method] = jest.fn().mockName(`${baseName}#${method}`);
- });
- return obj;
-}
diff --git a/spec/frontend/__helpers__/mock_window_location_helper.js b/spec/frontend/__helpers__/mock_window_location_helper.js
index 14082857053..de1e8c99b54 100644
--- a/spec/frontend/__helpers__/mock_window_location_helper.js
+++ b/spec/frontend/__helpers__/mock_window_location_helper.js
@@ -21,18 +21,31 @@ const useMockLocation = (fn) => {
afterEach(() => {
currentWindowLocation = origWindowLocation;
});
+
+ return () => {
+ beforeEach(() => {
+ currentWindowLocation = origWindowLocation;
+ });
+ };
};
/**
* Create an object with the location interface but `jest.fn()` implementations.
*/
export const createWindowLocationSpy = () => {
- return {
+ const { origin, href } = window.location;
+
+ const mockLocation = {
assign: jest.fn(),
reload: jest.fn(),
replace: jest.fn(),
toString: jest.fn(),
+ origin,
+ // TODO: Do we need to update `origin` if `href` is changed?
+ href,
};
+
+ return mockLocation;
};
/**
diff --git a/spec/frontend/__helpers__/raw_transformer.js b/spec/frontend/__helpers__/raw_transformer.js
index 09101b7a64f..3b0bed14e8d 100644
--- a/spec/frontend/__helpers__/raw_transformer.js
+++ b/spec/frontend/__helpers__/raw_transformer.js
@@ -1,6 +1,6 @@
/* eslint-disable import/no-commonjs */
module.exports = {
process: (content) => {
- return `module.exports = ${JSON.stringify(content)}`;
+ return { code: `module.exports = ${JSON.stringify(content)}` };
},
};
diff --git a/spec/frontend/__helpers__/set_timeout_promise_helper.js b/spec/frontend/__helpers__/set_timeout_promise_helper.js
deleted file mode 100644
index afd18d92d15..00000000000
--- a/spec/frontend/__helpers__/set_timeout_promise_helper.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export default (time = 0) =>
- new Promise((resolve) => {
- setTimeout(resolve, time);
- });
diff --git a/spec/frontend/__helpers__/web_worker_transformer.js b/spec/frontend/__helpers__/web_worker_transformer.js
index 767ab3f5675..86be856f7b7 100644
--- a/spec/frontend/__helpers__/web_worker_transformer.js
+++ b/spec/frontend/__helpers__/web_worker_transformer.js
@@ -1,18 +1,22 @@
/* eslint-disable import/no-commonjs */
-const babelJestTransformer = require('babel-jest');
+const { createTransformer } = require('babel-jest');
// This Jest will transform the code of a WebWorker module into a FakeWebWorker subclass.
// This is meant to mirror Webpack's [`worker-loader`][1].
// [1]: https://webpack.js.org/loaders/worker-loader/
module.exports = {
process: (contentArg, filename, ...args) => {
- const { code: content } = babelJestTransformer.default.process(contentArg, filename, ...args);
+ const { code: content } = createTransformer().process(contentArg, filename, ...args);
- return `const { FakeWebWorker } = require("helpers/web_worker_fake");
+ const jestTransformedWorkerCode = `const { FakeWebWorker } = require("helpers/web_worker_fake");
module.exports = class JestTransformedWorker extends FakeWebWorker {
constructor() {
super(${JSON.stringify(filename)}, ${JSON.stringify(content)});
}
};`;
+
+ return {
+ code: jestTransformedWorkerCode,
+ };
},
};
diff --git a/spec/frontend/__helpers__/yaml_transformer.js b/spec/frontend/__helpers__/yaml_transformer.js
index a23f9b1f715..e0b4d01f542 100644
--- a/spec/frontend/__helpers__/yaml_transformer.js
+++ b/spec/frontend/__helpers__/yaml_transformer.js
@@ -6,6 +6,6 @@ module.exports = {
process: (sourceContent) => {
const jsonContent = JsYaml.load(sourceContent);
const json = JSON.stringify(jsonContent);
- return `module.exports = ${json}`;
+ return { code: `module.exports = ${json}` };
},
};
diff --git a/spec/frontend/__mocks__/@gitlab/ui.js b/spec/frontend/__mocks__/@gitlab/ui.js
index 6f2888e5c42..4d893bcd0bd 100644
--- a/spec/frontend/__mocks__/@gitlab/ui.js
+++ b/spec/frontend/__mocks__/@gitlab/ui.js
@@ -49,6 +49,8 @@ jest.mock('@gitlab/ui/dist/components/base/popover/popover.js', () => ({
'boundary',
'container',
'showCloseButton',
+ 'show',
+ 'boundaryPadding',
].map((prop) => [prop, {}]),
),
},
diff --git a/spec/frontend/admin/background_migrations/components/database_listbox_spec.js b/spec/frontend/admin/background_migrations/components/database_listbox_spec.js
index 3778943872e..212f4c0842c 100644
--- a/spec/frontend/admin/background_migrations/components/database_listbox_spec.js
+++ b/spec/frontend/admin/background_migrations/components/database_listbox_spec.js
@@ -1,4 +1,4 @@
-import { GlListbox } from '@gitlab/ui';
+import { GlCollapsibleListbox } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import BackgroundMigrationsDatabaseListbox from '~/admin/background_migrations/components/database_listbox.vue';
import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
@@ -30,15 +30,15 @@ describe('BackgroundMigrationsDatabaseListbox', () => {
wrapper.destroy();
});
- const findGlListbox = () => wrapper.findComponent(GlListbox);
+ const findGlCollapsibleListbox = () => wrapper.findComponent(GlCollapsibleListbox);
describe('template always', () => {
beforeEach(() => {
createComponent();
});
- it('renders GlListbox', () => {
- expect(findGlListbox().exists()).toBe(true);
+ it('renders GlCollapsibleListbox', () => {
+ expect(findGlCollapsibleListbox().exists()).toBe(true);
});
});
@@ -48,7 +48,7 @@ describe('BackgroundMigrationsDatabaseListbox', () => {
});
it('selecting a listbox item fires visitUrl with the database param', () => {
- findGlListbox().vm.$emit('select', MOCK_DATABASES[1].value);
+ findGlCollapsibleListbox().vm.$emit('select', MOCK_DATABASES[1].value);
expect(setUrlParams).toHaveBeenCalledWith({ database: MOCK_DATABASES[1].value });
expect(visitUrl).toHaveBeenCalled();
diff --git a/spec/frontend/admin/broadcast_messages/components/datetime_picker_spec.js b/spec/frontend/admin/broadcast_messages/components/datetime_picker_spec.js
new file mode 100644
index 00000000000..291c3aed1cf
--- /dev/null
+++ b/spec/frontend/admin/broadcast_messages/components/datetime_picker_spec.js
@@ -0,0 +1,46 @@
+import { mount } from '@vue/test-utils';
+import { GlDatepicker } from '@gitlab/ui';
+import DatetimePicker from '~/admin/broadcast_messages/components/datetime_picker.vue';
+
+describe('DatetimePicker', () => {
+ let wrapper;
+
+ const toDate = (day, time) => new Date(`${day}T${time}:00.000Z`);
+ const findDatepicker = () => wrapper.findComponent(GlDatepicker);
+ const findTimepicker = () => wrapper.findComponent('[data-testid="time-picker"]');
+
+ const testDay = '2022-03-22';
+ const testTime = '01:23';
+
+ function createComponent() {
+ wrapper = mount(DatetimePicker, {
+ propsData: {
+ value: toDate(testDay, testTime),
+ },
+ });
+ }
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders the Date in the datepicker and timepicker inputs', () => {
+ expect(findDatepicker().props().value).toEqual(toDate(testDay, testTime));
+ expect(findTimepicker().element.value).toEqual(testTime);
+ });
+
+ it('emits Date with the new day/old time when the date picker changes', () => {
+ const newDay = '1992-06-30';
+ const newTime = '08:00';
+
+ findDatepicker().vm.$emit('input', toDate(newDay, newTime));
+ expect(wrapper.emitted().input).toEqual([[toDate(newDay, testTime)]]);
+ });
+
+ it('emits Date with the old day/new time when the time picker changes', () => {
+ const newTime = '08:00';
+
+ findTimepicker().vm.$emit('input', newTime);
+ expect(wrapper.emitted().input).toEqual([[toDate(testDay, newTime)]]);
+ });
+});
diff --git a/spec/frontend/admin/broadcast_messages/components/message_form_spec.js b/spec/frontend/admin/broadcast_messages/components/message_form_spec.js
new file mode 100644
index 00000000000..88ea79f38b3
--- /dev/null
+++ b/spec/frontend/admin/broadcast_messages/components/message_form_spec.js
@@ -0,0 +1,201 @@
+import { mount } from '@vue/test-utils';
+import { GlBroadcastMessage, GlForm } from '@gitlab/ui';
+import AxiosMockAdapter from 'axios-mock-adapter';
+import { createAlert } from '~/flash';
+import axios from '~/lib/utils/axios_utils';
+import httpStatus from '~/lib/utils/http_status';
+import MessageForm from '~/admin/broadcast_messages/components/message_form.vue';
+import {
+ BROADCAST_MESSAGES_PATH,
+ TYPE_BANNER,
+ TYPE_NOTIFICATION,
+ THEMES,
+} from '~/admin/broadcast_messages/constants';
+import waitForPromises from 'helpers/wait_for_promises';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import { MOCK_TARGET_ACCESS_LEVELS } from '../mock_data';
+
+jest.mock('~/flash');
+
+describe('MessageForm', () => {
+ let wrapper;
+ let axiosMock;
+
+ const defaultProps = {
+ message: 'zzzzzzz',
+ broadcastType: TYPE_BANNER,
+ theme: THEMES[0].value,
+ dismissable: false,
+ targetPath: '',
+ targetAccessLevels: [],
+ startsAt: new Date(),
+ endsAt: new Date(),
+ };
+
+ const findPreview = () => extendedWrapper(wrapper.findComponent(GlBroadcastMessage));
+ const findThemeSelect = () => wrapper.findComponent('[data-testid=theme-select]');
+ const findDismissable = () => wrapper.findComponent('[data-testid=dismissable-checkbox]');
+ const findTargetRoles = () => wrapper.findComponent('[data-testid=target-roles-checkboxes]');
+ const findSubmitButton = () => wrapper.findComponent('[data-testid=submit-button]');
+ const findForm = () => wrapper.findComponent(GlForm);
+
+ function createComponent({ broadcastMessage = {}, glFeatures = {} }) {
+ wrapper = mount(MessageForm, {
+ provide: {
+ glFeatures,
+ targetAccessLevelOptions: MOCK_TARGET_ACCESS_LEVELS,
+ },
+ propsData: {
+ broadcastMessage: {
+ ...defaultProps,
+ ...broadcastMessage,
+ },
+ },
+ });
+ }
+
+ beforeEach(() => {
+ axiosMock = new AxiosMockAdapter(axios);
+ });
+
+ afterEach(() => {
+ axiosMock.restore();
+ createAlert.mockClear();
+ });
+
+ describe('the message preview', () => {
+ it('renders the preview with the user selected theme', () => {
+ const theme = 'blue';
+ createComponent({ broadcastMessage: { theme } });
+ expect(findPreview().props().theme).toEqual(theme);
+ });
+
+ it('renders the placeholder text when the user message is blank', () => {
+ createComponent({ broadcastMessage: { message: ' ' } });
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.messagePlaceholder);
+ });
+ });
+
+ describe('theme select dropdown', () => {
+ it('renders for Banners', () => {
+ createComponent({ broadcastMessage: { broadcastType: TYPE_BANNER } });
+ expect(findThemeSelect().exists()).toBe(true);
+ });
+
+ it('does not render for Notifications', () => {
+ createComponent({ broadcastMessage: { broadcastType: TYPE_NOTIFICATION } });
+ expect(findThemeSelect().exists()).toBe(false);
+ });
+ });
+
+ describe('dismissable checkbox', () => {
+ it('renders for Banners', () => {
+ createComponent({ broadcastMessage: { broadcastType: TYPE_BANNER } });
+ expect(findDismissable().exists()).toBe(true);
+ });
+
+ it('does not render for Notifications', () => {
+ createComponent({ broadcastMessage: { broadcastType: TYPE_NOTIFICATION } });
+ expect(findDismissable().exists()).toBe(false);
+ });
+ });
+
+ describe('target roles checkboxes', () => {
+ it('renders when roleTargetedBroadcastMessages feature is enabled', () => {
+ createComponent({ glFeatures: { roleTargetedBroadcastMessages: true } });
+ expect(findTargetRoles().exists()).toBe(true);
+ });
+
+ it('does not render when roleTargetedBroadcastMessages feature is disabled', () => {
+ createComponent({ glFeatures: { roleTargetedBroadcastMessages: false } });
+ expect(findTargetRoles().exists()).toBe(false);
+ });
+ });
+
+ describe('form submit button', () => {
+ it('renders the "add" text when the message is not persisted', () => {
+ createComponent({ broadcastMessage: { id: undefined } });
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.add);
+ });
+
+ it('renders the "update" text when the message is persisted', () => {
+ createComponent({ broadcastMessage: { id: 100 } });
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.update);
+ });
+
+ it('is disabled when the user message is blank', () => {
+ createComponent({ broadcastMessage: { message: ' ' } });
+ expect(findSubmitButton().props().disabled).toBe(true);
+ });
+
+ it('is not disabled when the user message is present', () => {
+ createComponent({ broadcastMessage: { message: 'alsdjfkldsj' } });
+ expect(findSubmitButton().props().disabled).toBe(false);
+ });
+ });
+
+ describe('form submission', () => {
+ const defaultPayload = {
+ message: defaultProps.message,
+ broadcast_type: defaultProps.broadcastType,
+ theme: defaultProps.theme,
+ dismissable: defaultProps.dismissable,
+ target_path: defaultProps.targetPath,
+ target_access_levels: defaultProps.targetAccessLevels,
+ starts_at: defaultProps.startsAt,
+ ends_at: defaultProps.endsAt,
+ };
+
+ it('sends a create request for a new message form', async () => {
+ createComponent({ broadcastMessage: { id: undefined } });
+ findForm().vm.$emit('submit', { preventDefault: () => {} });
+ await waitForPromises();
+
+ expect(axiosMock.history.post).toHaveLength(1);
+ expect(axiosMock.history.post[0]).toMatchObject({
+ url: BROADCAST_MESSAGES_PATH,
+ data: JSON.stringify(defaultPayload),
+ });
+ });
+
+ it('shows an error alert if the create request fails', async () => {
+ createComponent({ broadcastMessage: { id: undefined } });
+ axiosMock.onPost(BROADCAST_MESSAGES_PATH).replyOnce(httpStatus.BAD_REQUEST);
+ findForm().vm.$emit('submit', { preventDefault: () => {} });
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalledWith(
+ expect.objectContaining({
+ message: wrapper.vm.$options.i18n.addError,
+ }),
+ );
+ });
+
+ it('sends an update request for a persisted message form', async () => {
+ const id = 1337;
+ createComponent({ broadcastMessage: { id } });
+ findForm().vm.$emit('submit', { preventDefault: () => {} });
+ await waitForPromises();
+
+ expect(axiosMock.history.patch).toHaveLength(1);
+ expect(axiosMock.history.patch[0]).toMatchObject({
+ url: `${BROADCAST_MESSAGES_PATH}/${id}`,
+ data: JSON.stringify(defaultPayload),
+ });
+ });
+
+ it('shows an error alert if the update request fails', async () => {
+ const id = 1337;
+ createComponent({ broadcastMessage: { id } });
+ axiosMock.onPost(`${BROADCAST_MESSAGES_PATH}/${id}`).replyOnce(httpStatus.BAD_REQUEST);
+ findForm().vm.$emit('submit', { preventDefault: () => {} });
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalledWith(
+ expect.objectContaining({
+ message: wrapper.vm.$options.i18n.updateError,
+ }),
+ );
+ });
+ });
+});
diff --git a/spec/frontend/admin/broadcast_messages/mock_data.js b/spec/frontend/admin/broadcast_messages/mock_data.js
index 8dd98c2319d..2e20b5cf638 100644
--- a/spec/frontend/admin/broadcast_messages/mock_data.js
+++ b/spec/frontend/admin/broadcast_messages/mock_data.js
@@ -15,3 +15,11 @@ export const generateMockMessages = (n) =>
[...Array(n).keys()].map((id) => generateMockMessage(id + 1));
export const MOCK_MESSAGES = generateMockMessages(5).map((id) => generateMockMessage(id));
+
+export const MOCK_TARGET_ACCESS_LEVELS = [
+ ['Guest', 10],
+ ['Reporter', 20],
+ ['Developer', 30],
+ ['Maintainer', 40],
+ ['Owner', 50],
+];
diff --git a/spec/frontend/admin/signup_restrictions/components/signup_form_spec.js b/spec/frontend/admin/signup_restrictions/components/signup_form_spec.js
index e6718f62b91..f2a951bcc76 100644
--- a/spec/frontend/admin/signup_restrictions/components/signup_form_spec.js
+++ b/spec/frontend/admin/signup_restrictions/components/signup_form_spec.js
@@ -54,7 +54,6 @@ describe('Signup Form', () => {
prop | propValue | elementSelector | formElementPassedDataType | formElementKey | expected
${'signupEnabled'} | ${mockData.signupEnabled} | ${'[name="application_setting[signup_enabled]"]'} | ${'prop'} | ${'value'} | ${mockData.signupEnabled}
${'requireAdminApprovalAfterUserSignup'} | ${mockData.requireAdminApprovalAfterUserSignup} | ${'[name="application_setting[require_admin_approval_after_user_signup]"]'} | ${'prop'} | ${'value'} | ${mockData.requireAdminApprovalAfterUserSignup}
- ${'sendUserConfirmationEmail'} | ${mockData.sendUserConfirmationEmail} | ${'[name="application_setting[send_user_confirmation_email]"]'} | ${'prop'} | ${'value'} | ${mockData.sendUserConfirmationEmail}
${'newUserSignupsCap'} | ${mockData.newUserSignupsCap} | ${'[name="application_setting[new_user_signups_cap]"]'} | ${'attribute'} | ${'value'} | ${mockData.newUserSignupsCap}
${'minimumPasswordLength'} | ${mockData.minimumPasswordLength} | ${'[name="application_setting[minimum_password_length]"]'} | ${'attribute'} | ${'value'} | ${mockData.minimumPasswordLength}
${'minimumPasswordLengthMin'} | ${mockData.minimumPasswordLengthMin} | ${'[name="application_setting[minimum_password_length]"]'} | ${'attribute'} | ${'min'} | ${mockData.minimumPasswordLengthMin}
diff --git a/spec/frontend/admin/signup_restrictions/mock_data.js b/spec/frontend/admin/signup_restrictions/mock_data.js
index dd1ed317497..ce5ec2248fe 100644
--- a/spec/frontend/admin/signup_restrictions/mock_data.js
+++ b/spec/frontend/admin/signup_restrictions/mock_data.js
@@ -3,7 +3,6 @@ export const rawMockData = {
settingsPath: 'path/to/settings',
signupEnabled: 'true',
requireAdminApprovalAfterUserSignup: 'true',
- sendUserConfirmationEmail: 'true',
emailConfirmationSetting: 'hard',
minimumPasswordLength: '8',
minimumPasswordLengthMin: '3',
@@ -32,7 +31,6 @@ export const mockData = {
settingsPath: 'path/to/settings',
signupEnabled: true,
requireAdminApprovalAfterUserSignup: true,
- sendUserConfirmationEmail: true,
emailConfirmationSetting: 'hard',
minimumPasswordLength: '8',
minimumPasswordLengthMin: '3',
diff --git a/spec/frontend/admin/signup_restrictions/utils_spec.js b/spec/frontend/admin/signup_restrictions/utils_spec.js
index f07e14430f9..e393b07baa9 100644
--- a/spec/frontend/admin/signup_restrictions/utils_spec.js
+++ b/spec/frontend/admin/signup_restrictions/utils_spec.js
@@ -10,7 +10,6 @@ describe('utils', () => {
booleanAttributes: [
'signupEnabled',
'requireAdminApprovalAfterUserSignup',
- 'sendUserConfirmationEmail',
'domainDenylistEnabled',
'denylistTypeRawSelected',
'emailRestrictionsEnabled',
diff --git a/spec/frontend/alerts_settings/components/__snapshots__/alerts_form_spec.js.snap b/spec/frontend/alerts_settings/components/__snapshots__/alerts_form_spec.js.snap
index 4693d5a47e4..bff4905a12c 100644
--- a/spec/frontend/alerts_settings/components/__snapshots__/alerts_form_spec.js.snap
+++ b/spec/frontend/alerts_settings/components/__snapshots__/alerts_form_spec.js.snap
@@ -16,7 +16,7 @@ exports[`Alert integration settings form default state should match the default
>
<gl-form-checkbox-stub
checked="true"
- data-qa-selector="create_issue_checkbox"
+ data-qa-selector="create_incident_checkbox"
id="2"
>
<span>
diff --git a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
index fcefcb7cf66..62a3e07186a 100644
--- a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
+++ b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
@@ -32,7 +32,7 @@ import {
} from '~/alerts_settings/utils/error_messages';
import { createAlert, VARIANT_SUCCESS } from '~/flash';
import axios from '~/lib/utils/axios_utils';
-import httpStatusCodes from '~/lib/utils/http_status';
+import httpStatusCodes, { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
import {
createHttpVariables,
updateHttpVariables,
@@ -358,7 +358,7 @@ describe('AlertsSettingsWrapper', () => {
});
it('shows an error alert when integration test payload is invalid', async () => {
- mock.onPost(/(.*)/).replyOnce(httpStatusCodes.UNPROCESSABLE_ENTITY);
+ mock.onPost(/(.*)/).replyOnce(HTTP_STATUS_UNPROCESSABLE_ENTITY);
await wrapper.vm.testAlertPayload({ endpoint: '', data: '', token: '' });
expect(createAlert).toHaveBeenCalledWith({ message: INTEGRATION_PAYLOAD_TEST_ERROR });
expect(createAlert).toHaveBeenCalledTimes(1);
diff --git a/spec/frontend/cycle_analytics/__snapshots__/total_time_spec.js.snap b/spec/frontend/analytics/cycle_analytics/__snapshots__/total_time_spec.js.snap
index 92927ef16ec..92927ef16ec 100644
--- a/spec/frontend/cycle_analytics/__snapshots__/total_time_spec.js.snap
+++ b/spec/frontend/analytics/cycle_analytics/__snapshots__/total_time_spec.js.snap
diff --git a/spec/frontend/analytics/cycle_analytics/base_spec.js b/spec/frontend/analytics/cycle_analytics/base_spec.js
new file mode 100644
index 00000000000..58588ff49ce
--- /dev/null
+++ b/spec/frontend/analytics/cycle_analytics/base_spec.js
@@ -0,0 +1,265 @@
+import { GlLoadingIcon, GlEmptyState } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import Vuex from 'vuex';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import ValueStreamMetrics from '~/analytics/shared/components/value_stream_metrics.vue';
+import BaseComponent from '~/analytics/cycle_analytics/components/base.vue';
+import PathNavigation from '~/analytics/cycle_analytics/components/path_navigation.vue';
+import StageTable from '~/analytics/cycle_analytics/components/stage_table.vue';
+import ValueStreamFilters from '~/analytics/cycle_analytics/components/value_stream_filters.vue';
+import { NOT_ENOUGH_DATA_ERROR } from '~/analytics/cycle_analytics/constants';
+import initState from '~/analytics/cycle_analytics/store/state';
+import {
+ transformedProjectStagePathData,
+ selectedStage,
+ issueEvents,
+ createdBefore,
+ createdAfter,
+ currentGroup,
+ stageCounts,
+ initialPaginationState as pagination,
+} from './mock_data';
+
+const selectedStageEvents = issueEvents.events;
+const noDataSvgPath = 'path/to/no/data';
+const noAccessSvgPath = 'path/to/no/access';
+const selectedStageCount = stageCounts[selectedStage.id];
+const fullPath = 'full/path/to/foo';
+
+Vue.use(Vuex);
+
+let wrapper;
+
+const { id: groupId, path: groupPath } = currentGroup;
+const defaultState = {
+ currentGroup,
+ createdBefore,
+ createdAfter,
+ stageCounts,
+ endpoints: { fullPath, groupId, groupPath },
+};
+
+function createStore({ initialState = {}, initialGetters = {} }) {
+ return new Vuex.Store({
+ state: {
+ ...initState(),
+ ...defaultState,
+ ...initialState,
+ },
+ getters: {
+ pathNavigationData: () => transformedProjectStagePathData,
+ filterParams: () => ({
+ created_after: createdAfter,
+ created_before: createdBefore,
+ }),
+ ...initialGetters,
+ },
+ });
+}
+
+function createComponent({ initialState, initialGetters } = {}) {
+ return extendedWrapper(
+ shallowMount(BaseComponent, {
+ store: createStore({ initialState, initialGetters }),
+ propsData: {
+ noDataSvgPath,
+ noAccessSvgPath,
+ },
+ stubs: {
+ StageTable,
+ },
+ }),
+ );
+}
+
+const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+const findPathNavigation = () => wrapper.findComponent(PathNavigation);
+const findFilters = () => wrapper.findComponent(ValueStreamFilters);
+const findOverviewMetrics = () => wrapper.findComponent(ValueStreamMetrics);
+const findStageTable = () => wrapper.findComponent(StageTable);
+const findStageEvents = () => findStageTable().props('stageEvents');
+const findEmptyStageTitle = () => wrapper.findComponent(GlEmptyState).props('title');
+const findPagination = () => wrapper.findByTestId('vsa-stage-pagination');
+
+const hasMetricsRequests = (reqs) => {
+ const foundReqs = findOverviewMetrics().props('requests');
+ expect(foundReqs.length).toEqual(reqs.length);
+ expect(foundReqs.map(({ name }) => name)).toEqual(reqs);
+};
+
+describe('Value stream analytics component', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ initialState: { selectedStage, selectedStageEvents, pagination } });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('renders the path navigation component', () => {
+ expect(findPathNavigation().exists()).toBe(true);
+ });
+
+ it('receives the stages formatted for the path navigation', () => {
+ expect(findPathNavigation().props('stages')).toBe(transformedProjectStagePathData);
+ });
+
+ it('renders the overview metrics', () => {
+ expect(findOverviewMetrics().exists()).toBe(true);
+ });
+
+ it('passes requests prop to the metrics component', () => {
+ hasMetricsRequests(['recent activity']);
+ });
+
+ it('renders the stage table', () => {
+ expect(findStageTable().exists()).toBe(true);
+ });
+
+ it('passes the selected stage count to the stage table', () => {
+ expect(findStageTable().props('stageCount')).toBe(selectedStageCount);
+ });
+
+ it('renders the stage table events', () => {
+ expect(findStageEvents()).toEqual(selectedStageEvents);
+ });
+
+ it('renders the filters', () => {
+ expect(findFilters().exists()).toBe(true);
+ });
+
+ it('displays the date range selector and hides the project selector', () => {
+ expect(findFilters().props()).toMatchObject({
+ hasProjectFilter: false,
+ hasDateRangeFilter: true,
+ });
+ });
+
+ it('passes the paths to the filter bar', () => {
+ expect(findFilters().props()).toEqual({
+ groupId,
+ groupPath,
+ endDate: createdBefore,
+ hasDateRangeFilter: true,
+ hasProjectFilter: false,
+ selectedProjects: [],
+ startDate: createdAfter,
+ });
+ });
+
+ it('does not render the loading icon', () => {
+ expect(findLoadingIcon().exists()).toBe(false);
+ });
+
+ it('renders pagination', () => {
+ expect(findPagination().exists()).toBe(true);
+ });
+
+ describe('with `cycleAnalyticsForGroups=true` license', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ initialState: { features: { cycleAnalyticsForGroups: true } } });
+ });
+
+ it('passes requests prop to the metrics component', () => {
+ hasMetricsRequests(['time summary', 'recent activity']);
+ });
+ });
+
+ describe('isLoading = true', () => {
+ beforeEach(() => {
+ wrapper = createComponent({
+ initialState: { isLoading: true },
+ });
+ });
+
+ it('renders the path navigation component with prop `loading` set to true', () => {
+ expect(findPathNavigation().props('loading')).toBe(true);
+ });
+
+ it('does not render the stage table', () => {
+ expect(findStageTable().exists()).toBe(false);
+ });
+
+ it('renders the overview metrics', () => {
+ expect(findOverviewMetrics().exists()).toBe(true);
+ });
+
+ it('renders the loading icon', () => {
+ expect(findLoadingIcon().exists()).toBe(true);
+ });
+ });
+
+ describe('isLoadingStage = true', () => {
+ beforeEach(() => {
+ wrapper = createComponent({
+ initialState: { isLoadingStage: true },
+ });
+ });
+
+ it('renders the stage table with a loading icon', () => {
+ const tableWrapper = findStageTable();
+ expect(tableWrapper.exists()).toBe(true);
+ expect(tableWrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
+ });
+
+ it('renders the path navigation loading state', () => {
+ expect(findPathNavigation().props('loading')).toBe(true);
+ });
+ });
+
+ describe('isEmptyStage = true', () => {
+ const emptyStageParams = {
+ isEmptyStage: true,
+ selectedStage: { ...selectedStage, emptyStageText: 'This stage is empty' },
+ };
+ beforeEach(() => {
+ wrapper = createComponent({ initialState: emptyStageParams });
+ });
+
+ it('renders the empty stage with `Not enough data` message', () => {
+ expect(findEmptyStageTitle()).toBe(NOT_ENOUGH_DATA_ERROR);
+ });
+
+ describe('with a selectedStageError', () => {
+ beforeEach(() => {
+ wrapper = createComponent({
+ initialState: {
+ ...emptyStageParams,
+ selectedStageError: 'There is too much data to calculate',
+ },
+ });
+ });
+
+ it('renders the empty stage with `There is too much data to calculate` message', () => {
+ expect(findEmptyStageTitle()).toBe('There is too much data to calculate');
+ });
+ });
+ });
+
+ describe('without a selected stage', () => {
+ beforeEach(() => {
+ wrapper = createComponent({
+ initialGetters: { pathNavigationData: () => [] },
+ initialState: { selectedStage: null, isEmptyStage: true },
+ });
+ });
+
+ it('renders the stage table', () => {
+ expect(findStageTable().exists()).toBe(true);
+ });
+
+ it('does not render the path navigation', () => {
+ expect(findPathNavigation().exists()).toBe(false);
+ });
+
+ it('does not render the stage table events', () => {
+ expect(findStageEvents()).toHaveLength(0);
+ });
+
+ it('does not render the loading icon', () => {
+ expect(findLoadingIcon().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/analytics/cycle_analytics/filter_bar_spec.js b/spec/frontend/analytics/cycle_analytics/filter_bar_spec.js
new file mode 100644
index 00000000000..2b26b202882
--- /dev/null
+++ b/spec/frontend/analytics/cycle_analytics/filter_bar_spec.js
@@ -0,0 +1,229 @@
+import { shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
+import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
+import Vuex from 'vuex';
+import {
+ filterMilestones,
+ filterLabels,
+} from 'jest/vue_shared/components/filtered_search_bar/store/modules/filters/mock_data';
+import FilterBar from '~/analytics/cycle_analytics/components/filter_bar.vue';
+import storeConfig from '~/analytics/cycle_analytics/store';
+import * as commonUtils from '~/lib/utils/common_utils';
+import * as urlUtils from '~/lib/utils/url_utility';
+import {
+ TOKEN_TYPE_ASSIGNEE,
+ TOKEN_TYPE_AUTHOR,
+ TOKEN_TYPE_LABEL,
+ TOKEN_TYPE_MILESTONE,
+} from '~/vue_shared/components/filtered_search_bar/constants';
+import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
+import * as utils from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
+import initialFiltersState from '~/vue_shared/components/filtered_search_bar/store/modules/filters/state';
+import UrlSync from '~/vue_shared/components/url_sync.vue';
+
+Vue.use(Vuex);
+
+const milestoneTokenType = TOKEN_TYPE_MILESTONE;
+const labelsTokenType = TOKEN_TYPE_LABEL;
+const authorTokenType = TOKEN_TYPE_AUTHOR;
+const assigneesTokenType = TOKEN_TYPE_ASSIGNEE;
+
+const initialFilterBarState = {
+ selectedMilestone: null,
+ selectedAuthor: null,
+ selectedAssigneeList: null,
+ selectedLabelList: null,
+};
+
+const defaultParams = {
+ milestone_title: null,
+ 'not[milestone_title]': null,
+ author_username: null,
+ 'not[author_username]': null,
+ assignee_username: null,
+ 'not[assignee_username]': null,
+ label_name: null,
+ 'not[label_name]': null,
+};
+
+async function shouldMergeUrlParams(wrapper, result) {
+ await nextTick();
+ expect(urlUtils.mergeUrlParams).toHaveBeenCalledWith(result, window.location.href, {
+ spreadArrays: true,
+ });
+ expect(commonUtils.historyPushState).toHaveBeenCalled();
+}
+
+describe('Filter bar', () => {
+ let wrapper;
+ let store;
+ let mock;
+
+ let setFiltersMock;
+
+ const createStore = (initialState = {}) => {
+ setFiltersMock = jest.fn();
+
+ return new Vuex.Store({
+ modules: {
+ filters: {
+ namespaced: true,
+ state: {
+ ...initialFiltersState(),
+ ...initialState,
+ },
+ actions: {
+ setFilters: setFiltersMock,
+ },
+ },
+ },
+ });
+ };
+
+ const createComponent = (initialStore) => {
+ return shallowMount(FilterBar, {
+ store: initialStore,
+ propsData: {
+ groupPath: 'foo',
+ },
+ stubs: {
+ UrlSync,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ mock.restore();
+ });
+
+ const selectedMilestone = [filterMilestones[0]];
+ const selectedLabelList = [filterLabels[0]];
+
+ const findFilteredSearch = () => wrapper.findComponent(FilteredSearchBar);
+ const getSearchToken = (type) =>
+ findFilteredSearch()
+ .props('tokens')
+ .find((token) => token.type === type);
+
+ describe('default', () => {
+ beforeEach(() => {
+ store = createStore();
+ wrapper = createComponent(store);
+ });
+
+ it('renders FilteredSearchBar component', () => {
+ expect(findFilteredSearch().exists()).toBe(true);
+ });
+ });
+
+ describe('when the state has data', () => {
+ beforeEach(() => {
+ store = createStore({
+ milestones: { data: selectedMilestone },
+ labels: { data: selectedLabelList },
+ authors: { data: [] },
+ assignees: { data: [] },
+ });
+ wrapper = createComponent(store);
+ });
+
+ it('displays the milestone and label token', () => {
+ const tokens = findFilteredSearch().props('tokens');
+
+ expect(tokens).toHaveLength(4);
+ expect(tokens[0].type).toBe(milestoneTokenType);
+ expect(tokens[1].type).toBe(labelsTokenType);
+ expect(tokens[2].type).toBe(authorTokenType);
+ expect(tokens[3].type).toBe(assigneesTokenType);
+ });
+
+ it('provides the initial milestone token', () => {
+ const { initialMilestones: milestoneToken } = getSearchToken(milestoneTokenType);
+
+ expect(milestoneToken).toHaveLength(selectedMilestone.length);
+ });
+
+ it('provides the initial label token', () => {
+ const { initialLabels: labelToken } = getSearchToken(labelsTokenType);
+
+ expect(labelToken).toHaveLength(selectedLabelList.length);
+ });
+ });
+
+ describe('when the user interacts', () => {
+ beforeEach(() => {
+ store = createStore({
+ milestones: { data: filterMilestones },
+ labels: { data: filterLabels },
+ });
+ wrapper = createComponent(store);
+ jest.spyOn(utils, 'processFilters');
+ });
+
+ it('clicks on the search button, setFilters is dispatched', () => {
+ const filters = [
+ { type: TOKEN_TYPE_MILESTONE, value: { data: selectedMilestone[0].title, operator: '=' } },
+ { type: TOKEN_TYPE_LABEL, value: { data: selectedLabelList[0].title, operator: '=' } },
+ ];
+
+ findFilteredSearch().vm.$emit('onFilter', filters);
+
+ expect(utils.processFilters).toHaveBeenCalledWith(filters);
+
+ expect(setFiltersMock).toHaveBeenCalledWith(expect.anything(), {
+ selectedLabelList: [{ value: selectedLabelList[0].title, operator: '=' }],
+ selectedMilestone: { value: selectedMilestone[0].title, operator: '=' },
+ selectedAssigneeList: [],
+ selectedAuthor: null,
+ });
+ });
+ });
+
+ describe.each([
+ ['selectedMilestone', 'milestone_title', { value: '12.0', operator: '=' }, '12.0'],
+ ['selectedAuthor', 'author_username', { value: 'rootUser', operator: '=' }, 'rootUser'],
+ [
+ 'selectedLabelList',
+ 'label_name',
+ [
+ { value: 'Afternix', operator: '=' },
+ { value: 'Brouceforge', operator: '=' },
+ ],
+ ['Afternix', 'Brouceforge'],
+ ],
+ [
+ 'selectedAssigneeList',
+ 'assignee_username',
+ [
+ { value: 'rootUser', operator: '=' },
+ { value: 'secondaryUser', operator: '=' },
+ ],
+ ['rootUser', 'secondaryUser'],
+ ],
+ ])('with a %s updates the %s url parameter', (stateKey, paramKey, payload, result) => {
+ beforeEach(() => {
+ commonUtils.historyPushState = jest.fn();
+ urlUtils.mergeUrlParams = jest.fn();
+
+ mock = new MockAdapter(axios);
+ wrapper = createComponent(storeConfig);
+
+ wrapper.vm.$store.dispatch('filters/setFilters', {
+ ...initialFilterBarState,
+ [stateKey]: payload,
+ });
+ });
+ it(`sets the ${paramKey} url parameter`, () => {
+ return shouldMergeUrlParams(wrapper, {
+ ...defaultParams,
+ [paramKey]: result,
+ });
+ });
+ });
+});
diff --git a/spec/frontend/analytics/cycle_analytics/formatted_stage_count_spec.js b/spec/frontend/analytics/cycle_analytics/formatted_stage_count_spec.js
new file mode 100644
index 00000000000..9be92bb92bc
--- /dev/null
+++ b/spec/frontend/analytics/cycle_analytics/formatted_stage_count_spec.js
@@ -0,0 +1,34 @@
+import { shallowMount } from '@vue/test-utils';
+import Component from '~/analytics/cycle_analytics/components/formatted_stage_count.vue';
+
+describe('Formatted Stage Count', () => {
+ let wrapper = null;
+
+ const createComponent = (stageCount = null) => {
+ wrapper = shallowMount(Component, {
+ propsData: {
+ stageCount,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it.each`
+ stageCount | expectedOutput
+ ${null} | ${'-'}
+ ${1} | ${'1 item'}
+ ${10} | ${'10 items'}
+ ${1000} | ${'1,000 items'}
+ ${1001} | ${'1,000+ items'}
+ `('returns "$expectedOutput" for stageCount=$stageCount', ({ stageCount, expectedOutput }) => {
+ createComponent(stageCount);
+ expect(wrapper.text()).toContain(expectedOutput);
+ });
+});
diff --git a/spec/frontend/analytics/cycle_analytics/mock_data.js b/spec/frontend/analytics/cycle_analytics/mock_data.js
new file mode 100644
index 00000000000..f820f755400
--- /dev/null
+++ b/spec/frontend/analytics/cycle_analytics/mock_data.js
@@ -0,0 +1,261 @@
+import valueStreamAnalyticsStages from 'test_fixtures/projects/analytics/value_stream_analytics/stages.json';
+import issueStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/issue.json';
+import planStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/plan.json';
+import reviewStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/review.json';
+import codeStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/code.json';
+import testStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/test.json';
+import stagingStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/staging.json';
+
+import { TEST_HOST } from 'helpers/test_constants';
+import {
+ DEFAULT_VALUE_STREAM,
+ PAGINATION_TYPE,
+ PAGINATION_SORT_DIRECTION_DESC,
+ PAGINATION_SORT_FIELD_END_EVENT,
+} from '~/analytics/cycle_analytics/constants';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import { getDateInPast } from '~/lib/utils/datetime_utility';
+
+const DEFAULT_DAYS_IN_PAST = 30;
+export const createdBefore = new Date(2019, 0, 14);
+export const createdAfter = getDateInPast(createdBefore, DEFAULT_DAYS_IN_PAST);
+
+export const deepCamelCase = (obj) => convertObjectPropsToCamelCase(obj, { deep: true });
+
+export const getStageByTitle = (stages, title) =>
+ stages.find((stage) => stage.title && stage.title.toLowerCase().trim() === title) || {};
+
+export const defaultStages = ['issue', 'plan', 'review', 'code', 'test', 'staging'];
+
+const stageFixtures = {
+ issue: issueStageFixtures,
+ plan: planStageFixtures,
+ review: reviewStageFixtures,
+ code: codeStageFixtures,
+ test: testStageFixtures,
+ staging: stagingStageFixtures,
+};
+
+export const summary = [
+ { value: '20', title: 'New Issues' },
+ { value: null, title: 'Commits' },
+ { value: null, title: 'Deploys' },
+ { value: null, title: 'Deployment Frequency', unit: '/day' },
+];
+
+export const issueStage = {
+ id: 'issue',
+ title: 'Issue',
+ name: 'issue',
+ legend: '',
+ description: 'Time before an issue gets scheduled',
+ value: null,
+};
+
+export const planStage = {
+ id: 'plan',
+ title: 'Plan',
+ name: 'plan',
+ legend: '',
+ description: 'Time before an issue starts implementation',
+ value: 75600,
+};
+
+export const codeStage = {
+ id: 'code',
+ title: 'Code',
+ name: 'code',
+ legend: '',
+ description: 'Time until first merge request',
+ value: 172800,
+};
+
+export const testStage = {
+ id: 'test',
+ title: 'Test',
+ name: 'test',
+ legend: '',
+ description: 'Total test time for all commits/merges',
+ value: 17550,
+};
+
+export const reviewStage = {
+ id: 'review',
+ title: 'Review',
+ name: 'review',
+ legend: '',
+ description: 'Time between merge request creation and merge/close',
+ value: null,
+};
+
+export const stagingStage = {
+ id: 'staging',
+ title: 'Staging',
+ name: 'staging',
+ legend: '',
+ description: 'From merge request merge until deploy to production',
+ value: 172800,
+};
+
+export const selectedStage = {
+ ...issueStage,
+ value: null,
+ active: false,
+ emptyStageText:
+ 'The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.',
+
+ slug: 'issue',
+};
+
+export const convertedData = {
+ summary: [
+ { value: '20', title: 'New Issues' },
+ { value: '-', title: 'Commits' },
+ { value: '-', title: 'Deploys' },
+ { value: '-', title: 'Deployment Frequency', unit: '/day' },
+ ],
+};
+
+export const rawIssueEvents = stageFixtures.issue;
+export const issueEvents = deepCamelCase(rawIssueEvents);
+export const reviewEvents = deepCamelCase(stageFixtures.review);
+
+export const pathNavIssueMetric = 172800;
+
+export const rawStageCounts = [
+ { id: 'issue', count: 6 },
+ { id: 'plan', count: 6 },
+ { id: 'code', count: 1 },
+ { id: 'test', count: 5 },
+ { id: 'review', count: 12 },
+ { id: 'staging', count: 3 },
+];
+
+export const stageCounts = {
+ code: 1,
+ issue: 6,
+ plan: 6,
+ review: 12,
+ staging: 3,
+ test: 5,
+};
+
+export const rawStageMedians = [
+ { id: 'issue', value: 172800 },
+ { id: 'plan', value: 86400 },
+ { id: 'review', value: 1036800 },
+ { id: 'code', value: 129600 },
+ { id: 'test', value: 259200 },
+ { id: 'staging', value: 388800 },
+];
+
+export const stageMedians = {
+ issue: 172800,
+ plan: 86400,
+ review: 1036800,
+ code: 129600,
+ test: 259200,
+ staging: 388800,
+};
+
+export const formattedStageMedians = {
+ issue: '2d',
+ plan: '1d',
+ review: '1w',
+ code: '1d',
+ test: '3d',
+ staging: '4d',
+};
+
+export const allowedStages = [issueStage, planStage, codeStage];
+
+export const transformedProjectStagePathData = [
+ {
+ metric: 172800,
+ selected: true,
+ stageCount: 6,
+ icon: null,
+ id: 'issue',
+ title: 'Issue',
+ name: 'issue',
+ legend: '',
+ description: 'Time before an issue gets scheduled',
+ value: null,
+ },
+ {
+ metric: 86400,
+ selected: false,
+ stageCount: 6,
+ icon: null,
+ id: 'plan',
+ title: 'Plan',
+ name: 'plan',
+ legend: '',
+ description: 'Time before an issue starts implementation',
+ value: 75600,
+ },
+ {
+ metric: 129600,
+ selected: false,
+ stageCount: 1,
+ icon: null,
+ id: 'code',
+ title: 'Code',
+ name: 'code',
+ legend: '',
+ description: 'Time until first merge request',
+ value: 172800,
+ },
+];
+
+export const selectedValueStream = DEFAULT_VALUE_STREAM;
+
+export const group = {
+ id: 1,
+ name: 'foo',
+ path: 'foo',
+ full_path: 'foo',
+ avatar_url: `${TEST_HOST}/images/home/nasa.svg`,
+};
+
+export const currentGroup = convertObjectPropsToCamelCase(group, { deep: true });
+
+export const selectedProjects = [
+ {
+ id: 'gid://gitlab/Project/1',
+ name: 'cool project',
+ pathWithNamespace: 'group/cool-project',
+ avatarUrl: null,
+ },
+ {
+ id: 'gid://gitlab/Project/2',
+ name: 'another cool project',
+ pathWithNamespace: 'group/another-cool-project',
+ avatarUrl: null,
+ },
+];
+
+export const rawValueStreamStages = valueStreamAnalyticsStages.stages;
+
+export const valueStreamStages = rawValueStreamStages.map((s) =>
+ convertObjectPropsToCamelCase(s, { deep: true }),
+);
+
+export const initialPaginationQuery = {
+ page: 15,
+ sort: PAGINATION_SORT_FIELD_END_EVENT,
+ direction: PAGINATION_SORT_DIRECTION_DESC,
+};
+
+export const initialPaginationState = {
+ ...initialPaginationQuery,
+ page: null,
+ hasNextPage: false,
+};
+
+export const basePaginationResult = {
+ pagination: PAGINATION_TYPE,
+ sort: PAGINATION_SORT_FIELD_END_EVENT,
+ direction: PAGINATION_SORT_DIRECTION_DESC,
+ page: null,
+};
diff --git a/spec/frontend/analytics/cycle_analytics/path_navigation_spec.js b/spec/frontend/analytics/cycle_analytics/path_navigation_spec.js
new file mode 100644
index 00000000000..107e62035c3
--- /dev/null
+++ b/spec/frontend/analytics/cycle_analytics/path_navigation_spec.js
@@ -0,0 +1,150 @@
+import { GlPath, GlSkeletonLoader } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import Component from '~/analytics/cycle_analytics/components/path_navigation.vue';
+import { transformedProjectStagePathData, selectedStage } from './mock_data';
+
+describe('Project PathNavigation', () => {
+ let wrapper = null;
+ let trackingSpy = null;
+
+ const createComponent = (props) => {
+ return extendedWrapper(
+ mount(Component, {
+ propsData: {
+ stages: transformedProjectStagePathData,
+ selectedStage,
+ loading: false,
+ ...props,
+ },
+ }),
+ );
+ };
+
+ const findPathNavigation = () => {
+ return wrapper.findByTestId('gl-path-nav');
+ };
+
+ const findPathNavigationItems = () => {
+ return findPathNavigation().findAll('li');
+ };
+
+ const findPathNavigationTitles = () => {
+ return findPathNavigation()
+ .findAll('li button')
+ .wrappers.map((w) => w.html());
+ };
+
+ const clickItemAt = (index) => {
+ findPathNavigationItems().at(index).find('button').trigger('click');
+ };
+
+ const pathItemContent = () => findPathNavigationItems().wrappers.map(extendedWrapper);
+ const firstPopover = () => wrapper.findAllByTestId('stage-item-popover').at(0);
+
+ beforeEach(() => {
+ wrapper = createComponent();
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ });
+
+ afterEach(() => {
+ unmockTracking();
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('displays correctly', () => {
+ it('has the correct props', () => {
+ expect(wrapper.findComponent(GlPath).props('items')).toMatchObject(
+ transformedProjectStagePathData,
+ );
+ });
+
+ it('contains all the expected stages', () => {
+ const stageContent = findPathNavigationTitles();
+ transformedProjectStagePathData.forEach((stage, index) => {
+ expect(stageContent[index]).toContain(stage.title);
+ });
+ });
+
+ describe('loading', () => {
+ describe('is false', () => {
+ it('displays the gl-path component', () => {
+ expect(wrapper.findComponent(GlPath).exists()).toBe(true);
+ });
+
+ it('hides the gl-skeleton-loading component', () => {
+ expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(false);
+ });
+
+ it('renders each stage', () => {
+ const result = findPathNavigationTitles();
+ expect(result.length).toBe(transformedProjectStagePathData.length);
+ });
+
+ it('renders each stage with its median', () => {
+ const result = findPathNavigationTitles();
+ transformedProjectStagePathData.forEach(({ title, metric }, index) => {
+ expect(result[index]).toContain(title);
+ expect(result[index]).toContain(metric.toString());
+ });
+ });
+
+ describe('popovers', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ stages: transformedProjectStagePathData });
+ });
+
+ it('renders popovers for all stages', () => {
+ pathItemContent().forEach((stage) => {
+ expect(stage.findByTestId('stage-item-popover').exists()).toBe(true);
+ });
+ });
+
+ it('shows the median stage time for the first stage item', () => {
+ expect(firstPopover().text()).toContain('Stage time (median)');
+ });
+ });
+ });
+
+ describe('is true', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ loading: true });
+ });
+
+ it('hides the gl-path component', () => {
+ expect(wrapper.findComponent(GlPath).exists()).toBe(false);
+ });
+
+ it('displays the gl-skeleton-loading component', () => {
+ expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(true);
+ });
+ });
+ });
+ });
+
+ describe('event handling', () => {
+ it('emits the selected event', () => {
+ expect(wrapper.emitted('selected')).toBeUndefined();
+
+ clickItemAt(0);
+ clickItemAt(1);
+ clickItemAt(2);
+
+ expect(wrapper.emitted().selected).toEqual([
+ [transformedProjectStagePathData[0]],
+ [transformedProjectStagePathData[1]],
+ [transformedProjectStagePathData[2]],
+ ]);
+ });
+
+ it('sends tracking information', () => {
+ clickItemAt(0);
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_path_navigation', {
+ extra: { stage_id: selectedStage.slug },
+ });
+ });
+ });
+});
diff --git a/spec/frontend/analytics/cycle_analytics/stage_table_spec.js b/spec/frontend/analytics/cycle_analytics/stage_table_spec.js
new file mode 100644
index 00000000000..cfccce7eae9
--- /dev/null
+++ b/spec/frontend/analytics/cycle_analytics/stage_table_spec.js
@@ -0,0 +1,371 @@
+import { GlEmptyState, GlLoadingIcon, GlTable } from '@gitlab/ui';
+import { shallowMount, mount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import StageTable from '~/analytics/cycle_analytics/components/stage_table.vue';
+import { PAGINATION_SORT_FIELD_DURATION } from '~/analytics/cycle_analytics/constants';
+import { issueEvents, issueStage, reviewStage, reviewEvents } from './mock_data';
+
+let wrapper = null;
+let trackingSpy = null;
+
+const noDataSvgPath = 'path/to/no/data';
+const emptyStateTitle = 'Too much data';
+const notEnoughDataError = "We don't have enough data to show this stage.";
+const issueEventItems = issueEvents.events;
+const reviewEventItems = reviewEvents.events;
+const [firstIssueEvent] = issueEventItems;
+const [firstReviewEvent] = reviewEventItems;
+const pagination = { page: 1, hasNextPage: true };
+
+const findStageEvents = () => wrapper.findAllByTestId('vsa-stage-event');
+const findPagination = () => wrapper.findByTestId('vsa-stage-pagination');
+const findTable = () => wrapper.findComponent(GlTable);
+const findTableHead = () => wrapper.find('thead');
+const findTableHeadColumns = () => findTableHead().findAll('th');
+const findStageEventTitle = (ev) => extendedWrapper(ev).findByTestId('vsa-stage-event-title');
+const findStageEventLink = (ev) => extendedWrapper(ev).findByTestId('vsa-stage-event-link');
+const findStageTime = () => wrapper.findByTestId('vsa-stage-event-time');
+const findStageLastEvent = () => wrapper.findByTestId('vsa-stage-last-event');
+const findIcon = (name) => wrapper.findByTestId(`${name}-icon`);
+
+function createComponent(props = {}, shallow = false) {
+ const func = shallow ? shallowMount : mount;
+ return extendedWrapper(
+ func(StageTable, {
+ propsData: {
+ isLoading: false,
+ stageEvents: issueEventItems,
+ noDataSvgPath,
+ selectedStage: issueStage,
+ pagination,
+ ...props,
+ },
+ stubs: {
+ GlLoadingIcon,
+ GlEmptyState,
+ },
+ }),
+ );
+}
+
+describe('StageTable', () => {
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('is loaded with data', () => {
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ it('will render the correct events', () => {
+ const evs = findStageEvents();
+ expect(evs).toHaveLength(issueEventItems.length);
+
+ const titles = evs.wrappers.map((ev) => findStageEventTitle(ev).text());
+ issueEventItems.forEach((ev, index) => {
+ expect(titles[index]).toBe(ev.title);
+ });
+ });
+
+ it('will not display the default data message', () => {
+ expect(wrapper.html()).not.toContain(notEnoughDataError);
+ });
+ });
+
+ describe('with minimal stage data', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ currentStage: { title: 'New stage title' } });
+ });
+
+ it('will render the correct events', () => {
+ const evs = findStageEvents();
+ expect(evs).toHaveLength(issueEventItems.length);
+
+ const titles = evs.wrappers.map((ev) => findStageEventTitle(ev).text());
+ issueEventItems.forEach((ev, index) => {
+ expect(titles[index]).toBe(ev.title);
+ });
+ });
+
+ it('will not display the project name in the record link', () => {
+ const evs = findStageEvents();
+
+ const links = evs.wrappers.map((ev) => findStageEventLink(ev).text());
+ issueEventItems.forEach((ev, index) => {
+ expect(links[index]).toBe(`#${ev.iid}`);
+ });
+ });
+ });
+
+ describe('default event', () => {
+ beforeEach(() => {
+ wrapper = createComponent({
+ stageEvents: [{ ...firstIssueEvent }],
+ selectedStage: { ...issueStage, custom: false },
+ });
+ });
+
+ it('will render the event title', () => {
+ expect(wrapper.findByTestId('vsa-stage-event-title').text()).toBe(firstIssueEvent.title);
+ });
+
+ it('will set the workflow title to "Issues"', () => {
+ expect(findTableHead().text()).toContain('Issues');
+ });
+
+ it('does not render the fork icon', () => {
+ expect(findIcon('fork').exists()).toBe(false);
+ });
+
+ it('does not render the branch icon', () => {
+ expect(findIcon('commit').exists()).toBe(false);
+ });
+
+ it('will render the total time', () => {
+ const createdAt = firstIssueEvent.createdAt.replace(' ago', '');
+ expect(findStageTime().text()).toBe(createdAt);
+ });
+
+ it('will render the end event', () => {
+ expect(findStageLastEvent().text()).toBe(firstIssueEvent.endEventTimestamp);
+ });
+
+ it('will render the author', () => {
+ expect(wrapper.findByTestId('vsa-stage-event-author').text()).toContain(
+ firstIssueEvent.author.name,
+ );
+ });
+
+ it('will render the created at date', () => {
+ expect(wrapper.findByTestId('vsa-stage-event-date').text()).toContain(
+ firstIssueEvent.createdAt,
+ );
+ });
+ });
+
+ describe('merge request event', () => {
+ beforeEach(() => {
+ wrapper = createComponent({
+ stageEvents: [{ ...firstReviewEvent }],
+ selectedStage: { ...reviewStage, custom: false },
+ });
+ });
+
+ it('will set the workflow title to "Merge requests"', () => {
+ expect(findTableHead().text()).toContain('Merge requests');
+ expect(findTableHead().text()).not.toContain('Issues');
+ });
+ });
+
+ describe('isLoading = true', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ isLoading: true }, true);
+ });
+
+ it('will display the loading icon', () => {
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
+ });
+
+ it('will not display pagination', () => {
+ expect(findPagination().exists()).toBe(false);
+ });
+ });
+
+ describe('with no stageEvents', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ stageEvents: [] });
+ });
+
+ it('will render the empty state', () => {
+ expect(wrapper.findComponent(GlEmptyState).exists()).toBe(true);
+ });
+
+ it('will display the default no data message', () => {
+ expect(wrapper.html()).toContain(notEnoughDataError);
+ });
+
+ it('will not display the pagination component', () => {
+ expect(findPagination().exists()).toBe(false);
+ });
+ });
+
+ describe('emptyStateTitle set', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ stageEvents: [], emptyStateTitle });
+ });
+
+ it('will display the custom message', () => {
+ expect(wrapper.html()).not.toContain(notEnoughDataError);
+ expect(wrapper.html()).toContain(emptyStateTitle);
+ });
+ });
+
+ describe('includeProjectName set', () => {
+ const fakenamespace = 'some/fake/path';
+ beforeEach(() => {
+ wrapper = createComponent({ includeProjectName: true });
+ });
+
+ it('will display the project name in the record link', () => {
+ const evs = findStageEvents();
+
+ const links = evs.wrappers.map((ev) => findStageEventLink(ev).text());
+ issueEventItems.forEach((ev, index) => {
+ expect(links[index]).toBe(`${ev.projectPath}#${ev.iid}`);
+ });
+ });
+
+ describe.each`
+ namespaceFullPath | hasFullPath
+ ${'fake'} | ${false}
+ ${fakenamespace} | ${true}
+ `('with a namespace', ({ namespaceFullPath, hasFullPath }) => {
+ let evs = null;
+ let links = null;
+
+ beforeEach(() => {
+ wrapper = createComponent({
+ includeProjectName: true,
+ stageEvents: issueEventItems.map((ie) => ({ ...ie, namespaceFullPath })),
+ });
+
+ evs = findStageEvents();
+ links = evs.wrappers.map((ev) => findStageEventLink(ev).text());
+ });
+
+ it(`with namespaceFullPath='${namespaceFullPath}' ${
+ hasFullPath ? 'will' : 'does not'
+ } include the namespace`, () => {
+ issueEventItems.forEach((ev, index) => {
+ if (hasFullPath) {
+ expect(links[index]).toBe(`${namespaceFullPath}/${ev.projectPath}#${ev.iid}`);
+ } else {
+ expect(links[index]).toBe(`${ev.projectPath}#${ev.iid}`);
+ }
+ });
+ });
+ });
+ });
+
+ describe('Pagination', () => {
+ beforeEach(() => {
+ wrapper = createComponent();
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ });
+
+ afterEach(() => {
+ unmockTracking();
+ wrapper.destroy();
+ });
+
+ it('will display the pagination component', () => {
+ expect(findPagination().exists()).toBe(true);
+ });
+
+ it('clicking prev or next will emit an event', async () => {
+ expect(wrapper.emitted('handleUpdatePagination')).toBeUndefined();
+
+ findPagination().vm.$emit('input', 2);
+ await nextTick();
+
+ expect(wrapper.emitted('handleUpdatePagination')[0]).toEqual([{ page: 2 }]);
+ });
+
+ it('clicking prev or next will send tracking information', () => {
+ findPagination().vm.$emit('input', 2);
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_button', { label: 'pagination' });
+ });
+
+ describe('with `hasNextPage=false', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ pagination: { page: 1, hasNextPage: false } });
+ });
+
+ it('will not display the pagination component', () => {
+ expect(findPagination().exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('Sorting', () => {
+ const triggerTableSort = (sortDesc = true) =>
+ findTable().vm.$emit('sort-changed', {
+ sortBy: PAGINATION_SORT_FIELD_DURATION,
+ sortDesc,
+ });
+
+ beforeEach(() => {
+ wrapper = createComponent();
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ });
+
+ afterEach(() => {
+ unmockTracking();
+ wrapper.destroy();
+ });
+
+ it('can sort the end event or duration', () => {
+ findTableHeadColumns()
+ .wrappers.slice(1)
+ .forEach((w) => {
+ expect(w.attributes('aria-sort')).toBe('none');
+ });
+ });
+
+ it('cannot be sorted by title', () => {
+ findTableHeadColumns()
+ .wrappers.slice(0, 1)
+ .forEach((w) => {
+ expect(w.attributes('aria-sort')).toBeUndefined();
+ });
+ });
+
+ it('clicking a table column will send tracking information', () => {
+ triggerTableSort();
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_button', {
+ label: 'sort_duration_desc',
+ });
+ });
+
+ it('clicking a table column will update the sort field', () => {
+ expect(wrapper.emitted('handleUpdatePagination')).toBeUndefined();
+ triggerTableSort();
+
+ expect(wrapper.emitted('handleUpdatePagination')[0]).toEqual([
+ {
+ direction: 'desc',
+ sort: 'duration',
+ },
+ ]);
+ });
+
+ it('with sortDesc=false will toggle the direction field', () => {
+ expect(wrapper.emitted('handleUpdatePagination')).toBeUndefined();
+ triggerTableSort(false);
+
+ expect(wrapper.emitted('handleUpdatePagination')[0]).toEqual([
+ {
+ direction: 'asc',
+ sort: 'duration',
+ },
+ ]);
+ });
+
+ describe('with sortable=false', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ sortable: false });
+ });
+
+ it('cannot sort the table', () => {
+ findTableHeadColumns().wrappers.forEach((w) => {
+ expect(w.attributes('aria-sort')).toBeUndefined();
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/analytics/cycle_analytics/store/actions_spec.js b/spec/frontend/analytics/cycle_analytics/store/actions_spec.js
new file mode 100644
index 00000000000..f87807804c9
--- /dev/null
+++ b/spec/frontend/analytics/cycle_analytics/store/actions_spec.js
@@ -0,0 +1,518 @@
+import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
+import testAction from 'helpers/vuex_action_helper';
+import * as actions from '~/analytics/cycle_analytics/store/actions';
+import * as getters from '~/analytics/cycle_analytics/store/getters';
+import httpStatusCodes from '~/lib/utils/http_status';
+import {
+ allowedStages,
+ selectedStage,
+ selectedValueStream,
+ currentGroup,
+ createdAfter,
+ createdBefore,
+ initialPaginationState,
+ reviewEvents,
+} from '../mock_data';
+
+const { id: groupId, path: groupPath } = currentGroup;
+const mockMilestonesPath = 'mock-milestones.json';
+const mockLabelsPath = 'mock-labels.json';
+const mockRequestPath = 'some/cool/path';
+const mockFullPath = '/namespace/-/analytics/value_stream_analytics/value_streams';
+const mockEndpoints = {
+ fullPath: mockFullPath,
+ requestPath: mockRequestPath,
+ labelsPath: mockLabelsPath,
+ milestonesPath: mockMilestonesPath,
+ groupId,
+ groupPath,
+};
+const mockSetDateActionCommit = {
+ payload: { createdAfter, createdBefore },
+ type: 'SET_DATE_RANGE',
+};
+
+const defaultState = {
+ ...getters,
+ selectedValueStream,
+ createdAfter,
+ createdBefore,
+ pagination: initialPaginationState,
+};
+
+describe('Project Value Stream Analytics actions', () => {
+ let state;
+ let mock;
+
+ beforeEach(() => {
+ state = { ...defaultState };
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ state = {};
+ });
+
+ const mutationTypes = (arr) => arr.map(({ type }) => type);
+
+ describe.each`
+ action | payload | expectedActions | expectedMutations
+ ${'setDateRange'} | ${{ createdAfter, createdBefore }} | ${[{ type: 'refetchStageData' }]} | ${[mockSetDateActionCommit]}
+ ${'setFilters'} | ${[]} | ${[{ type: 'refetchStageData' }]} | ${[]}
+ ${'setSelectedStage'} | ${{ selectedStage }} | ${[{ type: 'refetchStageData' }]} | ${[{ type: 'SET_SELECTED_STAGE', payload: { selectedStage } }]}
+ ${'setSelectedValueStream'} | ${{ selectedValueStream }} | ${[{ type: 'fetchValueStreamStages' }]} | ${[{ type: 'SET_SELECTED_VALUE_STREAM', payload: { selectedValueStream } }]}
+ `('$action', ({ action, payload, expectedActions, expectedMutations }) => {
+ const types = mutationTypes(expectedMutations);
+ it(`will dispatch ${expectedActions} and commit ${types}`, () =>
+ testAction({
+ action: actions[action],
+ state,
+ payload,
+ expectedMutations,
+ expectedActions,
+ }));
+ });
+
+ describe('initializeVsa', () => {
+ const selectedAuthor = 'Author';
+ const selectedMilestone = 'Milestone 1';
+ const selectedAssigneeList = ['Assignee 1', 'Assignee 2'];
+ const selectedLabelList = ['Label 1', 'Label 2'];
+ const payload = {
+ endpoints: mockEndpoints,
+ selectedAuthor,
+ selectedMilestone,
+ selectedAssigneeList,
+ selectedLabelList,
+ selectedStage,
+ };
+ const mockFilterEndpoints = {
+ groupEndpoint: 'foo',
+ labelsEndpoint: mockLabelsPath,
+ milestonesEndpoint: mockMilestonesPath,
+ projectEndpoint: '/namespace/-/analytics/value_stream_analytics/value_streams',
+ };
+
+ it('will dispatch fetchValueStreams actions and commit SET_LOADING and INITIALIZE_VSA', () => {
+ return testAction({
+ action: actions.initializeVsa,
+ state: {},
+ payload,
+ expectedMutations: [
+ { type: 'INITIALIZE_VSA', payload },
+ { type: 'SET_LOADING', payload: true },
+ { type: 'SET_LOADING', payload: false },
+ ],
+ expectedActions: [
+ { type: 'filters/setEndpoints', payload: mockFilterEndpoints },
+ {
+ type: 'filters/initialize',
+ payload: { selectedAuthor, selectedMilestone, selectedAssigneeList, selectedLabelList },
+ },
+ { type: 'fetchValueStreams' },
+ { type: 'setInitialStage', payload: selectedStage },
+ ],
+ });
+ });
+ });
+
+ describe('setInitialStage', () => {
+ beforeEach(() => {
+ state = { ...state, stages: allowedStages };
+ });
+
+ describe('with a selected stage', () => {
+ it('will commit `SET_SELECTED_STAGE` and fetchValueStreamStageData actions', () => {
+ const fakeStage = { ...selectedStage, id: 'fake', name: 'fake-stae' };
+ return testAction({
+ action: actions.setInitialStage,
+ state,
+ payload: fakeStage,
+ expectedMutations: [
+ {
+ type: 'SET_SELECTED_STAGE',
+ payload: fakeStage,
+ },
+ ],
+ expectedActions: [{ type: 'fetchValueStreamStageData' }],
+ });
+ });
+ });
+
+ describe('without a selected stage', () => {
+ it('will select the first stage from the value stream', () => {
+ const [firstStage] = allowedStages;
+ testAction({
+ action: actions.setInitialStage,
+ state,
+ payload: null,
+ expectedMutations: [{ type: 'SET_SELECTED_STAGE', payload: firstStage }],
+ expectedActions: [{ type: 'fetchValueStreamStageData' }],
+ });
+ });
+ });
+
+ describe('with no value stream stages available', () => {
+ it('will return SET_NO_ACCESS_ERROR', () => {
+ state = { ...state, stages: [] };
+ testAction({
+ action: actions.setInitialStage,
+ state,
+ payload: null,
+ expectedMutations: [{ type: 'SET_NO_ACCESS_ERROR' }],
+ expectedActions: [],
+ });
+ });
+ });
+ });
+
+ describe('updateStageTablePagination', () => {
+ beforeEach(() => {
+ state = { ...state, selectedStage };
+ });
+
+ it(`will dispatch the "fetchStageData" action and commit the 'SET_PAGINATION' mutation`, () => {
+ return testAction({
+ action: actions.updateStageTablePagination,
+ state,
+ expectedMutations: [{ type: 'SET_PAGINATION' }],
+ expectedActions: [{ type: 'fetchStageData', payload: selectedStage.id }],
+ });
+ });
+ });
+
+ describe('fetchStageData', () => {
+ const mockStagePath = /value_streams\/\w+\/stages\/\w+\/records/;
+ const headers = {
+ 'X-Next-Page': 2,
+ 'X-Page': 1,
+ };
+
+ beforeEach(() => {
+ state = {
+ ...defaultState,
+ endpoints: mockEndpoints,
+ selectedStage,
+ };
+ mock = new MockAdapter(axios);
+ mock.onGet(mockStagePath).reply(httpStatusCodes.OK, reviewEvents, headers);
+ });
+
+ it(`commits the 'RECEIVE_STAGE_DATA_SUCCESS' mutation`, () =>
+ testAction({
+ action: actions.fetchStageData,
+ state,
+ payload: {},
+ expectedMutations: [
+ { type: 'REQUEST_STAGE_DATA' },
+ { type: 'RECEIVE_STAGE_DATA_SUCCESS', payload: reviewEvents },
+ { type: 'SET_PAGINATION', payload: { hasNextPage: true, page: 1 } },
+ ],
+ expectedActions: [],
+ }));
+
+ describe('with a successful request, but an error in the payload', () => {
+ const tooMuchDataError = 'Too much data';
+
+ beforeEach(() => {
+ state = {
+ ...defaultState,
+ endpoints: mockEndpoints,
+ selectedStage,
+ };
+ mock = new MockAdapter(axios);
+ mock.onGet(mockStagePath).reply(httpStatusCodes.OK, { error: tooMuchDataError });
+ });
+
+ it(`commits the 'RECEIVE_STAGE_DATA_ERROR' mutation`, () =>
+ testAction({
+ action: actions.fetchStageData,
+ state,
+ payload: { error: tooMuchDataError },
+ expectedMutations: [
+ { type: 'REQUEST_STAGE_DATA' },
+ { type: 'RECEIVE_STAGE_DATA_ERROR', payload: tooMuchDataError },
+ ],
+ expectedActions: [],
+ }));
+ });
+
+ describe('with a failing request', () => {
+ beforeEach(() => {
+ state = {
+ ...defaultState,
+ endpoints: mockEndpoints,
+ selectedStage,
+ };
+ mock = new MockAdapter(axios);
+ mock.onGet(mockStagePath).reply(httpStatusCodes.BAD_REQUEST);
+ });
+
+ it(`commits the 'RECEIVE_STAGE_DATA_ERROR' mutation`, () =>
+ testAction({
+ action: actions.fetchStageData,
+ state,
+ payload: {},
+ expectedMutations: [{ type: 'REQUEST_STAGE_DATA' }, { type: 'RECEIVE_STAGE_DATA_ERROR' }],
+ expectedActions: [],
+ }));
+ });
+ });
+
+ describe('fetchValueStreams', () => {
+ const mockValueStreamPath = /\/analytics\/value_stream_analytics\/value_streams/;
+
+ beforeEach(() => {
+ state = {
+ endpoints: mockEndpoints,
+ };
+ mock = new MockAdapter(axios);
+ mock.onGet(mockValueStreamPath).reply(httpStatusCodes.OK);
+ });
+
+ it(`commits the 'REQUEST_VALUE_STREAMS' mutation`, () =>
+ testAction({
+ action: actions.fetchValueStreams,
+ state,
+ payload: {},
+ expectedMutations: [{ type: 'REQUEST_VALUE_STREAMS' }],
+ expectedActions: [{ type: 'receiveValueStreamsSuccess' }],
+ }));
+
+ describe('with a failing request', () => {
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ mock.onGet(mockValueStreamPath).reply(httpStatusCodes.BAD_REQUEST);
+ });
+
+ it(`commits the 'RECEIVE_VALUE_STREAMS_ERROR' mutation`, () =>
+ testAction({
+ action: actions.fetchValueStreams,
+ state,
+ payload: {},
+ expectedMutations: [
+ { type: 'REQUEST_VALUE_STREAMS' },
+ { type: 'RECEIVE_VALUE_STREAMS_ERROR', payload: httpStatusCodes.BAD_REQUEST },
+ ],
+ expectedActions: [],
+ }));
+ });
+ });
+
+ describe('receiveValueStreamsSuccess', () => {
+ const mockValueStream = {
+ id: 'mockDefault',
+ name: 'mock default',
+ };
+ const mockValueStreams = [mockValueStream, selectedValueStream];
+ it('with data, will set the first value stream', () => {
+ testAction({
+ action: actions.receiveValueStreamsSuccess,
+ state,
+ payload: mockValueStreams,
+ expectedMutations: [{ type: 'RECEIVE_VALUE_STREAMS_SUCCESS', payload: mockValueStreams }],
+ expectedActions: [{ type: 'setSelectedValueStream', payload: mockValueStream }],
+ });
+ });
+
+ it('without data, will set the default value stream', () => {
+ testAction({
+ action: actions.receiveValueStreamsSuccess,
+ state,
+ payload: [],
+ expectedMutations: [{ type: 'RECEIVE_VALUE_STREAMS_SUCCESS', payload: [] }],
+ expectedActions: [{ type: 'setSelectedValueStream', payload: selectedValueStream }],
+ });
+ });
+ });
+
+ describe('fetchValueStreamStages', () => {
+ const mockValueStreamPath = /\/analytics\/value_stream_analytics\/value_streams/;
+
+ beforeEach(() => {
+ state = {
+ endpoints: mockEndpoints,
+ selectedValueStream,
+ };
+ mock = new MockAdapter(axios);
+ mock.onGet(mockValueStreamPath).reply(httpStatusCodes.OK);
+ });
+
+ it(`commits the 'REQUEST_VALUE_STREAM_STAGES' and 'RECEIVE_VALUE_STREAM_STAGES_SUCCESS' mutations`, () =>
+ testAction({
+ action: actions.fetchValueStreamStages,
+ state,
+ payload: {},
+ expectedMutations: [
+ { type: 'REQUEST_VALUE_STREAM_STAGES' },
+ { type: 'RECEIVE_VALUE_STREAM_STAGES_SUCCESS' },
+ ],
+ expectedActions: [],
+ }));
+
+ describe('with a failing request', () => {
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ mock.onGet(mockValueStreamPath).reply(httpStatusCodes.BAD_REQUEST);
+ });
+
+ it(`commits the 'RECEIVE_VALUE_STREAM_STAGES_ERROR' mutation`, () =>
+ testAction({
+ action: actions.fetchValueStreamStages,
+ state,
+ payload: {},
+ expectedMutations: [
+ { type: 'REQUEST_VALUE_STREAM_STAGES' },
+ { type: 'RECEIVE_VALUE_STREAM_STAGES_ERROR', payload: httpStatusCodes.BAD_REQUEST },
+ ],
+ expectedActions: [],
+ }));
+ });
+ });
+
+ describe('fetchStageMedians', () => {
+ const mockValueStreamPath = /median/;
+
+ const stageMediansPayload = [
+ { id: 'issue', value: null },
+ { id: 'plan', value: null },
+ { id: 'code', value: null },
+ ];
+
+ const stageMedianError = new Error(
+ `Request failed with status code ${httpStatusCodes.BAD_REQUEST}`,
+ );
+
+ beforeEach(() => {
+ state = {
+ fullPath: mockFullPath,
+ selectedValueStream,
+ stages: allowedStages,
+ };
+ mock = new MockAdapter(axios);
+ mock.onGet(mockValueStreamPath).reply(httpStatusCodes.OK);
+ });
+
+ it(`commits the 'REQUEST_STAGE_MEDIANS' and 'RECEIVE_STAGE_MEDIANS_SUCCESS' mutations`, () =>
+ testAction({
+ action: actions.fetchStageMedians,
+ state,
+ payload: {},
+ expectedMutations: [
+ { type: 'REQUEST_STAGE_MEDIANS' },
+ { type: 'RECEIVE_STAGE_MEDIANS_SUCCESS', payload: stageMediansPayload },
+ ],
+ expectedActions: [],
+ }));
+
+ describe('with a failing request', () => {
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ mock.onGet(mockValueStreamPath).reply(httpStatusCodes.BAD_REQUEST);
+ });
+
+ it(`commits the 'RECEIVE_VALUE_STREAM_STAGES_ERROR' mutation`, () =>
+ testAction({
+ action: actions.fetchStageMedians,
+ state,
+ payload: {},
+ expectedMutations: [
+ { type: 'REQUEST_STAGE_MEDIANS' },
+ { type: 'RECEIVE_STAGE_MEDIANS_ERROR', payload: stageMedianError },
+ ],
+ expectedActions: [],
+ }));
+ });
+ });
+
+ describe('fetchStageCountValues', () => {
+ const mockValueStreamPath = /count/;
+ const stageCountsPayload = [
+ { id: 'issue', count: 1 },
+ { id: 'plan', count: 2 },
+ { id: 'code', count: 3 },
+ ];
+
+ const stageCountError = new Error(
+ `Request failed with status code ${httpStatusCodes.BAD_REQUEST}`,
+ );
+
+ beforeEach(() => {
+ state = {
+ fullPath: mockFullPath,
+ selectedValueStream,
+ stages: allowedStages,
+ };
+ mock = new MockAdapter(axios);
+ mock
+ .onGet(mockValueStreamPath)
+ .replyOnce(httpStatusCodes.OK, { count: 1 })
+ .onGet(mockValueStreamPath)
+ .replyOnce(httpStatusCodes.OK, { count: 2 })
+ .onGet(mockValueStreamPath)
+ .replyOnce(httpStatusCodes.OK, { count: 3 });
+ });
+
+ it(`commits the 'REQUEST_STAGE_COUNTS' and 'RECEIVE_STAGE_COUNTS_SUCCESS' mutations`, () =>
+ testAction({
+ action: actions.fetchStageCountValues,
+ state,
+ payload: {},
+ expectedMutations: [
+ { type: 'REQUEST_STAGE_COUNTS' },
+ { type: 'RECEIVE_STAGE_COUNTS_SUCCESS', payload: stageCountsPayload },
+ ],
+ expectedActions: [],
+ }));
+
+ describe('with a failing request', () => {
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ mock.onGet(mockValueStreamPath).reply(httpStatusCodes.BAD_REQUEST);
+ });
+
+ it(`commits the 'RECEIVE_STAGE_COUNTS_ERROR' mutation`, () =>
+ testAction({
+ action: actions.fetchStageCountValues,
+ state,
+ payload: {},
+ expectedMutations: [
+ { type: 'REQUEST_STAGE_COUNTS' },
+ { type: 'RECEIVE_STAGE_COUNTS_ERROR', payload: stageCountError },
+ ],
+ expectedActions: [],
+ }));
+ });
+ });
+
+ describe('refetchStageData', () => {
+ it('will commit SET_LOADING and dispatch fetchValueStreamStageData actions', () =>
+ testAction({
+ action: actions.refetchStageData,
+ state,
+ payload: {},
+ expectedMutations: [
+ { type: 'SET_LOADING', payload: true },
+ { type: 'SET_LOADING', payload: false },
+ ],
+ expectedActions: [{ type: 'fetchValueStreamStageData' }],
+ }));
+ });
+
+ describe('fetchValueStreamStageData', () => {
+ it('will dispatch the fetchStageData, fetchStageMedians and fetchStageCountValues actions', () =>
+ testAction({
+ action: actions.fetchValueStreamStageData,
+ state,
+ payload: {},
+ expectedMutations: [],
+ expectedActions: [
+ { type: 'fetchStageData' },
+ { type: 'fetchStageMedians' },
+ { type: 'fetchStageCountValues' },
+ ],
+ }));
+ });
+});
diff --git a/spec/frontend/analytics/cycle_analytics/store/getters_spec.js b/spec/frontend/analytics/cycle_analytics/store/getters_spec.js
new file mode 100644
index 00000000000..8ad1e1b27de
--- /dev/null
+++ b/spec/frontend/analytics/cycle_analytics/store/getters_spec.js
@@ -0,0 +1,42 @@
+import * as getters from '~/analytics/cycle_analytics/store/getters';
+
+import {
+ allowedStages,
+ stageMedians,
+ transformedProjectStagePathData,
+ selectedStage,
+ stageCounts,
+ basePaginationResult,
+ initialPaginationState,
+} from '../mock_data';
+
+describe('Value stream analytics getters', () => {
+ let state = {};
+
+ describe('pathNavigationData', () => {
+ it('returns the transformed data', () => {
+ state = { stages: allowedStages, medians: stageMedians, selectedStage, stageCounts };
+ expect(getters.pathNavigationData(state)).toEqual(transformedProjectStagePathData);
+ });
+ });
+
+ describe('paginationParams', () => {
+ beforeEach(() => {
+ state = { pagination: initialPaginationState };
+ });
+
+ it('returns the `pagination` type', () => {
+ expect(getters.paginationParams(state)).toEqual(basePaginationResult);
+ });
+
+ it('returns the `sort` type', () => {
+ expect(getters.paginationParams(state)).toEqual(basePaginationResult);
+ });
+
+ it('with page=10, sets the `page` property', () => {
+ const page = 10;
+ state = { pagination: { ...initialPaginationState, page } };
+ expect(getters.paginationParams(state)).toEqual({ ...basePaginationResult, page });
+ });
+ });
+});
diff --git a/spec/frontend/analytics/cycle_analytics/store/mutations_spec.js b/spec/frontend/analytics/cycle_analytics/store/mutations_spec.js
new file mode 100644
index 00000000000..567fac81e1f
--- /dev/null
+++ b/spec/frontend/analytics/cycle_analytics/store/mutations_spec.js
@@ -0,0 +1,132 @@
+import { useFakeDate } from 'helpers/fake_date';
+import * as types from '~/analytics/cycle_analytics/store/mutation_types';
+import mutations from '~/analytics/cycle_analytics/store/mutations';
+import {
+ PAGINATION_SORT_FIELD_END_EVENT,
+ PAGINATION_SORT_DIRECTION_DESC,
+} from '~/analytics/cycle_analytics/constants';
+import {
+ selectedStage,
+ rawIssueEvents,
+ issueEvents,
+ selectedValueStream,
+ rawValueStreamStages,
+ valueStreamStages,
+ rawStageMedians,
+ formattedStageMedians,
+ rawStageCounts,
+ stageCounts,
+ initialPaginationState as pagination,
+} from '../mock_data';
+
+let state;
+const rawEvents = rawIssueEvents.events;
+const convertedEvents = issueEvents.events;
+const mockRequestPath = 'fake/request/path';
+const mockCreatedAfter = '2020-06-18';
+const mockCreatedBefore = '2020-07-18';
+
+describe('Project Value Stream Analytics mutations', () => {
+ useFakeDate(2020, 6, 18);
+
+ beforeEach(() => {
+ state = { pagination };
+ });
+
+ afterEach(() => {
+ state = null;
+ });
+
+ it.each`
+ mutation | stateKey | value
+ ${types.REQUEST_VALUE_STREAMS} | ${'valueStreams'} | ${[]}
+ ${types.RECEIVE_VALUE_STREAMS_ERROR} | ${'valueStreams'} | ${[]}
+ ${types.REQUEST_VALUE_STREAM_STAGES} | ${'stages'} | ${[]}
+ ${types.RECEIVE_VALUE_STREAM_STAGES_ERROR} | ${'stages'} | ${[]}
+ ${types.REQUEST_STAGE_DATA} | ${'isLoadingStage'} | ${true}
+ ${types.REQUEST_STAGE_DATA} | ${'isEmptyStage'} | ${false}
+ ${types.REQUEST_STAGE_DATA} | ${'selectedStageEvents'} | ${[]}
+ ${types.RECEIVE_STAGE_DATA_SUCCESS} | ${'isLoadingStage'} | ${false}
+ ${types.RECEIVE_STAGE_DATA_SUCCESS} | ${'selectedStageEvents'} | ${[]}
+ ${types.RECEIVE_STAGE_DATA_ERROR} | ${'isLoadingStage'} | ${false}
+ ${types.RECEIVE_STAGE_DATA_ERROR} | ${'selectedStageEvents'} | ${[]}
+ ${types.RECEIVE_STAGE_DATA_ERROR} | ${'isEmptyStage'} | ${true}
+ ${types.REQUEST_STAGE_MEDIANS} | ${'medians'} | ${{}}
+ ${types.RECEIVE_STAGE_MEDIANS_ERROR} | ${'medians'} | ${{}}
+ ${types.REQUEST_STAGE_COUNTS} | ${'stageCounts'} | ${{}}
+ ${types.RECEIVE_STAGE_COUNTS_ERROR} | ${'stageCounts'} | ${{}}
+ ${types.SET_NO_ACCESS_ERROR} | ${'hasNoAccessError'} | ${true}
+ `('$mutation will set $stateKey to $value', ({ mutation, stateKey, value }) => {
+ mutations[mutation](state);
+
+ expect(state).toMatchObject({ [stateKey]: value });
+ });
+
+ const mockSetDatePayload = { createdAfter: mockCreatedAfter, createdBefore: mockCreatedBefore };
+ const mockInitialPayload = {
+ endpoints: { requestPath: mockRequestPath },
+ currentGroup: { title: 'cool-group' },
+ id: 1337,
+ ...mockSetDatePayload,
+ };
+ const mockInitializedObj = {
+ endpoints: { requestPath: mockRequestPath },
+ ...mockSetDatePayload,
+ };
+
+ it.each`
+ mutation | stateKey | value
+ ${types.INITIALIZE_VSA} | ${'endpoints'} | ${{ requestPath: mockRequestPath }}
+ ${types.INITIALIZE_VSA} | ${'createdAfter'} | ${mockCreatedAfter}
+ ${types.INITIALIZE_VSA} | ${'createdBefore'} | ${mockCreatedBefore}
+ `('$mutation will set $stateKey', ({ mutation, stateKey, value }) => {
+ mutations[mutation](state, { ...mockInitialPayload });
+
+ expect(state).toMatchObject({ ...mockInitializedObj, [stateKey]: value });
+ });
+
+ it.each`
+ mutation | payload | stateKey | value
+ ${types.SET_DATE_RANGE} | ${mockSetDatePayload} | ${'createdAfter'} | ${mockCreatedAfter}
+ ${types.SET_DATE_RANGE} | ${mockSetDatePayload} | ${'createdBefore'} | ${mockCreatedBefore}
+ ${types.SET_LOADING} | ${true} | ${'isLoading'} | ${true}
+ ${types.SET_LOADING} | ${false} | ${'isLoading'} | ${false}
+ ${types.SET_SELECTED_VALUE_STREAM} | ${selectedValueStream} | ${'selectedValueStream'} | ${selectedValueStream}
+ ${types.SET_PAGINATION} | ${pagination} | ${'pagination'} | ${{ ...pagination, sort: PAGINATION_SORT_FIELD_END_EVENT, direction: PAGINATION_SORT_DIRECTION_DESC }}
+ ${types.SET_PAGINATION} | ${{ ...pagination, sort: 'duration', direction: 'asc' }} | ${'pagination'} | ${{ ...pagination, sort: 'duration', direction: 'asc' }}
+ ${types.SET_SELECTED_STAGE} | ${selectedStage} | ${'selectedStage'} | ${selectedStage}
+ ${types.RECEIVE_VALUE_STREAMS_SUCCESS} | ${[selectedValueStream]} | ${'valueStreams'} | ${[selectedValueStream]}
+ ${types.RECEIVE_VALUE_STREAM_STAGES_SUCCESS} | ${{ stages: rawValueStreamStages }} | ${'stages'} | ${valueStreamStages}
+ ${types.RECEIVE_STAGE_MEDIANS_SUCCESS} | ${rawStageMedians} | ${'medians'} | ${formattedStageMedians}
+ ${types.RECEIVE_STAGE_COUNTS_SUCCESS} | ${rawStageCounts} | ${'stageCounts'} | ${stageCounts}
+ `(
+ '$mutation with $payload will set $stateKey to $value',
+ ({ mutation, payload, stateKey, value }) => {
+ mutations[mutation](state, payload);
+
+ expect(state).toMatchObject({ [stateKey]: value });
+ },
+ );
+
+ describe('with a stage selected', () => {
+ beforeEach(() => {
+ state = {
+ selectedStage,
+ };
+ });
+
+ it.each`
+ mutation | payload | stateKey | value
+ ${types.RECEIVE_STAGE_DATA_SUCCESS} | ${[]} | ${'isEmptyStage'} | ${true}
+ ${types.RECEIVE_STAGE_DATA_SUCCESS} | ${rawEvents} | ${'selectedStageEvents'} | ${convertedEvents}
+ ${types.RECEIVE_STAGE_DATA_SUCCESS} | ${rawEvents} | ${'isEmptyStage'} | ${false}
+ `(
+ '$mutation with $payload will set $stateKey to $value',
+ ({ mutation, payload, stateKey, value }) => {
+ mutations[mutation](state, payload);
+
+ expect(state).toMatchObject({ [stateKey]: value });
+ },
+ );
+ });
+});
diff --git a/spec/frontend/analytics/cycle_analytics/total_time_spec.js b/spec/frontend/analytics/cycle_analytics/total_time_spec.js
new file mode 100644
index 00000000000..47ee7aad8c4
--- /dev/null
+++ b/spec/frontend/analytics/cycle_analytics/total_time_spec.js
@@ -0,0 +1,45 @@
+import { mount } from '@vue/test-utils';
+import TotalTime from '~/analytics/cycle_analytics/components/total_time.vue';
+
+describe('TotalTime', () => {
+ let wrapper = null;
+
+ const createComponent = (propsData) => {
+ return mount(TotalTime, {
+ propsData,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('with a valid time object', () => {
+ it.each`
+ time
+ ${{ seconds: 35 }}
+ ${{ mins: 47, seconds: 3 }}
+ ${{ days: 3, mins: 47, seconds: 3 }}
+ ${{ hours: 23, mins: 10 }}
+ ${{ hours: 7, mins: 20, seconds: 10 }}
+ `('with $time', ({ time }) => {
+ wrapper = createComponent({
+ time,
+ });
+
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+ });
+
+ describe('with a blank object', () => {
+ beforeEach(() => {
+ wrapper = createComponent({
+ time: {},
+ });
+ });
+
+ it('should render --', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+ });
+});
diff --git a/spec/frontend/analytics/cycle_analytics/utils_spec.js b/spec/frontend/analytics/cycle_analytics/utils_spec.js
new file mode 100644
index 00000000000..fe412bf7498
--- /dev/null
+++ b/spec/frontend/analytics/cycle_analytics/utils_spec.js
@@ -0,0 +1,171 @@
+import {
+ transformStagesForPathNavigation,
+ medianTimeToParsedSeconds,
+ formatMedianValues,
+ filterStagesByHiddenStatus,
+ buildCycleAnalyticsInitialData,
+} from '~/analytics/cycle_analytics/utils';
+import {
+ selectedStage,
+ allowedStages,
+ stageMedians,
+ pathNavIssueMetric,
+ rawStageMedians,
+} from './mock_data';
+
+describe('Value stream analytics utils', () => {
+ describe('transformStagesForPathNavigation', () => {
+ const stages = allowedStages;
+ const response = transformStagesForPathNavigation({
+ stages,
+ medians: stageMedians,
+ selectedStage,
+ });
+
+ describe('transforms the data as expected', () => {
+ it('returns an array of stages', () => {
+ expect(Array.isArray(response)).toBe(true);
+ expect(response.length).toBe(stages.length);
+ });
+
+ it('selects the correct stage', () => {
+ const selected = response.filter((stage) => stage.selected === true)[0];
+
+ expect(selected.title).toBe(selectedStage.title);
+ });
+
+ it('includes the correct metric for the associated stage', () => {
+ const issue = response.filter((stage) => stage.name === 'issue')[0];
+
+ expect(issue.metric).toBe(pathNavIssueMetric);
+ });
+ });
+ });
+
+ describe('medianTimeToParsedSeconds', () => {
+ it.each`
+ value | result
+ ${1036800} | ${'1w'}
+ ${259200} | ${'3d'}
+ ${172800} | ${'2d'}
+ ${86400} | ${'1d'}
+ ${1000} | ${'16m'}
+ ${61} | ${'1m'}
+ ${59} | ${'<1m'}
+ ${0} | ${'-'}
+ `('will correctly parse $value seconds into $result', ({ value, result }) => {
+ expect(medianTimeToParsedSeconds(value)).toBe(result);
+ });
+ });
+
+ describe('formatMedianValues', () => {
+ const calculatedMedians = formatMedianValues(rawStageMedians);
+
+ it('returns an object with each stage and their median formatted for display', () => {
+ rawStageMedians.forEach(({ id, value }) => {
+ expect(calculatedMedians).toMatchObject({ [id]: medianTimeToParsedSeconds(value) });
+ });
+ });
+ });
+
+ describe('filterStagesByHiddenStatus', () => {
+ const hiddenStages = [{ title: 'three', hidden: true }];
+ const visibleStages = [
+ { title: 'one', hidden: false },
+ { title: 'two', hidden: false },
+ ];
+ const mockStages = [...visibleStages, ...hiddenStages];
+
+ it.each`
+ isHidden | result
+ ${false} | ${visibleStages}
+ ${undefined} | ${hiddenStages}
+ ${true} | ${hiddenStages}
+ `('with isHidden=$isHidden returns matching stages', ({ isHidden, result }) => {
+ expect(filterStagesByHiddenStatus(mockStages, isHidden)).toEqual(result);
+ });
+ });
+
+ describe('buildCycleAnalyticsInitialData', () => {
+ let res = null;
+ const projectId = '5';
+ const createdAfter = '2021-09-01';
+ const createdBefore = '2021-11-06';
+ const groupId = '146';
+ const groupPath = 'fake-group';
+ const fullPath = 'fake-group/fake-project';
+ const labelsPath = '/fake-group/fake-project/-/labels.json';
+ const milestonesPath = '/fake-group/fake-project/-/milestones.json';
+ const requestPath = '/fake-group/fake-project/-/value_stream_analytics';
+
+ const rawData = {
+ projectId,
+ createdBefore,
+ createdAfter,
+ fullPath,
+ requestPath,
+ labelsPath,
+ milestonesPath,
+ groupId,
+ groupPath,
+ };
+
+ describe('with minimal data', () => {
+ beforeEach(() => {
+ res = buildCycleAnalyticsInitialData(rawData);
+ });
+
+ it('sets the projectId', () => {
+ expect(res.projectId).toBe(parseInt(projectId, 10));
+ });
+
+ it('sets the date range', () => {
+ expect(res.createdBefore).toEqual(new Date(createdBefore));
+ expect(res.createdAfter).toEqual(new Date(createdAfter));
+ });
+
+ it('sets the endpoints', () => {
+ const { endpoints } = res;
+ expect(endpoints.fullPath).toBe(fullPath);
+ expect(endpoints.requestPath).toBe(requestPath);
+ expect(endpoints.labelsPath).toBe(labelsPath);
+ expect(endpoints.milestonesPath).toBe(milestonesPath);
+ expect(endpoints.groupId).toBe(parseInt(groupId, 10));
+ expect(endpoints.groupPath).toBe(groupPath);
+ });
+
+ it('returns null when there is no stage', () => {
+ expect(res.selectedStage).toBeNull();
+ });
+
+ it('returns false for missing features', () => {
+ expect(res.features.cycleAnalyticsForGroups).toBe(false);
+ });
+ });
+
+ describe('with a stage set', () => {
+ const jsonStage = '{"id":"fakeStage","title":"fakeStage"}';
+
+ it('parses the selectedStage data', () => {
+ res = buildCycleAnalyticsInitialData({ ...rawData, stage: jsonStage });
+
+ const { selectedStage: stage } = res;
+
+ expect(stage.id).toBe('fakeStage');
+ expect(stage.title).toBe('fakeStage');
+ });
+ });
+
+ describe('with features set', () => {
+ const fakeFeatures = { cycleAnalyticsForGroups: true };
+
+ it('sets the feature flags', () => {
+ res = buildCycleAnalyticsInitialData({
+ ...rawData,
+ gon: { licensed_features: fakeFeatures },
+ });
+ expect(res.features).toEqual(fakeFeatures);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/analytics/cycle_analytics/value_stream_filters_spec.js b/spec/frontend/analytics/cycle_analytics/value_stream_filters_spec.js
new file mode 100644
index 00000000000..4f333e95d89
--- /dev/null
+++ b/spec/frontend/analytics/cycle_analytics/value_stream_filters_spec.js
@@ -0,0 +1,91 @@
+import { shallowMount } from '@vue/test-utils';
+import Daterange from '~/analytics/shared/components/daterange.vue';
+import ProjectsDropdownFilter from '~/analytics/shared/components/projects_dropdown_filter.vue';
+import FilterBar from '~/analytics/cycle_analytics/components/filter_bar.vue';
+import ValueStreamFilters from '~/analytics/cycle_analytics/components/value_stream_filters.vue';
+import {
+ createdAfter as startDate,
+ createdBefore as endDate,
+ currentGroup,
+ selectedProjects,
+} from './mock_data';
+
+function createComponent(props = {}) {
+ return shallowMount(ValueStreamFilters, {
+ propsData: {
+ selectedProjects,
+ groupId: currentGroup.id,
+ groupPath: currentGroup.fullPath,
+ startDate,
+ endDate,
+ ...props,
+ },
+ });
+}
+
+describe('ValueStreamFilters', () => {
+ let wrapper;
+
+ const findProjectsDropdown = () => wrapper.findComponent(ProjectsDropdownFilter);
+ const findDateRangePicker = () => wrapper.findComponent(Daterange);
+ const findFilterBar = () => wrapper.findComponent(FilterBar);
+
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('will render the filter bar', () => {
+ expect(findFilterBar().exists()).toBe(true);
+ });
+
+ it('will render the projects dropdown', () => {
+ expect(findProjectsDropdown().exists()).toBe(true);
+ expect(wrapper.findComponent(ProjectsDropdownFilter).props()).toEqual(
+ expect.objectContaining({
+ queryParams: wrapper.vm.projectsQueryParams,
+ multiSelect: wrapper.vm.$options.multiProjectSelect,
+ }),
+ );
+ });
+
+ it('will render the date range picker', () => {
+ expect(findDateRangePicker().exists()).toBe(true);
+ });
+
+ it('will emit `selectProject` when a project is selected', () => {
+ findProjectsDropdown().vm.$emit('selected');
+
+ expect(wrapper.emitted('selectProject')).not.toBeUndefined();
+ });
+
+ it('will emit `setDateRange` when the date range changes', () => {
+ findDateRangePicker().vm.$emit('change');
+
+ expect(wrapper.emitted('setDateRange')).not.toBeUndefined();
+ });
+
+ describe('hasDateRangeFilter = false', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ hasDateRangeFilter: false });
+ });
+
+ it('will not render the date range picker', () => {
+ expect(findDateRangePicker().exists()).toBe(false);
+ });
+ });
+
+ describe('hasProjectFilter = false', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ hasProjectFilter: false });
+ });
+
+ it('will not render the project dropdown', () => {
+ expect(findProjectsDropdown().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/cycle_analytics/value_stream_metrics_spec.js b/spec/frontend/analytics/cycle_analytics/value_stream_metrics_spec.js
index 948dc5c9be2..948dc5c9be2 100644
--- a/spec/frontend/cycle_analytics/value_stream_metrics_spec.js
+++ b/spec/frontend/analytics/cycle_analytics/value_stream_metrics_spec.js
diff --git a/spec/frontend/api_spec.js b/spec/frontend/api_spec.js
index 1f92010b771..5209d9c2d2c 100644
--- a/spec/frontend/api_spec.js
+++ b/spec/frontend/api_spec.js
@@ -1,7 +1,11 @@
import MockAdapter from 'axios-mock-adapter';
import Api, { DEFAULT_PER_PAGE } from '~/api';
import axios from '~/lib/utils/axios_utils';
-import httpStatus from '~/lib/utils/http_status';
+import httpStatus, {
+ HTTP_STATUS_ACCEPTED,
+ HTTP_STATUS_CREATED,
+ HTTP_STATUS_NO_CONTENT,
+} from '~/lib/utils/http_status';
jest.mock('~/flash');
@@ -1069,7 +1073,7 @@ describe('Api', () => {
describe('when the release is successfully created', () => {
it('resolves the Promise', () => {
- mock.onPost(expectedUrl, release).replyOnce(httpStatus.CREATED);
+ mock.onPost(expectedUrl, release).replyOnce(HTTP_STATUS_CREATED);
return Api.createRelease(dummyProjectPath, release).then(() => {
expect(mock.history.post).toHaveLength(1);
@@ -1125,7 +1129,7 @@ describe('Api', () => {
describe('when the Release is successfully created', () => {
it('resolves the Promise', () => {
- mock.onPost(expectedUrl, expectedLink).replyOnce(httpStatus.CREATED);
+ mock.onPost(expectedUrl, expectedLink).replyOnce(HTTP_STATUS_CREATED);
return Api.createReleaseLink(dummyProjectPath, dummyTagName, expectedLink).then(() => {
expect(mock.history.post).toHaveLength(1);
@@ -1224,7 +1228,7 @@ describe('Api', () => {
describe('when the merge request is successfully created', () => {
it('resolves the Promise', () => {
- mock.onPost(expectedUrl, options).replyOnce(httpStatus.CREATED);
+ mock.onPost(expectedUrl, options).replyOnce(HTTP_STATUS_CREATED);
return Api.createProjectMergeRequest(dummyProjectPath, options).then(() => {
expect(mock.history.post).toHaveLength(1);
@@ -1332,7 +1336,7 @@ describe('Api', () => {
describe('when the freeze period is successfully created', () => {
it('resolves the Promise', () => {
- mock.onPost(expectedUrl, options).replyOnce(httpStatus.CREATED, expectedResult);
+ mock.onPost(expectedUrl, options).replyOnce(HTTP_STATUS_CREATED, expectedResult);
return Api.createFreezePeriod(projectId, options).then(({ data }) => {
expect(data).toStrictEqual(expectedResult);
@@ -1598,7 +1602,7 @@ describe('Api', () => {
const secureFileId = 2;
const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/${projectId}/secure_files/${secureFileId}`;
- mock.onDelete(expectedUrl).reply(httpStatus.NO_CONTENT, '');
+ mock.onDelete(expectedUrl).reply(HTTP_STATUS_NO_CONTENT, '');
const { data } = await Api.deleteProjectSecureFile(projectId, secureFileId);
expect(data).toEqual('');
});
@@ -1609,10 +1613,10 @@ describe('Api', () => {
const groupId = 1;
const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/groups/${groupId}/dependency_proxy/cache`;
- mock.onDelete(expectedUrl).reply(httpStatus.ACCEPTED);
+ mock.onDelete(expectedUrl).reply(HTTP_STATUS_ACCEPTED);
const { status } = await Api.deleteDependencyProxyCacheList(groupId, {});
- expect(status).toBe(httpStatus.ACCEPTED);
+ expect(status).toBe(HTTP_STATUS_ACCEPTED);
});
});
diff --git a/spec/frontend/batch_comments/components/draft_note_spec.js b/spec/frontend/batch_comments/components/draft_note_spec.js
index 03ecbc01a56..2dfcdd551a1 100644
--- a/spec/frontend/batch_comments/components/draft_note_spec.js
+++ b/spec/frontend/batch_comments/components/draft_note_spec.js
@@ -1,19 +1,20 @@
import { nextTick } from 'vue';
import { GlButton, GlBadge } from '@gitlab/ui';
-import { getByRole } from '@testing-library/dom';
import { shallowMount } from '@vue/test-utils';
import { stubComponent } from 'helpers/stub_component';
import DraftNote from '~/batch_comments/components/draft_note.vue';
import PublishButton from '~/batch_comments/components/publish_button.vue';
import { createStore } from '~/batch_comments/stores';
import NoteableNote from '~/notes/components/noteable_note.vue';
-import '~/behaviors/markdown/render_gfm';
import { createDraft } from '../mock_data';
+jest.mock('~/behaviors/markdown/render_gfm');
+
const NoteableNoteStub = stubComponent(NoteableNote, {
template: `
<div>
<slot name="note-header-info">Test</slot>
+ <slot name="after-note-body">Test</slot>
</div>
`,
});
@@ -29,7 +30,6 @@ describe('Batch comments draft note component', () => {
},
};
- const getList = () => getByRole(wrapper.element, 'list');
const findSubmitReviewButton = () => wrapper.findComponent(PublishButton);
const findAddCommentButton = () => wrapper.findComponent(GlButton);
@@ -189,7 +189,7 @@ describe('Batch comments draft note component', () => {
});
it(`calls store ${expectedCalls.length} times on ${event}`, () => {
- getList().dispatchEvent(new MouseEvent(event, { bubbles: true }));
+ wrapper.element.dispatchEvent(new MouseEvent(event, { bubbles: true }));
expect(store.dispatch.mock.calls).toEqual(expectedCalls);
});
});
diff --git a/spec/frontend/batch_comments/components/preview_item_spec.js b/spec/frontend/batch_comments/components/preview_item_spec.js
index 6a104f0c787..6a99294f855 100644
--- a/spec/frontend/batch_comments/components/preview_item_spec.js
+++ b/spec/frontend/batch_comments/components/preview_item_spec.js
@@ -3,9 +3,10 @@ import PreviewItem from '~/batch_comments/components/preview_item.vue';
import { createStore } from '~/batch_comments/stores';
import diffsModule from '~/diffs/store/modules';
import notesModule from '~/notes/stores/modules';
-import '~/behaviors/markdown/render_gfm';
import { createDraft } from '../mock_data';
+jest.mock('~/behaviors/markdown/render_gfm');
+
describe('Batch comments draft preview item component', () => {
let wrapper;
let draft;
diff --git a/spec/frontend/batch_comments/components/publish_dropdown_spec.js b/spec/frontend/batch_comments/components/publish_dropdown_spec.js
index d1b7160d231..e89934c0192 100644
--- a/spec/frontend/batch_comments/components/publish_dropdown_spec.js
+++ b/spec/frontend/batch_comments/components/publish_dropdown_spec.js
@@ -4,9 +4,10 @@ import Vue from 'vue';
import Vuex from 'vuex';
import PreviewDropdown from '~/batch_comments/components/preview_dropdown.vue';
import { createStore } from '~/mr_notes/stores';
-import '~/behaviors/markdown/render_gfm';
import { createDraft } from '../mock_data';
+jest.mock('~/behaviors/markdown/render_gfm');
+
Vue.use(Vuex);
describe('Batch comments publish dropdown component', () => {
diff --git a/spec/frontend/behaviors/markdown/render_observability_spec.js b/spec/frontend/behaviors/markdown/render_observability_spec.js
new file mode 100644
index 00000000000..c87d11742dc
--- /dev/null
+++ b/spec/frontend/behaviors/markdown/render_observability_spec.js
@@ -0,0 +1,38 @@
+import renderObservability from '~/behaviors/markdown/render_observability';
+import * as ColorUtils from '~/lib/utils/color_utils';
+
+describe('Observability iframe renderer', () => {
+ const findObservabilityIframes = (theme = 'light') =>
+ document.querySelectorAll(`iframe[src="https://observe.gitlab.com/?theme=${theme}&kiosk"]`);
+
+ const renderEmbeddedObservability = () => {
+ renderObservability([...document.querySelectorAll('.js-render-observability')]);
+ jest.runAllTimers();
+ };
+
+ beforeEach(() => {
+ document.body.dataset.page = '';
+ document.body.innerHTML = '';
+ });
+
+ it('renders an observability iframe', () => {
+ document.body.innerHTML = `<div class="js-render-observability" data-frame-url="https://observe.gitlab.com/"></div>`;
+
+ expect(findObservabilityIframes()).toHaveLength(0);
+
+ renderEmbeddedObservability();
+
+ expect(findObservabilityIframes()).toHaveLength(1);
+ });
+
+ it('renders iframe with dark param when GL has dark theme', () => {
+ document.body.innerHTML = `<div class="js-render-observability" data-frame-url="https://observe.gitlab.com/"></div>`;
+ jest.spyOn(ColorUtils, 'darkModeEnabled').mockImplementation(() => true);
+
+ expect(findObservabilityIframes('dark')).toHaveLength(0);
+
+ renderEmbeddedObservability();
+
+ expect(findObservabilityIframes('dark')).toHaveLength(1);
+ });
+});
diff --git a/spec/frontend/blob/openapi/index_spec.js b/spec/frontend/blob/openapi/index_spec.js
index 17e718df495..d9d65258516 100644
--- a/spec/frontend/blob/openapi/index_spec.js
+++ b/spec/frontend/blob/openapi/index_spec.js
@@ -21,7 +21,7 @@ describe('OpenAPI blob viewer', () => {
it('initializes SwaggerUI with the correct configuration', () => {
expect(document.body.innerHTML).toContain(
- '<iframe src="/-/sandbox/swagger" sandbox="allow-scripts allow-popups" frameborder="0" width="100%" height="1000"></iframe>',
+ '<iframe src="/-/sandbox/swagger" sandbox="allow-scripts allow-popups allow-forms" frameborder="0" width="100%" height="1000"></iframe>',
);
});
});
diff --git a/spec/frontend/blob_edit/blob_bundle_spec.js b/spec/frontend/blob_edit/blob_bundle_spec.js
index 644539308c2..ed42322b0e6 100644
--- a/spec/frontend/blob_edit/blob_bundle_spec.js
+++ b/spec/frontend/blob_edit/blob_bundle_spec.js
@@ -5,8 +5,10 @@ import waitForPromises from 'helpers/wait_for_promises';
import blobBundle from '~/blob_edit/blob_bundle';
import SourceEditor from '~/blob_edit/edit_blob';
+import { createAlert } from '~/flash';
jest.mock('~/blob_edit/edit_blob');
+jest.mock('~/flash');
describe('BlobBundle', () => {
it('does not load SourceEditor by default', () => {
@@ -93,4 +95,26 @@ describe('BlobBundle', () => {
});
});
});
+
+ describe('Error handling', () => {
+ let message;
+ beforeEach(() => {
+ setHTMLFixture(`<div class="js-edit-blob-form" data-blob-filename="blah"></div>`);
+ message = 'Foo';
+ SourceEditor.mockImplementation(() => {
+ throw new Error(message);
+ });
+ });
+
+ afterEach(() => {
+ resetHTMLFixture();
+ SourceEditor.mockClear();
+ });
+
+ it('correctly outputs error message when it occurs', async () => {
+ blobBundle();
+ await waitForPromises();
+ expect(createAlert).toHaveBeenCalledWith({ message });
+ });
+ });
});
diff --git a/spec/frontend/boards/board_list_spec.js b/spec/frontend/boards/board_list_spec.js
index 3a2beb714e9..34c0504143c 100644
--- a/spec/frontend/boards/board_list_spec.js
+++ b/spec/frontend/boards/board_list_spec.js
@@ -198,6 +198,13 @@ describe('Board list component', () => {
expect(findDraggable().exists()).toBe(true);
});
+ it('sets delay and delayOnTouchOnly attributes on board list', () => {
+ const listEl = wrapper.findComponent({ ref: 'list' });
+
+ expect(listEl.attributes('delay')).toBe('100');
+ expect(listEl.attributes('delayontouchonly')).toBe('true');
+ });
+
describe('handleDragOnStart', () => {
it('adds a class `is-dragging` to document body', () => {
expect(document.body.classList.contains('is-dragging')).toBe(false);
@@ -269,6 +276,10 @@ describe('Board list component', () => {
it('Draggable is not used', () => {
expect(findDraggable().exists()).toBe(false);
});
+
+ it('Board card move to position is not visible', () => {
+ expect(findMoveToPositionComponent().exists()).toBe(false);
+ });
});
});
});
diff --git a/spec/frontend/boards/components/board_content_sidebar_spec.js b/spec/frontend/boards/components/board_content_sidebar_spec.js
index 7e35c39cd48..0d5b1d16e30 100644
--- a/spec/frontend/boards/components/board_content_sidebar_spec.js
+++ b/spec/frontend/boards/components/board_content_sidebar_spec.js
@@ -12,7 +12,7 @@ import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue
import SidebarSeverity from '~/sidebar/components/severity/sidebar_severity.vue';
import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue';
import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
-import SidebarLabelsWidget from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue';
+import SidebarLabelsWidget from '~/sidebar/components/labels/labels_select_widget/labels_select_root.vue';
import { mockActiveIssue, mockIssue, mockIssueGroupPath, mockIssueProjectPath } from '../mock_data';
Vue.use(Vuex);
@@ -146,6 +146,20 @@ describe('BoardContentSidebar', () => {
expect(wrapper.findComponent(SidebarSeverity).exists()).toBe(false);
});
+ it('does not render SidebarHealthStatusWidget', async () => {
+ const SidebarHealthStatusWidget = (
+ await import('ee_component/sidebar/components/health_status/sidebar_health_status_widget.vue')
+ ).default;
+ expect(wrapper.findComponent(SidebarHealthStatusWidget).exists()).toBe(false);
+ });
+
+ it('does not render SidebarWeightWidget', async () => {
+ const SidebarWeightWidget = (
+ await import('ee_component/sidebar/components/weight/sidebar_weight_widget.vue')
+ ).default;
+ expect(wrapper.findComponent(SidebarWeightWidget).exists()).toBe(false);
+ });
+
describe('when we emit close', () => {
let toggleBoardItem;
diff --git a/spec/frontend/boards/components/board_content_spec.js b/spec/frontend/boards/components/board_content_spec.js
index b2138700602..82e7ab48e7d 100644
--- a/spec/frontend/boards/components/board_content_spec.js
+++ b/spec/frontend/boards/components/board_content_spec.js
@@ -123,15 +123,39 @@ describe('BoardContent', () => {
expect(wrapper.findComponent(GlAlert).exists()).toBe(false);
});
- it('resizes the list on resize', async () => {
+ it('on small screens, sets board container height to full height', async () => {
window.innerHeight = 1000;
+ window.innerWidth = 767;
jest.spyOn(Element.prototype, 'getBoundingClientRect').mockReturnValue({ top: 100 });
wrapper.vm.resizeObserver.trigger();
await nextTick();
- expect(wrapper.findComponent({ ref: 'list' }).attributes('style')).toBe('height: 900px;');
+ const style = wrapper.findComponent({ ref: 'list' }).attributes('style');
+
+ expect(style).toBe('height: 1000px;');
+ });
+
+ it('on large screens, sets board container height fill area below filters', async () => {
+ window.innerHeight = 1000;
+ window.innerWidth = 768;
+ jest.spyOn(Element.prototype, 'getBoundingClientRect').mockReturnValue({ top: 100 });
+
+ wrapper.vm.resizeObserver.trigger();
+
+ await nextTick();
+
+ const style = wrapper.findComponent({ ref: 'list' }).attributes('style');
+
+ expect(style).toBe('height: 900px;');
+ });
+
+ it('sets delay and delayOnTouchOnly attributes on board list', () => {
+ const listEl = wrapper.findComponent({ ref: 'list' });
+
+ expect(listEl.attributes('delay')).toBe('100');
+ expect(listEl.attributes('delayontouchonly')).toBe('true');
});
});
diff --git a/spec/frontend/boards/components/board_filtered_search_spec.js b/spec/frontend/boards/components/board_filtered_search_spec.js
index 6f17e4193a3..e80c66f7fb8 100644
--- a/spec/frontend/boards/components/board_filtered_search_spec.js
+++ b/spec/frontend/boards/components/board_filtered_search_spec.js
@@ -17,7 +17,7 @@ import {
TOKEN_TYPE_WEIGHT,
} from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchBarRoot from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
-import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
+import UserToken from '~/vue_shared/components/filtered_search_bar/tokens/user_token.vue';
import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
import { createStore } from '~/boards/stores';
@@ -30,7 +30,7 @@ describe('BoardFilteredSearch', () => {
{
icon: 'labels',
title: TOKEN_TITLE_LABEL,
- type: 'label',
+ type: TOKEN_TYPE_LABEL,
operators: [
{ value: '=', description: 'is' },
{ value: '!=', description: 'is not' },
@@ -43,15 +43,15 @@ describe('BoardFilteredSearch', () => {
{
icon: 'pencil',
title: TOKEN_TITLE_AUTHOR,
- type: 'author',
+ type: TOKEN_TYPE_AUTHOR,
operators: [
{ value: '=', description: 'is' },
{ value: '!=', description: 'is not' },
],
symbol: '@',
- token: AuthorToken,
+ token: UserToken,
unique: true,
- fetchAuthors: () => new Promise(() => {}),
+ fetchUsers: () => new Promise(() => {}),
},
];
@@ -109,7 +109,7 @@ describe('BoardFilteredSearch', () => {
createComponent({ props: { eeFilters: { labelName: ['label'] } } });
expect(findFilteredSearch().props('initialFilterValue')).toEqual([
- { type: 'label', value: { data: 'label', operator: '=' } },
+ { type: TOKEN_TYPE_LABEL, value: { data: 'label', operator: '=' } },
]);
});
});
@@ -158,7 +158,9 @@ describe('BoardFilteredSearch', () => {
['None', url('None')],
['Any', url('Any')],
])('sets the url param %s', (assigneeParam, expected) => {
- const mockFilters = [{ type: 'assignee', value: { data: assigneeParam, operator: '=' } }];
+ const mockFilters = [
+ { type: TOKEN_TYPE_ASSIGNEE, value: { data: assigneeParam, operator: '=' } },
+ ];
jest.spyOn(urlUtility, 'updateHistory');
findFilteredSearch().vm.$emit('onFilter', mockFilters);
diff --git a/spec/frontend/boards/components/issue_board_filtered_search_spec.js b/spec/frontend/boards/components/issue_board_filtered_search_spec.js
index e4a6a2b8b76..513561307cd 100644
--- a/spec/frontend/boards/components/issue_board_filtered_search_spec.js
+++ b/spec/frontend/boards/components/issue_board_filtered_search_spec.js
@@ -23,14 +23,14 @@ describe('IssueBoardFilter', () => {
});
};
- let fetchAuthorsSpy;
+ let fetchUsersSpy;
let fetchLabelsSpy;
beforeEach(() => {
- fetchAuthorsSpy = jest.fn();
+ fetchUsersSpy = jest.fn();
fetchLabelsSpy = jest.fn();
issueBoardFilters.mockReturnValue({
- fetchAuthors: fetchAuthorsSpy,
+ fetchUsers: fetchUsersSpy,
fetchLabels: fetchLabelsSpy,
});
});
@@ -59,7 +59,7 @@ describe('IssueBoardFilter', () => {
const tokens = mockTokens(
fetchLabelsSpy,
- fetchAuthorsSpy,
+ fetchUsersSpy,
wrapper.vm.fetchMilestones,
isSignedIn,
);
diff --git a/spec/frontend/boards/components/sidebar/board_sidebar_time_tracker_spec.js b/spec/frontend/boards/components/sidebar/board_sidebar_time_tracker_spec.js
index 5c435643425..e2e4baefad0 100644
--- a/spec/frontend/boards/components/sidebar/board_sidebar_time_tracker_spec.js
+++ b/spec/frontend/boards/components/sidebar/board_sidebar_time_tracker_spec.js
@@ -42,13 +42,20 @@ describe('BoardSidebarTimeTracker', () => {
wrapper = null;
});
- it.each([[true], [false]])(
- 'renders IssuableTimeTracker with correct spent and estimated time (timeTrackingLimitToHours=%s)',
- (timeTrackingLimitToHours) => {
- createComponent({ provide: { timeTrackingLimitToHours } });
+ it.each`
+ timeTrackingLimitToHours | canUpdate
+ ${true} | ${false}
+ ${true} | ${true}
+ ${false} | ${false}
+ ${false} | ${true}
+ `(
+ 'renders IssuableTimeTracker with correct spent and estimated time (timeTrackingLimitToHours=$timeTrackingLimitToHours, canUpdate=$canUpdate)',
+ ({ timeTrackingLimitToHours, canUpdate }) => {
+ createComponent({ provide: { timeTrackingLimitToHours, canUpdate } });
expect(wrapper.findComponent(IssuableTimeTracker).props()).toEqual({
limitToHours: timeTrackingLimitToHours,
+ canAddTimeEntries: canUpdate,
showCollapsed: false,
issuableId: '1',
issuableIid: '1',
diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js
index 3c26fa97338..df41eb05eae 100644
--- a/spec/frontend/boards/mock_data.js
+++ b/spec/frontend/boards/mock_data.js
@@ -2,22 +2,26 @@ import { GlFilteredSearchToken } from '@gitlab/ui';
import { keyBy } from 'lodash';
import { ListType } from '~/boards/constants';
import {
- OPERATOR_IS_AND_IS_NOT,
- OPERATOR_IS_ONLY,
+ OPERATORS_IS,
+ OPERATORS_IS_NOT,
TOKEN_TITLE_ASSIGNEE,
TOKEN_TITLE_AUTHOR,
+ TOKEN_TITLE_CONFIDENTIAL,
TOKEN_TITLE_LABEL,
TOKEN_TITLE_MILESTONE,
+ TOKEN_TITLE_MY_REACTION,
TOKEN_TITLE_RELEASE,
TOKEN_TITLE_TYPE,
TOKEN_TYPE_ASSIGNEE,
TOKEN_TYPE_AUTHOR,
+ TOKEN_TYPE_CONFIDENTIAL,
TOKEN_TYPE_LABEL,
TOKEN_TYPE_MILESTONE,
+ TOKEN_TYPE_MY_REACTION,
TOKEN_TYPE_RELEASE,
TOKEN_TYPE_TYPE,
} from '~/vue_shared/components/filtered_search_bar/constants';
-import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
+import UserToken from '~/vue_shared/components/filtered_search_bar/tokens/user_token.vue';
import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue';
import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
@@ -733,54 +737,54 @@ export const mockMoveData = {
};
export const mockEmojiToken = {
- type: 'my-reaction',
+ type: TOKEN_TYPE_MY_REACTION,
icon: 'thumb-up',
- title: 'My-Reaction',
+ title: TOKEN_TITLE_MY_REACTION,
unique: true,
token: EmojiToken,
fetchEmojis: expect.any(Function),
};
export const mockConfidentialToken = {
- type: 'confidential',
+ type: TOKEN_TYPE_CONFIDENTIAL,
icon: 'eye-slash',
- title: 'Confidential',
+ title: TOKEN_TITLE_CONFIDENTIAL,
unique: true,
token: GlFilteredSearchToken,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
options: [
{ icon: 'eye-slash', value: 'yes', title: 'Yes' },
{ icon: 'eye', value: 'no', title: 'No' },
],
};
-export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones, isSignedIn) => [
+export const mockTokens = (fetchLabels, fetchUsers, fetchMilestones, isSignedIn) => [
{
icon: 'user',
title: TOKEN_TITLE_ASSIGNEE,
type: TOKEN_TYPE_ASSIGNEE,
- operators: OPERATOR_IS_AND_IS_NOT,
- token: AuthorToken,
+ operators: OPERATORS_IS_NOT,
+ token: UserToken,
unique: true,
- fetchAuthors,
- preloadedAuthors: [],
+ fetchUsers,
+ preloadedUsers: [],
},
{
icon: 'pencil',
title: TOKEN_TITLE_AUTHOR,
type: TOKEN_TYPE_AUTHOR,
- operators: OPERATOR_IS_AND_IS_NOT,
+ operators: OPERATORS_IS_NOT,
symbol: '@',
- token: AuthorToken,
+ token: UserToken,
unique: true,
- fetchAuthors,
- preloadedAuthors: [],
+ fetchUsers,
+ preloadedUsers: [],
},
{
icon: 'labels',
title: TOKEN_TITLE_LABEL,
type: TOKEN_TYPE_LABEL,
- operators: OPERATOR_IS_AND_IS_NOT,
+ operators: OPERATORS_IS_NOT,
token: LabelToken,
unique: false,
symbol: '~',
diff --git a/spec/frontend/boards/project_select_spec.js b/spec/frontend/boards/project_select_spec.js
index 7ff34ffdf9e..4324e7068e0 100644
--- a/spec/frontend/boards/project_select_spec.js
+++ b/spec/frontend/boards/project_select_spec.js
@@ -156,7 +156,7 @@ describe('ProjectSelect component', () => {
});
it('renders the name of the selected project', () => {
- expect(findGlDropdown().find('.gl-new-dropdown-button-text').text()).toBe(
+ expect(findGlDropdown().find('.gl-dropdown-button-text').text()).toBe(
mockProjectsList1[0].name,
);
});
diff --git a/spec/frontend/captcha/captcha_modal_axios_interceptor_spec.js b/spec/frontend/captcha/captcha_modal_axios_interceptor_spec.js
index 553ca52f9ce..b2a25bc93ea 100644
--- a/spec/frontend/captcha/captcha_modal_axios_interceptor_spec.js
+++ b/spec/frontend/captcha/captcha_modal_axios_interceptor_spec.js
@@ -4,7 +4,10 @@ import { registerCaptchaModalInterceptor } from '~/captcha/captcha_modal_axios_i
import UnsolvedCaptchaError from '~/captcha/unsolved_captcha_error';
import { waitForCaptchaToBeSolved } from '~/captcha/wait_for_captcha_to_be_solved';
import axios from '~/lib/utils/axios_utils';
-import httpStatusCodes from '~/lib/utils/http_status';
+import httpStatusCodes, {
+ HTTP_STATUS_CONFLICT,
+ HTTP_STATUS_METHOD_NOT_ALLOWED,
+} from '~/lib/utils/http_status';
jest.mock('~/captcha/wait_for_captcha_to_be_solved');
@@ -33,7 +36,7 @@ describe('registerCaptchaModalInterceptor', () => {
mock.onAny('/endpoint-with-unrelated-error').reply(404, AXIOS_RESPONSE);
mock.onAny('/endpoint-with-captcha').reply((config) => {
if (!supportedMethods.includes(config.method)) {
- return [httpStatusCodes.METHOD_NOT_ALLOWED, { method: config.method }];
+ return [HTTP_STATUS_METHOD_NOT_ALLOWED, { method: config.method }];
}
const data = JSON.parse(config.data);
@@ -46,7 +49,7 @@ describe('registerCaptchaModalInterceptor', () => {
return [httpStatusCodes.OK, { ...data, method: config.method, CAPTCHA_SUCCESS }];
}
- return [httpStatusCodes.CONFLICT, NEEDS_CAPTCHA_RESPONSE];
+ return [HTTP_STATUS_CONFLICT, NEEDS_CAPTCHA_RESPONSE];
});
axios.interceptors.response.handlers = [];
@@ -123,7 +126,7 @@ describe('registerCaptchaModalInterceptor', () => {
await expect(() => axios[method]('/endpoint-with-captcha')).rejects.toThrow(
expect.objectContaining({
response: expect.objectContaining({
- status: httpStatusCodes.METHOD_NOT_ALLOWED,
+ status: HTTP_STATUS_METHOD_NOT_ALLOWED,
data: { method },
}),
}),
diff --git a/spec/frontend/ci/ci_lint/components/ci_lint_spec.js b/spec/frontend/ci/ci_lint/components/ci_lint_spec.js
new file mode 100644
index 00000000000..d4f588a0e09
--- /dev/null
+++ b/spec/frontend/ci/ci_lint/components/ci_lint_spec.js
@@ -0,0 +1,118 @@
+import { GlAlert } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import waitForPromises from 'helpers/wait_for_promises';
+import CiLint from '~/ci/ci_lint/components/ci_lint.vue';
+import CiLintResults from '~/ci/pipeline_editor/components/lint/ci_lint_results.vue';
+import lintCIMutation from '~/ci/pipeline_editor/graphql/mutations/client/lint_ci.mutation.graphql';
+import SourceEditor from '~/vue_shared/components/source_editor.vue';
+import { mockLintDataValid } from '../mock_data';
+
+describe('CI Lint', () => {
+ let wrapper;
+
+ const endpoint = '/namespace/project/-/ci/lint';
+ const content =
+ "test_job:\n stage: build\n script: echo 'Building'\n only:\n - web\n - chat\n - pushes\n allow_failure: true ";
+ const mockMutate = jest.fn().mockResolvedValue(mockLintDataValid);
+
+ const createComponent = () => {
+ wrapper = shallowMount(CiLint, {
+ data() {
+ return {
+ content,
+ };
+ },
+ propsData: {
+ endpoint,
+ pipelineSimulationHelpPagePath: '/help/ci/lint#pipeline-simulation',
+ lintHelpPagePath: '/help/ci/lint#anchor',
+ },
+ mocks: {
+ $apollo: {
+ mutate: mockMutate,
+ },
+ },
+ });
+ };
+
+ const findEditor = () => wrapper.findComponent(SourceEditor);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findCiLintResults = () => wrapper.findComponent(CiLintResults);
+ const findValidateBtn = () => wrapper.find('[data-testid="ci-lint-validate"]');
+ const findClearBtn = () => wrapper.find('[data-testid="ci-lint-clear"]');
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ mockMutate.mockClear();
+ wrapper.destroy();
+ });
+
+ it('displays the editor', () => {
+ expect(findEditor().exists()).toBe(true);
+ });
+
+ it('validate action calls mutation correctly', () => {
+ findValidateBtn().vm.$emit('click');
+
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
+ mutation: lintCIMutation,
+ variables: { content, dry: false, endpoint },
+ });
+ });
+
+ it('validate action calls mutation with dry run', async () => {
+ const dryRunEnabled = true;
+
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ await wrapper.setData({ dryRun: dryRunEnabled });
+
+ findValidateBtn().vm.$emit('click');
+
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
+ mutation: lintCIMutation,
+ variables: { content, dry: dryRunEnabled, endpoint },
+ });
+ });
+
+ it('validation displays results', async () => {
+ findValidateBtn().vm.$emit('click');
+
+ await nextTick();
+
+ expect(findValidateBtn().props('loading')).toBe(true);
+
+ await waitForPromises();
+
+ expect(findCiLintResults().exists()).toBe(true);
+ expect(findValidateBtn().props('loading')).toBe(false);
+ });
+
+ it('validation displays error', async () => {
+ mockMutate.mockRejectedValue('Error!');
+
+ findValidateBtn().vm.$emit('click');
+
+ await nextTick();
+
+ expect(findValidateBtn().props('loading')).toBe(true);
+
+ await waitForPromises();
+
+ expect(findCiLintResults().exists()).toBe(false);
+ expect(findAlert().text()).toBe('Error!');
+ expect(findValidateBtn().props('loading')).toBe(false);
+ });
+
+ it('content is cleared on clear action', async () => {
+ expect(findEditor().props('value')).toBe(content);
+
+ await findClearBtn().vm.$emit('click');
+
+ expect(findEditor().props('value')).toBe('');
+ });
+});
diff --git a/spec/frontend/ci/ci_lint/mock_data.js b/spec/frontend/ci/ci_lint/mock_data.js
new file mode 100644
index 00000000000..05582470dfa
--- /dev/null
+++ b/spec/frontend/ci/ci_lint/mock_data.js
@@ -0,0 +1,23 @@
+import { mockJobs } from 'jest/ci/pipeline_editor/mock_data';
+
+export const mockLintDataError = {
+ data: {
+ lintCI: {
+ errors: ['Error message'],
+ warnings: ['Warning message'],
+ valid: false,
+ jobs: mockJobs,
+ },
+ },
+};
+
+export const mockLintDataValid = {
+ data: {
+ lintCI: {
+ errors: [],
+ warnings: [],
+ valid: true,
+ jobs: mockJobs,
+ },
+ },
+};
diff --git a/spec/frontend/ci/pipeline_editor/components/code_snippet_alert/code_snippet_alert_spec.js b/spec/frontend/ci/pipeline_editor/components/code_snippet_alert/code_snippet_alert_spec.js
new file mode 100644
index 00000000000..b00e1adab63
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/code_snippet_alert/code_snippet_alert_spec.js
@@ -0,0 +1,61 @@
+import { within } from '@testing-library/dom';
+import { mount } from '@vue/test-utils';
+import { merge } from 'lodash';
+import { TEST_HOST } from 'helpers/test_constants';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import CodeSnippetAlert from '~/ci/pipeline_editor/components/code_snippet_alert/code_snippet_alert.vue';
+import { CODE_SNIPPET_SOURCE_API_FUZZING } from '~/ci/pipeline_editor/components/code_snippet_alert/constants';
+
+const apiFuzzingConfigurationPath = '/namespace/project/-/security/configuration/api_fuzzing';
+
+describe('EE - CodeSnippetAlert', () => {
+ let wrapper;
+
+ const createWrapper = (options) => {
+ wrapper = extendedWrapper(
+ mount(
+ CodeSnippetAlert,
+ merge(
+ {
+ provide: {
+ configurationPaths: {
+ [CODE_SNIPPET_SOURCE_API_FUZZING]: apiFuzzingConfigurationPath,
+ },
+ },
+ propsData: {
+ source: CODE_SNIPPET_SOURCE_API_FUZZING,
+ },
+ },
+ options,
+ ),
+ ),
+ );
+ };
+
+ const withinComponent = () => within(wrapper.element);
+ const findDocsLink = () => withinComponent().getByRole('link', { name: /read documentation/i });
+ const findConfigurationLink = () =>
+ withinComponent().getByRole('link', { name: /Go back to configuration/i });
+
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it("provides a link to the feature's documentation", () => {
+ const docsLink = findDocsLink();
+
+ expect(docsLink).not.toBe(null);
+ expect(docsLink.href).toBe(`${TEST_HOST}/help/user/application_security/api_fuzzing/index`);
+ });
+
+ it("provides a link to the feature's configuration form", () => {
+ const configurationLink = findConfigurationLink();
+
+ expect(configurationLink).not.toBe(null);
+ expect(configurationLink.href).toBe(TEST_HOST + apiFuzzingConfigurationPath);
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/commit/commit_form_spec.js b/spec/frontend/ci/pipeline_editor/components/commit/commit_form_spec.js
new file mode 100644
index 00000000000..8e1d8081dd8
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/commit/commit_form_spec.js
@@ -0,0 +1,158 @@
+import { nextTick } from 'vue';
+import { GlFormInput, GlFormTextarea } from '@gitlab/ui';
+import { shallowMount, mount } from '@vue/test-utils';
+
+import CommitForm from '~/ci/pipeline_editor/components/commit/commit_form.vue';
+
+import { mockCommitMessage, mockDefaultBranch } from '../../mock_data';
+
+const scrollIntoViewMock = jest.fn();
+HTMLElement.prototype.scrollIntoView = scrollIntoViewMock;
+
+describe('Pipeline Editor | Commit Form', () => {
+ let wrapper;
+
+ const createComponent = ({ props = {} } = {}, mountFn = shallowMount) => {
+ wrapper = mountFn(CommitForm, {
+ propsData: {
+ defaultMessage: mockCommitMessage,
+ currentBranch: mockDefaultBranch,
+ hasUnsavedChanges: true,
+ isNewCiConfigFile: false,
+ ...props,
+ },
+
+ // attachTo is required for input/submit events
+ attachTo: mountFn === mount ? document.body : null,
+ });
+ };
+
+ const findCommitTextarea = () => wrapper.findComponent(GlFormTextarea);
+ const findBranchInput = () => wrapper.findComponent(GlFormInput);
+ const findNewMrCheckbox = () => wrapper.find('[data-testid="new-mr-checkbox"]');
+ const findSubmitBtn = () => wrapper.find('[type="submit"]');
+ const findCancelBtn = () => wrapper.find('[type="reset"]');
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when the form is displayed', () => {
+ beforeEach(async () => {
+ createComponent();
+ });
+
+ it('shows a default commit message', () => {
+ expect(findCommitTextarea().attributes('value')).toBe(mockCommitMessage);
+ });
+
+ it('shows current branch', () => {
+ expect(findBranchInput().attributes('value')).toBe(mockDefaultBranch);
+ });
+
+ it('shows buttons', () => {
+ expect(findSubmitBtn().exists()).toBe(true);
+ expect(findCancelBtn().exists()).toBe(true);
+ });
+
+ it('does not show a new MR checkbox by default', () => {
+ expect(findNewMrCheckbox().exists()).toBe(false);
+ });
+ });
+
+ describe('when buttons are clicked', () => {
+ beforeEach(async () => {
+ createComponent({}, mount);
+ });
+
+ it('emits an event when the form submits', () => {
+ findSubmitBtn().trigger('click');
+
+ expect(wrapper.emitted('submit')[0]).toEqual([
+ {
+ message: mockCommitMessage,
+ sourceBranch: mockDefaultBranch,
+ openMergeRequest: false,
+ },
+ ]);
+ });
+
+ it('emits an event when the form resets', () => {
+ findCancelBtn().trigger('click');
+
+ expect(wrapper.emitted('resetContent')).toHaveLength(1);
+ });
+ });
+
+ describe('submit button', () => {
+ it.each`
+ hasUnsavedChanges | isNewCiConfigFile | isDisabled | btnState
+ ${false} | ${false} | ${true} | ${'disabled'}
+ ${true} | ${false} | ${false} | ${'enabled'}
+ ${true} | ${true} | ${false} | ${'enabled'}
+ ${false} | ${true} | ${false} | ${'enabled'}
+ `(
+ 'is $btnState when hasUnsavedChanges:$hasUnsavedChanges and isNewCiConfigfile:$isNewCiConfigFile',
+ ({ hasUnsavedChanges, isNewCiConfigFile, isDisabled }) => {
+ createComponent({ props: { hasUnsavedChanges, isNewCiConfigFile } });
+
+ if (isDisabled) {
+ expect(findSubmitBtn().attributes('disabled')).toBe('true');
+ } else {
+ expect(findSubmitBtn().attributes('disabled')).toBeUndefined();
+ }
+ },
+ );
+ });
+
+ describe('when user inputs values', () => {
+ const anotherMessage = 'Another commit message';
+ const anotherBranch = 'my-branch';
+
+ beforeEach(() => {
+ createComponent({}, mount);
+
+ findCommitTextarea().setValue(anotherMessage);
+ findBranchInput().setValue(anotherBranch);
+ });
+
+ it('shows a new MR checkbox', () => {
+ expect(findNewMrCheckbox().exists()).toBe(true);
+ });
+
+ it('emits an event with values', async () => {
+ await findNewMrCheckbox().setChecked();
+ await findSubmitBtn().trigger('click');
+
+ expect(wrapper.emitted('submit')[0]).toEqual([
+ {
+ message: anotherMessage,
+ sourceBranch: anotherBranch,
+ openMergeRequest: true,
+ },
+ ]);
+ });
+
+ it('when the commit message is empty, submit button is disabled', async () => {
+ await findCommitTextarea().setValue('');
+
+ expect(findSubmitBtn().attributes('disabled')).toBe('disabled');
+ });
+ });
+
+ describe('when scrollToCommitForm becomes true', () => {
+ beforeEach(async () => {
+ createComponent();
+ wrapper.setProps({ scrollToCommitForm: true });
+ await nextTick();
+ });
+
+ it('scrolls into view', () => {
+ expect(scrollIntoViewMock).toHaveBeenCalledWith({ behavior: 'smooth' });
+ });
+
+ it('emits "scrolled-to-commit-form"', () => {
+ expect(wrapper.emitted()['scrolled-to-commit-form']).toHaveLength(1);
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/commit/commit_section_spec.js b/spec/frontend/ci/pipeline_editor/components/commit/commit_section_spec.js
new file mode 100644
index 00000000000..f6e93c55bbb
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/commit/commit_section_spec.js
@@ -0,0 +1,287 @@
+import VueApollo from 'vue-apollo';
+import { GlFormTextarea, GlFormInput, GlLoadingIcon } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import Vue from 'vue';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import CommitForm from '~/ci/pipeline_editor/components/commit/commit_form.vue';
+import CommitSection from '~/ci/pipeline_editor/components/commit/commit_section.vue';
+import {
+ COMMIT_ACTION_CREATE,
+ COMMIT_ACTION_UPDATE,
+ COMMIT_SUCCESS,
+ COMMIT_SUCCESS_WITH_REDIRECT,
+} from '~/ci/pipeline_editor/constants';
+import { resolvers } from '~/ci/pipeline_editor/graphql/resolvers';
+import commitCreate from '~/ci/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql';
+import getCurrentBranch from '~/ci/pipeline_editor/graphql/queries/client/current_branch.query.graphql';
+import updatePipelineEtag from '~/ci/pipeline_editor/graphql/mutations/client/update_pipeline_etag.mutation.graphql';
+
+import {
+ mockCiConfigPath,
+ mockCiYml,
+ mockCommitCreateResponse,
+ mockCommitCreateResponseNewEtag,
+ mockCommitSha,
+ mockCommitMessage,
+ mockDefaultBranch,
+ mockProjectFullPath,
+} from '../../mock_data';
+
+const mockVariables = {
+ action: COMMIT_ACTION_UPDATE,
+ projectPath: mockProjectFullPath,
+ startBranch: mockDefaultBranch,
+ message: mockCommitMessage,
+ filePath: mockCiConfigPath,
+ content: mockCiYml,
+ lastCommitId: mockCommitSha,
+};
+
+const mockProvide = {
+ ciConfigPath: mockCiConfigPath,
+ projectFullPath: mockProjectFullPath,
+};
+
+describe('Pipeline Editor | Commit section', () => {
+ let wrapper;
+ let mockApollo;
+ const mockMutateCommitData = jest.fn();
+
+ const defaultProps = {
+ ciFileContent: mockCiYml,
+ commitSha: mockCommitSha,
+ hasUnsavedChanges: true,
+ isNewCiConfigFile: false,
+ };
+
+ const createComponent = ({ apolloConfig = {}, props = {}, options = {}, provide = {} } = {}) => {
+ wrapper = mount(CommitSection, {
+ propsData: { ...defaultProps, ...props },
+ provide: { ...mockProvide, ...provide },
+ data() {
+ return {
+ currentBranch: mockDefaultBranch,
+ };
+ },
+ attachTo: document.body,
+ ...apolloConfig,
+ ...options,
+ });
+ };
+
+ const createComponentWithApollo = (options) => {
+ const handlers = [[commitCreate, mockMutateCommitData]];
+ Vue.use(VueApollo);
+ mockApollo = createMockApollo(handlers, resolvers);
+
+ mockApollo.clients.defaultClient.cache.writeQuery({
+ query: getCurrentBranch,
+ data: {
+ workBranches: {
+ __typename: 'BranchList',
+ current: {
+ __typename: 'WorkBranch',
+ name: mockDefaultBranch,
+ },
+ },
+ },
+ });
+
+ const apolloConfig = {
+ apolloProvider: mockApollo,
+ };
+
+ createComponent({ ...options, apolloConfig });
+ };
+
+ const findCommitForm = () => wrapper.findComponent(CommitForm);
+ const findCommitBtnLoadingIcon = () =>
+ wrapper.find('[type="submit"]').findComponent(GlLoadingIcon);
+
+ const submitCommit = async ({
+ message = mockCommitMessage,
+ branch = mockDefaultBranch,
+ openMergeRequest = false,
+ } = {}) => {
+ await findCommitForm().findComponent(GlFormTextarea).setValue(message);
+ await findCommitForm().findComponent(GlFormInput).setValue(branch);
+ if (openMergeRequest) {
+ await findCommitForm().find('[data-testid="new-mr-checkbox"]').setChecked(openMergeRequest);
+ }
+ await findCommitForm().find('[type="submit"]').trigger('click');
+ await waitForPromises();
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when the user commits a new file', () => {
+ beforeEach(async () => {
+ mockMutateCommitData.mockResolvedValue(mockCommitCreateResponse);
+ createComponentWithApollo({ props: { isNewCiConfigFile: true } });
+ await submitCommit();
+ });
+
+ it('calls the mutation with the CREATE action', () => {
+ expect(mockMutateCommitData).toHaveBeenCalledTimes(1);
+ expect(mockMutateCommitData).toHaveBeenCalledWith({
+ ...mockVariables,
+ action: COMMIT_ACTION_CREATE,
+ branch: mockDefaultBranch,
+ });
+ });
+ });
+
+ describe('when the user commits an update to an existing file', () => {
+ beforeEach(async () => {
+ createComponentWithApollo();
+ await submitCommit();
+ });
+
+ it('calls the mutation with the UPDATE action', () => {
+ expect(mockMutateCommitData).toHaveBeenCalledTimes(1);
+ expect(mockMutateCommitData).toHaveBeenCalledWith({
+ ...mockVariables,
+ action: COMMIT_ACTION_UPDATE,
+ branch: mockDefaultBranch,
+ });
+ });
+ });
+
+ describe('when the user commits changes to the current branch', () => {
+ beforeEach(async () => {
+ createComponentWithApollo();
+ await submitCommit();
+ });
+
+ it('calls the mutation with the current branch', () => {
+ expect(mockMutateCommitData).toHaveBeenCalledTimes(1);
+ expect(mockMutateCommitData).toHaveBeenCalledWith({
+ ...mockVariables,
+ branch: mockDefaultBranch,
+ });
+ });
+
+ it('emits an event to communicate the commit was successful', () => {
+ expect(wrapper.emitted('commit')).toHaveLength(1);
+ expect(wrapper.emitted('commit')[0]).toEqual([{ type: COMMIT_SUCCESS }]);
+ });
+
+ it('emits an event to refetch the commit sha', () => {
+ expect(wrapper.emitted('updateCommitSha')).toHaveLength(1);
+ });
+
+ it('shows no saving state', () => {
+ expect(findCommitBtnLoadingIcon().exists()).toBe(false);
+ });
+
+ it('a second commit submits the latest sha, keeping the form updated', async () => {
+ await submitCommit();
+
+ expect(mockMutateCommitData).toHaveBeenCalledTimes(2);
+ expect(mockMutateCommitData).toHaveBeenCalledWith({
+ ...mockVariables,
+ branch: mockDefaultBranch,
+ });
+ });
+ });
+
+ describe('when the user commits changes to a new branch', () => {
+ const newBranch = 'new-branch';
+
+ beforeEach(async () => {
+ createComponentWithApollo();
+ await submitCommit({
+ branch: newBranch,
+ });
+ });
+
+ it('calls the mutation with the new branch', () => {
+ expect(mockMutateCommitData).toHaveBeenCalledWith({
+ ...mockVariables,
+ branch: newBranch,
+ });
+ });
+
+ it('does not emit an event to refetch the commit sha', () => {
+ expect(wrapper.emitted('updateCommitSha')).toBeUndefined();
+ });
+ });
+
+ describe('when the user commits changes to open a new merge request', () => {
+ const newBranch = 'new-branch';
+
+ beforeEach(async () => {
+ mockMutateCommitData.mockResolvedValue(mockCommitCreateResponse);
+ createComponentWithApollo();
+ mockMutateCommitData.mockResolvedValue(mockCommitCreateResponse);
+ await submitCommit({
+ branch: newBranch,
+ openMergeRequest: true,
+ });
+ });
+
+ it('emits a commit event with the right type, sourceBranch and targetBranch', () => {
+ expect(wrapper.emitted('commit')).toHaveLength(1);
+ expect(wrapper.emitted('commit')[0]).toMatchObject([
+ {
+ type: COMMIT_SUCCESS_WITH_REDIRECT,
+ params: { sourceBranch: newBranch, targetBranch: mockDefaultBranch },
+ },
+ ]);
+ });
+ });
+
+ describe('when the commit is ocurring', () => {
+ beforeEach(() => {
+ createComponentWithApollo();
+ });
+
+ it('shows a saving state', async () => {
+ mockMutateCommitData.mockImplementationOnce(() => {
+ expect(findCommitBtnLoadingIcon().exists()).toBe(true);
+ return Promise.resolve();
+ });
+
+ await submitCommit({
+ message: mockCommitMessage,
+ branch: mockDefaultBranch,
+ openMergeRequest: false,
+ });
+ });
+ });
+
+ describe('when the commit returns a different etag path', () => {
+ beforeEach(async () => {
+ createComponentWithApollo();
+ jest.spyOn(wrapper.vm.$apollo, 'mutate');
+ mockMutateCommitData.mockResolvedValue(mockCommitCreateResponseNewEtag);
+ await submitCommit();
+ });
+
+ it('calls the client mutation to update the etag', () => {
+ // 1:Commit submission, 2:etag update, 3:currentBranch update, 4:lastCommit update
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(4);
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenNthCalledWith(2, {
+ mutation: updatePipelineEtag,
+ variables: {
+ pipelineEtag: mockCommitCreateResponseNewEtag.data.commitCreate.commitPipelinePath,
+ },
+ });
+ });
+ });
+
+ it('sets listeners on commit form', () => {
+ const handler = jest.fn();
+ createComponent({ options: { listeners: { event: handler } } });
+ findCommitForm().vm.$emit('event');
+ expect(handler).toHaveBeenCalled();
+ });
+
+ it('passes down scroll-to-commit-form prop to commit form', () => {
+ createComponent({ props: { 'scroll-to-commit-form': true } });
+ expect(findCommitForm().props('scrollToCommitForm')).toBe(true);
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/drawer/cards/first_pipeline_card_spec.js b/spec/frontend/ci/pipeline_editor/components/drawer/cards/first_pipeline_card_spec.js
new file mode 100644
index 00000000000..137137ec657
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/drawer/cards/first_pipeline_card_spec.js
@@ -0,0 +1,60 @@
+import { getByRole } from '@testing-library/dom';
+import { mount } from '@vue/test-utils';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
+import FirstPipelineCard from '~/ci/pipeline_editor/components/drawer/cards/first_pipeline_card.vue';
+import { pipelineEditorTrackingOptions } from '~/ci/pipeline_editor/constants';
+
+describe('First pipeline card', () => {
+ let wrapper;
+ let trackingSpy;
+
+ const createComponent = () => {
+ wrapper = mount(FirstPipelineCard);
+ };
+
+ const getLinkByName = (name) => getByRole(wrapper.element, 'link', { name });
+ const findRunnersLink = () => getLinkByName(/make sure your instance has runners available/i);
+ const findInstructionsList = () => wrapper.find('ol');
+ const findAllInstructions = () => findInstructionsList().findAll('li');
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the title', () => {
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.title);
+ });
+
+ it('renders the content', () => {
+ expect(findInstructionsList().exists()).toBe(true);
+ expect(findAllInstructions()).toHaveLength(3);
+ });
+
+ it('renders the link', () => {
+ expect(findRunnersLink().href).toBe(wrapper.vm.$options.RUNNER_HELP_URL);
+ });
+
+ describe('tracking', () => {
+ beforeEach(() => {
+ createComponent();
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ });
+
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ it('tracks runners help page click', async () => {
+ const { label } = pipelineEditorTrackingOptions;
+ const { runners } = pipelineEditorTrackingOptions.actions.helpDrawerLinks;
+
+ await findRunnersLink().click();
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, runners, { label });
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/drawer/cards/getting_started_card_spec.js b/spec/frontend/ci/pipeline_editor/components/drawer/cards/getting_started_card_spec.js
new file mode 100644
index 00000000000..cdce757ce7c
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/drawer/cards/getting_started_card_spec.js
@@ -0,0 +1,26 @@
+import { shallowMount } from '@vue/test-utils';
+import GettingStartedCard from '~/ci/pipeline_editor/components/drawer/cards/getting_started_card.vue';
+
+describe('Getting started card', () => {
+ let wrapper;
+
+ const createComponent = () => {
+ wrapper = shallowMount(GettingStartedCard);
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the title', () => {
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.title);
+ });
+
+ it('renders the content', () => {
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.firstParagraph);
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/drawer/cards/pipeline_config_reference_card_spec.js b/spec/frontend/ci/pipeline_editor/components/drawer/cards/pipeline_config_reference_card_spec.js
new file mode 100644
index 00000000000..6909916c3e6
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/drawer/cards/pipeline_config_reference_card_spec.js
@@ -0,0 +1,89 @@
+import { getByRole } from '@testing-library/dom';
+import { mount } from '@vue/test-utils';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
+import PipelineConfigReferenceCard from '~/ci/pipeline_editor/components/drawer/cards/pipeline_config_reference_card.vue';
+import { pipelineEditorTrackingOptions } from '~/ci/pipeline_editor/constants';
+
+describe('Pipeline config reference card', () => {
+ let wrapper;
+ let trackingSpy;
+
+ const defaultProvide = {
+ ciExamplesHelpPagePath: 'help/ci/examples/',
+ ciHelpPagePath: 'help/ci/introduction',
+ needsHelpPagePath: 'help/ci/yaml#needs',
+ ymlHelpPagePath: 'help/ci/yaml',
+ };
+
+ const createComponent = () => {
+ wrapper = mount(PipelineConfigReferenceCard, {
+ provide: {
+ ...defaultProvide,
+ },
+ });
+ };
+
+ const getLinkByName = (name) => getByRole(wrapper.element, 'link', { name });
+ const findCiExamplesLink = () => getLinkByName(/CI\/CD examples and templates/i);
+ const findCiIntroLink = () => getLinkByName(/GitLab CI\/CD concepts/i);
+ const findNeedsLink = () => getLinkByName(/Needs keyword/i);
+ const findYmlSyntaxLink = () => getLinkByName(/.gitlab-ci.yml syntax reference/i);
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the title', () => {
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.title);
+ });
+
+ it('renders the content', () => {
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.firstParagraph);
+ });
+
+ it('renders the links', () => {
+ expect(findCiExamplesLink().href).toContain(defaultProvide.ciExamplesHelpPagePath);
+ expect(findCiIntroLink().href).toContain(defaultProvide.ciHelpPagePath);
+ expect(findNeedsLink().href).toContain(defaultProvide.needsHelpPagePath);
+ expect(findYmlSyntaxLink().href).toContain(defaultProvide.ymlHelpPagePath);
+ });
+
+ describe('tracking', () => {
+ beforeEach(() => {
+ createComponent();
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ });
+
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ const testTracker = async (element, expectedAction) => {
+ const { label } = pipelineEditorTrackingOptions;
+
+ await element.click();
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, expectedAction, {
+ label,
+ });
+ };
+
+ it('tracks help page links', async () => {
+ const {
+ CI_EXAMPLES_LINK,
+ CI_HELP_LINK,
+ CI_NEEDS_LINK,
+ CI_YAML_LINK,
+ } = pipelineEditorTrackingOptions.actions.helpDrawerLinks;
+
+ testTracker(findCiExamplesLink(), CI_EXAMPLES_LINK);
+ testTracker(findCiIntroLink(), CI_HELP_LINK);
+ testTracker(findNeedsLink(), CI_NEEDS_LINK);
+ testTracker(findYmlSyntaxLink(), CI_YAML_LINK);
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/drawer/cards/visualize_and_lint_card_spec.js b/spec/frontend/ci/pipeline_editor/components/drawer/cards/visualize_and_lint_card_spec.js
new file mode 100644
index 00000000000..0c6879020de
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/drawer/cards/visualize_and_lint_card_spec.js
@@ -0,0 +1,26 @@
+import { shallowMount } from '@vue/test-utils';
+import VisualizeAndLintCard from '~/ci/pipeline_editor/components/drawer/cards/getting_started_card.vue';
+
+describe('Visual and Lint card', () => {
+ let wrapper;
+
+ const createComponent = () => {
+ wrapper = shallowMount(VisualizeAndLintCard);
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the title', () => {
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.title);
+ });
+
+ it('renders the content', () => {
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.firstParagraph);
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/drawer/pipeline_editor_drawer_spec.js b/spec/frontend/ci/pipeline_editor/components/drawer/pipeline_editor_drawer_spec.js
new file mode 100644
index 00000000000..42e372cc1db
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/drawer/pipeline_editor_drawer_spec.js
@@ -0,0 +1,27 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlDrawer } from '@gitlab/ui';
+import PipelineEditorDrawer from '~/ci/pipeline_editor/components/drawer/pipeline_editor_drawer.vue';
+
+describe('Pipeline editor drawer', () => {
+ let wrapper;
+
+ const findDrawer = () => wrapper.findComponent(GlDrawer);
+
+ const createComponent = () => {
+ wrapper = shallowMount(PipelineEditorDrawer);
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('emits close event when closing the drawer', () => {
+ createComponent();
+
+ expect(wrapper.emitted('close-drawer')).toBeUndefined();
+
+ findDrawer().vm.$emit('close');
+
+ expect(wrapper.emitted('close-drawer')).toHaveLength(1);
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/drawer/ui/demo_job_pill_spec.js b/spec/frontend/ci/pipeline_editor/components/drawer/ui/demo_job_pill_spec.js
new file mode 100644
index 00000000000..f510c61ee74
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/drawer/ui/demo_job_pill_spec.js
@@ -0,0 +1,27 @@
+import { shallowMount } from '@vue/test-utils';
+import DemoJobPill from '~/ci/pipeline_editor/components/drawer/ui/demo_job_pill.vue';
+
+describe('Demo job pill', () => {
+ let wrapper;
+ const jobName = 'my-build-job';
+
+ const createComponent = () => {
+ wrapper = shallowMount(DemoJobPill, {
+ propsData: {
+ jobName,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the jobName', () => {
+ expect(wrapper.text()).toContain(jobName);
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/editor/ci_config_merged_preview_spec.js b/spec/frontend/ci/pipeline_editor/components/editor/ci_config_merged_preview_spec.js
new file mode 100644
index 00000000000..2a2bc2547cc
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/editor/ci_config_merged_preview_spec.js
@@ -0,0 +1,69 @@
+import { GlIcon } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+
+import { EDITOR_READY_EVENT } from '~/editor/constants';
+import CiConfigMergedPreview from '~/ci/pipeline_editor/components/editor/ci_config_merged_preview.vue';
+import { mockLintResponse, mockCiConfigPath } from '../../mock_data';
+
+describe('Text editor component', () => {
+ let wrapper;
+
+ const MockSourceEditor = {
+ template: '<div/>',
+ props: ['value', 'fileName', 'editorOptions'],
+ mounted() {
+ this.$emit(EDITOR_READY_EVENT);
+ },
+ };
+
+ const createComponent = ({ props = {} } = {}) => {
+ wrapper = shallowMount(CiConfigMergedPreview, {
+ propsData: {
+ ciConfigData: mockLintResponse,
+ ...props,
+ },
+ provide: {
+ ciConfigPath: mockCiConfigPath,
+ },
+ stubs: {
+ SourceEditor: MockSourceEditor,
+ },
+ });
+ };
+
+ const findIcon = () => wrapper.findComponent(GlIcon);
+ const findEditor = () => wrapper.findComponent(MockSourceEditor);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when status is valid', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('shows an information message that the section is not editable', () => {
+ expect(findIcon().exists()).toBe(true);
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.viewOnlyMessage);
+ });
+
+ it('contains an editor', () => {
+ expect(findEditor().exists()).toBe(true);
+ });
+
+ it('editor contains the value provided', () => {
+ expect(findEditor().props('value')).toBe(mockLintResponse.mergedYaml);
+ });
+
+ it('editor is configured for the CI config path', () => {
+ expect(findEditor().props('fileName')).toBe(mockCiConfigPath);
+ });
+
+ it('editor is readonly', () => {
+ expect(findEditor().props('editorOptions')).toMatchObject({
+ readOnly: true,
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/editor/ci_editor_header_spec.js b/spec/frontend/ci/pipeline_editor/components/editor/ci_editor_header_spec.js
new file mode 100644
index 00000000000..d7f0ce838d6
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/editor/ci_editor_header_spec.js
@@ -0,0 +1,115 @@
+import { shallowMount } from '@vue/test-utils';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
+import CiEditorHeader from '~/ci/pipeline_editor/components/editor/ci_editor_header.vue';
+import {
+ pipelineEditorTrackingOptions,
+ TEMPLATE_REPOSITORY_URL,
+} from '~/ci/pipeline_editor/constants';
+
+describe('CI Editor Header', () => {
+ let wrapper;
+ let trackingSpy = null;
+
+ const createComponent = ({ showDrawer = false } = {}) => {
+ wrapper = extendedWrapper(
+ shallowMount(CiEditorHeader, {
+ propsData: {
+ showDrawer,
+ },
+ }),
+ );
+ };
+
+ const findLinkBtn = () => wrapper.findByTestId('template-repo-link');
+ const findHelpBtn = () => wrapper.findByTestId('drawer-toggle');
+
+ afterEach(() => {
+ wrapper.destroy();
+ unmockTracking();
+ });
+
+ const testTracker = async (element, expectedAction) => {
+ const { label } = pipelineEditorTrackingOptions;
+
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ await element.vm.$emit('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, expectedAction, {
+ label,
+ });
+ };
+
+ describe('link button', () => {
+ beforeEach(() => {
+ createComponent();
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ });
+
+ it('finds the browse template button', () => {
+ expect(findLinkBtn().exists()).toBe(true);
+ });
+
+ it('contains the link to the template repo', () => {
+ expect(findLinkBtn().attributes('href')).toBe(TEMPLATE_REPOSITORY_URL);
+ });
+
+ it('has the external-link icon', () => {
+ expect(findLinkBtn().props('icon')).toBe('external-link');
+ });
+
+ it('tracks the click on the browse button', async () => {
+ const { browseTemplates } = pipelineEditorTrackingOptions.actions;
+
+ testTracker(findLinkBtn(), browseTemplates);
+ });
+ });
+
+ describe('help button', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('finds the help button', () => {
+ expect(findHelpBtn().exists()).toBe(true);
+ });
+
+ it('has the information-o icon', () => {
+ expect(findHelpBtn().props('icon')).toBe('information-o');
+ });
+
+ describe('when pipeline editor drawer is closed', () => {
+ beforeEach(() => {
+ createComponent({ showDrawer: false });
+ });
+
+ it('emits open drawer event when clicked', () => {
+ expect(wrapper.emitted('open-drawer')).toBeUndefined();
+
+ findHelpBtn().vm.$emit('click');
+
+ expect(wrapper.emitted('open-drawer')).toHaveLength(1);
+ });
+
+ it('tracks open help drawer action', async () => {
+ const { actions } = pipelineEditorTrackingOptions;
+
+ testTracker(findHelpBtn(), actions.openHelpDrawer);
+ });
+ });
+
+ describe('when pipeline editor drawer is open', () => {
+ beforeEach(() => {
+ createComponent({ showDrawer: true });
+ });
+
+ it('emits close drawer event when clicked', () => {
+ expect(wrapper.emitted('close-drawer')).toBeUndefined();
+
+ findHelpBtn().vm.$emit('click');
+
+ expect(wrapper.emitted('close-drawer')).toHaveLength(1);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/editor/text_editor_spec.js b/spec/frontend/ci/pipeline_editor/components/editor/text_editor_spec.js
new file mode 100644
index 00000000000..63e23c41263
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/editor/text_editor_spec.js
@@ -0,0 +1,134 @@
+import { shallowMount } from '@vue/test-utils';
+
+import { EDITOR_READY_EVENT } from '~/editor/constants';
+import { SOURCE_EDITOR_DEBOUNCE } from '~/ci/pipeline_editor/constants';
+import TextEditor from '~/ci/pipeline_editor/components/editor/text_editor.vue';
+import {
+ mockCiConfigPath,
+ mockCiYml,
+ mockCommitSha,
+ mockProjectPath,
+ mockProjectNamespace,
+ mockDefaultBranch,
+} from '../../mock_data';
+
+describe('Pipeline Editor | Text editor component', () => {
+ let wrapper;
+
+ let editorReadyListener;
+ let mockUse;
+ let mockRegisterCiSchema;
+ let mockEditorInstance;
+ let editorInstanceDetail;
+
+ const MockSourceEditor = {
+ template: '<div/>',
+ props: ['value', 'fileName', 'editorOptions', 'debounceValue'],
+ };
+
+ const createComponent = (glFeatures = {}, mountFn = shallowMount) => {
+ wrapper = mountFn(TextEditor, {
+ provide: {
+ projectPath: mockProjectPath,
+ projectNamespace: mockProjectNamespace,
+ ciConfigPath: mockCiConfigPath,
+ defaultBranch: mockDefaultBranch,
+ glFeatures,
+ },
+ propsData: {
+ commitSha: mockCommitSha,
+ },
+ attrs: {
+ value: mockCiYml,
+ },
+ listeners: {
+ [EDITOR_READY_EVENT]: editorReadyListener,
+ },
+ stubs: {
+ SourceEditor: MockSourceEditor,
+ },
+ });
+ };
+
+ const findEditor = () => wrapper.findComponent(MockSourceEditor);
+
+ beforeEach(() => {
+ editorReadyListener = jest.fn();
+ mockUse = jest.fn();
+ mockRegisterCiSchema = jest.fn();
+ mockEditorInstance = {
+ use: mockUse,
+ registerCiSchema: mockRegisterCiSchema,
+ };
+ editorInstanceDetail = {
+ detail: {
+ instance: mockEditorInstance,
+ },
+ };
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+
+ mockUse.mockClear();
+ mockRegisterCiSchema.mockClear();
+ });
+
+ describe('template', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('contains an editor', () => {
+ expect(findEditor().exists()).toBe(true);
+ });
+
+ it('editor contains the value provided', () => {
+ expect(findEditor().props('value')).toBe(mockCiYml);
+ });
+
+ it('editor is configured for the CI config path', () => {
+ expect(findEditor().props('fileName')).toBe(mockCiConfigPath);
+ });
+
+ it('passes down editor configs options', () => {
+ expect(findEditor().props('editorOptions')).toEqual({ quickSuggestions: true });
+ });
+
+ it('passes down editor debounce value', () => {
+ expect(findEditor().props('debounceValue')).toBe(SOURCE_EDITOR_DEBOUNCE);
+ });
+
+ it('bubbles up events', () => {
+ findEditor().vm.$emit(EDITOR_READY_EVENT, editorInstanceDetail);
+
+ expect(editorReadyListener).toHaveBeenCalled();
+ });
+ });
+
+ describe('CI schema', () => {
+ describe('when `schema_linting` feature flag is on', () => {
+ beforeEach(() => {
+ createComponent({ schemaLinting: true });
+ findEditor().vm.$emit(EDITOR_READY_EVENT, editorInstanceDetail);
+ });
+
+ it('configures editor with syntax highlight', () => {
+ expect(mockUse).toHaveBeenCalledTimes(1);
+ expect(mockRegisterCiSchema).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('when `schema_linting` feature flag is off', () => {
+ beforeEach(() => {
+ createComponent();
+ findEditor().vm.$emit(EDITOR_READY_EVENT, editorInstanceDetail);
+ });
+
+ it('does not call the register CI schema function', () => {
+ expect(mockUse).not.toHaveBeenCalled();
+ expect(mockRegisterCiSchema).not.toHaveBeenCalled();
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/file-nav/branch_switcher_spec.js b/spec/frontend/ci/pipeline_editor/components/file-nav/branch_switcher_spec.js
new file mode 100644
index 00000000000..a26232df58f
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/file-nav/branch_switcher_spec.js
@@ -0,0 +1,432 @@
+import {
+ GlDropdown,
+ GlDropdownItem,
+ GlInfiniteScroll,
+ GlLoadingIcon,
+ GlSearchBoxByType,
+} from '@gitlab/ui';
+import { createLocalVue, mount, shallowMount } from '@vue/test-utils';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import BranchSwitcher from '~/ci/pipeline_editor/components/file_nav/branch_switcher.vue';
+import { DEFAULT_FAILURE } from '~/ci/pipeline_editor/constants';
+import getAvailableBranchesQuery from '~/ci/pipeline_editor/graphql/queries/available_branches.query.graphql';
+import getCurrentBranch from '~/ci/pipeline_editor/graphql/queries/client/current_branch.query.graphql';
+import getLastCommitBranch from '~/ci/pipeline_editor/graphql/queries/client/last_commit_branch.query.graphql';
+import { resolvers } from '~/ci/pipeline_editor/graphql/resolvers';
+
+import {
+ mockBranchPaginationLimit,
+ mockDefaultBranch,
+ mockEmptySearchBranches,
+ mockProjectBranches,
+ mockProjectFullPath,
+ mockSearchBranches,
+ mockTotalBranches,
+ mockTotalBranchResults,
+ mockTotalSearchResults,
+} from '../../mock_data';
+
+const localVue = createLocalVue();
+localVue.use(VueApollo);
+
+describe('Pipeline editor branch switcher', () => {
+ let wrapper;
+ let mockApollo;
+ let mockAvailableBranchQuery;
+
+ const createComponent = ({
+ currentBranch = mockDefaultBranch,
+ availableBranches = ['main'],
+ isQueryLoading = false,
+ mountFn = shallowMount,
+ options = {},
+ props = {},
+ } = {}) => {
+ wrapper = mountFn(BranchSwitcher, {
+ propsData: {
+ ...props,
+ paginationLimit: mockBranchPaginationLimit,
+ },
+ provide: {
+ projectFullPath: mockProjectFullPath,
+ totalBranches: mockTotalBranches,
+ },
+ mocks: {
+ $apollo: {
+ queries: {
+ availableBranches: {
+ loading: isQueryLoading,
+ },
+ },
+ },
+ },
+ data() {
+ return {
+ availableBranches,
+ currentBranch,
+ };
+ },
+ ...options,
+ });
+ };
+
+ const createComponentWithApollo = ({
+ mountFn = shallowMount,
+ props = {},
+ availableBranches = ['main'],
+ } = {}) => {
+ const handlers = [[getAvailableBranchesQuery, mockAvailableBranchQuery]];
+ mockApollo = createMockApollo(handlers, resolvers);
+
+ mockApollo.clients.defaultClient.cache.writeQuery({
+ query: getCurrentBranch,
+ data: {
+ workBranches: {
+ __typename: 'BranchList',
+ current: {
+ __typename: 'WorkBranch',
+ name: mockDefaultBranch,
+ },
+ },
+ },
+ });
+
+ mockApollo.clients.defaultClient.cache.writeQuery({
+ query: getLastCommitBranch,
+ data: {
+ workBranches: {
+ __typename: 'BranchList',
+ lastCommit: {
+ __typename: 'WorkBranch',
+ name: '',
+ },
+ },
+ },
+ });
+
+ createComponent({
+ mountFn,
+ props,
+ availableBranches,
+ options: {
+ localVue,
+ apolloProvider: mockApollo,
+ mocks: {},
+ },
+ });
+ };
+
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
+ const findInfiniteScroll = () => wrapper.findComponent(GlInfiniteScroll);
+ const defaultBranchInDropdown = () => findDropdownItems().at(0);
+
+ const setAvailableBranchesMock = (availableBranches) => {
+ mockAvailableBranchQuery.mockResolvedValue(availableBranches);
+ };
+
+ beforeEach(() => {
+ mockAvailableBranchQuery = jest.fn();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const testErrorHandling = () => {
+ expect(wrapper.emitted('showError')).toBeDefined();
+ expect(wrapper.emitted('showError')[0]).toEqual([
+ {
+ reasons: [wrapper.vm.$options.i18n.fetchError],
+ type: DEFAULT_FAILURE,
+ },
+ ]);
+ };
+
+ describe('when querying for the first time', () => {
+ beforeEach(() => {
+ createComponentWithApollo({ availableBranches: [] });
+ });
+
+ it('disables the dropdown', () => {
+ expect(findDropdown().props('disabled')).toBe(true);
+ });
+ });
+
+ describe('after querying', () => {
+ beforeEach(async () => {
+ setAvailableBranchesMock(mockProjectBranches);
+ createComponentWithApollo({ mountFn: mount });
+ await waitForPromises();
+ });
+
+ it('renders search box', () => {
+ expect(findSearchBox().exists()).toBe(true);
+ });
+
+ it('renders list of branches', () => {
+ expect(findDropdown().exists()).toBe(true);
+ expect(findDropdownItems()).toHaveLength(mockTotalBranchResults);
+ });
+
+ it('renders current branch with a check mark', () => {
+ expect(defaultBranchInDropdown().text()).toBe(mockDefaultBranch);
+ expect(defaultBranchInDropdown().props('isChecked')).toBe(true);
+ });
+
+ it('does not render check mark for other branches', () => {
+ const nonDefaultBranch = findDropdownItems().at(1);
+
+ expect(nonDefaultBranch.text()).not.toBe(mockDefaultBranch);
+ expect(nonDefaultBranch.props('isChecked')).toBe(false);
+ });
+ });
+
+ describe('on fetch error', () => {
+ beforeEach(async () => {
+ setAvailableBranchesMock(new Error());
+ createComponentWithApollo({ availableBranches: [] });
+ await waitForPromises();
+ });
+
+ it('does not render dropdown', () => {
+ expect(findDropdown().props('disabled')).toBe(true);
+ });
+
+ it('shows an error message', () => {
+ testErrorHandling();
+ });
+ });
+
+ describe('when switching branches', () => {
+ beforeEach(async () => {
+ jest.spyOn(window.history, 'pushState').mockImplementation(() => {});
+ setAvailableBranchesMock(mockProjectBranches);
+ createComponentWithApollo({ mountFn: mount });
+ await waitForPromises();
+ });
+
+ it('updates session history when selecting a different branch', async () => {
+ const branch = findDropdownItems().at(1);
+ branch.vm.$emit('click');
+ await waitForPromises();
+
+ expect(window.history.pushState).toHaveBeenCalled();
+ expect(window.history.pushState.mock.calls[0][2]).toContain(`?branch_name=${branch.text()}`);
+ });
+
+ it('does not update session history when selecting current branch', async () => {
+ const branch = findDropdownItems().at(0);
+ branch.vm.$emit('click');
+ await waitForPromises();
+
+ expect(branch.text()).toBe(mockDefaultBranch);
+ expect(window.history.pushState).not.toHaveBeenCalled();
+ });
+
+ it('emits the refetchContent event when selecting a different branch', async () => {
+ const branch = findDropdownItems().at(1);
+
+ expect(branch.text()).not.toBe(mockDefaultBranch);
+ expect(wrapper.emitted('refetchContent')).toBeUndefined();
+
+ branch.vm.$emit('click');
+ await waitForPromises();
+
+ expect(wrapper.emitted('refetchContent')).toBeDefined();
+ expect(wrapper.emitted('refetchContent')).toHaveLength(1);
+ });
+
+ it('does not emit the refetchContent event when selecting the current branch', async () => {
+ const branch = findDropdownItems().at(0);
+
+ expect(branch.text()).toBe(mockDefaultBranch);
+ expect(wrapper.emitted('refetchContent')).toBeUndefined();
+
+ branch.vm.$emit('click');
+ await waitForPromises();
+
+ expect(wrapper.emitted('refetchContent')).toBeUndefined();
+ });
+
+ describe('with unsaved changes', () => {
+ beforeEach(async () => {
+ createComponentWithApollo({ mountFn: mount, props: { hasUnsavedChanges: true } });
+ await waitForPromises();
+ });
+
+ it('emits `select-branch` event and does not switch branch', async () => {
+ expect(wrapper.emitted('select-branch')).toBeUndefined();
+
+ const branch = findDropdownItems().at(1);
+ await branch.vm.$emit('click');
+
+ expect(wrapper.emitted('select-branch')).toEqual([[branch.text()]]);
+ expect(wrapper.emitted('refetchContent')).toBeUndefined();
+ });
+ });
+ });
+
+ describe('when searching', () => {
+ beforeEach(async () => {
+ setAvailableBranchesMock(mockProjectBranches);
+ createComponentWithApollo({ mountFn: mount });
+ await waitForPromises();
+ });
+
+ afterEach(() => {
+ mockAvailableBranchQuery.mockClear();
+ });
+
+ it('shows error message on fetch error', async () => {
+ mockAvailableBranchQuery.mockResolvedValue(new Error());
+
+ findSearchBox().vm.$emit('input', 'te');
+ await waitForPromises();
+
+ testErrorHandling();
+ });
+
+ describe('with a search term', () => {
+ beforeEach(async () => {
+ mockAvailableBranchQuery.mockResolvedValue(mockSearchBranches);
+ });
+
+ it('calls query with correct variables', async () => {
+ findSearchBox().vm.$emit('input', 'te');
+ await waitForPromises();
+
+ expect(mockAvailableBranchQuery).toHaveBeenCalledWith({
+ limit: mockTotalBranches, // fetch all branches
+ offset: 0,
+ projectFullPath: mockProjectFullPath,
+ searchPattern: '*te*',
+ });
+ });
+
+ it('fetches new list of branches', async () => {
+ expect(findDropdownItems()).toHaveLength(mockTotalBranchResults);
+
+ findSearchBox().vm.$emit('input', 'te');
+ await waitForPromises();
+
+ expect(findDropdownItems()).toHaveLength(mockTotalSearchResults);
+ });
+
+ it('does not hide dropdown when search result is empty', async () => {
+ mockAvailableBranchQuery.mockResolvedValue(mockEmptySearchBranches);
+ findSearchBox().vm.$emit('input', 'aaaaa');
+ await waitForPromises();
+
+ expect(findDropdown().exists()).toBe(true);
+ expect(findDropdownItems()).toHaveLength(0);
+ });
+ });
+
+ describe('without a search term', () => {
+ beforeEach(async () => {
+ mockAvailableBranchQuery.mockResolvedValue(mockSearchBranches);
+ findSearchBox().vm.$emit('input', 'te');
+ await waitForPromises();
+
+ mockAvailableBranchQuery.mockResolvedValue(mockProjectBranches);
+ });
+
+ it('calls query with correct variables', async () => {
+ findSearchBox().vm.$emit('input', '');
+ await waitForPromises();
+
+ expect(mockAvailableBranchQuery).toHaveBeenCalledWith({
+ limit: mockBranchPaginationLimit, // only fetch first n branches first
+ offset: 0,
+ projectFullPath: mockProjectFullPath,
+ searchPattern: '*',
+ });
+ });
+
+ it('fetches new list of branches', async () => {
+ expect(findDropdownItems()).toHaveLength(mockTotalSearchResults);
+
+ findSearchBox().vm.$emit('input', '');
+ await waitForPromises();
+
+ expect(findDropdownItems()).toHaveLength(mockTotalBranchResults);
+ });
+ });
+ });
+
+ describe('loading icon', () => {
+ it.each`
+ isQueryLoading | isRendered
+ ${true} | ${true}
+ ${false} | ${false}
+ `('checks if query is loading before rendering', ({ isQueryLoading, isRendered }) => {
+ createComponent({ isQueryLoading, mountFn: mount });
+
+ expect(findLoadingIcon().exists()).toBe(isRendered);
+ });
+ });
+
+ describe('when scrolling to the bottom of the list', () => {
+ beforeEach(async () => {
+ setAvailableBranchesMock(mockProjectBranches);
+ createComponentWithApollo();
+ await waitForPromises();
+ });
+
+ afterEach(() => {
+ mockAvailableBranchQuery.mockClear();
+ });
+
+ describe('when search term is empty', () => {
+ it('fetches more branches', async () => {
+ expect(mockAvailableBranchQuery).toHaveBeenCalledTimes(1);
+
+ findInfiniteScroll().vm.$emit('bottomReached');
+ await waitForPromises();
+
+ expect(mockAvailableBranchQuery).toHaveBeenCalledTimes(2);
+ });
+
+ it('calls the query with the correct variables', async () => {
+ findInfiniteScroll().vm.$emit('bottomReached');
+ await waitForPromises();
+
+ expect(mockAvailableBranchQuery).toHaveBeenCalledWith({
+ limit: mockBranchPaginationLimit,
+ offset: mockBranchPaginationLimit, // offset changed
+ projectFullPath: mockProjectFullPath,
+ searchPattern: '*',
+ });
+ });
+
+ it('shows error message on fetch error', async () => {
+ mockAvailableBranchQuery.mockResolvedValue(new Error());
+
+ findInfiniteScroll().vm.$emit('bottomReached');
+ await waitForPromises();
+
+ testErrorHandling();
+ });
+ });
+
+ describe('when search term exists', () => {
+ it('does not fetch more branches', async () => {
+ findSearchBox().vm.$emit('input', 'te');
+ await waitForPromises();
+
+ expect(mockAvailableBranchQuery).toHaveBeenCalledTimes(2);
+ mockAvailableBranchQuery.mockClear();
+
+ findInfiniteScroll().vm.$emit('bottomReached');
+ await waitForPromises();
+
+ expect(mockAvailableBranchQuery).not.toHaveBeenCalled();
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/file-nav/pipeline_editor_file_nav_spec.js b/spec/frontend/ci/pipeline_editor/components/file-nav/pipeline_editor_file_nav_spec.js
new file mode 100644
index 00000000000..907db16913c
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/file-nav/pipeline_editor_file_nav_spec.js
@@ -0,0 +1,126 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { shallowMount } from '@vue/test-utils';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import BranchSwitcher from '~/ci/pipeline_editor/components/file_nav/branch_switcher.vue';
+import PipelineEditorFileNav from '~/ci/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue';
+import FileTreePopover from '~/ci/pipeline_editor/components/popovers/file_tree_popover.vue';
+import getAppStatus from '~/ci/pipeline_editor/graphql/queries/client/app_status.query.graphql';
+import {
+ EDITOR_APP_STATUS_EMPTY,
+ EDITOR_APP_STATUS_LOADING,
+ EDITOR_APP_STATUS_VALID,
+} from '~/ci/pipeline_editor/constants';
+
+Vue.use(VueApollo);
+
+describe('Pipeline editor file nav', () => {
+ let wrapper;
+
+ const mockApollo = createMockApollo();
+
+ const createComponent = ({
+ appStatus = EDITOR_APP_STATUS_VALID,
+ isNewCiConfigFile = false,
+ } = {}) => {
+ mockApollo.clients.defaultClient.cache.writeQuery({
+ query: getAppStatus,
+ data: {
+ app: {
+ __typename: 'PipelineEditorApp',
+ status: appStatus,
+ },
+ },
+ });
+
+ wrapper = extendedWrapper(
+ shallowMount(PipelineEditorFileNav, {
+ apolloProvider: mockApollo,
+ propsData: {
+ isNewCiConfigFile,
+ },
+ }),
+ );
+ };
+
+ const findBranchSwitcher = () => wrapper.findComponent(BranchSwitcher);
+ const findFileTreeBtn = () => wrapper.findByTestId('file-tree-toggle');
+ const findPopoverContainer = () => wrapper.findComponent(FileTreePopover);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('template', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders the branch switcher', () => {
+ expect(findBranchSwitcher().exists()).toBe(true);
+ });
+ });
+
+ describe('file tree', () => {
+ describe('when editor is in the empty state', () => {
+ beforeEach(() => {
+ createComponent({ appStatus: EDITOR_APP_STATUS_EMPTY, isNewCiConfigFile: false });
+ });
+
+ it('does not render the file tree button', () => {
+ expect(findFileTreeBtn().exists()).toBe(false);
+ });
+
+ it('does not render the file tree popover', () => {
+ expect(findPopoverContainer().exists()).toBe(false);
+ });
+ });
+
+ describe('when user is about to create their config file for the first time', () => {
+ beforeEach(() => {
+ createComponent({ appStatus: EDITOR_APP_STATUS_VALID, isNewCiConfigFile: true });
+ });
+
+ it('does not render the file tree button', () => {
+ expect(findFileTreeBtn().exists()).toBe(false);
+ });
+
+ it('does not render the file tree popover', () => {
+ expect(findPopoverContainer().exists()).toBe(false);
+ });
+ });
+
+ describe('when app is in a global loading state', () => {
+ it('renders the file tree button with a loading icon', () => {
+ createComponent({ appStatus: EDITOR_APP_STATUS_LOADING, isNewCiConfigFile: false });
+
+ expect(findFileTreeBtn().exists()).toBe(true);
+ expect(findFileTreeBtn().attributes('loading')).toBe('true');
+ });
+ });
+
+ describe('when editor has a non-empty config file open', () => {
+ beforeEach(() => {
+ createComponent({ appStatus: EDITOR_APP_STATUS_VALID, isNewCiConfigFile: false });
+ });
+
+ it('renders the file tree button', () => {
+ expect(findFileTreeBtn().exists()).toBe(true);
+ expect(findFileTreeBtn().props('icon')).toBe('file-tree');
+ });
+
+ it('renders the file tree popover', () => {
+ expect(findPopoverContainer().exists()).toBe(true);
+ });
+
+ it('file tree button emits toggle-file-tree event', () => {
+ expect(wrapper.emitted('toggle-file-tree')).toBe(undefined);
+
+ findFileTreeBtn().vm.$emit('click');
+
+ expect(wrapper.emitted('toggle-file-tree')).toHaveLength(1);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/file-tree/container_spec.js b/spec/frontend/ci/pipeline_editor/components/file-tree/container_spec.js
new file mode 100644
index 00000000000..11ba517e0eb
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/file-tree/container_spec.js
@@ -0,0 +1,138 @@
+import { nextTick } from 'vue';
+import { shallowMount } from '@vue/test-utils';
+import { GlAlert } from '@gitlab/ui';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import { createMockDirective } from 'helpers/vue_mock_directive';
+import PipelineEditorFileTreeContainer from '~/ci/pipeline_editor/components/file_tree/container.vue';
+import PipelineEditorFileTreeItem from '~/ci/pipeline_editor/components/file_tree/file_item.vue';
+import { FILE_TREE_TIP_DISMISSED_KEY } from '~/ci/pipeline_editor/constants';
+import { mockCiConfigPath, mockIncludes, mockIncludesHelpPagePath } from '../../mock_data';
+
+describe('Pipeline editor file nav', () => {
+ let wrapper;
+
+ const createComponent = ({ includes = mockIncludes, stubs } = {}) => {
+ wrapper = extendedWrapper(
+ shallowMount(PipelineEditorFileTreeContainer, {
+ provide: {
+ ciConfigPath: mockCiConfigPath,
+ includesHelpPagePath: mockIncludesHelpPagePath,
+ },
+ propsData: {
+ includes,
+ },
+ directives: {
+ GlTooltip: createMockDirective(),
+ },
+ stubs,
+ }),
+ );
+ };
+
+ const findTip = () => wrapper.findComponent(GlAlert);
+ const findCurrentConfigFilename = () => wrapper.findByTestId('current-config-filename');
+ const fileTreeItems = () => wrapper.findAllComponents(PipelineEditorFileTreeItem);
+
+ afterEach(() => {
+ localStorage.clear();
+ wrapper.destroy();
+ });
+
+ describe('template', () => {
+ beforeEach(() => {
+ createComponent({ stubs: { GlAlert } });
+ });
+
+ it('renders config file as a file item', () => {
+ expect(findCurrentConfigFilename().text()).toBe(mockCiConfigPath);
+ });
+ });
+
+ describe('when includes list is empty', () => {
+ describe('when dismiss state is not saved in local storage', () => {
+ beforeEach(() => {
+ createComponent({
+ includes: [],
+ stubs: { GlAlert },
+ });
+ });
+
+ it('does not render filenames', () => {
+ expect(fileTreeItems().exists()).toBe(false);
+ });
+
+ it('renders alert tip', async () => {
+ expect(findTip().exists()).toBe(true);
+ });
+
+ it('renders learn more link', async () => {
+ expect(findTip().props('secondaryButtonLink')).toBe(mockIncludesHelpPagePath);
+ });
+
+ it('can dismiss the tip', async () => {
+ expect(findTip().exists()).toBe(true);
+
+ findTip().vm.$emit('dismiss');
+ await nextTick();
+
+ expect(findTip().exists()).toBe(false);
+ });
+ });
+
+ describe('when dismiss state is saved in local storage', () => {
+ beforeEach(() => {
+ localStorage.setItem(FILE_TREE_TIP_DISMISSED_KEY, 'true');
+ createComponent({
+ includes: [],
+ stubs: { GlAlert },
+ });
+ });
+
+ it('does not render alert tip', async () => {
+ expect(findTip().exists()).toBe(false);
+ });
+ });
+
+ describe('when component receives new props with includes files', () => {
+ beforeEach(() => {
+ createComponent({ includes: [] });
+ });
+
+ it('hides tip and renders list of files', async () => {
+ expect(findTip().exists()).toBe(true);
+ expect(fileTreeItems()).toHaveLength(0);
+
+ await wrapper.setProps({ includes: mockIncludes });
+
+ expect(findTip().exists()).toBe(false);
+ expect(fileTreeItems()).toHaveLength(mockIncludes.length);
+ });
+ });
+ });
+
+ describe('when there are includes files', () => {
+ beforeEach(() => {
+ createComponent({ stubs: { GlAlert } });
+ });
+
+ it('does not render alert tip', () => {
+ expect(findTip().exists()).toBe(false);
+ });
+
+ it('renders the list of files', () => {
+ expect(fileTreeItems()).toHaveLength(mockIncludes.length);
+ });
+
+ describe('when component receives new props with empty includes', () => {
+ it('shows tip and does not render list of files', async () => {
+ expect(findTip().exists()).toBe(false);
+ expect(fileTreeItems()).toHaveLength(mockIncludes.length);
+
+ await wrapper.setProps({ includes: [] });
+
+ expect(findTip().exists()).toBe(true);
+ expect(fileTreeItems()).toHaveLength(0);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/file-tree/file_item_spec.js b/spec/frontend/ci/pipeline_editor/components/file-tree/file_item_spec.js
new file mode 100644
index 00000000000..bceb741f91c
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/file-tree/file_item_spec.js
@@ -0,0 +1,52 @@
+import { GlLink } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import FileIcon from '~/vue_shared/components/file_icon.vue';
+import PipelineEditorFileTreeItem from '~/ci/pipeline_editor/components/file_tree/file_item.vue';
+import { mockIncludesWithBlob, mockDefaultIncludes } from '../../mock_data';
+
+describe('Pipeline editor file nav', () => {
+ let wrapper;
+
+ const createComponent = ({ file = mockDefaultIncludes } = {}) => {
+ wrapper = shallowMount(PipelineEditorFileTreeItem, {
+ propsData: {
+ file,
+ },
+ });
+ };
+
+ const fileIcon = () => wrapper.findComponent(FileIcon);
+ const link = () => wrapper.findComponent(GlLink);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('template', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders file icon', () => {
+ expect(fileIcon().exists()).toBe(true);
+ });
+
+ it('renders file name', () => {
+ expect(wrapper.text()).toBe(mockDefaultIncludes.location);
+ });
+
+ it('links to raw path by default', () => {
+ expect(link().attributes('href')).toBe(mockDefaultIncludes.raw);
+ });
+ });
+
+ describe('when file has blob link', () => {
+ beforeEach(() => {
+ createComponent({ file: mockIncludesWithBlob });
+ });
+
+ it('links to blob path', () => {
+ expect(link().attributes('href')).toBe(mockIncludesWithBlob.blob);
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/header/pipeline_editor_header_spec.js b/spec/frontend/ci/pipeline_editor/components/header/pipeline_editor_header_spec.js
new file mode 100644
index 00000000000..555b9f29fbf
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/header/pipeline_editor_header_spec.js
@@ -0,0 +1,53 @@
+import { shallowMount } from '@vue/test-utils';
+import PipelineEditorHeader from '~/ci/pipeline_editor/components/header/pipeline_editor_header.vue';
+import PipelineStatus from '~/ci/pipeline_editor/components/header/pipeline_status.vue';
+import ValidationSegment from '~/ci/pipeline_editor/components/header/validation_segment.vue';
+
+import { mockCiYml, mockLintResponse } from '../../mock_data';
+
+describe('Pipeline editor header', () => {
+ let wrapper;
+
+ const createComponent = ({ provide = {}, props = {} } = {}) => {
+ wrapper = shallowMount(PipelineEditorHeader, {
+ provide: {
+ ...provide,
+ },
+ propsData: {
+ ciConfigData: mockLintResponse,
+ ciFileContent: mockCiYml,
+ isCiConfigDataLoading: false,
+ isNewCiConfigFile: false,
+ ...props,
+ },
+ });
+ };
+
+ const findPipelineStatus = () => wrapper.findComponent(PipelineStatus);
+ const findValidationSegment = () => wrapper.findComponent(ValidationSegment);
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('template', () => {
+ it('hides the pipeline status for new projects without a CI file', () => {
+ createComponent({ props: { isNewCiConfigFile: true } });
+
+ expect(findPipelineStatus().exists()).toBe(false);
+ });
+
+ it('renders the pipeline status when CI file exists', () => {
+ createComponent({ props: { isNewCiConfigFile: false } });
+
+ expect(findPipelineStatus().exists()).toBe(true);
+ });
+
+ it('renders the validation segment', () => {
+ createComponent();
+
+ expect(findValidationSegment().exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js b/spec/frontend/ci/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js
new file mode 100644
index 00000000000..6f28362e478
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js
@@ -0,0 +1,109 @@
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import PipelineEditorMiniGraph from '~/ci/pipeline_editor/components/header/pipeline_editor_mini_graph.vue';
+import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
+import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql';
+import { PIPELINE_FAILURE } from '~/ci/pipeline_editor/constants';
+import { mockLinkedPipelines, mockProjectFullPath, mockProjectPipeline } from '../../mock_data';
+
+Vue.use(VueApollo);
+
+describe('Pipeline Status', () => {
+ let wrapper;
+ let mockApollo;
+ let mockLinkedPipelinesQuery;
+
+ const createComponent = ({ hasStages = true, options } = {}) => {
+ wrapper = shallowMount(PipelineEditorMiniGraph, {
+ provide: {
+ dataMethod: 'graphql',
+ projectFullPath: mockProjectFullPath,
+ },
+ propsData: {
+ pipeline: mockProjectPipeline({ hasStages }).pipeline,
+ },
+ ...options,
+ });
+ };
+
+ const createComponentWithApollo = (hasStages = true) => {
+ const handlers = [[getLinkedPipelinesQuery, mockLinkedPipelinesQuery]];
+ mockApollo = createMockApollo(handlers);
+
+ createComponent({
+ hasStages,
+ options: {
+ apolloProvider: mockApollo,
+ },
+ });
+ };
+
+ const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph);
+
+ beforeEach(() => {
+ mockLinkedPipelinesQuery = jest.fn();
+ });
+
+ afterEach(() => {
+ mockLinkedPipelinesQuery.mockReset();
+ wrapper.destroy();
+ });
+
+ describe('when there are stages', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders pipeline mini graph', () => {
+ expect(findPipelineMiniGraph().exists()).toBe(true);
+ });
+ });
+
+ describe('when there are no stages', () => {
+ beforeEach(() => {
+ createComponent({ hasStages: false });
+ });
+
+ it('does not render pipeline mini graph', () => {
+ expect(findPipelineMiniGraph().exists()).toBe(false);
+ });
+ });
+
+ describe('when querying upstream and downstream pipelines', () => {
+ describe('when query succeeds', () => {
+ beforeEach(() => {
+ mockLinkedPipelinesQuery.mockResolvedValue(mockLinkedPipelines());
+ createComponentWithApollo();
+ });
+
+ it('should call the query with the correct variables', () => {
+ expect(mockLinkedPipelinesQuery).toHaveBeenCalledTimes(1);
+ expect(mockLinkedPipelinesQuery).toHaveBeenCalledWith({
+ fullPath: mockProjectFullPath,
+ iid: mockProjectPipeline().pipeline.iid,
+ });
+ });
+ });
+
+ describe('when query fails', () => {
+ beforeEach(async () => {
+ mockLinkedPipelinesQuery.mockRejectedValue(new Error());
+ createComponentWithApollo();
+ await waitForPromises();
+ });
+
+ it('should emit an error event when query fails', async () => {
+ expect(wrapper.emitted('showError')).toHaveLength(1);
+ expect(wrapper.emitted('showError')[0]).toEqual([
+ {
+ type: PIPELINE_FAILURE,
+ reasons: [wrapper.vm.$options.i18n.linkedPipelinesFetchError],
+ },
+ ]);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/header/pipeline_status_spec.js b/spec/frontend/ci/pipeline_editor/components/header/pipeline_status_spec.js
new file mode 100644
index 00000000000..a62c51ffb59
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/header/pipeline_status_spec.js
@@ -0,0 +1,132 @@
+import { GlIcon, GlLink, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import PipelineStatus, { i18n } from '~/ci/pipeline_editor/components/header/pipeline_status.vue';
+import getPipelineQuery from '~/ci/pipeline_editor/graphql/queries/pipeline.query.graphql';
+import PipelineEditorMiniGraph from '~/ci/pipeline_editor/components/header/pipeline_editor_mini_graph.vue';
+import { mockCommitSha, mockProjectPipeline, mockProjectFullPath } from '../../mock_data';
+
+Vue.use(VueApollo);
+
+describe('Pipeline Status', () => {
+ let wrapper;
+ let mockApollo;
+ let mockPipelineQuery;
+
+ const createComponentWithApollo = () => {
+ const handlers = [[getPipelineQuery, mockPipelineQuery]];
+ mockApollo = createMockApollo(handlers);
+
+ wrapper = shallowMount(PipelineStatus, {
+ apolloProvider: mockApollo,
+ propsData: {
+ commitSha: mockCommitSha,
+ },
+ provide: {
+ projectFullPath: mockProjectFullPath,
+ },
+ stubs: { GlLink, GlSprintf },
+ });
+ };
+
+ const findIcon = () => wrapper.findComponent(GlIcon);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findPipelineEditorMiniGraph = () => wrapper.findComponent(PipelineEditorMiniGraph);
+ const findPipelineId = () => wrapper.find('[data-testid="pipeline-id"]');
+ const findPipelineCommit = () => wrapper.find('[data-testid="pipeline-commit"]');
+ const findPipelineErrorMsg = () => wrapper.find('[data-testid="pipeline-error-msg"]');
+ const findPipelineLoadingMsg = () => wrapper.find('[data-testid="pipeline-loading-msg"]');
+ const findPipelineViewBtn = () => wrapper.find('[data-testid="pipeline-view-btn"]');
+ const findStatusIcon = () => wrapper.find('[data-testid="pipeline-status-icon"]');
+
+ beforeEach(() => {
+ mockPipelineQuery = jest.fn();
+ });
+
+ afterEach(() => {
+ mockPipelineQuery.mockReset();
+ wrapper.destroy();
+ });
+
+ describe('loading icon', () => {
+ it('renders while query is being fetched', () => {
+ createComponentWithApollo();
+
+ expect(findLoadingIcon().exists()).toBe(true);
+ expect(findPipelineLoadingMsg().text()).toBe(i18n.fetchLoading);
+ });
+
+ it('does not render if query is no longer loading', async () => {
+ createComponentWithApollo();
+ await waitForPromises();
+
+ expect(findLoadingIcon().exists()).toBe(false);
+ });
+ });
+
+ describe('when querying data', () => {
+ describe('when data is set', () => {
+ beforeEach(async () => {
+ mockPipelineQuery.mockResolvedValue({
+ data: { project: mockProjectPipeline() },
+ });
+
+ createComponentWithApollo();
+ await waitForPromises();
+ });
+
+ it('query is called with correct variables', async () => {
+ expect(mockPipelineQuery).toHaveBeenCalledTimes(1);
+ expect(mockPipelineQuery).toHaveBeenCalledWith({
+ fullPath: mockProjectFullPath,
+ sha: mockCommitSha,
+ });
+ });
+
+ it('does not render error', () => {
+ expect(findPipelineErrorMsg().exists()).toBe(false);
+ });
+
+ it('renders pipeline data', () => {
+ const {
+ id,
+ commit: { title },
+ detailedStatus: { detailsPath },
+ } = mockProjectPipeline().pipeline;
+
+ expect(findStatusIcon().exists()).toBe(true);
+ expect(findPipelineId().text()).toBe(`#${id.match(/\d+/g)[0]}`);
+ expect(findPipelineCommit().text()).toBe(`${mockCommitSha}: ${title}`);
+ expect(findPipelineViewBtn().attributes('href')).toBe(detailsPath);
+ });
+
+ it('renders the pipeline mini graph', () => {
+ expect(findPipelineEditorMiniGraph().exists()).toBe(true);
+ });
+ });
+
+ describe('when data cannot be fetched', () => {
+ beforeEach(async () => {
+ mockPipelineQuery.mockRejectedValue(new Error());
+
+ createComponentWithApollo();
+ await waitForPromises();
+ });
+
+ it('renders error', () => {
+ expect(findIcon().attributes('name')).toBe('warning-solid');
+ expect(findPipelineErrorMsg().text()).toBe(i18n.fetchError);
+ });
+
+ it('does not render pipeline data', () => {
+ expect(findStatusIcon().exists()).toBe(false);
+ expect(findPipelineId().exists()).toBe(false);
+ expect(findPipelineCommit().exists()).toBe(false);
+ expect(findPipelineViewBtn().exists()).toBe(false);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/header/pipline_editor_mini_graph_spec.js b/spec/frontend/ci/pipeline_editor/components/header/pipline_editor_mini_graph_spec.js
new file mode 100644
index 00000000000..6f28362e478
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/header/pipline_editor_mini_graph_spec.js
@@ -0,0 +1,109 @@
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import PipelineEditorMiniGraph from '~/ci/pipeline_editor/components/header/pipeline_editor_mini_graph.vue';
+import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
+import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql';
+import { PIPELINE_FAILURE } from '~/ci/pipeline_editor/constants';
+import { mockLinkedPipelines, mockProjectFullPath, mockProjectPipeline } from '../../mock_data';
+
+Vue.use(VueApollo);
+
+describe('Pipeline Status', () => {
+ let wrapper;
+ let mockApollo;
+ let mockLinkedPipelinesQuery;
+
+ const createComponent = ({ hasStages = true, options } = {}) => {
+ wrapper = shallowMount(PipelineEditorMiniGraph, {
+ provide: {
+ dataMethod: 'graphql',
+ projectFullPath: mockProjectFullPath,
+ },
+ propsData: {
+ pipeline: mockProjectPipeline({ hasStages }).pipeline,
+ },
+ ...options,
+ });
+ };
+
+ const createComponentWithApollo = (hasStages = true) => {
+ const handlers = [[getLinkedPipelinesQuery, mockLinkedPipelinesQuery]];
+ mockApollo = createMockApollo(handlers);
+
+ createComponent({
+ hasStages,
+ options: {
+ apolloProvider: mockApollo,
+ },
+ });
+ };
+
+ const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph);
+
+ beforeEach(() => {
+ mockLinkedPipelinesQuery = jest.fn();
+ });
+
+ afterEach(() => {
+ mockLinkedPipelinesQuery.mockReset();
+ wrapper.destroy();
+ });
+
+ describe('when there are stages', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders pipeline mini graph', () => {
+ expect(findPipelineMiniGraph().exists()).toBe(true);
+ });
+ });
+
+ describe('when there are no stages', () => {
+ beforeEach(() => {
+ createComponent({ hasStages: false });
+ });
+
+ it('does not render pipeline mini graph', () => {
+ expect(findPipelineMiniGraph().exists()).toBe(false);
+ });
+ });
+
+ describe('when querying upstream and downstream pipelines', () => {
+ describe('when query succeeds', () => {
+ beforeEach(() => {
+ mockLinkedPipelinesQuery.mockResolvedValue(mockLinkedPipelines());
+ createComponentWithApollo();
+ });
+
+ it('should call the query with the correct variables', () => {
+ expect(mockLinkedPipelinesQuery).toHaveBeenCalledTimes(1);
+ expect(mockLinkedPipelinesQuery).toHaveBeenCalledWith({
+ fullPath: mockProjectFullPath,
+ iid: mockProjectPipeline().pipeline.iid,
+ });
+ });
+ });
+
+ describe('when query fails', () => {
+ beforeEach(async () => {
+ mockLinkedPipelinesQuery.mockRejectedValue(new Error());
+ createComponentWithApollo();
+ await waitForPromises();
+ });
+
+ it('should emit an error event when query fails', async () => {
+ expect(wrapper.emitted('showError')).toHaveLength(1);
+ expect(wrapper.emitted('showError')[0]).toEqual([
+ {
+ type: PIPELINE_FAILURE,
+ reasons: [wrapper.vm.$options.i18n.linkedPipelinesFetchError],
+ },
+ ]);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/header/validation_segment_spec.js b/spec/frontend/ci/pipeline_editor/components/header/validation_segment_spec.js
new file mode 100644
index 00000000000..0853a6f4ca4
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/header/validation_segment_spec.js
@@ -0,0 +1,197 @@
+import VueApollo from 'vue-apollo';
+import { GlIcon } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import { escape } from 'lodash';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { sprintf } from '~/locale';
+import ValidationSegment, {
+ i18n,
+} from '~/ci/pipeline_editor/components/header/validation_segment.vue';
+import getAppStatus from '~/ci/pipeline_editor/graphql/queries/client/app_status.query.graphql';
+import {
+ CI_CONFIG_STATUS_INVALID,
+ EDITOR_APP_STATUS_EMPTY,
+ EDITOR_APP_STATUS_INVALID,
+ EDITOR_APP_STATUS_LOADING,
+ EDITOR_APP_STATUS_LINT_UNAVAILABLE,
+ EDITOR_APP_STATUS_VALID,
+} from '~/ci/pipeline_editor/constants';
+import {
+ mergeUnwrappedCiConfig,
+ mockCiYml,
+ mockLintUnavailableHelpPagePath,
+ mockYmlHelpPagePath,
+} from '../../mock_data';
+
+Vue.use(VueApollo);
+
+describe('Validation segment component', () => {
+ let wrapper;
+
+ const mockApollo = createMockApollo();
+
+ const createComponent = ({ props = {}, appStatus = EDITOR_APP_STATUS_INVALID }) => {
+ mockApollo.clients.defaultClient.cache.writeQuery({
+ query: getAppStatus,
+ data: {
+ app: {
+ __typename: 'PipelineEditorApp',
+ status: appStatus,
+ },
+ },
+ });
+
+ wrapper = extendedWrapper(
+ shallowMount(ValidationSegment, {
+ apolloProvider: mockApollo,
+ provide: {
+ ymlHelpPagePath: mockYmlHelpPagePath,
+ lintUnavailableHelpPagePath: mockLintUnavailableHelpPagePath,
+ },
+ propsData: {
+ ciConfig: mergeUnwrappedCiConfig(),
+ ciFileContent: mockCiYml,
+ ...props,
+ },
+ }),
+ );
+ };
+
+ const findIcon = () => wrapper.findComponent(GlIcon);
+ const findLearnMoreLink = () => wrapper.findByTestId('learnMoreLink');
+ const findValidationMsg = () => wrapper.findByTestId('validationMsg');
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('shows the loading state', () => {
+ createComponent({ appStatus: EDITOR_APP_STATUS_LOADING });
+
+ expect(wrapper.text()).toBe(i18n.loading);
+ });
+
+ describe('when config is empty', () => {
+ beforeEach(() => {
+ createComponent({ appStatus: EDITOR_APP_STATUS_EMPTY });
+ });
+
+ it('has check icon', () => {
+ expect(findIcon().props('name')).toBe('check');
+ });
+
+ it('shows a message for empty state', () => {
+ expect(findValidationMsg().text()).toBe(i18n.empty);
+ });
+ });
+
+ describe('when config is valid', () => {
+ beforeEach(() => {
+ createComponent({ appStatus: EDITOR_APP_STATUS_VALID });
+ });
+
+ it('has check icon', () => {
+ expect(findIcon().props('name')).toBe('check');
+ });
+
+ it('shows a message for valid state', () => {
+ expect(findValidationMsg().text()).toContain(i18n.valid);
+ });
+
+ it('shows the learn more link', () => {
+ expect(findLearnMoreLink().attributes('href')).toBe(mockYmlHelpPagePath);
+ expect(findLearnMoreLink().text()).toBe(i18n.learnMore);
+ });
+ });
+
+ describe('when config is invalid', () => {
+ beforeEach(() => {
+ createComponent({
+ appStatus: EDITOR_APP_STATUS_INVALID,
+ });
+ });
+
+ it('has warning icon', () => {
+ expect(findIcon().props('name')).toBe('warning-solid');
+ });
+
+ it('has message for invalid state', () => {
+ expect(findValidationMsg().text()).toBe(i18n.invalid);
+ });
+
+ it('shows the learn more link', () => {
+ expect(findLearnMoreLink().attributes('href')).toBe(mockYmlHelpPagePath);
+ expect(findLearnMoreLink().text()).toBe('Learn more');
+ });
+
+ describe('with multiple errors', () => {
+ const firstError = 'First Error';
+ const secondError = 'Second Error';
+
+ beforeEach(() => {
+ createComponent({
+ props: {
+ ciConfig: mergeUnwrappedCiConfig({
+ status: CI_CONFIG_STATUS_INVALID,
+ errors: [firstError, secondError],
+ }),
+ },
+ });
+ });
+ it('shows an invalid state with an error', () => {
+ // Test the error is shown _and_ the string matches
+ expect(findValidationMsg().text()).toContain(firstError);
+ expect(findValidationMsg().text()).toBe(
+ sprintf(i18n.invalidWithReason, { reason: firstError }),
+ );
+ });
+ });
+
+ describe('with XSS inside the error', () => {
+ const evilError = '<script>evil();</script>';
+
+ beforeEach(() => {
+ createComponent({
+ props: {
+ ciConfig: mergeUnwrappedCiConfig({
+ status: CI_CONFIG_STATUS_INVALID,
+ errors: [evilError],
+ }),
+ },
+ });
+ });
+ it('shows an invalid state with an error while preventing XSS', () => {
+ const { innerHTML } = findValidationMsg().element;
+
+ expect(innerHTML).not.toContain(evilError);
+ expect(innerHTML).toContain(escape(evilError));
+ });
+ });
+ });
+
+ describe('when the lint service is unavailable', () => {
+ beforeEach(() => {
+ createComponent({
+ appStatus: EDITOR_APP_STATUS_LINT_UNAVAILABLE,
+ props: {
+ ciConfig: {},
+ },
+ });
+ });
+
+ it('show a message that the service is unavailable', () => {
+ expect(findValidationMsg().text()).toBe(i18n.unavailableValidation);
+ });
+
+ it('shows the time-out icon', () => {
+ expect(findIcon().props('name')).toBe('time-out');
+ });
+
+ it('shows the learn more link', () => {
+ expect(findLearnMoreLink().attributes('href')).toBe(mockLintUnavailableHelpPagePath);
+ expect(findLearnMoreLink().text()).toBe(i18n.learnMore);
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/lint/ci_lint_results_spec.js b/spec/frontend/ci/pipeline_editor/components/lint/ci_lint_results_spec.js
new file mode 100644
index 00000000000..d43bdec3a33
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/lint/ci_lint_results_spec.js
@@ -0,0 +1,177 @@
+import { GlTableLite, GlLink } from '@gitlab/ui';
+import { shallowMount, mount } from '@vue/test-utils';
+import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
+import CiLintResults from '~/ci/pipeline_editor/components/lint/ci_lint_results.vue';
+import { mockJobs, mockErrors, mockWarnings } from '../../mock_data';
+
+describe('CI Lint Results', () => {
+ let wrapper;
+ const defaultProps = {
+ isValid: true,
+ jobs: mockJobs,
+ errors: [],
+ warnings: [],
+ dryRun: false,
+ lintHelpPagePath: '/help',
+ };
+
+ const createComponent = (props = {}, mountFn = shallowMount) => {
+ wrapper = mountFn(CiLintResults, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ });
+ };
+
+ const findTable = () => wrapper.findComponent(GlTableLite);
+ const findByTestId = (selector) => () => wrapper.find(`[data-testid="ci-lint-${selector}"]`);
+ const findAllByTestId = (selector) => () =>
+ wrapper.findAll(`[data-testid="ci-lint-${selector}"]`);
+ const findLinkToDoc = () => wrapper.findComponent(GlLink);
+ const findErrors = findByTestId('errors');
+ const findWarnings = findByTestId('warnings');
+ const findStatus = findByTestId('status');
+ const findOnlyExcept = findByTestId('only-except');
+ const findLintParameters = findAllByTestId('parameter');
+ const findLintValues = findAllByTestId('value');
+ const findBeforeScripts = findAllByTestId('before-script');
+ const findScripts = findAllByTestId('script');
+ const findAfterScripts = findAllByTestId('after-script');
+ const filterEmptyScripts = (property) => mockJobs.filter((job) => job[property].length !== 0);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('Empty results', () => {
+ it('renders with no jobs, errors or warnings defined', () => {
+ createComponent({ jobs: undefined, errors: undefined, warnings: undefined }, shallowMount);
+ expect(findTable().exists()).toBe(true);
+ });
+
+ it('renders when job has no properties defined', () => {
+ // job with no attributes such as `tagList` or `environment`
+ const job = {
+ stage: 'Stage Name',
+ name: 'test job',
+ };
+ createComponent({ jobs: [job] }, mount);
+
+ const param = findLintParameters().at(0);
+ const value = findLintValues().at(0);
+
+ expect(param.text()).toBe(`${job.stage} Job - ${job.name}`);
+
+ // This test should be updated once properties of each job are shown
+ // See https://gitlab.com/gitlab-org/gitlab/-/issues/291031
+ expect(value.text()).toBe('');
+ });
+ });
+
+ describe('Invalid results', () => {
+ beforeEach(() => {
+ createComponent({ isValid: false, errors: mockErrors, warnings: mockWarnings }, mount);
+ });
+
+ it('does not display the table', () => {
+ expect(findTable().exists()).toBe(false);
+ });
+
+ it('displays the invalid status', () => {
+ expect(findStatus().text()).toContain(`Status: ${wrapper.vm.$options.incorrect.text}`);
+ expect(findStatus().props('variant')).toBe(wrapper.vm.$options.incorrect.variant);
+ });
+
+ it('contains the link to documentation', () => {
+ expect(findLinkToDoc().text()).toBe('More information');
+ expect(findLinkToDoc().attributes('href')).toBe(defaultProps.lintHelpPagePath);
+ });
+
+ it('displays the error message', () => {
+ const [expectedError] = mockErrors;
+
+ expect(findErrors().text()).toBe(expectedError);
+ });
+
+ it('displays the warning message', () => {
+ const [expectedWarning] = mockWarnings;
+
+ expect(findWarnings().exists()).toBe(true);
+ expect(findWarnings().text()).toContain(expectedWarning);
+ });
+ });
+
+ describe('Valid results with dry run', () => {
+ beforeEach(() => {
+ createComponent({ dryRun: true }, mount);
+ });
+
+ it('displays table', () => {
+ expect(findTable().exists()).toBe(true);
+ });
+
+ it('displays the valid status', () => {
+ expect(findStatus().text()).toContain(wrapper.vm.$options.correct.text);
+ expect(findStatus().props('variant')).toBe(wrapper.vm.$options.correct.variant);
+ });
+
+ it('does not display only/expect values with dry run', () => {
+ expect(findOnlyExcept().exists()).toBe(false);
+ });
+
+ it('contains the link to documentation', () => {
+ expect(findLinkToDoc().text()).toBe('More information');
+ expect(findLinkToDoc().attributes('href')).toBe(defaultProps.lintHelpPagePath);
+ });
+ });
+
+ describe('Lint results', () => {
+ beforeEach(() => {
+ createComponent({}, mount);
+ });
+
+ it('formats parameter value', () => {
+ findLintParameters().wrappers.forEach((job, index) => {
+ const { stage } = mockJobs[index];
+ const { name } = mockJobs[index];
+
+ expect(job.text()).toBe(`${capitalizeFirstCharacter(stage)} Job - ${name}`);
+ });
+ });
+
+ it('only shows before scripts when data is present', () => {
+ expect(findBeforeScripts()).toHaveLength(filterEmptyScripts('beforeScript').length);
+ });
+
+ it('only shows script when data is present', () => {
+ expect(findScripts()).toHaveLength(filterEmptyScripts('script').length);
+ });
+
+ it('only shows after script when data is present', () => {
+ expect(findAfterScripts()).toHaveLength(filterEmptyScripts('afterScript').length);
+ });
+ });
+
+ describe('Hide Alert', () => {
+ it('hides alert on success if hide-alert prop is true', async () => {
+ await createComponent({ dryRun: true, hideAlert: true }, mount);
+
+ expect(findStatus().exists()).toBe(false);
+ });
+
+ it('hides alert on error if hide-alert prop is true', async () => {
+ await createComponent(
+ {
+ hideAlert: true,
+ isValid: false,
+ errors: mockErrors,
+ warnings: mockWarnings,
+ },
+ mount,
+ );
+
+ expect(findStatus().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/lint/ci_lint_warnings_spec.js b/spec/frontend/ci/pipeline_editor/components/lint/ci_lint_warnings_spec.js
new file mode 100644
index 00000000000..b5e3ea06c2c
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/lint/ci_lint_warnings_spec.js
@@ -0,0 +1,54 @@
+import { GlAlert, GlSprintf } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import { trimText } from 'helpers/text_helper';
+import CiLintWarnings from '~/ci/pipeline_editor/components/lint/ci_lint_warnings.vue';
+
+const warnings = ['warning 1', 'warning 2', 'warning 3'];
+
+describe('CI lint warnings', () => {
+ let wrapper;
+
+ const createComponent = (limit = 25) => {
+ wrapper = mount(CiLintWarnings, {
+ propsData: {
+ warnings,
+ maxWarnings: limit,
+ },
+ });
+ };
+
+ const findWarningAlert = () => wrapper.findComponent(GlAlert);
+ const findWarnings = () => wrapper.findAll('[data-testid="ci-lint-warning"]');
+ const findWarningMessage = () => trimText(wrapper.findComponent(GlSprintf).text());
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('displays the warning alert', () => {
+ createComponent();
+
+ expect(findWarningAlert().exists()).toBe(true);
+ });
+
+ it('displays all the warnings', () => {
+ createComponent();
+
+ expect(findWarnings()).toHaveLength(warnings.length);
+ });
+
+ it('shows the correct message when the limit is not passed', () => {
+ createComponent();
+
+ expect(findWarningMessage()).toBe(`${warnings.length} warnings found:`);
+ });
+
+ it('shows the correct message when the limit is passed', () => {
+ const limit = 2;
+
+ createComponent(limit);
+
+ expect(findWarningMessage()).toBe(`${warnings.length} warnings found: showing first ${limit}`);
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/pipeline_editor_tabs_spec.js b/spec/frontend/ci/pipeline_editor/components/pipeline_editor_tabs_spec.js
new file mode 100644
index 00000000000..70310cbdb10
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/pipeline_editor_tabs_spec.js
@@ -0,0 +1,342 @@
+// TODO
+
+import { GlAlert, GlBadge, GlLoadingIcon, GlTabs } from '@gitlab/ui';
+import { mount, shallowMount } from '@vue/test-utils';
+import VueApollo from 'vue-apollo';
+import Vue, { nextTick } from 'vue';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import setWindowLocation from 'helpers/set_window_location_helper';
+import CiConfigMergedPreview from '~/ci/pipeline_editor/components/editor/ci_config_merged_preview.vue';
+import CiValidate from '~/ci/pipeline_editor/components/validate/ci_validate.vue';
+import WalkthroughPopover from '~/ci/pipeline_editor/components/popovers/walkthrough_popover.vue';
+import PipelineEditorTabs from '~/ci/pipeline_editor/components/pipeline_editor_tabs.vue';
+import EditorTab from '~/ci/pipeline_editor/components/ui/editor_tab.vue';
+import {
+ CREATE_TAB,
+ EDITOR_APP_STATUS_EMPTY,
+ EDITOR_APP_STATUS_LOADING,
+ EDITOR_APP_STATUS_INVALID,
+ EDITOR_APP_STATUS_VALID,
+ TAB_QUERY_PARAM,
+ VALIDATE_TAB,
+ VALIDATE_TAB_BADGE_DISMISSED_KEY,
+} from '~/ci/pipeline_editor/constants';
+import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
+import getBlobContent from '~/ci/pipeline_editor/graphql/queries/blob_content.query.graphql';
+import {
+ mockBlobContentQueryResponse,
+ mockCiLintPath,
+ mockCiYml,
+ mockLintResponse,
+ mockLintResponseWithoutMerged,
+} from '../mock_data';
+
+Vue.use(VueApollo);
+
+Vue.config.ignoredElements = ['gl-emoji'];
+
+describe('Pipeline editor tabs component', () => {
+ let wrapper;
+ const MockTextEditor = {
+ template: '<div />',
+ };
+
+ const createComponent = ({
+ listeners = {},
+ props = {},
+ provide = {},
+ appStatus = EDITOR_APP_STATUS_VALID,
+ mountFn = shallowMount,
+ options = {},
+ } = {}) => {
+ wrapper = mountFn(PipelineEditorTabs, {
+ propsData: {
+ ciConfigData: mockLintResponse,
+ ciFileContent: mockCiYml,
+ currentTab: CREATE_TAB,
+ isNewCiConfigFile: true,
+ showDrawer: false,
+ ...props,
+ },
+ data() {
+ return {
+ appStatus,
+ };
+ },
+ provide: {
+ ciConfigPath: '/path/to/ci-config',
+ ciLintPath: mockCiLintPath,
+ currentBranch: 'main',
+ projectFullPath: '/path/to/project',
+ simulatePipelineHelpPagePath: 'path/to/help/page',
+ validateTabIllustrationPath: 'path/to/svg',
+ ...provide,
+ },
+ stubs: {
+ TextEditor: MockTextEditor,
+ EditorTab,
+ },
+ listeners,
+ ...options,
+ });
+ };
+
+ let mockBlobContentData;
+ let mockApollo;
+
+ const createComponentWithApollo = ({ props, provide = {}, mountFn = shallowMount } = {}) => {
+ const handlers = [[getBlobContent, mockBlobContentData]];
+ mockApollo = createMockApollo(handlers);
+
+ createComponent({
+ props,
+ provide,
+ mountFn,
+ options: {
+ apolloProvider: mockApollo,
+ },
+ });
+ };
+
+ const findEditorTab = () => wrapper.find('[data-testid="editor-tab"]');
+ const findMergedTab = () => wrapper.find('[data-testid="merged-tab"]');
+ const findValidateTab = () => wrapper.find('[data-testid="validate-tab"]');
+ const findVisualizationTab = () => wrapper.find('[data-testid="visualization-tab"]');
+
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findBadge = () => wrapper.findComponent(GlBadge);
+ const findCiValidate = () => wrapper.findComponent(CiValidate);
+ const findGlTabs = () => wrapper.findComponent(GlTabs);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findPipelineGraph = () => wrapper.findComponent(PipelineGraph);
+ const findTextEditor = () => wrapper.findComponent(MockTextEditor);
+ const findMergedPreview = () => wrapper.findComponent(CiConfigMergedPreview);
+ const findWalkthroughPopover = () => wrapper.findComponent(WalkthroughPopover);
+
+ beforeEach(() => {
+ mockBlobContentData = jest.fn();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('editor tab', () => {
+ it('displays editor only after the tab is mounted', async () => {
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
+ createComponentWithApollo({ mountFn: mount });
+
+ expect(findTextEditor().exists()).toBe(false);
+
+ await nextTick();
+
+ expect(findTextEditor().exists()).toBe(true);
+ expect(findEditorTab().exists()).toBe(true);
+ });
+ });
+
+ describe('visualization tab', () => {
+ describe('while loading', () => {
+ beforeEach(() => {
+ createComponent({ appStatus: EDITOR_APP_STATUS_LOADING });
+ });
+
+ it('displays a loading icon if the lint query is loading', () => {
+ expect(findLoadingIcon().exists()).toBe(true);
+ expect(findPipelineGraph().exists()).toBe(false);
+ });
+ });
+ describe('after loading', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('display the tab and visualization', () => {
+ expect(findVisualizationTab().exists()).toBe(true);
+ expect(findPipelineGraph().exists()).toBe(true);
+ });
+ });
+ });
+
+ describe('validate tab', () => {
+ describe('after loading', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('displays the tab and the validate component', () => {
+ expect(findValidateTab().exists()).toBe(true);
+ expect(findCiValidate().exists()).toBe(true);
+ });
+ });
+
+ describe('NEW badge', () => {
+ describe('default', () => {
+ beforeEach(() => {
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
+ createComponentWithApollo({
+ mountFn: mount,
+ props: {
+ currentTab: VALIDATE_TAB,
+ },
+ });
+ });
+
+ it('renders badge by default', () => {
+ expect(findBadge().exists()).toBe(true);
+ expect(findBadge().text()).toBe(wrapper.vm.$options.i18n.new);
+ });
+
+ it('hides badge when moving away from the validate tab', async () => {
+ expect(findBadge().exists()).toBe(true);
+
+ await findEditorTab().vm.$emit('click');
+
+ expect(findBadge().exists()).toBe(false);
+ });
+ });
+
+ describe('if badge has been dismissed before', () => {
+ beforeEach(() => {
+ localStorage.setItem(VALIDATE_TAB_BADGE_DISMISSED_KEY, 'true');
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
+ createComponentWithApollo({ mountFn: mount });
+ });
+
+ it('does not render badge if it has been dismissed before', () => {
+ expect(findBadge().exists()).toBe(false);
+ });
+ });
+ });
+ });
+
+ describe('merged tab', () => {
+ describe('while loading', () => {
+ beforeEach(() => {
+ createComponent({ appStatus: EDITOR_APP_STATUS_LOADING });
+ });
+
+ it('displays a loading icon if the lint query is loading', () => {
+ expect(findLoadingIcon().exists()).toBe(true);
+ });
+ });
+
+ describe('when there is a fetch error', () => {
+ beforeEach(() => {
+ createComponent({ props: { ciConfigData: mockLintResponseWithoutMerged } });
+ });
+
+ it('show an error message', () => {
+ expect(findAlert().exists()).toBe(true);
+ expect(findAlert().text()).toBe(wrapper.vm.$options.errorTexts.loadMergedYaml);
+ });
+
+ it('does not render the `merged_preview` component', () => {
+ expect(findMergedPreview().exists()).toBe(false);
+ });
+ });
+
+ describe('after loading', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('display the tab and the merged preview component', () => {
+ expect(findMergedTab().exists()).toBe(true);
+ expect(findMergedPreview().exists()).toBe(true);
+ });
+ });
+ });
+
+ describe('show tab content based on status', () => {
+ it.each`
+ appStatus | editor | viz | validate | merged
+ ${undefined} | ${true} | ${true} | ${true} | ${true}
+ ${EDITOR_APP_STATUS_EMPTY} | ${true} | ${false} | ${true} | ${false}
+ ${EDITOR_APP_STATUS_INVALID} | ${true} | ${false} | ${true} | ${true}
+ ${EDITOR_APP_STATUS_VALID} | ${true} | ${true} | ${true} | ${true}
+ `(
+ 'when status is $appStatus, we show - editor:$editor | viz:$viz | validate:$validate | merged:$merged',
+ ({ appStatus, editor, viz, validate, merged }) => {
+ createComponent({ appStatus });
+
+ expect(findTextEditor().exists()).toBe(editor);
+ expect(findPipelineGraph().exists()).toBe(viz);
+ expect(findValidateTab().exists()).toBe(validate);
+ expect(findMergedPreview().exists()).toBe(merged);
+ },
+ );
+ });
+
+ describe('default tab based on url query param', () => {
+ const gitlabUrl = 'https://gitlab.test/ci/editor/';
+ const matchObject = {
+ hostname: 'gitlab.test',
+ pathname: '/ci/editor/',
+ search: '',
+ };
+
+ it(`is ${CREATE_TAB} if the query param ${TAB_QUERY_PARAM} is not present`, () => {
+ setWindowLocation(gitlabUrl);
+ createComponent();
+
+ expect(window.location).toMatchObject(matchObject);
+ });
+
+ it(`is ${CREATE_TAB} tab if the query param ${TAB_QUERY_PARAM} is invalid`, () => {
+ const queryValue = 'FOO';
+ setWindowLocation(`${gitlabUrl}?${TAB_QUERY_PARAM}=${queryValue}`);
+ createComponent();
+
+ // If the query param remains unchanged, then we have ignored it.
+ expect(window.location).toMatchObject({
+ ...matchObject,
+ search: `?${TAB_QUERY_PARAM}=${queryValue}`,
+ });
+ });
+ });
+
+ describe('glTabs', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('passes the `sync-active-tab-with-query-params` prop', () => {
+ expect(findGlTabs().props('syncActiveTabWithQueryParams')).toBe(true);
+ });
+ });
+
+ describe('pipeline editor walkthrough', () => {
+ describe('when isNewCiConfigFile prop is true (default)', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('shows walkthrough popover', async () => {
+ expect(findWalkthroughPopover().exists()).toBe(true);
+ });
+ });
+
+ describe('when isNewCiConfigFile prop is false', () => {
+ it('does not show walkthrough popover', async () => {
+ createComponent({ props: { isNewCiConfigFile: false } });
+ expect(findWalkthroughPopover().exists()).toBe(false);
+ });
+ });
+ });
+
+ it('sets listeners on walkthrough popover', async () => {
+ const handler = jest.fn();
+
+ createComponent({
+ listeners: {
+ event: handler,
+ },
+ });
+ await nextTick();
+
+ findWalkthroughPopover().vm.$emit('event');
+
+ expect(handler).toHaveBeenCalled();
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/popovers/file_tree_popover_spec.js b/spec/frontend/ci/pipeline_editor/components/popovers/file_tree_popover_spec.js
new file mode 100644
index 00000000000..63ebfc0559d
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/popovers/file_tree_popover_spec.js
@@ -0,0 +1,56 @@
+import { nextTick } from 'vue';
+import { GlLink, GlPopover, GlSprintf } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import FileTreePopover from '~/ci/pipeline_editor/components/popovers/file_tree_popover.vue';
+import { FILE_TREE_POPOVER_DISMISSED_KEY } from '~/ci/pipeline_editor/constants';
+import { mockIncludesHelpPagePath } from '../../mock_data';
+
+describe('FileTreePopover component', () => {
+ let wrapper;
+
+ const findPopover = () => wrapper.findComponent(GlPopover);
+ const findLink = () => findPopover().findComponent(GlLink);
+
+ const createComponent = ({ stubs } = {}) => {
+ wrapper = shallowMount(FileTreePopover, {
+ provide: {
+ includesHelpPagePath: mockIncludesHelpPagePath,
+ },
+ stubs,
+ });
+ };
+
+ afterEach(() => {
+ localStorage.clear();
+ wrapper.destroy();
+ });
+
+ describe('default', () => {
+ beforeEach(async () => {
+ createComponent({ stubs: { GlSprintf } });
+ });
+
+ it('renders dismissable popover', async () => {
+ expect(findPopover().exists()).toBe(true);
+
+ findPopover().vm.$emit('close-button-clicked');
+ await nextTick();
+
+ expect(findPopover().exists()).toBe(false);
+ });
+
+ it('renders learn more link', () => {
+ expect(findLink().exists()).toBe(true);
+ expect(findLink().attributes('href')).toBe(mockIncludesHelpPagePath);
+ });
+ });
+
+ describe('when popover has already been dismissed before', () => {
+ it('does not render popover', async () => {
+ localStorage.setItem(FILE_TREE_POPOVER_DISMISSED_KEY, 'true');
+ createComponent();
+
+ expect(findPopover().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/popovers/validate_pipeline_popover_spec.js b/spec/frontend/ci/pipeline_editor/components/popovers/validate_pipeline_popover_spec.js
new file mode 100644
index 00000000000..cf0b974081e
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/popovers/validate_pipeline_popover_spec.js
@@ -0,0 +1,43 @@
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import ValidatePopover from '~/ci/pipeline_editor/components/popovers/validate_pipeline_popover.vue';
+import { VALIDATE_TAB_FEEDBACK_URL } from '~/ci/pipeline_editor/constants';
+import { mockSimulatePipelineHelpPagePath } from '../../mock_data';
+
+describe('ValidatePopover component', () => {
+ let wrapper;
+
+ const createComponent = ({ stubs } = {}) => {
+ wrapper = shallowMountExtended(ValidatePopover, {
+ provide: {
+ simulatePipelineHelpPagePath: mockSimulatePipelineHelpPagePath,
+ },
+ stubs,
+ });
+ };
+
+ const findHelpLink = () => wrapper.findByTestId('help-link');
+ const findFeedbackLink = () => wrapper.findByTestId('feedback-link');
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('template', () => {
+ beforeEach(async () => {
+ createComponent({
+ stubs: { GlLink, GlSprintf },
+ });
+ });
+
+ it('renders help link', () => {
+ expect(findHelpLink().exists()).toBe(true);
+ expect(findHelpLink().attributes('href')).toBe(mockSimulatePipelineHelpPagePath);
+ });
+
+ it('renders feedback link', () => {
+ expect(findFeedbackLink().exists()).toBe(true);
+ expect(findFeedbackLink().attributes('href')).toBe(VALIDATE_TAB_FEEDBACK_URL);
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/popovers/walkthrough_popover_spec.js b/spec/frontend/ci/pipeline_editor/components/popovers/walkthrough_popover_spec.js
new file mode 100644
index 00000000000..ca6033f2ff5
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/popovers/walkthrough_popover_spec.js
@@ -0,0 +1,29 @@
+import { mount, shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import WalkthroughPopover from '~/ci/pipeline_editor/components/popovers/walkthrough_popover.vue';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+
+Vue.config.ignoredElements = ['gl-emoji'];
+
+describe('WalkthroughPopover component', () => {
+ let wrapper;
+
+ const createComponent = (mountFn = shallowMount) => {
+ return extendedWrapper(mountFn(WalkthroughPopover));
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('CTA button clicked', () => {
+ beforeEach(async () => {
+ wrapper = createComponent(mount);
+ await wrapper.findByTestId('ctaBtn').trigger('click');
+ });
+
+ it('emits "walkthrough-popover-cta-clicked" event', async () => {
+ expect(wrapper.emitted()['walkthrough-popover-cta-clicked']).toHaveLength(1);
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/ui/confirm_unsaved_changes_dialog_spec.js b/spec/frontend/ci/pipeline_editor/components/ui/confirm_unsaved_changes_dialog_spec.js
new file mode 100644
index 00000000000..b22c98e5544
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/ui/confirm_unsaved_changes_dialog_spec.js
@@ -0,0 +1,42 @@
+import { shallowMount } from '@vue/test-utils';
+import ConfirmDialog from '~/ci/pipeline_editor/components/ui/confirm_unsaved_changes_dialog.vue';
+
+describe('pipeline_editor/components/ui/confirm_unsaved_changes_dialog', () => {
+ let beforeUnloadEvent;
+ let setDialogContent;
+ let wrapper;
+
+ const createComponent = (propsData = {}) => {
+ wrapper = shallowMount(ConfirmDialog, {
+ propsData,
+ });
+ };
+
+ beforeEach(() => {
+ beforeUnloadEvent = new Event('beforeunload');
+ jest.spyOn(beforeUnloadEvent, 'preventDefault');
+ setDialogContent = jest.spyOn(beforeUnloadEvent, 'returnValue', 'set');
+ });
+
+ afterEach(() => {
+ beforeUnloadEvent.preventDefault.mockRestore();
+ setDialogContent.mockRestore();
+ wrapper.destroy();
+ });
+
+ it('shows confirmation dialog when there are unsaved changes', () => {
+ createComponent({ hasUnsavedChanges: true });
+ window.dispatchEvent(beforeUnloadEvent);
+
+ expect(beforeUnloadEvent.preventDefault).toHaveBeenCalled();
+ expect(setDialogContent).toHaveBeenCalledWith('');
+ });
+
+ it('does not show confirmation dialog when there are no unsaved changes', () => {
+ createComponent({ hasUnsavedChanges: false });
+ window.dispatchEvent(beforeUnloadEvent);
+
+ expect(beforeUnloadEvent.preventDefault).not.toHaveBeenCalled();
+ expect(setDialogContent).not.toHaveBeenCalled();
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/ui/editor_tab_spec.js b/spec/frontend/ci/pipeline_editor/components/ui/editor_tab_spec.js
new file mode 100644
index 00000000000..a4e7abba7b0
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/ui/editor_tab_spec.js
@@ -0,0 +1,200 @@
+import { GlAlert, GlBadge, GlTabs } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import EditorTab from '~/ci/pipeline_editor/components/ui/editor_tab.vue';
+
+const mockContent1 = 'MOCK CONTENT 1';
+const mockContent2 = 'MOCK CONTENT 2';
+
+const MockSourceEditor = {
+ template: '<div>EDITOR</div>',
+};
+
+describe('~/ci/pipeline_editor/components/ui/editor_tab.vue', () => {
+ let wrapper;
+ let mockChildMounted = jest.fn();
+
+ const MockChild = {
+ props: ['content'],
+ template: '<div>{{content}}</div>',
+ mounted() {
+ mockChildMounted(this.content);
+ },
+ };
+
+ const MockTabbedContent = {
+ components: {
+ EditorTab,
+ GlTabs,
+ MockChild,
+ },
+ template: `
+ <gl-tabs>
+ <editor-tab title="Tab 1" :title-link-attributes="{ 'data-testid': 'tab1-btn' }" :lazy="true">
+ <mock-child content="${mockContent1}"/>
+ </editor-tab>
+ <editor-tab title="Tab 2" :title-link-attributes="{ 'data-testid': 'tab2-btn' }" :lazy="true" badge-title="NEW">
+ <mock-child content="${mockContent2}"/>
+ </editor-tab>
+ </gl-tabs>
+ `,
+ };
+
+ const createMockedWrapper = () => {
+ wrapper = mount(MockTabbedContent);
+ };
+
+ const createWrapper = ({ props } = {}) => {
+ wrapper = mount(EditorTab, {
+ propsData: {
+ title: 'Tab 1',
+ ...props,
+ },
+ slots: {
+ default: MockSourceEditor,
+ },
+ });
+ };
+
+ const findSlotComponent = () => wrapper.findComponent(MockSourceEditor);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findBadges = () => wrapper.findAllComponents(GlBadge);
+
+ beforeEach(() => {
+ mockChildMounted = jest.fn();
+ });
+
+ it('tabs are mounted lazily', async () => {
+ createMockedWrapper();
+
+ expect(mockChildMounted).toHaveBeenCalledTimes(0);
+ });
+
+ it('first tab is only mounted after nextTick', async () => {
+ createMockedWrapper();
+
+ await nextTick();
+
+ expect(mockChildMounted).toHaveBeenCalledTimes(1);
+ expect(mockChildMounted).toHaveBeenCalledWith(mockContent1);
+ });
+
+ describe('alerts', () => {
+ describe('unavailable state', () => {
+ beforeEach(() => {
+ createWrapper({ props: { isUnavailable: true } });
+ });
+
+ it('shows the invalid alert when the status is invalid', () => {
+ const alert = findAlert();
+
+ expect(alert.exists()).toBe(true);
+ expect(alert.text()).toContain(wrapper.vm.$options.i18n.unavailable);
+ });
+ });
+
+ describe('invalid state', () => {
+ beforeEach(() => {
+ createWrapper({ props: { isInvalid: true } });
+ });
+
+ it('shows the invalid alert when the status is invalid', () => {
+ const alert = findAlert();
+
+ expect(alert.exists()).toBe(true);
+ expect(alert.text()).toBe(wrapper.vm.$options.i18n.invalid);
+ });
+ });
+
+ describe('empty state', () => {
+ const text = 'my custom alert message';
+
+ beforeEach(() => {
+ createWrapper({
+ props: { isEmpty: true, emptyMessage: text },
+ });
+ });
+
+ it('displays an empty message', () => {
+ createWrapper({
+ props: { isEmpty: true },
+ });
+
+ const alert = findAlert();
+
+ expect(alert.exists()).toBe(true);
+ expect(alert.text()).toBe(
+ 'This tab will be usable when the CI/CD configuration file is populated with valid syntax.',
+ );
+ });
+
+ it('can have a custom empty message', () => {
+ const alert = findAlert();
+
+ expect(alert.exists()).toBe(true);
+ expect(alert.text()).toBe(text);
+ });
+ });
+ });
+
+ describe('showing the tab content depending on `isEmpty`, `isUnavailable` and `isInvalid`', () => {
+ it.each`
+ isEmpty | isUnavailable | isInvalid | showSlotComponent | text
+ ${undefined} | ${undefined} | ${undefined} | ${true} | ${'renders'}
+ ${false} | ${false} | ${false} | ${true} | ${'renders'}
+ ${undefined} | ${true} | ${true} | ${false} | ${'hides'}
+ ${true} | ${false} | ${false} | ${false} | ${'hides'}
+ ${false} | ${true} | ${false} | ${false} | ${'hides'}
+ ${false} | ${false} | ${true} | ${false} | ${'hides'}
+ `(
+ '$text the slot component when isEmpty:$isEmpty, isUnavailable:$isUnavailable and isInvalid:$isInvalid',
+ ({ isEmpty, isUnavailable, isInvalid, showSlotComponent }) => {
+ createWrapper({
+ props: { isEmpty, isUnavailable, isInvalid },
+ });
+ expect(findSlotComponent().exists()).toBe(showSlotComponent);
+ expect(findAlert().exists()).toBe(!showSlotComponent);
+ },
+ );
+ });
+
+ describe('user interaction', () => {
+ const clickTab = async (testid) => {
+ wrapper.find(`[data-testid="${testid}"]`).trigger('click');
+ await nextTick();
+ };
+
+ beforeEach(() => {
+ createMockedWrapper();
+ });
+
+ it('mounts a tab once after selecting it', async () => {
+ await clickTab('tab2-btn');
+
+ expect(mockChildMounted).toHaveBeenCalledTimes(2);
+ expect(mockChildMounted).toHaveBeenNthCalledWith(1, mockContent1);
+ expect(mockChildMounted).toHaveBeenNthCalledWith(2, mockContent2);
+ });
+
+ it('mounts each tab once after selecting each', async () => {
+ await clickTab('tab2-btn');
+ await clickTab('tab1-btn');
+ await clickTab('tab2-btn');
+
+ expect(mockChildMounted).toHaveBeenCalledTimes(2);
+ expect(mockChildMounted).toHaveBeenNthCalledWith(1, mockContent1);
+ expect(mockChildMounted).toHaveBeenNthCalledWith(2, mockContent2);
+ });
+ });
+
+ describe('valid state', () => {
+ beforeEach(() => {
+ createMockedWrapper();
+ });
+
+ it('renders correct number of badges', async () => {
+ expect(findBadges()).toHaveLength(1);
+ expect(findBadges().at(0).text()).toBe('NEW');
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/ui/pipeline_editor_empty_state_spec.js b/spec/frontend/ci/pipeline_editor/components/ui/pipeline_editor_empty_state_spec.js
new file mode 100644
index 00000000000..3c68f74af43
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/ui/pipeline_editor_empty_state_spec.js
@@ -0,0 +1,92 @@
+import { GlButton, GlSprintf } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import PipelineEditorFileNav from '~/ci/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue';
+import PipelineEditorEmptyState from '~/ci/pipeline_editor/components/ui/pipeline_editor_empty_state.vue';
+
+describe('Pipeline editor empty state', () => {
+ let wrapper;
+ const defaultProvide = {
+ emptyStateIllustrationPath: 'my/svg/path',
+ usesExternalConfig: false,
+ };
+
+ const createComponent = ({ provide } = {}) => {
+ wrapper = shallowMount(PipelineEditorEmptyState, {
+ provide: { ...defaultProvide, ...provide },
+ });
+ };
+
+ const findFileNav = () => wrapper.findComponent(PipelineEditorFileNav);
+ const findSvgImage = () => wrapper.find('img');
+ const findTitle = () => wrapper.find('h1');
+ const findExternalCiInstructions = () => wrapper.find('p');
+ const findConfirmButton = () => wrapper.findComponent(GlButton);
+ const findDescription = () => wrapper.findComponent(GlSprintf);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when project uses an external CI config', () => {
+ beforeEach(() => {
+ createComponent({
+ provide: { usesExternalConfig: true },
+ });
+ });
+
+ it('renders an svg image', () => {
+ expect(findSvgImage().exists()).toBe(true);
+ });
+
+ it('renders the correct title and instructions', () => {
+ expect(findTitle().exists()).toBe(true);
+ expect(findExternalCiInstructions().exists()).toBe(true);
+
+ expect(findExternalCiInstructions().html()).toContain(
+ wrapper.vm.$options.i18n.externalCiInstructions,
+ );
+ expect(findTitle().text()).toBe(wrapper.vm.$options.i18n.externalCiNote);
+ });
+
+ it('does not render the CTA button', () => {
+ expect(findConfirmButton().exists()).toBe(false);
+ });
+ });
+
+ describe('when project uses an accessible CI config', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders an svg image', () => {
+ expect(findSvgImage().exists()).toBe(true);
+ });
+
+ it('renders a title', () => {
+ expect(findTitle().exists()).toBe(true);
+ expect(findTitle().text()).toBe(wrapper.vm.$options.i18n.title);
+ });
+
+ it('renders a description', () => {
+ expect(findDescription().exists()).toBe(true);
+ expect(findDescription().html()).toContain(wrapper.vm.$options.i18n.body);
+ });
+
+ it('renders the file nav', () => {
+ expect(findFileNav().exists()).toBe(true);
+ });
+
+ it('renders a CTA button', () => {
+ expect(findConfirmButton().exists()).toBe(true);
+ expect(findConfirmButton().text()).toBe(wrapper.vm.$options.i18n.btnText);
+ });
+
+ it('emits an event when clicking on the CTA', async () => {
+ const expectedEvent = 'createEmptyConfigFile';
+ expect(wrapper.emitted(expectedEvent)).toBeUndefined();
+
+ await findConfirmButton().vm.$emit('click');
+ expect(wrapper.emitted(expectedEvent)).toHaveLength(1);
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/ui/pipeline_editor_messages_spec.js b/spec/frontend/ci/pipeline_editor/components/ui/pipeline_editor_messages_spec.js
new file mode 100644
index 00000000000..fdb3be5c690
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/ui/pipeline_editor_messages_spec.js
@@ -0,0 +1,149 @@
+import { GlAlert } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import setWindowLocation from 'helpers/set_window_location_helper';
+import { TEST_HOST } from 'helpers/test_constants';
+import CodeSnippetAlert from '~/ci/pipeline_editor/components/code_snippet_alert/code_snippet_alert.vue';
+import { CODE_SNIPPET_SOURCES } from '~/ci/pipeline_editor/components/code_snippet_alert/constants';
+import PipelineEditorMessages from '~/ci/pipeline_editor/components/ui/pipeline_editor_messages.vue';
+import {
+ COMMIT_FAILURE,
+ COMMIT_SUCCESS,
+ COMMIT_SUCCESS_WITH_REDIRECT,
+ DEFAULT_FAILURE,
+ DEFAULT_SUCCESS,
+ LOAD_FAILURE_UNKNOWN,
+ PIPELINE_FAILURE,
+} from '~/ci/pipeline_editor/constants';
+
+beforeEach(() => {
+ setWindowLocation(TEST_HOST);
+});
+
+describe('Pipeline Editor messages', () => {
+ let wrapper;
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(PipelineEditorMessages, {
+ propsData: props,
+ });
+ };
+
+ const findCodeSnippetAlert = () => wrapper.findComponent(CodeSnippetAlert);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+
+ describe('success alert', () => {
+ it('shows a message for successful commit type', () => {
+ createComponent({ successType: COMMIT_SUCCESS, showSuccess: true });
+
+ expect(findAlert().text()).toBe(wrapper.vm.$options.success[COMMIT_SUCCESS]);
+ });
+
+ it('shows a message for successful commit with redirect type', () => {
+ createComponent({ successType: COMMIT_SUCCESS_WITH_REDIRECT, showSuccess: true });
+
+ expect(findAlert().text()).toBe(wrapper.vm.$options.success[COMMIT_SUCCESS_WITH_REDIRECT]);
+ });
+
+ it('does not show alert when there is a successType but visibility is off', () => {
+ createComponent({ successType: COMMIT_SUCCESS, showSuccess: false });
+
+ expect(findAlert().exists()).toBe(false);
+ });
+
+ it('shows a success alert with default copy if `showSuccess` is true and the `successType` is not valid,', () => {
+ createComponent({ successType: 'random', showSuccess: true });
+
+ expect(findAlert().text()).toBe(wrapper.vm.$options.success[DEFAULT_SUCCESS]);
+ });
+
+ it('emit `hide-success` event when clicking on the dismiss button', async () => {
+ const expectedEvent = 'hide-success';
+
+ createComponent({ successType: COMMIT_SUCCESS, showSuccess: true });
+ expect(wrapper.emitted(expectedEvent)).not.toBeDefined();
+
+ await findAlert().vm.$emit('dismiss');
+
+ expect(wrapper.emitted(expectedEvent)).toBeDefined();
+ });
+ });
+
+ describe('failure alert', () => {
+ it.each`
+ failureType | message | expectedFailureType
+ ${COMMIT_FAILURE} | ${'failed commit'} | ${COMMIT_FAILURE}
+ ${LOAD_FAILURE_UNKNOWN} | ${'loading failure'} | ${LOAD_FAILURE_UNKNOWN}
+ ${PIPELINE_FAILURE} | ${'pipeline failure'} | ${PIPELINE_FAILURE}
+ ${'random'} | ${'error without a specified type'} | ${DEFAULT_FAILURE}
+ `('shows a message for $message', ({ failureType, expectedFailureType }) => {
+ createComponent({ failureType, showFailure: true });
+
+ expect(findAlert().text()).toBe(wrapper.vm.$options.errors[expectedFailureType]);
+ });
+
+ it('show failure reasons when there are some', () => {
+ const failureReasons = ['There was a problem', 'ouppps'];
+ createComponent({ failureType: COMMIT_FAILURE, failureReasons, showFailure: true });
+
+ expect(wrapper.html()).toContain(failureReasons[0]);
+ expect(wrapper.html()).toContain(failureReasons[1]);
+ });
+
+ it('does not show a message for error with a disabled visibility', () => {
+ createComponent({ failureType: 'random', showFailure: false });
+
+ expect(findAlert().exists()).toBe(false);
+ });
+
+ it('emit `hide-failure` event when clicking on the dismiss button', async () => {
+ const expectedEvent = 'hide-failure';
+
+ createComponent({ failureType: COMMIT_FAILURE, showFailure: true });
+ expect(wrapper.emitted(expectedEvent)).not.toBeDefined();
+
+ await findAlert().vm.$emit('dismiss');
+
+ expect(wrapper.emitted(expectedEvent)).toBeDefined();
+ });
+ });
+
+ describe('code snippet alert', () => {
+ const setCodeSnippetUrlParam = (value) => {
+ setWindowLocation(`${TEST_HOST}/?code_snippet_copied_from=${value}`);
+ };
+
+ it('does not show by default', () => {
+ createComponent();
+
+ expect(findCodeSnippetAlert().exists()).toBe(false);
+ });
+
+ it.each(CODE_SNIPPET_SOURCES)('shows if URL param is %s, and cleans up URL', (source) => {
+ jest.spyOn(window.history, 'replaceState');
+ setCodeSnippetUrlParam(source);
+ createComponent();
+
+ expect(findCodeSnippetAlert().exists()).toBe(true);
+ expect(window.history.replaceState).toHaveBeenCalledWith({}, document.title, `${TEST_HOST}/`);
+ });
+
+ it('does not show if URL param is invalid', () => {
+ setCodeSnippetUrlParam('foo_bar');
+ createComponent();
+
+ expect(findCodeSnippetAlert().exists()).toBe(false);
+ });
+
+ it('disappears on dismiss', async () => {
+ setCodeSnippetUrlParam('api_fuzzing');
+ createComponent();
+ const alert = findCodeSnippetAlert();
+
+ expect(alert.exists()).toBe(true);
+
+ await alert.vm.$emit('dismiss');
+
+ expect(alert.exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/components/validate/ci_validate_spec.js b/spec/frontend/ci/pipeline_editor/components/validate/ci_validate_spec.js
new file mode 100644
index 00000000000..ae25142b455
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/components/validate/ci_validate_spec.js
@@ -0,0 +1,314 @@
+import { GlAlert, GlDropdown, GlIcon, GlLoadingIcon, GlPopover } from '@gitlab/ui';
+import { nextTick } from 'vue';
+import { createLocalVue } from '@vue/test-utils';
+import VueApollo from 'vue-apollo';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import CiLintResults from '~/ci/pipeline_editor/components/lint/ci_lint_results.vue';
+import CiValidate, { i18n } from '~/ci/pipeline_editor/components/validate/ci_validate.vue';
+import ValidatePipelinePopover from '~/ci/pipeline_editor/components/popovers/validate_pipeline_popover.vue';
+import getBlobContent from '~/ci/pipeline_editor/graphql/queries/blob_content.query.graphql';
+import lintCIMutation from '~/ci/pipeline_editor/graphql/mutations/client/lint_ci.mutation.graphql';
+import { pipelineEditorTrackingOptions } from '~/ci/pipeline_editor/constants';
+import {
+ mockBlobContentQueryResponse,
+ mockCiLintPath,
+ mockCiYml,
+ mockSimulatePipelineHelpPagePath,
+} from '../../mock_data';
+import { mockLintDataError, mockLintDataValid } from '../../../ci_lint/mock_data';
+
+const localVue = createLocalVue();
+localVue.use(VueApollo);
+
+describe('Pipeline Editor Validate Tab', () => {
+ let wrapper;
+ let mockApollo;
+ let mockBlobContentData;
+ let trackingSpy;
+
+ const createComponent = ({
+ props,
+ stubs,
+ options,
+ isBlobLoading = false,
+ isSimulationLoading = false,
+ } = {}) => {
+ wrapper = shallowMountExtended(CiValidate, {
+ propsData: {
+ ciFileContent: mockCiYml,
+ ...props,
+ },
+ provide: {
+ ciConfigPath: '/path/to/ci-config',
+ ciLintPath: mockCiLintPath,
+ currentBranch: 'main',
+ projectFullPath: '/path/to/project',
+ validateTabIllustrationPath: '/path/to/img',
+ simulatePipelineHelpPagePath: mockSimulatePipelineHelpPagePath,
+ },
+ stubs,
+ mocks: {
+ $apollo: {
+ queries: {
+ initialBlobContent: {
+ loading: isBlobLoading,
+ },
+ },
+ mutations: {
+ lintCiMutation: {
+ loading: isSimulationLoading,
+ },
+ },
+ },
+ },
+ ...options,
+ });
+ };
+
+ const createComponentWithApollo = ({ props, stubs } = {}) => {
+ const handlers = [[getBlobContent, mockBlobContentData]];
+ mockApollo = createMockApollo(handlers);
+
+ createComponent({
+ props,
+ stubs,
+ options: {
+ localVue,
+ apolloProvider: mockApollo,
+ mocks: {},
+ },
+ });
+ };
+
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findCancelBtn = () => wrapper.findByTestId('cancel-simulation');
+ const findContentChangeStatus = () => wrapper.findByTestId('content-status');
+ const findCta = () => wrapper.findByTestId('simulate-pipeline-button');
+ const findDisabledCtaTooltip = () => wrapper.findByTestId('cta-tooltip');
+ const findHelpIcon = () => wrapper.findComponent(GlIcon);
+ const findIllustration = () => wrapper.findByRole('img');
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findPipelineSource = () => wrapper.findComponent(GlDropdown);
+ const findPopover = () => wrapper.findComponent(GlPopover);
+ const findCiLintResults = () => wrapper.findComponent(CiLintResults);
+ const findResultsCta = () => wrapper.findByTestId('resimulate-pipeline-button');
+
+ beforeEach(() => {
+ mockBlobContentData = jest.fn();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('while initial CI content is loading', () => {
+ beforeEach(() => {
+ createComponent({ isBlobLoading: true });
+ });
+
+ it('renders disabled CTA with tooltip', () => {
+ expect(findCta().props('disabled')).toBe(true);
+ expect(findDisabledCtaTooltip().exists()).toBe(true);
+ });
+ });
+
+ describe('after initial CI content is loaded', () => {
+ beforeEach(async () => {
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
+ await createComponentWithApollo({ stubs: { GlPopover, ValidatePipelinePopover } });
+ });
+
+ it('renders disabled pipeline source dropdown', () => {
+ expect(findPipelineSource().exists()).toBe(true);
+ expect(findPipelineSource().attributes('text')).toBe(i18n.pipelineSourceDefault);
+ expect(findPipelineSource().props('disabled')).toBe(true);
+ });
+
+ it('renders enabled CTA without tooltip', () => {
+ expect(findCta().exists()).toBe(true);
+ expect(findCta().props('disabled')).toBe(false);
+ expect(findDisabledCtaTooltip().exists()).toBe(false);
+ });
+
+ it('popover is set to render when hovering over help icon', () => {
+ expect(findPopover().props('target')).toBe(findHelpIcon().attributes('id'));
+ expect(findPopover().props('triggers')).toBe('hover focus');
+ });
+ });
+
+ describe('simulating the pipeline', () => {
+ beforeEach(async () => {
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
+ await createComponentWithApollo();
+
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockLintDataValid);
+ });
+
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ it('tracks the simulation event', () => {
+ const {
+ label,
+ actions: { simulatePipeline },
+ } = pipelineEditorTrackingOptions;
+ findCta().vm.$emit('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, simulatePipeline, { label });
+ });
+
+ it('renders loading state while simulation is ongoing', async () => {
+ findCta().vm.$emit('click');
+ await nextTick();
+
+ expect(findLoadingIcon().exists()).toBe(true);
+ expect(findCancelBtn().exists()).toBe(true);
+ expect(findCta().props('loading')).toBe(true);
+ });
+
+ it('calls mutation with the correct input', async () => {
+ await findCta().vm.$emit('click');
+
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1);
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
+ mutation: lintCIMutation,
+ variables: {
+ dry: true,
+ content: mockCiYml,
+ endpoint: mockCiLintPath,
+ },
+ });
+ });
+
+ describe('when results are successful', () => {
+ beforeEach(async () => {
+ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockLintDataValid);
+ await findCta().vm.$emit('click');
+ });
+
+ it('renders success alert', () => {
+ expect(findAlert().exists()).toBe(true);
+ expect(findAlert().attributes('variant')).toBe('success');
+ expect(findAlert().attributes('title')).toBe(i18n.successAlertTitle);
+ });
+
+ it('does not render content change status or CTA for results page', () => {
+ expect(findContentChangeStatus().exists()).toBe(false);
+ expect(findResultsCta().exists()).toBe(false);
+ });
+
+ it('renders CI lint results with correct props', () => {
+ expect(findCiLintResults().exists()).toBe(true);
+ expect(findCiLintResults().props()).toMatchObject({
+ dryRun: true,
+ hideAlert: true,
+ isValid: true,
+ jobs: mockLintDataValid.data.lintCI.jobs,
+ });
+ });
+ });
+
+ describe('when results have errors', () => {
+ beforeEach(async () => {
+ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockLintDataError);
+ await findCta().vm.$emit('click');
+ });
+
+ it('renders error alert', () => {
+ expect(findAlert().exists()).toBe(true);
+ expect(findAlert().attributes('variant')).toBe('danger');
+ expect(findAlert().attributes('title')).toBe(i18n.errorAlertTitle);
+ });
+
+ it('renders CI lint results with correct props', () => {
+ expect(findCiLintResults().exists()).toBe(true);
+ expect(findCiLintResults().props()).toMatchObject({
+ dryRun: true,
+ hideAlert: true,
+ isValid: false,
+ errors: mockLintDataError.data.lintCI.errors,
+ warnings: mockLintDataError.data.lintCI.warnings,
+ });
+ });
+ });
+ });
+
+ describe('when CI content has changed after a simulation', () => {
+ beforeEach(async () => {
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
+ await createComponentWithApollo();
+
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockLintDataValid);
+ await findCta().vm.$emit('click');
+ });
+
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ it('tracks the second simulation event', async () => {
+ const {
+ label,
+ actions: { resimulatePipeline },
+ } = pipelineEditorTrackingOptions;
+
+ await wrapper.setProps({ ciFileContent: 'new yaml content' });
+ findResultsCta().vm.$emit('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, resimulatePipeline, { label });
+ });
+
+ it('renders content change status', async () => {
+ await wrapper.setProps({ ciFileContent: 'new yaml content' });
+
+ expect(findContentChangeStatus().exists()).toBe(true);
+ expect(findResultsCta().exists()).toBe(true);
+ });
+
+ it('calls mutation with new content', async () => {
+ await wrapper.setProps({ ciFileContent: 'new yaml content' });
+ await findResultsCta().vm.$emit('click');
+
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(2);
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
+ mutation: lintCIMutation,
+ variables: {
+ dry: true,
+ content: 'new yaml content',
+ endpoint: mockCiLintPath,
+ },
+ });
+ });
+ });
+
+ describe('canceling a simulation', () => {
+ beforeEach(async () => {
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
+ await createComponentWithApollo();
+ });
+
+ it('returns to init state', async () => {
+ // init state
+ expect(findIllustration().exists()).toBe(true);
+ expect(findCiLintResults().exists()).toBe(false);
+
+ // mutations should have successful results
+ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockLintDataValid);
+ findCta().vm.$emit('click');
+ await nextTick();
+
+ // cancel before simulation succeeds
+ expect(findCancelBtn().exists()).toBe(true);
+ await findCancelBtn().vm.$emit('click');
+
+ // should still render init state
+ expect(findIllustration().exists()).toBe(true);
+ expect(findCiLintResults().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap b/spec/frontend/ci/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap
new file mode 100644
index 00000000000..75a1354fd29
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap
@@ -0,0 +1,73 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`~/ci/pipeline_editor/graphql/resolvers Mutation lintCI lint data is as expected 1`] = `
+Object {
+ "__typename": "CiLintContent",
+ "errors": Array [],
+ "jobs": Array [
+ Object {
+ "__typename": "CiLintJob",
+ "afterScript": Array [
+ "echo 'after script 1",
+ ],
+ "allowFailure": false,
+ "beforeScript": Array [
+ "echo 'before script 1'",
+ ],
+ "environment": "prd",
+ "except": Object {
+ "refs": Array [
+ "main@gitlab-org/gitlab",
+ "/^release/.*$/@gitlab-org/gitlab",
+ ],
+ },
+ "name": "job_1",
+ "only": null,
+ "script": Array [
+ "echo 'script 1'",
+ ],
+ "stage": "test",
+ "tags": Array [
+ "tag 1",
+ ],
+ "when": "on_success",
+ },
+ Object {
+ "__typename": "CiLintJob",
+ "afterScript": Array [
+ "echo 'after script 2",
+ ],
+ "allowFailure": true,
+ "beforeScript": Array [
+ "echo 'before script 2'",
+ ],
+ "environment": "stg",
+ "except": Object {
+ "refs": Array [
+ "main@gitlab-org/gitlab",
+ "/^release/.*$/@gitlab-org/gitlab",
+ ],
+ },
+ "name": "job_2",
+ "only": Object {
+ "__typename": "CiLintJobOnlyPolicy",
+ "refs": Array [
+ "web",
+ "chat",
+ "pushes",
+ ],
+ },
+ "script": Array [
+ "echo 'script 2'",
+ ],
+ "stage": "test",
+ "tags": Array [
+ "tag 2",
+ ],
+ "when": "on_success",
+ },
+ ],
+ "valid": true,
+ "warnings": Array [],
+}
+`;
diff --git a/spec/frontend/ci/pipeline_editor/graphql/resolvers_spec.js b/spec/frontend/ci/pipeline_editor/graphql/resolvers_spec.js
new file mode 100644
index 00000000000..e54c72a758f
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/graphql/resolvers_spec.js
@@ -0,0 +1,52 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import httpStatus from '~/lib/utils/http_status';
+import { resolvers } from '~/ci/pipeline_editor/graphql/resolvers';
+import { mockLintResponse } from '../mock_data';
+
+jest.mock('~/api', () => {
+ return {
+ getRawFile: jest.fn(),
+ };
+});
+
+describe('~/ci/pipeline_editor/graphql/resolvers', () => {
+ describe('Mutation', () => {
+ describe('lintCI', () => {
+ let mock;
+ let result;
+
+ const endpoint = '/ci/lint';
+
+ beforeEach(async () => {
+ mock = new MockAdapter(axios);
+ mock.onPost(endpoint).reply(httpStatus.OK, mockLintResponse);
+
+ result = await resolvers.Mutation.lintCI(null, {
+ endpoint,
+ content: 'content',
+ dry_run: true,
+ });
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ /* eslint-disable no-underscore-dangle */
+ it('lint data has correct type names', async () => {
+ expect(result.__typename).toBe('CiLintContent');
+
+ expect(result.jobs[0].__typename).toBe('CiLintJob');
+ expect(result.jobs[1].__typename).toBe('CiLintJob');
+
+ expect(result.jobs[1].only.__typename).toBe('CiLintJobOnlyPolicy');
+ });
+ /* eslint-enable no-underscore-dangle */
+
+ it('lint data is as expected', () => {
+ expect(result).toMatchSnapshot();
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/mock_data.js b/spec/frontend/ci/pipeline_editor/mock_data.js
new file mode 100644
index 00000000000..176dc24f169
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/mock_data.js
@@ -0,0 +1,541 @@
+import { CI_CONFIG_STATUS_INVALID, CI_CONFIG_STATUS_VALID } from '~/ci/pipeline_editor/constants';
+import { unwrapStagesWithNeeds } from '~/pipelines/components/unwrapping_utils';
+
+export const mockProjectNamespace = 'user1';
+export const mockProjectPath = 'project1';
+export const mockProjectFullPath = `${mockProjectNamespace}/${mockProjectPath}`;
+export const mockDefaultBranch = 'main';
+export const mockNewBranch = 'new-branch';
+export const mockNewMergeRequestPath = '/-/merge_requests/new';
+export const mockCiLintPath = '/-/ci/lint';
+export const mockCommitSha = 'aabbccdd';
+export const mockCommitNextSha = 'eeffgghh';
+export const mockIncludesHelpPagePath = '/-/includes/help';
+export const mockLintHelpPagePath = '/-/lint-help';
+export const mockLintUnavailableHelpPagePath = '/-/pipeline-editor/troubleshoot';
+export const mockSimulatePipelineHelpPagePath = '/-/simulate-pipeline-help';
+export const mockYmlHelpPagePath = '/-/yml-help';
+export const mockCommitMessage = 'My commit message';
+
+export const mockCiConfigPath = '.gitlab-ci.yml';
+export const mockCiYml = `
+stages:
+ - test
+ - build
+
+job_test_1:
+ stage: test
+ script:
+ - echo "test 1"
+
+job_test_2:
+ stage: test
+ script:
+ - echo "test 2"
+
+job_build:
+ stage: build
+ script:
+ - echo "build"
+ needs: ["job_test_2"]
+`;
+
+export const mockCiTemplateQueryResponse = {
+ data: {
+ project: {
+ id: 'project-1',
+ ciTemplate: {
+ content: mockCiYml,
+ },
+ },
+ },
+};
+
+export const mockBlobContentQueryResponse = {
+ data: {
+ project: {
+ id: 'project-1',
+ repository: { blobs: { nodes: [{ id: 'blob-1', rawBlob: mockCiYml }] } },
+ },
+ },
+};
+
+export const mockBlobContentQueryResponseNoCiFile = {
+ data: {
+ project: { id: 'project-1', repository: { blobs: { nodes: [] } } },
+ },
+};
+
+export const mockBlobContentQueryResponseEmptyCiFile = {
+ data: {
+ project: { id: 'project-1', repository: { blobs: { nodes: [{ rawBlob: '' }] } } },
+ },
+};
+
+const mockJobFields = {
+ beforeScript: [],
+ afterScript: [],
+ environment: null,
+ allowFailure: false,
+ tags: [],
+ when: 'on_success',
+ only: { refs: ['branches', 'tags'], __typename: 'CiJobLimitType' },
+ except: null,
+ needs: { nodes: [], __typename: 'CiConfigNeedConnection' },
+ __typename: 'CiConfigJob',
+};
+
+export const mockIncludesWithBlob = {
+ location: 'test-include.yml',
+ type: 'local',
+ blob:
+ 'http://gdk.test:3000/root/upstream/-/blob/dd54f00bb3645f8ddce7665d2ffb3864540399cb/test-include.yml',
+ raw:
+ 'http://gdk.test:3000/root/upstream/-/raw/dd54f00bb3645f8ddce7665d2ffb3864540399cb/test-include.yml',
+ __typename: 'CiConfigInclude',
+};
+
+export const mockDefaultIncludes = {
+ location: 'npm.gitlab-ci.yml',
+ type: 'template',
+ blob: null,
+ raw:
+ 'https://gitlab.com/gitlab-org/gitlab/-/raw/master/lib/gitlab/ci/templates/npm.gitlab-ci.yml',
+ __typename: 'CiConfigInclude',
+};
+
+export const mockIncludes = [
+ mockDefaultIncludes,
+ mockIncludesWithBlob,
+ {
+ location: 'a_really_really_long_name_for_includes_file.yml',
+ type: 'local',
+ blob:
+ 'http://gdk.test:3000/root/upstream/-/blob/dd54f00bb3645f8ddce7665d2ffb3864540399cb/a_really_really_long_name_for_includes_file.yml',
+ raw:
+ 'http://gdk.test:3000/root/upstream/-/raw/dd54f00bb3645f8ddce7665d2ffb3864540399cb/a_really_really_long_name_for_includes_file.yml',
+ __typename: 'CiConfigInclude',
+ },
+];
+
+// Mock result of the graphql query at:
+// app/assets/javascripts/ci/pipeline_editor/graphql/queries/ci_config.graphql
+export const mockCiConfigQueryResponse = {
+ data: {
+ ciConfig: {
+ errors: [],
+ includes: mockIncludes,
+ mergedYaml: mockCiYml,
+ status: CI_CONFIG_STATUS_VALID,
+ stages: {
+ __typename: 'CiConfigStageConnection',
+ nodes: [
+ {
+ name: 'test',
+ groups: {
+ nodes: [
+ {
+ id: 'group-1',
+ name: 'job_test_1',
+ size: 1,
+ jobs: {
+ nodes: [
+ {
+ name: 'job_test_1',
+ script: ['echo "test 1"'],
+ ...mockJobFields,
+ },
+ ],
+ __typename: 'CiConfigJobConnection',
+ },
+ __typename: 'CiConfigGroup',
+ },
+ {
+ id: 'group-2',
+ name: 'job_test_2',
+ size: 1,
+ jobs: {
+ nodes: [
+ {
+ name: 'job_test_2',
+ script: ['echo "test 2"'],
+ ...mockJobFields,
+ },
+ ],
+ __typename: 'CiConfigJobConnection',
+ },
+ __typename: 'CiConfigGroup',
+ },
+ ],
+ __typename: 'CiConfigGroupConnection',
+ },
+ __typename: 'CiConfigStage',
+ },
+ {
+ name: 'build',
+ groups: {
+ nodes: [
+ {
+ name: 'job_build',
+ size: 1,
+ jobs: {
+ nodes: [
+ {
+ name: 'job_build',
+ script: ['echo "build"'],
+ ...mockJobFields,
+ },
+ ],
+ __typename: 'CiConfigJobConnection',
+ },
+ __typename: 'CiConfigGroup',
+ },
+ ],
+ __typename: 'CiConfigGroupConnection',
+ },
+ __typename: 'CiConfigStage',
+ },
+ ],
+ },
+ __typename: 'CiConfig',
+ },
+ },
+};
+
+export const mergeUnwrappedCiConfig = (mergedConfig) => {
+ const { ciConfig } = mockCiConfigQueryResponse.data;
+ return {
+ ...ciConfig,
+ stages: unwrapStagesWithNeeds(ciConfig.stages.nodes),
+ ...mergedConfig,
+ };
+};
+
+export const mockCommitShaResults = {
+ data: {
+ project: {
+ id: '1',
+ repository: {
+ tree: {
+ lastCommit: {
+ id: 'commit-1',
+ sha: mockCommitSha,
+ },
+ },
+ },
+ },
+ },
+};
+
+export const mockNewCommitShaResults = {
+ data: {
+ project: {
+ id: '1',
+ repository: {
+ tree: {
+ lastCommit: {
+ id: 'commit-1',
+ sha: 'eeff1122',
+ },
+ },
+ },
+ },
+ },
+};
+
+export const mockEmptyCommitShaResults = {
+ data: {
+ project: {
+ id: '1',
+ repository: {
+ tree: {
+ lastCommit: {
+ id: 'commit-1',
+ sha: '',
+ },
+ },
+ },
+ },
+ },
+};
+
+export const mockProjectBranches = {
+ data: {
+ project: {
+ id: '1',
+ repository: {
+ branchNames: [
+ 'main',
+ 'develop',
+ 'production',
+ 'test',
+ 'better-feature',
+ 'feature-abc',
+ 'update-ci',
+ 'mock-feature',
+ 'test-merge-request',
+ 'staging',
+ ],
+ },
+ },
+ },
+};
+
+export const mockTotalBranchResults =
+ mockProjectBranches.data.project.repository.branchNames.length;
+
+export const mockSearchBranches = {
+ data: {
+ project: {
+ id: '1',
+ repository: {
+ branchNames: ['test', 'better-feature', 'update-ci', 'test-merge-request'],
+ },
+ },
+ },
+};
+
+export const mockTotalSearchResults = mockSearchBranches.data.project.repository.branchNames.length;
+
+export const mockEmptySearchBranches = {
+ data: {
+ project: {
+ id: '1',
+ repository: {
+ branchNames: [],
+ },
+ },
+ },
+};
+
+export const mockBranchPaginationLimit = 10;
+export const mockTotalBranches = 20; // must be greater than mockBranchPaginationLimit to test pagination
+
+export const mockProjectPipeline = ({ hasStages = true } = {}) => {
+ const stages = hasStages
+ ? {
+ edges: [
+ {
+ node: {
+ id: 'gid://gitlab/Ci::Stage/605',
+ name: 'prepare',
+ status: 'success',
+ detailedStatus: {
+ detailsPath: '/root/sample-ci-project/-/pipelines/268#prepare',
+ group: 'success',
+ hasDetails: true,
+ icon: 'status_success',
+ id: 'success-605-605',
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
+ },
+ },
+ },
+ ],
+ }
+ : null;
+
+ return {
+ id: '1',
+ pipeline: {
+ id: 'gid://gitlab/Ci::Pipeline/118',
+ iid: '28',
+ shortSha: mockCommitSha,
+ status: 'SUCCESS',
+ commit: {
+ id: 'commit-1',
+ title: 'Update .gitlabe-ci.yml',
+ webPath: '/-/commit/aabbccdd',
+ },
+ detailedStatus: {
+ id: 'status-1',
+ detailsPath: '/root/sample-ci-project/-/pipelines/118',
+ group: 'success',
+ icon: 'status_success',
+ text: 'passed',
+ },
+ stages,
+ },
+ };
+};
+
+export const mockLinkedPipelines = ({ hasDownstream = true, hasUpstream = true } = {}) => {
+ let upstream = null;
+ let downstream = {
+ nodes: [],
+ __typename: 'PipelineConnection',
+ };
+
+ if (hasDownstream) {
+ downstream = {
+ nodes: [
+ {
+ id: 'gid://gitlab/Ci::Pipeline/612',
+ path: '/root/job-log-sections/-/pipelines/612',
+ project: { name: 'job-log-sections', __typename: 'Project' },
+ detailedStatus: {
+ group: 'success',
+ icon: 'status_success',
+ label: 'passed',
+ __typename: 'DetailedStatus',
+ },
+ __typename: 'Pipeline',
+ },
+ ],
+ __typename: 'PipelineConnection',
+ };
+ }
+
+ if (hasUpstream) {
+ upstream = {
+ id: 'gid://gitlab/Ci::Pipeline/610',
+ path: '/root/trigger-downstream/-/pipelines/610',
+ project: { name: 'trigger-downstream', __typename: 'Project' },
+ detailedStatus: {
+ group: 'success',
+ icon: 'status_success',
+ label: 'passed',
+ __typename: 'DetailedStatus',
+ },
+ __typename: 'Pipeline',
+ };
+ }
+
+ return {
+ data: {
+ project: {
+ pipeline: {
+ path: '/root/ci-project/-/pipelines/790',
+ downstream,
+ upstream,
+ },
+ __typename: 'Project',
+ },
+ },
+ };
+};
+
+export const mockLintResponse = {
+ valid: true,
+ mergedYaml: mockCiYml,
+ status: CI_CONFIG_STATUS_VALID,
+ errors: [],
+ warnings: [],
+ jobs: [
+ {
+ name: 'job_1',
+ stage: 'test',
+ before_script: ["echo 'before script 1'"],
+ script: ["echo 'script 1'"],
+ after_script: ["echo 'after script 1"],
+ tag_list: ['tag 1'],
+ environment: 'prd',
+ when: 'on_success',
+ allow_failure: false,
+ only: null,
+ except: { refs: ['main@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
+ },
+ {
+ name: 'job_2',
+ stage: 'test',
+ before_script: ["echo 'before script 2'"],
+ script: ["echo 'script 2'"],
+ after_script: ["echo 'after script 2"],
+ tag_list: ['tag 2'],
+ environment: 'stg',
+ when: 'on_success',
+ allow_failure: true,
+ only: { refs: ['web', 'chat', 'pushes'] },
+ except: { refs: ['main@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
+ },
+ ],
+};
+
+export const mockLintResponseWithoutMerged = {
+ valid: false,
+ status: CI_CONFIG_STATUS_INVALID,
+ errors: ['error'],
+ warnings: [],
+ jobs: [],
+};
+
+export const mockJobs = [
+ {
+ name: 'job_1',
+ stage: 'build',
+ beforeScript: [],
+ script: ["echo 'Building'"],
+ afterScript: [],
+ tagList: [],
+ environment: null,
+ when: 'on_success',
+ allowFailure: true,
+ only: { refs: ['web', 'chat', 'pushes'] },
+ except: null,
+ },
+ {
+ name: 'multi_project_job',
+ stage: 'test',
+ beforeScript: [],
+ script: [],
+ afterScript: [],
+ tagList: [],
+ environment: null,
+ when: 'on_success',
+ allowFailure: false,
+ only: { refs: ['branches', 'tags'] },
+ except: null,
+ },
+ {
+ name: 'job_2',
+ stage: 'test',
+ beforeScript: ["echo 'before script'"],
+ script: ["echo 'script'"],
+ afterScript: ["echo 'after script"],
+ tagList: [],
+ environment: null,
+ when: 'on_success',
+ allowFailure: false,
+ only: { refs: ['branches@gitlab-org/gitlab'] },
+ except: { refs: ['main@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
+ },
+];
+
+export const mockErrors = [
+ '"job_1 job: chosen stage does not exist; available stages are .pre, build, test, deploy, .post"',
+];
+
+export const mockWarnings = [
+ '"jobs:multi_project_job may allow multiple pipelines to run for a single action due to `rules:when` clause with no `workflow:rules` - read more: https://docs.gitlab.com/ee/ci/troubleshooting.html#pipeline-warnings"',
+];
+
+export const mockCommitCreateResponse = {
+ data: {
+ commitCreate: {
+ __typename: 'CommitCreatePayload',
+ errors: [],
+ commit: {
+ __typename: 'Commit',
+ id: 'commit-1',
+ sha: mockCommitNextSha,
+ },
+ commitPipelinePath: '',
+ },
+ },
+};
+
+export const mockCommitCreateResponseNewEtag = {
+ data: {
+ commitCreate: {
+ __typename: 'CommitCreatePayload',
+ errors: [],
+ commit: {
+ __typename: 'Commit',
+ id: 'commit-2',
+ sha: mockCommitNextSha,
+ },
+ commitPipelinePath: '/api/graphql:pipelines/sha/550ceace1acd373c84d02bd539cb9d4614f786db',
+ },
+ },
+};
diff --git a/spec/frontend/ci/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/ci/pipeline_editor/pipeline_editor_app_spec.js
new file mode 100644
index 00000000000..2246d0bbf7e
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/pipeline_editor_app_spec.js
@@ -0,0 +1,589 @@
+import { GlAlert, GlButton, GlLoadingIcon } from '@gitlab/ui';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import setWindowLocation from 'helpers/set_window_location_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+
+import { objectToQuery, redirectTo } from '~/lib/utils/url_utility';
+import { resolvers } from '~/ci/pipeline_editor/graphql/resolvers';
+import PipelineEditorTabs from '~/ci/pipeline_editor/components/pipeline_editor_tabs.vue';
+import PipelineEditorEmptyState from '~/ci/pipeline_editor/components/ui/pipeline_editor_empty_state.vue';
+import PipelineEditorMessages from '~/ci/pipeline_editor/components/ui/pipeline_editor_messages.vue';
+import PipelineEditorHeader from '~/ci/pipeline_editor/components/header/pipeline_editor_header.vue';
+import ValidationSegment, {
+ i18n as validationSegmenti18n,
+} from '~/ci/pipeline_editor/components/header/validation_segment.vue';
+import {
+ COMMIT_SUCCESS,
+ COMMIT_SUCCESS_WITH_REDIRECT,
+ COMMIT_FAILURE,
+ EDITOR_APP_STATUS_LOADING,
+} from '~/ci/pipeline_editor/constants';
+import getBlobContent from '~/ci/pipeline_editor/graphql/queries/blob_content.query.graphql';
+import getCiConfigData from '~/ci/pipeline_editor/graphql/queries/ci_config.query.graphql';
+import getTemplate from '~/ci/pipeline_editor/graphql/queries/get_starter_template.query.graphql';
+import getLatestCommitShaQuery from '~/ci/pipeline_editor/graphql/queries/latest_commit_sha.query.graphql';
+import getPipelineQuery from '~/ci/pipeline_editor/graphql/queries/pipeline.query.graphql';
+import getCurrentBranch from '~/ci/pipeline_editor/graphql/queries/client/current_branch.query.graphql';
+import getAppStatus from '~/ci/pipeline_editor/graphql/queries/client/app_status.query.graphql';
+
+import PipelineEditorApp from '~/ci/pipeline_editor/pipeline_editor_app.vue';
+import PipelineEditorHome from '~/ci/pipeline_editor/pipeline_editor_home.vue';
+
+import {
+ mockCiConfigPath,
+ mockCiConfigQueryResponse,
+ mockBlobContentQueryResponse,
+ mockBlobContentQueryResponseNoCiFile,
+ mockCiYml,
+ mockCiTemplateQueryResponse,
+ mockCommitSha,
+ mockCommitShaResults,
+ mockDefaultBranch,
+ mockEmptyCommitShaResults,
+ mockNewCommitShaResults,
+ mockNewMergeRequestPath,
+ mockProjectFullPath,
+} from './mock_data';
+
+jest.mock('~/lib/utils/url_utility', () => ({
+ ...jest.requireActual('~/lib/utils/url_utility'),
+ redirectTo: jest.fn(),
+}));
+
+const localVue = createLocalVue();
+localVue.use(VueApollo);
+
+const defaultProvide = {
+ ciConfigPath: mockCiConfigPath,
+ defaultBranch: mockDefaultBranch,
+ newMergeRequestPath: mockNewMergeRequestPath,
+ projectFullPath: mockProjectFullPath,
+ usesExternalConfig: false,
+};
+
+describe('Pipeline editor app component', () => {
+ let wrapper;
+
+ let mockApollo;
+ let mockBlobContentData;
+ let mockCiConfigData;
+ let mockGetTemplate;
+ let mockLatestCommitShaQuery;
+ let mockPipelineQuery;
+
+ const createComponent = ({
+ blobLoading = false,
+ options = {},
+ provide = {},
+ stubs = {},
+ } = {}) => {
+ wrapper = shallowMount(PipelineEditorApp, {
+ provide: { ...defaultProvide, ...provide },
+ stubs,
+ mocks: {
+ $apollo: {
+ queries: {
+ initialCiFileContent: {
+ loading: blobLoading,
+ },
+ },
+ },
+ },
+ ...options,
+ });
+ };
+
+ const createComponentWithApollo = async ({
+ provide = {},
+ stubs = {},
+ withUndefinedBranch = false,
+ } = {}) => {
+ const handlers = [
+ [getBlobContent, mockBlobContentData],
+ [getCiConfigData, mockCiConfigData],
+ [getTemplate, mockGetTemplate],
+ [getLatestCommitShaQuery, mockLatestCommitShaQuery],
+ [getPipelineQuery, mockPipelineQuery],
+ ];
+
+ mockApollo = createMockApollo(handlers, resolvers);
+
+ if (!withUndefinedBranch) {
+ mockApollo.clients.defaultClient.cache.writeQuery({
+ query: getCurrentBranch,
+ data: {
+ workBranches: {
+ __typename: 'BranchList',
+ current: {
+ __typename: 'WorkBranch',
+ name: mockDefaultBranch,
+ },
+ },
+ },
+ });
+ }
+
+ mockApollo.clients.defaultClient.cache.writeQuery({
+ query: getAppStatus,
+ data: {
+ app: {
+ __typename: 'AppData',
+ status: EDITOR_APP_STATUS_LOADING,
+ },
+ },
+ });
+
+ const options = {
+ localVue,
+ mocks: {},
+ apolloProvider: mockApollo,
+ };
+
+ createComponent({ provide, stubs, options });
+
+ return waitForPromises();
+ };
+
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findEditorHome = () => wrapper.findComponent(PipelineEditorHome);
+ const findEmptyState = () => wrapper.findComponent(PipelineEditorEmptyState);
+ const findEmptyStateButton = () => findEmptyState().findComponent(GlButton);
+ const findValidationSegment = () => wrapper.findComponent(ValidationSegment);
+
+ beforeEach(() => {
+ mockBlobContentData = jest.fn();
+ mockCiConfigData = jest.fn();
+ mockGetTemplate = jest.fn();
+ mockLatestCommitShaQuery = jest.fn();
+ mockPipelineQuery = jest.fn();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('loading state', () => {
+ it('displays a loading icon if the blob query is loading', () => {
+ createComponent({ blobLoading: true });
+
+ expect(findLoadingIcon().exists()).toBe(true);
+ expect(findEditorHome().exists()).toBe(false);
+ });
+ });
+
+ describe('skipping queries', () => {
+ describe('when branchName is undefined', () => {
+ beforeEach(async () => {
+ await createComponentWithApollo({ withUndefinedBranch: true });
+ });
+
+ it('does not calls getBlobContent', () => {
+ expect(mockBlobContentData).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when branchName is defined', () => {
+ beforeEach(async () => {
+ await createComponentWithApollo();
+ });
+
+ it('calls getBlobContent', () => {
+ expect(mockBlobContentData).toHaveBeenCalled();
+ });
+ });
+
+ describe('when commit sha is undefined', () => {
+ beforeEach(async () => {
+ mockLatestCommitShaQuery.mockResolvedValue(undefined);
+ await createComponentWithApollo();
+ });
+
+ it('calls getBlobContent', () => {
+ expect(mockBlobContentData).toHaveBeenCalled();
+ });
+
+ it('does not call ciConfigData', () => {
+ expect(mockCiConfigData).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when commit sha is defined', () => {
+ beforeEach(async () => {
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
+ mockLatestCommitShaQuery.mockResolvedValue(mockCommitShaResults);
+ await createComponentWithApollo();
+ });
+
+ it('calls ciConfigData', () => {
+ expect(mockCiConfigData).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('when queries are called', () => {
+ beforeEach(() => {
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
+ mockCiConfigData.mockResolvedValue(mockCiConfigQueryResponse);
+ mockLatestCommitShaQuery.mockResolvedValue(mockCommitShaResults);
+ });
+
+ describe('when project uses an external CI config file', () => {
+ beforeEach(async () => {
+ await createComponentWithApollo({
+ provide: {
+ usesExternalConfig: true,
+ },
+ });
+ });
+
+ it('shows an empty state and does not show editor home component', () => {
+ expect(findEmptyState().exists()).toBe(true);
+ expect(findAlert().exists()).toBe(false);
+ expect(findEditorHome().exists()).toBe(false);
+ });
+ });
+
+ describe('when file exists', () => {
+ beforeEach(async () => {
+ await createComponentWithApollo();
+
+ jest
+ .spyOn(wrapper.vm.$apollo.queries.commitSha, 'startPolling')
+ .mockImplementation(jest.fn());
+ });
+
+ it('shows pipeline editor home component', () => {
+ expect(findEditorHome().exists()).toBe(true);
+ });
+
+ it('no error is shown when data is set', () => {
+ expect(findAlert().exists()).toBe(false);
+ });
+
+ it('ci config query is called with correct variables', async () => {
+ expect(mockCiConfigData).toHaveBeenCalledWith({
+ content: mockCiYml,
+ projectPath: mockProjectFullPath,
+ sha: mockCommitSha,
+ });
+ });
+
+ it('does not poll for the commit sha', () => {
+ expect(wrapper.vm.$apollo.queries.commitSha.startPolling).toHaveBeenCalledTimes(0);
+ });
+ });
+
+ describe('when no CI config file exists', () => {
+ beforeEach(async () => {
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponseNoCiFile);
+ await createComponentWithApollo({
+ stubs: {
+ PipelineEditorEmptyState,
+ },
+ });
+
+ jest
+ .spyOn(wrapper.vm.$apollo.queries.commitSha, 'startPolling')
+ .mockImplementation(jest.fn());
+ });
+
+ it('shows an empty state and does not show editor home component', async () => {
+ expect(findEmptyState().exists()).toBe(true);
+ expect(findAlert().exists()).toBe(false);
+ expect(findEditorHome().exists()).toBe(false);
+ });
+
+ it('does not poll for the commit sha', () => {
+ expect(wrapper.vm.$apollo.queries.commitSha.startPolling).toHaveBeenCalledTimes(0);
+ });
+
+ describe('because of a fetching error', () => {
+ it('shows a unkown error message', async () => {
+ const loadUnknownFailureText = 'The CI configuration was not loaded, please try again.';
+
+ mockBlobContentData.mockRejectedValueOnce();
+ await createComponentWithApollo({
+ stubs: {
+ PipelineEditorMessages,
+ },
+ });
+
+ expect(findEmptyState().exists()).toBe(false);
+
+ expect(findAlert().text()).toBe(loadUnknownFailureText);
+ expect(findEditorHome().exists()).toBe(true);
+ });
+ });
+ });
+
+ describe('with no CI config setup', () => {
+ it('user can click on CTA button to get started', async () => {
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponseNoCiFile);
+ mockLatestCommitShaQuery.mockResolvedValue(mockEmptyCommitShaResults);
+
+ await createComponentWithApollo({
+ stubs: {
+ PipelineEditorHome,
+ PipelineEditorEmptyState,
+ },
+ });
+
+ expect(findEmptyState().exists()).toBe(true);
+ expect(findEditorHome().exists()).toBe(false);
+
+ await findEmptyStateButton().vm.$emit('click');
+
+ expect(findEmptyState().exists()).toBe(false);
+ expect(findEditorHome().exists()).toBe(true);
+ });
+ });
+
+ describe('when the lint query returns a 500 error', () => {
+ beforeEach(async () => {
+ mockCiConfigData.mockRejectedValueOnce(new Error(500));
+ await createComponentWithApollo({
+ stubs: { PipelineEditorHome, PipelineEditorHeader, ValidationSegment },
+ });
+ });
+
+ it('shows that the lint service is down', () => {
+ expect(findValidationSegment().text()).toContain(
+ validationSegmenti18n.unavailableValidation,
+ );
+ });
+
+ it('does not report an error or scroll to the top', () => {
+ expect(findAlert().exists()).toBe(false);
+ expect(window.scrollTo).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when the user commits', () => {
+ const updateFailureMessage = 'The GitLab CI configuration could not be updated.';
+ const updateSuccessMessage = 'Your changes have been successfully committed.';
+
+ describe('and the commit mutation succeeds', () => {
+ beforeEach(async () => {
+ window.scrollTo = jest.fn();
+ await createComponentWithApollo({ stubs: { PipelineEditorMessages } });
+
+ findEditorHome().vm.$emit('commit', { type: COMMIT_SUCCESS });
+ });
+
+ it('shows a confirmation message', () => {
+ expect(findAlert().text()).toBe(updateSuccessMessage);
+ });
+
+ it('scrolls to the top of the page to bring attention to the confirmation message', () => {
+ expect(window.scrollTo).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' });
+ });
+
+ it('polls for commit sha while pipeline data is not yet available for current branch', async () => {
+ jest
+ .spyOn(wrapper.vm.$apollo.queries.commitSha, 'startPolling')
+ .mockImplementation(jest.fn());
+
+ // simulate a commit to the current branch
+ findEditorHome().vm.$emit('updateCommitSha');
+ await waitForPromises();
+
+ expect(wrapper.vm.$apollo.queries.commitSha.startPolling).toHaveBeenCalledTimes(1);
+ });
+
+ it('stops polling for commit sha when pipeline data is available for newly committed branch', async () => {
+ jest
+ .spyOn(wrapper.vm.$apollo.queries.commitSha, 'stopPolling')
+ .mockImplementation(jest.fn());
+
+ mockLatestCommitShaQuery.mockResolvedValue(mockCommitShaResults);
+ await wrapper.vm.$apollo.queries.commitSha.refetch();
+
+ expect(wrapper.vm.$apollo.queries.commitSha.stopPolling).toHaveBeenCalledTimes(1);
+ });
+
+ it('stops polling for commit sha when pipeline data is available for current branch', async () => {
+ jest
+ .spyOn(wrapper.vm.$apollo.queries.commitSha, 'stopPolling')
+ .mockImplementation(jest.fn());
+
+ mockLatestCommitShaQuery.mockResolvedValue(mockNewCommitShaResults);
+ findEditorHome().vm.$emit('updateCommitSha');
+ await waitForPromises();
+
+ expect(wrapper.vm.$apollo.queries.commitSha.stopPolling).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('when the commit succeeds with a redirect', () => {
+ const newBranch = 'new-branch';
+
+ beforeEach(async () => {
+ await createComponentWithApollo({ stubs: { PipelineEditorMessages } });
+
+ findEditorHome().vm.$emit('commit', {
+ type: COMMIT_SUCCESS_WITH_REDIRECT,
+ params: { sourceBranch: newBranch, targetBranch: mockDefaultBranch },
+ });
+ });
+
+ it('redirects to the merge request page with source and target branches', () => {
+ const branchesQuery = objectToQuery({
+ 'merge_request[source_branch]': newBranch,
+ 'merge_request[target_branch]': mockDefaultBranch,
+ });
+
+ expect(redirectTo).toHaveBeenCalledWith(`${mockNewMergeRequestPath}?${branchesQuery}`);
+ });
+ });
+
+ describe('and the commit mutation fails', () => {
+ const commitFailedReasons = ['Commit failed'];
+
+ beforeEach(async () => {
+ window.scrollTo = jest.fn();
+ await createComponentWithApollo({ stubs: { PipelineEditorMessages } });
+
+ findEditorHome().vm.$emit('showError', {
+ type: COMMIT_FAILURE,
+ reasons: commitFailedReasons,
+ });
+ });
+
+ it('shows an error message', () => {
+ expect(findAlert().text()).toMatchInterpolatedText(
+ `${updateFailureMessage} ${commitFailedReasons[0]}`,
+ );
+ });
+
+ it('scrolls to the top of the page to bring attention to the error message', () => {
+ expect(window.scrollTo).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' });
+ });
+ });
+
+ describe('when an unknown error occurs', () => {
+ const unknownReasons = ['Commit failed'];
+
+ beforeEach(async () => {
+ window.scrollTo = jest.fn();
+ await createComponentWithApollo({ stubs: { PipelineEditorMessages } });
+
+ findEditorHome().vm.$emit('showError', {
+ type: COMMIT_FAILURE,
+ reasons: unknownReasons,
+ });
+ });
+
+ it('shows an error message', () => {
+ expect(findAlert().text()).toMatchInterpolatedText(
+ `${updateFailureMessage} ${unknownReasons[0]}`,
+ );
+ });
+
+ it('scrolls to the top of the page to bring attention to the error message', () => {
+ expect(window.scrollTo).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' });
+ });
+ });
+ });
+ });
+
+ describe('when refetching content', () => {
+ beforeEach(() => {
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
+ mockCiConfigData.mockResolvedValue(mockCiConfigQueryResponse);
+ mockLatestCommitShaQuery.mockResolvedValue(mockCommitShaResults);
+ });
+
+ it('refetches blob content', async () => {
+ await createComponentWithApollo();
+ jest
+ .spyOn(wrapper.vm.$apollo.queries.initialCiFileContent, 'refetch')
+ .mockImplementation(jest.fn());
+
+ expect(wrapper.vm.$apollo.queries.initialCiFileContent.refetch).toHaveBeenCalledTimes(0);
+
+ await wrapper.vm.refetchContent();
+
+ expect(wrapper.vm.$apollo.queries.initialCiFileContent.refetch).toHaveBeenCalledTimes(1);
+ });
+
+ it('hides start screen when refetch fetches CI file', async () => {
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponseNoCiFile);
+ await createComponentWithApollo();
+
+ expect(findEmptyState().exists()).toBe(true);
+ expect(findEditorHome().exists()).toBe(false);
+
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
+ await wrapper.vm.$apollo.queries.initialCiFileContent.refetch();
+
+ expect(findEmptyState().exists()).toBe(false);
+ expect(findEditorHome().exists()).toBe(true);
+ });
+ });
+
+ describe('when a template parameter is present in the URL', () => {
+ const originalLocation = window.location.href;
+
+ beforeEach(() => {
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
+ mockCiConfigData.mockResolvedValue(mockCiConfigQueryResponse);
+ mockLatestCommitShaQuery.mockResolvedValue(mockCommitShaResults);
+ mockGetTemplate.mockResolvedValue(mockCiTemplateQueryResponse);
+ setWindowLocation('?template=Android');
+ });
+
+ afterEach(() => {
+ setWindowLocation(originalLocation);
+ });
+
+ it('renders the given template', async () => {
+ await createComponentWithApollo({
+ stubs: { PipelineEditorHome, PipelineEditorTabs },
+ });
+
+ expect(mockGetTemplate).toHaveBeenCalledWith({
+ projectPath: mockProjectFullPath,
+ templateName: 'Android',
+ });
+
+ expect(findEmptyState().exists()).toBe(false);
+ expect(findEditorHome().exists()).toBe(true);
+ });
+ });
+
+ describe('when add_new_config_file query param is present', () => {
+ const originalLocation = window.location.href;
+
+ beforeEach(() => {
+ setWindowLocation('?add_new_config_file=true');
+
+ mockCiConfigData.mockResolvedValue(mockCiConfigQueryResponse);
+ });
+
+ afterEach(() => {
+ setWindowLocation(originalLocation);
+ });
+
+ describe('when CI config file does not exist', () => {
+ beforeEach(async () => {
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponseNoCiFile);
+ mockLatestCommitShaQuery.mockResolvedValue(mockEmptyCommitShaResults);
+ mockGetTemplate.mockResolvedValue(mockCiTemplateQueryResponse);
+
+ await createComponentWithApollo();
+
+ jest
+ .spyOn(wrapper.vm.$apollo.queries.commitSha, 'startPolling')
+ .mockImplementation(jest.fn());
+ });
+
+ it('skips empty state and shows editor home component', () => {
+ expect(findEmptyState().exists()).toBe(false);
+ expect(findEditorHome().exists()).toBe(true);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_editor/pipeline_editor_home_spec.js b/spec/frontend/ci/pipeline_editor/pipeline_editor_home_spec.js
new file mode 100644
index 00000000000..621e015e825
--- /dev/null
+++ b/spec/frontend/ci/pipeline_editor/pipeline_editor_home_spec.js
@@ -0,0 +1,330 @@
+import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import { GlButton, GlDrawer, GlModal } from '@gitlab/ui';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import setWindowLocation from 'helpers/set_window_location_helper';
+import CiEditorHeader from '~/ci/pipeline_editor/components/editor/ci_editor_header.vue';
+import CommitSection from '~/ci/pipeline_editor/components/commit/commit_section.vue';
+import PipelineEditorDrawer from '~/ci/pipeline_editor/components/drawer/pipeline_editor_drawer.vue';
+import PipelineEditorFileNav from '~/ci/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue';
+import PipelineEditorFileTree from '~/ci/pipeline_editor/components/file_tree/container.vue';
+import BranchSwitcher from '~/ci/pipeline_editor/components/file_nav/branch_switcher.vue';
+import PipelineEditorHeader from '~/ci/pipeline_editor/components/header/pipeline_editor_header.vue';
+import PipelineEditorTabs from '~/ci/pipeline_editor/components/pipeline_editor_tabs.vue';
+import {
+ CREATE_TAB,
+ FILE_TREE_DISPLAY_KEY,
+ VALIDATE_TAB,
+ MERGED_TAB,
+ TABS_INDEX,
+ VISUALIZE_TAB,
+} from '~/ci/pipeline_editor/constants';
+import PipelineEditorHome from '~/ci/pipeline_editor/pipeline_editor_home.vue';
+
+import { mockLintResponse, mockCiYml } from './mock_data';
+
+jest.mock('~/lib/utils/common_utils');
+
+describe('Pipeline editor home wrapper', () => {
+ let wrapper;
+
+ const createComponent = ({ props = {}, glFeatures = {}, data = {}, stubs = {} } = {}) => {
+ wrapper = extendedWrapper(
+ shallowMount(PipelineEditorHome, {
+ data: () => data,
+ propsData: {
+ ciConfigData: mockLintResponse,
+ ciFileContent: mockCiYml,
+ isCiConfigDataLoading: false,
+ isNewCiConfigFile: false,
+ ...props,
+ },
+ provide: {
+ projectFullPath: '',
+ totalBranches: 19,
+ glFeatures: {
+ ...glFeatures,
+ },
+ },
+ stubs,
+ }),
+ );
+ };
+
+ const findBranchSwitcher = () => wrapper.findComponent(BranchSwitcher);
+ const findCommitSection = () => wrapper.findComponent(CommitSection);
+ const findFileNav = () => wrapper.findComponent(PipelineEditorFileNav);
+ const findModal = () => wrapper.findComponent(GlModal);
+ const findPipelineEditorDrawer = () => wrapper.findComponent(PipelineEditorDrawer);
+ const findPipelineEditorFileTree = () => wrapper.findComponent(PipelineEditorFileTree);
+ const findPipelineEditorHeader = () => wrapper.findComponent(PipelineEditorHeader);
+ const findPipelineEditorTabs = () => wrapper.findComponent(PipelineEditorTabs);
+ const findFileTreeBtn = () => wrapper.findByTestId('file-tree-toggle');
+ const findHelpBtn = () => wrapper.findByTestId('drawer-toggle');
+
+ afterEach(() => {
+ localStorage.clear();
+ wrapper.destroy();
+ });
+
+ describe('renders', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('shows the file nav', () => {
+ expect(findFileNav().exists()).toBe(true);
+ });
+
+ it('shows the pipeline editor header', () => {
+ expect(findPipelineEditorHeader().exists()).toBe(true);
+ });
+
+ it('shows the pipeline editor tabs', () => {
+ expect(findPipelineEditorTabs().exists()).toBe(true);
+ });
+
+ it('shows the commit section by default', () => {
+ expect(findCommitSection().exists()).toBe(true);
+ });
+ });
+
+ describe('modal when switching branch', () => {
+ describe('when `showSwitchBranchModal` value is false', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('is not visible', () => {
+ expect(findModal().exists()).toBe(false);
+ });
+ });
+ describe('when `showSwitchBranchModal` value is true', () => {
+ beforeEach(() => {
+ createComponent({
+ data: { showSwitchBranchModal: true },
+ stubs: { PipelineEditorFileNav },
+ });
+ });
+
+ it('is visible', () => {
+ expect(findModal().exists()).toBe(true);
+ });
+
+ it('pass down `shouldLoadNewBranch` to the branch switcher when primary is selected', async () => {
+ expect(findBranchSwitcher().props('shouldLoadNewBranch')).toBe(false);
+
+ await findModal().vm.$emit('primary');
+
+ expect(findBranchSwitcher().props('shouldLoadNewBranch')).toBe(true);
+ });
+
+ it('closes the modal when secondary action is selected', async () => {
+ expect(findModal().exists()).toBe(true);
+
+ await findModal().vm.$emit('secondary');
+
+ expect(findModal().exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('commit form toggle', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it.each`
+ tab | shouldShow
+ ${MERGED_TAB} | ${false}
+ ${VISUALIZE_TAB} | ${false}
+ ${VALIDATE_TAB} | ${false}
+ ${CREATE_TAB} | ${true}
+ `(
+ 'when the active tab is $tab the commit form is shown: $shouldShow',
+ async ({ tab, shouldShow }) => {
+ expect(findCommitSection().exists()).toBe(true);
+
+ findPipelineEditorTabs().vm.$emit('set-current-tab', tab);
+
+ await nextTick();
+
+ expect(findCommitSection().isVisible()).toBe(shouldShow);
+ },
+ );
+
+ it('shows the commit form again when coming back to the create tab', async () => {
+ expect(findCommitSection().isVisible()).toBe(true);
+
+ findPipelineEditorTabs().vm.$emit('set-current-tab', MERGED_TAB);
+ await nextTick();
+ expect(findCommitSection().isVisible()).toBe(false);
+
+ findPipelineEditorTabs().vm.$emit('set-current-tab', CREATE_TAB);
+ await nextTick();
+ expect(findCommitSection().isVisible()).toBe(true);
+ });
+
+ describe('rendering with tab params', () => {
+ it.each`
+ tab | shouldShow
+ ${MERGED_TAB} | ${false}
+ ${VISUALIZE_TAB} | ${false}
+ ${VALIDATE_TAB} | ${false}
+ ${CREATE_TAB} | ${true}
+ `(
+ 'when the tab query param is $tab the commit form is shown: $shouldShow',
+ async ({ tab, shouldShow }) => {
+ setWindowLocation(`https://gitlab.test/ci/editor/?tab=${TABS_INDEX[tab]}`);
+ await createComponent({ stubs: { PipelineEditorTabs } });
+
+ expect(findCommitSection().isVisible()).toBe(shouldShow);
+ },
+ );
+ });
+ });
+
+ describe('WalkthroughPopover events', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ describe('when "walkthrough-popover-cta-clicked" is emitted from pipeline editor tabs', () => {
+ it('passes down `scrollToCommitForm=true` to commit section', async () => {
+ expect(findCommitSection().props('scrollToCommitForm')).toBe(false);
+ await findPipelineEditorTabs().vm.$emit('walkthrough-popover-cta-clicked');
+ expect(findCommitSection().props('scrollToCommitForm')).toBe(true);
+ });
+ });
+
+ describe('when "scrolled-to-commit-form" is emitted from commit section', () => {
+ it('passes down `scrollToCommitForm=false` to commit section', async () => {
+ await findPipelineEditorTabs().vm.$emit('walkthrough-popover-cta-clicked');
+ expect(findCommitSection().props('scrollToCommitForm')).toBe(true);
+ await findCommitSection().vm.$emit('scrolled-to-commit-form');
+ expect(findCommitSection().props('scrollToCommitForm')).toBe(false);
+ });
+ });
+ });
+
+ describe('help drawer', () => {
+ const clickHelpBtn = async () => {
+ findHelpBtn().vm.$emit('click');
+ await nextTick();
+ };
+
+ it('hides the drawer by default', () => {
+ createComponent();
+
+ expect(findPipelineEditorDrawer().props('isVisible')).toBe(false);
+ });
+
+ it('toggles the drawer on button click', async () => {
+ createComponent({
+ stubs: {
+ CiEditorHeader,
+ GlButton,
+ GlDrawer,
+ PipelineEditorTabs,
+ PipelineEditorDrawer,
+ },
+ });
+
+ await clickHelpBtn();
+
+ expect(findPipelineEditorDrawer().props('isVisible')).toBe(true);
+
+ await clickHelpBtn();
+
+ expect(findPipelineEditorDrawer().props('isVisible')).toBe(false);
+ });
+
+ it("closes the drawer through the drawer's close button", async () => {
+ createComponent({
+ stubs: {
+ CiEditorHeader,
+ GlButton,
+ GlDrawer,
+ PipelineEditorTabs,
+ PipelineEditorDrawer,
+ },
+ });
+
+ await clickHelpBtn();
+
+ expect(findPipelineEditorDrawer().props('isVisible')).toBe(true);
+
+ findPipelineEditorDrawer().findComponent(GlDrawer).vm.$emit('close');
+ await nextTick();
+
+ expect(findPipelineEditorDrawer().props('isVisible')).toBe(false);
+ });
+ });
+
+ describe('file tree', () => {
+ const toggleFileTree = async () => {
+ findFileTreeBtn().vm.$emit('click');
+ await nextTick();
+ };
+
+ describe('button toggle', () => {
+ beforeEach(() => {
+ createComponent({
+ stubs: {
+ GlButton,
+ PipelineEditorFileNav,
+ },
+ });
+ });
+
+ it('shows button toggle', () => {
+ expect(findFileTreeBtn().exists()).toBe(true);
+ });
+
+ it('toggles the drawer on button click', async () => {
+ await toggleFileTree();
+
+ expect(findPipelineEditorFileTree().exists()).toBe(true);
+
+ await toggleFileTree();
+
+ expect(findPipelineEditorFileTree().exists()).toBe(false);
+ });
+
+ it('sets the display state in local storage', async () => {
+ await toggleFileTree();
+
+ expect(localStorage.getItem(FILE_TREE_DISPLAY_KEY)).toBe('true');
+
+ await toggleFileTree();
+
+ expect(localStorage.getItem(FILE_TREE_DISPLAY_KEY)).toBe('false');
+ });
+ });
+
+ describe('when file tree display state is saved in local storage', () => {
+ beforeEach(() => {
+ localStorage.setItem(FILE_TREE_DISPLAY_KEY, 'true');
+ createComponent({
+ stubs: { PipelineEditorFileNav },
+ });
+ });
+
+ it('shows the file tree by default', () => {
+ expect(findPipelineEditorFileTree().exists()).toBe(true);
+ });
+ });
+
+ describe('when file tree display state is not saved in local storage', () => {
+ beforeEach(() => {
+ createComponent({
+ stubs: { PipelineEditorFileNav },
+ });
+ });
+
+ it('hides the file tree by default', () => {
+ expect(findPipelineEditorFileTree().exists()).toBe(false);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_form_spec.js b/spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_form_spec.js
index e5d9b378a42..639c2dbef4c 100644
--- a/spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_form_spec.js
+++ b/spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_form_spec.js
@@ -1,25 +1,160 @@
-import { shallowMount } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
import { GlForm } from '@gitlab/ui';
+import { nextTick } from 'vue';
+import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
+import axios from '~/lib/utils/axios_utils';
import PipelineSchedulesForm from '~/ci/pipeline_schedules/components/pipeline_schedules_form.vue';
+import RefSelector from '~/ref/components/ref_selector.vue';
+import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants';
+import TimezoneDropdown from '~/vue_shared/components/timezone_dropdown/timezone_dropdown.vue';
+import IntervalPatternInput from '~/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue';
+import { timezoneDataFixture } from '../../../vue_shared/components/timezone_dropdown/helpers';
describe('Pipeline schedules form', () => {
let wrapper;
+ const defaultBranch = 'main';
+ const projectId = '1';
+ const cron = '';
+ const dailyLimit = '';
- const createComponent = () => {
- wrapper = shallowMount(PipelineSchedulesForm);
+ const createComponent = (mountFn = shallowMountExtended, stubs = {}) => {
+ wrapper = mountFn(PipelineSchedulesForm, {
+ propsData: {
+ timezoneData: timezoneDataFixture,
+ refParam: 'master',
+ },
+ provide: {
+ fullPath: 'gitlab-org/gitlab',
+ projectId,
+ defaultBranch,
+ cron,
+ cronTimezone: '',
+ dailyLimit,
+ settingsLink: '',
+ },
+ stubs,
+ });
};
const findForm = () => wrapper.findComponent(GlForm);
+ const findDescription = () => wrapper.findByTestId('schedule-description');
+ const findIntervalComponent = () => wrapper.findComponent(IntervalPatternInput);
+ const findTimezoneDropdown = () => wrapper.findComponent(TimezoneDropdown);
+ const findRefSelector = () => wrapper.findComponent(RefSelector);
+ const findSubmitButton = () => wrapper.findByTestId('schedule-submit-button');
+ const findCancelButton = () => wrapper.findByTestId('schedule-cancel-button');
+ // Variables
+ const findVariableRows = () => wrapper.findAllByTestId('ci-variable-row');
+ const findKeyInputs = () => wrapper.findAllByTestId('pipeline-form-ci-variable-key');
+ const findValueInputs = () => wrapper.findAllByTestId('pipeline-form-ci-variable-value');
+ const findRemoveIcons = () => wrapper.findAllByTestId('remove-ci-variable-row');
beforeEach(() => {
createComponent();
});
- afterEach(() => {
- wrapper.destroy();
+ describe('Form elements', () => {
+ it('displays form', () => {
+ expect(findForm().exists()).toBe(true);
+ });
+
+ it('displays the description input', () => {
+ expect(findDescription().exists()).toBe(true);
+ });
+
+ it('displays the interval pattern component', () => {
+ const intervalPattern = findIntervalComponent();
+
+ expect(intervalPattern.exists()).toBe(true);
+ expect(intervalPattern.props()).toMatchObject({
+ initialCronInterval: cron,
+ dailyLimit,
+ sendNativeErrors: false,
+ });
+ });
+
+ it('displays the Timezone dropdown', () => {
+ const timezoneDropdown = findTimezoneDropdown();
+
+ expect(timezoneDropdown.exists()).toBe(true);
+ expect(timezoneDropdown.props()).toMatchObject({
+ value: '',
+ name: 'schedule-timezone',
+ timezoneData: timezoneDataFixture,
+ });
+ });
+
+ it('displays the branch/tag selector', () => {
+ const refSelector = findRefSelector();
+
+ expect(refSelector.exists()).toBe(true);
+ expect(refSelector.props()).toMatchObject({
+ enabledRefTypes: [REF_TYPE_BRANCHES, REF_TYPE_TAGS],
+ value: defaultBranch,
+ projectId,
+ translations: { dropdownHeader: 'Select target branch or tag' },
+ useSymbolicRefNames: true,
+ state: true,
+ name: '',
+ });
+ });
+
+ it('displays the submit and cancel buttons', () => {
+ expect(findSubmitButton().exists()).toBe(true);
+ expect(findCancelButton().exists()).toBe(true);
+ });
});
- it('displays form', () => {
- expect(findForm().exists()).toBe(true);
+ describe('CI variables', () => {
+ let mock;
+
+ const addVariableToForm = () => {
+ const input = findKeyInputs().at(0);
+ input.element.value = 'test_var_2';
+ input.trigger('change');
+ };
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ createComponent(mountExtended);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ it('creates blank variable on input change event', async () => {
+ expect(findVariableRows()).toHaveLength(1);
+
+ addVariableToForm();
+
+ await nextTick();
+
+ expect(findVariableRows()).toHaveLength(2);
+ expect(findKeyInputs().at(1).element.value).toBe('');
+ expect(findValueInputs().at(1).element.value).toBe('');
+ });
+
+ it('does not display remove icon for last row', async () => {
+ addVariableToForm();
+
+ await nextTick();
+
+ expect(findRemoveIcons()).toHaveLength(1);
+ });
+
+ it('removes ci variable row on remove icon button click', async () => {
+ addVariableToForm();
+
+ await nextTick();
+
+ expect(findVariableRows()).toHaveLength(2);
+
+ findRemoveIcons().at(0).trigger('click');
+
+ await nextTick();
+
+ expect(findVariableRows()).toHaveLength(1);
+ });
});
});
diff --git a/spec/frontend/ci/reports/codequality_report/components/codequality_issue_body_spec.js b/spec/frontend/ci/reports/codequality_report/components/codequality_issue_body_spec.js
new file mode 100644
index 00000000000..5ca4b25da9b
--- /dev/null
+++ b/spec/frontend/ci/reports/codequality_report/components/codequality_issue_body_spec.js
@@ -0,0 +1,102 @@
+import { GlIcon } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import component from '~/ci/reports/codequality_report/components/codequality_issue_body.vue';
+import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '~/ci/reports/constants';
+
+describe('code quality issue body issue body', () => {
+ let wrapper;
+
+ const findSeverityIcon = () => wrapper.findByTestId('codequality-severity-icon');
+ const findGlIcon = () => wrapper.findComponent(GlIcon);
+
+ const codequalityIssue = {
+ name:
+ 'rubygem-rest-client: session fixation vulnerability via Set-Cookie headers in 30x redirection responses',
+ path: 'Gemfile.lock',
+ severity: 'normal',
+ type: 'Issue',
+ urlPath: '/Gemfile.lock#L22',
+ };
+
+ const createComponent = (initialStatus, issue = codequalityIssue) => {
+ wrapper = extendedWrapper(
+ shallowMount(component, {
+ propsData: {
+ issue,
+ status: initialStatus,
+ },
+ }),
+ );
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('severity rating', () => {
+ it.each`
+ severity | iconClass | iconName
+ ${'INFO'} | ${'text-primary-400'} | ${'severity-info'}
+ ${'MINOR'} | ${'text-warning-200'} | ${'severity-low'}
+ ${'CRITICAL'} | ${'text-danger-600'} | ${'severity-high'}
+ ${'BLOCKER'} | ${'text-danger-800'} | ${'severity-critical'}
+ ${'UNKNOWN'} | ${'text-secondary-400'} | ${'severity-unknown'}
+ ${'INVALID'} | ${'text-secondary-400'} | ${'severity-unknown'}
+ ${'info'} | ${'text-primary-400'} | ${'severity-info'}
+ ${'minor'} | ${'text-warning-200'} | ${'severity-low'}
+ ${'major'} | ${'text-warning-400'} | ${'severity-medium'}
+ ${'critical'} | ${'text-danger-600'} | ${'severity-high'}
+ ${'blocker'} | ${'text-danger-800'} | ${'severity-critical'}
+ ${'unknown'} | ${'text-secondary-400'} | ${'severity-unknown'}
+ ${'invalid'} | ${'text-secondary-400'} | ${'severity-unknown'}
+ ${undefined} | ${'text-secondary-400'} | ${'severity-unknown'}
+ `(
+ 'renders correct icon for "$severity" severity rating',
+ ({ severity, iconClass, iconName }) => {
+ createComponent(STATUS_FAILED, {
+ ...codequalityIssue,
+ severity,
+ });
+ const icon = findGlIcon();
+
+ expect(findSeverityIcon().classes()).toContain(iconClass);
+ expect(icon.exists()).toBe(true);
+ expect(icon.props('name')).toBe(iconName);
+ },
+ );
+ });
+
+ describe('with success', () => {
+ it('renders fixed label', () => {
+ createComponent(STATUS_SUCCESS);
+
+ expect(wrapper.text()).toContain('Fixed');
+ });
+ });
+
+ describe('without success', () => {
+ it('does not render fixed label', () => {
+ createComponent(STATUS_FAILED);
+
+ expect(wrapper.text()).not.toContain('Fixed');
+ });
+ });
+
+ describe('name', () => {
+ it('renders name', () => {
+ createComponent(STATUS_NEUTRAL);
+
+ expect(wrapper.text()).toContain(codequalityIssue.name);
+ });
+ });
+
+ describe('path', () => {
+ it('renders the report-link path using the correct code quality issue', () => {
+ createComponent(STATUS_NEUTRAL);
+
+ expect(wrapper.find('report-link-stub').props('issue')).toBe(codequalityIssue);
+ });
+ });
+});
diff --git a/spec/frontend/reports/codequality_report/mock_data.js b/spec/frontend/ci/reports/codequality_report/mock_data.js
index 2c994116db6..2c994116db6 100644
--- a/spec/frontend/reports/codequality_report/mock_data.js
+++ b/spec/frontend/ci/reports/codequality_report/mock_data.js
diff --git a/spec/frontend/ci/reports/codequality_report/store/actions_spec.js b/spec/frontend/ci/reports/codequality_report/store/actions_spec.js
new file mode 100644
index 00000000000..88628210793
--- /dev/null
+++ b/spec/frontend/ci/reports/codequality_report/store/actions_spec.js
@@ -0,0 +1,185 @@
+import MockAdapter from 'axios-mock-adapter';
+import testAction from 'helpers/vuex_action_helper';
+import { TEST_HOST } from 'spec/test_constants';
+import axios from '~/lib/utils/axios_utils';
+import createStore from '~/ci/reports/codequality_report/store';
+import * as actions from '~/ci/reports/codequality_report/store/actions';
+import * as types from '~/ci/reports/codequality_report/store/mutation_types';
+import { STATUS_NOT_FOUND } from '~/ci/reports/constants';
+import { reportIssues, parsedReportIssues } from '../mock_data';
+
+const pollInterval = 123;
+const pollIntervalHeader = {
+ 'Poll-Interval': pollInterval,
+};
+
+describe('Codequality Reports actions', () => {
+ let localState;
+ let localStore;
+
+ beforeEach(() => {
+ localStore = createStore();
+ localState = localStore.state;
+ });
+
+ describe('setPaths', () => {
+ it('should commit SET_PATHS mutation', () => {
+ const paths = {
+ baseBlobPath: 'baseBlobPath',
+ headBlobPath: 'headBlobPath',
+ reportsPath: 'reportsPath',
+ };
+
+ return testAction(
+ actions.setPaths,
+ paths,
+ localState,
+ [{ type: types.SET_PATHS, payload: paths }],
+ [],
+ );
+ });
+ });
+
+ describe('fetchReports', () => {
+ const endpoint = `${TEST_HOST}/codequality_reports.json`;
+ let mock;
+
+ beforeEach(() => {
+ localState.reportsPath = endpoint;
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('on success', () => {
+ it('commits REQUEST_REPORTS and dispatches receiveReportsSuccess', () => {
+ mock.onGet(endpoint).reply(200, reportIssues);
+
+ return testAction(
+ actions.fetchReports,
+ null,
+ localState,
+ [{ type: types.REQUEST_REPORTS }],
+ [
+ {
+ payload: parsedReportIssues,
+ type: 'receiveReportsSuccess',
+ },
+ ],
+ );
+ });
+ });
+
+ describe('on error', () => {
+ it('commits REQUEST_REPORTS and dispatches receiveReportsError', () => {
+ mock.onGet(endpoint).reply(500);
+
+ return testAction(
+ actions.fetchReports,
+ null,
+ localState,
+ [{ type: types.REQUEST_REPORTS }],
+ [{ type: 'receiveReportsError', payload: expect.any(Error) }],
+ );
+ });
+ });
+
+ describe('when base report is not found', () => {
+ it('commits REQUEST_REPORTS and dispatches receiveReportsError', () => {
+ const data = { status: STATUS_NOT_FOUND };
+ mock.onGet(`${TEST_HOST}/codequality_reports.json`).reply(200, data);
+
+ return testAction(
+ actions.fetchReports,
+ null,
+ localState,
+ [{ type: types.REQUEST_REPORTS }],
+ [{ type: 'receiveReportsError', payload: data }],
+ );
+ });
+ });
+
+ describe('while waiting for report results', () => {
+ it('continues polling until it receives data', () => {
+ mock
+ .onGet(endpoint)
+ .replyOnce(204, undefined, pollIntervalHeader)
+ .onGet(endpoint)
+ .reply(200, reportIssues);
+
+ return Promise.all([
+ testAction(
+ actions.fetchReports,
+ null,
+ localState,
+ [{ type: types.REQUEST_REPORTS }],
+ [
+ {
+ payload: parsedReportIssues,
+ type: 'receiveReportsSuccess',
+ },
+ ],
+ ),
+ axios
+ // wait for initial NO_CONTENT response to be fulfilled
+ .waitForAll()
+ .then(() => {
+ jest.advanceTimersByTime(pollInterval);
+ }),
+ ]);
+ });
+
+ it('continues polling until it receives an error', () => {
+ mock
+ .onGet(endpoint)
+ .replyOnce(204, undefined, pollIntervalHeader)
+ .onGet(endpoint)
+ .reply(500);
+
+ return Promise.all([
+ testAction(
+ actions.fetchReports,
+ null,
+ localState,
+ [{ type: types.REQUEST_REPORTS }],
+ [{ type: 'receiveReportsError', payload: expect.any(Error) }],
+ ),
+ axios
+ // wait for initial NO_CONTENT response to be fulfilled
+ .waitForAll()
+ .then(() => {
+ jest.advanceTimersByTime(pollInterval);
+ }),
+ ]);
+ });
+ });
+ });
+
+ describe('receiveReportsSuccess', () => {
+ it('commits RECEIVE_REPORTS_SUCCESS', () => {
+ const data = { issues: [] };
+
+ return testAction(
+ actions.receiveReportsSuccess,
+ data,
+ localState,
+ [{ type: types.RECEIVE_REPORTS_SUCCESS, payload: data }],
+ [],
+ );
+ });
+ });
+
+ describe('receiveReportsError', () => {
+ it('commits RECEIVE_REPORTS_ERROR', () => {
+ return testAction(
+ actions.receiveReportsError,
+ null,
+ localState,
+ [{ type: types.RECEIVE_REPORTS_ERROR, payload: null }],
+ [],
+ );
+ });
+ });
+});
diff --git a/spec/frontend/ci/reports/codequality_report/store/getters_spec.js b/spec/frontend/ci/reports/codequality_report/store/getters_spec.js
new file mode 100644
index 00000000000..f4505204f67
--- /dev/null
+++ b/spec/frontend/ci/reports/codequality_report/store/getters_spec.js
@@ -0,0 +1,94 @@
+import createStore from '~/ci/reports/codequality_report/store';
+import * as getters from '~/ci/reports/codequality_report/store/getters';
+import { LOADING, ERROR, SUCCESS, STATUS_NOT_FOUND } from '~/ci/reports/constants';
+
+describe('Codequality reports store getters', () => {
+ let localState;
+ let localStore;
+
+ beforeEach(() => {
+ localStore = createStore();
+ localState = localStore.state;
+ });
+
+ describe('hasCodequalityIssues', () => {
+ describe('when there are issues', () => {
+ it('returns true', () => {
+ localState.newIssues = [{ reason: 'repetitive code' }];
+ localState.resolvedIssues = [];
+
+ expect(getters.hasCodequalityIssues(localState)).toEqual(true);
+
+ localState.newIssues = [];
+ localState.resolvedIssues = [{ reason: 'repetitive code' }];
+
+ expect(getters.hasCodequalityIssues(localState)).toEqual(true);
+ });
+ });
+
+ describe('when there are no issues', () => {
+ it('returns false when there are no issues', () => {
+ expect(getters.hasCodequalityIssues(localState)).toEqual(false);
+ });
+ });
+ });
+
+ describe('codequalityStatus', () => {
+ describe('when loading', () => {
+ it('returns loading status', () => {
+ localState.isLoading = true;
+
+ expect(getters.codequalityStatus(localState)).toEqual(LOADING);
+ });
+ });
+
+ describe('on error', () => {
+ it('returns error status', () => {
+ localState.hasError = true;
+
+ expect(getters.codequalityStatus(localState)).toEqual(ERROR);
+ });
+ });
+
+ describe('when successfully loaded', () => {
+ it('returns error status', () => {
+ expect(getters.codequalityStatus(localState)).toEqual(SUCCESS);
+ });
+ });
+ });
+
+ describe('codequalityText', () => {
+ it.each`
+ resolvedIssues | newIssues | expectedText
+ ${0} | ${0} | ${'No changes to code quality'}
+ ${0} | ${1} | ${'Code quality degraded due to 1 new issue'}
+ ${2} | ${0} | ${'Code quality improved due to 2 resolved issues'}
+ ${1} | ${2} | ${'Code quality scanning detected 3 changes in merged results'}
+ `(
+ 'returns a summary containing $resolvedIssues resolved issues and $newIssues new issues',
+ ({ newIssues, resolvedIssues, expectedText }) => {
+ localState.newIssues = new Array(newIssues).fill({ reason: 'Repetitive code' });
+ localState.resolvedIssues = new Array(resolvedIssues).fill({ reason: 'Repetitive code' });
+
+ expect(getters.codequalityText(localState)).toEqual(expectedText);
+ },
+ );
+ });
+
+ describe('codequalityPopover', () => {
+ describe('when base report is not available', () => {
+ it('returns a popover with a documentation link', () => {
+ localState.status = STATUS_NOT_FOUND;
+ localState.helpPath = 'codequality_help.html';
+
+ expect(getters.codequalityPopover(localState).title).toEqual(
+ 'Base pipeline codequality artifact not found',
+ );
+ expect(getters.codequalityPopover(localState).content).toContain(
+ 'Learn more about codequality reports',
+ 'href="codequality_help.html"',
+ );
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci/reports/codequality_report/store/mutations_spec.js b/spec/frontend/ci/reports/codequality_report/store/mutations_spec.js
new file mode 100644
index 00000000000..22ff86b1040
--- /dev/null
+++ b/spec/frontend/ci/reports/codequality_report/store/mutations_spec.js
@@ -0,0 +1,100 @@
+import createStore from '~/ci/reports/codequality_report/store';
+import mutations from '~/ci/reports/codequality_report/store/mutations';
+import { STATUS_NOT_FOUND } from '~/ci/reports/constants';
+
+describe('Codequality Reports mutations', () => {
+ let localState;
+ let localStore;
+
+ beforeEach(() => {
+ localStore = createStore();
+ localState = localStore.state;
+ });
+
+ describe('SET_PATHS', () => {
+ it('sets paths to given values', () => {
+ const baseBlobPath = 'base/blob/path/';
+ const headBlobPath = 'head/blob/path/';
+ const reportsPath = 'reports.json';
+ const helpPath = 'help.html';
+
+ mutations.SET_PATHS(localState, {
+ baseBlobPath,
+ headBlobPath,
+ reportsPath,
+ helpPath,
+ });
+
+ expect(localState.baseBlobPath).toEqual(baseBlobPath);
+ expect(localState.headBlobPath).toEqual(headBlobPath);
+ expect(localState.reportsPath).toEqual(reportsPath);
+ expect(localState.helpPath).toEqual(helpPath);
+ });
+ });
+
+ describe('REQUEST_REPORTS', () => {
+ it('sets isLoading to true', () => {
+ mutations.REQUEST_REPORTS(localState);
+
+ expect(localState.isLoading).toEqual(true);
+ });
+ });
+
+ describe('RECEIVE_REPORTS_SUCCESS', () => {
+ it('sets isLoading to false', () => {
+ mutations.RECEIVE_REPORTS_SUCCESS(localState, {});
+
+ expect(localState.isLoading).toEqual(false);
+ });
+
+ it('sets hasError to false', () => {
+ mutations.RECEIVE_REPORTS_SUCCESS(localState, {});
+
+ expect(localState.hasError).toEqual(false);
+ });
+
+ it('clears status and statusReason', () => {
+ mutations.RECEIVE_REPORTS_SUCCESS(localState, {});
+
+ expect(localState.status).toEqual('');
+ expect(localState.statusReason).toEqual('');
+ });
+
+ it('sets newIssues and resolvedIssues from response data', () => {
+ const data = { newIssues: [{ id: 1 }], resolvedIssues: [{ id: 2 }] };
+ mutations.RECEIVE_REPORTS_SUCCESS(localState, data);
+
+ expect(localState.newIssues).toEqual(data.newIssues);
+ expect(localState.resolvedIssues).toEqual(data.resolvedIssues);
+ });
+ });
+
+ describe('RECEIVE_REPORTS_ERROR', () => {
+ it('sets isLoading to false', () => {
+ mutations.RECEIVE_REPORTS_ERROR(localState);
+
+ expect(localState.isLoading).toEqual(false);
+ });
+
+ it('sets hasError to true', () => {
+ mutations.RECEIVE_REPORTS_ERROR(localState);
+
+ expect(localState.hasError).toEqual(true);
+ });
+
+ it('sets status based on error object', () => {
+ const error = { status: STATUS_NOT_FOUND };
+ mutations.RECEIVE_REPORTS_ERROR(localState, error);
+
+ expect(localState.status).toEqual(error.status);
+ });
+
+ it('sets statusReason to string from error response data', () => {
+ const data = { status_reason: 'This merge request does not have codequality reports' };
+ const error = { response: { data } };
+ mutations.RECEIVE_REPORTS_ERROR(localState, error);
+
+ expect(localState.statusReason).toEqual(data.status_reason);
+ });
+ });
+});
diff --git a/spec/frontend/ci/reports/codequality_report/store/utils/codequality_parser_spec.js b/spec/frontend/ci/reports/codequality_report/store/utils/codequality_parser_spec.js
new file mode 100644
index 00000000000..f7d82d2b662
--- /dev/null
+++ b/spec/frontend/ci/reports/codequality_report/store/utils/codequality_parser_spec.js
@@ -0,0 +1,86 @@
+import { reportIssues, parsedReportIssues } from 'jest/ci/reports/codequality_report/mock_data';
+import { parseCodeclimateMetrics } from '~/ci/reports/codequality_report/store/utils/codequality_parser';
+
+describe('Codequality report store utils', () => {
+ let result;
+
+ describe('parseCodeclimateMetrics', () => {
+ it('should parse the issues from backend codequality diff', () => {
+ [result] = parseCodeclimateMetrics(reportIssues.new_errors, 'path');
+
+ expect(result.name).toEqual(parsedReportIssues.newIssues[0].name);
+ expect(result.path).toEqual(parsedReportIssues.newIssues[0].path);
+ expect(result.line).toEqual(parsedReportIssues.newIssues[0].line);
+ });
+
+ describe('when an issue has no location or path', () => {
+ const issue = { description: 'Insecure Dependency' };
+
+ beforeEach(() => {
+ [result] = parseCodeclimateMetrics([issue], 'path');
+ });
+
+ it('is parsed', () => {
+ expect(result.name).toEqual(issue.description);
+ });
+ });
+
+ describe('when an issue has a non-nested path', () => {
+ const issue = { description: 'Insecure Dependency', path: 'Gemfile.lock' };
+
+ beforeEach(() => {
+ [result] = parseCodeclimateMetrics([issue], 'path');
+ });
+
+ it('is parsed', () => {
+ expect(result.name).toEqual(issue.description);
+ });
+ });
+
+ describe('when an issue has a path but no line', () => {
+ const issue = { description: 'Insecure Dependency', location: { path: 'Gemfile.lock' } };
+
+ beforeEach(() => {
+ [result] = parseCodeclimateMetrics([issue], 'path');
+ });
+
+ it('is parsed', () => {
+ expect(result.name).toEqual(issue.description);
+ expect(result.path).toEqual(issue.location.path);
+ expect(result.urlPath).toEqual(`path/${issue.location.path}`);
+ });
+ });
+
+ describe('when an issue has a line nested in positions', () => {
+ const issue = {
+ description: 'Insecure Dependency',
+ location: {
+ path: 'Gemfile.lock',
+ positions: { begin: { line: 84 } },
+ },
+ };
+
+ beforeEach(() => {
+ [result] = parseCodeclimateMetrics([issue], 'path');
+ });
+
+ it('is parsed', () => {
+ expect(result.name).toEqual(issue.description);
+ expect(result.path).toEqual(issue.location.path);
+ expect(result.urlPath).toEqual(
+ `path/${issue.location.path}#L${issue.location.positions.begin.line}`,
+ );
+ });
+ });
+
+ describe('with an empty issue array', () => {
+ beforeEach(() => {
+ result = parseCodeclimateMetrics([], 'path');
+ });
+
+ it('returns an empty array', () => {
+ expect(result).toEqual([]);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/reports/components/__snapshots__/grouped_issues_list_spec.js.snap b/spec/frontend/ci/reports/components/__snapshots__/grouped_issues_list_spec.js.snap
index 311a67a3e31..311a67a3e31 100644
--- a/spec/frontend/reports/components/__snapshots__/grouped_issues_list_spec.js.snap
+++ b/spec/frontend/ci/reports/components/__snapshots__/grouped_issues_list_spec.js.snap
diff --git a/spec/frontend/reports/components/__snapshots__/issue_status_icon_spec.js.snap b/spec/frontend/ci/reports/components/__snapshots__/issue_status_icon_spec.js.snap
index b5a4cb42463..b5a4cb42463 100644
--- a/spec/frontend/reports/components/__snapshots__/issue_status_icon_spec.js.snap
+++ b/spec/frontend/ci/reports/components/__snapshots__/issue_status_icon_spec.js.snap
diff --git a/spec/frontend/ci/reports/components/grouped_issues_list_spec.js b/spec/frontend/ci/reports/components/grouped_issues_list_spec.js
new file mode 100644
index 00000000000..3e4adfc7794
--- /dev/null
+++ b/spec/frontend/ci/reports/components/grouped_issues_list_spec.js
@@ -0,0 +1,87 @@
+import { shallowMount } from '@vue/test-utils';
+import GroupedIssuesList from '~/ci/reports/components/grouped_issues_list.vue';
+import ReportItem from '~/ci/reports/components/report_item.vue';
+import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
+
+describe('Grouped Issues List', () => {
+ let wrapper;
+
+ const createComponent = ({ propsData = {}, stubs = {} } = {}) => {
+ wrapper = shallowMount(GroupedIssuesList, {
+ propsData,
+ stubs,
+ });
+ };
+
+ const findHeading = (groupName) => wrapper.find(`[data-testid="${groupName}Heading"`);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders a smart virtual list with the correct props', () => {
+ createComponent({
+ propsData: {
+ resolvedIssues: [{ name: 'foo' }],
+ unresolvedIssues: [{ name: 'bar' }],
+ },
+ stubs: {
+ SmartVirtualList,
+ },
+ });
+
+ expect(wrapper.findComponent(SmartVirtualList).props()).toMatchSnapshot();
+ });
+
+ describe('without data', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it.each(['unresolved', 'resolved'])('does not a render a header for %s issues', (issueName) => {
+ expect(findHeading(issueName).exists()).toBe(false);
+ });
+
+ it.each(['resolved', 'unresolved'])('does not render report items for %s issues', () => {
+ expect(wrapper.findComponent(ReportItem).exists()).toBe(false);
+ });
+ });
+
+ describe('with data', () => {
+ it.each`
+ givenIssues | givenHeading | groupName
+ ${[{ name: 'foo issue' }]} | ${'Foo Heading'} | ${'resolved'}
+ ${[{ name: 'bar issue' }]} | ${'Bar Heading'} | ${'unresolved'}
+ `('renders the heading for $groupName issues', ({ givenIssues, givenHeading, groupName }) => {
+ createComponent({
+ propsData: { [`${groupName}Issues`]: givenIssues, [`${groupName}Heading`]: givenHeading },
+ });
+
+ expect(findHeading(groupName).text()).toBe(givenHeading);
+ });
+
+ it.each(['resolved', 'unresolved'])('renders all %s issues', (issueName) => {
+ const issues = [{ name: 'foo' }, { name: 'bar' }];
+
+ createComponent({
+ propsData: { [`${issueName}Issues`]: issues },
+ });
+
+ expect(wrapper.findAllComponents(ReportItem)).toHaveLength(issues.length);
+ });
+
+ it('renders a report item with the correct props', () => {
+ createComponent({
+ propsData: {
+ resolvedIssues: [{ name: 'foo' }],
+ component: 'CodequalityIssueBody',
+ },
+ stubs: {
+ ReportItem,
+ },
+ });
+
+ expect(wrapper.findComponent(ReportItem).props()).toMatchSnapshot();
+ });
+ });
+});
diff --git a/spec/frontend/ci/reports/components/issue_status_icon_spec.js b/spec/frontend/ci/reports/components/issue_status_icon_spec.js
new file mode 100644
index 00000000000..fb13d4407e2
--- /dev/null
+++ b/spec/frontend/ci/reports/components/issue_status_icon_spec.js
@@ -0,0 +1,29 @@
+import { shallowMount } from '@vue/test-utils';
+import ReportItem from '~/ci/reports/components/issue_status_icon.vue';
+import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '~/ci/reports/constants';
+
+describe('IssueStatusIcon', () => {
+ let wrapper;
+
+ const createComponent = ({ status }) => {
+ wrapper = shallowMount(ReportItem, {
+ propsData: {
+ status,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it.each([STATUS_SUCCESS, STATUS_NEUTRAL, STATUS_FAILED])(
+ 'renders "%s" state correctly',
+ (status) => {
+ createComponent({ status });
+
+ expect(wrapper.element).toMatchSnapshot();
+ },
+ );
+});
diff --git a/spec/frontend/ci/reports/components/report_item_spec.js b/spec/frontend/ci/reports/components/report_item_spec.js
new file mode 100644
index 00000000000..d835d549531
--- /dev/null
+++ b/spec/frontend/ci/reports/components/report_item_spec.js
@@ -0,0 +1,34 @@
+import { shallowMount } from '@vue/test-utils';
+import { componentNames } from '~/ci/reports/components/issue_body';
+import IssueStatusIcon from '~/ci/reports/components/issue_status_icon.vue';
+import ReportItem from '~/ci/reports/components/report_item.vue';
+import { STATUS_SUCCESS } from '~/ci/reports/constants';
+
+describe('ReportItem', () => {
+ describe('showReportSectionStatusIcon', () => {
+ it('does not render CI Status Icon when showReportSectionStatusIcon is false', () => {
+ const wrapper = shallowMount(ReportItem, {
+ propsData: {
+ issue: { foo: 'bar' },
+ component: componentNames.CodequalityIssueBody,
+ status: STATUS_SUCCESS,
+ showReportSectionStatusIcon: false,
+ },
+ });
+
+ expect(wrapper.findComponent(IssueStatusIcon).exists()).toBe(false);
+ });
+
+ it('shows status icon when unspecified', () => {
+ const wrapper = shallowMount(ReportItem, {
+ propsData: {
+ issue: { foo: 'bar' },
+ component: componentNames.CodequalityIssueBody,
+ status: STATUS_SUCCESS,
+ },
+ });
+
+ expect(wrapper.findComponent(IssueStatusIcon).exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/ci/reports/components/report_link_spec.js b/spec/frontend/ci/reports/components/report_link_spec.js
new file mode 100644
index 00000000000..ba541ba0303
--- /dev/null
+++ b/spec/frontend/ci/reports/components/report_link_spec.js
@@ -0,0 +1,56 @@
+import { shallowMount } from '@vue/test-utils';
+import ReportLink from '~/ci/reports/components/report_link.vue';
+
+describe('app/assets/javascripts/ci/reports/components/report_link.vue', () => {
+ let wrapper;
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const defaultProps = {
+ issue: {},
+ };
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(ReportLink, {
+ propsData: { ...defaultProps, ...props },
+ });
+ };
+
+ describe('When an issue prop has a $urlPath property', () => {
+ it('render a link that will take the user to the $urlPath', () => {
+ createComponent({ issue: { path: 'Gemfile.lock', urlPath: '/Gemfile.lock' } });
+
+ expect(wrapper.text()).toContain('in');
+ expect(wrapper.find('a').attributes('href')).toBe('/Gemfile.lock');
+ expect(wrapper.find('a').text()).toContain('Gemfile.lock');
+ });
+ });
+
+ describe('When an issue prop has no $urlPath property', () => {
+ it('does not render link', () => {
+ createComponent({ issue: { path: 'Gemfile.lock' } });
+
+ expect(wrapper.find('a').exists()).toBe(false);
+ expect(wrapper.text()).toContain('in');
+ expect(wrapper.text()).toContain('Gemfile.lock');
+ });
+ });
+
+ describe('When an issue prop has a $line property', () => {
+ it('render a line number', () => {
+ createComponent({ issue: { path: 'Gemfile.lock', urlPath: '/Gemfile.lock', line: 22 } });
+
+ expect(wrapper.find('a').text()).toContain('Gemfile.lock:22');
+ });
+ });
+
+ describe('When an issue prop does not have a $line property', () => {
+ it('does not render a line number', () => {
+ createComponent({ issue: { urlPath: '/Gemfile.lock' } });
+
+ expect(wrapper.find('a').text()).not.toContain(':22');
+ });
+ });
+});
diff --git a/spec/frontend/ci/reports/components/report_section_spec.js b/spec/frontend/ci/reports/components/report_section_spec.js
new file mode 100644
index 00000000000..f032b210184
--- /dev/null
+++ b/spec/frontend/ci/reports/components/report_section_spec.js
@@ -0,0 +1,285 @@
+import { GlButton } from '@gitlab/ui';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import HelpPopover from '~/vue_shared/components/help_popover.vue';
+import ReportItem from '~/ci/reports/components/report_item.vue';
+import ReportSection from '~/ci/reports/components/report_section.vue';
+
+describe('ReportSection component', () => {
+ let wrapper;
+
+ const findExpandButton = () => wrapper.findComponent(GlButton);
+ const findPopover = () => wrapper.findComponent(HelpPopover);
+ const findReportSection = () => wrapper.find('.js-report-section-container');
+ const expectExpandButtonOpen = () =>
+ expect(findExpandButton().props('icon')).toBe('chevron-lg-up');
+ const expectExpandButtonClosed = () =>
+ expect(findExpandButton().props('icon')).toBe('chevron-lg-down');
+
+ const resolvedIssues = [
+ {
+ name: 'Insecure Dependency',
+ fingerprint: 'ca2e59451e98ae60ba2f54e3857c50e5',
+ path: 'Gemfile.lock',
+ line: 12,
+ urlPath: 'foo/Gemfile.lock',
+ },
+ ];
+
+ const defaultProps = {
+ component: '',
+ status: 'SUCCESS',
+ loadingText: 'Loading Code Quality report',
+ errorText: 'foo',
+ successText: 'Code quality improved on 1 point and degraded on 1 point',
+ resolvedIssues,
+ hasIssues: false,
+ alwaysOpen: false,
+ };
+
+ const createComponent = ({ props = {}, data = {}, slots = {} } = {}) => {
+ wrapper = mountExtended(ReportSection, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ data() {
+ return data;
+ },
+ slots,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('computed', () => {
+ describe('isCollapsible', () => {
+ const testMatrix = [
+ { hasIssues: false, alwaysOpen: false, isCollapsible: false },
+ { hasIssues: false, alwaysOpen: true, isCollapsible: false },
+ { hasIssues: true, alwaysOpen: false, isCollapsible: true },
+ { hasIssues: true, alwaysOpen: true, isCollapsible: false },
+ ];
+
+ testMatrix.forEach(({ hasIssues, alwaysOpen, isCollapsible }) => {
+ const issues = hasIssues ? 'has issues' : 'has no issues';
+ const open = alwaysOpen ? 'is always open' : 'is not always open';
+
+ it(`is ${isCollapsible}, if the report ${issues} and ${open}`, () => {
+ createComponent({ props: { hasIssues, alwaysOpen } });
+
+ expect(wrapper.vm.isCollapsible).toBe(isCollapsible);
+ });
+ });
+ });
+
+ describe('isExpanded', () => {
+ const testMatrix = [
+ { isCollapsed: false, alwaysOpen: false, isExpanded: true },
+ { isCollapsed: false, alwaysOpen: true, isExpanded: true },
+ { isCollapsed: true, alwaysOpen: false, isExpanded: false },
+ { isCollapsed: true, alwaysOpen: true, isExpanded: true },
+ ];
+
+ testMatrix.forEach(({ isCollapsed, alwaysOpen, isExpanded }) => {
+ const issues = isCollapsed ? 'is collapsed' : 'is not collapsed';
+ const open = alwaysOpen ? 'is always open' : 'is not always open';
+
+ it(`is ${isExpanded}, if the report ${issues} and ${open}`, () => {
+ createComponent({ props: { alwaysOpen }, data: { isCollapsed } });
+
+ expect(wrapper.vm.isExpanded).toBe(isExpanded);
+ });
+ });
+ });
+ });
+
+ describe('when it is loading', () => {
+ it('should render loading indicator', () => {
+ createComponent({
+ props: {
+ component: '',
+ status: 'LOADING',
+ loadingText: 'Loading Code Quality report',
+ errorText: 'foo',
+ successText: 'Code quality improved on 1 point and degraded on 1 point',
+ hasIssues: false,
+ },
+ });
+
+ expect(wrapper.text()).toBe('Loading Code Quality report');
+ });
+ });
+
+ describe('with success status', () => {
+ it('should render provided data', () => {
+ createComponent({ props: { hasIssues: true } });
+
+ expect(wrapper.find('.js-code-text').text()).toBe(
+ 'Code quality improved on 1 point and degraded on 1 point',
+ );
+ expect(wrapper.findAllComponents(ReportItem)).toHaveLength(resolvedIssues.length);
+ });
+
+ describe('toggleCollapsed', () => {
+ it('toggles issues', async () => {
+ createComponent({ props: { hasIssues: true } });
+
+ await findExpandButton().trigger('click');
+
+ expect(findReportSection().isVisible()).toBe(true);
+ expectExpandButtonOpen();
+
+ await findExpandButton().trigger('click');
+
+ expect(findReportSection().isVisible()).toBe(false);
+ expectExpandButtonClosed();
+ });
+
+ it('is always expanded, if always-open is set to true', () => {
+ createComponent({ props: { hasIssues: true, alwaysOpen: true } });
+
+ expect(findReportSection().isVisible()).toBe(true);
+ expect(findExpandButton().exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('snowplow events', () => {
+ it('does emit an event on issue toggle if the shouldEmitToggleEvent prop does exist', () => {
+ createComponent({ props: { hasIssues: true, shouldEmitToggleEvent: true } });
+
+ expect(wrapper.emitted('toggleEvent')).toBeUndefined();
+
+ findExpandButton().trigger('click');
+
+ expect(wrapper.emitted('toggleEvent')).toEqual([[]]);
+ });
+
+ it('does not emit an event on issue toggle if the shouldEmitToggleEvent prop does not exist', () => {
+ createComponent({ props: { hasIssues: true } });
+
+ expect(wrapper.emitted('toggleEvent')).toBeUndefined();
+
+ findExpandButton().trigger('click');
+
+ expect(wrapper.emitted('toggleEvent')).toBeUndefined();
+ });
+
+ it('does not emit an event if always-open is set to true', () => {
+ createComponent({
+ props: { alwaysOpen: true, hasIssues: true, shouldEmitToggleEvent: true },
+ });
+
+ expect(wrapper.emitted('toggleEvent')).toBeUndefined();
+ });
+ });
+
+ describe('with failed request', () => {
+ it('should render error indicator', () => {
+ createComponent({
+ props: {
+ component: '',
+ status: 'ERROR',
+ loadingText: 'Loading Code Quality report',
+ errorText: 'Failed to load Code Quality report',
+ successText: 'Code quality improved on 1 point and degraded on 1 point',
+ hasIssues: false,
+ },
+ });
+
+ expect(wrapper.text()).toBe('Failed to load Code Quality report');
+ });
+ });
+
+ describe('with action buttons passed to the slot', () => {
+ beforeEach(() => {
+ createComponent({
+ props: {
+ status: 'SUCCESS',
+ successText: 'success',
+ hasIssues: true,
+ },
+ slots: {
+ 'action-buttons': ['Action!'],
+ },
+ });
+ });
+
+ it('should render the passed button', () => {
+ expect(wrapper.text()).toContain('Action!');
+ });
+
+ it('should still render the expand/collapse button', () => {
+ expectExpandButtonClosed();
+ });
+ });
+
+ describe('Success and Error slots', () => {
+ const createComponentWithSlots = (status) => {
+ createComponent({
+ props: {
+ status,
+ hasIssues: true,
+ },
+ slots: {
+ success: ['This is a success'],
+ loading: ['This is loading'],
+ error: ['This is an error'],
+ },
+ });
+ };
+
+ it('only renders success slot when status is "SUCCESS"', () => {
+ createComponentWithSlots('SUCCESS');
+
+ expect(wrapper.text()).toContain('This is a success');
+ expect(wrapper.text()).not.toContain('This is an error');
+ expect(wrapper.text()).not.toContain('This is loading');
+ });
+
+ it('only renders error slot when status is "ERROR"', () => {
+ createComponentWithSlots('ERROR');
+
+ expect(wrapper.text()).toContain('This is an error');
+ expect(wrapper.text()).not.toContain('This is a success');
+ expect(wrapper.text()).not.toContain('This is loading');
+ });
+
+ it('only renders loading slot when status is "LOADING"', () => {
+ createComponentWithSlots('LOADING');
+
+ expect(wrapper.text()).toContain('This is loading');
+ expect(wrapper.text()).not.toContain('This is an error');
+ expect(wrapper.text()).not.toContain('This is a success');
+ });
+ });
+
+ describe('help popover', () => {
+ describe('when popover options are defined', () => {
+ const options = {
+ title: 'foo',
+ content: 'bar',
+ };
+
+ beforeEach(() => {
+ createComponent({ props: { popoverOptions: options } });
+ });
+
+ it('popover is shown with options', () => {
+ expect(findPopover().props('options')).toEqual(options);
+ });
+ });
+
+ describe('when popover options are not defined', () => {
+ beforeEach(() => {
+ createComponent({ props: { popoverOptions: {} } });
+ });
+
+ it('popover is not shown', () => {
+ expect(findPopover().exists()).toBe(false);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci/reports/components/summary_row_spec.js b/spec/frontend/ci/reports/components/summary_row_spec.js
new file mode 100644
index 00000000000..fb2ae5371d5
--- /dev/null
+++ b/spec/frontend/ci/reports/components/summary_row_spec.js
@@ -0,0 +1,68 @@
+import { mount } from '@vue/test-utils';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import HelpPopover from '~/vue_shared/components/help_popover.vue';
+import SummaryRow from '~/ci/reports/components/summary_row.vue';
+
+describe('Summary row', () => {
+ let wrapper;
+
+ const summary = 'SAST detected 1 new vulnerability and 1 fixed vulnerability';
+ const popoverOptions = {
+ title: 'Static Application Security Testing (SAST)',
+ content: '<a>Learn more about SAST</a>',
+ };
+ const statusIcon = 'warning';
+
+ const createComponent = ({ props = {}, slots = {} } = {}) => {
+ wrapper = extendedWrapper(
+ mount(SummaryRow, {
+ propsData: {
+ summary,
+ popoverOptions,
+ statusIcon,
+ ...props,
+ },
+ slots,
+ }),
+ );
+ };
+
+ const findSummary = () => wrapper.findByTestId('summary-row-description');
+ const findStatusIcon = () => wrapper.findByTestId('summary-row-icon');
+ const findHelpPopover = () => wrapper.findComponent(HelpPopover);
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('renders provided summary', () => {
+ createComponent();
+ expect(findSummary().text()).toContain(summary);
+ });
+
+ it('renders provided icon', () => {
+ createComponent();
+ expect(findStatusIcon().classes()).toContain('js-ci-status-icon-warning');
+ });
+
+ it('renders help popover if popoverOptions are provided', () => {
+ createComponent();
+ expect(findHelpPopover().props('options')).toEqual(popoverOptions);
+ });
+
+ it('does not render help popover if popoverOptions are not provided', () => {
+ createComponent({ props: { popoverOptions: null } });
+ expect(findHelpPopover().exists()).toBe(false);
+ });
+
+ describe('summary slot', () => {
+ it('replaces the summary prop', () => {
+ const summarySlotContent = 'Summary slot content';
+ createComponent({ slots: { summary: summarySlotContent } });
+
+ expect(wrapper.text()).not.toContain(summary);
+ expect(findSummary().text()).toContain(summarySlotContent);
+ });
+ });
+});
diff --git a/spec/frontend/reports/mock_data/mock_data.js b/spec/frontend/ci/reports/mock_data/mock_data.js
index 2599b0ac365..2599b0ac365 100644
--- a/spec/frontend/reports/mock_data/mock_data.js
+++ b/spec/frontend/ci/reports/mock_data/mock_data.js
diff --git a/spec/frontend/ci/reports/mock_data/new_and_fixed_failures_report.json b/spec/frontend/ci/reports/mock_data/new_and_fixed_failures_report.json
new file mode 100644
index 00000000000..9018ad5e4cf
--- /dev/null
+++ b/spec/frontend/ci/reports/mock_data/new_and_fixed_failures_report.json
@@ -0,0 +1,70 @@
+{
+ "status": "failed",
+ "summary": {
+ "total": 11,
+ "resolved": 2,
+ "errored": 0,
+ "failed": 2
+ },
+ "suites": [
+ {
+ "name": "rspec:pg",
+ "status": "failed",
+ "summary": {
+ "total": 8,
+ "resolved": 2,
+ "errored": 0,
+ "failed": 1
+ },
+ "new_failures": [
+ {
+ "status": "failed",
+ "name": "Test#subtract when a is 2 and b is 1 returns correct result",
+ "execution_time": 0.00908,
+ "system_output": "Failure/Error: is_expected.to eq(1)\n\n expected: 1\n got: 3\n\n (compared using ==)\n./spec/test_spec.rb:43:in `block (4 levels) in <top (required)>'"
+ }
+ ],
+ "resolved_failures": [
+ {
+ "status": "success",
+ "name": "Test#sum when a is 1 and b is 2 returns summary",
+ "execution_time": 0.000318,
+ "system_output": null
+ },
+ {
+ "status": "success",
+ "name": "Test#sum when a is 100 and b is 200 returns summary",
+ "execution_time": 0.000074,
+ "system_output": null
+ }
+ ],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ },
+ {
+ "name": "java ant",
+ "status": "failed",
+ "summary": {
+ "total": 3,
+ "resolved": 0,
+ "errored": 0,
+ "failed": 1
+ },
+ "new_failures": [],
+ "resolved_failures": [],
+ "existing_failures": [
+ {
+ "status": "failed",
+ "name": "sumTest",
+ "execution_time": 0.004,
+ "system_output": "junit.framework.AssertionFailedError: expected:<3> but was:<-1>\n\tat CalculatorTest.sumTest(Unknown Source)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n"
+ }
+ ],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ }
+ ]
+} \ No newline at end of file
diff --git a/spec/frontend/ci/reports/mock_data/new_errors_report.json b/spec/frontend/ci/reports/mock_data/new_errors_report.json
new file mode 100644
index 00000000000..d3fb570c327
--- /dev/null
+++ b/spec/frontend/ci/reports/mock_data/new_errors_report.json
@@ -0,0 +1,53 @@
+{
+ "summary": {
+ "total": 11,
+ "resolved": 0,
+ "errored": 2,
+ "failed": 0
+ },
+ "suites": [
+ {
+ "name": "karma",
+ "summary": {
+ "total": 3,
+ "resolved": 0,
+ "errored": 2,
+ "failed": 0
+ },
+ "new_failures": [],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [
+ {
+ "result": "error",
+ "name": "Test#sum when a is 1 and b is 2 returns summary",
+ "execution_time": 0.009411,
+ "system_output": "Failed: Error in render: 'TypeError: Cannot read property 'status' of undefined'"
+ },
+ {
+ "result": "error",
+ "name": "Test#sum when a is 100 and b is 200 returns summary",
+ "execution_time": 0.000162,
+ "system_output": "Failed: Error in render: 'TypeError: Cannot read property 'length' of undefined'"
+ }
+ ],
+ "resolved_errors": [],
+ "existing_errors": []
+ },
+ {
+ "name": "rspec:pg",
+ "summary": {
+ "total": 8,
+ "resolved": 0,
+ "errored": 0,
+ "failed": 0
+ },
+ "new_failures": [],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ }
+ ]
+} \ No newline at end of file
diff --git a/spec/frontend/ci/reports/mock_data/new_failures_report.json b/spec/frontend/ci/reports/mock_data/new_failures_report.json
new file mode 100644
index 00000000000..03a875b7636
--- /dev/null
+++ b/spec/frontend/ci/reports/mock_data/new_failures_report.json
@@ -0,0 +1,55 @@
+{
+ "summary": {
+ "total": 11,
+ "resolved": 0,
+ "errored": 0,
+ "failed": 2
+ },
+ "suites": [
+ {
+ "name": "rspec:pg",
+ "summary": {
+ "total": 8,
+ "resolved": 0,
+ "errored": 0,
+ "failed": 2
+ },
+ "new_failures": [
+ {
+ "result": "failure",
+ "name": "Test#sum when a is 1 and b is 2 returns summary",
+ "file": "spec/file_1.rb",
+ "execution_time": 0.009411,
+ "system_output": "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in <top (required)>'"
+ },
+ {
+ "result": "failure",
+ "name": "Test#sum when a is 100 and b is 200 returns summary",
+ "file": "spec/file_2.rb",
+ "execution_time": 0.000162,
+ "system_output": "Failure/Error: is_expected.to eq(300)\n\n expected: 300\n got: -100\n\n (compared using ==)\n./spec/test_spec.rb:21:in `block (4 levels) in <top (required)>'"
+ }
+ ],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ },
+ {
+ "name": "java ant",
+ "summary": {
+ "total": 3,
+ "resolved": 0,
+ "errored": 0,
+ "failed": 0
+ },
+ "new_failures": [],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ }
+ ]
+} \ No newline at end of file
diff --git a/spec/frontend/ci/reports/mock_data/new_failures_with_null_files_report.json b/spec/frontend/ci/reports/mock_data/new_failures_with_null_files_report.json
new file mode 100644
index 00000000000..00a35a3d0a7
--- /dev/null
+++ b/spec/frontend/ci/reports/mock_data/new_failures_with_null_files_report.json
@@ -0,0 +1,55 @@
+{
+ "summary": {
+ "total": 11,
+ "resolved": 0,
+ "errored": 0,
+ "failed": 2
+ },
+ "suites": [
+ {
+ "name": "rspec:pg",
+ "summary": {
+ "total": 8,
+ "resolved": 0,
+ "errored": 0,
+ "failed": 2
+ },
+ "new_failures": [
+ {
+ "result": "failure",
+ "name": "Test#sum when a is 1 and b is 2 returns summary",
+ "file": null,
+ "execution_time": 0.009411,
+ "system_output": "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in <top (required)>'"
+ },
+ {
+ "result": "failure",
+ "name": "Test#sum when a is 100 and b is 200 returns summary",
+ "file": null,
+ "execution_time": 0.000162,
+ "system_output": "Failure/Error: is_expected.to eq(300)\n\n expected: 300\n got: -100\n\n (compared using ==)\n./spec/test_spec.rb:21:in `block (4 levels) in <top (required)>'"
+ }
+ ],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ },
+ {
+ "name": "java ant",
+ "summary": {
+ "total": 3,
+ "resolved": 0,
+ "errored": 0,
+ "failed": 0
+ },
+ "new_failures": [],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ }
+ ]
+} \ No newline at end of file
diff --git a/spec/frontend/ci/reports/mock_data/no_failures_report.json b/spec/frontend/ci/reports/mock_data/no_failures_report.json
new file mode 100644
index 00000000000..a48a206208d
--- /dev/null
+++ b/spec/frontend/ci/reports/mock_data/no_failures_report.json
@@ -0,0 +1,43 @@
+{
+ "status": "success",
+ "summary": {
+ "total": 11,
+ "resolved": 0,
+ "errored": 0,
+ "failed": 0
+ },
+ "suites": [
+ {
+ "name": "rspec:pg",
+ "status": "success",
+ "summary": {
+ "total": 8,
+ "resolved": 0,
+ "errored": 0,
+ "failed": 0
+ },
+ "new_failures": [],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ },
+ {
+ "name": "java ant",
+ "status": "success",
+ "summary": {
+ "total": 3,
+ "resolved": 0,
+ "errored": 0,
+ "failed": 0
+ },
+ "new_failures": [],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ }
+ ]
+} \ No newline at end of file
diff --git a/spec/frontend/ci/reports/mock_data/recent_failures_report.json b/spec/frontend/ci/reports/mock_data/recent_failures_report.json
new file mode 100644
index 00000000000..f4fc2d2e927
--- /dev/null
+++ b/spec/frontend/ci/reports/mock_data/recent_failures_report.json
@@ -0,0 +1,70 @@
+{
+ "summary": {
+ "total": 11,
+ "resolved": 0,
+ "errored": 0,
+ "failed": 3,
+ "recentlyFailed": 2
+ },
+ "suites": [
+ {
+ "name": "rspec:pg",
+ "summary": {
+ "total": 8,
+ "resolved": 0,
+ "errored": 0,
+ "failed": 2,
+ "recentlyFailed": 1
+ },
+ "new_failures": [
+ {
+ "result": "failure",
+ "name": "Test#sum when a is 1 and b is 2 returns summary",
+ "execution_time": 0.009411,
+ "system_output": "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in <top (required)>'",
+ "recent_failures": {
+ "count": 8,
+ "base_branch": "main"
+ }
+ },
+ {
+ "result": "failure",
+ "name": "Test#sum when a is 100 and b is 200 returns summary",
+ "execution_time": 0.000162,
+ "system_output": "Failure/Error: is_expected.to eq(300)\n\n expected: 300\n got: -100\n\n (compared using ==)\n./spec/test_spec.rb:21:in `block (4 levels) in <top (required)>'"
+ }
+ ],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ },
+ {
+ "name": "java ant",
+ "summary": {
+ "total": 3,
+ "resolved": 0,
+ "errored": 0,
+ "failed": 1,
+ "recentlyFailed": 1
+ },
+ "new_failures": [
+ {
+ "result": "failure",
+ "name": "Test#sum when a is 100 and b is 200 returns summary",
+ "execution_time": 0.000562,
+ "recent_failures": {
+ "count": 3,
+ "base_branch": "main"
+ }
+ }
+ ],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ }
+ ]
+} \ No newline at end of file
diff --git a/spec/frontend/ci/reports/mock_data/resolved_failures.json b/spec/frontend/ci/reports/mock_data/resolved_failures.json
new file mode 100644
index 00000000000..15012fb027d
--- /dev/null
+++ b/spec/frontend/ci/reports/mock_data/resolved_failures.json
@@ -0,0 +1,73 @@
+{
+ "status": "success",
+ "summary": {
+ "total": 11,
+ "resolved": 4,
+ "errored": 0,
+ "failed": 0
+ },
+ "suites": [
+ {
+ "name": "rspec:pg",
+ "status": "success",
+ "summary": {
+ "total": 8,
+ "resolved": 4,
+ "errored": 0,
+ "failed": 0
+ },
+ "new_failures": [],
+ "resolved_failures": [
+ {
+ "status": "success",
+ "name": "Test#sum when a is 1 and b is 2 returns summary",
+ "execution_time": 0.000411,
+ "system_output": null,
+ "stack_trace": null
+ },
+ {
+ "status": "success",
+ "name": "Test#sum when a is 100 and b is 200 returns summary",
+ "execution_time": 0.000076,
+ "system_output": null,
+ "stack_trace": null
+ }
+ ],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [
+ {
+ "status": "success",
+ "name": "Test#sum when a is 4 and b is 4 returns summary",
+ "execution_time": 0.00342,
+ "system_output": null,
+ "stack_trace": null
+ },
+ {
+ "status": "success",
+ "name": "Test#sum when a is 40 and b is 400 returns summary",
+ "execution_time": 0.0000231,
+ "system_output": null,
+ "stack_trace": null
+ }
+ ],
+ "existing_errors": []
+ },
+ {
+ "name": "java ant",
+ "status": "success",
+ "summary": {
+ "total": 3,
+ "resolved": 0,
+ "errored": 0,
+ "failed": 0
+ },
+ "new_failures": [],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ }
+ ]
+} \ No newline at end of file
diff --git a/spec/frontend/ci/runner/admin_runner_show/admin_runner_show_app_spec.js b/spec/frontend/ci/runner/admin_runner_show/admin_runner_show_app_spec.js
index 7081bc57467..e233268b756 100644
--- a/spec/frontend/ci/runner/admin_runner_show/admin_runner_show_app_spec.js
+++ b/spec/frontend/ci/runner/admin_runner_show/admin_runner_show_app_spec.js
@@ -1,6 +1,8 @@
import Vue from 'vue';
import { GlTab, GlTabs } from '@gitlab/ui';
+import VueRouter from 'vue-router';
import VueApollo from 'vue-apollo';
+import setWindowLocation from 'helpers/set_window_location_helper';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
@@ -33,12 +35,15 @@ const mockRunnerId = `${getIdFromGraphQLId(mockRunnerGraphqlId)}`;
const mockRunnersPath = '/admin/runners';
Vue.use(VueApollo);
+Vue.use(VueRouter);
describe('AdminRunnerShowApp', () => {
let wrapper;
let mockRunnerQuery;
const findRunnerHeader = () => wrapper.findComponent(RunnerHeader);
+ const findTabs = () => wrapper.findComponent(GlTabs);
+ const findTabAt = (i) => wrapper.findAllComponents(GlTab).at(i);
const findRunnerDetails = () => wrapper.findComponent(RunnerDetails);
const findRunnerDeleteButton = () => wrapper.findComponent(RunnerDeleteButton);
const findRunnerEditButton = () => wrapper.findComponent(RunnerEditButton);
@@ -113,6 +118,16 @@ describe('AdminRunnerShowApp', () => {
expect(wrapper.text().replace(/\s+/g, ' ')).toContain(expected);
});
+ it.each(['#/', '#/unknown-tab'])('shows details when location hash is `%s`', async (hash) => {
+ setWindowLocation(hash);
+
+ await createComponent({ mountFn: mountExtended });
+
+ expect(findTabs().props('value')).toBe(0);
+ expect(findRunnerDetails().exists()).toBe(true);
+ expect(findRunnersJobs().exists()).toBe(false);
+ });
+
describe('when runner cannot be updated', () => {
beforeEach(async () => {
mockRunnerQueryResult({
@@ -226,7 +241,7 @@ describe('AdminRunnerShowApp', () => {
});
});
- describe('Jobs tab', () => {
+ describe('When showing jobs', () => {
const stubs = {
GlTab,
GlTabs,
@@ -245,6 +260,17 @@ describe('AdminRunnerShowApp', () => {
expect(findRunnersJobs().exists()).toBe(false);
});
+ it('when URL hash links to jobs tab', async () => {
+ mockRunnerQueryResult();
+ setWindowLocation('#/jobs');
+
+ await createComponent({ mountFn: mountExtended });
+
+ expect(findTabs().props('value')).toBe(1);
+ expect(findRunnerDetails().exists()).toBe(false);
+ expect(findRunnersJobs().exists()).toBe(true);
+ });
+
it('without a job count, shows no jobs count', async () => {
mockRunnerQueryResult({ jobCount: null });
@@ -260,7 +286,28 @@ describe('AdminRunnerShowApp', () => {
await createComponent({ stubs });
expect(findJobCountBadge().text()).toBe('3');
- expect(findRunnersJobs().props('runner')).toEqual({ ...mockRunner, ...runner });
+ });
+ });
+
+ describe('When navigating to another tab', () => {
+ let routerPush;
+
+ beforeEach(async () => {
+ mockRunnerQueryResult();
+
+ await createComponent({ mountFn: mountExtended });
+
+ routerPush = jest.spyOn(wrapper.vm.$router, 'push').mockImplementation(() => {});
+ });
+
+ it('navigates to details', () => {
+ findTabAt(0).vm.$emit('click');
+ expect(routerPush).toHaveBeenLastCalledWith({ name: 'details' });
+ });
+
+ it('navigates to job', () => {
+ findTabAt(1).vm.$emit('click');
+ expect(routerPush).toHaveBeenLastCalledWith({ name: 'jobs' });
});
});
});
diff --git a/spec/frontend/ci/runner/admin_runners/admin_runners_app_spec.js b/spec/frontend/ci/runner/admin_runners/admin_runners_app_spec.js
index 9778a6fe66c..9084ecdb4cc 100644
--- a/spec/frontend/ci/runner/admin_runners/admin_runners_app_spec.js
+++ b/spec/frontend/ci/runner/admin_runners/admin_runners_app_spec.js
@@ -25,6 +25,7 @@ import RunnerStats from '~/ci/runner/components/stat/runner_stats.vue';
import RunnerActionsCell from '~/ci/runner/components/cells/runner_actions_cell.vue';
import RegistrationDropdown from '~/ci/runner/components/registration/registration_dropdown.vue';
import RunnerPagination from '~/ci/runner/components/runner_pagination.vue';
+import RunnerJobStatusBadge from '~/ci/runner/components/runner_job_status_badge.vue';
import {
ADMIN_FILTERED_SEARCH_NAMESPACE,
@@ -77,7 +78,9 @@ jest.mock('~/lib/utils/url_utility', () => ({
Vue.use(VueApollo);
Vue.use(GlToast);
-const COUNT_QUERIES = 7; // 4 tabs + 3 status queries
+const STATUS_COUNT_QUERIES = 3;
+const TAB_COUNT_QUERIES = 4;
+const COUNT_QUERIES = TAB_COUNT_QUERIES + STATUS_COUNT_QUERIES;
describe('AdminRunnersApp', () => {
let wrapper;
@@ -170,6 +173,29 @@ describe('AdminRunnersApp', () => {
});
});
+ describe('does not show total runner counts when total is 0', () => {
+ beforeEach(async () => {
+ mockRunnersCountHandler.mockResolvedValue({
+ data: {
+ runners: {
+ count: 0,
+ ...runnersCountData.runners,
+ },
+ },
+ });
+
+ await createComponent({ mountFn: mountExtended });
+ });
+
+ it('fetches only tab counts', () => {
+ expect(mockRunnersCountHandler).toHaveBeenCalledTimes(TAB_COUNT_QUERIES);
+ });
+
+ it('does not shows counters', () => {
+ expect(findRunnerStats().text()).toBe('');
+ });
+ });
+
it('shows the runners list', async () => {
await createComponent();
@@ -252,6 +278,15 @@ describe('AdminRunnersApp', () => {
expect(runnerLink.attributes('href')).toBe(`http://localhost/admin/runners/${id}`);
});
+ it('Shows job status and links to jobs', () => {
+ const badge = wrapper
+ .find('tr [data-testid="td-summary"]')
+ .findComponent(RunnerJobStatusBadge);
+
+ expect(badge.props('jobStatus')).toBe(mockRunners[0].jobExecutionStatus);
+ expect(badge.attributes('href')).toBe(`http://localhost/admin/runners/${id}#/jobs`);
+ });
+
it('When runner is paused or unpaused, some data is refetched', async () => {
expect(mockRunnersCountHandler).toHaveBeenCalledTimes(COUNT_QUERIES);
diff --git a/spec/frontend/ci/runner/components/cells/runner_stacked_summary_cell_spec.js b/spec/frontend/ci/runner/components/cells/runner_stacked_summary_cell_spec.js
deleted file mode 100644
index 4aa354f9b62..00000000000
--- a/spec/frontend/ci/runner/components/cells/runner_stacked_summary_cell_spec.js
+++ /dev/null
@@ -1,164 +0,0 @@
-import { __ } from '~/locale';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import RunnerStackedSummaryCell from '~/ci/runner/components/cells/runner_stacked_summary_cell.vue';
-import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
-import RunnerTags from '~/ci/runner/components/runner_tags.vue';
-import RunnerSummaryField from '~/ci/runner/components/cells/runner_summary_field.vue';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-
-import { INSTANCE_TYPE, I18N_INSTANCE_TYPE, PROJECT_TYPE } from '~/ci/runner/constants';
-
-import { allRunnersData } from '../../mock_data';
-
-const mockRunner = allRunnersData.data.runners.nodes[0];
-
-describe('RunnerTypeCell', () => {
- let wrapper;
-
- const findLockIcon = () => wrapper.findByTestId('lock-icon');
- const findRunnerTags = () => wrapper.findComponent(RunnerTags);
- const findRunnerSummaryField = (icon) =>
- wrapper.findAllComponents(RunnerSummaryField).filter((w) => w.props('icon') === icon)
- .wrappers[0];
-
- const createComponent = (runner, options) => {
- wrapper = mountExtended(RunnerStackedSummaryCell, {
- propsData: {
- runner: {
- ...mockRunner,
- ...runner,
- },
- },
- stubs: {
- RunnerSummaryField,
- },
- ...options,
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Displays the runner name as id and short token', () => {
- expect(wrapper.text()).toContain(
- `#${getIdFromGraphQLId(mockRunner.id)} (${mockRunner.shortSha})`,
- );
- });
-
- it('Does not display the locked icon', () => {
- expect(findLockIcon().exists()).toBe(false);
- });
-
- it('Displays the locked icon for locked runners', () => {
- createComponent({
- runnerType: PROJECT_TYPE,
- locked: true,
- });
-
- expect(findLockIcon().exists()).toBe(true);
- });
-
- it('Displays the runner type', () => {
- createComponent({
- runnerType: INSTANCE_TYPE,
- locked: true,
- });
-
- expect(wrapper.text()).toContain(I18N_INSTANCE_TYPE);
- });
-
- it('Displays the runner version', () => {
- expect(wrapper.text()).toContain(mockRunner.version);
- });
-
- it('Displays the runner description', () => {
- expect(wrapper.text()).toContain(mockRunner.description);
- });
-
- it('Displays last contact', () => {
- createComponent({
- contactedAt: '2022-01-02',
- });
-
- expect(findRunnerSummaryField('clock').findComponent(TimeAgo).props('time')).toBe('2022-01-02');
- });
-
- it('Displays empty last contact', () => {
- createComponent({
- contactedAt: null,
- });
-
- expect(findRunnerSummaryField('clock').findComponent(TimeAgo).exists()).toBe(false);
- expect(findRunnerSummaryField('clock').text()).toContain(__('Never'));
- });
-
- it('Displays ip address', () => {
- createComponent({
- ipAddress: '127.0.0.1',
- });
-
- expect(findRunnerSummaryField('disk').text()).toContain('127.0.0.1');
- });
-
- it('Displays no ip address', () => {
- createComponent({
- ipAddress: null,
- });
-
- expect(findRunnerSummaryField('disk')).toBeUndefined();
- });
-
- it('Displays job count', () => {
- expect(findRunnerSummaryField('pipeline').text()).toContain(`${mockRunner.jobCount}`);
- });
-
- it('Formats large job counts', () => {
- createComponent({
- jobCount: 1000,
- });
-
- expect(findRunnerSummaryField('pipeline').text()).toContain('1,000');
- });
-
- it('Formats large job counts with a plus symbol', () => {
- createComponent({
- jobCount: 1001,
- });
-
- expect(findRunnerSummaryField('pipeline').text()).toContain('1,000+');
- });
-
- it('Displays created at', () => {
- expect(findRunnerSummaryField('calendar').findComponent(TimeAgo).props('time')).toBe(
- mockRunner.createdAt,
- );
- });
-
- it('Displays tag list', () => {
- createComponent({
- tagList: ['shell', 'linux'],
- });
-
- expect(findRunnerTags().props('tagList')).toEqual(['shell', 'linux']);
- });
-
- it('Displays a custom slot', () => {
- const slotContent = 'My custom runner name';
-
- createComponent(
- {},
- {
- slots: {
- 'runner-name': slotContent,
- },
- },
- );
-
- expect(wrapper.text()).toContain(slotContent);
- });
-});
diff --git a/spec/frontend/ci/runner/components/cells/runner_summary_cell_spec.js b/spec/frontend/ci/runner/components/cells/runner_summary_cell_spec.js
new file mode 100644
index 00000000000..10280c77303
--- /dev/null
+++ b/spec/frontend/ci/runner/components/cells/runner_summary_cell_spec.js
@@ -0,0 +1,183 @@
+import { __ } from '~/locale';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import RunnerSummaryCell from '~/ci/runner/components/cells/runner_summary_cell.vue';
+import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
+import RunnerTags from '~/ci/runner/components/runner_tags.vue';
+import RunnerJobStatusBadge from '~/ci/runner/components/runner_job_status_badge.vue';
+import RunnerSummaryField from '~/ci/runner/components/cells/runner_summary_field.vue';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+
+import {
+ INSTANCE_TYPE,
+ I18N_INSTANCE_TYPE,
+ PROJECT_TYPE,
+ I18N_NO_DESCRIPTION,
+} from '~/ci/runner/constants';
+
+import { allRunnersData } from '../../mock_data';
+
+const mockRunner = allRunnersData.data.runners.nodes[0];
+
+describe('RunnerTypeCell', () => {
+ let wrapper;
+
+ const findLockIcon = () => wrapper.findByTestId('lock-icon');
+ const findRunnerJobStatusBadge = () => wrapper.findComponent(RunnerJobStatusBadge);
+ const findRunnerTags = () => wrapper.findComponent(RunnerTags);
+ const findRunnerSummaryField = (icon) =>
+ wrapper.findAllComponents(RunnerSummaryField).filter((w) => w.props('icon') === icon)
+ .wrappers[0];
+
+ const createComponent = (runner, options) => {
+ wrapper = mountExtended(RunnerSummaryCell, {
+ propsData: {
+ runner: {
+ ...mockRunner,
+ ...runner,
+ },
+ },
+ stubs: {
+ RunnerSummaryField,
+ },
+ ...options,
+ });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('Displays the runner name as id and short token', () => {
+ expect(wrapper.text()).toContain(
+ `#${getIdFromGraphQLId(mockRunner.id)} (${mockRunner.shortSha})`,
+ );
+ });
+
+ it('Does not display the locked icon', () => {
+ expect(findLockIcon().exists()).toBe(false);
+ });
+
+ it('Displays the locked icon for locked runners', () => {
+ createComponent({
+ runnerType: PROJECT_TYPE,
+ locked: true,
+ });
+
+ expect(findLockIcon().exists()).toBe(true);
+ });
+
+ it('Displays the runner type', () => {
+ createComponent({
+ runnerType: INSTANCE_TYPE,
+ locked: true,
+ });
+
+ expect(wrapper.text()).toContain(I18N_INSTANCE_TYPE);
+ });
+
+ it('Displays the runner version', () => {
+ expect(wrapper.text()).toContain(mockRunner.version);
+ });
+
+ it('Displays the runner description', () => {
+ expect(wrapper.text()).toContain(mockRunner.description);
+ });
+
+ it('Displays the no runner description', () => {
+ createComponent({
+ description: null,
+ });
+
+ expect(wrapper.text()).toContain(I18N_NO_DESCRIPTION);
+ });
+
+ it('Displays job execution status', () => {
+ expect(findRunnerJobStatusBadge().props('jobStatus')).toBe(mockRunner.jobExecutionStatus);
+ });
+
+ it('Displays last contact', () => {
+ createComponent({
+ contactedAt: '2022-01-02',
+ });
+
+ expect(findRunnerSummaryField('clock').findComponent(TimeAgo).props('time')).toBe('2022-01-02');
+ });
+
+ it('Displays empty last contact', () => {
+ createComponent({
+ contactedAt: null,
+ });
+
+ expect(findRunnerSummaryField('clock').findComponent(TimeAgo).exists()).toBe(false);
+ expect(findRunnerSummaryField('clock').text()).toContain(__('Never'));
+ });
+
+ it('Displays ip address', () => {
+ createComponent({
+ ipAddress: '127.0.0.1',
+ });
+
+ expect(findRunnerSummaryField('disk').text()).toContain('127.0.0.1');
+ });
+
+ it('Displays no ip address', () => {
+ createComponent({
+ ipAddress: null,
+ });
+
+ expect(findRunnerSummaryField('disk')).toBeUndefined();
+ });
+
+ it('Displays job count', () => {
+ expect(findRunnerSummaryField('pipeline').text()).toContain(`${mockRunner.jobCount}`);
+ });
+
+ it('Formats large job counts', () => {
+ createComponent({
+ jobCount: 1000,
+ });
+
+ expect(findRunnerSummaryField('pipeline').text()).toContain('1,000');
+ });
+
+ it('Formats large job counts with a plus symbol', () => {
+ createComponent({
+ jobCount: 1001,
+ });
+
+ expect(findRunnerSummaryField('pipeline').text()).toContain('1,000+');
+ });
+
+ it('Displays created at', () => {
+ expect(findRunnerSummaryField('calendar').findComponent(TimeAgo).props('time')).toBe(
+ mockRunner.createdAt,
+ );
+ });
+
+ it('Displays tag list', () => {
+ createComponent({
+ tagList: ['shell', 'linux'],
+ });
+
+ expect(findRunnerTags().props('tagList')).toEqual(['shell', 'linux']);
+ });
+
+ it.each(['runner-name', 'runner-job-status-badge'])('Displays a custom "%s" slot', (slotName) => {
+ const slotContent = 'My custom runner name';
+
+ createComponent(
+ {},
+ {
+ slots: {
+ [slotName]: slotContent,
+ },
+ },
+ );
+
+ expect(wrapper.text()).toContain(slotContent);
+ });
+});
diff --git a/spec/frontend/ci/runner/components/runner_filtered_search_bar_spec.js b/spec/frontend/ci/runner/components/runner_filtered_search_bar_spec.js
index 496c144083e..408750e646f 100644
--- a/spec/frontend/ci/runner/components/runner_filtered_search_bar_spec.js
+++ b/spec/frontend/ci/runner/components/runner_filtered_search_bar_spec.js
@@ -13,6 +13,7 @@ import {
DEFAULT_SORT,
CONTACTED_DESC,
} from '~/ci/runner/constants';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
@@ -34,7 +35,7 @@ describe('RunnerList', () => {
const mockOtherSort = CONTACTED_DESC;
const mockFilters = [
{ type: PARAM_KEY_STATUS, value: { data: STATUS_ONLINE, operator: '=' } },
- { type: 'filtered-search-term', value: { data: '' } },
+ { type: FILTERED_SEARCH_TERM, value: { data: '' } },
];
const expectToHaveLastEmittedInput = (value) => {
diff --git a/spec/frontend/ci/runner/components/runner_job_status_badge_spec.js b/spec/frontend/ci/runner/components/runner_job_status_badge_spec.js
new file mode 100644
index 00000000000..015bebf40e3
--- /dev/null
+++ b/spec/frontend/ci/runner/components/runner_job_status_badge_spec.js
@@ -0,0 +1,51 @@
+import { GlBadge } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import RunnerJobStatusBadge from '~/ci/runner/components/runner_job_status_badge.vue';
+import {
+ I18N_JOB_STATUS_RUNNING,
+ I18N_JOB_STATUS_IDLE,
+ JOB_STATUS_RUNNING,
+ JOB_STATUS_IDLE,
+} from '~/ci/runner/constants';
+
+describe('RunnerTypeBadge', () => {
+ let wrapper;
+
+ const findBadge = () => wrapper.findComponent(GlBadge);
+
+ const createComponent = ({ props, ...options } = {}) => {
+ wrapper = shallowMount(RunnerJobStatusBadge, {
+ propsData: {
+ ...props,
+ },
+ ...options,
+ });
+ };
+
+ it.each`
+ jobStatus | classes | text
+ ${JOB_STATUS_RUNNING} | ${['gl-mr-3', 'gl-bg-transparent!', 'gl-text-blue-600!', 'gl-border', 'gl-border-blue-600!']} | ${I18N_JOB_STATUS_RUNNING}
+ ${JOB_STATUS_IDLE} | ${['gl-mr-3', 'gl-bg-transparent!', 'gl-text-gray-700!', 'gl-border', 'gl-border-gray-500!']} | ${I18N_JOB_STATUS_IDLE}
+ `(
+ 'renders $jobStatus job status with "$text" text and styles',
+ ({ jobStatus, classes, text }) => {
+ createComponent({ props: { jobStatus } });
+
+ expect(findBadge().props()).toMatchObject({ size: 'sm', variant: 'muted' });
+ expect(findBadge().classes().sort()).toEqual(classes.sort());
+ expect(findBadge().text()).toBe(text);
+ },
+ );
+
+ it('does not render an unknown status', () => {
+ createComponent({ props: { jobStatus: 'UNKNOWN_STATUS' } });
+
+ expect(wrapper.html()).toBe('');
+ });
+
+ it('adds arbitrary attributes', () => {
+ createComponent({ props: { jobStatus: JOB_STATUS_RUNNING }, attrs: { href: '/url' } });
+
+ expect(findBadge().attributes('href')).toBe('/url');
+ });
+});
diff --git a/spec/frontend/ci/runner/components/runner_list_spec.js b/spec/frontend/ci/runner/components/runner_list_spec.js
index d53a0ce8f4f..1267d045623 100644
--- a/spec/frontend/ci/runner/components/runner_list_spec.js
+++ b/spec/frontend/ci/runner/components/runner_list_spec.js
@@ -188,6 +188,21 @@ describe('RunnerList', () => {
expect(findCell({ fieldKey: 'summary' }).text()).toContain(`Summary: ${mockRunners[0].id}`);
});
+ it('Render #runner-job-status-badge slot in "summary" cell', () => {
+ createComponent(
+ {
+ scopedSlots: {
+ 'runner-job-status-badge': ({ runner }) => `Job status ${runner.jobExecutionStatus}`,
+ },
+ },
+ mountExtended,
+ );
+
+ expect(findCell({ fieldKey: 'summary' }).text()).toContain(
+ `Job status ${mockRunners[0].jobExecutionStatus}`,
+ );
+ });
+
it('Render #runner-actions-cell slot in "actions" cell', () => {
createComponent(
{
diff --git a/spec/frontend/ci/runner/components/runner_status_badge_spec.js b/spec/frontend/ci/runner/components/runner_status_badge_spec.js
index 7d3064c2aef..45b410df2d4 100644
--- a/spec/frontend/ci/runner/components/runner_status_badge_spec.js
+++ b/spec/frontend/ci/runner/components/runner_status_badge_spec.js
@@ -37,12 +37,12 @@ describe('RunnerTypeBadge', () => {
};
beforeEach(() => {
- jest.useFakeTimers('modern');
+ jest.useFakeTimers({ legacyFakeTimers: false });
jest.setSystemTime(new Date('2021-01-01T00:00:00Z'));
});
afterEach(() => {
- jest.useFakeTimers('legacy');
+ jest.useFakeTimers({ legacyFakeTimers: true });
wrapper.destroy();
});
diff --git a/spec/frontend/ci/runner/components/search_tokens/tag_token_spec.js b/spec/frontend/ci/runner/components/search_tokens/tag_token_spec.js
index d3c7ea50f9d..3dce5a509ca 100644
--- a/spec/frontend/ci/runner/components/search_tokens/tag_token_spec.js
+++ b/spec/frontend/ci/runner/components/search_tokens/tag_token_spec.js
@@ -7,7 +7,7 @@ import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import TagToken, { TAG_SUGGESTIONS_PATH } from '~/ci/runner/components/search_tokens/tag_token.vue';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
+import { OPERATORS_IS } from '~/vue_shared/components/filtered_search_bar/constants';
import { getRecentlyUsedSuggestions } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
jest.mock('~/flash');
@@ -42,7 +42,7 @@ const mockTagTokenConfig = {
type: 'tag',
token: TagToken,
recentSuggestionsStorageKey: mockStorageKey,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
};
describe('TagToken', () => {
diff --git a/spec/frontend/ci/runner/components/stat/runner_stats_spec.js b/spec/frontend/ci/runner/components/stat/runner_stats_spec.js
index daebf3df050..3d45674d106 100644
--- a/spec/frontend/ci/runner/components/stat/runner_stats_spec.js
+++ b/spec/frontend/ci/runner/components/stat/runner_stats_spec.js
@@ -16,6 +16,23 @@ describe('RunnerStats', () => {
const findSingleStats = () => wrapper.findAllComponents(RunnerSingleStat);
+ const RunnerCountStub = {
+ props: ['variables'],
+ render() {
+ // return a count for each status
+ const mockCounts = {
+ undefined: 6, // no status returns "all"
+ [STATUS_ONLINE]: 3,
+ [STATUS_OFFLINE]: 2,
+ [STATUS_STALE]: 1,
+ };
+
+ return this.$scopedSlots.default({
+ count: mockCounts[this.variables.status],
+ });
+ },
+ };
+
const createComponent = ({ props = {}, mountFn = shallowMount, ...options } = {}) => {
wrapper = mountFn(RunnerStats, {
propsData: {
@@ -23,6 +40,9 @@ describe('RunnerStats', () => {
variables: {},
...props,
},
+ stubs: {
+ RunnerCount: RunnerCountStub,
+ },
...options,
});
};
@@ -32,24 +52,8 @@ describe('RunnerStats', () => {
});
it('Displays all the stats', () => {
- const mockCounts = {
- [STATUS_ONLINE]: 3,
- [STATUS_OFFLINE]: 2,
- [STATUS_STALE]: 1,
- };
-
createComponent({
mountFn: mount,
- stubs: {
- RunnerCount: {
- props: ['variables'],
- render() {
- return this.$scopedSlots.default({
- count: mockCounts[this.variables.status],
- });
- },
- },
- },
});
const text = wrapper.text();
@@ -78,4 +82,21 @@ describe('RunnerStats', () => {
expect(stat.props('variables')).toMatchObject(mockVariables);
});
});
+
+ it('Does not display counts when total is 0', () => {
+ createComponent({
+ mountFn: mount,
+ stubs: {
+ RunnerCount: {
+ render() {
+ return this.$scopedSlots.default({
+ count: 0,
+ });
+ },
+ },
+ },
+ });
+
+ expect(wrapper.html()).toBe('');
+ });
});
diff --git a/spec/frontend/ci/runner/group_runners/group_runners_app_spec.js b/spec/frontend/ci/runner/group_runners/group_runners_app_spec.js
index c3493b3c9fd..1e5bb828dbf 100644
--- a/spec/frontend/ci/runner/group_runners/group_runners_app_spec.js
+++ b/spec/frontend/ci/runner/group_runners/group_runners_app_spec.js
@@ -448,13 +448,15 @@ describe('GroupRunnersApp', () => {
it('navigates to the next page', async () => {
await findRunnerPaginationNext().trigger('click');
- expect(mockGroupRunnersHandler).toHaveBeenLastCalledWith({
- groupFullPath: mockGroupFullPath,
- membership: MEMBERSHIP_DESCENDANTS,
- sort: CREATED_DESC,
- first: RUNNER_PAGE_SIZE,
- after: pageInfo.endCursor,
- });
+ expect(mockGroupRunnersHandler).toHaveBeenLastCalledWith(
+ expect.objectContaining({
+ groupFullPath: mockGroupFullPath,
+ membership: MEMBERSHIP_DESCENDANTS,
+ sort: CREATED_DESC,
+ first: RUNNER_PAGE_SIZE,
+ after: pageInfo.endCursor,
+ }),
+ );
});
});
diff --git a/spec/frontend/ci/runner/mock_data.js b/spec/frontend/ci/runner/mock_data.js
index eff5abc21b5..525756ed513 100644
--- a/spec/frontend/ci/runner/mock_data.js
+++ b/spec/frontend/ci/runner/mock_data.js
@@ -18,6 +18,7 @@ import groupRunnersDataPaginated from 'test_fixtures/graphql/ci/runner/list/grou
import groupRunnersCountData from 'test_fixtures/graphql/ci/runner/list/group_runners_count.query.graphql.json';
import { DEFAULT_MEMBERSHIP, RUNNER_PAGE_SIZE } from '~/ci/runner/constants';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
const emptyPageInfo = {
__typename: 'PageInfo',
@@ -73,7 +74,7 @@ export const mockSearchExamples = [
membership: DEFAULT_MEMBERSHIP,
filters: [
{
- type: 'filtered-search-term',
+ type: FILTERED_SEARCH_TERM,
value: { data: 'something' },
},
],
@@ -95,11 +96,11 @@ export const mockSearchExamples = [
membership: DEFAULT_MEMBERSHIP,
filters: [
{
- type: 'filtered-search-term',
+ type: FILTERED_SEARCH_TERM,
value: { data: 'something' },
},
{
- type: 'filtered-search-term',
+ type: FILTERED_SEARCH_TERM,
value: { data: 'else' },
},
],
diff --git a/spec/frontend/ci/runner/runner_search_utils_spec.js b/spec/frontend/ci/runner/runner_search_utils_spec.js
index 1db8fa1829b..f64b89d47fd 100644
--- a/spec/frontend/ci/runner/runner_search_utils_spec.js
+++ b/spec/frontend/ci/runner/runner_search_utils_spec.js
@@ -6,6 +6,7 @@ import {
fromSearchToVariables,
isSearchFiltered,
} from 'ee_else_ce/ci/runner/runner_search_utils';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import { mockSearchExamples } from './mock_data';
describe('search_params.js', () => {
@@ -48,8 +49,8 @@ describe('search_params.js', () => {
it('When search params appear as array, they are concatenated', () => {
expect(fromUrlQueryToSearch('?search[]=my&search[]=text').filters).toEqual([
- { type: 'filtered-search-term', value: { data: 'my' } },
- { type: 'filtered-search-term', value: { data: 'text' } },
+ { type: FILTERED_SEARCH_TERM, value: { data: 'my' } },
+ { type: FILTERED_SEARCH_TERM, value: { data: 'text' } },
]);
});
});
@@ -64,12 +65,13 @@ describe('search_params.js', () => {
it.each([
'http://test.host/?status[]=ACTIVE',
'http://test.host/?runner_type[]=INSTANCE_TYPE',
+ 'http://test.host/?paused[]=true',
'http://test.host/?search=my_text',
- ])('When a filter is removed, it is removed from the URL', (initalUrl) => {
+ ])('When a filter is removed, it is removed from the URL', (initialUrl) => {
const search = { filters: [], sort: 'CREATED_DESC' };
const expectedUrl = `http://test.host/`;
- expect(fromSearchToUrl(search, initalUrl)).toBe(expectedUrl);
+ expect(fromSearchToUrl(search, initialUrl)).toBe(expectedUrl);
});
it('When unrelated search parameter is present, it does not get removed', () => {
@@ -93,7 +95,7 @@ describe('search_params.js', () => {
fromSearchToVariables({
filters: [
{
- type: 'filtered-search-term',
+ type: FILTERED_SEARCH_TERM,
value: { data: '' },
},
],
@@ -106,11 +108,11 @@ describe('search_params.js', () => {
fromSearchToVariables({
filters: [
{
- type: 'filtered-search-term',
+ type: FILTERED_SEARCH_TERM,
value: { data: 'something' },
},
{
- type: 'filtered-search-term',
+ type: FILTERED_SEARCH_TERM,
value: { data: '' },
},
],
diff --git a/spec/frontend/ci_lint/components/ci_lint_spec.js b/spec/frontend/ci_lint/components/ci_lint_spec.js
deleted file mode 100644
index ea69a80274e..00000000000
--- a/spec/frontend/ci_lint/components/ci_lint_spec.js
+++ /dev/null
@@ -1,118 +0,0 @@
-import { GlAlert } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import waitForPromises from 'helpers/wait_for_promises';
-import CiLint from '~/ci_lint/components/ci_lint.vue';
-import CiLintResults from '~/pipeline_editor/components/lint/ci_lint_results.vue';
-import lintCIMutation from '~/pipeline_editor/graphql/mutations/client/lint_ci.mutation.graphql';
-import SourceEditor from '~/vue_shared/components/source_editor.vue';
-import { mockLintDataValid } from '../mock_data';
-
-describe('CI Lint', () => {
- let wrapper;
-
- const endpoint = '/namespace/project/-/ci/lint';
- const content =
- "test_job:\n stage: build\n script: echo 'Building'\n only:\n - web\n - chat\n - pushes\n allow_failure: true ";
- const mockMutate = jest.fn().mockResolvedValue(mockLintDataValid);
-
- const createComponent = () => {
- wrapper = shallowMount(CiLint, {
- data() {
- return {
- content,
- };
- },
- propsData: {
- endpoint,
- pipelineSimulationHelpPagePath: '/help/ci/lint#pipeline-simulation',
- lintHelpPagePath: '/help/ci/lint#anchor',
- },
- mocks: {
- $apollo: {
- mutate: mockMutate,
- },
- },
- });
- };
-
- const findEditor = () => wrapper.findComponent(SourceEditor);
- const findAlert = () => wrapper.findComponent(GlAlert);
- const findCiLintResults = () => wrapper.findComponent(CiLintResults);
- const findValidateBtn = () => wrapper.find('[data-testid="ci-lint-validate"]');
- const findClearBtn = () => wrapper.find('[data-testid="ci-lint-clear"]');
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- mockMutate.mockClear();
- wrapper.destroy();
- });
-
- it('displays the editor', () => {
- expect(findEditor().exists()).toBe(true);
- });
-
- it('validate action calls mutation correctly', () => {
- findValidateBtn().vm.$emit('click');
-
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
- mutation: lintCIMutation,
- variables: { content, dry: false, endpoint },
- });
- });
-
- it('validate action calls mutation with dry run', async () => {
- const dryRunEnabled = true;
-
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- await wrapper.setData({ dryRun: dryRunEnabled });
-
- findValidateBtn().vm.$emit('click');
-
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
- mutation: lintCIMutation,
- variables: { content, dry: dryRunEnabled, endpoint },
- });
- });
-
- it('validation displays results', async () => {
- findValidateBtn().vm.$emit('click');
-
- await nextTick();
-
- expect(findValidateBtn().props('loading')).toBe(true);
-
- await waitForPromises();
-
- expect(findCiLintResults().exists()).toBe(true);
- expect(findValidateBtn().props('loading')).toBe(false);
- });
-
- it('validation displays error', async () => {
- mockMutate.mockRejectedValue('Error!');
-
- findValidateBtn().vm.$emit('click');
-
- await nextTick();
-
- expect(findValidateBtn().props('loading')).toBe(true);
-
- await waitForPromises();
-
- expect(findCiLintResults().exists()).toBe(false);
- expect(findAlert().text()).toBe('Error!');
- expect(findValidateBtn().props('loading')).toBe(false);
- });
-
- it('content is cleared on clear action', async () => {
- expect(findEditor().props('value')).toBe(content);
-
- await findClearBtn().vm.$emit('click');
-
- expect(findEditor().props('value')).toBe('');
- });
-});
diff --git a/spec/frontend/ci_lint/mock_data.js b/spec/frontend/ci_lint/mock_data.js
deleted file mode 100644
index 660b2ad6e8b..00000000000
--- a/spec/frontend/ci_lint/mock_data.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import { mockJobs } from 'jest/pipeline_editor/mock_data';
-
-export const mockLintDataError = {
- data: {
- lintCI: {
- errors: ['Error message'],
- warnings: ['Warning message'],
- valid: false,
- jobs: mockJobs,
- },
- },
-};
-
-export const mockLintDataValid = {
- data: {
- lintCI: {
- errors: [],
- warnings: [],
- valid: true,
- jobs: mockJobs,
- },
- },
-};
diff --git a/spec/frontend/ci_variable_list/components/ci_admin_variables_spec.js b/spec/frontend/ci_variable_list/components/ci_admin_variables_spec.js
index c7375acd8e5..aa83638773d 100644
--- a/spec/frontend/ci_variable_list/components/ci_admin_variables_spec.js
+++ b/spec/frontend/ci_variable_list/components/ci_admin_variables_spec.js
@@ -24,6 +24,7 @@ describe('Ci Project Variable wrapper', () => {
expect(findCiShared().props()).toEqual({
areScopedVariablesAvailable: false,
componentName: 'InstanceVariables',
+ entity: '',
hideEnvironmentScope: true,
mutationData: wrapper.vm.$options.mutationData,
queryData: wrapper.vm.$options.queryData,
diff --git a/spec/frontend/ci_variable_list/components/ci_group_variables_spec.js b/spec/frontend/ci_variable_list/components/ci_group_variables_spec.js
index ef5a86ccb61..ef624d8e4b4 100644
--- a/spec/frontend/ci_variable_list/components/ci_group_variables_spec.js
+++ b/spec/frontend/ci_variable_list/components/ci_group_variables_spec.js
@@ -39,6 +39,7 @@ describe('Ci Group Variable wrapper', () => {
id: convertToGraphQLId(GRAPHQL_GROUP_TYPE, mockProvide.groupId),
areScopedVariablesAvailable: false,
componentName: 'GroupVariables',
+ entity: 'group',
fullPath: mockProvide.groupPath,
hideEnvironmentScope: false,
mutationData: wrapper.vm.$options.mutationData,
diff --git a/spec/frontend/ci_variable_list/components/ci_project_variables_spec.js b/spec/frontend/ci_variable_list/components/ci_project_variables_spec.js
index 97051325f59..53c25e430f2 100644
--- a/spec/frontend/ci_variable_list/components/ci_project_variables_spec.js
+++ b/spec/frontend/ci_variable_list/components/ci_project_variables_spec.js
@@ -35,6 +35,7 @@ describe('Ci Project Variable wrapper', () => {
id: convertToGraphQLId(GRAPHQL_PROJECT_TYPE, mockProvide.projectId),
areScopedVariablesAvailable: true,
componentName: 'ProjectVariables',
+ entity: 'project',
fullPath: mockProvide.projectFullPath,
hideEnvironmentScope: false,
mutationData: wrapper.vm.$options.mutationData,
diff --git a/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js b/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js
index e4771f040d1..d177e755591 100644
--- a/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js
+++ b/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js
@@ -68,6 +68,7 @@ describe('Ci variable modal', () => {
findModal()
.findAllComponents(GlButton)
.wrappers.find((button) => button.props('variant') === 'danger');
+ const findExpandedVariableCheckbox = () => wrapper.findByTestId('ci-variable-expanded-checkbox');
const findProtectedVariableCheckbox = () =>
wrapper.findByTestId('ci-variable-protected-checkbox');
const findMaskedVariableCheckbox = () => wrapper.findByTestId('ci-variable-masked-checkbox');
@@ -75,6 +76,7 @@ describe('Ci variable modal', () => {
const findEnvScopeLink = () => wrapper.findByTestId('environment-scope-link');
const findEnvScopeInput = () =>
wrapper.findByTestId('environment-scope').findComponent(GlFormInput);
+ const findRawVarTip = () => wrapper.findByTestId('raw-variable-tip');
const findVariableTypeDropdown = () => wrapper.find('#ci-variable-type');
const findEnvironmentScopeText = () => wrapper.findByText('Environment scope');
@@ -188,7 +190,7 @@ describe('Ci variable modal', () => {
});
});
- describe('Reference warning when adding a variable', () => {
+ describe('when expanded', () => {
describe('with a $ character', () => {
beforeEach(() => {
const [variable] = mockVariables;
@@ -205,6 +207,10 @@ describe('Ci variable modal', () => {
it(`renders the variable reference warning`, () => {
expect(findReferenceWarning().exists()).toBe(true);
});
+
+ it(`does not render raw variable tip`, () => {
+ expect(findRawVarTip().exists()).toBe(false);
+ });
});
describe('without a $ character', () => {
@@ -219,6 +225,73 @@ describe('Ci variable modal', () => {
it(`does not render the variable reference warning`, () => {
expect(findReferenceWarning().exists()).toBe(false);
});
+
+ it(`does not render raw variable tip`, () => {
+ expect(findRawVarTip().exists()).toBe(false);
+ });
+ });
+
+ describe('setting raw value', () => {
+ const [variable] = mockVariables;
+
+ it('defaults to expanded and raw:false when adding a variable', () => {
+ createComponent({ props: { selectedVariable: variable } });
+ jest.spyOn(wrapper.vm, '$emit');
+
+ findModal().vm.$emit('shown');
+
+ expect(findExpandedVariableCheckbox().attributes('checked')).toBe('true');
+
+ findAddorUpdateButton().vm.$emit('click');
+
+ expect(wrapper.emitted('add-variable')).toEqual([
+ [
+ {
+ ...variable,
+ raw: false,
+ },
+ ],
+ ]);
+ });
+
+ it('sets correct raw value when editing', async () => {
+ createComponent({
+ props: {
+ selectedVariable: variable,
+ mode: EDIT_VARIABLE_ACTION,
+ },
+ });
+ jest.spyOn(wrapper.vm, '$emit');
+
+ findModal().vm.$emit('shown');
+ await findExpandedVariableCheckbox().vm.$emit('change');
+ await findAddorUpdateButton().vm.$emit('click');
+
+ expect(wrapper.emitted('update-variable')).toEqual([
+ [
+ {
+ ...variable,
+ raw: true,
+ },
+ ],
+ ]);
+ });
+ });
+ });
+
+ describe('when not expanded', () => {
+ describe('with a $ character', () => {
+ beforeEach(() => {
+ const selectedVariable = mockVariables[1];
+ createComponent({
+ mountFn: mountExtended,
+ props: { selectedVariable },
+ });
+ });
+
+ it(`renders raw variable tip`, () => {
+ expect(findRawVarTip().exists()).toBe(true);
+ });
});
});
diff --git a/spec/frontend/ci_variable_list/components/ci_variable_settings_spec.js b/spec/frontend/ci_variable_list/components/ci_variable_settings_spec.js
index 8b5a0f7ae9d..5e459ee390f 100644
--- a/spec/frontend/ci_variable_list/components/ci_variable_settings_spec.js
+++ b/spec/frontend/ci_variable_list/components/ci_variable_settings_spec.js
@@ -17,40 +17,45 @@ describe('Ci variable table', () => {
const defaultProps = {
areScopedVariablesAvailable: true,
+ entity: 'project',
environments: mapEnvironmentNames(mockEnvs),
hideEnvironmentScope: false,
isLoading: false,
+ maxVariableLimit: 5,
variables: mockVariablesWithScopes(projectString),
};
const findCiVariableTable = () => wrapper.findComponent(ciVariableTable);
const findCiVariableModal = () => wrapper.findComponent(ciVariableModal);
- const createComponent = () => {
+ const createComponent = ({ props = {} } = {}) => {
wrapper = shallowMount(CiVariableSettings, {
propsData: {
...defaultProps,
+ ...props,
},
});
};
- beforeEach(() => {
- createComponent();
- });
-
afterEach(() => {
wrapper.destroy();
});
describe('props passing', () => {
it('passes props down correctly to the ci table', () => {
+ createComponent();
+
expect(findCiVariableTable().props()).toEqual({
+ entity: 'project',
isLoading: defaultProps.isLoading,
+ maxVariableLimit: defaultProps.maxVariableLimit,
variables: defaultProps.variables,
});
});
it('passes props down correctly to the ci modal', async () => {
+ createComponent();
+
findCiVariableTable().vm.$emit('set-selected-variable');
await nextTick();
@@ -66,6 +71,10 @@ describe('Ci variable table', () => {
});
describe('modal mode', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
it('passes down ADD mode when receiving an empty variable', async () => {
findCiVariableTable().vm.$emit('set-selected-variable');
await nextTick();
@@ -82,6 +91,10 @@ describe('Ci variable table', () => {
});
describe('variable modal', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
it('is hidden by default', () => {
expect(findCiVariableModal().exists()).toBe(false);
});
@@ -112,6 +125,10 @@ describe('Ci variable table', () => {
});
describe('variable events', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
it.each`
eventName
${'add-variable'}
diff --git a/spec/frontend/ci_variable_list/components/ci_variable_shared_spec.js b/spec/frontend/ci_variable_list/components/ci_variable_shared_spec.js
index 0cc0ee7a9c7..65a58a1647f 100644
--- a/spec/frontend/ci_variable_list/components/ci_variable_shared_spec.js
+++ b/spec/frontend/ci_variable_list/components/ci_variable_shared_spec.js
@@ -29,6 +29,8 @@ import {
createGroupProps,
createInstanceProps,
createProjectProps,
+ createGroupProvide,
+ createProjectProvide,
devName,
mockProjectEnvironments,
mockProjectVariables,
@@ -44,6 +46,8 @@ Vue.use(VueApollo);
const mockProvide = {
endpoint: '/variables',
+ isGroup: false,
+ isProject: false,
};
const defaultProps = {
@@ -68,6 +72,7 @@ describe('Ci Variable Shared Component', () => {
customHandlers = null,
isLoading = false,
props = { ...createProjectProps() },
+ provide = {},
} = {}) {
const handlers = customHandlers || [
[getProjectEnvironments, mockEnvironments],
@@ -81,7 +86,10 @@ describe('Ci Variable Shared Component', () => {
...defaultProps,
...props,
},
- provide: mockProvide,
+ provide: {
+ ...mockProvide,
+ ...provide,
+ },
apolloProvider: mockApollo,
stubs: { ciVariableSettings, ciVariableTable },
});
@@ -108,12 +116,18 @@ describe('Ci Variable Shared Component', () => {
});
describe('when queries are resolved', () => {
- describe('successfuly', () => {
+ describe('successfully', () => {
beforeEach(async () => {
mockEnvironments.mockResolvedValue(mockProjectEnvironments);
mockVariables.mockResolvedValue(mockProjectVariables);
- await createComponentWithApollo();
+ await createComponentWithApollo({ provide: createProjectProvide() });
+ });
+
+ it('passes down the expected max variable limit as props', () => {
+ expect(findCiSettings().props('maxVariableLimit')).toBe(
+ mockProjectVariables.data.project.ciVariables.limit,
+ );
});
it('passes down the expected environments as props', () => {
@@ -285,23 +299,29 @@ describe('Ci Variable Shared Component', () => {
});
describe('Props', () => {
+ const mockGroupCiVariables = mockGroupVariables.data.group.ciVariables;
+ const mockProjectCiVariables = mockProjectVariables.data.project.ciVariables;
+
describe('in a specific context as', () => {
it.each`
- name | mockVariablesValue | mockEnvironmentsValue | withEnvironments | expectedEnvironments | propsFn | mutation
- ${'project'} | ${mockProjectVariables} | ${mockProjectEnvironments} | ${true} | ${['prod', 'dev']} | ${createProjectProps} | ${null}
- ${'group'} | ${mockGroupVariables} | ${[]} | ${false} | ${[]} | ${createGroupProps} | ${getGroupVariables}
- ${'instance'} | ${mockAdminVariables} | ${[]} | ${false} | ${[]} | ${createInstanceProps} | ${getAdminVariables}
+ name | mockVariablesValue | mockEnvironmentsValue | withEnvironments | expectedEnvironments | propsFn | provideFn | mutation | maxVariableLimit
+ ${'project'} | ${mockProjectVariables} | ${mockProjectEnvironments} | ${true} | ${['prod', 'dev']} | ${createProjectProps} | ${createProjectProvide} | ${null} | ${mockProjectCiVariables.limit}
+ ${'group'} | ${mockGroupVariables} | ${[]} | ${false} | ${[]} | ${createGroupProps} | ${createGroupProvide} | ${getGroupVariables} | ${mockGroupCiVariables.limit}
+ ${'instance'} | ${mockAdminVariables} | ${[]} | ${false} | ${[]} | ${createInstanceProps} | ${() => {}} | ${getAdminVariables} | ${0}
`(
'passes down all the required props when its a $name component',
async ({
mutation,
+ maxVariableLimit,
mockVariablesValue,
mockEnvironmentsValue,
withEnvironments,
expectedEnvironments,
propsFn,
+ provideFn,
}) => {
const props = propsFn();
+ const provide = provideFn();
mockVariables.mockResolvedValue(mockVariablesValue);
@@ -315,13 +335,15 @@ describe('Ci Variable Shared Component', () => {
customHandlers = [[mutation, mockVariables]];
}
- await createComponentWithApollo({ customHandlers, props });
+ await createComponentWithApollo({ customHandlers, props, provide });
expect(findCiSettings().props()).toEqual({
areScopedVariablesAvailable: wrapper.props().areScopedVariablesAvailable,
hideEnvironmentScope: defaultProps.hideEnvironmentScope,
isLoading: false,
+ maxVariableLimit,
variables: wrapper.props().queryData.ciVariables.lookup(mockVariablesValue.data)?.nodes,
+ entity: props.entity,
environments: expectedEnvironments,
});
},
diff --git a/spec/frontend/ci_variable_list/components/ci_variable_table_spec.js b/spec/frontend/ci_variable_list/components/ci_variable_table_spec.js
index 8a4c35173ec..9891bc397b6 100644
--- a/spec/frontend/ci_variable_list/components/ci_variable_table_spec.js
+++ b/spec/frontend/ci_variable_list/components/ci_variable_table_spec.js
@@ -1,16 +1,22 @@
+import { GlAlert } from '@gitlab/ui';
+import { sprintf } from '~/locale';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import CiVariableTable from '~/ci_variable_list/components/ci_variable_table.vue';
-import { projectString } from '~/ci_variable_list/constants';
+import { EXCEEDS_VARIABLE_LIMIT_TEXT, projectString } from '~/ci_variable_list/constants';
import { mockVariables } from '../mocks';
describe('Ci variable table', () => {
let wrapper;
const defaultProps = {
+ entity: 'project',
isLoading: false,
+ maxVariableLimit: mockVariables(projectString).length + 1,
variables: mockVariables(projectString),
};
+ const mockMaxVariableLimit = defaultProps.variables.length;
+
const createComponent = ({ props = {} } = {}) => {
wrapper = mountExtended(CiVariableTable, {
attachTo: document.body,
@@ -25,8 +31,15 @@ describe('Ci variable table', () => {
const findAddButton = () => wrapper.findByLabelText('Add');
const findEditButton = () => wrapper.findByLabelText('Edit');
const findEmptyVariablesPlaceholder = () => wrapper.findByText('There are no variables yet.');
- const findHiddenValues = () => wrapper.findAll('[data-testid="hiddenValue"]');
- const findRevealedValues = () => wrapper.findAll('[data-testid="revealedValue"]');
+ const findHiddenValues = () => wrapper.findAllByTestId('hiddenValue');
+ const findLimitReachedAlerts = () => wrapper.findAllComponents(GlAlert);
+ const findRevealedValues = () => wrapper.findAllByTestId('revealedValue');
+ const findOptionsValues = (rowIndex) =>
+ wrapper.findAllByTestId('ci-variable-table-row-options').at(rowIndex).text();
+
+ const generateExceedsVariableLimitText = (entity, currentVariableCount, maxVariableLimit) => {
+ return sprintf(EXCEEDS_VARIABLE_LIMIT_TEXT, { entity, currentVariableCount, maxVariableLimit });
+ };
beforeEach(() => {
createComponent();
@@ -66,6 +79,67 @@ describe('Ci variable table', () => {
it('displays the correct amount of variables', async () => {
expect(wrapper.findAll('.js-ci-variable-row')).toHaveLength(defaultProps.variables.length);
});
+
+ it('displays the correct variable options', async () => {
+ expect(findOptionsValues(0)).toBe('Protected, Expanded');
+ expect(findOptionsValues(1)).toBe('Masked');
+ });
+
+ it('enables the Add Variable button', () => {
+ expect(findAddButton().props('disabled')).toBe(false);
+ });
+ });
+
+ describe('When variables have exceeded the max limit', () => {
+ beforeEach(() => {
+ createComponent({ props: { maxVariableLimit: mockVariables(projectString).length } });
+ });
+
+ it('disables the Add Variable button', () => {
+ expect(findAddButton().props('disabled')).toBe(true);
+ });
+ });
+
+ describe('max limit reached alert', () => {
+ describe('when there is no variable limit', () => {
+ beforeEach(() => {
+ createComponent({
+ props: { maxVariableLimit: 0 },
+ });
+ });
+
+ it('hides alert', () => {
+ expect(findLimitReachedAlerts().length).toBe(0);
+ });
+ });
+
+ describe('when variable limit exists', () => {
+ it('hides alert when limit has not been reached', () => {
+ createComponent();
+
+ expect(findLimitReachedAlerts().length).toBe(0);
+ });
+
+ it('shows alert when limit has been reached', () => {
+ const exceedsVariableLimitText = generateExceedsVariableLimitText(
+ defaultProps.entity,
+ defaultProps.variables.length,
+ mockMaxVariableLimit,
+ );
+
+ createComponent({
+ props: { maxVariableLimit: mockMaxVariableLimit },
+ });
+
+ expect(findLimitReachedAlerts().length).toBe(2);
+
+ expect(findLimitReachedAlerts().at(0).props('dismissible')).toBe(false);
+ expect(findLimitReachedAlerts().at(0).text()).toContain(exceedsVariableLimitText);
+
+ expect(findLimitReachedAlerts().at(1).props('dismissible')).toBe(false);
+ expect(findLimitReachedAlerts().at(1).text()).toContain(exceedsVariableLimitText);
+ });
+ });
});
describe('Table click actions', () => {
diff --git a/spec/frontend/ci_variable_list/mocks.js b/spec/frontend/ci_variable_list/mocks.js
index 03b77f80430..065e9fa6667 100644
--- a/spec/frontend/ci_variable_list/mocks.js
+++ b/spec/frontend/ci_variable_list/mocks.js
@@ -34,6 +34,7 @@ export const mockVariables = (kind) => {
key: 'my-var',
masked: false,
protected: true,
+ raw: false,
value: 'variable_value',
variableType: variableTypes.envType,
},
@@ -43,6 +44,7 @@ export const mockVariables = (kind) => {
key: 'secret',
masked: true,
protected: false,
+ raw: true,
value: 'another_value',
variableType: variableTypes.fileType,
},
@@ -63,6 +65,7 @@ const createDefaultVars = ({ withScope = true, kind } = {}) => {
return {
__typename: `Ci${kind}VariableConnection`,
+ limit: 200,
pageInfo: {
startCursor: 'adsjsd12kldpsa',
endCursor: 'adsjsd12kldpsa',
@@ -140,6 +143,7 @@ export const newVariable = {
export const createProjectProps = () => {
return {
componentName: 'ProjectVariable',
+ entity: 'project',
fullPath: '/namespace/project/',
id: 'gid://gitlab/Project/20',
mutationData: {
@@ -163,6 +167,7 @@ export const createProjectProps = () => {
export const createGroupProps = () => {
return {
componentName: 'GroupVariable',
+ entity: 'group',
fullPath: '/my-group',
id: 'gid://gitlab/Group/20',
mutationData: {
@@ -182,6 +187,7 @@ export const createGroupProps = () => {
export const createInstanceProps = () => {
return {
componentName: 'InstanceVariable',
+ entity: '',
mutationData: {
[ADD_MUTATION_ACTION]: addAdminVariable,
[UPDATE_MUTATION_ACTION]: updateAdminVariable,
@@ -195,3 +201,13 @@ export const createInstanceProps = () => {
},
};
};
+
+export const createGroupProvide = () => ({
+ isGroup: true,
+ isProject: false,
+});
+
+export const createProjectProvide = () => ({
+ isGroup: false,
+ isProject: true,
+});
diff --git a/spec/frontend/clusters_list/components/agent_token_spec.js b/spec/frontend/clusters_list/components/agent_token_spec.js
index 8d3130b45a6..a92a03fedb6 100644
--- a/spec/frontend/clusters_list/components/agent_token_spec.js
+++ b/spec/frontend/clusters_list/components/agent_token_spec.js
@@ -1,7 +1,13 @@
-import { GlAlert, GlFormInputGroup } from '@gitlab/ui';
+import { GlAlert, GlFormInputGroup, GlSprintf, GlLink, GlIcon } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { sprintf } from '~/locale';
import AgentToken from '~/clusters_list/components/agent_token.vue';
-import { I18N_AGENT_TOKEN, INSTALL_AGENT_MODAL_ID } from '~/clusters_list/constants';
+import {
+ I18N_AGENT_TOKEN,
+ INSTALL_AGENT_MODAL_ID,
+ NAME_MAX_LENGTH,
+ HELM_VERSION_POLICY_URL,
+} from '~/clusters_list/constants';
import { generateAgentRegistrationCommand } from '~/clusters_list/clusters_util';
import CodeBlock from '~/vue_shared/components/code_block.vue';
import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
@@ -19,15 +25,17 @@ describe('InstallAgentModal', () => {
const findCodeBlock = () => wrapper.findComponent(CodeBlock);
const findCopyButton = () => wrapper.findComponent(ModalCopyButton);
const findInput = () => wrapper.findComponent(GlFormInputGroup);
+ const findHelmVersionPolicyLink = () => wrapper.findComponent(GlLink);
+ const findHelmExternalLinkIcon = () => wrapper.findComponent(GlIcon);
- const createWrapper = () => {
+ const createWrapper = (newAgentName = agentName) => {
const provide = {
kasAddress,
kasVersion,
};
const propsData = {
- agentName,
+ agentName: newAgentName,
agentToken,
modalId,
};
@@ -35,6 +43,9 @@ describe('InstallAgentModal', () => {
wrapper = shallowMountExtended(AgentToken, {
provide,
propsData,
+ stubs: {
+ GlSprintf,
+ },
});
};
@@ -52,6 +63,17 @@ describe('InstallAgentModal', () => {
expect(wrapper.text()).toContain(I18N_AGENT_TOKEN.basicInstallBody);
});
+ it('shows Helm version policy text with an external link', () => {
+ expect(wrapper.text()).toContain(
+ sprintf(I18N_AGENT_TOKEN.helmVersionText, { linkStart: '', linkEnd: ' ' }),
+ );
+ expect(findHelmVersionPolicyLink().attributes()).toMatchObject({
+ href: HELM_VERSION_POLICY_URL,
+ target: '_blank',
+ });
+ expect(findHelmExternalLinkIcon().props()).toMatchObject({ name: 'external-link', size: 12 });
+ });
+
it('shows advanced agent installation instructions', () => {
expect(wrapper.text()).toContain(I18N_AGENT_TOKEN.advancedInstallTitle);
});
@@ -79,9 +101,19 @@ describe('InstallAgentModal', () => {
it('shows code block with agent installation command', () => {
expect(findCodeBlock().props('code')).toContain(`helm upgrade --install ${agentName}`);
+ expect(findCodeBlock().props('code')).toContain(`--namespace gitlab-agent-${agentName}`);
expect(findCodeBlock().props('code')).toContain(`--set config.token=${agentToken}`);
expect(findCodeBlock().props('code')).toContain(`--set config.kasAddress=${kasAddress}`);
expect(findCodeBlock().props('code')).toContain(`--set image.tag=v${kasVersion}`);
});
+
+ it('truncates the namespace name if it exceeds the maximum length', () => {
+ const newAgentName = 'agent-name-that-is-too-long-and-needs-to-be-truncated-to-use';
+ createWrapper(newAgentName);
+
+ expect(findCodeBlock().props('code')).toContain(
+ `--namespace gitlab-agent-${newAgentName.substring(0, NAME_MAX_LENGTH)}`,
+ );
+ });
});
});
diff --git a/spec/frontend/clusters_list/components/agents_spec.js b/spec/frontend/clusters_list/components/agents_spec.js
index bff1e573dbd..2372ab30300 100644
--- a/spec/frontend/clusters_list/components/agents_spec.js
+++ b/spec/frontend/clusters_list/components/agents_spec.js
@@ -1,7 +1,7 @@
import { GlAlert, GlKeysetPagination, GlLoadingIcon, GlBanner } from '@gitlab/ui';
-import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
-import { nextTick } from 'vue';
+import Vue, { nextTick } from 'vue';
import AgentEmptyState from '~/clusters_list/components/agent_empty_state.vue';
import AgentTable from '~/clusters_list/components/agent_table.vue';
import Agents from '~/clusters_list/components/agents.vue';
@@ -12,10 +12,10 @@ import {
} from '~/clusters_list/constants';
import getAgentsQuery from '~/clusters_list/graphql/queries/get_agents.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
-const localVue = createLocalVue();
-localVue.use(VueApollo);
+Vue.use(VueApollo);
describe('Agents', () => {
let wrapper;
@@ -34,9 +34,10 @@ describe('Agents', () => {
pageInfo = null,
trees = [],
count = 0,
+ queryResponse = null,
}) => {
const provide = provideData;
- const apolloQueryResponse = {
+ const queryResponseData = {
data: {
project: {
id: '1',
@@ -51,13 +52,12 @@ describe('Agents', () => {
},
},
};
+ const agentQueryResponse =
+ queryResponse || jest.fn().mockResolvedValue(queryResponseData, provide);
- const apolloProvider = createMockApollo([
- [getAgentsQuery, jest.fn().mockResolvedValue(apolloQueryResponse, provide)],
- ]);
+ const apolloProvider = createMockApollo([[getAgentsQuery, agentQueryResponse]]);
wrapper = shallowMount(Agents, {
- localVue,
apolloProvider,
propsData: {
...defaultProps,
@@ -313,24 +313,11 @@ describe('Agents', () => {
});
describe('when agents query is loading', () => {
- const mocks = {
- $apollo: {
- queries: {
- agents: {
- loading: true,
- },
- },
- },
- };
-
- beforeEach(async () => {
- wrapper = shallowMount(Agents, {
- mocks,
- propsData: defaultProps,
- provide: provideData,
+ beforeEach(() => {
+ createWrapper({
+ queryResponse: jest.fn().mockReturnValue(new Promise(() => {})),
});
-
- await nextTick();
+ return waitForPromises();
});
it('displays a loading icon', () => {
diff --git a/spec/frontend/clusters_list/components/available_agents_dropwdown_spec.js b/spec/frontend/clusters_list/components/available_agents_dropwdown_spec.js
index 197735d3c77..02b455d0b61 100644
--- a/spec/frontend/clusters_list/components/available_agents_dropwdown_spec.js
+++ b/spec/frontend/clusters_list/components/available_agents_dropwdown_spec.js
@@ -1,34 +1,31 @@
-import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui';
+import { GlCollapsibleListbox, GlButton } from '@gitlab/ui';
+import { nextTick } from 'vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { ENTER_KEY } from '~/lib/utils/keys';
import AvailableAgentsDropdown from '~/clusters_list/components/available_agents_dropdown.vue';
import { I18N_AVAILABLE_AGENTS_DROPDOWN } from '~/clusters_list/constants';
describe('AvailableAgentsDropdown', () => {
let wrapper;
+ const configuredAgent = 'configured-agent';
+ const searchAgentName = 'search-agent';
+ const newAgentName = 'new-agent';
+
const i18n = I18N_AVAILABLE_AGENTS_DROPDOWN;
- const findDropdown = () => wrapper.findComponent(GlDropdown);
- const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
- const findFirstAgentItem = () => findDropdownItems().at(0);
- const findSearchInput = () => wrapper.findComponent(GlSearchBoxByType);
- const findCreateButton = () => wrapper.findByTestId('create-config-button');
+ const findDropdown = () => wrapper.findComponent(GlCollapsibleListbox);
+ const findCreateButton = () => wrapper.findComponent(GlButton);
const createWrapper = ({ propsData }) => {
wrapper = shallowMountExtended(AvailableAgentsDropdown, {
propsData,
- stubs: { GlDropdown },
+ stubs: { GlCollapsibleListbox },
});
- wrapper.vm.$refs.dropdown.hide = jest.fn();
+ wrapper.vm.$refs.dropdown.closeAndFocus = jest.fn();
};
- afterEach(() => {
- wrapper.destroy();
- });
-
describe('there are agents available', () => {
const propsData = {
- availableAgents: ['configured-agent', 'search-agent', 'test-agent'],
+ availableAgents: [configuredAgent, searchAgentName, 'test-agent'],
isRegistering: false,
};
@@ -37,91 +34,93 @@ describe('AvailableAgentsDropdown', () => {
});
it('prompts to select an agent', () => {
- expect(findDropdown().props('text')).toBe(i18n.selectAgent);
+ expect(findDropdown().props('toggleText')).toBe(i18n.selectAgent);
});
describe('search agent', () => {
it('renders search button', () => {
- expect(findSearchInput().exists()).toBe(true);
+ expect(findDropdown().props('searchable')).toBe(true);
});
it('renders all agents when search term is empty', () => {
- expect(findDropdownItems()).toHaveLength(3);
+ expect(findDropdown().props('items')).toHaveLength(3);
});
it('renders only the agent searched for when the search item exists', async () => {
- await findSearchInput().vm.$emit('input', 'search-agent');
-
- expect(findDropdownItems()).toHaveLength(1);
- expect(findFirstAgentItem().text()).toBe('search-agent');
- });
+ findDropdown().vm.$emit('search', searchAgentName);
+ await nextTick();
- it('renders create button when search started', async () => {
- await findSearchInput().vm.$emit('input', 'new-agent');
-
- expect(findCreateButton().exists()).toBe(true);
+ expect(findDropdown().props('items')).toMatchObject([
+ { text: searchAgentName, value: searchAgentName },
+ ]);
});
- it("doesn't render create button when search item is found", async () => {
- await findSearchInput().vm.$emit('input', 'search-agent');
-
- expect(findCreateButton().exists()).toBe(false);
+ describe('create button', () => {
+ it.each`
+ condition | search | createButtonRendered
+ ${'is rendered'} | ${newAgentName} | ${true}
+ ${'is not rendered'} | ${''} | ${false}
+ ${'is not rendered'} | ${searchAgentName} | ${false}
+ `('$condition when search is "$search"', async ({ search, createButtonRendered }) => {
+ findDropdown().vm.$emit('search', search);
+ await nextTick();
+
+ expect(findCreateButton().exists()).toBe(createButtonRendered);
+ });
});
});
describe('select existing agent configuration', () => {
beforeEach(() => {
- findFirstAgentItem().vm.$emit('click');
+ findDropdown().vm.$emit('select', configuredAgent);
});
- it('emits agentSelected with the name of the clicked agent', () => {
- expect(wrapper.emitted('agentSelected')).toEqual([['configured-agent']]);
+ it('emits `agentSelected` with the name of the clicked agent', () => {
+ expect(wrapper.emitted('agentSelected')).toEqual([[configuredAgent]]);
});
it('marks the clicked item as selected', () => {
- expect(findDropdown().props('text')).toBe('configured-agent');
- expect(findFirstAgentItem().props('isChecked')).toBe(true);
+ expect(findDropdown().props('toggleText')).toBe(configuredAgent);
});
});
describe('create new agent configuration', () => {
beforeEach(async () => {
- await findSearchInput().vm.$emit('input', 'new-agent');
+ findDropdown().vm.$emit('search', newAgentName);
+ await nextTick();
findCreateButton().vm.$emit('click');
});
it('emits agentSelected with the name of the clicked agent', () => {
- expect(wrapper.emitted('agentSelected')).toEqual([['new-agent']]);
+ expect(wrapper.emitted('agentSelected')).toEqual([[newAgentName]]);
});
it('marks the clicked item as selected', () => {
- expect(findDropdown().props('text')).toBe('new-agent');
+ expect(findDropdown().props('toggleText')).toBe(newAgentName);
});
});
describe('click enter to register new agent without configuration', () => {
beforeEach(async () => {
- await findSearchInput().vm.$emit('input', 'new-agent');
- await findSearchInput().vm.$emit('keydown', new KeyboardEvent({ key: ENTER_KEY }));
+ const dropdown = findDropdown();
+ dropdown.vm.$emit('search', newAgentName);
+ await nextTick();
+ await dropdown.trigger('keydown.enter');
});
it('emits agentSelected with the name of the clicked agent', () => {
- expect(wrapper.emitted('agentSelected')).toEqual([['new-agent']]);
+ expect(wrapper.emitted('agentSelected')).toEqual([[newAgentName]]);
});
it('marks the clicked item as selected', () => {
- expect(findDropdown().props('text')).toBe('new-agent');
- });
-
- it('closes the dropdown', () => {
- expect(wrapper.vm.$refs.dropdown.hide).toHaveBeenCalledTimes(1);
+ expect(findDropdown().props('toggleText')).toBe(newAgentName);
});
});
});
describe('registration in progress', () => {
const propsData = {
- availableAgents: ['configured-agent'],
+ availableAgents: [configuredAgent],
isRegistering: true,
};
@@ -130,7 +129,7 @@ describe('AvailableAgentsDropdown', () => {
});
it('updates the text in the dropdown', () => {
- expect(findDropdown().props('text')).toBe(i18n.registeringAgent);
+ expect(findDropdown().props('toggleText')).toBe(i18n.registeringAgent);
});
it('displays a loading icon', () => {
diff --git a/spec/frontend/clusters_list/components/clusters_spec.js b/spec/frontend/clusters_list/components/clusters_spec.js
index a3f42c1f161..e8e705a6384 100644
--- a/spec/frontend/clusters_list/components/clusters_spec.js
+++ b/spec/frontend/clusters_list/components/clusters_spec.js
@@ -61,6 +61,10 @@ describe('Clusters', () => {
let captureException;
beforeEach(() => {
+ jest.spyOn(Sentry, 'withScope').mockImplementation((fn) => {
+ const mockScope = { setTag: () => {} };
+ fn(mockScope);
+ });
captureException = jest.spyOn(Sentry, 'captureException');
mock = new MockAdapter(axios);
diff --git a/spec/frontend/clusters_list/store/actions_spec.js b/spec/frontend/clusters_list/store/actions_spec.js
index 09b1f80ff9b..1deebf8b75a 100644
--- a/spec/frontend/clusters_list/store/actions_spec.js
+++ b/spec/frontend/clusters_list/store/actions_spec.js
@@ -17,6 +17,10 @@ describe('Clusters store actions', () => {
describe('reportSentryError', () => {
beforeEach(() => {
+ jest.spyOn(Sentry, 'withScope').mockImplementation((fn) => {
+ const mockScope = { setTag: () => {} };
+ fn(mockScope);
+ });
captureException = jest.spyOn(Sentry, 'captureException');
});
diff --git a/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap b/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap
index 6ad8a9de8d3..331a0a474a3 100644
--- a/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap
+++ b/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap
@@ -14,15 +14,15 @@ exports[`content_editor/components/toolbar_link_button renders dropdown componen
</div>
</form>
</li>
- <li role=\\"presentation\\" class=\\"gl-new-dropdown-divider\\">
+ <li role=\\"presentation\\" class=\\"gl-dropdown-divider\\">
<hr role=\\"separator\\" aria-orientation=\\"horizontal\\" class=\\"dropdown-divider\\">
</li>
- <li role=\\"presentation\\" class=\\"gl-new-dropdown-item\\"><button role=\\"menuitem\\" type=\\"button\\" class=\\"dropdown-item\\">
+ <li role=\\"presentation\\" class=\\"gl-dropdown-item\\"><button role=\\"menuitem\\" type=\\"button\\" class=\\"dropdown-item\\">
<!---->
<!---->
<!---->
- <div class=\\"gl-new-dropdown-item-text-wrapper\\">
- <p class=\\"gl-new-dropdown-item-text-primary\\">
+ <div class=\\"gl-dropdown-item-text-wrapper\\">
+ <p class=\\"gl-dropdown-item-text-primary\\">
Upload file
</p>
<!---->
diff --git a/spec/frontend/content_editor/components/content_editor_spec.js b/spec/frontend/content_editor/components/content_editor_spec.js
index c1c2a125515..1a3cd36a8bb 100644
--- a/spec/frontend/content_editor/components/content_editor_spec.js
+++ b/spec/frontend/content_editor/components/content_editor_spec.js
@@ -10,7 +10,7 @@ import FormattingBubbleMenu from '~/content_editor/components/bubble_menus/forma
import CodeBlockBubbleMenu from '~/content_editor/components/bubble_menus/code_block_bubble_menu.vue';
import LinkBubbleMenu from '~/content_editor/components/bubble_menus/link_bubble_menu.vue';
import MediaBubbleMenu from '~/content_editor/components/bubble_menus/media_bubble_menu.vue';
-import TopToolbar from '~/content_editor/components/top_toolbar.vue';
+import FormattingToolbar from '~/content_editor/components/formatting_toolbar.vue';
import LoadingIndicator from '~/content_editor/components/loading_indicator.vue';
import waitForPromises from 'helpers/wait_for_promises';
import { KEYDOWN_EVENT } from '~/content_editor/constants';
@@ -27,13 +27,14 @@ describe('ContentEditor', () => {
const findEditorStateObserver = () => wrapper.findComponent(EditorStateObserver);
const findLoadingIndicator = () => wrapper.findComponent(LoadingIndicator);
const findContentEditorAlert = () => wrapper.findComponent(ContentEditorAlert);
- const createWrapper = ({ markdown, autofocus } = {}) => {
+ const createWrapper = ({ markdown, autofocus, useBottomToolbar } = {}) => {
wrapper = shallowMountExtended(ContentEditor, {
propsData: {
renderMarkdown,
uploadsPath,
markdown,
autofocus,
+ useBottomToolbar,
},
stubs: {
EditorStateObserver,
@@ -89,7 +90,19 @@ describe('ContentEditor', () => {
it('renders top toolbar component', () => {
createWrapper();
- expect(wrapper.findComponent(TopToolbar).exists()).toBe(true);
+ expect(wrapper.findComponent(FormattingToolbar).exists()).toBe(true);
+ expect(wrapper.findComponent(FormattingToolbar).classes('gl-border-t')).toBe(false);
+ expect(wrapper.findComponent(FormattingToolbar).classes('gl-border-b')).toBe(true);
+ });
+
+ it('renders bottom toolbar component', () => {
+ createWrapper({
+ useBottomToolbar: true,
+ });
+
+ expect(wrapper.findComponent(FormattingToolbar).exists()).toBe(true);
+ expect(wrapper.findComponent(FormattingToolbar).classes('gl-border-t')).toBe(true);
+ expect(wrapper.findComponent(FormattingToolbar).classes('gl-border-b')).toBe(false);
});
describe('when setting initial content', () => {
diff --git a/spec/frontend/content_editor/components/formatting_toolbar_spec.js b/spec/frontend/content_editor/components/formatting_toolbar_spec.js
new file mode 100644
index 00000000000..c4bf21ba813
--- /dev/null
+++ b/spec/frontend/content_editor/components/formatting_toolbar_spec.js
@@ -0,0 +1,65 @@
+import { mockTracking } from 'helpers/tracking_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import FormattingToolbar from '~/content_editor/components/formatting_toolbar.vue';
+import {
+ TOOLBAR_CONTROL_TRACKING_ACTION,
+ CONTENT_EDITOR_TRACKING_LABEL,
+} from '~/content_editor/constants';
+
+describe('content_editor/components/top_toolbar', () => {
+ let wrapper;
+ let trackingSpy;
+
+ const buildWrapper = () => {
+ wrapper = shallowMountExtended(FormattingToolbar);
+ };
+
+ beforeEach(() => {
+ trackingSpy = mockTracking(undefined, null, jest.spyOn);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe.each`
+ testId | controlProps
+ ${'text-styles'} | ${{}}
+ ${'bold'} | ${{ contentType: 'bold', iconName: 'bold', label: 'Bold text', editorCommand: 'toggleBold' }}
+ ${'italic'} | ${{ contentType: 'italic', iconName: 'italic', label: 'Italic text', editorCommand: 'toggleItalic' }}
+ ${'blockquote'} | ${{ contentType: 'blockquote', iconName: 'quote', label: 'Insert a quote', editorCommand: 'toggleBlockquote' }}
+ ${'code'} | ${{ contentType: 'code', iconName: 'code', label: 'Code', editorCommand: 'toggleCode' }}
+ ${'link'} | ${{}}
+ ${'bullet-list'} | ${{ contentType: 'bulletList', iconName: 'list-bulleted', label: 'Add a bullet list', editorCommand: 'toggleBulletList' }}
+ ${'ordered-list'} | ${{ contentType: 'orderedList', iconName: 'list-numbered', label: 'Add a numbered list', editorCommand: 'toggleOrderedList' }}
+ ${'task-list'} | ${{ contentType: 'taskList', iconName: 'list-task', label: 'Add a checklist', editorCommand: 'toggleTaskList' }}
+ ${'image'} | ${{}}
+ ${'table'} | ${{}}
+ ${'more'} | ${{}}
+ `('given a $testId toolbar control', ({ testId, controlProps }) => {
+ beforeEach(() => {
+ buildWrapper();
+ });
+
+ it('renders the toolbar control with the provided properties', () => {
+ expect(wrapper.findByTestId(testId).exists()).toBe(true);
+
+ Object.keys(controlProps).forEach((propName) => {
+ expect(wrapper.findByTestId(testId).props(propName)).toBe(controlProps[propName]);
+ });
+ });
+
+ it('tracks the execution of toolbar controls', () => {
+ const eventData = { contentType: 'blockquote', value: 1 };
+ const { contentType, value } = eventData;
+
+ wrapper.findByTestId(testId).vm.$emit('execute', eventData);
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, TOOLBAR_CONTROL_TRACKING_ACTION, {
+ label: CONTENT_EDITOR_TRACKING_LABEL,
+ property: contentType,
+ value,
+ });
+ });
+ });
+});
diff --git a/spec/frontend/content_editor/components/top_toolbar_spec.js b/spec/frontend/content_editor/components/top_toolbar_spec.js
deleted file mode 100644
index 8f194ff32e2..00000000000
--- a/spec/frontend/content_editor/components/top_toolbar_spec.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import { mockTracking } from 'helpers/tracking_helper';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import TopToolbar from '~/content_editor/components/top_toolbar.vue';
-import {
- TOOLBAR_CONTROL_TRACKING_ACTION,
- CONTENT_EDITOR_TRACKING_LABEL,
-} from '~/content_editor/constants';
-
-describe('content_editor/components/top_toolbar', () => {
- let wrapper;
- let trackingSpy;
-
- const buildWrapper = () => {
- wrapper = shallowMountExtended(TopToolbar);
- };
-
- beforeEach(() => {
- trackingSpy = mockTracking(undefined, null, jest.spyOn);
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe.each`
- testId | controlProps
- ${'text-styles'} | ${{}}
- ${'bold'} | ${{ contentType: 'bold', iconName: 'bold', label: 'Bold text', editorCommand: 'toggleBold' }}
- ${'italic'} | ${{ contentType: 'italic', iconName: 'italic', label: 'Italic text', editorCommand: 'toggleItalic' }}
- ${'blockquote'} | ${{ contentType: 'blockquote', iconName: 'quote', label: 'Insert a quote', editorCommand: 'toggleBlockquote' }}
- ${'code'} | ${{ contentType: 'code', iconName: 'code', label: 'Code', editorCommand: 'toggleCode' }}
- ${'link'} | ${{}}
- ${'bullet-list'} | ${{ contentType: 'bulletList', iconName: 'list-bulleted', label: 'Add a bullet list', editorCommand: 'toggleBulletList' }}
- ${'ordered-list'} | ${{ contentType: 'orderedList', iconName: 'list-numbered', label: 'Add a numbered list', editorCommand: 'toggleOrderedList' }}
- ${'task-list'} | ${{ contentType: 'taskList', iconName: 'list-task', label: 'Add a checklist', editorCommand: 'toggleTaskList' }}
- ${'image'} | ${{}}
- ${'table'} | ${{}}
- ${'more'} | ${{}}
- `('given a $testId toolbar control', ({ testId, controlProps }) => {
- beforeEach(() => {
- buildWrapper();
- });
-
- it('renders the toolbar control with the provided properties', () => {
- expect(wrapper.findByTestId(testId).exists()).toBe(true);
-
- Object.keys(controlProps).forEach((propName) => {
- expect(wrapper.findByTestId(testId).props(propName)).toBe(controlProps[propName]);
- });
- });
-
- it('tracks the execution of toolbar controls', () => {
- const eventData = { contentType: 'blockquote', value: 1 };
- const { contentType, value } = eventData;
-
- wrapper.findByTestId(testId).vm.$emit('execute', eventData);
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, TOOLBAR_CONTROL_TRACKING_ACTION, {
- label: CONTENT_EDITOR_TRACKING_LABEL,
- property: contentType,
- value,
- });
- });
- });
-});
diff --git a/spec/frontend/content_editor/extensions/comment_spec.js b/spec/frontend/content_editor/extensions/comment_spec.js
new file mode 100644
index 00000000000..7d8ff28e4d7
--- /dev/null
+++ b/spec/frontend/content_editor/extensions/comment_spec.js
@@ -0,0 +1,30 @@
+import Comment from '~/content_editor/extensions/comment';
+import { createTestEditor, createDocBuilder, triggerNodeInputRule } from '../test_utils';
+
+describe('content_editor/extensions/comment', () => {
+ let tiptapEditor;
+ let doc;
+ let comment;
+
+ beforeEach(() => {
+ tiptapEditor = createTestEditor({ extensions: [Comment] });
+ ({
+ builders: { doc, comment },
+ } = createDocBuilder({
+ tiptapEditor,
+ names: {
+ comment: { nodeType: Comment.name },
+ },
+ }));
+ });
+
+ describe('when typing the comment input rule', () => {
+ it('inserts a comment node', () => {
+ const expectedDoc = doc(comment());
+
+ triggerNodeInputRule({ tiptapEditor, inputRuleText: '<!-- ' });
+
+ expect(tiptapEditor.getJSON()).toEqual(expectedDoc.toJSON());
+ });
+ });
+});
diff --git a/spec/frontend/content_editor/services/gl_api_markdown_deserializer_spec.js b/spec/frontend/content_editor/services/gl_api_markdown_deserializer_spec.js
index 5458a42532f..90d83820c70 100644
--- a/spec/frontend/content_editor/services/gl_api_markdown_deserializer_spec.js
+++ b/spec/frontend/content_editor/services/gl_api_markdown_deserializer_spec.js
@@ -1,5 +1,6 @@
import createMarkdownDeserializer from '~/content_editor/services/gl_api_markdown_deserializer';
import Bold from '~/content_editor/extensions/bold';
+import Comment from '~/content_editor/extensions/comment';
import { createTestEditor, createDocBuilder } from '../test_utils';
describe('content_editor/services/gl_api_markdown_deserializer', () => {
@@ -7,19 +8,21 @@ describe('content_editor/services/gl_api_markdown_deserializer', () => {
let doc;
let p;
let bold;
+ let comment;
let tiptapEditor;
beforeEach(() => {
tiptapEditor = createTestEditor({
- extensions: [Bold],
+ extensions: [Bold, Comment],
});
({
- builders: { doc, p, bold },
+ builders: { doc, p, bold, comment },
} = createDocBuilder({
tiptapEditor,
names: {
bold: { markType: Bold.name },
+ comment: { nodeType: Comment.name },
},
}));
renderMarkdown = jest.fn();
@@ -33,7 +36,7 @@ describe('content_editor/services/gl_api_markdown_deserializer', () => {
const deserializer = createMarkdownDeserializer({ render: renderMarkdown });
renderMarkdown.mockResolvedValueOnce(
- `<p><strong>${text}</strong></p><pre lang="javascript"></pre>`,
+ `<p><strong>${text}</strong></p><pre lang="javascript"></pre><!-- some comment -->`,
);
result = await deserializer.deserialize({
@@ -41,8 +44,9 @@ describe('content_editor/services/gl_api_markdown_deserializer', () => {
schema: tiptapEditor.schema,
});
});
+
it('transforms HTML returned by render function to a ProseMirror document', async () => {
- const document = doc(p(bold(text)));
+ const document = doc(p(bold(text)), comment(' some comment '));
expect(result.document.toJSON()).toEqual(document.toJSON());
});
diff --git a/spec/frontend/content_editor/services/markdown_serializer_spec.js b/spec/frontend/content_editor/services/markdown_serializer_spec.js
index 1bf23415052..2cd8b8a0d6f 100644
--- a/spec/frontend/content_editor/services/markdown_serializer_spec.js
+++ b/spec/frontend/content_editor/services/markdown_serializer_spec.js
@@ -3,6 +3,7 @@ import Bold from '~/content_editor/extensions/bold';
import BulletList from '~/content_editor/extensions/bullet_list';
import Code from '~/content_editor/extensions/code';
import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight';
+import Comment from '~/content_editor/extensions/comment';
import DescriptionItem from '~/content_editor/extensions/description_item';
import DescriptionList from '~/content_editor/extensions/description_list';
import Details from '~/content_editor/extensions/details';
@@ -50,6 +51,7 @@ const {
bulletList,
code,
codeBlock,
+ comment,
details,
detailsContent,
div,
@@ -89,6 +91,7 @@ const {
bulletList: { nodeType: BulletList.name },
code: { markType: Code.name },
codeBlock: { nodeType: CodeBlockHighlight.name },
+ comment: { nodeType: Comment.name },
details: { nodeType: Details.name },
detailsContent: { nodeType: DetailsContent.name },
descriptionItem: { nodeType: DescriptionItem.name },
@@ -169,6 +172,17 @@ describe('markdownSerializer', () => {
);
});
+ it('correctly serializes a comment node', () => {
+ expect(serialize(paragraph('hi'), comment(' this is a\ncomment '))).toBe(
+ `
+hi
+
+<!-- this is a
+comment -->
+ `.trim(),
+ );
+ });
+
it('correctly serializes a line break', () => {
expect(serialize(paragraph('hello', hardBreak(), 'world'))).toBe('hello\\\nworld');
});
@@ -304,7 +318,7 @@ var y = 10;
expect(
serialize(
codeBlock(
- { language: 'json' },
+ { language: 'json', langParams: '' },
'this is not really json but just trying out whether this case works or not',
),
),
@@ -317,6 +331,23 @@ this is not really json but just trying out whether this case works or not
);
});
+ it('correctly serializes a code block with language parameters', () => {
+ expect(
+ serialize(
+ codeBlock(
+ { language: 'json', langParams: 'table' },
+ 'this is not really json:table but just trying out whether this case works or not',
+ ),
+ ),
+ ).toBe(
+ `
+\`\`\`json:table
+this is not really json:table but just trying out whether this case works or not
+\`\`\`
+ `.trim(),
+ );
+ });
+
it('correctly serializes emoji', () => {
expect(serialize(paragraph(emoji({ name: 'dog' })))).toBe(':dog:');
});
@@ -366,6 +397,26 @@ this is not really json but just trying out whether this case works or not
);
});
+ it.each`
+ width | height | outputAttributes
+ ${300} | ${undefined} | ${'width=300'}
+ ${undefined} | ${300} | ${'height=300'}
+ ${300} | ${300} | ${'width=300 height=300'}
+ ${'300%'} | ${'300px'} | ${'width="300%" height="300px"'}
+ `(
+ 'correctly serializes an image with width and height attributes',
+ ({ width, height, outputAttributes }) => {
+ const imageAttrs = { src: 'img.jpg', alt: 'foo bar' };
+
+ if (width) imageAttrs.width = width;
+ if (height) imageAttrs.height = height;
+
+ expect(serialize(paragraph(image(imageAttrs)))).toBe(
+ `![foo bar](img.jpg){${outputAttributes}}`,
+ );
+ },
+ );
+
it('does not serialize an image when src and canonicalSrc are empty', () => {
expect(serialize(paragraph(image({})))).toBe('');
});
diff --git a/spec/frontend/content_editor/test_utils.js b/spec/frontend/content_editor/test_utils.js
index 0768fa6e8df..0fa0e65cd26 100644
--- a/spec/frontend/content_editor/test_utils.js
+++ b/spec/frontend/content_editor/test_utils.js
@@ -11,10 +11,12 @@ import Bold from '~/content_editor/extensions/bold';
import BulletList from '~/content_editor/extensions/bullet_list';
import Code from '~/content_editor/extensions/code';
import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight';
+import Comment from '~/content_editor/extensions/comment';
import DescriptionItem from '~/content_editor/extensions/description_item';
import DescriptionList from '~/content_editor/extensions/description_list';
import Details from '~/content_editor/extensions/details';
import DetailsContent from '~/content_editor/extensions/details_content';
+import Diagram from '~/content_editor/extensions/diagram';
import Emoji from '~/content_editor/extensions/emoji';
import FootnoteDefinition from '~/content_editor/extensions/footnote_definition';
import FootnoteReference from '~/content_editor/extensions/footnote_reference';
@@ -211,10 +213,12 @@ export const createTiptapEditor = (extensions = []) =>
BulletList,
Code,
CodeBlockHighlight,
+ Comment,
DescriptionItem,
DescriptionList,
Details,
DetailsContent,
+ Diagram,
Emoji,
FootnoteDefinition,
FootnoteReference,
diff --git a/spec/frontend/crm/contact_form_wrapper_spec.js b/spec/frontend/crm/contact_form_wrapper_spec.js
index e49b553e4b5..50b432943fb 100644
--- a/spec/frontend/crm/contact_form_wrapper_spec.js
+++ b/spec/frontend/crm/contact_form_wrapper_spec.js
@@ -4,7 +4,7 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import createMockApollo from 'helpers/mock_apollo_helper';
import ContactFormWrapper from '~/crm/contacts/components/contact_form_wrapper.vue';
-import ContactForm from '~/crm/components/form.vue';
+import CrmForm from '~/crm/components/crm_form.vue';
import getGroupContactsQuery from '~/crm/contacts/components/graphql/get_group_contacts.query.graphql';
import createContactMutation from '~/crm/contacts/components/graphql/create_contact.mutation.graphql';
import updateContactMutation from '~/crm/contacts/components/graphql/update_contact.mutation.graphql';
@@ -16,7 +16,7 @@ describe('Customer relations contact form wrapper', () => {
let wrapper;
let fakeApollo;
- const findContactForm = () => wrapper.findComponent(ContactForm);
+ const findCrmForm = () => wrapper.findComponent(CrmForm);
const $route = {
params: {
@@ -65,21 +65,21 @@ describe('Customer relations contact form wrapper', () => {
});
it('renders correct getQuery prop', () => {
- expect(findContactForm().props('getQueryNodePath')).toBe('group.contacts');
+ expect(findCrmForm().props('getQueryNodePath')).toBe('group.contacts');
});
it('renders correct mutation prop', () => {
- expect(findContactForm().props('mutation')).toBe(mutation);
+ expect(findCrmForm().props('mutation')).toBe(mutation);
});
it('renders correct additionalCreateParams prop', () => {
- expect(findContactForm().props('additionalCreateParams')).toMatchObject({
+ expect(findCrmForm().props('additionalCreateParams')).toMatchObject({
groupId: 'gid://gitlab/Group/26',
});
});
it('renders correct existingId prop', () => {
- expect(findContactForm().props('existingId')).toBe(existingId);
+ expect(findCrmForm().props('existingId')).toBe(existingId);
});
it('renders correct fields prop', () => {
@@ -101,15 +101,15 @@ describe('Customer relations contact form wrapper', () => {
{ name: 'description', label: 'Description' },
];
if (isEditMode) fields.push({ name: 'active', label: 'Active', required: true, bool: true });
- expect(findContactForm().props('fields')).toEqual(fields);
+ expect(findCrmForm().props('fields')).toEqual(fields);
});
it('renders correct title prop', () => {
- expect(findContactForm().props('title')).toBe(title);
+ expect(findCrmForm().props('title')).toBe(title);
});
it('renders correct successMessage prop', () => {
- expect(findContactForm().props('successMessage')).toBe(successMessage);
+ expect(findCrmForm().props('successMessage')).toBe(successMessage);
});
});
});
diff --git a/spec/frontend/crm/crm_form_spec.js b/spec/frontend/crm/crm_form_spec.js
new file mode 100644
index 00000000000..eabcf5b1b1b
--- /dev/null
+++ b/spec/frontend/crm/crm_form_spec.js
@@ -0,0 +1,339 @@
+import { GlAlert, GlFormCheckbox, GlFormInput, GlFormSelect, GlFormGroup } from '@gitlab/ui';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import VueRouter from 'vue-router';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import CrmForm from '~/crm/components/crm_form.vue';
+import routes from '~/crm/contacts/routes';
+import createContactMutation from '~/crm/contacts/components/graphql/create_contact.mutation.graphql';
+import updateContactMutation from '~/crm/contacts/components/graphql/update_contact.mutation.graphql';
+import getGroupContactsQuery from '~/crm/contacts/components/graphql/get_group_contacts.query.graphql';
+import createOrganizationMutation from '~/crm/organizations/components/graphql/create_organization.mutation.graphql';
+import getGroupOrganizationsQuery from '~/crm/organizations/components/graphql/get_group_organizations.query.graphql';
+import {
+ createContactMutationErrorResponse,
+ createContactMutationResponse,
+ getGroupContactsQueryResponse,
+ updateContactMutationErrorResponse,
+ updateContactMutationResponse,
+ createOrganizationMutationErrorResponse,
+ createOrganizationMutationResponse,
+ getGroupOrganizationsQueryResponse,
+} from './mock_data';
+
+const FORM_CREATE_CONTACT = 'create contact';
+const FORM_UPDATE_CONTACT = 'update contact';
+const FORM_CREATE_ORG = 'create organization';
+
+describe('Reusable form component', () => {
+ Vue.use(VueApollo);
+ Vue.use(VueRouter);
+
+ const DEFAULT_RESPONSES = {
+ createContact: Promise.resolve(createContactMutationResponse),
+ updateContact: Promise.resolve(updateContactMutationResponse),
+ createOrg: Promise.resolve(createOrganizationMutationResponse),
+ };
+
+ let wrapper;
+ let handler;
+ let fakeApollo;
+ let router;
+
+ beforeEach(() => {
+ router = new VueRouter({
+ base: '',
+ mode: 'history',
+ routes,
+ });
+ router.push('/test');
+
+ handler = jest.fn().mockImplementation((key) => DEFAULT_RESPONSES[key]);
+
+ const hanlderWithKey = (key) => (...args) => handler(key, ...args);
+
+ fakeApollo = createMockApollo([
+ [createContactMutation, hanlderWithKey('createContact')],
+ [updateContactMutation, hanlderWithKey('updateContact')],
+ [createOrganizationMutation, hanlderWithKey('createOrg')],
+ ]);
+
+ fakeApollo.clients.defaultClient.cache.writeQuery({
+ query: getGroupContactsQuery,
+ variables: { groupFullPath: 'flightjs' },
+ data: getGroupContactsQueryResponse.data,
+ });
+
+ fakeApollo.clients.defaultClient.cache.writeQuery({
+ query: getGroupOrganizationsQuery,
+ variables: { groupFullPath: 'flightjs' },
+ data: getGroupOrganizationsQueryResponse.data,
+ });
+ });
+
+ const mockToastShow = jest.fn();
+
+ const findSaveButton = () => wrapper.findByTestId('save-button');
+ const findForm = () => wrapper.find('form');
+ const findError = () => wrapper.findComponent(GlAlert);
+ const findFormGroup = (at) => wrapper.findAllComponents(GlFormGroup).at(at);
+
+ const mountComponent = (propsData) => {
+ wrapper = shallowMountExtended(CrmForm, {
+ router,
+ apolloProvider: fakeApollo,
+ propsData: { drawerOpen: true, ...propsData },
+ mocks: {
+ $toast: {
+ show: mockToastShow,
+ },
+ },
+ });
+ };
+
+ const mountContact = ({ propsData, extraFields = [] } = {}) => {
+ mountComponent({
+ fields: [
+ { name: 'firstName', label: 'First name', required: true },
+ { name: 'lastName', label: 'Last name', required: true },
+ { name: 'email', label: 'Email', required: true },
+ { name: 'phone', label: 'Phone' },
+ { name: 'description', label: 'Description' },
+ {
+ name: 'organizationId',
+ label: 'Organization',
+ values: [
+ { key: 'gid://gitlab/CustomerRelations::Organization/1', value: 'GitLab' },
+ { key: 'gid://gitlab/CustomerRelations::Organization/2', value: 'ABC Corp' },
+ ],
+ },
+ ...extraFields,
+ ],
+ getQuery: {
+ query: getGroupContactsQuery,
+ variables: { groupFullPath: 'flightjs' },
+ },
+ getQueryNodePath: 'group.contacts',
+ ...propsData,
+ });
+ };
+
+ const mountContactCreate = () => {
+ const propsData = {
+ title: 'New contact',
+ successMessage: 'Contact has been added.',
+ buttonLabel: 'Create contact',
+ mutation: createContactMutation,
+ additionalCreateParams: { groupId: 'gid://gitlab/Group/26' },
+ };
+ mountContact({ propsData });
+ };
+
+ const mountContactUpdate = () => {
+ const propsData = {
+ title: 'Edit contact',
+ successMessage: 'Contact has been updated.',
+ mutation: updateContactMutation,
+ existingId: 'gid://gitlab/CustomerRelations::Contact/12',
+ };
+ const extraFields = [{ name: 'active', label: 'Active', required: true, bool: true }];
+ mountContact({ propsData, extraFields });
+ };
+
+ const mountOrganization = ({ propsData } = {}) => {
+ mountComponent({
+ fields: [
+ { name: 'name', label: 'Name', required: true },
+ { name: 'defaultRate', label: 'Default rate', input: { type: 'number', step: '0.01' } },
+ { name: 'description', label: 'Description' },
+ ],
+ getQuery: {
+ query: getGroupOrganizationsQuery,
+ variables: { groupFullPath: 'flightjs' },
+ },
+ getQueryNodePath: 'group.organizations',
+ ...propsData,
+ });
+ };
+
+ const mountOrganizationCreate = () => {
+ const propsData = {
+ title: 'New organization',
+ successMessage: 'Organization has been added.',
+ buttonLabel: 'Create organization',
+ mutation: createOrganizationMutation,
+ additionalCreateParams: { groupId: 'gid://gitlab/Group/26' },
+ };
+ mountOrganization({ propsData });
+ };
+
+ const forms = {
+ [FORM_CREATE_CONTACT]: {
+ mountFunction: mountContactCreate,
+ mutationErrorResponse: createContactMutationErrorResponse,
+ toastMessage: 'Contact has been added.',
+ },
+ [FORM_UPDATE_CONTACT]: {
+ mountFunction: mountContactUpdate,
+ mutationErrorResponse: updateContactMutationErrorResponse,
+ toastMessage: 'Contact has been updated.',
+ },
+ [FORM_CREATE_ORG]: {
+ mountFunction: mountOrganizationCreate,
+ mutationErrorResponse: createOrganizationMutationErrorResponse,
+ toastMessage: 'Organization has been added.',
+ },
+ };
+ const asTestParams = (...keys) => keys.map((name) => [name, forms[name]]);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe.each(asTestParams(FORM_CREATE_CONTACT, FORM_UPDATE_CONTACT))(
+ '%s form save button',
+ (name, { mountFunction }) => {
+ beforeEach(() => {
+ mountFunction();
+ });
+
+ it('should be disabled when required fields are empty', async () => {
+ wrapper.find('#firstName').vm.$emit('input', '');
+ await waitForPromises();
+
+ expect(findSaveButton().props('disabled')).toBe(true);
+ });
+
+ it('should not be disabled when required fields have values', async () => {
+ wrapper.find('#firstName').vm.$emit('input', 'A');
+ wrapper.find('#lastName').vm.$emit('input', 'B');
+ wrapper.find('#email').vm.$emit('input', 'C');
+ await waitForPromises();
+
+ expect(findSaveButton().props('disabled')).toBe(false);
+ });
+ },
+ );
+
+ describe.each(asTestParams(FORM_CREATE_ORG))('%s form save button', (name, { mountFunction }) => {
+ beforeEach(() => {
+ mountFunction();
+ });
+
+ it('should be disabled when required field is empty', async () => {
+ wrapper.find('#name').vm.$emit('input', '');
+ await waitForPromises();
+
+ expect(findSaveButton().props('disabled')).toBe(true);
+ });
+
+ it('should not be disabled when required field has a value', async () => {
+ wrapper.find('#name').vm.$emit('input', 'A');
+ await waitForPromises();
+
+ expect(findSaveButton().props('disabled')).toBe(false);
+ });
+ });
+
+ describe.each(asTestParams(FORM_CREATE_CONTACT, FORM_UPDATE_CONTACT, FORM_CREATE_ORG))(
+ 'when %s mutation is successful',
+ (name, { mountFunction, toastMessage }) => {
+ it('form should display correct toast message', async () => {
+ mountFunction();
+
+ findForm().trigger('submit');
+ await waitForPromises();
+
+ expect(mockToastShow).toHaveBeenCalledWith(toastMessage);
+ });
+ },
+ );
+
+ describe.each(asTestParams(FORM_CREATE_CONTACT, FORM_UPDATE_CONTACT, FORM_CREATE_ORG))(
+ 'when %s mutation fails',
+ (formName, { mutationErrorResponse, mountFunction }) => {
+ beforeEach(() => {
+ jest.spyOn(console, 'error').mockImplementation();
+ });
+
+ it('should show error on reject', async () => {
+ handler.mockRejectedValue('ERROR');
+
+ mountFunction();
+
+ findForm().trigger('submit');
+ await waitForPromises();
+
+ expect(findError().text()).toBe('Something went wrong. Please try again.');
+ });
+
+ it('should show error on error response', async () => {
+ handler.mockResolvedValue(mutationErrorResponse);
+
+ mountFunction();
+
+ findForm().trigger('submit');
+ await waitForPromises();
+
+ expect(findError().text()).toBe(`${formName} is invalid.`);
+ });
+ },
+ );
+
+ describe('edit form', () => {
+ beforeEach(() => {
+ mountContactUpdate();
+ });
+
+ it.each`
+ index | id | component | value
+ ${0} | ${'firstName'} | ${GlFormInput} | ${'Marty'}
+ ${1} | ${'lastName'} | ${GlFormInput} | ${'McFly'}
+ ${2} | ${'email'} | ${GlFormInput} | ${'example@gitlab.com'}
+ ${4} | ${'description'} | ${GlFormInput} | ${undefined}
+ ${3} | ${'phone'} | ${GlFormInput} | ${undefined}
+ ${5} | ${'organizationId'} | ${GlFormSelect} | ${'gid://gitlab/CustomerRelations::Organization/2'}
+ `(
+ 'should render the correct component for #$id with the value "$value"',
+ ({ index, id, component, value }) => {
+ const findFormElement = () => findFormGroup(index).findComponent(component);
+
+ expect(findFormElement().attributes('id')).toBe(id);
+ expect(findFormElement().attributes('value')).toBe(value);
+ },
+ );
+
+ it('should render a checked GlFormCheckbox for #active', () => {
+ const activeCheckboxIndex = 6;
+ const findFormElement = () =>
+ findFormGroup(activeCheckboxIndex).findComponent(GlFormCheckbox);
+
+ expect(findFormElement().attributes('id')).toBe('active');
+ expect(findFormElement().attributes('checked')).toBe('true');
+ });
+
+ it('should include updated values in update mutation', () => {
+ wrapper.find('#firstName').vm.$emit('input', 'Michael');
+ wrapper
+ .find('#organizationId')
+ .vm.$emit('input', 'gid://gitlab/CustomerRelations::Organization/1');
+
+ findForm().trigger('submit');
+
+ expect(handler).toHaveBeenCalledWith('updateContact', {
+ input: {
+ active: true,
+ description: null,
+ email: 'example@gitlab.com',
+ firstName: 'Michael',
+ id: 'gid://gitlab/CustomerRelations::Contact/12',
+ lastName: 'McFly',
+ organizationId: 'gid://gitlab/CustomerRelations::Organization/1',
+ phone: null,
+ },
+ });
+ });
+ });
+});
diff --git a/spec/frontend/crm/form_spec.js b/spec/frontend/crm/form_spec.js
deleted file mode 100644
index 57e28b396cf..00000000000
--- a/spec/frontend/crm/form_spec.js
+++ /dev/null
@@ -1,339 +0,0 @@
-import { GlAlert, GlFormCheckbox, GlFormInput, GlFormSelect, GlFormGroup } from '@gitlab/ui';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import VueRouter from 'vue-router';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import Form from '~/crm/components/form.vue';
-import routes from '~/crm/contacts/routes';
-import createContactMutation from '~/crm/contacts/components/graphql/create_contact.mutation.graphql';
-import updateContactMutation from '~/crm/contacts/components/graphql/update_contact.mutation.graphql';
-import getGroupContactsQuery from '~/crm/contacts/components/graphql/get_group_contacts.query.graphql';
-import createOrganizationMutation from '~/crm/organizations/components/graphql/create_organization.mutation.graphql';
-import getGroupOrganizationsQuery from '~/crm/organizations/components/graphql/get_group_organizations.query.graphql';
-import {
- createContactMutationErrorResponse,
- createContactMutationResponse,
- getGroupContactsQueryResponse,
- updateContactMutationErrorResponse,
- updateContactMutationResponse,
- createOrganizationMutationErrorResponse,
- createOrganizationMutationResponse,
- getGroupOrganizationsQueryResponse,
-} from './mock_data';
-
-const FORM_CREATE_CONTACT = 'create contact';
-const FORM_UPDATE_CONTACT = 'update contact';
-const FORM_CREATE_ORG = 'create organization';
-
-describe('Reusable form component', () => {
- Vue.use(VueApollo);
- Vue.use(VueRouter);
-
- const DEFAULT_RESPONSES = {
- createContact: Promise.resolve(createContactMutationResponse),
- updateContact: Promise.resolve(updateContactMutationResponse),
- createOrg: Promise.resolve(createOrganizationMutationResponse),
- };
-
- let wrapper;
- let handler;
- let fakeApollo;
- let router;
-
- beforeEach(() => {
- router = new VueRouter({
- base: '',
- mode: 'history',
- routes,
- });
- router.push('/test');
-
- handler = jest.fn().mockImplementation((key) => DEFAULT_RESPONSES[key]);
-
- const hanlderWithKey = (key) => (...args) => handler(key, ...args);
-
- fakeApollo = createMockApollo([
- [createContactMutation, hanlderWithKey('createContact')],
- [updateContactMutation, hanlderWithKey('updateContact')],
- [createOrganizationMutation, hanlderWithKey('createOrg')],
- ]);
-
- fakeApollo.clients.defaultClient.cache.writeQuery({
- query: getGroupContactsQuery,
- variables: { groupFullPath: 'flightjs' },
- data: getGroupContactsQueryResponse.data,
- });
-
- fakeApollo.clients.defaultClient.cache.writeQuery({
- query: getGroupOrganizationsQuery,
- variables: { groupFullPath: 'flightjs' },
- data: getGroupOrganizationsQueryResponse.data,
- });
- });
-
- const mockToastShow = jest.fn();
-
- const findSaveButton = () => wrapper.findByTestId('save-button');
- const findForm = () => wrapper.find('form');
- const findError = () => wrapper.findComponent(GlAlert);
- const findFormGroup = (at) => wrapper.findAllComponents(GlFormGroup).at(at);
-
- const mountComponent = (propsData) => {
- wrapper = shallowMountExtended(Form, {
- router,
- apolloProvider: fakeApollo,
- propsData: { drawerOpen: true, ...propsData },
- mocks: {
- $toast: {
- show: mockToastShow,
- },
- },
- });
- };
-
- const mountContact = ({ propsData, extraFields = [] } = {}) => {
- mountComponent({
- fields: [
- { name: 'firstName', label: 'First name', required: true },
- { name: 'lastName', label: 'Last name', required: true },
- { name: 'email', label: 'Email', required: true },
- { name: 'phone', label: 'Phone' },
- { name: 'description', label: 'Description' },
- {
- name: 'organizationId',
- label: 'Organization',
- values: [
- { key: 'gid://gitlab/CustomerRelations::Organization/1', value: 'GitLab' },
- { key: 'gid://gitlab/CustomerRelations::Organization/2', value: 'ABC Corp' },
- ],
- },
- ...extraFields,
- ],
- getQuery: {
- query: getGroupContactsQuery,
- variables: { groupFullPath: 'flightjs' },
- },
- getQueryNodePath: 'group.contacts',
- ...propsData,
- });
- };
-
- const mountContactCreate = () => {
- const propsData = {
- title: 'New contact',
- successMessage: 'Contact has been added.',
- buttonLabel: 'Create contact',
- mutation: createContactMutation,
- additionalCreateParams: { groupId: 'gid://gitlab/Group/26' },
- };
- mountContact({ propsData });
- };
-
- const mountContactUpdate = () => {
- const propsData = {
- title: 'Edit contact',
- successMessage: 'Contact has been updated.',
- mutation: updateContactMutation,
- existingId: 'gid://gitlab/CustomerRelations::Contact/12',
- };
- const extraFields = [{ name: 'active', label: 'Active', required: true, bool: true }];
- mountContact({ propsData, extraFields });
- };
-
- const mountOrganization = ({ propsData } = {}) => {
- mountComponent({
- fields: [
- { name: 'name', label: 'Name', required: true },
- { name: 'defaultRate', label: 'Default rate', input: { type: 'number', step: '0.01' } },
- { name: 'description', label: 'Description' },
- ],
- getQuery: {
- query: getGroupOrganizationsQuery,
- variables: { groupFullPath: 'flightjs' },
- },
- getQueryNodePath: 'group.organizations',
- ...propsData,
- });
- };
-
- const mountOrganizationCreate = () => {
- const propsData = {
- title: 'New organization',
- successMessage: 'Organization has been added.',
- buttonLabel: 'Create organization',
- mutation: createOrganizationMutation,
- additionalCreateParams: { groupId: 'gid://gitlab/Group/26' },
- };
- mountOrganization({ propsData });
- };
-
- const forms = {
- [FORM_CREATE_CONTACT]: {
- mountFunction: mountContactCreate,
- mutationErrorResponse: createContactMutationErrorResponse,
- toastMessage: 'Contact has been added.',
- },
- [FORM_UPDATE_CONTACT]: {
- mountFunction: mountContactUpdate,
- mutationErrorResponse: updateContactMutationErrorResponse,
- toastMessage: 'Contact has been updated.',
- },
- [FORM_CREATE_ORG]: {
- mountFunction: mountOrganizationCreate,
- mutationErrorResponse: createOrganizationMutationErrorResponse,
- toastMessage: 'Organization has been added.',
- },
- };
- const asTestParams = (...keys) => keys.map((name) => [name, forms[name]]);
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe.each(asTestParams(FORM_CREATE_CONTACT, FORM_UPDATE_CONTACT))(
- '%s form save button',
- (name, { mountFunction }) => {
- beforeEach(() => {
- mountFunction();
- });
-
- it('should be disabled when required fields are empty', async () => {
- wrapper.find('#firstName').vm.$emit('input', '');
- await waitForPromises();
-
- expect(findSaveButton().props('disabled')).toBe(true);
- });
-
- it('should not be disabled when required fields have values', async () => {
- wrapper.find('#firstName').vm.$emit('input', 'A');
- wrapper.find('#lastName').vm.$emit('input', 'B');
- wrapper.find('#email').vm.$emit('input', 'C');
- await waitForPromises();
-
- expect(findSaveButton().props('disabled')).toBe(false);
- });
- },
- );
-
- describe.each(asTestParams(FORM_CREATE_ORG))('%s form save button', (name, { mountFunction }) => {
- beforeEach(() => {
- mountFunction();
- });
-
- it('should be disabled when required field is empty', async () => {
- wrapper.find('#name').vm.$emit('input', '');
- await waitForPromises();
-
- expect(findSaveButton().props('disabled')).toBe(true);
- });
-
- it('should not be disabled when required field has a value', async () => {
- wrapper.find('#name').vm.$emit('input', 'A');
- await waitForPromises();
-
- expect(findSaveButton().props('disabled')).toBe(false);
- });
- });
-
- describe.each(asTestParams(FORM_CREATE_CONTACT, FORM_UPDATE_CONTACT, FORM_CREATE_ORG))(
- 'when %s mutation is successful',
- (name, { mountFunction, toastMessage }) => {
- it('form should display correct toast message', async () => {
- mountFunction();
-
- findForm().trigger('submit');
- await waitForPromises();
-
- expect(mockToastShow).toHaveBeenCalledWith(toastMessage);
- });
- },
- );
-
- describe.each(asTestParams(FORM_CREATE_CONTACT, FORM_UPDATE_CONTACT, FORM_CREATE_ORG))(
- 'when %s mutation fails',
- (formName, { mutationErrorResponse, mountFunction }) => {
- beforeEach(() => {
- jest.spyOn(console, 'error').mockImplementation();
- });
-
- it('should show error on reject', async () => {
- handler.mockRejectedValue('ERROR');
-
- mountFunction();
-
- findForm().trigger('submit');
- await waitForPromises();
-
- expect(findError().text()).toBe('Something went wrong. Please try again.');
- });
-
- it('should show error on error response', async () => {
- handler.mockResolvedValue(mutationErrorResponse);
-
- mountFunction();
-
- findForm().trigger('submit');
- await waitForPromises();
-
- expect(findError().text()).toBe(`${formName} is invalid.`);
- });
- },
- );
-
- describe('edit form', () => {
- beforeEach(() => {
- mountContactUpdate();
- });
-
- it.each`
- index | id | component | value
- ${0} | ${'firstName'} | ${GlFormInput} | ${'Marty'}
- ${1} | ${'lastName'} | ${GlFormInput} | ${'McFly'}
- ${2} | ${'email'} | ${GlFormInput} | ${'example@gitlab.com'}
- ${4} | ${'description'} | ${GlFormInput} | ${undefined}
- ${3} | ${'phone'} | ${GlFormInput} | ${undefined}
- ${5} | ${'organizationId'} | ${GlFormSelect} | ${'gid://gitlab/CustomerRelations::Organization/2'}
- `(
- 'should render the correct component for #$id with the value "$value"',
- ({ index, id, component, value }) => {
- const findFormElement = () => findFormGroup(index).findComponent(component);
-
- expect(findFormElement().attributes('id')).toBe(id);
- expect(findFormElement().attributes('value')).toBe(value);
- },
- );
-
- it('should render a checked GlFormCheckbox for #active', () => {
- const activeCheckboxIndex = 6;
- const findFormElement = () =>
- findFormGroup(activeCheckboxIndex).findComponent(GlFormCheckbox);
-
- expect(findFormElement().attributes('id')).toBe('active');
- expect(findFormElement().attributes('checked')).toBe('true');
- });
-
- it('should include updated values in update mutation', () => {
- wrapper.find('#firstName').vm.$emit('input', 'Michael');
- wrapper
- .find('#organizationId')
- .vm.$emit('input', 'gid://gitlab/CustomerRelations::Organization/1');
-
- findForm().trigger('submit');
-
- expect(handler).toHaveBeenCalledWith('updateContact', {
- input: {
- active: true,
- description: null,
- email: 'example@gitlab.com',
- firstName: 'Michael',
- id: 'gid://gitlab/CustomerRelations::Contact/12',
- lastName: 'McFly',
- organizationId: 'gid://gitlab/CustomerRelations::Organization/1',
- phone: null,
- },
- });
- });
- });
-});
diff --git a/spec/frontend/crm/organization_form_wrapper_spec.js b/spec/frontend/crm/organization_form_wrapper_spec.js
index 9f26b9157e6..d795c585622 100644
--- a/spec/frontend/crm/organization_form_wrapper_spec.js
+++ b/spec/frontend/crm/organization_form_wrapper_spec.js
@@ -1,6 +1,6 @@
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import OrganizationFormWrapper from '~/crm/organizations/components/organization_form_wrapper.vue';
-import OrganizationForm from '~/crm/components/form.vue';
+import CrmForm from '~/crm/components/crm_form.vue';
import getGroupOrganizationsQuery from '~/crm/organizations/components/graphql/get_group_organizations.query.graphql';
import createOrganizationMutation from '~/crm/organizations/components/graphql/create_organization.mutation.graphql';
import updateOrganizationMutation from '~/crm/organizations/components/graphql/update_organization.mutation.graphql';
@@ -8,7 +8,7 @@ import updateOrganizationMutation from '~/crm/organizations/components/graphql/u
describe('Customer relations organization form wrapper', () => {
let wrapper;
- const findOrganizationForm = () => wrapper.findComponent(OrganizationForm);
+ const findOrganizationForm = () => wrapper.findComponent(CrmForm);
const $apollo = {
queries: {
diff --git a/spec/frontend/cycle_analytics/base_spec.js b/spec/frontend/cycle_analytics/base_spec.js
deleted file mode 100644
index 013bea671a8..00000000000
--- a/spec/frontend/cycle_analytics/base_spec.js
+++ /dev/null
@@ -1,265 +0,0 @@
-import { GlLoadingIcon, GlEmptyState } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import Vuex from 'vuex';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import ValueStreamMetrics from '~/analytics/shared/components/value_stream_metrics.vue';
-import BaseComponent from '~/cycle_analytics/components/base.vue';
-import PathNavigation from '~/cycle_analytics/components/path_navigation.vue';
-import StageTable from '~/cycle_analytics/components/stage_table.vue';
-import ValueStreamFilters from '~/cycle_analytics/components/value_stream_filters.vue';
-import { NOT_ENOUGH_DATA_ERROR } from '~/cycle_analytics/constants';
-import initState from '~/cycle_analytics/store/state';
-import {
- transformedProjectStagePathData,
- selectedStage,
- issueEvents,
- createdBefore,
- createdAfter,
- currentGroup,
- stageCounts,
- initialPaginationState as pagination,
-} from './mock_data';
-
-const selectedStageEvents = issueEvents.events;
-const noDataSvgPath = 'path/to/no/data';
-const noAccessSvgPath = 'path/to/no/access';
-const selectedStageCount = stageCounts[selectedStage.id];
-const fullPath = 'full/path/to/foo';
-
-Vue.use(Vuex);
-
-let wrapper;
-
-const { id: groupId, path: groupPath } = currentGroup;
-const defaultState = {
- currentGroup,
- createdBefore,
- createdAfter,
- stageCounts,
- endpoints: { fullPath, groupId, groupPath },
-};
-
-function createStore({ initialState = {}, initialGetters = {} }) {
- return new Vuex.Store({
- state: {
- ...initState(),
- ...defaultState,
- ...initialState,
- },
- getters: {
- pathNavigationData: () => transformedProjectStagePathData,
- filterParams: () => ({
- created_after: createdAfter,
- created_before: createdBefore,
- }),
- ...initialGetters,
- },
- });
-}
-
-function createComponent({ initialState, initialGetters } = {}) {
- return extendedWrapper(
- shallowMount(BaseComponent, {
- store: createStore({ initialState, initialGetters }),
- propsData: {
- noDataSvgPath,
- noAccessSvgPath,
- },
- stubs: {
- StageTable,
- },
- }),
- );
-}
-
-const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
-const findPathNavigation = () => wrapper.findComponent(PathNavigation);
-const findFilters = () => wrapper.findComponent(ValueStreamFilters);
-const findOverviewMetrics = () => wrapper.findComponent(ValueStreamMetrics);
-const findStageTable = () => wrapper.findComponent(StageTable);
-const findStageEvents = () => findStageTable().props('stageEvents');
-const findEmptyStageTitle = () => wrapper.findComponent(GlEmptyState).props('title');
-const findPagination = () => wrapper.findByTestId('vsa-stage-pagination');
-
-const hasMetricsRequests = (reqs) => {
- const foundReqs = findOverviewMetrics().props('requests');
- expect(foundReqs.length).toEqual(reqs.length);
- expect(foundReqs.map(({ name }) => name)).toEqual(reqs);
-};
-
-describe('Value stream analytics component', () => {
- beforeEach(() => {
- wrapper = createComponent({ initialState: { selectedStage, selectedStageEvents, pagination } });
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- it('renders the path navigation component', () => {
- expect(findPathNavigation().exists()).toBe(true);
- });
-
- it('receives the stages formatted for the path navigation', () => {
- expect(findPathNavigation().props('stages')).toBe(transformedProjectStagePathData);
- });
-
- it('renders the overview metrics', () => {
- expect(findOverviewMetrics().exists()).toBe(true);
- });
-
- it('passes requests prop to the metrics component', () => {
- hasMetricsRequests(['recent activity']);
- });
-
- it('renders the stage table', () => {
- expect(findStageTable().exists()).toBe(true);
- });
-
- it('passes the selected stage count to the stage table', () => {
- expect(findStageTable().props('stageCount')).toBe(selectedStageCount);
- });
-
- it('renders the stage table events', () => {
- expect(findStageEvents()).toEqual(selectedStageEvents);
- });
-
- it('renders the filters', () => {
- expect(findFilters().exists()).toBe(true);
- });
-
- it('displays the date range selector and hides the project selector', () => {
- expect(findFilters().props()).toMatchObject({
- hasProjectFilter: false,
- hasDateRangeFilter: true,
- });
- });
-
- it('passes the paths to the filter bar', () => {
- expect(findFilters().props()).toEqual({
- groupId,
- groupPath,
- endDate: createdBefore,
- hasDateRangeFilter: true,
- hasProjectFilter: false,
- selectedProjects: [],
- startDate: createdAfter,
- });
- });
-
- it('does not render the loading icon', () => {
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- it('renders pagination', () => {
- expect(findPagination().exists()).toBe(true);
- });
-
- describe('with `cycleAnalyticsForGroups=true` license', () => {
- beforeEach(() => {
- wrapper = createComponent({ initialState: { features: { cycleAnalyticsForGroups: true } } });
- });
-
- it('passes requests prop to the metrics component', () => {
- hasMetricsRequests(['time summary', 'recent activity']);
- });
- });
-
- describe('isLoading = true', () => {
- beforeEach(() => {
- wrapper = createComponent({
- initialState: { isLoading: true },
- });
- });
-
- it('renders the path navigation component with prop `loading` set to true', () => {
- expect(findPathNavigation().props('loading')).toBe(true);
- });
-
- it('does not render the stage table', () => {
- expect(findStageTable().exists()).toBe(false);
- });
-
- it('renders the overview metrics', () => {
- expect(findOverviewMetrics().exists()).toBe(true);
- });
-
- it('renders the loading icon', () => {
- expect(findLoadingIcon().exists()).toBe(true);
- });
- });
-
- describe('isLoadingStage = true', () => {
- beforeEach(() => {
- wrapper = createComponent({
- initialState: { isLoadingStage: true },
- });
- });
-
- it('renders the stage table with a loading icon', () => {
- const tableWrapper = findStageTable();
- expect(tableWrapper.exists()).toBe(true);
- expect(tableWrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
- });
-
- it('renders the path navigation loading state', () => {
- expect(findPathNavigation().props('loading')).toBe(true);
- });
- });
-
- describe('isEmptyStage = true', () => {
- const emptyStageParams = {
- isEmptyStage: true,
- selectedStage: { ...selectedStage, emptyStageText: 'This stage is empty' },
- };
- beforeEach(() => {
- wrapper = createComponent({ initialState: emptyStageParams });
- });
-
- it('renders the empty stage with `Not enough data` message', () => {
- expect(findEmptyStageTitle()).toBe(NOT_ENOUGH_DATA_ERROR);
- });
-
- describe('with a selectedStageError', () => {
- beforeEach(() => {
- wrapper = createComponent({
- initialState: {
- ...emptyStageParams,
- selectedStageError: 'There is too much data to calculate',
- },
- });
- });
-
- it('renders the empty stage with `There is too much data to calculate` message', () => {
- expect(findEmptyStageTitle()).toBe('There is too much data to calculate');
- });
- });
- });
-
- describe('without a selected stage', () => {
- beforeEach(() => {
- wrapper = createComponent({
- initialGetters: { pathNavigationData: () => [] },
- initialState: { selectedStage: null, isEmptyStage: true },
- });
- });
-
- it('renders the stage table', () => {
- expect(findStageTable().exists()).toBe(true);
- });
-
- it('does not render the path navigation', () => {
- expect(findPathNavigation().exists()).toBe(false);
- });
-
- it('does not render the stage table events', () => {
- expect(findStageEvents()).toHaveLength(0);
- });
-
- it('does not render the loading icon', () => {
- expect(findLoadingIcon().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/cycle_analytics/filter_bar_spec.js b/spec/frontend/cycle_analytics/filter_bar_spec.js
deleted file mode 100644
index 36933790cf7..00000000000
--- a/spec/frontend/cycle_analytics/filter_bar_spec.js
+++ /dev/null
@@ -1,223 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-import axios from 'axios';
-import MockAdapter from 'axios-mock-adapter';
-import Vuex from 'vuex';
-import {
- filterMilestones,
- filterLabels,
-} from 'jest/vue_shared/components/filtered_search_bar/store/modules/filters/mock_data';
-import FilterBar from '~/cycle_analytics/components/filter_bar.vue';
-import storeConfig from '~/cycle_analytics/store';
-import * as commonUtils from '~/lib/utils/common_utils';
-import * as urlUtils from '~/lib/utils/url_utility';
-import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
-import * as utils from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
-import initialFiltersState from '~/vue_shared/components/filtered_search_bar/store/modules/filters/state';
-import UrlSync from '~/vue_shared/components/url_sync.vue';
-
-Vue.use(Vuex);
-
-const milestoneTokenType = 'milestone';
-const labelsTokenType = 'labels';
-const authorTokenType = 'author';
-const assigneesTokenType = 'assignees';
-
-const initialFilterBarState = {
- selectedMilestone: null,
- selectedAuthor: null,
- selectedAssigneeList: null,
- selectedLabelList: null,
-};
-
-const defaultParams = {
- milestone_title: null,
- 'not[milestone_title]': null,
- author_username: null,
- 'not[author_username]': null,
- assignee_username: null,
- 'not[assignee_username]': null,
- label_name: null,
- 'not[label_name]': null,
-};
-
-async function shouldMergeUrlParams(wrapper, result) {
- await nextTick();
- expect(urlUtils.mergeUrlParams).toHaveBeenCalledWith(result, window.location.href, {
- spreadArrays: true,
- });
- expect(commonUtils.historyPushState).toHaveBeenCalled();
-}
-
-describe('Filter bar', () => {
- let wrapper;
- let store;
- let mock;
-
- let setFiltersMock;
-
- const createStore = (initialState = {}) => {
- setFiltersMock = jest.fn();
-
- return new Vuex.Store({
- modules: {
- filters: {
- namespaced: true,
- state: {
- ...initialFiltersState(),
- ...initialState,
- },
- actions: {
- setFilters: setFiltersMock,
- },
- },
- },
- });
- };
-
- const createComponent = (initialStore) => {
- return shallowMount(FilterBar, {
- store: initialStore,
- propsData: {
- groupPath: 'foo',
- },
- stubs: {
- UrlSync,
- },
- });
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- wrapper.destroy();
- mock.restore();
- });
-
- const selectedMilestone = [filterMilestones[0]];
- const selectedLabelList = [filterLabels[0]];
-
- const findFilteredSearch = () => wrapper.findComponent(FilteredSearchBar);
- const getSearchToken = (type) =>
- findFilteredSearch()
- .props('tokens')
- .find((token) => token.type === type);
-
- describe('default', () => {
- beforeEach(() => {
- store = createStore();
- wrapper = createComponent(store);
- });
-
- it('renders FilteredSearchBar component', () => {
- expect(findFilteredSearch().exists()).toBe(true);
- });
- });
-
- describe('when the state has data', () => {
- beforeEach(() => {
- store = createStore({
- milestones: { data: selectedMilestone },
- labels: { data: selectedLabelList },
- authors: { data: [] },
- assignees: { data: [] },
- });
- wrapper = createComponent(store);
- });
-
- it('displays the milestone and label token', () => {
- const tokens = findFilteredSearch().props('tokens');
-
- expect(tokens).toHaveLength(4);
- expect(tokens[0].type).toBe(milestoneTokenType);
- expect(tokens[1].type).toBe(labelsTokenType);
- expect(tokens[2].type).toBe(authorTokenType);
- expect(tokens[3].type).toBe(assigneesTokenType);
- });
-
- it('provides the initial milestone token', () => {
- const { initialMilestones: milestoneToken } = getSearchToken(milestoneTokenType);
-
- expect(milestoneToken).toHaveLength(selectedMilestone.length);
- });
-
- it('provides the initial label token', () => {
- const { initialLabels: labelToken } = getSearchToken(labelsTokenType);
-
- expect(labelToken).toHaveLength(selectedLabelList.length);
- });
- });
-
- describe('when the user interacts', () => {
- beforeEach(() => {
- store = createStore({
- milestones: { data: filterMilestones },
- labels: { data: filterLabels },
- });
- wrapper = createComponent(store);
- jest.spyOn(utils, 'processFilters');
- });
-
- it('clicks on the search button, setFilters is dispatched', () => {
- const filters = [
- { type: 'milestone', value: { data: selectedMilestone[0].title, operator: '=' } },
- { type: 'labels', value: { data: selectedLabelList[0].title, operator: '=' } },
- ];
-
- findFilteredSearch().vm.$emit('onFilter', filters);
-
- expect(utils.processFilters).toHaveBeenCalledWith(filters);
-
- expect(setFiltersMock).toHaveBeenCalledWith(expect.anything(), {
- selectedLabelList: [{ value: selectedLabelList[0].title, operator: '=' }],
- selectedMilestone: { value: selectedMilestone[0].title, operator: '=' },
- selectedAssigneeList: [],
- selectedAuthor: null,
- });
- });
- });
-
- describe.each([
- ['selectedMilestone', 'milestone_title', { value: '12.0', operator: '=' }, '12.0'],
- ['selectedAuthor', 'author_username', { value: 'rootUser', operator: '=' }, 'rootUser'],
- [
- 'selectedLabelList',
- 'label_name',
- [
- { value: 'Afternix', operator: '=' },
- { value: 'Brouceforge', operator: '=' },
- ],
- ['Afternix', 'Brouceforge'],
- ],
- [
- 'selectedAssigneeList',
- 'assignee_username',
- [
- { value: 'rootUser', operator: '=' },
- { value: 'secondaryUser', operator: '=' },
- ],
- ['rootUser', 'secondaryUser'],
- ],
- ])('with a %s updates the %s url parameter', (stateKey, paramKey, payload, result) => {
- beforeEach(() => {
- commonUtils.historyPushState = jest.fn();
- urlUtils.mergeUrlParams = jest.fn();
-
- mock = new MockAdapter(axios);
- wrapper = createComponent(storeConfig);
-
- wrapper.vm.$store.dispatch('filters/setFilters', {
- ...initialFilterBarState,
- [stateKey]: payload,
- });
- });
- it(`sets the ${paramKey} url parameter`, () => {
- return shouldMergeUrlParams(wrapper, {
- ...defaultParams,
- [paramKey]: result,
- });
- });
- });
-});
diff --git a/spec/frontend/cycle_analytics/formatted_stage_count_spec.js b/spec/frontend/cycle_analytics/formatted_stage_count_spec.js
deleted file mode 100644
index 1228b8511ea..00000000000
--- a/spec/frontend/cycle_analytics/formatted_stage_count_spec.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import Component from '~/cycle_analytics/components/formatted_stage_count.vue';
-
-describe('Formatted Stage Count', () => {
- let wrapper = null;
-
- const createComponent = (stageCount = null) => {
- wrapper = shallowMount(Component, {
- propsData: {
- stageCount,
- },
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it.each`
- stageCount | expectedOutput
- ${null} | ${'-'}
- ${1} | ${'1 item'}
- ${10} | ${'10 items'}
- ${1000} | ${'1,000 items'}
- ${1001} | ${'1,000+ items'}
- `('returns "$expectedOutput" for stageCount=$stageCount', ({ stageCount, expectedOutput }) => {
- createComponent(stageCount);
- expect(wrapper.text()).toContain(expectedOutput);
- });
-});
diff --git a/spec/frontend/cycle_analytics/mock_data.js b/spec/frontend/cycle_analytics/mock_data.js
deleted file mode 100644
index 02666260cdb..00000000000
--- a/spec/frontend/cycle_analytics/mock_data.js
+++ /dev/null
@@ -1,261 +0,0 @@
-import valueStreamAnalyticsStages from 'test_fixtures/projects/analytics/value_stream_analytics/stages.json';
-import issueStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/issue.json';
-import planStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/plan.json';
-import reviewStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/review.json';
-import codeStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/code.json';
-import testStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/test.json';
-import stagingStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/staging.json';
-
-import { TEST_HOST } from 'helpers/test_constants';
-import {
- DEFAULT_VALUE_STREAM,
- PAGINATION_TYPE,
- PAGINATION_SORT_DIRECTION_DESC,
- PAGINATION_SORT_FIELD_END_EVENT,
-} from '~/cycle_analytics/constants';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
-import { getDateInPast } from '~/lib/utils/datetime_utility';
-
-const DEFAULT_DAYS_IN_PAST = 30;
-export const createdBefore = new Date(2019, 0, 14);
-export const createdAfter = getDateInPast(createdBefore, DEFAULT_DAYS_IN_PAST);
-
-export const deepCamelCase = (obj) => convertObjectPropsToCamelCase(obj, { deep: true });
-
-export const getStageByTitle = (stages, title) =>
- stages.find((stage) => stage.title && stage.title.toLowerCase().trim() === title) || {};
-
-export const defaultStages = ['issue', 'plan', 'review', 'code', 'test', 'staging'];
-
-const stageFixtures = {
- issue: issueStageFixtures,
- plan: planStageFixtures,
- review: reviewStageFixtures,
- code: codeStageFixtures,
- test: testStageFixtures,
- staging: stagingStageFixtures,
-};
-
-export const summary = [
- { value: '20', title: 'New Issues' },
- { value: null, title: 'Commits' },
- { value: null, title: 'Deploys' },
- { value: null, title: 'Deployment Frequency', unit: '/day' },
-];
-
-export const issueStage = {
- id: 'issue',
- title: 'Issue',
- name: 'issue',
- legend: '',
- description: 'Time before an issue gets scheduled',
- value: null,
-};
-
-export const planStage = {
- id: 'plan',
- title: 'Plan',
- name: 'plan',
- legend: '',
- description: 'Time before an issue starts implementation',
- value: 75600,
-};
-
-export const codeStage = {
- id: 'code',
- title: 'Code',
- name: 'code',
- legend: '',
- description: 'Time until first merge request',
- value: 172800,
-};
-
-export const testStage = {
- id: 'test',
- title: 'Test',
- name: 'test',
- legend: '',
- description: 'Total test time for all commits/merges',
- value: 17550,
-};
-
-export const reviewStage = {
- id: 'review',
- title: 'Review',
- name: 'review',
- legend: '',
- description: 'Time between merge request creation and merge/close',
- value: null,
-};
-
-export const stagingStage = {
- id: 'staging',
- title: 'Staging',
- name: 'staging',
- legend: '',
- description: 'From merge request merge until deploy to production',
- value: 172800,
-};
-
-export const selectedStage = {
- ...issueStage,
- value: null,
- active: false,
- emptyStageText:
- 'The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.',
-
- slug: 'issue',
-};
-
-export const convertedData = {
- summary: [
- { value: '20', title: 'New Issues' },
- { value: '-', title: 'Commits' },
- { value: '-', title: 'Deploys' },
- { value: '-', title: 'Deployment Frequency', unit: '/day' },
- ],
-};
-
-export const rawIssueEvents = stageFixtures.issue;
-export const issueEvents = deepCamelCase(rawIssueEvents);
-export const reviewEvents = deepCamelCase(stageFixtures.review);
-
-export const pathNavIssueMetric = 172800;
-
-export const rawStageCounts = [
- { id: 'issue', count: 6 },
- { id: 'plan', count: 6 },
- { id: 'code', count: 1 },
- { id: 'test', count: 5 },
- { id: 'review', count: 12 },
- { id: 'staging', count: 3 },
-];
-
-export const stageCounts = {
- code: 1,
- issue: 6,
- plan: 6,
- review: 12,
- staging: 3,
- test: 5,
-};
-
-export const rawStageMedians = [
- { id: 'issue', value: 172800 },
- { id: 'plan', value: 86400 },
- { id: 'review', value: 1036800 },
- { id: 'code', value: 129600 },
- { id: 'test', value: 259200 },
- { id: 'staging', value: 388800 },
-];
-
-export const stageMedians = {
- issue: 172800,
- plan: 86400,
- review: 1036800,
- code: 129600,
- test: 259200,
- staging: 388800,
-};
-
-export const formattedStageMedians = {
- issue: '2d',
- plan: '1d',
- review: '1w',
- code: '1d',
- test: '3d',
- staging: '4d',
-};
-
-export const allowedStages = [issueStage, planStage, codeStage];
-
-export const transformedProjectStagePathData = [
- {
- metric: 172800,
- selected: true,
- stageCount: 6,
- icon: null,
- id: 'issue',
- title: 'Issue',
- name: 'issue',
- legend: '',
- description: 'Time before an issue gets scheduled',
- value: null,
- },
- {
- metric: 86400,
- selected: false,
- stageCount: 6,
- icon: null,
- id: 'plan',
- title: 'Plan',
- name: 'plan',
- legend: '',
- description: 'Time before an issue starts implementation',
- value: 75600,
- },
- {
- metric: 129600,
- selected: false,
- stageCount: 1,
- icon: null,
- id: 'code',
- title: 'Code',
- name: 'code',
- legend: '',
- description: 'Time until first merge request',
- value: 172800,
- },
-];
-
-export const selectedValueStream = DEFAULT_VALUE_STREAM;
-
-export const group = {
- id: 1,
- name: 'foo',
- path: 'foo',
- full_path: 'foo',
- avatar_url: `${TEST_HOST}/images/home/nasa.svg`,
-};
-
-export const currentGroup = convertObjectPropsToCamelCase(group, { deep: true });
-
-export const selectedProjects = [
- {
- id: 'gid://gitlab/Project/1',
- name: 'cool project',
- pathWithNamespace: 'group/cool-project',
- avatarUrl: null,
- },
- {
- id: 'gid://gitlab/Project/2',
- name: 'another cool project',
- pathWithNamespace: 'group/another-cool-project',
- avatarUrl: null,
- },
-];
-
-export const rawValueStreamStages = valueStreamAnalyticsStages.stages;
-
-export const valueStreamStages = rawValueStreamStages.map((s) =>
- convertObjectPropsToCamelCase(s, { deep: true }),
-);
-
-export const initialPaginationQuery = {
- page: 15,
- sort: PAGINATION_SORT_FIELD_END_EVENT,
- direction: PAGINATION_SORT_DIRECTION_DESC,
-};
-
-export const initialPaginationState = {
- ...initialPaginationQuery,
- page: null,
- hasNextPage: false,
-};
-
-export const basePaginationResult = {
- pagination: PAGINATION_TYPE,
- sort: PAGINATION_SORT_FIELD_END_EVENT,
- direction: PAGINATION_SORT_DIRECTION_DESC,
- page: null,
-};
diff --git a/spec/frontend/cycle_analytics/path_navigation_spec.js b/spec/frontend/cycle_analytics/path_navigation_spec.js
deleted file mode 100644
index fec1526359c..00000000000
--- a/spec/frontend/cycle_analytics/path_navigation_spec.js
+++ /dev/null
@@ -1,150 +0,0 @@
-import { GlPath, GlSkeletonLoader } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import Component from '~/cycle_analytics/components/path_navigation.vue';
-import { transformedProjectStagePathData, selectedStage } from './mock_data';
-
-describe('Project PathNavigation', () => {
- let wrapper = null;
- let trackingSpy = null;
-
- const createComponent = (props) => {
- return extendedWrapper(
- mount(Component, {
- propsData: {
- stages: transformedProjectStagePathData,
- selectedStage,
- loading: false,
- ...props,
- },
- }),
- );
- };
-
- const findPathNavigation = () => {
- return wrapper.findByTestId('gl-path-nav');
- };
-
- const findPathNavigationItems = () => {
- return findPathNavigation().findAll('li');
- };
-
- const findPathNavigationTitles = () => {
- return findPathNavigation()
- .findAll('li button')
- .wrappers.map((w) => w.html());
- };
-
- const clickItemAt = (index) => {
- findPathNavigationItems().at(index).find('button').trigger('click');
- };
-
- const pathItemContent = () => findPathNavigationItems().wrappers.map(extendedWrapper);
- const firstPopover = () => wrapper.findAllByTestId('stage-item-popover').at(0);
-
- beforeEach(() => {
- wrapper = createComponent();
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- });
-
- afterEach(() => {
- unmockTracking();
- wrapper.destroy();
- wrapper = null;
- });
-
- describe('displays correctly', () => {
- it('has the correct props', () => {
- expect(wrapper.findComponent(GlPath).props('items')).toMatchObject(
- transformedProjectStagePathData,
- );
- });
-
- it('contains all the expected stages', () => {
- const stageContent = findPathNavigationTitles();
- transformedProjectStagePathData.forEach((stage, index) => {
- expect(stageContent[index]).toContain(stage.title);
- });
- });
-
- describe('loading', () => {
- describe('is false', () => {
- it('displays the gl-path component', () => {
- expect(wrapper.findComponent(GlPath).exists()).toBe(true);
- });
-
- it('hides the gl-skeleton-loading component', () => {
- expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(false);
- });
-
- it('renders each stage', () => {
- const result = findPathNavigationTitles();
- expect(result.length).toBe(transformedProjectStagePathData.length);
- });
-
- it('renders each stage with its median', () => {
- const result = findPathNavigationTitles();
- transformedProjectStagePathData.forEach(({ title, metric }, index) => {
- expect(result[index]).toContain(title);
- expect(result[index]).toContain(metric.toString());
- });
- });
-
- describe('popovers', () => {
- beforeEach(() => {
- wrapper = createComponent({ stages: transformedProjectStagePathData });
- });
-
- it('renders popovers for all stages', () => {
- pathItemContent().forEach((stage) => {
- expect(stage.findByTestId('stage-item-popover').exists()).toBe(true);
- });
- });
-
- it('shows the median stage time for the first stage item', () => {
- expect(firstPopover().text()).toContain('Stage time (median)');
- });
- });
- });
-
- describe('is true', () => {
- beforeEach(() => {
- wrapper = createComponent({ loading: true });
- });
-
- it('hides the gl-path component', () => {
- expect(wrapper.findComponent(GlPath).exists()).toBe(false);
- });
-
- it('displays the gl-skeleton-loading component', () => {
- expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(true);
- });
- });
- });
- });
-
- describe('event handling', () => {
- it('emits the selected event', () => {
- expect(wrapper.emitted('selected')).toBeUndefined();
-
- clickItemAt(0);
- clickItemAt(1);
- clickItemAt(2);
-
- expect(wrapper.emitted().selected).toEqual([
- [transformedProjectStagePathData[0]],
- [transformedProjectStagePathData[1]],
- [transformedProjectStagePathData[2]],
- ]);
- });
-
- it('sends tracking information', () => {
- clickItemAt(0);
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_path_navigation', {
- extra: { stage_id: selectedStage.slug },
- });
- });
- });
-});
diff --git a/spec/frontend/cycle_analytics/stage_table_spec.js b/spec/frontend/cycle_analytics/stage_table_spec.js
deleted file mode 100644
index 473e1d5b664..00000000000
--- a/spec/frontend/cycle_analytics/stage_table_spec.js
+++ /dev/null
@@ -1,371 +0,0 @@
-import { GlEmptyState, GlLoadingIcon, GlTable } from '@gitlab/ui';
-import { shallowMount, mount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import StageTable from '~/cycle_analytics/components/stage_table.vue';
-import { PAGINATION_SORT_FIELD_DURATION } from '~/cycle_analytics/constants';
-import { issueEvents, issueStage, reviewStage, reviewEvents } from './mock_data';
-
-let wrapper = null;
-let trackingSpy = null;
-
-const noDataSvgPath = 'path/to/no/data';
-const emptyStateTitle = 'Too much data';
-const notEnoughDataError = "We don't have enough data to show this stage.";
-const issueEventItems = issueEvents.events;
-const reviewEventItems = reviewEvents.events;
-const [firstIssueEvent] = issueEventItems;
-const [firstReviewEvent] = reviewEventItems;
-const pagination = { page: 1, hasNextPage: true };
-
-const findStageEvents = () => wrapper.findAllByTestId('vsa-stage-event');
-const findPagination = () => wrapper.findByTestId('vsa-stage-pagination');
-const findTable = () => wrapper.findComponent(GlTable);
-const findTableHead = () => wrapper.find('thead');
-const findTableHeadColumns = () => findTableHead().findAll('th');
-const findStageEventTitle = (ev) => extendedWrapper(ev).findByTestId('vsa-stage-event-title');
-const findStageEventLink = (ev) => extendedWrapper(ev).findByTestId('vsa-stage-event-link');
-const findStageTime = () => wrapper.findByTestId('vsa-stage-event-time');
-const findStageLastEvent = () => wrapper.findByTestId('vsa-stage-last-event');
-const findIcon = (name) => wrapper.findByTestId(`${name}-icon`);
-
-function createComponent(props = {}, shallow = false) {
- const func = shallow ? shallowMount : mount;
- return extendedWrapper(
- func(StageTable, {
- propsData: {
- isLoading: false,
- stageEvents: issueEventItems,
- noDataSvgPath,
- selectedStage: issueStage,
- pagination,
- ...props,
- },
- stubs: {
- GlLoadingIcon,
- GlEmptyState,
- },
- }),
- );
-}
-
-describe('StageTable', () => {
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('is loaded with data', () => {
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- it('will render the correct events', () => {
- const evs = findStageEvents();
- expect(evs).toHaveLength(issueEventItems.length);
-
- const titles = evs.wrappers.map((ev) => findStageEventTitle(ev).text());
- issueEventItems.forEach((ev, index) => {
- expect(titles[index]).toBe(ev.title);
- });
- });
-
- it('will not display the default data message', () => {
- expect(wrapper.html()).not.toContain(notEnoughDataError);
- });
- });
-
- describe('with minimal stage data', () => {
- beforeEach(() => {
- wrapper = createComponent({ currentStage: { title: 'New stage title' } });
- });
-
- it('will render the correct events', () => {
- const evs = findStageEvents();
- expect(evs).toHaveLength(issueEventItems.length);
-
- const titles = evs.wrappers.map((ev) => findStageEventTitle(ev).text());
- issueEventItems.forEach((ev, index) => {
- expect(titles[index]).toBe(ev.title);
- });
- });
-
- it('will not display the project name in the record link', () => {
- const evs = findStageEvents();
-
- const links = evs.wrappers.map((ev) => findStageEventLink(ev).text());
- issueEventItems.forEach((ev, index) => {
- expect(links[index]).toBe(`#${ev.iid}`);
- });
- });
- });
-
- describe('default event', () => {
- beforeEach(() => {
- wrapper = createComponent({
- stageEvents: [{ ...firstIssueEvent }],
- selectedStage: { ...issueStage, custom: false },
- });
- });
-
- it('will render the event title', () => {
- expect(wrapper.findByTestId('vsa-stage-event-title').text()).toBe(firstIssueEvent.title);
- });
-
- it('will set the workflow title to "Issues"', () => {
- expect(findTableHead().text()).toContain('Issues');
- });
-
- it('does not render the fork icon', () => {
- expect(findIcon('fork').exists()).toBe(false);
- });
-
- it('does not render the branch icon', () => {
- expect(findIcon('commit').exists()).toBe(false);
- });
-
- it('will render the total time', () => {
- const createdAt = firstIssueEvent.createdAt.replace(' ago', '');
- expect(findStageTime().text()).toBe(createdAt);
- });
-
- it('will render the end event', () => {
- expect(findStageLastEvent().text()).toBe(firstIssueEvent.endEventTimestamp);
- });
-
- it('will render the author', () => {
- expect(wrapper.findByTestId('vsa-stage-event-author').text()).toContain(
- firstIssueEvent.author.name,
- );
- });
-
- it('will render the created at date', () => {
- expect(wrapper.findByTestId('vsa-stage-event-date').text()).toContain(
- firstIssueEvent.createdAt,
- );
- });
- });
-
- describe('merge request event', () => {
- beforeEach(() => {
- wrapper = createComponent({
- stageEvents: [{ ...firstReviewEvent }],
- selectedStage: { ...reviewStage, custom: false },
- });
- });
-
- it('will set the workflow title to "Merge requests"', () => {
- expect(findTableHead().text()).toContain('Merge requests');
- expect(findTableHead().text()).not.toContain('Issues');
- });
- });
-
- describe('isLoading = true', () => {
- beforeEach(() => {
- wrapper = createComponent({ isLoading: true }, true);
- });
-
- it('will display the loading icon', () => {
- expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
- });
-
- it('will not display pagination', () => {
- expect(findPagination().exists()).toBe(false);
- });
- });
-
- describe('with no stageEvents', () => {
- beforeEach(() => {
- wrapper = createComponent({ stageEvents: [] });
- });
-
- it('will render the empty state', () => {
- expect(wrapper.findComponent(GlEmptyState).exists()).toBe(true);
- });
-
- it('will display the default no data message', () => {
- expect(wrapper.html()).toContain(notEnoughDataError);
- });
-
- it('will not display the pagination component', () => {
- expect(findPagination().exists()).toBe(false);
- });
- });
-
- describe('emptyStateTitle set', () => {
- beforeEach(() => {
- wrapper = createComponent({ stageEvents: [], emptyStateTitle });
- });
-
- it('will display the custom message', () => {
- expect(wrapper.html()).not.toContain(notEnoughDataError);
- expect(wrapper.html()).toContain(emptyStateTitle);
- });
- });
-
- describe('includeProjectName set', () => {
- const fakenamespace = 'some/fake/path';
- beforeEach(() => {
- wrapper = createComponent({ includeProjectName: true });
- });
-
- it('will display the project name in the record link', () => {
- const evs = findStageEvents();
-
- const links = evs.wrappers.map((ev) => findStageEventLink(ev).text());
- issueEventItems.forEach((ev, index) => {
- expect(links[index]).toBe(`${ev.projectPath}#${ev.iid}`);
- });
- });
-
- describe.each`
- namespaceFullPath | hasFullPath
- ${'fake'} | ${false}
- ${fakenamespace} | ${true}
- `('with a namespace', ({ namespaceFullPath, hasFullPath }) => {
- let evs = null;
- let links = null;
-
- beforeEach(() => {
- wrapper = createComponent({
- includeProjectName: true,
- stageEvents: issueEventItems.map((ie) => ({ ...ie, namespaceFullPath })),
- });
-
- evs = findStageEvents();
- links = evs.wrappers.map((ev) => findStageEventLink(ev).text());
- });
-
- it(`with namespaceFullPath='${namespaceFullPath}' ${
- hasFullPath ? 'will' : 'does not'
- } include the namespace`, () => {
- issueEventItems.forEach((ev, index) => {
- if (hasFullPath) {
- expect(links[index]).toBe(`${namespaceFullPath}/${ev.projectPath}#${ev.iid}`);
- } else {
- expect(links[index]).toBe(`${ev.projectPath}#${ev.iid}`);
- }
- });
- });
- });
- });
-
- describe('Pagination', () => {
- beforeEach(() => {
- wrapper = createComponent();
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- });
-
- afterEach(() => {
- unmockTracking();
- wrapper.destroy();
- });
-
- it('will display the pagination component', () => {
- expect(findPagination().exists()).toBe(true);
- });
-
- it('clicking prev or next will emit an event', async () => {
- expect(wrapper.emitted('handleUpdatePagination')).toBeUndefined();
-
- findPagination().vm.$emit('input', 2);
- await nextTick();
-
- expect(wrapper.emitted('handleUpdatePagination')[0]).toEqual([{ page: 2 }]);
- });
-
- it('clicking prev or next will send tracking information', () => {
- findPagination().vm.$emit('input', 2);
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_button', { label: 'pagination' });
- });
-
- describe('with `hasNextPage=false', () => {
- beforeEach(() => {
- wrapper = createComponent({ pagination: { page: 1, hasNextPage: false } });
- });
-
- it('will not display the pagination component', () => {
- expect(findPagination().exists()).toBe(false);
- });
- });
- });
-
- describe('Sorting', () => {
- const triggerTableSort = (sortDesc = true) =>
- findTable().vm.$emit('sort-changed', {
- sortBy: PAGINATION_SORT_FIELD_DURATION,
- sortDesc,
- });
-
- beforeEach(() => {
- wrapper = createComponent();
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- });
-
- afterEach(() => {
- unmockTracking();
- wrapper.destroy();
- });
-
- it('can sort the end event or duration', () => {
- findTableHeadColumns()
- .wrappers.slice(1)
- .forEach((w) => {
- expect(w.attributes('aria-sort')).toBe('none');
- });
- });
-
- it('cannot be sorted by title', () => {
- findTableHeadColumns()
- .wrappers.slice(0, 1)
- .forEach((w) => {
- expect(w.attributes('aria-sort')).toBeUndefined();
- });
- });
-
- it('clicking a table column will send tracking information', () => {
- triggerTableSort();
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_button', {
- label: 'sort_duration_desc',
- });
- });
-
- it('clicking a table column will update the sort field', () => {
- expect(wrapper.emitted('handleUpdatePagination')).toBeUndefined();
- triggerTableSort();
-
- expect(wrapper.emitted('handleUpdatePagination')[0]).toEqual([
- {
- direction: 'desc',
- sort: 'duration',
- },
- ]);
- });
-
- it('with sortDesc=false will toggle the direction field', () => {
- expect(wrapper.emitted('handleUpdatePagination')).toBeUndefined();
- triggerTableSort(false);
-
- expect(wrapper.emitted('handleUpdatePagination')[0]).toEqual([
- {
- direction: 'asc',
- sort: 'duration',
- },
- ]);
- });
-
- describe('with sortable=false', () => {
- beforeEach(() => {
- wrapper = createComponent({ sortable: false });
- });
-
- it('cannot sort the table', () => {
- findTableHeadColumns().wrappers.forEach((w) => {
- expect(w.attributes('aria-sort')).toBeUndefined();
- });
- });
- });
- });
-});
diff --git a/spec/frontend/cycle_analytics/store/actions_spec.js b/spec/frontend/cycle_analytics/store/actions_spec.js
deleted file mode 100644
index 94b6de85a5c..00000000000
--- a/spec/frontend/cycle_analytics/store/actions_spec.js
+++ /dev/null
@@ -1,518 +0,0 @@
-import axios from 'axios';
-import MockAdapter from 'axios-mock-adapter';
-import testAction from 'helpers/vuex_action_helper';
-import * as actions from '~/cycle_analytics/store/actions';
-import * as getters from '~/cycle_analytics/store/getters';
-import httpStatusCodes from '~/lib/utils/http_status';
-import {
- allowedStages,
- selectedStage,
- selectedValueStream,
- currentGroup,
- createdAfter,
- createdBefore,
- initialPaginationState,
- reviewEvents,
-} from '../mock_data';
-
-const { id: groupId, path: groupPath } = currentGroup;
-const mockMilestonesPath = 'mock-milestones.json';
-const mockLabelsPath = 'mock-labels.json';
-const mockRequestPath = 'some/cool/path';
-const mockFullPath = '/namespace/-/analytics/value_stream_analytics/value_streams';
-const mockEndpoints = {
- fullPath: mockFullPath,
- requestPath: mockRequestPath,
- labelsPath: mockLabelsPath,
- milestonesPath: mockMilestonesPath,
- groupId,
- groupPath,
-};
-const mockSetDateActionCommit = {
- payload: { createdAfter, createdBefore },
- type: 'SET_DATE_RANGE',
-};
-
-const defaultState = {
- ...getters,
- selectedValueStream,
- createdAfter,
- createdBefore,
- pagination: initialPaginationState,
-};
-
-describe('Project Value Stream Analytics actions', () => {
- let state;
- let mock;
-
- beforeEach(() => {
- state = { ...defaultState };
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- mock.restore();
- state = {};
- });
-
- const mutationTypes = (arr) => arr.map(({ type }) => type);
-
- describe.each`
- action | payload | expectedActions | expectedMutations
- ${'setDateRange'} | ${{ createdAfter, createdBefore }} | ${[{ type: 'refetchStageData' }]} | ${[mockSetDateActionCommit]}
- ${'setFilters'} | ${[]} | ${[{ type: 'refetchStageData' }]} | ${[]}
- ${'setSelectedStage'} | ${{ selectedStage }} | ${[{ type: 'refetchStageData' }]} | ${[{ type: 'SET_SELECTED_STAGE', payload: { selectedStage } }]}
- ${'setSelectedValueStream'} | ${{ selectedValueStream }} | ${[{ type: 'fetchValueStreamStages' }]} | ${[{ type: 'SET_SELECTED_VALUE_STREAM', payload: { selectedValueStream } }]}
- `('$action', ({ action, payload, expectedActions, expectedMutations }) => {
- const types = mutationTypes(expectedMutations);
- it(`will dispatch ${expectedActions} and commit ${types}`, () =>
- testAction({
- action: actions[action],
- state,
- payload,
- expectedMutations,
- expectedActions,
- }));
- });
-
- describe('initializeVsa', () => {
- const selectedAuthor = 'Author';
- const selectedMilestone = 'Milestone 1';
- const selectedAssigneeList = ['Assignee 1', 'Assignee 2'];
- const selectedLabelList = ['Label 1', 'Label 2'];
- const payload = {
- endpoints: mockEndpoints,
- selectedAuthor,
- selectedMilestone,
- selectedAssigneeList,
- selectedLabelList,
- selectedStage,
- };
- const mockFilterEndpoints = {
- groupEndpoint: 'foo',
- labelsEndpoint: mockLabelsPath,
- milestonesEndpoint: mockMilestonesPath,
- projectEndpoint: '/namespace/-/analytics/value_stream_analytics/value_streams',
- };
-
- it('will dispatch fetchValueStreams actions and commit SET_LOADING and INITIALIZE_VSA', () => {
- return testAction({
- action: actions.initializeVsa,
- state: {},
- payload,
- expectedMutations: [
- { type: 'INITIALIZE_VSA', payload },
- { type: 'SET_LOADING', payload: true },
- { type: 'SET_LOADING', payload: false },
- ],
- expectedActions: [
- { type: 'filters/setEndpoints', payload: mockFilterEndpoints },
- {
- type: 'filters/initialize',
- payload: { selectedAuthor, selectedMilestone, selectedAssigneeList, selectedLabelList },
- },
- { type: 'fetchValueStreams' },
- { type: 'setInitialStage', payload: selectedStage },
- ],
- });
- });
- });
-
- describe('setInitialStage', () => {
- beforeEach(() => {
- state = { ...state, stages: allowedStages };
- });
-
- describe('with a selected stage', () => {
- it('will commit `SET_SELECTED_STAGE` and fetchValueStreamStageData actions', () => {
- const fakeStage = { ...selectedStage, id: 'fake', name: 'fake-stae' };
- return testAction({
- action: actions.setInitialStage,
- state,
- payload: fakeStage,
- expectedMutations: [
- {
- type: 'SET_SELECTED_STAGE',
- payload: fakeStage,
- },
- ],
- expectedActions: [{ type: 'fetchValueStreamStageData' }],
- });
- });
- });
-
- describe('without a selected stage', () => {
- it('will select the first stage from the value stream', () => {
- const [firstStage] = allowedStages;
- testAction({
- action: actions.setInitialStage,
- state,
- payload: null,
- expectedMutations: [{ type: 'SET_SELECTED_STAGE', payload: firstStage }],
- expectedActions: [{ type: 'fetchValueStreamStageData' }],
- });
- });
- });
-
- describe('with no value stream stages available', () => {
- it('will return SET_NO_ACCESS_ERROR', () => {
- state = { ...state, stages: [] };
- testAction({
- action: actions.setInitialStage,
- state,
- payload: null,
- expectedMutations: [{ type: 'SET_NO_ACCESS_ERROR' }],
- expectedActions: [],
- });
- });
- });
- });
-
- describe('updateStageTablePagination', () => {
- beforeEach(() => {
- state = { ...state, selectedStage };
- });
-
- it(`will dispatch the "fetchStageData" action and commit the 'SET_PAGINATION' mutation`, () => {
- return testAction({
- action: actions.updateStageTablePagination,
- state,
- expectedMutations: [{ type: 'SET_PAGINATION' }],
- expectedActions: [{ type: 'fetchStageData', payload: selectedStage.id }],
- });
- });
- });
-
- describe('fetchStageData', () => {
- const mockStagePath = /value_streams\/\w+\/stages\/\w+\/records/;
- const headers = {
- 'X-Next-Page': 2,
- 'X-Page': 1,
- };
-
- beforeEach(() => {
- state = {
- ...defaultState,
- endpoints: mockEndpoints,
- selectedStage,
- };
- mock = new MockAdapter(axios);
- mock.onGet(mockStagePath).reply(httpStatusCodes.OK, reviewEvents, headers);
- });
-
- it(`commits the 'RECEIVE_STAGE_DATA_SUCCESS' mutation`, () =>
- testAction({
- action: actions.fetchStageData,
- state,
- payload: {},
- expectedMutations: [
- { type: 'REQUEST_STAGE_DATA' },
- { type: 'RECEIVE_STAGE_DATA_SUCCESS', payload: reviewEvents },
- { type: 'SET_PAGINATION', payload: { hasNextPage: true, page: 1 } },
- ],
- expectedActions: [],
- }));
-
- describe('with a successful request, but an error in the payload', () => {
- const tooMuchDataError = 'Too much data';
-
- beforeEach(() => {
- state = {
- ...defaultState,
- endpoints: mockEndpoints,
- selectedStage,
- };
- mock = new MockAdapter(axios);
- mock.onGet(mockStagePath).reply(httpStatusCodes.OK, { error: tooMuchDataError });
- });
-
- it(`commits the 'RECEIVE_STAGE_DATA_ERROR' mutation`, () =>
- testAction({
- action: actions.fetchStageData,
- state,
- payload: { error: tooMuchDataError },
- expectedMutations: [
- { type: 'REQUEST_STAGE_DATA' },
- { type: 'RECEIVE_STAGE_DATA_ERROR', payload: tooMuchDataError },
- ],
- expectedActions: [],
- }));
- });
-
- describe('with a failing request', () => {
- beforeEach(() => {
- state = {
- ...defaultState,
- endpoints: mockEndpoints,
- selectedStage,
- };
- mock = new MockAdapter(axios);
- mock.onGet(mockStagePath).reply(httpStatusCodes.BAD_REQUEST);
- });
-
- it(`commits the 'RECEIVE_STAGE_DATA_ERROR' mutation`, () =>
- testAction({
- action: actions.fetchStageData,
- state,
- payload: {},
- expectedMutations: [{ type: 'REQUEST_STAGE_DATA' }, { type: 'RECEIVE_STAGE_DATA_ERROR' }],
- expectedActions: [],
- }));
- });
- });
-
- describe('fetchValueStreams', () => {
- const mockValueStreamPath = /\/analytics\/value_stream_analytics\/value_streams/;
-
- beforeEach(() => {
- state = {
- endpoints: mockEndpoints,
- };
- mock = new MockAdapter(axios);
- mock.onGet(mockValueStreamPath).reply(httpStatusCodes.OK);
- });
-
- it(`commits the 'REQUEST_VALUE_STREAMS' mutation`, () =>
- testAction({
- action: actions.fetchValueStreams,
- state,
- payload: {},
- expectedMutations: [{ type: 'REQUEST_VALUE_STREAMS' }],
- expectedActions: [{ type: 'receiveValueStreamsSuccess' }],
- }));
-
- describe('with a failing request', () => {
- beforeEach(() => {
- mock = new MockAdapter(axios);
- mock.onGet(mockValueStreamPath).reply(httpStatusCodes.BAD_REQUEST);
- });
-
- it(`commits the 'RECEIVE_VALUE_STREAMS_ERROR' mutation`, () =>
- testAction({
- action: actions.fetchValueStreams,
- state,
- payload: {},
- expectedMutations: [
- { type: 'REQUEST_VALUE_STREAMS' },
- { type: 'RECEIVE_VALUE_STREAMS_ERROR', payload: httpStatusCodes.BAD_REQUEST },
- ],
- expectedActions: [],
- }));
- });
- });
-
- describe('receiveValueStreamsSuccess', () => {
- const mockValueStream = {
- id: 'mockDefault',
- name: 'mock default',
- };
- const mockValueStreams = [mockValueStream, selectedValueStream];
- it('with data, will set the first value stream', () => {
- testAction({
- action: actions.receiveValueStreamsSuccess,
- state,
- payload: mockValueStreams,
- expectedMutations: [{ type: 'RECEIVE_VALUE_STREAMS_SUCCESS', payload: mockValueStreams }],
- expectedActions: [{ type: 'setSelectedValueStream', payload: mockValueStream }],
- });
- });
-
- it('without data, will set the default value stream', () => {
- testAction({
- action: actions.receiveValueStreamsSuccess,
- state,
- payload: [],
- expectedMutations: [{ type: 'RECEIVE_VALUE_STREAMS_SUCCESS', payload: [] }],
- expectedActions: [{ type: 'setSelectedValueStream', payload: selectedValueStream }],
- });
- });
- });
-
- describe('fetchValueStreamStages', () => {
- const mockValueStreamPath = /\/analytics\/value_stream_analytics\/value_streams/;
-
- beforeEach(() => {
- state = {
- endpoints: mockEndpoints,
- selectedValueStream,
- };
- mock = new MockAdapter(axios);
- mock.onGet(mockValueStreamPath).reply(httpStatusCodes.OK);
- });
-
- it(`commits the 'REQUEST_VALUE_STREAM_STAGES' and 'RECEIVE_VALUE_STREAM_STAGES_SUCCESS' mutations`, () =>
- testAction({
- action: actions.fetchValueStreamStages,
- state,
- payload: {},
- expectedMutations: [
- { type: 'REQUEST_VALUE_STREAM_STAGES' },
- { type: 'RECEIVE_VALUE_STREAM_STAGES_SUCCESS' },
- ],
- expectedActions: [],
- }));
-
- describe('with a failing request', () => {
- beforeEach(() => {
- mock = new MockAdapter(axios);
- mock.onGet(mockValueStreamPath).reply(httpStatusCodes.BAD_REQUEST);
- });
-
- it(`commits the 'RECEIVE_VALUE_STREAM_STAGES_ERROR' mutation`, () =>
- testAction({
- action: actions.fetchValueStreamStages,
- state,
- payload: {},
- expectedMutations: [
- { type: 'REQUEST_VALUE_STREAM_STAGES' },
- { type: 'RECEIVE_VALUE_STREAM_STAGES_ERROR', payload: httpStatusCodes.BAD_REQUEST },
- ],
- expectedActions: [],
- }));
- });
- });
-
- describe('fetchStageMedians', () => {
- const mockValueStreamPath = /median/;
-
- const stageMediansPayload = [
- { id: 'issue', value: null },
- { id: 'plan', value: null },
- { id: 'code', value: null },
- ];
-
- const stageMedianError = new Error(
- `Request failed with status code ${httpStatusCodes.BAD_REQUEST}`,
- );
-
- beforeEach(() => {
- state = {
- fullPath: mockFullPath,
- selectedValueStream,
- stages: allowedStages,
- };
- mock = new MockAdapter(axios);
- mock.onGet(mockValueStreamPath).reply(httpStatusCodes.OK);
- });
-
- it(`commits the 'REQUEST_STAGE_MEDIANS' and 'RECEIVE_STAGE_MEDIANS_SUCCESS' mutations`, () =>
- testAction({
- action: actions.fetchStageMedians,
- state,
- payload: {},
- expectedMutations: [
- { type: 'REQUEST_STAGE_MEDIANS' },
- { type: 'RECEIVE_STAGE_MEDIANS_SUCCESS', payload: stageMediansPayload },
- ],
- expectedActions: [],
- }));
-
- describe('with a failing request', () => {
- beforeEach(() => {
- mock = new MockAdapter(axios);
- mock.onGet(mockValueStreamPath).reply(httpStatusCodes.BAD_REQUEST);
- });
-
- it(`commits the 'RECEIVE_VALUE_STREAM_STAGES_ERROR' mutation`, () =>
- testAction({
- action: actions.fetchStageMedians,
- state,
- payload: {},
- expectedMutations: [
- { type: 'REQUEST_STAGE_MEDIANS' },
- { type: 'RECEIVE_STAGE_MEDIANS_ERROR', payload: stageMedianError },
- ],
- expectedActions: [],
- }));
- });
- });
-
- describe('fetchStageCountValues', () => {
- const mockValueStreamPath = /count/;
- const stageCountsPayload = [
- { id: 'issue', count: 1 },
- { id: 'plan', count: 2 },
- { id: 'code', count: 3 },
- ];
-
- const stageCountError = new Error(
- `Request failed with status code ${httpStatusCodes.BAD_REQUEST}`,
- );
-
- beforeEach(() => {
- state = {
- fullPath: mockFullPath,
- selectedValueStream,
- stages: allowedStages,
- };
- mock = new MockAdapter(axios);
- mock
- .onGet(mockValueStreamPath)
- .replyOnce(httpStatusCodes.OK, { count: 1 })
- .onGet(mockValueStreamPath)
- .replyOnce(httpStatusCodes.OK, { count: 2 })
- .onGet(mockValueStreamPath)
- .replyOnce(httpStatusCodes.OK, { count: 3 });
- });
-
- it(`commits the 'REQUEST_STAGE_COUNTS' and 'RECEIVE_STAGE_COUNTS_SUCCESS' mutations`, () =>
- testAction({
- action: actions.fetchStageCountValues,
- state,
- payload: {},
- expectedMutations: [
- { type: 'REQUEST_STAGE_COUNTS' },
- { type: 'RECEIVE_STAGE_COUNTS_SUCCESS', payload: stageCountsPayload },
- ],
- expectedActions: [],
- }));
-
- describe('with a failing request', () => {
- beforeEach(() => {
- mock = new MockAdapter(axios);
- mock.onGet(mockValueStreamPath).reply(httpStatusCodes.BAD_REQUEST);
- });
-
- it(`commits the 'RECEIVE_STAGE_COUNTS_ERROR' mutation`, () =>
- testAction({
- action: actions.fetchStageCountValues,
- state,
- payload: {},
- expectedMutations: [
- { type: 'REQUEST_STAGE_COUNTS' },
- { type: 'RECEIVE_STAGE_COUNTS_ERROR', payload: stageCountError },
- ],
- expectedActions: [],
- }));
- });
- });
-
- describe('refetchStageData', () => {
- it('will commit SET_LOADING and dispatch fetchValueStreamStageData actions', () =>
- testAction({
- action: actions.refetchStageData,
- state,
- payload: {},
- expectedMutations: [
- { type: 'SET_LOADING', payload: true },
- { type: 'SET_LOADING', payload: false },
- ],
- expectedActions: [{ type: 'fetchValueStreamStageData' }],
- }));
- });
-
- describe('fetchValueStreamStageData', () => {
- it('will dispatch the fetchStageData, fetchStageMedians and fetchStageCountValues actions', () =>
- testAction({
- action: actions.fetchValueStreamStageData,
- state,
- payload: {},
- expectedMutations: [],
- expectedActions: [
- { type: 'fetchStageData' },
- { type: 'fetchStageMedians' },
- { type: 'fetchStageCountValues' },
- ],
- }));
- });
-});
diff --git a/spec/frontend/cycle_analytics/store/getters_spec.js b/spec/frontend/cycle_analytics/store/getters_spec.js
deleted file mode 100644
index c9208045a68..00000000000
--- a/spec/frontend/cycle_analytics/store/getters_spec.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import * as getters from '~/cycle_analytics/store/getters';
-
-import {
- allowedStages,
- stageMedians,
- transformedProjectStagePathData,
- selectedStage,
- stageCounts,
- basePaginationResult,
- initialPaginationState,
-} from '../mock_data';
-
-describe('Value stream analytics getters', () => {
- let state = {};
-
- describe('pathNavigationData', () => {
- it('returns the transformed data', () => {
- state = { stages: allowedStages, medians: stageMedians, selectedStage, stageCounts };
- expect(getters.pathNavigationData(state)).toEqual(transformedProjectStagePathData);
- });
- });
-
- describe('paginationParams', () => {
- beforeEach(() => {
- state = { pagination: initialPaginationState };
- });
-
- it('returns the `pagination` type', () => {
- expect(getters.paginationParams(state)).toEqual(basePaginationResult);
- });
-
- it('returns the `sort` type', () => {
- expect(getters.paginationParams(state)).toEqual(basePaginationResult);
- });
-
- it('with page=10, sets the `page` property', () => {
- const page = 10;
- state = { pagination: { ...initialPaginationState, page } };
- expect(getters.paginationParams(state)).toEqual({ ...basePaginationResult, page });
- });
- });
-});
diff --git a/spec/frontend/cycle_analytics/store/mutations_spec.js b/spec/frontend/cycle_analytics/store/mutations_spec.js
deleted file mode 100644
index 2e9e5d91471..00000000000
--- a/spec/frontend/cycle_analytics/store/mutations_spec.js
+++ /dev/null
@@ -1,132 +0,0 @@
-import { useFakeDate } from 'helpers/fake_date';
-import * as types from '~/cycle_analytics/store/mutation_types';
-import mutations from '~/cycle_analytics/store/mutations';
-import {
- PAGINATION_SORT_FIELD_END_EVENT,
- PAGINATION_SORT_DIRECTION_DESC,
-} from '~/cycle_analytics/constants';
-import {
- selectedStage,
- rawIssueEvents,
- issueEvents,
- selectedValueStream,
- rawValueStreamStages,
- valueStreamStages,
- rawStageMedians,
- formattedStageMedians,
- rawStageCounts,
- stageCounts,
- initialPaginationState as pagination,
-} from '../mock_data';
-
-let state;
-const rawEvents = rawIssueEvents.events;
-const convertedEvents = issueEvents.events;
-const mockRequestPath = 'fake/request/path';
-const mockCreatedAfter = '2020-06-18';
-const mockCreatedBefore = '2020-07-18';
-
-describe('Project Value Stream Analytics mutations', () => {
- useFakeDate(2020, 6, 18);
-
- beforeEach(() => {
- state = { pagination };
- });
-
- afterEach(() => {
- state = null;
- });
-
- it.each`
- mutation | stateKey | value
- ${types.REQUEST_VALUE_STREAMS} | ${'valueStreams'} | ${[]}
- ${types.RECEIVE_VALUE_STREAMS_ERROR} | ${'valueStreams'} | ${[]}
- ${types.REQUEST_VALUE_STREAM_STAGES} | ${'stages'} | ${[]}
- ${types.RECEIVE_VALUE_STREAM_STAGES_ERROR} | ${'stages'} | ${[]}
- ${types.REQUEST_STAGE_DATA} | ${'isLoadingStage'} | ${true}
- ${types.REQUEST_STAGE_DATA} | ${'isEmptyStage'} | ${false}
- ${types.REQUEST_STAGE_DATA} | ${'selectedStageEvents'} | ${[]}
- ${types.RECEIVE_STAGE_DATA_SUCCESS} | ${'isLoadingStage'} | ${false}
- ${types.RECEIVE_STAGE_DATA_SUCCESS} | ${'selectedStageEvents'} | ${[]}
- ${types.RECEIVE_STAGE_DATA_ERROR} | ${'isLoadingStage'} | ${false}
- ${types.RECEIVE_STAGE_DATA_ERROR} | ${'selectedStageEvents'} | ${[]}
- ${types.RECEIVE_STAGE_DATA_ERROR} | ${'isEmptyStage'} | ${true}
- ${types.REQUEST_STAGE_MEDIANS} | ${'medians'} | ${{}}
- ${types.RECEIVE_STAGE_MEDIANS_ERROR} | ${'medians'} | ${{}}
- ${types.REQUEST_STAGE_COUNTS} | ${'stageCounts'} | ${{}}
- ${types.RECEIVE_STAGE_COUNTS_ERROR} | ${'stageCounts'} | ${{}}
- ${types.SET_NO_ACCESS_ERROR} | ${'hasNoAccessError'} | ${true}
- `('$mutation will set $stateKey to $value', ({ mutation, stateKey, value }) => {
- mutations[mutation](state);
-
- expect(state).toMatchObject({ [stateKey]: value });
- });
-
- const mockSetDatePayload = { createdAfter: mockCreatedAfter, createdBefore: mockCreatedBefore };
- const mockInitialPayload = {
- endpoints: { requestPath: mockRequestPath },
- currentGroup: { title: 'cool-group' },
- id: 1337,
- ...mockSetDatePayload,
- };
- const mockInitializedObj = {
- endpoints: { requestPath: mockRequestPath },
- ...mockSetDatePayload,
- };
-
- it.each`
- mutation | stateKey | value
- ${types.INITIALIZE_VSA} | ${'endpoints'} | ${{ requestPath: mockRequestPath }}
- ${types.INITIALIZE_VSA} | ${'createdAfter'} | ${mockCreatedAfter}
- ${types.INITIALIZE_VSA} | ${'createdBefore'} | ${mockCreatedBefore}
- `('$mutation will set $stateKey', ({ mutation, stateKey, value }) => {
- mutations[mutation](state, { ...mockInitialPayload });
-
- expect(state).toMatchObject({ ...mockInitializedObj, [stateKey]: value });
- });
-
- it.each`
- mutation | payload | stateKey | value
- ${types.SET_DATE_RANGE} | ${mockSetDatePayload} | ${'createdAfter'} | ${mockCreatedAfter}
- ${types.SET_DATE_RANGE} | ${mockSetDatePayload} | ${'createdBefore'} | ${mockCreatedBefore}
- ${types.SET_LOADING} | ${true} | ${'isLoading'} | ${true}
- ${types.SET_LOADING} | ${false} | ${'isLoading'} | ${false}
- ${types.SET_SELECTED_VALUE_STREAM} | ${selectedValueStream} | ${'selectedValueStream'} | ${selectedValueStream}
- ${types.SET_PAGINATION} | ${pagination} | ${'pagination'} | ${{ ...pagination, sort: PAGINATION_SORT_FIELD_END_EVENT, direction: PAGINATION_SORT_DIRECTION_DESC }}
- ${types.SET_PAGINATION} | ${{ ...pagination, sort: 'duration', direction: 'asc' }} | ${'pagination'} | ${{ ...pagination, sort: 'duration', direction: 'asc' }}
- ${types.SET_SELECTED_STAGE} | ${selectedStage} | ${'selectedStage'} | ${selectedStage}
- ${types.RECEIVE_VALUE_STREAMS_SUCCESS} | ${[selectedValueStream]} | ${'valueStreams'} | ${[selectedValueStream]}
- ${types.RECEIVE_VALUE_STREAM_STAGES_SUCCESS} | ${{ stages: rawValueStreamStages }} | ${'stages'} | ${valueStreamStages}
- ${types.RECEIVE_STAGE_MEDIANS_SUCCESS} | ${rawStageMedians} | ${'medians'} | ${formattedStageMedians}
- ${types.RECEIVE_STAGE_COUNTS_SUCCESS} | ${rawStageCounts} | ${'stageCounts'} | ${stageCounts}
- `(
- '$mutation with $payload will set $stateKey to $value',
- ({ mutation, payload, stateKey, value }) => {
- mutations[mutation](state, payload);
-
- expect(state).toMatchObject({ [stateKey]: value });
- },
- );
-
- describe('with a stage selected', () => {
- beforeEach(() => {
- state = {
- selectedStage,
- };
- });
-
- it.each`
- mutation | payload | stateKey | value
- ${types.RECEIVE_STAGE_DATA_SUCCESS} | ${[]} | ${'isEmptyStage'} | ${true}
- ${types.RECEIVE_STAGE_DATA_SUCCESS} | ${rawEvents} | ${'selectedStageEvents'} | ${convertedEvents}
- ${types.RECEIVE_STAGE_DATA_SUCCESS} | ${rawEvents} | ${'isEmptyStage'} | ${false}
- `(
- '$mutation with $payload will set $stateKey to $value',
- ({ mutation, payload, stateKey, value }) => {
- mutations[mutation](state, payload);
-
- expect(state).toMatchObject({ [stateKey]: value });
- },
- );
- });
-});
diff --git a/spec/frontend/cycle_analytics/total_time_spec.js b/spec/frontend/cycle_analytics/total_time_spec.js
deleted file mode 100644
index 8cf9feab6e9..00000000000
--- a/spec/frontend/cycle_analytics/total_time_spec.js
+++ /dev/null
@@ -1,45 +0,0 @@
-import { mount } from '@vue/test-utils';
-import TotalTime from '~/cycle_analytics/components/total_time.vue';
-
-describe('TotalTime', () => {
- let wrapper = null;
-
- const createComponent = (propsData) => {
- return mount(TotalTime, {
- propsData,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('with a valid time object', () => {
- it.each`
- time
- ${{ seconds: 35 }}
- ${{ mins: 47, seconds: 3 }}
- ${{ days: 3, mins: 47, seconds: 3 }}
- ${{ hours: 23, mins: 10 }}
- ${{ hours: 7, mins: 20, seconds: 10 }}
- `('with $time', ({ time }) => {
- wrapper = createComponent({
- time,
- });
-
- expect(wrapper.html()).toMatchSnapshot();
- });
- });
-
- describe('with a blank object', () => {
- beforeEach(() => {
- wrapper = createComponent({
- time: {},
- });
- });
-
- it('should render --', () => {
- expect(wrapper.html()).toMatchSnapshot();
- });
- });
-});
diff --git a/spec/frontend/cycle_analytics/utils_spec.js b/spec/frontend/cycle_analytics/utils_spec.js
deleted file mode 100644
index 51405a1ba4d..00000000000
--- a/spec/frontend/cycle_analytics/utils_spec.js
+++ /dev/null
@@ -1,171 +0,0 @@
-import {
- transformStagesForPathNavigation,
- medianTimeToParsedSeconds,
- formatMedianValues,
- filterStagesByHiddenStatus,
- buildCycleAnalyticsInitialData,
-} from '~/cycle_analytics/utils';
-import {
- selectedStage,
- allowedStages,
- stageMedians,
- pathNavIssueMetric,
- rawStageMedians,
-} from './mock_data';
-
-describe('Value stream analytics utils', () => {
- describe('transformStagesForPathNavigation', () => {
- const stages = allowedStages;
- const response = transformStagesForPathNavigation({
- stages,
- medians: stageMedians,
- selectedStage,
- });
-
- describe('transforms the data as expected', () => {
- it('returns an array of stages', () => {
- expect(Array.isArray(response)).toBe(true);
- expect(response.length).toBe(stages.length);
- });
-
- it('selects the correct stage', () => {
- const selected = response.filter((stage) => stage.selected === true)[0];
-
- expect(selected.title).toBe(selectedStage.title);
- });
-
- it('includes the correct metric for the associated stage', () => {
- const issue = response.filter((stage) => stage.name === 'issue')[0];
-
- expect(issue.metric).toBe(pathNavIssueMetric);
- });
- });
- });
-
- describe('medianTimeToParsedSeconds', () => {
- it.each`
- value | result
- ${1036800} | ${'1w'}
- ${259200} | ${'3d'}
- ${172800} | ${'2d'}
- ${86400} | ${'1d'}
- ${1000} | ${'16m'}
- ${61} | ${'1m'}
- ${59} | ${'<1m'}
- ${0} | ${'-'}
- `('will correctly parse $value seconds into $result', ({ value, result }) => {
- expect(medianTimeToParsedSeconds(value)).toBe(result);
- });
- });
-
- describe('formatMedianValues', () => {
- const calculatedMedians = formatMedianValues(rawStageMedians);
-
- it('returns an object with each stage and their median formatted for display', () => {
- rawStageMedians.forEach(({ id, value }) => {
- expect(calculatedMedians).toMatchObject({ [id]: medianTimeToParsedSeconds(value) });
- });
- });
- });
-
- describe('filterStagesByHiddenStatus', () => {
- const hiddenStages = [{ title: 'three', hidden: true }];
- const visibleStages = [
- { title: 'one', hidden: false },
- { title: 'two', hidden: false },
- ];
- const mockStages = [...visibleStages, ...hiddenStages];
-
- it.each`
- isHidden | result
- ${false} | ${visibleStages}
- ${undefined} | ${hiddenStages}
- ${true} | ${hiddenStages}
- `('with isHidden=$isHidden returns matching stages', ({ isHidden, result }) => {
- expect(filterStagesByHiddenStatus(mockStages, isHidden)).toEqual(result);
- });
- });
-
- describe('buildCycleAnalyticsInitialData', () => {
- let res = null;
- const projectId = '5';
- const createdAfter = '2021-09-01';
- const createdBefore = '2021-11-06';
- const groupId = '146';
- const groupPath = 'fake-group';
- const fullPath = 'fake-group/fake-project';
- const labelsPath = '/fake-group/fake-project/-/labels.json';
- const milestonesPath = '/fake-group/fake-project/-/milestones.json';
- const requestPath = '/fake-group/fake-project/-/value_stream_analytics';
-
- const rawData = {
- projectId,
- createdBefore,
- createdAfter,
- fullPath,
- requestPath,
- labelsPath,
- milestonesPath,
- groupId,
- groupPath,
- };
-
- describe('with minimal data', () => {
- beforeEach(() => {
- res = buildCycleAnalyticsInitialData(rawData);
- });
-
- it('sets the projectId', () => {
- expect(res.projectId).toBe(parseInt(projectId, 10));
- });
-
- it('sets the date range', () => {
- expect(res.createdBefore).toEqual(new Date(createdBefore));
- expect(res.createdAfter).toEqual(new Date(createdAfter));
- });
-
- it('sets the endpoints', () => {
- const { endpoints } = res;
- expect(endpoints.fullPath).toBe(fullPath);
- expect(endpoints.requestPath).toBe(requestPath);
- expect(endpoints.labelsPath).toBe(labelsPath);
- expect(endpoints.milestonesPath).toBe(milestonesPath);
- expect(endpoints.groupId).toBe(parseInt(groupId, 10));
- expect(endpoints.groupPath).toBe(groupPath);
- });
-
- it('returns null when there is no stage', () => {
- expect(res.selectedStage).toBeNull();
- });
-
- it('returns false for missing features', () => {
- expect(res.features.cycleAnalyticsForGroups).toBe(false);
- });
- });
-
- describe('with a stage set', () => {
- const jsonStage = '{"id":"fakeStage","title":"fakeStage"}';
-
- it('parses the selectedStage data', () => {
- res = buildCycleAnalyticsInitialData({ ...rawData, stage: jsonStage });
-
- const { selectedStage: stage } = res;
-
- expect(stage.id).toBe('fakeStage');
- expect(stage.title).toBe('fakeStage');
- });
- });
-
- describe('with features set', () => {
- const fakeFeatures = { cycleAnalyticsForGroups: true };
-
- it('sets the feature flags', () => {
- res = buildCycleAnalyticsInitialData({
- ...rawData,
- gon: { licensed_features: fakeFeatures },
- });
- expect(res.features).toEqual(fakeFeatures);
- });
- });
- });
-});
diff --git a/spec/frontend/cycle_analytics/value_stream_filters_spec.js b/spec/frontend/cycle_analytics/value_stream_filters_spec.js
deleted file mode 100644
index 6e96a6d756a..00000000000
--- a/spec/frontend/cycle_analytics/value_stream_filters_spec.js
+++ /dev/null
@@ -1,91 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import Daterange from '~/analytics/shared/components/daterange.vue';
-import ProjectsDropdownFilter from '~/analytics/shared/components/projects_dropdown_filter.vue';
-import FilterBar from '~/cycle_analytics/components/filter_bar.vue';
-import ValueStreamFilters from '~/cycle_analytics/components/value_stream_filters.vue';
-import {
- createdAfter as startDate,
- createdBefore as endDate,
- currentGroup,
- selectedProjects,
-} from './mock_data';
-
-function createComponent(props = {}) {
- return shallowMount(ValueStreamFilters, {
- propsData: {
- selectedProjects,
- groupId: currentGroup.id,
- groupPath: currentGroup.fullPath,
- startDate,
- endDate,
- ...props,
- },
- });
-}
-
-describe('ValueStreamFilters', () => {
- let wrapper;
-
- const findProjectsDropdown = () => wrapper.findComponent(ProjectsDropdownFilter);
- const findDateRangePicker = () => wrapper.findComponent(Daterange);
- const findFilterBar = () => wrapper.findComponent(FilterBar);
-
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- it('will render the filter bar', () => {
- expect(findFilterBar().exists()).toBe(true);
- });
-
- it('will render the projects dropdown', () => {
- expect(findProjectsDropdown().exists()).toBe(true);
- expect(wrapper.findComponent(ProjectsDropdownFilter).props()).toEqual(
- expect.objectContaining({
- queryParams: wrapper.vm.projectsQueryParams,
- multiSelect: wrapper.vm.$options.multiProjectSelect,
- }),
- );
- });
-
- it('will render the date range picker', () => {
- expect(findDateRangePicker().exists()).toBe(true);
- });
-
- it('will emit `selectProject` when a project is selected', () => {
- findProjectsDropdown().vm.$emit('selected');
-
- expect(wrapper.emitted('selectProject')).not.toBeUndefined();
- });
-
- it('will emit `setDateRange` when the date range changes', () => {
- findDateRangePicker().vm.$emit('change');
-
- expect(wrapper.emitted('setDateRange')).not.toBeUndefined();
- });
-
- describe('hasDateRangeFilter = false', () => {
- beforeEach(() => {
- wrapper = createComponent({ hasDateRangeFilter: false });
- });
-
- it('will not render the date range picker', () => {
- expect(findDateRangePicker().exists()).toBe(false);
- });
- });
-
- describe('hasProjectFilter = false', () => {
- beforeEach(() => {
- wrapper = createComponent({ hasProjectFilter: false });
- });
-
- it('will not render the project dropdown', () => {
- expect(findProjectsDropdown().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/deploy_freeze/store/actions_spec.js b/spec/frontend/deploy_freeze/store/actions_spec.js
index ce0c924bed2..9b96ce5d252 100644
--- a/spec/frontend/deploy_freeze/store/actions_spec.js
+++ b/spec/frontend/deploy_freeze/store/actions_spec.js
@@ -4,7 +4,7 @@ import Api from '~/api';
import * as actions from '~/deploy_freeze/store/actions';
import * as types from '~/deploy_freeze/store/mutation_types';
import getInitialState from '~/deploy_freeze/store/state';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import * as logger from '~/lib/logger';
import axios from '~/lib/utils/axios_utils';
import { freezePeriodsFixture } from '../helpers';
@@ -99,8 +99,8 @@ describe('deploy freeze store actions', () => {
});
describe('addFreezePeriod', () => {
- it('dispatch correct actions on adding a freeze period', () => {
- testAction(
+ it('dispatch correct actions on adding a freeze period', async () => {
+ await testAction(
actions.addFreezePeriod,
{},
state,
@@ -110,32 +110,33 @@ describe('deploy freeze store actions', () => {
{ type: 'receiveFreezePeriodSuccess' },
{ type: 'fetchFreezePeriods' },
],
- () =>
- expect(Api.createFreezePeriod).toHaveBeenCalledWith(state.projectId, {
- freeze_start: state.freezeStartCron,
- freeze_end: state.freezeEndCron,
- cron_timezone: state.selectedTimezoneIdentifier,
- }),
);
+
+ expect(Api.createFreezePeriod).toHaveBeenCalledWith(state.projectId, {
+ freeze_start: state.freezeStartCron,
+ freeze_end: state.freezeEndCron,
+ cron_timezone: state.selectedTimezoneIdentifier,
+ });
});
- it('should show flash error and set error in state on add failure', () => {
+ it('should show alert and set error in state on add failure', async () => {
Api.createFreezePeriod.mockRejectedValue();
- testAction(
+ await testAction(
actions.addFreezePeriod,
{},
state,
[],
[{ type: 'requestFreezePeriod' }, { type: 'receiveFreezePeriodError' }],
- () => expect(createFlash).toHaveBeenCalled(),
);
+
+ expect(createAlert).toHaveBeenCalled();
});
});
describe('updateFreezePeriod', () => {
- it('dispatch correct actions on updating a freeze period', () => {
- testAction(
+ it('dispatch correct actions on updating a freeze period', async () => {
+ await testAction(
actions.updateFreezePeriod,
{},
state,
@@ -145,33 +146,34 @@ describe('deploy freeze store actions', () => {
{ type: 'receiveFreezePeriodSuccess' },
{ type: 'fetchFreezePeriods' },
],
- () =>
- expect(Api.updateFreezePeriod).toHaveBeenCalledWith(state.projectId, {
- id: state.selectedId,
- freeze_start: state.freezeStartCron,
- freeze_end: state.freezeEndCron,
- cron_timezone: state.selectedTimezoneIdentifier,
- }),
);
+
+ expect(Api.updateFreezePeriod).toHaveBeenCalledWith(state.projectId, {
+ id: state.selectedId,
+ freeze_start: state.freezeStartCron,
+ freeze_end: state.freezeEndCron,
+ cron_timezone: state.selectedTimezoneIdentifier,
+ });
});
- it('should show flash error and set error in state on add failure', () => {
+ it('should show alert and set error in state on add failure', async () => {
Api.updateFreezePeriod.mockRejectedValue();
- testAction(
+ await testAction(
actions.updateFreezePeriod,
{},
state,
[],
[{ type: 'requestFreezePeriod' }, { type: 'receiveFreezePeriodError' }],
- () => expect(createFlash).toHaveBeenCalled(),
);
+
+ expect(createAlert).toHaveBeenCalled();
});
});
describe('fetchFreezePeriods', () => {
it('dispatch correct actions on fetchFreezePeriods', () => {
- testAction(
+ return testAction(
actions.fetchFreezePeriods,
{},
state,
@@ -183,26 +185,26 @@ describe('deploy freeze store actions', () => {
);
});
- it('should show flash error and set error in state on fetch variables failure', () => {
+ it('should show alert and set error in state on fetch variables failure', async () => {
Api.freezePeriods.mockRejectedValue();
- testAction(
+ await testAction(
actions.fetchFreezePeriods,
{},
state,
[{ type: types.REQUEST_FREEZE_PERIODS }],
[],
- () =>
- expect(createFlash).toHaveBeenCalledWith({
- message: 'There was an error fetching the deploy freezes.',
- }),
);
+
+ expect(createAlert).toHaveBeenCalledWith({
+ message: 'There was an error fetching the deploy freezes.',
+ });
});
});
describe('deleteFreezePeriod', () => {
- it('dispatch correct actions on deleting a freeze period', () => {
- testAction(
+ it('dispatch correct actions on deleting a freeze period', async () => {
+ await testAction(
actions.deleteFreezePeriod,
freezePeriodFixture,
state,
@@ -211,20 +213,17 @@ describe('deploy freeze store actions', () => {
{ type: 'RECEIVE_DELETE_FREEZE_PERIOD_SUCCESS', payload: freezePeriodFixture.id },
],
[],
- () =>
- expect(Api.deleteFreezePeriod).toHaveBeenCalledWith(
- state.projectId,
- freezePeriodFixture.id,
- ),
);
+
+ expect(Api.deleteFreezePeriod).toHaveBeenCalledWith(state.projectId, freezePeriodFixture.id);
});
- it('should show flash error and set error in state on delete failure', () => {
+ it('should show alert and set error in state on delete failure', async () => {
jest.spyOn(logger, 'logError').mockImplementation();
const error = new Error();
Api.deleteFreezePeriod.mockRejectedValue(error);
- testAction(
+ await testAction(
actions.deleteFreezePeriod,
freezePeriodFixture,
state,
@@ -233,12 +232,11 @@ describe('deploy freeze store actions', () => {
{ type: 'RECEIVE_DELETE_FREEZE_PERIOD_ERROR', payload: freezePeriodFixture.id },
],
[],
- () => {
- expect(createFlash).toHaveBeenCalled();
-
- expect(logger.logError).toHaveBeenCalledWith('Unable to delete deploy freeze', error);
- },
);
+
+ expect(createAlert).toHaveBeenCalled();
+
+ expect(logger.logError).toHaveBeenCalledWith('Unable to delete deploy freeze', error);
});
});
});
diff --git a/spec/frontend/deploy_tokens/components/new_deploy_token_spec.js b/spec/frontend/deploy_tokens/components/new_deploy_token_spec.js
index 990f18d64c1..0bf69acd251 100644
--- a/spec/frontend/deploy_tokens/components/new_deploy_token_spec.js
+++ b/spec/frontend/deploy_tokens/components/new_deploy_token_spec.js
@@ -6,9 +6,21 @@ import axios from '~/lib/utils/axios_utils';
import { TEST_HOST } from 'helpers/test_constants';
import NewDeployToken from '~/deploy_tokens/components/new_deploy_token.vue';
import waitForPromises from 'helpers/wait_for_promises';
+import { createAlert, VARIANT_INFO } from '~/flash';
const createNewTokenPath = `${TEST_HOST}/create`;
const deployTokensHelpUrl = `${TEST_HOST}/help`;
+
+jest.mock('~/flash', () => {
+ const original = jest.requireActual('~/flash');
+
+ return {
+ __esModule: true,
+ ...original,
+ createAlert: jest.fn(),
+ };
+});
+
describe('New Deploy Token', () => {
let wrapper;
@@ -69,9 +81,69 @@ describe('New Deploy Token', () => {
expect(tokenUsername.props('value')).toBe('test token username');
expect(tokenValue.props('value')).toBe('test token');
+
+ expect(createAlert).toHaveBeenCalledWith(
+ expect.objectContaining({
+ variant: VARIANT_INFO,
+ }),
+ );
});
}
+ it('should flash error message if token creation fails', async () => {
+ const mockAxios = new MockAdapter(axios);
+
+ const date = new Date();
+ const formInputs = wrapper.findAllComponents(GlFormInput);
+ const name = formInputs.at(0);
+ const username = formInputs.at(2);
+ name.vm.$emit('input', 'test name');
+ username.vm.$emit('input', 'test username');
+
+ const datepicker = wrapper.findAllComponents(GlDatepicker).at(0);
+ datepicker.vm.$emit('input', date);
+
+ const [
+ readRepo,
+ readRegistry,
+ writeRegistry,
+ readPackageRegistry,
+ writePackageRegistry,
+ ] = wrapper.findAllComponents(GlFormCheckbox).wrappers;
+ readRepo.vm.$emit('input', true);
+ readRegistry.vm.$emit('input', true);
+ writeRegistry.vm.$emit('input', true);
+ readPackageRegistry.vm.$emit('input', true);
+ writePackageRegistry.vm.$emit('input', true);
+
+ const expectedErrorMessage = 'Server error while creating a token';
+
+ mockAxios
+ .onPost(createNewTokenPath, {
+ deploy_token: {
+ name: 'test name',
+ expires_at: date.toISOString(),
+ username: 'test username',
+ read_repository: true,
+ read_registry: true,
+ write_registry: true,
+ read_package_registry: true,
+ write_package_registry: true,
+ },
+ })
+ .replyOnce(500, { message: expectedErrorMessage });
+
+ wrapper.findAllComponents(GlButton).at(0).vm.$emit('click');
+
+ await waitForPromises().then(() => nextTick());
+
+ expect(createAlert).toHaveBeenCalledWith(
+ expect.objectContaining({
+ message: expectedErrorMessage,
+ }),
+ );
+ });
+
it('should make a request to create a token on submit', () => {
const mockAxios = new MockAdapter(axios);
diff --git a/spec/frontend/design_management/components/design_todo_button_spec.js b/spec/frontend/design_management/components/design_todo_button_spec.js
index b3afcefe1ed..ac26873b692 100644
--- a/spec/frontend/design_management/components/design_todo_button_spec.js
+++ b/spec/frontend/design_management/components/design_todo_button_spec.js
@@ -3,7 +3,7 @@ import { nextTick } from 'vue';
import DesignTodoButton from '~/design_management/components/design_todo_button.vue';
import createDesignTodoMutation from '~/design_management/graphql/mutations/create_design_todo.mutation.graphql';
import todoMarkDoneMutation from '~/graphql_shared/mutations/todo_mark_done.mutation.graphql';
-import TodoButton from '~/vue_shared/components/sidebar/todo_toggle/todo_button.vue';
+import TodoButton from '~/sidebar/components/todo_toggle/todo_button.vue';
import mockDesign from '../mock_data/design';
const mockDesignWithPendingTodos = {
diff --git a/spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap b/spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap
index 2b706d21f51..1acbf14db88 100644
--- a/spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap
+++ b/spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap
@@ -1,163 +1,229 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Design management design version dropdown component renders design version dropdown button 1`] = `
-<gl-dropdown-stub
+<gl-base-dropdown-stub
+ ariahaspopup="listbox"
category="primary"
- clearalltext="Clear all"
- clearalltextclass="gl-px-5"
- headertext=""
- hideheaderborder="true"
- highlighteditemstitle="Selected"
- highlighteditemstitleclass="gl-px-5"
+ icon=""
issueiid=""
projectpath=""
size="small"
- text="Showing latest version"
+ toggleid="dropdown-toggle-btn-2"
+ toggletext="Showing latest version"
variant="default"
>
- <gl-dropdown-item-stub
- avatarurl=""
- iconcolor=""
- iconname=""
- iconrightarialabel=""
- iconrightname=""
- ischeckcentered="true"
- ischecked="true"
- ischeckitem="true"
- secondarytext=""
+ <!---->
+
+ <!---->
+
+ <ul
+ aria-labelledby="dropdown-toggle-btn-2"
+ class="gl-dropdown-contents gl-list-style-none gl-pl-0 gl-mb-0"
+ id="listbox"
+ role="listbox"
+ tabindex="-1"
>
- <strong>
- Version
- 2
- (latest)
- </strong>
-
- <div
- class="gl-text-gray-600 gl-mt-1"
+ <gl-listbox-item-stub
+ ischeckcentered="true"
>
- <div>
- Adminstrator
- </div>
-
- <time-ago-stub
- class="text-1"
- cssclass=""
- time="2021-08-09T06:05:00Z"
- tooltipplacement="bottom"
- />
- </div>
- </gl-dropdown-item-stub>
- <gl-dropdown-item-stub
- avatarurl=""
- iconcolor=""
- iconname=""
- iconrightarialabel=""
- iconrightname=""
- ischeckcentered="true"
- ischeckitem="true"
- secondarytext=""
- >
- <strong>
- Version
- 1
-
- </strong>
-
- <div
- class="gl-text-gray-600 gl-mt-1"
+ <span
+ class="gl-display-flex gl-align-items-center"
+ >
+ <div
+ class="gl-avatar gl-avatar-identicon gl-avatar-circle gl-avatar-s32 gl-avatar-identicon-bg1"
+ >
+
+
+
+ </div>
+
+ <span
+ class="gl-display-flex gl-flex-direction-column"
+ >
+ <span
+ class="gl-font-weight-bold"
+ >
+ Version 2 (latest)
+ </span>
+
+ <span
+ class="gl-text-gray-600 gl-mt-1"
+ >
+ <span
+ class="gl-display-block"
+ >
+ Adminstrator
+ </span>
+
+ <time-ago-stub
+ class="text-1"
+ cssclass=""
+ time="2021-08-09T06:05:00Z"
+ tooltipplacement="bottom"
+ />
+ </span>
+ </span>
+ </span>
+ </gl-listbox-item-stub>
+ <gl-listbox-item-stub
+ ischeckcentered="true"
>
- <div>
- Adminstrator
- </div>
-
- <time-ago-stub
- class="text-1"
- cssclass=""
- time="2021-08-09T06:05:00Z"
- tooltipplacement="bottom"
- />
- </div>
- </gl-dropdown-item-stub>
-</gl-dropdown-stub>
+ <span
+ class="gl-display-flex gl-align-items-center"
+ >
+ <div
+ class="gl-avatar gl-avatar-identicon gl-avatar-circle gl-avatar-s32 gl-avatar-identicon-bg1"
+ >
+
+
+
+ </div>
+
+ <span
+ class="gl-display-flex gl-flex-direction-column"
+ >
+ <span
+ class="gl-font-weight-bold"
+ >
+ Version 1
+ </span>
+
+ <span
+ class="gl-text-gray-600 gl-mt-1"
+ >
+ <span
+ class="gl-display-block"
+ >
+ Adminstrator
+ </span>
+
+ <time-ago-stub
+ class="text-1"
+ cssclass=""
+ time="2021-08-09T06:05:00Z"
+ tooltipplacement="bottom"
+ />
+ </span>
+ </span>
+ </span>
+ </gl-listbox-item-stub>
+ </ul>
+
+ <!---->
+
+</gl-base-dropdown-stub>
`;
exports[`Design management design version dropdown component renders design version list 1`] = `
-<gl-dropdown-stub
+<gl-base-dropdown-stub
+ ariahaspopup="listbox"
category="primary"
- clearalltext="Clear all"
- clearalltextclass="gl-px-5"
- headertext=""
- hideheaderborder="true"
- highlighteditemstitle="Selected"
- highlighteditemstitleclass="gl-px-5"
+ icon=""
issueiid=""
projectpath=""
size="small"
- text="Showing latest version"
+ toggleid="dropdown-toggle-btn-4"
+ toggletext="Showing latest version"
variant="default"
>
- <gl-dropdown-item-stub
- avatarurl=""
- iconcolor=""
- iconname=""
- iconrightarialabel=""
- iconrightname=""
- ischeckcentered="true"
- ischecked="true"
- ischeckitem="true"
- secondarytext=""
+ <!---->
+
+ <!---->
+
+ <ul
+ aria-labelledby="dropdown-toggle-btn-4"
+ class="gl-dropdown-contents gl-list-style-none gl-pl-0 gl-mb-0"
+ id="listbox"
+ role="listbox"
+ tabindex="-1"
>
- <strong>
- Version
- 2
- (latest)
- </strong>
-
- <div
- class="gl-text-gray-600 gl-mt-1"
+ <gl-listbox-item-stub
+ ischeckcentered="true"
>
- <div>
- Adminstrator
- </div>
-
- <time-ago-stub
- class="text-1"
- cssclass=""
- time="2021-08-09T06:05:00Z"
- tooltipplacement="bottom"
- />
- </div>
- </gl-dropdown-item-stub>
- <gl-dropdown-item-stub
- avatarurl=""
- iconcolor=""
- iconname=""
- iconrightarialabel=""
- iconrightname=""
- ischeckcentered="true"
- ischeckitem="true"
- secondarytext=""
- >
- <strong>
- Version
- 1
-
- </strong>
-
- <div
- class="gl-text-gray-600 gl-mt-1"
+ <span
+ class="gl-display-flex gl-align-items-center"
+ >
+ <div
+ class="gl-avatar gl-avatar-identicon gl-avatar-circle gl-avatar-s32 gl-avatar-identicon-bg1"
+ >
+
+
+
+ </div>
+
+ <span
+ class="gl-display-flex gl-flex-direction-column"
+ >
+ <span
+ class="gl-font-weight-bold"
+ >
+ Version 2 (latest)
+ </span>
+
+ <span
+ class="gl-text-gray-600 gl-mt-1"
+ >
+ <span
+ class="gl-display-block"
+ >
+ Adminstrator
+ </span>
+
+ <time-ago-stub
+ class="text-1"
+ cssclass=""
+ time="2021-08-09T06:05:00Z"
+ tooltipplacement="bottom"
+ />
+ </span>
+ </span>
+ </span>
+ </gl-listbox-item-stub>
+ <gl-listbox-item-stub
+ ischeckcentered="true"
>
- <div>
- Adminstrator
- </div>
-
- <time-ago-stub
- class="text-1"
- cssclass=""
- time="2021-08-09T06:05:00Z"
- tooltipplacement="bottom"
- />
- </div>
- </gl-dropdown-item-stub>
-</gl-dropdown-stub>
+ <span
+ class="gl-display-flex gl-align-items-center"
+ >
+ <div
+ class="gl-avatar gl-avatar-identicon gl-avatar-circle gl-avatar-s32 gl-avatar-identicon-bg1"
+ >
+
+
+
+ </div>
+
+ <span
+ class="gl-display-flex gl-flex-direction-column"
+ >
+ <span
+ class="gl-font-weight-bold"
+ >
+ Version 1
+ </span>
+
+ <span
+ class="gl-text-gray-600 gl-mt-1"
+ >
+ <span
+ class="gl-display-block"
+ >
+ Adminstrator
+ </span>
+
+ <time-ago-stub
+ class="text-1"
+ cssclass=""
+ time="2021-08-09T06:05:00Z"
+ tooltipplacement="bottom"
+ />
+ </span>
+ </span>
+ </span>
+ </gl-listbox-item-stub>
+ </ul>
+
+ <!---->
+
+</gl-base-dropdown-stub>
`;
diff --git a/spec/frontend/design_management/components/upload/design_version_dropdown_spec.js b/spec/frontend/design_management/components/upload/design_version_dropdown_spec.js
index 7c26ab9739b..1e9f286a0ec 100644
--- a/spec/frontend/design_management/components/upload/design_version_dropdown_spec.js
+++ b/spec/frontend/design_management/components/upload/design_version_dropdown_spec.js
@@ -1,4 +1,4 @@
-import { GlDropdown, GlDropdownItem, GlSprintf } from '@gitlab/ui';
+import { GlAvatar, GlCollapsibleListbox, GlListboxItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import DesignVersionDropdown from '~/design_management/components/upload/design_version_dropdown.vue';
@@ -32,7 +32,7 @@ describe('Design management design version dropdown component', () => {
mocks: {
$route,
},
- stubs: { GlSprintf },
+ stubs: { GlAvatar, GlCollapsibleListbox },
});
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
@@ -46,7 +46,9 @@ describe('Design management design version dropdown component', () => {
wrapper.destroy();
});
- const findVersionLink = (index) => wrapper.findAllComponents(GlDropdownItem).at(index);
+ const findListbox = () => wrapper.findComponent(GlCollapsibleListbox);
+ const findAllListboxItems = () => wrapper.findAllComponents(GlListboxItem);
+ const findVersionLink = (index) => wrapper.findAllComponents(GlListboxItem).at(index);
it('renders design version dropdown button', async () => {
createComponent();
@@ -76,35 +78,36 @@ describe('Design management design version dropdown component', () => {
createComponent();
await nextTick();
- expect(wrapper.findComponent(GlDropdown).attributes('text')).toBe('Showing latest version');
+
+ expect(findListbox().props('toggleText')).toBe('Showing latest version');
});
it('displays latest version text when only 1 version is present', async () => {
createComponent({ maxVersions: 1 });
await nextTick();
- expect(wrapper.findComponent(GlDropdown).attributes('text')).toBe('Showing latest version');
+ expect(findListbox().props('toggleText')).toBe('Showing latest version');
});
it('displays version text when the current version is not the latest', async () => {
createComponent({ $route: designRouteFactory(PREVIOUS_VERSION_ID) });
await nextTick();
- expect(wrapper.findComponent(GlDropdown).attributes('text')).toBe(`Showing version #1`);
+ expect(findListbox().props('toggleText')).toBe(`Showing version #1`);
});
it('displays latest version text when the current version is the latest', async () => {
createComponent({ $route: designRouteFactory(LATEST_VERSION_ID) });
await nextTick();
- expect(wrapper.findComponent(GlDropdown).attributes('text')).toBe('Showing latest version');
+ expect(findListbox().props('toggleText')).toBe('Showing latest version');
});
it('should have the same length as apollo query', async () => {
createComponent();
await nextTick();
- expect(wrapper.findAllComponents(GlDropdownItem)).toHaveLength(wrapper.vm.allVersions.length);
+ expect(findAllListboxItems()).toHaveLength(wrapper.vm.allVersions.length);
});
it('should render TimeAgo', async () => {
diff --git a/spec/frontend/diffs/components/diff_code_quality_spec.js b/spec/frontend/diffs/components/diff_code_quality_spec.js
index b5dce4fc924..7bd9afab648 100644
--- a/spec/frontend/diffs/components/diff_code_quality_spec.js
+++ b/spec/frontend/diffs/components/diff_code_quality_spec.js
@@ -1,12 +1,14 @@
import { GlIcon } from '@gitlab/ui';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import DiffCodeQuality from '~/diffs/components/diff_code_quality.vue';
-import { SEVERITY_CLASSES, SEVERITY_ICONS } from '~/reports/codequality_report/constants';
+import { SEVERITY_CLASSES, SEVERITY_ICONS } from '~/ci/reports/codequality_report/constants';
+import { NEW_CODE_QUALITY_FINDINGS } from '~/diffs/i18n';
import { multipleFindingsArr } from '../mock_data/diff_code_quality';
let wrapper;
const findIcon = () => wrapper.findComponent(GlIcon);
+const findHeading = () => wrapper.findByTestId(`diff-codequality-findings-heading`);
describe('DiffCodeQuality', () => {
afterEach(() => {
@@ -30,14 +32,17 @@ describe('DiffCodeQuality', () => {
expect(wrapper.emitted('hideCodeQualityFindings').length).toBe(1);
});
- it('renders correct amount of list items for codequality array and their description', async () => {
+ it('renders heading and correct amount of list items for codequality array and their description', async () => {
wrapper = createWrapper(multipleFindingsArr);
- const listItems = wrapper.findAll('li');
+ expect(findHeading().text()).toEqual(NEW_CODE_QUALITY_FINDINGS);
- expect(wrapper.findAll('li').length).toBe(3);
+ const listItems = wrapper.findAll('li');
+ expect(wrapper.findAll('li').length).toBe(5);
listItems.wrappers.map((e, i) => {
- return expect(e.text()).toEqual(multipleFindingsArr[i].description);
+ return expect(e.text()).toContain(
+ `${multipleFindingsArr[i].severity} - ${multipleFindingsArr[i].description}`,
+ );
});
});
diff --git a/spec/frontend/diffs/components/diff_discussion_reply_spec.js b/spec/frontend/diffs/components/diff_discussion_reply_spec.js
index 5ccd2002462..bf4a1a1c1f7 100644
--- a/spec/frontend/diffs/components/diff_discussion_reply_spec.js
+++ b/spec/frontend/diffs/components/diff_discussion_reply_spec.js
@@ -1,10 +1,12 @@
import { shallowMount } from '@vue/test-utils';
+import { GlButton } from '@gitlab/ui';
import Vue from 'vue';
import Vuex from 'vuex';
import DiffDiscussionReply from '~/diffs/components/diff_discussion_reply.vue';
-import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue';
import NoteSignedOutWidget from '~/notes/components/note_signed_out_widget.vue';
+import { START_THREAD } from '~/diffs/i18n';
+
Vue.use(Vuex);
describe('DiffDiscussionReply', () => {
@@ -58,14 +60,42 @@ describe('DiffDiscussionReply', () => {
expect(wrapper.find('#test-form').exists()).toBe(true);
});
- it('should render a reply placeholder if there is no form', () => {
+ it('should render a reply placeholder button if there is no form', () => {
createComponent({
renderReplyPlaceholder: true,
hasForm: false,
});
- expect(wrapper.findComponent(ReplyPlaceholder).exists()).toBe(true);
+ expect(wrapper.findComponent(GlButton).text()).toBe(START_THREAD);
});
+
+ it.each`
+ userCanReply | hasForm | renderReplyPlaceholder | showButton
+ ${false} | ${false} | ${false} | ${false}
+ ${true} | ${false} | ${false} | ${false}
+ ${true} | ${true} | ${false} | ${false}
+ ${true} | ${true} | ${true} | ${false}
+ ${true} | ${false} | ${true} | ${true}
+ ${false} | ${false} | ${true} | ${false}
+ `(
+ 'reply button existence is `$showButton` when userCanReply is `$userCanReply`, hasForm is `$hasForm` and renderReplyPlaceholder is `$renderReplyPlaceholder`',
+ ({ userCanReply, hasForm, renderReplyPlaceholder, showButton }) => {
+ getters = {
+ userCanReply: () => userCanReply,
+ };
+
+ store = new Vuex.Store({
+ getters,
+ });
+
+ createComponent({
+ renderReplyPlaceholder,
+ hasForm,
+ });
+
+ expect(wrapper.findComponent(GlButton).exists()).toBe(showButton);
+ },
+ );
});
it('renders a signed out widget when user is not logged in', () => {
diff --git a/spec/frontend/diffs/components/diff_discussions_spec.js b/spec/frontend/diffs/components/diff_discussions_spec.js
index e9a0e0745fd..5092ae6ab6e 100644
--- a/spec/frontend/diffs/components/diff_discussions_spec.js
+++ b/spec/frontend/diffs/components/diff_discussions_spec.js
@@ -5,9 +5,10 @@ import { createStore } from '~/mr_notes/stores';
import DiscussionNotes from '~/notes/components/discussion_notes.vue';
import NoteableDiscussion from '~/notes/components/noteable_discussion.vue';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
-import '~/behaviors/markdown/render_gfm';
import discussionsMockData from '../mock_data/diff_discussions';
+jest.mock('~/behaviors/markdown/render_gfm');
+
describe('DiffDiscussions', () => {
let store;
let wrapper;
diff --git a/spec/frontend/diffs/mock_data/diff_code_quality.js b/spec/frontend/diffs/mock_data/diff_code_quality.js
index befab3b676b..7558592f6a4 100644
--- a/spec/frontend/diffs/mock_data/diff_code_quality.js
+++ b/spec/frontend/diffs/mock_data/diff_code_quality.js
@@ -1,25 +1,39 @@
export const multipleFindingsArr = [
{
severity: 'minor',
- description: 'Unexpected Debugger Statement.',
+ description: 'mocked minor Issue',
line: 2,
},
{
severity: 'major',
- description:
- 'Function `aVeryLongFunction` has 52 lines of code (exceeds 25 allowed). Consider refactoring.',
+ description: 'mocked major Issue',
line: 3,
},
{
- severity: 'minor',
- description: 'Arrow function has too many statements (52). Maximum allowed is 30.',
+ severity: 'info',
+ description: 'mocked info Issue',
+ line: 3,
+ },
+ {
+ severity: 'critical',
+ description: 'mocked critical Issue',
+ line: 3,
+ },
+ {
+ severity: 'blocker',
+ description: 'mocked blocker Issue',
line: 3,
},
];
-export const multipleFindings = {
+export const fiveFindings = {
+ filePath: 'index.js',
+ codequality: multipleFindingsArr.slice(0, 5),
+};
+
+export const threeFindings = {
filePath: 'index.js',
- codequality: multipleFindingsArr,
+ codequality: multipleFindingsArr.slice(0, 3),
};
export const singularFinding = {
diff --git a/spec/frontend/diffs/store/actions_spec.js b/spec/frontend/diffs/store/actions_spec.js
index 87366cdbfc5..9e0ffbf757f 100644
--- a/spec/frontend/diffs/store/actions_spec.js
+++ b/spec/frontend/diffs/store/actions_spec.js
@@ -606,6 +606,50 @@ describe('DiffsStoreActions', () => {
params: { commit_id: '123', w: '0' },
});
});
+
+ describe('version parameters', () => {
+ const diffId = '4';
+ const startSha = 'abc';
+ const pathRoot = 'a/a/-/merge_requests/1';
+ let file;
+ let getters;
+
+ beforeAll(() => {
+ file = { load_collapsed_diff_url: '/load/collapsed/diff/url' };
+ getters = {};
+ });
+
+ beforeEach(() => {
+ jest.spyOn(axios, 'get').mockReturnValue(Promise.resolve({ data: {} }));
+ });
+
+ it('fetches the data when there is no mergeRequestDiff', () => {
+ diffActions.loadCollapsedDiff({ commit() {}, getters, state }, file);
+
+ expect(axios.get).toHaveBeenCalledWith(file.load_collapsed_diff_url, {
+ params: expect.any(Object),
+ });
+ });
+
+ it.each`
+ desc | versionPath | start_sha | diff_id
+ ${'no additional version information'} | ${`${pathRoot}?search=terms`} | ${undefined} | ${undefined}
+ ${'the diff_id'} | ${`${pathRoot}?diff_id=${diffId}`} | ${undefined} | ${diffId}
+ ${'the start_sha'} | ${`${pathRoot}?start_sha=${startSha}`} | ${startSha} | ${undefined}
+ ${'all available version information'} | ${`${pathRoot}?diff_id=${diffId}&start_sha=${startSha}`} | ${startSha} | ${diffId}
+ `('fetches the data and includes $desc', ({ versionPath, start_sha, diff_id }) => {
+ jest.spyOn(axios, 'get').mockReturnValue(Promise.resolve({ data: {} }));
+
+ diffActions.loadCollapsedDiff(
+ { commit() {}, getters, state: { mergeRequestDiff: { version_path: versionPath } } },
+ file,
+ );
+
+ expect(axios.get).toHaveBeenCalledWith(file.load_collapsed_diff_url, {
+ params: expect.objectContaining({ start_sha, diff_id }),
+ });
+ });
+ });
});
describe('toggleFileDiscussions', () => {
diff --git a/spec/frontend/diffs/utils/merge_request_spec.js b/spec/frontend/diffs/utils/merge_request_spec.js
index 8c7b1e1f2a5..c070e8c004d 100644
--- a/spec/frontend/diffs/utils/merge_request_spec.js
+++ b/spec/frontend/diffs/utils/merge_request_spec.js
@@ -2,30 +2,64 @@ import { getDerivedMergeRequestInformation } from '~/diffs/utils/merge_request';
import { diffMetadata } from '../mock_data/diff_metadata';
describe('Merge Request utilities', () => {
- const derivedMrInfo = {
+ const derivedBaseInfo = {
mrPath: '/gitlab-org/gitlab-test/-/merge_requests/4',
userOrGroup: 'gitlab-org',
project: 'gitlab-test',
id: '4',
};
+ const derivedVersionInfo = {
+ diffId: '4',
+ startSha: 'eb227b3e214624708c474bdab7bde7afc17cefcc',
+ };
+ const noVersion = {
+ diffId: undefined,
+ startSha: undefined,
+ };
const unparseableEndpoint = {
mrPath: undefined,
userOrGroup: undefined,
project: undefined,
id: undefined,
+ ...noVersion,
};
describe('getDerivedMergeRequestInformation', () => {
- const endpoint = `${diffMetadata.latest_version_path}.json?searchParam=irrelevant`;
+ let endpoint = `${diffMetadata.latest_version_path}.json?searchParam=irrelevant`;
it.each`
argument | response
- ${{ endpoint }} | ${derivedMrInfo}
+ ${{ endpoint }} | ${{ ...derivedBaseInfo, ...noVersion }}
${{}} | ${unparseableEndpoint}
${{ endpoint: undefined }} | ${unparseableEndpoint}
${{ endpoint: null }} | ${unparseableEndpoint}
`('generates the correct derived results based on $argument', ({ argument, response }) => {
expect(getDerivedMergeRequestInformation(argument)).toStrictEqual(response);
});
+
+ describe('version information', () => {
+ const bare = diffMetadata.latest_version_path;
+ endpoint = diffMetadata.merge_request_diffs[0].compare_path;
+
+ it('still gets the correct derived information', () => {
+ expect(getDerivedMergeRequestInformation({ endpoint })).toMatchObject(derivedBaseInfo);
+ });
+
+ it.each`
+ url | versionPart
+ ${endpoint} | ${derivedVersionInfo}
+ ${`${bare}?diff_id=${derivedVersionInfo.diffId}`} | ${{ ...derivedVersionInfo, startSha: undefined }}
+ ${`${bare}?start_sha=${derivedVersionInfo.startSha}`} | ${{ ...derivedVersionInfo, diffId: undefined }}
+ `(
+ 'generates the correct derived version information based on $url',
+ ({ url, versionPart }) => {
+ expect(getDerivedMergeRequestInformation({ endpoint: url })).toMatchObject(versionPart);
+ },
+ );
+
+ it('extracts nothing if there is no available version-like information in the URL', () => {
+ expect(getDerivedMergeRequestInformation({ endpoint: bare })).toMatchObject(noVersion);
+ });
+ });
});
});
diff --git a/spec/frontend/editor/components/source_editor_toolbar_button_spec.js b/spec/frontend/editor/components/source_editor_toolbar_button_spec.js
index 1475d451ab3..ff377494312 100644
--- a/spec/frontend/editor/components/source_editor_toolbar_button_spec.js
+++ b/spec/frontend/editor/components/source_editor_toolbar_button_spec.js
@@ -15,6 +15,9 @@ describe('Source Editor Toolbar button', () => {
propsData: {
...props,
},
+ stubs: {
+ GlButton,
+ },
});
};
@@ -52,9 +55,69 @@ describe('Source Editor Toolbar button', () => {
const btn = findButton();
expect(btn.props()).toMatchObject(customProps);
});
+
+ describe('CSS class', () => {
+ let blueprintClasses;
+
+ beforeEach(() => {
+ createComponent();
+ blueprintClasses = findButton().element.classList;
+ });
+
+ it.each`
+ cssClass | expectedExtraClasses
+ ${undefined} | ${['']}
+ ${''} | ${['']}
+ ${'foo'} | ${['foo']}
+ ${'foo bar'} | ${['foo', 'bar']}
+ `(
+ 'does set CSS class correctly when `class` is "$cssClass"',
+ ({ cssClass, expectedExtraClasses }) => {
+ createComponent({
+ button: {
+ ...defaultBtn,
+ class: cssClass,
+ },
+ });
+ const btn = findButton().element;
+ expectedExtraClasses.forEach((c) => {
+ if (c) {
+ expect(btn.classList.contains(c)).toBe(true);
+ } else {
+ expect(btn.classList).toEqual(blueprintClasses);
+ }
+ });
+ },
+ );
+ });
+ });
+
+ describe('data attributes', () => {
+ it.each`
+ description | data | expectedDataset
+ ${'does not set any attribute'} | ${undefined} | ${{}}
+ ${'does not set any attribute'} | ${[]} | ${{}}
+ ${'does not set any attribute'} | ${['foo']} | ${{}}
+ ${'does not set any attribute'} | ${'bar'} | ${{}}
+ ${'does set single attribute correctly'} | ${{ qaSelector: 'foo' }} | ${{ qaSelector: 'foo' }}
+ ${'does set multiple attributes correctly'} | ${{ qaSelector: 'foo', youCanSeeMe: true }} | ${{ qaSelector: 'foo', youCanSeeMe: 'true' }}
+ `('$description when data="$data"', ({ data, expectedDataset }) => {
+ createComponent({
+ button: {
+ data,
+ },
+ });
+ expect(findButton().element.dataset).toEqual(expect.objectContaining(expectedDataset));
+ });
});
describe('click handler', () => {
+ let clickEvent;
+
+ beforeEach(() => {
+ clickEvent = new Event('click');
+ });
+
it('fires the click handler on the button when available', async () => {
const spy = jest.fn();
createComponent({
@@ -63,20 +126,20 @@ describe('Source Editor Toolbar button', () => {
},
});
expect(spy).not.toHaveBeenCalled();
- findButton().vm.$emit('click');
+ findButton().vm.$emit('click', clickEvent);
await nextTick();
- expect(spy).toHaveBeenCalled();
+ expect(spy).toHaveBeenCalledWith(clickEvent);
});
- it('emits the "click" event', async () => {
+ it('emits the "click" event, passing the event itself', async () => {
createComponent();
jest.spyOn(wrapper.vm, '$emit');
expect(wrapper.vm.$emit).not.toHaveBeenCalled();
- findButton().vm.$emit('click');
+ findButton().vm.$emit('click', clickEvent);
await nextTick();
- expect(wrapper.vm.$emit).toHaveBeenCalledWith('click');
+ expect(wrapper.vm.$emit).toHaveBeenCalledWith('click', clickEvent);
});
});
});
diff --git a/spec/frontend/editor/schema/ci/ci_schema_spec.js b/spec/frontend/editor/schema/ci/ci_schema_spec.js
index 32126a5fd9a..c822a0bfeaf 100644
--- a/spec/frontend/editor/schema/ci/ci_schema_spec.js
+++ b/spec/frontend/editor/schema/ci/ci_schema_spec.js
@@ -30,6 +30,9 @@ import RulesYaml from './yaml_tests/positive_tests/rules.yml';
import ProjectPathYaml from './yaml_tests/positive_tests/project_path.yml';
import VariablesYaml from './yaml_tests/positive_tests/variables.yml';
import JobWhenYaml from './yaml_tests/positive_tests/job_when.yml';
+import IdTokensYaml from './yaml_tests/positive_tests/id_tokens.yml';
+import HooksYaml from './yaml_tests/positive_tests/hooks.yml';
+import SecretsYaml from './yaml_tests/positive_tests/secrets.yml';
// YAML NEGATIVE TEST
import ArtifactsNegativeYaml from './yaml_tests/negative_tests/artifacts.yml';
@@ -43,8 +46,12 @@ import ProjectPathIncludeNoSlashYaml from './yaml_tests/negative_tests/project_p
import ProjectPathIncludeTailSlashYaml from './yaml_tests/negative_tests/project_path/include/tailing_slash.yml';
import RulesNegativeYaml from './yaml_tests/negative_tests/rules.yml';
import TriggerNegative from './yaml_tests/negative_tests/trigger.yml';
+import VariablesInvalidOptionsYaml from './yaml_tests/negative_tests/variables/invalid_options.yml';
import VariablesInvalidSyntaxDescYaml from './yaml_tests/negative_tests/variables/invalid_syntax_desc.yml';
import VariablesWrongSyntaxUsageExpand from './yaml_tests/negative_tests/variables/wrong_syntax_usage_expand.yml';
+import IdTokensNegativeYaml from './yaml_tests/negative_tests/id_tokens.yml';
+import HooksNegative from './yaml_tests/negative_tests/hooks.yml';
+import SecretsNegativeYaml from './yaml_tests/negative_tests/secrets.yml';
const ajv = new Ajv({
strictTypes: false,
@@ -77,9 +84,12 @@ describe('positive tests', () => {
FilterYaml,
IncludeYaml,
JobWhenYaml,
+ HooksYaml,
RulesYaml,
VariablesYaml,
ProjectPathYaml,
+ IdTokensYaml,
+ SecretsYaml,
}),
)('schema validates %s', (_, input) => {
// We construct a new "JSON" from each main key that is inside a
@@ -103,9 +113,11 @@ describe('negative tests', () => {
// YAML
ArtifactsNegativeYaml,
CacheKeyNeative,
+ IdTokensNegativeYaml,
IncludeNegativeYaml,
JobWhenNegativeYaml,
RulesNegativeYaml,
+ VariablesInvalidOptionsYaml,
VariablesInvalidSyntaxDescYaml,
VariablesWrongSyntaxUsageExpand,
ProjectPathIncludeEmptyYaml,
@@ -113,7 +125,9 @@ describe('negative tests', () => {
ProjectPathIncludeLeadSlashYaml,
ProjectPathIncludeNoSlashYaml,
ProjectPathIncludeTailSlashYaml,
+ SecretsNegativeYaml,
TriggerNegative,
+ HooksNegative,
}),
)('schema validates %s', (_, input) => {
// We construct a new "JSON" from each main key that is inside a
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/artifacts.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/artifacts.yml
index f5670376efc..29f4a0cd76d 100644
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/artifacts.yml
+++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/artifacts.yml
@@ -16,3 +16,16 @@ cyclonedx not an array or string:
paths:
- foo
- bar
+
+# invalid artifacts:when
+artifacts-when-unknown:
+ artifacts:
+ when: unknown
+
+artifacts-when-array:
+ artifacts:
+ when: [always]
+
+artifacts-when-boolean:
+ artifacts:
+ when: true
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml
index 3979c9ae2ac..9baed2a7922 100644
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml
+++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml
@@ -51,12 +51,39 @@ cache-untracked-string:
cache:
untracked: 'true'
-when_integer:
+# invalid cache:when
+cache-when-integer:
script: echo "This job uses a cache."
cache:
when: 0
-when_not_reserved_keyword:
+cache-when-array:
+ script: echo "This job uses a cache."
+ cache:
+ when: [always]
+
+cache-when-boolean:
+ script: echo "This job uses a cache."
+ cache:
+ when: true
+
+cache-when-never:
script: echo "This job uses a cache."
cache:
when: 'never'
+
+# invalid cache:policy
+cache-policy-array:
+ script: echo "This job uses a cache."
+ cache:
+ policy: [push]
+
+cache-policy-boolean:
+ script: echo "This job uses a cache."
+ cache:
+ policy: true
+
+cache-when-unknown:
+ script: echo "This job uses a cache."
+ cache:
+ policy: unknown
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/hooks.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/hooks.yml
new file mode 100644
index 00000000000..e3366b0b6d3
--- /dev/null
+++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/hooks.yml
@@ -0,0 +1,10 @@
+job1:
+ hooks:
+ invalid_script:
+ - echo 'hello job1 invalid_script'
+ script: echo 'hello job1 script'
+
+job2:
+ hooks:
+ pre_get_sources_script: true
+ script: echo 'hello job1 script'
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/id_tokens.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/id_tokens.yml
new file mode 100644
index 00000000000..aff2611f16c
--- /dev/null
+++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/id_tokens.yml
@@ -0,0 +1,11 @@
+id_token_with_wrong_aud_type:
+ id_tokens:
+ INVALID_ID_TOKEN:
+ aud:
+ invalid_prop: invalid
+
+id_token_with_extra_properties:
+ id_tokens:
+ INVALID_ID_TOKEN:
+ aud: 'https://gitlab.com'
+ sub: 'not a valid property'
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/secrets.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/secrets.yml
new file mode 100644
index 00000000000..14ba930b394
--- /dev/null
+++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/secrets.yml
@@ -0,0 +1,39 @@
+job_with_secrets_without_vault:
+ script:
+ - echo $TEST_DB_PASSWORD
+ secrets:
+ TEST_DB_PASSWORD:
+ token: $TEST_TOKEN
+
+job_with_secrets_with_extra_properties:
+ script:
+ - echo $TEST_DB_PASSWORD
+ secrets:
+ TEST_DB_PASSWORD:
+ vault: test/db/password
+ extra_prop: TEST
+
+job_with_secrets_with_invalid_vault_property:
+ script:
+ - echo $TEST_DB_PASSWORD
+ secrets:
+ TEST_DB_PASSWORD:
+ vault:
+ invalid: TEST
+
+job_with_secrets_with_missing_required_vault_property:
+ script:
+ - echo $TEST_DB_PASSWORD
+ secrets:
+ TEST_DB_PASSWORD:
+ vault:
+ path: gitlab
+
+job_with_secrets_with_missing_required_engine_property:
+ script:
+ - echo $TEST_DB_PASSWORD
+ secrets:
+ TEST_DB_PASSWORD:
+ vault:
+ engine:
+ path: kv
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables/invalid_options.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables/invalid_options.yml
new file mode 100644
index 00000000000..aac4c4e456d
--- /dev/null
+++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables/invalid_options.yml
@@ -0,0 +1,4 @@
+variables:
+ INVALID_OPTIONS:
+ value: "staging"
+ options: "staging" # must be an array
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/artifacts.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/artifacts.yml
index 20c1fc2c50f..a5c9153ee13 100644
--- a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/artifacts.yml
+++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/artifacts.yml
@@ -23,3 +23,13 @@ cylonedx mixed list of string paths and globs:
cyclonedx:
- ./foo
- "bar/*.baz"
+
+# valid artifacts:when
+artifacts-when-on-failure:
+ artifacts:
+ when: on_failure
+
+artifacts-no-when:
+ artifacts:
+ paths:
+ - binaries/
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/cache.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/cache.yml
index 75918cd2a1b..d50b74e1448 100644
--- a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/cache.yml
+++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/cache.yml
@@ -122,3 +122,20 @@ cache-untracked-false:
script: test
cache:
untracked: false
+
+# valid cache:policy
+cache-policy-push:
+ script: echo "This job uses a cache."
+ cache:
+ policy: push
+
+cache-policy-pull:
+ script: echo "This job uses a cache."
+ cache:
+ policy: pull
+
+cache-no-policy:
+ script: echo "This job uses a cache."
+ cache:
+ paths:
+ - binaries/
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/hooks.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/hooks.yml
new file mode 100644
index 00000000000..4d45c5528ea
--- /dev/null
+++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/hooks.yml
@@ -0,0 +1,10 @@
+default:
+ hooks:
+ pre_get_sources_script:
+ - echo 'hello default pre_get_sources_script'
+
+job1:
+ hooks:
+ pre_get_sources_script:
+ - echo 'hello job1 pre_get_sources_script'
+ script: echo 'hello job1 script'
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/id_tokens.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/id_tokens.yml
new file mode 100644
index 00000000000..169b09ee56f
--- /dev/null
+++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/id_tokens.yml
@@ -0,0 +1,11 @@
+valid_id_tokens:
+ script:
+ - echo $ID_TOKEN_1
+ - echo $ID_TOKEN_2
+ id_tokens:
+ ID_TOKEN_1:
+ aud: 'https://gitlab.com'
+ ID_TOKEN_2:
+ aud:
+ - 'https://aws.com'
+ - 'https://google.com'
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/secrets.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/secrets.yml
new file mode 100644
index 00000000000..083cb4348ed
--- /dev/null
+++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/secrets.yml
@@ -0,0 +1,28 @@
+valid_job_with_secrets:
+ script:
+ - echo $TEST_DB_PASSWORD
+ secrets:
+ TEST_DB_PASSWORD:
+ vault: test/db/password
+
+valid_job_with_secrets_and_token:
+ script:
+ - echo $TEST_DB_PASSWORD
+ secrets:
+ TEST_DB_PASSWORD:
+ vault: test/db/password
+ token: $TEST_TOKEN
+
+valid_job_with_secrets_with_every_vault_keyword:
+ script:
+ - echo $TEST_DB_PASSWORD
+ secrets:
+ TEST_DB_PASSWORD:
+ vault:
+ engine:
+ name: test-engine
+ path: test
+ path: test/db
+ field: password
+ file: true
+ token: $TEST_TOKEN
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/variables.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/variables.yml
index 53d020c432f..5c91de9be70 100644
--- a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/variables.yml
+++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/variables.yml
@@ -4,11 +4,18 @@ variables:
FOO:
value: "BAR"
description: "A single value variable"
- DEPLOY_ENVIRONMENT:
+ VAR_WITH_DESCRIPTION:
description: "A multi-value variable"
RAW_VAR:
value: "Hello $FOO"
expand: false
+ VAR_WITH_OPTIONS:
+ value: "staging"
+ options:
+ - "production"
+ - "staging"
+ - "canary"
+ description: "The deployment target. Set to 'production' by default."
rspec:
script: rspec
diff --git a/spec/frontend/editor/source_editor_markdown_ext_spec.js b/spec/frontend/editor/source_editor_markdown_ext_spec.js
index 3e8c287df2f..33e4b4bfc8e 100644
--- a/spec/frontend/editor/source_editor_markdown_ext_spec.js
+++ b/spec/frontend/editor/source_editor_markdown_ext_spec.js
@@ -1,7 +1,9 @@
import MockAdapter from 'axios-mock-adapter';
import { Range, Position } from 'monaco-editor';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
+import { EXTENSION_MARKDOWN_BUTTONS } from '~/editor/constants';
import { EditorMarkdownExtension } from '~/editor/extensions/source_editor_markdown_ext';
+import { ToolbarExtension } from '~/editor/extensions/source_editor_toolbar_ext';
import SourceEditor from '~/editor/source_editor';
import axios from '~/lib/utils/axios_utils';
@@ -36,7 +38,7 @@ describe('Markdown Extension for Source Editor', () => {
blobPath: markdownPath,
blobContent: text,
});
- instance.use({ definition: EditorMarkdownExtension });
+ instance.use([{ definition: ToolbarExtension }, { definition: EditorMarkdownExtension }]);
});
afterEach(() => {
@@ -47,6 +49,16 @@ describe('Markdown Extension for Source Editor', () => {
resetHTMLFixture();
});
+ describe('toolbar', () => {
+ it('renders all the buttons', () => {
+ const btns = instance.toolbar.getAllItems();
+ expect(btns).toHaveLength(EXTENSION_MARKDOWN_BUTTONS.length);
+ EXTENSION_MARKDOWN_BUTTONS.forEach((btn, i) => {
+ expect(btns[i].id).toBe(btn.id);
+ });
+ });
+ });
+
describe('getSelectedText', () => {
it('does not fail if there is no selection and returns the empty string', () => {
jest.spyOn(instance, 'getSelection');
diff --git a/spec/frontend/environment.js b/spec/frontend/environment.js
index 1c84350bd8e..82e3b50aeb8 100644
--- a/spec/frontend/environment.js
+++ b/spec/frontend/environment.js
@@ -1,7 +1,7 @@
/* eslint-disable import/no-commonjs, max-classes-per-file */
const path = require('path');
-const JSDOMEnvironment = require('jest-environment-jsdom');
+const { TestEnvironment } = require('jest-environment-jsdom');
const { ErrorWithStack } = require('jest-util');
const {
setGlobalDateToFakeDate,
@@ -11,10 +11,10 @@ const { TEST_HOST } = require('./__helpers__/test_constants');
const ROOT_PATH = path.resolve(__dirname, '../..');
-class CustomEnvironment extends JSDOMEnvironment {
- constructor(config, context) {
+class CustomEnvironment extends TestEnvironment {
+ constructor({ globalConfig, projectConfig }, context) {
// Setup testURL so that window.location is setup properly
- super({ ...config, testURL: TEST_HOST }, context);
+ super({ globalConfig, projectConfig: { ...projectConfig, testURL: TEST_HOST } }, context);
// Fake the `Date` for `jsdom` which fixes things like document.cookie
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39496#note_503084332
@@ -39,8 +39,7 @@ class CustomEnvironment extends JSDOMEnvironment {
},
});
- const { testEnvironmentOptions } = config;
- const { IS_EE } = testEnvironmentOptions;
+ const { IS_EE } = projectConfig.testEnvironmentOptions;
this.global.gon = {
ee: IS_EE,
};
diff --git a/spec/frontend/environments/environment_details_page_spec.js b/spec/frontend/environments/environment_details_page_spec.js
new file mode 100644
index 00000000000..5a02b34250f
--- /dev/null
+++ b/spec/frontend/environments/environment_details_page_spec.js
@@ -0,0 +1,50 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { GlLoadingIcon, GlTableLite } from '@gitlab/ui';
+import resolvedEnvironmentDetails from 'test_fixtures/graphql/environments/graphql/queries/environment_details.query.graphql.json';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import createMockApollo from '../__helpers__/mock_apollo_helper';
+import waitForPromises from '../__helpers__/wait_for_promises';
+import EnvironmentsDetailPage from '../../../app/assets/javascripts/environments/environment_details/index.vue';
+import getEnvironmentDetails from '../../../app/assets/javascripts/environments/graphql/queries/environment_details.query.graphql';
+
+describe('~/environments/environment_details/page.vue', () => {
+ Vue.use(VueApollo);
+
+ let wrapper;
+
+ const createWrapper = () => {
+ const mockApollo = createMockApollo([
+ [getEnvironmentDetails, jest.fn().mockResolvedValue(resolvedEnvironmentDetails)],
+ ]);
+
+ return mountExtended(EnvironmentsDetailPage, {
+ apolloProvider: mockApollo,
+ propsData: {
+ projectFullPath: resolvedEnvironmentDetails.data.project.fullPath,
+ environmentName: resolvedEnvironmentDetails.data.project.environment.name,
+ },
+ });
+ };
+
+ describe('when fetching data', () => {
+ it('should show a loading indicator', () => {
+ wrapper = createWrapper();
+
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlTableLite).exists()).not.toBe(true);
+ });
+ });
+
+ describe('when data is fetched', () => {
+ beforeEach(async () => {
+ wrapper = createWrapper();
+ await waitForPromises();
+ });
+
+ it('should render a table when query is loaded', async () => {
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).not.toBe(true);
+ expect(wrapper.findComponent(GlTableLite).exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/environments/helpers/__snapshots__/deployment_data_transformation_helper_spec.js.snap b/spec/frontend/environments/helpers/__snapshots__/deployment_data_transformation_helper_spec.js.snap
new file mode 100644
index 00000000000..401c10338c1
--- /dev/null
+++ b/spec/frontend/environments/helpers/__snapshots__/deployment_data_transformation_helper_spec.js.snap
@@ -0,0 +1,127 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`deployment_data_transformation_helper convertToDeploymentTableRow should be converted to proper table row data 1`] = `
+Object {
+ "commit": Object {
+ "author": Object {
+ "avatar_url": "/uploads/-/system/user/avatar/1/avatar.png",
+ "path": "http://gdk.test:3000/root",
+ "username": "Administrator",
+ },
+ "commitRef": Object {
+ "name": "main",
+ },
+ "commitUrl": "http://gdk.test:3000/gitlab-org/pipelinestest/-/commit/0cb48dd5deddb7632fd7c3defb16075fc6c3ca74",
+ "shortSha": "0cb48dd5",
+ "tag": false,
+ "title": "Update .gitlab-ci.yml file",
+ },
+ "created": "2022-10-17T07:44:17Z",
+ "deployed": "2022-10-17T07:44:43Z",
+ "id": "31",
+ "job": Object {
+ "label": "deploy-prod (#860)",
+ "webPath": "/gitlab-org/pipelinestest/-/jobs/860",
+ },
+ "status": "success",
+ "triggerer": Object {
+ "avatarUrl": "/uploads/-/system/user/avatar/1/avatar.png",
+ "id": "gid://gitlab/User/1",
+ "name": "Administrator",
+ "webUrl": "http://gdk.test:3000/root",
+ },
+}
+`;
+
+exports[`deployment_data_transformation_helper convertToDeploymentTableRow should be converted to proper table row data 2`] = `
+Object {
+ "commit": Object {
+ "author": Object {
+ "avatar_url": "/uploads/-/system/user/avatar/1/avatar.png",
+ "path": "http://gdk.test:3000/root",
+ "username": "Administrator",
+ },
+ "commitRef": Object {
+ "name": "main",
+ },
+ "commitUrl": "http://gdk.test:3000/gitlab-org/pipelinestest/-/commit/0cb48dd5deddb7632fd7c3defb16075fc6c3ca74",
+ "shortSha": "0cb48dd5",
+ "tag": false,
+ "title": "Update .gitlab-ci.yml file",
+ },
+ "created": "2022-10-17T07:44:17Z",
+ "deployed": "2022-10-17T07:44:43Z",
+ "id": "31",
+ "job": undefined,
+ "status": "success",
+ "triggerer": Object {
+ "avatarUrl": "/uploads/-/system/user/avatar/1/avatar.png",
+ "id": "gid://gitlab/User/1",
+ "name": "Administrator",
+ "webUrl": "http://gdk.test:3000/root",
+ },
+}
+`;
+
+exports[`deployment_data_transformation_helper convertToDeploymentTableRow should be converted to proper table row data 3`] = `
+Object {
+ "commit": Object {
+ "author": Object {
+ "avatar_url": "/uploads/-/system/user/avatar/1/avatar.png",
+ "path": "http://gdk.test:3000/root",
+ "username": "Administrator",
+ },
+ "commitRef": Object {
+ "name": "main",
+ },
+ "commitUrl": "http://gdk.test:3000/gitlab-org/pipelinestest/-/commit/0cb48dd5deddb7632fd7c3defb16075fc6c3ca74",
+ "shortSha": "0cb48dd5",
+ "tag": false,
+ "title": "Update .gitlab-ci.yml file",
+ },
+ "created": "2022-10-17T07:44:17Z",
+ "deployed": "",
+ "id": "31",
+ "job": null,
+ "status": "success",
+ "triggerer": Object {
+ "avatarUrl": "/uploads/-/system/user/avatar/1/avatar.png",
+ "id": "gid://gitlab/User/1",
+ "name": "Administrator",
+ "webUrl": "http://gdk.test:3000/root",
+ },
+}
+`;
+
+exports[`deployment_data_transformation_helper getAuthorFromCommit should be properly converted 1`] = `
+Object {
+ "avatar_url": "/uploads/-/system/user/avatar/1/avatar.png",
+ "path": "http://gdk.test:3000/root",
+ "username": "Administrator",
+}
+`;
+
+exports[`deployment_data_transformation_helper getAuthorFromCommit should be properly converted 2`] = `
+Object {
+ "avatar_url": "https://www.gravatar.com/avatar/91811aee1dec1b2655fa56f894e9e7c9?s=80&d=identicon",
+ "path": "mailto:azubov@gitlab.com",
+ "username": "Andrei Zubov",
+}
+`;
+
+exports[`deployment_data_transformation_helper getCommitFromDeploymentNode should get correclty formatted commit object 1`] = `
+Object {
+ "author": Object {
+ "avatar_url": "/uploads/-/system/user/avatar/1/avatar.png",
+ "path": "http://gdk.test:3000/root",
+ "username": "Administrator",
+ },
+ "commitRef": Object {
+ "name": "main",
+ },
+ "commitUrl": "http://gdk.test:3000/gitlab-org/pipelinestest/-/commit/0cb48dd5deddb7632fd7c3defb16075fc6c3ca74",
+ "shortSha": "0cb48dd5",
+ "tag": false,
+ "title": "Update .gitlab-ci.yml file",
+}
+`;
diff --git a/spec/frontend/environments/helpers/deployment_data_transformation_helper_spec.js b/spec/frontend/environments/helpers/deployment_data_transformation_helper_spec.js
new file mode 100644
index 00000000000..8bb87c0a208
--- /dev/null
+++ b/spec/frontend/environments/helpers/deployment_data_transformation_helper_spec.js
@@ -0,0 +1,96 @@
+import {
+ getAuthorFromCommit,
+ getCommitFromDeploymentNode,
+ convertToDeploymentTableRow,
+} from '~/environments/helpers/deployment_data_transformation_helper';
+
+describe('deployment_data_transformation_helper', () => {
+ const commitWithAuthor = {
+ id: 'gid://gitlab/CommitPresenter/0cb48dd5deddb7632fd7c3defb16075fc6c3ca74',
+ shortId: '0cb48dd5',
+ message: 'Update .gitlab-ci.yml file',
+ webUrl:
+ 'http://gdk.test:3000/gitlab-org/pipelinestest/-/commit/0cb48dd5deddb7632fd7c3defb16075fc6c3ca74',
+ authorGravatar:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ authorName: 'Administrator',
+ authorEmail: 'admin@example.com',
+ author: {
+ id: 'gid://gitlab/User/1',
+ name: 'Administrator',
+ avatarUrl: '/uploads/-/system/user/avatar/1/avatar.png',
+ webUrl: 'http://gdk.test:3000/root',
+ },
+ };
+
+ const commitWithourAuthor = {
+ id: 'gid://gitlab/CommitPresenter/02274a949a88c9aef68a29685d99bd9a661a7f9b',
+ shortId: '02274a94',
+ message: 'Commit message',
+ webUrl:
+ 'http://gdk.test:3000/gitlab-org/pipelinestest/-/commit/02274a949a88c9aef68a29685d99bd9a661a7f9b',
+ authorGravatar:
+ 'https://www.gravatar.com/avatar/91811aee1dec1b2655fa56f894e9e7c9?s=80&d=identicon',
+ authorName: 'Andrei Zubov',
+ authorEmail: 'azubov@gitlab.com',
+ author: null,
+ };
+
+ const deploymentNode = {
+ id: 'gid://gitlab/Deployment/76',
+ iid: '31',
+ status: 'SUCCESS',
+ createdAt: '2022-10-17T07:44:17Z',
+ ref: 'main',
+ tag: false,
+ job: {
+ name: 'deploy-prod',
+ refName: 'main',
+ id: 'gid://gitlab/Ci::Build/860',
+ webPath: '/gitlab-org/pipelinestest/-/jobs/860',
+ },
+ commit: commitWithAuthor,
+ triggerer: {
+ id: 'gid://gitlab/User/1',
+ webUrl: 'http://gdk.test:3000/root',
+ name: 'Administrator',
+ avatarUrl: '/uploads/-/system/user/avatar/1/avatar.png',
+ },
+ finishedAt: '2022-10-17T07:44:43Z',
+ };
+
+ const deploymentNodeWithNoJob = {
+ ...deploymentNode,
+ job: null,
+ finishedAt: null,
+ };
+
+ describe('getAuthorFromCommit', () => {
+ it.each([commitWithAuthor, commitWithourAuthor])('should be properly converted', (commit) => {
+ expect(getAuthorFromCommit(commit)).toMatchSnapshot();
+ });
+ });
+
+ describe('getCommitFromDeploymentNode', () => {
+ it('should throw an error when commit field is missing', () => {
+ const emptyDeploymentNode = {};
+
+ expect(() => getCommitFromDeploymentNode(emptyDeploymentNode)).toThrow();
+ });
+
+ it('should get correclty formatted commit object', () => {
+ expect(getCommitFromDeploymentNode(deploymentNode)).toMatchSnapshot();
+ });
+ });
+
+ describe('convertToDeploymentTableRow', () => {
+ const deploymentNodeWithEmptyJob = { ...deploymentNode, job: undefined };
+
+ it.each([deploymentNode, deploymentNodeWithEmptyJob, deploymentNodeWithNoJob])(
+ 'should be converted to proper table row data',
+ (node) => {
+ expect(convertToDeploymentTableRow(node)).toMatchSnapshot();
+ },
+ );
+ });
+});
diff --git a/spec/frontend/feature_flags/components/feature_flags_table_spec.js b/spec/frontend/feature_flags/components/feature_flags_table_spec.js
index 47f12f70056..f23bca54b55 100644
--- a/spec/frontend/feature_flags/components/feature_flags_table_spec.js
+++ b/spec/frontend/feature_flags/components/feature_flags_table_spec.js
@@ -1,6 +1,6 @@
-import { GlToggle, GlBadge } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { GlToggle } from '@gitlab/ui';
import { nextTick } from 'vue';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
import { trimText } from 'helpers/text_helper';
import { mockTracking } from 'helpers/tracking_helper';
import FeatureFlagsTable from '~/feature_flags/components/feature_flags_table.vue';
@@ -52,10 +52,10 @@ const getDefaultProps = () => ({
describe('Feature flag table', () => {
let wrapper;
let props;
- let badges;
+ let labels;
const createWrapper = (propsData, opts = {}) => {
- wrapper = shallowMount(FeatureFlagsTable, {
+ wrapper = mountExtended(FeatureFlagsTable, {
propsData,
provide: {
csrfToken: 'fakeToken',
@@ -70,18 +70,13 @@ describe('Feature flag table', () => {
provide: { csrfToken: 'fakeToken' },
});
- badges = wrapper.findAll('[data-testid="strategy-badge"]');
+ labels = wrapper.findAllByTestId('strategy-label');
});
beforeEach(() => {
props = getDefaultProps();
});
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
describe('with an active scope and a standard rollout strategy', () => {
beforeEach(() => {
createWrapper(props);
@@ -101,7 +96,7 @@ describe('Feature flag table', () => {
});
it('Should render a status column', () => {
- const badge = wrapper.find('[data-testid="feature-flag-status-badge"]');
+ const badge = wrapper.findByTestId('feature-flag-status-badge');
expect(badge.exists()).toBe(true);
expect(trimText(badge.text())).toEqual('Active');
@@ -116,10 +111,10 @@ describe('Feature flag table', () => {
);
});
- it('should render an environments specs badge with active class', () => {
- const envColumn = wrapper.find('.js-feature-flag-environments');
+ it('should render an environments specs label', () => {
+ const strategyLabel = wrapper.findByTestId('strategy-label');
- expect(trimText(envColumn.findComponent(GlBadge).text())).toBe('All Users: All Environments');
+ expect(trimText(strategyLabel.text())).toBe('All Users: All Environments');
});
it('should render an actions column', () => {
@@ -167,29 +162,29 @@ describe('Feature flag table', () => {
});
it('shows All Environments if the environment scope is *', () => {
- expect(badges.at(0).text()).toContain('All Environments');
+ expect(labels.at(0).text()).toContain('All Environments');
});
it('shows the environment scope if another is set', () => {
- expect(badges.at(1).text()).toContain('production');
- expect(badges.at(1).text()).toContain('staging');
- expect(badges.at(2).text()).toContain('review/*');
+ expect(labels.at(1).text()).toContain('production');
+ expect(labels.at(1).text()).toContain('staging');
+ expect(labels.at(2).text()).toContain('review/*');
});
it('shows All Users for the default strategy', () => {
- expect(badges.at(0).text()).toContain('All Users');
+ expect(labels.at(0).text()).toContain('All Users');
});
it('shows the percent for a percent rollout', () => {
- expect(badges.at(1).text()).toContain('Percent of users - 50%');
+ expect(labels.at(1).text()).toContain('Percent of users - 50%');
});
it('shows the number of users for users with ID', () => {
- expect(badges.at(2).text()).toContain('User IDs - 4 users');
+ expect(labels.at(2).text()).toContain('User IDs - 4 users');
});
it('shows the name of a user list for user list', () => {
- expect(badges.at(3).text()).toContain('User List - test list');
+ expect(labels.at(3).text()).toContain('User List - test list');
});
it('renders a feature flag without an iid', () => {
diff --git a/spec/frontend/feature_flags/components/strategy_label_spec.js b/spec/frontend/feature_flags/components/strategy_label_spec.js
new file mode 100644
index 00000000000..c2d5ce10448
--- /dev/null
+++ b/spec/frontend/feature_flags/components/strategy_label_spec.js
@@ -0,0 +1,61 @@
+import { mount } from '@vue/test-utils';
+import StrategyLabel from '~/feature_flags/components/strategy_label.vue';
+
+const DEFAULT_PROPS = {
+ name: 'All Users',
+ parameters: 'parameters',
+ scopes: 'scope1, scope2',
+};
+
+describe('feature_flags/components/feature_flags_tab.vue', () => {
+ let wrapper;
+
+ const factory = (props = {}) =>
+ mount(
+ {
+ components: {
+ StrategyLabel,
+ },
+ render(h) {
+ return h(StrategyLabel, { props: this.$attrs, on: this.$listeners }, this.$slots.default);
+ },
+ },
+ {
+ propsData: {
+ ...DEFAULT_PROPS,
+ ...props,
+ },
+ },
+ );
+
+ describe('render', () => {
+ let strategyLabel;
+
+ beforeEach(() => {
+ wrapper = factory({});
+ strategyLabel = wrapper.findComponent(StrategyLabel);
+ });
+
+ it('should show the strategy label with parameters and scope', () => {
+ expect(strategyLabel.text()).toContain(DEFAULT_PROPS.name);
+ expect(strategyLabel.text()).toContain(DEFAULT_PROPS.parameters);
+ expect(strategyLabel.text()).toContain(DEFAULT_PROPS.scopes);
+ expect(strategyLabel.text()).toContain('All Users - parameters: scope1, scope2');
+ });
+ });
+
+ describe('without parameters', () => {
+ let strategyLabel;
+
+ beforeEach(() => {
+ wrapper = factory({ parameters: null });
+ strategyLabel = wrapper.findComponent(StrategyLabel);
+ });
+
+ it('should hide empty params and dash', () => {
+ expect(strategyLabel.text()).toContain(DEFAULT_PROPS.name);
+ expect(strategyLabel.text()).not.toContain(' - ');
+ expect(strategyLabel.text()).toContain('All Users: scope1, scope2');
+ });
+ });
+});
diff --git a/spec/frontend/feature_highlight/feature_highlight_helper_spec.js b/spec/frontend/feature_highlight/feature_highlight_helper_spec.js
index 22bac3fca15..d82081041d9 100644
--- a/spec/frontend/feature_highlight/feature_highlight_helper_spec.js
+++ b/spec/frontend/feature_highlight/feature_highlight_helper_spec.js
@@ -2,7 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
import { dismiss } from '~/feature_highlight/feature_highlight_helper';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
-import httpStatusCodes from '~/lib/utils/http_status';
+import httpStatusCodes, { HTTP_STATUS_CREATED } from '~/lib/utils/http_status';
jest.mock('~/flash');
@@ -11,7 +11,7 @@ describe('feature highlight helper', () => {
let mockAxios;
const endpoint = '/-/callouts/dismiss';
const highlightId = '123';
- const { CREATED, INTERNAL_SERVER_ERROR } = httpStatusCodes;
+ const { INTERNAL_SERVER_ERROR } = httpStatusCodes;
beforeEach(() => {
mockAxios = new MockAdapter(axios);
@@ -22,7 +22,7 @@ describe('feature highlight helper', () => {
});
it('calls persistent dismissal endpoint with highlightId', async () => {
- mockAxios.onPost(endpoint, { feature_name: highlightId }).replyOnce(CREATED);
+ mockAxios.onPost(endpoint, { feature_name: highlightId }).replyOnce(HTTP_STATUS_CREATED);
await expect(dismiss(endpoint, highlightId)).resolves.toEqual(expect.anything());
});
diff --git a/spec/frontend/filtered_search/components/recent_searches_dropdown_content_spec.js b/spec/frontend/filtered_search/components/recent_searches_dropdown_content_spec.js
index 91457f10bf8..ebed477fa2f 100644
--- a/spec/frontend/filtered_search/components/recent_searches_dropdown_content_spec.js
+++ b/spec/frontend/filtered_search/components/recent_searches_dropdown_content_spec.js
@@ -2,6 +2,7 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import RecentSearchesDropdownContent from '~/filtered_search/components/recent_searches_dropdown_content.vue';
import eventHub from '~/filtered_search/event_hub';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
+import { TOKEN_TYPE_AUTHOR } from '~/vue_shared/components/filtered_search_bar/constants';
describe('Recent Searches Dropdown Content', () => {
let wrapper;
@@ -60,7 +61,7 @@ describe('Recent Searches Dropdown Content', () => {
items: [
'foo',
'author:@root label:~foo bar',
- [{ type: 'author_username', value: { data: 'toby', operator: '=' } }],
+ [{ type: TOKEN_TYPE_AUTHOR, value: { data: 'toby', operator: '=' } }],
],
isLocalStorageAvailable: true,
});
diff --git a/spec/frontend/filtered_search/filtered_search_manager_spec.js b/spec/frontend/filtered_search/filtered_search_manager_spec.js
index 5e68725c03e..26af7af701b 100644
--- a/spec/frontend/filtered_search/filtered_search_manager_spec.js
+++ b/spec/frontend/filtered_search/filtered_search_manager_spec.js
@@ -8,7 +8,7 @@ import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered
import RecentSearchesRoot from '~/filtered_search/recent_searches_root';
import RecentSearchesService from '~/filtered_search/services/recent_searches_service';
import RecentSearchesServiceError from '~/filtered_search/services/recent_searches_service_error';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import { BACKSPACE_KEY_CODE, DELETE_KEY_CODE } from '~/lib/utils/keycodes';
import { visitUrl, getParameterByName } from '~/lib/utils/url_utility';
@@ -130,14 +130,14 @@ describe('Filtered Search Manager', () => {
manager = new FilteredSearchManager({ page });
});
- it('should not instantiate Flash if an RecentSearchesServiceError is caught', () => {
+ it('should not show an alert if an RecentSearchesServiceError is caught', () => {
jest
.spyOn(RecentSearchesService.prototype, 'fetch')
.mockImplementation(() => Promise.reject(new RecentSearchesServiceError()));
manager.setup();
- expect(createFlash).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
});
});
diff --git a/spec/frontend/filtered_search/filtered_search_visual_tokens_spec.js b/spec/frontend/filtered_search/filtered_search_visual_tokens_spec.js
index 0e5c94edd05..28fcf0b7ec7 100644
--- a/spec/frontend/filtered_search/filtered_search_visual_tokens_spec.js
+++ b/spec/frontend/filtered_search/filtered_search_visual_tokens_spec.js
@@ -4,6 +4,7 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import FilteredSearchSpecHelper from 'helpers/filtered_search_spec_helper';
import waitForPromises from 'helpers/wait_for_promises';
import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
describe('Filtered Search Visual Tokens', () => {
let mock;
@@ -302,7 +303,7 @@ describe('Filtered Search Visual Tokens', () => {
});
const token = tokensContainer.querySelector('.js-visual-token');
- expect(token.classList.contains('filtered-search-term')).toEqual(true);
+ expect(token.classList.contains(FILTERED_SEARCH_TERM)).toEqual(true);
expect(token.querySelector('.name').innerText).toEqual('search term');
expect(token.querySelector('.operator').innerText).toEqual('=');
expect(token.querySelector('.value')).toEqual(null);
@@ -430,7 +431,7 @@ describe('Filtered Search Visual Tokens', () => {
subject.addSearchVisualToken('search term');
const token = tokensContainer.querySelector('.js-visual-token');
- expect(token.classList.contains('filtered-search-term')).toEqual(true);
+ expect(token.classList.contains(FILTERED_SEARCH_TERM)).toEqual(true);
expect(token.querySelector('.name').innerText).toEqual('search term');
expect(token.querySelector('.value')).toEqual(null);
});
diff --git a/spec/frontend/filtered_search/visual_token_value_spec.js b/spec/frontend/filtered_search/visual_token_value_spec.js
index e52ffa7bd9f..43c10090739 100644
--- a/spec/frontend/filtered_search/visual_token_value_spec.js
+++ b/spec/frontend/filtered_search/visual_token_value_spec.js
@@ -5,7 +5,7 @@ import FilteredSearchSpecHelper from 'helpers/filtered_search_spec_helper';
import { TEST_HOST } from 'helpers/test_constants';
import DropdownUtils from '~/filtered_search/dropdown_utils';
import VisualTokenValue from '~/filtered_search/visual_token_value';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import AjaxCache from '~/lib/utils/ajax_cache';
import UsersCache from '~/lib/utils/users_cache';
@@ -61,7 +61,7 @@ describe('Filtered Search Visual Tokens', () => {
};
await subject.updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue);
- expect(createFlash.mock.calls.length).toBe(0);
+ expect(createAlert).toHaveBeenCalledTimes(0);
});
it('does nothing if user cannot be found', async () => {
diff --git a/spec/frontend/fixtures/api_merge_requests.rb b/spec/frontend/fixtures/api_merge_requests.rb
index fae1f4056fb..a71a41dc5c4 100644
--- a/spec/frontend/fixtures/api_merge_requests.rb
+++ b/spec/frontend/fixtures/api_merge_requests.rb
@@ -6,7 +6,6 @@ RSpec.describe API::MergeRequests, '(JavaScript fixtures)', type: :request do
include ApiHelpers
include JavaScriptFixturesHelpers
- let_it_be(:admin) { create(:admin, name: 'root') }
let_it_be(:namespace) { create(:namespace, name: 'gitlab-test') }
let_it_be(:project) { create(:project, :repository, namespace: namespace, path: 'lorem-ipsum') }
let_it_be(:early_mrs) do
@@ -14,21 +13,22 @@ RSpec.describe API::MergeRequests, '(JavaScript fixtures)', type: :request do
end
let_it_be(:mr) { create(:merge_request, source_project: project) }
+ let_it_be(:user) { project.owner }
it 'api/merge_requests/get.json' do
- get api("/projects/#{project.id}/merge_requests", admin)
+ get api("/projects/#{project.id}/merge_requests", user)
expect(response).to be_successful
end
it 'api/merge_requests/versions.json' do
- get api("/projects/#{project.id}/merge_requests/#{mr.iid}/versions", admin)
+ get api("/projects/#{project.id}/merge_requests/#{mr.iid}/versions", user)
expect(response).to be_successful
end
it 'api/merge_requests/changes.json' do
- get api("/projects/#{project.id}/merge_requests/#{mr.iid}/changes", admin)
+ get api("/projects/#{project.id}/merge_requests/#{mr.iid}/changes", user)
expect(response).to be_successful
end
diff --git a/spec/frontend/fixtures/api_projects.rb b/spec/frontend/fixtures/api_projects.rb
index b14f402a7b9..d1dfd223419 100644
--- a/spec/frontend/fixtures/api_projects.rb
+++ b/spec/frontend/fixtures/api_projects.rb
@@ -6,25 +6,25 @@ RSpec.describe API::Projects, '(JavaScript fixtures)', type: :request do
include ApiHelpers
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin, name: 'root') }
let(:namespace) { create(:namespace, name: 'gitlab-test') }
let(:project) { create(:project, :repository, namespace: namespace, path: 'lorem-ipsum') }
let(:project_empty) { create(:project_empty_repo, namespace: namespace, path: 'lorem-ipsum-empty') }
+ let(:user) { project.owner }
it 'api/projects/get.json' do
- get api("/projects/#{project.id}", admin)
+ get api("/projects/#{project.id}", user)
expect(response).to be_successful
end
it 'api/projects/get_empty.json' do
- get api("/projects/#{project_empty.id}", admin)
+ get api("/projects/#{project_empty.id}", user)
expect(response).to be_successful
end
it 'api/projects/branches/get.json' do
- get api("/projects/#{project.id}/repository/branches/#{project.default_branch}", admin)
+ get api("/projects/#{project.id}/repository/branches/#{project.default_branch}", user)
expect(response).to be_successful
end
diff --git a/spec/frontend/fixtures/environments.rb b/spec/frontend/fixtures/environments.rb
new file mode 100644
index 00000000000..3ca5b50ac9c
--- /dev/null
+++ b/spec/frontend/fixtures/environments.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Environments (JavaScript fixtures)', feature_category: :environment_management do
+ include ApiHelpers
+ include JavaScriptFixturesHelpers
+ include GraphqlHelpers
+
+ let_it_be(:admin) { create(:admin, username: 'administrator', email: 'admin@example.gitlab.com') }
+ let_it_be(:group) { create(:group, path: 'environments-group') }
+ let_it_be(:project) { create(:project, :repository, group: group, path: 'environments-project') }
+
+ let_it_be(:environment) { create(:environment, name: 'staging', project: project) }
+
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:build) { create(:ci_build, :success, pipeline: pipeline) }
+
+ let(:user) { create(:user) }
+ let(:role) { :developer }
+ let_it_be(:deployment) do
+ create(:deployment, :success, environment: environment, deployable: nil)
+ end
+
+ let_it_be(:deployment_success) do
+ create(:deployment, :success, environment: environment, deployable: build)
+ end
+
+ let_it_be(:deployment_failed) do
+ create(:deployment, :failed, environment: environment, deployable: build)
+ end
+
+ let_it_be(:deployment_running) do
+ create(:deployment, :running, environment: environment, deployable: build)
+ end
+
+ describe GraphQL::Query, type: :request do
+ environment_details_query_path = 'environments/graphql/queries/environment_details.query.graphql'
+
+ it "graphql/#{environment_details_query_path}.json" do
+ query = get_graphql_query_as_string(environment_details_query_path)
+
+ post_graphql(query, current_user: admin,
+ variables:
+ {
+ projectFullPath: project.full_path,
+ environmentName: environment.name,
+ pageSize: 10
+ })
+ expect_graphql_errors_to_be_empty
+ end
+ end
+end
diff --git a/spec/frontend/fixtures/freeze_period.rb b/spec/frontend/fixtures/freeze_period.rb
index 5aa466ef015..a1c7564d36e 100644
--- a/spec/frontend/fixtures/freeze_period.rb
+++ b/spec/frontend/fixtures/freeze_period.rb
@@ -13,15 +13,6 @@ RSpec.describe 'Freeze Periods (JavaScript fixtures)' do
remove_repository(project)
end
- around do |example|
- freeze_time do
- # Mock time to sept 19 (intl. talk like a pirate day)
- travel_to(Time.utc(2020, 9, 19))
-
- example.run
- end
- end
-
describe API::FreezePeriods, '(JavaScript fixtures)', type: :request do
include ApiHelpers
diff --git a/spec/frontend/fixtures/releases.rb b/spec/frontend/fixtures/releases.rb
index fc344472588..c7e3d8fe804 100644
--- a/spec/frontend/fixtures/releases.rb
+++ b/spec/frontend/fixtures/releases.rb
@@ -6,9 +6,9 @@ RSpec.describe 'Releases (JavaScript fixtures)' do
include ApiHelpers
include JavaScriptFixturesHelpers
- let_it_be(:admin) { create(:admin, username: 'administrator', email: 'admin@example.gitlab.com') }
let_it_be(:namespace) { create(:namespace, path: 'releases-namespace') }
let_it_be(:project) { create(:project, :repository, namespace: namespace, path: 'releases-project') }
+ let_it_be(:user) { create(:user, email: 'user@example.gitlab.com', username: 'user1') }
let_it_be(:milestone_12_3) do
create(:milestone,
@@ -52,7 +52,7 @@ RSpec.describe 'Releases (JavaScript fixtures)' do
project: project,
tag: 'v1.1',
name: 'The first release',
- author: admin,
+ author: user,
description: 'Best. Release. **Ever.** :rocket:',
created_at: Time.zone.parse('2018-12-3'),
released_at: Time.zone.parse('2018-12-10'))
@@ -105,19 +105,23 @@ RSpec.describe 'Releases (JavaScript fixtures)' do
project: project,
tag: 'v1.2',
name: 'The second release',
- author: admin,
+ author: user,
description: 'An okay release :shrug:',
created_at: Time.zone.parse('2019-01-03'),
released_at: Time.zone.parse('2019-01-10'))
end
+ before do
+ project.add_owner(user)
+ end
+
after(:all) do
remove_repository(project)
end
describe API::Releases, type: :request do
it 'api/releases/release.json' do
- get api("/projects/#{project.id}/releases/#{release.tag}", admin)
+ get api("/projects/#{project.id}/releases/#{release.tag}", user)
expect(response).to be_successful
end
@@ -133,7 +137,7 @@ RSpec.describe 'Releases (JavaScript fixtures)' do
it "graphql/#{all_releases_query_path}.json" do
query = get_graphql_query_as_string(all_releases_query_path)
- post_graphql(query, current_user: admin, variables: { fullPath: project.full_path })
+ post_graphql(query, current_user: user, variables: { fullPath: project.full_path })
expect_graphql_errors_to_be_empty
expect(graphql_data_at(:project, :releases)).to be_present
@@ -142,7 +146,7 @@ RSpec.describe 'Releases (JavaScript fixtures)' do
it "graphql/#{one_release_query_path}.json" do
query = get_graphql_query_as_string(one_release_query_path)
- post_graphql(query, current_user: admin, variables: { fullPath: project.full_path, tagName: release.tag })
+ post_graphql(query, current_user: user, variables: { fullPath: project.full_path, tagName: release.tag })
expect_graphql_errors_to_be_empty
expect(graphql_data_at(:project, :release)).to be_present
@@ -151,7 +155,7 @@ RSpec.describe 'Releases (JavaScript fixtures)' do
it "graphql/#{one_release_for_editing_query_path}.json" do
query = get_graphql_query_as_string(one_release_for_editing_query_path)
- post_graphql(query, current_user: admin, variables: { fullPath: project.full_path, tagName: release.tag })
+ post_graphql(query, current_user: user, variables: { fullPath: project.full_path, tagName: release.tag })
expect_graphql_errors_to_be_empty
expect(graphql_data_at(:project, :release)).to be_present
diff --git a/spec/frontend/fixtures/runner_instructions.rb b/spec/frontend/fixtures/runner_instructions.rb
new file mode 100644
index 00000000000..90a01c37479
--- /dev/null
+++ b/spec/frontend/fixtures/runner_instructions.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Runner Instructions (JavaScript fixtures)', feature_category: :runner do
+ include ApiHelpers
+ include JavaScriptFixturesHelpers
+ include GraphqlHelpers
+
+ query_path = 'vue_shared/components/runner_instructions/graphql/queries'
+
+ describe GraphQL::Query do
+ describe 'get_runner_platforms.query.graphql', type: :request do
+ let_it_be(:query) do
+ get_graphql_query_as_string("#{query_path}/get_runner_platforms.query.graphql")
+ end
+
+ it 'graphql/runner_instructions/get_runner_platforms.query.graphql.json' do
+ post_graphql(query)
+
+ expect_graphql_errors_to_be_empty
+ end
+ end
+
+ describe 'get_runner_setup.query.graphql', type: :request do
+ let_it_be(:query) do
+ get_graphql_query_as_string("#{query_path}/get_runner_setup.query.graphql")
+ end
+
+ it 'graphql/runner_instructions/get_runner_setup.query.graphql.json' do
+ post_graphql(query, variables: { platform: 'linux', architecture: 'amd64' })
+
+ expect_graphql_errors_to_be_empty
+ end
+
+ it 'graphql/runner_instructions/get_runner_setup.query.graphql.windows.json' do
+ post_graphql(query, variables: { platform: 'windows', architecture: 'amd64' })
+
+ expect_graphql_errors_to_be_empty
+ end
+ end
+ end
+end
diff --git a/spec/frontend/fixtures/tabs.rb b/spec/frontend/fixtures/tabs.rb
index 697ff1c7c20..57ecb32e289 100644
--- a/spec/frontend/fixtures/tabs.rb
+++ b/spec/frontend/fixtures/tabs.rb
@@ -11,14 +11,14 @@ RSpec.describe 'GlTabsBehavior', '(JavaScript fixtures)', type: :helper do
it 'tabs/tabs.html' do
tabs = gl_tabs_nav({ data: { testid: 'tabs' } }) do
gl_tab_link_to('Foo', '#foo', item_active: true, data: { testid: 'foo-tab' }) +
- gl_tab_link_to('Bar', '#bar', item_active: false, data: { testid: 'bar-tab' }) +
- gl_tab_link_to('Qux', '#qux', item_active: false, data: { testid: 'qux-tab' })
+ gl_tab_link_to('Bar', '#bar', item_active: false, data: { testid: 'bar-tab' }) +
+ gl_tab_link_to('Qux', '#qux', item_active: false, data: { testid: 'qux-tab' })
end
panels = content_tag(:div, class: 'tab-content') do
content_tag(:div, 'Foo', { id: 'foo', class: 'tab-pane active', data: { testid: 'foo-panel' } }) +
- content_tag(:div, 'Bar', { id: 'bar', class: 'tab-pane', data: { testid: 'bar-panel' } }) +
- content_tag(:div, 'Qux', { id: 'qux', class: 'tab-pane', data: { testid: 'qux-panel' } })
+ content_tag(:div, 'Bar', { id: 'bar', class: 'tab-pane', data: { testid: 'bar-panel' } }) +
+ content_tag(:div, 'Qux', { id: 'qux', class: 'tab-pane', data: { testid: 'qux-panel' } })
end
@tabs = tabs + panels
diff --git a/spec/frontend/flash_spec.js b/spec/frontend/flash_spec.js
index a105b0b165c..ade36cd1637 100644
--- a/spec/frontend/flash_spec.js
+++ b/spec/frontend/flash_spec.js
@@ -12,6 +12,9 @@ import createFlash, {
jest.mock('@sentry/browser');
describe('Flash', () => {
+ const findTextContent = (containerSelector = '.flash-container') =>
+ document.querySelector(containerSelector).textContent.replace(/\s+/g, ' ').trim();
+
describe('hideFlash', () => {
let el;
@@ -99,7 +102,7 @@ describe('Flash', () => {
it('adds alert element into the document by default', () => {
alert = createAlert({ message: mockMessage });
- expect(document.querySelector('.flash-container').textContent.trim()).toBe(mockMessage);
+ expect(findTextContent()).toBe(mockMessage);
expect(document.querySelector('.flash-container .gl-alert')).not.toBeNull();
});
@@ -202,8 +205,7 @@ describe('Flash', () => {
message: mockMessage,
});
- const text = document.querySelector('.flash-container').textContent.trim();
- expect(text).toBe(`${mockTitle} ${mockMessage}`);
+ expect(findTextContent()).toBe(`${mockTitle} ${mockMessage}`);
});
});
@@ -319,6 +321,22 @@ describe('Flash', () => {
});
});
});
+
+ describe('when called multiple times', () => {
+ it('clears previous alerts', () => {
+ createAlert({ message: 'message 1' });
+ createAlert({ message: 'message 2' });
+
+ expect(findTextContent()).toBe('message 2');
+ });
+
+ it('preserves alerts when `preservePrevious` is true', () => {
+ createAlert({ message: 'message 1' });
+ createAlert({ message: 'message 2', preservePrevious: true });
+
+ expect(findTextContent()).toBe('message 1 message 2');
+ });
+ });
});
});
diff --git a/spec/frontend/gfm_auto_complete_spec.js b/spec/frontend/gfm_auto_complete_spec.js
index 68225f39c66..eeef92d4183 100644
--- a/spec/frontend/gfm_auto_complete_spec.js
+++ b/spec/frontend/gfm_auto_complete_spec.js
@@ -772,6 +772,7 @@ describe('GfmAutoComplete', () => {
input | output
${'~'} | ${unassignedLabels}
${'/label ~'} | ${unassignedLabels}
+ ${'/labels ~'} | ${unassignedLabels}
${'/relabel ~'} | ${unassignedLabels}
${'/unlabel ~'} | ${[]}
`('$input shows $output.length labels', expectLabels);
@@ -786,6 +787,7 @@ describe('GfmAutoComplete', () => {
input | output
${'~'} | ${allLabels}
${'/label ~'} | ${unassignedLabels}
+ ${'/labels ~'} | ${unassignedLabels}
${'/relabel ~'} | ${allLabels}
${'/unlabel ~'} | ${assignedLabels}
`('$input shows $output.length labels', expectLabels);
@@ -800,6 +802,7 @@ describe('GfmAutoComplete', () => {
input | output
${'~'} | ${assignedLabels}
${'/label ~'} | ${[]}
+ ${'/labels ~'} | ${[]}
${'/relabel ~'} | ${assignedLabels}
${'/unlabel ~'} | ${assignedLabels}
`('$input shows $output.length labels', expectLabels);
diff --git a/spec/frontend/gitlab_version_check/components/security_patch_upgrade_alert_modal_spec.js b/spec/frontend/gitlab_version_check/components/security_patch_upgrade_alert_modal_spec.js
new file mode 100644
index 00000000000..f1ed32a5f79
--- /dev/null
+++ b/spec/frontend/gitlab_version_check/components/security_patch_upgrade_alert_modal_spec.js
@@ -0,0 +1,202 @@
+import { GlModal, GlLink, GlSprintf } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
+import { sprintf } from '~/locale';
+import SecurityPatchUpgradeAlertModal from '~/gitlab_version_check/components/security_patch_upgrade_alert_modal.vue';
+import * as utils from '~/gitlab_version_check/utils';
+import {
+ UPGRADE_DOCS_URL,
+ ABOUT_RELEASES_PAGE,
+ TRACKING_ACTIONS,
+ TRACKING_LABELS,
+} from '~/gitlab_version_check/constants';
+
+describe('SecurityPatchUpgradeAlertModal', () => {
+ let wrapper;
+ let trackingSpy;
+
+ const defaultProps = {
+ currentVersion: '11.1.1',
+ };
+
+ const createComponent = (props = {}) => {
+ trackingSpy = mockTracking(undefined, undefined, jest.spyOn);
+
+ wrapper = shallowMountExtended(SecurityPatchUpgradeAlertModal, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ stubs: {
+ GlModal,
+ GlSprintf,
+ },
+ });
+ };
+
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ const expectDispatchedTracking = (action, label) => {
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, action, {
+ label,
+ property: defaultProps.currentVersion,
+ });
+ };
+
+ const findGlModal = () => wrapper.findComponent(GlModal);
+ const findGlModalTitle = () => wrapper.findByTestId('alert-modal-title');
+ const findGlModalBody = () => wrapper.findByTestId('alert-modal-body');
+ const findGlModalDetails = () => wrapper.findByTestId('alert-modal-details');
+ const findGlLink = () => wrapper.findComponent(GlLink);
+ const findGlRemindButton = () => wrapper.findByTestId('alert-modal-remind-button');
+ const findGlUpgradeButton = () => wrapper.findByTestId('alert-modal-upgrade-button');
+
+ describe('template defaults', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders visible critical security alert modal', () => {
+ expect(findGlModal().props('visible')).toBe(true);
+ });
+
+ it('renders the modal title correctly', () => {
+ expect(findGlModalTitle().text()).toBe(wrapper.vm.$options.i18n.modalTitle);
+ });
+
+ it('renders modal body without suggested versions', () => {
+ expect(findGlModalBody().text()).toBe(
+ sprintf(wrapper.vm.$options.i18n.modalBodyNoStableVersions, {
+ currentVersion: defaultProps.currentVersion,
+ }),
+ );
+ });
+
+ it('does not render modal details', () => {
+ expect(findGlModalDetails().exists()).toBe(false);
+ });
+
+ it(`tracks render ${TRACKING_LABELS.MODAL} correctly`, () => {
+ expectDispatchedTracking(TRACKING_ACTIONS.RENDER, TRACKING_LABELS.MODAL);
+ });
+
+ it(`tracks click ${TRACKING_LABELS.DISMISS} when close button clicked`, async () => {
+ await findGlModal().vm.$emit('close');
+
+ expectDispatchedTracking(TRACKING_ACTIONS.CLICK_BUTTON, TRACKING_LABELS.DISMISS);
+ });
+
+ describe('Learn more link', () => {
+ it('renders with correct text and link', () => {
+ expect(findGlLink().text()).toBe(wrapper.vm.$options.i18n.learnMore);
+ expect(findGlLink().attributes('href')).toBe(ABOUT_RELEASES_PAGE);
+ });
+
+ it(`tracks click ${TRACKING_LABELS.LEARN_MORE_LINK} when clicked`, async () => {
+ await findGlLink().vm.$emit('click');
+
+ expectDispatchedTracking(TRACKING_ACTIONS.CLICK_LINK, TRACKING_LABELS.LEARN_MORE_LINK);
+ });
+ });
+
+ describe('Remind me button', () => {
+ beforeEach(() => {
+ wrapper.vm.$refs.alertModal.hide = jest.fn();
+ });
+
+ it('renders with correct text', () => {
+ expect(findGlRemindButton().text()).toBe(wrapper.vm.$options.i18n.secondaryButtonText);
+ });
+
+ it(`tracks click ${TRACKING_LABELS.REMIND_ME_BTN} when clicked`, async () => {
+ await findGlRemindButton().vm.$emit('click');
+
+ expectDispatchedTracking(TRACKING_ACTIONS.CLICK_BUTTON, TRACKING_LABELS.REMIND_ME_BTN);
+ });
+
+ it('calls setHideAlertModalCookie with the currentVersion when clicked', async () => {
+ jest.spyOn(utils, 'setHideAlertModalCookie');
+ await findGlRemindButton().vm.$emit('click');
+
+ expect(utils.setHideAlertModalCookie).toHaveBeenCalledWith(defaultProps.currentVersion);
+ });
+
+ it('hides the modal', async () => {
+ await findGlRemindButton().vm.$emit('click');
+
+ expect(wrapper.vm.$refs.alertModal.hide).toHaveBeenCalled();
+ });
+ });
+
+ describe('Upgrade button', () => {
+ it('renders with correct text and link', () => {
+ expect(findGlUpgradeButton().text()).toBe(wrapper.vm.$options.i18n.primaryButtonText);
+ expect(findGlUpgradeButton().attributes('href')).toBe(UPGRADE_DOCS_URL);
+ });
+
+ it(`tracks click ${TRACKING_LABELS.UPGRADE_BTN_LINK} when clicked`, async () => {
+ await findGlUpgradeButton().vm.$emit('click');
+
+ expectDispatchedTracking(TRACKING_ACTIONS.CLICK_LINK, TRACKING_LABELS.UPGRADE_BTN_LINK);
+ });
+
+ it('calls setHideAlertModalCookie with the currentVersion when clicked', async () => {
+ jest.spyOn(utils, 'setHideAlertModalCookie');
+ await findGlUpgradeButton().vm.$emit('click');
+
+ expect(utils.setHideAlertModalCookie).toHaveBeenCalledWith(defaultProps.currentVersion);
+ });
+ });
+ });
+
+ describe('template with latestStableVersions', () => {
+ const latestStableVersions = ['88.8.3', '89.9.9', '90.0.0'];
+
+ beforeEach(() => {
+ createComponent({ latestStableVersions });
+ });
+
+ it('renders modal body with suggested versions', () => {
+ expect(findGlModalBody().text()).toBe(
+ sprintf(wrapper.vm.$options.i18n.modalBodyStableVersions, {
+ currentVersion: defaultProps.currentVersion,
+ latestStableVersions: latestStableVersions.join(', '),
+ }),
+ );
+ });
+ });
+
+ describe('template with details', () => {
+ const details = 'This is some details about the upgrade';
+
+ beforeEach(() => {
+ createComponent({ details });
+ });
+
+ it('renders modal details', () => {
+ expect(findGlModalDetails().text()).toBe(
+ sprintf(wrapper.vm.$options.i18n.modalDetails, { details }),
+ );
+ });
+ });
+
+ describe('when modal is hidden by cookie', () => {
+ beforeEach(() => {
+ jest.spyOn(utils, 'getHideAlertModalCookie').mockReturnValue(true);
+ createComponent();
+ });
+
+ it('renders modal with visibility false', () => {
+ expect(findGlModal().props('visible')).toBe(false);
+ });
+
+ it(`does not track render ${TRACKING_LABELS.MODAL} correctly`, () => {
+ expect(trackingSpy).not.toHaveBeenCalledWith(undefined, TRACKING_ACTIONS.RENDER, {
+ label: TRACKING_LABELS.MODAL,
+ property: defaultProps.currentVersion,
+ });
+ });
+ });
+});
diff --git a/spec/frontend/gitlab_version_check/components/security_patch_upgrade_alert_spec.js b/spec/frontend/gitlab_version_check/components/security_patch_upgrade_alert_spec.js
new file mode 100644
index 00000000000..665dacd5c47
--- /dev/null
+++ b/spec/frontend/gitlab_version_check/components/security_patch_upgrade_alert_spec.js
@@ -0,0 +1,84 @@
+import { GlAlert, GlButton, GlLink, GlSprintf } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
+import SecurityPatchUpgradeAlert from '~/gitlab_version_check/components/security_patch_upgrade_alert.vue';
+import { UPGRADE_DOCS_URL, ABOUT_RELEASES_PAGE } from '~/gitlab_version_check/constants';
+
+describe('SecurityPatchUpgradeAlert', () => {
+ let wrapper;
+ let trackingSpy;
+
+ const defaultProps = {
+ currentVersion: '99.9',
+ };
+
+ const createComponent = () => {
+ trackingSpy = mockTracking(undefined, undefined, jest.spyOn);
+
+ wrapper = shallowMount(SecurityPatchUpgradeAlert, {
+ propsData: {
+ ...defaultProps,
+ },
+ stubs: {
+ GlAlert,
+ GlSprintf,
+ },
+ });
+ };
+
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ const findGlAlert = () => wrapper.findComponent(GlAlert);
+ const findGlButton = () => wrapper.findComponent(GlButton);
+ const findGlLink = () => wrapper.findComponent(GlLink);
+
+ describe('template', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders non-dismissible GlAlert with version information', () => {
+ expect(findGlAlert().text()).toContain(
+ `You are currently on version ${defaultProps.currentVersion}.`,
+ );
+ expect(findGlAlert().props('dismissible')).toBe(false);
+ });
+
+ it('tracks render security_patch_upgrade_alert correctly', () => {
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'render', {
+ label: 'security_patch_upgrade_alert',
+ property: defaultProps.currentVersion,
+ });
+ });
+
+ it('renders GlLink with correct text and link', () => {
+ expect(findGlLink().text()).toBe('Learn more about this critical security release.');
+ expect(findGlLink().attributes('href')).toBe(ABOUT_RELEASES_PAGE);
+ });
+
+ it('tracks click security_patch_upgrade_alert_learn_more when link is clicked', async () => {
+ await findGlLink().vm.$emit('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_link', {
+ label: 'security_patch_upgrade_alert_learn_more',
+ property: defaultProps.currentVersion,
+ });
+ });
+
+ it('renders GlButton with correct text and link', () => {
+ expect(findGlButton().text()).toBe('Upgrade now');
+ expect(findGlButton().attributes('href')).toBe(UPGRADE_DOCS_URL);
+ });
+
+ it('tracks click security_patch_upgrade_alert_upgrade_now when button is clicked', async () => {
+ await findGlButton().vm.$emit('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_link', {
+ label: 'security_patch_upgrade_alert_upgrade_now',
+ property: defaultProps.currentVersion,
+ });
+ });
+ });
+});
diff --git a/spec/frontend/gitlab_version_check/index_spec.js b/spec/frontend/gitlab_version_check/index_spec.js
index 8a11ff48bf2..92bc103cede 100644
--- a/spec/frontend/gitlab_version_check/index_spec.js
+++ b/spec/frontend/gitlab_version_check/index_spec.js
@@ -1,116 +1,52 @@
-import Vue from 'vue';
-import * as Sentry from '@sentry/browser';
-import MockAdapter from 'axios-mock-adapter';
-import axios from '~/lib/utils/axios_utils';
+import { createWrapper } from '@vue/test-utils';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import initGitlabVersionCheck from '~/gitlab_version_check';
+import {
+ VERSION_CHECK_BADGE_NO_PROP_FIXTURE,
+ VERSION_CHECK_BADGE_NO_SEVERITY_FIXTURE,
+ VERSION_CHECK_BADGE_FIXTURE,
+ VERSION_CHECK_BADGE_FINDER,
+ VERSION_BADGE_TEXT,
+ SECURITY_PATCH_FIXTURE,
+ SECURITY_PATCH_FINDER,
+ SECURITY_PATCH_TEXT,
+ SECURITY_MODAL_FIXTURE,
+ SECURITY_MODAL_FINDER,
+ SECURITY_MODAL_TEXT,
+} from './mock_data';
describe('initGitlabVersionCheck', () => {
- let originalGon;
- let mock;
- let vueApps;
+ let wrapper;
- const defaultResponse = {
- code: 200,
- res: { severity: 'success' },
- };
-
- const dummyGon = {
- relative_url_root: '/',
- };
-
- const createApp = async (mockResponse, htmlClass) => {
- originalGon = window.gon;
-
- const response = {
- ...defaultResponse,
- ...mockResponse,
- };
-
- mock = new MockAdapter(axios);
- mock.onGet().replyOnce(response.code, response.res);
-
- setHTMLFixture(`<div class="${htmlClass}"></div>`);
-
- vueApps = await initGitlabVersionCheck();
+ const createApp = (fixture) => {
+ setHTMLFixture(fixture);
+ initGitlabVersionCheck();
+ wrapper = createWrapper(document.body);
};
afterEach(() => {
- mock.restore();
- window.gon = originalGon;
resetHTMLFixture();
});
- describe('with no .js-gitlab-version-check-badge elements', () => {
- beforeEach(async () => {
- await createApp();
- });
-
- it('does not make axios GET request', () => {
- expect(mock.history.get.length).toBe(0);
- });
-
- it('does not render the Version Check Badge', () => {
- expect(vueApps).toBeNull();
- });
- });
-
- describe('with .js-gitlab-version-check-badge element but API errors', () => {
- beforeEach(async () => {
- jest.spyOn(Sentry, 'captureException');
- await createApp({ code: 500, res: null }, 'js-gitlab-version-check-badge');
- });
-
- it('does make axios GET request', () => {
- expect(mock.history.get.length).toBe(1);
- expect(mock.history.get[0].url).toContain('/admin/version_check.json');
- });
-
- it('logs error to Sentry', () => {
- expect(Sentry.captureException).toHaveBeenCalled();
- });
-
- it('does not render the Version Check Badge', () => {
- expect(vueApps).toBeNull();
- });
- });
-
- describe('with .js-gitlab-version-check-badge element and successful API call', () => {
- beforeEach(async () => {
- await createApp({}, 'js-gitlab-version-check-badge');
- });
-
- it('does make axios GET request', () => {
- expect(mock.history.get.length).toBe(1);
- expect(mock.history.get[0].url).toContain('/admin/version_check.json');
- });
-
- it('does render the Version Check Badge', () => {
- expect(vueApps).toHaveLength(1);
- expect(vueApps[0]).toBeInstanceOf(Vue);
- });
- });
-
describe.each`
- root | description
- ${'/'} | ${'not used (uses its own (sub)domain)'}
- ${'/gitlab'} | ${'custom path'}
- ${'/service/gitlab'} | ${'custom path with 2 depth'}
- `('path for version_check.json', ({ root, description }) => {
- describe(`when relative url is ${description}: ${root}`, () => {
- beforeEach(async () => {
- originalGon = window.gon;
- window.gon = { ...dummyGon };
- window.gon.relative_url_root = root;
- await createApp({}, 'js-gitlab-version-check-badge');
- });
-
- it('reflects the relative url setting', () => {
- expect(mock.history.get.length).toBe(1);
-
- const pathRegex = new RegExp(`^${root}`);
- expect(mock.history.get[0].url).toMatch(pathRegex);
- });
+ description | fixture | finders | componentTexts
+ ${'with no version check elements'} | ${'<div></div>'} | ${[]} | ${[]}
+ ${'with version check badge el but no prop data'} | ${VERSION_CHECK_BADGE_NO_PROP_FIXTURE} | ${[VERSION_CHECK_BADGE_FINDER]} | ${[undefined]}
+ ${'with version check badge el but no severity data'} | ${VERSION_CHECK_BADGE_NO_SEVERITY_FIXTURE} | ${[VERSION_CHECK_BADGE_FINDER]} | ${[undefined]}
+ ${'with version check badge el and version data'} | ${VERSION_CHECK_BADGE_FIXTURE} | ${[VERSION_CHECK_BADGE_FINDER]} | ${[VERSION_BADGE_TEXT]}
+ ${'with security patch el'} | ${SECURITY_PATCH_FIXTURE} | ${[SECURITY_PATCH_FINDER]} | ${[SECURITY_PATCH_TEXT]}
+ ${'with security patch and version badge els'} | ${`${SECURITY_PATCH_FIXTURE}${VERSION_CHECK_BADGE_FIXTURE}`} | ${[SECURITY_PATCH_FINDER, VERSION_CHECK_BADGE_FINDER]} | ${[SECURITY_PATCH_TEXT, VERSION_BADGE_TEXT]}
+ ${'with security modal el'} | ${SECURITY_MODAL_FIXTURE} | ${[SECURITY_MODAL_FINDER]} | ${[SECURITY_MODAL_TEXT]}
+ ${'with security modal, security patch, and version badge els'} | ${`${SECURITY_PATCH_FIXTURE}${SECURITY_MODAL_FIXTURE}${VERSION_CHECK_BADGE_FIXTURE}`} | ${[SECURITY_PATCH_FINDER, SECURITY_MODAL_FINDER, VERSION_CHECK_BADGE_FINDER]} | ${[SECURITY_PATCH_TEXT, SECURITY_MODAL_TEXT, VERSION_BADGE_TEXT]}
+ `('$description', ({ fixture, finders, componentTexts }) => {
+ beforeEach(() => {
+ createApp(fixture);
+ });
+
+ it(`correctly renders the Version Check Components`, () => {
+ const renderedComponentTexts = finders.map((f) => wrapper.find(f)?.element?.innerText.trim());
+
+ expect(renderedComponentTexts).toStrictEqual(componentTexts);
});
});
});
diff --git a/spec/frontend/gitlab_version_check/mock_data.js b/spec/frontend/gitlab_version_check/mock_data.js
new file mode 100644
index 00000000000..707d45550eb
--- /dev/null
+++ b/spec/frontend/gitlab_version_check/mock_data.js
@@ -0,0 +1,22 @@
+export const VERSION_CHECK_BADGE_NO_PROP_FIXTURE =
+ '<div class="js-gitlab-version-check-badge"></div>';
+
+export const VERSION_CHECK_BADGE_NO_SEVERITY_FIXTURE = `<div class="js-gitlab-version-check-badge" data-version='{ "size": "sm" }'></div>`;
+
+export const VERSION_CHECK_BADGE_FIXTURE = `<div class="js-gitlab-version-check-badge" data-version='{ "severity": "success" }'></div>`;
+
+export const VERSION_CHECK_BADGE_FINDER = '[data-testid="badge-click-wrapper"]';
+
+export const VERSION_BADGE_TEXT = 'Up to date';
+
+export const SECURITY_PATCH_FIXTURE = `<div id="js-security-patch-upgrade-alert" data-current-version="15.1"></div>`;
+
+export const SECURITY_PATCH_FINDER = 'h2';
+
+export const SECURITY_PATCH_TEXT = 'Critical security upgrade available';
+
+export const SECURITY_MODAL_FIXTURE = `<div id="js-security-patch-upgrade-alert-modal" data-current-version="15.1" data-version='{ "details": "test details", "latest-stable-versions": "[]" }'></div>`;
+
+export const SECURITY_MODAL_FINDER = '[data-testid="alert-modal-title"]';
+
+export const SECURITY_MODAL_TEXT = 'Important notice - Critical security release';
diff --git a/spec/frontend/gitlab_version_check/utils_spec.js b/spec/frontend/gitlab_version_check/utils_spec.js
new file mode 100644
index 00000000000..6126d88dfec
--- /dev/null
+++ b/spec/frontend/gitlab_version_check/utils_spec.js
@@ -0,0 +1,35 @@
+import { parseBoolean, getCookie, setCookie } from '~/lib/utils/common_utils';
+import { getHideAlertModalCookie, setHideAlertModalCookie } from '~/gitlab_version_check/utils';
+import { COOKIE_EXPIRATION, COOKIE_SUFFIX } from '~/gitlab_version_check/constants';
+
+jest.mock('~/lib/utils/common_utils', () => ({
+ parseBoolean: jest.fn().mockReturnValue(true),
+ getCookie: jest.fn().mockReturnValue('true'),
+ setCookie: jest.fn(),
+}));
+
+describe('GitLab Version Check Utils', () => {
+ describe('setHideAlertModalCookie', () => {
+ it('properly generates a key based on the currentVersion and sets Cookie to `true`', () => {
+ const currentVersion = '99.9.9';
+
+ setHideAlertModalCookie(currentVersion);
+
+ expect(setCookie).toHaveBeenCalledWith(`${currentVersion}${COOKIE_SUFFIX}`, true, {
+ expires: COOKIE_EXPIRATION,
+ });
+ });
+ });
+
+ describe('getHideAlertModalCookie', () => {
+ it('properly generates a key based on the currentVersion, fetches said Cooke, and parsesBoolean it', () => {
+ const currentVersion = '99.9.9';
+
+ const res = getHideAlertModalCookie(currentVersion);
+
+ expect(getCookie).toHaveBeenCalledWith(`${currentVersion}${COOKIE_SUFFIX}`);
+ expect(parseBoolean).toHaveBeenCalledWith('true');
+ expect(res).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/groups/components/app_spec.js b/spec/frontend/groups/components/app_spec.js
index 091ec17d58e..140609161d4 100644
--- a/spec/frontend/groups/components/app_spec.js
+++ b/spec/frontend/groups/components/app_spec.js
@@ -1,7 +1,7 @@
import { GlModal, GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
import AxiosMockAdapter from 'axios-mock-adapter';
import Vue, { nextTick } from 'vue';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { createAlert } from '~/flash';
import appComponent from '~/groups/components/app.vue';
@@ -10,8 +10,6 @@ import groupItemComponent from '~/groups/components/group_item.vue';
import eventHub from '~/groups/event_hub';
import GroupsService from '~/groups/service/groups_service';
import GroupsStore from '~/groups/store/groups_store';
-import EmptyState from '~/groups/components/empty_state.vue';
-import GroupsComponent from '~/groups/components/groups.vue';
import axios from '~/lib/utils/axios_utils';
import * as urlUtilities from '~/lib/utils/url_utility';
import setWindowLocation from 'helpers/set_window_location_helper';
@@ -43,7 +41,7 @@ describe('AppComponent', () => {
const createShallowComponent = ({ propsData = {} } = {}) => {
store.state.pageInfo = mockPageInfo;
- wrapper = shallowMount(appComponent, {
+ wrapper = shallowMountExtended(appComponent, {
propsData: {
store,
service,
@@ -51,6 +49,9 @@ describe('AppComponent', () => {
containerId: 'js-groups-tree',
...propsData,
},
+ scopedSlots: {
+ 'empty-state': '<div data-testid="empty-state" />',
+ },
mocks: {
$toast,
},
@@ -68,6 +69,7 @@ describe('AppComponent', () => {
mock.onGet('/dashboard/groups.json').reply(200, mockGroups);
Vue.component('GroupFolder', groupFolderComponent);
Vue.component('GroupItem', groupItemComponent);
+ setWindowLocation('?filter=foobar');
document.body.innerHTML = `
<div id="js-groups-tree">
@@ -149,13 +151,13 @@ describe('AppComponent', () => {
expect(vm.fetchGroups).toHaveBeenCalledWith({
page: null,
- filterGroupsBy: null,
+ filterGroupsBy: 'foobar',
sortBy: null,
updatePagination: true,
archived: null,
});
return fetchPromise.then(() => {
- expect(vm.updateGroups).toHaveBeenCalled();
+ expect(vm.updateGroups).toHaveBeenCalledWith(mockSearchedGroups, true);
});
});
});
@@ -375,32 +377,16 @@ describe('AppComponent', () => {
expect(vm.store.setSearchedGroups).toHaveBeenCalledWith(mockGroups);
});
- it('should set `isSearchEmpty` prop based on groups count and `filter` query param', () => {
- setWindowLocation('?filter=foobar');
- createShallowComponent();
-
- vm.updateGroups(mockGroups);
-
- expect(vm.isSearchEmpty).toBe(false);
-
- vm.updateGroups([]);
-
- expect(vm.isSearchEmpty).toBe(true);
- });
-
describe.each`
- action | groups | fromSearch | shouldRenderEmptyState | searchEmpty
- ${'subgroups_and_projects'} | ${[]} | ${false} | ${true} | ${false}
- ${''} | ${[]} | ${false} | ${false} | ${false}
- ${'subgroups_and_projects'} | ${mockGroups} | ${false} | ${false} | ${false}
- ${'subgroups_and_projects'} | ${[]} | ${true} | ${false} | ${true}
+ groups | fromSearch | shouldRenderEmptyState | shouldRenderSearchEmptyState
+ ${[]} | ${false} | ${true} | ${false}
+ ${mockGroups} | ${false} | ${false} | ${false}
+ ${[]} | ${true} | ${false} | ${true}
`(
- 'when `action` is $action, `groups` is $groups, and `fromSearch` is $fromSearch',
- ({ action, groups, fromSearch, shouldRenderEmptyState, searchEmpty }) => {
+ 'when `groups` is $groups, and `fromSearch` is $fromSearch',
+ ({ groups, fromSearch, shouldRenderEmptyState, shouldRenderSearchEmptyState }) => {
it(`${shouldRenderEmptyState ? 'renders' : 'does not render'} empty state`, async () => {
- createShallowComponent({
- propsData: { action, renderEmptyState: true },
- });
+ createShallowComponent();
await waitForPromises();
@@ -408,28 +394,14 @@ describe('AppComponent', () => {
await nextTick();
- expect(wrapper.findComponent(EmptyState).exists()).toBe(shouldRenderEmptyState);
- expect(wrapper.findComponent(GroupsComponent).props('searchEmpty')).toBe(searchEmpty);
+ expect(wrapper.findByTestId('empty-state').exists()).toBe(shouldRenderEmptyState);
+ expect(wrapper.findByTestId('search-empty-state').exists()).toBe(
+ shouldRenderSearchEmptyState,
+ );
});
},
);
});
-
- describe('when `action` is subgroups_and_projects, `groups` is [], `fromSearch` is `false`, and `renderEmptyState` is `false`', () => {
- it('renders legacy empty state', async () => {
- createShallowComponent({
- propsData: { action: 'subgroups_and_projects' },
- });
-
- vm.updateGroups([], false);
-
- await nextTick();
-
- expect(
- document.querySelector('[data-testid="legacy-empty-state"]').classList.contains('hidden'),
- ).toBe(false);
- });
- });
});
describe('created', () => {
diff --git a/spec/frontend/groups/components/empty_state_spec.js b/spec/frontend/groups/components/empty_state_spec.js
deleted file mode 100644
index fbeaa32b1ec..00000000000
--- a/spec/frontend/groups/components/empty_state_spec.js
+++ /dev/null
@@ -1,78 +0,0 @@
-import { GlEmptyState } from '@gitlab/ui';
-
-import { mountExtended } from 'jest/__helpers__/vue_test_utils_helper';
-import EmptyState from '~/groups/components/empty_state.vue';
-
-let wrapper;
-
-const defaultProvide = {
- newProjectIllustration: '/assets/illustrations/project-create-new-sm.svg',
- newProjectPath: '/projects/new?namespace_id=231',
- newSubgroupIllustration: '/assets/illustrations/group-new.svg',
- newSubgroupPath: '/groups/new?parent_id=231',
- emptySubgroupIllustration: '/assets/illustrations/empty-state/empty-subgroup-md.svg',
- canCreateSubgroups: true,
- canCreateProjects: true,
-};
-
-const createComponent = ({ provide = {} } = {}) => {
- wrapper = mountExtended(EmptyState, {
- provide: {
- ...defaultProvide,
- ...provide,
- },
- });
-};
-
-afterEach(() => {
- wrapper.destroy();
-});
-
-const findNewSubgroupLink = () =>
- wrapper.findByRole('link', {
- name: new RegExp(EmptyState.i18n.withLinks.subgroup.title),
- });
-const findNewProjectLink = () =>
- wrapper.findByRole('link', {
- name: new RegExp(EmptyState.i18n.withLinks.project.title),
- });
-const findNewSubgroupIllustration = () =>
- wrapper.findByRole('img', { name: EmptyState.i18n.withLinks.subgroup.title });
-const findNewProjectIllustration = () =>
- wrapper.findByRole('img', { name: EmptyState.i18n.withLinks.project.title });
-
-describe('EmptyState', () => {
- describe('when user has permission to create a subgroup', () => {
- it('renders `Create new subgroup` link', () => {
- createComponent();
-
- expect(findNewSubgroupLink().attributes('href')).toBe(defaultProvide.newSubgroupPath);
- expect(findNewSubgroupIllustration().attributes('src')).toBe(
- defaultProvide.newSubgroupIllustration,
- );
- });
- });
-
- describe('when user has permission to create a project', () => {
- it('renders `Create new project` link', () => {
- createComponent();
-
- expect(findNewProjectLink().attributes('href')).toBe(defaultProvide.newProjectPath);
- expect(findNewProjectIllustration().attributes('src')).toBe(
- defaultProvide.newProjectIllustration,
- );
- });
- });
-
- describe('when user does not have permissions to create a project or a subgroup', () => {
- it('renders empty state', () => {
- createComponent({ provide: { canCreateSubgroups: false, canCreateProjects: false } });
-
- expect(wrapper.findComponent(GlEmptyState).props()).toMatchObject({
- title: EmptyState.i18n.withoutLinks.title,
- description: EmptyState.i18n.withoutLinks.description,
- svgPath: defaultProvide.emptySubgroupIllustration,
- });
- });
- });
-});
diff --git a/spec/frontend/groups/components/empty_states/archived_projects_empty_state_spec.js b/spec/frontend/groups/components/empty_states/archived_projects_empty_state_spec.js
new file mode 100644
index 00000000000..be61ffa92b4
--- /dev/null
+++ b/spec/frontend/groups/components/empty_states/archived_projects_empty_state_spec.js
@@ -0,0 +1,27 @@
+import { GlEmptyState } from '@gitlab/ui';
+
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import ArchivedProjectsEmptyState from '~/groups/components/empty_states/archived_projects_empty_state.vue';
+
+let wrapper;
+
+const defaultProvide = {
+ newProjectIllustration: '/assets/illustrations/project-create-new-sm.svg',
+};
+
+const createComponent = () => {
+ wrapper = mountExtended(ArchivedProjectsEmptyState, {
+ provide: defaultProvide,
+ });
+};
+
+describe('ArchivedProjectsEmptyState', () => {
+ it('renders empty state', () => {
+ createComponent();
+
+ expect(wrapper.findComponent(GlEmptyState).props()).toMatchObject({
+ title: ArchivedProjectsEmptyState.i18n.title,
+ svgPath: defaultProvide.newProjectIllustration,
+ });
+ });
+});
diff --git a/spec/frontend/groups/components/empty_states/shared_projects_empty_state_spec.js b/spec/frontend/groups/components/empty_states/shared_projects_empty_state_spec.js
new file mode 100644
index 00000000000..c4ace1be1f3
--- /dev/null
+++ b/spec/frontend/groups/components/empty_states/shared_projects_empty_state_spec.js
@@ -0,0 +1,27 @@
+import { GlEmptyState } from '@gitlab/ui';
+
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import SharedProjectsEmptyState from '~/groups/components/empty_states/shared_projects_empty_state.vue';
+
+let wrapper;
+
+const defaultProvide = {
+ newProjectIllustration: '/assets/illustrations/project-create-new-sm.svg',
+};
+
+const createComponent = () => {
+ wrapper = mountExtended(SharedProjectsEmptyState, {
+ provide: defaultProvide,
+ });
+};
+
+describe('SharedProjectsEmptyState', () => {
+ it('renders empty state', () => {
+ createComponent();
+
+ expect(wrapper.findComponent(GlEmptyState).props()).toMatchObject({
+ title: SharedProjectsEmptyState.i18n.title,
+ svgPath: defaultProvide.newProjectIllustration,
+ });
+ });
+});
diff --git a/spec/frontend/groups/components/empty_states/subgroups_and_projects_empty_state_spec.js b/spec/frontend/groups/components/empty_states/subgroups_and_projects_empty_state_spec.js
new file mode 100644
index 00000000000..75edc602fbf
--- /dev/null
+++ b/spec/frontend/groups/components/empty_states/subgroups_and_projects_empty_state_spec.js
@@ -0,0 +1,78 @@
+import { GlEmptyState } from '@gitlab/ui';
+
+import { mountExtended } from 'jest/__helpers__/vue_test_utils_helper';
+import SubgroupsAndProjectsEmptyState from '~/groups/components/empty_states/subgroups_and_projects_empty_state.vue';
+
+let wrapper;
+
+const defaultProvide = {
+ newProjectIllustration: '/assets/illustrations/project-create-new-sm.svg',
+ newProjectPath: '/projects/new?namespace_id=231',
+ newSubgroupIllustration: '/assets/illustrations/group-new.svg',
+ newSubgroupPath: '/groups/new?parent_id=231',
+ emptySubgroupIllustration: '/assets/illustrations/empty-state/empty-subgroup-md.svg',
+ canCreateSubgroups: true,
+ canCreateProjects: true,
+};
+
+const createComponent = ({ provide = {} } = {}) => {
+ wrapper = mountExtended(SubgroupsAndProjectsEmptyState, {
+ provide: {
+ ...defaultProvide,
+ ...provide,
+ },
+ });
+};
+
+afterEach(() => {
+ wrapper.destroy();
+});
+
+const findNewSubgroupLink = () =>
+ wrapper.findByRole('link', {
+ name: new RegExp(SubgroupsAndProjectsEmptyState.i18n.withLinks.subgroup.title),
+ });
+const findNewProjectLink = () =>
+ wrapper.findByRole('link', {
+ name: new RegExp(SubgroupsAndProjectsEmptyState.i18n.withLinks.project.title),
+ });
+const findNewSubgroupIllustration = () =>
+ wrapper.findByRole('img', { name: SubgroupsAndProjectsEmptyState.i18n.withLinks.subgroup.title });
+const findNewProjectIllustration = () =>
+ wrapper.findByRole('img', { name: SubgroupsAndProjectsEmptyState.i18n.withLinks.project.title });
+
+describe('SubgroupsAndProjectsEmptyState', () => {
+ describe('when user has permission to create a subgroup', () => {
+ it('renders `Create new subgroup` link', () => {
+ createComponent();
+
+ expect(findNewSubgroupLink().attributes('href')).toBe(defaultProvide.newSubgroupPath);
+ expect(findNewSubgroupIllustration().attributes('src')).toBe(
+ defaultProvide.newSubgroupIllustration,
+ );
+ });
+ });
+
+ describe('when user has permission to create a project', () => {
+ it('renders `Create new project` link', () => {
+ createComponent();
+
+ expect(findNewProjectLink().attributes('href')).toBe(defaultProvide.newProjectPath);
+ expect(findNewProjectIllustration().attributes('src')).toBe(
+ defaultProvide.newProjectIllustration,
+ );
+ });
+ });
+
+ describe('when user does not have permissions to create a project or a subgroup', () => {
+ it('renders empty state', () => {
+ createComponent({ provide: { canCreateSubgroups: false, canCreateProjects: false } });
+
+ expect(wrapper.findComponent(GlEmptyState).props()).toMatchObject({
+ title: SubgroupsAndProjectsEmptyState.i18n.withoutLinks.title,
+ description: SubgroupsAndProjectsEmptyState.i18n.withoutLinks.description,
+ svgPath: defaultProvide.emptySubgroupIllustration,
+ });
+ });
+ });
+});
diff --git a/spec/frontend/groups/components/group_name_and_path_spec.js b/spec/frontend/groups/components/group_name_and_path_spec.js
index 823d2ed286a..9965b608f27 100644
--- a/spec/frontend/groups/components/group_name_and_path_spec.js
+++ b/spec/frontend/groups/components/group_name_and_path_spec.js
@@ -398,7 +398,7 @@ describe('GroupNameAndPath', () => {
expect(findAlert().exists()).toBe(true);
expect(findAlert().findByRole('link', { name: 'Learn more' }).attributes('href')).toBe(
- helpPagePath('user/group/index', {
+ helpPagePath('user/group/manage', {
anchor: 'change-a-groups-path',
}),
);
diff --git a/spec/frontend/groups/components/groups_spec.js b/spec/frontend/groups/components/groups_spec.js
index 0cbb6cc8309..cae29a8f15a 100644
--- a/spec/frontend/groups/components/groups_spec.js
+++ b/spec/frontend/groups/components/groups_spec.js
@@ -16,7 +16,6 @@ describe('GroupsComponent', () => {
const defaultPropsData = {
groups: mockGroups,
pageInfo: mockPageInfo,
- searchEmpty: false,
};
const createComponent = ({ propsData } = {}) => {
@@ -69,14 +68,5 @@ describe('GroupsComponent', () => {
expect(findPaginationLinks().exists()).toBe(true);
expect(wrapper.findComponent(GlEmptyState).exists()).toBe(false);
});
-
- it('should render empty search message when `searchEmpty` is `true`', () => {
- createComponent({ propsData: { searchEmpty: true } });
-
- expect(wrapper.findComponent(GlEmptyState).props()).toMatchObject({
- title: GroupsComponent.i18n.emptyStateTitle,
- description: GroupsComponent.i18n.emptyStateDescription,
- });
- });
});
});
diff --git a/spec/frontend/groups/components/overview_tabs_spec.js b/spec/frontend/groups/components/overview_tabs_spec.js
index b615679dcc5..d1ae2c4be17 100644
--- a/spec/frontend/groups/components/overview_tabs_spec.js
+++ b/spec/frontend/groups/components/overview_tabs_spec.js
@@ -1,11 +1,13 @@
import { GlSorting, GlSortingItem, GlTab } from '@gitlab/ui';
-import { nextTick } from 'vue';
-import { createLocalVue } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
import AxiosMockAdapter from 'axios-mock-adapter';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import OverviewTabs from '~/groups/components/overview_tabs.vue';
import GroupsApp from '~/groups/components/app.vue';
import GroupFolderComponent from '~/groups/components/group_folder.vue';
+import SubgroupsAndProjectsEmptyState from '~/groups/components/empty_states/subgroups_and_projects_empty_state.vue';
+import SharedProjectsEmptyState from '~/groups/components/empty_states/shared_projects_empty_state.vue';
+import ArchivedProjectsEmptyState from '~/groups/components/empty_states/archived_projects_empty_state.vue';
import GroupsStore from '~/groups/store/groups_store';
import GroupsService from '~/groups/service/groups_service';
import { createRouter } from '~/groups/init_overview_tabs';
@@ -17,9 +19,9 @@ import {
OVERVIEW_TABS_SORTING_ITEMS,
} from '~/groups/constants';
import axios from '~/lib/utils/axios_utils';
+import waitForPromises from 'helpers/wait_for_promises';
-const localVue = createLocalVue();
-localVue.component('GroupFolder', GroupFolderComponent);
+Vue.component('GroupFolder', GroupFolderComponent);
const router = createRouter();
const [SORTING_ITEM_NAME, , SORTING_ITEM_UPDATED] = OVERVIEW_TABS_SORTING_ITEMS;
@@ -57,7 +59,6 @@ describe('OverviewTabs', () => {
...defaultProvide,
...provide,
},
- localVue,
mocks: { $route: route, $router: routerMock },
});
@@ -71,6 +72,7 @@ describe('OverviewTabs', () => {
beforeEach(() => {
axiosMock = new AxiosMockAdapter(axios);
+ axiosMock.onGet({ data: [] });
});
afterEach(() => {
@@ -78,7 +80,7 @@ describe('OverviewTabs', () => {
axiosMock.restore();
});
- it('renders `Subgroups and projects` tab with `GroupsApp` component', async () => {
+ it('renders `Subgroups and projects` tab with `GroupsApp` component with correct empty state', async () => {
await createComponent();
const tabPanel = findTabPanels().at(0);
@@ -92,11 +94,14 @@ describe('OverviewTabs', () => {
store: new GroupsStore({ showSchemaMarkup: true }),
service: new GroupsService(defaultProvide.endpoints[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS]),
hideProjects: false,
- renderEmptyState: true,
});
+
+ await waitForPromises();
+
+ expect(wrapper.findComponent(SubgroupsAndProjectsEmptyState).exists()).toBe(true);
});
- it('renders `Shared projects` tab and renders `GroupsApp` component after clicking tab', async () => {
+ it('renders `Shared projects` tab and renders `GroupsApp` component with correct empty state after clicking tab', async () => {
await createComponent();
const tabPanel = findTabPanels().at(1);
@@ -113,13 +118,16 @@ describe('OverviewTabs', () => {
store: new GroupsStore(),
service: new GroupsService(defaultProvide.endpoints[ACTIVE_TAB_SHARED]),
hideProjects: false,
- renderEmptyState: false,
});
expect(tabPanel.vm.$attrs.lazy).toBe(false);
+
+ await waitForPromises();
+
+ expect(wrapper.findComponent(SharedProjectsEmptyState).exists()).toBe(true);
});
- it('renders `Archived projects` tab and renders `GroupsApp` component after clicking tab', async () => {
+ it('renders `Archived projects` tab and renders `GroupsApp` component with correct empty state after clicking tab', async () => {
await createComponent();
const tabPanel = findTabPanels().at(2);
@@ -136,10 +144,13 @@ describe('OverviewTabs', () => {
store: new GroupsStore(),
service: new GroupsService(defaultProvide.endpoints[ACTIVE_TAB_ARCHIVED]),
hideProjects: false,
- renderEmptyState: false,
});
expect(tabPanel.vm.$attrs.lazy).toBe(false);
+
+ await waitForPromises();
+
+ expect(wrapper.findComponent(ArchivedProjectsEmptyState).exists()).toBe(true);
});
it('sets `lazy` prop to `false` for initially active tab and `true` for all other tabs', async () => {
diff --git a/spec/frontend/header_search/components/app_spec.js b/spec/frontend/header_search/components/app_spec.js
index b0bfe2b45f0..c714c269ca0 100644
--- a/spec/frontend/header_search/components/app_spec.js
+++ b/spec/frontend/header_search/components/app_spec.js
@@ -180,7 +180,6 @@ describe('HeaderSearchApp', () => {
findHeaderSearchInput().vm.$emit('keydown', new KeyboardEvent({ key: 27 }));
await nextTick();
expect(findHeaderSearchDropdown().exists()).toBe(false);
- // only one event emmited from findHeaderSearchInput().vm.$emit('click');
expect(wrapper.emitted().expandSearchBar.length).toBe(1);
});
});
diff --git a/spec/frontend/ide/components/panes/right_spec.js b/spec/frontend/ide/components/panes/right_spec.js
index b7349b8fed1..294f5eee863 100644
--- a/spec/frontend/ide/components/panes/right_spec.js
+++ b/spec/frontend/ide/components/panes/right_spec.js
@@ -3,16 +3,12 @@ import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import CollapsibleSidebar from '~/ide/components/panes/collapsible_sidebar.vue';
import RightPane from '~/ide/components/panes/right.vue';
-import SwitchEditorsView from '~/ide/components/switch_editors/switch_editors_view.vue';
import { rightSidebarViews } from '~/ide/constants';
import { createStore } from '~/ide/stores';
import extendStore from '~/ide/stores/extend';
-import { __ } from '~/locale';
Vue.use(Vuex);
-const SWITCH_EDITORS_VIEW_NAME = 'switch-editors';
-
describe('ide/components/panes/right.vue', () => {
let wrapper;
let store;
@@ -45,7 +41,6 @@ describe('ide/components/panes/right.vue', () => {
it('renders collapsible-sidebar', () => {
expect(wrapper.findComponent(CollapsibleSidebar).props()).toMatchObject({
side: 'right',
- initOpenView: SWITCH_EDITORS_VIEW_NAME,
});
});
});
@@ -130,32 +125,4 @@ describe('ide/components/panes/right.vue', () => {
);
});
});
-
- describe('switch editors tab', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it.each`
- desc | canUseNewWebIde | expectedShow
- ${'is shown'} | ${true} | ${true}
- ${'is not shown'} | ${false} | ${false}
- `('with canUseNewWebIde=$canUseNewWebIde, $desc', async ({ canUseNewWebIde, expectedShow }) => {
- Object.assign(store.state, { canUseNewWebIde });
-
- await nextTick();
-
- expect(wrapper.findComponent(CollapsibleSidebar).props('extensionTabs')).toEqual(
- expect.arrayContaining([
- expect.objectContaining({
- show: expectedShow,
- title: __('Switch editors'),
- views: [
- { component: SwitchEditorsView, name: SWITCH_EDITORS_VIEW_NAME, keepAlive: true },
- ],
- }),
- ]),
- );
- });
- });
});
diff --git a/spec/frontend/ide/components/pipelines/list_spec.js b/spec/frontend/ide/components/pipelines/list_spec.js
index 545924c9c11..d82b97561f0 100644
--- a/spec/frontend/ide/components/pipelines/list_spec.js
+++ b/spec/frontend/ide/components/pipelines/list_spec.js
@@ -185,7 +185,7 @@ describe('IDE pipelines list', () => {
},
);
- expect(wrapper.text()).toContain('Found errors in your .gitlab-ci.yml:');
+ expect(wrapper.text()).toContain('Unable to create pipeline');
expect(wrapper.text()).toContain(yamlError);
});
});
diff --git a/spec/frontend/ide/components/repo_editor_spec.js b/spec/frontend/ide/components/repo_editor_spec.js
index 9921d8cba18..211fee31a9c 100644
--- a/spec/frontend/ide/components/repo_editor_spec.js
+++ b/spec/frontend/ide/components/repo_editor_spec.js
@@ -4,13 +4,17 @@ import { editor as monacoEditor, Range } from 'monaco-editor';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import { shallowMount } from '@vue/test-utils';
-import '~/behaviors/markdown/render_gfm';
import waitForPromises from 'helpers/wait_for_promises';
import { stubPerformanceWebAPI } from 'helpers/performance';
import { exampleConfigs, exampleFiles } from 'jest/ide/lib/editorconfig/mock_data';
-import { EDITOR_CODE_INSTANCE_FN, EDITOR_DIFF_INSTANCE_FN } from '~/editor/constants';
+import {
+ EDITOR_CODE_INSTANCE_FN,
+ EDITOR_DIFF_INSTANCE_FN,
+ EXTENSION_CI_SCHEMA_FILE_NAME_MATCH,
+} from '~/editor/constants';
import { EditorMarkdownExtension } from '~/editor/extensions/source_editor_markdown_ext';
import { EditorMarkdownPreviewExtension } from '~/editor/extensions/source_editor_markdown_livepreview_ext';
+import { CiSchemaExtension } from '~/editor/extensions/source_editor_ci_schema_ext';
import SourceEditor from '~/editor/source_editor';
import RepoEditor from '~/ide/components/repo_editor.vue';
import { leftSidebarViews, FILE_VIEW_MODE_PREVIEW, viewerTypes } from '~/ide/constants';
@@ -22,6 +26,9 @@ import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer
import SourceEditorInstance from '~/editor/source_editor_instance';
import { file } from '../helpers';
+jest.mock('~/behaviors/markdown/render_gfm');
+jest.mock('~/editor/extensions/source_editor_ci_schema_ext');
+
const PREVIEW_MARKDOWN_PATH = '/foo/bar/preview_markdown';
const CURRENT_PROJECT_ID = 'gitlab-org/gitlab';
@@ -46,6 +53,12 @@ const dummyFile = {
tempFile: true,
active: true,
},
+ ciConfig: {
+ ...file(EXTENSION_CI_SCHEMA_FILE_NAME_MATCH),
+ content: '',
+ tempFile: true,
+ active: true,
+ },
empty: {
...file('empty'),
tempFile: false,
@@ -101,6 +114,7 @@ describe('RepoEditor', () => {
let createDiffInstanceSpy;
let createModelSpy;
let applyExtensionSpy;
+ let removeExtensionSpy;
let extensionsStore;
const waitForEditorSetup = () =>
@@ -108,7 +122,7 @@ describe('RepoEditor', () => {
vm.$once('editorSetup', resolve);
});
- const createComponent = async ({ state = {}, activeFile = dummyFile.text } = {}) => {
+ const createComponent = async ({ state = {}, activeFile = dummyFile.text, flags = {} } = {}) => {
const store = prepareStore(state, activeFile);
wrapper = shallowMount(RepoEditor, {
store,
@@ -118,6 +132,9 @@ describe('RepoEditor', () => {
mocks: {
ContentViewer,
},
+ provide: {
+ glFeatures: flags,
+ },
});
await waitForPromises();
vm = wrapper.vm;
@@ -137,6 +154,7 @@ describe('RepoEditor', () => {
createDiffInstanceSpy = jest.spyOn(SourceEditor.prototype, EDITOR_DIFF_INSTANCE_FN);
createModelSpy = jest.spyOn(monacoEditor, 'createModel');
applyExtensionSpy = jest.spyOn(SourceEditorInstance.prototype, 'use');
+ removeExtensionSpy = jest.spyOn(SourceEditorInstance.prototype, 'unuse');
jest.spyOn(service, 'getFileData').mockResolvedValue();
jest.spyOn(service, 'getRawFileData').mockResolvedValue();
});
@@ -177,6 +195,76 @@ describe('RepoEditor', () => {
});
});
+ describe('schema registration for .gitlab-ci.yml', () => {
+ const setup = async (activeFile, flagIsOn = true) => {
+ await createComponent({
+ flags: {
+ schemaLinting: flagIsOn,
+ },
+ });
+ vm.editor.registerCiSchema = jest.fn();
+ if (activeFile) {
+ wrapper.setProps({ file: activeFile });
+ }
+ await waitForPromises();
+ await nextTick();
+ };
+ it.each`
+ flagIsOn | activeFile | shouldUseExtension | desc
+ ${false} | ${dummyFile.markdown} | ${false} | ${`file is not CI config; should NOT`}
+ ${true} | ${dummyFile.markdown} | ${false} | ${`file is not CI config; should NOT`}
+ ${false} | ${dummyFile.ciConfig} | ${false} | ${`file is CI config; should NOT`}
+ ${true} | ${dummyFile.ciConfig} | ${true} | ${`file is CI config; should`}
+ `(
+ 'when the flag is "$flagIsOn", $desc use extension',
+ async ({ flagIsOn, activeFile, shouldUseExtension }) => {
+ await setup(activeFile, flagIsOn);
+
+ if (shouldUseExtension) {
+ expect(applyExtensionSpy).toHaveBeenCalledWith({
+ definition: CiSchemaExtension,
+ });
+ } else {
+ expect(applyExtensionSpy).not.toHaveBeenCalledWith({
+ definition: CiSchemaExtension,
+ });
+ }
+ },
+ );
+ it('stores the fetched extension and does not double-fetch the schema', async () => {
+ await setup();
+ expect(CiSchemaExtension).toHaveBeenCalledTimes(0);
+
+ wrapper.setProps({ file: dummyFile.ciConfig });
+ await waitForPromises();
+ await nextTick();
+ expect(CiSchemaExtension).toHaveBeenCalledTimes(1);
+ expect(vm.CiSchemaExtension).toEqual(CiSchemaExtension);
+ expect(vm.editor.registerCiSchema).toHaveBeenCalledTimes(1);
+
+ wrapper.setProps({ file: dummyFile.markdown });
+ await waitForPromises();
+ await nextTick();
+ expect(CiSchemaExtension).toHaveBeenCalledTimes(1);
+ expect(vm.editor.registerCiSchema).toHaveBeenCalledTimes(1);
+
+ wrapper.setProps({ file: dummyFile.ciConfig });
+ await waitForPromises();
+ await nextTick();
+ expect(CiSchemaExtension).toHaveBeenCalledTimes(1);
+ expect(vm.editor.registerCiSchema).toHaveBeenCalledTimes(2);
+ });
+ it('unuses the existing CI extension if the new model is not CI config', async () => {
+ await setup(dummyFile.ciConfig);
+
+ expect(removeExtensionSpy).not.toHaveBeenCalled();
+ wrapper.setProps({ file: dummyFile.markdown });
+ await waitForPromises();
+ await nextTick();
+ expect(removeExtensionSpy).toHaveBeenCalledWith(CiSchemaExtension);
+ });
+ });
+
describe('when file is markdown', () => {
let mock;
let activeFile;
diff --git a/spec/frontend/ide/components/switch_editors/switch_editors_view_spec.js b/spec/frontend/ide/components/switch_editors/switch_editors_view_spec.js
deleted file mode 100644
index 7a958391fea..00000000000
--- a/spec/frontend/ide/components/switch_editors/switch_editors_view_spec.js
+++ /dev/null
@@ -1,214 +0,0 @@
-import { GlButton, GlEmptyState, GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import waitForPromises from 'helpers/wait_for_promises';
-import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
-import { createAlert } from '~/flash';
-import axios from '~/lib/utils/axios_utils';
-import { logError } from '~/lib/logger';
-import { __ } from '~/locale';
-import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
-import SwitchEditorsView, {
- MSG_ERROR_ALERT,
- MSG_CONFIRM,
- MSG_TITLE,
- MSG_LEARN_MORE,
- MSG_DESCRIPTION,
-} from '~/ide/components/switch_editors/switch_editors_view.vue';
-import eventHub from '~/ide/eventhub';
-import { createStore } from '~/ide/stores';
-
-jest.mock('~/flash');
-jest.mock('~/lib/logger');
-jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal');
-
-const TEST_USER_PREFERENCES_PATH = '/test/user-pref/path';
-const TEST_SWITCH_EDITOR_SVG_PATH = '/test/switch/editor/path.svg';
-const TEST_HREF = '/test/new/web/ide/href';
-
-describe('~/ide/components/switch_editors/switch_editors_view.vue', () => {
- useMockLocationHelper();
-
- let store;
- let wrapper;
- let confirmResolve;
- let requestSpy;
- let skipBeforeunloadSpy;
- let axiosMock;
-
- // region: finders ------------------
- const findButton = () => wrapper.findComponent(GlButton);
- const findEmptyState = () => wrapper.findComponent(GlEmptyState);
-
- // region: actions ------------------
- const triggerSwitchPreference = () => findButton().vm.$emit('click');
- const submitConfirm = async (val) => {
- confirmResolve(val);
-
- // why: We need to wait for promises for the immediate next lines to be executed
- await waitForPromises();
- };
-
- const createComponent = () => {
- wrapper = shallowMount(SwitchEditorsView, {
- store,
- stubs: {
- GlEmptyState,
- },
- });
- };
-
- // region: test setup ------------------
- beforeEach(() => {
- // Setup skip-beforeunload side-effect
- skipBeforeunloadSpy = jest.fn();
- eventHub.$on('skip-beforeunload', skipBeforeunloadSpy);
-
- // Setup request side-effect
- requestSpy = jest.fn().mockImplementation(() => new Promise(() => {}));
- axiosMock = new MockAdapter(axios);
- axiosMock.onPut(TEST_USER_PREFERENCES_PATH).reply(({ data }) => requestSpy(data));
-
- // Setup store
- store = createStore();
- store.state.userPreferencesPath = TEST_USER_PREFERENCES_PATH;
- store.state.switchEditorSvgPath = TEST_SWITCH_EDITOR_SVG_PATH;
- store.state.links = {
- newWebIDEHelpPagePath: TEST_HREF,
- };
-
- // Setup user confirm side-effect
- confirmAction.mockImplementation(
- () =>
- new Promise((resolve) => {
- confirmResolve = resolve;
- }),
- );
- });
-
- afterEach(() => {
- eventHub.$off('skip-beforeunload', skipBeforeunloadSpy);
-
- axiosMock.restore();
- });
-
- // region: tests ------------------
- describe('default', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('render empty state', () => {
- expect(findEmptyState().props()).toMatchObject({
- svgPath: TEST_SWITCH_EDITOR_SVG_PATH,
- svgHeight: 150,
- title: MSG_TITLE,
- });
- });
-
- it('render link', () => {
- expect(wrapper.findComponent(GlLink).attributes('href')).toBe(TEST_HREF);
- expect(wrapper.findComponent(GlLink).text()).toBe(MSG_LEARN_MORE);
- });
-
- it('renders description', () => {
- expect(findEmptyState().text()).toContain(MSG_DESCRIPTION);
- });
-
- it('is not loading', () => {
- expect(findButton().props('loading')).toBe(false);
- });
- });
-
- describe('when user triggers switch preference', () => {
- beforeEach(() => {
- createComponent();
-
- triggerSwitchPreference();
- });
-
- it('creates a single confirm', () => {
- // Call again to ensure that we only show 1 confirm action
- triggerSwitchPreference();
-
- expect(confirmAction).toHaveBeenCalledTimes(1);
- expect(confirmAction).toHaveBeenCalledWith(MSG_CONFIRM, {
- primaryBtnText: __('Switch editors'),
- cancelBtnText: __('Cancel'),
- });
- });
-
- it('starts loading', () => {
- expect(findButton().props('loading')).toBe(true);
- });
-
- describe('when user cancels confirm', () => {
- beforeEach(async () => {
- await submitConfirm(false);
- });
-
- it('does not make request', () => {
- expect(requestSpy).not.toHaveBeenCalled();
- });
-
- it('can be triggered again', () => {
- triggerSwitchPreference();
-
- expect(confirmAction).toHaveBeenCalledTimes(2);
- });
- });
-
- describe('when user accepts confirm and response success', () => {
- beforeEach(async () => {
- requestSpy.mockReturnValue([200, {}]);
- await submitConfirm(true);
- });
-
- it('does not handle error', () => {
- expect(logError).not.toHaveBeenCalled();
- expect(createAlert).not.toHaveBeenCalled();
- });
-
- it('emits "skip-beforeunload" and reloads', () => {
- expect(skipBeforeunloadSpy).toHaveBeenCalledTimes(1);
- expect(window.location.reload).toHaveBeenCalledTimes(1);
- });
-
- it('calls request', () => {
- expect(requestSpy).toHaveBeenCalledTimes(1);
- expect(requestSpy).toHaveBeenCalledWith(
- JSON.stringify({ user: { use_legacy_web_ide: false } }),
- );
- });
-
- it('is not loading', () => {
- expect(findButton().props('loading')).toBe(false);
- });
- });
-
- describe('when user accepts confirm and response fails', () => {
- beforeEach(async () => {
- requestSpy.mockReturnValue([400, {}]);
- await submitConfirm(true);
- });
-
- it('handles error', () => {
- expect(logError).toHaveBeenCalledTimes(1);
- expect(logError).toHaveBeenCalledWith(
- 'Error while updating user preferences',
- expect.any(Error),
- );
-
- expect(createAlert).toHaveBeenCalledTimes(1);
- expect(createAlert).toHaveBeenCalledWith({
- message: MSG_ERROR_ALERT,
- });
- });
-
- it('does not reload', () => {
- expect(skipBeforeunloadSpy).not.toHaveBeenCalled();
- expect(window.location.reload).not.toHaveBeenCalled();
- });
- });
- });
-});
diff --git a/spec/frontend/ide/init_gitlab_web_ide_spec.js b/spec/frontend/ide/init_gitlab_web_ide_spec.js
index 067da25cb52..97254ab680b 100644
--- a/spec/frontend/ide/init_gitlab_web_ide_spec.js
+++ b/spec/frontend/ide/init_gitlab_web_ide_spec.js
@@ -1,62 +1,190 @@
import { start } from '@gitlab/web-ide';
+import { GITLAB_WEB_IDE_FEEDBACK_ISSUE } from '~/ide/constants';
import { initGitlabWebIDE } from '~/ide/init_gitlab_web_ide';
+import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_action';
+import { createAndSubmitForm } from '~/lib/utils/create_and_submit_form';
import { TEST_HOST } from 'helpers/test_constants';
+import setWindowLocation from 'helpers/set_window_location_helper';
+import waitForPromises from 'helpers/wait_for_promises';
jest.mock('@gitlab/web-ide');
+jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_action');
+jest.mock('~/lib/utils/create_and_submit_form');
+jest.mock('~/lib/utils/csrf', () => ({
+ token: 'mock-csrf-token',
+ headerKey: 'mock-csrf-header',
+}));
const ROOT_ELEMENT_ID = 'ide';
const TEST_NONCE = 'test123nonce';
const TEST_PROJECT_PATH = 'group1/project1';
const TEST_BRANCH_NAME = '12345-foo-patch';
const TEST_GITLAB_URL = 'https://test-gitlab/';
+const TEST_USER_PREFERENCES_PATH = '/user/preferences';
const TEST_GITLAB_WEB_IDE_PUBLIC_PATH = 'test/webpack/assets/gitlab-web-ide/public/path';
+const TEST_FILE_PATH = 'foo/README.md';
+const TEST_MR_ID = '7';
+const TEST_MR_TARGET_PROJECT = 'gitlab-org/the-real-gitlab';
+const TEST_FORK_INFO = { fork_path: '/forky' };
+const TEST_IDE_REMOTE_PATH = '/-/ide/remote/:remote_host/:remote_path';
+const TEST_START_REMOTE_PARAMS = {
+ remoteHost: 'dev.example.gitlab.com/test',
+ remotePath: '/test/projects/f oo',
+ connectionToken: '123abc',
+};
describe('ide/init_gitlab_web_ide', () => {
+ let resolveConfirm;
+
const createRootElement = () => {
const el = document.createElement('div');
el.id = ROOT_ELEMENT_ID;
// why: We'll test that this class is removed later
- el.classList.add('ide-loading');
+ el.classList.add('test-class');
el.dataset.projectPath = TEST_PROJECT_PATH;
el.dataset.cspNonce = TEST_NONCE;
el.dataset.branchName = TEST_BRANCH_NAME;
+ el.dataset.ideRemotePath = TEST_IDE_REMOTE_PATH;
+ el.dataset.userPreferencesPath = TEST_USER_PREFERENCES_PATH;
+ el.dataset.mergeRequest = TEST_MR_ID;
+ el.dataset.filePath = TEST_FILE_PATH;
document.body.append(el);
};
const findRootElement = () => document.getElementById(ROOT_ELEMENT_ID);
- const act = () => initGitlabWebIDE(findRootElement());
+ const createSubject = () => initGitlabWebIDE(findRootElement());
+ const triggerHandleStartRemote = (startRemoteParams) => {
+ const [, config] = start.mock.calls[0];
+
+ config.handleStartRemote(startRemoteParams);
+ };
beforeEach(() => {
process.env.GITLAB_WEB_IDE_PUBLIC_PATH = TEST_GITLAB_WEB_IDE_PUBLIC_PATH;
window.gon.gitlab_url = TEST_GITLAB_URL;
- createRootElement();
+ confirmAction.mockImplementation(
+ () =>
+ new Promise((resolve) => {
+ resolveConfirm = resolve;
+ }),
+ );
- act();
+ createRootElement();
});
afterEach(() => {
document.body.innerHTML = '';
});
- it('calls start with element', () => {
- expect(start).toHaveBeenCalledWith(findRootElement(), {
- baseUrl: `${TEST_HOST}/${TEST_GITLAB_WEB_IDE_PUBLIC_PATH}`,
- projectPath: TEST_PROJECT_PATH,
- ref: TEST_BRANCH_NAME,
- gitlabUrl: TEST_GITLAB_URL,
- nonce: TEST_NONCE,
+ describe('default', () => {
+ beforeEach(() => {
+ createSubject();
+ });
+
+ it('calls start with element', () => {
+ expect(start).toHaveBeenCalledTimes(1);
+ expect(start).toHaveBeenCalledWith(findRootElement(), {
+ baseUrl: `${TEST_HOST}/${TEST_GITLAB_WEB_IDE_PUBLIC_PATH}`,
+ projectPath: TEST_PROJECT_PATH,
+ ref: TEST_BRANCH_NAME,
+ filePath: TEST_FILE_PATH,
+ mrId: TEST_MR_ID,
+ mrTargetProject: '',
+ forkInfo: null,
+ gitlabUrl: TEST_GITLAB_URL,
+ nonce: TEST_NONCE,
+ httpHeaders: {
+ 'mock-csrf-header': 'mock-csrf-token',
+ 'X-Requested-With': 'XMLHttpRequest',
+ },
+ links: {
+ userPreferences: TEST_USER_PREFERENCES_PATH,
+ feedbackIssue: GITLAB_WEB_IDE_FEEDBACK_ISSUE,
+ },
+ handleStartRemote: expect.any(Function),
+ });
+ });
+
+ it('clears classes and data from root element', () => {
+ const rootEl = findRootElement();
+
+ // why: Snapshot to test that the element was cleaned including `test-class`
+ expect(rootEl.outerHTML).toBe(
+ '<div id="ide" class="gl--flex-center gl-relative gl-h-full"></div>',
+ );
+ });
+
+ describe('when handleStartRemote is triggered', () => {
+ beforeEach(() => {
+ triggerHandleStartRemote(TEST_START_REMOTE_PARAMS);
+ });
+
+ it('promts for confirm', () => {
+ expect(confirmAction).toHaveBeenCalledWith(expect.any(String), {
+ primaryBtnText: expect.any(String),
+ cancelBtnText: expect.any(String),
+ });
+ });
+
+ it('does not submit, when not confirmed', async () => {
+ resolveConfirm(false);
+
+ await waitForPromises();
+
+ expect(createAndSubmitForm).not.toHaveBeenCalled();
+ });
+
+ it('submits, when confirmed', async () => {
+ resolveConfirm(true);
+
+ await waitForPromises();
+
+ expect(createAndSubmitForm).toHaveBeenCalledWith({
+ url: '/-/ide/remote/dev.example.gitlab.com%2Ftest/test/projects/f%20oo',
+ data: {
+ connection_token: TEST_START_REMOTE_PARAMS.connectionToken,
+ return_url: window.location.href,
+ },
+ });
+ });
+ });
+ });
+
+ describe('when URL has target_project in query params', () => {
+ beforeEach(() => {
+ setWindowLocation(
+ `https://example.com/-/ide?target_project=${encodeURIComponent(TEST_MR_TARGET_PROJECT)}`,
+ );
+
+ createSubject();
+ });
+
+ it('includes mrTargetProject', () => {
+ expect(start).toHaveBeenCalledWith(
+ findRootElement(),
+ expect.objectContaining({
+ mrTargetProject: TEST_MR_TARGET_PROJECT,
+ }),
+ );
});
});
- it('clears classes and data from root element', () => {
- const rootEl = findRootElement();
+ describe('when forkInfo is in dataset', () => {
+ beforeEach(() => {
+ findRootElement().dataset.forkInfo = JSON.stringify(TEST_FORK_INFO);
- // why: Snapshot to test that `ide-loading` was removed and no other
- // artifacts are remaining.
- expect(rootEl.outerHTML).toBe(
- '<div id="ide" class="gl--flex-center gl-relative gl-h-full"></div>',
- );
+ createSubject();
+ });
+
+ it('includes forkInfo', () => {
+ expect(start).toHaveBeenCalledWith(
+ findRootElement(),
+ expect.objectContaining({
+ forkInfo: TEST_FORK_INFO,
+ }),
+ );
+ });
});
});
diff --git a/spec/frontend/ide/lib/common/model_spec.js b/spec/frontend/ide/lib/common/model_spec.js
index 5d1623429c0..39c50f628c2 100644
--- a/spec/frontend/ide/lib/common/model_spec.js
+++ b/spec/frontend/ide/lib/common/model_spec.js
@@ -149,7 +149,6 @@ describe('Multi-file editor library model', () => {
model.updateOptions({ insertSpaces: true, someOption: 'some value' });
expect(model.options).toEqual({
- endOfLine: 0,
insertFinalNewline: true,
insertSpaces: true,
someOption: 'some value',
@@ -181,16 +180,12 @@ describe('Multi-file editor library model', () => {
describe('applyCustomOptions', () => {
it.each`
option | value | contentBefore | contentAfter
- ${'endOfLine'} | ${0} | ${'hello\nworld\n'} | ${'hello\nworld\n'}
- ${'endOfLine'} | ${0} | ${'hello\r\nworld\r\n'} | ${'hello\nworld\n'}
- ${'endOfLine'} | ${1} | ${'hello\nworld\n'} | ${'hello\r\nworld\r\n'}
- ${'endOfLine'} | ${1} | ${'hello\r\nworld\r\n'} | ${'hello\r\nworld\r\n'}
${'insertFinalNewline'} | ${true} | ${'hello\nworld'} | ${'hello\nworld\n'}
${'insertFinalNewline'} | ${true} | ${'hello\nworld\n'} | ${'hello\nworld\n'}
${'insertFinalNewline'} | ${false} | ${'hello\nworld'} | ${'hello\nworld'}
${'trimTrailingWhitespace'} | ${true} | ${'hello \t\nworld \t\n'} | ${'hello\nworld\n'}
- ${'trimTrailingWhitespace'} | ${true} | ${'hello \t\r\nworld \t\r\n'} | ${'hello\nworld\n'}
- ${'trimTrailingWhitespace'} | ${false} | ${'hello \t\r\nworld \t\r\n'} | ${'hello \t\nworld \t\n'}
+ ${'trimTrailingWhitespace'} | ${true} | ${'hello \t\r\nworld \t\r\n'} | ${'hello\r\nworld\r\n'}
+ ${'trimTrailingWhitespace'} | ${false} | ${'hello \t\r\nworld \t\r\n'} | ${'hello \t\r\nworld \t\r\n'}
`(
'correctly applies custom option $option=$value to content',
({ option, value, contentBefore, contentAfter }) => {
diff --git a/spec/frontend/ide/lib/gitlab_web_ide/get_base_config_spec.js b/spec/frontend/ide/lib/gitlab_web_ide/get_base_config_spec.js
new file mode 100644
index 00000000000..4b4e96f3b41
--- /dev/null
+++ b/spec/frontend/ide/lib/gitlab_web_ide/get_base_config_spec.js
@@ -0,0 +1,22 @@
+import { getBaseConfig } from '~/ide/lib/gitlab_web_ide/get_base_config';
+import { TEST_HOST } from 'helpers/test_constants';
+
+const TEST_GITLAB_WEB_IDE_PUBLIC_PATH = 'test/gitlab-web-ide/public/path';
+const TEST_GITLAB_URL = 'https://gdk.test/';
+
+describe('~/ide/lib/gitlab_web_ide/get_base_config', () => {
+ it('returns base properties for @gitlab/web-ide config', () => {
+ // why: add trailing "/" to test that it gets removed
+ process.env.GITLAB_WEB_IDE_PUBLIC_PATH = `${TEST_GITLAB_WEB_IDE_PUBLIC_PATH}/`;
+ window.gon.gitlab_url = TEST_GITLAB_URL;
+
+ // act
+ const actual = getBaseConfig();
+
+ // asset
+ expect(actual).toEqual({
+ baseUrl: `${TEST_HOST}/${TEST_GITLAB_WEB_IDE_PUBLIC_PATH}`,
+ gitlabUrl: TEST_GITLAB_URL,
+ });
+ });
+});
diff --git a/spec/frontend/ide/lib/gitlab_web_ide/setup_root_element_spec.js b/spec/frontend/ide/lib/gitlab_web_ide/setup_root_element_spec.js
new file mode 100644
index 00000000000..35cf41b31f5
--- /dev/null
+++ b/spec/frontend/ide/lib/gitlab_web_ide/setup_root_element_spec.js
@@ -0,0 +1,32 @@
+import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
+import { setupRootElement } from '~/ide/lib/gitlab_web_ide/setup_root_element';
+
+describe('~/ide/lib/gitlab_web_ide/setup_root_element', () => {
+ beforeEach(() => {
+ setHTMLFixture(`
+ <div id="ide-test-root" class="js-not-a-real-class">
+ <span>We are loading lots of stuff...</span>
+ </div>
+ `);
+ });
+
+ afterEach(() => {
+ resetHTMLFixture();
+ });
+
+ const findIDERoot = () => document.getElementById('ide-test-root');
+
+ it('has no children, has original ID, and classes', () => {
+ const result = setupRootElement(findIDERoot());
+
+ // why: Assert that the return element matches the new one found in the dom
+ // (implying a el.replaceWith...)
+ expect(result).toBe(findIDERoot());
+ expect(result).toMatchInlineSnapshot(`
+ <div
+ class="gl--flex-center gl-relative gl-h-full"
+ id="ide-test-root"
+ />
+ `);
+ });
+});
diff --git a/spec/frontend/ide/remote/index_spec.js b/spec/frontend/ide/remote/index_spec.js
new file mode 100644
index 00000000000..0f23b0a4e45
--- /dev/null
+++ b/spec/frontend/ide/remote/index_spec.js
@@ -0,0 +1,91 @@
+import { startRemote } from '@gitlab/web-ide';
+import { getBaseConfig, setupRootElement } from '~/ide/lib/gitlab_web_ide';
+import { mountRemoteIDE } from '~/ide/remote';
+import { TEST_HOST } from 'helpers/test_constants';
+import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
+
+jest.mock('@gitlab/web-ide');
+jest.mock('~/ide/lib/gitlab_web_ide');
+
+const TEST_DATA = {
+ remoteHost: 'example.com:3443',
+ remotePath: 'test/path/gitlab',
+ cspNonce: 'just7some8noncense',
+ connectionToken: 'connectAtoken',
+ returnUrl: 'https://example.com/return',
+};
+
+const TEST_BASE_CONFIG = {
+ gitlabUrl: '/test/gitlab',
+};
+
+const TEST_RETURN_URL_SAME_ORIGIN = `${TEST_HOST}/foo/example`;
+
+describe('~/ide/remote/index', () => {
+ useMockLocationHelper();
+ const originalHref = window.location.href;
+
+ let el;
+ let rootEl;
+
+ beforeEach(() => {
+ el = document.createElement('div');
+ Object.entries(TEST_DATA).forEach(([key, value]) => {
+ el.dataset[key] = value;
+ });
+
+ // Stub setupRootElement so we can assert on return element
+ rootEl = document.createElement('div');
+ setupRootElement.mockReturnValue(rootEl);
+
+ // Stub getBaseConfig so we can assert
+ getBaseConfig.mockReturnValue(TEST_BASE_CONFIG);
+ });
+
+ describe('default', () => {
+ beforeEach(() => {
+ mountRemoteIDE(el);
+ });
+
+ it('calls startRemote', () => {
+ expect(startRemote).toHaveBeenCalledWith(rootEl, {
+ ...TEST_BASE_CONFIG,
+ nonce: TEST_DATA.cspNonce,
+ connectionToken: TEST_DATA.connectionToken,
+ remoteAuthority: `/${TEST_DATA.remoteHost}`,
+ hostPath: `/${TEST_DATA.remotePath}`,
+ handleError: expect.any(Function),
+ handleClose: expect.any(Function),
+ });
+ });
+ });
+
+ describe.each`
+ returnUrl | fnName | reloadExpectation | hrefExpectation
+ ${TEST_DATA.returnUrl} | ${'handleError'} | ${1} | ${originalHref}
+ ${TEST_DATA.returnUrl} | ${'handleClose'} | ${1} | ${originalHref}
+ ${TEST_RETURN_URL_SAME_ORIGIN} | ${'handleClose'} | ${0} | ${TEST_RETURN_URL_SAME_ORIGIN}
+ ${TEST_RETURN_URL_SAME_ORIGIN} | ${'handleError'} | ${0} | ${TEST_RETURN_URL_SAME_ORIGIN}
+ ${''} | ${'handleClose'} | ${1} | ${originalHref}
+ `(
+ 'with returnUrl=$returnUrl and fn=$fnName',
+ ({ returnUrl, fnName, reloadExpectation, hrefExpectation }) => {
+ beforeEach(() => {
+ el.dataset.returnUrl = returnUrl;
+
+ mountRemoteIDE(el);
+ });
+
+ it('changes location', () => {
+ expect(window.location.reload).not.toHaveBeenCalled();
+
+ const [, config] = startRemote.mock.calls[0];
+
+ config[fnName]();
+
+ expect(window.location.reload).toHaveBeenCalledTimes(reloadExpectation);
+ expect(window.location.href).toBe(hrefExpectation);
+ });
+ },
+ );
+});
diff --git a/spec/frontend/ide/services/index_spec.js b/spec/frontend/ide/services/index_spec.js
index 0fab828dfb3..5847e8e1518 100644
--- a/spec/frontend/ide/services/index_spec.js
+++ b/spec/frontend/ide/services/index_spec.js
@@ -6,7 +6,7 @@ import dismissUserCallout from '~/graphql_shared/mutations/dismiss_user_callout.
import services from '~/ide/services';
import { query, mutate } from '~/ide/services/gql';
import { escapeFileUrl } from '~/lib/utils/url_utility';
-import ciConfig from '~/pipeline_editor/graphql/queries/ci_config.query.graphql';
+import ciConfig from '~/ci/pipeline_editor/graphql/queries/ci_config.query.graphql';
import { projectData } from '../mock_data';
jest.mock('~/api');
diff --git a/spec/frontend/ide/stores/modules/terminal/actions/checks_spec.js b/spec/frontend/ide/stores/modules/terminal/actions/checks_spec.js
index fc00bd075e7..8d21088bcaf 100644
--- a/spec/frontend/ide/stores/modules/terminal/actions/checks_spec.js
+++ b/spec/frontend/ide/stores/modules/terminal/actions/checks_spec.js
@@ -10,7 +10,7 @@ import {
import * as messages from '~/ide/stores/modules/terminal/messages';
import * as mutationTypes from '~/ide/stores/modules/terminal/mutation_types';
import axios from '~/lib/utils/axios_utils';
-import httpStatus from '~/lib/utils/http_status';
+import httpStatus, { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
const TEST_PROJECT_PATH = 'lorem/root';
const TEST_BRANCH_ID = 'main';
@@ -78,7 +78,7 @@ describe('IDE store terminal check actions', () => {
describe('receiveConfigCheckError', () => {
it('handles error response', () => {
- const status = httpStatus.UNPROCESSABLE_ENTITY;
+ const status = HTTP_STATUS_UNPROCESSABLE_ENTITY;
const payload = { response: { status } };
return testAction(
diff --git a/spec/frontend/ide/stores/modules/terminal/actions/session_controls_spec.js b/spec/frontend/ide/stores/modules/terminal/actions/session_controls_spec.js
index f48797415df..df365442c67 100644
--- a/spec/frontend/ide/stores/modules/terminal/actions/session_controls_spec.js
+++ b/spec/frontend/ide/stores/modules/terminal/actions/session_controls_spec.js
@@ -6,7 +6,7 @@ import { STARTING, PENDING, STOPPING, STOPPED } from '~/ide/stores/modules/termi
import * as messages from '~/ide/stores/modules/terminal/messages';
import * as mutationTypes from '~/ide/stores/modules/terminal/mutation_types';
import axios from '~/lib/utils/axios_utils';
-import httpStatus from '~/lib/utils/http_status';
+import httpStatus, { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
jest.mock('~/flash');
@@ -285,7 +285,7 @@ describe('IDE store terminal session controls actions', () => {
);
});
- [httpStatus.NOT_FOUND, httpStatus.UNPROCESSABLE_ENTITY].forEach((status) => {
+ [httpStatus.NOT_FOUND, HTTP_STATUS_UNPROCESSABLE_ENTITY].forEach((status) => {
it(`dispatches request and startSession on ${status}`, () => {
mock
.onPost(state.session.retryPath, { branch: rootState.currentBranchId, format: 'json' })
diff --git a/spec/frontend/ide/stores/modules/terminal/messages_spec.js b/spec/frontend/ide/stores/modules/terminal/messages_spec.js
index e8f375a70b5..2a802d6b4af 100644
--- a/spec/frontend/ide/stores/modules/terminal/messages_spec.js
+++ b/spec/frontend/ide/stores/modules/terminal/messages_spec.js
@@ -1,7 +1,7 @@
import { escape } from 'lodash';
import { TEST_HOST } from 'spec/test_constants';
import * as messages from '~/ide/stores/modules/terminal/messages';
-import httpStatus from '~/lib/utils/http_status';
+import httpStatus, { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
import { sprintf } from '~/locale';
const TEST_HELP_URL = `${TEST_HOST}/help`;
@@ -9,7 +9,7 @@ const TEST_HELP_URL = `${TEST_HOST}/help`;
describe('IDE store terminal messages', () => {
describe('configCheckError', () => {
it('returns job error, with status UNPROCESSABLE_ENTITY', () => {
- const result = messages.configCheckError(httpStatus.UNPROCESSABLE_ENTITY, TEST_HELP_URL);
+ const result = messages.configCheckError(HTTP_STATUS_UNPROCESSABLE_ENTITY, TEST_HELP_URL);
expect(result).toBe(
sprintf(
diff --git a/spec/frontend/import_entities/components/group_dropdown_spec.js b/spec/frontend/import_entities/components/group_dropdown_spec.js
index b896437ecb2..31e097cfa7b 100644
--- a/spec/frontend/import_entities/components/group_dropdown_spec.js
+++ b/spec/frontend/import_entities/components/group_dropdown_spec.js
@@ -1,16 +1,61 @@
import { GlSearchBoxByType, GlDropdown } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import GroupDropdown from '~/import_entities/components/group_dropdown.vue';
+import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants';
+import searchNamespacesWhereUserCanCreateProjectsQuery from '~/projects/new/queries/search_namespaces_where_user_can_create_projects.query.graphql';
+
+Vue.use(VueApollo);
+
+const makeGroupMock = (fullPath) => ({
+ id: `gid://gitlab/Group/${fullPath}`,
+ fullPath,
+ name: fullPath,
+ visibility: 'public',
+ webUrl: `http://gdk.test:3000/groups/${fullPath}`,
+ __typename: 'Group',
+});
+
+const AVAILABLE_NAMESPACES = [
+ makeGroupMock('match1'),
+ makeGroupMock('unrelated'),
+ makeGroupMock('match2'),
+];
+
+const SEARCH_NAMESPACES_MOCK = Promise.resolve({
+ data: {
+ currentUser: {
+ id: 'gid://gitlab/User/1',
+ groups: {
+ nodes: AVAILABLE_NAMESPACES,
+ __typename: 'GroupConnection',
+ },
+ namespace: {
+ id: 'gid://gitlab/Namespaces::UserNamespace/1',
+ fullPath: 'root',
+ __typename: 'Namespace',
+ },
+ __typename: 'UserCore',
+ },
+ },
+});
describe('Import entities group dropdown component', () => {
let wrapper;
let namespacesTracker;
const createComponent = (propsData) => {
+ const apolloProvider = createMockApollo([
+ [searchNamespacesWhereUserCanCreateProjectsQuery, () => SEARCH_NAMESPACES_MOCK],
+ ]);
+
namespacesTracker = jest.fn();
wrapper = shallowMount(GroupDropdown, {
+ apolloProvider,
scopedSlots: {
default: namespacesTracker,
},
@@ -23,33 +68,30 @@ describe('Import entities group dropdown component', () => {
wrapper.destroy();
});
- it('passes namespaces from props to default slot', () => {
- const namespaces = [
- { id: 1, fullPath: 'ns1' },
- { id: 2, fullPath: 'ns2' },
- ];
- createComponent({ namespaces });
+ it('passes namespaces from graphql query to default slot', async () => {
+ createComponent();
+ jest.advanceTimersByTime(DEBOUNCE_DELAY);
+ await nextTick();
+ await waitForPromises();
+ await nextTick();
- expect(namespacesTracker).toHaveBeenCalledWith({ namespaces });
+ expect(namespacesTracker).toHaveBeenCalledWith({ namespaces: AVAILABLE_NAMESPACES });
});
it('filters namespaces based on user input', async () => {
- const namespaces = [
- { id: 1, fullPath: 'match1' },
- { id: 2, fullPath: 'some unrelated' },
- { id: 3, fullPath: 'match2' },
- ];
- createComponent({ namespaces });
+ createComponent();
namespacesTracker.mockReset();
wrapper.findComponent(GlSearchBoxByType).vm.$emit('input', 'match');
-
+ jest.advanceTimersByTime(DEBOUNCE_DELAY);
+ await nextTick();
+ await waitForPromises();
await nextTick();
expect(namespacesTracker).toHaveBeenCalledWith({
namespaces: [
- { id: 1, fullPath: 'match1' },
- { id: 3, fullPath: 'match2' },
+ expect.objectContaining({ fullPath: 'match1' }),
+ expect.objectContaining({ fullPath: 'match2' }),
],
});
});
diff --git a/spec/frontend/import_entities/import_groups/components/import_table_spec.js b/spec/frontend/import_entities/import_groups/components/import_table_spec.js
index 61f860688dc..f7a97f22d44 100644
--- a/spec/frontend/import_entities/import_groups/components/import_table_spec.js
+++ b/spec/frontend/import_entities/import_groups/components/import_table_spec.js
@@ -15,8 +15,13 @@ import ImportTable from '~/import_entities/import_groups/components/import_table
import importGroupsMutation from '~/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql';
import PaginationBar from '~/vue_shared/components/pagination_bar/pagination_bar.vue';
import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
+import searchNamespacesWhereUserCanCreateProjectsQuery from '~/projects/new/queries/search_namespaces_where_user_can_create_projects.query.graphql';
-import { availableNamespacesFixture, generateFakeEntry } from '../graphql/fixtures';
+import {
+ AVAILABLE_NAMESPACES,
+ availableNamespacesFixture,
+ generateFakeEntry,
+} from '../graphql/fixtures';
jest.mock('~/flash');
jest.mock('~/import_entities/import_groups/services/status_poller');
@@ -60,15 +65,22 @@ describe('import table', () => {
wrapper.findAll('tbody td input[type=checkbox]').at(idx).setChecked(true);
const createComponent = ({ bulkImportSourceGroups, importGroups, defaultTargetNamespace }) => {
- apolloProvider = createMockApollo([], {
- Query: {
- availableNamespaces: () => availableNamespacesFixture,
- bulkImportSourceGroups,
- },
- Mutation: {
- importGroups,
+ apolloProvider = createMockApollo(
+ [
+ [
+ searchNamespacesWhereUserCanCreateProjectsQuery,
+ () => Promise.resolve(availableNamespacesFixture),
+ ],
+ ],
+ {
+ Query: {
+ bulkImportSourceGroups,
+ },
+ Mutation: {
+ importGroups,
+ },
},
- });
+ );
wrapper = mount(ImportTable, {
propsData: {
@@ -173,7 +185,7 @@ describe('import table', () => {
});
it('respects default namespace if provided', async () => {
- const targetNamespace = availableNamespacesFixture[1];
+ const targetNamespace = AVAILABLE_NAMESPACES[1];
createComponent({
bulkImportSourceGroups: () => ({
@@ -227,7 +239,7 @@ describe('import table', () => {
{
newName: FAKE_GROUP.lastImportTarget.newName,
sourceGroupId: FAKE_GROUP.id,
- targetNamespace: availableNamespacesFixture[0].fullPath,
+ targetNamespace: AVAILABLE_NAMESPACES[0].fullPath,
},
],
},
@@ -519,12 +531,12 @@ describe('import table', () => {
variables: {
importRequests: [
{
- targetNamespace: availableNamespacesFixture[0].fullPath,
+ targetNamespace: AVAILABLE_NAMESPACES[0].fullPath,
newName: NEW_GROUPS[0].lastImportTarget.newName,
sourceGroupId: NEW_GROUPS[0].id,
},
{
- targetNamespace: availableNamespacesFixture[0].fullPath,
+ targetNamespace: AVAILABLE_NAMESPACES[0].fullPath,
newName: NEW_GROUPS[1].lastImportTarget.newName,
sourceGroupId: NEW_GROUPS[1].id,
},
diff --git a/spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js b/spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js
index 18dc1217fec..d5286e71c44 100644
--- a/spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js
+++ b/spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js
@@ -1,9 +1,22 @@
import { GlDropdownItem, GlFormInput } from '@gitlab/ui';
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
import { shallowMount } from '@vue/test-utils';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import ImportGroupDropdown from '~/import_entities/components/group_dropdown.vue';
import { STATUSES } from '~/import_entities/constants';
import ImportTargetCell from '~/import_entities/import_groups/components/import_target_cell.vue';
-import { generateFakeEntry, availableNamespacesFixture } from '../graphql/fixtures';
+import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants';
+import searchNamespacesWhereUserCanCreateProjectsQuery from '~/projects/new/queries/search_namespaces_where_user_can_create_projects.query.graphql';
+
+import {
+ generateFakeEntry,
+ availableNamespacesFixture,
+ AVAILABLE_NAMESPACES,
+} from '../graphql/fixtures';
+
+Vue.use(VueApollo);
const generateFakeTableEntry = ({ flags = {}, ...config }) => {
const entry = generateFakeEntry(config);
@@ -11,7 +24,7 @@ const generateFakeTableEntry = ({ flags = {}, ...config }) => {
return {
...entry,
importTarget: {
- targetNamespace: availableNamespacesFixture[0],
+ targetNamespace: AVAILABLE_NAMESPACES[0],
newName: entry.lastImportTarget.newName,
},
flags,
@@ -20,16 +33,24 @@ const generateFakeTableEntry = ({ flags = {}, ...config }) => {
describe('import target cell', () => {
let wrapper;
+ let apolloProvider;
let group;
const findNameInput = () => wrapper.findComponent(GlFormInput);
const findNamespaceDropdown = () => wrapper.findComponent(ImportGroupDropdown);
const createComponent = (props) => {
+ apolloProvider = createMockApollo([
+ [
+ searchNamespacesWhereUserCanCreateProjectsQuery,
+ () => Promise.resolve(availableNamespacesFixture),
+ ],
+ ]);
+
wrapper = shallowMount(ImportTargetCell, {
+ apolloProvider,
stubs: { ImportGroupDropdown },
propsData: {
- availableNamespaces: availableNamespacesFixture,
groupPathRegex: /.*/,
...props,
},
@@ -42,9 +63,12 @@ describe('import target cell', () => {
});
describe('events', () => {
- beforeEach(() => {
+ beforeEach(async () => {
group = generateFakeTableEntry({ id: 1, status: STATUSES.NONE });
createComponent({ group });
+ await nextTick();
+ jest.advanceTimersByTime(DEBOUNCE_DELAY);
+ await nextTick();
});
it('emits update-new-name when input value is changed', () => {
@@ -59,7 +83,9 @@ describe('import target cell', () => {
dropdownItem.vm.$emit('click');
expect(wrapper.emitted('update-target-namespace')).toBeDefined();
- expect(wrapper.emitted('update-target-namespace')[0][0]).toBe(availableNamespacesFixture[1]);
+ expect(wrapper.emitted('update-target-namespace')[0][0]).toStrictEqual(
+ AVAILABLE_NAMESPACES[1],
+ );
});
});
@@ -94,18 +120,20 @@ describe('import target cell', () => {
expect(items).toHaveLength(1);
});
- it('renders both no parent option and available namespaces list when available namespaces list is not empty', () => {
+ it('renders both no parent option and available namespaces list when available namespaces list is not empty', async () => {
createComponent({
group: generateFakeTableEntry({ id: 1, status: STATUSES.NONE }),
- availableNamespaces: availableNamespacesFixture,
});
+ jest.advanceTimersByTime(DEBOUNCE_DELAY);
+ await waitForPromises();
+ await nextTick();
const [firstItem, ...rest] = findNamespaceDropdown()
.findAllComponents(GlDropdownItem)
.wrappers.map((w) => w.text());
expect(firstItem).toBe('No parent');
- expect(rest).toHaveLength(availableNamespacesFixture.length);
+ expect(rest).toHaveLength(AVAILABLE_NAMESPACES.length);
});
describe('when entity is not available for import', () => {
diff --git a/spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js b/spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js
index 52c868e5356..adc4ebcffb8 100644
--- a/spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js
+++ b/spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js
@@ -10,12 +10,11 @@ import {
import { LocalStorageCache } from '~/import_entities/import_groups/graphql/services/local_storage_cache';
import importGroupsMutation from '~/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql';
import updateImportStatusMutation from '~/import_entities/import_groups/graphql/mutations/update_import_status.mutation.graphql';
-import availableNamespacesQuery from '~/import_entities/import_groups/graphql/queries/available_namespaces.query.graphql';
import bulkImportSourceGroupsQuery from '~/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql';
import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status';
-import { statusEndpointFixture, availableNamespacesFixture } from './fixtures';
+import { statusEndpointFixture } from './fixtures';
jest.mock('~/flash');
jest.mock('~/import_entities/import_groups/graphql/services/local_storage_cache', () => ({
@@ -28,7 +27,6 @@ jest.mock('~/import_entities/import_groups/graphql/services/local_storage_cache'
const FAKE_ENDPOINTS = {
status: '/fake_status_url',
- availableNamespaces: '/fake_available_namespaces',
createBulkImport: '/fake_create_bulk_import',
jobs: '/fake_jobs',
};
@@ -55,14 +53,6 @@ describe('Bulk import resolvers', () => {
client = createClient();
axiosMockAdapter.onGet(FAKE_ENDPOINTS.status).reply(httpStatus.OK, statusEndpointFixture);
- axiosMockAdapter.onGet(FAKE_ENDPOINTS.availableNamespaces).reply(
- httpStatus.OK,
- availableNamespacesFixture.map((ns) => ({
- id: ns.id,
- full_path: ns.fullPath,
- })),
- );
-
client.watchQuery({ query: bulkImportSourceGroupsQuery }).subscribe(({ data }) => {
results = data.bulkImportSourceGroups.nodes;
});
@@ -75,22 +65,6 @@ describe('Bulk import resolvers', () => {
});
describe('queries', () => {
- describe('availableNamespaces', () => {
- let namespacesResults;
- beforeEach(async () => {
- const response = await client.query({ query: availableNamespacesQuery });
- namespacesResults = response.data.availableNamespaces;
- });
-
- it('mirrors REST endpoint response fields', () => {
- const extractRelevantFields = (obj) => ({ id: obj.id, full_path: obj.full_path });
-
- expect(namespacesResults.map(extractRelevantFields)).toStrictEqual(
- availableNamespacesFixture.map(extractRelevantFields),
- );
- });
- });
-
describe('bulkImportSourceGroups', () => {
it('respects cached import state when provided by group manager', async () => {
const [localStorageCache] = LocalStorageCache.mock.instances;
diff --git a/spec/frontend/import_entities/import_groups/graphql/fixtures.js b/spec/frontend/import_entities/import_groups/graphql/fixtures.js
index 938020e03f0..7530e9fc348 100644
--- a/spec/frontend/import_entities/import_groups/graphql/fixtures.js
+++ b/spec/frontend/import_entities/import_groups/graphql/fixtures.js
@@ -59,9 +59,36 @@ export const statusEndpointFixture = {
},
};
-export const availableNamespacesFixture = Object.freeze([
- { id: 24, fullPath: 'Commit451' },
- { id: 22, fullPath: 'gitlab-org' },
- { id: 23, fullPath: 'gnuwget' },
- { id: 25, fullPath: 'jashkenas' },
-]);
+const makeGroupMock = ({ id, fullPath }) => ({
+ id,
+ fullPath,
+ name: fullPath,
+ visibility: 'public',
+ webUrl: `http://gdk.test:3000/groups/${fullPath}`,
+ __typename: 'Group',
+});
+
+export const AVAILABLE_NAMESPACES = [
+ makeGroupMock({ id: 24, fullPath: 'Commit451' }),
+ makeGroupMock({ id: 22, fullPath: 'gitlab-org' }),
+ makeGroupMock({ id: 23, fullPath: 'gnuwget' }),
+ makeGroupMock({ id: 25, fullPath: 'jashkenas' }),
+];
+
+export const availableNamespacesFixture = {
+ data: {
+ currentUser: {
+ id: 'gid://gitlab/User/1',
+ groups: {
+ nodes: AVAILABLE_NAMESPACES,
+ __typename: 'GroupConnection',
+ },
+ namespace: {
+ id: 'gid://gitlab/Namespaces::UserNamespace/1',
+ fullPath: 'root',
+ __typename: 'Namespace',
+ },
+ __typename: 'UserCore',
+ },
+ },
+};
diff --git a/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js b/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js
index 53807167fe8..51f82dab381 100644
--- a/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js
+++ b/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js
@@ -59,7 +59,6 @@ describe('ImportProjectsTable', () => {
actions: {
fetchRepos: fetchReposFn,
fetchJobs: jest.fn(),
- fetchNamespaces: jest.fn(),
importAll: importAllFn,
stopJobsPolling: jest.fn(),
clearJobsEtagPoll: jest.fn(),
@@ -95,12 +94,6 @@ describe('ImportProjectsTable', () => {
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
- it('renders a loading icon while namespaces are loading', () => {
- createComponent({ state: { isLoadingNamespaces: true } });
-
- expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
- });
-
it('renders a table with provider repos', () => {
const repositories = [
{ importSource: { id: 1 }, importedProject: null },
@@ -214,35 +207,52 @@ describe('ImportProjectsTable', () => {
});
describe('when paginatable is set to true', () => {
- const pageInfo = { page: 1 };
+ const initState = {
+ namespaces: [{ fullPath: 'path' }],
+ pageInfo: { page: 1, hasNextPage: true },
+ repositories: [
+ { importSource: { id: 1 }, importedProject: null, importStatus: STATUSES.NONE },
+ ],
+ };
+
+ describe('with hasNextPage true', () => {
+ beforeEach(() => {
+ createComponent({
+ state: initState,
+ paginatable: true,
+ });
+ });
- beforeEach(() => {
- createComponent({
- state: {
- namespaces: [{ fullPath: 'path' }],
- pageInfo,
- repositories: [
- { importSource: { id: 1 }, importedProject: null, importStatus: STATUSES.NONE },
- ],
- },
- paginatable: true,
+ it('does not call fetchRepos on mount', () => {
+ expect(fetchReposFn).not.toHaveBeenCalled();
});
- });
- it('does not call fetchRepos on mount', () => {
- expect(fetchReposFn).not.toHaveBeenCalled();
- });
+ it('renders intersection observer component', () => {
+ expect(wrapper.findComponent(GlIntersectionObserver).exists()).toBe(true);
+ });
+
+ it('calls fetchRepos when intersection observer appears', async () => {
+ wrapper.findComponent(GlIntersectionObserver).vm.$emit('appear');
- it('renders intersection observer component', () => {
- expect(wrapper.findComponent(GlIntersectionObserver).exists()).toBe(true);
+ await nextTick();
+
+ expect(fetchReposFn).toHaveBeenCalled();
+ });
});
- it('calls fetchRepos when intersection observer appears', async () => {
- wrapper.findComponent(GlIntersectionObserver).vm.$emit('appear');
+ describe('with hasNextPage false', () => {
+ beforeEach(() => {
+ initState.pageInfo.hasNextPage = false;
- await nextTick();
+ createComponent({
+ state: initState,
+ paginatable: true,
+ });
+ });
- expect(fetchReposFn).toHaveBeenCalled();
+ it('does not render intersection observer component', () => {
+ expect(wrapper.findComponent(GlIntersectionObserver).exists()).toBe(false);
+ });
});
});
diff --git a/spec/frontend/import_entities/import_projects/components/provider_repo_table_row_spec.js b/spec/frontend/import_entities/import_projects/components/provider_repo_table_row_spec.js
index 40934e90b78..d686036781f 100644
--- a/spec/frontend/import_entities/import_projects/components/provider_repo_table_row_spec.js
+++ b/spec/frontend/import_entities/import_projects/components/provider_repo_table_row_spec.js
@@ -10,13 +10,13 @@ import ProviderRepoTableRow from '~/import_entities/import_projects/components/p
describe('ProviderRepoTableRow', () => {
let wrapper;
const fetchImport = jest.fn();
+ const cancelImport = jest.fn();
const setImportTarget = jest.fn();
const fakeImportTarget = {
targetNamespace: 'target',
newName: 'newName',
};
- const availableNamespaces = ['test'];
const userNamespace = 'root';
function initStore(initialState) {
@@ -25,7 +25,7 @@ describe('ProviderRepoTableRow', () => {
getters: {
getImportTarget: () => () => fakeImportTarget,
},
- actions: { fetchImport, setImportTarget },
+ actions: { fetchImport, cancelImport, setImportTarget },
});
return store;
@@ -37,6 +37,14 @@ describe('ProviderRepoTableRow', () => {
return buttons.length ? buttons.at(0) : buttons;
};
+ const findCancelButton = () => {
+ const buttons = wrapper
+ .findAllComponents(GlButton)
+ .filter((node) => node.attributes('aria-label') === 'Cancel');
+
+ return buttons.length ? buttons.at(0) : buttons;
+ };
+
function mountComponent(props) {
Vue.use(Vuex);
@@ -44,7 +52,7 @@ describe('ProviderRepoTableRow', () => {
wrapper = shallowMount(ProviderRepoTableRow, {
store,
- propsData: { availableNamespaces, userNamespace, optionalStages: {}, ...props },
+ propsData: { userNamespace, optionalStages: {}, ...props },
});
}
@@ -78,9 +86,7 @@ describe('ProviderRepoTableRow', () => {
});
it('renders a group namespace select', () => {
- expect(wrapper.findComponent(ImportGroupDropdown).props().namespaces).toBe(
- availableNamespaces,
- );
+ expect(wrapper.findComponent(ImportGroupDropdown).exists()).toBe(true);
});
it('renders import button', () => {
@@ -113,6 +119,52 @@ describe('ProviderRepoTableRow', () => {
});
});
+ describe('when rendering importing project', () => {
+ const repo = {
+ importSource: {
+ id: 'remote-1',
+ fullName: 'fullName',
+ providerLink: 'providerLink',
+ },
+ importedProject: {
+ id: 1,
+ fullPath: 'fullPath',
+ importSource: 'importSource',
+ importStatus: STATUSES.STARTED,
+ },
+ };
+
+ describe('when cancelable is true', () => {
+ beforeEach(() => {
+ mountComponent({ repo, cancelable: true });
+ });
+
+ it('shows cancel button', () => {
+ expect(findCancelButton().isVisible()).toBe(true);
+ });
+
+ it('cancels import when clicking cancel button', async () => {
+ findCancelButton().vm.$emit('click');
+
+ await nextTick();
+
+ expect(cancelImport).toHaveBeenCalledWith(expect.anything(), {
+ repoId: repo.importSource.id,
+ });
+ });
+ });
+
+ describe('when cancelable is false', () => {
+ beforeEach(() => {
+ mountComponent({ repo, cancelable: false });
+ });
+
+ it('hides cancel button', () => {
+ expect(findCancelButton().isVisible()).toBe(false);
+ });
+ });
+ });
+
describe('when rendering imported project', () => {
const FAKE_STATS = {};
diff --git a/spec/frontend/import_entities/import_projects/store/actions_spec.js b/spec/frontend/import_entities/import_projects/store/actions_spec.js
index e154863f339..4b34c21daa3 100644
--- a/spec/frontend/import_entities/import_projects/store/actions_spec.js
+++ b/spec/frontend/import_entities/import_projects/store/actions_spec.js
@@ -2,7 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
import { TEST_HOST } from 'helpers/test_constants';
import testAction from 'helpers/vuex_action_helper';
import { createAlert } from '~/flash';
-import { STATUSES } from '~/import_entities/constants';
+import { STATUSES, PROVIDERS } from '~/import_entities/constants';
import actionsFactory from '~/import_entities/import_projects/store/actions';
import { getImportTarget } from '~/import_entities/import_projects/store/getters';
import {
@@ -13,11 +13,10 @@ import {
RECEIVE_IMPORT_SUCCESS,
RECEIVE_IMPORT_ERROR,
RECEIVE_JOBS_SUCCESS,
- REQUEST_NAMESPACES,
- RECEIVE_NAMESPACES_SUCCESS,
- RECEIVE_NAMESPACES_ERROR,
+ CANCEL_IMPORT_SUCCESS,
SET_PAGE,
SET_FILTER,
+ SET_PAGE_CURSORS,
} from '~/import_entities/import_projects/store/mutation_types';
import state from '~/import_entities/import_projects/store/state';
import axios from '~/lib/utils/axios_utils';
@@ -30,7 +29,7 @@ const endpoints = {
reposPath: MOCK_ENDPOINT,
importPath: MOCK_ENDPOINT,
jobsPath: MOCK_ENDPOINT,
- namespacesPath: MOCK_ENDPOINT,
+ cancelPath: MOCK_ENDPOINT,
};
const {
@@ -39,8 +38,8 @@ const {
importAll,
fetchRepos,
fetchImport,
+ cancelImport,
fetchJobs,
- fetchNamespaces,
setFilter,
} = actionsFactory({
endpoints,
@@ -59,14 +58,17 @@ describe('import_projects store actions', () => {
...state(),
defaultTargetNamespace,
repositories: [
- { importSource: { id: importRepoId, sanitizedName }, importStatus: STATUSES.NONE },
+ {
+ importSource: { id: importRepoId, sanitizedName },
+ importedProject: { importStatus: STATUSES.NONE },
+ },
{
importSource: { id: otherImportRepoId, sanitizedName: 's2' },
- importStatus: STATUSES.NONE,
+ importedProject: { importStatus: STATUSES.NONE },
},
{
importSource: { id: 3, sanitizedName: 's3', incompatible: true },
- importStatus: STATUSES.NONE,
+ importedProject: { importStatus: STATUSES.NONE },
},
],
provider: 'provider',
@@ -77,7 +79,11 @@ describe('import_projects store actions', () => {
describe('fetchRepos', () => {
let mock;
- const payload = { imported_projects: [{}], provider_repos: [{}] };
+ const payload = {
+ imported_projects: [{}],
+ provider_repos: [{}],
+ page_info: { startCursor: 'start', endCursor: 'end', hasNextPage: true },
+ };
beforeEach(() => {
mock = new MockAdapter(axios);
@@ -85,23 +91,53 @@ describe('import_projects store actions', () => {
afterEach(() => mock.restore());
- it('commits REQUEST_REPOS, SET_PAGE, RECEIVE_REPOS_SUCCESS mutations on a successful request', () => {
- mock.onGet(MOCK_ENDPOINT).reply(200, payload);
+ describe('with a successful request', () => {
+ it('commits REQUEST_REPOS, SET_PAGE, RECEIVE_REPOS_SUCCESS mutations', () => {
+ mock.onGet(MOCK_ENDPOINT).reply(200, payload);
- return testAction(
- fetchRepos,
- null,
- localState,
- [
- { type: REQUEST_REPOS },
- { type: SET_PAGE, payload: 1 },
- {
- type: RECEIVE_REPOS_SUCCESS,
- payload: convertObjectPropsToCamelCase(payload, { deep: true }),
- },
- ],
- [],
- );
+ return testAction(
+ fetchRepos,
+ null,
+ localState,
+ [
+ { type: REQUEST_REPOS },
+ { type: SET_PAGE, payload: 1 },
+ {
+ type: RECEIVE_REPOS_SUCCESS,
+ payload: convertObjectPropsToCamelCase(payload, { deep: true }),
+ },
+ ],
+ [],
+ );
+ });
+
+ describe('when provider is GITHUB_PROVIDER', () => {
+ beforeEach(() => {
+ localState.provider = PROVIDERS.GITHUB;
+ });
+
+ it('commits SET_PAGE_CURSORS instead of SET_PAGE', () => {
+ mock.onGet(MOCK_ENDPOINT).reply(200, payload);
+
+ return testAction(
+ fetchRepos,
+ null,
+ localState,
+ [
+ { type: REQUEST_REPOS },
+ {
+ type: SET_PAGE_CURSORS,
+ payload: { startCursor: 'start', endCursor: 'end', hasNextPage: true },
+ },
+ {
+ type: RECEIVE_REPOS_SUCCESS,
+ payload: convertObjectPropsToCamelCase(payload, { deep: true }),
+ },
+ ],
+ [],
+ );
+ });
+ });
});
it('commits REQUEST_REPOS, RECEIVE_REPOS_ERROR mutations on an unsuccessful request', () => {
@@ -116,18 +152,52 @@ describe('import_projects store actions', () => {
);
});
- it('includes page in url query params', async () => {
- let requestedUrl;
- mock.onGet().reply((config) => {
- requestedUrl = config.url;
- return [200, payload];
+ describe('with pagination params', () => {
+ it('includes page in url query params', async () => {
+ let requestedUrl;
+ mock.onGet().reply((config) => {
+ requestedUrl = config.url;
+ return [200, payload];
+ });
+
+ const localStateWithPage = { ...localState, pageInfo: { page: 2 } };
+
+ await testAction(
+ fetchRepos,
+ null,
+ localStateWithPage,
+ expect.any(Array),
+ expect.any(Array),
+ );
+
+ expect(requestedUrl).toBe(`${MOCK_ENDPOINT}?page=${localStateWithPage.pageInfo.page + 1}`);
});
- const localStateWithPage = { ...localState, pageInfo: { page: 2 } };
+ describe('when provider is "github"', () => {
+ beforeEach(() => {
+ localState.provider = PROVIDERS.GITHUB;
+ });
+
+ it('includes cursor in url query params', async () => {
+ let requestedUrl;
+ mock.onGet().reply((config) => {
+ requestedUrl = config.url;
+ return [200, payload];
+ });
- await testAction(fetchRepos, null, localStateWithPage, expect.any(Array), expect.any(Array));
+ const localStateWithPage = { ...localState, pageInfo: { endCursor: 'endTest' } };
- expect(requestedUrl).toBe(`${MOCK_ENDPOINT}?page=${localStateWithPage.pageInfo.page + 1}`);
+ await testAction(
+ fetchRepos,
+ null,
+ localStateWithPage,
+ expect.any(Array),
+ expect.any(Array),
+ );
+
+ expect(requestedUrl).toBe(`${MOCK_ENDPOINT}?after=endTest`);
+ });
+ });
});
it('correctly keeps current page on an unsuccessful request', () => {
@@ -319,51 +389,6 @@ describe('import_projects store actions', () => {
});
});
- describe('fetchNamespaces', () => {
- let mock;
- const namespaces = [{ full_name: 'test/ns1' }, { full_name: 'test_ns2' }];
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => mock.restore());
-
- it('commits REQUEST_NAMESPACES and RECEIVE_NAMESPACES_SUCCESS on success', async () => {
- mock.onGet(MOCK_ENDPOINT).reply(200, namespaces);
-
- await testAction(
- fetchNamespaces,
- null,
- localState,
- [
- { type: REQUEST_NAMESPACES },
- {
- type: RECEIVE_NAMESPACES_SUCCESS,
- payload: convertObjectPropsToCamelCase(namespaces, { deep: true }),
- },
- ],
- [],
- );
- });
-
- it('commits REQUEST_NAMESPACES and RECEIVE_NAMESPACES_ERROR and shows generic error message on an unsuccessful request', async () => {
- mock.onGet(MOCK_ENDPOINT).reply(500);
-
- await testAction(
- fetchNamespaces,
- null,
- localState,
- [{ type: REQUEST_NAMESPACES }, { type: RECEIVE_NAMESPACES_ERROR }],
- [],
- );
-
- expect(createAlert).toHaveBeenCalledWith({
- message: 'Requesting namespaces failed',
- });
- });
- });
-
describe('importAll', () => {
it('dispatches multiple fetchImport actions', async () => {
const OPTIONAL_STAGES = { stage1: true, stage2: false };
@@ -398,4 +423,51 @@ describe('import_projects store actions', () => {
);
});
});
+
+ describe('cancelImport', () => {
+ let mock;
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => mock.restore());
+
+ it('commits CANCEL_IMPORT_SUCCESS on success', async () => {
+ mock.onPost(MOCK_ENDPOINT).reply(200);
+
+ await testAction(
+ cancelImport,
+ { repoId: importRepoId },
+ localState,
+ [
+ {
+ type: CANCEL_IMPORT_SUCCESS,
+ payload: { repoId: 1 },
+ },
+ ],
+ [],
+ );
+ });
+
+ it('shows generic error message on an unsuccessful request', async () => {
+ mock.onPost(MOCK_ENDPOINT).reply(500);
+
+ await testAction(cancelImport, { repoId: importRepoId }, localState, [], []);
+
+ expect(createAlert).toHaveBeenCalledWith({
+ message: 'Cancelling project import failed',
+ });
+ });
+
+ it('shows detailed error message on an unsuccessful request with errors fields in response', async () => {
+ const ERROR_MESSAGE = 'dummy';
+ mock.onPost(MOCK_ENDPOINT).reply(500, { errors: ERROR_MESSAGE });
+
+ await testAction(cancelImport, { repoId: importRepoId }, localState, [], []);
+
+ expect(createAlert).toHaveBeenCalledWith({
+ message: `Cancelling project import failed: ${ERROR_MESSAGE}`,
+ });
+ });
+ });
});
diff --git a/spec/frontend/import_entities/import_projects/store/getters_spec.js b/spec/frontend/import_entities/import_projects/store/getters_spec.js
index 110b692b222..fced5670f25 100644
--- a/spec/frontend/import_entities/import_projects/store/getters_spec.js
+++ b/spec/frontend/import_entities/import_projects/store/getters_spec.js
@@ -1,6 +1,5 @@
import { STATUSES } from '~/import_entities/constants';
import {
- isLoading,
isImportingAnyRepo,
hasIncompatibleRepos,
hasImportableRepos,
@@ -31,24 +30,6 @@ describe('import_projects store getters', () => {
});
it.each`
- isLoadingRepos | isLoadingNamespaces | isLoadingValue
- ${false} | ${false} | ${false}
- ${true} | ${false} | ${true}
- ${false} | ${true} | ${true}
- ${true} | ${true} | ${true}
- `(
- 'isLoading returns $isLoadingValue when isLoadingRepos is $isLoadingRepos and isLoadingNamespaces is $isLoadingNamespaces',
- ({ isLoadingRepos, isLoadingNamespaces, isLoadingValue }) => {
- Object.assign(localState, {
- isLoadingRepos,
- isLoadingNamespaces,
- });
-
- expect(isLoading(localState)).toBe(isLoadingValue);
- },
- );
-
- it.each`
importStatus | value
${STATUSES.NONE} | ${false}
${STATUSES.SCHEDULING} | ${true}
diff --git a/spec/frontend/import_entities/import_projects/store/mutations_spec.js b/spec/frontend/import_entities/import_projects/store/mutations_spec.js
index 77fae951300..7884e9b4307 100644
--- a/spec/frontend/import_entities/import_projects/store/mutations_spec.js
+++ b/spec/frontend/import_entities/import_projects/store/mutations_spec.js
@@ -27,7 +27,12 @@ describe('import_projects store mutations', () => {
state = {
filter: 'some-value',
repositories: ['some', ' repositories'],
- pageInfo: { page: 1 },
+ pageInfo: {
+ page: 1,
+ startCursor: 'Y3Vyc30yOjI2',
+ endCursor: 'Y3Vyc29yOjI1',
+ hasNextPage: false,
+ },
};
mutations[types.SET_FILTER](state, NEW_VALUE);
});
@@ -36,8 +41,11 @@ describe('import_projects store mutations', () => {
expect(state.repositories.length).toBe(0);
});
- it('resets current page to 0', () => {
+ it('resets pagintation', () => {
expect(state.pageInfo.page).toBe(0);
+ expect(state.pageInfo.startCursor).toBe(null);
+ expect(state.pageInfo.endCursor).toBe(null);
+ expect(state.pageInfo.hasNextPage).toBe(true);
});
});
@@ -263,43 +271,6 @@ describe('import_projects store mutations', () => {
});
});
- describe(`${types.REQUEST_NAMESPACES}`, () => {
- it('sets namespaces loading flag to true', () => {
- state = {};
-
- mutations[types.REQUEST_NAMESPACES](state);
-
- expect(state.isLoadingNamespaces).toBe(true);
- });
- });
-
- describe(`${types.RECEIVE_NAMESPACES_SUCCESS}`, () => {
- const response = [{ fullPath: 'some/path' }];
-
- beforeEach(() => {
- state = {};
- mutations[types.RECEIVE_NAMESPACES_SUCCESS](state, response);
- });
-
- it('stores namespaces to state', () => {
- expect(state.namespaces).toStrictEqual(response);
- });
-
- it('sets namespaces loading flag to false', () => {
- expect(state.isLoadingNamespaces).toBe(false);
- });
- });
-
- describe(`${types.RECEIVE_NAMESPACES_ERROR}`, () => {
- it('sets namespaces loading flag to false', () => {
- state = {};
-
- mutations[types.RECEIVE_NAMESPACES_ERROR](state);
-
- expect(state.isLoadingNamespaces).toBe(false);
- });
- });
-
describe(`${types.SET_IMPORT_TARGET}`, () => {
const PROJECT = {
id: 2,
@@ -345,4 +316,34 @@ describe('import_projects store mutations', () => {
expect(state.pageInfo.page).toBe(NEW_PAGE);
});
});
+
+ describe(`${types.SET_PAGE_CURSORS}`, () => {
+ it('sets page cursors', () => {
+ const NEW_CURSORS = { startCursor: 'startCur', endCursor: 'endCur', hasNextPage: false };
+ state = { pageInfo: { page: 1, startCursor: null, endCursor: null, hasNextPage: true } };
+
+ mutations[types.SET_PAGE_CURSORS](state, NEW_CURSORS);
+ expect(state.pageInfo).toEqual({ ...NEW_CURSORS, page: 1 });
+ });
+ });
+
+ describe(`${types.CANCEL_IMPORT_SUCCESS}`, () => {
+ const payload = { repoId: 1 };
+
+ beforeEach(() => {
+ state = {
+ repositories: [
+ {
+ importSource: { id: 1 },
+ importedProject: { importStatus: STATUSES.NONE },
+ },
+ ],
+ };
+ mutations[types.CANCEL_IMPORT_SUCCESS](state, payload);
+ });
+
+ it('updates project status', () => {
+ expect(state.repositories[0].importedProject.importStatus).toBe(STATUSES.CANCELED);
+ });
+ });
});
diff --git a/spec/frontend/incidents_settings/components/incidents_settings_service_spec.js b/spec/frontend/incidents_settings/components/incidents_settings_service_spec.js
index 1b0253480e0..08c407cc4b4 100644
--- a/spec/frontend/incidents_settings/components/incidents_settings_service_spec.js
+++ b/spec/frontend/incidents_settings/components/incidents_settings_service_spec.js
@@ -1,5 +1,5 @@
import AxiosMockAdapter from 'axios-mock-adapter';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import { ERROR_MSG } from '~/incidents_settings/constants';
import IncidentsSettingsService from '~/incidents_settings/incidents_settings_service';
import axios from '~/lib/utils/axios_utils';
@@ -37,7 +37,7 @@ describe('IncidentsSettingsService', () => {
mock.onPatch().reply(httpStatusCodes.BAD_REQUEST);
return service.updateSettings({}).then(() => {
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: expect.stringContaining(ERROR_MSG),
});
});
diff --git a/spec/frontend/integrations/edit/components/dynamic_field_spec.js b/spec/frontend/integrations/edit/components/dynamic_field_spec.js
index 5af0e272285..7589b04b0fd 100644
--- a/spec/frontend/integrations/edit/components/dynamic_field_spec.js
+++ b/spec/frontend/integrations/edit/components/dynamic_field_spec.js
@@ -7,11 +7,16 @@ import { mockField } from '../mock_data';
describe('DynamicField', () => {
let wrapper;
- const createComponent = (props, isInheriting = false) => {
+ const createComponent = (props, isInheriting = false, editable = true) => {
wrapper = mount(DynamicField, {
propsData: { ...mockField, ...props },
computed: {
isInheriting: () => isInheriting,
+ propsSource: () => {
+ return {
+ editable,
+ };
+ },
},
});
};
@@ -28,12 +33,14 @@ describe('DynamicField', () => {
describe('template', () => {
describe.each`
- isInheriting | disabled | readonly | checkboxLabel
- ${true} | ${'disabled'} | ${'readonly'} | ${undefined}
- ${false} | ${undefined} | ${undefined} | ${'Custom checkbox label'}
+ isInheriting | editable | disabled | readonly | checkboxLabel
+ ${true} | ${true} | ${'disabled'} | ${'readonly'} | ${undefined}
+ ${false} | ${true} | ${undefined} | ${undefined} | ${'Custom checkbox label'}
+ ${true} | ${false} | ${'disabled'} | ${'readonly'} | ${undefined}
+ ${false} | ${false} | ${'disabled'} | ${undefined} | ${'Custom checkbox label'}
`(
- 'dynamic field, when isInheriting = `%p`',
- ({ isInheriting, disabled, readonly, checkboxLabel }) => {
+ 'dynamic field, when isInheriting = `$isInheriting` and editable = `$editable`',
+ ({ isInheriting, editable, disabled, readonly, checkboxLabel }) => {
describe('type is checkbox', () => {
beforeEach(() => {
createComponent(
@@ -42,6 +49,7 @@ describe('DynamicField', () => {
checkboxLabel,
},
isInheriting,
+ editable,
);
});
@@ -74,6 +82,7 @@ describe('DynamicField', () => {
],
},
isInheriting,
+ editable,
);
});
@@ -97,6 +106,7 @@ describe('DynamicField', () => {
type: 'textarea',
},
isInheriting,
+ editable,
);
});
@@ -119,6 +129,7 @@ describe('DynamicField', () => {
type: 'password',
},
isInheriting,
+ editable,
);
});
@@ -143,6 +154,7 @@ describe('DynamicField', () => {
required: true,
},
isInheriting,
+ editable,
);
});
@@ -204,7 +216,7 @@ describe('DynamicField', () => {
});
expect(findGlFormGroup().find('small').html()).toContain(
- '[<code>1</code> <a>3</a> <a href="foo">4</a>]',
+ '[<code>1</code> <a>3</a> <a href="foo" target="_blank" rel="noopener noreferrer">4</a>',
);
});
});
diff --git a/spec/frontend/integrations/edit/components/integration_form_actions_spec.js b/spec/frontend/integrations/edit/components/integration_form_actions_spec.js
new file mode 100644
index 00000000000..e95e30a1899
--- /dev/null
+++ b/spec/frontend/integrations/edit/components/integration_form_actions_spec.js
@@ -0,0 +1,227 @@
+import { nextTick } from 'vue';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import ConfirmationModal from '~/integrations/edit/components/confirmation_modal.vue';
+import ResetConfirmationModal from '~/integrations/edit/components/reset_confirmation_modal.vue';
+import IntegrationFormActions from '~/integrations/edit/components/integration_form_actions.vue';
+
+import { integrationLevels } from '~/integrations/constants';
+import { createStore } from '~/integrations/edit/store';
+import { mockIntegrationProps } from '../mock_data';
+
+describe('IntegrationFormActions', () => {
+ let wrapper;
+
+ const createComponent = ({ customStateProps = {} } = {}) => {
+ const store = createStore({
+ customState: { ...mockIntegrationProps, ...customStateProps },
+ });
+ jest.spyOn(store, 'dispatch');
+
+ wrapper = shallowMountExtended(IntegrationFormActions, {
+ store,
+ propsData: {
+ hasSections: false,
+ },
+ });
+ };
+
+ const findConfirmationModal = () => wrapper.findComponent(ConfirmationModal);
+ const findResetConfirmationModal = () => wrapper.findComponent(ResetConfirmationModal);
+ const findResetButton = () => wrapper.findByTestId('reset-button');
+ const findSaveButton = () => wrapper.findByTestId('save-button');
+ const findTestButton = () => wrapper.findByTestId('test-button');
+ const findCancelButton = () => wrapper.findByTestId('cancel-button');
+
+ describe('ConfirmationModal', () => {
+ it.each`
+ desc | integrationLevel | shouldRender
+ ${'Should'} | ${integrationLevels.INSTANCE} | ${true}
+ ${'Should'} | ${integrationLevels.GROUP} | ${true}
+ ${'Should not'} | ${integrationLevels.PROJECT} | ${false}
+ `(
+ '$desc render the ConfirmationModal when integrationLevel is "$integrationLevel"',
+ ({ integrationLevel, shouldRender }) => {
+ createComponent({
+ customStateProps: {
+ integrationLevel,
+ },
+ });
+ expect(findConfirmationModal().exists()).toBe(shouldRender);
+ },
+ );
+ });
+
+ describe('ResetConfirmationModal', () => {
+ it.each`
+ desc | integrationLevel | resetPath | shouldRender
+ ${'Should not'} | ${integrationLevels.INSTANCE} | ${''} | ${false}
+ ${'Should not'} | ${integrationLevels.GROUP} | ${''} | ${false}
+ ${'Should not'} | ${integrationLevels.PROJECT} | ${''} | ${false}
+ ${'Should'} | ${integrationLevels.INSTANCE} | ${'resetPath'} | ${true}
+ ${'Should'} | ${integrationLevels.GROUP} | ${'resetPath'} | ${true}
+ ${'Should not'} | ${integrationLevels.PROJECT} | ${'resetPath'} | ${false}
+ `(
+ '$desc render the ResetConfirmationModal modal when integrationLevel="$integrationLevel" and resetPath="$resetPath"',
+ ({ integrationLevel, resetPath, shouldRender }) => {
+ createComponent({
+ customStateProps: {
+ integrationLevel,
+ resetPath,
+ },
+ });
+ expect(findResetConfirmationModal().exists()).toBe(shouldRender);
+ },
+ );
+ });
+
+ describe('Buttons rendering', () => {
+ it.each`
+ integrationLevel | canTest | resetPath | saveBtn | testBtn | cancelBtn | resetBtn
+ ${integrationLevels.PROJECT} | ${true} | ${'resetPath'} | ${true} | ${true} | ${true} | ${false}
+ ${integrationLevels.PROJECT} | ${false} | ${'resetPath'} | ${true} | ${false} | ${true} | ${false}
+ ${integrationLevels.PROJECT} | ${true} | ${''} | ${true} | ${true} | ${true} | ${false}
+ ${integrationLevels.GROUP} | ${true} | ${'resetPath'} | ${true} | ${true} | ${true} | ${true}
+ ${integrationLevels.GROUP} | ${false} | ${'resetPath'} | ${true} | ${false} | ${true} | ${true}
+ ${integrationLevels.GROUP} | ${true} | ${''} | ${true} | ${true} | ${true} | ${false}
+ ${integrationLevels.INSTANCE} | ${true} | ${'resetPath'} | ${true} | ${true} | ${true} | ${true}
+ ${integrationLevels.INSTANCE} | ${false} | ${'resetPath'} | ${true} | ${false} | ${true} | ${true}
+ ${integrationLevels.INSTANCE} | ${true} | ${''} | ${true} | ${true} | ${true} | ${false}
+ `(
+ 'on $integrationLevel when canTest="$canTest" and resetPath="$resetPath"',
+ ({ integrationLevel, canTest, resetPath, saveBtn, testBtn, cancelBtn, resetBtn }) => {
+ createComponent({
+ customStateProps: {
+ integrationLevel,
+ canTest,
+ resetPath,
+ },
+ });
+
+ expect(findSaveButton().exists()).toBe(saveBtn);
+ expect(findTestButton().exists()).toBe(testBtn);
+ expect(findCancelButton().exists()).toBe(cancelBtn);
+ expect(findResetButton().exists()).toBe(resetBtn);
+ },
+ );
+ });
+
+ describe('interactions', () => {
+ describe('Save button clicked', () => {
+ const createAndSave = (integrationLevel, withModal = false) => {
+ createComponent({
+ customStateProps: {
+ integrationLevel,
+ canTest: true,
+ resetPath: 'resetPath',
+ },
+ });
+
+ findSaveButton().vm.$emit('click', new Event('click'));
+ if (withModal) {
+ findConfirmationModal().vm.$emit('submit');
+ }
+ wrapper.setProps({
+ isSaving: true,
+ });
+ };
+ const sharedFormStateTest = async (integrationLevel, withModal = false) => {
+ createAndSave(integrationLevel, withModal);
+
+ await nextTick();
+
+ const saveBtnWrapper = findSaveButton();
+ const testBtnWrapper = findTestButton();
+ const cancelBtnWrapper = findCancelButton();
+
+ expect(saveBtnWrapper.props('loading')).toBe(true);
+ expect(saveBtnWrapper.props('disabled')).toBe(true);
+
+ expect(testBtnWrapper.props('loading')).toBe(false);
+ expect(testBtnWrapper.props('disabled')).toBe(true);
+
+ expect(cancelBtnWrapper.props('loading')).toBe(false);
+ expect(cancelBtnWrapper.props('disabled')).toBe(true);
+ };
+
+ describe('on "project" level', () => {
+ const integrationLevel = integrationLevels.PROJECT;
+ it('emits the "save" event right away', async () => {
+ createAndSave(integrationLevel);
+ await nextTick();
+
+ expect(wrapper.emitted('save')).toHaveLength(1);
+ });
+
+ it('toggles the state of other buttons', async () => {
+ await sharedFormStateTest(integrationLevel);
+
+ const resetBtnWrapper = findResetButton();
+ expect(resetBtnWrapper.exists()).toBe(false);
+ });
+ });
+
+ describe.each([integrationLevels.INSTANCE, integrationLevels.GROUP])(
+ 'on "%s" level',
+ (integrationLevel) => {
+ it('emits the "save" event only after the confirmation', () => {
+ createComponent({
+ customStateProps: {
+ integrationLevel,
+ },
+ });
+
+ findSaveButton().vm.$emit('click', new Event('click'));
+ expect(wrapper.emitted('save')).toBeUndefined();
+
+ findConfirmationModal().vm.$emit('submit');
+ expect(wrapper.emitted('save')).toHaveLength(1);
+ });
+
+ it('toggles the state of other buttons', async () => {
+ await sharedFormStateTest(integrationLevel, true);
+
+ const resetBtnWrapper = findResetButton();
+ expect(resetBtnWrapper.props('loading')).toBe(false);
+ expect(resetBtnWrapper.props('disabled')).toBe(true);
+ });
+ },
+ );
+ });
+
+ describe('Reset button clicked', () => {
+ describe.each([integrationLevels.INSTANCE, integrationLevels.GROUP])(
+ 'on "%s" level',
+ (integrationLevel) => {
+ it('emits the "reset" event only after the confirmation', () => {
+ createComponent({
+ customStateProps: {
+ integrationLevel,
+ resetPath: 'resetPath',
+ },
+ });
+
+ findResetButton().vm.$emit('click', new Event('click'));
+ expect(wrapper.emitted('reset')).toBeUndefined();
+
+ findResetConfirmationModal().vm.$emit('reset');
+ expect(wrapper.emitted('reset')).toHaveLength(1);
+ });
+ },
+ );
+ });
+
+ describe('Test button clicked', () => {
+ it('emits the "test" event when clicked', () => {
+ createComponent({
+ customStateProps: {
+ integrationLevel: integrationLevels.PROJECT,
+ canTest: true,
+ },
+ });
+
+ findTestButton().vm.$emit('click', new Event('click'));
+ expect(wrapper.emitted('test')).toHaveLength(1);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/integrations/edit/components/integration_form_spec.js b/spec/frontend/integrations/edit/components/integration_form_spec.js
index 7e67379f5ab..4b49e492880 100644
--- a/spec/frontend/integrations/edit/components/integration_form_spec.js
+++ b/spec/frontend/integrations/edit/components/integration_form_spec.js
@@ -1,21 +1,20 @@
import { GlAlert, GlBadge, GlForm } from '@gitlab/ui';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
+import { nextTick } from 'vue';
import * as Sentry from '@sentry/browser';
import { setHTMLFixture } from 'helpers/fixtures';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import ActiveCheckbox from '~/integrations/edit/components/active_checkbox.vue';
-import ConfirmationModal from '~/integrations/edit/components/confirmation_modal.vue';
import DynamicField from '~/integrations/edit/components/dynamic_field.vue';
import IntegrationForm from '~/integrations/edit/components/integration_form.vue';
import OverrideDropdown from '~/integrations/edit/components/override_dropdown.vue';
-import ResetConfirmationModal from '~/integrations/edit/components/reset_confirmation_modal.vue';
import TriggerFields from '~/integrations/edit/components/trigger_fields.vue';
import IntegrationSectionConnection from '~/integrations/edit/components/sections/connection.vue';
+import IntegrationFormActions from '~/integrations/edit/components/integration_form_actions.vue';
import {
- integrationLevels,
I18N_SUCCESSFUL_CONNECTION_MESSAGE,
I18N_DEFAULT_ERROR_MESSAGE,
INTEGRATION_FORM_TYPE_SLACK,
@@ -60,7 +59,6 @@ describe('IntegrationForm', () => {
stubs: {
OverrideDropdown,
ActiveCheckbox,
- ConfirmationModal,
TriggerFields,
},
mocks: {
@@ -73,12 +71,6 @@ describe('IntegrationForm', () => {
const findOverrideDropdown = () => wrapper.findComponent(OverrideDropdown);
const findActiveCheckbox = () => wrapper.findComponent(ActiveCheckbox);
- const findConfirmationModal = () => wrapper.findComponent(ConfirmationModal);
- const findResetConfirmationModal = () => wrapper.findComponent(ResetConfirmationModal);
- const findResetButton = () => wrapper.findByTestId('reset-button');
- const findProjectSaveButton = () => wrapper.findByTestId('save-button');
- const findInstanceOrGroupSaveButton = () => wrapper.findByTestId('save-button-instance-group');
- const findTestButton = () => wrapper.findByTestId('test-button');
const findTriggerFields = () => wrapper.findComponent(TriggerFields);
const findAlert = () => wrapper.findComponent(GlAlert);
const findGlBadge = () => wrapper.findComponent(GlBadge);
@@ -91,6 +83,7 @@ describe('IntegrationForm', () => {
const findConnectionSectionComponent = () =>
findConnectionSection().findComponent(IntegrationSectionConnection);
const findHelpHtml = () => wrapper.findByTestId('help-html');
+ const findFormActions = () => wrapper.findComponent(IntegrationFormActions);
beforeEach(() => {
mockAxios = new MockAdapter(axios);
@@ -102,108 +95,6 @@ describe('IntegrationForm', () => {
});
describe('template', () => {
- describe('integrationLevel is instance', () => {
- it('renders ConfirmationModal', () => {
- createComponent({
- customStateProps: {
- integrationLevel: integrationLevels.INSTANCE,
- },
- });
-
- expect(findConfirmationModal().exists()).toBe(true);
- });
-
- describe('resetPath is empty', () => {
- it('does not render ResetConfirmationModal and button', () => {
- createComponent({
- customStateProps: {
- integrationLevel: integrationLevels.INSTANCE,
- },
- });
-
- expect(findResetButton().exists()).toBe(false);
- expect(findResetConfirmationModal().exists()).toBe(false);
- });
- });
-
- describe('resetPath is present', () => {
- it('renders ResetConfirmationModal and button', () => {
- createComponent({
- customStateProps: {
- integrationLevel: integrationLevels.INSTANCE,
- resetPath: 'resetPath',
- },
- });
-
- expect(findResetButton().exists()).toBe(true);
- expect(findResetConfirmationModal().exists()).toBe(true);
- });
- });
- });
-
- describe('integrationLevel is group', () => {
- it('renders ConfirmationModal', () => {
- createComponent({
- customStateProps: {
- integrationLevel: integrationLevels.GROUP,
- },
- });
-
- expect(findConfirmationModal().exists()).toBe(true);
- });
-
- describe('resetPath is empty', () => {
- it('does not render ResetConfirmationModal and button', () => {
- createComponent({
- customStateProps: {
- integrationLevel: integrationLevels.GROUP,
- },
- });
-
- expect(findResetButton().exists()).toBe(false);
- expect(findResetConfirmationModal().exists()).toBe(false);
- });
- });
-
- describe('resetPath is present', () => {
- it('renders ResetConfirmationModal and button', () => {
- createComponent({
- customStateProps: {
- integrationLevel: integrationLevels.GROUP,
- resetPath: 'resetPath',
- },
- });
-
- expect(findResetButton().exists()).toBe(true);
- expect(findResetConfirmationModal().exists()).toBe(true);
- });
- });
- });
-
- describe('integrationLevel is project', () => {
- it('does not render ConfirmationModal', () => {
- createComponent({
- customStateProps: {
- integrationLevel: 'project',
- },
- });
-
- expect(findConfirmationModal().exists()).toBe(false);
- });
-
- it('does not render ResetConfirmationModal and button', () => {
- createComponent({
- customStateProps: {
- integrationLevel: 'project',
- resetPath: 'resetPath',
- },
- });
-
- expect(findResetButton().exists()).toBe(false);
- expect(findResetConfirmationModal().exists()).toBe(false);
- });
- });
-
describe('triggerEvents is present', () => {
it('renders TriggerFields', () => {
const events = [{ title: 'push' }];
@@ -462,111 +353,85 @@ describe('IntegrationForm', () => {
);
});
- describe('when `save` button is clicked', () => {
- describe('buttons', () => {
- beforeEach(async () => {
- createComponent({
- customStateProps: {
- showActive: true,
- canTest: true,
- initialActivated: true,
- },
- mountFn: mountExtended,
- });
-
- await findProjectSaveButton().vm.$emit('click', new Event('click'));
- });
-
- it('sets save button `loading` prop to `true`', () => {
- expect(findProjectSaveButton().props('loading')).toBe(true);
+ describe('Response to the "save" event (form submission)', () => {
+ const prepareComponentAndSave = async (initialActivated = true, checkValidityReturn) => {
+ createComponent({
+ customStateProps: {
+ showActive: true,
+ initialActivated,
+ fields: [mockField],
+ },
+ mountFn: mountExtended,
});
+ jest.spyOn(findGlForm().element, 'submit');
+ jest.spyOn(findGlForm().element, 'checkValidity').mockReturnValue(checkValidityReturn);
- it('sets test button `disabled` prop to `true`', () => {
- expect(findTestButton().props('disabled')).toBe(true);
- });
- });
+ findFormActions().vm.$emit('save');
+ await nextTick();
+ };
- describe.each`
- checkValidityReturn | integrationActive
- ${true} | ${false}
- ${true} | ${true}
- ${false} | ${false}
+ it.each`
+ desc | checkValidityReturn | integrationActive | shouldSubmit
+ ${'form is valid'} | ${true} | ${false} | ${true}
+ ${'form is valid'} | ${true} | ${true} | ${true}
+ ${'form is invalid'} | ${false} | ${false} | ${true}
+ ${'form is invalid'} | ${false} | ${true} | ${false}
`(
- 'when form is valid (checkValidity returns $checkValidityReturn and integrationActive is $integrationActive)',
- ({ integrationActive, checkValidityReturn }) => {
- beforeEach(async () => {
- createComponent({
- customStateProps: {
- showActive: true,
- canTest: true,
- initialActivated: integrationActive,
- },
- mountFn: mountExtended,
- });
- jest.spyOn(findGlForm().element, 'submit');
- jest.spyOn(findGlForm().element, 'checkValidity').mockReturnValue(checkValidityReturn);
-
- await findProjectSaveButton().vm.$emit('click', new Event('click'));
- });
+ 'when $desc (checkValidity returns $checkValidityReturn and integrationActive is $integrationActive)',
+ async ({ integrationActive, checkValidityReturn, shouldSubmit }) => {
+ await prepareComponentAndSave(integrationActive, checkValidityReturn);
- it('submit form', () => {
+ if (shouldSubmit) {
expect(findGlForm().element.submit).toHaveBeenCalledTimes(1);
- });
+ } else {
+ expect(findGlForm().element.submit).not.toHaveBeenCalled();
+ }
},
);
- describe('when form is invalid (checkValidity returns false and integrationActive is true)', () => {
- beforeEach(async () => {
- createComponent({
- customStateProps: {
- showActive: true,
- canTest: true,
- initialActivated: true,
- fields: [mockField],
- },
- mountFn: mountExtended,
- });
- jest.spyOn(findGlForm().element, 'submit');
- jest.spyOn(findGlForm().element, 'checkValidity').mockReturnValue(false);
-
- await findProjectSaveButton().vm.$emit('click', new Event('click'));
- });
-
- it('does not submit form', () => {
- expect(findGlForm().element.submit).not.toHaveBeenCalled();
- });
+ it('flips `isSaving` to `true`', async () => {
+ await prepareComponentAndSave(true, true);
+ expect(findFormActions().props('isSaving')).toBe(true);
+ });
- it('sets save button `loading` prop to `false`', () => {
- expect(findProjectSaveButton().props('loading')).toBe(false);
+ describe('when form is invalid', () => {
+ beforeEach(async () => {
+ await prepareComponentAndSave(true, false);
});
- it('sets test button `disabled` prop to `false`', () => {
- expect(findTestButton().props('disabled')).toBe(false);
+ it('when form is invalid, it sets `isValidated` props on form fields', () => {
+ expect(findDynamicField().props('isValidated')).toBe(true);
});
- it('sets `isValidated` props on form fields', () => {
- expect(findDynamicField().props('isValidated')).toBe(true);
+ it('resets `isSaving`', () => {
+ expect(findFormActions().props('isSaving')).toBe(false);
});
});
});
- describe('when `test` button is clicked', () => {
+ describe('Response to the "test" event from the actions', () => {
describe('when form is invalid', () => {
- it('sets `isValidated` props on form fields', async () => {
+ beforeEach(async () => {
createComponent({
customStateProps: {
showActive: true,
- canTest: true,
fields: [mockField],
},
mountFn: mountExtended,
});
jest.spyOn(findGlForm().element, 'checkValidity').mockReturnValue(false);
- await findTestButton().vm.$emit('click', new Event('click'));
+ findFormActions().vm.$emit('test');
+ await nextTick();
+ });
+ it('sets `isValidated` props on form fields', () => {
expect(findDynamicField().props('isValidated')).toBe(true);
});
+
+ it('resets `isTesting`', () => {
+ expect(findFormActions().props('isTesting')).toBe(false);
+ });
});
describe('when form is valid', () => {
@@ -576,26 +441,18 @@ describe('IntegrationForm', () => {
createComponent({
customStateProps: {
showActive: true,
- canTest: true,
testPath: mockTestPath,
},
mountFn: mountExtended,
});
+
jest.spyOn(findGlForm().element, 'checkValidity').mockReturnValue(true);
});
- describe('buttons', () => {
- beforeEach(async () => {
- await findTestButton().vm.$emit('click', new Event('click'));
- });
-
- it('sets test button `loading` prop to `true`', () => {
- expect(findTestButton().props('loading')).toBe(true);
- });
-
- it('sets save button `disabled` prop to `true`', () => {
- expect(findProjectSaveButton().props('disabled')).toBe(true);
- });
+ it('flips `isTesting` to `true`', async () => {
+ findFormActions().vm.$emit('test');
+ await nextTick();
+ expect(findFormActions().props('isTesting')).toBe(true);
});
describe.each`
@@ -614,7 +471,7 @@ describe('IntegrationForm', () => {
service_response: serviceResponse,
});
- await findTestButton().vm.$emit('click', new Event('click'));
+ findFormActions().vm.$emit('test');
await waitForPromises();
});
@@ -622,14 +479,6 @@ describe('IntegrationForm', () => {
expect(mockToastShow).toHaveBeenCalledWith(expectToast);
});
- it('sets `loading` prop of test button to `false`', () => {
- expect(findTestButton().props('loading')).toBe(false);
- });
-
- it('sets save button `disabled` prop to `false`', () => {
- expect(findProjectSaveButton().props('disabled')).toBe(false);
- });
-
it(`${expectSentry ? 'does' : 'does not'} capture exception in Sentry`, () => {
expect(Sentry.captureException).toHaveBeenCalledTimes(expectSentry ? 1 : 0);
});
@@ -638,44 +487,27 @@ describe('IntegrationForm', () => {
});
});
- describe('when `reset-confirmation-modal` emits `reset` event', () => {
+ describe('Response to the "reset" event from the actions', () => {
const mockResetPath = '/reset';
- describe('buttons', () => {
- beforeEach(async () => {
- createComponent({
- customStateProps: {
- integrationLevel: integrationLevels.GROUP,
- canTest: true,
- resetPath: mockResetPath,
- },
- });
-
- await findResetConfirmationModal().vm.$emit('reset');
+ beforeEach(async () => {
+ mockAxios.onPost(mockResetPath).replyOnce(httpStatus.INTERNAL_SERVER_ERROR);
+ createComponent({
+ customStateProps: {
+ resetPath: mockResetPath,
+ },
});
- it('sets reset button `loading` prop to `true`', () => {
- expect(findResetButton().props('loading')).toBe(true);
- });
+ findFormActions().vm.$emit('reset');
+ await nextTick();
+ });
- it('sets other button `disabled` props to `true`', () => {
- expect(findInstanceOrGroupSaveButton().props('disabled')).toBe(true);
- expect(findTestButton().props('disabled')).toBe(true);
- });
+ it('flips `isResetting` to `true`', () => {
+ expect(findFormActions().props('isResetting')).toBe(true);
});
describe('when "reset settings" request fails', () => {
beforeEach(async () => {
- mockAxios.onPost(mockResetPath).replyOnce(httpStatus.INTERNAL_SERVER_ERROR);
- createComponent({
- customStateProps: {
- integrationLevel: integrationLevels.GROUP,
- canTest: true,
- resetPath: mockResetPath,
- },
- });
-
- await findResetConfirmationModal().vm.$emit('reset');
await waitForPromises();
});
@@ -687,13 +519,8 @@ describe('IntegrationForm', () => {
expect(Sentry.captureException).toHaveBeenCalledTimes(1);
});
- it('sets reset button `loading` prop to `false`', () => {
- expect(findResetButton().props('loading')).toBe(false);
- });
-
- it('sets button `disabled` props to `false`', () => {
- expect(findInstanceOrGroupSaveButton().props('disabled')).toBe(false);
- expect(findTestButton().props('disabled')).toBe(false);
+ it('resets `isResetting`', () => {
+ expect(findFormActions().props('isResetting')).toBe(false);
});
});
@@ -702,64 +529,74 @@ describe('IntegrationForm', () => {
mockAxios.onPost(mockResetPath).replyOnce(httpStatus.OK);
createComponent({
customStateProps: {
- integrationLevel: integrationLevels.GROUP,
resetPath: mockResetPath,
},
});
- await findResetConfirmationModal().vm.$emit('reset');
+ findFormActions().vm.$emit('reset');
await waitForPromises();
});
it('calls `refreshCurrentPage`', () => {
expect(refreshCurrentPage).toHaveBeenCalledTimes(1);
});
- });
- describe('Slack integration', () => {
- describe('Help and sections rendering', () => {
- const dummyHelp = 'Foo Help';
-
- it.each`
- integration | flagIsOn | helpHtml | sections | shouldShowSections | shouldShowHelp
- ${INTEGRATION_FORM_TYPE_SLACK} | ${false} | ${''} | ${[]} | ${false} | ${false}
- ${INTEGRATION_FORM_TYPE_SLACK} | ${false} | ${dummyHelp} | ${[]} | ${false} | ${true}
- ${INTEGRATION_FORM_TYPE_SLACK} | ${false} | ${undefined} | ${[mockSectionConnection]} | ${false} | ${false}
- ${INTEGRATION_FORM_TYPE_SLACK} | ${false} | ${dummyHelp} | ${[mockSectionConnection]} | ${false} | ${true}
- ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${''} | ${[]} | ${false} | ${false}
- ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${dummyHelp} | ${[]} | ${false} | ${true}
- ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${undefined} | ${[mockSectionConnection]} | ${true} | ${false}
- ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${dummyHelp} | ${[mockSectionConnection]} | ${true} | ${true}
- ${'foo'} | ${false} | ${''} | ${[]} | ${false} | ${false}
- ${'foo'} | ${false} | ${dummyHelp} | ${[]} | ${false} | ${true}
- ${'foo'} | ${false} | ${undefined} | ${[mockSectionConnection]} | ${true} | ${false}
- ${'foo'} | ${false} | ${dummyHelp} | ${[mockSectionConnection]} | ${true} | ${false}
- ${'foo'} | ${true} | ${''} | ${[]} | ${false} | ${false}
- ${'foo'} | ${true} | ${dummyHelp} | ${[]} | ${false} | ${true}
- ${'foo'} | ${true} | ${undefined} | ${[mockSectionConnection]} | ${true} | ${false}
- ${'foo'} | ${true} | ${dummyHelp} | ${[mockSectionConnection]} | ${true} | ${false}
- `(
- '$sections sections, and "$helpHtml" helpHtml when the FF is "$flagIsOn" for "$integration" integration',
- ({ integration, flagIsOn, helpHtml, sections, shouldShowSections, shouldShowHelp }) => {
- createComponent({
- provide: {
- helpHtml,
- glFeatures: { integrationSlackAppNotifications: flagIsOn },
- },
- customStateProps: {
- sections,
- type: integration,
- },
- });
- expect(findAllSections().length > 0).toEqual(shouldShowSections);
- expect(findHelpHtml().exists()).toBe(shouldShowHelp);
- if (shouldShowHelp) {
- expect(findHelpHtml().html()).toContain(helpHtml);
- }
- },
- );
+ it('resets `isResetting`', async () => {
+ expect(findFormActions().props('isResetting')).toBe(false);
});
+ });
+ });
+
+ describe('Slack integration', () => {
+ describe('Help and sections rendering', () => {
+ const dummyHelp = 'Foo Help';
+
+ it.each`
+ integration | flagIsOn | helpHtml | sections | shouldShowSections | shouldShowHelp
+ ${INTEGRATION_FORM_TYPE_SLACK} | ${false} | ${''} | ${[]} | ${false} | ${false}
+ ${INTEGRATION_FORM_TYPE_SLACK} | ${false} | ${dummyHelp} | ${[]} | ${false} | ${true}
+ ${INTEGRATION_FORM_TYPE_SLACK} | ${false} | ${undefined} | ${[mockSectionConnection]} | ${false} | ${false}
+ ${INTEGRATION_FORM_TYPE_SLACK} | ${false} | ${dummyHelp} | ${[mockSectionConnection]} | ${false} | ${true}
+ ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${''} | ${[]} | ${false} | ${false}
+ ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${dummyHelp} | ${[]} | ${false} | ${true}
+ ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${undefined} | ${[mockSectionConnection]} | ${true} | ${false}
+ ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${dummyHelp} | ${[mockSectionConnection]} | ${true} | ${true}
+ ${'foo'} | ${false} | ${''} | ${[]} | ${false} | ${false}
+ ${'foo'} | ${false} | ${dummyHelp} | ${[]} | ${false} | ${true}
+ ${'foo'} | ${false} | ${undefined} | ${[mockSectionConnection]} | ${true} | ${false}
+ ${'foo'} | ${false} | ${dummyHelp} | ${[mockSectionConnection]} | ${true} | ${false}
+ ${'foo'} | ${true} | ${''} | ${[]} | ${false} | ${false}
+ ${'foo'} | ${true} | ${dummyHelp} | ${[]} | ${false} | ${true}
+ ${'foo'} | ${true} | ${undefined} | ${[mockSectionConnection]} | ${true} | ${false}
+ ${'foo'} | ${true} | ${dummyHelp} | ${[mockSectionConnection]} | ${true} | ${false}
+ `(
+ '$sections sections, and "$helpHtml" helpHtml when the FF is "$flagIsOn" for "$integration" integration',
+ ({ integration, flagIsOn, helpHtml, sections, shouldShowSections, shouldShowHelp }) => {
+ createComponent({
+ provide: {
+ helpHtml,
+ glFeatures: { integrationSlackAppNotifications: flagIsOn },
+ },
+ customStateProps: {
+ sections,
+ type: integration,
+ },
+ });
+ expect(findAllSections().length > 0).toEqual(shouldShowSections);
+ expect(findHelpHtml().exists()).toBe(shouldShowHelp);
+ if (shouldShowHelp) {
+ expect(findHelpHtml().html()).toContain(helpHtml);
+ }
+ },
+ );
+ });
+ describe.each`
+ hasSections | hasFieldsWithoutSections | description
+ ${true} | ${true} | ${'When having both: the sections and the fields without a section'}
+ ${true} | ${false} | ${'When having the sections only'}
+ ${false} | ${true} | ${'When having only the fields without a section'}
+ `('$description', ({ hasSections, hasFieldsWithoutSections }) => {
it.each`
prefix | integration | shouldUpgradeSlack | flagIsOn | shouldShowAlert
${'does'} | ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${true} | ${true}
@@ -769,7 +606,7 @@ describe('IntegrationForm', () => {
${'does not'} | ${'foo'} | ${false} | ${true} | ${false}
${'does not'} | ${'foo'} | ${true} | ${false} | ${false}
`(
- '$prefix render the upgrade warnning when we are in "$integration" integration with the flag "$flagIsOn" and Slack-needs-upgrade is "$shouldUpgradeSlack"',
+ '$prefix render the upgrade warning when we are in "$integration" integration with the flag "$flagIsOn" and Slack-needs-upgrade is "$shouldUpgradeSlack" and have sections',
({ integration, shouldUpgradeSlack, flagIsOn, shouldShowAlert }) => {
createComponent({
provide: {
@@ -778,7 +615,8 @@ describe('IntegrationForm', () => {
customStateProps: {
shouldUpgradeSlack,
type: integration,
- sections: [mockSectionConnection],
+ sections: hasSections ? [mockSectionConnection] : [],
+ fields: hasFieldsWithoutSections ? [mockField] : [],
},
});
expect(findAlert().exists()).toBe(shouldShowAlert);
diff --git a/spec/frontend/invite_members/components/import_project_members_modal_spec.js b/spec/frontend/invite_members/components/import_project_members_modal_spec.js
index 8b2d13be309..d839cde163c 100644
--- a/spec/frontend/invite_members/components/import_project_members_modal_spec.js
+++ b/spec/frontend/invite_members/components/import_project_members_modal_spec.js
@@ -8,6 +8,12 @@ import * as ProjectsApi from '~/api/projects_api';
import ImportProjectMembersModal from '~/invite_members/components/import_project_members_modal.vue';
import ProjectSelect from '~/invite_members/components/project_select.vue';
import axios from '~/lib/utils/axios_utils';
+import {
+ displaySuccessfulInvitationAlert,
+ reloadOnInvitationSuccess,
+} from '~/invite_members/utils/trigger_successful_invite_alert';
+
+jest.mock('~/invite_members/utils/trigger_successful_invite_alert');
let wrapper;
let mock;
@@ -19,11 +25,12 @@ const $toast = {
show: jest.fn(),
};
-const createComponent = () => {
+const createComponent = ({ props = {} } = {}) => {
wrapper = shallowMountExtended(ImportProjectMembersModal, {
propsData: {
projectId,
projectName,
+ ...props,
},
stubs: {
GlModal: stubComponent(GlModal, {
@@ -101,6 +108,35 @@ describe('ImportProjectMembersModal', () => {
});
describe('submitting the import', () => {
+ describe('when the import is successful with reloadPageOnSubmit', () => {
+ beforeEach(() => {
+ createComponent({
+ props: { reloadPageOnSubmit: true },
+ });
+
+ findProjectSelect().vm.$emit('input', projectToBeImported);
+
+ jest.spyOn(ProjectsApi, 'importProjectMembers').mockResolvedValue();
+
+ clickImportButton();
+ });
+
+ it('calls displaySuccessfulInvitationAlert on mount', () => {
+ expect(displaySuccessfulInvitationAlert).toHaveBeenCalled();
+ });
+
+ it('calls reloadOnInvitationSuccess', () => {
+ expect(reloadOnInvitationSuccess).toHaveBeenCalled();
+ });
+
+ it('does not display the successful toastMessage', () => {
+ expect($toast.show).not.toHaveBeenCalledWith(
+ 'Successfully imported',
+ wrapper.vm.$options.toastOptions,
+ );
+ });
+ });
+
describe('when the import is successful', () => {
beforeEach(() => {
createComponent();
@@ -126,6 +162,14 @@ describe('ImportProjectMembersModal', () => {
);
});
+ it('does not call displaySuccessfulInvitationAlert on mount', () => {
+ expect(displaySuccessfulInvitationAlert).not.toHaveBeenCalled();
+ });
+
+ it('does not call reloadOnInvitationSuccess', () => {
+ expect(reloadOnInvitationSuccess).not.toHaveBeenCalled();
+ });
+
it('sets isLoading to false after success', () => {
expect(findGlModal().props('actionPrimary').attributes.loading).toBe(false);
});
diff --git a/spec/frontend/invite_members/components/invite_group_notification_spec.js b/spec/frontend/invite_members/components/invite_group_notification_spec.js
new file mode 100644
index 00000000000..3e6ba6da9f4
--- /dev/null
+++ b/spec/frontend/invite_members/components/invite_group_notification_spec.js
@@ -0,0 +1,42 @@
+import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { sprintf } from '~/locale';
+import InviteGroupNotification from '~/invite_members/components/invite_group_notification.vue';
+import { GROUP_MODAL_ALERT_BODY } from '~/invite_members/constants';
+
+describe('InviteGroupNotification', () => {
+ let wrapper;
+
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findLink = () => wrapper.findComponent(GlLink);
+
+ const createComponent = () => {
+ wrapper = shallowMountExtended(InviteGroupNotification, {
+ provide: { freeUsersLimit: 5 },
+ propsData: { name: 'name' },
+ stubs: { GlSprintf },
+ });
+ };
+
+ describe('when rendering', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('passes the correct props', () => {
+ expect(findAlert().props()).toMatchObject({ variant: 'warning', dismissible: false });
+ });
+
+ it('shows the correct message', () => {
+ const message = sprintf(GROUP_MODAL_ALERT_BODY, { count: 5 });
+
+ expect(findAlert().text()).toMatchInterpolatedText(message);
+ });
+
+ it('has a help link', () => {
+ expect(findLink().attributes('href')).toEqual(
+ 'https://docs.gitlab.com/ee/user/group/manage.html#share-a-group-with-another-group',
+ );
+ });
+ });
+});
diff --git a/spec/frontend/invite_members/components/invite_groups_modal_spec.js b/spec/frontend/invite_members/components/invite_groups_modal_spec.js
index f9cb4a149f2..c2a55517405 100644
--- a/spec/frontend/invite_members/components/invite_groups_modal_spec.js
+++ b/spec/frontend/invite_members/components/invite_groups_modal_spec.js
@@ -6,9 +6,16 @@ import InviteGroupsModal from '~/invite_members/components/invite_groups_modal.v
import InviteModalBase from '~/invite_members/components/invite_modal_base.vue';
import ContentTransition from '~/vue_shared/components/content_transition.vue';
import GroupSelect from '~/invite_members/components/group_select.vue';
+import InviteGroupNotification from '~/invite_members/components/invite_group_notification.vue';
import { stubComponent } from 'helpers/stub_component';
+import {
+ displaySuccessfulInvitationAlert,
+ reloadOnInvitationSuccess,
+} from '~/invite_members/utils/trigger_successful_invite_alert';
import { propsData, sharedGroup } from '../mock_data/group_modal';
+jest.mock('~/invite_members/utils/trigger_successful_invite_alert');
+
describe('InviteGroupsModal', () => {
let wrapper;
@@ -44,6 +51,7 @@ describe('InviteGroupsModal', () => {
const findModal = () => wrapper.findComponent(GlModal);
const findGroupSelect = () => wrapper.findComponent(GroupSelect);
+ const findInviteGroupAlert = () => wrapper.findComponent(InviteGroupNotification);
const findIntroText = () => wrapper.findByTestId('modal-base-intro-text').text();
const findMembersFormGroup = () => wrapper.findByTestId('members-form-group');
const membersFormGroupInvalidFeedback = () =>
@@ -74,6 +82,20 @@ describe('InviteGroupsModal', () => {
});
});
+ describe('rendering the invite group notification', () => {
+ it('shows the user limit notification alert when free user cap is enabled', () => {
+ createComponent({ freeUserCapEnabled: true });
+
+ expect(findInviteGroupAlert().exists()).toBe(true);
+ });
+
+ it('does not show the user limit notification alert', () => {
+ createComponent();
+
+ expect(findInviteGroupAlert().exists()).toBe(false);
+ });
+ });
+
describe('submitting the invite form', () => {
let apiResolve;
let apiReject;
@@ -126,6 +148,14 @@ describe('InviteGroupsModal', () => {
onComplete: expect.any(Function),
});
});
+
+ it('does not call displaySuccessfulInvitationAlert on mount', () => {
+ expect(displaySuccessfulInvitationAlert).not.toHaveBeenCalled();
+ });
+
+ it('does not call reloadOnInvitationSuccess', () => {
+ expect(reloadOnInvitationSuccess).not.toHaveBeenCalled();
+ });
});
describe('when fails', () => {
@@ -156,4 +186,37 @@ describe('InviteGroupsModal', () => {
});
});
});
+
+ describe('submitting the invite form with reloadPageOnSubmit set true', () => {
+ const groupPostData = {
+ group_id: sharedGroup.id,
+ group_access: propsData.defaultAccessLevel,
+ expires_at: undefined,
+ format: 'json',
+ };
+
+ beforeEach(() => {
+ createComponent({ reloadPageOnSubmit: true });
+ triggerGroupSelect(sharedGroup);
+
+ wrapper.vm.$toast = { show: jest.fn() };
+ jest.spyOn(Api, 'groupShareWithGroup').mockResolvedValue({ data: groupPostData });
+
+ clickInviteButton();
+ });
+
+ describe('when succeeds', () => {
+ it('calls displaySuccessfulInvitationAlert on mount', () => {
+ expect(displaySuccessfulInvitationAlert).toHaveBeenCalled();
+ });
+
+ it('calls reloadOnInvitationSuccess', () => {
+ expect(reloadOnInvitationSuccess).toHaveBeenCalled();
+ });
+
+ it('does not show the toast message on failure', () => {
+ expect(wrapper.vm.$toast.show).not.toHaveBeenCalled();
+ });
+ });
+ });
});
diff --git a/spec/frontend/invite_members/components/invite_members_modal_spec.js b/spec/frontend/invite_members/components/invite_members_modal_spec.js
index 47be1933ed7..22fcedb2eaf 100644
--- a/spec/frontend/invite_members/components/invite_members_modal_spec.js
+++ b/spec/frontend/invite_members/components/invite_members_modal_spec.js
@@ -19,13 +19,17 @@ import {
MEMBERS_TO_PROJECT_CELEBRATE_INTRO_TEXT,
LEARN_GITLAB,
EXPANDED_ERRORS,
- EMPTY_INVITES_ERROR_TEXT,
+ EMPTY_INVITES_ALERT_TEXT,
} from '~/invite_members/constants';
import eventHub from '~/invite_members/event_hub';
import ContentTransition from '~/vue_shared/components/content_transition.vue';
import axios from '~/lib/utils/axios_utils';
-import httpStatus from '~/lib/utils/http_status';
+import httpStatus, { HTTP_STATUS_CREATED } from '~/lib/utils/http_status';
import { getParameterValues } from '~/lib/utils/url_utility';
+import {
+ displaySuccessfulInvitationAlert,
+ reloadOnInvitationSuccess,
+} from '~/invite_members/utils/trigger_successful_invite_alert';
import { GROUPS_INVITATIONS_PATH, invitationsApiResponse } from '../mock_data/api_responses';
import {
propsData,
@@ -40,6 +44,7 @@ import {
GlEmoji,
} from '../mock_data/member_modal';
+jest.mock('~/invite_members/utils/trigger_successful_invite_alert');
jest.mock('~/experimentation/experiment_tracking');
jest.mock('~/lib/utils/url_utility', () => ({
...jest.requireActual('~/lib/utils/url_utility'),
@@ -57,6 +62,7 @@ describe('InviteMembersModal', () => {
},
propsData: {
usersLimitDataset: {},
+ fullPath: 'project',
...propsData,
...props,
},
@@ -95,6 +101,7 @@ describe('InviteMembersModal', () => {
const findModal = () => wrapper.findComponent(GlModal);
const findBase = () => wrapper.findComponent(InviteModalBase);
const findIntroText = () => wrapper.findByTestId('modal-base-intro-text').text();
+ const findEmptyInvitesAlert = () => wrapper.findByTestId('empty-invites-alert');
const findMemberErrorAlert = () => wrapper.findByTestId('alert-member-error');
const findMoreInviteErrorsButton = () => wrapper.findByTestId('accordion-button');
const findUserLimitAlert = () => wrapper.findComponent(UserLimitNotification);
@@ -397,7 +404,8 @@ describe('InviteMembersModal', () => {
await waitForPromises();
- expect(membersFormGroupInvalidFeedback()).toBe(EMPTY_INVITES_ERROR_TEXT);
+ expect(findEmptyInvitesAlert().text()).toBe(EMPTY_INVITES_ALERT_TEXT);
+ expect(membersFormGroupInvalidFeedback()).toBe(MEMBERS_PLACEHOLDER);
expect(findMembersSelect().props('exceptionState')).toBe(false);
await triggerMembersTokenSelect([user1]);
@@ -417,6 +425,29 @@ describe('InviteMembersModal', () => {
tasks_project_id: '',
};
+ describe('when reloadOnSubmit is true', () => {
+ beforeEach(async () => {
+ createComponent({ reloadPageOnSubmit: true });
+ await triggerMembersTokenSelect([user1, user2]);
+
+ wrapper.vm.$toast = { show: jest.fn() };
+ jest.spyOn(Api, 'inviteGroupMembers').mockResolvedValue({ data: postData });
+ clickInviteButton();
+ });
+
+ it('calls displaySuccessfulInvitationAlert on mount', () => {
+ expect(displaySuccessfulInvitationAlert).toHaveBeenCalled();
+ });
+
+ it('calls reloadOnInvitationSuccess', () => {
+ expect(reloadOnInvitationSuccess).toHaveBeenCalled();
+ });
+
+ it('does not show the toast message', () => {
+ expect(wrapper.vm.$toast.show).not.toHaveBeenCalled();
+ });
+ });
+
describe('when member is added successfully', () => {
beforeEach(async () => {
createComponent();
@@ -438,6 +469,14 @@ describe('InviteMembersModal', () => {
it('displays the successful toastMessage', () => {
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith('Members were successfully added');
});
+
+ it('does not call displaySuccessfulInvitationAlert on mount', () => {
+ expect(displaySuccessfulInvitationAlert).not.toHaveBeenCalled();
+ });
+
+ it('does not call reloadOnInvitationSuccess', () => {
+ expect(reloadOnInvitationSuccess).not.toHaveBeenCalled();
+ });
});
describe('when opened from a Learn GitLab page', () => {
@@ -464,7 +503,7 @@ describe('InviteMembersModal', () => {
describe('clearing the invalid state and message', () => {
beforeEach(async () => {
- mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.EMAIL_TAKEN);
+ mockInvitationsApi(HTTP_STATUS_CREATED, invitationsApiResponse.EMAIL_TAKEN);
clickInviteButton();
@@ -523,7 +562,7 @@ describe('InviteMembersModal', () => {
});
it('displays the restricted user api message for response with bad request', async () => {
- mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.EMAIL_RESTRICTED);
+ mockInvitationsApi(HTTP_STATUS_CREATED, invitationsApiResponse.EMAIL_RESTRICTED);
clickInviteButton();
@@ -536,7 +575,7 @@ describe('InviteMembersModal', () => {
});
it('displays all errors when there are multiple existing users that are restricted by email', async () => {
- mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.MULTIPLE_RESTRICTED);
+ mockInvitationsApi(HTTP_STATUS_CREATED, invitationsApiResponse.MULTIPLE_RESTRICTED);
clickInviteButton();
@@ -590,6 +629,14 @@ describe('InviteMembersModal', () => {
it('displays the successful toastMessage', () => {
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith('Members were successfully added');
});
+
+ it('does not call displaySuccessfulInvitationAlert on mount', () => {
+ expect(displaySuccessfulInvitationAlert).not.toHaveBeenCalled();
+ });
+
+ it('does not call reloadOnInvitationSuccess', () => {
+ expect(reloadOnInvitationSuccess).not.toHaveBeenCalled();
+ });
});
});
@@ -633,7 +680,7 @@ describe('InviteMembersModal', () => {
});
it('displays the restricted email error when restricted email is invited', async () => {
- mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.EMAIL_RESTRICTED);
+ mockInvitationsApi(HTTP_STATUS_CREATED, invitationsApiResponse.EMAIL_RESTRICTED);
clickInviteButton();
@@ -647,7 +694,7 @@ describe('InviteMembersModal', () => {
});
it('displays all errors when there are multiple emails that return a restricted error message', async () => {
- mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.MULTIPLE_RESTRICTED);
+ mockInvitationsApi(HTTP_STATUS_CREATED, invitationsApiResponse.MULTIPLE_RESTRICTED);
clickInviteButton();
@@ -677,6 +724,14 @@ describe('InviteMembersModal', () => {
expect(membersFormGroupInvalidFeedback()).toBe(expectedSyntaxError);
expect(findMembersSelect().props('exceptionState')).toBe(false);
});
+
+ it('does not call displaySuccessfulInvitationAlert on mount', () => {
+ expect(displaySuccessfulInvitationAlert).not.toHaveBeenCalled();
+ });
+
+ it('does not call reloadOnInvitationSuccess', () => {
+ expect(reloadOnInvitationSuccess).not.toHaveBeenCalled();
+ });
});
describe('when multiple emails are invited at the same time', () => {
@@ -698,7 +753,7 @@ describe('InviteMembersModal', () => {
createInviteMembersToGroupWrapper();
await triggerMembersTokenSelect([user3, user4, user5, user6]);
- mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.EXPANDED_RESTRICTED);
+ mockInvitationsApi(HTTP_STATUS_CREATED, invitationsApiResponse.EXPANDED_RESTRICTED);
clickInviteButton();
@@ -791,6 +846,14 @@ describe('InviteMembersModal', () => {
it('displays the successful toastMessage', () => {
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith('Members were successfully added');
});
+
+ it('does not call displaySuccessfulInvitationAlert on mount', () => {
+ expect(displaySuccessfulInvitationAlert).not.toHaveBeenCalled();
+ });
+
+ it('does not call reloadOnInvitationSuccess', () => {
+ expect(reloadOnInvitationSuccess).not.toHaveBeenCalled();
+ });
});
it('calls Apis with the invite source passed through to openModal', async () => {
diff --git a/spec/frontend/invite_members/components/invite_modal_base_spec.js b/spec/frontend/invite_members/components/invite_modal_base_spec.js
index aeead8809fd..db2afbbd141 100644
--- a/spec/frontend/invite_members/components/invite_modal_base_spec.js
+++ b/spec/frontend/invite_members/components/invite_modal_base_spec.js
@@ -1,16 +1,15 @@
import {
- GlDropdown,
- GlDropdownItem,
+ GlFormSelect,
GlDatepicker,
GlFormGroup,
- GlSprintf,
GlLink,
+ GlSprintf,
GlModal,
GlIcon,
} from '@gitlab/ui';
import { stubComponent } from 'helpers/stub_component';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import InviteModalBase from '~/invite_members/components/invite_modal_base.vue';
import ContentTransition from '~/vue_shared/components/content_transition.vue';
@@ -26,24 +25,30 @@ import { propsData, membersPath, purchasePath } from '../mock_data/modal_base';
describe('InviteModalBase', () => {
let wrapper;
- const createComponent = (props = {}, stubs = {}) => {
- wrapper = shallowMountExtended(InviteModalBase, {
+ const createComponent = ({ props = {}, stubs = {}, mountFn = shallowMountExtended } = {}) => {
+ const requiredStubs =
+ mountFn === mountExtended
+ ? {}
+ : {
+ ContentTransition,
+ GlFormSelect: true,
+ GlSprintf,
+ GlFormGroup: stubComponent(GlFormGroup, {
+ props: ['state', 'invalidFeedback'],
+ }),
+ };
+
+ wrapper = mountFn(InviteModalBase, {
propsData: {
...propsData,
...props,
},
stubs: {
- ContentTransition,
GlModal: stubComponent(GlModal, {
template:
'<div><slot name="modal-title"></slot><slot></slot><slot name="modal-footer"></slot></div>',
}),
- GlDropdown: true,
- GlDropdownItem: true,
- GlSprintf,
- GlFormGroup: stubComponent(GlFormGroup, {
- props: ['state', 'invalidFeedback'],
- }),
+ ...requiredStubs,
...stubs,
},
});
@@ -51,11 +56,10 @@ describe('InviteModalBase', () => {
afterEach(() => {
wrapper.destroy();
- wrapper = null;
});
- const findDropdown = () => wrapper.findComponent(GlDropdown);
- const findDropdownItems = () => findDropdown().findAllComponents(GlDropdownItem);
+ const findFormSelect = () => wrapper.findComponent(GlFormSelect);
+ const findFormSelectOptions = () => findFormSelect().findAllComponents('option');
const findDatepicker = () => wrapper.findComponent(GlDatepicker);
const findLink = () => wrapper.findComponent(GlLink);
const findIcon = () => wrapper.findComponent(GlIcon);
@@ -97,16 +101,29 @@ describe('InviteModalBase', () => {
});
describe('rendering the access levels dropdown', () => {
+ beforeEach(() => {
+ createComponent({
+ mountFn: mountExtended,
+ });
+ });
+
it('sets the default dropdown text to the default access level name', () => {
- expect(findDropdown().attributes('text')).toBe('Guest');
+ expect(findFormSelect().exists()).toBe(true);
+ expect(findFormSelect().element.value).toBe('10');
});
it('renders dropdown items for each accessLevel', () => {
- expect(findDropdownItems()).toHaveLength(5);
+ expect(findFormSelectOptions()).toHaveLength(5);
});
});
describe('rendering the help link', () => {
+ beforeEach(() => {
+ createComponent({
+ mountFn: mountExtended,
+ });
+ });
+
it('renders the correct link', () => {
expect(findLink().attributes('href')).toBe(propsData.helpLink);
});
@@ -126,7 +143,7 @@ describe('InviteModalBase', () => {
});
it('renders description', () => {
- createComponent({}, { GlFormGroup });
+ createComponent({ stubs: { GlFormGroup } });
expect(findMembersFormGroup().attributes('description')).toContain(
propsData.formGroupDescription,
@@ -144,13 +161,16 @@ describe('InviteModalBase', () => {
beforeEach(() => {
createComponent(
- { usersLimitDataset: { membersPath, purchasePath, reachedLimit: true } },
- { GlModal, GlFormGroup },
+ { props: { usersLimitDataset: { membersPath, purchasePath, reachedLimit: true } } },
+ { stubs: { GlModal, GlFormGroup } },
);
});
it('tracks actions', () => {
- createComponent({ usersLimitDataset: { reachedLimit: true } }, { GlFormGroup, GlModal });
+ createComponent({
+ props: { usersLimitDataset: { reachedLimit: true } },
+ stubs: { GlFormGroup, GlModal },
+ });
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
const modal = wrapper.findComponent(GlModal);
@@ -164,8 +184,8 @@ describe('InviteModalBase', () => {
describe('when user limit is close on a personal namespace', () => {
beforeEach(() => {
- createComponent(
- {
+ createComponent({
+ props: {
usersLimitDataset: {
membersPath,
userNamespace: true,
@@ -173,8 +193,8 @@ describe('InviteModalBase', () => {
reachedLimit: false,
},
},
- { GlModal, GlFormGroup },
- );
+ stubs: { GlModal, GlFormGroup },
+ });
});
it('renders correct buttons', () => {
@@ -190,16 +210,16 @@ describe('InviteModalBase', () => {
});
describe('when users limit is not reached', () => {
- const textRegex = /Select a role.+Read more about role permissions Access expiration date \(optional\)/;
+ const textRegex = /Select a role\s*Read more about role permissions\s*Access expiration date \(optional\)/;
beforeEach(() => {
- createComponent({ reachedLimit: false }, { GlModal, GlFormGroup });
+ createComponent({ props: { reachedLimit: false }, stubs: { GlModal, GlFormGroup } });
});
it('renders correct blocks', () => {
expect(findIcon().exists()).toBe(false);
expect(findDisabledInput().exists()).toBe(false);
- expect(findDropdown().exists()).toBe(true);
+ expect(findFormSelect().exists()).toBe(true);
expect(findDatepicker().exists()).toBe(true);
expect(wrapper.findComponent(GlModal).text()).toMatch(textRegex);
});
@@ -213,7 +233,9 @@ describe('InviteModalBase', () => {
it('with isLoading, shows loading for invite button', () => {
createComponent({
- isLoading: true,
+ props: {
+ isLoading: true,
+ },
});
expect(wrapper.findComponent(GlModal).props('actionPrimary').attributes.loading).toBe(true);
@@ -221,7 +243,9 @@ describe('InviteModalBase', () => {
it('with invalidFeedbackMessage, set members form group exception state', () => {
createComponent({
- invalidFeedbackMessage: 'invalid message!',
+ props: {
+ invalidFeedbackMessage: 'invalid message!',
+ },
});
expect(findMembersFormGroup().props()).toEqual({
diff --git a/spec/frontend/invite_members/mock_data/group_modal.js b/spec/frontend/invite_members/mock_data/group_modal.js
index c8588683885..65e8b025dd9 100644
--- a/spec/frontend/invite_members/mock_data/group_modal.js
+++ b/spec/frontend/invite_members/mock_data/group_modal.js
@@ -7,6 +7,8 @@ export const propsData = {
accessLevels: { Guest: 10, Reporter: 20, Developer: 30, Maintainer: 40, Owner: 50 },
defaultAccessLevel: 10,
helpLink: 'https://example.com',
+ fullPath: 'project',
+ freeUserCapEnabled: false,
};
export const sharedGroup = { id: '981' };
diff --git a/spec/frontend/invite_members/utils/trigger_successful_invite_alert_spec.js b/spec/frontend/invite_members/utils/trigger_successful_invite_alert_spec.js
new file mode 100644
index 00000000000..38b16dd0c2c
--- /dev/null
+++ b/spec/frontend/invite_members/utils/trigger_successful_invite_alert_spec.js
@@ -0,0 +1,54 @@
+import {
+ displaySuccessfulInvitationAlert,
+ reloadOnInvitationSuccess,
+} from '~/invite_members/utils/trigger_successful_invite_alert';
+import {
+ TOAST_MESSAGE_LOCALSTORAGE_KEY,
+ TOAST_MESSAGE_SUCCESSFUL,
+} from '~/invite_members/constants';
+import { createAlert } from '~/flash';
+import { useLocalStorageSpy } from 'helpers/local_storage_helper';
+
+jest.mock('~/flash');
+useLocalStorageSpy();
+
+describe('Display Successful Invitation Alert', () => {
+ it('does not show alert if localStorage key not present', () => {
+ localStorage.removeItem(TOAST_MESSAGE_LOCALSTORAGE_KEY);
+
+ displaySuccessfulInvitationAlert();
+
+ expect(createAlert).not.toHaveBeenCalled();
+ });
+
+ it('shows alert when localStorage key is present', () => {
+ localStorage.setItem(TOAST_MESSAGE_LOCALSTORAGE_KEY, 'true');
+
+ displaySuccessfulInvitationAlert();
+
+ expect(createAlert).toHaveBeenCalledWith({
+ message: TOAST_MESSAGE_SUCCESSFUL,
+ variant: 'info',
+ });
+ });
+});
+
+describe('Reload On Invitation Success', () => {
+ const { location } = window;
+
+ beforeAll(() => {
+ delete window.location;
+ window.location = { reload: jest.fn() };
+ });
+
+ afterAll(() => {
+ window.location = location;
+ });
+
+ it('sets localStorage value and calls window.location.reload', () => {
+ reloadOnInvitationSuccess();
+
+ expect(localStorage.setItem).toHaveBeenCalledWith(TOAST_MESSAGE_LOCALSTORAGE_KEY, 'true');
+ expect(window.location.reload).toHaveBeenCalled();
+ });
+});
diff --git a/spec/frontend/issuable/bulk_update_sidebar/components/move_issues_button_spec.js b/spec/frontend/issuable/bulk_update_sidebar/components/move_issues_button_spec.js
deleted file mode 100644
index c432d722637..00000000000
--- a/spec/frontend/issuable/bulk_update_sidebar/components/move_issues_button_spec.js
+++ /dev/null
@@ -1,554 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-import { cloneDeep } from 'lodash';
-import VueApollo from 'vue-apollo';
-import { GlAlert } from '@gitlab/ui';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
-import createFlash from '~/flash';
-import { logError } from '~/lib/logger';
-import IssuableMoveDropdown from '~/vue_shared/components/sidebar/issuable_move_dropdown.vue';
-import MoveIssuesButton from '~/issuable/bulk_update_sidebar/components/move_issues_button.vue';
-import issuableEventHub from '~/issues/list/eventhub';
-import moveIssueMutation from '~/issuable/bulk_update_sidebar/components/graphql/mutations/move_issue.mutation.graphql';
-import getIssuesQuery from 'ee_else_ce/issues/list/queries/get_issues.query.graphql';
-import getIssuesCountsQuery from 'ee_else_ce/issues/list/queries/get_issues_counts.query.graphql';
-import { getIssuesCountsQueryResponse, getIssuesQueryResponse } from 'jest/issues/list/mock_data';
-import {
- WORK_ITEM_TYPE_ENUM_ISSUE,
- WORK_ITEM_TYPE_ENUM_INCIDENT,
- WORK_ITEM_TYPE_ENUM_TASK,
- WORK_ITEM_TYPE_ENUM_TEST_CASE,
-} from '~/work_items/constants';
-
-jest.mock('~/flash');
-jest.mock('~/lib/logger');
-useMockLocationHelper();
-
-const mockDefaultProps = {
- projectFullPath: 'flight/FlightJS',
- projectsFetchPath: '/-/autocomplete/projects?project_id=1',
-};
-
-const mockDestinationProject = {
- full_path: 'gitlab-org/GitLabTest',
-};
-
-const mockMutationErrorMessage = 'Example error message';
-
-const mockIssue = {
- iid: '15',
- type: WORK_ITEM_TYPE_ENUM_ISSUE,
-};
-
-const mockIncident = {
- iid: '32',
- type: WORK_ITEM_TYPE_ENUM_INCIDENT,
-};
-
-const mockTask = {
- iid: '40',
- type: WORK_ITEM_TYPE_ENUM_TASK,
-};
-
-const mockTestCase = {
- iid: '51',
- type: WORK_ITEM_TYPE_ENUM_TEST_CASE,
-};
-
-const selectedIssuesMocks = {
- tasksOnly: [mockTask],
- testCasesOnly: [mockTestCase],
- issuesOnly: [mockIssue, mockIncident],
- tasksAndTestCases: [mockTask, mockTestCase],
- issuesAndTasks: [mockIssue, mockIncident, mockTask],
- issuesAndTestCases: [mockIssue, mockIncident, mockTestCase],
- issuesTasksAndTestCases: [mockIssue, mockIncident, mockTask, mockTestCase],
-};
-
-let getIssuesQueryCompleteResponse = getIssuesQueryResponse;
-if (IS_EE) {
- getIssuesQueryCompleteResponse = cloneDeep(getIssuesQueryResponse);
- getIssuesQueryCompleteResponse.data.project.issues.nodes[0].blockingCount = 1;
- getIssuesQueryCompleteResponse.data.project.issues.nodes[0].healthStatus = null;
- getIssuesQueryCompleteResponse.data.project.issues.nodes[0].weight = 5;
-}
-
-const resolvedMutationWithoutErrorsMock = jest.fn().mockResolvedValue({
- data: {
- issueMove: {
- errors: [],
- },
- },
-});
-
-const resolvedMutationWithErrorsMock = jest.fn().mockResolvedValue({
- data: {
- issueMove: {
- errors: [{ message: mockMutationErrorMessage }],
- },
- },
-});
-
-const rejectedMutationMock = jest.fn().mockRejectedValue({});
-
-const mockIssuesQueryResponse = jest.fn().mockResolvedValue(getIssuesQueryCompleteResponse);
-const mockIssuesCountsQueryResponse = jest.fn().mockResolvedValue(getIssuesCountsQueryResponse);
-
-describe('MoveIssuesButton', () => {
- Vue.use(VueApollo);
-
- let wrapper;
- let fakeApollo;
-
- const findAlert = () => wrapper.findComponent(GlAlert);
- const findDropdown = () => wrapper.findComponent(IssuableMoveDropdown);
- const emitMoveIssuablesEvent = () => {
- findDropdown().vm.$emit('move-issuable', mockDestinationProject);
- };
-
- const createComponent = (data = {}, mutationResolverMock = rejectedMutationMock) => {
- fakeApollo = createMockApollo([
- [moveIssueMutation, mutationResolverMock],
- [getIssuesQuery, mockIssuesQueryResponse],
- [getIssuesCountsQuery, mockIssuesCountsQueryResponse],
- ]);
-
- fakeApollo.defaultClient.cache.writeQuery({
- query: getIssuesQuery,
- variables: {
- isProject: true,
- fullPath: mockDefaultProps.projectFullPath,
- },
- data: getIssuesQueryCompleteResponse.data,
- });
-
- fakeApollo.defaultClient.cache.writeQuery({
- query: getIssuesCountsQuery,
- variables: {
- isProject: true,
- },
- data: getIssuesCountsQueryResponse.data,
- });
-
- wrapper = shallowMount(MoveIssuesButton, {
- data() {
- return {
- ...data,
- };
- },
- propsData: {
- ...mockDefaultProps,
- },
- apolloProvider: fakeApollo,
- });
- };
-
- beforeEach(() => {
- // Needed due to a bug in Apollo: https://github.com/apollographql/apollo-client/issues/8900
- // eslint-disable-next-line no-console
- console.warn = jest.fn();
- });
-
- afterEach(() => {
- wrapper.destroy();
- fakeApollo = null;
- });
-
- describe('`Move selected` dropdown', () => {
- it('renders disabled by default', () => {
- createComponent();
- expect(findDropdown().exists()).toBe(true);
- expect(findDropdown().attributes('disabled')).toBe('true');
- });
-
- it.each`
- selectedIssuablesMock | disabled | status | testMessage
- ${[]} | ${true} | ${'disabled'} | ${'nothing is selected'}
- ${selectedIssuesMocks.tasksOnly} | ${true} | ${'disabled'} | ${'only tasks are selected'}
- ${selectedIssuesMocks.testCasesOnly} | ${true} | ${'disabled'} | ${'only test cases are selected'}
- ${selectedIssuesMocks.issuesOnly} | ${false} | ${'enabled'} | ${'only issues are selected'}
- ${selectedIssuesMocks.tasksAndTestCases} | ${true} | ${'disabled'} | ${'tasks and test cases are selected'}
- ${selectedIssuesMocks.issuesAndTasks} | ${false} | ${'enabled'} | ${'issues and tasks are selected'}
- ${selectedIssuesMocks.issuesAndTestCases} | ${false} | ${'enabled'} | ${'issues and test cases are selected'}
- ${selectedIssuesMocks.issuesTasksAndTestCases} | ${false} | ${'enabled'} | ${'issues and tasks and test cases are selected'}
- `('renders $status if $testMessage', async ({ selectedIssuablesMock, disabled }) => {
- createComponent({ selectedIssuables: selectedIssuablesMock });
-
- await nextTick();
-
- if (disabled) {
- expect(findDropdown().attributes('disabled')).toBe('true');
- } else {
- expect(findDropdown().attributes('disabled')).toBeUndefined();
- }
- });
- });
-
- describe('warning message', () => {
- it.each`
- selectedIssuablesMock | warningExists | visibility | message | testMessage
- ${[]} | ${false} | ${'not visible'} | ${'empty'} | ${'nothing is selected'}
- ${selectedIssuesMocks.tasksOnly} | ${true} | ${'visible'} | ${'Tasks can not be moved.'} | ${'only tasks are selected'}
- ${selectedIssuesMocks.testCasesOnly} | ${true} | ${'visible'} | ${'Test cases can not be moved.'} | ${'only test cases are selected'}
- ${selectedIssuesMocks.issuesOnly} | ${false} | ${'not visible'} | ${'empty'} | ${'only issues are selected'}
- ${selectedIssuesMocks.tasksAndTestCases} | ${true} | ${'visible'} | ${'Tasks and test cases can not be moved.'} | ${'tasks and test cases are selected'}
- ${selectedIssuesMocks.issuesAndTasks} | ${true} | ${'visible'} | ${'Tasks can not be moved.'} | ${'issues and tasks are selected'}
- ${selectedIssuesMocks.issuesAndTestCases} | ${true} | ${'visible'} | ${'Test cases can not be moved.'} | ${'issues and test cases are selected'}
- ${selectedIssuesMocks.issuesTasksAndTestCases} | ${true} | ${'visible'} | ${'Tasks and test cases can not be moved.'} | ${'issues and tasks and test cases are selected'}
- `(
- 'is $visibility with `$message` message if $testMessage',
- async ({ selectedIssuablesMock, warningExists, message }) => {
- createComponent({ selectedIssuables: selectedIssuablesMock });
-
- await nextTick();
-
- const alert = findAlert();
- expect(alert.exists()).toBe(warningExists);
-
- if (warningExists) {
- expect(alert.text()).toBe(message);
- expect(alert.attributes('variant')).toBe('warning');
- }
- },
- );
- });
-
- describe('moveIssues method', () => {
- describe('changes the `Move selected` dropdown loading state', () => {
- it('keeps loading state to false when no issue is selected', async () => {
- createComponent();
- emitMoveIssuablesEvent();
-
- await nextTick();
-
- expect(findDropdown().props('moveInProgress')).toBe(false);
- });
-
- it('keeps loading state to false when only tasks are selected', async () => {
- createComponent({ selectedIssuables: selectedIssuesMocks.tasksOnly });
- emitMoveIssuablesEvent();
-
- await nextTick();
-
- expect(findDropdown().props('moveInProgress')).toBe(false);
- });
-
- it('keeps loading state to false when only test cases are selected', async () => {
- createComponent({ selectedIssuables: selectedIssuesMocks.testCasesOnly });
- emitMoveIssuablesEvent();
-
- await nextTick();
-
- expect(findDropdown().props('moveInProgress')).toBe(false);
- });
-
- it('keeps loading state to false when only tasks and test cases are selected', async () => {
- createComponent({ selectedIssuables: selectedIssuesMocks.tasksAndTestCases });
- emitMoveIssuablesEvent();
-
- await nextTick();
-
- expect(findDropdown().props('moveInProgress')).toBe(false);
- });
-
- it('sets loading state to true when issues are moving', async () => {
- createComponent({ selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases });
- emitMoveIssuablesEvent();
-
- await nextTick();
-
- expect(findDropdown().props('moveInProgress')).toBe(true);
- });
-
- it('sets loading state to false when all mutations succeed', async () => {
- createComponent(
- { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases },
- resolvedMutationWithoutErrorsMock,
- );
- emitMoveIssuablesEvent();
-
- await nextTick();
- await waitForPromises();
-
- expect(findDropdown().props('moveInProgress')).toBe(false);
- });
-
- it('sets loading state to false when a mutation returns errors', async () => {
- createComponent(
- { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases },
- resolvedMutationWithErrorsMock,
- );
- emitMoveIssuablesEvent();
-
- await nextTick();
- await waitForPromises();
-
- expect(findDropdown().props('moveInProgress')).toBe(false);
- });
-
- it('sets loading state to false when a mutation is rejected', async () => {
- createComponent({ selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases });
- emitMoveIssuablesEvent();
-
- await nextTick();
- await waitForPromises();
-
- expect(findDropdown().props('moveInProgress')).toBe(false);
- });
- });
-
- describe('handles events', () => {
- beforeEach(() => {
- jest.spyOn(issuableEventHub, '$emit');
- });
-
- it('does not emit any event when no issue is selected', async () => {
- createComponent();
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(issuableEventHub.$emit).not.toHaveBeenCalled();
- });
-
- it('does not emit any event when only tasks are selected', async () => {
- createComponent({ selectedIssuables: selectedIssuesMocks.tasksOnly });
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(issuableEventHub.$emit).not.toHaveBeenCalled();
- });
-
- it('does not emit any event when only test cases are selected', async () => {
- createComponent({ selectedIssuables: selectedIssuesMocks.testCasesOnly });
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(issuableEventHub.$emit).not.toHaveBeenCalled();
- });
-
- it('does not emit any event when only tasks and test cases are selected', async () => {
- createComponent({ selectedIssuables: selectedIssuesMocks.tasksAndTestCases });
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(issuableEventHub.$emit).not.toHaveBeenCalled();
- });
-
- it('emits `issuables:bulkMoveStarted` when issues are moving', async () => {
- createComponent({ selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases });
- emitMoveIssuablesEvent();
-
- expect(issuableEventHub.$emit).toHaveBeenCalledWith('issuables:bulkMoveStarted');
- });
-
- it('emits `issuables:bulkMoveEnded` when all mutations succeed', async () => {
- createComponent(
- { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases },
- resolvedMutationWithoutErrorsMock,
- );
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(issuableEventHub.$emit).toHaveBeenCalledWith('issuables:bulkMoveEnded');
- });
-
- it('emits `issuables:bulkMoveEnded` when a mutation returns errors', async () => {
- createComponent(
- { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases },
- resolvedMutationWithErrorsMock,
- );
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(issuableEventHub.$emit).toHaveBeenCalledWith('issuables:bulkMoveEnded');
- });
-
- it('emits `issuables:bulkMoveEnded` when a mutation is rejected', async () => {
- createComponent({ selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases });
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(issuableEventHub.$emit).toHaveBeenCalledWith('issuables:bulkMoveEnded');
- });
- });
-
- describe('shows errors', () => {
- it('does not create flashes or logs errors when no issue is selected', async () => {
- createComponent();
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(logError).not.toHaveBeenCalled();
- expect(createFlash).not.toHaveBeenCalled();
- });
-
- it('does not create flashes or logs errors when only tasks are selected', async () => {
- createComponent({ selectedIssuables: selectedIssuesMocks.tasksOnly });
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(logError).not.toHaveBeenCalled();
- expect(createFlash).not.toHaveBeenCalled();
- });
-
- it('does not create flashes or logs errors when only test cases are selected', async () => {
- createComponent({ selectedIssuables: selectedIssuesMocks.testCasesOnly });
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(logError).not.toHaveBeenCalled();
- expect(createFlash).not.toHaveBeenCalled();
- });
-
- it('does not create flashes or logs errors when only tasks and test cases are selected', async () => {
- createComponent({ selectedIssuables: selectedIssuesMocks.tasksAndTestCases });
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(logError).not.toHaveBeenCalled();
- expect(createFlash).not.toHaveBeenCalled();
- });
-
- it('does not create flashes or logs errors when issues are moved without errors', async () => {
- createComponent(
- { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases },
- resolvedMutationWithoutErrorsMock,
- );
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(logError).not.toHaveBeenCalled();
- expect(createFlash).not.toHaveBeenCalled();
- });
-
- it('creates a flash and logs errors when a mutation returns errors', async () => {
- createComponent(
- { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases },
- resolvedMutationWithErrorsMock,
- );
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- // We're mocking two issues so it will log two errors
- expect(logError).toHaveBeenCalledTimes(2);
- expect(logError).toHaveBeenNthCalledWith(
- 1,
- `Error moving issue. Error message: ${mockMutationErrorMessage}`,
- );
- expect(logError).toHaveBeenNthCalledWith(
- 2,
- `Error moving issue. Error message: ${mockMutationErrorMessage}`,
- );
-
- // Only one flash is created even if multiple errors are reported
- expect(createFlash).toHaveBeenCalledTimes(1);
- expect(createFlash).toHaveBeenCalledWith({
- message: 'There was an error while moving the issues.',
- });
- });
-
- it('creates a flash but not logs errors when a mutation is rejected', async () => {
- createComponent({ selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases });
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(logError).not.toHaveBeenCalled();
- expect(createFlash).toHaveBeenCalledTimes(1);
- expect(createFlash).toHaveBeenCalledWith({
- message: 'There was an error while moving the issues.',
- });
- });
- });
-
- describe('calls mutations', () => {
- it('does not call any mutation when no issue is selected', async () => {
- createComponent({}, resolvedMutationWithoutErrorsMock);
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(resolvedMutationWithoutErrorsMock).not.toHaveBeenCalled();
- });
-
- it('does not call any mutation when only tasks are selected', async () => {
- createComponent(
- { selectedIssuables: selectedIssuesMocks.tasksOnly },
- resolvedMutationWithoutErrorsMock,
- );
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(resolvedMutationWithoutErrorsMock).not.toHaveBeenCalled();
- });
-
- it('does not call any mutation when only test cases are selected', async () => {
- createComponent(
- { selectedIssuables: selectedIssuesMocks.testCasesOnly },
- resolvedMutationWithoutErrorsMock,
- );
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(resolvedMutationWithoutErrorsMock).not.toHaveBeenCalled();
- });
-
- it('does not call any mutation when only tasks and test cases are selected', async () => {
- createComponent(
- { selectedIssuables: selectedIssuesMocks.tasksAndTestCases },
- resolvedMutationWithoutErrorsMock,
- );
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- expect(resolvedMutationWithoutErrorsMock).not.toHaveBeenCalled();
- });
-
- it('calls a mutation for every selected issue skipping tasks', async () => {
- createComponent(
- { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases },
- resolvedMutationWithoutErrorsMock,
- );
- emitMoveIssuablesEvent();
-
- await waitForPromises();
-
- // We mock three elements but only two are valid issues since the task is skipped
- expect(resolvedMutationWithoutErrorsMock).toHaveBeenCalledTimes(2);
- expect(resolvedMutationWithoutErrorsMock).toHaveBeenNthCalledWith(1, {
- moveIssueInput: {
- projectPath: mockDefaultProps.projectFullPath,
- iid: selectedIssuesMocks.issuesTasksAndTestCases[0].iid.toString(),
- targetProjectPath: mockDestinationProject.full_path,
- },
- });
-
- expect(resolvedMutationWithoutErrorsMock).toHaveBeenNthCalledWith(2, {
- moveIssueInput: {
- projectPath: mockDefaultProps.projectFullPath,
- iid: selectedIssuesMocks.issuesTasksAndTestCases[1].iid.toString(),
- targetProjectPath: mockDestinationProject.full_path,
- },
- });
- });
- });
- });
-});
diff --git a/spec/frontend/issuable/bulk_update_sidebar/components/status_dropdown_spec.js b/spec/frontend/issuable/bulk_update_sidebar/components/status_dropdown_spec.js
deleted file mode 100644
index 2f281cb88f9..00000000000
--- a/spec/frontend/issuable/bulk_update_sidebar/components/status_dropdown_spec.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import StatusDropdown from '~/issuable/bulk_update_sidebar/components/status_dropdown.vue';
-import { statusDropdownOptions } from '~/issuable/bulk_update_sidebar/constants';
-
-describe('SubscriptionsDropdown component', () => {
- let wrapper;
-
- const findDropdown = () => wrapper.findComponent(GlDropdown);
- const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
- const findHiddenInput = () => wrapper.find('input');
-
- function createComponent() {
- wrapper = shallowMount(StatusDropdown);
- }
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('with no value selected', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders default text', () => {
- expect(findDropdown().props('text')).toBe('Select status');
- });
-
- it('renders dropdown items with `is-checked` prop set to `false`', () => {
- const dropdownItems = findAllDropdownItems();
-
- expect(dropdownItems.at(0).props('isChecked')).toBe(false);
- expect(dropdownItems.at(1).props('isChecked')).toBe(false);
- });
- });
-
- describe('when selecting a value', () => {
- const selectItemAtIndex = 0;
-
- beforeEach(async () => {
- createComponent();
- await findAllDropdownItems().at(selectItemAtIndex).vm.$emit('click');
- });
-
- it('updates value of the hidden input', () => {
- expect(findHiddenInput().attributes('value')).toBe(
- statusDropdownOptions[selectItemAtIndex].value,
- );
- });
-
- it('updates the dropdown text prop', () => {
- expect(findDropdown().props('text')).toBe(statusDropdownOptions[selectItemAtIndex].text);
- });
-
- it('sets dropdown item `is-checked` prop to `true`', () => {
- const dropdownItems = findAllDropdownItems();
-
- expect(dropdownItems.at(0).props('isChecked')).toBe(true);
- expect(dropdownItems.at(1).props('isChecked')).toBe(false);
- });
-
- describe('when selecting the value that is already selected', () => {
- it('clears dropdown selection', async () => {
- await findAllDropdownItems().at(selectItemAtIndex).vm.$emit('click');
-
- const dropdownItems = findAllDropdownItems();
-
- expect(dropdownItems.at(0).props('isChecked')).toBe(false);
- expect(dropdownItems.at(1).props('isChecked')).toBe(false);
- expect(findDropdown().props('text')).toBe('Select status');
- });
- });
- });
-});
diff --git a/spec/frontend/issuable/bulk_update_sidebar/components/subscriptions_dropdown_spec.js b/spec/frontend/issuable/bulk_update_sidebar/components/subscriptions_dropdown_spec.js
deleted file mode 100644
index 56ef7a1ed39..00000000000
--- a/spec/frontend/issuable/bulk_update_sidebar/components/subscriptions_dropdown_spec.js
+++ /dev/null
@@ -1,76 +0,0 @@
-import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import SubscriptionsDropdown from '~/issuable/bulk_update_sidebar/components/subscriptions_dropdown.vue';
-import { subscriptionsDropdownOptions } from '~/issuable/bulk_update_sidebar/constants';
-
-describe('SubscriptionsDropdown component', () => {
- let wrapper;
-
- const findDropdown = () => wrapper.findComponent(GlDropdown);
- const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
- const findHiddenInput = () => wrapper.find('input');
-
- function createComponent() {
- wrapper = shallowMount(SubscriptionsDropdown);
- }
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('with no value selected', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('hidden input value is undefined', () => {
- expect(findHiddenInput().attributes('value')).toBeUndefined();
- });
-
- it('renders default text', () => {
- expect(findDropdown().props('text')).toBe(SubscriptionsDropdown.i18n.defaultDropdownText);
- });
-
- it('renders dropdown items with `is-checked` prop set to `false`', () => {
- const dropdownItems = findAllDropdownItems();
-
- expect(dropdownItems.at(0).props('isChecked')).toBe(false);
- expect(dropdownItems.at(1).props('isChecked')).toBe(false);
- });
- });
-
- describe('when selecting a value', () => {
- beforeEach(() => {
- createComponent();
- findAllDropdownItems().at(0).vm.$emit('click');
- });
-
- it('updates value of the hidden input', () => {
- expect(findHiddenInput().attributes('value')).toBe(subscriptionsDropdownOptions[0].value);
- });
-
- it('updates the dropdown text prop', () => {
- expect(findDropdown().props('text')).toBe(subscriptionsDropdownOptions[0].text);
- });
-
- it('sets dropdown item `is-checked` prop to `true`', () => {
- const dropdownItems = findAllDropdownItems();
-
- expect(dropdownItems.at(0).props('isChecked')).toBe(true);
- expect(dropdownItems.at(1).props('isChecked')).toBe(false);
- });
-
- describe('when selecting the value that is already selected', () => {
- it('clears dropdown selection', async () => {
- findAllDropdownItems().at(0).vm.$emit('click');
- await nextTick();
- const dropdownItems = findAllDropdownItems();
-
- expect(dropdownItems.at(0).props('isChecked')).toBe(false);
- expect(dropdownItems.at(1).props('isChecked')).toBe(false);
- expect(findDropdown().props('text')).toBe(SubscriptionsDropdown.i18n.defaultDropdownText);
- });
- });
- });
-});
diff --git a/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js b/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js
index 3f72396cce6..3f40772f7fc 100644
--- a/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js
+++ b/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js
@@ -1,58 +1,380 @@
import { GlEmptyState } from '@gitlab/ui';
+import * as Sentry from '@sentry/browser';
+import AxiosMockAdapter from 'axios-mock-adapter';
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
+import { cloneDeep } from 'lodash';
+import getIssuesQuery from 'ee_else_ce/issues/dashboard/queries/get_issues.query.graphql';
+import IssueCardStatistics from 'ee_else_ce/issues/list/components/issue_card_statistics.vue';
+import IssueCardTimeInfo from 'ee_else_ce/issues/list/components/issue_card_time_info.vue';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import setWindowLocation from 'helpers/set_window_location_helper';
+import { TEST_HOST } from 'helpers/test_constants';
import { mountExtended } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import {
+ filteredTokens,
+ locationSearch,
+ setSortPreferenceMutationResponse,
+ setSortPreferenceMutationResponseWithErrors,
+} from 'jest/issues/list/mock_data';
import IssuesDashboardApp from '~/issues/dashboard/components/issues_dashboard_app.vue';
+import { CREATED_DESC, i18n, UPDATED_DESC, urlSortParams } from '~/issues/list/constants';
+import setSortPreferenceMutation from '~/issues/list/queries/set_sort_preference.mutation.graphql';
+import { getSortKey, getSortOptions } from '~/issues/list/utils';
+import axios from '~/lib/utils/axios_utils';
+import { scrollUp } from '~/lib/utils/scroll_utils';
+import {
+ TOKEN_TYPE_ASSIGNEE,
+ TOKEN_TYPE_AUTHOR,
+} from '~/vue_shared/components/filtered_search_bar/constants';
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
import { IssuableStates } from '~/vue_shared/issuable/list/constants';
+import { emptyIssuesQueryResponse, issuesQueryResponse } from '../mock_data';
+
+jest.mock('@sentry/browser');
+jest.mock('~/lib/utils/scroll_utils', () => ({ scrollUp: jest.fn() }));
describe('IssuesDashboardApp component', () => {
+ let axiosMock;
let wrapper;
+ Vue.use(VueApollo);
+
const defaultProvide = {
calendarPath: 'calendar/path',
emptyStateSvgPath: 'empty-state.svg',
+ hasBlockedIssuesFeature: true,
+ hasIssuableHealthStatusFeature: true,
+ hasIssueWeightsFeature: true,
+ hasScopedLabelsFeature: true,
+ initialSort: CREATED_DESC,
+ isPublicVisibilityRestricted: false,
isSignedIn: true,
rssPath: 'rss/path',
};
+ let defaultQueryResponse = issuesQueryResponse;
+ if (IS_EE) {
+ defaultQueryResponse = cloneDeep(issuesQueryResponse);
+ defaultQueryResponse.data.issues.nodes[0].blockingCount = 1;
+ defaultQueryResponse.data.issues.nodes[0].healthStatus = null;
+ defaultQueryResponse.data.issues.nodes[0].weight = 5;
+ }
+
const findCalendarButton = () =>
wrapper.findByRole('link', { name: IssuesDashboardApp.i18n.calendarButtonText });
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
const findIssuableList = () => wrapper.findComponent(IssuableList);
+ const findIssueCardStatistics = () => wrapper.findComponent(IssueCardStatistics);
+ const findIssueCardTimeInfo = () => wrapper.findComponent(IssueCardTimeInfo);
const findRssButton = () =>
wrapper.findByRole('link', { name: IssuesDashboardApp.i18n.rssButtonText });
- const mountComponent = () => {
- wrapper = mountExtended(IssuesDashboardApp, { provide: defaultProvide });
+ const mountComponent = ({
+ provide = {},
+ issuesQueryHandler = jest.fn().mockResolvedValue(defaultQueryResponse),
+ sortPreferenceMutationResponse = jest.fn().mockResolvedValue(setSortPreferenceMutationResponse),
+ } = {}) => {
+ wrapper = mountExtended(IssuesDashboardApp, {
+ apolloProvider: createMockApollo([
+ [getIssuesQuery, issuesQueryHandler],
+ [setSortPreferenceMutation, sortPreferenceMutationResponse],
+ ]),
+ provide: {
+ ...defaultProvide,
+ ...provide,
+ },
+ });
};
beforeEach(() => {
- mountComponent();
+ setWindowLocation(TEST_HOST);
+ axiosMock = new AxiosMockAdapter(axios);
});
- it('renders IssuableList component', () => {
+ afterEach(() => {
+ axiosMock.reset();
+ });
+
+ it('renders IssuableList component', async () => {
+ mountComponent();
+ jest.runOnlyPendingTimers();
+ await waitForPromises();
+
expect(findIssuableList().props()).toMatchObject({
currentTab: IssuableStates.Opened,
+ hasNextPage: true,
+ hasPreviousPage: false,
+ hasScopedLabelsFeature: defaultProvide.hasScopedLabelsFeature,
+ initialSortBy: CREATED_DESC,
+ issuables: issuesQueryResponse.data.issues.nodes,
+ issuablesLoading: false,
namespace: 'dashboard',
recentSearchesStorageKey: 'issues',
searchInputPlaceholder: IssuesDashboardApp.i18n.searchInputPlaceholder,
+ showPaginationControls: true,
+ sortOptions: getSortOptions({
+ hasBlockedIssuesFeature: defaultProvide.hasBlockedIssuesFeature,
+ hasIssuableHealthStatusFeature: defaultProvide.hasIssuableHealthStatusFeature,
+ hasIssueWeightsFeature: defaultProvide.hasIssueWeightsFeature,
+ }),
tabs: IssuesDashboardApp.IssuableListTabs,
+ urlParams: {
+ sort: urlSortParams[CREATED_DESC],
+ state: IssuableStates.Opened,
+ },
+ useKeysetPagination: true,
});
});
it('renders RSS button link', () => {
+ mountComponent();
+
expect(findRssButton().attributes('href')).toBe(defaultProvide.rssPath);
expect(findRssButton().props('icon')).toBe('rss');
});
it('renders calendar button link', () => {
+ mountComponent();
+
expect(findCalendarButton().attributes('href')).toBe(defaultProvide.calendarPath);
expect(findCalendarButton().props('icon')).toBe('calendar');
});
- it('renders empty state', () => {
+ it('renders issue time information', async () => {
+ mountComponent();
+ jest.runOnlyPendingTimers();
+ await waitForPromises();
+
+ expect(findIssueCardTimeInfo().exists()).toBe(true);
+ });
+
+ it('renders issue statistics', async () => {
+ mountComponent();
+ jest.runOnlyPendingTimers();
+ await waitForPromises();
+
+ expect(findIssueCardStatistics().exists()).toBe(true);
+ });
+
+ it('renders empty state', async () => {
+ mountComponent({ issuesQueryHandler: jest.fn().mockResolvedValue(emptyIssuesQueryResponse) });
+ await waitForPromises();
+
expect(findEmptyState().props()).toMatchObject({
svgPath: defaultProvide.emptyStateSvgPath,
title: IssuesDashboardApp.i18n.emptyStateTitle,
});
});
+
+ describe('initial url params', () => {
+ describe('search', () => {
+ it('is set from the url params', () => {
+ setWindowLocation(locationSearch);
+ mountComponent();
+
+ expect(findIssuableList().props('urlParams')).toMatchObject({ search: 'find issues' });
+ });
+ });
+
+ describe('sort', () => {
+ describe('when initial sort value uses old enum values', () => {
+ const oldEnumSortValues = Object.values(urlSortParams);
+
+ it.each(oldEnumSortValues)('initial sort is set with value %s', (sort) => {
+ mountComponent({ provide: { initialSort: sort } });
+
+ expect(findIssuableList().props('initialSortBy')).toBe(getSortKey(sort));
+ });
+ });
+
+ describe('when initial sort value uses new GraphQL enum values', () => {
+ const graphQLEnumSortValues = Object.keys(urlSortParams);
+
+ it.each(graphQLEnumSortValues)('initial sort is set with value %s', (sort) => {
+ mountComponent({ provide: { initialSort: sort.toLowerCase() } });
+
+ expect(findIssuableList().props('initialSortBy')).toBe(sort);
+ });
+ });
+
+ describe('when initial sort value is invalid', () => {
+ it.each(['', 'asdf', null, undefined])(
+ 'initial sort is set to value CREATED_DESC',
+ (sort) => {
+ mountComponent({ provide: { initialSort: sort } });
+
+ expect(findIssuableList().props('initialSortBy')).toBe(CREATED_DESC);
+ },
+ );
+ });
+ });
+
+ describe('state', () => {
+ it('is set from the url params', () => {
+ const initialState = IssuableStates.All;
+ setWindowLocation(`?state=${initialState}`);
+ mountComponent();
+
+ expect(findIssuableList().props('currentTab')).toBe(initialState);
+ });
+ });
+
+ describe('filter tokens', () => {
+ it('is set from the url params', () => {
+ setWindowLocation(locationSearch);
+ mountComponent();
+
+ expect(findIssuableList().props('initialFilterValue')).toEqual(filteredTokens);
+ });
+ });
+ });
+
+ describe('when there is an error fetching issues', () => {
+ beforeEach(() => {
+ mountComponent({ issuesQueryHandler: jest.fn().mockRejectedValue(new Error('ERROR')) });
+ jest.runOnlyPendingTimers();
+ return waitForPromises();
+ });
+
+ it('shows an error message', () => {
+ expect(findIssuableList().props('error')).toBe(i18n.errorFetchingIssues);
+ expect(Sentry.captureException).toHaveBeenCalledWith(new Error('ERROR'));
+ });
+
+ it('clears error message when "dismiss-alert" event is emitted from IssuableList', async () => {
+ findIssuableList().vm.$emit('dismiss-alert');
+ await nextTick();
+
+ expect(findIssuableList().props('error')).toBeNull();
+ });
+ });
+
+ describe('tokens', () => {
+ const mockCurrentUser = {
+ id: 1,
+ name: 'Administrator',
+ username: 'root',
+ avatar_url: 'avatar/url',
+ };
+ const originalGon = window.gon;
+
+ beforeEach(() => {
+ window.gon = {
+ ...originalGon,
+ current_user_id: mockCurrentUser.id,
+ current_user_fullname: mockCurrentUser.name,
+ current_username: mockCurrentUser.username,
+ current_user_avatar_url: mockCurrentUser.avatar_url,
+ };
+ mountComponent();
+ });
+
+ afterEach(() => {
+ window.gon = originalGon;
+ });
+
+ it('renders all tokens alphabetically', () => {
+ const preloadedUsers = [{ ...mockCurrentUser, id: mockCurrentUser.id }];
+
+ expect(findIssuableList().props('searchTokens')).toMatchObject([
+ { type: TOKEN_TYPE_ASSIGNEE, preloadedUsers },
+ { type: TOKEN_TYPE_AUTHOR, preloadedUsers },
+ ]);
+ });
+ });
+
+ describe('events', () => {
+ describe('when "click-tab" event is emitted by IssuableList', () => {
+ beforeEach(() => {
+ mountComponent();
+
+ findIssuableList().vm.$emit('click-tab', IssuableStates.Closed);
+ });
+
+ it('updates ui to the new tab', () => {
+ expect(findIssuableList().props('currentTab')).toBe(IssuableStates.Closed);
+ });
+
+ it('updates url to the new tab', () => {
+ expect(findIssuableList().props('urlParams')).toMatchObject({
+ state: IssuableStates.Closed,
+ });
+ });
+ });
+
+ describe.each(['next-page', 'previous-page'])(
+ 'when "%s" event is emitted by IssuableList',
+ (event) => {
+ beforeEach(() => {
+ mountComponent();
+
+ findIssuableList().vm.$emit(event);
+ });
+
+ it('scrolls to the top', () => {
+ expect(scrollUp).toHaveBeenCalled();
+ });
+ },
+ );
+
+ describe('when "sort" event is emitted by IssuableList', () => {
+ it.each(Object.keys(urlSortParams))(
+ 'updates to the new sort when payload is `%s`',
+ async (sortKey) => {
+ // Ensure initial sort key is different so we can trigger an update when emitting a sort key
+ if (sortKey === CREATED_DESC) {
+ mountComponent({ provide: { initialSort: UPDATED_DESC } });
+ } else {
+ mountComponent();
+ }
+
+ findIssuableList().vm.$emit('sort', sortKey);
+ await nextTick();
+
+ expect(findIssuableList().props('urlParams')).toMatchObject({
+ sort: urlSortParams[sortKey],
+ });
+ },
+ );
+
+ describe('when user is signed in', () => {
+ it('calls mutation to save sort preference', () => {
+ const mutationMock = jest.fn().mockResolvedValue(setSortPreferenceMutationResponse);
+ mountComponent({ sortPreferenceMutationResponse: mutationMock });
+
+ findIssuableList().vm.$emit('sort', UPDATED_DESC);
+
+ expect(mutationMock).toHaveBeenCalledWith({ input: { issuesSort: UPDATED_DESC } });
+ });
+
+ it('captures error when mutation response has errors', async () => {
+ const mutationMock = jest
+ .fn()
+ .mockResolvedValue(setSortPreferenceMutationResponseWithErrors);
+ mountComponent({ sortPreferenceMutationResponse: mutationMock });
+
+ findIssuableList().vm.$emit('sort', UPDATED_DESC);
+ await waitForPromises();
+
+ expect(Sentry.captureException).toHaveBeenCalledWith(new Error('oh no!'));
+ });
+ });
+
+ describe('when user is signed out', () => {
+ it('does not call mutation to save sort preference', () => {
+ const mutationMock = jest.fn().mockResolvedValue(setSortPreferenceMutationResponse);
+ mountComponent({
+ provide: { isSignedIn: false },
+ sortPreferenceMutationResponse: mutationMock,
+ });
+
+ findIssuableList().vm.$emit('sort', CREATED_DESC);
+
+ expect(mutationMock).not.toHaveBeenCalled();
+ });
+ });
+ });
+ });
});
diff --git a/spec/frontend/issues/dashboard/mock_data.js b/spec/frontend/issues/dashboard/mock_data.js
new file mode 100644
index 00000000000..feb4cb80bd8
--- /dev/null
+++ b/spec/frontend/issues/dashboard/mock_data.js
@@ -0,0 +1,88 @@
+export const issuesQueryResponse = {
+ data: {
+ issues: {
+ nodes: [
+ {
+ __typename: 'Issue',
+ id: 'gid://gitlab/Issue/123456',
+ iid: '789',
+ closedAt: null,
+ confidential: false,
+ createdAt: '2021-05-22T04:08:01Z',
+ downvotes: 2,
+ dueDate: '2021-05-29',
+ hidden: false,
+ humanTimeEstimate: null,
+ mergeRequestsCount: false,
+ moved: false,
+ reference: 'group/project#123456',
+ state: 'opened',
+ title: 'Issue title',
+ type: 'issue',
+ updatedAt: '2021-05-22T04:08:01Z',
+ upvotes: 3,
+ userDiscussionsCount: 4,
+ webPath: 'project/-/issues/789',
+ webUrl: 'project/-/issues/789',
+ assignees: {
+ nodes: [
+ {
+ __typename: 'UserCore',
+ id: 'gid://gitlab/User/234',
+ avatarUrl: 'avatar/url',
+ name: 'Marge Simpson',
+ username: 'msimpson',
+ webUrl: 'url/msimpson',
+ },
+ ],
+ },
+ author: {
+ __typename: 'UserCore',
+ id: 'gid://gitlab/User/456',
+ avatarUrl: 'avatar/url',
+ name: 'Homer Simpson',
+ username: 'hsimpson',
+ webUrl: 'url/hsimpson',
+ },
+ labels: {
+ nodes: [
+ {
+ id: 'gid://gitlab/ProjectLabel/456',
+ color: '#333',
+ title: 'Label title',
+ description: 'Label description',
+ },
+ ],
+ },
+ milestone: null,
+ taskCompletionStatus: {
+ completedCount: 1,
+ count: 2,
+ },
+ },
+ ],
+ pageInfo: {
+ __typename: 'PageInfo',
+ hasNextPage: true,
+ hasPreviousPage: false,
+ startCursor: 'startcursor',
+ endCursor: 'endcursor',
+ },
+ },
+ },
+};
+
+export const emptyIssuesQueryResponse = {
+ data: {
+ issues: {
+ nodes: [],
+ pageInfo: {
+ __typename: 'PageInfo',
+ hasNextPage: false,
+ hasPreviousPage: false,
+ startCursor: '',
+ endCursor: '',
+ },
+ },
+ },
+};
diff --git a/spec/frontend/issues/list/components/empty_state_with_any_issues_spec.js b/spec/frontend/issues/list/components/empty_state_with_any_issues_spec.js
new file mode 100644
index 00000000000..d0d20ef03e1
--- /dev/null
+++ b/spec/frontend/issues/list/components/empty_state_with_any_issues_spec.js
@@ -0,0 +1,68 @@
+import { GlEmptyState } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import EmptyStateWithAnyIssues from '~/issues/list/components/empty_state_with_any_issues.vue';
+import IssuesListApp from '~/issues/list/components/issues_list_app.vue';
+
+describe('EmptyStateWithAnyIssues component', () => {
+ let wrapper;
+
+ const defaultProvide = {
+ emptyStateSvgPath: 'empty/state/svg/path',
+ newIssuePath: 'new/issue/path',
+ showNewIssueLink: false,
+ };
+
+ const findGlEmptyState = () => wrapper.findComponent(GlEmptyState);
+
+ const mountComponent = (props = {}) => {
+ wrapper = shallowMount(EmptyStateWithAnyIssues, {
+ propsData: {
+ hasSearch: true,
+ isOpenTab: true,
+ ...props,
+ },
+ provide: defaultProvide,
+ });
+ };
+
+ describe('when there is a search (with no results)', () => {
+ beforeEach(() => {
+ mountComponent({ hasSearch: true });
+ });
+
+ it('shows empty state', () => {
+ expect(findGlEmptyState().props()).toMatchObject({
+ description: IssuesListApp.i18n.noSearchResultsDescription,
+ title: IssuesListApp.i18n.noSearchResultsTitle,
+ svgPath: defaultProvide.emptyStateSvgPath,
+ });
+ });
+ });
+
+ describe('when "Open" tab is active', () => {
+ beforeEach(() => {
+ mountComponent({ hasSearch: false, isOpenTab: true });
+ });
+
+ it('shows empty state', () => {
+ expect(findGlEmptyState().props()).toMatchObject({
+ description: IssuesListApp.i18n.noOpenIssuesDescription,
+ title: IssuesListApp.i18n.noOpenIssuesTitle,
+ svgPath: defaultProvide.emptyStateSvgPath,
+ });
+ });
+ });
+
+ describe('when "Closed" tab is active', () => {
+ beforeEach(() => {
+ mountComponent({ hasSearch: false, isOpenTab: false });
+ });
+
+ it('shows empty state', () => {
+ expect(findGlEmptyState().props()).toMatchObject({
+ title: IssuesListApp.i18n.noClosedIssuesTitle,
+ svgPath: defaultProvide.emptyStateSvgPath,
+ });
+ });
+ });
+});
diff --git a/spec/frontend/issues/list/components/empty_state_without_any_issues_spec.js b/spec/frontend/issues/list/components/empty_state_without_any_issues_spec.js
new file mode 100644
index 00000000000..065139f10f4
--- /dev/null
+++ b/spec/frontend/issues/list/components/empty_state_without_any_issues_spec.js
@@ -0,0 +1,211 @@
+import { GlEmptyState, GlLink } from '@gitlab/ui';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue';
+import EmptyStateWithoutAnyIssues from '~/issues/list/components/empty_state_without_any_issues.vue';
+import NewIssueDropdown from '~/issues/list/components/new_issue_dropdown.vue';
+import { i18n } from '~/issues/list/constants';
+
+describe('EmptyStateWithoutAnyIssues component', () => {
+ let wrapper;
+
+ const defaultProps = {
+ currentTabCount: 0,
+ exportCsvPathWithQuery: 'export/csv/path',
+ };
+
+ const defaultProvide = {
+ canCreateProjects: false,
+ emptyStateSvgPath: 'empty/state/svg/path',
+ fullPath: 'full/path',
+ isSignedIn: true,
+ jiraIntegrationPath: 'jira/integration/path',
+ newIssuePath: 'new/issue/path',
+ newProjectPath: 'new/project/path',
+ showNewIssueLink: false,
+ signInPath: 'sign/in/path',
+ };
+
+ const findCsvImportExportButtons = () => wrapper.findComponent(CsvImportExportButtons);
+ const findGlEmptyState = () => wrapper.findComponent(GlEmptyState);
+ const findGlLink = () => wrapper.findComponent(GlLink);
+ const findIssuesHelpPageLink = () =>
+ wrapper.findByRole('link', { name: i18n.noIssuesDescription });
+ const findJiraDocsLink = () =>
+ wrapper.findByRole('link', { name: 'Enable the Jira integration' });
+ const findNewIssueDropdown = () => wrapper.findComponent(NewIssueDropdown);
+ const findNewIssueLink = () => wrapper.findByRole('link', { name: i18n.newIssueLabel });
+ const findNewProjectLink = () => wrapper.findByRole('link', { name: i18n.newProjectLabel });
+
+ const mountComponent = ({ props = {}, provide = {} } = {}) => {
+ wrapper = mountExtended(EmptyStateWithoutAnyIssues, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ provide: {
+ ...defaultProvide,
+ ...provide,
+ },
+ stubs: {
+ NewIssueDropdown: true,
+ },
+ });
+ };
+
+ describe('when signed in', () => {
+ describe('empty state', () => {
+ it('renders empty state', () => {
+ mountComponent();
+
+ expect(findGlEmptyState().props()).toMatchObject({
+ title: i18n.noIssuesTitle,
+ svgPath: defaultProvide.emptyStateSvgPath,
+ });
+ });
+
+ describe('description', () => {
+ it('renders issues docs link', () => {
+ mountComponent();
+
+ expect(findIssuesHelpPageLink().attributes('href')).toBe(
+ EmptyStateWithoutAnyIssues.issuesHelpPagePath,
+ );
+ });
+
+ describe('"create a project first" description', () => {
+ describe('when can create projects', () => {
+ it('renders', () => {
+ mountComponent({ provide: { canCreateProjects: true } });
+
+ expect(findGlEmptyState().text()).toContain(i18n.noGroupIssuesSignedInDescription);
+ });
+ });
+
+ describe('when cannot create projects', () => {
+ it('does not render', () => {
+ mountComponent({ provide: { canCreateProjects: false } });
+
+ expect(findGlEmptyState().text()).not.toContain(
+ i18n.noGroupIssuesSignedInDescription,
+ );
+ });
+ });
+ });
+ });
+
+ describe('actions', () => {
+ describe('"New project" link', () => {
+ describe('when can create projects', () => {
+ it('renders', () => {
+ mountComponent({ provide: { canCreateProjects: true } });
+
+ expect(findNewProjectLink().attributes('href')).toBe(defaultProvide.newProjectPath);
+ });
+ });
+
+ describe('when cannot create projects', () => {
+ it('does not render', () => {
+ mountComponent({ provide: { canCreateProjects: false } });
+
+ expect(findNewProjectLink().exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('"New issue" link', () => {
+ describe('when can show new issue link', () => {
+ it('renders', () => {
+ mountComponent({ provide: { showNewIssueLink: true } });
+
+ expect(findNewIssueLink().attributes('href')).toBe(defaultProvide.newIssuePath);
+ });
+ });
+
+ describe('when cannot show new issue link', () => {
+ it('does not render', () => {
+ mountComponent({ provide: { showNewIssueLink: false } });
+
+ expect(findNewIssueLink().exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('CSV import/export buttons', () => {
+ describe('when can show csv buttons', () => {
+ it('renders', () => {
+ mountComponent({ props: { showCsvButtons: true } });
+
+ expect(findCsvImportExportButtons().props()).toMatchObject({
+ exportCsvPath: defaultProps.exportCsvPathWithQuery,
+ issuableCount: 0,
+ });
+ });
+ });
+
+ describe('when cannot show csv buttons', () => {
+ it('does not render', () => {
+ mountComponent({ props: { showCsvButtons: false } });
+
+ expect(findCsvImportExportButtons().exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('new issue dropdown', () => {
+ describe('when can show new issue dropdown', () => {
+ it('renders', () => {
+ mountComponent({ props: { showNewIssueDropdown: true } });
+
+ expect(findNewIssueDropdown().exists()).toBe(true);
+ });
+ });
+
+ describe('when cannot show new issue dropdown', () => {
+ it('does not render', () => {
+ mountComponent({ props: { showNewIssueDropdown: false } });
+
+ expect(findNewIssueDropdown().exists()).toBe(false);
+ });
+ });
+ });
+ });
+ });
+
+ describe('Jira section', () => {
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ it('shows Jira integration information', () => {
+ const paragraphs = wrapper.findAll('p');
+ expect(paragraphs.at(1).text()).toContain(i18n.jiraIntegrationTitle);
+ expect(paragraphs.at(2).text()).toMatchInterpolatedText(i18n.jiraIntegrationMessage);
+ expect(paragraphs.at(3).text()).toContain(i18n.jiraIntegrationSecondaryMessage);
+ });
+
+ it('renders Jira integration docs link', () => {
+ expect(findJiraDocsLink().attributes('href')).toBe(defaultProvide.jiraIntegrationPath);
+ });
+ });
+ });
+
+ describe('when signed out', () => {
+ beforeEach(() => {
+ mountComponent({ provide: { isSignedIn: false } });
+ });
+
+ it('renders empty state', () => {
+ expect(findGlEmptyState().props()).toMatchObject({
+ title: i18n.noIssuesTitle,
+ svgPath: defaultProvide.emptyStateSvgPath,
+ primaryButtonText: i18n.noIssuesSignedOutButtonText,
+ primaryButtonLink: defaultProvide.signInPath,
+ });
+ });
+
+ it('renders issues docs link', () => {
+ expect(findGlLink().attributes('href')).toBe(EmptyStateWithoutAnyIssues.issuesHelpPagePath);
+ expect(findGlLink().text()).toBe(i18n.noIssuesDescription);
+ });
+ });
+});
diff --git a/spec/frontend/issues/list/components/issue_card_statistics_spec.js b/spec/frontend/issues/list/components/issue_card_statistics_spec.js
new file mode 100644
index 00000000000..180d4ab7eb6
--- /dev/null
+++ b/spec/frontend/issues/list/components/issue_card_statistics_spec.js
@@ -0,0 +1,64 @@
+import { GlIcon } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import IssueCardStatistics from '~/issues/list/components/issue_card_statistics.vue';
+import { i18n } from '~/issues/list/constants';
+
+describe('IssueCardStatistics CE component', () => {
+ let wrapper;
+
+ const findMergeRequests = () => wrapper.findByTestId('merge-requests');
+ const findUpvotes = () => wrapper.findByTestId('issuable-upvotes');
+ const findDownvotes = () => wrapper.findByTestId('issuable-downvotes');
+
+ const mountComponent = ({ mergeRequestsCount, upvotes, downvotes } = {}) => {
+ wrapper = shallowMountExtended(IssueCardStatistics, {
+ propsData: {
+ issue: {
+ mergeRequestsCount,
+ upvotes,
+ downvotes,
+ },
+ },
+ });
+ };
+
+ describe('when issue attributes are undefined', () => {
+ it('does not render the attributes', () => {
+ mountComponent();
+
+ expect(findMergeRequests().exists()).toBe(false);
+ expect(findUpvotes().exists()).toBe(false);
+ expect(findDownvotes().exists()).toBe(false);
+ });
+ });
+
+ describe('when issue attributes are defined', () => {
+ beforeEach(() => {
+ mountComponent({ mergeRequestsCount: 1, upvotes: 5, downvotes: 9 });
+ });
+
+ it('renders merge requests', () => {
+ const mergeRequests = findMergeRequests();
+
+ expect(mergeRequests.text()).toBe('1');
+ expect(mergeRequests.attributes('title')).toBe(i18n.relatedMergeRequests);
+ expect(mergeRequests.findComponent(GlIcon).props('name')).toBe('merge-request');
+ });
+
+ it('renders upvotes', () => {
+ const upvotes = findUpvotes();
+
+ expect(upvotes.text()).toBe('5');
+ expect(upvotes.attributes('title')).toBe(i18n.upvotes);
+ expect(upvotes.findComponent(GlIcon).props('name')).toBe('thumb-up');
+ });
+
+ it('renders downvotes', () => {
+ const downvotes = findDownvotes();
+
+ expect(downvotes.text()).toBe('9');
+ expect(downvotes.attributes('title')).toBe(i18n.downvotes);
+ expect(downvotes.findComponent(GlIcon).props('name')).toBe('thumb-down');
+ });
+ });
+});
diff --git a/spec/frontend/issues/list/components/issues_list_app_spec.js b/spec/frontend/issues/list/components/issues_list_app_spec.js
index d0c93c896b3..4c5d8ce3cd1 100644
--- a/spec/frontend/issues/list/components/issues_list_app_spec.js
+++ b/spec/frontend/issues/list/components/issues_list_app_spec.js
@@ -1,4 +1,4 @@
-import { GlButton, GlEmptyState } from '@gitlab/ui';
+import { GlButton } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { mount, shallowMount } from '@vue/test-utils';
import AxiosMockAdapter from 'axios-mock-adapter';
@@ -21,18 +21,21 @@ import {
setSortPreferenceMutationResponseWithErrors,
urlParams,
} from 'jest/issues/list/mock_data';
-import createFlash, { FLASH_TYPES } from '~/flash';
+import { createAlert, VARIANT_INFO } from '~/flash';
import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue';
import IssuableByEmail from '~/issuable/components/issuable_by_email.vue';
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/constants';
+import EmptyStateWithAnyIssues from '~/issues/list/components/empty_state_with_any_issues.vue';
+import EmptyStateWithoutAnyIssues from '~/issues/list/components/empty_state_without_any_issues.vue';
import IssuesListApp from '~/issues/list/components/issues_list_app.vue';
import NewIssueDropdown from '~/issues/list/components/new_issue_dropdown.vue';
import {
CREATED_DESC,
RELATIVE_POSITION,
RELATIVE_POSITION_ASC,
+ UPDATED_DESC,
urlSortParams,
} from '~/issues/list/constants';
import eventHub from '~/issues/list/eventhub';
@@ -58,10 +61,11 @@ import {
TOKEN_TYPE_MY_REACTION,
TOKEN_TYPE_ORGANIZATION,
TOKEN_TYPE_RELEASE,
+ TOKEN_TYPE_SEARCH_WITHIN,
TOKEN_TYPE_TYPE,
} from '~/vue_shared/components/filtered_search_bar/constants';
-import('~/issuable/bulk_update_sidebar');
+import('~/issuable');
import('~/users_select');
jest.mock('@sentry/browser');
@@ -122,10 +126,8 @@ describe('CE IssuesListApp component', () => {
const findCsvImportExportButtons = () => wrapper.findComponent(CsvImportExportButtons);
const findIssuableByEmail = () => wrapper.findComponent(IssuableByEmail);
- const findGlButton = () => wrapper.findComponent(GlButton);
const findGlButtons = () => wrapper.findAllComponents(GlButton);
const findGlButtonAt = (index) => findGlButtons().at(index);
- const findGlEmptyState = () => wrapper.findComponent(GlEmptyState);
const findIssuableList = () => wrapper.findComponent(IssuableList);
const findNewIssueDropdown = () => wrapper.findComponent(NewIssueDropdown);
@@ -182,7 +184,11 @@ describe('CE IssuesListApp component', () => {
namespace: defaultProvide.fullPath,
recentSearchesStorageKey: 'issues',
searchInputPlaceholder: IssuesListApp.i18n.searchPlaceholder,
- sortOptions: getSortOptions(true, true),
+ sortOptions: getSortOptions({
+ hasBlockedIssuesFeature: defaultProvide.hasBlockedIssuesFeature,
+ hasIssuableHealthStatusFeature: defaultProvide.hasIssuableHealthStatusFeature,
+ hasIssueWeightsFeature: defaultProvide.hasIssueWeightsFeature,
+ }),
initialSortBy: CREATED_DESC,
issuables: getIssuesQueryResponse.data.project.issues.nodes,
tabs: IssuableListTabs,
@@ -395,9 +401,9 @@ describe('CE IssuesListApp component', () => {
});
it('shows an alert to tell the user that manual reordering is disabled', () => {
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: IssuesListApp.i18n.issueRepositioningMessage,
- type: FLASH_TYPES.NOTICE,
+ variant: VARIANT_INFO,
});
});
});
@@ -435,9 +441,9 @@ describe('CE IssuesListApp component', () => {
});
it('shows an alert to tell the user they must be signed in to search', () => {
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: IssuesListApp.i18n.anonymousSearchingMessage,
- type: FLASH_TYPES.NOTICE,
+ variant: VARIANT_INFO,
});
});
});
@@ -486,136 +492,29 @@ describe('CE IssuesListApp component', () => {
describe('empty states', () => {
describe('when there are issues', () => {
- describe('when search returns no results', () => {
- beforeEach(() => {
- setWindowLocation(`?search=no+results`);
-
- wrapper = mountComponent({ provide: { hasAnyIssues: true }, mountFn: mount });
- });
-
- it('shows empty state', () => {
- expect(findGlEmptyState().props()).toMatchObject({
- description: IssuesListApp.i18n.noSearchResultsDescription,
- title: IssuesListApp.i18n.noSearchResultsTitle,
- svgPath: defaultProvide.emptyStateSvgPath,
- });
- });
- });
-
- describe('when "Open" tab has no issues', () => {
- beforeEach(() => {
- wrapper = mountComponent({ provide: { hasAnyIssues: true }, mountFn: mount });
- });
-
- it('shows empty state', () => {
- expect(findGlEmptyState().props()).toMatchObject({
- description: IssuesListApp.i18n.noOpenIssuesDescription,
- title: IssuesListApp.i18n.noOpenIssuesTitle,
- svgPath: defaultProvide.emptyStateSvgPath,
- });
- });
+ beforeEach(() => {
+ wrapper = mountComponent({ provide: { hasAnyIssues: true }, mountFn: mount });
});
- describe('when "Closed" tab has no issues', () => {
- beforeEach(() => {
- setWindowLocation(`?state=${IssuableStates.Closed}`);
-
- wrapper = mountComponent({ provide: { hasAnyIssues: true }, mountFn: mount });
- });
-
- it('shows empty state', () => {
- expect(findGlEmptyState().props()).toMatchObject({
- title: IssuesListApp.i18n.noClosedIssuesTitle,
- svgPath: defaultProvide.emptyStateSvgPath,
- });
+ it('shows EmptyStateWithAnyIssues empty state', () => {
+ expect(wrapper.findComponent(EmptyStateWithAnyIssues).props()).toEqual({
+ hasSearch: false,
+ isOpenTab: true,
});
});
});
describe('when there are no issues', () => {
- describe('when user is logged in', () => {
- beforeEach(() => {
- wrapper = mountComponent({
- provide: { hasAnyIssues: false, isSignedIn: true },
- mountFn: mount,
- });
- });
-
- it('shows empty state', () => {
- expect(findGlEmptyState().props()).toMatchObject({
- title: IssuesListApp.i18n.noIssuesSignedInTitle,
- svgPath: defaultProvide.emptyStateSvgPath,
- });
- expect(findGlEmptyState().text()).toContain(
- IssuesListApp.i18n.noIssuesSignedInDescription,
- );
- });
-
- it('shows "New issue" and import/export buttons', () => {
- expect(findGlButton().text()).toBe(IssuesListApp.i18n.newIssueLabel);
- expect(findGlButton().attributes('href')).toBe(defaultProvide.newIssuePath);
- expect(findCsvImportExportButtons().props()).toMatchObject({
- exportCsvPath: defaultProvide.exportCsvPath,
- issuableCount: 0,
- });
- });
-
- it('shows Jira integration information', () => {
- const paragraphs = wrapper.findAll('p');
- const links = wrapper.findAll('.gl-link');
- expect(paragraphs.at(1).text()).toContain(IssuesListApp.i18n.jiraIntegrationTitle);
- expect(paragraphs.at(2).text()).toContain(
- 'Enable the Jira integration to view your Jira issues in GitLab.',
- );
- expect(paragraphs.at(3).text()).toContain(
- IssuesListApp.i18n.jiraIntegrationSecondaryMessage,
- );
- expect(links.at(1).text()).toBe('Enable the Jira integration');
- expect(links.at(1).attributes('href')).toBe(defaultProvide.jiraIntegrationPath);
- });
- });
-
- describe('when user is logged in and can create projects', () => {
- beforeEach(() => {
- wrapper = mountComponent({
- provide: { canCreateProjects: true, hasAnyIssues: false, isSignedIn: true },
- stubs: { GlEmptyState },
- });
- });
-
- it('shows empty state with additional description about creating projects', () => {
- expect(findGlEmptyState().text()).toContain(
- IssuesListApp.i18n.noIssuesSignedInDescription,
- );
- expect(findGlEmptyState().text()).toContain(
- IssuesListApp.i18n.noGroupIssuesSignedInDescription,
- );
- });
-
- it('shows "New project" button', () => {
- expect(findGlButton().text()).toBe(IssuesListApp.i18n.newProjectLabel);
- expect(findGlButton().attributes('href')).toBe(defaultProvide.newProjectPath);
- });
+ beforeEach(() => {
+ wrapper = mountComponent({ provide: { hasAnyIssues: false } });
});
- describe('when user is logged out', () => {
- beforeEach(() => {
- wrapper = mountComponent({
- provide: { hasAnyIssues: false, isSignedIn: false },
- mountFn: mount,
- });
- });
-
- it('shows empty state', () => {
- expect(findGlEmptyState().props()).toMatchObject({
- title: IssuesListApp.i18n.noIssuesSignedOutTitle,
- svgPath: defaultProvide.emptyStateSvgPath,
- primaryButtonText: IssuesListApp.i18n.noIssuesSignedOutButtonText,
- primaryButtonLink: defaultProvide.signInPath,
- });
- expect(findGlEmptyState().text()).toContain(
- IssuesListApp.i18n.noIssuesSignedOutDescription,
- );
+ it('shows EmptyStateWithoutAnyIssues empty state', () => {
+ expect(wrapper.findComponent(EmptyStateWithoutAnyIssues).props()).toEqual({
+ currentTabCount: 0,
+ exportCsvPathWithQuery: defaultProvide.exportCsvPath,
+ showCsvButtons: true,
+ showNewIssueDropdown: false,
});
});
});
@@ -636,8 +535,8 @@ describe('CE IssuesListApp component', () => {
it('does not render My-Reaction or Confidential tokens', () => {
expect(findIssuableList().props('searchTokens')).not.toMatchObject([
- { type: TOKEN_TYPE_AUTHOR, preloadedAuthors: [mockCurrentUser] },
- { type: TOKEN_TYPE_ASSIGNEE, preloadedAuthors: [mockCurrentUser] },
+ { type: TOKEN_TYPE_AUTHOR, preloadedUsers: [mockCurrentUser] },
+ { type: TOKEN_TYPE_ASSIGNEE, preloadedUsers: [mockCurrentUser] },
{ type: TOKEN_TYPE_MY_REACTION },
{ type: TOKEN_TYPE_CONFIDENTIAL },
]);
@@ -685,13 +584,13 @@ describe('CE IssuesListApp component', () => {
});
it('renders all tokens alphabetically', () => {
- const preloadedAuthors = [
+ const preloadedUsers = [
{ ...mockCurrentUser, id: convertToGraphQLId('User', mockCurrentUser.id) },
];
expect(findIssuableList().props('searchTokens')).toMatchObject([
- { type: TOKEN_TYPE_ASSIGNEE, preloadedAuthors },
- { type: TOKEN_TYPE_AUTHOR, preloadedAuthors },
+ { type: TOKEN_TYPE_ASSIGNEE, preloadedUsers },
+ { type: TOKEN_TYPE_AUTHOR, preloadedUsers },
{ type: TOKEN_TYPE_CONFIDENTIAL },
{ type: TOKEN_TYPE_CONTACT },
{ type: TOKEN_TYPE_LABEL },
@@ -699,6 +598,7 @@ describe('CE IssuesListApp component', () => {
{ type: TOKEN_TYPE_MY_REACTION },
{ type: TOKEN_TYPE_ORGANIZATION },
{ type: TOKEN_TYPE_RELEASE },
+ { type: TOKEN_TYPE_SEARCH_WITHIN },
{ type: TOKEN_TYPE_TYPE },
]);
});
@@ -899,7 +799,11 @@ describe('CE IssuesListApp component', () => {
it.each(Object.keys(urlSortParams))(
'updates to the new sort when payload is `%s`',
async (sortKey) => {
- wrapper = mountComponent();
+ // Ensure initial sort key is different so we can trigger an update when emitting a sort key
+ wrapper =
+ sortKey === CREATED_DESC
+ ? mountComponent({ provide: { initialSort: UPDATED_DESC } })
+ : mountComponent();
router.push = jest.fn();
findIssuableList().vm.$emit('sort', sortKey);
@@ -929,9 +833,9 @@ describe('CE IssuesListApp component', () => {
});
it('shows an alert to tell the user that manual reordering is disabled', () => {
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: IssuesListApp.i18n.issueRepositioningMessage,
- type: FLASH_TYPES.NOTICE,
+ variant: VARIANT_INFO,
});
});
});
@@ -941,9 +845,9 @@ describe('CE IssuesListApp component', () => {
const mutationMock = jest.fn().mockResolvedValue(setSortPreferenceMutationResponse);
wrapper = mountComponent({ sortPreferenceMutationResponse: mutationMock });
- findIssuableList().vm.$emit('sort', CREATED_DESC);
+ findIssuableList().vm.$emit('sort', UPDATED_DESC);
- expect(mutationMock).toHaveBeenCalledWith({ input: { issuesSort: CREATED_DESC } });
+ expect(mutationMock).toHaveBeenCalledWith({ input: { issuesSort: UPDATED_DESC } });
});
it('captures error when mutation response has errors', async () => {
@@ -952,7 +856,7 @@ describe('CE IssuesListApp component', () => {
.mockResolvedValue(setSortPreferenceMutationResponseWithErrors);
wrapper = mountComponent({ sortPreferenceMutationResponse: mutationMock });
- findIssuableList().vm.$emit('sort', CREATED_DESC);
+ findIssuableList().vm.$emit('sort', UPDATED_DESC);
await waitForPromises();
expect(Sentry.captureException).toHaveBeenCalledWith(new Error('oh no!'));
@@ -1016,9 +920,9 @@ describe('CE IssuesListApp component', () => {
});
it('shows an alert to tell the user they must be signed in to search', () => {
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: IssuesListApp.i18n.anonymousSearchingMessage,
- type: FLASH_TYPES.NOTICE,
+ variant: VARIANT_INFO,
});
});
});
diff --git a/spec/frontend/issues/list/mock_data.js b/spec/frontend/issues/list/mock_data.js
index 62fcbf7aad0..0690501dee9 100644
--- a/spec/frontend/issues/list/mock_data.js
+++ b/spec/frontend/issues/list/mock_data.js
@@ -1,7 +1,7 @@
import {
FILTERED_SEARCH_TERM,
OPERATOR_IS,
- OPERATOR_IS_NOT,
+ OPERATOR_NOT,
OPERATOR_OR,
TOKEN_TYPE_ASSIGNEE,
TOKEN_TYPE_AUTHOR,
@@ -132,6 +132,8 @@ export const locationSearch = [
'?search=find+issues',
'author_username=homer',
'not[author_username]=marge',
+ 'or[author_username]=burns',
+ 'or[author_username]=smithers',
'assignee_username[]=bart',
'assignee_username[]=lisa',
'assignee_username[]=5',
@@ -184,41 +186,43 @@ export const locationSearchWithSpecialValues = [
export const filteredTokens = [
{ type: TOKEN_TYPE_AUTHOR, value: { data: 'homer', operator: OPERATOR_IS } },
- { type: TOKEN_TYPE_AUTHOR, value: { data: 'marge', operator: OPERATOR_IS_NOT } },
+ { type: TOKEN_TYPE_AUTHOR, value: { data: 'marge', operator: OPERATOR_NOT } },
+ { type: TOKEN_TYPE_AUTHOR, value: { data: 'burns', operator: OPERATOR_OR } },
+ { type: TOKEN_TYPE_AUTHOR, value: { data: 'smithers', operator: OPERATOR_OR } },
{ type: TOKEN_TYPE_ASSIGNEE, value: { data: 'bart', operator: OPERATOR_IS } },
{ type: TOKEN_TYPE_ASSIGNEE, value: { data: 'lisa', operator: OPERATOR_IS } },
{ type: TOKEN_TYPE_ASSIGNEE, value: { data: '5', operator: OPERATOR_IS } },
- { type: TOKEN_TYPE_ASSIGNEE, value: { data: 'patty', operator: OPERATOR_IS_NOT } },
- { type: TOKEN_TYPE_ASSIGNEE, value: { data: 'selma', operator: OPERATOR_IS_NOT } },
+ { type: TOKEN_TYPE_ASSIGNEE, value: { data: 'patty', operator: OPERATOR_NOT } },
+ { type: TOKEN_TYPE_ASSIGNEE, value: { data: 'selma', operator: OPERATOR_NOT } },
{ type: TOKEN_TYPE_ASSIGNEE, value: { data: 'carl', operator: OPERATOR_OR } },
{ type: TOKEN_TYPE_ASSIGNEE, value: { data: 'lenny', operator: OPERATOR_OR } },
{ type: TOKEN_TYPE_MILESTONE, value: { data: 'season 3', operator: OPERATOR_IS } },
{ type: TOKEN_TYPE_MILESTONE, value: { data: 'season 4', operator: OPERATOR_IS } },
- { type: TOKEN_TYPE_MILESTONE, value: { data: 'season 20', operator: OPERATOR_IS_NOT } },
- { type: TOKEN_TYPE_MILESTONE, value: { data: 'season 30', operator: OPERATOR_IS_NOT } },
+ { type: TOKEN_TYPE_MILESTONE, value: { data: 'season 20', operator: OPERATOR_NOT } },
+ { type: TOKEN_TYPE_MILESTONE, value: { data: 'season 30', operator: OPERATOR_NOT } },
{ type: TOKEN_TYPE_LABEL, value: { data: 'cartoon', operator: OPERATOR_IS } },
{ type: TOKEN_TYPE_LABEL, value: { data: 'tv', operator: OPERATOR_IS } },
- { type: TOKEN_TYPE_LABEL, value: { data: 'live action', operator: OPERATOR_IS_NOT } },
- { type: TOKEN_TYPE_LABEL, value: { data: 'drama', operator: OPERATOR_IS_NOT } },
+ { type: TOKEN_TYPE_LABEL, value: { data: 'live action', operator: OPERATOR_NOT } },
+ { type: TOKEN_TYPE_LABEL, value: { data: 'drama', operator: OPERATOR_NOT } },
{ type: TOKEN_TYPE_RELEASE, value: { data: 'v3', operator: OPERATOR_IS } },
{ type: TOKEN_TYPE_RELEASE, value: { data: 'v4', operator: OPERATOR_IS } },
- { type: TOKEN_TYPE_RELEASE, value: { data: 'v20', operator: OPERATOR_IS_NOT } },
- { type: TOKEN_TYPE_RELEASE, value: { data: 'v30', operator: OPERATOR_IS_NOT } },
+ { type: TOKEN_TYPE_RELEASE, value: { data: 'v20', operator: OPERATOR_NOT } },
+ { type: TOKEN_TYPE_RELEASE, value: { data: 'v30', operator: OPERATOR_NOT } },
{ type: TOKEN_TYPE_TYPE, value: { data: 'issue', operator: OPERATOR_IS } },
{ type: TOKEN_TYPE_TYPE, value: { data: 'feature', operator: OPERATOR_IS } },
- { type: TOKEN_TYPE_TYPE, value: { data: 'bug', operator: OPERATOR_IS_NOT } },
- { type: TOKEN_TYPE_TYPE, value: { data: 'incident', operator: OPERATOR_IS_NOT } },
+ { type: TOKEN_TYPE_TYPE, value: { data: 'bug', operator: OPERATOR_NOT } },
+ { type: TOKEN_TYPE_TYPE, value: { data: 'incident', operator: OPERATOR_NOT } },
{ type: TOKEN_TYPE_MY_REACTION, value: { data: 'thumbsup', operator: OPERATOR_IS } },
- { type: TOKEN_TYPE_MY_REACTION, value: { data: 'thumbsdown', operator: OPERATOR_IS_NOT } },
+ { type: TOKEN_TYPE_MY_REACTION, value: { data: 'thumbsdown', operator: OPERATOR_NOT } },
{ type: TOKEN_TYPE_CONFIDENTIAL, value: { data: 'yes', operator: OPERATOR_IS } },
{ type: TOKEN_TYPE_ITERATION, value: { data: '4', operator: OPERATOR_IS } },
{ type: TOKEN_TYPE_ITERATION, value: { data: '12', operator: OPERATOR_IS } },
- { type: TOKEN_TYPE_ITERATION, value: { data: '20', operator: OPERATOR_IS_NOT } },
- { type: TOKEN_TYPE_ITERATION, value: { data: '42', operator: OPERATOR_IS_NOT } },
+ { type: TOKEN_TYPE_ITERATION, value: { data: '20', operator: OPERATOR_NOT } },
+ { type: TOKEN_TYPE_ITERATION, value: { data: '42', operator: OPERATOR_NOT } },
{ type: TOKEN_TYPE_EPIC, value: { data: '12', operator: OPERATOR_IS } },
- { type: TOKEN_TYPE_EPIC, value: { data: '34', operator: OPERATOR_IS_NOT } },
+ { type: TOKEN_TYPE_EPIC, value: { data: '34', operator: OPERATOR_NOT } },
{ type: TOKEN_TYPE_WEIGHT, value: { data: '1', operator: OPERATOR_IS } },
- { type: TOKEN_TYPE_WEIGHT, value: { data: '3', operator: OPERATOR_IS_NOT } },
+ { type: TOKEN_TYPE_WEIGHT, value: { data: '3', operator: OPERATOR_NOT } },
{ type: TOKEN_TYPE_CONTACT, value: { data: '123', operator: OPERATOR_IS } },
{ type: TOKEN_TYPE_ORGANIZATION, value: { data: '456', operator: OPERATOR_IS } },
{ type: FILTERED_SEARCH_TERM, value: { data: 'find' } },
@@ -264,6 +268,7 @@ export const apiParams = {
weight: '3',
},
or: {
+ authorUsernames: ['burns', 'smithers'],
assigneeUsernames: ['carl', 'lenny'],
},
};
@@ -283,6 +288,7 @@ export const apiParamsWithSpecialValues = {
export const urlParams = {
author_username: 'homer',
'not[author_username]': 'marge',
+ 'or[author_username]': ['burns', 'smithers'],
'assignee_username[]': ['bart', 'lisa', '5'],
'not[assignee_username][]': ['patty', 'selma'],
'or[assignee_username][]': ['carl', 'lenny'],
diff --git a/spec/frontend/issues/list/utils_spec.js b/spec/frontend/issues/list/utils_spec.js
index 3c6332d5728..a281ed1c989 100644
--- a/spec/frontend/issues/list/utils_spec.js
+++ b/spec/frontend/issues/list/utils_spec.js
@@ -69,26 +69,40 @@ describe('isSortKey', () => {
describe('getSortOptions', () => {
describe.each`
- hasIssueWeightsFeature | hasBlockedIssuesFeature | length | containsWeight | containsBlocking
- ${false} | ${false} | ${10} | ${false} | ${false}
- ${true} | ${false} | ${11} | ${true} | ${false}
- ${false} | ${true} | ${11} | ${false} | ${true}
- ${true} | ${true} | ${12} | ${true} | ${true}
+ hasIssuableHealthStatusFeature | hasIssueWeightsFeature | hasBlockedIssuesFeature | length | containsHealthStatus | containsWeight | containsBlocking
+ ${false} | ${false} | ${false} | ${10} | ${false} | ${false} | ${false}
+ ${false} | ${false} | ${true} | ${11} | ${false} | ${false} | ${true}
+ ${false} | ${true} | ${false} | ${11} | ${false} | ${true} | ${false}
+ ${false} | ${true} | ${true} | ${12} | ${false} | ${true} | ${true}
+ ${true} | ${false} | ${false} | ${11} | ${true} | ${false} | ${false}
+ ${true} | ${false} | ${true} | ${12} | ${true} | ${false} | ${true}
+ ${true} | ${true} | ${false} | ${12} | ${true} | ${true} | ${false}
+ ${true} | ${true} | ${true} | ${13} | ${true} | ${true} | ${true}
`(
- 'when hasIssueWeightsFeature=$hasIssueWeightsFeature and hasBlockedIssuesFeature=$hasBlockedIssuesFeature',
+ 'when hasIssuableHealthStatusFeature=$hasIssuableHealthStatusFeature, hasIssueWeightsFeature=$hasIssueWeightsFeature and hasBlockedIssuesFeature=$hasBlockedIssuesFeature',
({
+ hasIssuableHealthStatusFeature,
hasIssueWeightsFeature,
hasBlockedIssuesFeature,
length,
+ containsHealthStatus,
containsWeight,
containsBlocking,
}) => {
- const sortOptions = getSortOptions(hasIssueWeightsFeature, hasBlockedIssuesFeature);
+ const sortOptions = getSortOptions({
+ hasBlockedIssuesFeature,
+ hasIssuableHealthStatusFeature,
+ hasIssueWeightsFeature,
+ });
it('returns the correct length of sort options', () => {
expect(sortOptions).toHaveLength(length);
});
+ it(`${containsHealthStatus ? 'contains' : 'does not contain'} health status option`, () => {
+ expect(sortOptions.some((option) => option.title === 'Health')).toBe(containsHealthStatus);
+ });
+
it(`${containsWeight ? 'contains' : 'does not contain'} weight option`, () => {
expect(sortOptions.some((option) => option.title === 'Weight')).toBe(containsWeight);
});
diff --git a/spec/frontend/issues/related_merge_requests/store/actions_spec.js b/spec/frontend/issues/related_merge_requests/store/actions_spec.js
index 4327fac15d4..d3ec6c3bc9d 100644
--- a/spec/frontend/issues/related_merge_requests/store/actions_spec.js
+++ b/spec/frontend/issues/related_merge_requests/store/actions_spec.js
@@ -1,6 +1,6 @@
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import * as actions from '~/issues/related_merge_requests/store/actions';
import * as types from '~/issues/related_merge_requests/store/mutation_types';
@@ -95,8 +95,8 @@ describe('RelatedMergeRequest store actions', () => {
[],
[{ type: 'requestData' }, { type: 'receiveDataError' }],
);
- expect(createFlash).toHaveBeenCalledTimes(1);
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalledWith({
message: expect.stringMatching('Something went wrong'),
});
});
diff --git a/spec/frontend/issues/show/components/app_spec.js b/spec/frontend/issues/show/components/app_spec.js
index 3d027e2084c..6cf44e60092 100644
--- a/spec/frontend/issues/show/components/app_spec.js
+++ b/spec/frontend/issues/show/components/app_spec.js
@@ -5,7 +5,7 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import '~/behaviors/markdown/render_gfm';
+import { createAlert } from '~/flash';
import { IssuableStatus, IssuableStatusText, IssuableType } from '~/issues/constants';
import IssuableApp from '~/issues/show/components/app.vue';
import DescriptionComponent from '~/issues/show/components/description.vue';
@@ -26,8 +26,10 @@ import {
zoomMeetingUrl,
} from '../mock_data/mock_data';
-jest.mock('~/lib/utils/url_utility');
+jest.mock('~/flash');
jest.mock('~/issues/show/event_hub');
+jest.mock('~/lib/utils/url_utility');
+jest.mock('~/behaviors/markdown/render_gfm');
const REALTIME_REQUEST_STACK = [initialRequest, secondRequest];
@@ -270,9 +272,7 @@ describe('Issuable output', () => {
await wrapper.vm.updateIssuable();
expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form');
- expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
- `Error updating issue`,
- );
+ expect(createAlert).toHaveBeenCalledWith({ message: `Error updating issue` });
});
it('returns the correct error message for issuableType', async () => {
@@ -282,9 +282,7 @@ describe('Issuable output', () => {
await nextTick();
await wrapper.vm.updateIssuable();
expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form');
- expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
- `Error updating merge request`,
- );
+ expect(createAlert).toHaveBeenCalledWith({ message: `Error updating merge request` });
});
it('shows error message from backend if exists', async () => {
@@ -294,9 +292,9 @@ describe('Issuable output', () => {
.mockRejectedValue({ response: { data: { errors: [msg] } } });
await wrapper.vm.updateIssuable();
- expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
- `${wrapper.vm.defaultErrorMessage}. ${msg}`,
- );
+ expect(createAlert).toHaveBeenCalledWith({
+ message: `${wrapper.vm.defaultErrorMessage}. ${msg}`,
+ });
});
});
});
@@ -354,9 +352,7 @@ describe('Issuable output', () => {
.reply(() => Promise.reject(new Error('something went wrong')));
return wrapper.vm.requestTemplatesAndShowForm().then(() => {
- expect(document.querySelector('.flash-container .flash-text').textContent).toContain(
- 'Error updating issue',
- );
+ expect(createAlert).toHaveBeenCalledWith({ message: 'Error updating issue' });
expect(formSpy).toHaveBeenCalledWith();
});
@@ -402,9 +398,9 @@ describe('Issuable output', () => {
wrapper.setProps({ issuableType: 'merge request' });
return wrapper.vm.updateStoreState().then(() => {
- expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
- `Error updating ${wrapper.vm.issuableType}`,
- );
+ expect(createAlert).toHaveBeenCalledWith({
+ message: `Error updating ${wrapper.vm.issuableType}`,
+ });
});
});
});
diff --git a/spec/frontend/issues/show/components/description_spec.js b/spec/frontend/issues/show/components/description_spec.js
index 9d9abce887b..889ff450825 100644
--- a/spec/frontend/issues/show/components/description_spec.js
+++ b/spec/frontend/issues/show/components/description_spec.js
@@ -1,7 +1,6 @@
import $ from 'jquery';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
-import '~/behaviors/markdown/render_gfm';
import { GlTooltip, GlModal } from '@gitlab/ui';
import setWindowLocation from 'helpers/set_window_location_helper';
@@ -12,7 +11,7 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import Description from '~/issues/show/components/description.vue';
import { updateHistory } from '~/lib/utils/url_utility';
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
@@ -21,6 +20,7 @@ import createWorkItemFromTaskMutation from '~/work_items/graphql/create_work_ite
import TaskList from '~/task_list';
import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
import { TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import {
projectWorkItemTypesQueryResponse,
createWorkItemFromTaskMutationResponse,
@@ -37,6 +37,7 @@ jest.mock('~/lib/utils/url_utility', () => ({
updateHistory: jest.fn(),
}));
jest.mock('~/task_list');
+jest.mock('~/behaviors/markdown/render_gfm');
const showModal = jest.fn();
const hideModal = jest.fn();
@@ -161,7 +162,6 @@ describe('Description component', () => {
});
it('applies syntax highlighting and math when description changed', async () => {
- const prototypeSpy = jest.spyOn($.prototype, 'renderGFM');
createComponent();
await wrapper.setProps({
@@ -169,7 +169,7 @@ describe('Description component', () => {
});
expect(findGfmContent().exists()).toBe(true);
- expect(prototypeSpy).toHaveBeenCalled();
+ expect(renderGFM).toHaveBeenCalled();
});
it('sets data-update-url', () => {
@@ -370,7 +370,7 @@ describe('Description component', () => {
await waitForPromises();
- expect(createFlash).toHaveBeenCalledWith(
+ expect(createAlert).toHaveBeenCalledWith(
expect.objectContaining({
message: 'Something went wrong when creating task. Please try again.',
}),
diff --git a/spec/frontend/issues/show/components/header_actions_spec.js b/spec/frontend/issues/show/components/header_actions_spec.js
index dc2b3c6fc48..7d6ca44e679 100644
--- a/spec/frontend/issues/show/components/header_actions_spec.js
+++ b/spec/frontend/issues/show/components/header_actions_spec.js
@@ -3,7 +3,7 @@ import { GlButton, GlDropdownItem, GlLink, GlModal } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import { mockTracking } from 'helpers/tracking_helper';
-import createFlash, { FLASH_TYPES } from '~/flash';
+import { createAlert, VARIANT_SUCCESS } from '~/flash';
import { IssuableStatus, IssueType } from '~/issues/constants';
import DeleteIssueModal from '~/issues/show/components/delete_issue_modal.vue';
import HeaderActions from '~/issues/show/components/header_actions.vue';
@@ -171,19 +171,19 @@ describe('HeaderActions component', () => {
${'desktop dropdown'} | ${false} | ${findDesktopDropdownItems} | ${findDesktopDropdown}
`('$description', ({ isCloseIssueItemVisible, findDropdownItems, findDropdown }) => {
describe.each`
- description | itemText | isItemVisible | canUpdateIssue | canCreateIssue | isIssueAuthor | canReportSpam | canPromoteToEpic | canDestroyIssue
- ${`when user can update ${issueType}`} | ${`Close ${issueType}`} | ${isCloseIssueItemVisible} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true}
- ${`when user cannot update ${issueType}`} | ${`Close ${issueType}`} | ${false} | ${false} | ${true} | ${true} | ${true} | ${true} | ${true}
- ${`when user can create ${issueType}`} | ${`New related ${issueType}`} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true}
- ${`when user cannot create ${issueType}`} | ${`New related ${issueType}`} | ${false} | ${true} | ${false} | ${true} | ${true} | ${true} | ${true}
- ${'when user can promote to epic'} | ${'Promote to epic'} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true}
- ${'when user cannot promote to epic'} | ${'Promote to epic'} | ${false} | ${true} | ${true} | ${true} | ${true} | ${false} | ${true}
- ${'when user can report abuse'} | ${'Report abuse'} | ${true} | ${true} | ${true} | ${false} | ${true} | ${true} | ${true}
- ${'when user cannot report abuse'} | ${'Report abuse'} | ${false} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true}
- ${'when user can submit as spam'} | ${'Submit as spam'} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true}
- ${'when user cannot submit as spam'} | ${'Submit as spam'} | ${false} | ${true} | ${true} | ${true} | ${false} | ${true} | ${true}
- ${`when user can delete ${issueType}`} | ${`Delete ${issueType}`} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true}
- ${`when user cannot delete ${issueType}`} | ${`Delete ${issueType}`} | ${false} | ${true} | ${true} | ${true} | ${true} | ${true} | ${false}
+ description | itemText | isItemVisible | canUpdateIssue | canCreateIssue | isIssueAuthor | canReportSpam | canPromoteToEpic | canDestroyIssue
+ ${`when user can update ${issueType}`} | ${`Close ${issueType}`} | ${isCloseIssueItemVisible} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true}
+ ${`when user cannot update ${issueType}`} | ${`Close ${issueType}`} | ${false} | ${false} | ${true} | ${true} | ${true} | ${true} | ${true}
+ ${`when user can create ${issueType}`} | ${`New related ${issueType}`} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true}
+ ${`when user cannot create ${issueType}`} | ${`New related ${issueType}`} | ${false} | ${true} | ${false} | ${true} | ${true} | ${true} | ${true}
+ ${'when user can promote to epic'} | ${'Promote to epic'} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true}
+ ${'when user cannot promote to epic'} | ${'Promote to epic'} | ${false} | ${true} | ${true} | ${true} | ${true} | ${false} | ${true}
+ ${'when user can report abuse'} | ${'Report abuse to administrator'} | ${true} | ${true} | ${true} | ${false} | ${true} | ${true} | ${true}
+ ${'when user cannot report abuse'} | ${'Report abuse to administrator'} | ${false} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true}
+ ${'when user can submit as spam'} | ${'Submit as spam'} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true}
+ ${'when user cannot submit as spam'} | ${'Submit as spam'} | ${false} | ${true} | ${true} | ${true} | ${false} | ${true} | ${true}
+ ${`when user can delete ${issueType}`} | ${`Delete ${issueType}`} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true}
+ ${`when user cannot delete ${issueType}`} | ${`Delete ${issueType}`} | ${false} | ${true} | ${true} | ${true} | ${true} | ${true} | ${false}
`(
'$description',
({
@@ -284,9 +284,9 @@ describe('HeaderActions component', () => {
});
it('shows a success message and tells the user they are being redirected', () => {
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: 'The issue was successfully promoted to an epic. Redirecting to epic...',
- type: FLASH_TYPES.SUCCESS,
+ variant: VARIANT_SUCCESS,
});
});
@@ -309,7 +309,7 @@ describe('HeaderActions component', () => {
});
it('shows an error message', () => {
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: HeaderActions.i18n.promoteErrorMessage,
});
});
diff --git a/spec/frontend/issues/show/components/incidents/edit_timeline_event_spec.js b/spec/frontend/issues/show/components/incidents/edit_timeline_event_spec.js
index 4c1638a9147..81c3c30bf8a 100644
--- a/spec/frontend/issues/show/components/incidents/edit_timeline_event_spec.js
+++ b/spec/frontend/issues/show/components/incidents/edit_timeline_event_spec.js
@@ -40,5 +40,13 @@ describe('Edit Timeline events', () => {
expect(wrapper.emitted()).toEqual(cancelEvent);
});
+
+ it('should emit the delete event', async () => {
+ const deleteEvent = { delete: [[]] };
+
+ await findTimelineEventsForm().vm.$emit('delete');
+
+ expect(wrapper.emitted()).toEqual(deleteEvent);
+ });
});
});
diff --git a/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js b/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js
index 458c1c3f858..33a3a6eddfc 100644
--- a/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js
+++ b/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js
@@ -1,10 +1,11 @@
-import { GlTab } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
import merge from 'lodash/merge';
+import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { trackIncidentDetailsViewsOptions } from '~/incidents/constants';
import DescriptionComponent from '~/issues/show/components/description.vue';
import HighlightBar from '~/issues/show/components/incidents/highlight_bar.vue';
-import IncidentTabs from '~/issues/show/components/incidents/incident_tabs.vue';
+import IncidentTabs, {
+ incidentTabsI18n,
+} from '~/issues/show/components/incidents/incident_tabs.vue';
import INVALID_URL from '~/lib/utils/invalid_url';
import Tracking from '~/tracking';
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
@@ -16,11 +17,24 @@ const mockAlert = {
iid: '1',
};
+const defaultMocks = {
+ $apollo: {
+ queries: {
+ alert: {
+ loading: true,
+ },
+ timelineEvents: {
+ loading: false,
+ },
+ },
+ },
+};
+
describe('Incident Tabs component', () => {
let wrapper;
- const mountComponent = (data = {}, options = {}) => {
- wrapper = shallowMount(
+ const mountComponent = ({ data = {}, options = {}, mount = shallowMountExtended } = {}) => {
+ wrapper = mount(
IncidentTabs,
merge(
{
@@ -29,7 +43,7 @@ describe('Incident Tabs component', () => {
},
stubs: {
DescriptionComponent: true,
- MetricsTab: true,
+ IncidentMetricTab: true,
},
provide: {
fullPath: '',
@@ -37,41 +51,37 @@ describe('Incident Tabs component', () => {
projectId: '',
issuableId: '',
uploadMetricsFeatureAvailable: true,
+ slaFeatureAvailable: true,
+ canUpdate: true,
+ canUpdateTimelineEvent: true,
},
data() {
return { alert: mockAlert, ...data };
},
- mocks: {
- $apollo: {
- queries: {
- alert: {
- loading: true,
- },
- timelineEvents: {
- loading: false,
- },
- },
- },
- },
+ mocks: defaultMocks,
},
options,
),
);
};
- const findTabs = () => wrapper.findAllComponents(GlTab);
- const findSummaryTab = () => findTabs().at(0);
- const findAlertDetailsTab = () => wrapper.find('[data-testid="alert-details-tab"]');
+ const findSummaryTab = () => wrapper.findByTestId('summary-tab');
+ const findTimelineTab = () => wrapper.findByTestId('timeline-tab');
+ const findAlertDetailsTab = () => wrapper.findByTestId('alert-details-tab');
const findAlertDetailsComponent = () => wrapper.findComponent(AlertDetailsTable);
const findDescriptionComponent = () => wrapper.findComponent(DescriptionComponent);
const findHighlightBarComponent = () => wrapper.findComponent(HighlightBar);
+ const findTabButtonByFilter = (filter) => wrapper.findAllByRole('tab').filter(filter);
+ const findTimelineTabButton = () =>
+ findTabButtonByFilter((inner) => inner.text() === incidentTabsI18n.timelineTitle).at(0);
+ const findActiveTabs = () => findTabButtonByFilter((inner) => inner.classes('active'));
- describe('empty state', () => {
+ describe('with no alerts', () => {
beforeEach(() => {
- mountComponent({ alert: null });
+ mountComponent({ data: { alert: null } });
});
- it('does not show the alert details tab', () => {
+ it('does not show the alert details tab option', () => {
expect(findAlertDetailsComponent().exists()).toBe(false);
});
});
@@ -83,7 +93,12 @@ describe('Incident Tabs component', () => {
it('renders the summary tab', () => {
expect(findSummaryTab().exists()).toBe(true);
- expect(findSummaryTab().attributes('title')).toBe('Summary');
+ expect(findSummaryTab().attributes('title')).toBe(incidentTabsI18n.summaryTitle);
+ });
+
+ it('renders the timeline tab', () => {
+ expect(findTimelineTab().exists()).toBe(true);
+ expect(findTimelineTab().attributes('title')).toBe(incidentTabsI18n.timelineTitle);
});
it('renders the alert details tab', () => {
@@ -125,4 +140,22 @@ describe('Incident Tabs component', () => {
expect(Tracking.event).toHaveBeenCalledWith(category, action);
});
});
+
+ describe('tab changing', () => {
+ beforeEach(() => {
+ mountComponent({ mount: mountExtended });
+ });
+
+ it('shows only the summary tab by default', async () => {
+ expect(findActiveTabs()).toHaveLength(1);
+ expect(findActiveTabs().at(0).text()).toBe(incidentTabsI18n.summaryTitle);
+ });
+
+ it("shows the timeline tab after it's clicked", async () => {
+ await findTimelineTabButton().trigger('click');
+
+ expect(findActiveTabs()).toHaveLength(1);
+ expect(findActiveTabs().at(0).text()).toBe(incidentTabsI18n.timelineTitle);
+ });
+ });
});
diff --git a/spec/frontend/issues/show/components/incidents/mock_data.js b/spec/frontend/issues/show/components/incidents/mock_data.js
index adea2b6df59..9accfcea791 100644
--- a/spec/frontend/issues/show/components/incidents/mock_data.js
+++ b/spec/frontend/issues/show/components/incidents/mock_data.js
@@ -13,6 +13,9 @@ export const mockEvents = [
noteHtml: '<p>Dummy event 1</p>',
occurredAt: '2022-03-22T15:59:00Z',
updatedAt: '2022-03-22T15:59:08Z',
+ timelineEventTags: {
+ nodes: [],
+ },
__typename: 'TimelineEventType',
},
{
@@ -29,6 +32,18 @@ export const mockEvents = [
noteHtml: '<p>Dummy event 2</p>',
occurredAt: '2022-03-23T14:57:00Z',
updatedAt: '2022-03-23T14:57:08Z',
+ timelineEventTags: {
+ nodes: [
+ {
+ id: 'gid://gitlab/IncidentManagement::TimelineEvent/132',
+ name: 'Start time',
+ },
+ {
+ id: 'gid://gitlab/IncidentManagement::TimelineEvent/132',
+ name: 'End time',
+ },
+ ],
+ },
__typename: 'TimelineEventType',
},
{
@@ -45,6 +60,9 @@ export const mockEvents = [
noteHtml: '<p>Dummy event 3</p>',
occurredAt: '2022-03-23T15:59:00Z',
updatedAt: '2022-03-23T15:59:08Z',
+ timelineEventTags: {
+ nodes: [],
+ },
__typename: 'TimelineEventType',
},
];
@@ -152,6 +170,9 @@ export const mockGetTimelineData = {
action: 'comment',
occurredAt: '2022-07-01T12:47:00Z',
createdAt: '2022-07-20T12:47:40Z',
+ timelineEventTags: {
+ nodes: [],
+ },
},
],
},
diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js
index 0ce3f75f576..d5b199cc790 100644
--- a/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js
+++ b/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js
@@ -22,11 +22,12 @@ describe('Timeline events form', () => {
useFakeDate(fakeDate);
let wrapper;
- const mountComponent = ({ mountMethod = shallowMountExtended } = {}) => {
+ const mountComponent = ({ mountMethod = shallowMountExtended } = {}, props = {}) => {
wrapper = mountMethod(TimelineEventsForm, {
propsData: {
showSaveAndAdd: true,
isEventProcessed: false,
+ ...props,
},
stubs: {
GlButton: true,
@@ -43,6 +44,7 @@ describe('Timeline events form', () => {
const findSubmitButton = () => wrapper.findByText(timelineFormI18n.save);
const findSubmitAndAddButton = () => wrapper.findByText(timelineFormI18n.saveAndAdd);
const findCancelButton = () => wrapper.findByText(timelineFormI18n.cancel);
+ const findDeleteButton = () => wrapper.findByText(timelineFormI18n.delete);
const findDatePicker = () => wrapper.findComponent(GlDatepicker);
const findHourInput = () => wrapper.findByTestId('input-hours');
const findMinuteInput = () => wrapper.findByTestId('input-minutes');
@@ -68,6 +70,9 @@ describe('Timeline events form', () => {
findCancelButton().vm.$emit('click');
await waitForPromises();
};
+ const deleteForm = () => {
+ findDeleteButton().vm.$emit('click');
+ };
it('renders markdown-field component with correct list of toolbar items', () => {
mountComponent({ mountMethod: mountExtended });
@@ -165,4 +170,38 @@ describe('Timeline events form', () => {
expect(findSubmitAndAddButton().props('disabled')).toBe(true);
});
});
+
+ describe('Delete button', () => {
+ it('does not show the delete button if showDelete prop is false', () => {
+ mountComponent({ mountMethod: mountExtended }, { showDelete: false });
+
+ expect(findDeleteButton().exists()).toBe(false);
+ });
+
+ it('shows the delete button if showDelete prop is true', () => {
+ mountComponent({ mountMethod: mountExtended }, { showDelete: true });
+
+ expect(findDeleteButton().exists()).toBe(true);
+ });
+
+ it('disables the delete button if isEventProcessed prop is true', () => {
+ mountComponent({ mountMethod: mountExtended }, { showDelete: true, isEventProcessed: true });
+
+ expect(findDeleteButton().props('disabled')).toBe(true);
+ });
+
+ it('does not disable the delete button if isEventProcessed prop is false', () => {
+ mountComponent({ mountMethod: mountExtended }, { showDelete: true, isEventProcessed: false });
+
+ expect(findDeleteButton().props('disabled')).toBe(false);
+ });
+
+ it('emits delete event on click', () => {
+ mountComponent({ mountMethod: mountExtended }, { showDelete: true, isEventProcessed: true });
+
+ deleteForm();
+
+ expect(wrapper.emitted('delete')).toEqual([[]]);
+ });
+ });
});
diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js
index 1bf8d68efd4..ba0527e5395 100644
--- a/spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js
+++ b/spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js
@@ -1,5 +1,5 @@
import timezoneMock from 'timezone-mock';
-import { GlIcon, GlDropdown } from '@gitlab/ui';
+import { GlIcon, GlDropdown, GlBadge } from '@gitlab/ui';
import { nextTick } from 'vue';
import { timelineItemI18n } from '~/issues/show/components/incidents/constants';
import { mountExtended } from 'helpers/vue_test_utils_helper';
@@ -27,25 +27,24 @@ describe('IncidentTimelineEventList', () => {
const findCommentIcon = () => wrapper.findComponent(GlIcon);
const findEventTime = () => wrapper.findByTestId('event-time');
+ const findEventTag = () => wrapper.findComponent(GlBadge);
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findDeleteButton = () => wrapper.findByText(timelineItemI18n.delete);
describe('template', () => {
- it('shows comment icon', () => {
+ beforeEach(() => {
mountComponent();
+ });
+ it('shows comment icon', () => {
expect(findCommentIcon().exists()).toBe(true);
});
it('sets correct props for icon', () => {
- mountComponent();
-
expect(findCommentIcon().props('name')).toBe(mockEvents[0].action);
});
it('displays the correct time', () => {
- mountComponent();
-
expect(findEventTime().text()).toBe('15:59 UTC');
});
@@ -58,8 +57,6 @@ describe('IncidentTimelineEventList', () => {
describe(timezone, () => {
beforeEach(() => {
timezoneMock.register(timezone);
-
- mountComponent();
});
afterEach(() => {
@@ -72,10 +69,20 @@ describe('IncidentTimelineEventList', () => {
});
});
+ describe('timeline event tag', () => {
+ it('does not show when tag is not provided', () => {
+ expect(findEventTag().exists()).toBe(false);
+ });
+
+ it('shows when tag is provided', () => {
+ mountComponent({ propsData: { eventTag: 'Start time' } });
+
+ expect(findEventTag().exists()).toBe(true);
+ });
+ });
+
describe('action dropdown', () => {
it('does not show the action dropdown by default', () => {
- mountComponent();
-
expect(findDropdown().exists()).toBe(false);
expect(findDeleteButton().exists()).toBe(false);
});
diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_list_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_list_spec.js
index dff1c429d07..a7250e8ad0d 100644
--- a/spec/frontend/issues/show/components/incidents/timeline_events_list_spec.js
+++ b/spec/frontend/issues/show/components/incidents/timeline_events_list_spec.js
@@ -92,6 +92,9 @@ describe('IncidentTimelineEventList', () => {
expect(findItems().at(1).props('occurredAt')).toBe(mockEvents[1].occurredAt);
expect(findItems().at(1).props('action')).toBe(mockEvents[1].action);
expect(findItems().at(1).props('noteHtml')).toBe(mockEvents[1].noteHtml);
+ expect(findItems().at(1).props('eventTag')).toBe(
+ mockEvents[1].timelineEventTags.nodes[0].name,
+ );
});
it('formats dates correctly', () => {
@@ -120,6 +123,20 @@ describe('IncidentTimelineEventList', () => {
});
});
+ describe('getFirstTag', () => {
+ it('returns undefined, when timelineEventTags contains an empty array', () => {
+ const returnedTag = wrapper.vm.getFirstTag(mockEvents[0].timelineEventTags);
+
+ expect(returnedTag).toEqual(undefined);
+ });
+
+ it('returns the first string, when timelineEventTags contains array with at least one tag', () => {
+ const returnedTag = wrapper.vm.getFirstTag(mockEvents[1].timelineEventTags);
+
+ expect(returnedTag).toBe(mockEvents[1].timelineEventTags.nodes[0].name);
+ });
+ });
+
describe('delete functionality', () => {
beforeEach(() => {
mockConfirmAction({ confirmed: true });
diff --git a/spec/frontend/issues/show/components/locked_warning_spec.js b/spec/frontend/issues/show/components/locked_warning_spec.js
new file mode 100644
index 00000000000..08f0338d41b
--- /dev/null
+++ b/spec/frontend/issues/show/components/locked_warning_spec.js
@@ -0,0 +1,55 @@
+import { GlAlert, GlLink } from '@gitlab/ui';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { sprintf } from '~/locale';
+import { IssuableType } from '~/issues/constants';
+import LockedWarning, { i18n } from '~/issues/show/components/locked_warning.vue';
+
+describe('LockedWarning component', () => {
+ let wrapper;
+
+ const createComponent = (props = {}) => {
+ wrapper = mountExtended(LockedWarning, {
+ propsData: props,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findLink = () => wrapper.findComponent(GlLink);
+
+ describe.each([IssuableType.Issue, IssuableType.Epic])(
+ 'with issuableType set to %s',
+ (issuableType) => {
+ let alert;
+ let link;
+ beforeEach(() => {
+ createComponent({ issuableType });
+ alert = findAlert();
+ link = findLink();
+ });
+
+ afterEach(() => {
+ alert = null;
+ link = null;
+ });
+
+ it('displays a non-closable alert', () => {
+ expect(alert.exists()).toBe(true);
+ expect(alert.props('dismissible')).toBe(false);
+ });
+
+ it(`displays correct message`, async () => {
+ expect(alert.text()).toMatchInterpolatedText(sprintf(i18n.alertMessage, { issuableType }));
+ });
+
+ it(`displays a link with correct text`, async () => {
+ expect(link.exists()).toBe(true);
+ expect(link.text()).toBe(`the ${issuableType}`);
+ });
+ },
+ );
+});
diff --git a/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js b/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js
index 56eb6d75def..56e425fa4eb 100644
--- a/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js
+++ b/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js
@@ -1,4 +1,4 @@
-import { GlDropdown, GlDropdownItem, GlLoadingIcon, GlSearchBoxByType } from '@gitlab/ui';
+import { GlCollapsibleListbox } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
@@ -29,19 +29,12 @@ const mockQueryLoading = jest.fn().mockReturnValue(new Promise(() => {}));
describe('SourceBranchDropdown', () => {
let wrapper;
- const findDropdown = () => wrapper.findComponent(GlDropdown);
- const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findDropdownItemByText = (text) =>
- findAllDropdownItems().wrappers.find((item) => item.text() === text);
- const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
-
- const assertDropdownItems = () => {
- const dropdownItems = findAllDropdownItems();
- expect(dropdownItems.wrappers).toHaveLength(mockProject.repository.branchNames.length);
- expect(dropdownItems.wrappers.map((item) => item.text())).toEqual(
- mockProject.repository.branchNames,
- );
+ const findListbox = () => wrapper.findComponent(GlCollapsibleListbox);
+
+ const assertListboxItems = () => {
+ const listboxItems = findListbox().props('items');
+ expect(listboxItems).toHaveLength(mockProject.repository.branchNames.length);
+ expect(listboxItems.map((item) => item.text)).toEqual(mockProject.repository.branchNames);
};
function createMockApolloProvider({ getProjectQueryLoading = false } = {}) {
@@ -70,8 +63,8 @@ describe('SourceBranchDropdown', () => {
createComponent();
});
- it('sets dropdown `disabled` prop to `true`', () => {
- expect(findDropdown().props('disabled')).toBe(true);
+ it('sets listbox `disabled` prop to `true`', () => {
+ expect(findListbox().props('disabled')).toBe(true);
});
describe('when `selectedProject` becomes specified', () => {
@@ -83,29 +76,30 @@ describe('SourceBranchDropdown', () => {
await waitForPromises();
});
- it('sets dropdown props correctly', () => {
- expect(findDropdown().props()).toMatchObject({
- loading: false,
+ it('sets listbox props correctly', () => {
+ expect(findListbox().props()).toMatchObject({
disabled: false,
- text: 'Select a branch',
+ loading: false,
+ searchable: true,
+ searching: false,
+ toggleText: 'Select a branch',
});
});
- it('renders available source branches as dropdown items', () => {
- assertDropdownItems();
+ it('renders available source branches as listbox items', () => {
+ assertListboxItems();
});
});
});
describe('when `selectedProject` prop is specified', () => {
describe('when branches are loading', () => {
- it('renders loading icon in dropdown', () => {
+ it('sets loading prop to true', () => {
createComponent({
mockApollo: createMockApolloProvider({ getProjectQueryLoading: true }),
props: { selectedProject: mockSelectedProject },
});
-
- expect(findLoadingIcon().isVisible()).toBe(true);
+ expect(findListbox().props('loading')).toEqual(true);
});
});
@@ -117,7 +111,7 @@ describe('SourceBranchDropdown', () => {
jest.clearAllMocks();
const mockSearchTerm = 'mai';
- await findSearchBox().vm.$emit('input', mockSearchTerm);
+ await findListbox().vm.$emit('search', mockSearchTerm);
expect(mockGetProjectQuery).toHaveBeenCalledWith({
branchNamesLimit: BRANCHES_PER_PAGE,
@@ -134,32 +128,32 @@ describe('SourceBranchDropdown', () => {
await waitForPromises();
});
- it('sets dropdown props correctly', () => {
- expect(findDropdown().props()).toMatchObject({
- loading: false,
+ it('sets listbox props correctly', () => {
+ expect(findListbox().props()).toMatchObject({
disabled: false,
- text: 'Select a branch',
+ loading: false,
+ searchable: true,
+ searching: false,
+ toggleText: 'Select a branch',
});
});
- it('omits monospace styling from dropdown', () => {
- expect(findDropdown().classes()).not.toContain('gl-font-monospace');
+ it('omits monospace styling from listbox', () => {
+ expect(findListbox().classes()).not.toContain('gl-font-monospace');
});
- it('renders available source branches as dropdown items', () => {
- assertDropdownItems();
+ it('renders available source branches as listbox items', () => {
+ assertListboxItems();
});
it("emits `change` event with the repository's `rootRef` by default", () => {
expect(wrapper.emitted('change')[0]).toEqual([mockProject.repository.rootRef]);
});
- describe('when selecting a dropdown item', () => {
+ describe('when selecting a listbox item', () => {
it('emits `change` event with the selected branch name', async () => {
const mockBranchName = mockProject.repository.branchNames[1];
- const itemToSelect = findDropdownItemByText(mockBranchName);
- await itemToSelect.vm.$emit('click');
-
+ findListbox().vm.$emit('select', mockBranchName);
expect(wrapper.emitted('change')[1]).toEqual([mockBranchName]);
});
});
@@ -173,16 +167,12 @@ describe('SourceBranchDropdown', () => {
});
});
- it('sets `isChecked` prop of the corresponding dropdown item to `true`', () => {
- expect(findDropdownItemByText(mockBranchName).props('isChecked')).toBe(true);
- });
-
- it('sets dropdown text to `selectedBranchName` value', () => {
- expect(findDropdown().props('text')).toBe(mockBranchName);
+ it('sets listbox text to `selectedBranchName` value', () => {
+ expect(findListbox().props('toggleText')).toBe(mockBranchName);
});
- it('adds monospace styling to dropdown', () => {
- expect(findDropdown().classes()).toContain('gl-font-monospace');
+ it('adds monospace styling to listbox', () => {
+ expect(findListbox().classes()).toContain('gl-font-monospace');
});
});
});
diff --git a/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/index_spec.js b/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/index_spec.js
index 10696d25f17..e98c6ff1054 100644
--- a/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/index_spec.js
+++ b/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/index_spec.js
@@ -1,12 +1,14 @@
import { nextTick } from 'vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import SetupInstructions from '~/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions.vue';
import SignInGitlabMultiversion from '~/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/index.vue';
-import VersionSelectForm from '~/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/version_select_form.vue';
import SignInOauthButton from '~/jira_connect/subscriptions/components/sign_in_oauth_button.vue';
+import VersionSelectForm from '~/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/version_select_form.vue';
import { updateInstallation } from '~/jira_connect/subscriptions/api';
import { reloadPage, persistBaseUrl, retrieveBaseUrl } from '~/jira_connect/subscriptions/utils';
+import { GITLAB_COM_BASE_PATH } from '~/jira_connect/subscriptions/constants';
jest.mock('~/jira_connect/subscriptions/api', () => {
return {
@@ -21,8 +23,9 @@ describe('SignInGitlabMultiversion', () => {
const mockBasePath = 'gitlab.mycompany.com';
- const findVersionSelectForm = () => wrapper.findComponent(VersionSelectForm);
+ const findSetupInstructions = () => wrapper.findComponent(SetupInstructions);
const findSignInOauthButton = () => wrapper.findComponent(SignInOauthButton);
+ const findVersionSelectForm = () => wrapper.findComponent(VersionSelectForm);
const findSubtitle = () => wrapper.findByTestId('subtitle');
const createComponent = () => {
@@ -59,15 +62,48 @@ describe('SignInGitlabMultiversion', () => {
});
describe('when version is selected', () => {
- beforeEach(() => {
- retrieveBaseUrl.mockReturnValue(mockBasePath);
- createComponent();
+ describe('when on self-managed', () => {
+ beforeEach(() => {
+ retrieveBaseUrl.mockReturnValue(mockBasePath);
+ createComponent();
+ });
+
+ it('renders correct subtitle', () => {
+ expect(findSubtitle().text()).toBe(SignInGitlabMultiversion.i18n.signInSubtitle);
+ });
+
+ it('renders setup instructions', () => {
+ expect(findSetupInstructions().exists()).toBe(true);
+ });
+
+ describe('when SetupInstructions emits `next` event', () => {
+ beforeEach(async () => {
+ findSetupInstructions().vm.$emit('next');
+ await nextTick();
+ });
+
+ it('renders sign in button', () => {
+ expect(findSignInOauthButton().props('gitlabBasePath')).toBe(mockBasePath);
+ });
+
+ it('hides setup instructions', () => {
+ expect(findSetupInstructions().exists()).toBe(false);
+ });
+ });
});
- describe('sign in button', () => {
+ describe('when on GitLab.com', () => {
+ beforeEach(() => {
+ retrieveBaseUrl.mockReturnValue(GITLAB_COM_BASE_PATH);
+ createComponent();
+ });
+
+ it('does not render setup instructions', () => {
+ expect(findSetupInstructions().exists()).toBe(false);
+ });
+
it('renders sign in button', () => {
- expect(findSignInOauthButton().exists()).toBe(true);
- expect(findSignInOauthButton().props('gitlabBasePath')).toBe(mockBasePath);
+ expect(findSignInOauthButton().props('gitlabBasePath')).toBe(GITLAB_COM_BASE_PATH);
});
describe('when button emits `sign-in` event', () => {
@@ -90,9 +126,5 @@ describe('SignInGitlabMultiversion', () => {
});
});
});
-
- it('renders correct subtitle', () => {
- expect(findSubtitle().text()).toBe(SignInGitlabMultiversion.i18n.signInSubtitle);
- });
});
});
diff --git a/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions_spec.js b/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions_spec.js
new file mode 100644
index 00000000000..5496cf008c5
--- /dev/null
+++ b/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions_spec.js
@@ -0,0 +1,35 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlButton, GlLink } from '@gitlab/ui';
+
+import { OAUTH_SELF_MANAGED_DOC_LINK } from '~/jira_connect/subscriptions/constants';
+import SetupInstructions from '~/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions.vue';
+
+describe('SetupInstructions', () => {
+ let wrapper;
+
+ const findGlButton = () => wrapper.findComponent(GlButton);
+ const findGlLink = () => wrapper.findComponent(GlLink);
+
+ const createComponent = () => {
+ wrapper = shallowMount(SetupInstructions);
+ };
+
+ describe('template', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders "Learn more" link to documentation', () => {
+ expect(findGlLink().attributes('href')).toBe(OAUTH_SELF_MANAGED_DOC_LINK);
+ });
+
+ describe('when button is clicked', () => {
+ it('emits "next" event', () => {
+ expect(wrapper.emitted('next')).toBeUndefined();
+ findGlButton().vm.$emit('click');
+
+ expect(wrapper.emitted('next')).toHaveLength(1);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap b/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap
index a72528ae36b..748e151f31b 100644
--- a/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap
+++ b/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap
@@ -87,7 +87,7 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
>
<div
aria-label="The GitLab user to which the Jira user Jane Doe will be mapped"
- class="dropdown b-dropdown gl-new-dropdown w-100 btn-group"
+ class="dropdown b-dropdown gl-dropdown w-100 btn-group"
>
<!---->
<button
@@ -101,7 +101,7 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
<!---->
<span
- class="gl-new-dropdown-button-text"
+ class="gl-dropdown-button-text"
>
janedoe
</span>
@@ -123,14 +123,14 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
tabindex="-1"
>
<div
- class="gl-new-dropdown-inner"
+ class="gl-dropdown-inner"
>
<!---->
<!---->
<div
- class="gl-new-dropdown-contents"
+ class="gl-dropdown-contents"
>
<!---->
@@ -165,7 +165,7 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
</div>
<li
- class="gl-new-dropdown-text text-secondary"
+ class="gl-dropdown-text text-secondary"
role="presentation"
>
<p
@@ -218,7 +218,7 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
>
<div
aria-label="The GitLab user to which the Jira user Fred Chopin will be mapped"
- class="dropdown b-dropdown gl-new-dropdown w-100 btn-group"
+ class="dropdown b-dropdown gl-dropdown w-100 btn-group"
>
<!---->
<button
@@ -232,7 +232,7 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
<!---->
<span
- class="gl-new-dropdown-button-text"
+ class="gl-dropdown-button-text"
>
mrgitlab
</span>
@@ -254,14 +254,14 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
tabindex="-1"
>
<div
- class="gl-new-dropdown-inner"
+ class="gl-dropdown-inner"
>
<!---->
<!---->
<div
- class="gl-new-dropdown-contents"
+ class="gl-dropdown-contents"
>
<!---->
@@ -296,7 +296,7 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
</div>
<li
- class="gl-new-dropdown-text text-secondary"
+ class="gl-dropdown-text text-secondary"
role="presentation"
>
<p
diff --git a/spec/frontend/jobs/components/filtered_search/jobs_filtered_search_spec.js b/spec/frontend/jobs/components/filtered_search/jobs_filtered_search_spec.js
index 98bdfc3fcbc..14613775791 100644
--- a/spec/frontend/jobs/components/filtered_search/jobs_filtered_search_spec.js
+++ b/spec/frontend/jobs/components/filtered_search/jobs_filtered_search_spec.js
@@ -1,6 +1,10 @@
import { GlFilteredSearch } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
+import {
+ OPERATORS_IS,
+ TOKEN_TITLE_STATUS,
+ TOKEN_TYPE_STATUS,
+} from '~/vue_shared/components/filtered_search_bar/constants';
import JobsFilteredSearch from '~/jobs/components/filtered_search/jobs_filtered_search.vue';
import { mockFailedSearchToken } from '../../mock_data';
@@ -37,11 +41,11 @@ describe('Jobs filtered search', () => {
createComponent();
expect(findStatusToken()).toMatchObject({
- type: 'status',
+ type: TOKEN_TYPE_STATUS,
icon: 'status',
- title: 'Status',
+ title: TOKEN_TITLE_STATUS,
unique: true,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
});
});
@@ -65,7 +69,7 @@ describe('Jobs filtered search', () => {
createComponent({ queryString: { statuses: value } });
expect(findFilteredSearch().props('value')).toEqual([
- { type: 'status', value: { data: value, operator: '=' } },
+ { type: TOKEN_TYPE_STATUS, value: { data: value, operator: '=' } },
]);
});
});
diff --git a/spec/frontend/jobs/components/filtered_search/tokens/job_status_token_spec.js b/spec/frontend/jobs/components/filtered_search/tokens/job_status_token_spec.js
index 92ce3925a90..fbe5f6a2e11 100644
--- a/spec/frontend/jobs/components/filtered_search/tokens/job_status_token_spec.js
+++ b/spec/frontend/jobs/components/filtered_search/tokens/job_status_token_spec.js
@@ -2,6 +2,10 @@ import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlIcon } from '@gitl
import { shallowMount } from '@vue/test-utils';
import { stubComponent } from 'helpers/stub_component';
import JobStatusToken from '~/jobs/components/filtered_search/tokens/job_status_token.vue';
+import {
+ TOKEN_TITLE_STATUS,
+ TOKEN_TYPE_STATUS,
+} from '~/vue_shared/components/filtered_search_bar/constants';
describe('Job Status Token', () => {
let wrapper;
@@ -13,9 +17,9 @@ describe('Job Status Token', () => {
const defaultProps = {
config: {
- type: 'status',
+ type: TOKEN_TYPE_STATUS,
icon: 'status',
- title: 'Status',
+ title: TOKEN_TITLE_STATUS,
unique: true,
},
value: {
diff --git a/spec/frontend/jobs/components/job/empty_state_spec.js b/spec/frontend/jobs/components/job/empty_state_spec.js
index 299b607ad78..c6ab259bf46 100644
--- a/spec/frontend/jobs/components/job/empty_state_spec.js
+++ b/spec/frontend/jobs/components/job/empty_state_spec.js
@@ -1,5 +1,7 @@
-import { mount } from '@vue/test-utils';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import EmptyState from '~/jobs/components/job/empty_state.vue';
+import ManualVariablesForm from '~/jobs/components/job/manual_variables_form.vue';
+import { mockFullPath, mockId } from './mock_data';
describe('Empty State', () => {
let wrapper;
@@ -7,26 +9,31 @@ describe('Empty State', () => {
const defaultProps = {
illustrationPath: 'illustrations/pending_job_empty.svg',
illustrationSizeClass: 'svg-430',
+ jobId: mockId,
title: 'This job has not started yet',
playable: false,
+ isRetryable: true,
};
const createWrapper = (props) => {
- wrapper = mount(EmptyState, {
+ wrapper = shallowMountExtended(EmptyState, {
propsData: {
...defaultProps,
...props,
},
+ provide: {
+ projectPath: mockFullPath,
+ },
});
};
const content = 'This job is in pending state and is waiting to be picked by a runner';
const findEmptyStateImage = () => wrapper.find('img');
- const findTitle = () => wrapper.find('[data-testid="job-empty-state-title"]');
- const findContent = () => wrapper.find('[data-testid="job-empty-state-content"]');
- const findAction = () => wrapper.find('[data-testid="job-empty-state-action"]');
- const findManualVarsForm = () => wrapper.find('[data-testid="manual-vars-form"]');
+ const findTitle = () => wrapper.findByTestId('job-empty-state-title');
+ const findContent = () => wrapper.findByTestId('job-empty-state-content');
+ const findAction = () => wrapper.findByTestId('job-empty-state-action');
+ const findManualVarsForm = () => wrapper.findComponent(ManualVariablesForm);
afterEach(() => {
if (wrapper?.destroy) {
diff --git a/spec/frontend/jobs/components/job/job_app_spec.js b/spec/frontend/jobs/components/job/job_app_spec.js
index 822528403cf..98f1979db1b 100644
--- a/spec/frontend/jobs/components/job/job_app_spec.js
+++ b/spec/frontend/jobs/components/job/job_app_spec.js
@@ -1,14 +1,15 @@
-import { GlLoadingIcon } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
-import MockAdapter from 'axios-mock-adapter';
import Vuex from 'vuex';
-import delayedJobFixture from 'test_fixtures/jobs/delayed.json';
+import { GlLoadingIcon } from '@gitlab/ui';
+import MockAdapter from 'axios-mock-adapter';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { TEST_HOST } from 'helpers/test_constants';
import EmptyState from '~/jobs/components/job/empty_state.vue';
import EnvironmentsBlock from '~/jobs/components/job/environments_block.vue';
import ErasedBlock from '~/jobs/components/job/erased_block.vue';
import JobApp from '~/jobs/components/job/job_app.vue';
+import JobLog from '~/jobs/components/log/log.vue';
+import JobLogTopBar from '~/jobs/components/job/job_log_controllers.vue';
import Sidebar from '~/jobs/components/job/sidebar/sidebar.vue';
import StuckBlock from '~/jobs/components/job/stuck_block.vue';
import UnmetPrerequisitesBlock from '~/jobs/components/job/unmet_prerequisites_block.vue';
@@ -40,7 +41,10 @@ describe('Job App', () => {
};
const createComponent = () => {
- wrapper = mount(JobApp, { propsData: { ...props }, store });
+ wrapper = shallowMountExtended(JobApp, {
+ propsData: { ...props },
+ store,
+ });
};
const setupAndMount = async ({ jobData = {}, jobLogData = {} } = {}) => {
@@ -59,22 +63,16 @@ describe('Job App', () => {
const findLoadingComponent = () => wrapper.findComponent(GlLoadingIcon);
const findSidebar = () => wrapper.findComponent(Sidebar);
- const findJobContent = () => wrapper.find('[data-testid="job-content"');
const findStuckBlockComponent = () => wrapper.findComponent(StuckBlock);
- const findStuckBlockWithTags = () => wrapper.find('[data-testid="job-stuck-with-tags"');
- const findStuckBlockNoActiveRunners = () =>
- wrapper.find('[data-testid="job-stuck-no-active-runners"');
const findFailedJobComponent = () => wrapper.findComponent(UnmetPrerequisitesBlock);
const findEnvironmentsBlockComponent = () => wrapper.findComponent(EnvironmentsBlock);
const findErasedBlock = () => wrapper.findComponent(ErasedBlock);
- const findArchivedJob = () => wrapper.find('[data-testid="archived-job"]');
const findEmptyState = () => wrapper.findComponent(EmptyState);
- const findJobNewIssueLink = () => wrapper.find('[data-testid="job-new-issue"]');
- const findJobEmptyStateTitle = () => wrapper.find('[data-testid="job-empty-state-title"]');
- const findJobLogScrollTop = () => wrapper.find('[data-testid="job-controller-scroll-top"]');
- const findJobLogScrollBottom = () => wrapper.find('[data-testid="job-controller-scroll-bottom"]');
- const findJobLogController = () => wrapper.find('[data-testid="job-raw-link-controller"]');
- const findJobLogEraseLink = () => wrapper.find('[data-testid="job-log-erase-link"]');
+ const findJobLog = () => wrapper.findComponent(JobLog);
+ const findJobLogTopBar = () => wrapper.findComponent(JobLogTopBar);
+
+ const findJobContent = () => wrapper.findByTestId('job-content');
+ const findArchivedJob = () => wrapper.findByTestId('archived-job');
beforeEach(() => {
mock = new MockAdapter(axios);
@@ -116,36 +114,6 @@ describe('Job App', () => {
expect(wrapper.vm.shouldRenderCalloutMessage).toBe(true);
}));
});
-
- describe('triggered job', () => {
- beforeEach(() => {
- const aYearAgo = new Date();
- aYearAgo.setFullYear(aYearAgo.getFullYear() - 1);
-
- return setupAndMount({
- jobData: { started: aYearAgo.toISOString(), started_at: aYearAgo.toISOString() },
- });
- });
-
- it('should render provided job information', () => {
- expect(wrapper.find('.header-main-content').text().replace(/\s+/g, ' ').trim()).toContain(
- 'passed Job test triggered 1 year ago by Root',
- );
- });
-
- it('should render new issue link', () => {
- expect(findJobNewIssueLink().attributes('href')).toEqual(job.new_issue_path);
- });
- });
-
- describe('created job', () => {
- it('should render created key', () =>
- setupAndMount().then(() => {
- expect(
- wrapper.find('.header-main-content').text().replace(/\s+/g, ' ').trim(),
- ).toContain('passed Job test created 3 weeks ago by Root');
- }));
- });
});
describe('stuck block', () => {
@@ -169,57 +137,10 @@ describe('Job App', () => {
},
}).then(() => {
expect(findStuckBlockComponent().exists()).toBe(true);
- expect(findStuckBlockNoActiveRunners().exists()).toBe(true);
- }));
- });
-
- describe('when available runners can not run specified tag', () => {
- it('renders tags in stuck block when there are no runners', () =>
- setupAndMount({
- jobData: {
- status: {
- group: 'pending',
- icon: 'status_pending',
- label: 'pending',
- text: 'pending',
- details_path: 'path',
- },
- stuck: true,
- runners: {
- available: false,
- online: false,
- },
- },
- }).then(() => {
- expect(findStuckBlockComponent().text()).toContain(job.tags[0]);
- expect(findStuckBlockWithTags().exists()).toBe(true);
- }));
- });
-
- describe('when runners are offline and build has tags', () => {
- it('renders message about job being stuck because of no runners with the specified tags', () =>
- setupAndMount({
- jobData: {
- status: {
- group: 'pending',
- icon: 'status_pending',
- label: 'pending',
- text: 'pending',
- details_path: 'path',
- },
- stuck: true,
- runners: {
- available: true,
- online: true,
- },
- },
- }).then(() => {
- expect(findStuckBlockComponent().text()).toContain(job.tags[0]);
- expect(findStuckBlockWithTags().exists()).toBe(true);
}));
});
- it('does not renders stuck block when there are no runners', () =>
+ it('does not render stuck block when there are runners', () =>
setupAndMount({
jobData: {
runners: { available: true },
@@ -351,45 +272,13 @@ describe('Job App', () => {
setupAndMount({ jobData: { has_trace: true } }).then(() => {
expect(findEmptyState().exists()).toBe(false);
}));
-
- it('displays remaining time for a delayed job', () => {
- const oneHourInMilliseconds = 3600000;
- jest
- .spyOn(Date, 'now')
- .mockImplementation(
- () => new Date(delayedJobFixture.scheduled_at).getTime() - oneHourInMilliseconds,
- );
- return setupAndMount({ jobData: delayedJobFixture }).then(() => {
- expect(findEmptyState().exists()).toBe(true);
-
- const title = findJobEmptyStateTitle().text();
-
- expect(title).toEqual('This is a delayed job to run in 01:00:00');
- });
- });
});
describe('sidebar', () => {
- it('has no blank blocks', async () => {
- await setupAndMount({
- jobData: {
- duration: null,
- finished_at: null,
- erased_at: null,
- queued: null,
- runner: null,
- coverage: null,
- tags: [],
- cancel_path: null,
- },
- });
+ it('renders sidebar', async () => {
+ await setupAndMount();
- const blocks = wrapper.findAll('.blocks-container > *').wrappers;
- expect(blocks.length).toBeGreaterThan(0);
-
- blocks.forEach((block) => {
- expect(block.text().trim()).not.toBe('');
- });
+ expect(findSidebar().exists()).toBe(true);
});
});
});
@@ -410,31 +299,15 @@ describe('Job App', () => {
});
});
- describe('job log controls', () => {
- beforeEach(() =>
- setupAndMount({
- jobLogData: {
- html: '<span>Update</span>',
- status: 'success',
- append: false,
- size: 50,
- total: 100,
- complete: true,
- },
- }),
- );
-
- it('should render scroll buttons', () => {
- expect(findJobLogScrollTop().exists()).toBe(true);
- expect(findJobLogScrollBottom().exists()).toBe(true);
- });
+ describe('job log', () => {
+ beforeEach(() => setupAndMount());
- it('should render link to raw ouput', () => {
- expect(findJobLogController().exists()).toBe(true);
+ it('should render job log header', () => {
+ expect(findJobLogTopBar().exists()).toBe(true);
});
- it('should render link to erase job', () => {
- expect(findJobLogEraseLink().exists()).toBe(true);
+ it('should render job log', () => {
+ expect(findJobLog().exists()).toBe(true);
});
});
});
diff --git a/spec/frontend/jobs/components/job/job_sidebar_retry_button_spec.js b/spec/frontend/jobs/components/job/job_sidebar_retry_button_spec.js
index 18d5f35bde4..91821a38a78 100644
--- a/spec/frontend/jobs/components/job/job_sidebar_retry_button_spec.js
+++ b/spec/frontend/jobs/components/job/job_sidebar_retry_button_spec.js
@@ -16,6 +16,7 @@ describe('Job Sidebar Retry Button', () => {
wrapper = shallowMountExtended(JobsSidebarRetryButton, {
propsData: {
href: job.retry_path,
+ isManualJob: false,
modalId: 'modal-id',
...props,
},
diff --git a/spec/frontend/jobs/components/job/legacy_manual_variables_form_spec.js b/spec/frontend/jobs/components/job/legacy_manual_variables_form_spec.js
deleted file mode 100644
index 184562b2968..00000000000
--- a/spec/frontend/jobs/components/job/legacy_manual_variables_form_spec.js
+++ /dev/null
@@ -1,156 +0,0 @@
-import { GlSprintf, GlLink } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-import Vuex from 'vuex';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import LegacyManualVariablesForm from '~/jobs/components/job/legacy_manual_variables_form.vue';
-
-Vue.use(Vuex);
-
-describe('Manual Variables Form', () => {
- let wrapper;
- let store;
-
- const requiredProps = {
- action: {
- path: '/play',
- method: 'post',
- button_title: 'Trigger this manual action',
- },
- };
-
- const createComponent = (props = {}) => {
- store = new Vuex.Store({
- actions: {
- triggerManualJob: jest.fn(),
- },
- });
-
- wrapper = extendedWrapper(
- mount(LegacyManualVariablesForm, {
- propsData: { ...requiredProps, ...props },
- store,
- stubs: {
- GlSprintf,
- },
- }),
- );
- };
-
- const findHelpText = () => wrapper.findComponent(GlSprintf);
- const findHelpLink = () => wrapper.findComponent(GlLink);
-
- const findTriggerBtn = () => wrapper.findByTestId('trigger-manual-job-btn');
- const findDeleteVarBtn = () => wrapper.findByTestId('delete-variable-btn');
- const findAllDeleteVarBtns = () => wrapper.findAllByTestId('delete-variable-btn');
- const findDeleteVarBtnPlaceholder = () => wrapper.findByTestId('delete-variable-btn-placeholder');
- const findCiVariableKey = () => wrapper.findByTestId('ci-variable-key');
- const findAllCiVariableKeys = () => wrapper.findAllByTestId('ci-variable-key');
- const findCiVariableValue = () => wrapper.findByTestId('ci-variable-value');
- const findAllVariables = () => wrapper.findAllByTestId('ci-variable-row');
-
- const setCiVariableKey = () => {
- findCiVariableKey().setValue('new key');
- findCiVariableKey().vm.$emit('change');
- nextTick();
- };
-
- const setCiVariableKeyByPosition = (position, value) => {
- findAllCiVariableKeys().at(position).setValue(value);
- findAllCiVariableKeys().at(position).vm.$emit('change');
- nextTick();
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('creates a new variable when user enters a new key value', async () => {
- expect(findAllVariables()).toHaveLength(1);
-
- await setCiVariableKey();
-
- expect(findAllVariables()).toHaveLength(2);
- });
-
- it('does not create extra empty variables', async () => {
- expect(findAllVariables()).toHaveLength(1);
-
- await setCiVariableKey();
-
- expect(findAllVariables()).toHaveLength(2);
-
- await setCiVariableKey();
-
- expect(findAllVariables()).toHaveLength(2);
- });
-
- it('removes the correct variable row', async () => {
- const variableKeyNameOne = 'key-one';
- const variableKeyNameThree = 'key-three';
-
- await setCiVariableKeyByPosition(0, variableKeyNameOne);
-
- await setCiVariableKeyByPosition(1, 'key-two');
-
- await setCiVariableKeyByPosition(2, variableKeyNameThree);
-
- expect(findAllVariables()).toHaveLength(4);
-
- await findAllDeleteVarBtns().at(1).trigger('click');
-
- expect(findAllVariables()).toHaveLength(3);
-
- expect(findAllCiVariableKeys().at(0).element.value).toBe(variableKeyNameOne);
- expect(findAllCiVariableKeys().at(1).element.value).toBe(variableKeyNameThree);
- expect(findAllCiVariableKeys().at(2).element.value).toBe('');
- });
-
- it('trigger button is disabled after trigger action', async () => {
- expect(findTriggerBtn().props('disabled')).toBe(false);
-
- await findTriggerBtn().trigger('click');
-
- expect(findTriggerBtn().props('disabled')).toBe(true);
- });
-
- it('delete variable button should only show when there is more than one variable', async () => {
- expect(findDeleteVarBtn().exists()).toBe(false);
-
- await setCiVariableKey();
-
- expect(findDeleteVarBtn().exists()).toBe(true);
- });
-
- it('delete variable button placeholder should only exist when a user cannot remove', async () => {
- expect(findDeleteVarBtnPlaceholder().exists()).toBe(true);
- });
-
- it('renders help text with provided link', () => {
- expect(findHelpText().exists()).toBe(true);
- expect(findHelpLink().attributes('href')).toBe(
- '/help/ci/variables/index#add-a-cicd-variable-to-a-project',
- );
- });
-
- it('passes variables in correct format', async () => {
- jest.spyOn(store, 'dispatch');
-
- await setCiVariableKey();
-
- await findCiVariableValue().setValue('new value');
-
- await findTriggerBtn().trigger('click');
-
- expect(store.dispatch).toHaveBeenCalledWith('triggerManualJob', [
- {
- key: 'new key',
- secret_value: 'new value',
- },
- ]);
- });
-});
diff --git a/spec/frontend/jobs/components/job/legacy_sidebar_header_spec.js b/spec/frontend/jobs/components/job/legacy_sidebar_header_spec.js
deleted file mode 100644
index 95eb10118ee..00000000000
--- a/spec/frontend/jobs/components/job/legacy_sidebar_header_spec.js
+++ /dev/null
@@ -1,109 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import JobRetryButton from '~/jobs/components/job/sidebar/job_sidebar_retry_button.vue';
-import LegacySidebarHeader from '~/jobs/components/job/sidebar/legacy_sidebar_header.vue';
-import createStore from '~/jobs/store';
-import job, { failedJobStatus } from '../../mock_data';
-
-describe('Legacy Sidebar Header', () => {
- let store;
- let wrapper;
-
- const findCancelButton = () => wrapper.findByTestId('cancel-button');
- const findRetryButton = () => wrapper.findComponent(JobRetryButton);
- const findEraseLink = () => wrapper.findByTestId('job-log-erase-link');
-
- const createWrapper = (props) => {
- store = createStore();
-
- wrapper = extendedWrapper(
- shallowMount(LegacySidebarHeader, {
- propsData: {
- job,
- ...props,
- },
- store,
- }),
- );
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('when job log is erasable', () => {
- const path = '/root/ci-project/-/jobs/1447/erase';
-
- beforeEach(() => {
- createWrapper({
- erasePath: path,
- });
- });
-
- it('renders erase job link', () => {
- expect(findEraseLink().exists()).toBe(true);
- });
-
- it('erase job link has correct path', () => {
- expect(findEraseLink().attributes('href')).toBe(path);
- });
- });
-
- describe('when job log is not erasable', () => {
- beforeEach(() => {
- createWrapper();
- });
-
- it('does not render erase button', () => {
- expect(findEraseLink().exists()).toBe(false);
- });
- });
-
- describe('when the job is retryable', () => {
- beforeEach(() => {
- createWrapper();
- });
-
- it('should render the retry button', () => {
- expect(findRetryButton().props('href')).toBe(job.retry_path);
- });
-
- it('should have a different label when the job status is passed', () => {
- expect(findRetryButton().attributes('title')).toBe(
- LegacySidebarHeader.i18n.runAgainJobButtonLabel,
- );
- });
- });
-
- describe('when there is no retry path', () => {
- it('should not render a retry button', async () => {
- const copy = { ...job, retry_path: null };
- createWrapper({ job: copy });
-
- expect(findRetryButton().exists()).toBe(false);
- });
- });
-
- describe('when the job is cancelable', () => {
- beforeEach(() => {
- createWrapper();
- });
-
- it('should render link to cancel job', () => {
- expect(findCancelButton().props('icon')).toBe('cancel');
- expect(findCancelButton().attributes('href')).toBe(job.cancel_path);
- });
- });
-
- describe('when the job is failed', () => {
- describe('retry button', () => {
- it('should have a different label when the job status is failed', () => {
- createWrapper({ job: { ...job, status: failedJobStatus } });
-
- expect(findRetryButton().attributes('title')).toBe(
- LegacySidebarHeader.i18n.retryJobButtonLabel,
- );
- });
- });
- });
-});
diff --git a/spec/frontend/jobs/components/job/manual_variables_form_spec.js b/spec/frontend/jobs/components/job/manual_variables_form_spec.js
index 5806f9f75f9..45a1e9dca76 100644
--- a/spec/frontend/jobs/components/job/manual_variables_form_spec.js
+++ b/spec/frontend/jobs/components/job/manual_variables_form_spec.js
@@ -1,46 +1,71 @@
import { GlSprintf, GlLink } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-import Vuex from 'vuex';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import { createLocalVue } from '@vue/test-utils';
+import VueApollo from 'vue-apollo';
+import { nextTick } from 'vue';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+import { GRAPHQL_ID_TYPES } from '~/jobs/constants';
+import waitForPromises from 'helpers/wait_for_promises';
import ManualVariablesForm from '~/jobs/components/job/manual_variables_form.vue';
-
-Vue.use(Vuex);
+import getJobQuery from '~/jobs/components/job/graphql/queries/get_job.query.graphql';
+import retryJobMutation from '~/jobs/components/job/graphql/mutations/job_retry_with_variables.mutation.graphql';
+import {
+ mockFullPath,
+ mockId,
+ mockJobResponse,
+ mockJobWithVariablesResponse,
+ mockJobMutationData,
+} from './mock_data';
+
+const localVue = createLocalVue();
+localVue.use(VueApollo);
+
+const defaultProvide = {
+ projectPath: mockFullPath,
+};
describe('Manual Variables Form', () => {
let wrapper;
- let store;
-
- const requiredProps = {
- action: {
- path: '/play',
- method: 'post',
- button_title: 'Trigger this manual action',
- },
+ let mockApollo;
+ let getJobQueryResponse;
+
+ const createComponent = ({ options = {}, props = {} } = {}) => {
+ wrapper = mountExtended(ManualVariablesForm, {
+ propsData: {
+ ...props,
+ jobId: mockId,
+ isRetryable: true,
+ },
+ provide: {
+ ...defaultProvide,
+ },
+ ...options,
+ });
};
- const createComponent = (props = {}) => {
- store = new Vuex.Store({
- actions: {
- triggerManualJob: jest.fn(),
- },
+ const createComponentWithApollo = async ({ props = {} } = {}) => {
+ const requestHandlers = [[getJobQuery, getJobQueryResponse]];
+
+ mockApollo = createMockApollo(requestHandlers);
+
+ const options = {
+ localVue,
+ apolloProvider: mockApollo,
+ };
+
+ createComponent({
+ props,
+ options,
});
- wrapper = extendedWrapper(
- mount(ManualVariablesForm, {
- propsData: { ...requiredProps, ...props },
- store,
- stubs: {
- GlSprintf,
- },
- }),
- );
+ return waitForPromises();
};
const findHelpText = () => wrapper.findComponent(GlSprintf);
const findHelpLink = () => wrapper.findComponent(GlLink);
-
- const findTriggerBtn = () => wrapper.findByTestId('trigger-manual-job-btn');
+ const findCancelBtn = () => wrapper.findByTestId('cancel-btn');
+ const findRerunBtn = () => wrapper.findByTestId('run-manual-job-btn');
const findDeleteVarBtn = () => wrapper.findByTestId('delete-variable-btn');
const findAllDeleteVarBtns = () => wrapper.findAllByTestId('delete-variable-btn');
const findDeleteVarBtnPlaceholder = () => wrapper.findByTestId('delete-variable-btn-placeholder');
@@ -62,95 +87,134 @@ describe('Manual Variables Form', () => {
};
beforeEach(() => {
- createComponent();
+ getJobQueryResponse = jest.fn();
});
afterEach(() => {
wrapper.destroy();
});
- it('creates a new variable when user enters a new key value', async () => {
- expect(findAllVariables()).toHaveLength(1);
+ describe('when page renders', () => {
+ beforeEach(async () => {
+ getJobQueryResponse.mockResolvedValue(mockJobResponse);
+ await createComponentWithApollo();
+ });
+
+ it('renders help text with provided link', () => {
+ expect(findHelpText().exists()).toBe(true);
+ expect(findHelpLink().attributes('href')).toBe(
+ '/help/ci/variables/index#add-a-cicd-variable-to-a-project',
+ );
+ });
+
+ it('renders buttons', () => {
+ expect(findCancelBtn().exists()).toBe(true);
+ expect(findRerunBtn().exists()).toBe(true);
+ });
+ });
+
+ describe('when job has variables', () => {
+ beforeEach(async () => {
+ getJobQueryResponse.mockResolvedValue(mockJobWithVariablesResponse);
+ await createComponentWithApollo();
+ });
- await setCiVariableKey();
+ it('sets manual job variables', () => {
+ const queryKey = mockJobWithVariablesResponse.data.project.job.manualVariables.nodes[0].key;
+ const queryValue =
+ mockJobWithVariablesResponse.data.project.job.manualVariables.nodes[0].value;
- expect(findAllVariables()).toHaveLength(2);
+ expect(findCiVariableKey().element.value).toBe(queryKey);
+ expect(findCiVariableValue().element.value).toBe(queryValue);
+ });
});
- it('does not create extra empty variables', async () => {
- expect(findAllVariables()).toHaveLength(1);
+ describe('when mutation fires', () => {
+ beforeEach(async () => {
+ await createComponentWithApollo();
+ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockJobMutationData);
+ });
- await setCiVariableKey();
+ it('passes variables in correct format', async () => {
+ await setCiVariableKey();
- expect(findAllVariables()).toHaveLength(2);
+ await findCiVariableValue().setValue('new value');
- await setCiVariableKey();
+ await findRerunBtn().vm.$emit('click');
- expect(findAllVariables()).toHaveLength(2);
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1);
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
+ mutation: retryJobMutation,
+ variables: {
+ id: convertToGraphQLId(GRAPHQL_ID_TYPES.ciBuild, mockId),
+ variables: [
+ {
+ key: 'new key',
+ value: 'new value',
+ },
+ ],
+ },
+ });
+ });
});
- it('removes the correct variable row', async () => {
- const variableKeyNameOne = 'key-one';
- const variableKeyNameThree = 'key-three';
+ describe('updating variables in UI', () => {
+ beforeEach(async () => {
+ getJobQueryResponse.mockResolvedValue(mockJobResponse);
+ await createComponentWithApollo();
+ });
- await setCiVariableKeyByPosition(0, variableKeyNameOne);
+ it('creates a new variable when user enters a new key value', async () => {
+ expect(findAllVariables()).toHaveLength(1);
- await setCiVariableKeyByPosition(1, 'key-two');
+ await setCiVariableKey();
- await setCiVariableKeyByPosition(2, variableKeyNameThree);
+ expect(findAllVariables()).toHaveLength(2);
+ });
- expect(findAllVariables()).toHaveLength(4);
+ it('does not create extra empty variables', async () => {
+ expect(findAllVariables()).toHaveLength(1);
- await findAllDeleteVarBtns().at(1).trigger('click');
+ await setCiVariableKey();
- expect(findAllVariables()).toHaveLength(3);
+ expect(findAllVariables()).toHaveLength(2);
- expect(findAllCiVariableKeys().at(0).element.value).toBe(variableKeyNameOne);
- expect(findAllCiVariableKeys().at(1).element.value).toBe(variableKeyNameThree);
- expect(findAllCiVariableKeys().at(2).element.value).toBe('');
- });
+ await setCiVariableKey();
- it('trigger button is disabled after trigger action', async () => {
- expect(findTriggerBtn().props('disabled')).toBe(false);
+ expect(findAllVariables()).toHaveLength(2);
+ });
- await findTriggerBtn().trigger('click');
+ it('removes the correct variable row', async () => {
+ const variableKeyNameOne = 'key-one';
+ const variableKeyNameThree = 'key-three';
- expect(findTriggerBtn().props('disabled')).toBe(true);
- });
+ await setCiVariableKeyByPosition(0, variableKeyNameOne);
- it('delete variable button should only show when there is more than one variable', async () => {
- expect(findDeleteVarBtn().exists()).toBe(false);
+ await setCiVariableKeyByPosition(1, 'key-two');
- await setCiVariableKey();
+ await setCiVariableKeyByPosition(2, variableKeyNameThree);
- expect(findDeleteVarBtn().exists()).toBe(true);
- });
+ expect(findAllVariables()).toHaveLength(4);
- it('delete variable button placeholder should only exist when a user cannot remove', async () => {
- expect(findDeleteVarBtnPlaceholder().exists()).toBe(true);
- });
+ await findAllDeleteVarBtns().at(1).trigger('click');
- it('renders help text with provided link', () => {
- expect(findHelpText().exists()).toBe(true);
- expect(findHelpLink().attributes('href')).toBe(
- '/help/ci/variables/index#add-a-cicd-variable-to-a-project',
- );
- });
+ expect(findAllVariables()).toHaveLength(3);
- it('passes variables in correct format', async () => {
- jest.spyOn(store, 'dispatch');
+ expect(findAllCiVariableKeys().at(0).element.value).toBe(variableKeyNameOne);
+ expect(findAllCiVariableKeys().at(1).element.value).toBe(variableKeyNameThree);
+ expect(findAllCiVariableKeys().at(2).element.value).toBe('');
+ });
- await setCiVariableKey();
+ it('delete variable button should only show when there is more than one variable', async () => {
+ expect(findDeleteVarBtn().exists()).toBe(false);
- await findCiVariableValue().setValue('new value');
+ await setCiVariableKey();
- await findTriggerBtn().trigger('click');
+ expect(findDeleteVarBtn().exists()).toBe(true);
+ });
- expect(store.dispatch).toHaveBeenCalledWith('triggerManualJob', [
- {
- key: 'new key',
- secret_value: 'new value',
- },
- ]);
+ it('delete variable button placeholder should only exist when a user cannot remove', async () => {
+ expect(findDeleteVarBtnPlaceholder().exists()).toBe(true);
+ });
});
});
diff --git a/spec/frontend/jobs/components/job/mock_data.js b/spec/frontend/jobs/components/job/mock_data.js
new file mode 100644
index 00000000000..9596e859475
--- /dev/null
+++ b/spec/frontend/jobs/components/job/mock_data.js
@@ -0,0 +1,76 @@
+export const mockFullPath = 'Commit451/lab-coat';
+export const mockId = 401;
+
+export const mockJobResponse = {
+ data: {
+ project: {
+ id: 'gid://gitlab/Project/4',
+ job: {
+ id: 'gid://gitlab/Ci::Build/401',
+ manualJob: true,
+ manualVariables: {
+ nodes: [],
+ __typename: 'CiManualVariableConnection',
+ },
+ name: 'manual_job',
+ retryable: true,
+ status: 'SUCCESS',
+ __typename: 'CiJob',
+ },
+ __typename: 'Project',
+ },
+ },
+};
+
+export const mockJobWithVariablesResponse = {
+ data: {
+ project: {
+ id: 'gid://gitlab/Project/4',
+ job: {
+ id: 'gid://gitlab/Ci::Build/401',
+ manualJob: true,
+ manualVariables: {
+ nodes: [
+ {
+ id: 'gid://gitlab/Ci::JobVariable/150',
+ key: 'new key',
+ value: 'new value',
+ __typename: 'CiManualVariable',
+ },
+ ],
+ __typename: 'CiManualVariableConnection',
+ },
+ name: 'manual_job',
+ retryable: true,
+ status: 'SUCCESS',
+ __typename: 'CiJob',
+ },
+ __typename: 'Project',
+ },
+ },
+};
+
+export const mockJobMutationData = {
+ data: {
+ jobRetry: {
+ job: {
+ id: 'gid://gitlab/Ci::Build/401',
+ manualVariables: {
+ nodes: [
+ {
+ id: 'gid://gitlab/Ci::JobVariable/151',
+ key: 'new key',
+ value: 'new value',
+ __typename: 'CiManualVariable',
+ },
+ ],
+ __typename: 'CiManualVariableConnection',
+ },
+ webPath: '/Commit451/lab-coat/-/jobs/401',
+ __typename: 'CiJob',
+ },
+ errors: [],
+ __typename: 'JobRetryPayload',
+ },
+ },
+};
diff --git a/spec/frontend/jobs/components/job/sidebar_header_spec.js b/spec/frontend/jobs/components/job/sidebar_header_spec.js
index cb32ca9d3dc..da97945f9bf 100644
--- a/spec/frontend/jobs/components/job/sidebar_header_spec.js
+++ b/spec/frontend/jobs/components/job/sidebar_header_spec.js
@@ -1,91 +1,87 @@
-import { shallowMount } from '@vue/test-utils';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import SidebarHeader from '~/jobs/components/job/sidebar/sidebar_header.vue';
import JobRetryButton from '~/jobs/components/job/sidebar/job_sidebar_retry_button.vue';
-import LegacySidebarHeader from '~/jobs/components/job/sidebar/legacy_sidebar_header.vue';
-import createStore from '~/jobs/store';
-import job from '../../mock_data';
+import getJobQuery from '~/jobs/components/job/graphql/queries/get_job.query.graphql';
+import { mockFullPath, mockId, mockJobResponse } from './mock_data';
-describe('Legacy Sidebar Header', () => {
- let store;
+Vue.use(VueApollo);
+
+const defaultProvide = {
+ projectPath: mockFullPath,
+};
+
+describe('Sidebar Header', () => {
let wrapper;
- const findCancelButton = () => wrapper.findByTestId('cancel-button');
- const findRetryButton = () => wrapper.findComponent(JobRetryButton);
- const findEraseLink = () => wrapper.findByTestId('job-log-erase-link');
-
- const createWrapper = (props) => {
- store = createStore();
-
- wrapper = extendedWrapper(
- shallowMount(LegacySidebarHeader, {
- propsData: {
- job,
- ...props,
- },
- store,
- }),
- );
+ const createComponent = ({ options = {}, props = {}, restJob = {} } = {}) => {
+ wrapper = shallowMountExtended(SidebarHeader, {
+ propsData: {
+ ...props,
+ jobId: mockId,
+ restJob,
+ },
+ provide: {
+ ...defaultProvide,
+ },
+ ...options,
+ });
};
- afterEach(() => {
- wrapper.destroy();
- });
+ const createComponentWithApollo = async ({ props = {}, restJob = {} } = {}) => {
+ const getJobQueryResponse = jest.fn().mockResolvedValue(mockJobResponse);
- describe('when job log is erasable', () => {
- const path = '/root/ci-project/-/jobs/1447/erase';
+ const requestHandlers = [[getJobQuery, getJobQueryResponse]];
- beforeEach(() => {
- createWrapper({
- erasePath: path,
- });
- });
+ const apolloProvider = createMockApollo(requestHandlers);
- it('renders erase job link', () => {
- expect(findEraseLink().exists()).toBe(true);
- });
+ const options = {
+ apolloProvider,
+ };
- it('erase job link has correct path', () => {
- expect(findEraseLink().attributes('href')).toBe(path);
+ createComponent({
+ props,
+ restJob,
+ options,
});
- });
- describe('when job log is not erasable', () => {
- beforeEach(() => {
- createWrapper();
- });
+ return waitForPromises();
+ };
- it('does not render erase button', () => {
- expect(findEraseLink().exists()).toBe(false);
- });
- });
+ const findCancelButton = () => wrapper.findByTestId('cancel-button');
+ const findEraseButton = () => wrapper.findByTestId('job-log-erase-link');
+ const findJobName = () => wrapper.findByTestId('job-name');
+ const findRetryButton = () => wrapper.findComponent(JobRetryButton);
- describe('when the job is retryable', () => {
- beforeEach(() => {
- createWrapper();
+ describe('when rendering contents', () => {
+ it('renders the correct job name', async () => {
+ await createComponentWithApollo();
+ expect(findJobName().text()).toBe(mockJobResponse.data.project.job.name);
});
- it('should render the retry button', () => {
- expect(findRetryButton().props('href')).toBe(job.retry_path);
+ it('does not render buttons with no paths', async () => {
+ await createComponentWithApollo();
+ expect(findCancelButton().exists()).toBe(false);
+ expect(findEraseButton().exists()).toBe(false);
+ expect(findRetryButton().exists()).toBe(false);
});
- });
-
- describe('when there is no retry path', () => {
- it('should not render a retry button', async () => {
- const copy = { ...job, retry_path: null };
- createWrapper({ job: copy });
- expect(findRetryButton().exists()).toBe(false);
+ it('renders a retry button with a path', async () => {
+ await createComponentWithApollo({ restJob: { retry_path: 'retry/path' } });
+ expect(findRetryButton().exists()).toBe(true);
});
- });
- describe('when the job is cancelable', () => {
- beforeEach(() => {
- createWrapper();
+ it('renders a cancel button with a path', async () => {
+ await createComponentWithApollo({ restJob: { cancel_path: 'cancel/path' } });
+ expect(findCancelButton().exists()).toBe(true);
});
- it('should render link to cancel job', () => {
- expect(findCancelButton().props('icon')).toBe('cancel');
- expect(findCancelButton().attributes('href')).toBe(job.cancel_path);
+ it('renders an erase button with a path', async () => {
+ await createComponentWithApollo({ restJob: { erase_path: 'erase/path' } });
+ expect(findEraseButton().exists()).toBe(true);
});
});
});
diff --git a/spec/frontend/jobs/mock_data.js b/spec/frontend/jobs/mock_data.js
index a7fe6d5a626..9abd610c26d 100644
--- a/spec/frontend/jobs/mock_data.js
+++ b/spec/frontend/jobs/mock_data.js
@@ -3,6 +3,7 @@ import mockJobsPaginated from 'test_fixtures/graphql/jobs/get_jobs.query.graphql
import mockJobs from 'test_fixtures/graphql/jobs/get_jobs.query.graphql.json';
import mockJobsAsGuest from 'test_fixtures/graphql/jobs/get_jobs.query.graphql.as_guest.json';
import { TEST_HOST } from 'spec/test_constants';
+import { TOKEN_TYPE_STATUS } from '~/vue_shared/components/filtered_search_bar/constants';
const threeWeeksAgo = new Date();
threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21);
@@ -1365,7 +1366,10 @@ export const CIJobConnectionExistingCache = {
statuses: 'PENDING',
};
-export const mockFailedSearchToken = { type: 'status', value: { data: 'FAILED', operator: '=' } };
+export const mockFailedSearchToken = {
+ type: TOKEN_TYPE_STATUS,
+ value: { data: 'FAILED', operator: '=' },
+};
export const retryMutationResponse = {
data: {
diff --git a/spec/frontend/language_switcher/components/app_spec.js b/spec/frontend/language_switcher/components/app_spec.js
new file mode 100644
index 00000000000..6a1b94cd813
--- /dev/null
+++ b/spec/frontend/language_switcher/components/app_spec.js
@@ -0,0 +1,62 @@
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import LanguageSwitcherApp from '~/language_switcher/components/app.vue';
+import { PREFERRED_LANGUAGE_COOKIE_KEY } from '~/language_switcher/constants';
+import * as utils from '~/lib/utils/common_utils';
+import { locales, ES, EN } from '../mock_data';
+
+jest.mock('~/lib/utils/common_utils');
+
+describe('<LanguageSwitcher />', () => {
+ let wrapper;
+
+ const createComponent = (props = {}) => {
+ wrapper = mountExtended(LanguageSwitcherApp, {
+ provide: {
+ locales,
+ preferredLocale: EN,
+ ...props,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const getPreferredLanguage = () => wrapper.find('.gl-dropdown-button-text').text();
+ const findLanguageDropdownItem = (code) => wrapper.findByTestId(`language_switcher_lang_${code}`);
+
+ it('preferred language', () => {
+ expect(getPreferredLanguage()).toBe(EN.text);
+
+ createComponent({
+ preferredLocale: ES,
+ });
+
+ expect(getPreferredLanguage()).toBe(ES.text);
+ });
+
+ it('switches language', async () => {
+ // because window.location is **READ ONLY** we cannot simply use
+ // jest.spyOn to mock it.
+ const originalLocation = window.location;
+ delete window.location;
+ window.location = {};
+ window.location.reload = jest.fn();
+ const reloadSpy = window.location.reload;
+ expect(reloadSpy).not.toHaveBeenCalled();
+ expect(utils.setCookie).not.toHaveBeenCalled();
+
+ const es = findLanguageDropdownItem(ES.value);
+
+ await es.trigger('click');
+
+ expect(reloadSpy).toHaveBeenCalled();
+ expect(utils.setCookie).toHaveBeenCalledWith(PREFERRED_LANGUAGE_COOKIE_KEY, ES.value);
+ window.location = originalLocation;
+ });
+});
diff --git a/spec/frontend/language_switcher/mock_data.js b/spec/frontend/language_switcher/mock_data.js
new file mode 100644
index 00000000000..548bddf0173
--- /dev/null
+++ b/spec/frontend/language_switcher/mock_data.js
@@ -0,0 +1,26 @@
+export const EN = {
+ value: 'en',
+ text: 'English',
+};
+
+export const ZH_CN = {
+ value: 'zh_CN',
+ text: '简体中文',
+};
+
+export const ES = {
+ value: 'es',
+ text: 'Espanol',
+};
+
+export const ZH_HK = {
+ value: 'zh_HK',
+ text: 'ç¹ä½“中文(香港)',
+};
+
+export const ZH_TW = {
+ value: 'zh_TW',
+ text: 'ç¹ä½“中文(å°æ¹¾ï¼‰',
+};
+
+export const locales = [EN, ZH_CN, ES, ZH_HK, ZH_TW];
diff --git a/spec/frontend/lib/dompurify_spec.js b/spec/frontend/lib/dompurify_spec.js
index 412408ce377..f767a673553 100644
--- a/spec/frontend/lib/dompurify_spec.js
+++ b/spec/frontend/lib/dompurify_spec.js
@@ -94,6 +94,11 @@ describe('~/lib/dompurify', () => {
expect(sanitize('<link rel="stylesheet" href="styles.css">')).toBe('');
});
+ it("doesn't allow form tags", () => {
+ expect(sanitize('<form>')).toBe('');
+ expect(sanitize('<form method="post" action="path"></form>')).toBe('');
+ });
+
describe.each`
type | gon
${'root'} | ${rootGon}
diff --git a/spec/frontend/lib/utils/common_utils_spec.js b/spec/frontend/lib/utils/common_utils_spec.js
index 947c38c8ae8..08ba78cddff 100644
--- a/spec/frontend/lib/utils/common_utils_spec.js
+++ b/spec/frontend/lib/utils/common_utils_spec.js
@@ -1,4 +1,5 @@
import * as commonUtils from '~/lib/utils/common_utils';
+import setWindowLocation from 'helpers/set_window_location_helper';
describe('common_utils', () => {
describe('getPagePath', () => {
@@ -1069,4 +1070,35 @@ describe('common_utils', () => {
expect(result).toEqual([{ hello: '' }, { helloWorld: '' }]);
});
});
+
+ describe('useNewFonts', () => {
+ let beforeGon;
+ const beforeLocation = window.location.href;
+
+ beforeEach(() => {
+ window.gon = window.gon || {};
+ beforeGon = { ...window.gon };
+ });
+
+ describe.each`
+ featureFlag | queryParameter | fontEnabled
+ ${false} | ${false} | ${false}
+ ${true} | ${false} | ${true}
+ ${false} | ${true} | ${true}
+ `('new font', ({ featureFlag, queryParameter, fontEnabled }) => {
+ it(`will ${fontEnabled ? '' : 'NOT '}be applied when feature flag is ${
+ featureFlag ? '' : 'NOT '
+ }set and query parameter is ${queryParameter ? '' : 'NOT '}present`, () => {
+ const search = queryParameter ? `?new_fonts` : '';
+ setWindowLocation(search);
+ window.gon = { features: { newFonts: featureFlag } };
+ expect(commonUtils.useNewFonts()).toBe(fontEnabled);
+ });
+ });
+
+ afterEach(() => {
+ window.gon = beforeGon;
+ setWindowLocation(beforeLocation);
+ });
+ });
});
diff --git a/spec/frontend/lib/utils/create_and_submit_form_spec.js b/spec/frontend/lib/utils/create_and_submit_form_spec.js
new file mode 100644
index 00000000000..9f2472c60f7
--- /dev/null
+++ b/spec/frontend/lib/utils/create_and_submit_form_spec.js
@@ -0,0 +1,73 @@
+import csrf from '~/lib/utils/csrf';
+import { TEST_HOST } from 'helpers/test_constants';
+import { createAndSubmitForm } from '~/lib/utils/create_and_submit_form';
+import { joinPaths } from '~/lib/utils/url_utility';
+
+const TEST_URL = '/foo/bar/lorem';
+const TEST_DATA = {
+ 'test_thing[0]': 'Lorem Ipsum',
+ 'test_thing[1]': 'Dolar Sit',
+ x: 123,
+};
+const TEST_CSRF = 'testcsrf00==';
+
+describe('~/lib/utils/create_and_submit_form', () => {
+ let submitSpy;
+
+ const findForm = () => document.querySelector('form');
+ const findInputsModel = () =>
+ Array.from(findForm().querySelectorAll('input')).map((inputEl) => ({
+ type: inputEl.type,
+ name: inputEl.name,
+ value: inputEl.value,
+ }));
+
+ beforeEach(() => {
+ submitSpy = jest.spyOn(HTMLFormElement.prototype, 'submit');
+ document.head.innerHTML = `<meta name="csrf-token" content="${TEST_CSRF}">`;
+ csrf.init();
+ });
+
+ afterEach(() => {
+ document.head.innerHTML = '';
+ document.body.innerHTML = '';
+ });
+
+ describe('default', () => {
+ beforeEach(() => {
+ createAndSubmitForm({
+ url: TEST_URL,
+ data: TEST_DATA,
+ });
+ });
+
+ it('creates form', () => {
+ const form = findForm();
+
+ expect(form.action).toBe(joinPaths(TEST_HOST, TEST_URL));
+ expect(form.method).toBe('post');
+ expect(form.style).toMatchObject({
+ display: 'none',
+ });
+ });
+
+ it('creates inputs', () => {
+ expect(findInputsModel()).toEqual([
+ ...Object.keys(TEST_DATA).map((key) => ({
+ type: 'hidden',
+ name: key,
+ value: String(TEST_DATA[key]),
+ })),
+ {
+ type: 'hidden',
+ name: 'authenticity_token',
+ value: TEST_CSRF,
+ },
+ ]);
+ });
+
+ it('submits form', () => {
+ expect(submitSpy).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/frontend/lib/utils/dom_utils_spec.js b/spec/frontend/lib/utils/dom_utils_spec.js
index d6bac935970..172f8972653 100644
--- a/spec/frontend/lib/utils/dom_utils_spec.js
+++ b/spec/frontend/lib/utils/dom_utils_spec.js
@@ -10,6 +10,7 @@ import {
getParents,
getParentByTagName,
setAttributes,
+ replaceCommentsWith,
} from '~/lib/utils/dom_utils';
const TEST_MARGIN = 5;
@@ -263,4 +264,21 @@ describe('DOM Utils', () => {
expect(getContentWrapperHeight('.does-not-exist')).toBe('');
});
});
+
+ describe('replaceCommentsWith', () => {
+ let div;
+ beforeEach(() => {
+ div = document.createElement('div');
+ });
+
+ it('replaces the comments in a DOM node with an element', () => {
+ div.innerHTML = '<h1> hi there <!-- some comment --> <p> <!-- another comment -->';
+
+ replaceCommentsWith(div, 'comment');
+
+ expect(div.innerHTML).toBe(
+ '<h1> hi there <comment> some comment </comment> <p> <comment> another comment </comment></p></h1>',
+ );
+ });
+ });
});
diff --git a/spec/frontend/lib/utils/poll_until_complete_spec.js b/spec/frontend/lib/utils/poll_until_complete_spec.js
index 7509f954a84..3ce17ecfc8c 100644
--- a/spec/frontend/lib/utils/poll_until_complete_spec.js
+++ b/spec/frontend/lib/utils/poll_until_complete_spec.js
@@ -1,7 +1,7 @@
import AxiosMockAdapter from 'axios-mock-adapter';
import { TEST_HOST } from 'helpers/test_constants';
import axios from '~/lib/utils/axios_utils';
-import httpStatusCodes from '~/lib/utils/http_status';
+import httpStatusCodes, { HTTP_STATUS_NO_CONTENT } from '~/lib/utils/http_status';
import pollUntilComplete from '~/lib/utils/poll_until_complete';
const endpoint = `${TEST_HOST}/foo`;
@@ -37,7 +37,7 @@ describe('pollUntilComplete', () => {
beforeEach(() => {
mock
.onGet(endpoint)
- .replyOnce(httpStatusCodes.NO_CONTENT, undefined, pollIntervalHeader)
+ .replyOnce(HTTP_STATUS_NO_CONTENT, undefined, pollIntervalHeader)
.onGet(endpoint)
.replyOnce(httpStatusCodes.OK, mockData);
});
diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js
index 2c6b603197d..6afdab455a6 100644
--- a/spec/frontend/lib/utils/url_utility_spec.js
+++ b/spec/frontend/lib/utils/url_utility_spec.js
@@ -759,6 +759,19 @@ describe('URL utility', () => {
});
});
+ describe('cleanEndingSeparator', () => {
+ it.each`
+ path | expected
+ ${'foo/bar'} | ${'foo/bar'}
+ ${'/foo/bar/'} | ${'/foo/bar'}
+ ${'foo/bar//'} | ${'foo/bar'}
+ ${'foo/bar/./'} | ${'foo/bar/.'}
+ ${''} | ${''}
+ `('$path becomes $expected', ({ path, expected }) => {
+ expect(urlUtils.cleanEndingSeparator(path)).toBe(expected);
+ });
+ });
+
describe('joinPaths', () => {
it.each`
paths | expected
diff --git a/spec/frontend/listbox/index_spec.js b/spec/frontend/listbox/index_spec.js
index fd41531796b..0816152f4e3 100644
--- a/spec/frontend/listbox/index_spec.js
+++ b/spec/frontend/listbox/index_spec.js
@@ -1,6 +1,6 @@
import { nextTick } from 'vue';
import { getAllByRole, getByTestId } from '@testing-library/dom';
-import { GlListbox } from '@gitlab/ui';
+import { GlCollapsibleListbox } from '@gitlab/ui';
import { createWrapper } from '@vue/test-utils';
import { initListbox, parseAttributes } from '~/listbox';
import { getFixture, setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
@@ -41,7 +41,7 @@ describe('initListbox', () => {
describe('given a valid element', () => {
let onChangeSpy;
- const listbox = () => createWrapper(instance).findComponent(GlListbox);
+ const listbox = () => createWrapper(instance).findComponent(GlCollapsibleListbox);
const findToggleButton = () => getByTestId(document.body, 'base-dropdown-toggle');
const findSelectedItems = () => getAllByRole(document.body, 'option', { selected: true });
diff --git a/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js b/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js
index 4580fdb06f2..f346967121c 100644
--- a/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js
+++ b/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js
@@ -9,6 +9,7 @@ import {
FILTERED_SEARCH_TOKEN_TWO_FACTOR,
FILTERED_SEARCH_TOKEN_WITH_INHERITED_PERMISSIONS,
} from '~/members/constants';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
jest.mock('~/lib/utils/url_utility', () => {
@@ -130,7 +131,7 @@ describe('MembersFilteredSearchBar', () => {
expect(findFilteredSearchBar().props('initialFilterValue')).toEqual([
{
- type: 'filtered-search-term',
+ type: FILTERED_SEARCH_TERM,
value: {
data: 'foobar',
},
@@ -145,7 +146,7 @@ describe('MembersFilteredSearchBar', () => {
expect(findFilteredSearchBar().props('initialFilterValue')).toEqual([
{
- type: 'filtered-search-term',
+ type: FILTERED_SEARCH_TERM,
value: {
data: 'foo bar baz',
},
@@ -174,7 +175,7 @@ describe('MembersFilteredSearchBar', () => {
findFilteredSearchBar().vm.$emit('onFilter', [
{ type: FILTERED_SEARCH_TOKEN_TWO_FACTOR.type, value: { data: 'enabled', operator: '=' } },
- { type: 'filtered-search-term', value: { data: 'foobar' } },
+ { type: FILTERED_SEARCH_TERM, value: { data: 'foobar' } },
]);
expect(redirectTo).toHaveBeenCalledWith(
@@ -187,7 +188,7 @@ describe('MembersFilteredSearchBar', () => {
findFilteredSearchBar().vm.$emit('onFilter', [
{ type: FILTERED_SEARCH_TOKEN_TWO_FACTOR.type, value: { data: 'enabled', operator: '=' } },
- { type: 'filtered-search-term', value: { data: 'foo bar baz' } },
+ { type: FILTERED_SEARCH_TERM, value: { data: 'foo bar baz' } },
]);
expect(redirectTo).toHaveBeenCalledWith(
@@ -202,7 +203,7 @@ describe('MembersFilteredSearchBar', () => {
findFilteredSearchBar().vm.$emit('onFilter', [
{ type: FILTERED_SEARCH_TOKEN_TWO_FACTOR.type, value: { data: 'enabled', operator: '=' } },
- { type: 'filtered-search-term', value: { data: 'foobar' } },
+ { type: FILTERED_SEARCH_TERM, value: { data: 'foobar' } },
]);
expect(redirectTo).toHaveBeenCalledWith(
@@ -216,7 +217,7 @@ describe('MembersFilteredSearchBar', () => {
createComponent();
findFilteredSearchBar().vm.$emit('onFilter', [
- { type: 'filtered-search-term', value: { data: 'foobar' } },
+ { type: FILTERED_SEARCH_TERM, value: { data: 'foobar' } },
]);
expect(redirectTo).toHaveBeenCalledWith('https://localhost/?search=foobar&tab=invited');
diff --git a/spec/frontend/merge_request_tabs_spec.js b/spec/frontend/merge_request_tabs_spec.js
index c6e90a4b20d..69ff5e47689 100644
--- a/spec/frontend/merge_request_tabs_spec.js
+++ b/spec/frontend/merge_request_tabs_spec.js
@@ -303,6 +303,7 @@ describe('MergeRequestTabs', () => {
const tabContent = document.createElement('div');
beforeEach(() => {
+ $.fn.renderGFM = jest.fn();
jest.spyOn(mainContent, 'getBoundingClientRect').mockReturnValue({ top: 10 });
jest.spyOn(tabContent, 'getBoundingClientRect').mockReturnValue({ top: 100 });
jest.spyOn(window, 'scrollTo').mockImplementation(() => {});
diff --git a/spec/frontend/merge_requests/components/target_project_dropdown_spec.js b/spec/frontend/merge_requests/components/target_project_dropdown_spec.js
new file mode 100644
index 00000000000..3fddbe7ae21
--- /dev/null
+++ b/spec/frontend/merge_requests/components/target_project_dropdown_spec.js
@@ -0,0 +1,80 @@
+import { mount } from '@vue/test-utils';
+import { GlCollapsibleListbox } from '@gitlab/ui';
+import MockAdapter from 'axios-mock-adapter';
+import waitForPromises from 'helpers/wait_for_promises';
+import axios from '~/lib/utils/axios_utils';
+import TargetProjectDropdown from '~/merge_requests/components/target_project_dropdown.vue';
+
+let wrapper;
+let mock;
+
+function factory() {
+ wrapper = mount(TargetProjectDropdown, {
+ provide: {
+ targetProjectsPath: '/gitlab-org/gitlab/target_projects',
+ currentProject: { value: 1, text: 'gitlab-org/gitlab' },
+ },
+ });
+}
+
+const findDropdown = () => wrapper.findComponent(GlCollapsibleListbox);
+
+describe('Merge requests target project dropdown component', () => {
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ mock.onGet('/gitlab-org/gitlab/target_projects').reply(200, [
+ {
+ id: 10,
+ name: 'Gitlab Test',
+ full_path: '/root/gitlab-test',
+ full_name: 'Administrator / Gitlab Test',
+ refs_url: '/root/gitlab-test/refs',
+ },
+ {
+ id: 1,
+ name: 'Gitlab Test',
+ full_path: '/gitlab-org/gitlab-test',
+ full_name: 'Gitlab Org / Gitlab Test',
+ refs_url: '/gitlab-org/gitlab-test/refs',
+ },
+ ]);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ mock.restore();
+ });
+
+ it('creates hidden input with currentProject ID', () => {
+ factory();
+
+ expect(wrapper.find('[data-testid="target-project-input"]').attributes('value')).toBe('1');
+ });
+
+ it('renders list of projects', async () => {
+ factory();
+
+ wrapper.find('[data-testid="base-dropdown-toggle"]').trigger('click');
+
+ await waitForPromises();
+
+ expect(wrapper.findAll('li').length).toBe(2);
+ expect(wrapper.findAll('li').at(0).text()).toBe('root/gitlab-test');
+ expect(wrapper.findAll('li').at(1).text()).toBe('gitlab-org/gitlab-test');
+ });
+
+ it('searches projects', async () => {
+ factory();
+
+ wrapper.find('[data-testid="base-dropdown-toggle"]').trigger('click');
+
+ await waitForPromises();
+
+ findDropdown().vm.$emit('search', 'test');
+
+ jest.advanceTimersByTime(500);
+ await waitForPromises();
+
+ expect(mock.history.get[1].params).toEqual({ search: 'test' });
+ });
+});
diff --git a/spec/frontend/milestones/components/milestone_combobox_spec.js b/spec/frontend/milestones/components/milestone_combobox_spec.js
index ce5b2a1000b..c20c51db75e 100644
--- a/spec/frontend/milestones/components/milestone_combobox_spec.js
+++ b/spec/frontend/milestones/components/milestone_combobox_spec.js
@@ -346,7 +346,7 @@ describe('Milestone combobox component', () => {
expect(
findFirstProjectMilestonesDropdownItem()
.find('svg')
- .classes('gl-new-dropdown-item-check-icon'),
+ .classes('gl-dropdown-item-check-icon'),
).toBe(true);
selectFirstProjectMilestone();
@@ -473,7 +473,7 @@ describe('Milestone combobox component', () => {
expect(
findFirstGroupMilestonesDropdownItem()
.find('svg')
- .classes('gl-new-dropdown-item-check-icon'),
+ .classes('gl-dropdown-item-check-icon'),
).toBe(true);
selectFirstGroupMilestone();
diff --git a/spec/frontend/ml/experiment_tracking/components/__snapshots__/experiment_spec.js.snap b/spec/frontend/ml/experiment_tracking/components/__snapshots__/experiment_spec.js.snap
deleted file mode 100644
index 2eba8869535..00000000000
--- a/spec/frontend/ml/experiment_tracking/components/__snapshots__/experiment_spec.js.snap
+++ /dev/null
@@ -1,223 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ShowExperiment with candidates renders correctly 1`] = `
-<div>
- <div
- class="gl-alert gl-alert-warning"
- >
- <svg
- aria-hidden="true"
- class="gl-icon s16 gl-alert-icon"
- data-testid="warning-icon"
- role="img"
- >
- <use
- href="#warning"
- />
- </svg>
-
- <div
- aria-live="assertive"
- class="gl-alert-content"
- role="alert"
- >
- <h2
- class="gl-alert-title"
- >
- Machine Learning Experiment Tracking is in Incubating Phase
- </h2>
-
- <div
- class="gl-alert-body"
- >
-
- GitLab incubates features to explore new use cases. These features are updated regularly, and support is limited
-
- <a
- class="gl-link"
- href="https://about.gitlab.com/handbook/engineering/incubation/"
- rel="noopener noreferrer"
- target="_blank"
- >
- Learn More
- </a>
- </div>
-
- <div
- class="gl-alert-actions"
- >
- <a
- class="btn gl-alert-action btn-confirm btn-md gl-button"
- href="https://gitlab.com/groups/gitlab-org/-/epics/8560"
- >
- <!---->
-
- <!---->
-
- <span
- class="gl-button-text"
- >
-
- Feedback and Updates
-
- </span>
- </a>
- </div>
- </div>
-
- <button
- aria-label="Dismiss"
- class="btn gl-dismiss-btn btn-default btn-sm gl-button btn-default-tertiary btn-icon"
- type="button"
- >
- <!---->
-
- <svg
- aria-hidden="true"
- class="gl-button-icon gl-icon s16"
- data-testid="close-icon"
- role="img"
- >
- <use
- href="#close"
- />
- </svg>
-
- <!---->
- </button>
- </div>
-
- <h3>
-
- Experiment Candidates
-
- </h3>
-
- <table
- aria-busy="false"
- aria-colcount="4"
- class="table b-table gl-table gl-mt-0!"
- role="table"
- >
- <!---->
- <!---->
- <thead
- class=""
- role="rowgroup"
- >
- <!---->
- <tr
- class=""
- role="row"
- >
- <th
- aria-colindex="1"
- class=""
- role="columnheader"
- scope="col"
- >
- <div>
- L1 Ratio
- </div>
- </th>
- <th
- aria-colindex="2"
- class=""
- role="columnheader"
- scope="col"
- >
- <div>
- Rmse
- </div>
- </th>
- <th
- aria-colindex="3"
- class=""
- role="columnheader"
- scope="col"
- >
- <div>
- Auc
- </div>
- </th>
- <th
- aria-colindex="4"
- class=""
- role="columnheader"
- scope="col"
- >
- <div>
- Mae
- </div>
- </th>
- </tr>
- </thead>
- <tbody
- role="rowgroup"
- >
- <!---->
- <tr
- class=""
- role="row"
- >
- <td
- aria-colindex="1"
- class=""
- role="cell"
- >
- 0.4
- </td>
- <td
- aria-colindex="2"
- class=""
- role="cell"
- >
- 1
- </td>
- <td
- aria-colindex="3"
- class=""
- role="cell"
- />
- <td
- aria-colindex="4"
- class=""
- role="cell"
- />
- </tr>
- <tr
- class=""
- role="row"
- >
- <td
- aria-colindex="1"
- class=""
- role="cell"
- >
- 0.5
- </td>
- <td
- aria-colindex="2"
- class=""
- role="cell"
- />
- <td
- aria-colindex="3"
- class=""
- role="cell"
- >
- 0.3
- </td>
- <td
- aria-colindex="4"
- class=""
- role="cell"
- />
- </tr>
- <!---->
- <!---->
- </tbody>
- <!---->
- </table>
-</div>
-`;
diff --git a/spec/frontend/ml/experiment_tracking/components/__snapshots__/ml_candidate_spec.js.snap b/spec/frontend/ml/experiment_tracking/components/__snapshots__/ml_candidate_spec.js.snap
new file mode 100644
index 00000000000..8af0753f929
--- /dev/null
+++ b/spec/frontend/ml/experiment_tracking/components/__snapshots__/ml_candidate_spec.js.snap
@@ -0,0 +1,233 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`MlCandidate renders correctly 1`] = `
+<div>
+ <div
+ class="gl-alert gl-alert-warning"
+ >
+ <svg
+ aria-hidden="true"
+ class="gl-icon s16 gl-alert-icon"
+ data-testid="warning-icon"
+ role="img"
+ >
+ <use
+ href="#warning"
+ />
+ </svg>
+
+ <div
+ aria-live="assertive"
+ class="gl-alert-content"
+ role="alert"
+ >
+ <h2
+ class="gl-alert-title"
+ >
+ Machine Learning Experiment Tracking is in Incubating Phase
+ </h2>
+
+ <div
+ class="gl-alert-body"
+ >
+
+ GitLab incubates features to explore new use cases. These features are updated regularly, and support is limited
+
+ <a
+ class="gl-link"
+ href="https://about.gitlab.com/handbook/engineering/incubation/"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ Learn more
+ </a>
+ </div>
+
+ <div
+ class="gl-alert-actions"
+ >
+ <a
+ class="btn gl-alert-action btn-confirm btn-md gl-button"
+ href="https://gitlab.com/gitlab-org/gitlab/-/issues/381660"
+ >
+ <!---->
+
+ <!---->
+
+ <span
+ class="gl-button-text"
+ >
+
+ Feedback
+
+ </span>
+ </a>
+ </div>
+ </div>
+
+ <button
+ aria-label="Dismiss"
+ class="btn gl-dismiss-btn btn-default btn-sm gl-button btn-default-tertiary btn-icon"
+ type="button"
+ >
+ <!---->
+
+ <svg
+ aria-hidden="true"
+ class="gl-button-icon gl-icon s16"
+ data-testid="close-icon"
+ role="img"
+ >
+ <use
+ href="#close"
+ />
+ </svg>
+
+ <!---->
+ </button>
+ </div>
+
+ <h3>
+
+ Model candidate details
+
+ </h3>
+
+ <table
+ class="candidate-details"
+ >
+ <tbody>
+ <tr
+ class="divider"
+ />
+
+ <tr>
+ <td
+ class="gl-text-secondary gl-font-weight-bold"
+ >
+ Info
+ </td>
+
+ <td
+ class="gl-font-weight-bold"
+ >
+ ID
+ </td>
+
+ <td>
+ candidate_iid
+ </td>
+ </tr>
+
+ <tr>
+ <td />
+
+ <td
+ class="gl-font-weight-bold"
+ >
+ Status
+ </td>
+
+ <td>
+ SUCCESS
+ </td>
+ </tr>
+
+ <tr>
+ <td />
+
+ <td
+ class="gl-font-weight-bold"
+ >
+ Experiment
+ </td>
+
+ <td>
+ <a
+ class="gl-link"
+ href="#"
+ >
+ The Experiment
+ </a>
+ </td>
+ </tr>
+
+ <!---->
+
+ <tr
+ class="divider"
+ />
+
+ <tr>
+ <td
+ class="gl-text-secondary gl-font-weight-bold"
+ >
+
+ Parameters
+
+ </td>
+
+ <td
+ class="gl-font-weight-bold"
+ >
+ Algorithm
+ </td>
+
+ <td>
+ Decision Tree
+ </td>
+ </tr>
+ <tr>
+ <td />
+
+ <td
+ class="gl-font-weight-bold"
+ >
+ MaxDepth
+ </td>
+
+ <td>
+ 3
+ </td>
+ </tr>
+
+ <tr
+ class="divider"
+ />
+
+ <tr>
+ <td
+ class="gl-text-secondary gl-font-weight-bold"
+ >
+
+ Metrics
+
+ </td>
+
+ <td
+ class="gl-font-weight-bold"
+ >
+ AUC
+ </td>
+
+ <td>
+ .55
+ </td>
+ </tr>
+ <tr>
+ <td />
+
+ <td
+ class="gl-font-weight-bold"
+ >
+ Accuracy
+ </td>
+
+ <td>
+ .99
+ </td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+`;
diff --git a/spec/frontend/ml/experiment_tracking/components/__snapshots__/ml_experiment_spec.js.snap b/spec/frontend/ml/experiment_tracking/components/__snapshots__/ml_experiment_spec.js.snap
new file mode 100644
index 00000000000..e253a0afc6c
--- /dev/null
+++ b/spec/frontend/ml/experiment_tracking/components/__snapshots__/ml_experiment_spec.js.snap
@@ -0,0 +1,284 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`MlExperiment with candidates renders correctly 1`] = `
+<div>
+ <div
+ class="gl-alert gl-alert-warning"
+ >
+ <svg
+ aria-hidden="true"
+ class="gl-icon s16 gl-alert-icon"
+ data-testid="warning-icon"
+ role="img"
+ >
+ <use
+ href="#warning"
+ />
+ </svg>
+
+ <div
+ aria-live="assertive"
+ class="gl-alert-content"
+ role="alert"
+ >
+ <h2
+ class="gl-alert-title"
+ >
+ Machine Learning Experiment Tracking is in Incubating Phase
+ </h2>
+
+ <div
+ class="gl-alert-body"
+ >
+
+ GitLab incubates features to explore new use cases. These features are updated regularly, and support is limited
+
+ <a
+ class="gl-link"
+ href="https://about.gitlab.com/handbook/engineering/incubation/"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ Learn more
+ </a>
+ </div>
+
+ <div
+ class="gl-alert-actions"
+ >
+ <a
+ class="btn gl-alert-action btn-confirm btn-md gl-button"
+ href="https://gitlab.com/gitlab-org/gitlab/-/issues/381660"
+ >
+ <!---->
+
+ <!---->
+
+ <span
+ class="gl-button-text"
+ >
+
+ Feedback
+
+ </span>
+ </a>
+ </div>
+ </div>
+
+ <button
+ aria-label="Dismiss"
+ class="btn gl-dismiss-btn btn-default btn-sm gl-button btn-default-tertiary btn-icon"
+ type="button"
+ >
+ <!---->
+
+ <svg
+ aria-hidden="true"
+ class="gl-button-icon gl-icon s16"
+ data-testid="close-icon"
+ role="img"
+ >
+ <use
+ href="#close"
+ />
+ </svg>
+
+ <!---->
+ </button>
+ </div>
+
+ <h3>
+
+ Experiment candidates
+
+ </h3>
+
+ <table
+ aria-busy="false"
+ aria-colcount="6"
+ class="table b-table gl-table gl-mt-0!"
+ role="table"
+ >
+ <!---->
+ <!---->
+ <thead
+ class=""
+ role="rowgroup"
+ >
+ <!---->
+ <tr
+ class=""
+ role="row"
+ >
+ <th
+ aria-colindex="1"
+ class=""
+ role="columnheader"
+ scope="col"
+ >
+ <div>
+ L1 Ratio
+ </div>
+ </th>
+ <th
+ aria-colindex="2"
+ class=""
+ role="columnheader"
+ scope="col"
+ >
+ <div>
+ Rmse
+ </div>
+ </th>
+ <th
+ aria-colindex="3"
+ class=""
+ role="columnheader"
+ scope="col"
+ >
+ <div>
+ Auc
+ </div>
+ </th>
+ <th
+ aria-colindex="4"
+ class=""
+ role="columnheader"
+ scope="col"
+ >
+ <div>
+ Mae
+ </div>
+ </th>
+ <th
+ aria-colindex="5"
+ aria-label="Details"
+ class=""
+ role="columnheader"
+ scope="col"
+ >
+ <div />
+ </th>
+ <th
+ aria-colindex="6"
+ aria-label="Artifact"
+ class=""
+ role="columnheader"
+ scope="col"
+ >
+ <div />
+ </th>
+ </tr>
+ </thead>
+ <tbody
+ role="rowgroup"
+ >
+ <!---->
+ <tr
+ class=""
+ role="row"
+ >
+ <td
+ aria-colindex="1"
+ class=""
+ role="cell"
+ >
+ 0.4
+ </td>
+ <td
+ aria-colindex="2"
+ class=""
+ role="cell"
+ >
+ 1
+ </td>
+ <td
+ aria-colindex="3"
+ class=""
+ role="cell"
+ />
+ <td
+ aria-colindex="4"
+ class=""
+ role="cell"
+ />
+ <td
+ aria-colindex="5"
+ class=""
+ role="cell"
+ >
+ <a
+ class="gl-link"
+ href="link_to_candidate1"
+ >
+ Details
+ </a>
+ </td>
+ <td
+ aria-colindex="6"
+ class=""
+ role="cell"
+ >
+ <a
+ class="gl-link"
+ href="link_to_artifact"
+ rel="noopener"
+ target="_blank"
+ >
+ Artifacts
+ </a>
+ </td>
+ </tr>
+ <tr
+ class=""
+ role="row"
+ >
+ <td
+ aria-colindex="1"
+ class=""
+ role="cell"
+ >
+ 0.5
+ </td>
+ <td
+ aria-colindex="2"
+ class=""
+ role="cell"
+ />
+ <td
+ aria-colindex="3"
+ class=""
+ role="cell"
+ >
+ 0.3
+ </td>
+ <td
+ aria-colindex="4"
+ class=""
+ role="cell"
+ />
+ <td
+ aria-colindex="5"
+ class=""
+ role="cell"
+ >
+ <a
+ class="gl-link"
+ href="link_to_candidate2"
+ >
+ Details
+ </a>
+ </td>
+ <td
+ aria-colindex="6"
+ class=""
+ role="cell"
+ />
+ </tr>
+ <!---->
+ <!---->
+ </tbody>
+ <!---->
+ </table>
+</div>
+`;
diff --git a/spec/frontend/ml/experiment_tracking/components/experiment_spec.js b/spec/frontend/ml/experiment_tracking/components/experiment_spec.js
deleted file mode 100644
index af722d77532..00000000000
--- a/spec/frontend/ml/experiment_tracking/components/experiment_spec.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import { GlAlert } from '@gitlab/ui';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import ShowExperiment from '~/ml/experiment_tracking/components/experiment.vue';
-
-describe('ShowExperiment', () => {
- let wrapper;
-
- const createWrapper = (candidates = [], metricNames = [], paramNames = []) => {
- return mountExtended(ShowExperiment, { provide: { candidates, metricNames, paramNames } });
- };
-
- const findAlert = () => wrapper.findComponent(GlAlert);
-
- const findEmptyState = () => wrapper.findByText('This Experiment has no logged Candidates');
-
- it('shows incubation warning', () => {
- wrapper = createWrapper();
-
- expect(findAlert().exists()).toBe(true);
- });
-
- describe('no candidates', () => {
- it('shows empty state', () => {
- wrapper = createWrapper();
-
- expect(findEmptyState().exists()).toBe(true);
- });
- });
-
- describe('with candidates', () => {
- it('renders correctly', () => {
- wrapper = createWrapper(
- [
- { rmse: 1, l1_ratio: 0.4 },
- { auc: 0.3, l1_ratio: 0.5 },
- ],
- ['rmse', 'auc', 'mae'],
- ['l1_ratio'],
- );
-
- expect(wrapper.element).toMatchSnapshot();
- });
- });
-});
diff --git a/spec/frontend/ml/experiment_tracking/components/incubation_alert_spec.js b/spec/frontend/ml/experiment_tracking/components/incubation_alert_spec.js
index e07a4ed816b..7dca360c7ee 100644
--- a/spec/frontend/ml/experiment_tracking/components/incubation_alert_spec.js
+++ b/spec/frontend/ml/experiment_tracking/components/incubation_alert_spec.js
@@ -15,7 +15,7 @@ describe('IncubationAlert', () => {
it('displays link to issue', () => {
expect(findButton().attributes().href).toBe(
- 'https://gitlab.com/groups/gitlab-org/-/epics/8560',
+ 'https://gitlab.com/gitlab-org/gitlab/-/issues/381660',
);
});
diff --git a/spec/frontend/ml/experiment_tracking/components/ml_candidate_spec.js b/spec/frontend/ml/experiment_tracking/components/ml_candidate_spec.js
new file mode 100644
index 00000000000..4b16312815a
--- /dev/null
+++ b/spec/frontend/ml/experiment_tracking/components/ml_candidate_spec.js
@@ -0,0 +1,43 @@
+import { GlAlert } from '@gitlab/ui';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import MlCandidate from '~/ml/experiment_tracking/components/ml_candidate.vue';
+
+describe('MlCandidate', () => {
+ let wrapper;
+
+ const createWrapper = () => {
+ const candidate = {
+ params: [
+ { name: 'Algorithm', value: 'Decision Tree' },
+ { name: 'MaxDepth', value: '3' },
+ ],
+ metrics: [
+ { name: 'AUC', value: '.55' },
+ { name: 'Accuracy', value: '.99' },
+ ],
+ info: {
+ iid: 'candidate_iid',
+ artifact_link: 'path_to_artifact',
+ experiment_name: 'The Experiment',
+ experiment_path: 'path/to/experiment',
+ status: 'SUCCESS',
+ },
+ };
+
+ return mountExtended(MlCandidate, { provide: { candidate } });
+ };
+
+ const findAlert = () => wrapper.findComponent(GlAlert);
+
+ it('shows incubation warning', () => {
+ wrapper = createWrapper();
+
+ expect(findAlert().exists()).toBe(true);
+ });
+
+ it('renders correctly', () => {
+ wrapper = createWrapper();
+
+ expect(wrapper.element).toMatchSnapshot();
+ });
+});
diff --git a/spec/frontend/ml/experiment_tracking/components/ml_experiment_spec.js b/spec/frontend/ml/experiment_tracking/components/ml_experiment_spec.js
new file mode 100644
index 00000000000..50539440f25
--- /dev/null
+++ b/spec/frontend/ml/experiment_tracking/components/ml_experiment_spec.js
@@ -0,0 +1,44 @@
+import { GlAlert } from '@gitlab/ui';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import MlExperiment from '~/ml/experiment_tracking/components/ml_experiment.vue';
+
+describe('MlExperiment', () => {
+ let wrapper;
+
+ const createWrapper = (candidates = [], metricNames = [], paramNames = []) => {
+ return mountExtended(MlExperiment, { provide: { candidates, metricNames, paramNames } });
+ };
+
+ const findAlert = () => wrapper.findComponent(GlAlert);
+
+ const findEmptyState = () => wrapper.findByText('This experiment has no logged candidates');
+
+ it('shows incubation warning', () => {
+ wrapper = createWrapper();
+
+ expect(findAlert().exists()).toBe(true);
+ });
+
+ describe('no candidates', () => {
+ it('shows empty state', () => {
+ wrapper = createWrapper();
+
+ expect(findEmptyState().exists()).toBe(true);
+ });
+ });
+
+ describe('with candidates', () => {
+ it('renders correctly', () => {
+ wrapper = createWrapper(
+ [
+ { rmse: 1, l1_ratio: 0.4, details: 'link_to_candidate1', artifact: 'link_to_artifact' },
+ { auc: 0.3, l1_ratio: 0.5, details: 'link_to_candidate2' },
+ ],
+ ['rmse', 'auc', 'mae'],
+ ['l1_ratio'],
+ );
+
+ expect(wrapper.element).toMatchSnapshot();
+ });
+ });
+});
diff --git a/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap b/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap
index 263d6225a9f..3b4554700b4 100644
--- a/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap
+++ b/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap
@@ -3,7 +3,6 @@
exports[`Dashboard template matches the default snapshot 1`] = `
<div
class="prometheus-graphs"
- data-qa-selector="prometheus_graphs_content"
data-testid="prometheus-graphs"
environmentstate="available"
metricsdashboardbasepath="/monitoring/monitor-project/-/metrics?environment=1"
@@ -40,7 +39,6 @@ exports[`Dashboard template matches the default snapshot 1`] = `
>
<dashboards-dropdown-stub
class="flex-grow-1"
- data-qa-selector="dashboards_filter_dropdown"
defaultbranch="master"
id="monitor-dashboards-dropdown"
toggle-class="dropdown-menu-toggle"
@@ -60,7 +58,6 @@ exports[`Dashboard template matches the default snapshot 1`] = `
class="flex-grow-1"
clearalltext="Clear all"
clearalltextclass="gl-px-5"
- data-qa-selector="environments_dropdown"
data-testid="environments-dropdown"
headertext=""
hideheaderborder="true"
@@ -106,7 +103,6 @@ exports[`Dashboard template matches the default snapshot 1`] = `
<date-time-picker-stub
class="flex-grow-1 show-last-dropdown"
customenabled="true"
- data-qa-selector="range_picker_dropdown"
options="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]"
value="[object Object]"
/>
diff --git a/spec/frontend/monitoring/components/refresh_button_spec.js b/spec/frontend/monitoring/components/refresh_button_spec.js
index e00736954a9..cb300870689 100644
--- a/spec/frontend/monitoring/components/refresh_button_spec.js
+++ b/spec/frontend/monitoring/components/refresh_button_spec.js
@@ -52,20 +52,6 @@ describe('RefreshButton', () => {
expect(findDropdown().props('text')).toBe('Off');
});
- describe('when feature flag disable_metric_dashboard_refresh_rate is on', () => {
- beforeEach(() => {
- createWrapper({
- provide: {
- glFeatures: { disableMetricDashboardRefreshRate: true },
- },
- });
- });
-
- it('refresh rate is not available', () => {
- expect(findDropdown().exists()).toBe(false);
- });
- });
-
describe('refresh rate options', () => {
it('presents multiple options', () => {
expect(findOptions().length).toBeGreaterThan(1);
diff --git a/spec/frontend/monitoring/requests/index_spec.js b/spec/frontend/monitoring/requests/index_spec.js
index 6f9af911a9f..def4bfe9443 100644
--- a/spec/frontend/monitoring/requests/index_spec.js
+++ b/spec/frontend/monitoring/requests/index_spec.js
@@ -2,7 +2,10 @@ import MockAdapter from 'axios-mock-adapter';
import { backoffMockImplementation } from 'helpers/backoff_helper';
import axios from '~/lib/utils/axios_utils';
import * as commonUtils from '~/lib/utils/common_utils';
-import statusCodes from '~/lib/utils/http_status';
+import statusCodes, {
+ HTTP_STATUS_NO_CONTENT,
+ HTTP_STATUS_UNPROCESSABLE_ENTITY,
+} from '~/lib/utils/http_status';
import { getDashboard, getPrometheusQueryData } from '~/monitoring/requests';
import { metricsDashboardResponse } from '../fixture_data';
@@ -37,8 +40,8 @@ describe('monitoring metrics_requests', () => {
});
it('returns a dashboard response after retrying twice', () => {
- mock.onGet(dashboardEndpoint).replyOnce(statusCodes.NO_CONTENT);
- mock.onGet(dashboardEndpoint).replyOnce(statusCodes.NO_CONTENT);
+ mock.onGet(dashboardEndpoint).replyOnce(HTTP_STATUS_NO_CONTENT);
+ mock.onGet(dashboardEndpoint).replyOnce(HTTP_STATUS_NO_CONTENT);
mock.onGet(dashboardEndpoint).reply(statusCodes.OK, response);
return getDashboard(dashboardEndpoint, params).then((data) => {
@@ -81,8 +84,8 @@ describe('monitoring metrics_requests', () => {
it('returns a dashboard response after retrying twice', () => {
// Mock multiple attempts while the cache is filling up
- mock.onGet(prometheusEndpoint).replyOnce(statusCodes.NO_CONTENT);
- mock.onGet(prometheusEndpoint).replyOnce(statusCodes.NO_CONTENT);
+ mock.onGet(prometheusEndpoint).replyOnce(HTTP_STATUS_NO_CONTENT);
+ mock.onGet(prometheusEndpoint).replyOnce(HTTP_STATUS_NO_CONTENT);
mock.onGet(prometheusEndpoint).reply(statusCodes.OK, response); // 3rd attempt
return getPrometheusQueryData(prometheusEndpoint, params).then((data) => {
@@ -116,8 +119,8 @@ describe('monitoring metrics_requests', () => {
it('rejects after retrying twice and getting an HTTP 500 error', () => {
// Mock multiple attempts while the cache is filling up and fails
- mock.onGet(prometheusEndpoint).replyOnce(statusCodes.NO_CONTENT);
- mock.onGet(prometheusEndpoint).replyOnce(statusCodes.NO_CONTENT);
+ mock.onGet(prometheusEndpoint).replyOnce(HTTP_STATUS_NO_CONTENT);
+ mock.onGet(prometheusEndpoint).replyOnce(HTTP_STATUS_NO_CONTENT);
mock.onGet(prometheusEndpoint).reply(500, {
status: 'error',
error: 'An error occurred',
@@ -132,7 +135,7 @@ describe('monitoring metrics_requests', () => {
it.each`
code | reason
${statusCodes.BAD_REQUEST} | ${'Parameters are missing or incorrect'}
- ${statusCodes.UNPROCESSABLE_ENTITY} | ${"Expression can't be executed"}
+ ${HTTP_STATUS_UNPROCESSABLE_ENTITY} | ${"Expression can't be executed"}
${statusCodes.SERVICE_UNAVAILABLE} | ${'Query timed out or aborted'}
`('rejects with details: "$reason" after getting an HTTP $code error', ({ code, reason }) => {
mock.onGet(prometheusEndpoint).reply(code, {
diff --git a/spec/frontend/monitoring/store/actions_spec.js b/spec/frontend/monitoring/store/actions_spec.js
index ca66768c3cc..93af6526c67 100644
--- a/spec/frontend/monitoring/store/actions_spec.js
+++ b/spec/frontend/monitoring/store/actions_spec.js
@@ -4,7 +4,10 @@ import testAction from 'helpers/vuex_action_helper';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import * as commonUtils from '~/lib/utils/common_utils';
-import statusCodes from '~/lib/utils/http_status';
+import statusCodes, {
+ HTTP_STATUS_CREATED,
+ HTTP_STATUS_UNPROCESSABLE_ENTITY,
+} from '~/lib/utils/http_status';
import { ENVIRONMENT_AVAILABLE_STATE } from '~/monitoring/constants';
import getAnnotations from '~/monitoring/queries/get_annotations.query.graphql';
@@ -944,7 +947,7 @@ describe('Monitoring store actions', () => {
});
it('Succesful POST request resolves', async () => {
- mock.onPost(state.dashboardsEndpoint).reply(statusCodes.CREATED, {
+ mock.onPost(state.dashboardsEndpoint).reply(HTTP_STATUS_CREATED, {
dashboard: dashboardGitResponse[1],
});
@@ -969,7 +972,7 @@ describe('Monitoring store actions', () => {
commit_message: 'A new commit message',
});
- mock.onPost(state.dashboardsEndpoint).reply(statusCodes.CREATED, {
+ mock.onPost(state.dashboardsEndpoint).reply(HTTP_STATUS_CREATED, {
dashboard: mockCreatedDashboard,
});
@@ -1133,7 +1136,7 @@ describe('Monitoring store actions', () => {
mock
.onPost(panelPreviewEndpoint, { panel_yaml: mockYmlContent })
- .reply(statusCodes.UNPROCESSABLE_ENTITY, {
+ .reply(HTTP_STATUS_UNPROCESSABLE_ENTITY, {
message: mockErrorMsg,
});
diff --git a/spec/frontend/monitoring/utils_spec.js b/spec/frontend/monitoring/utils_spec.js
index 6c6c3d6b90f..348825c334a 100644
--- a/spec/frontend/monitoring/utils_spec.js
+++ b/spec/frontend/monitoring/utils_spec.js
@@ -435,6 +435,7 @@ describe('monitoring/utils', () => {
describe('setCustomVariablesFromUrl', () => {
beforeEach(() => {
+ window.history.pushState = jest.fn();
jest.spyOn(urlUtils, 'updateHistory');
});
diff --git a/spec/frontend/nav/components/new_nav_toggle_spec.js b/spec/frontend/nav/components/new_nav_toggle_spec.js
new file mode 100644
index 00000000000..f09bdef8caa
--- /dev/null
+++ b/spec/frontend/nav/components/new_nav_toggle_spec.js
@@ -0,0 +1,98 @@
+import { mount, createWrapper } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import { getByText as getByTextHelper } from '@testing-library/dom';
+import { GlToggle } from '@gitlab/ui';
+import axios from '~/lib/utils/axios_utils';
+import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
+import NewNavToggle from '~/nav/components/new_nav_toggle.vue';
+import waitForPromises from 'helpers/wait_for_promises';
+import { createAlert } from '~/flash';
+import { s__ } from '~/locale';
+
+jest.mock('~/flash');
+
+const TEST_ENDPONT = 'https://example.com/toggle';
+
+describe('NewNavToggle', () => {
+ let wrapper;
+
+ const findToggle = () => wrapper.findComponent(GlToggle);
+
+ const createComponent = (propsData = { enabled: false }) => {
+ wrapper = mount(NewNavToggle, {
+ propsData: {
+ endpoint: TEST_ENDPONT,
+ ...propsData,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const getByText = (text, options) =>
+ createWrapper(getByTextHelper(wrapper.element, text, options));
+
+ it('renders its title', () => {
+ createComponent();
+ expect(getByText('Navigation redesign').exists()).toBe(true);
+ });
+
+ describe('when user preference is enabled', () => {
+ beforeEach(() => {
+ createComponent({ enabled: true });
+ });
+
+ it('renders the toggle as enabled', () => {
+ expect(findToggle().props('value')).toBe(true);
+ });
+ });
+
+ describe('when user preference is disabled', () => {
+ beforeEach(() => {
+ createComponent({ enabled: false });
+ });
+
+ it('renders the toggle as disabled', () => {
+ expect(findToggle().props('value')).toBe(false);
+ });
+ });
+
+ describe('changing the toggle', () => {
+ useMockLocationHelper();
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ createComponent();
+ });
+
+ it('reloads the page on success', async () => {
+ mock.onPut(TEST_ENDPONT).reply(200);
+ findToggle().vm.$emit('change');
+ await waitForPromises();
+
+ expect(window.location.reload).toHaveBeenCalled();
+ });
+
+ it('shows an alert on error', async () => {
+ mock.onPut(TEST_ENDPONT).reply(500);
+ findToggle().vm.$emit('change');
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalledWith(
+ expect.objectContaining({
+ message: s__(
+ 'NorthstarNavigation|Could not update the new navigation preference. Please try again later.',
+ ),
+ }),
+ );
+ expect(window.location.reload).not.toHaveBeenCalled();
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+ });
+});
diff --git a/spec/frontend/notes/components/discussion_notes_spec.js b/spec/frontend/notes/components/discussion_notes_spec.js
index a74d709ed3a..add2ed1ba8a 100644
--- a/spec/frontend/notes/components/discussion_notes_spec.js
+++ b/spec/frontend/notes/components/discussion_notes_spec.js
@@ -1,6 +1,5 @@
import { getByRole } from '@testing-library/dom';
import { shallowMount, mount } from '@vue/test-utils';
-import '~/behaviors/markdown/render_gfm';
import { nextTick } from 'vue';
import DiscussionNotes from '~/notes/components/discussion_notes.vue';
import NoteableNote from '~/notes/components/noteable_note.vue';
@@ -11,6 +10,8 @@ import PlaceholderSystemNote from '~/vue_shared/components/notes/placeholder_sys
import SystemNote from '~/vue_shared/components/notes/system_note.vue';
import { noteableDataMock, discussionMock, notesDataMock } from '../mock_data';
+jest.mock('~/behaviors/markdown/render_gfm');
+
const LINE_RANGE = {};
const DISCUSSION_WITH_LINE_RANGE = {
...discussionMock,
diff --git a/spec/frontend/notes/components/noteable_discussion_spec.js b/spec/frontend/notes/components/noteable_discussion_spec.js
index 2175849aeb9..a90d8bdde06 100644
--- a/spec/frontend/notes/components/noteable_discussion_spec.js
+++ b/spec/frontend/notes/components/noteable_discussion_spec.js
@@ -9,7 +9,6 @@ import ResolveWithIssueButton from '~/notes/components/discussion_resolve_with_i
import NoteForm from '~/notes/components/note_form.vue';
import NoteableDiscussion from '~/notes/components/noteable_discussion.vue';
import createStore from '~/notes/stores';
-import '~/behaviors/markdown/render_gfm';
import {
noteableDataMock,
discussionMock,
@@ -18,6 +17,8 @@ import {
userDataMock,
} from '../mock_data';
+jest.mock('~/behaviors/markdown/render_gfm');
+
describe('noteable_discussion component', () => {
let store;
let wrapper;
diff --git a/spec/frontend/notes/components/notes_app_spec.js b/spec/frontend/notes/components/notes_app_spec.js
index 9051fcab97f..0c3d0da4f0f 100644
--- a/spec/frontend/notes/components/notes_app_spec.js
+++ b/spec/frontend/notes/components/notes_app_spec.js
@@ -2,23 +2,24 @@ import { mount, shallowMount } from '@vue/test-utils';
import AxiosMockAdapter from 'axios-mock-adapter';
import $ from 'jquery';
import { nextTick } from 'vue';
-import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import setWindowLocation from 'helpers/set_window_location_helper';
import waitForPromises from 'helpers/wait_for_promises';
import DraftNote from '~/batch_comments/components/draft_note.vue';
import batchComments from '~/batch_comments/stores/modules/batch_comments';
import axios from '~/lib/utils/axios_utils';
+import { getLocationHash } from '~/lib/utils/url_utility';
import * as urlUtility from '~/lib/utils/url_utility';
import CommentForm from '~/notes/components/comment_form.vue';
import NotesApp from '~/notes/components/notes_app.vue';
import NotesActivityHeader from '~/notes/components/notes_activity_header.vue';
import * as constants from '~/notes/constants';
import createStore from '~/notes/stores';
-import '~/behaviors/markdown/render_gfm';
-// TODO: use generated fixture (https://gitlab.com/gitlab-org/gitlab-foss/issues/62491)
import OrderedLayout from '~/vue_shared/components/ordered_layout.vue';
+// TODO: use generated fixture (https://gitlab.com/gitlab-org/gitlab-foss/issues/62491)
import * as mockData from '../mock_data';
+jest.mock('~/behaviors/markdown/render_gfm');
+
const TYPE_COMMENT_FORM = 'comment-form';
const TYPE_NOTES_LIST = 'notes-list';
const TEST_NOTES_FILTER_VALUE = 1;
@@ -26,7 +27,6 @@ const TEST_NOTES_FILTER_VALUE = 1;
const propsData = {
noteableData: mockData.noteableDataMock,
notesData: mockData.notesDataMock,
- userData: mockData.userDataMock,
notesFilters: mockData.notesFilters,
notesFilterValue: TEST_NOTES_FILTER_VALUE,
};
@@ -37,6 +37,19 @@ describe('note_app', () => {
let wrapper;
let store;
+ const initStore = (notesData = propsData.notesData) => {
+ store.dispatch('setNotesData', notesData);
+ store.dispatch('setNoteableData', propsData.noteableData);
+ store.dispatch('setUserData', mockData.userDataMock);
+ store.dispatch('setTargetNoteHash', getLocationHash());
+ // call after mounted hook
+ queueMicrotask(() => {
+ queueMicrotask(() => {
+ store.dispatch('fetchNotes');
+ });
+ });
+ };
+
const findCommentButton = () => wrapper.find('[data-testid="comment-button"]');
const getComponentOrder = () => {
@@ -51,7 +64,9 @@ describe('note_app', () => {
axiosMock = new AxiosMockAdapter(axios);
store = createStore();
+
mountComponent = ({ props = {} } = {}) => {
+ initStore();
return mount(
{
components: {
@@ -60,6 +75,7 @@ describe('note_app', () => {
template: `<div class="js-vue-notes-event">
<notes-app ref="notesApp" v-bind="$attrs" />
</div>`,
+ inheritAttrs: false,
},
{
propsData: {
@@ -77,53 +93,13 @@ describe('note_app', () => {
axiosMock.restore();
});
- describe('set data', () => {
- beforeEach(() => {
- setHTMLFixture('<div class="js-discussions-count"></div>');
-
- axiosMock.onAny().reply(200, []);
- wrapper = mountComponent();
- return waitForPromises();
- });
-
- afterEach(() => {
- resetHTMLFixture();
- });
-
- it('should set notes data', () => {
- expect(store.state.notesData).toEqual(mockData.notesDataMock);
- });
-
- it('should set issue data', () => {
- expect(store.state.noteableData).toEqual(mockData.noteableDataMock);
- });
-
- it('should set user data', () => {
- expect(store.state.userData).toEqual(mockData.userDataMock);
- });
-
- it('should fetch discussions', () => {
- expect(store.state.discussions).toEqual([]);
- });
-
- it('updates discussions badge', () => {
- expect(document.querySelector('.js-discussions-count').textContent).toEqual('0');
- });
- });
-
describe('render', () => {
beforeEach(() => {
- setHTMLFixture('<div class="js-discussions-count"></div>');
-
axiosMock.onAny().reply(mockData.getIndividualNoteResponse);
wrapper = mountComponent();
return waitForPromises();
});
- afterEach(() => {
- resetHTMLFixture();
- });
-
it('should render list of notes', () => {
const note =
mockData.INDIVIDUAL_NOTE_RESPONSE_MAP.GET[
@@ -148,10 +124,6 @@ describe('note_app', () => {
expect(findCommentButton().props('disabled')).toEqual(true);
});
- it('updates discussions badge', () => {
- expect(document.querySelector('.js-discussions-count').textContent).toEqual('2');
- });
-
it('should render notes activity header', () => {
expect(wrapper.findComponent(NotesActivityHeader).props()).toEqual({
notesFilterValue: TEST_NOTES_FILTER_VALUE,
@@ -162,8 +134,6 @@ describe('note_app', () => {
describe('render with comments disabled', () => {
beforeEach(() => {
- setHTMLFixture('<div class="js-discussions-count"></div>');
-
axiosMock.onAny().reply(mockData.getIndividualNoteResponse);
wrapper = mountComponent({
// why: In this integration test, previously we manually set store.state.commentsDisabled
@@ -177,10 +147,6 @@ describe('note_app', () => {
return waitForPromises();
});
- afterEach(() => {
- resetHTMLFixture();
- });
-
it('should not render form when commenting is disabled', () => {
expect(wrapper.find('.js-main-target-form').exists()).toBe(false);
});
@@ -192,8 +158,6 @@ describe('note_app', () => {
describe('timeline view', () => {
beforeEach(() => {
- setHTMLFixture('<div class="js-discussions-count"></div>');
-
axiosMock.onAny().reply(mockData.getIndividualNoteResponse);
store.state.commentsDisabled = false;
store.state.isTimelineEnabled = true;
@@ -202,10 +166,6 @@ describe('note_app', () => {
return waitForPromises();
});
- afterEach(() => {
- resetHTMLFixture();
- });
-
it('should not render comments form', () => {
expect(wrapper.find('.js-main-target-form').exists()).toBe(false);
});
@@ -213,14 +173,9 @@ describe('note_app', () => {
describe('while fetching data', () => {
beforeEach(async () => {
- setHTMLFixture('<div class="js-discussions-count"></div>');
wrapper = mountComponent();
});
- afterEach(() => {
- return waitForPromises().then(() => resetHTMLFixture());
- });
-
it('renders skeleton notes', () => {
expect(wrapper.find('.gl-skeleton-loader-default-container').exists()).toBe(true);
});
@@ -231,10 +186,6 @@ describe('note_app', () => {
'Write a comment or drag your files here…',
);
});
-
- it('should not update discussions badge (it should be blank)', () => {
- expect(document.querySelector('.js-discussions-count').textContent).toEqual('');
- });
});
describe('update note', () => {
@@ -468,7 +419,9 @@ describe('note_app', () => {
describe('fetching discussions', () => {
describe('when note anchor is not present', () => {
it('does not include extra query params', async () => {
- wrapper = shallowMount(NotesApp, { propsData, store: createStore() });
+ store = createStore();
+ initStore();
+ wrapper = shallowMount(NotesApp, { propsData, store });
await waitForPromises();
expect(axiosMock.history.get[0].params).toEqual({ per_page: 20 });
@@ -476,17 +429,16 @@ describe('note_app', () => {
});
describe('when note anchor is present', () => {
- const mountWithNotesFilter = (notesFilter) =>
- shallowMount(NotesApp, {
- propsData: {
- ...propsData,
- notesData: {
- ...propsData.notesData,
- notesFilter,
- },
- },
+ const mountWithNotesFilter = (notesFilter) => {
+ initStore({
+ ...propsData.notesData,
+ notesFilter,
+ });
+ return shallowMount(NotesApp, {
+ propsData,
store: createStore(),
});
+ };
beforeEach(() => {
setWindowLocation('#note_1');
diff --git a/spec/frontend/notes/deprecated_notes_spec.js b/spec/frontend/notes/deprecated_notes_spec.js
index d5e2a189afe..f52c3e28691 100644
--- a/spec/frontend/notes/deprecated_notes_spec.js
+++ b/spec/frontend/notes/deprecated_notes_spec.js
@@ -4,7 +4,6 @@ import MockAdapter from 'axios-mock-adapter';
import $ from 'jquery';
import '~/behaviors/markdown/render_gfm';
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
-import { createSpyObj } from 'helpers/jest_helpers';
import { TEST_HOST } from 'helpers/test_constants';
import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
@@ -254,16 +253,20 @@ describe.skip('Old Notes (~/deprecated_notes.js)', () => {
note: 'heya',
html: '<div>heya</div>',
};
- $notesList = createSpyObj('$notesList', ['find', 'append']);
-
- notes = createSpyObj('notes', [
- 'setupNewNote',
- 'refresh',
- 'collapseLongCommitList',
- 'updateNotesCount',
- 'putConflictEditWarningInPlace',
- ]);
- notes.taskList = createSpyObj('tasklist', ['init']);
+ $notesList = {
+ find: jest.fn(),
+ append: jest.fn(),
+ };
+ notes = {
+ setupNewNote: jest.fn(),
+ refresh: jest.fn(),
+ collapseLongCommitList: jest.fn(),
+ updateNotesCount: jest.fn(),
+ putConflictEditWarningInPlace: jest.fn(),
+ };
+ notes.taskList = {
+ init: jest.fn(),
+ };
notes.note_ids = [];
notes.updatedNotesTrackingMap = {};
@@ -383,11 +386,21 @@ describe.skip('Old Notes (~/deprecated_notes.js)', () => {
discussion_resolvable: false,
diff_discussion_html: false,
};
- $form = createSpyObj('$form', ['closest', 'find']);
+ $form = {
+ closest: jest.fn(),
+ find: jest.fn(),
+ };
$form.length = 1;
- row = createSpyObj('row', ['prevAll', 'first', 'find']);
+ row = {
+ prevAll: jest.fn(),
+ first: jest.fn(),
+ find: jest.fn(),
+ };
- notes = createSpyObj('notes', ['isParallelView', 'updateNotesCount']);
+ notes = {
+ isParallelView: jest.fn(),
+ updateNotesCount: jest.fn(),
+ };
notes.note_ids = [];
jest.spyOn(Notes, 'isNewNote');
@@ -403,7 +416,9 @@ describe.skip('Old Notes (~/deprecated_notes.js)', () => {
let body;
beforeEach(() => {
- body = createSpyObj('body', ['attr']);
+ body = {
+ attr: jest.fn(),
+ };
discussionContainer = { length: 0 };
$form.closest.mockReturnValueOnce(row).mockReturnValue($form);
@@ -462,7 +477,9 @@ describe.skip('Old Notes (~/deprecated_notes.js)', () => {
beforeEach(() => {
noteHTML = '<div></div>';
- $notesList = createSpyObj('$notesList', ['append']);
+ $notesList = {
+ append: jest.fn(),
+ };
$resultantNote = Notes.animateAppendNote(noteHTML, $notesList);
});
@@ -483,7 +500,9 @@ describe.skip('Old Notes (~/deprecated_notes.js)', () => {
beforeEach(() => {
noteHTML = '<div></div>';
- $note = createSpyObj('$note', ['replaceWith']);
+ $note = {
+ replaceWith: jest.fn(),
+ };
$updatedNote = Notes.animateUpdateNote(noteHTML, $note);
});
diff --git a/spec/frontend/notes/stores/actions_spec.js b/spec/frontend/notes/stores/actions_spec.js
index 989dd74b6d0..dce2e5d370d 100644
--- a/spec/frontend/notes/stores/actions_spec.js
+++ b/spec/frontend/notes/stores/actions_spec.js
@@ -3,7 +3,7 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import testAction from 'helpers/vuex_action_helper';
import { TEST_HOST } from 'spec/test_constants';
import Api from '~/api';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import toast from '~/vue_shared/plugins/global_toast';
import { EVENT_ISSUABLE_VUE_APP_CHANGE } from '~/issuable/constants';
import axios from '~/lib/utils/axios_utils';
@@ -13,8 +13,8 @@ import * as actions from '~/notes/stores/actions';
import * as mutationTypes from '~/notes/stores/mutation_types';
import mutations from '~/notes/stores/mutations';
import * as utils from '~/notes/stores/utils';
-import updateIssueLockMutation from '~/sidebar/components/lock/mutations/update_issue_lock.mutation.graphql';
-import updateMergeRequestLockMutation from '~/sidebar/components/lock/mutations/update_merge_request_lock.mutation.graphql';
+import updateIssueLockMutation from '~/sidebar/queries/update_issue_lock.mutation.graphql';
+import updateMergeRequestLockMutation from '~/sidebar/queries/update_merge_request_lock.mutation.graphql';
import promoteTimelineEvent from '~/notes/graphql/promote_timeline_event.mutation.graphql';
import mrWidgetEventHub from '~/vue_merge_request_widget/event_hub';
import notesEventHub from '~/notes/event_hub';
@@ -30,16 +30,12 @@ import {
} from '../mock_data';
const TEST_ERROR_MESSAGE = 'Test error message';
-const mockFlashClose = jest.fn();
-jest.mock('~/flash', () => {
- const flash = jest.fn().mockImplementation(() => {
- return {
- close: mockFlashClose,
- };
- });
-
- return flash;
-});
+const mockAlertDismiss = jest.fn();
+jest.mock('~/flash', () => ({
+ createAlert: jest.fn().mockImplementation(() => ({
+ dismiss: mockAlertDismiss,
+ })),
+}));
jest.mock('~/vue_shared/plugins/global_toast');
@@ -331,13 +327,13 @@ describe('Actions Notes Store', () => {
await startPolling();
expect(axiosMock.history.get).toHaveLength(1);
- expect(createFlash).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
await advanceXMoreIntervals(1);
expect(axiosMock.history.get).toHaveLength(2);
- expect(createFlash).toHaveBeenCalled();
- expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalled();
+ expect(createAlert).toHaveBeenCalledTimes(1);
});
it('resets the failure counter on success', async () => {
@@ -358,14 +354,13 @@ describe('Actions Notes Store', () => {
await advanceXMoreIntervals(1); // Failure #2
// That was the first failure AFTER a success, so we should NOT see the error displayed
- expect(createFlash).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
// Now we'll allow another failure
await advanceXMoreIntervals(1); // Failure #3
// Since this is the second failure in a row, the error should happen
- expect(createFlash).toHaveBeenCalled();
- expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalledTimes(1);
});
it('hides the error display if it exists on success', async () => {
@@ -375,16 +370,14 @@ describe('Actions Notes Store', () => {
await advanceXMoreIntervals(2);
// After two errors, the error should be displayed
- expect(createFlash).toHaveBeenCalled();
- expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalledTimes(1);
axiosMock.reset();
successMock();
await advanceXMoreIntervals(1);
- expect(mockFlashClose).toHaveBeenCalled();
- expect(mockFlashClose).toHaveBeenCalledTimes(1);
+ expect(mockAlertDismiss).toHaveBeenCalledTimes(1);
});
});
});
@@ -869,7 +862,7 @@ describe('Actions Notes Store', () => {
payload,
),
).rejects.toEqual(error);
- expect(createFlash).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
});
});
@@ -885,8 +878,8 @@ describe('Actions Notes Store', () => {
},
{ ...payload, flashContainer },
);
- expect(resp.hasFlash).toBe(true);
- expect(createFlash).toHaveBeenCalledWith({
+ expect(resp.hasAlert).toBe(true);
+ expect(createAlert).toHaveBeenCalledWith({
message: 'Your comment could not be submitted because something went wrong',
parent: flashContainer,
});
@@ -905,7 +898,7 @@ describe('Actions Notes Store', () => {
payload,
);
expect(data).toBe(res);
- expect(createFlash).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
});
});
});
@@ -943,7 +936,7 @@ describe('Actions Notes Store', () => {
['resolveDiscussion', { discussionId }],
['restartPolling'],
]);
- expect(createFlash).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
});
});
@@ -958,7 +951,7 @@ describe('Actions Notes Store', () => {
[mutationTypes.SET_RESOLVING_DISCUSSION, false],
]);
expect(dispatch.mock.calls).toEqual([['stopPolling'], ['restartPolling']]);
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: TEST_ERROR_MESSAGE,
parent: flashContainer,
});
@@ -976,7 +969,7 @@ describe('Actions Notes Store', () => {
[mutationTypes.SET_RESOLVING_DISCUSSION, false],
]);
expect(dispatch.mock.calls).toEqual([['stopPolling'], ['restartPolling']]);
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: 'Something went wrong while applying the suggestion. Please try again.',
parent: flashContainer,
});
@@ -987,7 +980,7 @@ describe('Actions Notes Store', () => {
dispatch.mockReturnValue(Promise.reject());
return testSubmitSuggestion(() => {
- expect(createFlash).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
});
});
});
@@ -1029,7 +1022,7 @@ describe('Actions Notes Store', () => {
['restartPolling'],
]);
- expect(createFlash).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
});
});
@@ -1047,7 +1040,7 @@ describe('Actions Notes Store', () => {
]);
expect(dispatch.mock.calls).toEqual([['stopPolling'], ['restartPolling']]);
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: TEST_ERROR_MESSAGE,
parent: flashContainer,
});
@@ -1068,7 +1061,7 @@ describe('Actions Notes Store', () => {
]);
expect(dispatch.mock.calls).toEqual([['stopPolling'], ['restartPolling']]);
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message:
'Something went wrong while applying the batch of suggestions. Please try again.',
parent: flashContainer,
@@ -1088,7 +1081,7 @@ describe('Actions Notes Store', () => {
[mutationTypes.SET_RESOLVING_DISCUSSION, false],
]);
- expect(createFlash).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
});
});
});
@@ -1234,7 +1227,7 @@ describe('Actions Notes Store', () => {
),
).rejects.toEqual(new Error());
- expect(createFlash).toHaveBeenCalled();
+ expect(createAlert).toHaveBeenCalled();
});
});
});
@@ -1414,7 +1407,7 @@ describe('Actions Notes Store', () => {
return actions
.promoteCommentToTimelineEvent({ commit: commitSpy }, actionArgs)
.then(() => {
- expect(createFlash).toHaveBeenCalledWith(expectedAlertArgs);
+ expect(createAlert).toHaveBeenCalledWith(expectedAlertArgs);
expect(commitSpy).toHaveBeenCalledWith(
mutationTypes.SET_PROMOTE_COMMENT_TO_TIMELINE_PROGRESS,
false,
diff --git a/spec/frontend/observability/observability_app_spec.js b/spec/frontend/observability/observability_app_spec.js
index f0b318e69ec..248b0a2057c 100644
--- a/spec/frontend/observability/observability_app_spec.js
+++ b/spec/frontend/observability/observability_app_spec.js
@@ -1,5 +1,16 @@
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ObservabilityApp from '~/observability/components/observability_app.vue';
+import ObservabilitySkeleton from '~/observability/components/skeleton/index.vue';
+
+import {
+ MESSAGE_EVENT_TYPE,
+ OBSERVABILITY_ROUTES,
+ SKELETON_VARIANT,
+} from '~/observability/constants';
+
+import { darkModeEnabled } from '~/lib/utils/color_utils';
+
+jest.mock('~/lib/utils/color_utils');
describe('Observability root app', () => {
let wrapper;
@@ -12,6 +23,8 @@ describe('Observability root app', () => {
query: { otherQuery: 100 },
};
+ const mockHandleSkeleton = jest.fn();
+
const findIframe = () => wrapper.findByTestId('observability-ui-iframe');
const TEST_IFRAME_SRC = 'https://observe.gitlab.com/9970/?groupId=14485840';
@@ -21,6 +34,9 @@ describe('Observability root app', () => {
propsData: {
observabilityIframeSrc: TEST_IFRAME_SRC,
},
+ stubs: {
+ 'observability-skeleton': ObservabilitySkeleton,
+ },
mocks: {
$router,
$route: route,
@@ -28,46 +44,156 @@ describe('Observability root app', () => {
});
};
+ const dispatchMessageEvent = (message) =>
+ window.dispatchEvent(new MessageEvent('message', message));
+
afterEach(() => {
wrapper.destroy();
});
- it('should render an iframe with observabilityIframeSrc as src', () => {
- mountComponent();
- const iframe = findIframe();
- expect(iframe.exists()).toBe(true);
- expect(iframe.attributes('src')).toBe(TEST_IFRAME_SRC);
+ describe('iframe src', () => {
+ const TEST_USERNAME = 'test-user';
+
+ beforeAll(() => {
+ gon.current_username = TEST_USERNAME;
+ });
+
+ it('should render an iframe with observabilityIframeSrc, decorated with light theme and username', () => {
+ darkModeEnabled.mockReturnValueOnce(false);
+ mountComponent();
+ const iframe = findIframe();
+
+ expect(iframe.exists()).toBe(true);
+ expect(iframe.attributes('src')).toBe(
+ `${TEST_IFRAME_SRC}&theme=light&username=${TEST_USERNAME}`,
+ );
+ });
+
+ it('should render an iframe with observabilityIframeSrc decorated with dark theme and username', () => {
+ darkModeEnabled.mockReturnValueOnce(true);
+ mountComponent();
+ const iframe = findIframe();
+
+ expect(iframe.exists()).toBe(true);
+ expect(iframe.attributes('src')).toBe(
+ `${TEST_IFRAME_SRC}&theme=dark&username=${TEST_USERNAME}`,
+ );
+ });
});
- it('should not call replace method from vue router if message event does not have url', () => {
- mountComponent();
- wrapper.vm.messageHandler({ data: 'some other data' });
- expect(replace).not.toHaveBeenCalled();
+ describe('iframe sandbox', () => {
+ it('should render an iframe with sandbox attributes', () => {
+ mountComponent();
+ const iframe = findIframe();
+
+ expect(iframe.exists()).toBe(true);
+ expect(iframe.attributes('sandbox')).toBe('allow-same-origin allow-forms allow-scripts');
+ });
});
- it.each`
- condition | origin | observability_path | url
- ${'message origin is different from iframe source origin'} | ${'https://example.com'} | ${'/'} | ${'/explore'}
- ${'path is same as before (observability_path)'} | ${'https://observe.gitlab.com'} | ${'/foo?bar=test'} | ${'/foo?bar=test'}
- `(
- 'should not call replace method from vue router if $condition',
- async ({ origin, observability_path, url }) => {
- mountComponent({ ...$route, query: { observability_path } });
- wrapper.vm.messageHandler({ data: { url }, origin });
+ describe('on GOUI_ROUTE_UPDATE', () => {
+ it('should not call replace method from vue router if message event does not have url', () => {
+ mountComponent();
+ dispatchMessageEvent({
+ type: MESSAGE_EVENT_TYPE.GOUI_ROUTE_UPDATE,
+ payload: { data: 'some other data' },
+ });
expect(replace).not.toHaveBeenCalled();
- },
- );
-
- it('should call replace method from vue router on messageHandle call', () => {
- mountComponent();
- wrapper.vm.messageHandler({ data: { url: '/explore' }, origin: 'https://observe.gitlab.com' });
- expect(replace).toHaveBeenCalled();
- expect(replace).toHaveBeenCalledWith({
- name: 'https://gitlab.com/gitlab-org/',
- query: {
- otherQuery: 100,
- observability_path: '/explore',
+ });
+
+ it.each`
+ condition | origin | observability_path | url
+ ${'message origin is different from iframe source origin'} | ${'https://example.com'} | ${'/'} | ${'/explore'}
+ ${'path is same as before (observability_path)'} | ${'https://observe.gitlab.com'} | ${'/foo?bar=test'} | ${'/foo?bar=test'}
+ `(
+ 'should not call replace method from vue router if $condition',
+ async ({ origin, observability_path, url }) => {
+ mountComponent({ ...$route, query: { observability_path } });
+ dispatchMessageEvent({
+ data: { type: MESSAGE_EVENT_TYPE.GOUI_ROUTE_UPDATE, payload: { url } },
+ origin,
+ });
+ expect(replace).not.toHaveBeenCalled();
},
+ );
+
+ it('should call replace method from vue router on message event callback', () => {
+ mountComponent();
+
+ dispatchMessageEvent({
+ data: { type: MESSAGE_EVENT_TYPE.GOUI_ROUTE_UPDATE, payload: { url: '/explore' } },
+ origin: 'https://observe.gitlab.com',
+ });
+
+ expect(replace).toHaveBeenCalled();
+ expect(replace).toHaveBeenCalledWith({
+ name: 'https://gitlab.com/gitlab-org/',
+ query: {
+ otherQuery: 100,
+ observability_path: '/explore',
+ },
+ });
+ });
+ });
+
+ describe('on GOUI_LOADED', () => {
+ beforeEach(() => {
+ mountComponent();
+ wrapper.vm.$refs.iframeSkeleton.handleSkeleton = mockHandleSkeleton;
+ });
+ it('should call handleSkeleton method', () => {
+ dispatchMessageEvent({
+ data: { type: MESSAGE_EVENT_TYPE.GOUI_LOADED },
+ origin: 'https://observe.gitlab.com',
+ });
+ expect(mockHandleSkeleton).toHaveBeenCalled();
+ });
+
+ it('should not call handleSkeleton method if origin is different', () => {
+ dispatchMessageEvent({
+ data: { type: MESSAGE_EVENT_TYPE.GOUI_LOADED },
+ origin: 'https://example.com',
+ });
+ expect(mockHandleSkeleton).not.toHaveBeenCalled();
+ });
+
+ it('should not call handleSkeleton method if event type is different', () => {
+ dispatchMessageEvent({
+ data: { type: 'UNKNOWN_EVENT' },
+ origin: 'https://observe.gitlab.com',
+ });
+ expect(mockHandleSkeleton).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('skeleton variant', () => {
+ it.each`
+ pathDescription | path | variant
+ ${'dashboards'} | ${OBSERVABILITY_ROUTES.DASHBOARDS} | ${SKELETON_VARIANT.DASHBOARDS}
+ ${'explore'} | ${OBSERVABILITY_ROUTES.EXPLORE} | ${SKELETON_VARIANT.EXPLORE}
+ ${'manage dashboards'} | ${OBSERVABILITY_ROUTES.MANAGE} | ${SKELETON_VARIANT.MANAGE}
+ ${'any other'} | ${'unknown/route'} | ${SKELETON_VARIANT.DASHBOARDS}
+ `('renders the $variant skeleton variant for $pathDescription path', ({ path, variant }) => {
+ mountComponent({ ...$route, path });
+ const props = wrapper.findComponent(ObservabilitySkeleton).props();
+
+ expect(props.variant).toBe(variant);
+ });
+ });
+
+ describe('on observability ui unmount', () => {
+ it('should remove message event and should not call replace method from vue router', () => {
+ mountComponent();
+ wrapper.destroy();
+
+ // testing event cleanup logic, should not call on messege event after component is destroyed
+
+ dispatchMessageEvent({
+ data: { type: MESSAGE_EVENT_TYPE.GOUI_ROUTE_UPDATE, payload: { url: '/explore' } },
+ origin: 'https://observe.gitlab.com',
+ });
+
+ expect(replace).not.toHaveBeenCalled();
});
});
});
diff --git a/spec/frontend/observability/skeleton_spec.js b/spec/frontend/observability/skeleton_spec.js
new file mode 100644
index 00000000000..5637c0e6d70
--- /dev/null
+++ b/spec/frontend/observability/skeleton_spec.js
@@ -0,0 +1,96 @@
+import { GlSkeletonLoader } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+
+import ObservabilitySkeleton from '~/observability/components/skeleton/index.vue';
+import DashboardsSkeleton from '~/observability/components/skeleton/dashboards.vue';
+import ExploreSkeleton from '~/observability/components/skeleton/explore.vue';
+import ManageSkeleton from '~/observability/components/skeleton/manage.vue';
+
+import { SKELETON_VARIANT } from '~/observability/constants';
+
+describe('ObservabilitySkeleton component', () => {
+ let wrapper;
+
+ const mountComponent = ({ ...props } = {}) => {
+ wrapper = shallowMountExtended(ObservabilitySkeleton, {
+ propsData: props,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('on mount', () => {
+ beforeEach(() => {
+ jest.spyOn(global, 'setTimeout');
+ mountComponent();
+ });
+
+ it('should call setTimeout on mount and show ObservabilitySkeleton if Observability UI is not loaded yet', () => {
+ jest.runAllTimers();
+
+ expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 500);
+ expect(wrapper.vm.loading).toBe(true);
+ expect(wrapper.vm.timerId).not.toBeNull();
+ });
+
+ it('should call setTimeout on mount and dont show ObservabilitySkeleton if Observability UI is loaded', () => {
+ wrapper.vm.loading = false;
+ jest.runAllTimers();
+
+ expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 500);
+ expect(wrapper.vm.loading).toBe(false);
+ expect(wrapper.vm.timerId).not.toBeNull();
+ });
+ });
+
+ describe('handleSkeleton', () => {
+ it('will not show the skeleton if Observability UI is loaded before', () => {
+ jest.spyOn(global, 'clearTimeout');
+ mountComponent();
+ wrapper.vm.handleSkeleton();
+ expect(clearTimeout).toHaveBeenCalledWith(wrapper.vm.timerId);
+ });
+
+ it('will hide skeleton gracefully after 400ms if skeleton was present on screen before Observability UI', () => {
+ jest.spyOn(global, 'setTimeout');
+ mountComponent();
+ jest.runAllTimers();
+ wrapper.vm.handleSkeleton();
+ jest.runAllTimers();
+
+ expect(setTimeout).toHaveBeenCalledWith(wrapper.vm.hideSkeleton, 400);
+ expect(wrapper.vm.loading).toBe(false);
+ });
+ });
+
+ describe('skeleton variant', () => {
+ it.each`
+ skeletonType | condition | variant
+ ${'dashboards'} | ${'variant is dashboards'} | ${SKELETON_VARIANT.DASHBOARDS}
+ ${'explore'} | ${'variant is explore'} | ${SKELETON_VARIANT.EXPLORE}
+ ${'manage'} | ${'variant is manage'} | ${SKELETON_VARIANT.MANAGE}
+ ${'default'} | ${'variant is not manage, dashboards or explore'} | ${'unknown'}
+ `('should render $skeletonType skeleton if $condition', async ({ skeletonType, variant }) => {
+ mountComponent({ variant });
+ const showsDefaultSkeleton = ![
+ SKELETON_VARIANT.DASHBOARDS,
+ SKELETON_VARIANT.EXPLORE,
+ SKELETON_VARIANT.MANAGE,
+ ].includes(variant);
+ expect(wrapper.findComponent(DashboardsSkeleton).exists()).toBe(
+ skeletonType === SKELETON_VARIANT.DASHBOARDS,
+ );
+ expect(wrapper.findComponent(ExploreSkeleton).exists()).toBe(
+ skeletonType === SKELETON_VARIANT.EXPLORE,
+ );
+ expect(wrapper.findComponent(ManageSkeleton).exists()).toBe(
+ skeletonType === SKELETON_VARIANT.MANAGE,
+ );
+
+ expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(showsDefaultSkeleton);
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_alert_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_alert_spec.js
index 4a026f35822..d45b993b5a2 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_alert_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_alert_spec.js
@@ -6,7 +6,6 @@ import {
DELETE_TAG_ERROR_MESSAGE,
DELETE_TAGS_SUCCESS_MESSAGE,
DELETE_TAGS_ERROR_MESSAGE,
- DETAILS_IMPORTING_ERROR_MESSAGE,
ADMIN_GARBAGE_COLLECTION_TIP,
} from '~/packages_and_registries/container_registry/explorer/constants';
@@ -77,7 +76,6 @@ describe('Delete alert', () => {
});
});
});
-
describe('error states', () => {
describe.each`
deleteAlertType | message
@@ -107,25 +105,6 @@ describe('Delete alert', () => {
});
});
- describe('importing repository error state', () => {
- beforeEach(() => {
- mountComponent({
- deleteAlertType: 'danger_importing',
- containerRegistryImportingHelpPagePath: 'https://foobar',
- });
- });
-
- it('alert exist and text is appropriate', () => {
- expect(findAlert().text()).toMatchInterpolatedText(DETAILS_IMPORTING_ERROR_MESSAGE);
- });
-
- it('alert body contains link', () => {
- const alertLink = findLink();
- expect(alertLink.exists()).toBe(true);
- expect(alertLink.attributes('href')).toBe('https://foobar');
- });
- });
-
describe('dismissing alert', () => {
it('GlAlert dismiss event triggers a change event', () => {
mountComponent({ deleteAlertType: 'success_tags' });
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js
index b163557618e..1017ff06a25 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js
@@ -18,7 +18,7 @@ import {
NO_TAGS_MATCHING_FILTERS_TITLE,
NO_TAGS_MATCHING_FILTERS_DESCRIPTION,
} from '~/packages_and_registries/container_registry/explorer/constants/index';
-import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import { tagsMock, imageTagsMock, tagsPageInfo } from '../../mock_data';
describe('Tags List', () => {
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js b/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js
index b11048cd7a2..e5b99f15e8c 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js
@@ -249,15 +249,6 @@ export const graphQLDeleteImageRepositoryTagsMock = {
},
};
-export const graphQLDeleteImageRepositoryTagImportingErrorMock = {
- data: {
- destroyContainerRepositoryTags: {
- errors: ['repository importing'],
- __typename: 'DestroyContainerRepositoryTagsPayload',
- },
- },
-};
-
export const dockerCommands = {
dockerBuildCommand: 'foofoo',
dockerPushCommand: 'barbar',
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js
index 310398b01cf..26f0e506829 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js
@@ -18,7 +18,6 @@ import {
UNFINISHED_STATUS,
DELETE_SCHEDULED,
ALERT_DANGER_IMAGE,
- ALERT_DANGER_IMPORTING,
MISSING_OR_DELETED_IMAGE_BREADCRUMB,
MISSING_OR_DELETED_IMAGE_TITLE,
MISSING_OR_DELETED_IMAGE_MESSAGE,
@@ -34,7 +33,6 @@ import Tracking from '~/tracking';
import {
graphQLImageDetailsMock,
graphQLDeleteImageRepositoryTagsMock,
- graphQLDeleteImageRepositoryTagImportingErrorMock,
graphQLProjectImageRepositoriesDetailsMock,
containerRepositoryMock,
graphQLEmptyImageDetailsMock,
@@ -341,7 +339,6 @@ describe('Details Page', () => {
const config = {
isAdmin: true,
garbageCollectionHelpPagePath: 'baz',
- containerRegistryImportingHelpPagePath: 'https://foobar',
};
const deleteAlertType = 'success_tag';
@@ -366,38 +363,6 @@ describe('Details Page', () => {
expect(findDeleteAlert().props()).toEqual({ ...config, deleteAlertType });
});
-
- describe('importing repository error', () => {
- let mutationResolver;
- let tagsResolver;
- let detailsResolver;
-
- beforeEach(async () => {
- mutationResolver = jest
- .fn()
- .mockResolvedValue(graphQLDeleteImageRepositoryTagImportingErrorMock);
- tagsResolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock(imageTagsMock()));
- detailsResolver = jest.fn().mockResolvedValue(graphQLProjectImageRepositoriesDetailsMock);
-
- mountComponent({ mutationResolver, tagsResolver, detailsResolver });
- await waitForApolloRequestRender();
- });
-
- it('displays the proper alert', async () => {
- findTagsList().vm.$emit('delete', [cleanTags[0]]);
- await nextTick();
-
- findDeleteModal().vm.$emit('confirmDelete');
- await waitForPromises();
-
- expect(tagsResolver).toHaveBeenCalled();
- expect(detailsResolver).toHaveBeenCalled();
-
- const deleteAlert = findDeleteAlert();
- expect(deleteAlert.exists()).toBe(true);
- expect(deleteAlert.props('deleteAlertType')).toBe(ALERT_DANGER_IMPORTING);
- });
- });
});
describe('Partial Cleanup Alert', () => {
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js
index 79403d29d18..1e514d85e82 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js
@@ -6,7 +6,6 @@ import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import getContainerRepositoriesQuery from 'shared_queries/container_registry/get_container_repositories.query.graphql';
-import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import DeleteImage from '~/packages_and_registries/container_registry/explorer/components/delete_image.vue';
import CliCommands from '~/packages_and_registries/shared/components/cli_commands.vue';
import GroupEmptyState from '~/packages_and_registries/container_registry/explorer/components/list_page/group_empty_state.vue';
@@ -23,6 +22,7 @@ import getContainerRepositoriesDetails from '~/packages_and_registries/container
import component from '~/packages_and_registries/container_registry/explorer/pages/list.vue';
import Tracking from '~/tracking';
import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import { $toast } from 'jest/packages_and_registries/shared/mocks';
diff --git a/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js b/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js
index fb50d623543..329cc15df97 100644
--- a/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js
+++ b/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js
@@ -14,7 +14,6 @@ import VueApollo from 'vue-apollo';
import MockAdapter from 'axios-mock-adapter';
import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { stripTypenames } from 'helpers/graphql_helpers';
import waitForPromises from 'helpers/wait_for_promises';
import { GRAPHQL_PAGE_SIZE } from '~/packages_and_registries/dependency_proxy/constants';
import axios from '~/lib/utils/axios_utils';
@@ -190,7 +189,7 @@ describe('DependencyProxyApp', () => {
it('shows list', () => {
expect(findManifestList().props()).toMatchObject({
manifests: proxyManifests(),
- pagination: stripTypenames(pagination()),
+ pagination: pagination(),
});
});
diff --git a/spec/frontend/packages_and_registries/dependency_proxy/components/manifest_list_spec.js b/spec/frontend/packages_and_registries/dependency_proxy/components/manifest_list_spec.js
index 9e4c747a1bd..2f415bfd6f9 100644
--- a/spec/frontend/packages_and_registries/dependency_proxy/components/manifest_list_spec.js
+++ b/spec/frontend/packages_and_registries/dependency_proxy/components/manifest_list_spec.js
@@ -1,5 +1,4 @@
import { GlKeysetPagination } from '@gitlab/ui';
-import { stripTypenames } from 'helpers/graphql_helpers';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ManifestRow from '~/packages_and_registries/dependency_proxy/components/manifest_row.vue';
@@ -14,7 +13,7 @@ describe('Manifests List', () => {
const defaultProps = {
manifests: proxyManifests(),
- pagination: stripTypenames(pagination()),
+ pagination: pagination(),
};
const createComponent = (propsData = defaultProps) => {
@@ -60,9 +59,8 @@ describe('Manifests List', () => {
it('has the correct props', () => {
createComponent();
- expect(findPagination().props()).toMatchObject({
- ...defaultProps.pagination,
- });
+ const { __typename, ...paginationProps } = defaultProps.pagination;
+ expect(findPagination().props()).toMatchObject(paginationProps);
});
it('emits the next-page event', () => {
diff --git a/spec/frontend/packages_and_registries/harbor_registry/pages/details_spec.js b/spec/frontend/packages_and_registries/harbor_registry/pages/details_spec.js
index 8fd50bea280..69765d31674 100644
--- a/spec/frontend/packages_and_registries/harbor_registry/pages/details_spec.js
+++ b/spec/frontend/packages_and_registries/harbor_registry/pages/details_spec.js
@@ -8,7 +8,7 @@ import ArtifactsList from '~/packages_and_registries/harbor_registry/components/
import waitForPromises from 'helpers/wait_for_promises';
import DetailsHeader from '~/packages_and_registries/harbor_registry/components/details/details_header.vue';
import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
+import { OPERATORS_IS } from '~/vue_shared/components/filtered_search_bar/constants';
import {
NAME_SORT_FIELD,
TOKEN_TYPE_TAG_NAME,
@@ -137,7 +137,7 @@ describe('Harbor Details Page', () => {
title: s__('HarborRegistry|Tag'),
unique: true,
token: GlFilteredSearchToken,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
},
],
});
diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/packages_list_app_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/packages_list_app_spec.js
index dff95364d7d..d237023d0cd 100644
--- a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/packages_list_app_spec.js
+++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/packages_list_app_spec.js
@@ -7,13 +7,11 @@ import { createAlert, VARIANT_INFO } from '~/flash';
import * as commonUtils from '~/lib/utils/common_utils';
import PackageListApp from '~/packages_and_registries/infrastructure_registry/list/components/packages_list_app.vue';
import { DELETE_PACKAGE_SUCCESS_MESSAGE } from '~/packages_and_registries/infrastructure_registry/list/constants';
-import {
- SHOW_DELETE_SUCCESS_ALERT,
- FILTERED_SEARCH_TERM,
-} from '~/packages_and_registries/shared/constants';
+import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages_and_registries/shared/constants';
import * as packageUtils from '~/packages_and_registries/shared/utils';
import InfrastructureSearch from '~/packages_and_registries/infrastructure_registry/list/components/infrastructure_search.vue';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
jest.mock('~/lib/utils/common_utils');
jest.mock('~/flash');
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/pypi_installation_spec.js.snap b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/pypi_installation_spec.js.snap
index 92c2cd90568..c4020eeb75f 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/pypi_installation_spec.js.snap
+++ b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/pypi_installation_spec.js.snap
@@ -13,7 +13,7 @@ exports[`PypiInstallation renders all the messages 1`] = `
<div>
<div
- class="dropdown b-dropdown gl-new-dropdown btn-group"
+ class="dropdown b-dropdown gl-dropdown btn-group"
id="__BVID__27"
lazy=""
>
@@ -30,7 +30,7 @@ exports[`PypiInstallation renders all the messages 1`] = `
<!---->
<span
- class="gl-new-dropdown-button-text"
+ class="gl-dropdown-button-text"
>
Show PyPi commands
</span>
diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/package_list_row_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/package_list_row_spec.js
index 913b4f5926f..bb04701a8b7 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/list/package_list_row_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/list/package_list_row_spec.js
@@ -1,4 +1,4 @@
-import { GlFormCheckbox, GlSprintf } from '@gitlab/ui';
+import { GlFormCheckbox, GlSprintf, GlTruncate } from '@gitlab/ui';
import Vue, { nextTick } from 'vue';
import VueRouter from 'vue-router';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
@@ -15,7 +15,13 @@ import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { PACKAGE_ERROR_STATUS } from '~/packages_and_registries/package_registry/constants';
import ListItem from '~/vue_shared/components/registry/list_item.vue';
-import { packageData, packagePipelines, packageProject, packageTags } from '../../mock_data';
+import {
+ linksData,
+ packageData,
+ packagePipelines,
+ packageProject,
+ packageTags,
+} from '../../mock_data';
Vue.use(VueRouter);
@@ -26,9 +32,9 @@ describe('packages_list_row', () => {
isGroupPage: false,
};
- const packageWithoutTags = { ...packageData(), project: packageProject() };
+ const packageWithoutTags = { ...packageData(), project: packageProject(), ...linksData };
const packageWithTags = { ...packageWithoutTags, tags: { nodes: packageTags() } };
- const packageCannotDestroy = { ...packageData(), canDestroy: false };
+ const packageCannotDestroy = { ...packageData(), ...linksData, canDestroy: false };
const findPackageTags = () => wrapper.findComponent(PackageTags);
const findPackagePath = () => wrapper.findComponent(PackagePath);
@@ -41,6 +47,7 @@ describe('packages_list_row', () => {
const findCreatedDateText = () => wrapper.findByTestId('created-date');
const findTimeAgoTooltip = () => wrapper.findComponent(TimeagoTooltip);
const findBulkDeleteAction = () => wrapper.findComponent(GlFormCheckbox);
+ const findPackageName = () => wrapper.findComponent(GlTruncate);
const mountComponent = ({
packageEntity = packageWithoutTags,
@@ -81,6 +88,22 @@ describe('packages_list_row', () => {
});
});
+ it('does not have a link to navigate to the details page', () => {
+ mountComponent({
+ packageEntity: {
+ ...packageWithoutTags,
+ _links: {
+ webPath: null,
+ },
+ },
+ });
+
+ expect(findPackageLink().exists()).toBe(false);
+ expect(findPackageName().props()).toMatchObject({
+ text: '@gitlab-org/package-15',
+ });
+ });
+
describe('tags', () => {
it('renders package tags when a package has tags', () => {
mountComponent({ packageEntity: packageWithTags });
diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js
index 19505618ff7..a884959ab62 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js
@@ -10,6 +10,7 @@ import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import { LIST_KEY_CREATED_AT } from '~/packages_and_registries/package_registry/constants';
import { getQueryParams, extractFilterAndSorting } from '~/packages_and_registries/shared/utils';
+import { TOKEN_TYPE_TYPE } from '~/vue_shared/components/filtered_search_bar/constants';
jest.mock('~/packages_and_registries/shared/utils');
@@ -92,7 +93,11 @@ describe('Package Search', () => {
expect(findRegistrySearch().props()).toMatchObject({
tokens: expect.arrayContaining([
- expect.objectContaining({ token: PackageTypeToken, type: 'type', icon: 'package' }),
+ expect.objectContaining({
+ token: PackageTypeToken,
+ type: TOKEN_TYPE_TYPE,
+ icon: 'package',
+ }),
]),
sortableFields: sortableFields(isGroupPage),
});
diff --git a/spec/frontend/packages_and_registries/package_registry/mock_data.js b/spec/frontend/packages_and_registries/package_registry/mock_data.js
index f36c5923532..9e9e08bc196 100644
--- a/spec/frontend/packages_and_registries/package_registry/mock_data.js
+++ b/spec/frontend/packages_and_registries/package_registry/mock_data.js
@@ -118,6 +118,13 @@ export const packageVersions = () => [
},
];
+export const linksData = {
+ _links: {
+ webPath: '/gitlab-org/package-15',
+ __typeName: 'PackageLinks',
+ },
+};
+
export const packageData = (extend) => ({
__typename: 'Package',
id: 'gid://gitlab/Packages::Package/111',
@@ -232,6 +239,7 @@ export const packageDetailsQuery = (extendPackage) => ({
__typename: 'PackageFileConnection',
},
versions: {
+ count: packageVersions().length,
nodes: packageVersions(),
pageInfo: {
hasNextPage: true,
@@ -376,6 +384,7 @@ export const packagesListQuery = ({ type = 'group', extend = {}, extendPaginatio
nodes: [
{
...packageData(),
+ ...linksData,
project: packageProject(),
tags: { nodes: packageTags() },
pipelines: {
@@ -387,6 +396,7 @@ export const packagesListQuery = ({ type = 'group', extend = {}, extendPaginatio
project: packageProject(),
tags: { nodes: [] },
pipelines: { nodes: [] },
+ ...linksData,
},
],
pageInfo: pagination(extendPagination),
diff --git a/spec/frontend/packages_and_registries/package_registry/pages/details_spec.js b/spec/frontend/packages_and_registries/package_registry/pages/details_spec.js
index f942a334f40..eb3b999c1ca 100644
--- a/spec/frontend/packages_and_registries/package_registry/pages/details_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/pages/details_spec.js
@@ -1,4 +1,4 @@
-import { GlEmptyState, GlBadge, GlTabs, GlTab, GlSprintf } from '@gitlab/ui';
+import { GlEmptyState, GlTabs, GlTab, GlSprintf } from '@gitlab/ui';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
@@ -42,6 +42,7 @@ import {
packageFiles,
packageDestroyFilesMutation,
packageDestroyFilesMutationError,
+ pagination,
} from '../mock_data';
jest.mock('~/flash');
@@ -122,7 +123,9 @@ describe('PackagesApp', () => {
const findDeleteFileModal = () => wrapper.findByTestId('delete-file-modal');
const findDeleteFilesModal = () => wrapper.findByTestId('delete-files-modal');
const findVersionsList = () => wrapper.findComponent(PackageVersionsList);
- const findDependenciesCountBadge = () => wrapper.findComponent(GlBadge);
+ const findVersionsCountBadge = () => wrapper.findByTestId('other-versions-badge');
+ const findNoVersionsMessage = () => wrapper.findByTestId('no-versions-message');
+ const findDependenciesCountBadge = () => wrapper.findByTestId('dependencies-badge');
const findNoDependenciesMessage = () => wrapper.findByTestId('no-dependencies-message');
const findDependencyRows = () => wrapper.findAllComponents(DependencyRow);
const findDeletePackage = () => wrapper.findComponent(DeletePackage);
@@ -564,6 +567,30 @@ describe('PackagesApp', () => {
await waitForPromises();
expect(findVersionsList()).toBeDefined();
+ expect(findVersionsCountBadge().exists()).toBe(true);
+ expect(findVersionsCountBadge().text()).toBe(packageVersions().length.toString());
+ });
+
+ it('displays tab with 0 count when package has no other versions', async () => {
+ createComponent({
+ resolver: jest.fn().mockResolvedValue(
+ packageDetailsQuery({
+ versions: {
+ count: 0,
+ nodes: [],
+ pageInfo: pagination({ hasNextPage: false, hasPreviousPage: false }),
+ },
+ }),
+ ),
+ });
+
+ await waitForPromises();
+
+ expect(findVersionsCountBadge().exists()).toBe(true);
+ expect(findVersionsCountBadge().text()).toBe('0');
+ expect(findNoVersionsMessage().text()).toMatchInterpolatedText(
+ 'There are no other versions of this package.',
+ );
});
it('binds the correct props', async () => {
@@ -576,6 +603,7 @@ describe('PackagesApp', () => {
});
});
});
+
describe('dependency links', () => {
it('does not show the dependency links for a non nuget package', async () => {
createComponent();
diff --git a/spec/frontend/packages_and_registries/settings/project/settings/components/container_expiration_policy_form_spec.js b/spec/frontend/packages_and_registries/settings/project/settings/components/container_expiration_policy_form_spec.js
index 8e08864bdb8..cbb5aa52694 100644
--- a/spec/frontend/packages_and_registries/settings/project/settings/components/container_expiration_policy_form_spec.js
+++ b/spec/frontend/packages_and_registries/settings/project/settings/components/container_expiration_policy_form_spec.js
@@ -232,6 +232,7 @@ describe('Container Expiration Policy Settings Form', () => {
describe('form', () => {
describe('form submit event', () => {
useMockLocationHelper();
+ const originalHref = window.location.href;
it('save has type submit', () => {
mountComponent();
@@ -319,7 +320,7 @@ describe('Container Expiration Policy Settings Form', () => {
await submitForm();
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(UPDATE_SETTINGS_ERROR_MESSAGE);
- expect(window.location.href).toBeUndefined();
+ expect(window.location.href).toBe(originalHref);
});
it('parses the error messages', async () => {
diff --git a/spec/frontend/packages_and_registries/shared/utils_spec.js b/spec/frontend/packages_and_registries/shared/utils_spec.js
index 962cb2257ce..d81cdbfd8bd 100644
--- a/spec/frontend/packages_and_registries/shared/utils_spec.js
+++ b/spec/frontend/packages_and_registries/shared/utils_spec.js
@@ -1,4 +1,3 @@
-import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import {
getQueryParams,
keyValueToFilterToken,
@@ -7,6 +6,7 @@ import {
beautifyPath,
getCommitLink,
} from '~/packages_and_registries/shared/utils';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import { packageList } from 'jest/packages_and_registries/infrastructure_registry/components/mock_data';
diff --git a/spec/frontend/pages/dashboard/todos/index/todos_spec.js b/spec/frontend/pages/dashboard/todos/index/todos_spec.js
index 03aed7454e3..825aef27327 100644
--- a/spec/frontend/pages/dashboard/todos/index/todos_spec.js
+++ b/spec/frontend/pages/dashboard/todos/index/todos_spec.js
@@ -4,7 +4,6 @@ import waitForPromises from 'helpers/wait_for_promises';
import '~/lib/utils/common_utils';
import axios from '~/lib/utils/axios_utils';
import { addDelimiter } from '~/lib/utils/text_utility';
-import { visitUrl } from '~/lib/utils/url_utility';
import Todos from '~/pages/dashboard/todos/index/todos';
jest.mock('~/lib/utils/url_utility', () => ({
@@ -15,12 +14,10 @@ const TEST_COUNT_BIG = 2000;
const TEST_DONE_COUNT_BIG = 7300;
describe('Todos', () => {
- let todoItem;
let mock;
beforeEach(() => {
loadHTMLFixture('todos/todos.html');
- todoItem = document.querySelector('.todos-list .todo');
mock = new MockAdapter(axios);
return new Todos();
@@ -34,95 +31,47 @@ describe('Todos', () => {
mock.restore();
});
- describe('goToTodoUrl', () => {
- it('opens the todo url', () => {
- const todoLink = todoItem.dataset.url;
+ describe('on done todo click', () => {
+ let onToggleSpy;
- let expectedUrl = null;
- visitUrl.mockImplementation((url) => {
- expectedUrl = url;
- });
+ beforeEach(() => {
+ const el = document.querySelector('.js-done-todo');
+ const path = el.dataset.href;
- todoItem.click();
+ // Arrange
+ mock
+ .onDelete(path)
+ .replyOnce(200, { count: TEST_COUNT_BIG, done_count: TEST_DONE_COUNT_BIG });
+ onToggleSpy = jest.fn();
+ document.addEventListener('todo:toggle', onToggleSpy);
- expect(expectedUrl).toEqual(todoLink);
- });
-
- describe('meta click', () => {
- let windowOpenSpy;
- let metakeyEvent;
-
- beforeEach(() => {
- metakeyEvent = new MouseEvent('click', { ctrlKey: true });
- windowOpenSpy = jest.spyOn(window, 'open').mockImplementation(() => {});
- });
-
- it('opens the todo url in another tab', () => {
- const todoLink = todoItem.dataset.url;
-
- document.querySelectorAll('.todos-list .todo').forEach((el) => {
- el.dispatchEvent(metakeyEvent);
- });
-
- expect(visitUrl).not.toHaveBeenCalled();
- expect(windowOpenSpy).toHaveBeenCalledWith(todoLink, '_blank');
- });
-
- it('run native funcionality when avatar is clicked', () => {
- document.querySelectorAll('.todos-list a').forEach((el) => {
- el.addEventListener('click', (e) => e.preventDefault());
- });
- document.querySelectorAll('.todos-list img').forEach((el) => {
- el.dispatchEvent(metakeyEvent);
- });
+ // Act
+ el.click();
- expect(visitUrl).not.toHaveBeenCalled();
- expect(windowOpenSpy).not.toHaveBeenCalled();
- });
+ // Wait for axios and HTML to udpate
+ return waitForPromises();
});
- describe('on done todo click', () => {
- let onToggleSpy;
-
- beforeEach(() => {
- const el = document.querySelector('.js-done-todo');
- const path = el.dataset.href;
-
- // Arrange
- mock
- .onDelete(path)
- .replyOnce(200, { count: TEST_COUNT_BIG, done_count: TEST_DONE_COUNT_BIG });
- onToggleSpy = jest.fn();
- document.addEventListener('todo:toggle', onToggleSpy);
-
- // Act
- el.click();
-
- // Wait for axios and HTML to udpate
- return waitForPromises();
- });
-
- it('dispatches todo:toggle', () => {
- expect(onToggleSpy).toHaveBeenCalledWith(
- expect.objectContaining({
- detail: {
- count: TEST_COUNT_BIG,
- },
- }),
- );
- });
+ it('dispatches todo:toggle', () => {
+ expect(onToggleSpy).toHaveBeenCalledWith(
+ expect.objectContaining({
+ detail: {
+ count: TEST_COUNT_BIG,
+ },
+ }),
+ );
+ });
- it('updates pending text', () => {
- expect(document.querySelector('.js-todos-pending .js-todos-badge').innerHTML).toEqual(
- addDelimiter(TEST_COUNT_BIG),
- );
- });
+ it('updates pending text', () => {
+ expect(document.querySelector('.js-todos-pending .js-todos-badge').innerHTML).toEqual(
+ addDelimiter(TEST_COUNT_BIG),
+ );
+ });
- it('updates done text', () => {
- expect(document.querySelector('.js-todos-done .js-todos-badge').innerHTML).toEqual(
- addDelimiter(TEST_DONE_COUNT_BIG),
- );
- });
+ it('updates done text', () => {
+ expect(document.querySelector('.js-todos-done .js-todos-badge').innerHTML).toEqual(
+ addDelimiter(TEST_DONE_COUNT_BIG),
+ );
});
});
});
diff --git a/spec/frontend/pages/import/fogbugz/new_user_map/components/user_select_spec.js b/spec/frontend/pages/import/fogbugz/new_user_map/components/user_select_spec.js
index c1e1545944b..d60730e630b 100644
--- a/spec/frontend/pages/import/fogbugz/new_user_map/components/user_select_spec.js
+++ b/spec/frontend/pages/import/fogbugz/new_user_map/components/user_select_spec.js
@@ -1,6 +1,6 @@
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
-import { GlListbox } from '@gitlab/ui';
+import { GlCollapsibleListbox } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import searchUsersQuery from '~/graphql_shared/queries/users_search_all.query.graphql';
@@ -59,7 +59,7 @@ describe('fogbugz user select component', () => {
const id = 8;
- wrapper.findComponent(GlListbox).vm.$emit('select', `gid://gitlab/User/${id}`);
+ wrapper.findComponent(GlCollapsibleListbox).vm.$emit('select', `gid://gitlab/User/${id}`);
await nextTick();
expect(wrapper.get('input').attributes('value')).toBe(id.toString());
@@ -69,7 +69,7 @@ describe('fogbugz user select component', () => {
createComponent();
jest.runOnlyPendingTimers();
- wrapper.findComponent(GlListbox).vm.$emit('search', 'test');
+ wrapper.findComponent(GlCollapsibleListbox).vm.$emit('search', 'test');
await nextTick();
jest.runOnlyPendingTimers();
diff --git a/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js b/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js
index 727c5164cdc..9718d847ed5 100644
--- a/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js
+++ b/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js
@@ -1,5 +1,5 @@
import { GlFormInputGroup, GlFormInput, GlForm, GlFormRadioGroup, GlFormRadio } from '@gitlab/ui';
-import { getByRole, getAllByRole } from '@testing-library/dom';
+import { getByRole } from '@testing-library/dom';
import { mount, shallowMount } from '@vue/test-utils';
import axios from 'axios';
import AxiosMockAdapter from 'axios-mock-adapter';
@@ -133,10 +133,15 @@ describe('ForkForm component', () => {
expect(cancelButton.attributes('href')).toBe(projectFullPath);
});
- const selectedMockNamespace = { name: 'two', full_name: 'two-group/two', id: 2 };
+ const selectedMockNamespace = {
+ name: 'two',
+ full_name: 'two-group/two',
+ id: 2,
+ visibility: 'public',
+ };
- const fillForm = () => {
- findForkUrlInput().vm.$emit('select', selectedMockNamespace);
+ const fillForm = (namespace = selectedMockNamespace) => {
+ findForkUrlInput().vm.$emit('select', namespace);
};
it('has input with csrf token', () => {
@@ -226,66 +231,139 @@ describe('ForkForm component', () => {
},
];
- it('resets the visibility to default "private"', async () => {
+ it('resets the visibility to max allowed below current level', async () => {
+ createFullComponent({ projectVisibility: 'public' }, { namespaces });
+
+ expect(wrapper.vm.form.fields.visibility.value).toBe('public');
+
+ fillForm({
+ name: 'one',
+ id: 1,
+ visibility: 'internal',
+ });
+ await nextTick();
+
+ expect(getByRole(wrapper.element, 'radio', { name: /internal/i }).checked).toBe(true);
+ });
+
+ it('does not reset the visibility when current level is allowed', async () => {
+ createFullComponent({ projectVisibility: 'public' }, { namespaces });
+
+ expect(wrapper.vm.form.fields.visibility.value).toBe('public');
+
+ fillForm({
+ name: 'two',
+ id: 2,
+ visibility: 'public',
+ });
+ await nextTick();
+
+ expect(getByRole(wrapper.element, 'radio', { name: /public/i }).checked).toBe(true);
+ });
+
+ it('does not reset the visibility when visibility cap is increased', async () => {
createFullComponent({ projectVisibility: 'public' }, { namespaces });
expect(wrapper.vm.form.fields.visibility.value).toBe('public');
- fillForm();
+ fillForm({
+ name: 'three',
+ id: 3,
+ visibility: 'internal',
+ });
+ await nextTick();
+
+ fillForm({
+ name: 'four',
+ id: 4,
+ visibility: 'public',
+ });
+ await nextTick();
+
+ expect(getByRole(wrapper.element, 'radio', { name: /internal/i }).checked).toBe(true);
+ });
+
+ it('sets the visibility to be next highest from current when restrictedVisibilityLevels is set', async () => {
+ createFullComponent(
+ { projectVisibility: 'public', restrictedVisibilityLevels: [10] },
+ { namespaces },
+ );
+
+ wrapper.vm.form.fields.visibility.value = 'internal';
+ fillForm({
+ name: 'five',
+ id: 5,
+ visibility: 'public',
+ });
await nextTick();
expect(getByRole(wrapper.element, 'radio', { name: /private/i }).checked).toBe(true);
});
- it('sets the visibility to be null when restrictedVisibilityLevels is set', async () => {
- createFullComponent({ restrictedVisibilityLevels: [10] }, { namespaces });
+ it('sets the visibility to be next lowest from current when nothing lower is allowed', async () => {
+ createFullComponent(
+ { projectVisibility: 'public', restrictedVisibilityLevels: [0] },
+ { namespaces },
+ );
+
+ fillForm({
+ name: 'six',
+ id: 6,
+ visibility: 'private',
+ });
+ await nextTick();
+
+ expect(getByRole(wrapper.element, 'radio', { name: /private/i }).checked).toBe(true);
- fillForm();
+ fillForm({
+ name: 'six',
+ id: 6,
+ visibility: 'public',
+ });
await nextTick();
- const container = getByRole(wrapper.element, 'radiogroup', { name: /visibility/i });
- const visibilityRadios = getAllByRole(container, 'radio');
- expect(visibilityRadios.filter((e) => e.checked)).toHaveLength(0);
+ expect(getByRole(wrapper.element, 'radio', { name: /internal/i }).checked).toBe(true);
});
});
it.each`
- project | restrictedVisibilityLevels
- ${'private'} | ${[]}
- ${'internal'} | ${[]}
- ${'public'} | ${[]}
- ${'private'} | ${[0]}
- ${'private'} | ${[10]}
- ${'private'} | ${[20]}
- ${'private'} | ${[0, 10]}
- ${'private'} | ${[0, 20]}
- ${'private'} | ${[10, 20]}
- ${'private'} | ${[0, 10, 20]}
- ${'internal'} | ${[0]}
- ${'internal'} | ${[10]}
- ${'internal'} | ${[20]}
- ${'internal'} | ${[0, 10]}
- ${'internal'} | ${[0, 20]}
- ${'internal'} | ${[10, 20]}
- ${'internal'} | ${[0, 10, 20]}
- ${'public'} | ${[0]}
- ${'public'} | ${[10]}
- ${'public'} | ${[0, 10]}
- ${'public'} | ${[0, 20]}
- ${'public'} | ${[10, 20]}
- ${'public'} | ${[0, 10, 20]}
- `('checks the correct radio button', ({ project, restrictedVisibilityLevels }) => {
- createFullComponent({
- projectVisibility: project,
- restrictedVisibilityLevels,
- });
+ project | restrictedVisibilityLevels | computedVisibilityLevel
+ ${'private'} | ${[]} | ${'private'}
+ ${'internal'} | ${[]} | ${'internal'}
+ ${'public'} | ${[]} | ${'public'}
+ ${'private'} | ${[0]} | ${'private'}
+ ${'private'} | ${[10]} | ${'private'}
+ ${'private'} | ${[20]} | ${'private'}
+ ${'private'} | ${[0, 10]} | ${'private'}
+ ${'private'} | ${[0, 20]} | ${'private'}
+ ${'private'} | ${[10, 20]} | ${'private'}
+ ${'private'} | ${[0, 10, 20]} | ${'private'}
+ ${'internal'} | ${[0]} | ${'internal'}
+ ${'internal'} | ${[10]} | ${'private'}
+ ${'internal'} | ${[20]} | ${'internal'}
+ ${'internal'} | ${[0, 10]} | ${'private'}
+ ${'internal'} | ${[0, 20]} | ${'internal'}
+ ${'internal'} | ${[10, 20]} | ${'private'}
+ ${'internal'} | ${[0, 10, 20]} | ${'private'}
+ ${'public'} | ${[0]} | ${'public'}
+ ${'public'} | ${[10]} | ${'public'}
+ ${'public'} | ${[0, 10]} | ${'public'}
+ ${'public'} | ${[0, 20]} | ${'internal'}
+ ${'public'} | ${[10, 20]} | ${'private'}
+ ${'public'} | ${[0, 10, 20]} | ${'private'}
+ `(
+ 'checks the correct radio button',
+ ({ project, restrictedVisibilityLevels, computedVisibilityLevel }) => {
+ createFullComponent({
+ projectVisibility: project,
+ restrictedVisibilityLevels,
+ });
- if (restrictedVisibilityLevels.length === 0) {
- expect(wrapper.find('[name="visibility"]:checked').attributes('value')).toBe(project);
- } else {
- expect(wrapper.find('[name="visibility"]:checked').exists()).toBe(false);
- }
- });
+ expect(wrapper.find('[name="visibility"]:checked').attributes('value')).toBe(
+ computedVisibilityLevel,
+ );
+ },
+ );
it.each`
project | namespace | privateIsDisabled | internalIsDisabled | publicIsDisabled | restrictedVisibilityLevels
diff --git a/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap b/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap
index 21a38f066d9..e7c7ec0d336 100644
--- a/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap
+++ b/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap
@@ -36,62 +36,48 @@ exports[`Code Coverage when fetching data is successful matches the snapshot 1`]
<!---->
- <gl-dropdown-stub
+ <gl-base-dropdown-stub
+ ariahaspopup="listbox"
category="primary"
- clearalltext="Clear all"
- clearalltextclass="gl-px-5"
- headertext=""
- hideheaderborder="true"
- highlighteditemstitle="Selected"
- highlighteditemstitleclass="gl-px-5"
+ icon=""
size="medium"
- text="rspec"
+ toggleid="dropdown-toggle-btn-6"
+ toggletext="rspec"
variant="default"
>
- <gl-dropdown-item-stub
- avatarurl=""
- iconcolor=""
- iconname=""
- iconrightarialabel=""
- iconrightname=""
- ischecked="true"
- ischeckitem="true"
- secondarytext=""
- value="rspec"
- >
-
- rspec
-
- </gl-dropdown-item-stub>
- <gl-dropdown-item-stub
- avatarurl=""
- iconcolor=""
- iconname=""
- iconrightarialabel=""
- iconrightname=""
- ischeckitem="true"
- secondarytext=""
- value="cypress"
+ <!---->
+
+ <!---->
+
+ <ul
+ aria-labelledby="dropdown-toggle-btn-6"
+ class="gl-dropdown-contents gl-list-style-none gl-pl-0 gl-mb-0"
+ id="listbox"
+ role="listbox"
+ tabindex="-1"
>
-
- cypress
-
- </gl-dropdown-item-stub>
- <gl-dropdown-item-stub
- avatarurl=""
- iconcolor=""
- iconname=""
- iconrightarialabel=""
- iconrightname=""
- ischeckitem="true"
- secondarytext=""
- value="karma"
- >
-
- karma
-
- </gl-dropdown-item-stub>
- </gl-dropdown-stub>
+ <gl-listbox-item-stub
+ isselected="true"
+ >
+
+ rspec
+
+ </gl-listbox-item-stub>
+ <gl-listbox-item-stub>
+
+ cypress
+
+ </gl-listbox-item-stub>
+ <gl-listbox-item-stub>
+
+ karma
+
+ </gl-listbox-item-stub>
+ </ul>
+
+ <!---->
+
+ </gl-base-dropdown-stub>
</div>
<gl-area-chart-stub
diff --git a/spec/frontend/pages/projects/graphs/code_coverage_spec.js b/spec/frontend/pages/projects/graphs/code_coverage_spec.js
index 2f2edd6b025..e99734963e3 100644
--- a/spec/frontend/pages/projects/graphs/code_coverage_spec.js
+++ b/spec/frontend/pages/projects/graphs/code_coverage_spec.js
@@ -1,4 +1,4 @@
-import { GlAlert, GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { GlAlert, GlListbox, GlListboxItem } from '@gitlab/ui';
import { GlAreaChart } from '@gitlab/ui/dist/charts';
import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
@@ -22,9 +22,10 @@ describe('Code Coverage', () => {
const findAlert = () => wrapper.findComponent(GlAlert);
const findAreaChart = () => wrapper.findComponent(GlAreaChart);
- const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
- const findFirstDropdownItem = () => findAllDropdownItems().at(0);
- const findSecondDropdownItem = () => findAllDropdownItems().at(1);
+ const findListBox = () => wrapper.findComponent(GlListbox);
+ const findListBoxItems = () => wrapper.findAllComponents(GlListboxItem);
+ const findFirstListBoxItem = () => findListBoxItems().at(0);
+ const findSecondListBoxItem = () => findListBoxItems().at(1);
const findDownloadButton = () => wrapper.find('[data-testid="download-button"]');
const createComponent = () => {
@@ -36,6 +37,7 @@ describe('Code Coverage', () => {
graphRef,
graphCsvPath,
},
+ stubs: { GlListbox },
});
};
@@ -142,9 +144,9 @@ describe('Code Coverage', () => {
});
it('renders the dropdown with all custom names as options', () => {
- expect(wrapper.findComponent(GlDropdown).exists()).toBeDefined();
- expect(findAllDropdownItems()).toHaveLength(codeCoverageMockData.length);
- expect(findFirstDropdownItem().text()).toBe(codeCoverageMockData[0].group_name);
+ expect(findListBox().exists()).toBe(true);
+ expect(findListBoxItems()).toHaveLength(codeCoverageMockData.length);
+ expect(findFirstListBoxItem().text()).toBe(codeCoverageMockData[0].group_name);
});
});
@@ -159,19 +161,19 @@ describe('Code Coverage', () => {
});
it('updates the selected dropdown option with an icon', async () => {
- findSecondDropdownItem().vm.$emit('click');
+ findListBox().vm.$emit('select', '1');
await nextTick();
- expect(findFirstDropdownItem().attributes('ischecked')).toBe(undefined);
- expect(findSecondDropdownItem().attributes('ischecked')).toBe('true');
+ expect(findFirstListBoxItem().attributes('isselected')).toBeUndefined();
+ expect(findSecondListBoxItem().attributes('isselected')).toBe('true');
});
it('updates the graph data when selecting a different option in dropdown', async () => {
const originalSelectedData = wrapper.vm.selectedDailyCoverage;
const expectedData = codeCoverageMockData[1];
- findSecondDropdownItem().vm.$emit('click');
+ findListBox().vm.$emit('select', '1');
await nextTick();
diff --git a/spec/frontend/pages/projects/pipeline_schedules/shared/components/timezone_dropdown_spec.js b/spec/frontend/pages/projects/pipeline_schedules/shared/components/timezone_dropdown_spec.js
deleted file mode 100644
index 4cac642bb50..00000000000
--- a/spec/frontend/pages/projects/pipeline_schedules/shared/components/timezone_dropdown_spec.js
+++ /dev/null
@@ -1,116 +0,0 @@
-import { formatUtcOffset, formatTimezone } from '~/lib/utils/datetime_utility';
-import { findTimezoneByIdentifier } from '~/pages/projects/pipeline_schedules/shared/components/timezone_dropdown';
-
-describe('Timezone Dropdown', () => {
- describe('formatUtcOffset', () => {
- it('will convert negative utc offsets in seconds to hours and minutes', () => {
- expect(formatUtcOffset(-21600)).toEqual('- 6');
- });
-
- it('will convert positive utc offsets in seconds to hours and minutes', () => {
- expect(formatUtcOffset(25200)).toEqual('+ 7');
- expect(formatUtcOffset(49500)).toEqual('+ 13.75');
- });
-
- it('will return 0 when given a string', () => {
- expect(formatUtcOffset('BLAH')).toEqual('0');
- expect(formatUtcOffset('$%$%')).toEqual('0');
- });
-
- it('will return 0 when given an array', () => {
- expect(formatUtcOffset(['an', 'array'])).toEqual('0');
- });
-
- it('will return 0 when given an object', () => {
- expect(formatUtcOffset({ some: '', object: '' })).toEqual('0');
- });
-
- it('will return 0 when given null', () => {
- expect(formatUtcOffset(null)).toEqual('0');
- });
-
- it('will return 0 when given undefined', () => {
- expect(formatUtcOffset(undefined)).toEqual('0');
- });
-
- it('will return 0 when given empty input', () => {
- expect(formatUtcOffset('')).toEqual('0');
- });
- });
-
- describe('formatTimezone', () => {
- it('given name: "Chatham Is.", offset: "49500", will format for display as "[UTC + 13.75] Chatham Is."', () => {
- expect(
- formatTimezone({
- name: 'Chatham Is.',
- offset: 49500,
- identifier: 'Pacific/Chatham',
- }),
- ).toEqual('[UTC + 13.75] Chatham Is.');
- });
-
- it('given name: "Saskatchewan", offset: "-21600", will format for display as "[UTC - 6] Saskatchewan"', () => {
- expect(
- formatTimezone({
- name: 'Saskatchewan',
- offset: -21600,
- identifier: 'America/Regina',
- }),
- ).toEqual('[UTC - 6] Saskatchewan');
- });
-
- it('given name: "Accra", offset: "0", will format for display as "[UTC 0] Accra"', () => {
- expect(
- formatTimezone({
- name: 'Accra',
- offset: 0,
- identifier: 'Africa/Accra',
- }),
- ).toEqual('[UTC 0] Accra');
- });
- });
-
- describe('findTimezoneByIdentifier', () => {
- const tzList = [
- {
- identifier: 'Asia/Tokyo',
- name: 'Sapporo',
- offset: 32400,
- },
- {
- identifier: 'Asia/Hong_Kong',
- name: 'Hong Kong',
- offset: 28800,
- },
- {
- identifier: 'Asia/Dhaka',
- name: 'Dhaka',
- offset: 21600,
- },
- ];
-
- const identifier = 'Asia/Dhaka';
- it('returns the correct object if the identifier exists', () => {
- const res = findTimezoneByIdentifier(tzList, identifier);
-
- expect(res).toBe(tzList[2]);
- });
-
- it('returns null if it doesnt find the identifier', () => {
- const res = findTimezoneByIdentifier(tzList, 'Australia/Melbourne');
-
- expect(res).toBeNull();
- });
-
- it('returns null if there is no identifier given', () => {
- expect(findTimezoneByIdentifier(tzList)).toBeNull();
- expect(findTimezoneByIdentifier(tzList, '')).toBeNull();
- });
-
- it('returns null if there is an empty or invalid array given', () => {
- expect(findTimezoneByIdentifier([], identifier)).toBeNull();
- expect(findTimezoneByIdentifier(null, identifier)).toBeNull();
- expect(findTimezoneByIdentifier(undefined, identifier)).toBeNull();
- });
- });
-});
diff --git a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js
index b202a148306..38f7a2e919d 100644
--- a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js
+++ b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js
@@ -24,7 +24,6 @@ const defaultProps = {
buildsAccessLevel: 20,
wikiAccessLevel: 20,
snippetsAccessLevel: 20,
- operationsAccessLevel: 20,
metricsDashboardAccessLevel: 20,
pagesAccessLevel: 10,
analyticsAccessLevel: 20,
@@ -114,9 +113,14 @@ describe('Settings Panel', () => {
const findPackageSettings = () => wrapper.findComponent({ ref: 'package-settings' });
const findPackageAccessLevel = () =>
wrapper.find('[data-testid="package-registry-access-level"]');
- const findPackageAccessLevels = () =>
- wrapper.find('[name="project[project_feature_attributes][package_registry_access_level]"]');
const findPackagesEnabledInput = () => wrapper.find('[name="project[packages_enabled]"]');
+ const findPackageRegistryEnabledInput = () => wrapper.find('[name="package_registry_enabled"]');
+ const findPackageRegistryAccessLevelHiddenInput = () =>
+ wrapper.find(
+ 'input[name="project[project_feature_attributes][package_registry_access_level]"]',
+ );
+ const findPackageRegistryApiForEveryoneEnabledInput = () =>
+ wrapper.find('[name="package_registry_api_for_everyone_enabled"]');
const findPagesSettings = () => wrapper.findComponent({ ref: 'pages-settings' });
const findPagesAccessLevels = () =>
wrapper.find('[name="project[project_feature_attributes][pages_access_level]"]');
@@ -131,9 +135,6 @@ describe('Settings Panel', () => {
wrapper.findComponent({ ref: 'metrics-visibility-settings' });
const findMetricsVisibilityInput = () =>
findMetricsVisibilitySettings().findComponent(ProjectFeatureSetting);
- const findOperationsSettings = () => wrapper.findComponent({ ref: 'operations-settings' });
- const findOperationsVisibilityInput = () =>
- findOperationsSettings().findComponent(ProjectFeatureSetting);
const findConfirmDangerButton = () => wrapper.findComponent(ConfirmDanger);
const findEnvironmentsSettings = () => wrapper.findComponent({ ref: 'environments-settings' });
const findFeatureFlagsSettings = () => wrapper.findComponent({ ref: 'feature-flags-settings' });
@@ -141,6 +142,8 @@ describe('Settings Panel', () => {
wrapper.findComponent({ ref: 'infrastructure-settings' });
const findReleasesSettings = () => wrapper.findComponent({ ref: 'environments-settings' });
const findMonitorSettings = () => wrapper.findComponent({ ref: 'monitor-settings' });
+ const findMonitorVisibilityInput = () =>
+ findMonitorSettings().findComponent(ProjectFeatureSetting);
afterEach(() => {
wrapper.destroy();
@@ -283,7 +286,7 @@ describe('Settings Panel', () => {
});
expect(findRepositoryFeatureProjectRow().props('helpText')).toBe(
- 'View and edit files in this project. Non-project members have only read access.',
+ 'View and edit files in this project. When set to **Everyone With Access** non-project members have only read access.',
);
});
});
@@ -587,28 +590,63 @@ describe('Settings Panel', () => {
expect(findPackageAccessLevel().exists()).toBe(true);
});
+ it('has hidden input field for package registry access level', () => {
+ wrapper = mountComponent({
+ glFeatures: { packageRegistryAccessLevel: true },
+ packagesAvailable: true,
+ });
+
+ expect(findPackageRegistryAccessLevelHiddenInput().exists()).toBe(true);
+ });
+
it.each`
- visibilityLevel | output
- ${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${[[featureAccessLevel.PROJECT_MEMBERS, 'Only Project Members'], [30, 'Everyone']]}
- ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${[[featureAccessLevel.EVERYONE, 'Everyone With Access'], [30, 'Everyone']]}
- ${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${[[30, 'Everyone']]}
+ projectVisibilityLevel | packageRegistryEnabled | packageRegistryApiForEveryoneEnabled | expectedAccessLevel
+ ${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${false} | ${'disabled'} | ${featureAccessLevel.NOT_ENABLED}
+ ${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${true} | ${false} | ${featureAccessLevel.PROJECT_MEMBERS}
+ ${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${true} | ${true} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS}
+ ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${false} | ${'disabled'} | ${featureAccessLevel.NOT_ENABLED}
+ ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${true} | ${false} | ${featureAccessLevel.EVERYONE}
+ ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${true} | ${true} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS}
+ ${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${false} | ${'hidden'} | ${featureAccessLevel.NOT_ENABLED}
+ ${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${true} | ${'hidden'} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS}
`(
- 'renders correct options when visibilityLevel is $visibilityLevel',
- async ({ visibilityLevel, output }) => {
+ 'sets correct access level',
+ async ({
+ projectVisibilityLevel,
+ packageRegistryEnabled,
+ packageRegistryApiForEveryoneEnabled,
+ expectedAccessLevel,
+ }) => {
wrapper = mountComponent({
glFeatures: { packageRegistryAccessLevel: true },
packagesAvailable: true,
currentSettings: {
- visibilityLevel,
+ visibilityLevel: projectVisibilityLevel,
},
});
- expect(findPackageAccessLevels().props('options')).toStrictEqual(output);
+ await findPackageRegistryEnabledInput().vm.$emit('change', packageRegistryEnabled);
+
+ const packageRegistryApiForEveryoneEnabledInput = findPackageRegistryApiForEveryoneEnabledInput();
+
+ if (packageRegistryApiForEveryoneEnabled === 'hidden') {
+ expect(packageRegistryApiForEveryoneEnabledInput.exists()).toBe(false);
+ } else if (packageRegistryApiForEveryoneEnabled === 'disabled') {
+ expect(packageRegistryApiForEveryoneEnabledInput.props('disabled')).toBe(true);
+ } else {
+ expect(packageRegistryApiForEveryoneEnabledInput.props('disabled')).toBe(false);
+ await packageRegistryApiForEveryoneEnabledInput.vm.$emit(
+ 'change',
+ packageRegistryApiForEveryoneEnabled,
+ );
+ }
+
+ expect(wrapper.vm.packageRegistryAccessLevel).toBe(expectedAccessLevel);
},
);
it.each`
- initialProjectVisibilityLevel | newProjectVisibilityLevel | initialPackageRegistryOption | expectedPackageRegistryOption
+ initialProjectVisibilityLevel | newProjectVisibilityLevel | initialAccessLevel | expectedAccessLevel
${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${featureAccessLevel.NOT_ENABLED} | ${featureAccessLevel.NOT_ENABLED}
${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${featureAccessLevel.PROJECT_MEMBERS} | ${featureAccessLevel.EVERYONE}
${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS}
@@ -626,27 +664,25 @@ describe('Settings Panel', () => {
${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${featureAccessLevel.NOT_ENABLED} | ${featureAccessLevel.NOT_ENABLED}
${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS} | ${featureAccessLevel.EVERYONE}
`(
- 'changes option from $initialPackageRegistryOption to $expectedPackageRegistryOption when visibilityLevel changed from $initialProjectVisibilityLevel to $newProjectVisibilityLevel',
+ 'changes access level when project visibility level changed',
async ({
initialProjectVisibilityLevel,
newProjectVisibilityLevel,
- initialPackageRegistryOption,
- expectedPackageRegistryOption,
+ initialAccessLevel,
+ expectedAccessLevel,
}) => {
wrapper = mountComponent({
glFeatures: { packageRegistryAccessLevel: true },
packagesAvailable: true,
currentSettings: {
visibilityLevel: initialProjectVisibilityLevel,
- packageRegistryAccessLevel: initialPackageRegistryOption,
+ packageRegistryAccessLevel: initialAccessLevel,
},
});
await findProjectVisibilityLevelInput().setValue(newProjectVisibilityLevel);
- expect(findPackageAccessLevels().props('value')).toStrictEqual(
- expectedPackageRegistryOption,
- );
+ expect(wrapper.vm.packageRegistryAccessLevel).toBe(expectedAccessLevel);
},
);
});
@@ -751,27 +787,27 @@ describe('Settings Panel', () => {
${featureAccessLevel.EVERYONE} | ${featureAccessLevel.NOT_ENABLED}
${featureAccessLevel.PROJECT_MEMBERS} | ${featureAccessLevel.NOT_ENABLED}
`(
- 'when updating Operations Settings access level from `$before` to `$after`, Metric Dashboard access is updated to `$after` as well',
+ 'when updating Monitor access level from `$before` to `$after`, Metric Dashboard access is updated to `$after` as well',
async ({ before, after }) => {
wrapper = mountComponent({
- currentSettings: { operationsAccessLevel: before, metricsDashboardAccessLevel: before },
+ currentSettings: { monitorAccessLevel: before, metricsDashboardAccessLevel: before },
});
- await findOperationsVisibilityInput().vm.$emit('change', after);
+ await findMonitorVisibilityInput().vm.$emit('change', after);
expect(findMetricsVisibilityInput().props('value')).toBe(after);
},
);
- it('when updating Operations Settings access level from `10` to `20`, Metric Dashboard access is not increased', async () => {
+ it('when updating Monitor access level from `10` to `20`, Metric Dashboard access is not increased', async () => {
wrapper = mountComponent({
currentSettings: {
- operationsAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
+ monitorAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
metricsDashboardAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
},
});
- await findOperationsVisibilityInput().vm.$emit('change', featureAccessLevel.EVERYONE);
+ await findMonitorVisibilityInput().vm.$emit('change', featureAccessLevel.EVERYONE);
expect(findMetricsVisibilityInput().props('value')).toBe(featureAccessLevel.PROJECT_MEMBERS);
});
@@ -780,7 +816,7 @@ describe('Settings Panel', () => {
wrapper = mountComponent({
currentSettings: {
visibilityLevel: VISIBILITY_LEVEL_PUBLIC_INTEGER,
- operationsAccessLevel: featureAccessLevel.EVERYONE,
+ monitorAccessLevel: featureAccessLevel.EVERYONE,
metricsDashboardAccessLevel: featureAccessLevel.EVERYONE,
},
});
@@ -799,84 +835,32 @@ describe('Settings Panel', () => {
});
});
- describe('Operations', () => {
- it('should show the operations toggle', () => {
- wrapper = mountComponent();
-
- expect(findOperationsSettings().exists()).toBe(true);
- });
- });
-
describe('Environments', () => {
- describe('with feature flag', () => {
- it('should show the environments toggle', () => {
- wrapper = mountComponent({
- glFeatures: { splitOperationsVisibilityPermissions: true },
- });
+ it('should show the environments toggle', () => {
+ wrapper = mountComponent({});
- expect(findEnvironmentsSettings().exists()).toBe(true);
- });
- });
- describe('without feature flag', () => {
- it('should not show the environments toggle', () => {
- wrapper = mountComponent({});
-
- expect(findEnvironmentsSettings().exists()).toBe(false);
- });
+ expect(findEnvironmentsSettings().exists()).toBe(true);
});
});
describe('Feature Flags', () => {
- describe('with feature flag', () => {
- it('should show the feature flags toggle', () => {
- wrapper = mountComponent({
- glFeatures: { splitOperationsVisibilityPermissions: true },
- });
-
- expect(findFeatureFlagsSettings().exists()).toBe(true);
- });
- });
- describe('without feature flag', () => {
- it('should not show the feature flags toggle', () => {
- wrapper = mountComponent({});
+ it('should show the feature flags toggle', () => {
+ wrapper = mountComponent({});
- expect(findFeatureFlagsSettings().exists()).toBe(false);
- });
+ expect(findFeatureFlagsSettings().exists()).toBe(true);
});
});
describe('Infrastructure', () => {
- describe('with feature flag', () => {
- it('should show the infrastructure toggle', () => {
- wrapper = mountComponent({
- glFeatures: { splitOperationsVisibilityPermissions: true },
- });
+ it('should show the infrastructure toggle', () => {
+ wrapper = mountComponent({});
- expect(findInfrastructureSettings().exists()).toBe(true);
- });
- });
- describe('without feature flag', () => {
- it('should not show the infrastructure toggle', () => {
- wrapper = mountComponent({});
-
- expect(findInfrastructureSettings().exists()).toBe(false);
- });
+ expect(findInfrastructureSettings().exists()).toBe(true);
});
});
describe('Releases', () => {
- describe('with feature flag', () => {
- it('should show the releases toggle', () => {
- wrapper = mountComponent({
- glFeatures: { splitOperationsVisibilityPermissions: true },
- });
+ it('should show the releases toggle', () => {
+ wrapper = mountComponent({});
- expect(findReleasesSettings().exists()).toBe(true);
- });
- });
- describe('without feature flag', () => {
- it('should not show the releases toggle', () => {
- wrapper = mountComponent({});
-
- expect(findReleasesSettings().exists()).toBe(false);
- });
+ expect(findReleasesSettings().exists()).toBe(true);
});
});
describe('Monitor', () => {
@@ -884,37 +868,20 @@ describe('Settings Panel', () => {
[10, 'Only Project Members'],
[20, 'Everyone With Access'],
];
- describe('with feature flag', () => {
- it('shows Monitor toggle instead of Operations toggle', () => {
- wrapper = mountComponent({
- glFeatures: { splitOperationsVisibilityPermissions: true },
- });
-
- expect(findMonitorSettings().exists()).toBe(true);
- expect(findOperationsSettings().exists()).toBe(false);
- expect(findMonitorSettings().findComponent(ProjectFeatureSetting).props('options')).toEqual(
- expectedAccessLevel,
- );
- });
- it('when monitorAccessLevel is for project members, it is also for everyone', () => {
- wrapper = mountComponent({
- glFeatures: { splitOperationsVisibilityPermissions: true },
- currentSettings: { monitorAccessLevel: featureAccessLevel.PROJECT_MEMBERS },
- });
+ it('shows Monitor toggle instead of Operations toggle', () => {
+ wrapper = mountComponent({});
- expect(findMetricsVisibilityInput().props('value')).toBe(featureAccessLevel.EVERYONE);
- });
+ expect(findMonitorSettings().exists()).toBe(true);
+ expect(findMonitorSettings().findComponent(ProjectFeatureSetting).props('options')).toEqual(
+ expectedAccessLevel,
+ );
});
- describe('without feature flag', () => {
- it('shows Operations toggle instead of Monitor toggle', () => {
- wrapper = mountComponent({});
-
- expect(findMonitorSettings().exists()).toBe(false);
- expect(findOperationsSettings().exists()).toBe(true);
- expect(
- findOperationsSettings().findComponent(ProjectFeatureSetting).props('options'),
- ).toEqual(expectedAccessLevel);
+ it('when monitorAccessLevel is for project members, it is also for everyone', () => {
+ wrapper = mountComponent({
+ currentSettings: { monitorAccessLevel: featureAccessLevel.PROJECT_MEMBERS },
});
+
+ expect(findMetricsVisibilityInput().props('value')).toBe(featureAccessLevel.EVERYONE);
});
});
});
diff --git a/spec/frontend/pages/shared/wikis/components/wiki_content_spec.js b/spec/frontend/pages/shared/wikis/components/wiki_content_spec.js
index 982c81b9272..7c9aae13d25 100644
--- a/spec/frontend/pages/shared/wikis/components/wiki_content_spec.js
+++ b/spec/frontend/pages/shared/wikis/components/wiki_content_spec.js
@@ -3,13 +3,13 @@ import { nextTick } from 'vue';
import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import WikiContent from '~/pages/shared/wikis/components/wiki_content.vue';
-import { renderGFM } from '~/pages/shared/wikis/render_gfm_facade';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status';
import waitForPromises from 'helpers/wait_for_promises';
import { handleLocationHash } from '~/lib/utils/common_utils';
-jest.mock('~/pages/shared/wikis/render_gfm_facade');
+jest.mock('~/behaviors/markdown/render_gfm');
jest.mock('~/lib/utils/common_utils');
describe('pages/shared/wikis/components/wiki_content', () => {
diff --git a/spec/frontend/performance_bar/components/detailed_metric_spec.js b/spec/frontend/performance_bar/components/detailed_metric_spec.js
index 437d51e02ba..5ab2c9abe5d 100644
--- a/spec/frontend/performance_bar/components/detailed_metric_spec.js
+++ b/spec/frontend/performance_bar/components/detailed_metric_spec.js
@@ -1,5 +1,4 @@
import { shallowMount } from '@vue/test-utils';
-import { GlDropdownItem } from '@gitlab/ui';
import { nextTick } from 'vue';
import { trimText } from 'helpers/text_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
@@ -31,12 +30,8 @@ describe('detailedMetric', () => {
const findExpandedBacktraceBtnAtIndex = (index) => findExpandBacktraceBtns().at(index);
const findDetailsLabel = () => wrapper.findByTestId('performance-bar-details-label');
const findSortOrderDropdown = () => wrapper.findByTestId('performance-bar-sort-order');
- const clickSortOrderDropdownItem = (sortOrder) =>
- findSortOrderDropdown()
- .findAllComponents(GlDropdownItem)
- .filter((item) => item.text() === sortOrderOptions[sortOrder])
- .at(0)
- .vm.$emit('click');
+ const selectSortOrder = (sortOrder) =>
+ findSortOrderDropdown().vm.$emit('select', sortOrderOptions[sortOrder].value);
const findEmptyDetailNotice = () => wrapper.findByTestId('performance-bar-empty-detail-notice');
const findAllDetailDurations = () =>
wrapper.findAllByTestId('performance-item-duration').wrappers.map((w) => w.text());
@@ -334,11 +329,11 @@ describe('detailedMetric', () => {
});
it('changes sortOrder on select', async () => {
- clickSortOrderDropdownItem(sortOrders.CHRONOLOGICAL);
+ selectSortOrder(sortOrders.CHRONOLOGICAL);
await nextTick();
expect(findAllDetailDurations()).toEqual(['23ms', '100ms', '75ms']);
- clickSortOrderDropdownItem(sortOrders.DURATION);
+ selectSortOrder(sortOrders.DURATION);
await nextTick();
expect(findAllDetailDurations()).toEqual(['100ms', '75ms', '23ms']);
});
diff --git a/spec/frontend/pipeline_editor/components/code_snippet_alert/code_snippet_alert_spec.js b/spec/frontend/pipeline_editor/components/code_snippet_alert/code_snippet_alert_spec.js
deleted file mode 100644
index d03f12bc249..00000000000
--- a/spec/frontend/pipeline_editor/components/code_snippet_alert/code_snippet_alert_spec.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { within } from '@testing-library/dom';
-import { mount } from '@vue/test-utils';
-import { merge } from 'lodash';
-import { TEST_HOST } from 'helpers/test_constants';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import CodeSnippetAlert from '~/pipeline_editor/components/code_snippet_alert/code_snippet_alert.vue';
-import { CODE_SNIPPET_SOURCE_API_FUZZING } from '~/pipeline_editor/components/code_snippet_alert/constants';
-
-const apiFuzzingConfigurationPath = '/namespace/project/-/security/configuration/api_fuzzing';
-
-describe('EE - CodeSnippetAlert', () => {
- let wrapper;
-
- const createWrapper = (options) => {
- wrapper = extendedWrapper(
- mount(
- CodeSnippetAlert,
- merge(
- {
- provide: {
- configurationPaths: {
- [CODE_SNIPPET_SOURCE_API_FUZZING]: apiFuzzingConfigurationPath,
- },
- },
- propsData: {
- source: CODE_SNIPPET_SOURCE_API_FUZZING,
- },
- },
- options,
- ),
- ),
- );
- };
-
- const withinComponent = () => within(wrapper.element);
- const findDocsLink = () => withinComponent().getByRole('link', { name: /read documentation/i });
- const findConfigurationLink = () =>
- withinComponent().getByRole('link', { name: /Go back to configuration/i });
-
- beforeEach(() => {
- createWrapper();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it("provides a link to the feature's documentation", () => {
- const docsLink = findDocsLink();
-
- expect(docsLink).not.toBe(null);
- expect(docsLink.href).toBe(`${TEST_HOST}/help/user/application_security/api_fuzzing/index`);
- });
-
- it("provides a link to the feature's configuration form", () => {
- const configurationLink = findConfigurationLink();
-
- expect(configurationLink).not.toBe(null);
- expect(configurationLink.href).toBe(TEST_HOST + apiFuzzingConfigurationPath);
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js b/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js
deleted file mode 100644
index 0ee6da9d329..00000000000
--- a/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js
+++ /dev/null
@@ -1,158 +0,0 @@
-import { nextTick } from 'vue';
-import { GlFormInput, GlFormTextarea } from '@gitlab/ui';
-import { shallowMount, mount } from '@vue/test-utils';
-
-import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue';
-
-import { mockCommitMessage, mockDefaultBranch } from '../../mock_data';
-
-const scrollIntoViewMock = jest.fn();
-HTMLElement.prototype.scrollIntoView = scrollIntoViewMock;
-
-describe('Pipeline Editor | Commit Form', () => {
- let wrapper;
-
- const createComponent = ({ props = {} } = {}, mountFn = shallowMount) => {
- wrapper = mountFn(CommitForm, {
- propsData: {
- defaultMessage: mockCommitMessage,
- currentBranch: mockDefaultBranch,
- hasUnsavedChanges: true,
- isNewCiConfigFile: false,
- ...props,
- },
-
- // attachTo is required for input/submit events
- attachTo: mountFn === mount ? document.body : null,
- });
- };
-
- const findCommitTextarea = () => wrapper.findComponent(GlFormTextarea);
- const findBranchInput = () => wrapper.findComponent(GlFormInput);
- const findNewMrCheckbox = () => wrapper.find('[data-testid="new-mr-checkbox"]');
- const findSubmitBtn = () => wrapper.find('[type="submit"]');
- const findCancelBtn = () => wrapper.find('[type="reset"]');
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('when the form is displayed', () => {
- beforeEach(async () => {
- createComponent();
- });
-
- it('shows a default commit message', () => {
- expect(findCommitTextarea().attributes('value')).toBe(mockCommitMessage);
- });
-
- it('shows current branch', () => {
- expect(findBranchInput().attributes('value')).toBe(mockDefaultBranch);
- });
-
- it('shows buttons', () => {
- expect(findSubmitBtn().exists()).toBe(true);
- expect(findCancelBtn().exists()).toBe(true);
- });
-
- it('does not show a new MR checkbox by default', () => {
- expect(findNewMrCheckbox().exists()).toBe(false);
- });
- });
-
- describe('when buttons are clicked', () => {
- beforeEach(async () => {
- createComponent({}, mount);
- });
-
- it('emits an event when the form submits', () => {
- findSubmitBtn().trigger('click');
-
- expect(wrapper.emitted('submit')[0]).toEqual([
- {
- message: mockCommitMessage,
- sourceBranch: mockDefaultBranch,
- openMergeRequest: false,
- },
- ]);
- });
-
- it('emits an event when the form resets', () => {
- findCancelBtn().trigger('click');
-
- expect(wrapper.emitted('resetContent')).toHaveLength(1);
- });
- });
-
- describe('submit button', () => {
- it.each`
- hasUnsavedChanges | isNewCiConfigFile | isDisabled | btnState
- ${false} | ${false} | ${true} | ${'disabled'}
- ${true} | ${false} | ${false} | ${'enabled'}
- ${true} | ${true} | ${false} | ${'enabled'}
- ${false} | ${true} | ${false} | ${'enabled'}
- `(
- 'is $btnState when hasUnsavedChanges:$hasUnsavedChanges and isNewCiConfigfile:$isNewCiConfigFile',
- ({ hasUnsavedChanges, isNewCiConfigFile, isDisabled }) => {
- createComponent({ props: { hasUnsavedChanges, isNewCiConfigFile } });
-
- if (isDisabled) {
- expect(findSubmitBtn().attributes('disabled')).toBe('true');
- } else {
- expect(findSubmitBtn().attributes('disabled')).toBeUndefined();
- }
- },
- );
- });
-
- describe('when user inputs values', () => {
- const anotherMessage = 'Another commit message';
- const anotherBranch = 'my-branch';
-
- beforeEach(() => {
- createComponent({}, mount);
-
- findCommitTextarea().setValue(anotherMessage);
- findBranchInput().setValue(anotherBranch);
- });
-
- it('shows a new MR checkbox', () => {
- expect(findNewMrCheckbox().exists()).toBe(true);
- });
-
- it('emits an event with values', async () => {
- await findNewMrCheckbox().setChecked();
- await findSubmitBtn().trigger('click');
-
- expect(wrapper.emitted('submit')[0]).toEqual([
- {
- message: anotherMessage,
- sourceBranch: anotherBranch,
- openMergeRequest: true,
- },
- ]);
- });
-
- it('when the commit message is empty, submit button is disabled', async () => {
- await findCommitTextarea().setValue('');
-
- expect(findSubmitBtn().attributes('disabled')).toBe('disabled');
- });
- });
-
- describe('when scrollToCommitForm becomes true', () => {
- beforeEach(async () => {
- createComponent();
- wrapper.setProps({ scrollToCommitForm: true });
- await nextTick();
- });
-
- it('scrolls into view', () => {
- expect(scrollIntoViewMock).toHaveBeenCalledWith({ behavior: 'smooth' });
- });
-
- it('emits "scrolled-to-commit-form"', () => {
- expect(wrapper.emitted()['scrolled-to-commit-form']).toHaveLength(1);
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/commit/commit_section_spec.js b/spec/frontend/pipeline_editor/components/commit/commit_section_spec.js
deleted file mode 100644
index 744b0378a75..00000000000
--- a/spec/frontend/pipeline_editor/components/commit/commit_section_spec.js
+++ /dev/null
@@ -1,287 +0,0 @@
-import VueApollo from 'vue-apollo';
-import { GlFormTextarea, GlFormInput, GlLoadingIcon } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import Vue from 'vue';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue';
-import CommitSection from '~/pipeline_editor/components/commit/commit_section.vue';
-import {
- COMMIT_ACTION_CREATE,
- COMMIT_ACTION_UPDATE,
- COMMIT_SUCCESS,
- COMMIT_SUCCESS_WITH_REDIRECT,
-} from '~/pipeline_editor/constants';
-import { resolvers } from '~/pipeline_editor/graphql/resolvers';
-import commitCreate from '~/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql';
-import getCurrentBranch from '~/pipeline_editor/graphql/queries/client/current_branch.query.graphql';
-import updatePipelineEtag from '~/pipeline_editor/graphql/mutations/client/update_pipeline_etag.mutation.graphql';
-
-import {
- mockCiConfigPath,
- mockCiYml,
- mockCommitCreateResponse,
- mockCommitCreateResponseNewEtag,
- mockCommitSha,
- mockCommitMessage,
- mockDefaultBranch,
- mockProjectFullPath,
-} from '../../mock_data';
-
-const mockVariables = {
- action: COMMIT_ACTION_UPDATE,
- projectPath: mockProjectFullPath,
- startBranch: mockDefaultBranch,
- message: mockCommitMessage,
- filePath: mockCiConfigPath,
- content: mockCiYml,
- lastCommitId: mockCommitSha,
-};
-
-const mockProvide = {
- ciConfigPath: mockCiConfigPath,
- projectFullPath: mockProjectFullPath,
-};
-
-describe('Pipeline Editor | Commit section', () => {
- let wrapper;
- let mockApollo;
- const mockMutateCommitData = jest.fn();
-
- const defaultProps = {
- ciFileContent: mockCiYml,
- commitSha: mockCommitSha,
- hasUnsavedChanges: true,
- isNewCiConfigFile: false,
- };
-
- const createComponent = ({ apolloConfig = {}, props = {}, options = {}, provide = {} } = {}) => {
- wrapper = mount(CommitSection, {
- propsData: { ...defaultProps, ...props },
- provide: { ...mockProvide, ...provide },
- data() {
- return {
- currentBranch: mockDefaultBranch,
- };
- },
- attachTo: document.body,
- ...apolloConfig,
- ...options,
- });
- };
-
- const createComponentWithApollo = (options) => {
- const handlers = [[commitCreate, mockMutateCommitData]];
- Vue.use(VueApollo);
- mockApollo = createMockApollo(handlers, resolvers);
-
- mockApollo.clients.defaultClient.cache.writeQuery({
- query: getCurrentBranch,
- data: {
- workBranches: {
- __typename: 'BranchList',
- current: {
- __typename: 'WorkBranch',
- name: mockDefaultBranch,
- },
- },
- },
- });
-
- const apolloConfig = {
- apolloProvider: mockApollo,
- };
-
- createComponent({ ...options, apolloConfig });
- };
-
- const findCommitForm = () => wrapper.findComponent(CommitForm);
- const findCommitBtnLoadingIcon = () =>
- wrapper.find('[type="submit"]').findComponent(GlLoadingIcon);
-
- const submitCommit = async ({
- message = mockCommitMessage,
- branch = mockDefaultBranch,
- openMergeRequest = false,
- } = {}) => {
- await findCommitForm().findComponent(GlFormTextarea).setValue(message);
- await findCommitForm().findComponent(GlFormInput).setValue(branch);
- if (openMergeRequest) {
- await findCommitForm().find('[data-testid="new-mr-checkbox"]').setChecked(openMergeRequest);
- }
- await findCommitForm().find('[type="submit"]').trigger('click');
- await waitForPromises();
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('when the user commits a new file', () => {
- beforeEach(async () => {
- mockMutateCommitData.mockResolvedValue(mockCommitCreateResponse);
- createComponentWithApollo({ props: { isNewCiConfigFile: true } });
- await submitCommit();
- });
-
- it('calls the mutation with the CREATE action', () => {
- expect(mockMutateCommitData).toHaveBeenCalledTimes(1);
- expect(mockMutateCommitData).toHaveBeenCalledWith({
- ...mockVariables,
- action: COMMIT_ACTION_CREATE,
- branch: mockDefaultBranch,
- });
- });
- });
-
- describe('when the user commits an update to an existing file', () => {
- beforeEach(async () => {
- createComponentWithApollo();
- await submitCommit();
- });
-
- it('calls the mutation with the UPDATE action', () => {
- expect(mockMutateCommitData).toHaveBeenCalledTimes(1);
- expect(mockMutateCommitData).toHaveBeenCalledWith({
- ...mockVariables,
- action: COMMIT_ACTION_UPDATE,
- branch: mockDefaultBranch,
- });
- });
- });
-
- describe('when the user commits changes to the current branch', () => {
- beforeEach(async () => {
- createComponentWithApollo();
- await submitCommit();
- });
-
- it('calls the mutation with the current branch', () => {
- expect(mockMutateCommitData).toHaveBeenCalledTimes(1);
- expect(mockMutateCommitData).toHaveBeenCalledWith({
- ...mockVariables,
- branch: mockDefaultBranch,
- });
- });
-
- it('emits an event to communicate the commit was successful', () => {
- expect(wrapper.emitted('commit')).toHaveLength(1);
- expect(wrapper.emitted('commit')[0]).toEqual([{ type: COMMIT_SUCCESS }]);
- });
-
- it('emits an event to refetch the commit sha', () => {
- expect(wrapper.emitted('updateCommitSha')).toHaveLength(1);
- });
-
- it('shows no saving state', () => {
- expect(findCommitBtnLoadingIcon().exists()).toBe(false);
- });
-
- it('a second commit submits the latest sha, keeping the form updated', async () => {
- await submitCommit();
-
- expect(mockMutateCommitData).toHaveBeenCalledTimes(2);
- expect(mockMutateCommitData).toHaveBeenCalledWith({
- ...mockVariables,
- branch: mockDefaultBranch,
- });
- });
- });
-
- describe('when the user commits changes to a new branch', () => {
- const newBranch = 'new-branch';
-
- beforeEach(async () => {
- createComponentWithApollo();
- await submitCommit({
- branch: newBranch,
- });
- });
-
- it('calls the mutation with the new branch', () => {
- expect(mockMutateCommitData).toHaveBeenCalledWith({
- ...mockVariables,
- branch: newBranch,
- });
- });
-
- it('does not emit an event to refetch the commit sha', () => {
- expect(wrapper.emitted('updateCommitSha')).toBeUndefined();
- });
- });
-
- describe('when the user commits changes to open a new merge request', () => {
- const newBranch = 'new-branch';
-
- beforeEach(async () => {
- mockMutateCommitData.mockResolvedValue(mockCommitCreateResponse);
- createComponentWithApollo();
- mockMutateCommitData.mockResolvedValue(mockCommitCreateResponse);
- await submitCommit({
- branch: newBranch,
- openMergeRequest: true,
- });
- });
-
- it('emits a commit event with the right type, sourceBranch and targetBranch', () => {
- expect(wrapper.emitted('commit')).toHaveLength(1);
- expect(wrapper.emitted('commit')[0]).toMatchObject([
- {
- type: COMMIT_SUCCESS_WITH_REDIRECT,
- params: { sourceBranch: newBranch, targetBranch: mockDefaultBranch },
- },
- ]);
- });
- });
-
- describe('when the commit is ocurring', () => {
- beforeEach(() => {
- createComponentWithApollo();
- });
-
- it('shows a saving state', async () => {
- mockMutateCommitData.mockImplementationOnce(() => {
- expect(findCommitBtnLoadingIcon().exists()).toBe(true);
- return Promise.resolve();
- });
-
- await submitCommit({
- message: mockCommitMessage,
- branch: mockDefaultBranch,
- openMergeRequest: false,
- });
- });
- });
-
- describe('when the commit returns a different etag path', () => {
- beforeEach(async () => {
- createComponentWithApollo();
- jest.spyOn(wrapper.vm.$apollo, 'mutate');
- mockMutateCommitData.mockResolvedValue(mockCommitCreateResponseNewEtag);
- await submitCommit();
- });
-
- it('calls the client mutation to update the etag', () => {
- // 1:Commit submission, 2:etag update, 3:currentBranch update, 4:lastCommit update
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(4);
- expect(wrapper.vm.$apollo.mutate).toHaveBeenNthCalledWith(2, {
- mutation: updatePipelineEtag,
- variables: {
- pipelineEtag: mockCommitCreateResponseNewEtag.data.commitCreate.commitPipelinePath,
- },
- });
- });
- });
-
- it('sets listeners on commit form', () => {
- const handler = jest.fn();
- createComponent({ options: { listeners: { event: handler } } });
- findCommitForm().vm.$emit('event');
- expect(handler).toHaveBeenCalled();
- });
-
- it('passes down scroll-to-commit-form prop to commit form', () => {
- createComponent({ props: { 'scroll-to-commit-form': true } });
- expect(findCommitForm().props('scrollToCommitForm')).toBe(true);
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/drawer/cards/first_pipeline_card_spec.js b/spec/frontend/pipeline_editor/components/drawer/cards/first_pipeline_card_spec.js
deleted file mode 100644
index 7e1e5004d91..00000000000
--- a/spec/frontend/pipeline_editor/components/drawer/cards/first_pipeline_card_spec.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import { getByRole } from '@testing-library/dom';
-import { mount } from '@vue/test-utils';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
-import FirstPipelineCard from '~/pipeline_editor/components/drawer/cards/first_pipeline_card.vue';
-import { pipelineEditorTrackingOptions } from '~/pipeline_editor/constants';
-
-describe('First pipeline card', () => {
- let wrapper;
- let trackingSpy;
-
- const createComponent = () => {
- wrapper = mount(FirstPipelineCard);
- };
-
- const getLinkByName = (name) => getByRole(wrapper.element, 'link', { name });
- const findRunnersLink = () => getLinkByName(/make sure your instance has runners available/i);
- const findInstructionsList = () => wrapper.find('ol');
- const findAllInstructions = () => findInstructionsList().findAll('li');
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders the title', () => {
- expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.title);
- });
-
- it('renders the content', () => {
- expect(findInstructionsList().exists()).toBe(true);
- expect(findAllInstructions()).toHaveLength(3);
- });
-
- it('renders the link', () => {
- expect(findRunnersLink().href).toBe(wrapper.vm.$options.RUNNER_HELP_URL);
- });
-
- describe('tracking', () => {
- beforeEach(() => {
- createComponent();
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- });
-
- afterEach(() => {
- unmockTracking();
- });
-
- it('tracks runners help page click', async () => {
- const { label } = pipelineEditorTrackingOptions;
- const { runners } = pipelineEditorTrackingOptions.actions.helpDrawerLinks;
-
- await findRunnersLink().click();
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, runners, { label });
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/drawer/cards/getting_started_card_spec.js b/spec/frontend/pipeline_editor/components/drawer/cards/getting_started_card_spec.js
deleted file mode 100644
index c592e959068..00000000000
--- a/spec/frontend/pipeline_editor/components/drawer/cards/getting_started_card_spec.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import GettingStartedCard from '~/pipeline_editor/components/drawer/cards/getting_started_card.vue';
-
-describe('Getting started card', () => {
- let wrapper;
-
- const createComponent = () => {
- wrapper = shallowMount(GettingStartedCard);
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders the title', () => {
- expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.title);
- });
-
- it('renders the content', () => {
- expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.firstParagraph);
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/drawer/cards/pipeline_config_reference_card_spec.js b/spec/frontend/pipeline_editor/components/drawer/cards/pipeline_config_reference_card_spec.js
deleted file mode 100644
index 49177befe0e..00000000000
--- a/spec/frontend/pipeline_editor/components/drawer/cards/pipeline_config_reference_card_spec.js
+++ /dev/null
@@ -1,89 +0,0 @@
-import { getByRole } from '@testing-library/dom';
-import { mount } from '@vue/test-utils';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
-import PipelineConfigReferenceCard from '~/pipeline_editor/components/drawer/cards/pipeline_config_reference_card.vue';
-import { pipelineEditorTrackingOptions } from '~/pipeline_editor/constants';
-
-describe('Pipeline config reference card', () => {
- let wrapper;
- let trackingSpy;
-
- const defaultProvide = {
- ciExamplesHelpPagePath: 'help/ci/examples/',
- ciHelpPagePath: 'help/ci/introduction',
- needsHelpPagePath: 'help/ci/yaml#needs',
- ymlHelpPagePath: 'help/ci/yaml',
- };
-
- const createComponent = () => {
- wrapper = mount(PipelineConfigReferenceCard, {
- provide: {
- ...defaultProvide,
- },
- });
- };
-
- const getLinkByName = (name) => getByRole(wrapper.element, 'link', { name });
- const findCiExamplesLink = () => getLinkByName(/CI\/CD examples and templates/i);
- const findCiIntroLink = () => getLinkByName(/GitLab CI\/CD concepts/i);
- const findNeedsLink = () => getLinkByName(/Needs keyword/i);
- const findYmlSyntaxLink = () => getLinkByName(/.gitlab-ci.yml syntax reference/i);
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders the title', () => {
- expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.title);
- });
-
- it('renders the content', () => {
- expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.firstParagraph);
- });
-
- it('renders the links', () => {
- expect(findCiExamplesLink().href).toContain(defaultProvide.ciExamplesHelpPagePath);
- expect(findCiIntroLink().href).toContain(defaultProvide.ciHelpPagePath);
- expect(findNeedsLink().href).toContain(defaultProvide.needsHelpPagePath);
- expect(findYmlSyntaxLink().href).toContain(defaultProvide.ymlHelpPagePath);
- });
-
- describe('tracking', () => {
- beforeEach(() => {
- createComponent();
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- });
-
- afterEach(() => {
- unmockTracking();
- });
-
- const testTracker = async (element, expectedAction) => {
- const { label } = pipelineEditorTrackingOptions;
-
- await element.click();
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, expectedAction, {
- label,
- });
- };
-
- it('tracks help page links', async () => {
- const {
- CI_EXAMPLES_LINK,
- CI_HELP_LINK,
- CI_NEEDS_LINK,
- CI_YAML_LINK,
- } = pipelineEditorTrackingOptions.actions.helpDrawerLinks;
-
- testTracker(findCiExamplesLink(), CI_EXAMPLES_LINK);
- testTracker(findCiIntroLink(), CI_HELP_LINK);
- testTracker(findNeedsLink(), CI_NEEDS_LINK);
- testTracker(findYmlSyntaxLink(), CI_YAML_LINK);
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/drawer/cards/visualize_and_lint_card_spec.js b/spec/frontend/pipeline_editor/components/drawer/cards/visualize_and_lint_card_spec.js
deleted file mode 100644
index bebd2484c1d..00000000000
--- a/spec/frontend/pipeline_editor/components/drawer/cards/visualize_and_lint_card_spec.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import VisualizeAndLintCard from '~/pipeline_editor/components/drawer/cards/getting_started_card.vue';
-
-describe('Visual and Lint card', () => {
- let wrapper;
-
- const createComponent = () => {
- wrapper = shallowMount(VisualizeAndLintCard);
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders the title', () => {
- expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.title);
- });
-
- it('renders the content', () => {
- expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.firstParagraph);
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/drawer/pipeline_editor_drawer_spec.js b/spec/frontend/pipeline_editor/components/drawer/pipeline_editor_drawer_spec.js
deleted file mode 100644
index 33b53bf6a56..00000000000
--- a/spec/frontend/pipeline_editor/components/drawer/pipeline_editor_drawer_spec.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { GlDrawer } from '@gitlab/ui';
-import PipelineEditorDrawer from '~/pipeline_editor/components/drawer/pipeline_editor_drawer.vue';
-
-describe('Pipeline editor drawer', () => {
- let wrapper;
-
- const findDrawer = () => wrapper.findComponent(GlDrawer);
-
- const createComponent = () => {
- wrapper = shallowMount(PipelineEditorDrawer);
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('emits close event when closing the drawer', () => {
- createComponent();
-
- expect(wrapper.emitted('close-drawer')).toBeUndefined();
-
- findDrawer().vm.$emit('close');
-
- expect(wrapper.emitted('close-drawer')).toHaveLength(1);
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/drawer/ui/demo_job_pill_spec.js b/spec/frontend/pipeline_editor/components/drawer/ui/demo_job_pill_spec.js
deleted file mode 100644
index edd2b45569a..00000000000
--- a/spec/frontend/pipeline_editor/components/drawer/ui/demo_job_pill_spec.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import DemoJobPill from '~/pipeline_editor/components/drawer/ui/demo_job_pill.vue';
-
-describe('Demo job pill', () => {
- let wrapper;
- const jobName = 'my-build-job';
-
- const createComponent = () => {
- wrapper = shallowMount(DemoJobPill, {
- propsData: {
- jobName,
- },
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders the jobName', () => {
- expect(wrapper.text()).toContain(jobName);
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/editor/ci_config_merged_preview_spec.js b/spec/frontend/pipeline_editor/components/editor/ci_config_merged_preview_spec.js
deleted file mode 100644
index 7dd8a77d055..00000000000
--- a/spec/frontend/pipeline_editor/components/editor/ci_config_merged_preview_spec.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import { GlIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-
-import { EDITOR_READY_EVENT } from '~/editor/constants';
-import CiConfigMergedPreview from '~/pipeline_editor/components/editor/ci_config_merged_preview.vue';
-import { mockLintResponse, mockCiConfigPath } from '../../mock_data';
-
-describe('Text editor component', () => {
- let wrapper;
-
- const MockSourceEditor = {
- template: '<div/>',
- props: ['value', 'fileName', 'editorOptions'],
- mounted() {
- this.$emit(EDITOR_READY_EVENT);
- },
- };
-
- const createComponent = ({ props = {} } = {}) => {
- wrapper = shallowMount(CiConfigMergedPreview, {
- propsData: {
- ciConfigData: mockLintResponse,
- ...props,
- },
- provide: {
- ciConfigPath: mockCiConfigPath,
- },
- stubs: {
- SourceEditor: MockSourceEditor,
- },
- });
- };
-
- const findIcon = () => wrapper.findComponent(GlIcon);
- const findEditor = () => wrapper.findComponent(MockSourceEditor);
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('when status is valid', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('shows an information message that the section is not editable', () => {
- expect(findIcon().exists()).toBe(true);
- expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.viewOnlyMessage);
- });
-
- it('contains an editor', () => {
- expect(findEditor().exists()).toBe(true);
- });
-
- it('editor contains the value provided', () => {
- expect(findEditor().props('value')).toBe(mockLintResponse.mergedYaml);
- });
-
- it('editor is configured for the CI config path', () => {
- expect(findEditor().props('fileName')).toBe(mockCiConfigPath);
- });
-
- it('editor is readonly', () => {
- expect(findEditor().props('editorOptions')).toMatchObject({
- readOnly: true,
- });
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/editor/ci_editor_header_spec.js b/spec/frontend/pipeline_editor/components/editor/ci_editor_header_spec.js
deleted file mode 100644
index 930f08ef545..00000000000
--- a/spec/frontend/pipeline_editor/components/editor/ci_editor_header_spec.js
+++ /dev/null
@@ -1,115 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
-import CiEditorHeader from '~/pipeline_editor/components/editor/ci_editor_header.vue';
-import {
- pipelineEditorTrackingOptions,
- TEMPLATE_REPOSITORY_URL,
-} from '~/pipeline_editor/constants';
-
-describe('CI Editor Header', () => {
- let wrapper;
- let trackingSpy = null;
-
- const createComponent = ({ showDrawer = false } = {}) => {
- wrapper = extendedWrapper(
- shallowMount(CiEditorHeader, {
- propsData: {
- showDrawer,
- },
- }),
- );
- };
-
- const findLinkBtn = () => wrapper.findByTestId('template-repo-link');
- const findHelpBtn = () => wrapper.findByTestId('drawer-toggle');
-
- afterEach(() => {
- wrapper.destroy();
- unmockTracking();
- });
-
- const testTracker = async (element, expectedAction) => {
- const { label } = pipelineEditorTrackingOptions;
-
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- await element.vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, expectedAction, {
- label,
- });
- };
-
- describe('link button', () => {
- beforeEach(() => {
- createComponent();
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- });
-
- it('finds the browse template button', () => {
- expect(findLinkBtn().exists()).toBe(true);
- });
-
- it('contains the link to the template repo', () => {
- expect(findLinkBtn().attributes('href')).toBe(TEMPLATE_REPOSITORY_URL);
- });
-
- it('has the external-link icon', () => {
- expect(findLinkBtn().props('icon')).toBe('external-link');
- });
-
- it('tracks the click on the browse button', async () => {
- const { browseTemplates } = pipelineEditorTrackingOptions.actions;
-
- testTracker(findLinkBtn(), browseTemplates);
- });
- });
-
- describe('help button', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('finds the help button', () => {
- expect(findHelpBtn().exists()).toBe(true);
- });
-
- it('has the information-o icon', () => {
- expect(findHelpBtn().props('icon')).toBe('information-o');
- });
-
- describe('when pipeline editor drawer is closed', () => {
- beforeEach(() => {
- createComponent({ showDrawer: false });
- });
-
- it('emits open drawer event when clicked', () => {
- expect(wrapper.emitted('open-drawer')).toBeUndefined();
-
- findHelpBtn().vm.$emit('click');
-
- expect(wrapper.emitted('open-drawer')).toHaveLength(1);
- });
-
- it('tracks open help drawer action', async () => {
- const { actions } = pipelineEditorTrackingOptions;
-
- testTracker(findHelpBtn(), actions.openHelpDrawer);
- });
- });
-
- describe('when pipeline editor drawer is open', () => {
- beforeEach(() => {
- createComponent({ showDrawer: true });
- });
-
- it('emits close drawer event when clicked', () => {
- expect(wrapper.emitted('close-drawer')).toBeUndefined();
-
- findHelpBtn().vm.$emit('click');
-
- expect(wrapper.emitted('close-drawer')).toHaveLength(1);
- });
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js b/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js
deleted file mode 100644
index 6cdf9a93d55..00000000000
--- a/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js
+++ /dev/null
@@ -1,134 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-
-import { EDITOR_READY_EVENT } from '~/editor/constants';
-import { SOURCE_EDITOR_DEBOUNCE } from '~/pipeline_editor/constants';
-import TextEditor from '~/pipeline_editor/components/editor/text_editor.vue';
-import {
- mockCiConfigPath,
- mockCiYml,
- mockCommitSha,
- mockProjectPath,
- mockProjectNamespace,
- mockDefaultBranch,
-} from '../../mock_data';
-
-describe('Pipeline Editor | Text editor component', () => {
- let wrapper;
-
- let editorReadyListener;
- let mockUse;
- let mockRegisterCiSchema;
- let mockEditorInstance;
- let editorInstanceDetail;
-
- const MockSourceEditor = {
- template: '<div/>',
- props: ['value', 'fileName', 'editorOptions', 'debounceValue'],
- };
-
- const createComponent = (glFeatures = {}, mountFn = shallowMount) => {
- wrapper = mountFn(TextEditor, {
- provide: {
- projectPath: mockProjectPath,
- projectNamespace: mockProjectNamespace,
- ciConfigPath: mockCiConfigPath,
- defaultBranch: mockDefaultBranch,
- glFeatures,
- },
- propsData: {
- commitSha: mockCommitSha,
- },
- attrs: {
- value: mockCiYml,
- },
- listeners: {
- [EDITOR_READY_EVENT]: editorReadyListener,
- },
- stubs: {
- SourceEditor: MockSourceEditor,
- },
- });
- };
-
- const findEditor = () => wrapper.findComponent(MockSourceEditor);
-
- beforeEach(() => {
- editorReadyListener = jest.fn();
- mockUse = jest.fn();
- mockRegisterCiSchema = jest.fn();
- mockEditorInstance = {
- use: mockUse,
- registerCiSchema: mockRegisterCiSchema,
- };
- editorInstanceDetail = {
- detail: {
- instance: mockEditorInstance,
- },
- };
- });
-
- afterEach(() => {
- wrapper.destroy();
-
- mockUse.mockClear();
- mockRegisterCiSchema.mockClear();
- });
-
- describe('template', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('contains an editor', () => {
- expect(findEditor().exists()).toBe(true);
- });
-
- it('editor contains the value provided', () => {
- expect(findEditor().props('value')).toBe(mockCiYml);
- });
-
- it('editor is configured for the CI config path', () => {
- expect(findEditor().props('fileName')).toBe(mockCiConfigPath);
- });
-
- it('passes down editor configs options', () => {
- expect(findEditor().props('editorOptions')).toEqual({ quickSuggestions: true });
- });
-
- it('passes down editor debounce value', () => {
- expect(findEditor().props('debounceValue')).toBe(SOURCE_EDITOR_DEBOUNCE);
- });
-
- it('bubbles up events', () => {
- findEditor().vm.$emit(EDITOR_READY_EVENT, editorInstanceDetail);
-
- expect(editorReadyListener).toHaveBeenCalled();
- });
- });
-
- describe('CI schema', () => {
- describe('when `schema_linting` feature flag is on', () => {
- beforeEach(() => {
- createComponent({ schemaLinting: true });
- findEditor().vm.$emit(EDITOR_READY_EVENT, editorInstanceDetail);
- });
-
- it('configures editor with syntax highlight', () => {
- expect(mockUse).toHaveBeenCalledTimes(1);
- expect(mockRegisterCiSchema).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('when `schema_linting` feature flag is off', () => {
- beforeEach(() => {
- createComponent();
- findEditor().vm.$emit(EDITOR_READY_EVENT, editorInstanceDetail);
- });
-
- it('does not call the register CI schema function', () => {
- expect(mockUse).not.toHaveBeenCalled();
- expect(mockRegisterCiSchema).not.toHaveBeenCalled();
- });
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js b/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js
deleted file mode 100644
index f0347ad19ac..00000000000
--- a/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js
+++ /dev/null
@@ -1,432 +0,0 @@
-import {
- GlDropdown,
- GlDropdownItem,
- GlInfiniteScroll,
- GlLoadingIcon,
- GlSearchBoxByType,
-} from '@gitlab/ui';
-import { createLocalVue, mount, shallowMount } from '@vue/test-utils';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import BranchSwitcher from '~/pipeline_editor/components/file_nav/branch_switcher.vue';
-import { DEFAULT_FAILURE } from '~/pipeline_editor/constants';
-import getAvailableBranchesQuery from '~/pipeline_editor/graphql/queries/available_branches.query.graphql';
-import getCurrentBranch from '~/pipeline_editor/graphql/queries/client/current_branch.query.graphql';
-import getLastCommitBranch from '~/pipeline_editor/graphql/queries/client/last_commit_branch.query.graphql';
-import { resolvers } from '~/pipeline_editor/graphql/resolvers';
-
-import {
- mockBranchPaginationLimit,
- mockDefaultBranch,
- mockEmptySearchBranches,
- mockProjectBranches,
- mockProjectFullPath,
- mockSearchBranches,
- mockTotalBranches,
- mockTotalBranchResults,
- mockTotalSearchResults,
-} from '../../mock_data';
-
-const localVue = createLocalVue();
-localVue.use(VueApollo);
-
-describe('Pipeline editor branch switcher', () => {
- let wrapper;
- let mockApollo;
- let mockAvailableBranchQuery;
-
- const createComponent = ({
- currentBranch = mockDefaultBranch,
- availableBranches = ['main'],
- isQueryLoading = false,
- mountFn = shallowMount,
- options = {},
- props = {},
- } = {}) => {
- wrapper = mountFn(BranchSwitcher, {
- propsData: {
- ...props,
- paginationLimit: mockBranchPaginationLimit,
- },
- provide: {
- projectFullPath: mockProjectFullPath,
- totalBranches: mockTotalBranches,
- },
- mocks: {
- $apollo: {
- queries: {
- availableBranches: {
- loading: isQueryLoading,
- },
- },
- },
- },
- data() {
- return {
- availableBranches,
- currentBranch,
- };
- },
- ...options,
- });
- };
-
- const createComponentWithApollo = ({
- mountFn = shallowMount,
- props = {},
- availableBranches = ['main'],
- } = {}) => {
- const handlers = [[getAvailableBranchesQuery, mockAvailableBranchQuery]];
- mockApollo = createMockApollo(handlers, resolvers);
-
- mockApollo.clients.defaultClient.cache.writeQuery({
- query: getCurrentBranch,
- data: {
- workBranches: {
- __typename: 'BranchList',
- current: {
- __typename: 'WorkBranch',
- name: mockDefaultBranch,
- },
- },
- },
- });
-
- mockApollo.clients.defaultClient.cache.writeQuery({
- query: getLastCommitBranch,
- data: {
- workBranches: {
- __typename: 'BranchList',
- lastCommit: {
- __typename: 'WorkBranch',
- name: '',
- },
- },
- },
- });
-
- createComponent({
- mountFn,
- props,
- availableBranches,
- options: {
- localVue,
- apolloProvider: mockApollo,
- mocks: {},
- },
- });
- };
-
- const findDropdown = () => wrapper.findComponent(GlDropdown);
- const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
- const findInfiniteScroll = () => wrapper.findComponent(GlInfiniteScroll);
- const defaultBranchInDropdown = () => findDropdownItems().at(0);
-
- const setAvailableBranchesMock = (availableBranches) => {
- mockAvailableBranchQuery.mockResolvedValue(availableBranches);
- };
-
- beforeEach(() => {
- mockAvailableBranchQuery = jest.fn();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const testErrorHandling = () => {
- expect(wrapper.emitted('showError')).toBeDefined();
- expect(wrapper.emitted('showError')[0]).toEqual([
- {
- reasons: [wrapper.vm.$options.i18n.fetchError],
- type: DEFAULT_FAILURE,
- },
- ]);
- };
-
- describe('when querying for the first time', () => {
- beforeEach(() => {
- createComponentWithApollo({ availableBranches: [] });
- });
-
- it('disables the dropdown', () => {
- expect(findDropdown().props('disabled')).toBe(true);
- });
- });
-
- describe('after querying', () => {
- beforeEach(async () => {
- setAvailableBranchesMock(mockProjectBranches);
- createComponentWithApollo({ mountFn: mount });
- await waitForPromises();
- });
-
- it('renders search box', () => {
- expect(findSearchBox().exists()).toBe(true);
- });
-
- it('renders list of branches', () => {
- expect(findDropdown().exists()).toBe(true);
- expect(findDropdownItems()).toHaveLength(mockTotalBranchResults);
- });
-
- it('renders current branch with a check mark', () => {
- expect(defaultBranchInDropdown().text()).toBe(mockDefaultBranch);
- expect(defaultBranchInDropdown().props('isChecked')).toBe(true);
- });
-
- it('does not render check mark for other branches', () => {
- const nonDefaultBranch = findDropdownItems().at(1);
-
- expect(nonDefaultBranch.text()).not.toBe(mockDefaultBranch);
- expect(nonDefaultBranch.props('isChecked')).toBe(false);
- });
- });
-
- describe('on fetch error', () => {
- beforeEach(async () => {
- setAvailableBranchesMock(new Error());
- createComponentWithApollo({ availableBranches: [] });
- await waitForPromises();
- });
-
- it('does not render dropdown', () => {
- expect(findDropdown().props('disabled')).toBe(true);
- });
-
- it('shows an error message', () => {
- testErrorHandling();
- });
- });
-
- describe('when switching branches', () => {
- beforeEach(async () => {
- jest.spyOn(window.history, 'pushState').mockImplementation(() => {});
- setAvailableBranchesMock(mockProjectBranches);
- createComponentWithApollo({ mountFn: mount });
- await waitForPromises();
- });
-
- it('updates session history when selecting a different branch', async () => {
- const branch = findDropdownItems().at(1);
- branch.vm.$emit('click');
- await waitForPromises();
-
- expect(window.history.pushState).toHaveBeenCalled();
- expect(window.history.pushState.mock.calls[0][2]).toContain(`?branch_name=${branch.text()}`);
- });
-
- it('does not update session history when selecting current branch', async () => {
- const branch = findDropdownItems().at(0);
- branch.vm.$emit('click');
- await waitForPromises();
-
- expect(branch.text()).toBe(mockDefaultBranch);
- expect(window.history.pushState).not.toHaveBeenCalled();
- });
-
- it('emits the refetchContent event when selecting a different branch', async () => {
- const branch = findDropdownItems().at(1);
-
- expect(branch.text()).not.toBe(mockDefaultBranch);
- expect(wrapper.emitted('refetchContent')).toBeUndefined();
-
- branch.vm.$emit('click');
- await waitForPromises();
-
- expect(wrapper.emitted('refetchContent')).toBeDefined();
- expect(wrapper.emitted('refetchContent')).toHaveLength(1);
- });
-
- it('does not emit the refetchContent event when selecting the current branch', async () => {
- const branch = findDropdownItems().at(0);
-
- expect(branch.text()).toBe(mockDefaultBranch);
- expect(wrapper.emitted('refetchContent')).toBeUndefined();
-
- branch.vm.$emit('click');
- await waitForPromises();
-
- expect(wrapper.emitted('refetchContent')).toBeUndefined();
- });
-
- describe('with unsaved changes', () => {
- beforeEach(async () => {
- createComponentWithApollo({ mountFn: mount, props: { hasUnsavedChanges: true } });
- await waitForPromises();
- });
-
- it('emits `select-branch` event and does not switch branch', async () => {
- expect(wrapper.emitted('select-branch')).toBeUndefined();
-
- const branch = findDropdownItems().at(1);
- await branch.vm.$emit('click');
-
- expect(wrapper.emitted('select-branch')).toEqual([[branch.text()]]);
- expect(wrapper.emitted('refetchContent')).toBeUndefined();
- });
- });
- });
-
- describe('when searching', () => {
- beforeEach(async () => {
- setAvailableBranchesMock(mockProjectBranches);
- createComponentWithApollo({ mountFn: mount });
- await waitForPromises();
- });
-
- afterEach(() => {
- mockAvailableBranchQuery.mockClear();
- });
-
- it('shows error message on fetch error', async () => {
- mockAvailableBranchQuery.mockResolvedValue(new Error());
-
- findSearchBox().vm.$emit('input', 'te');
- await waitForPromises();
-
- testErrorHandling();
- });
-
- describe('with a search term', () => {
- beforeEach(async () => {
- mockAvailableBranchQuery.mockResolvedValue(mockSearchBranches);
- });
-
- it('calls query with correct variables', async () => {
- findSearchBox().vm.$emit('input', 'te');
- await waitForPromises();
-
- expect(mockAvailableBranchQuery).toHaveBeenCalledWith({
- limit: mockTotalBranches, // fetch all branches
- offset: 0,
- projectFullPath: mockProjectFullPath,
- searchPattern: '*te*',
- });
- });
-
- it('fetches new list of branches', async () => {
- expect(findDropdownItems()).toHaveLength(mockTotalBranchResults);
-
- findSearchBox().vm.$emit('input', 'te');
- await waitForPromises();
-
- expect(findDropdownItems()).toHaveLength(mockTotalSearchResults);
- });
-
- it('does not hide dropdown when search result is empty', async () => {
- mockAvailableBranchQuery.mockResolvedValue(mockEmptySearchBranches);
- findSearchBox().vm.$emit('input', 'aaaaa');
- await waitForPromises();
-
- expect(findDropdown().exists()).toBe(true);
- expect(findDropdownItems()).toHaveLength(0);
- });
- });
-
- describe('without a search term', () => {
- beforeEach(async () => {
- mockAvailableBranchQuery.mockResolvedValue(mockSearchBranches);
- findSearchBox().vm.$emit('input', 'te');
- await waitForPromises();
-
- mockAvailableBranchQuery.mockResolvedValue(mockProjectBranches);
- });
-
- it('calls query with correct variables', async () => {
- findSearchBox().vm.$emit('input', '');
- await waitForPromises();
-
- expect(mockAvailableBranchQuery).toHaveBeenCalledWith({
- limit: mockBranchPaginationLimit, // only fetch first n branches first
- offset: 0,
- projectFullPath: mockProjectFullPath,
- searchPattern: '*',
- });
- });
-
- it('fetches new list of branches', async () => {
- expect(findDropdownItems()).toHaveLength(mockTotalSearchResults);
-
- findSearchBox().vm.$emit('input', '');
- await waitForPromises();
-
- expect(findDropdownItems()).toHaveLength(mockTotalBranchResults);
- });
- });
- });
-
- describe('loading icon', () => {
- it.each`
- isQueryLoading | isRendered
- ${true} | ${true}
- ${false} | ${false}
- `('checks if query is loading before rendering', ({ isQueryLoading, isRendered }) => {
- createComponent({ isQueryLoading, mountFn: mount });
-
- expect(findLoadingIcon().exists()).toBe(isRendered);
- });
- });
-
- describe('when scrolling to the bottom of the list', () => {
- beforeEach(async () => {
- setAvailableBranchesMock(mockProjectBranches);
- createComponentWithApollo();
- await waitForPromises();
- });
-
- afterEach(() => {
- mockAvailableBranchQuery.mockClear();
- });
-
- describe('when search term is empty', () => {
- it('fetches more branches', async () => {
- expect(mockAvailableBranchQuery).toHaveBeenCalledTimes(1);
-
- findInfiniteScroll().vm.$emit('bottomReached');
- await waitForPromises();
-
- expect(mockAvailableBranchQuery).toHaveBeenCalledTimes(2);
- });
-
- it('calls the query with the correct variables', async () => {
- findInfiniteScroll().vm.$emit('bottomReached');
- await waitForPromises();
-
- expect(mockAvailableBranchQuery).toHaveBeenCalledWith({
- limit: mockBranchPaginationLimit,
- offset: mockBranchPaginationLimit, // offset changed
- projectFullPath: mockProjectFullPath,
- searchPattern: '*',
- });
- });
-
- it('shows error message on fetch error', async () => {
- mockAvailableBranchQuery.mockResolvedValue(new Error());
-
- findInfiniteScroll().vm.$emit('bottomReached');
- await waitForPromises();
-
- testErrorHandling();
- });
- });
-
- describe('when search term exists', () => {
- it('does not fetch more branches', async () => {
- findSearchBox().vm.$emit('input', 'te');
- await waitForPromises();
-
- expect(mockAvailableBranchQuery).toHaveBeenCalledTimes(2);
- mockAvailableBranchQuery.mockClear();
-
- findInfiniteScroll().vm.$emit('bottomReached');
- await waitForPromises();
-
- expect(mockAvailableBranchQuery).not.toHaveBeenCalled();
- });
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/file-nav/pipeline_editor_file_nav_spec.js b/spec/frontend/pipeline_editor/components/file-nav/pipeline_editor_file_nav_spec.js
deleted file mode 100644
index d503aff40b8..00000000000
--- a/spec/frontend/pipeline_editor/components/file-nav/pipeline_editor_file_nav_spec.js
+++ /dev/null
@@ -1,126 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import { shallowMount } from '@vue/test-utils';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import BranchSwitcher from '~/pipeline_editor/components/file_nav/branch_switcher.vue';
-import PipelineEditorFileNav from '~/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue';
-import FileTreePopover from '~/pipeline_editor/components/popovers/file_tree_popover.vue';
-import getAppStatus from '~/pipeline_editor/graphql/queries/client/app_status.query.graphql';
-import {
- EDITOR_APP_STATUS_EMPTY,
- EDITOR_APP_STATUS_LOADING,
- EDITOR_APP_STATUS_VALID,
-} from '~/pipeline_editor/constants';
-
-Vue.use(VueApollo);
-
-describe('Pipeline editor file nav', () => {
- let wrapper;
-
- const mockApollo = createMockApollo();
-
- const createComponent = ({
- appStatus = EDITOR_APP_STATUS_VALID,
- isNewCiConfigFile = false,
- } = {}) => {
- mockApollo.clients.defaultClient.cache.writeQuery({
- query: getAppStatus,
- data: {
- app: {
- __typename: 'PipelineEditorApp',
- status: appStatus,
- },
- },
- });
-
- wrapper = extendedWrapper(
- shallowMount(PipelineEditorFileNav, {
- apolloProvider: mockApollo,
- propsData: {
- isNewCiConfigFile,
- },
- }),
- );
- };
-
- const findBranchSwitcher = () => wrapper.findComponent(BranchSwitcher);
- const findFileTreeBtn = () => wrapper.findByTestId('file-tree-toggle');
- const findPopoverContainer = () => wrapper.findComponent(FileTreePopover);
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('template', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders the branch switcher', () => {
- expect(findBranchSwitcher().exists()).toBe(true);
- });
- });
-
- describe('file tree', () => {
- describe('when editor is in the empty state', () => {
- beforeEach(() => {
- createComponent({ appStatus: EDITOR_APP_STATUS_EMPTY, isNewCiConfigFile: false });
- });
-
- it('does not render the file tree button', () => {
- expect(findFileTreeBtn().exists()).toBe(false);
- });
-
- it('does not render the file tree popover', () => {
- expect(findPopoverContainer().exists()).toBe(false);
- });
- });
-
- describe('when user is about to create their config file for the first time', () => {
- beforeEach(() => {
- createComponent({ appStatus: EDITOR_APP_STATUS_VALID, isNewCiConfigFile: true });
- });
-
- it('does not render the file tree button', () => {
- expect(findFileTreeBtn().exists()).toBe(false);
- });
-
- it('does not render the file tree popover', () => {
- expect(findPopoverContainer().exists()).toBe(false);
- });
- });
-
- describe('when app is in a global loading state', () => {
- it('renders the file tree button with a loading icon', () => {
- createComponent({ appStatus: EDITOR_APP_STATUS_LOADING, isNewCiConfigFile: false });
-
- expect(findFileTreeBtn().exists()).toBe(true);
- expect(findFileTreeBtn().attributes('loading')).toBe('true');
- });
- });
-
- describe('when editor has a non-empty config file open', () => {
- beforeEach(() => {
- createComponent({ appStatus: EDITOR_APP_STATUS_VALID, isNewCiConfigFile: false });
- });
-
- it('renders the file tree button', () => {
- expect(findFileTreeBtn().exists()).toBe(true);
- expect(findFileTreeBtn().props('icon')).toBe('file-tree');
- });
-
- it('renders the file tree popover', () => {
- expect(findPopoverContainer().exists()).toBe(true);
- });
-
- it('file tree button emits toggle-file-tree event', () => {
- expect(wrapper.emitted('toggle-file-tree')).toBe(undefined);
-
- findFileTreeBtn().vm.$emit('click');
-
- expect(wrapper.emitted('toggle-file-tree')).toHaveLength(1);
- });
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/file-tree/container_spec.js b/spec/frontend/pipeline_editor/components/file-tree/container_spec.js
deleted file mode 100644
index f79074f1e0f..00000000000
--- a/spec/frontend/pipeline_editor/components/file-tree/container_spec.js
+++ /dev/null
@@ -1,138 +0,0 @@
-import { nextTick } from 'vue';
-import { shallowMount } from '@vue/test-utils';
-import { GlAlert } from '@gitlab/ui';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import { createMockDirective } from 'helpers/vue_mock_directive';
-import PipelineEditorFileTreeContainer from '~/pipeline_editor/components/file_tree/container.vue';
-import PipelineEditorFileTreeItem from '~/pipeline_editor/components/file_tree/file_item.vue';
-import { FILE_TREE_TIP_DISMISSED_KEY } from '~/pipeline_editor/constants';
-import { mockCiConfigPath, mockIncludes, mockIncludesHelpPagePath } from '../../mock_data';
-
-describe('Pipeline editor file nav', () => {
- let wrapper;
-
- const createComponent = ({ includes = mockIncludes, stubs } = {}) => {
- wrapper = extendedWrapper(
- shallowMount(PipelineEditorFileTreeContainer, {
- provide: {
- ciConfigPath: mockCiConfigPath,
- includesHelpPagePath: mockIncludesHelpPagePath,
- },
- propsData: {
- includes,
- },
- directives: {
- GlTooltip: createMockDirective(),
- },
- stubs,
- }),
- );
- };
-
- const findTip = () => wrapper.findComponent(GlAlert);
- const findCurrentConfigFilename = () => wrapper.findByTestId('current-config-filename');
- const fileTreeItems = () => wrapper.findAllComponents(PipelineEditorFileTreeItem);
-
- afterEach(() => {
- localStorage.clear();
- wrapper.destroy();
- });
-
- describe('template', () => {
- beforeEach(() => {
- createComponent({ stubs: { GlAlert } });
- });
-
- it('renders config file as a file item', () => {
- expect(findCurrentConfigFilename().text()).toBe(mockCiConfigPath);
- });
- });
-
- describe('when includes list is empty', () => {
- describe('when dismiss state is not saved in local storage', () => {
- beforeEach(() => {
- createComponent({
- includes: [],
- stubs: { GlAlert },
- });
- });
-
- it('does not render filenames', () => {
- expect(fileTreeItems().exists()).toBe(false);
- });
-
- it('renders alert tip', async () => {
- expect(findTip().exists()).toBe(true);
- });
-
- it('renders learn more link', async () => {
- expect(findTip().props('secondaryButtonLink')).toBe(mockIncludesHelpPagePath);
- });
-
- it('can dismiss the tip', async () => {
- expect(findTip().exists()).toBe(true);
-
- findTip().vm.$emit('dismiss');
- await nextTick();
-
- expect(findTip().exists()).toBe(false);
- });
- });
-
- describe('when dismiss state is saved in local storage', () => {
- beforeEach(() => {
- localStorage.setItem(FILE_TREE_TIP_DISMISSED_KEY, 'true');
- createComponent({
- includes: [],
- stubs: { GlAlert },
- });
- });
-
- it('does not render alert tip', async () => {
- expect(findTip().exists()).toBe(false);
- });
- });
-
- describe('when component receives new props with includes files', () => {
- beforeEach(() => {
- createComponent({ includes: [] });
- });
-
- it('hides tip and renders list of files', async () => {
- expect(findTip().exists()).toBe(true);
- expect(fileTreeItems()).toHaveLength(0);
-
- await wrapper.setProps({ includes: mockIncludes });
-
- expect(findTip().exists()).toBe(false);
- expect(fileTreeItems()).toHaveLength(mockIncludes.length);
- });
- });
- });
-
- describe('when there are includes files', () => {
- beforeEach(() => {
- createComponent({ stubs: { GlAlert } });
- });
-
- it('does not render alert tip', () => {
- expect(findTip().exists()).toBe(false);
- });
-
- it('renders the list of files', () => {
- expect(fileTreeItems()).toHaveLength(mockIncludes.length);
- });
-
- describe('when component receives new props with empty includes', () => {
- it('shows tip and does not render list of files', async () => {
- expect(findTip().exists()).toBe(false);
- expect(fileTreeItems()).toHaveLength(mockIncludes.length);
-
- await wrapper.setProps({ includes: [] });
-
- expect(findTip().exists()).toBe(true);
- expect(fileTreeItems()).toHaveLength(0);
- });
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/file-tree/file_item_spec.js b/spec/frontend/pipeline_editor/components/file-tree/file_item_spec.js
deleted file mode 100644
index f12ac14c6be..00000000000
--- a/spec/frontend/pipeline_editor/components/file-tree/file_item_spec.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import { GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import FileIcon from '~/vue_shared/components/file_icon.vue';
-import PipelineEditorFileTreeItem from '~/pipeline_editor/components/file_tree/file_item.vue';
-import { mockIncludesWithBlob, mockDefaultIncludes } from '../../mock_data';
-
-describe('Pipeline editor file nav', () => {
- let wrapper;
-
- const createComponent = ({ file = mockDefaultIncludes } = {}) => {
- wrapper = shallowMount(PipelineEditorFileTreeItem, {
- propsData: {
- file,
- },
- });
- };
-
- const fileIcon = () => wrapper.findComponent(FileIcon);
- const link = () => wrapper.findComponent(GlLink);
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('template', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders file icon', () => {
- expect(fileIcon().exists()).toBe(true);
- });
-
- it('renders file name', () => {
- expect(wrapper.text()).toBe(mockDefaultIncludes.location);
- });
-
- it('links to raw path by default', () => {
- expect(link().attributes('href')).toBe(mockDefaultIncludes.raw);
- });
- });
-
- describe('when file has blob link', () => {
- beforeEach(() => {
- createComponent({ file: mockIncludesWithBlob });
- });
-
- it('links to blob path', () => {
- expect(link().attributes('href')).toBe(mockIncludesWithBlob.blob);
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/header/pipeline_editor_header_spec.js b/spec/frontend/pipeline_editor/components/header/pipeline_editor_header_spec.js
deleted file mode 100644
index e1dc08b637f..00000000000
--- a/spec/frontend/pipeline_editor/components/header/pipeline_editor_header_spec.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import PipelineEditorHeader from '~/pipeline_editor/components/header/pipeline_editor_header.vue';
-import PipelineStatus from '~/pipeline_editor/components/header/pipeline_status.vue';
-import ValidationSegment from '~/pipeline_editor/components/header/validation_segment.vue';
-
-import { mockCiYml, mockLintResponse } from '../../mock_data';
-
-describe('Pipeline editor header', () => {
- let wrapper;
-
- const createComponent = ({ provide = {}, props = {} } = {}) => {
- wrapper = shallowMount(PipelineEditorHeader, {
- provide: {
- ...provide,
- },
- propsData: {
- ciConfigData: mockLintResponse,
- ciFileContent: mockCiYml,
- isCiConfigDataLoading: false,
- isNewCiConfigFile: false,
- ...props,
- },
- });
- };
-
- const findPipelineStatus = () => wrapper.findComponent(PipelineStatus);
- const findValidationSegment = () => wrapper.findComponent(ValidationSegment);
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- describe('template', () => {
- it('hides the pipeline status for new projects without a CI file', () => {
- createComponent({ props: { isNewCiConfigFile: true } });
-
- expect(findPipelineStatus().exists()).toBe(false);
- });
-
- it('renders the pipeline status when CI file exists', () => {
- createComponent({ props: { isNewCiConfigFile: false } });
-
- expect(findPipelineStatus().exists()).toBe(true);
- });
-
- it('renders the validation segment', () => {
- createComponent();
-
- expect(findValidationSegment().exists()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js b/spec/frontend/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js
deleted file mode 100644
index d40a9cc8100..00000000000
--- a/spec/frontend/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js
+++ /dev/null
@@ -1,109 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import PipelineEditorMiniGraph from '~/pipeline_editor/components/header/pipeline_editor_mini_graph.vue';
-import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
-import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql';
-import { PIPELINE_FAILURE } from '~/pipeline_editor/constants';
-import { mockLinkedPipelines, mockProjectFullPath, mockProjectPipeline } from '../../mock_data';
-
-Vue.use(VueApollo);
-
-describe('Pipeline Status', () => {
- let wrapper;
- let mockApollo;
- let mockLinkedPipelinesQuery;
-
- const createComponent = ({ hasStages = true, options } = {}) => {
- wrapper = shallowMount(PipelineEditorMiniGraph, {
- provide: {
- dataMethod: 'graphql',
- projectFullPath: mockProjectFullPath,
- },
- propsData: {
- pipeline: mockProjectPipeline({ hasStages }).pipeline,
- },
- ...options,
- });
- };
-
- const createComponentWithApollo = (hasStages = true) => {
- const handlers = [[getLinkedPipelinesQuery, mockLinkedPipelinesQuery]];
- mockApollo = createMockApollo(handlers);
-
- createComponent({
- hasStages,
- options: {
- apolloProvider: mockApollo,
- },
- });
- };
-
- const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph);
-
- beforeEach(() => {
- mockLinkedPipelinesQuery = jest.fn();
- });
-
- afterEach(() => {
- mockLinkedPipelinesQuery.mockReset();
- wrapper.destroy();
- });
-
- describe('when there are stages', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders pipeline mini graph', () => {
- expect(findPipelineMiniGraph().exists()).toBe(true);
- });
- });
-
- describe('when there are no stages', () => {
- beforeEach(() => {
- createComponent({ hasStages: false });
- });
-
- it('does not render pipeline mini graph', () => {
- expect(findPipelineMiniGraph().exists()).toBe(false);
- });
- });
-
- describe('when querying upstream and downstream pipelines', () => {
- describe('when query succeeds', () => {
- beforeEach(() => {
- mockLinkedPipelinesQuery.mockResolvedValue(mockLinkedPipelines());
- createComponentWithApollo();
- });
-
- it('should call the query with the correct variables', () => {
- expect(mockLinkedPipelinesQuery).toHaveBeenCalledTimes(1);
- expect(mockLinkedPipelinesQuery).toHaveBeenCalledWith({
- fullPath: mockProjectFullPath,
- iid: mockProjectPipeline().pipeline.iid,
- });
- });
- });
-
- describe('when query fails', () => {
- beforeEach(async () => {
- mockLinkedPipelinesQuery.mockRejectedValue(new Error());
- createComponentWithApollo();
- await waitForPromises();
- });
-
- it('should emit an error event when query fails', async () => {
- expect(wrapper.emitted('showError')).toHaveLength(1);
- expect(wrapper.emitted('showError')[0]).toEqual([
- {
- type: PIPELINE_FAILURE,
- reasons: [wrapper.vm.$options.i18n.linkedPipelinesFetchError],
- },
- ]);
- });
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/header/pipeline_status_spec.js b/spec/frontend/pipeline_editor/components/header/pipeline_status_spec.js
deleted file mode 100644
index 35315db39f8..00000000000
--- a/spec/frontend/pipeline_editor/components/header/pipeline_status_spec.js
+++ /dev/null
@@ -1,132 +0,0 @@
-import { GlIcon, GlLink, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import PipelineStatus, { i18n } from '~/pipeline_editor/components/header/pipeline_status.vue';
-import getPipelineQuery from '~/pipeline_editor/graphql/queries/pipeline.query.graphql';
-import PipelineEditorMiniGraph from '~/pipeline_editor/components/header/pipeline_editor_mini_graph.vue';
-import { mockCommitSha, mockProjectPipeline, mockProjectFullPath } from '../../mock_data';
-
-Vue.use(VueApollo);
-
-describe('Pipeline Status', () => {
- let wrapper;
- let mockApollo;
- let mockPipelineQuery;
-
- const createComponentWithApollo = () => {
- const handlers = [[getPipelineQuery, mockPipelineQuery]];
- mockApollo = createMockApollo(handlers);
-
- wrapper = shallowMount(PipelineStatus, {
- apolloProvider: mockApollo,
- propsData: {
- commitSha: mockCommitSha,
- },
- provide: {
- projectFullPath: mockProjectFullPath,
- },
- stubs: { GlLink, GlSprintf },
- });
- };
-
- const findIcon = () => wrapper.findComponent(GlIcon);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findPipelineEditorMiniGraph = () => wrapper.findComponent(PipelineEditorMiniGraph);
- const findPipelineId = () => wrapper.find('[data-testid="pipeline-id"]');
- const findPipelineCommit = () => wrapper.find('[data-testid="pipeline-commit"]');
- const findPipelineErrorMsg = () => wrapper.find('[data-testid="pipeline-error-msg"]');
- const findPipelineLoadingMsg = () => wrapper.find('[data-testid="pipeline-loading-msg"]');
- const findPipelineViewBtn = () => wrapper.find('[data-testid="pipeline-view-btn"]');
- const findStatusIcon = () => wrapper.find('[data-testid="pipeline-status-icon"]');
-
- beforeEach(() => {
- mockPipelineQuery = jest.fn();
- });
-
- afterEach(() => {
- mockPipelineQuery.mockReset();
- wrapper.destroy();
- });
-
- describe('loading icon', () => {
- it('renders while query is being fetched', () => {
- createComponentWithApollo();
-
- expect(findLoadingIcon().exists()).toBe(true);
- expect(findPipelineLoadingMsg().text()).toBe(i18n.fetchLoading);
- });
-
- it('does not render if query is no longer loading', async () => {
- createComponentWithApollo();
- await waitForPromises();
-
- expect(findLoadingIcon().exists()).toBe(false);
- });
- });
-
- describe('when querying data', () => {
- describe('when data is set', () => {
- beforeEach(async () => {
- mockPipelineQuery.mockResolvedValue({
- data: { project: mockProjectPipeline() },
- });
-
- createComponentWithApollo();
- await waitForPromises();
- });
-
- it('query is called with correct variables', async () => {
- expect(mockPipelineQuery).toHaveBeenCalledTimes(1);
- expect(mockPipelineQuery).toHaveBeenCalledWith({
- fullPath: mockProjectFullPath,
- sha: mockCommitSha,
- });
- });
-
- it('does not render error', () => {
- expect(findPipelineErrorMsg().exists()).toBe(false);
- });
-
- it('renders pipeline data', () => {
- const {
- id,
- commit: { title },
- detailedStatus: { detailsPath },
- } = mockProjectPipeline().pipeline;
-
- expect(findStatusIcon().exists()).toBe(true);
- expect(findPipelineId().text()).toBe(`#${id.match(/\d+/g)[0]}`);
- expect(findPipelineCommit().text()).toBe(`${mockCommitSha}: ${title}`);
- expect(findPipelineViewBtn().attributes('href')).toBe(detailsPath);
- });
-
- it('renders the pipeline mini graph', () => {
- expect(findPipelineEditorMiniGraph().exists()).toBe(true);
- });
- });
-
- describe('when data cannot be fetched', () => {
- beforeEach(async () => {
- mockPipelineQuery.mockRejectedValue(new Error());
-
- createComponentWithApollo();
- await waitForPromises();
- });
-
- it('renders error', () => {
- expect(findIcon().attributes('name')).toBe('warning-solid');
- expect(findPipelineErrorMsg().text()).toBe(i18n.fetchError);
- });
-
- it('does not render pipeline data', () => {
- expect(findStatusIcon().exists()).toBe(false);
- expect(findPipelineId().exists()).toBe(false);
- expect(findPipelineCommit().exists()).toBe(false);
- expect(findPipelineViewBtn().exists()).toBe(false);
- });
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/header/pipline_editor_mini_graph_spec.js b/spec/frontend/pipeline_editor/components/header/pipline_editor_mini_graph_spec.js
deleted file mode 100644
index d40a9cc8100..00000000000
--- a/spec/frontend/pipeline_editor/components/header/pipline_editor_mini_graph_spec.js
+++ /dev/null
@@ -1,109 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import PipelineEditorMiniGraph from '~/pipeline_editor/components/header/pipeline_editor_mini_graph.vue';
-import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
-import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql';
-import { PIPELINE_FAILURE } from '~/pipeline_editor/constants';
-import { mockLinkedPipelines, mockProjectFullPath, mockProjectPipeline } from '../../mock_data';
-
-Vue.use(VueApollo);
-
-describe('Pipeline Status', () => {
- let wrapper;
- let mockApollo;
- let mockLinkedPipelinesQuery;
-
- const createComponent = ({ hasStages = true, options } = {}) => {
- wrapper = shallowMount(PipelineEditorMiniGraph, {
- provide: {
- dataMethod: 'graphql',
- projectFullPath: mockProjectFullPath,
- },
- propsData: {
- pipeline: mockProjectPipeline({ hasStages }).pipeline,
- },
- ...options,
- });
- };
-
- const createComponentWithApollo = (hasStages = true) => {
- const handlers = [[getLinkedPipelinesQuery, mockLinkedPipelinesQuery]];
- mockApollo = createMockApollo(handlers);
-
- createComponent({
- hasStages,
- options: {
- apolloProvider: mockApollo,
- },
- });
- };
-
- const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph);
-
- beforeEach(() => {
- mockLinkedPipelinesQuery = jest.fn();
- });
-
- afterEach(() => {
- mockLinkedPipelinesQuery.mockReset();
- wrapper.destroy();
- });
-
- describe('when there are stages', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders pipeline mini graph', () => {
- expect(findPipelineMiniGraph().exists()).toBe(true);
- });
- });
-
- describe('when there are no stages', () => {
- beforeEach(() => {
- createComponent({ hasStages: false });
- });
-
- it('does not render pipeline mini graph', () => {
- expect(findPipelineMiniGraph().exists()).toBe(false);
- });
- });
-
- describe('when querying upstream and downstream pipelines', () => {
- describe('when query succeeds', () => {
- beforeEach(() => {
- mockLinkedPipelinesQuery.mockResolvedValue(mockLinkedPipelines());
- createComponentWithApollo();
- });
-
- it('should call the query with the correct variables', () => {
- expect(mockLinkedPipelinesQuery).toHaveBeenCalledTimes(1);
- expect(mockLinkedPipelinesQuery).toHaveBeenCalledWith({
- fullPath: mockProjectFullPath,
- iid: mockProjectPipeline().pipeline.iid,
- });
- });
- });
-
- describe('when query fails', () => {
- beforeEach(async () => {
- mockLinkedPipelinesQuery.mockRejectedValue(new Error());
- createComponentWithApollo();
- await waitForPromises();
- });
-
- it('should emit an error event when query fails', async () => {
- expect(wrapper.emitted('showError')).toHaveLength(1);
- expect(wrapper.emitted('showError')[0]).toEqual([
- {
- type: PIPELINE_FAILURE,
- reasons: [wrapper.vm.$options.i18n.linkedPipelinesFetchError],
- },
- ]);
- });
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/header/validation_segment_spec.js b/spec/frontend/pipeline_editor/components/header/validation_segment_spec.js
deleted file mode 100644
index 1ad621e6f45..00000000000
--- a/spec/frontend/pipeline_editor/components/header/validation_segment_spec.js
+++ /dev/null
@@ -1,197 +0,0 @@
-import VueApollo from 'vue-apollo';
-import { GlIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import { escape } from 'lodash';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { sprintf } from '~/locale';
-import ValidationSegment, {
- i18n,
-} from '~/pipeline_editor/components/header/validation_segment.vue';
-import getAppStatus from '~/pipeline_editor/graphql/queries/client/app_status.query.graphql';
-import {
- CI_CONFIG_STATUS_INVALID,
- EDITOR_APP_STATUS_EMPTY,
- EDITOR_APP_STATUS_INVALID,
- EDITOR_APP_STATUS_LOADING,
- EDITOR_APP_STATUS_LINT_UNAVAILABLE,
- EDITOR_APP_STATUS_VALID,
-} from '~/pipeline_editor/constants';
-import {
- mergeUnwrappedCiConfig,
- mockCiYml,
- mockLintUnavailableHelpPagePath,
- mockYmlHelpPagePath,
-} from '../../mock_data';
-
-Vue.use(VueApollo);
-
-describe('Validation segment component', () => {
- let wrapper;
-
- const mockApollo = createMockApollo();
-
- const createComponent = ({ props = {}, appStatus = EDITOR_APP_STATUS_INVALID }) => {
- mockApollo.clients.defaultClient.cache.writeQuery({
- query: getAppStatus,
- data: {
- app: {
- __typename: 'PipelineEditorApp',
- status: appStatus,
- },
- },
- });
-
- wrapper = extendedWrapper(
- shallowMount(ValidationSegment, {
- apolloProvider: mockApollo,
- provide: {
- ymlHelpPagePath: mockYmlHelpPagePath,
- lintUnavailableHelpPagePath: mockLintUnavailableHelpPagePath,
- },
- propsData: {
- ciConfig: mergeUnwrappedCiConfig(),
- ciFileContent: mockCiYml,
- ...props,
- },
- }),
- );
- };
-
- const findIcon = () => wrapper.findComponent(GlIcon);
- const findLearnMoreLink = () => wrapper.findByTestId('learnMoreLink');
- const findValidationMsg = () => wrapper.findByTestId('validationMsg');
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('shows the loading state', () => {
- createComponent({ appStatus: EDITOR_APP_STATUS_LOADING });
-
- expect(wrapper.text()).toBe(i18n.loading);
- });
-
- describe('when config is empty', () => {
- beforeEach(() => {
- createComponent({ appStatus: EDITOR_APP_STATUS_EMPTY });
- });
-
- it('has check icon', () => {
- expect(findIcon().props('name')).toBe('check');
- });
-
- it('shows a message for empty state', () => {
- expect(findValidationMsg().text()).toBe(i18n.empty);
- });
- });
-
- describe('when config is valid', () => {
- beforeEach(() => {
- createComponent({ appStatus: EDITOR_APP_STATUS_VALID });
- });
-
- it('has check icon', () => {
- expect(findIcon().props('name')).toBe('check');
- });
-
- it('shows a message for valid state', () => {
- expect(findValidationMsg().text()).toContain(i18n.valid);
- });
-
- it('shows the learn more link', () => {
- expect(findLearnMoreLink().attributes('href')).toBe(mockYmlHelpPagePath);
- expect(findLearnMoreLink().text()).toBe(i18n.learnMore);
- });
- });
-
- describe('when config is invalid', () => {
- beforeEach(() => {
- createComponent({
- appStatus: EDITOR_APP_STATUS_INVALID,
- });
- });
-
- it('has warning icon', () => {
- expect(findIcon().props('name')).toBe('warning-solid');
- });
-
- it('has message for invalid state', () => {
- expect(findValidationMsg().text()).toBe(i18n.invalid);
- });
-
- it('shows the learn more link', () => {
- expect(findLearnMoreLink().attributes('href')).toBe(mockYmlHelpPagePath);
- expect(findLearnMoreLink().text()).toBe('Learn more');
- });
-
- describe('with multiple errors', () => {
- const firstError = 'First Error';
- const secondError = 'Second Error';
-
- beforeEach(() => {
- createComponent({
- props: {
- ciConfig: mergeUnwrappedCiConfig({
- status: CI_CONFIG_STATUS_INVALID,
- errors: [firstError, secondError],
- }),
- },
- });
- });
- it('shows an invalid state with an error', () => {
- // Test the error is shown _and_ the string matches
- expect(findValidationMsg().text()).toContain(firstError);
- expect(findValidationMsg().text()).toBe(
- sprintf(i18n.invalidWithReason, { reason: firstError }),
- );
- });
- });
-
- describe('with XSS inside the error', () => {
- const evilError = '<script>evil();</script>';
-
- beforeEach(() => {
- createComponent({
- props: {
- ciConfig: mergeUnwrappedCiConfig({
- status: CI_CONFIG_STATUS_INVALID,
- errors: [evilError],
- }),
- },
- });
- });
- it('shows an invalid state with an error while preventing XSS', () => {
- const { innerHTML } = findValidationMsg().element;
-
- expect(innerHTML).not.toContain(evilError);
- expect(innerHTML).toContain(escape(evilError));
- });
- });
- });
-
- describe('when the lint service is unavailable', () => {
- beforeEach(() => {
- createComponent({
- appStatus: EDITOR_APP_STATUS_LINT_UNAVAILABLE,
- props: {
- ciConfig: {},
- },
- });
- });
-
- it('show a message that the service is unavailable', () => {
- expect(findValidationMsg().text()).toBe(i18n.unavailableValidation);
- });
-
- it('shows the time-out icon', () => {
- expect(findIcon().props('name')).toBe('time-out');
- });
-
- it('shows the learn more link', () => {
- expect(findLearnMoreLink().attributes('href')).toBe(mockLintUnavailableHelpPagePath);
- expect(findLearnMoreLink().text()).toBe(i18n.learnMore);
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/lint/ci_lint_results_spec.js b/spec/frontend/pipeline_editor/components/lint/ci_lint_results_spec.js
deleted file mode 100644
index 7f89eda4dff..00000000000
--- a/spec/frontend/pipeline_editor/components/lint/ci_lint_results_spec.js
+++ /dev/null
@@ -1,177 +0,0 @@
-import { GlTableLite, GlLink } from '@gitlab/ui';
-import { shallowMount, mount } from '@vue/test-utils';
-import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
-import CiLintResults from '~/pipeline_editor/components/lint/ci_lint_results.vue';
-import { mockJobs, mockErrors, mockWarnings } from '../../mock_data';
-
-describe('CI Lint Results', () => {
- let wrapper;
- const defaultProps = {
- isValid: true,
- jobs: mockJobs,
- errors: [],
- warnings: [],
- dryRun: false,
- lintHelpPagePath: '/help',
- };
-
- const createComponent = (props = {}, mountFn = shallowMount) => {
- wrapper = mountFn(CiLintResults, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- });
- };
-
- const findTable = () => wrapper.findComponent(GlTableLite);
- const findByTestId = (selector) => () => wrapper.find(`[data-testid="ci-lint-${selector}"]`);
- const findAllByTestId = (selector) => () =>
- wrapper.findAll(`[data-testid="ci-lint-${selector}"]`);
- const findLinkToDoc = () => wrapper.findComponent(GlLink);
- const findErrors = findByTestId('errors');
- const findWarnings = findByTestId('warnings');
- const findStatus = findByTestId('status');
- const findOnlyExcept = findByTestId('only-except');
- const findLintParameters = findAllByTestId('parameter');
- const findLintValues = findAllByTestId('value');
- const findBeforeScripts = findAllByTestId('before-script');
- const findScripts = findAllByTestId('script');
- const findAfterScripts = findAllByTestId('after-script');
- const filterEmptyScripts = (property) => mockJobs.filter((job) => job[property].length !== 0);
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('Empty results', () => {
- it('renders with no jobs, errors or warnings defined', () => {
- createComponent({ jobs: undefined, errors: undefined, warnings: undefined }, shallowMount);
- expect(findTable().exists()).toBe(true);
- });
-
- it('renders when job has no properties defined', () => {
- // job with no attributes such as `tagList` or `environment`
- const job = {
- stage: 'Stage Name',
- name: 'test job',
- };
- createComponent({ jobs: [job] }, mount);
-
- const param = findLintParameters().at(0);
- const value = findLintValues().at(0);
-
- expect(param.text()).toBe(`${job.stage} Job - ${job.name}`);
-
- // This test should be updated once properties of each job are shown
- // See https://gitlab.com/gitlab-org/gitlab/-/issues/291031
- expect(value.text()).toBe('');
- });
- });
-
- describe('Invalid results', () => {
- beforeEach(() => {
- createComponent({ isValid: false, errors: mockErrors, warnings: mockWarnings }, mount);
- });
-
- it('does not display the table', () => {
- expect(findTable().exists()).toBe(false);
- });
-
- it('displays the invalid status', () => {
- expect(findStatus().text()).toContain(`Status: ${wrapper.vm.$options.incorrect.text}`);
- expect(findStatus().props('variant')).toBe(wrapper.vm.$options.incorrect.variant);
- });
-
- it('contains the link to documentation', () => {
- expect(findLinkToDoc().text()).toBe('More information');
- expect(findLinkToDoc().attributes('href')).toBe(defaultProps.lintHelpPagePath);
- });
-
- it('displays the error message', () => {
- const [expectedError] = mockErrors;
-
- expect(findErrors().text()).toBe(expectedError);
- });
-
- it('displays the warning message', () => {
- const [expectedWarning] = mockWarnings;
-
- expect(findWarnings().exists()).toBe(true);
- expect(findWarnings().text()).toContain(expectedWarning);
- });
- });
-
- describe('Valid results with dry run', () => {
- beforeEach(() => {
- createComponent({ dryRun: true }, mount);
- });
-
- it('displays table', () => {
- expect(findTable().exists()).toBe(true);
- });
-
- it('displays the valid status', () => {
- expect(findStatus().text()).toContain(wrapper.vm.$options.correct.text);
- expect(findStatus().props('variant')).toBe(wrapper.vm.$options.correct.variant);
- });
-
- it('does not display only/expect values with dry run', () => {
- expect(findOnlyExcept().exists()).toBe(false);
- });
-
- it('contains the link to documentation', () => {
- expect(findLinkToDoc().text()).toBe('More information');
- expect(findLinkToDoc().attributes('href')).toBe(defaultProps.lintHelpPagePath);
- });
- });
-
- describe('Lint results', () => {
- beforeEach(() => {
- createComponent({}, mount);
- });
-
- it('formats parameter value', () => {
- findLintParameters().wrappers.forEach((job, index) => {
- const { stage } = mockJobs[index];
- const { name } = mockJobs[index];
-
- expect(job.text()).toBe(`${capitalizeFirstCharacter(stage)} Job - ${name}`);
- });
- });
-
- it('only shows before scripts when data is present', () => {
- expect(findBeforeScripts()).toHaveLength(filterEmptyScripts('beforeScript').length);
- });
-
- it('only shows script when data is present', () => {
- expect(findScripts()).toHaveLength(filterEmptyScripts('script').length);
- });
-
- it('only shows after script when data is present', () => {
- expect(findAfterScripts()).toHaveLength(filterEmptyScripts('afterScript').length);
- });
- });
-
- describe('Hide Alert', () => {
- it('hides alert on success if hide-alert prop is true', async () => {
- await createComponent({ dryRun: true, hideAlert: true }, mount);
-
- expect(findStatus().exists()).toBe(false);
- });
-
- it('hides alert on error if hide-alert prop is true', async () => {
- await createComponent(
- {
- hideAlert: true,
- isValid: false,
- errors: mockErrors,
- warnings: mockWarnings,
- },
- mount,
- );
-
- expect(findStatus().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/lint/ci_lint_warnings_spec.js b/spec/frontend/pipeline_editor/components/lint/ci_lint_warnings_spec.js
deleted file mode 100644
index 36052a2e16a..00000000000
--- a/spec/frontend/pipeline_editor/components/lint/ci_lint_warnings_spec.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import { GlAlert, GlSprintf } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import { trimText } from 'helpers/text_helper';
-import CiLintWarnings from '~/pipeline_editor/components/lint/ci_lint_warnings.vue';
-
-const warnings = ['warning 1', 'warning 2', 'warning 3'];
-
-describe('CI lint warnings', () => {
- let wrapper;
-
- const createComponent = (limit = 25) => {
- wrapper = mount(CiLintWarnings, {
- propsData: {
- warnings,
- maxWarnings: limit,
- },
- });
- };
-
- const findWarningAlert = () => wrapper.findComponent(GlAlert);
- const findWarnings = () => wrapper.findAll('[data-testid="ci-lint-warning"]');
- const findWarningMessage = () => trimText(wrapper.findComponent(GlSprintf).text());
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- it('displays the warning alert', () => {
- createComponent();
-
- expect(findWarningAlert().exists()).toBe(true);
- });
-
- it('displays all the warnings', () => {
- createComponent();
-
- expect(findWarnings()).toHaveLength(warnings.length);
- });
-
- it('shows the correct message when the limit is not passed', () => {
- createComponent();
-
- expect(findWarningMessage()).toBe(`${warnings.length} warnings found:`);
- });
-
- it('shows the correct message when the limit is passed', () => {
- const limit = 2;
-
- createComponent(limit);
-
- expect(findWarningMessage()).toBe(`${warnings.length} warnings found: showing first ${limit}`);
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js b/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js
deleted file mode 100644
index 27707f8b01a..00000000000
--- a/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js
+++ /dev/null
@@ -1,342 +0,0 @@
-// TODO
-
-import { GlAlert, GlBadge, GlLoadingIcon, GlTabs } from '@gitlab/ui';
-import { mount, shallowMount } from '@vue/test-utils';
-import VueApollo from 'vue-apollo';
-import Vue, { nextTick } from 'vue';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import setWindowLocation from 'helpers/set_window_location_helper';
-import CiConfigMergedPreview from '~/pipeline_editor/components/editor/ci_config_merged_preview.vue';
-import CiValidate from '~/pipeline_editor/components/validate/ci_validate.vue';
-import WalkthroughPopover from '~/pipeline_editor/components/popovers/walkthrough_popover.vue';
-import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue';
-import EditorTab from '~/pipeline_editor/components/ui/editor_tab.vue';
-import {
- CREATE_TAB,
- EDITOR_APP_STATUS_EMPTY,
- EDITOR_APP_STATUS_LOADING,
- EDITOR_APP_STATUS_INVALID,
- EDITOR_APP_STATUS_VALID,
- TAB_QUERY_PARAM,
- VALIDATE_TAB,
- VALIDATE_TAB_BADGE_DISMISSED_KEY,
-} from '~/pipeline_editor/constants';
-import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
-import getBlobContent from '~/pipeline_editor/graphql/queries/blob_content.query.graphql';
-import {
- mockBlobContentQueryResponse,
- mockCiLintPath,
- mockCiYml,
- mockLintResponse,
- mockLintResponseWithoutMerged,
-} from '../mock_data';
-
-Vue.use(VueApollo);
-
-Vue.config.ignoredElements = ['gl-emoji'];
-
-describe('Pipeline editor tabs component', () => {
- let wrapper;
- const MockTextEditor = {
- template: '<div />',
- };
-
- const createComponent = ({
- listeners = {},
- props = {},
- provide = {},
- appStatus = EDITOR_APP_STATUS_VALID,
- mountFn = shallowMount,
- options = {},
- } = {}) => {
- wrapper = mountFn(PipelineEditorTabs, {
- propsData: {
- ciConfigData: mockLintResponse,
- ciFileContent: mockCiYml,
- currentTab: CREATE_TAB,
- isNewCiConfigFile: true,
- showDrawer: false,
- ...props,
- },
- data() {
- return {
- appStatus,
- };
- },
- provide: {
- ciConfigPath: '/path/to/ci-config',
- ciLintPath: mockCiLintPath,
- currentBranch: 'main',
- projectFullPath: '/path/to/project',
- simulatePipelineHelpPagePath: 'path/to/help/page',
- validateTabIllustrationPath: 'path/to/svg',
- ...provide,
- },
- stubs: {
- TextEditor: MockTextEditor,
- EditorTab,
- },
- listeners,
- ...options,
- });
- };
-
- let mockBlobContentData;
- let mockApollo;
-
- const createComponentWithApollo = ({ props, provide = {}, mountFn = shallowMount } = {}) => {
- const handlers = [[getBlobContent, mockBlobContentData]];
- mockApollo = createMockApollo(handlers);
-
- createComponent({
- props,
- provide,
- mountFn,
- options: {
- apolloProvider: mockApollo,
- },
- });
- };
-
- const findEditorTab = () => wrapper.find('[data-testid="editor-tab"]');
- const findMergedTab = () => wrapper.find('[data-testid="merged-tab"]');
- const findValidateTab = () => wrapper.find('[data-testid="validate-tab"]');
- const findVisualizationTab = () => wrapper.find('[data-testid="visualization-tab"]');
-
- const findAlert = () => wrapper.findComponent(GlAlert);
- const findBadge = () => wrapper.findComponent(GlBadge);
- const findCiValidate = () => wrapper.findComponent(CiValidate);
- const findGlTabs = () => wrapper.findComponent(GlTabs);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findPipelineGraph = () => wrapper.findComponent(PipelineGraph);
- const findTextEditor = () => wrapper.findComponent(MockTextEditor);
- const findMergedPreview = () => wrapper.findComponent(CiConfigMergedPreview);
- const findWalkthroughPopover = () => wrapper.findComponent(WalkthroughPopover);
-
- beforeEach(() => {
- mockBlobContentData = jest.fn();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('editor tab', () => {
- it('displays editor only after the tab is mounted', async () => {
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
- createComponentWithApollo({ mountFn: mount });
-
- expect(findTextEditor().exists()).toBe(false);
-
- await nextTick();
-
- expect(findTextEditor().exists()).toBe(true);
- expect(findEditorTab().exists()).toBe(true);
- });
- });
-
- describe('visualization tab', () => {
- describe('while loading', () => {
- beforeEach(() => {
- createComponent({ appStatus: EDITOR_APP_STATUS_LOADING });
- });
-
- it('displays a loading icon if the lint query is loading', () => {
- expect(findLoadingIcon().exists()).toBe(true);
- expect(findPipelineGraph().exists()).toBe(false);
- });
- });
- describe('after loading', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('display the tab and visualization', () => {
- expect(findVisualizationTab().exists()).toBe(true);
- expect(findPipelineGraph().exists()).toBe(true);
- });
- });
- });
-
- describe('validate tab', () => {
- describe('after loading', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('displays the tab and the validate component', () => {
- expect(findValidateTab().exists()).toBe(true);
- expect(findCiValidate().exists()).toBe(true);
- });
- });
-
- describe('NEW badge', () => {
- describe('default', () => {
- beforeEach(() => {
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
- createComponentWithApollo({
- mountFn: mount,
- props: {
- currentTab: VALIDATE_TAB,
- },
- });
- });
-
- it('renders badge by default', () => {
- expect(findBadge().exists()).toBe(true);
- expect(findBadge().text()).toBe(wrapper.vm.$options.i18n.new);
- });
-
- it('hides badge when moving away from the validate tab', async () => {
- expect(findBadge().exists()).toBe(true);
-
- await findEditorTab().vm.$emit('click');
-
- expect(findBadge().exists()).toBe(false);
- });
- });
-
- describe('if badge has been dismissed before', () => {
- beforeEach(() => {
- localStorage.setItem(VALIDATE_TAB_BADGE_DISMISSED_KEY, 'true');
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
- createComponentWithApollo({ mountFn: mount });
- });
-
- it('does not render badge if it has been dismissed before', () => {
- expect(findBadge().exists()).toBe(false);
- });
- });
- });
- });
-
- describe('merged tab', () => {
- describe('while loading', () => {
- beforeEach(() => {
- createComponent({ appStatus: EDITOR_APP_STATUS_LOADING });
- });
-
- it('displays a loading icon if the lint query is loading', () => {
- expect(findLoadingIcon().exists()).toBe(true);
- });
- });
-
- describe('when there is a fetch error', () => {
- beforeEach(() => {
- createComponent({ props: { ciConfigData: mockLintResponseWithoutMerged } });
- });
-
- it('show an error message', () => {
- expect(findAlert().exists()).toBe(true);
- expect(findAlert().text()).toBe(wrapper.vm.$options.errorTexts.loadMergedYaml);
- });
-
- it('does not render the `merged_preview` component', () => {
- expect(findMergedPreview().exists()).toBe(false);
- });
- });
-
- describe('after loading', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('display the tab and the merged preview component', () => {
- expect(findMergedTab().exists()).toBe(true);
- expect(findMergedPreview().exists()).toBe(true);
- });
- });
- });
-
- describe('show tab content based on status', () => {
- it.each`
- appStatus | editor | viz | validate | merged
- ${undefined} | ${true} | ${true} | ${true} | ${true}
- ${EDITOR_APP_STATUS_EMPTY} | ${true} | ${false} | ${true} | ${false}
- ${EDITOR_APP_STATUS_INVALID} | ${true} | ${false} | ${true} | ${true}
- ${EDITOR_APP_STATUS_VALID} | ${true} | ${true} | ${true} | ${true}
- `(
- 'when status is $appStatus, we show - editor:$editor | viz:$viz | validate:$validate | merged:$merged',
- ({ appStatus, editor, viz, validate, merged }) => {
- createComponent({ appStatus });
-
- expect(findTextEditor().exists()).toBe(editor);
- expect(findPipelineGraph().exists()).toBe(viz);
- expect(findValidateTab().exists()).toBe(validate);
- expect(findMergedPreview().exists()).toBe(merged);
- },
- );
- });
-
- describe('default tab based on url query param', () => {
- const gitlabUrl = 'https://gitlab.test/ci/editor/';
- const matchObject = {
- hostname: 'gitlab.test',
- pathname: '/ci/editor/',
- search: '',
- };
-
- it(`is ${CREATE_TAB} if the query param ${TAB_QUERY_PARAM} is not present`, () => {
- setWindowLocation(gitlabUrl);
- createComponent();
-
- expect(window.location).toMatchObject(matchObject);
- });
-
- it(`is ${CREATE_TAB} tab if the query param ${TAB_QUERY_PARAM} is invalid`, () => {
- const queryValue = 'FOO';
- setWindowLocation(`${gitlabUrl}?${TAB_QUERY_PARAM}=${queryValue}`);
- createComponent();
-
- // If the query param remains unchanged, then we have ignored it.
- expect(window.location).toMatchObject({
- ...matchObject,
- search: `?${TAB_QUERY_PARAM}=${queryValue}`,
- });
- });
- });
-
- describe('glTabs', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('passes the `sync-active-tab-with-query-params` prop', () => {
- expect(findGlTabs().props('syncActiveTabWithQueryParams')).toBe(true);
- });
- });
-
- describe('pipeline editor walkthrough', () => {
- describe('when isNewCiConfigFile prop is true (default)', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('shows walkthrough popover', async () => {
- expect(findWalkthroughPopover().exists()).toBe(true);
- });
- });
-
- describe('when isNewCiConfigFile prop is false', () => {
- it('does not show walkthrough popover', async () => {
- createComponent({ props: { isNewCiConfigFile: false } });
- expect(findWalkthroughPopover().exists()).toBe(false);
- });
- });
- });
-
- it('sets listeners on walkthrough popover', async () => {
- const handler = jest.fn();
-
- createComponent({
- listeners: {
- event: handler,
- },
- });
- await nextTick();
-
- findWalkthroughPopover().vm.$emit('event');
-
- expect(handler).toHaveBeenCalled();
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/popovers/file_tree_popover_spec.js b/spec/frontend/pipeline_editor/components/popovers/file_tree_popover_spec.js
deleted file mode 100644
index 98ce3f6ea40..00000000000
--- a/spec/frontend/pipeline_editor/components/popovers/file_tree_popover_spec.js
+++ /dev/null
@@ -1,56 +0,0 @@
-import { nextTick } from 'vue';
-import { GlLink, GlPopover, GlSprintf } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import FileTreePopover from '~/pipeline_editor/components/popovers/file_tree_popover.vue';
-import { FILE_TREE_POPOVER_DISMISSED_KEY } from '~/pipeline_editor/constants';
-import { mockIncludesHelpPagePath } from '../../mock_data';
-
-describe('FileTreePopover component', () => {
- let wrapper;
-
- const findPopover = () => wrapper.findComponent(GlPopover);
- const findLink = () => findPopover().findComponent(GlLink);
-
- const createComponent = ({ stubs } = {}) => {
- wrapper = shallowMount(FileTreePopover, {
- provide: {
- includesHelpPagePath: mockIncludesHelpPagePath,
- },
- stubs,
- });
- };
-
- afterEach(() => {
- localStorage.clear();
- wrapper.destroy();
- });
-
- describe('default', () => {
- beforeEach(async () => {
- createComponent({ stubs: { GlSprintf } });
- });
-
- it('renders dismissable popover', async () => {
- expect(findPopover().exists()).toBe(true);
-
- findPopover().vm.$emit('close-button-clicked');
- await nextTick();
-
- expect(findPopover().exists()).toBe(false);
- });
-
- it('renders learn more link', () => {
- expect(findLink().exists()).toBe(true);
- expect(findLink().attributes('href')).toBe(mockIncludesHelpPagePath);
- });
- });
-
- describe('when popover has already been dismissed before', () => {
- it('does not render popover', async () => {
- localStorage.setItem(FILE_TREE_POPOVER_DISMISSED_KEY, 'true');
- createComponent();
-
- expect(findPopover().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/popovers/validate_pipeline_popover_spec.js b/spec/frontend/pipeline_editor/components/popovers/validate_pipeline_popover_spec.js
deleted file mode 100644
index 97f785a71bc..00000000000
--- a/spec/frontend/pipeline_editor/components/popovers/validate_pipeline_popover_spec.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import { GlLink, GlSprintf } from '@gitlab/ui';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import ValidatePopover from '~/pipeline_editor/components/popovers/validate_pipeline_popover.vue';
-import { VALIDATE_TAB_FEEDBACK_URL } from '~/pipeline_editor/constants';
-import { mockSimulatePipelineHelpPagePath } from '../../mock_data';
-
-describe('ValidatePopover component', () => {
- let wrapper;
-
- const createComponent = ({ stubs } = {}) => {
- wrapper = shallowMountExtended(ValidatePopover, {
- provide: {
- simulatePipelineHelpPagePath: mockSimulatePipelineHelpPagePath,
- },
- stubs,
- });
- };
-
- const findHelpLink = () => wrapper.findByTestId('help-link');
- const findFeedbackLink = () => wrapper.findByTestId('feedback-link');
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('template', () => {
- beforeEach(async () => {
- createComponent({
- stubs: { GlLink, GlSprintf },
- });
- });
-
- it('renders help link', () => {
- expect(findHelpLink().exists()).toBe(true);
- expect(findHelpLink().attributes('href')).toBe(mockSimulatePipelineHelpPagePath);
- });
-
- it('renders feedback link', () => {
- expect(findFeedbackLink().exists()).toBe(true);
- expect(findFeedbackLink().attributes('href')).toBe(VALIDATE_TAB_FEEDBACK_URL);
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/popovers/walkthrough_popover_spec.js b/spec/frontend/pipeline_editor/components/popovers/walkthrough_popover_spec.js
deleted file mode 100644
index b86c82850c5..00000000000
--- a/spec/frontend/pipeline_editor/components/popovers/walkthrough_popover_spec.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import { mount, shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import WalkthroughPopover from '~/pipeline_editor/components/popovers/walkthrough_popover.vue';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-
-Vue.config.ignoredElements = ['gl-emoji'];
-
-describe('WalkthroughPopover component', () => {
- let wrapper;
-
- const createComponent = (mountFn = shallowMount) => {
- return extendedWrapper(mountFn(WalkthroughPopover));
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('CTA button clicked', () => {
- beforeEach(async () => {
- wrapper = createComponent(mount);
- await wrapper.findByTestId('ctaBtn').trigger('click');
- });
-
- it('emits "walkthrough-popover-cta-clicked" event', async () => {
- expect(wrapper.emitted()['walkthrough-popover-cta-clicked']).toHaveLength(1);
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/ui/confirm_unsaved_changes_dialog_spec.js b/spec/frontend/pipeline_editor/components/ui/confirm_unsaved_changes_dialog_spec.js
deleted file mode 100644
index 44fda2812d8..00000000000
--- a/spec/frontend/pipeline_editor/components/ui/confirm_unsaved_changes_dialog_spec.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import ConfirmDialog from '~/pipeline_editor/components/ui/confirm_unsaved_changes_dialog.vue';
-
-describe('pipeline_editor/components/ui/confirm_unsaved_changes_dialog', () => {
- let beforeUnloadEvent;
- let setDialogContent;
- let wrapper;
-
- const createComponent = (propsData = {}) => {
- wrapper = shallowMount(ConfirmDialog, {
- propsData,
- });
- };
-
- beforeEach(() => {
- beforeUnloadEvent = new Event('beforeunload');
- jest.spyOn(beforeUnloadEvent, 'preventDefault');
- setDialogContent = jest.spyOn(beforeUnloadEvent, 'returnValue', 'set');
- });
-
- afterEach(() => {
- beforeUnloadEvent.preventDefault.mockRestore();
- setDialogContent.mockRestore();
- wrapper.destroy();
- });
-
- it('shows confirmation dialog when there are unsaved changes', () => {
- createComponent({ hasUnsavedChanges: true });
- window.dispatchEvent(beforeUnloadEvent);
-
- expect(beforeUnloadEvent.preventDefault).toHaveBeenCalled();
- expect(setDialogContent).toHaveBeenCalledWith('');
- });
-
- it('does not show confirmation dialog when there are no unsaved changes', () => {
- createComponent({ hasUnsavedChanges: false });
- window.dispatchEvent(beforeUnloadEvent);
-
- expect(beforeUnloadEvent.preventDefault).not.toHaveBeenCalled();
- expect(setDialogContent).not.toHaveBeenCalled();
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js b/spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js
deleted file mode 100644
index 24f27e8c5fb..00000000000
--- a/spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js
+++ /dev/null
@@ -1,200 +0,0 @@
-import { GlAlert, GlBadge, GlTabs } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import EditorTab from '~/pipeline_editor/components/ui/editor_tab.vue';
-
-const mockContent1 = 'MOCK CONTENT 1';
-const mockContent2 = 'MOCK CONTENT 2';
-
-const MockSourceEditor = {
- template: '<div>EDITOR</div>',
-};
-
-describe('~/pipeline_editor/components/ui/editor_tab.vue', () => {
- let wrapper;
- let mockChildMounted = jest.fn();
-
- const MockChild = {
- props: ['content'],
- template: '<div>{{content}}</div>',
- mounted() {
- mockChildMounted(this.content);
- },
- };
-
- const MockTabbedContent = {
- components: {
- EditorTab,
- GlTabs,
- MockChild,
- },
- template: `
- <gl-tabs>
- <editor-tab title="Tab 1" :title-link-attributes="{ 'data-testid': 'tab1-btn' }" :lazy="true">
- <mock-child content="${mockContent1}"/>
- </editor-tab>
- <editor-tab title="Tab 2" :title-link-attributes="{ 'data-testid': 'tab2-btn' }" :lazy="true" badge-title="NEW">
- <mock-child content="${mockContent2}"/>
- </editor-tab>
- </gl-tabs>
- `,
- };
-
- const createMockedWrapper = () => {
- wrapper = mount(MockTabbedContent);
- };
-
- const createWrapper = ({ props } = {}) => {
- wrapper = mount(EditorTab, {
- propsData: {
- title: 'Tab 1',
- ...props,
- },
- slots: {
- default: MockSourceEditor,
- },
- });
- };
-
- const findSlotComponent = () => wrapper.findComponent(MockSourceEditor);
- const findAlert = () => wrapper.findComponent(GlAlert);
- const findBadges = () => wrapper.findAllComponents(GlBadge);
-
- beforeEach(() => {
- mockChildMounted = jest.fn();
- });
-
- it('tabs are mounted lazily', async () => {
- createMockedWrapper();
-
- expect(mockChildMounted).toHaveBeenCalledTimes(0);
- });
-
- it('first tab is only mounted after nextTick', async () => {
- createMockedWrapper();
-
- await nextTick();
-
- expect(mockChildMounted).toHaveBeenCalledTimes(1);
- expect(mockChildMounted).toHaveBeenCalledWith(mockContent1);
- });
-
- describe('alerts', () => {
- describe('unavailable state', () => {
- beforeEach(() => {
- createWrapper({ props: { isUnavailable: true } });
- });
-
- it('shows the invalid alert when the status is invalid', () => {
- const alert = findAlert();
-
- expect(alert.exists()).toBe(true);
- expect(alert.text()).toContain(wrapper.vm.$options.i18n.unavailable);
- });
- });
-
- describe('invalid state', () => {
- beforeEach(() => {
- createWrapper({ props: { isInvalid: true } });
- });
-
- it('shows the invalid alert when the status is invalid', () => {
- const alert = findAlert();
-
- expect(alert.exists()).toBe(true);
- expect(alert.text()).toBe(wrapper.vm.$options.i18n.invalid);
- });
- });
-
- describe('empty state', () => {
- const text = 'my custom alert message';
-
- beforeEach(() => {
- createWrapper({
- props: { isEmpty: true, emptyMessage: text },
- });
- });
-
- it('displays an empty message', () => {
- createWrapper({
- props: { isEmpty: true },
- });
-
- const alert = findAlert();
-
- expect(alert.exists()).toBe(true);
- expect(alert.text()).toBe(
- 'This tab will be usable when the CI/CD configuration file is populated with valid syntax.',
- );
- });
-
- it('can have a custom empty message', () => {
- const alert = findAlert();
-
- expect(alert.exists()).toBe(true);
- expect(alert.text()).toBe(text);
- });
- });
- });
-
- describe('showing the tab content depending on `isEmpty`, `isUnavailable` and `isInvalid`', () => {
- it.each`
- isEmpty | isUnavailable | isInvalid | showSlotComponent | text
- ${undefined} | ${undefined} | ${undefined} | ${true} | ${'renders'}
- ${false} | ${false} | ${false} | ${true} | ${'renders'}
- ${undefined} | ${true} | ${true} | ${false} | ${'hides'}
- ${true} | ${false} | ${false} | ${false} | ${'hides'}
- ${false} | ${true} | ${false} | ${false} | ${'hides'}
- ${false} | ${false} | ${true} | ${false} | ${'hides'}
- `(
- '$text the slot component when isEmpty:$isEmpty, isUnavailable:$isUnavailable and isInvalid:$isInvalid',
- ({ isEmpty, isUnavailable, isInvalid, showSlotComponent }) => {
- createWrapper({
- props: { isEmpty, isUnavailable, isInvalid },
- });
- expect(findSlotComponent().exists()).toBe(showSlotComponent);
- expect(findAlert().exists()).toBe(!showSlotComponent);
- },
- );
- });
-
- describe('user interaction', () => {
- const clickTab = async (testid) => {
- wrapper.find(`[data-testid="${testid}"]`).trigger('click');
- await nextTick();
- };
-
- beforeEach(() => {
- createMockedWrapper();
- });
-
- it('mounts a tab once after selecting it', async () => {
- await clickTab('tab2-btn');
-
- expect(mockChildMounted).toHaveBeenCalledTimes(2);
- expect(mockChildMounted).toHaveBeenNthCalledWith(1, mockContent1);
- expect(mockChildMounted).toHaveBeenNthCalledWith(2, mockContent2);
- });
-
- it('mounts each tab once after selecting each', async () => {
- await clickTab('tab2-btn');
- await clickTab('tab1-btn');
- await clickTab('tab2-btn');
-
- expect(mockChildMounted).toHaveBeenCalledTimes(2);
- expect(mockChildMounted).toHaveBeenNthCalledWith(1, mockContent1);
- expect(mockChildMounted).toHaveBeenNthCalledWith(2, mockContent2);
- });
- });
-
- describe('valid state', () => {
- beforeEach(() => {
- createMockedWrapper();
- });
-
- it('renders correct number of badges', async () => {
- expect(findBadges()).toHaveLength(1);
- expect(findBadges().at(0).text()).toBe('NEW');
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/ui/pipeline_editor_empty_state_spec.js b/spec/frontend/pipeline_editor/components/ui/pipeline_editor_empty_state_spec.js
deleted file mode 100644
index c76c3460e99..00000000000
--- a/spec/frontend/pipeline_editor/components/ui/pipeline_editor_empty_state_spec.js
+++ /dev/null
@@ -1,92 +0,0 @@
-import { GlButton, GlSprintf } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import PipelineEditorFileNav from '~/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue';
-import PipelineEditorEmptyState from '~/pipeline_editor/components/ui/pipeline_editor_empty_state.vue';
-
-describe('Pipeline editor empty state', () => {
- let wrapper;
- const defaultProvide = {
- emptyStateIllustrationPath: 'my/svg/path',
- usesExternalConfig: false,
- };
-
- const createComponent = ({ provide } = {}) => {
- wrapper = shallowMount(PipelineEditorEmptyState, {
- provide: { ...defaultProvide, ...provide },
- });
- };
-
- const findFileNav = () => wrapper.findComponent(PipelineEditorFileNav);
- const findSvgImage = () => wrapper.find('img');
- const findTitle = () => wrapper.find('h1');
- const findExternalCiInstructions = () => wrapper.find('p');
- const findConfirmButton = () => wrapper.findComponent(GlButton);
- const findDescription = () => wrapper.findComponent(GlSprintf);
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('when project uses an external CI config', () => {
- beforeEach(() => {
- createComponent({
- provide: { usesExternalConfig: true },
- });
- });
-
- it('renders an svg image', () => {
- expect(findSvgImage().exists()).toBe(true);
- });
-
- it('renders the correct title and instructions', () => {
- expect(findTitle().exists()).toBe(true);
- expect(findExternalCiInstructions().exists()).toBe(true);
-
- expect(findExternalCiInstructions().html()).toContain(
- wrapper.vm.$options.i18n.externalCiInstructions,
- );
- expect(findTitle().text()).toBe(wrapper.vm.$options.i18n.externalCiNote);
- });
-
- it('does not render the CTA button', () => {
- expect(findConfirmButton().exists()).toBe(false);
- });
- });
-
- describe('when project uses an accessible CI config', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders an svg image', () => {
- expect(findSvgImage().exists()).toBe(true);
- });
-
- it('renders a title', () => {
- expect(findTitle().exists()).toBe(true);
- expect(findTitle().text()).toBe(wrapper.vm.$options.i18n.title);
- });
-
- it('renders a description', () => {
- expect(findDescription().exists()).toBe(true);
- expect(findDescription().html()).toContain(wrapper.vm.$options.i18n.body);
- });
-
- it('renders the file nav', () => {
- expect(findFileNav().exists()).toBe(true);
- });
-
- it('renders a CTA button', () => {
- expect(findConfirmButton().exists()).toBe(true);
- expect(findConfirmButton().text()).toBe(wrapper.vm.$options.i18n.btnText);
- });
-
- it('emits an event when clicking on the CTA', async () => {
- const expectedEvent = 'createEmptyConfigFile';
- expect(wrapper.emitted(expectedEvent)).toBeUndefined();
-
- await findConfirmButton().vm.$emit('click');
- expect(wrapper.emitted(expectedEvent)).toHaveLength(1);
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/ui/pipeline_editor_messages_spec.js b/spec/frontend/pipeline_editor/components/ui/pipeline_editor_messages_spec.js
deleted file mode 100644
index d9ecee31e83..00000000000
--- a/spec/frontend/pipeline_editor/components/ui/pipeline_editor_messages_spec.js
+++ /dev/null
@@ -1,149 +0,0 @@
-import { GlAlert } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import setWindowLocation from 'helpers/set_window_location_helper';
-import { TEST_HOST } from 'helpers/test_constants';
-import CodeSnippetAlert from '~/pipeline_editor/components/code_snippet_alert/code_snippet_alert.vue';
-import { CODE_SNIPPET_SOURCES } from '~/pipeline_editor/components/code_snippet_alert/constants';
-import PipelineEditorMessages from '~/pipeline_editor/components/ui/pipeline_editor_messages.vue';
-import {
- COMMIT_FAILURE,
- COMMIT_SUCCESS,
- COMMIT_SUCCESS_WITH_REDIRECT,
- DEFAULT_FAILURE,
- DEFAULT_SUCCESS,
- LOAD_FAILURE_UNKNOWN,
- PIPELINE_FAILURE,
-} from '~/pipeline_editor/constants';
-
-beforeEach(() => {
- setWindowLocation(TEST_HOST);
-});
-
-describe('Pipeline Editor messages', () => {
- let wrapper;
-
- const createComponent = (props = {}) => {
- wrapper = shallowMount(PipelineEditorMessages, {
- propsData: props,
- });
- };
-
- const findCodeSnippetAlert = () => wrapper.findComponent(CodeSnippetAlert);
- const findAlert = () => wrapper.findComponent(GlAlert);
-
- describe('success alert', () => {
- it('shows a message for successful commit type', () => {
- createComponent({ successType: COMMIT_SUCCESS, showSuccess: true });
-
- expect(findAlert().text()).toBe(wrapper.vm.$options.success[COMMIT_SUCCESS]);
- });
-
- it('shows a message for successful commit with redirect type', () => {
- createComponent({ successType: COMMIT_SUCCESS_WITH_REDIRECT, showSuccess: true });
-
- expect(findAlert().text()).toBe(wrapper.vm.$options.success[COMMIT_SUCCESS_WITH_REDIRECT]);
- });
-
- it('does not show alert when there is a successType but visibility is off', () => {
- createComponent({ successType: COMMIT_SUCCESS, showSuccess: false });
-
- expect(findAlert().exists()).toBe(false);
- });
-
- it('shows a success alert with default copy if `showSuccess` is true and the `successType` is not valid,', () => {
- createComponent({ successType: 'random', showSuccess: true });
-
- expect(findAlert().text()).toBe(wrapper.vm.$options.success[DEFAULT_SUCCESS]);
- });
-
- it('emit `hide-success` event when clicking on the dismiss button', async () => {
- const expectedEvent = 'hide-success';
-
- createComponent({ successType: COMMIT_SUCCESS, showSuccess: true });
- expect(wrapper.emitted(expectedEvent)).not.toBeDefined();
-
- await findAlert().vm.$emit('dismiss');
-
- expect(wrapper.emitted(expectedEvent)).toBeDefined();
- });
- });
-
- describe('failure alert', () => {
- it.each`
- failureType | message | expectedFailureType
- ${COMMIT_FAILURE} | ${'failed commit'} | ${COMMIT_FAILURE}
- ${LOAD_FAILURE_UNKNOWN} | ${'loading failure'} | ${LOAD_FAILURE_UNKNOWN}
- ${PIPELINE_FAILURE} | ${'pipeline failure'} | ${PIPELINE_FAILURE}
- ${'random'} | ${'error without a specified type'} | ${DEFAULT_FAILURE}
- `('shows a message for $message', ({ failureType, expectedFailureType }) => {
- createComponent({ failureType, showFailure: true });
-
- expect(findAlert().text()).toBe(wrapper.vm.$options.errors[expectedFailureType]);
- });
-
- it('show failure reasons when there are some', () => {
- const failureReasons = ['There was a problem', 'ouppps'];
- createComponent({ failureType: COMMIT_FAILURE, failureReasons, showFailure: true });
-
- expect(wrapper.html()).toContain(failureReasons[0]);
- expect(wrapper.html()).toContain(failureReasons[1]);
- });
-
- it('does not show a message for error with a disabled visibility', () => {
- createComponent({ failureType: 'random', showFailure: false });
-
- expect(findAlert().exists()).toBe(false);
- });
-
- it('emit `hide-failure` event when clicking on the dismiss button', async () => {
- const expectedEvent = 'hide-failure';
-
- createComponent({ failureType: COMMIT_FAILURE, showFailure: true });
- expect(wrapper.emitted(expectedEvent)).not.toBeDefined();
-
- await findAlert().vm.$emit('dismiss');
-
- expect(wrapper.emitted(expectedEvent)).toBeDefined();
- });
- });
-
- describe('code snippet alert', () => {
- const setCodeSnippetUrlParam = (value) => {
- setWindowLocation(`${TEST_HOST}/?code_snippet_copied_from=${value}`);
- };
-
- it('does not show by default', () => {
- createComponent();
-
- expect(findCodeSnippetAlert().exists()).toBe(false);
- });
-
- it.each(CODE_SNIPPET_SOURCES)('shows if URL param is %s, and cleans up URL', (source) => {
- jest.spyOn(window.history, 'replaceState');
- setCodeSnippetUrlParam(source);
- createComponent();
-
- expect(findCodeSnippetAlert().exists()).toBe(true);
- expect(window.history.replaceState).toHaveBeenCalledWith({}, document.title, `${TEST_HOST}/`);
- });
-
- it('does not show if URL param is invalid', () => {
- setCodeSnippetUrlParam('foo_bar');
- createComponent();
-
- expect(findCodeSnippetAlert().exists()).toBe(false);
- });
-
- it('disappears on dismiss', async () => {
- setCodeSnippetUrlParam('api_fuzzing');
- createComponent();
- const alert = findCodeSnippetAlert();
-
- expect(alert.exists()).toBe(true);
-
- await alert.vm.$emit('dismiss');
-
- expect(alert.exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/components/validate/ci_validate_spec.js b/spec/frontend/pipeline_editor/components/validate/ci_validate_spec.js
deleted file mode 100644
index 09d4f9736ad..00000000000
--- a/spec/frontend/pipeline_editor/components/validate/ci_validate_spec.js
+++ /dev/null
@@ -1,314 +0,0 @@
-import { GlAlert, GlDropdown, GlIcon, GlLoadingIcon, GlPopover } from '@gitlab/ui';
-import { nextTick } from 'vue';
-import { createLocalVue } from '@vue/test-utils';
-import VueApollo from 'vue-apollo';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import CiLintResults from '~/pipeline_editor/components/lint/ci_lint_results.vue';
-import CiValidate, { i18n } from '~/pipeline_editor/components/validate/ci_validate.vue';
-import ValidatePipelinePopover from '~/pipeline_editor/components/popovers/validate_pipeline_popover.vue';
-import getBlobContent from '~/pipeline_editor/graphql/queries/blob_content.query.graphql';
-import lintCIMutation from '~/pipeline_editor/graphql/mutations/client/lint_ci.mutation.graphql';
-import { pipelineEditorTrackingOptions } from '~/pipeline_editor/constants';
-import {
- mockBlobContentQueryResponse,
- mockCiLintPath,
- mockCiYml,
- mockSimulatePipelineHelpPagePath,
-} from '../../mock_data';
-import { mockLintDataError, mockLintDataValid } from '../../../ci_lint/mock_data';
-
-const localVue = createLocalVue();
-localVue.use(VueApollo);
-
-describe('Pipeline Editor Validate Tab', () => {
- let wrapper;
- let mockApollo;
- let mockBlobContentData;
- let trackingSpy;
-
- const createComponent = ({
- props,
- stubs,
- options,
- isBlobLoading = false,
- isSimulationLoading = false,
- } = {}) => {
- wrapper = shallowMountExtended(CiValidate, {
- propsData: {
- ciFileContent: mockCiYml,
- ...props,
- },
- provide: {
- ciConfigPath: '/path/to/ci-config',
- ciLintPath: mockCiLintPath,
- currentBranch: 'main',
- projectFullPath: '/path/to/project',
- validateTabIllustrationPath: '/path/to/img',
- simulatePipelineHelpPagePath: mockSimulatePipelineHelpPagePath,
- },
- stubs,
- mocks: {
- $apollo: {
- queries: {
- initialBlobContent: {
- loading: isBlobLoading,
- },
- },
- mutations: {
- lintCiMutation: {
- loading: isSimulationLoading,
- },
- },
- },
- },
- ...options,
- });
- };
-
- const createComponentWithApollo = ({ props, stubs } = {}) => {
- const handlers = [[getBlobContent, mockBlobContentData]];
- mockApollo = createMockApollo(handlers);
-
- createComponent({
- props,
- stubs,
- options: {
- localVue,
- apolloProvider: mockApollo,
- mocks: {},
- },
- });
- };
-
- const findAlert = () => wrapper.findComponent(GlAlert);
- const findCancelBtn = () => wrapper.findByTestId('cancel-simulation');
- const findContentChangeStatus = () => wrapper.findByTestId('content-status');
- const findCta = () => wrapper.findByTestId('simulate-pipeline-button');
- const findDisabledCtaTooltip = () => wrapper.findByTestId('cta-tooltip');
- const findHelpIcon = () => wrapper.findComponent(GlIcon);
- const findIllustration = () => wrapper.findByRole('img');
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findPipelineSource = () => wrapper.findComponent(GlDropdown);
- const findPopover = () => wrapper.findComponent(GlPopover);
- const findCiLintResults = () => wrapper.findComponent(CiLintResults);
- const findResultsCta = () => wrapper.findByTestId('resimulate-pipeline-button');
-
- beforeEach(() => {
- mockBlobContentData = jest.fn();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('while initial CI content is loading', () => {
- beforeEach(() => {
- createComponent({ isBlobLoading: true });
- });
-
- it('renders disabled CTA with tooltip', () => {
- expect(findCta().props('disabled')).toBe(true);
- expect(findDisabledCtaTooltip().exists()).toBe(true);
- });
- });
-
- describe('after initial CI content is loaded', () => {
- beforeEach(async () => {
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
- await createComponentWithApollo({ stubs: { GlPopover, ValidatePipelinePopover } });
- });
-
- it('renders disabled pipeline source dropdown', () => {
- expect(findPipelineSource().exists()).toBe(true);
- expect(findPipelineSource().attributes('text')).toBe(i18n.pipelineSourceDefault);
- expect(findPipelineSource().props('disabled')).toBe(true);
- });
-
- it('renders enabled CTA without tooltip', () => {
- expect(findCta().exists()).toBe(true);
- expect(findCta().props('disabled')).toBe(false);
- expect(findDisabledCtaTooltip().exists()).toBe(false);
- });
-
- it('popover is set to render when hovering over help icon', () => {
- expect(findPopover().props('target')).toBe(findHelpIcon().attributes('id'));
- expect(findPopover().props('triggers')).toBe('hover focus');
- });
- });
-
- describe('simulating the pipeline', () => {
- beforeEach(async () => {
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
- await createComponentWithApollo();
-
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockLintDataValid);
- });
-
- afterEach(() => {
- unmockTracking();
- });
-
- it('tracks the simulation event', () => {
- const {
- label,
- actions: { simulatePipeline },
- } = pipelineEditorTrackingOptions;
- findCta().vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, simulatePipeline, { label });
- });
-
- it('renders loading state while simulation is ongoing', async () => {
- findCta().vm.$emit('click');
- await nextTick();
-
- expect(findLoadingIcon().exists()).toBe(true);
- expect(findCancelBtn().exists()).toBe(true);
- expect(findCta().props('loading')).toBe(true);
- });
-
- it('calls mutation with the correct input', async () => {
- await findCta().vm.$emit('click');
-
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1);
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
- mutation: lintCIMutation,
- variables: {
- dry: true,
- content: mockCiYml,
- endpoint: mockCiLintPath,
- },
- });
- });
-
- describe('when results are successful', () => {
- beforeEach(async () => {
- jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockLintDataValid);
- await findCta().vm.$emit('click');
- });
-
- it('renders success alert', () => {
- expect(findAlert().exists()).toBe(true);
- expect(findAlert().attributes('variant')).toBe('success');
- expect(findAlert().attributes('title')).toBe(i18n.successAlertTitle);
- });
-
- it('does not render content change status or CTA for results page', () => {
- expect(findContentChangeStatus().exists()).toBe(false);
- expect(findResultsCta().exists()).toBe(false);
- });
-
- it('renders CI lint results with correct props', () => {
- expect(findCiLintResults().exists()).toBe(true);
- expect(findCiLintResults().props()).toMatchObject({
- dryRun: true,
- hideAlert: true,
- isValid: true,
- jobs: mockLintDataValid.data.lintCI.jobs,
- });
- });
- });
-
- describe('when results have errors', () => {
- beforeEach(async () => {
- jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockLintDataError);
- await findCta().vm.$emit('click');
- });
-
- it('renders error alert', () => {
- expect(findAlert().exists()).toBe(true);
- expect(findAlert().attributes('variant')).toBe('danger');
- expect(findAlert().attributes('title')).toBe(i18n.errorAlertTitle);
- });
-
- it('renders CI lint results with correct props', () => {
- expect(findCiLintResults().exists()).toBe(true);
- expect(findCiLintResults().props()).toMatchObject({
- dryRun: true,
- hideAlert: true,
- isValid: false,
- errors: mockLintDataError.data.lintCI.errors,
- warnings: mockLintDataError.data.lintCI.warnings,
- });
- });
- });
- });
-
- describe('when CI content has changed after a simulation', () => {
- beforeEach(async () => {
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
- await createComponentWithApollo();
-
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockLintDataValid);
- await findCta().vm.$emit('click');
- });
-
- afterEach(() => {
- unmockTracking();
- });
-
- it('tracks the second simulation event', async () => {
- const {
- label,
- actions: { resimulatePipeline },
- } = pipelineEditorTrackingOptions;
-
- await wrapper.setProps({ ciFileContent: 'new yaml content' });
- findResultsCta().vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, resimulatePipeline, { label });
- });
-
- it('renders content change status', async () => {
- await wrapper.setProps({ ciFileContent: 'new yaml content' });
-
- expect(findContentChangeStatus().exists()).toBe(true);
- expect(findResultsCta().exists()).toBe(true);
- });
-
- it('calls mutation with new content', async () => {
- await wrapper.setProps({ ciFileContent: 'new yaml content' });
- await findResultsCta().vm.$emit('click');
-
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(2);
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
- mutation: lintCIMutation,
- variables: {
- dry: true,
- content: 'new yaml content',
- endpoint: mockCiLintPath,
- },
- });
- });
- });
-
- describe('canceling a simulation', () => {
- beforeEach(async () => {
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
- await createComponentWithApollo();
- });
-
- it('returns to init state', async () => {
- // init state
- expect(findIllustration().exists()).toBe(true);
- expect(findCiLintResults().exists()).toBe(false);
-
- // mutations should have successful results
- jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockLintDataValid);
- findCta().vm.$emit('click');
- await nextTick();
-
- // cancel before simulation succeeds
- expect(findCancelBtn().exists()).toBe(true);
- await findCancelBtn().vm.$emit('click');
-
- // should still render init state
- expect(findIllustration().exists()).toBe(true);
- expect(findCiLintResults().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap b/spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap
deleted file mode 100644
index ee5a3cb288f..00000000000
--- a/spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap
+++ /dev/null
@@ -1,73 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`~/pipeline_editor/graphql/resolvers Mutation lintCI lint data is as expected 1`] = `
-Object {
- "__typename": "CiLintContent",
- "errors": Array [],
- "jobs": Array [
- Object {
- "__typename": "CiLintJob",
- "afterScript": Array [
- "echo 'after script 1",
- ],
- "allowFailure": false,
- "beforeScript": Array [
- "echo 'before script 1'",
- ],
- "environment": "prd",
- "except": Object {
- "refs": Array [
- "main@gitlab-org/gitlab",
- "/^release/.*$/@gitlab-org/gitlab",
- ],
- },
- "name": "job_1",
- "only": null,
- "script": Array [
- "echo 'script 1'",
- ],
- "stage": "test",
- "tags": Array [
- "tag 1",
- ],
- "when": "on_success",
- },
- Object {
- "__typename": "CiLintJob",
- "afterScript": Array [
- "echo 'after script 2",
- ],
- "allowFailure": true,
- "beforeScript": Array [
- "echo 'before script 2'",
- ],
- "environment": "stg",
- "except": Object {
- "refs": Array [
- "main@gitlab-org/gitlab",
- "/^release/.*$/@gitlab-org/gitlab",
- ],
- },
- "name": "job_2",
- "only": Object {
- "__typename": "CiLintJobOnlyPolicy",
- "refs": Array [
- "web",
- "chat",
- "pushes",
- ],
- },
- "script": Array [
- "echo 'script 2'",
- ],
- "stage": "test",
- "tags": Array [
- "tag 2",
- ],
- "when": "on_success",
- },
- ],
- "valid": true,
- "warnings": Array [],
-}
-`;
diff --git a/spec/frontend/pipeline_editor/graphql/resolvers_spec.js b/spec/frontend/pipeline_editor/graphql/resolvers_spec.js
deleted file mode 100644
index 76ae96c623a..00000000000
--- a/spec/frontend/pipeline_editor/graphql/resolvers_spec.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import axios from '~/lib/utils/axios_utils';
-import httpStatus from '~/lib/utils/http_status';
-import { resolvers } from '~/pipeline_editor/graphql/resolvers';
-import { mockLintResponse } from '../mock_data';
-
-jest.mock('~/api', () => {
- return {
- getRawFile: jest.fn(),
- };
-});
-
-describe('~/pipeline_editor/graphql/resolvers', () => {
- describe('Mutation', () => {
- describe('lintCI', () => {
- let mock;
- let result;
-
- const endpoint = '/ci/lint';
-
- beforeEach(async () => {
- mock = new MockAdapter(axios);
- mock.onPost(endpoint).reply(httpStatus.OK, mockLintResponse);
-
- result = await resolvers.Mutation.lintCI(null, {
- endpoint,
- content: 'content',
- dry_run: true,
- });
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- /* eslint-disable no-underscore-dangle */
- it('lint data has correct type names', async () => {
- expect(result.__typename).toBe('CiLintContent');
-
- expect(result.jobs[0].__typename).toBe('CiLintJob');
- expect(result.jobs[1].__typename).toBe('CiLintJob');
-
- expect(result.jobs[1].only.__typename).toBe('CiLintJobOnlyPolicy');
- });
- /* eslint-enable no-underscore-dangle */
-
- it('lint data is as expected', () => {
- expect(result).toMatchSnapshot();
- });
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/mock_data.js b/spec/frontend/pipeline_editor/mock_data.js
deleted file mode 100644
index 2ea580b7b53..00000000000
--- a/spec/frontend/pipeline_editor/mock_data.js
+++ /dev/null
@@ -1,541 +0,0 @@
-import { CI_CONFIG_STATUS_INVALID, CI_CONFIG_STATUS_VALID } from '~/pipeline_editor/constants';
-import { unwrapStagesWithNeeds } from '~/pipelines/components/unwrapping_utils';
-
-export const mockProjectNamespace = 'user1';
-export const mockProjectPath = 'project1';
-export const mockProjectFullPath = `${mockProjectNamespace}/${mockProjectPath}`;
-export const mockDefaultBranch = 'main';
-export const mockNewBranch = 'new-branch';
-export const mockNewMergeRequestPath = '/-/merge_requests/new';
-export const mockCiLintPath = '/-/ci/lint';
-export const mockCommitSha = 'aabbccdd';
-export const mockCommitNextSha = 'eeffgghh';
-export const mockIncludesHelpPagePath = '/-/includes/help';
-export const mockLintHelpPagePath = '/-/lint-help';
-export const mockLintUnavailableHelpPagePath = '/-/pipeline-editor/troubleshoot';
-export const mockSimulatePipelineHelpPagePath = '/-/simulate-pipeline-help';
-export const mockYmlHelpPagePath = '/-/yml-help';
-export const mockCommitMessage = 'My commit message';
-
-export const mockCiConfigPath = '.gitlab-ci.yml';
-export const mockCiYml = `
-stages:
- - test
- - build
-
-job_test_1:
- stage: test
- script:
- - echo "test 1"
-
-job_test_2:
- stage: test
- script:
- - echo "test 2"
-
-job_build:
- stage: build
- script:
- - echo "build"
- needs: ["job_test_2"]
-`;
-
-export const mockCiTemplateQueryResponse = {
- data: {
- project: {
- id: 'project-1',
- ciTemplate: {
- content: mockCiYml,
- },
- },
- },
-};
-
-export const mockBlobContentQueryResponse = {
- data: {
- project: {
- id: 'project-1',
- repository: { blobs: { nodes: [{ id: 'blob-1', rawBlob: mockCiYml }] } },
- },
- },
-};
-
-export const mockBlobContentQueryResponseNoCiFile = {
- data: {
- project: { id: 'project-1', repository: { blobs: { nodes: [] } } },
- },
-};
-
-export const mockBlobContentQueryResponseEmptyCiFile = {
- data: {
- project: { id: 'project-1', repository: { blobs: { nodes: [{ rawBlob: '' }] } } },
- },
-};
-
-const mockJobFields = {
- beforeScript: [],
- afterScript: [],
- environment: null,
- allowFailure: false,
- tags: [],
- when: 'on_success',
- only: { refs: ['branches', 'tags'], __typename: 'CiJobLimitType' },
- except: null,
- needs: { nodes: [], __typename: 'CiConfigNeedConnection' },
- __typename: 'CiConfigJob',
-};
-
-export const mockIncludesWithBlob = {
- location: 'test-include.yml',
- type: 'local',
- blob:
- 'http://gdk.test:3000/root/upstream/-/blob/dd54f00bb3645f8ddce7665d2ffb3864540399cb/test-include.yml',
- raw:
- 'http://gdk.test:3000/root/upstream/-/raw/dd54f00bb3645f8ddce7665d2ffb3864540399cb/test-include.yml',
- __typename: 'CiConfigInclude',
-};
-
-export const mockDefaultIncludes = {
- location: 'npm.gitlab-ci.yml',
- type: 'template',
- blob: null,
- raw:
- 'https://gitlab.com/gitlab-org/gitlab/-/raw/master/lib/gitlab/ci/templates/npm.gitlab-ci.yml',
- __typename: 'CiConfigInclude',
-};
-
-export const mockIncludes = [
- mockDefaultIncludes,
- mockIncludesWithBlob,
- {
- location: 'a_really_really_long_name_for_includes_file.yml',
- type: 'local',
- blob:
- 'http://gdk.test:3000/root/upstream/-/blob/dd54f00bb3645f8ddce7665d2ffb3864540399cb/a_really_really_long_name_for_includes_file.yml',
- raw:
- 'http://gdk.test:3000/root/upstream/-/raw/dd54f00bb3645f8ddce7665d2ffb3864540399cb/a_really_really_long_name_for_includes_file.yml',
- __typename: 'CiConfigInclude',
- },
-];
-
-// Mock result of the graphql query at:
-// app/assets/javascripts/pipeline_editor/graphql/queries/ci_config.graphql
-export const mockCiConfigQueryResponse = {
- data: {
- ciConfig: {
- errors: [],
- includes: mockIncludes,
- mergedYaml: mockCiYml,
- status: CI_CONFIG_STATUS_VALID,
- stages: {
- __typename: 'CiConfigStageConnection',
- nodes: [
- {
- name: 'test',
- groups: {
- nodes: [
- {
- id: 'group-1',
- name: 'job_test_1',
- size: 1,
- jobs: {
- nodes: [
- {
- name: 'job_test_1',
- script: ['echo "test 1"'],
- ...mockJobFields,
- },
- ],
- __typename: 'CiConfigJobConnection',
- },
- __typename: 'CiConfigGroup',
- },
- {
- id: 'group-2',
- name: 'job_test_2',
- size: 1,
- jobs: {
- nodes: [
- {
- name: 'job_test_2',
- script: ['echo "test 2"'],
- ...mockJobFields,
- },
- ],
- __typename: 'CiConfigJobConnection',
- },
- __typename: 'CiConfigGroup',
- },
- ],
- __typename: 'CiConfigGroupConnection',
- },
- __typename: 'CiConfigStage',
- },
- {
- name: 'build',
- groups: {
- nodes: [
- {
- name: 'job_build',
- size: 1,
- jobs: {
- nodes: [
- {
- name: 'job_build',
- script: ['echo "build"'],
- ...mockJobFields,
- },
- ],
- __typename: 'CiConfigJobConnection',
- },
- __typename: 'CiConfigGroup',
- },
- ],
- __typename: 'CiConfigGroupConnection',
- },
- __typename: 'CiConfigStage',
- },
- ],
- },
- __typename: 'CiConfig',
- },
- },
-};
-
-export const mergeUnwrappedCiConfig = (mergedConfig) => {
- const { ciConfig } = mockCiConfigQueryResponse.data;
- return {
- ...ciConfig,
- stages: unwrapStagesWithNeeds(ciConfig.stages.nodes),
- ...mergedConfig,
- };
-};
-
-export const mockCommitShaResults = {
- data: {
- project: {
- id: '1',
- repository: {
- tree: {
- lastCommit: {
- id: 'commit-1',
- sha: mockCommitSha,
- },
- },
- },
- },
- },
-};
-
-export const mockNewCommitShaResults = {
- data: {
- project: {
- id: '1',
- repository: {
- tree: {
- lastCommit: {
- id: 'commit-1',
- sha: 'eeff1122',
- },
- },
- },
- },
- },
-};
-
-export const mockEmptyCommitShaResults = {
- data: {
- project: {
- id: '1',
- repository: {
- tree: {
- lastCommit: {
- id: 'commit-1',
- sha: '',
- },
- },
- },
- },
- },
-};
-
-export const mockProjectBranches = {
- data: {
- project: {
- id: '1',
- repository: {
- branchNames: [
- 'main',
- 'develop',
- 'production',
- 'test',
- 'better-feature',
- 'feature-abc',
- 'update-ci',
- 'mock-feature',
- 'test-merge-request',
- 'staging',
- ],
- },
- },
- },
-};
-
-export const mockTotalBranchResults =
- mockProjectBranches.data.project.repository.branchNames.length;
-
-export const mockSearchBranches = {
- data: {
- project: {
- id: '1',
- repository: {
- branchNames: ['test', 'better-feature', 'update-ci', 'test-merge-request'],
- },
- },
- },
-};
-
-export const mockTotalSearchResults = mockSearchBranches.data.project.repository.branchNames.length;
-
-export const mockEmptySearchBranches = {
- data: {
- project: {
- id: '1',
- repository: {
- branchNames: [],
- },
- },
- },
-};
-
-export const mockBranchPaginationLimit = 10;
-export const mockTotalBranches = 20; // must be greater than mockBranchPaginationLimit to test pagination
-
-export const mockProjectPipeline = ({ hasStages = true } = {}) => {
- const stages = hasStages
- ? {
- edges: [
- {
- node: {
- id: 'gid://gitlab/Ci::Stage/605',
- name: 'prepare',
- status: 'success',
- detailedStatus: {
- detailsPath: '/root/sample-ci-project/-/pipelines/268#prepare',
- group: 'success',
- hasDetails: true,
- icon: 'status_success',
- id: 'success-605-605',
- label: 'passed',
- text: 'passed',
- tooltip: 'passed',
- },
- },
- },
- ],
- }
- : null;
-
- return {
- id: '1',
- pipeline: {
- id: 'gid://gitlab/Ci::Pipeline/118',
- iid: '28',
- shortSha: mockCommitSha,
- status: 'SUCCESS',
- commit: {
- id: 'commit-1',
- title: 'Update .gitlabe-ci.yml',
- webPath: '/-/commit/aabbccdd',
- },
- detailedStatus: {
- id: 'status-1',
- detailsPath: '/root/sample-ci-project/-/pipelines/118',
- group: 'success',
- icon: 'status_success',
- text: 'passed',
- },
- stages,
- },
- };
-};
-
-export const mockLinkedPipelines = ({ hasDownstream = true, hasUpstream = true } = {}) => {
- let upstream = null;
- let downstream = {
- nodes: [],
- __typename: 'PipelineConnection',
- };
-
- if (hasDownstream) {
- downstream = {
- nodes: [
- {
- id: 'gid://gitlab/Ci::Pipeline/612',
- path: '/root/job-log-sections/-/pipelines/612',
- project: { name: 'job-log-sections', __typename: 'Project' },
- detailedStatus: {
- group: 'success',
- icon: 'status_success',
- label: 'passed',
- __typename: 'DetailedStatus',
- },
- __typename: 'Pipeline',
- },
- ],
- __typename: 'PipelineConnection',
- };
- }
-
- if (hasUpstream) {
- upstream = {
- id: 'gid://gitlab/Ci::Pipeline/610',
- path: '/root/trigger-downstream/-/pipelines/610',
- project: { name: 'trigger-downstream', __typename: 'Project' },
- detailedStatus: {
- group: 'success',
- icon: 'status_success',
- label: 'passed',
- __typename: 'DetailedStatus',
- },
- __typename: 'Pipeline',
- };
- }
-
- return {
- data: {
- project: {
- pipeline: {
- path: '/root/ci-project/-/pipelines/790',
- downstream,
- upstream,
- },
- __typename: 'Project',
- },
- },
- };
-};
-
-export const mockLintResponse = {
- valid: true,
- mergedYaml: mockCiYml,
- status: CI_CONFIG_STATUS_VALID,
- errors: [],
- warnings: [],
- jobs: [
- {
- name: 'job_1',
- stage: 'test',
- before_script: ["echo 'before script 1'"],
- script: ["echo 'script 1'"],
- after_script: ["echo 'after script 1"],
- tag_list: ['tag 1'],
- environment: 'prd',
- when: 'on_success',
- allow_failure: false,
- only: null,
- except: { refs: ['main@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
- },
- {
- name: 'job_2',
- stage: 'test',
- before_script: ["echo 'before script 2'"],
- script: ["echo 'script 2'"],
- after_script: ["echo 'after script 2"],
- tag_list: ['tag 2'],
- environment: 'stg',
- when: 'on_success',
- allow_failure: true,
- only: { refs: ['web', 'chat', 'pushes'] },
- except: { refs: ['main@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
- },
- ],
-};
-
-export const mockLintResponseWithoutMerged = {
- valid: false,
- status: CI_CONFIG_STATUS_INVALID,
- errors: ['error'],
- warnings: [],
- jobs: [],
-};
-
-export const mockJobs = [
- {
- name: 'job_1',
- stage: 'build',
- beforeScript: [],
- script: ["echo 'Building'"],
- afterScript: [],
- tagList: [],
- environment: null,
- when: 'on_success',
- allowFailure: true,
- only: { refs: ['web', 'chat', 'pushes'] },
- except: null,
- },
- {
- name: 'multi_project_job',
- stage: 'test',
- beforeScript: [],
- script: [],
- afterScript: [],
- tagList: [],
- environment: null,
- when: 'on_success',
- allowFailure: false,
- only: { refs: ['branches', 'tags'] },
- except: null,
- },
- {
- name: 'job_2',
- stage: 'test',
- beforeScript: ["echo 'before script'"],
- script: ["echo 'script'"],
- afterScript: ["echo 'after script"],
- tagList: [],
- environment: null,
- when: 'on_success',
- allowFailure: false,
- only: { refs: ['branches@gitlab-org/gitlab'] },
- except: { refs: ['main@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
- },
-];
-
-export const mockErrors = [
- '"job_1 job: chosen stage does not exist; available stages are .pre, build, test, deploy, .post"',
-];
-
-export const mockWarnings = [
- '"jobs:multi_project_job may allow multiple pipelines to run for a single action due to `rules:when` clause with no `workflow:rules` - read more: https://docs.gitlab.com/ee/ci/troubleshooting.html#pipeline-warnings"',
-];
-
-export const mockCommitCreateResponse = {
- data: {
- commitCreate: {
- __typename: 'CommitCreatePayload',
- errors: [],
- commit: {
- __typename: 'Commit',
- id: 'commit-1',
- sha: mockCommitNextSha,
- },
- commitPipelinePath: '',
- },
- },
-};
-
-export const mockCommitCreateResponseNewEtag = {
- data: {
- commitCreate: {
- __typename: 'CommitCreatePayload',
- errors: [],
- commit: {
- __typename: 'Commit',
- id: 'commit-2',
- sha: mockCommitNextSha,
- },
- commitPipelinePath: '/api/graphql:pipelines/sha/550ceace1acd373c84d02bd539cb9d4614f786db',
- },
- },
-};
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
deleted file mode 100644
index 9fe1536d3f5..00000000000
--- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
+++ /dev/null
@@ -1,589 +0,0 @@
-import { GlAlert, GlButton, GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import setWindowLocation from 'helpers/set_window_location_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-
-import { objectToQuery, redirectTo } from '~/lib/utils/url_utility';
-import { resolvers } from '~/pipeline_editor/graphql/resolvers';
-import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue';
-import PipelineEditorEmptyState from '~/pipeline_editor/components/ui/pipeline_editor_empty_state.vue';
-import PipelineEditorMessages from '~/pipeline_editor/components/ui/pipeline_editor_messages.vue';
-import PipelineEditorHeader from '~/pipeline_editor/components/header/pipeline_editor_header.vue';
-import ValidationSegment, {
- i18n as validationSegmenti18n,
-} from '~/pipeline_editor/components/header/validation_segment.vue';
-import {
- COMMIT_SUCCESS,
- COMMIT_SUCCESS_WITH_REDIRECT,
- COMMIT_FAILURE,
- EDITOR_APP_STATUS_LOADING,
-} from '~/pipeline_editor/constants';
-import getBlobContent from '~/pipeline_editor/graphql/queries/blob_content.query.graphql';
-import getCiConfigData from '~/pipeline_editor/graphql/queries/ci_config.query.graphql';
-import getTemplate from '~/pipeline_editor/graphql/queries/get_starter_template.query.graphql';
-import getLatestCommitShaQuery from '~/pipeline_editor/graphql/queries/latest_commit_sha.query.graphql';
-import getPipelineQuery from '~/pipeline_editor/graphql/queries/pipeline.query.graphql';
-import getCurrentBranch from '~/pipeline_editor/graphql/queries/client/current_branch.query.graphql';
-import getAppStatus from '~/pipeline_editor/graphql/queries/client/app_status.query.graphql';
-
-import PipelineEditorApp from '~/pipeline_editor/pipeline_editor_app.vue';
-import PipelineEditorHome from '~/pipeline_editor/pipeline_editor_home.vue';
-
-import {
- mockCiConfigPath,
- mockCiConfigQueryResponse,
- mockBlobContentQueryResponse,
- mockBlobContentQueryResponseNoCiFile,
- mockCiYml,
- mockCiTemplateQueryResponse,
- mockCommitSha,
- mockCommitShaResults,
- mockDefaultBranch,
- mockEmptyCommitShaResults,
- mockNewCommitShaResults,
- mockNewMergeRequestPath,
- mockProjectFullPath,
-} from './mock_data';
-
-jest.mock('~/lib/utils/url_utility', () => ({
- ...jest.requireActual('~/lib/utils/url_utility'),
- redirectTo: jest.fn(),
-}));
-
-const localVue = createLocalVue();
-localVue.use(VueApollo);
-
-const defaultProvide = {
- ciConfigPath: mockCiConfigPath,
- defaultBranch: mockDefaultBranch,
- newMergeRequestPath: mockNewMergeRequestPath,
- projectFullPath: mockProjectFullPath,
- usesExternalConfig: false,
-};
-
-describe('Pipeline editor app component', () => {
- let wrapper;
-
- let mockApollo;
- let mockBlobContentData;
- let mockCiConfigData;
- let mockGetTemplate;
- let mockLatestCommitShaQuery;
- let mockPipelineQuery;
-
- const createComponent = ({
- blobLoading = false,
- options = {},
- provide = {},
- stubs = {},
- } = {}) => {
- wrapper = shallowMount(PipelineEditorApp, {
- provide: { ...defaultProvide, ...provide },
- stubs,
- mocks: {
- $apollo: {
- queries: {
- initialCiFileContent: {
- loading: blobLoading,
- },
- },
- },
- },
- ...options,
- });
- };
-
- const createComponentWithApollo = async ({
- provide = {},
- stubs = {},
- withUndefinedBranch = false,
- } = {}) => {
- const handlers = [
- [getBlobContent, mockBlobContentData],
- [getCiConfigData, mockCiConfigData],
- [getTemplate, mockGetTemplate],
- [getLatestCommitShaQuery, mockLatestCommitShaQuery],
- [getPipelineQuery, mockPipelineQuery],
- ];
-
- mockApollo = createMockApollo(handlers, resolvers);
-
- if (!withUndefinedBranch) {
- mockApollo.clients.defaultClient.cache.writeQuery({
- query: getCurrentBranch,
- data: {
- workBranches: {
- __typename: 'BranchList',
- current: {
- __typename: 'WorkBranch',
- name: mockDefaultBranch,
- },
- },
- },
- });
- }
-
- mockApollo.clients.defaultClient.cache.writeQuery({
- query: getAppStatus,
- data: {
- app: {
- __typename: 'AppData',
- status: EDITOR_APP_STATUS_LOADING,
- },
- },
- });
-
- const options = {
- localVue,
- mocks: {},
- apolloProvider: mockApollo,
- };
-
- createComponent({ provide, stubs, options });
-
- return waitForPromises();
- };
-
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findAlert = () => wrapper.findComponent(GlAlert);
- const findEditorHome = () => wrapper.findComponent(PipelineEditorHome);
- const findEmptyState = () => wrapper.findComponent(PipelineEditorEmptyState);
- const findEmptyStateButton = () => findEmptyState().findComponent(GlButton);
- const findValidationSegment = () => wrapper.findComponent(ValidationSegment);
-
- beforeEach(() => {
- mockBlobContentData = jest.fn();
- mockCiConfigData = jest.fn();
- mockGetTemplate = jest.fn();
- mockLatestCommitShaQuery = jest.fn();
- mockPipelineQuery = jest.fn();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('loading state', () => {
- it('displays a loading icon if the blob query is loading', () => {
- createComponent({ blobLoading: true });
-
- expect(findLoadingIcon().exists()).toBe(true);
- expect(findEditorHome().exists()).toBe(false);
- });
- });
-
- describe('skipping queries', () => {
- describe('when branchName is undefined', () => {
- beforeEach(async () => {
- await createComponentWithApollo({ withUndefinedBranch: true });
- });
-
- it('does not calls getBlobContent', () => {
- expect(mockBlobContentData).not.toHaveBeenCalled();
- });
- });
-
- describe('when branchName is defined', () => {
- beforeEach(async () => {
- await createComponentWithApollo();
- });
-
- it('calls getBlobContent', () => {
- expect(mockBlobContentData).toHaveBeenCalled();
- });
- });
-
- describe('when commit sha is undefined', () => {
- beforeEach(async () => {
- mockLatestCommitShaQuery.mockResolvedValue(undefined);
- await createComponentWithApollo();
- });
-
- it('calls getBlobContent', () => {
- expect(mockBlobContentData).toHaveBeenCalled();
- });
-
- it('does not call ciConfigData', () => {
- expect(mockCiConfigData).not.toHaveBeenCalled();
- });
- });
-
- describe('when commit sha is defined', () => {
- beforeEach(async () => {
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
- mockLatestCommitShaQuery.mockResolvedValue(mockCommitShaResults);
- await createComponentWithApollo();
- });
-
- it('calls ciConfigData', () => {
- expect(mockCiConfigData).toHaveBeenCalled();
- });
- });
- });
-
- describe('when queries are called', () => {
- beforeEach(() => {
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
- mockCiConfigData.mockResolvedValue(mockCiConfigQueryResponse);
- mockLatestCommitShaQuery.mockResolvedValue(mockCommitShaResults);
- });
-
- describe('when project uses an external CI config file', () => {
- beforeEach(async () => {
- await createComponentWithApollo({
- provide: {
- usesExternalConfig: true,
- },
- });
- });
-
- it('shows an empty state and does not show editor home component', () => {
- expect(findEmptyState().exists()).toBe(true);
- expect(findAlert().exists()).toBe(false);
- expect(findEditorHome().exists()).toBe(false);
- });
- });
-
- describe('when file exists', () => {
- beforeEach(async () => {
- await createComponentWithApollo();
-
- jest
- .spyOn(wrapper.vm.$apollo.queries.commitSha, 'startPolling')
- .mockImplementation(jest.fn());
- });
-
- it('shows pipeline editor home component', () => {
- expect(findEditorHome().exists()).toBe(true);
- });
-
- it('no error is shown when data is set', () => {
- expect(findAlert().exists()).toBe(false);
- });
-
- it('ci config query is called with correct variables', async () => {
- expect(mockCiConfigData).toHaveBeenCalledWith({
- content: mockCiYml,
- projectPath: mockProjectFullPath,
- sha: mockCommitSha,
- });
- });
-
- it('does not poll for the commit sha', () => {
- expect(wrapper.vm.$apollo.queries.commitSha.startPolling).toHaveBeenCalledTimes(0);
- });
- });
-
- describe('when no CI config file exists', () => {
- beforeEach(async () => {
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponseNoCiFile);
- await createComponentWithApollo({
- stubs: {
- PipelineEditorEmptyState,
- },
- });
-
- jest
- .spyOn(wrapper.vm.$apollo.queries.commitSha, 'startPolling')
- .mockImplementation(jest.fn());
- });
-
- it('shows an empty state and does not show editor home component', async () => {
- expect(findEmptyState().exists()).toBe(true);
- expect(findAlert().exists()).toBe(false);
- expect(findEditorHome().exists()).toBe(false);
- });
-
- it('does not poll for the commit sha', () => {
- expect(wrapper.vm.$apollo.queries.commitSha.startPolling).toHaveBeenCalledTimes(0);
- });
-
- describe('because of a fetching error', () => {
- it('shows a unkown error message', async () => {
- const loadUnknownFailureText = 'The CI configuration was not loaded, please try again.';
-
- mockBlobContentData.mockRejectedValueOnce();
- await createComponentWithApollo({
- stubs: {
- PipelineEditorMessages,
- },
- });
-
- expect(findEmptyState().exists()).toBe(false);
-
- expect(findAlert().text()).toBe(loadUnknownFailureText);
- expect(findEditorHome().exists()).toBe(true);
- });
- });
- });
-
- describe('with no CI config setup', () => {
- it('user can click on CTA button to get started', async () => {
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponseNoCiFile);
- mockLatestCommitShaQuery.mockResolvedValue(mockEmptyCommitShaResults);
-
- await createComponentWithApollo({
- stubs: {
- PipelineEditorHome,
- PipelineEditorEmptyState,
- },
- });
-
- expect(findEmptyState().exists()).toBe(true);
- expect(findEditorHome().exists()).toBe(false);
-
- await findEmptyStateButton().vm.$emit('click');
-
- expect(findEmptyState().exists()).toBe(false);
- expect(findEditorHome().exists()).toBe(true);
- });
- });
-
- describe('when the lint query returns a 500 error', () => {
- beforeEach(async () => {
- mockCiConfigData.mockRejectedValueOnce(new Error(500));
- await createComponentWithApollo({
- stubs: { PipelineEditorHome, PipelineEditorHeader, ValidationSegment },
- });
- });
-
- it('shows that the lint service is down', () => {
- expect(findValidationSegment().text()).toContain(
- validationSegmenti18n.unavailableValidation,
- );
- });
-
- it('does not report an error or scroll to the top', () => {
- expect(findAlert().exists()).toBe(false);
- expect(window.scrollTo).not.toHaveBeenCalled();
- });
- });
-
- describe('when the user commits', () => {
- const updateFailureMessage = 'The GitLab CI configuration could not be updated.';
- const updateSuccessMessage = 'Your changes have been successfully committed.';
-
- describe('and the commit mutation succeeds', () => {
- beforeEach(async () => {
- window.scrollTo = jest.fn();
- await createComponentWithApollo({ stubs: { PipelineEditorMessages } });
-
- findEditorHome().vm.$emit('commit', { type: COMMIT_SUCCESS });
- });
-
- it('shows a confirmation message', () => {
- expect(findAlert().text()).toBe(updateSuccessMessage);
- });
-
- it('scrolls to the top of the page to bring attention to the confirmation message', () => {
- expect(window.scrollTo).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' });
- });
-
- it('polls for commit sha while pipeline data is not yet available for current branch', async () => {
- jest
- .spyOn(wrapper.vm.$apollo.queries.commitSha, 'startPolling')
- .mockImplementation(jest.fn());
-
- // simulate a commit to the current branch
- findEditorHome().vm.$emit('updateCommitSha');
- await waitForPromises();
-
- expect(wrapper.vm.$apollo.queries.commitSha.startPolling).toHaveBeenCalledTimes(1);
- });
-
- it('stops polling for commit sha when pipeline data is available for newly committed branch', async () => {
- jest
- .spyOn(wrapper.vm.$apollo.queries.commitSha, 'stopPolling')
- .mockImplementation(jest.fn());
-
- mockLatestCommitShaQuery.mockResolvedValue(mockCommitShaResults);
- await wrapper.vm.$apollo.queries.commitSha.refetch();
-
- expect(wrapper.vm.$apollo.queries.commitSha.stopPolling).toHaveBeenCalledTimes(1);
- });
-
- it('stops polling for commit sha when pipeline data is available for current branch', async () => {
- jest
- .spyOn(wrapper.vm.$apollo.queries.commitSha, 'stopPolling')
- .mockImplementation(jest.fn());
-
- mockLatestCommitShaQuery.mockResolvedValue(mockNewCommitShaResults);
- findEditorHome().vm.$emit('updateCommitSha');
- await waitForPromises();
-
- expect(wrapper.vm.$apollo.queries.commitSha.stopPolling).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('when the commit succeeds with a redirect', () => {
- const newBranch = 'new-branch';
-
- beforeEach(async () => {
- await createComponentWithApollo({ stubs: { PipelineEditorMessages } });
-
- findEditorHome().vm.$emit('commit', {
- type: COMMIT_SUCCESS_WITH_REDIRECT,
- params: { sourceBranch: newBranch, targetBranch: mockDefaultBranch },
- });
- });
-
- it('redirects to the merge request page with source and target branches', () => {
- const branchesQuery = objectToQuery({
- 'merge_request[source_branch]': newBranch,
- 'merge_request[target_branch]': mockDefaultBranch,
- });
-
- expect(redirectTo).toHaveBeenCalledWith(`${mockNewMergeRequestPath}?${branchesQuery}`);
- });
- });
-
- describe('and the commit mutation fails', () => {
- const commitFailedReasons = ['Commit failed'];
-
- beforeEach(async () => {
- window.scrollTo = jest.fn();
- await createComponentWithApollo({ stubs: { PipelineEditorMessages } });
-
- findEditorHome().vm.$emit('showError', {
- type: COMMIT_FAILURE,
- reasons: commitFailedReasons,
- });
- });
-
- it('shows an error message', () => {
- expect(findAlert().text()).toMatchInterpolatedText(
- `${updateFailureMessage} ${commitFailedReasons[0]}`,
- );
- });
-
- it('scrolls to the top of the page to bring attention to the error message', () => {
- expect(window.scrollTo).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' });
- });
- });
-
- describe('when an unknown error occurs', () => {
- const unknownReasons = ['Commit failed'];
-
- beforeEach(async () => {
- window.scrollTo = jest.fn();
- await createComponentWithApollo({ stubs: { PipelineEditorMessages } });
-
- findEditorHome().vm.$emit('showError', {
- type: COMMIT_FAILURE,
- reasons: unknownReasons,
- });
- });
-
- it('shows an error message', () => {
- expect(findAlert().text()).toMatchInterpolatedText(
- `${updateFailureMessage} ${unknownReasons[0]}`,
- );
- });
-
- it('scrolls to the top of the page to bring attention to the error message', () => {
- expect(window.scrollTo).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' });
- });
- });
- });
- });
-
- describe('when refetching content', () => {
- beforeEach(() => {
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
- mockCiConfigData.mockResolvedValue(mockCiConfigQueryResponse);
- mockLatestCommitShaQuery.mockResolvedValue(mockCommitShaResults);
- });
-
- it('refetches blob content', async () => {
- await createComponentWithApollo();
- jest
- .spyOn(wrapper.vm.$apollo.queries.initialCiFileContent, 'refetch')
- .mockImplementation(jest.fn());
-
- expect(wrapper.vm.$apollo.queries.initialCiFileContent.refetch).toHaveBeenCalledTimes(0);
-
- await wrapper.vm.refetchContent();
-
- expect(wrapper.vm.$apollo.queries.initialCiFileContent.refetch).toHaveBeenCalledTimes(1);
- });
-
- it('hides start screen when refetch fetches CI file', async () => {
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponseNoCiFile);
- await createComponentWithApollo();
-
- expect(findEmptyState().exists()).toBe(true);
- expect(findEditorHome().exists()).toBe(false);
-
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
- await wrapper.vm.$apollo.queries.initialCiFileContent.refetch();
-
- expect(findEmptyState().exists()).toBe(false);
- expect(findEditorHome().exists()).toBe(true);
- });
- });
-
- describe('when a template parameter is present in the URL', () => {
- const originalLocation = window.location.href;
-
- beforeEach(() => {
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
- mockCiConfigData.mockResolvedValue(mockCiConfigQueryResponse);
- mockLatestCommitShaQuery.mockResolvedValue(mockCommitShaResults);
- mockGetTemplate.mockResolvedValue(mockCiTemplateQueryResponse);
- setWindowLocation('?template=Android');
- });
-
- afterEach(() => {
- setWindowLocation(originalLocation);
- });
-
- it('renders the given template', async () => {
- await createComponentWithApollo({
- stubs: { PipelineEditorHome, PipelineEditorTabs },
- });
-
- expect(mockGetTemplate).toHaveBeenCalledWith({
- projectPath: mockProjectFullPath,
- templateName: 'Android',
- });
-
- expect(findEmptyState().exists()).toBe(false);
- expect(findEditorHome().exists()).toBe(true);
- });
- });
-
- describe('when add_new_config_file query param is present', () => {
- const originalLocation = window.location.href;
-
- beforeEach(() => {
- setWindowLocation('?add_new_config_file=true');
-
- mockCiConfigData.mockResolvedValue(mockCiConfigQueryResponse);
- });
-
- afterEach(() => {
- setWindowLocation(originalLocation);
- });
-
- describe('when CI config file does not exist', () => {
- beforeEach(async () => {
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponseNoCiFile);
- mockLatestCommitShaQuery.mockResolvedValue(mockEmptyCommitShaResults);
- mockGetTemplate.mockResolvedValue(mockCiTemplateQueryResponse);
-
- await createComponentWithApollo();
-
- jest
- .spyOn(wrapper.vm.$apollo.queries.commitSha, 'startPolling')
- .mockImplementation(jest.fn());
- });
-
- it('skips empty state and shows editor home component', () => {
- expect(findEmptyState().exists()).toBe(false);
- expect(findEditorHome().exists()).toBe(true);
- });
- });
- });
-});
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js
deleted file mode 100644
index 2b06660c4b3..00000000000
--- a/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js
+++ /dev/null
@@ -1,330 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import { GlButton, GlDrawer, GlModal } from '@gitlab/ui';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import setWindowLocation from 'helpers/set_window_location_helper';
-import CiEditorHeader from '~/pipeline_editor/components/editor/ci_editor_header.vue';
-import CommitSection from '~/pipeline_editor/components/commit/commit_section.vue';
-import PipelineEditorDrawer from '~/pipeline_editor/components/drawer/pipeline_editor_drawer.vue';
-import PipelineEditorFileNav from '~/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue';
-import PipelineEditorFileTree from '~/pipeline_editor/components/file_tree/container.vue';
-import BranchSwitcher from '~/pipeline_editor/components/file_nav/branch_switcher.vue';
-import PipelineEditorHeader from '~/pipeline_editor/components/header/pipeline_editor_header.vue';
-import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue';
-import {
- CREATE_TAB,
- FILE_TREE_DISPLAY_KEY,
- VALIDATE_TAB,
- MERGED_TAB,
- TABS_INDEX,
- VISUALIZE_TAB,
-} from '~/pipeline_editor/constants';
-import PipelineEditorHome from '~/pipeline_editor/pipeline_editor_home.vue';
-
-import { mockLintResponse, mockCiYml } from './mock_data';
-
-jest.mock('~/lib/utils/common_utils');
-
-describe('Pipeline editor home wrapper', () => {
- let wrapper;
-
- const createComponent = ({ props = {}, glFeatures = {}, data = {}, stubs = {} } = {}) => {
- wrapper = extendedWrapper(
- shallowMount(PipelineEditorHome, {
- data: () => data,
- propsData: {
- ciConfigData: mockLintResponse,
- ciFileContent: mockCiYml,
- isCiConfigDataLoading: false,
- isNewCiConfigFile: false,
- ...props,
- },
- provide: {
- projectFullPath: '',
- totalBranches: 19,
- glFeatures: {
- ...glFeatures,
- },
- },
- stubs,
- }),
- );
- };
-
- const findBranchSwitcher = () => wrapper.findComponent(BranchSwitcher);
- const findCommitSection = () => wrapper.findComponent(CommitSection);
- const findFileNav = () => wrapper.findComponent(PipelineEditorFileNav);
- const findModal = () => wrapper.findComponent(GlModal);
- const findPipelineEditorDrawer = () => wrapper.findComponent(PipelineEditorDrawer);
- const findPipelineEditorFileTree = () => wrapper.findComponent(PipelineEditorFileTree);
- const findPipelineEditorHeader = () => wrapper.findComponent(PipelineEditorHeader);
- const findPipelineEditorTabs = () => wrapper.findComponent(PipelineEditorTabs);
- const findFileTreeBtn = () => wrapper.findByTestId('file-tree-toggle');
- const findHelpBtn = () => wrapper.findByTestId('drawer-toggle');
-
- afterEach(() => {
- localStorage.clear();
- wrapper.destroy();
- });
-
- describe('renders', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('shows the file nav', () => {
- expect(findFileNav().exists()).toBe(true);
- });
-
- it('shows the pipeline editor header', () => {
- expect(findPipelineEditorHeader().exists()).toBe(true);
- });
-
- it('shows the pipeline editor tabs', () => {
- expect(findPipelineEditorTabs().exists()).toBe(true);
- });
-
- it('shows the commit section by default', () => {
- expect(findCommitSection().exists()).toBe(true);
- });
- });
-
- describe('modal when switching branch', () => {
- describe('when `showSwitchBranchModal` value is false', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('is not visible', () => {
- expect(findModal().exists()).toBe(false);
- });
- });
- describe('when `showSwitchBranchModal` value is true', () => {
- beforeEach(() => {
- createComponent({
- data: { showSwitchBranchModal: true },
- stubs: { PipelineEditorFileNav },
- });
- });
-
- it('is visible', () => {
- expect(findModal().exists()).toBe(true);
- });
-
- it('pass down `shouldLoadNewBranch` to the branch switcher when primary is selected', async () => {
- expect(findBranchSwitcher().props('shouldLoadNewBranch')).toBe(false);
-
- await findModal().vm.$emit('primary');
-
- expect(findBranchSwitcher().props('shouldLoadNewBranch')).toBe(true);
- });
-
- it('closes the modal when secondary action is selected', async () => {
- expect(findModal().exists()).toBe(true);
-
- await findModal().vm.$emit('secondary');
-
- expect(findModal().exists()).toBe(false);
- });
- });
- });
-
- describe('commit form toggle', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it.each`
- tab | shouldShow
- ${MERGED_TAB} | ${false}
- ${VISUALIZE_TAB} | ${false}
- ${VALIDATE_TAB} | ${false}
- ${CREATE_TAB} | ${true}
- `(
- 'when the active tab is $tab the commit form is shown: $shouldShow',
- async ({ tab, shouldShow }) => {
- expect(findCommitSection().exists()).toBe(true);
-
- findPipelineEditorTabs().vm.$emit('set-current-tab', tab);
-
- await nextTick();
-
- expect(findCommitSection().isVisible()).toBe(shouldShow);
- },
- );
-
- it('shows the commit form again when coming back to the create tab', async () => {
- expect(findCommitSection().isVisible()).toBe(true);
-
- findPipelineEditorTabs().vm.$emit('set-current-tab', MERGED_TAB);
- await nextTick();
- expect(findCommitSection().isVisible()).toBe(false);
-
- findPipelineEditorTabs().vm.$emit('set-current-tab', CREATE_TAB);
- await nextTick();
- expect(findCommitSection().isVisible()).toBe(true);
- });
-
- describe('rendering with tab params', () => {
- it.each`
- tab | shouldShow
- ${MERGED_TAB} | ${false}
- ${VISUALIZE_TAB} | ${false}
- ${VALIDATE_TAB} | ${false}
- ${CREATE_TAB} | ${true}
- `(
- 'when the tab query param is $tab the commit form is shown: $shouldShow',
- async ({ tab, shouldShow }) => {
- setWindowLocation(`https://gitlab.test/ci/editor/?tab=${TABS_INDEX[tab]}`);
- await createComponent({ stubs: { PipelineEditorTabs } });
-
- expect(findCommitSection().isVisible()).toBe(shouldShow);
- },
- );
- });
- });
-
- describe('WalkthroughPopover events', () => {
- beforeEach(() => {
- createComponent();
- });
-
- describe('when "walkthrough-popover-cta-clicked" is emitted from pipeline editor tabs', () => {
- it('passes down `scrollToCommitForm=true` to commit section', async () => {
- expect(findCommitSection().props('scrollToCommitForm')).toBe(false);
- await findPipelineEditorTabs().vm.$emit('walkthrough-popover-cta-clicked');
- expect(findCommitSection().props('scrollToCommitForm')).toBe(true);
- });
- });
-
- describe('when "scrolled-to-commit-form" is emitted from commit section', () => {
- it('passes down `scrollToCommitForm=false` to commit section', async () => {
- await findPipelineEditorTabs().vm.$emit('walkthrough-popover-cta-clicked');
- expect(findCommitSection().props('scrollToCommitForm')).toBe(true);
- await findCommitSection().vm.$emit('scrolled-to-commit-form');
- expect(findCommitSection().props('scrollToCommitForm')).toBe(false);
- });
- });
- });
-
- describe('help drawer', () => {
- const clickHelpBtn = async () => {
- findHelpBtn().vm.$emit('click');
- await nextTick();
- };
-
- it('hides the drawer by default', () => {
- createComponent();
-
- expect(findPipelineEditorDrawer().props('isVisible')).toBe(false);
- });
-
- it('toggles the drawer on button click', async () => {
- createComponent({
- stubs: {
- CiEditorHeader,
- GlButton,
- GlDrawer,
- PipelineEditorTabs,
- PipelineEditorDrawer,
- },
- });
-
- await clickHelpBtn();
-
- expect(findPipelineEditorDrawer().props('isVisible')).toBe(true);
-
- await clickHelpBtn();
-
- expect(findPipelineEditorDrawer().props('isVisible')).toBe(false);
- });
-
- it("closes the drawer through the drawer's close button", async () => {
- createComponent({
- stubs: {
- CiEditorHeader,
- GlButton,
- GlDrawer,
- PipelineEditorTabs,
- PipelineEditorDrawer,
- },
- });
-
- await clickHelpBtn();
-
- expect(findPipelineEditorDrawer().props('isVisible')).toBe(true);
-
- findPipelineEditorDrawer().findComponent(GlDrawer).vm.$emit('close');
- await nextTick();
-
- expect(findPipelineEditorDrawer().props('isVisible')).toBe(false);
- });
- });
-
- describe('file tree', () => {
- const toggleFileTree = async () => {
- findFileTreeBtn().vm.$emit('click');
- await nextTick();
- };
-
- describe('button toggle', () => {
- beforeEach(() => {
- createComponent({
- stubs: {
- GlButton,
- PipelineEditorFileNav,
- },
- });
- });
-
- it('shows button toggle', () => {
- expect(findFileTreeBtn().exists()).toBe(true);
- });
-
- it('toggles the drawer on button click', async () => {
- await toggleFileTree();
-
- expect(findPipelineEditorFileTree().exists()).toBe(true);
-
- await toggleFileTree();
-
- expect(findPipelineEditorFileTree().exists()).toBe(false);
- });
-
- it('sets the display state in local storage', async () => {
- await toggleFileTree();
-
- expect(localStorage.getItem(FILE_TREE_DISPLAY_KEY)).toBe('true');
-
- await toggleFileTree();
-
- expect(localStorage.getItem(FILE_TREE_DISPLAY_KEY)).toBe('false');
- });
- });
-
- describe('when file tree display state is saved in local storage', () => {
- beforeEach(() => {
- localStorage.setItem(FILE_TREE_DISPLAY_KEY, 'true');
- createComponent({
- stubs: { PipelineEditorFileNav },
- });
- });
-
- it('shows the file tree by default', () => {
- expect(findPipelineEditorFileTree().exists()).toBe(true);
- });
- });
-
- describe('when file tree display state is not saved in local storage', () => {
- beforeEach(() => {
- createComponent({
- stubs: { PipelineEditorFileNav },
- });
- });
-
- it('hides the file tree by default', () => {
- expect(findPipelineEditorFileTree().exists()).toBe(false);
- });
- });
- });
-});
diff --git a/spec/frontend/pipeline_new/components/legacy_pipeline_new_form_spec.js b/spec/frontend/pipeline_new/components/legacy_pipeline_new_form_spec.js
deleted file mode 100644
index 512b152f106..00000000000
--- a/spec/frontend/pipeline_new/components/legacy_pipeline_new_form_spec.js
+++ /dev/null
@@ -1,456 +0,0 @@
-import { GlForm, GlSprintf, GlLoadingIcon } from '@gitlab/ui';
-import { mount, shallowMount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import { nextTick } from 'vue';
-import CreditCardValidationRequiredAlert from 'ee_component/billings/components/cc_validation_required_alert.vue';
-import { TEST_HOST } from 'helpers/test_constants';
-import waitForPromises from 'helpers/wait_for_promises';
-import axios from '~/lib/utils/axios_utils';
-import httpStatusCodes from '~/lib/utils/http_status';
-import { redirectTo } from '~/lib/utils/url_utility';
-import LegacyPipelineNewForm from '~/pipeline_new/components/legacy_pipeline_new_form.vue';
-import RefsDropdown from '~/pipeline_new/components/refs_dropdown.vue';
-import {
- mockQueryParams,
- mockPostParams,
- mockProjectId,
- mockError,
- mockRefs,
- mockCreditCardValidationRequiredError,
-} from '../mock_data';
-
-jest.mock('~/lib/utils/url_utility', () => ({
- redirectTo: jest.fn(),
-}));
-
-const projectRefsEndpoint = '/root/project/refs';
-const pipelinesPath = '/root/project/-/pipelines';
-const configVariablesPath = '/root/project/-/pipelines/config_variables';
-const newPipelinePostResponse = { id: 1 };
-const defaultBranch = 'main';
-
-describe('Pipeline New Form', () => {
- let wrapper;
- let mock;
- let dummySubmitEvent;
-
- const findForm = () => wrapper.findComponent(GlForm);
- const findRefsDropdown = () => wrapper.findComponent(RefsDropdown);
- const findSubmitButton = () => wrapper.find('[data-testid="run_pipeline_button"]');
- const findVariableRows = () => wrapper.findAll('[data-testid="ci-variable-row"]');
- const findRemoveIcons = () => wrapper.findAll('[data-testid="remove-ci-variable-row"]');
- const findDropdowns = () => wrapper.findAll('[data-testid="pipeline-form-ci-variable-type"]');
- const findKeyInputs = () => wrapper.findAll('[data-testid="pipeline-form-ci-variable-key"]');
- const findValueInputs = () => wrapper.findAll('[data-testid="pipeline-form-ci-variable-value"]');
- const findErrorAlert = () => wrapper.find('[data-testid="run-pipeline-error-alert"]');
- const findWarningAlert = () => wrapper.find('[data-testid="run-pipeline-warning-alert"]');
- const findWarningAlertSummary = () => findWarningAlert().findComponent(GlSprintf);
- const findWarnings = () => wrapper.findAll('[data-testid="run-pipeline-warning"]');
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findCCAlert = () => wrapper.findComponent(CreditCardValidationRequiredAlert);
- const getFormPostParams = () => JSON.parse(mock.history.post[0].data);
-
- const selectBranch = (branch) => {
- // Select a branch in the dropdown
- findRefsDropdown().vm.$emit('input', {
- shortName: branch,
- fullName: `refs/heads/${branch}`,
- });
- };
-
- const createComponent = (props = {}, method = shallowMount) => {
- wrapper = method(LegacyPipelineNewForm, {
- provide: {
- projectRefsEndpoint,
- },
- propsData: {
- projectId: mockProjectId,
- pipelinesPath,
- configVariablesPath,
- defaultBranch,
- refParam: defaultBranch,
- settingsLink: '',
- maxWarnings: 25,
- ...props,
- },
- });
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- mock.onGet(configVariablesPath).reply(httpStatusCodes.OK, {});
- mock.onGet(projectRefsEndpoint).reply(httpStatusCodes.OK, mockRefs);
-
- dummySubmitEvent = {
- preventDefault: jest.fn(),
- };
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
-
- mock.restore();
- });
-
- describe('Form', () => {
- beforeEach(async () => {
- createComponent(mockQueryParams, mount);
-
- mock.onPost(pipelinesPath).reply(httpStatusCodes.OK, newPipelinePostResponse);
-
- await waitForPromises();
- });
-
- it('displays the correct values for the provided query params', async () => {
- expect(findDropdowns().at(0).props('text')).toBe('Variable');
- expect(findDropdowns().at(1).props('text')).toBe('File');
- expect(findRefsDropdown().props('value')).toEqual({ shortName: 'tag-1' });
- expect(findVariableRows()).toHaveLength(3);
- });
-
- it('displays a variable from provided query params', () => {
- expect(findKeyInputs().at(0).element.value).toBe('test_var');
- expect(findValueInputs().at(0).element.value).toBe('test_var_val');
- });
-
- it('displays an empty variable for the user to fill out', async () => {
- expect(findKeyInputs().at(2).element.value).toBe('');
- expect(findValueInputs().at(2).element.value).toBe('');
- expect(findDropdowns().at(2).props('text')).toBe('Variable');
- });
-
- it('does not display remove icon for last row', () => {
- expect(findRemoveIcons()).toHaveLength(2);
- });
-
- it('removes ci variable row on remove icon button click', async () => {
- findRemoveIcons().at(1).trigger('click');
-
- await nextTick();
-
- expect(findVariableRows()).toHaveLength(2);
- });
-
- it('creates blank variable on input change event', async () => {
- const input = findKeyInputs().at(2);
- input.element.value = 'test_var_2';
- input.trigger('change');
-
- await nextTick();
-
- expect(findVariableRows()).toHaveLength(4);
- expect(findKeyInputs().at(3).element.value).toBe('');
- expect(findValueInputs().at(3).element.value).toBe('');
- });
- });
-
- describe('Pipeline creation', () => {
- beforeEach(async () => {
- mock.onPost(pipelinesPath).reply(httpStatusCodes.OK, newPipelinePostResponse);
-
- await waitForPromises();
- });
-
- it('does not submit the native HTML form', async () => {
- createComponent();
-
- findForm().vm.$emit('submit', dummySubmitEvent);
-
- expect(dummySubmitEvent.preventDefault).toHaveBeenCalled();
- });
-
- it('disables the submit button immediately after submitting', async () => {
- createComponent();
-
- expect(findSubmitButton().props('disabled')).toBe(false);
-
- findForm().vm.$emit('submit', dummySubmitEvent);
- await waitForPromises();
-
- expect(findSubmitButton().props('disabled')).toBe(true);
- });
-
- it('creates pipeline with full ref and variables', async () => {
- createComponent();
-
- findForm().vm.$emit('submit', dummySubmitEvent);
- await waitForPromises();
-
- expect(getFormPostParams().ref).toEqual(`refs/heads/${defaultBranch}`);
- expect(redirectTo).toHaveBeenCalledWith(`${pipelinesPath}/${newPipelinePostResponse.id}`);
- });
-
- it('creates a pipeline with short ref and variables from the query params', async () => {
- createComponent(mockQueryParams);
-
- await waitForPromises();
-
- findForm().vm.$emit('submit', dummySubmitEvent);
-
- await waitForPromises();
-
- expect(getFormPostParams()).toEqual(mockPostParams);
- expect(redirectTo).toHaveBeenCalledWith(`${pipelinesPath}/${newPipelinePostResponse.id}`);
- });
- });
-
- describe('When the ref has been changed', () => {
- beforeEach(async () => {
- createComponent({}, mount);
-
- await waitForPromises();
- });
- it('variables persist between ref changes', async () => {
- selectBranch('main');
-
- await waitForPromises();
-
- const mainInput = findKeyInputs().at(0);
- mainInput.element.value = 'build_var';
- mainInput.trigger('change');
-
- await nextTick();
-
- selectBranch('branch-1');
-
- await waitForPromises();
-
- const branchOneInput = findKeyInputs().at(0);
- branchOneInput.element.value = 'deploy_var';
- branchOneInput.trigger('change');
-
- await nextTick();
-
- selectBranch('main');
-
- await waitForPromises();
-
- expect(findKeyInputs().at(0).element.value).toBe('build_var');
- expect(findVariableRows().length).toBe(2);
-
- selectBranch('branch-1');
-
- await waitForPromises();
-
- expect(findKeyInputs().at(0).element.value).toBe('deploy_var');
- expect(findVariableRows().length).toBe(2);
- });
- });
-
- describe('when yml defines a variable', () => {
- const mockYmlKey = 'yml_var';
- const mockYmlValue = 'yml_var_val';
- const mockYmlMultiLineValue = `A value
- with multiple
- lines`;
- const mockYmlDesc = 'A var from yml.';
-
- it('loading icon is shown when content is requested and hidden when received', async () => {
- createComponent(mockQueryParams, mount);
-
- mock.onGet(configVariablesPath).reply(httpStatusCodes.OK, {
- [mockYmlKey]: {
- value: mockYmlValue,
- description: mockYmlDesc,
- },
- });
-
- expect(findLoadingIcon().exists()).toBe(true);
-
- await waitForPromises();
-
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- it('multi-line strings are added to the value field without removing line breaks', async () => {
- createComponent(mockQueryParams, mount);
-
- mock.onGet(configVariablesPath).reply(httpStatusCodes.OK, {
- [mockYmlKey]: {
- value: mockYmlMultiLineValue,
- description: mockYmlDesc,
- },
- });
-
- await waitForPromises();
-
- expect(findValueInputs().at(0).element.value).toBe(mockYmlMultiLineValue);
- });
-
- describe('with description', () => {
- beforeEach(async () => {
- createComponent(mockQueryParams, mount);
-
- mock.onGet(configVariablesPath).reply(httpStatusCodes.OK, {
- [mockYmlKey]: {
- value: mockYmlValue,
- description: mockYmlDesc,
- },
- });
-
- await waitForPromises();
- });
-
- it('displays all the variables', async () => {
- expect(findVariableRows()).toHaveLength(4);
- });
-
- it('displays a variable from yml', () => {
- expect(findKeyInputs().at(0).element.value).toBe(mockYmlKey);
- expect(findValueInputs().at(0).element.value).toBe(mockYmlValue);
- });
-
- it('displays a variable from provided query params', () => {
- expect(findKeyInputs().at(1).element.value).toBe('test_var');
- expect(findValueInputs().at(1).element.value).toBe('test_var_val');
- });
-
- it('adds a description to the first variable from yml', () => {
- expect(findVariableRows().at(0).text()).toContain(mockYmlDesc);
- });
-
- it('removes the description when a variable key changes', async () => {
- findKeyInputs().at(0).element.value = 'yml_var_modified';
- findKeyInputs().at(0).trigger('change');
-
- await nextTick();
-
- expect(findVariableRows().at(0).text()).not.toContain(mockYmlDesc);
- });
- });
-
- describe('without description', () => {
- beforeEach(async () => {
- createComponent(mockQueryParams, mount);
-
- mock.onGet(configVariablesPath).reply(httpStatusCodes.OK, {
- [mockYmlKey]: {
- value: mockYmlValue,
- description: null,
- },
- yml_var2: {
- value: 'yml_var2_val',
- },
- yml_var3: {
- description: '',
- },
- });
-
- await waitForPromises();
- });
-
- it('displays all the variables', async () => {
- expect(findVariableRows()).toHaveLength(3);
- });
- });
- });
-
- describe('Form errors and warnings', () => {
- beforeEach(() => {
- createComponent();
- });
-
- describe('when the refs cannot be loaded', () => {
- beforeEach(() => {
- mock
- .onGet(projectRefsEndpoint, { params: { search: '' } })
- .reply(httpStatusCodes.INTERNAL_SERVER_ERROR);
-
- findRefsDropdown().vm.$emit('loadingError');
- });
-
- it('shows both an error alert', () => {
- expect(findErrorAlert().exists()).toBe(true);
- expect(findWarningAlert().exists()).toBe(false);
- });
- });
-
- describe('when the error response can be handled', () => {
- beforeEach(async () => {
- mock.onPost(pipelinesPath).reply(httpStatusCodes.BAD_REQUEST, mockError);
-
- findForm().vm.$emit('submit', dummySubmitEvent);
-
- await waitForPromises();
- });
-
- it('shows both error and warning', () => {
- expect(findErrorAlert().exists()).toBe(true);
- expect(findWarningAlert().exists()).toBe(true);
- });
-
- it('shows the correct error', () => {
- expect(findErrorAlert().text()).toBe(mockError.errors[0]);
- });
-
- it('shows the correct warning title', () => {
- const { length } = mockError.warnings;
-
- expect(findWarningAlertSummary().attributes('message')).toBe(`${length} warnings found:`);
- });
-
- it('shows the correct amount of warnings', () => {
- expect(findWarnings()).toHaveLength(mockError.warnings.length);
- });
-
- it('re-enables the submit button', () => {
- expect(findSubmitButton().props('disabled')).toBe(false);
- });
-
- it('does not show the credit card validation required alert', () => {
- expect(findCCAlert().exists()).toBe(false);
- });
-
- describe('when the error response is credit card validation required', () => {
- beforeEach(async () => {
- mock
- .onPost(pipelinesPath)
- .reply(httpStatusCodes.BAD_REQUEST, mockCreditCardValidationRequiredError);
-
- window.gon = {
- subscriptions_url: TEST_HOST,
- payment_form_url: TEST_HOST,
- };
-
- findForm().vm.$emit('submit', dummySubmitEvent);
-
- await waitForPromises();
- });
-
- it('shows credit card validation required alert', () => {
- expect(findErrorAlert().exists()).toBe(false);
- expect(findCCAlert().exists()).toBe(true);
- });
-
- it('clears error and hides the alert on dismiss', async () => {
- expect(findCCAlert().exists()).toBe(true);
- expect(wrapper.vm.$data.error).toBe(mockCreditCardValidationRequiredError.errors[0]);
-
- findCCAlert().vm.$emit('dismiss');
-
- await nextTick();
-
- expect(findCCAlert().exists()).toBe(false);
- expect(wrapper.vm.$data.error).toBe(null);
- });
- });
- });
-
- describe('when the error response cannot be handled', () => {
- beforeEach(async () => {
- mock
- .onPost(pipelinesPath)
- .reply(httpStatusCodes.INTERNAL_SERVER_ERROR, 'something went wrong');
-
- findForm().vm.$emit('submit', dummySubmitEvent);
-
- await waitForPromises();
- });
-
- it('re-enables the submit button', () => {
- expect(findSubmitButton().props('disabled')).toBe(false);
- });
- });
- });
-});
diff --git a/spec/frontend/pipeline_new/components/pipeline_new_form_spec.js b/spec/frontend/pipeline_new/components/pipeline_new_form_spec.js
index 3e699b93fd3..2360dd7d103 100644
--- a/spec/frontend/pipeline_new/components/pipeline_new_form_spec.js
+++ b/spec/frontend/pipeline_new/components/pipeline_new_form_spec.js
@@ -295,11 +295,11 @@ describe('Pipeline New Form', () => {
expect(dropdownItems.at(2).text()).toBe(valueOptions[2]);
});
- it('variables with multiple predefined values sets the first option as the default', () => {
+ it('variable with multiple predefined values sets value as the default', () => {
const dropdown = findValueDropdowns().at(0);
const { valueOptions } = mockYamlVariables[2];
- expect(dropdown.props('text')).toBe(valueOptions[0]);
+ expect(dropdown.props('text')).toBe(valueOptions[1]);
});
});
diff --git a/spec/frontend/pipeline_new/mock_data.js b/spec/frontend/pipeline_new/mock_data.js
index e95a65171fc..2af0ef4d7c4 100644
--- a/spec/frontend/pipeline_new/mock_data.js
+++ b/spec/frontend/pipeline_new/mock_data.js
@@ -83,7 +83,7 @@ export const mockYamlVariables = [
{
description: 'This is a variable with predefined values.',
key: 'VAR_WITH_OPTIONS',
- value: 'development',
+ value: 'staging',
valueOptions: ['development', 'staging', 'production'],
},
];
@@ -105,7 +105,7 @@ export const mockYamlVariablesWithoutDesc = [
{
description: null,
key: 'VAR_WITH_OPTIONS',
- value: 'development',
+ value: 'staging',
valueOptions: ['development', 'staging', 'production'],
},
];
diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_mini_graph_spec.js b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_mini_graph_spec.js
index 7fa8a18ea1f..036b82530d5 100644
--- a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_mini_graph_spec.js
+++ b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_mini_graph_spec.js
@@ -48,7 +48,6 @@ describe('Pipeline Mini Graph', () => {
isMergeTrain: false,
pipelinePath: '',
stages: expect.any(Array),
- stagesClass: '',
updateDropdown: false,
upstreamPipeline: undefined,
});
@@ -63,15 +62,6 @@ describe('Pipeline Mini Graph', () => {
expect(findUpstreamArrowIcon().exists()).toBe(false);
expect(findDownstreamArrowIcon().exists()).toBe(false);
});
-
- it('triggers events in "action request complete"', () => {
- createComponent();
-
- findPipelineMiniGraph(0).vm.$emit('pipelineActionRequestComplete');
- findPipelineMiniGraph(1).vm.$emit('pipelineActionRequestComplete');
-
- expect(wrapper.emitted('pipelineActionRequestComplete')).toHaveLength(2);
- });
});
describe('rendered state with upstream pipeline', () => {
@@ -92,7 +82,6 @@ describe('Pipeline Mini Graph', () => {
isMergeTrain: false,
pipelinePath: '',
stages: expect.any(Array),
- stagesClass: '',
updateDropdown: false,
upstreamPipeline: expect.any(Object),
});
@@ -124,7 +113,6 @@ describe('Pipeline Mini Graph', () => {
isMergeTrain: false,
pipelinePath: 'my/pipeline/path',
stages: expect.any(Array),
- stagesClass: '',
updateDropdown: false,
upstreamPipeline: undefined,
});
diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stage_spec.js b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stage_spec.js
index 52b440f18bb..b7a9297d856 100644
--- a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stage_spec.js
+++ b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stage_spec.js
@@ -186,7 +186,7 @@ describe('Pipelines stage component', () => {
});
});
- describe('pipelineActionRequestComplete', () => {
+ describe('job update in dropdown', () => {
beforeEach(async () => {
mock.onGet(dropdownPath).reply(200, stageReply);
mock.onPost(`${stageReply.latest_statuses[0].status.action.path}.json`).reply(200);
@@ -204,24 +204,11 @@ describe('Pipelines stage component', () => {
await findCiActionBtn().trigger('click');
};
- it('closes dropdown when job item action is clicked', async () => {
- const hidden = jest.fn();
-
- wrapper.vm.$root.$on('bv::dropdown::hide', hidden);
-
- expect(hidden).toHaveBeenCalledTimes(0);
-
- await clickCiAction();
- await waitForPromises();
-
- expect(hidden).toHaveBeenCalledTimes(1);
- });
-
- it('emits `pipelineActionRequestComplete` when job item action is clicked', async () => {
+ it('keeps dropdown open when job item action is clicked', async () => {
await clickCiAction();
await waitForPromises();
- expect(wrapper.emitted('pipelineActionRequestComplete')).toHaveLength(1);
+ expect(findDropdown().classes('show')).toBe(true);
});
});
diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stages_spec.js b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stages_spec.js
index bfb780d5d39..c123f53886e 100644
--- a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stages_spec.js
+++ b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stages_spec.js
@@ -26,12 +26,6 @@ describe('Pipeline Stages', () => {
expect(findPipelineStages()).toHaveLength(mockStages.length);
});
- it('renders stages with a custom class', () => {
- createComponent({ stagesClass: 'my-class' });
-
- expect(wrapper.findAll('.my-class')).toHaveLength(mockStages.length);
- });
-
it('does not fail when stages are empty', () => {
createComponent({ stages: [] });
@@ -39,15 +33,6 @@ describe('Pipeline Stages', () => {
expect(findPipelineStages()).toHaveLength(0);
});
- it('triggers events in "action request complete" in stages', () => {
- createComponent();
-
- findPipelineStagesAt(0).vm.$emit('pipelineActionRequestComplete');
- findPipelineStagesAt(1).vm.$emit('pipelineActionRequestComplete');
-
- expect(wrapper.emitted('pipelineActionRequestComplete')).toHaveLength(2);
- });
-
it('update dropdown is false by default', () => {
createComponent();
diff --git a/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js b/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js
index ee3eaaf5ef3..ba7262353f0 100644
--- a/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js
+++ b/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js
@@ -6,7 +6,10 @@ import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import Api from '~/api';
import axios from '~/lib/utils/axios_utils';
import PipelinesFilteredSearch from '~/pipelines/components/pipelines_list/pipelines_filtered_search.vue';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
+import {
+ FILTERED_SEARCH_TERM,
+ OPERATORS_IS,
+} from '~/vue_shared/components/filtered_search_bar/constants';
import { TRACKING_CATEGORIES } from '~/pipelines/constants';
import { users, mockSearch, branches, tags } from '../mock_data';
@@ -63,7 +66,7 @@ describe('Pipelines filtered search', () => {
title: 'Trigger author',
unique: true,
projectId: '21',
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
});
expect(findBranchToken()).toMatchObject({
@@ -73,7 +76,7 @@ describe('Pipelines filtered search', () => {
unique: true,
projectId: '21',
defaultBranchName: 'main',
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
});
expect(findSourceToken()).toMatchObject({
@@ -81,7 +84,7 @@ describe('Pipelines filtered search', () => {
icon: 'trigger-source',
title: 'Source',
unique: true,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
});
expect(findStatusToken()).toMatchObject({
@@ -89,7 +92,7 @@ describe('Pipelines filtered search', () => {
icon: 'status',
title: 'Status',
unique: true,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
});
expect(findTagToken()).toMatchObject({
@@ -97,7 +100,7 @@ describe('Pipelines filtered search', () => {
icon: 'tag',
title: 'Tag name',
unique: true,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
});
});
@@ -111,7 +114,7 @@ describe('Pipelines filtered search', () => {
it('disables tag name token when branch name token is active', async () => {
findFilteredSearch().vm.$emit('input', [
{ type: 'ref', value: { data: 'branch-1', operator: '=' } },
- { type: 'filtered-search-term', value: { data: '' } },
+ { type: FILTERED_SEARCH_TERM, value: { data: '' } },
]);
await nextTick();
@@ -122,7 +125,7 @@ describe('Pipelines filtered search', () => {
it('disables branch name token when tag name token is active', async () => {
findFilteredSearch().vm.$emit('input', [
{ type: 'tag', value: { data: 'tag-1', operator: '=' } },
- { type: 'filtered-search-term', value: { data: '' } },
+ { type: FILTERED_SEARCH_TERM, value: { data: '' } },
]);
await nextTick();
@@ -139,7 +142,7 @@ describe('Pipelines filtered search', () => {
});
it('resets tokens disabled state when clearing tokens by backspace', async () => {
- findFilteredSearch().vm.$emit('input', [{ type: 'filtered-search-term', value: { data: '' } }]);
+ findFilteredSearch().vm.$emit('input', [{ type: FILTERED_SEARCH_TERM, value: { data: '' } }]);
await nextTick();
expect(findBranchToken().disabled).toBe(false);
@@ -172,7 +175,7 @@ describe('Pipelines filtered search', () => {
operator: '=',
},
},
- { type: 'filtered-search-term', value: { data: '' } },
+ { type: FILTERED_SEARCH_TERM, value: { data: '' } },
];
expect(findFilteredSearch().props('value')).toMatchObject(expectedValueProp);
diff --git a/spec/frontend/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates_spec.js b/spec/frontend/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates_spec.js
index b537c81da3f..f255e0d857f 100644
--- a/spec/frontend/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates_spec.js
+++ b/spec/frontend/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates_spec.js
@@ -13,7 +13,7 @@ import {
RUNNERS_DOCUMENTATION_LINK_CLICKED_EVENT,
RUNNERS_SETTINGS_BUTTON_CLICKED_EVENT,
I18N,
-} from '~/pipeline_editor/constants';
+} from '~/ci/pipeline_editor/constants';
const pipelineEditorPath = '/-/ci/editor';
const ciRunnerSettingsPath = '/-/settings/ci_cd';
diff --git a/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js b/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js
index d9199f3b0f7..df10742fd93 100644
--- a/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js
+++ b/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js
@@ -1,7 +1,7 @@
import { GlAlert } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { setHTMLFixture } from 'helpers/fixtures';
-import { CI_CONFIG_STATUS_VALID } from '~/pipeline_editor/constants';
+import { CI_CONFIG_STATUS_VALID } from '~/ci/pipeline_editor/constants';
import LinksInner from '~/pipelines/components/graph_shared/links_inner.vue';
import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
import JobPill from '~/pipelines/components/pipeline_graph/job_pill.vue';
diff --git a/spec/frontend/pipelines/pipelines_table_spec.js b/spec/frontend/pipelines/pipelines_table_spec.js
index 044683ce533..740037a5ac8 100644
--- a/spec/frontend/pipelines/pipelines_table_spec.js
+++ b/spec/frontend/pipelines/pipelines_table_spec.js
@@ -17,7 +17,6 @@ import {
TRACKING_CATEGORIES,
} from '~/pipelines/constants';
-import eventHub from '~/pipelines/event_hub';
import CiBadge from '~/vue_shared/components/ci_badge_link.vue';
jest.mock('~/pipelines/event_hub');
@@ -134,12 +133,6 @@ describe('Pipelines Table', () => {
expect(findPipelineMiniGraph().props('stages')).toHaveLength(0);
});
});
-
- it('when action request is complete, should refresh table', () => {
- findPipelineMiniGraph().vm.$emit('pipelineActionRequestComplete');
-
- expect(eventHub.$emit).toHaveBeenCalledWith('refreshPipelinesTable');
- });
});
describe('duration cell', () => {
diff --git a/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
index 94f9a37f707..c090fd353f7 100644
--- a/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
+++ b/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
@@ -2,6 +2,10 @@ import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlIcon } from '@gitl
import { shallowMount } from '@vue/test-utils';
import { stubComponent } from 'helpers/stub_component';
import PipelineStatusToken from '~/pipelines/components/pipelines_list/tokens/pipeline_status_token.vue';
+import {
+ TOKEN_TITLE_STATUS,
+ TOKEN_TYPE_STATUS,
+} from '~/vue_shared/components/filtered_search_bar/constants';
describe('Pipeline Status Token', () => {
let wrapper;
@@ -13,9 +17,9 @@ describe('Pipeline Status Token', () => {
const defaultProps = {
config: {
- type: 'status',
+ type: TOKEN_TYPE_STATUS,
icon: 'status',
- title: 'Status',
+ title: TOKEN_TITLE_STATUS,
unique: true,
},
value: {
diff --git a/spec/frontend/popovers/components/popovers_spec.js b/spec/frontend/popovers/components/popovers_spec.js
index eba6b95214d..1299e7277d1 100644
--- a/spec/frontend/popovers/components/popovers_spec.js
+++ b/spec/frontend/popovers/components/popovers_spec.js
@@ -57,12 +57,13 @@ describe('popovers/components/popovers.vue', () => {
describe('supports HTML content', () => {
const svgIcon = '<svg><use xlink:href="icons.svg#test"></use></svg>';
+ const escapedSvgIcon = '<svg><use xlink:href=&quot;icons.svg#test&quot;></use></svg>';
it.each`
description | content | render
${'renders html content correctly'} | ${'<b>HTML</b>'} | ${'<b>HTML</b>'}
${'removes any unsafe content'} | ${'<script>alert(XSS)</script>'} | ${''}
- ${'renders svg icons correctly'} | ${svgIcon} | ${svgIcon}
+ ${'renders svg icons correctly'} | ${svgIcon} | ${escapedSvgIcon}
`('$description', async ({ content, render }) => {
await buildWrapper(createPopoverTarget({ content, html: true }));
diff --git a/spec/frontend/projects/commit/components/branches_dropdown_spec.js b/spec/frontend/projects/commit/components/branches_dropdown_spec.js
index e2848e615c3..a84dd246f5d 100644
--- a/spec/frontend/projects/commit/components/branches_dropdown_spec.js
+++ b/spec/frontend/projects/commit/components/branches_dropdown_spec.js
@@ -13,7 +13,7 @@ describe('BranchesDropdown', () => {
let store;
const spyFetchBranches = jest.fn();
- const createComponent = (term, state = { isFetching: false }) => {
+ const createComponent = (props, state = { isFetching: false }) => {
store = new Vuex.Store({
getters: {
joinedBranches: () => ['_main_', '_branch_1_', '_branch_2_'],
@@ -28,7 +28,8 @@ describe('BranchesDropdown', () => {
shallowMount(BranchesDropdown, {
store,
propsData: {
- value: term,
+ value: props.value,
+ blanked: props.blanked || false,
},
}),
);
@@ -48,23 +49,40 @@ describe('BranchesDropdown', () => {
describe('On mount', () => {
beforeEach(() => {
- createComponent('');
+ createComponent({ value: '' });
});
it('invokes fetchBranches', () => {
expect(spyFetchBranches).toHaveBeenCalled();
});
+
+ describe('with a value but visually blanked', () => {
+ beforeEach(() => {
+ createComponent({ value: '_main_', blanked: true }, { branch: '_main_' });
+ });
+
+ it('renders all branches', () => {
+ expect(findAllDropdownItems()).toHaveLength(3);
+ expect(findDropdownItemByIndex(0).text()).toBe('_main_');
+ expect(findDropdownItemByIndex(1).text()).toBe('_branch_1_');
+ expect(findDropdownItemByIndex(2).text()).toBe('_branch_2_');
+ });
+
+ it('selects the active branch', () => {
+ expect(wrapper.vm.isSelected('_main_')).toBe(true);
+ });
+ });
});
describe('Loading states', () => {
it('shows loading icon while fetching', () => {
- createComponent('', { isFetching: true });
+ createComponent({ value: '' }, { isFetching: true });
expect(findLoading().isVisible()).toBe(true);
});
it('does not show loading icon', () => {
- createComponent('');
+ createComponent({ value: '' });
expect(findLoading().isVisible()).toBe(false);
});
@@ -72,7 +90,7 @@ describe('BranchesDropdown', () => {
describe('No branches found', () => {
beforeEach(() => {
- createComponent('_non_existent_branch_');
+ createComponent({ value: '_non_existent_branch_' });
});
it('renders empty results message', () => {
@@ -90,7 +108,7 @@ describe('BranchesDropdown', () => {
describe('Search term is empty', () => {
beforeEach(() => {
- createComponent('');
+ createComponent({ value: '' });
});
it('renders all branches when search term is empty', () => {
@@ -107,7 +125,7 @@ describe('BranchesDropdown', () => {
describe('When searching', () => {
beforeEach(() => {
- createComponent('');
+ createComponent({ value: '' });
});
it('invokes fetchBranches', async () => {
@@ -124,7 +142,7 @@ describe('BranchesDropdown', () => {
describe('Branches found', () => {
beforeEach(() => {
- createComponent('_branch_1_', { branch: '_branch_1_' });
+ createComponent({ value: '_branch_1_' }, { branch: '_branch_1_' });
});
it('renders only the branch searched for', () => {
@@ -156,7 +174,7 @@ describe('BranchesDropdown', () => {
describe('Case insensitive for search term', () => {
beforeEach(() => {
- createComponent('_BrAnCh_1_');
+ createComponent({ value: '_BrAnCh_1_' });
});
it('renders only the branch searched for', () => {
diff --git a/spec/frontend/projects/new/components/new_project_url_select_spec.js b/spec/frontend/projects/new/components/new_project_url_select_spec.js
index b6d4ee32cf5..67532cea61e 100644
--- a/spec/frontend/projects/new/components/new_project_url_select_spec.js
+++ b/spec/frontend/projects/new/components/new_project_url_select_spec.js
@@ -63,6 +63,8 @@ describe('NewProjectUrlSelect component', () => {
rootUrl: 'https://gitlab.com/',
trackLabel: 'blank_project',
userNamespaceId: '1',
+ inputId: 'input_id',
+ inputName: 'input_name',
};
let mockQueryResponse;
@@ -92,7 +94,7 @@ describe('NewProjectUrlSelect component', () => {
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findSelectedPath = () => wrapper.findComponent(GlTruncate);
const findInput = () => wrapper.findComponent(GlSearchBoxByType);
- const findHiddenNamespaceInput = () => wrapper.find('[name="project[namespace_id]"]');
+ const findHiddenNamespaceInput = () => wrapper.find(`[name="${defaultProvide.inputName}`);
const findHiddenSelectedNamespaceInput = () =>
wrapper.find('[name="project[selected_namespace_id]"]');
@@ -165,6 +167,8 @@ describe('NewProjectUrlSelect component', () => {
it("renders a hidden input with the user's namespace id", () => {
expect(findHiddenNamespaceInput().attributes('value')).toBe(defaultProvide.userNamespaceId);
+ expect(findHiddenNamespaceInput().attributes('name')).toBe(defaultProvide.inputName);
+ expect(findHiddenNamespaceInput().attributes('id')).toBe(defaultProvide.inputId);
});
it('renders a hidden input with the selected namespace id', () => {
@@ -198,6 +202,18 @@ describe('NewProjectUrlSelect component', () => {
expect(listItems.at(5).text()).toBe(data.currentUser.namespace.fullPath);
});
+ it('does not render users section when user namespace id is not provided', async () => {
+ wrapper = mountComponent({
+ mountFn: mount,
+ provide: { ...defaultProvide, userNamespaceId: null },
+ });
+
+ await showDropdown();
+
+ expect(wrapper.findAllComponents(GlDropdownSectionHeader)).toHaveLength(1);
+ expect(wrapper.findAllComponents(GlDropdownSectionHeader).at(0).text()).toBe('Groups');
+ });
+
describe('query fetching', () => {
describe('on component mount', () => {
it('does not fetch query', () => {
@@ -297,7 +313,7 @@ describe('NewProjectUrlSelect component', () => {
);
});
- it('tracks clicking on the dropdown', () => {
+ it('tracks clicking on the dropdown when trackLabel is provided', () => {
wrapper = mountComponent();
const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
@@ -311,4 +327,16 @@ describe('NewProjectUrlSelect component', () => {
unmockTracking();
});
+
+ it('does not track clicking on the dropdown when trackLabel is not provided', () => {
+ wrapper = mountComponent({ provide: { ...defaultProvide, trackLabel: null } });
+
+ const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+
+ findDropdown().vm.$emit('show');
+
+ expect(trackingSpy).not.toHaveBeenCalled();
+
+ unmockTracking();
+ });
});
diff --git a/spec/frontend/projects/project_new_spec.js b/spec/frontend/projects/project_new_spec.js
index 4fcecc3a307..d69bfc4ec92 100644
--- a/spec/frontend/projects/project_new_spec.js
+++ b/spec/frontend/projects/project_new_spec.js
@@ -1,12 +1,14 @@
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import { TEST_HOST } from 'helpers/test_constants';
import projectNew from '~/projects/project_new';
+import { checkRules } from '~/projects/project_name_rules';
import { mockTracking, triggerEvent, unmockTracking } from 'helpers/tracking_helper';
describe('New Project', () => {
let $projectImportUrl;
let $projectPath;
let $projectName;
+ let $projectNameError;
const mockKeyup = (el) => el.dispatchEvent(new KeyboardEvent('keyup'));
const mockChange = (el) => el.dispatchEvent(new Event('change'));
@@ -29,6 +31,7 @@ describe('New Project', () => {
</div>
</div>
<input id="project_name" />
+ <div class="gl-field-error hidden" id="project_name_error" />
<input id="project_path" />
</div>
<div class="js-user-readme-repo"></div>
@@ -41,6 +44,7 @@ describe('New Project', () => {
$projectImportUrl = document.querySelector('#project_import_url');
$projectPath = document.querySelector('#project_path');
$projectName = document.querySelector('#project_name');
+ $projectNameError = document.querySelector('#project_name_error');
});
afterEach(() => {
@@ -84,6 +88,57 @@ describe('New Project', () => {
});
});
+ describe('tracks manual name input', () => {
+ beforeEach(() => {
+ projectNew.bindEvents();
+ });
+
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ it('no error message by default', () => {
+ expect($projectNameError.classList.contains('hidden')).toBe(true);
+ });
+
+ it('show error message if name is validate', () => {
+ $projectName.value = '.validate!Name';
+ triggerEvent($projectName, 'change');
+
+ expect($projectNameError.innerText).toBe(
+ "Name must start with a letter, digit, emoji, or '_'",
+ );
+ expect($projectNameError.classList.contains('hidden')).toBe(false);
+ });
+ });
+
+ describe('project name rule', () => {
+ describe("Name must start with a letter, digit, emoji, or '_'", () => {
+ const errormsg = "Name must start with a letter, digit, emoji, or '_'";
+ it("'.foo' should error", () => {
+ const text = '.foo';
+ expect(checkRules(text)).toBe(errormsg);
+ });
+ it('_foo should passed', () => {
+ const text = '_foo';
+ expect(checkRules(text)).toBe('');
+ });
+ });
+
+ describe("Name can contain only letters, digits, emojis, '_', '.', '+', dashes, or spaces", () => {
+ const errormsg =
+ "Name can contain only letters, digits, emojis, '_', '.', '+', dashes, or spaces";
+ it("'foo(#^.^#)foo' should error", () => {
+ const text = 'foo(#^.^#)foo';
+ expect(checkRules(text)).toBe(errormsg);
+ });
+ it("'foo123😊_.+- ' should passed", () => {
+ const text = 'foo123😊_.+- ';
+ expect(checkRules(text)).toBe('');
+ });
+ });
+ });
+
describe('deriveProjectPathFromUrl', () => {
const dummyImportUrl = `${TEST_HOST}/dummy/import/url.git`;
diff --git a/spec/frontend/projects/settings/branch_rules/components/view/index_spec.js b/spec/frontend/projects/settings/branch_rules/components/view/index_spec.js
index 27065a704e2..bc373d9deb7 100644
--- a/spec/frontend/projects/settings/branch_rules/components/view/index_spec.js
+++ b/spec/frontend/projects/settings/branch_rules/components/view/index_spec.js
@@ -16,10 +16,12 @@ import {
branchProtectionsMockResponse,
approvalRulesMock,
statusChecksRulesMock,
+ matchingBranchesCount,
} from './mock_data';
jest.mock('~/lib/utils/url_utility', () => ({
getParameterByName: jest.fn().mockReturnValue('main'),
+ mergeUrlParams: jest.fn().mockReturnValue('/branches?state=all&search=main'),
joinPaths: jest.fn(),
}));
@@ -65,6 +67,13 @@ describe('View branch rules', () => {
const findForcePushTitle = () => wrapper.findByText(I18N.allowForcePushDescription);
const findApprovalsTitle = () => wrapper.findByText(I18N.approvalsTitle);
const findStatusChecksTitle = () => wrapper.findByText(I18N.statusChecksTitle);
+ const findMatchingBranchesLink = () =>
+ wrapper.findByText(
+ sprintf(I18N.matchingBranchesLinkTitle, {
+ total: matchingBranchesCount,
+ subject: 'branches',
+ }),
+ );
it('gets the branch param from url and renders it in the view', () => {
expect(util.getParameterByName).toHaveBeenCalledWith('branch');
@@ -85,6 +94,12 @@ describe('View branch rules', () => {
expect(findBranchTitle().exists()).toBe(true);
});
+ it('renders matching branches link', () => {
+ const matchingBranchesLink = findMatchingBranchesLink();
+ expect(matchingBranchesLink.exists()).toBe(true);
+ expect(matchingBranchesLink.attributes().href).toBe('/branches?state=all&search=main');
+ });
+
it('renders a branch protection title', () => {
expect(findBranchProtectionTitle().exists()).toBe(true);
});
diff --git a/spec/frontend/projects/settings/branch_rules/components/view/mock_data.js b/spec/frontend/projects/settings/branch_rules/components/view/mock_data.js
index c07d4673344..821dba75b62 100644
--- a/spec/frontend/projects/settings/branch_rules/components/view/mock_data.js
+++ b/spec/frontend/projects/settings/branch_rules/components/view/mock_data.js
@@ -109,6 +109,8 @@ export const accessLevelsMockResponse = [
},
];
+export const matchingBranchesCount = 3;
+
export const branchProtectionsMockResponse = {
data: {
project: {
@@ -141,6 +143,7 @@ export const branchProtectionsMockResponse = {
__typename: 'ExternalStatusCheckConnection',
nodes: statusChecksRulesMock,
},
+ matchingBranchesCount,
},
{
__typename: 'BranchRule',
@@ -166,6 +169,7 @@ export const branchProtectionsMockResponse = {
__typename: 'ExternalStatusCheckConnection',
nodes: [],
},
+ matchingBranchesCount,
},
],
},
diff --git a/spec/frontend/projects/settings/mock_data.js b/spec/frontend/projects/settings/mock_data.js
new file mode 100644
index 00000000000..0262c0e3e43
--- /dev/null
+++ b/spec/frontend/projects/settings/mock_data.js
@@ -0,0 +1,57 @@
+const accessLevelsMockResponse = [
+ {
+ __typename: 'PushAccessLevelEdge',
+ node: {
+ __typename: 'PushAccessLevel',
+ accessLevel: 40,
+ accessLevelDescription: 'Jona Langworth',
+ group: null,
+ user: {
+ __typename: 'UserCore',
+ id: '123',
+ webUrl: 'test.com',
+ name: 'peter',
+ avatarUrl: 'test.com/user.png',
+ },
+ },
+ },
+ {
+ __typename: 'PushAccessLevelEdge',
+ node: {
+ __typename: 'PushAccessLevel',
+ accessLevel: 40,
+ accessLevelDescription: 'Maintainers',
+ group: null,
+ user: null,
+ },
+ },
+];
+
+export const pushAccessLevelsMockResponse = {
+ __typename: 'PushAccessLevelConnection',
+ edges: accessLevelsMockResponse,
+};
+
+export const pushAccessLevelsMockResult = {
+ total: 2,
+ users: [
+ {
+ src: 'test.com/user.png',
+ __typename: 'UserCore',
+ id: '123',
+ webUrl: 'test.com',
+ name: 'peter',
+ avatarUrl: 'test.com/user.png',
+ },
+ ],
+ groups: [],
+ roles: [
+ {
+ __typename: 'PushAccessLevel',
+ accessLevel: 40,
+ accessLevelDescription: 'Maintainers',
+ group: null,
+ user: null,
+ },
+ ],
+};
diff --git a/spec/frontend/projects/settings/repository/branch_rules/app_spec.js b/spec/frontend/projects/settings/repository/branch_rules/app_spec.js
index 6369f04781f..447d7e86ceb 100644
--- a/spec/frontend/projects/settings/repository/branch_rules/app_spec.js
+++ b/spec/frontend/projects/settings/repository/branch_rules/app_spec.js
@@ -5,9 +5,12 @@ import waitForPromises from 'helpers/wait_for_promises';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import BranchRules, { i18n } from '~/projects/settings/repository/branch_rules/app.vue';
import BranchRule from '~/projects/settings/repository/branch_rules/components/branch_rule.vue';
-import branchRulesQuery from '~/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql';
+import branchRulesQuery from 'ee_else_ce/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql';
import { createAlert } from '~/flash';
-import { branchRulesMockResponse, appProvideMock } from './mock_data';
+import {
+ branchRulesMockResponse,
+ appProvideMock,
+} from 'ee_else_ce_jest/projects/settings/repository/branch_rules/mock_data';
jest.mock('~/flash');
diff --git a/spec/frontend/projects/settings/repository/branch_rules/components/branch_rule_spec.js b/spec/frontend/projects/settings/repository/branch_rules/components/branch_rule_spec.js
index 2aa93fd0e28..49c45c080b4 100644
--- a/spec/frontend/projects/settings/repository/branch_rules/components/branch_rule_spec.js
+++ b/spec/frontend/projects/settings/repository/branch_rules/components/branch_rule_spec.js
@@ -50,17 +50,15 @@ describe('Branch rule', () => {
it('renders the protection details list items', () => {
expect(findProtectionDetailsListItems()).toHaveLength(wrapper.vm.approvalDetails.length);
expect(findProtectionDetailsListItems().at(0).text()).toBe(i18n.allowForcePush);
- expect(findProtectionDetailsListItems().at(1).text()).toBe(i18n.codeOwnerApprovalRequired);
- expect(findProtectionDetailsListItems().at(2).text()).toMatchInterpolatedText(
- sprintf(i18n.statusChecks, {
- total: branchRulePropsMock.statusChecksTotal,
- subject: n__('check', 'checks', branchRulePropsMock.statusChecksTotal),
- }),
- );
- expect(findProtectionDetailsListItems().at(3).text()).toMatchInterpolatedText(
- sprintf(i18n.approvalRules, {
- total: branchRulePropsMock.approvalRulesTotal,
- subject: n__('rule', 'rules', branchRulePropsMock.approvalRulesTotal),
+ expect(findProtectionDetailsListItems().at(1).text()).toBe(wrapper.vm.pushAccessLevelsText);
+ });
+
+ it('renders branches count for wildcards', () => {
+ createComponent({ name: 'test-*' });
+ expect(findProtectionDetailsListItems().at(0).text()).toMatchInterpolatedText(
+ sprintf(i18n.matchingBranches, {
+ total: branchRulePropsMock.matchingBranchesCount,
+ subject: n__('branch', 'branches', branchRulePropsMock.matchingBranchesCount),
}),
);
});
diff --git a/spec/frontend/projects/settings/repository/branch_rules/mock_data.js b/spec/frontend/projects/settings/repository/branch_rules/mock_data.js
index 8aa03a12996..6f506882c36 100644
--- a/spec/frontend/projects/settings/repository/branch_rules/mock_data.js
+++ b/spec/frontend/projects/settings/repository/branch_rules/mock_data.js
@@ -1,3 +1,22 @@
+export const accessLevelsMockResponse = [
+ {
+ __typename: 'PushAccessLevelEdge',
+ node: {
+ __typename: 'PushAccessLevel',
+ accessLevel: 40,
+ accessLevelDescription: 'Developers',
+ },
+ },
+ {
+ __typename: 'PushAccessLevelEdge',
+ node: {
+ __typename: 'PushAccessLevel',
+ accessLevel: 40,
+ accessLevelDescription: 'Maintainers',
+ },
+ },
+];
+
export const branchRulesMockResponse = {
data: {
project: {
@@ -9,34 +28,34 @@ export const branchRulesMockResponse = {
{
name: 'main',
isDefault: true,
+ matchingBranchesCount: 1,
branchProtection: {
allowForcePush: true,
- codeOwnerApprovalRequired: true,
- },
- approvalRules: {
- nodes: [{ id: 1 }],
- __typename: 'ApprovalProjectRuleConnection',
- },
- externalStatusChecks: {
- nodes: [{ id: 1 }, { id: 2 }],
- __typename: 'BranchRule',
+ mergeAccessLevels: {
+ edges: [],
+ __typename: 'MergeAccessLevelConnection',
+ },
+ pushAccessLevels: {
+ edges: accessLevelsMockResponse,
+ __typename: 'PushAccessLevelConnection',
+ },
},
__typename: 'BranchRule',
},
{
name: 'test-*',
isDefault: false,
+ matchingBranchesCount: 2,
branchProtection: {
allowForcePush: false,
- codeOwnerApprovalRequired: false,
- },
- approvalRules: {
- nodes: [],
- __typename: 'ApprovalProjectRuleConnection',
- },
- externalStatusChecks: {
- nodes: [],
- __typename: 'BranchRule',
+ mergeAccessLevels: {
+ edges: [],
+ __typename: 'MergeAccessLevelConnection',
+ },
+ pushAccessLevels: {
+ edges: [],
+ __typename: 'PushAccessLevelConnection',
+ },
},
__typename: 'BranchRule',
},
@@ -57,17 +76,22 @@ export const branchRuleProvideMock = {
export const branchRulePropsMock = {
name: 'main',
isDefault: true,
+ matchingBranchesCount: 1,
branchProtection: {
allowForcePush: true,
- codeOwnerApprovalRequired: true,
+ codeOwnerApprovalRequired: false,
+ pushAccessLevels: {
+ edges: accessLevelsMockResponse,
+ },
},
- approvalRulesTotal: 1,
- statusChecksTotal: 2,
+ approvalRulesTotal: 0,
+ statusChecksTotal: 0,
};
export const branchRuleWithoutDetailsPropsMock = {
- name: 'main',
+ name: 'branch-1',
isDefault: false,
+ matchingBranchesCount: 1,
branchProtection: {
allowForcePush: false,
codeOwnerApprovalRequired: false,
diff --git a/spec/frontend/projects/settings/utils_spec.js b/spec/frontend/projects/settings/utils_spec.js
new file mode 100644
index 00000000000..319aa4000b5
--- /dev/null
+++ b/spec/frontend/projects/settings/utils_spec.js
@@ -0,0 +1,11 @@
+import { getAccessLevels } from '~/projects/settings/utils';
+import { pushAccessLevelsMockResponse, pushAccessLevelsMockResult } from './mock_data';
+
+describe('Utils', () => {
+ describe('getAccessLevels', () => {
+ it('takes accessLevels response data and returns acecssLevels object', () => {
+ const pushAccessLevels = getAccessLevels(pushAccessLevelsMockResponse);
+ expect(pushAccessLevels).toEqual(pushAccessLevelsMockResult);
+ });
+ });
+});
diff --git a/spec/frontend/releases/__snapshots__/util_spec.js.snap b/spec/frontend/releases/__snapshots__/util_spec.js.snap
index d88d79d2cde..00fc521b716 100644
--- a/spec/frontend/releases/__snapshots__/util_spec.js.snap
+++ b/spec/frontend/releases/__snapshots__/util_spec.js.snap
@@ -43,16 +43,17 @@ Object {
},
"author": Object {
"__typename": "UserCore",
- "avatarUrl": "https://www.gravatar.com/avatar/16f8e2050ce10180ca571c2eb19cfce2?s=80&d=identicon",
+ "avatarUrl": "https://www.gravatar.com/avatar/eb329fbfeccd9e6d45ff159da8736876?s=80&d=identicon",
"id": Any<String>,
- "username": "administrator",
- "webUrl": "http://localhost/administrator",
+ "username": "user1",
+ "webUrl": "http://localhost/user1",
},
"commit": Object {
"shortId": "b83d6e39",
"title": "Merge branch 'branch-merged' into 'master'",
},
"commitPath": "http://localhost/releases-namespace/releases-project/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0",
+ "createdAt": 2019-01-03T00:00:00.000Z,
"descriptionHtml": "<p data-sourcepos=\\"1:1-1:23\\" dir=\\"auto\\">An okay release <gl-emoji title=\\"shrug\\" data-name=\\"shrug\\" data-unicode-version=\\"9.0\\">🤷</gl-emoji></p>",
"evidences": Array [],
"historicalRelease": false,
@@ -140,16 +141,17 @@ Object {
},
"author": Object {
"__typename": "UserCore",
- "avatarUrl": "https://www.gravatar.com/avatar/16f8e2050ce10180ca571c2eb19cfce2?s=80&d=identicon",
+ "avatarUrl": "https://www.gravatar.com/avatar/eb329fbfeccd9e6d45ff159da8736876?s=80&d=identicon",
"id": Any<String>,
- "username": "administrator",
- "webUrl": "http://localhost/administrator",
+ "username": "user1",
+ "webUrl": "http://localhost/user1",
},
"commit": Object {
"shortId": "b83d6e39",
"title": "Merge branch 'branch-merged' into 'master'",
},
"commitPath": "http://localhost/releases-namespace/releases-project/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0",
+ "createdAt": 2018-12-03T00:00:00.000Z,
"descriptionHtml": "<p data-sourcepos=\\"1:1-1:33\\" dir=\\"auto\\">Best. Release. <strong>Ever.</strong> <gl-emoji title=\\"rocket\\" data-name=\\"rocket\\" data-unicode-version=\\"6.0\\">🚀</gl-emoji></p>",
"evidences": Array [
Object {
@@ -253,6 +255,7 @@ Object {
"sources": Array [],
},
"author": undefined,
+ "createdAt": 2018-12-03T00:00:00.000Z,
"description": "Best. Release. **Ever.** :rocket:",
"evidences": Array [],
"milestones": Array [
@@ -362,16 +365,17 @@ Object {
},
"author": Object {
"__typename": "UserCore",
- "avatarUrl": "https://www.gravatar.com/avatar/16f8e2050ce10180ca571c2eb19cfce2?s=80&d=identicon",
+ "avatarUrl": "https://www.gravatar.com/avatar/eb329fbfeccd9e6d45ff159da8736876?s=80&d=identicon",
"id": Any<String>,
- "username": "administrator",
- "webUrl": "http://localhost/administrator",
+ "username": "user1",
+ "webUrl": "http://localhost/user1",
},
"commit": Object {
"shortId": "b83d6e39",
"title": "Merge branch 'branch-merged' into 'master'",
},
"commitPath": "http://localhost/releases-namespace/releases-project/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0",
+ "createdAt": 2018-12-03T00:00:00.000Z,
"descriptionHtml": "<p data-sourcepos=\\"1:1-1:33\\" dir=\\"auto\\">Best. Release. <strong>Ever.</strong> <gl-emoji title=\\"rocket\\" data-name=\\"rocket\\" data-unicode-version=\\"6.0\\">🚀</gl-emoji></p>",
"evidences": Array [
Object {
diff --git a/spec/frontend/releases/components/release_block_footer_spec.js b/spec/frontend/releases/components/release_block_footer_spec.js
index 8f4efad197f..19b41d05a44 100644
--- a/spec/frontend/releases/components/release_block_footer_spec.js
+++ b/spec/frontend/releases/components/release_block_footer_spec.js
@@ -4,6 +4,7 @@ import { cloneDeep } from 'lodash';
import { nextTick } from 'vue';
import originalOneReleaseQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release.query.graphql.json';
import { convertOneReleaseGraphQLResponse } from '~/releases/util';
+import { RELEASED_AT_ASC, RELEASED_AT_DESC, CREATED_ASC, CREATED_DESC } from '~/releases/constants';
import { trimText } from 'helpers/text_helper';
import ReleaseBlockFooter from '~/releases/components/release_block_footer.vue';
@@ -43,88 +44,118 @@ describe('Release block footer', () => {
const tagInfoSectionLink = () => tagInfoSection().findComponent(GlLink);
const authorDateInfoSection = () => wrapper.find('.js-author-date-info');
- describe('with all props provided', () => {
- beforeEach(() => factory());
-
- it('renders the commit icon', () => {
- const commitIcon = commitInfoSection().findComponent(GlIcon);
-
- expect(commitIcon.exists()).toBe(true);
- expect(commitIcon.props('name')).toBe('commit');
- });
-
- it('renders the commit SHA with a link', () => {
- const commitLink = commitInfoSectionLink();
-
- expect(commitLink.exists()).toBe(true);
- expect(commitLink.text()).toBe(release.commit.shortId);
- expect(commitLink.attributes('href')).toBe(release.commitPath);
- });
-
- it('renders the tag icon', () => {
- const commitIcon = tagInfoSection().findComponent(GlIcon);
-
- expect(commitIcon.exists()).toBe(true);
- expect(commitIcon.props('name')).toBe('tag');
- });
-
- it('renders the tag name with a link', () => {
- const commitLink = tagInfoSection().findComponent(GlLink);
-
- expect(commitLink.exists()).toBe(true);
- expect(commitLink.text()).toBe(release.tagName);
- expect(commitLink.attributes('href')).toBe(release.tagPath);
- });
-
- it('renders the author and creation time info', () => {
- expect(trimText(authorDateInfoSection().text())).toBe(
- `Created 1 year ago by ${release.author.username}`,
- );
- });
-
- describe('when the release date is in the past', () => {
- it('prefixes the creation info with "Created"', () => {
- expect(trimText(authorDateInfoSection().text())).toEqual(expect.stringMatching(/^Created/));
- });
- });
-
- describe('renders the author and creation time info with future release date', () => {
- beforeEach(() => {
- factory({ releasedAt: mockFutureDate });
- });
-
- it('renders the release date without the author name', () => {
- expect(trimText(authorDateInfoSection().text())).toBe(
- `Will be created in 1 month by ${release.author.username}`,
- );
- });
- });
-
- describe('when the release date is in the future', () => {
- beforeEach(() => {
- factory({ releasedAt: mockFutureDate });
- });
-
- it('prefixes the creation info with "Will be created"', () => {
- expect(trimText(authorDateInfoSection().text())).toEqual(
- expect.stringMatching(/^Will be created/),
- );
- });
- });
-
- it("renders the author's avatar image", () => {
- const avatarImg = authorDateInfoSection().find('img');
-
- expect(avatarImg.exists()).toBe(true);
- expect(avatarImg.attributes('src')).toBe(release.author.avatarUrl);
- });
-
- it("renders a link to the author's profile", () => {
- const authorLink = authorDateInfoSection().findComponent(GlLink);
-
- expect(authorLink.exists()).toBe(true);
- expect(authorLink.attributes('href')).toBe(release.author.webUrl);
- });
+ describe.each`
+ sortFlag | expectedInfoString
+ ${null} | ${'Created'}
+ ${CREATED_ASC} | ${'Created'}
+ ${CREATED_DESC} | ${'Created'}
+ ${RELEASED_AT_ASC} | ${'Released'}
+ ${RELEASED_AT_DESC} | ${'Released'}
+ `('with sorting set to $sortFlag', ({ sortFlag, expectedInfoString }) => {
+ const dateAt =
+ expectedInfoString === 'Created' ? originalRelease.createdAt : originalRelease.releasedAt;
+
+ describe.each`
+ dateType | dateFlag | expectedInfoStringPrefix | expectedDateString
+ ${'empty'} | ${undefined} | ${null} | ${null}
+ ${'in the past'} | ${dateAt} | ${null} | ${'1 year ago'}
+ ${'in the future'} | ${mockFutureDate} | ${'Will be'} | ${'in 1 month'}
+ `(
+ 'with date set to $dateType',
+ ({ dateFlag, expectedInfoStringPrefix, expectedDateString }) => {
+ describe.each`
+ authorType | authorFlag | expectedAuthorString
+ ${'empty'} | ${undefined} | ${null}
+ ${'present'} | ${originalRelease.author} | ${'by user1'}
+ `('with author set to $authorType', ({ authorFlag, expectedAuthorString }) => {
+ const propsData = { sort: sortFlag, author: authorFlag };
+ if (dateFlag !== '') {
+ propsData.createdAt = dateFlag;
+ propsData.releasedAt = dateFlag;
+ }
+
+ beforeEach(() => {
+ factory({ ...propsData });
+ });
+
+ const expectedString = [
+ expectedInfoStringPrefix,
+ expectedInfoStringPrefix ? expectedInfoString.toLowerCase() : expectedInfoString,
+ expectedDateString,
+ expectedAuthorString,
+ ];
+
+ if (authorFlag || dateFlag) {
+ it('renders the author and creation time info', () => {
+ expect(trimText(authorDateInfoSection().text())).toBe(
+ expectedString.filter((n) => n).join(' '),
+ );
+ });
+ if (authorFlag) {
+ it("renders the author's avatar image", () => {
+ const avatarImg = authorDateInfoSection().find('img');
+
+ expect(avatarImg.exists()).toBe(true);
+ expect(avatarImg.attributes('src')).toBe(release.author.avatarUrl);
+ });
+
+ it("renders a link to the author's profile", () => {
+ const authorLink = authorDateInfoSection().findComponent(GlLink);
+
+ expect(authorLink.exists()).toBe(true);
+ expect(authorLink.attributes('href')).toBe(release.author.webUrl);
+ });
+ } else {
+ it("does not render the author's avatar image", () => {
+ const avatarImg = authorDateInfoSection().find('img');
+
+ expect(avatarImg.exists()).toBe(false);
+ });
+
+ it("does not render a link to the author's profile", () => {
+ const authorLink = authorDateInfoSection().findComponent(GlLink);
+
+ expect(authorLink.exists()).toBe(false);
+ });
+ }
+ } else {
+ it('does not render the author and creation time info', () => {
+ expect(authorDateInfoSection().exists()).toBe(false);
+ });
+ }
+
+ it('renders the commit icon', () => {
+ const commitIcon = commitInfoSection().findComponent(GlIcon);
+
+ expect(commitIcon.exists()).toBe(true);
+ expect(commitIcon.props('name')).toBe('commit');
+ });
+
+ it('renders the commit SHA with a link', () => {
+ const commitLink = commitInfoSectionLink();
+
+ expect(commitLink.exists()).toBe(true);
+ expect(commitLink.text()).toBe(release.commit.shortId);
+ expect(commitLink.attributes('href')).toBe(release.commitPath);
+ });
+
+ it('renders the tag icon', () => {
+ const commitIcon = tagInfoSection().findComponent(GlIcon);
+
+ expect(commitIcon.exists()).toBe(true);
+ expect(commitIcon.props('name')).toBe('tag');
+ });
+
+ it('renders the tag name with a link', () => {
+ const commitLink = tagInfoSection().findComponent(GlLink);
+
+ expect(commitLink.exists()).toBe(true);
+ expect(commitLink.text()).toBe(release.tagName);
+ expect(commitLink.attributes('href')).toBe(release.tagPath);
+ });
+ });
+ },
+ );
});
describe('without any commit info', () => {
@@ -160,40 +191,4 @@ describe('Release block footer', () => {
expect(tagInfoSection().text()).toBe(release.tagName);
});
});
-
- describe('without any author info', () => {
- beforeEach(() => factory({ author: undefined }));
-
- it('renders the release date without the author name', () => {
- expect(trimText(authorDateInfoSection().text())).toBe(`Created 1 year ago`);
- });
- });
-
- describe('future release without any author info', () => {
- beforeEach(() => {
- factory({ author: undefined, releasedAt: mockFutureDate });
- });
-
- it('renders the release date without the author name', () => {
- expect(trimText(authorDateInfoSection().text())).toBe(`Will be created in 1 month`);
- });
- });
-
- describe('without a released at date', () => {
- beforeEach(() => factory({ releasedAt: undefined }));
-
- it('renders the author name without the release date', () => {
- expect(trimText(authorDateInfoSection().text())).toBe(
- `Created by ${release.author.username}`,
- );
- });
- });
-
- describe('without a release date or author info', () => {
- beforeEach(() => factory({ author: undefined, releasedAt: undefined }));
-
- it('does not render any author or release date info', () => {
- expect(authorDateInfoSection().exists()).toBe(false);
- });
- });
});
diff --git a/spec/frontend/releases/components/release_block_spec.js b/spec/frontend/releases/components/release_block_spec.js
index 096c3db8902..f1b8554fbc3 100644
--- a/spec/frontend/releases/components/release_block_spec.js
+++ b/spec/frontend/releases/components/release_block_spec.js
@@ -1,5 +1,4 @@
import { mount } from '@vue/test-utils';
-import $ from 'jquery';
import { nextTick } from 'vue';
import originalOneReleaseQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release.query.graphql.json';
import { convertOneReleaseGraphQLResponse } from '~/releases/util';
@@ -10,6 +9,9 @@ import ReleaseBlock from '~/releases/components/release_block.vue';
import ReleaseBlockFooter from '~/releases/components/release_block_footer.vue';
import { BACK_URL_PARAM } from '~/releases/constants';
import timeagoMixin from '~/vue_shared/mixins/timeago';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
+
+jest.mock('~/behaviors/markdown/render_gfm');
describe('Release block', () => {
let wrapper;
@@ -34,7 +36,6 @@ describe('Release block', () => {
const editButton = () => wrapper.find('.js-edit-button');
beforeEach(() => {
- jest.spyOn($.fn, 'renderGFM');
release = convertOneReleaseGraphQLResponse(originalOneReleaseQueryResponse).data;
});
@@ -62,7 +63,7 @@ describe('Release block', () => {
it('renders release description', () => {
expect(wrapper.vm.$refs['gfm-content']).toBeDefined();
- expect($.fn.renderGFM).toHaveBeenCalledTimes(1);
+ expect(renderGFM).toHaveBeenCalledTimes(1);
});
it('renders release date', () => {
diff --git a/spec/frontend/reports/codequality_report/components/codequality_issue_body_spec.js b/spec/frontend/reports/codequality_report/components/codequality_issue_body_spec.js
deleted file mode 100644
index c32b52d9e77..00000000000
--- a/spec/frontend/reports/codequality_report/components/codequality_issue_body_spec.js
+++ /dev/null
@@ -1,102 +0,0 @@
-import { GlIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import component from '~/reports/codequality_report/components/codequality_issue_body.vue';
-import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '~/reports/constants';
-
-describe('code quality issue body issue body', () => {
- let wrapper;
-
- const findSeverityIcon = () => wrapper.findByTestId('codequality-severity-icon');
- const findGlIcon = () => wrapper.findComponent(GlIcon);
-
- const codequalityIssue = {
- name:
- 'rubygem-rest-client: session fixation vulnerability via Set-Cookie headers in 30x redirection responses',
- path: 'Gemfile.lock',
- severity: 'normal',
- type: 'Issue',
- urlPath: '/Gemfile.lock#L22',
- };
-
- const createComponent = (initialStatus, issue = codequalityIssue) => {
- wrapper = extendedWrapper(
- shallowMount(component, {
- propsData: {
- issue,
- status: initialStatus,
- },
- }),
- );
- };
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- describe('severity rating', () => {
- it.each`
- severity | iconClass | iconName
- ${'INFO'} | ${'text-primary-400'} | ${'severity-info'}
- ${'MINOR'} | ${'text-warning-200'} | ${'severity-low'}
- ${'CRITICAL'} | ${'text-danger-600'} | ${'severity-high'}
- ${'BLOCKER'} | ${'text-danger-800'} | ${'severity-critical'}
- ${'UNKNOWN'} | ${'text-secondary-400'} | ${'severity-unknown'}
- ${'INVALID'} | ${'text-secondary-400'} | ${'severity-unknown'}
- ${'info'} | ${'text-primary-400'} | ${'severity-info'}
- ${'minor'} | ${'text-warning-200'} | ${'severity-low'}
- ${'major'} | ${'text-warning-400'} | ${'severity-medium'}
- ${'critical'} | ${'text-danger-600'} | ${'severity-high'}
- ${'blocker'} | ${'text-danger-800'} | ${'severity-critical'}
- ${'unknown'} | ${'text-secondary-400'} | ${'severity-unknown'}
- ${'invalid'} | ${'text-secondary-400'} | ${'severity-unknown'}
- ${undefined} | ${'text-secondary-400'} | ${'severity-unknown'}
- `(
- 'renders correct icon for "$severity" severity rating',
- ({ severity, iconClass, iconName }) => {
- createComponent(STATUS_FAILED, {
- ...codequalityIssue,
- severity,
- });
- const icon = findGlIcon();
-
- expect(findSeverityIcon().classes()).toContain(iconClass);
- expect(icon.exists()).toBe(true);
- expect(icon.props('name')).toBe(iconName);
- },
- );
- });
-
- describe('with success', () => {
- it('renders fixed label', () => {
- createComponent(STATUS_SUCCESS);
-
- expect(wrapper.text()).toContain('Fixed');
- });
- });
-
- describe('without success', () => {
- it('does not render fixed label', () => {
- createComponent(STATUS_FAILED);
-
- expect(wrapper.text()).not.toContain('Fixed');
- });
- });
-
- describe('name', () => {
- it('renders name', () => {
- createComponent(STATUS_NEUTRAL);
-
- expect(wrapper.text()).toContain(codequalityIssue.name);
- });
- });
-
- describe('path', () => {
- it('renders the report-link path using the correct code quality issue', () => {
- createComponent(STATUS_NEUTRAL);
-
- expect(wrapper.find('report-link-stub').props('issue')).toBe(codequalityIssue);
- });
- });
-});
diff --git a/spec/frontend/reports/codequality_report/store/actions_spec.js b/spec/frontend/reports/codequality_report/store/actions_spec.js
deleted file mode 100644
index 1878b9f44b2..00000000000
--- a/spec/frontend/reports/codequality_report/store/actions_spec.js
+++ /dev/null
@@ -1,185 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import testAction from 'helpers/vuex_action_helper';
-import { TEST_HOST } from 'spec/test_constants';
-import axios from '~/lib/utils/axios_utils';
-import createStore from '~/reports/codequality_report/store';
-import * as actions from '~/reports/codequality_report/store/actions';
-import * as types from '~/reports/codequality_report/store/mutation_types';
-import { STATUS_NOT_FOUND } from '~/reports/constants';
-import { reportIssues, parsedReportIssues } from '../mock_data';
-
-const pollInterval = 123;
-const pollIntervalHeader = {
- 'Poll-Interval': pollInterval,
-};
-
-describe('Codequality Reports actions', () => {
- let localState;
- let localStore;
-
- beforeEach(() => {
- localStore = createStore();
- localState = localStore.state;
- });
-
- describe('setPaths', () => {
- it('should commit SET_PATHS mutation', () => {
- const paths = {
- baseBlobPath: 'baseBlobPath',
- headBlobPath: 'headBlobPath',
- reportsPath: 'reportsPath',
- };
-
- return testAction(
- actions.setPaths,
- paths,
- localState,
- [{ type: types.SET_PATHS, payload: paths }],
- [],
- );
- });
- });
-
- describe('fetchReports', () => {
- const endpoint = `${TEST_HOST}/codequality_reports.json`;
- let mock;
-
- beforeEach(() => {
- localState.reportsPath = endpoint;
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- describe('on success', () => {
- it('commits REQUEST_REPORTS and dispatches receiveReportsSuccess', () => {
- mock.onGet(endpoint).reply(200, reportIssues);
-
- return testAction(
- actions.fetchReports,
- null,
- localState,
- [{ type: types.REQUEST_REPORTS }],
- [
- {
- payload: parsedReportIssues,
- type: 'receiveReportsSuccess',
- },
- ],
- );
- });
- });
-
- describe('on error', () => {
- it('commits REQUEST_REPORTS and dispatches receiveReportsError', () => {
- mock.onGet(endpoint).reply(500);
-
- return testAction(
- actions.fetchReports,
- null,
- localState,
- [{ type: types.REQUEST_REPORTS }],
- [{ type: 'receiveReportsError', payload: expect.any(Error) }],
- );
- });
- });
-
- describe('when base report is not found', () => {
- it('commits REQUEST_REPORTS and dispatches receiveReportsError', () => {
- const data = { status: STATUS_NOT_FOUND };
- mock.onGet(`${TEST_HOST}/codequality_reports.json`).reply(200, data);
-
- return testAction(
- actions.fetchReports,
- null,
- localState,
- [{ type: types.REQUEST_REPORTS }],
- [{ type: 'receiveReportsError', payload: data }],
- );
- });
- });
-
- describe('while waiting for report results', () => {
- it('continues polling until it receives data', () => {
- mock
- .onGet(endpoint)
- .replyOnce(204, undefined, pollIntervalHeader)
- .onGet(endpoint)
- .reply(200, reportIssues);
-
- return Promise.all([
- testAction(
- actions.fetchReports,
- null,
- localState,
- [{ type: types.REQUEST_REPORTS }],
- [
- {
- payload: parsedReportIssues,
- type: 'receiveReportsSuccess',
- },
- ],
- ),
- axios
- // wait for initial NO_CONTENT response to be fulfilled
- .waitForAll()
- .then(() => {
- jest.advanceTimersByTime(pollInterval);
- }),
- ]);
- });
-
- it('continues polling until it receives an error', () => {
- mock
- .onGet(endpoint)
- .replyOnce(204, undefined, pollIntervalHeader)
- .onGet(endpoint)
- .reply(500);
-
- return Promise.all([
- testAction(
- actions.fetchReports,
- null,
- localState,
- [{ type: types.REQUEST_REPORTS }],
- [{ type: 'receiveReportsError', payload: expect.any(Error) }],
- ),
- axios
- // wait for initial NO_CONTENT response to be fulfilled
- .waitForAll()
- .then(() => {
- jest.advanceTimersByTime(pollInterval);
- }),
- ]);
- });
- });
- });
-
- describe('receiveReportsSuccess', () => {
- it('commits RECEIVE_REPORTS_SUCCESS', () => {
- const data = { issues: [] };
-
- return testAction(
- actions.receiveReportsSuccess,
- data,
- localState,
- [{ type: types.RECEIVE_REPORTS_SUCCESS, payload: data }],
- [],
- );
- });
- });
-
- describe('receiveReportsError', () => {
- it('commits RECEIVE_REPORTS_ERROR', () => {
- return testAction(
- actions.receiveReportsError,
- null,
- localState,
- [{ type: types.RECEIVE_REPORTS_ERROR, payload: null }],
- [],
- );
- });
- });
-});
diff --git a/spec/frontend/reports/codequality_report/store/getters_spec.js b/spec/frontend/reports/codequality_report/store/getters_spec.js
deleted file mode 100644
index 646903390ff..00000000000
--- a/spec/frontend/reports/codequality_report/store/getters_spec.js
+++ /dev/null
@@ -1,94 +0,0 @@
-import createStore from '~/reports/codequality_report/store';
-import * as getters from '~/reports/codequality_report/store/getters';
-import { LOADING, ERROR, SUCCESS, STATUS_NOT_FOUND } from '~/reports/constants';
-
-describe('Codequality reports store getters', () => {
- let localState;
- let localStore;
-
- beforeEach(() => {
- localStore = createStore();
- localState = localStore.state;
- });
-
- describe('hasCodequalityIssues', () => {
- describe('when there are issues', () => {
- it('returns true', () => {
- localState.newIssues = [{ reason: 'repetitive code' }];
- localState.resolvedIssues = [];
-
- expect(getters.hasCodequalityIssues(localState)).toEqual(true);
-
- localState.newIssues = [];
- localState.resolvedIssues = [{ reason: 'repetitive code' }];
-
- expect(getters.hasCodequalityIssues(localState)).toEqual(true);
- });
- });
-
- describe('when there are no issues', () => {
- it('returns false when there are no issues', () => {
- expect(getters.hasCodequalityIssues(localState)).toEqual(false);
- });
- });
- });
-
- describe('codequalityStatus', () => {
- describe('when loading', () => {
- it('returns loading status', () => {
- localState.isLoading = true;
-
- expect(getters.codequalityStatus(localState)).toEqual(LOADING);
- });
- });
-
- describe('on error', () => {
- it('returns error status', () => {
- localState.hasError = true;
-
- expect(getters.codequalityStatus(localState)).toEqual(ERROR);
- });
- });
-
- describe('when successfully loaded', () => {
- it('returns error status', () => {
- expect(getters.codequalityStatus(localState)).toEqual(SUCCESS);
- });
- });
- });
-
- describe('codequalityText', () => {
- it.each`
- resolvedIssues | newIssues | expectedText
- ${0} | ${0} | ${'No changes to code quality'}
- ${0} | ${1} | ${'Code quality degraded due to 1 new issue'}
- ${2} | ${0} | ${'Code quality improved due to 2 resolved issues'}
- ${1} | ${2} | ${'Code quality scanning detected 3 changes in merged results'}
- `(
- 'returns a summary containing $resolvedIssues resolved issues and $newIssues new issues',
- ({ newIssues, resolvedIssues, expectedText }) => {
- localState.newIssues = new Array(newIssues).fill({ reason: 'Repetitive code' });
- localState.resolvedIssues = new Array(resolvedIssues).fill({ reason: 'Repetitive code' });
-
- expect(getters.codequalityText(localState)).toEqual(expectedText);
- },
- );
- });
-
- describe('codequalityPopover', () => {
- describe('when base report is not available', () => {
- it('returns a popover with a documentation link', () => {
- localState.status = STATUS_NOT_FOUND;
- localState.helpPath = 'codequality_help.html';
-
- expect(getters.codequalityPopover(localState).title).toEqual(
- 'Base pipeline codequality artifact not found',
- );
- expect(getters.codequalityPopover(localState).content).toContain(
- 'Learn more about codequality reports',
- 'href="codequality_help.html"',
- );
- });
- });
- });
-});
diff --git a/spec/frontend/reports/codequality_report/store/mutations_spec.js b/spec/frontend/reports/codequality_report/store/mutations_spec.js
deleted file mode 100644
index 6e14cd7438b..00000000000
--- a/spec/frontend/reports/codequality_report/store/mutations_spec.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import createStore from '~/reports/codequality_report/store';
-import mutations from '~/reports/codequality_report/store/mutations';
-import { STATUS_NOT_FOUND } from '~/reports/constants';
-
-describe('Codequality Reports mutations', () => {
- let localState;
- let localStore;
-
- beforeEach(() => {
- localStore = createStore();
- localState = localStore.state;
- });
-
- describe('SET_PATHS', () => {
- it('sets paths to given values', () => {
- const baseBlobPath = 'base/blob/path/';
- const headBlobPath = 'head/blob/path/';
- const reportsPath = 'reports.json';
- const helpPath = 'help.html';
-
- mutations.SET_PATHS(localState, {
- baseBlobPath,
- headBlobPath,
- reportsPath,
- helpPath,
- });
-
- expect(localState.baseBlobPath).toEqual(baseBlobPath);
- expect(localState.headBlobPath).toEqual(headBlobPath);
- expect(localState.reportsPath).toEqual(reportsPath);
- expect(localState.helpPath).toEqual(helpPath);
- });
- });
-
- describe('REQUEST_REPORTS', () => {
- it('sets isLoading to true', () => {
- mutations.REQUEST_REPORTS(localState);
-
- expect(localState.isLoading).toEqual(true);
- });
- });
-
- describe('RECEIVE_REPORTS_SUCCESS', () => {
- it('sets isLoading to false', () => {
- mutations.RECEIVE_REPORTS_SUCCESS(localState, {});
-
- expect(localState.isLoading).toEqual(false);
- });
-
- it('sets hasError to false', () => {
- mutations.RECEIVE_REPORTS_SUCCESS(localState, {});
-
- expect(localState.hasError).toEqual(false);
- });
-
- it('clears status and statusReason', () => {
- mutations.RECEIVE_REPORTS_SUCCESS(localState, {});
-
- expect(localState.status).toEqual('');
- expect(localState.statusReason).toEqual('');
- });
-
- it('sets newIssues and resolvedIssues from response data', () => {
- const data = { newIssues: [{ id: 1 }], resolvedIssues: [{ id: 2 }] };
- mutations.RECEIVE_REPORTS_SUCCESS(localState, data);
-
- expect(localState.newIssues).toEqual(data.newIssues);
- expect(localState.resolvedIssues).toEqual(data.resolvedIssues);
- });
- });
-
- describe('RECEIVE_REPORTS_ERROR', () => {
- it('sets isLoading to false', () => {
- mutations.RECEIVE_REPORTS_ERROR(localState);
-
- expect(localState.isLoading).toEqual(false);
- });
-
- it('sets hasError to true', () => {
- mutations.RECEIVE_REPORTS_ERROR(localState);
-
- expect(localState.hasError).toEqual(true);
- });
-
- it('sets status based on error object', () => {
- const error = { status: STATUS_NOT_FOUND };
- mutations.RECEIVE_REPORTS_ERROR(localState, error);
-
- expect(localState.status).toEqual(error.status);
- });
-
- it('sets statusReason to string from error response data', () => {
- const data = { status_reason: 'This merge request does not have codequality reports' };
- const error = { response: { data } };
- mutations.RECEIVE_REPORTS_ERROR(localState, error);
-
- expect(localState.statusReason).toEqual(data.status_reason);
- });
- });
-});
diff --git a/spec/frontend/reports/codequality_report/store/utils/codequality_parser_spec.js b/spec/frontend/reports/codequality_report/store/utils/codequality_parser_spec.js
deleted file mode 100644
index 5b77a2c74be..00000000000
--- a/spec/frontend/reports/codequality_report/store/utils/codequality_parser_spec.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import { reportIssues, parsedReportIssues } from 'jest/reports/codequality_report/mock_data';
-import { parseCodeclimateMetrics } from '~/reports/codequality_report/store/utils/codequality_parser';
-
-describe('Codequality report store utils', () => {
- let result;
-
- describe('parseCodeclimateMetrics', () => {
- it('should parse the issues from backend codequality diff', () => {
- [result] = parseCodeclimateMetrics(reportIssues.new_errors, 'path');
-
- expect(result.name).toEqual(parsedReportIssues.newIssues[0].name);
- expect(result.path).toEqual(parsedReportIssues.newIssues[0].path);
- expect(result.line).toEqual(parsedReportIssues.newIssues[0].line);
- });
-
- describe('when an issue has no location or path', () => {
- const issue = { description: 'Insecure Dependency' };
-
- beforeEach(() => {
- [result] = parseCodeclimateMetrics([issue], 'path');
- });
-
- it('is parsed', () => {
- expect(result.name).toEqual(issue.description);
- });
- });
-
- describe('when an issue has a non-nested path', () => {
- const issue = { description: 'Insecure Dependency', path: 'Gemfile.lock' };
-
- beforeEach(() => {
- [result] = parseCodeclimateMetrics([issue], 'path');
- });
-
- it('is parsed', () => {
- expect(result.name).toEqual(issue.description);
- });
- });
-
- describe('when an issue has a path but no line', () => {
- const issue = { description: 'Insecure Dependency', location: { path: 'Gemfile.lock' } };
-
- beforeEach(() => {
- [result] = parseCodeclimateMetrics([issue], 'path');
- });
-
- it('is parsed', () => {
- expect(result.name).toEqual(issue.description);
- expect(result.path).toEqual(issue.location.path);
- expect(result.urlPath).toEqual(`path/${issue.location.path}`);
- });
- });
-
- describe('when an issue has a line nested in positions', () => {
- const issue = {
- description: 'Insecure Dependency',
- location: {
- path: 'Gemfile.lock',
- positions: { begin: { line: 84 } },
- },
- };
-
- beforeEach(() => {
- [result] = parseCodeclimateMetrics([issue], 'path');
- });
-
- it('is parsed', () => {
- expect(result.name).toEqual(issue.description);
- expect(result.path).toEqual(issue.location.path);
- expect(result.urlPath).toEqual(
- `path/${issue.location.path}#L${issue.location.positions.begin.line}`,
- );
- });
- });
-
- describe('with an empty issue array', () => {
- beforeEach(() => {
- result = parseCodeclimateMetrics([], 'path');
- });
-
- it('returns an empty array', () => {
- expect(result).toEqual([]);
- });
- });
- });
-});
diff --git a/spec/frontend/reports/components/grouped_issues_list_spec.js b/spec/frontend/reports/components/grouped_issues_list_spec.js
deleted file mode 100644
index cacbde590d6..00000000000
--- a/spec/frontend/reports/components/grouped_issues_list_spec.js
+++ /dev/null
@@ -1,87 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import GroupedIssuesList from '~/reports/components/grouped_issues_list.vue';
-import ReportItem from '~/reports/components/report_item.vue';
-import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
-
-describe('Grouped Issues List', () => {
- let wrapper;
-
- const createComponent = ({ propsData = {}, stubs = {} } = {}) => {
- wrapper = shallowMount(GroupedIssuesList, {
- propsData,
- stubs,
- });
- };
-
- const findHeading = (groupName) => wrapper.find(`[data-testid="${groupName}Heading"`);
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders a smart virtual list with the correct props', () => {
- createComponent({
- propsData: {
- resolvedIssues: [{ name: 'foo' }],
- unresolvedIssues: [{ name: 'bar' }],
- },
- stubs: {
- SmartVirtualList,
- },
- });
-
- expect(wrapper.findComponent(SmartVirtualList).props()).toMatchSnapshot();
- });
-
- describe('without data', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it.each(['unresolved', 'resolved'])('does not a render a header for %s issues', (issueName) => {
- expect(findHeading(issueName).exists()).toBe(false);
- });
-
- it.each(['resolved', 'unresolved'])('does not render report items for %s issues', () => {
- expect(wrapper.findComponent(ReportItem).exists()).toBe(false);
- });
- });
-
- describe('with data', () => {
- it.each`
- givenIssues | givenHeading | groupName
- ${[{ name: 'foo issue' }]} | ${'Foo Heading'} | ${'resolved'}
- ${[{ name: 'bar issue' }]} | ${'Bar Heading'} | ${'unresolved'}
- `('renders the heading for $groupName issues', ({ givenIssues, givenHeading, groupName }) => {
- createComponent({
- propsData: { [`${groupName}Issues`]: givenIssues, [`${groupName}Heading`]: givenHeading },
- });
-
- expect(findHeading(groupName).text()).toBe(givenHeading);
- });
-
- it.each(['resolved', 'unresolved'])('renders all %s issues', (issueName) => {
- const issues = [{ name: 'foo' }, { name: 'bar' }];
-
- createComponent({
- propsData: { [`${issueName}Issues`]: issues },
- });
-
- expect(wrapper.findAllComponents(ReportItem)).toHaveLength(issues.length);
- });
-
- it('renders a report item with the correct props', () => {
- createComponent({
- propsData: {
- resolvedIssues: [{ name: 'foo' }],
- component: 'CodequalityIssueBody',
- },
- stubs: {
- ReportItem,
- },
- });
-
- expect(wrapper.findComponent(ReportItem).props()).toMatchSnapshot();
- });
- });
-});
diff --git a/spec/frontend/reports/components/issue_status_icon_spec.js b/spec/frontend/reports/components/issue_status_icon_spec.js
deleted file mode 100644
index 8706f2f8d83..00000000000
--- a/spec/frontend/reports/components/issue_status_icon_spec.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import ReportItem from '~/reports/components/issue_status_icon.vue';
-import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '~/reports/constants';
-
-describe('IssueStatusIcon', () => {
- let wrapper;
-
- const createComponent = ({ status }) => {
- wrapper = shallowMount(ReportItem, {
- propsData: {
- status,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- it.each([STATUS_SUCCESS, STATUS_NEUTRAL, STATUS_FAILED])(
- 'renders "%s" state correctly',
- (status) => {
- createComponent({ status });
-
- expect(wrapper.element).toMatchSnapshot();
- },
- );
-});
diff --git a/spec/frontend/reports/components/report_item_spec.js b/spec/frontend/reports/components/report_item_spec.js
deleted file mode 100644
index 60c7e5f2b44..00000000000
--- a/spec/frontend/reports/components/report_item_spec.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { componentNames } from '~/reports/components/issue_body';
-import IssueStatusIcon from '~/reports/components/issue_status_icon.vue';
-import ReportItem from '~/reports/components/report_item.vue';
-import { STATUS_SUCCESS } from '~/reports/constants';
-
-describe('ReportItem', () => {
- describe('showReportSectionStatusIcon', () => {
- it('does not render CI Status Icon when showReportSectionStatusIcon is false', () => {
- const wrapper = shallowMount(ReportItem, {
- propsData: {
- issue: { foo: 'bar' },
- component: componentNames.CodequalityIssueBody,
- status: STATUS_SUCCESS,
- showReportSectionStatusIcon: false,
- },
- });
-
- expect(wrapper.findComponent(IssueStatusIcon).exists()).toBe(false);
- });
-
- it('shows status icon when unspecified', () => {
- const wrapper = shallowMount(ReportItem, {
- propsData: {
- issue: { foo: 'bar' },
- component: componentNames.CodequalityIssueBody,
- status: STATUS_SUCCESS,
- },
- });
-
- expect(wrapper.findComponent(IssueStatusIcon).exists()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/reports/components/report_link_spec.js b/spec/frontend/reports/components/report_link_spec.js
deleted file mode 100644
index 2ed0617a598..00000000000
--- a/spec/frontend/reports/components/report_link_spec.js
+++ /dev/null
@@ -1,56 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import ReportLink from '~/reports/components/report_link.vue';
-
-describe('app/assets/javascripts/reports/components/report_link.vue', () => {
- let wrapper;
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const defaultProps = {
- issue: {},
- };
-
- const createComponent = (props = {}) => {
- wrapper = shallowMount(ReportLink, {
- propsData: { ...defaultProps, ...props },
- });
- };
-
- describe('When an issue prop has a $urlPath property', () => {
- it('render a link that will take the user to the $urlPath', () => {
- createComponent({ issue: { path: 'Gemfile.lock', urlPath: '/Gemfile.lock' } });
-
- expect(wrapper.text()).toContain('in');
- expect(wrapper.find('a').attributes('href')).toBe('/Gemfile.lock');
- expect(wrapper.find('a').text()).toContain('Gemfile.lock');
- });
- });
-
- describe('When an issue prop has no $urlPath property', () => {
- it('does not render link', () => {
- createComponent({ issue: { path: 'Gemfile.lock' } });
-
- expect(wrapper.find('a').exists()).toBe(false);
- expect(wrapper.text()).toContain('in');
- expect(wrapper.text()).toContain('Gemfile.lock');
- });
- });
-
- describe('When an issue prop has a $line property', () => {
- it('render a line number', () => {
- createComponent({ issue: { path: 'Gemfile.lock', urlPath: '/Gemfile.lock', line: 22 } });
-
- expect(wrapper.find('a').text()).toContain('Gemfile.lock:22');
- });
- });
-
- describe('When an issue prop does not have a $line property', () => {
- it('does not render a line number', () => {
- createComponent({ issue: { urlPath: '/Gemfile.lock' } });
-
- expect(wrapper.find('a').text()).not.toContain(':22');
- });
- });
-});
diff --git a/spec/frontend/reports/components/report_section_spec.js b/spec/frontend/reports/components/report_section_spec.js
deleted file mode 100644
index cc35b99a199..00000000000
--- a/spec/frontend/reports/components/report_section_spec.js
+++ /dev/null
@@ -1,285 +0,0 @@
-import { GlButton } from '@gitlab/ui';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import HelpPopover from '~/vue_shared/components/help_popover.vue';
-import ReportItem from '~/reports/components/report_item.vue';
-import ReportSection from '~/reports/components/report_section.vue';
-
-describe('ReportSection component', () => {
- let wrapper;
-
- const findExpandButton = () => wrapper.findComponent(GlButton);
- const findPopover = () => wrapper.findComponent(HelpPopover);
- const findReportSection = () => wrapper.find('.js-report-section-container');
- const expectExpandButtonOpen = () =>
- expect(findExpandButton().props('icon')).toBe('chevron-lg-up');
- const expectExpandButtonClosed = () =>
- expect(findExpandButton().props('icon')).toBe('chevron-lg-down');
-
- const resolvedIssues = [
- {
- name: 'Insecure Dependency',
- fingerprint: 'ca2e59451e98ae60ba2f54e3857c50e5',
- path: 'Gemfile.lock',
- line: 12,
- urlPath: 'foo/Gemfile.lock',
- },
- ];
-
- const defaultProps = {
- component: '',
- status: 'SUCCESS',
- loadingText: 'Loading Code Quality report',
- errorText: 'foo',
- successText: 'Code quality improved on 1 point and degraded on 1 point',
- resolvedIssues,
- hasIssues: false,
- alwaysOpen: false,
- };
-
- const createComponent = ({ props = {}, data = {}, slots = {} } = {}) => {
- wrapper = mountExtended(ReportSection, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- data() {
- return data;
- },
- slots,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('computed', () => {
- describe('isCollapsible', () => {
- const testMatrix = [
- { hasIssues: false, alwaysOpen: false, isCollapsible: false },
- { hasIssues: false, alwaysOpen: true, isCollapsible: false },
- { hasIssues: true, alwaysOpen: false, isCollapsible: true },
- { hasIssues: true, alwaysOpen: true, isCollapsible: false },
- ];
-
- testMatrix.forEach(({ hasIssues, alwaysOpen, isCollapsible }) => {
- const issues = hasIssues ? 'has issues' : 'has no issues';
- const open = alwaysOpen ? 'is always open' : 'is not always open';
-
- it(`is ${isCollapsible}, if the report ${issues} and ${open}`, () => {
- createComponent({ props: { hasIssues, alwaysOpen } });
-
- expect(wrapper.vm.isCollapsible).toBe(isCollapsible);
- });
- });
- });
-
- describe('isExpanded', () => {
- const testMatrix = [
- { isCollapsed: false, alwaysOpen: false, isExpanded: true },
- { isCollapsed: false, alwaysOpen: true, isExpanded: true },
- { isCollapsed: true, alwaysOpen: false, isExpanded: false },
- { isCollapsed: true, alwaysOpen: true, isExpanded: true },
- ];
-
- testMatrix.forEach(({ isCollapsed, alwaysOpen, isExpanded }) => {
- const issues = isCollapsed ? 'is collapsed' : 'is not collapsed';
- const open = alwaysOpen ? 'is always open' : 'is not always open';
-
- it(`is ${isExpanded}, if the report ${issues} and ${open}`, () => {
- createComponent({ props: { alwaysOpen }, data: { isCollapsed } });
-
- expect(wrapper.vm.isExpanded).toBe(isExpanded);
- });
- });
- });
- });
-
- describe('when it is loading', () => {
- it('should render loading indicator', () => {
- createComponent({
- props: {
- component: '',
- status: 'LOADING',
- loadingText: 'Loading Code Quality report',
- errorText: 'foo',
- successText: 'Code quality improved on 1 point and degraded on 1 point',
- hasIssues: false,
- },
- });
-
- expect(wrapper.text()).toBe('Loading Code Quality report');
- });
- });
-
- describe('with success status', () => {
- it('should render provided data', () => {
- createComponent({ props: { hasIssues: true } });
-
- expect(wrapper.find('.js-code-text').text()).toBe(
- 'Code quality improved on 1 point and degraded on 1 point',
- );
- expect(wrapper.findAllComponents(ReportItem)).toHaveLength(resolvedIssues.length);
- });
-
- describe('toggleCollapsed', () => {
- it('toggles issues', async () => {
- createComponent({ props: { hasIssues: true } });
-
- await findExpandButton().trigger('click');
-
- expect(findReportSection().isVisible()).toBe(true);
- expectExpandButtonOpen();
-
- await findExpandButton().trigger('click');
-
- expect(findReportSection().isVisible()).toBe(false);
- expectExpandButtonClosed();
- });
-
- it('is always expanded, if always-open is set to true', () => {
- createComponent({ props: { hasIssues: true, alwaysOpen: true } });
-
- expect(findReportSection().isVisible()).toBe(true);
- expect(findExpandButton().exists()).toBe(false);
- });
- });
- });
-
- describe('snowplow events', () => {
- it('does emit an event on issue toggle if the shouldEmitToggleEvent prop does exist', () => {
- createComponent({ props: { hasIssues: true, shouldEmitToggleEvent: true } });
-
- expect(wrapper.emitted('toggleEvent')).toBeUndefined();
-
- findExpandButton().trigger('click');
-
- expect(wrapper.emitted('toggleEvent')).toEqual([[]]);
- });
-
- it('does not emit an event on issue toggle if the shouldEmitToggleEvent prop does not exist', () => {
- createComponent({ props: { hasIssues: true } });
-
- expect(wrapper.emitted('toggleEvent')).toBeUndefined();
-
- findExpandButton().trigger('click');
-
- expect(wrapper.emitted('toggleEvent')).toBeUndefined();
- });
-
- it('does not emit an event if always-open is set to true', () => {
- createComponent({
- props: { alwaysOpen: true, hasIssues: true, shouldEmitToggleEvent: true },
- });
-
- expect(wrapper.emitted('toggleEvent')).toBeUndefined();
- });
- });
-
- describe('with failed request', () => {
- it('should render error indicator', () => {
- createComponent({
- props: {
- component: '',
- status: 'ERROR',
- loadingText: 'Loading Code Quality report',
- errorText: 'Failed to load Code Quality report',
- successText: 'Code quality improved on 1 point and degraded on 1 point',
- hasIssues: false,
- },
- });
-
- expect(wrapper.text()).toBe('Failed to load Code Quality report');
- });
- });
-
- describe('with action buttons passed to the slot', () => {
- beforeEach(() => {
- createComponent({
- props: {
- status: 'SUCCESS',
- successText: 'success',
- hasIssues: true,
- },
- slots: {
- 'action-buttons': ['Action!'],
- },
- });
- });
-
- it('should render the passed button', () => {
- expect(wrapper.text()).toContain('Action!');
- });
-
- it('should still render the expand/collapse button', () => {
- expectExpandButtonClosed();
- });
- });
-
- describe('Success and Error slots', () => {
- const createComponentWithSlots = (status) => {
- createComponent({
- props: {
- status,
- hasIssues: true,
- },
- slots: {
- success: ['This is a success'],
- loading: ['This is loading'],
- error: ['This is an error'],
- },
- });
- };
-
- it('only renders success slot when status is "SUCCESS"', () => {
- createComponentWithSlots('SUCCESS');
-
- expect(wrapper.text()).toContain('This is a success');
- expect(wrapper.text()).not.toContain('This is an error');
- expect(wrapper.text()).not.toContain('This is loading');
- });
-
- it('only renders error slot when status is "ERROR"', () => {
- createComponentWithSlots('ERROR');
-
- expect(wrapper.text()).toContain('This is an error');
- expect(wrapper.text()).not.toContain('This is a success');
- expect(wrapper.text()).not.toContain('This is loading');
- });
-
- it('only renders loading slot when status is "LOADING"', () => {
- createComponentWithSlots('LOADING');
-
- expect(wrapper.text()).toContain('This is loading');
- expect(wrapper.text()).not.toContain('This is an error');
- expect(wrapper.text()).not.toContain('This is a success');
- });
- });
-
- describe('help popover', () => {
- describe('when popover options are defined', () => {
- const options = {
- title: 'foo',
- content: 'bar',
- };
-
- beforeEach(() => {
- createComponent({ props: { popoverOptions: options } });
- });
-
- it('popover is shown with options', () => {
- expect(findPopover().props('options')).toEqual(options);
- });
- });
-
- describe('when popover options are not defined', () => {
- beforeEach(() => {
- createComponent({ props: { popoverOptions: {} } });
- });
-
- it('popover is not shown', () => {
- expect(findPopover().exists()).toBe(false);
- });
- });
- });
-});
diff --git a/spec/frontend/reports/components/summary_row_spec.js b/spec/frontend/reports/components/summary_row_spec.js
deleted file mode 100644
index 778660d9e44..00000000000
--- a/spec/frontend/reports/components/summary_row_spec.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import { mount } from '@vue/test-utils';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import HelpPopover from '~/vue_shared/components/help_popover.vue';
-import SummaryRow from '~/reports/components/summary_row.vue';
-
-describe('Summary row', () => {
- let wrapper;
-
- const summary = 'SAST detected 1 new vulnerability and 1 fixed vulnerability';
- const popoverOptions = {
- title: 'Static Application Security Testing (SAST)',
- content: '<a>Learn more about SAST</a>',
- };
- const statusIcon = 'warning';
-
- const createComponent = ({ props = {}, slots = {} } = {}) => {
- wrapper = extendedWrapper(
- mount(SummaryRow, {
- propsData: {
- summary,
- popoverOptions,
- statusIcon,
- ...props,
- },
- slots,
- }),
- );
- };
-
- const findSummary = () => wrapper.findByTestId('summary-row-description');
- const findStatusIcon = () => wrapper.findByTestId('summary-row-icon');
- const findHelpPopover = () => wrapper.findComponent(HelpPopover);
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- it('renders provided summary', () => {
- createComponent();
- expect(findSummary().text()).toContain(summary);
- });
-
- it('renders provided icon', () => {
- createComponent();
- expect(findStatusIcon().classes()).toContain('js-ci-status-icon-warning');
- });
-
- it('renders help popover if popoverOptions are provided', () => {
- createComponent();
- expect(findHelpPopover().props('options')).toEqual(popoverOptions);
- });
-
- it('does not render help popover if popoverOptions are not provided', () => {
- createComponent({ props: { popoverOptions: null } });
- expect(findHelpPopover().exists()).toBe(false);
- });
-
- describe('summary slot', () => {
- it('replaces the summary prop', () => {
- const summarySlotContent = 'Summary slot content';
- createComponent({ slots: { summary: summarySlotContent } });
-
- expect(wrapper.text()).not.toContain(summary);
- expect(findSummary().text()).toContain(summarySlotContent);
- });
- });
-});
diff --git a/spec/frontend/reports/mock_data/new_and_fixed_failures_report.json b/spec/frontend/reports/mock_data/new_and_fixed_failures_report.json
deleted file mode 100644
index 6141e5433a6..00000000000
--- a/spec/frontend/reports/mock_data/new_and_fixed_failures_report.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
- "status": "failed",
- "summary": { "total": 11, "resolved": 2, "errored": 0, "failed": 2 },
- "suites": [
- {
- "name": "rspec:pg",
- "status": "failed",
- "summary": { "total": 8, "resolved": 2, "errored": 0, "failed": 1 },
- "new_failures": [
- {
- "status": "failed",
- "name": "Test#subtract when a is 2 and b is 1 returns correct result",
- "execution_time": 0.00908,
- "system_output": "Failure/Error: is_expected.to eq(1)\n\n expected: 1\n got: 3\n\n (compared using ==)\n./spec/test_spec.rb:43:in `block (4 levels) in <top (required)>'"
- }
- ],
- "resolved_failures": [
- {
- "status": "success",
- "name": "Test#sum when a is 1 and b is 2 returns summary",
- "execution_time": 0.000318,
- "system_output": null
- },
- {
- "status": "success",
- "name": "Test#sum when a is 100 and b is 200 returns summary",
- "execution_time": 0.000074,
- "system_output": null
- }
- ],
- "existing_failures": [],
- "new_errors": [],
- "resolved_errors": [],
- "existing_errors": []
- },
- {
- "name": "java ant",
- "status": "failed",
- "summary": { "total": 3, "resolved": 0, "errored": 0, "failed": 1 },
- "new_failures": [],
- "resolved_failures": [],
- "existing_failures": [
- {
- "status": "failed",
- "name": "sumTest",
- "execution_time": 0.004,
- "system_output": "junit.framework.AssertionFailedError: expected:<3> but was:<-1>\n\tat CalculatorTest.sumTest(Unknown Source)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n"
- }
- ],
- "new_errors": [],
- "resolved_errors": [],
- "existing_errors": []
- }
- ]
-}
diff --git a/spec/frontend/reports/mock_data/new_errors_report.json b/spec/frontend/reports/mock_data/new_errors_report.json
deleted file mode 100644
index 6573d23ee50..00000000000
--- a/spec/frontend/reports/mock_data/new_errors_report.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
- "summary": { "total": 11, "resolved": 0, "errored": 2, "failed": 0 },
- "suites": [
- {
- "name": "karma",
- "summary": { "total": 3, "resolved": 0, "errored": 2, "failed": 0 },
- "new_failures": [],
- "resolved_failures": [],
- "existing_failures": [],
- "new_errors": [
- {
- "result": "error",
- "name": "Test#sum when a is 1 and b is 2 returns summary",
- "execution_time": 0.009411,
- "system_output": "Failed: Error in render: 'TypeError: Cannot read property 'status' of undefined'"
- },
- {
- "result": "error",
- "name": "Test#sum when a is 100 and b is 200 returns summary",
- "execution_time": 0.000162,
- "system_output": "Failed: Error in render: 'TypeError: Cannot read property 'length' of undefined'"
- }
- ],
- "resolved_errors": [],
- "existing_errors": []
- },
- {
- "name": "rspec:pg",
- "summary": { "total": 8, "resolved": 0, "errored": 0, "failed": 0 },
- "new_failures": [],
- "resolved_failures": [],
- "existing_failures": [],
- "new_errors": [],
- "resolved_errors": [],
- "existing_errors": []
- }
- ]
-}
diff --git a/spec/frontend/reports/mock_data/new_failures_report.json b/spec/frontend/reports/mock_data/new_failures_report.json
deleted file mode 100644
index 438f7c82788..00000000000
--- a/spec/frontend/reports/mock_data/new_failures_report.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "summary": { "total": 11, "resolved": 0, "errored": 0, "failed": 2 },
- "suites": [
- {
- "name": "rspec:pg",
- "summary": { "total": 8, "resolved": 0, "errored": 0, "failed": 2 },
- "new_failures": [
- {
- "result": "failure",
- "name": "Test#sum when a is 1 and b is 2 returns summary",
- "file": "spec/file_1.rb",
- "execution_time": 0.009411,
- "system_output": "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in <top (required)>'"
- },
- {
- "result": "failure",
- "name": "Test#sum when a is 100 and b is 200 returns summary",
- "file": "spec/file_2.rb",
- "execution_time": 0.000162,
- "system_output": "Failure/Error: is_expected.to eq(300)\n\n expected: 300\n got: -100\n\n (compared using ==)\n./spec/test_spec.rb:21:in `block (4 levels) in <top (required)>'"
- }
- ],
- "resolved_failures": [],
- "existing_failures": [],
- "new_errors": [],
- "resolved_errors": [],
- "existing_errors": []
- },
- {
- "name": "java ant",
- "summary": { "total": 3, "resolved": 0, "errored": 0, "failed": 0 },
- "new_failures": [],
- "resolved_failures": [],
- "existing_failures": [],
- "new_errors": [],
- "resolved_errors": [],
- "existing_errors": []
- }
- ]
-}
diff --git a/spec/frontend/reports/mock_data/new_failures_with_null_files_report.json b/spec/frontend/reports/mock_data/new_failures_with_null_files_report.json
deleted file mode 100644
index 28ee7d194b9..00000000000
--- a/spec/frontend/reports/mock_data/new_failures_with_null_files_report.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "summary": { "total": 11, "resolved": 0, "errored": 0, "failed": 2 },
- "suites": [
- {
- "name": "rspec:pg",
- "summary": { "total": 8, "resolved": 0, "errored": 0, "failed": 2 },
- "new_failures": [
- {
- "result": "failure",
- "name": "Test#sum when a is 1 and b is 2 returns summary",
- "file": null,
- "execution_time": 0.009411,
- "system_output": "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in <top (required)>'"
- },
- {
- "result": "failure",
- "name": "Test#sum when a is 100 and b is 200 returns summary",
- "file": null,
- "execution_time": 0.000162,
- "system_output": "Failure/Error: is_expected.to eq(300)\n\n expected: 300\n got: -100\n\n (compared using ==)\n./spec/test_spec.rb:21:in `block (4 levels) in <top (required)>'"
- }
- ],
- "resolved_failures": [],
- "existing_failures": [],
- "new_errors": [],
- "resolved_errors": [],
- "existing_errors": []
- },
- {
- "name": "java ant",
- "summary": { "total": 3, "resolved": 0, "errored": 0, "failed": 0 },
- "new_failures": [],
- "resolved_failures": [],
- "existing_failures": [],
- "new_errors": [],
- "resolved_errors": [],
- "existing_errors": []
- }
- ]
-}
diff --git a/spec/frontend/reports/mock_data/no_failures_report.json b/spec/frontend/reports/mock_data/no_failures_report.json
deleted file mode 100644
index 7da9e0c6211..00000000000
--- a/spec/frontend/reports/mock_data/no_failures_report.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "status": "success",
- "summary": { "total": 11, "resolved": 0, "errored": 0, "failed": 0 },
- "suites": [
- {
- "name": "rspec:pg",
- "status": "success",
- "summary": { "total": 8, "resolved": 0, "errored": 0, "failed": 0 },
- "new_failures": [],
- "resolved_failures": [],
- "existing_failures": [],
- "new_errors": [],
- "resolved_errors": [],
- "existing_errors": []
- },
- {
- "name": "java ant",
- "status": "success",
- "summary": { "total": 3, "resolved": 0, "errored": 0, "failed": 0 },
- "new_failures": [],
- "resolved_failures": [],
- "existing_failures": [],
- "new_errors": [],
- "resolved_errors": [],
- "existing_errors": []
- }
- ]
-}
diff --git a/spec/frontend/reports/mock_data/recent_failures_report.json b/spec/frontend/reports/mock_data/recent_failures_report.json
deleted file mode 100644
index c4a5fb78dcd..00000000000
--- a/spec/frontend/reports/mock_data/recent_failures_report.json
+++ /dev/null
@@ -1,52 +0,0 @@
-{
- "summary": { "total": 11, "resolved": 0, "errored": 0, "failed": 3, "recentlyFailed": 2 },
- "suites": [
- {
- "name": "rspec:pg",
- "summary": { "total": 8, "resolved": 0, "errored": 0, "failed": 2, "recentlyFailed": 1 },
- "new_failures": [
- {
- "result": "failure",
- "name": "Test#sum when a is 1 and b is 2 returns summary",
- "execution_time": 0.009411,
- "system_output": "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in <top (required)>'",
- "recent_failures": {
- "count": 8,
- "base_branch": "main"
- }
- },
- {
- "result": "failure",
- "name": "Test#sum when a is 100 and b is 200 returns summary",
- "execution_time": 0.000162,
- "system_output": "Failure/Error: is_expected.to eq(300)\n\n expected: 300\n got: -100\n\n (compared using ==)\n./spec/test_spec.rb:21:in `block (4 levels) in <top (required)>'"
- }
- ],
- "resolved_failures": [],
- "existing_failures": [],
- "new_errors": [],
- "resolved_errors": [],
- "existing_errors": []
- },
- {
- "name": "java ant",
- "summary": { "total": 3, "resolved": 0, "errored": 0, "failed": 1, "recentlyFailed": 1 },
- "new_failures": [
- {
- "result": "failure",
- "name": "Test#sum when a is 100 and b is 200 returns summary",
- "execution_time": 0.000562,
- "recent_failures": {
- "count": 3,
- "base_branch": "main"
- }
- }
- ],
- "resolved_failures": [],
- "existing_failures": [],
- "new_errors": [],
- "resolved_errors": [],
- "existing_errors": []
- }
- ]
-}
diff --git a/spec/frontend/reports/mock_data/resolved_failures.json b/spec/frontend/reports/mock_data/resolved_failures.json
deleted file mode 100644
index 49de6aa840b..00000000000
--- a/spec/frontend/reports/mock_data/resolved_failures.json
+++ /dev/null
@@ -1,58 +0,0 @@
-{
- "status": "success",
- "summary": { "total": 11, "resolved": 4, "errored": 0, "failed": 0 },
- "suites": [
- {
- "name": "rspec:pg",
- "status": "success",
- "summary": { "total": 8, "resolved": 4, "errored": 0, "failed": 0 },
- "new_failures": [],
- "resolved_failures": [
- {
- "status": "success",
- "name": "Test#sum when a is 1 and b is 2 returns summary",
- "execution_time": 0.000411,
- "system_output": null,
- "stack_trace": null
- },
- {
- "status": "success",
- "name": "Test#sum when a is 100 and b is 200 returns summary",
- "execution_time": 7.6e-5,
- "system_output": null,
- "stack_trace": null
- }
- ],
- "existing_failures": [],
- "new_errors": [],
- "resolved_errors": [
- {
- "status": "success",
- "name": "Test#sum when a is 4 and b is 4 returns summary",
- "execution_time": 0.00342,
- "system_output": null,
- "stack_trace": null
- },
- {
- "status": "success",
- "name": "Test#sum when a is 40 and b is 400 returns summary",
- "execution_time": 0.0000231,
- "system_output": null,
- "stack_trace": null
- }
- ],
- "existing_errors": []
- },
- {
- "name": "java ant",
- "status": "success",
- "summary": { "total": 3, "resolved": 0, "errored": 0, "failed": 0 },
- "new_failures": [],
- "resolved_failures": [],
- "existing_failures": [],
- "new_errors": [],
- "resolved_errors": [],
- "existing_errors": []
- }
- ]
-}
diff --git a/spec/frontend/repository/components/table/index_spec.js b/spec/frontend/repository/components/table/index_spec.js
index 2180f78a8df..8b987551b33 100644
--- a/spec/frontend/repository/components/table/index_spec.js
+++ b/spec/frontend/repository/components/table/index_spec.js
@@ -82,9 +82,6 @@ function factory({ path, isLoading = false, hasMore = true, entries = {}, commit
mocks: {
$apollo,
},
- provide: {
- glFeatures: { lazyLoadCommits: true },
- },
});
}
diff --git a/spec/frontend/repository/components/table/row_spec.js b/spec/frontend/repository/components/table/row_spec.js
index 64aa6d179a8..5d9138ab9cd 100644
--- a/spec/frontend/repository/components/table/row_spec.js
+++ b/spec/frontend/repository/components/table/row_spec.js
@@ -30,9 +30,6 @@ function factory(propsData = {}) {
directives: {
GlHoverLoad: createMockDirective(),
},
- provide: {
- glFeatures: { lazyLoadCommits: true },
- },
mocks: {
$router,
},
diff --git a/spec/frontend/repository/components/tree_content_spec.js b/spec/frontend/repository/components/tree_content_spec.js
index 352f4314232..6eea66f1a7d 100644
--- a/spec/frontend/repository/components/tree_content_spec.js
+++ b/spec/frontend/repository/components/tree_content_spec.js
@@ -31,7 +31,6 @@ function factory(path, data = () => ({})) {
glFeatures: {
increasePageSizeExponentially: true,
paginatedTreeGraphqlQuery: true,
- lazyLoadCommits: true,
},
},
});
diff --git a/spec/frontend/repository/utils/ref_switcher_utils_spec.js b/spec/frontend/repository/utils/ref_switcher_utils_spec.js
new file mode 100644
index 00000000000..3335059554f
--- /dev/null
+++ b/spec/frontend/repository/utils/ref_switcher_utils_spec.js
@@ -0,0 +1,22 @@
+import { generateRefDestinationPath } from '~/repository/utils/ref_switcher_utils';
+import setWindowLocation from 'helpers/set_window_location_helper';
+
+const projectRootPath = 'root/Project1';
+const currentRef = 'main';
+const selectedRef = 'feature';
+
+describe('generateRefDestinationPath', () => {
+ it.each`
+ currentPath | result
+ ${projectRootPath} | ${`${projectRootPath}/-/tree/${selectedRef}`}
+ ${`${projectRootPath}/-/tree/${currentRef}/dir1`} | ${`${projectRootPath}/-/tree/${selectedRef}/dir1`}
+ ${`${projectRootPath}/-/tree/${currentRef}/dir1/dir2`} | ${`${projectRootPath}/-/tree/${selectedRef}/dir1/dir2`}
+ ${`${projectRootPath}/-/blob/${currentRef}/test.js`} | ${`${projectRootPath}/-/blob/${selectedRef}/test.js`}
+ ${`${projectRootPath}/-/blob/${currentRef}/dir1/test.js`} | ${`${projectRootPath}/-/blob/${selectedRef}/dir1/test.js`}
+ ${`${projectRootPath}/-/blob/${currentRef}/dir1/dir2/test.js`} | ${`${projectRootPath}/-/blob/${selectedRef}/dir1/dir2/test.js`}
+ ${`${projectRootPath}/-/blob/${currentRef}/dir1/dir2/test.js#L123`} | ${`${projectRootPath}/-/blob/${selectedRef}/dir1/dir2/test.js#L123`}
+ `('generates the correct destination path for $currentPath', ({ currentPath, result }) => {
+ setWindowLocation(currentPath);
+ expect(generateRefDestinationPath(projectRootPath, selectedRef)).toBe(result);
+ });
+});
diff --git a/spec/frontend/search/mock_data.js b/spec/frontend/search/mock_data.js
index fa5ccfeb478..e02d3b0eab8 100644
--- a/spec/frontend/search/mock_data.js
+++ b/spec/frontend/search/mock_data.js
@@ -114,6 +114,7 @@ export const MOCK_NAVIGATION = {
scope: 'projects',
link: '/search?scope=projects&search=et',
count_link: '/search/count?scope=projects&search=et',
+ count: '10,000+',
},
blobs: {
label: 'Code',
diff --git a/spec/frontend/search/sidebar/components/confidentiality_filter_spec.js b/spec/frontend/search/sidebar/components/confidentiality_filter_spec.js
index c57eabd57b9..d5ecca4636c 100644
--- a/spec/frontend/search/sidebar/components/confidentiality_filter_spec.js
+++ b/spec/frontend/search/sidebar/components/confidentiality_filter_spec.js
@@ -1,39 +1,16 @@
import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import Vuex from 'vuex';
-import { MOCK_QUERY } from 'jest/search/mock_data';
import ConfidentialityFilter from '~/search/sidebar/components/confidentiality_filter.vue';
import RadioFilter from '~/search/sidebar/components/radio_filter.vue';
-Vue.use(Vuex);
-
describe('ConfidentialityFilter', () => {
let wrapper;
- const actionSpies = {
- applyQuery: jest.fn(),
- resetQuery: jest.fn(),
- };
-
- const createComponent = (initialState) => {
- const store = new Vuex.Store({
- state: {
- query: MOCK_QUERY,
- ...initialState,
- },
- actions: actionSpies,
- });
-
+ const createComponent = (initProps) => {
wrapper = shallowMount(ConfidentialityFilter, {
- store,
+ ...initProps,
});
};
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
const findRadioFilter = () => wrapper.findComponent(RadioFilter);
describe('template', () => {
@@ -41,24 +18,28 @@ describe('ConfidentialityFilter', () => {
createComponent();
});
- describe.each`
- scope | showFilter
- ${'issues'} | ${true}
- ${'merge_requests'} | ${false}
- ${'projects'} | ${false}
- ${'milestones'} | ${false}
- ${'users'} | ${false}
- ${'notes'} | ${false}
- ${'wiki_blobs'} | ${false}
- ${'blobs'} | ${false}
- `(`dropdown`, ({ scope, showFilter }) => {
- beforeEach(() => {
- createComponent({ query: { scope } });
- });
+ it('renders the component', () => {
+ expect(findRadioFilter().exists()).toBe(true);
+ });
+ });
- it(`does${showFilter ? '' : ' not'} render when scope is ${scope}`, () => {
- expect(findRadioFilter().exists()).toBe(showFilter);
+ describe.each`
+ hasFeatureFlagEnabled | paddingClass
+ ${true} | ${'gl-px-5'}
+ ${false} | ${'gl-px-0'}
+ `(`RadioFilter`, ({ hasFeatureFlagEnabled, paddingClass }) => {
+ beforeEach(() => {
+ createComponent({
+ provide: {
+ glFeatures: {
+ searchPageVerticalNav: hasFeatureFlagEnabled,
+ },
+ },
});
});
+
+ it(`has ${paddingClass} class`, () => {
+ expect(findRadioFilter().classes(paddingClass)).toBe(true);
+ });
});
});
diff --git a/spec/frontend/search/sidebar/components/filters_spec.js b/spec/frontend/search/sidebar/components/filters_spec.js
index 4f217709297..7e564bfa005 100644
--- a/spec/frontend/search/sidebar/components/filters_spec.js
+++ b/spec/frontend/search/sidebar/components/filters_spec.js
@@ -129,4 +129,44 @@ describe('GlobalSearchSidebarFilters', () => {
expect(actionSpies.resetQuery).toHaveBeenCalled();
});
});
+
+ describe.each`
+ scope | showFilter
+ ${'issues'} | ${true}
+ ${'merge_requests'} | ${false}
+ ${'projects'} | ${false}
+ ${'milestones'} | ${false}
+ ${'users'} | ${false}
+ ${'notes'} | ${false}
+ ${'wiki_blobs'} | ${false}
+ ${'blobs'} | ${false}
+ `(`ConfidentialityFilter`, ({ scope, showFilter }) => {
+ beforeEach(() => {
+ createComponent({ urlQuery: { scope } });
+ });
+
+ it(`does${showFilter ? '' : ' not'} render when scope is ${scope}`, () => {
+ expect(findConfidentialityFilter().exists()).toBe(showFilter);
+ });
+ });
+
+ describe.each`
+ scope | showFilter
+ ${'issues'} | ${true}
+ ${'merge_requests'} | ${true}
+ ${'projects'} | ${false}
+ ${'milestones'} | ${false}
+ ${'users'} | ${false}
+ ${'notes'} | ${false}
+ ${'wiki_blobs'} | ${false}
+ ${'blobs'} | ${false}
+ `(`StatusFilter`, ({ scope, showFilter }) => {
+ beforeEach(() => {
+ createComponent({ urlQuery: { scope } });
+ });
+
+ it(`does${showFilter ? '' : ' not'} render when scope is ${scope}`, () => {
+ expect(findStatusFilter().exists()).toBe(showFilter);
+ });
+ });
});
diff --git a/spec/frontend/search/sidebar/components/scope_navigation_spec.js b/spec/frontend/search/sidebar/components/scope_navigation_spec.js
index 6262a52e01a..23c158239dc 100644
--- a/spec/frontend/search/sidebar/components/scope_navigation_spec.js
+++ b/spec/frontend/search/sidebar/components/scope_navigation_spec.js
@@ -1,4 +1,4 @@
-import { GlNav, GlNavItem } from '@gitlab/ui';
+import { GlNav, GlNavItem, GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
@@ -37,6 +37,7 @@ describe('ScopeNavigation', () => {
const findGlNav = () => wrapper.findComponent(GlNav);
const findGlNavItems = () => wrapper.findAllComponents(GlNavItem);
const findGlNavItemActive = () => findGlNavItems().wrappers.filter((w) => w.attributes('active'));
+ const findGlNavItemActiveLabel = () => findGlNavItemActive().at(0).findAll('span').at(0).text();
const findGlNavItemActiveCount = () => findGlNavItemActive().at(0).findAll('span').at(1);
describe('scope navigation', () => {
@@ -56,7 +57,7 @@ describe('ScopeNavigation', () => {
expect(findGlNavItems()).toHaveLength(9);
});
- it('nav items have proper links', () => {
+ it('has all proper links', () => {
const linkAtPosition = 3;
const { link } = MOCK_NAVIGATION[Object.keys(MOCK_NAVIGATION)[linkAtPosition]];
@@ -64,17 +65,47 @@ describe('ScopeNavigation', () => {
});
});
- describe('scope navigation sets proper state', () => {
+ describe('scope navigation sets proper state with url scope set', () => {
beforeEach(() => {
createComponent();
});
- it('sets proper class to active item', () => {
+ it('has correct active item', () => {
expect(findGlNavItemActive()).toHaveLength(1);
+ expect(findGlNavItemActiveLabel()).toBe('Issues');
});
- it('active item', () => {
+ it('has correct active item count', () => {
expect(findGlNavItemActiveCount().text()).toBe('2.4K');
});
+
+ it('does not have plus sign after count text', () => {
+ expect(findGlNavItemActive().at(0).findComponent(GlIcon).exists()).toBe(false);
+ });
+
+ it('has count is highlighted correctly', () => {
+ expect(findGlNavItemActiveCount().classes('gl-text-gray-900')).toBe(true);
+ });
+ });
+
+ describe('scope navigation sets proper state with NO url scope set', () => {
+ beforeEach(() => {
+ createComponent({
+ urlQuery: {},
+ });
+ });
+
+ it('has correct active item', () => {
+ expect(findGlNavItems().at(0).attributes('active')).toBe('true');
+ expect(findGlNavItemActiveLabel()).toBe('Projects');
+ });
+
+ it('has correct active item count', () => {
+ expect(findGlNavItemActiveCount().text()).toBe('10K');
+ });
+
+ it('has correct active item count and over limit sign', () => {
+ expect(findGlNavItemActive().at(0).findComponent(GlIcon).exists()).toBe(true);
+ });
});
});
diff --git a/spec/frontend/search/sidebar/components/status_filter_spec.js b/spec/frontend/search/sidebar/components/status_filter_spec.js
index f3152c014b6..2ed199469e6 100644
--- a/spec/frontend/search/sidebar/components/status_filter_spec.js
+++ b/spec/frontend/search/sidebar/components/status_filter_spec.js
@@ -1,39 +1,16 @@
import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import Vuex from 'vuex';
-import { MOCK_QUERY } from 'jest/search/mock_data';
import RadioFilter from '~/search/sidebar/components/radio_filter.vue';
import StatusFilter from '~/search/sidebar/components/status_filter.vue';
-Vue.use(Vuex);
-
describe('StatusFilter', () => {
let wrapper;
- const actionSpies = {
- applyQuery: jest.fn(),
- resetQuery: jest.fn(),
- };
-
- const createComponent = (initialState) => {
- const store = new Vuex.Store({
- state: {
- query: MOCK_QUERY,
- ...initialState,
- },
- actions: actionSpies,
- });
-
+ const createComponent = (initProps) => {
wrapper = shallowMount(StatusFilter, {
- store,
+ ...initProps,
});
};
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
const findRadioFilter = () => wrapper.findComponent(RadioFilter);
describe('template', () => {
@@ -41,24 +18,28 @@ describe('StatusFilter', () => {
createComponent();
});
- describe.each`
- scope | showFilter
- ${'issues'} | ${true}
- ${'merge_requests'} | ${true}
- ${'projects'} | ${false}
- ${'milestones'} | ${false}
- ${'users'} | ${false}
- ${'notes'} | ${false}
- ${'wiki_blobs'} | ${false}
- ${'blobs'} | ${false}
- `(`dropdown`, ({ scope, showFilter }) => {
- beforeEach(() => {
- createComponent({ query: { scope } });
- });
+ it('renders the component', () => {
+ expect(findRadioFilter().exists()).toBe(true);
+ });
+ });
- it(`does${showFilter ? '' : ' not'} render when scope is ${scope}`, () => {
- expect(findRadioFilter().exists()).toBe(showFilter);
+ describe.each`
+ hasFeatureFlagEnabled | paddingClass
+ ${true} | ${'gl-px-5'}
+ ${false} | ${'gl-px-0'}
+ `(`RadioFilter`, ({ hasFeatureFlagEnabled, paddingClass }) => {
+ beforeEach(() => {
+ createComponent({
+ provide: {
+ glFeatures: {
+ searchPageVerticalNav: hasFeatureFlagEnabled,
+ },
+ },
});
});
+
+ it(`has ${paddingClass} class`, () => {
+ expect(findRadioFilter().classes(paddingClass)).toBe(true);
+ });
});
});
diff --git a/spec/frontend/search/topbar/components/app_spec.js b/spec/frontend/search/topbar/components/app_spec.js
index c7fd7084101..3975887cfff 100644
--- a/spec/frontend/search/topbar/components/app_spec.js
+++ b/spec/frontend/search/topbar/components/app_spec.js
@@ -1,4 +1,4 @@
-import { GlSearchBoxByClick } from '@gitlab/ui';
+import { GlSearchBoxByClick, GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
@@ -6,6 +6,8 @@ import { MOCK_QUERY } from 'jest/search/mock_data';
import GlobalSearchTopbar from '~/search/topbar/components/app.vue';
import GroupFilter from '~/search/topbar/components/group_filter.vue';
import ProjectFilter from '~/search/topbar/components/project_filter.vue';
+import MarkdownDrawer from '~/vue_shared/components/markdown_drawer/markdown_drawer.vue';
+import { SYNTAX_OPTIONS_DOCUMENT } from '~/search/topbar/constants';
Vue.use(Vuex);
@@ -18,7 +20,7 @@ describe('GlobalSearchTopbar', () => {
preloadStoredFrequentItems: jest.fn(),
};
- const createComponent = (initialState) => {
+ const createComponent = (initialState, props, stubs) => {
const store = new Vuex.Store({
state: {
query: MOCK_QUERY,
@@ -29,6 +31,8 @@ describe('GlobalSearchTopbar', () => {
wrapper = shallowMount(GlobalSearchTopbar, {
store,
+ propsData: props,
+ stubs,
});
};
@@ -39,6 +43,8 @@ describe('GlobalSearchTopbar', () => {
const findGlSearchBox = () => wrapper.findComponent(GlSearchBoxByClick);
const findGroupFilter = () => wrapper.findComponent(GroupFilter);
const findProjectFilter = () => wrapper.findComponent(ProjectFilter);
+ const findSyntaxOptionButton = () => wrapper.findComponent(GlButton);
+ const findSyntaxOptionDrawer = () => wrapper.findComponent(MarkdownDrawer);
describe('template', () => {
beforeEach(() => {
@@ -71,6 +77,72 @@ describe('GlobalSearchTopbar', () => {
expect(findProjectFilter().exists()).toBe(showFilters);
});
});
+
+ describe('syntax option feature', () => {
+ describe('template', () => {
+ beforeEach(() => {
+ createComponent(
+ { query: { repository_ref: '' } },
+ { elasticsearchEnabled: true, defaultBranchName: '' },
+ );
+ });
+
+ it('renders button correctly', () => {
+ expect(findSyntaxOptionButton().exists()).toBe(true);
+ });
+
+ it('renders drawer correctly', () => {
+ expect(findSyntaxOptionDrawer().exists()).toBe(true);
+ expect(findSyntaxOptionDrawer().attributes('documentpath')).toBe(SYNTAX_OPTIONS_DOCUMENT);
+ });
+
+ it('dispatched correct click action', () => {
+ const draweToggleSpy = jest.fn();
+ wrapper.vm.$refs.markdownDrawer.toggleDrawer = draweToggleSpy;
+
+ findSyntaxOptionButton().vm.$emit('click');
+ expect(draweToggleSpy).toHaveBeenCalled();
+ });
+ });
+
+ describe.each`
+ query | propsData | hasSyntaxOptions
+ ${null} | ${{ elasticsearchEnabled: false, defaultBranchName: '' }} | ${false}
+ ${{ query: { repository_ref: '' } }} | ${{ elasticsearchEnabled: false, defaultBranchName: '' }} | ${false}
+ ${{ query: { repository_ref: 'master' } }} | ${{ elasticsearchEnabled: false, defaultBranchName: 'master' }} | ${false}
+ ${{ query: { repository_ref: 'master' } }} | ${{ elasticsearchEnabled: true, defaultBranchName: '' }} | ${false}
+ ${{ query: { repository_ref: '' } }} | ${{ elasticsearchEnabled: true, defaultBranchName: 'master' }} | ${true}
+ ${{ query: { repository_ref: '' } }} | ${{ elasticsearchEnabled: true, defaultBranchName: '' }} | ${true}
+ ${{ query: { repository_ref: 'master' } }} | ${{ elasticsearchEnabled: true, defaultBranchName: 'master' }} | ${true}
+ `(
+ 'renders the syntax option based on component state',
+ ({ query, propsData, hasSyntaxOptions }) => {
+ beforeEach(() => {
+ createComponent(query, { ...propsData });
+ });
+
+ it(`does${
+ hasSyntaxOptions ? '' : ' not'
+ } have syntax option button when repository_ref: '${
+ query?.query?.repository_ref
+ }', elasticsearchEnabled: ${propsData.elasticsearchEnabled}, defaultBranchName: '${
+ propsData.defaultBranchName
+ }'`, () => {
+ expect(findSyntaxOptionButton().exists()).toBe(hasSyntaxOptions);
+ });
+
+ it(`does${
+ hasSyntaxOptions ? '' : ' not'
+ } have syntax option drawer when repository_ref: '${
+ query?.query?.repository_ref
+ }', elasticsearchEnabled: ${propsData.elasticsearchEnabled}, defaultBranchName: '${
+ propsData.defaultBranchName
+ }'`, () => {
+ expect(findSyntaxOptionDrawer().exists()).toBe(hasSyntaxOptions);
+ });
+ },
+ );
+ });
});
describe('actions', () => {
diff --git a/spec/frontend/self_monitor/store/actions_spec.js b/spec/frontend/self_monitor/store/actions_spec.js
index 21e63533c66..65c9d2f5f01 100644
--- a/spec/frontend/self_monitor/store/actions_spec.js
+++ b/spec/frontend/self_monitor/store/actions_spec.js
@@ -1,7 +1,7 @@
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
-import statusCodes from '~/lib/utils/http_status';
+import statusCodes, { HTTP_STATUS_ACCEPTED } from '~/lib/utils/http_status';
import * as actions from '~/self_monitor/store/actions';
import * as types from '~/self_monitor/store/mutation_types';
import createState from '~/self_monitor/store/state';
@@ -44,7 +44,7 @@ describe('self-monitor actions', () => {
beforeEach(() => {
state.createProjectEndpoint = '/create';
state.createProjectStatusEndpoint = '/create_status';
- mock.onPost(state.createProjectEndpoint).reply(statusCodes.ACCEPTED, {
+ mock.onPost(state.createProjectEndpoint).reply(HTTP_STATUS_ACCEPTED, {
job_id: '123',
});
mock.onGet(state.createProjectStatusEndpoint).reply(statusCodes.OK, {
@@ -151,7 +151,7 @@ describe('self-monitor actions', () => {
beforeEach(() => {
state.deleteProjectEndpoint = '/delete';
state.deleteProjectStatusEndpoint = '/delete-status';
- mock.onDelete(state.deleteProjectEndpoint).reply(statusCodes.ACCEPTED, {
+ mock.onDelete(state.deleteProjectEndpoint).reply(HTTP_STATUS_ACCEPTED, {
job_id: '456',
});
mock.onGet(state.deleteProjectStatusEndpoint).reply(statusCodes.OK, {
diff --git a/spec/frontend/sentry/index_spec.js b/spec/frontend/sentry/index_spec.js
index d1f098112e8..2dd528a8a1c 100644
--- a/spec/frontend/sentry/index_spec.js
+++ b/spec/frontend/sentry/index_spec.js
@@ -1,17 +1,20 @@
import index from '~/sentry/index';
+
+import LegacySentryConfig from '~/sentry/legacy_sentry_config';
import SentryConfig from '~/sentry/sentry_config';
-describe('SentryConfig options', () => {
+describe('Sentry init', () => {
+ let originalGon;
+
const dsn = 'https://123@sentry.gitlab.test/123';
- const currentUserId = 'currentUserId';
- const gitlabUrl = 'gitlabUrl';
const environment = 'test';
+ const currentUserId = '1';
+ const gitlabUrl = 'gitlabUrl';
const revision = 'revision';
const featureCategory = 'my_feature_category';
- let indexReturnValue;
-
beforeEach(() => {
+ originalGon = window.gon;
window.gon = {
sentry_dsn: dsn,
sentry_environment: environment,
@@ -21,28 +24,41 @@ describe('SentryConfig options', () => {
feature_category: featureCategory,
};
- process.env.HEAD_COMMIT_SHA = revision;
-
+ jest.spyOn(LegacySentryConfig, 'init').mockImplementation();
jest.spyOn(SentryConfig, 'init').mockImplementation();
+ });
- indexReturnValue = index();
+ afterEach(() => {
+ window.gon = originalGon;
});
- it('should init with .sentryDsn, .currentUserId, .whitelistUrls and environment', () => {
- expect(SentryConfig.init).toHaveBeenCalledWith({
- dsn,
- currentUserId,
- whitelistUrls: [gitlabUrl, 'webpack-internal://'],
- environment,
- release: revision,
- tags: {
- revision,
- feature_category: featureCategory,
- },
- });
+ it('exports new version of Sentry in the global object', () => {
+ // eslint-disable-next-line no-underscore-dangle
+ expect(window._Sentry.SDK_VERSION).not.toMatch(/^5\./);
});
- it('should return SentryConfig', () => {
- expect(indexReturnValue).toBe(SentryConfig);
+ describe('when called', () => {
+ beforeEach(() => {
+ index();
+ });
+
+ it('configures sentry', () => {
+ expect(SentryConfig.init).toHaveBeenCalledTimes(1);
+ expect(SentryConfig.init).toHaveBeenCalledWith({
+ dsn,
+ currentUserId,
+ allowUrls: [gitlabUrl, 'webpack-internal://'],
+ environment,
+ release: revision,
+ tags: {
+ revision,
+ feature_category: featureCategory,
+ },
+ });
+ });
+
+ it('does not configure legacy sentry', () => {
+ expect(LegacySentryConfig.init).not.toHaveBeenCalled();
+ });
});
});
diff --git a/spec/frontend/sentry/legacy_index_spec.js b/spec/frontend/sentry/legacy_index_spec.js
new file mode 100644
index 00000000000..5c336f8392e
--- /dev/null
+++ b/spec/frontend/sentry/legacy_index_spec.js
@@ -0,0 +1,64 @@
+import index from '~/sentry/legacy_index';
+
+import LegacySentryConfig from '~/sentry/legacy_sentry_config';
+import SentryConfig from '~/sentry/sentry_config';
+
+describe('Sentry init', () => {
+ let originalGon;
+
+ const dsn = 'https://123@sentry.gitlab.test/123';
+ const environment = 'test';
+ const currentUserId = '1';
+ const gitlabUrl = 'gitlabUrl';
+ const revision = 'revision';
+ const featureCategory = 'my_feature_category';
+
+ beforeEach(() => {
+ originalGon = window.gon;
+ window.gon = {
+ sentry_dsn: dsn,
+ sentry_environment: environment,
+ current_user_id: currentUserId,
+ gitlab_url: gitlabUrl,
+ revision,
+ feature_category: featureCategory,
+ };
+
+ jest.spyOn(LegacySentryConfig, 'init').mockImplementation();
+ jest.spyOn(SentryConfig, 'init').mockImplementation();
+ });
+
+ afterEach(() => {
+ window.gon = originalGon;
+ });
+
+ it('exports legacy version of Sentry in the global object', () => {
+ // eslint-disable-next-line no-underscore-dangle
+ expect(window._Sentry.SDK_VERSION).toMatch(/^5\./);
+ });
+
+ describe('when called', () => {
+ beforeEach(() => {
+ index();
+ });
+
+ it('configures legacy sentry', () => {
+ expect(LegacySentryConfig.init).toHaveBeenCalledTimes(1);
+ expect(LegacySentryConfig.init).toHaveBeenCalledWith({
+ dsn,
+ currentUserId,
+ whitelistUrls: [gitlabUrl, 'webpack-internal://'],
+ environment,
+ release: revision,
+ tags: {
+ revision,
+ feature_category: featureCategory,
+ },
+ });
+ });
+
+ it('does not configure new sentry', () => {
+ expect(SentryConfig.init).not.toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/frontend/sentry/legacy_sentry_config_spec.js b/spec/frontend/sentry/legacy_sentry_config_spec.js
new file mode 100644
index 00000000000..fe90cb49074
--- /dev/null
+++ b/spec/frontend/sentry/legacy_sentry_config_spec.js
@@ -0,0 +1,215 @@
+import * as Sentry5 from 'sentrybrowser5';
+import LegacySentryConfig from '~/sentry/legacy_sentry_config';
+
+describe('LegacySentryConfig', () => {
+ describe('IGNORE_ERRORS', () => {
+ it('should be an array of strings', () => {
+ const areStrings = LegacySentryConfig.IGNORE_ERRORS.every(
+ (error) => typeof error === 'string',
+ );
+
+ expect(areStrings).toBe(true);
+ });
+ });
+
+ describe('BLACKLIST_URLS', () => {
+ it('should be an array of regexps', () => {
+ const areRegExps = LegacySentryConfig.BLACKLIST_URLS.every((url) => url instanceof RegExp);
+
+ expect(areRegExps).toBe(true);
+ });
+ });
+
+ describe('SAMPLE_RATE', () => {
+ it('should be a finite number', () => {
+ expect(typeof LegacySentryConfig.SAMPLE_RATE).toEqual('number');
+ });
+ });
+
+ describe('init', () => {
+ const options = {
+ currentUserId: 1,
+ };
+
+ beforeEach(() => {
+ jest.spyOn(LegacySentryConfig, 'configure');
+ jest.spyOn(LegacySentryConfig, 'bindSentryErrors');
+ jest.spyOn(LegacySentryConfig, 'setUser');
+
+ LegacySentryConfig.init(options);
+ });
+
+ it('should set the options property', () => {
+ expect(LegacySentryConfig.options).toEqual(options);
+ });
+
+ it('should call the configure method', () => {
+ expect(LegacySentryConfig.configure).toHaveBeenCalled();
+ });
+
+ it('should call the error bindings method', () => {
+ expect(LegacySentryConfig.bindSentryErrors).toHaveBeenCalled();
+ });
+
+ it('should call setUser', () => {
+ expect(LegacySentryConfig.setUser).toHaveBeenCalled();
+ });
+
+ it('should not call setUser if there is no current user ID', () => {
+ LegacySentryConfig.setUser.mockClear();
+ options.currentUserId = undefined;
+
+ LegacySentryConfig.init(options);
+
+ expect(LegacySentryConfig.setUser).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('configure', () => {
+ const sentryConfig = {};
+ const options = {
+ dsn: 'https://123@sentry.gitlab.test/123',
+ whitelistUrls: ['//gitlabUrl', 'webpack-internal://'],
+ environment: 'test',
+ release: 'revision',
+ tags: {
+ revision: 'revision',
+ feature_category: 'my_feature_category',
+ },
+ };
+
+ beforeEach(() => {
+ jest.spyOn(Sentry5, 'init').mockImplementation();
+ jest.spyOn(Sentry5, 'setTags').mockImplementation();
+
+ sentryConfig.options = options;
+ sentryConfig.IGNORE_ERRORS = 'ignore_errors';
+ sentryConfig.BLACKLIST_URLS = 'blacklist_urls';
+
+ LegacySentryConfig.configure.call(sentryConfig);
+ });
+
+ it('should call Sentry5.init', () => {
+ expect(Sentry5.init).toHaveBeenCalledWith({
+ dsn: options.dsn,
+ release: options.release,
+ sampleRate: 0.95,
+ whitelistUrls: options.whitelistUrls,
+ environment: 'test',
+ ignoreErrors: sentryConfig.IGNORE_ERRORS,
+ blacklistUrls: sentryConfig.BLACKLIST_URLS,
+ });
+ });
+
+ it('should call Sentry5.setTags', () => {
+ expect(Sentry5.setTags).toHaveBeenCalledWith(options.tags);
+ });
+
+ it('should set environment from options', () => {
+ sentryConfig.options.environment = 'development';
+
+ LegacySentryConfig.configure.call(sentryConfig);
+
+ expect(Sentry5.init).toHaveBeenCalledWith({
+ dsn: options.dsn,
+ release: options.release,
+ sampleRate: 0.95,
+ whitelistUrls: options.whitelistUrls,
+ environment: 'development',
+ ignoreErrors: sentryConfig.IGNORE_ERRORS,
+ blacklistUrls: sentryConfig.BLACKLIST_URLS,
+ });
+ });
+ });
+
+ describe('setUser', () => {
+ let sentryConfig;
+
+ beforeEach(() => {
+ sentryConfig = { options: { currentUserId: 1 } };
+ jest.spyOn(Sentry5, 'setUser');
+
+ LegacySentryConfig.setUser.call(sentryConfig);
+ });
+
+ it('should call .setUser', () => {
+ expect(Sentry5.setUser).toHaveBeenCalledWith({
+ id: sentryConfig.options.currentUserId,
+ });
+ });
+ });
+
+ describe('handleSentryErrors', () => {
+ let event;
+ let req;
+ let config;
+ let err;
+
+ beforeEach(() => {
+ event = {};
+ req = { status: 'status', responseText: 'Unknown response text', statusText: 'statusText' };
+ config = { type: 'type', url: 'url', data: 'data' };
+ err = {};
+
+ jest.spyOn(Sentry5, 'captureMessage');
+
+ LegacySentryConfig.handleSentryErrors(event, req, config, err);
+ });
+
+ it('should call Sentry5.captureMessage', () => {
+ expect(Sentry5.captureMessage).toHaveBeenCalledWith(err, {
+ extra: {
+ type: config.type,
+ url: config.url,
+ data: config.data,
+ status: req.status,
+ response: req.responseText,
+ error: err,
+ event,
+ },
+ });
+ });
+
+ describe('if no err is provided', () => {
+ beforeEach(() => {
+ LegacySentryConfig.handleSentryErrors(event, req, config);
+ });
+
+ it('should use req.statusText as the error value', () => {
+ expect(Sentry5.captureMessage).toHaveBeenCalledWith(req.statusText, {
+ extra: {
+ type: config.type,
+ url: config.url,
+ data: config.data,
+ status: req.status,
+ response: req.responseText,
+ error: req.statusText,
+ event,
+ },
+ });
+ });
+ });
+
+ describe('if no req.responseText is provided', () => {
+ beforeEach(() => {
+ req.responseText = undefined;
+
+ LegacySentryConfig.handleSentryErrors(event, req, config, err);
+ });
+
+ it('should use `Unknown response text` as the response', () => {
+ expect(Sentry5.captureMessage).toHaveBeenCalledWith(err, {
+ extra: {
+ type: config.type,
+ url: config.url,
+ data: config.data,
+ status: req.status,
+ response: 'Unknown response text',
+ error: err,
+ event,
+ },
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/sentry/sentry_browser_wrapper_spec.js b/spec/frontend/sentry/sentry_browser_wrapper_spec.js
new file mode 100644
index 00000000000..f4d646bab78
--- /dev/null
+++ b/spec/frontend/sentry/sentry_browser_wrapper_spec.js
@@ -0,0 +1,59 @@
+import * as Sentry from '~/sentry/sentry_browser_wrapper';
+
+const mockError = new Error('error!');
+const mockMsg = 'msg!';
+const mockFn = () => {};
+
+describe('SentryBrowserWrapper', () => {
+ afterEach(() => {
+ // eslint-disable-next-line no-underscore-dangle
+ delete window._Sentry;
+ });
+
+ describe('when _Sentry is not defined', () => {
+ it('methods fail silently', () => {
+ expect(() => {
+ Sentry.captureException(mockError);
+ Sentry.captureMessage(mockMsg);
+ Sentry.withScope(mockFn);
+ }).not.toThrow();
+ });
+ });
+
+ describe('when _Sentry is defined', () => {
+ let mockCaptureException;
+ let mockCaptureMessage;
+ let mockWithScope;
+
+ beforeEach(async () => {
+ mockCaptureException = jest.fn();
+ mockCaptureMessage = jest.fn();
+ mockWithScope = jest.fn();
+
+ // eslint-disable-next-line no-underscore-dangle
+ window._Sentry = {
+ captureException: mockCaptureException,
+ captureMessage: mockCaptureMessage,
+ withScope: mockWithScope,
+ };
+ });
+
+ it('captureException is called', () => {
+ Sentry.captureException(mockError);
+
+ expect(mockCaptureException).toHaveBeenCalledWith(mockError);
+ });
+
+ it('captureMessage is called', () => {
+ Sentry.captureMessage(mockMsg);
+
+ expect(mockCaptureMessage).toHaveBeenCalledWith(mockMsg);
+ });
+
+ it('withScope is called', () => {
+ Sentry.withScope(mockFn);
+
+ expect(mockWithScope).toHaveBeenCalledWith(mockFn);
+ });
+ });
+});
diff --git a/spec/frontend/sentry/sentry_config_spec.js b/spec/frontend/sentry/sentry_config_spec.js
index 9f67b681b8d..44acbee9b38 100644
--- a/spec/frontend/sentry/sentry_config_spec.js
+++ b/spec/frontend/sentry/sentry_config_spec.js
@@ -1,29 +1,9 @@
-import * as Sentry from '@sentry/browser';
+import * as Sentry from 'sentrybrowser7';
+import { IGNORE_ERRORS, DENY_URLS, SAMPLE_RATE } from '~/sentry/constants';
+
import SentryConfig from '~/sentry/sentry_config';
describe('SentryConfig', () => {
- describe('IGNORE_ERRORS', () => {
- it('should be an array of strings', () => {
- const areStrings = SentryConfig.IGNORE_ERRORS.every((error) => typeof error === 'string');
-
- expect(areStrings).toBe(true);
- });
- });
-
- describe('BLACKLIST_URLS', () => {
- it('should be an array of regexps', () => {
- const areRegExps = SentryConfig.BLACKLIST_URLS.every((url) => url instanceof RegExp);
-
- expect(areRegExps).toBe(true);
- });
- });
-
- describe('SAMPLE_RATE', () => {
- it('should be a finite number', () => {
- expect(typeof SentryConfig.SAMPLE_RATE).toEqual('number');
- });
- });
-
describe('init', () => {
const options = {
currentUserId: 1,
@@ -31,7 +11,6 @@ describe('SentryConfig', () => {
beforeEach(() => {
jest.spyOn(SentryConfig, 'configure');
- jest.spyOn(SentryConfig, 'bindSentryErrors');
jest.spyOn(SentryConfig, 'setUser');
SentryConfig.init(options);
@@ -45,19 +24,13 @@ describe('SentryConfig', () => {
expect(SentryConfig.configure).toHaveBeenCalled();
});
- it('should call the error bindings method', () => {
- expect(SentryConfig.bindSentryErrors).toHaveBeenCalled();
- });
-
it('should call setUser', () => {
expect(SentryConfig.setUser).toHaveBeenCalled();
});
it('should not call setUser if there is no current user ID', () => {
SentryConfig.setUser.mockClear();
- options.currentUserId = undefined;
-
- SentryConfig.init(options);
+ SentryConfig.init({ currentUserId: undefined });
expect(SentryConfig.setUser).not.toHaveBeenCalled();
});
@@ -67,7 +40,7 @@ describe('SentryConfig', () => {
const sentryConfig = {};
const options = {
dsn: 'https://123@sentry.gitlab.test/123',
- whitelistUrls: ['//gitlabUrl', 'webpack-internal://'],
+ allowUrls: ['//gitlabUrl', 'webpack-internal://'],
environment: 'test',
release: 'revision',
tags: {
@@ -81,8 +54,6 @@ describe('SentryConfig', () => {
jest.spyOn(Sentry, 'setTags').mockImplementation();
sentryConfig.options = options;
- sentryConfig.IGNORE_ERRORS = 'ignore_errors';
- sentryConfig.BLACKLIST_URLS = 'blacklist_urls';
SentryConfig.configure.call(sentryConfig);
});
@@ -91,11 +62,11 @@ describe('SentryConfig', () => {
expect(Sentry.init).toHaveBeenCalledWith({
dsn: options.dsn,
release: options.release,
- sampleRate: 0.95,
- whitelistUrls: options.whitelistUrls,
- environment: 'test',
- ignoreErrors: sentryConfig.IGNORE_ERRORS,
- blacklistUrls: sentryConfig.BLACKLIST_URLS,
+ sampleRate: SAMPLE_RATE,
+ allowUrls: options.allowUrls,
+ environment: options.environment,
+ ignoreErrors: IGNORE_ERRORS,
+ denyUrls: DENY_URLS,
});
});
@@ -111,11 +82,11 @@ describe('SentryConfig', () => {
expect(Sentry.init).toHaveBeenCalledWith({
dsn: options.dsn,
release: options.release,
- sampleRate: 0.95,
- whitelistUrls: options.whitelistUrls,
+ sampleRate: SAMPLE_RATE,
+ allowUrls: options.allowUrls,
environment: 'development',
- ignoreErrors: sentryConfig.IGNORE_ERRORS,
- blacklistUrls: sentryConfig.BLACKLIST_URLS,
+ ignoreErrors: IGNORE_ERRORS,
+ denyUrls: DENY_URLS,
});
});
});
@@ -136,78 +107,4 @@ describe('SentryConfig', () => {
});
});
});
-
- describe('handleSentryErrors', () => {
- let event;
- let req;
- let config;
- let err;
-
- beforeEach(() => {
- event = {};
- req = { status: 'status', responseText: 'Unknown response text', statusText: 'statusText' };
- config = { type: 'type', url: 'url', data: 'data' };
- err = {};
-
- jest.spyOn(Sentry, 'captureMessage');
-
- SentryConfig.handleSentryErrors(event, req, config, err);
- });
-
- it('should call Sentry.captureMessage', () => {
- expect(Sentry.captureMessage).toHaveBeenCalledWith(err, {
- extra: {
- type: config.type,
- url: config.url,
- data: config.data,
- status: req.status,
- response: req.responseText,
- error: err,
- event,
- },
- });
- });
-
- describe('if no err is provided', () => {
- beforeEach(() => {
- SentryConfig.handleSentryErrors(event, req, config);
- });
-
- it('should use req.statusText as the error value', () => {
- expect(Sentry.captureMessage).toHaveBeenCalledWith(req.statusText, {
- extra: {
- type: config.type,
- url: config.url,
- data: config.data,
- status: req.status,
- response: req.responseText,
- error: req.statusText,
- event,
- },
- });
- });
- });
-
- describe('if no req.responseText is provided', () => {
- beforeEach(() => {
- req.responseText = undefined;
-
- SentryConfig.handleSentryErrors(event, req, config, err);
- });
-
- it('should use `Unknown response text` as the response', () => {
- expect(Sentry.captureMessage).toHaveBeenCalledWith(err, {
- extra: {
- type: config.type,
- url: config.url,
- data: config.data,
- status: req.status,
- response: 'Unknown response text',
- error: err,
- event,
- },
- });
- });
- });
- });
});
diff --git a/spec/frontend/sidebar/assignees_realtime_spec.js b/spec/frontend/sidebar/assignees_realtime_spec.js
deleted file mode 100644
index ae8f07bf901..00000000000
--- a/spec/frontend/sidebar/assignees_realtime_spec.js
+++ /dev/null
@@ -1,85 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import AssigneesRealtime from '~/sidebar/components/assignees/assignees_realtime.vue';
-import issuableAssigneesSubscription from '~/sidebar/queries/issuable_assignees.subscription.graphql';
-import SidebarMediator from '~/sidebar/sidebar_mediator';
-import getIssueAssigneesQuery from '~/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql';
-import Mock, {
- issuableQueryResponse,
- subscriptionNullResponse,
- subscriptionResponse,
-} from './mock_data';
-
-Vue.use(VueApollo);
-
-describe('Assignees Realtime', () => {
- let wrapper;
- let mediator;
- let fakeApollo;
-
- const issuableQueryHandler = jest.fn().mockResolvedValue(issuableQueryResponse);
- const subscriptionInitialHandler = jest.fn().mockResolvedValue(subscriptionNullResponse);
-
- const createComponent = ({
- issuableType = 'issue',
- subscriptionHandler = subscriptionInitialHandler,
- } = {}) => {
- fakeApollo = createMockApollo([
- [getIssueAssigneesQuery, issuableQueryHandler],
- [issuableAssigneesSubscription, subscriptionHandler],
- ]);
- wrapper = shallowMount(AssigneesRealtime, {
- propsData: {
- issuableType,
- queryVariables: {
- issuableIid: '1',
- projectPath: 'path/to/project',
- },
- mediator,
- },
- apolloProvider: fakeApollo,
- });
- };
-
- beforeEach(() => {
- mediator = new SidebarMediator(Mock.mediator);
- });
-
- afterEach(() => {
- wrapper.destroy();
- fakeApollo = null;
- SidebarMediator.singleton = null;
- });
-
- it('calls the query with correct variables', () => {
- createComponent();
-
- expect(issuableQueryHandler).toHaveBeenCalledWith({
- issuableIid: '1',
- projectPath: 'path/to/project',
- });
- });
-
- it('calls the subscription with correct variable for issue', async () => {
- createComponent();
- await waitForPromises();
-
- expect(subscriptionInitialHandler).toHaveBeenCalledWith({
- issuableId: 'gid://gitlab/Issue/1',
- });
- });
-
- it('emits an `assigneesUpdated` event on subscription response', async () => {
- createComponent({
- subscriptionHandler: jest.fn().mockResolvedValue(subscriptionResponse),
- });
- await waitForPromises();
-
- expect(wrapper.emitted('assigneesUpdated')).toEqual([
- [{ id: '1', assignees: subscriptionResponse.data.issuableAssigneesUpdated.assignees.nodes }],
- ]);
- });
-});
diff --git a/spec/frontend/sidebar/assignees_spec.js b/spec/frontend/sidebar/assignees_spec.js
deleted file mode 100644
index 7cf7fd33022..00000000000
--- a/spec/frontend/sidebar/assignees_spec.js
+++ /dev/null
@@ -1,205 +0,0 @@
-import { GlIcon } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import { trimText } from 'helpers/text_helper';
-import UsersMockHelper from 'helpers/user_mock_data_helper';
-import Assignee from '~/sidebar/components/assignees/assignees.vue';
-import AssigneeAvatarLink from '~/sidebar/components/assignees/assignee_avatar_link.vue';
-import UsersMock from './mock_data';
-
-describe('Assignee component', () => {
- const getDefaultProps = () => ({
- rootPath: 'http://localhost:3000',
- users: [],
- editable: false,
- });
- let wrapper;
-
- const createWrapper = (propsData = getDefaultProps()) => {
- wrapper = mount(Assignee, {
- propsData,
- });
- };
-
- const findAllAvatarLinks = () => wrapper.findAllComponents(AssigneeAvatarLink);
- const findComponentTextNoUsers = () => wrapper.find('[data-testid="no-value"]');
- const findCollapsedChildren = () => wrapper.findAll('.sidebar-collapsed-icon > *');
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('No assignees/users', () => {
- it('displays no assignee icon when collapsed', () => {
- createWrapper();
- const collapsedChildren = findCollapsedChildren();
- const userIcon = collapsedChildren.at(0).findComponent(GlIcon);
-
- expect(collapsedChildren.length).toBe(1);
- expect(collapsedChildren.at(0).attributes('aria-label')).toBe('None');
- expect(userIcon.exists()).toBe(true);
- expect(userIcon.props('name')).toBe('user');
- });
-
- it('displays only "None" when no users are assigned and the issue is read-only', () => {
- createWrapper();
- const componentTextNoUsers = trimText(findComponentTextNoUsers().text());
-
- expect(componentTextNoUsers).toBe('None');
- expect(componentTextNoUsers).not.toContain('assign yourself');
- });
-
- it('displays only "None" when no users are assigned and the issue can be edited', () => {
- createWrapper({
- ...getDefaultProps(),
- editable: true,
- });
- const componentTextNoUsers = trimText(findComponentTextNoUsers().text());
-
- expect(componentTextNoUsers).toContain('None');
- expect(componentTextNoUsers).toContain('assign yourself');
- });
-
- it('emits the assign-self event when "assign yourself" is clicked', async () => {
- createWrapper({
- ...getDefaultProps(),
- editable: true,
- });
-
- jest.spyOn(wrapper.vm, '$emit');
- wrapper.find('[data-testid="assign-yourself"]').trigger('click');
-
- await nextTick();
- expect(wrapper.emitted('assign-self')).toHaveLength(1);
- });
- });
-
- describe('One assignee/user', () => {
- it('displays one assignee icon when collapsed', () => {
- createWrapper({
- ...getDefaultProps(),
- users: [UsersMock.user],
- });
-
- const collapsedChildren = findCollapsedChildren();
- const assignee = collapsedChildren.at(0);
-
- expect(collapsedChildren.length).toBe(1);
- expect(assignee.find('.avatar').attributes('src')).toBe(UsersMock.user.avatar);
- expect(assignee.find('.avatar').attributes('alt')).toBe(`${UsersMock.user.name}'s avatar`);
-
- expect(trimText(assignee.find('.author').text())).toBe(UsersMock.user.name);
- });
- });
-
- describe('Two or more assignees/users', () => {
- it('displays two assignee icons when collapsed', () => {
- const users = UsersMockHelper.createNumberRandomUsers(2);
- createWrapper({
- ...getDefaultProps(),
- users,
- });
-
- const collapsedChildren = findCollapsedChildren();
-
- expect(collapsedChildren.length).toBe(2);
-
- const first = collapsedChildren.at(0);
-
- expect(first.find('.avatar').attributes('src')).toBe(users[0].avatar_url);
- expect(first.find('.avatar').attributes('alt')).toBe(`${users[0].name}'s avatar`);
-
- expect(trimText(first.find('.author').text())).toBe(users[0].name);
-
- const second = collapsedChildren.at(1);
-
- expect(second.find('.avatar').attributes('src')).toBe(users[1].avatar_url);
- expect(second.find('.avatar').attributes('alt')).toBe(`${users[1].name}'s avatar`);
-
- expect(trimText(second.find('.author').text())).toBe(users[1].name);
- });
-
- it('displays one assignee icon and counter when collapsed', () => {
- const users = UsersMockHelper.createNumberRandomUsers(3);
- createWrapper({
- ...getDefaultProps(),
- users,
- });
-
- const collapsedChildren = findCollapsedChildren();
-
- expect(collapsedChildren.length).toBe(2);
-
- const first = collapsedChildren.at(0);
-
- expect(first.find('.avatar').attributes('src')).toBe(users[0].avatar_url);
- expect(first.find('.avatar').attributes('alt')).toBe(`${users[0].name}'s avatar`);
-
- expect(trimText(first.find('.author').text())).toBe(users[0].name);
-
- const second = collapsedChildren.at(1);
-
- expect(trimText(second.find('.avatar-counter').text())).toBe('+2');
- });
-
- it('Shows two assignees', () => {
- const users = UsersMockHelper.createNumberRandomUsers(2);
- createWrapper({
- ...getDefaultProps(),
- users,
- editable: true,
- });
-
- expect(findAllAvatarLinks()).toHaveLength(users.length);
- expect(wrapper.find('.user-list-more').exists()).toBe(false);
- });
-
- it('shows sorted assignee where "can merge" users are sorted first', () => {
- const users = UsersMockHelper.createNumberRandomUsers(3);
- users[0].can_merge = false;
- users[1].can_merge = false;
- users[2].can_merge = true;
-
- createWrapper({
- ...getDefaultProps(),
- users,
- editable: true,
- });
-
- expect(wrapper.vm.sortedAssigness[0].can_merge).toBe(true);
- });
-
- it('passes the sorted assignees to the uncollapsed-assignee-list', () => {
- const users = UsersMockHelper.createNumberRandomUsers(3);
- users[0].can_merge = false;
- users[1].can_merge = false;
- users[2].can_merge = true;
-
- createWrapper({
- ...getDefaultProps(),
- users,
- });
-
- const userItems = findAllAvatarLinks();
-
- expect(userItems).toHaveLength(3);
- expect(userItems.at(0).attributes('title')).toBe(users[2].name);
- });
-
- it('passes the sorted assignees to the collapsed-assignee-list', () => {
- const users = UsersMockHelper.createNumberRandomUsers(3);
- users[0].can_merge = false;
- users[1].can_merge = false;
- users[2].can_merge = true;
-
- createWrapper({
- ...getDefaultProps(),
- users,
- });
-
- const collapsedButton = wrapper.find('.sidebar-collapsed-user button');
-
- expect(trimText(collapsedButton.text())).toBe(users[2].name);
- });
- });
-});
diff --git a/spec/frontend/sidebar/assignee_title_spec.js b/spec/frontend/sidebar/components/assignees/assignee_title_spec.js
index 14a6bdbf907..14a6bdbf907 100644
--- a/spec/frontend/sidebar/assignee_title_spec.js
+++ b/spec/frontend/sidebar/components/assignees/assignee_title_spec.js
diff --git a/spec/frontend/sidebar/components/assignees/assignees_realtime_spec.js b/spec/frontend/sidebar/components/assignees/assignees_realtime_spec.js
new file mode 100644
index 00000000000..080171fb2ea
--- /dev/null
+++ b/spec/frontend/sidebar/components/assignees/assignees_realtime_spec.js
@@ -0,0 +1,85 @@
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import AssigneesRealtime from '~/sidebar/components/assignees/assignees_realtime.vue';
+import issuableAssigneesSubscription from '~/sidebar/queries/issuable_assignees.subscription.graphql';
+import SidebarMediator from '~/sidebar/sidebar_mediator';
+import getIssueAssigneesQuery from '~/sidebar/queries/get_issue_assignees.query.graphql';
+import Mock, {
+ issuableQueryResponse,
+ subscriptionNullResponse,
+ subscriptionResponse,
+} from '../../mock_data';
+
+Vue.use(VueApollo);
+
+describe('Assignees Realtime', () => {
+ let wrapper;
+ let mediator;
+ let fakeApollo;
+
+ const issuableQueryHandler = jest.fn().mockResolvedValue(issuableQueryResponse);
+ const subscriptionInitialHandler = jest.fn().mockResolvedValue(subscriptionNullResponse);
+
+ const createComponent = ({
+ issuableType = 'issue',
+ subscriptionHandler = subscriptionInitialHandler,
+ } = {}) => {
+ fakeApollo = createMockApollo([
+ [getIssueAssigneesQuery, issuableQueryHandler],
+ [issuableAssigneesSubscription, subscriptionHandler],
+ ]);
+ wrapper = shallowMount(AssigneesRealtime, {
+ propsData: {
+ issuableType,
+ queryVariables: {
+ issuableIid: '1',
+ projectPath: 'path/to/project',
+ },
+ mediator,
+ },
+ apolloProvider: fakeApollo,
+ });
+ };
+
+ beforeEach(() => {
+ mediator = new SidebarMediator(Mock.mediator);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ fakeApollo = null;
+ SidebarMediator.singleton = null;
+ });
+
+ it('calls the query with correct variables', () => {
+ createComponent();
+
+ expect(issuableQueryHandler).toHaveBeenCalledWith({
+ issuableIid: '1',
+ projectPath: 'path/to/project',
+ });
+ });
+
+ it('calls the subscription with correct variable for issue', async () => {
+ createComponent();
+ await waitForPromises();
+
+ expect(subscriptionInitialHandler).toHaveBeenCalledWith({
+ issuableId: 'gid://gitlab/Issue/1',
+ });
+ });
+
+ it('emits an `assigneesUpdated` event on subscription response', async () => {
+ createComponent({
+ subscriptionHandler: jest.fn().mockResolvedValue(subscriptionResponse),
+ });
+ await waitForPromises();
+
+ expect(wrapper.emitted('assigneesUpdated')).toEqual([
+ [{ id: '1', assignees: subscriptionResponse.data.issuableAssigneesUpdated.assignees.nodes }],
+ ]);
+ });
+});
diff --git a/spec/frontend/sidebar/components/assignees/assignees_spec.js b/spec/frontend/sidebar/components/assignees/assignees_spec.js
new file mode 100644
index 00000000000..6971ae2f9ed
--- /dev/null
+++ b/spec/frontend/sidebar/components/assignees/assignees_spec.js
@@ -0,0 +1,205 @@
+import { GlIcon } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import { trimText } from 'helpers/text_helper';
+import UsersMockHelper from 'helpers/user_mock_data_helper';
+import Assignee from '~/sidebar/components/assignees/assignees.vue';
+import AssigneeAvatarLink from '~/sidebar/components/assignees/assignee_avatar_link.vue';
+import UsersMock from '../../mock_data';
+
+describe('Assignee component', () => {
+ const getDefaultProps = () => ({
+ rootPath: 'http://localhost:3000',
+ users: [],
+ editable: false,
+ });
+ let wrapper;
+
+ const createWrapper = (propsData = getDefaultProps()) => {
+ wrapper = mount(Assignee, {
+ propsData,
+ });
+ };
+
+ const findAllAvatarLinks = () => wrapper.findAllComponents(AssigneeAvatarLink);
+ const findComponentTextNoUsers = () => wrapper.find('[data-testid="no-value"]');
+ const findCollapsedChildren = () => wrapper.findAll('.sidebar-collapsed-icon > *');
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('No assignees/users', () => {
+ it('displays no assignee icon when collapsed', () => {
+ createWrapper();
+ const collapsedChildren = findCollapsedChildren();
+ const userIcon = collapsedChildren.at(0).findComponent(GlIcon);
+
+ expect(collapsedChildren.length).toBe(1);
+ expect(collapsedChildren.at(0).attributes('aria-label')).toBe('None');
+ expect(userIcon.exists()).toBe(true);
+ expect(userIcon.props('name')).toBe('user');
+ });
+
+ it('displays only "None" when no users are assigned and the issue is read-only', () => {
+ createWrapper();
+ const componentTextNoUsers = trimText(findComponentTextNoUsers().text());
+
+ expect(componentTextNoUsers).toBe('None');
+ expect(componentTextNoUsers).not.toContain('assign yourself');
+ });
+
+ it('displays only "None" when no users are assigned and the issue can be edited', () => {
+ createWrapper({
+ ...getDefaultProps(),
+ editable: true,
+ });
+ const componentTextNoUsers = trimText(findComponentTextNoUsers().text());
+
+ expect(componentTextNoUsers).toContain('None');
+ expect(componentTextNoUsers).toContain('assign yourself');
+ });
+
+ it('emits the assign-self event when "assign yourself" is clicked', async () => {
+ createWrapper({
+ ...getDefaultProps(),
+ editable: true,
+ });
+
+ jest.spyOn(wrapper.vm, '$emit');
+ wrapper.find('[data-testid="assign-yourself"]').trigger('click');
+
+ await nextTick();
+ expect(wrapper.emitted('assign-self')).toHaveLength(1);
+ });
+ });
+
+ describe('One assignee/user', () => {
+ it('displays one assignee icon when collapsed', () => {
+ createWrapper({
+ ...getDefaultProps(),
+ users: [UsersMock.user],
+ });
+
+ const collapsedChildren = findCollapsedChildren();
+ const assignee = collapsedChildren.at(0);
+
+ expect(collapsedChildren.length).toBe(1);
+ expect(assignee.find('.avatar').attributes('src')).toBe(UsersMock.user.avatar);
+ expect(assignee.find('.avatar').attributes('alt')).toBe(`${UsersMock.user.name}'s avatar`);
+
+ expect(trimText(assignee.find('.author').text())).toBe(UsersMock.user.name);
+ });
+ });
+
+ describe('Two or more assignees/users', () => {
+ it('displays two assignee icons when collapsed', () => {
+ const users = UsersMockHelper.createNumberRandomUsers(2);
+ createWrapper({
+ ...getDefaultProps(),
+ users,
+ });
+
+ const collapsedChildren = findCollapsedChildren();
+
+ expect(collapsedChildren.length).toBe(2);
+
+ const first = collapsedChildren.at(0);
+
+ expect(first.find('.avatar').attributes('src')).toBe(users[0].avatar_url);
+ expect(first.find('.avatar').attributes('alt')).toBe(`${users[0].name}'s avatar`);
+
+ expect(trimText(first.find('.author').text())).toBe(users[0].name);
+
+ const second = collapsedChildren.at(1);
+
+ expect(second.find('.avatar').attributes('src')).toBe(users[1].avatar_url);
+ expect(second.find('.avatar').attributes('alt')).toBe(`${users[1].name}'s avatar`);
+
+ expect(trimText(second.find('.author').text())).toBe(users[1].name);
+ });
+
+ it('displays one assignee icon and counter when collapsed', () => {
+ const users = UsersMockHelper.createNumberRandomUsers(3);
+ createWrapper({
+ ...getDefaultProps(),
+ users,
+ });
+
+ const collapsedChildren = findCollapsedChildren();
+
+ expect(collapsedChildren.length).toBe(2);
+
+ const first = collapsedChildren.at(0);
+
+ expect(first.find('.avatar').attributes('src')).toBe(users[0].avatar_url);
+ expect(first.find('.avatar').attributes('alt')).toBe(`${users[0].name}'s avatar`);
+
+ expect(trimText(first.find('.author').text())).toBe(users[0].name);
+
+ const second = collapsedChildren.at(1);
+
+ expect(trimText(second.find('.avatar-counter').text())).toBe('+2');
+ });
+
+ it('Shows two assignees', () => {
+ const users = UsersMockHelper.createNumberRandomUsers(2);
+ createWrapper({
+ ...getDefaultProps(),
+ users,
+ editable: true,
+ });
+
+ expect(findAllAvatarLinks()).toHaveLength(users.length);
+ expect(wrapper.find('.user-list-more').exists()).toBe(false);
+ });
+
+ it('shows sorted assignee where "can merge" users are sorted first', () => {
+ const users = UsersMockHelper.createNumberRandomUsers(3);
+ users[0].can_merge = false;
+ users[1].can_merge = false;
+ users[2].can_merge = true;
+
+ createWrapper({
+ ...getDefaultProps(),
+ users,
+ editable: true,
+ });
+
+ expect(wrapper.vm.sortedAssigness[0].can_merge).toBe(true);
+ });
+
+ it('passes the sorted assignees to the uncollapsed-assignee-list', () => {
+ const users = UsersMockHelper.createNumberRandomUsers(3);
+ users[0].can_merge = false;
+ users[1].can_merge = false;
+ users[2].can_merge = true;
+
+ createWrapper({
+ ...getDefaultProps(),
+ users,
+ });
+
+ const userItems = findAllAvatarLinks();
+
+ expect(userItems).toHaveLength(3);
+ expect(userItems.at(0).attributes('title')).toBe(users[2].name);
+ });
+
+ it('passes the sorted assignees to the collapsed-assignee-list', () => {
+ const users = UsersMockHelper.createNumberRandomUsers(3);
+ users[0].can_merge = false;
+ users[1].can_merge = false;
+ users[2].can_merge = true;
+
+ createWrapper({
+ ...getDefaultProps(),
+ users,
+ });
+
+ const collapsedButton = wrapper.find('.sidebar-collapsed-user button');
+
+ expect(trimText(collapsedButton.text())).toBe(users[2].name);
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/issuable_assignees_spec.js b/spec/frontend/sidebar/components/assignees/issuable_assignees_spec.js
index 1161fefcc64..1161fefcc64 100644
--- a/spec/frontend/sidebar/issuable_assignees_spec.js
+++ b/spec/frontend/sidebar/components/assignees/issuable_assignees_spec.js
diff --git a/spec/frontend/sidebar/components/assignees/sidebar_assignees_spec.js b/spec/frontend/sidebar/components/assignees/sidebar_assignees_spec.js
new file mode 100644
index 00000000000..58b174059fa
--- /dev/null
+++ b/spec/frontend/sidebar/components/assignees/sidebar_assignees_spec.js
@@ -0,0 +1,99 @@
+import { shallowMount } from '@vue/test-utils';
+import axios from 'axios';
+import AxiosMockAdapter from 'axios-mock-adapter';
+import { nextTick } from 'vue';
+import Assigness from '~/sidebar/components/assignees/assignees.vue';
+import AssigneesRealtime from '~/sidebar/components/assignees/assignees_realtime.vue';
+import SidebarAssignees from '~/sidebar/components/assignees/sidebar_assignees.vue';
+import SidebarService from '~/sidebar/services/sidebar_service';
+import SidebarMediator from '~/sidebar/sidebar_mediator';
+import SidebarStore from '~/sidebar/stores/sidebar_store';
+import Mock from '../../mock_data';
+
+describe('sidebar assignees', () => {
+ let wrapper;
+ let mediator;
+ let axiosMock;
+ const createComponent = (props) => {
+ wrapper = shallowMount(SidebarAssignees, {
+ propsData: {
+ issuableIid: '1',
+ issuableId: 1,
+ mediator,
+ field: '',
+ projectPath: 'projectPath',
+ changing: false,
+ ...props,
+ },
+ // Attaching to document is required because this component emits something from the parent element :/
+ attachTo: document.body,
+ });
+ };
+
+ beforeEach(() => {
+ axiosMock = new AxiosMockAdapter(axios);
+ mediator = new SidebarMediator(Mock.mediator);
+
+ jest.spyOn(mediator, 'saveAssignees');
+ jest.spyOn(mediator, 'assignYourself');
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+
+ SidebarService.singleton = null;
+ SidebarStore.singleton = null;
+ SidebarMediator.singleton = null;
+ axiosMock.restore();
+ });
+
+ it('calls the mediator when saves the assignees', () => {
+ createComponent();
+
+ expect(mediator.saveAssignees).not.toHaveBeenCalled();
+
+ wrapper.vm.saveAssignees();
+
+ expect(mediator.saveAssignees).toHaveBeenCalled();
+ });
+
+ it('calls the mediator when "assignSelf" method is called', () => {
+ createComponent();
+
+ expect(mediator.assignYourself).not.toHaveBeenCalled();
+ expect(mediator.store.assignees.length).toBe(0);
+
+ wrapper.vm.assignSelf();
+
+ expect(mediator.assignYourself).toHaveBeenCalled();
+ expect(mediator.store.assignees.length).toBe(1);
+ });
+
+ it('hides assignees until fetched', async () => {
+ createComponent();
+
+ expect(wrapper.findComponent(Assigness).exists()).toBe(false);
+
+ wrapper.vm.store.isFetching.assignees = false;
+
+ await nextTick();
+ expect(wrapper.findComponent(Assigness).exists()).toBe(true);
+ });
+
+ describe('when issuableType is issue', () => {
+ it('finds AssigneesRealtime component', () => {
+ createComponent();
+
+ expect(wrapper.findComponent(AssigneesRealtime).exists()).toBe(true);
+ });
+ });
+
+ describe('when issuableType is MR', () => {
+ it('does not find AssigneesRealtime component', () => {
+ createComponent({ issuableType: 'MR' });
+
+ expect(wrapper.findComponent(AssigneesRealtime).exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js b/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
index cbb4c41dd14..3aca346ff5f 100644
--- a/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
+++ b/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
@@ -12,8 +12,8 @@ import IssuableAssignees from '~/sidebar/components/assignees/issuable_assignees
import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assignees_widget.vue';
import SidebarInviteMembers from '~/sidebar/components/assignees/sidebar_invite_members.vue';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
-import getIssueAssigneesQuery from '~/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql';
-import updateIssueAssigneesMutation from '~/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql';
+import getIssueAssigneesQuery from '~/sidebar/queries/get_issue_assignees.query.graphql';
+import updateIssueAssigneesMutation from '~/sidebar/queries/update_issue_assignees.mutation.graphql';
import UserSelect from '~/vue_shared/components/user_select/user_select.vue';
import { issuableQueryResponse, updateIssueAssigneesMutationResponse } from '../../mock_data';
diff --git a/spec/frontend/sidebar/components/copy/copy_email_to_clipboard_spec.js b/spec/frontend/sidebar/components/copy/copy_email_to_clipboard_spec.js
new file mode 100644
index 00000000000..5b6db43a366
--- /dev/null
+++ b/spec/frontend/sidebar/components/copy/copy_email_to_clipboard_spec.js
@@ -0,0 +1,17 @@
+import { shallowMount } from '@vue/test-utils';
+import CopyEmailToClipboard from '~/sidebar/components/copy/copy_email_to_clipboard.vue';
+import CopyableField from '~/sidebar/components/copy/copyable_field.vue';
+
+describe('CopyEmailToClipboard component', () => {
+ const mockIssueEmailAddress = 'sample+email@test.com';
+
+ const wrapper = shallowMount(CopyEmailToClipboard, {
+ propsData: {
+ issueEmailAddress: mockIssueEmailAddress,
+ },
+ });
+
+ it('sets CopyableField `value` prop to issueEmailAddress', () => {
+ expect(wrapper.findComponent(CopyableField).props('value')).toBe(mockIssueEmailAddress);
+ });
+});
diff --git a/spec/frontend/sidebar/components/copy/copyable_field_spec.js b/spec/frontend/sidebar/components/copy/copyable_field_spec.js
new file mode 100644
index 00000000000..7790d77bc65
--- /dev/null
+++ b/spec/frontend/sidebar/components/copy/copyable_field_spec.js
@@ -0,0 +1,77 @@
+import { GlLoadingIcon, GlSprintf } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import CopyableField from '~/sidebar/components/copy/copyable_field.vue';
+
+describe('SidebarCopyableField', () => {
+ let wrapper;
+
+ const defaultProps = {
+ value: 'Gl-1',
+ name: 'Reference',
+ };
+
+ const createComponent = (propsData = defaultProps) => {
+ wrapper = shallowMount(CopyableField, {
+ propsData,
+ stubs: {
+ GlSprintf,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findClipboardButton = () => wrapper.findComponent(ClipboardButton);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+
+ describe('template', () => {
+ describe('when `isLoading` prop is `false`', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders copyable field', () => {
+ expect(wrapper.text()).toContain('Reference: Gl-1');
+ });
+
+ it('renders ClipboardButton with correct props', () => {
+ const clipboardButton = findClipboardButton();
+
+ expect(clipboardButton.exists()).toBe(true);
+ expect(clipboardButton.props('title')).toBe(`Copy ${defaultProps.name}`);
+ expect(clipboardButton.props('text')).toBe(defaultProps.value);
+ });
+
+ it('does not render loading icon', () => {
+ expect(findLoadingIcon().exists()).toBe(false);
+ });
+ });
+
+ describe('when `isLoading` prop is `true`', () => {
+ beforeEach(() => {
+ createComponent({ ...defaultProps, isLoading: true });
+ });
+
+ it('renders loading icon', () => {
+ expect(findLoadingIcon().exists()).toBe(true);
+ expect(findLoadingIcon().props('label')).toBe('Loading Reference');
+ });
+
+ it('does not render clipboard button', () => {
+ expect(findClipboardButton().exists()).toBe(false);
+ });
+ });
+
+ describe('with `clipboardTooltipText` prop', () => {
+ it('sets ClipboardButton `title` prop to `clipboardTooltipText` value', () => {
+ const mockClipboardTooltipText = 'Copy my custom value';
+ createComponent({ ...defaultProps, clipboardTooltipText: mockClipboardTooltipText });
+
+ expect(findClipboardButton().props('title')).toBe(mockClipboardTooltipText);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/copy/sidebar_reference_widget_spec.js b/spec/frontend/sidebar/components/copy/sidebar_reference_widget_spec.js
new file mode 100644
index 00000000000..c5161a748a9
--- /dev/null
+++ b/spec/frontend/sidebar/components/copy/sidebar_reference_widget_spec.js
@@ -0,0 +1,93 @@
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import { IssuableType } from '~/issues/constants';
+import SidebarReferenceWidget from '~/sidebar/components/copy/sidebar_reference_widget.vue';
+import issueReferenceQuery from '~/sidebar/queries/issue_reference.query.graphql';
+import mergeRequestReferenceQuery from '~/sidebar/queries/merge_request_reference.query.graphql';
+import CopyableField from '~/sidebar/components/copy/copyable_field.vue';
+import { issueReferenceResponse } from '../../mock_data';
+
+describe('Sidebar Reference Widget', () => {
+ let wrapper;
+ let fakeApollo;
+
+ const mockReferenceValue = 'reference-1234';
+
+ const findCopyableField = () => wrapper.findComponent(CopyableField);
+
+ const createComponent = ({
+ issuableType = IssuableType.Issue,
+ referenceQuery = issueReferenceQuery,
+ referenceQueryHandler = jest.fn().mockResolvedValue(issueReferenceResponse(mockReferenceValue)),
+ } = {}) => {
+ Vue.use(VueApollo);
+
+ fakeApollo = createMockApollo([[referenceQuery, referenceQueryHandler]]);
+
+ wrapper = shallowMount(SidebarReferenceWidget, {
+ apolloProvider: fakeApollo,
+ provide: {
+ fullPath: 'group/project',
+ iid: '1',
+ },
+ propsData: {
+ issuableType,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when reference is loading', () => {
+ it('sets CopyableField `is-loading` prop to `true`', () => {
+ createComponent({ referenceQueryHandler: jest.fn().mockReturnValue(new Promise(() => {})) });
+ expect(findCopyableField().props('isLoading')).toBe(true);
+ });
+ });
+
+ describe.each([
+ [IssuableType.Issue, issueReferenceQuery],
+ [IssuableType.MergeRequest, mergeRequestReferenceQuery],
+ ])('when issuableType is %s', (issuableType, referenceQuery) => {
+ it('sets CopyableField `value` prop to reference value', async () => {
+ createComponent({
+ issuableType,
+ referenceQuery,
+ });
+
+ await waitForPromises();
+
+ expect(findCopyableField().props('value')).toBe(mockReferenceValue);
+ });
+
+ describe('when error occurs', () => {
+ it('calls createFlash with correct parameters', async () => {
+ const mockError = new Error('mayday');
+
+ createComponent({
+ issuableType,
+ referenceQuery,
+ referenceQueryHandler: jest.fn().mockRejectedValue(mockError),
+ });
+
+ await waitForPromises();
+
+ const [
+ [
+ {
+ message,
+ error: { networkError },
+ },
+ ],
+ ] = wrapper.emitted('fetch-error');
+ expect(message).toBe('An error occurred while fetching reference');
+ expect(networkError).toEqual(mockError);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/copy_email_to_clipboard_spec.js b/spec/frontend/sidebar/components/copy_email_to_clipboard_spec.js
deleted file mode 100644
index 69a8d645973..00000000000
--- a/spec/frontend/sidebar/components/copy_email_to_clipboard_spec.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import CopyEmailToClipboard from '~/sidebar/components/copy_email_to_clipboard.vue';
-import CopyableField from '~/vue_shared/components/sidebar/copyable_field.vue';
-
-describe('CopyEmailToClipboard component', () => {
- const mockIssueEmailAddress = 'sample+email@test.com';
-
- const wrapper = shallowMount(CopyEmailToClipboard, {
- propsData: {
- issueEmailAddress: mockIssueEmailAddress,
- },
- });
-
- it('sets CopyableField `value` prop to issueEmailAddress', () => {
- expect(wrapper.findComponent(CopyableField).props('value')).toBe(mockIssueEmailAddress);
- });
-});
diff --git a/spec/frontend/sidebar/components/crm_contacts/crm_contacts_spec.js b/spec/frontend/sidebar/components/crm_contacts/crm_contacts_spec.js
new file mode 100644
index 00000000000..ca43c219d92
--- /dev/null
+++ b/spec/frontend/sidebar/components/crm_contacts/crm_contacts_spec.js
@@ -0,0 +1,96 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import { createAlert } from '~/flash';
+import CrmContacts from '~/sidebar/components/crm_contacts/crm_contacts.vue';
+import getIssueCrmContactsQuery from '~/sidebar/queries/get_issue_crm_contacts.query.graphql';
+import issueCrmContactsSubscription from '~/sidebar/queries/issue_crm_contacts.subscription.graphql';
+import {
+ getIssueCrmContactsQueryResponse,
+ issueCrmContactsUpdateResponse,
+ issueCrmContactsUpdateNullResponse,
+} from '../mock_data';
+
+jest.mock('~/flash');
+
+describe('Issue crm contacts component', () => {
+ Vue.use(VueApollo);
+ let wrapper;
+ let fakeApollo;
+
+ const successQueryHandler = jest.fn().mockResolvedValue(getIssueCrmContactsQueryResponse);
+ const successSubscriptionHandler = jest.fn().mockResolvedValue(issueCrmContactsUpdateResponse);
+ const nullSubscriptionHandler = jest.fn().mockResolvedValue(issueCrmContactsUpdateNullResponse);
+
+ const mountComponent = ({
+ queryHandler = successQueryHandler,
+ subscriptionHandler = successSubscriptionHandler,
+ } = {}) => {
+ fakeApollo = createMockApollo([
+ [getIssueCrmContactsQuery, queryHandler],
+ [issueCrmContactsSubscription, subscriptionHandler],
+ ]);
+ wrapper = shallowMountExtended(CrmContacts, {
+ propsData: { issueId: '123', groupIssuesPath: '/groups/flightjs/-/issues' },
+ apolloProvider: fakeApollo,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ fakeApollo = null;
+ });
+
+ it('should render error message on reject', async () => {
+ mountComponent({ queryHandler: jest.fn().mockRejectedValue('ERROR') });
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalled();
+ });
+
+ it('calls the query with correct variables', () => {
+ mountComponent();
+
+ expect(successQueryHandler).toHaveBeenCalledWith({
+ id: 'gid://gitlab/Issue/123',
+ });
+ });
+
+ it('calls the subscription with correct variable for issue', () => {
+ mountComponent();
+
+ expect(successSubscriptionHandler).toHaveBeenCalledWith({
+ id: 'gid://gitlab/Issue/123',
+ });
+ });
+
+ it('renders correct initial results', async () => {
+ mountComponent({ subscriptionHandler: nullSubscriptionHandler });
+ await waitForPromises();
+
+ expect(wrapper.find('#contact_0').text()).toContain('Someone Important');
+ expect(wrapper.find('#contact_0').attributes('href')).toBe(
+ '/groups/flightjs/-/issues?crm_contact_id=1',
+ );
+ expect(wrapper.find('#contact_container_0').text()).toContain('si@gitlab.com');
+ expect(wrapper.find('#contact_1').text()).toContain('Marty McFly');
+ expect(wrapper.find('#contact_1').attributes('href')).toBe(
+ '/groups/flightjs/-/issues?crm_contact_id=5',
+ );
+ });
+
+ it('renders correct results after subscription update', async () => {
+ mountComponent();
+ await waitForPromises();
+
+ const contact = ['Dave Davies', 'dd@gitlab.com', '+44 20 1111 2222', 'Vice President'];
+ contact.forEach((property) => {
+ expect(wrapper.find('#contact_container_0').text()).toContain(property);
+ });
+ expect(wrapper.find('#contact_0').attributes('href')).toBe(
+ '/groups/flightjs/-/issues?crm_contact_id=13',
+ );
+ });
+});
diff --git a/spec/frontend/sidebar/components/crm_contacts_spec.js b/spec/frontend/sidebar/components/crm_contacts_spec.js
deleted file mode 100644
index 6d76fa1f9df..00000000000
--- a/spec/frontend/sidebar/components/crm_contacts_spec.js
+++ /dev/null
@@ -1,96 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert } from '~/flash';
-import CrmContacts from '~/sidebar/components/crm_contacts/crm_contacts.vue';
-import getIssueCrmContactsQuery from '~/sidebar/components/crm_contacts/queries/get_issue_crm_contacts.query.graphql';
-import issueCrmContactsSubscription from '~/sidebar/components/crm_contacts/queries/issue_crm_contacts.subscription.graphql';
-import {
- getIssueCrmContactsQueryResponse,
- issueCrmContactsUpdateResponse,
- issueCrmContactsUpdateNullResponse,
-} from './mock_data';
-
-jest.mock('~/flash');
-
-describe('Issue crm contacts component', () => {
- Vue.use(VueApollo);
- let wrapper;
- let fakeApollo;
-
- const successQueryHandler = jest.fn().mockResolvedValue(getIssueCrmContactsQueryResponse);
- const successSubscriptionHandler = jest.fn().mockResolvedValue(issueCrmContactsUpdateResponse);
- const nullSubscriptionHandler = jest.fn().mockResolvedValue(issueCrmContactsUpdateNullResponse);
-
- const mountComponent = ({
- queryHandler = successQueryHandler,
- subscriptionHandler = successSubscriptionHandler,
- } = {}) => {
- fakeApollo = createMockApollo([
- [getIssueCrmContactsQuery, queryHandler],
- [issueCrmContactsSubscription, subscriptionHandler],
- ]);
- wrapper = shallowMountExtended(CrmContacts, {
- propsData: { issueId: '123', groupIssuesPath: '/groups/flightjs/-/issues' },
- apolloProvider: fakeApollo,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- fakeApollo = null;
- });
-
- it('should render error message on reject', async () => {
- mountComponent({ queryHandler: jest.fn().mockRejectedValue('ERROR') });
- await waitForPromises();
-
- expect(createAlert).toHaveBeenCalled();
- });
-
- it('calls the query with correct variables', () => {
- mountComponent();
-
- expect(successQueryHandler).toHaveBeenCalledWith({
- id: 'gid://gitlab/Issue/123',
- });
- });
-
- it('calls the subscription with correct variable for issue', () => {
- mountComponent();
-
- expect(successSubscriptionHandler).toHaveBeenCalledWith({
- id: 'gid://gitlab/Issue/123',
- });
- });
-
- it('renders correct initial results', async () => {
- mountComponent({ subscriptionHandler: nullSubscriptionHandler });
- await waitForPromises();
-
- expect(wrapper.find('#contact_0').text()).toContain('Someone Important');
- expect(wrapper.find('#contact_0').attributes('href')).toBe(
- '/groups/flightjs/-/issues?crm_contact_id=1',
- );
- expect(wrapper.find('#contact_container_0').text()).toContain('si@gitlab.com');
- expect(wrapper.find('#contact_1').text()).toContain('Marty McFly');
- expect(wrapper.find('#contact_1').attributes('href')).toBe(
- '/groups/flightjs/-/issues?crm_contact_id=5',
- );
- });
-
- it('renders correct results after subscription update', async () => {
- mountComponent();
- await waitForPromises();
-
- const contact = ['Dave Davies', 'dd@gitlab.com', '+44 20 1111 2222', 'Vice President'];
- contact.forEach((property) => {
- expect(wrapper.find('#contact_container_0').text()).toContain(property);
- });
- expect(wrapper.find('#contact_0').attributes('href')).toBe(
- '/groups/flightjs/-/issues?crm_contact_id=13',
- );
- });
-});
diff --git a/spec/frontend/sidebar/components/incidents/escalation_status_spec.js b/spec/frontend/sidebar/components/incidents/escalation_status_spec.js
index 83764cb6739..1a78ce4ddee 100644
--- a/spec/frontend/sidebar/components/incidents/escalation_status_spec.js
+++ b/spec/frontend/sidebar/components/incidents/escalation_status_spec.js
@@ -3,11 +3,7 @@ import { nextTick } from 'vue';
import waitForPromises from 'helpers/wait_for_promises';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import EscalationStatus from '~/sidebar/components/incidents/escalation_status.vue';
-import {
- STATUS_LABELS,
- STATUS_TRIGGERED,
- STATUS_ACKNOWLEDGED,
-} from '~/sidebar/components/incidents/constants';
+import { STATUS_LABELS, STATUS_TRIGGERED, STATUS_ACKNOWLEDGED } from '~/sidebar/constants';
describe('EscalationStatus', () => {
let wrapper;
diff --git a/spec/frontend/sidebar/components/incidents/escalation_utils_spec.js b/spec/frontend/sidebar/components/incidents/escalation_utils_spec.js
index edd65db0325..d9e7f29c10e 100644
--- a/spec/frontend/sidebar/components/incidents/escalation_utils_spec.js
+++ b/spec/frontend/sidebar/components/incidents/escalation_utils_spec.js
@@ -1,5 +1,5 @@
-import { STATUS_ACKNOWLEDGED } from '~/sidebar/components/incidents/constants';
-import { getStatusLabel } from '~/sidebar/components/incidents/utils';
+import { STATUS_ACKNOWLEDGED } from '~/sidebar/constants';
+import { getStatusLabel } from '~/sidebar/utils';
describe('EscalationUtils', () => {
describe('getStatusLabel', () => {
diff --git a/spec/frontend/sidebar/components/incidents/mock_data.js b/spec/frontend/sidebar/components/incidents/mock_data.js
index bbb6c61b162..2a5b7798110 100644
--- a/spec/frontend/sidebar/components/incidents/mock_data.js
+++ b/spec/frontend/sidebar/components/incidents/mock_data.js
@@ -1,4 +1,4 @@
-import { STATUS_TRIGGERED, STATUS_ACKNOWLEDGED } from '~/sidebar/components/incidents/constants';
+import { STATUS_TRIGGERED, STATUS_ACKNOWLEDGED } from '~/sidebar/constants';
export const fetchData = {
workspace: {
diff --git a/spec/frontend/sidebar/components/incidents/sidebar_escalation_status_spec.js b/spec/frontend/sidebar/components/incidents/sidebar_escalation_status_spec.js
index 88a4913a27f..2dded61c073 100644
--- a/spec/frontend/sidebar/components/incidents/sidebar_escalation_status_spec.js
+++ b/spec/frontend/sidebar/components/incidents/sidebar_escalation_status_spec.js
@@ -1,5 +1,4 @@
-import { createLocalVue } from '@vue/test-utils';
-import { nextTick } from 'vue';
+import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import {
fetchData,
@@ -12,26 +11,28 @@ import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import SidebarEscalationStatus from '~/sidebar/components/incidents/sidebar_escalation_status.vue';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
-import { escalationStatusQuery, escalationStatusMutation } from '~/sidebar/constants';
+import {
+ escalationStatusQuery,
+ escalationStatusMutation,
+ STATUS_ACKNOWLEDGED,
+} from '~/sidebar/constants';
import waitForPromises from 'helpers/wait_for_promises';
import EscalationStatus from 'ee_else_ce/sidebar/components/incidents/escalation_status.vue';
-import { STATUS_ACKNOWLEDGED } from '~/sidebar/components/incidents/constants';
import { createAlert } from '~/flash';
import { logError } from '~/lib/logger';
jest.mock('~/lib/logger');
jest.mock('~/flash');
-const localVue = createLocalVue();
+Vue.use(VueApollo);
describe('SidebarEscalationStatus', () => {
let wrapper;
+ let mockApollo;
const queryResolverMock = jest.fn();
const mutationResolverMock = jest.fn();
function createMockApolloProvider({ hasFetchError = false, hasMutationError = false } = {}) {
- localVue.use(VueApollo);
-
queryResolverMock.mockResolvedValue({ data: hasFetchError ? fetchError : fetchData });
mutationResolverMock.mockResolvedValue({
data: hasMutationError ? mutationError : mutationData,
@@ -45,15 +46,7 @@ describe('SidebarEscalationStatus', () => {
return createMockApollo(requestHandlers);
}
- function createComponent({ mockApollo } = {}) {
- let config;
-
- if (mockApollo) {
- config = { apolloProvider: mockApollo };
- } else {
- config = { mocks: { $apollo: { queries: { status: { loading: false } } } } };
- }
-
+ function createComponent(apolloProvider) {
wrapper = mountExtended(SidebarEscalationStatus, {
propsData: {
iid: '1',
@@ -66,13 +59,15 @@ describe('SidebarEscalationStatus', () => {
directives: {
GlTooltip: createMockDirective(),
},
- localVue,
- ...config,
+ apolloProvider,
});
+
+ // wait for apollo requests
+ return waitForPromises();
}
- afterEach(() => {
- wrapper.destroy();
+ beforeEach(() => {
+ mockApollo = createMockApolloProvider();
});
const findSidebarComponent = () => wrapper.findComponent(SidebarEditableItem);
@@ -80,36 +75,32 @@ describe('SidebarEscalationStatus', () => {
const findEditButton = () => wrapper.findByTestId('edit-button');
const findIcon = () => wrapper.findByTestId('status-icon');
- const clickEditButton = async () => {
+ const clickEditButton = () => {
findEditButton().vm.$emit('click');
- await nextTick();
+ return nextTick();
};
- const selectAcknowledgedStatus = async () => {
+ const selectAcknowledgedStatus = () => {
findStatusComponent().vm.$emit('input', STATUS_ACKNOWLEDGED);
// wait for apollo requests
- await waitForPromises();
+ return waitForPromises();
};
describe('sidebar', () => {
- it('renders the sidebar component', () => {
- createComponent();
+ it('renders the sidebar component', async () => {
+ await createComponent(mockApollo);
expect(findSidebarComponent().exists()).toBe(true);
});
describe('status icon', () => {
- it('is visible', () => {
- createComponent();
+ it('is visible', async () => {
+ await createComponent(mockApollo);
expect(findIcon().exists()).toBe(true);
expect(findIcon().isVisible()).toBe(true);
});
it('has correct tooltip', async () => {
- const mockApollo = createMockApolloProvider();
- createComponent({ mockApollo });
-
- // wait for apollo requests
- await waitForPromises();
+ await createComponent(mockApollo);
const tooltip = getBinding(findIcon().element, 'gl-tooltip');
@@ -120,11 +111,7 @@ describe('SidebarEscalationStatus', () => {
describe('status dropdown', () => {
beforeEach(async () => {
- const mockApollo = createMockApolloProvider();
- createComponent({ mockApollo });
-
- // wait for apollo requests
- await waitForPromises();
+ await createComponent(mockApollo);
});
it('is closed by default', () => {
@@ -148,11 +135,7 @@ describe('SidebarEscalationStatus', () => {
describe('update Status event', () => {
beforeEach(async () => {
- const mockApollo = createMockApolloProvider();
- createComponent({ mockApollo });
-
- // wait for apollo requests
- await waitForPromises();
+ await createComponent(mockApollo);
await clickEditButton();
await selectAcknowledgedStatus();
@@ -184,22 +167,16 @@ describe('SidebarEscalationStatus', () => {
describe('mutation errors', () => {
it('should error upon fetch', async () => {
- const mockApollo = createMockApolloProvider({ hasFetchError: true });
- createComponent({ mockApollo });
-
- // wait for apollo requests
- await waitForPromises();
+ mockApollo = createMockApolloProvider({ hasFetchError: true });
+ await createComponent(mockApollo);
expect(createAlert).toHaveBeenCalled();
expect(logError).toHaveBeenCalled();
});
it('should error upon mutation', async () => {
- const mockApollo = createMockApolloProvider({ hasMutationError: true });
- createComponent({ mockApollo });
-
- // wait for apollo requests
- await waitForPromises();
+ mockApollo = createMockApolloProvider({ hasMutationError: true });
+ await createComponent(mockApollo);
await clickEditButton();
await selectAcknowledgedStatus();
diff --git a/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_button_spec.js b/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_button_spec.js
new file mode 100644
index 00000000000..4f2a89e20db
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_button_spec.js
@@ -0,0 +1,89 @@
+import { GlIcon, GlButton } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
+import Vuex from 'vuex';
+
+import DropdownButton from '~/sidebar/components/labels/labels_select_vue/dropdown_button.vue';
+
+import labelSelectModule from '~/sidebar/components/labels/labels_select_vue/store';
+
+import { mockConfig } from './mock_data';
+
+let store;
+Vue.use(Vuex);
+
+const createComponent = (initialState = mockConfig) => {
+ store = new Vuex.Store(labelSelectModule());
+
+ store.dispatch('setInitialState', initialState);
+
+ return shallowMount(DropdownButton, {
+ store,
+ });
+};
+
+describe('DropdownButton', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findDropdownButton = () => wrapper.findComponent(GlButton);
+ const findDropdownText = () => wrapper.find('.dropdown-toggle-text');
+ const findDropdownIcon = () => wrapper.findComponent(GlIcon);
+
+ describe('methods', () => {
+ describe('handleButtonClick', () => {
+ it.each`
+ variant | expectPropagationStopped
+ ${'standalone'} | ${true}
+ ${'embedded'} | ${false}
+ `(
+ 'toggles dropdown content and handles event propagation when `state.variant` is "$variant"',
+ ({ variant, expectPropagationStopped }) => {
+ const event = { stopPropagation: jest.fn() };
+
+ wrapper = createComponent({ ...mockConfig, variant });
+
+ findDropdownButton().vm.$emit('click', event);
+
+ expect(store.state.showDropdownContents).toBe(true);
+ expect(event.stopPropagation).toHaveBeenCalledTimes(expectPropagationStopped ? 1 : 0);
+ },
+ );
+ });
+ });
+
+ describe('template', () => {
+ it('renders component container element', () => {
+ expect(wrapper.findComponent(GlButton).element).toBe(wrapper.element);
+ });
+
+ it('renders default button text element', () => {
+ const dropdownTextEl = findDropdownText();
+
+ expect(dropdownTextEl.exists()).toBe(true);
+ expect(dropdownTextEl.text()).toBe('Label');
+ });
+
+ it('renders provided button text element', async () => {
+ store.state.dropdownButtonText = 'Custom label';
+ const dropdownTextEl = findDropdownText();
+
+ await nextTick();
+ expect(dropdownTextEl.text()).toBe('Custom label');
+ });
+
+ it('renders chevron icon element', () => {
+ const iconEl = findDropdownIcon();
+
+ expect(iconEl.exists()).toBe(true);
+ expect(iconEl.props('name')).toBe('chevron-down');
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_create_view_spec.js b/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_create_view_spec.js
new file mode 100644
index 00000000000..59e95edfa20
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_create_view_spec.js
@@ -0,0 +1,211 @@
+import { GlButton, GlFormInput, GlLink, GlLoadingIcon } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
+import Vuex from 'vuex';
+
+import DropdownContentsCreateView from '~/sidebar/components/labels/labels_select_vue/dropdown_contents_create_view.vue';
+
+import labelSelectModule from '~/sidebar/components/labels/labels_select_vue/store';
+
+import { mockConfig, mockSuggestedColors } from './mock_data';
+
+Vue.use(Vuex);
+
+const createComponent = (initialState = mockConfig) => {
+ const store = new Vuex.Store(labelSelectModule());
+
+ store.dispatch('setInitialState', initialState);
+
+ return shallowMount(DropdownContentsCreateView, {
+ store,
+ });
+};
+
+describe('DropdownContentsCreateView', () => {
+ let wrapper;
+ const colors = Object.keys(mockSuggestedColors).map((color) => ({
+ [color]: mockSuggestedColors[color],
+ }));
+
+ beforeEach(() => {
+ gon.suggested_label_colors = mockSuggestedColors;
+ wrapper = createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('computed', () => {
+ describe('disableCreate', () => {
+ it('returns `true` when label title and color is not defined', () => {
+ expect(wrapper.vm.disableCreate).toBe(true);
+ });
+
+ it('returns `true` when `labelCreateInProgress` is true', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ labelTitle: 'Foo',
+ selectedColor: '#ff0000',
+ });
+ wrapper.vm.$store.dispatch('requestCreateLabel');
+
+ await nextTick();
+ expect(wrapper.vm.disableCreate).toBe(true);
+ });
+
+ it('returns `false` when label title and color is defined and create request is not already in progress', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ labelTitle: 'Foo',
+ selectedColor: '#ff0000',
+ });
+
+ await nextTick();
+ expect(wrapper.vm.disableCreate).toBe(false);
+ });
+ });
+
+ describe('suggestedColors', () => {
+ it('returns array of color objects containing color code and name', () => {
+ colors.forEach((color, index) => {
+ expect(wrapper.vm.suggestedColors[index]).toEqual(expect.objectContaining(color));
+ });
+ });
+ });
+ });
+
+ describe('methods', () => {
+ describe('getColorCode', () => {
+ it('returns color code from color object', () => {
+ expect(wrapper.vm.getColorCode(colors[0])).toBe(Object.keys(colors[0]).pop());
+ });
+ });
+
+ describe('getColorName', () => {
+ it('returns color name from color object', () => {
+ expect(wrapper.vm.getColorName(colors[0])).toBe(Object.values(colors[0]).pop());
+ });
+ });
+
+ describe('handleColorClick', () => {
+ it('sets provided `color` param to `selectedColor` prop', () => {
+ wrapper.vm.handleColorClick(colors[0]);
+
+ expect(wrapper.vm.selectedColor).toBe(Object.keys(colors[0]).pop());
+ });
+ });
+
+ describe('handleCreateClick', () => {
+ it('calls action `createLabel` with object containing `labelTitle` & `selectedColor`', async () => {
+ jest.spyOn(wrapper.vm, 'createLabel').mockImplementation();
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ labelTitle: 'Foo',
+ selectedColor: '#ff0000',
+ });
+
+ wrapper.vm.handleCreateClick();
+
+ await nextTick();
+ expect(wrapper.vm.createLabel).toHaveBeenCalledWith(
+ expect.objectContaining({
+ title: 'Foo',
+ color: '#ff0000',
+ }),
+ );
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('renders component container element with class "labels-select-contents-create"', () => {
+ expect(wrapper.attributes('class')).toContain('labels-select-contents-create');
+ });
+
+ it('renders dropdown back button element', () => {
+ const backBtnEl = wrapper.find('.dropdown-title').findAllComponents(GlButton).at(0);
+
+ expect(backBtnEl.exists()).toBe(true);
+ expect(backBtnEl.attributes('aria-label')).toBe('Go back');
+ expect(backBtnEl.props('icon')).toBe('arrow-left');
+ });
+
+ it('renders dropdown title element', () => {
+ const headerEl = wrapper.find('.dropdown-title > span');
+
+ expect(headerEl.exists()).toBe(true);
+ expect(headerEl.text()).toBe('Create label');
+ });
+
+ it('renders dropdown close button element', () => {
+ const closeBtnEl = wrapper.find('.dropdown-title').findAllComponents(GlButton).at(1);
+
+ expect(closeBtnEl.exists()).toBe(true);
+ expect(closeBtnEl.attributes('aria-label')).toBe('Close');
+ expect(closeBtnEl.props('icon')).toBe('close');
+ });
+
+ it('renders label title input element', () => {
+ const titleInputEl = wrapper.find('.dropdown-input').findComponent(GlFormInput);
+
+ expect(titleInputEl.exists()).toBe(true);
+ expect(titleInputEl.attributes('placeholder')).toBe('Name new label');
+ expect(titleInputEl.attributes('autofocus')).toBe('true');
+ });
+
+ it('renders color block element for all suggested colors', () => {
+ const colorBlocksEl = wrapper.find('.dropdown-content').findAllComponents(GlLink);
+
+ colorBlocksEl.wrappers.forEach((colorBlock, index) => {
+ expect(colorBlock.attributes('style')).toContain('background-color');
+ expect(colorBlock.attributes('title')).toBe(Object.values(colors[index]).pop());
+ });
+ });
+
+ it('renders color input element', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ selectedColor: '#ff0000',
+ });
+
+ await nextTick();
+ const colorPreviewEl = wrapper.find('.color-input-container > .dropdown-label-color-preview');
+ const colorInputEl = wrapper.find('.color-input-container').findComponent(GlFormInput);
+
+ expect(colorPreviewEl.exists()).toBe(true);
+ expect(colorPreviewEl.attributes('style')).toContain('background-color');
+ expect(colorInputEl.exists()).toBe(true);
+ expect(colorInputEl.attributes('placeholder')).toBe('Use custom color #FF0000');
+ expect(colorInputEl.attributes('value')).toBe('#ff0000');
+ });
+
+ it('renders create button element', () => {
+ const createBtnEl = wrapper.find('.dropdown-actions').findAllComponents(GlButton).at(0);
+
+ expect(createBtnEl.exists()).toBe(true);
+ expect(createBtnEl.text()).toContain('Create');
+ });
+
+ it('shows gl-loading-icon within create button element when `labelCreateInProgress` is `true`', async () => {
+ wrapper.vm.$store.dispatch('requestCreateLabel');
+
+ await nextTick();
+ const loadingIconEl = wrapper.find('.dropdown-actions').findComponent(GlLoadingIcon);
+
+ expect(loadingIconEl.exists()).toBe(true);
+ expect(loadingIconEl.isVisible()).toBe(true);
+ });
+
+ it('renders cancel button element', () => {
+ const cancelBtnEl = wrapper.find('.dropdown-actions').findAllComponents(GlButton).at(1);
+
+ expect(cancelBtnEl.exists()).toBe(true);
+ expect(cancelBtnEl.text()).toContain('Cancel');
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view_spec.js b/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view_spec.js
new file mode 100644
index 00000000000..865dc8fe8fb
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view_spec.js
@@ -0,0 +1,413 @@
+import {
+ GlIntersectionObserver,
+ GlButton,
+ GlLoadingIcon,
+ GlSearchBoxByType,
+ GlLink,
+} from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
+import Vuex from 'vuex';
+import { UP_KEY_CODE, DOWN_KEY_CODE, ENTER_KEY_CODE, ESC_KEY_CODE } from '~/lib/utils/keycodes';
+import DropdownContentsLabelsView from '~/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view.vue';
+import LabelItem from '~/sidebar/components/labels/labels_select_vue/label_item.vue';
+
+import * as actions from '~/sidebar/components/labels/labels_select_vue/store/actions';
+import * as getters from '~/sidebar/components/labels/labels_select_vue/store/getters';
+import mutations from '~/sidebar/components/labels/labels_select_vue/store/mutations';
+import defaultState from '~/sidebar/components/labels/labels_select_vue/store/state';
+
+import { mockConfig, mockLabels, mockRegularLabel } from './mock_data';
+
+Vue.use(Vuex);
+
+describe('DropdownContentsLabelsView', () => {
+ let wrapper;
+
+ const createComponent = (initialState = mockConfig) => {
+ const store = new Vuex.Store({
+ getters,
+ mutations,
+ state: {
+ ...defaultState(),
+ footerCreateLabelTitle: 'Create label',
+ footerManageLabelTitle: 'Manage labels',
+ },
+ actions: {
+ ...actions,
+ fetchLabels: jest.fn(),
+ },
+ });
+
+ store.dispatch('setInitialState', initialState);
+ store.dispatch('receiveLabelsSuccess', mockLabels);
+
+ wrapper = shallowMount(DropdownContentsLabelsView, {
+ store,
+ });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findDropdownContent = () => wrapper.find('[data-testid="dropdown-content"]');
+ const findDropdownTitle = () => wrapper.find('[data-testid="dropdown-title"]');
+ const findDropdownFooter = () => wrapper.find('[data-testid="dropdown-footer"]');
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+
+ describe('computed', () => {
+ describe('visibleLabels', () => {
+ it('returns matching labels filtered with `searchKey`', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ searchKey: 'bug',
+ });
+
+ expect(wrapper.vm.visibleLabels.length).toBe(1);
+ expect(wrapper.vm.visibleLabels[0].title).toBe('Bug');
+ });
+
+ it('returns matching labels with fuzzy filtering', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ searchKey: 'bg',
+ });
+
+ expect(wrapper.vm.visibleLabels.length).toBe(2);
+ expect(wrapper.vm.visibleLabels[0].title).toBe('Bug');
+ expect(wrapper.vm.visibleLabels[1].title).toBe('Boog');
+ });
+
+ it('returns all labels when `searchKey` is empty', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ searchKey: '',
+ });
+
+ expect(wrapper.vm.visibleLabels.length).toBe(mockLabels.length);
+ });
+ });
+
+ describe('showNoMatchingResultsMessage', () => {
+ it.each`
+ searchKey | labels | labelsDescription | returnValue
+ ${''} | ${[]} | ${'empty'} | ${false}
+ ${'bug'} | ${[]} | ${'empty'} | ${true}
+ ${''} | ${mockLabels} | ${'not empty'} | ${false}
+ ${'bug'} | ${mockLabels} | ${'not empty'} | ${false}
+ `(
+ 'returns $returnValue when searchKey is "$searchKey" and visibleLabels is $labelsDescription',
+ async ({ searchKey, labels, returnValue }) => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ searchKey,
+ });
+
+ wrapper.vm.$store.dispatch('receiveLabelsSuccess', labels);
+
+ await nextTick();
+
+ expect(wrapper.vm.showNoMatchingResultsMessage).toBe(returnValue);
+ },
+ );
+ });
+ });
+
+ describe('methods', () => {
+ const fakePreventDefault = jest.fn();
+
+ describe('isLabelSelected', () => {
+ it('returns true when provided `label` param is one of the selected labels', () => {
+ expect(wrapper.vm.isLabelSelected(mockRegularLabel)).toBe(true);
+ });
+
+ it('returns false when provided `label` param is not one of the selected labels', () => {
+ expect(wrapper.vm.isLabelSelected(mockLabels[1])).toBe(false);
+ });
+ });
+
+ describe('handleComponentAppear', () => {
+ it('calls `focusInput` on searchInput field', async () => {
+ wrapper.vm.$refs.searchInput.focusInput = jest.fn();
+
+ await wrapper.vm.handleComponentAppear();
+
+ expect(wrapper.vm.$refs.searchInput.focusInput).toHaveBeenCalled();
+ });
+ });
+
+ describe('handleComponentDisappear', () => {
+ it('calls action `receiveLabelsSuccess` with empty array', () => {
+ jest.spyOn(wrapper.vm, 'receiveLabelsSuccess');
+
+ wrapper.vm.handleComponentDisappear();
+
+ expect(wrapper.vm.receiveLabelsSuccess).toHaveBeenCalledWith([]);
+ });
+ });
+
+ describe('handleCreateLabelClick', () => {
+ it('calls actions `receiveLabelsSuccess` with empty array and `toggleDropdownContentsCreateView`', () => {
+ jest.spyOn(wrapper.vm, 'receiveLabelsSuccess');
+ jest.spyOn(wrapper.vm, 'toggleDropdownContentsCreateView');
+
+ wrapper.vm.handleCreateLabelClick();
+
+ expect(wrapper.vm.receiveLabelsSuccess).toHaveBeenCalledWith([]);
+ expect(wrapper.vm.toggleDropdownContentsCreateView).toHaveBeenCalled();
+ });
+ });
+
+ describe('handleKeyDown', () => {
+ it('decreases `currentHighlightItem` value by 1 when Up arrow key is pressed', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ currentHighlightItem: 1,
+ });
+
+ wrapper.vm.handleKeyDown({
+ keyCode: UP_KEY_CODE,
+ });
+
+ expect(wrapper.vm.currentHighlightItem).toBe(0);
+ });
+
+ it('increases `currentHighlightItem` value by 1 when Down arrow key is pressed', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ currentHighlightItem: 1,
+ });
+
+ wrapper.vm.handleKeyDown({
+ keyCode: DOWN_KEY_CODE,
+ });
+
+ expect(wrapper.vm.currentHighlightItem).toBe(2);
+ });
+
+ it('resets the search text when the Enter key is pressed', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ currentHighlightItem: 1,
+ searchKey: 'bug',
+ });
+
+ wrapper.vm.handleKeyDown({
+ keyCode: ENTER_KEY_CODE,
+ preventDefault: fakePreventDefault,
+ });
+
+ expect(wrapper.vm.searchKey).toBe('');
+ expect(fakePreventDefault).toHaveBeenCalled();
+ });
+
+ it('calls action `updateSelectedLabels` with currently highlighted label when Enter key is pressed', () => {
+ jest.spyOn(wrapper.vm, 'updateSelectedLabels').mockImplementation();
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ currentHighlightItem: 2,
+ });
+
+ wrapper.vm.handleKeyDown({
+ keyCode: ENTER_KEY_CODE,
+ preventDefault: fakePreventDefault,
+ });
+
+ expect(wrapper.vm.updateSelectedLabels).toHaveBeenCalledWith([mockLabels[2]]);
+ });
+
+ it('calls action `toggleDropdownContents` when Esc key is pressed', () => {
+ jest.spyOn(wrapper.vm, 'toggleDropdownContents').mockImplementation();
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ currentHighlightItem: 1,
+ });
+
+ wrapper.vm.handleKeyDown({
+ keyCode: ESC_KEY_CODE,
+ });
+
+ expect(wrapper.vm.toggleDropdownContents).toHaveBeenCalled();
+ });
+
+ it('calls action `scrollIntoViewIfNeeded` in next tick when any key is pressed', async () => {
+ jest.spyOn(wrapper.vm, 'scrollIntoViewIfNeeded').mockImplementation();
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ currentHighlightItem: 1,
+ });
+
+ wrapper.vm.handleKeyDown({
+ keyCode: DOWN_KEY_CODE,
+ });
+
+ await nextTick();
+ expect(wrapper.vm.scrollIntoViewIfNeeded).toHaveBeenCalled();
+ });
+ });
+
+ describe('handleLabelClick', () => {
+ beforeEach(() => {
+ jest.spyOn(wrapper.vm, 'updateSelectedLabels').mockImplementation();
+ });
+
+ it('calls action `updateSelectedLabels` with provided `label` param', () => {
+ wrapper.vm.handleLabelClick(mockRegularLabel);
+
+ expect(wrapper.vm.updateSelectedLabels).toHaveBeenCalledWith([mockRegularLabel]);
+ });
+
+ it('calls action `toggleDropdownContents` when `state.allowMultiselect` is false', () => {
+ jest.spyOn(wrapper.vm, 'toggleDropdownContents');
+ wrapper.vm.$store.state.allowMultiselect = false;
+
+ wrapper.vm.handleLabelClick(mockRegularLabel);
+
+ expect(wrapper.vm.toggleDropdownContents).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('renders gl-intersection-observer as component root', () => {
+ expect(wrapper.findComponent(GlIntersectionObserver).exists()).toBe(true);
+ });
+
+ it('renders gl-loading-icon component when `labelsFetchInProgress` prop is true', async () => {
+ wrapper.vm.$store.dispatch('requestLabels');
+
+ await nextTick();
+ const loadingIconEl = findLoadingIcon();
+
+ expect(loadingIconEl.exists()).toBe(true);
+ expect(loadingIconEl.attributes('class')).toContain('labels-fetch-loading');
+ });
+
+ it('renders dropdown title element', () => {
+ const titleEl = findDropdownTitle();
+
+ expect(titleEl.exists()).toBe(true);
+ expect(titleEl.text()).toBe('Assign labels');
+ });
+
+ it('does not render dropdown title element when `state.variant` is "standalone"', () => {
+ createComponent({ ...mockConfig, variant: 'standalone' });
+ expect(findDropdownTitle().exists()).toBe(false);
+ });
+
+ it('renders dropdown title element when `state.variant` is "embedded"', () => {
+ createComponent({ ...mockConfig, variant: 'embedded' });
+ expect(findDropdownTitle().exists()).toBe(true);
+ });
+
+ it('renders dropdown close button element', () => {
+ const closeButtonEl = findDropdownTitle().findComponent(GlButton);
+
+ expect(closeButtonEl.exists()).toBe(true);
+ expect(closeButtonEl.props('icon')).toBe('close');
+ });
+
+ it('renders label search input element', () => {
+ const searchInputEl = wrapper.findComponent(GlSearchBoxByType);
+
+ expect(searchInputEl.exists()).toBe(true);
+ });
+
+ it('renders label elements for all labels', () => {
+ expect(wrapper.findAllComponents(LabelItem)).toHaveLength(mockLabels.length);
+ });
+
+ it('renders label element with `highlight` set to true when value of `currentHighlightItem` is more than -1', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ currentHighlightItem: 0,
+ });
+
+ await nextTick();
+ const labelItemEl = findDropdownContent().findComponent(LabelItem);
+
+ expect(labelItemEl.attributes('highlight')).toBe('true');
+ });
+
+ it('renders element containing "No matching results" when `searchKey` does not match with any label', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ searchKey: 'abc',
+ });
+
+ await nextTick();
+ const noMatchEl = findDropdownContent().find('li');
+
+ expect(noMatchEl.isVisible()).toBe(true);
+ expect(noMatchEl.text()).toContain('No matching results');
+ });
+
+ it('renders empty content while loading', async () => {
+ wrapper.vm.$store.state.labelsFetchInProgress = true;
+
+ await nextTick();
+ const dropdownContent = findDropdownContent();
+ const loadingIcon = findLoadingIcon();
+
+ expect(dropdownContent.exists()).toBe(true);
+ expect(dropdownContent.isVisible()).toBe(true);
+ expect(loadingIcon.exists()).toBe(true);
+ expect(loadingIcon.isVisible()).toBe(true);
+ });
+
+ it('renders footer list items', () => {
+ const footerLinks = findDropdownFooter().findAllComponents(GlLink);
+ const createLabelLink = footerLinks.at(0);
+ const manageLabelsLink = footerLinks.at(1);
+
+ expect(createLabelLink.exists()).toBe(true);
+ expect(createLabelLink.text()).toBe('Create label');
+ expect(manageLabelsLink.exists()).toBe(true);
+ expect(manageLabelsLink.text()).toBe('Manage labels');
+ });
+
+ it('does not render "Create label" footer link when `state.allowLabelCreate` is `false`', async () => {
+ wrapper.vm.$store.state.allowLabelCreate = false;
+
+ await nextTick();
+ const createLabelLink = findDropdownFooter().findAllComponents(GlLink).at(0);
+
+ expect(createLabelLink.text()).not.toBe('Create label');
+ });
+
+ it('does not render footer list items when `state.variant` is "standalone"', () => {
+ createComponent({ ...mockConfig, variant: 'standalone' });
+ expect(findDropdownFooter().exists()).toBe(false);
+ });
+
+ it('does not render footer list items when `allowLabelCreate` is false and `labelsManagePath` is null', () => {
+ createComponent({
+ ...mockConfig,
+ allowLabelCreate: false,
+ labelsManagePath: null,
+ });
+ expect(findDropdownFooter().exists()).toBe(false);
+ });
+
+ it('renders footer list items when `state.variant` is "embedded"', () => {
+ expect(findDropdownFooter().exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_spec.js b/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_spec.js
new file mode 100644
index 00000000000..e9ffda7c251
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_spec.js
@@ -0,0 +1,68 @@
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import Vuex from 'vuex';
+
+import { DropdownVariant } from '~/sidebar/components/labels/labels_select_vue/constants';
+import DropdownContents from '~/sidebar/components/labels/labels_select_vue/dropdown_contents.vue';
+import labelsSelectModule from '~/sidebar/components/labels/labels_select_vue/store';
+
+import { mockConfig } from './mock_data';
+
+Vue.use(Vuex);
+
+const createComponent = (initialState = mockConfig, propsData = {}) => {
+ const store = new Vuex.Store(labelsSelectModule());
+
+ store.dispatch('setInitialState', initialState);
+
+ return shallowMount(DropdownContents, {
+ propsData,
+ store,
+ });
+};
+
+describe('DropdownContent', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('computed', () => {
+ describe('dropdownContentsView', () => {
+ it('returns string "dropdown-contents-create-view" when `showDropdownContentsCreateView` prop is `true`', () => {
+ wrapper.vm.$store.dispatch('toggleDropdownContentsCreateView');
+
+ expect(wrapper.vm.dropdownContentsView).toBe('dropdown-contents-create-view');
+ });
+
+ it('returns string "dropdown-contents-labels-view" when `showDropdownContentsCreateView` prop is `false`', () => {
+ expect(wrapper.vm.dropdownContentsView).toBe('dropdown-contents-labels-view');
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('renders component container element with class `labels-select-dropdown-contents` and no styles', () => {
+ expect(wrapper.attributes('class')).toContain('labels-select-dropdown-contents');
+ expect(wrapper.attributes('style')).toBeUndefined();
+ });
+
+ describe('when `renderOnTop` is true', () => {
+ it.each`
+ variant | expected
+ ${DropdownVariant.Sidebar} | ${'bottom: 3rem'}
+ ${DropdownVariant.Standalone} | ${'bottom: 2rem'}
+ ${DropdownVariant.Embedded} | ${'bottom: 2rem'}
+ `('renders upward for $variant variant', ({ variant, expected }) => {
+ wrapper = createComponent({ ...mockConfig, variant }, { renderOnTop: true });
+
+ expect(wrapper.attributes('style')).toContain(expected);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_title_spec.js b/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_title_spec.js
new file mode 100644
index 00000000000..6c3fda421ff
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_title_spec.js
@@ -0,0 +1,59 @@
+import { GlButton, GlLoadingIcon } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
+import Vuex from 'vuex';
+
+import DropdownTitle from '~/sidebar/components/labels/labels_select_vue/dropdown_title.vue';
+
+import labelsSelectModule from '~/sidebar/components/labels/labels_select_vue/store';
+
+import { mockConfig } from './mock_data';
+
+Vue.use(Vuex);
+
+const createComponent = (initialState = mockConfig) => {
+ const store = new Vuex.Store(labelsSelectModule());
+
+ store.dispatch('setInitialState', initialState);
+
+ return shallowMount(DropdownTitle, {
+ store,
+ propsData: {
+ labelsSelectInProgress: false,
+ },
+ });
+};
+
+describe('DropdownTitle', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('template', () => {
+ it('renders component container element with string "Labels"', () => {
+ expect(wrapper.text()).toContain('Labels');
+ });
+
+ it('renders edit link', () => {
+ const editBtnEl = wrapper.findComponent(GlButton);
+
+ expect(editBtnEl.exists()).toBe(true);
+ expect(editBtnEl.text()).toBe('Edit');
+ });
+
+ it('renders loading icon element when `labelsSelectInProgress` prop is true', async () => {
+ wrapper.setProps({
+ labelsSelectInProgress: true,
+ });
+
+ await nextTick();
+ expect(wrapper.findComponent(GlLoadingIcon).isVisible()).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_value_collapsed_spec.js b/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_value_collapsed_spec.js
new file mode 100644
index 00000000000..56f25a1c6a4
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_value_collapsed_spec.js
@@ -0,0 +1,75 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlIcon } from '@gitlab/ui';
+import { nextTick } from 'vue';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+import DropdownValueCollapsedComponent from '~/sidebar/components/labels/labels_select_vue/dropdown_value_collapsed.vue';
+
+import { mockCollapsedLabels as mockLabels, mockRegularLabel } from './mock_data';
+
+describe('DropdownValueCollapsedComponent', () => {
+ let wrapper;
+
+ const defaultProps = {
+ labels: [],
+ };
+
+ const mockManyLabels = [...mockLabels, ...mockLabels, ...mockLabels];
+
+ const createComponent = ({ props = {} } = {}) => {
+ wrapper = shallowMount(DropdownValueCollapsedComponent, {
+ propsData: { ...defaultProps, ...props },
+ directives: {
+ GlTooltip: createMockDirective(),
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findGlIcon = () => wrapper.findComponent(GlIcon);
+ const getTooltip = () => getBinding(wrapper.element, 'gl-tooltip');
+
+ describe('template', () => {
+ it('renders tags icon element', () => {
+ createComponent();
+
+ expect(findGlIcon().exists()).toBe(true);
+ });
+
+ it('emits onValueClick event on click', async () => {
+ createComponent();
+
+ wrapper.trigger('click');
+
+ await nextTick();
+
+ expect(wrapper.emitted('onValueClick')[0]).toBeDefined();
+ });
+
+ describe.each`
+ scenario | labels | expectedResult | expectedText
+ ${'`labels` is empty'} | ${[]} | ${'default text'} | ${'Labels'}
+ ${'`labels` has 1 item'} | ${[mockRegularLabel]} | ${'label name'} | ${'Foo Label'}
+ ${'`labels` has 2 items'} | ${mockLabels} | ${'comma separated label names'} | ${'Foo Label, Foo::Bar'}
+ ${'`labels` has more than 5 items'} | ${mockManyLabels} | ${'comma separated label names with "and more" phrase'} | ${'Foo Label, Foo::Bar, Foo Label, Foo::Bar, Foo Label, and 1 more'}
+ `('when $scenario', ({ labels, expectedResult, expectedText }) => {
+ beforeEach(() => {
+ createComponent({
+ props: {
+ labels,
+ },
+ });
+ });
+
+ it('renders labels count', () => {
+ expect(wrapper.text()).toBe(`${labels.length}`);
+ });
+
+ it(`renders "${expectedResult}" as tooltip`, () => {
+ expect(getTooltip().value).toBe(expectedText);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_value_spec.js b/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_value_spec.js
new file mode 100644
index 00000000000..a1ccc9d2ab1
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_value_spec.js
@@ -0,0 +1,99 @@
+import { GlLabel } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import Vuex from 'vuex';
+
+import DropdownValue from '~/sidebar/components/labels/labels_select_vue/dropdown_value.vue';
+
+import labelsSelectModule from '~/sidebar/components/labels/labels_select_vue/store';
+
+import { mockConfig, mockLabels, mockRegularLabel, mockScopedLabel } from './mock_data';
+
+Vue.use(Vuex);
+
+describe('DropdownValue', () => {
+ let wrapper;
+
+ const findAllLabels = () => wrapper.findAllComponents(GlLabel);
+ const findLabel = (index) => findAllLabels().at(index).props('title');
+
+ const createComponent = (initialState = {}, slots = {}) => {
+ const store = new Vuex.Store(labelsSelectModule());
+
+ store.dispatch('setInitialState', { ...mockConfig, ...initialState });
+
+ wrapper = shallowMount(DropdownValue, {
+ store,
+ slots,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('methods', () => {
+ describe('labelFilterUrl', () => {
+ it('returns a label filter URL based on provided label param', () => {
+ createComponent();
+
+ expect(wrapper.vm.labelFilterUrl(mockRegularLabel)).toBe(
+ '/gitlab-org/my-project/issues?label_name[]=Foo%20Label',
+ );
+ });
+ });
+
+ describe('scopedLabel', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('returns `true` when provided label param is a scoped label', () => {
+ expect(wrapper.vm.scopedLabel(mockScopedLabel)).toBe(true);
+ });
+
+ it('returns `false` when provided label param is a regular label', () => {
+ expect(wrapper.vm.scopedLabel(mockRegularLabel)).toBe(false);
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('renders class `has-labels` on component container element when `selectedLabels` is not empty', () => {
+ createComponent();
+
+ expect(wrapper.attributes('class')).toContain('has-labels');
+ });
+
+ it('renders element containing `None` when `selectedLabels` is empty', () => {
+ createComponent(
+ {
+ selectedLabels: [],
+ },
+ {
+ default: 'None',
+ },
+ );
+ const noneEl = wrapper.find('span.text-secondary');
+
+ expect(noneEl.exists()).toBe(true);
+ expect(noneEl.text()).toBe('None');
+ });
+
+ it('renders labels when `selectedLabels` is not empty', () => {
+ createComponent();
+
+ expect(findAllLabels()).toHaveLength(2);
+ });
+
+ it('orders scoped labels first', () => {
+ createComponent({ selectedLabels: mockLabels });
+
+ expect(findAllLabels()).toHaveLength(mockLabels.length);
+ expect(findLabel(0)).toBe('Foo::Bar');
+ expect(findLabel(1)).toBe('Boog');
+ expect(findLabel(2)).toBe('Bug');
+ expect(findLabel(3)).toBe('Foo Label');
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_vue/label_item_spec.js b/spec/frontend/sidebar/components/labels/labels_select_vue/label_item_spec.js
new file mode 100644
index 00000000000..e14c0e308ce
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_vue/label_item_spec.js
@@ -0,0 +1,92 @@
+import { GlLink } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+
+import LabelItem from '~/sidebar/components/labels/labels_select_vue/label_item.vue';
+import { mockRegularLabel } from './mock_data';
+
+const mockLabel = { ...mockRegularLabel, set: true };
+
+const createComponent = ({
+ label = mockLabel,
+ isLabelSet = mockLabel.set,
+ highlight = true,
+} = {}) =>
+ shallowMount(LabelItem, {
+ propsData: {
+ label,
+ isLabelSet,
+ highlight,
+ },
+ });
+
+describe('LabelItem', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('template', () => {
+ it('renders gl-link component', () => {
+ expect(wrapper.findComponent(GlLink).exists()).toBe(true);
+ });
+
+ it('renders component root with class `is-focused` when `highlight` prop is true', () => {
+ const wrapperTemp = createComponent({
+ highlight: true,
+ });
+
+ expect(wrapperTemp.classes()).toContain('is-focused');
+
+ wrapperTemp.destroy();
+ });
+
+ it.each`
+ isLabelSet | isLabelIndeterminate | testId | iconName
+ ${true} | ${false} | ${'checked-icon'} | ${'mobile-issue-close'}
+ ${false} | ${true} | ${'indeterminate-icon'} | ${'dash'}
+ `(
+ 'renders visible gl-icon component when `isLabelSet` prop is $isLabelSet and `isLabelIndeterminate` is $isLabelIndeterminate',
+ ({ isLabelSet, isLabelIndeterminate, testId, iconName }) => {
+ const wrapperTemp = createComponent({
+ isLabelSet,
+ isLabelIndeterminate,
+ });
+
+ const iconEl = wrapperTemp.find(`[data-testid="${testId}"]`);
+
+ expect(iconEl.isVisible()).toBe(true);
+ expect(iconEl.props('name')).toBe(iconName);
+
+ wrapperTemp.destroy();
+ },
+ );
+
+ it('renders visible span element as placeholder instead of gl-icon when `isLabelSet` prop is false', () => {
+ const wrapperTemp = createComponent({
+ isLabelSet: false,
+ });
+
+ const placeholderEl = wrapperTemp.find('[data-testid="no-icon"]');
+
+ expect(placeholderEl.isVisible()).toBe(true);
+
+ wrapperTemp.destroy();
+ });
+
+ it('renders label color element', () => {
+ const colorEl = wrapper.find('[data-testid="label-color-box"]');
+
+ expect(colorEl.exists()).toBe(true);
+ expect(colorEl.attributes('style')).toBe('background-color: rgb(186, 218, 85);');
+ });
+
+ it('renders label title', () => {
+ expect(wrapper.text()).toContain(mockLabel.title);
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_vue/labels_select_root_spec.js b/spec/frontend/sidebar/components/labels/labels_select_vue/labels_select_root_spec.js
new file mode 100644
index 00000000000..a3b10c18374
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_vue/labels_select_root_spec.js
@@ -0,0 +1,231 @@
+import { shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
+import Vuex from 'vuex';
+
+import { isInViewport } from '~/lib/utils/common_utils';
+import { DropdownVariant } from '~/sidebar/components/labels/labels_select_vue/constants';
+import DropdownButton from '~/sidebar/components/labels/labels_select_vue/dropdown_button.vue';
+import DropdownContents from '~/sidebar/components/labels/labels_select_vue/dropdown_contents.vue';
+import DropdownTitle from '~/sidebar/components/labels/labels_select_vue/dropdown_title.vue';
+import DropdownValue from '~/sidebar/components/labels/labels_select_vue/dropdown_value.vue';
+import DropdownValueCollapsed from '~/sidebar/components/labels/labels_select_vue/dropdown_value_collapsed.vue';
+import LabelsSelectRoot from '~/sidebar/components/labels/labels_select_vue/labels_select_root.vue';
+
+import labelsSelectModule from '~/sidebar/components/labels/labels_select_vue/store';
+
+import { mockConfig } from './mock_data';
+
+jest.mock('~/lib/utils/common_utils', () => ({
+ isInViewport: jest.fn().mockReturnValue(true),
+}));
+
+Vue.use(Vuex);
+
+describe('LabelsSelectRoot', () => {
+ let wrapper;
+ let store;
+
+ const createComponent = (config = mockConfig, slots = {}) => {
+ wrapper = shallowMount(LabelsSelectRoot, {
+ slots,
+ store,
+ propsData: config,
+ stubs: {
+ 'dropdown-contents': DropdownContents,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ store = new Vuex.Store(labelsSelectModule());
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('methods', () => {
+ describe('handleVuexActionDispatch', () => {
+ const touchedLabels = [
+ {
+ id: 2,
+ touched: true,
+ },
+ ];
+
+ it('calls `handleDropdownClose` when params `action.type` is `toggleDropdownContents` and state has `showDropdownButton` & `showDropdownContents` props `false`', () => {
+ createComponent();
+
+ wrapper.vm.handleVuexActionDispatch(
+ { type: 'toggleDropdownContents' },
+ {
+ showDropdownButton: false,
+ showDropdownContents: false,
+ labels: [{ id: 1 }, { id: 2, touched: true }],
+ },
+ );
+
+ // We're utilizing `onDropdownClose` event emitted from the component to always include `touchedLabels`
+ // while the first param of the method is the labels list which were added/removed.
+ expect(wrapper.emitted('updateSelectedLabels')).toHaveLength(1);
+ expect(wrapper.emitted('updateSelectedLabels')[0]).toEqual([touchedLabels]);
+ expect(wrapper.emitted('onDropdownClose')).toHaveLength(1);
+ expect(wrapper.emitted('onDropdownClose')[0]).toEqual([touchedLabels]);
+ });
+
+ it('calls `handleDropdownClose` with state.labels filterd using `set` prop when dropdown variant is `embedded`', () => {
+ createComponent({
+ ...mockConfig,
+ variant: 'embedded',
+ });
+
+ wrapper.vm.handleVuexActionDispatch(
+ { type: 'toggleDropdownContents' },
+ {
+ showDropdownButton: false,
+ showDropdownContents: false,
+ labels: [{ id: 1 }, { id: 2, set: true }],
+ },
+ );
+
+ expect(wrapper.emitted('updateSelectedLabels')).toHaveLength(1);
+ expect(wrapper.emitted('updateSelectedLabels')[0]).toEqual([
+ [
+ {
+ id: 2,
+ set: true,
+ },
+ ],
+ ]);
+ expect(wrapper.emitted('onDropdownClose')).toHaveLength(1);
+ expect(wrapper.emitted('onDropdownClose')[0]).toEqual([[]]);
+ });
+ });
+
+ describe('handleCollapsedValueClick', () => {
+ it('emits `toggleCollapse` event on component', () => {
+ createComponent();
+ wrapper.vm.handleCollapsedValueClick();
+ expect(wrapper.emitted().toggleCollapse).toHaveLength(1);
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('renders component with classes `labels-select-wrapper position-relative`', () => {
+ createComponent();
+ expect(wrapper.attributes('class')).toContain('labels-select-wrapper position-relative');
+ });
+
+ it.each`
+ variant | cssClass
+ ${'standalone'} | ${'is-standalone'}
+ ${'embedded'} | ${'is-embedded'}
+ `(
+ 'renders component root element with CSS class `$cssClass` when `state.variant` is "$variant"',
+ async ({ variant, cssClass }) => {
+ createComponent({
+ ...mockConfig,
+ variant,
+ });
+
+ await nextTick();
+ expect(wrapper.classes()).toContain(cssClass);
+ },
+ );
+
+ it('renders `dropdown-value-collapsed` component when `allowLabelCreate` prop is `true`', async () => {
+ createComponent();
+ await nextTick();
+ expect(wrapper.findComponent(DropdownValueCollapsed).exists()).toBe(true);
+ });
+
+ it('renders `dropdown-title` component', async () => {
+ createComponent();
+ await nextTick();
+ expect(wrapper.findComponent(DropdownTitle).exists()).toBe(true);
+ });
+
+ it('renders `dropdown-value` component', async () => {
+ createComponent(mockConfig, {
+ default: 'None',
+ });
+ await nextTick();
+
+ const valueComp = wrapper.findComponent(DropdownValue);
+
+ expect(valueComp.exists()).toBe(true);
+ expect(valueComp.text()).toBe('None');
+ });
+
+ it('renders `dropdown-button` component when `showDropdownButton` prop is `true`', async () => {
+ createComponent();
+ wrapper.vm.$store.dispatch('toggleDropdownButton');
+ await nextTick();
+ expect(wrapper.findComponent(DropdownButton).exists()).toBe(true);
+ });
+
+ it('renders `dropdown-contents` component when `showDropdownButton` & `showDropdownContents` prop is `true`', async () => {
+ createComponent();
+ wrapper.vm.$store.dispatch('toggleDropdownContents');
+ await nextTick();
+ expect(wrapper.findComponent(DropdownContents).exists()).toBe(true);
+ });
+
+ describe('sets content direction based on viewport', () => {
+ describe.each(Object.values(DropdownVariant))(
+ 'when labels variant is "%s"',
+ ({ variant }) => {
+ beforeEach(() => {
+ createComponent({ ...mockConfig, variant });
+ wrapper.vm.$store.dispatch('toggleDropdownContents');
+ });
+
+ it('set direction when out of viewport', async () => {
+ isInViewport.mockImplementation(() => false);
+ wrapper.vm.setContentIsOnViewport(wrapper.vm.$store.state);
+
+ await nextTick();
+ expect(wrapper.findComponent(DropdownContents).props('renderOnTop')).toBe(true);
+ });
+
+ it('does not set direction when inside of viewport', async () => {
+ isInViewport.mockImplementation(() => true);
+ wrapper.vm.setContentIsOnViewport(wrapper.vm.$store.state);
+
+ await nextTick();
+ expect(wrapper.findComponent(DropdownContents).props('renderOnTop')).toBe(false);
+ });
+ },
+ );
+ });
+ });
+
+ it('calls toggleDropdownContents action when isEditing prop is changing to true', async () => {
+ createComponent();
+
+ jest.spyOn(store, 'dispatch').mockResolvedValue();
+ await wrapper.setProps({ isEditing: true });
+
+ expect(store.dispatch).toHaveBeenCalledWith('toggleDropdownContents');
+ });
+
+ it('does not call toggleDropdownContents action when isEditing prop is changing to false', async () => {
+ createComponent();
+
+ jest.spyOn(store, 'dispatch').mockResolvedValue();
+ await wrapper.setProps({ isEditing: false });
+
+ expect(store.dispatch).not.toHaveBeenCalled();
+ });
+
+ it('calls updateLabelsSetState after selected labels were updated', async () => {
+ createComponent();
+
+ jest.spyOn(store, 'dispatch').mockResolvedValue();
+ await wrapper.setProps({ selectedLabels: [] });
+ jest.advanceTimersByTime(100);
+
+ expect(store.dispatch).toHaveBeenCalledWith('updateLabelsSetState');
+ });
+});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/mock_data.js b/spec/frontend/sidebar/components/labels/labels_select_vue/mock_data.js
index 884bc4684ba..884bc4684ba 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/mock_data.js
+++ b/spec/frontend/sidebar/components/labels/labels_select_vue/mock_data.js
diff --git a/spec/frontend/sidebar/components/labels/labels_select_vue/store/actions_spec.js b/spec/frontend/sidebar/components/labels/labels_select_vue/store/actions_spec.js
new file mode 100644
index 00000000000..0e0024aa6c2
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_vue/store/actions_spec.js
@@ -0,0 +1,265 @@
+import MockAdapter from 'axios-mock-adapter';
+
+import testAction from 'helpers/vuex_action_helper';
+import { createAlert } from '~/flash';
+import axios from '~/lib/utils/axios_utils';
+import * as actions from '~/sidebar/components/labels/labels_select_vue/store/actions';
+import * as types from '~/sidebar/components/labels/labels_select_vue/store/mutation_types';
+import defaultState from '~/sidebar/components/labels/labels_select_vue/store/state';
+
+jest.mock('~/flash');
+
+describe('LabelsSelect Actions', () => {
+ let state;
+ const mockInitialState = {
+ labels: [],
+ selectedLabels: [],
+ };
+
+ beforeEach(() => {
+ state = { ...defaultState() };
+ });
+
+ describe('setInitialState', () => {
+ it('sets initial store state', () => {
+ return testAction(
+ actions.setInitialState,
+ mockInitialState,
+ state,
+ [{ type: types.SET_INITIAL_STATE, payload: mockInitialState }],
+ [],
+ );
+ });
+ });
+
+ describe('toggleDropdownButton', () => {
+ it('toggles dropdown button', () => {
+ return testAction(
+ actions.toggleDropdownButton,
+ {},
+ state,
+ [{ type: types.TOGGLE_DROPDOWN_BUTTON }],
+ [],
+ );
+ });
+ });
+
+ describe('toggleDropdownContents', () => {
+ it('toggles dropdown contents', () => {
+ return testAction(
+ actions.toggleDropdownContents,
+ {},
+ state,
+ [{ type: types.TOGGLE_DROPDOWN_CONTENTS }],
+ [],
+ );
+ });
+ });
+
+ describe('toggleDropdownContentsCreateView', () => {
+ it('toggles dropdown create view', () => {
+ return testAction(
+ actions.toggleDropdownContentsCreateView,
+ {},
+ state,
+ [{ type: types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW }],
+ [],
+ );
+ });
+ });
+
+ describe('requestLabels', () => {
+ it('sets value of `state.labelsFetchInProgress` to `true`', () => {
+ return testAction(actions.requestLabels, {}, state, [{ type: types.REQUEST_LABELS }], []);
+ });
+ });
+
+ describe('receiveLabelsSuccess', () => {
+ it('sets provided labels to `state.labels`', () => {
+ const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
+
+ return testAction(
+ actions.receiveLabelsSuccess,
+ labels,
+ state,
+ [{ type: types.RECEIVE_SET_LABELS_SUCCESS, payload: labels }],
+ [],
+ );
+ });
+ });
+
+ describe('receiveLabelsFailure', () => {
+ it('sets value `state.labelsFetchInProgress` to `false`', () => {
+ return testAction(
+ actions.receiveLabelsFailure,
+ {},
+ state,
+ [{ type: types.RECEIVE_SET_LABELS_FAILURE }],
+ [],
+ );
+ });
+
+ it('shows flash error', () => {
+ actions.receiveLabelsFailure({ commit: () => {} });
+
+ expect(createAlert).toHaveBeenCalledWith({ message: 'Error fetching labels.' });
+ });
+ });
+
+ describe('fetchLabels', () => {
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ state.labelsFetchPath = 'labels.json';
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('on success', () => {
+ it('dispatches `requestLabels` & `receiveLabelsSuccess` actions', () => {
+ const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
+ mock.onGet(/labels.json/).replyOnce(200, labels);
+
+ return testAction(
+ actions.fetchLabels,
+ {},
+ state,
+ [],
+ [{ type: 'requestLabels' }, { type: 'receiveLabelsSuccess', payload: labels }],
+ );
+ });
+ });
+
+ describe('on failure', () => {
+ it('dispatches `requestLabels` & `receiveLabelsFailure` actions', () => {
+ mock.onGet(/labels.json/).replyOnce(500, {});
+
+ return testAction(
+ actions.fetchLabels,
+ {},
+ state,
+ [],
+ [{ type: 'requestLabels' }, { type: 'receiveLabelsFailure' }],
+ );
+ });
+ });
+ });
+
+ describe('requestCreateLabel', () => {
+ it('sets value `state.labelCreateInProgress` to `true`', () => {
+ return testAction(
+ actions.requestCreateLabel,
+ {},
+ state,
+ [{ type: types.REQUEST_CREATE_LABEL }],
+ [],
+ );
+ });
+ });
+
+ describe('receiveCreateLabelSuccess', () => {
+ it('sets value `state.labelCreateInProgress` to `false`', () => {
+ return testAction(
+ actions.receiveCreateLabelSuccess,
+ {},
+ state,
+ [{ type: types.RECEIVE_CREATE_LABEL_SUCCESS }],
+ [],
+ );
+ });
+ });
+
+ describe('receiveCreateLabelFailure', () => {
+ it('sets value `state.labelCreateInProgress` to `false`', () => {
+ return testAction(
+ actions.receiveCreateLabelFailure,
+ {},
+ state,
+ [{ type: types.RECEIVE_CREATE_LABEL_FAILURE }],
+ [],
+ );
+ });
+
+ it('shows flash error', () => {
+ actions.receiveCreateLabelFailure({ commit: () => {} });
+
+ expect(createAlert).toHaveBeenCalledWith({ message: 'Error creating label.' });
+ });
+ });
+
+ describe('createLabel', () => {
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ state.labelsManagePath = 'labels.json';
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('on success', () => {
+ it('dispatches `requestCreateLabel`, `fetchLabels` & `receiveCreateLabelSuccess` & `toggleDropdownContentsCreateView` actions', () => {
+ const label = { id: 1 };
+ mock.onPost(/labels.json/).replyOnce(200, label);
+
+ return testAction(
+ actions.createLabel,
+ {},
+ state,
+ [],
+ [
+ { type: 'requestCreateLabel' },
+ { payload: { refetch: true }, type: 'fetchLabels' },
+ { type: 'receiveCreateLabelSuccess' },
+ { type: 'toggleDropdownContentsCreateView' },
+ ],
+ );
+ });
+ });
+
+ describe('on failure', () => {
+ it('dispatches `requestCreateLabel` & `receiveCreateLabelFailure` actions', () => {
+ mock.onPost(/labels.json/).replyOnce(500, {});
+
+ return testAction(
+ actions.createLabel,
+ {},
+ state,
+ [],
+ [{ type: 'requestCreateLabel' }, { type: 'receiveCreateLabelFailure' }],
+ );
+ });
+ });
+ });
+
+ describe('updateSelectedLabels', () => {
+ it('updates `state.labels` based on provided `labels` param', () => {
+ const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
+
+ return testAction(
+ actions.updateSelectedLabels,
+ labels,
+ state,
+ [{ type: types.UPDATE_SELECTED_LABELS, payload: { labels } }],
+ [],
+ );
+ });
+ });
+
+ describe('updateLabelsSetState', () => {
+ it('updates labels `set` state to match `selectedLabels`', () => {
+ testAction(
+ actions.updateLabelsSetState,
+ {},
+ state,
+ [{ type: types.UPDATE_LABELS_SET_STATE }],
+ [],
+ );
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_vue/store/getters_spec.js b/spec/frontend/sidebar/components/labels/labels_select_vue/store/getters_spec.js
new file mode 100644
index 00000000000..e32256831a3
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_vue/store/getters_spec.js
@@ -0,0 +1,74 @@
+import * as getters from '~/sidebar/components/labels/labels_select_vue/store/getters';
+
+describe('LabelsSelect Getters', () => {
+ describe('dropdownButtonText', () => {
+ it.each`
+ labelType | dropdownButtonText | expected
+ ${'default'} | ${''} | ${'Label'}
+ ${'custom'} | ${'Custom label'} | ${'Custom label'}
+ `(
+ 'returns $labelType text when state.labels has no selected labels',
+ ({ dropdownButtonText, expected }) => {
+ const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
+ const selectedLabels = [];
+ const state = { labels, selectedLabels, dropdownButtonText };
+
+ expect(getters.dropdownButtonText(state, {})).toBe(expected);
+ },
+ );
+
+ describe.each`
+ dropdownVariant | isDropdownVariantSidebar | isDropdownVariantEmbedded
+ ${'sidebar'} | ${true} | ${false}
+ ${'embedded'} | ${false} | ${true}
+ `(
+ 'when dropdown variant is $dropdownVariant',
+ ({ isDropdownVariantSidebar, isDropdownVariantEmbedded }) => {
+ it('returns label title when state.labels has only 1 label', () => {
+ const labels = [{ id: 1, title: 'Foobar', set: true }];
+
+ expect(
+ getters.dropdownButtonText(
+ { labels },
+ { isDropdownVariantSidebar, isDropdownVariantEmbedded },
+ ),
+ ).toBe('Foobar');
+ });
+
+ it('returns first label title and remaining labels count when state.labels has more than 1 label', () => {
+ const labels = [
+ { id: 1, title: 'Foo', set: true },
+ { id: 2, title: 'Bar', set: true },
+ ];
+
+ expect(
+ getters.dropdownButtonText(
+ { labels },
+ { isDropdownVariantSidebar, isDropdownVariantEmbedded },
+ ),
+ ).toBe('Foo +1 more');
+ });
+ },
+ );
+ });
+
+ describe('selectedLabelsList', () => {
+ it('returns array of IDs of all labels within `state.selectedLabels`', () => {
+ const selectedLabels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
+
+ expect(getters.selectedLabelsList({ selectedLabels })).toEqual([1, 2, 3, 4]);
+ });
+ });
+
+ describe('isDropdownVariantSidebar', () => {
+ it('returns `true` when `state.variant` is "sidebar"', () => {
+ expect(getters.isDropdownVariantSidebar({ variant: 'sidebar' })).toBe(true);
+ });
+ });
+
+ describe('isDropdownVariantStandalone', () => {
+ it('returns `true` when `state.variant` is "standalone"', () => {
+ expect(getters.isDropdownVariantStandalone({ variant: 'standalone' })).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_vue/store/mutations_spec.js b/spec/frontend/sidebar/components/labels/labels_select_vue/store/mutations_spec.js
new file mode 100644
index 00000000000..cee5d2e77d1
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_vue/store/mutations_spec.js
@@ -0,0 +1,232 @@
+import { cloneDeep } from 'lodash';
+import * as types from '~/sidebar/components/labels/labels_select_vue/store/mutation_types';
+import mutations from '~/sidebar/components/labels/labels_select_vue/store/mutations';
+
+describe('LabelsSelect Mutations', () => {
+ describe(`${types.SET_INITIAL_STATE}`, () => {
+ it('initializes provided props to store state', () => {
+ const state = {};
+ mutations[types.SET_INITIAL_STATE](state, {
+ labels: 'foo',
+ });
+
+ expect(state.labels).toEqual('foo');
+ });
+ });
+
+ describe(`${types.TOGGLE_DROPDOWN_BUTTON}`, () => {
+ it('toggles value of `state.showDropdownButton`', () => {
+ const state = {
+ showDropdownButton: false,
+ };
+ mutations[types.TOGGLE_DROPDOWN_BUTTON](state);
+
+ expect(state.showDropdownButton).toBe(true);
+ });
+ });
+
+ describe(`${types.TOGGLE_DROPDOWN_CONTENTS}`, () => {
+ it('toggles value of `state.showDropdownButton` when `state.dropdownOnly` is false', () => {
+ const state = {
+ dropdownOnly: false,
+ showDropdownButton: false,
+ variant: 'sidebar',
+ };
+ mutations[types.TOGGLE_DROPDOWN_CONTENTS](state);
+
+ expect(state.showDropdownButton).toBe(true);
+ });
+
+ it('toggles value of `state.showDropdownContents`', () => {
+ const state = {
+ showDropdownContents: false,
+ };
+ mutations[types.TOGGLE_DROPDOWN_CONTENTS](state);
+
+ expect(state.showDropdownContents).toBe(true);
+ });
+
+ it('sets value of `state.showDropdownContentsCreateView` to `false` when `showDropdownContents` is true', () => {
+ const state = {
+ showDropdownContents: false,
+ showDropdownContentsCreateView: true,
+ };
+ mutations[types.TOGGLE_DROPDOWN_CONTENTS](state);
+
+ expect(state.showDropdownContentsCreateView).toBe(false);
+ });
+ });
+
+ describe(`${types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW}`, () => {
+ it('toggles value of `state.showDropdownContentsCreateView`', () => {
+ const state = {
+ showDropdownContentsCreateView: false,
+ };
+ mutations[types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW](state);
+
+ expect(state.showDropdownContentsCreateView).toBe(true);
+ });
+ });
+
+ describe(`${types.REQUEST_LABELS}`, () => {
+ it('sets value of `state.labelsFetchInProgress` to true', () => {
+ const state = {
+ labelsFetchInProgress: false,
+ };
+ mutations[types.REQUEST_LABELS](state);
+
+ expect(state.labelsFetchInProgress).toBe(true);
+ });
+ });
+
+ describe(`${types.RECEIVE_SET_LABELS_SUCCESS}`, () => {
+ const selectedLabels = [
+ { id: 2, set: true },
+ { id: 4, set: true },
+ ];
+ const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
+
+ it('sets value of `state.labelsFetchInProgress` to false', () => {
+ const state = {
+ selectedLabels,
+ labelsFetchInProgress: true,
+ };
+ mutations[types.RECEIVE_SET_LABELS_SUCCESS](state, labels);
+
+ expect(state.labelsFetchInProgress).toBe(false);
+ });
+
+ it('sets provided `labels` to `state.labels` along with `set` prop based on `state.selectedLabels`', () => {
+ const selectedLabelIds = selectedLabels.map((label) => label.id);
+ const state = {
+ selectedLabels,
+ labelsFetchInProgress: true,
+ };
+ mutations[types.RECEIVE_SET_LABELS_SUCCESS](state, labels);
+
+ state.labels.forEach((label) => {
+ if (selectedLabelIds.includes(label.id)) {
+ expect(label.set).toBe(true);
+ }
+ });
+ });
+ });
+
+ describe(`${types.RECEIVE_SET_LABELS_FAILURE}`, () => {
+ it('sets value of `state.labelsFetchInProgress` to false', () => {
+ const state = {
+ labelsFetchInProgress: true,
+ };
+ mutations[types.RECEIVE_SET_LABELS_FAILURE](state);
+
+ expect(state.labelsFetchInProgress).toBe(false);
+ });
+ });
+
+ describe(`${types.REQUEST_CREATE_LABEL}`, () => {
+ it('sets value of `state.labelCreateInProgress` to true', () => {
+ const state = {
+ labelCreateInProgress: false,
+ };
+ mutations[types.REQUEST_CREATE_LABEL](state);
+
+ expect(state.labelCreateInProgress).toBe(true);
+ });
+ });
+
+ describe(`${types.RECEIVE_CREATE_LABEL_SUCCESS}`, () => {
+ it('sets value of `state.labelCreateInProgress` to false', () => {
+ const state = {
+ labelCreateInProgress: false,
+ };
+ mutations[types.RECEIVE_CREATE_LABEL_SUCCESS](state);
+
+ expect(state.labelCreateInProgress).toBe(false);
+ });
+ });
+
+ describe(`${types.RECEIVE_CREATE_LABEL_FAILURE}`, () => {
+ it('sets value of `state.labelCreateInProgress` to false', () => {
+ const state = {
+ labelCreateInProgress: false,
+ };
+ mutations[types.RECEIVE_CREATE_LABEL_FAILURE](state);
+
+ expect(state.labelCreateInProgress).toBe(false);
+ });
+ });
+
+ describe(`${types.UPDATE_SELECTED_LABELS}`, () => {
+ const labels = [
+ { id: 1, title: 'scoped' },
+ { id: 2, title: 'scoped::label::one', set: false },
+ { id: 3, title: 'scoped::label::two', set: false },
+ { id: 4, title: 'scoped::label::three', set: true },
+ { id: 5, title: 'scoped::one', set: false },
+ { id: 6, title: 'scoped::two', set: false },
+ { id: 7, title: 'scoped::three', set: true },
+ { id: 8, title: '' },
+ ];
+
+ it.each`
+ label | labelGroupIds
+ ${labels[0]} | ${[]}
+ ${labels[1]} | ${[labels[2], labels[3]]}
+ ${labels[2]} | ${[labels[1], labels[3]]}
+ ${labels[3]} | ${[labels[1], labels[2]]}
+ ${labels[4]} | ${[labels[5], labels[6]]}
+ ${labels[5]} | ${[labels[4], labels[6]]}
+ ${labels[6]} | ${[labels[4], labels[5]]}
+ ${labels[7]} | ${[]}
+ `('updates `touched` and `set` props for $label.title', ({ label, labelGroupIds }) => {
+ const state = { labels: cloneDeep(labels) };
+
+ mutations[types.UPDATE_SELECTED_LABELS](state, { labels: [{ id: label.id }] });
+
+ expect(state.labels[label.id - 1]).toMatchObject({
+ touched: true,
+ set: !labels[label.id - 1].set,
+ });
+
+ labelGroupIds.forEach((l) => {
+ expect(state.labels[l.id - 1].touched).toBeUndefined();
+ expect(state.labels[l.id - 1].set).toBe(false);
+ });
+ });
+ it('allows selection of multiple scoped labels', () => {
+ const state = { labels: cloneDeep(labels), allowMultipleScopedLabels: true };
+
+ mutations[types.UPDATE_SELECTED_LABELS](state, { labels: [{ id: labels[4].id }] });
+ mutations[types.UPDATE_SELECTED_LABELS](state, { labels: [{ id: labels[5].id }] });
+
+ expect(state.labels[4].set).toBe(true);
+ expect(state.labels[5].set).toBe(true);
+ expect(state.labels[6].set).toBe(true);
+ });
+ });
+
+ describe(`${types.UPDATE_LABELS_SET_STATE}`, () => {
+ it('updates labels `set` state to match selected labels', () => {
+ const state = {
+ labels: [
+ { id: 1, title: 'scoped::test', set: false, indeterminate: false },
+ { id: 2, title: 'scoped::one', set: true, indeterminate: false, touched: true },
+ { id: 3, title: '', set: false, indeterminate: false },
+ { id: 4, title: '', set: false, indeterminate: false },
+ ],
+ selectedLabels: [
+ { id: 1, set: true },
+ { id: 3, set: true },
+ ],
+ };
+ mutations[types.UPDATE_LABELS_SET_STATE](state);
+
+ expect(state.labels).toEqual([
+ { id: 1, title: 'scoped::test', set: true, indeterminate: false },
+ { id: 2, title: 'scoped::one', set: false, indeterminate: false, touched: true },
+ { id: 3, title: '', set: true, indeterminate: false },
+ { id: 4, title: '', set: false, indeterminate: false },
+ ]);
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view_spec.js b/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view_spec.js
new file mode 100644
index 00000000000..79b164b0ea7
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view_spec.js
@@ -0,0 +1,239 @@
+import { GlAlert, GlLoadingIcon, GlLink } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import { createAlert } from '~/flash';
+import { workspaceLabelsQueries } from '~/sidebar/constants';
+import DropdownContentsCreateView from '~/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view.vue';
+import createLabelMutation from '~/sidebar/components/labels/labels_select_widget/graphql/create_label.mutation.graphql';
+import {
+ mockRegularLabel,
+ mockSuggestedColors,
+ createLabelSuccessfulResponse,
+ workspaceLabelsQueryResponse,
+} from './mock_data';
+
+jest.mock('~/flash');
+
+const colors = Object.keys(mockSuggestedColors);
+
+Vue.use(VueApollo);
+
+const userRecoverableError = {
+ ...createLabelSuccessfulResponse,
+ errors: ['Houston, we have a problem'],
+};
+
+const titleTakenError = {
+ data: {
+ labelCreate: {
+ label: mockRegularLabel,
+ errors: ['Title has already been taken'],
+ },
+ },
+};
+
+const createLabelSuccessHandler = jest.fn().mockResolvedValue(createLabelSuccessfulResponse);
+const createLabelUserRecoverableErrorHandler = jest.fn().mockResolvedValue(userRecoverableError);
+const createLabelDuplicateErrorHandler = jest.fn().mockResolvedValue(titleTakenError);
+const createLabelErrorHandler = jest.fn().mockRejectedValue('Houston, we have a problem');
+
+describe('DropdownContentsCreateView', () => {
+ let wrapper;
+
+ const findAllColors = () => wrapper.findAllComponents(GlLink);
+ const findSelectedColor = () => wrapper.find('[data-testid="selected-color"]');
+ const findSelectedColorText = () => wrapper.find('[data-testid="selected-color-text"]');
+ const findCreateButton = () => wrapper.find('[data-testid="create-button"]');
+ const findCancelButton = () => wrapper.find('[data-testid="cancel-button"]');
+ const findLabelTitleInput = () => wrapper.find('[data-testid="label-title-input"]');
+
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+
+ const fillLabelAttributes = () => {
+ findLabelTitleInput().vm.$emit('input', 'Test title');
+ findAllColors().at(0).vm.$emit('click', new Event('mouseclick'));
+ };
+
+ const createComponent = ({
+ mutationHandler = createLabelSuccessHandler,
+ labelCreateType = 'project',
+ workspaceType = 'project',
+ } = {}) => {
+ const mockApollo = createMockApollo([[createLabelMutation, mutationHandler]]);
+ mockApollo.clients.defaultClient.cache.writeQuery({
+ query: workspaceLabelsQueries[workspaceType].query,
+ data: workspaceLabelsQueryResponse.data,
+ variables: {
+ fullPath: '',
+ searchTerm: '',
+ },
+ });
+
+ wrapper = shallowMount(DropdownContentsCreateView, {
+ apolloProvider: mockApollo,
+ propsData: {
+ fullPath: '',
+ attrWorkspacePath: '',
+ labelCreateType,
+ workspaceType,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ gon.suggested_label_colors = mockSuggestedColors;
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders a palette of 21 colors', () => {
+ createComponent();
+ expect(findAllColors()).toHaveLength(21);
+ });
+
+ it('selects a color after clicking on colored block', async () => {
+ createComponent();
+ expect(findSelectedColor().attributes('style')).toBeUndefined();
+
+ findAllColors().at(0).vm.$emit('click', new Event('mouseclick'));
+ await nextTick();
+
+ expect(findSelectedColor().attributes('style')).toBe('background-color: rgb(0, 153, 102);');
+ });
+
+ it('shows correct color hex code after selecting a color', async () => {
+ createComponent();
+ expect(findSelectedColorText().attributes('value')).toBe('');
+
+ findAllColors().at(0).vm.$emit('click', new Event('mouseclick'));
+ await nextTick();
+
+ expect(findSelectedColorText().attributes('value')).toBe(colors[0]);
+ });
+
+ it('disables a Create button if label title is not set', async () => {
+ createComponent();
+ findAllColors().at(0).vm.$emit('click', new Event('mouseclick'));
+ await nextTick();
+
+ expect(findCreateButton().props('disabled')).toBe(true);
+ });
+
+ it('disables a Create button if color is not set', async () => {
+ createComponent();
+ findLabelTitleInput().vm.$emit('input', 'Test title');
+ await nextTick();
+
+ expect(findCreateButton().props('disabled')).toBe(true);
+ });
+
+ it('does not render a loader spinner', () => {
+ createComponent();
+ expect(findLoadingIcon().exists()).toBe(false);
+ });
+
+ it('emits a `hideCreateView` event on Cancel button click', () => {
+ createComponent();
+ const event = { stopPropagation: jest.fn() };
+ findCancelButton().vm.$emit('click', event);
+
+ expect(wrapper.emitted('hideCreateView')).toHaveLength(1);
+ expect(event.stopPropagation).toHaveBeenCalled();
+ });
+
+ describe('when label title and selected color are set', () => {
+ beforeEach(() => {
+ createComponent();
+ fillLabelAttributes();
+ });
+
+ it('enables a Create button', () => {
+ expect(findCreateButton().props()).toMatchObject({
+ disabled: false,
+ category: 'primary',
+ variant: 'confirm',
+ });
+ });
+
+ it('renders a loader spinner after Create button click', async () => {
+ findCreateButton().vm.$emit('click');
+ await nextTick();
+
+ expect(findLoadingIcon().exists()).toBe(true);
+ });
+
+ it('does not loader spinner after mutation is resolved', async () => {
+ findCreateButton().vm.$emit('click');
+ await nextTick();
+
+ expect(findLoadingIcon().exists()).toBe(true);
+ await waitForPromises();
+
+ expect(findLoadingIcon().exists()).toBe(false);
+ });
+ });
+
+ it('calls a mutation with `projectPath` variable on the issue', () => {
+ createComponent();
+ fillLabelAttributes();
+ findCreateButton().vm.$emit('click');
+
+ expect(createLabelSuccessHandler).toHaveBeenCalledWith({
+ color: '#009966',
+ projectPath: '',
+ title: 'Test title',
+ });
+ });
+
+ it('calls a mutation with `groupPath` variable on the epic', () => {
+ createComponent({ labelCreateType: 'group', workspaceType: 'group' });
+ fillLabelAttributes();
+ findCreateButton().vm.$emit('click');
+
+ expect(createLabelSuccessHandler).toHaveBeenCalledWith({
+ color: '#009966',
+ groupPath: '',
+ title: 'Test title',
+ });
+ });
+
+ it('calls createAlert is mutation has a user-recoverable error', async () => {
+ createComponent({ mutationHandler: createLabelUserRecoverableErrorHandler });
+ fillLabelAttributes();
+ await nextTick();
+
+ findCreateButton().vm.$emit('click');
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalled();
+ });
+
+ it('calls createAlert is mutation was rejected', async () => {
+ createComponent({ mutationHandler: createLabelErrorHandler });
+ fillLabelAttributes();
+ await nextTick();
+
+ findCreateButton().vm.$emit('click');
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalled();
+ });
+
+ it('displays error in alert if label title is already taken', async () => {
+ createComponent({ mutationHandler: createLabelDuplicateErrorHandler });
+ fillLabelAttributes();
+ await nextTick();
+
+ findCreateButton().vm.$emit('click');
+ await waitForPromises();
+
+ expect(wrapper.findComponent(GlAlert).text()).toEqual(
+ titleTakenError.data.labelCreate.errors[0],
+ );
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_contents_labels_view_spec.js b/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_contents_labels_view_spec.js
new file mode 100644
index 00000000000..913badccbe4
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_contents_labels_view_spec.js
@@ -0,0 +1,170 @@
+import {
+ GlLoadingIcon,
+ GlSearchBoxByType,
+ GlDropdownItem,
+ GlIntersectionObserver,
+} from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import { createAlert } from '~/flash';
+import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
+import { DropdownVariant } from '~/sidebar/components/labels/labels_select_widget/constants';
+import DropdownContentsLabelsView from '~/sidebar/components/labels/labels_select_widget/dropdown_contents_labels_view.vue';
+import projectLabelsQuery from '~/sidebar/components/labels/labels_select_widget/graphql/project_labels.query.graphql';
+import LabelItem from '~/sidebar/components/labels/labels_select_widget/label_item.vue';
+import { mockConfig, workspaceLabelsQueryResponse } from './mock_data';
+
+jest.mock('~/flash');
+
+Vue.use(VueApollo);
+
+const localSelectedLabels = [
+ {
+ color: '#2f7b2e',
+ description: null,
+ id: 'gid://gitlab/ProjectLabel/2',
+ title: 'Label2',
+ },
+];
+
+describe('DropdownContentsLabelsView', () => {
+ let wrapper;
+
+ const successfulQueryHandler = jest.fn().mockResolvedValue(workspaceLabelsQueryResponse);
+
+ const findFirstLabel = () => wrapper.findAllComponents(GlDropdownItem).at(0);
+
+ const createComponent = ({
+ initialState = mockConfig,
+ queryHandler = successfulQueryHandler,
+ injected = {},
+ searchKey = '',
+ } = {}) => {
+ const mockApollo = createMockApollo([[projectLabelsQuery, queryHandler]]);
+
+ wrapper = shallowMount(DropdownContentsLabelsView, {
+ apolloProvider: mockApollo,
+ provide: {
+ variant: DropdownVariant.Sidebar,
+ ...injected,
+ },
+ propsData: {
+ ...initialState,
+ localSelectedLabels,
+ searchKey,
+ labelCreateType: 'project',
+ workspaceType: 'project',
+ },
+ stubs: {
+ GlSearchBoxByType,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findLabels = () => wrapper.findAllComponents(LabelItem);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findObserver = () => wrapper.findComponent(GlIntersectionObserver);
+
+ const findLabelsList = () => wrapper.find('[data-testid="labels-list"]');
+ const findNoResultsMessage = () => wrapper.find('[data-testid="no-results"]');
+
+ async function makeObserverAppear() {
+ await findObserver().vm.$emit('appear');
+ }
+
+ describe('when loading labels', () => {
+ it('renders loading icon', async () => {
+ createComponent();
+ await makeObserverAppear();
+ expect(findLoadingIcon().exists()).toBe(true);
+ });
+
+ it('does not render labels list', async () => {
+ createComponent();
+ await makeObserverAppear();
+ expect(findLabelsList().exists()).toBe(false);
+ });
+ });
+
+ describe('when labels are loaded', () => {
+ beforeEach(async () => {
+ createComponent();
+ await makeObserverAppear();
+ await waitForPromises();
+ });
+
+ it('does not render loading icon', async () => {
+ expect(findLoadingIcon().exists()).toBe(false);
+ });
+
+ it('renders labels list', async () => {
+ expect(findLabelsList().exists()).toBe(true);
+ expect(findLabels()).toHaveLength(2);
+ });
+ });
+
+ it('first item is highlighted when search is not empty', async () => {
+ createComponent({
+ queryHandler: jest.fn().mockResolvedValue(workspaceLabelsQueryResponse),
+ searchKey: 'Label',
+ });
+ await makeObserverAppear();
+ await waitForPromises();
+ await nextTick();
+
+ expect(findLabelsList().exists()).toBe(true);
+ expect(findFirstLabel().attributes('active')).toBe('true');
+ });
+
+ it('when search returns 0 results', async () => {
+ createComponent({
+ queryHandler: jest.fn().mockResolvedValue({
+ data: {
+ workspace: {
+ labels: {
+ nodes: [],
+ },
+ },
+ },
+ }),
+ searchKey: '123',
+ });
+ await makeObserverAppear();
+ await waitForPromises();
+ await nextTick();
+
+ expect(findNoResultsMessage().isVisible()).toBe(true);
+ });
+
+ it('calls `createAlert` when fetching labels failed', async () => {
+ createComponent({ queryHandler: jest.fn().mockRejectedValue('Houston, we have a problem!') });
+ await makeObserverAppear();
+ jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalled();
+ });
+
+ it('emits an `input` event on label click', async () => {
+ createComponent();
+ await makeObserverAppear();
+ await waitForPromises();
+ findFirstLabel().trigger('click');
+
+ expect(wrapper.emitted('input')[0][0]).toEqual(expect.arrayContaining(localSelectedLabels));
+ });
+
+ it('does not trigger query when component did not appear', () => {
+ createComponent();
+ expect(findLoadingIcon().exists()).toBe(false);
+ expect(findLabelsList().exists()).toBe(false);
+ expect(successfulQueryHandler).not.toHaveBeenCalled();
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_contents_spec.js b/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_contents_spec.js
new file mode 100644
index 00000000000..9bbb1413ee9
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_contents_spec.js
@@ -0,0 +1,207 @@
+import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import { DropdownVariant } from '~/sidebar/components/labels/labels_select_widget/constants';
+import DropdownContents from '~/sidebar/components/labels/labels_select_widget/dropdown_contents.vue';
+import DropdownContentsCreateView from '~/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view.vue';
+import DropdownContentsLabelsView from '~/sidebar/components/labels/labels_select_widget/dropdown_contents_labels_view.vue';
+import DropdownFooter from '~/sidebar/components/labels/labels_select_widget/dropdown_footer.vue';
+
+import { mockLabels } from './mock_data';
+
+const showDropdown = jest.fn();
+const focusInput = jest.fn();
+
+const GlDropdownStub = {
+ template: `
+ <div data-testid="dropdown">
+ <slot name="header"></slot>
+ <slot></slot>
+ <slot name="footer"></slot>
+ </div>
+ `,
+ methods: {
+ show: showDropdown,
+ hide: jest.fn(),
+ },
+};
+
+const DropdownHeaderStub = {
+ template: `
+ <div>Hello, I am a header</div>
+ `,
+ methods: {
+ focusInput,
+ },
+};
+
+describe('DropdownContent', () => {
+ let wrapper;
+
+ const createComponent = ({ props = {}, data = {} } = {}) => {
+ wrapper = shallowMount(DropdownContents, {
+ propsData: {
+ labelsCreateTitle: 'test',
+ selectedLabels: mockLabels,
+ allowMultiselect: true,
+ labelsListTitle: 'Assign labels',
+ footerCreateLabelTitle: 'create',
+ footerManageLabelTitle: 'manage',
+ dropdownButtonText: 'Labels',
+ variant: 'sidebar',
+ fullPath: 'test',
+ workspaceType: 'project',
+ labelCreateType: 'project',
+ attrWorkspacePath: 'path',
+ ...props,
+ },
+ data() {
+ return {
+ ...data,
+ };
+ },
+ stubs: {
+ GlDropdown: GlDropdownStub,
+ DropdownHeader: DropdownHeaderStub,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findCreateView = () => wrapper.findComponent(DropdownContentsCreateView);
+ const findLabelsView = () => wrapper.findComponent(DropdownContentsLabelsView);
+ const findDropdownHeader = () => wrapper.findComponent(DropdownHeaderStub);
+ const findDropdownFooter = () => wrapper.findComponent(DropdownFooter);
+ const findDropdown = () => wrapper.findComponent(GlDropdownStub);
+
+ it('calls dropdown `show` method on `isVisible` prop change', async () => {
+ createComponent();
+ await wrapper.setProps({
+ isVisible: true,
+ });
+
+ expect(findDropdown().emitted('show')).toBeUndefined();
+ });
+
+ it('does not emit `setLabels` event on dropdown hide if labels did not change', () => {
+ createComponent();
+ findDropdown().vm.$emit('hide');
+
+ expect(wrapper.emitted('setLabels')).toBeUndefined();
+ });
+
+ it('emits `setLabels` event on dropdown hide if labels changed on non-sidebar widget', async () => {
+ createComponent({ props: { variant: DropdownVariant.Standalone } });
+ const updatedLabel = {
+ id: 28,
+ title: 'Bug',
+ description: 'Label for bugs',
+ color: '#FF0000',
+ textColor: '#FFFFFF',
+ };
+ findLabelsView().vm.$emit('input', [updatedLabel]);
+ await nextTick();
+ findDropdown().vm.$emit('hide');
+
+ expect(wrapper.emitted('setLabels')).toEqual([[[updatedLabel]]]);
+ });
+
+ it('emits `setLabels` event on visibility change if labels changed on sidebar widget', async () => {
+ createComponent({ props: { variant: DropdownVariant.Standalone, isVisible: true } });
+ const updatedLabel = {
+ id: 28,
+ title: 'Bug',
+ description: 'Label for bugs',
+ color: '#FF0000',
+ textColor: '#FFFFFF',
+ };
+ findLabelsView().vm.$emit('input', [updatedLabel]);
+ wrapper.setProps({ isVisible: false });
+ await nextTick();
+
+ expect(wrapper.emitted('setLabels')).toEqual([[[updatedLabel]]]);
+ });
+
+ it('renders header', () => {
+ createComponent();
+
+ expect(findDropdownHeader().exists()).toBe(true);
+ });
+
+ it('sets searchKey for labels view on input event from header', async () => {
+ createComponent();
+
+ expect(findLabelsView().props('searchKey')).toBe('');
+ findDropdownHeader().vm.$emit('input', '123');
+ await nextTick();
+
+ expect(findLabelsView().props('searchKey')).toBe('123');
+ });
+
+ it('clears and focuses search input on selecting a label', () => {
+ createComponent();
+ findDropdownHeader().vm.$emit('input', '123');
+ findLabelsView().vm.$emit('input', []);
+
+ expect(findLabelsView().props('searchKey')).toBe('');
+ expect(focusInput).toHaveBeenCalled();
+ });
+
+ describe('Create view', () => {
+ beforeEach(() => {
+ createComponent({ data: { showDropdownContentsCreateView: true } });
+ });
+
+ it('renders create view when `showDropdownContentsCreateView` prop is `true`', () => {
+ expect(findCreateView().exists()).toBe(true);
+ });
+
+ it('does not render footer', () => {
+ expect(findDropdownFooter().exists()).toBe(false);
+ });
+
+ it('changes the view to Labels view on `toggleDropdownContentsCreateView` event', async () => {
+ findDropdownHeader().vm.$emit('toggleDropdownContentsCreateView');
+ await nextTick();
+
+ expect(findCreateView().exists()).toBe(false);
+ expect(findLabelsView().exists()).toBe(true);
+ });
+
+ it('changes the view to Labels view on `hideCreateView` event', async () => {
+ findCreateView().vm.$emit('hideCreateView');
+ await nextTick();
+
+ expect(findCreateView().exists()).toBe(false);
+ expect(findLabelsView().exists()).toBe(true);
+ });
+ });
+
+ describe('Labels view', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders labels view when `showDropdownContentsCreateView` when `showDropdownContentsCreateView` prop is `false`', () => {
+ expect(findLabelsView().exists()).toBe(true);
+ });
+
+ it('renders footer on sidebar dropdown', () => {
+ expect(findDropdownFooter().exists()).toBe(true);
+ });
+
+ it('does not render footer on standalone dropdown', () => {
+ createComponent({ props: { variant: DropdownVariant.Standalone } });
+
+ expect(findDropdownFooter().exists()).toBe(false);
+ });
+
+ it('renders footer on embedded dropdown', () => {
+ createComponent({ props: { variant: DropdownVariant.Embedded } });
+
+ expect(findDropdownFooter().exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_footer_spec.js b/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_footer_spec.js
new file mode 100644
index 00000000000..9a6e0ca3ccd
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_footer_spec.js
@@ -0,0 +1,57 @@
+import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import DropdownFooter from '~/sidebar/components/labels/labels_select_widget/dropdown_footer.vue';
+
+describe('DropdownFooter', () => {
+ let wrapper;
+
+ const createComponent = ({ props = {}, injected = {} } = {}) => {
+ wrapper = shallowMount(DropdownFooter, {
+ propsData: {
+ footerCreateLabelTitle: 'create',
+ footerManageLabelTitle: 'manage',
+ ...props,
+ },
+ provide: {
+ allowLabelCreate: true,
+ labelsManagePath: 'foo/bar',
+ ...injected,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findCreateLabelButton = () => wrapper.find('[data-testid="create-label-button"]');
+
+ describe('Labels view', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('does not render create label button if `allowLabelCreate` is false', () => {
+ createComponent({ injected: { allowLabelCreate: false } });
+
+ expect(findCreateLabelButton().exists()).toBe(false);
+ });
+
+ describe('when `allowLabelCreate` is true', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders create label button', () => {
+ expect(findCreateLabelButton().exists()).toBe(true);
+ });
+
+ it('emits `toggleDropdownContentsCreateView` event on create label button click', async () => {
+ findCreateLabelButton().trigger('click');
+
+ await nextTick();
+ expect(wrapper.emitted('toggleDropdownContentsCreateView')).toEqual([[]]);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_header_spec.js b/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_header_spec.js
new file mode 100644
index 00000000000..d9001dface4
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_header_spec.js
@@ -0,0 +1,92 @@
+import { GlSearchBoxByType } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import DropdownHeader from '~/sidebar/components/labels/labels_select_widget/dropdown_header.vue';
+
+describe('DropdownHeader', () => {
+ let wrapper;
+
+ const createComponent = ({
+ showDropdownContentsCreateView = false,
+ labelsFetchInProgress = false,
+ isStandalone = false,
+ } = {}) => {
+ wrapper = extendedWrapper(
+ shallowMount(DropdownHeader, {
+ propsData: {
+ showDropdownContentsCreateView,
+ labelsFetchInProgress,
+ labelsCreateTitle: 'Create label',
+ labelsListTitle: 'Select label',
+ searchKey: '',
+ isStandalone,
+ },
+ stubs: {
+ GlSearchBoxByType,
+ },
+ }),
+ );
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findSearchInput = () => wrapper.findComponent(GlSearchBoxByType);
+ const findGoBackButton = () => wrapper.findByTestId('go-back-button');
+ const findDropdownTitle = () => wrapper.findByTestId('dropdown-header-title');
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ describe('Create view', () => {
+ beforeEach(() => {
+ createComponent({ showDropdownContentsCreateView: true });
+ });
+
+ it('renders go back button', () => {
+ expect(findGoBackButton().exists()).toBe(true);
+ });
+
+ it('does not render search input field', async () => {
+ expect(findSearchInput().exists()).toBe(false);
+ });
+ });
+
+ describe('Labels view', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('does not render go back button', () => {
+ expect(findGoBackButton().exists()).toBe(false);
+ });
+
+ it.each`
+ labelsFetchInProgress | disabled
+ ${true} | ${true}
+ ${false} | ${false}
+ `(
+ 'when labelsFetchInProgress is $labelsFetchInProgress, renders search input with disabled prop to $disabled',
+ ({ labelsFetchInProgress, disabled }) => {
+ createComponent({ labelsFetchInProgress });
+ expect(findSearchInput().props('disabled')).toBe(disabled);
+ },
+ );
+ });
+
+ describe('Standalone variant', () => {
+ beforeEach(() => {
+ createComponent({ isStandalone: true });
+ });
+
+ it('renders search input', () => {
+ expect(findSearchInput().exists()).toBe(true);
+ });
+
+ it('does not render title', async () => {
+ expect(findDropdownTitle().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_value_spec.js b/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_value_spec.js
new file mode 100644
index 00000000000..585048983c9
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_value_spec.js
@@ -0,0 +1,104 @@
+import { GlLabel } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+
+import DropdownValue from '~/sidebar/components/labels/labels_select_widget/dropdown_value.vue';
+
+import { mockRegularLabel, mockScopedLabel } from './mock_data';
+
+describe('DropdownValue', () => {
+ let wrapper;
+
+ const findAllLabels = () => wrapper.findAllComponents(GlLabel);
+ const findRegularLabel = () => findAllLabels().at(1);
+ const findScopedLabel = () => findAllLabels().at(0);
+ const findWrapper = () => wrapper.find('[data-testid="value-wrapper"]');
+ const findEmptyPlaceholder = () => wrapper.find('[data-testid="empty-placeholder"]');
+
+ const createComponent = (props = {}, slots = {}) => {
+ wrapper = shallowMount(DropdownValue, {
+ slots,
+ propsData: {
+ selectedLabels: [mockRegularLabel, mockScopedLabel],
+ allowLabelRemove: true,
+ labelsFilterBasePath: '/gitlab-org/my-project/issues',
+ labelsFilterParam: 'label_name',
+ ...props,
+ },
+ provide: {
+ allowScopedLabels: true,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when there are no labels', () => {
+ beforeEach(() => {
+ createComponent(
+ {
+ selectedLabels: [],
+ },
+ {
+ default: 'None',
+ },
+ );
+ });
+
+ it('does not apply `has-labels` class to the wrapping container', () => {
+ expect(findWrapper().classes()).not.toContain('has-labels');
+ });
+
+ it('renders an empty placeholder', () => {
+ expect(findEmptyPlaceholder().exists()).toBe(true);
+ expect(findEmptyPlaceholder().text()).toBe('None');
+ });
+
+ it('does not render any labels', () => {
+ expect(findAllLabels().length).toBe(0);
+ });
+ });
+
+ describe('when there are labels', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('applies `has-labels` class to the wrapping container', () => {
+ expect(findWrapper().classes()).toContain('has-labels');
+ });
+
+ it('does not render an empty placeholder', () => {
+ expect(findEmptyPlaceholder().exists()).toBe(false);
+ });
+
+ it('renders a list of two labels', () => {
+ expect(findAllLabels().length).toBe(2);
+ });
+
+ it('passes correct props to the regular label', () => {
+ expect(findRegularLabel().props('target')).toBe(
+ '/gitlab-org/my-project/issues?label_name[]=Foo%20Label',
+ );
+ expect(findRegularLabel().props('scoped')).toBe(false);
+ });
+
+ it('passes correct props to the scoped label', () => {
+ expect(findScopedLabel().props('target')).toBe(
+ '/gitlab-org/my-project/issues?label_name[]=Foo%3A%3ABar',
+ );
+ expect(findScopedLabel().props('scoped')).toBe(true);
+ });
+
+ it('emits `onLabelRemove` event with the correct ID', () => {
+ findRegularLabel().vm.$emit('close');
+ expect(wrapper.emitted('onLabelRemove')).toEqual([[mockRegularLabel.id]]);
+ });
+
+ it('emits `onCollapsedValueClick` when clicking on collapsed value', () => {
+ wrapper.find('.sidebar-collapsed-icon').trigger('click');
+ expect(wrapper.emitted('onCollapsedValueClick')).toEqual([[]]);
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_widget/embedded_labels_list_spec.js b/spec/frontend/sidebar/components/labels/labels_select_widget/embedded_labels_list_spec.js
new file mode 100644
index 00000000000..4fa65c752f9
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_widget/embedded_labels_list_spec.js
@@ -0,0 +1,77 @@
+import { GlLabel } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import EmbeddedLabelsList from '~/sidebar/components/labels/labels_select_widget/embedded_labels_list.vue';
+import { mockRegularLabel, mockScopedLabel } from './mock_data';
+
+describe('EmbeddedLabelsList', () => {
+ let wrapper;
+
+ const findAllLabels = () => wrapper.findAllComponents(GlLabel);
+ const findLabelByTitle = (title) =>
+ findAllLabels()
+ .filter((label) => label.props('title') === title)
+ .at(0);
+ const findRegularLabel = () => findLabelByTitle(mockRegularLabel.title);
+ const findScopedLabel = () => findLabelByTitle(mockScopedLabel.title);
+
+ const createComponent = (props = {}, slots = {}) => {
+ wrapper = shallowMountExtended(EmbeddedLabelsList, {
+ slots,
+ propsData: {
+ selectedLabels: [mockRegularLabel, mockScopedLabel],
+ allowLabelRemove: true,
+ labelsFilterBasePath: '/gitlab-org/my-project/issues',
+ labelsFilterParam: 'label_name',
+ ...props,
+ },
+ provide: {
+ allowScopedLabels: true,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when there are no labels', () => {
+ beforeEach(() => {
+ createComponent({
+ selectedLabels: [],
+ });
+ });
+
+ it('does not render any labels', () => {
+ expect(findAllLabels()).toHaveLength(0);
+ });
+ });
+
+ describe('when there are labels', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders a list of two labels', () => {
+ expect(findAllLabels()).toHaveLength(2);
+ });
+
+ it('passes correct props to the regular label', () => {
+ expect(findRegularLabel().props('target')).toBe(
+ '/gitlab-org/my-project/issues?label_name[]=Foo%20Label',
+ );
+ expect(findRegularLabel().props('scoped')).toBe(false);
+ });
+
+ it('passes correct props to the scoped label', () => {
+ expect(findScopedLabel().props('target')).toBe(
+ '/gitlab-org/my-project/issues?label_name[]=Foo%3A%3ABar',
+ );
+ expect(findScopedLabel().props('scoped')).toBe(true);
+ });
+
+ it('emits `onLabelRemove` event with the correct ID', () => {
+ findRegularLabel().vm.$emit('close');
+ expect(wrapper.emitted('onLabelRemove')).toStrictEqual([[mockRegularLabel.id]]);
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_widget/label_item_spec.js b/spec/frontend/sidebar/components/labels/labels_select_widget/label_item_spec.js
new file mode 100644
index 00000000000..74188a77994
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_widget/label_item_spec.js
@@ -0,0 +1,38 @@
+import { shallowMount } from '@vue/test-utils';
+
+import LabelItem from '~/sidebar/components/labels/labels_select_widget/label_item.vue';
+import { mockRegularLabel } from './mock_data';
+
+const mockLabel = { ...mockRegularLabel, set: true };
+
+const createComponent = ({ label = mockLabel } = {}) =>
+ shallowMount(LabelItem, {
+ propsData: {
+ label,
+ },
+ });
+
+describe('LabelItem', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('template', () => {
+ it('renders label color element', () => {
+ const colorEl = wrapper.find('[data-testid="label-color-box"]');
+
+ expect(colorEl.exists()).toBe(true);
+ expect(colorEl.attributes('style')).toBe('background-color: rgb(186, 218, 85);');
+ });
+
+ it('renders label title', () => {
+ expect(wrapper.text()).toContain(mockLabel.title);
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/labels/labels_select_widget/labels_select_root_spec.js b/spec/frontend/sidebar/components/labels/labels_select_widget/labels_select_root_spec.js
new file mode 100644
index 00000000000..2995c268966
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_widget/labels_select_root_spec.js
@@ -0,0 +1,267 @@
+import { shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import { createAlert } from '~/flash';
+import { IssuableType } from '~/issues/constants';
+import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
+import DropdownContents from '~/sidebar/components/labels/labels_select_widget/dropdown_contents.vue';
+import DropdownValue from '~/sidebar/components/labels/labels_select_widget/dropdown_value.vue';
+import EmbeddedLabelsList from '~/sidebar/components/labels/labels_select_widget/embedded_labels_list.vue';
+import issueLabelsQuery from '~/sidebar/components/labels/labels_select_widget/graphql/issue_labels.query.graphql';
+import updateIssueLabelsMutation from '~/boards/graphql/issue_set_labels.mutation.graphql';
+import updateMergeRequestLabelsMutation from '~/sidebar/queries/update_merge_request_labels.mutation.graphql';
+import issuableLabelsSubscription from 'ee_else_ce/sidebar/queries/issuable_labels.subscription.graphql';
+import updateEpicLabelsMutation from '~/sidebar/components/labels/labels_select_widget/graphql/epic_update_labels.mutation.graphql';
+import LabelsSelectRoot from '~/sidebar/components/labels/labels_select_widget/labels_select_root.vue';
+import {
+ mockConfig,
+ issuableLabelsQueryResponse,
+ updateLabelsMutationResponse,
+ issuableLabelsSubscriptionResponse,
+ mockLabels,
+ mockRegularLabel,
+} from './mock_data';
+
+jest.mock('~/flash');
+
+Vue.use(VueApollo);
+
+const successfulQueryHandler = jest.fn().mockResolvedValue(issuableLabelsQueryResponse);
+const successfulMutationHandler = jest.fn().mockResolvedValue(updateLabelsMutationResponse);
+const subscriptionHandler = jest.fn().mockResolvedValue(issuableLabelsSubscriptionResponse);
+const errorQueryHandler = jest.fn().mockRejectedValue('Houston, we have a problem');
+
+const updateLabelsMutation = {
+ [IssuableType.Issue]: updateIssueLabelsMutation,
+ [IssuableType.MergeRequest]: updateMergeRequestLabelsMutation,
+ [IssuableType.Epic]: updateEpicLabelsMutation,
+};
+
+describe('LabelsSelectRoot', () => {
+ let wrapper;
+
+ const findSidebarEditableItem = () => wrapper.findComponent(SidebarEditableItem);
+ const findDropdownValue = () => wrapper.findComponent(DropdownValue);
+ const findDropdownContents = () => wrapper.findComponent(DropdownContents);
+ const findEmbeddedLabelsList = () => wrapper.findComponent(EmbeddedLabelsList);
+
+ const createComponent = ({
+ config = mockConfig,
+ slots = {},
+ issuableType = IssuableType.Issue,
+ queryHandler = successfulQueryHandler,
+ mutationHandler = successfulMutationHandler,
+ } = {}) => {
+ const mockApollo = createMockApollo([
+ [issueLabelsQuery, queryHandler],
+ [updateLabelsMutation[issuableType], mutationHandler],
+ [issuableLabelsSubscription, subscriptionHandler],
+ ]);
+
+ wrapper = shallowMount(LabelsSelectRoot, {
+ slots,
+ apolloProvider: mockApollo,
+ propsData: {
+ ...config,
+ issuableType,
+ labelCreateType: 'project',
+ workspaceType: 'project',
+ },
+ stubs: {
+ SidebarEditableItem,
+ },
+ provide: {
+ canUpdate: true,
+ allowLabelEdit: true,
+ allowLabelCreate: true,
+ labelsManagePath: 'test',
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders component with classes `labels-select-wrapper gl-relative`', () => {
+ createComponent();
+ expect(wrapper.classes()).toEqual(['labels-select-wrapper', 'gl-relative']);
+ });
+
+ it.each`
+ variant | cssClass
+ ${'standalone'} | ${'is-standalone'}
+ ${'embedded'} | ${'is-embedded'}
+ `(
+ 'renders component root element with CSS class `$cssClass` when `state.variant` is "$variant"',
+ async ({ variant, cssClass }) => {
+ createComponent({
+ config: { ...mockConfig, variant },
+ });
+
+ await nextTick();
+ expect(wrapper.classes()).toContain(cssClass);
+ },
+ );
+
+ describe('if dropdown variant is `sidebar`', () => {
+ it('renders sidebar editable item', () => {
+ createComponent();
+ expect(findSidebarEditableItem().exists()).toBe(true);
+ });
+
+ it('passes true `loading` prop to sidebar editable item when loading labels', () => {
+ createComponent();
+ expect(findSidebarEditableItem().props('loading')).toBe(true);
+ });
+
+ describe('when labels are fetched successfully', () => {
+ beforeEach(async () => {
+ createComponent();
+ await waitForPromises();
+ });
+
+ it('passes true `loading` prop to sidebar editable item', () => {
+ expect(findSidebarEditableItem().props('loading')).toBe(false);
+ });
+
+ it('renders dropdown value component when query labels is resolved', () => {
+ expect(findDropdownValue().exists()).toBe(true);
+ expect(findDropdownValue().props('selectedLabels')).toEqual([
+ {
+ __typename: 'Label',
+ color: '#330066',
+ description: null,
+ id: 'gid://gitlab/ProjectLabel/1',
+ title: 'Label1',
+ textColor: '#000000',
+ },
+ ]);
+ });
+
+ it('emits `onLabelRemove` event on dropdown value label remove event', () => {
+ const label = { id: 'gid://gitlab/ProjectLabel/1' };
+ findDropdownValue().vm.$emit('onLabelRemove', label);
+ expect(wrapper.emitted('onLabelRemove')).toEqual([[label]]);
+ });
+ });
+
+ it('creates flash with error message when query is rejected', async () => {
+ createComponent({ queryHandler: errorQueryHandler });
+ await waitForPromises();
+ expect(createAlert).toHaveBeenCalledWith({ message: 'Error fetching labels.' });
+ });
+ });
+
+ describe('if dropdown variant is `embedded`', () => {
+ it('shows the embedded labels list', () => {
+ createComponent({
+ config: { ...mockConfig, iid: '', variant: 'embedded', showEmbeddedLabelsList: true },
+ });
+
+ expect(findEmbeddedLabelsList().props()).toMatchObject({
+ disabled: false,
+ selectedLabels: [],
+ allowLabelRemove: false,
+ labelsFilterBasePath: mockConfig.labelsFilterBasePath,
+ labelsFilterParam: mockConfig.labelsFilterParam,
+ });
+ });
+
+ it('passes the selected labels if provided', () => {
+ createComponent({
+ config: {
+ ...mockConfig,
+ iid: '',
+ variant: 'embedded',
+ showEmbeddedLabelsList: true,
+ selectedLabels: mockLabels,
+ },
+ });
+
+ expect(findEmbeddedLabelsList().props('selectedLabels')).toStrictEqual(mockLabels);
+ expect(findDropdownContents().props('selectedLabels')).toStrictEqual(mockLabels);
+ });
+
+ it('emits the `onLabelRemove` when the embedded list triggers a removal', () => {
+ createComponent({
+ config: {
+ ...mockConfig,
+ iid: '',
+ variant: 'embedded',
+ showEmbeddedLabelsList: true,
+ selectedLabels: [mockRegularLabel],
+ },
+ });
+
+ findEmbeddedLabelsList().vm.$emit('onLabelRemove', [mockRegularLabel.id]);
+ expect(wrapper.emitted('onLabelRemove')).toStrictEqual([[[mockRegularLabel.id]]]);
+ });
+ });
+
+ it('emits `updateSelectedLabels` event on dropdown contents `setLabels` event if iid is not set', async () => {
+ const label = { id: 'gid://gitlab/ProjectLabel/1' };
+ createComponent({ config: { ...mockConfig, iid: undefined } });
+
+ findDropdownContents().vm.$emit('setLabels', [label]);
+ expect(wrapper.emitted('updateSelectedLabels')).toEqual([[{ labels: [label] }]]);
+ });
+
+ describe.each`
+ issuableType
+ ${IssuableType.Issue}
+ ${IssuableType.MergeRequest}
+ ${IssuableType.Epic}
+ `('when updating labels for $issuableType', ({ issuableType }) => {
+ const label = { id: 'gid://gitlab/ProjectLabel/2' };
+
+ it('sets the loading state', async () => {
+ createComponent({ issuableType });
+ await nextTick();
+ findDropdownContents().vm.$emit('setLabels', [label]);
+ await nextTick();
+
+ expect(findSidebarEditableItem().props('loading')).toBe(true);
+ });
+
+ it('updates labels correctly after successful mutation', async () => {
+ createComponent({ issuableType });
+ await nextTick();
+ findDropdownContents().vm.$emit('setLabels', [label]);
+ await waitForPromises();
+
+ expect(findDropdownValue().props('selectedLabels')).toEqual(
+ updateLabelsMutationResponse.data.updateIssuableLabels.issuable.labels.nodes,
+ );
+ });
+
+ it('displays an error if mutation was rejected', async () => {
+ createComponent({ issuableType, mutationHandler: errorQueryHandler });
+ await nextTick();
+ findDropdownContents().vm.$emit('setLabels', [label]);
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalledWith({
+ captureError: true,
+ error: expect.anything(),
+ message: 'An error occurred while updating labels.',
+ });
+ });
+
+ it('emits `updateSelectedLabels` event when the subscription is triggered', async () => {
+ createComponent();
+ await waitForPromises();
+
+ expect(wrapper.emitted('updateSelectedLabels')).toEqual([
+ [
+ {
+ id: '1',
+ labels: issuableLabelsSubscriptionResponse.data.issuableLabelsUpdated.labels.nodes,
+ },
+ ],
+ ]);
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/mock_data.js b/spec/frontend/sidebar/components/labels/labels_select_widget/mock_data.js
index 48530a0261f..48530a0261f 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/mock_data.js
+++ b/spec/frontend/sidebar/components/labels/labels_select_widget/mock_data.js
diff --git a/spec/frontend/sidebar/lock/__snapshots__/edit_form_spec.js.snap b/spec/frontend/sidebar/components/lock/__snapshots__/edit_form_spec.js.snap
index 18d4df297df..18d4df297df 100644
--- a/spec/frontend/sidebar/lock/__snapshots__/edit_form_spec.js.snap
+++ b/spec/frontend/sidebar/components/lock/__snapshots__/edit_form_spec.js.snap
diff --git a/spec/frontend/sidebar/lock/constants.js b/spec/frontend/sidebar/components/lock/constants.js
index b9f08e9286d..b9f08e9286d 100644
--- a/spec/frontend/sidebar/lock/constants.js
+++ b/spec/frontend/sidebar/components/lock/constants.js
diff --git a/spec/frontend/sidebar/lock/edit_form_buttons_spec.js b/spec/frontend/sidebar/components/lock/edit_form_buttons_spec.js
index 2abb0c24d7d..2abb0c24d7d 100644
--- a/spec/frontend/sidebar/lock/edit_form_buttons_spec.js
+++ b/spec/frontend/sidebar/components/lock/edit_form_buttons_spec.js
diff --git a/spec/frontend/sidebar/lock/edit_form_spec.js b/spec/frontend/sidebar/components/lock/edit_form_spec.js
index 4ae9025ee39..4ae9025ee39 100644
--- a/spec/frontend/sidebar/lock/edit_form_spec.js
+++ b/spec/frontend/sidebar/components/lock/edit_form_spec.js
diff --git a/spec/frontend/sidebar/lock/issuable_lock_form_spec.js b/spec/frontend/sidebar/components/lock/issuable_lock_form_spec.js
index 8f825847cfc..8f825847cfc 100644
--- a/spec/frontend/sidebar/lock/issuable_lock_form_spec.js
+++ b/spec/frontend/sidebar/components/lock/issuable_lock_form_spec.js
diff --git a/spec/frontend/sidebar/components/move/issuable_move_dropdown_spec.js b/spec/frontend/sidebar/components/move/issuable_move_dropdown_spec.js
new file mode 100644
index 00000000000..72279f44e80
--- /dev/null
+++ b/spec/frontend/sidebar/components/move/issuable_move_dropdown_spec.js
@@ -0,0 +1,388 @@
+import {
+ GlIcon,
+ GlLoadingIcon,
+ GlDropdown,
+ GlDropdownForm,
+ GlDropdownItem,
+ GlSearchBoxByType,
+ GlButton,
+} from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+
+import { nextTick } from 'vue';
+import axios from '~/lib/utils/axios_utils';
+import IssuableMoveDropdown from '~/sidebar/components/move/issuable_move_dropdown.vue';
+
+const mockProjects = [
+ {
+ id: 2,
+ name_with_namespace: 'Gitlab Org / Gitlab Shell',
+ full_path: 'gitlab-org/gitlab-shell',
+ },
+ {
+ id: 3,
+ name_with_namespace: 'Gnuwget / Wget2',
+ full_path: 'gnuwget/wget2',
+ },
+ {
+ id: 4,
+ name_with_namespace: 'Commit451 / Lab Coat',
+ full_path: 'Commit451/lab-coat',
+ },
+];
+
+const mockProps = {
+ projectsFetchPath: '/-/autocomplete/projects?project_id=1',
+ dropdownButtonTitle: 'Move issuable',
+ dropdownHeaderTitle: 'Move issuable',
+ moveInProgress: false,
+ disabled: false,
+};
+
+const mockEvent = {
+ stopPropagation: jest.fn(),
+ preventDefault: jest.fn(),
+};
+
+describe('IssuableMoveDropdown', () => {
+ let mock;
+ let wrapper;
+
+ const createComponent = (propsData = mockProps) => {
+ wrapper = shallowMount(IssuableMoveDropdown, {
+ propsData,
+ });
+ wrapper.vm.$refs.dropdown.hide = jest.fn();
+ wrapper.vm.$refs.searchInput.focusInput = jest.fn();
+ };
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ mock.restore();
+ });
+
+ describe('watch', () => {
+ describe('searchKey', () => {
+ it('calls `fetchProjects` with value of the prop', async () => {
+ jest.spyOn(wrapper.vm, 'fetchProjects');
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ searchKey: 'foo',
+ });
+
+ await nextTick();
+
+ expect(wrapper.vm.fetchProjects).toHaveBeenCalledWith('foo');
+ });
+ });
+ });
+
+ describe('methods', () => {
+ describe('fetchProjects', () => {
+ it('sets projectsListLoading to true and projectsListLoadFailed to false', () => {
+ wrapper.vm.fetchProjects();
+
+ expect(wrapper.vm.projectsListLoading).toBe(true);
+ expect(wrapper.vm.projectsListLoadFailed).toBe(false);
+ });
+
+ it('calls `axios.get` with `projectsFetchPath` and query param `search`', () => {
+ jest.spyOn(axios, 'get').mockResolvedValue({
+ data: mockProjects,
+ });
+
+ wrapper.vm.fetchProjects('foo');
+
+ expect(axios.get).toHaveBeenCalledWith(
+ mockProps.projectsFetchPath,
+ expect.objectContaining({
+ params: {
+ search: 'foo',
+ },
+ }),
+ );
+ });
+
+ it('sets response to `projects` and focuses on searchInput when request is successful', async () => {
+ jest.spyOn(axios, 'get').mockResolvedValue({
+ data: mockProjects,
+ });
+
+ await wrapper.vm.fetchProjects('foo');
+
+ expect(wrapper.vm.projects).toBe(mockProjects);
+ expect(wrapper.vm.$refs.searchInput.focusInput).toHaveBeenCalled();
+ });
+
+ it('sets projectsListLoadFailed to true when request fails', async () => {
+ jest.spyOn(axios, 'get').mockRejectedValue({});
+
+ await wrapper.vm.fetchProjects('foo');
+
+ expect(wrapper.vm.projectsListLoadFailed).toBe(true);
+ });
+
+ it('sets projectsListLoading to false when request completes', async () => {
+ jest.spyOn(axios, 'get').mockResolvedValue({
+ data: mockProjects,
+ });
+
+ await wrapper.vm.fetchProjects('foo');
+
+ expect(wrapper.vm.projectsListLoading).toBe(false);
+ });
+ });
+
+ describe('isSelectedProject', () => {
+ it.each`
+ project | selectedProject | title | returnValue
+ ${mockProjects[0]} | ${mockProjects[0]} | ${'are same projects'} | ${true}
+ ${mockProjects[0]} | ${mockProjects[1]} | ${'are different projects'} | ${false}
+ `(
+ 'returns $returnValue when selectedProject and provided project param $title',
+ async ({ project, selectedProject, returnValue }) => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ selectedProject,
+ });
+
+ await nextTick();
+
+ expect(wrapper.vm.isSelectedProject(project)).toBe(returnValue);
+ },
+ );
+
+ it('returns false when selectedProject is null', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ selectedProject: null,
+ });
+
+ await nextTick();
+
+ expect(wrapper.vm.isSelectedProject(mockProjects[0])).toBe(false);
+ });
+ });
+ });
+
+ describe('template', () => {
+ const findDropdownEl = () => wrapper.findComponent(GlDropdown);
+
+ it('renders collapsed state element with icon', () => {
+ const collapsedEl = wrapper.find('[data-testid="move-collapsed"]');
+
+ expect(collapsedEl.exists()).toBe(true);
+ expect(collapsedEl.attributes('title')).toBe(mockProps.dropdownButtonTitle);
+ expect(collapsedEl.findComponent(GlIcon).exists()).toBe(true);
+ expect(collapsedEl.findComponent(GlIcon).props('name')).toBe('arrow-right');
+ });
+
+ describe('gl-dropdown component', () => {
+ it('renders component container element', () => {
+ expect(findDropdownEl().exists()).toBe(true);
+ expect(findDropdownEl().props('block')).toBe(true);
+ });
+
+ it('renders gl-dropdown-form component', () => {
+ expect(findDropdownEl().findComponent(GlDropdownForm).exists()).toBe(true);
+ });
+
+ it('renders disabled dropdown when `disabled` is true', () => {
+ createComponent({ ...mockProps, disabled: true });
+
+ expect(findDropdownEl().attributes('disabled')).toBe('true');
+ });
+
+ it('renders header element', () => {
+ const headerEl = findDropdownEl().find('[data-testid="header"]');
+
+ expect(headerEl.exists()).toBe(true);
+ expect(headerEl.find('span').text()).toBe(mockProps.dropdownHeaderTitle);
+ expect(headerEl.findComponent(GlButton).props('icon')).toBe('close');
+ });
+
+ it('renders gl-search-box-by-type component', () => {
+ const searchEl = findDropdownEl().findComponent(GlSearchBoxByType);
+
+ expect(searchEl.exists()).toBe(true);
+ expect(searchEl.attributes()).toMatchObject({
+ placeholder: 'Search project',
+ debounce: '300',
+ });
+ });
+
+ it('renders gl-loading-icon component when projectsListLoading prop is true', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ projectsListLoading: true,
+ });
+
+ await nextTick();
+
+ expect(findDropdownEl().findComponent(GlLoadingIcon).exists()).toBe(true);
+ });
+
+ it('renders gl-dropdown-item components for available projects', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ projects: mockProjects,
+ selectedProject: mockProjects[0],
+ });
+
+ await nextTick();
+
+ const dropdownItems = wrapper.findAllComponents(GlDropdownItem);
+
+ expect(dropdownItems).toHaveLength(mockProjects.length);
+ expect(dropdownItems.at(0).props()).toMatchObject({
+ isCheckItem: true,
+ isChecked: true,
+ });
+ expect(dropdownItems.at(0).text()).toBe(mockProjects[0].name_with_namespace);
+ });
+
+ it('renders string "No matching results" when search does not yield any matches', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ searchKey: 'foo',
+ });
+
+ // Wait for `searchKey` watcher to run.
+ await nextTick();
+
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ projects: [],
+ projectsListLoading: false,
+ });
+
+ await nextTick();
+
+ const dropdownContentEl = wrapper.find('[data-testid="content"]');
+
+ expect(dropdownContentEl.text()).toContain('No matching results');
+ });
+
+ it('renders string "Failed to load projects" when loading projects list fails', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ projects: [],
+ projectsListLoading: false,
+ projectsListLoadFailed: true,
+ });
+
+ await nextTick();
+
+ const dropdownContentEl = wrapper.find('[data-testid="content"]');
+
+ expect(dropdownContentEl.text()).toContain('Failed to load projects');
+ });
+
+ it('renders gl-button within footer', async () => {
+ const moveButtonEl = wrapper.find('[data-testid="footer"]').findComponent(GlButton);
+
+ expect(moveButtonEl.text()).toBe('Move');
+ expect(moveButtonEl.attributes('disabled')).toBe('true');
+
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ selectedProject: mockProjects[0],
+ });
+
+ await nextTick();
+
+ expect(
+ wrapper.find('[data-testid="footer"]').findComponent(GlButton).attributes('disabled'),
+ ).not.toBeDefined();
+ });
+ });
+
+ describe('events', () => {
+ it('collapsed state element emits `toggle-collapse` event on component when clicked', () => {
+ wrapper.find('[data-testid="move-collapsed"]').trigger('click');
+
+ expect(wrapper.emitted('toggle-collapse')).toHaveLength(1);
+ });
+
+ it('gl-dropdown component calls `fetchProjects` on `shown` event', () => {
+ jest.spyOn(axios, 'get').mockResolvedValue({
+ data: mockProjects,
+ });
+
+ findDropdownEl().vm.$emit('shown');
+
+ expect(axios.get).toHaveBeenCalled();
+ });
+
+ it('gl-dropdown component prevents dropdown body from closing on `hide` event when `projectItemClick` prop is true', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ projectItemClick: true,
+ });
+
+ findDropdownEl().vm.$emit('hide', mockEvent);
+
+ expect(mockEvent.preventDefault).toHaveBeenCalled();
+ expect(wrapper.vm.projectItemClick).toBe(false);
+ });
+
+ it('gl-dropdown component emits `dropdown-close` event on component from `hide` event', async () => {
+ findDropdownEl().vm.$emit('hide');
+
+ expect(wrapper.emitted('dropdown-close')).toHaveLength(1);
+ });
+
+ it('close icon in dropdown header closes the dropdown when clicked', () => {
+ wrapper.find('[data-testid="header"]').findComponent(GlButton).vm.$emit('click', mockEvent);
+
+ expect(wrapper.vm.$refs.dropdown.hide).toHaveBeenCalled();
+ });
+
+ it('sets project for clicked gl-dropdown-item to selectedProject', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ projects: mockProjects,
+ });
+
+ await nextTick();
+
+ wrapper.findAllComponents(GlDropdownItem).at(0).vm.$emit('click', mockEvent);
+
+ expect(wrapper.vm.selectedProject).toBe(mockProjects[0]);
+ });
+
+ it('hides dropdown and emits `move-issuable` event when move button is clicked', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ selectedProject: mockProjects[0],
+ });
+
+ await nextTick();
+
+ wrapper.find('[data-testid="footer"]').findComponent(GlButton).vm.$emit('click');
+
+ expect(wrapper.vm.$refs.dropdown.hide).toHaveBeenCalled();
+ expect(wrapper.emitted('move-issuable')).toHaveLength(1);
+ expect(wrapper.emitted('move-issuable')[0]).toEqual([mockProjects[0]]);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/move/move_issues_button_spec.js b/spec/frontend/sidebar/components/move/move_issues_button_spec.js
new file mode 100644
index 00000000000..999340da27c
--- /dev/null
+++ b/spec/frontend/sidebar/components/move/move_issues_button_spec.js
@@ -0,0 +1,554 @@
+import { shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
+import { cloneDeep } from 'lodash';
+import VueApollo from 'vue-apollo';
+import { GlAlert } from '@gitlab/ui';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
+import { createAlert } from '~/flash';
+import { logError } from '~/lib/logger';
+import IssuableMoveDropdown from '~/sidebar/components/move/issuable_move_dropdown.vue';
+import issuableEventHub from '~/issues/list/eventhub';
+import MoveIssuesButton from '~/sidebar/components/move/move_issues_button.vue';
+import moveIssueMutation from '~/sidebar/queries/move_issue.mutation.graphql';
+import getIssuesQuery from 'ee_else_ce/issues/list/queries/get_issues.query.graphql';
+import getIssuesCountsQuery from 'ee_else_ce/issues/list/queries/get_issues_counts.query.graphql';
+import { getIssuesCountsQueryResponse, getIssuesQueryResponse } from 'jest/issues/list/mock_data';
+import {
+ WORK_ITEM_TYPE_ENUM_ISSUE,
+ WORK_ITEM_TYPE_ENUM_INCIDENT,
+ WORK_ITEM_TYPE_ENUM_TASK,
+ WORK_ITEM_TYPE_ENUM_TEST_CASE,
+} from '~/work_items/constants';
+
+jest.mock('~/flash');
+jest.mock('~/lib/logger');
+useMockLocationHelper();
+
+const mockDefaultProps = {
+ projectFullPath: 'flight/FlightJS',
+ projectsFetchPath: '/-/autocomplete/projects?project_id=1',
+};
+
+const mockDestinationProject = {
+ full_path: 'gitlab-org/GitLabTest',
+};
+
+const mockMutationErrorMessage = 'Example error message';
+
+const mockIssue = {
+ iid: '15',
+ type: WORK_ITEM_TYPE_ENUM_ISSUE,
+};
+
+const mockIncident = {
+ iid: '32',
+ type: WORK_ITEM_TYPE_ENUM_INCIDENT,
+};
+
+const mockTask = {
+ iid: '40',
+ type: WORK_ITEM_TYPE_ENUM_TASK,
+};
+
+const mockTestCase = {
+ iid: '51',
+ type: WORK_ITEM_TYPE_ENUM_TEST_CASE,
+};
+
+const selectedIssuesMocks = {
+ tasksOnly: [mockTask],
+ testCasesOnly: [mockTestCase],
+ issuesOnly: [mockIssue, mockIncident],
+ tasksAndTestCases: [mockTask, mockTestCase],
+ issuesAndTasks: [mockIssue, mockIncident, mockTask],
+ issuesAndTestCases: [mockIssue, mockIncident, mockTestCase],
+ issuesTasksAndTestCases: [mockIssue, mockIncident, mockTask, mockTestCase],
+};
+
+let getIssuesQueryCompleteResponse = getIssuesQueryResponse;
+if (IS_EE) {
+ getIssuesQueryCompleteResponse = cloneDeep(getIssuesQueryResponse);
+ getIssuesQueryCompleteResponse.data.project.issues.nodes[0].blockingCount = 1;
+ getIssuesQueryCompleteResponse.data.project.issues.nodes[0].healthStatus = null;
+ getIssuesQueryCompleteResponse.data.project.issues.nodes[0].weight = 5;
+}
+
+const resolvedMutationWithoutErrorsMock = jest.fn().mockResolvedValue({
+ data: {
+ issueMove: {
+ errors: [],
+ },
+ },
+});
+
+const resolvedMutationWithErrorsMock = jest.fn().mockResolvedValue({
+ data: {
+ issueMove: {
+ errors: [{ message: mockMutationErrorMessage }],
+ },
+ },
+});
+
+const rejectedMutationMock = jest.fn().mockRejectedValue({});
+
+const mockIssuesQueryResponse = jest.fn().mockResolvedValue(getIssuesQueryCompleteResponse);
+const mockIssuesCountsQueryResponse = jest.fn().mockResolvedValue(getIssuesCountsQueryResponse);
+
+describe('MoveIssuesButton', () => {
+ Vue.use(VueApollo);
+
+ let wrapper;
+ let fakeApollo;
+
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findDropdown = () => wrapper.findComponent(IssuableMoveDropdown);
+ const emitMoveIssuablesEvent = () => {
+ findDropdown().vm.$emit('move-issuable', mockDestinationProject);
+ };
+
+ const createComponent = (data = {}, mutationResolverMock = rejectedMutationMock) => {
+ fakeApollo = createMockApollo([
+ [moveIssueMutation, mutationResolverMock],
+ [getIssuesQuery, mockIssuesQueryResponse],
+ [getIssuesCountsQuery, mockIssuesCountsQueryResponse],
+ ]);
+
+ fakeApollo.defaultClient.cache.writeQuery({
+ query: getIssuesQuery,
+ variables: {
+ isProject: true,
+ fullPath: mockDefaultProps.projectFullPath,
+ },
+ data: getIssuesQueryCompleteResponse.data,
+ });
+
+ fakeApollo.defaultClient.cache.writeQuery({
+ query: getIssuesCountsQuery,
+ variables: {
+ isProject: true,
+ },
+ data: getIssuesCountsQueryResponse.data,
+ });
+
+ wrapper = shallowMount(MoveIssuesButton, {
+ data() {
+ return {
+ ...data,
+ };
+ },
+ propsData: {
+ ...mockDefaultProps,
+ },
+ apolloProvider: fakeApollo,
+ });
+ };
+
+ beforeEach(() => {
+ // Needed due to a bug in Apollo: https://github.com/apollographql/apollo-client/issues/8900
+ // eslint-disable-next-line no-console
+ console.warn = jest.fn();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ fakeApollo = null;
+ });
+
+ describe('`Move selected` dropdown', () => {
+ it('renders disabled by default', () => {
+ createComponent();
+ expect(findDropdown().exists()).toBe(true);
+ expect(findDropdown().attributes('disabled')).toBe('true');
+ });
+
+ it.each`
+ selectedIssuablesMock | disabled | status | testMessage
+ ${[]} | ${true} | ${'disabled'} | ${'nothing is selected'}
+ ${selectedIssuesMocks.tasksOnly} | ${true} | ${'disabled'} | ${'only tasks are selected'}
+ ${selectedIssuesMocks.testCasesOnly} | ${true} | ${'disabled'} | ${'only test cases are selected'}
+ ${selectedIssuesMocks.issuesOnly} | ${false} | ${'enabled'} | ${'only issues are selected'}
+ ${selectedIssuesMocks.tasksAndTestCases} | ${true} | ${'disabled'} | ${'tasks and test cases are selected'}
+ ${selectedIssuesMocks.issuesAndTasks} | ${false} | ${'enabled'} | ${'issues and tasks are selected'}
+ ${selectedIssuesMocks.issuesAndTestCases} | ${false} | ${'enabled'} | ${'issues and test cases are selected'}
+ ${selectedIssuesMocks.issuesTasksAndTestCases} | ${false} | ${'enabled'} | ${'issues and tasks and test cases are selected'}
+ `('renders $status if $testMessage', async ({ selectedIssuablesMock, disabled }) => {
+ createComponent({ selectedIssuables: selectedIssuablesMock });
+
+ await nextTick();
+
+ if (disabled) {
+ expect(findDropdown().attributes('disabled')).toBe('true');
+ } else {
+ expect(findDropdown().attributes('disabled')).toBeUndefined();
+ }
+ });
+ });
+
+ describe('warning message', () => {
+ it.each`
+ selectedIssuablesMock | warningExists | visibility | message | testMessage
+ ${[]} | ${false} | ${'not visible'} | ${'empty'} | ${'nothing is selected'}
+ ${selectedIssuesMocks.tasksOnly} | ${true} | ${'visible'} | ${'Tasks can not be moved.'} | ${'only tasks are selected'}
+ ${selectedIssuesMocks.testCasesOnly} | ${true} | ${'visible'} | ${'Test cases can not be moved.'} | ${'only test cases are selected'}
+ ${selectedIssuesMocks.issuesOnly} | ${false} | ${'not visible'} | ${'empty'} | ${'only issues are selected'}
+ ${selectedIssuesMocks.tasksAndTestCases} | ${true} | ${'visible'} | ${'Tasks and test cases can not be moved.'} | ${'tasks and test cases are selected'}
+ ${selectedIssuesMocks.issuesAndTasks} | ${true} | ${'visible'} | ${'Tasks can not be moved.'} | ${'issues and tasks are selected'}
+ ${selectedIssuesMocks.issuesAndTestCases} | ${true} | ${'visible'} | ${'Test cases can not be moved.'} | ${'issues and test cases are selected'}
+ ${selectedIssuesMocks.issuesTasksAndTestCases} | ${true} | ${'visible'} | ${'Tasks and test cases can not be moved.'} | ${'issues and tasks and test cases are selected'}
+ `(
+ 'is $visibility with `$message` message if $testMessage',
+ async ({ selectedIssuablesMock, warningExists, message }) => {
+ createComponent({ selectedIssuables: selectedIssuablesMock });
+
+ await nextTick();
+
+ const alert = findAlert();
+ expect(alert.exists()).toBe(warningExists);
+
+ if (warningExists) {
+ expect(alert.text()).toBe(message);
+ expect(alert.attributes('variant')).toBe('warning');
+ }
+ },
+ );
+ });
+
+ describe('moveIssues method', () => {
+ describe('changes the `Move selected` dropdown loading state', () => {
+ it('keeps loading state to false when no issue is selected', async () => {
+ createComponent();
+ emitMoveIssuablesEvent();
+
+ await nextTick();
+
+ expect(findDropdown().props('moveInProgress')).toBe(false);
+ });
+
+ it('keeps loading state to false when only tasks are selected', async () => {
+ createComponent({ selectedIssuables: selectedIssuesMocks.tasksOnly });
+ emitMoveIssuablesEvent();
+
+ await nextTick();
+
+ expect(findDropdown().props('moveInProgress')).toBe(false);
+ });
+
+ it('keeps loading state to false when only test cases are selected', async () => {
+ createComponent({ selectedIssuables: selectedIssuesMocks.testCasesOnly });
+ emitMoveIssuablesEvent();
+
+ await nextTick();
+
+ expect(findDropdown().props('moveInProgress')).toBe(false);
+ });
+
+ it('keeps loading state to false when only tasks and test cases are selected', async () => {
+ createComponent({ selectedIssuables: selectedIssuesMocks.tasksAndTestCases });
+ emitMoveIssuablesEvent();
+
+ await nextTick();
+
+ expect(findDropdown().props('moveInProgress')).toBe(false);
+ });
+
+ it('sets loading state to true when issues are moving', async () => {
+ createComponent({ selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases });
+ emitMoveIssuablesEvent();
+
+ await nextTick();
+
+ expect(findDropdown().props('moveInProgress')).toBe(true);
+ });
+
+ it('sets loading state to false when all mutations succeed', async () => {
+ createComponent(
+ { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases },
+ resolvedMutationWithoutErrorsMock,
+ );
+ emitMoveIssuablesEvent();
+
+ await nextTick();
+ await waitForPromises();
+
+ expect(findDropdown().props('moveInProgress')).toBe(false);
+ });
+
+ it('sets loading state to false when a mutation returns errors', async () => {
+ createComponent(
+ { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases },
+ resolvedMutationWithErrorsMock,
+ );
+ emitMoveIssuablesEvent();
+
+ await nextTick();
+ await waitForPromises();
+
+ expect(findDropdown().props('moveInProgress')).toBe(false);
+ });
+
+ it('sets loading state to false when a mutation is rejected', async () => {
+ createComponent({ selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases });
+ emitMoveIssuablesEvent();
+
+ await nextTick();
+ await waitForPromises();
+
+ expect(findDropdown().props('moveInProgress')).toBe(false);
+ });
+ });
+
+ describe('handles events', () => {
+ beforeEach(() => {
+ jest.spyOn(issuableEventHub, '$emit');
+ });
+
+ it('does not emit any event when no issue is selected', async () => {
+ createComponent();
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(issuableEventHub.$emit).not.toHaveBeenCalled();
+ });
+
+ it('does not emit any event when only tasks are selected', async () => {
+ createComponent({ selectedIssuables: selectedIssuesMocks.tasksOnly });
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(issuableEventHub.$emit).not.toHaveBeenCalled();
+ });
+
+ it('does not emit any event when only test cases are selected', async () => {
+ createComponent({ selectedIssuables: selectedIssuesMocks.testCasesOnly });
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(issuableEventHub.$emit).not.toHaveBeenCalled();
+ });
+
+ it('does not emit any event when only tasks and test cases are selected', async () => {
+ createComponent({ selectedIssuables: selectedIssuesMocks.tasksAndTestCases });
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(issuableEventHub.$emit).not.toHaveBeenCalled();
+ });
+
+ it('emits `issuables:bulkMoveStarted` when issues are moving', async () => {
+ createComponent({ selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases });
+ emitMoveIssuablesEvent();
+
+ expect(issuableEventHub.$emit).toHaveBeenCalledWith('issuables:bulkMoveStarted');
+ });
+
+ it('emits `issuables:bulkMoveEnded` when all mutations succeed', async () => {
+ createComponent(
+ { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases },
+ resolvedMutationWithoutErrorsMock,
+ );
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(issuableEventHub.$emit).toHaveBeenCalledWith('issuables:bulkMoveEnded');
+ });
+
+ it('emits `issuables:bulkMoveEnded` when a mutation returns errors', async () => {
+ createComponent(
+ { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases },
+ resolvedMutationWithErrorsMock,
+ );
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(issuableEventHub.$emit).toHaveBeenCalledWith('issuables:bulkMoveEnded');
+ });
+
+ it('emits `issuables:bulkMoveEnded` when a mutation is rejected', async () => {
+ createComponent({ selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases });
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(issuableEventHub.$emit).toHaveBeenCalledWith('issuables:bulkMoveEnded');
+ });
+ });
+
+ describe('shows errors', () => {
+ it('does not create flashes or logs errors when no issue is selected', async () => {
+ createComponent();
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(logError).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
+ });
+
+ it('does not create flashes or logs errors when only tasks are selected', async () => {
+ createComponent({ selectedIssuables: selectedIssuesMocks.tasksOnly });
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(logError).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
+ });
+
+ it('does not create flashes or logs errors when only test cases are selected', async () => {
+ createComponent({ selectedIssuables: selectedIssuesMocks.testCasesOnly });
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(logError).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
+ });
+
+ it('does not create flashes or logs errors when only tasks and test cases are selected', async () => {
+ createComponent({ selectedIssuables: selectedIssuesMocks.tasksAndTestCases });
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(logError).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
+ });
+
+ it('does not create flashes or logs errors when issues are moved without errors', async () => {
+ createComponent(
+ { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases },
+ resolvedMutationWithoutErrorsMock,
+ );
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(logError).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
+ });
+
+ it('creates a flash and logs errors when a mutation returns errors', async () => {
+ createComponent(
+ { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases },
+ resolvedMutationWithErrorsMock,
+ );
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ // We're mocking two issues so it will log two errors
+ expect(logError).toHaveBeenCalledTimes(2);
+ expect(logError).toHaveBeenNthCalledWith(
+ 1,
+ `Error moving issue. Error message: ${mockMutationErrorMessage}`,
+ );
+ expect(logError).toHaveBeenNthCalledWith(
+ 2,
+ `Error moving issue. Error message: ${mockMutationErrorMessage}`,
+ );
+
+ // Only one flash is created even if multiple errors are reported
+ expect(createAlert).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalledWith({
+ message: 'There was an error while moving the issues.',
+ });
+ });
+
+ it('creates a flash but not logs errors when a mutation is rejected', async () => {
+ createComponent({ selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases });
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(logError).not.toHaveBeenCalled();
+ expect(createAlert).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalledWith({
+ message: 'There was an error while moving the issues.',
+ });
+ });
+ });
+
+ describe('calls mutations', () => {
+ it('does not call any mutation when no issue is selected', async () => {
+ createComponent({}, resolvedMutationWithoutErrorsMock);
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(resolvedMutationWithoutErrorsMock).not.toHaveBeenCalled();
+ });
+
+ it('does not call any mutation when only tasks are selected', async () => {
+ createComponent(
+ { selectedIssuables: selectedIssuesMocks.tasksOnly },
+ resolvedMutationWithoutErrorsMock,
+ );
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(resolvedMutationWithoutErrorsMock).not.toHaveBeenCalled();
+ });
+
+ it('does not call any mutation when only test cases are selected', async () => {
+ createComponent(
+ { selectedIssuables: selectedIssuesMocks.testCasesOnly },
+ resolvedMutationWithoutErrorsMock,
+ );
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(resolvedMutationWithoutErrorsMock).not.toHaveBeenCalled();
+ });
+
+ it('does not call any mutation when only tasks and test cases are selected', async () => {
+ createComponent(
+ { selectedIssuables: selectedIssuesMocks.tasksAndTestCases },
+ resolvedMutationWithoutErrorsMock,
+ );
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ expect(resolvedMutationWithoutErrorsMock).not.toHaveBeenCalled();
+ });
+
+ it('calls a mutation for every selected issue skipping tasks', async () => {
+ createComponent(
+ { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases },
+ resolvedMutationWithoutErrorsMock,
+ );
+ emitMoveIssuablesEvent();
+
+ await waitForPromises();
+
+ // We mock three elements but only two are valid issues since the task is skipped
+ expect(resolvedMutationWithoutErrorsMock).toHaveBeenCalledTimes(2);
+ expect(resolvedMutationWithoutErrorsMock).toHaveBeenNthCalledWith(1, {
+ moveIssueInput: {
+ projectPath: mockDefaultProps.projectFullPath,
+ iid: selectedIssuesMocks.issuesTasksAndTestCases[0].iid.toString(),
+ targetProjectPath: mockDestinationProject.full_path,
+ },
+ });
+
+ expect(resolvedMutationWithoutErrorsMock).toHaveBeenNthCalledWith(2, {
+ moveIssueInput: {
+ projectPath: mockDefaultProps.projectFullPath,
+ iid: selectedIssuesMocks.issuesTasksAndTestCases[1].iid.toString(),
+ targetProjectPath: mockDestinationProject.full_path,
+ },
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/participants_spec.js b/spec/frontend/sidebar/components/participants/participants_spec.js
index f7a626a189c..f7a626a189c 100644
--- a/spec/frontend/sidebar/participants_spec.js
+++ b/spec/frontend/sidebar/components/participants/participants_spec.js
diff --git a/spec/frontend/sidebar/components/reference/sidebar_reference_widget_spec.js b/spec/frontend/sidebar/components/reference/sidebar_reference_widget_spec.js
deleted file mode 100644
index 69e35cd1d05..00000000000
--- a/spec/frontend/sidebar/components/reference/sidebar_reference_widget_spec.js
+++ /dev/null
@@ -1,93 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { IssuableType } from '~/issues/constants';
-import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue';
-import issueReferenceQuery from '~/sidebar/queries/issue_reference.query.graphql';
-import mergeRequestReferenceQuery from '~/sidebar/queries/merge_request_reference.query.graphql';
-import CopyableField from '~/vue_shared/components/sidebar/copyable_field.vue';
-import { issueReferenceResponse } from '../../mock_data';
-
-describe('Sidebar Reference Widget', () => {
- let wrapper;
- let fakeApollo;
-
- const mockReferenceValue = 'reference-1234';
-
- const findCopyableField = () => wrapper.findComponent(CopyableField);
-
- const createComponent = ({
- issuableType = IssuableType.Issue,
- referenceQuery = issueReferenceQuery,
- referenceQueryHandler = jest.fn().mockResolvedValue(issueReferenceResponse(mockReferenceValue)),
- } = {}) => {
- Vue.use(VueApollo);
-
- fakeApollo = createMockApollo([[referenceQuery, referenceQueryHandler]]);
-
- wrapper = shallowMount(SidebarReferenceWidget, {
- apolloProvider: fakeApollo,
- provide: {
- fullPath: 'group/project',
- iid: '1',
- },
- propsData: {
- issuableType,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('when reference is loading', () => {
- it('sets CopyableField `is-loading` prop to `true`', () => {
- createComponent({ referenceQueryHandler: jest.fn().mockReturnValue(new Promise(() => {})) });
- expect(findCopyableField().props('isLoading')).toBe(true);
- });
- });
-
- describe.each([
- [IssuableType.Issue, issueReferenceQuery],
- [IssuableType.MergeRequest, mergeRequestReferenceQuery],
- ])('when issuableType is %s', (issuableType, referenceQuery) => {
- it('sets CopyableField `value` prop to reference value', async () => {
- createComponent({
- issuableType,
- referenceQuery,
- });
-
- await waitForPromises();
-
- expect(findCopyableField().props('value')).toBe(mockReferenceValue);
- });
-
- describe('when error occurs', () => {
- it('calls createFlash with correct parameters', async () => {
- const mockError = new Error('mayday');
-
- createComponent({
- issuableType,
- referenceQuery,
- referenceQueryHandler: jest.fn().mockRejectedValue(mockError),
- });
-
- await waitForPromises();
-
- const [
- [
- {
- message,
- error: { networkError },
- },
- ],
- ] = wrapper.emitted('fetch-error');
- expect(message).toBe('An error occurred while fetching reference');
- expect(networkError).toEqual(mockError);
- });
- });
- });
-});
diff --git a/spec/frontend/sidebar/reviewer_title_spec.js b/spec/frontend/sidebar/components/reviewers/reviewer_title_spec.js
index 68ecd62e4c6..68ecd62e4c6 100644
--- a/spec/frontend/sidebar/reviewer_title_spec.js
+++ b/spec/frontend/sidebar/components/reviewers/reviewer_title_spec.js
diff --git a/spec/frontend/sidebar/reviewers_spec.js b/spec/frontend/sidebar/components/reviewers/reviewers_spec.js
index 229f7ffbe04..229f7ffbe04 100644
--- a/spec/frontend/sidebar/reviewers_spec.js
+++ b/spec/frontend/sidebar/components/reviewers/reviewers_spec.js
diff --git a/spec/frontend/sidebar/components/reviewers/sidebar_reviewers_spec.js b/spec/frontend/sidebar/components/reviewers/sidebar_reviewers_spec.js
new file mode 100644
index 00000000000..57ae146a27a
--- /dev/null
+++ b/spec/frontend/sidebar/components/reviewers/sidebar_reviewers_spec.js
@@ -0,0 +1,77 @@
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import axios from 'axios';
+import AxiosMockAdapter from 'axios-mock-adapter';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import SidebarReviewers from '~/sidebar/components/reviewers/sidebar_reviewers.vue';
+import SidebarService from '~/sidebar/services/sidebar_service';
+import SidebarMediator from '~/sidebar/sidebar_mediator';
+import SidebarStore from '~/sidebar/stores/sidebar_store';
+import Mock from '../../mock_data';
+
+Vue.use(VueApollo);
+
+describe('sidebar reviewers', () => {
+ const apolloMock = createMockApollo();
+ let wrapper;
+ let mediator;
+ let axiosMock;
+
+ const createComponent = (props) => {
+ wrapper = shallowMount(SidebarReviewers, {
+ apolloProvider: apolloMock,
+ propsData: {
+ issuableIid: '1',
+ issuableId: 1,
+ mediator,
+ field: '',
+ projectPath: 'projectPath',
+ changing: false,
+ ...props,
+ },
+ // Attaching to document is required because this component emits something from the parent element :/
+ attachTo: document.body,
+ });
+ };
+
+ beforeEach(() => {
+ axiosMock = new AxiosMockAdapter(axios);
+ mediator = new SidebarMediator(Mock.mediator);
+
+ jest.spyOn(mediator, 'saveReviewers');
+ jest.spyOn(mediator, 'addSelfReview');
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+
+ SidebarService.singleton = null;
+ SidebarStore.singleton = null;
+ SidebarMediator.singleton = null;
+ axiosMock.restore();
+ });
+
+ it('calls the mediator when it saves the reviewers', () => {
+ createComponent();
+
+ expect(mediator.saveReviewers).not.toHaveBeenCalled();
+
+ wrapper.vm.saveReviewers();
+
+ expect(mediator.saveReviewers).toHaveBeenCalled();
+ });
+
+ it('calls the mediator when "reviewBySelf" method is called', () => {
+ createComponent();
+
+ expect(mediator.addSelfReview).not.toHaveBeenCalled();
+ expect(mediator.store.reviewers.length).toBe(0);
+
+ wrapper.vm.reviewBySelf();
+
+ expect(mediator.addSelfReview).toHaveBeenCalled();
+ expect(mediator.store.reviewers.length).toBe(1);
+ });
+});
diff --git a/spec/frontend/sidebar/components/severity/severity_spec.js b/spec/frontend/sidebar/components/severity/severity_spec.js
index 2146155791e..99d33e840d5 100644
--- a/spec/frontend/sidebar/components/severity/severity_spec.js
+++ b/spec/frontend/sidebar/components/severity/severity_spec.js
@@ -1,6 +1,6 @@
import { GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import { INCIDENT_SEVERITY } from '~/sidebar/components/severity/constants';
+import { INCIDENT_SEVERITY } from '~/sidebar/constants';
import SeverityToken from '~/sidebar/components/severity/severity.vue';
describe('SeverityToken', () => {
diff --git a/spec/frontend/sidebar/components/severity/sidebar_severity_spec.js b/spec/frontend/sidebar/components/severity/sidebar_severity_spec.js
index bdea33371d8..8f936240b7a 100644
--- a/spec/frontend/sidebar/components/severity/sidebar_severity_spec.js
+++ b/spec/frontend/sidebar/components/severity/sidebar_severity_spec.js
@@ -3,8 +3,8 @@ import { nextTick } from 'vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { createAlert } from '~/flash';
-import { INCIDENT_SEVERITY, ISSUABLE_TYPES } from '~/sidebar/components/severity/constants';
-import updateIssuableSeverity from '~/sidebar/components/severity/graphql/mutations/update_issuable_severity.mutation.graphql';
+import { INCIDENT_SEVERITY, ISSUABLE_TYPES } from '~/sidebar/constants';
+import updateIssuableSeverity from '~/sidebar/queries/update_issuable_severity.mutation.graphql';
import SeverityToken from '~/sidebar/components/severity/severity.vue';
import SidebarSeverity from '~/sidebar/components/severity/sidebar_severity.vue';
diff --git a/spec/frontend/sidebar/components/status/status_dropdown_spec.js b/spec/frontend/sidebar/components/status/status_dropdown_spec.js
new file mode 100644
index 00000000000..5a75299c3a4
--- /dev/null
+++ b/spec/frontend/sidebar/components/status/status_dropdown_spec.js
@@ -0,0 +1,75 @@
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import StatusDropdown from '~/sidebar/components/status/status_dropdown.vue';
+import { statusDropdownOptions } from '~/sidebar/constants';
+
+describe('SubscriptionsDropdown component', () => {
+ let wrapper;
+
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
+ const findHiddenInput = () => wrapper.find('input');
+
+ function createComponent() {
+ wrapper = shallowMount(StatusDropdown);
+ }
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('with no value selected', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders default text', () => {
+ expect(findDropdown().props('text')).toBe('Select status');
+ });
+
+ it('renders dropdown items with `is-checked` prop set to `false`', () => {
+ const dropdownItems = findAllDropdownItems();
+
+ expect(dropdownItems.at(0).props('isChecked')).toBe(false);
+ expect(dropdownItems.at(1).props('isChecked')).toBe(false);
+ });
+ });
+
+ describe('when selecting a value', () => {
+ const selectItemAtIndex = 0;
+
+ beforeEach(async () => {
+ createComponent();
+ await findAllDropdownItems().at(selectItemAtIndex).vm.$emit('click');
+ });
+
+ it('updates value of the hidden input', () => {
+ expect(findHiddenInput().attributes('value')).toBe(
+ statusDropdownOptions[selectItemAtIndex].value,
+ );
+ });
+
+ it('updates the dropdown text prop', () => {
+ expect(findDropdown().props('text')).toBe(statusDropdownOptions[selectItemAtIndex].text);
+ });
+
+ it('sets dropdown item `is-checked` prop to `true`', () => {
+ const dropdownItems = findAllDropdownItems();
+
+ expect(dropdownItems.at(0).props('isChecked')).toBe(true);
+ expect(dropdownItems.at(1).props('isChecked')).toBe(false);
+ });
+
+ describe('when selecting the value that is already selected', () => {
+ it('clears dropdown selection', async () => {
+ await findAllDropdownItems().at(selectItemAtIndex).vm.$emit('click');
+
+ const dropdownItems = findAllDropdownItems();
+
+ expect(dropdownItems.at(0).props('isChecked')).toBe(false);
+ expect(dropdownItems.at(1).props('isChecked')).toBe(false);
+ expect(findDropdown().props('text')).toBe('Select status');
+ });
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/subscriptions/subscriptions_dropdown_spec.js b/spec/frontend/sidebar/components/subscriptions/subscriptions_dropdown_spec.js
new file mode 100644
index 00000000000..3fb8214606c
--- /dev/null
+++ b/spec/frontend/sidebar/components/subscriptions/subscriptions_dropdown_spec.js
@@ -0,0 +1,76 @@
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import SubscriptionsDropdown from '~/sidebar/components/subscriptions/subscriptions_dropdown.vue';
+import { subscriptionsDropdownOptions } from '~/sidebar/constants';
+
+describe('SubscriptionsDropdown component', () => {
+ let wrapper;
+
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
+ const findHiddenInput = () => wrapper.find('input');
+
+ function createComponent() {
+ wrapper = shallowMount(SubscriptionsDropdown);
+ }
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('with no value selected', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('hidden input value is undefined', () => {
+ expect(findHiddenInput().attributes('value')).toBeUndefined();
+ });
+
+ it('renders default text', () => {
+ expect(findDropdown().props('text')).toBe(SubscriptionsDropdown.i18n.defaultDropdownText);
+ });
+
+ it('renders dropdown items with `is-checked` prop set to `false`', () => {
+ const dropdownItems = findAllDropdownItems();
+
+ expect(dropdownItems.at(0).props('isChecked')).toBe(false);
+ expect(dropdownItems.at(1).props('isChecked')).toBe(false);
+ });
+ });
+
+ describe('when selecting a value', () => {
+ beforeEach(() => {
+ createComponent();
+ findAllDropdownItems().at(0).vm.$emit('click');
+ });
+
+ it('updates value of the hidden input', () => {
+ expect(findHiddenInput().attributes('value')).toBe(subscriptionsDropdownOptions[0].value);
+ });
+
+ it('updates the dropdown text prop', () => {
+ expect(findDropdown().props('text')).toBe(subscriptionsDropdownOptions[0].text);
+ });
+
+ it('sets dropdown item `is-checked` prop to `true`', () => {
+ const dropdownItems = findAllDropdownItems();
+
+ expect(dropdownItems.at(0).props('isChecked')).toBe(true);
+ expect(dropdownItems.at(1).props('isChecked')).toBe(false);
+ });
+
+ describe('when selecting the value that is already selected', () => {
+ it('clears dropdown selection', async () => {
+ findAllDropdownItems().at(0).vm.$emit('click');
+ await nextTick();
+ const dropdownItems = findAllDropdownItems();
+
+ expect(dropdownItems.at(0).props('isChecked')).toBe(false);
+ expect(dropdownItems.at(1).props('isChecked')).toBe(false);
+ expect(findDropdown().props('text')).toBe(SubscriptionsDropdown.i18n.defaultDropdownText);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/subscriptions_spec.js b/spec/frontend/sidebar/components/subscriptions/subscriptions_spec.js
index 1a1aa370eef..1a1aa370eef 100644
--- a/spec/frontend/sidebar/subscriptions_spec.js
+++ b/spec/frontend/sidebar/components/subscriptions/subscriptions_spec.js
diff --git a/spec/frontend/sidebar/components/time_tracking/create_timelog_form_spec.js b/spec/frontend/sidebar/components/time_tracking/create_timelog_form_spec.js
new file mode 100644
index 00000000000..cb3bb7a4538
--- /dev/null
+++ b/spec/frontend/sidebar/components/time_tracking/create_timelog_form_spec.js
@@ -0,0 +1,219 @@
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
+import { GlAlert, GlModal } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+import CreateTimelogForm from '~/sidebar/components/time_tracking/create_timelog_form.vue';
+import createTimelogMutation from '~/sidebar/queries/create_timelog.mutation.graphql';
+import { TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/graphql_shared/constants';
+
+const mockMutationErrorMessage = 'Example error message';
+
+const resolvedMutationWithoutErrorsMock = jest.fn().mockResolvedValue({
+ data: {
+ timelogCreate: {
+ errors: [],
+ timelog: {
+ id: 'gid://gitlab/Timelog/1',
+ issue: {},
+ mergeRequest: {},
+ },
+ },
+ },
+});
+
+const resolvedMutationWithErrorsMock = jest.fn().mockResolvedValue({
+ data: {
+ timelogCreate: {
+ errors: [{ message: mockMutationErrorMessage }],
+ timelog: null,
+ },
+ },
+});
+
+const rejectedMutationMock = jest.fn().mockRejectedValue();
+const modalCloseMock = jest.fn();
+
+describe('Create Timelog Form', () => {
+ Vue.use(VueApollo);
+
+ let wrapper;
+ let fakeApollo;
+
+ const findForm = () => wrapper.find('form');
+ const findModal = () => wrapper.findComponent(GlModal);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findDocsLink = () => wrapper.findByTestId('timetracking-docs-link');
+ const findSaveButton = () => findModal().props('actionPrimary');
+ const findSaveButtonLoadingState = () => findSaveButton().attributes[0].loading;
+ const findSaveButtonDisabledState = () => findSaveButton().attributes[0].disabled;
+
+ const submitForm = () => findForm().trigger('submit');
+
+ const mountComponent = (
+ { props, data, providedProps } = {},
+ mutationResolverMock = rejectedMutationMock,
+ ) => {
+ fakeApollo = createMockApollo([[createTimelogMutation, mutationResolverMock]]);
+
+ wrapper = shallowMountExtended(CreateTimelogForm, {
+ data() {
+ return {
+ ...data,
+ };
+ },
+ provide: {
+ issuableType: 'issue',
+ ...providedProps,
+ },
+ propsData: {
+ issuableId: '1',
+ ...props,
+ },
+ apolloProvider: fakeApollo,
+ });
+
+ wrapper.vm.$refs.modal.close = modalCloseMock;
+ };
+
+ afterEach(() => {
+ fakeApollo = null;
+ });
+
+ describe('save button', () => {
+ it('is disabled and not loading by default', () => {
+ mountComponent();
+
+ expect(findSaveButtonLoadingState()).toBe(false);
+ expect(findSaveButtonDisabledState()).toBe(true);
+ });
+
+ it('is enabled and not loading when time spent is not empty', () => {
+ mountComponent({ data: { timeSpent: '2d' } });
+
+ expect(findSaveButtonLoadingState()).toBe(false);
+ expect(findSaveButtonDisabledState()).toBe(false);
+ });
+
+ it('is disabled and loading when the the form is submitted', async () => {
+ mountComponent({ data: { timeSpent: '2d' } });
+
+ submitForm();
+
+ await nextTick();
+
+ expect(findSaveButtonLoadingState()).toBe(true);
+ expect(findSaveButtonDisabledState()).toBe(true);
+ });
+
+ it('is enabled and not loading the when form is submitted but the mutation has errors', async () => {
+ mountComponent({ data: { timeSpent: '2d' } });
+
+ submitForm();
+
+ await waitForPromises();
+
+ expect(rejectedMutationMock).toHaveBeenCalled();
+ expect(findSaveButtonLoadingState()).toBe(false);
+ expect(findSaveButtonDisabledState()).toBe(false);
+ });
+
+ it('is enabled and not loading the when form is submitted but the mutation returns errors', async () => {
+ mountComponent({ data: { timeSpent: '2d' } }, resolvedMutationWithErrorsMock);
+
+ submitForm();
+
+ await waitForPromises();
+
+ expect(resolvedMutationWithErrorsMock).toHaveBeenCalled();
+ expect(findSaveButtonLoadingState()).toBe(false);
+ expect(findSaveButtonDisabledState()).toBe(false);
+ });
+ });
+
+ describe('form', () => {
+ it('does not call any mutation when the the form is incomplete', async () => {
+ mountComponent();
+
+ submitForm();
+
+ await waitForPromises();
+
+ expect(rejectedMutationMock).not.toHaveBeenCalled();
+ });
+
+ it('closes the modal after a successful mutation', async () => {
+ mountComponent({ data: { timeSpent: '2d' } }, resolvedMutationWithoutErrorsMock);
+
+ submitForm();
+
+ await waitForPromises();
+ await nextTick();
+
+ expect(modalCloseMock).toHaveBeenCalled();
+ });
+
+ it.each`
+ issuableType | typeConstant
+ ${'issue'} | ${TYPE_ISSUE}
+ ${'merge_request'} | ${TYPE_MERGE_REQUEST}
+ `(
+ 'calls the mutation with all the fields when the the form is submitted and issuable type is $issuableType',
+ async ({ issuableType, typeConstant }) => {
+ const timeSpent = '2d';
+ const spentAt = '2022-11-20T21:53:00+0000';
+ const summary = 'Example';
+
+ mountComponent({ data: { timeSpent, spentAt, summary }, providedProps: { issuableType } });
+
+ submitForm();
+
+ await waitForPromises();
+
+ expect(rejectedMutationMock).toHaveBeenCalledWith({
+ input: { timeSpent, spentAt, summary, issuableId: convertToGraphQLId(typeConstant, '1') },
+ });
+ },
+ );
+ });
+
+ describe('alert', () => {
+ it('is hidden by default', () => {
+ mountComponent();
+
+ expect(findAlert().exists()).toBe(false);
+ });
+
+ it('shows an error if the submission fails with a handled error', async () => {
+ mountComponent({ data: { timeSpent: '2d' } }, resolvedMutationWithErrorsMock);
+
+ submitForm();
+
+ await waitForPromises();
+
+ expect(findAlert().exists()).toBe(true);
+ expect(findAlert().text()).toBe(mockMutationErrorMessage);
+ });
+
+ it('shows an error if the submission fails with an unhandled error', async () => {
+ mountComponent({ data: { timeSpent: '2d' } });
+
+ submitForm();
+
+ await waitForPromises();
+
+ expect(findAlert().exists()).toBe(true);
+ expect(findAlert().text()).toBe('An error occurred while saving the time entry.');
+ });
+ });
+
+ describe('docs link message', () => {
+ it('is present', () => {
+ mountComponent();
+
+ expect(findDocsLink().exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/time_tracking/report_spec.js b/spec/frontend/sidebar/components/time_tracking/report_spec.js
index af72122052f..0259aee48f0 100644
--- a/spec/frontend/sidebar/components/time_tracking/report_spec.js
+++ b/spec/frontend/sidebar/components/time_tracking/report_spec.js
@@ -8,9 +8,9 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { createAlert } from '~/flash';
import Report from '~/sidebar/components/time_tracking/report.vue';
-import getIssueTimelogsQuery from '~/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql';
-import getMrTimelogsQuery from '~/vue_shared/components/sidebar/queries/get_mr_timelogs.query.graphql';
-import deleteTimelogMutation from '~/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql';
+import getIssueTimelogsQuery from '~/sidebar/queries/get_issue_timelogs.query.graphql';
+import getMrTimelogsQuery from '~/sidebar/queries/get_mr_timelogs.query.graphql';
+import deleteTimelogMutation from '~/sidebar/queries/delete_timelog.mutation.graphql';
import {
getIssueTimelogsQueryResponse,
getMrTimelogsQueryResponse,
diff --git a/spec/frontend/sidebar/components/time_tracking/time_tracker_spec.js b/spec/frontend/sidebar/components/time_tracking/time_tracker_spec.js
index 835e700e63c..45d8b5e4647 100644
--- a/spec/frontend/sidebar/components/time_tracking/time_tracker_spec.js
+++ b/spec/frontend/sidebar/components/time_tracking/time_tracker_spec.js
@@ -268,47 +268,32 @@ describe('Issuable Time Tracker', () => {
});
});
- describe('Help pane', () => {
- const findHelpButton = () => findByTestId('helpButton');
- const findCloseHelpButton = () => findByTestId('closeHelpButton');
-
- beforeEach(async () => {
- wrapper = mountComponent({
- props: {
- initialTimeTracking: {
- timeEstimate: 0,
- totalTimeSpent: 0,
- humanTimeEstimate: '',
- humanTotalTimeSpent: '',
+ describe('Add button', () => {
+ const findAddButton = () => findByTestId('add-time-entry-button');
+
+ it.each`
+ visibility | canAddTimeEntries
+ ${'not visible'} | ${false}
+ ${'visible'} | ${true}
+ `(
+ 'is $visibility when canAddTimeEntries is $canAddTimeEntries',
+ async ({ canAddTimeEntries }) => {
+ wrapper = mountComponent({
+ props: {
+ initialTimeTracking: {
+ timeEstimate: 0,
+ totalTimeSpent: 0,
+ humanTimeEstimate: '',
+ humanTotalTimeSpent: '',
+ },
+ canAddTimeEntries,
},
- },
- });
- await nextTick();
- });
-
- it('should not show the "Help" pane by default', () => {
- expect(findByTestId('helpPane').exists()).toBe(false);
- });
-
- it('should show the "Help" pane when help button is clicked', async () => {
- findHelpButton().trigger('click');
-
- await nextTick();
-
- expect(findByTestId('helpPane').exists()).toBe(true);
- });
-
- it('should not show the "Help" pane when help button is clicked and then closed', async () => {
- findHelpButton().trigger('click');
- await nextTick();
-
- expect(findByTestId('helpPane').exists()).toBe(true);
-
- findCloseHelpButton().trigger('click');
- await nextTick();
+ });
+ await nextTick();
- expect(findByTestId('helpPane').exists()).toBe(false);
- });
+ expect(findAddButton().exists()).toBe(canAddTimeEntries);
+ },
+ );
});
});
diff --git a/spec/frontend/sidebar/__snapshots__/todo_spec.js.snap b/spec/frontend/sidebar/components/todo_toggle/__snapshots__/todo_spec.js.snap
index 846f45345e7..846f45345e7 100644
--- a/spec/frontend/sidebar/__snapshots__/todo_spec.js.snap
+++ b/spec/frontend/sidebar/components/todo_toggle/__snapshots__/todo_spec.js.snap
diff --git a/spec/frontend/sidebar/components/todo_toggle/sidebar_todo_widget_spec.js b/spec/frontend/sidebar/components/todo_toggle/sidebar_todo_widget_spec.js
index f73491ca95f..5bfe3b59eb3 100644
--- a/spec/frontend/sidebar/components/todo_toggle/sidebar_todo_widget_spec.js
+++ b/spec/frontend/sidebar/components/todo_toggle/sidebar_todo_widget_spec.js
@@ -7,7 +7,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import { createAlert } from '~/flash';
import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
import epicTodoQuery from '~/sidebar/queries/epic_todo.query.graphql';
-import TodoButton from '~/vue_shared/components/sidebar/todo_toggle/todo_button.vue';
+import TodoButton from '~/sidebar/components/todo_toggle/todo_button.vue';
import { todosResponse, noTodosResponse } from '../../mock_data';
jest.mock('~/flash');
diff --git a/spec/frontend/sidebar/components/todo_toggle/todo_button_spec.js b/spec/frontend/sidebar/components/todo_toggle/todo_button_spec.js
new file mode 100644
index 00000000000..fb07029a249
--- /dev/null
+++ b/spec/frontend/sidebar/components/todo_toggle/todo_button_spec.js
@@ -0,0 +1,68 @@
+import { GlButton } from '@gitlab/ui';
+import { shallowMount, mount } from '@vue/test-utils';
+import TodoButton from '~/sidebar/components/todo_toggle/todo_button.vue';
+
+describe('Todo Button', () => {
+ let wrapper;
+ let dispatchEventSpy;
+
+ const createComponent = (props = {}, mountFn = shallowMount) => {
+ wrapper = mountFn(TodoButton, {
+ propsData: {
+ ...props,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ dispatchEventSpy = jest.spyOn(document, 'dispatchEvent');
+ jest.spyOn(document, 'querySelector').mockReturnValue({
+ innerText: 2,
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ dispatchEventSpy = null;
+ jest.clearAllMocks();
+ });
+
+ it('renders GlButton', () => {
+ createComponent();
+
+ expect(wrapper.findComponent(GlButton).exists()).toBe(true);
+ });
+
+ it('emits click event when clicked', () => {
+ createComponent({}, mount);
+ wrapper.findComponent(GlButton).trigger('click');
+
+ expect(wrapper.emitted().click).toHaveLength(1);
+ });
+
+ it('calls dispatchDocumentEvent to update global To-Do counter correctly', () => {
+ createComponent({}, mount);
+ wrapper.findComponent(GlButton).trigger('click');
+ const dispatchedEvent = dispatchEventSpy.mock.calls[0][0];
+
+ expect(dispatchEventSpy).toHaveBeenCalledTimes(1);
+ expect(dispatchedEvent.detail).toEqual({ count: 1 });
+ expect(dispatchedEvent.type).toBe('todo:toggle');
+ });
+
+ it.each`
+ label | isTodo
+ ${'Mark as done'} | ${true}
+ ${'Add a to do'} | ${false}
+ `('sets correct label when isTodo is $isTodo', ({ label, isTodo }) => {
+ createComponent({ isTodo });
+
+ expect(wrapper.findComponent(GlButton).text()).toBe(label);
+ });
+
+ it('binds additional props to GlButton', () => {
+ createComponent({ loading: true });
+
+ expect(wrapper.findComponent(GlButton).props('loading')).toBe(true);
+ });
+});
diff --git a/spec/frontend/sidebar/todo_spec.js b/spec/frontend/sidebar/components/todo_toggle/todo_spec.js
index 8e6597bf80f..8e6597bf80f 100644
--- a/spec/frontend/sidebar/todo_spec.js
+++ b/spec/frontend/sidebar/components/todo_toggle/todo_spec.js
diff --git a/spec/frontend/sidebar/components/toggle/toggle_sidebar_spec.js b/spec/frontend/sidebar/components/toggle/toggle_sidebar_spec.js
new file mode 100644
index 00000000000..cf9b2828dde
--- /dev/null
+++ b/spec/frontend/sidebar/components/toggle/toggle_sidebar_spec.js
@@ -0,0 +1,46 @@
+import { GlButton } from '@gitlab/ui';
+import { mount, shallowMount } from '@vue/test-utils';
+
+import { nextTick } from 'vue';
+import ToggleSidebar from '~/sidebar/components/toggle/toggle_sidebar.vue';
+
+describe('ToggleSidebar', () => {
+ let wrapper;
+
+ const defaultProps = {
+ collapsed: true,
+ };
+
+ const createComponent = ({ mountFn = shallowMount, props = {} } = {}) => {
+ wrapper = mountFn(ToggleSidebar, {
+ propsData: { ...defaultProps, ...props },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findGlButton = () => wrapper.findComponent(GlButton);
+
+ it('should render the "chevron-double-lg-left" icon when collapsed', () => {
+ createComponent();
+
+ expect(findGlButton().props('icon')).toBe('chevron-double-lg-left');
+ });
+
+ it('should render the "chevron-double-lg-right" icon when expanded', async () => {
+ createComponent({ props: { collapsed: false } });
+
+ expect(findGlButton().props('icon')).toBe('chevron-double-lg-right');
+ });
+
+ it('should emit toggle event when button clicked', async () => {
+ createComponent({ mountFn: mount });
+
+ findGlButton().trigger('click');
+ await nextTick();
+
+ expect(wrapper.emitted('toggle')[0]).toBeDefined();
+ });
+});
diff --git a/spec/frontend/sidebar/lib/sidebar_move_issue_spec.js b/spec/frontend/sidebar/lib/sidebar_move_issue_spec.js
new file mode 100644
index 00000000000..6e365df329b
--- /dev/null
+++ b/spec/frontend/sidebar/lib/sidebar_move_issue_spec.js
@@ -0,0 +1,162 @@
+import MockAdapter from 'axios-mock-adapter';
+import $ from 'jquery';
+import waitForPromises from 'helpers/wait_for_promises';
+import { createAlert } from '~/flash';
+import axios from '~/lib/utils/axios_utils';
+import SidebarMoveIssue from '~/sidebar/lib/sidebar_move_issue';
+import SidebarService from '~/sidebar/services/sidebar_service';
+import SidebarMediator from '~/sidebar/sidebar_mediator';
+import SidebarStore from '~/sidebar/stores/sidebar_store';
+import { GitLabDropdown } from '~/deprecated_jquery_dropdown/gl_dropdown';
+import Mock from '../mock_data';
+
+jest.mock('~/flash');
+
+describe('SidebarMoveIssue', () => {
+ let mock;
+ const test = {};
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ const mockData = Mock.responseMap.GET['/autocomplete/projects?project_id=15'];
+ mock.onGet('/autocomplete/projects?project_id=15').reply(200, mockData);
+ test.mediator = new SidebarMediator(Mock.mediator);
+ test.$content = $(`
+ <div class="dropdown">
+ <div class="js-toggle"></div>
+ <div class="dropdown-menu">
+ <div class="dropdown-content"></div>
+ </div>
+ <div class="js-confirm-button"></div>
+ </div>
+ `);
+ test.$toggleButton = test.$content.find('.js-toggle');
+ test.$confirmButton = test.$content.find('.js-confirm-button');
+
+ test.sidebarMoveIssue = new SidebarMoveIssue(
+ test.mediator,
+ test.$toggleButton,
+ test.$confirmButton,
+ );
+ test.sidebarMoveIssue.init();
+ });
+
+ afterEach(() => {
+ SidebarService.singleton = null;
+ SidebarStore.singleton = null;
+ SidebarMediator.singleton = null;
+
+ test.sidebarMoveIssue.destroy();
+ mock.restore();
+ });
+
+ describe('init', () => {
+ it('should initialize the dropdown and listeners', () => {
+ jest.spyOn(test.sidebarMoveIssue, 'initDropdown').mockImplementation(() => {});
+ jest.spyOn(test.sidebarMoveIssue, 'addEventListeners').mockImplementation(() => {});
+
+ test.sidebarMoveIssue.init();
+
+ expect(test.sidebarMoveIssue.initDropdown).toHaveBeenCalled();
+ expect(test.sidebarMoveIssue.addEventListeners).toHaveBeenCalled();
+ });
+ });
+
+ describe('destroy', () => {
+ it('should remove the listeners', () => {
+ jest.spyOn(test.sidebarMoveIssue, 'removeEventListeners').mockImplementation(() => {});
+
+ test.sidebarMoveIssue.destroy();
+
+ expect(test.sidebarMoveIssue.removeEventListeners).toHaveBeenCalled();
+ });
+ });
+
+ describe('initDropdown', () => {
+ it('should initialize the deprecatedJQueryDropdown', () => {
+ test.sidebarMoveIssue.initDropdown();
+
+ expect(test.sidebarMoveIssue.$dropdownToggle.data('deprecatedJQueryDropdown')).toBeInstanceOf(
+ GitLabDropdown,
+ );
+ });
+
+ it('escapes html from project name', async () => {
+ test.$toggleButton.dropdown('toggle');
+
+ await waitForPromises();
+
+ expect(test.$content.find('.js-move-issue-dropdown-item')[1].innerHTML.trim()).toEqual(
+ '&lt;img src=x onerror=alert(document.domain)&gt; foo / bar',
+ );
+ });
+ });
+
+ describe('onConfirmClicked', () => {
+ it('should move the issue with valid project ID', () => {
+ jest.spyOn(test.mediator, 'moveIssue').mockReturnValue(Promise.resolve());
+ test.mediator.setMoveToProjectId(7);
+
+ test.sidebarMoveIssue.onConfirmClicked();
+
+ expect(test.mediator.moveIssue).toHaveBeenCalled();
+ expect(test.$confirmButton.prop('disabled')).toBe(true);
+ expect(test.$confirmButton.hasClass('is-loading')).toBe(true);
+ });
+
+ it('should remove loading state from confirm button on failure', async () => {
+ jest.spyOn(test.mediator, 'moveIssue').mockReturnValue(Promise.reject());
+ test.mediator.setMoveToProjectId(7);
+
+ test.sidebarMoveIssue.onConfirmClicked();
+
+ expect(test.mediator.moveIssue).toHaveBeenCalled();
+
+ // Wait for the move issue request to fail
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalled();
+ expect(test.$confirmButton.prop('disabled')).toBe(false);
+ expect(test.$confirmButton.hasClass('is-loading')).toBe(false);
+ });
+
+ it('should not move the issue with id=0', () => {
+ jest.spyOn(test.mediator, 'moveIssue').mockImplementation(() => {});
+ test.mediator.setMoveToProjectId(0);
+
+ test.sidebarMoveIssue.onConfirmClicked();
+
+ expect(test.mediator.moveIssue).not.toHaveBeenCalled();
+ });
+ });
+
+ it('should set moveToProjectId on dropdown item "No project" click', async () => {
+ jest.spyOn(test.mediator, 'setMoveToProjectId').mockImplementation(() => {});
+
+ // Open the dropdown
+ test.$toggleButton.dropdown('toggle');
+
+ // Wait for the autocomplete request to finish
+ await waitForPromises();
+
+ test.$content.find('.js-move-issue-dropdown-item').eq(0).trigger('click');
+
+ expect(test.mediator.setMoveToProjectId).toHaveBeenCalledWith(0);
+ expect(test.$confirmButton.prop('disabled')).toBe(true);
+ });
+
+ it('should set moveToProjectId on dropdown item click', async () => {
+ jest.spyOn(test.mediator, 'setMoveToProjectId').mockImplementation(() => {});
+
+ // Open the dropdown
+ test.$toggleButton.dropdown('toggle');
+
+ // Wait for the autocomplete request to finish
+ await waitForPromises();
+
+ test.$content.find('.js-move-issue-dropdown-item').eq(1).trigger('click');
+
+ expect(test.mediator.setMoveToProjectId).toHaveBeenCalledWith(20);
+ expect(test.$confirmButton.attr('disabled')).toBe(undefined);
+ });
+});
diff --git a/spec/frontend/sidebar/sidebar_assignees_spec.js b/spec/frontend/sidebar/sidebar_assignees_spec.js
deleted file mode 100644
index 2cb2425532b..00000000000
--- a/spec/frontend/sidebar/sidebar_assignees_spec.js
+++ /dev/null
@@ -1,99 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import axios from 'axios';
-import AxiosMockAdapter from 'axios-mock-adapter';
-import { nextTick } from 'vue';
-import Assigness from '~/sidebar/components/assignees/assignees.vue';
-import AssigneesRealtime from '~/sidebar/components/assignees/assignees_realtime.vue';
-import SidebarAssignees from '~/sidebar/components/assignees/sidebar_assignees.vue';
-import SidebarService from '~/sidebar/services/sidebar_service';
-import SidebarMediator from '~/sidebar/sidebar_mediator';
-import SidebarStore from '~/sidebar/stores/sidebar_store';
-import Mock from './mock_data';
-
-describe('sidebar assignees', () => {
- let wrapper;
- let mediator;
- let axiosMock;
- const createComponent = (props) => {
- wrapper = shallowMount(SidebarAssignees, {
- propsData: {
- issuableIid: '1',
- issuableId: 1,
- mediator,
- field: '',
- projectPath: 'projectPath',
- changing: false,
- ...props,
- },
- // Attaching to document is required because this component emits something from the parent element :/
- attachTo: document.body,
- });
- };
-
- beforeEach(() => {
- axiosMock = new AxiosMockAdapter(axios);
- mediator = new SidebarMediator(Mock.mediator);
-
- jest.spyOn(mediator, 'saveAssignees');
- jest.spyOn(mediator, 'assignYourself');
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
-
- SidebarService.singleton = null;
- SidebarStore.singleton = null;
- SidebarMediator.singleton = null;
- axiosMock.restore();
- });
-
- it('calls the mediator when saves the assignees', () => {
- createComponent();
-
- expect(mediator.saveAssignees).not.toHaveBeenCalled();
-
- wrapper.vm.saveAssignees();
-
- expect(mediator.saveAssignees).toHaveBeenCalled();
- });
-
- it('calls the mediator when "assignSelf" method is called', () => {
- createComponent();
-
- expect(mediator.assignYourself).not.toHaveBeenCalled();
- expect(mediator.store.assignees.length).toBe(0);
-
- wrapper.vm.assignSelf();
-
- expect(mediator.assignYourself).toHaveBeenCalled();
- expect(mediator.store.assignees.length).toBe(1);
- });
-
- it('hides assignees until fetched', async () => {
- createComponent();
-
- expect(wrapper.findComponent(Assigness).exists()).toBe(false);
-
- wrapper.vm.store.isFetching.assignees = false;
-
- await nextTick();
- expect(wrapper.findComponent(Assigness).exists()).toBe(true);
- });
-
- describe('when issuableType is issue', () => {
- it('finds AssigneesRealtime component', () => {
- createComponent();
-
- expect(wrapper.findComponent(AssigneesRealtime).exists()).toBe(true);
- });
- });
-
- describe('when issuableType is MR', () => {
- it('does not find AssigneesRealtime component', () => {
- createComponent({ issuableType: 'MR' });
-
- expect(wrapper.findComponent(AssigneesRealtime).exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/sidebar/sidebar_mediator_spec.js b/spec/frontend/sidebar/sidebar_mediator_spec.js
index bb5e7f7ff16..cdb9ced70b8 100644
--- a/spec/frontend/sidebar/sidebar_mediator_spec.js
+++ b/spec/frontend/sidebar/sidebar_mediator_spec.js
@@ -24,7 +24,8 @@ describe('Sidebar mediator', () => {
SidebarService.singleton = null;
SidebarStore.singleton = null;
SidebarMediator.singleton = null;
- mock.restore();
+
+ jest.clearAllMocks();
});
it('assigns yourself', () => {
@@ -42,6 +43,52 @@ describe('Sidebar mediator', () => {
});
});
+ it('assigns yourself as a reviewer', () => {
+ mediator.addSelfReview();
+
+ expect(mediator.store.currentUser).toEqual(mediatorMockData.currentUser);
+ expect(mediator.store.reviewers[0]).toEqual(mediatorMockData.currentUser);
+ });
+
+ describe('saves reviewers', () => {
+ const mockUpdateResponseData = {
+ reviewers: [1, 2],
+ assignees: [3, 4],
+ };
+ const field = 'merge_request[reviewers_ids]';
+ const reviewers = [
+ { id: 1, suggested: true },
+ { id: 2, suggested: false },
+ ];
+
+ let serviceSpy;
+
+ beforeEach(() => {
+ mediator.store.reviewers = reviewers;
+ serviceSpy = jest
+ .spyOn(mediator.service, 'update')
+ .mockReturnValue(Promise.resolve({ data: mockUpdateResponseData }));
+ });
+
+ it('sends correct data to service', () => {
+ const data = {
+ reviewer_ids: [1, 2],
+ suggested_reviewer_ids: [1],
+ };
+
+ mediator.saveReviewers(field);
+
+ expect(serviceSpy).toHaveBeenCalledWith(field, data);
+ });
+
+ it('saves reviewers', () => {
+ return mediator.saveReviewers(field).then(() => {
+ expect(mediator.store.assignees).toEqual(mockUpdateResponseData.assignees);
+ expect(mediator.store.reviewers).toEqual(mockUpdateResponseData.reviewers);
+ });
+ });
+ });
+
it('fetches the data', async () => {
const mockData = Mock.responseMap.GET[mediatorMockData.endpoint];
mock.onGet(mediatorMockData.endpoint).reply(200, mockData);
@@ -49,7 +96,6 @@ describe('Sidebar mediator', () => {
await mediator.fetch();
expect(spy).toHaveBeenCalledWith(mockData);
- spy.mockRestore();
});
it('processes fetched data', () => {
@@ -70,8 +116,6 @@ describe('Sidebar mediator', () => {
mediator.setMoveToProjectId(projectId);
expect(spy).toHaveBeenCalledWith(projectId);
-
- spy.mockRestore();
});
it('fetches autocomplete projects', () => {
@@ -87,9 +131,6 @@ describe('Sidebar mediator', () => {
return mediator.fetchAutocompleteProjects(searchTerm).then(() => {
expect(getterSpy).toHaveBeenCalledWith(searchTerm);
expect(setterSpy).toHaveBeenCalled();
-
- getterSpy.mockRestore();
- setterSpy.mockRestore();
});
});
@@ -106,9 +147,6 @@ describe('Sidebar mediator', () => {
return mediator.moveIssue().then(() => {
expect(moveIssueSpy).toHaveBeenCalledWith(moveToProjectId);
expect(urlSpy).toHaveBeenCalledWith(mockData.web_url);
-
- moveIssueSpy.mockRestore();
- urlSpy.mockRestore();
});
});
});
diff --git a/spec/frontend/sidebar/sidebar_move_issue_spec.js b/spec/frontend/sidebar/sidebar_move_issue_spec.js
deleted file mode 100644
index 195cc6ddeeb..00000000000
--- a/spec/frontend/sidebar/sidebar_move_issue_spec.js
+++ /dev/null
@@ -1,162 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import $ from 'jquery';
-import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert } from '~/flash';
-import axios from '~/lib/utils/axios_utils';
-import SidebarMoveIssue from '~/sidebar/lib/sidebar_move_issue';
-import SidebarService from '~/sidebar/services/sidebar_service';
-import SidebarMediator from '~/sidebar/sidebar_mediator';
-import SidebarStore from '~/sidebar/stores/sidebar_store';
-import { GitLabDropdown } from '~/deprecated_jquery_dropdown/gl_dropdown';
-import Mock from './mock_data';
-
-jest.mock('~/flash');
-
-describe('SidebarMoveIssue', () => {
- let mock;
- const test = {};
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- const mockData = Mock.responseMap.GET['/autocomplete/projects?project_id=15'];
- mock.onGet('/autocomplete/projects?project_id=15').reply(200, mockData);
- test.mediator = new SidebarMediator(Mock.mediator);
- test.$content = $(`
- <div class="dropdown">
- <div class="js-toggle"></div>
- <div class="dropdown-menu">
- <div class="dropdown-content"></div>
- </div>
- <div class="js-confirm-button"></div>
- </div>
- `);
- test.$toggleButton = test.$content.find('.js-toggle');
- test.$confirmButton = test.$content.find('.js-confirm-button');
-
- test.sidebarMoveIssue = new SidebarMoveIssue(
- test.mediator,
- test.$toggleButton,
- test.$confirmButton,
- );
- test.sidebarMoveIssue.init();
- });
-
- afterEach(() => {
- SidebarService.singleton = null;
- SidebarStore.singleton = null;
- SidebarMediator.singleton = null;
-
- test.sidebarMoveIssue.destroy();
- mock.restore();
- });
-
- describe('init', () => {
- it('should initialize the dropdown and listeners', () => {
- jest.spyOn(test.sidebarMoveIssue, 'initDropdown').mockImplementation(() => {});
- jest.spyOn(test.sidebarMoveIssue, 'addEventListeners').mockImplementation(() => {});
-
- test.sidebarMoveIssue.init();
-
- expect(test.sidebarMoveIssue.initDropdown).toHaveBeenCalled();
- expect(test.sidebarMoveIssue.addEventListeners).toHaveBeenCalled();
- });
- });
-
- describe('destroy', () => {
- it('should remove the listeners', () => {
- jest.spyOn(test.sidebarMoveIssue, 'removeEventListeners').mockImplementation(() => {});
-
- test.sidebarMoveIssue.destroy();
-
- expect(test.sidebarMoveIssue.removeEventListeners).toHaveBeenCalled();
- });
- });
-
- describe('initDropdown', () => {
- it('should initialize the deprecatedJQueryDropdown', () => {
- test.sidebarMoveIssue.initDropdown();
-
- expect(test.sidebarMoveIssue.$dropdownToggle.data('deprecatedJQueryDropdown')).toBeInstanceOf(
- GitLabDropdown,
- );
- });
-
- it('escapes html from project name', async () => {
- test.$toggleButton.dropdown('toggle');
-
- await waitForPromises();
-
- expect(test.$content.find('.js-move-issue-dropdown-item')[1].innerHTML.trim()).toEqual(
- '&lt;img src=x onerror=alert(document.domain)&gt; foo / bar',
- );
- });
- });
-
- describe('onConfirmClicked', () => {
- it('should move the issue with valid project ID', () => {
- jest.spyOn(test.mediator, 'moveIssue').mockReturnValue(Promise.resolve());
- test.mediator.setMoveToProjectId(7);
-
- test.sidebarMoveIssue.onConfirmClicked();
-
- expect(test.mediator.moveIssue).toHaveBeenCalled();
- expect(test.$confirmButton.prop('disabled')).toBe(true);
- expect(test.$confirmButton.hasClass('is-loading')).toBe(true);
- });
-
- it('should remove loading state from confirm button on failure', async () => {
- jest.spyOn(test.mediator, 'moveIssue').mockReturnValue(Promise.reject());
- test.mediator.setMoveToProjectId(7);
-
- test.sidebarMoveIssue.onConfirmClicked();
-
- expect(test.mediator.moveIssue).toHaveBeenCalled();
-
- // Wait for the move issue request to fail
- await waitForPromises();
-
- expect(createAlert).toHaveBeenCalled();
- expect(test.$confirmButton.prop('disabled')).toBe(false);
- expect(test.$confirmButton.hasClass('is-loading')).toBe(false);
- });
-
- it('should not move the issue with id=0', () => {
- jest.spyOn(test.mediator, 'moveIssue').mockImplementation(() => {});
- test.mediator.setMoveToProjectId(0);
-
- test.sidebarMoveIssue.onConfirmClicked();
-
- expect(test.mediator.moveIssue).not.toHaveBeenCalled();
- });
- });
-
- it('should set moveToProjectId on dropdown item "No project" click', async () => {
- jest.spyOn(test.mediator, 'setMoveToProjectId').mockImplementation(() => {});
-
- // Open the dropdown
- test.$toggleButton.dropdown('toggle');
-
- // Wait for the autocomplete request to finish
- await waitForPromises();
-
- test.$content.find('.js-move-issue-dropdown-item').eq(0).trigger('click');
-
- expect(test.mediator.setMoveToProjectId).toHaveBeenCalledWith(0);
- expect(test.$confirmButton.prop('disabled')).toBe(true);
- });
-
- it('should set moveToProjectId on dropdown item click', async () => {
- jest.spyOn(test.mediator, 'setMoveToProjectId').mockImplementation(() => {});
-
- // Open the dropdown
- test.$toggleButton.dropdown('toggle');
-
- // Wait for the autocomplete request to finish
- await waitForPromises();
-
- test.$content.find('.js-move-issue-dropdown-item').eq(1).trigger('click');
-
- expect(test.mediator.setMoveToProjectId).toHaveBeenCalledWith(20);
- expect(test.$confirmButton.attr('disabled')).toBe(undefined);
- });
-});
diff --git a/spec/frontend/sidebar/sidebar_store_spec.js b/spec/frontend/sidebar/sidebar_store_spec.js
deleted file mode 100644
index 3930dabfcfa..00000000000
--- a/spec/frontend/sidebar/sidebar_store_spec.js
+++ /dev/null
@@ -1,160 +0,0 @@
-import UsersMockHelper from 'helpers/user_mock_data_helper';
-import SidebarStore from '~/sidebar/stores/sidebar_store';
-import Mock from './mock_data';
-
-const ASSIGNEE = {
- id: 2,
- name: 'gitlab user 2',
- username: 'gitlab2',
- avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
-};
-
-const ANOTHER_ASSINEE = {
- id: 3,
- name: 'gitlab user 3',
- username: 'gitlab3',
- avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
-};
-
-describe('Sidebar store', () => {
- let testContext;
-
- beforeEach(() => {
- testContext = {};
- });
-
- beforeEach(() => {
- testContext.store = new SidebarStore({
- currentUser: {
- id: 1,
- name: 'Administrator',
- username: 'root',
- avatar_url:
- 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- },
- editable: true,
- rootPath: '/',
- endpoint: '/gitlab-org/gitlab-shell/issues/5.json',
- });
- });
-
- afterEach(() => {
- SidebarStore.singleton = null;
- });
-
- it('has default isFetching values', () => {
- expect(testContext.store.isFetching.assignees).toBe(true);
- });
-
- it('resets changing when resetChanging is called', () => {
- testContext.store.changing = true;
-
- testContext.store.resetChanging();
-
- expect(testContext.store.changing).toBe(false);
- });
-
- describe('when it adds a new assignee', () => {
- beforeEach(() => {
- testContext.store.addAssignee(ASSIGNEE);
- });
-
- it('adds a new assignee', () => {
- expect(testContext.store.assignees).toHaveLength(1);
- });
-
- it('sets changing to true', () => {
- expect(testContext.store.changing).toBe(true);
- });
- });
-
- describe('when it removes an assignee', () => {
- beforeEach(() => {
- testContext.store.removeAssignee(ASSIGNEE);
- });
-
- it('removes an assignee', () => {
- expect(testContext.store.assignees).toHaveLength(0);
- });
-
- it('sets changing to true', () => {
- expect(testContext.store.changing).toBe(true);
- });
- });
-
- it('finds an existent assignee', () => {
- let foundAssignee;
-
- testContext.store.addAssignee(ASSIGNEE);
- foundAssignee = testContext.store.findAssignee(ASSIGNEE);
-
- expect(foundAssignee).toBeDefined();
- expect(foundAssignee).toEqual(ASSIGNEE);
- foundAssignee = testContext.store.findAssignee(ANOTHER_ASSINEE);
-
- expect(foundAssignee).toBeUndefined();
- });
-
- it('removes all assignees', () => {
- testContext.store.removeAllAssignees();
-
- expect(testContext.store.assignees.length).toEqual(0);
- expect(testContext.store.changing).toBe(true);
- });
-
- it('set assigned data', () => {
- const users = {
- assignees: UsersMockHelper.createNumberRandomUsers(3),
- };
-
- testContext.store.setAssigneeData(users);
-
- expect(testContext.store.isFetching.assignees).toBe(false);
- expect(testContext.store.assignees.length).toEqual(3);
- });
-
- it('sets fetching state', () => {
- expect(testContext.store.isFetching.assignees).toEqual(true);
-
- testContext.store.setFetchingState('assignees', false);
-
- expect(testContext.store.isFetching.assignees).toEqual(false);
- });
-
- it('sets loading state', () => {
- testContext.store.setLoadingState('assignees', true);
-
- expect(testContext.store.isLoading.assignees).toEqual(true);
- });
-
- it('set time tracking data', () => {
- testContext.store.setTimeTrackingData(Mock.time);
-
- expect(testContext.store.timeEstimate).toEqual(Mock.time.time_estimate);
- expect(testContext.store.totalTimeSpent).toEqual(Mock.time.total_time_spent);
- expect(testContext.store.humanTimeEstimate).toEqual(Mock.time.human_time_estimate);
- expect(testContext.store.humanTotalTimeSpent).toEqual(Mock.time.human_total_time_spent);
- });
-
- it('set autocomplete projects', () => {
- const projects = [{ id: 0 }];
- testContext.store.setAutocompleteProjects(projects);
-
- expect(testContext.store.autocompleteProjects).toEqual(projects);
- });
-
- it('sets subscribed state', () => {
- expect(testContext.store.subscribed).toEqual(null);
-
- testContext.store.setSubscribedState(true);
-
- expect(testContext.store.subscribed).toEqual(true);
- });
-
- it('set move to project ID', () => {
- const projectId = 7;
- testContext.store.setMoveToProjectId(projectId);
-
- expect(testContext.store.moveToProjectId).toEqual(projectId);
- });
-});
diff --git a/spec/frontend/sidebar/stores/sidebar_store_spec.js b/spec/frontend/sidebar/stores/sidebar_store_spec.js
new file mode 100644
index 00000000000..3f4b80409c2
--- /dev/null
+++ b/spec/frontend/sidebar/stores/sidebar_store_spec.js
@@ -0,0 +1,160 @@
+import UsersMockHelper from 'helpers/user_mock_data_helper';
+import SidebarStore from '~/sidebar/stores/sidebar_store';
+import Mock from '../mock_data';
+
+const ASSIGNEE = {
+ id: 2,
+ name: 'gitlab user 2',
+ username: 'gitlab2',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+};
+
+const ANOTHER_ASSINEE = {
+ id: 3,
+ name: 'gitlab user 3',
+ username: 'gitlab3',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+};
+
+describe('Sidebar store', () => {
+ let testContext;
+
+ beforeEach(() => {
+ testContext = {};
+ });
+
+ beforeEach(() => {
+ testContext.store = new SidebarStore({
+ currentUser: {
+ id: 1,
+ name: 'Administrator',
+ username: 'root',
+ avatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ },
+ editable: true,
+ rootPath: '/',
+ endpoint: '/gitlab-org/gitlab-shell/issues/5.json',
+ });
+ });
+
+ afterEach(() => {
+ SidebarStore.singleton = null;
+ });
+
+ it('has default isFetching values', () => {
+ expect(testContext.store.isFetching.assignees).toBe(true);
+ });
+
+ it('resets changing when resetChanging is called', () => {
+ testContext.store.changing = true;
+
+ testContext.store.resetChanging();
+
+ expect(testContext.store.changing).toBe(false);
+ });
+
+ describe('when it adds a new assignee', () => {
+ beforeEach(() => {
+ testContext.store.addAssignee(ASSIGNEE);
+ });
+
+ it('adds a new assignee', () => {
+ expect(testContext.store.assignees).toHaveLength(1);
+ });
+
+ it('sets changing to true', () => {
+ expect(testContext.store.changing).toBe(true);
+ });
+ });
+
+ describe('when it removes an assignee', () => {
+ beforeEach(() => {
+ testContext.store.removeAssignee(ASSIGNEE);
+ });
+
+ it('removes an assignee', () => {
+ expect(testContext.store.assignees).toHaveLength(0);
+ });
+
+ it('sets changing to true', () => {
+ expect(testContext.store.changing).toBe(true);
+ });
+ });
+
+ it('finds an existent assignee', () => {
+ let foundAssignee;
+
+ testContext.store.addAssignee(ASSIGNEE);
+ foundAssignee = testContext.store.findAssignee(ASSIGNEE);
+
+ expect(foundAssignee).toBeDefined();
+ expect(foundAssignee).toEqual(ASSIGNEE);
+ foundAssignee = testContext.store.findAssignee(ANOTHER_ASSINEE);
+
+ expect(foundAssignee).toBeUndefined();
+ });
+
+ it('removes all assignees', () => {
+ testContext.store.removeAllAssignees();
+
+ expect(testContext.store.assignees.length).toEqual(0);
+ expect(testContext.store.changing).toBe(true);
+ });
+
+ it('set assigned data', () => {
+ const users = {
+ assignees: UsersMockHelper.createNumberRandomUsers(3),
+ };
+
+ testContext.store.setAssigneeData(users);
+
+ expect(testContext.store.isFetching.assignees).toBe(false);
+ expect(testContext.store.assignees.length).toEqual(3);
+ });
+
+ it('sets fetching state', () => {
+ expect(testContext.store.isFetching.assignees).toEqual(true);
+
+ testContext.store.setFetchingState('assignees', false);
+
+ expect(testContext.store.isFetching.assignees).toEqual(false);
+ });
+
+ it('sets loading state', () => {
+ testContext.store.setLoadingState('assignees', true);
+
+ expect(testContext.store.isLoading.assignees).toEqual(true);
+ });
+
+ it('set time tracking data', () => {
+ testContext.store.setTimeTrackingData(Mock.time);
+
+ expect(testContext.store.timeEstimate).toEqual(Mock.time.time_estimate);
+ expect(testContext.store.totalTimeSpent).toEqual(Mock.time.total_time_spent);
+ expect(testContext.store.humanTimeEstimate).toEqual(Mock.time.human_time_estimate);
+ expect(testContext.store.humanTotalTimeSpent).toEqual(Mock.time.human_total_time_spent);
+ });
+
+ it('set autocomplete projects', () => {
+ const projects = [{ id: 0 }];
+ testContext.store.setAutocompleteProjects(projects);
+
+ expect(testContext.store.autocompleteProjects).toEqual(projects);
+ });
+
+ it('sets subscribed state', () => {
+ expect(testContext.store.subscribed).toEqual(null);
+
+ testContext.store.setSubscribedState(true);
+
+ expect(testContext.store.subscribed).toEqual(true);
+ });
+
+ it('set move to project ID', () => {
+ const projectId = 7;
+ testContext.store.setMoveToProjectId(projectId);
+
+ expect(testContext.store.moveToProjectId).toEqual(projectId);
+ });
+});
diff --git a/spec/frontend/terms/components/app_spec.js b/spec/frontend/terms/components/app_spec.js
index f1dbc004da8..ce1c126f868 100644
--- a/spec/frontend/terms/components/app_spec.js
+++ b/spec/frontend/terms/components/app_spec.js
@@ -1,4 +1,3 @@
-import $ from 'jquery';
import { merge } from 'lodash';
import { GlIntersectionObserver } from '@gitlab/ui';
import { nextTick } from 'vue';
@@ -7,13 +6,14 @@ import { mountExtended } from 'helpers/vue_test_utils_helper';
import { FLASH_TYPES, FLASH_CLOSED_EVENT } from '~/flash';
import { isLoggedIn } from '~/lib/utils/common_utils';
import TermsApp from '~/terms/components/app.vue';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
jest.mock('~/lib/utils/common_utils');
+jest.mock('~/behaviors/markdown/render_gfm');
describe('TermsApp', () => {
let wrapper;
- let renderGFMSpy;
const defaultProvide = {
terms: 'foo bar',
@@ -35,7 +35,6 @@ describe('TermsApp', () => {
};
beforeEach(() => {
- renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
isLoggedIn.mockReturnValue(true);
});
@@ -65,7 +64,7 @@ describe('TermsApp', () => {
createComponent();
expect(wrapper.findByText(defaultProvide.terms).exists()).toBe(true);
- expect(renderGFMSpy).toHaveBeenCalled();
+ expect(renderGFM).toHaveBeenCalled();
});
describe('accept button', () => {
diff --git a/spec/frontend/terraform/components/init_command_modal_spec.js b/spec/frontend/terraform/components/init_command_modal_spec.js
index dbdff899bac..911bb8878da 100644
--- a/spec/frontend/terraform/components/init_command_modal_spec.js
+++ b/spec/frontend/terraform/components/init_command_modal_spec.js
@@ -7,12 +7,13 @@ const accessTokensPath = '/path/to/access-tokens-page';
const terraformApiUrl = 'https://gitlab.com/api/v4/projects/1';
const username = 'username';
const modalId = 'fake-modal-id';
-const stateName = 'production';
+const stateName = 'aws/eu-central-1';
+const stateNameEncoded = encodeURIComponent(stateName);
const modalInfoCopyStr = `export GITLAB_ACCESS_TOKEN=<YOUR-ACCESS-TOKEN>
terraform init \\
- -backend-config="address=${terraformApiUrl}/${stateName}" \\
- -backend-config="lock_address=${terraformApiUrl}/${stateName}/lock" \\
- -backend-config="unlock_address=${terraformApiUrl}/${stateName}/lock" \\
+ -backend-config="address=${terraformApiUrl}/${stateNameEncoded}" \\
+ -backend-config="lock_address=${terraformApiUrl}/${stateNameEncoded}/lock" \\
+ -backend-config="unlock_address=${terraformApiUrl}/${stateNameEncoded}/lock" \\
-backend-config="username=${username}" \\
-backend-config="password=$GITLAB_ACCESS_TOKEN" \\
-backend-config="lock_method=POST" \\
@@ -61,9 +62,15 @@ describe('InitCommandModal', () => {
expect(findLink().attributes('href')).toBe(accessTokensPath);
});
- it('renders the init command with the username and state name prepopulated', () => {
- expect(findInitCommand().text()).toContain(username);
- expect(findInitCommand().text()).toContain(stateName);
+ describe('init command', () => {
+ it('includes correct address', () => {
+ expect(findInitCommand().text()).toContain(
+ `-backend-config="address=${terraformApiUrl}/${stateNameEncoded}"`,
+ );
+ });
+ it('includes correct username', () => {
+ expect(findInitCommand().text()).toContain(`-backend-config="username=${username}"`);
+ });
});
it('renders the copyToClipboard button', () => {
diff --git a/spec/frontend/token_access/mock_data.js b/spec/frontend/token_access/mock_data.js
index 2eed1e30d0d..0c8ba266201 100644
--- a/spec/frontend/token_access/mock_data.js
+++ b/spec/frontend/token_access/mock_data.js
@@ -68,6 +68,19 @@ export const removeProjectSuccess = {
},
};
+export const updateScopeSuccess = {
+ data: {
+ ciCdSettingsUpdate: {
+ ciCdSettings: {
+ jobTokenScopeEnabled: false,
+ __typename: 'ProjectCiCdSetting',
+ },
+ errors: [],
+ __typename: 'CiCdSettingsUpdatePayload',
+ },
+ },
+};
+
export const mockProjects = [
{
id: '1',
diff --git a/spec/frontend/token_access/token_access_spec.js b/spec/frontend/token_access/token_access_spec.js
index ea1d9db515a..6fe94e28548 100644
--- a/spec/frontend/token_access/token_access_spec.js
+++ b/spec/frontend/token_access/token_access_spec.js
@@ -8,6 +8,7 @@ import { createAlert } from '~/flash';
import TokenAccess from '~/token_access/components/token_access.vue';
import addProjectCIJobTokenScopeMutation from '~/token_access/graphql/mutations/add_project_ci_job_token_scope.mutation.graphql';
import removeProjectCIJobTokenScopeMutation from '~/token_access/graphql/mutations/remove_project_ci_job_token_scope.mutation.graphql';
+import updateCIJobTokenScopeMutation from '~/token_access/graphql/mutations/update_ci_job_token_scope.mutation.graphql';
import getCIJobTokenScopeQuery from '~/token_access/graphql/queries/get_ci_job_token_scope.query.graphql';
import getProjectsWithCIJobTokenScopeQuery from '~/token_access/graphql/queries/get_projects_with_ci_job_token_scope.query.graphql';
import {
@@ -16,6 +17,7 @@ import {
projectsWithScope,
addProjectSuccess,
removeProjectSuccess,
+ updateScopeSuccess,
} from './mock_data';
const projectPath = 'root/my-repo';
@@ -31,11 +33,11 @@ describe('TokenAccess component', () => {
const enabledJobTokenScopeHandler = jest.fn().mockResolvedValue(enabledJobTokenScope);
const disabledJobTokenScopeHandler = jest.fn().mockResolvedValue(disabledJobTokenScope);
- const getProjectsWithScope = jest.fn().mockResolvedValue(projectsWithScope);
+ const getProjectsWithScopeHandler = jest.fn().mockResolvedValue(projectsWithScope);
const addProjectSuccessHandler = jest.fn().mockResolvedValue(addProjectSuccess);
- const addProjectFailureHandler = jest.fn().mockRejectedValue(error);
const removeProjectSuccessHandler = jest.fn().mockResolvedValue(removeProjectSuccess);
- const removeProjectFailureHandler = jest.fn().mockRejectedValue(error);
+ const updateScopeSuccessHandler = jest.fn().mockResolvedValue(updateScopeSuccess);
+ const failureHandler = jest.fn().mockRejectedValue(error);
const findToggle = () => wrapper.findComponent(GlToggle);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
@@ -69,7 +71,7 @@ describe('TokenAccess component', () => {
it('shows loading state while waiting on query to resolve', async () => {
createComponent([
[getCIJobTokenScopeQuery, enabledJobTokenScopeHandler],
- [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScope],
+ [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScopeHandler],
]);
expect(findLoadingIcon().exists()).toBe(true);
@@ -80,11 +82,53 @@ describe('TokenAccess component', () => {
});
});
+ describe('fetching projects and scope', () => {
+ it('fetches projects and scope correctly', () => {
+ const expectedVariables = {
+ fullPath: 'root/my-repo',
+ };
+
+ createComponent([
+ [getCIJobTokenScopeQuery, enabledJobTokenScopeHandler],
+ [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScopeHandler],
+ ]);
+
+ expect(enabledJobTokenScopeHandler).toHaveBeenCalledWith(expectedVariables);
+ expect(getProjectsWithScopeHandler).toHaveBeenCalledWith(expectedVariables);
+ });
+
+ it('handles fetch projects error correctly', async () => {
+ createComponent([
+ [getCIJobTokenScopeQuery, enabledJobTokenScopeHandler],
+ [getProjectsWithCIJobTokenScopeQuery, failureHandler],
+ ]);
+
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalledWith({
+ message: 'There was a problem fetching the projects',
+ });
+ });
+
+ it('handles fetch scope error correctly', async () => {
+ createComponent([
+ [getCIJobTokenScopeQuery, failureHandler],
+ [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScopeHandler],
+ ]);
+
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalledWith({
+ message: 'There was a problem fetching the job token scope value',
+ });
+ });
+ });
+
describe('toggle', () => {
it('the toggle is on and the alert is hidden', async () => {
createComponent([
[getCIJobTokenScopeQuery, enabledJobTokenScopeHandler],
- [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScope],
+ [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScopeHandler],
]);
await waitForPromises();
@@ -96,7 +140,7 @@ describe('TokenAccess component', () => {
it('the toggle is off and the alert is visible', async () => {
createComponent([
[getCIJobTokenScopeQuery, disabledJobTokenScopeHandler],
- [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScope],
+ [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScopeHandler],
]);
await waitForPromises();
@@ -104,6 +148,47 @@ describe('TokenAccess component', () => {
expect(findToggle().props('value')).toBe(false);
expect(findTokenDisabledAlert().exists()).toBe(true);
});
+
+ describe('update ci job token scope', () => {
+ it('calls updateCIJobTokenScopeMutation mutation', async () => {
+ createComponent(
+ [
+ [getCIJobTokenScopeQuery, enabledJobTokenScopeHandler],
+ [updateCIJobTokenScopeMutation, updateScopeSuccessHandler],
+ ],
+ mountExtended,
+ );
+
+ await waitForPromises();
+
+ findToggle().vm.$emit('change', false);
+
+ expect(updateScopeSuccessHandler).toHaveBeenCalledWith({
+ input: {
+ fullPath: 'root/my-repo',
+ jobTokenScopeEnabled: false,
+ },
+ });
+ });
+
+ it('handles update scope error correctly', async () => {
+ createComponent(
+ [
+ [getCIJobTokenScopeQuery, disabledJobTokenScopeHandler],
+ [updateCIJobTokenScopeMutation, failureHandler],
+ ],
+ mountExtended,
+ );
+
+ await waitForPromises();
+
+ findToggle().vm.$emit('change', true);
+
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalledWith({ message });
+ });
+ });
});
describe('add project', () => {
@@ -111,7 +196,7 @@ describe('TokenAccess component', () => {
createComponent(
[
[getCIJobTokenScopeQuery, enabledJobTokenScopeHandler],
- [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScope],
+ [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScopeHandler],
[addProjectCIJobTokenScopeMutation, addProjectSuccessHandler],
],
mountExtended,
@@ -133,8 +218,8 @@ describe('TokenAccess component', () => {
createComponent(
[
[getCIJobTokenScopeQuery, enabledJobTokenScopeHandler],
- [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScope],
- [addProjectCIJobTokenScopeMutation, addProjectFailureHandler],
+ [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScopeHandler],
+ [addProjectCIJobTokenScopeMutation, failureHandler],
],
mountExtended,
);
@@ -154,7 +239,7 @@ describe('TokenAccess component', () => {
createComponent(
[
[getCIJobTokenScopeQuery, enabledJobTokenScopeHandler],
- [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScope],
+ [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScopeHandler],
[removeProjectCIJobTokenScopeMutation, removeProjectSuccessHandler],
],
mountExtended,
@@ -176,8 +261,8 @@ describe('TokenAccess component', () => {
createComponent(
[
[getCIJobTokenScopeQuery, enabledJobTokenScopeHandler],
- [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScope],
- [removeProjectCIJobTokenScopeMutation, removeProjectFailureHandler],
+ [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScopeHandler],
+ [removeProjectCIJobTokenScopeMutation, failureHandler],
],
mountExtended,
);
diff --git a/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap b/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap
index bd40a968392..4077564486c 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap
+++ b/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap
@@ -2,7 +2,7 @@
exports[`MRWidgetAutoMergeEnabled template should have correct elements 1`] = `
<div
- class="mr-widget-body media mr-widget-body-line-height-1 gl-line-height-normal"
+ class="mr-widget-body media gl-display-flex gl-align-items-center"
>
<div
class="gl-w-6 gl-h-6 gl-display-flex gl-align-self-start gl-mr-3"
@@ -61,7 +61,7 @@ exports[`MRWidgetAutoMergeEnabled template should have correct elements 1`] = `
class="gl-display-flex gl-align-items-flex-start"
>
<div
- class="dropdown b-dropdown gl-new-dropdown gl-display-block gl-md-display-none! btn-group"
+ class="dropdown b-dropdown gl-dropdown gl-display-block gl-md-display-none! btn-group"
lazy=""
no-caret=""
title="Options"
@@ -87,7 +87,7 @@ exports[`MRWidgetAutoMergeEnabled template should have correct elements 1`] = `
</svg>
<span
- class="gl-new-dropdown-button-text gl-sr-only"
+ class="gl-dropdown-button-text gl-sr-only"
>
</span>
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_closed_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_closed_spec.js
index 06ee017dee7..270a37f87e7 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_closed_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_closed_spec.js
@@ -1,9 +1,28 @@
-import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import { shallowMount, mount } from '@vue/test-utils';
+import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+
+import api from '~/api';
+
+import showGlobalToast from '~/vue_shared/plugins/global_toast';
+
import closedComponent from '~/vue_merge_request_widget/components/states/mr_widget_closed.vue';
import MrWidgetAuthorTime from '~/vue_merge_request_widget/components/mr_widget_author_time.vue';
import StateContainer from '~/vue_merge_request_widget/components/state_container.vue';
+import Actions from '~/vue_merge_request_widget/components/action_buttons.vue';
+
+import { MR_WIDGET_CLOSED_REOPEN_FAILURE } from '~/vue_merge_request_widget/i18n';
+
+jest.mock('~/api', () => ({
+ updateMergeRequest: jest.fn(),
+}));
+jest.mock('~/vue_shared/plugins/global_toast');
+
+useMockLocationHelper();
const MOCK_DATA = {
+ iid: 1,
metrics: {
mergedBy: {},
closedBy: {
@@ -19,22 +38,39 @@ const MOCK_DATA = {
},
targetBranchPath: '/twitter/flight/commits/so_long_jquery',
targetBranch: 'so_long_jquery',
+ targetProjectId: 'twitter/flight',
};
+function createComponent({ shallow = true, props = {} } = {}) {
+ const mounter = shallow ? shallowMount : mount;
+
+ return mounter(closedComponent, {
+ propsData: {
+ mr: MOCK_DATA,
+ ...props,
+ },
+ });
+}
+
+function findActions(wrapper) {
+ return wrapper.findComponent(StateContainer).findComponent(Actions);
+}
+
+function findReopenActionButton(wrapper) {
+ return findActions(wrapper).find('button[data-testid="extension-actions-reopen-button"]');
+}
+
describe('MRWidgetClosed', () => {
let wrapper;
beforeEach(() => {
- wrapper = shallowMount(closedComponent, {
- propsData: {
- mr: MOCK_DATA,
- },
- });
+ wrapper = createComponent();
});
afterEach(() => {
- wrapper.destroy();
- wrapper = null;
+ if (wrapper) {
+ wrapper.destroy();
+ }
});
it('renders closed icon', () => {
@@ -51,4 +87,93 @@ describe('MRWidgetClosed', () => {
dateReadable: MOCK_DATA.metrics.readableClosedAt,
});
});
+
+ describe('actions', () => {
+ describe('reopen', () => {
+ beforeEach(() => {
+ window.gon = { current_user_id: 1 };
+ api.updateMergeRequest.mockResolvedValue(true);
+ wrapper = createComponent({ shallow: false });
+ });
+
+ it('shows the "reopen" button', () => {
+ expect(wrapper.findComponent(StateContainer).props().actions.length).toBe(1);
+ expect(findReopenActionButton(wrapper).text()).toBe('Reopen');
+ });
+
+ it('does not show widget actions when the user is not logged in', () => {
+ window.gon = {};
+
+ wrapper = createComponent();
+
+ expect(findActions(wrapper).exists()).toBe(false);
+ });
+
+ it('makes the reopen request with the correct MR information', async () => {
+ const reopenButton = findReopenActionButton(wrapper);
+
+ reopenButton.trigger('click');
+ await nextTick();
+
+ expect(api.updateMergeRequest).toHaveBeenCalledWith(
+ MOCK_DATA.targetProjectId,
+ MOCK_DATA.iid,
+ { state_event: 'reopen' },
+ );
+ });
+
+ it('shows "Reopening..." while the reopen network request is pending', async () => {
+ const reopenButton = findReopenActionButton(wrapper);
+
+ api.updateMergeRequest.mockReturnValue(new Promise(() => {}));
+
+ reopenButton.trigger('click');
+ await nextTick();
+
+ expect(reopenButton.text()).toBe('Reopening...');
+ });
+
+ it('shows "Refreshing..." when the reopen has succeeded', async () => {
+ const reopenButton = findReopenActionButton(wrapper);
+
+ reopenButton.trigger('click');
+ await waitForPromises();
+
+ expect(reopenButton.text()).toBe('Refreshing...');
+ });
+
+ it('reloads the page when a reopen has succeeded', async () => {
+ const reopenButton = findReopenActionButton(wrapper);
+
+ reopenButton.trigger('click');
+ await waitForPromises();
+
+ expect(window.location.reload).toHaveBeenCalledTimes(1);
+ });
+
+ it('shows "Reopen" when a reopen request has failed', async () => {
+ const reopenButton = findReopenActionButton(wrapper);
+
+ api.updateMergeRequest.mockRejectedValue(false);
+
+ reopenButton.trigger('click');
+ await waitForPromises();
+
+ expect(window.location.reload).not.toHaveBeenCalled();
+ expect(reopenButton.text()).toBe('Reopen');
+ });
+
+ it('requests a toast popup when a reopen request has failed', async () => {
+ const reopenButton = findReopenActionButton(wrapper);
+
+ api.updateMergeRequest.mockRejectedValue(false);
+
+ reopenButton.trigger('click');
+ await waitForPromises();
+
+ expect(showGlobalToast).toHaveBeenCalledTimes(1);
+ expect(showGlobalToast).toHaveBeenCalledWith(MR_WIDGET_CLOSED_REOPEN_FAILURE);
+ });
+ });
+ });
});
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js
index 407bd60b2b7..d34fc0c1e61 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js
@@ -812,14 +812,30 @@ describe('ReadyToMerge', () => {
);
});
- it('shows the diverged commits text when the source branch is behind the target', () => {
- createComponent({
- mr: { divergedCommitsCount: 9001, userPermissions: { canMerge: false }, canMerge: false },
+ describe('shows the diverged commits text when the source branch is behind the target', () => {
+ it('when the MR can be merged', () => {
+ createComponent({
+ mr: { divergedCommitsCount: 9001 },
+ });
+
+ expect(wrapper.text()).toEqual(
+ expect.stringContaining('The source branch is 9001 commits behind the target branch'),
+ );
});
- expect(wrapper.text()).toEqual(
- expect.stringContaining('The source branch is 9001 commits behind the target branch'),
- );
+ it('when the MR cannot be merged', () => {
+ createComponent({
+ mr: {
+ divergedCommitsCount: 9001,
+ userPermissions: { canMerge: false },
+ canMerge: false,
+ },
+ });
+
+ expect(wrapper.text()).toEqual(
+ expect.stringContaining('The source branch is 9001 commits behind the target branch'),
+ );
+ });
});
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/widget/action_buttons_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/action_buttons_spec.js
new file mode 100644
index 00000000000..366ea113162
--- /dev/null
+++ b/spec/frontend/vue_merge_request_widget/components/widget/action_buttons_spec.js
@@ -0,0 +1,47 @@
+import { GlButton, GlDropdownItem } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Actions from '~/vue_merge_request_widget/components/widget/action_buttons.vue';
+
+let wrapper;
+
+function factory(propsData = {}) {
+ wrapper = shallowMount(Actions, {
+ propsData: { ...propsData, widget: 'test' },
+ });
+}
+
+describe('~/vue_merge_request_widget/components/widget/action_buttons.vue', () => {
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('tertiaryButtons', () => {
+ it('renders buttons', () => {
+ factory({
+ tertiaryButtons: [{ text: 'hello world', href: 'https://gitlab.com', target: '_blank' }],
+ });
+
+ expect(wrapper.findAllComponents(GlButton)).toHaveLength(1);
+ });
+
+ it('calls action click handler', async () => {
+ const onClick = jest.fn();
+
+ factory({
+ tertiaryButtons: [{ text: 'hello world', onClick }],
+ });
+
+ await wrapper.findComponent(GlButton).vm.$emit('click');
+
+ expect(onClick).toHaveBeenCalled();
+ });
+
+ it('renders tertiary actions in dropdown', () => {
+ factory({
+ tertiaryButtons: [{ text: 'hello world', href: 'https://gitlab.com', target: '_blank' }],
+ });
+
+ expect(wrapper.findAllComponents(GlDropdownItem)).toHaveLength(1);
+ });
+ });
+});
diff --git a/spec/frontend/vue_merge_request_widget/components/widget/widget_content_row_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/widget_content_row_spec.js
index e4bee6b8652..791fe541eb6 100644
--- a/spec/frontend/vue_merge_request_widget/components/widget/widget_content_row_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/widget/widget_content_row_spec.js
@@ -1,7 +1,7 @@
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import WidgetContentRow from '~/vue_merge_request_widget/components/widget/widget_content_row.vue';
import StatusIcon from '~/vue_merge_request_widget/components/widget/status_icon.vue';
-import ActionButtons from '~/vue_merge_request_widget/components/action_buttons.vue';
+import ActionButtons from '~/vue_merge_request_widget/components/widget/action_buttons.vue';
import HelpPopover from '~/vue_shared/components/help_popover.vue';
describe('~/vue_merge_request_widget/components/widget/widget_content_row.vue', () => {
@@ -76,7 +76,10 @@ describe('~/vue_merge_request_widget/components/widget/widget_content_row.vue',
},
});
- expect(findHelpPopover().props('options')).toEqual({ title: 'Help popover title' });
+ const popover = findHelpPopover();
+
+ expect(popover.props('options')).toEqual({ title: 'Help popover title' });
+ expect(popover.props('icon')).toBe('information-o');
expect(wrapper.findByText('Help popover content').exists()).toBe(true);
expect(wrapper.findByText('Learn more').attributes('href')).toBe('/path/to/docs');
expect(wrapper.findByText('Learn more').attributes('target')).toBe('_blank');
diff --git a/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js
index 9635e050e4d..4c93c88de16 100644
--- a/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js
@@ -4,7 +4,7 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import HelpPopover from '~/vue_shared/components/help_popover.vue';
import waitForPromises from 'helpers/wait_for_promises';
import StatusIcon from '~/vue_merge_request_widget/components/extensions/status_icon.vue';
-import ActionButtons from '~/vue_merge_request_widget/components/action_buttons.vue';
+import ActionButtons from '~/vue_merge_request_widget/components/widget/action_buttons.vue';
import Widget from '~/vue_merge_request_widget/components/widget/widget.vue';
import WidgetContentRow from '~/vue_merge_request_widget/components/widget/widget_content_row.vue';
@@ -24,6 +24,7 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => {
const findActionButtons = () => wrapper.findComponent(ActionButtons);
const findToggleButton = () => wrapper.findByTestId('toggle-button');
const findHelpPopover = () => wrapper.findComponent(HelpPopover);
+ const findDynamicScroller = () => wrapper.findByTestId('dynamic-content-scroller');
const createComponent = ({ propsData, slots } = {}) => {
wrapper = shallowMountExtended(Widget, {
@@ -212,7 +213,10 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => {
},
});
- expect(findHelpPopover().props('options')).toEqual({ title: 'My help popover title' });
+ const popover = findHelpPopover();
+
+ expect(popover.props('options')).toEqual({ title: 'My help popover title' });
+ expect(popover.props('icon')).toBe('information-o');
expect(wrapper.findByText('Help popover content').exists()).toBe(true);
expect(wrapper.findByText('Learn more').attributes('href')).toBe('/path/to/docs');
expect(wrapper.findByText('Learn more').attributes('target')).toBe('_blank');
@@ -370,7 +374,7 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => {
href: '#',
target: '_blank',
id: 'full-report-button',
- text: 'Full Report',
+ text: 'Full report',
},
],
},
@@ -388,7 +392,7 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => {
it('when full report is clicked it should call the respective telemetry event', async () => {
expect(wrapper.vm.telemetryHub.fullReportClicked).not.toHaveBeenCalled();
- wrapper.findByText('Full Report').vm.$emit('click');
+ wrapper.findByText('Full report').vm.$emit('click');
await nextTick();
expect(wrapper.vm.telemetryHub.fullReportClicked).toHaveBeenCalledTimes(1);
});
@@ -408,4 +412,30 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => {
expect(wrapper.vm.telemetryHub).toBe(null);
});
});
+
+ describe('dynamic content', () => {
+ const content = [
+ {
+ id: 'row-id',
+ header: ['This is a header', 'This is a subheader'],
+ text: 'Main text for the row',
+ subtext: 'Optional: Smaller sub-text to be displayed below the main text',
+ },
+ ];
+
+ beforeEach(() => {
+ createComponent({
+ propsData: {
+ isCollapsible: true,
+ content,
+ },
+ });
+ });
+
+ it('uses a dynamic scroller to show the items', async () => {
+ findToggleButton().vm.$emit('click');
+ await waitForPromises();
+ expect(findDynamicScroller().props('items')).toEqual(content);
+ });
+ });
});
diff --git a/spec/frontend/vue_merge_request_widget/extensions/test_report/index_spec.js b/spec/frontend/vue_merge_request_widget/extensions/test_report/index_spec.js
index 05df66165dd..baef247b649 100644
--- a/spec/frontend/vue_merge_request_widget/extensions/test_report/index_spec.js
+++ b/spec/frontend/vue_merge_request_widget/extensions/test_report/index_spec.js
@@ -8,17 +8,17 @@ import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
import extensionsContainer from '~/vue_merge_request_widget/components/extensions/container';
import { registerExtension } from '~/vue_merge_request_widget/components/extensions';
-import httpStatusCodes from '~/lib/utils/http_status';
+import httpStatusCodes, { HTTP_STATUS_NO_CONTENT } from '~/lib/utils/http_status';
import TestCaseDetails from '~/pipelines/components/test_reports/test_case_details.vue';
-import { failedReport } from 'jest/reports/mock_data/mock_data';
-import mixedResultsTestReports from 'jest/reports/mock_data/new_and_fixed_failures_report.json';
-import newErrorsTestReports from 'jest/reports/mock_data/new_errors_report.json';
-import newFailedTestReports from 'jest/reports/mock_data/new_failures_report.json';
-import newFailedTestWithNullFilesReport from 'jest/reports/mock_data/new_failures_with_null_files_report.json';
-import successTestReports from 'jest/reports/mock_data/no_failures_report.json';
-import resolvedFailures from 'jest/reports/mock_data/resolved_failures.json';
-import recentFailures from 'jest/reports/mock_data/recent_failures_report.json';
+import { failedReport } from 'jest/ci/reports/mock_data/mock_data';
+import mixedResultsTestReports from 'jest/ci/reports/mock_data/new_and_fixed_failures_report.json';
+import newErrorsTestReports from 'jest/ci/reports/mock_data/new_errors_report.json';
+import newFailedTestReports from 'jest/ci/reports/mock_data/new_failures_report.json';
+import newFailedTestWithNullFilesReport from 'jest/ci/reports/mock_data/new_failures_with_null_files_report.json';
+import successTestReports from 'jest/ci/reports/mock_data/no_failures_report.json';
+import resolvedFailures from 'jest/ci/reports/mock_data/resolved_failures.json';
+import recentFailures from 'jest/ci/reports/mock_data/recent_failures_report.json';
const reportWithParsingErrors = failedReport;
reportWithParsingErrors.suites[0].suite_errors = {
@@ -82,7 +82,7 @@ describe('Test report extension', () => {
});
it('with a 204 response, continues to display loading state', async () => {
- mockApi(httpStatusCodes.NO_CONTENT, '');
+ mockApi(HTTP_STATUS_NO_CONTENT, '');
createComponent();
await waitForPromises();
diff --git a/spec/frontend/vue_merge_request_widget/extentions/code_quality/index_spec.js b/spec/frontend/vue_merge_request_widget/extentions/code_quality/index_spec.js
index 9a72e4a086b..f0ebbb1a82e 100644
--- a/spec/frontend/vue_merge_request_widget/extentions/code_quality/index_spec.js
+++ b/spec/frontend/vue_merge_request_widget/extentions/code_quality/index_spec.js
@@ -1,4 +1,5 @@
import MockAdapter from 'axios-mock-adapter';
+import { GlBadge } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { trimText } from 'helpers/text_helper';
import waitForPromises from 'helpers/wait_for_promises';
@@ -6,10 +7,10 @@ import axios from '~/lib/utils/axios_utils';
import extensionsContainer from '~/vue_merge_request_widget/components/extensions/container';
import { registerExtension } from '~/vue_merge_request_widget/components/extensions';
import codeQualityExtension from '~/vue_merge_request_widget/extensions/code_quality';
-import httpStatusCodes from '~/lib/utils/http_status';
+import httpStatusCodes, { HTTP_STATUS_NO_CONTENT } from '~/lib/utils/http_status';
+import { i18n } from '~/vue_merge_request_widget/extensions/code_quality/constants';
import {
codeQualityResponseNewErrors,
- codeQualityResponseResolvedErrors,
codeQualityResponseResolvedAndNewErrors,
codeQualityResponseNoErrors,
} from './mock_data';
@@ -58,46 +59,55 @@ describe('Code Quality extension', () => {
createComponent();
- expect(wrapper.text()).toBe('Code Quality test metrics results are being parsed');
+ expect(wrapper.text()).toBe(i18n.loading);
});
- it('displays failed loading text', async () => {
- mockApi(httpStatusCodes.INTERNAL_SERVER_ERROR);
-
+ it('with a 204 response, continues to display loading state', async () => {
+ mockApi(HTTP_STATUS_NO_CONTENT, '');
createComponent();
await waitForPromises();
- expect(wrapper.text()).toBe('Code Quality failed loading results');
+
+ expect(wrapper.text()).toBe(i18n.loading);
});
- it('displays quality degradation', async () => {
- mockApi(httpStatusCodes.OK, codeQualityResponseNewErrors);
+ it('displays failed loading text', async () => {
+ mockApi(httpStatusCodes.INTERNAL_SERVER_ERROR);
createComponent();
await waitForPromises();
-
- expect(wrapper.text()).toBe('Code Quality degraded on 2 points.');
+ expect(wrapper.text()).toBe(i18n.error);
});
- it('displays quality improvement', async () => {
- mockApi(httpStatusCodes.OK, codeQualityResponseResolvedErrors);
+ it('displays correct single Report', async () => {
+ mockApi(httpStatusCodes.OK, codeQualityResponseNewErrors);
createComponent();
await waitForPromises();
- expect(wrapper.text()).toBe('Code Quality improved on 2 points.');
+ expect(wrapper.text()).toBe(
+ i18n.degradedCopy(i18n.singularReport(codeQualityResponseNewErrors.new_errors)),
+ );
});
it('displays quality improvement and degradation', async () => {
mockApi(httpStatusCodes.OK, codeQualityResponseResolvedAndNewErrors);
createComponent();
-
await waitForPromises();
- expect(wrapper.text()).toBe('Code Quality improved on 1 point and degraded on 1 point.');
+ // replacing strong tags because they will not be found in the rendered text
+ expect(wrapper.text()).toBe(
+ i18n
+ .improvementAndDegradationCopy(
+ i18n.pluralReport(codeQualityResponseResolvedAndNewErrors.resolved_errors),
+ i18n.pluralReport(codeQualityResponseResolvedAndNewErrors.new_errors),
+ )
+ .replace(/%{strong_start}/g, '')
+ .replace(/%{strong_end}/g, ''),
+ );
});
it('displays no detected errors', async () => {
@@ -107,7 +117,7 @@ describe('Code Quality extension', () => {
await waitForPromises();
- expect(wrapper.text()).toBe('No changes to Code Quality.');
+ expect(wrapper.text()).toBe(i18n.noChanges);
});
});
@@ -138,8 +148,17 @@ describe('Code Quality extension', () => {
"Minor - Parsing error: 'return' outside of function in index.js:12",
);
expect(text.resolvedError).toContain(
- "Minor - Parsing error: 'return' outside of function in index.js:12",
+ "Minor - Parsing error: 'return' outside of function Fixed in index.js:12",
);
});
+
+ it('adds fixed indicator (badge) when error is resolved', () => {
+ expect(findAllExtensionListItems().at(1).findComponent(GlBadge).exists()).toBe(true);
+ expect(findAllExtensionListItems().at(1).findComponent(GlBadge).text()).toEqual(i18n.fixed);
+ });
+
+ it('should not add fixed indicator (badge) when error is new', () => {
+ expect(findAllExtensionListItems().at(0).findComponent(GlBadge).exists()).toBe(false);
+ });
});
});
diff --git a/spec/frontend/vue_merge_request_widget/extentions/code_quality/mock_data.js b/spec/frontend/vue_merge_request_widget/extentions/code_quality/mock_data.js
index f5ad0ce7377..2e8e70f25db 100644
--- a/spec/frontend/vue_merge_request_widget/extentions/code_quality/mock_data.js
+++ b/spec/frontend/vue_merge_request_widget/extentions/code_quality/mock_data.js
@@ -23,31 +23,6 @@ export const codeQualityResponseNewErrors = {
},
};
-export const codeQualityResponseResolvedErrors = {
- status: 'failed',
- new_errors: [],
- resolved_errors: [
- {
- description: "Parsing error: 'return' outside of function",
- severity: 'minor',
- file_path: 'index.js',
- line: 12,
- },
- {
- description: 'TODO found',
- severity: 'minor',
- file_path: '.gitlab-ci.yml',
- line: 73,
- },
- ],
- existing_errors: [],
- summary: {
- total: 2,
- resolved: 2,
- errored: 0,
- },
-};
-
export const codeQualityResponseResolvedAndNewErrors = {
status: 'failed',
new_errors: [
diff --git a/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js b/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js
index 0f4637d18d9..683858b331d 100644
--- a/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js
+++ b/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js
@@ -22,6 +22,7 @@ import {
import { SUCCESS } from '~/vue_merge_request_widget/components/deployment/constants';
import eventHub from '~/vue_merge_request_widget/event_hub';
import MrWidgetOptions from '~/vue_merge_request_widget/mr_widget_options.vue';
+import WidgetContainer from '~/vue_merge_request_widget/components/widget/app.vue';
import StatusIcon from '~/vue_merge_request_widget/components/extensions/status_icon.vue';
import securityReportMergeRequestDownloadPathsQuery from '~/vue_shared/security_reports/graphql/queries/security_report_merge_request_download_paths.query.graphql';
import getStateQuery from '~/vue_merge_request_widget/queries/get_state.query.graphql';
@@ -62,6 +63,7 @@ describe('MrWidgetOptions', () => {
let mock;
const COLLABORATION_MESSAGE = 'Members who can merge are allowed to add commits';
+ const findWidgetContainer = () => wrapper.findComponent(WidgetContainer);
const findExtensionToggleButton = () =>
wrapper.find('[data-testid="widget-extension"] [data-testid="toggle-button"]');
const findExtensionLink = (linkHref) =>
@@ -1228,5 +1230,22 @@ describe('MrWidgetOptions', () => {
expect(api.trackRedisCounterEvent).not.toHaveBeenCalled();
});
});
+
+ describe('widget container', () => {
+ afterEach(() => {
+ delete window.gon.features.refactorSecurityExtension;
+ });
+
+ it('should not be displayed when the refactor_security_extension feature flag is turned off', () => {
+ createComponent();
+ expect(findWidgetContainer().exists()).toBe(false);
+ });
+
+ it('should be displayed when the refactor_security_extension feature flag is turned on', () => {
+ window.gon.features.refactorSecurityExtension = true;
+ createComponent();
+ expect(findWidgetContainer().exists()).toBe(true);
+ });
+ });
});
});
diff --git a/spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap
deleted file mode 100644
index bdf5ea23812..00000000000
--- a/spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap
+++ /dev/null
@@ -1,305 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`vue_shared/components/awards_list default matches snapshot 1`] = `
-<div
- class="awards js-awards-block"
->
- <button
- class="btn gl-mr-3 gl-my-2 btn-default btn-md gl-button"
- data-testid="award-button"
- title="Ada, Leonardo, and Marie reacted with :thumbsup:"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="award-emoji-block"
- data-testid="award-html"
- >
- <gl-emoji
- data-name="thumbsup"
- />
- </span>
-
- <span
- class="gl-button-text"
- >
-
- <span
- class="js-counter"
- >
- 3
- </span>
- </span>
- </button>
- <button
- class="btn gl-mr-3 gl-my-2 btn-default btn-md gl-button selected"
- data-testid="award-button"
- title="You, Ada, and Marie reacted with :thumbsdown:"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="award-emoji-block"
- data-testid="award-html"
- >
- <gl-emoji
- data-name="thumbsdown"
- />
- </span>
-
- <span
- class="gl-button-text"
- >
-
- <span
- class="js-counter"
- >
- 3
- </span>
- </span>
- </button>
- <button
- class="btn gl-mr-3 gl-my-2 btn-default btn-md gl-button"
- data-testid="award-button"
- title="Ada and Jane reacted with :smile:"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="award-emoji-block"
- data-testid="award-html"
- >
- <gl-emoji
- data-name="smile"
- />
- </span>
-
- <span
- class="gl-button-text"
- >
-
- <span
- class="js-counter"
- >
- 2
- </span>
- </span>
- </button>
- <button
- class="btn gl-mr-3 gl-my-2 btn-default btn-md gl-button selected"
- data-testid="award-button"
- title="You, Ada, Jane, and Leonardo reacted with :ok_hand:"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="award-emoji-block"
- data-testid="award-html"
- >
- <gl-emoji
- data-name="ok_hand"
- />
- </span>
-
- <span
- class="gl-button-text"
- >
-
- <span
- class="js-counter"
- >
- 4
- </span>
- </span>
- </button>
- <button
- class="btn gl-mr-3 gl-my-2 btn-default btn-md gl-button selected"
- data-testid="award-button"
- title="You reacted with :cactus:"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="award-emoji-block"
- data-testid="award-html"
- >
- <gl-emoji
- data-name="cactus"
- />
- </span>
-
- <span
- class="gl-button-text"
- >
-
- <span
- class="js-counter"
- >
- 1
- </span>
- </span>
- </button>
- <button
- class="btn gl-mr-3 gl-my-2 btn-default btn-md gl-button"
- data-testid="award-button"
- title="Marie reacted with :a:"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="award-emoji-block"
- data-testid="award-html"
- >
- <gl-emoji
- data-name="a"
- />
- </span>
-
- <span
- class="gl-button-text"
- >
-
- <span
- class="js-counter"
- >
- 1
- </span>
- </span>
- </button>
- <button
- class="btn gl-mr-3 gl-my-2 btn-default btn-md gl-button selected"
- data-testid="award-button"
- title="You reacted with :b:"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="award-emoji-block"
- data-testid="award-html"
- >
- <gl-emoji
- data-name="b"
- />
- </span>
-
- <span
- class="gl-button-text"
- >
-
- <span
- class="js-counter"
- >
- 1
- </span>
- </span>
- </button>
-
- <div
- class="award-menu-holder gl-my-2"
- >
- <div
- class="emoji-picker"
- data-testid="emoji-picker"
- title="Add reaction"
- >
- <div
- boundary="scrollParent"
- class="dropdown b-dropdown gl-new-dropdown btn-group"
- id="__BVID__13"
- lazy=""
- menu-class="dropdown-extended-height"
- no-flip=""
- >
- <!---->
- <button
- aria-expanded="false"
- aria-haspopup="true"
- class="btn dropdown-toggle btn-default btn-md add-reaction-button btn-icon gl-relative! gl-button gl-dropdown-toggle btn-default-secondary"
- id="__BVID__13__BV_toggle_"
- type="button"
- >
- <span
- class="gl-sr-only"
- >
- Add reaction
- </span>
-
- <span
- class="reaction-control-icon reaction-control-icon-neutral"
- >
- <svg
- aria-hidden="true"
- class="gl-icon s16"
- data-testid="slight-smile-icon"
- role="img"
- >
- <use
- href="#slight-smile"
- />
- </svg>
- </span>
-
- <span
- class="reaction-control-icon reaction-control-icon-positive"
- >
- <svg
- aria-hidden="true"
- class="gl-icon s16"
- data-testid="smiley-icon"
- role="img"
- >
- <use
- href="#smiley"
- />
- </svg>
- </span>
-
- <span
- class="reaction-control-icon reaction-control-icon-super-positive"
- >
- <svg
- aria-hidden="true"
- class="gl-icon s16"
- data-testid="smile-icon"
- role="img"
- >
- <use
- href="#smile"
- />
- </svg>
- </span>
- </button>
- <ul
- aria-labelledby="__BVID__13__BV_toggle_"
- class="dropdown-menu dropdown-extended-height dropdown-menu-right"
- role="menu"
- tabindex="-1"
- >
- <!---->
- </ul>
- </div>
- </div>
- </div>
-</div>
-`;
diff --git a/spec/frontend/vue_shared/components/__snapshots__/memory_graph_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/memory_graph_spec.js.snap
index 87eaabf4e98..b7b43264330 100644
--- a/spec/frontend/vue_shared/components/__snapshots__/memory_graph_spec.js.snap
+++ b/spec/frontend/vue_shared/components/__snapshots__/memory_graph_spec.js.snap
@@ -7,6 +7,7 @@ exports[`MemoryGraph Render chart should draw container with chart 1`] = `
>
<gl-sparkline-chart-stub
data="Nov 12 2019 19:17:33,2.87,Nov 12 2019 19:18:33,2.78,Nov 12 2019 19:19:33,2.78,Nov 12 2019 19:20:33,3.01"
+ gradient=""
height="25"
tooltiplabel="MB"
/>
diff --git a/spec/frontend/vue_shared/components/actions_button_spec.js b/spec/frontend/vue_shared/components/actions_button_spec.js
index 07c53c04723..f3fb840b270 100644
--- a/spec/frontend/vue_shared/components/actions_button_spec.js
+++ b/spec/frontend/vue_shared/components/actions_button_spec.js
@@ -1,6 +1,5 @@
-import { GlDropdown, GlDropdownDivider, GlButton } from '@gitlab/ui';
+import { GlDropdown, GlDropdownDivider, GlButton, GlTooltip } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import ActionsButton from '~/vue_shared/components/actions_button.vue';
const TEST_ACTION = {
@@ -32,7 +31,6 @@ describe('Actions button component', () => {
function createComponent(props) {
wrapper = shallowMount(ActionsButton, {
propsData: { ...props },
- directives: { GlTooltip: createMockDirective() },
});
}
@@ -40,15 +38,9 @@ describe('Actions button component', () => {
wrapper.destroy();
});
- const getTooltip = (child) => {
- const directiveBinding = getBinding(child.element, 'gl-tooltip');
-
- return directiveBinding.value;
- };
const findButton = () => wrapper.findComponent(GlButton);
- const findButtonTooltip = () => getTooltip(findButton());
+ const findTooltip = () => wrapper.findComponent(GlTooltip);
const findDropdown = () => wrapper.findComponent(GlDropdown);
- const findDropdownTooltip = () => getTooltip(findDropdown());
const parseDropdownItems = () =>
findDropdown()
.findAll('gl-dropdown-item-stub,gl-dropdown-divider-stub')
@@ -88,8 +80,8 @@ describe('Actions button component', () => {
expect(findButton().text()).toBe(TEST_ACTION.text);
});
- it('should have tooltip', () => {
- expect(findButtonTooltip()).toBe(TEST_ACTION.tooltip);
+ it('should not have tooltip', () => {
+ expect(findTooltip().exists()).toBe(false);
});
it('should have attrs', () => {
@@ -105,7 +97,18 @@ describe('Actions button component', () => {
it('should have tooltip', () => {
createComponent({ actions: [{ ...TEST_ACTION, tooltip: TEST_TOOLTIP }] });
- expect(findButtonTooltip()).toBe(TEST_TOOLTIP);
+ expect(findTooltip().text()).toBe(TEST_TOOLTIP);
+ });
+ });
+
+ describe('when showActionTooltip is false', () => {
+ it('should not have tooltip', () => {
+ createComponent({
+ actions: [{ ...TEST_ACTION, tooltip: TEST_TOOLTIP }],
+ showActionTooltip: false,
+ });
+
+ expect(findTooltip().exists()).toBe(false);
});
});
@@ -174,8 +177,8 @@ describe('Actions button component', () => {
expect(wrapper.emitted('select')).toEqual([[TEST_ACTION_2.key]]);
});
- it('should have tooltip value', () => {
- expect(findDropdownTooltip()).toBe(TEST_ACTION.tooltip);
+ it('should not have tooltip value', () => {
+ expect(findTooltip().exists()).toBe(false);
});
});
@@ -199,7 +202,7 @@ describe('Actions button component', () => {
});
it('should have tooltip value', () => {
- expect(findDropdownTooltip()).toBe(TEST_ACTION_2.tooltip);
+ expect(findTooltip().text()).toBe(TEST_ACTION_2.tooltip);
});
});
});
diff --git a/spec/frontend/vue_shared/components/awards_list_spec.js b/spec/frontend/vue_shared/components/awards_list_spec.js
index 1c8cf726aca..c7f9d8fd8d5 100644
--- a/spec/frontend/vue_shared/components/awards_list_spec.js
+++ b/spec/frontend/vue_shared/components/awards_list_spec.js
@@ -38,7 +38,18 @@ const TEST_AWARDS = [
createAward(EMOJI_CACTUS, USERS.root),
createAward(EMOJI_A, USERS.marie),
createAward(EMOJI_B, USERS.root),
+ createAward(EMOJI_100, USERS.ada),
];
+const TEST_AWARDS_LENGTH = [
+ EMOJI_SMILE,
+ EMOJI_OK,
+ EMOJI_THUMBSUP,
+ EMOJI_THUMBSDOWN,
+ EMOJI_A,
+ EMOJI_B,
+ EMOJI_CACTUS,
+ EMOJI_100,
+].length;
const TEST_ADD_BUTTON_CLASS = 'js-test-add-button-class';
const REACTION_CONTROL_CLASSES = [
@@ -88,10 +99,6 @@ describe('vue_shared/components/awards_list', () => {
});
});
- it('matches snapshot', () => {
- expect(wrapper.element).toMatchSnapshot();
- });
-
it('shows awards in correct order', () => {
expect(findAwardsData()).toEqual([
{
@@ -108,6 +115,12 @@ describe('vue_shared/components/awards_list', () => {
},
{
classes: REACTION_CONTROL_CLASSES,
+ count: 1,
+ html: matchingEmojiTag(EMOJI_100),
+ title: `Ada reacted with :${EMOJI_100}:`,
+ },
+ {
+ classes: REACTION_CONTROL_CLASSES,
count: 2,
html: matchingEmojiTag(EMOJI_SMILE),
title: `Ada and Jane reacted with :${EMOJI_SMILE}:`,
@@ -142,33 +155,23 @@ describe('vue_shared/components/awards_list', () => {
it('with award clicked, it emits award', () => {
expect(wrapper.emitted().award).toBeUndefined();
- findAwardButtons().at(2).vm.$emit('click');
+ findAwardButtons().at(3).vm.$emit('click');
expect(wrapper.emitted().award).toEqual([[EMOJI_SMILE]]);
});
- it('shows add award button', () => {
- const btn = findAddAwardButton();
+ it('with numeric award clicked, it emits award as is', () => {
+ expect(wrapper.emitted().award).toBeUndefined();
- expect(btn.exists()).toBe(true);
- });
- });
+ findAwardButtons().at(2).vm.$emit('click');
- describe('with numeric award', () => {
- beforeEach(() => {
- createComponent({
- awards: [createAward(EMOJI_100, USERS.ada)],
- canAwardEmoji: true,
- currentUserId: USERS.root.id,
- });
+ expect(wrapper.emitted().award).toEqual([[EMOJI_100]]);
});
- it('when clicked, it emits award as number', () => {
- expect(wrapper.emitted().award).toBeUndefined();
-
- findAwardButtons().at(0).vm.$emit('click');
+ it('shows add award button', () => {
+ const btn = findAddAwardButton();
- expect(wrapper.emitted().award).toEqual([[Number(EMOJI_100)]]);
+ expect(btn.exists()).toBe(true);
});
});
@@ -210,7 +213,7 @@ describe('vue_shared/components/awards_list', () => {
it('disables award buttons', () => {
const buttons = findAwardButtons();
- expect(buttons.length).toBe(7);
+ expect(buttons.length).toBe(TEST_AWARDS_LENGTH);
expect(buttons.wrappers.every((x) => x.classes('disabled'))).toBe(true);
});
});
diff --git a/spec/frontend/vue_shared/components/content_viewer/content_viewer_spec.js b/spec/frontend/vue_shared/components/content_viewer/content_viewer_spec.js
index f28805471f8..a37071aec9b 100644
--- a/spec/frontend/vue_shared/components/content_viewer/content_viewer_spec.js
+++ b/spec/frontend/vue_shared/components/content_viewer/content_viewer_spec.js
@@ -1,7 +1,8 @@
import { mount } from '@vue/test-utils';
import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants';
import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
-import '~/behaviors/markdown/render_gfm';
+
+jest.mock('~/behaviors/markdown/render_gfm');
describe('ContentViewer', () => {
let wrapper;
diff --git a/spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js b/spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js
index 01ef52c6af9..0d329b6a065 100644
--- a/spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js
+++ b/spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js
@@ -1,11 +1,12 @@
import { GlSkeletonLoader } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
-import $ from 'jquery';
import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
import MarkdownViewer from '~/vue_shared/components/content_viewer/viewers/markdown_viewer.vue';
+jest.mock('~/behaviors/markdown/render_gfm');
+
describe('MarkdownViewer', () => {
let wrapper;
let mock;
@@ -26,7 +27,6 @@ describe('MarkdownViewer', () => {
mock = new MockAdapter(axios);
jest.spyOn(axios, 'post');
- jest.spyOn($.fn, 'renderGFM');
});
afterEach(() => {
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js
index 1b9ca8e6092..b0e393bbf5e 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js
@@ -13,7 +13,10 @@ import RecentSearchesService from '~/filtered_search/services/recent_searches_se
import RecentSearchesStore from '~/filtered_search/stores/recent_searches_store';
import {
FILTERED_SEARCH_TERM,
- SortDirection,
+ SORT_DIRECTION,
+ TOKEN_TYPE_AUTHOR,
+ TOKEN_TYPE_LABEL,
+ TOKEN_TYPE_MILESTONE,
} from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchBarRoot from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import { uniqueTokens } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
@@ -87,7 +90,7 @@ describe('FilteredSearchBarRoot', () => {
it('initializes `filterValue`, `selectedSortOption` and `selectedSortDirection` data props and displays the sort dropdown', () => {
expect(wrapper.vm.filterValue).toEqual([]);
expect(wrapper.vm.selectedSortOption).toBe(mockSortOptions[0]);
- expect(wrapper.vm.selectedSortDirection).toBe(SortDirection.descending);
+ expect(wrapper.vm.selectedSortDirection).toBe(SORT_DIRECTION.descending);
expect(wrapper.findComponent(GlButtonGroup).exists()).toBe(true);
expect(wrapper.findComponent(GlButton).exists()).toBe(true);
expect(wrapper.findComponent(GlDropdown).exists()).toBe(true);
@@ -110,9 +113,9 @@ describe('FilteredSearchBarRoot', () => {
describe('tokenSymbols', () => {
it('returns a map containing type and symbols from `tokens` prop', () => {
expect(wrapper.vm.tokenSymbols).toEqual({
- author_username: '@',
- label_name: '~',
- milestone_title: '%',
+ [TOKEN_TYPE_AUTHOR]: '@',
+ [TOKEN_TYPE_LABEL]: '~',
+ [TOKEN_TYPE_MILESTONE]: '%',
});
});
});
@@ -120,9 +123,9 @@ describe('FilteredSearchBarRoot', () => {
describe('tokenTitles', () => {
it('returns a map containing type and title from `tokens` prop', () => {
expect(wrapper.vm.tokenTitles).toEqual({
- author_username: 'Author',
- label_name: 'Label',
- milestone_title: 'Milestone',
+ [TOKEN_TYPE_AUTHOR]: 'Author',
+ [TOKEN_TYPE_LABEL]: 'Label',
+ [TOKEN_TYPE_MILESTONE]: 'Milestone',
});
});
});
@@ -132,7 +135,7 @@ describe('FilteredSearchBarRoot', () => {
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
- selectedSortDirection: SortDirection.ascending,
+ selectedSortDirection: SORT_DIRECTION.ascending,
});
expect(wrapper.vm.sortDirectionIcon).toBe('sort-lowest');
@@ -142,7 +145,7 @@ describe('FilteredSearchBarRoot', () => {
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
- selectedSortDirection: SortDirection.descending,
+ selectedSortDirection: SORT_DIRECTION.descending,
});
expect(wrapper.vm.sortDirectionIcon).toBe('sort-highest');
@@ -154,7 +157,7 @@ describe('FilteredSearchBarRoot', () => {
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
- selectedSortDirection: SortDirection.ascending,
+ selectedSortDirection: SORT_DIRECTION.ascending,
});
expect(wrapper.vm.sortDirectionTooltip).toBe('Sort direction: Ascending');
@@ -164,7 +167,7 @@ describe('FilteredSearchBarRoot', () => {
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
- selectedSortDirection: SortDirection.descending,
+ selectedSortDirection: SORT_DIRECTION.descending,
});
expect(wrapper.vm.sortDirectionTooltip).toBe('Sort direction: Descending');
@@ -272,11 +275,11 @@ describe('FilteredSearchBarRoot', () => {
});
it('sets `selectedSortDirection` to be opposite of its current value', () => {
- expect(wrapper.vm.selectedSortDirection).toBe(SortDirection.descending);
+ expect(wrapper.vm.selectedSortDirection).toBe(SORT_DIRECTION.descending);
wrapper.vm.handleSortDirectionClick();
- expect(wrapper.vm.selectedSortDirection).toBe(SortDirection.ascending);
+ expect(wrapper.vm.selectedSortDirection).toBe(SORT_DIRECTION.ascending);
});
it('emits component event `onSort` with opposite of currently selected sort by value', () => {
@@ -384,7 +387,7 @@ describe('FilteredSearchBarRoot', () => {
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
selectedSortOption: mockSortOptions[0],
- selectedSortDirection: SortDirection.descending,
+ selectedSortDirection: SORT_DIRECTION.descending,
recentSearches: mockHistoryItems,
});
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js b/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js
index a6713b7e7e4..b2f4c780f51 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js
@@ -1,8 +1,28 @@
import { GlFilteredSearchToken } from '@gitlab/ui';
-import { mockLabels } from 'jest/vue_shared/components/sidebar/labels_select_vue/mock_data';
+import { mockLabels } from 'jest/sidebar/components/labels/labels_select_vue/mock_data';
import Api from '~/api';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
-import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
+import {
+ FILTERED_SEARCH_TERM,
+ OPERATORS_IS,
+ TOKEN_TITLE_AUTHOR,
+ TOKEN_TITLE_CONTACT,
+ TOKEN_TITLE_LABEL,
+ TOKEN_TITLE_MILESTONE,
+ TOKEN_TITLE_MY_REACTION,
+ TOKEN_TITLE_ORGANIZATION,
+ TOKEN_TITLE_RELEASE,
+ TOKEN_TITLE_SOURCE_BRANCH,
+ TOKEN_TYPE_AUTHOR,
+ TOKEN_TYPE_CONFIDENTIAL,
+ TOKEN_TYPE_CONTACT,
+ TOKEN_TYPE_LABEL,
+ TOKEN_TYPE_MILESTONE,
+ TOKEN_TYPE_MY_REACTION,
+ TOKEN_TYPE_ORGANIZATION,
+ TOKEN_TYPE_RELEASE,
+ TOKEN_TYPE_SOURCE_BRANCH,
+} from '~/vue_shared/components/filtered_search_bar/constants';
+import UserToken from '~/vue_shared/components/filtered_search_bar/tokens/user_token.vue';
import BranchToken from '~/vue_shared/components/filtered_search_bar/tokens/branch_token.vue';
import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue';
import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
@@ -11,7 +31,7 @@ import ReleaseToken from '~/vue_shared/components/filtered_search_bar/tokens/rel
import CrmContactToken from '~/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue';
import CrmOrganizationToken from '~/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue';
-export const mockAuthor1 = {
+export const mockUser1 = {
id: 1,
name: 'Administrator',
username: 'root',
@@ -20,7 +40,7 @@ export const mockAuthor1 = {
web_url: 'http://0.0.0.0:3000/root',
};
-export const mockAuthor2 = {
+export const mockUser2 = {
id: 2,
name: 'Claudio Beer',
username: 'ericka_terry',
@@ -29,7 +49,7 @@ export const mockAuthor2 = {
web_url: 'http://0.0.0.0:3000/ericka_terry',
};
-export const mockAuthor3 = {
+export const mockUser3 = {
id: 6,
name: 'Shizue Hartmann',
username: 'junita.weimann',
@@ -38,7 +58,7 @@ export const mockAuthor3 = {
web_url: 'http://0.0.0.0:3000/junita.weimann',
};
-export const mockAuthors = [mockAuthor1, mockAuthor2, mockAuthor3];
+export const mockUsers = [mockUser1, mockUser2, mockUser3];
export const mockBranches = [{ name: 'Main' }, { name: 'v1.x' }, { name: 'my-Branch' }];
@@ -197,86 +217,86 @@ export const mockEmoji2 = {
export const mockEmojis = [mockEmoji1, mockEmoji2];
export const mockBranchToken = {
- type: 'source_branch',
+ type: TOKEN_TYPE_SOURCE_BRANCH,
icon: 'branch',
- title: 'Source Branch',
+ title: TOKEN_TITLE_SOURCE_BRANCH,
unique: true,
token: BranchToken,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
fetchBranches: Api.branches.bind(Api),
};
export const mockAuthorToken = {
- type: 'author_username',
+ type: TOKEN_TYPE_AUTHOR,
icon: 'user',
- title: 'Author',
+ title: TOKEN_TITLE_AUTHOR,
unique: false,
symbol: '@',
- token: AuthorToken,
- operators: OPERATOR_IS_ONLY,
+ token: UserToken,
+ operators: OPERATORS_IS,
fetchPath: 'gitlab-org/gitlab-test',
- fetchAuthors: Api.projectUsers.bind(Api),
+ fetchUsers: Api.projectUsers.bind(Api),
};
export const mockLabelToken = {
- type: 'label_name',
+ type: TOKEN_TYPE_LABEL,
icon: 'labels',
- title: 'Label',
+ title: TOKEN_TITLE_LABEL,
unique: false,
symbol: '~',
token: LabelToken,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
fetchLabels: () => Promise.resolve(mockLabels),
};
export const mockMilestoneToken = {
- type: 'milestone_title',
+ type: TOKEN_TYPE_MILESTONE,
icon: 'clock',
- title: 'Milestone',
+ title: TOKEN_TITLE_MILESTONE,
unique: true,
symbol: '%',
token: MilestoneToken,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
fetchMilestones: () => Promise.resolve({ data: mockMilestones }),
};
export const mockReleaseToken = {
- type: 'release',
+ type: TOKEN_TYPE_RELEASE,
icon: 'rocket',
- title: 'Release',
+ title: TOKEN_TITLE_RELEASE,
token: ReleaseToken,
fetchReleases: () => Promise.resolve(),
};
export const mockReactionEmojiToken = {
- type: 'my_reaction_emoji',
+ type: TOKEN_TYPE_MY_REACTION,
icon: 'thumb-up',
- title: 'My-Reaction',
+ title: TOKEN_TITLE_MY_REACTION,
unique: true,
token: EmojiToken,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
fetchEmojis: () => Promise.resolve(mockEmojis),
};
export const mockCrmContactToken = {
- type: 'crm_contact',
- title: 'Contact',
+ type: TOKEN_TYPE_CONTACT,
+ title: TOKEN_TITLE_CONTACT,
icon: 'user',
token: CrmContactToken,
isProject: false,
fullPath: 'group',
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
unique: true,
};
export const mockCrmOrganizationToken = {
- type: 'crm_contact',
- title: 'Organization',
+ type: TOKEN_TYPE_ORGANIZATION,
+ title: TOKEN_TITLE_ORGANIZATION,
icon: 'user',
token: CrmOrganizationToken,
isProject: false,
fullPath: 'group',
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
unique: true,
};
@@ -286,7 +306,7 @@ export const mockMembershipToken = {
title: 'Membership',
token: GlFilteredSearchToken,
unique: true,
- operators: OPERATOR_IS_ONLY,
+ operators: OPERATORS_IS,
options: [
{ value: 'exclude', title: 'Direct' },
{ value: 'only', title: 'Inherited' },
@@ -301,7 +321,7 @@ export const mockMembershipTokenOptionsWithoutTitles = {
export const mockAvailableTokens = [mockAuthorToken, mockLabelToken, mockMilestoneToken];
export const tokenValueAuthor = {
- type: 'author_username',
+ type: TOKEN_TYPE_AUTHOR,
value: {
data: 'root',
operator: '=',
@@ -309,7 +329,7 @@ export const tokenValueAuthor = {
};
export const tokenValueLabel = {
- type: 'label_name',
+ type: TOKEN_TYPE_LABEL,
value: {
operator: '=',
data: 'bug',
@@ -317,7 +337,7 @@ export const tokenValueLabel = {
};
export const tokenValueMilestone = {
- type: 'milestone_title',
+ type: TOKEN_TYPE_MILESTONE,
value: {
operator: '=',
data: 'v1.0',
@@ -333,7 +353,7 @@ export const tokenValueMembership = {
};
export const tokenValueConfidential = {
- type: 'confidential',
+ type: TOKEN_TYPE_CONFIDENTIAL,
value: {
operator: '=',
data: true,
@@ -341,23 +361,10 @@ export const tokenValueConfidential = {
};
export const tokenValuePlain = {
- type: 'filtered-search-term',
+ type: FILTERED_SEARCH_TERM,
value: { data: 'foo' },
};
-export const tokenValueEmpty = {
- type: 'filtered-search-term',
- value: { data: '' },
-};
-
-export const tokenValueEpic = {
- type: 'epic_iid',
- value: {
- operator: '=',
- data: '"foo"::&42',
- },
-};
-
export const mockHistoryItems = [
[tokenValueAuthor, tokenValueLabel, tokenValueMilestone, 'duo'],
[tokenValueAuthor, 'si'],
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
deleted file mode 100644
index 5371b9af475..00000000000
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
+++ /dev/null
@@ -1,333 +0,0 @@
-import {
- GlFilteredSearchTokenSegment,
- GlFilteredSearchSuggestion,
- GlDropdownDivider,
- GlAvatar,
-} from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import { nextTick } from 'vue';
-import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert } from '~/flash';
-import axios from '~/lib/utils/axios_utils';
-
-import { DEFAULT_NONE_ANY } from '~/vue_shared/components/filtered_search_bar/constants';
-import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
-import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
-
-import { mockAuthorToken, mockAuthors } from '../mock_data';
-
-jest.mock('~/flash');
-const defaultStubs = {
- Portal: true,
- GlFilteredSearchSuggestionList: {
- template: '<div></div>',
- methods: {
- getValue: () => '=',
- },
- },
-};
-
-const mockPreloadedAuthors = [
- {
- id: 13,
- name: 'Administrator',
- username: 'root',
- avatar_url: 'avatar/url',
- },
-];
-
-function createComponent(options = {}) {
- const {
- config = mockAuthorToken,
- value = { data: '' },
- active = false,
- stubs = defaultStubs,
- data = {},
- listeners = {},
- } = options;
- return mount(AuthorToken, {
- propsData: {
- config,
- value,
- active,
- cursorPosition: 'start',
- },
- provide: {
- portalName: 'fake target',
- alignSuggestions: function fakeAlignSuggestions() {},
- suggestionsListClass: () => 'custom-class',
- },
- data() {
- return { ...data };
- },
- stubs,
- listeners,
- });
-}
-
-describe('AuthorToken', () => {
- const originalGon = window.gon;
- const currentUserLength = 1;
- let mock;
- let wrapper;
-
- const getBaseToken = () => wrapper.findComponent(BaseToken);
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- window.gon = originalGon;
- mock.restore();
- wrapper.destroy();
- });
-
- describe('methods', () => {
- describe('fetchAuthors', () => {
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- it('calls `config.fetchAuthors` with provided searchTerm param', () => {
- jest.spyOn(wrapper.vm.config, 'fetchAuthors');
-
- getBaseToken().vm.$emit('fetch-suggestions', mockAuthors[0].username);
-
- expect(wrapper.vm.config.fetchAuthors).toHaveBeenCalledWith(
- mockAuthorToken.fetchPath,
- mockAuthors[0].username,
- );
- });
-
- it('sets response to `authors` when request is succesful', () => {
- jest.spyOn(wrapper.vm.config, 'fetchAuthors').mockResolvedValue(mockAuthors);
-
- getBaseToken().vm.$emit('fetch-suggestions', 'root');
-
- return waitForPromises().then(() => {
- expect(getBaseToken().props('suggestions')).toEqual(mockAuthors);
- });
- });
-
- // TODO: rm when completed https://gitlab.com/gitlab-org/gitlab/-/issues/345756
- describe('when there are null users presents', () => {
- const mockAuthorsWithNullUser = mockAuthors.concat([null]);
-
- beforeEach(() => {
- jest
- .spyOn(wrapper.vm.config, 'fetchAuthors')
- .mockResolvedValue({ data: mockAuthorsWithNullUser });
-
- getBaseToken().vm.$emit('fetch-suggestions', 'root');
- });
-
- describe('when res.data is present', () => {
- it('filters the successful response when null values are present', () => {
- return waitForPromises().then(() => {
- expect(getBaseToken().props('suggestions')).toEqual(mockAuthors);
- });
- });
- });
-
- describe('when response is an array', () => {
- it('filters the successful response when null values are present', () => {
- return waitForPromises().then(() => {
- expect(getBaseToken().props('suggestions')).toEqual(mockAuthors);
- });
- });
- });
- });
-
- it('calls `createAlert` with flash error message when request fails', () => {
- jest.spyOn(wrapper.vm.config, 'fetchAuthors').mockRejectedValue({});
-
- getBaseToken().vm.$emit('fetch-suggestions', 'root');
-
- return waitForPromises().then(() => {
- expect(createAlert).toHaveBeenCalledWith({
- message: 'There was a problem fetching users.',
- });
- });
- });
-
- it('sets `loading` to false when request completes', async () => {
- jest.spyOn(wrapper.vm.config, 'fetchAuthors').mockRejectedValue({});
-
- getBaseToken().vm.$emit('fetch-suggestions', 'root');
-
- await waitForPromises();
-
- expect(getBaseToken().props('suggestionsLoading')).toBe(false);
- });
- });
- });
-
- describe('template', () => {
- const activateSuggestionsList = async () => {
- const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment);
- const suggestionsSegment = tokenSegments.at(2);
- suggestionsSegment.vm.$emit('activate');
- await nextTick();
- };
-
- it('renders base-token component', () => {
- wrapper = createComponent({
- value: { data: mockAuthors[0].username },
- data: { authors: mockAuthors },
- });
-
- const baseTokenEl = getBaseToken();
-
- expect(baseTokenEl.exists()).toBe(true);
- expect(baseTokenEl.props()).toMatchObject({
- suggestions: mockAuthors,
- getActiveTokenValue: wrapper.vm.getActiveAuthor,
- });
- });
-
- it('renders token item when value is selected', async () => {
- wrapper = createComponent({
- value: { data: mockAuthors[0].username },
- data: { authors: mockAuthors },
- stubs: { Portal: true },
- });
-
- await nextTick();
- const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment);
-
- expect(tokenSegments).toHaveLength(3); // Author, =, "Administrator"
-
- const tokenValue = tokenSegments.at(2);
-
- expect(tokenValue.findComponent(GlAvatar).props('src')).toBe(mockAuthors[0].avatar_url);
- expect(tokenValue.text()).toBe(mockAuthors[0].name); // "Administrator"
- });
-
- it('renders token value with correct avatarUrl from author object', async () => {
- const getAvatarEl = () =>
- wrapper.findAllComponents(GlFilteredSearchTokenSegment).at(2).findComponent(GlAvatar);
-
- wrapper = createComponent({
- value: { data: mockAuthors[0].username },
- data: {
- authors: [
- {
- ...mockAuthors[0],
- },
- ],
- },
- stubs: { Portal: true },
- });
-
- await nextTick();
-
- expect(getAvatarEl().props('src')).toBe(mockAuthors[0].avatar_url);
-
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- authors: [
- {
- ...mockAuthors[0],
- avatarUrl: mockAuthors[0].avatar_url,
- avatar_url: undefined,
- },
- ],
- });
-
- await nextTick();
-
- expect(getAvatarEl().props('src')).toBe(mockAuthors[0].avatar_url);
- });
-
- it('renders provided defaultAuthors as suggestions', async () => {
- const defaultAuthors = DEFAULT_NONE_ANY;
- wrapper = createComponent({
- active: true,
- config: { ...mockAuthorToken, defaultAuthors, preloadedAuthors: mockPreloadedAuthors },
- stubs: { Portal: true },
- });
-
- await activateSuggestionsList();
-
- const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion);
-
- expect(suggestions).toHaveLength(defaultAuthors.length + currentUserLength);
- defaultAuthors.forEach((label, index) => {
- expect(suggestions.at(index).text()).toBe(label.text);
- });
- });
-
- it('does not render divider when no defaultAuthors', async () => {
- wrapper = createComponent({
- active: true,
- config: { ...mockAuthorToken, defaultAuthors: [] },
- stubs: { Portal: true },
- });
- const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment);
- const suggestionsSegment = tokenSegments.at(2);
- suggestionsSegment.vm.$emit('activate');
- await nextTick();
-
- expect(wrapper.findComponent(GlDropdownDivider).exists()).toBe(false);
- });
-
- it('renders `DEFAULT_NONE_ANY` as default suggestions', async () => {
- wrapper = createComponent({
- active: true,
- config: { ...mockAuthorToken, preloadedAuthors: mockPreloadedAuthors },
- stubs: { Portal: true },
- });
-
- await activateSuggestionsList();
-
- const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion);
-
- expect(suggestions).toHaveLength(2 + currentUserLength);
- expect(suggestions.at(0).text()).toBe(DEFAULT_NONE_ANY[0].text);
- expect(suggestions.at(1).text()).toBe(DEFAULT_NONE_ANY[1].text);
- });
-
- it('emits listeners in the base-token', () => {
- const mockInput = jest.fn();
- wrapper = createComponent({
- listeners: {
- input: mockInput,
- },
- });
- wrapper.findComponent(BaseToken).vm.$emit('input', [{ data: 'mockData', operator: '=' }]);
-
- expect(mockInput).toHaveBeenLastCalledWith([{ data: 'mockData', operator: '=' }]);
- });
-
- describe('when loading', () => {
- beforeEach(() => {
- wrapper = createComponent({
- active: true,
- config: {
- ...mockAuthorToken,
- preloadedAuthors: mockPreloadedAuthors,
- defaultAuthors: [],
- },
- stubs: { Portal: true },
- });
- });
-
- it('shows current user', () => {
- const firstSuggestion = wrapper.findComponent(GlFilteredSearchSuggestion).text();
- expect(firstSuggestion).toContain('Administrator');
- expect(firstSuggestion).toContain('@root');
- });
-
- it('does not show current user while searching', async () => {
- wrapper.findComponent(BaseToken).vm.handleInput({ data: 'foo' });
-
- await nextTick();
-
- expect(wrapper.findComponent(GlFilteredSearchSuggestion).exists()).toBe(false);
- });
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/base_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/base_token_spec.js
index a0126c2bd63..164235e4bb9 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/base_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/base_token_spec.js
@@ -12,12 +12,12 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import {
mockRegularLabel,
mockLabels,
-} from 'jest/vue_shared/components/sidebar/labels_select_vue/mock_data';
+} from 'jest/sidebar/components/labels/labels_select_vue/mock_data';
import {
- DEFAULT_NONE_ANY,
+ OPTIONS_NONE_ANY,
OPERATOR_IS,
- OPERATOR_IS_NOT,
+ OPERATOR_NOT,
} from '~/vue_shared/components/filtered_search_bar/constants';
import {
getRecentlyUsedSuggestions,
@@ -76,7 +76,7 @@ const mockProps = {
active: false,
suggestions: [],
suggestionsLoading: false,
- defaultSuggestions: DEFAULT_NONE_ANY,
+ defaultSuggestions: OPTIONS_NONE_ANY,
getActiveTokenValue: (labels, data) => labels.find((label) => label.title === data),
cursorPosition: 'start',
};
@@ -301,13 +301,13 @@ describe('BaseToken', () => {
describe('with default suggestions', () => {
describe.each`
- operator | shouldRenderFilteredSearchSuggestion
- ${OPERATOR_IS} | ${true}
- ${OPERATOR_IS_NOT} | ${false}
+ operator | shouldRenderFilteredSearchSuggestion
+ ${OPERATOR_IS} | ${true}
+ ${OPERATOR_NOT} | ${false}
`('when operator is $operator', ({ shouldRenderFilteredSearchSuggestion, operator }) => {
beforeEach(() => {
const props = {
- defaultSuggestions: DEFAULT_NONE_ANY,
+ defaultSuggestions: OPTIONS_NONE_ANY,
value: { data: '', operator },
};
@@ -322,7 +322,7 @@ describe('BaseToken', () => {
if (shouldRenderFilteredSearchSuggestion) {
expect(filteredSearchSuggestions.map((c) => c.props())).toMatchObject(
- DEFAULT_NONE_ANY.map((opt) => ({ value: opt.value })),
+ OPTIONS_NONE_ANY.map((opt) => ({ value: opt.value })),
);
} else {
expect(filteredSearchSuggestions).toHaveLength(0);
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js
index 05b42011fe1..311d5a13280 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js
@@ -11,7 +11,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
-import { DEFAULT_NONE_ANY } from '~/vue_shared/components/filtered_search_bar/constants';
+import { OPTIONS_NONE_ANY } from '~/vue_shared/components/filtered_search_bar/constants';
import BranchToken from '~/vue_shared/components/filtered_search_bar/tokens/branch_token.vue';
import { mockBranches, mockBranchToken } from '../mock_data';
@@ -112,7 +112,7 @@ describe('BranchToken', () => {
});
describe('template', () => {
- const defaultBranches = DEFAULT_NONE_ANY;
+ const defaultBranches = OPTIONS_NONE_ANY;
async function showSuggestions() {
const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment);
const suggestionsSegment = tokenSegments.at(2);
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_contact_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_contact_token_spec.js
index 5b744521979..7be7035a0f2 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_contact_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_contact_token_spec.js
@@ -10,7 +10,7 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { createAlert } from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { DEFAULT_NONE_ANY } from '~/vue_shared/components/filtered_search_bar/constants';
+import { OPTIONS_NONE_ANY } from '~/vue_shared/components/filtered_search_bar/constants';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
import CrmContactToken from '~/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue';
import searchCrmContactsQuery from '~/vue_shared/components/filtered_search_bar/queries/search_crm_contacts.query.graphql';
@@ -187,7 +187,7 @@ describe('CrmContactToken', () => {
});
describe('template', () => {
- const defaultContacts = DEFAULT_NONE_ANY;
+ const defaultContacts = OPTIONS_NONE_ANY;
it('renders base-token component', () => {
mountComponent({
@@ -250,7 +250,7 @@ describe('CrmContactToken', () => {
expect(wrapper.findComponent(GlDropdownDivider).exists()).toBe(false);
});
- it('renders `DEFAULT_NONE_ANY` as default suggestions', () => {
+ it('renders `OPTIONS_NONE_ANY` as default suggestions', () => {
mountComponent({
active: true,
config: { ...mockCrmContactToken },
@@ -262,8 +262,8 @@ describe('CrmContactToken', () => {
const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion);
- expect(suggestions).toHaveLength(DEFAULT_NONE_ANY.length);
- DEFAULT_NONE_ANY.forEach((contact, index) => {
+ expect(suggestions).toHaveLength(OPTIONS_NONE_ANY.length);
+ OPTIONS_NONE_ANY.forEach((contact, index) => {
expect(suggestions.at(index).text()).toBe(contact.text);
});
});
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_organization_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_organization_token_spec.js
index 3a3e96032e8..ecd3e8a04f1 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_organization_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_organization_token_spec.js
@@ -10,7 +10,7 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { createAlert } from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { DEFAULT_NONE_ANY } from '~/vue_shared/components/filtered_search_bar/constants';
+import { OPTIONS_NONE_ANY } from '~/vue_shared/components/filtered_search_bar/constants';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
import CrmOrganizationToken from '~/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue';
import searchCrmOrganizationsQuery from '~/vue_shared/components/filtered_search_bar/queries/search_crm_organizations.query.graphql';
@@ -186,7 +186,7 @@ describe('CrmOrganizationToken', () => {
});
describe('template', () => {
- const defaultOrganizations = DEFAULT_NONE_ANY;
+ const defaultOrganizations = OPTIONS_NONE_ANY;
it('renders base-token component', () => {
mountComponent({
@@ -249,7 +249,7 @@ describe('CrmOrganizationToken', () => {
expect(wrapper.findComponent(GlDropdownDivider).exists()).toBe(false);
});
- it('renders `DEFAULT_NONE_ANY` as default suggestions', () => {
+ it('renders `OPTIONS_NONE_ANY` as default suggestions', () => {
mountComponent({
active: true,
config: { ...mockCrmOrganizationToken },
@@ -261,8 +261,8 @@ describe('CrmOrganizationToken', () => {
const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion);
- expect(suggestions).toHaveLength(DEFAULT_NONE_ANY.length);
- DEFAULT_NONE_ANY.forEach((organization, index) => {
+ expect(suggestions).toHaveLength(OPTIONS_NONE_ANY.length);
+ OPTIONS_NONE_ANY.forEach((organization, index) => {
expect(suggestions.at(index).text()).toBe(organization.text);
});
});
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js
index e8436d2db17..773df01ada7 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js
@@ -12,9 +12,9 @@ import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import {
- DEFAULT_LABEL_NONE,
- DEFAULT_LABEL_ANY,
- DEFAULT_NONE_ANY,
+ OPTION_NONE,
+ OPTION_ANY,
+ OPTIONS_NONE_ANY,
} from '~/vue_shared/components/filtered_search_bar/constants';
import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue';
@@ -118,7 +118,7 @@ describe('EmojiToken', () => {
});
describe('template', () => {
- const defaultEmojis = DEFAULT_NONE_ANY;
+ const defaultEmojis = OPTIONS_NONE_ANY;
beforeEach(async () => {
wrapper = createComponent({
@@ -181,7 +181,7 @@ describe('EmojiToken', () => {
expect(wrapper.findComponent(GlDropdownDivider).exists()).toBe(false);
});
- it('renders `DEFAULT_LABEL_NONE` and `DEFAULT_LABEL_ANY` as default suggestions', async () => {
+ it('renders `OPTION_NONE` and `OPTION_ANY` as default suggestions', async () => {
wrapper = createComponent({
active: true,
config: { ...mockReactionEmojiToken },
@@ -195,8 +195,8 @@ describe('EmojiToken', () => {
const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion);
expect(suggestions).toHaveLength(2);
- expect(suggestions.at(0).text()).toBe(DEFAULT_LABEL_NONE.text);
- expect(suggestions.at(1).text()).toBe(DEFAULT_LABEL_ANY.text);
+ expect(suggestions.at(0).text()).toBe(OPTION_NONE.text);
+ expect(suggestions.at(1).text()).toBe(OPTION_ANY.text);
});
});
});
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js
index 8ca12afacec..9d96123c17f 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js
@@ -10,11 +10,11 @@ import waitForPromises from 'helpers/wait_for_promises';
import {
mockRegularLabel,
mockLabels,
-} from 'jest/vue_shared/components/sidebar/labels_select_vue/mock_data';
+} from 'jest/sidebar/components/labels/labels_select_vue/mock_data';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
-import { DEFAULT_NONE_ANY } from '~/vue_shared/components/filtered_search_bar/constants';
+import { OPTIONS_NONE_ANY } from '~/vue_shared/components/filtered_search_bar/constants';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
@@ -141,7 +141,7 @@ describe('LabelToken', () => {
});
describe('template', () => {
- const defaultLabels = DEFAULT_NONE_ANY;
+ const defaultLabels = OPTIONS_NONE_ANY;
beforeEach(async () => {
wrapper = createComponent({ value: { data: `"${mockRegularLabel.title}"` } });
@@ -209,7 +209,7 @@ describe('LabelToken', () => {
expect(wrapper.findComponent(GlDropdownDivider).exists()).toBe(false);
});
- it('renders `DEFAULT_NONE_ANY` as default suggestions', () => {
+ it('renders `OPTIONS_NONE_ANY` as default suggestions', () => {
wrapper = createComponent({
active: true,
config: { ...mockLabelToken },
@@ -221,8 +221,8 @@ describe('LabelToken', () => {
const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion);
- expect(suggestions).toHaveLength(DEFAULT_NONE_ANY.length);
- DEFAULT_NONE_ANY.forEach((label, index) => {
+ expect(suggestions).toHaveLength(OPTIONS_NONE_ANY.length);
+ OPTIONS_NONE_ANY.forEach((label, index) => {
expect(suggestions.at(index).text()).toBe(label.text);
});
});
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/user_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/user_token_spec.js
new file mode 100644
index 00000000000..32cb74d5f80
--- /dev/null
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/user_token_spec.js
@@ -0,0 +1,333 @@
+import {
+ GlFilteredSearchTokenSegment,
+ GlFilteredSearchSuggestion,
+ GlDropdownDivider,
+ GlAvatar,
+} from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import { nextTick } from 'vue';
+import waitForPromises from 'helpers/wait_for_promises';
+import { createAlert } from '~/flash';
+import axios from '~/lib/utils/axios_utils';
+
+import { OPTIONS_NONE_ANY } from '~/vue_shared/components/filtered_search_bar/constants';
+import UserToken from '~/vue_shared/components/filtered_search_bar/tokens/user_token.vue';
+import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
+
+import { mockAuthorToken, mockUsers } from '../mock_data';
+
+jest.mock('~/flash');
+const defaultStubs = {
+ Portal: true,
+ GlFilteredSearchSuggestionList: {
+ template: '<div></div>',
+ methods: {
+ getValue: () => '=',
+ },
+ },
+};
+
+const mockPreloadedUsers = [
+ {
+ id: 13,
+ name: 'Administrator',
+ username: 'root',
+ avatar_url: 'avatar/url',
+ },
+];
+
+function createComponent(options = {}) {
+ const {
+ config = mockAuthorToken,
+ value = { data: '' },
+ active = false,
+ stubs = defaultStubs,
+ data = {},
+ listeners = {},
+ } = options;
+ return mount(UserToken, {
+ propsData: {
+ config,
+ value,
+ active,
+ cursorPosition: 'start',
+ },
+ provide: {
+ portalName: 'fake target',
+ alignSuggestions: function fakeAlignSuggestions() {},
+ suggestionsListClass: () => 'custom-class',
+ },
+ data() {
+ return { ...data };
+ },
+ stubs,
+ listeners,
+ });
+}
+
+describe('UserToken', () => {
+ const originalGon = window.gon;
+ const currentUserLength = 1;
+ let mock;
+ let wrapper;
+
+ const getBaseToken = () => wrapper.findComponent(BaseToken);
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ window.gon = originalGon;
+ mock.restore();
+ wrapper.destroy();
+ });
+
+ describe('methods', () => {
+ describe('fetchUsers', () => {
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ it('calls `config.fetchUsers` with provided searchTerm param', () => {
+ jest.spyOn(wrapper.vm.config, 'fetchUsers');
+
+ getBaseToken().vm.$emit('fetch-suggestions', mockUsers[0].username);
+
+ expect(wrapper.vm.config.fetchUsers).toHaveBeenCalledWith(
+ mockAuthorToken.fetchPath,
+ mockUsers[0].username,
+ );
+ });
+
+ it('sets response to `users` when request is successful', () => {
+ jest.spyOn(wrapper.vm.config, 'fetchUsers').mockResolvedValue(mockUsers);
+
+ getBaseToken().vm.$emit('fetch-suggestions', 'root');
+
+ return waitForPromises().then(() => {
+ expect(getBaseToken().props('suggestions')).toEqual(mockUsers);
+ });
+ });
+
+ // TODO: rm when completed https://gitlab.com/gitlab-org/gitlab/-/issues/345756
+ describe('when there are null users presents', () => {
+ const mockUsersWithNullUser = mockUsers.concat([null]);
+
+ beforeEach(() => {
+ jest
+ .spyOn(wrapper.vm.config, 'fetchUsers')
+ .mockResolvedValue({ data: mockUsersWithNullUser });
+
+ getBaseToken().vm.$emit('fetch-suggestions', 'root');
+ });
+
+ describe('when res.data is present', () => {
+ it('filters the successful response when null values are present', () => {
+ return waitForPromises().then(() => {
+ expect(getBaseToken().props('suggestions')).toEqual(mockUsers);
+ });
+ });
+ });
+
+ describe('when response is an array', () => {
+ it('filters the successful response when null values are present', () => {
+ return waitForPromises().then(() => {
+ expect(getBaseToken().props('suggestions')).toEqual(mockUsers);
+ });
+ });
+ });
+ });
+
+ it('calls `createAlert` with flash error message when request fails', () => {
+ jest.spyOn(wrapper.vm.config, 'fetchUsers').mockRejectedValue({});
+
+ getBaseToken().vm.$emit('fetch-suggestions', 'root');
+
+ return waitForPromises().then(() => {
+ expect(createAlert).toHaveBeenCalledWith({
+ message: 'There was a problem fetching users.',
+ });
+ });
+ });
+
+ it('sets `loading` to false when request completes', async () => {
+ jest.spyOn(wrapper.vm.config, 'fetchUsers').mockRejectedValue({});
+
+ getBaseToken().vm.$emit('fetch-suggestions', 'root');
+
+ await waitForPromises();
+
+ expect(getBaseToken().props('suggestionsLoading')).toBe(false);
+ });
+ });
+ });
+
+ describe('template', () => {
+ const activateSuggestionsList = async () => {
+ const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment);
+ const suggestionsSegment = tokenSegments.at(2);
+ suggestionsSegment.vm.$emit('activate');
+ await nextTick();
+ };
+
+ it('renders base-token component', () => {
+ wrapper = createComponent({
+ value: { data: mockUsers[0].username },
+ data: { users: mockUsers },
+ });
+
+ const baseTokenEl = getBaseToken();
+
+ expect(baseTokenEl.exists()).toBe(true);
+ expect(baseTokenEl.props()).toMatchObject({
+ suggestions: mockUsers,
+ getActiveTokenValue: wrapper.vm.getActiveUser,
+ });
+ });
+
+ it('renders token item when value is selected', async () => {
+ wrapper = createComponent({
+ value: { data: mockUsers[0].username },
+ data: { users: mockUsers },
+ stubs: { Portal: true },
+ });
+
+ await nextTick();
+ const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment);
+
+ expect(tokenSegments).toHaveLength(3); // Author, =, "Administrator"
+
+ const tokenValue = tokenSegments.at(2);
+
+ expect(tokenValue.findComponent(GlAvatar).props('src')).toBe(mockUsers[0].avatar_url);
+ expect(tokenValue.text()).toBe(mockUsers[0].name); // "Administrator"
+ });
+
+ it('renders token value with correct avatarUrl from user object', async () => {
+ const getAvatarEl = () =>
+ wrapper.findAllComponents(GlFilteredSearchTokenSegment).at(2).findComponent(GlAvatar);
+
+ wrapper = createComponent({
+ value: { data: mockUsers[0].username },
+ data: {
+ users: [
+ {
+ ...mockUsers[0],
+ },
+ ],
+ },
+ stubs: { Portal: true },
+ });
+
+ await nextTick();
+
+ expect(getAvatarEl().props('src')).toBe(mockUsers[0].avatar_url);
+
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ users: [
+ {
+ ...mockUsers[0],
+ avatarUrl: mockUsers[0].avatar_url,
+ avatar_url: undefined,
+ },
+ ],
+ });
+
+ await nextTick();
+
+ expect(getAvatarEl().props('src')).toBe(mockUsers[0].avatar_url);
+ });
+
+ it('renders provided defaultUsers as suggestions', async () => {
+ const defaultUsers = OPTIONS_NONE_ANY;
+ wrapper = createComponent({
+ active: true,
+ config: { ...mockAuthorToken, defaultUsers, preloadedUsers: mockPreloadedUsers },
+ stubs: { Portal: true },
+ });
+
+ await activateSuggestionsList();
+
+ const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion);
+
+ expect(suggestions).toHaveLength(defaultUsers.length + currentUserLength);
+ defaultUsers.forEach((label, index) => {
+ expect(suggestions.at(index).text()).toBe(label.text);
+ });
+ });
+
+ it('does not render divider when no defaultUsers', async () => {
+ wrapper = createComponent({
+ active: true,
+ config: { ...mockAuthorToken, defaultUsers: [] },
+ stubs: { Portal: true },
+ });
+ const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment);
+ const suggestionsSegment = tokenSegments.at(2);
+ suggestionsSegment.vm.$emit('activate');
+ await nextTick();
+
+ expect(wrapper.findComponent(GlDropdownDivider).exists()).toBe(false);
+ });
+
+ it('renders `OPTIONS_NONE_ANY` as default suggestions', async () => {
+ wrapper = createComponent({
+ active: true,
+ config: { ...mockAuthorToken, preloadedUsers: mockPreloadedUsers },
+ stubs: { Portal: true },
+ });
+
+ await activateSuggestionsList();
+
+ const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion);
+
+ expect(suggestions).toHaveLength(2 + currentUserLength);
+ expect(suggestions.at(0).text()).toBe(OPTIONS_NONE_ANY[0].text);
+ expect(suggestions.at(1).text()).toBe(OPTIONS_NONE_ANY[1].text);
+ });
+
+ it('emits listeners in the base-token', () => {
+ const mockInput = jest.fn();
+ wrapper = createComponent({
+ listeners: {
+ input: mockInput,
+ },
+ });
+ wrapper.findComponent(BaseToken).vm.$emit('input', [{ data: 'mockData', operator: '=' }]);
+
+ expect(mockInput).toHaveBeenLastCalledWith([{ data: 'mockData', operator: '=' }]);
+ });
+
+ describe('when loading', () => {
+ beforeEach(() => {
+ wrapper = createComponent({
+ active: true,
+ config: {
+ ...mockAuthorToken,
+ preloadedUsers: mockPreloadedUsers,
+ defaultUsers: [],
+ },
+ stubs: { Portal: true },
+ });
+ });
+
+ it('shows current user', () => {
+ const firstSuggestion = wrapper.findComponent(GlFilteredSearchSuggestion).text();
+ expect(firstSuggestion).toContain('Administrator');
+ expect(firstSuggestion).toContain('@root');
+ });
+
+ it('does not show current user while searching', async () => {
+ wrapper.findComponent(BaseToken).vm.handleInput({ data: 'foo' });
+
+ await nextTick();
+
+ expect(wrapper.findComponent(GlFilteredSearchSuggestion).exists()).toBe(false);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/group_select/group_select_spec.js b/spec/frontend/vue_shared/components/group_select/group_select_spec.js
index f959d2225fa..c10b32c6acc 100644
--- a/spec/frontend/vue_shared/components/group_select/group_select_spec.js
+++ b/spec/frontend/vue_shared/components/group_select/group_select_spec.js
@@ -1,5 +1,5 @@
import { nextTick } from 'vue';
-import { GlListbox } from '@gitlab/ui';
+import { GlCollapsibleListbox } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import axios from '~/lib/utils/axios_utils';
@@ -31,7 +31,7 @@ describe('GroupSelect', () => {
const inputId = 'inputId';
// Finders
- const findListbox = () => wrapper.findComponent(GlListbox);
+ const findListbox = () => wrapper.findComponent(GlCollapsibleListbox);
const findInput = () => wrapper.findByTestId('input');
// Helpers
diff --git a/spec/frontend/vue_shared/components/listbox_input/listbox_input_spec.js b/spec/frontend/vue_shared/components/listbox_input/listbox_input_spec.js
new file mode 100644
index 00000000000..cb7262b15e3
--- /dev/null
+++ b/spec/frontend/vue_shared/components/listbox_input/listbox_input_spec.js
@@ -0,0 +1,132 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlListbox } from '@gitlab/ui';
+import ListboxInput from '~/vue_shared/components/listbox_input/listbox_input.vue';
+
+describe('ListboxInput', () => {
+ let wrapper;
+
+ // Props
+ const name = 'name';
+ const defaultToggleText = 'defaultToggleText';
+ const items = [
+ {
+ text: 'Group 1',
+ options: [
+ { text: 'Item 1', value: '1' },
+ { text: 'Item 2', value: '2' },
+ ],
+ },
+ {
+ text: 'Group 2',
+ options: [{ text: 'Item 3', value: '3' }],
+ },
+ ];
+
+ // Finders
+ const findGlListbox = () => wrapper.findComponent(GlListbox);
+ const findInput = () => wrapper.find('input');
+
+ const createComponent = (propsData) => {
+ wrapper = shallowMount(ListboxInput, {
+ propsData: {
+ name,
+ defaultToggleText,
+ items,
+ ...propsData,
+ },
+ });
+ };
+
+ describe('input attributes', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('sets the input name', () => {
+ expect(findInput().attributes('name')).toBe(name);
+ });
+ });
+
+ describe('toggle text', () => {
+ it('uses the default toggle text while no value is selected', () => {
+ createComponent();
+
+ expect(findGlListbox().props('toggleText')).toBe(defaultToggleText);
+ });
+
+ it("uses the selected option's text as the toggle text", () => {
+ const selectedOption = items[0].options[0];
+ createComponent({ selected: selectedOption.value });
+
+ expect(findGlListbox().props('toggleText')).toBe(selectedOption.text);
+ });
+ });
+
+ describe('input value', () => {
+ const selectedOption = items[0].options[0];
+
+ beforeEach(() => {
+ createComponent({ selected: selectedOption.value });
+ jest.spyOn(findInput().element, 'dispatchEvent');
+ });
+
+ it("sets the listbox's and input's values", () => {
+ const { value } = selectedOption;
+
+ expect(findGlListbox().props('selected')).toBe(value);
+ expect(findInput().attributes('value')).toBe(value);
+ });
+
+ describe("when the listbox's value changes", () => {
+ const newSelectedOption = items[1].options[0];
+
+ beforeEach(() => {
+ findGlListbox().vm.$emit('select', newSelectedOption.value);
+ });
+
+ it('emits the `select` event', () => {
+ expect(wrapper.emitted('select')).toEqual([[newSelectedOption.value]]);
+ });
+ });
+ });
+
+ describe('search', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('passes all items to GlListbox by default', () => {
+ createComponent();
+ expect(findGlListbox().props('items')).toStrictEqual(items);
+ });
+
+ describe('with groups', () => {
+ beforeEach(() => {
+ createComponent();
+ findGlListbox().vm.$emit('search', '1');
+ });
+
+ it('passes only the items that match the search string', async () => {
+ expect(findGlListbox().props('items')).toStrictEqual([
+ {
+ text: 'Group 1',
+ options: [{ text: 'Item 1', value: '1' }],
+ },
+ ]);
+ });
+ });
+
+ describe('with flat items', () => {
+ beforeEach(() => {
+ createComponent({
+ items: items[0].options,
+ });
+ findGlListbox().vm.$emit('search', '1');
+ });
+
+ it('passes only the items that match the search string', async () => {
+ expect(findGlListbox().props('items')).toStrictEqual([{ text: 'Item 1', value: '1' }]);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/markdown/field_spec.js b/spec/frontend/vue_shared/components/markdown/field_spec.js
index 50864a4bf25..285ea10c813 100644
--- a/spec/frontend/vue_shared/components/markdown/field_spec.js
+++ b/spec/frontend/vue_shared/components/markdown/field_spec.js
@@ -1,12 +1,14 @@
import { nextTick } from 'vue';
import AxiosMockAdapter from 'axios-mock-adapter';
-import $ from 'jquery';
import { TEST_HOST, FIXTURES_PATH } from 'spec/test_constants';
import axios from '~/lib/utils/axios_utils';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import MarkdownFieldHeader from '~/vue_shared/components/markdown/header.vue';
import MarkdownToolbar from '~/vue_shared/components/markdown/toolbar.vue';
import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
+
+jest.mock('~/behaviors/markdown/render_gfm');
const markdownPreviewPath = `${TEST_HOST}/preview`;
const markdownDocsPath = `${TEST_HOST}/docs`;
@@ -138,15 +140,13 @@ describe('Markdown field component', () => {
});
it('renders markdown preview and GFM', async () => {
- const renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
-
previewLink = getPreviewLink();
previewLink.vm.$emit('click', { target: {} });
await axios.waitFor(markdownPreviewPath);
expect(subject.find('.md-preview-holder').element.innerHTML).toContain(previewHTML);
- expect(renderGFMSpy).toHaveBeenCalled();
+ expect(renderGFM).toHaveBeenCalled();
});
it('calls video.pause() on comment input when isSubmitting is changed to true', async () => {
diff --git a/spec/frontend/vue_shared/components/markdown/field_view_spec.js b/spec/frontend/vue_shared/components/markdown/field_view_spec.js
index be1d840dd29..176ccfc5a69 100644
--- a/spec/frontend/vue_shared/components/markdown/field_view_spec.js
+++ b/spec/frontend/vue_shared/components/markdown/field_view_spec.js
@@ -1,10 +1,11 @@
import { shallowMount } from '@vue/test-utils';
-import $ from 'jquery';
import MarkdownFieldView from '~/vue_shared/components/markdown/field_view.vue';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
+
+jest.mock('~/behaviors/markdown/render_gfm');
describe('Markdown Field View component', () => {
- let renderGFMSpy;
let wrapper;
function createComponent() {
@@ -12,7 +13,6 @@ describe('Markdown Field View component', () => {
}
beforeEach(() => {
- renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
createComponent();
});
@@ -21,6 +21,6 @@ describe('Markdown Field View component', () => {
});
it('processes rendering with GFM', () => {
- expect(renderGFMSpy).toHaveBeenCalledTimes(1);
+ expect(renderGFM).toHaveBeenCalledTimes(1);
});
});
diff --git a/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js b/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js
index 625e67c7cc1..5f416db2676 100644
--- a/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js
+++ b/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js
@@ -171,6 +171,7 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
expect.objectContaining({
renderMarkdown: expect.any(Function),
uploadsPath: window.uploads_path,
+ useBottomToolbar: false,
markdown: value,
}),
);
diff --git a/spec/frontend/vue_shared/components/markdown_drawer/markdown_drawer_spec.js b/spec/frontend/vue_shared/components/markdown_drawer/markdown_drawer_spec.js
index 8edcb905096..2b311b75f85 100644
--- a/spec/frontend/vue_shared/components/markdown_drawer/markdown_drawer_spec.js
+++ b/spec/frontend/vue_shared/components/markdown_drawer/markdown_drawer_spec.js
@@ -1,5 +1,5 @@
import { GlDrawer, GlAlert, GlSkeletonLoader } from '@gitlab/ui';
-import { nextTick } from 'vue';
+import Vue, { nextTick } from 'vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import MarkdownDrawer, { cache } from '~/vue_shared/components/markdown_drawer/markdown_drawer.vue';
import { getRenderedMarkdown } from '~/vue_shared/components/markdown_drawer/utils/fetch';
@@ -82,7 +82,10 @@ describe('MarkdownDrawer', () => {
contentTop.mockClear();
});
- it(`computes offsetTop ${hasNavbar ? 'with' : 'without'} .navbar-gitlab`, () => {
+ it(`computes offsetTop ${hasNavbar ? 'with' : 'without'} .navbar-gitlab`, async () => {
+ wrapper.vm.getDrawerTop();
+ await Vue.nextTick();
+
expect(findDrawer().attributes('headerheight')).toBe(`${navbarHeight}px`);
});
});
@@ -95,11 +98,11 @@ describe('MarkdownDrawer', () => {
renderGLFMSpy = jest.spyOn(MarkdownDrawer.methods, 'renderGLFM');
fetchMarkdownSpy = jest.spyOn(MarkdownDrawer.methods, 'fetchMarkdown');
global.document.querySelector = jest.fn(() => ({
- getBoundingClientRect: jest.fn(() => ({ bottom: 100 })),
dataset: {
page: 'test',
},
}));
+ contentTop.mockReturnValue(100);
createComponent();
await nextTick();
});
@@ -118,12 +121,28 @@ describe('MarkdownDrawer', () => {
expect(fetchMarkdownSpy).toHaveBeenCalledTimes(2);
});
- it('for open triggers renderGLFM', async () => {
+ it('triggers renderGLFM in openDrawer', async () => {
wrapper.vm.fetchMarkdown();
wrapper.vm.openDrawer();
await nextTick();
expect(renderGLFMSpy).toHaveBeenCalled();
});
+
+ it('triggers height calculation in openDrawer', async () => {
+ expect(findDrawer().attributes('headerheight')).toBe(`${0}px`);
+ wrapper.vm.fetchMarkdown();
+ wrapper.vm.openDrawer();
+ await nextTick();
+ expect(findDrawer().attributes('headerheight')).toBe(`${100}px`);
+ });
+
+ it('triggers height calculation in toggleDrawer', async () => {
+ expect(findDrawer().attributes('headerheight')).toBe(`${0}px`);
+ wrapper.vm.fetchMarkdown();
+ wrapper.vm.toggleDrawer();
+ await nextTick();
+ expect(findDrawer().attributes('headerheight')).toBe(`${100}px`);
+ });
});
describe('Markdown fetching', () => {
diff --git a/spec/frontend/vue_shared/components/markdown_drawer/utils/fetch_spec.js b/spec/frontend/vue_shared/components/markdown_drawer/utils/fetch_spec.js
index ff07b2cf838..adcf57b76a4 100644
--- a/spec/frontend/vue_shared/components/markdown_drawer/utils/fetch_spec.js
+++ b/spec/frontend/vue_shared/components/markdown_drawer/utils/fetch_spec.js
@@ -20,9 +20,9 @@ describe('utils/fetch', () => {
});
describe.each`
- axiosMock | type | toExpect
- ${{ code: 200, res: { html: MOCK_HTML } }} | ${'success'} | ${MOCK_DRAWER_DATA}
- ${{ code: 500, res: null }} | ${'error'} | ${MOCK_DRAWER_DATA_ERROR}
+ axiosMock | type | toExpect
+ ${{ code: 200, res: MOCK_HTML }} | ${'success'} | ${MOCK_DRAWER_DATA}
+ ${{ code: 500, res: null }} | ${'error'} | ${MOCK_DRAWER_DATA_ERROR}
`('process markdown data', ({ axiosMock, type, toExpect }) => {
describe(`if api fetch responds with ${type}`, () => {
beforeEach(() => {
diff --git a/spec/frontend/vue_shared/components/notes/system_note_spec.js b/spec/frontend/vue_shared/components/notes/system_note_spec.js
index 98b04ede943..559f9bcb1a8 100644
--- a/spec/frontend/vue_shared/components/notes/system_note_spec.js
+++ b/spec/frontend/vue_shared/components/notes/system_note_spec.js
@@ -1,10 +1,12 @@
import MockAdapter from 'axios-mock-adapter';
import { mount } from '@vue/test-utils';
-import $ from 'jquery';
import waitForPromises from 'helpers/wait_for_promises';
import createStore from '~/notes/stores';
import IssueSystemNote from '~/vue_shared/components/notes/system_note.vue';
import axios from '~/lib/utils/axios_utils';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
+
+jest.mock('~/behaviors/markdown/render_gfm');
describe('system note component', () => {
let vm;
@@ -75,11 +77,9 @@ describe('system note component', () => {
});
it('should renderGFM onMount', () => {
- const renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
-
createComponent(props);
- expect(renderGFMSpy).toHaveBeenCalled();
+ expect(renderGFM).toHaveBeenCalled();
});
it('renders outdated code lines', async () => {
diff --git a/spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/mocks/items_filters.json b/spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/mocks/items_filters.json
index b42ec42d8b8..e5678c9a956 100644
--- a/spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/mocks/items_filters.json
+++ b/spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/mocks/items_filters.json
@@ -1,14 +1,20 @@
[
- {
- "type": "assignee_username",
- "value": { "data": "root2" }
- },
- {
- "type": "author_username",
- "value": { "data": "root" }
- },
- {
- "type": "filtered-search-term",
- "value": { "data": "bar" }
+ {
+ "type": "assignee",
+ "value": {
+ "data": "root2"
}
- ] \ No newline at end of file
+ },
+ {
+ "type": "author",
+ "value": {
+ "data": "root"
+ }
+ },
+ {
+ "type": "filtered-search-term",
+ "value": {
+ "data": "bar"
+ }
+ }
+] \ No newline at end of file
diff --git a/spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs_spec.js b/spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs_spec.js
index c0c3c4a9729..86a63db0d9e 100644
--- a/spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs_spec.js
+++ b/spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs_spec.js
@@ -2,9 +2,15 @@ import { GlAlert, GlBadge, GlPagination, GlTabs, GlTab } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import Tracking from '~/tracking';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
+import {
+ OPERATORS_IS,
+ TOKEN_TITLE_ASSIGNEE,
+ TOKEN_TITLE_AUTHOR,
+ TOKEN_TYPE_ASSIGNEE,
+ TOKEN_TYPE_AUTHOR,
+} from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
-import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
+import UserToken from '~/vue_shared/components/filtered_search_bar/tokens/user_token.vue';
import PageWrapper from '~/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue';
import mockItems from './mocks/items.json';
import mockFilters from './mocks/items_filters.json';
@@ -166,7 +172,7 @@ describe('AlertManagementEmptyState', () => {
it('renders the filter set with the tokens according to the prop filterSearchTokens', () => {
mountComponent({
- props: { filterSearchTokens: ['assignee_username'] },
+ props: { filterSearchTokens: [TOKEN_TYPE_ASSIGNEE] },
});
expect(Filters().exists()).toBe(true);
@@ -287,26 +293,26 @@ describe('AlertManagementEmptyState', () => {
expect(Filters().props('searchInputPlaceholder')).toBe('Search or filter results…');
expect(Filters().props('tokens')).toEqual([
{
- type: 'author_username',
+ type: TOKEN_TYPE_AUTHOR,
icon: 'user',
- title: 'Author',
+ title: TOKEN_TITLE_AUTHOR,
unique: true,
symbol: '@',
- token: AuthorToken,
- operators: OPERATOR_IS_ONLY,
+ token: UserToken,
+ operators: OPERATORS_IS,
fetchPath: '/link',
- fetchAuthors: expect.any(Function),
+ fetchUsers: expect.any(Function),
},
{
- type: 'assignee_username',
+ type: TOKEN_TYPE_ASSIGNEE,
icon: 'user',
- title: 'Assignee',
+ title: TOKEN_TITLE_ASSIGNEE,
unique: true,
symbol: '@',
- token: AuthorToken,
- operators: OPERATOR_IS_ONLY,
+ token: UserToken,
+ operators: OPERATORS_IS,
fetchPath: '/link',
- fetchAuthors: expect.any(Function),
+ fetchUsers: expect.any(Function),
},
]);
expect(Filters().props('recentSearchesStorageKey')).toBe('items');
diff --git a/spec/frontend/vue_shared/components/registry/registry_search_spec.js b/spec/frontend/vue_shared/components/registry/registry_search_spec.js
index fa7fabfaef6..591447a37c2 100644
--- a/spec/frontend/vue_shared/components/registry/registry_search_spec.js
+++ b/spec/frontend/vue_shared/components/registry/registry_search_spec.js
@@ -1,6 +1,6 @@
import { GlSorting, GlSortingItem, GlFilteredSearch } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import component from '~/vue_shared/components/registry/registry_search.vue';
describe('Registry Search', () => {
diff --git a/spec/frontend/vue_shared/components/runner_instructions/mock_data.js b/spec/frontend/vue_shared/components/runner_instructions/mock_data.js
index bc1545014d7..79cacadd6af 100644
--- a/spec/frontend/vue_shared/components/runner_instructions/mock_data.js
+++ b/spec/frontend/vue_shared/components/runner_instructions/mock_data.js
@@ -1,119 +1,5 @@
-export const mockGraphqlRunnerPlatforms = {
- data: {
- runnerPlatforms: {
- nodes: [
- {
- name: 'linux',
- humanReadableName: 'Linux',
- architectures: {
- nodes: [
- {
- name: 'amd64',
- downloadLocation:
- 'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64',
- __typename: 'RunnerArchitecture',
- },
- {
- name: '386',
- downloadLocation:
- 'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386',
- __typename: 'RunnerArchitecture',
- },
- {
- name: 'arm',
- downloadLocation:
- 'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm',
- __typename: 'RunnerArchitecture',
- },
- {
- name: 'arm64',
- downloadLocation:
- 'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm64',
- __typename: 'RunnerArchitecture',
- },
- ],
- __typename: 'RunnerArchitectureConnection',
- },
- __typename: 'RunnerPlatform',
- },
- {
- name: 'osx',
- humanReadableName: 'macOS',
- architectures: {
- nodes: [
- {
- name: 'amd64',
- downloadLocation:
- 'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64',
- __typename: 'RunnerArchitecture',
- },
- ],
- __typename: 'RunnerArchitectureConnection',
- },
- __typename: 'RunnerPlatform',
- },
- {
- name: 'windows',
- humanReadableName: 'Windows',
- architectures: {
- nodes: [
- {
- name: 'amd64',
- downloadLocation:
- 'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-amd64.exe',
- __typename: 'RunnerArchitecture',
- },
- {
- name: '386',
- downloadLocation:
- 'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-386.exe',
- __typename: 'RunnerArchitecture',
- },
- ],
- __typename: 'RunnerArchitectureConnection',
- },
- __typename: 'RunnerPlatform',
- },
- {
- name: 'docker',
- humanReadableName: 'Docker',
- architectures: null,
- __typename: 'RunnerPlatform',
- },
- {
- name: 'kubernetes',
- humanReadableName: 'Kubernetes',
- architectures: null,
- __typename: 'RunnerPlatform',
- },
- ],
- __typename: 'RunnerPlatformConnection',
- },
- project: { id: 'gid://gitlab/Project/1', __typename: 'Project' },
- group: null,
- },
-};
+import mockGraphqlRunnerPlatforms from 'test_fixtures/graphql/runner_instructions/get_runner_platforms.query.graphql.json';
+import mockGraphqlInstructions from 'test_fixtures/graphql/runner_instructions/get_runner_setup.query.graphql.json';
+import mockGraphqlInstructionsWindows from 'test_fixtures/graphql/runner_instructions/get_runner_setup.query.graphql.windows.json';
-export const mockGraphqlInstructions = {
- data: {
- runnerSetup: {
- installInstructions:
- '# Install and run as service\nsudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner\nsudo gitlab-runner start',
- registerInstructions:
- 'sudo gitlab-runner register --url http://gdk.test:3000/ --registration-token $REGISTRATION_TOKEN',
- __typename: 'RunnerSetup',
- },
- },
-};
-
-export const mockGraphqlInstructionsWindows = {
- data: {
- runnerSetup: {
- installInstructions:
- '# Windows runner, then run\n.gitlab-runner.exe install\n.gitlab-runner.exe start',
- registerInstructions:
- './gitlab-runner.exe register --url http://gdk.test:3000/ --registration-token $REGISTRATION_TOKEN',
- __typename: 'RunnerSetup',
- },
- },
-};
+export { mockGraphqlRunnerPlatforms, mockGraphqlInstructions, mockGraphqlInstructionsWindows };
diff --git a/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js b/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js
index 7c5fc63856a..ae9157591c5 100644
--- a/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js
+++ b/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js
@@ -113,10 +113,7 @@ describe('RunnerInstructionsModal component', () => {
});
describe('should display default instructions', () => {
- const {
- installInstructions,
- registerInstructions,
- } = mockGraphqlInstructions.data.runnerSetup;
+ const { installInstructions } = mockGraphqlInstructions.data.runnerSetup;
it('runner instructions are requested', () => {
expect(runnerSetupInstructionsHandler).toHaveBeenCalledWith({
@@ -128,53 +125,16 @@ describe('RunnerInstructionsModal component', () => {
it('binary instructions are shown', async () => {
const instructions = findBinaryInstructions().text();
- expect(instructions).toBe(installInstructions);
+ expect(instructions).toBe(installInstructions.trim());
});
it('register command is shown with a replaced token', async () => {
const command = findRegisterCommand().text();
expect(command).toBe(
- 'sudo gitlab-runner register --url http://gdk.test:3000/ --registration-token MY_TOKEN',
+ 'sudo gitlab-runner register --url http://localhost/ --registration-token MY_TOKEN',
);
});
-
- describe('when a register token is not shown', () => {
- beforeEach(async () => {
- createComponent({ props: { registrationToken: undefined } });
- await waitForPromises();
- });
-
- it('register command is shown without a defined registration token', () => {
- const instructions = findRegisterCommand().text();
-
- expect(instructions).toBe(registerInstructions);
- });
- });
-
- describe('when providing a defaultPlatformName', () => {
- beforeEach(async () => {
- createComponent({ props: { defaultPlatformName: 'osx' } });
- await waitForPromises();
- });
-
- it('runner instructions for the default selected platform are requested', () => {
- expect(runnerSetupInstructionsHandler).toHaveBeenCalledWith({
- platform: 'osx',
- architecture: 'amd64',
- });
- });
-
- it('sets the focus on the default selected platform', () => {
- const findOsxPlatformButton = () => wrapper.findComponent({ ref: 'osx' });
-
- findOsxPlatformButton().element.focus = jest.fn();
-
- findModal().vm.$emit('shown');
-
- expect(findOsxPlatformButton().element.focus).toHaveBeenCalled();
- });
- });
});
describe('after a platform and architecture are selected', () => {
@@ -207,14 +167,14 @@ describe('RunnerInstructionsModal component', () => {
it('other binary instructions are shown', () => {
const instructions = findBinaryInstructions().text();
- expect(instructions).toBe(installInstructions);
+ expect(instructions).toBe(installInstructions.trim());
});
it('register command is shown', () => {
const command = findRegisterCommand().text();
expect(command).toBe(
- './gitlab-runner.exe register --url http://gdk.test:3000/ --registration-token MY_TOKEN',
+ './gitlab-runner.exe register --url http://localhost/ --registration-token MY_TOKEN',
);
});
@@ -246,6 +206,43 @@ describe('RunnerInstructionsModal component', () => {
});
});
+ describe('when a register token is not known', () => {
+ beforeEach(async () => {
+ createComponent({ props: { registrationToken: undefined } });
+ await waitForPromises();
+ });
+
+ it('register command is shown without a defined registration token', () => {
+ const instructions = findRegisterCommand().text();
+
+ expect(instructions).toBe(mockGraphqlInstructions.data.runnerSetup.registerInstructions);
+ });
+ });
+
+ describe('with a defaultPlatformName', () => {
+ beforeEach(async () => {
+ createComponent({ props: { defaultPlatformName: 'osx' } });
+ await waitForPromises();
+ });
+
+ it('runner instructions for the default selected platform are requested', () => {
+ expect(runnerSetupInstructionsHandler).toHaveBeenLastCalledWith({
+ platform: 'osx',
+ architecture: 'amd64',
+ });
+ });
+
+ it('sets the focus on the default selected platform', () => {
+ const findOsxPlatformButton = () => wrapper.findComponent({ ref: 'osx' });
+
+ findOsxPlatformButton().element.focus = jest.fn();
+
+ findModal().vm.$emit('shown');
+
+ expect(findOsxPlatformButton().element.focus).toHaveBeenCalled();
+ });
+ });
+
describe('when the modal is not shown', () => {
beforeEach(async () => {
createComponent({ shown: false });
diff --git a/spec/frontend/vue_shared/components/sidebar/copyable_field_spec.js b/spec/frontend/vue_shared/components/sidebar/copyable_field_spec.js
deleted file mode 100644
index 3980033862e..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/copyable_field_spec.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import { GlLoadingIcon, GlSprintf } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import CopyableField from '~/vue_shared/components/sidebar/copyable_field.vue';
-
-describe('SidebarCopyableField', () => {
- let wrapper;
-
- const defaultProps = {
- value: 'Gl-1',
- name: 'Reference',
- };
-
- const createComponent = (propsData = defaultProps) => {
- wrapper = shallowMount(CopyableField, {
- propsData,
- stubs: {
- GlSprintf,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findClipboardButton = () => wrapper.findComponent(ClipboardButton);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
-
- describe('template', () => {
- describe('when `isLoading` prop is `false`', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders copyable field', () => {
- expect(wrapper.text()).toContain('Reference: Gl-1');
- });
-
- it('renders ClipboardButton with correct props', () => {
- const clipboardButton = findClipboardButton();
-
- expect(clipboardButton.exists()).toBe(true);
- expect(clipboardButton.props('title')).toBe(`Copy ${defaultProps.name}`);
- expect(clipboardButton.props('text')).toBe(defaultProps.value);
- });
-
- it('does not render loading icon', () => {
- expect(findLoadingIcon().exists()).toBe(false);
- });
- });
-
- describe('when `isLoading` prop is `true`', () => {
- beforeEach(() => {
- createComponent({ ...defaultProps, isLoading: true });
- });
-
- it('renders loading icon', () => {
- expect(findLoadingIcon().exists()).toBe(true);
- expect(findLoadingIcon().props('label')).toBe('Loading Reference');
- });
-
- it('does not render clipboard button', () => {
- expect(findClipboardButton().exists()).toBe(false);
- });
- });
-
- describe('with `clipboardTooltipText` prop', () => {
- it('sets ClipboardButton `title` prop to `clipboardTooltipText` value', () => {
- const mockClipboardTooltipText = 'Copy my custom value';
- createComponent({ ...defaultProps, clipboardTooltipText: mockClipboardTooltipText });
-
- expect(findClipboardButton().props('title')).toBe(mockClipboardTooltipText);
- });
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js b/spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js
deleted file mode 100644
index d531147c0e6..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js
+++ /dev/null
@@ -1,388 +0,0 @@
-import {
- GlIcon,
- GlLoadingIcon,
- GlDropdown,
- GlDropdownForm,
- GlDropdownItem,
- GlSearchBoxByType,
- GlButton,
-} from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-
-import { nextTick } from 'vue';
-import axios from '~/lib/utils/axios_utils';
-import IssuableMoveDropdown from '~/vue_shared/components/sidebar/issuable_move_dropdown.vue';
-
-const mockProjects = [
- {
- id: 2,
- name_with_namespace: 'Gitlab Org / Gitlab Shell',
- full_path: 'gitlab-org/gitlab-shell',
- },
- {
- id: 3,
- name_with_namespace: 'Gnuwget / Wget2',
- full_path: 'gnuwget/wget2',
- },
- {
- id: 4,
- name_with_namespace: 'Commit451 / Lab Coat',
- full_path: 'Commit451/lab-coat',
- },
-];
-
-const mockProps = {
- projectsFetchPath: '/-/autocomplete/projects?project_id=1',
- dropdownButtonTitle: 'Move issuable',
- dropdownHeaderTitle: 'Move issuable',
- moveInProgress: false,
- disabled: false,
-};
-
-const mockEvent = {
- stopPropagation: jest.fn(),
- preventDefault: jest.fn(),
-};
-
-describe('IssuableMoveDropdown', () => {
- let mock;
- let wrapper;
-
- const createComponent = (propsData = mockProps) => {
- wrapper = shallowMount(IssuableMoveDropdown, {
- propsData,
- });
- wrapper.vm.$refs.dropdown.hide = jest.fn();
- wrapper.vm.$refs.searchInput.focusInput = jest.fn();
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- mock.restore();
- });
-
- describe('watch', () => {
- describe('searchKey', () => {
- it('calls `fetchProjects` with value of the prop', async () => {
- jest.spyOn(wrapper.vm, 'fetchProjects');
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- searchKey: 'foo',
- });
-
- await nextTick();
-
- expect(wrapper.vm.fetchProjects).toHaveBeenCalledWith('foo');
- });
- });
- });
-
- describe('methods', () => {
- describe('fetchProjects', () => {
- it('sets projectsListLoading to true and projectsListLoadFailed to false', () => {
- wrapper.vm.fetchProjects();
-
- expect(wrapper.vm.projectsListLoading).toBe(true);
- expect(wrapper.vm.projectsListLoadFailed).toBe(false);
- });
-
- it('calls `axios.get` with `projectsFetchPath` and query param `search`', () => {
- jest.spyOn(axios, 'get').mockResolvedValue({
- data: mockProjects,
- });
-
- wrapper.vm.fetchProjects('foo');
-
- expect(axios.get).toHaveBeenCalledWith(
- mockProps.projectsFetchPath,
- expect.objectContaining({
- params: {
- search: 'foo',
- },
- }),
- );
- });
-
- it('sets response to `projects` and focuses on searchInput when request is successful', async () => {
- jest.spyOn(axios, 'get').mockResolvedValue({
- data: mockProjects,
- });
-
- await wrapper.vm.fetchProjects('foo');
-
- expect(wrapper.vm.projects).toBe(mockProjects);
- expect(wrapper.vm.$refs.searchInput.focusInput).toHaveBeenCalled();
- });
-
- it('sets projectsListLoadFailed to true when request fails', async () => {
- jest.spyOn(axios, 'get').mockRejectedValue({});
-
- await wrapper.vm.fetchProjects('foo');
-
- expect(wrapper.vm.projectsListLoadFailed).toBe(true);
- });
-
- it('sets projectsListLoading to false when request completes', async () => {
- jest.spyOn(axios, 'get').mockResolvedValue({
- data: mockProjects,
- });
-
- await wrapper.vm.fetchProjects('foo');
-
- expect(wrapper.vm.projectsListLoading).toBe(false);
- });
- });
-
- describe('isSelectedProject', () => {
- it.each`
- project | selectedProject | title | returnValue
- ${mockProjects[0]} | ${mockProjects[0]} | ${'are same projects'} | ${true}
- ${mockProjects[0]} | ${mockProjects[1]} | ${'are different projects'} | ${false}
- `(
- 'returns $returnValue when selectedProject and provided project param $title',
- async ({ project, selectedProject, returnValue }) => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- selectedProject,
- });
-
- await nextTick();
-
- expect(wrapper.vm.isSelectedProject(project)).toBe(returnValue);
- },
- );
-
- it('returns false when selectedProject is null', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- selectedProject: null,
- });
-
- await nextTick();
-
- expect(wrapper.vm.isSelectedProject(mockProjects[0])).toBe(false);
- });
- });
- });
-
- describe('template', () => {
- const findDropdownEl = () => wrapper.findComponent(GlDropdown);
-
- it('renders collapsed state element with icon', () => {
- const collapsedEl = wrapper.find('[data-testid="move-collapsed"]');
-
- expect(collapsedEl.exists()).toBe(true);
- expect(collapsedEl.attributes('title')).toBe(mockProps.dropdownButtonTitle);
- expect(collapsedEl.findComponent(GlIcon).exists()).toBe(true);
- expect(collapsedEl.findComponent(GlIcon).props('name')).toBe('arrow-right');
- });
-
- describe('gl-dropdown component', () => {
- it('renders component container element', () => {
- expect(findDropdownEl().exists()).toBe(true);
- expect(findDropdownEl().props('block')).toBe(true);
- });
-
- it('renders gl-dropdown-form component', () => {
- expect(findDropdownEl().findComponent(GlDropdownForm).exists()).toBe(true);
- });
-
- it('renders disabled dropdown when `disabled` is true', () => {
- createComponent({ ...mockProps, disabled: true });
-
- expect(findDropdownEl().attributes('disabled')).toBe('true');
- });
-
- it('renders header element', () => {
- const headerEl = findDropdownEl().find('[data-testid="header"]');
-
- expect(headerEl.exists()).toBe(true);
- expect(headerEl.find('span').text()).toBe(mockProps.dropdownHeaderTitle);
- expect(headerEl.findComponent(GlButton).props('icon')).toBe('close');
- });
-
- it('renders gl-search-box-by-type component', () => {
- const searchEl = findDropdownEl().findComponent(GlSearchBoxByType);
-
- expect(searchEl.exists()).toBe(true);
- expect(searchEl.attributes()).toMatchObject({
- placeholder: 'Search project',
- debounce: '300',
- });
- });
-
- it('renders gl-loading-icon component when projectsListLoading prop is true', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- projectsListLoading: true,
- });
-
- await nextTick();
-
- expect(findDropdownEl().findComponent(GlLoadingIcon).exists()).toBe(true);
- });
-
- it('renders gl-dropdown-item components for available projects', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- projects: mockProjects,
- selectedProject: mockProjects[0],
- });
-
- await nextTick();
-
- const dropdownItems = wrapper.findAllComponents(GlDropdownItem);
-
- expect(dropdownItems).toHaveLength(mockProjects.length);
- expect(dropdownItems.at(0).props()).toMatchObject({
- isCheckItem: true,
- isChecked: true,
- });
- expect(dropdownItems.at(0).text()).toBe(mockProjects[0].name_with_namespace);
- });
-
- it('renders string "No matching results" when search does not yield any matches', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- searchKey: 'foo',
- });
-
- // Wait for `searchKey` watcher to run.
- await nextTick();
-
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- projects: [],
- projectsListLoading: false,
- });
-
- await nextTick();
-
- const dropdownContentEl = wrapper.find('[data-testid="content"]');
-
- expect(dropdownContentEl.text()).toContain('No matching results');
- });
-
- it('renders string "Failed to load projects" when loading projects list fails', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- projects: [],
- projectsListLoading: false,
- projectsListLoadFailed: true,
- });
-
- await nextTick();
-
- const dropdownContentEl = wrapper.find('[data-testid="content"]');
-
- expect(dropdownContentEl.text()).toContain('Failed to load projects');
- });
-
- it('renders gl-button within footer', async () => {
- const moveButtonEl = wrapper.find('[data-testid="footer"]').findComponent(GlButton);
-
- expect(moveButtonEl.text()).toBe('Move');
- expect(moveButtonEl.attributes('disabled')).toBe('true');
-
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- selectedProject: mockProjects[0],
- });
-
- await nextTick();
-
- expect(
- wrapper.find('[data-testid="footer"]').findComponent(GlButton).attributes('disabled'),
- ).not.toBeDefined();
- });
- });
-
- describe('events', () => {
- it('collapsed state element emits `toggle-collapse` event on component when clicked', () => {
- wrapper.find('[data-testid="move-collapsed"]').trigger('click');
-
- expect(wrapper.emitted('toggle-collapse')).toHaveLength(1);
- });
-
- it('gl-dropdown component calls `fetchProjects` on `shown` event', () => {
- jest.spyOn(axios, 'get').mockResolvedValue({
- data: mockProjects,
- });
-
- findDropdownEl().vm.$emit('shown');
-
- expect(axios.get).toHaveBeenCalled();
- });
-
- it('gl-dropdown component prevents dropdown body from closing on `hide` event when `projectItemClick` prop is true', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- projectItemClick: true,
- });
-
- findDropdownEl().vm.$emit('hide', mockEvent);
-
- expect(mockEvent.preventDefault).toHaveBeenCalled();
- expect(wrapper.vm.projectItemClick).toBe(false);
- });
-
- it('gl-dropdown component emits `dropdown-close` event on component from `hide` event', async () => {
- findDropdownEl().vm.$emit('hide');
-
- expect(wrapper.emitted('dropdown-close')).toHaveLength(1);
- });
-
- it('close icon in dropdown header closes the dropdown when clicked', () => {
- wrapper.find('[data-testid="header"]').findComponent(GlButton).vm.$emit('click', mockEvent);
-
- expect(wrapper.vm.$refs.dropdown.hide).toHaveBeenCalled();
- });
-
- it('sets project for clicked gl-dropdown-item to selectedProject', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- projects: mockProjects,
- });
-
- await nextTick();
-
- wrapper.findAllComponents(GlDropdownItem).at(0).vm.$emit('click', mockEvent);
-
- expect(wrapper.vm.selectedProject).toBe(mockProjects[0]);
- });
-
- it('hides dropdown and emits `move-issuable` event when move button is clicked', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- selectedProject: mockProjects[0],
- });
-
- await nextTick();
-
- wrapper.find('[data-testid="footer"]').findComponent(GlButton).vm.$emit('click');
-
- expect(wrapper.vm.$refs.dropdown.hide).toHaveBeenCalled();
- expect(wrapper.emitted('move-issuable')).toHaveLength(1);
- expect(wrapper.emitted('move-issuable')[0]).toEqual([mockProjects[0]]);
- });
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_button_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_button_spec.js
deleted file mode 100644
index c0e5408e1bd..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_button_spec.js
+++ /dev/null
@@ -1,89 +0,0 @@
-import { GlIcon, GlButton } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-import Vuex from 'vuex';
-
-import DropdownButton from '~/vue_shared/components/sidebar/labels_select_vue/dropdown_button.vue';
-
-import labelSelectModule from '~/vue_shared/components/sidebar/labels_select_vue/store';
-
-import { mockConfig } from './mock_data';
-
-let store;
-Vue.use(Vuex);
-
-const createComponent = (initialState = mockConfig) => {
- store = new Vuex.Store(labelSelectModule());
-
- store.dispatch('setInitialState', initialState);
-
- return shallowMount(DropdownButton, {
- store,
- });
-};
-
-describe('DropdownButton', () => {
- let wrapper;
-
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findDropdownButton = () => wrapper.findComponent(GlButton);
- const findDropdownText = () => wrapper.find('.dropdown-toggle-text');
- const findDropdownIcon = () => wrapper.findComponent(GlIcon);
-
- describe('methods', () => {
- describe('handleButtonClick', () => {
- it.each`
- variant | expectPropagationStopped
- ${'standalone'} | ${true}
- ${'embedded'} | ${false}
- `(
- 'toggles dropdown content and handles event propagation when `state.variant` is "$variant"',
- ({ variant, expectPropagationStopped }) => {
- const event = { stopPropagation: jest.fn() };
-
- wrapper = createComponent({ ...mockConfig, variant });
-
- findDropdownButton().vm.$emit('click', event);
-
- expect(store.state.showDropdownContents).toBe(true);
- expect(event.stopPropagation).toHaveBeenCalledTimes(expectPropagationStopped ? 1 : 0);
- },
- );
- });
- });
-
- describe('template', () => {
- it('renders component container element', () => {
- expect(wrapper.findComponent(GlButton).element).toBe(wrapper.element);
- });
-
- it('renders default button text element', () => {
- const dropdownTextEl = findDropdownText();
-
- expect(dropdownTextEl.exists()).toBe(true);
- expect(dropdownTextEl.text()).toBe('Label');
- });
-
- it('renders provided button text element', async () => {
- store.state.dropdownButtonText = 'Custom label';
- const dropdownTextEl = findDropdownText();
-
- await nextTick();
- expect(dropdownTextEl.text()).toBe('Custom label');
- });
-
- it('renders chevron icon element', () => {
- const iconEl = findDropdownIcon();
-
- expect(iconEl.exists()).toBe(true);
- expect(iconEl.props('name')).toBe('chevron-down');
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view_spec.js
deleted file mode 100644
index 799e2c1d08e..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view_spec.js
+++ /dev/null
@@ -1,211 +0,0 @@
-import { GlButton, GlFormInput, GlLink, GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-import Vuex from 'vuex';
-
-import DropdownContentsCreateView from '~/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue';
-
-import labelSelectModule from '~/vue_shared/components/sidebar/labels_select_vue/store';
-
-import { mockConfig, mockSuggestedColors } from './mock_data';
-
-Vue.use(Vuex);
-
-const createComponent = (initialState = mockConfig) => {
- const store = new Vuex.Store(labelSelectModule());
-
- store.dispatch('setInitialState', initialState);
-
- return shallowMount(DropdownContentsCreateView, {
- store,
- });
-};
-
-describe('DropdownContentsCreateView', () => {
- let wrapper;
- const colors = Object.keys(mockSuggestedColors).map((color) => ({
- [color]: mockSuggestedColors[color],
- }));
-
- beforeEach(() => {
- gon.suggested_label_colors = mockSuggestedColors;
- wrapper = createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('computed', () => {
- describe('disableCreate', () => {
- it('returns `true` when label title and color is not defined', () => {
- expect(wrapper.vm.disableCreate).toBe(true);
- });
-
- it('returns `true` when `labelCreateInProgress` is true', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- labelTitle: 'Foo',
- selectedColor: '#ff0000',
- });
- wrapper.vm.$store.dispatch('requestCreateLabel');
-
- await nextTick();
- expect(wrapper.vm.disableCreate).toBe(true);
- });
-
- it('returns `false` when label title and color is defined and create request is not already in progress', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- labelTitle: 'Foo',
- selectedColor: '#ff0000',
- });
-
- await nextTick();
- expect(wrapper.vm.disableCreate).toBe(false);
- });
- });
-
- describe('suggestedColors', () => {
- it('returns array of color objects containing color code and name', () => {
- colors.forEach((color, index) => {
- expect(wrapper.vm.suggestedColors[index]).toEqual(expect.objectContaining(color));
- });
- });
- });
- });
-
- describe('methods', () => {
- describe('getColorCode', () => {
- it('returns color code from color object', () => {
- expect(wrapper.vm.getColorCode(colors[0])).toBe(Object.keys(colors[0]).pop());
- });
- });
-
- describe('getColorName', () => {
- it('returns color name from color object', () => {
- expect(wrapper.vm.getColorName(colors[0])).toBe(Object.values(colors[0]).pop());
- });
- });
-
- describe('handleColorClick', () => {
- it('sets provided `color` param to `selectedColor` prop', () => {
- wrapper.vm.handleColorClick(colors[0]);
-
- expect(wrapper.vm.selectedColor).toBe(Object.keys(colors[0]).pop());
- });
- });
-
- describe('handleCreateClick', () => {
- it('calls action `createLabel` with object containing `labelTitle` & `selectedColor`', async () => {
- jest.spyOn(wrapper.vm, 'createLabel').mockImplementation();
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- labelTitle: 'Foo',
- selectedColor: '#ff0000',
- });
-
- wrapper.vm.handleCreateClick();
-
- await nextTick();
- expect(wrapper.vm.createLabel).toHaveBeenCalledWith(
- expect.objectContaining({
- title: 'Foo',
- color: '#ff0000',
- }),
- );
- });
- });
- });
-
- describe('template', () => {
- it('renders component container element with class "labels-select-contents-create"', () => {
- expect(wrapper.attributes('class')).toContain('labels-select-contents-create');
- });
-
- it('renders dropdown back button element', () => {
- const backBtnEl = wrapper.find('.dropdown-title').findAllComponents(GlButton).at(0);
-
- expect(backBtnEl.exists()).toBe(true);
- expect(backBtnEl.attributes('aria-label')).toBe('Go back');
- expect(backBtnEl.props('icon')).toBe('arrow-left');
- });
-
- it('renders dropdown title element', () => {
- const headerEl = wrapper.find('.dropdown-title > span');
-
- expect(headerEl.exists()).toBe(true);
- expect(headerEl.text()).toBe('Create label');
- });
-
- it('renders dropdown close button element', () => {
- const closeBtnEl = wrapper.find('.dropdown-title').findAllComponents(GlButton).at(1);
-
- expect(closeBtnEl.exists()).toBe(true);
- expect(closeBtnEl.attributes('aria-label')).toBe('Close');
- expect(closeBtnEl.props('icon')).toBe('close');
- });
-
- it('renders label title input element', () => {
- const titleInputEl = wrapper.find('.dropdown-input').findComponent(GlFormInput);
-
- expect(titleInputEl.exists()).toBe(true);
- expect(titleInputEl.attributes('placeholder')).toBe('Name new label');
- expect(titleInputEl.attributes('autofocus')).toBe('true');
- });
-
- it('renders color block element for all suggested colors', () => {
- const colorBlocksEl = wrapper.find('.dropdown-content').findAllComponents(GlLink);
-
- colorBlocksEl.wrappers.forEach((colorBlock, index) => {
- expect(colorBlock.attributes('style')).toContain('background-color');
- expect(colorBlock.attributes('title')).toBe(Object.values(colors[index]).pop());
- });
- });
-
- it('renders color input element', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- selectedColor: '#ff0000',
- });
-
- await nextTick();
- const colorPreviewEl = wrapper.find('.color-input-container > .dropdown-label-color-preview');
- const colorInputEl = wrapper.find('.color-input-container').findComponent(GlFormInput);
-
- expect(colorPreviewEl.exists()).toBe(true);
- expect(colorPreviewEl.attributes('style')).toContain('background-color');
- expect(colorInputEl.exists()).toBe(true);
- expect(colorInputEl.attributes('placeholder')).toBe('Use custom color #FF0000');
- expect(colorInputEl.attributes('value')).toBe('#ff0000');
- });
-
- it('renders create button element', () => {
- const createBtnEl = wrapper.find('.dropdown-actions').findAllComponents(GlButton).at(0);
-
- expect(createBtnEl.exists()).toBe(true);
- expect(createBtnEl.text()).toContain('Create');
- });
-
- it('shows gl-loading-icon within create button element when `labelCreateInProgress` is `true`', async () => {
- wrapper.vm.$store.dispatch('requestCreateLabel');
-
- await nextTick();
- const loadingIconEl = wrapper.find('.dropdown-actions').findComponent(GlLoadingIcon);
-
- expect(loadingIconEl.exists()).toBe(true);
- expect(loadingIconEl.isVisible()).toBe(true);
- });
-
- it('renders cancel button element', () => {
- const cancelBtnEl = wrapper.find('.dropdown-actions').findAllComponents(GlButton).at(1);
-
- expect(cancelBtnEl.exists()).toBe(true);
- expect(cancelBtnEl.text()).toContain('Cancel');
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js
deleted file mode 100644
index cc9b9f393ce..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js
+++ /dev/null
@@ -1,413 +0,0 @@
-import {
- GlIntersectionObserver,
- GlButton,
- GlLoadingIcon,
- GlSearchBoxByType,
- GlLink,
-} from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-import Vuex from 'vuex';
-import { UP_KEY_CODE, DOWN_KEY_CODE, ENTER_KEY_CODE, ESC_KEY_CODE } from '~/lib/utils/keycodes';
-import DropdownContentsLabelsView from '~/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue';
-import LabelItem from '~/vue_shared/components/sidebar/labels_select_vue/label_item.vue';
-
-import * as actions from '~/vue_shared/components/sidebar/labels_select_vue/store/actions';
-import * as getters from '~/vue_shared/components/sidebar/labels_select_vue/store/getters';
-import mutations from '~/vue_shared/components/sidebar/labels_select_vue/store/mutations';
-import defaultState from '~/vue_shared/components/sidebar/labels_select_vue/store/state';
-
-import { mockConfig, mockLabels, mockRegularLabel } from './mock_data';
-
-Vue.use(Vuex);
-
-describe('DropdownContentsLabelsView', () => {
- let wrapper;
-
- const createComponent = (initialState = mockConfig) => {
- const store = new Vuex.Store({
- getters,
- mutations,
- state: {
- ...defaultState(),
- footerCreateLabelTitle: 'Create label',
- footerManageLabelTitle: 'Manage labels',
- },
- actions: {
- ...actions,
- fetchLabels: jest.fn(),
- },
- });
-
- store.dispatch('setInitialState', initialState);
- store.dispatch('receiveLabelsSuccess', mockLabels);
-
- wrapper = shallowMount(DropdownContentsLabelsView, {
- store,
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findDropdownContent = () => wrapper.find('[data-testid="dropdown-content"]');
- const findDropdownTitle = () => wrapper.find('[data-testid="dropdown-title"]');
- const findDropdownFooter = () => wrapper.find('[data-testid="dropdown-footer"]');
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
-
- describe('computed', () => {
- describe('visibleLabels', () => {
- it('returns matching labels filtered with `searchKey`', () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- searchKey: 'bug',
- });
-
- expect(wrapper.vm.visibleLabels.length).toBe(1);
- expect(wrapper.vm.visibleLabels[0].title).toBe('Bug');
- });
-
- it('returns matching labels with fuzzy filtering', () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- searchKey: 'bg',
- });
-
- expect(wrapper.vm.visibleLabels.length).toBe(2);
- expect(wrapper.vm.visibleLabels[0].title).toBe('Bug');
- expect(wrapper.vm.visibleLabels[1].title).toBe('Boog');
- });
-
- it('returns all labels when `searchKey` is empty', () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- searchKey: '',
- });
-
- expect(wrapper.vm.visibleLabels.length).toBe(mockLabels.length);
- });
- });
-
- describe('showNoMatchingResultsMessage', () => {
- it.each`
- searchKey | labels | labelsDescription | returnValue
- ${''} | ${[]} | ${'empty'} | ${false}
- ${'bug'} | ${[]} | ${'empty'} | ${true}
- ${''} | ${mockLabels} | ${'not empty'} | ${false}
- ${'bug'} | ${mockLabels} | ${'not empty'} | ${false}
- `(
- 'returns $returnValue when searchKey is "$searchKey" and visibleLabels is $labelsDescription',
- async ({ searchKey, labels, returnValue }) => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- searchKey,
- });
-
- wrapper.vm.$store.dispatch('receiveLabelsSuccess', labels);
-
- await nextTick();
-
- expect(wrapper.vm.showNoMatchingResultsMessage).toBe(returnValue);
- },
- );
- });
- });
-
- describe('methods', () => {
- const fakePreventDefault = jest.fn();
-
- describe('isLabelSelected', () => {
- it('returns true when provided `label` param is one of the selected labels', () => {
- expect(wrapper.vm.isLabelSelected(mockRegularLabel)).toBe(true);
- });
-
- it('returns false when provided `label` param is not one of the selected labels', () => {
- expect(wrapper.vm.isLabelSelected(mockLabels[1])).toBe(false);
- });
- });
-
- describe('handleComponentAppear', () => {
- it('calls `focusInput` on searchInput field', async () => {
- wrapper.vm.$refs.searchInput.focusInput = jest.fn();
-
- await wrapper.vm.handleComponentAppear();
-
- expect(wrapper.vm.$refs.searchInput.focusInput).toHaveBeenCalled();
- });
- });
-
- describe('handleComponentDisappear', () => {
- it('calls action `receiveLabelsSuccess` with empty array', () => {
- jest.spyOn(wrapper.vm, 'receiveLabelsSuccess');
-
- wrapper.vm.handleComponentDisappear();
-
- expect(wrapper.vm.receiveLabelsSuccess).toHaveBeenCalledWith([]);
- });
- });
-
- describe('handleCreateLabelClick', () => {
- it('calls actions `receiveLabelsSuccess` with empty array and `toggleDropdownContentsCreateView`', () => {
- jest.spyOn(wrapper.vm, 'receiveLabelsSuccess');
- jest.spyOn(wrapper.vm, 'toggleDropdownContentsCreateView');
-
- wrapper.vm.handleCreateLabelClick();
-
- expect(wrapper.vm.receiveLabelsSuccess).toHaveBeenCalledWith([]);
- expect(wrapper.vm.toggleDropdownContentsCreateView).toHaveBeenCalled();
- });
- });
-
- describe('handleKeyDown', () => {
- it('decreases `currentHighlightItem` value by 1 when Up arrow key is pressed', () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- currentHighlightItem: 1,
- });
-
- wrapper.vm.handleKeyDown({
- keyCode: UP_KEY_CODE,
- });
-
- expect(wrapper.vm.currentHighlightItem).toBe(0);
- });
-
- it('increases `currentHighlightItem` value by 1 when Down arrow key is pressed', () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- currentHighlightItem: 1,
- });
-
- wrapper.vm.handleKeyDown({
- keyCode: DOWN_KEY_CODE,
- });
-
- expect(wrapper.vm.currentHighlightItem).toBe(2);
- });
-
- it('resets the search text when the Enter key is pressed', () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- currentHighlightItem: 1,
- searchKey: 'bug',
- });
-
- wrapper.vm.handleKeyDown({
- keyCode: ENTER_KEY_CODE,
- preventDefault: fakePreventDefault,
- });
-
- expect(wrapper.vm.searchKey).toBe('');
- expect(fakePreventDefault).toHaveBeenCalled();
- });
-
- it('calls action `updateSelectedLabels` with currently highlighted label when Enter key is pressed', () => {
- jest.spyOn(wrapper.vm, 'updateSelectedLabels').mockImplementation();
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- currentHighlightItem: 2,
- });
-
- wrapper.vm.handleKeyDown({
- keyCode: ENTER_KEY_CODE,
- preventDefault: fakePreventDefault,
- });
-
- expect(wrapper.vm.updateSelectedLabels).toHaveBeenCalledWith([mockLabels[2]]);
- });
-
- it('calls action `toggleDropdownContents` when Esc key is pressed', () => {
- jest.spyOn(wrapper.vm, 'toggleDropdownContents').mockImplementation();
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- currentHighlightItem: 1,
- });
-
- wrapper.vm.handleKeyDown({
- keyCode: ESC_KEY_CODE,
- });
-
- expect(wrapper.vm.toggleDropdownContents).toHaveBeenCalled();
- });
-
- it('calls action `scrollIntoViewIfNeeded` in next tick when any key is pressed', async () => {
- jest.spyOn(wrapper.vm, 'scrollIntoViewIfNeeded').mockImplementation();
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- currentHighlightItem: 1,
- });
-
- wrapper.vm.handleKeyDown({
- keyCode: DOWN_KEY_CODE,
- });
-
- await nextTick();
- expect(wrapper.vm.scrollIntoViewIfNeeded).toHaveBeenCalled();
- });
- });
-
- describe('handleLabelClick', () => {
- beforeEach(() => {
- jest.spyOn(wrapper.vm, 'updateSelectedLabels').mockImplementation();
- });
-
- it('calls action `updateSelectedLabels` with provided `label` param', () => {
- wrapper.vm.handleLabelClick(mockRegularLabel);
-
- expect(wrapper.vm.updateSelectedLabels).toHaveBeenCalledWith([mockRegularLabel]);
- });
-
- it('calls action `toggleDropdownContents` when `state.allowMultiselect` is false', () => {
- jest.spyOn(wrapper.vm, 'toggleDropdownContents');
- wrapper.vm.$store.state.allowMultiselect = false;
-
- wrapper.vm.handleLabelClick(mockRegularLabel);
-
- expect(wrapper.vm.toggleDropdownContents).toHaveBeenCalled();
- });
- });
- });
-
- describe('template', () => {
- it('renders gl-intersection-observer as component root', () => {
- expect(wrapper.findComponent(GlIntersectionObserver).exists()).toBe(true);
- });
-
- it('renders gl-loading-icon component when `labelsFetchInProgress` prop is true', async () => {
- wrapper.vm.$store.dispatch('requestLabels');
-
- await nextTick();
- const loadingIconEl = findLoadingIcon();
-
- expect(loadingIconEl.exists()).toBe(true);
- expect(loadingIconEl.attributes('class')).toContain('labels-fetch-loading');
- });
-
- it('renders dropdown title element', () => {
- const titleEl = findDropdownTitle();
-
- expect(titleEl.exists()).toBe(true);
- expect(titleEl.text()).toBe('Assign labels');
- });
-
- it('does not render dropdown title element when `state.variant` is "standalone"', () => {
- createComponent({ ...mockConfig, variant: 'standalone' });
- expect(findDropdownTitle().exists()).toBe(false);
- });
-
- it('renders dropdown title element when `state.variant` is "embedded"', () => {
- createComponent({ ...mockConfig, variant: 'embedded' });
- expect(findDropdownTitle().exists()).toBe(true);
- });
-
- it('renders dropdown close button element', () => {
- const closeButtonEl = findDropdownTitle().findComponent(GlButton);
-
- expect(closeButtonEl.exists()).toBe(true);
- expect(closeButtonEl.props('icon')).toBe('close');
- });
-
- it('renders label search input element', () => {
- const searchInputEl = wrapper.findComponent(GlSearchBoxByType);
-
- expect(searchInputEl.exists()).toBe(true);
- });
-
- it('renders label elements for all labels', () => {
- expect(wrapper.findAllComponents(LabelItem)).toHaveLength(mockLabels.length);
- });
-
- it('renders label element with `highlight` set to true when value of `currentHighlightItem` is more than -1', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- currentHighlightItem: 0,
- });
-
- await nextTick();
- const labelItemEl = findDropdownContent().findComponent(LabelItem);
-
- expect(labelItemEl.attributes('highlight')).toBe('true');
- });
-
- it('renders element containing "No matching results" when `searchKey` does not match with any label', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- searchKey: 'abc',
- });
-
- await nextTick();
- const noMatchEl = findDropdownContent().find('li');
-
- expect(noMatchEl.isVisible()).toBe(true);
- expect(noMatchEl.text()).toContain('No matching results');
- });
-
- it('renders empty content while loading', async () => {
- wrapper.vm.$store.state.labelsFetchInProgress = true;
-
- await nextTick();
- const dropdownContent = findDropdownContent();
- const loadingIcon = findLoadingIcon();
-
- expect(dropdownContent.exists()).toBe(true);
- expect(dropdownContent.isVisible()).toBe(true);
- expect(loadingIcon.exists()).toBe(true);
- expect(loadingIcon.isVisible()).toBe(true);
- });
-
- it('renders footer list items', () => {
- const footerLinks = findDropdownFooter().findAllComponents(GlLink);
- const createLabelLink = footerLinks.at(0);
- const manageLabelsLink = footerLinks.at(1);
-
- expect(createLabelLink.exists()).toBe(true);
- expect(createLabelLink.text()).toBe('Create label');
- expect(manageLabelsLink.exists()).toBe(true);
- expect(manageLabelsLink.text()).toBe('Manage labels');
- });
-
- it('does not render "Create label" footer link when `state.allowLabelCreate` is `false`', async () => {
- wrapper.vm.$store.state.allowLabelCreate = false;
-
- await nextTick();
- const createLabelLink = findDropdownFooter().findAllComponents(GlLink).at(0);
-
- expect(createLabelLink.text()).not.toBe('Create label');
- });
-
- it('does not render footer list items when `state.variant` is "standalone"', () => {
- createComponent({ ...mockConfig, variant: 'standalone' });
- expect(findDropdownFooter().exists()).toBe(false);
- });
-
- it('does not render footer list items when `allowLabelCreate` is false and `labelsManagePath` is null', () => {
- createComponent({
- ...mockConfig,
- allowLabelCreate: false,
- labelsManagePath: null,
- });
- expect(findDropdownFooter().exists()).toBe(false);
- });
-
- it('renders footer list items when `state.variant` is "embedded"', () => {
- expect(findDropdownFooter().exists()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_spec.js
deleted file mode 100644
index 9781d9c4de0..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_spec.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import Vuex from 'vuex';
-
-import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_vue/constants';
-import DropdownContents from '~/vue_shared/components/sidebar/labels_select_vue/dropdown_contents.vue';
-import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_vue/store';
-
-import { mockConfig } from './mock_data';
-
-Vue.use(Vuex);
-
-const createComponent = (initialState = mockConfig, propsData = {}) => {
- const store = new Vuex.Store(labelsSelectModule());
-
- store.dispatch('setInitialState', initialState);
-
- return shallowMount(DropdownContents, {
- propsData,
- store,
- });
-};
-
-describe('DropdownContent', () => {
- let wrapper;
-
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('computed', () => {
- describe('dropdownContentsView', () => {
- it('returns string "dropdown-contents-create-view" when `showDropdownContentsCreateView` prop is `true`', () => {
- wrapper.vm.$store.dispatch('toggleDropdownContentsCreateView');
-
- expect(wrapper.vm.dropdownContentsView).toBe('dropdown-contents-create-view');
- });
-
- it('returns string "dropdown-contents-labels-view" when `showDropdownContentsCreateView` prop is `false`', () => {
- expect(wrapper.vm.dropdownContentsView).toBe('dropdown-contents-labels-view');
- });
- });
- });
-
- describe('template', () => {
- it('renders component container element with class `labels-select-dropdown-contents` and no styles', () => {
- expect(wrapper.attributes('class')).toContain('labels-select-dropdown-contents');
- expect(wrapper.attributes('style')).toBeUndefined();
- });
-
- describe('when `renderOnTop` is true', () => {
- it.each`
- variant | expected
- ${DropdownVariant.Sidebar} | ${'bottom: 3rem'}
- ${DropdownVariant.Standalone} | ${'bottom: 2rem'}
- ${DropdownVariant.Embedded} | ${'bottom: 2rem'}
- `('renders upward for $variant variant', ({ variant, expected }) => {
- wrapper = createComponent({ ...mockConfig, variant }, { renderOnTop: true });
-
- expect(wrapper.attributes('style')).toContain(expected);
- });
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_title_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_title_spec.js
deleted file mode 100644
index 54804f85f81..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_title_spec.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import { GlButton, GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-import Vuex from 'vuex';
-
-import DropdownTitle from '~/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue';
-
-import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_vue/store';
-
-import { mockConfig } from './mock_data';
-
-Vue.use(Vuex);
-
-const createComponent = (initialState = mockConfig) => {
- const store = new Vuex.Store(labelsSelectModule());
-
- store.dispatch('setInitialState', initialState);
-
- return shallowMount(DropdownTitle, {
- store,
- propsData: {
- labelsSelectInProgress: false,
- },
- });
-};
-
-describe('DropdownTitle', () => {
- let wrapper;
-
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('template', () => {
- it('renders component container element with string "Labels"', () => {
- expect(wrapper.text()).toContain('Labels');
- });
-
- it('renders edit link', () => {
- const editBtnEl = wrapper.findComponent(GlButton);
-
- expect(editBtnEl.exists()).toBe(true);
- expect(editBtnEl.text()).toBe('Edit');
- });
-
- it('renders loading icon element when `labelsSelectInProgress` prop is true', async () => {
- wrapper.setProps({
- labelsSelectInProgress: true,
- });
-
- await nextTick();
- expect(wrapper.findComponent(GlLoadingIcon).isVisible()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed_spec.js
deleted file mode 100644
index c6400320dea..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed_spec.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { GlIcon } from '@gitlab/ui';
-import { nextTick } from 'vue';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import DropdownValueCollapsedComponent from '~/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed.vue';
-
-import { mockCollapsedLabels as mockLabels, mockRegularLabel } from './mock_data';
-
-describe('DropdownValueCollapsedComponent', () => {
- let wrapper;
-
- const defaultProps = {
- labels: [],
- };
-
- const mockManyLabels = [...mockLabels, ...mockLabels, ...mockLabels];
-
- const createComponent = ({ props = {} } = {}) => {
- wrapper = shallowMount(DropdownValueCollapsedComponent, {
- propsData: { ...defaultProps, ...props },
- directives: {
- GlTooltip: createMockDirective(),
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findGlIcon = () => wrapper.findComponent(GlIcon);
- const getTooltip = () => getBinding(wrapper.element, 'gl-tooltip');
-
- describe('template', () => {
- it('renders tags icon element', () => {
- createComponent();
-
- expect(findGlIcon().exists()).toBe(true);
- });
-
- it('emits onValueClick event on click', async () => {
- createComponent();
-
- wrapper.trigger('click');
-
- await nextTick();
-
- expect(wrapper.emitted('onValueClick')[0]).toBeDefined();
- });
-
- describe.each`
- scenario | labels | expectedResult | expectedText
- ${'`labels` is empty'} | ${[]} | ${'default text'} | ${'Labels'}
- ${'`labels` has 1 item'} | ${[mockRegularLabel]} | ${'label name'} | ${'Foo Label'}
- ${'`labels` has 2 items'} | ${mockLabels} | ${'comma separated label names'} | ${'Foo Label, Foo::Bar'}
- ${'`labels` has more than 5 items'} | ${mockManyLabels} | ${'comma separated label names with "and more" phrase'} | ${'Foo Label, Foo::Bar, Foo Label, Foo::Bar, Foo Label, and 1 more'}
- `('when $scenario', ({ labels, expectedResult, expectedText }) => {
- beforeEach(() => {
- createComponent({
- props: {
- labels,
- },
- });
- });
-
- it('renders labels count', () => {
- expect(wrapper.text()).toBe(`${labels.length}`);
- });
-
- it(`renders "${expectedResult}" as tooltip`, () => {
- expect(getTooltip().value).toBe(expectedText);
- });
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_value_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_value_spec.js
deleted file mode 100644
index f3c4839002b..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_value_spec.js
+++ /dev/null
@@ -1,99 +0,0 @@
-import { GlLabel } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import Vuex from 'vuex';
-
-import DropdownValue from '~/vue_shared/components/sidebar/labels_select_vue/dropdown_value.vue';
-
-import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_vue/store';
-
-import { mockConfig, mockLabels, mockRegularLabel, mockScopedLabel } from './mock_data';
-
-Vue.use(Vuex);
-
-describe('DropdownValue', () => {
- let wrapper;
-
- const findAllLabels = () => wrapper.findAllComponents(GlLabel);
- const findLabel = (index) => findAllLabels().at(index).props('title');
-
- const createComponent = (initialState = {}, slots = {}) => {
- const store = new Vuex.Store(labelsSelectModule());
-
- store.dispatch('setInitialState', { ...mockConfig, ...initialState });
-
- wrapper = shallowMount(DropdownValue, {
- store,
- slots,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('methods', () => {
- describe('labelFilterUrl', () => {
- it('returns a label filter URL based on provided label param', () => {
- createComponent();
-
- expect(wrapper.vm.labelFilterUrl(mockRegularLabel)).toBe(
- '/gitlab-org/my-project/issues?label_name[]=Foo%20Label',
- );
- });
- });
-
- describe('scopedLabel', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('returns `true` when provided label param is a scoped label', () => {
- expect(wrapper.vm.scopedLabel(mockScopedLabel)).toBe(true);
- });
-
- it('returns `false` when provided label param is a regular label', () => {
- expect(wrapper.vm.scopedLabel(mockRegularLabel)).toBe(false);
- });
- });
- });
-
- describe('template', () => {
- it('renders class `has-labels` on component container element when `selectedLabels` is not empty', () => {
- createComponent();
-
- expect(wrapper.attributes('class')).toContain('has-labels');
- });
-
- it('renders element containing `None` when `selectedLabels` is empty', () => {
- createComponent(
- {
- selectedLabels: [],
- },
- {
- default: 'None',
- },
- );
- const noneEl = wrapper.find('span.text-secondary');
-
- expect(noneEl.exists()).toBe(true);
- expect(noneEl.text()).toBe('None');
- });
-
- it('renders labels when `selectedLabels` is not empty', () => {
- createComponent();
-
- expect(findAllLabels()).toHaveLength(2);
- });
-
- it('orders scoped labels first', () => {
- createComponent({ selectedLabels: mockLabels });
-
- expect(findAllLabels()).toHaveLength(mockLabels.length);
- expect(findLabel(0)).toBe('Foo::Bar');
- expect(findLabel(1)).toBe('Boog');
- expect(findLabel(2)).toBe('Bug');
- expect(findLabel(3)).toBe('Foo Label');
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/label_item_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/label_item_spec.js
deleted file mode 100644
index bb0f1777de6..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/label_item_spec.js
+++ /dev/null
@@ -1,92 +0,0 @@
-import { GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-
-import LabelItem from '~/vue_shared/components/sidebar/labels_select_vue/label_item.vue';
-import { mockRegularLabel } from './mock_data';
-
-const mockLabel = { ...mockRegularLabel, set: true };
-
-const createComponent = ({
- label = mockLabel,
- isLabelSet = mockLabel.set,
- highlight = true,
-} = {}) =>
- shallowMount(LabelItem, {
- propsData: {
- label,
- isLabelSet,
- highlight,
- },
- });
-
-describe('LabelItem', () => {
- let wrapper;
-
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('template', () => {
- it('renders gl-link component', () => {
- expect(wrapper.findComponent(GlLink).exists()).toBe(true);
- });
-
- it('renders component root with class `is-focused` when `highlight` prop is true', () => {
- const wrapperTemp = createComponent({
- highlight: true,
- });
-
- expect(wrapperTemp.classes()).toContain('is-focused');
-
- wrapperTemp.destroy();
- });
-
- it.each`
- isLabelSet | isLabelIndeterminate | testId | iconName
- ${true} | ${false} | ${'checked-icon'} | ${'mobile-issue-close'}
- ${false} | ${true} | ${'indeterminate-icon'} | ${'dash'}
- `(
- 'renders visible gl-icon component when `isLabelSet` prop is $isLabelSet and `isLabelIndeterminate` is $isLabelIndeterminate',
- ({ isLabelSet, isLabelIndeterminate, testId, iconName }) => {
- const wrapperTemp = createComponent({
- isLabelSet,
- isLabelIndeterminate,
- });
-
- const iconEl = wrapperTemp.find(`[data-testid="${testId}"]`);
-
- expect(iconEl.isVisible()).toBe(true);
- expect(iconEl.props('name')).toBe(iconName);
-
- wrapperTemp.destroy();
- },
- );
-
- it('renders visible span element as placeholder instead of gl-icon when `isLabelSet` prop is false', () => {
- const wrapperTemp = createComponent({
- isLabelSet: false,
- });
-
- const placeholderEl = wrapperTemp.find('[data-testid="no-icon"]');
-
- expect(placeholderEl.isVisible()).toBe(true);
-
- wrapperTemp.destroy();
- });
-
- it('renders label color element', () => {
- const colorEl = wrapper.find('[data-testid="label-color-box"]');
-
- expect(colorEl.exists()).toBe(true);
- expect(colorEl.attributes('style')).toBe('background-color: rgb(186, 218, 85);');
- });
-
- it('renders label title', () => {
- expect(wrapper.text()).toContain(mockLabel.title);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js
deleted file mode 100644
index 30c1a4b7d2f..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js
+++ /dev/null
@@ -1,231 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-import Vuex from 'vuex';
-
-import { isInViewport } from '~/lib/utils/common_utils';
-import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_vue/constants';
-import DropdownButton from '~/vue_shared/components/sidebar/labels_select_vue/dropdown_button.vue';
-import DropdownContents from '~/vue_shared/components/sidebar/labels_select_vue/dropdown_contents.vue';
-import DropdownTitle from '~/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue';
-import DropdownValue from '~/vue_shared/components/sidebar/labels_select_vue/dropdown_value.vue';
-import DropdownValueCollapsed from '~/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed.vue';
-import LabelsSelectRoot from '~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue';
-
-import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_vue/store';
-
-import { mockConfig } from './mock_data';
-
-jest.mock('~/lib/utils/common_utils', () => ({
- isInViewport: jest.fn().mockReturnValue(true),
-}));
-
-Vue.use(Vuex);
-
-describe('LabelsSelectRoot', () => {
- let wrapper;
- let store;
-
- const createComponent = (config = mockConfig, slots = {}) => {
- wrapper = shallowMount(LabelsSelectRoot, {
- slots,
- store,
- propsData: config,
- stubs: {
- 'dropdown-contents': DropdownContents,
- },
- });
- };
-
- beforeEach(() => {
- store = new Vuex.Store(labelsSelectModule());
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('methods', () => {
- describe('handleVuexActionDispatch', () => {
- const touchedLabels = [
- {
- id: 2,
- touched: true,
- },
- ];
-
- it('calls `handleDropdownClose` when params `action.type` is `toggleDropdownContents` and state has `showDropdownButton` & `showDropdownContents` props `false`', () => {
- createComponent();
-
- wrapper.vm.handleVuexActionDispatch(
- { type: 'toggleDropdownContents' },
- {
- showDropdownButton: false,
- showDropdownContents: false,
- labels: [{ id: 1 }, { id: 2, touched: true }],
- },
- );
-
- // We're utilizing `onDropdownClose` event emitted from the component to always include `touchedLabels`
- // while the first param of the method is the labels list which were added/removed.
- expect(wrapper.emitted('updateSelectedLabels')).toHaveLength(1);
- expect(wrapper.emitted('updateSelectedLabels')[0]).toEqual([touchedLabels]);
- expect(wrapper.emitted('onDropdownClose')).toHaveLength(1);
- expect(wrapper.emitted('onDropdownClose')[0]).toEqual([touchedLabels]);
- });
-
- it('calls `handleDropdownClose` with state.labels filterd using `set` prop when dropdown variant is `embedded`', () => {
- createComponent({
- ...mockConfig,
- variant: 'embedded',
- });
-
- wrapper.vm.handleVuexActionDispatch(
- { type: 'toggleDropdownContents' },
- {
- showDropdownButton: false,
- showDropdownContents: false,
- labels: [{ id: 1 }, { id: 2, set: true }],
- },
- );
-
- expect(wrapper.emitted('updateSelectedLabels')).toHaveLength(1);
- expect(wrapper.emitted('updateSelectedLabels')[0]).toEqual([
- [
- {
- id: 2,
- set: true,
- },
- ],
- ]);
- expect(wrapper.emitted('onDropdownClose')).toHaveLength(1);
- expect(wrapper.emitted('onDropdownClose')[0]).toEqual([[]]);
- });
- });
-
- describe('handleCollapsedValueClick', () => {
- it('emits `toggleCollapse` event on component', () => {
- createComponent();
- wrapper.vm.handleCollapsedValueClick();
- expect(wrapper.emitted().toggleCollapse).toHaveLength(1);
- });
- });
- });
-
- describe('template', () => {
- it('renders component with classes `labels-select-wrapper position-relative`', () => {
- createComponent();
- expect(wrapper.attributes('class')).toContain('labels-select-wrapper position-relative');
- });
-
- it.each`
- variant | cssClass
- ${'standalone'} | ${'is-standalone'}
- ${'embedded'} | ${'is-embedded'}
- `(
- 'renders component root element with CSS class `$cssClass` when `state.variant` is "$variant"',
- async ({ variant, cssClass }) => {
- createComponent({
- ...mockConfig,
- variant,
- });
-
- await nextTick();
- expect(wrapper.classes()).toContain(cssClass);
- },
- );
-
- it('renders `dropdown-value-collapsed` component when `allowLabelCreate` prop is `true`', async () => {
- createComponent();
- await nextTick();
- expect(wrapper.findComponent(DropdownValueCollapsed).exists()).toBe(true);
- });
-
- it('renders `dropdown-title` component', async () => {
- createComponent();
- await nextTick();
- expect(wrapper.findComponent(DropdownTitle).exists()).toBe(true);
- });
-
- it('renders `dropdown-value` component', async () => {
- createComponent(mockConfig, {
- default: 'None',
- });
- await nextTick();
-
- const valueComp = wrapper.findComponent(DropdownValue);
-
- expect(valueComp.exists()).toBe(true);
- expect(valueComp.text()).toBe('None');
- });
-
- it('renders `dropdown-button` component when `showDropdownButton` prop is `true`', async () => {
- createComponent();
- wrapper.vm.$store.dispatch('toggleDropdownButton');
- await nextTick();
- expect(wrapper.findComponent(DropdownButton).exists()).toBe(true);
- });
-
- it('renders `dropdown-contents` component when `showDropdownButton` & `showDropdownContents` prop is `true`', async () => {
- createComponent();
- wrapper.vm.$store.dispatch('toggleDropdownContents');
- await nextTick();
- expect(wrapper.findComponent(DropdownContents).exists()).toBe(true);
- });
-
- describe('sets content direction based on viewport', () => {
- describe.each(Object.values(DropdownVariant))(
- 'when labels variant is "%s"',
- ({ variant }) => {
- beforeEach(() => {
- createComponent({ ...mockConfig, variant });
- wrapper.vm.$store.dispatch('toggleDropdownContents');
- });
-
- it('set direction when out of viewport', async () => {
- isInViewport.mockImplementation(() => false);
- wrapper.vm.setContentIsOnViewport(wrapper.vm.$store.state);
-
- await nextTick();
- expect(wrapper.findComponent(DropdownContents).props('renderOnTop')).toBe(true);
- });
-
- it('does not set direction when inside of viewport', async () => {
- isInViewport.mockImplementation(() => true);
- wrapper.vm.setContentIsOnViewport(wrapper.vm.$store.state);
-
- await nextTick();
- expect(wrapper.findComponent(DropdownContents).props('renderOnTop')).toBe(false);
- });
- },
- );
- });
- });
-
- it('calls toggleDropdownContents action when isEditing prop is changing to true', async () => {
- createComponent();
-
- jest.spyOn(store, 'dispatch').mockResolvedValue();
- await wrapper.setProps({ isEditing: true });
-
- expect(store.dispatch).toHaveBeenCalledWith('toggleDropdownContents');
- });
-
- it('does not call toggleDropdownContents action when isEditing prop is changing to false', async () => {
- createComponent();
-
- jest.spyOn(store, 'dispatch').mockResolvedValue();
- await wrapper.setProps({ isEditing: false });
-
- expect(store.dispatch).not.toHaveBeenCalled();
- });
-
- it('calls updateLabelsSetState after selected labels were updated', async () => {
- createComponent();
-
- jest.spyOn(store, 'dispatch').mockResolvedValue();
- await wrapper.setProps({ selectedLabels: [] });
- jest.advanceTimersByTime(100);
-
- expect(store.dispatch).toHaveBeenCalledWith('updateLabelsSetState');
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/actions_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/actions_spec.js
deleted file mode 100644
index edd044bd754..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/actions_spec.js
+++ /dev/null
@@ -1,265 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-
-import testAction from 'helpers/vuex_action_helper';
-import { createAlert } from '~/flash';
-import axios from '~/lib/utils/axios_utils';
-import * as actions from '~/vue_shared/components/sidebar/labels_select_vue/store/actions';
-import * as types from '~/vue_shared/components/sidebar/labels_select_vue/store/mutation_types';
-import defaultState from '~/vue_shared/components/sidebar/labels_select_vue/store/state';
-
-jest.mock('~/flash');
-
-describe('LabelsSelect Actions', () => {
- let state;
- const mockInitialState = {
- labels: [],
- selectedLabels: [],
- };
-
- beforeEach(() => {
- state = { ...defaultState() };
- });
-
- describe('setInitialState', () => {
- it('sets initial store state', () => {
- return testAction(
- actions.setInitialState,
- mockInitialState,
- state,
- [{ type: types.SET_INITIAL_STATE, payload: mockInitialState }],
- [],
- );
- });
- });
-
- describe('toggleDropdownButton', () => {
- it('toggles dropdown button', () => {
- return testAction(
- actions.toggleDropdownButton,
- {},
- state,
- [{ type: types.TOGGLE_DROPDOWN_BUTTON }],
- [],
- );
- });
- });
-
- describe('toggleDropdownContents', () => {
- it('toggles dropdown contents', () => {
- return testAction(
- actions.toggleDropdownContents,
- {},
- state,
- [{ type: types.TOGGLE_DROPDOWN_CONTENTS }],
- [],
- );
- });
- });
-
- describe('toggleDropdownContentsCreateView', () => {
- it('toggles dropdown create view', () => {
- return testAction(
- actions.toggleDropdownContentsCreateView,
- {},
- state,
- [{ type: types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW }],
- [],
- );
- });
- });
-
- describe('requestLabels', () => {
- it('sets value of `state.labelsFetchInProgress` to `true`', () => {
- return testAction(actions.requestLabels, {}, state, [{ type: types.REQUEST_LABELS }], []);
- });
- });
-
- describe('receiveLabelsSuccess', () => {
- it('sets provided labels to `state.labels`', () => {
- const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
-
- return testAction(
- actions.receiveLabelsSuccess,
- labels,
- state,
- [{ type: types.RECEIVE_SET_LABELS_SUCCESS, payload: labels }],
- [],
- );
- });
- });
-
- describe('receiveLabelsFailure', () => {
- it('sets value `state.labelsFetchInProgress` to `false`', () => {
- return testAction(
- actions.receiveLabelsFailure,
- {},
- state,
- [{ type: types.RECEIVE_SET_LABELS_FAILURE }],
- [],
- );
- });
-
- it('shows flash error', () => {
- actions.receiveLabelsFailure({ commit: () => {} });
-
- expect(createAlert).toHaveBeenCalledWith({ message: 'Error fetching labels.' });
- });
- });
-
- describe('fetchLabels', () => {
- let mock;
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- state.labelsFetchPath = 'labels.json';
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- describe('on success', () => {
- it('dispatches `requestLabels` & `receiveLabelsSuccess` actions', () => {
- const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
- mock.onGet(/labels.json/).replyOnce(200, labels);
-
- return testAction(
- actions.fetchLabels,
- {},
- state,
- [],
- [{ type: 'requestLabels' }, { type: 'receiveLabelsSuccess', payload: labels }],
- );
- });
- });
-
- describe('on failure', () => {
- it('dispatches `requestLabels` & `receiveLabelsFailure` actions', () => {
- mock.onGet(/labels.json/).replyOnce(500, {});
-
- return testAction(
- actions.fetchLabels,
- {},
- state,
- [],
- [{ type: 'requestLabels' }, { type: 'receiveLabelsFailure' }],
- );
- });
- });
- });
-
- describe('requestCreateLabel', () => {
- it('sets value `state.labelCreateInProgress` to `true`', () => {
- return testAction(
- actions.requestCreateLabel,
- {},
- state,
- [{ type: types.REQUEST_CREATE_LABEL }],
- [],
- );
- });
- });
-
- describe('receiveCreateLabelSuccess', () => {
- it('sets value `state.labelCreateInProgress` to `false`', () => {
- return testAction(
- actions.receiveCreateLabelSuccess,
- {},
- state,
- [{ type: types.RECEIVE_CREATE_LABEL_SUCCESS }],
- [],
- );
- });
- });
-
- describe('receiveCreateLabelFailure', () => {
- it('sets value `state.labelCreateInProgress` to `false`', () => {
- return testAction(
- actions.receiveCreateLabelFailure,
- {},
- state,
- [{ type: types.RECEIVE_CREATE_LABEL_FAILURE }],
- [],
- );
- });
-
- it('shows flash error', () => {
- actions.receiveCreateLabelFailure({ commit: () => {} });
-
- expect(createAlert).toHaveBeenCalledWith({ message: 'Error creating label.' });
- });
- });
-
- describe('createLabel', () => {
- let mock;
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- state.labelsManagePath = 'labels.json';
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- describe('on success', () => {
- it('dispatches `requestCreateLabel`, `fetchLabels` & `receiveCreateLabelSuccess` & `toggleDropdownContentsCreateView` actions', () => {
- const label = { id: 1 };
- mock.onPost(/labels.json/).replyOnce(200, label);
-
- return testAction(
- actions.createLabel,
- {},
- state,
- [],
- [
- { type: 'requestCreateLabel' },
- { payload: { refetch: true }, type: 'fetchLabels' },
- { type: 'receiveCreateLabelSuccess' },
- { type: 'toggleDropdownContentsCreateView' },
- ],
- );
- });
- });
-
- describe('on failure', () => {
- it('dispatches `requestCreateLabel` & `receiveCreateLabelFailure` actions', () => {
- mock.onPost(/labels.json/).replyOnce(500, {});
-
- return testAction(
- actions.createLabel,
- {},
- state,
- [],
- [{ type: 'requestCreateLabel' }, { type: 'receiveCreateLabelFailure' }],
- );
- });
- });
- });
-
- describe('updateSelectedLabels', () => {
- it('updates `state.labels` based on provided `labels` param', () => {
- const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
-
- return testAction(
- actions.updateSelectedLabels,
- labels,
- state,
- [{ type: types.UPDATE_SELECTED_LABELS, payload: { labels } }],
- [],
- );
- });
- });
-
- describe('updateLabelsSetState', () => {
- it('updates labels `set` state to match `selectedLabels`', () => {
- testAction(
- actions.updateLabelsSetState,
- {},
- state,
- [{ type: types.UPDATE_LABELS_SET_STATE }],
- [],
- );
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/getters_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/getters_spec.js
deleted file mode 100644
index 6ad46dbe898..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/getters_spec.js
+++ /dev/null
@@ -1,74 +0,0 @@
-import * as getters from '~/vue_shared/components/sidebar/labels_select_vue/store/getters';
-
-describe('LabelsSelect Getters', () => {
- describe('dropdownButtonText', () => {
- it.each`
- labelType | dropdownButtonText | expected
- ${'default'} | ${''} | ${'Label'}
- ${'custom'} | ${'Custom label'} | ${'Custom label'}
- `(
- 'returns $labelType text when state.labels has no selected labels',
- ({ dropdownButtonText, expected }) => {
- const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
- const selectedLabels = [];
- const state = { labels, selectedLabels, dropdownButtonText };
-
- expect(getters.dropdownButtonText(state, {})).toBe(expected);
- },
- );
-
- describe.each`
- dropdownVariant | isDropdownVariantSidebar | isDropdownVariantEmbedded
- ${'sidebar'} | ${true} | ${false}
- ${'embedded'} | ${false} | ${true}
- `(
- 'when dropdown variant is $dropdownVariant',
- ({ isDropdownVariantSidebar, isDropdownVariantEmbedded }) => {
- it('returns label title when state.labels has only 1 label', () => {
- const labels = [{ id: 1, title: 'Foobar', set: true }];
-
- expect(
- getters.dropdownButtonText(
- { labels },
- { isDropdownVariantSidebar, isDropdownVariantEmbedded },
- ),
- ).toBe('Foobar');
- });
-
- it('returns first label title and remaining labels count when state.labels has more than 1 label', () => {
- const labels = [
- { id: 1, title: 'Foo', set: true },
- { id: 2, title: 'Bar', set: true },
- ];
-
- expect(
- getters.dropdownButtonText(
- { labels },
- { isDropdownVariantSidebar, isDropdownVariantEmbedded },
- ),
- ).toBe('Foo +1 more');
- });
- },
- );
- });
-
- describe('selectedLabelsList', () => {
- it('returns array of IDs of all labels within `state.selectedLabels`', () => {
- const selectedLabels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
-
- expect(getters.selectedLabelsList({ selectedLabels })).toEqual([1, 2, 3, 4]);
- });
- });
-
- describe('isDropdownVariantSidebar', () => {
- it('returns `true` when `state.variant` is "sidebar"', () => {
- expect(getters.isDropdownVariantSidebar({ variant: 'sidebar' })).toBe(true);
- });
- });
-
- describe('isDropdownVariantStandalone', () => {
- it('returns `true` when `state.variant` is "standalone"', () => {
- expect(getters.isDropdownVariantStandalone({ variant: 'standalone' })).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/mutations_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/mutations_spec.js
deleted file mode 100644
index 2b2508b5e11..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/mutations_spec.js
+++ /dev/null
@@ -1,232 +0,0 @@
-import { cloneDeep } from 'lodash';
-import * as types from '~/vue_shared/components/sidebar/labels_select_vue/store/mutation_types';
-import mutations from '~/vue_shared/components/sidebar/labels_select_vue/store/mutations';
-
-describe('LabelsSelect Mutations', () => {
- describe(`${types.SET_INITIAL_STATE}`, () => {
- it('initializes provided props to store state', () => {
- const state = {};
- mutations[types.SET_INITIAL_STATE](state, {
- labels: 'foo',
- });
-
- expect(state.labels).toEqual('foo');
- });
- });
-
- describe(`${types.TOGGLE_DROPDOWN_BUTTON}`, () => {
- it('toggles value of `state.showDropdownButton`', () => {
- const state = {
- showDropdownButton: false,
- };
- mutations[types.TOGGLE_DROPDOWN_BUTTON](state);
-
- expect(state.showDropdownButton).toBe(true);
- });
- });
-
- describe(`${types.TOGGLE_DROPDOWN_CONTENTS}`, () => {
- it('toggles value of `state.showDropdownButton` when `state.dropdownOnly` is false', () => {
- const state = {
- dropdownOnly: false,
- showDropdownButton: false,
- variant: 'sidebar',
- };
- mutations[types.TOGGLE_DROPDOWN_CONTENTS](state);
-
- expect(state.showDropdownButton).toBe(true);
- });
-
- it('toggles value of `state.showDropdownContents`', () => {
- const state = {
- showDropdownContents: false,
- };
- mutations[types.TOGGLE_DROPDOWN_CONTENTS](state);
-
- expect(state.showDropdownContents).toBe(true);
- });
-
- it('sets value of `state.showDropdownContentsCreateView` to `false` when `showDropdownContents` is true', () => {
- const state = {
- showDropdownContents: false,
- showDropdownContentsCreateView: true,
- };
- mutations[types.TOGGLE_DROPDOWN_CONTENTS](state);
-
- expect(state.showDropdownContentsCreateView).toBe(false);
- });
- });
-
- describe(`${types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW}`, () => {
- it('toggles value of `state.showDropdownContentsCreateView`', () => {
- const state = {
- showDropdownContentsCreateView: false,
- };
- mutations[types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW](state);
-
- expect(state.showDropdownContentsCreateView).toBe(true);
- });
- });
-
- describe(`${types.REQUEST_LABELS}`, () => {
- it('sets value of `state.labelsFetchInProgress` to true', () => {
- const state = {
- labelsFetchInProgress: false,
- };
- mutations[types.REQUEST_LABELS](state);
-
- expect(state.labelsFetchInProgress).toBe(true);
- });
- });
-
- describe(`${types.RECEIVE_SET_LABELS_SUCCESS}`, () => {
- const selectedLabels = [
- { id: 2, set: true },
- { id: 4, set: true },
- ];
- const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
-
- it('sets value of `state.labelsFetchInProgress` to false', () => {
- const state = {
- selectedLabels,
- labelsFetchInProgress: true,
- };
- mutations[types.RECEIVE_SET_LABELS_SUCCESS](state, labels);
-
- expect(state.labelsFetchInProgress).toBe(false);
- });
-
- it('sets provided `labels` to `state.labels` along with `set` prop based on `state.selectedLabels`', () => {
- const selectedLabelIds = selectedLabels.map((label) => label.id);
- const state = {
- selectedLabels,
- labelsFetchInProgress: true,
- };
- mutations[types.RECEIVE_SET_LABELS_SUCCESS](state, labels);
-
- state.labels.forEach((label) => {
- if (selectedLabelIds.includes(label.id)) {
- expect(label.set).toBe(true);
- }
- });
- });
- });
-
- describe(`${types.RECEIVE_SET_LABELS_FAILURE}`, () => {
- it('sets value of `state.labelsFetchInProgress` to false', () => {
- const state = {
- labelsFetchInProgress: true,
- };
- mutations[types.RECEIVE_SET_LABELS_FAILURE](state);
-
- expect(state.labelsFetchInProgress).toBe(false);
- });
- });
-
- describe(`${types.REQUEST_CREATE_LABEL}`, () => {
- it('sets value of `state.labelCreateInProgress` to true', () => {
- const state = {
- labelCreateInProgress: false,
- };
- mutations[types.REQUEST_CREATE_LABEL](state);
-
- expect(state.labelCreateInProgress).toBe(true);
- });
- });
-
- describe(`${types.RECEIVE_CREATE_LABEL_SUCCESS}`, () => {
- it('sets value of `state.labelCreateInProgress` to false', () => {
- const state = {
- labelCreateInProgress: false,
- };
- mutations[types.RECEIVE_CREATE_LABEL_SUCCESS](state);
-
- expect(state.labelCreateInProgress).toBe(false);
- });
- });
-
- describe(`${types.RECEIVE_CREATE_LABEL_FAILURE}`, () => {
- it('sets value of `state.labelCreateInProgress` to false', () => {
- const state = {
- labelCreateInProgress: false,
- };
- mutations[types.RECEIVE_CREATE_LABEL_FAILURE](state);
-
- expect(state.labelCreateInProgress).toBe(false);
- });
- });
-
- describe(`${types.UPDATE_SELECTED_LABELS}`, () => {
- const labels = [
- { id: 1, title: 'scoped' },
- { id: 2, title: 'scoped::label::one', set: false },
- { id: 3, title: 'scoped::label::two', set: false },
- { id: 4, title: 'scoped::label::three', set: true },
- { id: 5, title: 'scoped::one', set: false },
- { id: 6, title: 'scoped::two', set: false },
- { id: 7, title: 'scoped::three', set: true },
- { id: 8, title: '' },
- ];
-
- it.each`
- label | labelGroupIds
- ${labels[0]} | ${[]}
- ${labels[1]} | ${[labels[2], labels[3]]}
- ${labels[2]} | ${[labels[1], labels[3]]}
- ${labels[3]} | ${[labels[1], labels[2]]}
- ${labels[4]} | ${[labels[5], labels[6]]}
- ${labels[5]} | ${[labels[4], labels[6]]}
- ${labels[6]} | ${[labels[4], labels[5]]}
- ${labels[7]} | ${[]}
- `('updates `touched` and `set` props for $label.title', ({ label, labelGroupIds }) => {
- const state = { labels: cloneDeep(labels) };
-
- mutations[types.UPDATE_SELECTED_LABELS](state, { labels: [{ id: label.id }] });
-
- expect(state.labels[label.id - 1]).toMatchObject({
- touched: true,
- set: !labels[label.id - 1].set,
- });
-
- labelGroupIds.forEach((l) => {
- expect(state.labels[l.id - 1].touched).toBeUndefined();
- expect(state.labels[l.id - 1].set).toBe(false);
- });
- });
- it('allows selection of multiple scoped labels', () => {
- const state = { labels: cloneDeep(labels), allowMultipleScopedLabels: true };
-
- mutations[types.UPDATE_SELECTED_LABELS](state, { labels: [{ id: labels[4].id }] });
- mutations[types.UPDATE_SELECTED_LABELS](state, { labels: [{ id: labels[5].id }] });
-
- expect(state.labels[4].set).toBe(true);
- expect(state.labels[5].set).toBe(true);
- expect(state.labels[6].set).toBe(true);
- });
- });
-
- describe(`${types.UPDATE_LABELS_SET_STATE}`, () => {
- it('updates labels `set` state to match selected labels', () => {
- const state = {
- labels: [
- { id: 1, title: 'scoped::test', set: false, indeterminate: false },
- { id: 2, title: 'scoped::one', set: true, indeterminate: false, touched: true },
- { id: 3, title: '', set: false, indeterminate: false },
- { id: 4, title: '', set: false, indeterminate: false },
- ],
- selectedLabels: [
- { id: 1, set: true },
- { id: 3, set: true },
- ],
- };
- mutations[types.UPDATE_LABELS_SET_STATE](state);
-
- expect(state.labels).toEqual([
- { id: 1, title: 'scoped::test', set: true, indeterminate: false },
- { id: 2, title: 'scoped::one', set: false, indeterminate: false, touched: true },
- { id: 3, title: '', set: true, indeterminate: false },
- { id: 4, title: '', set: false, indeterminate: false },
- ]);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view_spec.js
deleted file mode 100644
index 237f174e048..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view_spec.js
+++ /dev/null
@@ -1,239 +0,0 @@
-import { GlAlert, GlLoadingIcon, GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert } from '~/flash';
-import { workspaceLabelsQueries } from '~/sidebar/constants';
-import DropdownContentsCreateView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue';
-import createLabelMutation from '~/vue_shared/components/sidebar/labels_select_widget/graphql/create_label.mutation.graphql';
-import {
- mockRegularLabel,
- mockSuggestedColors,
- createLabelSuccessfulResponse,
- workspaceLabelsQueryResponse,
-} from './mock_data';
-
-jest.mock('~/flash');
-
-const colors = Object.keys(mockSuggestedColors);
-
-Vue.use(VueApollo);
-
-const userRecoverableError = {
- ...createLabelSuccessfulResponse,
- errors: ['Houston, we have a problem'],
-};
-
-const titleTakenError = {
- data: {
- labelCreate: {
- label: mockRegularLabel,
- errors: ['Title has already been taken'],
- },
- },
-};
-
-const createLabelSuccessHandler = jest.fn().mockResolvedValue(createLabelSuccessfulResponse);
-const createLabelUserRecoverableErrorHandler = jest.fn().mockResolvedValue(userRecoverableError);
-const createLabelDuplicateErrorHandler = jest.fn().mockResolvedValue(titleTakenError);
-const createLabelErrorHandler = jest.fn().mockRejectedValue('Houston, we have a problem');
-
-describe('DropdownContentsCreateView', () => {
- let wrapper;
-
- const findAllColors = () => wrapper.findAllComponents(GlLink);
- const findSelectedColor = () => wrapper.find('[data-testid="selected-color"]');
- const findSelectedColorText = () => wrapper.find('[data-testid="selected-color-text"]');
- const findCreateButton = () => wrapper.find('[data-testid="create-button"]');
- const findCancelButton = () => wrapper.find('[data-testid="cancel-button"]');
- const findLabelTitleInput = () => wrapper.find('[data-testid="label-title-input"]');
-
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
-
- const fillLabelAttributes = () => {
- findLabelTitleInput().vm.$emit('input', 'Test title');
- findAllColors().at(0).vm.$emit('click', new Event('mouseclick'));
- };
-
- const createComponent = ({
- mutationHandler = createLabelSuccessHandler,
- labelCreateType = 'project',
- workspaceType = 'project',
- } = {}) => {
- const mockApollo = createMockApollo([[createLabelMutation, mutationHandler]]);
- mockApollo.clients.defaultClient.cache.writeQuery({
- query: workspaceLabelsQueries[workspaceType].query,
- data: workspaceLabelsQueryResponse.data,
- variables: {
- fullPath: '',
- searchTerm: '',
- },
- });
-
- wrapper = shallowMount(DropdownContentsCreateView, {
- apolloProvider: mockApollo,
- propsData: {
- fullPath: '',
- attrWorkspacePath: '',
- labelCreateType,
- workspaceType,
- },
- });
- };
-
- beforeEach(() => {
- gon.suggested_label_colors = mockSuggestedColors;
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders a palette of 21 colors', () => {
- createComponent();
- expect(findAllColors()).toHaveLength(21);
- });
-
- it('selects a color after clicking on colored block', async () => {
- createComponent();
- expect(findSelectedColor().attributes('style')).toBeUndefined();
-
- findAllColors().at(0).vm.$emit('click', new Event('mouseclick'));
- await nextTick();
-
- expect(findSelectedColor().attributes('style')).toBe('background-color: rgb(0, 153, 102);');
- });
-
- it('shows correct color hex code after selecting a color', async () => {
- createComponent();
- expect(findSelectedColorText().attributes('value')).toBe('');
-
- findAllColors().at(0).vm.$emit('click', new Event('mouseclick'));
- await nextTick();
-
- expect(findSelectedColorText().attributes('value')).toBe(colors[0]);
- });
-
- it('disables a Create button if label title is not set', async () => {
- createComponent();
- findAllColors().at(0).vm.$emit('click', new Event('mouseclick'));
- await nextTick();
-
- expect(findCreateButton().props('disabled')).toBe(true);
- });
-
- it('disables a Create button if color is not set', async () => {
- createComponent();
- findLabelTitleInput().vm.$emit('input', 'Test title');
- await nextTick();
-
- expect(findCreateButton().props('disabled')).toBe(true);
- });
-
- it('does not render a loader spinner', () => {
- createComponent();
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- it('emits a `hideCreateView` event on Cancel button click', () => {
- createComponent();
- const event = { stopPropagation: jest.fn() };
- findCancelButton().vm.$emit('click', event);
-
- expect(wrapper.emitted('hideCreateView')).toHaveLength(1);
- expect(event.stopPropagation).toHaveBeenCalled();
- });
-
- describe('when label title and selected color are set', () => {
- beforeEach(() => {
- createComponent();
- fillLabelAttributes();
- });
-
- it('enables a Create button', () => {
- expect(findCreateButton().props()).toMatchObject({
- disabled: false,
- category: 'primary',
- variant: 'confirm',
- });
- });
-
- it('renders a loader spinner after Create button click', async () => {
- findCreateButton().vm.$emit('click');
- await nextTick();
-
- expect(findLoadingIcon().exists()).toBe(true);
- });
-
- it('does not loader spinner after mutation is resolved', async () => {
- findCreateButton().vm.$emit('click');
- await nextTick();
-
- expect(findLoadingIcon().exists()).toBe(true);
- await waitForPromises();
-
- expect(findLoadingIcon().exists()).toBe(false);
- });
- });
-
- it('calls a mutation with `projectPath` variable on the issue', () => {
- createComponent();
- fillLabelAttributes();
- findCreateButton().vm.$emit('click');
-
- expect(createLabelSuccessHandler).toHaveBeenCalledWith({
- color: '#009966',
- projectPath: '',
- title: 'Test title',
- });
- });
-
- it('calls a mutation with `groupPath` variable on the epic', () => {
- createComponent({ labelCreateType: 'group', workspaceType: 'group' });
- fillLabelAttributes();
- findCreateButton().vm.$emit('click');
-
- expect(createLabelSuccessHandler).toHaveBeenCalledWith({
- color: '#009966',
- groupPath: '',
- title: 'Test title',
- });
- });
-
- it('calls createAlert is mutation has a user-recoverable error', async () => {
- createComponent({ mutationHandler: createLabelUserRecoverableErrorHandler });
- fillLabelAttributes();
- await nextTick();
-
- findCreateButton().vm.$emit('click');
- await waitForPromises();
-
- expect(createAlert).toHaveBeenCalled();
- });
-
- it('calls createAlert is mutation was rejected', async () => {
- createComponent({ mutationHandler: createLabelErrorHandler });
- fillLabelAttributes();
- await nextTick();
-
- findCreateButton().vm.$emit('click');
- await waitForPromises();
-
- expect(createAlert).toHaveBeenCalled();
- });
-
- it('displays error in alert if label title is already taken', async () => {
- createComponent({ mutationHandler: createLabelDuplicateErrorHandler });
- fillLabelAttributes();
- await nextTick();
-
- findCreateButton().vm.$emit('click');
- await waitForPromises();
-
- expect(wrapper.findComponent(GlAlert).text()).toEqual(
- titleTakenError.data.labelCreate.errors[0],
- );
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js
deleted file mode 100644
index 5d8ad5ddee5..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js
+++ /dev/null
@@ -1,170 +0,0 @@
-import {
- GlLoadingIcon,
- GlSearchBoxByType,
- GlDropdownItem,
- GlIntersectionObserver,
-} from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert } from '~/flash';
-import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
-import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
-import DropdownContentsLabelsView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue';
-import projectLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql';
-import LabelItem from '~/vue_shared/components/sidebar/labels_select_widget/label_item.vue';
-import { mockConfig, workspaceLabelsQueryResponse } from './mock_data';
-
-jest.mock('~/flash');
-
-Vue.use(VueApollo);
-
-const localSelectedLabels = [
- {
- color: '#2f7b2e',
- description: null,
- id: 'gid://gitlab/ProjectLabel/2',
- title: 'Label2',
- },
-];
-
-describe('DropdownContentsLabelsView', () => {
- let wrapper;
-
- const successfulQueryHandler = jest.fn().mockResolvedValue(workspaceLabelsQueryResponse);
-
- const findFirstLabel = () => wrapper.findAllComponents(GlDropdownItem).at(0);
-
- const createComponent = ({
- initialState = mockConfig,
- queryHandler = successfulQueryHandler,
- injected = {},
- searchKey = '',
- } = {}) => {
- const mockApollo = createMockApollo([[projectLabelsQuery, queryHandler]]);
-
- wrapper = shallowMount(DropdownContentsLabelsView, {
- apolloProvider: mockApollo,
- provide: {
- variant: DropdownVariant.Sidebar,
- ...injected,
- },
- propsData: {
- ...initialState,
- localSelectedLabels,
- searchKey,
- labelCreateType: 'project',
- workspaceType: 'project',
- },
- stubs: {
- GlSearchBoxByType,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findLabels = () => wrapper.findAllComponents(LabelItem);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findObserver = () => wrapper.findComponent(GlIntersectionObserver);
-
- const findLabelsList = () => wrapper.find('[data-testid="labels-list"]');
- const findNoResultsMessage = () => wrapper.find('[data-testid="no-results"]');
-
- async function makeObserverAppear() {
- await findObserver().vm.$emit('appear');
- }
-
- describe('when loading labels', () => {
- it('renders loading icon', async () => {
- createComponent();
- await makeObserverAppear();
- expect(findLoadingIcon().exists()).toBe(true);
- });
-
- it('does not render labels list', async () => {
- createComponent();
- await makeObserverAppear();
- expect(findLabelsList().exists()).toBe(false);
- });
- });
-
- describe('when labels are loaded', () => {
- beforeEach(async () => {
- createComponent();
- await makeObserverAppear();
- await waitForPromises();
- });
-
- it('does not render loading icon', async () => {
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- it('renders labels list', async () => {
- expect(findLabelsList().exists()).toBe(true);
- expect(findLabels()).toHaveLength(2);
- });
- });
-
- it('first item is highlighted when search is not empty', async () => {
- createComponent({
- queryHandler: jest.fn().mockResolvedValue(workspaceLabelsQueryResponse),
- searchKey: 'Label',
- });
- await makeObserverAppear();
- await waitForPromises();
- await nextTick();
-
- expect(findLabelsList().exists()).toBe(true);
- expect(findFirstLabel().attributes('active')).toBe('true');
- });
-
- it('when search returns 0 results', async () => {
- createComponent({
- queryHandler: jest.fn().mockResolvedValue({
- data: {
- workspace: {
- labels: {
- nodes: [],
- },
- },
- },
- }),
- searchKey: '123',
- });
- await makeObserverAppear();
- await waitForPromises();
- await nextTick();
-
- expect(findNoResultsMessage().isVisible()).toBe(true);
- });
-
- it('calls `createAlert` when fetching labels failed', async () => {
- createComponent({ queryHandler: jest.fn().mockRejectedValue('Houston, we have a problem!') });
- await makeObserverAppear();
- jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
- await waitForPromises();
-
- expect(createAlert).toHaveBeenCalled();
- });
-
- it('emits an `input` event on label click', async () => {
- createComponent();
- await makeObserverAppear();
- await waitForPromises();
- findFirstLabel().trigger('click');
-
- expect(wrapper.emitted('input')[0][0]).toEqual(expect.arrayContaining(localSelectedLabels));
- });
-
- it('does not trigger query when component did not appear', () => {
- createComponent();
- expect(findLoadingIcon().exists()).toBe(false);
- expect(findLabelsList().exists()).toBe(false);
- expect(successfulQueryHandler).not.toHaveBeenCalled();
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_spec.js
deleted file mode 100644
index 00da9b74957..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_spec.js
+++ /dev/null
@@ -1,207 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
-import DropdownContents from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue';
-import DropdownContentsCreateView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue';
-import DropdownContentsLabelsView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue';
-import DropdownFooter from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_footer.vue';
-
-import { mockLabels } from './mock_data';
-
-const showDropdown = jest.fn();
-const focusInput = jest.fn();
-
-const GlDropdownStub = {
- template: `
- <div data-testid="dropdown">
- <slot name="header"></slot>
- <slot></slot>
- <slot name="footer"></slot>
- </div>
- `,
- methods: {
- show: showDropdown,
- hide: jest.fn(),
- },
-};
-
-const DropdownHeaderStub = {
- template: `
- <div>Hello, I am a header</div>
- `,
- methods: {
- focusInput,
- },
-};
-
-describe('DropdownContent', () => {
- let wrapper;
-
- const createComponent = ({ props = {}, data = {} } = {}) => {
- wrapper = shallowMount(DropdownContents, {
- propsData: {
- labelsCreateTitle: 'test',
- selectedLabels: mockLabels,
- allowMultiselect: true,
- labelsListTitle: 'Assign labels',
- footerCreateLabelTitle: 'create',
- footerManageLabelTitle: 'manage',
- dropdownButtonText: 'Labels',
- variant: 'sidebar',
- fullPath: 'test',
- workspaceType: 'project',
- labelCreateType: 'project',
- attrWorkspacePath: 'path',
- ...props,
- },
- data() {
- return {
- ...data,
- };
- },
- stubs: {
- GlDropdown: GlDropdownStub,
- DropdownHeader: DropdownHeaderStub,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findCreateView = () => wrapper.findComponent(DropdownContentsCreateView);
- const findLabelsView = () => wrapper.findComponent(DropdownContentsLabelsView);
- const findDropdownHeader = () => wrapper.findComponent(DropdownHeaderStub);
- const findDropdownFooter = () => wrapper.findComponent(DropdownFooter);
- const findDropdown = () => wrapper.findComponent(GlDropdownStub);
-
- it('calls dropdown `show` method on `isVisible` prop change', async () => {
- createComponent();
- await wrapper.setProps({
- isVisible: true,
- });
-
- expect(findDropdown().emitted('show')).toBeUndefined();
- });
-
- it('does not emit `setLabels` event on dropdown hide if labels did not change', () => {
- createComponent();
- findDropdown().vm.$emit('hide');
-
- expect(wrapper.emitted('setLabels')).toBeUndefined();
- });
-
- it('emits `setLabels` event on dropdown hide if labels changed on non-sidebar widget', async () => {
- createComponent({ props: { variant: DropdownVariant.Standalone } });
- const updatedLabel = {
- id: 28,
- title: 'Bug',
- description: 'Label for bugs',
- color: '#FF0000',
- textColor: '#FFFFFF',
- };
- findLabelsView().vm.$emit('input', [updatedLabel]);
- await nextTick();
- findDropdown().vm.$emit('hide');
-
- expect(wrapper.emitted('setLabels')).toEqual([[[updatedLabel]]]);
- });
-
- it('emits `setLabels` event on visibility change if labels changed on sidebar widget', async () => {
- createComponent({ props: { variant: DropdownVariant.Standalone, isVisible: true } });
- const updatedLabel = {
- id: 28,
- title: 'Bug',
- description: 'Label for bugs',
- color: '#FF0000',
- textColor: '#FFFFFF',
- };
- findLabelsView().vm.$emit('input', [updatedLabel]);
- wrapper.setProps({ isVisible: false });
- await nextTick();
-
- expect(wrapper.emitted('setLabels')).toEqual([[[updatedLabel]]]);
- });
-
- it('renders header', () => {
- createComponent();
-
- expect(findDropdownHeader().exists()).toBe(true);
- });
-
- it('sets searchKey for labels view on input event from header', async () => {
- createComponent();
-
- expect(findLabelsView().props('searchKey')).toBe('');
- findDropdownHeader().vm.$emit('input', '123');
- await nextTick();
-
- expect(findLabelsView().props('searchKey')).toBe('123');
- });
-
- it('clears and focuses search input on selecting a label', () => {
- createComponent();
- findDropdownHeader().vm.$emit('input', '123');
- findLabelsView().vm.$emit('input', []);
-
- expect(findLabelsView().props('searchKey')).toBe('');
- expect(focusInput).toHaveBeenCalled();
- });
-
- describe('Create view', () => {
- beforeEach(() => {
- createComponent({ data: { showDropdownContentsCreateView: true } });
- });
-
- it('renders create view when `showDropdownContentsCreateView` prop is `true`', () => {
- expect(findCreateView().exists()).toBe(true);
- });
-
- it('does not render footer', () => {
- expect(findDropdownFooter().exists()).toBe(false);
- });
-
- it('changes the view to Labels view on `toggleDropdownContentsCreateView` event', async () => {
- findDropdownHeader().vm.$emit('toggleDropdownContentsCreateView');
- await nextTick();
-
- expect(findCreateView().exists()).toBe(false);
- expect(findLabelsView().exists()).toBe(true);
- });
-
- it('changes the view to Labels view on `hideCreateView` event', async () => {
- findCreateView().vm.$emit('hideCreateView');
- await nextTick();
-
- expect(findCreateView().exists()).toBe(false);
- expect(findLabelsView().exists()).toBe(true);
- });
- });
-
- describe('Labels view', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders labels view when `showDropdownContentsCreateView` when `showDropdownContentsCreateView` prop is `false`', () => {
- expect(findLabelsView().exists()).toBe(true);
- });
-
- it('renders footer on sidebar dropdown', () => {
- expect(findDropdownFooter().exists()).toBe(true);
- });
-
- it('does not render footer on standalone dropdown', () => {
- createComponent({ props: { variant: DropdownVariant.Standalone } });
-
- expect(findDropdownFooter().exists()).toBe(false);
- });
-
- it('renders footer on embedded dropdown', () => {
- createComponent({ props: { variant: DropdownVariant.Embedded } });
-
- expect(findDropdownFooter().exists()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_footer_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_footer_spec.js
deleted file mode 100644
index 0508a059195..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_footer_spec.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import DropdownFooter from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_footer.vue';
-
-describe('DropdownFooter', () => {
- let wrapper;
-
- const createComponent = ({ props = {}, injected = {} } = {}) => {
- wrapper = shallowMount(DropdownFooter, {
- propsData: {
- footerCreateLabelTitle: 'create',
- footerManageLabelTitle: 'manage',
- ...props,
- },
- provide: {
- allowLabelCreate: true,
- labelsManagePath: 'foo/bar',
- ...injected,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findCreateLabelButton = () => wrapper.find('[data-testid="create-label-button"]');
-
- describe('Labels view', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('does not render create label button if `allowLabelCreate` is false', () => {
- createComponent({ injected: { allowLabelCreate: false } });
-
- expect(findCreateLabelButton().exists()).toBe(false);
- });
-
- describe('when `allowLabelCreate` is true', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders create label button', () => {
- expect(findCreateLabelButton().exists()).toBe(true);
- });
-
- it('emits `toggleDropdownContentsCreateView` event on create label button click', async () => {
- findCreateLabelButton().trigger('click');
-
- await nextTick();
- expect(wrapper.emitted('toggleDropdownContentsCreateView')).toEqual([[]]);
- });
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_header_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_header_spec.js
deleted file mode 100644
index c4faef8ccdd..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_header_spec.js
+++ /dev/null
@@ -1,92 +0,0 @@
-import { GlSearchBoxByType } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import DropdownHeader from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue';
-
-describe('DropdownHeader', () => {
- let wrapper;
-
- const createComponent = ({
- showDropdownContentsCreateView = false,
- labelsFetchInProgress = false,
- isStandalone = false,
- } = {}) => {
- wrapper = extendedWrapper(
- shallowMount(DropdownHeader, {
- propsData: {
- showDropdownContentsCreateView,
- labelsFetchInProgress,
- labelsCreateTitle: 'Create label',
- labelsListTitle: 'Select label',
- searchKey: '',
- isStandalone,
- },
- stubs: {
- GlSearchBoxByType,
- },
- }),
- );
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findSearchInput = () => wrapper.findComponent(GlSearchBoxByType);
- const findGoBackButton = () => wrapper.findByTestId('go-back-button');
- const findDropdownTitle = () => wrapper.findByTestId('dropdown-header-title');
-
- beforeEach(() => {
- createComponent();
- });
-
- describe('Create view', () => {
- beforeEach(() => {
- createComponent({ showDropdownContentsCreateView: true });
- });
-
- it('renders go back button', () => {
- expect(findGoBackButton().exists()).toBe(true);
- });
-
- it('does not render search input field', async () => {
- expect(findSearchInput().exists()).toBe(false);
- });
- });
-
- describe('Labels view', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('does not render go back button', () => {
- expect(findGoBackButton().exists()).toBe(false);
- });
-
- it.each`
- labelsFetchInProgress | disabled
- ${true} | ${true}
- ${false} | ${false}
- `(
- 'when labelsFetchInProgress is $labelsFetchInProgress, renders search input with disabled prop to $disabled',
- ({ labelsFetchInProgress, disabled }) => {
- createComponent({ labelsFetchInProgress });
- expect(findSearchInput().props('disabled')).toBe(disabled);
- },
- );
- });
-
- describe('Standalone variant', () => {
- beforeEach(() => {
- createComponent({ isStandalone: true });
- });
-
- it('renders search input', () => {
- expect(findSearchInput().exists()).toBe(true);
- });
-
- it('does not render title', async () => {
- expect(findDropdownTitle().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_value_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_value_spec.js
deleted file mode 100644
index 0c4f4b7d504..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_value_spec.js
+++ /dev/null
@@ -1,104 +0,0 @@
-import { GlLabel } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-
-import DropdownValue from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue';
-
-import { mockRegularLabel, mockScopedLabel } from './mock_data';
-
-describe('DropdownValue', () => {
- let wrapper;
-
- const findAllLabels = () => wrapper.findAllComponents(GlLabel);
- const findRegularLabel = () => findAllLabels().at(1);
- const findScopedLabel = () => findAllLabels().at(0);
- const findWrapper = () => wrapper.find('[data-testid="value-wrapper"]');
- const findEmptyPlaceholder = () => wrapper.find('[data-testid="empty-placeholder"]');
-
- const createComponent = (props = {}, slots = {}) => {
- wrapper = shallowMount(DropdownValue, {
- slots,
- propsData: {
- selectedLabels: [mockRegularLabel, mockScopedLabel],
- allowLabelRemove: true,
- labelsFilterBasePath: '/gitlab-org/my-project/issues',
- labelsFilterParam: 'label_name',
- ...props,
- },
- provide: {
- allowScopedLabels: true,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('when there are no labels', () => {
- beforeEach(() => {
- createComponent(
- {
- selectedLabels: [],
- },
- {
- default: 'None',
- },
- );
- });
-
- it('does not apply `has-labels` class to the wrapping container', () => {
- expect(findWrapper().classes()).not.toContain('has-labels');
- });
-
- it('renders an empty placeholder', () => {
- expect(findEmptyPlaceholder().exists()).toBe(true);
- expect(findEmptyPlaceholder().text()).toBe('None');
- });
-
- it('does not render any labels', () => {
- expect(findAllLabels().length).toBe(0);
- });
- });
-
- describe('when there are labels', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('applies `has-labels` class to the wrapping container', () => {
- expect(findWrapper().classes()).toContain('has-labels');
- });
-
- it('does not render an empty placeholder', () => {
- expect(findEmptyPlaceholder().exists()).toBe(false);
- });
-
- it('renders a list of two labels', () => {
- expect(findAllLabels().length).toBe(2);
- });
-
- it('passes correct props to the regular label', () => {
- expect(findRegularLabel().props('target')).toBe(
- '/gitlab-org/my-project/issues?label_name[]=Foo%20Label',
- );
- expect(findRegularLabel().props('scoped')).toBe(false);
- });
-
- it('passes correct props to the scoped label', () => {
- expect(findScopedLabel().props('target')).toBe(
- '/gitlab-org/my-project/issues?label_name[]=Foo%3A%3ABar',
- );
- expect(findScopedLabel().props('scoped')).toBe(true);
- });
-
- it('emits `onLabelRemove` event with the correct ID', () => {
- findRegularLabel().vm.$emit('close');
- expect(wrapper.emitted('onLabelRemove')).toEqual([[mockRegularLabel.id]]);
- });
-
- it('emits `onCollapsedValueClick` when clicking on collapsed value', () => {
- wrapper.find('.sidebar-collapsed-icon').trigger('click');
- expect(wrapper.emitted('onCollapsedValueClick')).toEqual([[]]);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/label_item_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/label_item_spec.js
deleted file mode 100644
index 6e8841411a2..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/label_item_spec.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-
-import LabelItem from '~/vue_shared/components/sidebar/labels_select_widget/label_item.vue';
-import { mockRegularLabel } from './mock_data';
-
-const mockLabel = { ...mockRegularLabel, set: true };
-
-const createComponent = ({ label = mockLabel } = {}) =>
- shallowMount(LabelItem, {
- propsData: {
- label,
- },
- });
-
-describe('LabelItem', () => {
- let wrapper;
-
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('template', () => {
- it('renders label color element', () => {
- const colorEl = wrapper.find('[data-testid="label-color-box"]');
-
- expect(colorEl.exists()).toBe(true);
- expect(colorEl.attributes('style')).toBe('background-color: rgb(186, 218, 85);');
- });
-
- it('renders label title', () => {
- expect(wrapper.text()).toContain(mockLabel.title);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js
deleted file mode 100644
index 74ddd07d041..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js
+++ /dev/null
@@ -1,217 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert } from '~/flash';
-import { IssuableType } from '~/issues/constants';
-import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
-import DropdownContents from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue';
-import DropdownValue from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue';
-import issueLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql';
-import updateIssueLabelsMutation from '~/boards/graphql/issue_set_labels.mutation.graphql';
-import updateMergeRequestLabelsMutation from '~/sidebar/queries/update_merge_request_labels.mutation.graphql';
-import issuableLabelsSubscription from 'ee_else_ce/sidebar/queries/issuable_labels.subscription.graphql';
-import updateEpicLabelsMutation from '~/vue_shared/components/sidebar/labels_select_widget/graphql/epic_update_labels.mutation.graphql';
-import LabelsSelectRoot from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue';
-import {
- mockConfig,
- issuableLabelsQueryResponse,
- updateLabelsMutationResponse,
- issuableLabelsSubscriptionResponse,
-} from './mock_data';
-
-jest.mock('~/flash');
-
-Vue.use(VueApollo);
-
-const successfulQueryHandler = jest.fn().mockResolvedValue(issuableLabelsQueryResponse);
-const successfulMutationHandler = jest.fn().mockResolvedValue(updateLabelsMutationResponse);
-const subscriptionHandler = jest.fn().mockResolvedValue(issuableLabelsSubscriptionResponse);
-const errorQueryHandler = jest.fn().mockRejectedValue('Houston, we have a problem');
-
-const updateLabelsMutation = {
- [IssuableType.Issue]: updateIssueLabelsMutation,
- [IssuableType.MergeRequest]: updateMergeRequestLabelsMutation,
- [IssuableType.Epic]: updateEpicLabelsMutation,
-};
-
-describe('LabelsSelectRoot', () => {
- let wrapper;
-
- const findSidebarEditableItem = () => wrapper.findComponent(SidebarEditableItem);
- const findDropdownValue = () => wrapper.findComponent(DropdownValue);
- const findDropdownContents = () => wrapper.findComponent(DropdownContents);
-
- const createComponent = ({
- config = mockConfig,
- slots = {},
- issuableType = IssuableType.Issue,
- queryHandler = successfulQueryHandler,
- mutationHandler = successfulMutationHandler,
- } = {}) => {
- const mockApollo = createMockApollo([
- [issueLabelsQuery, queryHandler],
- [updateLabelsMutation[issuableType], mutationHandler],
- [issuableLabelsSubscription, subscriptionHandler],
- ]);
-
- wrapper = shallowMount(LabelsSelectRoot, {
- slots,
- apolloProvider: mockApollo,
- propsData: {
- ...config,
- issuableType,
- labelCreateType: 'project',
- workspaceType: 'project',
- },
- stubs: {
- SidebarEditableItem,
- },
- provide: {
- canUpdate: true,
- allowLabelEdit: true,
- allowLabelCreate: true,
- labelsManagePath: 'test',
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders component with classes `labels-select-wrapper gl-relative`', () => {
- createComponent();
- expect(wrapper.classes()).toEqual(['labels-select-wrapper', 'gl-relative']);
- });
-
- it.each`
- variant | cssClass
- ${'standalone'} | ${'is-standalone'}
- ${'embedded'} | ${'is-embedded'}
- `(
- 'renders component root element with CSS class `$cssClass` when `state.variant` is "$variant"',
- async ({ variant, cssClass }) => {
- createComponent({
- config: { ...mockConfig, variant },
- });
-
- await nextTick();
- expect(wrapper.classes()).toContain(cssClass);
- },
- );
-
- describe('if dropdown variant is `sidebar`', () => {
- it('renders sidebar editable item', () => {
- createComponent();
- expect(findSidebarEditableItem().exists()).toBe(true);
- });
-
- it('passes true `loading` prop to sidebar editable item when loading labels', () => {
- createComponent();
- expect(findSidebarEditableItem().props('loading')).toBe(true);
- });
-
- describe('when labels are fetched successfully', () => {
- beforeEach(async () => {
- createComponent();
- await waitForPromises();
- });
-
- it('passes true `loading` prop to sidebar editable item', () => {
- expect(findSidebarEditableItem().props('loading')).toBe(false);
- });
-
- it('renders dropdown value component when query labels is resolved', () => {
- expect(findDropdownValue().exists()).toBe(true);
- expect(findDropdownValue().props('selectedLabels')).toEqual([
- {
- __typename: 'Label',
- color: '#330066',
- description: null,
- id: 'gid://gitlab/ProjectLabel/1',
- title: 'Label1',
- textColor: '#000000',
- },
- ]);
- });
-
- it('emits `onLabelRemove` event on dropdown value label remove event', () => {
- const label = { id: 'gid://gitlab/ProjectLabel/1' };
- findDropdownValue().vm.$emit('onLabelRemove', label);
- expect(wrapper.emitted('onLabelRemove')).toEqual([[label]]);
- });
- });
-
- it('creates flash with error message when query is rejected', async () => {
- createComponent({ queryHandler: errorQueryHandler });
- await waitForPromises();
- expect(createAlert).toHaveBeenCalledWith({ message: 'Error fetching labels.' });
- });
- });
-
- it('emits `updateSelectedLabels` event on dropdown contents `setLabels` event if iid is not set', async () => {
- const label = { id: 'gid://gitlab/ProjectLabel/1' };
- createComponent({ config: { ...mockConfig, iid: undefined } });
-
- findDropdownContents().vm.$emit('setLabels', [label]);
- expect(wrapper.emitted('updateSelectedLabels')).toEqual([[{ labels: [label] }]]);
- });
-
- describe.each`
- issuableType
- ${IssuableType.Issue}
- ${IssuableType.MergeRequest}
- ${IssuableType.Epic}
- `('when updating labels for $issuableType', ({ issuableType }) => {
- const label = { id: 'gid://gitlab/ProjectLabel/2' };
-
- it('sets the loading state', async () => {
- createComponent({ issuableType });
- await nextTick();
- findDropdownContents().vm.$emit('setLabels', [label]);
- await nextTick();
-
- expect(findSidebarEditableItem().props('loading')).toBe(true);
- });
-
- it('updates labels correctly after successful mutation', async () => {
- createComponent({ issuableType });
- await nextTick();
- findDropdownContents().vm.$emit('setLabels', [label]);
- await waitForPromises();
-
- expect(findDropdownValue().props('selectedLabels')).toEqual(
- updateLabelsMutationResponse.data.updateIssuableLabels.issuable.labels.nodes,
- );
- });
-
- it('displays an error if mutation was rejected', async () => {
- createComponent({ issuableType, mutationHandler: errorQueryHandler });
- await nextTick();
- findDropdownContents().vm.$emit('setLabels', [label]);
- await waitForPromises();
-
- expect(createAlert).toHaveBeenCalledWith({
- captureError: true,
- error: expect.anything(),
- message: 'An error occurred while updating labels.',
- });
- });
-
- it('emits `updateSelectedLabels` event when the subscription is triggered', async () => {
- createComponent();
- await waitForPromises();
-
- expect(wrapper.emitted('updateSelectedLabels')).toEqual([
- [
- {
- id: '1',
- labels: issuableLabelsSubscriptionResponse.data.issuableLabelsUpdated.labels.nodes,
- },
- ],
- ]);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/todo_button_spec.js b/spec/frontend/vue_shared/components/sidebar/todo_button_spec.js
deleted file mode 100644
index 01958a144ed..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/todo_button_spec.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import { GlButton } from '@gitlab/ui';
-import { shallowMount, mount } from '@vue/test-utils';
-import TodoButton from '~/vue_shared/components/sidebar/todo_toggle/todo_button.vue';
-
-describe('Todo Button', () => {
- let wrapper;
- let dispatchEventSpy;
-
- const createComponent = (props = {}, mountFn = shallowMount) => {
- wrapper = mountFn(TodoButton, {
- propsData: {
- ...props,
- },
- });
- };
-
- beforeEach(() => {
- dispatchEventSpy = jest.spyOn(document, 'dispatchEvent');
- jest.spyOn(document, 'querySelector').mockReturnValue({
- innerText: 2,
- });
- });
-
- afterEach(() => {
- wrapper.destroy();
- dispatchEventSpy = null;
- jest.clearAllMocks();
- });
-
- it('renders GlButton', () => {
- createComponent();
-
- expect(wrapper.findComponent(GlButton).exists()).toBe(true);
- });
-
- it('emits click event when clicked', () => {
- createComponent({}, mount);
- wrapper.findComponent(GlButton).trigger('click');
-
- expect(wrapper.emitted().click).toHaveLength(1);
- });
-
- it('calls dispatchDocumentEvent to update global To-Do counter correctly', () => {
- createComponent({}, mount);
- wrapper.findComponent(GlButton).trigger('click');
- const dispatchedEvent = dispatchEventSpy.mock.calls[0][0];
-
- expect(dispatchEventSpy).toHaveBeenCalledTimes(1);
- expect(dispatchedEvent.detail).toEqual({ count: 1 });
- expect(dispatchedEvent.type).toBe('todo:toggle');
- });
-
- it.each`
- label | isTodo
- ${'Mark as done'} | ${true}
- ${'Add a to do'} | ${false}
- `('sets correct label when isTodo is $isTodo', ({ label, isTodo }) => {
- createComponent({ isTodo });
-
- expect(wrapper.findComponent(GlButton).text()).toBe(label);
- });
-
- it('binds additional props to GlButton', () => {
- createComponent({ loading: true });
-
- expect(wrapper.findComponent(GlButton).props('loading')).toBe(true);
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/toggle_sidebar_spec.js b/spec/frontend/vue_shared/components/sidebar/toggle_sidebar_spec.js
deleted file mode 100644
index 267a467059d..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/toggle_sidebar_spec.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import { GlButton } from '@gitlab/ui';
-import { mount, shallowMount } from '@vue/test-utils';
-
-import { nextTick } from 'vue';
-import ToggleSidebar from '~/vue_shared/components/sidebar/toggle_sidebar.vue';
-
-describe('ToggleSidebar', () => {
- let wrapper;
-
- const defaultProps = {
- collapsed: true,
- };
-
- const createComponent = ({ mountFn = shallowMount, props = {} } = {}) => {
- wrapper = mountFn(ToggleSidebar, {
- propsData: { ...defaultProps, ...props },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findGlButton = () => wrapper.findComponent(GlButton);
-
- it('should render the "chevron-double-lg-left" icon when collapsed', () => {
- createComponent();
-
- expect(findGlButton().props('icon')).toBe('chevron-double-lg-left');
- });
-
- it('should render the "chevron-double-lg-right" icon when expanded', async () => {
- createComponent({ props: { collapsed: false } });
-
- expect(findGlButton().props('icon')).toBe('chevron-double-lg-right');
- });
-
- it('should emit toggle event when button clicked', async () => {
- createComponent({ mountFn: mount });
-
- findGlButton().trigger('click');
- await nextTick();
-
- expect(wrapper.emitted('toggle')[0]).toBeDefined();
- });
-});
diff --git a/spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js b/spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js
index d720574ce6d..657bd59dac6 100644
--- a/spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js
+++ b/spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js
@@ -1,10 +1,16 @@
+import { nextTick } from 'vue';
import { GlIntersectionObserver } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import Chunk from '~/vue_shared/components/source_viewer/components/chunk.vue';
import ChunkLine from '~/vue_shared/components/source_viewer/components/chunk_line.vue';
-import { scrollToElement } from '~/lib/utils/common_utils';
+import LineHighlighter from '~/blob/line_highlighter';
-jest.mock('~/lib/utils/common_utils');
+const lineHighlighter = new LineHighlighter();
+jest.mock('~/blob/line_highlighter', () =>
+ jest.fn().mockReturnValue({
+ highlightHash: jest.fn(),
+ }),
+);
const DEFAULT_PROPS = {
chunkIndex: 2,
@@ -104,12 +110,14 @@ describe('Chunk component', () => {
});
it('does not scroll to route hash if last chunk is not loaded', () => {
- expect(scrollToElement).not.toHaveBeenCalled();
+ expect(LineHighlighter).not.toHaveBeenCalled();
});
- it('scrolls to route hash if last chunk is loaded', () => {
+ it('scrolls to route hash if last chunk is loaded', async () => {
createComponent({ totalChunks: DEFAULT_PROPS.chunkIndex + 1 });
- expect(scrollToElement).toHaveBeenCalledWith(hash, { behavior: 'auto' });
+ await nextTick();
+ expect(LineHighlighter).toHaveBeenCalledWith({ scrollBehavior: 'auto' });
+ expect(lineHighlighter.highlightHash).toHaveBeenCalledWith(hash);
});
});
});
diff --git a/spec/frontend/vue_shared/components/source_viewer/plugins/link_dependencies_spec.js b/spec/frontend/vue_shared/components/source_viewer/plugins/link_dependencies_spec.js
index a7b55d7332f..4d38e8ef25d 100644
--- a/spec/frontend/vue_shared/components/source_viewer/plugins/link_dependencies_spec.js
+++ b/spec/frontend/vue_shared/components/source_viewer/plugins/link_dependencies_spec.js
@@ -4,15 +4,16 @@ import gemspecLinker from '~/vue_shared/components/source_viewer/plugins/utils/g
import gemfileLinker from '~/vue_shared/components/source_viewer/plugins/utils/gemfile_linker';
import podspecJsonLinker from '~/vue_shared/components/source_viewer/plugins/utils/podspec_json_linker';
import composerJsonLinker from '~/vue_shared/components/source_viewer/plugins/utils/composer_json_linker';
+import goSumLinker from '~/vue_shared/components/source_viewer/plugins/utils/go_sum_linker';
import linkDependencies from '~/vue_shared/components/source_viewer/plugins/link_dependencies';
import {
PACKAGE_JSON_FILE_TYPE,
- PACKAGE_JSON_CONTENT,
GEMSPEC_FILE_TYPE,
GODEPS_JSON_FILE_TYPE,
GEMFILE_FILE_TYPE,
PODSPEC_JSON_FILE_TYPE,
COMPOSER_JSON_FILE_TYPE,
+ GO_SUM_FILE_TYPE,
} from './mock_data';
jest.mock('~/vue_shared/components/source_viewer/plugins/utils/package_json_linker');
@@ -21,37 +22,31 @@ jest.mock('~/vue_shared/components/source_viewer/plugins/utils/godeps_json_linke
jest.mock('~/vue_shared/components/source_viewer/plugins/utils/gemfile_linker');
jest.mock('~/vue_shared/components/source_viewer/plugins/utils/podspec_json_linker');
jest.mock('~/vue_shared/components/source_viewer/plugins/utils/composer_json_linker');
+jest.mock('~/vue_shared/components/source_viewer/plugins/utils/go_sum_linker');
describe('Highlight.js plugin for linking dependencies', () => {
const hljsResultMock = { value: 'test' };
- it('calls packageJsonLinker for package_json file types', () => {
- linkDependencies(hljsResultMock, PACKAGE_JSON_FILE_TYPE, PACKAGE_JSON_CONTENT);
- expect(packageJsonLinker).toHaveBeenCalled();
- });
-
- it('calls gemspecLinker for gemspec file types', () => {
- linkDependencies(hljsResultMock, GEMSPEC_FILE_TYPE);
- expect(gemspecLinker).toHaveBeenCalled();
- });
-
- it('calls godepsJsonLinker for godeps_json file types', () => {
- linkDependencies(hljsResultMock, GODEPS_JSON_FILE_TYPE);
- expect(godepsJsonLinker).toHaveBeenCalled();
- });
-
- it('calls gemfileLinker for gemfile file types', () => {
- linkDependencies(hljsResultMock, GEMFILE_FILE_TYPE);
- expect(gemfileLinker).toHaveBeenCalled();
- });
-
- it('calls podspecJsonLinker for podspec_json file types', () => {
- linkDependencies(hljsResultMock, PODSPEC_JSON_FILE_TYPE);
- expect(podspecJsonLinker).toHaveBeenCalled();
- });
-
- it('calls composerJsonLinker for composer_json file types', () => {
- linkDependencies(hljsResultMock, COMPOSER_JSON_FILE_TYPE);
- expect(composerJsonLinker).toHaveBeenCalled();
+ describe.each`
+ fileType | linker
+ ${PACKAGE_JSON_FILE_TYPE} | ${packageJsonLinker}
+ ${GEMSPEC_FILE_TYPE} | ${gemspecLinker}
+ ${GODEPS_JSON_FILE_TYPE} | ${godepsJsonLinker}
+ ${GEMFILE_FILE_TYPE} | ${gemfileLinker}
+ ${PODSPEC_JSON_FILE_TYPE} | ${podspecJsonLinker}
+ ${COMPOSER_JSON_FILE_TYPE} | ${composerJsonLinker}
+ ${GO_SUM_FILE_TYPE} | ${goSumLinker}
+ `('$fileType file type', ({ fileType, linker }) => {
+ it('calls the correct linker', () => {
+ linkDependencies(hljsResultMock, fileType);
+ expect(linker).toHaveBeenCalled();
+ });
+
+ it('does not call the linker for non-matching file types', () => {
+ const unknownFileType = 'unknown';
+
+ linkDependencies(hljsResultMock, unknownFileType);
+ expect(linker).not.toHaveBeenCalled();
+ });
});
});
diff --git a/spec/frontend/vue_shared/components/source_viewer/plugins/mock_data.js b/spec/frontend/vue_shared/components/source_viewer/plugins/mock_data.js
index 5455479ec71..631baf19a2d 100644
--- a/spec/frontend/vue_shared/components/source_viewer/plugins/mock_data.js
+++ b/spec/frontend/vue_shared/components/source_viewer/plugins/mock_data.js
@@ -32,3 +32,5 @@ export const PODSPEC_JSON_CONTENT = `{
}`;
export const COMPOSER_JSON_FILE_TYPE = 'composer_json';
+
+export const GO_SUM_FILE_TYPE = 'go_sum';
diff --git a/spec/frontend/vue_shared/components/source_viewer/plugins/utils/go_sum_linker_spec.js b/spec/frontend/vue_shared/components/source_viewer/plugins/utils/go_sum_linker_spec.js
new file mode 100644
index 00000000000..cc3ee41523f
--- /dev/null
+++ b/spec/frontend/vue_shared/components/source_viewer/plugins/utils/go_sum_linker_spec.js
@@ -0,0 +1,14 @@
+import goSumLinker from '~/vue_shared/components/source_viewer/plugins/utils/go_sum_linker';
+
+describe('Highlight.js plugin for linking go.sum dependencies', () => {
+ it('mutates the input value by wrapping dependencies and tags in anchors', () => {
+ const inputValue =
+ '<span class="">cloud.google.com/Go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=</span>';
+ const outputValue =
+ '<span class=""><a href="https://pkg.go.dev/cloud.google.com/go/bigquery" target="_blank" rel="nofollow noreferrer noopener">cloud.google.com/Go/bigquery</a> v1.0.1/go.mod h1:<a href="https://sum.golang.org/lookup/cloud.google.com/go/bigquery@v1.0.1" target="_blank" rel="nofollow noreferrer noopener">i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=</a></span>';
+ const hljsResultMock = { value: inputValue };
+
+ const output = goSumLinker(hljsResultMock);
+ expect(output).toBe(outputValue);
+ });
+});
diff --git a/spec/frontend/vue_shared/components/user_select_spec.js b/spec/frontend/vue_shared/components/user_select_spec.js
index 4188adc72a1..874796f653a 100644
--- a/spec/frontend/vue_shared/components/user_select_spec.js
+++ b/spec/frontend/vue_shared/components/user_select_spec.js
@@ -10,7 +10,7 @@ import searchUsersQueryOnMR from '~/graphql_shared/queries/users_search_with_mr_
import { IssuableType } from '~/issues/constants';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import SidebarParticipant from '~/sidebar/components/assignees/sidebar_participant.vue';
-import getIssueParticipantsQuery from '~/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql';
+import getIssueParticipantsQuery from '~/sidebar/queries/get_issue_participants.query.graphql';
import UserSelect from '~/vue_shared/components/user_select/user_select.vue';
import {
searchResponse,
diff --git a/spec/frontend/vue_shared/components/web_ide_link_spec.js b/spec/frontend/vue_shared/components/web_ide_link_spec.js
index a0b868d1d52..3b0f0fe6e73 100644
--- a/spec/frontend/vue_shared/components/web_ide_link_spec.js
+++ b/spec/frontend/vue_shared/components/web_ide_link_spec.js
@@ -1,13 +1,20 @@
-import { GlModal } from '@gitlab/ui';
+import { GlButton, GlModal, GlPopover } from '@gitlab/ui';
import { nextTick } from 'vue';
import ActionsButton from '~/vue_shared/components/actions_button.vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
-import WebIdeLink, { i18n } from '~/vue_shared/components/web_ide_link.vue';
+import WebIdeLink, {
+ i18n,
+ PREFERRED_EDITOR_RESET_KEY,
+ PREFERRED_EDITOR_KEY,
+ KEY_WEB_IDE,
+} from '~/vue_shared/components/web_ide_link.vue';
import ConfirmForkModal from '~/vue_shared/components/confirm_fork_modal.vue';
+import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
import { stubComponent } from 'helpers/stub_component';
import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
+import { useLocalStorageSpy } from 'helpers/local_storage_helper';
const TEST_EDIT_URL = '/gitlab-test/test/-/edit/main/';
const TEST_WEB_IDE_URL = '/-/ide/project/gitlab-test/test/edit/main/-/';
@@ -79,9 +86,18 @@ const ACTION_PIPELINE_EDITOR = {
};
describe('Web IDE link component', () => {
+ useLocalStorageSpy();
+
let wrapper;
- function createComponent(props, mountFn = shallowMountExtended) {
+ function createComponent(
+ props,
+ {
+ mountFn = shallowMountExtended,
+ glFeatures = {},
+ userCalloutDismisserSlotProps = { dismiss: jest.fn() },
+ } = {},
+ ) {
wrapper = mountFn(WebIdeLink, {
propsData: {
editUrl: TEST_EDIT_URL,
@@ -91,6 +107,9 @@ describe('Web IDE link component', () => {
forkPath,
...props,
},
+ provide: {
+ glFeatures,
+ },
stubs: {
GlModal: stubComponent(GlModal, {
template: `
@@ -100,10 +119,19 @@ describe('Web IDE link component', () => {
<slot name="modal-footer"></slot>
</div>`,
}),
+ UserCalloutDismisser: stubComponent(UserCalloutDismisser, {
+ render() {
+ return this.$scopedSlots.default(userCalloutDismisserSlotProps);
+ },
+ }),
},
});
}
+ beforeEach(() => {
+ localStorage.setItem(PREFERRED_EDITOR_RESET_KEY, 'true');
+ });
+
afterEach(() => {
wrapper.destroy();
});
@@ -112,6 +140,8 @@ describe('Web IDE link component', () => {
const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
const findModal = () => wrapper.findComponent(GlModal);
const findForkConfirmModal = () => wrapper.findComponent(ConfirmForkModal);
+ const findUserCalloutDismisser = () => wrapper.findComponent(UserCalloutDismisser);
+ const findNewWebIdeCalloutPopover = () => wrapper.findComponent(GlPopover);
it.each([
{
@@ -322,9 +352,9 @@ describe('Web IDE link component', () => {
});
it.each(testActions)('opens the modal when the button is clicked', async ({ props }) => {
- createComponent({ ...props, needsToFork: true }, mountExtended);
+ createComponent({ ...props, needsToFork: true }, { mountFn: mountExtended });
- await findActionsButton().trigger('click');
+ await findActionsButton().findComponent(GlButton).trigger('click');
expect(findForkConfirmModal().props()).toEqual({
visible: true,
@@ -377,7 +407,7 @@ describe('Web IDE link component', () => {
gitpodEnabled: false,
gitpodText,
},
- mountExtended,
+ { mountFn: mountExtended },
);
findLocalStorageSync().vm.$emit('input', ACTION_GITPOD.key);
@@ -401,4 +431,178 @@ describe('Web IDE link component', () => {
expect(findModal().exists()).toBe(false);
});
});
+
+ describe('Web IDE callout', () => {
+ describe('vscode_web_ide feature flag is enabled and the edit button is not shown', () => {
+ let dismiss;
+
+ beforeEach(() => {
+ dismiss = jest.fn();
+ createComponent(
+ {
+ showEditButton: false,
+ },
+ {
+ glFeatures: { vscodeWebIde: true },
+ userCalloutDismisserSlotProps: { dismiss },
+ },
+ );
+ });
+ it('does not skip the user_callout_dismisser query', () => {
+ expect(findUserCalloutDismisser().props()).toEqual(
+ expect.objectContaining({
+ skipQuery: false,
+ featureName: 'vscode_web_ide_callout',
+ }),
+ );
+ });
+
+ it('mounts new web ide callout popover', () => {
+ expect(findNewWebIdeCalloutPopover().props()).toEqual(
+ expect.objectContaining({
+ showCloseButton: '',
+ target: 'web-ide-link',
+ triggers: 'manual',
+ boundaryPadding: 80,
+ }),
+ );
+ });
+
+ describe.each`
+ calloutStatus | shouldShowCallout | popoverVisibility | tooltipVisibility
+ ${'show'} | ${true} | ${true} | ${false}
+ ${'hide'} | ${false} | ${false} | ${true}
+ `(
+ 'when should $calloutStatus web ide callout',
+ ({ shouldShowCallout, popoverVisibility, tooltipVisibility }) => {
+ beforeEach(() => {
+ createComponent(
+ {
+ showEditButton: false,
+ },
+ {
+ glFeatures: { vscodeWebIde: true },
+ userCalloutDismisserSlotProps: { shouldShowCallout, dismiss },
+ },
+ );
+ });
+
+ it(`popover visibility = ${popoverVisibility}`, () => {
+ expect(findNewWebIdeCalloutPopover().props().show).toBe(popoverVisibility);
+ });
+
+ it(`action button tooltip visibility = ${tooltipVisibility}`, () => {
+ expect(findActionsButton().props().showActionTooltip).toBe(tooltipVisibility);
+ });
+ },
+ );
+
+ it('dismisses the callout when popover close button is clicked', () => {
+ findNewWebIdeCalloutPopover().vm.$emit('close-button-clicked');
+
+ expect(dismiss).toHaveBeenCalled();
+ });
+
+ it('dismisses the callout when action button is clicked', () => {
+ findActionsButton().vm.$emit('actionClicked');
+
+ expect(dismiss).toHaveBeenCalled();
+ });
+ });
+
+ describe.each`
+ featureFlag | showEditButton
+ ${false} | ${true}
+ ${true} | ${false}
+ ${false} | ${false}
+ `(
+ 'when vscode_web_ide=$featureFlag and showEditButton = $showEditButton',
+ ({ vscodeWebIde, showEditButton }) => {
+ let dismiss;
+
+ beforeEach(() => {
+ dismiss = jest.fn();
+
+ createComponent(
+ {
+ showEditButton,
+ },
+ { glFeatures: { vscodeWebIde }, userCalloutDismisserSlotProps: { dismiss } },
+ );
+ });
+
+ it('skips the user_callout_dismisser query', () => {
+ expect(findUserCalloutDismisser().props().skipQuery).toBe(true);
+ });
+
+ it('displays actions button tooltip', () => {
+ expect(findActionsButton().props().showActionTooltip).toBe(true);
+ });
+
+ it('mounts new web ide callout popover', () => {
+ expect(findNewWebIdeCalloutPopover().exists()).toBe(false);
+ });
+
+ it('does not dismiss the callout when action button is clicked', () => {
+ findActionsButton().vm.$emit('actionClicked');
+
+ expect(dismiss).not.toHaveBeenCalled();
+ });
+ },
+ );
+ });
+
+ describe('when vscode_web_ide feature flag is enabled', () => {
+ describe('when is not showing edit button', () => {
+ describe(`when ${PREFERRED_EDITOR_RESET_KEY} is unset`, () => {
+ beforeEach(() => {
+ localStorage.setItem.mockReset();
+ localStorage.getItem.mockReturnValueOnce(null);
+ createComponent({ showEditButton: false }, { glFeatures: { vscodeWebIde: true } });
+ });
+
+ it(`sets ${PREFERRED_EDITOR_KEY} local storage key to ${KEY_WEB_IDE}`, () => {
+ expect(localStorage.getItem).toHaveBeenCalledWith(PREFERRED_EDITOR_RESET_KEY);
+ expect(localStorage.setItem).toHaveBeenCalledWith(PREFERRED_EDITOR_KEY, KEY_WEB_IDE);
+ });
+
+ it(`sets ${PREFERRED_EDITOR_RESET_KEY} local storage key to true`, () => {
+ expect(localStorage.setItem).toHaveBeenCalledWith(PREFERRED_EDITOR_RESET_KEY, true);
+ });
+
+ it(`selects ${KEY_WEB_IDE} as the preferred editor`, () => {
+ expect(findActionsButton().props().selectedKey).toBe(KEY_WEB_IDE);
+ });
+ });
+
+ describe(`when ${PREFERRED_EDITOR_RESET_KEY} is set to true`, () => {
+ beforeEach(() => {
+ localStorage.setItem.mockReset();
+ localStorage.getItem.mockReturnValueOnce('true');
+ createComponent({ showEditButton: false }, { glFeatures: { vscodeWebIde: true } });
+ });
+
+ it(`does not update the persisted preferred editor`, () => {
+ expect(localStorage.getItem).toHaveBeenCalledWith(PREFERRED_EDITOR_RESET_KEY);
+ expect(localStorage.setItem).not.toHaveBeenCalledWith(PREFERRED_EDITOR_RESET_KEY);
+ });
+ });
+ });
+
+ describe('when is showing the edit button', () => {
+ it(`does not try to reset the ${PREFERRED_EDITOR_KEY}`, () => {
+ createComponent({ showEditButton: true }, { glFeatures: { vscodeWebIde: true } });
+
+ expect(localStorage.getItem).not.toHaveBeenCalledWith(PREFERRED_EDITOR_RESET_KEY);
+ });
+ });
+ });
+
+ describe('when vscode_web_ide feature flag is disabled', () => {
+ it(`does not try to reset the ${PREFERRED_EDITOR_KEY}`, () => {
+ createComponent({}, { glFeatures: { vscodeWebIde: false } });
+
+ expect(localStorage.getItem).not.toHaveBeenCalledWith(PREFERRED_EDITOR_RESET_KEY);
+ });
+ });
});
diff --git a/spec/frontend/vue_shared/issuable/create/components/issuable_form_spec.js b/spec/frontend/vue_shared/issuable/create/components/issuable_form_spec.js
index f98e7a678f4..ff21b3bc356 100644
--- a/spec/frontend/vue_shared/issuable/create/components/issuable_form_spec.js
+++ b/spec/frontend/vue_shared/issuable/create/components/issuable_form_spec.js
@@ -3,7 +3,7 @@ import { shallowMount } from '@vue/test-utils';
import IssuableForm from '~/vue_shared/issuable/create/components/issuable_form.vue';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
-import LabelsSelect from '~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue';
+import LabelsSelect from '~/sidebar/components/labels/labels_select_vue/labels_select_root.vue';
const createComponent = ({
descriptionPreviewPath = '/gitlab-org/gitlab-shell/preview_markdown',
diff --git a/spec/frontend/vue_shared/issuable/create/components/issuable_label_selector_spec.js b/spec/frontend/vue_shared/issuable/create/components/issuable_label_selector_spec.js
new file mode 100644
index 00000000000..76b6efa15b6
--- /dev/null
+++ b/spec/frontend/vue_shared/issuable/create/components/issuable_label_selector_spec.js
@@ -0,0 +1,141 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlIcon } from '@gitlab/ui';
+import {
+ mockRegularLabel,
+ mockScopedLabel,
+} from 'jest/sidebar/components/labels/labels_select_widget/mock_data';
+import IssuableLabelSelector from '~/vue_shared/issuable/create/components/issuable_label_selector.vue';
+import LabelsSelect from '~/sidebar/components/labels/labels_select_widget/labels_select_root.vue';
+import {
+ DropdownVariant,
+ LabelType,
+} from '~/sidebar/components/labels/labels_select_widget/constants';
+import { WorkspaceType } from '~/issues/constants';
+import { __ } from '~/locale';
+
+const allowLabelRemove = true;
+const attrWorkspacePath = '/workspace-path';
+const fieldName = 'field_name[]';
+const fullPath = '/full-path';
+const labelsFilterBasePath = '/labels-filter-base-path';
+const initialLabels = [];
+const issuableType = 'issue';
+const labelType = LabelType.project;
+const variant = DropdownVariant.Embedded;
+const workspaceType = WorkspaceType.project;
+
+describe('IssuableLabelSelector', () => {
+ let wrapper;
+
+ const findTitle = () => wrapper.find('label').text().replace(/\s+/, ' ');
+ const findLabelIcon = () => wrapper.findComponent(GlIcon);
+ const findAllHiddenInputs = () => wrapper.findAll('input[type="hidden"]');
+ const findLabelSelector = () => wrapper.findComponent(LabelsSelect);
+
+ const createComponent = (injectedProps = {}) => {
+ return shallowMount(IssuableLabelSelector, {
+ provide: {
+ allowLabelRemove,
+ attrWorkspacePath,
+ fieldName,
+ fullPath,
+ labelsFilterBasePath,
+ initialLabels,
+ issuableType,
+ labelType,
+ variant,
+ workspaceType,
+ ...injectedProps,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const expectTitleWithCount = (count) => {
+ const title = findTitle();
+
+ expect(title).toContain(__('Labels'));
+ expect(title).toContain(count.toString());
+ };
+
+ describe('by default', () => {
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ it('has the selected labels count', () => {
+ expectTitleWithCount(0);
+ expect(findLabelIcon().props('name')).toBe('labels');
+ });
+
+ it('has the label selector', () => {
+ expect(findLabelSelector().props()).toMatchObject({
+ allowLabelRemove,
+ allowMultiselect: true,
+ showEmbeddedLabelsList: true,
+ fullPath,
+ attrWorkspacePath,
+ labelsFilterBasePath,
+ dropdownButtonText: __('Select label'),
+ labelsListTitle: __('Select label'),
+ footerCreateLabelTitle: __('Create project label'),
+ footerManageLabelTitle: __('Manage project labels'),
+ variant,
+ workspaceType,
+ labelCreateType: labelType,
+ selectedLabels: initialLabels,
+ });
+
+ expect(findLabelSelector().text()).toBe(__('None'));
+ });
+ });
+
+ it('passing initial labels applies them to the form', () => {
+ wrapper = createComponent({ initialLabels: [mockRegularLabel, mockScopedLabel] });
+
+ expectTitleWithCount(2);
+ expect(findLabelSelector().props('selectedLabels')).toStrictEqual([
+ mockRegularLabel,
+ mockScopedLabel,
+ ]);
+ expect(findAllHiddenInputs().wrappers.map((input) => input.element.value)).toStrictEqual([
+ `${mockRegularLabel.id}`,
+ `${mockScopedLabel.id}`,
+ ]);
+ });
+
+ it('updates the selected labels on the `updateSelectedLabels` event', async () => {
+ wrapper = createComponent();
+
+ expectTitleWithCount(0);
+ expect(findLabelSelector().props('selectedLabels')).toStrictEqual([]);
+ expect(findAllHiddenInputs()).toHaveLength(0);
+
+ await findLabelSelector().vm.$emit('updateSelectedLabels', { labels: [mockRegularLabel] });
+
+ expectTitleWithCount(1);
+ expect(findLabelSelector().props('selectedLabels')).toStrictEqual([mockRegularLabel]);
+ expect(findAllHiddenInputs().wrappers.map((input) => input.element.value)).toStrictEqual([
+ `${mockRegularLabel.id}`,
+ ]);
+ });
+
+ it('updates the selected labels on the `onLabelRemove` event', async () => {
+ wrapper = createComponent({ initialLabels: [mockRegularLabel] });
+
+ expectTitleWithCount(1);
+ expect(findLabelSelector().props('selectedLabels')).toStrictEqual([mockRegularLabel]);
+ expect(findAllHiddenInputs().wrappers.map((input) => input.element.value)).toStrictEqual([
+ `${mockRegularLabel.id}`,
+ ]);
+
+ await findLabelSelector().vm.$emit('onLabelRemove', mockRegularLabel.id);
+
+ expectTitleWithCount(0);
+ expect(findLabelSelector().props('selectedLabels')).toStrictEqual([]);
+ expect(findAllHiddenInputs()).toHaveLength(0);
+ });
+});
diff --git a/spec/frontend/vue_shared/issuable/list/components/issuable_item_spec.js b/spec/frontend/vue_shared/issuable/list/components/issuable_item_spec.js
index e1c6020686c..2fac004875a 100644
--- a/spec/frontend/vue_shared/issuable/list/components/issuable_item_spec.js
+++ b/spec/frontend/vue_shared/issuable/list/components/issuable_item_spec.js
@@ -225,7 +225,7 @@ describe('IssuableItem', () => {
},
});
- expect(wrapper.findByTestId('issuable-discussions').exists()).toBe(returnValue);
+ expect(wrapper.findByTestId('issuable-comments').exists()).toBe(returnValue);
},
);
});
@@ -489,7 +489,7 @@ describe('IssuableItem', () => {
it('renders discussions count', () => {
wrapper = createComponent();
- const discussionsEl = wrapper.find('[data-testid="issuable-discussions"]');
+ const discussionsEl = wrapper.findByTestId('issuable-comments');
expect(discussionsEl.exists()).toBe(true);
expect(discussionsEl.findComponent(GlLink).attributes()).toMatchObject({
diff --git a/spec/frontend/vue_shared/issuable/show/components/issuable_description_spec.js b/spec/frontend/vue_shared/issuable/show/components/issuable_description_spec.js
index f2211e5b2bb..ea58cc2baf5 100644
--- a/spec/frontend/vue_shared/issuable/show/components/issuable_description_spec.js
+++ b/spec/frontend/vue_shared/issuable/show/components/issuable_description_spec.js
@@ -1,10 +1,12 @@
import { shallowMount } from '@vue/test-utils';
-import $ from 'jquery';
import IssuableDescription from '~/vue_shared/issuable/show/components/issuable_description.vue';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import { mockIssuable } from '../mock_data';
+jest.mock('~/behaviors/markdown/render_gfm');
+
const createComponent = ({
issuable = mockIssuable,
enableTaskList = true,
@@ -16,11 +18,9 @@ const createComponent = ({
});
describe('IssuableDescription', () => {
- let renderGFMSpy;
let wrapper;
beforeEach(() => {
- renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
wrapper = createComponent();
});
@@ -30,17 +30,7 @@ describe('IssuableDescription', () => {
describe('mounted', () => {
it('calls `renderGFM`', () => {
- expect(renderGFMSpy).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('methods', () => {
- describe('renderGFM', () => {
- it('calls `renderGFM` on container element', () => {
- wrapper.vm.renderGFM();
-
- expect(renderGFMSpy).toHaveBeenCalled();
- });
+ expect(renderGFM).toHaveBeenCalledTimes(1);
});
});
diff --git a/spec/frontend/webhooks/components/__snapshots__/push_events_spec.js.snap b/spec/frontend/webhooks/components/__snapshots__/push_events_spec.js.snap
index 3dbff024a6b..aec0f84cb82 100644
--- a/spec/frontend/webhooks/components/__snapshots__/push_events_spec.js.snap
+++ b/spec/frontend/webhooks/components/__snapshots__/push_events_spec.js.snap
@@ -141,7 +141,7 @@ exports[`Webhook push events form editor component Different push events rules w
class="form-text text-muted custom-control"
>
<gl-sprintf-stub
- message="Regex such as %{REGEX_CODE} is supported."
+ message="Regular expressions such as %{REGEX_CODE} are supported."
/>
</p>
</gl-form-radio-group-stub>
@@ -367,7 +367,7 @@ exports[`Webhook push events form editor component Different push events rules w
class="form-text text-muted custom-control"
>
<gl-sprintf-stub
- message="Regex such as %{REGEX_CODE} is supported."
+ message="Regular expressions such as %{REGEX_CODE} are supported."
/>
</p>
</gl-form-radio-group-stub>
diff --git a/spec/frontend/work_items/components/notes/system_note_spec.js b/spec/frontend/work_items/components/notes/system_note_spec.js
new file mode 100644
index 00000000000..3e3b8bf65b2
--- /dev/null
+++ b/spec/frontend/work_items/components/notes/system_note_spec.js
@@ -0,0 +1,111 @@
+import { GlIcon } from '@gitlab/ui';
+import MockAdapter from 'axios-mock-adapter';
+import { shallowMount } from '@vue/test-utils';
+import waitForPromises from 'helpers/wait_for_promises';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
+import WorkItemSystemNote from '~/work_items/components/notes/system_note.vue';
+import NoteHeader from '~/notes/components/note_header.vue';
+import axios from '~/lib/utils/axios_utils';
+
+jest.mock('~/behaviors/markdown/render_gfm');
+
+describe('system note component', () => {
+ let wrapper;
+ let props;
+ let mock;
+
+ const findTimelineIcon = () => wrapper.findComponent(GlIcon);
+ const findSystemNoteMessage = () => wrapper.findComponent(NoteHeader);
+ const findOutdatedLineButton = () =>
+ wrapper.findComponent('[data-testid="outdated-lines-change-btn"]');
+ const findOutdatedLines = () => wrapper.findComponent('[data-testid="outdated-lines"]');
+
+ const createComponent = (propsData = {}) => {
+ wrapper = shallowMount(WorkItemSystemNote, {
+ propsData,
+ slots: {
+ 'extra-controls':
+ '<gl-button data-testid="outdated-lines-change-btn">Compare with last version</gl-button>',
+ },
+ });
+ };
+
+ beforeEach(() => {
+ props = {
+ note: {
+ id: '1424',
+ author: {
+ id: 1,
+ name: 'Root',
+ username: 'root',
+ state: 'active',
+ avatarUrl: 'path',
+ path: '/root',
+ },
+ bodyHtml: '<p dir="auto">closed</p>',
+ systemNoteIconName: 'status_closed',
+ createdAt: '2017-08-02T10:51:58.559Z',
+ },
+ };
+
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ it('should render a list item with correct id', () => {
+ createComponent(props);
+
+ expect(wrapper.attributes('id')).toBe(`note_${props.note.id}`);
+ });
+
+ // Note: The test case below is to handle a use case related to vuex store but since this does not
+ // have a vuex store , disabling it now will be fixing it in the next iteration
+ // eslint-disable-next-line jest/no-disabled-tests
+ it.skip('should render target class is note is target note', () => {
+ createComponent(props);
+
+ expect(wrapper.classes()).toContain('target');
+ });
+
+ it('should render svg icon', () => {
+ createComponent(props);
+
+ expect(findTimelineIcon().exists()).toBe(true);
+ });
+
+ // Redcarpet Markdown renderer wraps text in `<p>` tags
+ // we need to strip them because they break layout of commit lists in system notes:
+ // https://gitlab.com/gitlab-org/gitlab-foss/uploads/b07a10670919254f0220d3ff5c1aa110/jqzI.png
+ it('removes wrapping paragraph from note HTML', () => {
+ createComponent(props);
+
+ expect(findSystemNoteMessage().html()).toContain('<span>closed</span>');
+ });
+
+ it('should renderGFM onMount', () => {
+ createComponent(props);
+
+ expect(renderGFM).toHaveBeenCalled();
+ });
+
+ // eslint-disable-next-line jest/no-disabled-tests
+ it.skip('renders outdated code lines', async () => {
+ mock
+ .onGet('/outdated_line_change_path')
+ .reply(200, [
+ { rich_text: 'console.log', type: 'new', line_code: '123', old_line: null, new_line: 1 },
+ ]);
+
+ createComponent({
+ note: { ...props.note, outdated_line_change_path: '/outdated_line_change_path' },
+ });
+
+ await findOutdatedLineButton().vm.$emit('click');
+ await waitForPromises();
+
+ expect(findOutdatedLines().exists()).toBe(true);
+ });
+});
diff --git a/spec/frontend/work_items/components/work_item_assignees_spec.js b/spec/frontend/work_items/components/work_item_assignees_spec.js
index 7367212e49f..e85f62b881d 100644
--- a/spec/frontend/work_items/components/work_item_assignees_spec.js
+++ b/spec/frontend/work_items/components/work_item_assignees_spec.js
@@ -435,6 +435,20 @@ describe('WorkItemAssignees component', () => {
expect(findTokenSelector().props('containerClass')).toBe('gl-shadow-none!');
});
+
+ it('calls the mutation for updating assignees with the correct input', async () => {
+ findTokenSelector().vm.$emit('input', [mockAssignees[1]]);
+ await waitForPromises();
+
+ expect(successUpdateWorkItemMutationHandler).toHaveBeenCalledWith({
+ input: {
+ assigneesWidget: {
+ assigneeIds: [mockAssignees[1].id],
+ },
+ id: 'gid://gitlab/WorkItem/1',
+ },
+ });
+ });
});
describe('tracking', () => {
diff --git a/spec/frontend/work_items/components/work_item_description_rendered_spec.js b/spec/frontend/work_items/components/work_item_description_rendered_spec.js
index 01ab7824975..0ab2546440b 100644
--- a/spec/frontend/work_items/components/work_item_description_rendered_spec.js
+++ b/spec/frontend/work_items/components/work_item_description_rendered_spec.js
@@ -1,9 +1,11 @@
import { shallowMount } from '@vue/test-utils';
-import $ from 'jquery';
import { nextTick } from 'vue';
import WorkItemDescriptionRendered from '~/work_items/components/work_item_description_rendered.vue';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import { descriptionTextWithCheckboxes, descriptionHtmlWithCheckboxes } from '../mock_data';
+jest.mock('~/behaviors/markdown/render_gfm');
+
describe('WorkItemDescription', () => {
let wrapper;
@@ -32,13 +34,11 @@ describe('WorkItemDescription', () => {
});
it('renders gfm', async () => {
- const renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
-
createComponent();
await nextTick();
- expect(renderGFMSpy).toHaveBeenCalled();
+ expect(renderGFM).toHaveBeenCalled();
});
describe('with checkboxes', () => {
diff --git a/spec/frontend/work_items/components/work_item_description_spec.js b/spec/frontend/work_items/components/work_item_description_spec.js
index c79b049442d..05476ef5ca0 100644
--- a/spec/frontend/work_items/components/work_item_description_spec.js
+++ b/spec/frontend/work_items/components/work_item_description_spec.js
@@ -38,7 +38,7 @@ describe('WorkItemDescription', () => {
const subscriptionHandler = jest.fn().mockResolvedValue(workItemDescriptionSubscriptionResponse);
const workItemByIidResponseHandler = jest.fn().mockResolvedValue(projectWorkItemResponse);
let workItemResponseHandler;
- let workItemsMvc2;
+ let workItemsMvc;
const findMarkdownField = () => wrapper.findComponent(MarkdownField);
const findMarkdownEditor = () => wrapper.findComponent(MarkdownEditor);
@@ -46,7 +46,7 @@ describe('WorkItemDescription', () => {
const findEditedAt = () => wrapper.findComponent(EditedAt);
const editDescription = (newText) => {
- if (workItemsMvc2) {
+ if (workItemsMvc) {
return findMarkdownEditor().vm.$emit('input', newText);
}
return wrapper.find('textarea').setValue(newText);
@@ -60,6 +60,7 @@ describe('WorkItemDescription', () => {
canUpdate = true,
workItemResponse = workItemResponseFactory({ canUpdate }),
isEditing = false,
+ queryVariables = { id: workItemId },
fetchByIid = false,
} = {}) => {
workItemResponseHandler = jest.fn().mockResolvedValue(workItemResponse);
@@ -75,14 +76,12 @@ describe('WorkItemDescription', () => {
propsData: {
workItemId: id,
fullPath: 'test-project-path',
- queryVariables: {
- id: workItemId,
- },
+ queryVariables,
fetchByIid,
},
provide: {
glFeatures: {
- workItemsMvc2,
+ workItemsMvc,
},
},
stubs: {
@@ -104,11 +103,21 @@ describe('WorkItemDescription', () => {
});
describe.each([true, false])(
- 'editing description with workItemsMvc2 %workItemsMvc2Enabled',
- (workItemsMvc2Enabled) => {
+ 'editing description with workItemsMvc %workItemsMvcEnabled',
+ (workItemsMvcEnabled) => {
beforeEach(() => {
beforeEach(() => {
- workItemsMvc2 = workItemsMvc2Enabled;
+ workItemsMvc = workItemsMvcEnabled;
+ });
+ });
+
+ it('has a subscription', async () => {
+ createComponent();
+
+ await waitForPromises();
+
+ expect(subscriptionHandler).toHaveBeenCalledWith({
+ issuableId: workItemQueryResponse.data.workItem.id,
});
});
@@ -275,6 +284,13 @@ describe('WorkItemDescription', () => {
expect(workItemResponseHandler).not.toHaveBeenCalled();
expect(workItemByIidResponseHandler).toHaveBeenCalled();
});
+
+ it('skips calling the handlers when missing the needed queryVariables', async () => {
+ createComponent({ queryVariables: {}, fetchByIid: false });
+ await waitForPromises();
+
+ expect(workItemResponseHandler).not.toHaveBeenCalled();
+ });
},
);
});
diff --git a/spec/frontend/work_items/components/work_item_detail_modal_spec.js b/spec/frontend/work_items/components/work_item_detail_modal_spec.js
index 4029e47c390..686641800b3 100644
--- a/spec/frontend/work_items/components/work_item_detail_modal_spec.js
+++ b/spec/frontend/work_items/components/work_item_detail_modal_spec.js
@@ -86,7 +86,7 @@ describe('WorkItemDetailModal component', () => {
isModal: true,
workItemId: defaultPropsData.workItemId,
workItemParentId: defaultPropsData.issueGid,
- iid: null,
+ workItemIid: null,
});
});
diff --git a/spec/frontend/work_items/components/work_item_detail_spec.js b/spec/frontend/work_items/components/work_item_detail_spec.js
index 26777b57797..bbab45c7055 100644
--- a/spec/frontend/work_items/components/work_item_detail_spec.js
+++ b/spec/frontend/work_items/components/work_item_detail_spec.js
@@ -11,7 +11,7 @@ import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
+import setWindowLocation from 'helpers/set_window_location_helper';
import WorkItemDetail from '~/work_items/components/work_item_detail.vue';
import WorkItemActions from '~/work_items/components/work_item_actions.vue';
import WorkItemDescription from '~/work_items/components/work_item_description.vue';
@@ -21,7 +21,7 @@ import WorkItemTitle from '~/work_items/components/work_item_title.vue';
import WorkItemAssignees from '~/work_items/components/work_item_assignees.vue';
import WorkItemLabels from '~/work_items/components/work_item_labels.vue';
import WorkItemMilestone from '~/work_items/components/work_item_milestone.vue';
-import WorkItemInformation from '~/work_items/components/work_item_information.vue';
+import WorkItemTree from '~/work_items/components/work_item_links/work_item_tree.vue';
import { i18n } from '~/work_items/constants';
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
@@ -31,7 +31,6 @@ import workItemAssigneesSubscription from '~/work_items/graphql/work_item_assign
import workItemMilestoneSubscription from '~/work_items/graphql/work_item_milestone.subscription.graphql';
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
import updateWorkItemTaskMutation from '~/work_items/graphql/update_work_item_task.mutation.graphql';
-import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import {
mockParent,
workItemDatesSubscriptionResponse,
@@ -40,11 +39,11 @@ import {
workItemAssigneesSubscriptionResponse,
workItemMilestoneSubscriptionResponse,
projectWorkItemResponse,
+ objectiveType,
} from '../mock_data';
describe('WorkItemDetail component', () => {
let wrapper;
- useLocalStorageSpy();
Vue.use(VueApollo);
@@ -81,8 +80,7 @@ describe('WorkItemDetail component', () => {
const findParentButton = () => findParent().findComponent(GlButton);
const findCloseButton = () => wrapper.find('[data-testid="work-item-close"]');
const findWorkItemType = () => wrapper.find('[data-testid="work-item-type"]');
- const findWorkItemInformationAlert = () => wrapper.findComponent(WorkItemInformation);
- const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
+ const findHierarchyTree = () => wrapper.findComponent(WorkItemTree);
const createComponent = ({
isModal = false,
@@ -92,9 +90,9 @@ describe('WorkItemDetail component', () => {
subscriptionHandler = titleSubscriptionHandler,
confidentialityMock = [updateWorkItemMutation, jest.fn()],
error = undefined,
+ workItemsMvcEnabled = false,
workItemsMvc2Enabled = false,
fetchByIid = false,
- iidPathQueryParam = undefined,
} = {}) => {
const handlers = [
[workItemQuery, handler],
@@ -108,7 +106,7 @@ describe('WorkItemDetail component', () => {
wrapper = shallowMount(WorkItemDetail, {
apolloProvider: createMockApollo(handlers),
- propsData: { isModal, workItemId, iid: '1' },
+ propsData: { isModal, workItemId, workItemIid: '1' },
data() {
return {
updateInProgress,
@@ -117,11 +115,14 @@ describe('WorkItemDetail component', () => {
},
provide: {
glFeatures: {
+ workItemsMvc: workItemsMvcEnabled,
workItemsMvc2: workItemsMvc2Enabled,
useIidInWorkItemsPath: fetchByIid,
},
hasIssueWeightsFeature: true,
hasIterationsFeature: true,
+ hasOkrsFeature: true,
+ hasIssuableHealthStatusFeature: true,
projectNamespace: 'namespace',
fullPath: 'group/project',
},
@@ -129,18 +130,12 @@ describe('WorkItemDetail component', () => {
WorkItemWeight: true,
WorkItemIteration: true,
},
- mocks: {
- $route: {
- query: {
- iid_path: iidPathQueryParam,
- },
- },
- },
});
};
afterEach(() => {
wrapper.destroy();
+ setWindowLocation('');
});
describe('when there is no `workItemId` prop', () => {
@@ -406,9 +401,31 @@ describe('WorkItemDetail component', () => {
expect(findWorkItemType().exists()).toBe(false);
});
- it('sets the parent breadcrumb URL', () => {
+ it('shows parent breadcrumb icon', () => {
+ expect(findParentButton().props('icon')).toBe(mockParent.parent.workItemType.iconName);
+ });
+
+ it('sets the parent breadcrumb URL pointing to issue page when parent type is `Issue`', () => {
expect(findParentButton().attributes().href).toBe('../../issues/5');
});
+
+ it('sets the parent breadcrumb URL based on parent webUrl when parent type is not `Issue`', async () => {
+ const mockParentObjective = {
+ parent: {
+ ...mockParent.parent,
+ workItemType: {
+ id: mockParent.parent.workItemType.id,
+ name: 'Objective',
+ iconName: 'issue-type-objective',
+ },
+ },
+ };
+ const parentResponse = workItemResponseFactory(mockParentObjective);
+ createComponent({ handler: jest.fn().mockResolvedValue(parentResponse) });
+ await waitForPromises();
+
+ expect(findParentButton().attributes().href).toBe(mockParentObjective.parent.webUrl);
+ });
});
});
@@ -563,7 +580,7 @@ describe('WorkItemDetail component', () => {
`('$description', async ({ milestoneWidgetPresent, exists }) => {
const response = workItemResponseFactory({ milestoneWidgetPresent });
const handler = jest.fn().mockResolvedValue(response);
- createComponent({ handler, workItemsMvc2Enabled: true });
+ createComponent({ handler });
await waitForPromises();
expect(findWorkItemMilestone().exists()).toBe(exists);
@@ -594,24 +611,6 @@ describe('WorkItemDetail component', () => {
});
});
- describe('work item information', () => {
- beforeEach(() => {
- createComponent();
- return waitForPromises();
- });
-
- it('is visible when viewed for the first time and sets localStorage value', async () => {
- localStorage.clear();
- expect(findWorkItemInformationAlert().exists()).toBe(true);
- expect(findLocalStorageSync().props('value')).toBe(true);
- });
-
- it('is not visible after reading local storage input', async () => {
- await findLocalStorageSync().vm.$emit('input', false);
- expect(findWorkItemInformationAlert().exists()).toBe(false);
- });
- });
-
it('calls the global ID work item query when `useIidInWorkItemsPath` feature flag is false', async () => {
createComponent();
await waitForPromises();
@@ -633,6 +632,8 @@ describe('WorkItemDetail component', () => {
});
it('calls the IID work item query when `useIidInWorkItemsPath` feature flag is true and `iid_path` route parameter is present', async () => {
+ setWindowLocation(`?iid_path=true`);
+
createComponent({ fetchByIid: true, iidPathQueryParam: 'true' });
await waitForPromises();
@@ -642,4 +643,24 @@ describe('WorkItemDetail component', () => {
iid: '1',
});
});
+
+ describe('hierarchy widget', () => {
+ it('does not render children tree by default', async () => {
+ createComponent();
+ await waitForPromises();
+
+ expect(findHierarchyTree().exists()).toBe(false);
+ });
+
+ it('renders children tree when work item is an Objective', async () => {
+ const objectiveWorkItem = workItemResponseFactory({
+ workItemType: objectiveType,
+ });
+ const handler = jest.fn().mockResolvedValue(objectiveWorkItem);
+ createComponent({ handler });
+ await waitForPromises();
+
+ expect(findHierarchyTree().exists()).toBe(true);
+ });
+ });
});
diff --git a/spec/frontend/work_items/components/work_item_information_spec.js b/spec/frontend/work_items/components/work_item_information_spec.js
deleted file mode 100644
index 887c5f615e9..00000000000
--- a/spec/frontend/work_items/components/work_item_information_spec.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import { mount } from '@vue/test-utils';
-import { GlAlert, GlLink } from '@gitlab/ui';
-import WorkItemInformation from '~/work_items/components/work_item_information.vue';
-import { helpPagePath } from '~/helpers/help_page_helper';
-
-const createComponent = () => mount(WorkItemInformation);
-
-describe('Work item information alert', () => {
- let wrapper;
- const tasksHelpPath = helpPagePath('user/tasks');
-
- const findAlert = () => wrapper.findComponent(GlAlert);
- const findHelpLink = () => wrapper.findComponent(GlLink);
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('should be visible', () => {
- expect(findAlert().exists()).toBe(true);
- });
-
- it('should emit `work-item-banner-dismissed` event when cross icon is clicked', () => {
- findAlert().vm.$emit('dismiss');
- expect(wrapper.emitted('work-item-banner-dismissed').length).toBe(1);
- });
-
- it('the alert variant should be tip', () => {
- expect(findAlert().props('variant')).toBe('tip');
- });
-
- it('should have the correct text for title', () => {
- expect(findAlert().props('title')).toBe(WorkItemInformation.i18n.tasksInformationTitle);
- });
-
- it('should have the correct link to work item link', () => {
- expect(findHelpLink().exists()).toBe(true);
- expect(findHelpLink().attributes('href')).toBe(tasksHelpPath);
- });
-});
diff --git a/spec/frontend/work_items/components/work_item_labels_spec.js b/spec/frontend/work_items/components/work_item_labels_spec.js
index 9f7659b3f8d..083bb5bc4a4 100644
--- a/spec/frontend/work_items/components/work_item_labels_spec.js
+++ b/spec/frontend/work_items/components/work_item_labels_spec.js
@@ -5,7 +5,7 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
-import labelSearchQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql';
+import labelSearchQuery from '~/sidebar/components/labels/labels_select_widget/graphql/project_labels.query.graphql';
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
import workItemLabelsSubscription from 'ee_else_ce/work_items/graphql/work_item_labels.subscription.graphql';
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
@@ -49,6 +49,7 @@ describe('WorkItemLabels component', () => {
searchQueryHandler = successSearchQueryHandler,
updateWorkItemMutationHandler = successUpdateWorkItemMutationHandler,
fetchByIid = false,
+ queryVariables = { id: workItemId },
} = {}) => {
const apolloProvider = createMockApollo([
[workItemQuery, workItemQueryHandler],
@@ -63,9 +64,7 @@ describe('WorkItemLabels component', () => {
workItemId,
canUpdate,
fullPath: 'test-project-path',
- queryVariables: {
- id: workItemId,
- },
+ queryVariables,
fetchByIid,
},
attachTo: document.body,
@@ -251,4 +250,11 @@ describe('WorkItemLabels component', () => {
expect(workItemQuerySuccess).not.toHaveBeenCalled();
expect(workItemByIidResponseHandler).toHaveBeenCalled();
});
+
+ it('skips calling the handlers when missing the needed queryVariables', async () => {
+ createComponent({ queryVariables: {}, fetchByIid: false });
+ await waitForPromises();
+
+ expect(workItemQuerySuccess).not.toHaveBeenCalled();
+ });
});
diff --git a/spec/frontend/work_items/components/work_item_links/okr_actions_split_button_spec.js b/spec/frontend/work_items/components/work_item_links/okr_actions_split_button_spec.js
new file mode 100644
index 00000000000..5fbd8e7e1a7
--- /dev/null
+++ b/spec/frontend/work_items/components/work_item_links/okr_actions_split_button_spec.js
@@ -0,0 +1,35 @@
+import { GlDropdownSectionHeader } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+
+import OkrActionsSplitButton from '~/work_items/components/work_item_links/okr_actions_split_button.vue';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+
+const createComponent = () => {
+ return extendedWrapper(shallowMount(OkrActionsSplitButton));
+};
+
+describe('RelatedItemsTree', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('OkrActionsSplitButton', () => {
+ describe('template', () => {
+ it('renders objective and key results sections', () => {
+ expect(wrapper.findAllComponents(GlDropdownSectionHeader).at(0).text()).toContain(
+ 'Objective',
+ );
+
+ expect(wrapper.findAllComponents(GlDropdownSectionHeader).at(1).text()).toContain(
+ 'Key result',
+ );
+ });
+ });
+ });
+});
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_link_child_metadata_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_link_child_metadata_spec.js
new file mode 100644
index 00000000000..47489d4796b
--- /dev/null
+++ b/spec/frontend/work_items/components/work_item_links/work_item_link_child_metadata_spec.js
@@ -0,0 +1,67 @@
+import { GlLabel, GlAvatarsInline } from '@gitlab/ui';
+
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+
+import ItemMilestone from '~/issuable/components/issue_milestone.vue';
+import WorkItemLinkChildMetadata from '~/work_items/components/work_item_links/work_item_link_child_metadata.vue';
+
+import { mockMilestone, mockAssignees, mockLabels } from '../../mock_data';
+
+describe('WorkItemLinkChildMetadata', () => {
+ let wrapper;
+
+ const createComponent = ({
+ allowsScopedLabels = true,
+ milestone = mockMilestone,
+ assignees = mockAssignees,
+ labels = mockLabels,
+ } = {}) => {
+ wrapper = shallowMountExtended(WorkItemLinkChildMetadata, {
+ propsData: {
+ allowsScopedLabels,
+ milestone,
+ assignees,
+ labels,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders milestone link button', () => {
+ const milestoneLink = wrapper.findComponent(ItemMilestone);
+
+ expect(milestoneLink.exists()).toBe(true);
+ expect(milestoneLink.props('milestone')).toEqual(mockMilestone);
+ });
+
+ it('renders avatars for assignees', () => {
+ const avatars = wrapper.findComponent(GlAvatarsInline);
+
+ expect(avatars.exists()).toBe(true);
+ expect(avatars.props()).toMatchObject({
+ avatars: mockAssignees,
+ collapsed: true,
+ maxVisible: 2,
+ avatarSize: 24,
+ badgeTooltipProp: 'name',
+ badgeSrOnlyText: '',
+ });
+ });
+
+ it('renders labels', () => {
+ const labels = wrapper.findAllComponents(GlLabel);
+ const mockLabel = mockLabels[0];
+
+ expect(labels).toHaveLength(mockLabels.length);
+ expect(labels.at(0).props()).toMatchObject({
+ title: mockLabel.title,
+ backgroundColor: mockLabel.color,
+ description: mockLabel.description,
+ scoped: false,
+ });
+ expect(labels.at(1).props('scoped')).toBe(true); // Second label is scoped
+ });
+});
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js
index 1d5472a0473..73d498ad055 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js
@@ -1,33 +1,73 @@
-import { GlButton, GlIcon } from '@gitlab/ui';
+import { GlIcon } from '@gitlab/ui';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import { createAlert } from '~/flash';
import RichTimestampTooltip from '~/vue_shared/components/rich_timestamp_tooltip.vue';
+import getWorkItemTreeQuery from '~/work_items/graphql/work_item_tree.query.graphql';
+import WorkItemLinkChildMetadata from '~/work_items/components/work_item_links/work_item_link_child_metadata.vue';
import WorkItemLinkChild from '~/work_items/components/work_item_links/work_item_link_child.vue';
import WorkItemLinksMenu from '~/work_items/components/work_item_links/work_item_links_menu.vue';
+import WorkItemTreeChildren from '~/work_items/components/work_item_links/work_item_tree_children.vue';
+import {
+ WIDGET_TYPE_HIERARCHY,
+ TASK_TYPE_NAME,
+ WORK_ITEM_TYPE_VALUE_OBJECTIVE,
+} from '~/work_items/constants';
-import { workItemTask, confidentialWorkItemTask, closedWorkItemTask } from '../../mock_data';
+import {
+ workItemTask,
+ workItemObjectiveWithChild,
+ workItemObjectiveNoMetadata,
+ confidentialWorkItemTask,
+ closedWorkItemTask,
+ mockMilestone,
+ mockAssignees,
+ mockLabels,
+ workItemHierarchyTreeResponse,
+ workItemHierarchyTreeFailureResponse,
+} from '../../mock_data';
+
+jest.mock('~/flash');
describe('WorkItemLinkChild', () => {
const WORK_ITEM_ID = 'gid://gitlab/WorkItem/2';
let wrapper;
+ let getWorkItemTreeQueryHandler;
+
+ Vue.use(VueApollo);
const createComponent = ({
projectPath = 'gitlab-org/gitlab-test',
canUpdate = true,
issuableGid = WORK_ITEM_ID,
childItem = workItemTask,
+ workItemType = TASK_TYPE_NAME,
+ apolloProvider = null,
} = {}) => {
+ getWorkItemTreeQueryHandler = jest.fn().mockResolvedValue(workItemHierarchyTreeResponse);
+
wrapper = shallowMountExtended(WorkItemLinkChild, {
+ apolloProvider:
+ apolloProvider || createMockApollo([[getWorkItemTreeQuery, getWorkItemTreeQueryHandler]]),
propsData: {
projectPath,
canUpdate,
issuableGid,
childItem,
+ workItemType,
},
});
};
+ beforeEach(() => {
+ createAlert.mockClear();
+ });
+
afterEach(() => {
wrapper.destroy();
});
@@ -66,7 +106,7 @@ describe('WorkItemLinkChild', () => {
beforeEach(() => {
createComponent();
- titleEl = wrapper.findComponent(GlButton);
+ titleEl = wrapper.findByTestId('item-title');
});
it('renders item title', () => {
@@ -76,16 +116,52 @@ describe('WorkItemLinkChild', () => {
it.each`
action | event | emittedEvent
- ${'clicking'} | ${'click'} | ${'click'}
${'doing mouseover on'} | ${'mouseover'} | ${'mouseover'}
${'doing mouseout on'} | ${'mouseout'} | ${'mouseout'}
`('$action item title emit `$emittedEvent` event', ({ event, emittedEvent }) => {
+ titleEl.vm.$emit(event);
+
+ expect(wrapper.emitted(emittedEvent)).toEqual([[]]);
+ });
+
+ it('emits click event with correct parameters on clicking title', () => {
const eventObj = {
preventDefault: jest.fn(),
};
- titleEl.vm.$emit(event, eventObj);
+ titleEl.vm.$emit('click', eventObj);
- expect(wrapper.emitted(emittedEvent)).toEqual([[workItemTask.id, eventObj]]);
+ expect(wrapper.emitted('click')).toEqual([[eventObj]]);
+ });
+ });
+
+ describe('item metadata', () => {
+ const findMetadataComponent = () => wrapper.findComponent(WorkItemLinkChildMetadata);
+
+ beforeEach(() => {
+ createComponent({
+ childItem: workItemObjectiveWithChild,
+ workItemType: WORK_ITEM_TYPE_VALUE_OBJECTIVE,
+ });
+ });
+
+ it('renders item metadata component when item has metadata present', () => {
+ const metadataEl = findMetadataComponent();
+ expect(metadataEl.exists()).toBe(true);
+ expect(metadataEl.props()).toMatchObject({
+ allowsScopedLabels: true,
+ milestone: mockMilestone,
+ assignees: mockAssignees,
+ labels: mockLabels,
+ });
+ });
+
+ it('does not render item metadata component when item has no metadata present', () => {
+ createComponent({
+ childItem: workItemObjectiveNoMetadata,
+ workItemType: WORK_ITEM_TYPE_VALUE_OBJECTIVE,
+ });
+
+ expect(findMetadataComponent().exists()).toBe(false);
});
});
@@ -116,7 +192,78 @@ describe('WorkItemLinkChild', () => {
it('removeChild event on menu triggers `click-remove-child` event', () => {
itemMenuEl.vm.$emit('removeChild');
- expect(wrapper.emitted('remove')).toEqual([[workItemTask.id]]);
+ expect(wrapper.emitted('removeChild')).toEqual([[workItemTask.id]]);
+ });
+ });
+
+ describe('nested children', () => {
+ const findExpandButton = () => wrapper.findByTestId('expand-child');
+ const findTreeChildren = () => wrapper.findComponent(WorkItemTreeChildren);
+
+ beforeEach(() => {
+ getWorkItemTreeQueryHandler.mockClear();
+ createComponent({
+ childItem: workItemObjectiveWithChild,
+ workItemType: WORK_ITEM_TYPE_VALUE_OBJECTIVE,
+ });
+ });
+
+ it('displays expand button when item has children, children are not displayed by default', () => {
+ expect(findExpandButton().exists()).toBe(true);
+ expect(findTreeChildren().exists()).toBe(false);
+ });
+
+ it('fetches and displays children of item when clicking on expand button', async () => {
+ await findExpandButton().vm.$emit('click');
+
+ expect(findExpandButton().props('loading')).toBe(true);
+ await waitForPromises();
+
+ expect(getWorkItemTreeQueryHandler).toHaveBeenCalled();
+ expect(findTreeChildren().exists()).toBe(true);
+
+ const widgetHierarchy = workItemHierarchyTreeResponse.data.workItem.widgets.find(
+ (widget) => widget.type === WIDGET_TYPE_HIERARCHY,
+ );
+ expect(findTreeChildren().props('children')).toEqual(widgetHierarchy.children.nodes);
+ });
+
+ it('does not fetch children if already fetched once while clicking expand button', async () => {
+ findExpandButton().vm.$emit('click'); // Expand for the first time
+ await waitForPromises();
+
+ expect(findTreeChildren().exists()).toBe(true);
+
+ await findExpandButton().vm.$emit('click'); // Collapse
+ findExpandButton().vm.$emit('click'); // Expand again
+ await waitForPromises();
+
+ expect(getWorkItemTreeQueryHandler).toHaveBeenCalledTimes(1); // ensure children were fetched only once.
+ expect(findTreeChildren().exists()).toBe(true);
+ });
+
+ it('calls createAlert when children fetch request fails on clicking expand button', async () => {
+ const getWorkItemTreeQueryFailureHandler = jest
+ .fn()
+ .mockRejectedValue(workItemHierarchyTreeFailureResponse);
+ const apolloProvider = createMockApollo([
+ [getWorkItemTreeQuery, getWorkItemTreeQueryFailureHandler],
+ ]);
+
+ createComponent({
+ childItem: workItemObjectiveWithChild,
+ workItemType: WORK_ITEM_TYPE_VALUE_OBJECTIVE,
+ apolloProvider,
+ });
+
+ findExpandButton().vm.$emit('click');
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalledWith({
+ captureError: true,
+ error: expect.any(Object),
+ message: 'Something went wrong while fetching children.',
+ });
});
});
});
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js
index 071d5fb715a..bbe460a55ba 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js
@@ -33,7 +33,7 @@ describe('WorkItemLinksForm', () => {
typesResponse = projectWorkItemTypesQueryResponse,
parentConfidential = false,
hasIterationsFeature = false,
- workItemsMvc2Enabled = false,
+ workItemsMvcEnabled = false,
parentIteration = null,
formType = FORM_TYPES.create,
} = {}) => {
@@ -52,7 +52,7 @@ describe('WorkItemLinksForm', () => {
},
provide: {
glFeatures: {
- workItemsMvc2: workItemsMvc2Enabled,
+ workItemsMvc: workItemsMvcEnabled,
},
projectPath: 'project/path',
hasIterationsFeature,
@@ -165,23 +165,8 @@ describe('WorkItemLinksForm', () => {
});
describe('associate iteration with task', () => {
- it('does not update iteration when mvc2 feature flag is not enabled', async () => {
- await createComponent({
- hasIterationsFeature: true,
- parentIteration: mockParentIteration,
- });
-
- findInput().vm.$emit('input', 'Create task test');
-
- findForm().vm.$emit('submit', {
- preventDefault: jest.fn(),
- });
- await waitForPromises();
- expect(updateMutationResolver).not.toHaveBeenCalled();
- });
it('updates when parent has an iteration associated', async () => {
await createComponent({
- workItemsMvc2Enabled: true,
hasIterationsFeature: true,
parentIteration: mockParentIteration,
});
@@ -191,18 +176,23 @@ describe('WorkItemLinksForm', () => {
preventDefault: jest.fn(),
});
await waitForPromises();
- expect(updateMutationResolver).toHaveBeenCalledWith({
+ expect(createMutationResolver).toHaveBeenCalledWith({
input: {
- id: 'gid://gitlab/WorkItem/1',
+ title: 'Create task test',
+ projectPath: 'project/path',
+ workItemTypeId: 'gid://gitlab/WorkItems::Type/3',
+ hierarchyWidget: {
+ parentId: 'gid://gitlab/WorkItem/1',
+ },
+ confidential: false,
iterationWidget: {
iterationId: mockParentIteration.id,
},
},
});
});
- it('does not update when parent has no iteration associated', async () => {
+ it('does not send the iteration widget to mutation when parent has no iteration associated', async () => {
await createComponent({
- workItemsMvc2Enabled: true,
hasIterationsFeature: true,
});
findInput().vm.$emit('input', 'Create task test');
@@ -211,7 +201,20 @@ describe('WorkItemLinksForm', () => {
preventDefault: jest.fn(),
});
await waitForPromises();
- expect(updateMutationResolver).not.toHaveBeenCalled();
+ expect(createMutationResolver).not.toHaveBeenCalledWith({
+ input: {
+ title: 'Create task test',
+ projectPath: 'project/path',
+ workItemTypeId: 'gid://gitlab/WorkItems::Type/3',
+ hierarchyWidget: {
+ parentId: 'gid://gitlab/WorkItem/1',
+ },
+ confidential: false,
+ iterationWidget: {
+ iterationId: mockParentIteration.id,
+ },
+ },
+ });
});
});
});
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js
index 66ce2c1becf..a61de78c623 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js
@@ -4,20 +4,25 @@ import VueApollo from 'vue-apollo';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
+import setWindowLocation from 'helpers/set_window_location_helper';
+import { stubComponent } from 'helpers/stub_component';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import issueDetailsQuery from 'ee_else_ce/work_items/graphql/get_issue_details.query.graphql';
import WorkItemLinks from '~/work_items/components/work_item_links/work_item_links.vue';
import WorkItemLinkChild from '~/work_items/components/work_item_links/work_item_link_child.vue';
+import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
import { FORM_TYPES } from '~/work_items/constants';
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
import changeWorkItemParentMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
import getWorkItemLinksQuery from '~/work_items/graphql/work_item_links.query.graphql';
+import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
import {
workItemHierarchyResponse,
workItemHierarchyEmptyResponse,
workItemHierarchyNoUpdatePermissionResponse,
changeWorkItemParentMutationResponse,
workItemQueryResponse,
+ projectWorkItemResponse,
} from '../../mock_data';
Vue.use(VueApollo);
@@ -55,6 +60,7 @@ const issueDetailsResponse = (confidential = false) => ({
},
},
});
+const showModal = jest.fn();
describe('WorkItemLinks', () => {
let wrapper;
@@ -71,6 +77,7 @@ describe('WorkItemLinks', () => {
.mockResolvedValue(changeWorkItemParentMutationResponse);
const childWorkItemQueryHandler = jest.fn().mockResolvedValue(workItemQueryResponse);
+ const childWorkItemByIidHandler = jest.fn().mockResolvedValue(projectWorkItemResponse);
const createComponent = async ({
data = {},
@@ -78,6 +85,7 @@ describe('WorkItemLinks', () => {
mutationHandler = mutationChangeParentHandler,
issueDetailsQueryHandler = jest.fn().mockResolvedValue(issueDetailsResponse()),
hasIterationsFeature = false,
+ fetchByIid = false,
} = {}) => {
mockApollo = createMockApollo(
[
@@ -85,6 +93,7 @@ describe('WorkItemLinks', () => {
[changeWorkItemParentMutation, mutationHandler],
[workItemQuery, childWorkItemQueryHandler],
[issueDetailsQuery, issueDetailsQueryHandler],
+ [workItemByIidQuery, childWorkItemByIidHandler],
],
{},
{ addTypename: true },
@@ -100,12 +109,22 @@ describe('WorkItemLinks', () => {
projectPath: 'project/path',
iid: '1',
hasIterationsFeature,
+ glFeatures: {
+ useIidInWorkItemsPath: fetchByIid,
+ },
},
propsData: { issuableId: 1 },
apolloProvider: mockApollo,
mocks: {
$toast,
},
+ stubs: {
+ WorkItemDetailModal: stubComponent(WorkItemDetailModal, {
+ methods: {
+ show: showModal,
+ },
+ }),
+ },
});
await waitForPromises();
@@ -130,6 +149,7 @@ describe('WorkItemLinks', () => {
afterEach(() => {
wrapper.destroy();
mockApollo = null;
+ setWindowLocation('');
});
it('is expanded by default', () => {
@@ -237,7 +257,7 @@ describe('WorkItemLinks', () => {
});
it('calls correct mutation with correct variables', async () => {
- firstChild.vm.$emit('remove', firstChild.vm.childItem.id);
+ firstChild.vm.$emit('removeChild', firstChild.vm.childItem.id);
await waitForPromises();
@@ -252,7 +272,7 @@ describe('WorkItemLinks', () => {
});
it('shows toast when mutation succeeds', async () => {
- firstChild.vm.$emit('remove', firstChild.vm.childItem.id);
+ firstChild.vm.$emit('removeChild', firstChild.vm.childItem.id);
await waitForPromises();
@@ -264,56 +284,164 @@ describe('WorkItemLinks', () => {
it('renders correct number of children after removal', async () => {
expect(findWorkItemLinkChildItems()).toHaveLength(4);
- firstChild.vm.$emit('remove', firstChild.vm.childItem.id);
+ firstChild.vm.$emit('removeChild', firstChild.vm.childItem.id);
await waitForPromises();
expect(findWorkItemLinkChildItems()).toHaveLength(3);
});
});
- describe('prefetching child items', () => {
- let firstChild;
-
- beforeEach(async () => {
- await createComponent();
+ describe('when parent item is confidential', () => {
+ it('passes correct confidentiality status to form', async () => {
+ await createComponent({
+ issueDetailsQueryHandler: jest.fn().mockResolvedValue(issueDetailsResponse(true)),
+ });
+ findToggleFormDropdown().vm.$emit('click');
+ findToggleAddFormButton().vm.$emit('click');
+ await nextTick();
- firstChild = findFirstWorkItemLinkChild();
+ expect(findAddLinksForm().props('parentConfidential')).toBe(true);
});
+ });
- it('does not fetch the child work item before hovering work item links', () => {
- expect(childWorkItemQueryHandler).not.toHaveBeenCalled();
+ describe('when work item is fetched by id', () => {
+ describe('prefetching child items', () => {
+ let firstChild;
+
+ beforeEach(async () => {
+ await createComponent();
+
+ firstChild = findFirstWorkItemLinkChild();
+ });
+
+ it('does not fetch the child work item by id before hovering work item links', () => {
+ expect(childWorkItemQueryHandler).not.toHaveBeenCalled();
+ });
+
+ it('fetches the child work item by id if link is hovered for 250+ ms', async () => {
+ firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
+ jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
+ await waitForPromises();
+
+ expect(childWorkItemQueryHandler).toHaveBeenCalledWith({
+ id: 'gid://gitlab/WorkItem/2',
+ });
+ });
+
+ it('does not fetch the child work item by id if link is hovered for less than 250 ms', async () => {
+ firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
+ jest.advanceTimersByTime(200);
+ firstChild.vm.$emit('mouseout', firstChild.vm.childItem.id);
+ await waitForPromises();
+
+ expect(childWorkItemQueryHandler).not.toHaveBeenCalled();
+ });
+
+ it('does not fetch work item by iid if link is hovered for 250+ ms', async () => {
+ firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
+ jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
+ await waitForPromises();
+
+ expect(childWorkItemByIidHandler).not.toHaveBeenCalled();
+ });
});
- it('fetches the child work item if link is hovered for 250+ ms', async () => {
- firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
- jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
- await waitForPromises();
+ it('starts prefetching work item by id if URL contains work item id', async () => {
+ setWindowLocation('?work_item_id=5');
+ await createComponent();
expect(childWorkItemQueryHandler).toHaveBeenCalledWith({
- id: 'gid://gitlab/WorkItem/2',
+ id: 'gid://gitlab/WorkItem/5',
});
});
- it('does not fetch the child work item if link is hovered for less than 250 ms', async () => {
- firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
- jest.advanceTimersByTime(200);
- firstChild.vm.$emit('mouseout', firstChild.vm.childItem.id);
- await waitForPromises();
+ it('does not open the modal if work item id URL parameter is not found in child items', async () => {
+ setWindowLocation('?work_item_id=555');
+ await createComponent();
+
+ expect(showModal).not.toHaveBeenCalled();
+ expect(wrapper.findComponent(WorkItemDetailModal).props('workItemId')).toBe(null);
+ });
+
+ it('opens the modal if work item id URL parameter is found in child items', async () => {
+ setWindowLocation('?work_item_id=2');
+ await createComponent();
- expect(childWorkItemQueryHandler).not.toHaveBeenCalled();
+ expect(showModal).toHaveBeenCalled();
+ expect(wrapper.findComponent(WorkItemDetailModal).props('workItemId')).toBe(
+ 'gid://gitlab/WorkItem/2',
+ );
});
});
- describe('when parent item is confidential', () => {
- it('passes correct confidentiality status to form', async () => {
- await createComponent({
- issueDetailsQueryHandler: jest.fn().mockResolvedValue(issueDetailsResponse(true)),
+ describe('when work item is fetched by iid', () => {
+ describe('prefetching child items', () => {
+ let firstChild;
+
+ beforeEach(async () => {
+ setWindowLocation('?iid_path=true');
+ await createComponent({ fetchByIid: true });
+
+ firstChild = findFirstWorkItemLinkChild();
});
- findToggleFormDropdown().vm.$emit('click');
- findToggleAddFormButton().vm.$emit('click');
- await nextTick();
- expect(findAddLinksForm().props('parentConfidential')).toBe(true);
+ it('does not fetch the child work item by iid before hovering work item links', () => {
+ expect(childWorkItemByIidHandler).not.toHaveBeenCalled();
+ });
+
+ it('fetches the child work item by iid if link is hovered for 250+ ms', async () => {
+ firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
+ jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
+ await waitForPromises();
+
+ expect(childWorkItemByIidHandler).toHaveBeenCalledWith({
+ fullPath: 'project/path',
+ iid: '2',
+ });
+ });
+
+ it('does not fetch the child work item by iid if link is hovered for less than 250 ms', async () => {
+ firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
+ jest.advanceTimersByTime(200);
+ firstChild.vm.$emit('mouseout', firstChild.vm.childItem.id);
+ await waitForPromises();
+
+ expect(childWorkItemByIidHandler).not.toHaveBeenCalled();
+ });
+
+ it('does not fetch work item by id if link is hovered for 250+ ms', async () => {
+ firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
+ jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
+ await waitForPromises();
+
+ expect(childWorkItemQueryHandler).not.toHaveBeenCalled();
+ });
});
+
+ it('starts prefetching work item by iid if URL contains work item id', async () => {
+ setWindowLocation('?work_item_iid=5&iid_path=true');
+ await createComponent({ fetchByIid: true });
+
+ expect(childWorkItemByIidHandler).toHaveBeenCalledWith({
+ iid: '5',
+ fullPath: 'project/path',
+ });
+ });
+ });
+
+ it('does not open the modal if work item iid URL parameter is not found in child items', async () => {
+ setWindowLocation('?work_item_iid=555&iid_path=true');
+ await createComponent({ fetchByIid: true });
+
+ expect(showModal).not.toHaveBeenCalled();
+ expect(wrapper.findComponent(WorkItemDetailModal).props('workItemIid')).toBe(null);
+ });
+
+ it('opens the modal if work item iid URL parameter is found in child items', async () => {
+ setWindowLocation('?work_item_iid=2&iid_path=true');
+ await createComponent({ fetchByIid: true });
+
+ expect(showModal).toHaveBeenCalled();
+ expect(wrapper.findComponent(WorkItemDetailModal).props('workItemIid')).toBe('2');
});
});
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js
new file mode 100644
index 00000000000..96211e12755
--- /dev/null
+++ b/spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js
@@ -0,0 +1,147 @@
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
+
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import WorkItemTree from '~/work_items/components/work_item_links/work_item_tree.vue';
+import WorkItemLinksForm from '~/work_items/components/work_item_links/work_item_links_form.vue';
+import WorkItemLinkChild from '~/work_items/components/work_item_links/work_item_link_child.vue';
+import OkrActionsSplitButton from '~/work_items/components/work_item_links/okr_actions_split_button.vue';
+import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
+
+import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
+
+import {
+ FORM_TYPES,
+ WORK_ITEM_TYPE_ENUM_OBJECTIVE,
+ WORK_ITEM_TYPE_ENUM_KEY_RESULT,
+} from '~/work_items/constants';
+import { childrenWorkItems, workItemObjectiveWithChild } from '../../mock_data';
+
+describe('WorkItemTree', () => {
+ let getWorkItemQueryHandler;
+ let wrapper;
+
+ const findToggleButton = () => wrapper.findByTestId('toggle-tree');
+ const findTreeBody = () => wrapper.findByTestId('tree-body');
+ const findEmptyState = () => wrapper.findByTestId('tree-empty');
+ const findToggleFormSplitButton = () => wrapper.findComponent(OkrActionsSplitButton);
+ const findForm = () => wrapper.findComponent(WorkItemLinksForm);
+ const findWorkItemLinkChildItems = () => wrapper.findAllComponents(WorkItemLinkChild);
+
+ Vue.use(VueApollo);
+
+ const createComponent = ({
+ workItemType = 'Objective',
+ children = childrenWorkItems,
+ apolloProvider = null,
+ } = {}) => {
+ const mockWorkItemResponse = {
+ data: {
+ workItem: {
+ ...workItemObjectiveWithChild,
+ workItemType: {
+ ...workItemObjectiveWithChild.workItemType,
+ name: workItemType,
+ },
+ },
+ },
+ };
+ getWorkItemQueryHandler = jest.fn().mockResolvedValue(mockWorkItemResponse);
+
+ wrapper = shallowMountExtended(WorkItemTree, {
+ apolloProvider:
+ apolloProvider || createMockApollo([[workItemQuery, getWorkItemQueryHandler]]),
+ propsData: {
+ workItemType,
+ workItemId: 'gid://gitlab/WorkItem/515',
+ children,
+ projectPath: 'test/project',
+ },
+ });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('is expanded by default and displays Add button', () => {
+ expect(findToggleButton().props('icon')).toBe('chevron-lg-up');
+ expect(findTreeBody().exists()).toBe(true);
+ expect(findToggleFormSplitButton().exists()).toBe(true);
+ });
+
+ it('collapses on click toggle button', async () => {
+ findToggleButton().vm.$emit('click');
+ await nextTick();
+
+ expect(findToggleButton().props('icon')).toBe('chevron-lg-down');
+ expect(findTreeBody().exists()).toBe(false);
+ });
+
+ it('displays empty state if there are no children', () => {
+ createComponent({ children: [] });
+ expect(findEmptyState().exists()).toBe(true);
+ });
+
+ it('renders all hierarchy widget children', () => {
+ expect(findWorkItemLinkChildItems()).toHaveLength(4);
+ });
+
+ it('does not display form by default', () => {
+ expect(findForm().exists()).toBe(false);
+ });
+
+ it.each`
+ option | event | formType | childType
+ ${'New objective'} | ${'showCreateObjectiveForm'} | ${FORM_TYPES.create} | ${WORK_ITEM_TYPE_ENUM_OBJECTIVE}
+ ${'Existing objective'} | ${'showAddObjectiveForm'} | ${FORM_TYPES.add} | ${WORK_ITEM_TYPE_ENUM_OBJECTIVE}
+ ${'New key result'} | ${'showCreateKeyResultForm'} | ${FORM_TYPES.create} | ${WORK_ITEM_TYPE_ENUM_KEY_RESULT}
+ ${'Existing key result'} | ${'showAddKeyResultForm'} | ${FORM_TYPES.add} | ${WORK_ITEM_TYPE_ENUM_KEY_RESULT}
+ `(
+ 'when selecting $option from split button, renders the form passing $formType and $childType',
+ async ({ event, formType, childType }) => {
+ findToggleFormSplitButton().vm.$emit(event);
+ await nextTick();
+
+ expect(findForm().exists()).toBe(true);
+ expect(findForm().props('formType')).toBe(formType);
+ expect(findForm().props('childrenType')).toBe(childType);
+ },
+ );
+
+ it('remove event on child triggers `removeChild` event', () => {
+ const firstChild = findWorkItemLinkChildItems().at(0);
+ firstChild.vm.$emit('removeChild', 'gid://gitlab/WorkItem/2');
+
+ expect(wrapper.emitted('removeChild')).toEqual([['gid://gitlab/WorkItem/2']]);
+ });
+
+ it.each`
+ description | workItemType | prefetch
+ ${'prefetches'} | ${'Issue'} | ${true}
+ ${'does not prefetch'} | ${'Objective'} | ${false}
+ `(
+ '$description work-item-link-child on mouseover when workItemType is "$workItemType"',
+ async ({ workItemType, prefetch }) => {
+ createComponent({ workItemType });
+ const firstChild = findWorkItemLinkChildItems().at(0);
+ firstChild.vm.$emit('mouseover', childrenWorkItems[0]);
+ await nextTick();
+ await waitForPromises();
+
+ jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
+
+ if (prefetch) {
+ expect(getWorkItemQueryHandler).toHaveBeenCalled();
+ } else {
+ expect(getWorkItemQueryHandler).not.toHaveBeenCalled();
+ }
+ },
+ );
+});
diff --git a/spec/frontend/work_items/components/work_item_milestone_spec.js b/spec/frontend/work_items/components/work_item_milestone_spec.js
index 60ba2b55f76..5997de01274 100644
--- a/spec/frontend/work_items/components/work_item_milestone_spec.js
+++ b/spec/frontend/work_items/components/work_item_milestone_spec.js
@@ -179,6 +179,18 @@ describe('WorkItemMilestone component', () => {
createComponent({ canUpdate: true });
});
+ it('calls successSearchQueryHandler with variables when dropdown is opened', async () => {
+ showDropdown();
+ await nextTick();
+
+ expect(successSearchQueryHandler).toHaveBeenCalledWith({
+ first: 20,
+ fullPath: 'full-path',
+ state: 'active',
+ title: '',
+ });
+ });
+
it('shows the skeleton loader when the items are being fetched on click', async () => {
showDropdown();
await nextTick();
diff --git a/spec/frontend/work_items/components/work_item_notes_spec.js b/spec/frontend/work_items/components/work_item_notes_spec.js
new file mode 100644
index 00000000000..ed68d214fc9
--- /dev/null
+++ b/spec/frontend/work_items/components/work_item_notes_spec.js
@@ -0,0 +1,107 @@
+import { GlSkeletonLoader } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import SystemNote from '~/work_items/components/notes/system_note.vue';
+import WorkItemNotes from '~/work_items/components/work_item_notes.vue';
+import workItemNotesQuery from '~/work_items/graphql/work_item_notes.query.graphql';
+import workItemNotesByIidQuery from '~/work_items/graphql/work_item_notes_by_iid.query.graphql';
+import { WIDGET_TYPE_NOTES } from '~/work_items/constants';
+import {
+ mockWorkItemNotesResponse,
+ workItemQueryResponse,
+ mockWorkItemNotesByIidResponse,
+} from '../mock_data';
+
+const mockWorkItemId = workItemQueryResponse.data.workItem.id;
+const mockNotesWidgetResponse = mockWorkItemNotesResponse.data.workItem.widgets.find(
+ (widget) => widget.type === WIDGET_TYPE_NOTES,
+);
+
+const mockNotesByIidWidgetResponse = mockWorkItemNotesByIidResponse.data.workspace.workItems.nodes[0].widgets.find(
+ (widget) => widget.type === WIDGET_TYPE_NOTES,
+);
+
+describe('WorkItemNotes component', () => {
+ let wrapper;
+
+ Vue.use(VueApollo);
+
+ const findAllSystemNotes = () => wrapper.findAllComponents(SystemNote);
+ const findActivityLabel = () => wrapper.find('label');
+ const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
+ const workItemNotesQueryHandler = jest.fn().mockResolvedValue(mockWorkItemNotesResponse);
+ const workItemNotesByIidQueryHandler = jest
+ .fn()
+ .mockResolvedValue(mockWorkItemNotesByIidResponse);
+
+ const createComponent = ({ workItemId = mockWorkItemId, fetchByIid = false } = {}) => {
+ wrapper = shallowMount(WorkItemNotes, {
+ apolloProvider: createMockApollo([
+ [workItemNotesQuery, workItemNotesQueryHandler],
+ [workItemNotesByIidQuery, workItemNotesByIidQueryHandler],
+ ]),
+ propsData: {
+ workItemId,
+ queryVariables: {
+ id: workItemId,
+ },
+ fullPath: 'test-path',
+ fetchByIid,
+ },
+ provide: {
+ glFeatures: {
+ useIidInWorkItemsPath: fetchByIid,
+ },
+ },
+ });
+ };
+
+ beforeEach(async () => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders activity label', () => {
+ expect(findActivityLabel().exists()).toBe(true);
+ });
+
+ describe('when notes are loading', () => {
+ it('renders skeleton loader', () => {
+ expect(findSkeletonLoader().exists()).toBe(true);
+ });
+
+ it('does not render system notes', () => {
+ expect(findAllSystemNotes().exists()).toBe(false);
+ });
+ });
+
+ describe('when notes have been loaded', () => {
+ it('does not render skeleton loader', () => {
+ expect(findSkeletonLoader().exists()).toBe(true);
+ });
+
+ it('renders system notes to the length of the response', async () => {
+ await waitForPromises();
+ expect(findAllSystemNotes()).toHaveLength(mockNotesWidgetResponse.discussions.nodes.length);
+ });
+ });
+
+ describe('when the notes are fetched by `iid`', () => {
+ beforeEach(async () => {
+ createComponent({ workItemId: mockWorkItemId, fetchByIid: true });
+ await waitForPromises();
+ });
+
+ it('shows the notes list', () => {
+ expect(findAllSystemNotes()).toHaveLength(
+ mockNotesByIidWidgetResponse.discussions.nodes.length,
+ );
+ });
+ });
+});
diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js
index 635a1f326f8..850672b68d0 100644
--- a/spec/frontend/work_items/mock_data.js
+++ b/spec/frontend/work_items/mock_data.js
@@ -36,6 +36,16 @@ export const mockLabels = [
},
];
+export const mockMilestone = {
+ __typename: 'Milestone',
+ id: 'gid://gitlab/Milestone/30',
+ title: 'v4.0',
+ state: 'active',
+ expired: false,
+ startDate: '2022-10-17',
+ dueDate: '2022-10-24',
+};
+
export const workItemQueryResponse = {
data: {
workItem: {
@@ -85,11 +95,18 @@ export const workItemQueryResponse = {
{
__typename: 'WorkItemWidgetHierarchy',
type: 'HIERARCHY',
+ hasChildren: true,
parent: {
id: 'gid://gitlab/Issue/1',
iid: '5',
title: 'Parent title',
confidential: false,
+ webUrl: 'http://gdk.test/gitlab-org/gitlab/-/issues/1',
+ workItemType: {
+ id: 'gid://gitlab/WorkItems::Type/1',
+ name: 'Issue',
+ iconName: 'issue-type-issue',
+ },
},
children: {
nodes: [
@@ -97,6 +114,20 @@ export const workItemQueryResponse = {
id: 'gid://gitlab/WorkItem/444',
createdAt: '2022-08-03T12:41:54Z',
closedAt: null,
+ confidential: false,
+ title: '123',
+ state: 'OPEN',
+ workItemType: {
+ id: '1',
+ name: 'Task',
+ iconName: 'issue-type-task',
+ },
+ widgets: [
+ {
+ type: 'HIERARCHY',
+ hasChildren: false,
+ },
+ ],
},
],
},
@@ -138,13 +169,25 @@ export const updateWorkItemMutationResponse = {
},
widgets: [
{
+ type: 'HIERARCHY',
children: {
nodes: [
{
id: 'gid://gitlab/WorkItem/444',
+ createdAt: '2022-08-03T12:41:54Z',
+ closedAt: null,
+ confidential: false,
+ title: '123',
+ state: 'OPEN',
+ workItemType: {
+ id: '1',
+ name: 'Task',
+ iconName: 'issue-type-task',
+ },
},
],
},
+ __typename: 'WorkItemConnection',
},
{
__typename: 'WorkItemWidgetAssignees',
@@ -177,6 +220,12 @@ export const mockParent = {
iid: '5',
title: 'Parent title',
confidential: false,
+ webUrl: 'http://gdk.test/gitlab-org/gitlab/-/issues/1',
+ workItemType: {
+ id: 'gid://gitlab/WorkItems::Type/1',
+ name: 'Issue',
+ iconName: 'issue-type-issue',
+ },
},
};
@@ -193,6 +242,20 @@ export const descriptionHtmlWithCheckboxes = `
</ul>
`;
+const taskType = {
+ __typename: 'WorkItemType',
+ id: 'gid://gitlab/WorkItems::Type/5',
+ name: 'Task',
+ iconName: 'issue-type-task',
+};
+
+export const objectiveType = {
+ __typename: 'WorkItemType',
+ id: 'gid://gitlab/WorkItems::Type/2411',
+ name: 'Objective',
+ iconName: 'issue-type-objective',
+};
+
export const workItemResponseFactory = ({
canUpdate = false,
canDelete = false,
@@ -201,8 +264,10 @@ export const workItemResponseFactory = ({
datesWidgetPresent = true,
labelsWidgetPresent = true,
weightWidgetPresent = true,
+ progressWidgetPresent = true,
milestoneWidgetPresent = true,
iterationWidgetPresent = true,
+ healthStatusWidgetPresent = true,
confidential = false,
canInviteMembers = false,
allowsScopedLabels = false,
@@ -210,6 +275,7 @@ export const workItemResponseFactory = ({
lastEditedBy = null,
withCheckboxes = false,
parent = mockParent.parent,
+ workItemType = taskType,
} = {}) => ({
data: {
workItem: {
@@ -227,12 +293,7 @@ export const workItemResponseFactory = ({
id: '1',
fullPath: 'test-project-path',
},
- workItemType: {
- __typename: 'WorkItemType',
- id: 'gid://gitlab/WorkItems::Type/5',
- name: 'Task',
- iconName: 'issue-type-task',
- },
+ workItemType,
userPermissions: {
deleteWorkItem: canDelete,
updateWorkItem: canUpdate,
@@ -298,26 +359,51 @@ export const workItemResponseFactory = ({
},
}
: { type: 'MOCK TYPE' },
+ progressWidgetPresent
+ ? {
+ __typename: 'WorkItemWidgetProgress',
+ type: 'PROGRESS',
+ progress: 0,
+ }
+ : { type: 'MOCK TYPE' },
milestoneWidgetPresent
? {
__typename: 'WorkItemWidgetMilestone',
type: 'MILESTONE',
- milestone: {
- expired: false,
- id: 'gid://gitlab/Milestone/30',
- title: 'v4.0',
- },
+ milestone: mockMilestone,
+ }
+ : { type: 'MOCK TYPE' },
+ healthStatusWidgetPresent
+ ? {
+ __typename: 'WorkItemWidgetHealthStatus',
+ type: 'HEALTH_STATUS',
+ healthStatus: 'onTrack',
}
: { type: 'MOCK TYPE' },
{
__typename: 'WorkItemWidgetHierarchy',
type: 'HIERARCHY',
+ hasChildren: true,
children: {
nodes: [
{
id: 'gid://gitlab/WorkItem/444',
createdAt: '2022-08-03T12:41:54Z',
closedAt: null,
+ confidential: false,
+ title: '123',
+ state: 'OPEN',
+ workItemType: {
+ id: '1',
+ name: 'Task',
+ iconName: 'issue-type-task',
+ },
+ widgets: [
+ {
+ type: 'HIERARCHY',
+ hasChildren: false,
+ },
+ ],
},
],
},
@@ -637,6 +723,8 @@ export const workItemHierarchyEmptyResponse = {
id: 'gid://gitlab/WorkItem/1',
workItemType: {
id: 'gid://gitlab/WorkItems::Type/6',
+ name: 'Issue',
+ iconName: 'issue-type-issue',
__typename: 'WorkItemType',
},
title: 'New title',
@@ -660,6 +748,7 @@ export const workItemHierarchyEmptyResponse = {
{
type: 'HIERARCHY',
parent: null,
+ hasChildren: false,
children: {
nodes: [],
__typename: 'WorkItemConnection',
@@ -678,6 +767,8 @@ export const workItemHierarchyNoUpdatePermissionResponse = {
id: 'gid://gitlab/WorkItem/1',
workItemType: {
id: 'gid://gitlab/WorkItems::Type/6',
+ name: 'Issue',
+ iconName: 'issue-type-issue',
__typename: 'WorkItemType',
},
title: 'New title',
@@ -699,12 +790,16 @@ export const workItemHierarchyNoUpdatePermissionResponse = {
{
type: 'HIERARCHY',
parent: null,
+ hasChildren: true,
children: {
nodes: [
{
id: 'gid://gitlab/WorkItem/2',
+ iid: '2',
workItemType: {
id: 'gid://gitlab/WorkItems::Type/5',
+ name: 'Task',
+ iconName: 'issue-type-task',
__typename: 'WorkItemType',
},
title: 'xyz',
@@ -712,6 +807,12 @@ export const workItemHierarchyNoUpdatePermissionResponse = {
confidential: false,
createdAt: '2022-08-03T12:41:54Z',
closedAt: null,
+ widgets: [
+ {
+ type: 'HIERARCHY',
+ hasChildren: false,
+ },
+ ],
__typename: 'WorkItem',
},
],
@@ -727,8 +828,11 @@ export const workItemHierarchyNoUpdatePermissionResponse = {
export const workItemTask = {
id: 'gid://gitlab/WorkItem/4',
+ iid: '4',
workItemType: {
id: 'gid://gitlab/WorkItems::Type/5',
+ name: 'Task',
+ iconName: 'issue-type-task',
__typename: 'WorkItemType',
},
title: 'bar',
@@ -741,8 +845,11 @@ export const workItemTask = {
export const confidentialWorkItemTask = {
id: 'gid://gitlab/WorkItem/2',
+ iid: '2',
workItemType: {
id: 'gid://gitlab/WorkItems::Type/5',
+ name: 'Task',
+ iconName: 'issue-type-task',
__typename: 'WorkItemType',
},
title: 'xyz',
@@ -755,8 +862,11 @@ export const confidentialWorkItemTask = {
export const closedWorkItemTask = {
id: 'gid://gitlab/WorkItem/3',
+ iid: '3',
workItemType: {
id: 'gid://gitlab/WorkItems::Type/5',
+ name: 'Task',
+ iconName: 'issue-type-task',
__typename: 'WorkItemType',
},
title: 'abc',
@@ -767,12 +877,153 @@ export const closedWorkItemTask = {
__typename: 'WorkItem',
};
+export const childrenWorkItems = [
+ confidentialWorkItemTask,
+ closedWorkItemTask,
+ workItemTask,
+ {
+ id: 'gid://gitlab/WorkItem/5',
+ iid: '5',
+ workItemType: {
+ id: 'gid://gitlab/WorkItems::Type/5',
+ name: 'Task',
+ iconName: 'issue-type-task',
+ __typename: 'WorkItemType',
+ },
+ title: 'foobar',
+ state: 'OPEN',
+ confidential: false,
+ createdAt: '2022-08-03T12:41:54Z',
+ closedAt: null,
+ __typename: 'WorkItem',
+ },
+];
+
export const workItemHierarchyResponse = {
data: {
workItem: {
id: 'gid://gitlab/WorkItem/1',
+ iid: '1',
workItemType: {
id: 'gid://gitlab/WorkItems::Type/6',
+ name: 'Objective',
+ iconName: 'issue-type-objective',
+ __typename: 'WorkItemType',
+ },
+ title: 'New title',
+ userPermissions: {
+ deleteWorkItem: true,
+ updateWorkItem: true,
+ },
+ confidential: false,
+ project: {
+ __typename: 'Project',
+ id: '1',
+ fullPath: 'test-project-path',
+ },
+ widgets: [
+ {
+ type: 'DESCRIPTION',
+ __typename: 'WorkItemWidgetDescription',
+ },
+ {
+ type: 'HIERARCHY',
+ parent: null,
+ hasChildren: true,
+ children: {
+ nodes: childrenWorkItems,
+ __typename: 'WorkItemConnection',
+ },
+ __typename: 'WorkItemWidgetHierarchy',
+ },
+ ],
+ __typename: 'WorkItem',
+ },
+ },
+};
+
+export const workItemObjectiveWithChild = {
+ id: 'gid://gitlab/WorkItem/12',
+ iid: '12',
+ workItemType: {
+ id: 'gid://gitlab/WorkItems::Type/2411',
+ name: 'Objective',
+ iconName: 'issue-type-objective',
+ __typename: 'WorkItemType',
+ },
+ project: {
+ __typename: 'Project',
+ id: '1',
+ fullPath: 'test-project-path',
+ },
+ userPermissions: {
+ deleteWorkItem: true,
+ updateWorkItem: true,
+ },
+ title: 'Objective',
+ description: 'Objective description',
+ state: 'OPEN',
+ confidential: false,
+ createdAt: '2022-08-03T12:41:54Z',
+ closedAt: null,
+ widgets: [
+ {
+ type: 'HIERARCHY',
+ hasChildren: true,
+ parent: null,
+ children: {
+ nodes: [],
+ },
+ __typename: 'WorkItemWidgetHierarchy',
+ },
+ {
+ type: 'MILESTONE',
+ __typename: 'WorkItemWidgetMilestone',
+ milestone: mockMilestone,
+ },
+ {
+ type: 'ASSIGNEES',
+ __typename: 'WorkItemWidgetAssignees',
+ canInviteMembers: true,
+ allowsMultipleAssignees: true,
+ assignees: {
+ __typename: 'UserCoreConnection',
+ nodes: mockAssignees,
+ },
+ },
+ {
+ type: 'LABELS',
+ __typename: 'WorkItemWidgetLabels',
+ allowsScopedLabels: true,
+ labels: {
+ __typename: 'LabelConnection',
+ nodes: mockLabels,
+ },
+ },
+ ],
+ __typename: 'WorkItem',
+};
+
+export const workItemObjectiveNoMetadata = {
+ ...workItemObjectiveWithChild,
+ widgets: [
+ {
+ type: 'HIERARCHY',
+ hasChildren: true,
+ __typename: 'WorkItemWidgetHierarchy',
+ },
+ ],
+};
+
+export const workItemHierarchyTreeResponse = {
+ data: {
+ workItem: {
+ id: 'gid://gitlab/WorkItem/2',
+ iid: '2',
+ workItemType: {
+ id: 'gid://gitlab/WorkItems::Type/2411',
+ name: 'Objective',
+ iconName: 'issue-type-objective',
__typename: 'WorkItemType',
},
title: 'New title',
@@ -794,22 +1045,30 @@ export const workItemHierarchyResponse = {
{
type: 'HIERARCHY',
parent: null,
+ hasChildren: true,
children: {
nodes: [
- confidentialWorkItemTask,
- closedWorkItemTask,
- workItemTask,
{
- id: 'gid://gitlab/WorkItem/5',
+ id: 'gid://gitlab/WorkItem/13',
+ iid: '13',
workItemType: {
- id: 'gid://gitlab/WorkItems::Type/5',
+ id: 'gid://gitlab/WorkItems::Type/2411',
+ name: 'Objective',
+ iconName: 'issue-type-objective',
__typename: 'WorkItemType',
},
- title: 'foobar',
+ title: 'Objective 2',
state: 'OPEN',
confidential: false,
createdAt: '2022-08-03T12:41:54Z',
closedAt: null,
+ widgets: [
+ {
+ type: 'HIERARCHY',
+ hasChildren: true,
+ __typename: 'WorkItemWidgetHierarchy',
+ },
+ ],
__typename: 'WorkItem',
},
],
@@ -823,6 +1082,15 @@ export const workItemHierarchyResponse = {
},
};
+export const workItemHierarchyTreeFailureResponse = {
+ data: {},
+ errors: [
+ {
+ message: 'Something went wrong',
+ },
+ ],
+};
+
export const changeWorkItemParentMutationResponse = {
data: {
workItemUpdate: {
@@ -856,6 +1124,7 @@ export const changeWorkItemParentMutationResponse = {
__typename: 'WorkItemWidgetHierarchy',
type: 'HIERARCHY',
parent: null,
+ hasChildren: false,
children: {
nodes: [],
},
@@ -1196,3 +1465,288 @@ export const projectWorkItemResponse = {
},
},
};
+
+export const mockWorkItemNotesResponse = {
+ data: {
+ workItem: {
+ id: 'gid://gitlab/WorkItem/600',
+ iid: '60',
+ widgets: [
+ {
+ __typename: 'WorkItemWidgetIteration',
+ },
+ {
+ __typename: 'WorkItemWidgetWeight',
+ },
+ {
+ __typename: 'WorkItemWidgetAssignees',
+ },
+ {
+ __typename: 'WorkItemWidgetLabels',
+ },
+ {
+ __typename: 'WorkItemWidgetDescription',
+ },
+ {
+ __typename: 'WorkItemWidgetHierarchy',
+ },
+ {
+ __typename: 'WorkItemWidgetStartAndDueDate',
+ },
+ {
+ __typename: 'WorkItemWidgetMilestone',
+ },
+ {
+ type: 'NOTES',
+ discussions: {
+ pageInfo: {
+ hasNextPage: false,
+ hasPreviousPage: false,
+ startCursor: null,
+ endCursor: null,
+ __typename: 'PageInfo',
+ },
+ nodes: [
+ {
+ id:
+ 'gid://gitlab/IndividualNoteDiscussion/8bbc4890b6ff0f2cde93a5a0947cd2b8a13d3b6e',
+ notes: {
+ nodes: [
+ {
+ id: 'gid://gitlab/Note/2428',
+ body: 'added #31 as parent issue',
+ bodyHtml:
+ '<p data-sourcepos="1:1-1:25" dir="auto">added <a href="/flightjs/Flight/-/issues/31" data-reference-type="issue" data-original="#31" data-link="false" data-link-reference="false" data-project="6" data-issue="224" data-project-path="flightjs/Flight" data-iid="31" data-issue-type="issue" data-container=body data-placement="top" title="Perferendis est quae totam quia laborum tempore ut voluptatem." class="gfm gfm-issue">#31</a> as parent issue</p>',
+ systemNoteIconName: 'link',
+ createdAt: '2022-11-14T04:18:59Z',
+ author: {
+ avatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ id: 'gid://gitlab/User/1',
+ name: 'Administrator',
+ username: 'root',
+ webUrl: 'http://127.0.0.1:3000/root',
+ __typename: 'UserCore',
+ },
+ __typename: 'Note',
+ },
+ ],
+ __typename: 'NoteConnection',
+ },
+ __typename: 'Discussion',
+ },
+ {
+ id:
+ 'gid://gitlab/IndividualNoteDiscussion/7b08b89a728a5ceb7de8334246837ba1d07270dc',
+ notes: {
+ nodes: [
+ {
+ id: 'gid://gitlab/MilestoneNote/not-persisted',
+ body: 'changed milestone to %5',
+ bodyHtml:
+ '<p data-sourcepos="1:1-1:23" dir="auto">changed milestone to <a href="/flightjs/Flight/-/milestones/5" data-reference-type="milestone" data-original="%5" data-link="false" data-link-reference="false" data-project="6" data-milestone="30" data-container=body data-placement="top" title="" class="gfm gfm-milestone has-tooltip">%v4.0</a></p>',
+ systemNoteIconName: 'clock',
+ createdAt: '2022-11-14T04:18:59Z',
+ author: {
+ avatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ id: 'gid://gitlab/User/1',
+ name: 'Administrator',
+ username: 'root',
+ webUrl: 'http://127.0.0.1:3000/root',
+ __typename: 'UserCore',
+ },
+ __typename: 'Note',
+ },
+ ],
+ __typename: 'NoteConnection',
+ },
+ __typename: 'Discussion',
+ },
+ {
+ id:
+ 'gid://gitlab/IndividualNoteDiscussion/0f2f195ec0d1ef95ee9d5b10446b8e96a7d83864',
+ notes: {
+ nodes: [
+ {
+ id: 'gid://gitlab/WeightNote/not-persisted',
+ body: 'changed weight to 89',
+ bodyHtml: '<p dir="auto">changed weight to <strong>89</strong></p>',
+ systemNoteIconName: 'weight',
+ createdAt: '2022-11-25T07:16:20Z',
+ author: {
+ avatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ id: 'gid://gitlab/User/1',
+ name: 'Administrator',
+ username: 'root',
+ webUrl: 'http://127.0.0.1:3000/root',
+ __typename: 'UserCore',
+ },
+ __typename: 'Note',
+ },
+ ],
+ __typename: 'NoteConnection',
+ },
+ __typename: 'Discussion',
+ },
+ ],
+ __typename: 'DiscussionConnection',
+ },
+ __typename: 'WorkItemWidgetNotes',
+ },
+ ],
+ __typename: 'WorkItem',
+ },
+ },
+};
+export const mockWorkItemNotesByIidResponse = {
+ data: {
+ workspace: {
+ id: 'gid://gitlab/Project/6',
+ workItems: {
+ nodes: [
+ {
+ id: 'gid://gitlab/WorkItem/600',
+ iid: '51',
+ widgets: [
+ {
+ __typename: 'WorkItemWidgetIteration',
+ },
+ {
+ __typename: 'WorkItemWidgetWeight',
+ },
+ {
+ __typename: 'WorkItemWidgetHealthStatus',
+ },
+ {
+ __typename: 'WorkItemWidgetAssignees',
+ },
+ {
+ __typename: 'WorkItemWidgetLabels',
+ },
+ {
+ __typename: 'WorkItemWidgetDescription',
+ },
+ {
+ __typename: 'WorkItemWidgetHierarchy',
+ },
+ {
+ __typename: 'WorkItemWidgetStartAndDueDate',
+ },
+ {
+ __typename: 'WorkItemWidgetMilestone',
+ },
+ {
+ type: 'NOTES',
+ discussions: {
+ pageInfo: {
+ hasNextPage: true,
+ hasPreviousPage: false,
+ startCursor: null,
+ endCursor:
+ 'eyJjcmVhdGVkX2F0IjoiMjAyMi0xMS0xNCAwNDoxOTowMC4wOTkxMTcwMDAgKzAwMDAiLCJpZCI6IjQyNyIsIl9rZCI6Im4ifQ==',
+ __typename: 'PageInfo',
+ },
+ nodes: [
+ {
+ id:
+ 'gid://gitlab/IndividualNoteDiscussion/8bbc4890b6ff0f2cde93a5a0947cd2b8a13d3b6e',
+ notes: {
+ nodes: [
+ {
+ id: 'gid://gitlab/Note/2428',
+ body: 'added #31 as parent issue',
+ bodyHtml:
+ '\u003cp data-sourcepos="1:1-1:25" dir="auto"\u003eadded \u003ca href="/flightjs/Flight/-/issues/31" data-reference-type="issue" data-original="#31" data-link="false" data-link-reference="false" data-project="6" data-issue="224" data-project-path="flightjs/Flight" data-iid="31" data-issue-type="issue" data-container="body" data-placement="top" title="Perferendis est quae totam quia laborum tempore ut voluptatem." class="gfm gfm-issue"\u003e#31\u003c/a\u003e as parent issue\u003c/p\u003e',
+ systemNoteIconName: 'link',
+ createdAt: '2022-11-14T04:18:59Z',
+ author: {
+ id: 'gid://gitlab/User/1',
+ avatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ name: 'Administrator',
+ username: 'root',
+ webUrl: 'http://127.0.0.1:3000/root',
+ __typename: 'UserCore',
+ },
+ __typename: 'Note',
+ },
+ ],
+ __typename: 'NoteConnection',
+ },
+ __typename: 'Discussion',
+ },
+ {
+ id:
+ 'gid://gitlab/IndividualNoteDiscussion/7b08b89a728a5ceb7de8334246837ba1d07270dc',
+ notes: {
+ nodes: [
+ {
+ id:
+ 'gid://gitlab/MilestoneNote/7b08b89a728a5ceb7de8334246837ba1d07270dc',
+ body: 'changed milestone to %5',
+ bodyHtml:
+ '\u003cp data-sourcepos="1:1-1:23" dir="auto"\u003echanged milestone to \u003ca href="/flightjs/Flight/-/milestones/5" data-reference-type="milestone" data-original="%5" data-link="false" data-link-reference="false" data-project="6" data-milestone="30" data-container="body" data-placement="top" title="" class="gfm gfm-milestone has-tooltip"\u003e%v4.0\u003c/a\u003e\u003c/p\u003e',
+ systemNoteIconName: 'clock',
+ createdAt: '2022-11-14T04:18:59Z',
+ author: {
+ id: 'gid://gitlab/User/1',
+ avatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ name: 'Administrator',
+ username: 'root',
+ webUrl: 'http://127.0.0.1:3000/root',
+ __typename: 'UserCore',
+ },
+ __typename: 'Note',
+ },
+ ],
+ __typename: 'NoteConnection',
+ },
+ __typename: 'Discussion',
+ },
+ {
+ id:
+ 'gid://gitlab/IndividualNoteDiscussion/addbc177f7664699a135130ab05ffb78c57e4db3',
+ notes: {
+ nodes: [
+ {
+ id:
+ 'gid://gitlab/IterationNote/addbc177f7664699a135130ab05ffb78c57e4db3',
+ body: 'changed iteration to *iteration:5352',
+ bodyHtml:
+ '\u003cp data-sourcepos="1:1-1:36" dir="auto"\u003echanged iteration to \u003ca href="/groups/flightjs/-/iterations/5352" data-reference-type="iteration" data-original="*iteration:5352" data-link="false" data-link-reference="false" data-project="6" data-iteration="5352" data-container="body" data-placement="top" title="Iteration" class="gfm gfm-iteration has-tooltip"\u003eEt autem debitis nam suscipit eos ut. Jul 13, 2022 - Jul 19, 2022\u003c/a\u003e\u003c/p\u003e',
+ systemNoteIconName: 'iteration',
+ createdAt: '2022-11-14T04:19:00Z',
+ author: {
+ id: 'gid://gitlab/User/1',
+ avatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ name: 'Administrator',
+ username: 'root',
+ webUrl: 'http://127.0.0.1:3000/root',
+ __typename: 'UserCore',
+ },
+ __typename: 'Note',
+ },
+ ],
+ __typename: 'NoteConnection',
+ },
+ __typename: 'Discussion',
+ },
+ ],
+ __typename: 'DiscussionConnection',
+ },
+ __typename: 'WorkItemWidgetNotes',
+ },
+ ],
+ __typename: 'WorkItem',
+ },
+ ],
+ __typename: 'WorkItemConnection',
+ },
+ __typename: 'Project',
+ },
+ },
+};
diff --git a/spec/frontend/work_items/pages/work_item_root_spec.js b/spec/frontend/work_items/pages/work_item_root_spec.js
index 880c4271024..a766962771a 100644
--- a/spec/frontend/work_items/pages/work_item_root_spec.js
+++ b/spec/frontend/work_items/pages/work_item_root_spec.js
@@ -55,7 +55,7 @@ describe('Work items root component', () => {
isModal: false,
workItemId: 'gid://gitlab/WorkItem/1',
workItemParentId: null,
- iid: '1',
+ workItemIid: '1',
});
});
diff --git a/spec/frontend/work_items/router_spec.js b/spec/frontend/work_items/router_spec.js
index 982f9f71f9e..b503d819435 100644
--- a/spec/frontend/work_items/router_spec.js
+++ b/spec/frontend/work_items/router_spec.js
@@ -1,14 +1,12 @@
import { mount } from '@vue/test-utils';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
-import workItemWeightSubscription from 'ee_component/work_items/graphql/work_item_weight.subscription.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
import {
workItemAssigneesSubscriptionResponse,
workItemDatesSubscriptionResponse,
workItemResponseFactory,
workItemTitleSubscriptionResponse,
- workItemWeightSubscriptionResponse,
workItemLabelsSubscriptionResponse,
workItemMilestoneSubscriptionResponse,
workItemDescriptionSubscriptionResponse,
@@ -25,6 +23,8 @@ import CreateWorkItem from '~/work_items/pages/create_work_item.vue';
import WorkItemsRoot from '~/work_items/pages/work_item_root.vue';
import { createRouter } from '~/work_items/router';
+jest.mock('~/behaviors/markdown/render_gfm');
+
describe('Work items router', () => {
let wrapper;
@@ -33,7 +33,6 @@ describe('Work items router', () => {
const workItemQueryHandler = jest.fn().mockResolvedValue(workItemResponseFactory());
const datesSubscriptionHandler = jest.fn().mockResolvedValue(workItemDatesSubscriptionResponse);
const titleSubscriptionHandler = jest.fn().mockResolvedValue(workItemTitleSubscriptionResponse);
- const weightSubscriptionHandler = jest.fn().mockResolvedValue(workItemWeightSubscriptionResponse);
const assigneesSubscriptionHandler = jest
.fn()
.mockResolvedValue(workItemAssigneesSubscriptionResponse);
@@ -61,10 +60,6 @@ describe('Work items router', () => {
[workItemDescriptionSubscription, descriptionSubscriptionHandler],
];
- if (IS_EE) {
- handlers.push([workItemWeightSubscription, weightSubscriptionHandler]);
- }
-
wrapper = mount(App, {
apolloProvider: createMockApollo(handlers),
router,
@@ -72,6 +67,13 @@ describe('Work items router', () => {
fullPath: 'full-path',
issuesListPath: 'full-path/-/issues',
hasIssueWeightsFeature: false,
+ hasIterationsFeature: false,
+ hasOkrsFeature: false,
+ hasIssuableHealthStatusFeature: false,
+ },
+ stubs: {
+ WorkItemWeight: true,
+ WorkItemIteration: true,
},
});
};
diff --git a/spec/frontend_integration/content_editor/content_editor_integration_spec.js b/spec/frontend_integration/content_editor/content_editor_integration_spec.js
index 2fa491196ff..8521e85a971 100644
--- a/spec/frontend_integration/content_editor/content_editor_integration_spec.js
+++ b/spec/frontend_integration/content_editor/content_editor_integration_spec.js
@@ -114,8 +114,6 @@ This reference tag is a mix of letters and numbers [^footnote].
});
it('renders table of contents', async () => {
- jest.useFakeTimers();
-
renderMarkdown.mockResolvedValueOnce(`
<ul class="section-nav">
</ul>
diff --git a/spec/graphql/graphql_triggers_spec.rb b/spec/graphql/graphql_triggers_spec.rb
index a54cb8a7988..00b5aec366e 100644
--- a/spec/graphql/graphql_triggers_spec.rb
+++ b/spec/graphql/graphql_triggers_spec.rb
@@ -116,4 +116,18 @@ RSpec.describe GraphqlTriggers do
GraphqlTriggers.merge_request_merge_status_updated(merge_request)
end
end
+
+ describe '.merge_request_approval_state_updated' do
+ it 'triggers the mergeRequestApprovalStateUpdated subscription' do
+ merge_request = build_stubbed(:merge_request)
+
+ expect(GitlabSchema.subscriptions).to receive(:trigger).with(
+ 'mergeRequestApprovalStateUpdated',
+ { issuable_id: merge_request.to_gid },
+ merge_request
+ ).and_call_original
+
+ GraphqlTriggers.merge_request_approval_state_updated(merge_request)
+ end
+ end
end
diff --git a/spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb b/spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb
index 31abbabe385..125e15b70cf 100644
--- a/spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb
+++ b/spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb
@@ -56,6 +56,15 @@ RSpec.describe Mutations::AlertManagement::Alerts::SetAssignees do
context 'when operation mode is not specified' do
it_behaves_like 'successful resolution'
it_behaves_like 'an incident management tracked event', :incident_management_alert_assigned
+
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { project.namespace.reload }
+ let(:category) { described_class.to_s }
+ let(:user) { current_user }
+ let(:action) { 'incident_management_alert_assigned' }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ end
end
context 'when user does not have permission to update alerts' do
diff --git a/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb b/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb
index ea5e21ec4b8..bcb7c74fa09 100644
--- a/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb
+++ b/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb
@@ -19,6 +19,15 @@ RSpec.describe Mutations::AlertManagement::Alerts::Todo::Create do
it_behaves_like 'an incident management tracked event', :incident_management_alert_todo
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { project.namespace.reload }
+ let(:category) { described_class.to_s }
+ let(:user) { current_user }
+ let(:action) { 'incident_management_alert_todo' }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ end
+
context 'when user does not have permissions' do
let(:current_user) { nil }
diff --git a/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb b/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb
index 4758ac526a5..e49596b37c9 100644
--- a/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb
+++ b/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb
@@ -30,6 +30,15 @@ RSpec.describe Mutations::AlertManagement::CreateAlertIssue do
it_behaves_like 'an incident management tracked event', :incident_management_incident_created
it_behaves_like 'an incident management tracked event', :incident_management_alert_create_incident
+
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { project.namespace.reload }
+ let(:category) { described_class.to_s }
+ let(:user) { current_user }
+ let(:action) { 'incident_management_incident_created' }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ end
end
context 'when CreateAlertIssue responds with an error' do
@@ -46,6 +55,15 @@ RSpec.describe Mutations::AlertManagement::CreateAlertIssue do
errors: ['An issue already exists']
)
end
+
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { project.namespace.reload }
+ let(:category) { described_class.to_s }
+ let(:user) { current_user }
+ let(:action) { 'incident_management_incident_created' }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ end
end
end
diff --git a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
index 2c2518e046a..22ad93df79b 100644
--- a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
+++ b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
@@ -35,6 +35,15 @@ RSpec.describe Mutations::AlertManagement::UpdateAlertStatus do
let(:user) { current_user }
end
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { project.namespace }
+ let(:category) { described_class.to_s }
+ let(:user) { current_user }
+ let(:action) { 'incident_management_alert_status_changed' }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ end
+
context 'error occurs when updating' do
it 'returns the alert with errors' do
# Stub an error on the alert
diff --git a/spec/graphql/mutations/ci/runner/bulk_delete_spec.rb b/spec/graphql/mutations/ci/runner/bulk_delete_spec.rb
index 2eccfd3409f..aaa74fa78aa 100644
--- a/spec/graphql/mutations/ci/runner/bulk_delete_spec.rb
+++ b/spec/graphql/mutations/ci/runner/bulk_delete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Mutations::Ci::Runner::BulkDelete do
+RSpec.describe Mutations::Ci::Runner::BulkDelete, feature_category: :runner_fleet do
include GraphqlHelpers
let_it_be(:admin_user) { create(:user, :admin) }
diff --git a/spec/graphql/mutations/ci/runner/delete_spec.rb b/spec/graphql/mutations/ci/runner/delete_spec.rb
index 06d360430f8..f19fa7c34a9 100644
--- a/spec/graphql/mutations/ci/runner/delete_spec.rb
+++ b/spec/graphql/mutations/ci/runner/delete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Mutations::Ci::Runner::Delete do
+RSpec.describe Mutations::Ci::Runner::Delete, feature_category: :runner_fleet do
include GraphqlHelpers
let_it_be(:runner) { create(:ci_runner) }
diff --git a/spec/graphql/mutations/ci/runner/update_spec.rb b/spec/graphql/mutations/ci/runner/update_spec.rb
index 098b7ac6aa4..e0c8219e0f6 100644
--- a/spec/graphql/mutations/ci/runner/update_spec.rb
+++ b/spec/graphql/mutations/ci/runner/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Mutations::Ci::Runner::Update do
+RSpec.describe Mutations::Ci::Runner::Update, feature_category: :runner_fleet do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/graphql/mutations/container_repositories/destroy_spec.rb b/spec/graphql/mutations/container_repositories/destroy_spec.rb
index 9f3ff8da80b..50e83ccdd30 100644
--- a/spec/graphql/mutations/container_repositories/destroy_spec.rb
+++ b/spec/graphql/mutations/container_repositories/destroy_spec.rb
@@ -55,23 +55,6 @@ RSpec.describe Mutations::ContainerRepositories::Destroy do
it_behaves_like params[:shared_examples_name]
end
-
- context 'with container_registry_delete_repository_with_cron_worker disabled' do
- before do
- project.add_maintainer(user)
- stub_feature_flags(container_registry_delete_repository_with_cron_worker: false)
- end
-
- it 'enqueues a removal job' do
- expect(::Packages::CreateEventService)
- .to receive(:new).with(nil, user, event_name: :delete_repository, scope: :container).and_call_original
- expect(DeleteContainerRepositoryWorker)
- .to receive(:perform_async).with(user.id, container_repository.id)
-
- expect { subject }.to change { ::Packages::Event.count }.by(1)
- expect(container_repository.reload.delete_scheduled?).to be true
- end
- end
end
end
end
diff --git a/spec/graphql/mutations/incident_management/timeline_event/update_spec.rb b/spec/graphql/mutations/incident_management/timeline_event/update_spec.rb
index 7081fb7117e..317e8f5fcb6 100644
--- a/spec/graphql/mutations/incident_management/timeline_event/update_spec.rb
+++ b/spec/graphql/mutations/incident_management/timeline_event/update_spec.rb
@@ -7,21 +7,33 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Update do
let_it_be(:reporter) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:incident) { create(:incident, project: project) }
+ let_it_be(:tag1) { create(:incident_management_timeline_event_tag, project: project, name: 'Tag 1') }
+ let_it_be(:tag2) { create(:incident_management_timeline_event_tag, project: project, name: 'Tag 2') }
let_it_be_with_reload(:timeline_event) do
create(:incident_management_timeline_event, project: project, incident: incident)
end
+ # Pre-attach a tag to the event
+ let_it_be(:tag_link1) do
+ create(:incident_management_timeline_event_tag_link,
+ timeline_event: timeline_event,
+ timeline_event_tag: tag1
+ )
+ end
+
let(:args) do
{
id: timeline_event_id,
note: note,
- occurred_at: occurred_at
+ occurred_at: occurred_at,
+ timeline_event_tag_names: tag_names
}
end
let(:note) { 'Updated Note' }
let(:timeline_event_id) { GitlabSchema.id_from_object(timeline_event).to_s }
let(:occurred_at) { 1.minute.ago }
+ let(:tag_names) { [] }
before do
project.add_developer(developer)
@@ -92,6 +104,36 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Update do
expect(resolve).to eq(timeline_event: nil, errors: ["Occurred at can't be blank"])
end
end
+
+ context 'when timeline event tag do not exist' do
+ let(:tag_names) { ['some other tag'] }
+
+ it 'does not update the timeline event' do
+ expect { resolve }.not_to change { timeline_event.reload.updated_at }
+ end
+
+ it 'responds with error' do
+ expect(resolve).to eq(timeline_event: nil, errors: ["Following tags don't exist: [\"some other tag\"]"])
+ end
+ end
+ end
+
+ context 'when timeline event tags are passed' do
+ let(:tag_names) { [tag2.name] }
+
+ it 'returns updated timeline event' do
+ expect(resolve).to eq(
+ timeline_event: timeline_event.reload,
+ errors: []
+ )
+ end
+
+ it 'removes tag1 and assigns tag2 to the event' do
+ response = resolve
+ timeline_event = response[:timeline_event]
+
+ expect(timeline_event.timeline_event_tags).to contain_exactly(tag2)
+ end
end
end
diff --git a/spec/graphql/mutations/issues/link_alerts_spec.rb b/spec/graphql/mutations/issues/link_alerts_spec.rb
new file mode 100644
index 00000000000..a6ce5cdd7ab
--- /dev/null
+++ b/spec/graphql/mutations/issues/link_alerts_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::Issues::LinkAlerts, feature_category: :incident_management do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:issue) { create(:incident, project: project) }
+ let_it_be(:alert1) { create(:alert_management_alert, project: project) }
+ let_it_be(:alert2) { create(:alert_management_alert, project: project) }
+
+ let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
+
+ specify { expect(described_class).to require_graphql_authorizations(:update_issue, :admin_issue) }
+
+ before_all do
+ project.add_guest(guest)
+ project.add_developer(developer)
+ end
+
+ describe '#resolve' do
+ let(:alert_references) { [alert1.to_reference, alert2.details_url, 'invalid-reference'] }
+
+ subject(:resolve) do
+ mutation.resolve(
+ project_path: issue.project.full_path,
+ iid: issue.iid,
+ alert_references: alert_references
+ )
+ end
+
+ context 'when the user is a guest' do
+ let(:user) { guest }
+
+ it 'raises an error' do
+ expect { resolve }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+
+ context 'when a user is also an author' do
+ let!(:issue) { create(:incident, project: project, author: user) }
+
+ it 'raises an error' do
+ expect { resolve }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'when a user is also an assignee' do
+ let!(:issue) { create(:incident, project: project, assignee_ids: [user.id]) }
+
+ it 'raises an error' do
+ expect { resolve }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+ end
+
+ context 'when the user is a developer' do
+ let(:user) { developer }
+
+ context 'when issue type is an incident' do
+ it 'calls LinkAlerts::CreateService with correct arguments' do
+ expect(::IncidentManagement::LinkAlerts::CreateService)
+ .to receive(:new)
+ .with(issue, user, alert_references)
+ .and_call_original
+
+ resolve
+ end
+
+ it 'returns no errors' do
+ expect(resolve[:errors]).to be_empty
+ end
+ end
+
+ context 'when issue type is not an incident' do
+ let!(:issue) { create(:issue, project: project) }
+
+ it 'does not update alert_management_alerts' do
+ expect { resolve }.not_to change { issue.alert_management_alerts }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/graphql/mutations/issues/unlink_alert_spec.rb b/spec/graphql/mutations/issues/unlink_alert_spec.rb
new file mode 100644
index 00000000000..2f1d5084faf
--- /dev/null
+++ b/spec/graphql/mutations/issues/unlink_alert_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::Issues::UnlinkAlert, feature_category: :incident_management do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:another_project) { create(:project) }
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:internal_alert) { create(:alert_management_alert, project: project) }
+ let_it_be(:external_alert) { create(:alert_management_alert, project: another_project) }
+ let_it_be(:issue) { create(:incident, project: project, alert_management_alerts: [internal_alert, external_alert]) }
+
+ let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
+
+ specify { expect(described_class).to require_graphql_authorizations(:update_issue, :admin_issue) }
+
+ before_all do
+ project.add_guest(guest)
+ project.add_developer(developer)
+ end
+
+ describe '#resolve' do
+ let(:alert_to_unlink) { internal_alert }
+
+ subject(:resolve) do
+ mutation.resolve(
+ project_path: issue.project.full_path,
+ iid: issue.iid,
+ alert_id: alert_to_unlink.to_global_id.to_s
+ )
+ end
+
+ context 'when the user is a guest' do
+ let(:user) { guest }
+
+ it 'raises an error' do
+ expect { resolve }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'when the user is a developer' do
+ let(:user) { developer }
+
+ shared_examples 'unlinking an alert' do
+ it 'unlinks the alert' do
+ expect { resolve }.to change { issue.reload.alert_management_alerts }.to match_array(remainded_alerts)
+ end
+
+ it 'returns no errors' do
+ expect(resolve[:errors]).to be_empty
+ end
+ end
+
+ context 'when unlinking internal alert' do
+ let(:alert_to_unlink) { internal_alert }
+ let(:remainded_alerts) { [external_alert] }
+
+ it_behaves_like 'unlinking an alert'
+ end
+
+ context 'when unlinking external alert' do
+ let(:alert_to_unlink) { external_alert }
+ let(:remainded_alerts) { [internal_alert] }
+
+ it_behaves_like 'unlinking an alert'
+ end
+
+ context 'when LinkAlerts::DestroyService responds with an error' do
+ it 'returns the error' do
+ service_instance = instance_double(
+ ::IncidentManagement::LinkAlerts::DestroyService,
+ execute: ServiceResponse.error(message: 'some error message')
+ )
+
+ allow(::IncidentManagement::LinkAlerts::DestroyService).to receive(:new).and_return(service_instance)
+
+ expect(resolve[:errors]).to match_array(['some error message'])
+ end
+ end
+ end
+ end
+end
diff --git a/spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb b/spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb
index 2a7d0a8171b..5c632ed3443 100644
--- a/spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb
@@ -11,29 +11,46 @@ RSpec.describe Resolvers::Ci::AllJobsResolver do
let_it_be(:pending_job) { create(:ci_build, :pending, name: 'Job Three') }
let(:args) { {} }
- let(:current_user) { create(:admin) }
subject { resolve_jobs(args) }
describe '#resolve' do
- context 'with authorized user' do
- context 'with statuses argument' do
- let(:args) { { statuses: [Types::Ci::JobStatusEnum.coerce_isolated_input('SUCCESS')] } }
+ context 'with admin' do
+ let(:current_user) { create(:admin) }
- it { is_expected.to contain_exactly(successful_job, successful_job_two) }
- end
+ shared_examples 'executes as admin' do
+ context 'with statuses argument' do
+ let(:args) { { statuses: [Types::Ci::JobStatusEnum.coerce_isolated_input('SUCCESS')] } }
+
+ it { is_expected.to contain_exactly(successful_job, successful_job_two) }
+ end
+
+ context 'with multiple statuses' do
+ let(:args) do
+ { statuses: [Types::Ci::JobStatusEnum.coerce_isolated_input('SUCCESS'),
+ Types::Ci::JobStatusEnum.coerce_isolated_input('FAILED')] }
+ end
+
+ it { is_expected.to contain_exactly(successful_job, successful_job_two, failed_job) }
+ end
- context 'with multiple statuses' do
- let(:args) do
- { statuses: [Types::Ci::JobStatusEnum.coerce_isolated_input('SUCCESS'),
- Types::Ci::JobStatusEnum.coerce_isolated_input('FAILED')] }
+ context 'without statuses argument' do
+ it { is_expected.to contain_exactly(successful_job, successful_job_two, failed_job, pending_job) }
end
+ end
- it { is_expected.to contain_exactly(successful_job, successful_job_two, failed_job) }
+ context 'when admin mode setting is disabled', :do_not_mock_admin_mode_setting do
+ it_behaves_like 'executes as admin'
end
- context 'without statuses argument' do
- it { is_expected.to contain_exactly(successful_job, successful_job_two, failed_job, pending_job) }
+ context 'when admin mode setting is enabled' do
+ context 'when in admin mode', :enable_admin_mode do
+ it_behaves_like 'executes as admin'
+ end
+
+ context 'when not in admin mode' do
+ it { is_expected.to be_empty }
+ end
end
end
diff --git a/spec/graphql/resolvers/ci/group_runners_resolver_spec.rb b/spec/graphql/resolvers/ci/group_runners_resolver_spec.rb
index 57b2fcbea63..5d06db904d5 100644
--- a/spec/graphql/resolvers/ci/group_runners_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/group_runners_resolver_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Resolvers::Ci::GroupRunnersResolver do
+RSpec.describe Resolvers::Ci::GroupRunnersResolver, feature_category: :runner_fleet do
include GraphqlHelpers
describe '#resolve' do
@@ -78,7 +78,7 @@ RSpec.describe Resolvers::Ci::GroupRunnersResolver do
status_status: 'active',
type_type: :group_type,
tag_name: ['active_runner'],
- preload: { tag_name: nil },
+ preload: { tag_name: false },
search: 'abc',
sort: 'contacted_asc',
membership: :descendants,
diff --git a/spec/graphql/resolvers/ci/project_runners_resolver_spec.rb b/spec/graphql/resolvers/ci/project_runners_resolver_spec.rb
new file mode 100644
index 00000000000..4cc00ced104
--- /dev/null
+++ b/spec/graphql/resolvers/ci/project_runners_resolver_spec.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Ci::ProjectRunnersResolver, feature_category: :runner_fleet do
+ include GraphqlHelpers
+
+ describe '#resolve' do
+ subject do
+ resolve(described_class, obj: obj, ctx: { current_user: user }, args: args,
+ arg_style: :internal)
+ end
+
+ include_context 'runners resolver setup'
+
+ let(:obj) { project }
+ let(:args) { {} }
+
+ context 'when user cannot see runners' do
+ it 'returns no runners' do
+ expect(subject.items.to_a).to eq([])
+ end
+ end
+
+ context 'with user as project admin' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ let(:available_runners) { [inactive_project_runner, offline_project_runner, group_runner, instance_runner] }
+
+ it 'returns all runners available to the project' do
+ expect(subject.items.to_a).to match_array(available_runners)
+ end
+ end
+
+ context 'with obj set to nil' do
+ let(:obj) { nil }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error('Expected project missing')
+ end
+ end
+
+ context 'with obj not set to project' do
+ let(:obj) { build(:group) }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error('Expected project missing')
+ end
+ end
+
+ describe 'Allowed query arguments' do
+ let(:finder) { instance_double(::Ci::RunnersFinder) }
+ let(:args) do
+ {
+ status: 'active',
+ type: :group_type,
+ tag_list: ['active_runner'],
+ search: 'abc',
+ sort: :contacted_asc
+ }
+ end
+
+ let(:expected_params) do
+ {
+ status_status: 'active',
+ type_type: :group_type,
+ tag_name: ['active_runner'],
+ preload: { tag_name: false },
+ search: 'abc',
+ sort: 'contacted_asc',
+ project: project
+ }
+ end
+
+ it 'calls RunnersFinder with expected arguments' do
+ allow(::Ci::RunnersFinder).to receive(:new).with(current_user: user,
+ params: expected_params).once.and_return(finder)
+ allow(finder).to receive(:execute).once.and_return([:execute_return_value])
+
+ expect(subject.items.to_a).to eq([:execute_return_value])
+ end
+ end
+ end
+end
diff --git a/spec/graphql/resolvers/ci/runner_groups_resolver_spec.rb b/spec/graphql/resolvers/ci/runner_groups_resolver_spec.rb
new file mode 100644
index 00000000000..9272689ef0b
--- /dev/null
+++ b/spec/graphql/resolvers/ci/runner_groups_resolver_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Ci::RunnerGroupsResolver, feature_category: :runner_fleet do
+ include GraphqlHelpers
+
+ let_it_be(:group1) { create(:group) }
+ let_it_be(:runner) { create(:ci_runner, :group, groups: [group1]) }
+
+ let(:args) { {} }
+
+ subject(:response) { resolve_groups(args) }
+
+ describe '#resolve' do
+ context 'with authorized user', :enable_admin_mode do
+ let(:current_user) { create(:user, :admin) }
+
+ it 'returns a lazy value with all groups' do
+ expect(response).to be_a(GraphQL::Execution::Lazy)
+ expect(response.value).to contain_exactly(group1)
+ end
+ end
+
+ context 'with unauthorized user' do
+ let(:current_user) { create(:user) }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ private
+
+ def resolve_groups(args = {}, context = { current_user: current_user })
+ resolve(described_class, obj: runner, args: args, ctx: context)
+ end
+end
diff --git a/spec/graphql/resolvers/ci/runner_jobs_resolver_spec.rb b/spec/graphql/resolvers/ci/runner_jobs_resolver_spec.rb
index ba8a127bec5..963a642fa4e 100644
--- a/spec/graphql/resolvers/ci/runner_jobs_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/runner_jobs_resolver_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Resolvers::Ci::RunnerJobsResolver do
+RSpec.describe Resolvers::Ci::RunnerJobsResolver, feature_category: :runner_fleet do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/graphql/resolvers/ci/runner_platforms_resolver_spec.rb b/spec/graphql/resolvers/ci/runner_platforms_resolver_spec.rb
index 3cb6e94e81e..1d1fb4a9967 100644
--- a/spec/graphql/resolvers/ci/runner_platforms_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/runner_platforms_resolver_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Resolvers::Ci::RunnerPlatformsResolver do
+RSpec.describe Resolvers::Ci::RunnerPlatformsResolver, feature_category: :runner_fleet do
include GraphqlHelpers
describe '#resolve' do
diff --git a/spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb b/spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb
index 952c7337d65..6c69cdc19cc 100644
--- a/spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Resolvers::Ci::RunnerProjectsResolver do
+RSpec.describe Resolvers::Ci::RunnerProjectsResolver, feature_category: :runner_fleet do
include GraphqlHelpers
let_it_be(:project1) { create(:project, description: 'Project1.1') }
diff --git a/spec/graphql/resolvers/ci/runner_setup_resolver_spec.rb b/spec/graphql/resolvers/ci/runner_setup_resolver_spec.rb
index 13ef89023d9..734337f7c92 100644
--- a/spec/graphql/resolvers/ci/runner_setup_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/runner_setup_resolver_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Resolvers::Ci::RunnerSetupResolver do
+RSpec.describe Resolvers::Ci::RunnerSetupResolver, feature_category: :runner_fleet do
include GraphqlHelpers
describe '#resolve' do
diff --git a/spec/graphql/resolvers/ci/runner_status_resolver_spec.rb b/spec/graphql/resolvers/ci/runner_status_resolver_spec.rb
index fbef07b72e6..2bea256856d 100644
--- a/spec/graphql/resolvers/ci/runner_status_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/runner_status_resolver_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Resolvers::Ci::RunnerStatusResolver do
+RSpec.describe Resolvers::Ci::RunnerStatusResolver, feature_category: :runner_fleet do
include GraphqlHelpers
describe '#resolve' do
diff --git a/spec/graphql/resolvers/ci/runners_resolver_spec.rb b/spec/graphql/resolvers/ci/runners_resolver_spec.rb
index 4038192a68a..d6da8222234 100644
--- a/spec/graphql/resolvers/ci/runners_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/runners_resolver_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Resolvers::Ci::RunnersResolver do
+RSpec.describe Resolvers::Ci::RunnersResolver, feature_category: :runner_fleet do
include GraphqlHelpers
describe '#resolve' do
@@ -28,8 +28,24 @@ RSpec.describe Resolvers::Ci::RunnersResolver do
context 'when user can see runners' do
let(:obj) { nil }
- it 'returns all the runners' do
- expect(subject.items.to_a).to contain_exactly(inactive_project_runner, offline_project_runner, group_runner, subgroup_runner, instance_runner)
+ context 'when admin mode setting is disabled', :do_not_mock_admin_mode_setting do
+ it 'returns all the runners' do
+ expect(subject.items.to_a).to contain_exactly(inactive_project_runner, offline_project_runner, group_runner, subgroup_runner, instance_runner)
+ end
+ end
+
+ context 'when admin mode setting is enabled' do
+ context 'when in admin mode', :enable_admin_mode do
+ it 'returns all the runners' do
+ expect(subject.items.to_a).to contain_exactly(inactive_project_runner, offline_project_runner, group_runner, subgroup_runner, instance_runner)
+ end
+ end
+
+ context 'when not in admin mode' do
+ it 'returns no runners' do
+ expect(subject.items.to_a).to eq([])
+ end
+ end
end
end
@@ -67,7 +83,7 @@ RSpec.describe Resolvers::Ci::RunnersResolver do
upgrade_status: 'recommended',
type_type: :instance_type,
tag_name: ['active_runner'],
- preload: { tag_name: nil },
+ preload: { tag_name: false },
search: 'abc',
sort: 'contacted_asc'
}
@@ -92,7 +108,7 @@ RSpec.describe Resolvers::Ci::RunnersResolver do
let(:expected_params) do
{
active: false,
- preload: { tag_name: nil }
+ preload: { tag_name: false }
}
end
@@ -112,7 +128,7 @@ RSpec.describe Resolvers::Ci::RunnersResolver do
let(:expected_params) do
{
active: false,
- preload: { tag_name: nil }
+ preload: { tag_name: false }
}
end
@@ -131,7 +147,7 @@ RSpec.describe Resolvers::Ci::RunnersResolver do
let(:expected_params) do
{
- preload: { tag_name: nil }
+ preload: { tag_name: false }
}
end
diff --git a/spec/graphql/resolvers/design_management/design_resolver_spec.rb b/spec/graphql/resolvers/design_management/design_resolver_spec.rb
index 0915dddf438..3a62f24993a 100644
--- a/spec/graphql/resolvers/design_management/design_resolver_spec.rb
+++ b/spec/graphql/resolvers/design_management/design_resolver_spec.rb
@@ -6,14 +6,14 @@ RSpec.describe Resolvers::DesignManagement::DesignResolver do
include GraphqlHelpers
include DesignManagementTestHelpers
- specify do
- expect(described_class).to have_nullable_graphql_type(::Types::DesignManagement::DesignType)
- end
-
before do
enable_design_management
end
+ specify do
+ expect(described_class).to have_nullable_graphql_type(::Types::DesignManagement::DesignType)
+ end
+
describe '#resolve' do
let_it_be(:issue) { create(:issue) }
let_it_be(:project) { issue.project }
diff --git a/spec/graphql/resolvers/design_management/designs_resolver_spec.rb b/spec/graphql/resolvers/design_management/designs_resolver_spec.rb
index 64eae14d888..7024e62c670 100644
--- a/spec/graphql/resolvers/design_management/designs_resolver_spec.rb
+++ b/spec/graphql/resolvers/design_management/designs_resolver_spec.rb
@@ -6,14 +6,14 @@ RSpec.describe Resolvers::DesignManagement::DesignsResolver do
include GraphqlHelpers
include DesignManagementTestHelpers
- specify do
- expect(described_class).to have_nullable_graphql_type(::Types::DesignManagement::DesignType.connection_type)
- end
-
before do
enable_design_management
end
+ specify do
+ expect(described_class).to have_nullable_graphql_type(::Types::DesignManagement::DesignType.connection_type)
+ end
+
describe '#resolve' do
let_it_be(:issue) { create(:issue) }
let_it_be(:project) { issue.project }
diff --git a/spec/graphql/resolvers/environments/nested_environments_resolver_spec.rb b/spec/graphql/resolvers/environments/nested_environments_resolver_spec.rb
new file mode 100644
index 00000000000..07197b1838f
--- /dev/null
+++ b/spec/graphql/resolvers/environments/nested_environments_resolver_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Environments::NestedEnvironmentsResolver, feature_category: :continuous_delivery do
+ include GraphqlHelpers
+ include Gitlab::Graphql::Laziness
+
+ let_it_be(:project) { create(:project, :repository, :private) }
+ let_it_be(:environment) { create(:environment, project: project, name: 'test') }
+ let_it_be(:environment2) { create(:environment, project: project, name: 'folder1/test') }
+ let_it_be(:environment3) { create(:environment, project: project, name: 'folder1/test2') }
+ let_it_be(:environment4) { create(:environment, project: project, name: 'folder2/test') }
+
+ let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
+
+ let(:current_user) { developer }
+
+ describe '#resolve' do
+ it 'finds the nested environments when status matches' do
+ expect(resolve_nested_environments(status: :created).to_a.pluck(:name, :size))
+ .to match_array([
+ ['test', 1],
+ ['folder1', 2],
+ ['folder2', 1]
+ ])
+ end
+
+ it 'finds the nested environments when searching by name' do
+ expect(resolve_nested_environments(search: 'folder2').to_a.pluck(:name, :size))
+ .to match_array([
+ ['folder2', 1]
+ ])
+ end
+
+ it 'finds the nested environments when name matches exactly' do
+ expect(resolve_nested_environments(name: 'test').to_a.pluck(:name, :size))
+ .to match_array([
+ ['test', 1]
+ ])
+ end
+ end
+
+ def resolve_nested_environments(args = {}, context = { current_user: current_user })
+ resolve(described_class, obj: project, ctx: context, args: args)
+ end
+end
diff --git a/spec/graphql/resolvers/environments_resolver_spec.rb b/spec/graphql/resolvers/environments_resolver_spec.rb
index 9f4c4716de0..b1f7dc1673e 100644
--- a/spec/graphql/resolvers/environments_resolver_spec.rb
+++ b/spec/graphql/resolvers/environments_resolver_spec.rb
@@ -13,6 +13,9 @@ RSpec.describe Resolvers::EnvironmentsResolver do
let!(:environment1) { create(:environment, :available, name: 'production', project: project) }
let!(:environment2) { create(:environment, :stopped, name: 'test', project: project) }
let!(:environment3) { create(:environment, :available, name: 'test2', project: project) }
+ let!(:environment4) { create(:environment, :available, name: 'folder1/test1', project: project) }
+ let!(:environment5) { create(:environment, :available, name: 'folder1/test2', project: project) }
+ let!(:environment6) { create(:environment, :available, name: 'folder2/test3', project: project) }
before do
group.add_developer(current_user)
@@ -20,7 +23,12 @@ RSpec.describe Resolvers::EnvironmentsResolver do
describe '#resolve' do
it 'finds all environments' do
- expect(resolve_environments).to contain_exactly(environment1, environment2, environment3)
+ expect(resolve_environments).to contain_exactly(environment1,
+ environment2,
+ environment3,
+ environment4,
+ environment5,
+ environment6)
end
context 'with name' do
@@ -31,7 +39,7 @@ RSpec.describe Resolvers::EnvironmentsResolver do
context 'with search' do
it 'searches environment by name' do
- expect(resolve_environments(search: 'test')).to contain_exactly(environment2, environment3)
+ expect(resolve_environments(search: 'production')).to contain_exactly(environment1)
end
context 'when the search term does not match any environments' do
@@ -43,7 +51,11 @@ RSpec.describe Resolvers::EnvironmentsResolver do
context 'with states' do
it 'searches environments by state' do
- expect(resolve_environments(states: ['available'])).to contain_exactly(environment1, environment3)
+ expect(resolve_environments(states: ['available'])).to contain_exactly(environment1,
+ environment3,
+ environment4,
+ environment5,
+ environment6)
end
it 'generates an error if requested state is invalid' do
@@ -53,6 +65,16 @@ RSpec.describe Resolvers::EnvironmentsResolver do
end
end
+ context 'with environment_type' do
+ it 'searches environments by type' do
+ expect(resolve_environments(type: 'folder1')).to contain_exactly(environment4, environment5)
+ end
+
+ it 'returns an empty result' do
+ expect(resolve_environments(type: 'folder3')).to be_empty
+ end
+ end
+
context 'when project is nil' do
subject { resolve(described_class, obj: nil, args: {}, ctx: { current_user: current_user }) }
diff --git a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
index 78dd5173449..07ea98f00c7 100644
--- a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
+++ b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Resolvers::NamespaceProjectsResolver do
+RSpec.describe Resolvers::NamespaceProjectsResolver, feature_category: :subgroups do
include GraphqlHelpers
let(:current_user) { create(:user) }
diff --git a/spec/graphql/resolvers/paginated_tree_resolver_spec.rb b/spec/graphql/resolvers/paginated_tree_resolver_spec.rb
index 4b05e9076d7..9a04b716001 100644
--- a/spec/graphql/resolvers/paginated_tree_resolver_spec.rb
+++ b/spec/graphql/resolvers/paginated_tree_resolver_spec.rb
@@ -66,9 +66,8 @@ RSpec.describe Resolvers::PaginatedTreeResolver do
let(:args) { super().merge(after: 'invalid') }
it 'generates an error' do
- expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError) do
- subject
- end
+ expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::BaseError) { subject }
+ expect(subject.extensions.keys).to match_array([:code, :gitaly_code, :service])
end
end
@@ -92,6 +91,22 @@ RSpec.describe Resolvers::PaginatedTreeResolver do
expect(collected_entries).to match_array(expected_entries)
end
end
+
+ describe 'Custom error handling' do
+ before do
+ grpc_err = GRPC::Unavailable.new
+ allow(repository).to receive(:tree).and_raise(Gitlab::Git::CommandError, grpc_err)
+ end
+
+ context 'when gitaly is not available' do
+ let(:request) { get :index, format: :html, params: { namespace_id: project.namespace, project_id: project } }
+
+ it 'generates an unavailable error' do
+ expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::BaseError) { subject }
+ expect(subject.extensions).to eq(code: 'unavailable', gitaly_code: 14, service: 'git')
+ end
+ end
+ end
end
def resolve_repository(args, opts = {})
diff --git a/spec/graphql/resolvers/project_pipeline_resolver_spec.rb b/spec/graphql/resolvers/project_pipeline_resolver_spec.rb
index 398f8f52269..9250485e4c8 100644
--- a/spec/graphql/resolvers/project_pipeline_resolver_spec.rb
+++ b/spec/graphql/resolvers/project_pipeline_resolver_spec.rb
@@ -12,6 +12,10 @@ RSpec.describe Resolvers::ProjectPipelineResolver do
let(:current_user) { create(:user) }
+ before do
+ project.add_developer(current_user)
+ end
+
specify do
expect(described_class).to have_nullable_graphql_type(::Types::Ci::PipelineType)
end
@@ -20,10 +24,6 @@ RSpec.describe Resolvers::ProjectPipelineResolver do
resolve(described_class, obj: project, args: args, ctx: { current_user: current_user })
end
- before do
- project.add_developer(current_user)
- end
-
it 'resolves pipeline for the passed iid' do
expect(Ci::PipelinesFinder)
.to receive(:new)
diff --git a/spec/graphql/resolvers/users/participants_resolver_spec.rb b/spec/graphql/resolvers/users/participants_resolver_spec.rb
index eb2418b63f4..27c3b9643ce 100644
--- a/spec/graphql/resolvers/users/participants_resolver_spec.rb
+++ b/spec/graphql/resolvers/users/participants_resolver_spec.rb
@@ -115,7 +115,8 @@ RSpec.describe Resolvers::Users::ParticipantsResolver do
create(:award_emoji, name: 'thumbsup', awardable: public_note)
# 1 extra query per source (3 emojis + 2 notes) to fetch participables collection
- expect { query.call }.not_to exceed_query_limit(control_count).with_threshold(5)
+ # 1 extra query to load work item widgets collection
+ expect { query.call }.not_to exceed_query_limit(control_count).with_threshold(6)
end
it 'does not execute N+1 for system note metadata relation' do
diff --git a/spec/graphql/types/alert_management/alert_type_spec.rb b/spec/graphql/types/alert_management/alert_type_spec.rb
index 69cbdb998eb..c1df24ccb5c 100644
--- a/spec/graphql/types/alert_management/alert_type_spec.rb
+++ b/spec/graphql/types/alert_management/alert_type_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe GitlabSchema.types['AlertManagementAlert'] do
+RSpec.describe GitlabSchema.types['AlertManagementAlert'], feature_category: :incident_management do
specify { expect(described_class.graphql_name).to eq('AlertManagementAlert') }
specify { expect(described_class).to require_graphql_authorizations(:read_alert_management_alert) }
@@ -11,6 +11,7 @@ RSpec.describe GitlabSchema.types['AlertManagementAlert'] do
it 'exposes the expected fields' do
expected_fields = %i[
+ id
iid
issueIid
issue
diff --git a/spec/graphql/types/ci/detailed_status_type_spec.rb b/spec/graphql/types/ci/detailed_status_type_spec.rb
index 686461cb9a5..81ab1b52552 100644
--- a/spec/graphql/types/ci/detailed_status_type_spec.rb
+++ b/spec/graphql/types/ci/detailed_status_type_spec.rb
@@ -5,6 +5,8 @@ require 'spec_helper'
RSpec.describe Types::Ci::DetailedStatusType do
include GraphqlHelpers
+ let_it_be(:stage) { create(:ci_stage, status: :skipped) }
+
specify { expect(described_class.graphql_name).to eq('DetailedStatus') }
it 'has all fields' do
@@ -13,8 +15,6 @@ RSpec.describe Types::Ci::DetailedStatusType do
:label, :text, :tooltip, :action)
end
- let_it_be(:stage) { create(:ci_stage, status: :skipped) }
-
describe 'id field' do
it 'correctly renders the field' do
status = stage.detailed_status(stage.pipeline.user)
diff --git a/spec/graphql/types/ci/freeze_period_status_enum_spec.rb b/spec/graphql/types/ci/freeze_period_status_enum_spec.rb
new file mode 100644
index 00000000000..2d9c7071392
--- /dev/null
+++ b/spec/graphql/types/ci/freeze_period_status_enum_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['CiFreezePeriodStatus'], feature_category: :release_orchestration do
+ it 'exposes all freeze period statuses' do
+ expect(described_class.values.keys).to contain_exactly(*%w[ACTIVE INACTIVE])
+ end
+end
diff --git a/spec/graphql/types/ci/freeze_period_type_spec.rb b/spec/graphql/types/ci/freeze_period_type_spec.rb
new file mode 100644
index 00000000000..a7f7c2f9dc8
--- /dev/null
+++ b/spec/graphql/types/ci/freeze_period_type_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['CiFreezePeriod'], feature_category: :release_orchestration do
+ specify { expect(described_class.graphql_name).to eq('CiFreezePeriod') }
+
+ it 'has the expected fields' do
+ expected_fields = %w[
+ status start_cron end_cron cron_timezone start_time end_time
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+
+ specify { expect(described_class).to require_graphql_authorizations(:read_freeze_period) }
+end
diff --git a/spec/graphql/types/ci/pipeline_counts_type_spec.rb b/spec/graphql/types/ci/pipeline_counts_type_spec.rb
index 7fdb286d253..6452827dc7b 100644
--- a/spec/graphql/types/ci/pipeline_counts_type_spec.rb
+++ b/spec/graphql/types/ci/pipeline_counts_type_spec.rb
@@ -32,7 +32,7 @@ RSpec.describe GitlabSchema.types['PipelineCounts'] do
expect(described_class).to include_graphql_fields(*expected_fields)
end
- shared_examples 'pipeline counts query' do |args: "", expected_counts:|
+ shared_examples 'pipeline counts query' do |expected_counts:, args: ""|
let_it_be(:query) do
%(
query {
diff --git a/spec/graphql/types/ci/pipeline_schedule_type_spec.rb b/spec/graphql/types/ci/pipeline_schedule_type_spec.rb
index bf1413ef657..6e6c6c63969 100644
--- a/spec/graphql/types/ci/pipeline_schedule_type_spec.rb
+++ b/spec/graphql/types/ci/pipeline_schedule_type_spec.rb
@@ -14,6 +14,7 @@ RSpec.describe Types::Ci::PipelineScheduleType do
description
owner
active
+ project
lastPipeline
refForDisplay
refPath
@@ -23,6 +24,13 @@ RSpec.describe Types::Ci::PipelineScheduleType do
cron
cronTimezone
userPermissions
+ editPath
+ cron
+ cronTimezone
+ ref
+ variables
+ createdAt
+ updatedAt
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/ci/pipeline_schedule_variable_type_spec.rb b/spec/graphql/types/ci/pipeline_schedule_variable_type_spec.rb
new file mode 100644
index 00000000000..1c98539e308
--- /dev/null
+++ b/spec/graphql/types/ci/pipeline_schedule_variable_type_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Ci::PipelineScheduleVariableType do
+ specify { expect(described_class.graphql_name).to eq('PipelineScheduleVariable') }
+ specify { expect(described_class.interfaces).to contain_exactly(Types::Ci::VariableInterface) }
+ specify { expect(described_class).to require_graphql_authorizations(:read_pipeline_schedule_variables) }
+
+ it 'contains attributes related to a pipeline message' do
+ expected_fields = %w[
+ id key raw value variable_type
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/ci/runner_type_spec.rb b/spec/graphql/types/ci/runner_type_spec.rb
index 4ec35db13fb..b078d7f5fac 100644
--- a/spec/graphql/types/ci/runner_type_spec.rb
+++ b/spec/graphql/types/ci/runner_type_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe GitlabSchema.types['CiRunner'] do
id description created_at contacted_at maximum_timeout access_level active paused status
version short_sha revision locked run_untagged ip_address runner_type tag_list
project_count job_count admin_url edit_admin_url user_permissions executor_name architecture_name platform_name
- maintenance_note maintenance_note_html groups projects jobs token_expires_at owner_project
+ maintenance_note maintenance_note_html groups projects jobs token_expires_at owner_project job_execution_status
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/commit_signatures/ssh_signature_type_spec.rb b/spec/graphql/types/commit_signatures/ssh_signature_type_spec.rb
new file mode 100644
index 00000000000..4ffb70a0b22
--- /dev/null
+++ b/spec/graphql/types/commit_signatures/ssh_signature_type_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['SshSignature'], feature_category: :source_code_management do
+ specify { expect(described_class.graphql_name).to eq('SshSignature') }
+
+ specify { expect(described_class).to require_graphql_authorizations(:download_code) }
+
+ specify { expect(described_class).to include(Types::CommitSignatureInterface) }
+
+ it 'contains attributes related to SSH signatures' do
+ expect(described_class).to have_graphql_fields(
+ :user, :verification_status, :commit_sha, :project, :key
+ )
+ end
+end
diff --git a/spec/graphql/types/deployment_details_type_spec.rb b/spec/graphql/types/deployment_details_type_spec.rb
deleted file mode 100644
index 7dc0c8f97ac..00000000000
--- a/spec/graphql/types/deployment_details_type_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe GitlabSchema.types['DeploymentDetails'] do
- specify { expect(described_class.graphql_name).to eq('DeploymentDetails') }
-
- it 'has the expected fields' do
- expected_fields = %w[
- id iid ref tag tags sha created_at updated_at finished_at status commit job triggerer
- ]
-
- expect(described_class).to include_graphql_fields(*expected_fields)
- end
-
- specify { expect(described_class).to require_graphql_authorizations(:read_deployment) }
-end
diff --git a/spec/graphql/types/deployment_type_spec.rb b/spec/graphql/types/deployment_type_spec.rb
index bf4be0523c6..4c49391d7a8 100644
--- a/spec/graphql/types/deployment_type_spec.rb
+++ b/spec/graphql/types/deployment_type_spec.rb
@@ -2,15 +2,16 @@
require 'spec_helper'
-RSpec.describe GitlabSchema.types['Deployment'] do
+RSpec.describe GitlabSchema.types['Deployment'], feature_category: :continuous_delivery do
specify { expect(described_class.graphql_name).to eq('Deployment') }
+ specify { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Deployment) }
it 'has the expected fields' do
expected_fields = %w[
- id iid ref tag sha created_at updated_at finished_at status commit job triggerer
+ id iid ref tag tags sha created_at updated_at finished_at status commit job triggerer userPermissions
]
- expect(described_class).to have_graphql_fields(*expected_fields)
+ expect(described_class).to include_graphql_fields(*expected_fields)
end
specify { expect(described_class).to require_graphql_authorizations(:read_deployment) }
diff --git a/spec/graphql/types/environment_type_spec.rb b/spec/graphql/types/environment_type_spec.rb
index 2605beac95a..4471735876a 100644
--- a/spec/graphql/types/environment_type_spec.rb
+++ b/spec/graphql/types/environment_type_spec.rb
@@ -4,11 +4,12 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['Environment'] do
specify { expect(described_class.graphql_name).to eq('Environment') }
+ specify { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Environment) }
it 'includes the expected fields' do
expected_fields = %w[
name id state metrics_dashboard latest_opened_most_severe_alert path external_url deployments
- slug createdAt updatedAt autoStopAt autoDeleteAt tier environmentType lastDeployment
+ slug createdAt updatedAt autoStopAt autoDeleteAt tier environmentType lastDeployment deployFreezes
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/issue_type_enum_spec.rb b/spec/graphql/types/issue_type_enum_spec.rb
index cd1737c3ebb..33a3a9cf8ce 100644
--- a/spec/graphql/types/issue_type_enum_spec.rb
+++ b/spec/graphql/types/issue_type_enum_spec.rb
@@ -2,12 +2,12 @@
require 'spec_helper'
-RSpec.describe Types::IssueTypeEnum do
+RSpec.describe Types::IssueTypeEnum, feature_category: :team_planning do
specify { expect(described_class.graphql_name).to eq('IssueType') }
it 'exposes all the existing issue type values except key_result' do
expect(described_class.values.keys).to match_array(
- %w[ISSUE INCIDENT TEST_CASE REQUIREMENT TASK OBJECTIVE]
+ %w[ISSUE INCIDENT TEST_CASE REQUIREMENT TASK OBJECTIVE KEY_RESULT]
)
end
end
diff --git a/spec/graphql/types/key_type_spec.rb b/spec/graphql/types/key_type_spec.rb
new file mode 100644
index 00000000000..78144076467
--- /dev/null
+++ b/spec/graphql/types/key_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['Key'], feature_category: :authentication_and_authorization do
+ specify { expect(described_class.graphql_name).to eq('Key') }
+
+ it 'contains attributes for SSH keys' do
+ expect(described_class).to have_graphql_fields(
+ :id, :title, :created_at, :expires_at, :key
+ )
+ end
+end
diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb
index 7ab254238fb..8a4c89fc340 100644
--- a/spec/graphql/types/merge_request_type_spec.rb
+++ b/spec/graphql/types/merge_request_type_spec.rb
@@ -116,12 +116,12 @@ RSpec.describe GitlabSchema.types['MergeRequest'] do
describe 'merge_status_enum' do
let(:type) { GitlabSchema.types['MergeStatus'] }
+ let_it_be(:project) { create(:project, :public) }
+
it 'has the type MergeStatus' do
expect(described_class.fields['mergeStatusEnum']).to have_graphql_type(type)
end
- let_it_be(:project) { create(:project, :public) }
-
%i[preparing unchecked cannot_be_merged_recheck checking cannot_be_merged_rechecking can_be_merged cannot_be_merged].each do |state|
context "when the the DB value is #{state}" do
let(:merge_request) { create(:merge_request, :unique_branches, source_project: project, merge_status: state.to_s) }
diff --git a/spec/graphql/types/permission_types/base_permission_type_spec.rb b/spec/graphql/types/permission_types/base_permission_type_spec.rb
index e4726ad0e6e..f437c3778c6 100644
--- a/spec/graphql/types/permission_types/base_permission_type_spec.rb
+++ b/spec/graphql/types/permission_types/base_permission_type_spec.rb
@@ -51,4 +51,25 @@ RSpec.describe Types::PermissionTypes::BasePermissionType do
expect(test_type).to have_graphql_field(:admin_issue)
end
end
+
+ describe 'extensions' do
+ subject(:test_type) do
+ Class.new(described_class) do
+ graphql_name 'TestClass'
+
+ permission_field :read_entity_a do
+ extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
+ end
+
+ ability_field(:read_entity_b) do
+ extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
+ end
+ end
+ end
+
+ it 'has the extension' do
+ expect(test_type.fields['readEntityA'].extensions).to include(a_kind_of(::Gitlab::Graphql::Limit::FieldCallCount))
+ expect(test_type.fields['readEntityB'].extensions).to include(a_kind_of(::Gitlab::Graphql::Limit::FieldCallCount))
+ end
+ end
end
diff --git a/spec/graphql/types/permission_types/deployment_spec.rb b/spec/graphql/types/permission_types/deployment_spec.rb
new file mode 100644
index 00000000000..ccf5798984c
--- /dev/null
+++ b/spec/graphql/types/permission_types/deployment_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::PermissionTypes::Deployment, feature_category: :continuous_delivery do
+ it do
+ expected_permissions = %i[update_deployment destroy_deployment]
+
+ expect(described_class).to include_graphql_fields(*expected_permissions)
+ end
+end
diff --git a/spec/graphql/types/permission_types/environment_spec.rb b/spec/graphql/types/permission_types/environment_spec.rb
new file mode 100644
index 00000000000..944699c972a
--- /dev/null
+++ b/spec/graphql/types/permission_types/environment_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::PermissionTypes::Environment, feature_category: :continuous_delivery do
+ it do
+ expected_permissions = [
+ :update_environment, :destroy_environment, :stop_environment
+ ]
+
+ expected_permissions.each do |permission|
+ expect(described_class).to have_graphql_field(permission)
+ end
+ end
+end
diff --git a/spec/graphql/types/permission_types/project_spec.rb b/spec/graphql/types/permission_types/project_spec.rb
index c6853a0eadc..645fc6c68d1 100644
--- a/spec/graphql/types/permission_types/project_spec.rb
+++ b/spec/graphql/types/permission_types/project_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Types::PermissionTypes::Project do
:create_merge_request_from, :create_wiki, :push_code, :create_deployment, :push_to_delete_protected_branch,
:admin_wiki, :admin_project, :update_pages, :admin_remote_mirror, :create_label,
:update_wiki, :destroy_wiki, :create_pages, :destroy_pages, :read_pages_content,
- :read_merge_request, :read_design, :create_design, :destroy_design
+ :read_merge_request, :read_design, :create_design, :destroy_design, :read_environment
]
expected_permissions.each do |permission|
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index 30fabb8e9e2..4151789372b 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -492,7 +492,7 @@ RSpec.describe GitlabSchema.types['Project'] do
subject { described_class.fields['jobs'] }
it { is_expected.to have_graphql_type(Types::Ci::JobType.connection_type) }
- it { is_expected.to have_graphql_arguments(:statuses) }
+ it { is_expected.to have_graphql_arguments(:statuses, :with_artifacts) }
end
describe 'ci_template field' do
diff --git a/spec/graphql/types/projects/fork_details_type_spec.rb b/spec/graphql/types/projects/fork_details_type_spec.rb
new file mode 100644
index 00000000000..8e20e2c8299
--- /dev/null
+++ b/spec/graphql/types/projects/fork_details_type_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['ForkDetails'], feature_category: :source_code_management do
+ specify { expect(described_class.graphql_name).to eq('ForkDetails') }
+
+ it 'has specific fields' do
+ fields = %i[
+ ahead
+ behind
+ ]
+
+ expect(described_class).to have_graphql_fields(*fields)
+ end
+end
diff --git a/spec/graphql/types/projects/service_type_enum_spec.rb b/spec/graphql/types/projects/service_type_enum_spec.rb
index f7256910bb0..8b444a08c3b 100644
--- a/spec/graphql/types/projects/service_type_enum_spec.rb
+++ b/spec/graphql/types/projects/service_type_enum_spec.rb
@@ -23,7 +23,6 @@ RSpec.describe GitlabSchema.types['ServiceType'] do
EMAILS_ON_PUSH_SERVICE
EWM_SERVICE
EXTERNAL_WIKI_SERVICE
- FLOWDOCK_SERVICE
HANGOUTS_CHAT_SERVICE
IRKER_SERVICE
JENKINS_SERVICE
diff --git a/spec/graphql/types/snippets/blob_type_spec.rb b/spec/graphql/types/snippets/blob_type_spec.rb
index e20b001ba7f..9eef6ac4cdc 100644
--- a/spec/graphql/types/snippets/blob_type_spec.rb
+++ b/spec/graphql/types/snippets/blob_type_spec.rb
@@ -5,15 +5,7 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['SnippetBlob'] do
include GraphqlHelpers
- it 'has the correct fields' do
- expected_fields = [:rich_data, :plain_data, :raw_plain_data,
- :raw_path, :size, :binary, :name, :path,
- :simple_viewer, :rich_viewer, :mode, :external_storage,
- :rendered_as_text]
-
- expect(described_class).to have_graphql_fields(*expected_fields)
- end
-
+ let_it_be(:blob) { create(:snippet, :public, :repository).blobs.first }
let_it_be(:nullity) do
{
'richData' => be_nullable,
@@ -32,7 +24,14 @@ RSpec.describe GitlabSchema.types['SnippetBlob'] do
}
end
- let_it_be(:blob) { create(:snippet, :public, :repository).blobs.first }
+ it 'has the correct fields' do
+ expected_fields = [:rich_data, :plain_data, :raw_plain_data,
+ :raw_path, :size, :binary, :name, :path,
+ :simple_viewer, :rich_viewer, :mode, :external_storage,
+ :rendered_as_text]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
shared_examples 'a field from the snippet blob presenter' do |field|
it "resolves using the presenter", :request_store do
diff --git a/spec/graphql/types/subscription_type_spec.rb b/spec/graphql/types/subscription_type_spec.rb
index 04f0c72b06f..a57a8e751ac 100644
--- a/spec/graphql/types/subscription_type_spec.rb
+++ b/spec/graphql/types/subscription_type_spec.rb
@@ -14,6 +14,7 @@ RSpec.describe GitlabSchema.types['Subscription'] do
issuable_milestone_updated
merge_request_reviewers_updated
merge_request_merge_status_updated
+ merge_request_approval_state_updated
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/todo_type_spec.rb b/spec/graphql/types/todo_type_spec.rb
index c7bb7c67959..2118a777a45 100644
--- a/spec/graphql/types/todo_type_spec.rb
+++ b/spec/graphql/types/todo_type_spec.rb
@@ -3,6 +3,11 @@
require 'spec_helper'
RSpec.describe GitlabSchema.types['Todo'] do
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:author) { create(:user) }
+
+ let(:issue) { create(:issue, project: project) }
+
it 'has the correct fields' do
expected_fields = [
:id,
@@ -22,4 +27,116 @@ RSpec.describe GitlabSchema.types['Todo'] do
end
specify { expect(described_class).to require_graphql_authorizations(:read_todo) }
+
+ subject { GitlabSchema.execute(query, context: { current_user: current_user }).as_json }
+
+ describe 'project field' do
+ let(:todo) do
+ create(:todo,
+ user: current_user,
+ project: project,
+ state: :done,
+ action: Todo::ASSIGNED,
+ author: author,
+ target: issue)
+ end
+
+ let(:query) do
+ %(
+ query {
+ todo(id: "#{todo.to_global_id}") {
+ project {
+ id
+ }
+ }
+ }
+ )
+ end
+
+ context 'when the project is public' do
+ let_it_be(:project) { create(:project, :public) }
+
+ context 'when the user does not have access' do
+ it 'returns the project' do
+ expect(subject.dig('data', 'todo', 'project', 'id')).to eq(project.to_global_id.to_s)
+ end
+ end
+ end
+
+ context 'when the project is not public' do
+ let_it_be(:project) { create(:project) }
+
+ context 'when the user does not have access' do
+ it 'returns null' do
+ expect(subject.dig('data', 'todo', 'project')).to be_nil
+ end
+ end
+
+ context 'when the user does have access' do
+ before do
+ project.add_guest(current_user)
+ end
+
+ it 'returns the project' do
+ expect(subject.dig('data', 'todo', 'project', 'id')).to eq(project.to_global_id.to_s)
+ end
+ end
+ end
+ end
+
+ describe 'group field' do
+ let(:todo) do
+ create(:todo,
+ user: current_user,
+ group: group,
+ state: :done,
+ action: Todo::MENTIONED,
+ author: author,
+ target: issue)
+ end
+
+ let(:query) do
+ %(
+ query {
+ todo(id: "#{todo.to_global_id}") {
+ group {
+ id
+ }
+ }
+ }
+ )
+ end
+
+ context 'when the group is public' do
+ let_it_be(:group) { create(:group, :public) }
+ let_it_be(:project) { create(:project, :public, group: group) }
+
+ context 'when the user does not have access' do
+ it 'returns the group' do
+ expect(subject.dig('data', 'todo', 'group', 'id')).to eq(group.to_global_id.to_s)
+ end
+ end
+ end
+
+ context 'when the group is not public' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+
+ context 'when the user does not have access' do
+ it 'returns null' do
+ expect(subject.dig('data', 'todo', 'group')).to be_nil
+ end
+ end
+
+ context 'when the user does have access' do
+ before do
+ group.add_guest(current_user)
+ end
+
+ it 'returns the group' do
+ expect(subject.dig('data', 'todo', 'group', 'id')).to eq(group.to_global_id.to_s)
+ end
+ end
+ end
+ end
end
diff --git a/spec/graphql/types/work_items/notes_filter_type_enum_spec.rb b/spec/graphql/types/work_items/notes_filter_type_enum_spec.rb
new file mode 100644
index 00000000000..13ce559c529
--- /dev/null
+++ b/spec/graphql/types/work_items/notes_filter_type_enum_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['NotesFilterType'], feature_category: :team_planning do
+ specify { expect(described_class.graphql_name).to eq('NotesFilterType') }
+
+ it 'exposes all the existing widget type values' do
+ expect(described_class.values.transform_values(&:value)).to include(
+ "ALL_NOTES" => 0, "ONLY_ACTIVITY" => 2, "ONLY_COMMENTS" => 1
+ )
+ end
+end
diff --git a/spec/graphql/types/work_items/widget_interface_spec.rb b/spec/graphql/types/work_items/widget_interface_spec.rb
index b9e8edacf15..a2b12ed52dc 100644
--- a/spec/graphql/types/work_items/widget_interface_spec.rb
+++ b/spec/graphql/types/work_items/widget_interface_spec.rb
@@ -19,6 +19,7 @@ RSpec.describe Types::WorkItems::WidgetInterface do
WorkItems::Widgets::Hierarchy | Types::WorkItems::Widgets::HierarchyType
WorkItems::Widgets::Assignees | Types::WorkItems::Widgets::AssigneesType
WorkItems::Widgets::Labels | Types::WorkItems::Widgets::LabelsType
+ WorkItems::Widgets::Notes | Types::WorkItems::Widgets::NotesType
end
with_them do
diff --git a/spec/graphql/types/work_items/widgets/hierarchy_type_spec.rb b/spec/graphql/types/work_items/widgets/hierarchy_type_spec.rb
index 1722a07c5f4..20413a35c58 100644
--- a/spec/graphql/types/work_items/widgets/hierarchy_type_spec.rb
+++ b/spec/graphql/types/work_items/widgets/hierarchy_type_spec.rb
@@ -2,9 +2,9 @@
require 'spec_helper'
-RSpec.describe Types::WorkItems::Widgets::HierarchyType do
+RSpec.describe Types::WorkItems::Widgets::HierarchyType, feature_category: :team_planning do
it 'exposes the expected fields' do
- expected_fields = %i[parent children type]
+ expected_fields = %i[parent children has_children type]
expect(described_class).to have_graphql_fields(*expected_fields)
end
diff --git a/spec/graphql/types/work_items/widgets/notes_type_spec.rb b/spec/graphql/types/work_items/widgets/notes_type_spec.rb
new file mode 100644
index 00000000000..3ac61a59a9c
--- /dev/null
+++ b/spec/graphql/types/work_items/widgets/notes_type_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::WorkItems::Widgets::NotesType, feature_category: :team_planning do
+ it 'exposes the expected fields' do
+ expected_fields = %i[discussions type]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/haml_lint/linter/documentation_links_spec.rb b/spec/haml_lint/linter/documentation_links_spec.rb
index 49a720700da..380df49cde3 100644
--- a/spec/haml_lint/linter/documentation_links_spec.rb
+++ b/spec/haml_lint/linter/documentation_links_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe HamlLint::Linter::DocumentationLinks do
end
context 'when link_to points to the existing file with valid anchor' do
- let(:haml) { "= link_to 'Description', #{link_pattern}('index.md', anchor: 'overview'), target: '_blank'" }
+ let(:haml) { "= link_to 'Description', #{link_pattern}('index.md', anchor: 'user-account'), target: '_blank'" }
it { is_expected.not_to report_lint }
end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 261d8c8c302..3384f9fea05 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe ApplicationHelper do
+ include Devise::Test::ControllerHelpers
+
describe 'current_controller?' do
before do
stub_controller_name('foo')
@@ -419,7 +421,7 @@ RSpec.describe ApplicationHelper do
end
it 'includes all possible body data elements and associates the project elements with project' do
- expect(helper).to receive(:can?).with(nil, :download_code, project)
+ expect(helper).to receive(:can?).with(nil, :read_code, project)
expect(helper.body_data).to eq(
{
page: 'application',
@@ -437,7 +439,7 @@ RSpec.describe ApplicationHelper do
let_it_be(:project) { create(:project, :repository, group: create(:group)) }
it 'includes all possible body data elements and associates the project elements with project' do
- expect(helper).to receive(:can?).with(nil, :download_code, project)
+ expect(helper).to receive(:can?).with(nil, :read_code, project)
expect(helper.body_data).to eq(
{
page: 'application',
@@ -463,7 +465,7 @@ RSpec.describe ApplicationHelper do
stub_controller_method(:action_name, 'show')
stub_controller_method(:params, { id: issue.id })
- expect(helper).to receive(:can?).with(nil, :download_code, project).and_return(false)
+ expect(helper).to receive(:can?).with(nil, :read_code, project).and_return(false)
expect(helper.body_data).to eq(
{
page: 'projects:issues:show',
@@ -479,12 +481,34 @@ RSpec.describe ApplicationHelper do
end
end
- context 'when current_user has download_code permission' do
- it 'returns find_file with the default branch' do
+ describe 'find_file attribute' do
+ subject { helper.body_data[:find_file] }
+
+ before do
allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ context 'when the project has no repository' do
+ before do
+ allow(project).to receive(:empty_repo?).and_return(true)
+ end
+
+ it { is_expected.to be_nil }
+ end
- expect(helper).to receive(:can?).with(user, :download_code, project).and_return(true)
- expect(helper.body_data[:find_file]).to end_with(project.default_branch)
+ context 'when user cannot read_code for the project' do
+ before do
+ allow(helper).to receive(:can?).with(user, :read_code, project).and_return(false)
+ end
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when current_user has read_code permission' do
+ it 'returns find_file with the default branch' do
+ expect(helper).to receive(:can?).with(user, :read_code, project).and_return(true)
+ expect(subject).to end_with(project.default_branch)
+ end
end
end
end
@@ -651,4 +675,40 @@ RSpec.describe ApplicationHelper do
end
end
end
+
+ describe 'stylesheet_link_tag_defer' do
+ it 'uses print stylesheet by default' do
+ expect(helper.stylesheet_link_tag_defer('test')).to eq( '<link rel="stylesheet" media="print" href="/stylesheets/test.css" />')
+ end
+
+ it 'uses regular stylesheet when no_startup_css param present' do
+ allow(helper.controller).to receive(:params).and_return({ no_startup_css: '' })
+
+ expect(helper.stylesheet_link_tag_defer('test')).to eq( '<link rel="stylesheet" media="screen" href="/stylesheets/test.css" />')
+ end
+ end
+
+ describe '#use_new_fonts?' do
+ subject { helper.use_new_fonts? }
+
+ it { is_expected.to eq true }
+
+ context 'when the feature flag is disabled' do
+ before do
+ stub_feature_flags(new_fonts: false)
+ end
+
+ it { is_expected.to eq false }
+
+ context 'with special request param' do
+ let(:request) { instance_double(ActionController::TestRequest, params: { new_fonts: true }) }
+
+ before do
+ allow(helper).to receive(:request).and_return(request)
+ end
+
+ it { is_expected.to eq true }
+ end
+ end
+ end
end
diff --git a/spec/helpers/application_settings_helper_spec.rb b/spec/helpers/application_settings_helper_spec.rb
index eafdbfa8d0a..914c866c464 100644
--- a/spec/helpers/application_settings_helper_spec.rb
+++ b/spec/helpers/application_settings_helper_spec.rb
@@ -273,15 +273,9 @@ RSpec.describe ApplicationSettingsHelper do
end
end
- describe '.registration_features_can_be_prompted?' do
+ describe '.registration_features_can_be_prompted?', :without_license do
subject { helper.registration_features_can_be_prompted? }
- before do
- if Gitlab.ee?
- allow(License).to receive(:current).and_return(nil)
- end
- end
-
context 'when service ping is enabled' do
before do
stub_application_setting(usage_ping_enabled: true)
diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb
index 9c0f8b77d45..cef72d24c43 100644
--- a/spec/helpers/avatars_helper_spec.rb
+++ b/spec/helpers/avatars_helper_spec.rb
@@ -297,28 +297,22 @@ RSpec.describe AvatarsHelper do
subject { helper.user_avatar_without_link(options) }
it 'displays user avatar' do
- is_expected.to eq tag(
- :img,
- alt: "#{user.name}'s avatar",
- src: avatar_icon_for_user(user, 16),
- data: { container: 'body' },
- class: 'avatar s16 has-tooltip',
- title: user.name
- )
+ is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
+ src: avatar_icon_for_user(user, 16),
+ data: { container: 'body' },
+ class: 'avatar s16 has-tooltip',
+ title: user.name)
end
context 'with css_class parameter' do
let(:options) { { user: user, css_class: '.cat-pics' } }
it 'uses provided css_class' do
- is_expected.to eq tag(
- :img,
- alt: "#{user.name}'s avatar",
- src: avatar_icon_for_user(user, 16),
- data: { container: 'body' },
- class: "avatar s16 #{options[:css_class]} has-tooltip",
- title: user.name
- )
+ is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
+ src: avatar_icon_for_user(user, 16),
+ data: { container: 'body' },
+ class: "avatar s16 #{options[:css_class]} has-tooltip",
+ title: user.name)
end
end
@@ -326,14 +320,11 @@ RSpec.describe AvatarsHelper do
let(:options) { { user: user, size: 99 } }
it 'uses provided size' do
- is_expected.to eq tag(
- :img,
- alt: "#{user.name}'s avatar",
- src: avatar_icon_for_user(user, options[:size]),
- data: { container: 'body' },
- class: "avatar s#{options[:size]} has-tooltip",
- title: user.name
- )
+ is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
+ src: avatar_icon_for_user(user, options[:size]),
+ data: { container: 'body' },
+ class: "avatar s#{options[:size]} has-tooltip",
+ title: user.name)
end
end
@@ -341,14 +332,11 @@ RSpec.describe AvatarsHelper do
let(:options) { { user: user, url: '/over/the/rainbow.png' } }
it 'uses provided url' do
- is_expected.to eq tag(
- :img,
- alt: "#{user.name}'s avatar",
- src: options[:url],
- data: { container: 'body' },
- class: "avatar s16 has-tooltip",
- title: user.name
- )
+ is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
+ src: options[:url],
+ data: { container: 'body' },
+ class: "avatar s16 has-tooltip",
+ title: user.name)
end
end
@@ -356,14 +344,11 @@ RSpec.describe AvatarsHelper do
let(:options) { { user: user, lazy: true } }
it 'adds `lazy` class to class list, sets `data-src` with avatar URL and `src` with placeholder image' do
- is_expected.to eq tag(
- :img,
- alt: "#{user.name}'s avatar",
- src: LazyImageTagHelper.placeholder_image,
- data: { container: 'body', src: avatar_icon_for_user(user, 16) },
- class: "avatar s16 has-tooltip lazy",
- title: user.name
- )
+ is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
+ src: LazyImageTagHelper.placeholder_image,
+ data: { container: 'body', src: avatar_icon_for_user(user, 16) },
+ class: "avatar s16 has-tooltip lazy",
+ title: user.name)
end
end
@@ -372,14 +357,11 @@ RSpec.describe AvatarsHelper do
let(:options) { { user: user, has_tooltip: true } }
it 'adds has-tooltip' do
- is_expected.to eq tag(
- :img,
- alt: "#{user.name}'s avatar",
- src: avatar_icon_for_user(user, 16),
- data: { container: 'body' },
- class: "avatar s16 has-tooltip",
- title: user.name
- )
+ is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
+ src: avatar_icon_for_user(user, 16),
+ data: { container: 'body' },
+ class: "avatar s16 has-tooltip",
+ title: user.name)
end
end
@@ -387,13 +369,10 @@ RSpec.describe AvatarsHelper do
let(:options) { { user: user, has_tooltip: false } }
it 'does not add has-tooltip or data container' do
- is_expected.to eq tag(
- :img,
- alt: "#{user.name}'s avatar",
- src: avatar_icon_for_user(user, 16),
- class: "avatar s16",
- title: user.name
- )
+ is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
+ src: avatar_icon_for_user(user, 16),
+ class: "avatar s16",
+ title: user.name)
end
end
end
@@ -405,26 +384,20 @@ RSpec.describe AvatarsHelper do
let(:options) { { user: user, user_name: 'Tinky Winky' } }
it 'prefers user parameter' do
- is_expected.to eq tag(
- :img,
- alt: "#{user.name}'s avatar",
- src: avatar_icon_for_user(user, 16),
- data: { container: 'body' },
- class: "avatar s16 has-tooltip",
- title: user.name
- )
+ is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
+ src: avatar_icon_for_user(user, 16),
+ data: { container: 'body' },
+ class: "avatar s16 has-tooltip",
+ title: user.name)
end
end
it 'uses user_name and user_email parameter if user is not present' do
- is_expected.to eq tag(
- :img,
- alt: "#{options[:user_name]}'s avatar",
- src: helper.avatar_icon_for_email(options[:user_email], 16),
- data: { container: 'body' },
- class: "avatar s16 has-tooltip",
- title: options[:user_name]
- )
+ is_expected.to eq tag.img(alt: "#{options[:user_name]}'s avatar",
+ src: helper.avatar_icon_for_email(options[:user_email], 16),
+ data: { container: 'body' },
+ class: "avatar s16 has-tooltip",
+ title: options[:user_name])
end
end
@@ -435,14 +408,11 @@ RSpec.describe AvatarsHelper do
let(:options) { { user: user_with_avatar, only_path: false } }
it 'will return avatar with a full path' do
- is_expected.to eq tag(
- :img,
- alt: "#{user_with_avatar.name}'s avatar",
- src: avatar_icon_for_user(user_with_avatar, 16, only_path: false),
- data: { container: 'body' },
- class: "avatar s16 has-tooltip",
- title: user_with_avatar.name
- )
+ is_expected.to eq tag.img(alt: "#{user_with_avatar.name}'s avatar",
+ src: avatar_icon_for_user(user_with_avatar, 16, only_path: false),
+ data: { container: 'body' },
+ class: "avatar s16 has-tooltip",
+ title: user_with_avatar.name)
end
end
@@ -450,14 +420,11 @@ RSpec.describe AvatarsHelper do
let(:options) { { user_email: user_with_avatar.email, user_name: user_with_avatar.username, only_path: false } }
it 'will return avatar with a full path' do
- is_expected.to eq tag(
- :img,
- alt: "#{user_with_avatar.username}'s avatar",
- src: helper.avatar_icon_for_email(user_with_avatar.email, 16, only_path: false),
- data: { container: 'body' },
- class: "avatar s16 has-tooltip",
- title: user_with_avatar.username
- )
+ is_expected.to eq tag.img(alt: "#{user_with_avatar.username}'s avatar",
+ src: helper.avatar_icon_for_email(user_with_avatar.email, 16, only_path: false),
+ data: { container: 'body' },
+ class: "avatar s16 has-tooltip",
+ title: user_with_avatar.username)
end
end
end
@@ -480,14 +447,11 @@ RSpec.describe AvatarsHelper do
let(:resource) { user.namespace }
it 'displays user avatar' do
- is_expected.to eq tag(
- :img,
- alt: "#{user.name}'s avatar",
- src: avatar_icon_for_user(user, 32),
- data: { container: 'body' },
- class: 'avatar s32 has-tooltip',
- title: user.name
- )
+ is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
+ src: avatar_icon_for_user(user, 32),
+ data: { container: 'body' },
+ class: 'avatar s32 has-tooltip',
+ title: user.name)
end
end
diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb
index fe652e905cc..dac0d3fe182 100644
--- a/spec/helpers/blob_helper_spec.rb
+++ b/spec/helpers/blob_helper_spec.rb
@@ -80,6 +80,7 @@ RSpec.describe BlobHelper do
end
end
end
+
context 'viewer related' do
include FakeBlobHelpers
diff --git a/spec/helpers/ci/secure_files_helper_spec.rb b/spec/helpers/ci/secure_files_helper_spec.rb
index 02da44f56b2..54307e670e1 100644
--- a/spec/helpers/ci/secure_files_helper_spec.rb
+++ b/spec/helpers/ci/secure_files_helper_spec.rb
@@ -19,58 +19,40 @@ RSpec.describe Ci::SecureFilesHelper do
subject { helper.show_secure_files_setting(project, user) }
describe '#show_secure_files_setting' do
- context 'when the :ci_secure_files feature flag is enabled' do
- before do
- stub_feature_flags(ci_secure_files: true)
- end
+ context 'authenticated user with admin permissions' do
+ let(:user) { maintainer }
- context 'authenticated user with admin permissions' do
- let(:user) { maintainer }
-
- it { is_expected.to be true }
- end
-
- context 'authenticated user with read permissions' do
- let(:user) { developer }
-
- it { is_expected.to be true }
- end
+ it { is_expected.to be true }
+ end
- context 'authenticated user with guest permissions' do
- let(:user) { guest }
+ context 'authenticated user with read permissions' do
+ let(:user) { developer }
- it { is_expected.to be false }
- end
+ it { is_expected.to be true }
+ end
- context 'authenticated user with no permissions' do
- let(:user) { anonymous }
+ context 'authenticated user with guest permissions' do
+ let(:user) { guest }
- it { is_expected.to be false }
- end
+ it { is_expected.to be false }
+ end
- context 'unconfirmed user' do
- let(:user) { unconfirmed }
+ context 'authenticated user with no permissions' do
+ let(:user) { anonymous }
- it { is_expected.to be false }
- end
+ it { is_expected.to be false }
+ end
- context 'unauthenticated user' do
- let(:user) { nil }
+ context 'unconfirmed user' do
+ let(:user) { unconfirmed }
- it { is_expected.to be false }
- end
+ it { is_expected.to be false }
end
- context 'when the :ci_secure_files feature flag is disabled' do
- before do
- stub_feature_flags(ci_secure_files: false)
- end
-
- context 'authenticated user with admin permissions' do
- let(:user) { maintainer }
+ context 'unauthenticated user' do
+ let(:user) { nil }
- it { is_expected.to be false }
- end
+ it { is_expected.to be false }
end
end
end
diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb
index 78c0d0a2b11..a46f8c13f00 100644
--- a/spec/helpers/diff_helper_spec.rb
+++ b/spec/helpers/diff_helper_spec.rb
@@ -483,7 +483,18 @@ RSpec.describe DiffHelper do
end
describe '#conflicts' do
- let(:merge_request) { instance_double(MergeRequest, cannot_be_merged?: true) }
+ let(:merge_request) do
+ instance_double(
+ MergeRequest,
+ cannot_be_merged?: cannot_be_merged?,
+ source_branch_exists?: source_branch_exists?,
+ target_branch_exists?: target_branch_exists?
+ )
+ end
+
+ let(:cannot_be_merged?) { true }
+ let(:source_branch_exists?) { true }
+ let(:target_branch_exists?) { true }
let(:can_be_resolved_in_ui?) { true }
let(:allow_tree_conflicts) { false }
let(:files) { [instance_double(Gitlab::Conflict::File, path: 'a')] }
@@ -508,7 +519,23 @@ RSpec.describe DiffHelper do
end
context 'when merge request can be merged' do
- let(:merge_request) { instance_double(MergeRequest, cannot_be_merged?: false) }
+ let(:cannot_be_merged?) { false }
+
+ it 'returns nil' do
+ expect(helper.conflicts).to be_nil
+ end
+ end
+
+ context 'when source branch does not exist' do
+ let(:source_branch_exists?) { false }
+
+ it 'returns nil' do
+ expect(helper.conflicts).to be_nil
+ end
+ end
+
+ context 'when target branch does not exist' do
+ let(:target_branch_exists?) { false }
it 'returns nil' do
expect(helper.conflicts).to be_nil
diff --git a/spec/helpers/environment_helper_spec.rb b/spec/helpers/environment_helper_spec.rb
index 1fcbcd8c4f9..c8d67d6dac2 100644
--- a/spec/helpers/environment_helper_spec.rb
+++ b/spec/helpers/environment_helper_spec.rb
@@ -50,6 +50,7 @@ RSpec.describe EnvironmentHelper do
expect(subject).to eq({
name: environment.name,
id: environment.id,
+ project_full_path: project.full_path,
external_url: environment.external_url,
can_update_environment: true,
can_destroy_environment: true,
diff --git a/spec/helpers/git_helper_spec.rb b/spec/helpers/git_helper_spec.rb
index 0dd9eecb7f0..543b9ce7a82 100644
--- a/spec/helpers/git_helper_spec.rb
+++ b/spec/helpers/git_helper_spec.rb
@@ -15,11 +15,13 @@ RSpec.describe GitHelper do
it { expect(strip_signature).to eq("Version 1.69.0\n\n") }
end
+
context 'strips PGP MESSAGE' do
let(:strip_signature) { helper.strip_signature( pgp_message_tag ) }
it { expect(strip_signature).to eq("Version 1.69.0\n\n") }
end
+
context 'strips SIGNED MESSAGE' do
let(:strip_signature) { helper.strip_signature( x509_message_tag ) }
diff --git a/spec/helpers/groups/observability_helper_spec.rb b/spec/helpers/groups/observability_helper_spec.rb
index 4393f4e9bec..6d0a8631f78 100644
--- a/spec/helpers/groups/observability_helper_spec.rb
+++ b/spec/helpers/groups/observability_helper_spec.rb
@@ -10,17 +10,17 @@ RSpec.describe Groups::ObservabilityHelper do
context 'if observability_path is missing from params' do
it 'returns the iframe src for action: dashboards' do
allow(helper).to receive(:params).and_return({ action: 'dashboards' })
- expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/#{group.id}/")
+ expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/-/#{group.id}/")
end
it 'returns the iframe src for action: manage' do
allow(helper).to receive(:params).and_return({ action: 'manage' })
- expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/#{group.id}/dashboards")
+ expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/-/#{group.id}/dashboards")
end
it 'returns the iframe src for action: explore' do
allow(helper).to receive(:params).and_return({ action: 'explore' })
- expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/#{group.id}/explore")
+ expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/-/#{group.id}/explore")
end
end
@@ -28,7 +28,7 @@ RSpec.describe Groups::ObservabilityHelper do
context 'if observability_path is valid' do
it 'returns the iframe src by injecting the observability path' do
allow(helper).to receive(:params).and_return({ action: '/explore', observability_path: '/foo?bar=foobar' })
- expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/#{group.id}/foo?bar=foobar")
+ expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/-/#{group.id}/foo?bar=foobar")
end
end
@@ -40,7 +40,7 @@ RSpec.describe Groups::ObservabilityHelper do
"/test?groupId=<script>alert('attack!')</script>"
})
expect(helper.observability_iframe_src(group)).to eq(
- "#{observability_url}/#{group.id}/test?groupId=alert('attack!')"
+ "#{observability_url}/-/#{group.id}/test?groupId=alert('attack!')"
)
end
end
diff --git a/spec/helpers/groups/settings_helper_spec.rb b/spec/helpers/groups/settings_helper_spec.rb
index f8c0bfc19a1..ed948f5456c 100644
--- a/spec/helpers/groups/settings_helper_spec.rb
+++ b/spec/helpers/groups/settings_helper_spec.rb
@@ -11,8 +11,7 @@ RSpec.describe Groups::SettingsHelper do
using RSpec::Parameterized::TableSyntax
fake_form_id = "fake_form_id"
-
- where(:is_paid, :is_button_disabled, :form_value_id) do
+ where(:prevent_delete_response, :is_button_disabled, :form_value_id) do
true | "true" | nil
true | "true" | fake_form_id
false | "false" | nil
@@ -21,7 +20,7 @@ RSpec.describe Groups::SettingsHelper do
with_them do
it "returns expected parameters" do
- allow(group).to receive(:paid?).and_return(is_paid)
+ allow(group).to receive(:prevent_delete?).and_return(prevent_delete_response)
expected = helper.group_settings_confirm_modal_data(group, form_value_id)
expect(expected).to eq({
diff --git a/spec/helpers/ide_helper_spec.rb b/spec/helpers/ide_helper_spec.rb
index 447967fd345..29b2784412e 100644
--- a/spec/helpers/ide_helper_spec.rb
+++ b/spec/helpers/ide_helper_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe IdeHelper do
+RSpec.describe IdeHelper, feature_category: :web_ide do
describe '#ide_data' do
let_it_be(:project) { create(:project) }
let_it_be(:user) { project.creator }
@@ -18,6 +18,8 @@ RSpec.describe IdeHelper do
self.instance_variable_set(:@branch, 'master')
self.instance_variable_set(:@project, project)
+ self.instance_variable_set(:@path, 'foo/README.md')
+ self.instance_variable_set(:@merge_request, '7')
end
it 'returns hash' do
@@ -30,7 +32,11 @@ RSpec.describe IdeHelper do
help_page_path('user/project/web_ide/index.md', anchor: 'vscode-reimplementation'),
'branch-name' => 'master',
'project-path' => project.path_with_namespace,
- 'csp-nonce' => 'test-csp-nonce'
+ 'csp-nonce' => 'test-csp-nonce',
+ 'ide-remote-path' => ide_remote_path(remote_host: ':remote_host', remote_path: ':remote_path'),
+ 'file-path' => 'foo/README.md',
+ 'merge-request' => '7',
+ 'fork-info' => nil
)
end
diff --git a/spec/helpers/invite_members_helper_spec.rb b/spec/helpers/invite_members_helper_spec.rb
index c753d553371..48e94ec7e98 100644
--- a/spec/helpers/invite_members_helper_spec.rb
+++ b/spec/helpers/invite_members_helper_spec.rb
@@ -21,7 +21,8 @@ RSpec.describe InviteMembersHelper do
invalid_groups: project.related_group_ids,
help_link: help_page_url('user/permissions'),
is_project: 'true',
- access_levels: ProjectMember.access_level_roles.to_json
+ access_levels: ProjectMember.access_level_roles.to_json,
+ full_path: project.full_path
}
expect(helper.common_invite_group_modal_data(project, ProjectMember, 'true')).to include(attributes)
@@ -56,7 +57,8 @@ RSpec.describe InviteMembersHelper do
id: project.id,
root_id: project.root_ancestor.id,
name: project.name,
- default_access_level: Gitlab::Access::GUEST
+ default_access_level: Gitlab::Access::GUEST,
+ full_path: project.full_path
}
expect(helper.common_invite_modal_dataset(project)).to include(attributes)
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index 18a21b59409..15b57a4c9eb 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -98,6 +98,66 @@ RSpec.describe IssuablesHelper do
end
end
+ describe '#assigned_issuables_count', feature_category: :project_management do
+ context 'when issuable is issues' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project).tap { |p| p.add_developer(user) } }
+
+ subject { helper.assigned_issuables_count(:issues) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ context 'when assigned issues count is over 100' do
+ let_it_be(:issues) { create_list(:issue, 101, project: project, assignees: [user]) }
+
+ before do
+ stub_feature_flags(limit_assigned_issues_count: false)
+ end
+
+ it { is_expected.to eq 101 }
+
+ context 'when FF limit_assigned_issues_count is enabled' do
+ before do
+ stub_feature_flags(limit_assigned_issues_count: true)
+ end
+
+ it { is_expected.to eq 100 }
+ end
+ end
+ end
+ end
+
+ describe '#assigned_open_issues_count_text', feature_category: :project_management do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project).tap { |p| p.add_developer(user) } }
+
+ subject { helper.assigned_open_issues_count_text }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ context 'when assigned issues count is over 99' do
+ let_it_be(:issues) { create_list(:issue, 100, project: project, assignees: [user]) }
+
+ before do
+ stub_feature_flags(limit_assigned_issues_count: false)
+ end
+
+ it { is_expected.to eq '100' }
+
+ context 'when FF limit_assigned_issues_count is enabled' do
+ before do
+ stub_feature_flags(limit_assigned_issues_count: true)
+ end
+
+ it { is_expected.to eq '99+' }
+ end
+ end
+ end
+
describe '#issuable_meta', time_travel_to: '2022-08-05 00:00:00 +0000' do
let(:user) { create(:user) }
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index e5bd8e6532f..ed363268cdf 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -3,9 +3,8 @@
require 'spec_helper'
RSpec.describe IssuesHelper do
- let(:project) { create(:project) }
- let(:issue) { create(:issue, project: project) }
- let(:ext_project) { create(:project, :with_redmine_integration) }
+ let_it_be(:project) { create(:project) }
+ let_it_be_with_reload(:issue) { create(:issue, project: project) }
describe '#work_item_type_icon' do
it 'returns icon of all standard base types' do
@@ -381,6 +380,27 @@ RSpec.describe IssuesHelper do
end
end
+ describe '#dashboard_issues_list_data' do
+ let(:current_user) { double.as_null_object }
+
+ it 'returns expected result' do
+ allow(helper).to receive(:current_user).and_return(current_user)
+ allow(helper).to receive(:image_path).and_return('#')
+ allow(helper).to receive(:url_for).and_return('#')
+
+ expected = {
+ calendar_path: '#',
+ empty_state_svg_path: '#',
+ initial_sort: current_user&.user_preference&.issues_sort,
+ is_public_visibility_restricted: Gitlab::CurrentSettings.restricted_visibility_levels ? 'false' : '',
+ is_signed_in: current_user.present?.to_s,
+ rss_path: '#'
+ }
+
+ expect(helper.dashboard_issues_list_data(current_user)).to include(expected)
+ end
+ end
+
describe '#issues_form_data' do
it 'returns expected result' do
expected = {
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index 85420d4afda..e8e981251e3 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -321,4 +321,27 @@ RSpec.describe LabelsHelper do
expect(wrap_label_html('xss', label: xss_label, small: false)).not_to include('color:')
end
end
+
+ describe '#label_subscription_toggle_button_text' do
+ let(:label) { instance_double(Label) }
+ let(:current_user) { instance_double(User) }
+
+ subject { label_subscription_toggle_button_text(label) }
+
+ context 'when the label is subscribed' do
+ before do
+ allow(label).to receive(:subscribed?).and_return(true)
+ end
+
+ it { is_expected.to eq(_('Unsubscribe')) }
+ end
+
+ context 'when the label is not subscribed' do
+ before do
+ allow(label).to receive(:subscribed?).and_return(false)
+ end
+
+ it { is_expected.to eq(_('Subscribe')) }
+ end
+ end
end
diff --git a/spec/helpers/listbox_helper_spec.rb b/spec/helpers/listbox_helper_spec.rb
index cba00b43ae5..bae9c40aa02 100644
--- a/spec/helpers/listbox_helper_spec.rb
+++ b/spec/helpers/listbox_helper_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe ListboxHelper do
*%w[
dropdown
b-dropdown
- gl-new-dropdown
+ gl-dropdown
btn-group
js-redirect-listbox
])
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index 0b3d400041c..d1c86abf6e9 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -445,11 +445,25 @@ RSpec.describe MarkupHelper do
shared_examples_for 'common markdown examples' do
let(:project_base) { build(:project, :repository) }
+ it 'displays inline code' do
+ object = create_object('Text with `inline code`')
+ expected = 'Text with <code>inline code</code>'
+
+ expect(first_line_in_markdown(object, attribute, 100, is_todo: true, project: project)).to match(expected)
+ end
+
+ it 'truncates the text with multiple paragraphs' do
+ object = create_object("Paragraph 1\n\nParagraph 2")
+ expected = 'Paragraph 1...'
+
+ expect(first_line_in_markdown(object, attribute, 100, is_todo: true, project: project)).to match(expected)
+ end
+
it 'displays the first line of a code block' do
object = create_object("```\nCode block\nwith two lines\n```")
expected = %r{<pre.+><code><span class="line">Code block\.\.\.</span>\n</code></pre>}
- expect(first_line_in_markdown(object, attribute, 100, project: project)).to match(expected)
+ expect(first_line_in_markdown(object, attribute, 100, is_todo: true, project: project)).to match(expected)
end
it 'truncates a single long line of text' do
@@ -457,7 +471,7 @@ RSpec.describe MarkupHelper do
object = create_object(text * 4)
expected = (text * 2).sub(/.{3}/, '...')
- expect(first_line_in_markdown(object, attribute, 150, project: project)).to match(expected)
+ expect(first_line_in_markdown(object, attribute, 150, is_todo: true, project: project)).to match(expected)
end
it 'preserves code color scheme' do
@@ -466,28 +480,15 @@ RSpec.describe MarkupHelper do
"<code><span class=\"line\"><span class=\"k\">def</span> <span class=\"nf\">test</span>...</span>\n" \
"</code></pre>\n"
- expect(first_line_in_markdown(object, attribute, 150, project: project)).to eq(expected)
+ expect(first_line_in_markdown(object, attribute, 150, is_todo: true, project: project)).to eq(expected)
end
- context 'when images are allowed' do
- it 'preserves data-src for lazy images' do
- object = create_object("![ImageTest](/uploads/test.png)")
- image_url = "data-src=\".*/uploads/test.png\""
- text = first_line_in_markdown(object, attribute, 150, project: project, allow_images: true)
+ it 'removes any images' do
+ object = create_object("![ImageTest](/uploads/test.png)")
+ text = first_line_in_markdown(object, attribute, 150, is_todo: true, project: project)
- expect(text).to match(image_url)
- expect(text).to match('<a')
- end
- end
-
- context 'when images are not allowed' do
- it 'removes any images' do
- object = create_object("![ImageTest](/uploads/test.png)")
- text = first_line_in_markdown(object, attribute, 150, project: project)
-
- expect(text).not_to match('<img')
- expect(text).not_to match('<a')
- end
+ expect(text).not_to match('<img')
+ expect(text).not_to match('<a')
end
context 'labels formatting' do
@@ -497,7 +498,7 @@ RSpec.describe MarkupHelper do
create(:label, title: 'label_1', project: project)
object = create_object(label_title, project: project)
- first_line_in_markdown(object, attribute, 150, project: project)
+ first_line_in_markdown(object, attribute, 150, is_todo: true, project: project)
end
it 'preserves style attribute for a label that can be accessed by current_user' do
@@ -518,10 +519,10 @@ RSpec.describe MarkupHelper do
end
it 'keeps whitelisted tags' do
- html = '<a><i></i></a> <strong>strong</strong><em>em</em><b>b</b>'
+ html = '<i></i> <strong>strong</strong><em>em</em><b>b</b>'
object = create_object(html)
- result = first_line_in_markdown(object, attribute, 100, project: project)
+ result = first_line_in_markdown(object, attribute, 100, is_todo: true, project: project)
expect(result).to include(html)
end
@@ -530,7 +531,7 @@ RSpec.describe MarkupHelper do
object = create_object("hello \n\n [Test](README.md)")
expect do
- first_line_in_markdown(object, attribute, nil, project: project)
+ first_line_in_markdown(object, attribute, 100, is_todo: true, project: project)
end.not_to change { Gitlab::GitalyClient.get_request_count }
end
end
diff --git a/spec/helpers/nav/top_nav_helper_spec.rb b/spec/helpers/nav/top_nav_helper_spec.rb
index 0d43cfaae90..c4a8536032e 100644
--- a/spec/helpers/nav/top_nav_helper_spec.rb
+++ b/spec/helpers/nav/top_nav_helper_spec.rb
@@ -122,10 +122,10 @@ RSpec.describe Nav::TopNavHelper do
title: 'Switch to'
)
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
- css_class: 'qa-projects-dropdown',
data: {
track_action: 'click_dropdown',
- track_label: 'projects_dropdown'
+ track_label: 'projects_dropdown',
+ qa_selector: 'projects_dropdown'
},
icon: 'project',
id: 'project',
@@ -219,10 +219,10 @@ RSpec.describe Nav::TopNavHelper do
title: 'Switch to'
)
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
- css_class: 'qa-groups-dropdown',
data: {
track_action: 'click_dropdown',
- track_label: 'groups_dropdown'
+ track_label: 'groups_dropdown',
+ qa_selector: 'groups_dropdown'
},
icon: 'group',
id: 'groups',
@@ -323,10 +323,7 @@ RSpec.describe Nav::TopNavHelper do
title: 'Explore'
)
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
- data: {
- qa_selector: 'milestones_link',
- **menu_data_tracking_attrs('milestones')
- },
+ data: { **menu_data_tracking_attrs('milestones') },
href: '/dashboard/milestones',
icon: 'clock',
id: 'milestones',
@@ -385,10 +382,7 @@ RSpec.describe Nav::TopNavHelper do
title: 'Explore'
)
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
- data: {
- qa_selector: 'activity_link',
- **menu_data_tracking_attrs('activity')
- },
+ data: { **menu_data_tracking_attrs('activity') },
href: '/dashboard/activity',
icon: 'history',
id: 'activity',
@@ -417,15 +411,13 @@ RSpec.describe Nav::TopNavHelper do
it 'has admin as first :secondary item' do
expected_admin_item = ::Gitlab::Nav::TopNavMenuItem.build(
data: {
- qa_selector: 'menu_item_link',
- qa_title: 'Admin',
+ qa_selector: 'admin_area_link',
**menu_data_tracking_attrs('admin')
},
id: 'admin',
title: 'Admin',
icon: 'admin',
- href: '/admin',
- css_class: 'qa-admin-area-link'
+ href: '/admin'
)
expect(subject[:secondary].first).to eq(expected_admin_item)
diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb
index 1e16d969744..34d7cadf048 100644
--- a/spec/helpers/page_layout_helper_spec.rb
+++ b/spec/helpers/page_layout_helper_spec.rb
@@ -108,8 +108,8 @@ RSpec.describe PageLayoutHelper do
tags = helper.page_card_meta_tags
aggregate_failures do
- expect(tags).to include %q(<meta property="twitter:label1" content="foo" />)
- expect(tags).to include %q(<meta property="twitter:data1" content="bar" />)
+ expect(tags).to include %q(<meta property="twitter:label1" content="foo">)
+ expect(tags).to include %q(<meta property="twitter:data1" content="bar">)
end
end
diff --git a/spec/helpers/preferred_language_switcher_helper_spec.rb b/spec/helpers/preferred_language_switcher_helper_spec.rb
new file mode 100644
index 00000000000..aab65ecc210
--- /dev/null
+++ b/spec/helpers/preferred_language_switcher_helper_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe PreferredLanguageSwitcherHelper do
+ include StubLanguagesTranslationPercentage
+
+ describe '#ordered_selectable_locales' do
+ before do
+ stub_languages_translation_percentage(es: 65, en: 100, zh_CN: described_class::SWITCHER_MINIMUM_TRANSLATION_LEVEL)
+ end
+
+ it 'returns filtered and ordered by translation level selectable locales' do
+ expect(helper.ordered_selectable_locales).to eq(
+ [
+ { value: 'en', text: 'English', percentage: 100 },
+ { value: 'zh_CN', text: "简体中文", percentage: described_class::SWITCHER_MINIMUM_TRANSLATION_LEVEL }
+ ]
+ )
+ end
+ end
+end
diff --git a/spec/helpers/programming_languages_helper_spec.rb b/spec/helpers/programming_languages_helper_spec.rb
new file mode 100644
index 00000000000..bbea48be64d
--- /dev/null
+++ b/spec/helpers/programming_languages_helper_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ProgrammingLanguagesHelper do
+ describe '.search_language_placeholder' do
+ let(:programming_language) { build(:programming_language, id: 1, name: 'Ruby') }
+
+ before do
+ allow(helper).to receive(:programming_languages).and_return([programming_language])
+ end
+
+ context 'with no `language` param' do
+ it 'returns a placeholder' do
+ expect(helper.search_language_placeholder).to eq(_('Language'))
+ end
+ end
+
+ context 'with a `language` param' do
+ before do
+ allow(helper).to receive(:params).and_return({ language: '2' })
+ end
+
+ context 'when invalid' do
+ it 'returns a placeholder' do
+ expect(helper.search_language_placeholder).to eq(_('Language'))
+ end
+ end
+
+ context 'when valid' do
+ let(:programming_language) { build(:programming_language, id: 2, name: 'Ruby') }
+
+ it 'returns the chosen language' do
+ expect(helper.search_language_placeholder).to eq('Ruby')
+ end
+ end
+ end
+ end
+
+ describe '.programming_languages' do
+ it 'callings ProgrammingLanguage.most_popular' do
+ expect(ProgrammingLanguage).to receive(:most_popular)
+
+ helper.programming_languages
+ end
+ end
+
+ describe '.language_state_class' do
+ let(:language) { build(:programming_language, id: language_id) }
+
+ before do
+ allow(helper).to receive(:params).and_return({ language: '1' })
+ end
+
+ context 'when language param matches' do
+ let(:language_id) { 1 }
+
+ it 'returns `is-active`' do
+ expect(helper.language_state_class(language)).to be('is-active')
+ end
+ end
+
+ context 'when language param does not match' do
+ let(:language_id) { 2 }
+
+ it 'returns ``' do
+ expect(helper.language_state_class(language)).to be('')
+ end
+ end
+ end
+end
diff --git a/spec/helpers/projects/ml/experiments_helper_spec.rb b/spec/helpers/projects/ml/experiments_helper_spec.rb
index e4421ff7606..e6959a03c4a 100644
--- a/spec/helpers/projects/ml/experiments_helper_spec.rb
+++ b/spec/helpers/projects/ml/experiments_helper_spec.rb
@@ -5,28 +5,36 @@ require 'rspec'
require 'spec_helper'
require 'mime/types'
-RSpec.describe Projects::Ml::ExperimentsHelper do
- let_it_be(:project) { build(:project, :private) }
- let_it_be(:experiment) { build(:ml_experiments, user_id: project.creator, project: project) }
- let_it_be(:candidates) do
- create_list(:ml_candidates, 2, experiment: experiment, user: project.creator).tap do |c|
- c[0].params.create!([{ name: 'param1', value: 'p1' }, { name: 'param2', value: 'p2' }])
- c[0].metrics.create!(
+RSpec.describe Projects::Ml::ExperimentsHelper, feature_category: :mlops do
+ let_it_be(:project) { create(:project, :private) }
+ let_it_be(:experiment) { create(:ml_experiments, user_id: project.creator, project: project) }
+ let_it_be(:candidate0) do
+ create(:ml_candidates, experiment: experiment, user: project.creator).tap do |c|
+ c.params.build([{ name: 'param1', value: 'p1' }, { name: 'param2', value: 'p2' }])
+ c.metrics.create!(
[{ name: 'metric1', value: 0.1 }, { name: 'metric2', value: 0.2 }, { name: 'metric3', value: 0.3 }]
)
+ end
+ end
- c[1].params.create!([{ name: 'param2', value: 'p3' }, { name: 'param3', value: 'p4' }])
- c[1].metrics.create!(name: 'metric3', value: 0.4)
+ let_it_be(:candidate1) do
+ create(:ml_candidates, experiment: experiment, user: project.creator).tap do |c|
+ c.params.build([{ name: 'param2', value: 'p3' }, { name: 'param3', value: 'p4' }])
+ c.metrics.create!(name: 'metric3', value: 0.4)
end
end
+ let_it_be(:candidates) { [candidate0, candidate1] }
+
describe '#candidates_table_items' do
subject { helper.candidates_table_items(candidates) }
it 'creates the correct model for the table' do
expected_value = [
- { 'param1' => 'p1', 'param2' => 'p2', 'metric1' => '0.1000', 'metric2' => '0.2000', 'metric3' => '0.3000' },
- { 'param2' => 'p3', 'param3' => 'p4', 'metric3' => '0.4000' }
+ { 'param1' => 'p1', 'param2' => 'p2', 'metric1' => '0.1000', 'metric2' => '0.2000', 'metric3' => '0.3000',
+ 'artifact' => nil, 'details' => "/#{project.full_path}/-/ml/candidates/#{candidate0.iid}" },
+ { 'param2' => 'p3', 'param3' => 'p4', 'metric3' => '0.4000',
+ 'artifact' => nil, 'details' => "/#{project.full_path}/-/ml/candidates/#{candidate1.iid}" }
]
expect(Gitlab::Json.parse(subject)).to match_array(expected_value)
@@ -46,4 +54,40 @@ RSpec.describe Projects::Ml::ExperimentsHelper do
it { is_expected.to match_array(%w[metric1 metric2 metric3]) }
end
end
+
+ describe '#candidate_as_data' do
+ let(:candidate) { candidate0 }
+ let(:package) do
+ create(:generic_package, name: candidate.package_name, version: candidate.package_version, project: project)
+ end
+
+ subject { Gitlab::Json.parse(helper.candidate_as_data(candidate)) }
+
+ it 'generates the correct params' do
+ expect(subject['params']).to include(
+ hash_including('name' => 'param1', 'value' => 'p1'),
+ hash_including('name' => 'param2', 'value' => 'p2')
+ )
+ end
+
+ it 'generates the correct metrics' do
+ expect(subject['metrics']).to include(
+ hash_including('name' => 'metric1', 'value' => 0.1),
+ hash_including('name' => 'metric2', 'value' => 0.2),
+ hash_including('name' => 'metric3', 'value' => 0.3)
+ )
+ end
+
+ it 'generates the correct info' do
+ expected_info = {
+ 'iid' => candidate.iid,
+ 'path_to_artifact' => "/#{project.full_path}/-/packages/#{package.id}",
+ 'experiment_name' => candidate.experiment.name,
+ 'path_to_experiment' => "/#{project.full_path}/-/ml/experiments/#{experiment.iid}",
+ 'status' => 'running'
+ }
+
+ expect(subject['info']).to include(expected_info)
+ end
+ end
end
diff --git a/spec/helpers/projects/pipeline_helper_spec.rb b/spec/helpers/projects/pipeline_helper_spec.rb
index 0d3466d6ed2..35045aaef2a 100644
--- a/spec/helpers/projects/pipeline_helper_spec.rb
+++ b/spec/helpers/projects/pipeline_helper_spec.rb
@@ -25,6 +25,7 @@ RSpec.describe Projects::PipelineHelper do
graphql_resource_etag: graphql_etag_pipeline_path(pipeline),
metrics_path: namespace_project_ci_prometheus_metrics_histograms_path(namespace_id: project.namespace, project_id: project, format: :json),
pipeline_iid: pipeline.iid,
+ pipeline_path: pipeline_path(pipeline),
pipeline_project_path: project.full_path,
total_job_count: pipeline.total_size,
summary_endpoint: summary_project_pipeline_tests_path(project, pipeline, format: :json),
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 39b8b552672..db50c74ec4e 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -404,10 +404,6 @@ RSpec.describe ProjectsHelper do
Project.all
end
- before do
- stub_feature_flags(project_list_filter_bar: false)
- end
-
it 'returns true when there are projects' do
expect(helper.show_projects?(projects, {})).to eq(true)
end
@@ -963,7 +959,6 @@ RSpec.describe ProjectsHelper do
lfsEnabled: !!project.lfs_enabled,
emailsDisabled: project.emails_disabled?,
metricsDashboardAccessLevel: project.project_feature.metrics_dashboard_access_level,
- operationsAccessLevel: project.project_feature.operations_access_level,
showDefaultAwardEmojis: project.show_default_award_emojis?,
securityAndComplianceAccessLevel: project.security_and_compliance_access_level,
containerRegistryAccessLevel: project.project_feature.container_registry_access_level,
@@ -1338,6 +1333,27 @@ RSpec.describe ProjectsHelper do
end
end
+ describe '#fork_divergence_message' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:behind, :ahead, :message) do
+ 0 | 0 | 'Up to date with upstream repository'
+ 1 | 0 | '1 commit behind upstream repository'
+ 2 | 0 | '2 commits behind upstream repository'
+ 0 | 1 | '1 commit ahead of upstream repository'
+ 0 | 2 | '2 commits ahead of upstream repository'
+ 5 | 7 | '5 commits behind, 7 commits ahead of upstream repository'
+ nil | 7 | 'Fork has diverged from upstream repository'
+ 7 | nil | 'Fork has diverged from upstream repository'
+ end
+
+ with_them do
+ it 'returns message based on behind/ahead values' do
+ expect(helper.fork_divergence_message({ behind: behind, ahead: ahead })).to eq(message)
+ end
+ end
+ end
+
describe '#localized_project_human_access' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index 192dfaa9caf..45864320115 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe SearchHelper do
+RSpec.describe SearchHelper, feature_category: :global_search do
include MarkupHelper
include BadgesHelper
@@ -60,6 +60,44 @@ RSpec.describe SearchHelper do
expect(search_autocomplete_opts(project.name).size).to eq(1)
end
+ context 'for users' do
+ let_it_be(:another_user) { create(:user, name: 'Jane Doe') }
+ let(:term) { 'jane' }
+
+ it 'makes a call to SearchService' do
+ params = { search: term, per_page: 5, scope: 'users' }
+ expect(SearchService).to receive(:new).with(current_user, params).and_call_original
+
+ search_autocomplete_opts(term)
+ end
+
+ it 'returns users matching the term' do
+ result = search_autocomplete_opts(term)
+ expect(result.size).to eq(1)
+ expect(result.first[:id]).to eq(another_user.id)
+ end
+
+ context 'when current_user cannot read_users_list' do
+ before do
+ allow(Ability).to receive(:allowed?).and_return(true)
+ allow(Ability).to receive(:allowed?).with(current_user, :read_users_list).and_return(false)
+ end
+
+ it 'returns an empty array' do
+ expect(search_autocomplete_opts(term)).to eq([])
+ end
+ end
+
+ context 'with limiting' do
+ let!(:users) { create_list(:user, 6, name: 'Jane Doe') }
+
+ it 'only returns the first 5 users' do
+ result = search_autocomplete_opts(term)
+ expect(result.size).to eq(5)
+ end
+ end
+ end
+
it "includes the required project attrs" do
project = create(:project, namespace: create(:namespace, owner: user))
result = search_autocomplete_opts(project.name).first
@@ -858,17 +896,18 @@ RSpec.describe SearchHelper do
end
context 'code' do
- where(:feature_flag_tab_enabled, :show_elasticsearch_tabs, :project_search_tabs, :condition) do
- false | false | false | false
- true | true | true | true
- true | false | false | false
- false | true | false | false
- false | false | true | true
- true | false | true | true
+ where(:feature_flag_tab_enabled, :show_elasticsearch_tabs, :global_project, :project_search_tabs, :condition) do
+ false | false | nil | false | false
+ true | true | nil | true | true
+ true | false | nil | false | false
+ false | true | nil | false | false
+ false | false | ref(:project) | true | true
+ true | false | ref(:project) | false | false
end
with_them do
it 'data item condition is set correctly' do
+ @project = global_project
allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(show_elasticsearch_tabs)
allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_code_tab).and_return(feature_flag_tab_enabled)
allow(self).to receive(:project_search_tabs?).with(:blobs).and_return(project_search_tabs)
@@ -879,16 +918,16 @@ RSpec.describe SearchHelper do
end
context 'issues' do
- where(:feature_flag_tab_enabled, :project_search_tabs, :condition) do
- false | false | false
- true | true | true
- true | false | true
- false | true | true
+ where(:project_search_tabs, :global_search_issues_tab, :condition) do
+ false | false | false
+ false | true | true
+ true | false | true
+ true | true | true
end
with_them do
it 'data item condition is set correctly' do
- allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_issues_tab).and_return(feature_flag_tab_enabled)
+ allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_issues_tab).and_return(global_search_issues_tab)
allow(self).to receive(:project_search_tabs?).with(:issues).and_return(project_search_tabs)
expect(search_navigation[:issues][:condition]).to eq(condition)
@@ -897,11 +936,11 @@ RSpec.describe SearchHelper do
end
context 'merge requests' do
- where(:feature_flag_tab_enabled, :project_search_tabs, :condition) do
- false | false | false
- true | true | true
- true | false | true
- false | true | true
+ where(:project_search_tabs, :feature_flag_tab_enabled, :condition) do
+ false | false | false
+ true | false | true
+ false | true | true
+ true | true | true
end
with_them do
@@ -915,16 +954,19 @@ RSpec.describe SearchHelper do
end
context 'wiki' do
- where(:project_search_tabs, :show_elasticsearch_tabs, :condition) do
- false | false | false
- true | true | true
- true | false | true
- false | true | true
+ where(:global_search_wiki_tab, :show_elasticsearch_tabs, :global_project, :project_search_tabs, :condition) do
+ false | false | nil | true | true
+ false | false | nil | false | false
+ false | true | nil | false | false
+ true | false | nil | false | false
+ true | true | ref(:project) | false | false
end
with_them do
it 'data item condition is set correctly' do
+ @project = global_project
allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(show_elasticsearch_tabs)
+ allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_wiki_tab).and_return(global_search_wiki_tab)
allow(self).to receive(:project_search_tabs?).with(:wiki).and_return(project_search_tabs)
expect(search_navigation[:wiki_blobs][:condition]).to eq(condition)
@@ -933,17 +975,20 @@ RSpec.describe SearchHelper do
end
context 'commits' do
- where(:feature_flag_tab_enabled, :show_elasticsearch_tabs, :project_search_tabs, :condition) do
- false | false | false | false
- true | true | true | true
- true | false | false | false
- false | true | true | true
+ where(:global_search_commits_tab, :show_elasticsearch_tabs, :global_project, :project_search_tabs, :condition) do
+ false | false | nil | true | true
+ false | false | nil | false | false
+ false | true | nil | false | false
+ true | false | nil | false | false
+ true | true | ref(:project) | false | false
+ true | true | nil | false | true
end
with_them do
it 'data item condition is set correctly' do
+ @project = global_project
allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(show_elasticsearch_tabs)
- allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_commits_tab).and_return(feature_flag_tab_enabled)
+ allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_commits_tab).and_return(global_search_commits_tab)
allow(self).to receive(:project_search_tabs?).with(:commits).and_return(project_search_tabs)
expect(search_navigation[:commits][:condition]).to eq(condition)
@@ -952,11 +997,11 @@ RSpec.describe SearchHelper do
end
context 'comments' do
- where(:show_elasticsearch_tabs, :project_search_tabs, :condition) do
- true | true | true
- false | false | false
- true | false | true
- false | true | true
+ where(:project_search_tabs, :show_elasticsearch_tabs, :condition) do
+ true | true | true
+ false | false | false
+ false | true | true
+ true | false | true
end
with_them do
@@ -1012,7 +1057,7 @@ RSpec.describe SearchHelper do
with_them do
it 'data item condition is set correctly' do
- @show_snippets = global_show_snippets
+ allow(search_service).to receive(:show_snippets?).and_return(global_show_snippets)
@project = global_project
expect(search_navigation[:snippet_titles][:condition]).to eq(condition)
@@ -1063,9 +1108,9 @@ RSpec.describe SearchHelper do
allow(self).to receive(:can?).and_return(true)
allow(self).to receive(:project_search_tabs?).and_return(true)
allow(self).to receive(:feature_flag_tab_enabled?).and_return(true)
- allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(true)
allow(self).to receive(:feature_flag_tab_enabled?).and_return(true)
- @show_snippets = true
+ allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(true)
+ allow(search_service).to receive(:show_snippets?).and_return(true)
@project = nil
end
diff --git a/spec/helpers/sorting_helper_spec.rb b/spec/helpers/sorting_helper_spec.rb
index 3e555301325..d561b08efac 100644
--- a/spec/helpers/sorting_helper_spec.rb
+++ b/spec/helpers/sorting_helper_spec.rb
@@ -109,103 +109,14 @@ RSpec.describe SortingHelper do
describe '#projects_sort_options_hash' do
it 'returns a hash of available sorting options' do
- expect(projects_sort_options_hash).to include(project_common_options)
- end
- end
-
- describe '#projects_reverse_sort_options_hash' do
- context 'returns a reversed hash of available sorting options' do
- using RSpec::Parameterized::TableSyntax
-
- where(:sort_key, :reverse_sort_title) do
- sort_value_latest_activity | sort_value_oldest_activity
- sort_value_recently_created | sort_value_oldest_created
- sort_value_name | sort_value_name_desc
- sort_value_stars_desc | sort_value_stars_asc
- sort_value_oldest_activity | sort_value_latest_activity
- sort_value_oldest_created | sort_value_recently_created
- sort_value_name_desc | sort_value_name
- sort_value_stars_asc | sort_value_stars_desc
- end
-
- with_them do
- it do
- reverse_hash = projects_reverse_sort_options_hash
-
- expect(reverse_hash).to include(sort_key)
- expect(reverse_hash[sort_key]).to eq(reverse_sort_title)
- end
- end
- end
- end
-
- describe '#project_sort_direction_button' do
- context 'returns the correct icon for each sort option' do
- using RSpec::Parameterized::TableSyntax
-
- sort_lowest_icon = 'sort-lowest'
- sort_highest_icon = 'sort-highest'
-
- where(:selected_sort, :icon) do
- sort_value_latest_activity | sort_highest_icon
- sort_value_recently_created | sort_highest_icon
- sort_value_name_desc | sort_highest_icon
- sort_value_stars_desc | sort_highest_icon
- sort_value_oldest_activity | sort_lowest_icon
- sort_value_oldest_created | sort_lowest_icon
- sort_value_name | sort_lowest_icon
- sort_value_stars_asc | sort_lowest_icon
- end
-
- with_them do
- it do
- set_sorting_url selected_sort
-
- expect(project_sort_direction_button(selected_sort)).to include(icon)
- end
- end
- end
-
- it 'returns the correct link to reverse the current sort option' do
- sort_options_links = projects_reverse_sort_options_hash
-
- sort_options_links.each do |selected_sort, reverse_sort|
- set_sorting_url selected_sort
-
- expect(project_sort_direction_button(selected_sort)).to include(reverse_sort)
- end
- end
- end
-
- describe '#projects_sort_option_titles' do
- it 'returns a hash of titles for the sorting options' do
options = project_common_options.merge({
- sort_value_oldest_activity => sort_title_latest_activity,
- sort_value_oldest_created => sort_title_created_date,
- sort_value_name_desc => sort_title_name,
- sort_value_stars_asc => sort_title_stars
+ sort_value_oldest_activity => sort_title_oldest_activity,
+ sort_value_oldest_created => sort_title_oldest_created,
+ sort_value_recently_created => sort_title_recently_created,
+ sort_value_stars_desc => sort_title_most_stars
})
- expect(projects_sort_option_titles).to eq(options)
- end
- end
-
- describe 'with project_list_filter_bar off' do
- before do
- stub_feature_flags(project_list_filter_bar: false)
- end
-
- describe '#projects_sort_options_hash' do
- it 'returns a hash of available sorting options' do
- options = project_common_options.merge({
- sort_value_oldest_activity => sort_title_oldest_activity,
- sort_value_oldest_created => sort_title_oldest_created,
- sort_value_recently_created => sort_title_recently_created,
- sort_value_stars_desc => sort_title_most_stars
- })
-
- expect(projects_sort_options_hash).to eq(options)
- end
+ expect(projects_sort_options_hash).to eq(options)
end
end
end
diff --git a/spec/helpers/tab_helper_spec.rb b/spec/helpers/tab_helper_spec.rb
index 80a1224abbb..5b74eda34cd 100644
--- a/spec/helpers/tab_helper_spec.rb
+++ b/spec/helpers/tab_helper_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe TabHelper do
describe 'gl_tabs_nav' do
it 'creates a tabs navigation' do
- expect(helper.gl_tabs_nav).to match(%r{<ul class="nav gl-tabs-nav"><\/ul>})
+ expect(helper.gl_tabs_nav).to match(%r{<ul class="nav gl-tabs-nav"></ul>})
end
it 'captures block output' do
diff --git a/spec/helpers/todos_helper_spec.rb b/spec/helpers/todos_helper_spec.rb
index 7c91dd0570f..ca334a04fe9 100644
--- a/spec/helpers/todos_helper_spec.rb
+++ b/spec/helpers/todos_helper_spec.rb
@@ -14,6 +14,8 @@ RSpec.describe TodosHelper do
note: 'I am note, hear me roar')
end
+ let_it_be(:group) { create(:group, :public, name: 'Group 1') }
+
let_it_be(:design_todo) do
create(:todo, :mentioned,
user: user,
@@ -37,6 +39,10 @@ RSpec.describe TodosHelper do
create(:todo, target: issue)
end
+ let_it_be(:group_todo) do
+ create(:todo, target: group)
+ end
+
describe '#todos_count_format' do
it 'shows fuzzy count for 100 or more items' do
expect(helper.todos_count_format(100)).to eq '99+'
@@ -50,16 +56,14 @@ RSpec.describe TodosHelper do
end
end
- describe '#todo_target_link' do
+ describe '#todo_target_name' do
context 'when given a design' do
let(:todo) { design_todo }
- it 'produces a good link' do
- path = helper.todo_target_path(todo)
- link = helper.todo_target_link(todo)
- expected = "<a href=\"#{path}\">design #{design.to_reference}</a>"
+ it 'references the filename of the design' do
+ name = helper.todo_target_name(todo)
- expect(link).to eq(expected)
+ expect(name).to eq(design.to_reference.to_s)
end
end
end
@@ -94,7 +98,7 @@ RSpec.describe TodosHelper do
it 'returns the title' do
title = helper.todo_target_title(todo)
- expect(title).to eq("\"Issue 1\"")
+ expect(title).to eq("Issue 1")
end
end
end
@@ -155,33 +159,51 @@ RSpec.describe TodosHelper do
expect(path).to eq("/#{issue.project.full_path}/-/issues/#{issue.iid}##{dom_id(note)}")
end
end
+
+ context 'when a user requests access to group' do
+ let_it_be(:group_access_request_todo) do
+ create(:todo,
+ target_id: group.id,
+ target_type: group.class.polymorphic_name,
+ group: group,
+ action: Todo::MEMBER_ACCESS_REQUESTED)
+ end
+
+ it 'responds with access requests tab' do
+ path = helper.todo_target_path(group_access_request_todo)
+
+ access_request_path = Gitlab::Routing.url_helpers.group_group_members_url(group, tab: 'access_requests')
+
+ expect(path).to eq(access_request_path)
+ end
+ end
end
- describe '#todo_target_type_name' do
- subject { helper.todo_target_type_name(todo) }
+ describe '#todo_target_aria_label' do
+ subject { helper.todo_target_aria_label(todo) }
context 'when given a design todo' do
let(:todo) { design_todo }
- it { is_expected.to eq('design') }
+ it { is_expected.to eq("Design ##{todo.target.iid}[#{todo.target.title}]") }
end
context 'when given an alert todo' do
let(:todo) { alert_todo }
- it { is_expected.to eq('alert') }
+ it { is_expected.to eq("Alert ^alert##{todo.target.iid}") }
end
context 'when given a task todo' do
let(:todo) { task_todo }
- it { is_expected.to eq('task') }
+ it { is_expected.to eq("Task ##{todo.target.iid}") }
end
context 'when given an issue todo' do
let(:todo) { issue_todo }
- it { is_expected.to eq('issue') }
+ it { is_expected.to eq("Issue ##{todo.target.iid}") }
end
context 'when given a merge request todo' do
@@ -190,7 +212,7 @@ RSpec.describe TodosHelper do
create(:todo, target: merge_request)
end
- it { is_expected.to eq('merge request') }
+ it { is_expected.to eq("Merge Request !#{todo.target.iid}") }
end
end
@@ -229,7 +251,7 @@ RSpec.describe TodosHelper do
todo.target.update!(state: 'closed')
end
- it_behaves_like 'a rendered state pill', css: '.gl-bg-red-500', state: 'closed'
+ it_behaves_like 'a rendered state pill', css: '.badge-danger', state: 'closed'
end
context 'merged MR' do
@@ -237,7 +259,7 @@ RSpec.describe TodosHelper do
todo.target.update!(state: 'merged')
end
- it_behaves_like 'a rendered state pill', css: '.gl-bg-blue-500', state: 'merged'
+ it_behaves_like 'a rendered state pill', css: '.badge-info', state: 'merged'
end
end
@@ -251,7 +273,7 @@ RSpec.describe TodosHelper do
todo.target.update!(state: 'closed')
end
- it_behaves_like 'a rendered state pill', css: '.gl-bg-blue-500', state: 'closed'
+ it_behaves_like 'a rendered state pill', css: '.badge-info', state: 'closed'
end
end
@@ -265,7 +287,7 @@ RSpec.describe TodosHelper do
todo.target.resolve!
end
- it_behaves_like 'a rendered state pill', css: '.gl-bg-blue-500', state: 'resolved'
+ it_behaves_like 'a rendered state pill', css: '.badge-info', state: 'resolved'
end
end
end
@@ -329,17 +351,17 @@ RSpec.describe TodosHelper do
where(:action, :self_added?, :expected_action_name) do
Todo::ASSIGNED | false | s_('Todos|assigned you')
Todo::ASSIGNED | true | s_('Todos|assigned')
- Todo::REVIEW_REQUESTED | true | s_('Todos|requested a review of')
- Todo::MENTIONED | true | format(s_("Todos|mentioned %{who} on"), who: s_('Todos|yourself'))
- Todo::MENTIONED | false | format(s_("Todos|mentioned %{who} on"), who: _('you'))
- Todo::DIRECTLY_ADDRESSED | true | format(s_("Todos|mentioned %{who} on"), who: s_('Todos|yourself'))
- Todo::DIRECTLY_ADDRESSED | false | format(s_("Todos|mentioned %{who} on"), who: _('you'))
- Todo::BUILD_FAILED | true | s_('Todos|The pipeline failed in')
- Todo::MARKED | true | s_('Todos|added a todo for')
- Todo::APPROVAL_REQUIRED | true | format(s_("Todos|set %{who} as an approver for"), who: s_('Todos|yourself'))
- Todo::APPROVAL_REQUIRED | false | format(s_("Todos|set %{who} as an approver for"), who: _('you'))
+ Todo::REVIEW_REQUESTED | true | s_('Todos|requested a review')
+ Todo::MENTIONED | true | format(s_("Todos|mentioned %{who}"), who: s_('Todos|yourself'))
+ Todo::MENTIONED | false | format(s_("Todos|mentioned %{who}"), who: _('you'))
+ Todo::DIRECTLY_ADDRESSED | true | format(s_("Todos|mentioned %{who}"), who: s_('Todos|yourself'))
+ Todo::DIRECTLY_ADDRESSED | false | format(s_("Todos|mentioned %{who}"), who: _('you'))
+ Todo::BUILD_FAILED | true | s_('Todos|The pipeline failed')
+ Todo::MARKED | true | s_('Todos|added a to-do item')
+ Todo::APPROVAL_REQUIRED | true | format(s_("Todos|set %{who} as an approver"), who: s_('Todos|yourself'))
+ Todo::APPROVAL_REQUIRED | false | format(s_("Todos|set %{who} as an approver"), who: _('you'))
Todo::UNMERGEABLE | true | s_('Todos|Could not merge')
- Todo::MERGE_TRAIN_REMOVED | true | s_("Todos|Removed from Merge Train:")
+ Todo::MERGE_TRAIN_REMOVED | true | s_("Todos|Removed from Merge Train")
end
with_them do
@@ -350,5 +372,45 @@ RSpec.describe TodosHelper do
it { expect(helper.todo_action_name(alert_todo)).to eq(expected_action_name) }
end
+
+ context 'member access requested' do
+ context 'when source is group' do
+ it 'returns group access message' do
+ group_todo.action = Todo::MEMBER_ACCESS_REQUESTED
+
+ expect(helper.todo_action_name(group_todo)).to eq(
+ format(s_("Todos|has requested access to group %{which}"), which: _(group.name))
+ )
+ end
+ end
+ end
+ end
+
+ describe '#todo_due_date' do
+ subject(:result) { helper.todo_due_date(todo) }
+
+ context 'due date is today' do
+ let_it_be(:issue_with_today_due_date) do
+ create(:issue, title: 'Issue 1', project: project, due_date: Date.current)
+ end
+
+ let(:todo) do
+ create(:todo, project: issue_with_today_due_date.project, target: issue_with_today_due_date, note: note)
+ end
+
+ it { expect(result).to match('Due today') }
+ end
+
+ context 'due date is not today' do
+ let_it_be(:issue_with_tomorrow_due_date) do
+ create(:issue, title: 'Issue 1', project: project, due_date: Date.tomorrow)
+ end
+
+ let(:todo) do
+ create(:todo, project: issue_with_tomorrow_due_date.project, target: issue_with_tomorrow_due_date, note: note)
+ end
+
+ it { expect(result).to match("Due #{l(Date.tomorrow, format: Date::DATE_FORMATS[:medium])}") }
+ end
end
end
diff --git a/spec/helpers/version_check_helper_spec.rb b/spec/helpers/version_check_helper_spec.rb
index 959c4a94a78..2bb85e7b6b8 100644
--- a/spec/helpers/version_check_helper_spec.rb
+++ b/spec/helpers/version_check_helper_spec.rb
@@ -34,4 +34,42 @@ RSpec.describe VersionCheckHelper do
end
end
end
+
+ describe '#gitlab_version_check' do
+ before do
+ allow_next_instance_of(VersionCheck) do |instance|
+ allow(instance).to receive(:response).and_return({ "severity" => "success" })
+ end
+ end
+
+ it 'returns an instance of the VersionCheck class' do
+ expect(helper.gitlab_version_check).to eq({ "severity" => "success" })
+ end
+ end
+
+ describe '#show_security_patch_upgrade_alert?' do
+ describe 'return conditions' do
+ where(:show_version_check, :gitlab_version_check, :result) do
+ [
+ [false, nil, false],
+ [false, { "severity" => "success" }, false],
+ [false, { "severity" => "danger" }, false],
+ [true, nil, false],
+ [true, { "severity" => "success" }, false],
+ [true, { "severity" => "danger" }, true]
+ ]
+ end
+
+ with_them do
+ before do
+ allow(helper).to receive(:show_version_check?).and_return(show_version_check)
+ allow(helper).to receive(:gitlab_version_check).and_return(gitlab_version_check)
+ end
+
+ it 'returns correct results' do
+ expect(helper.show_security_patch_upgrade_alert?).to eq result
+ end
+ end
+ end
+ end
end
diff --git a/spec/helpers/web_hooks/web_hooks_helper_spec.rb b/spec/helpers/web_hooks/web_hooks_helper_spec.rb
index 473f33a982f..bcd9d2df1dc 100644
--- a/spec/helpers/web_hooks/web_hooks_helper_spec.rb
+++ b/spec/helpers/web_hooks/web_hooks_helper_spec.rb
@@ -7,39 +7,16 @@ RSpec.describe WebHooks::WebHooksHelper do
let(:current_user) { nil }
let(:callout_dismissed) { false }
- let(:web_hooks_disable_failed) { false }
- let(:webhooks_failed_callout) { false }
before do
allow(helper).to receive(:current_user).and_return(current_user)
allow(helper).to receive(:web_hook_disabled_dismissed?).with(project).and_return(callout_dismissed)
-
- stub_feature_flags(
- webhooks_failed_callout: webhooks_failed_callout,
- web_hooks_disable_failed: web_hooks_disable_failed
- )
end
shared_context 'user is logged in' do
let(:current_user) { create(:user) }
end
- shared_context 'webhooks_failed_callout is enabled' do
- let(:webhooks_failed_callout) { true }
- end
-
- shared_context 'webhooks_failed_callout is enabled for this project' do
- let(:webhooks_failed_callout) { project }
- end
-
- shared_context 'web_hooks_disable_failed is enabled' do
- let(:web_hooks_disable_failed) { true }
- end
-
- shared_context 'web_hooks_disable_failed is enabled for this project' do
- let(:web_hooks_disable_failed) { project }
- end
-
shared_context 'the user has permission' do
before do
project.add_maintainer(current_user)
@@ -59,8 +36,6 @@ RSpec.describe WebHooks::WebHooksHelper do
describe '#show_project_hook_failed_callout?' do
context 'all conditions are met' do
include_context 'user is logged in'
- include_context 'webhooks_failed_callout is enabled'
- include_context 'web_hooks_disable_failed is enabled'
include_context 'the user has permission'
include_context 'a hook has failed'
@@ -85,23 +60,9 @@ RSpec.describe WebHooks::WebHooksHelper do
end
end
- context 'all conditions are met, project scoped flags' do
- include_context 'user is logged in'
- include_context 'webhooks_failed_callout is enabled for this project'
- include_context 'web_hooks_disable_failed is enabled for this project'
- include_context 'the user has permission'
- include_context 'a hook has failed'
-
- it 'is true' do
- expect(helper).to be_show_project_hook_failed_callout(project: project)
- end
- end
-
context 'one condition is not met' do
contexts = [
'user is logged in',
- 'webhooks_failed_callout is enabled',
- 'web_hooks_disable_failed is enabled',
'the user has permission',
'a hook has failed'
]
diff --git a/spec/helpers/wiki_helper_spec.rb b/spec/helpers/wiki_helper_spec.rb
index 59624dc0682..497cd5d1e7f 100644
--- a/spec/helpers/wiki_helper_spec.rb
+++ b/spec/helpers/wiki_helper_spec.rb
@@ -76,7 +76,7 @@ RSpec.describe WikiHelper do
describe '#wiki_sort_controls' do
let(:wiki) { create(:project_wiki) }
let(:wiki_link) { helper.wiki_sort_controls(wiki, direction) }
- let(:classes) { "gl-button btn btn-default btn-icon has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort" }
+ let(:classes) { "gl-button btn btn-default btn-icon has-tooltip reverse-sort-btn rspec-reverse-sort" }
def expected_link(direction, icon_class)
path = "/#{wiki.project.full_path}/-/wikis/pages?direction=#{direction}"
diff --git a/spec/helpers/x509_helper_spec.rb b/spec/helpers/x509_helper_spec.rb
index 4e3e8c8d3f6..dfe9259bd0f 100644
--- a/spec/helpers/x509_helper_spec.rb
+++ b/spec/helpers/x509_helper_spec.rb
@@ -57,22 +57,4 @@ RSpec.describe X509Helper do
end
end
end
-
- describe '#x509_signature?' do
- let(:x509_signature) { create(:x509_commit_signature) }
- let(:gpg_signature) { create(:gpg_signature) }
-
- it 'detects a x509 signed commit' do
- signature = Gitlab::X509::Signature.new(
- X509Helpers::User1.signed_commit_signature,
- X509Helpers::User1.signed_commit_base_data,
- X509Helpers::User1.certificate_email,
- X509Helpers::User1.signed_commit_time
- )
-
- expect(x509_signature?(x509_signature)).to be_truthy
- expect(x509_signature?(signature)).to be_truthy
- expect(x509_signature?(gpg_signature)).to be_falsey
- end
- end
end
diff --git a/spec/initializers/database_config_spec.rb b/spec/initializers/database_config_spec.rb
index 230f1296760..bbb5e7b1923 100644
--- a/spec/initializers/database_config_spec.rb
+++ b/spec/initializers/database_config_spec.rb
@@ -7,15 +7,33 @@ RSpec.describe 'Database config initializer', :reestablished_active_record_base
load Rails.root.join('config/initializers/database_config.rb')
end
- it 'retains the correct database name for the connection' do
- previous_db_name = ApplicationRecord.connection.pool.db_config.name
+ shared_examples 'does not change connection attributes' do
+ it 'retains the correct database name for connection' do
+ previous_db_name = database_base_model.connection.pool.db_config.name
- subject
+ subject
- expect(ApplicationRecord.connection.pool.db_config.name).to eq(previous_db_name)
+ expect(database_base_model.connection.pool.db_config.name).to eq(previous_db_name)
+ end
+
+ it 'does not overwrite custom pool settings' do
+ expect { subject }.not_to change { database_base_model.connection_db_config.pool }
+ end
+ end
+
+ context 'when main database connection' do
+ let(:database_base_model) { Gitlab::Database.database_base_models[:main] }
+
+ it_behaves_like 'does not change connection attributes'
end
- it 'does not overwrite custom pool settings' do
- expect { subject }.not_to change { ActiveRecord::Base.connection_db_config.pool }
+ context 'when ci database connection' do
+ before do
+ skip_if_multiple_databases_not_setup
+ end
+
+ let(:database_base_model) { Gitlab::Database.database_base_models[:ci] }
+
+ it_behaves_like 'does not change connection attributes'
end
end
diff --git a/spec/initializers/diagnostic_reports_spec.rb b/spec/initializers/diagnostic_reports_spec.rb
index 01b1ed9b7b5..dc989efe809 100644
--- a/spec/initializers/diagnostic_reports_spec.rb
+++ b/spec/initializers/diagnostic_reports_spec.rb
@@ -2,15 +2,17 @@
require 'spec_helper'
-RSpec.describe 'diagnostic reports' do
+RSpec.describe 'diagnostic reports', :aggregate_failures, feature_category: :application_performance do
subject(:load_initializer) do
load Rails.root.join('config/initializers/diagnostic_reports.rb')
end
- shared_examples 'does not modify worker startup hooks' do
+ shared_examples 'does not modify worker hooks' do
it do
expect(Gitlab::Cluster::LifecycleEvents).not_to receive(:on_worker_start)
+ expect(Gitlab::Cluster::LifecycleEvents).not_to receive(:on_worker_stop)
expect(Gitlab::Memory::ReportsDaemon).not_to receive(:instance)
+ expect(Gitlab::Memory::Reporter).not_to receive(:new)
load_initializer
end
@@ -27,21 +29,27 @@ RSpec.describe 'diagnostic reports' do
end
let(:report_daemon) { instance_double(Gitlab::Memory::ReportsDaemon) }
+ let(:reporter) { instance_double(Gitlab::Memory::Reporter) }
it 'modifies worker startup hooks, starts Gitlab::Memory::ReportsDaemon' do
expect(Gitlab::Cluster::LifecycleEvents).to receive(:on_worker_start).and_call_original
-
+ expect(Gitlab::Cluster::LifecycleEvents).to receive(:on_worker_stop) # stub this out to not mutate global state
expect_next_instance_of(Gitlab::Memory::ReportsDaemon) do |daemon|
- expect(daemon).to receive(:start).and_call_original
+ expect(daemon).to receive(:start)
+ end
- # make sleep no-op
- allow(daemon).to receive(:sleep).and_return(nil)
+ load_initializer
+ end
- # let alive return 3 times: true, true, false
- allow(daemon).to receive(:alive).and_return(true, true, false)
- end
+ it 'writes scheduled heap dumps in on_worker_stop' do
+ expect(Gitlab::Cluster::LifecycleEvents).to receive(:on_worker_start)
+ expect(Gitlab::Cluster::LifecycleEvents).to receive(:on_worker_stop).and_call_original
+ expect(Gitlab::Memory::Reporter).to receive(:new).and_return(reporter)
+ expect(reporter).to receive(:run_report).with(an_instance_of(Gitlab::Memory::Reports::HeapDump))
load_initializer
+ # This is necessary because this hook normally fires during worker shutdown.
+ Gitlab::Cluster::LifecycleEvents.do_worker_stop
end
end
@@ -50,7 +58,7 @@ RSpec.describe 'diagnostic reports' do
allow(::Gitlab::Runtime).to receive(:puma?).and_return(false)
end
- include_examples 'does not modify worker startup hooks'
+ include_examples 'does not modify worker hooks'
end
end
@@ -59,7 +67,7 @@ RSpec.describe 'diagnostic reports' do
allow(::Gitlab::Runtime).to receive(:puma?).and_return(true)
end
- include_examples 'does not modify worker startup hooks'
+ include_examples 'does not modify worker hooks'
end
context 'when GITLAB_DIAGNOSTIC_REPORTS_ENABLED is set to false' do
@@ -68,6 +76,6 @@ RSpec.describe 'diagnostic reports' do
allow(::Gitlab::Runtime).to receive(:puma?).and_return(true)
end
- include_examples 'does not modify worker startup hooks'
+ include_examples 'does not modify worker hooks'
end
end
diff --git a/spec/initializers/forbid_sidekiq_in_transactions_spec.rb b/spec/initializers/forbid_sidekiq_in_transactions_spec.rb
index a89ac73f6fa..7b1907a7451 100644
--- a/spec/initializers/forbid_sidekiq_in_transactions_spec.rb
+++ b/spec/initializers/forbid_sidekiq_in_transactions_spec.rb
@@ -34,8 +34,7 @@ RSpec.describe 'Sidekiq::Worker' do
Class.new do
include Sidekiq::Worker
- def perform
- end
+ def perform; end
end
end
@@ -47,8 +46,7 @@ RSpec.describe 'Sidekiq::Worker' do
context 'for mailers' do
let(:mailer_class) do
Class.new(ApplicationMailer) do
- def test_mail
- end
+ def test_mail; end
end
end
diff --git a/spec/initializers/lograge_spec.rb b/spec/initializers/lograge_spec.rb
index 0a794e8ebcd..c423c144dc2 100644
--- a/spec/initializers/lograge_spec.rb
+++ b/spec/initializers/lograge_spec.rb
@@ -93,7 +93,7 @@ RSpec.describe 'lograge', type: :request do
include MemoryInstrumentationHelper
before do
- skip_memory_instrumentation!
+ verify_memory_instrumentation_available!
end
it 'logs memory usage metrics' do
diff --git a/spec/initializers/rails_yaml_safe_load_spec.rb b/spec/initializers/rails_yaml_safe_load_spec.rb
index 8cf6a3676e0..714c568b07a 100644
--- a/spec/initializers/rails_yaml_safe_load_spec.rb
+++ b/spec/initializers/rails_yaml_safe_load_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Rails YAML safe load' do
+ let_it_be(:project_namespace) { create(:project_namespace) }
+
let(:unsafe_load) { false }
let(:klass) do
@@ -13,7 +15,8 @@ RSpec.describe 'Rails YAML safe load' do
end
end
- let(:instance) { klass.new(description: data) }
+ let(:issue_type) { WorkItems::Type.default_by_type(:issue) }
+ let(:instance) { klass.new(description: data, work_item_type_id: issue_type.id, namespace_id: project_namespace.id) }
context 'with default permitted classes' do
let(:data) do
diff --git a/spec/lib/api/ci/helpers/runner_helpers_spec.rb b/spec/lib/api/ci/helpers/runner_helpers_spec.rb
index b254c419cbc..d32f7e4f0be 100644
--- a/spec/lib/api/ci/helpers/runner_helpers_spec.rb
+++ b/spec/lib/api/ci/helpers/runner_helpers_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Helpers::Runner do
+RSpec.describe API::Ci::Helpers::Runner, feature_category: :runner do
let(:ip_address) { '1.2.3.4' }
let(:runner_class) do
Class.new do
diff --git a/spec/lib/api/entities/package_spec.rb b/spec/lib/api/entities/package_spec.rb
index d63ea7833ac..9288f6fe8eb 100644
--- a/spec/lib/api/entities/package_spec.rb
+++ b/spec/lib/api/entities/package_spec.rb
@@ -32,4 +32,12 @@ RSpec.describe API::Entities::Package do
expect(subject[:_links][:web_path]).to match('/infrastructure_registry/')
end
end
+
+ context 'when package has no default status' do
+ let(:package) { create(:package, :error) }
+
+ it 'does not expose web_path in _links' do
+ expect(subject[:_links]).not_to have_key(:web_path)
+ end
+ end
end
diff --git a/spec/lib/api/entities/plan_limit_spec.rb b/spec/lib/api/entities/plan_limit_spec.rb
index a88ea3f4cad..baaaeb0b600 100644
--- a/spec/lib/api/entities/plan_limit_spec.rb
+++ b/spec/lib/api/entities/plan_limit_spec.rb
@@ -25,7 +25,8 @@ RSpec.describe API::Entities::PlanLimit do
:nuget_max_file_size,
:pypi_max_file_size,
:terraform_module_max_file_size,
- :storage_size_limit
+ :storage_size_limit,
+ :pipeline_hierarchy_size
)
end
diff --git a/spec/lib/api/entities/ssh_key_spec.rb b/spec/lib/api/entities/ssh_key_spec.rb
index 768ad416fbe..b4310035a66 100644
--- a/spec/lib/api/entities/ssh_key_spec.rb
+++ b/spec/lib/api/entities/ssh_key_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Entities::SSHKey do
+RSpec.describe API::Entities::SSHKey, feature_category: :authentication_and_authorization do
describe '#as_json' do
subject { entity.as_json }
@@ -15,7 +15,8 @@ RSpec.describe API::Entities::SSHKey do
title: key.title,
created_at: key.created_at,
expires_at: key.expires_at,
- key: key.publishable_key
+ key: key.publishable_key,
+ usage_type: 'auth_and_signing'
)
end
end
diff --git a/spec/lib/api/every_api_endpoint_spec.rb b/spec/lib/api/every_api_endpoint_spec.rb
index 5fe14823a29..c45ff9eb628 100644
--- a/spec/lib/api/every_api_endpoint_spec.rb
+++ b/spec/lib/api/every_api_endpoint_spec.rb
@@ -32,10 +32,21 @@ RSpec.describe 'Every API endpoint' do
next unless used_category
next if used_category == :not_owned
- [path, used_category] unless feature_categories.include?(used_category)
+ [klass, path, used_category] unless feature_categories.include?(used_category)
end.compact
- expect(routes_unknown_category).to be_empty, "#{routes_unknown_category.first(10)} had an unknown category"
+ message = -> do
+ list = routes_unknown_category.map do |klass, path, category|
+ "- #{klass} (#{path}): #{category}"
+ end
+
+ <<~MESSAGE
+ Unknown categories found for:
+ #{list.join("\n")}
+ MESSAGE
+ end
+
+ expect(routes_unknown_category).to be_empty, message
end
# This is required for API::Base.path_for_app to work, as it picks
diff --git a/spec/lib/api/helpers/packages_helpers_spec.rb b/spec/lib/api/helpers/packages_helpers_spec.rb
index b9c887b3e16..a3b21059334 100644
--- a/spec/lib/api/helpers/packages_helpers_spec.rb
+++ b/spec/lib/api/helpers/packages_helpers_spec.rb
@@ -238,4 +238,26 @@ RSpec.describe API::Helpers::PackagesHelpers do
end
end
end
+
+ describe '#track_package_event' do
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:action) { 'push_package' }
+ let(:scope) { :terraform_module }
+ let(:category) { described_class.name }
+ let(:namespace) { project.namespace }
+ let(:user) { project.creator }
+ let(:feature_flag_name) { nil }
+ let(:label) { 'redis_hll_counters.user_packages.user_packages_total_unique_counts_monthly' }
+ let(:property) { 'i_package_terraform_module_user' }
+
+ subject(:package_action) do
+ args = { category: category, namespace: namespace, user: user, project: project }
+ helper.track_package_event(action, scope, **args)
+ end
+ end
+ end
end
diff --git a/spec/lib/api/helpers/rate_limiter_spec.rb b/spec/lib/api/helpers/rate_limiter_spec.rb
index 2fed1cf3604..3640c7e30e7 100644
--- a/spec/lib/api/helpers/rate_limiter_spec.rb
+++ b/spec/lib/api/helpers/rate_limiter_spec.rb
@@ -19,8 +19,7 @@ RSpec.describe API::Helpers::RateLimiter do
@current_user = current_user
end
- def render_api_error!(**args)
- end
+ def render_api_error!(**args); end
end
end
diff --git a/spec/lib/api/support/git_access_actor_spec.rb b/spec/lib/api/support/git_access_actor_spec.rb
index e1c800d25a7..b3e8787583c 100644
--- a/spec/lib/api/support/git_access_actor_spec.rb
+++ b/spec/lib/api/support/git_access_actor_spec.rb
@@ -9,7 +9,8 @@ RSpec.describe API::Support::GitAccessActor do
subject { described_class.new(user: user, key: key) }
describe '.from_params' do
- let(:key) { create(:key) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:key) { create(:key, user: user) }
context 'with params that are valid' do
it 'returns an instance of API::Support::GitAccessActor' do
@@ -31,6 +32,42 @@ RSpec.describe API::Support::GitAccessActor do
expect(described_class.from_params(identifier: "key-#{key.id}").user).to eq(key.user)
end
end
+
+ context 'when passing a signing key' do
+ let_it_be(:key) { create(:key, usage_type: :signing, user: user) }
+
+ it 'does not identify the user' do
+ actor = described_class.from_params({ identifier: "key-#{key.id}" })
+
+ expect(actor).to be_instance_of(described_class)
+ expect(actor.user).to be_nil
+ end
+
+ it 'does not identify the key' do
+ actor = described_class.from_params({ key_id: key.id })
+
+ expect(actor).to be_instance_of(described_class)
+ expect(actor.key).to be_nil
+ end
+ end
+
+ context 'when passing an auth-only key' do
+ let_it_be(:key) { create(:key, usage_type: :auth, user: user) }
+
+ it 'identifies the user' do
+ actor = described_class.from_params({ identifier: "key-#{key.id}" })
+
+ expect(actor).to be_instance_of(described_class)
+ expect(actor.user).to eq(key.user)
+ end
+
+ it 'identifies the key' do
+ actor = described_class.from_params({ key_id: key.id })
+
+ expect(actor).to be_instance_of(described_class)
+ expect(actor.key).to eq(key)
+ end
+ end
end
describe 'attributes' do
diff --git a/spec/lib/atlassian/jira_connect/client_spec.rb b/spec/lib/atlassian/jira_connect/client_spec.rb
index 0ae0f02c46e..a8ee28d3714 100644
--- a/spec/lib/atlassian/jira_connect/client_spec.rb
+++ b/spec/lib/atlassian/jira_connect/client_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Atlassian::JiraConnect::Client do
+RSpec.describe Atlassian::JiraConnect::Client, feature_category: :integrations do
include StubRequests
subject(:client) { described_class.new('https://gitlab-test.atlassian.net', 'sample_secret') }
@@ -119,7 +119,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
let(:errors) { [{ 'message' => 'X' }, { 'message' => 'Y' }] }
let(:processed) { subject.send(:handle_response, response, 'foo') { |x| [:data, x] } }
- context 'the response is 200 OK' do
+ context 'when the response is 200 OK' do
let(:response) { double(code: 200, parsed_response: :foo) }
it 'yields to the block' do
@@ -127,7 +127,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
end
end
- context 'the response is 202 accepted' do
+ context 'when the response is 202 accepted' do
let(:response) { double(code: 202, parsed_response: :foo) }
it 'yields to the block' do
@@ -135,15 +135,15 @@ RSpec.describe Atlassian::JiraConnect::Client do
end
end
- context 'the response is 400 bad request' do
+ context 'when the response is 400 bad request' do
let(:response) { double(code: 400, parsed_response: errors) }
it 'extracts the errors messages' do
- expect(processed).to eq('errorMessages' => %w(X Y), 'responseCode' => 400)
+ expect(processed).to eq('errorMessages' => %w[X Y], 'responseCode' => 400)
end
end
- context 'the response is 401 forbidden' do
+ context 'when the response is 401 forbidden' do
let(:response) { double(code: 401, parsed_response: nil) }
it 'reports that our JWT is wrong' do
@@ -151,7 +151,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
end
end
- context 'the response is 403' do
+ context 'when the response is 403' do
let(:response) { double(code: 403, parsed_response: nil) }
it 'reports that the App is misconfigured' do
@@ -159,7 +159,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
end
end
- context 'the response is 413' do
+ context 'when the response is 413' do
let(:response) { double(code: 413, parsed_response: errors) }
it 'extracts the errors messages' do
@@ -167,7 +167,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
end
end
- context 'the response is 429' do
+ context 'when the response is 429' do
let(:response) { double(code: 429, parsed_response: nil) }
it 'reports that we exceeded the rate limit' do
@@ -175,7 +175,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
end
end
- context 'the response is 503' do
+ context 'when the response is 503' do
let(:response) { double(code: 503, parsed_response: nil) }
it 'reports that the service is unavailable' do
@@ -183,7 +183,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
end
end
- context 'the response is anything else' do
+ context 'when the response is anything else' do
let(:response) { double(code: 1000, parsed_response: :something) }
it 'reports that this was unanticipated' do
@@ -192,6 +192,26 @@ RSpec.describe Atlassian::JiraConnect::Client do
end
end
+ describe '#request_body_schema' do
+ let(:response) { instance_double(HTTParty::Response, success?: true, code: 200, request: request) }
+
+ context 'with valid JSON request body' do
+ let(:request) { instance_double(HTTParty::Request, raw_body: '{ "foo": 1, "bar": 2 }') }
+
+ it 'returns the request body' do
+ expect(subject.send(:request_body_schema, response)).to eq({ "foo" => nil, "bar" => nil })
+ end
+ end
+
+ context 'with invalid JSON request body' do
+ let(:request) { instance_double(HTTParty::Request, raw_body: 'invalid json') }
+
+ it 'reports the invalid json' do
+ expect(subject.send(:request_body_schema, response)).to eq('Request body includes invalid JSON')
+ end
+ end
+ end
+
describe '#store_deploy_info' do
let_it_be(:environment) { create(:environment, name: 'DEV', project: project) }
let_it_be(:deployments) do
@@ -222,7 +242,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
before do
path = '/rest/deployments/0.1/bulk'
- stub_full_request('https://gitlab-test.atlassian.net' + path, method: :post)
+ stub_full_request("https://gitlab-test.atlassian.net#{path}", method: :post)
.with(body: body, headers: expected_headers(path))
.to_return(body: response_body, headers: { 'Content-Type': 'application/json' })
end
@@ -232,7 +252,9 @@ RSpec.describe Atlassian::JiraConnect::Client do
end
it 'only sends information about relevant MRs' do
- expect(subject).to receive(:post).with('/rest/deployments/0.1/bulk', { deployments: have_attributes(size: 6) }).and_call_original
+ expect(subject).to receive(:post).with(
+ '/rest/deployments/0.1/bulk', { deployments: have_attributes(size: 6) }
+ ).and_call_original
subject.send(:store_deploy_info, project: project, deployments: deployments)
end
@@ -243,7 +265,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
subject.send(:store_deploy_info, project: project, deployments: deployments.take(1))
end
- context 'there are errors' do
+ context 'when there are errors' do
let(:rejections) do
[{ errors: [{ message: 'X' }, { message: 'Y' }] }, { errors: [{ message: 'Z' }] }]
end
@@ -251,7 +273,9 @@ RSpec.describe Atlassian::JiraConnect::Client do
it 'reports the errors' do
response = subject.send(:store_deploy_info, project: project, deployments: deployments)
- expect(response['errorMessages']).to eq(%w(X Y Z))
+ expect(response['errorMessages']).to eq(%w[X Y Z])
+ expect(response['responseCode']).to eq(200)
+ expect(response['requestBody']).to be_a(Hash)
end
end
end
@@ -282,7 +306,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
feature_flags.first.update!(description: 'RELEVANT-123')
feature_flags.second.update!(description: 'RELEVANT-123')
path = '/rest/featureflags/0.1/bulk'
- stub_full_request('https://gitlab-test.atlassian.net' + path, method: :post)
+ stub_full_request("https://gitlab-test.atlassian.net#{path}", method: :post)
.with(body: body, headers: expected_headers(path))
.to_return(body: response_body, headers: { 'Content-Type': 'application/json' })
end
@@ -292,9 +316,9 @@ RSpec.describe Atlassian::JiraConnect::Client do
end
it 'only sends information about relevant MRs' do
- expect(subject).to receive(:post).with('/rest/featureflags/0.1/bulk', {
- flags: have_attributes(size: 2), properties: Hash
- }).and_call_original
+ expect(subject).to receive(:post).with(
+ '/rest/featureflags/0.1/bulk', { flags: have_attributes(size: 2), properties: Hash }
+ ).and_call_original
subject.send(:store_ff_info, project: project, feature_flags: feature_flags)
end
@@ -305,7 +329,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
subject.send(:store_ff_info, project: project, feature_flags: [feature_flags.last])
end
- context 'there are errors' do
+ context 'when there are errors' do
let(:failures) do
{
a: [{ message: 'X' }, { message: 'Y' }],
@@ -343,7 +367,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
before do
path = '/rest/builds/0.1/bulk'
- stub_full_request('https://gitlab-test.atlassian.net' + path, method: :post)
+ stub_full_request("https://gitlab-test.atlassian.net#{path}", method: :post)
.with(body: body, headers: expected_headers(path))
.to_return(body: response_body, headers: { 'Content-Type': 'application/json' })
end
@@ -366,7 +390,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
subject.send(:store_build_info, project: project, pipelines: pipelines.take(1))
end
- context 'there are errors' do
+ context 'when there are errors' do
let(:failures) do
[{ errors: [{ message: 'X' }, { message: 'Y' }] }, { errors: [{ message: 'Z' }] }]
end
@@ -374,7 +398,9 @@ RSpec.describe Atlassian::JiraConnect::Client do
it 'reports the errors' do
response = subject.send(:store_build_info, project: project, pipelines: pipelines)
- expect(response['errorMessages']).to eq(%w(X Y Z))
+ expect(response['errorMessages']).to eq(%w[X Y Z])
+ expect(response['responseCode']).to eq(200)
+ expect(response['requestBody']).to be_a(Hash)
end
end
@@ -385,19 +411,21 @@ RSpec.describe Atlassian::JiraConnect::Client do
subject.send(:store_build_info, project: project, pipelines: pipelines)
end
- pipelines << create(:ci_pipeline, head_pipeline_of: create(:merge_request, :jira_branch))
+ pipelines << create(:ci_pipeline, project: project, head_pipeline_of: create(:merge_request, :jira_branch, source_project: project))
- expect { subject.send(:store_build_info, project: project, pipelines: pipelines) }.not_to exceed_query_limit(baseline)
+ expect do
+ subject.send(:store_build_info, project: project, pipelines: pipelines)
+ end.not_to exceed_query_limit(baseline)
end
end
describe '#store_dev_info' do
- let_it_be(:merge_requests) { create_list(:merge_request, 2, :unique_branches) }
+ let_it_be(:merge_requests) { create_list(:merge_request, 2, :unique_branches, source_project: project) }
before do
path = '/rest/devinfo/0.10/bulk'
- stub_full_request('https://gitlab-test.atlassian.net' + path, method: :post)
+ stub_full_request("https://gitlab-test.atlassian.net#{path}", method: :post)
.with(headers: expected_headers(path))
end
@@ -406,11 +434,16 @@ RSpec.describe Atlassian::JiraConnect::Client do
end
it 'avoids N+1 database queries' do
- control_count = ActiveRecord::QueryRecorder.new { subject.send(:store_dev_info, project: project, merge_requests: merge_requests) }.count
+ control_count = ActiveRecord::QueryRecorder.new do
+ subject.send(:store_dev_info, project: project, merge_requests: merge_requests)
+ end.count
- merge_requests << create(:merge_request, :unique_branches)
+ merge_requests << create(:merge_request, :unique_branches, source_project: project)
- expect { subject.send(:store_dev_info, project: project, merge_requests: merge_requests) }.not_to exceed_query_limit(control_count)
+ expect do
+ subject.send(:store_dev_info, project: project,
+ merge_requests: merge_requests)
+ end.not_to exceed_query_limit(control_count)
end
end
diff --git a/spec/lib/atlassian/jira_connect/jwt/asymmetric_spec.rb b/spec/lib/atlassian/jira_connect/jwt/asymmetric_spec.rb
index 86d672067a3..89c85489aea 100644
--- a/spec/lib/atlassian/jira_connect/jwt/asymmetric_spec.rb
+++ b/spec/lib/atlassian/jira_connect/jwt/asymmetric_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Atlassian::JiraConnect::Jwt::Asymmetric do
+RSpec.describe Atlassian::JiraConnect::Jwt::Asymmetric, feature_category: :integrations do
describe '#valid?' do
let_it_be(:private_key) { OpenSSL::PKey::RSA.generate 3072 }
@@ -17,6 +17,7 @@ RSpec.describe Atlassian::JiraConnect::Jwt::Asymmetric do
let(:jwt) { JWT.encode(jwt_claims, private_key, 'RS256', jwt_headers) }
let(:public_key) { private_key.public_key }
let(:stub_asymmetric_jwt_cdn) { 'https://connect-install-keys.atlassian.com' }
+ let(:jira_connect_proxy_url_setting) { nil }
let(:install_keys_url) { "#{stub_asymmetric_jwt_cdn}/#{public_key_id}" }
let(:qsh) do
Atlassian::Jwt.create_query_string_hash('https://gitlab.test/events/installed', 'POST', 'https://gitlab.test')
@@ -25,6 +26,8 @@ RSpec.describe Atlassian::JiraConnect::Jwt::Asymmetric do
before do
stub_request(:get, install_keys_url)
.to_return(body: public_key.to_s, status: 200)
+
+ stub_application_setting(jira_connect_proxy_url: jira_connect_proxy_url_setting)
end
it 'returns true when verified with public key from CDN' do
@@ -89,10 +92,7 @@ RSpec.describe Atlassian::JiraConnect::Jwt::Asymmetric do
context 'with jira_connect_proxy_url setting' do
let(:stub_asymmetric_jwt_cdn) { 'https://example.com/-/jira_connect/public_keys' }
-
- before do
- stub_application_setting(jira_connect_proxy_url: 'https://example.com')
- end
+ let(:jira_connect_proxy_url_setting) { 'https://example.com' }
it 'requests the settings CDN' do
expect(JWT).to receive(:decode).twice.and_call_original
@@ -101,22 +101,6 @@ RSpec.describe Atlassian::JiraConnect::Jwt::Asymmetric do
expect(WebMock).to have_requested(:get, "https://example.com/-/jira_connect/public_keys/#{public_key_id}")
end
-
- context 'when jira_connect_oauth_self_managed disabled' do
- let(:stub_asymmetric_jwt_cdn) { 'https://connect-install-keys.atlassian.com' }
-
- before do
- stub_feature_flags(jira_connect_oauth_self_managed: false)
- end
-
- it 'requests the default CDN' do
- expect(JWT).to receive(:decode).twice.and_call_original
-
- expect(asymmetric_jwt).to be_valid
-
- expect(WebMock).to have_requested(:get, install_keys_url)
- end
- end
end
end
diff --git a/spec/lib/atlassian/jira_connect/jwt/symmetric_spec.rb b/spec/lib/atlassian/jira_connect/jwt/symmetric_spec.rb
index 61adff7e221..109868f5d95 100644
--- a/spec/lib/atlassian/jira_connect/jwt/symmetric_spec.rb
+++ b/spec/lib/atlassian/jira_connect/jwt/symmetric_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Atlassian::JiraConnect::Jwt::Symmetric do
+RSpec.describe Atlassian::JiraConnect::Jwt::Symmetric, feature_category: :integrations do
let(:shared_secret) { 'secret' }
describe '#iss_claim' do
diff --git a/spec/lib/atlassian/jira_connect/serializers/author_entity_spec.rb b/spec/lib/atlassian/jira_connect/serializers/author_entity_spec.rb
index f31cf929244..e1158bb5988 100644
--- a/spec/lib/atlassian/jira_connect/serializers/author_entity_spec.rb
+++ b/spec/lib/atlassian/jira_connect/serializers/author_entity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Atlassian::JiraConnect::Serializers::AuthorEntity do
+RSpec.describe Atlassian::JiraConnect::Serializers::AuthorEntity, feature_category: :integrations do
subject { described_class.represent(user).as_json }
context 'when object is a User model' do
diff --git a/spec/lib/atlassian/jira_connect/serializers/base_entity_spec.rb b/spec/lib/atlassian/jira_connect/serializers/base_entity_spec.rb
index d7672c0baf1..f34bec1413b 100644
--- a/spec/lib/atlassian/jira_connect/serializers/base_entity_spec.rb
+++ b/spec/lib/atlassian/jira_connect/serializers/base_entity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Atlassian::JiraConnect::Serializers::BaseEntity do
+RSpec.describe Atlassian::JiraConnect::Serializers::BaseEntity, feature_category: :integrations do
let(:update_sequence_id) { nil }
subject do
diff --git a/spec/lib/atlassian/jira_connect/serializers/branch_entity_spec.rb b/spec/lib/atlassian/jira_connect/serializers/branch_entity_spec.rb
index e69e2aae94c..86e48a4a0fd 100644
--- a/spec/lib/atlassian/jira_connect/serializers/branch_entity_spec.rb
+++ b/spec/lib/atlassian/jira_connect/serializers/branch_entity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Atlassian::JiraConnect::Serializers::BranchEntity do
+RSpec.describe Atlassian::JiraConnect::Serializers::BranchEntity, feature_category: :integrations do
let(:project) { create(:project, :repository) }
let(:branch) { project.repository.find_branch('improve/awesome') }
diff --git a/spec/lib/atlassian/jira_connect/serializers/build_entity_spec.rb b/spec/lib/atlassian/jira_connect/serializers/build_entity_spec.rb
index a29f32d35b8..48787f2a0d2 100644
--- a/spec/lib/atlassian/jira_connect/serializers/build_entity_spec.rb
+++ b/spec/lib/atlassian/jira_connect/serializers/build_entity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Atlassian::JiraConnect::Serializers::BuildEntity do
+RSpec.describe Atlassian::JiraConnect::Serializers::BuildEntity, feature_category: :integrations do
let_it_be(:user) { create_default(:user) }
let_it_be(:project) { create_default(:project) }
diff --git a/spec/lib/atlassian/jira_connect/serializers/deployment_entity_spec.rb b/spec/lib/atlassian/jira_connect/serializers/deployment_entity_spec.rb
index 40b9e83719b..f6fca39fa68 100644
--- a/spec/lib/atlassian/jira_connect/serializers/deployment_entity_spec.rb
+++ b/spec/lib/atlassian/jira_connect/serializers/deployment_entity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Atlassian::JiraConnect::Serializers::DeploymentEntity do
+RSpec.describe Atlassian::JiraConnect::Serializers::DeploymentEntity, feature_category: :integrations do
let_it_be(:user) { create_default(:user) }
let_it_be(:project) { create_default(:project, :repository) }
let_it_be(:environment) { create(:environment, name: 'prod', project: project) }
diff --git a/spec/lib/atlassian/jira_connect/serializers/feature_flag_entity_spec.rb b/spec/lib/atlassian/jira_connect/serializers/feature_flag_entity_spec.rb
index 2d12cd1ed0a..3f84404f38d 100644
--- a/spec/lib/atlassian/jira_connect/serializers/feature_flag_entity_spec.rb
+++ b/spec/lib/atlassian/jira_connect/serializers/feature_flag_entity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Atlassian::JiraConnect::Serializers::FeatureFlagEntity do
+RSpec.describe Atlassian::JiraConnect::Serializers::FeatureFlagEntity, feature_category: :integrations do
let_it_be(:user) { create_default(:user) }
let_it_be(:project) { create_default(:project) }
diff --git a/spec/lib/atlassian/jira_connect/serializers/pull_request_entity_spec.rb b/spec/lib/atlassian/jira_connect/serializers/pull_request_entity_spec.rb
index 6399fc9053b..5ebb5ffed3b 100644
--- a/spec/lib/atlassian/jira_connect/serializers/pull_request_entity_spec.rb
+++ b/spec/lib/atlassian/jira_connect/serializers/pull_request_entity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Atlassian::JiraConnect::Serializers::PullRequestEntity do
+RSpec.describe Atlassian::JiraConnect::Serializers::PullRequestEntity, feature_category: :integrations do
let_it_be(:project) { create_default(:project, :repository) }
let_it_be(:merge_requests) { create_list(:merge_request, 2, :unique_branches) }
let_it_be(:notes) { create_list(:note, 2, system: false, noteable: merge_requests.first) }
diff --git a/spec/lib/atlassian/jira_connect/serializers/repository_entity_spec.rb b/spec/lib/atlassian/jira_connect/serializers/repository_entity_spec.rb
index 9100398ecc5..2a4fba0f00e 100644
--- a/spec/lib/atlassian/jira_connect/serializers/repository_entity_spec.rb
+++ b/spec/lib/atlassian/jira_connect/serializers/repository_entity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Atlassian::JiraConnect::Serializers::RepositoryEntity do
+RSpec.describe Atlassian::JiraConnect::Serializers::RepositoryEntity, feature_category: :integrations do
let(:update_sequence_id) { nil }
subject do
diff --git a/spec/lib/atlassian/jira_connect_spec.rb b/spec/lib/atlassian/jira_connect_spec.rb
index d9c34e938b4..14bf13b8fe6 100644
--- a/spec/lib/atlassian/jira_connect_spec.rb
+++ b/spec/lib/atlassian/jira_connect_spec.rb
@@ -2,7 +2,7 @@
require 'fast_spec_helper'
-RSpec.describe Atlassian::JiraConnect do
+RSpec.describe Atlassian::JiraConnect, feature_category: :integrations do
describe '.app_name' do
subject { described_class.app_name }
@@ -25,5 +25,13 @@ RSpec.describe Atlassian::JiraConnect do
expect(app_key).to eq('gitlab-jira-connect-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
end
end
+
+ context 'with jira_connect_proxy_url setting' do
+ before do
+ stub_application_setting(jira_connect_proxy_url: 'https://example.com')
+ end
+
+ it { is_expected.to eq('gitlab-jira-connect-example.com') }
+ end
end
end
diff --git a/spec/lib/backup/gitaly_backup_spec.rb b/spec/lib/backup/gitaly_backup_spec.rb
index 6b0747735ed..7cc8ce2cbae 100644
--- a/spec/lib/backup/gitaly_backup_spec.rb
+++ b/spec/lib/backup/gitaly_backup_spec.rb
@@ -61,7 +61,7 @@ RSpec.describe Backup::GitalyBackup do
it 'erases any existing repository backups' do
existing_file = File.join(destination, 'some_existing_file')
- IO.write(existing_file, "Some existing file.\n")
+ File.write(existing_file, "Some existing file.\n")
subject.start(:create, destination, backup_id: backup_id)
subject.finish!
diff --git a/spec/lib/backup/manager_spec.rb b/spec/lib/backup/manager_spec.rb
index f85b005f4d1..992dbec73c2 100644
--- a/spec/lib/backup/manager_spec.rb
+++ b/spec/lib/backup/manager_spec.rb
@@ -166,7 +166,7 @@ RSpec.describe Backup::Manager do
describe '#create' do
let(:incremental_env) { 'false' }
let(:expected_backup_contents) { %w{backup_information.yml task1.tar.gz task2.tar.gz} }
- let(:backup_time) { Time.utc(2019, 1, 1) }
+ let(:backup_time) { Time.zone.parse('2019-1-1') }
let(:backup_id) { "1546300800_2019_01_01_#{Gitlab::VERSION}" }
let(:full_backup_id) { backup_id }
let(:pack_tar_file) { "#{backup_id}_gitlab_backup.tar" }
@@ -284,7 +284,7 @@ RSpec.describe Backup::Manager do
allow(Dir).to receive(:chdir).and_yield
allow(Dir).to receive(:glob).and_return(files)
allow(FileUtils).to receive(:rm)
- allow(Time).to receive(:now).and_return(Time.utc(2016))
+ allow(Time).to receive(:now).and_return(Time.zone.parse('2016-1-1'))
end
context 'when keep_time is zero' do
diff --git a/spec/lib/banzai/filter/attributes_filter_spec.rb b/spec/lib/banzai/filter/attributes_filter_spec.rb
new file mode 100644
index 00000000000..cef5e24cdaa
--- /dev/null
+++ b/spec/lib/banzai/filter/attributes_filter_spec.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Banzai::Filter::AttributesFilter, feature_category: :team_planning do
+ using RSpec::Parameterized::TableSyntax
+ include FilterSpecHelper
+
+ def image
+ %(<img src="example.jpg">)
+ end
+
+ describe 'attribute syntax' do
+ context 'when attribute syntax is valid' do
+ where(:text, :result) do
+ "#{image}{width=100}" | '<img src="example.jpg" width="100">'
+ "#{image}{ width=100 }" | '<img src="example.jpg" width="100">'
+ "#{image}{width=\"100\"}" | '<img src="example.jpg" width="100">'
+ "#{image}{width=100 width=200}" | '<img src="example.jpg" width="200">'
+
+ "#{image}{.test_class width=100 style=\"width:400\"}" | '<img src="example.jpg" width="100">'
+ "<img src=\"example.jpg\" class=\"lazy\" />{width=100}" | '<img src="example.jpg" class="lazy" width="100">'
+ end
+
+ with_them do
+ it 'adds them to the img' do
+ expect(filter(text).to_html).to eq result
+ end
+ end
+ end
+
+ context 'when attribute syntax is invalid' do
+ where(:text, :result) do
+ "#{image} {width=100}" | '<img src="example.jpg"> {width=100}'
+ "#{image}{width=100\nheight=100}" | "<img src=\"example.jpg\">{width=100\nheight=100}"
+ "{width=100 height=100}\n#{image}" | "{width=100 height=100}\n<img src=\"example.jpg\">"
+ '<h1>header</h1>{width=100}' | '<h1>header</h1>{width=100}'
+ end
+
+ with_them do
+ it 'does not recognize as attributes' do
+ expect(filter(text).to_html).to eq result
+ end
+ end
+ end
+ end
+
+ describe 'height and width' do
+ context 'when size attributes are valid' do
+ where(:text, :result) do
+ "#{image}{width=100 height=200px}" | '<img src="example.jpg" width="100" height="200px">'
+ "#{image}{width=100}" | '<img src="example.jpg" width="100">'
+ "#{image}{width=100px}" | '<img src="example.jpg" width="100px">'
+ "#{image}{height=100%}" | '<img src="example.jpg" height="100%">'
+ "#{image}{width=\"100%\"}" | '<img src="example.jpg" width="100%">'
+ end
+
+ with_them do
+ it 'adds them to the img' do
+ expect(filter(text).to_html).to eq result
+ end
+ end
+ end
+
+ context 'when size attributes are invalid' do
+ where(:text, :result) do
+ "#{image}{width=100cs}" | '<img src="example.jpg">'
+ "#{image}{width=auto height=200}" | '<img src="example.jpg" height="200">'
+ end
+
+ with_them do
+ it 'ignores them' do
+ expect(filter(text).to_html).to eq result
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/banzai/filter/commit_trailers_filter_spec.rb b/spec/lib/banzai/filter/commit_trailers_filter_spec.rb
index c22517621c1..3ebe0798972 100644
--- a/spec/lib/banzai/filter/commit_trailers_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_trailers_filter_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require 'ffaker'
-RSpec.describe Banzai::Filter::CommitTrailersFilter do
+RSpec.describe Banzai::Filter::CommitTrailersFilter, feature_category: :source_code_management do
include FilterSpecHelper
include CommitTrailersSpecHelper
diff --git a/spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb b/spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb
index d29af311ee5..db0c10a802b 100644
--- a/spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb
+++ b/spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb
@@ -29,12 +29,12 @@ RSpec.describe Banzai::Filter::InlineGrafanaMetricsFilter do
)
end
- it_behaves_like 'a metrics embed filter'
-
around do |example|
travel_to(Time.utc(2019, 3, 17, 13, 10)) { example.run }
end
+ it_behaves_like 'a metrics embed filter'
+
context 'when grafana is not configured' do
before do
allow(project).to receive(:grafana_integration).and_return(nil)
diff --git a/spec/lib/banzai/filter/inline_observability_filter_spec.rb b/spec/lib/banzai/filter/inline_observability_filter_spec.rb
new file mode 100644
index 00000000000..341ada6d2b5
--- /dev/null
+++ b/spec/lib/banzai/filter/inline_observability_filter_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Banzai::Filter::InlineObservabilityFilter do
+ include FilterSpecHelper
+
+ let(:input) { %(<a href="#{url}">example</a>) }
+ let(:doc) { filter(input) }
+
+ context 'when the document has an external link' do
+ let(:url) { 'https://foo.com' }
+
+ it 'leaves regular non-observability links unchanged' do
+ expect(doc.to_s).to eq(input)
+ end
+ end
+
+ context 'when the document contains an embeddable observability link' do
+ let(:url) { 'https://observe.gitlab.com/12345' }
+
+ it 'leaves the original link unchanged' do
+ expect(doc.at_css('a').to_s).to eq(input)
+ end
+
+ it 'appends a observability charts placeholder' do
+ node = doc.at_css('.js-render-observability')
+
+ expect(node).to be_present
+ expect(node.attribute('data-frame-url').to_s).to eq(url)
+ end
+ end
+end
diff --git a/spec/lib/banzai/filter/references/reference_filter_spec.rb b/spec/lib/banzai/filter/references/reference_filter_spec.rb
index b14b9374364..6d7396ef216 100644
--- a/spec/lib/banzai/filter/references/reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/references/reference_filter_spec.rb
@@ -148,7 +148,7 @@ RSpec.describe Banzai::Filter::References::ReferenceFilter do
include_context 'document nodes'
let(:node) { Nokogiri::HTML.fragment('text @reference') }
- let(:ref_pattern) { %r{(?<!\w)@(?<user>[a-zA-Z0-9_\-\.]*)}x }
+ let(:ref_pattern) { %r{(?<!\w)@(?<user>[a-zA-Z0-9_\-.]*)}x }
context 'when node has no reference pattern' do
let(:node) { Nokogiri::HTML.fragment('random text') }
diff --git a/spec/lib/banzai/filter/references/user_reference_filter_spec.rb b/spec/lib/banzai/filter/references/user_reference_filter_spec.rb
index b153efd9655..d61b71c711d 100644
--- a/spec/lib/banzai/filter/references/user_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/references/user_reference_filter_spec.rb
@@ -42,12 +42,12 @@ RSpec.describe Banzai::Filter::References::UserReferenceFilter do
context 'mentioning @all' do
let(:reference) { User.reference_prefix + 'all' }
- it_behaves_like 'a reference containing an element node'
-
before do
project.add_developer(project.creator)
end
+ it_behaves_like 'a reference containing an element node'
+
it 'supports a special @all mention' do
project.add_developer(user)
doc = reference_filter("Hey #{reference}", author: user)
diff --git a/spec/lib/banzai/filter/repository_link_filter_spec.rb b/spec/lib/banzai/filter/repository_link_filter_spec.rb
index 4aeb6e2a722..0df680dc0c8 100644
--- a/spec/lib/banzai/filter/repository_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/repository_link_filter_spec.rb
@@ -69,7 +69,7 @@ RSpec.describe Banzai::Filter::RepositoryLinkFilter do
expect { filter(raw_doc) }.to change { Gitlab::GitalyClient.get_request_count }.by(2)
end
- shared_examples :preserve_unchanged do
+ shared_examples 'preserve unchanged' do
it 'does not modify any relative URL in anchor' do
doc = filter(link('README.md'))
expect(doc.at_css('a')['href']).to eq 'README.md'
@@ -96,25 +96,25 @@ RSpec.describe Banzai::Filter::RepositoryLinkFilter do
context 'with a wiki' do
let(:wiki) { double('ProjectWiki') }
- include_examples :preserve_unchanged
+ include_examples 'preserve unchanged'
end
context 'without a repository' do
let(:project) { create(:project) }
- include_examples :preserve_unchanged
+ include_examples 'preserve unchanged'
end
context 'with an empty repository' do
let(:project) { create(:project_empty_repo) }
- include_examples :preserve_unchanged
+ include_examples 'preserve unchanged'
end
context 'without project repository access' do
let(:project) { create(:project, :repository, repository_access_level: ProjectFeature::PRIVATE) }
- include_examples :preserve_unchanged
+ include_examples 'preserve unchanged'
end
it 'does not raise an exception on invalid URIs' do
@@ -147,7 +147,7 @@ RSpec.describe Banzai::Filter::RepositoryLinkFilter do
.to eq "/#{project_path}/-/blob/#{ref}/non/existent.file"
end
- shared_examples :valid_repository do
+ shared_examples 'valid repository' do
it 'handles Gitaly unavailable exceptions gracefully' do
allow_next_instance_of(Gitlab::GitalyClient::BlobService) do |blob_service|
allow(blob_service).to receive(:get_blob_types).and_raise(GRPC::Unavailable)
@@ -364,13 +364,13 @@ RSpec.describe Banzai::Filter::RepositoryLinkFilter do
end
context 'with a valid commit' do
- include_examples :valid_repository
+ include_examples 'valid repository'
end
context 'with a valid ref' do
# force filter to use ref instead of commit
let(:commit) { nil }
- include_examples :valid_repository
+ include_examples 'valid repository'
end
end
diff --git a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
index a409c15533b..b4be26ef8d2 100644
--- a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
+++ b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe Banzai::Filter::SyntaxHighlightFilter do
result = filter(%{<pre lang="#{lang}"><code>&lt;script&gt;alert(1)&lt;/script&gt;</code></pre>})
# `(1)` symbols are wrapped by lexer tags.
- expect(result.to_html).not_to match(%r{<script>alert.*<\/script>})
+ expect(result.to_html).not_to match(%r{<script>alert.*</script>})
# `<>` stands for lexer tags like <span ...>, not &lt;s above.
expect(result.to_html).to match(%r{alert(<.*>)?\((<.*>)?1(<.*>)?\)})
@@ -192,4 +192,8 @@ RSpec.describe Banzai::Filter::SyntaxHighlightFilter do
include_examples "XSS prevention", "ruby"
end
+
+ it_behaves_like "filter timeout" do
+ let(:text) { '<pre lang="ruby"><code>def fun end</code></pre>' }
+ end
end
diff --git a/spec/lib/banzai/filter/timeout_html_pipeline_filter_spec.rb b/spec/lib/banzai/filter/timeout_html_pipeline_filter_spec.rb
new file mode 100644
index 00000000000..95d2e54459d
--- /dev/null
+++ b/spec/lib/banzai/filter/timeout_html_pipeline_filter_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Banzai::Filter::TimeoutHtmlPipelineFilter do
+ include FilterSpecHelper
+
+ it_behaves_like 'filter timeout' do
+ let(:text) { '<p>some text</p>' }
+ end
+
+ it 'raises NotImplementedError' do
+ expect { filter('test') }.to raise_error NotImplementedError
+ end
+end
diff --git a/spec/lib/banzai/pipeline/incident_management/timeline_event_pipeline_spec.rb b/spec/lib/banzai/pipeline/incident_management/timeline_event_pipeline_spec.rb
index 4bccae04fda..8d15dbc8f2f 100644
--- a/spec/lib/banzai/pipeline/incident_management/timeline_event_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/incident_management/timeline_event_pipeline_spec.rb
@@ -78,7 +78,7 @@ RSpec.describe Banzai::Pipeline::IncidentManagement::TimelineEventPipeline do
it 'replaces existing label to a link' do
# rubocop:disable Layout/LineLength
is_expected.to match(
- %r(<p>.+<a href=\"[\w/]+-/issues\?label_name=#{label.name}\".+style=\"background-color: #\d{6}\".*>#{label.name}</span></a></span> ~unknown</p>)
+ %r(<p>.+<a href="[\w/]+-/issues\?label_name=#{label.name}".+style="background-color: #\d{6}".*>#{label.name}</span></a></span> ~unknown</p>)
)
# rubocop:enable Layout/LineLength
end
diff --git a/spec/lib/banzai/reference_parser/base_parser_spec.rb b/spec/lib/banzai/reference_parser/base_parser_spec.rb
index 9e77137795a..61751b69842 100644
--- a/spec/lib/banzai/reference_parser/base_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/base_parser_spec.rb
@@ -18,6 +18,29 @@ RSpec.describe Banzai::ReferenceParser::BaseParser do
parser_class.new(context)
end
+ describe '.reference_class' do
+ context 'when the method is not defined' do
+ it 'build the reference class' do
+ dummy = Class.new(described_class)
+ dummy.reference_type = :issue
+
+ expect(dummy.reference_class).to eq(Issue)
+ end
+ end
+
+ context 'when the method is redefined' do
+ it 'uses specified reference class' do
+ dummy = Class.new(described_class) do
+ def self.reference_class
+ AlertManagement::Alert
+ end
+ end
+
+ expect(dummy.reference_class).to eq(AlertManagement::Alert)
+ end
+ end
+ end
+
describe '.reference_type=' do
it 'sets the reference type' do
dummy = Class.new(described_class)
diff --git a/spec/lib/bitbucket_server/connection_spec.rb b/spec/lib/bitbucket_server/connection_spec.rb
index ae73955e1d1..8341ca10f43 100644
--- a/spec/lib/bitbucket_server/connection_spec.rb
+++ b/spec/lib/bitbucket_server/connection_spec.rb
@@ -56,6 +56,12 @@ RSpec.describe BitbucketServer::Connection do
expect { subject.post(url, payload) }.to raise_error(described_class::ConnectionError)
end
+
+ it 'throws an exception if the URI is invalid' do
+ stub_request(:post, url).with(headers: { 'Accept' => 'application/json' }).to_raise(URI::InvalidURIError)
+
+ expect { subject.post(url, payload) }.to raise_error(described_class::ConnectionError)
+ end
end
describe '#delete' do
diff --git a/spec/lib/bulk_imports/clients/http_spec.rb b/spec/lib/bulk_imports/clients/http_spec.rb
index 6962a943755..4fb08fc0478 100644
--- a/spec/lib/bulk_imports/clients/http_spec.rb
+++ b/spec/lib/bulk_imports/clients/http_spec.rb
@@ -9,13 +9,23 @@ RSpec.describe BulkImports::Clients::HTTP do
let(:token) { 'token' }
let(:resource) { 'resource' }
let(:version) { "#{BulkImport::MIN_MAJOR_VERSION}.0.0" }
+ let(:enterprise) { false }
let(:response_double) { double(code: 200, success?: true, parsed_response: {}) }
- let(:version_response) { double(code: 200, success?: true, parsed_response: { 'version' => version }) }
+ let(:metadata_response) do
+ double(
+ code: 200,
+ success?: true,
+ parsed_response: {
+ 'version' => version,
+ 'enterprise' => enterprise
+ }
+ )
+ end
before do
allow(Gitlab::HTTP).to receive(:get)
.with('http://gitlab.example/api/v4/version', anything)
- .and_return(version_response)
+ .and_return(metadata_response)
end
subject { described_class.new(url: url, token: token) }
@@ -213,13 +223,27 @@ RSpec.describe BulkImports::Clients::HTTP do
expect(Gitlab::HTTP).to receive(:get)
.with('http://gitlab.example/api/v4/metadata', anything)
- .and_return(version_response)
+ .and_return(metadata_response)
expect(subject.instance_version).to eq(Gitlab::VersionInfo.parse(version))
end
end
end
+ describe '#instance_enterprise' do
+ it 'returns source instance enterprise information' do
+ expect(subject.instance_enterprise).to eq(false)
+ end
+
+ context 'when enterprise information is missing' do
+ let(:enterprise) { nil }
+
+ it 'defaults to true' do
+ expect(subject.instance_enterprise).to eq(true)
+ end
+ end
+ end
+
describe '#compatible_for_project_migration?' do
context 'when instance version is lower the the expected minimum' do
it 'returns false' do
@@ -254,7 +278,7 @@ RSpec.describe BulkImports::Clients::HTTP do
before do
allow(Gitlab::HTTP).to receive(:get)
.with('http://website.example/gitlab/api/v4/version', anything)
- .and_return(version_response)
+ .and_return(metadata_response)
end
it 'performs network request to a relative gitlab url' do
diff --git a/spec/lib/bulk_imports/common/pipelines/uploads_pipeline_spec.rb b/spec/lib/bulk_imports/common/pipelines/uploads_pipeline_spec.rb
index 7a93365d098..35ca67c8a4c 100644
--- a/spec/lib/bulk_imports/common/pipelines/uploads_pipeline_spec.rb
+++ b/spec/lib/bulk_imports/common/pipelines/uploads_pipeline_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BulkImports::Common::Pipelines::UploadsPipeline do
+RSpec.describe BulkImports::Common::Pipelines::UploadsPipeline, feature_category: :import do
let_it_be(:project) { create(:project) }
let_it_be(:group) { create(:group) }
@@ -103,7 +103,9 @@ RSpec.describe BulkImports::Common::Pipelines::UploadsPipeline do
context 'when dynamic path is nil' do
it 'returns' do
- expect { pipeline.load(context, File.join(tmpdir, 'test')) }.not_to change { portable.uploads.count }
+ path = File.join(tmpdir, 'test')
+ FileUtils.touch(path)
+ expect { pipeline.load(context, path) }.not_to change { portable.uploads.count }
end
end
@@ -122,6 +124,20 @@ RSpec.describe BulkImports::Common::Pipelines::UploadsPipeline do
expect { pipeline.load(context, symlink) }.not_to change { portable.uploads.count }
end
end
+
+ context 'when path traverses' do
+ it 'does not upload the file' do
+ path_traversal = "#{uploads_dir_path}/avatar/../../../../etc/passwd"
+ expect { pipeline.load(context, path_traversal) }.to not_change { portable.uploads.count }.and raise_error(Gitlab::Utils::PathTraversalAttackError)
+ end
+ end
+
+ context 'when path is outside the tmpdir' do
+ it 'does not upload the file' do
+ path = "/etc/passwd"
+ expect { pipeline.load(context, path) }.to not_change { portable.uploads.count }.and raise_error(StandardError, /not allowed/)
+ end
+ end
end
describe '#after_run' do
diff --git a/spec/lib/bulk_imports/projects/pipelines/issues_pipeline_spec.rb b/spec/lib/bulk_imports/projects/pipelines/issues_pipeline_spec.rb
index 97fcddefd42..19e3a1fecc3 100644
--- a/spec/lib/bulk_imports/projects/pipelines/issues_pipeline_spec.rb
+++ b/spec/lib/bulk_imports/projects/pipelines/issues_pipeline_spec.rb
@@ -89,6 +89,7 @@ RSpec.describe BulkImports::Projects::Pipelines::IssuesPipeline do
expect(award_emoji.user).to eq(user)
end
end
+
context 'issue state' do
let(:issue_attributes) { { 'state' => 'closed' } }
diff --git a/spec/lib/extracts_ref_spec.rb b/spec/lib/extracts_ref_spec.rb
index 3e9a7499fdd..ca8af9413f3 100644
--- a/spec/lib/extracts_ref_spec.rb
+++ b/spec/lib/extracts_ref_spec.rb
@@ -44,6 +44,19 @@ RSpec.describe ExtractsRef do
expect { assign_ref_vars }.not_to raise_error
end
end
+
+ context 'when a ref_type parameter is provided' do
+ let(:params) { ActionController::Parameters.new(path: path, ref: ref, ref_type: 'tags') }
+
+ context 'and the use_ref_type_parameter feature flag is enabled' do
+ it 'sets a fully_qualified_ref variable' do
+ fully_qualified_ref = "refs/tags/#{ref}"
+ expect(container.repository).to receive(:commit).with(fully_qualified_ref)
+ assign_ref_vars
+ expect(@fully_qualified_ref).to eq(fully_qualified_ref)
+ end
+ end
+ end
end
it_behaves_like 'extracts refs'
diff --git a/spec/lib/feature/definition_spec.rb b/spec/lib/feature/definition_spec.rb
index 3d11ad4c0d8..595725d357c 100644
--- a/spec/lib/feature/definition_spec.rb
+++ b/spec/lib/feature/definition_spec.rb
@@ -25,6 +25,9 @@ RSpec.describe Feature::Definition do
using RSpec::Parameterized::TableSyntax
where(:param, :value, :result) do
+ :name | 'colon:separated' | /Feature flag 'colon:separated' is invalid/
+ :name | 'space separated' | /Feature flag 'space separated' is invalid/
+ :name | 'ALL_CAPS' | /Feature flag 'ALL_CAPS' is invalid/
:name | nil | /Feature flag is missing name/
:path | nil | /Feature flag 'feature_flag' is missing path/
:type | nil | /Feature flag 'feature_flag' is missing type/
@@ -111,6 +114,11 @@ RSpec.describe Feature::Definition do
subject { described_class.send(:load_all!) }
+ after do
+ FileUtils.rm_rf(store1)
+ FileUtils.rm_rf(store2)
+ end
+
it "when there's no feature flags a list of definitions is empty" do
is_expected.to be_empty
end
@@ -136,11 +144,6 @@ RSpec.describe Feature::Definition do
.to raise_error(/Feature flag is missing name/)
end
- after do
- FileUtils.rm_rf(store1)
- FileUtils.rm_rf(store2)
- end
-
def write_feature_flag(store, path, content)
path = File.join(store, path)
dir = File.dirname(path)
diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb
index ad324406450..c087931d36a 100644
--- a/spec/lib/feature_spec.rb
+++ b/spec/lib/feature_spec.rb
@@ -162,6 +162,13 @@ RSpec.describe Feature, stub_feature_flags: false do
stub_feature_flag_definition(:enabled_feature_flag, default_enabled: true)
end
+ context 'when using redis cache', :use_clean_rails_redis_caching do
+ it 'does not make recursive feature-flag calls' do
+ expect(described_class).to receive(:enabled?).once.and_call_original
+ described_class.enabled?(:disabled_feature_flag)
+ end
+ end
+
context 'when self-recursive' do
before do
allow(Feature).to receive(:with_feature).and_wrap_original do |original, name, &block|
@@ -318,6 +325,31 @@ RSpec.describe Feature, stub_feature_flags: false do
end
end
+ context 'with a group member' do
+ let(:key) { :awesome_feature }
+ let(:guinea_pigs) { create_list(:user, 3) }
+
+ before do
+ described_class.reset
+ stub_feature_flag_definition(key)
+ Flipper.unregister_groups
+ Flipper.register(:guinea_pigs) do |actor|
+ guinea_pigs.include?(actor.thing)
+ end
+ described_class.enable(key, described_class.group(:guinea_pigs))
+ end
+
+ it 'is true for all group members' do
+ expect(described_class.enabled?(key, guinea_pigs[0])).to be_truthy
+ expect(described_class.enabled?(key, guinea_pigs[1])).to be_truthy
+ expect(described_class.enabled?(key, guinea_pigs[2])).to be_truthy
+ end
+
+ it 'is false for any other actor' do
+ expect(described_class.enabled?(key, create(:user))).to be_falsey
+ end
+ end
+
context 'with an individual actor' do
let(:actor) { stub_feature_flag_gate('CustomActor:5') }
let(:another_actor) { stub_feature_flag_gate('CustomActor:10') }
@@ -495,7 +527,7 @@ RSpec.describe Feature, stub_feature_flags: false do
let(:expected_extra) {}
it 'logs the event' do
- expect(Feature.logger).to receive(:info).with(key: key, action: expected_action, **expected_extra)
+ expect(Feature.logger).to receive(:info).at_least(:once).with(key: key, action: expected_action, **expected_extra)
subject
end
@@ -518,10 +550,10 @@ RSpec.describe Feature, stub_feature_flags: false do
end
context 'when thing is an actor' do
- let(:thing) { create(:project) }
+ let(:thing) { create(:user) }
it_behaves_like 'logging' do
- let(:expected_action) { :enable }
+ let(:expected_action) { eq(:enable) | eq(:remove_opt_out) }
let(:expected_extra) { { "extra.thing" => thing.flipper_id.to_s } }
end
end
@@ -544,12 +576,160 @@ RSpec.describe Feature, stub_feature_flags: false do
end
context 'when thing is an actor' do
- let(:thing) { create(:project) }
+ let(:thing) { create(:user) }
+ let(:flag_opts) { {} }
it_behaves_like 'logging' do
let(:expected_action) { :disable }
let(:expected_extra) { { "extra.thing" => thing.flipper_id.to_s } }
end
+
+ before do
+ stub_feature_flag_definition(key, flag_opts)
+ end
+
+ context 'when the feature flag was enabled for this actor' do
+ before do
+ described_class.enable(key, thing)
+ end
+
+ it 'marks this thing as disabled' do
+ expect { subject }.to change { thing_enabled? }.from(true).to(false)
+ end
+
+ it 'does not change the global value' do
+ expect { subject }.not_to change { described_class.enabled?(key) }.from(false)
+ end
+
+ it 'is possible to re-enable the feature' do
+ subject
+
+ expect { described_class.enable(key, thing) }
+ .to change { thing_enabled? }.from(false).to(true)
+ end
+ end
+
+ context 'when the feature flag is enabled globally' do
+ before do
+ described_class.enable(key)
+ end
+
+ it 'does not mark this thing as disabled' do
+ expect { subject }.not_to change { thing_enabled? }.from(true)
+ end
+
+ it 'does not change the global value' do
+ expect { subject }.not_to change { described_class.enabled?(key) }.from(true)
+ end
+ end
+ end
+ end
+
+ describe 'opt_out' do
+ subject { described_class.opt_out(key, thing) }
+
+ let(:key) { :awesome_feature }
+
+ before do
+ stub_feature_flag_definition(key)
+ described_class.enable(key)
+ end
+
+ context 'when thing is an actor' do
+ let_it_be(:thing) { create(:project) }
+
+ it 'marks this thing as disabled' do
+ expect { subject }.to change { thing_enabled? }.from(true).to(false)
+ end
+
+ it 'does not change the global value' do
+ expect { subject }.not_to change { described_class.enabled?(key) }.from(true)
+ end
+
+ it_behaves_like 'logging' do
+ let(:expected_action) { eq(:opt_out) }
+ let(:expected_extra) { { "extra.thing" => thing.flipper_id.to_s } }
+ end
+
+ it 'stores the opt-out information as a gate' do
+ subject
+
+ flag = described_class.get(key)
+
+ expect(flag.actors_value).to include(include(thing.flipper_id))
+ expect(flag.actors_value).not_to include(thing.flipper_id)
+ end
+ end
+
+ context 'when thing is a group' do
+ let(:thing) { Feature.group(:guinea_pigs) }
+ let(:guinea_pigs) { create_list(:user, 3) }
+
+ before do
+ Feature.reset
+ Flipper.unregister_groups
+ Flipper.register(:guinea_pigs) do |actor|
+ guinea_pigs.include?(actor.thing)
+ end
+ end
+
+ it 'has no effect' do
+ expect { subject }.not_to change { described_class.enabled?(key, guinea_pigs.first) }.from(true)
+ end
+ end
+ end
+
+ describe 'remove_opt_out' do
+ subject { described_class.remove_opt_out(key, thing) }
+
+ let(:key) { :awesome_feature }
+
+ before do
+ stub_feature_flag_definition(key)
+ described_class.enable(key)
+ described_class.opt_out(key, thing)
+ end
+
+ context 'when thing is an actor' do
+ let_it_be(:thing) { create(:project) }
+
+ it 're-enables this thing' do
+ expect { subject }.to change { thing_enabled? }.from(false).to(true)
+ end
+
+ it 'does not change the global value' do
+ expect { subject }.not_to change { described_class.enabled?(key) }.from(true)
+ end
+
+ it_behaves_like 'logging' do
+ let(:expected_action) { eq(:remove_opt_out) }
+ let(:expected_extra) { { "extra.thing" => thing.flipper_id.to_s } }
+ end
+
+ it 'removes the opt-out information' do
+ subject
+
+ flag = described_class.get(key)
+
+ expect(flag.actors_value).to be_empty
+ end
+ end
+
+ context 'when thing is a group' do
+ let(:thing) { Feature.group(:guinea_pigs) }
+ let(:guinea_pigs) { create_list(:user, 3) }
+
+ before do
+ Feature.reset
+ Flipper.unregister_groups
+ Flipper.register(:guinea_pigs) do |actor|
+ guinea_pigs.include?(actor.thing)
+ end
+ end
+
+ it 'has no effect' do
+ expect { subject }.not_to change { described_class.enabled?(key, guinea_pigs.first) }.from(true)
+ end
end
end
@@ -563,6 +743,16 @@ RSpec.describe Feature, stub_feature_flags: false do
let(:expected_action) { :enable_percentage_of_time }
let(:expected_extra) { { "extra.percentage" => percentage.to_s } }
end
+
+ context 'when the flag is on' do
+ before do
+ described_class.enable(key)
+ end
+
+ it 'fails with InvalidOperation' do
+ expect { subject }.to raise_error(described_class::InvalidOperation)
+ end
+ end
end
describe '.disable_percentage_of_time' do
@@ -586,6 +776,16 @@ RSpec.describe Feature, stub_feature_flags: false do
let(:expected_action) { :enable_percentage_of_actors }
let(:expected_extra) { { "extra.percentage" => percentage.to_s } }
end
+
+ context 'when the flag is on' do
+ before do
+ described_class.enable(key)
+ end
+
+ it 'fails with InvalidOperation' do
+ expect { subject }.to raise_error(described_class::InvalidOperation)
+ end
+ end
end
describe '.disable_percentage_of_actors' do
@@ -603,6 +803,7 @@ RSpec.describe Feature, stub_feature_flags: false do
subject { described_class.remove(key) }
let(:key) { :awesome_feature }
+ let(:actor) { create(:user) }
before do
described_class.enable(key)
@@ -617,13 +818,10 @@ RSpec.describe Feature, stub_feature_flags: false do
it 'returns nil' do
expect(described_class.remove(:non_persisted_feature_flag)).to be_nil
end
- end
- context 'for a persisted feature' do
- it 'returns true' do
- described_class.enable(:persisted_feature_flag)
-
- expect(described_class.remove(:persisted_feature_flag)).to be_truthy
+ it 'returns true, and cleans up' do
+ expect(subject).to be_truthy
+ expect(described_class.persisted_names).not_to include(key)
end
end
end
@@ -712,6 +910,10 @@ RSpec.describe Feature, stub_feature_flags: false do
end
end
+ before do
+ stub_feature_flag_definition(:enabled_feature_flag)
+ end
+
it 'gives the correct value when enabling for an additional actor' do
described_class.enable(:enabled_feature_flag, actor)
initial_gate_values = active_record_adapter.get(described_class.get(:enabled_feature_flag))
@@ -834,4 +1036,8 @@ RSpec.describe Feature, stub_feature_flags: false do
end
end
end
+
+ def thing_enabled?
+ described_class.enabled?(key, thing)
+ end
end
diff --git a/spec/lib/generators/model/mocks/migration_file.txt b/spec/lib/generators/model/mocks/migration_file.txt
index e92c2d2b534..091e086ba65 100644
--- a/spec/lib/generators/model/mocks/migration_file.txt
+++ b/spec/lib/generators/model/mocks/migration_file.txt
@@ -3,7 +3,7 @@
# See https://docs.gitlab.com/ee/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
-class CreateModelGeneratorTestFoos < Gitlab::Database::Migration[2.0]
+class CreateModelGeneratorTestFoos < Gitlab::Database::Migration[2.1]
# When using the methods "add_concurrent_index" or "remove_concurrent_index"
# you must disable the use of transactions
# as these methods can not run in an existing transaction.
diff --git a/spec/lib/gitlab/analytics/cycle_analytics/base_query_builder_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/base_query_builder_spec.rb
index 24f8fb40445..271022e7c55 100644
--- a/spec/lib/gitlab/analytics/cycle_analytics/base_query_builder_spec.rb
+++ b/spec/lib/gitlab/analytics/cycle_analytics/base_query_builder_spec.rb
@@ -22,10 +22,7 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::BaseQueryBuilder do
project.add_maintainer(user)
mr1.metrics.update!(merged_at: 1.month.ago)
mr2.metrics.update!(merged_at: Time.now)
- end
-
- around do |example|
- Timecop.freeze { example.run }
+ freeze_time
end
describe 'date range parameters' do
diff --git a/spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb
index 258f4a0d019..4db5d64164e 100644
--- a/spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb
+++ b/spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb
@@ -18,10 +18,6 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Median do
subject { described_class.new(stage: stage, query: query).seconds }
- around do |example|
- Timecop.freeze { example.run }
- end
-
it 'retruns nil when no results' do
expect(subject).to eq(nil)
end
@@ -30,11 +26,11 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Median do
merge_request1 = create(:merge_request, source_branch: '1', target_project: project, source_project: project)
merge_request2 = create(:merge_request, source_branch: '2', target_project: project, source_project: project)
- travel_to(5.minutes.from_now) do
+ travel(5.minutes) do
merge_request1.metrics.update!(merged_at: Time.zone.now)
end
- travel_to(10.minutes.from_now) do
+ travel(10.minutes) do
merge_request2.metrics.update!(merged_at: Time.zone.now)
end
diff --git a/spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb
index 34d5158a5ab..7f70a4cfc4e 100644
--- a/spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb
+++ b/spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
RSpec.describe Gitlab::Analytics::CycleAnalytics::RecordsFetcher do
- around do |example|
- Timecop.freeze { example.run }
+ before_all do
+ freeze_time
end
let(:params) { { from: 1.year.ago, current_user: user } }
diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events_spec.rb
new file mode 100644
index 00000000000..f5a8035b9b4
--- /dev/null
+++ b/spec/lib/gitlab/analytics/cycle_analytics/stage_events_spec.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Analytics::CycleAnalytics::StageEvents, feature_category: :value_stream_management do
+ describe '#selectable_events' do
+ subject(:selectable_events) { described_class.selectable_events }
+
+ it 'excludes internal events' do
+ expect(selectable_events).to include(Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestCreated)
+ expect(selectable_events).to exclude(Gitlab::Analytics::CycleAnalytics::StageEvents::IssueStageEnd)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/api_authentication/sent_through_builder_spec.rb b/spec/lib/gitlab/api_authentication/sent_through_builder_spec.rb
index 845e317f3aa..aa088a5d6d5 100644
--- a/spec/lib/gitlab/api_authentication/sent_through_builder_spec.rb
+++ b/spec/lib/gitlab/api_authentication/sent_through_builder_spec.rb
@@ -8,10 +8,10 @@ RSpec.describe Gitlab::APIAuthentication::SentThroughBuilder do
let(:locators) { Array.new(3) { double } }
it 'adds a strategy for each of locators x resolvers' do
- strategies = locators.to_h { |l| [l, []] }
+ strategies = locators.index_with { [] }
described_class.new(strategies, resolvers).sent_through(*locators)
- expect(strategies).to eq(locators.to_h { |l| [l, resolvers] })
+ expect(strategies).to eq(locators.index_with { resolvers })
end
end
end
diff --git a/spec/lib/gitlab/application_context_spec.rb b/spec/lib/gitlab/application_context_spec.rb
index 58d462aa27f..20c1536b9e6 100644
--- a/spec/lib/gitlab/application_context_spec.rb
+++ b/spec/lib/gitlab/application_context_spec.rb
@@ -141,7 +141,8 @@ RSpec.describe Gitlab::ApplicationContext do
describe 'setting the client' do
let_it_be(:remote_ip) { '127.0.0.1' }
let_it_be(:runner) { create(:ci_runner) }
- let_it_be(:options) { { remote_ip: remote_ip, runner: runner, user: user } }
+ let_it_be(:job) { create(:ci_build, :pending, :queued, user: user, project: project) }
+ let_it_be(:options) { { remote_ip: remote_ip, runner: runner, user: user, job: job } }
using RSpec::Parameterized::TableSyntax
@@ -150,6 +151,7 @@ RSpec.describe Gitlab::ApplicationContext do
[:remote_ip, :runner] | :runner
[:remote_ip, :runner, :user] | :runner
[:remote_ip, :user] | :user
+ [:job] | :user
end
with_them do
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index d2eb9209f42..3d9076c6fa7 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -697,7 +697,7 @@ module Gitlab
end
end
- shared_examples :invalid_include do
+ shared_examples 'invalid include' do
let(:include_path) { 'dk.png' }
before do
@@ -716,7 +716,7 @@ module Gitlab
context 'with path to a binary file' do
let(:blob) { fake_blob(path: 'dk.png', binary: true) }
- include_examples :invalid_include
+ include_examples 'invalid include'
end
context 'with path to file in external storage' do
@@ -727,7 +727,7 @@ module Gitlab
project.update_attribute(:lfs_enabled, true)
end
- include_examples :invalid_include
+ include_examples 'invalid include'
end
context 'with path to a textual file' do
@@ -737,7 +737,7 @@ module Gitlab
create_file(file_path, "Content from #{include_path}")
end
- shared_examples :valid_include do
+ shared_examples 'valid include' do
[
['/doc/sample.adoc', 'doc/sample.adoc', 'absolute path'],
['sample.adoc', 'doc/api/sample.adoc', 'relative path'],
@@ -760,24 +760,24 @@ module Gitlab
context 'when requested path is a file in the repo' do
let(:requested_path) { 'doc/api/README.adoc' }
- include_examples :valid_include
+ include_examples 'valid include'
context 'without a commit (only ref)' do
let(:commit) { nil }
- include_examples :valid_include
+ include_examples 'valid include'
end
end
context 'when requested path is a directory in the repo' do
let(:requested_path) { 'doc/api/' }
- include_examples :valid_include
+ include_examples 'valid include'
context 'without a commit (only ref)' do
let(:commit) { nil }
- include_examples :valid_include
+ include_examples 'valid include'
end
end
end
diff --git a/spec/lib/gitlab/audit/auditor_spec.rb b/spec/lib/gitlab/audit/auditor_spec.rb
index f743515e616..4b16333d913 100644
--- a/spec/lib/gitlab/audit/auditor_spec.rb
+++ b/spec/lib/gitlab/audit/auditor_spec.rb
@@ -68,6 +68,7 @@ RSpec.describe Gitlab::Audit::Auditor do
expect(logger).to have_received(:info).with(
hash_including(
+ 'id' => AuditEvent.last.id,
'author_id' => author.id,
'author_name' => author.name,
'entity_id' => group.id,
@@ -112,6 +113,7 @@ RSpec.describe Gitlab::Audit::Auditor do
expect(logger).to have_received(:info).with(
hash_including(
+ 'id' => AuditEvent.last.id,
'author_id' => author.id,
'author_name' => author.name,
'entity_id' => group.id,
@@ -244,7 +246,9 @@ RSpec.describe Gitlab::Audit::Auditor do
let(:audit!) { auditor.audit(context) }
before do
- allow(AuditEvent).to receive(:bulk_insert!).and_raise(ActiveRecord::RecordInvalid)
+ expect_next_instance_of(AuditEvent) do |instance|
+ allow(instance).to receive(:save!).and_raise(ActiveRecord::RecordInvalid)
+ end
allow(Gitlab::ErrorTracking).to receive(:track_exception)
end
@@ -261,5 +265,27 @@ RSpec.describe Gitlab::Audit::Auditor do
expect { auditor.audit(context) }.not_to raise_exception
end
end
+
+ context 'when audit event is not saved in database due to some database infra issue' do
+ let(:audit!) { auditor.audit(context) }
+
+ before do
+ allow_any_instance_of(auditor) do |auditor_instance|
+ allow(auditor_instance).to receive(:log_to_database).and_return(nil)
+ end
+ end
+
+ it 'calls log_to_file_and_stream with in memory events' do
+ audit!
+
+ expect_any_instance_of(auditor) do |auditor_instance|
+ expect(auditor_instance).to receive(:log_to_file_and_stream).with(include(kind_of(AuditEvent)))
+ end
+ end
+
+ it 'does not throw exception' do
+ expect { auditor.audit(context) }.not_to raise_exception
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/audit/type/definition_spec.rb b/spec/lib/gitlab/audit/type/definition_spec.rb
index 9f4282a4ec0..d1d6b0d7a78 100644
--- a/spec/lib/gitlab/audit/type/definition_spec.rb
+++ b/spec/lib/gitlab/audit/type/definition_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe Gitlab::Audit::Type::Definition do
description: 'Group deploy token is deleted',
introduced_by_issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/1',
introduced_by_mr: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1',
- group: 'govern::compliance',
+ feature_category: 'continuous_delivery',
milestone: '15.4',
saved_to_database: true,
streamed: true }
@@ -18,6 +18,12 @@ RSpec.describe Gitlab::Audit::Type::Definition do
let(:definition) { described_class.new(path, attributes) }
let(:yaml_content) { attributes.deep_stringify_keys.to_yaml }
+ around do |example|
+ described_class.clear_memoization(:definitions)
+ example.run
+ described_class.clear_memoization(:definitions)
+ end
+
describe '#key' do
subject { definition.key }
@@ -36,7 +42,7 @@ RSpec.describe Gitlab::Audit::Type::Definition do
:description | nil | %r{property '/description' is not of type: string}
:introduced_by_issue | nil | %r{property '/introduced_by_issue' is not of type: string}
:introduced_by_mr | nil | %r{property '/introduced_by_mr' is not of type: string}
- :group | nil | %r{property '/group' is not of type: string}
+ :feature_category | nil | %r{property '/feature_category' is not of type: string}
:milestone | nil | %r{property '/milestone' is not of type: string}
end
# rubocop:enable Layout/LineLength
@@ -103,7 +109,7 @@ RSpec.describe Gitlab::Audit::Type::Definition do
expect(audit_event_type_definition.name).to eq "group_deploy_token_destroyed"
expect(audit_event_type_definition.description).to eq "Group deploy token is deleted"
- expect(audit_event_type_definition.group).to eq "govern::compliance"
+ expect(audit_event_type_definition.feature_category).to eq "continuous_delivery"
expect(audit_event_type_definition.milestone).to eq "15.4"
expect(audit_event_type_definition.saved_to_database).to be true
expect(audit_event_type_definition.streamed).to be true
@@ -111,6 +117,65 @@ RSpec.describe Gitlab::Audit::Type::Definition do
end
end
+ describe '.event_names' do
+ before do
+ allow(described_class).to receive(:definitions) do
+ { definition.key => definition }
+ end
+ end
+
+ it 'returns names of event types as string array' do
+ expect(described_class.event_names).to match_array([definition.attributes[:name]])
+ end
+ end
+
+ describe '.defined?' do
+ before do
+ allow(described_class).to receive(:definitions) do
+ { definition.key => definition }
+ end
+ end
+
+ it 'returns true if definition for the event name exists' do
+ expect(described_class.defined?('group_deploy_token_destroyed')).to be_truthy
+ end
+
+ it 'returns false if definition for the event name exists' do
+ expect(described_class.defined?('random_event_name')).to be_falsey
+ end
+ end
+
+ describe '.stream_only?' do
+ let(:stream_only_event_attributes) do
+ { name: 'policy_project_updated',
+ description: 'This event is triggered whenever the security policy project is updated for a project',
+ introduced_by_issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/2',
+ introduced_by_mr: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/2',
+ feature_category: 'security_policy_management',
+ milestone: '15.6',
+ saved_to_database: false,
+ streamed: true }
+ end
+
+ let(:stream_only_event_path) { File.join('types', 'policy_project_updated.yml') }
+ let(:stream_only_event_definition) { described_class.new(stream_only_event_path, stream_only_event_attributes) }
+
+ before do
+ allow(described_class).to receive(:definitions) do
+ { definition.key => definition,
+ stream_only_event_definition.key => stream_only_event_definition }
+ end
+ end
+
+ it 'returns true for a stream only event' do
+ expect(described_class.stream_only?('group_deploy_token_destroyed')).to be_falsey
+ end
+
+ it 'returns false for an event that is saved to database' do
+ expect(described_class.stream_only?('policy_project_updated')).to be_truthy
+ end
+ end
+
describe '.load_from_file' do
it 'properly loads a definition from file' do
expect_file_read(path, content: yaml_content)
@@ -186,6 +251,12 @@ RSpec.describe Gitlab::Audit::Type::Definition do
end
end
+ describe 'validate that all the YAML definitions matches the audit event type schema' do
+ it 'successfully loads all the YAML definitions' do
+ expect { described_class.definitions }.not_to raise_error
+ end
+ end
+
describe '.definitions' do
let(:store1) { Dir.mktmpdir('path1') }
diff --git a/spec/lib/gitlab/auth/auth_finders_spec.rb b/spec/lib/gitlab/auth/auth_finders_spec.rb
index 9283c31a207..484b4702497 100644
--- a/spec/lib/gitlab/auth/auth_finders_spec.rb
+++ b/spec/lib/gitlab/auth/auth_finders_spec.rb
@@ -69,7 +69,7 @@ RSpec.describe Gitlab::Auth::AuthFinders do
expect(subject).to eq(user)
expect(@current_authenticated_job).to eq job
expect(subject).to be_from_ci_job_token
- expect(subject.ci_job_token_scope.source_project).to eq(job.project)
+ expect(subject.ci_job_token_scope.current_project).to eq(job.project)
end
end
@@ -100,7 +100,7 @@ RSpec.describe Gitlab::Auth::AuthFinders do
expect(subject).to eq(user)
expect(@current_authenticated_job).to eq job
expect(subject).to be_from_ci_job_token
- expect(subject.ci_job_token_scope.source_project).to eq(job.project)
+ expect(subject.ci_job_token_scope.current_project).to eq(job.project)
end
else
it 'returns nil' do
diff --git a/spec/lib/gitlab/auth/current_user_mode_spec.rb b/spec/lib/gitlab/auth/current_user_mode_spec.rb
index a21f0931b78..0a68a4a0ae2 100644
--- a/spec/lib/gitlab/auth/current_user_mode_spec.rb
+++ b/spec/lib/gitlab/auth/current_user_mode_spec.rb
@@ -194,10 +194,41 @@ RSpec.describe Gitlab::Auth::CurrentUserMode, :request_store do
it 'creates a timestamp in the session' do
subject.request_admin_mode!
+
subject.enable_admin_mode!(password: user.password)
expect(session).to include(expected_session_entry(be_within(1.second).of(Time.now)))
end
+
+ it 'returns true after successful enable' do
+ subject.request_admin_mode!
+
+ expect(subject.enable_admin_mode!(password: user.password)).to eq(true)
+ end
+
+ it 'returns false after unsuccessful enable' do
+ subject.request_admin_mode!
+
+ expect(subject.enable_admin_mode!(password: 'wrong password')).to eq(false)
+ end
+
+ context 'when user is not an admin' do
+ let(:user) { build_stubbed(:user) }
+
+ it 'returns false' do
+ subject.request_admin_mode!
+
+ expect(subject.enable_admin_mode!(password: user.password)).to eq(false)
+ end
+ end
+
+ context 'when admin mode is not requested' do
+ it 'raises error' do
+ expect do
+ subject.enable_admin_mode!(password: user.password)
+ end.to raise_error(Gitlab::Auth::CurrentUserMode::NotRequestedError)
+ end
+ end
end
describe '#disable_admin_mode!' do
diff --git a/spec/lib/gitlab/auth/ldap/config_spec.rb b/spec/lib/gitlab/auth/ldap/config_spec.rb
index 3be983857bc..160fd78b2b9 100644
--- a/spec/lib/gitlab/auth/ldap/config_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/config_spec.rb
@@ -99,7 +99,7 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
expect { described_class.new }.to raise_error ArgumentError
end
- it 'works' do
+ it 'returns an instance of Gitlab::Auth::Ldap::Config' do
expect(config).to be_a described_class
end
@@ -122,7 +122,8 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
host: 'ldap.example.com',
port: 386,
hosts: nil,
- encryption: nil
+ encryption: nil,
+ instrumentation_service: ActiveSupport::Notifications
)
end
diff --git a/spec/lib/gitlab/auth/ldap/user_spec.rb b/spec/lib/gitlab/auth/ldap/user_spec.rb
index b471a89b491..5771b1cd609 100644
--- a/spec/lib/gitlab/auth/ldap/user_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/user_spec.rb
@@ -133,7 +133,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do
context 'when user confirmation email is enabled' do
before do
- stub_application_setting send_user_confirmation_email: true
+ stub_application_setting_enum('email_confirmation_setting', 'hard')
end
it 'creates and confirms the user anyway' do
diff --git a/spec/lib/gitlab/auth/o_auth/user_spec.rb b/spec/lib/gitlab/auth/o_auth/user_spec.rb
index 95a518afcf1..bb81621ec92 100644
--- a/spec/lib/gitlab/auth/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/auth/o_auth/user_spec.rb
@@ -108,7 +108,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
context 'when user confirmation email is enabled' do
before do
- stub_application_setting send_user_confirmation_email: true
+ stub_application_setting_enum('email_confirmation_setting', 'hard')
end
it 'creates and confirms the user anyway' do
diff --git a/spec/lib/gitlab/auth/saml/user_spec.rb b/spec/lib/gitlab/auth/saml/user_spec.rb
index 796512bc52b..a8a5d8ae5df 100644
--- a/spec/lib/gitlab/auth/saml/user_spec.rb
+++ b/spec/lib/gitlab/auth/saml/user_spec.rb
@@ -46,6 +46,10 @@ RSpec.describe Gitlab::Auth::Saml::User do
end
context 'external groups' do
+ before do
+ stub_saml_group_config(%w(Interns))
+ end
+
context 'are defined' do
it 'marks the user as external' do
stub_saml_group_config(%w(Freelancers))
@@ -55,10 +59,6 @@ RSpec.describe Gitlab::Auth::Saml::User do
end
end
- before do
- stub_saml_group_config(%w(Interns))
- end
-
context 'are defined but the user does not belong there' do
it 'does not mark the user as external' do
saml_user.save # rubocop:disable Rails/SaveBang
@@ -317,7 +317,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
context 'when user confirmation email is enabled' do
before do
- stub_application_setting send_user_confirmation_email: true
+ stub_application_setting_enum('email_confirmation_setting', 'hard')
end
it 'creates and confirms the user anyway' do
diff --git a/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb b/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb
index b239de841b6..84f6411eae6 100644
--- a/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb
+++ b/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb
@@ -22,14 +22,14 @@ RSpec.describe Gitlab::Auth::UniqueIpsLimiter, :clean_gitlab_redis_shared_state
end
it 'resets count after specified time window' do
- Timecop.freeze do
+ freeze_time do
expect(described_class.update_and_return_ips_count(user.id, 'ip2')).to eq(1)
expect(described_class.update_and_return_ips_count(user.id, 'ip3')).to eq(2)
+ end
- travel_to(Time.now.utc + described_class.config.unique_ips_limit_time_window) do
- expect(described_class.update_and_return_ips_count(user.id, 'ip4')).to eq(1)
- expect(described_class.update_and_return_ips_count(user.id, 'ip5')).to eq(2)
- end
+ travel_to(Time.now.utc + described_class.config.unique_ips_limit_time_window) do
+ expect(described_class.update_and_return_ips_count(user.id, 'ip4')).to eq(1)
+ expect(described_class.update_and_return_ips_count(user.id, 'ip5')).to eq(2)
end
end
end
diff --git a/spec/lib/gitlab/background_migration/backfill_environment_tiers_spec.rb b/spec/lib/gitlab/background_migration/backfill_environment_tiers_spec.rb
new file mode 100644
index 00000000000..788ed40b61e
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_environment_tiers_spec.rb
@@ -0,0 +1,119 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BackfillEnvironmentTiers,
+ :migration, schema: 20221205151917, feature_category: :continuous_delivery do
+ let!(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
+ let!(:project) { table(:projects).create!(namespace_id: namespace.id, project_namespace_id: namespace.id) }
+
+ let(:migration) do
+ described_class.new(start_id: 1, end_id: 1000,
+ batch_table: :environments, batch_column: :id,
+ sub_batch_size: 10, pause_ms: 0,
+ connection: ApplicationRecord.connection)
+ end
+
+ describe '#perform' do
+ let!(:production) { table(:environments).create!(name: 'production', slug: 'production', project_id: project.id) }
+ let!(:staging) { table(:environments).create!(name: 'staging', slug: 'staging', project_id: project.id) }
+ let!(:testing) { table(:environments).create!(name: 'testing', slug: 'testing', project_id: project.id) }
+
+ let!(:development) do
+ table(:environments).create!(name: 'development', slug: 'development', project_id: project.id)
+ end
+
+ let!(:other) { table(:environments).create!(name: 'other', slug: 'other', project_id: project.id) }
+
+ it 'backfill tiers for all environments in range' do
+ expect(production.tier).to be_nil
+ expect(staging.tier).to be_nil
+ expect(testing.tier).to be_nil
+ expect(development.tier).to be_nil
+ expect(other.tier).to be_nil
+
+ migration.perform
+
+ expect(production.reload.tier).to eq(described_class::PRODUCTION_TIER)
+ expect(staging.reload.tier).to eq(described_class::STAGING_TIER)
+ expect(testing.reload.tier).to eq(described_class::TESTING_TIER)
+ expect(development.reload.tier).to eq(described_class::DEVELOPMENT_TIER)
+ expect(other.reload.tier).to eq(described_class::OTHER_TIER)
+ end
+ end
+
+ # Equivalent to spec/models/environment_spec.rb#guess_tier
+ describe 'same behavior with guess tier' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:environment) { table(:environments).create!(name: name, slug: name, project_id: project.id) }
+
+ where(:name, :tier) do
+ 'review/feature' | described_class::DEVELOPMENT_TIER
+ 'review/product' | described_class::DEVELOPMENT_TIER
+ 'DEV' | described_class::DEVELOPMENT_TIER
+ 'development' | described_class::DEVELOPMENT_TIER
+ 'trunk' | described_class::DEVELOPMENT_TIER
+ 'dev' | described_class::DEVELOPMENT_TIER
+ 'review/app' | described_class::DEVELOPMENT_TIER
+ 'PRODUCTION' | described_class::PRODUCTION_TIER
+ 'prod' | described_class::PRODUCTION_TIER
+ 'prod-east-2' | described_class::PRODUCTION_TIER
+ 'us-prod-east' | described_class::PRODUCTION_TIER
+ 'fe-production' | described_class::PRODUCTION_TIER
+ 'test' | described_class::TESTING_TIER
+ 'TEST' | described_class::TESTING_TIER
+ 'testing' | described_class::TESTING_TIER
+ 'testing-prd' | described_class::TESTING_TIER
+ 'acceptance-testing' | described_class::TESTING_TIER
+ 'production-test' | described_class::TESTING_TIER
+ 'test-production' | described_class::TESTING_TIER
+ 'QC' | described_class::TESTING_TIER
+ 'qa-env-2' | described_class::TESTING_TIER
+ 'gstg' | described_class::STAGING_TIER
+ 'staging' | described_class::STAGING_TIER
+ 'stage' | described_class::STAGING_TIER
+ 'Model' | described_class::STAGING_TIER
+ 'MODL' | described_class::STAGING_TIER
+ 'Pre-production' | described_class::STAGING_TIER
+ 'pre' | described_class::STAGING_TIER
+ 'Demo' | described_class::STAGING_TIER
+ 'staging' | described_class::STAGING_TIER
+ 'pre-prod' | described_class::STAGING_TIER
+ 'blue-kit-stage' | described_class::STAGING_TIER
+ 'nonprod' | described_class::STAGING_TIER
+ 'nonlive' | described_class::STAGING_TIER
+ 'non-prod' | described_class::STAGING_TIER
+ 'non-live' | described_class::STAGING_TIER
+ 'gprd' | described_class::PRODUCTION_TIER
+ 'gprd-cny' | described_class::PRODUCTION_TIER
+ 'production' | described_class::PRODUCTION_TIER
+ 'Production' | described_class::PRODUCTION_TIER
+ 'PRODUCTION' | described_class::PRODUCTION_TIER
+ 'Production/eu' | described_class::PRODUCTION_TIER
+ 'production/eu' | described_class::PRODUCTION_TIER
+ 'PRODUCTION/EU' | described_class::PRODUCTION_TIER
+ 'productioneu' | described_class::PRODUCTION_TIER
+ 'store-produce' | described_class::PRODUCTION_TIER
+ 'unproductive' | described_class::PRODUCTION_TIER
+ 'production/www.gitlab.com' | described_class::PRODUCTION_TIER
+ 'prod' | described_class::PRODUCTION_TIER
+ 'PROD' | described_class::PRODUCTION_TIER
+ 'Live' | described_class::PRODUCTION_TIER
+ 'canary' | described_class::OTHER_TIER
+ 'other' | described_class::OTHER_TIER
+ 'EXP' | described_class::OTHER_TIER
+ 'something-else' | described_class::OTHER_TIER
+ end
+
+ with_them do
+ it 'backfill tiers for all environments in range' do
+ expect(environment.tier).to be_nil
+
+ migration.perform
+
+ expect(environment.reload.tier).to eq(tier)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb b/spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb
index 8947262ae9e..479afb56210 100644
--- a/spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb
@@ -23,6 +23,7 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillImportedIssueSearchData,
let!(:issue) do
table(:issues).create!(
project_id: project.id,
+ namespace_id: project.project_namespace_id,
title: 'Patterson',
description: FFaker::HipsterIpsum.paragraph
)
@@ -71,6 +72,7 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillImportedIssueSearchData,
let!(:issue2) do
table(:issues).create!(
project_id: project.id,
+ namespace_id: project.project_namespace_id,
title: 'Chatterton',
description: FFaker::HipsterIpsum.paragraph
)
diff --git a/spec/lib/gitlab/background_migration/backfill_jira_tracker_deployment_type2_spec.rb b/spec/lib/gitlab/background_migration/backfill_jira_tracker_deployment_type2_spec.rb
index 65f5f8368df..8db45ac0f57 100644
--- a/spec/lib/gitlab/background_migration/backfill_jira_tracker_deployment_type2_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_jira_tracker_deployment_type2_spec.rb
@@ -3,11 +3,11 @@
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::BackfillJiraTrackerDeploymentType2, :migration, schema: 20210301200959 do
- let_it_be(:jira_integration_temp) { described_class::JiraServiceTemp }
- let_it_be(:jira_tracker_data_temp) { described_class::JiraTrackerDataTemp }
- let_it_be(:atlassian_host) { 'https://api.atlassian.net' }
- let_it_be(:mixedcase_host) { 'https://api.AtlassiaN.nEt' }
- let_it_be(:server_host) { 'https://my.server.net' }
+ let!(:jira_integration_temp) { described_class::JiraServiceTemp }
+ let!(:jira_tracker_data_temp) { described_class::JiraTrackerDataTemp }
+ let!(:atlassian_host) { 'https://api.atlassian.net' }
+ let!(:mixedcase_host) { 'https://api.AtlassiaN.nEt' }
+ let!(:server_host) { 'https://my.server.net' }
let(:jira_integration) { jira_integration_temp.create!(type: 'JiraService', active: true, category: 'issue_tracker') }
diff --git a/spec/lib/gitlab/background_migration/backfill_project_namespace_details_spec.rb b/spec/lib/gitlab/background_migration/backfill_project_namespace_details_spec.rb
index 77d6cc43114..01daf16d10c 100644
--- a/spec/lib/gitlab/background_migration/backfill_project_namespace_details_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_project_namespace_details_spec.rb
@@ -3,9 +3,9 @@
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::BackfillProjectNamespaceDetails, :migration do
- let_it_be(:namespace_details) { table(:namespace_details) }
- let_it_be(:namespaces) { table(:namespaces) }
- let_it_be(:projects) { table(:projects) }
+ let!(:namespace_details) { table(:namespace_details) }
+ let!(:namespaces) { table(:namespaces) }
+ let!(:projects) { table(:projects) }
subject(:perform_migration) do
described_class.new(start_id: projects.minimum(:id),
diff --git a/spec/lib/gitlab/background_migration/backfill_project_namespace_on_issues_spec.rb b/spec/lib/gitlab/background_migration/backfill_project_namespace_on_issues_spec.rb
index 3ca7d28f09d..5fa92759cf9 100644
--- a/spec/lib/gitlab/background_migration/backfill_project_namespace_on_issues_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_project_namespace_on_issues_spec.rb
@@ -1,12 +1,14 @@
# frozen_string_literal: true
require 'spec_helper'
-# todo: this will need to specify schema version once we introduce the not null constraint on issues#namespace_id
-# https://gitlab.com/gitlab-org/gitlab/-/issues/367835
-RSpec.describe Gitlab::BackgroundMigration::BackfillProjectNamespaceOnIssues do
+
+RSpec.describe Gitlab::BackgroundMigration::BackfillProjectNamespaceOnIssues,
+ :migration, schema: 20221118103352, feature_category: :team_planning do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:issues) { table(:issues) }
+ let(:issue_base_type_enum_value) { 0 }
+ let(:issue_type) { table(:work_item_types).find_by!(namespace_id: nil, base_type: issue_base_type_enum_value) }
let(:namespace1) { namespaces.create!(name: 'batchtest1', type: 'Group', path: 'space1') }
let(:namespace2) { namespaces.create!(name: 'batchtest2', type: 'Group', parent_id: namespace1.id, path: 'space2') }
@@ -18,12 +20,12 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillProjectNamespaceOnIssues do
let(:proj1) { projects.create!(name: 'proj1', path: 'proj1', namespace_id: namespace1.id, project_namespace_id: proj_namespace1.id) }
let(:proj2) { projects.create!(name: 'proj2', path: 'proj2', namespace_id: namespace2.id, project_namespace_id: proj_namespace2.id) }
- let!(:proj1_issue_with_namespace) { issues.create!(title: 'issue1', project_id: proj1.id, namespace_id: proj_namespace1.id) }
- let!(:proj1_issue_without_namespace1) { issues.create!(title: 'issue2', project_id: proj1.id) }
- let!(:proj1_issue_without_namespace2) { issues.create!(title: 'issue3', project_id: proj1.id) }
- let!(:proj2_issue_with_namespace) { issues.create!(title: 'issue4', project_id: proj2.id, namespace_id: proj_namespace2.id) }
- let!(:proj2_issue_without_namespace1) { issues.create!(title: 'issue5', project_id: proj2.id) }
- let!(:proj2_issue_without_namespace2) { issues.create!(title: 'issue6', project_id: proj2.id) }
+ let!(:proj1_issue_with_namespace) { issues.create!(title: 'issue1', project_id: proj1.id, namespace_id: proj_namespace1.id, work_item_type_id: issue_type.id) }
+ let!(:proj1_issue_without_namespace1) { issues.create!(title: 'issue2', project_id: proj1.id, work_item_type_id: issue_type.id) }
+ let!(:proj1_issue_without_namespace2) { issues.create!(title: 'issue3', project_id: proj1.id, work_item_type_id: issue_type.id) }
+ let!(:proj2_issue_with_namespace) { issues.create!(title: 'issue4', project_id: proj2.id, namespace_id: proj_namespace2.id, work_item_type_id: issue_type.id) }
+ let!(:proj2_issue_without_namespace1) { issues.create!(title: 'issue5', project_id: proj2.id, work_item_type_id: issue_type.id) }
+ let!(:proj2_issue_without_namespace2) { issues.create!(title: 'issue6', project_id: proj2.id, work_item_type_id: issue_type.id) }
# rubocop:enable Layout/LineLength
let(:migration) do
diff --git a/spec/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues_spec.rb b/spec/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues_spec.rb
index 6ef474ad7f9..5f93424faf6 100644
--- a/spec/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues_spec.rb
@@ -45,7 +45,7 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillWorkItemTypeIdForIssues, :mi
it 'sets work_item_type_id only for the given type' do
expect(all_issues).to all(have_attributes(work_item_type_id: nil))
- expect { migrate }.to make_queries_matching(/UPDATE \"issues\" SET "work_item_type_id"/, 2)
+ expect { migrate }.to make_queries_matching(/UPDATE "issues" SET "work_item_type_id"/, 2)
all_issues.each(&:reload)
expect([issue1, issue2, issue3]).to all(have_attributes(work_item_type_id: issue_type.id))
diff --git a/spec/lib/gitlab/background_migration/batched_migration_job_spec.rb b/spec/lib/gitlab/background_migration/batched_migration_job_spec.rb
index 95be14cefb1..7280ca0b58e 100644
--- a/spec/lib/gitlab/background_migration/batched_migration_job_spec.rb
+++ b/spec/lib/gitlab/background_migration/batched_migration_job_spec.rb
@@ -158,6 +158,28 @@ RSpec.describe Gitlab::BackgroundMigration::BatchedMigrationJob do
end
end
+ describe '.feature_category' do
+ context 'when jobs does not have feature_category attribute set' do
+ let(:job_class) { Class.new(described_class) }
+
+ it 'returns :database as default' do
+ expect(job_class.feature_category).to eq(:database)
+ end
+ end
+
+ context 'when jobs have feature_category attribute set' do
+ let(:job_class) do
+ Class.new(described_class) do
+ feature_category :delivery
+ end
+ end
+
+ it 'returns the provided value' do
+ expect(job_class.feature_category).to eq(:delivery)
+ end
+ end
+ end
+
describe 'descendants', :eager_load do
it 'have the same method signature for #perform' do
expected_arity = described_class.instance_method(:perform).arity
diff --git a/spec/lib/gitlab/background_migration/batching_strategies/loose_index_scan_batching_strategy_spec.rb b/spec/lib/gitlab/background_migration/batching_strategies/loose_index_scan_batching_strategy_spec.rb
index 1a00fd7c8b3..72958700ca2 100644
--- a/spec/lib/gitlab/background_migration/batching_strategies/loose_index_scan_batching_strategy_spec.rb
+++ b/spec/lib/gitlab/background_migration/batching_strategies/loose_index_scan_batching_strategy_spec.rb
@@ -7,6 +7,8 @@ RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::LooseIndexScanBa
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:issues) { table(:issues) }
+ let(:issue_base_type_enum_value) { 0 }
+ let(:issue_type) { table(:work_item_types).find_by!(namespace_id: nil, base_type: issue_base_type_enum_value) }
let!(:namespace1) { namespaces.create!(name: 'ns1', path: 'ns1') }
let!(:namespace2) { namespaces.create!(name: 'ns2', path: 'ns2') }
@@ -19,13 +21,15 @@ RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::LooseIndexScanBa
let!(:project4) { projects.create!(name: 'p4', namespace_id: namespace4.id, project_namespace_id: namespace4.id) }
let!(:project5) { projects.create!(name: 'p5', namespace_id: namespace5.id, project_namespace_id: namespace5.id) }
- let!(:issue1) { issues.create!(title: 'title', description: 'description', project_id: project2.id) }
- let!(:issue2) { issues.create!(title: 'title', description: 'description', project_id: project1.id) }
- let!(:issue3) { issues.create!(title: 'title', description: 'description', project_id: project2.id) }
- let!(:issue4) { issues.create!(title: 'title', description: 'description', project_id: project3.id) }
- let!(:issue5) { issues.create!(title: 'title', description: 'description', project_id: project2.id) }
- let!(:issue6) { issues.create!(title: 'title', description: 'description', project_id: project4.id) }
- let!(:issue7) { issues.create!(title: 'title', description: 'description', project_id: project5.id) }
+ # rubocop:disable Layout/LineLength
+ let!(:issue1) { issues.create!(title: 'title', description: 'description', project_id: project2.id, namespace_id: project2.project_namespace_id, work_item_type_id: issue_type.id) }
+ let!(:issue2) { issues.create!(title: 'title', description: 'description', project_id: project1.id, namespace_id: project1.project_namespace_id, work_item_type_id: issue_type.id) }
+ let!(:issue3) { issues.create!(title: 'title', description: 'description', project_id: project2.id, namespace_id: project2.project_namespace_id, work_item_type_id: issue_type.id) }
+ let!(:issue4) { issues.create!(title: 'title', description: 'description', project_id: project3.id, namespace_id: project3.project_namespace_id, work_item_type_id: issue_type.id) }
+ let!(:issue5) { issues.create!(title: 'title', description: 'description', project_id: project2.id, namespace_id: project2.project_namespace_id, work_item_type_id: issue_type.id) }
+ let!(:issue6) { issues.create!(title: 'title', description: 'description', project_id: project4.id, namespace_id: project4.project_namespace_id, work_item_type_id: issue_type.id) }
+ let!(:issue7) { issues.create!(title: 'title', description: 'description', project_id: project5.id, namespace_id: project5.project_namespace_id, work_item_type_id: issue_type.id) }
+ # rubocop:enable Layout/LineLength
it { expect(described_class).to be < Gitlab::BackgroundMigration::BatchingStrategies::BaseStrategy }
diff --git a/spec/lib/gitlab/background_migration/delete_orphaned_operational_vulnerabilities_spec.rb b/spec/lib/gitlab/background_migration/delete_orphaned_operational_vulnerabilities_spec.rb
index afa955a6056..c03962c8d21 100644
--- a/spec/lib/gitlab/background_migration/delete_orphaned_operational_vulnerabilities_spec.rb
+++ b/spec/lib/gitlab/background_migration/delete_orphaned_operational_vulnerabilities_spec.rb
@@ -5,9 +5,9 @@ require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::DeleteOrphanedOperationalVulnerabilities, :migration do
include MigrationHelpers::VulnerabilitiesHelper
- let_it_be(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
- let_it_be(:users) { table(:users) }
- let_it_be(:user) do
+ let!(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
+ let!(:users) { table(:users) }
+ let!(:user) do
users.create!(
name: "Example User",
email: "user@example.com",
@@ -17,7 +17,7 @@ RSpec.describe Gitlab::BackgroundMigration::DeleteOrphanedOperationalVulnerabili
)
end
- let_it_be(:project) do
+ let!(:project) do
table(:projects).create!(
id: 123,
namespace_id: namespace.id,
@@ -25,9 +25,9 @@ RSpec.describe Gitlab::BackgroundMigration::DeleteOrphanedOperationalVulnerabili
)
end
- let_it_be(:scanners) { table(:vulnerability_scanners) }
- let_it_be(:scanner) { scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
- let_it_be(:different_scanner) do
+ let!(:scanners) { table(:vulnerability_scanners) }
+ let!(:scanner) { scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
+ let!(:different_scanner) do
scanners.create!(
project_id: project.id,
external_id: 'test 2',
@@ -35,22 +35,22 @@ RSpec.describe Gitlab::BackgroundMigration::DeleteOrphanedOperationalVulnerabili
)
end
- let_it_be(:vulnerabilities) { table(:vulnerabilities) }
- let_it_be(:vulnerability_with_finding) do
+ let!(:vulnerabilities) { table(:vulnerabilities) }
+ let!(:vulnerability_with_finding) do
create_vulnerability!(
project_id: project.id,
author_id: user.id
)
end
- let_it_be(:vulnerability_without_finding) do
+ let!(:vulnerability_without_finding) do
create_vulnerability!(
project_id: project.id,
author_id: user.id
)
end
- let_it_be(:cis_vulnerability_without_finding) do
+ let!(:cis_vulnerability_without_finding) do
create_vulnerability!(
project_id: project.id,
author_id: user.id,
@@ -58,7 +58,7 @@ RSpec.describe Gitlab::BackgroundMigration::DeleteOrphanedOperationalVulnerabili
)
end
- let_it_be(:custom_vulnerability_without_finding) do
+ let!(:custom_vulnerability_without_finding) do
create_vulnerability!(
project_id: project.id,
author_id: user.id,
@@ -66,8 +66,8 @@ RSpec.describe Gitlab::BackgroundMigration::DeleteOrphanedOperationalVulnerabili
)
end
- let_it_be(:vulnerability_identifiers) { table(:vulnerability_identifiers) }
- let_it_be(:primary_identifier) do
+ let!(:vulnerability_identifiers) { table(:vulnerability_identifiers) }
+ let!(:primary_identifier) do
vulnerability_identifiers.create!(
project_id: project.id,
external_type: 'uuid-v5',
@@ -76,8 +76,8 @@ RSpec.describe Gitlab::BackgroundMigration::DeleteOrphanedOperationalVulnerabili
name: 'Identifier for UUIDv5')
end
- let_it_be(:vulnerabilities_findings) { table(:vulnerability_occurrences) }
- let_it_be(:finding) do
+ let!(:vulnerabilities_findings) { table(:vulnerability_occurrences) }
+ let!(:finding) do
create_finding!(
vulnerability_id: vulnerability_with_finding.id,
project_id: project.id,
diff --git a/spec/lib/gitlab/background_migration/delete_orphans_approval_merge_request_rules_spec.rb b/spec/lib/gitlab/background_migration/delete_orphans_approval_merge_request_rules_spec.rb
new file mode 100644
index 00000000000..c5b46d3f57c
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/delete_orphans_approval_merge_request_rules_spec.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::DeleteOrphansApprovalMergeRequestRules do
+ describe '#perform' do
+ let(:batch_table) { :approval_merge_request_rules }
+ let(:batch_column) { :id }
+ let(:sub_batch_size) { 1 }
+ let(:pause_ms) { 0 }
+ let(:connection) { ApplicationRecord.connection }
+
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:approval_merge_request_rules) { table(:approval_merge_request_rules) }
+ let(:security_orchestration_policy_configurations) { table(:security_orchestration_policy_configurations) }
+ let(:namespace) { namespaces.create!(name: 'name', path: 'path') }
+ let(:project) do
+ projects
+ .create!(name: "project", path: "project", namespace_id: namespace.id, project_namespace_id: namespace.id)
+ end
+
+ let(:namespace_2) { namespaces.create!(name: 'name_2', path: 'path_2') }
+ let(:security_project) do
+ projects
+ .create!(name: "security_project", path: "security_project", namespace_id: namespace_2.id,
+ project_namespace_id: namespace_2.id)
+ end
+
+ let!(:security_orchestration_policy_configuration) do
+ security_orchestration_policy_configurations
+ .create!(project_id: project.id, security_policy_management_project_id: security_project.id)
+ end
+
+ let(:merge_request) do
+ table(:merge_requests).create!(target_project_id: project.id, target_branch: 'main', source_branch: 'feature')
+ end
+
+ let!(:approval_rule) do
+ approval_merge_request_rules.create!(
+ name: 'rule',
+ merge_request_id: merge_request.id,
+ report_type: 4,
+ security_orchestration_policy_configuration_id: security_orchestration_policy_configuration.id)
+ end
+
+ let!(:approval_rule_other_report_type) do
+ approval_merge_request_rules.create!(
+ name: 'rule 2',
+ merge_request_id: merge_request.id,
+ report_type: 1,
+ security_orchestration_policy_configuration_id: security_orchestration_policy_configuration.id)
+ end
+
+ let!(:approval_rule_last) do
+ approval_merge_request_rules.create!(name: 'rule 3', merge_request_id: merge_request.id, report_type: 4)
+ end
+
+ subject do
+ described_class.new(
+ start_id: approval_rule.id,
+ end_id: approval_rule_last.id,
+ batch_table: batch_table,
+ batch_column: batch_column,
+ sub_batch_size: sub_batch_size,
+ pause_ms: pause_ms,
+ connection: connection
+ ).perform
+ end
+
+ it 'delete only approval rules without association with the security project and report_type equals to 4' do
+ expect { subject }.to change { approval_merge_request_rules.count }.from(3).to(2)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/delete_orphans_approval_project_rules_spec.rb b/spec/lib/gitlab/background_migration/delete_orphans_approval_project_rules_spec.rb
new file mode 100644
index 00000000000..16253255764
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/delete_orphans_approval_project_rules_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::DeleteOrphansApprovalProjectRules do
+ describe '#perform' do
+ let(:batch_table) { :approval_project_rules }
+ let(:batch_column) { :id }
+ let(:sub_batch_size) { 1 }
+ let(:pause_ms) { 0 }
+ let(:connection) { ApplicationRecord.connection }
+
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:approval_project_rules) { table(:approval_project_rules) }
+ let(:security_orchestration_policy_configurations) { table(:security_orchestration_policy_configurations) }
+ let(:namespace) { namespaces.create!(name: 'name', path: 'path') }
+ let(:project) do
+ projects
+ .create!(name: "project", path: "project", namespace_id: namespace.id, project_namespace_id: namespace.id)
+ end
+
+ let(:namespace_2) { namespaces.create!(name: 'name_2', path: 'path_2') }
+ let(:security_project) do
+ projects
+ .create!(name: "security_project", path: "security_project", namespace_id: namespace_2.id,
+ project_namespace_id: namespace_2.id)
+ end
+
+ let!(:security_orchestration_policy_configuration) do
+ security_orchestration_policy_configurations
+ .create!(project_id: project.id, security_policy_management_project_id: security_project.id)
+ end
+
+ let!(:project_rule) do
+ approval_project_rules.create!(
+ name: 'rule',
+ project_id: project.id,
+ report_type: 4,
+ security_orchestration_policy_configuration_id: security_orchestration_policy_configuration.id)
+ end
+
+ let!(:project_rule_other_report_type) do
+ approval_project_rules.create!(
+ name: 'rule 2',
+ project_id: project.id,
+ report_type: 1,
+ security_orchestration_policy_configuration_id: security_orchestration_policy_configuration.id)
+ end
+
+ let!(:project_rule_last) do
+ approval_project_rules.create!(name: 'rule 3', project_id: project.id, report_type: 4)
+ end
+
+ subject do
+ described_class.new(
+ start_id: project_rule.id,
+ end_id: project_rule_last.id,
+ batch_table: batch_table,
+ batch_column: batch_column,
+ sub_batch_size: sub_batch_size,
+ pause_ms: pause_ms,
+ connection: connection
+ ).perform
+ end
+
+ it 'delete only approval rules without association with the security project and report_type equals to 4' do
+ expect { subject }.to change { approval_project_rules.count }.from(3).to(2)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/disable_expiration_policies_linked_to_no_container_images_spec.rb b/spec/lib/gitlab/background_migration/disable_expiration_policies_linked_to_no_container_images_spec.rb
index 8a63673bf38..e7b0471810d 100644
--- a/spec/lib/gitlab/background_migration/disable_expiration_policies_linked_to_no_container_images_spec.rb
+++ b/spec/lib/gitlab/background_migration/disable_expiration_policies_linked_to_no_container_images_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::DisableExpirationPoliciesLinkedToNoContainerImages, :migration, schema: 20220326161803 do # rubocop:disable Layout/LineLength
- let_it_be(:projects) { table(:projects) }
- let_it_be(:container_expiration_policies) { table(:container_expiration_policies) }
- let_it_be(:container_repositories) { table(:container_repositories) }
- let_it_be(:namespaces) { table(:namespaces) }
+ let!(:projects) { table(:projects) }
+ let!(:container_expiration_policies) { table(:container_expiration_policies) }
+ let!(:container_repositories) { table(:container_repositories) }
+ let!(:namespaces) { table(:namespaces) }
let!(:namespace) { namespaces.create!(name: 'test', path: 'test') }
diff --git a/spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_no_issues_no_repo_projects_spec.rb b/spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_no_issues_no_repo_projects_spec.rb
index d20eaef3650..d60874c3159 100644
--- a/spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_no_issues_no_repo_projects_spec.rb
+++ b/spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_no_issues_no_repo_projects_spec.rb
@@ -50,7 +50,7 @@ RSpec.describe Gitlab::BackgroundMigration::DisableLegacyOpenSourceLicenseForNoI
)
project_statistics_table.create!(project_id: project.id, namespace_id: namespace.id, repository_size: repo_size)
- issues_table.create!(project_id: project.id) if with_issue
+ issues_table.create!(project_id: project.id, namespace_id: project.project_namespace_id) if with_issue
project_settings_table.create!(project_id: project.id, legacy_open_source_license_available: true)
project
diff --git a/spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_five_mb_spec.rb b/spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_five_mb_spec.rb
new file mode 100644
index 00000000000..b92f1a74551
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_five_mb_spec.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::DisableLegacyOpenSourceLicenseForProjectsLessThanFiveMb,
+ :migration,
+ schema: 20221018095434,
+ feature_category: :projects do
+ let(:namespaces_table) { table(:namespaces) }
+ let(:projects_table) { table(:projects) }
+ let(:project_settings_table) { table(:project_settings) }
+ let(:project_statistics_table) { table(:project_statistics) }
+
+ subject(:perform_migration) do
+ described_class.new(start_id: project_settings_table.minimum(:project_id),
+ end_id: project_settings_table.maximum(:project_id),
+ batch_table: :project_settings,
+ batch_column: :project_id,
+ sub_batch_size: 2,
+ pause_ms: 0,
+ connection: ActiveRecord::Base.connection)
+ .perform
+ end
+
+ it 'sets `legacy_open_source_license_available` to false only for projects less than 5 MB', :aggregate_failures do
+ project_setting_2_mb = create_legacy_license_project_setting(repo_size: 2)
+ project_setting_4_mb = create_legacy_license_project_setting(repo_size: 4)
+ project_setting_5_mb = create_legacy_license_project_setting(repo_size: 5)
+ project_setting_6_mb = create_legacy_license_project_setting(repo_size: 6)
+
+ record = ActiveRecord::QueryRecorder.new do
+ expect { perform_migration }
+ .to change { migrated_attribute(project_setting_2_mb) }.from(true).to(false)
+ .and change { migrated_attribute(project_setting_4_mb) }.from(true).to(false)
+ .and not_change { migrated_attribute(project_setting_5_mb) }.from(true)
+ .and not_change { migrated_attribute(project_setting_6_mb) }.from(true)
+ end
+
+ expect(record.count).to eq(15)
+ end
+
+ private
+
+ # @param repo_size: Repo size in MB
+ def create_legacy_license_project_setting(repo_size:)
+ path = "path-for-repo-size-#{repo_size}"
+ namespace = namespaces_table.create!(name: "namespace-#{path}", path: "namespace-#{path}")
+ project_namespace =
+ namespaces_table.create!(name: "-project-namespace-#{path}", path: "project-namespace-#{path}", type: 'Project')
+ project = projects_table
+ .create!(name: path, path: path, namespace_id: namespace.id, project_namespace_id: project_namespace.id)
+
+ size_in_bytes = 1.megabyte * repo_size
+ project_statistics_table.create!(project_id: project.id, namespace_id: namespace.id, repository_size: size_in_bytes)
+ project_settings_table.create!(project_id: project.id, legacy_open_source_license_available: true)
+ end
+
+ def migrated_attribute(project_setting)
+ project_settings_table.find(project_setting.project_id).legacy_open_source_license_available
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/drop_invalid_vulnerabilities_spec.rb b/spec/lib/gitlab/background_migration/drop_invalid_vulnerabilities_spec.rb
index 5b6722a3384..ba04f2d20a7 100644
--- a/spec/lib/gitlab/background_migration/drop_invalid_vulnerabilities_spec.rb
+++ b/spec/lib/gitlab/background_migration/drop_invalid_vulnerabilities_spec.rb
@@ -3,33 +3,33 @@
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::DropInvalidVulnerabilities, schema: 20210301200959 do
- let_it_be(:background_migration_jobs) { table(:background_migration_jobs) }
- let_it_be(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
- let_it_be(:users) { table(:users) }
- let_it_be(:user) { create_user! }
- let_it_be(:project) { table(:projects).create!(id: 123, namespace_id: namespace.id) }
-
- let_it_be(:scanners) { table(:vulnerability_scanners) }
- let_it_be(:scanner) { scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
- let_it_be(:different_scanner) { scanners.create!(project_id: project.id, external_id: 'test 2', name: 'test scanner 2') }
-
- let_it_be(:vulnerabilities) { table(:vulnerabilities) }
- let_it_be(:vulnerability_with_finding) do
+ let!(:background_migration_jobs) { table(:background_migration_jobs) }
+ let!(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
+ let!(:users) { table(:users) }
+ let!(:user) { create_user! }
+ let!(:project) { table(:projects).create!(id: 123, namespace_id: namespace.id) }
+
+ let!(:scanners) { table(:vulnerability_scanners) }
+ let!(:scanner) { scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
+ let!(:different_scanner) { scanners.create!(project_id: project.id, external_id: 'test 2', name: 'test scanner 2') }
+
+ let!(:vulnerabilities) { table(:vulnerabilities) }
+ let!(:vulnerability_with_finding) do
create_vulnerability!(
project_id: project.id,
author_id: user.id
)
end
- let_it_be(:vulnerability_without_finding) do
+ let!(:vulnerability_without_finding) do
create_vulnerability!(
project_id: project.id,
author_id: user.id
)
end
- let_it_be(:vulnerability_identifiers) { table(:vulnerability_identifiers) }
- let_it_be(:primary_identifier) do
+ let!(:vulnerability_identifiers) { table(:vulnerability_identifiers) }
+ let!(:primary_identifier) do
vulnerability_identifiers.create!(
project_id: project.id,
external_type: 'uuid-v5',
@@ -38,8 +38,8 @@ RSpec.describe Gitlab::BackgroundMigration::DropInvalidVulnerabilities, schema:
name: 'Identifier for UUIDv5')
end
- let_it_be(:vulnerabilities_findings) { table(:vulnerability_occurrences) }
- let_it_be(:finding) do
+ let!(:vulnerabilities_findings) { table(:vulnerability_occurrences) }
+ let!(:finding) do
create_finding!(
vulnerability_id: vulnerability_with_finding.id,
project_id: project.id,
@@ -94,7 +94,7 @@ RSpec.describe Gitlab::BackgroundMigration::DropInvalidVulnerabilities, schema:
vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:,
name: "test", severity: 7, confidence: 7, report_type: 0,
project_fingerprint: '123qweasdzxc', location_fingerprint: 'test',
- metadata_version: 'test', raw_metadata: 'test', uuid: 'test')
+ metadata_version: 'test', raw_metadata: 'test', uuid: SecureRandom.uuid)
vulnerabilities_findings.create!(
vulnerability_id: vulnerability_id,
project_id: project_id,
diff --git a/spec/lib/gitlab/background_migration/populate_container_repository_migration_plan_spec.rb b/spec/lib/gitlab/background_migration/populate_container_repository_migration_plan_spec.rb
index 0463f5a0c0d..477167c9074 100644
--- a/spec/lib/gitlab/background_migration/populate_container_repository_migration_plan_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_container_repository_migration_plan_spec.rb
@@ -3,12 +3,12 @@
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::PopulateContainerRepositoryMigrationPlan, schema: 20220316202640 do
- let_it_be(:container_repositories) { table(:container_repositories) }
- let_it_be(:projects) { table(:projects) }
- let_it_be(:namespaces) { table(:namespaces) }
- let_it_be(:gitlab_subscriptions) { table(:gitlab_subscriptions) }
- let_it_be(:plans) { table(:plans) }
- let_it_be(:namespace_statistics) { table(:namespace_statistics) }
+ let!(:container_repositories) { table(:container_repositories) }
+ let!(:projects) { table(:projects) }
+ let!(:namespaces) { table(:namespaces) }
+ let!(:gitlab_subscriptions) { table(:gitlab_subscriptions) }
+ let!(:plans) { table(:plans) }
+ let!(:namespace_statistics) { table(:namespace_statistics) }
let!(:namepace1) { namespaces.create!(id: 1, type: 'Group', name: 'group1', path: 'group1', traversal_ids: [1]) }
let!(:namepace2) { namespaces.create!(id: 2, type: 'Group', name: 'group2', path: 'group2', traversal_ids: [2]) }
diff --git a/spec/lib/gitlab/background_migration/populate_namespace_statistics_spec.rb b/spec/lib/gitlab/background_migration/populate_namespace_statistics_spec.rb
index 98b2bc437f3..4a7d52ee784 100644
--- a/spec/lib/gitlab/background_migration/populate_namespace_statistics_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_namespace_statistics_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::PopulateNamespaceStatistics do
- let_it_be(:namespaces) { table(:namespaces) }
- let_it_be(:namespace_statistics) { table(:namespace_statistics) }
- let_it_be(:dependency_proxy_manifests) { table(:dependency_proxy_manifests) }
- let_it_be(:dependency_proxy_blobs) { table(:dependency_proxy_blobs) }
+ let!(:namespaces) { table(:namespaces) }
+ let!(:namespace_statistics) { table(:namespace_statistics) }
+ let!(:dependency_proxy_manifests) { table(:dependency_proxy_manifests) }
+ let!(:dependency_proxy_blobs) { table(:dependency_proxy_blobs) }
let!(:group1) { namespaces.create!(id: 10, type: 'Group', name: 'group1', path: 'group1') }
let!(:group2) { namespaces.create!(id: 20, type: 'Group', name: 'group2', path: 'group2') }
diff --git a/spec/lib/gitlab/background_migration/populate_vulnerability_reads_spec.rb b/spec/lib/gitlab/background_migration/populate_vulnerability_reads_spec.rb
index fc06012ed20..c0470f26d9e 100644
--- a/spec/lib/gitlab/background_migration/populate_vulnerability_reads_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_vulnerability_reads_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe Gitlab::BackgroundMigration::PopulateVulnerabilityReads, :migrati
# rubocop:disable Metrics/ParameterLists
def create_finding!(
- vulnerability_id: nil, project_id:, scanner_id:, primary_identifier_id:,
+ project_id:, scanner_id:, primary_identifier_id:, vulnerability_id: nil,
name: "test", severity: 7, confidence: 7, report_type: 0,
project_fingerprint: '123qweasdzxc', location: { "image" => "alpine:3.4" }, location_fingerprint: 'test',
metadata_version: 'test', raw_metadata: 'test', uuid: SecureRandom.uuid)
diff --git a/spec/lib/gitlab/background_migration/prune_stale_project_export_jobs_spec.rb b/spec/lib/gitlab/background_migration/prune_stale_project_export_jobs_spec.rb
new file mode 100644
index 00000000000..5150d0ea4b0
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/prune_stale_project_export_jobs_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::PruneStaleProjectExportJobs, feature_category: :importers do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:project_export_jobs) { table(:project_export_jobs) }
+ let(:project_relation_exports) { table(:project_relation_exports) }
+ let(:uploads) { table(:project_relation_export_uploads) }
+
+ subject(:perform_migration) do
+ described_class.new(start_id: 1,
+ end_id: 300,
+ batch_table: :project_export_jobs,
+ batch_column: :id,
+ sub_batch_size: 2,
+ pause_ms: 0,
+ connection: ActiveRecord::Base.connection)
+ .perform
+ end
+
+ it 'removes export jobs and associated relations older than 7 days' do
+ namespaces.create!(id: 1000, name: "Sally", path: 'sally')
+ projects.create!(id: 1, namespace_id: 1000, project_namespace_id: 1000)
+
+ project = Project.find 1
+
+ project_export_jobs.create!(id: 10, project_id: project.id, jid: SecureRandom.hex(10), updated_at: 37.months.ago)
+ project_export_jobs.create!(id: 20, project_id: project.id, jid: SecureRandom.hex(10), updated_at: 12.months.ago)
+ project_export_jobs.create!(id: 30, project_id: project.id, jid: SecureRandom.hex(10), updated_at: 8.days.ago)
+ project_export_jobs.create!(id: 40, project_id: project.id, jid: SecureRandom.hex(10), updated_at: 1.day.ago)
+ project_export_jobs.create!(id: 50, project_id: project.id, jid: SecureRandom.hex(10), updated_at: 2.days.ago)
+ project_export_jobs.create!(id: 60, project_id: project.id, jid: SecureRandom.hex(10), updated_at: 6.days.ago)
+
+ project_relation_exports.create!(id: 100, project_export_job_id: 10, relation: 'Project')
+ project_relation_exports.create!(id: 200, project_export_job_id: 20, relation: 'Project')
+ project_relation_exports.create!(id: 300, project_export_job_id: 30, relation: 'Project')
+ project_relation_exports.create!(id: 400, project_export_job_id: 40, relation: 'Project')
+ project_relation_exports.create!(id: 500, project_export_job_id: 50, relation: 'Project')
+ project_relation_exports.create!(id: 600, project_export_job_id: 60, relation: 'Project')
+
+ uploads.create!(project_relation_export_id: 100, export_file: "#{SecureRandom.alphanumeric(5)}_export.tar.gz")
+ uploads.create!(project_relation_export_id: 200, export_file: "#{SecureRandom.alphanumeric(5)}_export.tar.gz")
+ uploads.create!(project_relation_export_id: 300, export_file: "#{SecureRandom.alphanumeric(5)}_export.tar.gz")
+ uploads.create!(project_relation_export_id: 400, export_file: "#{SecureRandom.alphanumeric(5)}_export.tar.gz")
+ uploads.create!(project_relation_export_id: 500, export_file: "#{SecureRandom.alphanumeric(5)}_export.tar.gz")
+ uploads.create!(project_relation_export_id: 600, export_file: "#{SecureRandom.alphanumeric(5)}_export.tar.gz")
+
+ expect(project_export_jobs.all.size).to eq(6)
+ expect(project_relation_exports.all.size).to eq(6)
+ expect(uploads.all.size).to eq(6)
+
+ expect { perform_migration }
+ .to change { project_export_jobs.count }.by(-3)
+ .and change { project_relation_exports.count }.by(-3)
+ .and change { uploads.count }.by(-3)
+
+ expect(project_export_jobs.all.size).to eq(3)
+ expect(project_relation_exports.all.size).to eq(3)
+ expect(uploads.all.size).to eq(3)
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid_spec.rb b/spec/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid_spec.rb
index 29cc4f34f6d..2271bbfb2f3 100644
--- a/spec/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid_spec.rb
+++ b/spec/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid_spec.rb
@@ -488,11 +488,10 @@ RSpec.describe Gitlab::BackgroundMigration::RecalculateVulnerabilitiesOccurrence
# rubocop:disable Metrics/ParameterLists
def create_finding!(
- id: nil,
- vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:,
+ vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:, id: nil,
name: "test", severity: 7, confidence: 7, report_type: 0,
project_fingerprint: '123qweasdzxc', location_fingerprint: 'test',
- metadata_version: 'test', raw_metadata: 'test', uuid: 'test')
+ metadata_version: 'test', raw_metadata: 'test', uuid: SecureRandom.uuid)
vulnerability_findings.create!({
id: id,
vulnerability_id: vulnerability_id,
diff --git a/spec/lib/gitlab/background_migration/remove_backfilled_job_artifacts_expire_at_spec.rb b/spec/lib/gitlab/background_migration/remove_backfilled_job_artifacts_expire_at_spec.rb
index 10597e65910..5fede892463 100644
--- a/spec/lib/gitlab/background_migration/remove_backfilled_job_artifacts_expire_at_spec.rb
+++ b/spec/lib/gitlab/background_migration/remove_backfilled_job_artifacts_expire_at_spec.rb
@@ -20,8 +20,8 @@ RSpec.describe Gitlab::BackgroundMigration::RemoveBackfilledJobArtifactsExpireAt
)
end
- let_it_be(:namespace) { table(:namespaces).create!(id: 1, name: 'user', path: 'user') }
- let_it_be(:project) do
+ let!(:namespace) { table(:namespaces).create!(id: 1, name: 'user', path: 'user') }
+ let!(:project) do
table(:projects).create!(
id: 1,
name: 'gitlab1',
diff --git a/spec/lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings_spec.rb b/spec/lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings_spec.rb
index 8003159f59e..ed08ae22245 100644
--- a/spec/lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings_spec.rb
+++ b/spec/lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings_spec.rb
@@ -134,11 +134,10 @@ RSpec.describe Gitlab::BackgroundMigration::RemoveDuplicateVulnerabilitiesFindin
# rubocop:disable Metrics/ParameterLists
def create_finding!(
- id: nil,
- vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:,
+ vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:, id: nil,
name: "test", severity: 7, confidence: 7, report_type: 0,
project_fingerprint: '123qweasdzxc', location_fingerprint: 'test',
- metadata_version: 'test', raw_metadata: 'test', uuid: 'test')
+ metadata_version: 'test', raw_metadata: 'test', uuid: SecureRandom.uuid)
params = {
vulnerability_id: vulnerability_id,
project_id: project_id,
diff --git a/spec/lib/gitlab/background_migration/remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb b/spec/lib/gitlab/background_migration/remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb
index 33ad74fbee8..1844347f4a9 100644
--- a/spec/lib/gitlab/background_migration/remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb
+++ b/spec/lib/gitlab/background_migration/remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb
@@ -89,7 +89,6 @@ RSpec.describe Gitlab::BackgroundMigration::RemoveOccurrencePipelinesAndDuplicat
let!(:unrelated_finding) do
create_finding!(
id: 9999999,
- uuid: "unreleated_finding",
vulnerability_id: nil,
report_type: 1,
location_fingerprint: 'random_location_fingerprint',
@@ -133,11 +132,10 @@ RSpec.describe Gitlab::BackgroundMigration::RemoveOccurrencePipelinesAndDuplicat
# rubocop:disable Metrics/ParameterLists
def create_finding!(
- id: nil,
- vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:,
+ vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:, id: nil,
name: "test", severity: 7, confidence: 7, report_type: 0,
project_fingerprint: '123qweasdzxc', location_fingerprint: 'test',
- metadata_version: 'test', raw_metadata: 'test', uuid: 'test')
+ metadata_version: 'test', raw_metadata: 'test', uuid: SecureRandom.uuid)
params = {
vulnerability_id: vulnerability_id,
project_id: project_id,
diff --git a/spec/lib/gitlab/background_migration/remove_vulnerability_finding_links_spec.rb b/spec/lib/gitlab/background_migration/remove_vulnerability_finding_links_spec.rb
index ccf96e036ae..918df8f4442 100644
--- a/spec/lib/gitlab/background_migration/remove_vulnerability_finding_links_spec.rb
+++ b/spec/lib/gitlab/background_migration/remove_vulnerability_finding_links_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe Gitlab::BackgroundMigration::RemoveVulnerabilityFindingLinks, :mi
location_fingerprint: "location_fingerprint_#{id}",
metadata_version: 'metadata_version',
raw_metadata: 'raw_metadata',
- uuid: "uuid_#{id}"
+ uuid: SecureRandom.uuid
)
end
end
diff --git a/spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb b/spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb
index 45932defaf9..580465df4d9 100644
--- a/spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb
+++ b/spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb
@@ -5,10 +5,22 @@ require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::RenameTaskSystemNoteToChecklistItem do
let(:notes) { table(:notes) }
let(:projects) { table(:projects) }
-
let(:namespace) { table(:namespaces).create!(name: 'batchtest1', type: 'Group', path: 'space1') }
- let(:project) { table(:projects).create!(name: 'proj1', path: 'proj1', namespace_id: namespace.id) }
- let(:issue) { table(:issues).create!(title: 'Test issue') }
+ let(:issue_base_type_enum_value) { 0 }
+ let(:issue_type) { table(:work_item_types).find_by!(namespace_id: nil, base_type: issue_base_type_enum_value) }
+
+ let(:project) do
+ table(:projects).create!(
+ name: 'proj1', path: 'proj1', namespace_id: namespace.id, project_namespace_id: namespace.id
+ )
+ end
+
+ let(:issue) do
+ table(:issues).create!(
+ title: 'Test issue', project_id: project.id,
+ namespace_id: project.project_namespace_id, work_item_type_id: issue_type.id
+ )
+ end
let!(:note1) do
notes.create!(
diff --git a/spec/lib/gitlab/background_migration/reset_status_on_container_repositories_spec.rb b/spec/lib/gitlab/background_migration/reset_status_on_container_repositories_spec.rb
new file mode 100644
index 00000000000..d50b04857d6
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/reset_status_on_container_repositories_spec.rb
@@ -0,0 +1,261 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::ResetStatusOnContainerRepositories, feature_category: :container_registry do
+ let(:projects_table) { table(:projects) }
+ let(:namespaces_table) { table(:namespaces) }
+ let(:container_repositories_table) { table(:container_repositories) }
+ let(:routes_table) { table(:routes) }
+
+ let!(:root_group) do
+ namespaces_table.create!(name: 'root_group', path: 'root_group', type: 'Group') do |new_group|
+ new_group.update!(traversal_ids: [new_group.id])
+ end
+ end
+
+ let!(:group1) do
+ namespaces_table.create!(name: 'group1', path: 'group1', parent_id: root_group.id, type: 'Group') do |new_group|
+ new_group.update!(traversal_ids: [root_group.id, new_group.id])
+ end
+ end
+
+ let!(:subgroup1) do
+ namespaces_table.create!(name: 'subgroup1', path: 'subgroup1', parent_id: group1.id, type: 'Group') do |new_group|
+ new_group.update!(traversal_ids: [root_group.id, group1.id, new_group.id])
+ end
+ end
+
+ let!(:group2) do
+ namespaces_table.create!(name: 'group2', path: 'group2', parent_id: root_group.id, type: 'Group') do |new_group|
+ new_group.update!(traversal_ids: [root_group.id, new_group.id])
+ end
+ end
+
+ let!(:group1_project_namespace) do
+ namespaces_table.create!(name: 'group1_project', path: 'group1_project', type: 'Project', parent_id: group1.id)
+ end
+
+ let!(:subgroup1_project_namespace) do
+ namespaces_table.create!(
+ name: 'subgroup1_project',
+ path: 'subgroup1_project',
+ type: 'Project',
+ parent_id: subgroup1.id
+ )
+ end
+
+ let!(:group2_project_namespace) do
+ namespaces_table.create!(
+ name: 'group2_project',
+ path: 'group2_project',
+ type: 'Project',
+ parent_id: group2.id
+ )
+ end
+
+ let!(:group1_project) do
+ projects_table.create!(
+ name: 'group1_project',
+ path: 'group1_project',
+ namespace_id: group1.id,
+ project_namespace_id: group1_project_namespace.id
+ )
+ end
+
+ let!(:subgroup1_project) do
+ projects_table.create!(
+ name: 'subgroup1_project',
+ path: 'subgroup1_project',
+ namespace_id: subgroup1.id,
+ project_namespace_id: subgroup1_project_namespace.id
+ )
+ end
+
+ let!(:group2_project) do
+ projects_table.create!(
+ name: 'group2_project',
+ path: 'group2_project',
+ namespace_id: group2.id,
+ project_namespace_id: group2_project_namespace.id
+ )
+ end
+
+ let!(:route2) do
+ routes_table.create!(
+ source_id: group2_project.id,
+ source_type: 'Project',
+ path: 'root_group/group2/group2_project',
+ namespace_id: group2_project_namespace.id
+ )
+ end
+
+ let!(:delete_scheduled_container_repository1) do
+ container_repositories_table.create!(project_id: group1_project.id, status: 0, name: 'container_repository1')
+ end
+
+ let!(:delete_scheduled_container_repository2) do
+ container_repositories_table.create!(project_id: subgroup1_project.id, status: 0, name: 'container_repository2')
+ end
+
+ let!(:delete_scheduled_container_repository3) do
+ container_repositories_table.create!(project_id: group2_project.id, status: 0, name: 'container_repository3')
+ end
+
+ let!(:delete_ongoing_container_repository4) do
+ container_repositories_table.create!(project_id: group2_project.id, status: 2, name: 'container_repository4')
+ end
+
+ let(:migration) do
+ described_class.new(
+ start_id: container_repositories_table.minimum(:id),
+ end_id: container_repositories_table.maximum(:id),
+ batch_table: :container_repositories,
+ batch_column: :id,
+ sub_batch_size: 50,
+ pause_ms: 0,
+ connection: ApplicationRecord.connection
+ )
+ end
+
+ describe '#filter_batch' do
+ it 'scopes the relation to delete scheduled container repositories' do
+ expected = container_repositories_table.where(status: 0).pluck(:id)
+ actual = migration.filter_batch(container_repositories_table).pluck(:id)
+
+ expect(actual).to match_array(expected)
+ end
+ end
+
+ describe '#perform' do
+ let(:registry_api_url) { 'http://example.com' }
+
+ subject(:perform) { migration.perform }
+
+ before do
+ stub_container_registry_config(
+ enabled: true,
+ api_url: registry_api_url,
+ key: 'spec/fixtures/x509_certificate_pk.key'
+ )
+ stub_tags_list(path: 'root_group/group1/group1_project/container_repository1')
+ stub_tags_list(path: 'root_group/group1/subgroup1/subgroup1_project/container_repository2', tags: [])
+ stub_tags_list(path: 'root_group/group2/group2_project/container_repository3')
+ end
+
+ shared_examples 'resetting status of all container repositories scheduled for deletion' do
+ it 'resets all statuses' do
+ expect_logging_on(
+ path: 'root_group/group1/group1_project/container_repository1',
+ id: delete_scheduled_container_repository1.id,
+ has_tags: true
+ )
+ expect_logging_on(
+ path: 'root_group/group1/subgroup1/subgroup1_project/container_repository2',
+ id: delete_scheduled_container_repository2.id,
+ has_tags: true
+ )
+ expect_logging_on(
+ path: 'root_group/group2/group2_project/container_repository3',
+ id: delete_scheduled_container_repository3.id,
+ has_tags: true
+ )
+
+ expect { perform }
+ .to change { delete_scheduled_container_repository1.reload.status }.from(0).to(nil)
+ .and change { delete_scheduled_container_repository3.reload.status }.from(0).to(nil)
+ .and change { delete_scheduled_container_repository2.reload.status }.from(0).to(nil)
+ end
+ end
+
+ it 'resets status of container repositories with tags' do
+ expect_pull_access_token_on(path: 'root_group/group1/group1_project/container_repository1')
+ expect_pull_access_token_on(path: 'root_group/group1/subgroup1/subgroup1_project/container_repository2')
+ expect_pull_access_token_on(path: 'root_group/group2/group2_project/container_repository3')
+
+ expect_logging_on(
+ path: 'root_group/group1/group1_project/container_repository1',
+ id: delete_scheduled_container_repository1.id,
+ has_tags: true
+ )
+ expect_logging_on(
+ path: 'root_group/group1/subgroup1/subgroup1_project/container_repository2',
+ id: delete_scheduled_container_repository2.id,
+ has_tags: false
+ )
+ expect_logging_on(
+ path: 'root_group/group2/group2_project/container_repository3',
+ id: delete_scheduled_container_repository3.id,
+ has_tags: true
+ )
+
+ expect { perform }
+ .to change { delete_scheduled_container_repository1.reload.status }.from(0).to(nil)
+ .and change { delete_scheduled_container_repository3.reload.status }.from(0).to(nil)
+ .and not_change { delete_scheduled_container_repository2.reload.status }
+ end
+
+ context 'with the registry disabled' do
+ before do
+ allow(::Gitlab.config.registry).to receive(:enabled).and_return(false)
+ end
+
+ it_behaves_like 'resetting status of all container repositories scheduled for deletion'
+ end
+
+ context 'with the registry api url not defined' do
+ before do
+ allow(::Gitlab.config.registry).to receive(:api_url).and_return('')
+ end
+
+ it_behaves_like 'resetting status of all container repositories scheduled for deletion'
+ end
+
+ context 'with a faraday error' do
+ before do
+ client_double = instance_double('::ContainerRegistry::Client')
+ allow(::ContainerRegistry::Client).to receive(:new).and_return(client_double)
+ allow(client_double).to receive(:repository_tags).and_raise(Faraday::TimeoutError)
+
+ expect_pull_access_token_on(path: 'root_group/group1/group1_project/container_repository1')
+ expect_pull_access_token_on(path: 'root_group/group1/subgroup1/subgroup1_project/container_repository2')
+ expect_pull_access_token_on(path: 'root_group/group2/group2_project/container_repository3')
+ end
+
+ it_behaves_like 'resetting status of all container repositories scheduled for deletion'
+ end
+
+ def stub_tags_list(path:, tags: %w[tag1])
+ url = "#{registry_api_url}/v2/#{path}/tags/list?n=1"
+
+ stub_request(:get, url)
+ .with(
+ headers: {
+ 'Accept' => ContainerRegistry::Client::ACCEPTED_TYPES.join(', '),
+ 'Authorization' => /bearer .+/,
+ 'User-Agent' => "GitLab/#{Gitlab::VERSION}"
+ }
+ )
+ .to_return(
+ status: 200,
+ body: Gitlab::Json.dump(tags: tags),
+ headers: { 'Content-Type' => 'application/json' }
+ )
+ end
+
+ def expect_pull_access_token_on(path:)
+ expect(Auth::ContainerRegistryAuthenticationService)
+ .to receive(:pull_access_token).with(path).and_call_original
+ end
+
+ def expect_logging_on(path:, id:, has_tags:)
+ expect(::Gitlab::BackgroundMigration::Logger)
+ .to receive(:info).with(
+ migrator: described_class::MIGRATOR,
+ has_tags: has_tags,
+ container_repository_id: id,
+ container_repository_path: path
+ )
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/sanitize_confidential_todos_spec.rb b/spec/lib/gitlab/background_migration/sanitize_confidential_todos_spec.rb
index 2c5c47e39c9..c58f2060001 100644
--- a/spec/lib/gitlab/background_migration/sanitize_confidential_todos_spec.rb
+++ b/spec/lib/gitlab/background_migration/sanitize_confidential_todos_spec.rb
@@ -27,8 +27,15 @@ RSpec.describe Gitlab::BackgroundMigration::SanitizeConfidentialTodos, :migratio
project_namespace_id: project_namespace2.id)
end
- let(:issue1) { issues.create!(project_id: project1.id, issue_type: 1, title: 'issue1', author_id: user.id) }
- let(:issue2) { issues.create!(project_id: project2.id, issue_type: 1, title: 'issue2') }
+ let(:issue1) do
+ issues.create!(
+ project_id: project1.id, namespace_id: project_namespace1.id, issue_type: 1, title: 'issue1', author_id: user.id
+ )
+ end
+
+ let(:issue2) do
+ issues.create!(project_id: project2.id, namespace_id: project_namespace2.id, issue_type: 1, title: 'issue2')
+ end
let(:public_note) { notes.create!(note: 'text', project_id: project1.id) }
diff --git a/spec/lib/gitlab/background_migration/update_timelogs_null_spent_at_spec.rb b/spec/lib/gitlab/background_migration/update_timelogs_null_spent_at_spec.rb
index 982e3319063..908f11aabc3 100644
--- a/spec/lib/gitlab/background_migration/update_timelogs_null_spent_at_spec.rb
+++ b/spec/lib/gitlab/background_migration/update_timelogs_null_spent_at_spec.rb
@@ -3,19 +3,19 @@
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::UpdateTimelogsNullSpentAt, schema: 20211215090620 do
- let_it_be(:previous_time) { 10.days.ago }
- let_it_be(:namespace) { table(:namespaces).create!(name: 'namespace', path: 'namespace') }
- let_it_be(:project) { table(:projects).create!(namespace_id: namespace.id) }
- let_it_be(:issue) { table(:issues).create!(project_id: project.id) }
- let_it_be(:merge_request) { table(:merge_requests).create!(target_project_id: project.id, source_branch: 'master', target_branch: 'feature') }
- let_it_be(:timelog1) { create_timelog!(issue_id: issue.id) }
- let_it_be(:timelog2) { create_timelog!(merge_request_id: merge_request.id) }
- let_it_be(:timelog3) { create_timelog!(issue_id: issue.id, spent_at: previous_time) }
- let_it_be(:timelog4) { create_timelog!(merge_request_id: merge_request.id, spent_at: previous_time) }
+ let!(:previous_time) { 10.days.ago }
+ let!(:namespace) { table(:namespaces).create!(name: 'namespace', path: 'namespace') }
+ let!(:project) { table(:projects).create!(namespace_id: namespace.id) }
+ let!(:issue) { table(:issues).create!(project_id: project.id) }
+ let!(:merge_request) { table(:merge_requests).create!(target_project_id: project.id, source_branch: 'master', target_branch: 'feature') }
+ let!(:timelog1) { create_timelog!(issue_id: issue.id) }
+ let!(:timelog2) { create_timelog!(merge_request_id: merge_request.id) }
+ let!(:timelog3) { create_timelog!(issue_id: issue.id, spent_at: previous_time) }
+ let!(:timelog4) { create_timelog!(merge_request_id: merge_request.id, spent_at: previous_time) }
subject(:background_migration) { described_class.new }
- before_all do
+ before do
table(:timelogs).where.not(id: [timelog3.id, timelog4.id]).update_all(spent_at: nil)
end
diff --git a/spec/lib/gitlab/blob_helper_spec.rb b/spec/lib/gitlab/blob_helper_spec.rb
index a2f20dcd4fc..e18277ba8d2 100644
--- a/spec/lib/gitlab/blob_helper_spec.rb
+++ b/spec/lib/gitlab/blob_helper_spec.rb
@@ -68,6 +68,7 @@ RSpec.describe Gitlab::BlobHelper do
expect(blob.image?).to be_falsey
end
end
+
context 'with a .webp file' do
it 'returns true' do
expect(webp_blob.image?).to be_truthy
diff --git a/spec/lib/gitlab/bullet_spec.rb b/spec/lib/gitlab/bullet_spec.rb
index 1262a0b8bde..c575c656bb4 100644
--- a/spec/lib/gitlab/bullet_spec.rb
+++ b/spec/lib/gitlab/bullet_spec.rb
@@ -3,48 +3,55 @@
require 'spec_helper'
RSpec.describe Gitlab::Bullet do
- describe '#enabled?' do
- it 'is enabled' do
- stub_env('ENABLE_BULLET', true)
-
- expect(described_class.enabled?).to be(true)
- end
-
- it 'is not enabled' do
+ context 'with bullet installed' do
+ before do
stub_env('ENABLE_BULLET', nil)
-
- expect(described_class.enabled?).to be(false)
+ stub_const('::Bullet', double)
end
- it 'is correctly aliased for #extra_logging_enabled?' do
- expect(described_class.method(:extra_logging_enabled?).original_name).to eq(:enabled?)
- end
- end
+ describe '#enabled?' do
+ context 'with env enabled' do
+ before do
+ stub_env('ENABLE_BULLET', true)
+ allow(Gitlab.config.bullet).to receive(:enabled).and_return(false)
+ end
- describe '#configure_bullet?' do
- context 'with ENABLE_BULLET true' do
- before do
- stub_env('ENABLE_BULLET', true)
+ it 'is enabled' do
+ expect(described_class.enabled?).to be(true)
+ end
end
- it 'is configurable' do
- expect(described_class.configure_bullet?).to be(true)
+ context 'with env disabled' do
+ before do
+ stub_env('ENABLE_BULLET', false)
+ allow(Gitlab.config.bullet).to receive(:enabled).and_return(true)
+ end
+
+ it 'is not enabled' do
+ expect(described_class.enabled?).to be(false)
+ end
end
end
- context 'with ENABLE_BULLET falsey' do
- before do
- stub_env('ENABLE_BULLET', nil)
- end
+ describe '#configure_bullet?' do
+ context 'with config enabled' do
+ before do
+ allow(Gitlab.config.bullet).to receive(:enabled).and_return(true)
+ end
- it 'is not configurable' do
- expect(described_class.configure_bullet?).to be(false)
+ it 'is configurable' do
+ expect(described_class.configure_bullet?).to be(true)
+ end
end
- it 'is configurable in development' do
- allow(Rails).to receive_message_chain(:env, :development?).and_return(true)
+ context 'with config disabled' do
+ before do
+ allow(Gitlab.config.bullet).to receive(:enabled).and_return(false)
+ end
- expect(described_class.configure_bullet?).to be(true)
+ it 'is not configurable' do
+ expect(described_class.configure_bullet?).to be(false)
+ end
end
end
end
diff --git a/spec/lib/gitlab/checks/timed_logger_spec.rb b/spec/lib/gitlab/checks/timed_logger_spec.rb
index 6c488212eca..261fdd6c002 100644
--- a/spec/lib/gitlab/checks/timed_logger_spec.rb
+++ b/spec/lib/gitlab/checks/timed_logger_spec.rb
@@ -17,38 +17,44 @@ RSpec.describe Gitlab::Checks::TimedLogger do
logger.append_message("Checking ref: #{ref}")
end
+ around do |example|
+ freeze_time do
+ example.run
+ end
+ end
+
describe '#log_timed' do
it 'logs message' do
- Timecop.freeze(start + 30.seconds) do
- logger.log_timed(log_messages[:foo], start) { bar_check }
- end
+ travel_to(start + 30.seconds)
+
+ logger.log_timed(log_messages[:foo], start) { bar_check }
expect(logger.full_message).to eq("Checking ref: bar\nFoo message... (30000.0ms)")
end
context 'when time limit was reached' do
it 'cancels action' do
- Timecop.freeze(start + 50.seconds) do
- expect do
- logger.log_timed(log_messages[:foo], start) do
- bar_check
- end
- end.to raise_error(described_class::TimeoutError)
- end
+ travel_to(start + 50.seconds)
+
+ expect do
+ logger.log_timed(log_messages[:foo], start) do
+ bar_check
+ end
+ end.to raise_error(described_class::TimeoutError)
expect(logger.full_message).to eq("Checking ref: bar\nFoo message... (cancelled)")
end
it 'cancels action with time elapsed if work was performed' do
- Timecop.freeze(start + 30.seconds) do
- expect do
- logger.log_timed(log_messages[:foo], start) do
- grpc_check
- end
- end.to raise_error(described_class::TimeoutError)
-
- expect(logger.full_message).to eq("Checking ref: bar\nFoo message... (cancelled after 30000.0ms)")
- end
+ travel_to(start + 30.seconds)
+
+ expect do
+ logger.log_timed(log_messages[:foo], start) do
+ grpc_check
+ end
+ end.to raise_error(described_class::TimeoutError)
+
+ expect(logger.full_message).to eq("Checking ref: bar\nFoo message... (cancelled after 30000.0ms)")
end
end
end
diff --git a/spec/lib/gitlab/ci/build/cache_spec.rb b/spec/lib/gitlab/ci/build/cache_spec.rb
index 7477aedb994..a8fa14b4b4c 100644
--- a/spec/lib/gitlab/ci/build/cache_spec.rb
+++ b/spec/lib/gitlab/ci/build/cache_spec.rb
@@ -14,8 +14,8 @@ RSpec.describe Gitlab::Ci::Build::Cache do
cache = described_class.new(cache_config, pipeline)
- expect(Gitlab::Ci::Pipeline::Seed::Build::Cache).to have_received(:new).with(pipeline, { key: 'key-a' })
- expect(Gitlab::Ci::Pipeline::Seed::Build::Cache).to have_received(:new).with(pipeline, { key: 'key-b' })
+ expect(Gitlab::Ci::Pipeline::Seed::Build::Cache).to have_received(:new).with(pipeline, { key: 'key-a' }, 0)
+ expect(Gitlab::Ci::Pipeline::Seed::Build::Cache).to have_received(:new).with(pipeline, { key: 'key-b' }, 1)
expect(cache.instance_variable_get(:@cache)).to eq([cache_seed_a, cache_seed_b])
end
end
@@ -29,7 +29,7 @@ RSpec.describe Gitlab::Ci::Build::Cache do
cache = described_class.new(cache_config, pipeline)
- expect(Gitlab::Ci::Pipeline::Seed::Build::Cache).to have_received(:new).with(pipeline, cache_config)
+ expect(Gitlab::Ci::Pipeline::Seed::Build::Cache).to have_received(:new).with(pipeline, cache_config, 0)
expect(cache.instance_variable_get(:@cache)).to eq([cache_seed])
end
end
diff --git a/spec/lib/gitlab/ci/build/context/build_spec.rb b/spec/lib/gitlab/ci/build/context/build_spec.rb
index 7f862a3b80a..74739a67be0 100644
--- a/spec/lib/gitlab/ci/build/context/build_spec.rb
+++ b/spec/lib/gitlab/ci/build/context/build_spec.rb
@@ -2,11 +2,11 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Build::Context::Build do
+RSpec.describe Gitlab::Ci::Build::Context::Build, feature_category: :pipeline_authoring do
let(:pipeline) { create(:ci_pipeline) }
let(:seed_attributes) { { 'name' => 'some-job' } }
- let(:context) { described_class.new(pipeline, seed_attributes) }
+ subject(:context) { described_class.new(pipeline, seed_attributes) }
shared_examples 'variables collection' do
it { is_expected.to include('CI_COMMIT_REF_NAME' => 'master') }
@@ -22,6 +22,12 @@ RSpec.describe Gitlab::Ci::Build::Context::Build do
it { is_expected.to include('CI_BUILD_REF_NAME' => 'master') }
it { is_expected.to include('CI_PROJECT_PATH' => pipeline.project.full_path) }
end
+
+ context 'when environment:name is provided' do
+ let(:seed_attributes) { { 'name' => 'some-job', 'environment' => 'test' } }
+
+ it { is_expected.to include('CI_ENVIRONMENT_NAME' => 'test') }
+ end
end
describe '#variables' do
diff --git a/spec/lib/gitlab/ci/build/hook_spec.rb b/spec/lib/gitlab/ci/build/hook_spec.rb
new file mode 100644
index 00000000000..6ed40a44c97
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/hook_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Build::Hook, feature_category: :pipeline_authoring do
+ let_it_be(:build1) do
+ FactoryBot.build(:ci_build,
+ options: { hooks: { pre_get_sources_script: ["echo 'hello pre_get_sources_script'"] } })
+ end
+
+ describe '.from_hooks' do
+ subject(:from_hooks) { described_class.from_hooks(build1) }
+
+ it 'initializes and returns hooks' do
+ expect(from_hooks.size).to eq(1)
+ expect(from_hooks[0].name).to eq('pre_get_sources_script')
+ expect(from_hooks[0].script).to eq(["echo 'hello pre_get_sources_script'"])
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
index 7476fc6c25f..6264e0c8e33 100644
--- a/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
@@ -142,6 +142,26 @@ RSpec.describe Gitlab::Ci::Config::Entry::Artifacts do
end
end
+ context 'when the `when` keyword is not a string' do
+ context 'when it is an array' do
+ let(:config) { { paths: %w[results.txt], when: ['always'] } }
+
+ it 'returns error' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'artifacts when should be a string'
+ end
+ end
+
+ context 'when it is a boolean' do
+ let(:config) { { paths: %w[results.txt], when: true } }
+
+ it 'returns error' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'artifacts when should be a string'
+ end
+ end
+ end
+
describe 'excluded artifacts' do
context 'when configuration is valid' do
let(:config) { { untracked: true, exclude: ['some/directory/'] } }
diff --git a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb
index 8da46561b73..736c184a289 100644
--- a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Bridge do
# that we know that we don't want to inherit
# as they do not have sense in context of Bridge
let(:ignored_inheritable_columns) do
- %i[before_script after_script image services cache interruptible timeout
+ %i[before_script after_script hooks image services cache interruptible timeout
retry tags artifacts]
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/cache_spec.rb b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
index 247f4b63910..414cbb169b9 100644
--- a/spec/lib/gitlab/ci/config/entry/cache_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
@@ -163,22 +163,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Cache do
end
end
- context 'when policy is unknown' do
- let(:config) { { policy: 'unknown' } }
-
- it 'reports error' do
- is_expected.to include('cache policy should be pull-push, push, or pull')
- end
- end
-
- context 'when `when` is unknown' do
- let(:config) { { when: 'unknown' } }
-
- it 'reports error' do
- is_expected.to include('cache when should be on_success, on_failure or always')
- end
- end
-
context 'when descendants are invalid' do
context 'with invalid keys' do
let(:config) { { key: 1 } }
@@ -228,6 +212,62 @@ RSpec.describe Gitlab::Ci::Config::Entry::Cache do
is_expected.to include 'cache config contains unknown keys: invalid'
end
end
+
+ context 'when the `when` keyword is not a valid string' do
+ context 'when `when` is unknown' do
+ let(:config) { { when: 'unknown' } }
+
+ it 'returns error' do
+ is_expected.to include('cache when should be one of: on_success, on_failure, always')
+ end
+ end
+
+ context 'when it is an array' do
+ let(:config) { { when: ['always'] } }
+
+ it 'returns error' do
+ expect(entry).not_to be_valid
+ is_expected.to include('cache when should be a string')
+ end
+ end
+
+ context 'when it is a boolean' do
+ let(:config) { { when: true } }
+
+ it 'returns error' do
+ expect(entry).not_to be_valid
+ is_expected.to include('cache when should be a string')
+ end
+ end
+ end
+
+ context 'when the `policy` keyword is not a valid string' do
+ context 'when `policy` is unknown' do
+ let(:config) { { policy: 'unknown' } }
+
+ it 'returns error' do
+ is_expected.to include('cache policy should be one of: pull-push, push, pull')
+ end
+ end
+
+ context 'when it is an array' do
+ let(:config) { { policy: ['pull-push'] } }
+
+ it 'returns error' do
+ expect(entry).not_to be_valid
+ is_expected.to include('cache policy should be a string')
+ end
+ end
+
+ context 'when it is a boolean' do
+ let(:config) { { policy: true } }
+
+ it 'returns error' do
+ expect(entry).not_to be_valid
+ is_expected.to include('cache policy should be a string')
+ end
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/default_spec.rb b/spec/lib/gitlab/ci/config/entry/default_spec.rb
index 5613b0f09d1..46e96843ee3 100644
--- a/spec/lib/gitlab/ci/config/entry/default_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/default_spec.rb
@@ -26,9 +26,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Default do
context 'when filtering all the entry/node names' do
it 'contains the expected node names' do
expect(described_class.nodes.keys)
- .to match_array(%i[before_script image services
- after_script cache interruptible
- timeout retry tags artifacts])
+ .to match_array(%i[before_script after_script hooks cache image services
+ interruptible timeout retry tags artifacts])
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/hooks_spec.rb b/spec/lib/gitlab/ci/config/entry/hooks_spec.rb
new file mode 100644
index 00000000000..7a5ff244e18
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/hooks_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+RSpec.describe Gitlab::Ci::Config::Entry::Hooks do
+ subject(:entry) { described_class.new(config) }
+
+ before do
+ entry.compose!
+ end
+
+ describe 'validations' do
+ context 'when passing a valid hook' do
+ let(:config) { { pre_get_sources_script: ['ls'] } }
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'when passing an invalid hook' do
+ let(:config) { { x_get_something: ['ls'] } }
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when entry config is not a hash' do
+ let(:config) { 'ls' }
+
+ it { is_expected.not_to be_valid }
+ end
+ end
+
+ describe '#value' do
+ let(:config) { { pre_get_sources_script: ['ls'] } }
+
+ it 'returns a hash' do
+ expect(entry.value).to eq(config)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/id_token_spec.rb b/spec/lib/gitlab/ci/config/entry/id_token_spec.rb
new file mode 100644
index 00000000000..12585d662ec
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/id_token_spec.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::Entry::IdToken do
+ context 'when given `aud` as a string' do
+ it 'is valid' do
+ config = { aud: 'https://gitlab.com' }
+ id_token = described_class.new(config)
+
+ id_token.compose!
+
+ expect(id_token).to be_valid
+ expect(id_token.value).to eq(aud: 'https://gitlab.com')
+ end
+ end
+
+ context 'when given `aud` as an array' do
+ it 'is valid and concatenates the values' do
+ config = { aud: ['https://gitlab.com', 'https://aws.com'] }
+ id_token = described_class.new(config)
+
+ id_token.compose!
+
+ expect(id_token).to be_valid
+ expect(id_token.value).to eq(aud: ['https://gitlab.com', 'https://aws.com'])
+ end
+ end
+
+ context 'when not given an `aud`' do
+ it 'is invalid' do
+ config = {}
+ id_token = described_class.new(config)
+
+ id_token.compose!
+
+ expect(id_token).not_to be_valid
+ expect(id_token.errors).to match_array([
+ 'id token config missing required keys: aud',
+ 'id token aud should be an array of strings or a string'
+ ])
+ end
+ end
+
+ context 'when given an unknown keyword' do
+ it 'is invalid' do
+ config = { aud: 'https://gitlab.com', unknown: 'test' }
+ id_token = described_class.new(config)
+
+ id_token.compose!
+
+ expect(id_token).not_to be_valid
+ expect(id_token.errors).to match_array([
+ 'id token config contains unknown keys: unknown'
+ ])
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index acf60a6cdda..becb46ac2e7 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
subject { described_class.nodes.keys }
let(:result) do
- %i[before_script script stage after_script cache
+ %i[before_script script after_script hooks stage cache
image services only except rules needs variables artifacts
environment coverage retry interruptible timeout release tags
inherit parallel]
@@ -716,7 +716,9 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
let(:config) do
{ before_script: %w[ls pwd],
script: 'rspec',
- after_script: %w[cleanup] }
+ after_script: %w[cleanup],
+ id_tokens: { TEST_ID_TOKEN: { aud: 'https://gitlab.com' } },
+ hooks: { pre_get_sources_script: 'echo hello' } }
end
it 'returns correct value' do
@@ -727,10 +729,33 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
stage: 'test',
ignore: false,
after_script: %w[cleanup],
+ hooks: { pre_get_sources_script: ['echo hello'] },
only: { refs: %w[branches tags] },
job_variables: {},
root_variables_inheritance: true,
- scheduling_type: :stage)
+ scheduling_type: :stage,
+ id_tokens: { TEST_ID_TOKEN: { aud: 'https://gitlab.com' } })
+ end
+
+ context 'when the FF ci_hooks_pre_get_sources_script is disabled' do
+ before do
+ stub_feature_flags(ci_hooks_pre_get_sources_script: false)
+ end
+
+ it 'returns correct value' do
+ expect(entry.value)
+ .to eq(name: :rspec,
+ before_script: %w[ls pwd],
+ script: %w[rspec],
+ stage: 'test',
+ ignore: false,
+ after_script: %w[cleanup],
+ only: { refs: %w[branches tags] },
+ job_variables: {},
+ root_variables_inheritance: true,
+ scheduling_type: :stage,
+ id_tokens: { TEST_ID_TOKEN: { aud: 'https://gitlab.com' } })
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb
index 085293d7368..c40589104cd 100644
--- a/spec/lib/gitlab/ci/config/entry/root_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb
@@ -5,7 +5,8 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::Entry::Root do
let(:user) {}
let(:project) {}
- let(:root) { described_class.new(hash, user: user, project: project) }
+ let(:logger) { Gitlab::Ci::Pipeline::Logger.new(project: project) }
+ let(:root) { described_class.new(hash, user: user, project: project, logger: logger) }
describe '.nodes' do
it 'returns a hash' do
@@ -37,7 +38,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
variables: {
VAR: 'root',
VAR2: { value: 'val 2', description: 'this is var 2' },
- VAR3: { value: %w[val3 val3b], description: 'this is var 3' }
+ VAR3: { value: 'val3', options: %w[val3 val4 val5], description: 'this is var 3 and some options' }
},
after_script: ['make clean'],
stages: %w(build pages release),
@@ -228,6 +229,14 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
)
end
end
+
+ it 'tracks log entries' do
+ expect(logger.observations_hash).to match(
+ a_hash_including(
+ 'config_root_compose_jobs_factory_duration_s' => a_kind_of(Numeric)
+ )
+ )
+ end
end
end
@@ -317,6 +326,42 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
end
end
+ context 'when variables have `options` data' do
+ before do
+ root.compose!
+ end
+
+ context 'and the value is in the `options` array' do
+ let(:hash) do
+ {
+ variables: { 'VAR' => { value: 'val1', options: %w[val1 val2] } },
+ rspec: { script: 'bin/rspec' }
+ }
+ end
+
+ it 'returns correct value' do
+ expect(root.variables_entry.value_with_data).to eq(
+ 'VAR' => { value: 'val1' }
+ )
+
+ expect(root.variables_value).to eq('VAR' => 'val1')
+ end
+ end
+
+ context 'and the value is not in the `options` array' do
+ let(:hash) do
+ {
+ variables: { 'VAR' => { value: 'val', options: %w[val1 val2] } },
+ rspec: { script: 'bin/rspec' }
+ }
+ end
+
+ it 'returns an error' do
+ expect(root.errors).to contain_exactly('variables:var config value must be present in options')
+ end
+ end
+ end
+
context 'when variables have "expand" data' do
let(:hash) do
{
diff --git a/spec/lib/gitlab/ci/config/entry/trigger_spec.rb b/spec/lib/gitlab/ci/config/entry/trigger_spec.rb
index d0116c961d7..f47923af45a 100644
--- a/spec/lib/gitlab/ci/config/entry/trigger_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/trigger_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Config::Entry::Trigger do
+RSpec.describe Gitlab::Ci::Config::Entry::Trigger, feature_category: :pipeline_authoring do
subject { described_class.new(config) }
context 'when trigger config is a non-empty string' do
@@ -35,6 +35,48 @@ RSpec.describe Gitlab::Ci::Config::Entry::Trigger do
end
context 'when trigger is a hash - cross-project' do
+ context 'when project is a string' do
+ context 'when project is a non-empty string' do
+ let(:config) { { project: 'some/project' } }
+
+ it 'is valid' do
+ expect(subject).to be_valid
+ end
+ end
+
+ context 'when project is an empty string' do
+ let(:config) { { project: '' } }
+
+ it 'returns error' do
+ expect(subject).not_to be_valid
+ expect(subject.errors.first)
+ .to match /project can't be blank/
+ end
+ end
+ end
+
+ context 'when project is not a string' do
+ context 'when project is an array' do
+ let(:config) { { project: ['some/project'] } }
+
+ it 'returns error' do
+ expect(subject).not_to be_valid
+ expect(subject.errors.first)
+ .to match /should be a string/
+ end
+ end
+
+ context 'when project is a boolean' do
+ let(:config) { { project: true } }
+
+ it 'returns error' do
+ expect(subject).not_to be_valid
+ expect(subject.errors.first)
+ .to match /should be a string/
+ end
+ end
+ end
+
context 'when branch is provided' do
let(:config) { { project: 'some/project', branch: 'feature' } }
diff --git a/spec/lib/gitlab/ci/config/entry/variable_spec.rb b/spec/lib/gitlab/ci/config/entry/variable_spec.rb
index d7023072312..97b06c8b1a5 100644
--- a/spec/lib/gitlab/ci/config/entry/variable_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/variable_spec.rb
@@ -306,48 +306,48 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variable do
end
end
end
- end
- describe 'ComplexArrayVariable' do
- context 'when allow_array_value metadata is false' do
- let(:config) { { value: %w[value value2], description: 'description' } }
- let(:metadata) { { allow_array_value: false } }
+ context 'when config is a hash with options' do
+ context 'when there is no metadata' do
+ let(:config) { { value: 'value', options: %w[value value2] } }
+ let(:metadata) { {} }
- describe '#valid?' do
- it { is_expected.not_to be_valid }
- end
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
- describe '#errors' do
- subject(:errors) { entry.errors }
+ describe '#errors' do
+ subject(:errors) { entry.errors }
- it { is_expected.to include 'var1 config value must be an alphanumeric string' }
+ it { is_expected.to include 'var1 config must be a string' }
+ end
end
- end
- context 'when allow_array_value metadata is true' do
- let(:config) { { value: %w[value value2], description: 'description' } }
- let(:metadata) { { allowed_value_data: %i[value description], allow_array_value: true } }
+ context 'when options are allowed' do
+ let(:config) { { value: 'value', options: %w[value value2] } }
+ let(:metadata) { { allowed_value_data: %i[value options] } }
- describe '#valid?' do
- it { is_expected.to be_valid }
- end
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
- describe '#value' do
- subject(:value) { entry.value }
+ describe '#value' do
+ subject(:value) { entry.value }
- it { is_expected.to eq('value') }
- end
+ it { is_expected.to eq('value') }
+ end
- describe '#value_with_data' do
- subject(:value_with_data) { entry.value_with_data }
+ describe '#value_with_data' do
+ subject(:value_with_data) { entry.value_with_data }
- it { is_expected.to eq(value: 'value') }
- end
+ it { is_expected.to eq(value: 'value') }
+ end
- describe '#value_with_prefill_data' do
- subject(:value_with_prefill_data) { entry.value_with_prefill_data }
+ describe '#value_with_prefill_data' do
+ subject(:value_with_prefill_data) { entry.value_with_prefill_data }
- it { is_expected.to eq(value: 'value', description: 'description', value_options: %w[value value2]) }
+ it { is_expected.to eq(value: 'value', options: %w[value value2]) }
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/variables_spec.rb b/spec/lib/gitlab/ci/config/entry/variables_spec.rb
index 609e4422d5c..e7dbc78729d 100644
--- a/spec/lib/gitlab/ci/config/entry/variables_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/variables_spec.rb
@@ -116,8 +116,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
it_behaves_like 'invalid config', /variable_1 config must be a string/
end
- context 'when metadata has allow_array_value and allowed_value_data' do
- let(:metadata) { { allowed_value_data: %i[value description], allow_array_value: true } }
+ context 'when metadata has the allowed_value_data key' do
+ let(:metadata) { { allowed_value_data: %i[value description options] } }
let(:result) do
{ 'VARIABLE_1' => 'value' }
@@ -143,17 +143,15 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
end
end
- context 'when entry config value has key-value pair and value is an array' do
+ context 'when entry config value has options' do
let(:config) do
- { 'VARIABLE_1' => { value: %w[value1 value2], description: 'variable 1' } }
+ { 'VARIABLE_1' => {
+ value: 'value1', options: %w[value1 value2], description: 'variable 1'
+ } }
end
- context 'when there is no allowed_value_data metadata' do
- it_behaves_like 'invalid config', /variable_1 config value must be an alphanumeric string/
- end
-
- context 'when metadata has allow_array_value and allowed_value_data' do
- let(:metadata) { { allowed_value_data: %i[value description], allow_array_value: true } }
+ context 'when metadata has allowed_value_data' do
+ let(:metadata) { { allowed_value_data: %i[value description options] } }
let(:result) do
{ 'VARIABLE_1' => 'value1' }
@@ -172,7 +170,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
describe '#value_with_prefill_data' do
it 'returns variable with prefill data' do
expect(entry.value_with_prefill_data).to eq(
- 'VARIABLE_1' => { value: 'value1', value_options: %w[value1 value2], description: 'variable 1' }
+ 'VARIABLE_1' => { value: 'value1', options: %w[value1 value2], description: 'variable 1' }
)
end
end
@@ -234,14 +232,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
it_behaves_like 'invalid config', /variable_1 config uses invalid data keys: hello/
end
- context 'when entry config value has hash with nil description' do
- let(:config) do
- { 'VARIABLE_1' => { value: 'value 1', description: nil } }
- end
-
- it_behaves_like 'invalid config', /variable_1 config description must be an alphanumeric string/
- end
-
context 'when entry config value has hash without description' do
let(:config) do
{ 'VARIABLE_1' => { value: 'value 1' } }
diff --git a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
index c22afb32756..8d93cdcf378 100644
--- a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
@@ -188,6 +188,19 @@ RSpec.describe Gitlab::Ci::Config::External::File::Remote do
'is blocked: Requests to localhost are not allowed!'
end
end
+
+ context 'when connection refused error has been raised' do
+ let(:location) { 'http://127.0.0.1/some/path/to/config.yaml' }
+ let(:exception) { Errno::ECONNREFUSED.new }
+
+ before do
+ stub_full_request(location).to_raise(exception)
+ end
+
+ it 'returns details about connection failure' do
+ expect(subject).to eq "Remote file could not be fetched because Connection refused!"
+ end
+ end
end
describe '#expand_context' do
diff --git a/spec/lib/gitlab/ci/config/external/mapper/base_spec.rb b/spec/lib/gitlab/ci/config/external/mapper/base_spec.rb
new file mode 100644
index 00000000000..0fdcc5e8ff7
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/external/mapper/base_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::External::Mapper::Base, feature_category: :pipeline_authoring do
+ let(:test_class) do
+ Class.new(described_class) do
+ def self.name
+ 'TestClass'
+ end
+ end
+ end
+
+ let(:context) { Gitlab::Ci::Config::External::Context.new }
+ let(:mapper) { test_class.new(context) }
+
+ describe '#process' do
+ subject(:process) { mapper.process }
+
+ context 'when the method is not implemented' do
+ it 'raises NotImplementedError' do
+ expect { process }.to raise_error(NotImplementedError)
+ end
+ end
+
+ context 'when the method is implemented' do
+ before do
+ test_class.class_eval do
+ def process_without_instrumentation
+ 'test'
+ end
+ end
+ end
+
+ it 'calls the method' do
+ expect(process).to eq('test')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/external/mapper/filter_spec.rb b/spec/lib/gitlab/ci/config/external/mapper/filter_spec.rb
new file mode 100644
index 00000000000..df2a2f0fd01
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/external/mapper/filter_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::External::Mapper::Filter, feature_category: :pipeline_authoring do
+ let_it_be(:variables) do
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ variables.append(key: 'VARIABLE1', value: 'hello')
+ end
+ end
+
+ let_it_be(:context) do
+ Gitlab::Ci::Config::External::Context.new(variables: variables)
+ end
+
+ subject(:filter) { described_class.new(context) }
+
+ describe '#process' do
+ let(:locations) do
+ [{ local: 'config/.gitlab-ci.yml', rules: [{ if: '$VARIABLE1' }] },
+ { remote: 'https://example.com/.gitlab-ci.yml', rules: [{ if: '$VARIABLE2' }] }]
+ end
+
+ subject(:process) { filter.process(locations) }
+
+ it 'filters locations according to rules' do
+ is_expected.to eq(
+ [{ local: 'config/.gitlab-ci.yml', rules: [{ if: '$VARIABLE1' }] }]
+ )
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/external/mapper/location_expander_spec.rb b/spec/lib/gitlab/ci/config/external/mapper/location_expander_spec.rb
new file mode 100644
index 00000000000..b14b6b0ca29
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/external/mapper/location_expander_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::External::Mapper::LocationExpander, feature_category: :pipeline_authoring do
+ include RepoHelpers
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { project.owner }
+
+ let(:sha) { project.commit.sha }
+
+ let(:context) do
+ Gitlab::Ci::Config::External::Context.new(project: project, user: user, sha: sha)
+ end
+
+ subject(:location_expander) { described_class.new(context) }
+
+ describe '#process' do
+ subject(:process) { location_expander.process(locations) }
+
+ context 'when there are project files' do
+ let(:locations) do
+ [{ project: 'gitlab-org/gitlab-1', file: ['builds.yml', 'tests.yml'] },
+ { project: 'gitlab-org/gitlab-2', file: 'deploy.yml' }]
+ end
+
+ it 'returns expanded locations' do
+ is_expected.to eq(
+ [{ project: 'gitlab-org/gitlab-1', file: 'builds.yml' },
+ { project: 'gitlab-org/gitlab-1', file: 'tests.yml' },
+ { project: 'gitlab-org/gitlab-2', file: 'deploy.yml' }]
+ )
+ end
+ end
+
+ context 'when there are local files' do
+ let(:locations) do
+ [{ local: 'builds/*.yml' },
+ { local: 'tests.yml' }]
+ end
+
+ let(:project_files) do
+ { 'builds/1.yml' => 'a', 'builds/2.yml' => 'b', 'tests.yml' => 'c' }
+ end
+
+ around do |example|
+ create_and_delete_files(project, project_files) do
+ example.run
+ end
+ end
+
+ it 'returns expanded locations' do
+ is_expected.to eq(
+ [{ local: 'builds/1.yml' },
+ { local: 'builds/2.yml' },
+ { local: 'tests.yml' }]
+ )
+ end
+ end
+
+ context 'when there are other files' do
+ let(:locations) do
+ [{ remote: 'https://gitlab.com/gitlab-org/gitlab-ce/raw/master/.gitlab-ci.yml' }]
+ end
+
+ it 'returns the same location' do
+ is_expected.to eq(locations)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/external/mapper/matcher_spec.rb b/spec/lib/gitlab/ci/config/external/mapper/matcher_spec.rb
new file mode 100644
index 00000000000..5f321a696c9
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/external/mapper/matcher_spec.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::External::Mapper::Matcher, feature_category: :pipeline_authoring do
+ let_it_be(:variables) do
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ variables.append(key: 'A_MASKED_VAR', value: 'this-is-secret', masked: true)
+ end
+ end
+
+ let_it_be(:context) do
+ Gitlab::Ci::Config::External::Context.new(variables: variables)
+ end
+
+ subject(:matcher) { described_class.new(context) }
+
+ describe '#process' do
+ let(:locations) do
+ [{ local: 'file.yml' },
+ { file: 'file.yml', project: 'namespace/project' },
+ { remote: 'https://example.com/.gitlab-ci.yml' },
+ { template: 'file.yml' },
+ { artifact: 'generated.yml', job: 'test' }]
+ end
+
+ subject(:process) { matcher.process(locations) }
+
+ it 'returns an array of file objects' do
+ is_expected.to contain_exactly(
+ an_instance_of(Gitlab::Ci::Config::External::File::Local),
+ an_instance_of(Gitlab::Ci::Config::External::File::Project),
+ an_instance_of(Gitlab::Ci::Config::External::File::Remote),
+ an_instance_of(Gitlab::Ci::Config::External::File::Template),
+ an_instance_of(Gitlab::Ci::Config::External::File::Artifact)
+ )
+ end
+
+ context 'when a location is not valid' do
+ let(:locations) { [{ invalid: 'file.yml' }] }
+
+ it 'raises an error' do
+ expect { process }.to raise_error(
+ Gitlab::Ci::Config::External::Mapper::AmbigiousSpecificationError,
+ '`{"invalid":"file.yml"}` does not have a valid subkey for include. ' \
+ 'Valid subkeys are: `local`, `project`, `remote`, `template`, `artifact`'
+ )
+ end
+
+ context 'when the invalid location includes a masked variable' do
+ let(:locations) { [{ invalid: 'this-is-secret.yml' }] }
+
+ it 'raises an error with a masked sentence' do
+ expect { process }.to raise_error(
+ Gitlab::Ci::Config::External::Mapper::AmbigiousSpecificationError,
+ '`{"invalid":"xxxxxxxxxxxxxx.yml"}` does not have a valid subkey for include. ' \
+ 'Valid subkeys are: `local`, `project`, `remote`, `template`, `artifact`'
+ )
+ end
+ end
+ end
+
+ context 'when a location is ambiguous' do
+ let(:locations) { [{ local: 'file.yml', remote: 'https://example.com/.gitlab-ci.yml' }] }
+
+ it 'raises an error' do
+ expect { process }.to raise_error(
+ Gitlab::Ci::Config::External::Mapper::AmbigiousSpecificationError,
+ "Each include must use only one of: `local`, `project`, `remote`, `template`, `artifact`"
+ )
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/external/mapper/normalizer_spec.rb b/spec/lib/gitlab/ci/config/external/mapper/normalizer_spec.rb
new file mode 100644
index 00000000000..709c234253b
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/external/mapper/normalizer_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::External::Mapper::Normalizer, feature_category: :pipeline_authoring do
+ let_it_be(:variables) do
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ variables.append(key: 'VARIABLE1', value: 'config')
+ variables.append(key: 'VARIABLE2', value: 'https://example.com')
+ end
+ end
+
+ let_it_be(:context) do
+ Gitlab::Ci::Config::External::Context.new(variables: variables)
+ end
+
+ subject(:normalizer) { described_class.new(context) }
+
+ describe '#process' do
+ let(:locations) do
+ ['https://example.com/.gitlab-ci.yml',
+ 'config/.gitlab-ci.yml',
+ { local: 'config/.gitlab-ci.yml' },
+ { remote: 'https://example.com/.gitlab-ci.yml' },
+ { template: 'Template.gitlab-ci.yml' },
+ '$VARIABLE1/.gitlab-ci.yml',
+ '$VARIABLE2/.gitlab-ci.yml']
+ end
+
+ subject(:process) { normalizer.process(locations) }
+
+ it 'converts locations to canonical form' do
+ is_expected.to eq(
+ [{ remote: 'https://example.com/.gitlab-ci.yml' },
+ { local: 'config/.gitlab-ci.yml' },
+ { local: 'config/.gitlab-ci.yml' },
+ { remote: 'https://example.com/.gitlab-ci.yml' },
+ { template: 'Template.gitlab-ci.yml' },
+ { local: 'config/.gitlab-ci.yml' },
+ { remote: 'https://example.com/.gitlab-ci.yml' }]
+ )
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/external/mapper/variables_expander_spec.rb b/spec/lib/gitlab/ci/config/external/mapper/variables_expander_spec.rb
new file mode 100644
index 00000000000..f7454dcd4be
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/external/mapper/variables_expander_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::External::Mapper::VariablesExpander, feature_category: :pipeline_authoring do
+ let_it_be(:variables) do
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ variables.append(key: 'VARIABLE1', value: 'hello')
+ end
+ end
+
+ let_it_be(:context) do
+ Gitlab::Ci::Config::External::Context.new(variables: variables)
+ end
+
+ subject(:variables_expander) { described_class.new(context) }
+
+ describe '#process' do
+ subject(:process) { variables_expander.process(locations) }
+
+ context 'when locations are strings' do
+ let(:locations) { ['$VARIABLE1.gitlab-ci.yml'] }
+
+ it 'expands variables' do
+ is_expected.to eq(['hello.gitlab-ci.yml'])
+ end
+ end
+
+ context 'when locations are hashes' do
+ let(:locations) { [{ local: '$VARIABLE1.gitlab-ci.yml' }] }
+
+ it 'expands variables' do
+ is_expected.to eq([{ local: 'hello.gitlab-ci.yml' }])
+ end
+ end
+
+ context 'when locations are arrays' do
+ let(:locations) { [{ local: ['$VARIABLE1.gitlab-ci.yml'] }] }
+
+ it 'expands variables' do
+ is_expected.to eq([{ local: ['hello.gitlab-ci.yml'] }])
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/external/mapper/verifier_spec.rb b/spec/lib/gitlab/ci/config/external/mapper/verifier_spec.rb
new file mode 100644
index 00000000000..7c7252c6b0e
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/external/mapper/verifier_spec.rb
@@ -0,0 +1,137 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::External::Mapper::Verifier, feature_category: :pipeline_authoring do
+ include RepoHelpers
+ include StubRequests
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { project.owner }
+
+ let(:context) do
+ Gitlab::Ci::Config::External::Context.new(project: project, user: user, sha: project.commit.id)
+ end
+
+ let(:remote_url) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.gitlab-ci-1.yml' }
+
+ let(:project_files) do
+ {
+ 'myfolder/file1.yml' => <<~YAML,
+ my_build:
+ script: echo Hello World
+ YAML
+ 'myfolder/file2.yml' => <<~YAML,
+ my_test:
+ script: echo Hello World
+ YAML
+ 'nested_configs.yml' => <<~YAML
+ include:
+ - local: myfolder/file1.yml
+ - local: myfolder/file2.yml
+ - remote: #{remote_url}
+ YAML
+ }
+ end
+
+ around(:all) do |example|
+ create_and_delete_files(project, project_files) do
+ example.run
+ end
+ end
+
+ before do
+ stub_full_request(remote_url).to_return(
+ body: <<~YAML
+ remote_test:
+ script: echo Hello World
+ YAML
+ )
+ end
+
+ subject(:verifier) { described_class.new(context) }
+
+ describe '#process' do
+ subject(:process) { verifier.process(files) }
+
+ context 'when files are local' do
+ let(:files) do
+ [
+ Gitlab::Ci::Config::External::File::Local.new({ local: 'myfolder/file1.yml' }, context),
+ Gitlab::Ci::Config::External::File::Local.new({ local: 'myfolder/file2.yml' }, context)
+ ]
+ end
+
+ it 'returns an array of file objects' do
+ expect(process.map(&:location)).to contain_exactly('myfolder/file1.yml', 'myfolder/file2.yml')
+ end
+
+ it 'adds files to the expandset' do
+ expect { process }.to change { context.expandset.count }.by(2)
+ end
+ end
+
+ context 'when a file includes other files' do
+ let(:files) do
+ [
+ Gitlab::Ci::Config::External::File::Local.new({ local: 'nested_configs.yml' }, context)
+ ]
+ end
+
+ it 'returns an array of file objects with combined hash' do
+ expect(process.map(&:to_hash)).to contain_exactly(
+ { my_build: { script: 'echo Hello World' },
+ my_test: { script: 'echo Hello World' },
+ remote_test: { script: 'echo Hello World' } }
+ )
+ end
+ end
+
+ context 'when there is an invalid file' do
+ let(:files) do
+ [
+ Gitlab::Ci::Config::External::File::Local.new({ local: 'myfolder/invalid.yml' }, context)
+ ]
+ end
+
+ it 'adds an error to the file' do
+ expect(process.first.errors).to include("Local file `myfolder/invalid.yml` does not exist!")
+ end
+ end
+
+ context 'when max_includes is exceeded' do
+ context 'when files are nested' do
+ let(:files) do
+ [
+ Gitlab::Ci::Config::External::File::Local.new({ local: 'nested_configs.yml' }, context)
+ ]
+ end
+
+ before do
+ allow(context).to receive(:max_includes).and_return(1)
+ end
+
+ it 'raises Processor::IncludeError' do
+ expect { process }.to raise_error(Gitlab::Ci::Config::External::Processor::IncludeError)
+ end
+ end
+
+ context 'when files are not nested' do
+ let(:files) do
+ [
+ Gitlab::Ci::Config::External::File::Local.new({ local: 'myfolder/file1.yml' }, context),
+ Gitlab::Ci::Config::External::File::Local.new({ local: 'myfolder/file2.yml' }, context)
+ ]
+ end
+
+ before do
+ allow(context).to receive(:max_includes).and_return(1)
+ end
+
+ it 'raises Mapper::TooManyIncludesError' do
+ expect { process }.to raise_error(Gitlab::Ci::Config::External::Mapper::TooManyIncludesError)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
index d905568f01e..b7e58d4dfa1 100644
--- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
@@ -2,8 +2,10 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Config::External::Mapper do
+# This will be removed with FF ci_refactoring_external_mapper and moved to below.
+RSpec.shared_context 'gitlab_ci_config_external_mapper' do
include StubRequests
+ include RepoHelpers
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.owner }
@@ -12,13 +14,13 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
let(:remote_url) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.gitlab-ci-1.yml' }
let(:template_file) { 'Auto-DevOps.gitlab-ci.yml' }
let(:variables) { project.predefined_variables }
- let(:context_params) { { project: project, sha: '123456', user: user, variables: variables } }
+ let(:context_params) { { project: project, sha: project.commit.sha, user: user, variables: variables } }
let(:context) { Gitlab::Ci::Config::External::Context.new(**context_params) }
let(:file_content) do
- <<~HEREDOC
+ <<~YAML
image: 'image:1.0'
- HEREDOC
+ YAML
end
subject(:mapper) { described_class.new(values, context) }
@@ -38,7 +40,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
it 'propagates the pipeline logger' do
process
- fetch_content_log_count = mapper
+ fetch_content_log_count = context
.logger
.observations_hash
.dig(key, 'count')
@@ -231,7 +233,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
it 'has expanset with one' do
process
- expect(mapper.expandset.size).to eq(1)
+ expect(context.expandset.size).to eq(1)
end
end
@@ -379,17 +381,28 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
end
context 'when local file path has wildcard' do
- let(:project) { create(:project, :repository) }
+ let_it_be(:project) { create(:project, :repository) }
let(:values) do
{ include: 'myfolder/*.yml' }
end
- before do
- allow_next_instance_of(Repository) do |repository|
- allow(repository).to receive(:search_files_by_wildcard_path).with('myfolder/*.yml', '123456') do
- ['myfolder/file1.yml', 'myfolder/file2.yml']
- end
+ let(:project_files) do
+ {
+ 'myfolder/file1.yml' => <<~YAML,
+ my_build:
+ script: echo Hello World
+ YAML
+ 'myfolder/file2.yml' => <<~YAML
+ my_test:
+ script: echo Hello World
+ YAML
+ }
+ end
+
+ around do |example|
+ create_and_delete_files(project, project_files) do
+ example.run
end
end
@@ -445,8 +458,20 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
it 'has expanset with two' do
process
- expect(mapper.expandset.size).to eq(2)
+ expect(context.expandset.size).to eq(2)
end
end
end
end
+
+RSpec.describe Gitlab::Ci::Config::External::Mapper, feature_category: :pipeline_authoring do
+ it_behaves_like 'gitlab_ci_config_external_mapper'
+
+ context 'when the FF ci_refactoring_external_mapper is disabled' do
+ before do
+ stub_feature_flags(ci_refactoring_external_mapper: false)
+ end
+
+ it_behaves_like 'gitlab_ci_config_external_mapper'
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/external/processor_spec.rb b/spec/lib/gitlab/ci/config/external/processor_spec.rb
index b1dff6f9723..c9efaf2e1af 100644
--- a/spec/lib/gitlab/ci/config/external/processor_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/processor_spec.rb
@@ -2,17 +2,31 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Config::External::Processor do
+RSpec.describe Gitlab::Ci::Config::External::Processor, feature_category: :pipeline_authoring do
include StubRequests
+ include RepoHelpers
- let_it_be(:project) { create(:project, :repository) }
- let_it_be_with_reload(:another_project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
- let(:sha) { '12345' }
+ let_it_be_with_reload(:project) { create(:project, :repository) }
+ let_it_be_with_reload(:another_project) { create(:project, :repository) }
+
+ let(:project_files) { {} }
+ let(:other_project_files) { {} }
+
+ let(:sha) { project.commit.sha }
let(:context_params) { { project: project, sha: sha, user: user } }
let(:context) { Gitlab::Ci::Config::External::Context.new(**context_params) }
- let(:processor) { described_class.new(values, context) }
+
+ subject(:processor) { described_class.new(values, context) }
+
+ around do |example|
+ create_and_delete_files(project, project_files) do
+ create_and_delete_files(another_project, other_project_files) do
+ example.run
+ end
+ end
+ end
before do
project.add_developer(user)
@@ -63,7 +77,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
let(:remote_file) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.gitlab-ci-1.yml' }
let(:values) { { include: remote_file, image: 'image:1.0' } }
let(:external_file_content) do
- <<-HEREDOC
+ <<-YAML
before_script:
- apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
- ruby -v
@@ -77,7 +91,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
rubocop:
script:
- bundle exec rubocop
- HEREDOC
+ YAML
end
before do
@@ -98,7 +112,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
let(:remote_file) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.gitlab-ci-1.yml' }
let(:values) { { include: remote_file, image: 'image:1.0' } }
let(:external_file_content) do
- <<-HEREDOC
+ <<-YAML
include:
- local: another-file.yml
rules:
@@ -107,7 +121,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
rspec:
script:
- bundle exec rspec
- HEREDOC
+ YAML
end
before do
@@ -127,19 +141,16 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
context 'with a valid local external file is defined' do
let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'image:1.0' } }
let(:local_file_content) do
- <<-HEREDOC
+ <<-YAML
before_script:
- apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
- ruby -v
- which ruby
- bundle install --jobs $(nproc) "${FLAGS[@]}"
- HEREDOC
+ YAML
end
- before do
- allow_any_instance_of(Gitlab::Ci::Config::External::File::Local)
- .to receive(:fetch_local_content).and_return(local_file_content)
- end
+ let(:project_files) { { '/lib/gitlab/ci/templates/template.yml' => local_file_content } }
it 'appends the file to the values' do
output = processor.perform
@@ -153,6 +164,11 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
context 'with multiple external files are defined' do
let(:remote_file) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.gitlab-ci-1.yml' }
+
+ let(:local_file_content) do
+ File.read(Rails.root.join('spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml'))
+ end
+
let(:external_files) do
[
'/spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml',
@@ -168,20 +184,21 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
end
let(:remote_file_content) do
- <<-HEREDOC
+ <<-YAML
stages:
- build
- review
- cleanup
- HEREDOC
+ YAML
end
- before do
- local_file_content = File.read(Rails.root.join('spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml'))
-
- allow_any_instance_of(Gitlab::Ci::Config::External::File::Local)
- .to receive(:fetch_local_content).and_return(local_file_content)
+ let(:project_files) do
+ {
+ '/spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml' => local_file_content
+ }
+ end
+ before do
stub_full_request(remote_file).to_return(body: remote_file_content)
end
@@ -199,10 +216,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
let(:local_file_content) { 'invalid content file ////' }
- before do
- allow_any_instance_of(Gitlab::Ci::Config::External::File::Local)
- .to receive(:fetch_local_content).and_return(local_file_content)
- end
+ let(:project_files) { { '/lib/gitlab/ci/templates/template.yml' => local_file_content } }
it 'raises an error' do
expect { processor.perform }.to raise_error(
@@ -222,9 +236,9 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
end
let(:remote_file_content) do
- <<~HEREDOC
+ <<~YAML
image: php:5-fpm-alpine
- HEREDOC
+ YAML
end
it 'takes precedence' do
@@ -244,31 +258,32 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
}
end
- before do
- allow(project.repository).to receive(:blob_data_at).with('12345', '/local/file.yml') do
- <<~HEREDOC
- include:
- - template: Ruby.gitlab-ci.yml
- - remote: http://my.domain.com/config.yml
- - project: #{another_project.full_path}
- file: /templates/my-workflow.yml
- HEREDOC
- end
-
- allow_any_instance_of(Repository).to receive(:blob_data_at).with(another_project.commit.id, '/templates/my-workflow.yml') do
- <<~HEREDOC
- include:
- - local: /templates/my-build.yml
- HEREDOC
- end
+ let(:project_files) do
+ {
+ '/local/file.yml' => <<~YAML
+ include:
+ - template: Ruby.gitlab-ci.yml
+ - remote: http://my.domain.com/config.yml
+ - project: #{another_project.full_path}
+ file: /templates/my-workflow.yml
+ YAML
+ }
+ end
- allow_any_instance_of(Repository).to receive(:blob_data_at).with(another_project.commit.id, '/templates/my-build.yml') do
- <<~HEREDOC
- my_build:
- script: echo Hello World
- HEREDOC
- end
+ let(:other_project_files) do
+ {
+ '/templates/my-workflow.yml' => <<~YAML,
+ include:
+ - local: /templates/my-build.yml
+ YAML
+ '/templates/my-build.yml' => <<~YAML
+ my_build:
+ script: echo Hello World
+ YAML
+ }
+ end
+ before do
stub_full_request('http://my.domain.com/config.yml')
.to_return(body: 'remote_build: { script: echo Hello World }')
end
@@ -299,32 +314,32 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
expect(context.includes).to contain_exactly(
{ type: :local,
location: '/local/file.yml',
- blob: "http://localhost/#{project.full_path}/-/blob/12345/local/file.yml",
- raw: "http://localhost/#{project.full_path}/-/raw/12345/local/file.yml",
+ blob: "http://localhost/#{project.full_path}/-/blob/#{sha}/local/file.yml",
+ raw: "http://localhost/#{project.full_path}/-/raw/#{sha}/local/file.yml",
extra: {},
context_project: project.full_path,
- context_sha: '12345' },
+ context_sha: sha },
{ type: :template,
location: 'Ruby.gitlab-ci.yml',
blob: nil,
raw: 'https://gitlab.com/gitlab-org/gitlab/-/raw/master/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml',
extra: {},
context_project: project.full_path,
- context_sha: '12345' },
+ context_sha: sha },
{ type: :remote,
location: 'http://my.domain.com/config.yml',
blob: nil,
raw: "http://my.domain.com/config.yml",
extra: {},
context_project: project.full_path,
- context_sha: '12345' },
+ context_sha: sha },
{ type: :file,
location: '/templates/my-workflow.yml',
blob: "http://localhost/#{another_project.full_path}/-/blob/#{another_project.commit.sha}/templates/my-workflow.yml",
raw: "http://localhost/#{another_project.full_path}/-/raw/#{another_project.commit.sha}/templates/my-workflow.yml",
extra: { project: another_project.full_path, ref: 'HEAD' },
context_project: project.full_path,
- context_sha: '12345' },
+ context_sha: sha },
{ type: :local,
location: '/templates/my-build.yml',
blob: "http://localhost/#{another_project.full_path}/-/blob/#{another_project.commit.sha}/templates/my-build.yml",
@@ -393,17 +408,17 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
}
end
+ let(:other_project_files) do
+ {
+ '/templates/my-build.yml' => <<~YAML
+ my_build:
+ script: echo Hello World
+ YAML
+ }
+ end
+
before do
another_project.add_developer(user)
-
- allow_next_instance_of(Repository) do |repository|
- allow(repository).to receive(:blob_data_at).with(another_project.commit.id, '/templates/my-build.yml') do
- <<~HEREDOC
- my_build:
- script: echo Hello World
- HEREDOC
- end
- end
end
it 'appends the file to the values' do
@@ -423,24 +438,21 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
}
end
+ let(:other_project_files) do
+ {
+ '/templates/my-build.yml' => <<~YAML,
+ my_build:
+ script: echo Hello World
+ YAML
+ '/templates/my-test.yml' => <<~YAML
+ my_test:
+ script: echo Hello World
+ YAML
+ }
+ end
+
before do
another_project.add_developer(user)
-
- allow_next_instance_of(Repository) do |repository|
- allow(repository).to receive(:blob_data_at).with(another_project.commit.id, '/templates/my-build.yml') do
- <<~HEREDOC
- my_build:
- script: echo Hello World
- HEREDOC
- end
-
- allow(repository).to receive(:blob_data_at).with(another_project.commit.id, '/templates/my-test.yml') do
- <<~HEREDOC
- my_test:
- script: echo Hello World
- HEREDOC
- end
- end
end
it 'appends the file to the values' do
@@ -458,45 +470,34 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
raw: "http://localhost/#{another_project.full_path}/-/raw/#{another_project.commit.sha}/templates/my-build.yml",
extra: { project: another_project.full_path, ref: 'HEAD' },
context_project: project.full_path,
- context_sha: '12345' },
+ context_sha: sha },
{ type: :file,
blob: "http://localhost/#{another_project.full_path}/-/blob/#{another_project.commit.sha}/templates/my-test.yml",
raw: "http://localhost/#{another_project.full_path}/-/raw/#{another_project.commit.sha}/templates/my-test.yml",
location: '/templates/my-test.yml',
extra: { project: another_project.full_path, ref: 'HEAD' },
context_project: project.full_path,
- context_sha: '12345' }
+ context_sha: sha }
)
end
end
context 'when local file path has wildcard' do
- let(:project) { create(:project, :repository) }
-
let(:values) do
{ include: 'myfolder/*.yml', image: 'image:1.0' }
end
- before do
- allow_next_instance_of(Repository) do |repository|
- allow(repository).to receive(:search_files_by_wildcard_path).with('myfolder/*.yml', sha) do
- ['myfolder/file1.yml', 'myfolder/file2.yml']
- end
-
- allow(repository).to receive(:blob_data_at).with(sha, 'myfolder/file1.yml') do
- <<~HEREDOC
- my_build:
- script: echo Hello World
- HEREDOC
- end
-
- allow(repository).to receive(:blob_data_at).with(sha, 'myfolder/file2.yml') do
- <<~HEREDOC
- my_test:
- script: echo Hello World
- HEREDOC
- end
- end
+ let(:project_files) do
+ {
+ 'myfolder/file1.yml' => <<~YAML,
+ my_build:
+ script: echo Hello World
+ YAML
+ 'myfolder/file2.yml' => <<~YAML
+ my_test:
+ script: echo Hello World
+ YAML
+ }
end
it 'fetches the matched files' do
@@ -510,18 +511,18 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
expect(context.includes).to contain_exactly(
{ type: :local,
location: 'myfolder/file1.yml',
- blob: "http://localhost/#{project.full_path}/-/blob/12345/myfolder/file1.yml",
- raw: "http://localhost/#{project.full_path}/-/raw/12345/myfolder/file1.yml",
+ blob: "http://localhost/#{project.full_path}/-/blob/#{sha}/myfolder/file1.yml",
+ raw: "http://localhost/#{project.full_path}/-/raw/#{sha}/myfolder/file1.yml",
extra: {},
context_project: project.full_path,
- context_sha: '12345' },
+ context_sha: sha },
{ type: :local,
- blob: "http://localhost/#{project.full_path}/-/blob/12345/myfolder/file2.yml",
- raw: "http://localhost/#{project.full_path}/-/raw/12345/myfolder/file2.yml",
+ blob: "http://localhost/#{project.full_path}/-/blob/#{sha}/myfolder/file2.yml",
+ raw: "http://localhost/#{project.full_path}/-/raw/#{sha}/myfolder/file2.yml",
location: 'myfolder/file2.yml',
extra: {},
context_project: project.full_path,
- context_sha: '12345' }
+ context_sha: sha }
)
end
end
diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb
index c4a6641ff6b..b48a89059bf 100644
--- a/spec/lib/gitlab/ci/config_spec.rb
+++ b/spec/lib/gitlab/ci/config_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Config do
+RSpec.describe Gitlab::Ci::Config, feature_category: :pipeline_authoring do
include StubRequests
let_it_be(:user) { create(:user) }
@@ -305,7 +305,7 @@ RSpec.describe Gitlab::Ci::Config do
it 'raises error' do
expect { config }.to raise_error(
described_class::ConfigError,
- /\!reference \["job-2", "before_script"\] is part of a circular chain/
+ /!reference \["job-2", "before_script"\] is part of a circular chain/
)
end
end
@@ -503,7 +503,7 @@ RSpec.describe Gitlab::Ci::Config do
expect { config }.to raise_error(
described_class::ConfigError,
- 'Resolving config took longer than expected'
+ 'Request timed out when fetching configuration files.'
)
end
end
diff --git a/spec/lib/gitlab/ci/cron_parser_spec.rb b/spec/lib/gitlab/ci/cron_parser_spec.rb
index 33474865a93..4b750cf3bcf 100644
--- a/spec/lib/gitlab/ci/cron_parser_spec.rb
+++ b/spec/lib/gitlab/ci/cron_parser_spec.rb
@@ -358,4 +358,22 @@ RSpec.describe Gitlab::Ci::CronParser do
end
end
end
+
+ describe '#match?' do
+ let(:run_date) { Time.zone.local(2021, 3, 2, 1, 0) }
+
+ subject(:matched) { described_class.new(cron, Gitlab::Ci::CronParser::VALID_SYNTAX_SAMPLE_TIME_ZONE).match?(run_date) }
+
+ context 'when cron matches up' do
+ let(:cron) { '0 1 2 3 *' }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when cron does not match' do
+ let(:cron) { '5 4 3 2 1' }
+
+ it { is_expected.to eq(false) }
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/environment_matcher_spec.rb b/spec/lib/gitlab/ci/environment_matcher_spec.rb
new file mode 100644
index 00000000000..172ada1b764
--- /dev/null
+++ b/spec/lib/gitlab/ci/environment_matcher_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::EnvironmentMatcher, feature_category: :continuous_integration do
+ describe '#match?' do
+ context 'when given pattern is a normal string' do
+ subject { described_class.new('production') }
+
+ it 'returns true on an exact match' do
+ expect(subject.match?('production')).to eq true
+ end
+
+ it 'returns false if not an exact match' do
+ expect(subject.match?('productiom')).to eq false
+ end
+ end
+
+ context 'when given pattern has a wildcard' do
+ it 'returns true on wildcard matches', :aggregate_failures do
+ expect(described_class.new('review/*').match?('review/123')).to eq true
+ expect(described_class.new('review/*/*').match?('review/123/456')).to eq true
+ expect(described_class.new('*-this-is-a-pattern-*').match?('abc123-this-is-a-pattern-abc123')).to eq true
+ end
+
+ it 'returns false when not a wildcard match', :aggregate_failures do
+ expect(described_class.new('review/*').match?('review123')).to eq false
+ expect(described_class.new('review/*/*').match?('review/123')).to eq false
+ expect(described_class.new('*-this-is-a-pattern-*').match?('abc123-this-is-a-pattern')).to eq false
+ end
+ end
+
+ context 'when given pattern is nil' do
+ subject { described_class.new(nil) }
+
+ it 'always returns false' do
+ expect(subject.match?('production')).to eq false
+ expect(subject.match?('review/123')).to eq false
+ end
+ end
+
+ context 'when given pattern is an empty string' do
+ subject { described_class.new('') }
+
+ it 'always returns false' do
+ expect(subject.match?('production')).to eq false
+ expect(subject.match?('review/123')).to eq false
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/lint_spec.rb b/spec/lib/gitlab/ci/lint_spec.rb
index cf07e952f26..b836ca395fa 100644
--- a/spec/lib/gitlab/ci/lint_spec.rb
+++ b/spec/lib/gitlab/ci/lint_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Lint do
+RSpec.describe Gitlab::Ci::Lint, feature_category: :pipeline_authoring do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
@@ -337,35 +337,28 @@ RSpec.describe Gitlab::Ci::Lint do
end
end
- context 'pipeline logger' do
- let(:counters) do
- {
- 'count' => a_kind_of(Numeric),
- 'avg' => a_kind_of(Numeric),
- 'sum' => a_kind_of(Numeric),
- 'max' => a_kind_of(Numeric),
- 'min' => a_kind_of(Numeric)
- }
- end
-
- let(:loggable_data) do
+ describe 'pipeline logger' do
+ let(:expected_data) do
{
'class' => 'Gitlab::Ci::Pipeline::Logger',
- 'config_build_context_duration_s' => counters,
- 'config_build_variables_duration_s' => counters,
- 'config_compose_duration_s' => counters,
- 'config_expand_duration_s' => counters,
- 'config_external_process_duration_s' => counters,
- 'config_stages_inject_duration_s' => counters,
- 'config_tags_resolve_duration_s' => counters,
- 'config_yaml_extend_duration_s' => counters,
- 'config_yaml_load_duration_s' => counters,
+ 'config_build_context_duration_s' => a_kind_of(Numeric),
+ 'config_build_variables_duration_s' => a_kind_of(Numeric),
+ 'config_root_duration_s' => a_kind_of(Numeric),
+ 'config_root_compose_duration_s' => a_kind_of(Numeric),
+ 'config_root_compose_jobs_factory_duration_s' => a_kind_of(Numeric),
+ 'config_root_compose_jobs_create_duration_s' => a_kind_of(Numeric),
+ 'config_expand_duration_s' => a_kind_of(Numeric),
+ 'config_external_process_duration_s' => a_kind_of(Numeric),
+ 'config_stages_inject_duration_s' => a_kind_of(Numeric),
+ 'config_tags_resolve_duration_s' => a_kind_of(Numeric),
+ 'config_yaml_extend_duration_s' => a_kind_of(Numeric),
+ 'config_yaml_load_duration_s' => a_kind_of(Numeric),
'pipeline_creation_caller' => 'Gitlab::Ci::Lint',
'pipeline_creation_service_duration_s' => a_kind_of(Numeric),
'pipeline_persisted' => false,
'pipeline_source' => 'unknown',
'project_id' => project&.id,
- 'yaml_process_duration_s' => counters
+ 'yaml_process_duration_s' => a_kind_of(Numeric)
}
end
@@ -403,7 +396,7 @@ RSpec.describe Gitlab::Ci::Lint do
end
it 'creates a log entry' do
- expect(Gitlab::AppJsonLogger).to receive(:info).with(loggable_data)
+ expect(Gitlab::AppJsonLogger).to receive(:info).with(a_hash_including(expected_data))
validate
end
@@ -424,11 +417,11 @@ RSpec.describe Gitlab::Ci::Lint do
let(:project) { nil }
let(:project_nil_loggable_data) do
- loggable_data.except('project_id')
+ expected_data.except('project_id')
end
it 'creates a log entry without project_id' do
- expect(Gitlab::AppJsonLogger).to receive(:info).with(project_nil_loggable_data)
+ expect(Gitlab::AppJsonLogger).to receive(:info).with(a_hash_including(project_nil_loggable_data))
validate
end
diff --git a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb
index f09b85aa2c7..dacbe07c8b3 100644
--- a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb
@@ -2,7 +2,7 @@
require 'fast_spec_helper'
-RSpec.describe Gitlab::Ci::Parsers::Sbom::CyclonedxProperties do
+RSpec.describe Gitlab::Ci::Parsers::Sbom::CyclonedxProperties, feature_category: :dependency_management do
subject(:parse_source_from_properties) { described_class.parse_source(properties) }
context 'when properties are nil' do
diff --git a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb
index 0b094880f69..d06537ac330 100644
--- a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx do
+RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx, feature_category: :dependency_management do
let(:report) { instance_double('Gitlab::Ci::Reports::Sbom::Report') }
let(:report_data) { base_report_data }
let(:raw_report_data) { report_data.to_json }
diff --git a/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb
index e12fa380209..bc97eb2d950 100644
--- a/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb
@@ -2,7 +2,7 @@
require 'fast_spec_helper'
-RSpec.describe Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning do
+RSpec.describe Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning, feature_category: :dependency_management do
subject { described_class.source(property_data) }
context 'when all property data is present' do
diff --git a/spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb
index f58a463f047..712dc00ec7a 100644
--- a/spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb
@@ -2,7 +2,8 @@
require "spec_helper"
-RSpec.describe Gitlab::Ci::Parsers::Sbom::Validators::CyclonedxSchemaValidator do
+RSpec.describe Gitlab::Ci::Parsers::Sbom::Validators::CyclonedxSchemaValidator,
+ feature_category: :dependency_management do
# Reports should be valid or invalid according to the specification at
# https://cyclonedx.org/docs/1.4/json/
diff --git a/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb b/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
index e730afc72b5..c94ed1f8d6d 100644
--- a/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
@@ -95,7 +95,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
context 'when all files under schema path are explicitly listed' do
# We only care about the part that comes before report-format.json
# https://rubular.com/r/N8Juz7r8hYDYgD
- filename_regex = /(?<report_type>[-\w]*)\-report-format.json/
+ filename_regex = /(?<report_type>[-\w]*)-report-format.json/
versions = Dir.glob(File.join(schema_path, "*", File::SEPARATOR)).map { |path| path.split("/").last }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb
index 15df5b2f68c..74a68f28f3e 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb
@@ -10,13 +10,15 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::AssignPartition do
Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user)
end
- let(:pipeline) { build(:ci_pipeline, project: project) }
+ let(:pipeline) { build(:ci_pipeline, project: project, partition_id: nil) }
let(:step) { described_class.new(pipeline, command) }
let(:current_partition_id) { 123 }
describe '#perform!' do
+ include Ci::PartitioningHelpers
+
before do
- allow(Ci::Pipeline).to receive(:current_partition_value) { current_partition_id }
+ stub_current_partition_id(current_partition_id)
end
subject { step.perform! }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/build/associations_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/build/associations_spec.rb
index 32c92724f62..b2128f77960 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/build/associations_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/build/associations_spec.rb
@@ -2,11 +2,12 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Pipeline::Chain::Build::Associations do
+RSpec.describe Gitlab::Ci::Pipeline::Chain::Build::Associations, feature_category: :continuous_integration do
let_it_be_with_reload(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user, developer_projects: [project]) }
- let(:pipeline) { Ci::Pipeline.new }
+ # Assigning partition_id here to validate it is being propagated correctly
+ let(:pipeline) { Ci::Pipeline.new(partition_id: ci_testing_partition_id) }
let(:bridge) { nil }
let(:variables_attributes) do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb
index fc3de2a14cd..16deeb6916f 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb
@@ -173,21 +173,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::CancelPendingPipelines do
expect(build_statuses(prev_pipeline)).to contain_exactly('running', 'success', 'created')
expect(build_statuses(parent_pipeline)).to contain_exactly('running', 'running')
end
-
- context 'when feature flag ci_skip_auto_cancelation_on_child_pipelines is disabled' do
- before do
- stub_feature_flags(ci_skip_auto_cancelation_on_child_pipelines: false)
- end
-
- it 'does not cancel the parent pipeline' do
- expect(build_statuses(parent_pipeline)).to contain_exactly('running', 'running')
-
- perform
-
- expect(build_statuses(prev_pipeline)).to contain_exactly('success', 'canceled', 'canceled')
- expect(build_statuses(parent_pipeline)).to contain_exactly('running', 'running')
- end
- end
end
context 'when the previous pipeline source is webide' do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
index 9126c6dab21..68158503628 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
@@ -374,21 +374,57 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do
end
end
+ describe '#observe_creation_duration' do
+ let(:histogram) { instance_double(Prometheus::Client::Histogram) }
+ let(:duration) { 1.hour }
+ let(:command) { described_class.new(project: project) }
+
+ subject(:observe_creation_duration) do
+ command.observe_creation_duration(duration)
+ end
+
+ it 'records the duration as histogram' do
+ expect(::Gitlab::Ci::Pipeline::Metrics).to receive(:pipeline_creation_duration_histogram)
+ .and_return(histogram)
+ expect(histogram).to receive(:observe)
+ .with({ gitlab: 'false' }, duration.seconds)
+
+ observe_creation_duration
+ end
+
+ context 'when project is gitlab-org/gitlab' do
+ before do
+ allow(project).to receive(:full_path).and_return('gitlab-org/gitlab')
+ end
+
+ it 'tracks the duration with the expected label' do
+ expect(::Gitlab::Ci::Pipeline::Metrics).to receive(:pipeline_creation_duration_histogram)
+ .and_return(histogram)
+ expect(histogram).to receive(:observe)
+ .with({ gitlab: 'true' }, duration.seconds)
+
+ observe_creation_duration
+ end
+ end
+ end
+
describe '#observe_step_duration' do
+ let(:histogram) { instance_double(Prometheus::Client::Histogram) }
+ let(:duration) { 1.hour }
+ let(:command) { described_class.new }
+
+ subject(:observe_step_duration) do
+ command.observe_step_duration(Gitlab::Ci::Pipeline::Chain::Build, duration)
+ end
+
context 'when ci_pipeline_creation_step_duration_tracking is enabled' do
it 'adds the duration to the step duration histogram' do
- histogram = instance_double(Prometheus::Client::Histogram)
- duration = 1.hour
-
expect(::Gitlab::Ci::Pipeline::Metrics).to receive(:pipeline_creation_step_duration_histogram)
.and_return(histogram)
expect(histogram).to receive(:observe)
.with({ step: 'Gitlab::Ci::Pipeline::Chain::Build' }, duration.seconds)
- described_class.new.observe_step_duration(
- Gitlab::Ci::Pipeline::Chain::Build,
- duration
- )
+ observe_step_duration
end
end
@@ -398,14 +434,9 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do
end
it 'does nothing' do
- duration = 1.hour
-
expect(::Gitlab::Ci::Pipeline::Metrics).not_to receive(:pipeline_creation_step_duration_histogram)
- described_class.new.observe_step_duration(
- Gitlab::Ci::Pipeline::Chain::Build,
- duration
- )
+ observe_step_duration
end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb
index 7fb5b0b4200..39520149032 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb
@@ -36,9 +36,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments, :aggregate_failu
end
context 'and the pipeline is for a merge request' do
- let(:command) do
- Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user, merge_request: merge_request)
- end
+ let(:pipeline) { build(:ci_pipeline, project: project, stages: [stage], merge_request: merge_request) }
it 'associates the environment with the merge request' do
expect { subject }.to change { Environment.count }.by(1)
@@ -62,9 +60,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments, :aggregate_failu
end
context 'and the pipeline is for a merge request' do
- let(:command) do
- Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user, merge_request: merge_request)
- end
+ let(:pipeline) { build(:ci_pipeline, project: project, stages: [stage], merge_request: merge_request) }
it 'does not associate the environment with the merge request' do
expect { subject }.not_to change { Environment.count }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
index 7aaeee32f49..9373888aada 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
@@ -2,8 +2,8 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do
- let_it_be(:project, reload: true) { create(:project, :repository) }
+RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities, feature_category: :pipeline_execution do
+ let(:project) { create(:project, :test_repo) }
let_it_be(:user) { create(:user) }
let(:pipeline) do
diff --git a/spec/lib/gitlab/ci/pipeline/logger_spec.rb b/spec/lib/gitlab/ci/pipeline/logger_spec.rb
index 3af0ebe7484..1c285889d1b 100644
--- a/spec/lib/gitlab/ci/pipeline/logger_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/logger_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
+RSpec.describe ::Gitlab::Ci::Pipeline::Logger, feature_category: :continuous_integration do
let_it_be(:project) { build_stubbed(:project) }
let_it_be(:pipeline) { build_stubbed(:ci_pipeline, project: project) }
@@ -22,61 +22,54 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
end
it 'records durations of instrumented operations' do
- loggable_data = {
+ logger.instrument(:expensive_operation) { 123 }
+
+ expected_data = {
'expensive_operation_duration_s' => {
'count' => 1,
- 'sum' => a_kind_of(Numeric),
- 'avg' => a_kind_of(Numeric),
'max' => a_kind_of(Numeric),
- 'min' => a_kind_of(Numeric)
+ 'sum' => a_kind_of(Numeric)
}
}
-
- logger.instrument(:expensive_operation) { 123 }
- expect(logger.observations_hash).to match(a_hash_including(loggable_data))
+ expect(logger.observations_hash).to match(a_hash_including(expected_data))
end
it 'raises an error when block is not provided' do
expect { logger.instrument(:expensive_operation) }
.to raise_error(ArgumentError, 'block not given')
end
+
+ context 'when once: true' do
+ it 'logs only one observation' do
+ logger.instrument(:expensive_operation, once: true) { 123 }
+ logger.instrument(:expensive_operation, once: true) { 123 }
+
+ expected_data = {
+ 'expensive_operation_duration_s' => a_kind_of(Numeric)
+ }
+ expect(logger.observations_hash).to match(a_hash_including(expected_data))
+ end
+ end
end
- describe '#instrument_with_sql', :request_store do
- subject(:instrument_with_sql) do
- logger.instrument_with_sql(:expensive_operation, &operation)
+ describe '#instrument_once_with_sql', :request_store do
+ subject(:instrument_once_with_sql) do
+ logger.instrument_once_with_sql(:expensive_operation, &operation)
end
- def loggable_data(count:, db_count: nil)
+ def expected_data(count:, db_count: nil)
database_name = Ci::ApplicationRecord.connection.pool.db_config.name
- keys = %W[
- expensive_operation_duration_s
- expensive_operation_db_count
- expensive_operation_db_primary_count
- expensive_operation_db_primary_duration_s
- expensive_operation_db_#{database_name}_count
- expensive_operation_db_#{database_name}_duration_s
- ]
-
- data = keys.each.with_object({}) do |key, accumulator|
- accumulator[key] = {
- 'count' => count,
- 'avg' => a_kind_of(Numeric),
- 'sum' => a_kind_of(Numeric),
- 'max' => a_kind_of(Numeric),
- 'min' => a_kind_of(Numeric)
- }
- end
-
- if db_count
- data['expensive_operation_db_count']['max'] = db_count
- data['expensive_operation_db_count']['min'] = db_count
- data['expensive_operation_db_count']['avg'] = db_count
- data['expensive_operation_db_count']['sum'] = count * db_count
- end
+ total_db_count = count * db_count if db_count
- data
+ {
+ "expensive_operation_duration_s" => a_kind_of(Numeric),
+ "expensive_operation_db_count" => total_db_count || a_kind_of(Numeric),
+ "expensive_operation_db_primary_count" => a_kind_of(Numeric),
+ "expensive_operation_db_primary_duration_s" => a_kind_of(Numeric),
+ "expensive_operation_db_#{database_name}_count" => a_kind_of(Numeric),
+ "expensive_operation_db_#{database_name}_duration_s" => a_kind_of(Numeric)
+ }
end
context 'with a single query' do
@@ -85,10 +78,10 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
it { is_expected.to eq(operation.call) }
it 'includes SQL metrics' do
- instrument_with_sql
+ instrument_once_with_sql
expect(logger.observations_hash)
- .to match(a_hash_including(loggable_data(count: 1, db_count: 1)))
+ .to match(a_hash_including(expected_data(count: 1, db_count: 1)))
end
end
@@ -98,21 +91,10 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
it { is_expected.to eq(operation.call) }
it 'includes SQL metrics' do
- instrument_with_sql
-
- expect(logger.observations_hash)
- .to match(a_hash_including(loggable_data(count: 1, db_count: 2)))
- end
- end
-
- context 'with multiple observations' do
- let(:operation) { -> { Ci::Build.count + Ci::Bridge.count } }
-
- it 'includes SQL metrics' do
- 2.times { logger.instrument_with_sql(:expensive_operation, &operation) }
+ instrument_once_with_sql
expect(logger.observations_hash)
- .to match(a_hash_including(loggable_data(count: 2, db_count: 2)))
+ .to match(a_hash_including(expected_data(count: 1, db_count: 2)))
end
end
@@ -122,7 +104,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
it { is_expected.to eq(operation.call) }
it 'does not include SQL metrics' do
- instrument_with_sql
+ instrument_once_with_sql
expect(logger.observations_hash.keys)
.to match_array(['expensive_operation_duration_s'])
@@ -132,14 +114,40 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
describe '#observe' do
it 'records durations of observed operations' do
- loggable_data = {
+ expect(logger.observe(:pipeline_creation_duration_s, 30)).to be_truthy
+
+ expected_data = {
'pipeline_creation_duration_s' => {
- 'avg' => 30, 'sum' => 30, 'count' => 1, 'max' => 30, 'min' => 30
+ 'sum' => 30, 'count' => 1, 'max' => 30
}
}
+ expect(logger.observations_hash).to match(a_hash_including(expected_data))
+ end
- expect(logger.observe(:pipeline_creation_duration_s, 30)).to be_truthy
- expect(logger.observations_hash).to match(a_hash_including(loggable_data))
+ context 'when once: true' do
+ it 'records the latest observation' do
+ expect(logger.observe(:pipeline_creation_duration_s, 20, once: true)).to be_truthy
+ expect(logger.observe(:pipeline_creation_duration_s, 30, once: true)).to be_truthy
+
+ expected_data = {
+ 'pipeline_creation_duration_s' => 30
+ }
+ expect(logger.observations_hash).to match(a_hash_including(expected_data))
+ end
+
+ it 'logs data as expected' do
+ expect(logger.observe(:pipeline_creation_duration_s, 30, once: true)).to be_truthy
+ expect(logger.observe(:pipeline_operation_x_duration_s, 20)).to be_truthy
+ expect(logger.observe(:pipeline_operation_x_duration_s, 20)).to be_truthy
+
+ expected_data = {
+ 'pipeline_creation_duration_s' => 30,
+ 'pipeline_operation_x_duration_s' => {
+ 'sum' => 40, 'count' => 2, 'max' => 20
+ }
+ }
+ expect(logger.observations_hash).to match(a_hash_including(expected_data))
+ end
end
end
@@ -158,8 +166,11 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
context 'when the feature flag is enabled' do
let(:flag) { true }
- let(:loggable_data) do
+ let(:expected_data) do
{
+ 'correlation_id' => a_kind_of(String),
+ 'meta.project' => project.full_path,
+ 'meta.root_namespace' => project.root_namespace.full_path,
'class' => described_class.name.to_s,
'pipeline_id' => pipeline.id,
'pipeline_persisted' => true,
@@ -168,10 +179,10 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
'pipeline_creation_caller' => 'source',
'pipeline_source' => pipeline.source,
'pipeline_save_duration_s' => {
- 'avg' => 60, 'sum' => 60, 'count' => 1, 'max' => 60, 'min' => 60
+ 'sum' => 60, 'count' => 1, 'max' => 60
},
'pipeline_creation_duration_s' => {
- 'avg' => 20, 'sum' => 40, 'count' => 2, 'max' => 30, 'min' => 10
+ 'sum' => 40, 'count' => 2, 'max' => 30
}
}
end
@@ -179,7 +190,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
it 'logs to application.json' do
expect(Gitlab::AppJsonLogger)
.to receive(:info)
- .with(a_hash_including(loggable_data))
+ .with(a_hash_including(expected_data))
.and_call_original
expect(commit).to be_truthy
@@ -200,28 +211,43 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
expect(Gitlab::AppJsonLogger)
.to receive(:info)
- .with(a_hash_including(loggable_data))
+ .with(a_hash_including(expected_data))
.and_call_original
expect(commit).to be_truthy
end
+
+ context 'with unexistent observations in condition' do
+ it 'does not commit the log' do
+ logger.log_when do |observations|
+ value = observations['non_existent_value']
+ next false unless value
+
+ value > 0
+ end
+
+ expect(Gitlab::AppJsonLogger).not_to receive(:info)
+
+ expect(commit).to be_falsey
+ end
+ end
end
context 'when project is not passed and pipeline is not persisted' do
let(:project) {}
let(:pipeline) { build(:ci_pipeline) }
- let(:loggable_data) do
+ let(:expected_data) do
{
'class' => described_class.name.to_s,
'pipeline_persisted' => false,
'pipeline_creation_service_duration_s' => a_kind_of(Numeric),
'pipeline_creation_caller' => 'source',
'pipeline_save_duration_s' => {
- 'avg' => 60, 'sum' => 60, 'count' => 1, 'max' => 60, 'min' => 60
+ 'sum' => 60, 'count' => 1, 'max' => 60
},
'pipeline_creation_duration_s' => {
- 'avg' => 20, 'sum' => 40, 'count' => 2, 'max' => 30, 'min' => 10
+ 'sum' => 40, 'count' => 2, 'max' => 30
}
}
end
@@ -229,7 +255,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
it 'logs to application.json' do
expect(Gitlab::AppJsonLogger)
.to receive(:info)
- .with(a_hash_including(loggable_data))
+ .with(a_hash_including(expected_data))
.and_call_original
expect(commit).to be_truthy
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb
index 910c12389c3..fb8020bf43e 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb
@@ -6,8 +6,9 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build::Cache do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:head_sha) { project.repository.head_commit.id }
let_it_be(:pipeline) { create(:ci_pipeline, project: project, sha: head_sha) }
+ let(:index) { 1 }
- let(:processor) { described_class.new(pipeline, config) }
+ let(:processor) { described_class.new(pipeline, config, index) }
describe '#attributes' do
subject { processor.attributes }
@@ -40,10 +41,12 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build::Cache do
{ key: { files: files } }
end
- it 'uses default key' do
- expected = { key: 'default' }
+ context 'without a prefix' do
+ it 'uses default key with an index as a prefix' do
+ expected = { key: '1-default' }
- is_expected.to include(expected)
+ is_expected.to include(expected)
+ end
end
end
@@ -57,13 +60,15 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build::Cache do
}
end
- it 'builds a string key' do
- expected = {
- key: '703ecc8fef1635427a1f86a8a1a308831c122392',
- paths: ['vendor/ruby']
- }
+ context 'without a prefix' do
+ it 'builds a string key with an index as a prefix' do
+ expected = {
+ key: '1-703ecc8fef1635427a1f86a8a1a308831c122392',
+ paths: ['vendor/ruby']
+ }
- is_expected.to include(expected)
+ is_expected.to include(expected)
+ end
end
end
@@ -107,10 +112,12 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build::Cache do
}
end
- it 'builds a string key' do
- expected = { key: '74bf43fb1090f161bdd4e265802775dbda2f03d1' }
+ context 'without a prefix' do
+ it 'builds a string key with an index as a prefix' do
+ expected = { key: '1-74bf43fb1090f161bdd4e265802775dbda2f03d1' }
- is_expected.to include(expected)
+ is_expected.to include(expected)
+ end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
index 75f6a773c2d..1f7f800e238 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
+RSpec.describe Gitlab::Ci::Pipeline::Seed::Build, feature_category: :pipeline_authoring do
let_it_be_with_reload(:project) { create(:project, :repository) }
let_it_be(:head_sha) { project.repository.head_commit.id }
@@ -11,861 +11,954 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
let(:seed_context) { Gitlab::Ci::Pipeline::Seed::Context.new(pipeline, root_variables: root_variables) }
let(:attributes) { { name: 'rspec', ref: 'master', scheduling_type: :stage, when: 'on_success' } }
let(:previous_stages) { [] }
- let(:current_stage) { double(seeds_names: [attributes[:name]]) }
+ let(:current_stage) { instance_double(Gitlab::Ci::Pipeline::Seed::Stage, seeds_names: [attributes[:name]]) }
+ let(:current_ci_stage) { build(:ci_stage, pipeline: pipeline) }
- let(:seed_build) { described_class.new(seed_context, attributes, previous_stages + [current_stage]) }
+ let(:seed_build) { described_class.new(seed_context, attributes, previous_stages + [current_stage], current_ci_stage) }
- describe '#attributes' do
- subject { seed_build.attributes }
+ shared_examples 'build seed' do
+ describe '#attributes' do
+ subject { seed_build.attributes }
- it { is_expected.to be_a(Hash) }
- it { is_expected.to include(:name, :project, :ref) }
+ it { is_expected.to be_a(Hash) }
+ it { is_expected.to include(:name, :project, :ref) }
- context 'with job:when' do
- let(:attributes) { { name: 'rspec', ref: 'master', when: 'on_failure' } }
+ context 'with job:when' do
+ let(:attributes) { { name: 'rspec', ref: 'master', when: 'on_failure' } }
- it { is_expected.to include(when: 'on_failure') }
- end
-
- context 'with job:when:delayed' do
- let(:attributes) { { name: 'rspec', ref: 'master', when: 'delayed', start_in: '3 hours' } }
-
- it { is_expected.to include(when: 'delayed', start_in: '3 hours') }
- end
-
- context 'with job:rules:[when:]' do
- context 'is matched' do
- let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR == null', when: 'always' }] } }
-
- it { is_expected.to include(when: 'always') }
+ it { is_expected.to include(when: 'on_failure') }
end
- context 'is not matched' do
- let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR != null', when: 'always' }] } }
-
- it { is_expected.to include(when: 'never') }
- end
- end
-
- context 'with job:rules:[when:delayed]' do
- context 'is matched' do
- let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR == null', when: 'delayed', start_in: '3 hours' }] } }
+ context 'with job:when:delayed' do
+ let(:attributes) { { name: 'rspec', ref: 'master', when: 'delayed', options: { start_in: '3 hours' } } }
it { is_expected.to include(when: 'delayed', options: { start_in: '3 hours' }) }
end
- context 'is not matched' do
- let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR != null', when: 'delayed', start_in: '3 hours' }] } }
-
- it { is_expected.to include(when: 'never') }
- end
- end
-
- context 'with job: rules but no explicit when:' do
- let(:base_attributes) { { name: 'rspec', ref: 'master' } }
-
- context 'with a manual job' do
- context 'with a matched rule' do
- let(:attributes) { base_attributes.merge(when: 'manual', rules: [{ if: '$VAR == null' }]) }
+ context 'with job:rules:[when:]' do
+ context 'is matched' do
+ let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR == null', when: 'always' }] } }
- it { is_expected.to include(when: 'manual') }
+ it { is_expected.to include(when: 'always') }
end
context 'is not matched' do
- let(:attributes) { base_attributes.merge(when: 'manual', rules: [{ if: '$VAR != null' }]) }
+ let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR != null', when: 'always' }] } }
it { is_expected.to include(when: 'never') }
end
end
- context 'with an automatic job' do
+ context 'with job:rules:[when:delayed]' do
context 'is matched' do
- let(:attributes) { base_attributes.merge(when: 'on_success', rules: [{ if: '$VAR == null' }]) }
+ let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR == null', when: 'delayed', start_in: '3 hours' }] } }
- it { is_expected.to include(when: 'on_success') }
+ it { is_expected.to include(when: 'delayed', options: { start_in: '3 hours' }) }
end
context 'is not matched' do
- let(:attributes) { base_attributes.merge(when: 'on_success', rules: [{ if: '$VAR != null' }]) }
+ let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$VAR != null', when: 'delayed', start_in: '3 hours' }] } }
it { is_expected.to include(when: 'never') }
end
end
- end
- context 'with job:rules:[variables:]' do
- let(:attributes) do
- { name: 'rspec',
- ref: 'master',
- job_variables: [{ key: 'VAR1', value: 'var 1' },
- { key: 'VAR2', value: 'var 2' }],
- rules: [{ if: '$VAR == null', variables: { VAR1: 'new var 1', VAR3: 'var 3' } }] }
- end
+ context 'with job: rules but no explicit when:' do
+ let(:base_attributes) { { name: 'rspec', ref: 'master' } }
- it do
- is_expected.to include(yaml_variables: [{ key: 'VAR1', value: 'new var 1' },
- { key: 'VAR3', value: 'var 3' },
- { key: 'VAR2', value: 'var 2' }])
- end
- end
+ context 'with a manual job' do
+ context 'with a matched rule' do
+ let(:attributes) { base_attributes.merge(when: 'manual', rules: [{ if: '$VAR == null' }]) }
- context 'with job:tags' do
- let(:attributes) do
- {
- name: 'rspec',
- ref: 'master',
- job_variables: [{ key: 'VARIABLE', value: 'value' }],
- tag_list: ['static-tag', '$VARIABLE', '$NO_VARIABLE']
- }
- end
+ it { is_expected.to include(when: 'manual') }
+ end
- it { is_expected.to include(tag_list: ['static-tag', 'value', '$NO_VARIABLE']) }
- it { is_expected.to include(yaml_variables: [{ key: 'VARIABLE', value: 'value' }]) }
- end
+ context 'is not matched' do
+ let(:attributes) { base_attributes.merge(when: 'manual', rules: [{ if: '$VAR != null' }]) }
- context 'with cache:key' do
- let(:attributes) do
- {
- name: 'rspec',
- ref: 'master',
- cache: [{
- key: 'a-value'
- }]
- }
- end
+ it { is_expected.to include(when: 'never') }
+ end
+ end
+
+ context 'with an automatic job' do
+ context 'is matched' do
+ let(:attributes) { base_attributes.merge(when: 'on_success', rules: [{ if: '$VAR == null' }]) }
+
+ it { is_expected.to include(when: 'on_success') }
+ end
- it { is_expected.to include(options: { cache: [a_hash_including(key: 'a-value')] }) }
+ context 'is not matched' do
+ let(:attributes) { base_attributes.merge(when: 'on_success', rules: [{ if: '$VAR != null' }]) }
+
+ it { is_expected.to include(when: 'never') }
+ end
+ end
+ end
- context 'with cache:key:files' do
+ context 'with job:rules:[variables:]' do
let(:attributes) do
- {
- name: 'rspec',
+ { name: 'rspec',
ref: 'master',
- cache: [{
- key: {
- files: ['VERSION']
- }
- }]
- }
+ job_variables: [{ key: 'VAR1', value: 'var 1' },
+ { key: 'VAR2', value: 'var 2' }],
+ rules: [{ if: '$VAR == null', variables: { VAR1: 'new var 1', VAR3: 'var 3' } }] }
end
- it 'includes cache options' do
- cache_options = {
- options: {
- cache: [a_hash_including(key: 'f155568ad0933d8358f66b846133614f76dd0ca4')]
- }
- }
+ it do
+ is_expected.to include(yaml_variables: [{ key: 'VAR1', value: 'new var 1' },
+ { key: 'VAR3', value: 'var 3' },
+ { key: 'VAR2', value: 'var 2' }])
+ end
- is_expected.to include(cache_options)
+ it 'expects the same results on to_resource' do
+ expect(seed_build.to_resource.yaml_variables).to include({ key: 'VAR1', value: 'new var 1' },
+ { key: 'VAR3', value: 'var 3' },
+ { key: 'VAR2', value: 'var 2' })
end
end
- context 'with cache:key:prefix' do
+ context 'with job:tags' do
let(:attributes) do
{
name: 'rspec',
ref: 'master',
- cache: [{
- key: {
- prefix: 'something'
- }
- }]
+ job_variables: [{ key: 'VARIABLE', value: 'value' }],
+ tag_list: ['static-tag', '$VARIABLE', '$NO_VARIABLE']
}
end
- it { is_expected.to include(options: { cache: [a_hash_including( key: 'something-default' )] }) }
+ it { is_expected.to include(tag_list: ['static-tag', 'value', '$NO_VARIABLE']) }
+ it { is_expected.to include(yaml_variables: [{ key: 'VARIABLE', value: 'value' }]) }
end
- context 'with cache:key:files and prefix' do
+ context 'with cache:key' do
let(:attributes) do
{
name: 'rspec',
ref: 'master',
cache: [{
- key: {
- files: ['VERSION'],
- prefix: 'something'
- }
+ key: 'a-value'
}]
}
end
- it 'includes cache options' do
- cache_options = {
- options: {
- cache: [a_hash_including(key: 'something-f155568ad0933d8358f66b846133614f76dd0ca4')]
+ it { is_expected.to include(options: { cache: [a_hash_including(key: 'a-value')] }) }
+
+ context 'with cache:key:files' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ ref: 'master',
+ cache: [{
+ key: {
+ files: ['VERSION']
+ }
+ }]
}
- }
+ end
- is_expected.to include(cache_options)
- end
- end
- end
+ it 'includes cache options' do
+ cache_options = {
+ options: {
+ cache: [a_hash_including(key: '0-f155568ad0933d8358f66b846133614f76dd0ca4')]
+ }
+ }
- context 'with empty cache' do
- let(:attributes) do
- {
- name: 'rspec',
- ref: 'master',
- cache: {}
- }
- end
+ is_expected.to include(cache_options)
+ end
+ end
- it { is_expected.to include({}) }
- end
+ context 'with cache:key:prefix' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ ref: 'master',
+ cache: [{
+ key: {
+ prefix: 'something'
+ }
+ }]
+ }
+ end
- context 'with allow_failure' do
- let(:options) do
- { allow_failure_criteria: { exit_codes: [42] } }
- end
+ it { is_expected.to include(options: { cache: [a_hash_including( key: 'something-default' )] }) }
+ end
- let(:rules) do
- [{ if: '$VAR == null', when: 'always' }]
- end
+ context 'with cache:key:files and prefix' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ ref: 'master',
+ cache: [{
+ key: {
+ files: ['VERSION'],
+ prefix: 'something'
+ }
+ }]
+ }
+ end
- let(:attributes) do
- {
- name: 'rspec',
- ref: 'master',
- options: options,
- rules: rules
- }
- end
+ it 'includes cache options' do
+ cache_options = {
+ options: {
+ cache: [a_hash_including(key: 'something-f155568ad0933d8358f66b846133614f76dd0ca4')]
+ }
+ }
- context 'when rules does not override allow_failure' do
- it { is_expected.to match a_hash_including(options: options) }
+ is_expected.to include(cache_options)
+ end
+ end
end
- context 'when rules set allow_failure to true' do
- let(:rules) do
- [{ if: '$VAR == null', when: 'always', allow_failure: true }]
+ context 'with empty cache' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ ref: 'master',
+ cache: {}
+ }
end
- it { is_expected.to match a_hash_including(options: { allow_failure_criteria: nil }) }
+ it { is_expected.to include({}) }
end
- context 'when rules set allow_failure to false' do
- let(:rules) do
- [{ if: '$VAR == null', when: 'always', allow_failure: false }]
+ context 'with allow_failure' do
+ let(:options) do
+ { allow_failure_criteria: { exit_codes: [42] } }
end
- it { is_expected.to match a_hash_including(options: { allow_failure_criteria: nil }) }
- end
- end
-
- context 'with workflow:rules:[variables:]' do
- let(:attributes) do
- { name: 'rspec',
- ref: 'master',
- yaml_variables: [{ key: 'VAR2', value: 'var 2' },
- { key: 'VAR3', value: 'var 3' }],
- job_variables: [{ key: 'VAR2', value: 'var 2' },
- { key: 'VAR3', value: 'var 3' }],
- root_variables_inheritance: root_variables_inheritance }
- end
+ let(:rules) do
+ [{ if: '$VAR == null', when: 'always' }]
+ end
- context 'when the pipeline has variables' do
- let(:root_variables) do
- [{ key: 'VAR1', value: 'var overridden pipeline 1' },
- { key: 'VAR2', value: 'var pipeline 2' },
- { key: 'VAR3', value: 'var pipeline 3' },
- { key: 'VAR4', value: 'new var pipeline 4' }]
+ let(:attributes) do
+ {
+ name: 'rspec',
+ ref: 'master',
+ options: options,
+ rules: rules
+ }
end
- context 'when root_variables_inheritance is true' do
- let(:root_variables_inheritance) { true }
+ context 'when rules does not override allow_failure' do
+ it { is_expected.to match a_hash_including(options: options) }
+ end
- it 'returns calculated yaml variables' do
- expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR1', value: 'var overridden pipeline 1' },
- { key: 'VAR2', value: 'var 2' },
- { key: 'VAR3', value: 'var 3' },
- { key: 'VAR4', value: 'new var pipeline 4' }]
- )
+ context 'when rules set allow_failure to true' do
+ let(:rules) do
+ [{ if: '$VAR == null', when: 'always', allow_failure: true }]
end
- end
- context 'when root_variables_inheritance is false' do
- let(:root_variables_inheritance) { false }
+ it { is_expected.to match a_hash_including(options: { allow_failure_criteria: nil }) }
- it 'returns job variables' do
- expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR2', value: 'var 2' },
- { key: 'VAR3', value: 'var 3' }]
- )
- end
- end
+ context 'when options contain other static values' do
+ let(:options) do
+ { image: 'busybox', allow_failure_criteria: { exit_codes: [42] } }
+ end
- context 'when root_variables_inheritance is an array' do
- let(:root_variables_inheritance) { %w(VAR1 VAR2 VAR3) }
+ it { is_expected.to match a_hash_including(options: { image: 'busybox', allow_failure_criteria: nil }) }
- it 'returns calculated yaml variables' do
- expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR1', value: 'var overridden pipeline 1' },
- { key: 'VAR2', value: 'var 2' },
- { key: 'VAR3', value: 'var 3' }]
- )
+ it 'deep merges options when exporting to_resource' do
+ expect(seed_build.to_resource.options).to match a_hash_including(
+ image: 'busybox', allow_failure_criteria: nil
+ )
+ end
end
end
- end
- context 'when the pipeline has not a variable' do
- let(:root_variables_inheritance) { true }
+ context 'when rules set allow_failure to false' do
+ let(:rules) do
+ [{ if: '$VAR == null', when: 'always', allow_failure: false }]
+ end
- it 'returns seed yaml variables' do
- expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR2', value: 'var 2' },
- { key: 'VAR3', value: 'var 3' }])
+ it { is_expected.to match a_hash_including(options: { allow_failure_criteria: nil }) }
end
end
- end
- context 'when the job rule depends on variables' do
- let(:attributes) do
- { name: 'rspec',
- ref: 'master',
- yaml_variables: [{ key: 'VAR1', value: 'var 1' }],
- job_variables: [{ key: 'VAR1', value: 'var 1' }],
- root_variables_inheritance: root_variables_inheritance,
- rules: rules }
- end
+ context 'with workflow:rules:[variables:]' do
+ let(:attributes) do
+ { name: 'rspec',
+ ref: 'master',
+ yaml_variables: [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }],
+ job_variables: [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }],
+ root_variables_inheritance: root_variables_inheritance }
+ end
+
+ context 'when the pipeline has variables' do
+ let(:root_variables) do
+ [{ key: 'VAR1', value: 'var overridden pipeline 1' },
+ { key: 'VAR2', value: 'var pipeline 2' },
+ { key: 'VAR3', value: 'var pipeline 3' },
+ { key: 'VAR4', value: 'new var pipeline 4' }]
+ end
- let(:root_variables_inheritance) { true }
+ context 'when root_variables_inheritance is true' do
+ let(:root_variables_inheritance) { true }
- context 'when the rules use job variables' do
- let(:rules) do
- [{ if: '$VAR1 == "var 1"', variables: { VAR1: 'overridden var 1', VAR2: 'new var 2' } }]
+ it 'returns calculated yaml variables' do
+ expect(subject[:yaml_variables]).to match_array(
+ [{ key: 'VAR1', value: 'var overridden pipeline 1' },
+ { key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' },
+ { key: 'VAR4', value: 'new var pipeline 4' }]
+ )
+ end
+ end
+
+ context 'when root_variables_inheritance is false' do
+ let(:root_variables_inheritance) { false }
+
+ it 'returns job variables' do
+ expect(subject[:yaml_variables]).to match_array(
+ [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }]
+ )
+ end
+ end
+
+ context 'when root_variables_inheritance is an array' do
+ let(:root_variables_inheritance) { %w(VAR1 VAR2 VAR3) }
+
+ it 'returns calculated yaml variables' do
+ expect(subject[:yaml_variables]).to match_array(
+ [{ key: 'VAR1', value: 'var overridden pipeline 1' },
+ { key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }]
+ )
+ end
+ end
end
- it 'recalculates the variables' do
- expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1' },
- { key: 'VAR2', value: 'new var 2' })
+ context 'when the pipeline has not a variable' do
+ let(:root_variables_inheritance) { true }
+
+ it 'returns seed yaml variables' do
+ expect(subject[:yaml_variables]).to match_array(
+ [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }])
+ end
end
end
- context 'when the rules use root variables' do
- let(:root_variables) do
- [{ key: 'VAR2', value: 'var pipeline 2' }]
+ context 'when the job rule depends on variables' do
+ let(:attributes) do
+ { name: 'rspec',
+ ref: 'master',
+ yaml_variables: [{ key: 'VAR1', value: 'var 1' }],
+ job_variables: [{ key: 'VAR1', value: 'var 1' }],
+ root_variables_inheritance: root_variables_inheritance,
+ rules: rules }
end
- let(:rules) do
- [{ if: '$VAR2 == "var pipeline 2"', variables: { VAR1: 'overridden var 1', VAR2: 'overridden var 2' } }]
- end
+ let(:root_variables_inheritance) { true }
+
+ context 'when the rules use job variables' do
+ let(:rules) do
+ [{ if: '$VAR1 == "var 1"', variables: { VAR1: 'overridden var 1', VAR2: 'new var 2' } }]
+ end
- it 'recalculates the variables' do
- expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1' },
- { key: 'VAR2', value: 'overridden var 2' })
+ it 'recalculates the variables' do
+ expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1' },
+ { key: 'VAR2', value: 'new var 2' })
+ end
end
- context 'when the root_variables_inheritance is false' do
- let(:root_variables_inheritance) { false }
+ context 'when the rules use root variables' do
+ let(:root_variables) do
+ [{ key: 'VAR2', value: 'var pipeline 2' }]
+ end
- it 'does not recalculate the variables' do
- expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'var 1' })
+ let(:rules) do
+ [{ if: '$VAR2 == "var pipeline 2"', variables: { VAR1: 'overridden var 1', VAR2: 'overridden var 2' } }]
+ end
+
+ it 'recalculates the variables' do
+ expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1' },
+ { key: 'VAR2', value: 'overridden var 2' })
end
- end
- end
- end
- end
- describe '#bridge?' do
- subject { seed_build.bridge? }
+ context 'when the root_variables_inheritance is false' do
+ let(:root_variables_inheritance) { false }
- context 'when job is a downstream bridge' do
- let(:attributes) do
- { name: 'rspec', ref: 'master', options: { trigger: 'my/project' } }
+ it 'does not recalculate the variables' do
+ expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'var 1' })
+ end
+ end
+ end
end
+ end
- it { is_expected.to be_truthy }
+ describe '#bridge?' do
+ subject { seed_build.bridge? }
- context 'when trigger definition is empty' do
+ context 'when job is a downstream bridge' do
let(:attributes) do
- { name: 'rspec', ref: 'master', options: { trigger: '' } }
+ { name: 'rspec', ref: 'master', options: { trigger: 'my/project' } }
end
- it { is_expected.to be_falsey }
- end
- end
+ it { is_expected.to be_truthy }
- context 'when job is an upstream bridge' do
- let(:attributes) do
- { name: 'rspec', ref: 'master', options: { bridge_needs: { pipeline: 'my/project' } } }
- end
+ context 'when trigger definition is empty' do
+ let(:attributes) do
+ { name: 'rspec', ref: 'master', options: { trigger: '' } }
+ end
- it { is_expected.to be_truthy }
+ it { is_expected.to be_falsey }
+ end
+ end
- context 'when upstream definition is empty' do
+ context 'when job is an upstream bridge' do
let(:attributes) do
- { name: 'rspec', ref: 'master', options: { bridge_needs: { pipeline: '' } } }
+ { name: 'rspec', ref: 'master', options: { bridge_needs: { pipeline: 'my/project' } } }
end
- it { is_expected.to be_falsey }
- end
- end
+ it { is_expected.to be_truthy }
- context 'when job is not a bridge' do
- it { is_expected.to be_falsey }
- end
- end
+ context 'when upstream definition is empty' do
+ let(:attributes) do
+ { name: 'rspec', ref: 'master', options: { bridge_needs: { pipeline: '' } } }
+ end
- describe '#to_resource' do
- subject { seed_build.to_resource }
+ it { is_expected.to be_falsey }
+ end
+ end
- it 'memoizes a resource object' do
- expect(subject.object_id).to eq seed_build.to_resource.object_id
+ context 'when job is not a bridge' do
+ it { is_expected.to be_falsey }
+ end
end
- it 'can not be persisted without explicit assignment' do
- pipeline.save!
+ describe '#to_resource' do
+ subject { seed_build.to_resource }
- expect(subject).not_to be_persisted
- end
- end
+ it 'memoizes a resource object' do
+ expect(subject.object_id).to eq seed_build.to_resource.object_id
+ end
- describe 'applying job inclusion policies' do
- subject { seed_build }
+ it 'can not be persisted without explicit assignment' do
+ pipeline.save!
- context 'when no branch policy is specified' do
- let(:attributes) do
- { name: 'rspec' }
+ expect(subject).not_to be_persisted
end
-
- it { is_expected.to be_included }
end
- context 'when branch policy does not match' do
- context 'when using only' do
- let(:attributes) do
- { name: 'rspec', only: { refs: ['deploy'] } }
- end
-
- it { is_expected.not_to be_included }
- end
+ describe 'applying job inclusion policies' do
+ subject { seed_build }
- context 'when using except' do
+ context 'when no branch policy is specified' do
let(:attributes) do
- { name: 'rspec', except: { refs: ['deploy'] } }
+ { name: 'rspec' }
end
it { is_expected.to be_included }
end
- context 'with both only and except policies' do
- let(:attributes) do
- {
- name: 'rspec',
- only: { refs: %w[deploy] },
- except: { refs: %w[deploy] }
- }
+ context 'when branch policy does not match' do
+ context 'when using only' do
+ let(:attributes) do
+ { name: 'rspec', only: { refs: ['deploy'] } }
+ end
+
+ it { is_expected.not_to be_included }
end
- it { is_expected.not_to be_included }
- end
- end
+ context 'when using except' do
+ let(:attributes) do
+ { name: 'rspec', except: { refs: ['deploy'] } }
+ end
- context 'when branch regexp policy does not match' do
- context 'when using only' do
- let(:attributes) do
- { name: 'rspec', only: { refs: %w[/^deploy$/] } }
+ it { is_expected.to be_included }
end
- it { is_expected.not_to be_included }
- end
+ context 'with both only and except policies' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ only: { refs: %w[deploy] },
+ except: { refs: %w[deploy] }
+ }
+ end
- context 'when using except' do
- let(:attributes) do
- { name: 'rspec', except: { refs: %w[/^deploy$/] } }
+ it { is_expected.not_to be_included }
end
-
- it { is_expected.to be_included }
end
- context 'with both only and except policies' do
- let(:attributes) do
- {
- name: 'rspec',
- only: { refs: %w[/^deploy$/] },
- except: { refs: %w[/^deploy$/] }
- }
+ context 'when branch regexp policy does not match' do
+ context 'when using only' do
+ let(:attributes) do
+ { name: 'rspec', only: { refs: %w[/^deploy$/] } }
+ end
+
+ it { is_expected.not_to be_included }
end
- it { is_expected.not_to be_included }
- end
- end
+ context 'when using except' do
+ let(:attributes) do
+ { name: 'rspec', except: { refs: %w[/^deploy$/] } }
+ end
- context 'when branch policy matches' do
- context 'when using only' do
- let(:attributes) do
- { name: 'rspec', only: { refs: %w[deploy master] } }
+ it { is_expected.to be_included }
end
- it { is_expected.to be_included }
- end
+ context 'with both only and except policies' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ only: { refs: %w[/^deploy$/] },
+ except: { refs: %w[/^deploy$/] }
+ }
+ end
- context 'when using except' do
- let(:attributes) do
- { name: 'rspec', except: { refs: %w[deploy master] } }
+ it { is_expected.not_to be_included }
end
-
- it { is_expected.not_to be_included }
end
- context 'when using both only and except policies' do
- let(:attributes) do
- {
- name: 'rspec',
- only: { refs: %w[deploy master] },
- except: { refs: %w[deploy master] }
- }
+ context 'when branch policy matches' do
+ context 'when using only' do
+ let(:attributes) do
+ { name: 'rspec', only: { refs: %w[deploy master] } }
+ end
+
+ it { is_expected.to be_included }
end
- it { is_expected.not_to be_included }
- end
- end
+ context 'when using except' do
+ let(:attributes) do
+ { name: 'rspec', except: { refs: %w[deploy master] } }
+ end
- context 'when keyword policy matches' do
- context 'when using only' do
- let(:attributes) do
- { name: 'rspec', only: { refs: %w[branches] } }
+ it { is_expected.not_to be_included }
end
- it { is_expected.to be_included }
- end
+ context 'when using both only and except policies' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ only: { refs: %w[deploy master] },
+ except: { refs: %w[deploy master] }
+ }
+ end
- context 'when using except' do
- let(:attributes) do
- { name: 'rspec', except: { refs: %w[branches] } }
+ it { is_expected.not_to be_included }
end
-
- it { is_expected.not_to be_included }
end
- context 'when using both only and except policies' do
- let(:attributes) do
- {
- name: 'rspec',
- only: { refs: %w[branches] },
- except: { refs: %w[branches] }
- }
+ context 'when keyword policy matches' do
+ context 'when using only' do
+ let(:attributes) do
+ { name: 'rspec', only: { refs: %w[branches] } }
+ end
+
+ it { is_expected.to be_included }
end
- it { is_expected.not_to be_included }
- end
- end
+ context 'when using except' do
+ let(:attributes) do
+ { name: 'rspec', except: { refs: %w[branches] } }
+ end
- context 'when keyword policy does not match' do
- context 'when using only' do
- let(:attributes) do
- { name: 'rspec', only: { refs: %w[tags] } }
+ it { is_expected.not_to be_included }
end
- it { is_expected.not_to be_included }
- end
+ context 'when using both only and except policies' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ only: { refs: %w[branches] },
+ except: { refs: %w[branches] }
+ }
+ end
- context 'when using except' do
- let(:attributes) do
- { name: 'rspec', except: { refs: %w[tags] } }
+ it { is_expected.not_to be_included }
end
-
- it { is_expected.to be_included }
end
- context 'when using both only and except policies' do
- let(:attributes) do
- {
- name: 'rspec',
- only: { refs: %w[tags] },
- except: { refs: %w[tags] }
- }
+ context 'when keyword policy does not match' do
+ context 'when using only' do
+ let(:attributes) do
+ { name: 'rspec', only: { refs: %w[tags] } }
+ end
+
+ it { is_expected.not_to be_included }
end
- it { is_expected.not_to be_included }
- end
- end
+ context 'when using except' do
+ let(:attributes) do
+ { name: 'rspec', except: { refs: %w[tags] } }
+ end
- context 'with source-keyword policy' do
- using RSpec::Parameterized
+ it { is_expected.to be_included }
+ end
- let(:pipeline) do
- build(:ci_empty_pipeline, ref: 'deploy', tag: false, source: source, project: project)
- end
+ context 'when using both only and except policies' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ only: { refs: %w[tags] },
+ except: { refs: %w[tags] }
+ }
+ end
- context 'matches' do
- where(:keyword, :source) do
- [
- %w[pushes push],
- %w[web web],
- %w[triggers trigger],
- %w[schedules schedule],
- %w[api api],
- %w[external external]
- ]
+ it { is_expected.not_to be_included }
end
+ end
- with_them do
- context 'using an only policy' do
- let(:attributes) do
- { name: 'rspec', only: { refs: [keyword] } }
- end
+ context 'with source-keyword policy' do
+ using RSpec::Parameterized
- it { is_expected.to be_included }
+ let(:pipeline) do
+ build(:ci_empty_pipeline, ref: 'deploy', tag: false, source: source, project: project)
+ end
+
+ context 'matches' do
+ where(:keyword, :source) do
+ [
+ %w[pushes push],
+ %w[web web],
+ %w[triggers trigger],
+ %w[schedules schedule],
+ %w[api api],
+ %w[external external]
+ ]
end
- context 'using an except policy' do
- let(:attributes) do
- { name: 'rspec', except: { refs: [keyword] } }
+ with_them do
+ context 'using an only policy' do
+ let(:attributes) do
+ { name: 'rspec', only: { refs: [keyword] } }
+ end
+
+ it { is_expected.to be_included }
end
- it { is_expected.not_to be_included }
- end
+ context 'using an except policy' do
+ let(:attributes) do
+ { name: 'rspec', except: { refs: [keyword] } }
+ end
- context 'using both only and except policies' do
- let(:attributes) do
- {
- name: 'rspec',
- only: { refs: [keyword] },
- except: { refs: [keyword] }
- }
+ it { is_expected.not_to be_included }
end
- it { is_expected.not_to be_included }
+ context 'using both only and except policies' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ only: { refs: [keyword] },
+ except: { refs: [keyword] }
+ }
+ end
+
+ it { is_expected.not_to be_included }
+ end
end
end
- end
- context 'non-matches' do
- where(:keyword, :source) do
- %w[web trigger schedule api external].map { |source| ['pushes', source] } +
- %w[push trigger schedule api external].map { |source| ['web', source] } +
- %w[push web schedule api external].map { |source| ['triggers', source] } +
- %w[push web trigger api external].map { |source| ['schedules', source] } +
- %w[push web trigger schedule external].map { |source| ['api', source] } +
- %w[push web trigger schedule api].map { |source| ['external', source] }
- end
+ context 'non-matches' do
+ where(:keyword, :source) do
+ %w[web trigger schedule api external].map { |source| ['pushes', source] } +
+ %w[push trigger schedule api external].map { |source| ['web', source] } +
+ %w[push web schedule api external].map { |source| ['triggers', source] } +
+ %w[push web trigger api external].map { |source| ['schedules', source] } +
+ %w[push web trigger schedule external].map { |source| ['api', source] } +
+ %w[push web trigger schedule api].map { |source| ['external', source] }
+ end
- with_them do
- context 'using an only policy' do
- let(:attributes) do
- { name: 'rspec', only: { refs: [keyword] } }
+ with_them do
+ context 'using an only policy' do
+ let(:attributes) do
+ { name: 'rspec', only: { refs: [keyword] } }
+ end
+
+ it { is_expected.not_to be_included }
end
- it { is_expected.not_to be_included }
- end
+ context 'using an except policy' do
+ let(:attributes) do
+ { name: 'rspec', except: { refs: [keyword] } }
+ end
- context 'using an except policy' do
- let(:attributes) do
- { name: 'rspec', except: { refs: [keyword] } }
+ it { is_expected.to be_included }
end
- it { is_expected.to be_included }
- end
+ context 'using both only and except policies' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ only: { refs: [keyword] },
+ except: { refs: [keyword] }
+ }
+ end
- context 'using both only and except policies' do
- let(:attributes) do
- {
- name: 'rspec',
- only: { refs: [keyword] },
- except: { refs: [keyword] }
- }
+ it { is_expected.not_to be_included }
end
-
- it { is_expected.not_to be_included }
end
end
end
- end
- context 'when repository path matches' do
- context 'when using only' do
- let(:attributes) do
- { name: 'rspec', only: { refs: ["branches@#{pipeline.project_full_path}"] } }
+ context 'when repository path matches' do
+ context 'when using only' do
+ let(:attributes) do
+ { name: 'rspec', only: { refs: ["branches@#{pipeline.project_full_path}"] } }
+ end
+
+ it { is_expected.to be_included }
end
- it { is_expected.to be_included }
- end
+ context 'when using except' do
+ let(:attributes) do
+ { name: 'rspec', except: { refs: ["branches@#{pipeline.project_full_path}"] } }
+ end
- context 'when using except' do
- let(:attributes) do
- { name: 'rspec', except: { refs: ["branches@#{pipeline.project_full_path}"] } }
+ it { is_expected.not_to be_included }
end
- it { is_expected.not_to be_included }
- end
+ context 'when using both only and except policies' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ only: { refs: ["branches@#{pipeline.project_full_path}"] },
+ except: { refs: ["branches@#{pipeline.project_full_path}"] }
+ }
+ end
- context 'when using both only and except policies' do
- let(:attributes) do
- {
- name: 'rspec',
- only: { refs: ["branches@#{pipeline.project_full_path}"] },
- except: { refs: ["branches@#{pipeline.project_full_path}"] }
- }
+ it { is_expected.not_to be_included }
end
- it { is_expected.not_to be_included }
- end
-
- context 'when using both only and except policies' do
- let(:attributes) do
- {
- name: 'rspec',
- only: {
- refs: ["branches@#{pipeline.project_full_path}"]
- },
- except: {
- refs: ["branches@#{pipeline.project_full_path}"]
+ context 'when using both only and except policies' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ only: {
+ refs: ["branches@#{pipeline.project_full_path}"]
+ },
+ except: {
+ refs: ["branches@#{pipeline.project_full_path}"]
+ }
}
- }
- end
+ end
- it { is_expected.not_to be_included }
+ it { is_expected.not_to be_included }
+ end
end
- end
- context 'when repository path does not match' do
- context 'when using only' do
- let(:attributes) do
- { name: 'rspec', only: { refs: %w[branches@fork] } }
+ context 'when repository path does not match' do
+ context 'when using only' do
+ let(:attributes) do
+ { name: 'rspec', only: { refs: %w[branches@fork] } }
+ end
+
+ it { is_expected.not_to be_included }
end
- it { is_expected.not_to be_included }
- end
+ context 'when using except' do
+ let(:attributes) do
+ { name: 'rspec', except: { refs: %w[branches@fork] } }
+ end
- context 'when using except' do
- let(:attributes) do
- { name: 'rspec', except: { refs: %w[branches@fork] } }
+ it { is_expected.to be_included }
end
- it { is_expected.to be_included }
- end
+ context 'when using both only and except policies' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ only: { refs: %w[branches@fork] },
+ except: { refs: %w[branches@fork] }
+ }
+ end
- context 'when using both only and except policies' do
- let(:attributes) do
- {
- name: 'rspec',
- only: { refs: %w[branches@fork] },
- except: { refs: %w[branches@fork] }
- }
+ it { is_expected.not_to be_included }
end
-
- it { is_expected.not_to be_included }
end
- end
- context 'using rules:' do
- using RSpec::Parameterized
+ context 'using rules:' do
+ using RSpec::Parameterized
- let(:attributes) { { name: 'rspec', rules: rule_set, when: 'on_success' } }
+ let(:attributes) { { name: 'rspec', rules: rule_set, when: 'on_success' } }
- context 'with a matching if: rule' do
- context 'with an explicit `when: never`' do
- where(:rule_set) do
- [
- [[{ if: '$VARIABLE == null', when: 'never' }]],
- [[{ if: '$VARIABLE == null', when: 'never' }, { if: '$VARIABLE == null', when: 'always' }]],
- [[{ if: '$VARIABLE != "the wrong value"', when: 'never' }, { if: '$VARIABLE == null', when: 'always' }]]
- ]
- end
+ context 'with a matching if: rule' do
+ context 'with an explicit `when: never`' do
+ where(:rule_set) do
+ [
+ [[{ if: '$VARIABLE == null', when: 'never' }]],
+ [[{ if: '$VARIABLE == null', when: 'never' }, { if: '$VARIABLE == null', when: 'always' }]],
+ [[{ if: '$VARIABLE != "the wrong value"', when: 'never' }, { if: '$VARIABLE == null', when: 'always' }]]
+ ]
+ end
- with_them do
- it { is_expected.not_to be_included }
+ with_them do
+ it { is_expected.not_to be_included }
- it 'still correctly populates when:' do
- expect(seed_build.attributes).to include(when: 'never')
+ it 'still correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'never')
+ end
end
end
- end
- context 'with an explicit `when: always`' do
- where(:rule_set) do
- [
- [[{ if: '$VARIABLE == null', when: 'always' }]],
- [[{ if: '$VARIABLE == null', when: 'always' }, { if: '$VARIABLE == null', when: 'never' }]],
- [[{ if: '$VARIABLE != "the wrong value"', when: 'always' }, { if: '$VARIABLE == null', when: 'never' }]]
- ]
+ context 'with an explicit `when: always`' do
+ where(:rule_set) do
+ [
+ [[{ if: '$VARIABLE == null', when: 'always' }]],
+ [[{ if: '$VARIABLE == null', when: 'always' }, { if: '$VARIABLE == null', when: 'never' }]],
+ [[{ if: '$VARIABLE != "the wrong value"', when: 'always' }, { if: '$VARIABLE == null', when: 'never' }]]
+ ]
+ end
+
+ with_them do
+ it { is_expected.to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'always')
+ end
+ end
end
- with_them do
- it { is_expected.to be_included }
+ context 'with an explicit `when: on_failure`' do
+ where(:rule_set) do
+ [
+ [[{ if: '$CI_JOB_NAME == "rspec" && $VAR == null', when: 'on_failure' }]],
+ [[{ if: '$VARIABLE != null', when: 'delayed', start_in: '1 day' }, { if: '$CI_JOB_NAME == "rspec"', when: 'on_failure' }]],
+ [[{ if: '$VARIABLE == "the wrong value"', when: 'delayed', start_in: '1 day' }, { if: '$CI_BUILD_NAME == "rspec"', when: 'on_failure' }]]
+ ]
+ end
- it 'correctly populates when:' do
- expect(seed_build.attributes).to include(when: 'always')
+ with_them do
+ it { is_expected.to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'on_failure')
+ end
end
end
- end
- context 'with an explicit `when: on_failure`' do
- where(:rule_set) do
- [
- [[{ if: '$CI_JOB_NAME == "rspec" && $VAR == null', when: 'on_failure' }]],
- [[{ if: '$VARIABLE != null', when: 'delayed', start_in: '1 day' }, { if: '$CI_JOB_NAME == "rspec"', when: 'on_failure' }]],
- [[{ if: '$VARIABLE == "the wrong value"', when: 'delayed', start_in: '1 day' }, { if: '$CI_BUILD_NAME == "rspec"', when: 'on_failure' }]]
- ]
+ context 'with an explicit `when: delayed`' do
+ where(:rule_set) do
+ [
+ [[{ if: '$VARIABLE == null', when: 'delayed', start_in: '1 day' }]],
+ [[{ if: '$VARIABLE == null', when: 'delayed', start_in: '1 day' }, { if: '$VARIABLE == null', when: 'never' }]],
+ [[{ if: '$VARIABLE != "the wrong value"', when: 'delayed', start_in: '1 day' }, { if: '$VARIABLE == null', when: 'never' }]]
+ ]
+ end
+
+ with_them do
+ it { is_expected.to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'delayed', options: { start_in: '1 day' })
+ end
+ end
end
- with_them do
- it { is_expected.to be_included }
+ context 'without an explicit when: value' do
+ where(:rule_set) do
+ [
+ [[{ if: '$VARIABLE == null' }]],
+ [[{ if: '$VARIABLE == null' }, { if: '$VARIABLE == null' }]],
+ [[{ if: '$VARIABLE != "the wrong value"' }, { if: '$VARIABLE == null' }]]
+ ]
+ end
- it 'correctly populates when:' do
- expect(seed_build.attributes).to include(when: 'on_failure')
+ with_them do
+ it { is_expected.to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'on_success')
+ end
end
end
end
- context 'with an explicit `when: delayed`' do
- where(:rule_set) do
- [
- [[{ if: '$VARIABLE == null', when: 'delayed', start_in: '1 day' }]],
- [[{ if: '$VARIABLE == null', when: 'delayed', start_in: '1 day' }, { if: '$VARIABLE == null', when: 'never' }]],
- [[{ if: '$VARIABLE != "the wrong value"', when: 'delayed', start_in: '1 day' }, { if: '$VARIABLE == null', when: 'never' }]]
- ]
+ context 'with a matching changes: rule' do
+ let(:pipeline) do
+ build(:ci_pipeline, project: project).tap do |pipeline|
+ stub_pipeline_modified_paths(pipeline, %w[app/models/ci/pipeline.rb spec/models/ci/pipeline_spec.rb .gitlab-ci.yml])
+ end
end
- with_them do
- it { is_expected.to be_included }
+ context 'with an explicit `when: never`' do
+ where(:rule_set) do
+ [
+ [[{ changes: { paths: %w[*/**/*.rb] }, when: 'never' }, { changes: { paths: %w[*/**/*.rb] }, when: 'always' }]],
+ [[{ changes: { paths: %w[app/models/ci/pipeline.rb] }, when: 'never' }, { changes: { paths: %w[app/models/ci/pipeline.rb] }, when: 'always' }]],
+ [[{ changes: { paths: %w[spec/**/*.rb] }, when: 'never' }, { changes: { paths: %w[spec/**/*.rb] }, when: 'always' }]],
+ [[{ changes: { paths: %w[*.yml] }, when: 'never' }, { changes: { paths: %w[*.yml] }, when: 'always' }]],
+ [[{ changes: { paths: %w[.*.yml] }, when: 'never' }, { changes: { paths: %w[.*.yml] }, when: 'always' }]],
+ [[{ changes: { paths: %w[**/*] }, when: 'never' }, { changes: { paths: %w[**/*] }, when: 'always' }]],
+ [[{ changes: { paths: %w[*/**/*.rb *.yml] }, when: 'never' }, { changes: { paths: %w[*/**/*.rb *.yml] }, when: 'always' }]],
+ [[{ changes: { paths: %w[.*.yml **/*] }, when: 'never' }, { changes: { paths: %w[.*.yml **/*] }, when: 'always' }]]
+ ]
+ end
- it 'correctly populates when:' do
- expect(seed_build.attributes).to include(when: 'delayed', options: { start_in: '1 day' })
+ with_them do
+ it { is_expected.not_to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'never')
+ end
end
end
- end
- context 'without an explicit when: value' do
- where(:rule_set) do
- [
- [[{ if: '$VARIABLE == null' }]],
- [[{ if: '$VARIABLE == null' }, { if: '$VARIABLE == null' }]],
- [[{ if: '$VARIABLE != "the wrong value"' }, { if: '$VARIABLE == null' }]]
- ]
- end
+ context 'with an explicit `when: always`' do
+ where(:rule_set) do
+ [
+ [[{ changes: { paths: %w[*/**/*.rb] }, when: 'always' }, { changes: { paths: %w[*/**/*.rb] }, when: 'never' }]],
+ [[{ changes: { paths: %w[app/models/ci/pipeline.rb] }, when: 'always' }, { changes: { paths: %w[app/models/ci/pipeline.rb] }, when: 'never' }]],
+ [[{ changes: { paths: %w[spec/**/*.rb] }, when: 'always' }, { changes: { paths: %w[spec/**/*.rb] }, when: 'never' }]],
+ [[{ changes: { paths: %w[*.yml] }, when: 'always' }, { changes: { paths: %w[*.yml] }, when: 'never' }]],
+ [[{ changes: { paths: %w[.*.yml] }, when: 'always' }, { changes: { paths: %w[.*.yml] }, when: 'never' }]],
+ [[{ changes: { paths: %w[**/*] }, when: 'always' }, { changes: { paths: %w[**/*] }, when: 'never' }]],
+ [[{ changes: { paths: %w[*/**/*.rb *.yml] }, when: 'always' }, { changes: { paths: %w[*/**/*.rb *.yml] }, when: 'never' }]],
+ [[{ changes: { paths: %w[.*.yml **/*] }, when: 'always' }, { changes: { paths: %w[.*.yml **/*] }, when: 'never' }]]
+ ]
+ end
- with_them do
- it { is_expected.to be_included }
+ with_them do
+ it { is_expected.to be_included }
- it 'correctly populates when:' do
- expect(seed_build.attributes).to include(when: 'on_success')
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'always')
+ end
end
end
- end
- end
- context 'with a matching changes: rule' do
- let(:pipeline) do
- build(:ci_pipeline, project: project).tap do |pipeline|
- stub_pipeline_modified_paths(pipeline, %w[app/models/ci/pipeline.rb spec/models/ci/pipeline_spec.rb .gitlab-ci.yml])
+ context 'without an explicit when: value' do
+ where(:rule_set) do
+ [
+ [[{ changes: { paths: %w[*/**/*.rb] } }]],
+ [[{ changes: { paths: %w[app/models/ci/pipeline.rb] } }]],
+ [[{ changes: { paths: %w[spec/**/*.rb] } }]],
+ [[{ changes: { paths: %w[*.yml] } }]],
+ [[{ changes: { paths: %w[.*.yml] } }]],
+ [[{ changes: { paths: %w[**/*] } }]],
+ [[{ changes: { paths: %w[*/**/*.rb *.yml] } }]],
+ [[{ changes: { paths: %w[.*.yml **/*] } }]]
+ ]
+ end
+
+ with_them do
+ it { is_expected.to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'on_success')
+ end
+ end
end
end
- context 'with an explicit `when: never`' do
+ context 'with no matching rule' do
where(:rule_set) do
[
- [[{ changes: { paths: %w[*/**/*.rb] }, when: 'never' }, { changes: { paths: %w[*/**/*.rb] }, when: 'always' }]],
- [[{ changes: { paths: %w[app/models/ci/pipeline.rb] }, when: 'never' }, { changes: { paths: %w[app/models/ci/pipeline.rb] }, when: 'always' }]],
- [[{ changes: { paths: %w[spec/**/*.rb] }, when: 'never' }, { changes: { paths: %w[spec/**/*.rb] }, when: 'always' }]],
- [[{ changes: { paths: %w[*.yml] }, when: 'never' }, { changes: { paths: %w[*.yml] }, when: 'always' }]],
- [[{ changes: { paths: %w[.*.yml] }, when: 'never' }, { changes: { paths: %w[.*.yml] }, when: 'always' }]],
- [[{ changes: { paths: %w[**/*] }, when: 'never' }, { changes: { paths: %w[**/*] }, when: 'always' }]],
- [[{ changes: { paths: %w[*/**/*.rb *.yml] }, when: 'never' }, { changes: { paths: %w[*/**/*.rb *.yml] }, when: 'always' }]],
- [[{ changes: { paths: %w[.*.yml **/*] }, when: 'never' }, { changes: { paths: %w[.*.yml **/*] }, when: 'always' }]]
+ [[{ if: '$VARIABLE != null', when: 'never' }]],
+ [[{ if: '$VARIABLE != null', when: 'never' }, { if: '$VARIABLE != null', when: 'always' }]],
+ [[{ if: '$VARIABLE == "the wrong value"', when: 'never' }, { if: '$VARIABLE != null', when: 'always' }]],
+ [[{ if: '$VARIABLE != null', when: 'always' }]],
+ [[{ if: '$VARIABLE != null', when: 'always' }, { if: '$VARIABLE != null', when: 'never' }]],
+ [[{ if: '$VARIABLE == "the wrong value"', when: 'always' }, { if: '$VARIABLE != null', when: 'never' }]],
+ [[{ if: '$VARIABLE != null' }]],
+ [[{ if: '$VARIABLE != null' }, { if: '$VARIABLE != null' }]],
+ [[{ if: '$VARIABLE == "the wrong value"' }, { if: '$VARIABLE != null' }]]
]
end
@@ -878,257 +971,249 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
end
end
- context 'with an explicit `when: always`' do
- where(:rule_set) do
- [
- [[{ changes: { paths: %w[*/**/*.rb] }, when: 'always' }, { changes: { paths: %w[*/**/*.rb] }, when: 'never' }]],
- [[{ changes: { paths: %w[app/models/ci/pipeline.rb] }, when: 'always' }, { changes: { paths: %w[app/models/ci/pipeline.rb] }, when: 'never' }]],
- [[{ changes: { paths: %w[spec/**/*.rb] }, when: 'always' }, { changes: { paths: %w[spec/**/*.rb] }, when: 'never' }]],
- [[{ changes: { paths: %w[*.yml] }, when: 'always' }, { changes: { paths: %w[*.yml] }, when: 'never' }]],
- [[{ changes: { paths: %w[.*.yml] }, when: 'always' }, { changes: { paths: %w[.*.yml] }, when: 'never' }]],
- [[{ changes: { paths: %w[**/*] }, when: 'always' }, { changes: { paths: %w[**/*] }, when: 'never' }]],
- [[{ changes: { paths: %w[*/**/*.rb *.yml] }, when: 'always' }, { changes: { paths: %w[*/**/*.rb *.yml] }, when: 'never' }]],
- [[{ changes: { paths: %w[.*.yml **/*] }, when: 'always' }, { changes: { paths: %w[.*.yml **/*] }, when: 'never' }]]
- ]
+ context 'with a rule using CI_ENVIRONMENT_NAME variable' do
+ let(:rule_set) do
+ [{ if: '$CI_ENVIRONMENT_NAME == "test"' }]
end
- with_them do
+ context 'when environment:name satisfies the rule' do
+ let(:attributes) { { name: 'rspec', rules: rule_set, environment: 'test', when: 'on_success' } }
+
it { is_expected.to be_included }
it 'correctly populates when:' do
- expect(seed_build.attributes).to include(when: 'always')
+ expect(seed_build.attributes).to include(when: 'on_success')
end
end
- end
- context 'without an explicit when: value' do
- where(:rule_set) do
- [
- [[{ changes: { paths: %w[*/**/*.rb] } }]],
- [[{ changes: { paths: %w[app/models/ci/pipeline.rb] } }]],
- [[{ changes: { paths: %w[spec/**/*.rb] } }]],
- [[{ changes: { paths: %w[*.yml] } }]],
- [[{ changes: { paths: %w[.*.yml] } }]],
- [[{ changes: { paths: %w[**/*] } }]],
- [[{ changes: { paths: %w[*/**/*.rb *.yml] } }]],
- [[{ changes: { paths: %w[.*.yml **/*] } }]]
- ]
+ context 'when environment:name does not satisfy rule' do
+ let(:attributes) { { name: 'rspec', rules: rule_set, environment: 'dev', when: 'on_success' } }
+
+ it { is_expected.not_to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'never')
+ end
end
- with_them do
- it { is_expected.to be_included }
+ context 'when environment:name is not set' do
+ it { is_expected.not_to be_included }
it 'correctly populates when:' do
- expect(seed_build.attributes).to include(when: 'on_success')
+ expect(seed_build.attributes).to include(when: 'never')
end
end
end
- end
- context 'with no matching rule' do
- where(:rule_set) do
- [
- [[{ if: '$VARIABLE != null', when: 'never' }]],
- [[{ if: '$VARIABLE != null', when: 'never' }, { if: '$VARIABLE != null', when: 'always' }]],
- [[{ if: '$VARIABLE == "the wrong value"', when: 'never' }, { if: '$VARIABLE != null', when: 'always' }]],
- [[{ if: '$VARIABLE != null', when: 'always' }]],
- [[{ if: '$VARIABLE != null', when: 'always' }, { if: '$VARIABLE != null', when: 'never' }]],
- [[{ if: '$VARIABLE == "the wrong value"', when: 'always' }, { if: '$VARIABLE != null', when: 'never' }]],
- [[{ if: '$VARIABLE != null' }]],
- [[{ if: '$VARIABLE != null' }, { if: '$VARIABLE != null' }]],
- [[{ if: '$VARIABLE == "the wrong value"' }, { if: '$VARIABLE != null' }]]
- ]
+ context 'with no rules' do
+ let(:rule_set) { [] }
+
+ it { is_expected.not_to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'never')
+ end
end
- with_them do
+ context 'with invalid rules raising error' do
+ let(:rule_set) do
+ [
+ { changes: { paths: ['README.md'], compare_to: 'invalid-ref' }, when: 'never' }
+ ]
+ end
+
it { is_expected.not_to be_included }
it 'correctly populates when:' do
expect(seed_build.attributes).to include(when: 'never')
end
+
+ it 'returns an error' do
+ expect(seed_build.errors).to contain_exactly(
+ 'Failed to parse rule for rspec: rules:changes:compare_to is not a valid ref'
+ )
+ end
end
end
+ end
- context 'with no rules' do
- let(:rule_set) { [] }
+ describe 'applying needs: dependency' do
+ subject { seed_build }
- it { is_expected.not_to be_included }
+ let(:needs_count) { 1 }
- it 'correctly populates when:' do
- expect(seed_build.attributes).to include(when: 'never')
- end
+ let(:needs_attributes) do
+ Array.new(needs_count, name: 'build')
end
- context 'with invalid rules raising error' do
- let(:rule_set) do
- [
- { changes: { paths: ['README.md'], compare_to: 'invalid-ref' }, when: 'never' }
- ]
- end
+ let(:attributes) do
+ {
+ name: 'rspec',
+ needs_attributes: needs_attributes
+ }
+ end
- it { is_expected.not_to be_included }
+ context 'when build job is not present in prior stages' do
+ it "is included" do
+ is_expected.to be_included
+ end
- it 'correctly populates when:' do
- expect(seed_build.attributes).to include(when: 'never')
+ it "returns an error" do
+ expect(subject.errors).to contain_exactly(
+ "'rspec' job needs 'build' job, but 'build' is not in any previous stage")
end
- it 'returns an error' do
- expect(seed_build.errors).to contain_exactly(
- 'Failed to parse rule for rspec: rules:changes:compare_to is not a valid ref'
- )
+ context 'when the needed job is optional' do
+ let(:needs_attributes) { [{ name: 'build', optional: true }] }
+
+ it "does not return an error" do
+ expect(subject.errors).to be_empty
+ end
end
end
- end
- end
- describe 'applying needs: dependency' do
- subject { seed_build }
+ context 'when build job is part of prior stages' do
+ let(:stage_attributes) do
+ {
+ name: 'build',
+ index: 0,
+ builds: [{ name: 'build' }]
+ }
+ end
- let(:needs_count) { 1 }
+ let(:stage_seed) do
+ Gitlab::Ci::Pipeline::Seed::Stage.new(seed_context, stage_attributes, [])
+ end
- let(:needs_attributes) do
- Array.new(needs_count, name: 'build')
- end
+ let(:previous_stages) { [stage_seed] }
- let(:attributes) do
- {
- name: 'rspec',
- needs_attributes: needs_attributes
- }
- end
+ it "is included" do
+ is_expected.to be_included
+ end
- context 'when build job is not present in prior stages' do
- it "is included" do
- is_expected.to be_included
+ it "does not have errors" do
+ expect(subject.errors).to be_empty
+ end
end
- it "returns an error" do
- expect(subject.errors).to contain_exactly(
- "'rspec' job needs 'build' job, but 'build' is not in any previous stage")
- end
+ context 'when build job is part of the same stage' do
+ let(:current_stage) { double(seeds_names: [attributes[:name], 'build']) }
- context 'when the needed job is optional' do
- let(:needs_attributes) { [{ name: 'build', optional: true }] }
+ it 'is included' do
+ is_expected.to be_included
+ end
- it "does not return an error" do
+ it 'does not have errors' do
expect(subject.errors).to be_empty
end
end
- end
-
- context 'when build job is part of prior stages' do
- let(:stage_attributes) do
- {
- name: 'build',
- index: 0,
- builds: [{ name: 'build' }]
- }
- end
-
- let(:stage_seed) do
- Gitlab::Ci::Pipeline::Seed::Stage.new(seed_context, stage_attributes, [])
- end
- let(:previous_stages) { [stage_seed] }
+ context 'when using 101 needs' do
+ let(:needs_count) { 101 }
- it "is included" do
- is_expected.to be_included
- end
+ it "returns an error" do
+ expect(subject.errors).to contain_exactly(
+ "rspec: one job can only need 50 others, but you have listed 101. See needs keyword documentation for more details")
+ end
- it "does not have errors" do
- expect(subject.errors).to be_empty
- end
- end
+ context 'when ci_needs_size_limit is set to 100' do
+ before do
+ project.actual_limits.update!(ci_needs_size_limit: 100)
+ end
- context 'when build job is part of the same stage' do
- let(:current_stage) { double(seeds_names: [attributes[:name], 'build']) }
+ it "returns an error" do
+ expect(subject.errors).to contain_exactly(
+ "rspec: one job can only need 100 others, but you have listed 101. See needs keyword documentation for more details")
+ end
+ end
- it 'is included' do
- is_expected.to be_included
- end
+ context 'when ci_needs_size_limit is set to 0' do
+ before do
+ project.actual_limits.update!(ci_needs_size_limit: 0)
+ end
- it 'does not have errors' do
- expect(subject.errors).to be_empty
+ it "returns an error" do
+ expect(subject.errors).to contain_exactly(
+ "rspec: one job can only need 0 others, but you have listed 101. See needs keyword documentation for more details")
+ end
+ end
end
end
- context 'when using 101 needs' do
- let(:needs_count) { 101 }
+ describe 'applying pipeline variables' do
+ subject { seed_build }
- it "returns an error" do
- expect(subject.errors).to contain_exactly(
- "rspec: one job can only need 50 others, but you have listed 101. See needs keyword documentation for more details")
+ let(:pipeline_variables) { [] }
+ let(:pipeline) do
+ build(:ci_empty_pipeline, project: project, sha: head_sha, variables: pipeline_variables)
end
- context 'when ci_needs_size_limit is set to 100' do
- before do
- project.actual_limits.update!(ci_needs_size_limit: 100)
+ context 'containing variable references' do
+ let(:pipeline_variables) do
+ [
+ build(:ci_pipeline_variable, key: 'A', value: '$B'),
+ build(:ci_pipeline_variable, key: 'B', value: '$C')
+ ]
end
- it "returns an error" do
- expect(subject.errors).to contain_exactly(
- "rspec: one job can only need 100 others, but you have listed 101. See needs keyword documentation for more details")
+ it "does not have errors" do
+ expect(subject.errors).to be_empty
end
end
- context 'when ci_needs_size_limit is set to 0' do
- before do
- project.actual_limits.update!(ci_needs_size_limit: 0)
+ context 'containing cyclic reference' do
+ let(:pipeline_variables) do
+ [
+ build(:ci_pipeline_variable, key: 'A', value: '$B'),
+ build(:ci_pipeline_variable, key: 'B', value: '$C'),
+ build(:ci_pipeline_variable, key: 'C', value: '$A')
+ ]
end
it "returns an error" do
expect(subject.errors).to contain_exactly(
- "rspec: one job can only need 0 others, but you have listed 101. See needs keyword documentation for more details")
+ 'rspec: circular variable reference detected: ["A", "B", "C"]')
+ end
+
+ context 'with job:rules:[if:]' do
+ let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$C != null', when: 'always' }] } }
+
+ it "included? does not raise" do
+ expect { subject.included? }.not_to raise_error
+ end
+
+ it "included? returns true" do
+ expect(subject.included?).to eq(true)
+ end
end
end
end
end
- describe 'applying pipeline variables' do
- subject { seed_build }
-
- let(:pipeline_variables) { [] }
- let(:pipeline) do
- build(:ci_empty_pipeline, project: project, sha: head_sha, variables: pipeline_variables)
+ describe 'feature flag ci_reuse_build_in_seed_context' do
+ let(:attributes) do
+ { name: 'rspec', rules: [{ if: '$VARIABLE == null' }], when: 'on_success' }
end
- context 'containing variable references' do
- let(:pipeline_variables) do
- [
- build(:ci_pipeline_variable, key: 'A', value: '$B'),
- build(:ci_pipeline_variable, key: 'B', value: '$C')
- ]
- end
+ context 'when enabled' do
+ it_behaves_like 'build seed'
- it "does not have errors" do
- expect(subject.errors).to be_empty
+ it 'initializes the build once' do
+ expect(Ci::Build).to receive(:new).once.and_call_original
+ seed_build.to_resource
end
end
- context 'containing cyclic reference' do
- let(:pipeline_variables) do
- [
- build(:ci_pipeline_variable, key: 'A', value: '$B'),
- build(:ci_pipeline_variable, key: 'B', value: '$C'),
- build(:ci_pipeline_variable, key: 'C', value: '$A')
- ]
- end
-
- it "returns an error" do
- expect(subject.errors).to contain_exactly(
- 'rspec: circular variable reference detected: ["A", "B", "C"]')
+ context 'when disabled' do
+ before do
+ stub_feature_flags(ci_reuse_build_in_seed_context: false)
end
- context 'with job:rules:[if:]' do
- let(:attributes) { { name: 'rspec', ref: 'master', rules: [{ if: '$C != null', when: 'always' }] } }
-
- it "included? does not raise" do
- expect { subject.included? }.not_to raise_error
- end
+ it_behaves_like 'build seed'
- it "included? returns true" do
- expect(subject.included?).to eq(true)
- end
+ it 'initializes the build twice' do
+ expect(Ci::Build).to receive(:new).twice.and_call_original
+ seed_build.to_resource
end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
index a632b5dedcf..288ac3f3854 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Pipeline::Seed::Stage do
+RSpec.describe Gitlab::Ci::Pipeline::Seed::Stage, feature_category: :pipeline_authoring do
let(:project) { create(:project, :repository) }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:previous_stages) { [] }
diff --git a/spec/lib/gitlab/ci/reports/sbom/component_spec.rb b/spec/lib/gitlab/ci/reports/sbom/component_spec.rb
index cdaf9354104..5dbcc1991d4 100644
--- a/spec/lib/gitlab/ci/reports/sbom/component_spec.rb
+++ b/spec/lib/gitlab/ci/reports/sbom/component_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Reports::Sbom::Component do
+RSpec.describe Gitlab::Ci::Reports::Sbom::Component, feature_category: :dependency_management do
let(:component_type) { 'library' }
let(:name) { 'component-name' }
let(:purl_type) { 'npm' }
diff --git a/spec/lib/gitlab/ci/reports/sbom/report_spec.rb b/spec/lib/gitlab/ci/reports/sbom/report_spec.rb
index f9a83378f46..5d281f6ed76 100644
--- a/spec/lib/gitlab/ci/reports/sbom/report_spec.rb
+++ b/spec/lib/gitlab/ci/reports/sbom/report_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Reports::Sbom::Report do
+RSpec.describe Gitlab::Ci::Reports::Sbom::Report, feature_category: :dependency_management do
subject(:report) { described_class.new }
describe '#valid?' do
diff --git a/spec/lib/gitlab/ci/reports/sbom/reports_spec.rb b/spec/lib/gitlab/ci/reports/sbom/reports_spec.rb
index 75ea91251eb..4fb766d7d38 100644
--- a/spec/lib/gitlab/ci/reports/sbom/reports_spec.rb
+++ b/spec/lib/gitlab/ci/reports/sbom/reports_spec.rb
@@ -2,7 +2,7 @@
require 'fast_spec_helper'
-RSpec.describe Gitlab::Ci::Reports::Sbom::Reports do
+RSpec.describe Gitlab::Ci::Reports::Sbom::Reports, feature_category: :dependency_management do
subject(:reports_list) { described_class.new }
describe '#add_report' do
diff --git a/spec/lib/gitlab/ci/reports/sbom/source_spec.rb b/spec/lib/gitlab/ci/reports/sbom/source_spec.rb
index 343c0d8c15c..63b8e5fdf01 100644
--- a/spec/lib/gitlab/ci/reports/sbom/source_spec.rb
+++ b/spec/lib/gitlab/ci/reports/sbom/source_spec.rb
@@ -2,7 +2,7 @@
require 'fast_spec_helper'
-RSpec.describe Gitlab::Ci::Reports::Sbom::Source do
+RSpec.describe Gitlab::Ci::Reports::Sbom::Source, feature_category: :dependency_management do
let(:attributes) do
{
type: :dependency_scanning,
diff --git a/spec/lib/gitlab/ci/reports/security/reports_spec.rb b/spec/lib/gitlab/ci/reports/security/reports_spec.rb
index 33f3317c655..cb6a91655ed 100644
--- a/spec/lib/gitlab/ci/reports/security/reports_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/reports_spec.rb
@@ -52,105 +52,4 @@ RSpec.describe Gitlab::Ci::Reports::Security::Reports do
it { is_expected.to match_array(expected_findings) }
end
-
- describe "#violates_default_policy_against?" do
- let(:high_severity_dast) { build(:ci_reports_security_finding, severity: 'high', report_type: 'dast') }
- let(:vulnerabilities_allowed) { 0 }
- let(:severity_levels) { %w(critical high) }
- let(:vulnerability_states) { %w(newly_detected) }
-
- subject { security_reports.violates_default_policy_against?(target_reports, vulnerabilities_allowed, severity_levels, vulnerability_states) }
-
- before do
- security_reports.get_report('sast', artifact).add_finding(high_severity_dast)
- end
-
- context 'when the target_reports is `nil`' do
- let(:target_reports) { nil }
-
- context 'with severity levels matching the existing vulnerabilities' do
- it { is_expected.to be(true) }
- end
-
- context "without any severity levels matching the existing vulnerabilities" do
- let(:severity_levels) { %w(critical) }
-
- it { is_expected.to be(false) }
- end
- end
-
- context 'when the target_reports is not `nil`' do
- let(:target_reports) { described_class.new(pipeline) }
-
- context "when a report has a new unsafe vulnerability" do
- context 'with severity levels matching the existing vulnerabilities' do
- it { is_expected.to be(true) }
- end
-
- it { is_expected.to be(true) }
-
- context 'with vulnerabilities_allowed higher than the number of new vulnerabilities' do
- let(:vulnerabilities_allowed) { 10000 }
-
- it { is_expected.to be(false) }
- end
-
- context "without any severity levels matching the existing vulnerabilities" do
- let(:severity_levels) { %w(critical) }
-
- it { is_expected.to be(false) }
- end
- end
-
- context "when none of the reports have a new unsafe vulnerability" do
- before do
- target_reports.get_report('sast', artifact).add_finding(high_severity_dast)
- end
-
- it { is_expected.to be(false) }
- end
-
- context 'with related report_types' do
- let(:report_types) { %w(dast sast) }
-
- subject { security_reports.violates_default_policy_against?(target_reports, vulnerabilities_allowed, severity_levels, vulnerability_states, report_types) }
-
- it { is_expected.to be(true) }
- end
-
- context 'with unrelated report_types' do
- let(:report_types) { %w(dependency_scanning sast) }
-
- subject { security_reports.violates_default_policy_against?(target_reports, vulnerabilities_allowed, severity_levels, vulnerability_states, report_types) }
-
- it { is_expected.to be(false) }
- end
-
- context 'when target_reports is not nil and reports is empty' do
- let(:without_reports) { described_class.new(pipeline) }
-
- subject { without_reports.violates_default_policy_against?(target_reports, vulnerabilities_allowed, severity_levels, vulnerability_states) }
-
- before do
- target_reports.get_report('sast', artifact).add_finding(high_severity_dast)
- end
-
- context 'when require_approval_on_scan_removal feature is enabled' do
- before do
- stub_feature_flags(require_approval_on_scan_removal: true)
- end
-
- it { is_expected.to be(true) }
- end
-
- context 'when require_approval_on_scan_removal feature is disabled' do
- before do
- stub_feature_flags(require_approval_on_scan_removal: false)
- end
-
- it { is_expected.to be(false) }
- end
- end
- end
- end
end
diff --git a/spec/lib/gitlab/ci/runner_instructions_spec.rb b/spec/lib/gitlab/ci/runner_instructions_spec.rb
index f872c631a50..56f69720b87 100644
--- a/spec/lib/gitlab/ci/runner_instructions_spec.rb
+++ b/spec/lib/gitlab/ci/runner_instructions_spec.rb
@@ -69,6 +69,7 @@ RSpec.describe Gitlab::Ci::RunnerInstructions do
'windows' | 'amd64'
'windows' | '386'
'osx' | 'amd64'
+ 'osx' | 'arm64'
end
with_them do
diff --git a/spec/lib/gitlab/ci/trace/archive_spec.rb b/spec/lib/gitlab/ci/trace/archive_spec.rb
index f91cb03883a..582c4ad343f 100644
--- a/spec/lib/gitlab/ci/trace/archive_spec.rb
+++ b/spec/lib/gitlab/ci/trace/archive_spec.rb
@@ -75,15 +75,6 @@ RSpec.describe Gitlab::Ci::Trace::Archive do
include_context 'with FIPS'
end
- context 'with background_upload enabled' do
- before do
- stub_artifacts_object_storage(background_upload: true)
- end
-
- it_behaves_like 'skips validations'
- include_context 'with FIPS'
- end
-
context 'with direct_upload enabled' do
before do
stub_artifacts_object_storage(direct_upload: true)
diff --git a/spec/lib/gitlab/ci/variables/builder_spec.rb b/spec/lib/gitlab/ci/variables/builder_spec.rb
index 52ba85d2df1..5aa752ee429 100644
--- a/spec/lib/gitlab/ci/variables/builder_spec.rb
+++ b/spec/lib/gitlab/ci/variables/builder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache do
+RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache, feature_category: :pipeline_authoring do
include Ci::TemplateHelpers
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, namespace: group) }
@@ -13,7 +13,8 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache do
name: 'rspec:test 1',
pipeline: pipeline,
user: user,
- yaml_variables: [{ key: 'YAML_VARIABLE', value: 'value' }]
+ yaml_variables: [{ key: 'YAML_VARIABLE', value: 'value' }],
+ environment: 'test'
)
end
@@ -32,6 +33,8 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache do
value: job.stage_name },
{ key: 'CI_NODE_TOTAL',
value: '1' },
+ { key: 'CI_ENVIRONMENT_NAME',
+ value: 'test' },
{ key: 'CI_BUILD_NAME',
value: 'rspec:test 1' },
{ key: 'CI_BUILD_STAGE',
@@ -76,6 +79,8 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache do
value: project.full_path_slug },
{ key: 'CI_PROJECT_NAMESPACE',
value: project.namespace.full_path },
+ { key: 'CI_PROJECT_NAMESPACE_ID',
+ value: project.namespace.id.to_s },
{ key: 'CI_PROJECT_ROOT_NAMESPACE',
value: project.namespace.root_ancestor.path },
{ key: 'CI_PROJECT_URL',
@@ -276,11 +281,17 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache do
subject { builder.kubernetes_variables(environment: nil, job: job) }
before do
- allow(Ci::GenerateKubeconfigService).to receive(:new).with(job.pipeline, token: job.token).and_return(service)
+ allow(Ci::GenerateKubeconfigService).to receive(:new).with(job.pipeline, token: job.token, environment: anything).and_return(service)
end
it { is_expected.to include(key: 'KUBECONFIG', value: 'example-kubeconfig', public: false, file: true) }
+ it 'calls the GenerateKubeconfigService with the correct arguments' do
+ expect(Ci::GenerateKubeconfigService).to receive(:new).with(job.pipeline, token: job.token, environment: nil)
+
+ subject
+ end
+
context 'generated config is invalid' do
let(:template_valid) { false }
@@ -297,6 +308,16 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache do
expect(subject['KUBECONFIG'].value).to eq('example-kubeconfig')
expect(subject['OTHER'].value).to eq('some value')
end
+
+ context 'when environment is not nil' do
+ subject { builder.kubernetes_variables(environment: 'production', job: job) }
+
+ it 'passes the environment when generating the KUBECONFIG' do
+ expect(Ci::GenerateKubeconfigService).to receive(:new).with(job.pipeline, token: job.token, environment: 'production')
+
+ subject
+ end
+ end
end
describe '#deployment_variables' do
diff --git a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
index 7f203168706..5c9f156e054 100644
--- a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
@@ -12,6 +12,20 @@ module Gitlab
let(:ci_config) { Gitlab::Ci::Config.new(config_content, user: user) }
let(:result) { described_class.new(ci_config: ci_config, warnings: ci_config&.warnings) }
+ describe '#builds' do
+ context 'when a job has ID tokens' do
+ let(:config_content) do
+ YAML.dump(
+ test: { stage: 'test', script: 'echo', id_tokens: { TEST_ID_TOKEN: { aud: 'https://gitlab.com' } } }
+ )
+ end
+
+ it 'includes `id_tokens`' do
+ expect(result.builds.first[:id_tokens]).to eq({ TEST_ID_TOKEN: { aud: 'https://gitlab.com' } })
+ end
+ end
+ end
+
describe '#config_metadata' do
subject(:config_metadata) { result.config_metadata }
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 5de813f7739..ae98d2e0cad 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -870,6 +870,69 @@ module Gitlab
end
end
end
+
+ describe "hooks" do
+ context 'when it is a simple script' do
+ let(:config) do
+ {
+ test: { script: ["script"],
+ hooks: { pre_get_sources_script: ["echo 1", "echo 2", "pwd"] } }
+ }
+ end
+
+ it "returns hooks in options" do
+ expect(subject[:options][:hooks]).to eq(
+ { pre_get_sources_script: ["echo 1", "echo 2", "pwd"] }
+ )
+ end
+ end
+
+ context 'when it is nested arrays of strings' do
+ let(:config) do
+ {
+ test: { script: ["script"],
+ hooks: { pre_get_sources_script: [[["global script"], "echo 1"], "echo 2", ["ls"], "pwd"] } }
+ }
+ end
+
+ it "returns hooks in options" do
+ expect(subject[:options][:hooks]).to eq(
+ { pre_get_sources_script: ["global script", "echo 1", "echo 2", "ls", "pwd"] }
+ )
+ end
+ end
+
+ context 'when receiving from the default' do
+ let(:config) do
+ {
+ default: { hooks: { pre_get_sources_script: ["echo 1", "echo 2", "pwd"] } },
+ test: { script: ["script"] }
+ }
+ end
+
+ it "inherits hooks" do
+ expect(subject[:options][:hooks]).to eq(
+ { pre_get_sources_script: ["echo 1", "echo 2", "pwd"] }
+ )
+ end
+ end
+
+ context 'when overriding the default' do
+ let(:config) do
+ {
+ default: { hooks: { pre_get_sources_script: ["echo 1", "echo 2", "pwd"] } },
+ test: { script: ["script"],
+ hooks: { pre_get_sources_script: ["echo 3", "echo 4", "pwd"] } }
+ }
+ end
+
+ it "overrides hooks" do
+ expect(subject[:options][:hooks]).to eq(
+ { pre_get_sources_script: ["echo 3", "echo 4", "pwd"] }
+ )
+ end
+ end
+ end
end
describe "Image and service handling" do
@@ -2883,7 +2946,7 @@ module Gitlab
context 'returns errors if job artifacts:when is not an a predefined value' do
let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", artifacts: { when: 1 } } }) }
- it_behaves_like 'returns errors', 'jobs:rspec:artifacts when should be on_success, on_failure or always'
+ it_behaves_like 'returns errors', 'jobs:rspec:artifacts when should be one of: on_success, on_failure, always'
end
context 'returns errors if job artifacts:expire_in is not an a string' do
diff --git a/spec/lib/gitlab/cluster/rack_timeout_observer_spec.rb b/spec/lib/gitlab/cluster/rack_timeout_observer_spec.rb
index 05df4089075..2d4a7a3b170 100644
--- a/spec/lib/gitlab/cluster/rack_timeout_observer_spec.rb
+++ b/spec/lib/gitlab/cluster/rack_timeout_observer_spec.rb
@@ -73,5 +73,28 @@ RSpec.describe Gitlab::Cluster::RackTimeoutObserver do
subject.callback.call(env)
end
end
+
+ context 'when request contains invalid string' do
+ let(:env) do
+ {
+ ::Rack::Timeout::ENV_INFO_KEY => double(state: :timed_out),
+ 'action_dispatch.request.parameters' => {
+ 'controller' => 'foo',
+ 'action' => '\u003c',
+ 'route' => '?8\u003c/x'
+ }
+ }
+ end
+
+ subject { described_class.new }
+
+ it 'sanitizes string' do
+ expect(counter)
+ .to receive(:increment)
+ .with({ controller: 'foo', action: '\\u003c', route: '?8\\u003c/x', state: :timed_out })
+
+ subject.callback.call(env)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/config/entry/attributable_spec.rb b/spec/lib/gitlab/config/entry/attributable_spec.rb
index 8a207bddaae..0a2f8ac2c3a 100644
--- a/spec/lib/gitlab/config/entry/attributable_spec.rb
+++ b/spec/lib/gitlab/config/entry/attributable_spec.rb
@@ -10,10 +10,11 @@ RSpec.describe Gitlab::Config::Entry::Attributable do
end
let(:instance) { node.new }
+ let(:prefix) { nil }
before do
- node.class_eval do
- attributes :name, :test
+ node.class_exec(prefix) do |pre|
+ attributes :name, :test, prefix: pre
end
end
@@ -24,6 +25,17 @@ RSpec.describe Gitlab::Config::Entry::Attributable do
.and_return({ name: 'some name', test: 'some test' })
end
+ context 'and is provided a prefix' do
+ let(:prefix) { :pre }
+
+ it 'returns the value of config' do
+ expect(instance).to have_pre_name
+ expect(instance.pre_name).to eq 'some name'
+ expect(instance).to have_pre_test
+ expect(instance.pre_test).to eq 'some test'
+ end
+ end
+
it 'returns the value of config' do
expect(instance).to have_name
expect(instance.name).to eq 'some name'
diff --git a/spec/lib/gitlab/conflict/file_spec.rb b/spec/lib/gitlab/conflict/file_spec.rb
index 165305476d2..6ea8e6c6706 100644
--- a/spec/lib/gitlab/conflict/file_spec.rb
+++ b/spec/lib/gitlab/conflict/file_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe Gitlab::Conflict::File do
let(:section_keys) { conflict_file.sections.map { |section| section[:id] }.compact }
context 'when resolving everything to the same side' do
- let(:resolution_hash) { section_keys.to_h { |key| [key, 'head'] } }
+ let(:resolution_hash) { section_keys.index_with { 'head' } }
let(:resolved_lines) { conflict_file.resolve_lines(resolution_hash) }
let(:expected_lines) { conflict_file.lines.reject { |line| line.type == 'old' } }
@@ -63,8 +63,8 @@ RSpec.describe Gitlab::Conflict::File do
end
it 'raises ResolutionError when passed a hash without resolutions for all sections' do
- empty_hash = section_keys.to_h { |key| [key, nil] }
- invalid_hash = section_keys.to_h { |key| [key, 'invalid'] }
+ empty_hash = section_keys.index_with { nil }
+ invalid_hash = section_keys.index_with { 'invalid' }
expect { conflict_file.resolve_lines({}) }
.to raise_error(Gitlab::Git::Conflict::Resolver::ResolutionError)
diff --git a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
index aadfb41a46e..88bffd41947 100644
--- a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
+++ b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
@@ -102,13 +102,65 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
end
context 'when sentry is configured' do
+ let(:legacy_dsn) { 'dummy://abc@legacy-sentry.example.com/1' }
+ let(:dsn) { 'dummy://def@sentry.example.com/2' }
+
before do
- stub_sentry_settings
stub_config_setting(host: 'gitlab.example.com')
end
- it 'adds sentry path to CSP without user' do
- expect(directives['connect_src']).to eq("'self' ws://gitlab.example.com dummy://example.com")
+ context 'when legacy sentry is configured' do
+ before do
+ allow(Gitlab.config.sentry).to receive(:enabled).and_return(true)
+ allow(Gitlab.config.sentry).to receive(:clientside_dsn).and_return(legacy_dsn)
+ allow(Gitlab::CurrentSettings).to receive(:sentry_enabled).and_return(false)
+ end
+
+ it 'adds legacy sentry path to CSP' do
+ expect(directives['connect_src']).to eq("'self' ws://gitlab.example.com dummy://legacy-sentry.example.com")
+ end
+ end
+
+ context 'when sentry is configured' do
+ before do
+ allow(Gitlab.config.sentry).to receive(:enabled).and_return(false)
+ allow(Gitlab::CurrentSettings).to receive(:sentry_enabled).and_return(true)
+ allow(Gitlab::CurrentSettings).to receive(:sentry_clientside_dsn).and_return(dsn)
+ end
+
+ it 'adds new sentry path to CSP' do
+ expect(directives['connect_src']).to eq("'self' ws://gitlab.example.com dummy://sentry.example.com")
+ end
+ end
+
+ context 'when sentry settings are from older schemas and sentry setting are missing' do
+ before do
+ allow(Gitlab.config.sentry).to receive(:enabled).and_return(false)
+
+ allow(Gitlab::CurrentSettings).to receive(:respond_to?).with(:sentry_enabled).and_return(false)
+ allow(Gitlab::CurrentSettings).to receive(:sentry_enabled).and_raise(NoMethodError)
+
+ allow(Gitlab::CurrentSettings).to receive(:respond_to?).with(:sentry_clientside_dsn).and_return(false)
+ allow(Gitlab::CurrentSettings).to receive(:sentry_clientside_dsn).and_raise(NoMethodError)
+ end
+
+ it 'config is backwards compatible, does not add sentry path to CSP' do
+ expect(directives['connect_src']).to eq("'self' ws://gitlab.example.com")
+ end
+ end
+
+ context 'when legacy sentry and sentry are both configured' do
+ before do
+ allow(Gitlab.config.sentry).to receive(:enabled).and_return(true)
+ allow(Gitlab.config.sentry).to receive(:clientside_dsn).and_return(legacy_dsn)
+
+ allow(Gitlab::CurrentSettings).to receive(:sentry_enabled).and_return(true)
+ allow(Gitlab::CurrentSettings).to receive(:sentry_clientside_dsn).and_return(dsn)
+ end
+
+ it 'adds both sentry paths to CSP' do
+ expect(directives['connect_src']).to eq("'self' ws://gitlab.example.com dummy://legacy-sentry.example.com dummy://sentry.example.com")
+ end
end
end
diff --git a/spec/lib/gitlab/contributions_calendar_spec.rb b/spec/lib/gitlab/contributions_calendar_spec.rb
index 8a9ab736d46..3736914669a 100644
--- a/spec/lib/gitlab/contributions_calendar_spec.rb
+++ b/spec/lib/gitlab/contributions_calendar_spec.rb
@@ -2,24 +2,24 @@
require 'spec_helper'
-RSpec.describe Gitlab::ContributionsCalendar do
- let(:contributor) { create(:user) }
- let(:user) { create(:user) }
+RSpec.describe Gitlab::ContributionsCalendar, feature_category: :users do
+ let_it_be_with_reload(:contributor) { create(:user) }
+ let_it_be_with_reload(:user) { create(:user) }
let(:travel_time) { nil }
- let(:private_project) do
+ let_it_be_with_reload(:private_project) do
create(:project, :private) do |project|
create(:project_member, user: contributor, project: project)
end
end
- let(:public_project) do
+ let_it_be(:public_project) do
create(:project, :public, :repository) do |project|
create(:project_member, user: contributor, project: project)
end
end
- let(:feature_project) do
+ let_it_be(:feature_project) do
create(:project, :public, :issues_private) do |project|
create(:project_member, user: contributor, project: project).project
end
@@ -30,6 +30,7 @@ RSpec.describe Gitlab::ContributionsCalendar do
let(:tomorrow) { today + 1.day }
let(:last_week) { today - 7.days }
let(:last_year) { today - 1.year }
+ let(:targets) { {} }
before do
travel_to travel_time || Time.now.utc.end_of_day
@@ -44,26 +45,28 @@ RSpec.describe Gitlab::ContributionsCalendar do
end
def create_event(project, day, hour = 0, action = :created, target_symbol = :issue)
- @targets ||= {}
- @targets[project] ||= create(target_symbol, project: project, author: contributor)
+ targets[project] ||= create(target_symbol, project: project, author: contributor)
Event.create!(
project: project,
action: action,
- target_type: @targets[project].class.name,
- target_id: @targets[project].id,
+ target_type: targets[project].class.name,
+ target_id: targets[project].id,
author: contributor,
created_at: DateTime.new(day.year, day.month, day.day, hour)
)
end
- describe '#activity_dates' do
+ describe '#activity_dates', :aggregate_failures do
it "returns a hash of date => count" do
create_event(public_project, last_week)
create_event(public_project, last_week)
create_event(public_project, today)
+ work_item_event = create_event(private_project, today, 0, :created, :work_item)
- expect(calendar.activity_dates).to eq(last_week => 2, today => 1)
+ # make sure the target is a work item as we want to include those in the count
+ expect(work_item_event.target_type).to eq('WorkItem')
+ expect(calendar(contributor).activity_dates).to eq(last_week => 2, today => 2)
end
context "when the user has opted-in for private contributions" do
@@ -176,9 +179,11 @@ RSpec.describe Gitlab::ContributionsCalendar do
it "returns all events for a given date" do
e1 = create_event(public_project, today)
e2 = create_event(public_project, today)
+ e3 = create_event(private_project, today, 0, :created, :work_item)
create_event(public_project, last_week)
- expect(calendar.events_by_date(today)).to contain_exactly(e1, e2)
+ expect([e1, e2, e3].map(&:target_type)).to contain_exactly('WorkItem', 'Issue', 'Issue')
+ expect(calendar(contributor).events_by_date(today)).to contain_exactly(e1, e2, e3)
end
it "only shows private events to authorized users" do
diff --git a/spec/lib/gitlab/counters/buffered_counter_spec.rb b/spec/lib/gitlab/counters/buffered_counter_spec.rb
new file mode 100644
index 00000000000..a1fd97768ea
--- /dev/null
+++ b/spec/lib/gitlab/counters/buffered_counter_spec.rb
@@ -0,0 +1,233 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Counters::BufferedCounter, :clean_gitlab_redis_shared_state do
+ using RSpec::Parameterized::TableSyntax
+
+ subject(:counter) { described_class.new(counter_record, attribute) }
+
+ let(:counter_record) { create(:project_statistics) }
+ let(:attribute) { :build_artifacts_size }
+
+ describe '#get' do
+ it 'returns the value when there is an existing value stored in the counter' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set(counter.key, 456)
+ end
+
+ expect(counter.get).to eq(456)
+ end
+
+ it 'returns 0 when there is no existing value' do
+ expect(counter.get).to eq(0)
+ end
+ end
+
+ describe '#increment' do
+ it 'sets a new key by the given value' do
+ counter.increment(123)
+
+ expect(counter.get).to eq(123)
+ end
+
+ it 'increments an existing key by the given value' do
+ counter.increment(100)
+ counter.increment(123)
+
+ expect(counter.get).to eq(100 + 123)
+ end
+
+ it 'returns the new value' do
+ counter.increment(123)
+
+ expect(counter.increment(23)).to eq(146)
+ end
+
+ it 'schedules a worker to commit the counter into database' do
+ expect(FlushCounterIncrementsWorker).to receive(:perform_in)
+ .with(described_class::WORKER_DELAY, counter_record.class.to_s, counter_record.id, attribute)
+
+ counter.increment(123)
+ end
+ end
+
+ describe '#reset!' do
+ before do
+ allow(counter_record).to receive(:update!)
+
+ counter.increment(123)
+ end
+
+ it 'removes the key from Redis' do
+ counter.reset!
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.exists?(counter.key)).to eq(false)
+ end
+ end
+
+ it 'resets the counter to 0' do
+ counter.reset!
+
+ expect(counter.get).to eq(0)
+ end
+
+ it 'resets the record to 0' do
+ expect(counter_record).to receive(:update!).with(attribute => 0)
+
+ counter.reset!
+ end
+ end
+
+ describe '#commit_increment!' do
+ it 'obtains an exclusive lease during processing' do
+ expect(counter).to receive(:with_exclusive_lease).and_call_original
+
+ counter.commit_increment!
+ end
+
+ context 'when there is an amount to commit' do
+ let(:increments) { [10, -3] }
+
+ before do
+ increments.each { |i| counter.increment(i) }
+ end
+
+ it 'commits the increment into the database' do
+ expect { counter.commit_increment! }
+ .to change { counter_record.reset.read_attribute(attribute) }.by(increments.sum)
+ end
+
+ it 'removes the increment entry from Redis' do
+ Gitlab::Redis::SharedState.with do |redis|
+ key_exists = redis.exists?(counter.key)
+ expect(key_exists).to be_truthy
+ end
+
+ counter.commit_increment!
+
+ Gitlab::Redis::SharedState.with do |redis|
+ key_exists = redis.exists?(counter.key)
+ expect(key_exists).to be_falsey
+ end
+ end
+ end
+
+ context 'when there are no counters to flush' do
+ context 'when there are no counters in the relative :flushed key' do
+ it 'does not change the record' do
+ expect { counter.commit_increment! }.not_to change { counter_record.reset.attributes }
+ end
+ end
+
+ # This can be the case where updating counters in the database fails with error
+ # and retrying the worker will retry flushing the counters but the main key has
+ # disappeared and the increment has been moved to the "<...>:flushed" key.
+ context 'when there are counters in the relative :flushed key' do
+ let(:flushed_amount) { 10 }
+
+ before do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.incrby(counter.flushed_key, flushed_amount)
+ end
+ end
+
+ it 'updates the record' do
+ expect { counter.commit_increment! }
+ .to change { counter_record.reset.read_attribute(attribute) }.by(flushed_amount)
+ end
+
+ it 'deletes the relative :flushed key' do
+ counter.commit_increment!
+
+ Gitlab::Redis::SharedState.with do |redis|
+ key_exists = redis.exists?(counter.flushed_key)
+ expect(key_exists).to be_falsey
+ end
+ end
+ end
+
+ context 'when deleting :flushed key fails' do
+ before do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.incrby(counter.flushed_key, 10)
+
+ allow(redis).to receive(:del).and_raise('could not delete key')
+ end
+ end
+
+ it 'does a rollback of the counter update' do
+ expect { counter.commit_increment! }.to raise_error('could not delete key')
+
+ expect(counter_record.reset.read_attribute(attribute)).to eq(0)
+ end
+ end
+
+ context 'when the counter record has after_commit callbacks' do
+ it 'has registered callbacks' do
+ expect(counter_record.class.after_commit_callbacks.size).to eq(1)
+ end
+
+ context 'when there are increments to flush' do
+ before do
+ counter.increment(10)
+ end
+
+ it 'executes the callbacks' do
+ expect(counter_record).to receive(:execute_after_commit_callbacks).and_call_original
+
+ counter.commit_increment!
+ end
+ end
+
+ context 'when there are no increments to flush' do
+ it 'does not execute the callbacks' do
+ expect(counter_record).not_to receive(:execute_after_commit_callbacks).and_call_original
+
+ counter.commit_increment!
+ end
+ end
+ end
+ end
+ end
+
+ describe '#amount_to_be_flushed' do
+ let(:increment_key) { counter.key }
+ let(:flushed_key) { counter.flushed_key }
+
+ where(:increment, :flushed, :result, :flushed_key_present) do
+ nil | nil | 0 | false
+ nil | 0 | 0 | false
+ 0 | 0 | 0 | false
+ 1 | 0 | 1 | true
+ 1 | nil | 1 | true
+ 1 | 1 | 2 | true
+ 1 | -2 | -1 | true
+ -1 | 1 | 0 | false
+ end
+
+ with_them do
+ before do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set(increment_key, increment) if increment
+ redis.set(flushed_key, flushed) if flushed
+ end
+ end
+
+ it 'returns the current value to be flushed' do
+ value = counter.amount_to_be_flushed
+ expect(value).to eq(result)
+ end
+
+ it 'drops the increment key and creates the flushed key if it does not exist' do
+ counter.amount_to_be_flushed
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.exists?(increment_key)).to eq(false)
+ expect(redis.exists?(flushed_key)).to eq(flushed_key_present)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/counters/legacy_counter_spec.rb b/spec/lib/gitlab/counters/legacy_counter_spec.rb
new file mode 100644
index 00000000000..e66b1ce08c4
--- /dev/null
+++ b/spec/lib/gitlab/counters/legacy_counter_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Counters::LegacyCounter do
+ subject(:counter) { described_class.new(counter_record, attribute) }
+
+ let(:counter_record) { create(:project_statistics) }
+ let(:attribute) { :snippets_size }
+ let(:amount) { 123 }
+
+ describe '#increment' do
+ it 'increments the attribute in the counter record' do
+ expect { counter.increment(amount) }.to change { counter_record.reload.method(attribute).call }.by(amount)
+ end
+
+ it 'returns the value after the increment' do
+ counter.increment(100)
+
+ expect(counter.increment(amount)).to eq(100 + amount)
+ end
+
+ it 'executes after counter_record after commit callback' do
+ expect(counter_record).to receive(:execute_after_commit_callbacks).and_call_original
+
+ counter.increment(amount)
+ end
+ end
+
+ describe '#reset!' do
+ before do
+ allow(counter_record).to receive(:update!)
+ end
+
+ it 'resets the record to 0' do
+ expect(counter_record).to receive(:update!).with(attribute => 0)
+
+ counter.reset!
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
index 0e7d7f1efda..92ffeee8509 100644
--- a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
@@ -29,8 +29,8 @@ RSpec.describe Gitlab::CycleAnalytics::StageSummary do
context 'when from date is given' do
before do
- Timecop.freeze(5.days.ago) { create(:issue, project: project) }
- Timecop.freeze(5.days.from_now) { create(:issue, project: project) }
+ travel_to(5.days.ago) { create(:issue, project: project) }
+ travel_to(5.days.from_now) { create(:issue, project: project) }
end
it "finds the number of issues created after the 'from date'" do
@@ -45,15 +45,15 @@ RSpec.describe Gitlab::CycleAnalytics::StageSummary do
end
it "doesn't find issues from other projects" do
- Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project)) }
+ travel_to(5.days.from_now) { create(:issue, project: create(:project)) }
expect(subject[:value]).to eq('-')
end
context 'when `to` parameter is given' do
before do
- Timecop.freeze(5.days.ago) { create(:issue, project: project) }
- Timecop.freeze(5.days.from_now) { create(:issue, project: project) }
+ travel_to(5.days.ago) { create(:issue, project: project) }
+ travel_to(5.days.from_now) { create(:issue, project: project) }
end
it "doesn't find any record" do
@@ -78,8 +78,8 @@ RSpec.describe Gitlab::CycleAnalytics::StageSummary do
context 'when from date is given' do
before do
- Timecop.freeze(5.days.ago) { create_commit("Test message", project, user, 'master') }
- Timecop.freeze(5.days.from_now) { create_commit("Test message", project, user, 'master') }
+ travel_to(5.days.ago) { create_commit("Test message", project, user, 'master') }
+ travel_to(5.days.from_now) { create_commit("Test message", project, user, 'master') }
end
it "finds the number of commits created after the 'from date'" do
@@ -94,21 +94,21 @@ RSpec.describe Gitlab::CycleAnalytics::StageSummary do
end
it "doesn't find commits from other projects" do
- Timecop.freeze(5.days.from_now) { create_commit("Test message", create(:project, :repository), user, 'master') }
+ travel_to(5.days.from_now) { create_commit("Test message", create(:project, :repository), user, 'master') }
expect(subject[:value]).to eq('-')
end
it "finds a large (> 100) number of commits if present" do
- Timecop.freeze(5.days.from_now) { create_commit("Test message", project, user, 'master', count: 100) }
+ travel_to(5.days.from_now) { create_commit("Test message", project, user, 'master', count: 100) }
expect(subject[:value]).to eq('100')
end
context 'when `to` parameter is given' do
before do
- Timecop.freeze(5.days.ago) { create_commit("Test message", project, user, 'master') }
- Timecop.freeze(5.days.from_now) { create_commit("Test message", project, user, 'master') }
+ travel_to(5.days.ago) { create_commit("Test message", project, user, 'master') }
+ travel_to(5.days.from_now) { create_commit("Test message", project, user, 'master') }
end
it "doesn't find any record" do
diff --git a/spec/lib/gitlab/data_builder/deployment_spec.rb b/spec/lib/gitlab/data_builder/deployment_spec.rb
index 8ee57542d43..bf08e782035 100644
--- a/spec/lib/gitlab/data_builder/deployment_spec.rb
+++ b/spec/lib/gitlab/data_builder/deployment_spec.rb
@@ -12,8 +12,8 @@ RSpec.describe Gitlab::DataBuilder::Deployment do
expect(data[:object_kind]).to eq('deployment')
end
- it 'returns data for the given build' do
- environment = create(:environment, name: "somewhere")
+ it 'returns data for the given build', :aggregate_failures do
+ environment = create(:environment, name: 'somewhere/1', external_url: 'https://test.com')
project = create(:project, :repository, name: 'myproj')
commit = project.commit('HEAD')
deployment = create(:deployment, status: :failed, environment: environment, sha: commit.sha, project: project)
@@ -30,7 +30,9 @@ RSpec.describe Gitlab::DataBuilder::Deployment do
expect(data[:deployment_id]).to eq(deployment.id)
expect(data[:deployable_id]).to eq(deployable.id)
expect(data[:deployable_url]).to eq(expected_deployable_url)
- expect(data[:environment]).to eq("somewhere")
+ expect(data[:environment]).to eq('somewhere/1')
+ expect(data[:environment_slug]).to eq('somewhere-1-78avk6')
+ expect(data[:environment_external_url]).to eq('https://test.com')
expect(data[:project]).to eq(project.hook_attrs)
expect(data[:short_sha]).to eq(deployment.short_sha)
expect(data[:user]).to eq(deployment.deployed_by.hook_attrs)
diff --git a/spec/lib/gitlab/database/gitlab_schema_spec.rb b/spec/lib/gitlab/database/gitlab_schema_spec.rb
index 72950895022..4b37cbda047 100644
--- a/spec/lib/gitlab/database/gitlab_schema_spec.rb
+++ b/spec/lib/gitlab/database/gitlab_schema_spec.rb
@@ -1,42 +1,86 @@
# frozen_string_literal: true
require 'spec_helper'
+RSpec.shared_examples 'validate path globs' do |path_globs|
+ it 'returns an array of path globs' do
+ expect(path_globs).to be_an(Array)
+ expect(path_globs).to all(be_an(Pathname))
+ end
+end
+
RSpec.describe Gitlab::Database::GitlabSchema do
- describe '.tables_to_schema' do
- it 'all tables have assigned a known gitlab_schema' do
- expect(described_class.tables_to_schema).to all(
+ describe '.views_and_tables_to_schema' do
+ it 'all tables and views have assigned a known gitlab_schema' do
+ expect(described_class.views_and_tables_to_schema).to all(
match([be_a(String), be_in(Gitlab::Database.schemas_to_base_models.keys.map(&:to_sym))])
)
end
# This being run across different databases indirectly also tests
# a general consistency of structure across databases
- Gitlab::Database.database_base_models.select { |k, _| k != 'geo' }.each do |db_config_name, db_class|
+ Gitlab::Database.database_base_models.except(:geo).each do |db_config_name, db_class|
context "for #{db_config_name} using #{db_class}" do
let(:db_data_sources) { db_class.connection.data_sources }
# The Geo database does not share the same structure as all decomposed databases
- subject { described_class.tables_to_schema.select { |_, v| v != :gitlab_geo } }
+ subject { described_class.views_and_tables_to_schema.select { |_, v| v != :gitlab_geo } }
it 'new data sources are added' do
- missing_tables = db_data_sources.to_set - subject.keys
+ missing_data_sources = db_data_sources.to_set - subject.keys
- expect(missing_tables).to be_empty, \
- "Missing table(s) #{missing_tables.to_a} not found in #{described_class}.tables_to_schema. " \
- "Any new tables must be added to #{described_class::GITLAB_SCHEMAS_FILE}."
+ expect(missing_data_sources).to be_empty, \
+ "Missing table/view(s) #{missing_data_sources.to_a} not found in " \
+ "#{described_class}.views_and_tables_to_schema. " \
+ "Any new tables or views must be added to the database dictionary. " \
+ "More info: https://docs.gitlab.com/ee/development/database/database_dictionary.html"
end
it 'non-existing data sources are removed' do
- extra_tables = subject.keys.to_set - db_data_sources
+ extra_data_sources = subject.keys.to_set - db_data_sources
- expect(extra_tables).to be_empty, \
- "Extra table(s) #{extra_tables.to_a} found in #{described_class}.tables_to_schema. " \
- "Any removed or renamed tables must be removed from #{described_class::GITLAB_SCHEMAS_FILE}."
+ expect(extra_data_sources).to be_empty, \
+ "Extra table/view(s) #{extra_data_sources.to_a} found in #{described_class}.views_and_tables_to_schema. " \
+ "Any removed or renamed tables or views must be removed from the database dictionary. " \
+ "More info: https://docs.gitlab.com/ee/development/database/database_dictionary.html"
end
end
end
end
+ describe '.dictionary_path_globs' do
+ include_examples 'validate path globs', described_class.dictionary_path_globs
+ end
+
+ describe '.view_path_globs' do
+ include_examples 'validate path globs', described_class.view_path_globs
+ end
+
+ describe '.tables_to_schema' do
+ let(:database_models) { Gitlab::Database.database_base_models.except(:geo) }
+ let(:views) { database_models.flat_map { |_, m| m.connection.views }.sort.uniq }
+
+ subject { described_class.tables_to_schema }
+
+ it 'returns only tables' do
+ tables = subject.keys
+
+ expect(tables).not_to include(views.to_set)
+ end
+ end
+
+ describe '.views_to_schema' do
+ let(:database_models) { Gitlab::Database.database_base_models.except(:geo) }
+ let(:tables) { database_models.flat_map { |_, m| m.connection.tables }.sort.uniq }
+
+ subject { described_class.views_to_schema }
+
+ it 'returns only views' do
+ views = subject.keys
+
+ expect(views).not_to include(tables.to_set)
+ end
+ end
+
describe '.table_schema' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/lib/gitlab/database/load_balancing/sidekiq_client_middleware_spec.rb b/spec/lib/gitlab/database/load_balancing/sidekiq_client_middleware_spec.rb
index b7915e6cf69..7eb20f77417 100644
--- a/spec/lib/gitlab/database/load_balancing/sidekiq_client_middleware_spec.rb
+++ b/spec/lib/gitlab/database/load_balancing/sidekiq_client_middleware_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Database::LoadBalancing::SidekiqClientMiddleware do
+RSpec.describe Gitlab::Database::LoadBalancing::SidekiqClientMiddleware, feature_category: :database do
let(:middleware) { described_class.new }
let(:worker_class) { 'TestDataConsistencyWorker' }
@@ -34,8 +34,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqClientMiddleware do
data_consistency data_consistency, feature_flag: feature_flag
- def perform(*args)
- end
+ def perform(*args); end
end
end
@@ -83,21 +82,41 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqClientMiddleware do
allow(Gitlab::Database::LoadBalancing::Session.current).to receive(:use_primary?).and_return(false)
end
- it 'passes database_replica_location' do
- expected_location = {}
+ context 'when replica hosts are available' do
+ it 'passes database_replica_location' do
+ expected_location = {}
- Gitlab::Database::LoadBalancing.each_load_balancer do |lb|
- expect(lb.host)
- .to receive(:database_replica_location)
- .and_return(location)
+ Gitlab::Database::LoadBalancing.each_load_balancer do |lb|
+ expect(lb.host)
+ .to receive(:database_replica_location)
+ .and_return(location)
- expected_location[lb.name] = location
+ expected_location[lb.name] = location
+ end
+
+ run_middleware
+
+ expect(job['wal_locations']).to eq(expected_location)
+ expect(job['wal_location_source']).to eq(:replica)
end
+ end
- run_middleware
+ context 'when no replica hosts are available' do
+ it 'passes primary_write_location' do
+ expected_location = {}
- expect(job['wal_locations']).to eq(expected_location)
- expect(job['wal_location_source']).to eq(:replica)
+ Gitlab::Database::LoadBalancing.each_load_balancer do |lb|
+ expect(lb).to receive(:host).and_return(nil)
+ expect(lb).to receive(:primary_write_location).and_return(location)
+
+ expected_location[lb.name] = location
+ end
+
+ run_middleware
+
+ expect(job['wal_locations']).to eq(expected_location)
+ expect(job['wal_location_source']).to eq(:replica)
+ end
end
include_examples 'job data consistency'
diff --git a/spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb b/spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb
index 61b63016f1a..abf10456d0a 100644
--- a/spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb
+++ b/spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb
@@ -33,8 +33,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware, :clean_
data_consistency data_consistency, feature_flag: feature_flag
- def perform(*args)
- end
+ def perform(*args); end
end
end
@@ -332,28 +331,6 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware, :clean_
expect(middleware.send(:databases_in_sync?, locations))
.to eq(false)
end
-
- context 'when "indifferent_wal_location_keys" FF is off' do
- before do
- stub_feature_flags(indifferent_wal_location_keys: false)
- end
-
- it 'returns true when the load balancers are not in sync' do
- locations = {}
-
- Gitlab::Database::LoadBalancing.each_load_balancer do |lb|
- locations[lb.name.to_s] = 'foo'
-
- allow(lb)
- .to receive(:select_up_to_date_host)
- .with('foo')
- .and_return(false)
- end
-
- expect(middleware.send(:databases_in_sync?, locations))
- .to eq(true)
- end
- end
end
end
diff --git a/spec/lib/gitlab/database/lock_writes_manager_spec.rb b/spec/lib/gitlab/database/lock_writes_manager_spec.rb
index b1cc8add55a..242b2040eaa 100644
--- a/spec/lib/gitlab/database/lock_writes_manager_spec.rb
+++ b/spec/lib/gitlab/database/lock_writes_manager_spec.rb
@@ -37,6 +37,14 @@ RSpec.describe Gitlab::Database::LockWritesManager do
it 'returns true for a table that is locked for writes' do
expect { subject.lock_writes }.to change { subject.table_locked_for_writes?(test_table) }.from(false).to(true)
end
+
+ context 'for detached partition tables in another schema' do
+ let(:test_table) { 'gitlab_partitions_dynamic._test_table_20220101' }
+
+ it 'returns true for a table that is locked for writes' do
+ expect { subject.lock_writes }.to change { subject.table_locked_for_writes?(test_table) }.from(false).to(true)
+ end
+ end
end
describe '#lock_writes' do
diff --git a/spec/lib/gitlab/database/migration_helpers/automatic_lock_writes_on_tables_spec.rb b/spec/lib/gitlab/database/migration_helpers/automatic_lock_writes_on_tables_spec.rb
new file mode 100644
index 00000000000..9fd49b312eb
--- /dev/null
+++ b/spec/lib/gitlab/database/migration_helpers/automatic_lock_writes_on_tables_spec.rb
@@ -0,0 +1,334 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::MigrationHelpers::AutomaticLockWritesOnTables,
+ :reestablished_active_record_base, query_analyzers: false do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:schema_class) { Class.new(Gitlab::Database::Migration[2.1]) }
+ let(:gitlab_main_table_name) { :_test_gitlab_main_table }
+ let(:gitlab_ci_table_name) { :_test_gitlab_ci_table }
+ let(:gitlab_geo_table_name) { :_test_gitlab_geo_table }
+ let(:gitlab_shared_table_name) { :_test_table }
+
+ before do
+ stub_feature_flags(automatic_lock_writes_on_table: true)
+ reconfigure_db_connection(model: ActiveRecord::Base, config_model: config_model)
+ end
+
+ shared_examples 'does not lock writes on table' do |config_model|
+ let(:config_model) { config_model }
+
+ it 'allows deleting records from the table' do
+ allow_next_instance_of(Gitlab::Database::LockWritesManager) do |instance|
+ expect(instance).not_to receive(:lock_writes)
+ end
+
+ run_migration
+
+ expect do
+ migration_class.connection.execute("DELETE FROM #{table_name}")
+ end.not_to raise_error
+ end
+ end
+
+ shared_examples 'locks writes on table' do |config_model|
+ let(:config_model) { config_model }
+
+ it 'errors on deleting' do
+ allow_next_instance_of(Gitlab::Database::LockWritesManager) do |instance|
+ expect(instance).to receive(:lock_writes).and_call_original
+ end
+
+ run_migration
+
+ expect do
+ migration_class.connection.execute("DELETE FROM #{table_name}")
+ end.to raise_error(ActiveRecord::StatementInvalid, /is write protected/)
+ end
+ end
+
+ context 'when executing create_table migrations' do
+ let(:create_gitlab_main_table_migration_class) { create_table_migration(gitlab_main_table_name) }
+ let(:create_gitlab_ci_table_migration_class) { create_table_migration(gitlab_ci_table_name) }
+ let(:create_gitlab_shared_table_migration_class) { create_table_migration(gitlab_shared_table_name) }
+
+ context 'when single database' do
+ let(:config_model) { Gitlab::Database.database_base_models[:main] }
+
+ before do
+ skip_if_multiple_databases_are_setup
+ end
+
+ it 'does not lock any newly created tables' do
+ allow_next_instance_of(Gitlab::Database::LockWritesManager) do |instance|
+ expect(instance).not_to receive(:lock_writes)
+ end
+
+ create_gitlab_main_table_migration_class.migrate(:up)
+ create_gitlab_ci_table_migration_class.migrate(:up)
+ create_gitlab_shared_table_migration_class.migrate(:up)
+
+ expect do
+ create_gitlab_main_table_migration_class.connection.execute("DELETE FROM #{gitlab_main_table_name}")
+ create_gitlab_ci_table_migration_class.connection.execute("DELETE FROM #{gitlab_ci_table_name}")
+ create_gitlab_shared_table_migration_class.connection.execute("DELETE FROM #{gitlab_shared_table_name}")
+ end.not_to raise_error
+ end
+ end
+
+ context 'when multiple databases' do
+ before do
+ skip_if_multiple_databases_not_setup
+ end
+
+ let(:skip_automatic_lock_on_writes) { false }
+ let(:migration_class) { create_table_migration(table_name, skip_automatic_lock_on_writes) }
+ let(:run_migration) { migration_class.migrate(:up) }
+
+ context 'for creating a gitlab_main table' do
+ let(:table_name) { gitlab_main_table_name }
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main]
+ it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:ci]
+
+ context 'when table listed as a deleted table' do
+ before do
+ stub_const("Gitlab::Database::GitlabSchema::DELETED_TABLES", { table_name.to_s => :gitlab_main })
+ end
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci]
+ end
+
+ context 'when the migration skips automatic locking of tables' do
+ let(:skip_automatic_lock_on_writes) { true }
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci]
+ end
+
+ context 'when the SKIP_AUTOMATIC_LOCK_ON_WRITES feature flag is set' do
+ before do
+ stub_env('SKIP_AUTOMATIC_LOCK_ON_WRITES' => 'true')
+ end
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci]
+ end
+
+ context 'when the automatic_lock_writes_on_table feature flag is disabled' do
+ before do
+ stub_feature_flags(automatic_lock_writes_on_table: false)
+ end
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci]
+ end
+ end
+
+ context 'for creating a gitlab_ci table' do
+ let(:table_name) { gitlab_ci_table_name }
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci]
+ it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:main]
+
+ context 'when table listed as a deleted table' do
+ before do
+ stub_const("Gitlab::Database::GitlabSchema::DELETED_TABLES", { table_name.to_s => :gitlab_ci })
+ end
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main]
+ end
+
+ context 'when the migration skips automatic locking of tables' do
+ let(:skip_automatic_lock_on_writes) { true }
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main]
+ end
+
+ context 'when the SKIP_AUTOMATIC_LOCK_ON_WRITES feature flag is set' do
+ before do
+ stub_env('SKIP_AUTOMATIC_LOCK_ON_WRITES' => 'true')
+ end
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main]
+ end
+
+ context 'when the automatic_lock_writes_on_table feature flag is disabled' do
+ before do
+ stub_feature_flags(automatic_lock_writes_on_table: false)
+ end
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main]
+ end
+ end
+
+ context 'for creating gitlab_shared table' do
+ let(:table_name) { gitlab_shared_table_name }
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main]
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci]
+ end
+
+ context 'for creating a gitlab_geo table' do
+ before do
+ skip unless geo_configured?
+ end
+
+ let(:table_name) { gitlab_geo_table_name }
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:geo]
+ end
+
+ context 'for creating an unknown gitlab_schema table' do
+ let(:table_name) { :foobar } # no gitlab_schema defined
+ let(:config_model) { Gitlab::Database.database_base_models[:main] }
+
+ it "raises an error about undefined gitlab_schema" do
+ expected_error_message = <<~ERROR
+ No gitlab_schema is defined for the table #{table_name}. Please consider
+ adding it to the database dictionary.
+ More info: https://docs.gitlab.com/ee/development/database/database_dictionary.html
+ ERROR
+
+ expect { run_migration }.to raise_error(expected_error_message)
+ end
+ end
+ end
+ end
+
+ context 'when renaming a table' do
+ before do
+ skip_if_multiple_databases_not_setup
+ create_table_migration(old_table_name).migrate(:up) # create the table first before renaming it
+ end
+
+ let(:migration_class) { rename_table_migration(old_table_name, table_name) }
+ let(:run_migration) { migration_class.migrate(:up) }
+
+ context 'when a gitlab_main table' do
+ let(:old_table_name) { gitlab_main_table_name }
+ let(:table_name) { :_test_gitlab_main_new_table }
+ let(:database_base_model) { Gitlab::Database.database_base_models[:main] }
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main]
+ it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:ci]
+ end
+
+ context 'when a gitlab_ci table' do
+ let(:old_table_name) { gitlab_ci_table_name }
+ let(:table_name) { :_test_gitlab_ci_new_table }
+ let(:database_base_model) { Gitlab::Database.database_base_models[:ci] }
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci]
+ it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:main]
+ end
+ end
+
+ context 'when reversing drop_table migrations' do
+ let(:drop_gitlab_main_table_migration_class) { drop_table_migration(gitlab_main_table_name) }
+ let(:drop_gitlab_ci_table_migration_class) { drop_table_migration(gitlab_ci_table_name) }
+ let(:drop_gitlab_shared_table_migration_class) { drop_table_migration(gitlab_shared_table_name) }
+
+ context 'when single database' do
+ let(:config_model) { Gitlab::Database.database_base_models[:main] }
+
+ before do
+ skip_if_multiple_databases_are_setup
+ end
+
+ it 'does not lock any newly created tables' do
+ allow_next_instance_of(Gitlab::Database::LockWritesManager) do |instance|
+ expect(instance).not_to receive(:lock_writes)
+ end
+
+ drop_gitlab_main_table_migration_class.connection.execute("CREATE TABLE #{gitlab_main_table_name}()")
+ drop_gitlab_ci_table_migration_class.connection.execute("CREATE TABLE #{gitlab_ci_table_name}()")
+ drop_gitlab_shared_table_migration_class.connection.execute("CREATE TABLE #{gitlab_shared_table_name}()")
+
+ drop_gitlab_main_table_migration_class.migrate(:up)
+ drop_gitlab_ci_table_migration_class.migrate(:up)
+ drop_gitlab_shared_table_migration_class.migrate(:up)
+
+ drop_gitlab_main_table_migration_class.migrate(:down)
+ drop_gitlab_ci_table_migration_class.migrate(:down)
+ drop_gitlab_shared_table_migration_class.migrate(:down)
+
+ expect do
+ drop_gitlab_main_table_migration_class.connection.execute("DELETE FROM #{gitlab_main_table_name}")
+ drop_gitlab_ci_table_migration_class.connection.execute("DELETE FROM #{gitlab_ci_table_name}")
+ drop_gitlab_shared_table_migration_class.connection.execute("DELETE FROM #{gitlab_shared_table_name}")
+ end.not_to raise_error
+ end
+ end
+
+ context 'when multiple databases' do
+ before do
+ skip_if_multiple_databases_not_setup
+ migration_class.connection.execute("CREATE TABLE #{table_name}()")
+ migration_class.migrate(:up)
+ end
+
+ let(:migration_class) { drop_table_migration(table_name) }
+ let(:run_migration) { migration_class.migrate(:down) }
+
+ context 'for re-creating a gitlab_main table' do
+ let(:table_name) { gitlab_main_table_name }
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main]
+ it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:ci]
+ end
+
+ context 'for re-creating a gitlab_ci table' do
+ let(:table_name) { gitlab_ci_table_name }
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci]
+ it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:main]
+ end
+
+ context 'for re-creating a gitlab_shared table' do
+ let(:table_name) { gitlab_shared_table_name }
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main]
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci]
+ end
+ end
+ end
+
+ def create_table_migration(table_name, skip_lock_on_writes = false)
+ migration_class = Class.new(schema_class) do
+ class << self; attr_accessor :table_name; end
+ def change
+ create_table self.class.table_name
+ end
+ end
+ migration_class.skip_automatic_lock_on_writes = skip_lock_on_writes
+ migration_class.tap { |klass| klass.table_name = table_name }
+ end
+
+ def rename_table_migration(old_table_name, new_table_name)
+ migration_class = Class.new(schema_class) do
+ class << self; attr_accessor :old_table_name, :new_table_name; end
+ def change
+ rename_table self.class.old_table_name, self.class.new_table_name
+ end
+ end
+
+ migration_class.tap do |klass|
+ klass.old_table_name = old_table_name
+ klass.new_table_name = new_table_name
+ end
+ end
+
+ def drop_table_migration(table_name)
+ migration_class = Class.new(schema_class) do
+ class << self; attr_accessor :table_name; end
+ def change
+ drop_table(self.class.table_name) {}
+ end
+ end
+ migration_class.tap { |klass| klass.table_name = table_name }
+ end
+
+ def geo_configured?
+ !!ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'geo')
+ end
+end
diff --git a/spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb b/spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb
index e43cfe0814e..e8045f5afec 100644
--- a/spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a
describe '#restrict_gitlab_migration' do
it 'invalid schema raises exception' do
- expect { schema_class.restrict_gitlab_migration gitlab_schema: :gitlab_non_exisiting }
+ expect { schema_class.restrict_gitlab_migration gitlab_schema: :gitlab_non_existing }
.to raise_error /Unknown 'gitlab_schema:/
end
@@ -102,7 +102,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a
"does add index to projects in gitlab_main and gitlab_ci" => {
migration: ->(klass) do
def change
- # Due to running in transactin we cannot use `add_concurrent_index`
+ # Due to running in transaction we cannot use `add_concurrent_index`
add_index :projects, :hidden
end
end,
@@ -185,8 +185,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a
execute("create schema __test_schema")
end
- def down
- end
+ def down; end
end,
query_matcher: /create schema __test_schema/,
expected: {
@@ -306,8 +305,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a
detached_partitions_class.create!(drop_after: Time.current, table_name: '_test_table')
end
- def down
- end
+ def down; end
def detached_partitions_class
Class.new(Gitlab::Database::Migration[2.0]::MigrationRecord) do
@@ -450,8 +448,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a
ApplicationSetting.last
end
- def down
- end
+ def down; end
end,
query_matcher: /FROM "application_settings"/,
expected: {
@@ -475,8 +472,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a
Feature.enabled?(:redis_hll_tracking, type: :ops)
end
- def down
- end
+ def down; end
end,
query_matcher: /FROM "features"/,
expected: {
@@ -505,8 +501,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a
end
end
- def down
- end
+ def down; end
end,
query_matcher: /FROM ci_builds/,
setup: -> (_) { skip_if_multiple_databases_not_setup },
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 65fbc8d9935..30eeff31326 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -1199,18 +1199,6 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
end
end
- describe '#add_column_with_default' do
- let(:column) { Project.columns.find { |c| c.name == "id" } }
-
- it 'delegates to #add_column' do
- expect(model).to receive(:add_column).with(:projects, :foo, :integer, default: 10, limit: nil, null: true)
-
- model.add_column_with_default(:projects, :foo, :integer,
- default: 10,
- allow_null: true)
- end
- end
-
describe '#rename_column_concurrently' do
context 'in a transaction' do
it 'raises RuntimeError' do
@@ -2006,170 +1994,6 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
end
end
- describe 'sidekiq migration helpers', :redis do
- let(:worker) do
- Class.new do
- include Sidekiq::Worker
-
- sidekiq_options queue: 'test'
-
- def self.name
- 'WorkerClass'
- end
- end
- end
-
- let(:same_queue_different_worker) do
- Class.new do
- include Sidekiq::Worker
-
- sidekiq_options queue: 'test'
-
- def self.name
- 'SameQueueDifferentWorkerClass'
- end
- end
- end
-
- let(:unrelated_worker) do
- Class.new do
- include Sidekiq::Worker
-
- sidekiq_options queue: 'unrelated'
-
- def self.name
- 'UnrelatedWorkerClass'
- end
- end
- end
-
- before do
- stub_const(worker.name, worker)
- stub_const(unrelated_worker.name, unrelated_worker)
- stub_const(same_queue_different_worker.name, same_queue_different_worker)
- end
-
- describe '#sidekiq_remove_jobs', :clean_gitlab_redis_queues do
- def clear_queues
- Sidekiq::Queue.new('test').clear
- Sidekiq::Queue.new('unrelated').clear
- Sidekiq::RetrySet.new.clear
- Sidekiq::ScheduledSet.new.clear
- end
-
- around do |example|
- clear_queues
- Sidekiq::Testing.disable!(&example)
- clear_queues
- end
-
- it "removes all related job instances from the job class's queue" do
- worker.perform_async
- same_queue_different_worker.perform_async
- unrelated_worker.perform_async
-
- queue_we_care_about = Sidekiq::Queue.new(worker.queue)
- unrelated_queue = Sidekiq::Queue.new(unrelated_worker.queue)
-
- expect(queue_we_care_about.size).to eq(2)
- expect(unrelated_queue.size).to eq(1)
-
- model.sidekiq_remove_jobs(job_klass: worker)
-
- expect(queue_we_care_about.size).to eq(1)
- expect(queue_we_care_about.map(&:klass)).not_to include(worker.name)
- expect(queue_we_care_about.map(&:klass)).to include(
- same_queue_different_worker.name
- )
- expect(unrelated_queue.size).to eq(1)
- end
-
- context 'when job instances are in the scheduled set' do
- it 'removes all related job instances from the scheduled set' do
- worker.perform_in(1.hour)
- unrelated_worker.perform_in(1.hour)
-
- scheduled = Sidekiq::ScheduledSet.new
-
- expect(scheduled.size).to eq(2)
- expect(scheduled.map(&:klass)).to include(
- worker.name,
- unrelated_worker.name
- )
-
- model.sidekiq_remove_jobs(job_klass: worker)
-
- expect(scheduled.size).to eq(1)
- expect(scheduled.map(&:klass)).not_to include(worker.name)
- expect(scheduled.map(&:klass)).to include(unrelated_worker.name)
- end
- end
-
- context 'when job instances are in the retry set' do
- include_context 'when handling retried jobs'
-
- it 'removes all related job instances from the retry set' do
- retry_in(worker, 1.hour)
- retry_in(worker, 2.hours)
- retry_in(worker, 3.hours)
- retry_in(unrelated_worker, 4.hours)
-
- retries = Sidekiq::RetrySet.new
-
- expect(retries.size).to eq(4)
- expect(retries.map(&:klass)).to include(
- worker.name,
- unrelated_worker.name
- )
-
- model.sidekiq_remove_jobs(job_klass: worker)
-
- expect(retries.size).to eq(1)
- expect(retries.map(&:klass)).not_to include(worker.name)
- expect(retries.map(&:klass)).to include(unrelated_worker.name)
- end
- end
- end
-
- describe '#sidekiq_queue_length' do
- context 'when queue is empty' do
- it 'returns zero' do
- Sidekiq::Testing.disable! do
- expect(model.sidekiq_queue_length('test')).to eq 0
- end
- end
- end
-
- context 'when queue contains jobs' do
- it 'returns correct size of the queue' do
- Sidekiq::Testing.disable! do
- worker.perform_async('Something', [1])
- worker.perform_async('Something', [2])
-
- expect(model.sidekiq_queue_length('test')).to eq 2
- end
- end
- end
- end
-
- describe '#sidekiq_queue_migrate' do
- it 'migrates jobs from one sidekiq queue to another' do
- Sidekiq::Testing.disable! do
- worker.perform_async('Something', [1])
- worker.perform_async('Something', [2])
-
- expect(model.sidekiq_queue_length('test')).to eq 2
- expect(model.sidekiq_queue_length('new_test')).to eq 0
-
- model.sidekiq_queue_migrate('test', to: 'new_test')
-
- expect(model.sidekiq_queue_length('test')).to eq 0
- expect(model.sidekiq_queue_length('new_test')).to eq 2
- end
- end
- end
- end
-
describe '#check_trigger_permissions!' do
it 'does nothing when the user has the correct permissions' do
expect { model.check_trigger_permissions!('users') }
@@ -2790,18 +2614,18 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
model.backfill_iids('issues')
- issue = issue_class.create!(project_id: project.id)
+ issue = issue_class.create!(project_id: project.id, namespace_id: project.project_namespace_id)
expect(issue.iid).to eq(1)
end
it 'generates iids properly for models created after the migration when iids are backfilled' do
project = setup
- issue_a = issues.create!(project_id: project.id, work_item_type_id: issue_type.id)
+ issue_a = issues.create!(project_id: project.id, namespace_id: project.project_namespace_id, work_item_type_id: issue_type.id)
model.backfill_iids('issues')
- issue_b = issue_class.create!(project_id: project.id)
+ issue_b = issue_class.create!(project_id: project.id, namespace_id: project.project_namespace_id)
expect(issue_a.reload.iid).to eq(1)
expect(issue_b.iid).to eq(2)
@@ -2810,14 +2634,14 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
it 'generates iids properly for models created after the migration across multiple projects' do
project_a = setup
project_b = setup
- issues.create!(project_id: project_a.id, work_item_type_id: issue_type.id)
- issues.create!(project_id: project_b.id, work_item_type_id: issue_type.id)
- issues.create!(project_id: project_b.id, work_item_type_id: issue_type.id)
+ issues.create!(project_id: project_a.id, namespace_id: project_a.project_namespace_id, work_item_type_id: issue_type.id)
+ issues.create!(project_id: project_b.id, namespace_id: project_b.project_namespace_id, work_item_type_id: issue_type.id)
+ issues.create!(project_id: project_b.id, namespace_id: project_b.project_namespace_id, work_item_type_id: issue_type.id)
model.backfill_iids('issues')
- issue_a = issue_class.create!(project_id: project_a.id, work_item_type_id: issue_type.id)
- issue_b = issue_class.create!(project_id: project_b.id, work_item_type_id: issue_type.id)
+ issue_a = issue_class.create!(project_id: project_a.id, namespace_id: project_a.project_namespace_id, work_item_type_id: issue_type.id)
+ issue_b = issue_class.create!(project_id: project_b.id, namespace_id: project_b.project_namespace_id, work_item_type_id: issue_type.id)
expect(issue_a.iid).to eq(2)
expect(issue_b.iid).to eq(3)
@@ -2827,11 +2651,11 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
it 'generates an iid' do
project_a = setup
project_b = setup
- issue_a = issues.create!(project_id: project_a.id, work_item_type_id: issue_type.id)
+ issue_a = issues.create!(project_id: project_a.id, namespace_id: project_a.project_namespace_id, work_item_type_id: issue_type.id)
model.backfill_iids('issues')
- issue_b = issue_class.create!(project_id: project_b.id)
+ issue_b = issue_class.create!(project_id: project_b.id, namespace_id: project_b.project_namespace_id)
expect(issue_a.reload.iid).to eq(1)
expect(issue_b.reload.iid).to eq(1)
@@ -2841,8 +2665,8 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
context 'when a row already has an iid set in the database' do
it 'backfills iids' do
project = setup
- issue_a = issues.create!(project_id: project.id, work_item_type_id: issue_type.id, iid: 1)
- issue_b = issues.create!(project_id: project.id, work_item_type_id: issue_type.id, iid: 2)
+ issue_a = issues.create!(project_id: project.id, namespace_id: project.project_namespace_id, work_item_type_id: issue_type.id, iid: 1)
+ issue_b = issues.create!(project_id: project.id, namespace_id: project.project_namespace_id, work_item_type_id: issue_type.id, iid: 2)
model.backfill_iids('issues')
@@ -2853,9 +2677,9 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
it 'backfills for multiple projects' do
project_a = setup
project_b = setup
- issue_a = issues.create!(project_id: project_a.id, work_item_type_id: issue_type.id, iid: 1)
- issue_b = issues.create!(project_id: project_b.id, work_item_type_id: issue_type.id, iid: 1)
- issue_c = issues.create!(project_id: project_a.id, work_item_type_id: issue_type.id, iid: 2)
+ issue_a = issues.create!(project_id: project_a.id, namespace_id: project_a.project_namespace_id, work_item_type_id: issue_type.id, iid: 1)
+ issue_b = issues.create!(project_id: project_b.id, namespace_id: project_b.project_namespace_id, work_item_type_id: issue_type.id, iid: 1)
+ issue_c = issues.create!(project_id: project_a.id, namespace_id: project_a.project_namespace_id, work_item_type_id: issue_type.id, iid: 2)
model.backfill_iids('issues')
diff --git a/spec/lib/gitlab/database/migrations/batched_migration_last_id_spec.rb b/spec/lib/gitlab/database/migrations/batched_migration_last_id_spec.rb
new file mode 100644
index 00000000000..97b432406eb
--- /dev/null
+++ b/spec/lib/gitlab/database/migrations/batched_migration_last_id_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::Migrations::BatchedMigrationLastId, feature_category: :pipeline_insights do
+ subject(:test_sampling) { described_class.new(connection, base_dir) }
+
+ let(:base_dir) { Pathname.new(Dir.mktmpdir) }
+ let(:file_name) { 'last-batched-background-migration-id.txt' }
+ let(:file_path) { base_dir.join(file_name) }
+ let(:file_contents) { nil }
+
+ where(:base_model) do
+ [
+ [ApplicationRecord], [Ci::ApplicationRecord]
+ ]
+ end
+
+ with_them do
+ let(:connection) { base_model.connection }
+
+ after do
+ FileUtils.rm_rf(file_path)
+ end
+
+ describe '#read' do
+ before do
+ File.write(file_path, file_contents)
+ end
+
+ context 'when the file exists and have content' do
+ let(:file_contents) { 99 }
+
+ it { expect(test_sampling.read).to eq(file_contents) }
+ end
+
+ context 'when the file exists and is blank' do
+ it { expect(test_sampling.read).to be_nil }
+ end
+
+ context "when the file doesn't exists" do
+ before do
+ FileUtils.rm_rf(file_path)
+ end
+
+ it { expect(test_sampling.read).to be_nil }
+ end
+ end
+
+ describe '#store' do
+ let(:file_contents) { File.read(file_path) }
+ let(:migration) do
+ Gitlab::Database::SharedModel.using_connection(connection) do
+ create(:batched_background_migration)
+ end
+ end
+
+ it 'creates the file properly' do
+ test_sampling.store
+
+ expect(File).to exist(file_path)
+ end
+
+ it 'stores the proper id in the file' do
+ migration
+ test_sampling.store
+
+ expect(file_contents).to eq(migration.id.to_s)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/migrations/runner_spec.rb b/spec/lib/gitlab/database/migrations/runner_spec.rb
index bd382547689..66eb5a5de51 100644
--- a/spec/lib/gitlab/database/migrations/runner_spec.rb
+++ b/spec/lib/gitlab/database/migrations/runner_spec.rb
@@ -230,5 +230,13 @@ RSpec.describe Gitlab::Database::Migrations::Runner, :reestablished_active_recor
end
end
end
+
+ describe '.batched_migrations_last_id' do
+ let(:runner_class) { Gitlab::Database::Migrations::BatchedMigrationLastId }
+
+ it 'matches the expected runner class' do
+ expect(described_class.batched_migrations_last_id(database)).to be_a(runner_class)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/database/migrations/sidekiq_helpers_spec.rb b/spec/lib/gitlab/database/migrations/sidekiq_helpers_spec.rb
new file mode 100644
index 00000000000..fb1cb46171f
--- /dev/null
+++ b/spec/lib/gitlab/database/migrations/sidekiq_helpers_spec.rb
@@ -0,0 +1,276 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe Gitlab::Database::Migrations::SidekiqHelpers do
+ let(:model) do
+ ActiveRecord::Migration.new.extend(described_class)
+ end
+
+ describe "sidekiq migration helpers", :redis do
+ let(:worker) do
+ Class.new do
+ include Sidekiq::Worker
+
+ sidekiq_options queue: "test"
+
+ def self.name
+ "WorkerClass"
+ end
+ end
+ end
+
+ let(:worker_two) do
+ Class.new do
+ include Sidekiq::Worker
+
+ sidekiq_options queue: "test_two"
+
+ def self.name
+ "WorkerTwoClass"
+ end
+ end
+ end
+
+ let(:same_queue_different_worker) do
+ Class.new do
+ include Sidekiq::Worker
+
+ sidekiq_options queue: "test"
+
+ def self.name
+ "SameQueueDifferentWorkerClass"
+ end
+ end
+ end
+
+ let(:unrelated_worker) do
+ Class.new do
+ include Sidekiq::Worker
+
+ sidekiq_options queue: "unrelated"
+
+ def self.name
+ "UnrelatedWorkerClass"
+ end
+ end
+ end
+
+ before do
+ stub_const(worker.name, worker)
+ stub_const(worker_two.name, worker_two)
+ stub_const(unrelated_worker.name, unrelated_worker)
+ stub_const(same_queue_different_worker.name, same_queue_different_worker)
+ end
+
+ describe "#sidekiq_remove_jobs", :clean_gitlab_redis_queues do
+ def clear_queues
+ Sidekiq::Queue.new("test").clear
+ Sidekiq::Queue.new("test_two").clear
+ Sidekiq::Queue.new("unrelated").clear
+ Sidekiq::RetrySet.new.clear
+ Sidekiq::ScheduledSet.new.clear
+ end
+
+ around do |example|
+ clear_queues
+ Sidekiq::Testing.disable!(&example)
+ clear_queues
+ end
+
+ context "when the constant is not defined" do
+ it "doesn't try to delete it" do
+ my_non_constant = +"SomeThingThatIsNotAConstant"
+
+ expect(Sidekiq::Queue).not_to receive(:new).with(any_args)
+ model.sidekiq_remove_jobs(job_klasses: [my_non_constant])
+ end
+ end
+
+ context "when the constant is defined" do
+ it "will use it find job instances to delete" do
+ my_constant = worker.name
+ expect(Sidekiq::Queue)
+ .to receive(:new)
+ .with(worker.queue)
+ .and_call_original
+ model.sidekiq_remove_jobs(job_klasses: [my_constant])
+ end
+ end
+
+ it "removes all related job instances from the job classes' queues" do
+ worker.perform_async
+ worker_two.perform_async
+ same_queue_different_worker.perform_async
+ unrelated_worker.perform_async
+
+ worker_queue = Sidekiq::Queue.new(worker.queue)
+ worker_two_queue = Sidekiq::Queue.new(worker_two.queue)
+ unrelated_queue = Sidekiq::Queue.new(unrelated_worker.queue)
+
+ expect(worker_queue.size).to eq(2)
+ expect(worker_two_queue.size).to eq(1)
+ expect(unrelated_queue.size).to eq(1)
+
+ model.sidekiq_remove_jobs(job_klasses: [worker.name, worker_two.name])
+
+ expect(worker_queue.size).to eq(1)
+ expect(worker_two_queue.size).to eq(0)
+ expect(worker_queue.map(&:klass)).not_to include(worker.name)
+ expect(worker_queue.map(&:klass)).to include(
+ same_queue_different_worker.name
+ )
+ expect(worker_two_queue.map(&:klass)).not_to include(worker_two.name)
+ expect(unrelated_queue.size).to eq(1)
+ end
+
+ context "when job instances are in the scheduled set" do
+ it "removes all related job instances from the scheduled set" do
+ worker.perform_in(1.hour)
+ worker_two.perform_in(1.hour)
+ unrelated_worker.perform_in(1.hour)
+
+ scheduled = Sidekiq::ScheduledSet.new
+
+ expect(scheduled.size).to eq(3)
+ expect(scheduled.map(&:klass)).to include(
+ worker.name,
+ worker_two.name,
+ unrelated_worker.name
+ )
+
+ model.sidekiq_remove_jobs(job_klasses: [worker.name, worker_two.name])
+
+ expect(scheduled.size).to eq(1)
+ expect(scheduled.map(&:klass)).not_to include(worker.name)
+ expect(scheduled.map(&:klass)).not_to include(worker_two.name)
+ expect(scheduled.map(&:klass)).to include(unrelated_worker.name)
+ end
+ end
+
+ context "when job instances are in the retry set" do
+ include_context "when handling retried jobs"
+
+ it "removes all related job instances from the retry set" do
+ retry_in(worker, 1.hour)
+ retry_in(worker, 2.hours)
+ retry_in(worker, 3.hours)
+ retry_in(worker_two, 4.hours)
+ retry_in(unrelated_worker, 5.hours)
+
+ retries = Sidekiq::RetrySet.new
+
+ expect(retries.size).to eq(5)
+ expect(retries.map(&:klass)).to include(
+ worker.name,
+ worker_two.name,
+ unrelated_worker.name
+ )
+
+ model.sidekiq_remove_jobs(job_klasses: [worker.name, worker_two.name])
+
+ expect(retries.size).to eq(1)
+ expect(retries.map(&:klass)).not_to include(worker.name)
+ expect(retries.map(&:klass)).not_to include(worker_two.name)
+ expect(retries.map(&:klass)).to include(unrelated_worker.name)
+ end
+ end
+
+ # Imitate job deletion returning zero and then non zero.
+ context "when job fails to be deleted" do
+ let(:job_double) do
+ instance_double(
+ "Sidekiq::JobRecord",
+ klass: worker.name
+ )
+ end
+
+ context "and does not work enough times in a row before max attempts" do
+ it "tries the max attempts without succeeding" do
+ worker.perform_async
+
+ allow(job_double).to receive(:delete).and_return(true)
+
+ # Scheduled set runs last so only need to stub out its values.
+ allow(Sidekiq::ScheduledSet)
+ .to receive(:new)
+ .and_return([job_double])
+
+ expect(model.sidekiq_remove_jobs(job_klasses: [worker.name]))
+ .to eq(
+ {
+ attempts: 5,
+ success: false
+ }
+ )
+ end
+ end
+
+ context "and then it works enough times in a row before max attempts" do
+ it "succeeds" do
+ worker.perform_async
+
+ # attempt 1: false will increment the streak once to 1
+ # attempt 2: true resets it back to 0
+ # attempt 3: false will increment the streak once to 1
+ # attempt 4: false will increment the streak once to 2, loop breaks
+ allow(job_double).to receive(:delete).and_return(false, true, false)
+
+ worker.perform_async
+
+ # Scheduled set runs last so only need to stub out its values.
+ allow(Sidekiq::ScheduledSet)
+ .to receive(:new)
+ .and_return([job_double])
+
+ expect(model.sidekiq_remove_jobs(job_klasses: [worker.name]))
+ .to eq(
+ {
+ attempts: 4,
+ success: true
+ }
+ )
+ end
+ end
+ end
+ end
+
+ describe "#sidekiq_queue_length" do
+ context "when queue is empty" do
+ it "returns zero" do
+ Sidekiq::Testing.disable! do
+ expect(model.sidekiq_queue_length("test")).to eq 0
+ end
+ end
+ end
+
+ context "when queue contains jobs" do
+ it "returns correct size of the queue" do
+ Sidekiq::Testing.disable! do
+ worker.perform_async("Something", [1])
+ worker.perform_async("Something", [2])
+
+ expect(model.sidekiq_queue_length("test")).to eq 2
+ end
+ end
+ end
+ end
+
+ describe "#sidekiq_queue_migrate" do
+ it "migrates jobs from one sidekiq queue to another" do
+ Sidekiq::Testing.disable! do
+ worker.perform_async("Something", [1])
+ worker.perform_async("Something", [2])
+
+ expect(model.sidekiq_queue_length("test")).to eq 2
+ expect(model.sidekiq_queue_length("new_test")).to eq 0
+
+ model.sidekiq_queue_migrate("test", to: "new_test")
+
+ expect(model.sidekiq_queue_length("test")).to eq 0
+ expect(model.sidekiq_queue_length("new_test")).to eq 2
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb b/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb
index 07226f3d025..73d69d55e5a 100644
--- a/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb
+++ b/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb
@@ -55,6 +55,8 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez
let(:table_name) { "_test_column_copying" }
+ let(:from_id) { 0 }
+
before do
connection.execute(<<~SQL)
CREATE TABLE #{table_name} (
@@ -76,7 +78,8 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez
end
subject(:sample_migration) do
- described_class.new(result_dir: result_dir, connection: connection).run_jobs(for_duration: 1.minute)
+ described_class.new(result_dir: result_dir, connection: connection,
+ from_id: from_id).run_jobs(for_duration: 1.minute)
end
it 'runs sampled jobs from the batched background migration' do
@@ -111,20 +114,26 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez
job_interval: 5.minutes,
batch_size: 100)
- described_class.new(result_dir: result_dir, connection: connection).run_jobs(for_duration: 3.minutes)
+ described_class.new(result_dir: result_dir, connection: connection,
+ from_id: from_id).run_jobs(for_duration: 3.minutes)
expect(calls).not_to be_empty
end
context 'with multiple jobs to run' do
- it 'runs all jobs created within the last 3 hours' do
+ let(:last_id) do
+ Gitlab::Database::SharedModel.using_connection(connection) do
+ Gitlab::Database::BackgroundMigration::BatchedMigration.maximum(:id)
+ end
+ end
+
+ it 'runs all pending jobs based on the last migration id' do
old_migration = define_background_migration(migration_name)
queue_migration(migration_name, table_name, :id,
job_interval: 5.minutes,
batch_size: 100)
- travel 4.hours
-
+ last_id
new_migration = define_background_migration('NewMigration') { travel 1.second }
queue_migration('NewMigration', table_name, :id,
job_interval: 5.minutes,
@@ -138,14 +147,15 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez
sub_batch_size: 5)
expect_migration_runs(new_migration => 3, other_new_migration => 2, old_migration => 0) do
- described_class.new(result_dir: result_dir, connection: connection).run_jobs(for_duration: 5.seconds)
+ described_class.new(result_dir: result_dir, connection: connection,
+ from_id: last_id).run_jobs(for_duration: 5.seconds)
end
end
end
end
context 'choosing uniform batches to run' do
- subject { described_class.new(result_dir: result_dir, connection: connection) }
+ subject { described_class.new(result_dir: result_dir, connection: connection, from_id: from_id) }
describe '#uniform_fractions' do
it 'generates evenly distributed sequences of fractions' do
diff --git a/spec/lib/gitlab/database/partitioning/detached_partition_dropper_spec.rb b/spec/lib/gitlab/database/partitioning/detached_partition_dropper_spec.rb
index 336dec3a8a0..646ae50fb44 100644
--- a/spec/lib/gitlab/database/partitioning/detached_partition_dropper_spec.rb
+++ b/spec/lib/gitlab/database/partitioning/detached_partition_dropper_spec.rb
@@ -41,7 +41,7 @@ RSpec.describe Gitlab::Database::Partitioning::DetachedPartitionDropper do
SQL
end
- def create_partition(name:, table: 'parent_table', from:, to:, attached:, drop_after:)
+ def create_partition(name:, from:, to:, attached:, drop_after:, table: 'parent_table')
from = from.beginning_of_month
to = to.beginning_of_month
full_name = "#{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}.#{name}"
diff --git a/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb b/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb
index 550f254c4da..e6014f81b74 100644
--- a/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb
+++ b/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb
@@ -229,11 +229,9 @@ RSpec.describe Gitlab::Database::Partitioning::SlidingListStrategy do
next_partition_if: method(:next_partition_if_wrapper),
detach_partition_if: method(:detach_partition_if_wrapper)
- def self.next_partition?(current_partition)
- end
+ def self.next_partition?(current_partition); end
- def self.detach_partition?(partition)
- end
+ def self.detach_partition?(partition); end
end
end
diff --git a/spec/lib/gitlab/database/postgresql_adapter/dump_schema_versions_mixin_spec.rb b/spec/lib/gitlab/database/postgresql_adapter/dump_schema_versions_mixin_spec.rb
index 8b06f068503..884c4f625dd 100644
--- a/spec/lib/gitlab/database/postgresql_adapter/dump_schema_versions_mixin_spec.rb
+++ b/spec/lib/gitlab/database/postgresql_adapter/dump_schema_versions_mixin_spec.rb
@@ -9,8 +9,7 @@ RSpec.describe Gitlab::Database::PostgresqlAdapter::DumpSchemaVersionsMixin do
original_dump_schema_information
end
- def original_dump_schema_information
- end
+ def original_dump_schema_information; end
end
klass.prepend(described_class)
diff --git a/spec/lib/gitlab/database/postgresql_database_tasks/load_schema_versions_mixin_spec.rb b/spec/lib/gitlab/database/postgresql_database_tasks/load_schema_versions_mixin_spec.rb
index 3e675a85999..3bb206c6627 100644
--- a/spec/lib/gitlab/database/postgresql_database_tasks/load_schema_versions_mixin_spec.rb
+++ b/spec/lib/gitlab/database/postgresql_database_tasks/load_schema_versions_mixin_spec.rb
@@ -9,8 +9,7 @@ RSpec.describe Gitlab::Database::PostgresqlDatabaseTasks::LoadSchemaVersionsMixi
original_structure_load
end
- def original_structure_load
- end
+ def original_structure_load; end
end
klass.prepend(described_class)
diff --git a/spec/lib/gitlab/database/query_analyzers/query_recorder_spec.rb b/spec/lib/gitlab/database/query_analyzers/query_recorder_spec.rb
index ec01ae623ae..bcc39c0c3db 100644
--- a/spec/lib/gitlab/database/query_analyzers/query_recorder_spec.rb
+++ b/spec/lib/gitlab/database/query_analyzers/query_recorder_spec.rb
@@ -10,29 +10,76 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::QueryRecorder, query_analyzers:
end
end
- context 'when analyzer is enabled for tests' do
+ context 'with query analyzer' do
let(:query) { 'SELECT 1 FROM projects' }
- let(:log_path) { Rails.root.join(described_class::LOG_FILE) }
+ let(:log_path) { Rails.root.join(described_class::LOG_PATH) }
+ let(:log_file) { described_class.log_file }
- before do
- stub_env('CI', 'true')
+ after do
+ ::Gitlab::Database::QueryAnalyzer.instance.end!([described_class])
+ end
- # This is needed to be able to stub_env the CI variable
- ::Gitlab::Database::QueryAnalyzer.instance.begin!([described_class])
+ shared_examples_for 'an enabled query recorder' do
+ it 'logs queries to a file' do
+ allow(FileUtils).to receive(:mkdir_p)
+ .with(log_path)
+ expect(File).to receive(:write)
+ .with(log_file, /^{"sql":"#{query}/, mode: 'a')
+ expect(described_class).to receive(:analyze).with(/^#{query}/).and_call_original
+
+ expect { ApplicationRecord.connection.execute(query) }.not_to raise_error
+ end
end
- after do
- ::Gitlab::Database::QueryAnalyzer.instance.end!([described_class])
+ context 'on default branch' do
+ before do
+ stub_env('CI_MERGE_REQUEST_LABELS', nil)
+ stub_env('CI_DEFAULT_BRANCH', 'default_branch_name')
+ stub_env('CI_COMMIT_REF_NAME', 'default_branch_name')
+
+ # This is needed to be able to stub_env the CI variable
+ ::Gitlab::Database::QueryAnalyzer.instance.begin!([described_class])
+ end
+
+ it_behaves_like 'an enabled query recorder'
+ end
+
+ context 'on database merge requests' do
+ before do
+ stub_env('CI_MERGE_REQUEST_LABELS', 'database')
+
+ # This is needed to be able to stub_env the CI variable
+ ::Gitlab::Database::QueryAnalyzer.instance.begin!([described_class])
+ end
+
+ it_behaves_like 'an enabled query recorder'
+ end
+ end
+
+ describe '.log_file' do
+ let(:folder) { 'query_recorder' }
+ let(:extension) { 'ndjson' }
+ let(:default_name) { 'rspec' }
+ let(:job_name) { 'test-job-1' }
+
+ subject { described_class.log_file.to_s }
+
+ context 'when in CI' do
+ before do
+ stub_env('CI_JOB_NAME_SLUG', job_name)
+ end
+
+ it { is_expected.to include("#{folder}/#{job_name}.#{extension}") }
+ it { is_expected.not_to include("#{folder}/#{default_name}.#{extension}") }
end
- it 'logs queries to a file' do
- allow(FileUtils).to receive(:mkdir_p)
- .with(File.dirname(log_path))
- expect(File).to receive(:write)
- .with(log_path, /^{"sql":"#{query}/, mode: 'a')
- expect(described_class).to receive(:analyze).with(/^#{query}/).and_call_original
+ context 'when not in CI' do
+ before do
+ stub_env('CI_JOB_NAME_SLUG', nil)
+ end
- expect { ApplicationRecord.connection.execute(query) }.not_to raise_error
+ it { is_expected.to include("#{folder}/#{default_name}.#{extension}") }
+ it { is_expected.not_to include("#{folder}/#{job_name}.#{extension}") }
end
end
end
diff --git a/spec/lib/gitlab/database/reindexing_spec.rb b/spec/lib/gitlab/database/reindexing_spec.rb
index 4c98185e780..fa26aa59329 100644
--- a/spec/lib/gitlab/database/reindexing_spec.rb
+++ b/spec/lib/gitlab/database/reindexing_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Database::Reindexing do
+RSpec.describe Gitlab::Database::Reindexing, feature_category: :database do
include ExclusiveLeaseHelpers
include Database::DatabaseHelpers
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
index ac2de43b7c6..c507bce634e 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
@@ -97,39 +97,48 @@ RSpec.describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespa
let(:namespace) { create(:group, name: 'hello-group') }
it 'moves a project for a namespace' do
- create(:project, :repository, :legacy_storage, namespace: namespace, path: 'hello-project')
- expected_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- File.join(TestEnv.repos_path, 'bye-group', 'hello-project.git')
- end
+ project = create(:project, :repository, :legacy_storage, namespace: namespace, path: 'hello-project')
+ expected_repository = Gitlab::Git::Repository.new(
+ project.repository_storage,
+ 'bye-group/hello-project.git',
+ nil,
+ nil
+ )
subject.move_repositories(namespace, 'hello-group', 'bye-group')
- expect(File.directory?(expected_path)).to be(true)
+ expect(expected_repository).to exist
end
it 'moves a namespace in a subdirectory correctly' do
child_namespace = create(:group, name: 'sub-group', parent: namespace)
- create(:project, :repository, :legacy_storage, namespace: child_namespace, path: 'hello-project')
+ project = create(:project, :repository, :legacy_storage, namespace: child_namespace, path: 'hello-project')
- expected_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- File.join(TestEnv.repos_path, 'hello-group', 'renamed-sub-group', 'hello-project.git')
- end
+ expected_repository = Gitlab::Git::Repository.new(
+ project.repository_storage,
+ 'hello-group/renamed-sub-group/hello-project.git',
+ nil,
+ nil
+ )
subject.move_repositories(child_namespace, 'hello-group/sub-group', 'hello-group/renamed-sub-group')
- expect(File.directory?(expected_path)).to be(true)
+ expect(expected_repository).to exist
end
it 'moves a parent namespace with subdirectories' do
child_namespace = create(:group, name: 'sub-group', parent: namespace)
- create(:project, :repository, :legacy_storage, namespace: child_namespace, path: 'hello-project')
- expected_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- File.join(TestEnv.repos_path, 'renamed-group', 'sub-group', 'hello-project.git')
- end
+ project = create(:project, :repository, :legacy_storage, namespace: child_namespace, path: 'hello-project')
+ expected_repository = Gitlab::Git::Repository.new(
+ project.repository_storage,
+ 'renamed-group/sub-group/hello-project.git',
+ nil,
+ nil
+ )
subject.move_repositories(child_namespace, 'hello-group', 'renamed-group')
- expect(File.directory?(expected_path)).to be(true)
+ expect(expected_repository).to exist
end
end
@@ -175,14 +184,17 @@ RSpec.describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespa
describe '#rename_namespace_dependencies' do
it "moves the repository for a project in the namespace" do
- create(:project, :repository, :legacy_storage, namespace: namespace, path: "the-path-project")
- expected_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- File.join(TestEnv.repos_path, "the-path0", "the-path-project.git")
- end
+ project = create(:project, :repository, :legacy_storage, namespace: namespace, path: "the-path-project")
+ expected_repository = Gitlab::Git::Repository.new(
+ project.repository_storage,
+ "the-path0/the-path-project.git",
+ nil,
+ nil
+ )
subject.rename_namespace_dependencies(namespace, 'the-path', 'the-path0')
- expect(File.directory?(expected_repo)).to be(true)
+ expect(expected_repository).to exist
end
it "moves the uploads for the namespace" do
@@ -276,9 +288,7 @@ RSpec.describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespa
project.create_repository
subject.rename_namespace(namespace)
- expected_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- File.join(TestEnv.repos_path, 'the-path', 'a-project.git')
- end
+ expected_repository = Gitlab::Git::Repository.new(project.repository_storage, 'the-path/a-project.git', nil, nil)
expect(subject).to receive(:rename_namespace_dependencies)
.with(
@@ -289,7 +299,7 @@ RSpec.describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespa
subject.revert_renames
- expect(File.directory?(expected_path)).to be_truthy
+ expect(expected_repository).to exist
end
it "doesn't break when the namespace was renamed" do
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
index 6292f0246f7..aa2a3329477 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
@@ -126,13 +126,16 @@ RSpec.describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProject
let(:project) { create(:project, :repository, :legacy_storage, path: 'the-path', namespace: known_parent) }
it 'moves the repository for a project' do
- expected_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- File.join(TestEnv.repos_path, 'known-parent', 'new-repo.git')
- end
+ expected_repository = Gitlab::Git::Repository.new(
+ project.repository_storage,
+ 'known-parent/new-repo.git',
+ nil,
+ nil
+ )
subject.move_repository(project, 'known-parent/the-path', 'known-parent/new-repo')
- expect(File.directory?(expected_path)).to be(true)
+ expect(expected_repository).to exist
end
end
@@ -157,9 +160,12 @@ RSpec.describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProject
project.create_repository
subject.rename_project(project)
- expected_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- File.join(TestEnv.repos_path, 'known-parent', 'the-path.git')
- end
+ expected_repository = Gitlab::Git::Repository.new(
+ project.repository_storage,
+ 'known-parent/the-path.git',
+ nil,
+ nil
+ )
expect(subject).to receive(:move_project_folders)
.with(
@@ -170,7 +176,7 @@ RSpec.describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProject
subject.revert_renames
- expect(File.directory?(expected_path)).to be_truthy
+ expect(expected_repository).to exist
end
it "doesn't break when the project was renamed" do
diff --git a/spec/lib/gitlab/database/schema_cleaner_spec.rb b/spec/lib/gitlab/database/schema_cleaner_spec.rb
index 950759c7f96..5283b34ca86 100644
--- a/spec/lib/gitlab/database/schema_cleaner_spec.rb
+++ b/spec/lib/gitlab/database/schema_cleaner_spec.rb
@@ -19,6 +19,15 @@ RSpec.describe Gitlab::Database::SchemaCleaner do
expect(subject).not_to match(/public\.\w+/)
end
+ it 'cleans up all the gitlab_schema_prevent_write table triggers' do
+ expect(subject).not_to match(/CREATE TRIGGER gitlab_schema_write_trigger_for_\w+/)
+ expect(subject).not_to match(/FOR EACH STATEMENT EXECUTE FUNCTION gitlab_schema_prevent_write/)
+ end
+
+ it 'keeps the lock_writes trigger functions' do
+ expect(subject).to match(/CREATE FUNCTION gitlab_schema_prevent_write/)
+ end
+
it 'cleans up the full schema as expected (blackbox test with example)' do
expected_schema = fixture_file(File.join('gitlab', 'database', 'structure_example_cleaned.sql'))
diff --git a/spec/lib/gitlab/database/tables_sorted_by_foreign_keys_spec.rb b/spec/lib/gitlab/database/tables_sorted_by_foreign_keys_spec.rb
index 97abd6d23bd..aa25590ed58 100644
--- a/spec/lib/gitlab/database/tables_sorted_by_foreign_keys_spec.rb
+++ b/spec/lib/gitlab/database/tables_sorted_by_foreign_keys_spec.rb
@@ -4,7 +4,10 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::TablesSortedByForeignKeys do
let(:connection) { ApplicationRecord.connection }
- let(:tables) { %w[_test_gitlab_main_items _test_gitlab_main_references] }
+ let(:tables) do
+ %w[_test_gitlab_main_items _test_gitlab_main_references _test_gitlab_partition_parent
+ gitlab_partitions_dynamic._test_gitlab_partition_20220101]
+ end
subject do
described_class.new(connection, tables).execute
@@ -19,13 +22,33 @@ RSpec.describe Gitlab::Database::TablesSortedByForeignKeys do
item_id BIGINT NOT NULL,
CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_main_items(id)
);
+
+ CREATE TABLE _test_gitlab_partition_parent (
+ id bigserial not null,
+ created_at timestamptz not null,
+ item_id BIGINT NOT NULL,
+ primary key (id, created_at),
+ CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_main_items(id)
+ ) PARTITION BY RANGE(created_at);
+
+ CREATE TABLE gitlab_partitions_dynamic._test_gitlab_partition_20220101
+ PARTITION OF _test_gitlab_partition_parent
+ FOR VALUES FROM ('20220101') TO ('20220131');
+
+ ALTER TABLE _test_gitlab_partition_parent DETACH PARTITION gitlab_partitions_dynamic._test_gitlab_partition_20220101;
SQL
connection.execute(statement)
end
describe '#execute' do
it 'returns the tables sorted by the foreign keys dependency' do
- expect(subject).to eq([['_test_gitlab_main_references'], ['_test_gitlab_main_items']])
+ expect(subject).to eq(
+ [
+ ['_test_gitlab_main_references'],
+ ['_test_gitlab_partition_parent'],
+ ['gitlab_partitions_dynamic._test_gitlab_partition_20220101'],
+ ['_test_gitlab_main_items']
+ ])
end
it 'returns both tables together if they are strongly connected' do
@@ -35,7 +58,12 @@ RSpec.describe Gitlab::Database::TablesSortedByForeignKeys do
SQL
connection.execute(statement)
- expect(subject).to eq([tables])
+ expect(subject).to eq(
+ [
+ ['_test_gitlab_partition_parent'],
+ ['gitlab_partitions_dynamic._test_gitlab_partition_20220101'],
+ %w[_test_gitlab_main_items _test_gitlab_main_references]
+ ])
end
end
end
diff --git a/spec/lib/gitlab/database/tables_truncate_spec.rb b/spec/lib/gitlab/database/tables_truncate_spec.rb
index 4f68cd93a8e..4d04bd67a1e 100644
--- a/spec/lib/gitlab/database/tables_truncate_spec.rb
+++ b/spec/lib/gitlab/database/tables_truncate_spec.rb
@@ -6,14 +6,9 @@ RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_ba
:suppress_gitlab_schemas_validate_connection do
include MigrationsHelpers
- let(:logger) { instance_double(Logger) }
- let(:dry_run) { false }
- let(:until_table) { nil }
let(:min_batch_size) { 1 }
let(:main_connection) { ApplicationRecord.connection }
let(:ci_connection) { Ci::ApplicationRecord.connection }
- let(:test_gitlab_main_table) { '_test_gitlab_main_table' }
- let(:test_gitlab_ci_table) { '_test_gitlab_ci_table' }
# Main Database
let(:main_db_main_item_model) { table("_test_gitlab_main_items", database: "main") }
@@ -21,24 +16,37 @@ RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_ba
let(:main_db_ci_item_model) { table("_test_gitlab_ci_items", database: "main") }
let(:main_db_ci_reference_model) { table("_test_gitlab_ci_references", database: "main") }
let(:main_db_shared_item_model) { table("_test_gitlab_shared_items", database: "main") }
+ let(:main_db_partitioned_item) { table("_test_gitlab_hook_logs", database: "main") }
+ let(:main_db_partitioned_item_detached) do
+ table("gitlab_partitions_dynamic._test_gitlab_hook_logs_20220101", database: "main")
+ end
+
# CI Database
let(:ci_db_main_item_model) { table("_test_gitlab_main_items", database: "ci") }
let(:ci_db_main_reference_model) { table("_test_gitlab_main_references", database: "ci") }
let(:ci_db_ci_item_model) { table("_test_gitlab_ci_items", database: "ci") }
let(:ci_db_ci_reference_model) { table("_test_gitlab_ci_references", database: "ci") }
let(:ci_db_shared_item_model) { table("_test_gitlab_shared_items", database: "ci") }
-
- subject(:truncate_legacy_tables) do
- described_class.new(
- database_name: database_name,
- min_batch_size: min_batch_size,
- logger: logger,
- dry_run: dry_run,
- until_table: until_table
- ).execute
+ let(:ci_db_partitioned_item) { table("_test_gitlab_hook_logs", database: "ci") }
+ let(:ci_db_partitioned_item_detached) do
+ table("gitlab_partitions_dynamic._test_gitlab_hook_logs_20220101", database: "ci")
end
shared_examples 'truncating legacy tables on a database' do
+ let(:logger) { instance_double(Logger) }
+ let(:dry_run) { false }
+ let(:until_table) { nil }
+
+ subject(:truncate_legacy_tables) do
+ described_class.new(
+ database_name: connection.pool.db_config.name,
+ min_batch_size: min_batch_size,
+ logger: logger,
+ dry_run: dry_run,
+ until_table: until_table
+ ).execute
+ end
+
before do
skip_if_multiple_databases_not_setup
@@ -51,6 +59,24 @@ RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_ba
item_id BIGINT NOT NULL,
CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_main_items(id)
);
+
+ CREATE TABLE _test_gitlab_hook_logs (
+ id bigserial not null,
+ created_at timestamptz not null,
+ item_id BIGINT NOT NULL,
+ primary key (id, created_at),
+ CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_main_items(id)
+ ) PARTITION BY RANGE(created_at);
+
+ CREATE TABLE gitlab_partitions_dynamic._test_gitlab_hook_logs_20220101
+ PARTITION OF _test_gitlab_hook_logs
+ FOR VALUES FROM ('20220101') TO ('20220131');
+
+ CREATE TABLE gitlab_partitions_dynamic._test_gitlab_hook_logs_20220201
+ PARTITION OF _test_gitlab_hook_logs
+ FOR VALUES FROM ('20220201') TO ('20220228');
+
+ ALTER TABLE _test_gitlab_hook_logs DETACH PARTITION gitlab_partitions_dynamic._test_gitlab_hook_logs_20220101;
SQL
main_connection.execute(main_tables_sql)
@@ -84,18 +110,49 @@ RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_ba
main_db_ci_item_model.create!(id: i)
main_db_ci_reference_model.create!(item_id: i)
main_db_shared_item_model.create!(id: i)
+ main_db_partitioned_item.create!(item_id: i, created_at: '2022-02-02 02:00')
+ main_db_partitioned_item_detached.create!(item_id: i, created_at: '2022-01-01 01:00')
# CI Database
ci_db_main_item_model.create!(id: i)
ci_db_main_reference_model.create!(item_id: i)
ci_db_ci_item_model.create!(id: i)
ci_db_ci_reference_model.create!(item_id: i)
ci_db_shared_item_model.create!(id: i)
+ ci_db_partitioned_item.create!(item_id: i, created_at: '2022-02-02 02:00')
+ ci_db_partitioned_item_detached.create!(item_id: i, created_at: '2022-01-01 01:00')
+ end
+
+ Gitlab::Database::SharedModel.using_connection(main_connection) do
+ Postgresql::DetachedPartition.create!(
+ table_name: '_test_gitlab_hook_logs_20220101',
+ drop_after: Time.current
+ )
+ end
+
+ Gitlab::Database::SharedModel.using_connection(ci_connection) do
+ Postgresql::DetachedPartition.create!(
+ table_name: '_test_gitlab_hook_logs_20220101',
+ drop_after: Time.current
+ )
end
allow(Gitlab::Database::GitlabSchema).to receive(:tables_to_schema).and_return(
{
"_test_gitlab_main_items" => :gitlab_main,
"_test_gitlab_main_references" => :gitlab_main,
+ "_test_gitlab_hook_logs" => :gitlab_main,
+ "_test_gitlab_ci_items" => :gitlab_ci,
+ "_test_gitlab_ci_references" => :gitlab_ci,
+ "_test_gitlab_shared_items" => :gitlab_shared,
+ "_test_gitlab_geo_items" => :gitlab_geo
+ }
+ )
+
+ allow(Gitlab::Database::GitlabSchema).to receive(:views_and_tables_to_schema).and_return(
+ {
+ "_test_gitlab_main_items" => :gitlab_main,
+ "_test_gitlab_main_references" => :gitlab_main,
+ "_test_gitlab_hook_logs" => :gitlab_main,
"_test_gitlab_ci_items" => :gitlab_ci,
"_test_gitlab_ci_references" => :gitlab_ci,
"_test_gitlab_shared_items" => :gitlab_shared,
@@ -119,7 +176,7 @@ RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_ba
Gitlab::Database::LockWritesManager.new(
table_name: table,
connection: connection,
- database_name: database_name
+ database_name: connection.pool.db_config.name
).lock_writes
end
end
@@ -199,7 +256,6 @@ RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_ba
context 'when truncating gitlab_ci tables on the main database' do
let(:connection) { ApplicationRecord.connection }
- let(:database_name) { "main" }
let(:legacy_tables_models) { [main_db_ci_item_model, main_db_ci_reference_model] }
let(:referencing_table_model) { main_db_ci_reference_model }
let(:referenced_table_model) { main_db_ci_item_model }
@@ -217,8 +273,10 @@ RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_ba
context 'when truncating gitlab_main tables on the ci database' do
let(:connection) { Ci::ApplicationRecord.connection }
- let(:database_name) { "ci" }
- let(:legacy_tables_models) { [ci_db_main_item_model, ci_db_main_reference_model] }
+ let(:legacy_tables_models) do
+ [ci_db_main_item_model, ci_db_main_reference_model, ci_db_partitioned_item, ci_db_partitioned_item_detached]
+ end
+
let(:referencing_table_model) { ci_db_main_reference_model }
let(:referenced_table_model) { ci_db_main_item_model }
let(:other_tables_models) do
diff --git a/spec/lib/gitlab/database/transaction/context_spec.rb b/spec/lib/gitlab/database/transaction/context_spec.rb
index 33a47150060..1681098e20c 100644
--- a/spec/lib/gitlab/database/transaction/context_spec.rb
+++ b/spec/lib/gitlab/database/transaction/context_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Database::Transaction::Context do
+RSpec.describe Gitlab::Database::Transaction::Context, feature_category: :database do
subject { described_class.new }
let(:data) { subject.context }
diff --git a/spec/lib/gitlab/database/type/indifferent_jsonb_spec.rb b/spec/lib/gitlab/database/type/indifferent_jsonb_spec.rb
new file mode 100644
index 00000000000..6d27cbe180d
--- /dev/null
+++ b/spec/lib/gitlab/database/type/indifferent_jsonb_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::Type::IndifferentJsonb do
+ let(:type) { described_class.new }
+
+ describe '#deserialize' do
+ using RSpec::Parameterized::TableSyntax
+
+ subject { type.deserialize(json) }
+
+ where(:json, :value) do
+ nil | nil
+ '{"key":"value"}' | { key: 'value' }
+ '{"key":[1,2,3]}' | { key: [1, 2, 3] }
+ '{"key":{"subkey":"value"}}' | { key: { subkey: 'value' } }
+ '{"key":{"a":[{"b":"c"},{"d":"e"}]}}' | { key: { a: [{ b: 'c' }, { d: 'e' }] } }
+ end
+
+ with_them do
+ it { is_expected.to match(value) }
+ it { is_expected.to match(value&.deep_stringify_keys) }
+ end
+ end
+
+ context 'when used by a model' do
+ let(:model) do
+ Class.new(ApplicationRecord) do
+ self.table_name = :_test_indifferent_jsonb
+
+ attribute :options, :ind_jsonb
+ end
+ end
+
+ let(:record) do
+ model.create!(name: 'test', options: { key: 'value' })
+ end
+
+ before do
+ model.connection.execute(<<~SQL)
+ CREATE TABLE _test_indifferent_jsonb(
+ id serial NOT NULL PRIMARY KEY,
+ name text,
+ options jsonb);
+ SQL
+
+ model.reset_column_information
+ end
+
+ it { expect(record.options).to match({ key: 'value' }) }
+ it { expect(record.options).to match({ 'key' => 'value' }) }
+
+ it 'ignores changes to other attributes' do
+ record.name = 'other test'
+
+ expect(record.changes).to match('name' => ['test', 'other test'])
+ end
+
+ it 'tracks changes to options' do
+ record.options = { key: 'other value' }
+
+ expect(record.changes).to match('options' => [{ 'key' => 'value' }, { 'key' => 'other value' }])
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database_importers/work_items/hierarchy_restrictions_importer_spec.rb b/spec/lib/gitlab/database_importers/work_items/hierarchy_restrictions_importer_spec.rb
new file mode 100644
index 00000000000..d8173794b3f
--- /dev/null
+++ b/spec/lib/gitlab/database_importers/work_items/hierarchy_restrictions_importer_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter,
+ feature_category: :portfolio_management do
+ subject { described_class.upsert_restrictions }
+
+ it_behaves_like 'work item hierarchy restrictions importer'
+end
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index c788022bd3a..1a482b33a92 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -139,7 +139,7 @@ RSpec.describe Gitlab::Database do
describe '.db_config_for_connection' do
context 'when the regular connection is used' do
it 'returns db_config' do
- connection = ActiveRecord::Base.retrieve_connection
+ connection = ApplicationRecord.retrieve_connection
expect(described_class.db_config_for_connection(connection)).to eq(connection.pool.db_config)
end
@@ -147,12 +147,15 @@ RSpec.describe Gitlab::Database do
context 'when the connection is LoadBalancing::ConnectionProxy', :database_replica do
it 'returns primary db config even if ambiguous queries default to replica' do
- Gitlab::Database::LoadBalancing::Session.current.use_primary!
- primary_config = described_class.db_config_for_connection(ActiveRecord::Base.connection)
-
- Gitlab::Database::LoadBalancing::Session.clear_session
- Gitlab::Database::LoadBalancing::Session.current.fallback_to_replicas_for_ambiguous_queries do
- expect(described_class.db_config_for_connection(ActiveRecord::Base.connection)).to eq(primary_config)
+ Gitlab::Database.database_base_models_using_load_balancing.each_value do |database_base_model|
+ connection = database_base_model.connection
+ Gitlab::Database::LoadBalancing::Session.current.use_primary!
+ primary_config = described_class.db_config_for_connection(connection)
+
+ Gitlab::Database::LoadBalancing::Session.clear_session
+ Gitlab::Database::LoadBalancing::Session.current.fallback_to_replicas_for_ambiguous_queries do
+ expect(described_class.db_config_for_connection(connection)).to eq(primary_config)
+ end
end
end
end
@@ -180,11 +183,16 @@ RSpec.describe Gitlab::Database do
end
context 'when replicas are configured', :database_replica do
- it 'returns the name for a replica' do
- replica = ActiveRecord::Base.load_balancer.host
-
+ it 'returns the main_replica for a main database replica' do
+ replica = ApplicationRecord.load_balancer.host
expect(described_class.db_config_name(replica)).to eq('main_replica')
end
+
+ it 'returns the ci_replica for a ci database replica' do
+ skip_if_multiple_databases_not_setup
+ replica = Ci::ApplicationRecord.load_balancer.host
+ expect(described_class.db_config_name(replica)).to eq('ci_replica')
+ end
end
end
@@ -214,13 +222,17 @@ RSpec.describe Gitlab::Database do
expect(described_class.gitlab_schemas_for_connection(Ci::Build.connection)).to include(:gitlab_ci, :gitlab_shared)
end
+ # rubocop:disable Database/MultipleDatabases
it 'does return gitlab_ci when a ActiveRecord::Base is using CI connection' do
with_reestablished_active_record_base do
reconfigure_db_connection(model: ActiveRecord::Base, config_model: Ci::Build)
- expect(described_class.gitlab_schemas_for_connection(ActiveRecord::Base.connection)).to include(:gitlab_ci, :gitlab_shared)
+ expect(
+ described_class.gitlab_schemas_for_connection(ActiveRecord::Base.connection)
+ ).to include(:gitlab_ci, :gitlab_shared)
end
end
+ # rubocop:enable Database/MultipleDatabases
it 'does return a valid schema for a replica connection' do
with_replica_pool_for(ActiveRecord::Base) do |main_replica_pool|
@@ -281,7 +293,8 @@ RSpec.describe Gitlab::Database do
it 'does return empty for non-adopted connections' do
new_connection = ActiveRecord::Base.postgresql_connection(
- ActiveRecord::Base.connection_db_config.configuration_hash)
+ ActiveRecord::Base.connection_db_config.configuration_hash # rubocop:disable Database/MultipleDatabases
+ )
expect(described_class.gitlab_schemas_for_connection(new_connection)).to be_nil
ensure
@@ -405,7 +418,7 @@ RSpec.describe Gitlab::Database do
context 'within a transaction block' do
it 'publishes a transaction event' do
events = subscribe_events do
- ActiveRecord::Base.transaction do
+ ApplicationRecord.transaction do
User.first
end
end
@@ -424,10 +437,11 @@ RSpec.describe Gitlab::Database do
context 'within an empty transaction block' do
it 'publishes a transaction event' do
events = subscribe_events do
- ActiveRecord::Base.transaction {}
+ ApplicationRecord.transaction {}
+ Ci::ApplicationRecord.transaction {}
end
- expect(events.length).to be(1)
+ expect(events.length).to be(2)
event = events.first
expect(event).not_to be_nil
@@ -441,9 +455,9 @@ RSpec.describe Gitlab::Database do
context 'within a nested transaction block' do
it 'publishes multiple transaction events' do
events = subscribe_events do
- ActiveRecord::Base.transaction do
- ActiveRecord::Base.transaction do
- ActiveRecord::Base.transaction do
+ ApplicationRecord.transaction do
+ ApplicationRecord.transaction do
+ ApplicationRecord.transaction do
User.first
end
end
@@ -465,7 +479,7 @@ RSpec.describe Gitlab::Database do
context 'within a cancelled transaction block' do
it 'publishes multiple transaction events' do
events = subscribe_events do
- ActiveRecord::Base.transaction do
+ ApplicationRecord.transaction do
User.first
raise ActiveRecord::Rollback
end
diff --git a/spec/lib/gitlab/diff/file_collection/compare_spec.rb b/spec/lib/gitlab/diff/file_collection/compare_spec.rb
index ce70903a480..c3f768db7f0 100644
--- a/spec/lib/gitlab/diff/file_collection/compare_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection/compare_spec.rb
@@ -16,10 +16,11 @@ RSpec.describe Gitlab::Diff::FileCollection::Compare do
end
let(:diffable) { Compare.new(raw_compare, project) }
+ let(:diff_options) { {} }
let(:collection_default_args) do
{
project: diffable.project,
- diff_options: {},
+ diff_options: diff_options,
diff_refs: diffable.diff_refs
}
end
@@ -65,4 +66,32 @@ RSpec.describe Gitlab::Diff::FileCollection::Compare do
expect(cache_key).to eq ['compare', head_commit.id, start_commit.id]
end
end
+
+ describe 'pagination methods' do
+ subject(:compare) { described_class.new(diffable, **collection_default_args) }
+
+ context 'when pagination options are not present' do
+ it 'returns default values' do
+ expect(compare.limit_value).to eq(Kaminari.config.default_per_page)
+ expect(compare.current_page).to eq(1)
+ expect(compare.next_page).to be_nil
+ expect(compare.prev_page).to be_nil
+ expect(compare.total_count).to be_nil
+ expect(compare.total_pages).to eq(0)
+ end
+ end
+
+ context 'when pagination options are present' do
+ let(:diff_options) { { page: 1, per_page: 10, count: 20 } }
+
+ it 'returns values based on options' do
+ expect(compare.limit_value).to eq(10)
+ expect(compare.current_page).to eq(1)
+ expect(compare.next_page).to eq(2)
+ expect(compare.prev_page).to be_nil
+ expect(compare.total_count).to eq(20)
+ expect(compare.total_pages).to eq(2)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_batch_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_batch_spec.rb
index beb85d383a0..9ac242459bf 100644
--- a/spec/lib/gitlab/diff/file_collection/merge_request_diff_batch_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_batch_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Diff::FileCollection::MergeRequestDiffBatch do
+RSpec.describe Gitlab::Diff::FileCollection::MergeRequestDiffBatch, feature_category: :code_review do
let(:merge_request) { create(:merge_request) }
let(:batch_page) { 0 }
let(:batch_size) { 10 }
diff --git a/spec/lib/gitlab/diff/file_collection/paginated_merge_request_diff_spec.rb b/spec/lib/gitlab/diff/file_collection/paginated_merge_request_diff_spec.rb
new file mode 100644
index 00000000000..74e5e667702
--- /dev/null
+++ b/spec/lib/gitlab/diff/file_collection/paginated_merge_request_diff_spec.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Diff::FileCollection::PaginatedMergeRequestDiff, feature_category: :code_review do
+ let(:merge_request) { create(:merge_request) }
+ let(:page) { 1 }
+ let(:per_page) { 10 }
+ let(:diffable) { merge_request.merge_request_diff }
+ let(:diff_files_relation) { diffable.merge_request_diff_files }
+ let(:diff_files) { subject.diff_files }
+
+ subject do
+ described_class.new(diffable,
+ page,
+ per_page)
+ end
+
+ describe '#diff_files' do
+ let(:per_page) { 3 }
+ let(:paginated_rel) { diff_files_relation.page(page).per(per_page) }
+
+ let(:expected_batch_files) do
+ paginated_rel.map(&:new_path)
+ end
+
+ it 'returns paginated diff files' do
+ expect(diff_files.size).to eq(3)
+ end
+
+ it 'returns a valid instance of a DiffCollection' do
+ expect(diff_files).to be_a(Gitlab::Git::DiffCollection)
+ end
+
+ context 'when first page' do
+ it 'returns correct diff files' do
+ expect(diff_files.map(&:new_path)).to eq(expected_batch_files)
+ end
+ end
+
+ context 'when another page' do
+ let(:page) { 2 }
+
+ it 'returns correct diff files' do
+ expect(diff_files.map(&:new_path)).to eq(expected_batch_files)
+ end
+ end
+
+ context 'when page is nil' do
+ let(:page) { nil }
+
+ it 'returns correct diff files' do
+ expected_batch_files =
+ diff_files_relation.page(described_class::DEFAULT_PAGE).per(per_page).map(&:new_path)
+
+ expect(diff_files.map(&:new_path)).to eq(expected_batch_files)
+ end
+ end
+
+ context 'when per_page is nil' do
+ let(:per_page) { nil }
+
+ it 'returns correct diff files' do
+ expected_batch_files =
+ diff_files_relation.page(page).per(described_class::DEFAULT_PER_PAGE).map(&:new_path)
+
+ expect(diff_files.map(&:new_path)).to eq(expected_batch_files)
+ end
+ end
+
+ context 'when invalid page' do
+ let(:page) { 999 }
+
+ it 'returns correct diff files' do
+ expect(diff_files.map(&:new_path)).to be_empty
+ end
+ end
+
+ context 'when last page' do
+ it 'returns correct diff files' do
+ last_page = diff_files_relation.count - per_page
+ collection = described_class.new(diffable,
+ last_page,
+ per_page)
+
+ expected_batch_files = diff_files_relation.page(last_page).per(per_page).map(&:new_path)
+
+ expect(collection.diff_files.map(&:new_path)).to eq(expected_batch_files)
+ end
+ end
+ end
+
+ it_behaves_like 'unfoldable diff' do
+ subject do
+ described_class.new(merge_request.merge_request_diff,
+ page,
+ per_page)
+ end
+ end
+
+ it_behaves_like 'cacheable diff collection' do
+ let(:cacheable_files_count) { per_page }
+ end
+
+ it_behaves_like 'unsortable diff files' do
+ let(:diffable) { merge_request.merge_request_diff }
+
+ subject do
+ described_class.new(merge_request.merge_request_diff,
+ page,
+ per_page)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb
index 75538baf07f..8ff8de2379a 100644
--- a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Email::Handler::CreateIssueHandler do
- include_context :email_shared_context
+ include_context 'email shared context'
let!(:user) do
create(
:user,
@@ -16,13 +16,13 @@ RSpec.describe Gitlab::Email::Handler::CreateIssueHandler do
let(:namespace) { create(:namespace, path: 'gitlabhq') }
let(:email_raw) { email_fixture('emails/valid_new_issue.eml') }
- it_behaves_like :reply_processing_shared_examples
-
before do
stub_incoming_email_setting(enabled: true, address: "incoming+%{key}@appmail.adventuretime.ooo")
stub_config_setting(host: 'localhost')
end
+ it_behaves_like 'reply processing shared examples'
+
context "when email key" do
let(:mail) { Mail::Message.new(email_raw) }
diff --git a/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb b/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
index 37ee4591db0..f5b44d30c50 100644
--- a/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Email::Handler::CreateMergeRequestHandler do
- include_context :email_shared_context
+ include_context 'email shared context'
let!(:user) do
create(
:user,
@@ -16,16 +16,16 @@ RSpec.describe Gitlab::Email::Handler::CreateMergeRequestHandler do
let(:namespace) { create(:namespace, path: 'gitlabhq') }
let(:email_raw) { email_fixture('emails/valid_new_merge_request.eml') }
- it_behaves_like :reply_processing_shared_examples
+ after do
+ TestEnv.clean_test_path
+ end
before do
stub_incoming_email_setting(enabled: true, address: "incoming+%{key}@appmail.adventuretime.ooo")
stub_config_setting(host: 'localhost')
end
- after do
- TestEnv.clean_test_path
- end
+ it_behaves_like 'reply processing shared examples'
context "when email key" do
let(:mail) { Mail::Message.new(email_raw) }
diff --git a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
index 585dce331ed..f70645a8272 100644
--- a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do
- include_context :email_shared_context
+ include_context 'email shared context'
let_it_be(:user) { create(:user, email: 'jake@adventuretime.ooo') }
let_it_be(:project) { create(:project, :public, :repository) }
@@ -15,9 +15,14 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do
SentNotification.record_note(note, user.id, mail_key)
end
- it_behaves_like :reply_processing_shared_examples
+ before do
+ stub_incoming_email_setting(enabled: true, address: "reply+%{key}@appmail.adventuretime.ooo")
+ stub_config_setting(host: 'localhost')
+ end
+
+ it_behaves_like 'reply processing shared examples'
- it_behaves_like :note_handler_shared_examples do
+ it_behaves_like 'note handler shared examples' do
let(:recipient) { sent_notification.recipient }
let(:update_commands_only) { fixture_file('emails/update_commands_only_reply.eml') }
@@ -26,11 +31,6 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do
let(:with_quick_actions) { fixture_file('emails/valid_reply_with_quick_actions.eml') }
end
- before do
- stub_incoming_email_setting(enabled: true, address: "reply+%{key}@appmail.adventuretime.ooo")
- stub_config_setting(host: 'localhost')
- end
-
context 'when the recipient address does not include a mail key' do
let(:email_raw) { fixture_file('emails/valid_reply.eml').gsub(mail_key, '') }
@@ -92,7 +92,7 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do
issue.update_attribute(:confidential, true)
end
- it_behaves_like :checks_permissions_on_noteable_examples
+ it_behaves_like 'checks permissions on noteable examples'
end
shared_examples 'a reply to existing comment' do
diff --git a/spec/lib/gitlab/email/handler/create_note_on_issuable_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_on_issuable_handler_spec.rb
index d3535fa9bd3..6e83c06c1b4 100644
--- a/spec/lib/gitlab/email/handler/create_note_on_issuable_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_note_on_issuable_handler_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Email::Handler::CreateNoteOnIssuableHandler do
- include_context :email_shared_context
+ include_context 'email shared context'
let_it_be(:user) { create(:user, email: 'jake@adventuretime.ooo', incoming_email_token: 'auth_token') }
let_it_be(:namespace) { create(:namespace, path: 'gitlabhq') }
@@ -17,9 +17,9 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteOnIssuableHandler do
stub_config_setting(host: 'localhost')
end
- it_behaves_like :reply_processing_shared_examples
+ it_behaves_like 'reply processing shared examples'
- it_behaves_like :note_handler_shared_examples, true do
+ it_behaves_like 'note handler shared examples', true do
let_it_be(:recipient) { user }
let(:update_commands_only) { email_reply_fixture('emails/update_commands_only.eml') }
@@ -42,7 +42,7 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteOnIssuableHandler do
noteable.update_attribute(:confidential, true)
end
- it_behaves_like :checks_permissions_on_noteable_examples
+ it_behaves_like 'checks permissions on noteable examples'
end
def email_fixture(path)
diff --git a/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb b/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb
index 08a7383700b..7bba0775668 100644
--- a/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler do
- include_context :email_shared_context
+ include ServiceDeskHelper
+ include_context 'email shared context'
before do
stub_incoming_email_setting(enabled: true, address: "incoming+%{key}@appmail.adventuretime.ooo")
@@ -184,12 +185,6 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler do
context 'and template is present' do
let_it_be(:settings) { create(:service_desk_setting, project: project) }
- def set_template_file(file_name, content)
- file_path = ".gitlab/issue_templates/#{file_name}.md"
- project.repository.create_file(user, file_path, content, message: 'message', branch_name: 'master')
- settings.update!(issue_template_key: file_name)
- end
-
it 'appends template text to issue description' do
set_template_file('service_desk', 'text from template')
diff --git a/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb b/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb
index 2bc3cd81b48..f33e9eba5c6 100644
--- a/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Email::Handler::UnsubscribeHandler do
- include_context :email_shared_context
+ include_context 'email shared context'
before do
stub_incoming_email_setting(enabled: true, address: 'reply+%{key}@appmail.adventuretime.ooo')
diff --git a/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb b/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb
index 47f6015c6f8..b22c55208f0 100644
--- a/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb
+++ b/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb
@@ -7,15 +7,15 @@ RSpec.describe Gitlab::Email::Hook::DisableEmailInterceptor do
Mail.register_interceptor(described_class)
end
+ after do
+ Mail.unregister_interceptor(described_class)
+ end
+
it 'does not send emails' do
allow(Gitlab.config.gitlab).to receive(:email_enabled).and_return(false)
expect { deliver_mail }.not_to change(ActionMailer::Base.deliveries, :count)
end
- after do
- Mail.unregister_interceptor(described_class)
- end
-
def deliver_mail
key = create :personal_key
Notify.new_ssh_key_email(key.id)
diff --git a/spec/lib/gitlab/email/receiver_spec.rb b/spec/lib/gitlab/email/receiver_spec.rb
index 9240d07fd59..865e40d4ecb 100644
--- a/spec/lib/gitlab/email/receiver_spec.rb
+++ b/spec/lib/gitlab/email/receiver_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Email::Receiver do
- include_context :email_shared_context
+ include_context 'email shared context'
let_it_be(:project) { create(:project) }
let(:metric_transaction) { instance_double(Gitlab::Metrics::WebTransaction) }
diff --git a/spec/lib/gitlab/file_type_detection_spec.rb b/spec/lib/gitlab/file_type_detection_spec.rb
index c435d3f6097..1be0f7d53fa 100644
--- a/spec/lib/gitlab/file_type_detection_spec.rb
+++ b/spec/lib/gitlab/file_type_detection_spec.rb
@@ -31,6 +31,7 @@ RSpec.describe Gitlab::FileTypeDetection do
expect(described_class.extension_match?('my/file.foo', extensions)).to eq(true)
end
end
+
context 'when class is an uploader' do
let(:uploader) do
example_uploader = Class.new(CarrierWave::Uploader::Base) do
diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
index b1bff242f33..e1c0da69317 100644
--- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
@@ -31,7 +31,7 @@ RSpec.describe Gitlab::Gfm::UploadsRewriter do
referenced_files.compact.select(&:exists?)
end
- shared_examples "files are accessible" do
+ shared_examples 'files are accessible' do
describe '#rewrite' do
subject(:rewrite) { new_text }
@@ -82,6 +82,18 @@ RSpec.describe Gitlab::Gfm::UploadsRewriter do
rewrite
expect(new_files).to be_empty
+ expect(new_text).to eq(text)
+ end
+
+ it 'skips non-existant files' do
+ allow_next_instance_of(FileUploader) do |file|
+ allow(file).to receive(:exists?).and_return(false)
+ end
+
+ rewrite
+
+ expect(new_files).to be_empty
+ expect(new_text).to eq(text)
end
end
end
@@ -107,11 +119,11 @@ RSpec.describe Gitlab::Gfm::UploadsRewriter do
end
end
- context "file are stored locally" do
- include_examples "files are accessible"
+ context 'file are stored locally' do
+ include_examples 'files are accessible'
end
- context "files are stored remotely" do
+ context 'files are stored remotely' do
before do
stub_uploads_object_storage(FileUploader)
@@ -120,7 +132,7 @@ RSpec.describe Gitlab::Gfm::UploadsRewriter do
end
end
- include_examples "files are accessible"
+ include_examples 'files are accessible'
end
describe '#needs_rewrite?' do
diff --git a/spec/lib/gitlab/git/base_error_spec.rb b/spec/lib/gitlab/git/base_error_spec.rb
index 851cfa16512..d4db7cf2430 100644
--- a/spec/lib/gitlab/git/base_error_spec.rb
+++ b/spec/lib/gitlab/git/base_error_spec.rb
@@ -20,4 +20,15 @@ RSpec.describe Gitlab::Git::BaseError do
with_them do
it { is_expected.to eq(result) }
end
+
+ describe "When initialized with GRPC errors" do
+ let(:grpc_error) { GRPC::DeadlineExceeded.new }
+ let(:git_error) { described_class.new grpc_error }
+
+ it "has status and code fields" do
+ expect(git_error.service).to eq('git')
+ expect(git_error.status).to eq(4)
+ expect(git_error.code).to eq('deadline_exceeded')
+ end
+ end
end
diff --git a/spec/lib/gitlab/git/cross_repo_comparer_spec.rb b/spec/lib/gitlab/git/cross_repo_comparer_spec.rb
deleted file mode 100644
index 7888e224d59..00000000000
--- a/spec/lib/gitlab/git/cross_repo_comparer_spec.rb
+++ /dev/null
@@ -1,117 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Git::CrossRepoComparer do
- let(:source_project) { create(:project, :repository) }
- let(:target_project) { create(:project, :repository) }
-
- let(:source_repo) { source_project.repository.raw_repository }
- let(:target_repo) { target_project.repository.raw_repository }
-
- let(:source_branch) { 'feature' }
- let(:target_branch) { 'master' }
- let(:straight) { false }
-
- let(:source_commit) { source_repo.commit(source_branch) }
- let(:target_commit) { source_repo.commit(target_branch) }
-
- subject(:result) { described_class.new(source_repo, target_repo).compare(source_branch, target_branch, straight: straight) }
-
- describe '#compare' do
- context 'within a single repository' do
- let(:target_project) { source_project }
-
- context 'a non-straight comparison' do
- it 'compares without fetching from another repo' do
- expect(source_repo).not_to receive(:fetch_source_branch!)
-
- expect_compare(result, from: source_commit, to: target_commit)
- expect(result.straight).to eq(false)
- end
- end
-
- context 'a straight comparison' do
- let(:straight) { true }
-
- it 'compares without fetching from another repo' do
- expect(source_repo).not_to receive(:fetch_source_branch!)
-
- expect_compare(result, from: source_commit, to: target_commit)
- expect(result.straight).to eq(true)
- end
- end
- end
-
- context 'across two repositories' do
- context 'target ref exists in source repo' do
- it 'compares without fetching from another repo' do
- expect(source_repo).not_to receive(:fetch_source_branch!)
- expect(source_repo).not_to receive(:delete_refs)
-
- expect_compare(result, from: source_commit, to: target_commit)
- end
- end
-
- context 'target ref does not exist in source repo' do
- it 'compares in the source repo by fetching from the target to a temporary ref' do
- new_commit_id = create_commit(target_project.owner, target_repo, target_branch)
- new_commit = target_repo.commit(new_commit_id)
-
- # This is how the temporary ref is generated
- expect(SecureRandom).to receive(:hex).at_least(:once).and_return('foo')
-
- expect(source_repo)
- .to receive(:fetch_source_branch!)
- .with(target_repo, new_commit_id, 'refs/tmp/foo')
- .and_call_original
-
- expect(source_repo).to receive(:delete_refs).with('refs/tmp/foo').and_call_original
-
- expect_compare(result, from: source_commit, to: new_commit)
- end
- end
-
- context 'source ref does not exist in source repo' do
- let(:source_branch) { 'does-not-exist' }
-
- it 'returns an empty comparison' do
- expect(source_repo).not_to receive(:fetch_source_branch!)
- expect(source_repo).not_to receive(:delete_refs)
-
- expect(result).to be_a(::Gitlab::Git::Compare)
- expect(result.commits.size).to eq(0)
- end
- end
-
- context 'target ref does not exist in target repo' do
- let(:target_branch) { 'does-not-exist' }
-
- it 'returns nil' do
- expect(source_repo).not_to receive(:fetch_source_branch!)
- expect(source_repo).not_to receive(:delete_refs)
-
- is_expected.to be_nil
- end
- end
- end
- end
-
- def expect_compare(of, from:, to:)
- expect(of).to be_a(::Gitlab::Git::Compare)
- expect(from).to be_a(::Gitlab::Git::Commit)
- expect(to).to be_a(::Gitlab::Git::Commit)
-
- expect(of.commits).not_to be_empty
- expect(of.head).to eq(from)
- expect(of.base).to eq(to)
- end
-
- def create_commit(user, repo, branch)
- action = { action: :create, file_path: '/FILE', content: 'content' }
-
- result = repo.commit_files(user, branch_name: branch, message: 'Commit', actions: [action])
-
- result.newrev
- end
-end
diff --git a/spec/lib/gitlab/git/cross_repo_spec.rb b/spec/lib/gitlab/git/cross_repo_spec.rb
new file mode 100644
index 00000000000..09a28c144a4
--- /dev/null
+++ b/spec/lib/gitlab/git/cross_repo_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Git::CrossRepo do
+ let_it_be(:source_project) { create(:project, :repository) }
+ let_it_be(:target_project) { create(:project, :repository) }
+
+ let(:source_repo) { source_project.repository.raw_repository }
+ let(:target_repo) { target_project.repository.raw_repository }
+
+ let(:source_branch) { 'feature' }
+ let(:target_branch) { target_repo.root_ref }
+
+ let(:source_commit) { source_repo.commit(source_branch) }
+ let(:target_commit) { source_repo.commit(target_branch) }
+
+ def execute(&block)
+ described_class.new(source_repo, target_repo).execute(target_branch, &block)
+ end
+
+ describe '#execute' do
+ context 'when executed within a single repository' do
+ let(:target_project) { source_project }
+
+ it 'does not fetch from another repo' do
+ expect(source_repo).not_to receive(:fetch_source_branch!)
+
+ expect { |block| execute(&block) }.to yield_with_args(target_branch)
+ end
+ end
+
+ context 'when executed across two repositories' do
+ context 'and target ref exists in source repo' do
+ it 'does not fetch from another repo' do
+ expect(source_repo).not_to receive(:fetch_source_branch!)
+ expect(source_repo).not_to receive(:delete_refs)
+
+ expect { |block| execute(&block) }.to yield_with_args(target_commit.id)
+ end
+ end
+
+ context 'and target ref does not exist in source repo' do
+ let_it_be(:target_project) { create(:project, :repository) }
+
+ it 'fetches from the target to a temporary ref' do
+ new_commit_id = create_commit(target_project.owner, target_repo, target_branch)
+
+ # This is how the temporary ref is generated
+ expect(SecureRandom).to receive(:hex).at_least(:once).and_return('foo')
+
+ expect(source_repo)
+ .to receive(:fetch_source_branch!)
+ .with(target_repo, new_commit_id, 'refs/tmp/foo')
+ .and_call_original
+
+ expect(source_repo).to receive(:delete_refs).with('refs/tmp/foo').and_call_original
+
+ expect { |block| execute(&block) }.to yield_with_args(new_commit_id)
+ end
+ end
+
+ context 'and target ref does not exist in target repo' do
+ let(:target_branch) { 'does-not-exist' }
+
+ it 'returns nil' do
+ expect(source_repo).not_to receive(:fetch_source_branch!)
+ expect(source_repo).not_to receive(:delete_refs)
+
+ expect { |block| execute(&block) }.not_to yield_control
+ end
+ end
+ end
+ end
+
+ def create_commit(user, repo, branch)
+ action = { action: :create, file_path: '/FILE', content: 'content' }
+
+ result = repo.commit_files(user, branch_name: branch, message: 'Commit', actions: [action])
+
+ result.newrev
+ end
+end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 197662943a0..6cff39c1167 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe Gitlab::Git::Repository do
+RSpec.describe Gitlab::Git::Repository, feature_category: :source_code_management do
include Gitlab::EncodingHelper
include RepoHelpers
using RSpec::Parameterized::TableSyntax
@@ -70,12 +70,7 @@ RSpec.describe Gitlab::Git::Repository do
it { is_expected.to include("master") }
it { is_expected.not_to include("branch-from-space") }
- it 'gets the branch names from GitalyClient' do
- expect_any_instance_of(Gitlab::GitalyClient::RefService).to receive(:branch_names)
- subject
- end
-
- it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :branch_names
+ it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :list_refs
end
describe '#tag_names' do
@@ -100,7 +95,7 @@ RSpec.describe Gitlab::Git::Repository do
it { is_expected.to include("v1.0.0") }
it { is_expected.not_to include("v5.0.0") }
- it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :tag_names
+ it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :list_refs
end
describe '#tags' do
@@ -1353,7 +1348,7 @@ RSpec.describe Gitlab::Git::Repository do
it "returns the number of commits in the whole repository" do
options = { all: true }
- expect(repository.count_commits(options)).to eq(314)
+ expect(repository.count_commits(options)).to eq(315)
end
end
@@ -1378,6 +1373,24 @@ RSpec.describe Gitlab::Git::Repository do
expect(branch).to eq(nil)
end
+
+ context 'when branch is ambiguous' do
+ let(:ambiguous_branch) { 'prefix' }
+ let(:branch_with_prefix) { 'prefix/branch' }
+
+ before do
+ repository.create_branch(branch_with_prefix)
+ end
+
+ after do
+ repository.delete_branch(branch_with_prefix)
+ end
+
+ it 'returns nil for ambiguous branch' do
+ expect(repository.find_branch(branch_with_prefix)).to be_a_kind_of(Gitlab::Git::Branch)
+ expect(repository.find_branch(ambiguous_branch)).to eq(nil)
+ end
+ end
end
describe '#branches' do
@@ -1416,16 +1429,6 @@ RSpec.describe Gitlab::Git::Repository do
it 'returns the count of local branches' do
expect(repository.branch_count).to eq(repository.local_branches.count)
end
-
- context 'with Gitaly disabled' do
- before do
- allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false)
- end
-
- it 'returns the count of local branches' do
- expect(repository.branch_count).to eq(repository.local_branches.count)
- end
- end
end
end
@@ -2212,15 +2215,49 @@ RSpec.describe Gitlab::Git::Repository do
end
describe '#compare_source_branch' do
- it 'delegates to Gitlab::Git::CrossRepoComparer' do
- expect_next_instance_of(::Gitlab::Git::CrossRepoComparer) do |instance|
- expect(instance.source_repo).to eq(:source_repository)
- expect(instance.target_repo).to eq(repository)
+ it 'compares two branches cross repo' do
+ mutable_repository.commit_files(
+ user,
+ branch_name: mutable_repository.root_ref, message: 'Committing something',
+ actions: [{ action: :create, file_path: 'encoding/CHANGELOG', content: 'New file' }]
+ )
+
+ repository.commit_files(
+ user,
+ branch_name: repository.root_ref, message: 'Commit to root ref',
+ actions: [{ action: :create, file_path: 'encoding/CHANGELOG', content: 'One more' }]
+ )
- expect(instance).to receive(:compare).with('feature', 'master', straight: :straight)
+ [
+ [repository, mutable_repository, true],
+ [repository, mutable_repository, false],
+ [mutable_repository, repository, true],
+ [mutable_repository, repository, false]
+ ].each do |source_repo, target_repo, straight|
+ raw_compare = target_repo.compare_source_branch(
+ target_repo.root_ref, source_repo, source_repo.root_ref, straight: straight)
+
+ expect(raw_compare).to be_a(::Gitlab::Git::Compare)
+
+ expect(raw_compare.commits).to eq([source_repo.commit])
+ expect(raw_compare.head).to eq(source_repo.commit)
+ expect(raw_compare.base).to eq(target_repo.commit)
+ expect(raw_compare.straight).to eq(straight)
end
+ end
+
+ context 'source ref does not exist in source repo' do
+ it 'returns an empty comparison' do
+ expect_next_instance_of(::Gitlab::Git::CrossRepo) do |instance|
+ expect(instance).not_to receive(:fetch_source_branch!)
+ end
+
+ raw_compare = repository.compare_source_branch(
+ repository.root_ref, mutable_repository, 'does-not-exist', straight: true)
- repository.compare_source_branch('master', :source_repository, 'feature', straight: :straight)
+ expect(raw_compare).to be_a(::Gitlab::Git::Compare)
+ expect(raw_compare.commits.size).to eq(0)
+ end
end
end
@@ -2517,4 +2554,30 @@ RSpec.describe Gitlab::Git::Repository do
end
end
end
+
+ describe '#check_objects_exist' do
+ it 'returns hash specifying which object exists in repo' do
+ refs_exist = %w(
+ b83d6e391c22777fca1ed3012fce84f633d7fed0
+ 498214de67004b1da3d820901307bed2a68a8ef6
+ 1b12f15a11fc6e62177bef08f47bc7b5ce50b141
+ )
+ refs_dont_exist = %w(
+ 1111111111111111111111111111111111111111
+ 2222222222222222222222222222222222222222
+ )
+ object_existence_map = repository.check_objects_exist(refs_exist + refs_dont_exist)
+ expect(object_existence_map).to eq({
+ 'b83d6e391c22777fca1ed3012fce84f633d7fed0' => true,
+ '498214de67004b1da3d820901307bed2a68a8ef6' => true,
+ '1b12f15a11fc6e62177bef08f47bc7b5ce50b141' => true,
+ '1111111111111111111111111111111111111111' => false,
+ '2222222222222222222222222222222222222222' => false
+ })
+ expect(object_existence_map.keys).to eq(refs_exist + refs_dont_exist)
+
+ single_sha = 'b83d6e391c22777fca1ed3012fce84f633d7fed0'
+ expect(repository.check_objects_exist(single_sha)).to eq({ single_sha => true })
+ end
+ end
end
diff --git a/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb
index 524b373a5b7..1b8da0b380b 100644
--- a/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb
+++ b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require 'json'
require 'tempfile'
-RSpec.describe Gitlab::Git::RuggedImpl::UseRugged do
+RSpec.describe Gitlab::Git::RuggedImpl::UseRugged, feature_category: :gitlay do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:feature_flag_name) { wrapper.rugged_feature_keys.first }
@@ -18,8 +18,7 @@ RSpec.describe Gitlab::Git::RuggedImpl::UseRugged do
klazz = Class.new do
include Gitlab::Git::RuggedImpl::UseRugged
- def rugged_test(ref, test_number)
- end
+ def rugged_test(ref, test_number); end
end
klazz.new
diff --git a/spec/lib/gitlab/git/tree_spec.rb b/spec/lib/gitlab/git/tree_spec.rb
index 17f802b9f66..2a68fa66b18 100644
--- a/spec/lib/gitlab/git/tree_spec.rb
+++ b/spec/lib/gitlab/git/tree_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe Gitlab::Git::Tree do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository.raw }
- shared_examples :repo do
+ shared_examples 'repo' do
subject(:tree) { Gitlab::Git::Tree.where(repository, sha, path, recursive, skip_flat_paths, pagination_params) }
let(:sha) { SeedRepo::Commit::ID }
@@ -151,7 +151,7 @@ RSpec.describe Gitlab::Git::Tree do
end
describe '.where with Gitaly enabled' do
- it_behaves_like :repo do
+ it_behaves_like 'repo' do
context 'with pagination parameters' do
let(:pagination_params) { { limit: 3, page_token: nil } }
@@ -172,7 +172,7 @@ RSpec.describe Gitlab::Git::Tree do
described_class.where(repository, SeedRepo::Commit::ID, 'files', false, false)
end
- it_behaves_like :repo do
+ it_behaves_like 'repo' do
describe 'Pagination' do
context 'with restrictive limit' do
let(:pagination_params) { { limit: 3, page_token: nil } }
diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
index 604feeea325..82d5d0f292b 100644
--- a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
@@ -898,7 +898,7 @@ RSpec.describe Gitlab::GitalyClient::OperationService do
end
shared_examples '#user_commit_files failure' do
- it 'raises a PreReceiveError' do
+ it 'raises an IndexError' do
expect_any_instance_of(Gitaly::OperationService::Stub)
.to receive(:user_commit_files).with(kind_of(Enumerator), kind_of(Hash))
.and_raise(structured_error)
@@ -912,7 +912,7 @@ RSpec.describe Gitlab::GitalyClient::OperationService do
context 'with missing file' do
let(:status_code) { GRPC::Core::StatusCodes::NOT_FOUND }
- let(:expected_message) { "File not found: README.md" }
+ let(:expected_message) { "A file with this name doesn't exist" }
let(:expected_error) do
Gitaly::UserCommitFilesError.new(
index_update: Gitaly::IndexError.new(
@@ -926,7 +926,7 @@ RSpec.describe Gitlab::GitalyClient::OperationService do
context 'with existing directory' do
let(:status_code) { GRPC::Core::StatusCodes::ALREADY_EXISTS }
- let(:expected_message) { "Directory already exists: dir1" }
+ let(:expected_message) { "A directory with this name already exists" }
let(:expected_error) do
Gitaly::UserCommitFilesError.new(
index_update: Gitaly::IndexError.new(
@@ -940,7 +940,7 @@ RSpec.describe Gitlab::GitalyClient::OperationService do
context 'with existing file' do
let(:status_code) { GRPC::Core::StatusCodes::ALREADY_EXISTS }
- let(:expected_message) { "File already exists: README.md" }
+ let(:expected_message) { "A file with this name already exists" }
let(:expected_error) do
Gitaly::UserCommitFilesError.new(
index_update: Gitaly::IndexError.new(
@@ -954,7 +954,7 @@ RSpec.describe Gitlab::GitalyClient::OperationService do
context 'with invalid path' do
let(:status_code) { GRPC::Core::StatusCodes::INVALID_ARGUMENT }
- let(:expected_message) { "Invalid path: invalid://file/name" }
+ let(:expected_message) { "invalid path: 'invalid://file/name'" }
let(:expected_error) do
Gitaly::UserCommitFilesError.new(
index_update: Gitaly::IndexError.new(
@@ -968,7 +968,7 @@ RSpec.describe Gitlab::GitalyClient::OperationService do
context 'with directory traversal' do
let(:status_code) { GRPC::Core::StatusCodes::INVALID_ARGUMENT }
- let(:expected_message) { "Directory traversal in path escapes repository: ../../../../etc/shadow" }
+ let(:expected_message) { "Path cannot include directory traversal" }
let(:expected_error) do
Gitaly::UserCommitFilesError.new(
index_update: Gitaly::IndexError.new(
@@ -982,7 +982,7 @@ RSpec.describe Gitlab::GitalyClient::OperationService do
context 'with empty path' do
let(:status_code) { GRPC::Core::StatusCodes::INVALID_ARGUMENT }
- let(:expected_message) { "Received empty path" }
+ let(:expected_message) { "You must provide a file path" }
let(:expected_error) do
Gitaly::UserCommitFilesError.new(
index_update: Gitaly::IndexError.new(
@@ -1009,16 +1009,33 @@ RSpec.describe Gitlab::GitalyClient::OperationService do
end
context 'with an exception without the detailed error' do
- let(:permission_error) do
- GRPC::PermissionDenied.new
- end
-
- it 'raises PermissionDenied' do
+ before do
expect_any_instance_of(Gitaly::OperationService::Stub)
.to receive(:user_commit_files).with(kind_of(Enumerator), kind_of(Hash))
- .and_raise(permission_error)
+ .and_raise(raised_error)
+ end
- expect { subject }.to raise_error(GRPC::PermissionDenied)
+ context 'with an index error from libgit2' do
+ let(:raised_error) do
+ GRPC::Internal.new('invalid path: .git/foo')
+ end
+
+ it 'raises IndexError' do
+ expect { subject }.to raise_error do |error|
+ expect(error).to be_a(Gitlab::Git::Index::IndexError)
+ expect(error.message).to eq('invalid path: .git/foo')
+ end
+ end
+ end
+
+ context 'with a generic error' do
+ let(:raised_error) do
+ GRPC::PermissionDenied.new
+ end
+
+ it 'raises PermissionDenied' do
+ expect { subject }.to raise_error(GRPC::PermissionDenied)
+ end
end
end
end
diff --git a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
index bd96e9baf1d..ae2e343377d 100644
--- a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
@@ -71,28 +71,6 @@ RSpec.describe Gitlab::GitalyClient::RefService do
end
end
- describe '#branch_names' do
- it 'sends a find_all_branch_names message' do
- expect_any_instance_of(Gitaly::RefService::Stub)
- .to receive(:find_all_branch_names)
- .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
- .and_return([])
-
- client.branch_names
- end
- end
-
- describe '#tag_names' do
- it 'sends a find_all_tag_names message' do
- expect_any_instance_of(Gitaly::RefService::Stub)
- .to receive(:find_all_tag_names)
- .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
- .and_return([])
-
- client.tag_names
- end
- end
-
describe '#find_branch' do
it 'sends a find_branch message' do
expect_any_instance_of(Gitaly::RefService::Stub)
@@ -102,6 +80,16 @@ RSpec.describe Gitlab::GitalyClient::RefService do
client.find_branch('name')
end
+
+ context 'when Gitaly returns a ambiguios reference error' do
+ it 'raises an UnknownRef error' do
+ expect_any_instance_of(Gitaly::RefService::Stub)
+ .to receive(:find_branch)
+ .and_raise(GRPC::BadStatus.new(2, 'reference is ambiguous'))
+
+ expect { client.find_branch('name') }.to raise_error(Gitlab::Git::AmbiguousRef, 'branch is ambiguous: name')
+ end
+ end
end
describe '#find_tag' do
diff --git a/spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb b/spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb
index 41dce5d76dd..61945cc06b8 100644
--- a/spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb
@@ -157,70 +157,47 @@ RSpec.describe Gitlab::GitalyClient::WithFeatureFlagActors do
let(:call_arg_2) { double }
let(:call_arg_3) { double }
let(:call_result) { double }
+ let(:repository_actor) { instance_double(::Repository) }
+ let(:user_actor) { instance_double(::User) }
+ let(:project_actor) { instance_double(Project) }
+ let(:group_actor) { instance_double(Group) }
before do
+ allow(service).to receive(:user_actor).and_return(user_actor)
+ allow(service).to receive(:repository_actor).and_return(repository_actor)
+ allow(service).to receive(:project_actor).and_return(project_actor)
+ allow(service).to receive(:group_actor).and_return(group_actor)
+ allow(Gitlab::GitalyClient).to receive(:with_feature_flag_actors).and_call_original
allow(Gitlab::GitalyClient).to receive(:call).and_return(call_result)
end
- context 'when actors_aware_gitaly_calls flag is enabled' do
- let(:repository_actor) { instance_double(::Repository) }
- let(:user_actor) { instance_double(::User) }
- let(:project_actor) { instance_double(Project) }
- let(:group_actor) { instance_double(Group) }
-
- before do
- stub_feature_flags(actors_aware_gitaly_calls: true)
-
- allow(service).to receive(:user_actor).and_return(user_actor)
- allow(service).to receive(:repository_actor).and_return(repository_actor)
- allow(service).to receive(:project_actor).and_return(project_actor)
- allow(service).to receive(:group_actor).and_return(group_actor)
- allow(Gitlab::GitalyClient).to receive(:with_feature_flag_actors).and_call_original
- end
-
- it 'triggers client call with feature flag actors' do
- result = service.gitaly_client_call(call_arg_1, call_arg_2, karg: call_arg_3)
-
- expect(Gitlab::GitalyClient).to have_received(:call).with(call_arg_1, call_arg_2, karg: call_arg_3)
- expect(Gitlab::GitalyClient).to have_received(:with_feature_flag_actors).with(
- repository: repository_actor,
- user: user_actor,
- project: project_actor,
- group: group_actor
- )
- expect(result).to be(call_result)
- end
-
- context 'when call without repository_actor' do
- before do
- allow(service).to receive(:repository_actor).and_return(nil)
- allow(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception).and_call_original
- end
-
- it 'calls error tracking track_and_raise_for_dev_exception' do
- expect do
- service.gitaly_client_call(call_arg_1, call_arg_2, karg: call_arg_3)
- end.to raise_error /gitaly_client_call called without setting repository_actor/
-
- expect(Gitlab::ErrorTracking).to have_received(:track_and_raise_for_dev_exception).with(
- be_a(Feature::InvalidFeatureFlagError)
- )
- end
- end
+ it 'triggers client call with feature flag actors' do
+ result = service.gitaly_client_call(call_arg_1, call_arg_2, karg: call_arg_3)
+
+ expect(Gitlab::GitalyClient).to have_received(:call).with(call_arg_1, call_arg_2, karg: call_arg_3)
+ expect(Gitlab::GitalyClient).to have_received(:with_feature_flag_actors).with(
+ repository: repository_actor,
+ user: user_actor,
+ project: project_actor,
+ group: group_actor
+ )
+ expect(result).to be(call_result)
end
- context 'when actors_aware_gitaly_calls not enabled' do
+ context 'when call without repository_actor' do
before do
- stub_feature_flags(actors_aware_gitaly_calls: false)
+ allow(service).to receive(:repository_actor).and_return(nil)
+ allow(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception).and_call_original
end
- it 'triggers client call without feature flag actors' do
- expect(Gitlab::GitalyClient).not_to receive(:with_feature_flag_actors)
+ it 'calls error tracking track_and_raise_for_dev_exception' do
+ expect do
+ service.gitaly_client_call(call_arg_1, call_arg_2, karg: call_arg_3)
+ end.to raise_error /gitaly_client_call called without setting repository_actor/
- result = service.gitaly_client_call(call_arg_1, call_arg_2, karg: call_arg_3)
-
- expect(Gitlab::GitalyClient).to have_received(:call).with(call_arg_1, call_arg_2, karg: call_arg_3)
- expect(result).to be(call_result)
+ expect(Gitlab::ErrorTracking).to have_received(:track_and_raise_for_dev_exception).with(
+ be_a(Feature::InvalidFeatureFlagError)
+ )
end
end
@@ -228,47 +205,28 @@ RSpec.describe Gitlab::GitalyClient::WithFeatureFlagActors do
let_it_be(:project) { create(:project) }
let(:repository_actor) { project.repository }
- context 'when actors_aware_gitaly_calls flag is enabled' do
- let(:user_actor) { instance_double(::User) }
- let(:project_actor) { instance_double(Project) }
- let(:group_actor) { instance_double(Group) }
-
- before do
- stub_feature_flags(actors_aware_gitaly_calls: true)
-
- allow(Feature::Gitaly).to receive(:user_actor).and_return(user_actor)
- allow(Feature::Gitaly).to receive(:project_actor).with(project).and_return(project_actor)
- allow(Feature::Gitaly).to receive(:group_actor).with(project).and_return(group_actor)
- end
-
- it 'returns a hash with collected feature flag actors' do
- result = service.gitaly_feature_flag_actors(repository_actor)
- expect(result).to eql(
- repository: repository_actor,
- user: user_actor,
- project: project_actor,
- group: group_actor
- )
-
- expect(Feature::Gitaly).to have_received(:user_actor).with(no_args)
- expect(Feature::Gitaly).to have_received(:project_actor).with(project)
- expect(Feature::Gitaly).to have_received(:group_actor).with(project)
- end
- end
+ let(:user_actor) { instance_double(::User) }
+ let(:project_actor) { instance_double(Project) }
+ let(:group_actor) { instance_double(Group) }
- context 'when actors_aware_gitaly_calls not enabled' do
- before do
- stub_feature_flags(actors_aware_gitaly_calls: false)
- end
+ before do
+ allow(Feature::Gitaly).to receive(:user_actor).and_return(user_actor)
+ allow(Feature::Gitaly).to receive(:project_actor).with(project).and_return(project_actor)
+ allow(Feature::Gitaly).to receive(:group_actor).with(project).and_return(group_actor)
+ end
- it 'returns an empty hash' do
- expect(Feature::Gitaly).not_to receive(:user_actor)
- expect(Feature::Gitaly).not_to receive(:project_actor)
- expect(Feature::Gitaly).not_to receive(:group_actor)
+ it 'returns a hash with collected feature flag actors' do
+ result = service.gitaly_feature_flag_actors(repository_actor)
+ expect(result).to eql(
+ repository: repository_actor,
+ user: user_actor,
+ project: project_actor,
+ group: group_actor
+ )
- result = service.gitaly_feature_flag_actors(repository_actor)
- expect(result).to eql({})
- end
+ expect(Feature::Gitaly).to have_received(:user_actor).with(no_args)
+ expect(Feature::Gitaly).to have_received(:project_actor).with(project)
+ expect(Feature::Gitaly).to have_received(:group_actor).with(project)
end
end
end
diff --git a/spec/lib/gitlab/github_gists_import/importer/gist_importer_spec.rb b/spec/lib/gitlab/github_gists_import/importer/gist_importer_spec.rb
new file mode 100644
index 00000000000..69a4d646562
--- /dev/null
+++ b/spec/lib/gitlab/github_gists_import/importer/gist_importer_spec.rb
@@ -0,0 +1,128 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubGistsImport::Importer::GistImporter, feature_category: :importer do
+ subject { described_class.new(gist_object, user.id).execute }
+
+ let_it_be(:user) { create(:user) }
+ let(:created_at) { Time.utc(2022, 1, 9, 12, 15) }
+ let(:updated_at) { Time.utc(2022, 5, 9, 12, 17) }
+ let(:gist_file) { { file_name: '_Summary.md', file_content: 'File content' } }
+ let(:url) { 'https://host.com/gistid.git' }
+ let(:gist_object) do
+ instance_double('Gitlab::GithubGistsImport::Representation::Gist',
+ truncated_title: 'My Gist',
+ visibility_level: 0,
+ files: { '_Summary.md': gist_file },
+ first_file: gist_file,
+ git_pull_url: url,
+ created_at: created_at,
+ updated_at: updated_at
+ )
+ end
+
+ let(:expected_snippet_attrs) do
+ {
+ title: 'My Gist',
+ visibility_level: 0,
+ content: 'File content',
+ file_name: '_Summary.md',
+ author_id: user.id,
+ created_at: gist_object.created_at,
+ updated_at: gist_object.updated_at
+ }.stringify_keys
+ end
+
+ describe '#execute' do
+ context 'when success' do
+ it 'creates expected snippet and snippet repository' do
+ expect_next_instance_of(Repository) do |repository|
+ expect(repository).to receive(:fetch_as_mirror)
+ end
+
+ expect { subject }.to change { user.snippets.count }.by(1)
+ expect(user.snippets[0].attributes).to include expected_snippet_attrs
+ end
+ end
+
+ context 'when file size limit exeeded' do
+ before do
+ files = [].tap { |array| 11.times { |n| array << ["file#{n}.txt", {}] } }.to_h
+
+ allow(gist_object).to receive(:files).and_return(files)
+ allow_next_instance_of(Repository) do |repository|
+ allow(repository).to receive(:fetch_as_mirror)
+ allow(repository).to receive(:empty?).and_return(false)
+ allow(repository).to receive(:ls_files).and_return(files.keys)
+ end
+ end
+
+ it 'returns error' do
+ result = subject
+
+ expect(user.snippets.count).to eq(0)
+ expect(result.error?).to eq(true)
+ expect(result.errors).to match_array(['Snippet max file count exceeded'])
+ end
+ end
+
+ context 'when invalid attributes' do
+ let(:gist_file) { { file_name: '_Summary.md', file_content: nil } }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Content can't be blank")
+ end
+ end
+
+ context 'when repository cloning fails' do
+ it 'returns error' do
+ expect_next_instance_of(Repository) do |repository|
+ expect(repository).to receive(:fetch_as_mirror).and_raise(Gitlab::Shell::Error)
+ expect(repository).to receive(:remove)
+ end
+
+ expect { subject }.to raise_error(Gitlab::Shell::Error)
+ expect(user.snippets.count).to eq(0)
+ end
+ end
+
+ context 'when url is invalid' do
+ let(:url) { 'invalid' }
+
+ context 'when local network is allowed' do
+ before do
+ allow(::Gitlab::CurrentSettings)
+ .to receive(:allow_local_requests_from_web_hooks_and_services?).and_return(true)
+ end
+
+ it 'raises error' do
+ expect(Gitlab::UrlBlocker)
+ .to receive(:validate!)
+ .with(url, ports: [80, 443], schemes: %w[http https git],
+ allow_localhost: true, allow_local_network: true)
+ .and_raise(Gitlab::UrlBlocker::BlockedUrlError)
+
+ expect { subject }.to raise_error(Gitlab::UrlBlocker::BlockedUrlError)
+ end
+ end
+
+ context 'when local network is not allowed' do
+ before do
+ allow(::Gitlab::CurrentSettings)
+ .to receive(:allow_local_requests_from_web_hooks_and_services?).and_return(false)
+ end
+
+ it 'raises error' do
+ expect(Gitlab::UrlBlocker)
+ .to receive(:validate!)
+ .with(url, ports: [80, 443], schemes: %w[http https git],
+ allow_localhost: false, allow_local_network: false)
+ .and_raise(Gitlab::UrlBlocker::BlockedUrlError)
+
+ expect { subject }.to raise_error(Gitlab::UrlBlocker::BlockedUrlError)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_gists_import/importer/gists_importer_spec.rb b/spec/lib/gitlab/github_gists_import/importer/gists_importer_spec.rb
new file mode 100644
index 00000000000..704999a99a9
--- /dev/null
+++ b/spec/lib/gitlab/github_gists_import/importer/gists_importer_spec.rb
@@ -0,0 +1,121 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubGistsImport::Importer::GistsImporter, feature_category: :importer do
+ subject(:result) { described_class.new(user, token).execute }
+
+ let_it_be(:user) { create(:user) }
+ let(:client) { instance_double('Gitlab::GithubImport::Client', rate_limit_resets_in: 5) }
+ let(:token) { 'token' }
+ let(:page_counter) { instance_double('Gitlab::GithubImport::PageCounter', current: 1, set: true, expire!: true) }
+ let(:page) { instance_double('Gitlab::GithubImport::Client::Page', objects: [gist], number: 1) }
+ let(:url) { 'https://gist.github.com/foo/bar.git' }
+ let(:waiter) { Gitlab::JobWaiter.new(0, 'some-job-key') }
+
+ let(:gist) do
+ {
+ id: '055b70',
+ git_pull_url: url,
+ files: {
+ 'random.txt': {
+ filename: 'random.txt',
+ type: 'text/plain',
+ language: 'Text',
+ raw_url: 'https://gist.githubusercontent.com/user_name/055b70/raw/66a7be0d/random.txt',
+ size: 166903
+ }
+ },
+ public: false,
+ created_at: '2022-09-06T11:38:18Z',
+ updated_at: '2022-09-06T11:38:18Z',
+ description: 'random text'
+ }
+ end
+
+ let(:gist_hash) do
+ {
+ id: '055b70',
+ import_url: url,
+ files: {
+ 'random.txt': {
+ filename: 'random.txt',
+ type: 'text/plain',
+ language: 'Text',
+ raw_url: 'https://gist.githubusercontent.com/user_name/055b70/raw/66a7be0d/random.txt',
+ size: 166903
+ }
+ },
+ public: false,
+ created_at: '2022-09-06T11:38:18Z',
+ updated_at: '2022-09-06T11:38:18Z',
+ title: 'random text'
+ }
+ end
+
+ let(:gist_represent) { instance_double('Gitlab::GithubGistsImport::Representation::Gist', to_hash: gist_hash) }
+
+ describe '#execute' do
+ before do
+ allow(Gitlab::GithubImport::Client)
+ .to receive(:new)
+ .with(token, parallel: true)
+ .and_return(client)
+
+ allow(Gitlab::GithubImport::PageCounter)
+ .to receive(:new)
+ .with(user, :gists, 'github-gists-importer')
+ .and_return(page_counter)
+
+ allow(client)
+ .to receive(:each_page)
+ .with(:gists, nil, { page: 1 })
+ .and_yield(page)
+
+ allow(Gitlab::GithubGistsImport::Representation::Gist)
+ .to receive(:from_api_response)
+ .with(gist)
+ .and_return(gist_represent)
+
+ allow(Gitlab::JobWaiter)
+ .to receive(:new)
+ .and_return(waiter)
+ end
+
+ context 'when success' do
+ it 'spread parallel import' do
+ expect(Gitlab::GithubGistsImport::ImportGistWorker)
+ .to receive(:bulk_perform_in)
+ .with(
+ 1.second,
+ [[user.id, gist_hash, waiter.key]],
+ batch_delay: 1.minute,
+ batch_size: 1000
+ )
+
+ expect(result.waiter).to be_an_instance_of(Gitlab::JobWaiter)
+ expect(result.waiter.jobs_remaining).to eq(1)
+ end
+ end
+
+ context 'when failure' do
+ it 'returns an error' do
+ expect(Gitlab::GithubGistsImport::ImportGistWorker)
+ .to receive(:bulk_perform_in)
+ .and_raise(StandardError, 'Error Message')
+
+ expect(result.error).to be_an_instance_of(StandardError)
+ end
+ end
+
+ context 'when rate limit reached' do
+ it 'returns an error' do
+ expect(Gitlab::GithubGistsImport::ImportGistWorker)
+ .to receive(:bulk_perform_in)
+ .and_raise(Gitlab::GithubImport::RateLimitError)
+
+ expect(result.error).to be_an_instance_of(Gitlab::GithubImport::RateLimitError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_gists_import/representation/gist_spec.rb b/spec/lib/gitlab/github_gists_import/representation/gist_spec.rb
new file mode 100644
index 00000000000..480aefb2c74
--- /dev/null
+++ b/spec/lib/gitlab/github_gists_import/representation/gist_spec.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubGistsImport::Representation::Gist, feature_category: :importer do
+ shared_examples 'a Gist' do
+ it 'returns an instance of Gist' do
+ expect(gist).to be_an_instance_of(described_class)
+ end
+
+ context 'with Gist' do
+ it 'includes gist attributes' do
+ expect(gist).to have_attributes(
+ id: '1',
+ description: 'Gist title',
+ is_public: true,
+ files: { '_Summary.md': { filename: '_Summary.md', raw_url: 'https://some_url' } },
+ git_pull_url: 'https://gist.github.com/gistid.git'
+ )
+ end
+ end
+ end
+
+ describe '.from_api_response' do
+ let(:response) do
+ {
+ id: '1',
+ description: 'Gist title',
+ public: true,
+ created_at: '2022-04-26 18:30:53 UTC',
+ updated_at: '2022-04-26 18:30:53 UTC',
+ files: { '_Summary.md': { filename: '_Summary.md', raw_url: 'https://some_url' } },
+ git_pull_url: 'https://gist.github.com/gistid.git'
+ }
+ end
+
+ it_behaves_like 'a Gist' do
+ let(:gist) { described_class.from_api_response(response) }
+ end
+ end
+
+ describe '.from_json_hash' do
+ it_behaves_like 'a Gist' do
+ let(:hash) do
+ {
+ 'id' => '1',
+ 'description' => 'Gist title',
+ 'is_public' => true,
+ 'files' => { '_Summary.md': { filename: '_Summary.md', raw_url: 'https://some_url' } },
+ 'git_pull_url' => 'https://gist.github.com/gistid.git'
+ }
+ end
+
+ let(:gist) { described_class.from_json_hash(hash) }
+ end
+ end
+
+ describe '#truncated_title' do
+ it 'truncates the title to 255 characters' do
+ object = described_class.new(description: 'm' * 300)
+
+ expect(object.truncated_title.length).to eq(255)
+ end
+
+ it 'does not truncate the title if it is shorter than 255 characters' do
+ object = described_class.new(description: 'foo')
+
+ expect(object.truncated_title).to eq('foo')
+ end
+ end
+
+ describe '#github_identifiers' do
+ it 'returns a hash with needed identifiers' do
+ github_identifiers = { id: 1 }
+ gist = described_class.new(github_identifiers.merge(something_else: '_something_else_'))
+
+ expect(gist.github_identifiers).to eq(github_identifiers)
+ end
+ end
+
+ describe '#visibility_level' do
+ it 'returns 20 when public' do
+ visibility = { is_public: true }
+ gist = described_class.new(visibility.merge(something_else: '_something_else_'))
+
+ expect(gist.visibility_level).to eq(20)
+ end
+
+ it 'returns 0 when private' do
+ visibility = { is_public: false }
+ gist = described_class.new(visibility.merge(something_else: '_something_else_'))
+
+ expect(gist.visibility_level).to eq(0)
+ end
+ end
+
+ describe '#first_file' do
+ let(:http_response) { instance_double('HTTParty::Response', body: 'File content') }
+
+ before do
+ allow(Gitlab::HTTP).to receive(:try_get).and_return(http_response)
+ end
+
+ it 'returns a hash with needed identifiers' do
+ files = { files: { '_Summary.md': { filename: '_Summary.md', raw_url: 'https://some_url' } } }
+ gist = described_class.new(files.merge(something_else: '_something_else_'))
+
+ expect(gist.first_file).to eq(file_name: '_Summary.md', file_content: 'File content')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_gists_import/status_spec.rb b/spec/lib/gitlab/github_gists_import/status_spec.rb
new file mode 100644
index 00000000000..4cbbbd430eb
--- /dev/null
+++ b/spec/lib/gitlab/github_gists_import/status_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubGistsImport::Status, :clean_gitlab_redis_cache, feature_category: :importer do
+ subject(:import_status) { described_class.new(user.id) }
+
+ let_it_be(:user) { create(:user) }
+ let(:key) { "gitlab:github-gists-import:#{user.id}" }
+
+ describe '#start!' do
+ it 'expires the key' do
+ import_status.start!
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.get(key)).to eq('started')
+ end
+ end
+ end
+
+ describe '#fail!' do
+ it 'sets failed status' do
+ import_status.fail!
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.get(key)).to eq('failed')
+ end
+ end
+ end
+
+ describe '#finish!' do
+ it 'sets finished status' do
+ import_status.finish!
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.get(key)).to eq('finished')
+ end
+ end
+ end
+
+ describe '#started?' do
+ before do
+ Gitlab::Redis::SharedState.with { |redis| redis.set(key, 'started') }
+ end
+
+ it 'checks if status is started' do
+ expect(import_status.started?).to eq(true)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/bulk_importing_spec.rb b/spec/lib/gitlab/github_import/bulk_importing_spec.rb
index e170496ff7b..af31cb6c873 100644
--- a/spec/lib/gitlab/github_import/bulk_importing_spec.rb
+++ b/spec/lib/gitlab/github_import/bulk_importing_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::GithubImport::BulkImporting do
+RSpec.describe Gitlab::GithubImport::BulkImporting, feature_category: :importer do
let(:project) { instance_double(Project, id: 1) }
let(:importer) { MyImporter.new(project, double) }
let(:importer_class) do
@@ -12,22 +12,33 @@ RSpec.describe Gitlab::GithubImport::BulkImporting do
def object_type
:object_type
end
+
+ def model
+ Label
+ end
end
end
+ let(:label) { instance_double('Label', invalid?: false) }
+
before do
stub_const 'MyImporter', importer_class
end
describe '#build_database_rows' do
- it 'returns an Array containing the rows to insert' do
+ it 'returns an Array containing the rows to insert and validation errors if object invalid' do
object = double(:object, title: 'Foo')
expect(importer)
- .to receive(:build)
+ .to receive(:build_attributes)
.with(object)
.and_return({ title: 'Foo' })
+ expect(Label)
+ .to receive(:new)
+ .with({ title: 'Foo' })
+ .and_return(label)
+
expect(importer)
.to receive(:already_imported?)
.with(object)
@@ -53,14 +64,17 @@ RSpec.describe Gitlab::GithubImport::BulkImporting do
enum = [[object, 1]].to_enum
- expect(importer.build_database_rows(enum)).to eq([{ title: 'Foo' }])
+ rows, errors = importer.build_database_rows(enum)
+
+ expect(rows).to match_array([{ title: 'Foo' }])
+ expect(errors).to be_empty
end
it 'does not import objects that have already been imported' do
object = double(:object, title: 'Foo')
expect(importer)
- .not_to receive(:build)
+ .not_to receive(:build_attributes)
expect(importer)
.to receive(:already_imported?)
@@ -87,14 +101,16 @@ RSpec.describe Gitlab::GithubImport::BulkImporting do
enum = [[object, 1]].to_enum
- expect(importer.build_database_rows(enum)).to be_empty
+ rows, errors = importer.build_database_rows(enum)
+
+ expect(rows).to be_empty
+ expect(errors).to be_empty
end
end
describe '#bulk_insert' do
it 'bulk inserts rows into the database' do
rows = [{ title: 'Foo' }] * 10
- model = double(:model, table_name: 'kittens')
expect(Gitlab::Import::Logger)
.to receive(:info)
@@ -119,14 +135,43 @@ RSpec.describe Gitlab::GithubImport::BulkImporting do
expect(ApplicationRecord)
.to receive(:legacy_bulk_insert)
.ordered
- .with('kittens', rows.first(5))
+ .with('labels', rows.first(5))
expect(ApplicationRecord)
.to receive(:legacy_bulk_insert)
.ordered
- .with('kittens', rows.last(5))
+ .with('labels', rows.last(5))
+
+ importer.bulk_insert(rows, batch_size: 5)
+ end
+ end
+
+ describe '#bulk_insert_failures', :timecop do
+ let(:import_failures) { instance_double('ImportFailure::ActiveRecord_Associations_CollectionProxy') }
+ let(:label) { Label.new(title: 'invalid,title') }
+ let(:validation_errors) { ActiveModel::Errors.new(label) }
+ let(:formatted_errors) do
+ [{
+ source: 'MyImporter',
+ exception_class: 'ActiveRecord::RecordInvalid',
+ exception_message: 'Title invalid',
+ correlation_id_value: 'cid',
+ retry_count: nil,
+ created_at: Time.zone.now
+ }]
+ end
- importer.bulk_insert(model, rows, batch_size: 5)
+ it 'bulk inserts validation errors into import_failures' do
+ error = ActiveModel::Errors.new(label)
+ error.add(:base, 'Title invalid')
+
+ freeze_time do
+ expect(project).to receive(:import_failures).and_return(import_failures)
+ expect(import_failures).to receive(:insert_all).with(formatted_errors)
+ expect(Labkit::Correlation::CorrelationId).to receive(:current_or_new_id).and_return('cid')
+
+ importer.bulk_insert_failures([error])
+ end
end
end
end
diff --git a/spec/lib/gitlab/github_import/client_spec.rb b/spec/lib/gitlab/github_import/client_spec.rb
index 95f7933fbc5..526a8721ff3 100644
--- a/spec/lib/gitlab/github_import/client_spec.rb
+++ b/spec/lib/gitlab/github_import/client_spec.rb
@@ -579,13 +579,69 @@ RSpec.describe Gitlab::GithubImport::Client do
allow(client.octokit).to receive(:user).and_return(user)
end
- describe '#search_repos_by_name' do
+ describe '#search_repos_by_name_graphql' do
+ let(:expected_query) { 'test in:name is:public,private user:user repo:repo1 repo:repo2 org:org1 org:org2' }
+ let(:expected_graphql_params) { "type: REPOSITORY, query: \"#{expected_query}\"" }
+ let(:expected_graphql) do
+ <<-TEXT
+ {
+ search(#{expected_graphql_params}) {
+ nodes {
+ __typename
+ ... on Repository {
+ id: databaseId
+ name
+ full_name: nameWithOwner
+ owner { login }
+ }
+ }
+ pageInfo {
+ startCursor
+ endCursor
+ hasNextPage
+ hasPreviousPage
+ }
+ }
+ }
+ TEXT
+ end
+
it 'searches for repositories based on name' do
- expected_search_query = 'test in:name is:public,private user:user repo:repo1 repo:repo2 org:org1 org:org2'
+ expect(client.octokit).to receive(:post).with(
+ '/graphql', { query: expected_graphql }.to_json
+ )
- expect(client.octokit).to receive(:search_repositories).with(expected_search_query, {})
+ client.search_repos_by_name_graphql('test')
+ end
- client.search_repos_by_name('test')
+ context 'when pagination options present' do
+ context 'with "first" option' do
+ let(:expected_graphql_params) do
+ "type: REPOSITORY, query: \"#{expected_query}\", first: 25"
+ end
+
+ it 'searches for repositories via expected query' do
+ expect(client.octokit).to receive(:post).with(
+ '/graphql', { query: expected_graphql }.to_json
+ )
+
+ client.search_repos_by_name_graphql('test', { first: 25 })
+ end
+ end
+
+ context 'with "after" option' do
+ let(:expected_graphql_params) do
+ "type: REPOSITORY, query: \"#{expected_query}\", after: \"Y3Vyc29yOjE=\""
+ end
+
+ it 'searches for repositories via expected query' do
+ expect(client.octokit).to receive(:post).with(
+ '/graphql', { query: expected_graphql }.to_json
+ )
+
+ client.search_repos_by_name_graphql('test', { after: 'Y3Vyc29yOjE=' })
+ end
+ end
end
context 'when Faraday error received from octokit', :aggregate_failures do
@@ -593,41 +649,62 @@ RSpec.describe Gitlab::GithubImport::Client do
let(:info_params) { { 'error.class': error_class } }
it 'retries on error and succeeds' do
- allow_retry(:search_repositories)
+ allow_retry(:post)
expect(Gitlab::Import::Logger).to receive(:info).with(hash_including(info_params)).once
- expect(client.search_repos_by_name('test')).to eq({})
+ expect(client.search_repos_by_name_graphql('test')).to eq({})
end
it 'retries and does not succeed' do
- allow(client.octokit).to receive(:search_repositories).and_raise(error_class, 'execution expired')
+ allow(client.octokit)
+ .to receive(:post)
+ .with('/graphql', { query: expected_graphql }.to_json)
+ .and_raise(error_class, 'execution expired')
- expect { client.search_repos_by_name('test') }.to raise_error(error_class, 'execution expired')
+ expect { client.search_repos_by_name_graphql('test') }.to raise_error(error_class, 'execution expired')
end
end
end
- describe '#search_query' do
- it 'returns base search query' do
- result = client.search_query(str: 'test', type: :test, include_collaborations: false, include_orgs: false)
+ describe '#search_repos_by_name' do
+ let(:expected_query) { 'test in:name is:public,private user:user repo:repo1 repo:repo2 org:org1 org:org2' }
- expect(result).to eq('test in:test is:public,private user:user')
+ it 'searches for repositories based on name' do
+ expect(client.octokit).to receive(:search_repositories).with(expected_query, {})
+
+ client.search_repos_by_name('test')
end
- context 'when include_collaborations is true' do
- it 'returns search query including users' do
- result = client.search_query(str: 'test', type: :test, include_collaborations: true, include_orgs: false)
+ context 'when pagination options present' do
+ it 'searches for repositories via expected query' do
+ expect(client.octokit).to receive(:search_repositories).with(
+ expected_query, { page: 2, per_page: 25 }
+ )
- expect(result).to eq('test in:test is:public,private user:user repo:repo1 repo:repo2')
+ client.search_repos_by_name('test', { page: 2, per_page: 25 })
end
end
- context 'when include_orgs is true' do
- it 'returns search query including users' do
- result = client.search_query(str: 'test', type: :test, include_collaborations: false, include_orgs: true)
+ context 'when Faraday error received from octokit', :aggregate_failures do
+ let(:error_class) { described_class::CLIENT_CONNECTION_ERROR }
+ let(:info_params) { { 'error.class': error_class } }
+
+ it 'retries on error and succeeds' do
+ allow_retry(:search_repositories)
+
+ expect(Gitlab::Import::Logger).to receive(:info).with(hash_including(info_params)).once
+
+ expect(client.search_repos_by_name('test')).to eq({})
+ end
+
+ it 'retries and does not succeed' do
+ allow(client.octokit)
+ .to receive(:search_repositories)
+ .with(expected_query, {})
+ .and_raise(error_class, 'execution expired')
- expect(result).to eq('test in:test is:public,private user:user org:org1 org:org2')
+ expect { client.search_repos_by_name('test') }.to raise_error(error_class, 'execution expired')
end
end
end
diff --git a/spec/lib/gitlab/github_import/clients/proxy_spec.rb b/spec/lib/gitlab/github_import/clients/proxy_spec.rb
new file mode 100644
index 00000000000..9fef57f2a38
--- /dev/null
+++ b/spec/lib/gitlab/github_import/clients/proxy_spec.rb
@@ -0,0 +1,102 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Clients::Proxy, :manage, feature_category: :import do
+ subject(:client) { described_class.new(access_token, client_options) }
+
+ let(:access_token) { 'test_token' }
+ let(:client_options) { { foo: :bar } }
+
+ describe '#repos' do
+ let(:search_text) { 'search text' }
+ let(:pagination_options) { { limit: 10 } }
+
+ context 'when remove_legacy_github_client FF is enabled' do
+ let(:client_stub) { instance_double(Gitlab::GithubImport::Client) }
+
+ context 'with github_client_fetch_repos_via_graphql FF enabled' do
+ let(:client_response) do
+ {
+ data: {
+ search: {
+ nodes: [{ name: 'foo' }, { name: 'bar' }],
+ pageInfo: { startCursor: 'foo', endCursor: 'bar' }
+ }
+ }
+ }
+ end
+
+ it 'fetches repos with Gitlab::GithubImport::Client (GraphQL API)' do
+ expect(Gitlab::GithubImport::Client)
+ .to receive(:new).with(access_token).and_return(client_stub)
+ expect(client_stub)
+ .to receive(:search_repos_by_name_graphql)
+ .with(search_text, pagination_options).and_return(client_response)
+
+ expect(client.repos(search_text, pagination_options)).to eq(
+ {
+ repos: [{ name: 'foo' }, { name: 'bar' }],
+ page_info: { startCursor: 'foo', endCursor: 'bar' }
+ }
+ )
+ end
+ end
+
+ context 'with github_client_fetch_repos_via_graphql FF disabled' do
+ let(:client_response) do
+ { items: [{ name: 'foo' }, { name: 'bar' }] }
+ end
+
+ before do
+ stub_feature_flags(github_client_fetch_repos_via_graphql: false)
+ end
+
+ it 'fetches repos with Gitlab::GithubImport::Client (REST API)' do
+ expect(Gitlab::GithubImport::Client)
+ .to receive(:new).with(access_token).and_return(client_stub)
+ expect(client_stub)
+ .to receive(:search_repos_by_name)
+ .with(search_text, pagination_options).and_return(client_response)
+
+ expect(client.repos(search_text, pagination_options)).to eq(
+ { repos: [{ name: 'foo' }, { name: 'bar' }] }
+ )
+ end
+ end
+ end
+
+ context 'when remove_legacy_github_client FF is disabled' do
+ let(:client_stub) { instance_double(Gitlab::LegacyGithubImport::Client) }
+ let(:search_text) { nil }
+
+ before do
+ stub_feature_flags(remove_legacy_github_client: false)
+ end
+
+ it 'fetches repos with Gitlab::LegacyGithubImport::Client' do
+ expect(Gitlab::LegacyGithubImport::Client)
+ .to receive(:new).with(access_token, client_options).and_return(client_stub)
+ expect(client_stub).to receive(:repos)
+ .and_return([{ name: 'foo' }, { name: 'bar' }])
+
+ expect(client.repos(search_text, pagination_options))
+ .to eq({ repos: [{ name: 'foo' }, { name: 'bar' }] })
+ end
+
+ context 'with filter params' do
+ let(:search_text) { 'fo' }
+
+ it 'fetches repos with Gitlab::LegacyGithubImport::Client' do
+ expect(Gitlab::LegacyGithubImport::Client)
+ .to receive(:new).with(access_token, client_options).and_return(client_stub)
+ expect(client_stub).to receive(:repos)
+ .and_return([{ name: 'FOO' }, { name: 'bAr' }])
+
+ expect(client.repos(search_text, pagination_options))
+ .to eq({ repos: [{ name: 'FOO' }] })
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb b/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
index 8eeb2332131..73ba49bf4ed 100644
--- a/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
@@ -35,7 +35,8 @@ RSpec.describe Gitlab::GithubImport::Importer::DiffNoteImporter, :aggregate_fail
end_line: end_line,
github_id: 1,
diff_hunk: diff_hunk,
- side: 'RIGHT'
+ side: 'RIGHT',
+ discussion_id: discussion_id
)
end
@@ -114,10 +115,6 @@ RSpec.describe Gitlab::GithubImport::Importer::DiffNoteImporter, :aggregate_fail
.to receive(:database_id)
.and_return(merge_request.id)
end
-
- expect(Discussion)
- .to receive(:discussion_id)
- .and_return(discussion_id)
end
it_behaves_like 'diff notes without suggestion'
@@ -218,6 +215,16 @@ RSpec.describe Gitlab::GithubImport::Importer::DiffNoteImporter, :aggregate_fail
end
end
end
+
+ context 'when diff note is invalid' do
+ it 'fails validation' do
+ stub_user_finder(user.id, true)
+
+ expect(note_representation).to receive(:line_code).and_return(nil)
+
+ expect { subject.execute }.to raise_error(ActiveRecord::RecordInvalid)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/issues_importer_spec.rb b/spec/lib/gitlab/github_import/importer/issues_importer_spec.rb
index 308b8185589..4a5525c250e 100644
--- a/spec/lib/gitlab/github_import/importer/issues_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/issues_importer_spec.rb
@@ -90,9 +90,13 @@ RSpec.describe Gitlab::GithubImport::Importer::IssuesImporter do
.to receive(:each_object_to_import)
.and_yield(github_issue)
- expect(Gitlab::GithubImport::ImportIssueWorker).to receive(:bulk_perform_in).with(1.second, [
- [project.id, an_instance_of(Hash), an_instance_of(String)]
- ], batch_size: 1000, batch_delay: 1.minute)
+ expect(Gitlab::GithubImport::ImportIssueWorker)
+ .to receive(:bulk_perform_in)
+ .with(1.second,
+ [[project.id, an_instance_of(Hash), an_instance_of(String)]],
+ batch_size: 1000,
+ batch_delay: 1.minute
+ )
waiter = importer.parallel_import
diff --git a/spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb b/spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb
index e68849755b2..e005d8eda84 100644
--- a/spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb
@@ -36,26 +36,11 @@ RSpec.describe Gitlab::GithubImport::Importer::LabelLinksImporter do
expect(importer)
.to receive(:find_target_id)
- .and_return(1)
+ .and_return(4)
- freeze_time do
- expect(ApplicationRecord)
- .to receive(:legacy_bulk_insert)
- .with(
- LabelLink.table_name,
- [
- {
- label_id: 2,
- target_id: 1,
- target_type: Issue,
- created_at: Time.zone.now,
- updated_at: Time.zone.now
- }
- ]
- )
+ expect(LabelLink).to receive(:bulk_insert!)
- importer.create_labels
- end
+ importer.create_labels
end
it 'does not insert label links for non-existing labels' do
@@ -64,9 +49,9 @@ RSpec.describe Gitlab::GithubImport::Importer::LabelLinksImporter do
.with('bug')
.and_return(nil)
- expect(ApplicationRecord)
- .to receive(:legacy_bulk_insert)
- .with(LabelLink.table_name, [])
+ expect(LabelLink)
+ .to receive(:bulk_insert!)
+ .with([])
importer.create_labels
end
diff --git a/spec/lib/gitlab/github_import/importer/labels_importer_spec.rb b/spec/lib/gitlab/github_import/importer/labels_importer_spec.rb
index 81d534c566f..ad9ef4afddd 100644
--- a/spec/lib/gitlab/github_import/importer/labels_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/labels_importer_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::GithubImport::Importer::LabelsImporter, :clean_gitlab_redis_cache do
+RSpec.describe Gitlab::GithubImport::Importer::LabelsImporter, :clean_gitlab_redis_cache, feature_category: :importer do
let(:project) { create(:project, import_source: 'foo/bar') }
let(:client) { double(:client) }
let(:importer) { described_class.new(project, client) }
@@ -11,40 +11,58 @@ RSpec.describe Gitlab::GithubImport::Importer::LabelsImporter, :clean_gitlab_red
it 'imports the labels in bulk' do
label_hash = { title: 'bug', color: '#fffaaa' }
- expect(importer)
- .to receive(:build_labels)
- .and_return([label_hash])
-
- expect(importer)
- .to receive(:bulk_insert)
- .with(Label, [label_hash])
-
- expect(importer)
- .to receive(:build_labels_cache)
+ expect(importer).to receive(:build_labels).and_return([[label_hash], []])
+ expect(importer).to receive(:bulk_insert).with([label_hash])
+ expect(importer).to receive(:build_labels_cache)
importer.execute
end
end
describe '#build_labels' do
- it 'returns an Array containnig label rows' do
+ it 'returns an Array containing label rows' do
label = { name: 'bug', color: 'ffffff' }
expect(importer).to receive(:each_label).and_return([label])
- rows = importer.build_labels
+ rows, errors = importer.build_labels
expect(rows.length).to eq(1)
expect(rows[0][:title]).to eq('bug')
+ expect(errors).to be_blank
end
- it 'does not create labels that already exist' do
+ it 'does not build labels that already exist' do
create(:label, project: project, title: 'bug')
label = { name: 'bug', color: 'ffffff' }
expect(importer).to receive(:each_label).and_return([label])
- expect(importer.build_labels).to be_empty
+
+ rows, errors = importer.build_labels
+
+ expect(rows).to be_empty
+ expect(errors).to be_empty
+ end
+
+ it 'does not build labels that are invalid' do
+ label = { id: 1, name: 'bug,bug', color: 'ffffff' }
+
+ expect(importer).to receive(:each_label).and_return([label])
+ expect(Gitlab::Import::Logger).to receive(:error)
+ .with(
+ import_type: :github,
+ project_id: project.id,
+ importer: described_class.name,
+ message: ['Title is invalid'],
+ github_identifier: 1
+ )
+
+ rows, errors = importer.build_labels
+
+ expect(rows).to be_empty
+ expect(errors.length).to eq(1)
+ expect(errors[0].full_messages).to match_array(['Title is invalid'])
end
end
@@ -58,9 +76,9 @@ RSpec.describe Gitlab::GithubImport::Importer::LabelsImporter, :clean_gitlab_red
end
end
- describe '#build' do
+ describe '#build_attributes' do
let(:label_hash) do
- importer.build({ name: 'bug', color: 'ffffff' })
+ importer.build_attributes({ name: 'bug', color: 'ffffff' })
end
it 'returns the attributes of the label as a Hash' do
diff --git a/spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb b/spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb
index 99536588718..678aa705b6c 100644
--- a/spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb
@@ -58,7 +58,7 @@ RSpec.describe Gitlab::GithubImport::Importer::LfsObjectsImporter do
expect_next_instance_of(Projects::LfsPointers::LfsObjectDownloadListService) do |service|
expect(service)
- .to receive(:execute)
+ .to receive(:each_list_item)
.and_raise(exception)
end
@@ -79,7 +79,7 @@ RSpec.describe Gitlab::GithubImport::Importer::LfsObjectsImporter do
expect_next_instance_of(Projects::LfsPointers::LfsObjectDownloadListService) do |service|
expect(service)
- .to receive(:execute)
+ .to receive(:each_list_item)
.and_raise(exception)
end
@@ -94,7 +94,7 @@ RSpec.describe Gitlab::GithubImport::Importer::LfsObjectsImporter do
lfs_object_importer = double(:lfs_object_importer)
expect_next_instance_of(Projects::LfsPointers::LfsObjectDownloadListService) do |service|
- expect(service).to receive(:execute).and_return([lfs_download_object])
+ expect(service).to receive(:each_list_item).and_yield(lfs_download_object)
end
expect(Gitlab::GithubImport::Importer::LfsObjectImporter)
@@ -115,7 +115,7 @@ RSpec.describe Gitlab::GithubImport::Importer::LfsObjectsImporter do
importer = described_class.new(project, client)
expect_next_instance_of(Projects::LfsPointers::LfsObjectDownloadListService) do |service|
- expect(service).to receive(:execute).and_return([lfs_download_object])
+ expect(service).to receive(:each_list_item).and_yield(lfs_download_object)
end
expect(Gitlab::GithubImport::ImportLfsObjectWorker).to receive(:bulk_perform_in)
diff --git a/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb b/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
index 04d76bd1f06..8667729d79b 100644
--- a/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Gitlab::GithubImport::Importer::MilestonesImporter, :clean_gitlab_redis_cache do
+RSpec.describe Gitlab::GithubImport::Importer::MilestonesImporter, :clean_gitlab_redis_cache,
+ feature_category: :importer do
let(:project) { create(:project, import_source: 'foo/bar') }
let(:client) { double(:client) }
let(:importer) { described_class.new(project, client) }
@@ -38,41 +39,61 @@ RSpec.describe Gitlab::GithubImport::Importer::MilestonesImporter, :clean_gitlab
it 'imports the milestones in bulk' do
milestone_hash = { number: 1, title: '1.0' }
- expect(importer)
- .to receive(:build_milestones)
- .and_return([milestone_hash])
-
- expect(importer)
- .to receive(:bulk_insert)
- .with(Milestone, [milestone_hash])
-
- expect(importer)
- .to receive(:build_milestones_cache)
+ expect(importer).to receive(:build_milestones).and_return([[milestone_hash], []])
+ expect(importer).to receive(:bulk_insert).with([milestone_hash])
+ expect(importer).to receive(:build_milestones_cache)
importer.execute
end
end
describe '#build_milestones' do
- it 'returns an Array containnig milestone rows' do
+ it 'returns an Array containing milestone rows' do
expect(importer)
.to receive(:each_milestone)
.and_return([milestone])
- rows = importer.build_milestones
+ rows, errors = importer.build_milestones
expect(rows.length).to eq(1)
expect(rows[0][:title]).to eq('1.0')
+ expect(errors).to be_empty
end
- it 'does not create milestones that already exist' do
+ it 'does not build milestones that already exist' do
create(:milestone, project: project, title: '1.0', iid: 1)
expect(importer)
.to receive(:each_milestone)
.and_return([milestone])
- expect(importer.build_milestones).to be_empty
+ rows, errors = importer.build_milestones
+
+ expect(rows).to be_empty
+ expect(errors).to be_empty
+ end
+
+ it 'does not build milestones that are invalid' do
+ milestone = { id: 1, title: nil }
+
+ expect(importer)
+ .to receive(:each_milestone)
+ .and_return([milestone])
+
+ expect(Gitlab::Import::Logger).to receive(:error)
+ .with(
+ import_type: :github,
+ project_id: project.id,
+ importer: described_class.name,
+ message: ["Title can't be blank"],
+ github_identifier: 1
+ )
+
+ rows, errors = importer.build_milestones
+
+ expect(rows).to be_empty
+ expect(errors.length).to eq(1)
+ expect(errors[0].full_messages).to match_array(["Title can't be blank"])
end
end
@@ -86,9 +107,9 @@ RSpec.describe Gitlab::GithubImport::Importer::MilestonesImporter, :clean_gitlab
end
end
- describe '#build' do
- let(:milestone_hash) { importer.build(milestone) }
- let(:milestone_hash2) { importer.build(milestone2) }
+ describe '#build_attributes' do
+ let(:milestone_hash) { importer.build_attributes(milestone) }
+ let(:milestone_hash2) { importer.build_attributes(milestone2) }
it 'returns the attributes of the milestone as a Hash' do
expect(milestone_hash).to be_an_instance_of(Hash)
diff --git a/spec/lib/gitlab/github_import/importer/note_importer_spec.rb b/spec/lib/gitlab/github_import/importer/note_importer_spec.rb
index c60ecd85e92..5ac50578b6a 100644
--- a/spec/lib/gitlab/github_import/importer/note_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/note_importer_spec.rb
@@ -113,6 +113,19 @@ RSpec.describe Gitlab::GithubImport::Importer::NoteImporter do
.to eq('There were an invalid char "" <= right here')
end
end
+
+ context 'when note is invalid' do
+ it 'fails validation' do
+ expect(importer.user_finder)
+ .to receive(:author_id_for)
+ .with(github_note)
+ .and_return([user.id, true])
+
+ expect(github_note).to receive(:discussion_id).and_return('invalid')
+
+ expect { importer.execute }.to raise_error(ActiveRecord::RecordInvalid)
+ end
+ end
end
context 'when the noteable does not exist' do
diff --git a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
index c7388314253..dd73b6879e0 100644
--- a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
@@ -202,6 +202,20 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitla
importer.create_merge_request
end
end
+
+ context 'when merge request is invalid' do
+ before do
+ allow(pull_request).to receive(:formatted_source_branch).and_return(nil)
+ allow(importer.user_finder)
+ .to receive(:author_id_for)
+ .with(pull_request)
+ .and_return([project.creator_id, false])
+ end
+
+ it 'fails validation' do
+ expect { importer.create_merge_request }.to raise_error(ActiveRecord::RecordInvalid)
+ end
+ end
end
describe '#set_merge_request_assignees' do
@@ -292,6 +306,16 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitla
expect(project.repository.branch_exists?(mr.source_branch)).to be_falsey
expect(project.repository.branch_exists?(mr.target_branch)).to be_truthy
end
+
+ it 'ignores Git PreReceive errors when creating a branch' do
+ expect(project.repository).to receive(:add_branch).and_raise(Gitlab::Git::PreReceiveError)
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).and_call_original
+
+ mr = insert_git_data
+
+ expect(project.repository.branch_exists?(mr.source_branch)).to be_falsey
+ expect(project.repository.branch_exists?(mr.target_branch)).to be_truthy
+ end
end
it 'creates a merge request diff and sets it as the latest' do
diff --git a/spec/lib/gitlab/github_import/importer/pull_request_merged_by_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_request_merged_by_importer_spec.rb
index f3a9bbac785..01d706beea2 100644
--- a/spec/lib/gitlab/github_import/importer/pull_request_merged_by_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_request_merged_by_importer_spec.rb
@@ -22,6 +22,23 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestMergedByImporter, :cle
subject { described_class.new(pull_request, project, client_double) }
+ shared_examples 'adds a note referencing the merger user' do
+ it 'adds a note referencing the merger user' do
+ expect { subject.execute }
+ .to change(Note, :count).by(1)
+ .and not_change(merge_request, :updated_at)
+
+ metrics = merge_request.metrics.reload
+ expect(metrics.merged_by).to be_nil
+ expect(metrics.merged_at).to eq(merged_at)
+
+ last_note = merge_request.notes.last
+ expect(last_note.created_at).to eq(merged_at)
+ expect(last_note.author).to eq(project.creator)
+ expect(last_note.note).to eq("*Merged by: merger at #{merged_at}*")
+ end
+ end
+
context 'when the merger user can be mapped' do
it 'assigns the merged by user when mapped' do
merge_user = create(:user, email: 'merger@email.com')
@@ -35,19 +52,14 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestMergedByImporter, :cle
end
context 'when the merger user cannot be mapped to a gitlab user' do
- it 'adds a note referencing the merger user' do
- expect { subject.execute }
- .to change(Note, :count).by(1)
- .and not_change(merge_request, :updated_at)
+ it_behaves_like 'adds a note referencing the merger user'
- metrics = merge_request.metrics.reload
- expect(metrics.merged_by).to be_nil
- expect(metrics.merged_at).to eq(merged_at)
+ context 'when original user cannot be found on github' do
+ before do
+ allow(client_double).to receive(:user).and_raise(Octokit::NotFound)
+ end
- last_note = merge_request.notes.last
- expect(last_note.note).to eq("*Merged by: merger at 2017-01-01 12:00:00 UTC*")
- expect(last_note.created_at).to eq(merged_at)
- expect(last_note.author).to eq(project.creator)
+ it_behaves_like 'adds a note referencing the merger user'
end
end
@@ -64,9 +76,9 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestMergedByImporter, :cle
expect(metrics.merged_at).to eq(merged_at)
last_note = merge_request.notes.last
- expect(last_note.note).to eq("*Merged by: ghost at 2017-01-01 12:00:00 UTC*")
expect(last_note.created_at).to eq(merged_at)
expect(last_note.author).to eq(project.creator)
+ expect(last_note.note).to eq("*Merged by: ghost at #{merged_at}*")
end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/pull_request_review_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_request_review_importer_spec.rb
index 49794eceb5a..2e1a3c496cc 100644
--- a/spec/lib/gitlab/github_import/importer/pull_request_review_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_request_review_importer_spec.rb
@@ -208,6 +208,23 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestReviewImporter, :clean
end
end
+ context 'when original author cannot be found on github' do
+ before do
+ allow(client_double).to receive(:user).and_raise(Octokit::NotFound)
+ end
+
+ let(:review) { create_review(type: 'APPROVED', note: '') }
+
+ it 'creates a note for the review with the author username' do
+ expect { subject.execute }
+ .to change(Note, :count).by(1)
+ last_note = merge_request.notes.last
+ expect(last_note.note).to eq("*Created by: author*\n\n**Review:** Approved")
+ expect(last_note.author).to eq(project.creator)
+ expect(last_note.created_at).to eq(submitted_at)
+ end
+ end
+
context 'when the submitted_at is not provided' do
let(:review) { create_review(type: 'APPROVED', note: '', submitted_at: nil) }
diff --git a/spec/lib/gitlab/github_import/importer/pull_requests/review_requests_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_requests/review_requests_importer_spec.rb
index 6c7fc4d5b15..3bf1976ee10 100644
--- a/spec/lib/gitlab/github_import/importer/pull_requests/review_requests_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_requests/review_requests_importer_spec.rb
@@ -109,7 +109,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequests::ReviewRequestsImpor
expect(Gitlab::GithubImport::PullRequests::ImportReviewRequestWorker)
.to receive(:bulk_perform_in).with(
1.second,
- expected_worker_payload,
+ match_array(expected_worker_payload),
batch_size: 1000,
batch_delay: 1.minute
)
diff --git a/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb b/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb
index 84d639a09ef..ccbe5b5fc50 100644
--- a/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::GithubImport::Importer::ReleasesImporter do
+RSpec.describe Gitlab::GithubImport::Importer::ReleasesImporter, feature_category: :importer do
let(:project) { create(:project) }
let(:client) { double(:client) }
let(:importer) { described_class.new(project, client) }
@@ -48,8 +48,8 @@ RSpec.describe Gitlab::GithubImport::Importer::ReleasesImporter do
released_at: released_at
}
- expect(importer).to receive(:build_releases).and_return([release_hash])
- expect(importer).to receive(:bulk_insert).with(Release, [release_hash])
+ expect(importer).to receive(:build_releases).and_return([[release_hash], []])
+ expect(importer).to receive(:bulk_insert).with([release_hash])
importer.execute
end
@@ -86,24 +86,29 @@ RSpec.describe Gitlab::GithubImport::Importer::ReleasesImporter do
it 'returns an Array containing release rows' do
expect(importer).to receive(:each_release).and_return([github_release])
- rows = importer.build_releases
+ rows, errors = importer.build_releases
expect(rows.length).to eq(1)
expect(rows[0][:tag]).to eq('1.0')
+ expect(errors).to be_empty
end
it 'does not create releases that already exist' do
create(:release, project: project, tag: '1.0', description: '1.0')
expect(importer).to receive(:each_release).and_return([github_release])
- expect(importer.build_releases).to be_empty
+
+ rows, errors = importer.build_releases
+
+ expect(rows).to be_empty
+ expect(errors).to be_empty
end
it 'uses a default release description if none is provided' do
github_release[:body] = nil
expect(importer).to receive(:each_release).and_return([github_release])
- release = importer.build_releases.first
+ release, _ = importer.build_releases.first
expect(release[:description]).to eq('Release for tag 1.0')
end
@@ -115,20 +120,36 @@ RSpec.describe Gitlab::GithubImport::Importer::ReleasesImporter do
}
expect(importer).to receive(:each_release).and_return([null_tag_release])
- expect(importer.build_releases).to be_empty
+
+ rows, errors = importer.build_releases
+
+ expect(rows).to be_empty
+ expect(errors).to be_empty
end
it 'does not create duplicate release tags' do
expect(importer).to receive(:each_release).and_return([github_release, github_release])
- releases = importer.build_releases
+ releases, _ = importer.build_releases
expect(releases.length).to eq(1)
expect(releases[0][:description]).to eq('This is my release')
end
+
+ it 'does not create invalid release' do
+ github_release[:body] = SecureRandom.alphanumeric(Gitlab::Database::MAX_TEXT_SIZE_LIMIT + 1)
+
+ expect(importer).to receive(:each_release).and_return([github_release])
+
+ releases, errors = importer.build_releases
+
+ expect(releases).to be_empty
+ expect(errors.length).to eq(1)
+ expect(errors[0].full_messages).to match_array(['Description is too long (maximum is 1000000 characters)'])
+ end
end
- describe '#build' do
- let(:release_hash) { importer.build(github_release) }
+ describe '#build_attributes' do
+ let(:release_hash) { importer.build_attributes(github_release) }
context 'the returned Hash' do
before do
diff --git a/spec/lib/gitlab/github_import/markdown/attachment_spec.rb b/spec/lib/gitlab/github_import/markdown/attachment_spec.rb
index 5d29de34141..588a3076f59 100644
--- a/spec/lib/gitlab/github_import/markdown/attachment_spec.rb
+++ b/spec/lib/gitlab/github_import/markdown/attachment_spec.rb
@@ -35,6 +35,12 @@ RSpec.describe Gitlab::GithubImport::Markdown::Attachment do
it { expect(described_class.from_markdown(markdown_node)).to eq nil }
end
+
+ context 'when URL is blank' do
+ let(:url) { nil }
+
+ it { expect(described_class.from_markdown(markdown_node)).to eq nil }
+ end
end
context "when it's an image attachment" do
@@ -63,6 +69,12 @@ RSpec.describe Gitlab::GithubImport::Markdown::Attachment do
it { expect(described_class.from_markdown(markdown_node)).to eq nil }
end
+
+ context 'when URL is blank' do
+ let(:url) { nil }
+
+ it { expect(described_class.from_markdown(markdown_node)).to eq nil }
+ end
end
context "when it's an inline html node" do
@@ -80,6 +92,12 @@ RSpec.describe Gitlab::GithubImport::Markdown::Attachment do
expect(attachment.name).to eq name
expect(attachment.url).to eq url
end
+
+ context 'when image src is not present' do
+ let(:img) { "<img width=\"248\" alt=\"#{name}\">" }
+
+ it { expect(described_class.from_markdown(markdown_node)).to eq nil }
+ end
end
end
diff --git a/spec/lib/gitlab/github_import/page_counter_spec.rb b/spec/lib/gitlab/github_import/page_counter_spec.rb
index 568bc8cbbef..511b19c00e5 100644
--- a/spec/lib/gitlab/github_import/page_counter_spec.rb
+++ b/spec/lib/gitlab/github_import/page_counter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::GithubImport::PageCounter, :clean_gitlab_redis_cache do
+RSpec.describe Gitlab::GithubImport::PageCounter, :clean_gitlab_redis_cache, feature_category: :importer do
let(:project) { double(:project, id: 1) }
let(:counter) { described_class.new(project, :issues) }
@@ -16,6 +16,16 @@ RSpec.describe Gitlab::GithubImport::PageCounter, :clean_gitlab_redis_cache do
expect(described_class.new(project, :issues).current).to eq(2)
end
+
+ context 'when gists import' do
+ let(:user) { instance_double('User', id: 2) }
+
+ it 'uses gists specific key' do
+ result = described_class.new(user, :gists, 'github-gists-importer')
+
+ expect(result.cache_key).to eq('github-gists-importer/page-counter/2/gists')
+ end
+ end
end
describe '#set' do
diff --git a/spec/lib/gitlab/github_import/representation/diff_note_spec.rb b/spec/lib/gitlab/github_import/representation/diff_note_spec.rb
index a656cd0d056..56fabe854f9 100644
--- a/spec/lib/gitlab/github_import/representation/diff_note_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/diff_note_spec.rb
@@ -128,64 +128,6 @@ RSpec.describe Gitlab::GithubImport::Representation::DiffNote, :clean_gitlab_red
end
end
- describe '#discussion_id' do
- before do
- note.project = project
- note.merge_request = merge_request
- end
-
- context 'when the note is a reply to a discussion' do
- it 'uses the cached value as the discussion_id only when responding an existing discussion' do
- expect(Discussion)
- .to receive(:discussion_id)
- .and_return('FIRST_DISCUSSION_ID', 'SECOND_DISCUSSION_ID')
-
- # Creates the first discussion id and caches its value
- expect(note.discussion_id)
- .to eq('FIRST_DISCUSSION_ID')
-
- reply_note = described_class.from_json_hash(
- 'note_id' => note.note_id + 1,
- 'in_reply_to_id' => note.note_id
- )
- reply_note.project = project
- reply_note.merge_request = merge_request
-
- # Reading from the cached value
- expect(reply_note.discussion_id)
- .to eq('FIRST_DISCUSSION_ID')
-
- new_discussion_note = described_class.from_json_hash(
- 'note_id' => note.note_id + 2,
- 'in_reply_to_id' => nil
- )
- new_discussion_note.project = project
- new_discussion_note.merge_request = merge_request
-
- # Because it's a new discussion, it must not use the cached value
- expect(new_discussion_note.discussion_id)
- .to eq('SECOND_DISCUSSION_ID')
- end
-
- context 'when cached value does not exist' do
- it 'falls back to generating a new discussion_id' do
- expect(Discussion)
- .to receive(:discussion_id)
- .and_return('NEW_DISCUSSION_ID')
-
- reply_note = described_class.from_json_hash(
- 'note_id' => note.note_id + 1,
- 'in_reply_to_id' => note.note_id
- )
- reply_note.project = project
- reply_note.merge_request = merge_request
-
- expect(reply_note.discussion_id).to eq('NEW_DISCUSSION_ID')
- end
- end
- end
- end
-
describe '#github_identifiers' do
it 'returns a hash with needed identifiers' do
expect(note.github_identifiers).to eq(
@@ -273,27 +215,40 @@ RSpec.describe Gitlab::GithubImport::Representation::DiffNote, :clean_gitlab_red
end
describe '.from_api_response' do
- it_behaves_like 'a DiffNote representation' do
- let(:response) do
- {
- id: note_id,
- html_url: 'https://github.com/foo/bar/pull/42',
- path: 'README.md',
- commit_id: '123abc',
- original_commit_id: 'original123abc',
- side: side,
- user: user_data,
- diff_hunk: hunk,
- body: note_body,
- created_at: created_at,
- updated_at: updated_at,
- line: end_line,
- start_line: start_line,
- in_reply_to_id: in_reply_to_id
- }
- end
+ let(:response) do
+ {
+ id: note_id,
+ html_url: 'https://github.com/foo/bar/pull/42',
+ path: 'README.md',
+ commit_id: '123abc',
+ original_commit_id: 'original123abc',
+ side: side,
+ user: user_data,
+ diff_hunk: hunk,
+ body: note_body,
+ created_at: created_at,
+ updated_at: updated_at,
+ line: end_line,
+ start_line: start_line,
+ in_reply_to_id: in_reply_to_id
+ }
+ end
+
+ subject(:note) { described_class.from_api_response(response) }
+
+ it_behaves_like 'a DiffNote representation'
+
+ describe '#discussion_id' do
+ it 'finds or generates discussion_id value' do
+ discussion_id = 'discussion_id'
+ discussion_id_class = Gitlab::GithubImport::Representation::DiffNotes::DiscussionId
- subject(:note) { described_class.from_api_response(response) }
+ expect_next_instance_of(discussion_id_class, response) do |discussion_id_object|
+ expect(discussion_id_object).to receive(:find_or_generate).and_return(discussion_id)
+ end
+
+ expect(note.discussion_id).to eq(discussion_id)
+ end
end
end
@@ -302,6 +257,7 @@ RSpec.describe Gitlab::GithubImport::Representation::DiffNote, :clean_gitlab_red
let(:hash) do
{
'note_id' => note_id,
+ 'html_url' => 'https://github.com/foo/bar/pull/42',
'noteable_type' => 'MergeRequest',
'noteable_id' => 42,
'file_path' => 'README.md',
@@ -315,7 +271,8 @@ RSpec.describe Gitlab::GithubImport::Representation::DiffNote, :clean_gitlab_red
'updated_at' => updated_at.to_s,
'end_line' => end_line,
'start_line' => start_line,
- 'in_reply_to_id' => in_reply_to_id
+ 'in_reply_to_id' => in_reply_to_id,
+ 'discussion_id' => 'FIRST_DISCUSSION_ID'
}
end
diff --git a/spec/lib/gitlab/github_import/representation/diff_notes/discussion_id_spec.rb b/spec/lib/gitlab/github_import/representation/diff_notes/discussion_id_spec.rb
new file mode 100644
index 00000000000..64a16516e07
--- /dev/null
+++ b/spec/lib/gitlab/github_import/representation/diff_notes/discussion_id_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Representation::DiffNotes::DiscussionId, :clean_gitlab_redis_cache,
+ feature_category: :importers do
+ describe '#discussion_id' do
+ let(:hunk) do
+ '@@ -1 +1 @@
+ -Hello
+ +Hello world'
+ end
+
+ let(:note_id) { 1 }
+ let(:html_url) { 'https://github.com/foo/project_name/pull/42' }
+ let(:note) do
+ {
+ id: note_id,
+ html_url: html_url,
+ path: 'README.md',
+ commit_id: '123abc',
+ original_commit_id: 'original123abc',
+ side: 'RIGHT',
+ user: { id: 4, login: 'alice' },
+ diff_hunk: hunk,
+ body: 'Hello world',
+ created_at: Time.new(2017, 1, 1, 12, 10).utc,
+ updated_at: Time.new(2017, 1, 1, 12, 15).utc,
+ line: 23,
+ start_line: nil,
+ in_reply_to_id: nil
+ }
+ end
+
+ context 'when the note is not a reply to a discussion' do
+ subject(:discussion_id) { described_class.new(note).find_or_generate }
+
+ it 'generates and caches new discussion_id' do
+ expect(Discussion)
+ .to receive(:discussion_id)
+ .and_return('FIRST_DISCUSSION_ID')
+
+ expect(Gitlab::Cache::Import::Caching).to receive(:write).with(
+ "github-importer/discussion-id-map/project_name/42/#{note_id}",
+ 'FIRST_DISCUSSION_ID'
+ ).and_return('FIRST_DISCUSSION_ID')
+
+ expect(discussion_id).to eq('FIRST_DISCUSSION_ID')
+ end
+ end
+
+ context 'when the note is a reply to a discussion' do
+ let(:reply_note) do
+ {
+ note_id: note_id + 1,
+ in_reply_to_id: note_id,
+ html_url: html_url
+ }
+ end
+
+ subject(:discussion_id) { described_class.new(reply_note).find_or_generate }
+
+ it 'uses the cached value as the discussion_id' do
+ expect(Discussion)
+ .to receive(:discussion_id)
+ .and_return('FIRST_DISCUSSION_ID')
+
+ described_class.new(note).find_or_generate
+
+ expect(discussion_id).to eq('FIRST_DISCUSSION_ID')
+ end
+
+ context 'when cached value does not exist' do
+ it 'falls back to generating a new discussion_id' do
+ expect(Discussion)
+ .to receive(:discussion_id)
+ .and_return('NEW_DISCUSSION_ID')
+
+ expect(discussion_id).to eq('NEW_DISCUSSION_ID')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/gon_helper_spec.rb b/spec/lib/gitlab/gon_helper_spec.rb
index 5a1fcc5e2dc..6e8997d51c3 100644
--- a/spec/lib/gitlab/gon_helper_spec.rb
+++ b/spec/lib/gitlab/gon_helper_spec.rb
@@ -40,11 +40,11 @@ RSpec.describe Gitlab::GonHelper do
end
end
- describe 'sentry configuration' do
+ context 'when sentry is configured' do
let(:clientside_dsn) { 'https://xxx@sentry.example.com/1' }
let(:environment) { 'staging' }
- describe 'sentry integration' do
+ context 'with legacy sentry configuration' do
before do
stub_config(sentry: { enabled: true, clientside_dsn: clientside_dsn, environment: environment })
end
@@ -57,7 +57,7 @@ RSpec.describe Gitlab::GonHelper do
end
end
- describe 'new sentry integration' do
+ context 'with sentry settings' do
before do
stub_application_setting(sentry_enabled: true)
stub_application_setting(sentry_clientside_dsn: clientside_dsn)
@@ -104,6 +104,7 @@ RSpec.describe Gitlab::GonHelper do
thing = stub_feature_flag_gate('thing')
stub_feature_flags(my_feature_flag: thing)
+ stub_feature_flag_definition(:my_feature_flag)
allow(helper)
.to receive(:gon)
diff --git a/spec/lib/gitlab/graphql/limit/field_call_count_spec.rb b/spec/lib/gitlab/graphql/limit/field_call_count_spec.rb
index 5858986dfc8..afcfb063af3 100644
--- a/spec/lib/gitlab/graphql/limit/field_call_count_spec.rb
+++ b/spec/lib/gitlab/graphql/limit/field_call_count_spec.rb
@@ -36,6 +36,15 @@ RSpec.describe Gitlab::Graphql::Limit::FieldCallCount do
expect(resolve_value).to be_an_instance_of(Gitlab::Graphql::Errors::LimitError)
end
+ it 'does not return an error when the field is called multiple times in separte queries' do
+ query_1 = GraphQL::Query.new(GitlabSchema)
+ query_2 = GraphQL::Query.new(GitlabSchema)
+
+ resolve_field(field, { value: 'foo' }, object_type: owner, query: query_1)
+
+ expect { resolve_field(field, { value: 'foo' }, object_type: owner, query: query_2) }.not_to raise_error
+ end
+
context 'when limit is not specified' do
let(:field) do
::Types::BaseField.new(name: 'value', type: GraphQL::Types::String, null: true, owner: owner) do
diff --git a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb
index 1124868bdae..773df9b20ee 100644
--- a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb
+++ b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb
@@ -50,17 +50,16 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do
end
before do
- stub_feature_flags(graphql_keyset_pagination_without_next_page_query: false)
allow(GitlabSchema).to receive(:default_max_page_size).and_return(2)
end
- it 'invokes an extra query for the next page check' do
+ it 'invokes no an extra query for the next page check' do
arguments[:first] = 1
subject.nodes
count = ActiveRecord::QueryRecorder.new { subject.has_next_page }.count
- expect(count).to eq(1)
+ expect(count).to eq(0)
end
context 'when the relation is loaded' do
@@ -438,382 +437,4 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do
end
end
end
-
- # duplicated tests, remove with the removal of the graphql_keyset_pagination_without_next_page_query FF
- context 'when the graphql_keyset_pagination_without_next_page_query is on' do
- let(:nodes) { Project.all.order(Gitlab::Pagination::Keyset::Order.build([column_order_id])) }
-
- before do
- stub_feature_flags(graphql_keyset_pagination_without_next_page_query: true)
- end
-
- it 'does not invoke an extra query for the next page check' do
- arguments[:first] = 1
-
- subject.nodes
-
- count = ActiveRecord::QueryRecorder.new { subject.has_next_page }.count
- expect(count).to eq(0)
- end
-
- it_behaves_like 'a connection with collection methods'
-
- it_behaves_like 'a redactable connection' do
- let_it_be(:projects) { create_list(:project, 2) }
- let(:unwanted) { projects.second }
- end
-
- describe '#cursor_for' do
- let(:project) { create(:project) }
- let(:cursor) { connection.cursor_for(project) }
-
- it 'returns an encoded ID' do
- expect(decoded_cursor(cursor)).to eq('id' => project.id.to_s)
- end
-
- context 'when an order is specified' do
- let(:nodes) { Project.all.order(Gitlab::Pagination::Keyset::Order.build([column_order_id])) }
-
- it 'returns the encoded value of the order' do
- expect(decoded_cursor(cursor)).to include('id' => project.id.to_s)
- end
- end
-
- context 'when multiple orders are specified' do
- let(:nodes) { Project.all.order(Gitlab::Pagination::Keyset::Order.build([column_order_updated_at, column_order_created_at, column_order_id])) }
-
- it 'returns the encoded value of the order' do
- expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.to_s(:inspect))
- end
- end
- end
-
- describe '#sliced_nodes' do
- let(:projects) { create_list(:project, 4) }
-
- context 'when before is passed' do
- let(:arguments) { { before: encoded_cursor(projects[1]) } }
-
- it 'only returns the project before the selected one' do
- expect(subject.sliced_nodes).to contain_exactly(projects.first)
- end
-
- context 'when the sort order is descending' do
- let(:nodes) { Project.all.order(Gitlab::Pagination::Keyset::Order.build([column_order_id_desc])) }
-
- it 'returns the correct nodes' do
- expect(subject.sliced_nodes).to contain_exactly(*projects[2..])
- end
- end
- end
-
- context 'when after is passed' do
- let(:arguments) { { after: encoded_cursor(projects[1]) } }
-
- it 'only returns the project before the selected one' do
- expect(subject.sliced_nodes).to contain_exactly(*projects[2..])
- end
-
- context 'when the sort order is descending' do
- let(:nodes) { Project.all.order(Gitlab::Pagination::Keyset::Order.build([column_order_id_desc])) }
-
- it 'returns the correct nodes' do
- expect(subject.sliced_nodes).to contain_exactly(projects.first)
- end
- end
- end
-
- context 'when both before and after are passed' do
- let(:arguments) do
- {
- after: encoded_cursor(projects[1]),
- before: encoded_cursor(projects[3])
- }
- end
-
- it 'returns the expected set' do
- expect(subject.sliced_nodes).to contain_exactly(projects[2])
- end
- end
-
- shared_examples 'nodes are in ascending order' do
- context 'when no cursor is passed' do
- let(:arguments) { {} }
-
- it 'returns projects in ascending order' do
- expect(subject.sliced_nodes).to eq(ascending_nodes)
- end
- end
-
- context 'when before cursor value is not NULL' do
- let(:arguments) { { before: encoded_cursor(ascending_nodes[2]) } }
-
- it 'returns all projects before the cursor' do
- expect(subject.sliced_nodes).to eq(ascending_nodes.first(2))
- end
- end
-
- context 'when after cursor value is not NULL' do
- let(:arguments) { { after: encoded_cursor(ascending_nodes[1]) } }
-
- it 'returns all projects after the cursor' do
- expect(subject.sliced_nodes).to eq(ascending_nodes.last(3))
- end
- end
-
- context 'when before and after cursor' do
- let(:arguments) { { before: encoded_cursor(ascending_nodes.last), after: encoded_cursor(ascending_nodes.first) } }
-
- it 'returns all projects after the cursor' do
- expect(subject.sliced_nodes).to eq(ascending_nodes[1..3])
- end
- end
- end
-
- shared_examples 'nodes are in descending order' do
- context 'when no cursor is passed' do
- let(:arguments) { {} }
-
- it 'only returns projects in descending order' do
- expect(subject.sliced_nodes).to eq(descending_nodes)
- end
- end
-
- context 'when before cursor value is not NULL' do
- let(:arguments) { { before: encoded_cursor(descending_nodes[2]) } }
-
- it 'returns all projects before the cursor' do
- expect(subject.sliced_nodes).to eq(descending_nodes.first(2))
- end
- end
-
- context 'when after cursor value is not NULL' do
- let(:arguments) { { after: encoded_cursor(descending_nodes[1]) } }
-
- it 'returns all projects after the cursor' do
- expect(subject.sliced_nodes).to eq(descending_nodes.last(3))
- end
- end
-
- context 'when before and after cursor' do
- let(:arguments) { { before: encoded_cursor(descending_nodes.last), after: encoded_cursor(descending_nodes.first) } }
-
- it 'returns all projects after the cursor' do
- expect(subject.sliced_nodes).to eq(descending_nodes[1..3])
- end
- end
- end
-
- context 'when multiple orders with nil values are defined' do
- let_it_be(:project1) { create(:project, last_repository_check_at: 10.days.ago) } # Asc: project5 Desc: project3
- let_it_be(:project2) { create(:project, last_repository_check_at: nil) } # Asc: project1 Desc: project1
- let_it_be(:project3) { create(:project, last_repository_check_at: 5.days.ago) } # Asc: project3 Desc: project5
- let_it_be(:project4) { create(:project, last_repository_check_at: nil) } # Asc: project2 Desc: project2
- let_it_be(:project5) { create(:project, last_repository_check_at: 20.days.ago) } # Asc: project4 Desc: project4
-
- context 'when ascending' do
- let_it_be(:order) { Gitlab::Pagination::Keyset::Order.build([column_order_last_repo, column_order_id]) }
- let_it_be(:nodes) { Project.order(order) }
- let_it_be(:ascending_nodes) { [project5, project1, project3, project2, project4] }
-
- it_behaves_like 'nodes are in ascending order'
-
- context 'when before cursor value is NULL' do
- let(:arguments) { { before: encoded_cursor(project4) } }
-
- it 'returns all projects before the cursor' do
- expect(subject.sliced_nodes).to eq([project5, project1, project3, project2])
- end
- end
-
- context 'when after cursor value is NULL' do
- let(:arguments) { { after: encoded_cursor(project2) } }
-
- it 'returns all projects after the cursor' do
- expect(subject.sliced_nodes).to eq([project4])
- end
- end
- end
-
- context 'when descending' do
- let_it_be(:order) { Gitlab::Pagination::Keyset::Order.build([column_order_last_repo_desc, column_order_id]) }
- let_it_be(:nodes) { Project.order(order) }
- let_it_be(:descending_nodes) { [project3, project1, project5, project2, project4] }
-
- it_behaves_like 'nodes are in descending order'
-
- context 'when before cursor value is NULL' do
- let(:arguments) { { before: encoded_cursor(project4) } }
-
- it 'returns all projects before the cursor' do
- expect(subject.sliced_nodes).to eq([project3, project1, project5, project2])
- end
- end
-
- context 'when after cursor value is NULL' do
- let(:arguments) { { after: encoded_cursor(project2) } }
-
- it 'returns all projects after the cursor' do
- expect(subject.sliced_nodes).to eq([project4])
- end
- end
- end
- end
-
- context 'when ordering by similarity' do
- let_it_be(:project1) { create(:project, name: 'test') }
- let_it_be(:project2) { create(:project, name: 'testing') }
- let_it_be(:project3) { create(:project, name: 'tests') }
- let_it_be(:project4) { create(:project, name: 'testing stuff') }
- let_it_be(:project5) { create(:project, name: 'test') }
-
- let_it_be(:nodes) do
- # Note: sorted_by_similarity_desc scope internally supports the generic keyset order.
- Project.sorted_by_similarity_desc('test', include_in_select: true)
- end
-
- let_it_be(:descending_nodes) { nodes.to_a }
-
- it_behaves_like 'nodes are in descending order'
- end
-
- context 'when an invalid cursor is provided' do
- let(:arguments) { { before: Base64Bp.urlsafe_encode64('invalidcursor', padding: false) } }
-
- it 'raises an error' do
- expect { subject.sliced_nodes }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
- end
- end
- end
-
- describe '#nodes' do
- let_it_be(:all_nodes) { create_list(:project, 5) }
-
- let(:paged_nodes) { subject.nodes }
-
- it_behaves_like 'connection with paged nodes' do
- let(:paged_nodes_size) { 3 }
- end
-
- context 'when both are passed' do
- let(:arguments) { { first: 2, last: 2 } }
-
- it 'raises an error' do
- expect { paged_nodes }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
- end
- end
-
- context 'when primary key is not in original order' do
- let(:nodes) { Project.order(last_repository_check_at: :desc) }
-
- it 'is added to end' do
- sliced = subject.sliced_nodes
-
- order_sql = sliced.order_values.last.to_sql
-
- expect(order_sql).to end_with(Project.arel_table[:id].desc.to_sql)
- end
- end
-
- context 'when there is no primary key' do
- before do
- stub_const('NoPrimaryKey', Class.new(ActiveRecord::Base))
- NoPrimaryKey.class_eval do
- self.table_name = 'no_primary_key'
- self.primary_key = nil
- end
- end
-
- let(:nodes) { NoPrimaryKey.all }
-
- it 'raises an error' do
- expect(NoPrimaryKey.primary_key).to be_nil
- expect { subject.sliced_nodes }.to raise_error(ArgumentError, 'Relation must have a primary key')
- end
- end
- end
-
- describe '#has_previous_page and #has_next_page' do
- # using a list of 5 items with a max_page of 3
- let_it_be(:project_list) { create_list(:project, 5) }
- let_it_be(:nodes) { Project.order(Gitlab::Pagination::Keyset::Order.build([column_order_id])) }
-
- context 'when default query' do
- let(:arguments) { {} }
-
- it 'has no previous, but a next' do
- expect(subject.has_previous_page).to be_falsey
- expect(subject.has_next_page).to be_truthy
- end
- end
-
- context 'when before is first item' do
- let(:arguments) { { before: encoded_cursor(project_list.first) } }
-
- it 'has no previous, but a next' do
- expect(subject.has_previous_page).to be_falsey
- expect(subject.has_next_page).to be_truthy
- end
- end
-
- describe 'using `before`' do
- context 'when before is the last item' do
- let(:arguments) { { before: encoded_cursor(project_list.last) } }
-
- it 'has no previous, but a next' do
- expect(subject.has_previous_page).to be_falsey
- expect(subject.has_next_page).to be_truthy
- end
- end
-
- context 'when before and last specified' do
- let(:arguments) { { before: encoded_cursor(project_list.last), last: 2 } }
-
- it 'has a previous and a next' do
- expect(subject.has_previous_page).to be_truthy
- expect(subject.has_next_page).to be_truthy
- end
- end
-
- context 'when before and last does request all remaining nodes' do
- let(:arguments) { { before: encoded_cursor(project_list[1]), last: 3 } }
-
- it 'has a previous and a next' do
- expect(subject.has_previous_page).to be_falsey
- expect(subject.has_next_page).to be_truthy
- expect(subject.nodes).to eq [project_list[0]]
- end
- end
- end
-
- describe 'using `after`' do
- context 'when after is the first item' do
- let(:arguments) { { after: encoded_cursor(project_list.first) } }
-
- it 'has a previous, and a next' do
- expect(subject.has_previous_page).to be_truthy
- expect(subject.has_next_page).to be_truthy
- end
- end
-
- context 'when after and first specified' do
- let(:arguments) { { after: encoded_cursor(project_list.first), first: 2 } }
-
- it 'has a previous and a next' do
- expect(subject.has_previous_page).to be_truthy
- expect(subject.has_next_page).to be_truthy
- end
- end
-
- context 'when before and last does request all remaining nodes' do
- let(:arguments) { { after: encoded_cursor(project_list[2]), last: 3 } }
-
- it 'has a previous but no next' do
- expect(subject.has_previous_page).to be_truthy
- expect(subject.has_next_page).to be_falsey
- end
- end
- end
- end
- end
end
diff --git a/spec/lib/gitlab/http_connection_adapter_spec.rb b/spec/lib/gitlab/http_connection_adapter_spec.rb
index a241a4b6490..5e2c6be8993 100644
--- a/spec/lib/gitlab/http_connection_adapter_spec.rb
+++ b/spec/lib/gitlab/http_connection_adapter_spec.rb
@@ -124,5 +124,16 @@ RSpec.describe Gitlab::HTTPConnectionAdapter do
expect(connection.port).to eq(443)
end
end
+
+ context 'when URL scheme is not HTTP/HTTPS' do
+ let(:uri) { URI('ssh://example.org') }
+
+ it 'raises error' do
+ expect { subject }.to raise_error(
+ Gitlab::HTTP::BlockedUrlError,
+ "URL 'ssh://example.org' is blocked: Only allowed schemes are http, https"
+ )
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/i18n_spec.rb b/spec/lib/gitlab/i18n_spec.rb
index aae4a13bd73..b752d89bf0d 100644
--- a/spec/lib/gitlab/i18n_spec.rb
+++ b/spec/lib/gitlab/i18n_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe Gitlab::I18n do
describe '.selectable_locales' do
include StubLanguagesTranslationPercentage
- it 'does not return languages with low translation levels' do
+ it 'does not return languages with default translation levels 60%' do
stub_languages_translation_percentage(pt_BR: 0, en: 100, es: 65)
expect(described_class.selectable_locales).to eq({
@@ -16,6 +16,12 @@ RSpec.describe Gitlab::I18n do
'es' => 'Spanish - español'
})
end
+
+ it 'does not return languages with less than 100% translation levels' do
+ stub_languages_translation_percentage(pt_BR: 0, en: 100, es: 65)
+
+ expect(described_class.selectable_locales(100)).to eq({ 'en' => 'English' })
+ end
end
describe '.locale=' do
diff --git a/spec/lib/gitlab/import/merge_request_helpers_spec.rb b/spec/lib/gitlab/import/merge_request_helpers_spec.rb
index f858ab934bb..9626b7b893f 100644
--- a/spec/lib/gitlab/import/merge_request_helpers_spec.rb
+++ b/spec/lib/gitlab/import/merge_request_helpers_spec.rb
@@ -37,11 +37,8 @@ RSpec.describe Gitlab::Import::MergeRequestHelpers, type: :helper do
attributes.merge(iid: iid, source_branch: iid.to_s))
end
- # does ensure that we only load object twice
- # 1. by #insert_and_return_id
- # 2. by project.merge_requests.find
- expect_any_instance_of(MergeRequest).to receive(:attributes)
- .twice.times.and_call_original
+ # ensures that we only load object once by project.merge_requests.find
+ expect(MergeRequest).to receive(:allocate).once.and_call_original
expect(subject.first).not_to be_nil
expect(subject.second).to eq(false)
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index e9dde1c6180..b34399d20f1 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -157,6 +157,7 @@ merge_requests:
- author
- assignee
- reviewers
+- reviewed_by_users
- updated_by
- milestone
- iteration
@@ -408,6 +409,20 @@ project:
- boards
- last_event
- integrations
+- push_hooks_integrations
+- tag_push_hooks_integrations
+- issue_hooks_integrations
+- confidential_issue_hooks_integrations
+- merge_request_hooks_integrations
+- note_hooks_integrations
+- confidential_note_hooks_integrations
+- job_hooks_integrations
+- archive_trace_hooks_integrations
+- pipeline_hooks_integrations
+- wiki_page_hooks_integrations
+- deployment_hooks_integrations
+- alert_hooks_integrations
+- vulnerability_hooks_integrations
- campfire_integration
- confluence_integration
- datadog_integration
@@ -423,7 +438,6 @@ project:
- packagist_integration
- pivotaltracker_integration
- prometheus_integration
-- flowdock_integration
- assembla_integration
- asana_integration
- slack_integration
@@ -649,6 +663,7 @@ project:
- project_callouts
- pipeline_metadata
- disable_download_button
+- dependency_list_exports
award_emoji:
- awardable
- user
diff --git a/spec/lib/gitlab/import_export/group/legacy_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/group/legacy_tree_restorer_spec.rb
index dbd6cb243f6..a5b03974bc0 100644
--- a/spec/lib/gitlab/import_export/group/legacy_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/group/legacy_tree_restorer_spec.rb
@@ -77,7 +77,7 @@ RSpec.describe Gitlab::ImportExport::Group::LegacyTreeRestorer do
let(:group) { create(:group) }
let(:shared) { Gitlab::ImportExport::Shared.new(group) }
let(:group_tree_restorer) { described_class.new(user: importer_user, shared: shared, group: group, group_hash: nil) }
- let(:group_json) { Gitlab::Json.parse(IO.read(File.join(shared.export_path, 'group.json'))) }
+ let(:group_json) { Gitlab::Json.parse(File.read(File.join(shared.export_path, 'group.json'))) }
shared_examples 'excluded attributes' do
excluded_attributes = %w[
diff --git a/spec/lib/gitlab/import_export/group/legacy_tree_saver_spec.rb b/spec/lib/gitlab/import_export/group/legacy_tree_saver_spec.rb
index 31d647f883a..f5a4fc79c90 100644
--- a/spec/lib/gitlab/import_export/group/legacy_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/group/legacy_tree_saver_spec.rb
@@ -154,6 +154,6 @@ RSpec.describe Gitlab::ImportExport::Group::LegacyTreeSaver do
end
def group_json(filename)
- ::JSON.parse(IO.read(filename))
+ ::JSON.parse(File.read(filename))
end
end
diff --git a/spec/lib/gitlab/import_export/group/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/group/tree_restorer_spec.rb
index 1444897e136..aa30e24296e 100644
--- a/spec/lib/gitlab/import_export/group/tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/group/tree_restorer_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::ImportExport::Group::TreeRestorer do
+RSpec.describe Gitlab::ImportExport::Group::TreeRestorer, feature: :subgroups do
include ImportExport::CommonUtil
shared_examples 'group restoration' do
@@ -112,7 +112,7 @@ RSpec.describe Gitlab::ImportExport::Group::TreeRestorer do
let(:shared) { Gitlab::ImportExport::Shared.new(group) }
let(:group_tree_restorer) { described_class.new(user: importer_user, shared: shared, group: group) }
let(:exported_file) { File.join(shared.export_path, 'tree/groups/4352.json') }
- let(:group_json) { Gitlab::Json.parse(IO.read(exported_file)) }
+ let(:group_json) { Gitlab::Json.parse(File.read(exported_file)) }
shared_examples 'excluded attributes' do
excluded_attributes = %w[
diff --git a/spec/lib/gitlab/import_export/import_test_coverage_spec.rb b/spec/lib/gitlab/import_export/import_test_coverage_spec.rb
index b1f5574fba1..d8a4230e5da 100644
--- a/spec/lib/gitlab/import_export/import_test_coverage_spec.rb
+++ b/spec/lib/gitlab/import_export/import_test_coverage_spec.rb
@@ -86,7 +86,7 @@ RSpec.describe 'Test coverage of the Project Import' do
end
def relations_from_json(json_file)
- json = Gitlab::Json.parse(IO.read(json_file))
+ json = Gitlab::Json.parse(File.read(json_file))
[].tap { |res| gather_relations({ project: json }, res, []) }
.map { |relation_names| relation_names.join('.') }
diff --git a/spec/lib/gitlab/import_export/json/legacy_writer_spec.rb b/spec/lib/gitlab/import_export/json/legacy_writer_spec.rb
index ed4368ba802..e8ecd98b1e1 100644
--- a/spec/lib/gitlab/import_export/json/legacy_writer_spec.rb
+++ b/spec/lib/gitlab/import_export/json/legacy_writer_spec.rb
@@ -96,6 +96,6 @@ RSpec.describe Gitlab::ImportExport::Json::LegacyWriter do
def subject_json
subject.close
- ::JSON.parse(IO.read(subject.path))
+ ::JSON.parse(File.read(subject.path))
end
end
diff --git a/spec/lib/gitlab/import_export/lfs_saver_spec.rb b/spec/lib/gitlab/import_export/lfs_saver_spec.rb
index aa456736f78..5b6f50025ff 100644
--- a/spec/lib/gitlab/import_export/lfs_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/lfs_saver_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe Gitlab::ImportExport::LfsSaver do
let(:lfs_json_file) { File.join(shared.export_path, Gitlab::ImportExport.lfs_objects_filename) }
def lfs_json
- Gitlab::Json.parse(IO.read(lfs_json_file))
+ Gitlab::Json.parse(File.read(lfs_json_file))
end
before do
diff --git a/spec/lib/gitlab/import_export/model_configuration_spec.rb b/spec/lib/gitlab/import_export/model_configuration_spec.rb
index 34591122a97..4f01f470ce7 100644
--- a/spec/lib/gitlab/import_export/model_configuration_spec.rb
+++ b/spec/lib/gitlab/import_export/model_configuration_spec.rb
@@ -23,8 +23,8 @@ RSpec.describe 'Import/Export model configuration' do
# List of current models between models, in the format of
# {model: [model_2, model3], ...}
def setup_models
- model_names.each_with_object({}) do |model_name, hash|
- hash[model_name] = associations_for(relation_class_for_name(model_name)) - ['project']
+ model_names.index_with do |model_name|
+ associations_for(relation_class_for_name(model_name)) - ['project']
end
end
diff --git a/spec/lib/gitlab/import_export/project/exported_relations_merger_spec.rb b/spec/lib/gitlab/import_export/project/exported_relations_merger_spec.rb
index a781139acab..d70e89c6856 100644
--- a/spec/lib/gitlab/import_export/project/exported_relations_merger_spec.rb
+++ b/spec/lib/gitlab/import_export/project/exported_relations_merger_spec.rb
@@ -8,17 +8,17 @@ RSpec.describe Gitlab::ImportExport::Project::ExportedRelationsMerger do
let(:shared) { Gitlab::ImportExport::Shared.new(export_job.project) }
before do
- create(:project_relation_export_upload,
+ create(:relation_export_upload,
relation_export: create(:project_relation_export, relation: 'project', project_export_job: export_job),
export_file: fixture_file_upload("spec/fixtures/gitlab/import_export/project.tar.gz")
)
- create(:project_relation_export_upload,
+ create(:relation_export_upload,
relation_export: create(:project_relation_export, relation: 'labels', project_export_job: export_job),
export_file: fixture_file_upload("spec/fixtures/gitlab/import_export/labels.tar.gz")
)
- create(:project_relation_export_upload,
+ create(:relation_export_upload,
relation_export: create(:project_relation_export, relation: 'uploads', project_export_job: export_job),
export_file: fixture_file_upload("spec/fixtures/gitlab/import_export/uploads.tar.gz")
)
diff --git a/spec/lib/gitlab/import_export/project/relation_saver_spec.rb b/spec/lib/gitlab/import_export/project/relation_saver_spec.rb
index 0467b63e918..5032dd864bb 100644
--- a/spec/lib/gitlab/import_export/project/relation_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project/relation_saver_spec.rb
@@ -111,7 +111,7 @@ RSpec.describe Gitlab::ImportExport::Project::RelationSaver do
end
def read_json(path)
- Gitlab::Json.parse(IO.read(path))
+ Gitlab::Json.parse(File.read(path))
end
def read_ndjson(path)
diff --git a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
index b753746cd8c..2699dc10b18 100644
--- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
@@ -768,7 +768,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do
it 'overrides project feature access levels' do
access_level_keys = ProjectFeature.available_features.map { |feature| ProjectFeature.access_level_attribute(feature) }
- disabled_access_levels = access_level_keys.to_h { |item| [item, 'disabled'] }
+ disabled_access_levels = access_level_keys.index_with { 'disabled' }
project.create_import_data(data: { override_params: disabled_access_levels })
diff --git a/spec/lib/gitlab/import_export/repo_restorer_spec.rb b/spec/lib/gitlab/import_export/repo_restorer_spec.rb
index 727ca4f630b..3da7af7509e 100644
--- a/spec/lib/gitlab/import_export/repo_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/repo_restorer_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe Gitlab::ImportExport::RepoRestorer do
Gitlab::Shell.new.remove_repository(project.repository_storage, project.disk_path)
end
- it 'restores the repo successfully' do
+ it 'restores the repo successfully', :aggregate_failures do
expect(project.repository.exists?).to be false
expect(subject.restore).to be_truthy
@@ -42,7 +42,7 @@ RSpec.describe Gitlab::ImportExport::RepoRestorer do
end
context 'when the repository already exists' do
- it 'deletes the existing repository before importing' do
+ it 'deletes the existing repository before importing', :aggregate_failures do
allow(project.repository).to receive(:exists?).and_return(true)
allow(project.repository).to receive(:disk_path).and_return('repository_path')
@@ -69,7 +69,7 @@ RSpec.describe Gitlab::ImportExport::RepoRestorer do
Gitlab::Shell.new.remove_repository(project.wiki.repository_storage, project.wiki.disk_path)
end
- it 'restores the wiki repo successfully' do
+ it 'restores the wiki repo successfully', :aggregate_failures do
expect(project.wiki_repository_exists?).to be false
subject.restore
@@ -83,10 +83,21 @@ RSpec.describe Gitlab::ImportExport::RepoRestorer do
let(:bundler) { Gitlab::ImportExport::WikiRepoSaver.new(exportable: project_without_wiki, shared: shared) }
- it 'does not creates an empty wiki' do
+ it 'does not creates an empty wiki', :aggregate_failures do
expect(subject.restore).to be true
expect(project.wiki_repository_exists?).to be false
end
end
+
+ context 'when wiki already exists' do
+ subject do
+ described_class.new(path_to_bundle: bundle_path, shared: shared, importable: ProjectWiki.new(project_with_repo))
+ end
+
+ it 'does not cause an error when restoring', :aggregate_failures do
+ expect(subject.restore).to be true
+ expect(shared.errors).to be_empty
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/import_export/saver_spec.rb b/spec/lib/gitlab/import_export/saver_spec.rb
index f5eed81f73c..a34e68ecd19 100644
--- a/spec/lib/gitlab/import_export/saver_spec.rb
+++ b/spec/lib/gitlab/import_export/saver_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe Gitlab::ImportExport::Saver do
subject.save # rubocop:disable Rails/SaveBang
expect(ImportExportUpload.find_by(project: project).export_file.url)
- .to match(%r[\/uploads\/-\/system\/import_export_upload\/export_file.*])
+ .to match(%r[/uploads/-/system/import_export_upload/export_file.*])
end
it 'logs metrics after saving' do
diff --git a/spec/lib/gitlab/import_sources_spec.rb b/spec/lib/gitlab/import_sources_spec.rb
index 41ffcece221..393e0a9be10 100644
--- a/spec/lib/gitlab/import_sources_spec.rb
+++ b/spec/lib/gitlab/import_sources_spec.rb
@@ -11,7 +11,6 @@ RSpec.describe Gitlab::ImportSources do
'Bitbucket Cloud' => 'bitbucket',
'Bitbucket Server' => 'bitbucket_server',
'GitLab.com' => 'gitlab',
- 'Google Code' => 'google_code',
'FogBugz' => 'fogbugz',
'Repository by URL' => 'git',
'GitLab export' => 'gitlab_project',
@@ -32,7 +31,6 @@ RSpec.describe Gitlab::ImportSources do
bitbucket
bitbucket_server
gitlab
- google_code
fogbugz
git
gitlab_project
@@ -69,7 +67,6 @@ RSpec.describe Gitlab::ImportSources do
'bitbucket' => Gitlab::BitbucketImport::Importer,
'bitbucket_server' => Gitlab::BitbucketServerImport::Importer,
'gitlab' => Gitlab::GitlabImport::Importer,
- 'google_code' => nil,
'fogbugz' => Gitlab::FogbugzImport::Importer,
'git' => nil,
'gitlab_project' => Gitlab::ImportExport::Importer,
@@ -91,7 +88,6 @@ RSpec.describe Gitlab::ImportSources do
'bitbucket' => 'Bitbucket Cloud',
'bitbucket_server' => 'Bitbucket Server',
'gitlab' => 'GitLab.com',
- 'google_code' => 'Google Code',
'fogbugz' => 'FogBugz',
'git' => 'Repository by URL',
'gitlab_project' => 'GitLab export',
diff --git a/spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb b/spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb
index c5288b9afbc..e2c67c68eb7 100644
--- a/spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb
+++ b/spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb
@@ -10,8 +10,8 @@ RSpec.describe Gitlab::IncidentManagement::PagerDuty::IncidentIssueDescription d
[{ 'summary' => 'Laura Haley', 'url' => 'https://webdemo.pagerduty.com/users/P553OPV' }]
end
- let(:impacted_services) do
- [{ 'summary' => 'Production XDB Cluster', 'url' => 'https://webdemo.pagerduty.com/services/PN49J75' }]
+ let(:impacted_service) do
+ { 'summary' => 'Production XDB Cluster', 'url' => 'https://webdemo.pagerduty.com/services/PN49J75' }
end
let(:incident_payload) do
@@ -24,7 +24,7 @@ RSpec.describe Gitlab::IncidentManagement::PagerDuty::IncidentIssueDescription d
'urgency' => 'high',
'incident_key' => 'SOME-KEY',
'assignees' => assignees,
- 'impacted_services' => impacted_services
+ 'impacted_service' => impacted_service
}
end
@@ -40,7 +40,7 @@ RSpec.describe Gitlab::IncidentManagement::PagerDuty::IncidentIssueDescription d
**Incident key:** SOME-KEY#{markdown_line_break}
**Created at:** 26 September 2017, 3:14PM (UTC)#{markdown_line_break}
**Assignees:** [Laura Haley](https://webdemo.pagerduty.com/users/P553OPV)#{markdown_line_break}
- **Impacted services:** [Production XDB Cluster](https://webdemo.pagerduty.com/services/PN49J75)
+ **Impacted service:** [Production XDB Cluster](https://webdemo.pagerduty.com/services/PN49J75)
MARKDOWN
)
end
@@ -78,18 +78,15 @@ RSpec.describe Gitlab::IncidentManagement::PagerDuty::IncidentIssueDescription d
end
end
- context 'when there are several impacted services' do
- let(:impacted_services) do
- [
- { 'summary' => 'XDB Cluster', 'url' => 'https://xdb.pagerduty.com' },
- { 'summary' => 'BRB Cluster', 'url' => 'https://brb.pagerduty.com' }
- ]
+ context 'when there is an impacted service' do
+ let(:impacted_service) do
+ { 'summary' => 'XDB Cluster', 'url' => 'https://xdb.pagerduty.com' }
end
- it 'impacted services is a list of links' do
+ it 'impacted service is a single link' do
expect(to_s).to include(
<<~MARKDOWN.chomp
- **Impacted services:** [XDB Cluster](https://xdb.pagerduty.com), [BRB Cluster](https://brb.pagerduty.com)
+ **Impacted service:** [XDB Cluster](https://xdb.pagerduty.com)
MARKDOWN
)
end
diff --git a/spec/lib/gitlab/instrumentation/redis_base_spec.rb b/spec/lib/gitlab/instrumentation/redis_base_spec.rb
index f9dd0c94c97..656e6ffba05 100644
--- a/spec/lib/gitlab/instrumentation/redis_base_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_base_spec.rb
@@ -1,8 +1,10 @@
# frozen_string_literal: true
require 'spec_helper'
+require 'rspec-parameterized'
RSpec.describe Gitlab::Instrumentation::RedisBase, :request_store do
+ using RSpec::Parameterized::TableSyntax
let(:instrumentation_class_a) do
stub_const('InstanceA', Class.new(described_class))
end
@@ -88,6 +90,44 @@ RSpec.describe Gitlab::Instrumentation::RedisBase, :request_store do
end
end
+ describe '.increment_cross_slot_request_count' do
+ context 'storage key overlapping' do
+ it 'keys do not overlap across storages' do
+ 3.times { instrumentation_class_a.increment_cross_slot_request_count }
+ 2.times { instrumentation_class_b.increment_cross_slot_request_count }
+
+ expect(instrumentation_class_a.get_cross_slot_request_count).to eq(3)
+ expect(instrumentation_class_b.get_cross_slot_request_count).to eq(2)
+ end
+
+ it 'increments by the given amount' do
+ instrumentation_class_a.increment_cross_slot_request_count(2)
+ instrumentation_class_a.increment_cross_slot_request_count(3)
+
+ expect(instrumentation_class_a.get_cross_slot_request_count).to eq(5)
+ end
+ end
+ end
+
+ describe '.increment_allowed_cross_slot_request_count' do
+ context 'storage key overlapping' do
+ it 'keys do not overlap across storages' do
+ 3.times { instrumentation_class_a.increment_allowed_cross_slot_request_count }
+ 2.times { instrumentation_class_b.increment_allowed_cross_slot_request_count }
+
+ expect(instrumentation_class_a.get_allowed_cross_slot_request_count).to eq(3)
+ expect(instrumentation_class_b.get_allowed_cross_slot_request_count).to eq(2)
+ end
+
+ it 'increments by the given amount' do
+ instrumentation_class_a.increment_allowed_cross_slot_request_count(2)
+ instrumentation_class_a.increment_allowed_cross_slot_request_count(3)
+
+ expect(instrumentation_class_a.get_allowed_cross_slot_request_count).to eq(5)
+ end
+ end
+ end
+
describe '.increment_read_bytes' do
context 'storage key overlapping' do
it 'keys do not overlap across storages' do
@@ -130,4 +170,44 @@ RSpec.describe Gitlab::Instrumentation::RedisBase, :request_store do
end
end
end
+
+ describe '.redis_cluster_validate!' do
+ let(:args) { [[:mget, 'foo', 'bar']] }
+
+ before do
+ instrumentation_class_a.enable_redis_cluster_validation
+ end
+
+ context 'Rails environments' do
+ where(:env, :allowed, :should_raise) do
+ 'production' | false | false
+ 'production' | true | false
+ 'staging' | false | false
+ 'staging' | true | false
+ 'development' | true | false
+ 'development' | false | true
+ 'test' | true | false
+ 'test' | false | true
+ end
+
+ with_them do
+ it do
+ stub_rails_env(env)
+
+ validation = -> { instrumentation_class_a.redis_cluster_validate!(args) }
+ under_test = if allowed
+ -> { Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands(&validation) }
+ else
+ validation
+ end
+
+ if should_raise
+ expect(&under_test).to raise_error(::Gitlab::Instrumentation::RedisClusterValidator::CrossSlotError)
+ else
+ expect(&under_test).not_to raise_error
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/instrumentation/redis_cluster_validator_spec.rb b/spec/lib/gitlab/instrumentation/redis_cluster_validator_spec.rb
index 58c75bff9dd..892b8e69124 100644
--- a/spec/lib/gitlab/instrumentation/redis_cluster_validator_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_cluster_validator_spec.rb
@@ -7,107 +7,102 @@ require 'rspec-parameterized'
RSpec.describe Gitlab::Instrumentation::RedisClusterValidator do
include RailsHelpers
- describe '.validate!' do
+ describe '.validate' do
using RSpec::Parameterized::TableSyntax
- context 'Rails environments' do
- where(:env, :should_raise) do
- 'production' | false
- 'staging' | false
- 'development' | true
- 'test' | true
- end
-
- with_them do
- it do
- stub_rails_env(env)
-
- args = [[:mget, 'foo', 'bar']]
-
- if should_raise
- expect { described_class.validate!(args) }
- .to raise_error(described_class::CrossSlotError)
- else
- expect { described_class.validate!(args) }.not_to raise_error
- end
- end
- end
- end
-
- where(:command, :arguments, :should_raise) do
- :rename | %w(foo bar) | true
- :RENAME | %w(foo bar) | true
- 'rename' | %w(foo bar) | true
- 'RENAME' | %w(foo bar) | true
- :rename | %w(iaa ahy) | false # 'iaa' and 'ahy' hash to the same slot
- :rename | %w({foo}:1 {foo}:2) | false
- :rename | %w(foo foo bar) | false # This is not a valid command but should not raise here
- :mget | %w(foo bar) | true
- :mget | %w(foo foo bar) | true
- :mget | %w(foo foo) | false
- :blpop | %w(foo bar 1) | true
- :blpop | %w(foo foo 1) | false
- :mset | %w(foo a bar a) | true
- :mset | %w(foo a foo a) | false
- :del | %w(foo bar) | true
- :del | [%w(foo bar)] | true # Arguments can be a nested array
- :del | %w(foo foo) | false
- :hset | %w(foo bar) | false # Not a multi-key command
- :mget | [] | false # This is invalid, but not because it's a cross-slot command
+ where(:command, :arguments, :keys, :is_valid) do
+ :rename | %w(foo bar) | 2 | false
+ :RENAME | %w(foo bar) | 2 | false
+ 'rename' | %w(foo bar) | 2 | false
+ 'RENAME' | %w(foo bar) | 2 | false
+ :rename | %w(iaa ahy) | 2 | true # 'iaa' and 'ahy' hash to the same slot
+ :rename | %w({foo}:1 {foo}:2) | 2 | true
+ :rename | %w(foo foo bar) | 2 | true # This is not a valid command but should not raise here
+ :mget | %w(foo bar) | 2 | false
+ :mget | %w(foo foo bar) | 3 | false
+ :mget | %w(foo foo) | 2 | true
+ :blpop | %w(foo bar 1) | 2 | false
+ :blpop | %w(foo foo 1) | 2 | true
+ :mset | %w(foo a bar a) | 2 | false
+ :mset | %w(foo a foo a) | 2 | true
+ :del | %w(foo bar) | 2 | false
+ :del | [%w(foo bar)] | 2 | false # Arguments can be a nested array
+ :del | %w(foo foo) | 2 | true
+ :hset | %w(foo bar) | 1 | nil # Single key write
+ :get | %w(foo) | 1 | nil # Single key read
+ :mget | [] | 0 | true # This is invalid, but not because it's a cross-slot command
end
with_them do
it do
args = [[command] + arguments]
-
- if should_raise
- expect { described_class.validate!(args) }
- .to raise_error(described_class::CrossSlotError)
+ if is_valid.nil?
+ expect(described_class.validate(args)).to eq(nil)
else
- expect { described_class.validate!(args) }.not_to raise_error
+ expect(described_class.validate(args)[:valid]).to eq(is_valid)
+ expect(described_class.validate(args)[:allowed]).to eq(false)
+ expect(described_class.validate(args)[:command_name]).to eq(command.to_s.upcase)
+ expect(described_class.validate(args)[:key_count]).to eq(keys)
end
end
end
- where(:arguments, :should_raise) do
- [[:get, "foo"], [:get, "bar"]] | true
- [[:get, "foo"], [:mget, "foo", "bar"]] | true # mix of single-key and multi-key cmds
- [[:get, "{foo}:name"], [:get, "{foo}:profile"]] | false
- [[:del, "foo"], [:del, "bar"]] | true
- [] | false # pipeline or transaction opened and closed without ops
+ where(:arguments, :should_raise, :output) do
+ [
+ [
+ [[:get, "foo"], [:get, "bar"]],
+ true,
+ { valid: false, key_count: 2, command_name: 'PIPELINE/MULTI', allowed: false }
+ ],
+ [
+ [[:get, "foo"], [:mget, "foo", "bar"]],
+ true,
+ { valid: false, key_count: 3, command_name: 'PIPELINE/MULTI', allowed: false }
+ ],
+ [
+ [[:get, "{foo}:name"], [:get, "{foo}:profile"]],
+ false,
+ { valid: true, key_count: 2, command_name: 'PIPELINE/MULTI', allowed: false }
+ ],
+ [
+ [[:del, "foo"], [:del, "bar"]],
+ true,
+ { valid: false, key_count: 2, command_name: 'PIPELINE/MULTI', allowed: false }
+ ],
+ [
+ [],
+ false,
+ nil # pipeline or transaction opened and closed without ops
+ ]
+ ]
end
with_them do
it do
- if should_raise
- expect { described_class.validate!(arguments) }
- .to raise_error(described_class::CrossSlotError)
- else
- expect { described_class.validate!(arguments) }.not_to raise_error
- end
+ expect(described_class.validate(arguments)).to eq(output)
end
end
end
describe '.allow_cross_slot_commands' do
- it 'does not raise for invalid arguments' do
- expect do
+ it 'skips validation for allowed commands' do
+ expect(
described_class.allow_cross_slot_commands do
- described_class.validate!([[:mget, 'foo', 'bar']])
+ described_class.validate([[:mget, 'foo', 'bar']])
end
- end.not_to raise_error
+ ).to eq({ valid: true, key_count: 2, command_name: 'MGET', allowed: true })
end
it 'allows nested invocation' do
- expect do
+ expect(
described_class.allow_cross_slot_commands do
described_class.allow_cross_slot_commands do
- described_class.validate!([[:mget, 'foo', 'bar']])
+ described_class.validate([[:mget, 'foo', 'bar']])
end
- described_class.validate!([[:mget, 'foo', 'bar']])
+ described_class.validate([[:mget, 'foo', 'bar']])
end
- end.not_to raise_error
+ ).to eq({ valid: true, key_count: 2, command_name: 'MGET', allowed: true })
end
end
end
diff --git a/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb b/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
index 02c5dfb7521..187a6ff1739 100644
--- a/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
@@ -2,6 +2,7 @@
require 'spec_helper'
require 'rspec-parameterized'
+require 'support/helpers/rails_helpers'
RSpec.describe Gitlab::Instrumentation::RedisInterceptor, :clean_gitlab_redis_shared_state, :request_store do
using RSpec::Parameterized::TableSyntax
@@ -74,6 +75,47 @@ RSpec.describe Gitlab::Instrumentation::RedisInterceptor, :clean_gitlab_redis_sh
end
end.to raise_exception(Redis::CommandError)
end
+
+ context 'in production environment' do
+ before do
+ stub_rails_env('production') # to avoid raising CrossSlotError
+ end
+
+ it 'counts disallowed cross-slot requests' do
+ expect(instrumentation_class).to receive(:increment_cross_slot_request_count).and_call_original
+ expect(instrumentation_class).not_to receive(:increment_allowed_cross_slot_request_count).and_call_original
+
+ Gitlab::Redis::SharedState.with { |redis| redis.call(:mget, 'foo', 'bar') }
+ end
+
+ it 'does not count allowed cross-slot requests' do
+ expect(instrumentation_class).not_to receive(:increment_cross_slot_request_count).and_call_original
+ expect(instrumentation_class).to receive(:increment_allowed_cross_slot_request_count).and_call_original
+
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ Gitlab::Redis::SharedState.with { |redis| redis.call(:mget, 'foo', 'bar') }
+ end
+ end
+
+ it 'skips count for non-cross-slot requests' do
+ expect(instrumentation_class).not_to receive(:increment_cross_slot_request_count).and_call_original
+ expect(instrumentation_class).not_to receive(:increment_allowed_cross_slot_request_count).and_call_original
+
+ Gitlab::Redis::SharedState.with { |redis| redis.call(:mget, '{foo}bar', '{foo}baz') }
+ end
+ end
+
+ context 'without active RequestStore' do
+ before do
+ ::RequestStore.end!
+ end
+
+ it 'still runs cross-slot validation' do
+ expect do
+ Gitlab::Redis::SharedState.with { |redis| redis.mget('foo', 'bar') }
+ end.to raise_error(instance_of(Gitlab::Instrumentation::RedisClusterValidator::CrossSlotError))
+ end
+ end
end
describe 'latency' do
@@ -103,7 +145,7 @@ RSpec.describe Gitlab::Instrumentation::RedisInterceptor, :clean_gitlab_redis_sh
Gitlab::Redis::SharedState.with do |redis|
redis.pipelined do |pipeline|
- pipeline.call(:get, '{foobar}:buz')
+ pipeline.call(:get, '{foobar}buz')
pipeline.call(:get, '{foobar}baz')
end
end
@@ -123,37 +165,36 @@ RSpec.describe Gitlab::Instrumentation::RedisInterceptor, :clean_gitlab_redis_sh
end
describe 'commands not in the apdex' do
- where(:command) do
- [
- [%w[brpop foobar 0.01]],
- [%w[blpop foobar 0.01]],
- [%w[brpoplpush foobar bazqux 0.01]],
- [%w[bzpopmin foobar 0.01]],
- [%w[bzpopmax foobar 0.01]],
- [%w[xread block 1 streams mystream 0-0]],
- [%w[xreadgroup group mygroup myconsumer block 1 streams foobar 0-0]]
- ]
+ where(:setup, :command) do
+ [['rpush', 'foobar', 1]] | ['brpop', 'foobar', 0]
+ [['rpush', 'foobar', 1]] | ['blpop', 'foobar', 0]
+ [['rpush', '{abc}foobar', 1]] | ['brpoplpush', '{abc}foobar', '{abc}bazqux', 0]
+ [['rpush', '{abc}foobar', 1]] | ['brpoplpush', '{abc}foobar', '{abc}bazqux', 0]
+ [['zadd', 'foobar', 1, 'a']] | ['bzpopmin', 'foobar', 0]
+ [['zadd', 'foobar', 1, 'a']] | ['bzpopmax', 'foobar', 0]
+ [['xadd', 'mystream', 1, 'myfield', 'mydata']] | ['xread', 'block', 1, 'streams', 'mystream', '0-0']
+ [['xadd', 'foobar', 1, 'myfield', 'mydata'], ['xgroup', 'create', 'foobar', 'mygroup', 0]] | ['xreadgroup', 'group', 'mygroup', 'myconsumer', 'block', 1, 'streams', 'foobar', '0-0']
end
with_them do
it 'skips requests we do not want in the apdex' do
+ Gitlab::Redis::SharedState.with { |redis| setup.each { |cmd| redis.call(*cmd) } }
+
expect(instrumentation_class).not_to receive(:instance_observe_duration)
- begin
- Gitlab::Redis::SharedState.with { |redis| redis.call(*command) }
- rescue Gitlab::Instrumentation::RedisClusterValidator::CrossSlotError, ::Redis::CommandError
- end
+ Gitlab::Redis::SharedState.with { |redis| redis.call(*command) }
end
end
context 'with pipelined commands' do
- it 'skips requests that have blocking commands', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/373026' do
+ it 'skips requests that have blocking commands' do
expect(instrumentation_class).not_to receive(:instance_observe_duration)
Gitlab::Redis::SharedState.with do |redis|
redis.pipelined do |pipeline|
- pipeline.call(:get, 'foo')
- pipeline.call(:brpop, 'foobar', '0.01')
+ pipeline.call(:get, '{foobar}buz')
+ pipeline.call(:rpush, '{foobar}baz', 1)
+ pipeline.call(:brpop, '{foobar}baz', 0)
end
end
end
diff --git a/spec/lib/gitlab/instrumentation/redis_spec.rb b/spec/lib/gitlab/instrumentation/redis_spec.rb
index c01d06c97b0..3e02eadba4b 100644
--- a/spec/lib/gitlab/instrumentation/redis_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
+require 'support/helpers/rails_helpers'
RSpec.describe Gitlab::Instrumentation::Redis do
def stub_storages(method, value)
@@ -22,6 +23,8 @@ RSpec.describe Gitlab::Instrumentation::Redis do
end
it_behaves_like 'aggregation of redis storage data', :get_request_count
+ it_behaves_like 'aggregation of redis storage data', :get_cross_slot_request_count
+ it_behaves_like 'aggregation of redis storage data', :get_allowed_cross_slot_request_count
it_behaves_like 'aggregation of redis storage data', :query_time
it_behaves_like 'aggregation of redis storage data', :read_bytes
it_behaves_like 'aggregation of redis storage data', :write_bytes
@@ -35,20 +38,28 @@ RSpec.describe Gitlab::Instrumentation::Redis do
Gitlab::Redis::Cache.with { |redis| redis.info }
RequestStore.clear!
- Gitlab::Redis::Cache.with { |redis| redis.set('cache-test', 321) }
+ stub_rails_env('staging') # to avoid raising CrossSlotError
+ Gitlab::Redis::Cache.with { |redis| redis.mset('cache-test', 321, 'cache-test-2', 321) }
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ Gitlab::Redis::Cache.with { |redis| redis.mget('cache-test', 'cache-test-2') }
+ end
Gitlab::Redis::SharedState.with { |redis| redis.set('shared-state-test', 123) }
end
it 'returns payload filtering out zeroed values' do
expected_payload = {
# Aggregated results
- redis_calls: 2,
+ redis_calls: 3,
+ redis_cross_slot_calls: 1,
+ redis_allowed_cross_slot_calls: 1,
redis_duration_s: be >= 0,
redis_read_bytes: be >= 0,
redis_write_bytes: be >= 0,
# Cache results
- redis_cache_calls: 1,
+ redis_cache_calls: 2,
+ redis_cache_cross_slot_calls: 1,
+ redis_cache_allowed_cross_slot_calls: 1,
redis_cache_duration_s: be >= 0,
redis_cache_read_bytes: be >= 0,
redis_cache_write_bytes: be >= 0,
diff --git a/spec/lib/gitlab/instrumentation_helper_spec.rb b/spec/lib/gitlab/instrumentation_helper_spec.rb
index d5ff39767c4..7d78d25f18e 100644
--- a/spec/lib/gitlab/instrumentation_helper_spec.rb
+++ b/spec/lib/gitlab/instrumentation_helper_spec.rb
@@ -2,6 +2,7 @@
require 'spec_helper'
require 'rspec-parameterized'
+require 'support/helpers/rails_helpers'
RSpec.describe Gitlab::InstrumentationHelper do
using RSpec::Parameterized::TableSyntax
@@ -38,25 +39,33 @@ RSpec.describe Gitlab::InstrumentationHelper do
context 'when Redis calls are made' do
it 'adds Redis data and omits Gitaly data' do
- Gitlab::Redis::Cache.with { |redis| redis.set('test-cache', 123) }
+ stub_rails_env('staging') # to avoid raising CrossSlotError
+ Gitlab::Redis::Cache.with { |redis| redis.mset('test-cache', 123, 'test-cache2', 123) }
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ Gitlab::Redis::Cache.with { |redis| redis.mget('cache-test', 'cache-test-2') }
+ end
Gitlab::Redis::Queues.with { |redis| redis.set('test-queues', 321) }
subject
# Aggregated payload
- expect(payload[:redis_calls]).to eq(2)
+ expect(payload[:redis_calls]).to eq(3)
+ expect(payload[:redis_cross_slot_calls]).to eq(1)
+ expect(payload[:redis_allowed_cross_slot_calls]).to eq(1)
expect(payload[:redis_duration_s]).to be >= 0
expect(payload[:redis_read_bytes]).to be >= 0
expect(payload[:redis_write_bytes]).to be >= 0
- # Shared state payload
+ # Queue payload
expect(payload[:redis_queues_calls]).to eq(1)
expect(payload[:redis_queues_duration_s]).to be >= 0
expect(payload[:redis_queues_read_bytes]).to be >= 0
expect(payload[:redis_queues_write_bytes]).to be >= 0
# Cache payload
- expect(payload[:redis_cache_calls]).to eq(1)
+ expect(payload[:redis_cache_calls]).to eq(2)
+ expect(payload[:redis_cache_cross_slot_calls]).to eq(1)
+ expect(payload[:redis_cache_allowed_cross_slot_calls]).to eq(1)
expect(payload[:redis_cache_duration_s]).to be >= 0
expect(payload[:redis_cache_read_bytes]).to be >= 0
expect(payload[:redis_cache_write_bytes]).to be >= 0
@@ -67,6 +76,26 @@ RSpec.describe Gitlab::InstrumentationHelper do
end
end
+ context 'when LDAP requests are made' do
+ let(:provider) { 'ldapmain' }
+ let(:adapter) { Gitlab::Auth::Ldap::Adapter.new(provider) }
+ let(:conn) { instance_double(Net::LDAP::Connection, search: search) }
+ let(:search) { double(:search, result_code: 200) } # rubocop: disable RSpec/VerifiedDoubles
+
+ it 'adds LDAP data' do
+ allow_next_instance_of(Net::LDAP) do |net_ldap|
+ allow(net_ldap).to receive(:use_connection).and_yield(conn)
+ end
+
+ adapter.users('uid', 'foo')
+ subject
+
+ # Query count should be 2, as it will call `open` then `search`
+ expect(payload[:net_ldap_count]).to eq(2)
+ expect(payload[:net_ldap_duration_s]).to be >= 0
+ end
+ end
+
context 'when the request matched a Rack::Attack safelist' do
it 'logs the safelist name' do
Gitlab::Instrumentation::Throttle.safelist = 'foobar'
@@ -122,7 +151,7 @@ RSpec.describe Gitlab::InstrumentationHelper do
include MemoryInstrumentationHelper
before do
- skip_memory_instrumentation!
+ verify_memory_instrumentation_available!
end
it 'logs memory usage metrics' do
diff --git a/spec/lib/gitlab/kubernetes/kube_client_spec.rb b/spec/lib/gitlab/kubernetes/kube_client_spec.rb
index 8abd041fd4e..2ead188dc93 100644
--- a/spec/lib/gitlab/kubernetes/kube_client_spec.rb
+++ b/spec/lib/gitlab/kubernetes/kube_client_spec.rb
@@ -132,6 +132,14 @@ RSpec.describe Gitlab::Kubernetes::KubeClient do
it_behaves_like 'local address'
end
+ context 'when a non HTTP/HTTPS URL is provided' do
+ let(:api_url) { 'ssh://192.168.1.2' }
+
+ it 'raises an error' do
+ expect { client }.to raise_error(Gitlab::UrlBlocker::BlockedUrlError)
+ end
+ end
+
it 'falls back to default options, but allows overriding' do
client = described_class.new(api_url)
defaults = Gitlab::Kubernetes::KubeClient::DEFAULT_KUBECLIENT_OPTIONS
@@ -149,7 +157,7 @@ RSpec.describe Gitlab::Kubernetes::KubeClient do
it_behaves_like 'a Kubeclient'
it 'has the core API endpoint' do
- expect(subject.api_endpoint.to_s).to match(%r{\/api\Z})
+ expect(subject.api_endpoint.to_s).to match(%r{/api\Z})
end
it 'has the api_version' do
@@ -163,7 +171,7 @@ RSpec.describe Gitlab::Kubernetes::KubeClient do
it_behaves_like 'a Kubeclient'
it 'has the RBAC API group endpoint' do
- expect(subject.api_endpoint.to_s).to match(%r{\/apis\/rbac.authorization.k8s.io\Z})
+ expect(subject.api_endpoint.to_s).to match(%r{/apis/rbac.authorization.k8s.io\Z})
end
it 'has the api_version' do
@@ -177,7 +185,7 @@ RSpec.describe Gitlab::Kubernetes::KubeClient do
it_behaves_like 'a Kubeclient'
it 'has the Istio API group endpoint' do
- expect(subject.api_endpoint.to_s).to match(%r{\/apis\/networking.istio.io\Z})
+ expect(subject.api_endpoint.to_s).to match(%r{/apis/networking.istio.io\Z})
end
it 'has the api_version' do
@@ -191,7 +199,7 @@ RSpec.describe Gitlab::Kubernetes::KubeClient do
it_behaves_like 'a Kubeclient'
it 'has the extensions API group endpoint' do
- expect(subject.api_endpoint.to_s).to match(%r{\/apis\/serving.knative.dev\Z})
+ expect(subject.api_endpoint.to_s).to match(%r{/apis/serving.knative.dev\Z})
end
it 'has the api_version' do
@@ -205,7 +213,7 @@ RSpec.describe Gitlab::Kubernetes::KubeClient do
it_behaves_like 'a Kubeclient'
it 'has the networking API group endpoint' do
- expect(subject.api_endpoint.to_s).to match(%r{\/apis\/networking.k8s.io\Z})
+ expect(subject.api_endpoint.to_s).to match(%r{/apis/networking.k8s.io\Z})
end
it 'has the api_version' do
@@ -219,7 +227,7 @@ RSpec.describe Gitlab::Kubernetes::KubeClient do
it_behaves_like 'a Kubeclient'
it 'has the metrics API group endpoint' do
- expect(subject.api_endpoint.to_s).to match(%r{\/apis\/metrics.k8s.io\Z})
+ expect(subject.api_endpoint.to_s).to match(%r{/apis/metrics.k8s.io\Z})
end
it 'has the api_version' do
diff --git a/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb b/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
index 57f2b1cfd96..81d423598f2 100644
--- a/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
+++ b/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
@@ -2,6 +2,8 @@
require 'spec_helper'
RSpec.describe Gitlab::MarkdownCache::ActiveRecord::Extension do
+ let_it_be(:project) { create(:project) }
+
let(:klass) do
Class.new(ActiveRecord::Base) do
self.table_name = 'issues'
@@ -17,7 +19,12 @@ RSpec.describe Gitlab::MarkdownCache::ActiveRecord::Extension do
end
let(:cache_version) { Gitlab::MarkdownCache::CACHE_COMMONMARK_VERSION << 16 }
- let(:thing) { klass.create!(title: markdown, title_html: html, cached_markdown_version: cache_version) }
+ let(:thing) do
+ klass.create!(
+ project_id: project.id, namespace_id: project.project_namespace_id,
+ title: markdown, title_html: html, cached_markdown_version: cache_version
+ )
+ end
let(:markdown) { '`Foo`' }
let(:html) { '<p data-sourcepos="1:1-1:5" dir="auto"><code>Foo</code></p>' }
@@ -26,7 +33,7 @@ RSpec.describe Gitlab::MarkdownCache::ActiveRecord::Extension do
let(:updated_html) { '<p data-sourcepos="1:1-1:5" dir="auto"><code>Bar</code></p>' }
context 'an unchanged markdown field' do
- let(:thing) { klass.new(title: markdown) }
+ let(:thing) { klass.new(project_id: project.id, namespace_id: project.project_namespace_id, title: markdown) }
before do
thing.title = thing.title
@@ -40,7 +47,12 @@ RSpec.describe Gitlab::MarkdownCache::ActiveRecord::Extension do
end
context 'a changed markdown field' do
- let(:thing) { klass.create!(title: markdown, title_html: html, cached_markdown_version: cache_version) }
+ let(:thing) do
+ klass.create!(
+ project_id: project.id, namespace_id: project.project_namespace_id,
+ title: markdown, title_html: html, cached_markdown_version: cache_version
+ )
+ end
before do
thing.title = updated_markdown
@@ -72,7 +84,12 @@ RSpec.describe Gitlab::MarkdownCache::ActiveRecord::Extension do
end
context 'a non-markdown field changed' do
- let(:thing) { klass.new(title: markdown, title_html: html, cached_markdown_version: cache_version) }
+ let(:thing) do
+ klass.new(
+ project_id: project.id, namespace_id: project.project_namespace_id, title: markdown,
+ title_html: html, cached_markdown_version: cache_version
+ )
+ end
before do
thing.state_id = 2
@@ -86,7 +103,12 @@ RSpec.describe Gitlab::MarkdownCache::ActiveRecord::Extension do
end
context 'version is out of date' do
- let(:thing) { klass.new(title: updated_markdown, title_html: html, cached_markdown_version: nil) }
+ let(:thing) do
+ klass.new(
+ project_id: project.id, namespace_id: project.project_namespace_id,
+ title: updated_markdown, title_html: html, cached_markdown_version: nil
+ )
+ end
before do
thing.save!
@@ -127,7 +149,12 @@ RSpec.describe Gitlab::MarkdownCache::ActiveRecord::Extension do
end
describe '#cached_html_up_to_date?' do
- let(:thing) { klass.create!(title: updated_markdown, title_html: html, cached_markdown_version: nil) }
+ let(:thing) do
+ klass.create!(
+ project_id: project.id, namespace_id: project.project_namespace_id,
+ title: updated_markdown, title_html: html, cached_markdown_version: nil
+ )
+ end
subject { thing.cached_html_up_to_date?(:title) }
diff --git a/spec/lib/gitlab/memory/instrumentation_spec.rb b/spec/lib/gitlab/memory/instrumentation_spec.rb
index 069c45da18a..3d58f28ec1e 100644
--- a/spec/lib/gitlab/memory/instrumentation_spec.rb
+++ b/spec/lib/gitlab/memory/instrumentation_spec.rb
@@ -2,11 +2,11 @@
require 'spec_helper'
-RSpec.describe Gitlab::Memory::Instrumentation do
+RSpec.describe Gitlab::Memory::Instrumentation, feature_category: :application_performance do
include MemoryInstrumentationHelper
before do
- skip_memory_instrumentation!
+ verify_memory_instrumentation_available!
end
describe '.available?' do
diff --git a/spec/lib/gitlab/memory/jemalloc_spec.rb b/spec/lib/gitlab/memory/jemalloc_spec.rb
index 414d6017534..8cce2278f8e 100644
--- a/spec/lib/gitlab/memory/jemalloc_spec.rb
+++ b/spec/lib/gitlab/memory/jemalloc_spec.rb
@@ -1,15 +1,14 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'tmpdir'
+require 'tempfile'
RSpec.describe Gitlab::Memory::Jemalloc do
- let(:outdir) { Dir.mktmpdir }
- let(:tmp_outdir) { Dir.mktmpdir }
+ let(:outfile) { Tempfile.new }
after do
- FileUtils.rm_f(outdir)
- FileUtils.rm_f(tmp_outdir)
+ outfile.close
+ outfile.unlink
end
context 'when jemalloc is loaded' do
@@ -31,12 +30,11 @@ RSpec.describe Gitlab::Memory::Jemalloc do
describe '.dump_stats' do
it 'writes stats JSON file' do
- file_path = described_class.dump_stats(path: outdir, tmp_dir: tmp_outdir, format: format)
+ described_class.dump_stats(outfile, format: format)
- file = Dir.entries(outdir).find { |e| e.match(/jemalloc_stats\.#{$$}\.\d+\.json$/) }
- expect(file).not_to be_nil
- expect(file_path).to eq(File.join(outdir, file))
- expect(File.read(file_path)).to eq(output)
+ outfile.rewind
+
+ expect(outfile.read).to eq(output)
end
end
end
@@ -56,23 +54,12 @@ RSpec.describe Gitlab::Memory::Jemalloc do
end
describe '.dump_stats' do
- shared_examples 'writes stats text file' do |filename_label, filename_pattern|
- it do
- described_class.dump_stats(
- path: outdir, tmp_dir: tmp_outdir, format: format, filename_label: filename_label)
-
- file = Dir.entries(outdir).find { |e| e.match(filename_pattern) }
- expect(file).not_to be_nil
- expect(File.read(File.join(outdir, file))).to eq(output)
- end
- end
+ it 'writes stats text file' do
+ described_class.dump_stats(outfile, format: format)
- context 'when custom filename label is passed' do
- include_examples 'writes stats text file', 'puma_0', /jemalloc_stats\.#{$$}\.puma_0\.\d+\.txt$/
- end
+ outfile.rewind
- context 'when custom filename label is not passed' do
- include_examples 'writes stats text file', nil, /jemalloc_stats\.#{$$}\.\d+\.txt$/
+ expect(outfile.read).to eq(output)
end
end
end
@@ -91,7 +78,7 @@ RSpec.describe Gitlab::Memory::Jemalloc do
describe '.dump_stats' do
it 'raises an error' do
expect do
- described_class.dump_stats(path: outdir, tmp_dir: tmp_outdir, format: format)
+ described_class.dump_stats(outfile, format: format)
end.to raise_error(/format must be one of/)
end
end
@@ -104,18 +91,18 @@ RSpec.describe Gitlab::Memory::Jemalloc do
end
describe '.stats' do
- it 'returns nil' do
- expect(described_class.stats).to be_nil
+ it 'returns empty string' do
+ expect(described_class.stats).to be_empty
end
end
describe '.dump_stats' do
it 'does nothing' do
- stub_env('LD_PRELOAD', nil)
+ described_class.dump_stats(outfile)
- described_class.dump_stats(path: outdir, tmp_dir: tmp_outdir)
+ outfile.rewind
- expect(Dir.empty?(outdir)).to be(true)
+ expect(outfile.read).to be_empty
end
end
end
diff --git a/spec/lib/gitlab/memory/reporter_spec.rb b/spec/lib/gitlab/memory/reporter_spec.rb
new file mode 100644
index 00000000000..924397ceb4f
--- /dev/null
+++ b/spec/lib/gitlab/memory/reporter_spec.rb
@@ -0,0 +1,206 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Memory::Reporter, :aggregate_failures, feature_category: :application_performance do
+ let(:fake_report) do
+ Class.new do
+ def name
+ 'fake_report'
+ end
+
+ def active?
+ true
+ end
+
+ def run(writer)
+ writer << 'I ran'
+ end
+ end
+ end
+
+ let(:logger) { instance_double(::Logger) }
+ let(:report) { fake_report.new }
+
+ after do
+ FileUtils.rm_rf(reports_path)
+ end
+
+ describe '#run_report', time_travel_to: '2020-02-02 10:30:45 0000' do
+ let(:report_duration_counter) { instance_double(::Prometheus::Client::Counter) }
+ let(:file_size) { 1_000_000 }
+ let(:report_file) { "#{reports_path}/fake_report.2020-02-02.10:30:45:000.worker_1.abc123.gz" }
+
+ let(:input) { StringIO.new }
+ let(:output) { StringIO.new }
+
+ before do
+ allow(SecureRandom).to receive(:uuid).and_return('abc123')
+
+ allow(Gitlab::Metrics).to receive(:counter).and_return(report_duration_counter)
+ allow(report_duration_counter).to receive(:increment)
+
+ allow(::Prometheus::PidProvider).to receive(:worker_id).and_return('worker_1')
+ allow(File).to receive(:size).with(report_file).and_return(file_size)
+
+ allow(logger).to receive(:info)
+
+ stub_gzip
+ end
+
+ shared_examples 'runs and stores reports' do
+ it 'runs the given report and returns true' do
+ expect(reporter.run_report(report)).to be(true)
+
+ expect(output.string).to eq('I ran')
+ end
+
+ it 'closes read and write streams' do
+ expect(input).to receive(:close).ordered.at_least(:once)
+ expect(output).to receive(:close).ordered.at_least(:once)
+
+ reporter.run_report(report)
+ end
+
+ it 'logs start and finish event' do
+ expect(logger).to receive(:info).ordered.with(
+ hash_including(
+ message: 'started',
+ pid: Process.pid,
+ worker_id: 'worker_1',
+ perf_report_worker_uuid: 'abc123',
+ perf_report: 'fake_report'
+ ))
+ expect(logger).to receive(:info).ordered.with(
+ hash_including(
+ :duration_s,
+ :cpu_s,
+ perf_report_file: report_file,
+ perf_report_size_bytes: file_size,
+ message: 'finished',
+ pid: Process.pid,
+ worker_id: 'worker_1',
+ perf_report_worker_uuid: 'abc123',
+ perf_report: 'fake_report'
+ ))
+
+ reporter.run_report(report)
+ end
+
+ it 'increments Prometheus duration counter' do
+ expect(report_duration_counter).to receive(:increment).with({ report: 'fake_report' }, an_instance_of(Float))
+
+ reporter.run_report(report)
+ end
+
+ context 'when the report returns invalid file path' do
+ before do
+ allow(File).to receive(:size).with(report_file).and_raise(Errno::ENOENT)
+ end
+
+ it 'logs `0` as `perf_report_size_bytes`' do
+ expect(logger).to receive(:info).ordered.with(
+ hash_including(message: 'started')
+ )
+ expect(logger).to receive(:info).ordered.with(
+ hash_including(message: 'finished', perf_report_size_bytes: 0)
+ )
+
+ reporter.run_report(report)
+ end
+ end
+
+ context 'when an error occurs' do
+ before do
+ allow(report).to receive(:run).and_raise(RuntimeError.new('report failed'))
+ end
+
+ it 'logs the error and returns false' do
+ expect(logger).to receive(:info).ordered.with(hash_including(message: 'started'))
+ expect(logger).to receive(:error).ordered.with(
+ hash_including(
+ message: 'failed', error: '#<RuntimeError: report failed>'
+ ))
+
+ expect(reporter.run_report(report)).to be(false)
+ end
+
+ it 'closes read and write streams' do
+ allow(logger).to receive(:info)
+ allow(logger).to receive(:error)
+
+ expect(input).to receive(:close).ordered.at_least(:once)
+ expect(output).to receive(:close).ordered.at_least(:once)
+
+ reporter.run_report(report)
+ end
+
+ context 'when compression process is still running' do
+ it 'terminates the process' do
+ allow(logger).to receive(:info)
+ allow(logger).to receive(:error)
+
+ expect(Gitlab::ProcessManagement).to receive(:signal).with(an_instance_of(Integer), :KILL)
+
+ reporter.run_report(report)
+ end
+ end
+ end
+
+ context 'when a report is disabled' do
+ it 'does nothing and returns false' do
+ expect(report).to receive(:active?).and_return(false)
+ expect(report).not_to receive(:run)
+ expect(logger).not_to receive(:info)
+ expect(report_duration_counter).not_to receive(:increment)
+
+ reporter.run_report(report)
+ end
+ end
+ end
+
+ context 'when reports path is specified directly' do
+ let(:reports_path) { Dir.mktmpdir }
+
+ subject(:reporter) { described_class.new(reports_path: reports_path, logger: logger) }
+
+ it_behaves_like 'runs and stores reports'
+ end
+
+ context 'when reports path is specified via environment' do
+ let(:reports_path) { Dir.mktmpdir }
+
+ subject(:reporter) { described_class.new(logger: logger) }
+
+ before do
+ stub_env('GITLAB_DIAGNOSTIC_REPORTS_PATH', reports_path)
+ end
+
+ it_behaves_like 'runs and stores reports'
+ end
+
+ context 'when reports path is not specified' do
+ let(:reports_path) { reporter.reports_path }
+
+ subject(:reporter) { described_class.new(logger: logger) }
+
+ it 'defaults to a temporary location' do
+ expect(reports_path).not_to be_empty
+ end
+
+ it_behaves_like 'runs and stores reports'
+ end
+ end
+
+ # We need to stub out the call into gzip. We do this by intercepting the write
+ # end of the pipe and replacing it with a StringIO instead, which we can
+ # easily inspect for contents.
+ def stub_gzip
+ pid = 42
+ allow(IO).to receive(:pipe).and_return([input, output])
+ allow(Process).to receive(:spawn).with(
+ "gzip", "--fast", in: input, out: an_instance_of(File), err: an_instance_of(IO)
+ ).and_return(pid)
+ allow(Process).to receive(:waitpid).with(pid)
+ end
+end
diff --git a/spec/lib/gitlab/memory/reports/heap_dump_spec.rb b/spec/lib/gitlab/memory/reports/heap_dump_spec.rb
new file mode 100644
index 00000000000..4e235a71bdb
--- /dev/null
+++ b/spec/lib/gitlab/memory/reports/heap_dump_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Memory::Reports::HeapDump, feature_category: :application_performance do
+ # Copy this class so we do not mess with its state.
+ let(:klass) { described_class.dup }
+
+ subject(:report) { klass.new }
+
+ describe '#name' do
+ # This is a bit silly, but it caused code coverage failures.
+ it 'is set' do
+ expect(report.name).to eq('heap_dump')
+ end
+ end
+
+ describe '#active?' do
+ it 'is true when report_heap_dumps is enabled' do
+ expect(report).to be_active
+ end
+
+ it 'is false when report_heap_dumps is disabled' do
+ stub_feature_flags(report_heap_dumps: false)
+
+ expect(report).not_to be_active
+ end
+ end
+
+ describe '#run' do
+ subject(:run) { report.run(writer) }
+
+ let(:writer) { StringIO.new }
+
+ context 'when no heap dump is enqueued' do
+ it 'does nothing and returns false' do
+ expect(ObjectSpace).not_to receive(:dump_all)
+
+ expect(run).to be(false)
+ end
+ end
+
+ context 'when a heap dump is enqueued', :aggregate_failures do
+ it 'dumps heap and returns true' do
+ expect(ObjectSpace).to receive(:dump_all).with(output: writer) do |output:|
+ output << 'heap contents'
+ end
+
+ klass.enqueue!
+
+ expect(run).to be(true)
+ expect(writer.string).to eq('heap contents')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb b/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb
index b327a40bc2c..ce06c270a05 100644
--- a/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb
+++ b/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb
@@ -3,96 +3,16 @@
require 'spec_helper'
RSpec.describe Gitlab::Memory::Reports::JemallocStats do
- let_it_be(:outdir) { Dir.mktmpdir }
+ subject(:jemalloc_stats) { described_class.new }
- let(:jemalloc_stats) { described_class.new(reports_path: outdir) }
-
- after do
- FileUtils.rm_f(outdir)
- end
+ let(:writer) { StringIO.new }
describe '.run' do
context 'when :report_jemalloc_stats ops FF is enabled' do
- let(:worker_id) { 'puma_1' }
- let(:report_name) { 'report.json' }
- let(:report_path) { File.join(outdir, report_name) }
-
- before do
- allow(Prometheus::PidProvider).to receive(:worker_id).and_return(worker_id)
- end
-
- it 'invokes Jemalloc.dump_stats and returns file path' do
- expect(Gitlab::Memory::Jemalloc)
- .to receive(:dump_stats)
- .with(path: outdir,
- tmp_dir: File.join(outdir, '/tmp'),
- filename_label: worker_id)
- .and_return(report_path)
-
- expect(jemalloc_stats.run).to eq(report_path)
- end
-
- describe 'reports cleanup' do
- let(:jemalloc_stats) { described_class.new(reports_path: outdir) }
-
- before do
- stub_env('GITLAB_DIAGNOSTIC_REPORTS_JEMALLOC_MAX_REPORTS_STORED', 3)
- allow(Gitlab::Memory::Jemalloc).to receive(:dump_stats)
- end
-
- context 'when number of reports exceeds `max_reports_stored`' do
- let_it_be(:reports) do
- now = Time.current
-
- (1..5).map do |i|
- Tempfile.new("jemalloc_stats.#{i}.worker_#{i}.#{Time.current.to_i}.json", outdir).tap do |f|
- FileUtils.touch(f, mtime: (now + i.second).to_i)
- end
- end
- end
-
- after do
- reports.each do |f|
- f.close
- f.unlink
- rescue Errno::ENOENT
- # Some of the files are already unlinked by the code we test; Ignore
- end
- end
-
- it 'keeps only `max_reports_stored` total newest files' do
- expect { jemalloc_stats.run }
- .to change { Dir.entries(outdir).count { |e| e.match(/jemalloc_stats.*/) } }
- .from(5).to(3)
-
- # Keeps only the newest reports
- expect(reports.last(3).all? { |r| File.exist?(r) }).to be true
- end
- end
-
- context 'when number of reports does not exceed `max_reports_stored`' do
- let_it_be(:reports) do
- now = Time.current
-
- (1..3).map do |i|
- Tempfile.new("jemalloc_stats.#{i}.worker_#{i}.#{Time.current.to_i}.json", outdir).tap do |f|
- FileUtils.touch(f, mtime: (now + i.second).to_i)
- end
- end
- end
-
- after do
- reports.each do |f|
- f.close
- f.unlink
- end
- end
+ it 'dumps jemalloc stats to the given writer' do
+ expect(Gitlab::Memory::Jemalloc).to receive(:dump_stats).with(writer)
- it 'does not remove any reports' do
- expect { jemalloc_stats.run }
- .not_to change { Dir.entries(outdir).count { |e| e.match(/jemalloc_stats.*/) } }
- end
- end
+ jemalloc_stats.run(writer)
end
end
@@ -101,10 +21,10 @@ RSpec.describe Gitlab::Memory::Reports::JemallocStats do
stub_feature_flags(report_jemalloc_stats: false)
end
- it 'does not run the report and returns nil' do
+ it 'does not run the report' do
expect(Gitlab::Memory::Jemalloc).not_to receive(:dump_stats)
- expect(jemalloc_stats.run).to be_nil
+ jemalloc_stats.run(writer)
end
end
end
diff --git a/spec/lib/gitlab/memory/reports_daemon_spec.rb b/spec/lib/gitlab/memory/reports_daemon_spec.rb
index 0473e170502..91c36c87253 100644
--- a/spec/lib/gitlab/memory/reports_daemon_spec.rb
+++ b/spec/lib/gitlab/memory/reports_daemon_spec.rb
@@ -3,94 +3,58 @@
require 'spec_helper'
RSpec.describe Gitlab::Memory::ReportsDaemon, :aggregate_failures do
- let(:daemon) { described_class.new }
+ let(:reporter) { instance_double(Gitlab::Memory::Reporter) }
+ let(:reports) { nil }
- let_it_be(:tmp_dir) { Dir.mktmpdir }
-
- after(:all) do
- FileUtils.remove_entry(tmp_dir)
- end
+ subject(:daemon) { described_class.new(reporter: reporter, reports: reports) }
describe '#run_thread' do
- let(:report_duration_counter) { instance_double(::Prometheus::Client::Counter) }
- let(:file_size) { 1_000_000 }
-
before do
- allow(Gitlab::Metrics).to receive(:counter).and_return(report_duration_counter)
- allow(report_duration_counter).to receive(:increment)
-
# make sleep no-op
allow(daemon).to receive(:sleep) {}
# let alive return 3 times: true, true, false
allow(daemon).to receive(:alive).and_return(true, true, false)
-
- allow(File).to receive(:size).with(/#{daemon.reports_path}.*\.json/).and_return(file_size)
- end
-
- it 'runs reports, logs and sets gauge' do
- expect(daemon.send(:reports))
- .to all(receive(:run).twice { Tempfile.new("report.json", tmp_dir).path })
-
- expect(::Prometheus::PidProvider).to receive(:worker_id).at_least(:once).and_return('worker_1')
-
- expect(Gitlab::AppLogger).to receive(:info).with(
- hash_including(
- :duration_s,
- :cpu_s,
- perf_report_size_bytes: file_size,
- message: 'finished',
- pid: Process.pid,
- worker_id: 'worker_1',
- perf_report: 'jemalloc_stats'
- )).twice
-
- expect(report_duration_counter).to receive(:increment).with({ report: 'jemalloc_stats' }, an_instance_of(Float))
-
- daemon.send(:run_thread)
end
- context 'when the report object returns invalid file path' do
- before do
- allow(File).to receive(:size).with(/#{daemon.reports_path}.*\.json/).and_raise(Errno::ENOENT)
- end
-
- it 'logs `0` as `perf_report_size_bytes`' do
- expect(daemon.send(:reports))
- .to all(receive(:run).twice { Tempfile.new("report.json", tmp_dir).path })
-
- expect(Gitlab::AppLogger).to receive(:info).with(hash_including(perf_report_size_bytes: 0)).twice
+ context 'with default reports' do
+ it 'runs them using the given reporter' do
+ expect(reporter).to receive(:run_report).twice.with(an_instance_of(Gitlab::Memory::Reports::JemallocStats))
daemon.send(:run_thread)
end
end
- it 'allows configure and run multiple reports' do
+ context 'with inactive reports' do
# rubocop: disable RSpec/VerifiedDoubles
# We test how ReportsDaemon could be extended in the future
# We configure it with new reports classes which are not yet defined so we cannot make this an instance_double.
- active_report_1 = double("Active Report 1", active?: true)
- active_report_2 = double("Active Report 2", active?: true)
- inactive_report = double("Inactive Report", active?: false)
+ let(:active_report_1) { double("Active Report 1", active?: true) }
+ let(:active_report_2) { double("Active Report 2", active?: true) }
+ let(:inactive_report) { double("Inactive Report", active?: false) }
# rubocop: enable RSpec/VerifiedDoubles
- allow(daemon).to receive(:reports).and_return([active_report_1, inactive_report, active_report_2])
+ let(:reports) do
+ [active_report_1, active_report_2, inactive_report]
+ end
- expect(active_report_1).to receive(:run).and_return(File.join(tmp_dir, 'report_1.json')).twice
- expect(active_report_2).to receive(:run).and_return(File.join(tmp_dir, 'report_2.json')).twice
- expect(inactive_report).not_to receive(:run)
+ it 'runs only active reports' do
+ expect(reporter).to receive(:run_report).ordered.with(active_report_1)
+ expect(reporter).to receive(:run_report).ordered.with(active_report_2)
+ expect(reporter).to receive(:run_report).ordered.with(active_report_1)
+ expect(reporter).to receive(:run_report).ordered.with(active_report_2)
+ expect(reporter).not_to receive(:run_report).with(inactive_report)
- daemon.send(:run_thread)
+ daemon.send(:run_thread)
+ end
end
context 'sleep timers logic' do
it 'wakes up every (fixed interval + defined delta), sleeps between reports each cycle' do
stub_env('GITLAB_DIAGNOSTIC_REPORTS_SLEEP_MAX_DELTA_S', 1) # rand(1) == 0, so we will have fixed sleep interval
- daemon = described_class.new
+ daemon = described_class.new(reporter: reporter, reports: reports)
allow(daemon).to receive(:alive).and_return(true, true, false)
-
- expect(daemon.send(:reports))
- .to all(receive(:run).twice { Tempfile.new("report.json", tmp_dir).path })
+ allow(reporter).to receive(:run_report)
expect(daemon).to receive(:sleep).with(described_class::DEFAULT_SLEEP_S).ordered
expect(daemon).to receive(:sleep).with(described_class::DEFAULT_SLEEP_BETWEEN_REPORTS_S).ordered
@@ -116,7 +80,6 @@ RSpec.describe Gitlab::Memory::ReportsDaemon, :aggregate_failures do
expect(daemon.sleep_s).to eq(described_class::DEFAULT_SLEEP_S)
expect(daemon.sleep_max_delta_s).to eq(described_class::DEFAULT_SLEEP_MAX_DELTA_S)
expect(daemon.sleep_between_reports_s).to eq(described_class::DEFAULT_SLEEP_BETWEEN_REPORTS_S)
- expect(daemon.reports_path).to eq(described_class::DEFAULT_REPORTS_PATH)
end
end
@@ -125,7 +88,6 @@ RSpec.describe Gitlab::Memory::ReportsDaemon, :aggregate_failures do
stub_env('GITLAB_DIAGNOSTIC_REPORTS_SLEEP_S', 100)
stub_env('GITLAB_DIAGNOSTIC_REPORTS_SLEEP_MAX_DELTA_S', 50)
stub_env('GITLAB_DIAGNOSTIC_REPORTS_SLEEP_BETWEEN_REPORTS_S', 2)
- stub_env('GITLAB_DIAGNOSTIC_REPORTS_PATH', tmp_dir)
end
it 'uses provided values' do
@@ -134,7 +96,6 @@ RSpec.describe Gitlab::Memory::ReportsDaemon, :aggregate_failures do
expect(daemon.sleep_s).to eq(100)
expect(daemon.sleep_max_delta_s).to eq(50)
expect(daemon.sleep_between_reports_s).to eq(2)
- expect(daemon.reports_path).to eq(tmp_dir)
end
end
end
diff --git a/spec/lib/gitlab/memory/watchdog/configuration_spec.rb b/spec/lib/gitlab/memory/watchdog/configuration_spec.rb
index 38a39f6a33a..9242344ead2 100644
--- a/spec/lib/gitlab/memory/watchdog/configuration_spec.rb
+++ b/spec/lib/gitlab/memory/watchdog/configuration_spec.rb
@@ -20,10 +20,14 @@ RSpec.describe Gitlab::Memory::Watchdog::Configuration do
end
end
- describe '#logger' do
- context 'when logger is not set, defaults to stdout logger' do
- it 'defaults to Logger' do
- expect(configuration.logger).to be_an_instance_of(::Gitlab::Logger)
+ describe '#event_reporter' do
+ context 'when event reporter is not set' do
+ before do
+ allow(Gitlab::Metrics).to receive(:counter)
+ end
+
+ it 'defaults to EventReporter' do
+ expect(configuration.event_reporter).to be_an_instance_of(::Gitlab::Memory::Watchdog::EventReporter)
end
end
end
@@ -38,6 +42,8 @@ RSpec.describe Gitlab::Memory::Watchdog::Configuration do
describe '#monitors' do
context 'when monitors are configured to be used' do
+ let(:monitor_name1) { :monitor1 }
+ let(:monitor_name2) { :monitor2 }
let(:payload1) do
{
message: 'monitor_1_text',
@@ -96,7 +102,17 @@ RSpec.describe Gitlab::Memory::Watchdog::Configuration do
expect(payloads).to eq([payload1, payload2])
expect(thresholds).to eq([false, true])
expect(strikes).to eq([false, true])
- expect(monitor_names).to eq([:monitor1, :monitor2])
+ expect(monitor_names).to eq([monitor_name1, monitor_name2])
+ end
+
+ it 'monitors are not empty' do
+ expect(configuration.monitors).not_to be_empty
+ end
+ end
+
+ context 'when monitors are not configured' do
+ it 'monitors are empty' do
+ expect(configuration.monitors).to be_empty
end
end
@@ -119,18 +135,19 @@ RSpec.describe Gitlab::Memory::Watchdog::Configuration do
include_examples 'executes monitors and returns correct results'
end
- end
- context 'when same monitor class is configured twice' do
- before do
- configuration.monitors.push monitor_class_1, max_strikes: 1
- configuration.monitors.push monitor_class_1, max_strikes: 1
- end
+ context 'when monitors are configured with monitor name' do
+ let(:monitor_name1) { :mon_one }
+ let(:monitor_name2) { :mon_two }
- it 'calls same monitor only once' do
- expect do |b|
- configuration.monitors.call_each(&b)
- end.to yield_control.once
+ before do
+ configuration.monitors do |stack|
+ stack.push monitor_class_1, false, { message: 'monitor_1_text' }, max_strikes: 5, monitor_name: :mon_one
+ stack.push monitor_class_2, true, { message: 'monitor_2_text' }, max_strikes: 0, monitor_name: :mon_two
+ end
+ end
+
+ include_examples 'executes monitors and returns correct results'
end
end
end
diff --git a/spec/lib/gitlab/memory/watchdog/configurator_spec.rb b/spec/lib/gitlab/memory/watchdog/configurator_spec.rb
index 86cbb724cfd..a901be84a21 100644
--- a/spec/lib/gitlab/memory/watchdog/configurator_spec.rb
+++ b/spec/lib/gitlab/memory/watchdog/configurator_spec.rb
@@ -6,17 +6,23 @@ require 'sidekiq'
require_dependency 'gitlab/cluster/lifecycle_events'
RSpec.describe Gitlab::Memory::Watchdog::Configurator do
- shared_examples 'as configurator' do |handler_class, sleep_time_env, sleep_time|
+ shared_examples 'as configurator' do |handler_class, event_reporter_class, sleep_time_env, sleep_time|
it 'configures the correct handler' do
configurator.call(configuration)
expect(configuration.handler).to be_an_instance_of(handler_class)
end
+ it 'configures the correct event reporter' do
+ configurator.call(configuration)
+
+ expect(configuration.event_reporter).to be_an_instance_of(event_reporter_class)
+ end
+
it 'configures the correct logger' do
configurator.call(configuration)
- expect(configuration.logger).to eq(logger)
+ expect(configuration.event_reporter.logger).to eq(logger)
end
context 'when sleep_time_seconds is not passed through the environment' do
@@ -87,12 +93,13 @@ RSpec.describe Gitlab::Memory::Watchdog::Configurator do
it_behaves_like 'as configurator',
Gitlab::Memory::Watchdog::PumaHandler,
+ Gitlab::Memory::Watchdog::EventReporter,
'GITLAB_MEMWD_SLEEP_TIME_SEC',
- 60
+ described_class::DEFAULT_SLEEP_INTERVAL_S
context 'with DISABLE_PUMA_WORKER_KILLER set to true' do
- let(:primary_memory) { 2048 }
- let(:worker_memory) { max_mem_growth * primary_memory + 1 }
+ let(:primary_memory_bytes) { 2_097_152_000 }
+ let(:worker_memory_bytes) { max_mem_growth * primary_memory_bytes + 1 }
let(:expected_payloads) do
{
heap_fragmentation: {
@@ -105,9 +112,9 @@ RSpec.describe Gitlab::Memory::Watchdog::Configurator do
},
unique_memory_growth: {
message: 'memory limit exceeded',
- memwd_uss_bytes: worker_memory,
- memwd_ref_uss_bytes: primary_memory,
- memwd_max_uss_bytes: max_mem_growth * primary_memory,
+ memwd_uss_bytes: worker_memory_bytes,
+ memwd_ref_uss_bytes: primary_memory_bytes,
+ memwd_max_uss_bytes: max_mem_growth * primary_memory_bytes,
memwd_max_strikes: max_strikes,
memwd_cur_strikes: 1
}
@@ -117,10 +124,10 @@ RSpec.describe Gitlab::Memory::Watchdog::Configurator do
before do
stub_env('DISABLE_PUMA_WORKER_KILLER', true)
allow(Gitlab::Metrics::Memory).to receive(:gc_heap_fragmentation).and_return(max_heap_fragmentation + 0.1)
- allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).and_return({ uss: worker_memory })
+ allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).and_return({ uss: worker_memory_bytes })
allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).with(
pid: Gitlab::Cluster::PRIMARY_PID
- ).and_return({ uss: primary_memory })
+ ).and_return({ uss: primary_memory_bytes })
end
context 'when settings are set via environment variables' do
@@ -138,21 +145,22 @@ RSpec.describe Gitlab::Memory::Watchdog::Configurator do
end
context 'when settings are not set via environment variables' do
- let(:max_heap_fragmentation) { 0.5 }
- let(:max_mem_growth) { 3.0 }
- let(:max_strikes) { 5 }
+ let(:max_heap_fragmentation) { described_class::DEFAULT_MAX_HEAP_FRAG }
+ let(:max_mem_growth) { described_class::DEFAULT_MAX_MEM_GROWTH }
+ let(:max_strikes) { described_class::DEFAULT_MAX_STRIKES }
it_behaves_like 'as monitor configurator'
end
end
context 'with DISABLE_PUMA_WORKER_KILLER set to false' do
+ let(:memory_limit_bytes) { memory_limit_mb.megabytes }
let(:expected_payloads) do
{
rss_memory_limit: {
message: 'rss memory limit exceeded',
- memwd_rss_bytes: memory_limit + 1,
- memwd_max_rss_bytes: memory_limit,
+ memwd_rss_bytes: memory_limit_bytes + 1,
+ memwd_max_rss_bytes: memory_limit_bytes,
memwd_max_strikes: max_strikes,
memwd_cur_strikes: 1
}
@@ -161,15 +169,15 @@ RSpec.describe Gitlab::Memory::Watchdog::Configurator do
before do
stub_env('DISABLE_PUMA_WORKER_KILLER', false)
- allow(Gitlab::Metrics::System).to receive(:memory_usage_rss).and_return({ total: memory_limit + 1 })
+ allow(Gitlab::Metrics::System).to receive(:memory_usage_rss).and_return({ total: memory_limit_bytes + 1 })
end
context 'when settings are set via environment variables' do
- let(:memory_limit) { 1300.megabytes }
+ let(:memory_limit_mb) { 1300 }
let(:max_strikes) { 4 }
before do
- stub_env('PUMA_WORKER_MAX_MEMORY', 1300)
+ stub_env('PUMA_WORKER_MAX_MEMORY', memory_limit_mb)
stub_env('GITLAB_MEMWD_MAX_STRIKES', 4)
end
@@ -177,8 +185,8 @@ RSpec.describe Gitlab::Memory::Watchdog::Configurator do
end
context 'when settings are not set via environment variables' do
- let(:memory_limit) { 1200.megabytes }
- let(:max_strikes) { 5 }
+ let(:memory_limit_mb) { described_class::DEFAULT_PUMA_WORKER_RSS_LIMIT_MB }
+ let(:max_strikes) { described_class::DEFAULT_MAX_STRIKES }
it_behaves_like 'as monitor configurator'
end
@@ -193,7 +201,115 @@ RSpec.describe Gitlab::Memory::Watchdog::Configurator do
it_behaves_like 'as configurator',
Gitlab::Memory::Watchdog::TermProcessHandler,
+ Gitlab::Memory::Watchdog::SidekiqEventReporter,
'SIDEKIQ_MEMORY_KILLER_CHECK_INTERVAL',
- 3
+ described_class::DEFAULT_SIDEKIQ_SLEEP_INTERVAL_S
+
+ context 'when sleep_time_seconds is less than MIN_SIDEKIQ_SLEEP_INTERVAL_S seconds' do
+ before do
+ stub_env('SIDEKIQ_MEMORY_KILLER_CHECK_INTERVAL', 0)
+ end
+
+ it 'configures the correct sleep time' do
+ configurator.call(configuration)
+
+ expect(configuration.sleep_time_seconds).to eq(described_class::MIN_SIDEKIQ_SLEEP_INTERVAL_S)
+ end
+ end
+
+ context 'with monitors' do
+ let(:soft_limit_bytes) { soft_limit_kb.kilobytes }
+ let(:hard_limit_bytes) { hard_limit_kb.kilobytes }
+
+ context 'when settings are set via environment variables' do
+ let(:soft_limit_kb) { 2000001 }
+ let(:hard_limit_kb) { 300000 }
+ let(:max_strikes) { 150 }
+ let(:grace_time) { 300 }
+ let(:expected_payloads) do
+ {
+ rss_memory_soft_limit: {
+ message: 'rss memory limit exceeded',
+ memwd_rss_bytes: soft_limit_bytes + 1,
+ memwd_max_rss_bytes: soft_limit_bytes,
+ memwd_max_strikes: max_strikes,
+ memwd_cur_strikes: 1
+ },
+ rss_memory_hard_limit: {
+ message: 'rss memory limit exceeded',
+ memwd_rss_bytes: hard_limit_bytes + 1,
+ memwd_max_rss_bytes: hard_limit_bytes,
+ memwd_max_strikes: 0,
+ memwd_cur_strikes: 1
+ }
+ }
+ end
+
+ before do
+ stub_env('SIDEKIQ_MEMORY_KILLER_MAX_RSS', soft_limit_kb)
+ stub_env('SIDEKIQ_MEMORY_KILLER_HARD_LIMIT_RSS', hard_limit_kb)
+ stub_env('SIDEKIQ_MEMORY_KILLER_GRACE_TIME', grace_time)
+ stub_env('SIDEKIQ_MEMORY_KILLER_CHECK_INTERVAL', 2)
+ allow(Gitlab::Metrics::System).to receive(:memory_usage_rss)
+ .and_return({ total: soft_limit_bytes + 1 }, { total: hard_limit_bytes + 1 })
+ end
+
+ it_behaves_like 'as monitor configurator'
+ end
+
+ context 'when only SIDEKIQ_MEMORY_KILLER_MAX_RSS is set via environment variable' do
+ let(:soft_limit_kb) { 2000000 }
+ let(:max_strikes) do
+ described_class::DEFAULT_SIDEKIQ_GRACE_TIME_S / described_class::DEFAULT_SIDEKIQ_SLEEP_INTERVAL_S
+ end
+
+ let(:expected_payloads) do
+ {
+ rss_memory_soft_limit: {
+ message: 'rss memory limit exceeded',
+ memwd_rss_bytes: soft_limit_bytes + 1,
+ memwd_max_rss_bytes: soft_limit_bytes,
+ memwd_max_strikes: max_strikes,
+ memwd_cur_strikes: 1
+ }
+ }
+ end
+
+ before do
+ allow(Gitlab::Metrics::System).to receive(:memory_usage_rss).and_return({ total: soft_limit_bytes + 1 })
+ stub_env('SIDEKIQ_MEMORY_KILLER_MAX_RSS', soft_limit_kb)
+ end
+
+ it_behaves_like 'as monitor configurator'
+ end
+
+ context 'when only SIDEKIQ_MEMORY_KILLER_HARD_LIMIT_RSS is set via environment variable' do
+ let(:hard_limit_kb) { 2000000 }
+ let(:expected_payloads) do
+ {
+ rss_memory_hard_limit: {
+ message: 'rss memory limit exceeded',
+ memwd_rss_bytes: hard_limit_bytes + 1,
+ memwd_max_rss_bytes: hard_limit_bytes,
+ memwd_max_strikes: 0,
+ memwd_cur_strikes: 1
+ }
+ }
+ end
+
+ before do
+ allow(Gitlab::Metrics::System).to receive(:memory_usage_rss).and_return({ total: hard_limit_bytes + 1 })
+ stub_env('SIDEKIQ_MEMORY_KILLER_HARD_LIMIT_RSS', hard_limit_kb)
+ end
+
+ it_behaves_like 'as monitor configurator'
+ end
+
+ context 'when both SIDEKIQ_MEMORY_KILLER_MAX_RSS and SIDEKIQ_MEMORY_KILLER_HARD_LIMIT_RSS are not set' do
+ let(:expected_payloads) { {} }
+
+ it_behaves_like 'as monitor configurator'
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/memory/watchdog/event_reporter_spec.rb b/spec/lib/gitlab/memory/watchdog/event_reporter_spec.rb
new file mode 100644
index 00000000000..f667bc724d2
--- /dev/null
+++ b/spec/lib/gitlab/memory/watchdog/event_reporter_spec.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'prometheus/client'
+
+RSpec.describe Gitlab::Memory::Watchdog::EventReporter, feature_category: :application_performance do
+ let(:logger) { instance_double(::Logger) }
+ let(:violations_counter) { instance_double(::Prometheus::Client::Counter) }
+ let(:violations_handled_counter) { instance_double(::Prometheus::Client::Counter) }
+ let(:reporter) { described_class.new(logger: logger) }
+
+ def stub_prometheus_metrics
+ allow(Gitlab::Metrics).to receive(:counter)
+ .with(:gitlab_memwd_violations_total, anything, anything)
+ .and_return(violations_counter)
+ allow(Gitlab::Metrics).to receive(:counter)
+ .with(:gitlab_memwd_violations_handled_total, anything, anything)
+ .and_return(violations_handled_counter)
+
+ allow(violations_counter).to receive(:increment)
+ allow(violations_handled_counter).to receive(:increment)
+ end
+
+ before do
+ stub_prometheus_metrics
+ allow(Gitlab::Metrics::System).to receive(:memory_usage_rss).at_least(:once).and_return(
+ total: 1024
+ )
+ allow(::Prometheus::PidProvider).to receive(:worker_id).and_return('worker_1')
+ end
+
+ describe '#logger' do
+ context 'when logger is not provided' do
+ let(:reporter) { described_class.new }
+
+ it 'uses default Gitlab::AppLogger' do
+ expect(reporter.logger).to eq(Gitlab::AppLogger)
+ end
+ end
+ end
+
+ describe '#started' do
+ it 'logs start message once' do
+ expect(logger).to receive(:info).once
+ .with(
+ pid: Process.pid,
+ worker_id: 'worker_1',
+ custom_label: 'dummy_label',
+ memwd_rss_bytes: 1024,
+ message: 'started')
+
+ reporter.started(custom_label: 'dummy_label')
+ end
+ end
+
+ describe '#stopped' do
+ subject { reporter.stopped(custom_label: 'dummy_label') }
+
+ it 'logs stop message once' do
+ expect(logger).to receive(:info).once
+ .with(
+ pid: Process.pid,
+ worker_id: 'worker_1',
+ custom_label: 'dummy_label',
+ memwd_rss_bytes: 1024,
+ message: 'stopped')
+
+ reporter.stopped(custom_label: 'dummy_label')
+ end
+ end
+
+ describe '#threshold_violated' do
+ subject { reporter.threshold_violated(:monitor_name) }
+
+ it 'increments violations counter' do
+ expect(violations_counter).to receive(:increment).with(reason: :monitor_name)
+
+ subject
+ end
+
+ it 'does not increment handled violations counter' do
+ expect(violations_handled_counter).not_to receive(:increment)
+
+ subject
+ end
+
+ it 'does not log violation' do
+ expect(logger).not_to receive(:warn)
+
+ subject
+ end
+ end
+
+ describe '#strikes_exceeded' do
+ subject { reporter.strikes_exceeded(:monitor_name, { message: 'dummy_text' }) }
+
+ before do
+ allow(logger).to receive(:warn)
+ end
+
+ it 'increments handled violations counter' do
+ expect(violations_handled_counter).to receive(:increment).with(reason: :monitor_name)
+
+ subject
+ end
+
+ it 'logs violation' do
+ expect(logger).to receive(:warn)
+ .with(
+ pid: Process.pid,
+ worker_id: 'worker_1',
+ memwd_rss_bytes: 1024,
+ message: 'dummy_text')
+
+ subject
+ end
+ end
+end
diff --git a/spec/lib/gitlab/memory/watchdog/monitor/rss_memory_limit_spec.rb b/spec/lib/gitlab/memory/watchdog/monitor/rss_memory_limit_spec.rb
index 9e25cfda782..4780b1eba53 100644
--- a/spec/lib/gitlab/memory/watchdog/monitor/rss_memory_limit_spec.rb
+++ b/spec/lib/gitlab/memory/watchdog/monitor/rss_memory_limit_spec.rb
@@ -4,25 +4,38 @@ require 'fast_spec_helper'
require 'support/shared_examples/lib/gitlab/memory/watchdog/monitor_result_shared_examples'
RSpec.describe Gitlab::Memory::Watchdog::Monitor::RssMemoryLimit do
- let(:memory_limit) { 2048 }
- let(:worker_memory) { 1024 }
+ let(:max_rss_limit_gauge) { instance_double(::Prometheus::Client::Gauge) }
+ let(:memory_limit_bytes) { 2_097_152_000 }
+ let(:worker_memory_bytes) { 1_048_576_000 }
subject(:monitor) do
- described_class.new(memory_limit: memory_limit)
+ described_class.new(memory_limit_bytes: memory_limit_bytes)
end
before do
- allow(Gitlab::Metrics::System).to receive(:memory_usage_rss).and_return({ total: worker_memory })
+ allow(Gitlab::Metrics).to receive(:gauge)
+ .with(:gitlab_memwd_max_memory_limit, anything)
+ .and_return(max_rss_limit_gauge)
+ allow(max_rss_limit_gauge).to receive(:set)
+ allow(Gitlab::Metrics::System).to receive(:memory_usage_rss).and_return({ total: worker_memory_bytes })
+ end
+
+ describe '#initialize' do
+ it 'sets the max rss limit gauge' do
+ expect(max_rss_limit_gauge).to receive(:set).with({}, memory_limit_bytes)
+
+ monitor
+ end
end
describe '#call' do
context 'when process exceeds threshold' do
- let(:worker_memory) { memory_limit + 1 }
+ let(:worker_memory_bytes) { memory_limit_bytes + 1 }
let(:payload) do
{
message: 'rss memory limit exceeded',
- memwd_rss_bytes: worker_memory,
- memwd_max_rss_bytes: memory_limit
+ memwd_rss_bytes: worker_memory_bytes,
+ memwd_max_rss_bytes: memory_limit_bytes
}
end
@@ -30,7 +43,7 @@ RSpec.describe Gitlab::Memory::Watchdog::Monitor::RssMemoryLimit do
end
context 'when process does not exceed threshold' do
- let(:worker_memory) { memory_limit - 1 }
+ let(:worker_memory_bytes) { memory_limit_bytes - 1 }
let(:payload) { {} }
include_examples 'returns Watchdog Monitor result', threshold_violated: false
diff --git a/spec/lib/gitlab/memory/watchdog/monitor_state_spec.rb b/spec/lib/gitlab/memory/watchdog/monitor_state_spec.rb
index ace1353c6e3..7802e274c53 100644
--- a/spec/lib/gitlab/memory/watchdog/monitor_state_spec.rb
+++ b/spec/lib/gitlab/memory/watchdog/monitor_state_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe Gitlab::Memory::Watchdog::MonitorState do
let(:payload) { { message: 'DummyMessage' } }
let(:threshold_violated) { true }
let(:monitor) { monitor_class.new(threshold_violated, payload) }
+ let(:monitor_name) { :dummy_monitor_name }
let(:monitor_class) do
Struct.new(:threshold_violated, :payload) do
def call
@@ -19,7 +20,7 @@ RSpec.describe Gitlab::Memory::Watchdog::MonitorState do
end
end
- subject(:monitor_state) { described_class.new(monitor, max_strikes: max_strikes) }
+ subject(:monitor_state) { described_class.new(monitor, max_strikes: max_strikes, monitor_name: monitor_name) }
shared_examples 'returns correct result' do
it 'returns correct result', :aggregate_failures do
@@ -29,7 +30,7 @@ RSpec.describe Gitlab::Memory::Watchdog::MonitorState do
expect(result.strikes_exceeded?).to eq(strikes_exceeded)
expect(result.threshold_violated?).to eq(threshold_violated)
expect(result.payload).to eq(expected_payload)
- expect(result.monitor_name).to eq(:monitor_name)
+ expect(result.monitor_name).to eq(monitor_name)
end
end
@@ -63,10 +64,4 @@ RSpec.describe Gitlab::Memory::Watchdog::MonitorState do
end
end
end
-
- describe '#monitor_class' do
- subject { monitor_state.monitor_class }
-
- it { is_expected.to eq(monitor_class) }
- end
end
diff --git a/spec/lib/gitlab/memory/watchdog/sidekiq_event_reporter_spec.rb b/spec/lib/gitlab/memory/watchdog/sidekiq_event_reporter_spec.rb
new file mode 100644
index 00000000000..48595c3f172
--- /dev/null
+++ b/spec/lib/gitlab/memory/watchdog/sidekiq_event_reporter_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Memory::Watchdog::SidekiqEventReporter, feature_category: :application_performance do
+ let(:counter) { instance_double(::Prometheus::Client::Counter) }
+
+ before do
+ allow(Gitlab::Metrics).to receive(:counter).and_return(counter)
+ allow(counter).to receive(:increment)
+ end
+
+ describe 'delegations' do
+ it { is_expected.to delegate_method(:started).to(:event_reporter) }
+ it { is_expected.to delegate_method(:stopped).to(:event_reporter) }
+ it { is_expected.to delegate_method(:threshold_violated).to(:event_reporter) }
+ it { is_expected.to delegate_method(:logger).to(:event_reporter) }
+ end
+
+ describe '#strikes_exceeded' do
+ let(:sidekiq_event_reporter) { described_class.new(logger: logger) }
+ let(:sidekiq_watchdog_running_jobs_counter) { instance_double(::Prometheus::Client::Counter) }
+ let(:logger) { instance_double(::Logger) }
+ let(:queue) { 'default' }
+ let(:jid) { SecureRandom.hex }
+ let(:running_jobs) { { jid => { worker_class: DummyWorker } } }
+ let(:sidekiq_daemon_monitor) { instance_double(Gitlab::SidekiqDaemon::Monitor) }
+ let(:worker) do
+ Class.new do
+ def self.name
+ 'DummyWorker'
+ end
+ end
+ end
+
+ before do
+ stub_const('DummyWorker', worker)
+ allow(Gitlab::SidekiqDaemon::Monitor).to receive(:instance).and_return(sidekiq_daemon_monitor)
+ allow(::Gitlab::Metrics).to receive(:counter)
+ .with(:sidekiq_watchdog_running_jobs_total, anything)
+ .and_return(sidekiq_watchdog_running_jobs_counter)
+ allow(sidekiq_watchdog_running_jobs_counter).to receive(:increment)
+ allow(logger).to receive(:warn)
+
+ allow(sidekiq_daemon_monitor).to receive(:jobs).and_return(running_jobs)
+ end
+
+ it 'delegates #strikes_exceeded with correct arguments' do
+ is_expected.to delegate_method(:strikes_exceeded).to(:event_reporter)
+ .with_arguments(
+ :monitor_name,
+ {
+ message: 'dummy_text',
+ running_jobs: [jid: jid, worker_class: 'DummyWorker']
+ }
+ )
+ end
+
+ it 'increment running jobs counter' do
+ expect(sidekiq_watchdog_running_jobs_counter).to receive(:increment)
+ .with({ worker_class: "DummyWorker" })
+
+ sidekiq_event_reporter.strikes_exceeded(:monitor_name, { message: 'dummy_text' })
+ end
+ end
+end
diff --git a/spec/lib/gitlab/memory/watchdog_spec.rb b/spec/lib/gitlab/memory/watchdog_spec.rb
index 5d9599d6eab..668ea36d420 100644
--- a/spec/lib/gitlab/memory/watchdog_spec.rb
+++ b/spec/lib/gitlab/memory/watchdog_spec.rb
@@ -2,15 +2,13 @@
require 'spec_helper'
-RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures do
+RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, feature_category: :application_performance do
context 'watchdog' do
let(:configuration) { instance_double(described_class::Configuration) }
let(:handler) { instance_double(described_class::NullHandler) }
- let(:logger) { instance_double(::Logger) }
+ let(:reporter) { instance_double(described_class::EventReporter) }
let(:sleep_time_seconds) { 60 }
let(:threshold_violated) { false }
- let(:violations_counter) { instance_double(::Prometheus::Client::Counter) }
- let(:violations_handled_counter) { instance_double(::Prometheus::Client::Counter) }
let(:watchdog_iterations) { 1 }
let(:name) { :monitor_name }
let(:payload) { { message: 'dummy_text' } }
@@ -37,18 +35,6 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures do
end
end
- def stub_prometheus_metrics
- allow(Gitlab::Metrics).to receive(:counter)
- .with(:gitlab_memwd_violations_total, anything, anything)
- .and_return(violations_counter)
- allow(Gitlab::Metrics).to receive(:counter)
- .with(:gitlab_memwd_violations_handled_total, anything, anything)
- .and_return(violations_handled_counter)
-
- allow(violations_counter).to receive(:increment)
- allow(violations_handled_counter).to receive(:increment)
- end
-
describe '#initialize' do
it 'initialize new configuration' do
expect(described_class::Configuration).to receive(:new)
@@ -59,33 +45,25 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures do
describe '#call' do
before do
- stub_prometheus_metrics
- allow(Gitlab::Metrics::System).to receive(:memory_usage_rss).at_least(:once).and_return(
- total: 1024
- )
- allow(::Prometheus::PidProvider).to receive(:worker_id).and_return('worker_1')
-
watchdog.configure do |config|
config.handler = handler
- config.logger = logger
+ config.event_reporter = reporter
config.sleep_time_seconds = sleep_time_seconds
- config.monitors.push monitor_class, threshold_violated, payload, max_strikes: max_strikes
end
allow(handler).to receive(:call).and_return(true)
- allow(logger).to receive(:info)
- allow(logger).to receive(:warn)
+ allow(reporter).to receive(:started)
+ allow(reporter).to receive(:stopped)
+ allow(reporter).to receive(:threshold_violated)
+ allow(reporter).to receive(:strikes_exceeded)
end
- it 'logs start message once' do
- expect(logger).to receive(:info).once
+ it 'reports started event once' do
+ expect(reporter).to receive(:started).once
.with(
- pid: Process.pid,
- worker_id: 'worker_1',
memwd_handler_class: handler.class.name,
- memwd_sleep_time_s: sleep_time_seconds,
- memwd_rss_bytes: 1024,
- message: 'started')
+ memwd_sleep_time_s: sleep_time_seconds
+ )
watchdog.call
end
@@ -96,55 +74,50 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures do
watchdog.call
end
- context 'when gitlab_memory_watchdog ops toggle is off' do
- before do
- stub_feature_flags(gitlab_memory_watchdog: false)
- end
-
- it 'does not trigger any monitor' do
- expect(configuration).not_to receive(:monitors)
- end
- end
-
- context 'when process does not exceed threshold' do
- it 'does not increment violations counters' do
- expect(violations_counter).not_to receive(:increment)
- expect(violations_handled_counter).not_to receive(:increment)
-
- watchdog.call
- end
-
- it 'does not log violation' do
- expect(logger).not_to receive(:warn)
-
- watchdog.call
- end
-
- it 'does not execute handler' do
- expect(handler).not_to receive(:call)
+ context 'when no monitors are configured' do
+ it 'reports stopped event once with correct reason' do
+ expect(reporter).to receive(:stopped).once
+ .with(
+ memwd_handler_class: handler.class.name,
+ memwd_sleep_time_s: sleep_time_seconds,
+ memwd_reason: 'monitors are not configured'
+ )
watchdog.call
end
end
- context 'when process exceeds threshold' do
- let(:threshold_violated) { true }
+ context 'when monitors are configured' do
+ before do
+ watchdog.configure do |config|
+ config.monitors.push monitor_class, threshold_violated, payload, max_strikes: max_strikes
+ end
+ end
- it 'increments violations counter' do
- expect(violations_counter).to receive(:increment).with(reason: name)
+ it 'reports stopped event once' do
+ expect(reporter).to receive(:stopped).once
+ .with(
+ memwd_handler_class: handler.class.name,
+ memwd_sleep_time_s: sleep_time_seconds
+ )
watchdog.call
end
- context 'when process does not exceed the allowed number of strikes' do
- it 'does not increment handled violations counter' do
- expect(violations_handled_counter).not_to receive(:increment)
+ context 'when gitlab_memory_watchdog ops toggle is off' do
+ before do
+ stub_feature_flags(gitlab_memory_watchdog: false)
+ end
- watchdog.call
+ it 'does not trigger any monitor' do
+ expect(configuration).not_to receive(:monitors)
end
+ end
- it 'does not log violation' do
- expect(logger).not_to receive(:warn)
+ context 'when process does not exceed threshold' do
+ it 'does not report violations event' do
+ expect(reporter).not_to receive(:threshold_violated)
+ expect(reporter).not_to receive(:strikes_exceeded)
watchdog.call
end
@@ -156,81 +129,94 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures do
end
end
- context 'when monitor exceeds the allowed number of strikes' do
- let(:max_strikes) { 0 }
+ context 'when process exceeds threshold' do
+ let(:threshold_violated) { true }
- it 'increments handled violations counter' do
- expect(violations_handled_counter).to receive(:increment).with(reason: name)
+ it 'reports threshold violated event' do
+ expect(reporter).to receive(:threshold_violated).with(name)
watchdog.call
end
- it 'logs violation' do
- expect(logger).to receive(:warn)
- .with(
- pid: Process.pid,
- worker_id: 'worker_1',
- memwd_handler_class: handler.class.name,
- memwd_sleep_time_s: sleep_time_seconds,
- memwd_rss_bytes: 1024,
- memwd_cur_strikes: 1,
- memwd_max_strikes: max_strikes,
- message: 'dummy_text')
+ context 'when process does not exceed the allowed number of strikes' do
+ it 'does not report strikes exceeded event' do
+ expect(reporter).not_to receive(:strikes_exceeded)
- watchdog.call
- end
+ watchdog.call
+ end
- it 'executes handler' do
- expect(handler).to receive(:call)
+ it 'does not execute handler' do
+ expect(handler).not_to receive(:call)
- watchdog.call
+ watchdog.call
+ end
end
- context 'when enforce_memory_watchdog ops toggle is off' do
- before do
- stub_feature_flags(enforce_memory_watchdog: false)
+ context 'when monitor exceeds the allowed number of strikes' do
+ let(:max_strikes) { 0 }
+
+ it 'reports strikes exceeded event' do
+ expect(reporter).to receive(:strikes_exceeded)
+ .with(
+ name,
+ memwd_handler_class: handler.class.name,
+ memwd_sleep_time_s: sleep_time_seconds,
+ memwd_cur_strikes: 1,
+ memwd_max_strikes: max_strikes,
+ message: "dummy_text"
+ )
+
+ watchdog.call
end
- it 'always uses the NullHandler' do
- expect(handler).not_to receive(:call)
- expect(described_class::NullHandler.instance).to receive(:call).and_return(true)
+ it 'executes handler and stops the watchdog' do
+ expect(handler).to receive(:call).and_return(true)
+ expect(reporter).to receive(:stopped).once
+ .with(
+ memwd_handler_class: handler.class.name,
+ memwd_sleep_time_s: sleep_time_seconds,
+ memwd_reason: 'successfully handled'
+ )
watchdog.call
end
- end
- context 'when multiple monitors exceeds allowed number of strikes' do
- before do
- watchdog.configure do |config|
- config.handler = handler
- config.logger = logger
- config.sleep_time_seconds = sleep_time_seconds
- config.monitors.push monitor_class, threshold_violated, payload, max_strikes: max_strikes
- config.monitors.push monitor_class, threshold_violated, payload, max_strikes: max_strikes
+ it 'schedules a heap dump' do
+ expect(Gitlab::Memory::Reports::HeapDump).to receive(:enqueue!)
+
+ watchdog.call
+ end
+
+ context 'when enforce_memory_watchdog ops toggle is off' do
+ before do
+ stub_feature_flags(enforce_memory_watchdog: false)
+ end
+
+ it 'always uses the NullHandler' do
+ expect(handler).not_to receive(:call)
+ expect(described_class::NullHandler.instance).to receive(:call).and_return(true)
+
+ watchdog.call
end
end
- it 'only calls the handler once' do
- expect(handler).to receive(:call).once.and_return(true)
+ context 'when multiple monitors exceeds allowed number of strikes' do
+ before do
+ watchdog.configure do |config|
+ config.monitors.push monitor_class, threshold_violated, payload, max_strikes: max_strikes
+ config.monitors.push monitor_class, threshold_violated, payload, max_strikes: max_strikes
+ end
+ end
+
+ it 'only calls the handler once' do
+ expect(handler).to receive(:call).once.and_return(true)
- watchdog.call
+ watchdog.call
+ end
end
end
end
end
-
- it 'logs stop message once' do
- expect(logger).to receive(:info).once
- .with(
- pid: Process.pid,
- worker_id: 'worker_1',
- memwd_handler_class: handler.class.name,
- memwd_sleep_time_s: sleep_time_seconds,
- memwd_rss_bytes: 1024,
- message: 'stopped')
-
- watchdog.call
- end
end
describe '#configure' do
@@ -255,6 +241,10 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures do
subject(:handler) { described_class::TermProcessHandler.new(42) }
describe '#call' do
+ before do
+ allow(Process).to receive(:kill)
+ end
+
it 'sends SIGTERM to the current process' do
expect(Process).to receive(:kill).with(:TERM, 42)
@@ -274,11 +264,12 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures do
before do
stub_const('::Puma::Cluster::WorkerHandle', puma_worker_handle_class)
+ allow(puma_worker_handle_class).to receive(:new).and_return(puma_worker_handle)
+ allow(puma_worker_handle).to receive(:term)
end
describe '#call' do
it 'invokes orderly termination via Puma API' do
- expect(puma_worker_handle_class).to receive(:new).and_return(puma_worker_handle)
expect(puma_worker_handle).to receive(:term)
expect(handler.call).to be(true)
diff --git a/spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb b/spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb
deleted file mode 100644
index ad528dca81a..00000000000
--- a/spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb
+++ /dev/null
@@ -1,694 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::MergeRequests::CommitMessageGenerator do
- let(:merge_commit_template) { nil }
- let(:squash_commit_template) { nil }
- let(:project) do
- create(
- :project,
- :public,
- :repository,
- merge_commit_template: merge_commit_template,
- squash_commit_template: squash_commit_template
- )
- end
-
- let(:current_user) { create(:user, name: 'John Doe', email: 'john.doe@example.com') }
- let(:author) { project.creator }
- let(:source_branch) { 'feature' }
- let(:merge_request_description) { "Merge Request Description\nNext line" }
- let(:merge_request_title) { 'Bugfix' }
- let(:merge_request) do
- create(
- :merge_request,
- :simple,
- source_project: project,
- target_project: project,
- target_branch: 'master',
- source_branch: source_branch,
- author: author,
- description: merge_request_description,
- title: merge_request_title
- )
- end
-
- subject { described_class.new(merge_request: merge_request, current_user: current_user) }
-
- shared_examples_for 'commit message with template' do |message_template_name|
- it 'returns nil when template is not set in target project' do
- expect(result_message).to be_nil
- end
-
- context 'when project has custom commit template' do
- let(message_template_name) { <<~MSG.rstrip }
- %{title}
-
- See merge request %{reference}
- MSG
-
- it 'uses custom template' do
- expect(result_message).to eq <<~MSG.rstrip
- Bugfix
-
- See merge request #{merge_request.to_reference(full: true)}
- MSG
- end
- end
-
- context 'when project has commit template with only the title' do
- let(:merge_request) do
- double(:merge_request, title: 'Fixes', target_project: project, to_reference: '!123', metrics: nil, merge_user: nil)
- end
-
- let(message_template_name) { '%{title}' }
-
- it 'evaluates only necessary variables' do
- expect(result_message).to eq 'Fixes'
- expect(merge_request).not_to have_received(:to_reference)
- end
- end
-
- context 'when project has commit template with closed issues' do
- let(message_template_name) { <<~MSG.rstrip }
- Merge branch '%{source_branch}' into '%{target_branch}'
-
- %{title}
-
- %{issues}
-
- See merge request %{reference}
- MSG
-
- it 'omits issues and new lines when no issues are mentioned in description' do
- expect(result_message).to eq <<~MSG.rstrip
- Merge branch 'feature' into 'master'
-
- Bugfix
-
- See merge request #{merge_request.to_reference(full: true)}
- MSG
- end
-
- context 'when MR closes issues' do
- let(:issue_1) { create(:issue, project: project) }
- let(:issue_2) { create(:issue, project: project) }
- let(:merge_request_description) { "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}" }
-
- it 'includes them and keeps new line characters' do
- expect(result_message).to eq <<~MSG.rstrip
- Merge branch 'feature' into 'master'
-
- Bugfix
-
- Closes #{issue_1.to_reference} and #{issue_2.to_reference}
-
- See merge request #{merge_request.to_reference(full: true)}
- MSG
- end
- end
- end
-
- context 'when project has commit template with description' do
- let(message_template_name) { <<~MSG.rstrip }
- Merge branch '%{source_branch}' into '%{target_branch}'
-
- %{title}
-
- %{description}
-
- See merge request %{reference}
- MSG
-
- it 'uses template' do
- expect(result_message).to eq <<~MSG.rstrip
- Merge branch 'feature' into 'master'
-
- Bugfix
-
- Merge Request Description
- Next line
-
- See merge request #{merge_request.to_reference(full: true)}
- MSG
- end
-
- context 'when description is empty string' do
- let(:merge_request_description) { '' }
-
- it 'skips description placeholder and removes new line characters before it' do
- expect(result_message).to eq <<~MSG.rstrip
- Merge branch 'feature' into 'master'
-
- Bugfix
-
- See merge request #{merge_request.to_reference(full: true)}
- MSG
- end
- end
-
- context 'when description is nil' do
- let(:merge_request_description) { nil }
-
- it 'skips description placeholder and removes new line characters before it' do
- expect(result_message).to eq <<~MSG.rstrip
- Merge branch 'feature' into 'master'
-
- Bugfix
-
- See merge request #{merge_request.to_reference(full: true)}
- MSG
- end
- end
-
- context 'when description is blank string' do
- let(:merge_request_description) { "\n\r \n" }
-
- it 'skips description placeholder and removes new line characters before it' do
- expect(result_message).to eq <<~MSG.rstrip
- Merge branch 'feature' into 'master'
-
- Bugfix
-
- See merge request #{merge_request.to_reference(full: true)}
- MSG
- end
- end
- end
-
- context 'when custom commit template contains placeholder in the middle or beginning of the line' do
- let(message_template_name) { <<~MSG.rstrip }
- Merge branch '%{source_branch}' into '%{target_branch}'
-
- %{description} %{title}
-
- See merge request %{reference}
- MSG
-
- it 'uses custom template' do
- expect(result_message).to eq <<~MSG.rstrip
- Merge branch 'feature' into 'master'
-
- Merge Request Description
- Next line Bugfix
-
- See merge request #{merge_request.to_reference(full: true)}
- MSG
- end
-
- context 'when description is empty string' do
- let(:merge_request_description) { '' }
-
- it 'does not remove new line characters before empty placeholder' do
- expect(result_message).to eq <<~MSG.rstrip
- Merge branch 'feature' into 'master'
-
- Bugfix
-
- See merge request #{merge_request.to_reference(full: true)}
- MSG
- end
- end
- end
-
- context 'when project has template with CRLF newlines' do
- let(message_template_name) do
- "Merge branch '%{source_branch}' into '%{target_branch}'\r\n\r\n%{title}\r\n\r\n%{description}\r\n\r\nSee merge request %{reference}"
- end
-
- it 'converts it to LF newlines' do
- expect(result_message).to eq <<~MSG.rstrip
- Merge branch 'feature' into 'master'
-
- Bugfix
-
- Merge Request Description
- Next line
-
- See merge request #{merge_request.to_reference(full: true)}
- MSG
- end
-
- context 'when description is empty string' do
- let(:merge_request_description) { '' }
-
- it 'skips description placeholder and removes new line characters before it' do
- expect(result_message).to eq <<~MSG.rstrip
- Merge branch 'feature' into 'master'
-
- Bugfix
-
- See merge request #{merge_request.to_reference(full: true)}
- MSG
- end
- end
-
- context 'when project has merge commit template with first_commit' do
- let(message_template_name) { <<~MSG.rstrip }
- Message: %{first_commit}
- MSG
-
- it 'uses first commit' do
- expect(result_message).to eq <<~MSG.rstrip
- Message: Feature added
-
- Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
- MSG
- end
-
- context 'when branch has no unmerged commits' do
- let(:source_branch) { 'v1.1.0' }
-
- it 'is an empty string' do
- expect(result_message).to eq 'Message: '
- end
- end
- end
-
- context 'when project has merge commit template with first_multiline_commit' do
- let(message_template_name) { <<~MSG.rstrip }
- Message: %{first_multiline_commit}
- MSG
-
- it 'uses first multiline commit' do
- expect(result_message).to eq <<~MSG.rstrip
- Message: Feature added
-
- Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
- MSG
- end
-
- context 'when branch has no multiline commits' do
- let(:source_branch) { 'spooky-stuff' }
-
- it 'is mr title' do
- expect(result_message).to eq 'Message: Bugfix'
- end
- end
- end
- end
-
- context 'when project has merge commit template with approvers' do
- let(:user1) { create(:user) }
- let(:user2) { create(:user) }
- let(message_template_name) { <<~MSG.rstrip }
- Merge branch '%{source_branch}' into '%{target_branch}'
-
- %{approved_by}
- MSG
-
- context 'and mr has no approval' do
- before do
- merge_request.approved_by_users = []
- end
-
- it 'removes variable and blank line' do
- expect(result_message).to eq <<~MSG.rstrip
- Merge branch 'feature' into 'master'
- MSG
- end
-
- context 'when there is blank line after approved_by' do
- let(message_template_name) { <<~MSG.rstrip }
- Merge branch '%{source_branch}' into '%{target_branch}'
-
- %{approved_by}
-
- Type: merge
- MSG
-
- it 'removes blank line before it' do
- expect(result_message).to eq <<~MSG.rstrip
- Merge branch 'feature' into 'master'
-
- Type: merge
- MSG
- end
- end
-
- context 'when there is no blank line after approved_by' do
- let(message_template_name) { <<~MSG.rstrip }
- Merge branch '%{source_branch}' into '%{target_branch}'
-
- %{approved_by}
- Type: merge
- MSG
-
- it 'does not remove blank line before it' do
- expect(result_message).to eq <<~MSG.rstrip
- Merge branch 'feature' into 'master'
-
- Type: merge
- MSG
- end
- end
- end
-
- context 'and mr has one approval' do
- before do
- merge_request.approved_by_users = [user1]
- end
-
- it 'returns user name and email' do
- expect(result_message).to eq <<~MSG.rstrip
- Merge branch 'feature' into 'master'
-
- Approved-by: #{user1.name} <#{user1.email}>
- MSG
- end
- end
-
- context 'and mr has multiple approvals' do
- before do
- merge_request.approved_by_users = [user1, user2]
- end
-
- it 'returns users names and emails' do
- expect(result_message).to eq <<~MSG.rstrip
- Merge branch 'feature' into 'master'
-
- Approved-by: #{user1.name} <#{user1.email}>
- Approved-by: #{user2.name} <#{user2.email}>
- MSG
- end
- end
- end
-
- context 'when project has merge commit template with url' do
- let(message_template_name) do
- "Merge Request URL is '%{url}'"
- end
-
- context "and merge request has url" do
- it "returns mr url" do
- expect(result_message).to eq <<~MSG.rstrip
- Merge Request URL is '#{Gitlab::UrlBuilder.build(merge_request)}'
- MSG
- end
- end
- end
-
- context 'when project has merge commit template with merged_by' do
- let(message_template_name) do
- "Merge Request merged by '%{merged_by}'"
- end
-
- context "and current_user is passed" do
- it "returns user name and email" do
- expect(result_message).to eq <<~MSG.rstrip
- Merge Request merged by '#{current_user.name} <#{current_user.email}>'
- MSG
- end
- end
- end
-
- context 'when project has commit template with all_commits' do
- let(message_template_name) { "All commits:\n%{all_commits}" }
-
- it 'returns all commit messages' do
- expect(result_message).to eq <<~MSG.rstrip
- All commits:
- * Feature added
-
- Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
- MSG
- end
-
- context 'with 2 commits' do
- let(:source_branch) { 'fix' }
-
- it 'returns both messages' do
- expect(result_message).to eq <<~MSG.rstrip
- All commits:
- * Test file for directories with a leading dot
-
- * JS fix
-
- Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
- MSG
- end
- end
-
- context 'with over 100 commits' do
- let(:source_branch) { 'signed-commits' }
-
- it 'returns first 100 commits skipping merge commit' do
- expected_message = <<~MSG
- All commits:
- * Multiple signatures commit
-
- * Add conflicting file
-
- * Add conflicting file
-
- MSG
- expected_message += (5..100).to_a.reverse
- .map { |n| "* Unrelated signed commit #{n} to exceed page size of endpoint\n\n" }
- .join.rstrip
- expect(result_message).to eq expected_message
- end
- end
-
- context 'when branch has no unmerged commits' do
- let(:source_branch) { 'v1.1.0' }
-
- it 'is an empty string' do
- expect(result_message).to eq "All commits:\n"
- end
- end
-
- context 'when branch has commit with message over 100kb' do
- let(:source_branch) { 'add_commit_with_5mb_subject' }
-
- it 'skips commit body' do
- expect(result_message).to eq <<~MSG.rstrip
- All commits:
- * Commit with 5MB text subject
-
- -- Skipped commit body exceeding 100KiB in size.
-
- * Correct test_env.rb path for adding branch
-
- * Add file with a _flattable_ path
-
-
- (cherry picked from commit ce369011c189f62c815f5971d096b26759bab0d1)
-
- * Add file larger than 1 mb
-
- In order to test Max File Size push rule we need a file larger than 1 MB
-
- * LFS tracks "*.lfs" through .gitattributes
-
- * Update README.md to include `Usage in testing and development`
- MSG
- end
- end
- end
-
- context 'user' do
- subject { described_class.new(merge_request: merge_request, current_user: nil) }
-
- let(:user1) { create(:user) }
- let(:user2) { create(:user) }
- let(message_template_name) do
- "Merge Request merged by '%{merged_by}'"
- end
-
- context 'comes from metrics' do
- before do
- merge_request.metrics.merged_by = user1
- end
-
- it "returns user name and email" do
- expect(result_message).to eq <<~MSG.rstrip
- Merge Request merged by '#{user1.name} <#{user1.email}>'
- MSG
- end
- end
-
- context 'comes from merge_user' do
- before do
- merge_request.merge_user = user2
- end
-
- it "returns user name and email" do
- expect(result_message).to eq <<~MSG.rstrip
- Merge Request merged by '#{user2.name} <#{user2.email}>'
- MSG
- end
- end
- end
-
- context 'when project has commit template with the same variable used twice' do
- let(message_template_name) { '%{title} %{title}' }
-
- it 'uses custom template' do
- expect(result_message).to eq 'Bugfix Bugfix'
- end
- end
-
- context 'when project has commit template without any variable' do
- let(message_template_name) { 'static text' }
-
- it 'uses custom template' do
- expect(result_message).to eq 'static text'
- end
- end
-
- context 'when project has template with all variables' do
- let(message_template_name) { <<~MSG.rstrip }
- source_branch:%{source_branch}
- target_branch:%{target_branch}
- title:%{title}
- issues:%{issues}
- description:%{description}
- first_commit:%{first_commit}
- first_multiline_commit:%{first_multiline_commit}
- url:%{url}
- approved_by:%{approved_by}
- merged_by:%{merged_by}
- co_authored_by:%{co_authored_by}
- all_commits:%{all_commits}
- MSG
-
- it 'uses custom template' do
- expect(result_message).to eq <<~MSG.rstrip
- source_branch:feature
- target_branch:master
- title:Bugfix
- issues:
- description:Merge Request Description
- Next line
- first_commit:Feature added
-
- Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
- first_multiline_commit:Feature added
-
- Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
- url:#{Gitlab::UrlBuilder.build(merge_request)}
- approved_by:
- merged_by:#{current_user.name} <#{current_user.commit_email_or_default}>
- co_authored_by:Co-authored-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
- all_commits:* Feature added
-
- Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
- MSG
- end
- end
-
- context 'when project has merge commit template with co_authored_by' do
- let(:source_branch) { 'signed-commits' }
- let(message_template_name) { <<~MSG.rstrip }
- %{title}
-
- %{co_authored_by}
- MSG
-
- it 'uses custom template' do
- expect(result_message).to eq <<~MSG.rstrip
- Bugfix
-
- Co-authored-by: Nannie Bernhard <nannie.bernhard@example.com>
- Co-authored-by: Winnie Hellmann <winnie@gitlab.com>
- MSG
- end
-
- context 'when author and merging user is one of the commit authors' do
- let(:author) { create(:user, email: 'nannie.bernhard@example.com') }
-
- before do
- merge_request.merge_user = author
- end
-
- it 'skips his mail in coauthors' do
- expect(result_message).to eq <<~MSG.rstrip
- Bugfix
-
- Co-authored-by: Winnie Hellmann <winnie@gitlab.com>
- MSG
- end
- end
-
- context 'when author and merging user is the only author of commits' do
- let(:author) { create(:user, email: 'dmitriy.zaporozhets@gmail.com') }
- let(:source_branch) { 'feature' }
-
- before do
- merge_request.merge_user = author
- end
-
- it 'skips coauthors and empty lines before it' do
- expect(result_message).to eq <<~MSG.rstrip
- Bugfix
- MSG
- end
- end
- end
- end
-
- describe '#merge_message' do
- let(:result_message) { subject.merge_message }
-
- it_behaves_like 'commit message with template', :merge_commit_template
-
- context 'when project has merge commit template with co_authored_by' do
- let(:source_branch) { 'signed-commits' }
- let(:merge_commit_template) { <<~MSG.rstrip }
- %{title}
-
- %{co_authored_by}
- MSG
-
- context 'when author and merging user are one of the commit authors' do
- let(:author) { create(:user, email: 'nannie.bernhard@example.com') }
- let(:merge_user) { create(:user, email: 'winnie@gitlab.com') }
-
- before do
- merge_request.merge_user = merge_user
- end
-
- it 'skips merging user, but does not skip merge request author' do
- expect(result_message).to eq <<~MSG.rstrip
- Bugfix
-
- Co-authored-by: Nannie Bernhard <nannie.bernhard@example.com>
- MSG
- end
- end
- end
- end
-
- describe '#squash_message' do
- let(:result_message) { subject.squash_message }
-
- it_behaves_like 'commit message with template', :squash_commit_template
-
- context 'when project has merge commit template with co_authored_by' do
- let(:source_branch) { 'signed-commits' }
- let(:squash_commit_template) { <<~MSG.rstrip }
- %{title}
-
- %{co_authored_by}
- MSG
-
- context 'when author and merging user are one of the commit authors' do
- let(:author) { create(:user, email: 'nannie.bernhard@example.com') }
- let(:merge_user) { create(:user, email: 'winnie@gitlab.com') }
-
- before do
- merge_request.merge_user = merge_user
- end
-
- it 'skips merge request author, but does not skip merging user' do
- expect(result_message).to eq <<~MSG.rstrip
- Bugfix
-
- Co-authored-by: Winnie Hellmann <winnie@gitlab.com>
- MSG
- end
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/merge_requests/message_generator_spec.rb b/spec/lib/gitlab/merge_requests/message_generator_spec.rb
new file mode 100644
index 00000000000..59aaffc4377
--- /dev/null
+++ b/spec/lib/gitlab/merge_requests/message_generator_spec.rb
@@ -0,0 +1,881 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::MergeRequests::MessageGenerator, feature_category: :code_review do
+ let(:merge_commit_template) { nil }
+ let(:squash_commit_template) { nil }
+ let(:project) do
+ create(
+ :project,
+ :public,
+ :repository,
+ merge_commit_template: merge_commit_template,
+ squash_commit_template: squash_commit_template
+ )
+ end
+
+ let(:current_user) { create(:user, name: 'John Doe', email: 'john.doe@example.com') }
+ let(:author) { project.creator }
+ let(:source_branch) { 'feature' }
+ let(:merge_request_description) { "Merge Request Description\nNext line" }
+ let(:merge_request_title) { 'Bugfix' }
+ let(:merge_request) do
+ create(
+ :merge_request,
+ :simple,
+ source_project: project,
+ target_project: project,
+ target_branch: 'master',
+ source_branch: source_branch,
+ author: author,
+ description: merge_request_description,
+ title: merge_request_title
+ )
+ end
+
+ subject { described_class.new(merge_request: merge_request, current_user: current_user) }
+
+ shared_examples_for 'commit message with template' do |message_template_name|
+ it 'returns nil when template is not set in target project' do
+ expect(result_message).to be_nil
+ end
+
+ context 'when project has custom commit template' do
+ let(message_template_name) { <<~MSG.rstrip }
+ %{title}
+
+ See merge request %{reference}
+ MSG
+
+ it 'uses custom template' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Bugfix
+
+ See merge request #{merge_request.to_reference(full: true)}
+ MSG
+ end
+ end
+
+ context 'when project has commit template with only the title' do
+ let(:merge_request) do
+ double(
+ :merge_request,
+ title: 'Fixes',
+ target_project: project,
+ to_reference: '!123',
+ metrics: nil,
+ merge_user: nil
+ )
+ end
+
+ let(message_template_name) { '%{title}' }
+
+ it 'evaluates only necessary variables' do
+ expect(result_message).to eq 'Fixes'
+ expect(merge_request).not_to have_received(:to_reference)
+ end
+ end
+
+ context 'when project has commit template with closed issues' do
+ let(message_template_name) { <<~MSG.rstrip }
+ Merge branch '%{source_branch}' into '%{target_branch}'
+
+ %{title}
+
+ %{issues}
+
+ See merge request %{reference}
+ MSG
+
+ it 'omits issues and new lines when no issues are mentioned in description' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Bugfix
+
+ See merge request #{merge_request.to_reference(full: true)}
+ MSG
+ end
+
+ context 'when MR closes issues' do
+ let(:issue_1) { create(:issue, project: project) }
+ let(:issue_2) { create(:issue, project: project) }
+ let(:merge_request_description) { "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}" }
+
+ it 'includes them and keeps new line characters' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Bugfix
+
+ Closes #{issue_1.to_reference} and #{issue_2.to_reference}
+
+ See merge request #{merge_request.to_reference(full: true)}
+ MSG
+ end
+ end
+ end
+
+ context 'when project has commit template with description' do
+ let(message_template_name) { <<~MSG.rstrip }
+ Merge branch '%{source_branch}' into '%{target_branch}'
+
+ %{title}
+
+ %{description}
+
+ See merge request %{reference}
+ MSG
+
+ it 'uses template' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Bugfix
+
+ Merge Request Description
+ Next line
+
+ See merge request #{merge_request.to_reference(full: true)}
+ MSG
+ end
+
+ context 'when description is empty string' do
+ let(:merge_request_description) { '' }
+
+ it 'skips description placeholder and removes new line characters before it' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Bugfix
+
+ See merge request #{merge_request.to_reference(full: true)}
+ MSG
+ end
+ end
+
+ context 'when description is nil' do
+ let(:merge_request_description) { nil }
+
+ it 'skips description placeholder and removes new line characters before it' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Bugfix
+
+ See merge request #{merge_request.to_reference(full: true)}
+ MSG
+ end
+ end
+
+ context 'when description is blank string' do
+ let(:merge_request_description) { "\n\r \n" }
+
+ it 'skips description placeholder and removes new line characters before it' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Bugfix
+
+ See merge request #{merge_request.to_reference(full: true)}
+ MSG
+ end
+ end
+ end
+
+ context 'when custom commit template contains placeholder in the middle or beginning of the line' do
+ let(message_template_name) { <<~MSG.rstrip }
+ Merge branch '%{source_branch}' into '%{target_branch}'
+
+ %{description} %{title}
+
+ See merge request %{reference}
+ MSG
+
+ it 'uses custom template' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Merge Request Description
+ Next line Bugfix
+
+ See merge request #{merge_request.to_reference(full: true)}
+ MSG
+ end
+
+ context 'when description is empty string' do
+ let(:merge_request_description) { '' }
+
+ it 'does not remove new line characters before empty placeholder' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Bugfix
+
+ See merge request #{merge_request.to_reference(full: true)}
+ MSG
+ end
+ end
+ end
+
+ context 'when project has template with CRLF newlines' do
+ let(message_template_name) do
+ "Merge branch '%{source_branch}' into '%{target_branch}'\r\n\r\n%{title}\r\n\r\n%{description}\r\n\r\nSee merge request %{reference}" # rubocop: disable Layout/LineLength
+ end
+
+ it 'converts it to LF newlines' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Bugfix
+
+ Merge Request Description
+ Next line
+
+ See merge request #{merge_request.to_reference(full: true)}
+ MSG
+ end
+
+ context 'when description is empty string' do
+ let(:merge_request_description) { '' }
+
+ it 'skips description placeholder and removes new line characters before it' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Bugfix
+
+ See merge request #{merge_request.to_reference(full: true)}
+ MSG
+ end
+ end
+
+ context 'when project has merge commit template with first_commit' do
+ let(message_template_name) { <<~MSG.rstrip }
+ Message: %{first_commit}
+ MSG
+
+ it 'uses first commit' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Message: Feature added
+
+ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ MSG
+ end
+
+ context 'when branch has no unmerged commits' do
+ let(:source_branch) { 'v1.1.0' }
+
+ it 'is an empty string' do
+ expect(result_message).to eq 'Message: '
+ end
+ end
+ end
+
+ context 'when project has merge commit template with first_multiline_commit' do
+ let(message_template_name) { <<~MSG.rstrip }
+ Message: %{first_multiline_commit}
+ MSG
+
+ it 'uses first multiline commit' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Message: Feature added
+
+ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ MSG
+ end
+
+ context 'when branch has no multiline commits' do
+ let(:source_branch) { 'spooky-stuff' }
+
+ it 'is mr title' do
+ expect(result_message).to eq 'Message: Bugfix'
+ end
+ end
+ end
+ end
+
+ context 'when project has merge commit template with reviewers' do
+ let(:user1) { create(:user) }
+ let(:user2) { create(:user) }
+ let(message_template_name) { <<~MSG.rstrip }
+ Merge branch '%{source_branch}' into '%{target_branch}'
+
+ %{reviewed_by}
+ MSG
+
+ context 'and mr has no reviewers' do
+ before do
+ merge_request.reviews = []
+ end
+
+ it 'removes variable and blank line' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+ MSG
+ end
+
+ context 'when there is blank line after reviewed_by' do
+ let(message_template_name) { <<~MSG.rstrip }
+ Merge branch '%{source_branch}' into '%{target_branch}'
+
+ %{reviewed_by}
+
+ Type: merge
+ MSG
+
+ it 'removes blank line before it' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Type: merge
+ MSG
+ end
+ end
+
+ context 'when there is no blank line after reviewed_by' do
+ let(message_template_name) { <<~MSG.rstrip }
+ Merge branch '%{source_branch}' into '%{target_branch}'
+
+ %{reviewed_by}
+ Type: merge
+ MSG
+
+ it 'does not remove blank line before it' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Type: merge
+ MSG
+ end
+ end
+ end
+
+ context 'and mr has one reviewer' do
+ before do
+ merge_request.reviews.create!(project: merge_request.project, author: user1)
+ end
+
+ it 'returns user name and email' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Reviewed-by: #{user1.name} <#{user1.email}>
+ MSG
+ end
+ end
+
+ context 'and mr has multiple reviewers' do
+ before do
+ merge_request.reviews.create!(project: merge_request.project, author: user1)
+ merge_request.reviews.create!(project: merge_request.project, author: user2)
+ end
+
+ it 'returns users names and emails' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Reviewed-by: #{user1.name} <#{user1.email}>
+ Reviewed-by: #{user2.name} <#{user2.email}>
+ MSG
+ end
+ end
+ end
+
+ context 'when project has merge commit template with approvers' do
+ let(:user1) { create(:user) }
+ let(:user2) { create(:user) }
+ let(message_template_name) { <<~MSG.rstrip }
+ Merge branch '%{source_branch}' into '%{target_branch}'
+
+ %{approved_by}
+ MSG
+
+ context 'and mr has no approval' do
+ before do
+ merge_request.approved_by_users = []
+ end
+
+ it 'removes variable and blank line' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+ MSG
+ end
+
+ context 'when there is blank line after approved_by' do
+ let(message_template_name) { <<~MSG.rstrip }
+ Merge branch '%{source_branch}' into '%{target_branch}'
+
+ %{approved_by}
+
+ Type: merge
+ MSG
+
+ it 'removes blank line before it' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Type: merge
+ MSG
+ end
+ end
+
+ context 'when there is no blank line after approved_by' do
+ let(message_template_name) { <<~MSG.rstrip }
+ Merge branch '%{source_branch}' into '%{target_branch}'
+
+ %{approved_by}
+ Type: merge
+ MSG
+
+ it 'does not remove blank line before it' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Type: merge
+ MSG
+ end
+ end
+ end
+
+ context 'and mr has one approval' do
+ before do
+ merge_request.approved_by_users = [user1]
+ end
+
+ it 'returns user name and email' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Approved-by: #{user1.name} <#{user1.email}>
+ MSG
+ end
+ end
+
+ context 'and mr has multiple approvals' do
+ before do
+ merge_request.approved_by_users = [user1, user2]
+ end
+
+ it 'returns users names and emails' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge branch 'feature' into 'master'
+
+ Approved-by: #{user1.name} <#{user1.email}>
+ Approved-by: #{user2.name} <#{user2.email}>
+ MSG
+ end
+ end
+ end
+
+ context 'when project has merge commit template with url' do
+ let(message_template_name) do
+ "Merge Request URL is '%{url}'"
+ end
+
+ context "and merge request has url" do
+ it "returns mr url" do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge Request URL is '#{Gitlab::UrlBuilder.build(merge_request)}'
+ MSG
+ end
+ end
+ end
+
+ context 'when project has merge commit template with merged_by' do
+ let(message_template_name) do
+ "Merge Request merged by '%{merged_by}'"
+ end
+
+ context "and current_user is passed" do
+ it "returns user name and email" do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge Request merged by '#{current_user.name} <#{current_user.email}>'
+ MSG
+ end
+ end
+ end
+
+ context 'when project has commit template with all_commits' do
+ let(message_template_name) { "All commits:\n%{all_commits}" }
+
+ it 'returns all commit messages' do
+ expect(result_message).to eq <<~MSG.rstrip
+ All commits:
+ * Feature added
+
+ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ MSG
+ end
+
+ context 'with 2 commits' do
+ let(:source_branch) { 'fix' }
+
+ it 'returns both messages' do
+ expect(result_message).to eq <<~MSG.rstrip
+ All commits:
+ * Test file for directories with a leading dot
+
+ * JS fix
+
+ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ MSG
+ end
+ end
+
+ context 'with over 100 commits' do
+ let(:source_branch) { 'signed-commits' }
+
+ it 'returns first 100 commits skipping merge commit' do
+ expected_message = <<~MSG
+ All commits:
+ * Multiple signatures commit
+
+ * Add conflicting file
+
+ * Add conflicting file
+
+ MSG
+ expected_message += (5..100).to_a.reverse
+ .map { |n| "* Unrelated signed commit #{n} to exceed page size of endpoint\n\n" }
+ .join.rstrip
+ expect(result_message).to eq expected_message
+ end
+ end
+
+ context 'when branch has no unmerged commits' do
+ let(:source_branch) { 'v1.1.0' }
+
+ it 'is an empty string' do
+ expect(result_message).to eq "All commits:\n"
+ end
+ end
+
+ context 'when branch has commit with message over 100kb' do
+ let(:source_branch) { 'add_commit_with_5mb_subject' }
+
+ it 'skips commit body' do
+ expect(result_message).to eq <<~MSG.rstrip
+ All commits:
+ * Commit with 5MB text subject
+
+ -- Skipped commit body exceeding 100KiB in size.
+
+ * Correct test_env.rb path for adding branch
+
+ * Add file with a _flattable_ path
+
+
+ (cherry picked from commit ce369011c189f62c815f5971d096b26759bab0d1)
+
+ * Add file larger than 1 mb
+
+ In order to test Max File Size push rule we need a file larger than 1 MB
+
+ * LFS tracks "*.lfs" through .gitattributes
+
+ * Update README.md to include `Usage in testing and development`
+ MSG
+ end
+ end
+ end
+
+ context 'user' do
+ subject { described_class.new(merge_request: merge_request, current_user: nil) }
+
+ let(:user1) { create(:user) }
+ let(:user2) { create(:user) }
+ let(message_template_name) do
+ "Merge Request merged by '%{merged_by}'"
+ end
+
+ context 'comes from metrics' do
+ before do
+ merge_request.metrics.merged_by = user1
+ end
+
+ it "returns user name and email" do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge Request merged by '#{user1.name} <#{user1.email}>'
+ MSG
+ end
+ end
+
+ context 'comes from merge_user' do
+ before do
+ merge_request.merge_user = user2
+ end
+
+ it "returns user name and email" do
+ expect(result_message).to eq <<~MSG.rstrip
+ Merge Request merged by '#{user2.name} <#{user2.email}>'
+ MSG
+ end
+ end
+ end
+
+ context 'when project has commit template with the same variable used twice' do
+ let(message_template_name) { '%{title} %{title}' }
+
+ it 'uses custom template' do
+ expect(result_message).to eq 'Bugfix Bugfix'
+ end
+ end
+
+ context 'when project has commit template without any variable' do
+ let(message_template_name) { 'static text' }
+
+ it 'uses custom template' do
+ expect(result_message).to eq 'static text'
+ end
+ end
+
+ context 'when project has template with all variables' do
+ let(message_template_name) { <<~MSG.rstrip }
+ source_branch:%{source_branch}
+ target_branch:%{target_branch}
+ title:%{title}
+ issues:%{issues}
+ description:%{description}
+ first_commit:%{first_commit}
+ first_multiline_commit:%{first_multiline_commit}
+ url:%{url}
+ reviewed_by:%{reviewed_by}
+ approved_by:%{approved_by}
+ merged_by:%{merged_by}
+ co_authored_by:%{co_authored_by}
+ all_commits:%{all_commits}
+ MSG
+
+ it 'uses custom template' do
+ expect(result_message).to eq <<~MSG.rstrip
+ source_branch:feature
+ target_branch:master
+ title:Bugfix
+ issues:
+ description:Merge Request Description
+ Next line
+ first_commit:Feature added
+
+ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ first_multiline_commit:Feature added
+
+ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ url:#{Gitlab::UrlBuilder.build(merge_request)}
+ reviewed_by:
+ approved_by:
+ merged_by:#{current_user.name} <#{current_user.commit_email_or_default}>
+ co_authored_by:Co-authored-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ all_commits:* Feature added
+
+ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ MSG
+ end
+ end
+
+ context 'when project has merge commit template with co_authored_by' do
+ let(:source_branch) { 'signed-commits' }
+ let(message_template_name) { <<~MSG.rstrip }
+ %{title}
+
+ %{co_authored_by}
+ MSG
+
+ it 'uses custom template' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Bugfix
+
+ Co-authored-by: Nannie Bernhard <nannie.bernhard@example.com>
+ Co-authored-by: Winnie Hellmann <winnie@gitlab.com>
+ MSG
+ end
+
+ context 'when author and merging user is one of the commit authors' do
+ let(:author) { create(:user, email: 'nannie.bernhard@example.com') }
+
+ before do
+ merge_request.merge_user = author
+ end
+
+ it 'skips his mail in coauthors' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Bugfix
+
+ Co-authored-by: Winnie Hellmann <winnie@gitlab.com>
+ MSG
+ end
+ end
+
+ context 'when author and merging user is the only author of commits' do
+ let(:author) { create(:user, email: 'dmitriy.zaporozhets@gmail.com') }
+ let(:source_branch) { 'feature' }
+
+ before do
+ merge_request.merge_user = author
+ end
+
+ it 'skips coauthors and empty lines before it' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Bugfix
+ MSG
+ end
+ end
+ end
+ end
+
+ describe '#merge_commit_message' do
+ let(:result_message) { subject.merge_commit_message }
+
+ it_behaves_like 'commit message with template', :merge_commit_template
+
+ context 'when project has merge commit template with co_authored_by' do
+ let(:source_branch) { 'signed-commits' }
+ let(:merge_commit_template) { <<~MSG.rstrip }
+ %{title}
+
+ %{co_authored_by}
+ MSG
+
+ context 'when author and merging user are one of the commit authors' do
+ let(:author) { create(:user, email: 'nannie.bernhard@example.com') }
+ let(:merge_user) { create(:user, email: 'winnie@gitlab.com') }
+
+ before do
+ merge_request.merge_user = merge_user
+ end
+
+ it 'skips merging user, but does not skip merge request author' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Bugfix
+
+ Co-authored-by: Nannie Bernhard <nannie.bernhard@example.com>
+ MSG
+ end
+ end
+ end
+ end
+
+ describe '#squash_commit_message' do
+ let(:result_message) { subject.squash_commit_message }
+
+ it_behaves_like 'commit message with template', :squash_commit_template
+
+ context 'when project has merge commit template with co_authored_by' do
+ let(:source_branch) { 'signed-commits' }
+ let(:squash_commit_template) { <<~MSG.rstrip }
+ %{title}
+
+ %{co_authored_by}
+ MSG
+
+ context 'when author and merging user are one of the commit authors' do
+ let(:author) { create(:user, email: 'nannie.bernhard@example.com') }
+ let(:merge_user) { create(:user, email: 'winnie@gitlab.com') }
+
+ before do
+ merge_request.merge_user = merge_user
+ end
+
+ it 'skips merge request author, but does not skip merging user' do
+ expect(result_message).to eq <<~MSG.rstrip
+ Bugfix
+
+ Co-authored-by: Winnie Hellmann <winnie@gitlab.com>
+ MSG
+ end
+ end
+ end
+ end
+
+ describe '#new_mr_description' do
+ let(:merge_request) do
+ build(
+ :merge_request,
+ source_project: project,
+ target_project: project,
+ target_branch: 'master',
+ source_branch: source_branch,
+ author: author,
+ description: merge_request_description,
+ title: merge_request_title
+ )
+ end
+
+ let(:result_message) { subject.new_mr_description }
+
+ before do
+ compare = CompareService.new(
+ project,
+ merge_request.source_branch
+ ).execute(
+ project,
+ merge_request.target_branch
+ )
+
+ merge_request.compare_commits = compare.commits
+ merge_request.compare = compare
+ end
+
+ context 'when project has template with all variables' do
+ let(:merge_request_description) { <<~MSG.rstrip }
+ source_branch:%{source_branch}
+ target_branch:%{target_branch}
+ title:%{title}
+ issues:%{issues}
+ description:%{description}
+ first_commit:%{first_commit}
+ first_multiline_commit:%{first_multiline_commit}
+ url:%{url}
+ approved_by:%{approved_by}
+ merged_by:%{merged_by}
+ co_authored_by:%{co_authored_by}
+ all_commits:%{all_commits}
+ MSG
+
+ it 'renders only variables specific to a new non-persisted merge request' do
+ expect(result_message).to eq <<~MSG.rstrip
+ source_branch:feature
+ target_branch:master
+ title:
+ issues:
+ description:
+ first_commit:Feature added
+
+ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ first_multiline_commit:Feature added
+
+ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ url:
+ approved_by:
+ merged_by:
+ co_authored_by:Co-authored-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ all_commits:* Feature added
+
+ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ MSG
+ end
+
+ context 'when no first commit exists' do
+ let(:source_branch) { 'master' }
+
+ it 'does not populate any commit-related variables' do
+ expect(result_message).to eq <<~MSG.rstrip
+ source_branch:master
+ target_branch:master
+ title:
+ issues:
+ description:
+ first_commit:
+ first_multiline_commit:Bugfix
+ url:
+ approved_by:
+ merged_by:
+ co_authored_by:
+ all_commits:
+ MSG
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/metrics/dashboard/validator_spec.rb b/spec/lib/gitlab/metrics/dashboard/validator_spec.rb
index aaa9daf8fee..fb55b736354 100644
--- a/spec/lib/gitlab/metrics/dashboard/validator_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/validator_spec.rb
@@ -143,56 +143,4 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator do
end
end
end
-
- describe '#errors' do
- context 'valid dashboard schema' do
- it 'returns no errors' do
- expect(described_class.errors(valid_dashboard)).to eq []
- end
-
- context 'with duplicate metric_ids' do
- it 'returns errors' do
- expect(described_class.errors(duplicate_id_dashboard)).to eq [Gitlab::Metrics::Dashboard::Validator::Errors::DuplicateMetricIds.new]
- end
- end
-
- context 'with dashboard_path and project' do
- subject { described_class.errors(valid_dashboard, dashboard_path: 'test/path.yml', project: project) }
-
- context 'with no conflicting metric identifiers in db' do
- it { is_expected.to eq [] }
- end
-
- context 'with metric identifier present in current dashboard' do
- before do
- create(:prometheus_metric,
- identifier: 'metric_a1',
- dashboard_path: 'test/path.yml',
- project: project
- )
- end
-
- it { is_expected.to eq [] }
- end
-
- context 'with metric identifier present in another dashboard' do
- before do
- create(:prometheus_metric,
- identifier: 'metric_a1',
- dashboard_path: 'some/other/dashboard/path.yml',
- project: project
- )
- end
-
- it { is_expected.to eq [Gitlab::Metrics::Dashboard::Validator::Errors::DuplicateMetricIds.new] }
- end
- end
- end
-
- context 'invalid dashboard schema' do
- it 'returns collection of validation errors' do
- expect(described_class.errors(invalid_dashboard)).to all be_kind_of(Gitlab::Metrics::Dashboard::Validator::Errors::SchemaValidationError)
- end
- end
- end
end
diff --git a/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb b/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb
index fa50adb4e4f..6673cc50d67 100644
--- a/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb
+++ b/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do
+RSpec.describe Gitlab::Metrics::Exporter::BaseExporter, feature_category: :application_performance do
let(:settings) { double('settings') }
let(:log_enabled) { false }
let(:exporter) { described_class.new(settings, log_enabled: log_enabled, log_file: 'test_exporter.log') }
diff --git a/spec/lib/gitlab/metrics/global_search_slis_spec.rb b/spec/lib/gitlab/metrics/global_search_slis_spec.rb
index c10d83664ea..1aa2c4398a7 100644
--- a/spec/lib/gitlab/metrics/global_search_slis_spec.rb
+++ b/spec/lib/gitlab/metrics/global_search_slis_spec.rb
@@ -5,12 +5,6 @@ require 'spec_helper'
RSpec.describe Gitlab::Metrics::GlobalSearchSlis do
using RSpec::Parameterized::TableSyntax
- let(:error_rate_feature_flag_enabled) { true }
-
- before do
- stub_feature_flags(global_search_error_rate_sli: error_rate_feature_flag_enabled)
- end
-
describe '#initialize_slis!' do
it 'initializes Apdex SLIs for global_search' do
expect(Gitlab::Metrics::Sli::Apdex).to receive(:initialize_sli).with(
@@ -21,27 +15,13 @@ RSpec.describe Gitlab::Metrics::GlobalSearchSlis do
described_class.initialize_slis!
end
- context 'when global_search_error_rate_sli feature flag is enabled' do
- let(:error_rate_feature_flag_enabled) { true }
-
- it 'initializes ErrorRate SLIs for global_search' do
- expect(Gitlab::Metrics::Sli::ErrorRate).to receive(:initialize_sli).with(
- :global_search,
- a_kind_of(Array)
- )
-
- described_class.initialize_slis!
- end
- end
-
- context 'when global_search_error_rate_sli feature flag is disabled' do
- let(:error_rate_feature_flag_enabled) { false }
-
- it 'does not initialize the ErrorRate SLIs for global_search' do
- expect(Gitlab::Metrics::Sli::ErrorRate).not_to receive(:initialize_sli)
+ it 'initializes ErrorRate SLIs for global_search' do
+ expect(Gitlab::Metrics::Sli::ErrorRate).to receive(:initialize_sli).with(
+ :global_search,
+ a_kind_of(Array)
+ )
- described_class.initialize_slis!
- end
+ described_class.initialize_slis!
end
end
@@ -105,34 +85,15 @@ RSpec.describe Gitlab::Metrics::GlobalSearchSlis do
end
describe '#record_error_rate' do
- context 'when global_search_error_rate_sli feature flag is enabled' do
- let(:error_rate_feature_flag_enabled) { true }
-
- it 'calls increment on the error rate SLI' do
- expect(Gitlab::Metrics::Sli::ErrorRate[:global_search]).to receive(:increment)
-
- described_class.record_error_rate(
- error: true,
- search_type: 'basic',
- search_level: 'global',
- search_scope: 'issues'
- )
- end
- end
-
- context 'when global_search_error_rate_sli feature flag is disabled' do
- let(:error_rate_feature_flag_enabled) { false }
-
- it 'does not call increment on the error rate SLI' do
- expect(Gitlab::Metrics::Sli::ErrorRate[:global_search]).not_to receive(:increment)
-
- described_class.record_error_rate(
- error: true,
- search_type: 'basic',
- search_level: 'global',
- search_scope: 'issues'
- )
- end
+ it 'calls increment on the error rate SLI' do
+ expect(Gitlab::Metrics::Sli::ErrorRate[:global_search]).to receive(:increment)
+
+ described_class.record_error_rate(
+ error: true,
+ search_type: 'basic',
+ search_level: 'global',
+ search_scope: 'issues'
+ )
end
end
end
diff --git a/spec/lib/gitlab/metrics/rails_slis_spec.rb b/spec/lib/gitlab/metrics/rails_slis_spec.rb
index b30eb57101f..9da102fb8b8 100644
--- a/spec/lib/gitlab/metrics/rails_slis_spec.rb
+++ b/spec/lib/gitlab/metrics/rails_slis_spec.rb
@@ -14,8 +14,8 @@ RSpec.describe Gitlab::Metrics::RailsSlis do
end
describe '.initialize_request_slis!' do
- it "initializes the SLI for all possible endpoints if they weren't", :aggregate_failures do
- possible_labels = [
+ let(:possible_labels) do
+ [
{
endpoint_id: "GET /api/:version/version",
feature_category: :not_owned,
@@ -27,17 +27,32 @@ RSpec.describe Gitlab::Metrics::RailsSlis do
request_urgency: :default
}
]
+ end
- possible_graphql_labels = ['graphql:foo', 'graphql:bar', 'graphql:unknown'].map do |endpoint_id|
+ let(:possible_graphql_labels) do
+ ['graphql:foo', 'graphql:bar', 'graphql:unknown'].map do |endpoint_id|
{
endpoint_id: endpoint_id,
feature_category: nil,
query_urgency: ::Gitlab::EndpointAttributes::DEFAULT_URGENCY.name
}
end
+ end
+
+ it "initializes the SLI for all possible endpoints if they weren't", :aggregate_failures do
+ expect(Gitlab::Metrics::Sli::Apdex).to receive(:initialize_sli).with(:rails_request, array_including(*possible_labels)).and_call_original
+ expect(Gitlab::Metrics::Sli::Apdex).to receive(:initialize_sli).with(:graphql_query, array_including(*possible_graphql_labels)).and_call_original
+ expect(Gitlab::Metrics::Sli::ErrorRate).to receive(:initialize_sli).with(:rails_request, array_including(*possible_labels)).and_call_original
+
+ described_class.initialize_request_slis!
+ end
+
+ it "initializes the SLI for all possible endpoints if they weren't given error rate feature flag is disabled", :aggregate_failures do
+ stub_feature_flags(gitlab_metrics_error_rate_sli: false)
expect(Gitlab::Metrics::Sli::Apdex).to receive(:initialize_sli).with(:rails_request, array_including(*possible_labels)).and_call_original
expect(Gitlab::Metrics::Sli::Apdex).to receive(:initialize_sli).with(:graphql_query, array_including(*possible_graphql_labels)).and_call_original
+ expect(Gitlab::Metrics::Sli::ErrorRate).not_to receive(:initialize_sli)
described_class.initialize_request_slis!
end
@@ -51,6 +66,14 @@ RSpec.describe Gitlab::Metrics::RailsSlis do
end
end
+ describe '.request_error' do
+ it 'returns the initialized request error rate SLI object' do
+ described_class.initialize_request_slis!
+
+ expect(described_class.request_error_rate).to be_initialized
+ end
+ end
+
describe '.graphql_query_apdex' do
it 'returns the initialized request apdex SLI object' do
described_class.initialize_request_slis!
diff --git a/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
index ed78548ef62..61c690b85e9 100644
--- a/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
+RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures, feature_category: :error_budgets do
let(:app) { double('app') }
subject { described_class.new(app) }
@@ -38,6 +38,29 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
expect(described_class).to receive_message_chain(:http_request_duration_seconds, :observe).with({ method: 'get' }, a_positive_execution_time)
expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment)
.with(labels: { feature_category: 'unknown', endpoint_id: 'unknown', request_urgency: :default }, success: true)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment)
+ .with(labels: { feature_category: 'unknown', endpoint_id: 'unknown', request_urgency: :default }, error: false)
+
+ subject.call(env)
+ end
+
+ it 'guarantees SLI metrics are incremented with all the required labels' do
+ described_class.initialize_metrics
+
+ expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment).and_call_original
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).and_call_original
+
+ subject.call(env)
+ end
+
+ it 'does not track error rate when feature flag is disabled' do
+ stub_feature_flags(gitlab_metrics_error_rate_sli: false)
+
+ expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: '200', feature_category: 'unknown')
+ expect(described_class).to receive_message_chain(:http_request_duration_seconds, :observe).with({ method: 'get' }, a_positive_execution_time)
+ expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment)
+ .with(labels: { feature_category: 'unknown', endpoint_id: 'unknown', request_urgency: :default }, success: true)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).not_to receive(:increment)
subject.call(env)
end
@@ -84,10 +107,23 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
context '@app.call returns an error code' do
let(:status) { '500' }
- it 'tracks count but not duration or apdex' do
+ it 'tracks count and error rate but not duration and apdex' do
+ expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: '500', feature_category: 'unknown')
+ expect(described_class).not_to receive(:http_request_duration_seconds)
+ expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_apdex)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment)
+ .with(labels: { feature_category: 'unknown', endpoint_id: 'unknown', request_urgency: :default }, error: true)
+
+ subject.call(env)
+ end
+
+ it 'does not track error rate when feature flag is disabled' do
+ stub_feature_flags(gitlab_metrics_error_rate_sli: false)
+
expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: '500', feature_category: 'unknown')
expect(described_class).not_to receive(:http_request_duration_seconds)
expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_apdex)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).not_to receive(:increment)
subject.call(env)
end
@@ -108,6 +144,7 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: 'undefined', feature_category: 'unknown')
expect(described_class.http_request_duration_seconds).not_to receive(:observe)
expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_apdex)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).not_to receive(:increment)
expect { subject.call(env) }.to raise_error(StandardError)
end
@@ -124,6 +161,8 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
expect(described_class).not_to receive(:http_health_requests_total)
expect(Gitlab::Metrics::RailsSlis.request_apdex)
.to receive(:increment).with(labels: { feature_category: 'team_planning', endpoint_id: 'IssuesController#show', request_urgency: :default }, success: true)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment)
+ .with(labels: { feature_category: 'team_planning', endpoint_id: 'IssuesController#show', request_urgency: :default }, error: false)
subject.call(env)
end
@@ -134,6 +173,7 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
expect(described_class).to receive_message_chain(:http_health_requests_total, :increment).with(method: 'get', status: '200')
expect(described_class).not_to receive(:http_requests_total)
expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_apdex)
+ expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_error_rate)
subject.call(env)
end
@@ -147,8 +187,9 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
it 'adds the feature category to the labels for http_requests_total' do
expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: 'undefined', feature_category: 'team_planning')
- expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_apdex)
+ expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_apdex)
+ expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_error_rate)
expect { subject.call(env) }.to raise_error(StandardError)
end
end
@@ -159,6 +200,8 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
expect(described_class).not_to receive(:http_health_requests_total)
expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment)
.with(labels: { feature_category: 'unknown', endpoint_id: 'unknown', request_urgency: :default }, success: true)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment)
+ .with(labels: { feature_category: 'unknown', endpoint_id: 'unknown', request_urgency: :default }, error: false)
subject.call(env)
end
@@ -214,6 +257,15 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
},
success: success
)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).with(
+ labels: {
+ feature_category: 'hello_world',
+ endpoint_id: 'GET /projects/:id/archive',
+ request_urgency: request_urgency_name
+ },
+ error: false
+ )
+
subject.call(env)
end
end
@@ -247,6 +299,15 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
},
success: success
)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).with(
+ labels: {
+ feature_category: 'hello_world',
+ endpoint_id: 'AnonymousController#index',
+ request_urgency: request_urgency_name
+ },
+ error: false
+ )
+
subject.call(env)
end
end
@@ -273,6 +334,14 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
},
success: true
)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).with(
+ labels: {
+ feature_category: 'unknown',
+ endpoint_id: 'unknown',
+ request_urgency: :default
+ },
+ error: false
+ )
subject.call(env)
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(100, 101)
@@ -284,6 +353,14 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
},
success: false
)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).with(
+ labels: {
+ feature_category: 'unknown',
+ endpoint_id: 'unknown',
+ request_urgency: :default
+ },
+ error: false
+ )
subject.call(env)
end
end
@@ -307,6 +384,14 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
},
success: true
)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).with(
+ labels: {
+ feature_category: 'unknown',
+ endpoint_id: 'unknown',
+ request_urgency: :default
+ },
+ error: false
+ )
subject.call(env)
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(100, 101)
@@ -318,6 +403,14 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
},
success: false
)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).with(
+ labels: {
+ feature_category: 'unknown',
+ endpoint_id: 'unknown',
+ request_urgency: :default
+ },
+ error: false
+ )
subject.call(env)
end
end
@@ -337,6 +430,14 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
},
success: true
)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).with(
+ labels: {
+ feature_category: 'unknown',
+ endpoint_id: 'unknown',
+ request_urgency: :default
+ },
+ error: false
+ )
subject.call(env)
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(100, 101)
@@ -348,6 +449,14 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
},
success: false
)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).with(
+ labels: {
+ feature_category: 'unknown',
+ endpoint_id: 'unknown',
+ request_urgency: :default
+ },
+ error: false
+ )
subject.call(env)
end
end
diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
index 005c1ae2d0a..4569f3134ae 100644
--- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
@@ -7,7 +7,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
let(:env) { {} }
let(:subscriber) { described_class.new }
- let(:connection) { ActiveRecord::Base.retrieve_connection }
+
+ let(:connection) { Gitlab::Database.database_base_models[:main].retrieve_connection }
let(:db_config_name) { ::Gitlab::Database.db_config_name(connection) }
describe '.load_balancing_metric_counter_keys' do
@@ -155,7 +156,9 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
end
it 'captures the metrics for web only' do
- expect(web_transaction).to receive(:observe).with(:gitlab_database_transaction_seconds, 0.23, { db_config_name: db_config_name })
+ expect(web_transaction).to receive(:observe).with(
+ :gitlab_database_transaction_seconds, 0.23, { db_config_name: db_config_name }
+ )
expect(background_transaction).not_to receive(:observe)
expect(background_transaction).not_to receive(:increment)
@@ -175,7 +178,9 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
end
it 'captures the metrics for web only' do
- expect(web_transaction).to receive(:observe).with(:gitlab_database_transaction_seconds, 0.23, { db_config_name: db_config_name })
+ expect(web_transaction).to receive(:observe).with(
+ :gitlab_database_transaction_seconds, 0.23, { db_config_name: db_config_name }
+ )
expect(background_transaction).not_to receive(:observe)
expect(background_transaction).not_to receive(:increment)
@@ -195,7 +200,9 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
end
it 'captures the metrics for web only' do
- expect(background_transaction).to receive(:observe).with(:gitlab_database_transaction_seconds, 0.23, { db_config_name: db_config_name })
+ expect(background_transaction).to receive(:observe).with(
+ :gitlab_database_transaction_seconds, 0.23, { db_config_name: db_config_name }
+ )
expect(web_transaction).not_to receive(:observe)
expect(web_transaction).not_to receive(:increment)
diff --git a/spec/lib/gitlab/metrics/subscribers/ldap_spec.rb b/spec/lib/gitlab/metrics/subscribers/ldap_spec.rb
new file mode 100644
index 00000000000..b81000be62a
--- /dev/null
+++ b/spec/lib/gitlab/metrics/subscribers/ldap_spec.rb
@@ -0,0 +1,124 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe Gitlab::Metrics::Subscribers::Ldap, :request_store, feature_category: :logging do
+ let(:transaction) { Gitlab::Metrics::WebTransaction.new({}) }
+ let(:subscriber) { described_class.new }
+
+ let(:attributes) do
+ [
+ :altServer, :namingContexts, :supportedCapabilities, :supportedControl,
+ :supportedExtension, :supportedFeatures, :supportedLdapVersion, :supportedSASLMechanisms
+ ]
+ end
+
+ let(:event_1) do
+ instance_double(
+ ActiveSupport::Notifications::Event,
+ name: "open.net_ldap",
+ payload: {
+ ignore_server_caps: true,
+ base: "",
+ scope: 0,
+ attributes: attributes,
+ result: nil
+ },
+ time: Time.current,
+ duration: 0.321
+ )
+ end
+
+ let(:event_2) do
+ instance_double(
+ ActiveSupport::Notifications::Event,
+ name: "search.net_ldap",
+ payload: {
+ ignore_server_caps: true,
+ base: "",
+ scope: 0,
+ attributes: attributes,
+ result: nil
+ },
+ time: Time.current,
+ duration: 0.12
+ )
+ end
+
+ let(:event_3) do
+ instance_double(
+ ActiveSupport::Notifications::Event,
+ name: "search.net_ldap",
+ payload: {
+ ignore_server_caps: true,
+ base: "",
+ scope: 0,
+ attributes: attributes,
+ result: nil
+ },
+ time: Time.current,
+ duration: 5.3
+ )
+ end
+
+ around do |example|
+ freeze_time { example.run }
+ end
+
+ describe ".payload" do
+ context "when SafeRequestStore is empty" do
+ it "returns an empty array" do
+ expect(described_class.payload).to eql(net_ldap_count: 0, net_ldap_duration_s: 0.0)
+ end
+ end
+
+ context "when LDAP recorded some values" do
+ before do
+ Gitlab::SafeRequestStore[:net_ldap_count] = 7
+ Gitlab::SafeRequestStore[:net_ldap_duration_s] = 1.2
+ end
+
+ it "returns the populated payload" do
+ expect(described_class.payload).to eql(net_ldap_count: 7, net_ldap_duration_s: 1.2)
+ end
+ end
+ end
+
+ describe "#observe_event" do
+ before do
+ allow(subscriber).to receive(:current_transaction).and_return(transaction)
+ end
+
+ it "tracks LDAP request count" do
+ expect(transaction).to receive(:increment)
+ .with(:gitlab_net_ldap_total, 1, { name: "open" })
+ expect(transaction).to receive(:increment)
+ .with(:gitlab_net_ldap_total, 1, { name: "search" })
+
+ subscriber.observe_event(event_1)
+ subscriber.observe_event(event_2)
+ end
+
+ it "tracks LDAP request duration" do
+ expect(transaction).to receive(:observe)
+ .with(:gitlab_net_ldap_duration_seconds, 0.321, { name: "open" })
+ expect(transaction).to receive(:observe)
+ .with(:gitlab_net_ldap_duration_seconds, 0.12, { name: "search" })
+ expect(transaction).to receive(:observe)
+ .with(:gitlab_net_ldap_duration_seconds, 5.3, { name: "search" })
+
+ subscriber.observe_event(event_1)
+ subscriber.observe_event(event_2)
+ subscriber.observe_event(event_3)
+ end
+
+ it "stores per-request counters" do
+ subscriber.observe_event(event_1)
+ subscriber.observe_event(event_2)
+ subscriber.observe_event(event_3)
+
+ expect(Gitlab::SafeRequestStore[:net_ldap_count]).to eq(3)
+ expect(Gitlab::SafeRequestStore[:net_ldap_duration_s]).to eq(5.741) # 0.321 + 0.12 + 5.3
+ end
+ end
+end
diff --git a/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
index 9aba6ac293c..59bfe2042fa 100644
--- a/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::RailsCache do
let(:transaction) { Gitlab::Metrics::WebTransaction.new(env) }
let(:subscriber) { described_class.new }
- let(:event) { double(:event, duration: 15.2) }
+ let(:event) { double(:event, duration: 15.2, payload: { key: %w[a b c] }) }
describe '#cache_read' do
it 'increments the cache_read duration' do
@@ -64,6 +64,40 @@ RSpec.describe Gitlab::Metrics::Subscribers::RailsCache do
end
end
+ describe '#cache_read_multi' do
+ subject { subscriber.cache_read_multi(event) }
+
+ context 'with a transaction' do
+ before do
+ allow(subscriber).to receive(:current_transaction)
+ .and_return(transaction)
+ end
+
+ it 'observes multi-key count' do
+ expect(transaction).to receive(:observe)
+ .with(:gitlab_cache_read_multikey_count, event.payload[:key].size)
+
+ subject
+ end
+ end
+
+ context 'with no transaction' do
+ it 'does not observes multi-key count' do
+ expect(transaction).not_to receive(:observe)
+ .with(:gitlab_cache_read_multikey_count, event.payload[:key].size)
+
+ subject
+ end
+ end
+
+ it 'observes read_multi duration' do
+ expect(subscriber).to receive(:observe)
+ .with(:read_multi, event.duration)
+
+ subject
+ end
+ end
+
describe '#cache_write' do
it 'observes write duration' do
expect(subscriber).to receive(:observe)
diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb
index 366843a4c03..dbd6c07ef75 100644
--- a/spec/lib/gitlab/metrics_spec.rb
+++ b/spec/lib/gitlab/metrics_spec.rb
@@ -101,14 +101,32 @@ RSpec.describe Gitlab::Metrics do
401 | true
nil | false
500 | false
- 503 | false
- '100' | false
- '201' | true
+ 503 | false
'nothing' | false
end
with_them do
specify { expect(described_class.record_duration_for_status?(status)).to be(should_record) }
+ specify { expect(described_class.record_duration_for_status?(status.to_s)).to be(should_record) }
+ end
+ end
+
+ describe '.server_error?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:status, :should_record) do
+ 100 | false
+ 200 | false
+ 401 | false
+ 500 | true
+ 503 | true
+ nil | false
+ 'nothing' | false
+ end
+
+ with_them do
+ specify { expect(described_class.server_error?(status)).to be(should_record) }
+ specify { expect(described_class.server_error?(status.to_s)).to be(should_record) }
end
end
diff --git a/spec/lib/gitlab/middleware/compressed_json_spec.rb b/spec/lib/gitlab/middleware/compressed_json_spec.rb
index 6d49ab58d5d..1444e6a9881 100644
--- a/spec/lib/gitlab/middleware/compressed_json_spec.rb
+++ b/spec/lib/gitlab/middleware/compressed_json_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe Gitlab::Middleware::CompressedJson do
let(:app) { double(:app) }
let(:middleware) { described_class.new(app) }
let(:content_type) { 'application/json' }
+ let(:relative_url_root) { '/gitlab' }
let(:env) do
{
'HTTP_CONTENT_ENCODING' => 'gzip',
@@ -31,6 +32,43 @@ RSpec.describe Gitlab::Middleware::CompressedJson do
end
end
+ shared_examples 'passes input' do
+ it 'keeps the original input' do
+ expect(app).to receive(:call)
+
+ middleware.call(env)
+
+ expect(env['rack.input'].read).to eq(input)
+ expect(env['HTTP_CONTENT_ENCODING']).to eq('gzip')
+ end
+ end
+
+ shared_context 'with relative url' do
+ before do
+ stub_config_setting(relative_url_root: relative_url_root)
+ end
+ end
+
+ shared_examples 'handles non integer project ID' do
+ context 'with a URL-encoded project ID' do
+ let_it_be(:project_id) { 'gitlab-org%2fgitlab' }
+
+ it_behaves_like 'decompress middleware'
+ end
+
+ context 'with a non URL-encoded project ID' do
+ let_it_be(:project_id) { '1/repository/files/api/v4' }
+
+ it_behaves_like 'passes input'
+ end
+
+ context 'with a blank project ID' do
+ let_it_be(:project_id) { '' }
+
+ it_behaves_like 'passes input'
+ end
+ end
+
describe '#call' do
context 'with collector route' do
let(:path) { '/api/v4/error_tracking/collector/1/store' }
@@ -42,31 +80,80 @@ RSpec.describe Gitlab::Middleware::CompressedJson do
it_behaves_like 'decompress middleware'
end
+
+ include_context 'with relative url' do
+ let(:path) { "#{relative_url_root}/api/v4/error_tracking/collector/1/store" }
+
+ it_behaves_like 'decompress middleware'
+ end
end
- context 'with collector route under relative url' do
- let(:path) { '/gitlab/api/v4/error_tracking/collector/1/store' }
+ context 'with packages route' do
+ context 'with instance level endpoint' do
+ context 'with npm advisory bulk url' do
+ let(:path) { '/api/v4/packages/npm/-/npm/v1/security/advisories/bulk' }
+
+ it_behaves_like 'decompress middleware'
+
+ include_context 'with relative url' do
+ let(:path) { "#{relative_url_root}/api/v4/packages/npm/-/npm/v1/security/advisories/bulk" }
+
+ it_behaves_like 'decompress middleware'
+ end
+ end
+
+ context 'with npm quick audit url' do
+ let(:path) { '/api/v4/packages/npm/-/npm/v1/security/audits/quick' }
- before do
- stub_config_setting(relative_url_root: '/gitlab')
+ it_behaves_like 'decompress middleware'
+
+ include_context 'with relative url' do
+ let(:path) { "#{relative_url_root}/api/v4/packages/npm/-/npm/v1/security/audits/quick" }
+
+ it_behaves_like 'decompress middleware'
+ end
+ end
end
- it_behaves_like 'decompress middleware'
- end
+ context 'with project level endpoint' do
+ let_it_be(:project_id) { 1 }
- context 'with some other route' do
- let(:path) { '/api/projects/123' }
+ context 'with npm advisory bulk url' do
+ let(:path) { "/api/v4/projects/#{project_id}/packages/npm/-/npm/v1/security/advisories/bulk" }
- it 'keeps the original input' do
- expect(app).to receive(:call)
+ it_behaves_like 'decompress middleware'
- middleware.call(env)
+ include_context 'with relative url' do
+ let(:path) { "#{relative_url_root}/api/v4/projects/#{project_id}/packages/npm/-/npm/v1/security/advisories/bulk" } # rubocop disable Layout/LineLength
- expect(env['rack.input'].read).to eq(input)
- expect(env['HTTP_CONTENT_ENCODING']).to eq('gzip')
+ it_behaves_like 'decompress middleware'
+ end
+
+ it_behaves_like 'handles non integer project ID'
+ end
+
+ context 'with npm quick audit url' do
+ let(:path) { "/api/v4/projects/#{project_id}/packages/npm/-/npm/v1/security/audits/quick" }
+
+ it_behaves_like 'decompress middleware'
+
+ include_context 'with relative url' do
+ let(:path) { "#{relative_url_root}/api/v4/projects/#{project_id}/packages/npm/-/npm/v1/security/audits/quick" } # rubocop disable Layout/LineLength
+
+ it_behaves_like 'decompress middleware'
+ end
+
+ it_behaves_like 'handles non integer project ID'
+ end
end
end
+ context 'with some other route' do
+ let(:path) { '/api/projects/123' }
+
+ it_behaves_like 'passes input'
+ end
+
context 'payload is too large' do
let(:body_limit) { Gitlab::Middleware::CompressedJson::MAXIMUM_BODY_SIZE }
let(:decompressed_input) { 'a' * (body_limit + 100) }
diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb
index bc1d53b2ccb..bed43c04460 100644
--- a/spec/lib/gitlab/middleware/go_spec.rb
+++ b/spec/lib/gitlab/middleware/go_spec.rb
@@ -278,7 +278,7 @@ RSpec.describe Gitlab::Middleware::Go do
project_url = "http://#{Gitlab.config.gitlab.host}/#{path}"
expect(response[0]).to eq(200)
expect(response[1]['Content-Type']).to eq('text/html')
- expected_body = %{<html><head><meta name="go-import" content="#{Gitlab.config.gitlab.host}/#{path} git #{repository_url}" /><meta name="go-source" content="#{Gitlab.config.gitlab.host}/#{path} #{project_url} #{project_url}/-/tree/#{branch}{/dir} #{project_url}/-/blob/#{branch}{/dir}/{file}#L{line}" /></head><body>go get #{Gitlab.config.gitlab.url}/#{path}</body></html>}
+ expected_body = %{<html><head><meta name="go-import" content="#{Gitlab.config.gitlab.host}/#{path} git #{repository_url}"><meta name="go-source" content="#{Gitlab.config.gitlab.host}/#{path} #{project_url} #{project_url}/-/tree/#{branch}{/dir} #{project_url}/-/blob/#{branch}{/dir}/{file}#L{line}"></head><body>go get #{Gitlab.config.gitlab.url}/#{path}</body></html>}
expect(response[2]).to eq([expected_body])
end
end
diff --git a/spec/lib/gitlab/other_markup_spec.rb b/spec/lib/gitlab/other_markup_spec.rb
index 26e60251abb..6b24c8a8710 100644
--- a/spec/lib/gitlab/other_markup_spec.rb
+++ b/spec/lib/gitlab/other_markup_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::OtherMarkup do
let(:context) { {} }
- context "XSS Checks" do
+ context 'XSS Checks' do
links = {
'links' => {
file: 'file.rdoc',
@@ -20,6 +20,33 @@ RSpec.describe Gitlab::OtherMarkup do
end
end
+ context 'when rendering takes too long' do
+ let_it_be(:file_name) { 'foo.bar' }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:context) { { project: project } }
+ let_it_be(:text) { +'Noël' }
+
+ before do
+ stub_const('Gitlab::OtherMarkup::RENDER_TIMEOUT', 0.1)
+ allow(GitHub::Markup).to receive(:render) do
+ sleep(0.2)
+ text
+ end
+ end
+
+ it 'times out' do
+ # expect twice because of timeout in SyntaxHighlightFilter
+ expect(Gitlab::RenderTimeout).to receive(:timeout).twice.and_call_original
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
+ instance_of(Timeout::Error),
+ project_id: context[:project].id, file_name: file_name,
+ class_name: described_class.name.demodulize
+ )
+
+ expect(render(file_name, text, context)).to eq("<p>#{text}</p>")
+ end
+ end
+
def render(*args)
described_class.render(*args)
end
diff --git a/spec/lib/gitlab/pages/cache_control_spec.rb b/spec/lib/gitlab/pages/cache_control_spec.rb
index 431c989e874..d46124e0e16 100644
--- a/spec/lib/gitlab/pages/cache_control_spec.rb
+++ b/spec/lib/gitlab/pages/cache_control_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Pages::CacheControl do
+RSpec.describe Gitlab::Pages::CacheControl, feature_category: :pages do
describe '.for_namespace' do
subject(:cache_control) { described_class.for_namespace(1) }
@@ -11,8 +11,14 @@ RSpec.describe Gitlab::Pages::CacheControl do
describe '#clear_cache' do
it 'clears the cache' do
expect(Rails.cache)
- .to receive(:delete)
- .with(/pages_domain_for_namespace_1_*/)
+ .to receive(:delete_multi)
+ .with(
+ array_including(
+ [
+ "pages_domain_for_namespace_1",
+ /pages_domain_for_namespace_1_*/
+ ]
+ ))
subject.clear_cache
end
@@ -27,8 +33,14 @@ RSpec.describe Gitlab::Pages::CacheControl do
describe '#clear_cache' do
it 'clears the cache' do
expect(Rails.cache)
- .to receive(:delete)
- .with(/pages_domain_for_project_1_*/)
+ .to receive(:delete_multi)
+ .with(
+ array_including(
+ [
+ "pages_domain_for_project_1",
+ /pages_domain_for_project_1_*/
+ ]
+ ))
subject.clear_cache
end
@@ -58,6 +70,14 @@ RSpec.describe Gitlab::Pages::CacheControl do
expect(described_class.new(type: :project, id: 1).cache_key).not_to eq(cache_key)
end
+
+ it 'caches the application settings hash' do
+ expect(Rails.cache)
+ .to receive(:write)
+ .with("pages_domain_for_project_1", kind_of(Set))
+
+ described_class.new(type: :project, id: 1).cache_key
+ end
end
it 'fails with invalid type' do
diff --git a/spec/lib/gitlab/pagination/offset_pagination_spec.rb b/spec/lib/gitlab/pagination/offset_pagination_spec.rb
index ebbd207cc11..b1c4ffd6c29 100644
--- a/spec/lib/gitlab/pagination/offset_pagination_spec.rb
+++ b/spec/lib/gitlab/pagination/offset_pagination_spec.rb
@@ -101,6 +101,28 @@ RSpec.describe Gitlab::Pagination::OffsetPagination do
end
end
+ context 'when without_count is true' do
+ it_behaves_like 'paginated response'
+
+ it 'does not return the X-Total and X-Total-Pages headers' do
+ expect_no_header('X-Total')
+ expect_no_header('X-Total-Pages')
+ expect_header('X-Per-Page', '2')
+ expect_header('X-Page', '1')
+ expect_header('X-Next-Page', '2')
+ expect_header('X-Prev-Page', '')
+
+ expect_header('Link', anything) do |_key, val|
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
+ expect(val).not_to include('rel="last"')
+ expect(val).not_to include('rel="prev"')
+ end
+
+ expect { subject.paginate(resource, without_count: true) }.to make_queries_matching(/SELECT COUNT/, 0)
+ end
+ end
+
it 'does not return the total headers when excluding them' do
expect_no_header('X-Total')
expect_no_header('X-Total-Pages')
diff --git a/spec/lib/gitlab/process_management_spec.rb b/spec/lib/gitlab/process_management_spec.rb
index a71a476b540..fbd39702efb 100644
--- a/spec/lib/gitlab/process_management_spec.rb
+++ b/spec/lib/gitlab/process_management_spec.rb
@@ -41,15 +41,6 @@ RSpec.describe Gitlab::ProcessManagement do
end
end
- describe '.wait_async' do
- it 'waits for a process in a separate thread' do
- thread = described_class.wait_async(Process.spawn('true'))
-
- # Upon success Process.wait just returns the PID.
- expect(thread.value).to be_a_kind_of(Numeric)
- end
- end
-
# In the X_alive? checks, we check negative PIDs sometimes as a simple way
# to be sure the pids are definitely for non-existent processes.
# Note that -1 is special, and sends the signal to every process we have permission
diff --git a/spec/lib/gitlab/process_supervisor_spec.rb b/spec/lib/gitlab/process_supervisor_spec.rb
index 8356197805c..18de5053362 100644
--- a/spec/lib/gitlab/process_supervisor_spec.rb
+++ b/spec/lib/gitlab/process_supervisor_spec.rb
@@ -2,7 +2,7 @@
require_relative '../../../lib/gitlab/process_supervisor'
-RSpec.describe Gitlab::ProcessSupervisor do
+RSpec.describe Gitlab::ProcessSupervisor, feature_category: :application_performance do
let(:health_check_interval_seconds) { 0.1 }
let(:check_terminate_interval_seconds) { 1 }
let(:forwarded_signals) { [] }
diff --git a/spec/lib/gitlab/puma_logging/json_formatter_spec.rb b/spec/lib/gitlab/puma_logging/json_formatter_spec.rb
index 64ace09e01b..d38f54bccf1 100644
--- a/spec/lib/gitlab/puma_logging/json_formatter_spec.rb
+++ b/spec/lib/gitlab/puma_logging/json_formatter_spec.rb
@@ -4,8 +4,8 @@ require 'spec_helper'
RSpec.describe Gitlab::PumaLogging::JSONFormatter do
it "generate json format with timestamp and pid" do
- Timecop.freeze( Time.utc(2019, 12, 04, 9, 10, 11, 123456)) do
- expect(subject.call('log message')).to eq "{\"timestamp\":\"2019-12-04T09:10:11.123Z\",\"pid\":#{Process.pid},\"message\":\"log message\"}"
+ travel_to(Time.utc(2019, 12, 04, 9, 10, 11)) do
+ expect(subject.call('log message')).to eq "{\"timestamp\":\"2019-12-04T09:10:11.000Z\",\"pid\":#{Process.pid},\"message\":\"log message\"}"
end
end
end
diff --git a/spec/lib/gitlab/quick_actions/dsl_spec.rb b/spec/lib/gitlab/quick_actions/dsl_spec.rb
index 942d347424f..c0469537c68 100644
--- a/spec/lib/gitlab/quick_actions/dsl_spec.rb
+++ b/spec/lib/gitlab/quick_actions/dsl_spec.rb
@@ -3,9 +3,10 @@
require 'spec_helper'
RSpec.describe Gitlab::QuickActions::Dsl do
- before :all do
- DummyClass = Struct.new(:project) do
- include Gitlab::QuickActions::Dsl
+ before do
+ stub_const('DummyClass', Struct.new(:project))
+ DummyClass.class_eval do
+ include Gitlab::QuickActions::Dsl # rubocop:disable RSpec/DescribedClass
desc 'A command with no args'
command :no_args, :none do
diff --git a/spec/lib/gitlab/redis/multi_store_spec.rb b/spec/lib/gitlab/redis/multi_store_spec.rb
index 207fe28e84e..0e7eedf66b1 100644
--- a/spec/lib/gitlab/redis/multi_store_spec.rb
+++ b/spec/lib/gitlab/redis/multi_store_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Redis::MultiStore do
+RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
using RSpec::Parameterized::TableSyntax
let_it_be(:redis_store_class) do
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index 0ee8c35ae81..4d608c07736 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -262,8 +262,11 @@ RSpec.describe Gitlab::ReferenceExtractor do
describe '#all' do
let(:issue) { create(:issue, project: project) }
+ let(:issue2) { create(:issue, project: project) }
+ let(:issue2_url) { Rails.application.routes.url_helpers.project_issue_url(project, issue2) }
let(:label) { create(:label, project: project) }
- let(:text) { "Ref. #{issue.to_reference} and #{label.to_reference}" }
+ let(:alert) { create(:alert_management_alert, project: project) }
+ let(:text) { "Ref. #{issue.to_reference} and #{label.to_reference} and #{alert.to_reference} and #{issue2_url}" }
before do
project.add_developer(project.creator)
@@ -271,7 +274,22 @@ RSpec.describe Gitlab::ReferenceExtractor do
end
it 'returns all referables' do
- expect(subject.all).to match_array([issue, label])
+ expect(subject.all).to match_array([issue, label, alert, issue2])
+ end
+ end
+
+ describe '#alerts' do
+ let(:alert1) { create(:alert_management_alert, project: project) }
+ let(:alert2) { create(:alert_management_alert, project: project) }
+ let(:text) { "Alert ref: #{alert1.to_reference} URL: #{alert2.details_url} Infalid ref: ^alert#0" }
+
+ before do
+ project.add_developer(project.creator)
+ subject.analyze(text)
+ end
+
+ it 'returns alert referables' do
+ expect(subject.alerts).to match_array([alert1, alert2])
end
end
diff --git a/spec/lib/gitlab/repository_archive_rate_limiter_spec.rb b/spec/lib/gitlab/repository_archive_rate_limiter_spec.rb
index 49df70f3cb3..4599c647d5c 100644
--- a/spec/lib/gitlab/repository_archive_rate_limiter_spec.rb
+++ b/spec/lib/gitlab/repository_archive_rate_limiter_spec.rb
@@ -7,8 +7,7 @@ RSpec.describe ::Gitlab::RepositoryArchiveRateLimiter do
Class.new do
include ::Gitlab::RepositoryArchiveRateLimiter
- def check_rate_limit!(**args)
- end
+ def check_rate_limit!(**args); end
end
end
diff --git a/spec/lib/gitlab/repository_cache_adapter_spec.rb b/spec/lib/gitlab/repository_cache_adapter_spec.rb
index d14c3f44c6f..71a20cc58fd 100644
--- a/spec/lib/gitlab/repository_cache_adapter_spec.rb
+++ b/spec/lib/gitlab/repository_cache_adapter_spec.rb
@@ -27,8 +27,7 @@ RSpec.describe Gitlab::RepositoryCacheAdapter do
'foo/bar'
end
- def project
- end
+ def project; end
def cached_methods
[:letters]
diff --git a/spec/lib/gitlab/search/found_blob_spec.rb b/spec/lib/gitlab/search/found_blob_spec.rb
index 8b1c91f689d..c41a051bc42 100644
--- a/spec/lib/gitlab/search/found_blob_spec.rb
+++ b/spec/lib/gitlab/search/found_blob_spec.rb
@@ -141,9 +141,8 @@ RSpec.describe Gitlab::Search::FoundBlob do
subject { described_class.new(blob_path: path, project: project, ref: 'master') }
before do
- allow(Gitlab::Git::Blob).to receive(:batch).and_return([
- Gitlab::Git::Blob.new(path: path)
- ])
+ allow(Gitlab::Git::Blob)
+ .to receive(:batch).and_return([Gitlab::Git::Blob.new(path: path)])
end
it { expect(subject.path).to eq('a/b/c.md') }
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index 785429aa3b0..049b8d4ed86 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -88,18 +88,11 @@ RSpec.describe Gitlab::Shell do
let(:disk_path) { "#{project.disk_path}.git" }
it 'returns true when the command succeeds' do
- expect(TestEnv.storage_dir_exists?(project.repository_storage, disk_path)).to be(true)
+ expect(project.repository.raw).to exist
expect(gitlab_shell.remove_repository(project.repository_storage, project.disk_path)).to be(true)
- expect(TestEnv.storage_dir_exists?(project.repository_storage, disk_path)).to be(false)
- end
-
- it 'keeps the namespace directory' do
- gitlab_shell.remove_repository(project.repository_storage, project.disk_path)
-
- expect(TestEnv.storage_dir_exists?(project.repository_storage, disk_path)).to be(false)
- expect(TestEnv.storage_dir_exists?(project.repository_storage, project.disk_path.gsub(project.name, ''))).to be(true)
+ expect(project.repository.raw).not_to exist
end
end
@@ -107,21 +100,22 @@ RSpec.describe Gitlab::Shell do
let!(:project2) { create(:project, :repository) }
it 'returns true when the command succeeds' do
- old_path = project2.disk_path
+ old_repo = project2.repository.raw
new_path = "project/new_path"
+ new_repo = Gitlab::Git::Repository.new(project2.repository_storage, "#{new_path}.git", nil, nil)
- expect(TestEnv.storage_dir_exists?(project2.repository_storage, "#{old_path}.git")).to be(true)
- expect(TestEnv.storage_dir_exists?(project2.repository_storage, "#{new_path}.git")).to be(false)
+ expect(old_repo).to exist
+ expect(new_repo).not_to exist
- expect(gitlab_shell.mv_repository(project2.repository_storage, old_path, new_path)).to be_truthy
+ expect(gitlab_shell.mv_repository(project2.repository_storage, project2.disk_path, new_path)).to be_truthy
- expect(TestEnv.storage_dir_exists?(project2.repository_storage, "#{old_path}.git")).to be(false)
- expect(TestEnv.storage_dir_exists?(project2.repository_storage, "#{new_path}.git")).to be(true)
+ expect(old_repo).not_to exist
+ expect(new_repo).to exist
end
it 'returns false when the command fails' do
expect(gitlab_shell.mv_repository(project2.repository_storage, project2.disk_path, '')).to be_falsy
- expect(TestEnv.storage_dir_exists?(project2.repository_storage, "#{project2.disk_path}.git")).to be(true)
+ expect(project2.repository.raw).to exist
end
end
end
@@ -133,9 +127,11 @@ RSpec.describe Gitlab::Shell do
describe '#add_namespace' do
it 'creates a namespace' do
- Gitlab::GitalyClient::NamespaceService.allow { subject.add_namespace(storage, "mepmep") }
+ Gitlab::GitalyClient::NamespaceService.allow do
+ subject.add_namespace(storage, "mepmep")
- expect(TestEnv.storage_dir_exists?(storage, "mepmep")).to be(true)
+ expect(Gitlab::GitalyClient::NamespaceService.new(storage).exists?("mepmep")).to be(true)
+ end
end
end
@@ -160,9 +156,9 @@ RSpec.describe Gitlab::Shell do
Gitlab::GitalyClient::NamespaceService.allow do
subject.add_namespace(storage, "mepmep")
subject.rm_namespace(storage, "mepmep")
- end
- expect(TestEnv.storage_dir_exists?(storage, "mepmep")).to be(false)
+ expect(Gitlab::GitalyClient::NamespaceService.new(storage).exists?("mepmep")).to be(false)
+ end
end
end
@@ -171,10 +167,10 @@ RSpec.describe Gitlab::Shell do
Gitlab::GitalyClient::NamespaceService.allow do
subject.add_namespace(storage, "mepmep")
subject.mv_namespace(storage, "mepmep", "2mep")
- end
- expect(TestEnv.storage_dir_exists?(storage, "mepmep")).to be(false)
- expect(TestEnv.storage_dir_exists?(storage, "2mep")).to be(true)
+ expect(Gitlab::GitalyClient::NamespaceService.new(storage).exists?("mepmep")).to be(false)
+ expect(Gitlab::GitalyClient::NamespaceService.new(storage).exists?("2mep")).to be(true)
+ end
end
end
end
diff --git a/spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb b/spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb
index 8c9a1abba5a..5baeec93036 100644
--- a/spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb
+++ b/spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb
@@ -4,11 +4,23 @@ require 'spec_helper'
RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
let(:memory_killer) { described_class.new }
+ let(:sidekiq_daemon_monitor) { instance_double(Gitlab::SidekiqDaemon::Monitor) }
+ let(:running_jobs) { {} }
let(:pid) { 12345 }
+ let(:worker) do
+ Class.new do
+ def self.name
+ 'DummyWorker'
+ end
+ end
+ end
before do
+ stub_const('DummyWorker', worker)
allow(Sidekiq.logger).to receive(:info)
allow(Sidekiq.logger).to receive(:warn)
+ allow(Gitlab::SidekiqDaemon::Monitor).to receive(:instance).and_return(sidekiq_daemon_monitor)
+ allow(sidekiq_daemon_monitor).to receive(:jobs).and_return(running_jobs)
allow(memory_killer).to receive(:pid).and_return(pid)
# make sleep no-op
@@ -306,31 +318,37 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
stub_const("#{described_class}::CHECK_INTERVAL_SECONDS", check_interval_seconds)
end
- it 'send signal and return when all jobs finished' do
- expect(Process).to receive(:kill).with(signal, pid).ordered
- expect(Gitlab::Metrics::System).to receive(:monotonic_time).and_call_original
+ context 'when all jobs are finished' do
+ let(:running_jobs) { {} }
- expect(memory_killer).to receive(:enabled?).and_return(true)
- expect(memory_killer).to receive(:any_jobs?).and_return(false)
+ it 'send signal and return when all jobs finished' do
+ expect(Process).to receive(:kill).with(signal, pid).ordered
+ expect(Gitlab::Metrics::System).to receive(:monotonic_time).and_call_original
- expect(memory_killer).not_to receive(:sleep)
+ expect(memory_killer).to receive(:enabled?).and_return(true)
- subject
+ expect(memory_killer).not_to receive(:sleep)
+
+ subject
+ end
end
- it 'send signal and wait till deadline if any job not finished' do
- expect(Process).to receive(:kill)
- .with(signal, pid)
- .ordered
+ context 'when there are still running jobs' do
+ let(:running_jobs) { { 'jid1' => { worker_class: DummyWorker } } }
- expect(Gitlab::Metrics::System).to receive(:monotonic_time)
- .and_call_original
- .at_least(:once)
+ it 'send signal and wait till deadline if any job not finished' do
+ expect(Process).to receive(:kill)
+ .with(signal, pid)
+ .ordered
+
+ expect(Gitlab::Metrics::System).to receive(:monotonic_time)
+ .and_call_original
+ .at_least(:once)
- expect(memory_killer).to receive(:enabled?).and_return(true).at_least(:once)
- expect(memory_killer).to receive(:any_jobs?).and_return(true).at_least(:once)
+ expect(memory_killer).to receive(:enabled?).and_return(true).at_least(:once)
- subject
+ subject
+ end
end
end
@@ -377,21 +395,11 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
let(:jid) { 1 }
let(:reason) { 'rss out of range reason description' }
let(:queue) { 'default' }
- let(:running_jobs) { [{ jid: jid, worker_class: 'DummyWorker' }] }
- let(:metrics) { memory_killer.instance_variable_get(:@metrics) }
- let(:worker) do
- Class.new do
- def self.name
- 'DummyWorker'
- end
- include ApplicationWorker
- end
- end
+ let(:metrics) { memory_killer.instance_variable_get(:@metrics) }
+ let(:running_jobs) { { jid => { worker_class: DummyWorker } } }
before do
- stub_const("DummyWorker", worker)
-
allow(memory_killer).to receive(:get_rss_kb).and_return(*current_rss)
allow(memory_killer).to receive(:get_soft_limit_rss_kb).and_return(soft_limit_rss)
allow(memory_killer).to receive(:get_hard_limit_rss_kb).and_return(hard_limit_rss)
@@ -413,15 +421,13 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
hard_limit_rss: hard_limit_rss,
soft_limit_rss: soft_limit_rss,
reason: reason,
- running_jobs: running_jobs,
+ running_jobs: [jid: jid, worker_class: 'DummyWorker'],
memory_total_kb: memory_total)
expect(metrics[:sidekiq_memory_killer_running_jobs]).to receive(:increment)
.with({ worker_class: "DummyWorker", deadline_exceeded: true })
- Gitlab::SidekiqDaemon::Monitor.instance.within_job(DummyWorker, jid, queue) do
- subject
- end
+ subject
end
end
@@ -452,6 +458,7 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
expect(subject).to eq("current_rss(#{rss}) > soft_limit_rss(#{soft_limit}) longer than GRACE_BALLOON_SECONDS(#{grace_balloon_seconds})")
end
end
+
context 'deadline not exceeded' do
let(:deadline_exceeded) { false }
@@ -463,21 +470,24 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
end
describe '#rss_increase_by_jobs' do
- let(:running_jobs) { { id1: 'job1', id2: 'job2' } }
+ let(:running_jobs) { { 'job1' => { worker_class: "Job1" }, 'job2' => { worker_class: "Job2" } } }
subject { memory_killer.send(:rss_increase_by_jobs) }
+ before do
+ allow(memory_killer).to receive(:rss_increase_by_job).and_return(11, 22)
+ end
+
it 'adds up individual rss_increase_by_job' do
- allow(Gitlab::SidekiqDaemon::Monitor).to receive_message_chain(:instance, :jobs_mutex, :synchronize).and_yield
- expect(Gitlab::SidekiqDaemon::Monitor).to receive_message_chain(:instance, :jobs).and_return(running_jobs)
- expect(memory_killer).to receive(:rss_increase_by_job).and_return(11, 22)
expect(subject).to eq(33)
end
- it 'return 0 if no job' do
- allow(Gitlab::SidekiqDaemon::Monitor).to receive_message_chain(:instance, :jobs_mutex, :synchronize).and_yield
- expect(Gitlab::SidekiqDaemon::Monitor).to receive_message_chain(:instance, :jobs).and_return({})
- expect(subject).to eq(0)
+ context 'when there is no running job' do
+ let(:running_jobs) { {} }
+
+ it 'return 0 if no job' do
+ expect(subject).to eq(0)
+ end
end
end
diff --git a/spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb b/spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb
index f93c0e28fc0..479ef29bbf9 100644
--- a/spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb
+++ b/spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb
@@ -6,9 +6,15 @@ RSpec.describe Gitlab::SidekiqDaemon::Monitor do
let(:monitor) { described_class.new }
describe '#within_job' do
- it 'tracks thread' do
+ it 'tracks thread, jid and worker_class' do
blk = proc do
- expect(monitor.jobs.dig('jid', :thread)).not_to be_nil
+ monitor.jobs do |jobs|
+ jobs.each do |jid, job|
+ expect(job[:thread]).not_to be_nil
+ expect(jid).to eq('jid')
+ expect(job[:worker_class]).to eq('worker_class')
+ end
+ end
"OK"
end
@@ -37,6 +43,17 @@ RSpec.describe Gitlab::SidekiqDaemon::Monitor do
end
end
+ describe '#jobs' do
+ it 'returns running jobs hash' do
+ jid = SecureRandom.hex
+ running_jobs = { jid => hash_including(worker_class: 'worker_class') }
+
+ monitor.within_job('worker_class', jid, 'queue') do
+ expect(monitor.jobs).to match(running_jobs)
+ end
+ end
+ end
+
describe '#run_thread when notification channel not enabled' do
subject { monitor.send(:run_thread) }
@@ -220,7 +237,7 @@ RSpec.describe Gitlab::SidekiqDaemon::Monitor do
let(:thread) { Thread.new { sleep 1000 } }
before do
- monitor.jobs[jid] = { worker_class: 'worker_class', thread: thread, started_at: Time.now.to_i }
+ allow(monitor).to receive(:find_thread_unsafe).with(jid).and_return(thread)
end
after do
diff --git a/spec/lib/gitlab/sidekiq_middleware/client_metrics_spec.rb b/spec/lib/gitlab/sidekiq_middleware/client_metrics_spec.rb
index dca00c85e30..472591bde5e 100644
--- a/spec/lib/gitlab/sidekiq_middleware/client_metrics_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/client_metrics_spec.rb
@@ -40,8 +40,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::ClientMetrics do
TestWorker.class_eval do
include Sidekiq::Worker
- def perform(*args)
- end
+ def perform(*args); end
end
allow(Gitlab::Metrics).to receive(:counter).and_return(Gitlab::Metrics::NullMetric.instance)
diff --git a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb
index 44c8df73463..14eb568b974 100644
--- a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb
@@ -17,8 +17,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::Client, :clean_gitlab_r
include ApplicationWorker
- def perform(*args)
- end
+ def perform(*args); end
end
end
diff --git a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb
index 09548d21106..1b01793d80d 100644
--- a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb
@@ -18,8 +18,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::Server, :clean_gitlab_r
self.class.work
end
- def self.work
- end
+ def self.work; end
end
end
diff --git a/spec/lib/gitlab/sidekiq_middleware/instrumentation_logger_spec.rb b/spec/lib/gitlab/sidekiq_middleware/instrumentation_logger_spec.rb
index 8cf65e1be5b..cfb2c7ab5c3 100644
--- a/spec/lib/gitlab/sidekiq_middleware/instrumentation_logger_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/instrumentation_logger_spec.rb
@@ -13,8 +13,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::InstrumentationLogger do
include ApplicationWorker
- def perform(*args)
- end
+ def perform(*args); end
end
end
diff --git a/spec/lib/gitlab/sidekiq_middleware/query_analyzer_spec.rb b/spec/lib/gitlab/sidekiq_middleware/query_analyzer_spec.rb
index e58af1d60fe..c31f05f00e4 100644
--- a/spec/lib/gitlab/sidekiq_middleware/query_analyzer_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/query_analyzer_spec.rb
@@ -10,8 +10,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::QueryAnalyzer, query_analyzers: false
let(:queue) { 'some-queue' }
let(:middleware) { described_class.new }
- def do_queries
- end
+ def do_queries; end
subject { middleware.call(worker, job, queue) { do_queries } }
diff --git a/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb b/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb
index 1a53a9b8701..f7cee6beb58 100644
--- a/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb
@@ -231,8 +231,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do
include Sidekiq::Worker
include WorkerAttributes
- def perform(*args)
- end
+ def perform(*args); end
end
allow(::Gitlab::Database::LoadBalancing).to receive_message_chain(:proxy, :load_balancer).and_return(load_balancer)
@@ -306,8 +305,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do
feature_category :not_owned
end
- def perform
- end
+ def perform; end
end
end
diff --git a/spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb b/spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb
index 821d8b8fe7b..1b6cd7ac5fb 100644
--- a/spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb
@@ -17,8 +17,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::WorkerContext::Client do
jobs.find { |job| job['args'] == args }
end
- def perform(*args)
- end
+ def perform(*args); end
end
end
@@ -38,8 +37,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::WorkerContext::Client do
'TestMailer'
end
- def test_mail
- end
+ def test_mail; end
end
end
diff --git a/spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb b/spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb
index 05b328e55d3..2deab3064eb 100644
--- a/spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb
@@ -41,8 +41,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::WorkerContext::Server do
include Sidekiq::Worker
- def perform
- end
+ def perform; end
end
end
diff --git a/spec/lib/gitlab/slash_commands/application_help_spec.rb b/spec/lib/gitlab/slash_commands/application_help_spec.rb
index b182c0e5cc6..d0cefdf4895 100644
--- a/spec/lib/gitlab/slash_commands/application_help_spec.rb
+++ b/spec/lib/gitlab/slash_commands/application_help_spec.rb
@@ -4,13 +4,11 @@ require 'spec_helper'
RSpec.describe Gitlab::SlashCommands::ApplicationHelp do
let(:params) { { command: '/gitlab', text: 'help' } }
- let_it_be(:user) { create(:user) }
- let_it_be(:chat_user) { create(:chat_name, user: user) }
let(:project) { build(:project) }
describe '#execute' do
subject do
- described_class.new(project, chat_user, params).execute
+ described_class.new(project, params).execute
end
it 'displays the help section' do
diff --git a/spec/lib/gitlab/slash_commands/deploy_spec.rb b/spec/lib/gitlab/slash_commands/deploy_spec.rb
index 5af234ff88e..94a95fb417f 100644
--- a/spec/lib/gitlab/slash_commands/deploy_spec.rb
+++ b/spec/lib/gitlab/slash_commands/deploy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::SlashCommands::Deploy do
+RSpec.describe Gitlab::SlashCommands::Deploy, feature_category: :team_planning do
describe '#execute' do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
diff --git a/spec/lib/gitlab/sql/pattern_spec.rb b/spec/lib/gitlab/sql/pattern_spec.rb
index 60bb006673f..a34ddf8773c 100644
--- a/spec/lib/gitlab/sql/pattern_spec.rb
+++ b/spec/lib/gitlab/sql/pattern_spec.rb
@@ -210,7 +210,7 @@ RSpec.describe Gitlab::SQL::Pattern do
let(:query) { 'foo' }
it 'returns a single ILIKE condition' do
- expect(fuzzy_arel_match.to_sql).to match(/title.*I?LIKE '\%foo\%'/)
+ expect(fuzzy_arel_match.to_sql).to match(/title.*I?LIKE '%foo%'/)
end
end
@@ -232,7 +232,7 @@ RSpec.describe Gitlab::SQL::Pattern do
let(:query) { 'foo baz' }
it 'returns a joining LIKE condition using a AND' do
- expect(fuzzy_arel_match.to_sql).to match(/title.+I?LIKE '\%foo\%' AND .*title.*I?LIKE '\%baz\%'/)
+ expect(fuzzy_arel_match.to_sql).to match(/title.+I?LIKE '%foo%' AND .*title.*I?LIKE '%baz%'/)
end
end
@@ -248,7 +248,7 @@ RSpec.describe Gitlab::SQL::Pattern do
let(:query) { 'foo ba' }
it 'returns a single ILIKE condition using the longer word' do
- expect(fuzzy_arel_match.to_sql).to match(/title.+I?LIKE '\%foo\%'/)
+ expect(fuzzy_arel_match.to_sql).to match(/title.+I?LIKE '%foo%'/)
end
end
@@ -256,7 +256,7 @@ RSpec.describe Gitlab::SQL::Pattern do
let(:query) { 'foo "really bar" baz' }
it 'returns a joining LIKE condition using a AND' do
- expect(fuzzy_arel_match.to_sql).to match(/title.+I?LIKE '\%foo\%' AND .*title.*I?LIKE '\%baz\%' AND .*title.*I?LIKE '\%really bar\%'/)
+ expect(fuzzy_arel_match.to_sql).to match(/title.+I?LIKE '%foo%' AND .*title.*I?LIKE '%baz%' AND .*title.*I?LIKE '%really bar%'/)
end
end
@@ -266,7 +266,7 @@ RSpec.describe Gitlab::SQL::Pattern do
subject(:fuzzy_arel_match) { Project.fuzzy_arel_match(Route.arel_table[:path], query) }
it 'returns a condition with the table and column name' do
- expect(fuzzy_arel_match.to_sql).to match(/"routes"."path".*ILIKE '\%foo\%'/)
+ expect(fuzzy_arel_match.to_sql).to match(/"routes"."path".*ILIKE '%foo%'/)
end
end
end
diff --git a/spec/lib/gitlab/ssh/signature_spec.rb b/spec/lib/gitlab/ssh/signature_spec.rb
index e8d366f0762..5149972dbf9 100644
--- a/spec/lib/gitlab/ssh/signature_spec.rb
+++ b/spec/lib/gitlab/ssh/signature_spec.rb
@@ -5,20 +5,20 @@ require 'spec_helper'
RSpec.describe Gitlab::Ssh::Signature do
# ssh-keygen -t ed25519
let_it_be(:committer_email) { 'ssh-commit-test@example.com' }
- let_it_be(:public_key_text) { 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJKOfqOH0fDde+Ua/1SObkXB1CEDF5M6UfARMpW3F87u' }
+ let_it_be(:public_key_text) { 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHZ8NHEnCIpC4mnot+BRxv6L+fq+TnN1CgsRrHWLmfwb' }
let_it_be_with_reload(:user) { create(:user, email: committer_email) }
- let_it_be_with_reload(:key) { create(:key, key: public_key_text, user: user) }
+ let_it_be_with_reload(:key) { create(:key, usage_type: :signing, key: public_key_text, user: user) }
let(:signed_text) { 'This message was signed by an ssh key' }
let(:signature_text) do
- # ssh-keygen -Y sign -n file -f id_test message.txt
+ # ssh-keygen -Y sign -n git -f id_test message.txt
<<~SIG
-----BEGIN SSH SIGNATURE-----
- U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgko5+o4fR8N175Rr/VI5uRcHUIQ
- MXkzpR8BEylbcXzu4AAAAEZmlsZQAAAAAAAAAGc2hhNTEyAAAAUwAAAAtzc2gtZWQyNTUx
- OQAAAECQa95KgBkgbMwIPNwHRjHu0WYrKvAc5O/FaBXlTDcPWQHi8WRDhbPNN6MqSYLg/S
- hsei6Y8VYPv85StrEHYdoF
+ U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgdnw0cScIikLiaei34FHG/ov5+r
+ 5Oc3UKCxGsdYuZ/BsAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
+ AAAAQDWOEauf0jXyA9caa5bOgK5QZD6c69pm+EbG3GMw5QBL3N/Gt+r413McCSJFohWWBk
+ Lxemg8NzZ0nB7lTFbaxQc=
-----END SSH SIGNATURE-----
SIG
end
@@ -51,37 +51,37 @@ RSpec.describe Gitlab::Ssh::Signature do
context 'when using an RSA key' do
let(:public_key_text) do
<<~KEY.delete("\n")
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCr3ucg9tLf87S2TxgeDaO4Cs5Mzv7wwi5w
- OnSG8hE/Zj7xzf0kXAYns/dHhPilkQMCulMQuGGprGzDJXZ9WrrVDHgBj2+kLB8cc+XYIb29
- HPsoz5a1T776wWrzs5cw3Vbb0ZEMPG27SfJ+HtIqnIAcgBoRxgP/+I9we7tVxrTuog/9jSzU
- H1IscwfwgKdUrvN5cyhqqxWspwZVlf6s4jaVjC9sKlF7u9CBCxqM2G7GZRKH2sEV2Tw0mT4z
- 39UQ5uz9+4hxWChosiQChrT9zSJDGWQm3WGn5ubYPeB/xINEKkFxuEupnSK7l8PQxeLAwlcN
- YHKMkHdO16O6PlpxvcLR1XVy4F12NXCxFjTr8GmFvJTvevf9iuFRmYQpffqm+EMN0shuhPag
- Z1poVK7ZMO49b4HD6csGwDjXEgNAnyi7oPV1WMHVy+xi2j+yaAgiVk50kgTwp9sGkHTiMTM8
- YWjCq+Hb+HXLINmqO5V1QChT7PAFYycmQ0Fe2x39eLLMHy0=
+ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDkq6ko8LMxf2NwyJKh+77KSDc7/ynPgUJD
+ IopkhqftuHFYe2Y+V3MBJnpzfSRwR2xGfXQUUzLU9AGyfZIO/ZLK2yvfhlO3k//5PbAaZb3y
+ urlnF9T1d2nhtfi8wuzsEn7Boh6qdoWPFIsloAL/X0PXH1HWKmzyNer92HKGrnWFfaaEMo0n
+ T3ureAhRG4IONyUcOK+DyoH+YbxXSlHnLO2oHHlWaP9RrJCHbfAQbfDhaZCI0cNkXXOwUwA4
+ yWGzDibfXZTvaYxpjbz1xoHmCAq8IrobCgkQaEg3PH3vPGnbP0TpViXjMnZyBZyT7tg9WHBV
+ kAsl0CizyUgZHPAPYuqKy5JNlnjVjeqYeIgdN4Tj7hpJ1n0hVpRk4zQNYRmAAj3GNqgPAsd0
+ 3i4rW8cqmhO0fmhP5DgQ7Mt5S9AgcTcCr6niPacK34XrwKiRjxXmCLjr36q8wuRU3QdMt+MK
+ Zxk/qJdAUIltz+nuGiwct0w+sWefYzmiRXu6hljBBrRAvnU=
KEY
end
let(:signature_text) do
<<~SIG
-----BEGIN SSH SIGNATURE-----
- U1NIU0lHAAAAAQAAAZcAAAAHc3NoLXJzYQAAAAMBAAEAAAGBAKve5yD20t/ztLZPGB4No7
- gKzkzO/vDCLnA6dIbyET9mPvHN/SRcBiez90eE+KWRAwK6UxC4YamsbMMldn1autUMeAGP
- b6QsHxxz5dghvb0c+yjPlrVPvvrBavOzlzDdVtvRkQw8bbtJ8n4e0iqcgByAGhHGA//4j3
- B7u1XGtO6iD/2NLNQfUixzB/CAp1Su83lzKGqrFaynBlWV/qziNpWML2wqUXu70IELGozY
- bsZlEofawRXZPDSZPjPf1RDm7P37iHFYKGiyJAKGtP3NIkMZZCbdYafm5tg94H/Eg0QqQX
- G4S6mdIruXw9DF4sDCVw1gcoyQd07Xo7o+WnG9wtHVdXLgXXY1cLEWNOvwaYW8lO969/2K
- 4VGZhCl9+qb4Qw3SyG6E9qBnWmhUrtkw7j1vgcPpywbAONcSA0CfKLug9XVYwdXL7GLaP7
- JoCCJWTnSSBPCn2waQdOIxMzxhaMKr4dv4dcsg2ao7lXVAKFPs8AVjJyZDQV7bHf14sswf
- LQAAAARmaWxlAAAAAAAAAAZzaGE1MTIAAAGUAAAADHJzYS1zaGEyLTUxMgAAAYAXgXpXWw
- A1fYHTUON+e1yrTw8AKB4ymfqpR9Zr1OUmYUKJ9xXvvyNCfKHL6XD14CkMu1Tx8Z3TTPG9
- C6uAXBniKRwwaLVOKffZMshf5sbjcy65KkqBPC7n/cDiCAeoJ8Y05trEDV62+pOpB2lLdv
- pwwg2o0JaoLbdRcKCD0pw1u0O7VDDngTKFZ4ghHrEslxwlFruht1h9hs3rmdITlT0RMNuU
- PHGAIB56u4E4UeoMd3D5rga+4Boj0s6551VgP3vCmcz9ZojPHhTCQdUZU1yHdEBTadYTq6
- UWHhQwDCUDkSNKCRxWo6EyKZQeTakedAt4qkdSpSUCKOJGWKmPOfAm2/sDEmSxffRdxRRg
- QUe8lklyFTZd6U/ZkJ/y7VR46fcSkEqLSLd9jAZT/3HJXbZfULpwsTcvcLcJLkCuzHEaU1
- LRyJBsanLCYHTv7ep5PvIuAngUWrXK2eb7oacVs94mWXfs1PG482Ym4+bZA5u0QliGTVaC
- M2EMhRTf0cqFuA4=
+ U1NIU0lHAAAAAQAAAZcAAAAHc3NoLXJzYQAAAAMBAAEAAAGBAOSrqSjwszF/Y3DIkqH7vs
+ pINzv/Kc+BQkMiimSGp+24cVh7Zj5XcwEmenN9JHBHbEZ9dBRTMtT0AbJ9kg79ksrbK9+G
+ U7eT//k9sBplvfK6uWcX1PV3aeG1+LzC7OwSfsGiHqp2hY8UiyWgAv9fQ9cfUdYqbPI16v
+ 3YcoaudYV9poQyjSdPe6t4CFEbgg43JRw4r4PKgf5hvFdKUecs7agceVZo/1GskIdt8BBt
+ 8OFpkIjRw2Rdc7BTADjJYbMOJt9dlO9pjGmNvPXGgeYICrwiuhsKCRBoSDc8fe88ads/RO
+ lWJeMydnIFnJPu2D1YcFWQCyXQKLPJSBkc8A9i6orLkk2WeNWN6ph4iB03hOPuGknWfSFW
+ lGTjNA1hGYACPcY2qA8Cx3TeLitbxyqaE7R+aE/kOBDsy3lL0CBxNwKvqeI9pwrfhevAqJ
+ GPFeYIuOvfqrzC5FTdB0y34wpnGT+ol0BQiW3P6e4aLBy3TD6xZ59jOaJFe7qGWMEGtEC+
+ dQAAAANnaXQAAAAAAAAABnNoYTUxMgAAAZQAAAAMcnNhLXNoYTItNTEyAAABgEnuYyYOlM
+ CSR+wvmBY7eKHzFor5ByM7N4F7VZAGKK/vbS3C38xDdiJZwsZUscpe5WspJVCWUTkFxXjn
+ GW7vseIfJBVkyqnu2uN8X1j/VDLFESEajcchPhPxtfAMK1/NL99O7rCrYX2pmpkm9tWsFk
+ NX5B93sRyDUnHAOkB+zdqU8P0xdzc8kmBl5OOqu1rSjZIgnQjcauEIRIUN+rFuiRRmIvJp
+ UvMhkKSsRCH93btGW7A6x5e4iPzP+Em0UFYJdOx2lvu9aVAktQzysGwDN+9c4IC+07UHKT
+ UIE5jSbR1QKfavcywNQnCltQ2bTxpnm4A6QHKcdr9Q57dV014FgtmtT/Pw03iyl5MwbEqW
+ 7YEHSkMyAcd1rjEpOCN2pJjjbrOKLePG0R2ffgvVJnTWGFklCxsJ1/7IASHst1wg1/gu1g
+ Kx/TEv+gOKpehAgs2Sz/4kZtFuHO2dbHYC3UrPR5HT8JnQWeCfiT0qwsVQ6xribw0jEYyd
+ ZBNWKkPdNocAbA==
-----END SSH SIGNATURE-----
SIG
end
@@ -98,10 +98,10 @@ RSpec.describe Gitlab::Ssh::Signature do
let(:signature_text) do
<<~SIG
-----BEGIN SSH SIGNATURE-----
- U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgko5+o4fR8N175Rr/VI5uRcHUIQ
- MXkzpR8BEylbcXzu4AAAAEZmlsZQAAAAAAAAAGc2hhNTEyAAAAUwAAAAtzc2gtZWQyNTUx
- OQAAAEC1y2I7o3KqKFlnM+MLkhIo+uRX3YQOYCqycfibyfvmkZTcwqMxgNBInBM9pY3VvS
- sbW2iEdgz34agHbi+1BHIM
+ U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgdnw0cScIikLiaei34FHG/ov5+r
+ 5Oc3UKCxGsdYuZ/BsAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
+ AAAAQP2liwaQ44PC9oXf5Xzjq20WLdWEK9nyonvDGtduGUXMOL4yP5A6WvKz7kSt7Vba/U
+ MNK0nmnNc7Aokfh/2eRQE=
-----END SSH SIGNATURE-----
SIG
end
@@ -151,16 +151,32 @@ RSpec.describe Gitlab::Ssh::Signature do
context 'when user email is not verified' do
before do
+ email = user.emails.find_by(email: committer_email)
+ email.update!(confirmed_at: nil)
user.update!(confirmed_at: nil)
end
- it_behaves_like 'unverified signature'
+ it 'reports unverified status' do
+ expect(signature.verification_status).to eq(:unverified)
+ end
+ end
+
+ context 'when no user exist with the committer email' do
+ before do
+ user.delete
+ end
+
+ it 'reports other_user status' do
+ expect(signature.verification_status).to eq(:other_user)
+ end
end
context 'when no user exists with the committer email' do
let(:committer_email) { 'different-email+ssh-commit-test@example.com' }
- it_behaves_like 'unverified signature'
+ it 'reports other_user status' do
+ expect(signature.verification_status).to eq(:other_user)
+ end
end
context 'when signature is invalid' do
@@ -178,6 +194,21 @@ RSpec.describe Gitlab::Ssh::Signature do
it_behaves_like 'unverified signature'
end
+ context 'when signature is for a different namespace' do
+ let(:signature_text) do
+ <<~SIG
+ -----BEGIN SSH SIGNATURE-----
+ U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgdnw0cScIikLiaei34FHG/ov5+r
+ 5Oc3UKCxGsdYuZ/BsAAAAEZmlsZQAAAAAAAAAGc2hhNTEyAAAAUwAAAAtzc2gtZWQyNTUx
+ OQAAAEAd6Psg4D/5IdSVTy35D4t2iNX4udJnX8JrUCjQl0GoPl1vzPjgyvxdzdoQl6bh1w
+ 4rror3RuzUYBGzIioIc1MP
+ -----END SSH SIGNATURE-----
+ SIG
+ end
+
+ it_behaves_like 'unverified signature'
+ end
+
context 'when signature is for a different message' do
let(:signature_text) do
<<~SIG
@@ -204,13 +235,25 @@ RSpec.describe Gitlab::Ssh::Signature do
it_behaves_like 'unverified signature'
end
- context 'when key does not exist in GitLab' do
- before do
- key.delete
+ context 'when the signing key does not exist in GitLab' do
+ context 'when the key is not a signing one' do
+ before do
+ key.auth!
+ end
+
+ it 'reports unknown_key status' do
+ expect(signature.verification_status).to eq(:unknown_key)
+ end
end
- it 'reports unknown_key status' do
- expect(signature.verification_status).to eq(:unknown_key)
+ context 'when the key is removed' do
+ before do
+ key.delete
+ end
+
+ it 'reports unknown_key status' do
+ expect(signature.verification_status).to eq(:unknown_key)
+ end
end
end
diff --git a/spec/lib/gitlab/ssh_public_key_spec.rb b/spec/lib/gitlab/ssh_public_key_spec.rb
index a2524314458..d4b0b1ea53b 100644
--- a/spec/lib/gitlab/ssh_public_key_spec.rb
+++ b/spec/lib/gitlab/ssh_public_key_spec.rb
@@ -260,8 +260,7 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true, fips_mode: false do
context 'when the key is represented by a subclass of the class that is in the list of supported technologies' do
it 'raises error' do
rsa_subclass = Class.new(described_class.technology(:rsa).key_class) do
- def initialize
- end
+ def initialize; end
end
key = rsa_subclass.new
diff --git a/spec/lib/gitlab/tracking/destinations/snowplow_spec.rb b/spec/lib/gitlab/tracking/destinations/snowplow_spec.rb
index 1d4725cf405..e79535358f9 100644
--- a/spec/lib/gitlab/tracking/destinations/snowplow_spec.rb
+++ b/spec/lib/gitlab/tracking/destinations/snowplow_spec.rb
@@ -3,8 +3,11 @@
require 'spec_helper'
RSpec.describe Gitlab::Tracking::Destinations::Snowplow, :do_not_stub_snowplow_by_default do
- let(:emitter) { SnowplowTracker::Emitter.new('localhost', buffer_size: 1) }
- let(:tracker) { SnowplowTracker::Tracker.new(emitter, SnowplowTracker::Subject.new, 'namespace', 'app_id') }
+ let(:emitter) { SnowplowTracker::Emitter.new(endpoint: 'localhost', options: { buffer_size: 1 }) }
+ let(:tracker) do
+ SnowplowTracker::Tracker.new(emitters: [emitter], subject: SnowplowTracker::Subject.new, namespace: 'namespace',
+ app_id: 'app_id')
+ end
before do
stub_application_setting(snowplow_collector_hostname: 'gitfoo.com')
@@ -21,16 +24,19 @@ RSpec.describe Gitlab::Tracking::Destinations::Snowplow, :do_not_stub_snowplow_b
expect(SnowplowTracker::AsyncEmitter)
.to receive(:new)
- .with('gitfoo.com',
- { protocol: 'https',
- on_success: subject.method(:increment_successful_events_emissions),
- on_failure: subject.method(:failure_callback) })
+ .with(endpoint: 'gitfoo.com',
+ options: { protocol: 'https',
+ on_success: subject.method(:increment_successful_events_emissions),
+ on_failure: subject.method(:failure_callback) })
.and_return(emitter)
expect(SnowplowTracker::Tracker)
.to receive(:new)
- .with(emitter, an_instance_of(SnowplowTracker::Subject), described_class::SNOWPLOW_NAMESPACE, '_abc123_')
- .and_return(tracker)
+ .with(emitters: [emitter],
+ subject: an_instance_of(SnowplowTracker::Subject),
+ namespace: described_class::SNOWPLOW_NAMESPACE,
+ app_id: '_abc123_')
+ .and_return(tracker)
end
describe '#event' do
@@ -41,7 +47,8 @@ RSpec.describe Gitlab::Tracking::Destinations::Snowplow, :do_not_stub_snowplow_b
expect(tracker)
.to have_received(:track_struct_event)
- .with('category', 'action', 'label', 'property', 1.5, nil, (Time.now.to_f * 1000).to_i)
+ .with(category: 'category', action: 'action', label: 'label', property: 'property', value: 1.5, context: nil,
+ tstamp: (Time.now.to_f * 1000).to_i)
end
it 'increase total snowplow events counter' do
diff --git a/spec/lib/gitlab/tracking/event_definition_spec.rb b/spec/lib/gitlab/tracking/event_definition_spec.rb
index 623009e9a30..c8e616b092b 100644
--- a/spec/lib/gitlab/tracking/event_definition_spec.rb
+++ b/spec/lib/gitlab/tracking/event_definition_spec.rb
@@ -83,6 +83,11 @@ RSpec.describe Gitlab::Tracking::EventDefinition do
subject { described_class.definitions }
+ after do
+ FileUtils.rm_rf(metric1)
+ FileUtils.rm_rf(metric2)
+ end
+
it 'has empty list when there are no definition files' do
is_expected.to be_empty
end
@@ -92,10 +97,5 @@ RSpec.describe Gitlab::Tracking::EventDefinition do
is_expected.to be_one
end
-
- after do
- FileUtils.rm_rf(metric1)
- FileUtils.rm_rf(metric2)
- end
end
end
diff --git a/spec/lib/gitlab/tracking/service_ping_context_spec.rb b/spec/lib/gitlab/tracking/service_ping_context_spec.rb
index d70dfaa4e0b..7530650b902 100644
--- a/spec/lib/gitlab/tracking/service_ping_context_spec.rb
+++ b/spec/lib/gitlab/tracking/service_ping_context_spec.rb
@@ -4,16 +4,55 @@ require 'spec_helper'
RSpec.describe Gitlab::Tracking::ServicePingContext do
describe '#init' do
- it 'does not accept unsupported data sources' do
- expect { described_class.new(data_source: :random, event: 'event a') }.to raise_error(ArgumentError)
+ using RSpec::Parameterized::TableSyntax
+
+ context 'with valid configuration' do
+ where(:data_source, :event, :key_path) do
+ :redis | nil | 'counts.some_metric'
+ :redis_hll | 'some_event' | nil
+ end
+
+ with_them do
+ it 'does not raise errors' do
+ expect { described_class.new(data_source: data_source, event: event, key_path: key_path) }.not_to raise_error
+ end
+ end
+ end
+
+ context 'with invalid configuration' do
+ where(:data_source, :event, :key_path) do
+ :redis | nil | nil
+ :redis | 'some_event' | nil
+ :redis_hll | nil | nil
+ :redis_hll | nil | 'some key_path'
+ :random | 'some_event' | nil
+ end
+
+ with_them do
+ subject(:new_instance) { described_class.new(data_source: data_source, event: event, key_path: key_path) }
+
+ it 'does not raise errors' do
+ expect { new_instance }.to raise_error(ArgumentError)
+ end
+ end
end
end
describe '#to_context' do
- let(:subject) { described_class.new(data_source: :redis_hll, event: 'sample_event') }
+ context 'for redis_hll data source' do
+ let(:context_instance) { described_class.new(data_source: :redis_hll, event: 'sample_event') }
+
+ it 'contains event_name' do
+ expect(context_instance.to_context.to_json.dig(:data, :event_name)).to eq('sample_event')
+ end
+ end
+
+ context 'for redis data source' do
+ let(:context_instance) { described_class.new(data_source: :redis, key_path: 'counts.sample_metric') }
- it 'contains event_name' do
- expect(subject.to_context.to_json.dig(:data, :event_name)).to eq('sample_event')
+ it 'contains event_name' do
+ expect(context_instance.to_context.to_json.dig(:data, :key_path)).to eq('counts.sample_metric')
+ end
end
end
end
diff --git a/spec/lib/gitlab/tracking_spec.rb b/spec/lib/gitlab/tracking_spec.rb
index e11175c776d..99ca402616a 100644
--- a/spec/lib/gitlab/tracking_spec.rb
+++ b/spec/lib/gitlab/tracking_spec.rb
@@ -180,15 +180,6 @@ RSpec.describe Gitlab::Tracking do
it_behaves_like 'delegates to destination', Gitlab::Tracking::Destinations::SnowplowMicro
end
-
- it 'tracks errors' do
- expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception).with(
- an_instance_of(ContractError),
- snowplow_category: nil, snowplow_action: 'some_action'
- )
-
- described_class.event(nil, 'some_action')
- end
end
describe '.definition' do
diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb
index 8f505606e04..05f7af7606d 100644
--- a/spec/lib/gitlab/url_blocker_spec.rb
+++ b/spec/lib/gitlab/url_blocker_spec.rb
@@ -5,8 +5,10 @@ require 'spec_helper'
RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
include StubRequests
+ let(:schemes) { %w[http https] }
+
describe '#validate!' do
- subject { described_class.validate!(import_url) }
+ subject { described_class.validate!(import_url, schemes: schemes) }
shared_examples 'validates URI and hostname' do
it 'runs the url validations' do
@@ -59,7 +61,7 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
end
context 'when allow_object_storage is true' do
- subject { described_class.validate!(import_url, allow_object_storage: true) }
+ subject { described_class.validate!(import_url, allow_object_storage: true, schemes: schemes) }
context 'with a local domain name' do
let(:host) { 'http://review-minio-svc.svc:9000' }
@@ -218,7 +220,7 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
end
context 'disabled DNS rebinding protection' do
- subject { described_class.validate!(import_url, dns_rebind_protection: false) }
+ subject { described_class.validate!(import_url, dns_rebind_protection: false, schemes: schemes) }
context 'when URI is internal' do
let(:import_url) { 'http://localhost' }
@@ -278,115 +280,114 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
it 'allows imports from configured web host and port' do
import_url = "http://#{Gitlab.host_with_port}/t.git"
- expect(described_class.blocked_url?(import_url)).to be false
+ expect(described_class.blocked_url?(import_url, schemes: schemes)).to be false
end
it 'allows mirroring from configured SSH host and port' do
import_url = "ssh://#{Gitlab.config.gitlab_shell.ssh_host}:#{Gitlab.config.gitlab_shell.ssh_port}/t.git"
- expect(described_class.blocked_url?(import_url)).to be false
+ expect(described_class.blocked_url?(import_url, schemes: schemes)).to be false
end
it 'returns true for bad localhost hostname' do
- expect(described_class.blocked_url?('https://localhost:65535/foo/foo.git')).to be true
+ expect(described_class.blocked_url?('https://localhost:65535/foo/foo.git', schemes: schemes)).to be true
end
it 'returns true for bad port' do
- expect(described_class.blocked_url?('https://gitlab.com:25/foo/foo.git', ports: ports)).to be true
+ expect(described_class.blocked_url?('https://gitlab.com:25/foo/foo.git', ports: ports, schemes: schemes)).to be true
end
it 'returns true for bad scheme' do
expect(described_class.blocked_url?('https://gitlab.com/foo/foo.git', schemes: ['https'])).to be false
- expect(described_class.blocked_url?('https://gitlab.com/foo/foo.git')).to be false
expect(described_class.blocked_url?('https://gitlab.com/foo/foo.git', schemes: ['http'])).to be true
end
it 'returns true for bad protocol on configured web/SSH host and ports' do
web_url = "javascript://#{Gitlab.host_with_port}/t.git%0aalert(1)"
- expect(described_class.blocked_url?(web_url)).to be true
+ expect(described_class.blocked_url?(web_url, schemes: schemes)).to be true
ssh_url = "javascript://#{Gitlab.config.gitlab_shell.ssh_host}:#{Gitlab.config.gitlab_shell.ssh_port}/t.git%0aalert(1)"
- expect(described_class.blocked_url?(ssh_url)).to be true
+ expect(described_class.blocked_url?(ssh_url, schemes: schemes)).to be true
end
it 'returns true for localhost IPs' do
- expect(described_class.blocked_url?('https://[0:0:0:0:0:0:0:0]/foo/foo.git')).to be true
- expect(described_class.blocked_url?('https://0.0.0.0/foo/foo.git')).to be true
- expect(described_class.blocked_url?('https://[::]/foo/foo.git')).to be true
+ expect(described_class.blocked_url?('https://[0:0:0:0:0:0:0:0]/foo/foo.git', schemes: schemes)).to be true
+ expect(described_class.blocked_url?('https://0.0.0.0/foo/foo.git', schemes: schemes)).to be true
+ expect(described_class.blocked_url?('https://[::]/foo/foo.git', schemes: schemes)).to be true
end
it 'returns true for loopback IP' do
- expect(described_class.blocked_url?('https://127.0.0.2/foo/foo.git')).to be true
- expect(described_class.blocked_url?('https://127.0.0.1/foo/foo.git')).to be true
- expect(described_class.blocked_url?('https://[::1]/foo/foo.git')).to be true
+ expect(described_class.blocked_url?('https://127.0.0.2/foo/foo.git', schemes: schemes)).to be true
+ expect(described_class.blocked_url?('https://127.0.0.1/foo/foo.git', schemes: schemes)).to be true
+ expect(described_class.blocked_url?('https://[::1]/foo/foo.git', schemes: schemes)).to be true
end
it 'returns true for alternative version of 127.0.0.1 (0177.1)' do
- expect(described_class.blocked_url?('https://0177.1:65535/foo/foo.git')).to be true
+ expect(described_class.blocked_url?('https://0177.1:65535/foo/foo.git', schemes: schemes)).to be true
end
it 'returns true for alternative version of 127.0.0.1 (017700000001)' do
- expect(described_class.blocked_url?('https://017700000001:65535/foo/foo.git')).to be true
+ expect(described_class.blocked_url?('https://017700000001:65535/foo/foo.git', schemes: schemes)).to be true
end
it 'returns true for alternative version of 127.0.0.1 (0x7f.1)' do
- expect(described_class.blocked_url?('https://0x7f.1:65535/foo/foo.git')).to be true
+ expect(described_class.blocked_url?('https://0x7f.1:65535/foo/foo.git', schemes: schemes)).to be true
end
it 'returns true for alternative version of 127.0.0.1 (0x7f.0.0.1)' do
- expect(described_class.blocked_url?('https://0x7f.0.0.1:65535/foo/foo.git')).to be true
+ expect(described_class.blocked_url?('https://0x7f.0.0.1:65535/foo/foo.git', schemes: schemes)).to be true
end
it 'returns true for alternative version of 127.0.0.1 (0x7f000001)' do
- expect(described_class.blocked_url?('https://0x7f000001:65535/foo/foo.git')).to be true
+ expect(described_class.blocked_url?('https://0x7f000001:65535/foo/foo.git', schemes: schemes)).to be true
end
it 'returns true for alternative version of 127.0.0.1 (2130706433)' do
- expect(described_class.blocked_url?('https://2130706433:65535/foo/foo.git')).to be true
+ expect(described_class.blocked_url?('https://2130706433:65535/foo/foo.git', schemes: schemes)).to be true
end
it 'returns true for alternative version of 127.0.0.1 (127.000.000.001)' do
- expect(described_class.blocked_url?('https://127.000.000.001:65535/foo/foo.git')).to be true
+ expect(described_class.blocked_url?('https://127.000.000.001:65535/foo/foo.git', schemes: schemes)).to be true
end
it 'returns true for alternative version of 127.0.0.1 (127.0.1)' do
- expect(described_class.blocked_url?('https://127.0.1:65535/foo/foo.git')).to be true
+ expect(described_class.blocked_url?('https://127.0.1:65535/foo/foo.git', schemes: schemes)).to be true
end
context 'with ipv6 mapped address' do
it 'returns true for localhost IPs' do
- expect(described_class.blocked_url?('https://[0:0:0:0:0:ffff:0.0.0.0]/foo/foo.git')).to be true
- expect(described_class.blocked_url?('https://[::ffff:0.0.0.0]/foo/foo.git')).to be true
- expect(described_class.blocked_url?('https://[::ffff:0:0]/foo/foo.git')).to be true
+ expect(described_class.blocked_url?('https://[0:0:0:0:0:ffff:0.0.0.0]/foo/foo.git', schemes: schemes)).to be true
+ expect(described_class.blocked_url?('https://[::ffff:0.0.0.0]/foo/foo.git', schemes: schemes)).to be true
+ expect(described_class.blocked_url?('https://[::ffff:0:0]/foo/foo.git', schemes: schemes)).to be true
end
it 'returns true for loopback IPs' do
- expect(described_class.blocked_url?('https://[0:0:0:0:0:ffff:127.0.0.1]/foo/foo.git')).to be true
- expect(described_class.blocked_url?('https://[::ffff:127.0.0.1]/foo/foo.git')).to be true
- expect(described_class.blocked_url?('https://[::ffff:7f00:1]/foo/foo.git')).to be true
- expect(described_class.blocked_url?('https://[0:0:0:0:0:ffff:127.0.0.2]/foo/foo.git')).to be true
- expect(described_class.blocked_url?('https://[::ffff:127.0.0.2]/foo/foo.git')).to be true
- expect(described_class.blocked_url?('https://[::ffff:7f00:2]/foo/foo.git')).to be true
+ expect(described_class.blocked_url?('https://[0:0:0:0:0:ffff:127.0.0.1]/foo/foo.git', schemes: schemes)).to be true
+ expect(described_class.blocked_url?('https://[::ffff:127.0.0.1]/foo/foo.git', schemes: schemes)).to be true
+ expect(described_class.blocked_url?('https://[::ffff:7f00:1]/foo/foo.git', schemes: schemes)).to be true
+ expect(described_class.blocked_url?('https://[0:0:0:0:0:ffff:127.0.0.2]/foo/foo.git', schemes: schemes)).to be true
+ expect(described_class.blocked_url?('https://[::ffff:127.0.0.2]/foo/foo.git', schemes: schemes)).to be true
+ expect(described_class.blocked_url?('https://[::ffff:7f00:2]/foo/foo.git', schemes: schemes)).to be true
end
end
it 'returns true for a non-alphanumeric hostname' do
aggregate_failures do
- expect(described_class).to be_blocked_url('ssh://-oProxyCommand=whoami/a')
+ expect(described_class).to be_blocked_url('ssh://-oProxyCommand=whoami/a', schemes: ['ssh'])
# The leading character here is a Unicode "soft hyphen"
- expect(described_class).to be_blocked_url('ssh://­oProxyCommand=whoami/a')
+ expect(described_class).to be_blocked_url('ssh://­oProxyCommand=whoami/a', schemes: ['ssh'])
# Unicode alphanumerics are allowed
- expect(described_class).not_to be_blocked_url('ssh://ÄŸitlab.com/a')
+ expect(described_class).not_to be_blocked_url('ssh://ÄŸitlab.com/a', schemes: ['ssh'])
end
end
it 'returns true for invalid URL' do
- expect(described_class.blocked_url?('http://:8080')).to be true
+ expect(described_class.blocked_url?('http://:8080', schemes: schemes)).to be true
end
it 'returns false for legitimate URL' do
- expect(described_class.blocked_url?('https://gitlab.com/foo/foo.git')).to be false
+ expect(described_class.blocked_url?('https://gitlab.com/foo/foo.git', schemes: schemes)).to be false
end
context 'when allow_local_network is' do
@@ -471,33 +472,33 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
end
context 'true (default)' do
- it_behaves_like 'allows local requests', { allow_localhost: true, allow_local_network: true }
+ it_behaves_like 'allows local requests', { allow_localhost: true, allow_local_network: true, schemes: %w[http https] }
end
context 'false' do
it 'blocks urls from private networks' do
local_ips.each do |ip|
stub_domain_resolv(fake_domain, ip) do
- expect(described_class).to be_blocked_url("http://#{fake_domain}", allow_local_network: false)
+ expect(described_class).to be_blocked_url("http://#{fake_domain}", allow_local_network: false, schemes: schemes)
end
- expect(described_class).to be_blocked_url("http://#{ip}", allow_local_network: false)
+ expect(described_class).to be_blocked_url("http://#{ip}", allow_local_network: false, schemes: schemes)
end
end
it 'blocks IPv4 link-local endpoints' do
- expect(described_class).to be_blocked_url('http://169.254.169.254', allow_local_network: false)
- expect(described_class).to be_blocked_url('http://169.254.168.100', allow_local_network: false)
+ expect(described_class).to be_blocked_url('http://169.254.169.254', allow_local_network: false, schemes: schemes)
+ expect(described_class).to be_blocked_url('http://169.254.168.100', allow_local_network: false, schemes: schemes)
end
it 'blocks IPv6 link-local endpoints' do
- expect(described_class).to be_blocked_url('http://[0:0:0:0:0:ffff:169.254.169.254]', allow_local_network: false)
- expect(described_class).to be_blocked_url('http://[::ffff:169.254.169.254]', allow_local_network: false)
- expect(described_class).to be_blocked_url('http://[::ffff:a9fe:a9fe]', allow_local_network: false)
- expect(described_class).to be_blocked_url('http://[0:0:0:0:0:ffff:169.254.168.100]', allow_local_network: false)
- expect(described_class).to be_blocked_url('http://[::ffff:169.254.168.100]', allow_local_network: false)
- expect(described_class).to be_blocked_url('http://[::ffff:a9fe:a864]', allow_local_network: false)
- expect(described_class).to be_blocked_url('http://[fe80::c800:eff:fe74:8]', allow_local_network: false)
+ expect(described_class).to be_blocked_url('http://[0:0:0:0:0:ffff:169.254.169.254]', allow_local_network: false, schemes: schemes)
+ expect(described_class).to be_blocked_url('http://[::ffff:169.254.169.254]', allow_local_network: false, schemes: schemes)
+ expect(described_class).to be_blocked_url('http://[::ffff:a9fe:a9fe]', allow_local_network: false, schemes: schemes)
+ expect(described_class).to be_blocked_url('http://[0:0:0:0:0:ffff:169.254.168.100]', allow_local_network: false, schemes: schemes)
+ expect(described_class).to be_blocked_url('http://[::ffff:169.254.168.100]', allow_local_network: false, schemes: schemes)
+ expect(described_class).to be_blocked_url('http://[::ffff:a9fe:a864]', allow_local_network: false, schemes: schemes)
+ expect(described_class).to be_blocked_url('http://[fe80::c800:eff:fe74:8]', allow_local_network: false, schemes: schemes)
end
it 'blocks limited broadcast address 255.255.255.255 and variants' do
@@ -507,7 +508,7 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
stub_env('RSPEC_ALLOW_INVALID_URLS', 'false')
limited_broadcast_address_variants.each do |variant|
- expect(described_class).to be_blocked_url("https://#{variant}", allow_local_network: false), "Expected #{variant} to be blocked"
+ expect(described_class).to be_blocked_url("https://#{variant}", allow_local_network: false, schemes: schemes), "Expected #{variant} to be blocked"
end
end
@@ -515,7 +516,8 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
let(:url_blocker_attributes) do
{
allow_localhost: false,
- allow_local_network: false
+ allow_local_network: false,
+ schemes: schemes
}
end
@@ -545,7 +547,7 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
]
end
- it_behaves_like 'allows local requests', { allow_localhost: false, allow_local_network: false }
+ it_behaves_like 'allows local requests', { allow_localhost: false, allow_local_network: false, schemes: %w[http https] }
it 'allows IP when dns_rebind_protection is disabled' do
url = "http://example.com"
@@ -622,7 +624,7 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
end
it do
- expect(described_class).not_to be_blocked_url(url, dns_rebind_protection: dns_rebind_value)
+ expect(described_class).not_to be_blocked_url(url, dns_rebind_protection: dns_rebind_value, schemes: schemes)
end
end
@@ -676,26 +678,26 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
context 'when enforce_user is' do
context 'false (default)' do
it 'does not block urls with a non-alphanumeric username' do
- expect(described_class).not_to be_blocked_url('ssh://-oProxyCommand=whoami@example.com/a')
+ expect(described_class).not_to be_blocked_url('ssh://-oProxyCommand=whoami@example.com/a', schemes: ['ssh'])
# The leading character here is a Unicode "soft hyphen"
- expect(described_class).not_to be_blocked_url('ssh://­oProxyCommand=whoami@example.com/a')
+ expect(described_class).not_to be_blocked_url('ssh://­oProxyCommand=whoami@example.com/a', schemes: ['ssh'])
# Unicode alphanumerics are allowed
- expect(described_class).not_to be_blocked_url('ssh://ÄŸitlab@example.com/a')
+ expect(described_class).not_to be_blocked_url('ssh://ÄŸitlab@example.com/a', schemes: ['ssh'])
end
end
context 'true' do
it 'blocks urls with a non-alphanumeric username' do
aggregate_failures do
- expect(described_class).to be_blocked_url('ssh://-oProxyCommand=whoami@example.com/a', enforce_user: true)
+ expect(described_class).to be_blocked_url('ssh://-oProxyCommand=whoami@example.com/a', enforce_user: true, schemes: ['ssh'])
# The leading character here is a Unicode "soft hyphen"
- expect(described_class).to be_blocked_url('ssh://­oProxyCommand=whoami@example.com/a', enforce_user: true)
+ expect(described_class).to be_blocked_url('ssh://­oProxyCommand=whoami@example.com/a', enforce_user: true, schemes: ['ssh'])
# Unicode alphanumerics are allowed
- expect(described_class).not_to be_blocked_url('ssh://ÄŸitlab@example.com/a', enforce_user: true)
+ expect(described_class).not_to be_blocked_url('ssh://ÄŸitlab@example.com/a', enforce_user: true, schemes: ['ssh'])
end
end
end
@@ -703,35 +705,35 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
context 'when ascii_only is true' do
it 'returns true for unicode domain' do
- expect(described_class.blocked_url?('https://ð•˜itⅼαƄ.com/foo/foo.bar', ascii_only: true)).to be true
+ expect(described_class.blocked_url?('https://ð•˜itⅼαƄ.com/foo/foo.bar', ascii_only: true, schemes: schemes)).to be true
end
it 'returns true for unicode tld' do
- expect(described_class.blocked_url?('https://gitlab.ᴄοï½/foo/foo.bar', ascii_only: true)).to be true
+ expect(described_class.blocked_url?('https://gitlab.ᴄοï½/foo/foo.bar', ascii_only: true, schemes: schemes)).to be true
end
it 'returns true for unicode path' do
- expect(described_class.blocked_url?('https://gitlab.com/ð’‡Î¿Î¿/ð’‡Î¿Î¿.Ƅαê®', ascii_only: true)).to be true
+ expect(described_class.blocked_url?('https://gitlab.com/ð’‡Î¿Î¿/ð’‡Î¿Î¿.Ƅαê®', ascii_only: true, schemes: schemes)).to be true
end
it 'returns true for IDNA deviations' do
- expect(described_class.blocked_url?('https://mißile.com/foo/foo.bar', ascii_only: true)).to be true
- expect(described_class.blocked_url?('https://miςςile.com/foo/foo.bar', ascii_only: true)).to be true
- expect(described_class.blocked_url?('https://gitâ€lab.com/foo/foo.bar', ascii_only: true)).to be true
- expect(described_class.blocked_url?('https://git‌lab.com/foo/foo.bar', ascii_only: true)).to be true
+ expect(described_class.blocked_url?('https://mißile.com/foo/foo.bar', ascii_only: true, schemes: schemes)).to be true
+ expect(described_class.blocked_url?('https://miςςile.com/foo/foo.bar', ascii_only: true, schemes: schemes)).to be true
+ expect(described_class.blocked_url?('https://gitâ€lab.com/foo/foo.bar', ascii_only: true, schemes: schemes)).to be true
+ expect(described_class.blocked_url?('https://git‌lab.com/foo/foo.bar', ascii_only: true, schemes: schemes)).to be true
end
end
it 'blocks urls with invalid ip address' do
stub_env('RSPEC_ALLOW_INVALID_URLS', 'false')
- expect(described_class).to be_blocked_url('http://8.8.8.8.8')
+ expect(described_class).to be_blocked_url('http://8.8.8.8.8', schemes: schemes)
end
it 'blocks urls whose hostname cannot be resolved' do
stub_env('RSPEC_ALLOW_INVALID_URLS', 'false')
- expect(described_class).to be_blocked_url('http://foobar.x')
+ expect(described_class).to be_blocked_url('http://foobar.x', schemes: schemes)
end
context 'when gitlab is running on a non-default port' do
@@ -743,13 +745,13 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
it 'returns true for url targeting the wrong port' do
stub_domain_resolv('gitlab.local', '127.0.0.1') do
- expect(described_class).to be_blocked_url("http://gitlab.local/foo")
+ expect(described_class).to be_blocked_url("http://gitlab.local/foo", schemes: schemes)
end
end
it 'does not block url on gitlab port' do
stub_domain_resolv('gitlab.local', '127.0.0.1') do
- expect(described_class).not_to be_blocked_url("http://gitlab.local:#{gitlab_port}/foo")
+ expect(described_class).not_to be_blocked_url("http://gitlab.local:#{gitlab_port}/foo", schemes: schemes)
end
end
end
diff --git a/spec/lib/gitlab/usage/metric_definition_spec.rb b/spec/lib/gitlab/usage/metric_definition_spec.rb
index 931340947a2..4b835d11975 100644
--- a/spec/lib/gitlab/usage/metric_definition_spec.rb
+++ b/spec/lib/gitlab/usage/metric_definition_spec.rb
@@ -233,6 +233,11 @@ RSpec.describe Gitlab::Usage::MetricDefinition do
subject { described_class.send(:load_all!) }
+ after do
+ FileUtils.rm_rf(metric1)
+ FileUtils.rm_rf(metric2)
+ end
+
it 'has empty list when there are no definition files' do
is_expected.to be_empty
end
@@ -251,11 +256,6 @@ RSpec.describe Gitlab::Usage::MetricDefinition do
subject
end
-
- after do
- FileUtils.rm_rf(metric1)
- FileUtils.rm_rf(metric2)
- end
end
describe 'dump_metrics_yaml' do
diff --git a/spec/lib/gitlab/usage/metrics/aggregates/aggregate_spec.rb b/spec/lib/gitlab/usage/metrics/aggregates/aggregate_spec.rb
index 1f00f7bbec3..10e336e9235 100644
--- a/spec/lib/gitlab/usage/metrics/aggregates/aggregate_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/aggregates/aggregate_spec.rb
@@ -12,6 +12,12 @@ RSpec.describe Gitlab::Usage::Metrics::Aggregates::Aggregate, :clean_gitlab_redi
describe '.calculate_count_for_aggregation' do
using RSpec::Parameterized::TableSyntax
+ before do
+ %w[event1 event2].each do |event_name|
+ allow(Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:known_event?).with(event_name).and_return(true)
+ end
+ end
+
context 'with valid configuration' do
where(:number_of_days, :operator, :datasource, :expected_method) do
28 | 'AND' | 'redis_hll' | :calculate_metrics_intersections
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/count_merge_request_authors_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/count_merge_request_authors_metric_spec.rb
deleted file mode 100644
index 92459e92eac..00000000000
--- a/spec/lib/gitlab/usage/metrics/instrumentations/count_merge_request_authors_metric_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountMergeRequestAuthorsMetric do
- let(:expected_value) { 1 }
- let(:start) { 30.days.ago.to_s(:db) }
- let(:finish) { 2.days.ago.to_s(:db) }
-
- let(:expected_query) do
- "SELECT COUNT(DISTINCT \"merge_requests\".\"author_id\") FROM \"merge_requests\"" \
- " WHERE \"merge_requests\".\"created_at\" BETWEEN '#{start}' AND '#{finish}'"
- end
-
- before do
- user = create(:user)
- user2 = create(:user)
-
- create(:merge_request, created_at: 1.year.ago, author: user)
- create(:merge_request, created_at: 1.week.ago, author: user2)
- create(:merge_request, created_at: 1.week.ago, author: user2)
- end
-
- it_behaves_like 'a correct instrumented metric value and query', { time_frame: '28d' }
-end
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/database_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/database_metric_spec.rb
index f1ecc8c8ab5..8ca42a6f007 100644
--- a/spec/lib/gitlab/usage/metrics/instrumentations/database_metric_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/database_metric_spec.rb
@@ -196,6 +196,22 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::DatabaseMetric do
end
end
+ context 'with 7 days time frame' do
+ subject do
+ database_metric_class.tap do |metric_class|
+ metric_class.relation { Issue }
+ metric_class.operation :count
+ end.new(time_frame: '7d')
+ end
+
+ it 'calculates a correct result' do
+ create(:issue, created_at: 10.days.ago)
+ create(:issue, created_at: 5.days.ago)
+
+ expect(subject.value).to eq(1)
+ end
+ end
+
context 'with additional parameters passed via options' do
subject do
database_metric_class.tap do |metric_class|
diff --git a/spec/lib/gitlab/usage/metrics/name_suggestion_spec.rb b/spec/lib/gitlab/usage/metrics/name_suggestion_spec.rb
index 24107727a8e..9dba64ff59f 100644
--- a/spec/lib/gitlab/usage/metrics/name_suggestion_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/name_suggestion_spec.rb
@@ -44,7 +44,7 @@ RSpec.describe Gitlab::Usage::Metrics::NameSuggestion do
let(:operation) { :count }
let(:relation) { Issue.with_alert_management_alerts.not_authored_by(::User.alert_bot) }
let(:column) { nil }
- let(:name_suggestion) { /count_<adjective describing\: '\(issues\.author_id != \d+\)'>_issues_<with>_alert_management_alerts/ }
+ let(:name_suggestion) { /count_<adjective describing: '\(issues\.author_id != \d+\)'>_issues_<with>_alert_management_alerts/ }
end
end
end
@@ -55,7 +55,7 @@ RSpec.describe Gitlab::Usage::Metrics::NameSuggestion do
let(:operation) { :distinct_count }
let(:relation) { ::Clusters::Cluster.aws_installed.enabled.where(created_at: 30.days.ago..2.days.ago ) }
let(:column) { :user_id }
- let(:constraints) { /<adjective describing\: '\(clusters.provider_type = \d+ AND \(cluster_providers_aws\.status IN \(\d+\)\) AND clusters\.enabled = TRUE\)'>/ }
+ let(:constraints) { /<adjective describing: '\(clusters.provider_type = \d+ AND \(cluster_providers_aws\.status IN \(\d+\)\) AND clusters\.enabled = TRUE\)'>/ }
let(:name_suggestion) { /count_distinct_user_id_from_#{constraints}_clusters_<with>_#{constraints}_cluster_providers_aws/ }
end
end
@@ -66,7 +66,7 @@ RSpec.describe Gitlab::Usage::Metrics::NameSuggestion do
let(:operation) { :sum }
let(:relation) { JiraImportState.finished }
let(:column) { :imported_issues_count }
- let(:name_suggestion) { /sum_imported_issues_count_from_<adjective describing\: '\(jira_imports\.status = \d+\)'>_jira_imports/ }
+ let(:name_suggestion) { /sum_imported_issues_count_from_<adjective describing: '\(jira_imports\.status = \d+\)'>_jira_imports/ }
end
end
diff --git a/spec/lib/gitlab/usage/metrics/names_suggestions/generator_spec.rb b/spec/lib/gitlab/usage/metrics/names_suggestions/generator_spec.rb
index 7e8b15d23db..83a4ea8e948 100644
--- a/spec/lib/gitlab/usage/metrics/names_suggestions/generator_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/names_suggestions/generator_spec.rb
@@ -45,7 +45,7 @@ RSpec.describe Gitlab::Usage::Metrics::NamesSuggestions::Generator do
it_behaves_like 'name suggestion' do
# corresponding metric is collected with count(Issue.with_alert_management_alerts.not_authored_by(::User.alert_bot), start: issue_minimum_id, finish: issue_maximum_id)
let(:key_path) { 'counts.issues_created_manually_from_alerts' }
- let(:name_suggestion) { /count_<adjective describing\: '\(issues\.author_id != \d+\)'>_issues_<with>_alert_management_alerts/ }
+ let(:name_suggestion) { /count_<adjective describing: '\(issues\.author_id != \d+\)'>_issues_<with>_alert_management_alerts/ }
end
end
end
@@ -54,7 +54,7 @@ RSpec.describe Gitlab::Usage::Metrics::NamesSuggestions::Generator do
it_behaves_like 'name suggestion' do
# corresponding metric is collected with distinct_count(::Clusters::Cluster.aws_installed.enabled.where(time_period), :user_id)
let(:key_path) { 'usage_activity_by_stage_monthly.configure.clusters_platforms_eks' }
- let(:constraints) { /<adjective describing\: '\(clusters.provider_type = \d+ AND \(cluster_providers_aws\.status IN \(\d+\)\) AND clusters\.enabled = TRUE\)'>/ }
+ let(:constraints) { /<adjective describing: '\(clusters.provider_type = \d+ AND \(cluster_providers_aws\.status IN \(\d+\)\) AND clusters\.enabled = TRUE\)'>/ }
let(:name_suggestion) { /count_distinct_user_id_from_#{constraints}_clusters_<with>_#{constraints}_cluster_providers_aws/ }
end
end
@@ -63,7 +63,7 @@ RSpec.describe Gitlab::Usage::Metrics::NamesSuggestions::Generator do
it_behaves_like 'name suggestion' do
# corresponding metric is collected with sum(JiraImportState.finished, :imported_issues_count)
let(:key_path) { 'counts.jira_imports_total_imported_issues_count' }
- let(:name_suggestion) { /sum_imported_issues_count_from_<adjective describing\: '\(jira_imports\.status = \d+\)'>_jira_imports/ }
+ let(:name_suggestion) { /sum_imported_issues_count_from_<adjective describing: '\(jira_imports\.status = \d+\)'>_jira_imports/ }
end
end
@@ -71,7 +71,7 @@ RSpec.describe Gitlab::Usage::Metrics::NamesSuggestions::Generator do
it_behaves_like 'name suggestion' do
# corresponding metric is collected with add(data[:personal_snippets], data[:project_snippets])
let(:key_path) { 'counts.snippets' }
- let(:name_suggestion) { /add_count_<adjective describing\: '\(snippets\.type = 'PersonalSnippet'\)'>_snippets_and_count_<adjective describing\: '\(snippets\.type = 'ProjectSnippet'\)'>_snippets/ }
+ let(:name_suggestion) { /add_count_<adjective describing: '\(snippets\.type = 'PersonalSnippet'\)'>_snippets_and_count_<adjective describing: '\(snippets\.type = 'ProjectSnippet'\)'>_snippets/ }
end
end
diff --git a/spec/lib/gitlab/usage/metrics/names_suggestions/relation_parsers/joins_spec.rb b/spec/lib/gitlab/usage/metrics/names_suggestions/relation_parsers/joins_spec.rb
index fb3bd564e34..3e72d118ac6 100644
--- a/spec/lib/gitlab/usage/metrics/names_suggestions/relation_parsers/joins_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/names_suggestions/relation_parsers/joins_spec.rb
@@ -4,7 +4,9 @@ require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::NamesSuggestions::RelationParsers::Joins do
describe '#accept' do
- let(:collector) { Arel::Collectors::SubstituteBinds.new(ActiveRecord::Base.connection, Arel::Collectors::SQLString.new) }
+ let(:collector) do
+ Arel::Collectors::SubstituteBinds.new(ApplicationRecord.connection, Arel::Collectors::SQLString.new)
+ end
context 'with join added via string' do
it 'collects join parts' do
@@ -33,7 +35,10 @@ RSpec.describe Gitlab::Usage::Metrics::NamesSuggestions::RelationParsers::Joins
result = described_class.new(ApplicationRecord.connection).accept(arel)
- expect(result).to match_array [{ source: "joins", constraints: "records.id = joins.records_id" }, { source: "second_level_joins", constraints: "joins.id = second_level_joins.joins_id" }]
+ expect(result).to match_array [
+ { source: "joins", constraints: "records.id = joins.records_id" },
+ { source: "second_level_joins", constraints: "joins.id = second_level_joins.joins_id" }
+ ]
end
end
end
diff --git a/spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb b/spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb
index e122d9a3026..63a1da490ed 100644
--- a/spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe 'Code review events' do
definition.attributes.dig(:options, :events)
end.uniq
- exceptions = %w[i_code_review_mr_diffs i_code_review_mr_with_invalid_approvers i_code_review_mr_single_file_diffs i_code_review_total_suggestions_applied i_code_review_total_suggestions_added i_code_review_create_note_in_ipynb_diff i_code_review_create_note_in_ipynb_diff_mr i_code_review_create_note_in_ipynb_diff_commit]
+ exceptions = %w[i_code_review_create_mr i_code_review_mr_diffs i_code_review_mr_with_invalid_approvers i_code_review_mr_single_file_diffs i_code_review_total_suggestions_applied i_code_review_total_suggestions_added i_code_review_create_note_in_ipynb_diff i_code_review_create_note_in_ipynb_diff_mr i_code_review_create_note_in_ipynb_diff_commit]
code_review_aggregated_events += exceptions
expect(code_review_events - code_review_aggregated_events).to be_empty
diff --git a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
index 0bea06f602f..1d980c48c72 100644
--- a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
@@ -23,70 +23,6 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
described_class.clear_memoization(:known_events)
end
- context 'migration to instrumentation classes data collection' do
- let_it_be(:instrumented_events) do
- instrumentation_classes = %w[AggregatedMetric RedisHLLMetric]
- ::Gitlab::Usage::MetricDefinition.all.map do |definition|
- next unless definition.available?
- next unless instrumentation_classes.include?(definition.attributes[:instrumentation_class])
-
- definition.attributes.dig(:options, :events)&.sort
- end.compact.to_set
- end
-
- def not_instrumented_events(category)
- described_class
- .events_for_category(category)
- .sort
- .reject do |event|
- instrumented_events.include?([event])
- end
- end
-
- def not_instrumented_aggregate(category)
- events = described_class.events_for_category(category).sort
-
- return unless described_class::CATEGORIES_FOR_TOTALS.include?(category)
- return unless described_class.send(:eligible_for_totals?, events)
- return if instrumented_events.include?(events)
-
- events
- end
-
- describe 'Gitlab::UsageDataCounters::HLLRedisCounter::CATEGORIES_COLLECTED_FROM_METRICS_DEFINITIONS' do
- it 'includes only fully migrated categories' do
- wrong_skipped_events = described_class::CATEGORIES_COLLECTED_FROM_METRICS_DEFINITIONS.map do |category|
- next if not_instrumented_events(category).empty? && not_instrumented_aggregate(category).nil?
-
- [category, [not_instrumented_events(category), not_instrumented_aggregate(category)].compact]
- end.compact.to_h
-
- expect(wrong_skipped_events).to be_empty
- end
-
- context 'with not instrumented category' do
- let(:instrumented_events) { [] }
-
- it 'can detect not migrated category' do
- wrong_skipped_events = described_class::CATEGORIES_COLLECTED_FROM_METRICS_DEFINITIONS.map do |category|
- next if not_instrumented_events(category).empty? && not_instrumented_aggregate(category).nil?
-
- [category, [not_instrumented_events(category), not_instrumented_aggregate(category)].compact]
- end.compact.to_h
-
- expect(wrong_skipped_events).not_to be_empty
- end
- end
- end
-
- describe '.unique_events_data' do
- it 'does not include instrumented categories' do
- expect(described_class.unique_events_data.keys)
- .not_to include(*described_class.categories_collected_from_metrics_definitions)
- end
- end
- end
-
describe '.categories' do
it 'gets CE unique category names' do
expect(described_class.categories).to include(
@@ -138,14 +74,14 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
File.open(ce_temp_file.path, "w+b") { |f| f.write [ce_event].to_yaml }
end
- it 'returns ce events' do
- expect(described_class.known_events).to include(ce_event)
- end
-
after do
ce_temp_file.unlink
FileUtils.remove_entry(ce_temp_dir) if Dir.exist?(ce_temp_dir)
end
+
+ it 'returns ce events' do
+ expect(described_class.known_events).to include(ce_event)
+ end
end
describe 'known_events' do
@@ -273,6 +209,22 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
expect { described_class.track_event('unknown', values: entity1, time: Date.current) }.to raise_error(Gitlab::UsageDataCounters::HLLRedisCounter::UnknownEvent)
end
+ context 'when Rails environment is production' do
+ before do
+ allow(Rails.env).to receive(:development?).and_return(false)
+ allow(Rails.env).to receive(:test?).and_return(false)
+ end
+
+ it 'reports only UnknownEvent exception' do
+ expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
+ .with(Gitlab::UsageDataCounters::HLLRedisCounter::UnknownEvent)
+ .once
+ .and_call_original
+
+ expect { described_class.track_event('unknown', values: entity1, time: Date.current) }.not_to raise_error
+ end
+ end
+
it 'reports an error if Feature.enabled raise an error' do
expect(Feature).to receive(:enabled?).and_raise(StandardError.new)
expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
@@ -342,7 +294,7 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
context 'with valid contex' do
it 'increments context event counter' do
expect(Gitlab::Redis::HLL).to receive(:add) do |kwargs|
- expect(kwargs[:key]).to match(/^#{default_context}\_.*/)
+ expect(kwargs[:key]).to match(/^#{default_context}_.*/)
end
described_class.track_event_in_context(context_event, values: entity1, context: default_context)
@@ -544,53 +496,6 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
end
end
- describe 'unique_events_data' do
- let(:known_events) do
- [
- { name: 'event1_slot', redis_slot: "slot", category: 'category1', aggregation: "weekly" },
- { name: 'event2_slot', redis_slot: "slot", category: 'category1', aggregation: "weekly" },
- { name: 'event3', category: 'category2', aggregation: "weekly" },
- { name: 'event4', category: 'category2', aggregation: "weekly" }
- ].map(&:with_indifferent_access)
- end
-
- before do
- allow(described_class).to receive(:known_events).and_return(known_events)
- allow(described_class).to receive(:categories).and_return(%w(category1 category2))
-
- stub_const('Gitlab::UsageDataCounters::HLLRedisCounter::CATEGORIES_FOR_TOTALS', %w(category1 category2))
-
- described_class.track_event('event1_slot', values: entity1, time: 2.days.ago)
- described_class.track_event('event2_slot', values: entity2, time: 2.days.ago)
- described_class.track_event('event2_slot', values: entity3, time: 2.weeks.ago)
-
- # events in different slots
- described_class.track_event('event3', values: entity2, time: 2.days.ago)
- described_class.track_event('event4', values: entity2, time: 2.days.ago)
- end
-
- it 'returns the number of unique events for all known events' do
- results = {
- "category1" => {
- "event1_slot_weekly" => 1,
- "event1_slot_monthly" => 1,
- "event2_slot_weekly" => 1,
- "event2_slot_monthly" => 2,
- "category1_total_unique_counts_weekly" => 2,
- "category1_total_unique_counts_monthly" => 3
- },
- "category2" => {
- "event3_weekly" => 1,
- "event3_monthly" => 1,
- "event4_weekly" => 1,
- "event4_monthly" => 1
- }
- }
-
- expect(subject.unique_events_data).to eq(results)
- end
- end
-
describe '.calculate_events_union' do
let(:time_range) { { start_date: 7.days.ago, end_date: DateTime.current } }
let(:known_events) do
diff --git a/spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb
index 74e63d219bd..9a1ffd8d01d 100644
--- a/spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb
@@ -50,11 +50,29 @@ RSpec.describe Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter, :cl
end
describe '.track_create_mr_action' do
- subject { described_class.track_create_mr_action(user: user) }
+ subject { described_class.track_create_mr_action(user: user, merge_request: merge_request) }
+
+ let(:merge_request) { create(:merge_request) }
+ let(:target_project) { merge_request.target_project }
+
+ it_behaves_like 'a tracked merge request unique event' do
+ let(:action) { described_class::MR_USER_CREATE_ACTION }
+ end
it_behaves_like 'a tracked merge request unique event' do
let(:action) { described_class::MR_CREATE_ACTION }
end
+
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:action) { :create }
+ let(:category) { described_class.name }
+ let(:project) { target_project }
+ let(:namespace) { project.namespace.reload }
+ let(:user) { project.creator }
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:label) { 'redis_hll_counters.code_review.i_code_review_create_mr_monthly' }
+ let(:property) { described_class::MR_CREATE_ACTION }
+ end
end
describe '.track_close_mr_action' do
@@ -94,30 +112,15 @@ RSpec.describe Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter, :cl
let(:action) { described_class::MR_APPROVE_ACTION }
end
- it 'records correct payload with Snowplow event', :snowplow do
- stub_feature_flags(route_hll_to_snowplow_phase2: true)
-
- subject
-
- expect_snowplow_event(
- category: 'merge_requests',
- action: 'i_code_review_user_approve_mr',
- namespace: target_project.namespace,
- user: user,
- project: target_project
- )
- end
-
- context 'when FF is disabled' do
- before do
- stub_feature_flags(route_hll_to_snowplow_phase2: false)
- end
-
- it 'doesnt emit snowplow events', :snowplow do
- subject
-
- expect_no_snowplow_event
- end
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:action) { :approve }
+ let(:category) { described_class.name }
+ let(:project) { target_project }
+ let(:namespace) { project.namespace.reload }
+ let(:user) { project.creator }
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:label) { 'redis_hll_counters.code_review.i_code_review_user_approve_mr_monthly' }
+ let(:property) { described_class::MR_APPROVE_ACTION }
end
end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index d8f50fa27bb..214331e15e8 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -598,35 +598,10 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
external_diffs: { enabled: false },
lfs: { enabled: true, object_store: { enabled: false, direct_upload: true, background_upload: false, provider: "AWS" } },
uploads: { enabled: nil, object_store: { enabled: false, direct_upload: true, background_upload: false, provider: "AWS" } },
- packages: { enabled: true, object_store: { enabled: false, direct_upload: false, background_upload: true, provider: "AWS" } } }
+ packages: { enabled: true, object_store: { enabled: false, direct_upload: false, background_upload: false, provider: "AWS" } } }
)
end
- context 'with existing container expiration policies' do
- let_it_be(:disabled) { create(:container_expiration_policy, enabled: false) }
- let_it_be(:enabled) { create(:container_expiration_policy, enabled: true) }
-
- ::ContainerExpirationPolicy.older_than_options.keys.each do |value|
- let_it_be("container_expiration_policy_with_older_than_set_to_#{value}") { create(:container_expiration_policy, older_than: value) }
- end
-
- let_it_be('container_expiration_policy_with_older_than_set_to_null') { create(:container_expiration_policy, older_than: nil) }
-
- let(:inactive_policies) { ::ContainerExpirationPolicy.where(enabled: false) }
- let(:active_policies) { ::ContainerExpirationPolicy.active }
-
- subject { described_class.data[:counts] }
-
- it 'gathers usage data' do
- expect(subject[:projects_with_expiration_policy_enabled_with_older_than_unset]).to eq 1
- expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_7d]).to eq 1
- expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_14d]).to eq 1
- expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_30d]).to eq 1
- expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_60d]).to eq 1
- expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_90d]).to eq 2
- end
- end
-
context 'when queries time out' do
let(:metric_method) { :count }
@@ -860,7 +835,6 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
'direct_upload' => true,
'connection' =>
{ 'provider' => 'AWS', 'aws_access_key_id' => 'minio', 'aws_secret_access_key' => 'gdk-minio', 'region' => 'gdk', 'endpoint' => 'http://127.0.0.1:9000', 'path_style' => true },
- 'background_upload' => false,
'proxy_download' => false } })
expect(subject).to eq(
@@ -1135,36 +1109,6 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
end
end
- describe 'redis_hll_counters' do
- subject { described_class.redis_hll_counters }
-
- let(:migrated_categories) do
- ::Gitlab::UsageDataCounters::HLLRedisCounter.categories_collected_from_metrics_definitions
- end
-
- let(:categories) { ::Gitlab::UsageDataCounters::HLLRedisCounter.categories - migrated_categories }
- let(:ignored_metrics) { ["i_package_composer_deploy_token_weekly"] }
-
- it 'has all known_events' do
- expect(subject).to have_key(:redis_hll_counters)
-
- expect(subject[:redis_hll_counters].keys).to match_array(categories)
-
- categories.each do |category|
- keys = ::Gitlab::UsageDataCounters::HLLRedisCounter.events_for_category(category)
-
- metrics = keys.map { |key| "#{key}_weekly" } + keys.map { |key| "#{key}_monthly" }
- metrics -= ignored_metrics
-
- if ::Gitlab::UsageDataCounters::HLLRedisCounter::CATEGORIES_FOR_TOTALS.include?(category)
- metrics.append("#{category}_total_unique_counts_weekly", "#{category}_total_unique_counts_monthly")
- end
-
- expect(subject[:redis_hll_counters][category].keys).to match_array(metrics)
- end
- end
- end
-
describe '.service_desk_counts' do
subject { described_class.send(:service_desk_counts) }
diff --git a/spec/lib/gitlab/utils/delegator_override/validator_spec.rb b/spec/lib/gitlab/utils/delegator_override/validator_spec.rb
index a58bc65c708..4fcf01ea256 100644
--- a/spec/lib/gitlab/utils/delegator_override/validator_spec.rb
+++ b/spec/lib/gitlab/utils/delegator_override/validator_spec.rb
@@ -7,8 +7,7 @@ RSpec.describe Gitlab::Utils::DelegatorOverride::Validator do
Class.new(::SimpleDelegator) do
extend(::Gitlab::Utils::DelegatorOverride)
- def foo
- end
+ def foo; end
end.prepend(ee_delegator_extension)
end
@@ -16,18 +15,15 @@ RSpec.describe Gitlab::Utils::DelegatorOverride::Validator do
Module.new do
extend(::Gitlab::Utils::DelegatorOverride)
- def bar
- end
+ def bar; end
end
end
let(:target_class) do
Class.new do
- def foo
- end
+ def foo; end
- def bar
- end
+ def bar; end
end
end
diff --git a/spec/lib/gitlab/utils/delegator_override_spec.rb b/spec/lib/gitlab/utils/delegator_override_spec.rb
index 2dafa75e344..b566b7a2cad 100644
--- a/spec/lib/gitlab/utils/delegator_override_spec.rb
+++ b/spec/lib/gitlab/utils/delegator_override_spec.rb
@@ -7,25 +7,21 @@ RSpec.describe Gitlab::Utils::DelegatorOverride do
Class.new(::SimpleDelegator) do
extend(::Gitlab::Utils::DelegatorOverride)
- def foo
- end
+ def foo; end
end
end
let(:target_class) do
Class.new do
- def foo
- end
+ def foo; end
- def bar
- end
+ def bar; end
end
end
let(:dummy_module) do
Module.new do
- def foobar
- end
+ def foobar; end
end
end
diff --git a/spec/lib/gitlab/utils/override_spec.rb b/spec/lib/gitlab/utils/override_spec.rb
index a5e53c1dfc1..63f7b1623d8 100644
--- a/spec/lib/gitlab/utils/override_spec.rb
+++ b/spec/lib/gitlab/utils/override_spec.rb
@@ -35,8 +35,7 @@ RSpec.describe Gitlab::Utils::Override do
override :good
if bad_arity
- def good(num)
- end
+ def good(num); end
elsif negative_arity
def good(*args)
super.succ
diff --git a/spec/lib/gitlab/utils/strong_memoize_spec.rb b/spec/lib/gitlab/utils/strong_memoize_spec.rb
index 236b6d29ba7..287858579d6 100644
--- a/spec/lib/gitlab/utils/strong_memoize_spec.rb
+++ b/spec/lib/gitlab/utils/strong_memoize_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe Gitlab::Utils::StrongMemoize do
end
def method_name
- strong_memoize(:method_name) do
+ strong_memoize(:method_name) do # rubocop: disable Gitlab/StrongMemoizeAttr
trace << value
value
end
@@ -59,22 +59,19 @@ RSpec.describe Gitlab::Utils::StrongMemoize do
protected
- def private_method
- end
+ def private_method; end
private :private_method
strong_memoize_attr :private_method
public
- def protected_method
- end
+ def protected_method; end
protected :protected_method
strong_memoize_attr :protected_method
private
- def public_method
- end
+ def public_method; end
public :public_method
strong_memoize_attr :public_method
end
@@ -219,6 +216,10 @@ RSpec.describe Gitlab::Utils::StrongMemoize do
it 'calls the existing .method_added' do
expect(klass.method_added_list).to include(:method_name_attr)
end
+
+ it 'retains method arity' do
+ expect(klass.instance_method(member_name).arity).to eq(0)
+ end
end
context "memoized before method definition with different member name and value #{value}" do
@@ -280,5 +281,22 @@ RSpec.describe Gitlab::Utils::StrongMemoize do
expect { subject }.to raise_error(NameError, %r{undefined method `nonexistent_method' for class})
end
end
+
+ context 'when memoized method has parameters' do
+ it 'raises an error' do
+ expected_message = /Using `strong_memoize_attr` on methods with parameters is not supported/
+
+ expect do
+ strong_memoize_class = described_class
+
+ Class.new do
+ include strong_memoize_class
+
+ def method_with_parameters(params); end
+ strong_memoize_attr :method_with_parameters
+ end
+ end.to raise_error(RuntimeError, expected_message)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/work_items/work_item_hierarchy_spec.rb b/spec/lib/gitlab/work_items/work_item_hierarchy_spec.rb
new file mode 100644
index 00000000000..b2f298a7d05
--- /dev/null
+++ b/spec/lib/gitlab/work_items/work_item_hierarchy_spec.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::WorkItems::WorkItemHierarchy, feature_category: :portfolio_management do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:type1) { create(:work_item_type, namespace: project.namespace) }
+ let_it_be(:type2) { create(:work_item_type, namespace: project.namespace) }
+ let_it_be(:hierarchy_restriction1) { create(:hierarchy_restriction, parent_type: type1, child_type: type2) }
+ let_it_be(:hierarchy_restriction2) { create(:hierarchy_restriction, parent_type: type2, child_type: type2) }
+ let_it_be(:hierarchy_restriction3) { create(:hierarchy_restriction, parent_type: type2, child_type: type1) }
+ let_it_be(:item1) { create(:work_item, work_item_type: type1, project: project) }
+ let_it_be(:item2) { create(:work_item, work_item_type: type2, project: project) }
+ let_it_be(:item3) { create(:work_item, work_item_type: type2, project: project) }
+ let_it_be(:item4) { create(:work_item, work_item_type: type1, project: project) }
+ let_it_be(:ignored1) { create(:work_item, work_item_type: type1, project: project) }
+ let_it_be(:ignored2) { create(:work_item, work_item_type: type2, project: project) }
+ let_it_be(:link1) { create(:parent_link, work_item_parent: item1, work_item: item2) }
+ let_it_be(:link2) { create(:parent_link, work_item_parent: item2, work_item: item3) }
+ let_it_be(:link3) { create(:parent_link, work_item_parent: item3, work_item: item4) }
+
+ let(:options) { {} }
+
+ describe '#base_and_ancestors' do
+ subject { described_class.new(::WorkItem.where(id: item3.id), options: options) }
+
+ it 'includes the base and its ancestors' do
+ relation = subject.base_and_ancestors
+
+ expect(relation).to eq([item3, item2, item1])
+ end
+
+ context 'when same_type option is used' do
+ let(:options) { { same_type: true } }
+
+ it 'includes the base and its ancestors' do
+ relation = subject.base_and_ancestors
+
+ expect(relation).to eq([item3, item2])
+ end
+ end
+
+ it 'can find ancestors upto a certain level' do
+ relation = subject.base_and_ancestors(upto: item1)
+
+ expect(relation).to eq([item3, item2])
+ end
+
+ describe 'hierarchy_order option' do
+ let(:relation) do
+ subject.base_and_ancestors(hierarchy_order: hierarchy_order)
+ end
+
+ context 'for :asc' do
+ let(:hierarchy_order) { :asc }
+
+ it 'orders by child to ancestor' do
+ expect(relation).to eq([item3, item2, item1])
+ end
+ end
+
+ context 'for :desc' do
+ let(:hierarchy_order) { :desc }
+
+ it 'orders by ancestor to child' do
+ expect(relation).to eq([item1, item2, item3])
+ end
+ end
+ end
+ end
+
+ describe '#base_and_descendants' do
+ subject { described_class.new(::WorkItem.where(id: item2.id), options: options) }
+
+ it 'includes the base and its descendants' do
+ relation = subject.base_and_descendants
+
+ expect(relation).to eq([item2, item3, item4])
+ end
+
+ context 'when same_type option is used' do
+ let(:options) { { same_type: true } }
+
+ it 'includes the base and its ancestors' do
+ relation = subject.base_and_descendants
+
+ expect(relation).to eq([item2, item3])
+ end
+ end
+
+ context 'when with_depth is true' do
+ let(:relation) do
+ subject.base_and_descendants(with_depth: true)
+ end
+
+ it 'includes depth in the results' do
+ object_depths = {
+ item2.id => 1,
+ item3.id => 2,
+ item4.id => 3
+ }
+
+ relation.each do |object|
+ expect(object.depth).to eq(object_depths[object.id])
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index 5c9a3cc0a24..3c7542ea5f9 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Workhorse do
let_it_be(:project) { create(:project, :repository) }
+ let(:features) { { 'gitaly-feature-enforce-requests-limits' => 'true' } }
let(:repository) { project.repository }
@@ -42,7 +43,7 @@ RSpec.describe Gitlab::Workhorse do
expect(command).to eq('git-archive')
expect(params).to eq({
'GitalyServer' => {
- features: { 'gitaly-feature-enforce-requests-limits' => 'true' },
+ 'call_metadata' => features,
address: Gitlab::GitalyClient.address(project.repository_storage),
token: Gitlab::GitalyClient.token(project.repository_storage)
},
@@ -92,7 +93,7 @@ RSpec.describe Gitlab::Workhorse do
expect(command).to eq("git-format-patch")
expect(params).to eq({
'GitalyServer' => {
- features: { 'gitaly-feature-enforce-requests-limits' => 'true' },
+ 'call_metadata' => features,
address: Gitlab::GitalyClient.address(project.repository_storage),
token: Gitlab::GitalyClient.token(project.repository_storage)
},
@@ -155,7 +156,7 @@ RSpec.describe Gitlab::Workhorse do
expect(command).to eq("git-diff")
expect(params).to eq({
'GitalyServer' => {
- features: { 'gitaly-feature-enforce-requests-limits' => 'true' },
+ 'call_metadata' => features,
address: Gitlab::GitalyClient.address(project.repository_storage),
token: Gitlab::GitalyClient.token(project.repository_storage)
},
@@ -208,6 +209,16 @@ RSpec.describe Gitlab::Workhorse do
describe '.git_http_ok' do
let(:user) { create(:user) }
+ let(:gitaly_params) do
+ {
+ GitalyServer: {
+ call_metadata: call_metadata,
+ address: Gitlab::GitalyClient.address('default'),
+ token: Gitlab::GitalyClient.token('default')
+ }
+ }
+ end
+
let(:repo_path) { 'ignored but not allowed to be empty in gitlab-workhorse' }
let(:action) { 'info_refs' }
let(:params) do
@@ -219,6 +230,13 @@ RSpec.describe Gitlab::Workhorse do
}
end
+ let(:call_metadata) do
+ features.merge({
+ 'user_id' => params[:GL_ID],
+ 'username' => params[:GL_USERNAME]
+ })
+ end
+
subject { described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action) }
it { expect(subject).to include(params) }
@@ -238,100 +256,79 @@ RSpec.describe Gitlab::Workhorse do
it { expect(subject).to include(params) }
end
- context 'when Gitaly is enabled' do
- let(:gitaly_params) do
- {
- GitalyServer: {
- features: { 'gitaly-feature-enforce-requests-limits' => 'true' },
- address: Gitlab::GitalyClient.address('default'),
- token: Gitlab::GitalyClient.token('default')
- }
- }
- end
+ it 'includes a Repository param' do
+ repo_param = {
+ storage_name: 'default',
+ relative_path: project.disk_path + '.git',
+ gl_repository: "project-#{project.id}"
+ }
- before do
- allow(Gitlab.config.gitaly).to receive(:enabled).and_return(true)
- end
+ expect(subject[:Repository]).to include(repo_param)
+ end
- it 'includes a Repository param' do
- repo_param = {
- storage_name: 'default',
- relative_path: project.disk_path + '.git',
- gl_repository: "project-#{project.id}"
- }
+ context "when git_upload_pack action is passed" do
+ let(:action) { 'git_upload_pack' }
- expect(subject[:Repository]).to include(repo_param)
- end
+ it { expect(subject).to include(gitaly_params) }
- context "when git_upload_pack action is passed" do
- let(:action) { 'git_upload_pack' }
- let(:feature_flag) { :post_upload_pack }
+ context 'show_all_refs enabled' do
+ subject { described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action, show_all_refs: true) }
- it 'includes Gitaly params in the returned value' do
- allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(feature_flag).and_return(true)
+ it { is_expected.to include(ShowAllRefs: true) }
+ end
- expect(subject).to include(gitaly_params)
+ context 'when a feature flag is set for a single project' do
+ before do
+ stub_feature_flags(gitaly_mep_mep: project)
end
- context 'show_all_refs enabled' do
- subject { described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action, show_all_refs: true) }
+ it 'sets the flag to true for that project' do
+ response = described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action)
- it { is_expected.to include(ShowAllRefs: true) }
+ expect(response.dig(:GitalyServer, :call_metadata)).to include('gitaly-feature-enforce-requests-limits' => 'true',
+ 'gitaly-feature-mep-mep' => 'true')
end
- context 'when a feature flag is set for a single project' do
- before do
- stub_feature_flags(gitaly_mep_mep: project)
- end
+ it 'sets the flag to false for other projects' do
+ other_project = create(:project, :public, :repository)
+ response = described_class.git_http_ok(other_project.repository, Gitlab::GlRepository::PROJECT, user, action)
- it 'sets the flag to true for that project' do
- response = described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action)
-
- expect(response.dig(:GitalyServer, :features)).to eq('gitaly-feature-enforce-requests-limits' => 'true',
- 'gitaly-feature-mep-mep' => 'true')
- end
-
- it 'sets the flag to false for other projects' do
- other_project = create(:project, :public, :repository)
- response = described_class.git_http_ok(other_project.repository, Gitlab::GlRepository::PROJECT, user, action)
-
- expect(response.dig(:GitalyServer, :features)).to eq('gitaly-feature-enforce-requests-limits' => 'true',
- 'gitaly-feature-mep-mep' => 'false')
- end
+ expect(response.dig(:GitalyServer, :call_metadata)).to include('gitaly-feature-enforce-requests-limits' => 'true',
+ 'gitaly-feature-mep-mep' => 'false')
+ end
- it 'sets the flag to false when there is no project' do
- snippet = create(:personal_snippet, :repository)
- response = described_class.git_http_ok(snippet.repository, Gitlab::GlRepository::SNIPPET, user, action)
+ it 'sets the flag to false when there is no project' do
+ snippet = create(:personal_snippet, :repository)
+ response = described_class.git_http_ok(snippet.repository, Gitlab::GlRepository::SNIPPET, user, action)
- expect(response.dig(:GitalyServer, :features)).to eq('gitaly-feature-enforce-requests-limits' => 'true',
- 'gitaly-feature-mep-mep' => 'false')
- end
+ expect(response.dig(:GitalyServer, :call_metadata)).to include('gitaly-feature-enforce-requests-limits' => 'true',
+ 'gitaly-feature-mep-mep' => 'false')
end
end
+ end
- context "when git_receive_pack action is passed" do
- let(:action) { 'git_receive_pack' }
+ context "when git_receive_pack action is passed" do
+ let(:action) { 'git_receive_pack' }
- it { expect(subject).to include(gitaly_params) }
- end
+ it { expect(subject).to include(gitaly_params) }
+ end
- context "when info_refs action is passed" do
- let(:action) { 'info_refs' }
+ context "when info_refs action is passed" do
+ let(:action) { 'info_refs' }
- it { expect(subject).to include(gitaly_params) }
+ it { expect(subject).to include(gitaly_params) }
- context 'show_all_refs enabled' do
- subject { described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action, show_all_refs: true) }
+ context 'show_all_refs enabled' do
+ subject { described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action, show_all_refs: true) }
- it { is_expected.to include(ShowAllRefs: true) }
- end
+ it { is_expected.to include(ShowAllRefs: true) }
end
+ end
- context 'when action passed is not supported by Gitaly' do
- let(:action) { 'download' }
+ context 'when action passed is not supported by Gitaly' do
+ let(:action) { 'download' }
- it { expect { subject }.to raise_exception('Unsupported action: download') }
- end
+ it { expect { subject }.to raise_exception('Unsupported action: download') }
end
context 'when receive_max_input_size has been updated' do
@@ -349,6 +346,23 @@ RSpec.describe Gitlab::Workhorse do
expect(subject[:GitConfigOptions]).to be_empty
end
end
+
+ context 'when remote_ip is available in the application context' do
+ it 'includes a RemoteIP params' do
+ result = {}
+ Gitlab::ApplicationContext.with_context(remote_ip: "1.2.3.4") do
+ result = described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action)
+ end
+ expect(result[:GitalyServer][:call_metadata]['remote_ip']).to eql("1.2.3.4")
+ end
+ end
+
+ context 'when remote_ip is not available in the application context' do
+ it 'does not include RemoteIP params' do
+ result = described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action)
+ expect(result[:GitalyServer][:call_metadata]).not_to have_key('remote_ip')
+ end
+ end
end
describe '.set_key_and_notify' do
@@ -428,7 +442,7 @@ RSpec.describe Gitlab::Workhorse do
expect(command).to eq('git-blob')
expect(params).to eq({
'GitalyServer' => {
- features: { 'gitaly-feature-enforce-requests-limits' => 'true' },
+ 'call_metadata' => features,
address: Gitlab::GitalyClient.address(project.repository_storage),
token: Gitlab::GitalyClient.token(project.repository_storage)
},
@@ -508,7 +522,7 @@ RSpec.describe Gitlab::Workhorse do
expect(command).to eq('git-snapshot')
expect(params).to eq(
'GitalyServer' => {
- 'features' => { 'gitaly-feature-enforce-requests-limits' => 'true' },
+ 'call_metadata' => features,
'address' => Gitlab::GitalyClient.address(project.repository_storage),
'token' => Gitlab::GitalyClient.token(project.repository_storage)
},
diff --git a/spec/lib/gitlab/x509/signature_spec.rb b/spec/lib/gitlab/x509/signature_spec.rb
index 31f66232f38..eb8c0bd0aff 100644
--- a/spec/lib/gitlab/x509/signature_spec.rb
+++ b/spec/lib/gitlab/x509/signature_spec.rb
@@ -11,6 +11,17 @@ RSpec.describe Gitlab::X509::Signature do
}
end
+ it_behaves_like 'signature with type checking', :x509 do
+ subject(:signature) do
+ described_class.new(
+ X509Helpers::User1.signed_commit_signature,
+ X509Helpers::User1.signed_commit_base_data,
+ X509Helpers::User1.certificate_email,
+ X509Helpers::User1.signed_commit_time
+ )
+ end
+ end
+
shared_examples "a verified signature" do
let!(:user) { create(:user, email: X509Helpers::User1.certificate_email) }
@@ -271,21 +282,21 @@ RSpec.describe Gitlab::X509::Signature do
end
end
- describe '#user' do
+ describe '#signed_by_user' do
subject do
described_class.new(
X509Helpers::User1.signed_tag_signature,
X509Helpers::User1.signed_tag_base_data,
X509Helpers::User1.certificate_email,
X509Helpers::User1.signed_commit_time
- ).user
+ ).signed_by_user
end
context 'if email is assigned to a user' do
- let!(:user) { create(:user, email: X509Helpers::User1.certificate_email) }
+ let!(:signed_by_user) { create(:user, email: X509Helpers::User1.certificate_email) }
it 'returns user' do
- is_expected.to eq(user)
+ is_expected.to eq(signed_by_user)
end
end
diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb
index 0c207161927..82ab6c089da 100644
--- a/spec/lib/google_api/cloud_platform/client_spec.rb
+++ b/spec/lib/google_api/cloud_platform/client_spec.rb
@@ -106,8 +106,8 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
let(:enable_addons) { [] }
let(:addons_config) do
- enable_addons.each_with_object({}) do |addon, hash|
- hash[addon] = { disabled: false }
+ enable_addons.index_with do
+ { disabled: false }
end
end
diff --git a/spec/lib/json_web_token/hmac_token_spec.rb b/spec/lib/json_web_token/hmac_token_spec.rb
index cf7e5c54f45..016084eaf69 100644
--- a/spec/lib/json_web_token/hmac_token_spec.rb
+++ b/spec/lib/json_web_token/hmac_token_spec.rb
@@ -1,9 +1,11 @@
# frozen_string_literal: true
require 'json'
-require 'timecop'
+require 'active_support/testing/time_helpers'
RSpec.describe JSONWebToken::HMACToken do
+ include ActiveSupport::Testing::TimeHelpers
+
let(:secret) { 'shh secret squirrel' }
shared_examples 'a valid, non-expired token' do
@@ -54,13 +56,13 @@ RSpec.describe JSONWebToken::HMACToken do
end
context 'that is expired' do
- # Needs the ! so Timecop.freeze() is effective
+ # Needs the ! so freeze_time() is effective
let!(:encoded_token) { described_class.new(secret).encoded }
it "raises exception saying 'Signature has expired'" do
# Needs to be 120 seconds, because the default expiry is 60 seconds
# with an additional 60 second leeway.
- Timecop.freeze(Time.now + 120) do
+ travel_to(Time.now + 120) do
expect { decoded_token }.to raise_error(JWT::ExpiredSignature, 'Signature has expired')
end
end
@@ -77,19 +79,19 @@ RSpec.describe JSONWebToken::HMACToken do
context 'that has expired' do
let(:expire_time) { 0 }
+ around do |example|
+ travel_to(Time.now + 1) { example.run }
+ end
+
context 'with the default leeway' do
- Timecop.freeze(Time.now + 1) do
- it_behaves_like 'a valid, non-expired token'
- end
+ it_behaves_like 'a valid, non-expired token'
end
context 'with a leeway of 0 seconds' do
let(:leeway) { 0 }
it "raises exception saying 'Signature has expired'" do
- Timecop.freeze(Time.now + 1) do
- expect { decoded_token }.to raise_error(JWT::ExpiredSignature, 'Signature has expired')
- end
+ expect { decoded_token }.to raise_error(JWT::ExpiredSignature, 'Signature has expired')
end
end
end
diff --git a/spec/lib/mattermost/session_spec.rb b/spec/lib/mattermost/session_spec.rb
index d208ef93224..87e2e341777 100644
--- a/spec/lib/mattermost/session_spec.rb
+++ b/spec/lib/mattermost/session_spec.rb
@@ -14,15 +14,15 @@ RSpec.describe Mattermost::Session, type: :request do
subject { described_class.new(user) }
# Needed for doorkeeper to function
+ before do
+ subject.base_uri = mattermost_url
+ end
+
it { is_expected.to respond_to(:current_resource_owner) }
it { is_expected.to respond_to(:request) }
it { is_expected.to respond_to(:authorization) }
it { is_expected.to respond_to(:strategy) }
- before do
- subject.base_uri = mattermost_url
- end
-
describe '#with session' do
let(:location) { 'http://location.tld' }
let(:cookie_header) { 'MMOAUTH=taskik8az7rq8k6rkpuas7htia; Path=/;' }
diff --git a/spec/lib/pager_duty/webhook_payload_parser_spec.rb b/spec/lib/pager_duty/webhook_payload_parser_spec.rb
index 647f19e3d3a..1606d03c746 100644
--- a/spec/lib/pager_duty/webhook_payload_parser_spec.rb
+++ b/spec/lib/pager_duty/webhook_payload_parser_spec.rb
@@ -10,23 +10,27 @@ RSpec.describe PagerDuty::WebhookPayloadParser do
let(:triggered_event) do
{
- 'event' => 'incident.trigger',
+ 'event' => 'incident.triggered',
'incident' => {
- 'url' => 'https://webdemo.pagerduty.com/incidents/PRORDTY',
- 'incident_number' => 33,
- 'title' => 'My new incident',
+ 'url' => 'https://gitlab-1.pagerduty.com/incidents/Q1XZUF87W1HB5A',
+ 'incident_number' => 2,
+ 'title' => '[FILTERED]',
'status' => 'triggered',
- 'created_at' => '2017-09-26T15:14:36Z',
+ 'created_at' => '2022-11-30T08:46:19Z',
'urgency' => 'high',
- 'incident_key' => nil,
- 'assignees' => [{
- 'summary' => 'Laura Haley',
- 'url' => 'https://webdemo.pagerduty.com/users/P553OPV'
- }],
- 'impacted_services' => [{
- 'summary' => 'Production XDB Cluster',
- 'url' => 'https://webdemo.pagerduty.com/services/PN49J75'
- }]
+ 'incident_key' => '[FILTERED]',
+ 'assignees' =>
+ [
+ {
+ 'summary' => 'Rajendra Kadam',
+ 'url' => 'https://gitlab-1.pagerduty.com/users/PIN0B5C'
+ }
+ ],
+ 'impacted_service' =>
+ {
+ 'summary' => 'Test service',
+ 'url' => 'https://gitlab-1.pagerduty.com/services/PK6IKMT'
+ }
}
}
end
@@ -37,74 +41,50 @@ RSpec.describe PagerDuty::WebhookPayloadParser do
let(:payload) { Gitlab::Json.parse(fixture_file) }
it 'returns parsed payload' do
- is_expected.to eq([triggered_event])
+ is_expected.to eq(triggered_event)
end
context 'when assignments summary and html_url are blank' do
before do
- payload['messages'].each do |m|
- m['incident']['assignments'] = [{ 'assignee' => { 'summary' => '', 'html_url' => '' } }]
- end
+ payload['event']['data']['assignees'] = [{ 'summary' => '', 'html_url' => '' }]
end
it 'returns parsed payload with blank assignees' do
- assignees = parse.map { |events| events['incident'].slice('assignees') }
+ assignees = parse['incident'].slice('assignees')
- expect(assignees).to eq([{ 'assignees' => [] }])
+ expect(assignees).to eq({ 'assignees' => [] })
end
end
context 'when impacted_services summary and html_url are blank' do
before do
- payload['messages'].each do |m|
- m['incident']['impacted_services'] = [{ 'summary' => '', 'html_url' => '' }]
- end
+ payload['event']['data']['service'] = { 'summary' => '', 'html_url' => '' }
end
- it 'returns parsed payload with blank assignees' do
- assignees = parse.map { |events| events['incident'].slice('impacted_services') }
+ it 'returns parsed payload with blank impacted service' do
+ assignees = parse['incident'].slice('impacted_service')
- expect(assignees).to eq([{ 'impacted_services' => [] }])
+ expect(assignees).to eq({ 'impacted_service' => {} })
end
end
end
context 'when payload schema is invalid' do
- let(:payload) { { 'messages' => [{ 'event' => 'incident.trigger' }] } }
+ let(:payload) { { 'event' => 'incident.triggered' } }
- it 'returns payload with blank incidents' do
- is_expected.to eq([])
+ it 'returns payload with blank incident' do
+ is_expected.to eq({})
end
end
- context 'when payload consists of two messages' do
- context 'when one of the messages has no incident data' do
- let(:payload) do
- valid_payload = Gitlab::Json.parse(fixture_file)
- event = { 'event' => 'incident.trigger' }
- valid_payload['messages'] = valid_payload['messages'].append(event)
- valid_payload
- end
-
- it 'returns parsed payload with valid events only' do
- is_expected.to eq([triggered_event])
- end
+ context 'when event is unknown' do
+ let(:payload) do
+ valid_payload = Gitlab::Json.parse(fixture_file)
+ valid_payload['event'] = 'incident.unknown'
end
- context 'when one of the messages has unknown event' do
- let(:payload) do
- valid_payload = Gitlab::Json.parse(fixture_file)
- event = { 'event' => 'incident.unknown', 'incident' => valid_payload['messages'].first['incident'] }
- valid_payload['messages'] = valid_payload['messages'].append(event)
- valid_payload
- end
-
- it 'returns parsed payload' do
- unknown_event = triggered_event.dup
- unknown_event['event'] = 'incident.unknown'
-
- is_expected.to contain_exactly(triggered_event, unknown_event)
- end
+ it 'returns empty payload' do
+ is_expected.to be_empty
end
end
end
diff --git a/spec/lib/peek/views/active_record_spec.rb b/spec/lib/peek/views/active_record_spec.rb
index 7bc15f40065..fc768bdcb82 100644
--- a/spec/lib/peek/views/active_record_spec.rb
+++ b/spec/lib/peek/views/active_record_spec.rb
@@ -61,7 +61,7 @@ RSpec.describe Peek::Views::ActiveRecord, :request_store do
end
it 'includes db role data and db_config_name name' do
- Timecop.freeze(2021, 2, 23, 10, 0) do
+ travel_to(Time.utc(2021, 2, 23, 10, 0)) do
ActiveSupport::Notifications.publish('sql.active_record', Time.current, Time.current + 1.second, '1', event_1)
ActiveSupport::Notifications.publish('sql.active_record', Time.current, Time.current + 2.seconds, '2', event_2)
ActiveSupport::Notifications.publish('sql.active_record', Time.current, Time.current + 3.seconds, '3', event_3)
diff --git a/spec/lib/sbom/package_url/argument_validator_spec.rb b/spec/lib/sbom/package_url/argument_validator_spec.rb
index 246da1c0bda..56dc1d54ba9 100644
--- a/spec/lib/sbom/package_url/argument_validator_spec.rb
+++ b/spec/lib/sbom/package_url/argument_validator_spec.rb
@@ -5,7 +5,7 @@ require 'rspec-parameterized'
require_relative '../../../support/shared_contexts/lib/sbom/package_url_shared_contexts'
-RSpec.describe Sbom::PackageUrl::ArgumentValidator do
+RSpec.describe Sbom::PackageUrl::ArgumentValidator, feature_category: :dependency_management do
let(:mock_package_url) { Struct.new(:type, :namespace, :name, :version, :qualifiers, keyword_init: true) }
let(:package) do
mock_package_url.new(
diff --git a/spec/lib/sbom/package_url/decoder_spec.rb b/spec/lib/sbom/package_url/decoder_spec.rb
index 5b480475b7c..3c092b35ea2 100644
--- a/spec/lib/sbom/package_url/decoder_spec.rb
+++ b/spec/lib/sbom/package_url/decoder_spec.rb
@@ -5,7 +5,7 @@ require 'rspec-parameterized'
require_relative '../../../support/shared_contexts/lib/sbom/package_url_shared_contexts'
-RSpec.describe Sbom::PackageUrl::Decoder do
+RSpec.describe Sbom::PackageUrl::Decoder, feature_category: :dependency_management do
describe '#decode' do
subject(:decode) { described_class.new(purl).decode! }
diff --git a/spec/lib/sbom/package_url/encoder_spec.rb b/spec/lib/sbom/package_url/encoder_spec.rb
index bdbd61636b5..a0b51007008 100644
--- a/spec/lib/sbom/package_url/encoder_spec.rb
+++ b/spec/lib/sbom/package_url/encoder_spec.rb
@@ -5,7 +5,7 @@ require 'rspec-parameterized'
require_relative '../../../support/shared_contexts/lib/sbom/package_url_shared_contexts'
-RSpec.describe Sbom::PackageUrl::Encoder do
+RSpec.describe Sbom::PackageUrl::Encoder, feature_category: :dependency_management do
describe '#encode' do
let(:package) do
::Sbom::PackageUrl.new(
diff --git a/spec/lib/sbom/package_url/normalizer_spec.rb b/spec/lib/sbom/package_url/normalizer_spec.rb
index bbc2bd3ca13..3ad548a5c84 100644
--- a/spec/lib/sbom/package_url/normalizer_spec.rb
+++ b/spec/lib/sbom/package_url/normalizer_spec.rb
@@ -5,7 +5,7 @@ require 'rspec-parameterized'
require_relative '../../../support/shared_contexts/lib/sbom/package_url_shared_contexts'
-RSpec.describe Sbom::PackageUrl::Normalizer do
+RSpec.describe Sbom::PackageUrl::Normalizer, feature_category: :dependency_management do
shared_examples 'name normalization' do
context 'with bitbucket url' do
let(:type) { 'bitbucket' }
diff --git a/spec/lib/sbom/package_url_spec.rb b/spec/lib/sbom/package_url_spec.rb
index 6760b0a68e5..92490b184df 100644
--- a/spec/lib/sbom/package_url_spec.rb
+++ b/spec/lib/sbom/package_url_spec.rb
@@ -29,7 +29,7 @@ require 'rspec-parameterized'
require_relative '../../support/helpers/next_instance_of'
require_relative '../../support/shared_contexts/lib/sbom/package_url_shared_contexts'
-RSpec.describe Sbom::PackageUrl do
+RSpec.describe Sbom::PackageUrl, feature_category: :dependency_management do
include NextInstanceOf
describe '#initialize' do
diff --git a/spec/lib/security/ci_configuration/container_scanning_build_action_spec.rb b/spec/lib/security/ci_configuration/container_scanning_build_action_spec.rb
index 38066e41c53..5b1db66beb0 100644
--- a/spec/lib/security/ci_configuration/container_scanning_build_action_spec.rb
+++ b/spec/lib/security/ci_configuration/container_scanning_build_action_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe Security::CiConfiguration::ContainerScanningBuildAction do
RANDOM: make sure this persists
include:
- template: existing.yml
- - template: Security/Container-Scanning.gitlab-ci.yml
+ - template: Jobs/Container-Scanning.gitlab-ci.yml
CI_YML
end
@@ -85,7 +85,7 @@ RSpec.describe Security::CiConfiguration::ContainerScanningBuildAction do
variables:
RANDOM: make sure this persists
include:
- - template: Security/Container-Scanning.gitlab-ci.yml
+ - template: Jobs/Container-Scanning.gitlab-ci.yml
CI_YML
end
@@ -93,7 +93,7 @@ RSpec.describe Security::CiConfiguration::ContainerScanningBuildAction do
let(:gitlab_ci_content) do
{ "stages" => %w(test),
"variables" => { "RANDOM" => "make sure this persists" },
- "include" => [{ "template" => "Security/Container-Scanning.gitlab-ci.yml" }] }
+ "include" => [{ "template" => "Jobs/Container-Scanning.gitlab-ci.yml" }] }
end
it 'generates the correct YML' do
@@ -106,7 +106,7 @@ RSpec.describe Security::CiConfiguration::ContainerScanningBuildAction do
let(:gitlab_ci_content) do
{ "stages" => %w(test),
"variables" => { "RANDOM" => "make sure this persists" },
- "include" => { "template" => "Security/Container-Scanning.gitlab-ci.yml" } }
+ "include" => { "template" => "Jobs/Container-Scanning.gitlab-ci.yml" } }
end
it 'generates the correct YML' do
@@ -138,7 +138,7 @@ RSpec.describe Security::CiConfiguration::ContainerScanningBuildAction do
# DOCKER_USER: ...
# DOCKER_PASSWORD: ...
include:
- - template: Security/Container-Scanning.gitlab-ci.yml
+ - template: Jobs/Container-Scanning.gitlab-ci.yml
CI_YML
end
diff --git a/spec/lib/security/weak_passwords_spec.rb b/spec/lib/security/weak_passwords_spec.rb
index 9d12c352abf..afa9448e746 100644
--- a/spec/lib/security/weak_passwords_spec.rb
+++ b/spec/lib/security/weak_passwords_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Security::WeakPasswords do
+RSpec.describe Security::WeakPasswords, feature_category: :authentication_and_authorization do
describe "#weak_for_user?" do
using RSpec::Parameterized::TableSyntax
@@ -34,6 +34,7 @@ RSpec.describe Security::WeakPasswords do
"!@mCwEaKy" | true
"A1B2pass" | true
"A1B2C3jr" | false # jr is too short
+ "3e18a7f60a908e329958396d68131d39e1b66a03ea420725e2a0fce7cb17pass" | false # Password is >= 64 chars
# Predictable username substrings
"56d4ab689a" | true
diff --git a/spec/lib/serializers/json_spec.rb b/spec/lib/serializers/json_spec.rb
deleted file mode 100644
index 96a57cde056..00000000000
--- a/spec/lib/serializers/json_spec.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-
-require 'fast_spec_helper'
-require 'oj'
-
-RSpec.describe Serializers::Json do
- describe '.dump' do
- let(:obj) { { key: "value" } }
-
- subject { described_class.dump(obj) }
-
- it 'returns a hash' do
- is_expected.to eq(obj)
- end
- end
-
- describe '.load' do
- let(:data_string) { '{"key":"value","variables":[{"key":"VAR1","value":"VALUE1"}]}' }
- let(:data_hash) { Gitlab::Json.parse(data_string) }
-
- context 'when loading a hash' do
- subject { described_class.load(data_hash) }
-
- it 'decodes a string' do
- is_expected.to be_a(Hash)
- end
-
- it 'allows to access with symbols' do
- expect(subject[:key]).to eq('value')
- expect(subject[:variables].first[:key]).to eq('VAR1')
- end
-
- it 'allows to access with strings' do
- expect(subject["key"]).to eq('value')
- expect(subject["variables"].first["key"]).to eq('VAR1')
- end
- end
-
- context 'when loading a nil' do
- subject { described_class.load(nil) }
-
- it 'returns nil' do
- is_expected.to be_nil
- end
- end
- end
-end
diff --git a/spec/lib/sidebars/projects/menus/deployments_menu_spec.rb b/spec/lib/sidebars/projects/menus/deployments_menu_spec.rb
index 685ba0c31c7..ce971915174 100644
--- a/spec/lib/sidebars/projects/menus/deployments_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/deployments_menu_spec.rb
@@ -47,44 +47,16 @@ RSpec.describe Sidebars::Projects::Menus::DeploymentsMenu do
end
end
- shared_examples 'split_operations_visibility_permissions FF disabled' do
- before do
- stub_feature_flags(split_operations_visibility_permissions: false)
- end
-
- it { is_expected.not_to be_nil }
-
- context 'and the feature is disabled' do
- before do
- project.update_attribute("#{item_id}_access_level", 'disabled')
- end
-
- it { is_expected.not_to be_nil }
- end
-
- context 'and operations is disabled' do
- before do
- project.update_attribute(:operations_access_level, 'disabled')
- end
-
- it do
- is_expected.to be_nil if [:environments, :feature_flags].include?(item_id)
- end
- end
- end
-
describe 'Feature Flags' do
let(:item_id) { :feature_flags }
it_behaves_like 'access rights checks'
- it_behaves_like 'split_operations_visibility_permissions FF disabled'
end
describe 'Environments' do
let(:item_id) { :environments }
it_behaves_like 'access rights checks'
- it_behaves_like 'split_operations_visibility_permissions FF disabled'
end
describe 'Releases' do
diff --git a/spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb b/spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb
index 64408ac3b88..116948b7cb0 100644
--- a/spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb
@@ -31,43 +31,18 @@ RSpec.describe Sidebars::Projects::Menus::InfrastructureMenu do
let(:enabled) { Featurable::PRIVATE }
let(:disabled) { Featurable::DISABLED }
- where(:operations_access_level, :infrastructure_access_level, :render) do
- ref(:disabled) | ref(:enabled) | true
- ref(:disabled) | ref(:disabled) | false
- ref(:enabled) | ref(:enabled) | true
- ref(:enabled) | ref(:disabled) | false
+ where(:infrastructure_access_level, :render) do
+ ref(:enabled) | true
+ ref(:disabled) | false
end
with_them do
it 'renders based on the infrastructure access level' do
- project.project_feature.update!(operations_access_level: operations_access_level)
project.project_feature.update!(infrastructure_access_level: infrastructure_access_level)
expect(subject.render?).to be render
end
end
-
- context 'when `split_operations_visibility_permissions` feature flag is disabled' do
- before do
- stub_feature_flags(split_operations_visibility_permissions: false)
- end
-
- where(:operations_access_level, :infrastructure_access_level, :render) do
- ref(:disabled) | ref(:enabled) | false
- ref(:disabled) | ref(:disabled) | false
- ref(:enabled) | ref(:enabled) | true
- ref(:enabled) | ref(:disabled) | true
- end
-
- with_them do
- it 'renders based on the operations access level' do
- project.project_feature.update!(operations_access_level: operations_access_level)
- project.project_feature.update!(infrastructure_access_level: infrastructure_access_level)
-
- expect(subject.render?).to be render
- end
- end
- end
end
end
diff --git a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb
index f6a8dd7367d..a1e6ae13e68 100644
--- a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb
@@ -16,40 +16,30 @@ RSpec.describe Sidebars::Projects::Menus::MonitorMenu do
let(:enabled) { Featurable::PRIVATE }
let(:disabled) { Featurable::DISABLED }
- where(:flag_enabled, :operations_access_level, :monitor_level, :render) do
- true | ref(:disabled) | ref(:enabled) | true
- true | ref(:disabled) | ref(:disabled) | false
- true | ref(:enabled) | ref(:enabled) | true
- true | ref(:enabled) | ref(:disabled) | false
- false | ref(:disabled) | ref(:enabled) | false
- false | ref(:disabled) | ref(:disabled) | false
- false | ref(:enabled) | ref(:enabled) | true
- false | ref(:enabled) | ref(:disabled) | true
+ where(:monitor_level, :render) do
+ ref(:enabled) | true
+ ref(:disabled) | false
end
with_them do
it 'renders when expected to' do
- stub_feature_flags(split_operations_visibility_permissions: flag_enabled)
- project.project_feature.update!(operations_access_level: operations_access_level)
project.project_feature.update!(monitor_access_level: monitor_level)
expect(subject.render?).to be render
end
end
- context 'when operation feature is enabled' do
- context 'when menu does not have any renderable menu items' do
- it 'returns false' do
- allow(subject).to receive(:has_renderable_items?).and_return(false)
+ context 'when menu does not have any renderable menu items' do
+ it 'returns false' do
+ allow(subject).to receive(:has_renderable_items?).and_return(false)
- expect(subject.render?).to be false
- end
+ expect(subject.render?).to be false
end
+ end
- context 'when menu has menu items' do
- it 'returns true' do
- expect(subject.render?).to be true
- end
+ context 'when menu has menu items' do
+ it 'returns true' do
+ expect(subject.render?).to be true
end
end
end
diff --git a/spec/lib/sidebars/projects/menus/repository_menu_spec.rb b/spec/lib/sidebars/projects/menus/repository_menu_spec.rb
index f26433306b6..e7aa2b7edca 100644
--- a/spec/lib/sidebars/projects/menus/repository_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/repository_menu_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Sidebars::Projects::Menus::RepositoryMenu do
+RSpec.describe Sidebars::Projects::Menus::RepositoryMenu, feature_category: :source_code_management do
let_it_be(:project) { create(:project, :repository) }
let(:user) { project.first_owner }
@@ -36,12 +36,68 @@ RSpec.describe Sidebars::Projects::Menus::RepositoryMenu do
end
context 'for menu items' do
- subject { described_class.new(context).renderable_items.index { |e| e.item_id == item_id } }
+ shared_examples_for 'repository menu item link for' do |item_id|
+ let(:ref) { 'master' }
+ let(:item_id) { item_id }
+ subject { described_class.new(context).renderable_items.find { |e| e.item_id == item_id }.link }
+
+ using RSpec::Parameterized::TableSyntax
+
+ let(:context) do
+ Sidebars::Projects::Context.new(current_user: user, container: project, current_ref: ref,
+ ref_type: ref_type)
+ end
+
+ where(:feature_flag_enabled, :ref_type, :link) do
+ true | nil | lazy { "#{route}?ref_type=heads" }
+ true | 'heads' | lazy { "#{route}?ref_type=heads" }
+ true | 'tags' | lazy { "#{route}?ref_type=tags" }
+ false | nil | lazy { route }
+ false | 'heads' | lazy { route }
+ false | 'tags' | lazy { route }
+ end
+
+ with_them do
+ before do
+ stub_feature_flags(use_ref_type_parameter: feature_flag_enabled)
+ end
+
+ it 'has a link with the fully qualifed ref route' do
+ expect(subject).to eq(link)
+ end
+ end
+
+ context 'when ref is not the default' do
+ let(:ref) { 'nonmain' }
+
+ context 'and ref_type is not provided' do
+ let(:ref_type) { nil }
+
+ it { is_expected.to eq(route) }
+ end
+
+ context 'and ref_type is provided' do
+ let(:ref_type) { 'heads' }
+
+ it { is_expected.to eq("#{route}?ref_type=heads") }
+ end
+ end
+ end
+
+ describe 'Commits' do
+ let_it_be(:item_id) { :commits }
+
+ it_behaves_like 'repository menu item link for', :commits do
+ let(:route) { "/#{project.full_path}/-/commits/#{ref}" }
+ end
+ end
describe 'Contributors' do
let_it_be(:item_id) { :contributors }
context 'when analytics is disabled' do
+ subject { described_class.new(context).renderable_items.find { |e| e.item_id == item_id } }
+
before do
project.project_feature.update!(analytics_access_level: ProjectFeature::DISABLED)
end
@@ -54,7 +110,15 @@ RSpec.describe Sidebars::Projects::Menus::RepositoryMenu do
project.project_feature.update!(analytics_access_level: ProjectFeature::ENABLED)
end
- it { is_expected.not_to be_nil }
+ it_behaves_like 'repository menu item link for', :contributors do
+ let(:route) { "/#{project.full_path}/-/graphs/#{ref}" }
+ end
+ end
+ end
+
+ describe 'Network' do
+ it_behaves_like 'repository menu item link for', :graphs do
+ let(:route) { "/#{project.full_path}/-/network/#{ref}" }
end
end
end
diff --git a/spec/lib/system_check/app/gitlab_cable_config_exists_check_spec.rb b/spec/lib/system_check/app/gitlab_cable_config_exists_check_spec.rb
new file mode 100644
index 00000000000..8e127bb715c
--- /dev/null
+++ b/spec/lib/system_check/app/gitlab_cable_config_exists_check_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe SystemCheck::App::GitlabCableConfigExistsCheck, feature_category: :redis do
+ subject(:system_check) { described_class.new }
+
+ describe '#check?' do
+ subject { system_check.check? }
+
+ context 'when config/cable.yml exists' do
+ before do
+ allow(File).to receive(:exist?).and_return(true)
+ end
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when config/cable.yml does not exist' do
+ before do
+ allow(File).to receive(:exist?).and_return(false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
+ end
+end
diff --git a/spec/lib/system_check/app/gitlab_resque_config_exists_check_spec.rb b/spec/lib/system_check/app/gitlab_resque_config_exists_check_spec.rb
new file mode 100644
index 00000000000..d2e5dec7460
--- /dev/null
+++ b/spec/lib/system_check/app/gitlab_resque_config_exists_check_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe SystemCheck::App::GitlabResqueConfigExistsCheck, feature_category: :redis do
+ subject(:system_check) { described_class.new }
+
+ describe '#check?' do
+ subject { system_check.check? }
+
+ context 'when config/resque.yml exists' do
+ before do
+ allow(File).to receive(:exist?).and_return(true)
+ end
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when config/resque.yml does not exist' do
+ before do
+ allow(File).to receive(:exist?).and_return(false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
+ end
+end
diff --git a/spec/lib/system_check/base_check_spec.rb b/spec/lib/system_check/base_check_spec.rb
index 241c3b33777..168bda07791 100644
--- a/spec/lib/system_check/base_check_spec.rb
+++ b/spec/lib/system_check/base_check_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe SystemCheck::BaseCheck do
it 'responds to Gitlab::TaskHelpers methods' do
expect(subject).to respond_to :ask_to_continue, :os_name, :prompt, :run_and_match, :run_command,
- :run_command!, :uid_for, :gid_for, :gitlab_user, :gitlab_user?, :warn_user_is_not_gitlab, :all_repos,
+ :run_command!, :uid_for, :gid_for, :gitlab_user, :gitlab_user?, :warn_user_is_not_gitlab,
:repository_storage_paths_args, :user_home, :checkout_or_clone_version, :clone_repo, :checkout_version
end
end
diff --git a/spec/lib/system_check/sidekiq_check_spec.rb b/spec/lib/system_check/sidekiq_check_spec.rb
index c2f61e0e4b7..ff4eece8f7c 100644
--- a/spec/lib/system_check/sidekiq_check_spec.rb
+++ b/spec/lib/system_check/sidekiq_check_spec.rb
@@ -37,45 +37,53 @@ RSpec.describe SystemCheck::SidekiqCheck do
)
end
- it 'succeeds when one cluster process and one or more worker processes are running' do
- stub_ps_output <<~PS
- root 2193947 0.9 0.1 146564 18104 ? Ssl 17:34 0:00 ruby bin/sidekiq-cluster * -P ...
- root 2193955 92.2 3.1 4675972 515516 ? Sl 17:34 0:13 sidekiq 5.2.9 ...
- root 2193956 92.2 3.1 4675972 515516 ? Sl 17:34 0:13 sidekiq 5.2.9 ...
- PS
-
- expect_check_output <<~OUTPUT
- Running? ... yes
- Number of Sidekiq processes (cluster/worker) ... 1/2
- OUTPUT
- end
-
- # TODO: Running without a cluster is deprecated and will be removed in GitLab 14.0
- # https://gitlab.com/gitlab-org/gitlab/-/issues/323225
- context 'when running without a cluster' do
- it 'fails when more than one worker process is running' do
+ context 'when only a worker process is running' do
+ before do
stub_ps_output <<~PS
root 2193955 92.2 3.1 4675972 515516 ? Sl 17:34 0:13 sidekiq 5.2.9 ...
- root 2193956 92.2 3.1 4675972 515516 ? Sl 17:34 0:13 sidekiq 5.2.9 ...
PS
+ end
- expect_check_output include(
- 'Running? ... yes',
- 'Number of Sidekiq processes (cluster/worker) ... 0/2',
- 'Please fix the error above and rerun the checks.'
- )
+ it 'fails with the right message for systemd' do
+ allow(File).to receive(:symlink?).with(described_class::SYSTEMD_UNIT_PATH).and_return(true)
+
+ expect_check_output <<~OUTPUT
+ Running? ... yes
+ Number of Sidekiq processes (cluster/worker) ... 0/1
+ Try fixing it:
+ sudo systemctl restart gitlab-sidekiq.service
+ Please fix the error above and rerun the checks.
+ OUTPUT
end
- it 'succeeds when one worker process is running' do
- stub_ps_output <<~PS
- root 2193955 92.2 3.1 4675972 515516 ? Sl 17:34 0:13 sidekiq 5.2.9 ...
- PS
+ it 'fails with the right message for sysvinit' do
+ allow(File).to receive(:symlink?).with(described_class::SYSTEMD_UNIT_PATH).and_return(false)
+ allow(subject).to receive(:gitlab_user).and_return('git')
expect_check_output <<~OUTPUT
Running? ... yes
Number of Sidekiq processes (cluster/worker) ... 0/1
+ Try fixing it:
+ sudo service gitlab stop
+ sudo pkill -u git -f sidekiq
+ sleep 10 && sudo pkill -9 -u git -f sidekiq
+ sudo service gitlab start
+ Please fix the error above and rerun the checks.
OUTPUT
end
end
+
+ it 'succeeds when one cluster process and one or more worker processes are running' do
+ stub_ps_output <<~PS
+ root 2193947 0.9 0.1 146564 18104 ? Ssl 17:34 0:00 ruby bin/sidekiq-cluster * -P ...
+ root 2193955 92.2 3.1 4675972 515516 ? Sl 17:34 0:13 sidekiq 5.2.9 ...
+ root 2193956 92.2 3.1 4675972 515516 ? Sl 17:34 0:13 sidekiq 5.2.9 ...
+ PS
+
+ expect_check_output <<~OUTPUT
+ Running? ... yes
+ Number of Sidekiq processes (cluster/worker) ... 1/2
+ OUTPUT
+ end
end
end
diff --git a/spec/lib/version_check_spec.rb b/spec/lib/version_check_spec.rb
index 1803dd66ba7..4aa8975b7cf 100644
--- a/spec/lib/version_check_spec.rb
+++ b/spec/lib/version_check_spec.rb
@@ -2,7 +2,9 @@
require 'spec_helper'
-RSpec.describe VersionCheck do
+RSpec.describe VersionCheck, :use_clean_rails_memory_store_caching do
+ include ReactiveCachingHelpers
+
describe '.url' do
it 'returns the correct URL' do
expect(described_class.url).to match(%r{\A#{Regexp.escape(described_class.host)}/check\.json\?gitlab_info=\w+})
@@ -24,13 +26,25 @@ RSpec.describe VersionCheck do
end
describe '#calculate_reactive_cache' do
- context 'response code is 200' do
+ context 'response code is 200 with valid body' do
before do
stub_request(:get, described_class.url).to_return(status: 200, body: '{ "status": "success" }', headers: {})
end
it 'returns the response object' do
- expect(described_class.new.calculate_reactive_cache).to eq("{ \"status\": \"success\" }")
+ expect(described_class.new.calculate_reactive_cache).to eq({ "status" => "success" })
+ end
+ end
+
+ context 'response code is 200 with invalid body' do
+ before do
+ stub_request(:get, described_class.url).to_return(status: 200, body: '{ "invalid: json" }', headers: {})
+ end
+
+ it 'returns an error hash' do
+ expect(described_class.new.calculate_reactive_cache).to eq(
+ { error: 'parsing version check response failed', status: 200 }
+ )
end
end
@@ -39,38 +53,61 @@ RSpec.describe VersionCheck do
stub_request(:get, described_class.url).to_return(status: 500, body: nil, headers: {})
end
- it 'returns nil' do
- expect(described_class.new.calculate_reactive_cache).to be(nil)
+ it 'returns an error hash' do
+ expect(described_class.new.calculate_reactive_cache).to eq({ error: 'version check failed', status: 500 })
end
end
end
describe '#response' do
- context 'cache returns value' do
- let(:response) { { "severity" => "success" }.to_json }
-
+ # see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106254
+ context "with old string value in cache" do
before do
- allow_next_instance_of(described_class) do |instance|
- allow(instance).to receive(:with_reactive_cache).and_return(response)
- end
+ old_version_check = described_class.new
+ allow(old_version_check).to receive(:id).and_return(Gitlab::VERSION)
+ write_reactive_cache(old_version_check,
+ "{\"latest_stable_versions\":[],\"latest_version\":\"15.6.2\",\"severity\":\"success\",\"details\":\"\"}"
+ )
end
- it 'returns the response object' do
- expect(described_class.new.response).to be(response)
+ it 'returns nil' do
+ version_check = described_class.new
+ expect(version_check.response).to be_nil
end
end
- context 'cache returns nil' do
- let(:response) { nil }
+ # see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106254
+ context "with non-hash value in cache" do
+ it 'returns nil and invalidates the reactive cache' do
+ version_check = described_class.new
+ stub_reactive_cache(version_check,
+ "{\"latest_stable_versions\":[],\"latest_version\":\"15.6.2\",\"severity\":\"success\",\"details\":\"\"}"
+ )
- before do
- allow_next_instance_of(described_class) do |instance|
- allow(instance).to receive(:with_reactive_cache).and_return(response)
- end
+ expect(version_check).to receive(:refresh_reactive_cache!).and_call_original
+ expect(version_check.response).to be_nil
+ expect(read_reactive_cache(version_check)).to be_nil
end
+ end
- it 'returns nil' do
- expect(described_class.new.response).to be(nil)
+ context 'cache returns value' do
+ it 'returns the response object' do
+ version_check = described_class.new
+ data = { status: 'success' }
+ stub_reactive_cache(version_check, data)
+
+ expect(version_check.response).to eq(data)
+ end
+ end
+
+ context 'cache returns error' do
+ it 'returns nil and invalidates the reactive cache' do
+ version_check = described_class.new
+ stub_reactive_cache(version_check, error: 'version check failed')
+
+ expect(version_check).to receive(:refresh_reactive_cache!).and_call_original
+ expect(version_check.response).to be_nil
+ expect(read_reactive_cache(version_check)).to be_nil
end
end
end
diff --git a/spec/mailers/emails/profile_spec.rb b/spec/mailers/emails/profile_spec.rb
index 767eddb7f98..cdc298d685e 100644
--- a/spec/mailers/emails/profile_spec.rb
+++ b/spec/mailers/emails/profile_spec.rb
@@ -152,7 +152,7 @@ RSpec.describe Emails::Profile do
end
it 'includes the email reason' do
- is_expected.to have_body_text %r{You're receiving this email because of your account on <a .*>localhost<\/a>}
+ is_expected.to have_body_text %r{You're receiving this email because of your account on <a .*>localhost</a>}
end
end
end
@@ -188,7 +188,7 @@ RSpec.describe Emails::Profile do
end
it 'includes the email reason' do
- is_expected.to have_body_text %r{You're receiving this email because of your account on <a .*>localhost<\/a>}
+ is_expected.to have_body_text %r{You're receiving this email because of your account on <a .*>localhost</a>}
end
context 'with User does not exist' do
@@ -223,7 +223,7 @@ RSpec.describe Emails::Profile do
end
it 'includes the email reason' do
- is_expected.to have_body_text %r{You're receiving this email because of your account on <a .*>localhost<\/a>}
+ is_expected.to have_body_text %r{You're receiving this email because of your account on <a .*>localhost</a>}
end
end
@@ -269,8 +269,40 @@ RSpec.describe Emails::Profile do
is_expected.to have_body_text /#{token.name}/
end
+ it 'wont include the revocation reason' do
+ is_expected.not_to have_body_text %r{We found your token in a public project and have automatically revoked it to protect your account.$}
+ end
+
+ it 'includes the email reason' do
+ is_expected.to have_body_text %r{You're receiving this email because of your account on <a .*>localhost</a>}
+ end
+ end
+
+ context 'when source is provided' do
+ subject { Notify.access_token_revoked_email(user, token.name, 'secret_detection') }
+
+ it_behaves_like 'an email sent from GitLab'
+ it_behaves_like 'it should not have Gmail Actions links'
+ it_behaves_like 'a user cannot unsubscribe through footer link'
+
+ it 'is sent to the user' do
+ is_expected.to deliver_to user.email
+ end
+
+ it 'has the correct subject' do
+ is_expected.to have_subject /^A personal access token has been revoked$/i
+ end
+
+ it 'provides the names of the token' do
+ is_expected.to have_body_text /#{token.name}/
+ end
+
+ it 'includes the revocation reason' do
+ is_expected.to have_body_text %r{We found your token in a public project and have automatically revoked it to protect your account.$}
+ end
+
it 'includes the email reason' do
- is_expected.to have_body_text %r{You're receiving this email because of your account on <a .*>localhost<\/a>}
+ is_expected.to have_body_text %r{You're receiving this email because of your account on <a .*>localhost</a>}
end
end
end
@@ -296,7 +328,7 @@ RSpec.describe Emails::Profile do
end
shared_examples 'includes the email reason' do
- it { is_expected.to have_body_text %r{You're receiving this email because of your account on <a .*>localhost<\/a>} }
+ it { is_expected.to have_body_text %r{You're receiving this email because of your account on <a .*>localhost</a>} }
end
shared_examples 'valid use case' do
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 5733e892d2a..684fe9bb9cf 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -419,15 +419,15 @@ RSpec.describe Notify do
end
it 'includes the reason in the footer' do
- text = EmailsHelper.instance_method(:notification_reason_text).bind(self).call(reason: NotificationReason::ASSIGNED, format: :html)
+ text = EmailsHelper.instance_method(:notification_reason_text).bind_call(self, reason: NotificationReason::ASSIGNED, format: :html)
is_expected.to have_body_text(text)
new_subject = described_class.reassigned_merge_request_email(recipient.id, merge_request.id, [previous_assignee.id], current_user.id, NotificationReason::MENTIONED)
- text = EmailsHelper.instance_method(:notification_reason_text).bind(self).call(reason: NotificationReason::MENTIONED, format: :html)
+ text = EmailsHelper.instance_method(:notification_reason_text).bind_call(self, reason: NotificationReason::MENTIONED, format: :html)
expect(new_subject).to have_body_text(text)
new_subject = described_class.reassigned_merge_request_email(recipient.id, merge_request.id, [previous_assignee.id], current_user.id, nil)
- text = EmailsHelper.instance_method(:notification_reason_text).bind(self).call(format: :html)
+ text = EmailsHelper.instance_method(:notification_reason_text).bind_call(self, format: :html)
expect(new_subject).to have_body_text(text)
end
end
@@ -649,16 +649,20 @@ RSpec.describe Notify do
end
context 'the model has no namespace' do
- class TopLevelThing
- include Referable
- include Noteable
+ before do
+ stub_const('TopLevelThing', Class.new)
- def to_reference(*_args)
- 'tlt-ref'
- end
+ TopLevelThing.class_eval do
+ include Referable
+ include Noteable
- def id
- 'tlt-id'
+ def to_reference(*_args)
+ 'tlt-ref'
+ end
+
+ def id
+ 'tlt-id'
+ end
end
end
@@ -672,8 +676,10 @@ RSpec.describe Notify do
end
context 'the model has a namespace' do
- module Namespaced
- class Thing
+ before do
+ stub_const('Namespaced::Thing', Class.new)
+
+ Namespaced::Thing.class_eval do
include Referable
include Noteable
diff --git a/spec/metrics_server/metrics_server_spec.rb b/spec/metrics_server/metrics_server_spec.rb
index c7716184d48..58577d4d633 100644
--- a/spec/metrics_server/metrics_server_spec.rb
+++ b/spec/metrics_server/metrics_server_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_relative '../../metrics_server/metrics_server'
-RSpec.describe MetricsServer do # rubocop:disable RSpec/FilePath
+RSpec.describe MetricsServer, feature_category: :application_performance do # rubocop:disable RSpec/FilePath
let(:prometheus_config) { ::Prometheus::Client.configuration }
let(:metrics_dir) { Dir.mktmpdir }
@@ -118,6 +118,7 @@ RSpec.describe MetricsServer do # rubocop:disable RSpec/FilePath
let(:expected_port) { target == 'puma' ? '8083' : '8082' }
let(:expected_env) do
{
+ 'GOGC' => '10',
'GME_MMAP_METRICS_DIR' => metrics_dir,
'GME_PROBES' => 'self,mmap',
'GME_SERVER_HOST' => 'localhost',
diff --git a/spec/migrations/20210406144743_backfill_total_tuple_count_for_batched_migrations_spec.rb b/spec/migrations/20210406144743_backfill_total_tuple_count_for_batched_migrations_spec.rb
index 1f18f7e581a..18aa8e92560 100644
--- a/spec/migrations/20210406144743_backfill_total_tuple_count_for_batched_migrations_spec.rb
+++ b/spec/migrations/20210406144743_backfill_total_tuple_count_for_batched_migrations_spec.rb
@@ -3,12 +3,13 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillTotalTupleCountForBatchedMigrations, :migration, schema: 20210406140057 do
- let_it_be(:table_name) { 'projects' }
+RSpec.describe BackfillTotalTupleCountForBatchedMigrations, :migration, schema: 20210406140057,
+ feature_category: :database do
+ let!(:table_name) { 'projects' }
- let_it_be(:migrations) { table(:batched_background_migrations) }
+ let!(:migrations) { table(:batched_background_migrations) }
- let_it_be(:migration) do
+ let!(:migration) do
migrations.create!(
created_at: Time.now,
updated_at: Time.now,
diff --git a/spec/migrations/20210423160427_schedule_drop_invalid_vulnerabilities_spec.rb b/spec/migrations/20210423160427_schedule_drop_invalid_vulnerabilities_spec.rb
index faf440eb117..258bf7a3e69 100644
--- a/spec/migrations/20210423160427_schedule_drop_invalid_vulnerabilities_spec.rb
+++ b/spec/migrations/20210423160427_schedule_drop_invalid_vulnerabilities_spec.rb
@@ -3,33 +3,33 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleDropInvalidVulnerabilities, :migration do
- let_it_be(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
- let_it_be(:users) { table(:users) }
- let_it_be(:user) { create_user! }
- let_it_be(:project) { table(:projects).create!(id: 123, namespace_id: namespace.id) }
+RSpec.describe ScheduleDropInvalidVulnerabilities, :migration, feature_category: :value_stream_management do
+ let!(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
+ let!(:users) { table(:users) }
+ let!(:user) { create_user! }
+ let!(:project) { table(:projects).create!(id: 123, namespace_id: namespace.id) }
- let_it_be(:scanners) { table(:vulnerability_scanners) }
- let_it_be(:scanner) { scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
- let_it_be(:different_scanner) { scanners.create!(project_id: project.id, external_id: 'test 2', name: 'test scanner 2') }
+ let!(:scanners) { table(:vulnerability_scanners) }
+ let!(:scanner) { scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
+ let!(:different_scanner) { scanners.create!(project_id: project.id, external_id: 'test 2', name: 'test scanner 2') }
- let_it_be(:vulnerabilities) { table(:vulnerabilities) }
- let_it_be(:vulnerability_with_finding) do
+ let!(:vulnerabilities) { table(:vulnerabilities) }
+ let!(:vulnerability_with_finding) do
create_vulnerability!(
project_id: project.id,
author_id: user.id
)
end
- let_it_be(:vulnerability_without_finding) do
+ let!(:vulnerability_without_finding) do
create_vulnerability!(
project_id: project.id,
author_id: user.id
)
end
- let_it_be(:vulnerability_identifiers) { table(:vulnerability_identifiers) }
- let_it_be(:primary_identifier) do
+ let!(:vulnerability_identifiers) { table(:vulnerability_identifiers) }
+ let!(:primary_identifier) do
vulnerability_identifiers.create!(
project_id: project.id,
external_type: 'uuid-v5',
@@ -38,8 +38,8 @@ RSpec.describe ScheduleDropInvalidVulnerabilities, :migration do
name: 'Identifier for UUIDv5')
end
- let_it_be(:vulnerabilities_findings) { table(:vulnerability_occurrences) }
- let_it_be(:finding) do
+ let!(:vulnerabilities_findings) { table(:vulnerability_occurrences) }
+ let!(:finding) do
create_finding!(
vulnerability_id: vulnerability_with_finding.id,
project_id: project.id,
@@ -82,7 +82,7 @@ RSpec.describe ScheduleDropInvalidVulnerabilities, :migration do
vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:,
name: "test", severity: 7, confidence: 7, report_type: 0,
project_fingerprint: '123qweasdzxc', location_fingerprint: 'test',
- metadata_version: 'test', raw_metadata: 'test', uuid: 'test')
+ metadata_version: 'test', raw_metadata: 'test', uuid: SecureRandom.uuid)
vulnerabilities_findings.create!(
vulnerability_id: vulnerability_id,
project_id: project_id,
diff --git a/spec/migrations/20210430134202_copy_adoption_snapshot_namespace_spec.rb b/spec/migrations/20210430134202_copy_adoption_snapshot_namespace_spec.rb
index ed18820ec8d..688fc5eb23a 100644
--- a/spec/migrations/20210430134202_copy_adoption_snapshot_namespace_spec.rb
+++ b/spec/migrations/20210430134202_copy_adoption_snapshot_namespace_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe CopyAdoptionSnapshotNamespace, :migration, schema: 20210430124630 do
+RSpec.describe CopyAdoptionSnapshotNamespace, :migration, schema: 20210430124630, feature_category: :devops_reports do
let(:namespaces_table) { table(:namespaces) }
let(:segments_table) { table(:analytics_devops_adoption_segments) }
let(:snapshots_table) { table(:analytics_devops_adoption_snapshots) }
diff --git a/spec/migrations/20210430135954_copy_adoption_segments_namespace_spec.rb b/spec/migrations/20210430135954_copy_adoption_segments_namespace_spec.rb
index 25dfaa2e314..0fb3029ec6a 100644
--- a/spec/migrations/20210430135954_copy_adoption_segments_namespace_spec.rb
+++ b/spec/migrations/20210430135954_copy_adoption_segments_namespace_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe CopyAdoptionSegmentsNamespace, :migration do
+RSpec.describe CopyAdoptionSegmentsNamespace, :migration, feature_category: :devops_reports do
let(:namespaces_table) { table(:namespaces) }
let(:segments_table) { table(:analytics_devops_adoption_segments) }
diff --git a/spec/migrations/20210503105845_add_project_value_stream_id_to_project_stages_spec.rb b/spec/migrations/20210503105845_add_project_value_stream_id_to_project_stages_spec.rb
index 187b9115ba7..07a90c2d276 100644
--- a/spec/migrations/20210503105845_add_project_value_stream_id_to_project_stages_spec.rb
+++ b/spec/migrations/20210503105845_add_project_value_stream_id_to_project_stages_spec.rb
@@ -4,7 +4,8 @@ require 'spec_helper'
require_migration!
-RSpec.describe AddProjectValueStreamIdToProjectStages, schema: 20210503105022 do
+RSpec.describe AddProjectValueStreamIdToProjectStages, schema: 20210503105022,
+ feature_category: :value_stream_management do
let(:stages) { table(:analytics_cycle_analytics_project_stages) }
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
diff --git a/spec/migrations/20210511142748_schedule_drop_invalid_vulnerabilities2_spec.rb b/spec/migrations/20210511142748_schedule_drop_invalid_vulnerabilities2_spec.rb
index dd557c833f3..b514c92c52d 100644
--- a/spec/migrations/20210511142748_schedule_drop_invalid_vulnerabilities2_spec.rb
+++ b/spec/migrations/20210511142748_schedule_drop_invalid_vulnerabilities2_spec.rb
@@ -3,35 +3,35 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleDropInvalidVulnerabilities2, :migration do
- let_it_be(:background_migration_jobs) { table(:background_migration_jobs) }
+RSpec.describe ScheduleDropInvalidVulnerabilities2, :migration, feature_category: :value_stream_management do
+ let!(:background_migration_jobs) { table(:background_migration_jobs) }
- let_it_be(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
- let_it_be(:users) { table(:users) }
- let_it_be(:user) { create_user! }
- let_it_be(:project) { table(:projects).create!(id: 123, namespace_id: namespace.id) }
+ let!(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
+ let!(:users) { table(:users) }
+ let!(:user) { create_user! }
+ let!(:project) { table(:projects).create!(id: 123, namespace_id: namespace.id) }
- let_it_be(:scanners) { table(:vulnerability_scanners) }
- let_it_be(:scanner) { scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
- let_it_be(:different_scanner) { scanners.create!(project_id: project.id, external_id: 'test 2', name: 'test scanner 2') }
+ let!(:scanners) { table(:vulnerability_scanners) }
+ let!(:scanner) { scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
+ let!(:different_scanner) { scanners.create!(project_id: project.id, external_id: 'test 2', name: 'test scanner 2') }
- let_it_be(:vulnerabilities) { table(:vulnerabilities) }
- let_it_be(:vulnerability_with_finding) do
+ let!(:vulnerabilities) { table(:vulnerabilities) }
+ let!(:vulnerability_with_finding) do
create_vulnerability!(
project_id: project.id,
author_id: user.id
)
end
- let_it_be(:vulnerability_without_finding) do
+ let!(:vulnerability_without_finding) do
create_vulnerability!(
project_id: project.id,
author_id: user.id
)
end
- let_it_be(:vulnerability_identifiers) { table(:vulnerability_identifiers) }
- let_it_be(:primary_identifier) do
+ let!(:vulnerability_identifiers) { table(:vulnerability_identifiers) }
+ let!(:primary_identifier) do
vulnerability_identifiers.create!(
project_id: project.id,
external_type: 'uuid-v5',
@@ -40,8 +40,8 @@ RSpec.describe ScheduleDropInvalidVulnerabilities2, :migration do
name: 'Identifier for UUIDv5')
end
- let_it_be(:vulnerabilities_findings) { table(:vulnerability_occurrences) }
- let_it_be(:finding) do
+ let!(:vulnerabilities_findings) { table(:vulnerability_occurrences) }
+ let!(:finding) do
create_finding!(
vulnerability_id: vulnerability_with_finding.id,
project_id: project.id,
@@ -88,7 +88,7 @@ RSpec.describe ScheduleDropInvalidVulnerabilities2, :migration do
vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:,
name: "test", severity: 7, confidence: 7, report_type: 0,
project_fingerprint: '123qweasdzxc', location_fingerprint: 'test',
- metadata_version: 'test', raw_metadata: 'test', uuid: 'test')
+ metadata_version: 'test', raw_metadata: 'test', uuid: SecureRandom.uuid)
vulnerabilities_findings.create!(
vulnerability_id: vulnerability_id,
project_id: project_id,
diff --git a/spec/migrations/20210514063252_schedule_cleanup_orphaned_lfs_objects_projects_spec.rb b/spec/migrations/20210514063252_schedule_cleanup_orphaned_lfs_objects_projects_spec.rb
index 4ac4af19eb9..8a76f0847e9 100644
--- a/spec/migrations/20210514063252_schedule_cleanup_orphaned_lfs_objects_projects_spec.rb
+++ b/spec/migrations/20210514063252_schedule_cleanup_orphaned_lfs_objects_projects_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleCleanupOrphanedLfsObjectsProjects, schema: 20210511165250 do
+RSpec.describe ScheduleCleanupOrphanedLfsObjectsProjects, schema: 20210511165250, feature_category: :git_lfs do
let(:lfs_objects_projects) { table(:lfs_objects_projects) }
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
diff --git a/spec/migrations/20210601073400_fix_total_stage_in_vsa_spec.rb b/spec/migrations/20210601073400_fix_total_stage_in_vsa_spec.rb
index fa4b747aaed..24a71e48035 100644
--- a/spec/migrations/20210601073400_fix_total_stage_in_vsa_spec.rb
+++ b/spec/migrations/20210601073400_fix_total_stage_in_vsa_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe FixTotalStageInVsa, :migration, schema: 20210518001450 do
+RSpec.describe FixTotalStageInVsa, :migration, schema: 20210518001450, feature_category: :devops_reports do
let(:namespaces) { table(:namespaces) }
let(:group_value_streams) { table(:analytics_cycle_analytics_group_value_streams) }
let(:group_stages) { table(:analytics_cycle_analytics_group_stages) }
diff --git a/spec/migrations/20210601080039_group_protected_environments_add_index_and_constraint_spec.rb b/spec/migrations/20210601080039_group_protected_environments_add_index_and_constraint_spec.rb
index 8d45f571969..592497805de 100644
--- a/spec/migrations/20210601080039_group_protected_environments_add_index_and_constraint_spec.rb
+++ b/spec/migrations/20210601080039_group_protected_environments_add_index_and_constraint_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe GroupProtectedEnvironmentsAddIndexAndConstraint do
+RSpec.describe GroupProtectedEnvironmentsAddIndexAndConstraint, feature_category: :continuous_delivery do
let(:migration) { described_class.new }
let(:protected_environments) { table(:protected_environments) }
let(:group) { table(:namespaces).create!(name: 'group', path: 'group') }
diff --git a/spec/migrations/20210603222333_remove_builds_email_service_from_services_spec.rb b/spec/migrations/20210603222333_remove_builds_email_service_from_services_spec.rb
index 14aa4fe8da7..706e0b14492 100644
--- a/spec/migrations/20210603222333_remove_builds_email_service_from_services_spec.rb
+++ b/spec/migrations/20210603222333_remove_builds_email_service_from_services_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe RemoveBuildsEmailServiceFromServices do
+RSpec.describe RemoveBuildsEmailServiceFromServices, feature_category: :navigation do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:services) { table(:services) }
diff --git a/spec/migrations/20210610153556_delete_legacy_operations_feature_flags_spec.rb b/spec/migrations/20210610153556_delete_legacy_operations_feature_flags_spec.rb
index 17599e75947..300c43b9133 100644
--- a/spec/migrations/20210610153556_delete_legacy_operations_feature_flags_spec.rb
+++ b/spec/migrations/20210610153556_delete_legacy_operations_feature_flags_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe DeleteLegacyOperationsFeatureFlags do
+RSpec.describe DeleteLegacyOperationsFeatureFlags, feature_category: :feature_flags do
let(:namespace) { table(:namespaces).create!(name: 'foo', path: 'bar') }
let(:project) { table(:projects).create!(namespace_id: namespace.id) }
let(:issue) { table(:issues).create!(id: 123, project_id: project.id) }
diff --git a/spec/migrations/2021061716138_cascade_delete_freeze_periods_spec.rb b/spec/migrations/2021061716138_cascade_delete_freeze_periods_spec.rb
index 8dfeacc4774..baa5fd7efbd 100644
--- a/spec/migrations/2021061716138_cascade_delete_freeze_periods_spec.rb
+++ b/spec/migrations/2021061716138_cascade_delete_freeze_periods_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe CascadeDeleteFreezePeriods, :suppress_gitlab_schemas_validate_connection do
+RSpec.describe CascadeDeleteFreezePeriods, :suppress_gitlab_schemas_validate_connection, feature_category: :continuous_delivery do
let(:namespace) { table(:namespaces).create!(name: 'deploy_freeze', path: 'deploy_freeze') }
let(:project) { table(:projects).create!(id: 1, namespace_id: namespace.id) }
let(:freeze_periods) { table(:ci_freeze_periods) }
diff --git a/spec/migrations/20210708130419_reschedule_merge_request_diff_users_background_migration_spec.rb b/spec/migrations/20210708130419_reschedule_merge_request_diff_users_background_migration_spec.rb
index 7a281611650..604504d2206 100644
--- a/spec/migrations/20210708130419_reschedule_merge_request_diff_users_background_migration_spec.rb
+++ b/spec/migrations/20210708130419_reschedule_merge_request_diff_users_background_migration_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe RescheduleMergeRequestDiffUsersBackgroundMigration, :migration do
+RSpec.describe RescheduleMergeRequestDiffUsersBackgroundMigration, :migration, feature_category: :code_review do
let(:migration) { described_class.new }
describe '#up' do
diff --git a/spec/migrations/20210713042000_fix_ci_sources_pipelines_index_names_spec.rb b/spec/migrations/20210713042000_fix_ci_sources_pipelines_index_names_spec.rb
index adec1e05533..6761b69aed5 100644
--- a/spec/migrations/20210713042000_fix_ci_sources_pipelines_index_names_spec.rb
+++ b/spec/migrations/20210713042000_fix_ci_sources_pipelines_index_names_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe FixCiSourcesPipelinesIndexNames, :migration do
+RSpec.describe FixCiSourcesPipelinesIndexNames, :migration, feature_category: :continuous_integration do
def validate_foreign_keys_and_index!
aggregate_failures do
expect(subject.foreign_key_exists?(:ci_sources_pipelines, :ci_builds, column: :source_job_id, name: 'fk_be5624bf37')).to be_truthy
diff --git a/spec/migrations/20210722042939_update_issuable_slas_where_issue_closed_spec.rb b/spec/migrations/20210722042939_update_issuable_slas_where_issue_closed_spec.rb
index 63802acceb5..5674efbf187 100644
--- a/spec/migrations/20210722042939_update_issuable_slas_where_issue_closed_spec.rb
+++ b/spec/migrations/20210722042939_update_issuable_slas_where_issue_closed_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe UpdateIssuableSlasWhereIssueClosed, :migration do
+RSpec.describe UpdateIssuableSlasWhereIssueClosed, :migration, feature_category: :team_planning do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:issues) { table(:issues) }
diff --git a/spec/migrations/20210722150102_operations_feature_flags_correct_flexible_rollout_values_spec.rb b/spec/migrations/20210722150102_operations_feature_flags_correct_flexible_rollout_values_spec.rb
index 94af2bb1e9a..098dd647b27 100644
--- a/spec/migrations/20210722150102_operations_feature_flags_correct_flexible_rollout_values_spec.rb
+++ b/spec/migrations/20210722150102_operations_feature_flags_correct_flexible_rollout_values_spec.rb
@@ -4,8 +4,8 @@ require 'spec_helper'
require_migration!
-RSpec.describe OperationsFeatureFlagsCorrectFlexibleRolloutValues, :migration do
- let_it_be(:strategies) { table(:operations_strategies) }
+RSpec.describe OperationsFeatureFlagsCorrectFlexibleRolloutValues, :migration, feature_category: :feature_flags do
+ let!(:strategies) { table(:operations_strategies) }
let(:namespace) { table(:namespaces).create!(name: 'feature_flag', path: 'feature_flag') }
let(:project) { table(:projects).create!(namespace_id: namespace.id) }
diff --git a/spec/migrations/20210804150320_create_base_work_item_types_spec.rb b/spec/migrations/20210804150320_create_base_work_item_types_spec.rb
index ae510826fe1..5626b885626 100644
--- a/spec/migrations/20210804150320_create_base_work_item_types_spec.rb
+++ b/spec/migrations/20210804150320_create_base_work_item_types_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
require_migration!
-RSpec.describe CreateBaseWorkItemTypes, :migration do
+RSpec.describe CreateBaseWorkItemTypes, :migration, feature_category: :team_planning do
include MigrationHelpers::WorkItemTypesHelper
- let_it_be(:work_item_types) { table(:work_item_types) }
+ let!(:work_item_types) { table(:work_item_types) }
let(:base_types) do
{
diff --git a/spec/migrations/20210805192450_update_trial_plans_ci_daily_pipeline_schedule_triggers_spec.rb b/spec/migrations/20210805192450_update_trial_plans_ci_daily_pipeline_schedule_triggers_spec.rb
index b1885b96adb..d18673db757 100644
--- a/spec/migrations/20210805192450_update_trial_plans_ci_daily_pipeline_schedule_triggers_spec.rb
+++ b/spec/migrations/20210805192450_update_trial_plans_ci_daily_pipeline_schedule_triggers_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe UpdateTrialPlansCiDailyPipelineScheduleTriggers, :migration do
+RSpec.describe UpdateTrialPlansCiDailyPipelineScheduleTriggers, :migration, feature_category: :purchase do
let!(:plans) { table(:plans) }
let!(:plan_limits) { table(:plan_limits) }
let!(:premium_trial_plan) { plans.create!(name: 'premium_trial', title: 'Premium Trial') }
diff --git a/spec/migrations/20210811122206_update_external_project_bots_spec.rb b/spec/migrations/20210811122206_update_external_project_bots_spec.rb
index 365fb8e3218..aa0bce63865 100644
--- a/spec/migrations/20210811122206_update_external_project_bots_spec.rb
+++ b/spec/migrations/20210811122206_update_external_project_bots_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe UpdateExternalProjectBots, :migration do
+RSpec.describe UpdateExternalProjectBots, :migration, feature_category: :users do
def create_user(**extra_options)
defaults = { projects_limit: 0, email: "#{extra_options[:username]}@example.com" }
diff --git a/spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb b/spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb
index c88f94c6426..fcc2e1657d0 100644
--- a/spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb
+++ b/spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!('remove_duplicate_project_authorizations')
-RSpec.describe RemoveDuplicateProjectAuthorizations, :migration do
+RSpec.describe RemoveDuplicateProjectAuthorizations, :migration, feature_category: :authentication_and_authorization do
let(:users) { table(:users) }
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
diff --git a/spec/migrations/20210819145000_drop_temporary_columns_and_triggers_for_ci_builds_runner_session_spec.rb b/spec/migrations/20210819145000_drop_temporary_columns_and_triggers_for_ci_builds_runner_session_spec.rb
index 4ad4bea058b..e48f933ad5f 100644
--- a/spec/migrations/20210819145000_drop_temporary_columns_and_triggers_for_ci_builds_runner_session_spec.rb
+++ b/spec/migrations/20210819145000_drop_temporary_columns_and_triggers_for_ci_builds_runner_session_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe DropTemporaryColumnsAndTriggersForCiBuildsRunnerSession, :migration do
+RSpec.describe DropTemporaryColumnsAndTriggersForCiBuildsRunnerSession, :migration, feature_category: :runner do
let(:ci_builds_runner_session_table) { table(:ci_builds_runner_session) }
it 'correctly migrates up and down' do
diff --git a/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb b/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb
index 552602983d9..2a19dc025a7 100644
--- a/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb
+++ b/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
require_migration!
-RSpec.describe UpsertBaseWorkItemTypes, :migration do
+RSpec.describe UpsertBaseWorkItemTypes, :migration, feature_category: :team_planning do
include MigrationHelpers::WorkItemTypesHelper
- let_it_be(:work_item_types) { table(:work_item_types) }
+ let!(:work_item_types) { table(:work_item_types) }
let(:base_types) do
{
diff --git a/spec/migrations/20210902144144_drop_temporary_columns_and_triggers_for_ci_build_needs_spec.rb b/spec/migrations/20210902144144_drop_temporary_columns_and_triggers_for_ci_build_needs_spec.rb
index 4ec3c5b7211..0d89851cac1 100644
--- a/spec/migrations/20210902144144_drop_temporary_columns_and_triggers_for_ci_build_needs_spec.rb
+++ b/spec/migrations/20210902144144_drop_temporary_columns_and_triggers_for_ci_build_needs_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe DropTemporaryColumnsAndTriggersForCiBuildNeeds do
+RSpec.describe DropTemporaryColumnsAndTriggersForCiBuildNeeds, feature_category: :pipeline_authoring do
let(:ci_build_needs_table) { table(:ci_build_needs) }
it 'correctly migrates up and down' do
diff --git a/spec/migrations/20210906100316_drop_temporary_columns_and_triggers_for_ci_build_trace_chunks_spec.rb b/spec/migrations/20210906100316_drop_temporary_columns_and_triggers_for_ci_build_trace_chunks_spec.rb
index f1408e4ecab..eef4c7bc9fd 100644
--- a/spec/migrations/20210906100316_drop_temporary_columns_and_triggers_for_ci_build_trace_chunks_spec.rb
+++ b/spec/migrations/20210906100316_drop_temporary_columns_and_triggers_for_ci_build_trace_chunks_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe DropTemporaryColumnsAndTriggersForCiBuildTraceChunks do
+RSpec.describe DropTemporaryColumnsAndTriggersForCiBuildTraceChunks, feature_category: :continuous_integration do
let(:ci_build_trace_chunks_table) { table(:ci_build_trace_chunks) }
it 'correctly migrates up and down' do
diff --git a/spec/migrations/20210906130643_drop_temporary_columns_and_triggers_for_taggings_spec.rb b/spec/migrations/20210906130643_drop_temporary_columns_and_triggers_for_taggings_spec.rb
index e4385e501b2..208cbac2ae9 100644
--- a/spec/migrations/20210906130643_drop_temporary_columns_and_triggers_for_taggings_spec.rb
+++ b/spec/migrations/20210906130643_drop_temporary_columns_and_triggers_for_taggings_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe DropTemporaryColumnsAndTriggersForTaggings do
+RSpec.describe DropTemporaryColumnsAndTriggersForTaggings, feature_category: :continuous_integration do
let(:taggings_table) { table(:taggings) }
it 'correctly migrates up and down' do
diff --git a/spec/migrations/20210907013944_cleanup_bigint_conversion_for_ci_builds_metadata_spec.rb b/spec/migrations/20210907013944_cleanup_bigint_conversion_for_ci_builds_metadata_spec.rb
index 194832fbc43..63664803fba 100644
--- a/spec/migrations/20210907013944_cleanup_bigint_conversion_for_ci_builds_metadata_spec.rb
+++ b/spec/migrations/20210907013944_cleanup_bigint_conversion_for_ci_builds_metadata_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe CleanupBigintConversionForCiBuildsMetadata do
+RSpec.describe CleanupBigintConversionForCiBuildsMetadata, feature_category: :continuous_integration do
let(:ci_builds_metadata) { table(:ci_builds_metadata) }
it 'correctly migrates up and down' do
diff --git a/spec/migrations/20210907211557_finalize_ci_builds_bigint_conversion_spec.rb b/spec/migrations/20210907211557_finalize_ci_builds_bigint_conversion_spec.rb
index c0f56da7b4f..663b90f3fa7 100644
--- a/spec/migrations/20210907211557_finalize_ci_builds_bigint_conversion_spec.rb
+++ b/spec/migrations/20210907211557_finalize_ci_builds_bigint_conversion_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe FinalizeCiBuildsBigintConversion, :migration, schema: 20210907182359 do
+RSpec.describe FinalizeCiBuildsBigintConversion, :migration, schema: 20210907182359, feature_category: :continuous_integration do
context 'with an unexpected FK fk_3f0c88d7dc' do
it 'removes the FK and migrates successfully' do
# Add the unexpected FK
diff --git a/spec/migrations/20210910194952_update_report_type_for_existing_approval_project_rules_spec.rb b/spec/migrations/20210910194952_update_report_type_for_existing_approval_project_rules_spec.rb
index 69ee10eb0d1..e9d34fad76d 100644
--- a/spec/migrations/20210910194952_update_report_type_for_existing_approval_project_rules_spec.rb
+++ b/spec/migrations/20210910194952_update_report_type_for_existing_approval_project_rules_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe UpdateReportTypeForExistingApprovalProjectRules, :migration do
+RSpec.describe UpdateReportTypeForExistingApprovalProjectRules, :migration, feature_category: :source_code_management do
using RSpec::Parameterized::TableSyntax
let(:group) { table(:namespaces).create!(name: 'user', path: 'user') }
diff --git a/spec/migrations/20210914095310_cleanup_orphan_project_access_tokens_spec.rb b/spec/migrations/20210914095310_cleanup_orphan_project_access_tokens_spec.rb
index 2b755dfe11c..a9a814f9a48 100644
--- a/spec/migrations/20210914095310_cleanup_orphan_project_access_tokens_spec.rb
+++ b/spec/migrations/20210914095310_cleanup_orphan_project_access_tokens_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe CleanupOrphanProjectAccessTokens, :migration do
+RSpec.describe CleanupOrphanProjectAccessTokens, :migration, feature_category: :users do
def create_user(**extra_options)
defaults = { state: 'active', projects_limit: 0, email: "#{extra_options[:username]}@example.com" }
diff --git a/spec/migrations/20210915022415_cleanup_bigint_conversion_for_ci_builds_spec.rb b/spec/migrations/20210915022415_cleanup_bigint_conversion_for_ci_builds_spec.rb
index cedc62a6565..808c5371018 100644
--- a/spec/migrations/20210915022415_cleanup_bigint_conversion_for_ci_builds_spec.rb
+++ b/spec/migrations/20210915022415_cleanup_bigint_conversion_for_ci_builds_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe CleanupBigintConversionForCiBuilds do
+RSpec.describe CleanupBigintConversionForCiBuilds, feature_category: :continuous_integration do
let(:ci_builds) { table(:ci_builds) }
it 'correctly migrates up and down' do
diff --git a/spec/migrations/20210918201050_remove_old_pending_jobs_for_recalculate_vulnerabilities_occurrences_uuid_spec.rb b/spec/migrations/20210918201050_remove_old_pending_jobs_for_recalculate_vulnerabilities_occurrences_uuid_spec.rb
index d1c04c5d320..b3d1b41c330 100644
--- a/spec/migrations/20210918201050_remove_old_pending_jobs_for_recalculate_vulnerabilities_occurrences_uuid_spec.rb
+++ b/spec/migrations/20210918201050_remove_old_pending_jobs_for_recalculate_vulnerabilities_occurrences_uuid_spec.rb
@@ -21,10 +21,11 @@ def create_background_migration_jobs(ids, status, created_at)
)
end
-RSpec.describe RemoveOldPendingJobsForRecalculateVulnerabilitiesOccurrencesUuid, :migration do
- let_it_be(:background_migration_jobs) { table(:background_migration_jobs) }
- let_it_be(:before_target_date) { -Float::INFINITY..(DateTime.new(2021, 8, 17, 23, 59, 59)) }
- let_it_be(:after_target_date) { (DateTime.new(2021, 8, 18, 0, 0, 0))..Float::INFINITY }
+RSpec.describe RemoveOldPendingJobsForRecalculateVulnerabilitiesOccurrencesUuid, :migration,
+feature_category: :vulnerability_management do
+ let!(:background_migration_jobs) { table(:background_migration_jobs) }
+ let!(:before_target_date) { -Float::INFINITY..(DateTime.new(2021, 8, 17, 23, 59, 59)) }
+ let!(:after_target_date) { (DateTime.new(2021, 8, 18, 0, 0, 0))..Float::INFINITY }
context 'when old RecalculateVulnerabilitiesOccurrencesUuid jobs are pending' do
before do
diff --git a/spec/migrations/20210922021816_drop_int4_columns_for_ci_job_artifacts_spec.rb b/spec/migrations/20210922021816_drop_int4_columns_for_ci_job_artifacts_spec.rb
index a6eede8a8f1..c463f69c80c 100644
--- a/spec/migrations/20210922021816_drop_int4_columns_for_ci_job_artifacts_spec.rb
+++ b/spec/migrations/20210922021816_drop_int4_columns_for_ci_job_artifacts_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe DropInt4ColumnsForCiJobArtifacts do
+RSpec.describe DropInt4ColumnsForCiJobArtifacts, feature_category: :build_artifacts do
let(:ci_job_artifacts) { table(:ci_job_artifacts) }
it 'correctly migrates up and down' do
diff --git a/spec/migrations/20210922025631_drop_int4_column_for_ci_sources_pipelines_spec.rb b/spec/migrations/20210922025631_drop_int4_column_for_ci_sources_pipelines_spec.rb
index 730c9ade1fb..6b0c3a6db9a 100644
--- a/spec/migrations/20210922025631_drop_int4_column_for_ci_sources_pipelines_spec.rb
+++ b/spec/migrations/20210922025631_drop_int4_column_for_ci_sources_pipelines_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe DropInt4ColumnForCiSourcesPipelines do
+RSpec.describe DropInt4ColumnForCiSourcesPipelines, feature_category: :pipeline_authoring do
let(:ci_sources_pipelines) { table(:ci_sources_pipelines) }
it 'correctly migrates up and down' do
diff --git a/spec/migrations/20210922082019_drop_int4_column_for_events_spec.rb b/spec/migrations/20210922082019_drop_int4_column_for_events_spec.rb
index e460612a7d5..f615c8bb50e 100644
--- a/spec/migrations/20210922082019_drop_int4_column_for_events_spec.rb
+++ b/spec/migrations/20210922082019_drop_int4_column_for_events_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe DropInt4ColumnForEvents do
+RSpec.describe DropInt4ColumnForEvents, feature_category: :users do
let(:events) { table(:events) }
it 'correctly migrates up and down' do
diff --git a/spec/migrations/20210922091402_drop_int4_column_for_push_event_payloads_spec.rb b/spec/migrations/20210922091402_drop_int4_column_for_push_event_payloads_spec.rb
index 8c89cd19f7f..5c39e7530ff 100644
--- a/spec/migrations/20210922091402_drop_int4_column_for_push_event_payloads_spec.rb
+++ b/spec/migrations/20210922091402_drop_int4_column_for_push_event_payloads_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe DropInt4ColumnForPushEventPayloads do
+RSpec.describe DropInt4ColumnForPushEventPayloads, feature_category: :users do
let(:push_event_payloads) { table(:push_event_payloads) }
it 'correctly migrates up and down' do
diff --git a/spec/migrations/20211006060436_schedule_populate_topics_total_projects_count_cache_spec.rb b/spec/migrations/20211006060436_schedule_populate_topics_total_projects_count_cache_spec.rb
index 09ce0858b12..2f3903a20a9 100644
--- a/spec/migrations/20211006060436_schedule_populate_topics_total_projects_count_cache_spec.rb
+++ b/spec/migrations/20211006060436_schedule_populate_topics_total_projects_count_cache_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe SchedulePopulateTopicsTotalProjectsCountCache do
+RSpec.describe SchedulePopulateTopicsTotalProjectsCountCache, feature_category: :projects do
let(:topics) { table(:topics) }
let!(:topic_1) { topics.create!(name: 'Topic1') }
let!(:topic_2) { topics.create!(name: 'Topic2') }
diff --git a/spec/migrations/20211012134316_clean_up_migrate_merge_request_diff_commit_users_spec.rb b/spec/migrations/20211012134316_clean_up_migrate_merge_request_diff_commit_users_spec.rb
index 910e6d1d91b..f627ea825b3 100644
--- a/spec/migrations/20211012134316_clean_up_migrate_merge_request_diff_commit_users_spec.rb
+++ b/spec/migrations/20211012134316_clean_up_migrate_merge_request_diff_commit_users_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration! 'clean_up_migrate_merge_request_diff_commit_users'
-RSpec.describe CleanUpMigrateMergeRequestDiffCommitUsers, :migration do
+RSpec.describe CleanUpMigrateMergeRequestDiffCommitUsers, :migration, feature_category: :code_review do
describe '#up' do
context 'when there are pending jobs' do
it 'processes the jobs immediately' do
diff --git a/spec/migrations/20211018152654_schedule_remove_duplicate_vulnerabilities_findings3_spec.rb b/spec/migrations/20211018152654_schedule_remove_duplicate_vulnerabilities_findings3_spec.rb
index 95c5be2fc30..3e8176a36a1 100644
--- a/spec/migrations/20211018152654_schedule_remove_duplicate_vulnerabilities_findings3_spec.rb
+++ b/spec/migrations/20211018152654_schedule_remove_duplicate_vulnerabilities_findings3_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
require_migration!('schedule_remove_duplicate_vulnerabilities_findings3')
-RSpec.describe ScheduleRemoveDuplicateVulnerabilitiesFindings3, :migration do
+RSpec.describe ScheduleRemoveDuplicateVulnerabilitiesFindings3, :migration, feature_category: :vulnerability_management do
let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
let(:users) { table(:users) }
let(:user) { create_user! }
@@ -88,7 +88,6 @@ RSpec.describe ScheduleRemoveDuplicateVulnerabilitiesFindings3, :migration do
let!(:unrelated_finding) do
create_finding!(
id: 9999999,
- uuid: "unreleated_finding",
vulnerability_id: nil,
report_type: 1,
location_fingerprint: 'random_location_fingerprint',
@@ -131,11 +130,10 @@ RSpec.describe ScheduleRemoveDuplicateVulnerabilitiesFindings3, :migration do
# rubocop:disable Metrics/ParameterLists
def create_finding!(
- id: nil,
- vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:,
+ vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:, id: nil,
name: "test", severity: 7, confidence: 7, report_type: 0,
project_fingerprint: '123qweasdzxc', location_fingerprint: 'test',
- metadata_version: 'test', raw_metadata: 'test', uuid: 'test')
+ metadata_version: 'test', raw_metadata: 'test', uuid: SecureRandom.uuid)
vulnerability_findings.create!({
id: id,
vulnerability_id: vulnerability_id,
diff --git a/spec/migrations/20211028155449_schedule_fix_merge_request_diff_commit_users_migration_spec.rb b/spec/migrations/20211028155449_schedule_fix_merge_request_diff_commit_users_migration_spec.rb
index 6511f554436..c7a0b938ca1 100644
--- a/spec/migrations/20211028155449_schedule_fix_merge_request_diff_commit_users_migration_spec.rb
+++ b/spec/migrations/20211028155449_schedule_fix_merge_request_diff_commit_users_migration_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration! 'schedule_fix_merge_request_diff_commit_users_migration'
-RSpec.describe ScheduleFixMergeRequestDiffCommitUsersMigration, :migration do
+RSpec.describe ScheduleFixMergeRequestDiffCommitUsersMigration, :migration, feature_category: :code_review do
let(:migration) { described_class.new }
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
diff --git a/spec/migrations/20211101222614_consume_remaining_user_namespace_jobs_spec.rb b/spec/migrations/20211101222614_consume_remaining_user_namespace_jobs_spec.rb
index d78ecc26ebf..1688ebf7cb1 100644
--- a/spec/migrations/20211101222614_consume_remaining_user_namespace_jobs_spec.rb
+++ b/spec/migrations/20211101222614_consume_remaining_user_namespace_jobs_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ConsumeRemainingUserNamespaceJobs do
+RSpec.describe ConsumeRemainingUserNamespaceJobs, feature_category: :subgroups do
let(:namespaces) { table(:namespaces) }
let!(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org', type: nil) }
diff --git a/spec/migrations/20211110143306_add_not_null_constraint_to_security_findings_uuid_spec.rb b/spec/migrations/20211110143306_add_not_null_constraint_to_security_findings_uuid_spec.rb
index 946fbf7f568..3b69169b2d6 100644
--- a/spec/migrations/20211110143306_add_not_null_constraint_to_security_findings_uuid_spec.rb
+++ b/spec/migrations/20211110143306_add_not_null_constraint_to_security_findings_uuid_spec.rb
@@ -2,9 +2,9 @@
require 'spec_helper'
require_migration!
-RSpec.describe AddNotNullConstraintToSecurityFindingsUuid do
- let_it_be(:security_findings) { table(:security_findings) }
- let_it_be(:migration) { described_class.new }
+RSpec.describe AddNotNullConstraintToSecurityFindingsUuid, feature_category: :vulnerability_management do
+ let!(:security_findings) { table(:security_findings) }
+ let!(:migration) { described_class.new }
before do
allow(migration).to receive(:transaction_open?).and_return(false)
diff --git a/spec/migrations/20211110151350_schedule_drop_invalid_security_findings_spec.rb b/spec/migrations/20211110151350_schedule_drop_invalid_security_findings_spec.rb
index b35cf5cbf4c..d05828112e6 100644
--- a/spec/migrations/20211110151350_schedule_drop_invalid_security_findings_spec.rb
+++ b/spec/migrations/20211110151350_schedule_drop_invalid_security_findings_spec.rb
@@ -3,20 +3,21 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleDropInvalidSecurityFindings, :migration, schema: 20211108211434 do
- let_it_be(:background_migration_jobs) { table(:background_migration_jobs) }
+RSpec.describe ScheduleDropInvalidSecurityFindings, :migration, :suppress_gitlab_schemas_validate_connection, schema: 20211108211434,
+ feature_category: :vulnerability_management do
+ let!(:background_migration_jobs) { table(:background_migration_jobs) }
- let_it_be(:namespace) { table(:namespaces).create!(name: 'user', path: 'user', type: Namespaces::UserNamespace.sti_name) }
- let_it_be(:project) { table(:projects).create!(namespace_id: namespace.id) }
+ let!(:namespace) { table(:namespaces).create!(name: 'user', path: 'user', type: Namespaces::UserNamespace.sti_name) }
+ let!(:project) { table(:projects).create!(namespace_id: namespace.id) }
- let_it_be(:pipelines) { table(:ci_pipelines) }
- let_it_be(:pipeline) { pipelines.create!(project_id: project.id) }
+ let!(:pipelines) { table(:ci_pipelines) }
+ let!(:pipeline) { pipelines.create!(project_id: project.id) }
- let_it_be(:ci_builds) { table(:ci_builds) }
- let_it_be(:ci_build) { ci_builds.create! }
+ let!(:ci_builds) { table(:ci_builds) }
+ let!(:ci_build) { ci_builds.create! }
- let_it_be(:security_scans) { table(:security_scans) }
- let_it_be(:security_scan) do
+ let!(:security_scans) { table(:security_scans) }
+ let!(:security_scan) do
security_scans.create!(
scan_type: 1,
status: 1,
@@ -26,11 +27,11 @@ RSpec.describe ScheduleDropInvalidSecurityFindings, :migration, schema: 20211108
)
end
- let_it_be(:vulnerability_scanners) { table(:vulnerability_scanners) }
- let_it_be(:vulnerability_scanner) { vulnerability_scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
+ let!(:vulnerability_scanners) { table(:vulnerability_scanners) }
+ let!(:vulnerability_scanner) { vulnerability_scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
- let_it_be(:security_findings) { table(:security_findings) }
- let_it_be(:security_finding_without_uuid) do
+ let!(:security_findings) { table(:security_findings) }
+ let!(:security_finding_without_uuid) do
security_findings.create!(
severity: 1,
confidence: 1,
@@ -40,7 +41,7 @@ RSpec.describe ScheduleDropInvalidSecurityFindings, :migration, schema: 20211108
)
end
- let_it_be(:security_finding_with_uuid) do
+ let!(:security_finding_with_uuid) do
security_findings.create!(
severity: 1,
confidence: 1,
diff --git a/spec/migrations/20211116111644_schedule_remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb b/spec/migrations/20211116111644_schedule_remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb
index cf6a033b4b8..18513656029 100644
--- a/spec/migrations/20211116111644_schedule_remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb
+++ b/spec/migrations/20211116111644_schedule_remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb
@@ -4,23 +4,23 @@ require 'spec_helper'
require_migration!
RSpec.describe ScheduleRemoveOccurrencePipelinesAndDuplicateVulnerabilitiesFindings,
- :suppress_gitlab_schemas_validate_connection, :migration do
- let_it_be(:background_migration_jobs) { table(:background_migration_jobs) }
- let_it_be(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
- let_it_be(:users) { table(:users) }
- let_it_be(:user) { create_user! }
- let_it_be(:project) { table(:projects).create!(id: 14219619, namespace_id: namespace.id) }
- let_it_be(:pipelines) { table(:ci_pipelines) }
- let_it_be(:scanners) { table(:vulnerability_scanners) }
- let_it_be(:scanner1) { scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
- let_it_be(:scanner2) { scanners.create!(project_id: project.id, external_id: 'test 2', name: 'test scanner 2') }
- let_it_be(:scanner3) { scanners.create!(project_id: project.id, external_id: 'test 3', name: 'test scanner 3') }
- let_it_be(:unrelated_scanner) { scanners.create!(project_id: project.id, external_id: 'unreleated_scanner', name: 'unrelated scanner') }
- let_it_be(:vulnerabilities) { table(:vulnerabilities) }
- let_it_be(:vulnerability_findings) { table(:vulnerability_occurrences) }
- let_it_be(:vulnerability_finding_pipelines) { table(:vulnerability_occurrence_pipelines) }
- let_it_be(:vulnerability_identifiers) { table(:vulnerability_identifiers) }
- let_it_be(:vulnerability_identifier) do
+ :suppress_gitlab_schemas_validate_connection, :migration, feature_category: :vulnerability_management do
+ let!(:background_migration_jobs) { table(:background_migration_jobs) }
+ let!(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
+ let!(:users) { table(:users) }
+ let!(:user) { create_user! }
+ let!(:project) { table(:projects).create!(id: 14219619, namespace_id: namespace.id) }
+ let!(:pipelines) { table(:ci_pipelines) }
+ let!(:scanners) { table(:vulnerability_scanners) }
+ let!(:scanner1) { scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
+ let!(:scanner2) { scanners.create!(project_id: project.id, external_id: 'test 2', name: 'test scanner 2') }
+ let!(:scanner3) { scanners.create!(project_id: project.id, external_id: 'test 3', name: 'test scanner 3') }
+ let!(:unrelated_scanner) { scanners.create!(project_id: project.id, external_id: 'unreleated_scanner', name: 'unrelated scanner') }
+ let!(:vulnerabilities) { table(:vulnerabilities) }
+ let!(:vulnerability_findings) { table(:vulnerability_occurrences) }
+ let!(:vulnerability_finding_pipelines) { table(:vulnerability_occurrence_pipelines) }
+ let!(:vulnerability_identifiers) { table(:vulnerability_identifiers) }
+ let!(:vulnerability_identifier) do
vulnerability_identifiers.create!(
id: 1244459,
project_id: project.id,
@@ -30,14 +30,14 @@ RSpec.describe ScheduleRemoveOccurrencePipelinesAndDuplicateVulnerabilitiesFindi
name: 'vulnerability identifier')
end
- let_it_be(:vulnerability_for_first_duplicate) do
+ let!(:vulnerability_for_first_duplicate) do
create_vulnerability!(
project_id: project.id,
author_id: user.id
)
end
- let_it_be(:first_finding_duplicate) do
+ let!(:first_finding_duplicate) do
create_finding!(
id: 5606961,
uuid: "bd95c085-71aa-51d7-9bb6-08ae669c262e",
@@ -50,14 +50,14 @@ RSpec.describe ScheduleRemoveOccurrencePipelinesAndDuplicateVulnerabilitiesFindi
)
end
- let_it_be(:vulnerability_for_second_duplicate) do
+ let!(:vulnerability_for_second_duplicate) do
create_vulnerability!(
project_id: project.id,
author_id: user.id
)
end
- let_it_be(:second_finding_duplicate) do
+ let!(:second_finding_duplicate) do
create_finding!(
id: 8765432,
uuid: "5b714f58-1176-5b26-8fd5-e11dfcb031b5",
@@ -70,14 +70,14 @@ RSpec.describe ScheduleRemoveOccurrencePipelinesAndDuplicateVulnerabilitiesFindi
)
end
- let_it_be(:vulnerability_for_third_duplicate) do
+ let!(:vulnerability_for_third_duplicate) do
create_vulnerability!(
project_id: project.id,
author_id: user.id
)
end
- let_it_be(:third_finding_duplicate) do
+ let!(:third_finding_duplicate) do
create_finding!(
id: 8832995,
uuid: "cfe435fa-b25b-5199-a56d-7b007cc9e2d4",
@@ -90,10 +90,9 @@ RSpec.describe ScheduleRemoveOccurrencePipelinesAndDuplicateVulnerabilitiesFindi
)
end
- let_it_be(:unrelated_finding) do
+ let!(:unrelated_finding) do
create_finding!(
id: 9999999,
- uuid: "unreleated_finding",
vulnerability_id: nil,
report_type: 1,
location_fingerprint: 'random_location_fingerprint',
@@ -149,11 +148,10 @@ RSpec.describe ScheduleRemoveOccurrencePipelinesAndDuplicateVulnerabilitiesFindi
# rubocop:disable Metrics/ParameterLists
def create_finding!(
- id: nil,
- vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:,
+ vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:, id: nil,
name: "test", severity: 7, confidence: 7, report_type: 0,
project_fingerprint: '123qweasdzxc', location_fingerprint: 'test',
- metadata_version: 'test', raw_metadata: 'test', uuid: 'test')
+ metadata_version: 'test', raw_metadata: 'test', uuid: SecureRandom.uuid)
params = {
vulnerability_id: vulnerability_id,
project_id: project_id,
diff --git a/spec/migrations/20211117084814_migrate_remaining_u2f_registrations_spec.rb b/spec/migrations/20211117084814_migrate_remaining_u2f_registrations_spec.rb
index ef6dd94d9e3..bfe2b661a31 100644
--- a/spec/migrations/20211117084814_migrate_remaining_u2f_registrations_spec.rb
+++ b/spec/migrations/20211117084814_migrate_remaining_u2f_registrations_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe MigrateRemainingU2fRegistrations, :migration do
+RSpec.describe MigrateRemainingU2fRegistrations, :migration, feature_category: :authentication_and_authorization do
let(:u2f_registrations) { table(:u2f_registrations) }
let(:webauthn_registrations) { table(:webauthn_registrations) }
let(:users) { table(:users) }
diff --git a/spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb b/spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb
index bf4094eaa49..09a8bb44d88 100644
--- a/spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb
+++ b/spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe EncryptStaticObjectsExternalStorageAuthToken, :migration do
+RSpec.describe EncryptStaticObjectsExternalStorageAuthToken, :migration, feature_category: :source_code_management do
let(:application_settings) do
Class.new(ActiveRecord::Base) do
self.table_name = 'application_settings'
diff --git a/spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb b/spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb
index 34a6e2fdd12..32edd3615ff 100644
--- a/spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb
+++ b/spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
require_migration!
-RSpec.describe AddTaskToWorkItemTypes, :migration do
+RSpec.describe AddTaskToWorkItemTypes, :migration, feature_category: :team_planning do
include MigrationHelpers::WorkItemTypesHelper
- let_it_be(:work_item_types) { table(:work_item_types) }
+ let!(:work_item_types) { table(:work_item_types) }
let(:base_types) do
{
diff --git a/spec/migrations/20211130165043_backfill_sequence_column_for_sprints_table_spec.rb b/spec/migrations/20211130165043_backfill_sequence_column_for_sprints_table_spec.rb
index 809ee53462f..91646da4791 100644
--- a/spec/migrations/20211130165043_backfill_sequence_column_for_sprints_table_spec.rb
+++ b/spec/migrations/20211130165043_backfill_sequence_column_for_sprints_table_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe BackfillSequenceColumnForSprintsTable, :migration, schema: 20211126042235 do
+RSpec.describe BackfillSequenceColumnForSprintsTable, :migration, schema: 20211126042235, feature_category: :team_planning do
let(:migration) { described_class.new }
let(:namespaces) { table(:namespaces) }
let(:sprints) { table(:sprints) }
diff --git a/spec/migrations/20211203091642_add_index_to_projects_on_marked_for_deletion_at_spec.rb b/spec/migrations/20211203091642_add_index_to_projects_on_marked_for_deletion_at_spec.rb
index 2e1289c58f7..7be54bc13cc 100644
--- a/spec/migrations/20211203091642_add_index_to_projects_on_marked_for_deletion_at_spec.rb
+++ b/spec/migrations/20211203091642_add_index_to_projects_on_marked_for_deletion_at_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe AddIndexToProjectsOnMarkedForDeletionAt do
+RSpec.describe AddIndexToProjectsOnMarkedForDeletionAt, feature_category: :projects do
it 'correctly migrates up and down' do
reversible_migration do |migration|
migration.before -> {
diff --git a/spec/migrations/20211207125331_remove_jobs_for_recalculate_vulnerabilities_occurrences_uuid_spec.rb b/spec/migrations/20211207125331_remove_jobs_for_recalculate_vulnerabilities_occurrences_uuid_spec.rb
index 491aad1b30b..be89ee9d2aa 100644
--- a/spec/migrations/20211207125331_remove_jobs_for_recalculate_vulnerabilities_occurrences_uuid_spec.rb
+++ b/spec/migrations/20211207125331_remove_jobs_for_recalculate_vulnerabilities_occurrences_uuid_spec.rb
@@ -20,8 +20,9 @@ def create_background_migration_jobs(ids, status, created_at)
)
end
-RSpec.describe RemoveJobsForRecalculateVulnerabilitiesOccurrencesUuid, :migration do
- let_it_be(:background_migration_jobs) { table(:background_migration_jobs) }
+RSpec.describe RemoveJobsForRecalculateVulnerabilitiesOccurrencesUuid, :migration,
+feature_category: :vulnerability_management do
+ let!(:background_migration_jobs) { table(:background_migration_jobs) }
context 'when RecalculateVulnerabilitiesOccurrencesUuid jobs are present' do
before do
diff --git a/spec/migrations/20211207135331_schedule_recalculate_uuid_on_vulnerabilities_occurrences4_spec.rb b/spec/migrations/20211207135331_schedule_recalculate_uuid_on_vulnerabilities_occurrences4_spec.rb
index 71ffcafaae1..c7401c4790d 100644
--- a/spec/migrations/20211207135331_schedule_recalculate_uuid_on_vulnerabilities_occurrences4_spec.rb
+++ b/spec/migrations/20211207135331_schedule_recalculate_uuid_on_vulnerabilities_occurrences4_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleRecalculateUuidOnVulnerabilitiesOccurrences4 do
+RSpec.describe ScheduleRecalculateUuidOnVulnerabilitiesOccurrences4, feature_category: :vulnerability_management do
let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
let(:users) { table(:users) }
let(:user) { create_user! }
diff --git a/spec/migrations/20211210140629_encrypt_static_object_token_spec.rb b/spec/migrations/20211210140629_encrypt_static_object_token_spec.rb
index 289cf9a93ed..f103ee54990 100644
--- a/spec/migrations/20211210140629_encrypt_static_object_token_spec.rb
+++ b/spec/migrations/20211210140629_encrypt_static_object_token_spec.rb
@@ -3,9 +3,9 @@ require 'spec_helper'
require_migration!
-RSpec.describe EncryptStaticObjectToken, :migration do
- let_it_be(:background_migration_jobs) { table(:background_migration_jobs) }
- let_it_be(:users) { table(:users) }
+RSpec.describe EncryptStaticObjectToken, :migration, feature_category: :source_code_management do
+ let!(:background_migration_jobs) { table(:background_migration_jobs) }
+ let!(:users) { table(:users) }
let!(:user_without_tokens) { create_user!(name: 'notoken') }
let!(:user_with_plaintext_token_1) { create_user!(name: 'plaintext_1', token: 'token') }
diff --git a/spec/migrations/20211214012507_backfill_incident_issue_escalation_statuses_spec.rb b/spec/migrations/20211214012507_backfill_incident_issue_escalation_statuses_spec.rb
index 791c0595f0e..0df52df43d8 100644
--- a/spec/migrations/20211214012507_backfill_incident_issue_escalation_statuses_spec.rb
+++ b/spec/migrations/20211214012507_backfill_incident_issue_escalation_statuses_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillIncidentIssueEscalationStatuses do
+RSpec.describe BackfillIncidentIssueEscalationStatuses, feature_category: :incident_management do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:issues) { table(:issues) }
diff --git a/spec/migrations/20211217174331_mark_recalculate_finding_signatures_as_completed_spec.rb b/spec/migrations/20211217174331_mark_recalculate_finding_signatures_as_completed_spec.rb
index c5058f30d82..2d808adf578 100644
--- a/spec/migrations/20211217174331_mark_recalculate_finding_signatures_as_completed_spec.rb
+++ b/spec/migrations/20211217174331_mark_recalculate_finding_signatures_as_completed_spec.rb
@@ -20,8 +20,8 @@ def create_background_migration_jobs(ids, status, created_at)
)
end
-RSpec.describe MarkRecalculateFindingSignaturesAsCompleted, :migration do
- let_it_be(:background_migration_jobs) { table(:background_migration_jobs) }
+RSpec.describe MarkRecalculateFindingSignaturesAsCompleted, :migration, feature_category: :vulnerability_management do
+ let!(:background_migration_jobs) { table(:background_migration_jobs) }
context 'when RecalculateVulnerabilitiesOccurrencesUuid jobs are present' do
before do
diff --git a/spec/migrations/20220106111958_add_insert_or_update_vulnerability_reads_trigger_spec.rb b/spec/migrations/20220106111958_add_insert_or_update_vulnerability_reads_trigger_spec.rb
index 3e450546315..263289462ba 100644
--- a/spec/migrations/20220106111958_add_insert_or_update_vulnerability_reads_trigger_spec.rb
+++ b/spec/migrations/20220106111958_add_insert_or_update_vulnerability_reads_trigger_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe AddInsertOrUpdateVulnerabilityReadsTrigger do
+RSpec.describe AddInsertOrUpdateVulnerabilityReadsTrigger, feature_category: :vulnerability_management do
let(:migration) { described_class.new }
let(:vulnerabilities) { table(:vulnerabilities) }
let(:vulnerability_reads) { table(:vulnerability_reads) }
@@ -126,7 +126,7 @@ RSpec.describe AddInsertOrUpdateVulnerabilityReadsTrigger do
# rubocop:disable Metrics/ParameterLists
def create_finding!(
- vulnerability_id: nil, project_id:, scanner_id:, primary_identifier_id:,
+ project_id:, scanner_id:, primary_identifier_id:, vulnerability_id: nil,
name: "test", severity: 7, confidence: 7, report_type: 0,
project_fingerprint: '123qweasdzxc', location: { "image" => "alpine:3.4" }, location_fingerprint: 'test',
metadata_version: 'test', raw_metadata: 'test', uuid: SecureRandom.uuid)
diff --git a/spec/migrations/20220106112043_add_update_vulnerability_reads_trigger_spec.rb b/spec/migrations/20220106112043_add_update_vulnerability_reads_trigger_spec.rb
index d988b1e42b9..152a551bc7b 100644
--- a/spec/migrations/20220106112043_add_update_vulnerability_reads_trigger_spec.rb
+++ b/spec/migrations/20220106112043_add_update_vulnerability_reads_trigger_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe AddUpdateVulnerabilityReadsTrigger do
+RSpec.describe AddUpdateVulnerabilityReadsTrigger, feature_category: :vulnerability_management do
let(:migration) { described_class.new }
let(:vulnerability_reads) { table(:vulnerability_reads) }
let(:issue_links) { table(:vulnerability_issue_links) }
@@ -103,7 +103,7 @@ RSpec.describe AddUpdateVulnerabilityReadsTrigger do
# rubocop:disable Metrics/ParameterLists
def create_finding!(
- vulnerability_id: nil, project_id:, scanner_id:, primary_identifier_id:,
+ project_id:, scanner_id:, primary_identifier_id:, vulnerability_id: nil,
name: "test", severity: 7, confidence: 7, report_type: 0,
project_fingerprint: '123qweasdzxc', location: { "image" => "alpine:3.4" }, location_fingerprint: 'test',
metadata_version: 'test', raw_metadata: 'test', uuid: SecureRandom.uuid)
diff --git a/spec/migrations/20220106112085_add_update_vulnerability_reads_location_trigger_spec.rb b/spec/migrations/20220106112085_add_update_vulnerability_reads_location_trigger_spec.rb
index 901f1cf6041..9fc40b0b5f1 100644
--- a/spec/migrations/20220106112085_add_update_vulnerability_reads_location_trigger_spec.rb
+++ b/spec/migrations/20220106112085_add_update_vulnerability_reads_location_trigger_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe AddUpdateVulnerabilityReadsLocationTrigger do
+RSpec.describe AddUpdateVulnerabilityReadsLocationTrigger, feature_category: :vulnerability_management do
let(:migration) { described_class.new }
let(:vulnerability_reads) { table(:vulnerability_reads) }
let(:issue_links) { table(:vulnerability_issue_links) }
@@ -111,7 +111,7 @@ RSpec.describe AddUpdateVulnerabilityReadsLocationTrigger do
# rubocop:disable Metrics/ParameterLists
def create_finding!(
- vulnerability_id: nil, project_id:, scanner_id:, primary_identifier_id:,
+ project_id:, scanner_id:, primary_identifier_id:, vulnerability_id: nil,
name: "test", severity: 7, confidence: 7, report_type: 0,
project_fingerprint: '123qweasdzxc', location: { "image" => "alpine:3.4" }, location_fingerprint: 'test',
metadata_version: 'test', raw_metadata: 'test', uuid: SecureRandom.uuid)
diff --git a/spec/migrations/20220106163326_add_has_issues_on_vulnerability_reads_trigger_spec.rb b/spec/migrations/20220106163326_add_has_issues_on_vulnerability_reads_trigger_spec.rb
index 8e50b74eb9c..e58fdfb1591 100644
--- a/spec/migrations/20220106163326_add_has_issues_on_vulnerability_reads_trigger_spec.rb
+++ b/spec/migrations/20220106163326_add_has_issues_on_vulnerability_reads_trigger_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe AddHasIssuesOnVulnerabilityReadsTrigger do
+RSpec.describe AddHasIssuesOnVulnerabilityReadsTrigger, feature_category: :vulnerability_management do
let(:migration) { described_class.new }
let(:vulnerability_reads) { table(:vulnerability_reads) }
let(:issue_links) { table(:vulnerability_issue_links) }
@@ -109,7 +109,7 @@ RSpec.describe AddHasIssuesOnVulnerabilityReadsTrigger do
# rubocop:disable Metrics/ParameterLists
def create_finding!(
- vulnerability_id: nil, project_id:, scanner_id:, primary_identifier_id:,
+ project_id:, scanner_id:, primary_identifier_id:, vulnerability_id: nil,
name: "test", severity: 7, confidence: 7, report_type: 0,
project_fingerprint: '123qweasdzxc', location: { "image" => "alpine:3.4" }, location_fingerprint: 'test',
metadata_version: 'test', raw_metadata: 'test', uuid: SecureRandom.uuid)
diff --git a/spec/migrations/20220107064845_populate_vulnerability_reads_spec.rb b/spec/migrations/20220107064845_populate_vulnerability_reads_spec.rb
index 063a51227dd..1338f826537 100644
--- a/spec/migrations/20220107064845_populate_vulnerability_reads_spec.rb
+++ b/spec/migrations/20220107064845_populate_vulnerability_reads_spec.rb
@@ -3,17 +3,17 @@ require 'spec_helper'
require_migration!
-RSpec.describe PopulateVulnerabilityReads, :migration do
- let_it_be(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
- let_it_be(:user) { table(:users).create!(email: 'author@example.com', username: 'author', projects_limit: 10) }
- let_it_be(:project) { table(:projects).create!(namespace_id: namespace.id) }
- let_it_be(:scanner) { table(:vulnerability_scanners).create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
- let_it_be(:background_migration_jobs) { table(:background_migration_jobs) }
- let_it_be(:vulnerabilities) { table(:vulnerabilities) }
- let_it_be(:vulnerability_reads) { table(:vulnerability_reads) }
- let_it_be(:vulnerabilities_findings) { table(:vulnerability_occurrences) }
- let_it_be(:vulnerability_issue_links) { table(:vulnerability_issue_links) }
- let_it_be(:vulnerability_ids) { [] }
+RSpec.describe PopulateVulnerabilityReads, :migration, feature_category: :vulnerability_management do
+ let!(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
+ let!(:user) { table(:users).create!(email: 'author@example.com', username: 'author', projects_limit: 10) }
+ let!(:project) { table(:projects).create!(namespace_id: namespace.id) }
+ let!(:scanner) { table(:vulnerability_scanners).create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
+ let!(:background_migration_jobs) { table(:background_migration_jobs) }
+ let!(:vulnerabilities) { table(:vulnerabilities) }
+ let!(:vulnerability_reads) { table(:vulnerability_reads) }
+ let!(:vulnerabilities_findings) { table(:vulnerability_occurrences) }
+ let!(:vulnerability_issue_links) { table(:vulnerability_issue_links) }
+ let!(:vulnerability_ids) { [] }
before do
stub_const("#{described_class}::BATCH_SIZE", 1)
@@ -80,8 +80,7 @@ RSpec.describe PopulateVulnerabilityReads, :migration do
# rubocop:disable Metrics/ParameterLists
def create_finding!(
- id: nil,
- vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:,
+ vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:, id: nil,
name: "test", severity: 7, confidence: 7, report_type: 0,
project_fingerprint: '123qweasdzxc', location_fingerprint: 'test',
metadata_version: 'test', raw_metadata: 'test', uuid: SecureRandom.uuid)
diff --git a/spec/migrations/20220120094340_drop_position_from_security_findings_spec.rb b/spec/migrations/20220120094340_drop_position_from_security_findings_spec.rb
index 2ad9a8220c3..1470f2b3cad 100644
--- a/spec/migrations/20220120094340_drop_position_from_security_findings_spec.rb
+++ b/spec/migrations/20220120094340_drop_position_from_security_findings_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!('drop_position_from_security_findings')
-RSpec.describe DropPositionFromSecurityFindings do
+RSpec.describe DropPositionFromSecurityFindings, feature_category: :vulnerability_management do
let(:events) { table(:security_findings) }
it 'correctly migrates up and down' do
diff --git a/spec/migrations/20220124130028_dedup_runner_projects_spec.rb b/spec/migrations/20220124130028_dedup_runner_projects_spec.rb
index 3429ccc4df1..ee468f40908 100644
--- a/spec/migrations/20220124130028_dedup_runner_projects_spec.rb
+++ b/spec/migrations/20220124130028_dedup_runner_projects_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe DedupRunnerProjects, :migration, :suppress_gitlab_schemas_validate_connection, schema: 20220120085655 do
+RSpec.describe DedupRunnerProjects, :migration, :suppress_gitlab_schemas_validate_connection,
+schema: 20220120085655, feature_category: :runner do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:runners) { table(:ci_runners) }
diff --git a/spec/migrations/20220128155251_remove_dangling_running_builds_spec.rb b/spec/migrations/20220128155251_remove_dangling_running_builds_spec.rb
index a23f9995875..ea88cf1a2ce 100644
--- a/spec/migrations/20220128155251_remove_dangling_running_builds_spec.rb
+++ b/spec/migrations/20220128155251_remove_dangling_running_builds_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
require_migration!('remove_dangling_running_builds')
-RSpec.describe RemoveDanglingRunningBuilds, :suppress_gitlab_schemas_validate_connection do
+RSpec.describe RemoveDanglingRunningBuilds, :suppress_gitlab_schemas_validate_connection,
+feature_category: :continuous_integration do
let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
let(:project) { table(:projects).create!(namespace_id: namespace.id) }
let(:runner) { table(:ci_runners).create!(runner_type: 1) }
diff --git a/spec/migrations/20220128155814_fix_approval_rules_code_owners_rule_type_index_spec.rb b/spec/migrations/20220128155814_fix_approval_rules_code_owners_rule_type_index_spec.rb
index 1558facdf96..3f3fdd0889d 100644
--- a/spec/migrations/20220128155814_fix_approval_rules_code_owners_rule_type_index_spec.rb
+++ b/spec/migrations/20220128155814_fix_approval_rules_code_owners_rule_type_index_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!('fix_approval_rules_code_owners_rule_type_index')
-RSpec.describe FixApprovalRulesCodeOwnersRuleTypeIndex, :migration do
+RSpec.describe FixApprovalRulesCodeOwnersRuleTypeIndex, :migration, feature_category: :source_code_management do
let(:table_name) { :approval_merge_request_rules }
let(:index_name) { 'index_approval_rules_code_owners_rule_type' }
diff --git a/spec/migrations/20220202105733_delete_service_template_records_spec.rb b/spec/migrations/20220202105733_delete_service_template_records_spec.rb
index c9f6b5cbe66..41762a3a5c3 100644
--- a/spec/migrations/20220202105733_delete_service_template_records_spec.rb
+++ b/spec/migrations/20220202105733_delete_service_template_records_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe DeleteServiceTemplateRecords do
+RSpec.describe DeleteServiceTemplateRecords, feature_category: :integrations do
let(:integrations) { table(:integrations) }
let(:chat_names) { table(:chat_names) }
let(:web_hooks) { table(:web_hooks) }
diff --git a/spec/migrations/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size_spec.rb b/spec/migrations/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size_spec.rb
index 39398fa058d..cbae5674d78 100644
--- a/spec/migrations/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size_spec.rb
+++ b/spec/migrations/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size_spec.rb
@@ -3,23 +3,23 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillNamespaceStatisticsWithDependencyProxySize do
- let_it_be(:groups) { table(:namespaces) }
- let_it_be(:group1) { groups.create!(id: 10, name: 'test1', path: 'test1', type: 'Group') }
- let_it_be(:group2) { groups.create!(id: 20, name: 'test2', path: 'test2', type: 'Group') }
- let_it_be(:group3) { groups.create!(id: 30, name: 'test3', path: 'test3', type: 'Group') }
- let_it_be(:group4) { groups.create!(id: 40, name: 'test4', path: 'test4', type: 'Group') }
+RSpec.describe BackfillNamespaceStatisticsWithDependencyProxySize, feature_category: :dependency_proxy do
+ let!(:groups) { table(:namespaces) }
+ let!(:group1) { groups.create!(id: 10, name: 'test1', path: 'test1', type: 'Group') }
+ let!(:group2) { groups.create!(id: 20, name: 'test2', path: 'test2', type: 'Group') }
+ let!(:group3) { groups.create!(id: 30, name: 'test3', path: 'test3', type: 'Group') }
+ let!(:group4) { groups.create!(id: 40, name: 'test4', path: 'test4', type: 'Group') }
- let_it_be(:dependency_proxy_blobs) { table(:dependency_proxy_blobs) }
- let_it_be(:dependency_proxy_manifests) { table(:dependency_proxy_manifests) }
+ let!(:dependency_proxy_blobs) { table(:dependency_proxy_blobs) }
+ let!(:dependency_proxy_manifests) { table(:dependency_proxy_manifests) }
- let_it_be(:group1_manifest) { create_manifest(10, 10) }
- let_it_be(:group2_manifest) { create_manifest(20, 20) }
- let_it_be(:group3_manifest) { create_manifest(30, 30) }
+ let!(:group1_manifest) { create_manifest(10, 10) }
+ let!(:group2_manifest) { create_manifest(20, 20) }
+ let!(:group3_manifest) { create_manifest(30, 30) }
- let_it_be(:group1_blob) { create_blob(10, 10) }
- let_it_be(:group2_blob) { create_blob(20, 20) }
- let_it_be(:group3_blob) { create_blob(30, 30) }
+ let!(:group1_blob) { create_blob(10, 10) }
+ let!(:group2_blob) { create_blob(20, 20) }
+ let!(:group3_blob) { create_blob(30, 30) }
describe '#up' do
it 'correctly schedules background migrations' do
diff --git a/spec/migrations/20220204194347_encrypt_integration_properties_spec.rb b/spec/migrations/20220204194347_encrypt_integration_properties_spec.rb
index 78e3b43ff76..5e728bb396c 100644
--- a/spec/migrations/20220204194347_encrypt_integration_properties_spec.rb
+++ b/spec/migrations/20220204194347_encrypt_integration_properties_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe EncryptIntegrationProperties, :migration, schema: 20220204193000 do
+RSpec.describe EncryptIntegrationProperties, :migration, schema: 20220204193000, feature_category: :integrations do
subject(:migration) { described_class.new }
let(:integrations) { table(:integrations) }
diff --git a/spec/migrations/20220208080921_schedule_migrate_personal_namespace_project_maintainer_to_owner_spec.rb b/spec/migrations/20220208080921_schedule_migrate_personal_namespace_project_maintainer_to_owner_spec.rb
index 41f3476dea8..89583d1050b 100644
--- a/spec/migrations/20220208080921_schedule_migrate_personal_namespace_project_maintainer_to_owner_spec.rb
+++ b/spec/migrations/20220208080921_schedule_migrate_personal_namespace_project_maintainer_to_owner_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleMigratePersonalNamespaceProjectMaintainerToOwner do
- let_it_be(:migration) { described_class::MIGRATION }
+RSpec.describe ScheduleMigratePersonalNamespaceProjectMaintainerToOwner, feature_category: :subgroups do
+ let!(:migration) { described_class::MIGRATION }
describe '#up' do
it 'schedules background jobs for each batch of members' do
diff --git a/spec/migrations/20220211214605_update_integrations_trigger_type_new_on_insert_null_safe_spec.rb b/spec/migrations/20220211214605_update_integrations_trigger_type_new_on_insert_null_safe_spec.rb
index bf79ee02ff1..8a6a542bc5e 100644
--- a/spec/migrations/20220211214605_update_integrations_trigger_type_new_on_insert_null_safe_spec.rb
+++ b/spec/migrations/20220211214605_update_integrations_trigger_type_new_on_insert_null_safe_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe UpdateIntegrationsTriggerTypeNewOnInsertNullSafe, :migration do
+RSpec.describe UpdateIntegrationsTriggerTypeNewOnInsertNullSafe, :migration, feature_category: :integrations do
let(:integrations) { table(:integrations) }
before do
diff --git a/spec/migrations/20220213103859_remove_integrations_type_spec.rb b/spec/migrations/20220213103859_remove_integrations_type_spec.rb
index b1a4370700a..8f6d9b0d9b5 100644
--- a/spec/migrations/20220213103859_remove_integrations_type_spec.rb
+++ b/spec/migrations/20220213103859_remove_integrations_type_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe RemoveIntegrationsType, :migration do
+RSpec.describe RemoveIntegrationsType, :migration, feature_category: :integrations do
subject(:migration) { described_class.new }
let(:integrations) { table(:integrations) }
diff --git a/spec/migrations/20220222192524_create_not_null_constraint_releases_tag_spec.rb b/spec/migrations/20220222192524_create_not_null_constraint_releases_tag_spec.rb
index bd7d992240a..b8a37dcd6d9 100644
--- a/spec/migrations/20220222192524_create_not_null_constraint_releases_tag_spec.rb
+++ b/spec/migrations/20220222192524_create_not_null_constraint_releases_tag_spec.rb
@@ -2,9 +2,9 @@
require 'spec_helper'
require_migration!
-RSpec.describe CreateNotNullConstraintReleasesTag do
- let_it_be(:releases) { table(:releases) }
- let_it_be(:migration) { described_class.new }
+RSpec.describe CreateNotNullConstraintReleasesTag, feature_category: :release_orchestration do
+ let!(:releases) { table(:releases) }
+ let!(:migration) { described_class.new }
before do
allow(migration).to receive(:transaction_open?).and_return(false)
diff --git a/spec/migrations/20220222192525_remove_null_releases_spec.rb b/spec/migrations/20220222192525_remove_null_releases_spec.rb
index 6043f2c8cc8..ce42dea077d 100644
--- a/spec/migrations/20220222192525_remove_null_releases_spec.rb
+++ b/spec/migrations/20220222192525_remove_null_releases_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe RemoveNullReleases do
+RSpec.describe RemoveNullReleases, feature_category: :release_orchestration do
let(:releases) { table(:releases) }
before do
diff --git a/spec/migrations/20220223124428_schedule_merge_topics_with_same_name_spec.rb b/spec/migrations/20220223124428_schedule_merge_topics_with_same_name_spec.rb
index d9f6729475c..425f622581b 100644
--- a/spec/migrations/20220223124428_schedule_merge_topics_with_same_name_spec.rb
+++ b/spec/migrations/20220223124428_schedule_merge_topics_with_same_name_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleMergeTopicsWithSameName do
+RSpec.describe ScheduleMergeTopicsWithSameName, feature_category: :projects do
let(:topics) { table(:topics) }
describe '#up' do
diff --git a/spec/migrations/20220305223212_add_security_training_providers_spec.rb b/spec/migrations/20220305223212_add_security_training_providers_spec.rb
index 3d0089aaa8d..f67db3b68cd 100644
--- a/spec/migrations/20220305223212_add_security_training_providers_spec.rb
+++ b/spec/migrations/20220305223212_add_security_training_providers_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
require_migration!
-RSpec.describe AddSecurityTrainingProviders, :migration do
+RSpec.describe AddSecurityTrainingProviders, :migration, feature_category: :vulnerability_management do
include MigrationHelpers::WorkItemTypesHelper
- let_it_be(:security_training_providers) { table(:security_training_providers) }
+ let!(:security_training_providers) { table(:security_training_providers) }
it 'creates default data' do
# Need to delete all as security training providers are seeded before entire test suite
diff --git a/spec/migrations/20220307192610_remove_duplicate_project_tag_releases_spec.rb b/spec/migrations/20220307192610_remove_duplicate_project_tag_releases_spec.rb
index 8a653869a9b..3bdd6e5fab9 100644
--- a/spec/migrations/20220307192610_remove_duplicate_project_tag_releases_spec.rb
+++ b/spec/migrations/20220307192610_remove_duplicate_project_tag_releases_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe RemoveDuplicateProjectTagReleases do
+RSpec.describe RemoveDuplicateProjectTagReleases, feature_category: :release_orchestration do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:users) { table(:users) }
diff --git a/spec/migrations/20220309084954_remove_leftover_external_pull_request_deletions_spec.rb b/spec/migrations/20220309084954_remove_leftover_external_pull_request_deletions_spec.rb
index c471fd86bf5..a57d3633ecf 100644
--- a/spec/migrations/20220309084954_remove_leftover_external_pull_request_deletions_spec.rb
+++ b/spec/migrations/20220309084954_remove_leftover_external_pull_request_deletions_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe RemoveLeftoverExternalPullRequestDeletions do
+RSpec.describe RemoveLeftoverExternalPullRequestDeletions, feature_category: :sharding do
let(:deleted_records) { table(:loose_foreign_keys_deleted_records) }
let(:pending_record1) { deleted_records.create!(id: 1, fully_qualified_table_name: 'public.external_pull_requests', primary_key_value: 1, status: 1) }
diff --git a/spec/migrations/20220310141349_remove_dependency_list_usage_data_from_redis_spec.rb b/spec/migrations/20220310141349_remove_dependency_list_usage_data_from_redis_spec.rb
index c00685c1397..f40f9c70833 100644
--- a/spec/migrations/20220310141349_remove_dependency_list_usage_data_from_redis_spec.rb
+++ b/spec/migrations/20220310141349_remove_dependency_list_usage_data_from_redis_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe RemoveDependencyListUsageDataFromRedis, :migration, :clean_gitlab_redis_shared_state do
+RSpec.describe RemoveDependencyListUsageDataFromRedis, :migration, :clean_gitlab_redis_shared_state,
+feature_category: :dependency_management do
let(:key) { "DEPENDENCY_LIST_USAGE_COUNTER" }
describe "#up" do
diff --git a/spec/migrations/20220315171129_cleanup_draft_data_from_faulty_regex_spec.rb b/spec/migrations/20220315171129_cleanup_draft_data_from_faulty_regex_spec.rb
index 925f1e573be..1760535e66f 100644
--- a/spec/migrations/20220315171129_cleanup_draft_data_from_faulty_regex_spec.rb
+++ b/spec/migrations/20220315171129_cleanup_draft_data_from_faulty_regex_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe CleanupDraftDataFromFaultyRegex do
+RSpec.describe CleanupDraftDataFromFaultyRegex, feature_category: :code_review do
let(:merge_requests) { table(:merge_requests) }
let!(:namespace) { table(:namespaces).create!(name: 'namespace', path: 'namespace') }
diff --git a/spec/migrations/20220316202640_populate_container_repositories_migration_plan_spec.rb b/spec/migrations/20220316202640_populate_container_repositories_migration_plan_spec.rb
index 7b5c8254163..16ebbf8b004 100644
--- a/spec/migrations/20220316202640_populate_container_repositories_migration_plan_spec.rb
+++ b/spec/migrations/20220316202640_populate_container_repositories_migration_plan_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
require_migration!
-RSpec.describe PopulateContainerRepositoriesMigrationPlan, :aggregate_failures do
- let_it_be(:namespaces) { table(:namespaces) }
- let_it_be(:projects) { table(:projects) }
- let_it_be(:container_repositories) { table(:container_repositories) }
+RSpec.describe PopulateContainerRepositoriesMigrationPlan, :aggregate_failures, feature_category: :container_registry do
+ let!(:namespaces) { table(:namespaces) }
+ let!(:projects) { table(:projects) }
+ let!(:container_repositories) { table(:container_repositories) }
let!(:namespace) { namespaces.create!(id: 1, name: 'namespace', path: 'namespace') }
let!(:project) { projects.create!(id: 1, name: 'project', path: 'project', namespace_id: 1) }
diff --git a/spec/migrations/20220321234317_remove_all_issuable_escalation_statuses_spec.rb b/spec/migrations/20220321234317_remove_all_issuable_escalation_statuses_spec.rb
index 44e20df1130..c645a768969 100644
--- a/spec/migrations/20220321234317_remove_all_issuable_escalation_statuses_spec.rb
+++ b/spec/migrations/20220321234317_remove_all_issuable_escalation_statuses_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe RemoveAllIssuableEscalationStatuses do
+RSpec.describe RemoveAllIssuableEscalationStatuses, feature_category: :incident_management do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:issues) { table(:issues) }
diff --git a/spec/migrations/20220322132242_update_pages_onboarding_state_spec.rb b/spec/migrations/20220322132242_update_pages_onboarding_state_spec.rb
index fbd5fe546fa..6b08b4f853d 100644
--- a/spec/migrations/20220322132242_update_pages_onboarding_state_spec.rb
+++ b/spec/migrations/20220322132242_update_pages_onboarding_state_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe UpdatePagesOnboardingState do
+RSpec.describe UpdatePagesOnboardingState, feature_category: :pages do
let(:migration) { described_class.new }
let!(:namespaces) { table(:namespaces) }
let!(:projects) { table(:projects) }
diff --git a/spec/migrations/20220324032250_migrate_shimo_confluence_service_category_spec.rb b/spec/migrations/20220324032250_migrate_shimo_confluence_service_category_spec.rb
index 38db6d51e7e..15c16a2b232 100644
--- a/spec/migrations/20220324032250_migrate_shimo_confluence_service_category_spec.rb
+++ b/spec/migrations/20220324032250_migrate_shimo_confluence_service_category_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe MigrateShimoConfluenceServiceCategory, :migration do
+RSpec.describe MigrateShimoConfluenceServiceCategory, :migration, feature_category: :integrations do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:integrations) { table(:integrations) }
diff --git a/spec/migrations/20220324165436_schedule_backfill_project_settings_spec.rb b/spec/migrations/20220324165436_schedule_backfill_project_settings_spec.rb
index a8014e73bf0..3fcfb84c214 100644
--- a/spec/migrations/20220324165436_schedule_backfill_project_settings_spec.rb
+++ b/spec/migrations/20220324165436_schedule_backfill_project_settings_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleBackfillProjectSettings do
- let_it_be(:migration) { described_class::MIGRATION }
+RSpec.describe ScheduleBackfillProjectSettings, feature_category: :projects do
+ let!(:migration) { described_class::MIGRATION }
describe '#up' do
it 'schedules background jobs for each batch of projects' do
diff --git a/spec/migrations/20220329175119_remove_leftover_ci_job_artifact_deletions_spec.rb b/spec/migrations/20220329175119_remove_leftover_ci_job_artifact_deletions_spec.rb
index 13884007af2..555856788b7 100644
--- a/spec/migrations/20220329175119_remove_leftover_ci_job_artifact_deletions_spec.rb
+++ b/spec/migrations/20220329175119_remove_leftover_ci_job_artifact_deletions_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe RemoveLeftoverCiJobArtifactDeletions do
+RSpec.describe RemoveLeftoverCiJobArtifactDeletions, feature_category: :sharding do
let(:deleted_records) { table(:loose_foreign_keys_deleted_records) }
target_table_name = Ci::JobArtifact.table_name
diff --git a/spec/migrations/20220331133802_schedule_backfill_topics_title_spec.rb b/spec/migrations/20220331133802_schedule_backfill_topics_title_spec.rb
index 13e8c42269b..b26cd9688ae 100644
--- a/spec/migrations/20220331133802_schedule_backfill_topics_title_spec.rb
+++ b/spec/migrations/20220331133802_schedule_backfill_topics_title_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleBackfillTopicsTitle do
+RSpec.describe ScheduleBackfillTopicsTitle, feature_category: :projects do
let(:topics) { table(:topics) }
let!(:topic1) { topics.create!(name: 'topic1') }
diff --git a/spec/migrations/20220412143552_consume_remaining_encrypt_integration_property_jobs_spec.rb b/spec/migrations/20220412143552_consume_remaining_encrypt_integration_property_jobs_spec.rb
index 4a1b68a5a85..77bf80621c4 100644
--- a/spec/migrations/20220412143552_consume_remaining_encrypt_integration_property_jobs_spec.rb
+++ b/spec/migrations/20220412143552_consume_remaining_encrypt_integration_property_jobs_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe ConsumeRemainingEncryptIntegrationPropertyJobs, :migration do
+RSpec.describe ConsumeRemainingEncryptIntegrationPropertyJobs, :migration, feature_category: :integrations do
subject(:migration) { described_class.new }
let(:integrations) { table(:integrations) }
diff --git a/spec/migrations/20220416054011_schedule_backfill_project_member_namespace_id_spec.rb b/spec/migrations/20220416054011_schedule_backfill_project_member_namespace_id_spec.rb
index 2838fc9387c..c81ecc07779 100644
--- a/spec/migrations/20220416054011_schedule_backfill_project_member_namespace_id_spec.rb
+++ b/spec/migrations/20220416054011_schedule_backfill_project_member_namespace_id_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleBackfillProjectMemberNamespaceId do
- let_it_be(:migration) { described_class::MIGRATION }
+RSpec.describe ScheduleBackfillProjectMemberNamespaceId, feature_category: :subgroups do
+ let!(:migration) { described_class::MIGRATION }
describe '#up' do
it 'schedules background jobs for each batch of project members' do
diff --git a/spec/migrations/20220420135946_update_batched_background_migration_arguments_spec.rb b/spec/migrations/20220420135946_update_batched_background_migration_arguments_spec.rb
index 6dbee483e15..c740c893ad6 100644
--- a/spec/migrations/20220420135946_update_batched_background_migration_arguments_spec.rb
+++ b/spec/migrations/20220420135946_update_batched_background_migration_arguments_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe UpdateBatchedBackgroundMigrationArguments do
+RSpec.describe UpdateBatchedBackgroundMigrationArguments, feature_category: :database do
let(:batched_migrations) { table(:batched_background_migrations) }
before do
diff --git a/spec/migrations/20220426185933_backfill_deployments_finished_at_spec.rb b/spec/migrations/20220426185933_backfill_deployments_finished_at_spec.rb
index c79325c5077..c41e1402bf1 100644
--- a/spec/migrations/20220426185933_backfill_deployments_finished_at_spec.rb
+++ b/spec/migrations/20220426185933_backfill_deployments_finished_at_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe BackfillDeploymentsFinishedAt, :migration do
+RSpec.describe BackfillDeploymentsFinishedAt, :migration, feature_category: :continuous_delivery do
let(:deployments) { table(:deployments) }
let(:namespaces) { table(:namespaces) }
diff --git a/spec/migrations/20220502015011_clean_up_fix_merge_request_diff_commit_users_spec.rb b/spec/migrations/20220502015011_clean_up_fix_merge_request_diff_commit_users_spec.rb
index 2bc3e89a748..e316ad25214 100644
--- a/spec/migrations/20220502015011_clean_up_fix_merge_request_diff_commit_users_spec.rb
+++ b/spec/migrations/20220502015011_clean_up_fix_merge_request_diff_commit_users_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration! 'clean_up_fix_merge_request_diff_commit_users'
-RSpec.describe CleanUpFixMergeRequestDiffCommitUsers, :migration do
+RSpec.describe CleanUpFixMergeRequestDiffCommitUsers, :migration, feature_category: :code_review do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:project_namespace) { namespaces.create!(name: 'project2', path: 'project2', type: 'Project') }
diff --git a/spec/migrations/20220502173045_reset_too_many_tags_skipped_registry_imports_spec.rb b/spec/migrations/20220502173045_reset_too_many_tags_skipped_registry_imports_spec.rb
index cc4041fe151..a65e991d566 100644
--- a/spec/migrations/20220502173045_reset_too_many_tags_skipped_registry_imports_spec.rb
+++ b/spec/migrations/20220502173045_reset_too_many_tags_skipped_registry_imports_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ResetTooManyTagsSkippedRegistryImports, :aggregate_failures do
+RSpec.describe ResetTooManyTagsSkippedRegistryImports, :aggregate_failures, feature_category: :container_registry do
let(:migration) { described_class::MIGRATION }
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
diff --git a/spec/migrations/20220503035221_add_gitlab_schema_to_batched_background_migrations_spec.rb b/spec/migrations/20220503035221_add_gitlab_schema_to_batched_background_migrations_spec.rb
index 5002c665c79..9086700c513 100644
--- a/spec/migrations/20220503035221_add_gitlab_schema_to_batched_background_migrations_spec.rb
+++ b/spec/migrations/20220503035221_add_gitlab_schema_to_batched_background_migrations_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe AddGitlabSchemaToBatchedBackgroundMigrations do
+RSpec.describe AddGitlabSchemaToBatchedBackgroundMigrations, feature_category: :database do
it 'sets gitlab_schema for existing methods to "gitlab_main" and default to NULL' do
batched_migrations = table(:batched_background_migrations)
batched_migration = batched_migrations.create!(
diff --git a/spec/migrations/20220505044348_fix_automatic_iterations_cadences_start_date_spec.rb b/spec/migrations/20220505044348_fix_automatic_iterations_cadences_start_date_spec.rb
index 575157f8331..3a6a8f5dbe5 100644
--- a/spec/migrations/20220505044348_fix_automatic_iterations_cadences_start_date_spec.rb
+++ b/spec/migrations/20220505044348_fix_automatic_iterations_cadences_start_date_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe FixAutomaticIterationsCadencesStartDate do
+RSpec.describe FixAutomaticIterationsCadencesStartDate, feature_category: :team_planning do
let(:migration) { described_class.new }
let(:namespaces) { table(:namespaces) }
let(:sprints) { table(:sprints) }
diff --git a/spec/migrations/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints_spec.rb b/spec/migrations/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints_spec.rb
index ec58a54b085..255d99eb8ca 100644
--- a/spec/migrations/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints_spec.rb
+++ b/spec/migrations/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe UpdateIndexOnAlertsToExcludeNullFingerprints do
+RSpec.describe UpdateIndexOnAlertsToExcludeNullFingerprints, feature_category: :incident_management do
let(:alerts) { 'alert_management_alerts' }
let(:old_index) { described_class::OLD_INDEX_NAME }
let(:new_index) { described_class::NEW_INDEX_NAME }
diff --git a/spec/migrations/20220506154054_create_sync_namespace_details_trigger_spec.rb b/spec/migrations/20220506154054_create_sync_namespace_details_trigger_spec.rb
index 411b1eacb86..3e784761dd4 100644
--- a/spec/migrations/20220506154054_create_sync_namespace_details_trigger_spec.rb
+++ b/spec/migrations/20220506154054_create_sync_namespace_details_trigger_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe CreateSyncNamespaceDetailsTrigger do
+RSpec.describe CreateSyncNamespaceDetailsTrigger, feature_category: :subgroups do
let(:migration) { described_class.new }
let(:namespaces) { table(:namespaces) }
let(:namespace_details) { table(:namespace_details) }
diff --git a/spec/migrations/20220512190659_remove_web_hooks_web_hook_logs_web_hook_id_fk_spec.rb b/spec/migrations/20220512190659_remove_web_hooks_web_hook_logs_web_hook_id_fk_spec.rb
index fa94a73582d..66649eebf70 100644
--- a/spec/migrations/20220512190659_remove_web_hooks_web_hook_logs_web_hook_id_fk_spec.rb
+++ b/spec/migrations/20220512190659_remove_web_hooks_web_hook_logs_web_hook_id_fk_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe RemoveWebHooksWebHookLogsWebHookIdFk do
+RSpec.describe RemoveWebHooksWebHookLogsWebHookIdFk, feature_category: :integrations do
let(:web_hooks) { table(:web_hooks) }
let(:logs) { table(:web_hook_logs) }
diff --git a/spec/migrations/20220513043344_reschedule_expire_o_auth_tokens_spec.rb b/spec/migrations/20220513043344_reschedule_expire_o_auth_tokens_spec.rb
index 63fff279acc..735232dfac7 100644
--- a/spec/migrations/20220513043344_reschedule_expire_o_auth_tokens_spec.rb
+++ b/spec/migrations/20220513043344_reschedule_expire_o_auth_tokens_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe RescheduleExpireOAuthTokens do
- let_it_be(:migration) { described_class::MIGRATION }
+RSpec.describe RescheduleExpireOAuthTokens, feature_category: :authentication_and_authorization do
+ let!(:migration) { described_class::MIGRATION }
describe '#up' do
it 'schedules background jobs for each batch of oauth tokens' do
diff --git a/spec/migrations/20220523171107_drop_deploy_tokens_token_column_spec.rb b/spec/migrations/20220523171107_drop_deploy_tokens_token_column_spec.rb
index 78df6f5fc35..9cbc6dea6a9 100644
--- a/spec/migrations/20220523171107_drop_deploy_tokens_token_column_spec.rb
+++ b/spec/migrations/20220523171107_drop_deploy_tokens_token_column_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe DropDeployTokensTokenColumn do
+RSpec.describe DropDeployTokensTokenColumn, feature_category: :continuous_delivery do
let(:deploy_tokens) { table(:deploy_tokens) }
it 'correctly migrates up and down' do
diff --git a/spec/migrations/20220524074947_finalize_backfill_null_note_discussion_ids_spec.rb b/spec/migrations/20220524074947_finalize_backfill_null_note_discussion_ids_spec.rb
index 74ad4662b3e..9071c61ca0e 100644
--- a/spec/migrations/20220524074947_finalize_backfill_null_note_discussion_ids_spec.rb
+++ b/spec/migrations/20220524074947_finalize_backfill_null_note_discussion_ids_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe FinalizeBackfillNullNoteDiscussionIds, :migration do
+RSpec.describe FinalizeBackfillNullNoteDiscussionIds, :migration, feature_category: :team_planning do
subject(:migration) { described_class.new }
let(:notes) { table(:notes) }
diff --git a/spec/migrations/20220524184149_create_sync_project_namespace_details_trigger_spec.rb b/spec/migrations/20220524184149_create_sync_project_namespace_details_trigger_spec.rb
index f85a59357e1..21fddb08771 100644
--- a/spec/migrations/20220524184149_create_sync_project_namespace_details_trigger_spec.rb
+++ b/spec/migrations/20220524184149_create_sync_project_namespace_details_trigger_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe CreateSyncProjectNamespaceDetailsTrigger do
+RSpec.describe CreateSyncProjectNamespaceDetailsTrigger, feature_category: :projects do
let(:migration) { described_class.new }
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
diff --git a/spec/migrations/20220525221133_schedule_backfill_vulnerability_reads_cluster_agent_spec.rb b/spec/migrations/20220525221133_schedule_backfill_vulnerability_reads_cluster_agent_spec.rb
index 3f1a2d8c4b9..9e414157b3f 100644
--- a/spec/migrations/20220525221133_schedule_backfill_vulnerability_reads_cluster_agent_spec.rb
+++ b/spec/migrations/20220525221133_schedule_backfill_vulnerability_reads_cluster_agent_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleBackfillVulnerabilityReadsClusterAgent do
- let_it_be(:batched_migration) { described_class::MIGRATION_NAME }
+RSpec.describe ScheduleBackfillVulnerabilityReadsClusterAgent, feature_category: :vulnerability_management do
+ let!(:batched_migration) { described_class::MIGRATION_NAME }
it 'schedules background jobs for each batch of vulnerability reads' do
reversible_migration do |migration|
diff --git a/spec/migrations/20220601110011_schedule_remove_self_managed_wiki_notes_spec.rb b/spec/migrations/20220601110011_schedule_remove_self_managed_wiki_notes_spec.rb
index 44e80980b27..63174d054d7 100644
--- a/spec/migrations/20220601110011_schedule_remove_self_managed_wiki_notes_spec.rb
+++ b/spec/migrations/20220601110011_schedule_remove_self_managed_wiki_notes_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleRemoveSelfManagedWikiNotes do
- let_it_be(:batched_migration) { described_class::MIGRATION }
+RSpec.describe ScheduleRemoveSelfManagedWikiNotes, feature_category: :wiki do
+ let!(:batched_migration) { described_class::MIGRATION }
it 'schedules new batched migration' do
reversible_migration do |migration|
diff --git a/spec/migrations/20220601152916_add_user_id_and_ip_address_success_index_to_authentication_events_spec.rb b/spec/migrations/20220601152916_add_user_id_and_ip_address_success_index_to_authentication_events_spec.rb
index 8cb6ab23fef..1b8ec47f61b 100644
--- a/spec/migrations/20220601152916_add_user_id_and_ip_address_success_index_to_authentication_events_spec.rb
+++ b/spec/migrations/20220601152916_add_user_id_and_ip_address_success_index_to_authentication_events_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe AddUserIdAndIpAddressSuccessIndexToAuthenticationEvents do
+RSpec.describe AddUserIdAndIpAddressSuccessIndexToAuthenticationEvents,
+feature_category: :authentication_and_authorization do
let(:db) { described_class.new }
let(:old_index) { described_class::OLD_INDEX_NAME }
let(:new_index) { described_class::NEW_INDEX_NAME }
diff --git a/spec/migrations/20220606080509_fix_incorrect_job_artifacts_expire_at_spec.rb b/spec/migrations/20220606080509_fix_incorrect_job_artifacts_expire_at_spec.rb
index 5921dd64c0e..314385e35da 100644
--- a/spec/migrations/20220606080509_fix_incorrect_job_artifacts_expire_at_spec.rb
+++ b/spec/migrations/20220606080509_fix_incorrect_job_artifacts_expire_at_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe FixIncorrectJobArtifactsExpireAt, migration: :gitlab_ci do
- let_it_be(:batched_migration) { described_class::MIGRATION }
+RSpec.describe FixIncorrectJobArtifactsExpireAt, migration: :gitlab_ci, feature_category: :build_artifacts do
+ let!(:batched_migration) { described_class::MIGRATION }
it 'does not schedule background jobs when Gitlab.com is true' do
allow(Gitlab).to receive(:com?).and_return(true)
diff --git a/spec/migrations/20220606082910_add_tmp_index_for_potentially_misassociated_vulnerability_occurrences_spec.rb b/spec/migrations/20220606082910_add_tmp_index_for_potentially_misassociated_vulnerability_occurrences_spec.rb
index 1450811b3b9..b74e15d804f 100644
--- a/spec/migrations/20220606082910_add_tmp_index_for_potentially_misassociated_vulnerability_occurrences_spec.rb
+++ b/spec/migrations/20220606082910_add_tmp_index_for_potentially_misassociated_vulnerability_occurrences_spec.rb
@@ -4,7 +4,8 @@ require "spec_helper"
require_migration!
-RSpec.describe AddTmpIndexForPotentiallyMisassociatedVulnerabilityOccurrences do
+RSpec.describe AddTmpIndexForPotentiallyMisassociatedVulnerabilityOccurrences,
+feature_category: :vulnerability_management do
let(:async_index) { Gitlab::Database::AsyncIndexes::PostgresAsyncIndex }
let(:index_name) { described_class::INDEX_NAME }
diff --git a/spec/migrations/20220607082910_add_sync_tmp_index_for_potentially_misassociated_vulnerability_occurrences_spec.rb b/spec/migrations/20220607082910_add_sync_tmp_index_for_potentially_misassociated_vulnerability_occurrences_spec.rb
index 68fac1c2221..8d3ef9a46d7 100644
--- a/spec/migrations/20220607082910_add_sync_tmp_index_for_potentially_misassociated_vulnerability_occurrences_spec.rb
+++ b/spec/migrations/20220607082910_add_sync_tmp_index_for_potentially_misassociated_vulnerability_occurrences_spec.rb
@@ -4,7 +4,8 @@ require "spec_helper"
require_migration!
-RSpec.describe AddSyncTmpIndexForPotentiallyMisassociatedVulnerabilityOccurrences do
+RSpec.describe AddSyncTmpIndexForPotentiallyMisassociatedVulnerabilityOccurrences,
+feature_category: :vulnerability_management do
let(:table) { "vulnerability_occurrences" }
let(:index) { described_class::INDEX_NAME }
diff --git a/spec/migrations/20220620132300_update_last_run_date_for_iterations_cadences_spec.rb b/spec/migrations/20220620132300_update_last_run_date_for_iterations_cadences_spec.rb
index d23ca8741a2..5ac4bba4cb5 100644
--- a/spec/migrations/20220620132300_update_last_run_date_for_iterations_cadences_spec.rb
+++ b/spec/migrations/20220620132300_update_last_run_date_for_iterations_cadences_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe UpdateLastRunDateForIterationsCadences, :migration do
+RSpec.describe UpdateLastRunDateForIterationsCadences, :migration, feature_category: :team_planning do
let(:current_date) { Date.parse(ApplicationRecord.connection.execute("SELECT CURRENT_DATE").first["current_date"]) }
let(:namespaces) { table(:namespaces) }
let(:iterations_cadences) { table(:iterations_cadences) }
diff --git a/spec/migrations/20220622080547_backfill_project_statistics_with_container_registry_size_spec.rb b/spec/migrations/20220622080547_backfill_project_statistics_with_container_registry_size_spec.rb
index 52b75f0b8a9..3ca8c1709f3 100644
--- a/spec/migrations/20220622080547_backfill_project_statistics_with_container_registry_size_spec.rb
+++ b/spec/migrations/20220622080547_backfill_project_statistics_with_container_registry_size_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillProjectStatisticsWithContainerRegistrySize do
- let_it_be(:batched_migration) { described_class::MIGRATION_CLASS }
+RSpec.describe BackfillProjectStatisticsWithContainerRegistrySize, feature_category: :container_registry do
+ let!(:batched_migration) { described_class::MIGRATION_CLASS }
it 'does not schedule background jobs when Gitlab.com is false' do
allow(Gitlab).to receive(:com?).and_return(false)
diff --git a/spec/migrations/20220627090231_schedule_disable_legacy_open_source_license_for_inactive_public_projects_spec.rb b/spec/migrations/20220627090231_schedule_disable_legacy_open_source_license_for_inactive_public_projects_spec.rb
index 3e7f2a3457b..edefc378575 100644
--- a/spec/migrations/20220627090231_schedule_disable_legacy_open_source_license_for_inactive_public_projects_spec.rb
+++ b/spec/migrations/20220627090231_schedule_disable_legacy_open_source_license_for_inactive_public_projects_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleDisableLegacyOpenSourceLicenseForInactivePublicProjects do
+RSpec.describe ScheduleDisableLegacyOpenSourceLicenseForInactivePublicProjects, feature_category: :projects do
context 'on gitlab.com' do
let(:migration) { described_class::MIGRATION }
diff --git a/spec/migrations/20220627152642_queue_update_delayed_project_removal_to_null_for_user_namespace_spec.rb b/spec/migrations/20220627152642_queue_update_delayed_project_removal_to_null_for_user_namespace_spec.rb
index 190f1c830ae..fe46d6a8608 100644
--- a/spec/migrations/20220627152642_queue_update_delayed_project_removal_to_null_for_user_namespace_spec.rb
+++ b/spec/migrations/20220627152642_queue_update_delayed_project_removal_to_null_for_user_namespace_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe QueueUpdateDelayedProjectRemovalToNullForUserNamespace do
+RSpec.describe QueueUpdateDelayedProjectRemovalToNullForUserNamespace, feature_category: :subgroups do
let(:migration) { described_class::MIGRATION }
describe '#up' do
diff --git a/spec/migrations/20220628012902_finalise_project_namespace_members_spec.rb b/spec/migrations/20220628012902_finalise_project_namespace_members_spec.rb
index 1f116cf6a7e..55cabc21997 100644
--- a/spec/migrations/20220628012902_finalise_project_namespace_members_spec.rb
+++ b/spec/migrations/20220628012902_finalise_project_namespace_members_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
require_migration!
-RSpec.describe FinaliseProjectNamespaceMembers, :migration do
+RSpec.describe FinaliseProjectNamespaceMembers, :migration, feature_category: :subgroups do
let(:batched_migrations) { table(:batched_background_migrations) }
- let_it_be(:migration) { described_class::MIGRATION }
+ let!(:migration) { described_class::MIGRATION }
describe '#up' do
shared_examples 'finalizes the migration' do
diff --git a/spec/migrations/20220629184402_unset_escalation_policies_for_alert_incidents_spec.rb b/spec/migrations/20220629184402_unset_escalation_policies_for_alert_incidents_spec.rb
index bd821714605..e01cca038ea 100644
--- a/spec/migrations/20220629184402_unset_escalation_policies_for_alert_incidents_spec.rb
+++ b/spec/migrations/20220629184402_unset_escalation_policies_for_alert_incidents_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe UnsetEscalationPoliciesForAlertIncidents do
+RSpec.describe UnsetEscalationPoliciesForAlertIncidents, feature_category: :incident_management do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:issues) { table(:issues) }
@@ -45,7 +45,7 @@ RSpec.describe UnsetEscalationPoliciesForAlertIncidents do
private
def create_issue
- issues.create!(project_id: project.id)
+ issues.create!(project_id: project.id, namespace_id: project.project_namespace_id)
end
def create_status(issue, policy = nil, escalations_started_at = nil)
diff --git a/spec/migrations/20220715163254_update_notes_in_past_spec.rb b/spec/migrations/20220715163254_update_notes_in_past_spec.rb
index 58e6cabc129..6250229a1f9 100644
--- a/spec/migrations/20220715163254_update_notes_in_past_spec.rb
+++ b/spec/migrations/20220715163254_update_notes_in_past_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe UpdateNotesInPast, :migration do
+RSpec.describe UpdateNotesInPast, :migration, feature_category: :team_planning do
let(:notes) { table(:notes) }
it 'updates created_at when it is too much in the past' do
diff --git a/spec/migrations/20220721031446_schedule_disable_legacy_open_source_license_for_one_member_no_repo_projects_spec.rb b/spec/migrations/20220721031446_schedule_disable_legacy_open_source_license_for_one_member_no_repo_projects_spec.rb
index b17a0215f4e..2dff9eb90cd 100644
--- a/spec/migrations/20220721031446_schedule_disable_legacy_open_source_license_for_one_member_no_repo_projects_spec.rb
+++ b/spec/migrations/20220721031446_schedule_disable_legacy_open_source_license_for_one_member_no_repo_projects_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleDisableLegacyOpenSourceLicenseForOneMemberNoRepoProjects do
+RSpec.describe ScheduleDisableLegacyOpenSourceLicenseForOneMemberNoRepoProjects, feature_category: :projects do
context 'when on gitlab.com' do
let(:migration) { described_class::MIGRATION }
diff --git a/spec/migrations/20220722084543_schedule_disable_legacy_open_source_license_for_no_issues_no_repo_projects_spec.rb b/spec/migrations/20220722084543_schedule_disable_legacy_open_source_license_for_no_issues_no_repo_projects_spec.rb
index cb0f941aea1..a994ddad850 100644
--- a/spec/migrations/20220722084543_schedule_disable_legacy_open_source_license_for_no_issues_no_repo_projects_spec.rb
+++ b/spec/migrations/20220722084543_schedule_disable_legacy_open_source_license_for_no_issues_no_repo_projects_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleDisableLegacyOpenSourceLicenseForNoIssuesNoRepoProjects do
+RSpec.describe ScheduleDisableLegacyOpenSourceLicenseForNoIssuesNoRepoProjects, feature_category: :projects do
context 'when on gitlab.com' do
let(:migration) { described_class::MIGRATION }
diff --git a/spec/migrations/20220722110026_reschedule_set_legacy_open_source_license_available_for_non_public_projects_spec.rb b/spec/migrations/20220722110026_reschedule_set_legacy_open_source_license_available_for_non_public_projects_spec.rb
index 99a30c7f2a9..ab246ea1b10 100644
--- a/spec/migrations/20220722110026_reschedule_set_legacy_open_source_license_available_for_non_public_projects_spec.rb
+++ b/spec/migrations/20220722110026_reschedule_set_legacy_open_source_license_available_for_non_public_projects_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe RescheduleSetLegacyOpenSourceLicenseAvailableForNonPublicProjects do
+RSpec.describe RescheduleSetLegacyOpenSourceLicenseAvailableForNonPublicProjects, feature_category: :projects do
context 'when on gitlab.com' do
let(:migration) { described_class::MIGRATION }
diff --git a/spec/migrations/20220725150127_update_jira_tracker_data_deployment_type_based_on_url_spec.rb b/spec/migrations/20220725150127_update_jira_tracker_data_deployment_type_based_on_url_spec.rb
index 2651e46ba53..1bd186a77e7 100644
--- a/spec/migrations/20220725150127_update_jira_tracker_data_deployment_type_based_on_url_spec.rb
+++ b/spec/migrations/20220725150127_update_jira_tracker_data_deployment_type_based_on_url_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe UpdateJiraTrackerDataDeploymentTypeBasedOnUrl, :migration do
+RSpec.describe UpdateJiraTrackerDataDeploymentTypeBasedOnUrl, :migration, feature_category: :integrations do
let(:integrations_table) { table(:integrations) }
let(:service_jira_cloud) { integrations_table.create!(id: 1, type_new: 'JiraService') }
let(:service_jira_server) { integrations_table.create!(id: 2, type_new: 'JiraService') }
diff --git a/spec/migrations/20220801155858_schedule_disable_legacy_open_source_licence_for_recent_public_projects_spec.rb b/spec/migrations/20220801155858_schedule_disable_legacy_open_source_licence_for_recent_public_projects_spec.rb
index fdd97f2d008..f8f1565fe4c 100644
--- a/spec/migrations/20220801155858_schedule_disable_legacy_open_source_licence_for_recent_public_projects_spec.rb
+++ b/spec/migrations/20220801155858_schedule_disable_legacy_open_source_licence_for_recent_public_projects_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleDisableLegacyOpenSourceLicenceForRecentPublicProjects, schema: 20220801155858 do
+RSpec.describe ScheduleDisableLegacyOpenSourceLicenceForRecentPublicProjects, schema: 20220801155858,
+ feature_category: :projects do
context 'when on gitlab.com' do
let(:background_migration) { described_class::MIGRATION }
let(:migration) { described_class.new }
diff --git a/spec/migrations/20220802114351_reschedule_backfill_container_registry_size_into_project_statistics_spec.rb b/spec/migrations/20220802114351_reschedule_backfill_container_registry_size_into_project_statistics_spec.rb
index cc1c1dac4c3..35d0cdfa25e 100644
--- a/spec/migrations/20220802114351_reschedule_backfill_container_registry_size_into_project_statistics_spec.rb
+++ b/spec/migrations/20220802114351_reschedule_backfill_container_registry_size_into_project_statistics_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe RescheduleBackfillContainerRegistrySizeIntoProjectStatistics do
- let_it_be(:batched_migration) { described_class::MIGRATION_CLASS }
+RSpec.describe RescheduleBackfillContainerRegistrySizeIntoProjectStatistics, feature_category: :container_registry do
+ let!(:batched_migration) { described_class::MIGRATION_CLASS }
it 'does not schedule background jobs when Gitlab.com is false' do
allow(Gitlab).to receive(:com?).and_return(false)
diff --git a/spec/migrations/20220802204737_remove_deactivated_user_highest_role_stats_spec.rb b/spec/migrations/20220802204737_remove_deactivated_user_highest_role_stats_spec.rb
index 3ea286ca138..dd77ce503b8 100644
--- a/spec/migrations/20220802204737_remove_deactivated_user_highest_role_stats_spec.rb
+++ b/spec/migrations/20220802204737_remove_deactivated_user_highest_role_stats_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe RemoveDeactivatedUserHighestRoleStats do
+RSpec.describe RemoveDeactivatedUserHighestRoleStats, feature_category: :utilization do
let!(:users) { table(:users) }
let!(:user_highest_roles) { table(:user_highest_roles) }
diff --git a/spec/migrations/20220816163444_update_start_date_for_iterations_cadences_spec.rb b/spec/migrations/20220816163444_update_start_date_for_iterations_cadences_spec.rb
index 5a5e2362a53..25b2b5c2e18 100644
--- a/spec/migrations/20220816163444_update_start_date_for_iterations_cadences_spec.rb
+++ b/spec/migrations/20220816163444_update_start_date_for_iterations_cadences_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe UpdateStartDateForIterationsCadences, :freeze_time do
+RSpec.describe UpdateStartDateForIterationsCadences, :freeze_time, feature_category: :team_planning do
let(:migration) { described_class.new }
let(:namespaces) { table(:namespaces) }
let(:sprints) { table(:sprints) }
diff --git a/spec/migrations/20220819153725_add_vulnerability_advisory_foreign_key_to_sbom_vulnerable_component_versions_spec.rb b/spec/migrations/20220819153725_add_vulnerability_advisory_foreign_key_to_sbom_vulnerable_component_versions_spec.rb
index c53dd9de649..5a61f49485c 100644
--- a/spec/migrations/20220819153725_add_vulnerability_advisory_foreign_key_to_sbom_vulnerable_component_versions_spec.rb
+++ b/spec/migrations/20220819153725_add_vulnerability_advisory_foreign_key_to_sbom_vulnerable_component_versions_spec.rb
@@ -4,7 +4,8 @@ require "spec_helper"
require_migration!
-RSpec.describe AddVulnerabilityAdvisoryForeignKeyToSbomVulnerableComponentVersions do
+RSpec.describe AddVulnerabilityAdvisoryForeignKeyToSbomVulnerableComponentVersions,
+feature_category: :dependency_management do
let(:table) { described_class::SOURCE_TABLE }
let(:column) { described_class::COLUMN }
let(:foreign_key) { -> { described_class.new.foreign_keys_for(table, column).first } }
diff --git a/spec/migrations/20220819162852_add_sbom_component_version_foreign_key_to_sbom_vulnerable_component_versions_spec.rb b/spec/migrations/20220819162852_add_sbom_component_version_foreign_key_to_sbom_vulnerable_component_versions_spec.rb
index b9cb6891681..999c833f9e3 100644
--- a/spec/migrations/20220819162852_add_sbom_component_version_foreign_key_to_sbom_vulnerable_component_versions_spec.rb
+++ b/spec/migrations/20220819162852_add_sbom_component_version_foreign_key_to_sbom_vulnerable_component_versions_spec.rb
@@ -4,7 +4,8 @@ require "spec_helper"
require_migration!
-RSpec.describe AddSbomComponentVersionForeignKeyToSbomVulnerableComponentVersions do
+RSpec.describe AddSbomComponentVersionForeignKeyToSbomVulnerableComponentVersions,
+feature_category: :dependency_management do
let(:table) { described_class::SOURCE_TABLE }
let(:column) { described_class::COLUMN }
let(:foreign_key) { -> { described_class.new.foreign_keys_for(table, column).first } }
diff --git a/spec/migrations/20220906074449_schedule_disable_legacy_open_source_license_for_projects_less_than_one_mb_spec.rb b/spec/migrations/20220906074449_schedule_disable_legacy_open_source_license_for_projects_less_than_one_mb_spec.rb
index e4ac094ab48..852748bcdc1 100644
--- a/spec/migrations/20220906074449_schedule_disable_legacy_open_source_license_for_projects_less_than_one_mb_spec.rb
+++ b/spec/migrations/20220906074449_schedule_disable_legacy_open_source_license_for_projects_less_than_one_mb_spec.rb
@@ -3,9 +3,9 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleDisableLegacyOpenSourceLicenseForProjectsLessThanOneMb do
- let_it_be(:migration) { described_class.new }
- let_it_be(:post_migration) { described_class::MIGRATION }
+RSpec.describe ScheduleDisableLegacyOpenSourceLicenseForProjectsLessThanOneMb, feature_category: :projects do
+ let!(:migration) { described_class.new }
+ let!(:post_migration) { described_class::MIGRATION }
context 'when on gitlab.com' do
before do
diff --git a/spec/migrations/20220913030624_cleanup_attention_request_related_system_notes_spec.rb b/spec/migrations/20220913030624_cleanup_attention_request_related_system_notes_spec.rb
index 7338a6ab9ae..03e53a406ed 100644
--- a/spec/migrations/20220913030624_cleanup_attention_request_related_system_notes_spec.rb
+++ b/spec/migrations/20220913030624_cleanup_attention_request_related_system_notes_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe CleanupAttentionRequestRelatedSystemNotes, :migration do
+RSpec.describe CleanupAttentionRequestRelatedSystemNotes, :migration, feature_category: :team_planning do
let(:notes) { table(:notes) }
let(:system_note_metadata) { table(:system_note_metadata) }
diff --git a/spec/migrations/20220920124709_backfill_internal_on_notes_spec.rb b/spec/migrations/20220920124709_backfill_internal_on_notes_spec.rb
index f4ac6e6fc8e..6e3a058f245 100644
--- a/spec/migrations/20220920124709_backfill_internal_on_notes_spec.rb
+++ b/spec/migrations/20220920124709_backfill_internal_on_notes_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillInternalOnNotes, :migration do
+RSpec.describe BackfillInternalOnNotes, :migration, feature_category: :team_planning do
let(:migration) { described_class::MIGRATION }
describe '#up' do
diff --git a/spec/migrations/20220920180451_schedule_vulnerabilities_feedback_migration_spec.rb b/spec/migrations/20220920180451_schedule_vulnerabilities_feedback_migration_spec.rb
new file mode 100644
index 00000000000..4f2b5f6b50f
--- /dev/null
+++ b/spec/migrations/20220920180451_schedule_vulnerabilities_feedback_migration_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ScheduleVulnerabilitiesFeedbackMigration, feature_category: :vulnerability_management do
+ let(:migration) { described_class::MIGRATION }
+
+ describe '#up' do
+ it 'schedules background jobs for each batch of Vulnerabilities::Feedback' do
+ migrate!
+
+ expect(migration).to have_scheduled_batched_migration(
+ table_name: :vulnerability_feedback,
+ column_name: :id,
+ interval: described_class::DELAY_INTERVAL,
+ batch_size: described_class::BATCH_SIZE,
+ max_batch_size: described_class::MAX_BATCH_SIZE
+ )
+ end
+ end
+
+ describe '#down' do
+ it 'deletes all batched migration records' do
+ migrate!
+ schema_migrate_down!
+
+ expect(migration).not_to have_scheduled_batched_migration
+ end
+ end
+end
diff --git a/spec/migrations/20220921093355_schedule_backfill_namespace_details_spec.rb b/spec/migrations/20220921093355_schedule_backfill_namespace_details_spec.rb
index 61e4af3d10c..5ac49762dbf 100644
--- a/spec/migrations/20220921093355_schedule_backfill_namespace_details_spec.rb
+++ b/spec/migrations/20220921093355_schedule_backfill_namespace_details_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleBackfillNamespaceDetails, schema: 20220921093355 do
+RSpec.describe ScheduleBackfillNamespaceDetails, schema: 20220921093355, feature_category: :subgroups do
context 'when on gitlab.com' do
let(:background_migration) { described_class::MIGRATION }
let(:migration) { described_class.new }
diff --git a/spec/migrations/20220921144258_remove_orphan_group_token_users_spec.rb b/spec/migrations/20220921144258_remove_orphan_group_token_users_spec.rb
index 174cfda1a46..19cf3b2fb69 100644
--- a/spec/migrations/20220921144258_remove_orphan_group_token_users_spec.rb
+++ b/spec/migrations/20220921144258_remove_orphan_group_token_users_spec.rb
@@ -4,7 +4,8 @@ require 'spec_helper'
require_migration!
-RSpec.describe RemoveOrphanGroupTokenUsers, :migration, :sidekiq_inline do
+RSpec.describe RemoveOrphanGroupTokenUsers, :migration, :sidekiq_inline,
+feature_category: :authentication_and_authorization do
subject(:migration) { described_class.new }
let(:users) { table(:users) }
diff --git a/spec/migrations/20220922143143_schedule_reset_duplicate_ci_runners_token_values_spec.rb b/spec/migrations/20220922143143_schedule_reset_duplicate_ci_runners_token_values_spec.rb
index 409f7d544ee..07627725ed0 100644
--- a/spec/migrations/20220922143143_schedule_reset_duplicate_ci_runners_token_values_spec.rb
+++ b/spec/migrations/20220922143143_schedule_reset_duplicate_ci_runners_token_values_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleResetDuplicateCiRunnersTokenValues, migration: :gitlab_ci do
+RSpec.describe ScheduleResetDuplicateCiRunnersTokenValues, feature_category: :runner_fleet, migration: :gitlab_ci do
let(:migration) { described_class::MIGRATION }
describe '#up' do
diff --git a/spec/migrations/20220922143634_schedule_reset_duplicate_ci_runners_token_encrypted_values_spec.rb b/spec/migrations/20220922143634_schedule_reset_duplicate_ci_runners_token_encrypted_values_spec.rb
index 4f3103927d5..42f200e0d6f 100644
--- a/spec/migrations/20220922143634_schedule_reset_duplicate_ci_runners_token_encrypted_values_spec.rb
+++ b/spec/migrations/20220922143634_schedule_reset_duplicate_ci_runners_token_encrypted_values_spec.rb
@@ -3,7 +3,9 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleResetDuplicateCiRunnersTokenEncryptedValues, migration: :gitlab_ci do
+RSpec.describe ScheduleResetDuplicateCiRunnersTokenEncryptedValues,
+ feature_category: :runner_fleet,
+ migration: :gitlab_ci do
let(:migration) { described_class::MIGRATION }
describe '#up' do
diff --git a/spec/migrations/20220928225711_schedule_update_ci_pipeline_artifacts_locked_status_spec.rb b/spec/migrations/20220928225711_schedule_update_ci_pipeline_artifacts_locked_status_spec.rb
index 7e3f8caa966..5c1b5c8f2a7 100644
--- a/spec/migrations/20220928225711_schedule_update_ci_pipeline_artifacts_locked_status_spec.rb
+++ b/spec/migrations/20220928225711_schedule_update_ci_pipeline_artifacts_locked_status_spec.rb
@@ -3,8 +3,9 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleUpdateCiPipelineArtifactsLockedStatus, migration: :gitlab_ci do
- let_it_be(:migration) { described_class::MIGRATION }
+RSpec.describe ScheduleUpdateCiPipelineArtifactsLockedStatus, migration: :gitlab_ci,
+ feature_category: :build_artifacts do
+ let!(:migration) { described_class::MIGRATION }
describe '#up' do
it 'schedules background jobs for each batch of ci_pipeline_artifacts' do
diff --git a/spec/migrations/20220929213730_schedule_delete_orphaned_operational_vulnerabilities_spec.rb b/spec/migrations/20220929213730_schedule_delete_orphaned_operational_vulnerabilities_spec.rb
index 9220b5e8a95..2e391868060 100644
--- a/spec/migrations/20220929213730_schedule_delete_orphaned_operational_vulnerabilities_spec.rb
+++ b/spec/migrations/20220929213730_schedule_delete_orphaned_operational_vulnerabilities_spec.rb
@@ -3,9 +3,9 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleDeleteOrphanedOperationalVulnerabilities do
- let_it_be(:migration) { described_class.new }
- let_it_be(:post_migration) { described_class::MIGRATION }
+RSpec.describe ScheduleDeleteOrphanedOperationalVulnerabilities, feature_category: :vulnerability_management do
+ let!(:migration) { described_class.new }
+ let!(:post_migration) { described_class::MIGRATION }
describe '#up' do
it 'schedules background jobs for each batch of vulnerabilities' do
diff --git a/spec/migrations/20221002234454_finalize_group_member_namespace_id_migration_spec.rb b/spec/migrations/20221002234454_finalize_group_member_namespace_id_migration_spec.rb
index 9c27005065d..4ff16111417 100644
--- a/spec/migrations/20221002234454_finalize_group_member_namespace_id_migration_spec.rb
+++ b/spec/migrations/20221002234454_finalize_group_member_namespace_id_migration_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
require_migration!
-RSpec.describe FinalizeGroupMemberNamespaceIdMigration, :migration do
+RSpec.describe FinalizeGroupMemberNamespaceIdMigration, :migration, feature_category: :subgroups do
let(:batched_migrations) { table(:batched_background_migrations) }
- let_it_be(:migration) { described_class::MIGRATION }
+ let!(:migration) { described_class::MIGRATION }
describe '#up' do
shared_examples 'finalizes the migration' do
diff --git a/spec/migrations/20221004094814_schedule_destroy_invalid_members_spec.rb b/spec/migrations/20221004094814_schedule_destroy_invalid_members_spec.rb
index 73fdfa78eb4..8bffa4b9b99 100644
--- a/spec/migrations/20221004094814_schedule_destroy_invalid_members_spec.rb
+++ b/spec/migrations/20221004094814_schedule_destroy_invalid_members_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleDestroyInvalidMembers, :migration do
- let_it_be(:migration) { described_class::MIGRATION }
+RSpec.describe ScheduleDestroyInvalidMembers, :migration, feature_category: :subgroups do
+ let!(:migration) { described_class::MIGRATION }
describe '#up' do
it 'schedules background jobs for each batch of members' do
diff --git a/spec/migrations/20221008032350_add_password_expiration_migration_spec.rb b/spec/migrations/20221008032350_add_password_expiration_migration_spec.rb
index 05e557f1f52..1663966816c 100644
--- a/spec/migrations/20221008032350_add_password_expiration_migration_spec.rb
+++ b/spec/migrations/20221008032350_add_password_expiration_migration_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe AddPasswordExpirationMigration do
+RSpec.describe AddPasswordExpirationMigration, feature_category: :users do
let(:application_setting) { table(:application_settings).create! }
describe "#up" do
diff --git a/spec/migrations/20221012033107_add_password_last_changed_at_to_user_details_spec.rb b/spec/migrations/20221012033107_add_password_last_changed_at_to_user_details_spec.rb
index 46a7b097d02..e2c508938fd 100644
--- a/spec/migrations/20221012033107_add_password_last_changed_at_to_user_details_spec.rb
+++ b/spec/migrations/20221012033107_add_password_last_changed_at_to_user_details_spec.rb
@@ -4,10 +4,10 @@ require 'spec_helper'
require_migration!
-RSpec.describe AddPasswordLastChangedAtToUserDetails do
- let_it_be(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
- let_it_be(:users) { table(:users) }
- let_it_be(:user) { create_user! }
+RSpec.describe AddPasswordLastChangedAtToUserDetails, feature_category: :users do
+ let!(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
+ let!(:users) { table(:users) }
+ let!(:user) { create_user! }
let(:user_detail) { table(:user_details).create!(user_id: user.id, provisioned_by_group_id: namespace.id) }
describe "#up" do
diff --git a/spec/migrations/20221013154159_update_invalid_dormant_user_setting_spec.rb b/spec/migrations/20221013154159_update_invalid_dormant_user_setting_spec.rb
index eac71e428be..0686d9ca786 100644
--- a/spec/migrations/20221013154159_update_invalid_dormant_user_setting_spec.rb
+++ b/spec/migrations/20221013154159_update_invalid_dormant_user_setting_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe UpdateInvalidDormantUserSetting, :migration do
+RSpec.describe UpdateInvalidDormantUserSetting, :migration, feature_category: :users do
let(:settings) { table(:application_settings) }
context 'with no rows in the application_settings table' do
diff --git a/spec/migrations/20221018050323_add_objective_and_keyresult_to_work_item_types_spec.rb b/spec/migrations/20221018050323_add_objective_and_keyresult_to_work_item_types_spec.rb
index 4de897802b9..3ab33367303 100644
--- a/spec/migrations/20221018050323_add_objective_and_keyresult_to_work_item_types_spec.rb
+++ b/spec/migrations/20221018050323_add_objective_and_keyresult_to_work_item_types_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
require_migration!
-RSpec.describe AddObjectiveAndKeyresultToWorkItemTypes, :migration do
+RSpec.describe AddObjectiveAndKeyresultToWorkItemTypes, :migration, feature_category: :team_planning do
include MigrationHelpers::WorkItemTypesHelper
- let_it_be(:work_item_types) { table(:work_item_types) }
+ let!(:work_item_types) { table(:work_item_types) }
let(:base_types) do
{
diff --git a/spec/migrations/20221018062308_schedule_backfill_project_namespace_details_spec.rb b/spec/migrations/20221018062308_schedule_backfill_project_namespace_details_spec.rb
index 4dd6d5757ce..4175d9b1ad8 100644
--- a/spec/migrations/20221018062308_schedule_backfill_project_namespace_details_spec.rb
+++ b/spec/migrations/20221018062308_schedule_backfill_project_namespace_details_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleBackfillProjectNamespaceDetails, schema: 20221018062308 do
+RSpec.describe ScheduleBackfillProjectNamespaceDetails, schema: 20221018062308, feature_category: :projects do
context 'when on gitlab.com' do
- let_it_be(:background_migration) { described_class::MIGRATION }
- let_it_be(:migration) { described_class.new }
+ let!(:background_migration) { described_class::MIGRATION }
+ let!(:migration) { described_class.new }
before do
migration.up
diff --git a/spec/migrations/20221018095434_schedule_disable_legacy_open_source_license_for_projects_less_than_five_mb_spec.rb b/spec/migrations/20221018095434_schedule_disable_legacy_open_source_license_for_projects_less_than_five_mb_spec.rb
new file mode 100644
index 00000000000..34bba8ed9c8
--- /dev/null
+++ b/spec/migrations/20221018095434_schedule_disable_legacy_open_source_license_for_projects_less_than_five_mb_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ScheduleDisableLegacyOpenSourceLicenseForProjectsLessThanFiveMb, feature_category: :projects do
+ let!(:migration) { described_class.new }
+ let!(:post_migration) { described_class::MIGRATION }
+
+ context 'when on gitlab.com' do
+ before do
+ allow(Gitlab).to receive(:com?).and_return(true)
+ end
+
+ describe '#up' do
+ it 'schedules background jobs for each batch of project_settings' do
+ migration.up
+
+ expect(post_migration).to(
+ have_scheduled_batched_migration(
+ table_name: :project_settings,
+ column_name: :project_id,
+ interval: described_class::INTERVAL,
+ batch_size: described_class::BATCH_SIZE,
+ max_batch_size: described_class::MAX_BATCH_SIZE,
+ sub_batch_size: described_class::SUB_BATCH_SIZE
+ )
+ )
+ end
+ end
+
+ describe '#down' do
+ it 'deletes all batched migration records' do
+ migration.down
+
+ expect(post_migration).not_to have_scheduled_batched_migration
+ end
+ end
+ end
+
+ context 'when on self-managed instance' do
+ before do
+ allow(Gitlab).to receive(:com?).and_return(false)
+ end
+
+ describe '#up' do
+ it 'does not schedule background job' do
+ expect(migration).not_to receive(:queue_batched_background_migration)
+
+ migration.up
+ end
+ end
+
+ describe '#down' do
+ it 'does not delete background job' do
+ expect(migration).not_to receive(:delete_batched_background_migration)
+
+ migration.down
+ end
+ end
+ end
+end
diff --git a/spec/migrations/20221018193635_ensure_task_note_renaming_background_migration_finished_spec.rb b/spec/migrations/20221018193635_ensure_task_note_renaming_background_migration_finished_spec.rb
index ea95c34674e..8b599881359 100644
--- a/spec/migrations/20221018193635_ensure_task_note_renaming_background_migration_finished_spec.rb
+++ b/spec/migrations/20221018193635_ensure_task_note_renaming_background_migration_finished_spec.rb
@@ -3,12 +3,12 @@
require 'spec_helper'
require_migration!
-RSpec.describe EnsureTaskNoteRenamingBackgroundMigrationFinished, :migration do
+RSpec.describe EnsureTaskNoteRenamingBackgroundMigrationFinished, :migration, feature_category: :team_planning do
let(:batched_migrations) { table(:batched_background_migrations) }
let(:batch_failed_status) { 2 }
let(:batch_finalized_status) { 3 }
- let_it_be(:migration) { described_class::MIGRATION }
+ let!(:migration) { described_class::MIGRATION }
describe '#up' do
shared_examples 'finalizes the migration' do
diff --git a/spec/migrations/20221021145820_create_routing_table_for_builds_metadata_v2_spec.rb b/spec/migrations/20221021145820_create_routing_table_for_builds_metadata_v2_spec.rb
index 48a00df430d..235351956c4 100644
--- a/spec/migrations/20221021145820_create_routing_table_for_builds_metadata_v2_spec.rb
+++ b/spec/migrations/20221021145820_create_routing_table_for_builds_metadata_v2_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe CreateRoutingTableForBuildsMetadataV2, :migration do
- let_it_be(:migration) { described_class.new }
+RSpec.describe CreateRoutingTableForBuildsMetadataV2, :migration, feature_category: :continuous_integration do
+ let!(:migration) { described_class.new }
describe '#up' do
context 'when the table is already partitioned' do
diff --git a/spec/migrations/20221025043930_change_default_value_on_password_last_changed_at_to_user_details_spec.rb b/spec/migrations/20221025043930_change_default_value_on_password_last_changed_at_to_user_details_spec.rb
index 4d6f06eb146..d6acf31fdc6 100644
--- a/spec/migrations/20221025043930_change_default_value_on_password_last_changed_at_to_user_details_spec.rb
+++ b/spec/migrations/20221025043930_change_default_value_on_password_last_changed_at_to_user_details_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ChangeDefaultValueOnPasswordLastChangedAtToUserDetails, :migration do
+RSpec.describe ChangeDefaultValueOnPasswordLastChangedAtToUserDetails, :migration, feature_category: :users do
let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
let(:users) { table(:users) }
let(:user_details) { table(:user_details) }
diff --git a/spec/migrations/20221028022627_add_index_on_password_last_changed_at_to_user_details_spec.rb b/spec/migrations/20221028022627_add_index_on_password_last_changed_at_to_user_details_spec.rb
index 5f8467f9307..6b6fb553b1f 100644
--- a/spec/migrations/20221028022627_add_index_on_password_last_changed_at_to_user_details_spec.rb
+++ b/spec/migrations/20221028022627_add_index_on_password_last_changed_at_to_user_details_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe AddIndexOnPasswordLastChangedAtToUserDetails, :migration do
+RSpec.describe AddIndexOnPasswordLastChangedAtToUserDetails, :migration, feature_category: :users do
let(:index_name) { 'index_user_details_on_password_last_changed_at' }
it 'correctly migrates up and down' do
diff --git a/spec/migrations/20221101032521_add_default_preferred_language_to_application_settings_spec.rb b/spec/migrations/20221101032521_add_default_preferred_language_to_application_settings_spec.rb
index 3ae4287f3c4..deca498146b 100644
--- a/spec/migrations/20221101032521_add_default_preferred_language_to_application_settings_spec.rb
+++ b/spec/migrations/20221101032521_add_default_preferred_language_to_application_settings_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe AddDefaultPreferredLanguageToApplicationSettings do
+RSpec.describe AddDefaultPreferredLanguageToApplicationSettings, feature_category: :internationalization do
let(:application_setting) { table(:application_settings).create! }
describe "#up" do
diff --git a/spec/migrations/20221101032600_add_text_limit_to_default_preferred_language_on_application_settings_spec.rb b/spec/migrations/20221101032600_add_text_limit_to_default_preferred_language_on_application_settings_spec.rb
index e0370e48db6..3e36e99a0ca 100644
--- a/spec/migrations/20221101032600_add_text_limit_to_default_preferred_language_on_application_settings_spec.rb
+++ b/spec/migrations/20221101032600_add_text_limit_to_default_preferred_language_on_application_settings_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe AddTextLimitToDefaultPreferredLanguageOnApplicationSettings do
+RSpec.describe AddTextLimitToDefaultPreferredLanguageOnApplicationSettings, feature_category: :internationalization do
let(:application_setting) { table(:application_settings).create! }
let(:too_long_text) { SecureRandom.alphanumeric(described_class::MAXIMUM_LIMIT + 1) }
diff --git a/spec/migrations/20221102090940_create_next_ci_partitions_record_spec.rb b/spec/migrations/20221102090940_create_next_ci_partitions_record_spec.rb
index c55e4bcfba7..dc6f365fe2b 100644
--- a/spec/migrations/20221102090940_create_next_ci_partitions_record_spec.rb
+++ b/spec/migrations/20221102090940_create_next_ci_partitions_record_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe CreateNextCiPartitionsRecord, migration: :gitlab_ci do
+RSpec.describe CreateNextCiPartitionsRecord, migration: :gitlab_ci, feature_category: :continuous_integration do
let(:migration) { described_class.new }
let(:partitions) { table(:ci_partitions) }
diff --git a/spec/migrations/20221102090943_create_second_partition_for_builds_metadata_spec.rb b/spec/migrations/20221102090943_create_second_partition_for_builds_metadata_spec.rb
index 99754d609ed..b4bd5136383 100644
--- a/spec/migrations/20221102090943_create_second_partition_for_builds_metadata_spec.rb
+++ b/spec/migrations/20221102090943_create_second_partition_for_builds_metadata_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe CreateSecondPartitionForBuildsMetadata, :migration do
+RSpec.describe CreateSecondPartitionForBuildsMetadata, :migration, feature_category: :continuous_integration do
let(:migration) { described_class.new }
let(:partitions) { table(:ci_partitions) }
diff --git a/spec/migrations/20221104115712_backfill_project_statistics_storage_size_without_uploads_size_spec.rb b/spec/migrations/20221104115712_backfill_project_statistics_storage_size_without_uploads_size_spec.rb
new file mode 100644
index 00000000000..d86720365c4
--- /dev/null
+++ b/spec/migrations/20221104115712_backfill_project_statistics_storage_size_without_uploads_size_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe BackfillProjectStatisticsStorageSizeWithoutUploadsSize,
+ feature_category: :subscription_cost_management do
+ let!(:batched_migration) { described_class::MIGRATION_CLASS }
+
+ it 'does not schedule background jobs when Gitlab.org_or_com? is false' do
+ allow(Gitlab).to receive(:dev_or_test_env?).and_return(false)
+ allow(Gitlab).to receive(:org_or_com?).and_return(false)
+
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+
+ migration.after -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+ end
+ end
+
+ it 'schedules background jobs for each batch of project_statistics' do
+ allow(Gitlab).to receive(:dev_or_test_env?).and_return(false)
+ allow(Gitlab).to receive(:org_or_com?).and_return(true)
+
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+
+ migration.after -> {
+ expect(batched_migration).to have_scheduled_batched_migration(
+ table_name: :project_statistics,
+ column_name: :project_id,
+ interval: described_class::DELAY_INTERVAL
+ )
+ }
+ end
+ end
+end
diff --git a/spec/migrations/20221110152133_delete_orphans_approval_rules_spec.rb b/spec/migrations/20221110152133_delete_orphans_approval_rules_spec.rb
new file mode 100644
index 00000000000..3efee67f7af
--- /dev/null
+++ b/spec/migrations/20221110152133_delete_orphans_approval_rules_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe DeleteOrphansApprovalRules, feature_category: :source_code_management do
+ describe '#up' do
+ it 'schedules background migration for both levels of approval rules' do
+ migrate!
+
+ expect(described_class::MERGE_REQUEST_MIGRATION).to have_scheduled_batched_migration(
+ table_name: :approval_merge_request_rules,
+ column_name: :id,
+ interval: described_class::INTERVAL)
+
+ expect(described_class::PROJECT_MIGRATION).to have_scheduled_batched_migration(
+ table_name: :approval_project_rules,
+ column_name: :id,
+ interval: described_class::INTERVAL)
+ end
+ end
+end
diff --git a/spec/migrations/20221115173607_ensure_work_item_type_backfill_migration_finished_spec.rb b/spec/migrations/20221115173607_ensure_work_item_type_backfill_migration_finished_spec.rb
new file mode 100644
index 00000000000..e9250625832
--- /dev/null
+++ b/spec/migrations/20221115173607_ensure_work_item_type_backfill_migration_finished_spec.rb
@@ -0,0 +1,102 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe EnsureWorkItemTypeBackfillMigrationFinished, :migration, feature_category: :team_planning do
+ let(:batched_migrations) { table(:batched_background_migrations) }
+ let(:work_item_types) { table(:work_item_types) }
+ let(:batch_failed_status) { 2 }
+ let(:batch_finalized_status) { 3 }
+
+ let!(:migration_class) { described_class::MIGRATION }
+
+ describe '#up', :redis do
+ context 'when migration is missing' do
+ it 'warns migration not found' do
+ expect(Gitlab::AppLogger)
+ .to receive(:warn).with(/Could not find batched background migration for the given configuration:/)
+ .exactly(5).times
+
+ migrate!
+ end
+ end
+
+ context 'with migration present' do
+ let(:relevant_types) do
+ {
+ issue: 0,
+ incident: 1,
+ test_case: 2,
+ requirement: 3,
+ task: 4
+ }
+ end
+
+ let!(:backfill_migrations) do
+ relevant_types.map do |_base_type, enum_value|
+ type_id = work_item_types.find_by!(namespace_id: nil, base_type: enum_value).id
+
+ create_migration_with(status, enum_value, type_id)
+ end
+ end
+
+ context 'when migrations have finished' do
+ let(:status) { 3 } # finished enum value
+
+ it 'does not raise an error' do
+ expect { migrate! }.not_to raise_error
+ end
+ end
+
+ context 'with different migration statuses' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:status, :description) do
+ 0 | 'paused'
+ 1 | 'active'
+ 4 | 'failed'
+ 5 | 'finalizing'
+ end
+
+ with_them do
+ it 'finalizes the migration' do
+ expect do
+ migrate!
+
+ backfill_migrations.each(&:reload)
+ end.to change { backfill_migrations.map(&:status).uniq }.from([status]).to([3])
+ end
+ end
+ end
+ end
+ end
+
+ def create_migration_with(status, base_type, type_id)
+ migration = batched_migrations.create!(
+ job_class_name: migration_class,
+ table_name: :issues,
+ column_name: :id,
+ job_arguments: [base_type, type_id],
+ interval: 2.minutes,
+ min_value: 1,
+ max_value: 2,
+ batch_size: 1000,
+ sub_batch_size: 200,
+ gitlab_schema: :gitlab_main,
+ status: status
+ )
+
+ table(:batched_background_migration_jobs).create!(
+ batched_background_migration_id: migration.id,
+ status: batch_failed_status,
+ min_value: 1,
+ max_value: 10,
+ attempts: 2,
+ batch_size: 100,
+ sub_batch_size: 10
+ )
+
+ migration
+ end
+end
diff --git a/spec/migrations/20221122132812_schedule_prune_stale_project_export_jobs_spec.rb b/spec/migrations/20221122132812_schedule_prune_stale_project_export_jobs_spec.rb
new file mode 100644
index 00000000000..5a5bc42a37b
--- /dev/null
+++ b/spec/migrations/20221122132812_schedule_prune_stale_project_export_jobs_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe SchedulePruneStaleProjectExportJobs, category: :importers do
+ let!(:batched_migration) { described_class::MIGRATION }
+
+ it 'schedules a new batched migration' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+
+ migration.after -> {
+ expect(batched_migration).to have_scheduled_batched_migration(
+ table_name: :project_export_jobs,
+ column_name: :id,
+ interval: described_class::DELAY_INTERVAL
+ )
+ }
+ end
+ end
+end
diff --git a/spec/migrations/20221123133054_queue_reset_status_on_container_repositories_spec.rb b/spec/migrations/20221123133054_queue_reset_status_on_container_repositories_spec.rb
new file mode 100644
index 00000000000..2951b738243
--- /dev/null
+++ b/spec/migrations/20221123133054_queue_reset_status_on_container_repositories_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe QueueResetStatusOnContainerRepositories, feature_category: :container_registry do
+ let!(:batched_migration) { described_class::MIGRATION }
+
+ before do
+ stub_container_registry_config(
+ enabled: true,
+ api_url: 'http://example.com',
+ key: 'spec/fixtures/x509_certificate_pk.key'
+ )
+ end
+
+ it 'schedules a new batched migration' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+
+ migration.after -> {
+ expect(batched_migration).to have_scheduled_batched_migration(
+ table_name: :container_repositories,
+ column_name: :id,
+ interval: described_class::DELAY_INTERVAL,
+ sub_batch_size: described_class::BATCH_SIZE
+ )
+ }
+ end
+ end
+
+ context 'with the container registry disabled' do
+ before do
+ allow(::Gitlab.config.registry).to receive(:enabled).and_return(false)
+ end
+
+ it 'does not schedule a new batched migration' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+
+ migration.after -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+ end
+ end
+ end
+end
diff --git a/spec/migrations/20221205151917_schedule_backfill_environment_tier_spec.rb b/spec/migrations/20221205151917_schedule_backfill_environment_tier_spec.rb
new file mode 100644
index 00000000000..b76f889d743
--- /dev/null
+++ b/spec/migrations/20221205151917_schedule_backfill_environment_tier_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ScheduleBackfillEnvironmentTier, category: :continuous_delivery do
+ let!(:batched_migration) { described_class::MIGRATION }
+
+ it 'schedules a new batched migration' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+
+ migration.after -> {
+ expect(batched_migration).to have_scheduled_batched_migration(
+ table_name: :environments,
+ column_name: :id,
+ interval: described_class::DELAY_INTERVAL
+ )
+ }
+ end
+ end
+end
diff --git a/spec/migrations/20221209110934_update_import_sources_on_application_settings_spec.rb b/spec/migrations/20221209110934_update_import_sources_on_application_settings_spec.rb
new file mode 100644
index 00000000000..899074399a1
--- /dev/null
+++ b/spec/migrations/20221209110934_update_import_sources_on_application_settings_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe UpdateImportSourcesOnApplicationSettings, feature_category: :migration do
+ let(:settings) { table(:application_settings) }
+ let(:import_sources_with_google) { %w[google_code github git bitbucket bitbucket_server] }
+ let(:import_sources_without_google) { %w[github git bitbucket bitbucket_server] }
+
+ describe "#up" do
+ it 'removes google_code and preserves existing valid import sources' do
+ record = settings.create!(import_sources: import_sources_with_google.to_yaml)
+
+ migrate!
+
+ expect(record.reload.import_sources).to start_with('---')
+ expect(ApplicationSetting.last.import_sources).to eq(import_sources_without_google)
+ end
+ end
+end
diff --git a/spec/migrations/20221209110935_fix_update_import_sources_on_application_settings_spec.rb b/spec/migrations/20221209110935_fix_update_import_sources_on_application_settings_spec.rb
new file mode 100644
index 00000000000..e5b20b2d48a
--- /dev/null
+++ b/spec/migrations/20221209110935_fix_update_import_sources_on_application_settings_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe FixUpdateImportSourcesOnApplicationSettings, feature_category: :migration do
+ let(:settings) { table(:application_settings) }
+ let(:import_sources) { %w[github git bitbucket bitbucket_server] }
+
+ describe "#up" do
+ shared_examples 'fixes import_sources on application_settings' do
+ it 'ensures YAML is stored' do
+ record = settings.create!(import_sources: data)
+
+ migrate!
+
+ expect(record.reload.import_sources).to start_with('---')
+ expect(ApplicationSetting.last.import_sources).to eq(import_sources)
+ end
+ end
+
+ context 'when import_sources is a String' do
+ let(:data) { import_sources.to_s }
+
+ it_behaves_like 'fixes import_sources on application_settings'
+ end
+
+ context 'when import_sources is already YAML' do
+ let(:data) { import_sources.to_yaml }
+
+ it_behaves_like 'fixes import_sources on application_settings'
+ end
+ end
+end
diff --git a/spec/migrations/20221210154044_update_active_billable_users_index_spec.rb b/spec/migrations/20221210154044_update_active_billable_users_index_spec.rb
new file mode 100644
index 00000000000..3341df2ce51
--- /dev/null
+++ b/spec/migrations/20221210154044_update_active_billable_users_index_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe UpdateActiveBillableUsersIndex, feature_category: :database do
+ let(:db) { described_class.new }
+ let(:table_name) { described_class::TABLE_NAME }
+ let(:old_index_name) { described_class::OLD_INDEX_NAME }
+ let(:new_index_name) { described_class::NEW_INDEX_NAME }
+ let(:old_filter_condition) { "(user_type <> ALL ('{2,6,1,3,7,8}'::smallint[])))" }
+ let(:new_filter_condition) { "(user_type <> ALL ('{1,2,3,4,5,6,7,8,9,11}'::smallint[])))" }
+
+ it 'correctly migrates up and down' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(subject.index_exists_by_name?(table_name, new_index_name)).to be_falsy
+ expect(subject.index_exists_by_name?(table_name, old_index_name)).to be_truthy
+ expect(db.connection.indexes(table_name).find do |i|
+ i.name == old_index_name
+ end.where).to include(old_filter_condition)
+ }
+
+ migration.after -> {
+ expect(subject.index_exists_by_name?(table_name, old_index_name)).to be_falsy
+ expect(subject.index_exists_by_name?(table_name, new_index_name)).to be_truthy
+ expect(db.connection.indexes(table_name).find do |i|
+ i.name == new_index_name
+ end.where).to include(new_filter_condition)
+ }
+ end
+ end
+end
diff --git a/spec/migrations/active_record/schema_spec.rb b/spec/migrations/active_record/schema_spec.rb
index 042b5710dce..f3adffb9a37 100644
--- a/spec/migrations/active_record/schema_spec.rb
+++ b/spec/migrations/active_record/schema_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
# Check consistency of db/structure.sql version, migrations' timestamps, and the latest migration timestamp
# stored in the database's schema_migrations table.
-RSpec.describe ActiveRecord::Schema, schema: :latest do
+RSpec.describe ActiveRecord::Schema, schema: :latest, feature_category: :database do
let(:all_migrations) do
migrations_directories = Rails.application.paths["db/migrate"].paths.map(&:to_s)
migrations_paths = migrations_directories.map { |path| File.join(path, '*') }
diff --git a/spec/migrations/add_default_project_approval_rules_vuln_allowed_spec.rb b/spec/migrations/add_default_project_approval_rules_vuln_allowed_spec.rb
index 057e95eb158..a6c892db131 100644
--- a/spec/migrations/add_default_project_approval_rules_vuln_allowed_spec.rb
+++ b/spec/migrations/add_default_project_approval_rules_vuln_allowed_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe AddDefaultProjectApprovalRulesVulnAllowed do
+RSpec.describe AddDefaultProjectApprovalRulesVulnAllowed, feature_category: :source_code_management do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:namespace) { namespaces.create!(name: 'namespace', path: 'namespace') }
diff --git a/spec/migrations/add_epics_relative_position_spec.rb b/spec/migrations/add_epics_relative_position_spec.rb
index f3b7dd1727b..bdfaacc2bf8 100644
--- a/spec/migrations/add_epics_relative_position_spec.rb
+++ b/spec/migrations/add_epics_relative_position_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe AddEpicsRelativePosition, :migration do
+RSpec.describe AddEpicsRelativePosition, :migration, feature_category: :portfolio_management do
let(:groups) { table(:namespaces) }
let(:epics) { table(:epics) }
let(:users) { table(:users) }
diff --git a/spec/migrations/add_new_trail_plans_spec.rb b/spec/migrations/add_new_trail_plans_spec.rb
index c1b488e8c3c..6f8de8435c6 100644
--- a/spec/migrations/add_new_trail_plans_spec.rb
+++ b/spec/migrations/add_new_trail_plans_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe AddNewTrailPlans, :migration do
+RSpec.describe AddNewTrailPlans, :migration, feature_category: :purchase do
describe '#up' do
before do
allow(Gitlab).to receive(:com?).and_return true
diff --git a/spec/migrations/add_okr_hierarchy_restrictions_spec.rb b/spec/migrations/add_okr_hierarchy_restrictions_spec.rb
new file mode 100644
index 00000000000..ace581c7e3c
--- /dev/null
+++ b/spec/migrations/add_okr_hierarchy_restrictions_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe AddOkrHierarchyRestrictions, :migration, feature_category: :portfolio_management do
+ include MigrationHelpers::WorkItemTypesHelper
+
+ let!(:restrictions) { table(:work_item_hierarchy_restrictions) }
+ let!(:work_item_types) { table(:work_item_types) }
+
+ it 'creates default restrictions' do
+ restrictions.delete_all
+
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(restrictions.count).to eq(0)
+ }
+
+ migration.after -> {
+ expect(restrictions.count).to eq(4)
+ }
+ end
+ end
+
+ context 'when work items are missing' do
+ before do
+ work_item_types.delete_all
+ end
+
+ it 'does nothing' do
+ expect { migrate! }.not_to change { restrictions.count }
+ end
+ end
+end
diff --git a/spec/migrations/add_open_source_plan_spec.rb b/spec/migrations/add_open_source_plan_spec.rb
index 6e1cd544141..f5d68f455e6 100644
--- a/spec/migrations/add_open_source_plan_spec.rb
+++ b/spec/migrations/add_open_source_plan_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe AddOpenSourcePlan, :migration do
+RSpec.describe AddOpenSourcePlan, :migration, feature_category: :purchase do
describe '#up' do
before do
allow(Gitlab).to receive(:com?).and_return true
diff --git a/spec/migrations/add_premium_and_ultimate_plan_limits_spec.rb b/spec/migrations/add_premium_and_ultimate_plan_limits_spec.rb
index 0ae4559ca9f..670541128a0 100644
--- a/spec/migrations/add_premium_and_ultimate_plan_limits_spec.rb
+++ b/spec/migrations/add_premium_and_ultimate_plan_limits_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe AddPremiumAndUltimatePlanLimits, :migration do
+RSpec.describe AddPremiumAndUltimatePlanLimits, :migration, feature_category: :purchase do
shared_examples_for 'a migration that does not alter plans or plan limits' do
it do
expect { migrate! }.not_to change {
diff --git a/spec/migrations/add_triggers_to_integrations_type_new_spec.rb b/spec/migrations/add_triggers_to_integrations_type_new_spec.rb
index 01af5884170..4fa5fe31d2b 100644
--- a/spec/migrations/add_triggers_to_integrations_type_new_spec.rb
+++ b/spec/migrations/add_triggers_to_integrations_type_new_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe AddTriggersToIntegrationsTypeNew do
+RSpec.describe AddTriggersToIntegrationsTypeNew, feature_category: :purchase do
let(:migration) { described_class.new }
let(:integrations) { table(:integrations) }
diff --git a/spec/migrations/add_upvotes_count_index_to_issues_spec.rb b/spec/migrations/add_upvotes_count_index_to_issues_spec.rb
index c04cb98a107..0012b8a0b96 100644
--- a/spec/migrations/add_upvotes_count_index_to_issues_spec.rb
+++ b/spec/migrations/add_upvotes_count_index_to_issues_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe AddUpvotesCountIndexToIssues do
+RSpec.describe AddUpvotesCountIndexToIssues, feature_category: :team_planning do
let(:migration_instance) { described_class.new }
describe '#up' do
diff --git a/spec/migrations/add_web_hook_calls_to_plan_limits_paid_tiers_spec.rb b/spec/migrations/add_web_hook_calls_to_plan_limits_paid_tiers_spec.rb
index 63ad9367503..0ad99be1c7b 100644
--- a/spec/migrations/add_web_hook_calls_to_plan_limits_paid_tiers_spec.rb
+++ b/spec/migrations/add_web_hook_calls_to_plan_limits_paid_tiers_spec.rb
@@ -3,9 +3,9 @@
require 'spec_helper'
require_migration!
-RSpec.describe AddWebHookCallsToPlanLimitsPaidTiers do
- let_it_be(:plans) { table(:plans) }
- let_it_be(:plan_limits) { table(:plan_limits) }
+RSpec.describe AddWebHookCallsToPlanLimitsPaidTiers, feature_category: :purchase do
+ let!(:plans) { table(:plans) }
+ let!(:plan_limits) { table(:plan_limits) }
context 'when on Gitlab.com' do
let(:free_plan) { plans.create!(name: 'free') }
diff --git a/spec/migrations/adjust_task_note_rename_background_migration_values_spec.rb b/spec/migrations/adjust_task_note_rename_background_migration_values_spec.rb
index 422d0655e36..01680fa12cc 100644
--- a/spec/migrations/adjust_task_note_rename_background_migration_values_spec.rb
+++ b/spec/migrations/adjust_task_note_rename_background_migration_values_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe AdjustTaskNoteRenameBackgroundMigrationValues, :migration do
+RSpec.describe AdjustTaskNoteRenameBackgroundMigrationValues, :migration, feature_category: :team_planning do
let(:finished_status) { 3 }
let(:failed_status) { described_class::MIGRATION_FAILED_STATUS }
let(:active_status) { described_class::MIGRATION_ACTIVE_STATUS }
diff --git a/spec/migrations/associate_existing_dast_builds_with_variables_spec.rb b/spec/migrations/associate_existing_dast_builds_with_variables_spec.rb
index dd86989912f..67d215c781b 100644
--- a/spec/migrations/associate_existing_dast_builds_with_variables_spec.rb
+++ b/spec/migrations/associate_existing_dast_builds_with_variables_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe AssociateExistingDastBuildsWithVariables do
+RSpec.describe AssociateExistingDastBuildsWithVariables, feature_category: :dynamic_application_security_testing do
it 'is a no-op' do
migrate!
end
diff --git a/spec/migrations/backfill_all_project_namespaces_spec.rb b/spec/migrations/backfill_all_project_namespaces_spec.rb
index 1bcaad783b2..52fa46eea57 100644
--- a/spec/migrations/backfill_all_project_namespaces_spec.rb
+++ b/spec/migrations/backfill_all_project_namespaces_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillAllProjectNamespaces, :migration do
- let_it_be(:migration) { described_class::MIGRATION }
+RSpec.describe BackfillAllProjectNamespaces, :migration, feature_category: :subgroups do
+ let!(:migration) { described_class::MIGRATION }
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
diff --git a/spec/migrations/backfill_cadence_id_for_boards_scoped_to_iteration_spec.rb b/spec/migrations/backfill_cadence_id_for_boards_scoped_to_iteration_spec.rb
index 16a08ec47c4..a9500b9f942 100644
--- a/spec/migrations/backfill_cadence_id_for_boards_scoped_to_iteration_spec.rb
+++ b/spec/migrations/backfill_cadence_id_for_boards_scoped_to_iteration_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillCadenceIdForBoardsScopedToIteration, :migration do
+RSpec.describe BackfillCadenceIdForBoardsScopedToIteration, :migration, feature_category: :team_planning do
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
let(:iterations_cadences) { table(:iterations_cadences) }
diff --git a/spec/migrations/backfill_clusters_integration_prometheus_enabled_spec.rb b/spec/migrations/backfill_clusters_integration_prometheus_enabled_spec.rb
index 6c116120f05..1c7745a64ef 100644
--- a/spec/migrations/backfill_clusters_integration_prometheus_enabled_spec.rb
+++ b/spec/migrations/backfill_clusters_integration_prometheus_enabled_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillClustersIntegrationPrometheusEnabled, :migration do
+RSpec.describe BackfillClustersIntegrationPrometheusEnabled, :migration, feature_category: :clusters_applications_prometheus do
def create_cluster!(label = rand(2**64).to_s)
table(:clusters).create!(
name: "cluster: #{label}",
diff --git a/spec/migrations/backfill_cycle_analytics_aggregations_spec.rb b/spec/migrations/backfill_cycle_analytics_aggregations_spec.rb
index 2a5d33742ce..47950f918c3 100644
--- a/spec/migrations/backfill_cycle_analytics_aggregations_spec.rb
+++ b/spec/migrations/backfill_cycle_analytics_aggregations_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillCycleAnalyticsAggregations, :migration do
+RSpec.describe BackfillCycleAnalyticsAggregations, :migration, feature_category: :value_stream_management do
let(:migration) { described_class.new }
let(:aggregations) { table(:analytics_cycle_analytics_aggregations) }
diff --git a/spec/migrations/backfill_epic_cache_counts_spec.rb b/spec/migrations/backfill_epic_cache_counts_spec.rb
index 6084fdad0a6..1dc0079bb01 100644
--- a/spec/migrations/backfill_epic_cache_counts_spec.rb
+++ b/spec/migrations/backfill_epic_cache_counts_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillEpicCacheCounts, :migration do
+RSpec.describe BackfillEpicCacheCounts, :migration, feature_category: :portfolio_management do
let(:migration) { described_class::MIGRATION }
describe '#up' do
diff --git a/spec/migrations/backfill_escalation_policies_for_oncall_schedules_spec.rb b/spec/migrations/backfill_escalation_policies_for_oncall_schedules_spec.rb
index 87855285203..aa77a5c228a 100644
--- a/spec/migrations/backfill_escalation_policies_for_oncall_schedules_spec.rb
+++ b/spec/migrations/backfill_escalation_policies_for_oncall_schedules_spec.rb
@@ -3,48 +3,48 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillEscalationPoliciesForOncallSchedules do
- let_it_be(:projects) { table(:projects) }
- let_it_be(:schedules) { table(:incident_management_oncall_schedules) }
- let_it_be(:policies) { table(:incident_management_escalation_policies) }
- let_it_be(:rules) { table(:incident_management_escalation_rules) }
+RSpec.describe BackfillEscalationPoliciesForOncallSchedules, feature_category: :incident_management do
+ let!(:projects) { table(:projects) }
+ let!(:schedules) { table(:incident_management_oncall_schedules) }
+ let!(:policies) { table(:incident_management_escalation_policies) }
+ let!(:rules) { table(:incident_management_escalation_rules) }
# Project with no schedules
- let_it_be(:namespace) { table(:namespaces).create!(name: 'gitlab', path: 'gitlab') }
- let_it_be(:project_a) { projects.create!(namespace_id: namespace.id) }
+ let!(:namespace) { table(:namespaces).create!(name: 'gitlab', path: 'gitlab') }
+ let!(:project_a) { projects.create!(namespace_id: namespace.id) }
context 'with backfill-able schedules' do
# Project with one schedule
- let_it_be(:project_b) { projects.create!(namespace_id: namespace.id) }
- let_it_be(:schedule_b1) { schedules.create!(project_id: project_b.id, iid: 1, name: 'Schedule B1') }
+ let!(:project_b) { projects.create!(namespace_id: namespace.id) }
+ let!(:schedule_b1) { schedules.create!(project_id: project_b.id, iid: 1, name: 'Schedule B1') }
# Project with multiple schedules
- let_it_be(:project_c) { projects.create!(namespace_id: namespace.id) }
- let_it_be(:schedule_c1) { schedules.create!(project_id: project_c.id, iid: 1, name: 'Schedule C1') }
- let_it_be(:schedule_c2) { schedules.create!(project_id: project_c.id, iid: 2, name: 'Schedule C2') }
+ let!(:project_c) { projects.create!(namespace_id: namespace.id) }
+ let!(:schedule_c1) { schedules.create!(project_id: project_c.id, iid: 1, name: 'Schedule C1') }
+ let!(:schedule_c2) { schedules.create!(project_id: project_c.id, iid: 2, name: 'Schedule C2') }
# Project with a single schedule which already has a policy
- let_it_be(:project_d) { projects.create!(namespace_id: namespace.id) }
- let_it_be(:schedule_d1) { schedules.create!(project_id: project_d.id, iid: 1, name: 'Schedule D1') }
- let_it_be(:policy_d1) { policies.create!(project_id: project_d.id, name: 'Policy D1') }
- let_it_be(:rule_d1) { rules.create!(policy_id: policy_d1.id, oncall_schedule_id: schedule_d1.id, status: 2, elapsed_time_seconds: 60) }
+ let!(:project_d) { projects.create!(namespace_id: namespace.id) }
+ let!(:schedule_d1) { schedules.create!(project_id: project_d.id, iid: 1, name: 'Schedule D1') }
+ let!(:policy_d1) { policies.create!(project_id: project_d.id, name: 'Policy D1') }
+ let!(:rule_d1) { rules.create!(policy_id: policy_d1.id, oncall_schedule_id: schedule_d1.id, status: 2, elapsed_time_seconds: 60) }
# Project with a multiple schedule, one of which already has a policy
- let_it_be(:project_e) { projects.create!(namespace_id: namespace.id) }
- let_it_be(:schedule_e1) { schedules.create!(project_id: project_e.id, iid: 1, name: 'Schedule E1') }
- let_it_be(:schedule_e2) { schedules.create!(project_id: project_e.id, iid: 2, name: 'Schedule E2') }
- let_it_be(:policy_e1) { policies.create!(project_id: project_e.id, name: 'Policy E1') }
- let_it_be(:rule_e1) { rules.create!(policy_id: policy_e1.id, oncall_schedule_id: schedule_e2.id, status: 2, elapsed_time_seconds: 60) }
+ let!(:project_e) { projects.create!(namespace_id: namespace.id) }
+ let!(:schedule_e1) { schedules.create!(project_id: project_e.id, iid: 1, name: 'Schedule E1') }
+ let!(:schedule_e2) { schedules.create!(project_id: project_e.id, iid: 2, name: 'Schedule E2') }
+ let!(:policy_e1) { policies.create!(project_id: project_e.id, name: 'Policy E1') }
+ let!(:rule_e1) { rules.create!(policy_id: policy_e1.id, oncall_schedule_id: schedule_e2.id, status: 2, elapsed_time_seconds: 60) }
# Project with a multiple schedule, with multiple policies
- let_it_be(:project_f) { projects.create!(namespace_id: namespace.id) }
- let_it_be(:schedule_f1) { schedules.create!(project_id: project_f.id, iid: 1, name: 'Schedule F1') }
- let_it_be(:schedule_f2) { schedules.create!(project_id: project_f.id, iid: 2, name: 'Schedule F2') }
- let_it_be(:policy_f1) { policies.create!(project_id: project_f.id, name: 'Policy F1') }
- let_it_be(:rule_f1) { rules.create!(policy_id: policy_f1.id, oncall_schedule_id: schedule_f1.id, status: 2, elapsed_time_seconds: 60) }
- let_it_be(:rule_f2) { rules.create!(policy_id: policy_f1.id, oncall_schedule_id: schedule_f2.id, status: 2, elapsed_time_seconds: 60) }
- let_it_be(:policy_f2) { policies.create!(project_id: project_f.id, name: 'Policy F2') }
- let_it_be(:rule_f3) { rules.create!(policy_id: policy_f2.id, oncall_schedule_id: schedule_f2.id, status: 1, elapsed_time_seconds: 10) }
+ let!(:project_f) { projects.create!(namespace_id: namespace.id) }
+ let!(:schedule_f1) { schedules.create!(project_id: project_f.id, iid: 1, name: 'Schedule F1') }
+ let!(:schedule_f2) { schedules.create!(project_id: project_f.id, iid: 2, name: 'Schedule F2') }
+ let!(:policy_f1) { policies.create!(project_id: project_f.id, name: 'Policy F1') }
+ let!(:rule_f1) { rules.create!(policy_id: policy_f1.id, oncall_schedule_id: schedule_f1.id, status: 2, elapsed_time_seconds: 60) }
+ let!(:rule_f2) { rules.create!(policy_id: policy_f1.id, oncall_schedule_id: schedule_f2.id, status: 2, elapsed_time_seconds: 60) }
+ let!(:policy_f2) { policies.create!(project_id: project_f.id, name: 'Policy F2') }
+ let!(:rule_f3) { rules.create!(policy_id: policy_f2.id, oncall_schedule_id: schedule_f2.id, status: 1, elapsed_time_seconds: 10) }
it 'backfills escalation policies correctly' do
expect { migrate! }
diff --git a/spec/migrations/backfill_group_features_spec.rb b/spec/migrations/backfill_group_features_spec.rb
index 922d54f43be..1e7729a97d8 100644
--- a/spec/migrations/backfill_group_features_spec.rb
+++ b/spec/migrations/backfill_group_features_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillGroupFeatures, :migration do
+RSpec.describe BackfillGroupFeatures, :migration, feature_category: :feature_flags do
let(:migration) { described_class::MIGRATION }
describe '#up' do
diff --git a/spec/migrations/backfill_integrations_enable_ssl_verification_spec.rb b/spec/migrations/backfill_integrations_enable_ssl_verification_spec.rb
index 28578a3d79a..5029a861d31 100644
--- a/spec/migrations/backfill_integrations_enable_ssl_verification_spec.rb
+++ b/spec/migrations/backfill_integrations_enable_ssl_verification_spec.rb
@@ -3,9 +3,9 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillIntegrationsEnableSslVerification do
- let_it_be(:migration) { described_class::MIGRATION }
- let_it_be(:integrations) { described_class::Integration }
+RSpec.describe BackfillIntegrationsEnableSslVerification, feature_category: :authentication_and_authorization do
+ let!(:migration) { described_class::MIGRATION }
+ let!(:integrations) { described_class::Integration }
before do
stub_const("#{described_class.name}::BATCH_SIZE", 2)
diff --git a/spec/migrations/backfill_integrations_type_new_spec.rb b/spec/migrations/backfill_integrations_type_new_spec.rb
index 5b8fbf6f555..79519c4439a 100644
--- a/spec/migrations/backfill_integrations_type_new_spec.rb
+++ b/spec/migrations/backfill_integrations_type_new_spec.rb
@@ -3,9 +3,9 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillIntegrationsTypeNew do
- let_it_be(:migration) { described_class::MIGRATION }
- let_it_be(:integrations) { table(:integrations) }
+RSpec.describe BackfillIntegrationsTypeNew, feature_category: :integrations do
+ let!(:migration) { described_class::MIGRATION }
+ let!(:integrations) { table(:integrations) }
before do
integrations.create!(id: 1)
diff --git a/spec/migrations/backfill_issues_upvotes_count_spec.rb b/spec/migrations/backfill_issues_upvotes_count_spec.rb
index 94cfa29ae89..b8687595b35 100644
--- a/spec/migrations/backfill_issues_upvotes_count_spec.rb
+++ b/spec/migrations/backfill_issues_upvotes_count_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillIssuesUpvotesCount do
+RSpec.describe BackfillIssuesUpvotesCount, feature_category: :team_planning do
let(:migration) { described_class.new }
let(:issues) { table(:issues) }
let(:award_emoji) { table(:award_emoji) }
diff --git a/spec/migrations/backfill_member_namespace_id_for_group_members_spec.rb b/spec/migrations/backfill_member_namespace_id_for_group_members_spec.rb
index 0c0acf85d41..892589dd770 100644
--- a/spec/migrations/backfill_member_namespace_id_for_group_members_spec.rb
+++ b/spec/migrations/backfill_member_namespace_id_for_group_members_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillMemberNamespaceIdForGroupMembers do
- let_it_be(:migration) { described_class::MIGRATION }
+RSpec.describe BackfillMemberNamespaceIdForGroupMembers, feature_category: :subgroups do
+ let!(:migration) { described_class::MIGRATION }
describe '#up' do
it 'schedules background jobs for each batch of group members' do
diff --git a/spec/migrations/backfill_namespace_id_for_namespace_routes_spec.rb b/spec/migrations/backfill_namespace_id_for_namespace_routes_spec.rb
index 913ec404795..627b18cd889 100644
--- a/spec/migrations/backfill_namespace_id_for_namespace_routes_spec.rb
+++ b/spec/migrations/backfill_namespace_id_for_namespace_routes_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillNamespaceIdForNamespaceRoutes do
- let_it_be(:migration) { described_class::MIGRATION }
+RSpec.describe BackfillNamespaceIdForNamespaceRoutes, feature_category: :projects do
+ let!(:migration) { described_class::MIGRATION }
describe '#up' do
it 'schedules background jobs for each batch of routes' do
diff --git a/spec/migrations/backfill_namespace_id_for_project_routes_spec.rb b/spec/migrations/backfill_namespace_id_for_project_routes_spec.rb
index 28edd17731f..773c1733a4a 100644
--- a/spec/migrations/backfill_namespace_id_for_project_routes_spec.rb
+++ b/spec/migrations/backfill_namespace_id_for_project_routes_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillNamespaceIdForProjectRoutes, :migration do
+RSpec.describe BackfillNamespaceIdForProjectRoutes, :migration, feature_category: :subgroups do
let(:migration) { described_class::MIGRATION }
describe '#up' do
diff --git a/spec/migrations/backfill_namespace_id_on_issues_spec.rb b/spec/migrations/backfill_namespace_id_on_issues_spec.rb
index 2721d7ce8f1..28453394cb0 100644
--- a/spec/migrations/backfill_namespace_id_on_issues_spec.rb
+++ b/spec/migrations/backfill_namespace_id_on_issues_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillNamespaceIdOnIssues, :migration do
+RSpec.describe BackfillNamespaceIdOnIssues, :migration, feature_category: :team_planning do
let(:migration) { described_class::MIGRATION }
describe '#up' do
diff --git a/spec/migrations/backfill_nuget_temporary_packages_to_processing_status_spec.rb b/spec/migrations/backfill_nuget_temporary_packages_to_processing_status_spec.rb
index 574020e52d5..ae2656eaf98 100644
--- a/spec/migrations/backfill_nuget_temporary_packages_to_processing_status_spec.rb
+++ b/spec/migrations/backfill_nuget_temporary_packages_to_processing_status_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe BackfillNugetTemporaryPackagesToProcessingStatus, :migration do
+RSpec.describe BackfillNugetTemporaryPackagesToProcessingStatus, :migration, feature_category: :package_registry do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:packages) { table(:packages_packages) }
diff --git a/spec/migrations/backfill_project_import_level_spec.rb b/spec/migrations/backfill_project_import_level_spec.rb
index c24ddac0730..b41e323a92f 100644
--- a/spec/migrations/backfill_project_import_level_spec.rb
+++ b/spec/migrations/backfill_project_import_level_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillProjectImportLevel do
- let_it_be(:batched_migration) { described_class::MIGRATION }
+RSpec.describe BackfillProjectImportLevel, feature_category: :importers do
+ let!(:batched_migration) { described_class::MIGRATION }
describe '#up' do
it 'schedules background jobs for each batch of namespaces' do
diff --git a/spec/migrations/backfill_project_namespaces_for_group_spec.rb b/spec/migrations/backfill_project_namespaces_for_group_spec.rb
index 0d34d19d42a..b21ed6e1aa2 100644
--- a/spec/migrations/backfill_project_namespaces_for_group_spec.rb
+++ b/spec/migrations/backfill_project_namespaces_for_group_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillProjectNamespacesForGroup do
- let_it_be(:migration) { described_class::MIGRATION }
+RSpec.describe BackfillProjectNamespacesForGroup, feature_category: :subgroups do
+ let!(:migration) { described_class::MIGRATION }
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
diff --git a/spec/migrations/backfill_stage_event_hash_spec.rb b/spec/migrations/backfill_stage_event_hash_spec.rb
index cecaddcd3d4..399a9c4dfde 100644
--- a/spec/migrations/backfill_stage_event_hash_spec.rb
+++ b/spec/migrations/backfill_stage_event_hash_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe BackfillStageEventHash, schema: 20210730103808 do
+RSpec.describe BackfillStageEventHash, schema: 20210730103808, feature_category: :value_stream_management do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:labels) { table(:labels) }
diff --git a/spec/migrations/backfill_user_namespace_spec.rb b/spec/migrations/backfill_user_namespace_spec.rb
index 094aec82e9c..a58030803b1 100644
--- a/spec/migrations/backfill_user_namespace_spec.rb
+++ b/spec/migrations/backfill_user_namespace_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe BackfillUserNamespace do
- let_it_be(:migration) { described_class::MIGRATION }
+RSpec.describe BackfillUserNamespace, feature_category: :subgroups do
+ let!(:migration) { described_class::MIGRATION }
describe '#up' do
it 'schedules background jobs for each batch of namespaces' do
diff --git a/spec/migrations/bulk_insert_cluster_enabled_grants_spec.rb b/spec/migrations/bulk_insert_cluster_enabled_grants_spec.rb
index a359a78ab45..e85489198ee 100644
--- a/spec/migrations/bulk_insert_cluster_enabled_grants_spec.rb
+++ b/spec/migrations/bulk_insert_cluster_enabled_grants_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe BulkInsertClusterEnabledGrants, :migration do
+RSpec.describe BulkInsertClusterEnabledGrants, :migration, feature_category: :kubernetes_management do
let(:migration) { described_class.new }
let(:cluster_enabled_grants) { table(:cluster_enabled_grants) }
diff --git a/spec/migrations/change_public_projects_cost_factor_spec.rb b/spec/migrations/change_public_projects_cost_factor_spec.rb
index 039edda750b..656c8a45c57 100644
--- a/spec/migrations/change_public_projects_cost_factor_spec.rb
+++ b/spec/migrations/change_public_projects_cost_factor_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ChangePublicProjectsCostFactor, migration: :gitlab_ci do
+RSpec.describe ChangePublicProjectsCostFactor, migration: :gitlab_ci, feature_category: :runner do
let(:runners) { table(:ci_runners) }
let!(:shared_1) { runners.create!(runner_type: 1, public_projects_minutes_cost_factor: 0) }
diff --git a/spec/migrations/change_task_system_note_wording_to_checklist_item_spec.rb b/spec/migrations/change_task_system_note_wording_to_checklist_item_spec.rb
index 039ee92f8bd..421c519b2bc 100644
--- a/spec/migrations/change_task_system_note_wording_to_checklist_item_spec.rb
+++ b/spec/migrations/change_task_system_note_wording_to_checklist_item_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ChangeTaskSystemNoteWordingToChecklistItem, :migration do
+RSpec.describe ChangeTaskSystemNoteWordingToChecklistItem, :migration, feature_category: :team_planning do
let(:migration) { described_class::MIGRATION }
describe '#up' do
diff --git a/spec/migrations/change_web_hook_events_default_spec.rb b/spec/migrations/change_web_hook_events_default_spec.rb
index aad187187d0..c6c3f285ff1 100644
--- a/spec/migrations/change_web_hook_events_default_spec.rb
+++ b/spec/migrations/change_web_hook_events_default_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ChangeWebHookEventsDefault do
+RSpec.describe ChangeWebHookEventsDefault, feature_category: :integrations do
let(:web_hooks) { table(:web_hooks) }
let(:projects) { table(:projects) }
let(:groups) { table(:namespaces) }
diff --git a/spec/migrations/clean_up_pending_builds_table_spec.rb b/spec/migrations/clean_up_pending_builds_table_spec.rb
index 17e62e1b486..e044d4a702b 100644
--- a/spec/migrations/clean_up_pending_builds_table_spec.rb
+++ b/spec/migrations/clean_up_pending_builds_table_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe CleanUpPendingBuildsTable, :suppress_gitlab_schemas_validate_connection do
+RSpec.describe CleanUpPendingBuildsTable, :suppress_gitlab_schemas_validate_connection,
+feature_category: :continuous_integration do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:queue) { table(:ci_pending_builds) }
diff --git a/spec/migrations/cleanup_after_add_primary_email_to_emails_if_user_confirmed_spec.rb b/spec/migrations/cleanup_after_add_primary_email_to_emails_if_user_confirmed_spec.rb
index abff7c6aba1..6027199c11c 100644
--- a/spec/migrations/cleanup_after_add_primary_email_to_emails_if_user_confirmed_spec.rb
+++ b/spec/migrations/cleanup_after_add_primary_email_to_emails_if_user_confirmed_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe CleanupAfterAddPrimaryEmailToEmailsIfUserConfirmed, :sidekiq do
+RSpec.describe CleanupAfterAddPrimaryEmailToEmailsIfUserConfirmed, :sidekiq, feature_category: :users do
let(:migration) { described_class.new }
let(:users) { table(:users) }
let(:emails) { table(:emails) }
diff --git a/spec/migrations/cleanup_after_fixing_issue_when_admin_changed_primary_email_spec.rb b/spec/migrations/cleanup_after_fixing_issue_when_admin_changed_primary_email_spec.rb
index eda57545c7a..e8dce46bdbc 100644
--- a/spec/migrations/cleanup_after_fixing_issue_when_admin_changed_primary_email_spec.rb
+++ b/spec/migrations/cleanup_after_fixing_issue_when_admin_changed_primary_email_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe CleanupAfterFixingIssueWhenAdminChangedPrimaryEmail, :sidekiq do
+RSpec.describe CleanupAfterFixingIssueWhenAdminChangedPrimaryEmail, :sidekiq, feature_category: :users do
let(:migration) { described_class.new }
let(:users) { table(:users) }
let(:emails) { table(:emails) }
diff --git a/spec/migrations/cleanup_after_fixing_regression_with_new_users_emails_spec.rb b/spec/migrations/cleanup_after_fixing_regression_with_new_users_emails_spec.rb
index 043bb091df3..01ceef9f3a1 100644
--- a/spec/migrations/cleanup_after_fixing_regression_with_new_users_emails_spec.rb
+++ b/spec/migrations/cleanup_after_fixing_regression_with_new_users_emails_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe CleanupAfterFixingRegressionWithNewUsersEmails, :sidekiq do
+RSpec.describe CleanupAfterFixingRegressionWithNewUsersEmails, :sidekiq, feature_category: :users do
let(:migration) { described_class.new }
let(:users) { table(:users) }
let(:emails) { table(:emails) }
diff --git a/spec/migrations/cleanup_backfill_integrations_enable_ssl_verification_spec.rb b/spec/migrations/cleanup_backfill_integrations_enable_ssl_verification_spec.rb
index 1517405b358..7aaa90ee985 100644
--- a/spec/migrations/cleanup_backfill_integrations_enable_ssl_verification_spec.rb
+++ b/spec/migrations/cleanup_backfill_integrations_enable_ssl_verification_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe CleanupBackfillIntegrationsEnableSslVerification, :migration do
+RSpec.describe CleanupBackfillIntegrationsEnableSslVerification, :migration,
+feature_category: :authentication_and_authorization do
let(:job_class_name) { 'BackfillIntegrationsEnableSslVerification' }
before do
diff --git a/spec/migrations/cleanup_move_container_registry_enabled_to_project_feature_spec.rb b/spec/migrations/cleanup_move_container_registry_enabled_to_project_feature_spec.rb
index f0f9249515b..1badde62526 100644
--- a/spec/migrations/cleanup_move_container_registry_enabled_to_project_feature_spec.rb
+++ b/spec/migrations/cleanup_move_container_registry_enabled_to_project_feature_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe CleanupMoveContainerRegistryEnabledToProjectFeature, :migration do
+RSpec.describe CleanupMoveContainerRegistryEnabledToProjectFeature, :migration, feature_category: :navigation do
let(:namespace) { table(:namespaces).create!(name: 'gitlab', path: 'gitlab-org') }
let(:non_null_project_features) { { pages_access_level: 20 } }
let(:bg_class_name) { 'MoveContainerRegistryEnabledToProjectFeature' }
diff --git a/spec/migrations/cleanup_mr_attention_request_todos_spec.rb b/spec/migrations/cleanup_mr_attention_request_todos_spec.rb
index 9f593ca8292..4fa2419aa7c 100644
--- a/spec/migrations/cleanup_mr_attention_request_todos_spec.rb
+++ b/spec/migrations/cleanup_mr_attention_request_todos_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe CleanupMrAttentionRequestTodos, :migration do
+RSpec.describe CleanupMrAttentionRequestTodos, :migration, feature_category: :code_review do
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
let(:users) { table(:users) }
diff --git a/spec/migrations/cleanup_orphaned_routes_spec.rb b/spec/migrations/cleanup_orphaned_routes_spec.rb
index 68598939557..a0ce9062c70 100644
--- a/spec/migrations/cleanup_orphaned_routes_spec.rb
+++ b/spec/migrations/cleanup_orphaned_routes_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe CleanupOrphanedRoutes, :migration do
+RSpec.describe CleanupOrphanedRoutes, :migration, feature_category: :projects do
let(:migration) { described_class::MIGRATION }
describe '#up' do
diff --git a/spec/migrations/cleanup_remaining_orphan_invites_spec.rb b/spec/migrations/cleanup_remaining_orphan_invites_spec.rb
index 987535a4f09..598030c99a0 100644
--- a/spec/migrations/cleanup_remaining_orphan_invites_spec.rb
+++ b/spec/migrations/cleanup_remaining_orphan_invites_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe CleanupRemainingOrphanInvites, :migration do
+RSpec.describe CleanupRemainingOrphanInvites, :migration, feature_category: :subgroups do
def create_member(**extra_attributes)
defaults = {
access_level: 10,
diff --git a/spec/migrations/cleanup_vulnerability_state_transitions_with_same_from_state_to_state_spec.rb b/spec/migrations/cleanup_vulnerability_state_transitions_with_same_from_state_to_state_spec.rb
index 92ece81ffc8..b808f03428d 100644
--- a/spec/migrations/cleanup_vulnerability_state_transitions_with_same_from_state_to_state_spec.rb
+++ b/spec/migrations/cleanup_vulnerability_state_transitions_with_same_from_state_to_state_spec.rb
@@ -3,10 +3,11 @@
require 'spec_helper'
require_migration!
-RSpec.describe CleanupVulnerabilityStateTransitionsWithSameFromStateToState, :migration do
- let_it_be(:namespace) { table(:namespaces).create!(name: 'namespace', type: 'Group', path: 'namespace') }
- let_it_be(:user) { table(:users).create!(email: 'author@example.com', username: 'author', projects_limit: 10) }
- let_it_be(:project) do
+RSpec.describe CleanupVulnerabilityStateTransitionsWithSameFromStateToState, :migration,
+feature_category: :vulnerability_management do
+ let!(:namespace) { table(:namespaces).create!(name: 'namespace', type: 'Group', path: 'namespace') }
+ let!(:user) { table(:users).create!(email: 'author@example.com', username: 'author', projects_limit: 10) }
+ let!(:project) do
table(:projects).create!(
path: 'project',
namespace_id: namespace.id,
@@ -14,7 +15,7 @@ RSpec.describe CleanupVulnerabilityStateTransitionsWithSameFromStateToState, :mi
)
end
- let_it_be(:vulnerability) do
+ let!(:vulnerability) do
table(:vulnerabilities).create!(
project_id: project.id,
author_id: user.id,
@@ -25,7 +26,7 @@ RSpec.describe CleanupVulnerabilityStateTransitionsWithSameFromStateToState, :mi
)
end
- let_it_be(:state_transitions) { table(:vulnerability_state_transitions) }
+ let!(:state_transitions) { table(:vulnerability_state_transitions) }
let!(:state_transition_with_no_state_change) do
state_transitions.create!(
diff --git a/spec/migrations/confirm_security_bot_spec.rb b/spec/migrations/confirm_security_bot_spec.rb
index 19ca81f92f3..3761c113692 100644
--- a/spec/migrations/confirm_security_bot_spec.rb
+++ b/spec/migrations/confirm_security_bot_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ConfirmSecurityBot, :migration do
+RSpec.describe ConfirmSecurityBot, :migration, feature_category: :users do
let(:users) { table(:users) }
let(:user_type) { 8 }
diff --git a/spec/migrations/confirm_support_bot_user_spec.rb b/spec/migrations/confirm_support_bot_user_spec.rb
index c60c7fe45f7..863bdb13585 100644
--- a/spec/migrations/confirm_support_bot_user_spec.rb
+++ b/spec/migrations/confirm_support_bot_user_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ConfirmSupportBotUser, :migration do
+RSpec.describe ConfirmSupportBotUser, :migration, feature_category: :users do
let(:users) { table(:users) }
context 'when support bot user is currently unconfirmed' do
@@ -72,7 +72,7 @@ RSpec.describe ConfirmSupportBotUser, :migration do
private
- def create_user!(name: 'GitLab Support Bot', email: 'support@example.com', user_type:, created_at: Time.now, confirmed_at: nil, record_timestamps: true)
+ def create_user!(user_type:, name: 'GitLab Support Bot', email: 'support@example.com', created_at: Time.now, confirmed_at: nil, record_timestamps: true)
users.create!(
name: name,
email: email,
diff --git a/spec/migrations/delete_migrate_shared_vulnerability_scanners_spec.rb b/spec/migrations/delete_migrate_shared_vulnerability_scanners_spec.rb
index 259b175cd19..562b1e25db4 100644
--- a/spec/migrations/delete_migrate_shared_vulnerability_scanners_spec.rb
+++ b/spec/migrations/delete_migrate_shared_vulnerability_scanners_spec.rb
@@ -4,7 +4,7 @@ require "spec_helper"
require_migration!
-RSpec.describe DeleteMigrateSharedVulnerabilityScanners, :migration do
+RSpec.describe DeleteMigrateSharedVulnerabilityScanners, :migration, feature_category: :vulnerability_management do
let(:batched_background_migrations) { table(:batched_background_migrations) }
let(:batched_background_migration_jobs) { table(:batched_background_migration_jobs) }
diff --git a/spec/migrations/delete_security_findings_without_uuid_spec.rb b/spec/migrations/delete_security_findings_without_uuid_spec.rb
index bfd89f1aa82..e4c17288384 100644
--- a/spec/migrations/delete_security_findings_without_uuid_spec.rb
+++ b/spec/migrations/delete_security_findings_without_uuid_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe DeleteSecurityFindingsWithoutUuid, :suppress_gitlab_schemas_validate_connection do
+RSpec.describe DeleteSecurityFindingsWithoutUuid, :suppress_gitlab_schemas_validate_connection,
+feature_category: :vulnerability_management do
let(:users) { table(:users) }
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
diff --git a/spec/migrations/disable_expiration_policies_linked_to_no_container_images_spec.rb b/spec/migrations/disable_expiration_policies_linked_to_no_container_images_spec.rb
index f2be06f1ed6..1d948257fcc 100644
--- a/spec/migrations/disable_expiration_policies_linked_to_no_container_images_spec.rb
+++ b/spec/migrations/disable_expiration_policies_linked_to_no_container_images_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe DisableExpirationPoliciesLinkedToNoContainerImages do
+RSpec.describe DisableExpirationPoliciesLinkedToNoContainerImages, feature_category: :container_registry do
let(:projects) { table(:projects) }
let(:container_expiration_policies) { table(:container_expiration_policies) }
let(:container_repositories) { table(:container_repositories) }
diff --git a/spec/migrations/disable_job_token_scope_when_unused_spec.rb b/spec/migrations/disable_job_token_scope_when_unused_spec.rb
index 3ce4ef5c102..fddf3594e2b 100644
--- a/spec/migrations/disable_job_token_scope_when_unused_spec.rb
+++ b/spec/migrations/disable_job_token_scope_when_unused_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe DisableJobTokenScopeWhenUnused do
+RSpec.describe DisableJobTokenScopeWhenUnused, feature_category: :continuous_integration do
it 'is a no-op' do
migrate!
end
diff --git a/spec/migrations/finalize_invalid_member_cleanup_spec.rb b/spec/migrations/finalize_invalid_member_cleanup_spec.rb
index a29a89c2396..29d03f8983c 100644
--- a/spec/migrations/finalize_invalid_member_cleanup_spec.rb
+++ b/spec/migrations/finalize_invalid_member_cleanup_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
require_migration!
-RSpec.describe FinalizeInvalidMemberCleanup, :migration do
+RSpec.describe FinalizeInvalidMemberCleanup, :migration, feature_category: :subgroups do
let(:batched_migrations) { table(:batched_background_migrations) }
- let_it_be(:migration) { described_class::MIGRATION }
+ let!(:migration) { described_class::MIGRATION }
describe '#up' do
shared_examples 'finalizes the migration' do
diff --git a/spec/migrations/finalize_issues_namespace_id_backfilling_spec.rb b/spec/migrations/finalize_issues_namespace_id_backfilling_spec.rb
new file mode 100644
index 00000000000..d0c25fb3dd6
--- /dev/null
+++ b/spec/migrations/finalize_issues_namespace_id_backfilling_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe FinalizeIssuesNamespaceIdBackfilling, :migration, feature_category: :team_planning do
+ let(:batched_migrations) { table(:batched_background_migrations) }
+
+ let!(:migration) { described_class::MIGRATION }
+
+ describe '#up' do
+ shared_examples 'finalizes the migration' do
+ it 'finalizes the migration' do
+ allow_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner) do |runner|
+ expect(runner).to receive(:finalize).with('BackfillProjectNamespaceOnIssues', :projects, :id, [])
+ end
+ end
+ end
+
+ context 'when routes backfilling migration is missing' do
+ it 'warns migration not found' do
+ expect(Gitlab::AppLogger)
+ .to receive(:warn).with(/Could not find batched background migration for the given configuration:/)
+
+ migrate!
+ end
+ end
+
+ context 'with backfilling migration present' do
+ let!(:project_namespace_backfill) do
+ batched_migrations.create!(
+ job_class_name: 'BackfillProjectNamespaceOnIssues',
+ table_name: :routes,
+ column_name: :id,
+ job_arguments: [],
+ interval: 2.minutes,
+ min_value: 1,
+ max_value: 2,
+ batch_size: 1000,
+ sub_batch_size: 200,
+ gitlab_schema: :gitlab_main,
+ status: 3 # finished
+ )
+ end
+
+ context 'when backfilling migration finished successfully' do
+ it 'does not raise exception' do
+ expect { migrate! }.not_to raise_error
+ end
+ end
+
+ context 'with different backfilling migration statuses' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:status, :description) do
+ 0 | 'paused'
+ 1 | 'active'
+ 4 | 'failed'
+ 5 | 'finalizing'
+ end
+
+ with_them do
+ before do
+ project_namespace_backfill.update!(status: status)
+ end
+
+ it_behaves_like 'finalizes the migration'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/migrations/finalize_orphaned_routes_cleanup_spec.rb b/spec/migrations/finalize_orphaned_routes_cleanup_spec.rb
index dfc95ed9e63..78546806039 100644
--- a/spec/migrations/finalize_orphaned_routes_cleanup_spec.rb
+++ b/spec/migrations/finalize_orphaned_routes_cleanup_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
require_migration!
-RSpec.describe FinalizeOrphanedRoutesCleanup, :migration do
+RSpec.describe FinalizeOrphanedRoutesCleanup, :migration, feature_category: :projects do
let(:batched_migrations) { table(:batched_background_migrations) }
- let_it_be(:migration) { described_class::MIGRATION }
+ let!(:migration) { described_class::MIGRATION }
describe '#up' do
shared_examples 'finalizes the migration' do
diff --git a/spec/migrations/finalize_project_namespaces_backfill_spec.rb b/spec/migrations/finalize_project_namespaces_backfill_spec.rb
index 56f3b0f6ba5..6cc3a694de8 100644
--- a/spec/migrations/finalize_project_namespaces_backfill_spec.rb
+++ b/spec/migrations/finalize_project_namespaces_backfill_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
require_migration!
-RSpec.describe FinalizeProjectNamespacesBackfill, :migration do
+RSpec.describe FinalizeProjectNamespacesBackfill, :migration, feature_category: :projects do
let(:batched_migrations) { table(:batched_background_migrations) }
- let_it_be(:migration) { described_class::MIGRATION }
+ let!(:migration) { described_class::MIGRATION }
describe '#up' do
shared_examples 'finalizes the migration' do
diff --git a/spec/migrations/finalize_routes_backfilling_for_projects_spec.rb b/spec/migrations/finalize_routes_backfilling_for_projects_spec.rb
index 2bb740d0c2f..b79fdc98425 100644
--- a/spec/migrations/finalize_routes_backfilling_for_projects_spec.rb
+++ b/spec/migrations/finalize_routes_backfilling_for_projects_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
require_migration!
-RSpec.describe FinalizeRoutesBackfillingForProjects, :migration do
+RSpec.describe FinalizeRoutesBackfillingForProjects, :migration, feature_category: :projects do
let(:batched_migrations) { table(:batched_background_migrations) }
- let_it_be(:migration) { described_class::MIGRATION }
+ let!(:migration) { described_class::MIGRATION }
describe '#up' do
shared_examples 'finalizes the migration' do
diff --git a/spec/migrations/finalize_traversal_ids_background_migrations_spec.rb b/spec/migrations/finalize_traversal_ids_background_migrations_spec.rb
index 74d6447e6a7..0cebe7b9f91 100644
--- a/spec/migrations/finalize_traversal_ids_background_migrations_spec.rb
+++ b/spec/migrations/finalize_traversal_ids_background_migrations_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!('finalize_traversal_ids_background_migrations')
-RSpec.describe FinalizeTraversalIdsBackgroundMigrations, :migration do
+RSpec.describe FinalizeTraversalIdsBackgroundMigrations, :migration, feature_category: :database do
shared_context 'incomplete background migration' do
before do
# Jobs enqueued in Sidekiq.
diff --git a/spec/migrations/fix_and_backfill_project_namespaces_for_projects_with_duplicate_name_spec.rb b/spec/migrations/fix_and_backfill_project_namespaces_for_projects_with_duplicate_name_spec.rb
index 44a2220b2ad..6b9fb1c6f2c 100644
--- a/spec/migrations/fix_and_backfill_project_namespaces_for_projects_with_duplicate_name_spec.rb
+++ b/spec/migrations/fix_and_backfill_project_namespaces_for_projects_with_duplicate_name_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe FixAndBackfillProjectNamespacesForProjectsWithDuplicateName, :migration do
+RSpec.describe FixAndBackfillProjectNamespacesForProjectsWithDuplicateName, :migration, feature_category: :projects do
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
diff --git a/spec/migrations/fix_batched_migrations_old_format_job_arguments_spec.rb b/spec/migrations/fix_batched_migrations_old_format_job_arguments_spec.rb
index e15011d0dab..8def53e1858 100644
--- a/spec/migrations/fix_batched_migrations_old_format_job_arguments_spec.rb
+++ b/spec/migrations/fix_batched_migrations_old_format_job_arguments_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
# rubocop:disable Style/WordArray
-RSpec.describe FixBatchedMigrationsOldFormatJobArguments do
+RSpec.describe FixBatchedMigrationsOldFormatJobArguments, feature_category: :users do
let(:batched_background_migrations) { table(:batched_background_migrations) }
context 'when migrations with legacy job arguments exists' do
diff --git a/spec/migrations/generate_customers_dot_jwt_signing_key_spec.rb b/spec/migrations/generate_customers_dot_jwt_signing_key_spec.rb
index b7a91abf5d7..1385b67b607 100644
--- a/spec/migrations/generate_customers_dot_jwt_signing_key_spec.rb
+++ b/spec/migrations/generate_customers_dot_jwt_signing_key_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe GenerateCustomersDotJwtSigningKey do
+RSpec.describe GenerateCustomersDotJwtSigningKey, feature_category: :customersdot_application do
let(:application_settings) do
Class.new(ActiveRecord::Base) do
self.table_name = 'application_settings'
diff --git a/spec/migrations/insert_ci_daily_pipeline_schedule_triggers_plan_limits_spec.rb b/spec/migrations/insert_ci_daily_pipeline_schedule_triggers_plan_limits_spec.rb
index 1b6cb6a86a0..9358b71132c 100644
--- a/spec/migrations/insert_ci_daily_pipeline_schedule_triggers_plan_limits_spec.rb
+++ b/spec/migrations/insert_ci_daily_pipeline_schedule_triggers_plan_limits_spec.rb
@@ -3,9 +3,9 @@
require 'spec_helper'
require_migration!
-RSpec.describe InsertCiDailyPipelineScheduleTriggersPlanLimits do
- let_it_be(:plans) { table(:plans) }
- let_it_be(:plan_limits) { table(:plan_limits) }
+RSpec.describe InsertCiDailyPipelineScheduleTriggersPlanLimits, feature_category: :purchase do
+ let!(:plans) { table(:plans) }
+ let!(:plan_limits) { table(:plan_limits) }
context 'when on Gitlab.com' do
let(:free_plan) { plans.create!(name: 'free') }
@@ -46,25 +46,25 @@ RSpec.describe InsertCiDailyPipelineScheduleTriggersPlanLimits do
end
context 'when on self hosted' do
- let(:free_plan) { plans.create!(name: 'free') }
+ let(:default_plan) { plans.create!(name: 'default') }
before do
allow(Gitlab).to receive(:com?).and_return(false)
- plan_limits.create!(plan_id: free_plan.id)
+ plan_limits.create!(plan_id: default_plan.id)
end
it 'does nothing' do
reversible_migration do |migration|
migration.before -> {
expect(plan_limits.pluck(:plan_id, :ci_daily_pipeline_schedule_triggers)).to contain_exactly(
- [free_plan.id, 0]
+ [default_plan.id, 0]
)
}
migration.after -> {
expect(plan_limits.pluck(:plan_id, :ci_daily_pipeline_schedule_triggers)).to contain_exactly(
- [free_plan.id, 0]
+ [default_plan.id, 0]
)
}
end
diff --git a/spec/migrations/migrate_elastic_index_settings_spec.rb b/spec/migrations/migrate_elastic_index_settings_spec.rb
index 5f39d9b9fc1..b67c4d902c7 100644
--- a/spec/migrations/migrate_elastic_index_settings_spec.rb
+++ b/spec/migrations/migrate_elastic_index_settings_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe MigrateElasticIndexSettings do
+RSpec.describe MigrateElasticIndexSettings, feature_category: :global_search do
let(:elastic_index_settings) { table(:elastic_index_settings) }
let(:application_settings) { table(:application_settings) }
diff --git a/spec/migrations/migrate_protected_attribute_to_pending_builds_spec.rb b/spec/migrations/migrate_protected_attribute_to_pending_builds_spec.rb
index 01805a9eb79..2f62147da9d 100644
--- a/spec/migrations/migrate_protected_attribute_to_pending_builds_spec.rb
+++ b/spec/migrations/migrate_protected_attribute_to_pending_builds_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe MigrateProtectedAttributeToPendingBuilds, :suppress_gitlab_schemas_validate_connection do
+RSpec.describe MigrateProtectedAttributeToPendingBuilds, :suppress_gitlab_schemas_validate_connection,
+feature_category: :continuous_integration do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:queue) { table(:ci_pending_builds) }
diff --git a/spec/migrations/move_container_registry_enabled_to_project_features3_spec.rb b/spec/migrations/move_container_registry_enabled_to_project_features3_spec.rb
index e47cea749d6..25e0ef439bd 100644
--- a/spec/migrations/move_container_registry_enabled_to_project_features3_spec.rb
+++ b/spec/migrations/move_container_registry_enabled_to_project_features3_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe MoveContainerRegistryEnabledToProjectFeatures3, :migration do
+RSpec.describe MoveContainerRegistryEnabledToProjectFeatures3, :migration, feature_category: :container_registry do
let(:namespace) { table(:namespaces).create!(name: 'gitlab', path: 'gitlab-org') }
let!(:background_jobs) do
diff --git a/spec/migrations/move_security_findings_table_to_gitlab_partitions_dynamic_schema_spec.rb b/spec/migrations/move_security_findings_table_to_gitlab_partitions_dynamic_schema_spec.rb
index b5bb86edce2..2533d3224a6 100644
--- a/spec/migrations/move_security_findings_table_to_gitlab_partitions_dynamic_schema_spec.rb
+++ b/spec/migrations/move_security_findings_table_to_gitlab_partitions_dynamic_schema_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe MoveSecurityFindingsTableToGitlabPartitionsDynamicSchema do
+RSpec.describe MoveSecurityFindingsTableToGitlabPartitionsDynamicSchema, feature_category: :vulnerability_management do
let(:partitions_sql) do
<<~SQL
SELECT
diff --git a/spec/migrations/orphaned_invite_tokens_cleanup_spec.rb b/spec/migrations/orphaned_invite_tokens_cleanup_spec.rb
index b33e29f82e2..56f47fca864 100644
--- a/spec/migrations/orphaned_invite_tokens_cleanup_spec.rb
+++ b/spec/migrations/orphaned_invite_tokens_cleanup_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe OrphanedInviteTokensCleanup, :migration do
+RSpec.describe OrphanedInviteTokensCleanup, :migration, feature_category: :subgroups do
def create_member(**extra_attributes)
defaults = {
access_level: 10,
diff --git a/spec/migrations/orphaned_invited_members_cleanup_spec.rb b/spec/migrations/orphaned_invited_members_cleanup_spec.rb
index 4427e707f56..1d4db5306bc 100644
--- a/spec/migrations/orphaned_invited_members_cleanup_spec.rb
+++ b/spec/migrations/orphaned_invited_members_cleanup_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe OrphanedInvitedMembersCleanup, :migration do
+RSpec.describe OrphanedInvitedMembersCleanup, :migration, feature_category: :subgroups do
describe '#up', :aggregate_failures do
it 'removes accepted members with no associated user' do
user = create_user!('testuser1')
diff --git a/spec/migrations/populate_audit_event_streaming_verification_token_spec.rb b/spec/migrations/populate_audit_event_streaming_verification_token_spec.rb
index b3fe1776183..e2c117903d4 100644
--- a/spec/migrations/populate_audit_event_streaming_verification_token_spec.rb
+++ b/spec/migrations/populate_audit_event_streaming_verification_token_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe PopulateAuditEventStreamingVerificationToken do
+RSpec.describe PopulateAuditEventStreamingVerificationToken, feature_category: :audit_events do
let(:groups) { table(:namespaces) }
let(:destinations) { table(:audit_events_external_audit_event_destinations) }
let(:migration) { described_class.new }
diff --git a/spec/migrations/populate_dismissal_information_for_vulnerabilities_spec.rb b/spec/migrations/populate_dismissal_information_for_vulnerabilities_spec.rb
index 1db52781956..66fd5eb5ae5 100644
--- a/spec/migrations/populate_dismissal_information_for_vulnerabilities_spec.rb
+++ b/spec/migrations/populate_dismissal_information_for_vulnerabilities_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe PopulateDismissalInformationForVulnerabilities do
+RSpec.describe PopulateDismissalInformationForVulnerabilities, feature_category: :vulnerability_management do
let(:users) { table(:users) }
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
diff --git a/spec/migrations/populate_operation_visibility_permissions_spec.rb b/spec/migrations/populate_operation_visibility_permissions_spec.rb
index 6737a6f84c3..704152bd6a9 100644
--- a/spec/migrations/populate_operation_visibility_permissions_spec.rb
+++ b/spec/migrations/populate_operation_visibility_permissions_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe PopulateOperationVisibilityPermissions, :migration do
+RSpec.describe PopulateOperationVisibilityPermissions, :migration, feature_category: :navigation do
let(:migration) { described_class::MIGRATION }
before do
diff --git a/spec/migrations/populate_releases_access_level_from_repository_spec.rb b/spec/migrations/populate_releases_access_level_from_repository_spec.rb
index 2bb97662923..ebb7aa6f7fa 100644
--- a/spec/migrations/populate_releases_access_level_from_repository_spec.rb
+++ b/spec/migrations/populate_releases_access_level_from_repository_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe PopulateReleasesAccessLevelFromRepository, :migration do
+RSpec.describe PopulateReleasesAccessLevelFromRepository, :migration, feature_category: :navigation do
let(:projects) { table(:projects) }
let(:groups) { table(:namespaces) }
let(:project_features) { table(:project_features) }
diff --git a/spec/migrations/queue_backfill_project_feature_package_registry_access_level_spec.rb b/spec/migrations/queue_backfill_project_feature_package_registry_access_level_spec.rb
index 487d94b82a1..6a01b30445b 100644
--- a/spec/migrations/queue_backfill_project_feature_package_registry_access_level_spec.rb
+++ b/spec/migrations/queue_backfill_project_feature_package_registry_access_level_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe QueueBackfillProjectFeaturePackageRegistryAccessLevel do
- let_it_be(:batched_migration) { described_class::MIGRATION }
+RSpec.describe QueueBackfillProjectFeaturePackageRegistryAccessLevel, feature_category: :package_registry do
+ let!(:batched_migration) { described_class::MIGRATION }
it 'schedules a new batched migration' do
reversible_migration do |migration|
diff --git a/spec/migrations/queue_backfill_user_details_fields_spec.rb b/spec/migrations/queue_backfill_user_details_fields_spec.rb
index 388ac6d1bce..e77a66907de 100644
--- a/spec/migrations/queue_backfill_user_details_fields_spec.rb
+++ b/spec/migrations/queue_backfill_user_details_fields_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe QueueBackfillUserDetailsFields do
- let_it_be(:batched_migration) { described_class::MIGRATION }
+RSpec.describe QueueBackfillUserDetailsFields, feature_category: :users do
+ let!(:batched_migration) { described_class::MIGRATION }
it 'schedules a new batched migration' do
reversible_migration do |migration|
diff --git a/spec/migrations/queue_populate_projects_star_count_spec.rb b/spec/migrations/queue_populate_projects_star_count_spec.rb
index 848136d8005..84565d14d52 100644
--- a/spec/migrations/queue_populate_projects_star_count_spec.rb
+++ b/spec/migrations/queue_populate_projects_star_count_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe QueuePopulateProjectsStarCount do
- let_it_be(:batched_migration) { described_class::MIGRATION }
+RSpec.describe QueuePopulateProjectsStarCount, feature_category: :users do
+ let!(:batched_migration) { described_class::MIGRATION }
it 'schedules a new batched migration' do
reversible_migration do |migration|
diff --git a/spec/migrations/re_schedule_latest_pipeline_id_population_with_all_security_related_artifact_types_spec.rb b/spec/migrations/re_schedule_latest_pipeline_id_population_with_all_security_related_artifact_types_spec.rb
index 45a2772adda..5ebe6787f15 100644
--- a/spec/migrations/re_schedule_latest_pipeline_id_population_with_all_security_related_artifact_types_spec.rb
+++ b/spec/migrations/re_schedule_latest_pipeline_id_population_with_all_security_related_artifact_types_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
RSpec.describe ReScheduleLatestPipelineIdPopulationWithAllSecurityRelatedArtifactTypes,
- :suppress_gitlab_schemas_validate_connection do
+ :suppress_gitlab_schemas_validate_connection, feature_category: :vulnerability_management do
let(:namespaces) { table(:namespaces) }
let(:pipelines) { table(:ci_pipelines) }
let(:projects) { table(:projects) }
diff --git a/spec/migrations/recount_epic_cache_counts_spec.rb b/spec/migrations/recount_epic_cache_counts_spec.rb
index 56aa96cb40c..d065389a726 100644
--- a/spec/migrations/recount_epic_cache_counts_spec.rb
+++ b/spec/migrations/recount_epic_cache_counts_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe RecountEpicCacheCounts, :migration do
+RSpec.describe RecountEpicCacheCounts, :migration, feature_category: :portfolio_management do
let(:migration) { described_class::MIGRATION }
describe '#up' do
diff --git a/spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_features_spec.rb b/spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_features_spec.rb
index 77824a743fb..80ecc23dfbe 100644
--- a/spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_features_spec.rb
+++ b/spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_features_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe RecreateIndexSecurityCiBuildsOnNameAndIdParserFeatures, :migration do
+RSpec.describe RecreateIndexSecurityCiBuildsOnNameAndIdParserFeatures, :migration, feature_category: :database do
let(:db) { described_class.new }
let(:pg_class) { table(:pg_class) }
let(:pg_index) { table(:pg_index) }
diff --git a/spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features_spec.rb b/spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features_spec.rb
index 8ec51d86779..c7709764727 100644
--- a/spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features_spec.rb
+++ b/spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe RecreateIndexSecurityCiBuildsOnNameAndIdParserWithNewFeatures, :migration do
+RSpec.describe RecreateIndexSecurityCiBuildsOnNameAndIdParserWithNewFeatures, :migration, feature_category: :continuous_integration do
let(:db) { described_class.new }
let(:pg_class) { table(:pg_class) }
let(:pg_index) { table(:pg_index) }
diff --git a/spec/migrations/remove_duplicate_dast_site_tokens_spec.rb b/spec/migrations/remove_duplicate_dast_site_tokens_spec.rb
index fed9941b2a4..2b21dc3b67f 100644
--- a/spec/migrations/remove_duplicate_dast_site_tokens_spec.rb
+++ b/spec/migrations/remove_duplicate_dast_site_tokens_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe RemoveDuplicateDastSiteTokens do
+RSpec.describe RemoveDuplicateDastSiteTokens, feature_category: :dynamic_application_security_testing do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:dast_site_tokens) { table(:dast_site_tokens) }
@@ -15,7 +15,7 @@ RSpec.describe RemoveDuplicateDastSiteTokens do
context 'when duplicate dast site tokens exists' do
# create duplicate dast site token
- let_it_be(:duplicate_url) { 'https://about.gitlab.com' }
+ let!(:duplicate_url) { 'https://about.gitlab.com' }
let!(:project2) { projects.create!(id: 2, namespace_id: namespace.id, path: 'project2') }
let!(:dast_site_token2) { dast_site_tokens.create!(project_id: project2.id, url: duplicate_url, token: SecureRandom.uuid) }
diff --git a/spec/migrations/remove_duplicate_dast_site_tokens_with_same_token_spec.rb b/spec/migrations/remove_duplicate_dast_site_tokens_with_same_token_spec.rb
index 57d677af5cf..6cc25b74d02 100644
--- a/spec/migrations/remove_duplicate_dast_site_tokens_with_same_token_spec.rb
+++ b/spec/migrations/remove_duplicate_dast_site_tokens_with_same_token_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe RemoveDuplicateDastSiteTokensWithSameToken do
+RSpec.describe RemoveDuplicateDastSiteTokensWithSameToken, feature_category: :dynamic_application_security_testing do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:dast_site_tokens) { table(:dast_site_tokens) }
@@ -15,8 +15,8 @@ RSpec.describe RemoveDuplicateDastSiteTokensWithSameToken do
context 'when duplicate dast site tokens exists' do
# create duplicate dast site token
- let_it_be(:duplicate_token) { 'duplicate_token' }
- let_it_be(:other_duplicate_token) { 'other_duplicate_token' }
+ let!(:duplicate_token) { 'duplicate_token' }
+ let!(:other_duplicate_token) { 'other_duplicate_token' }
let!(:project2) { projects.create!(id: 2, namespace_id: namespace.id, path: 'project2') }
let!(:dast_site_token2) { dast_site_tokens.create!(project_id: project2.id, url: 'https://gitlab2.com', token: duplicate_token) }
diff --git a/spec/migrations/remove_flowdock_integration_records_spec.rb b/spec/migrations/remove_flowdock_integration_records_spec.rb
new file mode 100644
index 00000000000..3f57515d18b
--- /dev/null
+++ b/spec/migrations/remove_flowdock_integration_records_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db/post_migrate/20221129124240_remove_flowdock_integration_records.rb')
+
+RSpec.describe RemoveFlowdockIntegrationRecords, feature_category: :integrations do
+ let(:integrations) { table(:integrations) }
+
+ before do
+ integrations.create!(type_new: 'Integrations::Flowdock')
+ integrations.create!(type_new: 'SomeOtherType')
+ end
+
+ it 'removes integrations records of type_new Integrations::Flowdock' do
+ expect(integrations.count).to eq(2)
+
+ migrate!
+
+ expect(integrations.count).to eq(1)
+ expect(integrations.first.type_new).to eq('SomeOtherType')
+ expect(integrations.where(type_new: 'Integrations::Flowdock')).to be_empty
+ end
+end
diff --git a/spec/migrations/remove_hipchat_service_records_spec.rb b/spec/migrations/remove_hipchat_service_records_spec.rb
index d218b6b23a5..b89572b069e 100644
--- a/spec/migrations/remove_hipchat_service_records_spec.rb
+++ b/spec/migrations/remove_hipchat_service_records_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe RemoveHipchatServiceRecords do
+RSpec.describe RemoveHipchatServiceRecords, feature_category: :integrations do
let(:services) { table(:services) }
before do
diff --git a/spec/migrations/remove_invalid_integrations_spec.rb b/spec/migrations/remove_invalid_integrations_spec.rb
index cab2d79998e..52adc087e0a 100644
--- a/spec/migrations/remove_invalid_integrations_spec.rb
+++ b/spec/migrations/remove_invalid_integrations_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe RemoveInvalidIntegrations, :migration do
+RSpec.describe RemoveInvalidIntegrations, :migration, feature_category: :integrations do
describe '#up' do
let!(:integrations) { table(:integrations) }
diff --git a/spec/migrations/remove_not_null_contraint_on_title_from_sprints_spec.rb b/spec/migrations/remove_not_null_contraint_on_title_from_sprints_spec.rb
index 198644fe183..91687d8d730 100644
--- a/spec/migrations/remove_not_null_contraint_on_title_from_sprints_spec.rb
+++ b/spec/migrations/remove_not_null_contraint_on_title_from_sprints_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe RemoveNotNullContraintOnTitleFromSprints, :migration do
+RSpec.describe RemoveNotNullContraintOnTitleFromSprints, :migration, feature_category: :team_planning do
let(:migration) { described_class.new }
let(:namespaces) { table(:namespaces) }
let(:sprints) { table(:sprints) }
diff --git a/spec/migrations/remove_records_without_group_from_webhooks_table_spec.rb b/spec/migrations/remove_records_without_group_from_webhooks_table_spec.rb
index c267e419b42..eabf6271ded 100644
--- a/spec/migrations/remove_records_without_group_from_webhooks_table_spec.rb
+++ b/spec/migrations/remove_records_without_group_from_webhooks_table_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
require_migration!
require_migration!('add_not_valid_foreign_key_to_group_hooks')
-RSpec.describe RemoveRecordsWithoutGroupFromWebhooksTable, schema: 20210330091751 do
+RSpec.describe RemoveRecordsWithoutGroupFromWebhooksTable, schema: 20210330091751, feature_category: :integrations do
let(:web_hooks) { table(:web_hooks) }
let(:groups) { table(:namespaces) }
diff --git a/spec/migrations/remove_schedule_and_status_from_pending_alert_escalations_spec.rb b/spec/migrations/remove_schedule_and_status_from_pending_alert_escalations_spec.rb
index f595261ff90..86e161cea43 100644
--- a/spec/migrations/remove_schedule_and_status_from_pending_alert_escalations_spec.rb
+++ b/spec/migrations/remove_schedule_and_status_from_pending_alert_escalations_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe RemoveScheduleAndStatusFromPendingAlertEscalations do
+RSpec.describe RemoveScheduleAndStatusFromPendingAlertEscalations, feature_category: :incident_management do
let(:escalations) { table(:incident_management_pending_alert_escalations) }
let(:schedule_index) { 'index_incident_management_pending_alert_escalations_on_schedule' }
let(:schedule_foreign_key) { 'fk_rails_fcbfd9338b' }
diff --git a/spec/migrations/remove_wiki_notes_spec.rb b/spec/migrations/remove_wiki_notes_spec.rb
index 2ffebdee106..55f58ef7be6 100644
--- a/spec/migrations/remove_wiki_notes_spec.rb
+++ b/spec/migrations/remove_wiki_notes_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe RemoveWikiNotes, :migration do
+RSpec.describe RemoveWikiNotes, :migration, feature_category: :team_planning do
let(:notes) { table(:notes) }
it 'removes all wiki notes' do
diff --git a/spec/migrations/rename_services_to_integrations_spec.rb b/spec/migrations/rename_services_to_integrations_spec.rb
index 812dd5efecb..a90b0bfabd2 100644
--- a/spec/migrations/rename_services_to_integrations_spec.rb
+++ b/spec/migrations/rename_services_to_integrations_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe RenameServicesToIntegrations do
+RSpec.describe RenameServicesToIntegrations, feature_category: :integrations do
let(:migration) { described_class.new }
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
diff --git a/spec/migrations/replace_external_wiki_triggers_spec.rb b/spec/migrations/replace_external_wiki_triggers_spec.rb
index 392ef76c5ba..c2bc5c44c77 100644
--- a/spec/migrations/replace_external_wiki_triggers_spec.rb
+++ b/spec/migrations/replace_external_wiki_triggers_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe ReplaceExternalWikiTriggers do
+RSpec.describe ReplaceExternalWikiTriggers, feature_category: :integrations do
let(:migration) { described_class.new }
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
diff --git a/spec/migrations/reschedule_backfill_imported_issue_search_data_spec.rb b/spec/migrations/reschedule_backfill_imported_issue_search_data_spec.rb
index 7581c201a59..fe730f452f7 100644
--- a/spec/migrations/reschedule_backfill_imported_issue_search_data_spec.rb
+++ b/spec/migrations/reschedule_backfill_imported_issue_search_data_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe RescheduleBackfillImportedIssueSearchData do
- let_it_be(:reschedule_migration) { described_class::MIGRATION }
+RSpec.describe RescheduleBackfillImportedIssueSearchData, feature_category: :global_search do
+ let!(:reschedule_migration) { described_class::MIGRATION }
def create_batched_migration(max_value:)
Gitlab::Database::BackgroundMigration::BatchedMigration
@@ -55,12 +55,23 @@ RSpec.describe RescheduleBackfillImportedIssueSearchData do
end
context 'when an issue is available' do
- let_it_be(:namespaces_table) { table(:namespaces) }
- let_it_be(:projects_table) { table(:projects) }
+ let!(:namespaces_table) { table(:namespaces) }
+ let!(:projects_table) { table(:projects) }
let(:namespace) { namespaces_table.create!(name: 'gitlab-org', path: 'gitlab-org') }
- let(:project) { projects_table.create!(name: 'gitlab', path: 'gitlab-org/gitlab-ce', namespace_id: namespace.id, project_namespace_id: namespace.id) } # rubocop:disable Layout/LineLength
- let(:issue) { table(:issues).create!(project_id: project.id, title: 'test title', description: 'test description') }
+
+ let(:project) do
+ projects_table.create!(
+ name: 'gitlab', path: 'gitlab-org/gitlab-ce', namespace_id: namespace.id, project_namespace_id: namespace.id
+ )
+ end
+
+ let(:issue) do
+ table(:issues).create!(
+ project_id: project.id, namespace_id: project.project_namespace_id,
+ title: 'test title', description: 'test description'
+ )
+ end
before do
create_batched_migration(max_value: max_value)
diff --git a/spec/migrations/reschedule_delete_orphaned_deployments_spec.rb b/spec/migrations/reschedule_delete_orphaned_deployments_spec.rb
index eb91602388c..bbc4494837a 100644
--- a/spec/migrations/reschedule_delete_orphaned_deployments_spec.rb
+++ b/spec/migrations/reschedule_delete_orphaned_deployments_spec.rb
@@ -4,7 +4,8 @@ require 'spec_helper'
require_migration!
-RSpec.describe RescheduleDeleteOrphanedDeployments, :sidekiq, schema: 20210617161348 do
+RSpec.describe RescheduleDeleteOrphanedDeployments, :sidekiq, schema: 20210617161348,
+ feature_category: :continuous_delivery do
let!(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
let!(:project) { table(:projects).create!(namespace_id: namespace.id) }
let!(:environment) { table(:environments).create!(name: 'production', slug: 'production', project_id: project.id) }
diff --git a/spec/migrations/reschedule_issue_work_item_type_id_backfill_spec.rb b/spec/migrations/reschedule_issue_work_item_type_id_backfill_spec.rb
index 126d49790a5..1443ff09241 100644
--- a/spec/migrations/reschedule_issue_work_item_type_id_backfill_spec.rb
+++ b/spec/migrations/reschedule_issue_work_item_type_id_backfill_spec.rb
@@ -3,11 +3,11 @@
require 'spec_helper'
require_migration!
-RSpec.describe RescheduleIssueWorkItemTypeIdBackfill, :migration do
- let_it_be(:migration) { described_class::MIGRATION }
- let_it_be(:interval) { 2.minutes }
- let_it_be(:issue_type_enum) { { issue: 0, incident: 1, test_case: 2, requirement: 3, task: 4 } }
- let_it_be(:base_work_item_type_ids) do
+RSpec.describe RescheduleIssueWorkItemTypeIdBackfill, :migration, feature_category: :team_planning do
+ let!(:migration) { described_class::MIGRATION }
+ let!(:interval) { 2.minutes }
+ let!(:issue_type_enum) { { issue: 0, incident: 1, test_case: 2, requirement: 3, task: 4 } }
+ let!(:base_work_item_type_ids) do
table(:work_item_types).where(namespace_id: nil).order(:base_type).each_with_object({}) do |type, hash|
hash[type.base_type] = type.id
end
diff --git a/spec/migrations/reschedule_migrate_shared_vulnerability_scanners_spec.rb b/spec/migrations/reschedule_migrate_shared_vulnerability_scanners_spec.rb
index e8253f39c68..48422de81fe 100644
--- a/spec/migrations/reschedule_migrate_shared_vulnerability_scanners_spec.rb
+++ b/spec/migrations/reschedule_migrate_shared_vulnerability_scanners_spec.rb
@@ -4,7 +4,7 @@ require "spec_helper"
require_migration!
-RSpec.describe RescheduleMigrateSharedVulnerabilityScanners, :migration do
+RSpec.describe RescheduleMigrateSharedVulnerabilityScanners, :migration, feature_category: :vulnerability_management do
include Gitlab::Database::Migrations::BatchedBackgroundMigrationHelpers
def connection
diff --git a/spec/migrations/reset_job_token_scope_enabled_again_spec.rb b/spec/migrations/reset_job_token_scope_enabled_again_spec.rb
index 8f9e12852e1..9f1180b6e24 100644
--- a/spec/migrations/reset_job_token_scope_enabled_again_spec.rb
+++ b/spec/migrations/reset_job_token_scope_enabled_again_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe ResetJobTokenScopeEnabledAgain do
+RSpec.describe ResetJobTokenScopeEnabledAgain, feature_category: :continuous_integration do
let(:settings) { table(:project_ci_cd_settings) }
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
diff --git a/spec/migrations/reset_job_token_scope_enabled_spec.rb b/spec/migrations/reset_job_token_scope_enabled_spec.rb
index fb7bd78c11f..4ce9078246a 100644
--- a/spec/migrations/reset_job_token_scope_enabled_spec.rb
+++ b/spec/migrations/reset_job_token_scope_enabled_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe ResetJobTokenScopeEnabled do
+RSpec.describe ResetJobTokenScopeEnabled, feature_category: :continuous_integration do
let(:settings) { table(:project_ci_cd_settings) }
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
diff --git a/spec/migrations/reset_severity_levels_to_new_default_spec.rb b/spec/migrations/reset_severity_levels_to_new_default_spec.rb
index c352f1f3cee..83e57b852a0 100644
--- a/spec/migrations/reset_severity_levels_to_new_default_spec.rb
+++ b/spec/migrations/reset_severity_levels_to_new_default_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe ResetSeverityLevelsToNewDefault do
+RSpec.describe ResetSeverityLevelsToNewDefault, feature_category: :source_code_management do
let(:approval_project_rules) { table(:approval_project_rules) }
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
diff --git a/spec/migrations/retry_backfill_traversal_ids_spec.rb b/spec/migrations/retry_backfill_traversal_ids_spec.rb
index 910be9f2c69..f3658d1b8a3 100644
--- a/spec/migrations/retry_backfill_traversal_ids_spec.rb
+++ b/spec/migrations/retry_backfill_traversal_ids_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
require_migration!
-RSpec.describe RetryBackfillTraversalIds, :migration do
+RSpec.describe RetryBackfillTraversalIds, :migration, feature_category: :subgroups do
include ReloadHelpers
- let_it_be(:namespaces_table) { table(:namespaces) }
+ let!(:namespaces_table) { table(:namespaces) }
context 'when BackfillNamespaceTraversalIdsRoots jobs are pending' do
before do
diff --git a/spec/migrations/sanitize_confidential_note_todos_spec.rb b/spec/migrations/sanitize_confidential_note_todos_spec.rb
index 00dece82cc1..142378e07e1 100644
--- a/spec/migrations/sanitize_confidential_note_todos_spec.rb
+++ b/spec/migrations/sanitize_confidential_note_todos_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe SanitizeConfidentialNoteTodos do
+RSpec.describe SanitizeConfidentialNoteTodos, feature_category: :team_planning do
let(:migration) { described_class::MIGRATION }
describe '#up' do
diff --git a/spec/migrations/schedule_add_primary_email_to_emails_if_user_confirmed_spec.rb b/spec/migrations/schedule_add_primary_email_to_emails_if_user_confirmed_spec.rb
index c66ac1bd7e9..98d3e9b7c7c 100644
--- a/spec/migrations/schedule_add_primary_email_to_emails_if_user_confirmed_spec.rb
+++ b/spec/migrations/schedule_add_primary_email_to_emails_if_user_confirmed_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleAddPrimaryEmailToEmailsIfUserConfirmed, :sidekiq do
+RSpec.describe ScheduleAddPrimaryEmailToEmailsIfUserConfirmed, :sidekiq, feature_category: :users do
let(:migration) { described_class.new }
let(:users) { table(:users) }
diff --git a/spec/migrations/schedule_backfill_cluster_agents_has_vulnerabilities_spec.rb b/spec/migrations/schedule_backfill_cluster_agents_has_vulnerabilities_spec.rb
index 675cc332e69..84764c89adb 100644
--- a/spec/migrations/schedule_backfill_cluster_agents_has_vulnerabilities_spec.rb
+++ b/spec/migrations/schedule_backfill_cluster_agents_has_vulnerabilities_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleBackfillClusterAgentsHasVulnerabilities do
- let_it_be(:batched_migration) { described_class::MIGRATION }
+RSpec.describe ScheduleBackfillClusterAgentsHasVulnerabilities, feature_category: :vulnerability_management do
+ let!(:batched_migration) { described_class::MIGRATION }
it 'schedules background jobs for each batch of cluster agents' do
reversible_migration do |migration|
diff --git a/spec/migrations/schedule_backfill_draft_status_on_merge_requests_corrected_regex_spec.rb b/spec/migrations/schedule_backfill_draft_status_on_merge_requests_corrected_regex_spec.rb
index 9d7651d01ed..8a14bf58698 100644
--- a/spec/migrations/schedule_backfill_draft_status_on_merge_requests_corrected_regex_spec.rb
+++ b/spec/migrations/schedule_backfill_draft_status_on_merge_requests_corrected_regex_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe ScheduleBackfillDraftStatusOnMergeRequestsCorrectedRegex, :sidekiq do
+RSpec.describe ScheduleBackfillDraftStatusOnMergeRequestsCorrectedRegex, :sidekiq, feature_category: :code_review do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:merge_requests) { table(:merge_requests) }
diff --git a/spec/migrations/schedule_backfilling_the_namespace_id_for_vulnerability_reads_spec.rb b/spec/migrations/schedule_backfilling_the_namespace_id_for_vulnerability_reads_spec.rb
index e03096de98d..e547b321c52 100644
--- a/spec/migrations/schedule_backfilling_the_namespace_id_for_vulnerability_reads_spec.rb
+++ b/spec/migrations/schedule_backfilling_the_namespace_id_for_vulnerability_reads_spec.rb
@@ -4,8 +4,8 @@ require 'spec_helper'
require_migration!
-RSpec.describe ScheduleBackfillingTheNamespaceIdForVulnerabilityReads do
- let_it_be(:migration) { described_class::MIGRATION_NAME }
+RSpec.describe ScheduleBackfillingTheNamespaceIdForVulnerabilityReads, feature_category: :vulnerability_management do
+ let!(:migration) { described_class::MIGRATION_NAME }
describe '#up' do
it 'schedules background jobs for each batch of vulnerabilities' do
diff --git a/spec/migrations/schedule_copy_ci_builds_columns_to_security_scans2_spec.rb b/spec/migrations/schedule_copy_ci_builds_columns_to_security_scans2_spec.rb
index 67d54ea92a0..63678a094a7 100644
--- a/spec/migrations/schedule_copy_ci_builds_columns_to_security_scans2_spec.rb
+++ b/spec/migrations/schedule_copy_ci_builds_columns_to_security_scans2_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleCopyCiBuildsColumnsToSecurityScans2 do
+RSpec.describe ScheduleCopyCiBuildsColumnsToSecurityScans2, feature_category: :dependency_scanning do
it 'is a no-op' do
migrate!
end
diff --git a/spec/migrations/schedule_disable_expiration_policies_linked_to_no_container_images_spec.rb b/spec/migrations/schedule_disable_expiration_policies_linked_to_no_container_images_spec.rb
index 888d306f893..ebcc3fda0a3 100644
--- a/spec/migrations/schedule_disable_expiration_policies_linked_to_no_container_images_spec.rb
+++ b/spec/migrations/schedule_disable_expiration_policies_linked_to_no_container_images_spec.rb
@@ -4,22 +4,22 @@ require 'spec_helper'
require_migration!
-RSpec.describe ScheduleDisableExpirationPoliciesLinkedToNoContainerImages do
- let_it_be(:projects) { table(:projects) }
- let_it_be(:container_expiration_policies) { table(:container_expiration_policies) }
- let_it_be(:container_repositories) { table(:container_repositories) }
- let_it_be(:namespaces) { table(:namespaces) }
- let_it_be(:namespace) { namespaces.create!(name: 'test', path: 'test') }
-
- let_it_be(:policy1) { create_expiration_policy(id: 1, enabled: true) }
- let_it_be(:policy2) { create_expiration_policy(id: 2, enabled: false) }
- let_it_be(:policy3) { create_expiration_policy(id: 3, enabled: false) }
- let_it_be(:policy4) { create_expiration_policy(id: 4, enabled: true) }
- let_it_be(:policy5) { create_expiration_policy(id: 5, enabled: false) }
- let_it_be(:policy6) { create_expiration_policy(id: 6, enabled: false) }
- let_it_be(:policy7) { create_expiration_policy(id: 7, enabled: true) }
- let_it_be(:policy8) { create_expiration_policy(id: 8, enabled: true) }
- let_it_be(:policy9) { create_expiration_policy(id: 9, enabled: true) }
+RSpec.describe ScheduleDisableExpirationPoliciesLinkedToNoContainerImages, feature_category: :container_registry do
+ let!(:projects) { table(:projects) }
+ let!(:container_expiration_policies) { table(:container_expiration_policies) }
+ let!(:container_repositories) { table(:container_repositories) }
+ let!(:namespaces) { table(:namespaces) }
+ let!(:namespace) { namespaces.create!(name: 'test', path: 'test') }
+
+ let!(:policy1) { create_expiration_policy(id: 1, enabled: true) }
+ let!(:policy2) { create_expiration_policy(id: 2, enabled: false) }
+ let!(:policy3) { create_expiration_policy(id: 3, enabled: false) }
+ let!(:policy4) { create_expiration_policy(id: 4, enabled: true) }
+ let!(:policy5) { create_expiration_policy(id: 5, enabled: false) }
+ let!(:policy6) { create_expiration_policy(id: 6, enabled: false) }
+ let!(:policy7) { create_expiration_policy(id: 7, enabled: true) }
+ let!(:policy8) { create_expiration_policy(id: 8, enabled: true) }
+ let!(:policy9) { create_expiration_policy(id: 9, enabled: true) }
it 'schedules background migrations', :aggregate_failures do
stub_const("#{described_class}::BATCH_SIZE", 2)
diff --git a/spec/migrations/schedule_fix_incorrect_max_seats_used2_spec.rb b/spec/migrations/schedule_fix_incorrect_max_seats_used2_spec.rb
index 3720be6cf3e..26764f855b7 100644
--- a/spec/migrations/schedule_fix_incorrect_max_seats_used2_spec.rb
+++ b/spec/migrations/schedule_fix_incorrect_max_seats_used2_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleFixIncorrectMaxSeatsUsed2, :migration do
+RSpec.describe ScheduleFixIncorrectMaxSeatsUsed2, :migration, feature_category: :purchase do
let(:migration_name) { described_class::MIGRATION.to_s.demodulize }
describe '#up' do
diff --git a/spec/migrations/schedule_fix_incorrect_max_seats_used_spec.rb b/spec/migrations/schedule_fix_incorrect_max_seats_used_spec.rb
index 74258f03630..194a1d39ad1 100644
--- a/spec/migrations/schedule_fix_incorrect_max_seats_used_spec.rb
+++ b/spec/migrations/schedule_fix_incorrect_max_seats_used_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleFixIncorrectMaxSeatsUsed, :migration do
+RSpec.describe ScheduleFixIncorrectMaxSeatsUsed, :migration, feature_category: :purchase do
let(:migration) { described_class.new }
describe '#up' do
diff --git a/spec/migrations/schedule_fixing_security_scan_statuses_spec.rb b/spec/migrations/schedule_fixing_security_scan_statuses_spec.rb
new file mode 100644
index 00000000000..c4c7819bda7
--- /dev/null
+++ b/spec/migrations/schedule_fixing_security_scan_statuses_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ScheduleFixingSecurityScanStatuses, :suppress_gitlab_schemas_validate_connection,
+ feature_category: :vulnerability_management do
+ let!(:namespaces) { table(:namespaces) }
+ let!(:projects) { table(:projects) }
+ let!(:pipelines) { table(:ci_pipelines) }
+ let!(:builds) { table(:ci_builds) }
+ let!(:security_scans) { table(:security_scans) }
+
+ let!(:namespace) { namespaces.create!(name: "foo", path: "bar") }
+ let!(:project) { projects.create!(namespace_id: namespace.id, project_namespace_id: namespace.id) }
+ let!(:pipeline) do
+ pipelines.create!(project_id: project.id, ref: 'master', sha: 'adf43c3a', status: 'success', partition_id: 1)
+ end
+
+ let!(:ci_build) { builds.create!(commit_id: pipeline.id, retried: false, type: 'Ci::Build', partition_id: 1) }
+
+ let!(:security_scan_1) { security_scans.create!(build_id: ci_build.id, scan_type: 1, created_at: 91.days.ago) }
+ let!(:security_scan_2) { security_scans.create!(build_id: ci_build.id, scan_type: 2) }
+
+ let(:com?) { false }
+ let(:dev_or_test_env?) { false }
+ let(:migration) { described_class::MIGRATION }
+
+ before do
+ allow(::Gitlab).to receive(:com?).and_return(com?)
+ allow(::Gitlab).to receive(:dev_or_test_env?).and_return(dev_or_test_env?)
+
+ migrate!
+ end
+
+ describe '#up' do
+ shared_examples_for 'scheduler for fixing the security scans status' do
+ it 'schedules background job' do
+ expect(migration).to have_scheduled_batched_migration(
+ table_name: :security_scans,
+ column_name: :id,
+ interval: 2.minutes,
+ batch_size: 10_000,
+ max_batch_size: 50_000,
+ sub_batch_size: 100,
+ batch_min_value: security_scan_2.id
+ )
+ end
+ end
+
+ context 'when the migration does not run on GitLab.com or development environment' do
+ it 'does not schedule the migration' do
+ expect('FixSecurityScanStatuses').not_to have_scheduled_batched_migration
+ end
+ end
+
+ context 'when the migration runs on GitLab.com' do
+ let(:com?) { true }
+
+ it_behaves_like 'scheduler for fixing the security scans status'
+ end
+
+ context 'when the migration runs on dev environment' do
+ let(:dev_or_test_env?) { true }
+
+ it_behaves_like 'scheduler for fixing the security scans status'
+ end
+ end
+
+ describe '#down' do
+ it 'deletes all batched migration records' do
+ schema_migrate_down!
+
+ expect(migration).not_to have_scheduled_batched_migration
+ end
+ end
+end
diff --git a/spec/migrations/schedule_populate_requirements_issue_id_spec.rb b/spec/migrations/schedule_populate_requirements_issue_id_spec.rb
index 2702c000b60..000c42cc4fc 100644
--- a/spec/migrations/schedule_populate_requirements_issue_id_spec.rb
+++ b/spec/migrations/schedule_populate_requirements_issue_id_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe SchedulePopulateRequirementsIssueId do
+RSpec.describe SchedulePopulateRequirementsIssueId, feature_category: :requirements_management do
include MigrationHelpers::WorkItemTypesHelper
let(:issues) { table(:issues) }
@@ -48,7 +48,7 @@ RSpec.describe SchedulePopulateRequirementsIssueId do
# Create one requirement with issue_id present, to make
# sure a job won't be scheduled for it
- work_item_type_id = work_item_types_table.find_by(namespace_id: nil, name: 'Issue').id
+ work_item_type_id = table(:work_item_types).find_by(namespace_id: nil, name: 'Issue').id
issue = issues.create!(state_id: 1, work_item_type_id: work_item_type_id)
create_requirement(iid: 2, title: 'r 2', issue_id: issue.id)
diff --git a/spec/migrations/schedule_purging_stale_security_scans_spec.rb b/spec/migrations/schedule_purging_stale_security_scans_spec.rb
index b5a38634b58..b39baa145ff 100644
--- a/spec/migrations/schedule_purging_stale_security_scans_spec.rb
+++ b/spec/migrations/schedule_purging_stale_security_scans_spec.rb
@@ -3,17 +3,18 @@
require 'spec_helper'
require_migration!
-RSpec.describe SchedulePurgingStaleSecurityScans do
- let_it_be(:namespaces) { table(:namespaces) }
- let_it_be(:projects) { table(:projects) }
- let_it_be(:pipelines) { table(:ci_pipelines) }
- let_it_be(:builds) { table(:ci_builds) }
- let_it_be(:security_scans) { table(:security_scans) }
-
- let_it_be(:namespace) { namespaces.create!(name: "foo", path: "bar") }
- let_it_be(:project) { projects.create!(namespace_id: namespace.id, project_namespace_id: namespace.id) }
- let_it_be(:pipeline) { pipelines.create!(project_id: project.id, ref: 'master', sha: 'adf43c3a', status: 'success') }
- let_it_be(:ci_build) { builds.create!(commit_id: pipeline.id, retried: false, type: 'Ci::Build') }
+RSpec.describe SchedulePurgingStaleSecurityScans, :suppress_gitlab_schemas_validate_connection,
+feature_category: :vulnerability_management do
+ let!(:namespaces) { table(:namespaces) }
+ let!(:projects) { table(:projects) }
+ let!(:pipelines) { table(:ci_pipelines) }
+ let!(:builds) { table(:ci_builds) }
+ let!(:security_scans) { table(:security_scans) }
+
+ let!(:namespace) { namespaces.create!(name: "foo", path: "bar") }
+ let!(:project) { projects.create!(namespace_id: namespace.id, project_namespace_id: namespace.id) }
+ let!(:pipeline) { pipelines.create!(project_id: project.id, ref: 'master', sha: 'adf43c3a', status: 'success') }
+ let!(:ci_build) { builds.create!(commit_id: pipeline.id, retried: false, type: 'Ci::Build') }
let!(:security_scan_1) { security_scans.create!(build_id: ci_build.id, scan_type: 1, created_at: 92.days.ago) }
let!(:security_scan_2) { security_scans.create!(build_id: ci_build.id, scan_type: 2, created_at: 91.days.ago) }
diff --git a/spec/migrations/schedule_recalculate_vulnerability_finding_signatures_for_findings_spec.rb b/spec/migrations/schedule_recalculate_vulnerability_finding_signatures_for_findings_spec.rb
index 9b62dd79e08..8903a32285e 100644
--- a/spec/migrations/schedule_recalculate_vulnerability_finding_signatures_for_findings_spec.rb
+++ b/spec/migrations/schedule_recalculate_vulnerability_finding_signatures_for_findings_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleRecalculateVulnerabilityFindingSignaturesForFindings, :migration do
+RSpec.describe ScheduleRecalculateVulnerabilityFindingSignaturesForFindings, :migration,
+feature_category: :vulnerability_management do
before do
allow(Gitlab).to receive(:ee?).and_return(ee?)
stub_const("#{described_class.name}::BATCH_SIZE", 2)
@@ -20,21 +21,21 @@ RSpec.describe ScheduleRecalculateVulnerabilityFindingSignaturesForFindings, :mi
context 'when the Gitlab instance is EE' do
let(:ee?) { true }
- let_it_be(:namespaces) { table(:namespaces) }
- let_it_be(:projects) { table(:projects) }
- let_it_be(:findings) { table(:vulnerability_occurrences) }
- let_it_be(:scanners) { table(:vulnerability_scanners) }
- let_it_be(:identifiers) { table(:vulnerability_identifiers) }
- let_it_be(:vulnerability_finding_signatures) { table(:vulnerability_finding_signatures) }
+ let!(:namespaces) { table(:namespaces) }
+ let!(:projects) { table(:projects) }
+ let!(:findings) { table(:vulnerability_occurrences) }
+ let!(:scanners) { table(:vulnerability_scanners) }
+ let!(:identifiers) { table(:vulnerability_identifiers) }
+ let!(:vulnerability_finding_signatures) { table(:vulnerability_finding_signatures) }
- let_it_be(:namespace) { namespaces.create!(name: 'test', path: 'test') }
- let_it_be(:project) { projects.create!(namespace_id: namespace.id, name: 'gitlab', path: 'gitlab') }
+ let!(:namespace) { namespaces.create!(name: 'test', path: 'test') }
+ let!(:project) { projects.create!(namespace_id: namespace.id, name: 'gitlab', path: 'gitlab') }
- let_it_be(:scanner) do
+ let!(:scanner) do
scanners.create!(project_id: project.id, external_id: 'trivy', name: 'Security Scanner')
end
- let_it_be(:identifier) do
+ let!(:identifier) do
identifiers.create!(project_id: project.id,
fingerprint: 'd432c2ad2953e8bd587a3a43b3ce309b5b0154c123',
external_type: 'SECURITY_ID',
@@ -42,14 +43,14 @@ RSpec.describe ScheduleRecalculateVulnerabilityFindingSignaturesForFindings, :mi
name: 'SECURITY_IDENTIFIER 0')
end
- let_it_be(:finding1) { findings.create!(finding_params) }
- let_it_be(:signature1) { vulnerability_finding_signatures.create!(finding_id: finding1.id, algorithm_type: 0, signature_sha: ::Digest::SHA1.digest(SecureRandom.hex(50))) }
+ let!(:finding1) { findings.create!(finding_params) }
+ let!(:signature1) { vulnerability_finding_signatures.create!(finding_id: finding1.id, algorithm_type: 0, signature_sha: ::Digest::SHA1.digest(SecureRandom.hex(50))) }
- let_it_be(:finding2) { findings.create!(finding_params) }
- let_it_be(:signature2) { vulnerability_finding_signatures.create!(finding_id: finding2.id, algorithm_type: 0, signature_sha: ::Digest::SHA1.digest(SecureRandom.hex(50))) }
+ let!(:finding2) { findings.create!(finding_params) }
+ let!(:signature2) { vulnerability_finding_signatures.create!(finding_id: finding2.id, algorithm_type: 0, signature_sha: ::Digest::SHA1.digest(SecureRandom.hex(50))) }
- let_it_be(:finding3) { findings.create!(finding_params) }
- let_it_be(:signature3) { vulnerability_finding_signatures.create!(finding_id: finding3.id, algorithm_type: 0, signature_sha: ::Digest::SHA1.digest(SecureRandom.hex(50))) }
+ let!(:finding3) { findings.create!(finding_params) }
+ let!(:signature3) { vulnerability_finding_signatures.create!(finding_id: finding3.id, algorithm_type: 0, signature_sha: ::Digest::SHA1.digest(SecureRandom.hex(50))) }
# this migration is now a no-op
it 'does not schedule the background jobs', :aggregate_failure do
diff --git a/spec/migrations/schedule_security_setting_creation_spec.rb b/spec/migrations/schedule_security_setting_creation_spec.rb
index e1b7b540d7f..edabb2a2299 100644
--- a/spec/migrations/schedule_security_setting_creation_spec.rb
+++ b/spec/migrations/schedule_security_setting_creation_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleSecuritySettingCreation, :sidekiq do
+RSpec.describe ScheduleSecuritySettingCreation, :sidekiq, feature_category: :projects do
describe '#up' do
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
diff --git a/spec/migrations/schedule_set_correct_vulnerability_state_spec.rb b/spec/migrations/schedule_set_correct_vulnerability_state_spec.rb
index 08dccf1f37a..e888a1132c0 100644
--- a/spec/migrations/schedule_set_correct_vulnerability_state_spec.rb
+++ b/spec/migrations/schedule_set_correct_vulnerability_state_spec.rb
@@ -4,8 +4,8 @@ require 'spec_helper'
require_migration!
-RSpec.describe ScheduleSetCorrectVulnerabilityState do
- let_it_be(:migration) { described_class::MIGRATION_NAME }
+RSpec.describe ScheduleSetCorrectVulnerabilityState, feature_category: :vulnerability_management do
+ let!(:migration) { described_class::MIGRATION_NAME }
describe '#up' do
it 'schedules background jobs for each batch of vulnerabilities' do
diff --git a/spec/migrations/schedule_update_timelogs_null_spent_at_spec.rb b/spec/migrations/schedule_update_timelogs_null_spent_at_spec.rb
index a81059518e6..99ee9e58f4e 100644
--- a/spec/migrations/schedule_update_timelogs_null_spent_at_spec.rb
+++ b/spec/migrations/schedule_update_timelogs_null_spent_at_spec.rb
@@ -3,18 +3,18 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleUpdateTimelogsNullSpentAt do
- let_it_be(:namespace) { table(:namespaces).create!(name: 'namespace', path: 'namespace') }
- let_it_be(:project) { table(:projects).create!(namespace_id: namespace.id) }
- let_it_be(:issue) { table(:issues).create!(project_id: project.id) }
- let_it_be(:merge_request) { table(:merge_requests).create!(target_project_id: project.id, source_branch: 'master', target_branch: 'feature') }
- let_it_be(:timelog1) { create_timelog!(merge_request_id: merge_request.id) }
- let_it_be(:timelog2) { create_timelog!(merge_request_id: merge_request.id) }
- let_it_be(:timelog3) { create_timelog!(merge_request_id: merge_request.id) }
- let_it_be(:timelog4) { create_timelog!(issue_id: issue.id) }
- let_it_be(:timelog5) { create_timelog!(issue_id: issue.id) }
-
- before_all do
+RSpec.describe ScheduleUpdateTimelogsNullSpentAt, feature_category: :team_planning do
+ let!(:namespace) { table(:namespaces).create!(name: 'namespace', path: 'namespace') }
+ let!(:project) { table(:projects).create!(namespace_id: namespace.id) }
+ let!(:issue) { table(:issues).create!(project_id: project.id) }
+ let!(:merge_request) { table(:merge_requests).create!(target_project_id: project.id, source_branch: 'master', target_branch: 'feature') }
+ let!(:timelog1) { create_timelog!(merge_request_id: merge_request.id) }
+ let!(:timelog2) { create_timelog!(merge_request_id: merge_request.id) }
+ let!(:timelog3) { create_timelog!(merge_request_id: merge_request.id) }
+ let!(:timelog4) { create_timelog!(issue_id: issue.id) }
+ let!(:timelog5) { create_timelog!(issue_id: issue.id) }
+
+ before do
table(:timelogs).where.not(id: timelog3.id).update_all(spent_at: nil)
end
diff --git a/spec/migrations/schedule_update_timelogs_project_id_spec.rb b/spec/migrations/schedule_update_timelogs_project_id_spec.rb
index b9130fd86be..5ce3f7dd36c 100644
--- a/spec/migrations/schedule_update_timelogs_project_id_spec.rb
+++ b/spec/migrations/schedule_update_timelogs_project_id_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleUpdateTimelogsProjectId do
+RSpec.describe ScheduleUpdateTimelogsProjectId, feature_category: :team_planning do
let!(:namespace) { table(:namespaces).create!(name: 'namespace', path: 'namespace') }
let!(:project) { table(:projects).create!(namespace_id: namespace.id) }
let!(:issue) { table(:issues).create!(project_id: project.id) }
diff --git a/spec/migrations/schedule_update_users_where_two_factor_auth_required_from_group_spec.rb b/spec/migrations/schedule_update_users_where_two_factor_auth_required_from_group_spec.rb
index 2fe739659f0..c9f22c02a0b 100644
--- a/spec/migrations/schedule_update_users_where_two_factor_auth_required_from_group_spec.rb
+++ b/spec/migrations/schedule_update_users_where_two_factor_auth_required_from_group_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ScheduleUpdateUsersWhereTwoFactorAuthRequiredFromGroup do
+RSpec.describe ScheduleUpdateUsersWhereTwoFactorAuthRequiredFromGroup, feature_category: :require_two_factor_authentication_from_group do
let(:users) { table(:users) }
let!(:user_1) { users.create!(require_two_factor_authentication_from_group: false, name: "user1", email: "user1@example.com", projects_limit: 1) }
let!(:user_2) { users.create!(require_two_factor_authentication_from_group: true, name: "user2", email: "user2@example.com", projects_limit: 1) }
diff --git a/spec/migrations/set_default_job_token_scope_true_spec.rb b/spec/migrations/set_default_job_token_scope_true_spec.rb
index e7c77357318..25f4f07e15a 100644
--- a/spec/migrations/set_default_job_token_scope_true_spec.rb
+++ b/spec/migrations/set_default_job_token_scope_true_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe SetDefaultJobTokenScopeTrue, schema: 20210819153805 do
+RSpec.describe SetDefaultJobTokenScopeTrue, schema: 20210819153805, feature_category: :continuous_integration do
let(:ci_cd_settings) { table(:project_ci_cd_settings) }
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
diff --git a/spec/migrations/set_email_confirmation_setting_before_removing_send_user_confirmation_email_column_spec.rb b/spec/migrations/set_email_confirmation_setting_before_removing_send_user_confirmation_email_column_spec.rb
new file mode 100644
index 00000000000..4303713744e
--- /dev/null
+++ b/spec/migrations/set_email_confirmation_setting_before_removing_send_user_confirmation_email_column_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe SetEmailConfirmationSettingBeforeRemovingSendUserConfirmationEmailColumn, feature_category: :users do
+ let(:migration) { described_class.new }
+ let(:application_settings_table) { table(:application_settings) }
+
+ describe '#up' do
+ context "when 'send_user_confirmation_email' is set to 'true'" do
+ it "updates 'email_confirmation_setting' to '2' (hard)" do
+ application_settings_table.create!(send_user_confirmation_email: true, email_confirmation_setting: 0)
+
+ migration.up
+
+ expect(application_settings_table.last.email_confirmation_setting).to eq 2
+ end
+ end
+
+ context "when 'send_user_confirmation_email' is set to 'false'" do
+ it "updates 'email_confirmation_setting' to '0' (off)" do
+ application_settings_table.create!(send_user_confirmation_email: false, email_confirmation_setting: 0)
+
+ migration.up
+
+ expect(application_settings_table.last.email_confirmation_setting).to eq 0
+ end
+ end
+ end
+
+ describe '#down' do
+ it "updates 'email_confirmation_setting' to default value: '0' (off)" do
+ application_settings_table.create!(send_user_confirmation_email: true, email_confirmation_setting: 2)
+
+ migration.down
+
+ expect(application_settings_table.last.email_confirmation_setting).to eq 0
+ end
+ end
+end
diff --git a/spec/migrations/set_email_confirmation_setting_from_send_user_confirmation_email_setting_spec.rb b/spec/migrations/set_email_confirmation_setting_from_send_user_confirmation_email_setting_spec.rb
index 761c0ef2fdb..e08aa8679a1 100644
--- a/spec/migrations/set_email_confirmation_setting_from_send_user_confirmation_email_setting_spec.rb
+++ b/spec/migrations/set_email_confirmation_setting_from_send_user_confirmation_email_setting_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe SetEmailConfirmationSettingFromSendUserConfirmationEmailSetting do
+RSpec.describe SetEmailConfirmationSettingFromSendUserConfirmationEmailSetting, feature_category: :users do
let(:migration) { described_class.new }
let(:application_settings_table) { table(:application_settings) }
diff --git a/spec/migrations/slice_merge_request_diff_commit_migrations_spec.rb b/spec/migrations/slice_merge_request_diff_commit_migrations_spec.rb
index b03a5c41a11..fdbd8093fa5 100644
--- a/spec/migrations/slice_merge_request_diff_commit_migrations_spec.rb
+++ b/spec/migrations/slice_merge_request_diff_commit_migrations_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe SliceMergeRequestDiffCommitMigrations, :migration do
+RSpec.describe SliceMergeRequestDiffCommitMigrations, :migration, feature_category: :code_review do
let(:migration) { described_class.new }
describe '#up' do
diff --git a/spec/migrations/start_backfill_ci_queuing_tables_spec.rb b/spec/migrations/start_backfill_ci_queuing_tables_spec.rb
index 08fd244089f..c308a16d5b8 100644
--- a/spec/migrations/start_backfill_ci_queuing_tables_spec.rb
+++ b/spec/migrations/start_backfill_ci_queuing_tables_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe StartBackfillCiQueuingTables, :suppress_gitlab_schemas_validate_connection do
+RSpec.describe StartBackfillCiQueuingTables, :suppress_gitlab_schemas_validate_connection,
+feature_category: :continuous_integration do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:builds) { table(:ci_builds) }
diff --git a/spec/migrations/steal_merge_request_diff_commit_users_migration_spec.rb b/spec/migrations/steal_merge_request_diff_commit_users_migration_spec.rb
index 4fb4ba61a34..d2cd7a6980d 100644
--- a/spec/migrations/steal_merge_request_diff_commit_users_migration_spec.rb
+++ b/spec/migrations/steal_merge_request_diff_commit_users_migration_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe StealMergeRequestDiffCommitUsersMigration, :migration do
+RSpec.describe StealMergeRequestDiffCommitUsersMigration, :migration, feature_category: :source_code_management do
let(:migration) { described_class.new }
describe '#up' do
diff --git a/spec/migrations/sync_new_amount_used_for_ci_namespace_monthly_usages_spec.rb b/spec/migrations/sync_new_amount_used_for_ci_namespace_monthly_usages_spec.rb
index 9a17f375f82..da8790f4450 100644
--- a/spec/migrations/sync_new_amount_used_for_ci_namespace_monthly_usages_spec.rb
+++ b/spec/migrations/sync_new_amount_used_for_ci_namespace_monthly_usages_spec.rb
@@ -4,7 +4,8 @@ require 'spec_helper'
require_migration!
-RSpec.describe SyncNewAmountUsedForCiNamespaceMonthlyUsages, migration: :gitlab_ci do
+RSpec.describe SyncNewAmountUsedForCiNamespaceMonthlyUsages, migration: :gitlab_ci,
+ feature_category: :continuous_integration do
let(:namespace_usages) { table(:ci_namespace_monthly_usages) }
before do
diff --git a/spec/migrations/sync_new_amount_used_for_ci_project_monthly_usages_spec.rb b/spec/migrations/sync_new_amount_used_for_ci_project_monthly_usages_spec.rb
index 8d45f1107ea..1c9b2711687 100644
--- a/spec/migrations/sync_new_amount_used_for_ci_project_monthly_usages_spec.rb
+++ b/spec/migrations/sync_new_amount_used_for_ci_project_monthly_usages_spec.rb
@@ -4,7 +4,8 @@ require 'spec_helper'
require_migration!
-RSpec.describe SyncNewAmountUsedForCiProjectMonthlyUsages, migration: :gitlab_ci do
+RSpec.describe SyncNewAmountUsedForCiProjectMonthlyUsages, migration: :gitlab_ci,
+ feature_category: :continuous_integration do
let(:project_usages) { table(:ci_project_monthly_usages) }
before do
diff --git a/spec/migrations/toggle_vsa_aggregations_enable_spec.rb b/spec/migrations/toggle_vsa_aggregations_enable_spec.rb
index a6850d493b7..5b3e513e9f6 100644
--- a/spec/migrations/toggle_vsa_aggregations_enable_spec.rb
+++ b/spec/migrations/toggle_vsa_aggregations_enable_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe ToggleVsaAggregationsEnable, :migration do
+RSpec.describe ToggleVsaAggregationsEnable, :migration, feature_category: :value_stream_management do
let(:aggregations) { table(:analytics_cycle_analytics_aggregations) }
let(:groups) { table(:namespaces) }
diff --git a/spec/migrations/update_application_settings_container_registry_exp_pol_worker_capacity_default_spec.rb b/spec/migrations/update_application_settings_container_registry_exp_pol_worker_capacity_default_spec.rb
index 842456089fe..d249fcecf66 100644
--- a/spec/migrations/update_application_settings_container_registry_exp_pol_worker_capacity_default_spec.rb
+++ b/spec/migrations/update_application_settings_container_registry_exp_pol_worker_capacity_default_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
require_migration!
-RSpec.describe UpdateApplicationSettingsContainerRegistryExpPolWorkerCapacityDefault do
+RSpec.describe UpdateApplicationSettingsContainerRegistryExpPolWorkerCapacityDefault,
+feature_category: :container_registry do
let(:settings) { table(:application_settings) }
context 'with no rows in the application_settings table' do
diff --git a/spec/migrations/update_application_settings_protected_paths_spec.rb b/spec/migrations/update_application_settings_protected_paths_spec.rb
index 21879995f1b..d61eadf9f9c 100644
--- a/spec/migrations/update_application_settings_protected_paths_spec.rb
+++ b/spec/migrations/update_application_settings_protected_paths_spec.rb
@@ -3,12 +3,13 @@
require 'spec_helper'
require_migration!
-RSpec.describe UpdateApplicationSettingsProtectedPaths, :aggregate_failures do
+RSpec.describe UpdateApplicationSettingsProtectedPaths, :aggregate_failures,
+feature_category: :authentication_and_authorization do
subject(:migration) { described_class.new }
- let_it_be(:application_settings) { table(:application_settings) }
- let_it_be(:oauth_paths) { %w[/oauth/authorize /oauth/token] }
- let_it_be(:custom_paths) { %w[/foo /bar] }
+ let!(:application_settings) { table(:application_settings) }
+ let!(:oauth_paths) { %w[/oauth/authorize /oauth/token] }
+ let!(:custom_paths) { %w[/foo /bar] }
let(:default_paths) { application_settings.column_defaults.fetch('protected_paths') }
diff --git a/spec/migrations/update_default_scan_method_of_dast_site_profile_spec.rb b/spec/migrations/update_default_scan_method_of_dast_site_profile_spec.rb
index b73aa7016a1..ac7a4171063 100644
--- a/spec/migrations/update_default_scan_method_of_dast_site_profile_spec.rb
+++ b/spec/migrations/update_default_scan_method_of_dast_site_profile_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe UpdateDefaultScanMethodOfDastSiteProfile do
+RSpec.describe UpdateDefaultScanMethodOfDastSiteProfile, feature_category: :dynamic_application_security_testing do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:dast_sites) { table(:dast_sites) }
diff --git a/spec/migrations/update_integrations_trigger_type_new_on_insert_spec.rb b/spec/migrations/update_integrations_trigger_type_new_on_insert_spec.rb
index 41cf35b40f4..efc051d9a68 100644
--- a/spec/migrations/update_integrations_trigger_type_new_on_insert_spec.rb
+++ b/spec/migrations/update_integrations_trigger_type_new_on_insert_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe UpdateIntegrationsTriggerTypeNewOnInsert do
+RSpec.describe UpdateIntegrationsTriggerTypeNewOnInsert, feature_category: :integrations do
let(:migration) { described_class.new }
let(:integrations) { table(:integrations) }
diff --git a/spec/migrations/update_invalid_member_states_spec.rb b/spec/migrations/update_invalid_member_states_spec.rb
index 802634230a9..6ae4b9f3c0f 100644
--- a/spec/migrations/update_invalid_member_states_spec.rb
+++ b/spec/migrations/update_invalid_member_states_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe UpdateInvalidMemberStates do
+RSpec.describe UpdateInvalidMemberStates, feature_category: :subgroups do
let(:members) { table(:members) }
let(:groups) { table(:namespaces) }
let(:projects) { table(:projects) }
diff --git a/spec/migrations/update_invalid_web_hooks_spec.rb b/spec/migrations/update_invalid_web_hooks_spec.rb
index a65f82d7082..9e69d3637b8 100644
--- a/spec/migrations/update_invalid_web_hooks_spec.rb
+++ b/spec/migrations/update_invalid_web_hooks_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require_migration!
-RSpec.describe UpdateInvalidWebHooks do
+RSpec.describe UpdateInvalidWebHooks, feature_category: :integrations do
let(:web_hooks) { table(:web_hooks) }
let(:groups) { table(:namespaces) }
let(:projects) { table(:projects) }
diff --git a/spec/models/achievements/achievement_spec.rb b/spec/models/achievements/achievement_spec.rb
new file mode 100644
index 00000000000..10c04d184af
--- /dev/null
+++ b/spec/models/achievements/achievement_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Achievements::Achievement, type: :model, feature_category: :users do
+ describe 'associations' do
+ it { is_expected.to belong_to(:namespace).required }
+ end
+
+ describe 'modules' do
+ subject { described_class }
+
+ it { is_expected.to include_module(Avatarable) }
+ end
+
+ describe 'validations' do
+ subject { create(:achievement) }
+
+ it { is_expected.to validate_presence_of(:name) }
+ it { is_expected.to validate_uniqueness_of(:name).case_insensitive.scoped_to([:namespace_id]) }
+ it { is_expected.to validate_length_of(:name).is_at_most(255) }
+ it { is_expected.to validate_length_of(:description).is_at_most(1024) }
+ end
+
+ describe '#name' do
+ it 'strips name' do
+ achievement = described_class.new(name: ' AchievementTest ')
+ achievement.valid?
+
+ expect(achievement.name).to eq('AchievementTest')
+ end
+ end
+end
diff --git a/spec/models/appearance_spec.rb b/spec/models/appearance_spec.rb
index 9d84279a75e..289408231a9 100644
--- a/spec/models/appearance_spec.rb
+++ b/spec/models/appearance_spec.rb
@@ -14,6 +14,7 @@ RSpec.describe Appearance do
subject(:appearance) { described_class.new }
it { expect(appearance.title).to eq('') }
+ it { expect(appearance.short_title).to eq('') }
it { expect(appearance.description).to eq('') }
it { expect(appearance.new_project_guidelines).to eq('') }
it { expect(appearance.profile_image_guidelines).to eq('') }
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index fd86a784b2d..1454c82c531 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -125,6 +125,8 @@ RSpec.describe ApplicationSetting do
it { is_expected.to validate_numericality_of(:max_yaml_depth).only_integer.is_greater_than(0) }
it { is_expected.to validate_presence_of(:max_pages_size) }
it { is_expected.to validate_presence_of(:max_pages_custom_domains_per_project) }
+ it { is_expected.to validate_presence_of(:max_terraform_state_size_bytes) }
+ it { is_expected.to validate_numericality_of(:max_terraform_state_size_bytes).only_integer.is_greater_than_or_equal_to(0) }
it 'ensures max_pages_size is an integer greater than 0 (or equal to 0 to indicate unlimited/maximum)' do
is_expected.to validate_numericality_of(:max_pages_size).only_integer.is_greater_than_or_equal_to(0)
@@ -214,6 +216,10 @@ RSpec.describe ApplicationSetting do
it { is_expected.to allow_value(http).for(:jira_connect_proxy_url) }
it { is_expected.to allow_value(https).for(:jira_connect_proxy_url) }
+ it { is_expected.to allow_value(true).for(:bulk_import_enabled) }
+ it { is_expected.to allow_value(false).for(:bulk_import_enabled) }
+ it { is_expected.not_to allow_value(nil).for(:bulk_import_enabled) }
+
context 'when deactivate_dormant_users is enabled' do
before do
stub_application_setting(deactivate_dormant_users: true)
@@ -1283,11 +1289,10 @@ RSpec.describe ApplicationSetting do
end
end
- describe '#instance_review_permitted?', :request_store, :use_clean_rails_memory_store_caching do
+ describe '#instance_review_permitted?', :request_store, :use_clean_rails_memory_store_caching, :without_license do
subject { setting.instance_review_permitted? }
before do
- allow(License).to receive(:current).and_return(nil) if Gitlab.ee?
allow(Rails.cache).to receive(:fetch).and_call_original
expect(Rails.cache).to receive(:fetch).with('limited_users_count', anything).and_return(
::ApplicationSetting::INSTANCE_REVIEW_MIN_USERS + users_over_minimum
diff --git a/spec/models/badge_spec.rb b/spec/models/badge_spec.rb
index f3c95332ca0..79a716c2087 100644
--- a/spec/models/badge_spec.rb
+++ b/spec/models/badge_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Badge do
- let(:placeholder_url) { 'http://www.example.com/%{project_path}/%{project_id}/%{default_branch}/%{commit_sha}' }
+ let(:placeholder_url) { 'http://www.example.com/%{project_path}/%{project_id}/%{project_name}/%{default_branch}/%{commit_sha}/%{project_title}' }
describe 'validations' do
# Requires the let variable url_sym
@@ -64,7 +64,7 @@ RSpec.describe Badge do
it 'uses the project information to populate the url placeholders' do
stub_project_commit_info(project)
- expect(badge.public_send("rendered_#{method}", project)).to eq "http://www.example.com/#{project.full_path}/#{project.id}/master/whatever"
+ expect(badge.public_send("rendered_#{method}", project)).to eq "http://www.example.com/#{project.full_path}/#{project.id}/#{project.path}/master/whatever/#{project.title}"
end
it 'returns the url if the project used is nil' do
diff --git a/spec/models/blob_viewer/metrics_dashboard_yml_spec.rb b/spec/models/blob_viewer/metrics_dashboard_yml_spec.rb
index 8d5c7ce84f6..d28fa0bbe97 100644
--- a/spec/models/blob_viewer/metrics_dashboard_yml_spec.rb
+++ b/spec/models/blob_viewer/metrics_dashboard_yml_spec.rb
@@ -14,224 +14,109 @@ RSpec.describe BlobViewer::MetricsDashboardYml do
subject(:viewer) { described_class.new(blob) }
- context 'with metrics_dashboard_exhaustive_validations feature flag on' do
- before do
- stub_feature_flags(metrics_dashboard_exhaustive_validations: true)
- end
-
- context 'when the definition is valid' do
+ context 'when the definition is valid' do
+ describe '#valid?' do
before do
- allow(Gitlab::Metrics::Dashboard::Validator).to receive(:errors).and_return([])
- end
-
- describe '#valid?' do
- it 'calls prepare! on the viewer' do
- expect(viewer).to receive(:prepare!)
-
- viewer.valid?
- end
-
- it 'processes dashboard yaml and returns true', :aggregate_failures do
- yml = ::Gitlab::Config::Loader::Yaml.new(data).load_raw!
-
- expect_next_instance_of(::Gitlab::Config::Loader::Yaml, data) do |loader|
- expect(loader).to receive(:load_raw!).and_call_original
- end
- expect(Gitlab::Metrics::Dashboard::Validator)
- .to receive(:errors)
- .with(yml, dashboard_path: '.gitlab/dashboards/custom-dashboard.yml', project: project)
- .and_call_original
- expect(viewer.valid?).to be true
- end
- end
-
- describe '#errors' do
- it 'returns empty array' do
- expect(viewer.errors).to eq []
- end
+ allow(PerformanceMonitoring::PrometheusDashboard).to receive(:from_json)
end
- end
- context 'when definition is invalid' do
- let(:error) { ::Gitlab::Metrics::Dashboard::Validator::Errors::SchemaValidationError.new }
- let(:data) do
- <<~YAML
- dashboard:
- YAML
- end
+ it 'calls prepare! on the viewer' do
+ expect(viewer).to receive(:prepare!)
- before do
- allow(Gitlab::Metrics::Dashboard::Validator).to receive(:errors).and_return([error])
+ viewer.valid?
end
- describe '#valid?' do
- it 'returns false' do
- expect(viewer.valid?).to be false
- end
- end
+ it 'processes dashboard yaml and returns true', :aggregate_failures do
+ yml = ::Gitlab::Config::Loader::Yaml.new(data).load_raw!
- describe '#errors' do
- it 'returns validation errors' do
- expect(viewer.errors).to eq ["Dashboard failed schema validation"]
+ expect_next_instance_of(::Gitlab::Config::Loader::Yaml, data) do |loader|
+ expect(loader).to receive(:load_raw!).and_call_original
end
+ expect(PerformanceMonitoring::PrometheusDashboard)
+ .to receive(:from_json)
+ .with(yml)
+ .and_call_original
+ expect(viewer.valid?).to be true
end
end
- context 'when YAML syntax is invalid' do
- let(:data) do
- <<~YAML
- dashboard: 'empty metrics'
- panel_groups:
- - group: 'Group Title'
- YAML
- end
-
- describe '#valid?' do
- it 'returns false' do
- expect(Gitlab::Metrics::Dashboard::Validator).not_to receive(:errors)
- expect(viewer.valid?).to be false
- end
- end
-
- describe '#errors' do
- it 'returns validation errors' do
- expect(viewer.errors).to all be_kind_of String
- end
- end
- end
-
- context 'when YAML loader raises error' do
- let(:data) do
- <<~YAML
- large yaml file
- YAML
- end
-
- before do
- allow(::Gitlab::Config::Loader::Yaml).to receive(:new)
- .and_raise(::Gitlab::Config::Loader::Yaml::DataTooLargeError, 'The parsed YAML is too big')
- end
-
- it 'is invalid' do
- expect(Gitlab::Metrics::Dashboard::Validator).not_to receive(:errors)
- expect(viewer.valid?).to be false
- end
-
- it 'returns validation errors' do
- expect(viewer.errors).to eq ['The parsed YAML is too big']
+ describe '#errors' do
+ it 'returns empty array' do
+ expect(viewer.errors).to eq []
end
end
end
- context 'with metrics_dashboard_exhaustive_validations feature flag off' do
- before do
- stub_feature_flags(metrics_dashboard_exhaustive_validations: false)
+ context 'when definition is invalid' do
+ let(:error) { ActiveModel::ValidationError.new(PerformanceMonitoring::PrometheusDashboard.new.tap(&:validate)) }
+ let(:data) do
+ <<~YAML
+ dashboard:
+ YAML
end
- context 'when the definition is valid' do
- describe '#valid?' do
- before do
- allow(PerformanceMonitoring::PrometheusDashboard).to receive(:from_json)
- end
-
- it 'calls prepare! on the viewer' do
- expect(viewer).to receive(:prepare!)
+ describe '#valid?' do
+ it 'returns false' do
+ expect(PerformanceMonitoring::PrometheusDashboard)
+ .to receive(:from_json).and_raise(error)
- viewer.valid?
- end
-
- it 'processes dashboard yaml and returns true', :aggregate_failures do
- yml = ::Gitlab::Config::Loader::Yaml.new(data).load_raw!
-
- expect_next_instance_of(::Gitlab::Config::Loader::Yaml, data) do |loader|
- expect(loader).to receive(:load_raw!).and_call_original
- end
- expect(PerformanceMonitoring::PrometheusDashboard)
- .to receive(:from_json)
- .with(yml)
- .and_call_original
- expect(viewer.valid?).to be true
- end
- end
-
- describe '#errors' do
- it 'returns empty array' do
- expect(viewer.errors).to eq []
- end
+ expect(viewer.valid?).to be false
end
end
- context 'when definition is invalid' do
- let(:error) { ActiveModel::ValidationError.new(PerformanceMonitoring::PrometheusDashboard.new.tap(&:validate)) }
- let(:data) do
- <<~YAML
- dashboard:
- YAML
- end
-
- describe '#valid?' do
- it 'returns false' do
- expect(PerformanceMonitoring::PrometheusDashboard)
- .to receive(:from_json).and_raise(error)
+ describe '#errors' do
+ it 'returns validation errors' do
+ allow(PerformanceMonitoring::PrometheusDashboard)
+ .to receive(:from_json).and_raise(error)
- expect(viewer.valid?).to be false
- end
- end
-
- describe '#errors' do
- it 'returns validation errors' do
- allow(PerformanceMonitoring::PrometheusDashboard)
- .to receive(:from_json).and_raise(error)
-
- expect(viewer.errors).to eq error.model.errors.messages.map { |messages| messages.join(': ') }
- end
+ expect(viewer.errors).to eq error.model.errors.messages.map { |messages| messages.join(': ') }
end
end
+ end
- context 'when YAML syntax is invalid' do
- let(:data) do
- <<~YAML
+ context 'when YAML syntax is invalid' do
+ let(:data) do
+ <<~YAML
dashboard: 'empty metrics'
panel_groups:
- group: 'Group Title'
- YAML
- end
+ YAML
+ end
- describe '#valid?' do
- it 'returns false' do
- expect(PerformanceMonitoring::PrometheusDashboard).not_to receive(:from_json)
- expect(viewer.valid?).to be false
- end
+ describe '#valid?' do
+ it 'returns false' do
+ expect(PerformanceMonitoring::PrometheusDashboard).not_to receive(:from_json)
+ expect(viewer.valid?).to be false
end
+ end
- describe '#errors' do
- it 'returns validation errors' do
- expect(viewer.errors).to eq ["YAML syntax: (<unknown>): did not find expected key while parsing a block mapping at line 1 column 1"]
- end
+ describe '#errors' do
+ it 'returns validation errors' do
+ expect(viewer.errors).to eq ["YAML syntax: (<unknown>): did not find expected key while parsing a block mapping at line 1 column 1"]
end
end
+ end
- context 'when YAML loader raises error' do
- let(:data) do
- <<~YAML
+ context 'when YAML loader raises error' do
+ let(:data) do
+ <<~YAML
large yaml file
- YAML
- end
+ YAML
+ end
- before do
- allow(::Gitlab::Config::Loader::Yaml).to(
- receive(:new).and_raise(::Gitlab::Config::Loader::Yaml::DataTooLargeError, 'The parsed YAML is too big')
- )
- end
+ before do
+ allow(::Gitlab::Config::Loader::Yaml).to(
+ receive(:new).and_raise(::Gitlab::Config::Loader::Yaml::DataTooLargeError, 'The parsed YAML is too big')
+ )
+ end
- it 'is invalid' do
- expect(PerformanceMonitoring::PrometheusDashboard).not_to receive(:from_json)
- expect(viewer.valid?).to be false
- end
+ it 'is invalid' do
+ expect(PerformanceMonitoring::PrometheusDashboard).not_to receive(:from_json)
+ expect(viewer.valid?).to be false
+ end
- it 'returns validation errors' do
- expect(viewer.errors).to eq ["YAML syntax: The parsed YAML is too big"]
- end
+ it 'returns validation errors' do
+ expect(viewer.errors).to eq ["YAML syntax: The parsed YAML is too big"]
end
end
end
diff --git a/spec/models/bulk_imports/tracker_spec.rb b/spec/models/bulk_imports/tracker_spec.rb
index 1aa76d4dadd..1516ab106cb 100644
--- a/spec/models/bulk_imports/tracker_spec.rb
+++ b/spec/models/bulk_imports/tracker_spec.rb
@@ -54,13 +54,16 @@ RSpec.describe BulkImports::Tracker, type: :model do
it 'returns the not started pipeline trackers from the minimum stage number' do
stage_1_tracker = create(:bulk_import_tracker, entity: entity, stage: 1)
+ stage_1_finished_tracker = create(:bulk_import_tracker, :finished, entity: entity, stage: 1)
+ stage_1_failed_tracker = create(:bulk_import_tracker, :failed, entity: entity, stage: 1)
+ stage_1_skipped_tracker = create(:bulk_import_tracker, :skipped, entity: entity, stage: 1)
stage_2_tracker = create(:bulk_import_tracker, entity: entity, stage: 2)
expect(described_class.next_pipeline_trackers_for(entity.id))
.to include(stage_1_tracker)
expect(described_class.next_pipeline_trackers_for(entity.id))
- .not_to include(stage_2_tracker)
+ .not_to include(stage_2_tracker, stage_1_finished_tracker, stage_1_failed_tracker, stage_1_skipped_tracker)
end
end
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
index df24c92149d..169b00b9c74 100644
--- a/spec/models/ci/bridge_spec.rb
+++ b/spec/models/ci/bridge_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::Bridge do
+RSpec.describe Ci::Bridge, feature_category: :continuous_integration do
let_it_be(:project) { create(:project) }
let_it_be(:target_project) { create(:project, name: 'project', namespace: create(:namespace, name: 'my')) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
@@ -34,6 +34,24 @@ RSpec.describe Ci::Bridge do
expect(bridge).to have_one(:downstream_pipeline)
end
+ describe '#sourced_pipelines' do
+ subject { bridge.sourced_pipelines }
+
+ it 'raises error' do
+ expect { subject }.to raise_error RuntimeError, 'Ci::Bridge does not have sourced_pipelines association'
+ end
+
+ context 'when ci_bridge_remove_sourced_pipelines is disabled' do
+ before do
+ stub_feature_flags(ci_bridge_remove_sourced_pipelines: false)
+ end
+
+ it 'returns the sourced_pipelines association' do
+ expect(bridge.sourced_pipelines).to eq([])
+ end
+ end
+ end
+
describe '#retryable?' do
let(:bridge) { create(:ci_bridge, :success) }
diff --git a/spec/models/ci/build_metadata_spec.rb b/spec/models/ci/build_metadata_spec.rb
index e728ce0f474..8bf3af44be6 100644
--- a/spec/models/ci/build_metadata_spec.rb
+++ b/spec/models/ci/build_metadata_spec.rb
@@ -6,7 +6,6 @@ RSpec.describe Ci::BuildMetadata do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, group: group, build_timeout: 2000) }
-
let_it_be(:pipeline) do
create(:ci_pipeline, project: project,
sha: project.commit.id,
@@ -14,7 +13,9 @@ RSpec.describe Ci::BuildMetadata do
status: 'success')
end
- let(:job) { create(:ci_build, pipeline: pipeline) }
+ let_it_be_with_reload(:runner) { create(:ci_runner) }
+
+ let(:job) { create(:ci_build, pipeline: pipeline, runner: runner) }
let(:metadata) { job.metadata }
it_behaves_like 'having unique enum values'
@@ -32,63 +33,110 @@ RSpec.describe Ci::BuildMetadata do
end
end
- context 'when project timeout is set' do
- context 'when runner is assigned to the job' do
+ context 'when job, project and runner timeouts are set' do
+ context 'when job timeout is lower then runner timeout' do
before do
- job.update!(runner: runner)
+ runner.update!(maximum_timeout: 4000)
+ job.update!(options: { job_timeout: 3000 })
end
- context 'when runner timeout is not set' do
- let(:runner) { create(:ci_runner, maximum_timeout: nil) }
+ it_behaves_like 'sets timeout', 'job_timeout_source', 3000
+ end
- it_behaves_like 'sets timeout', 'project_timeout_source', 2000
+ context 'when runner timeout is lower then job timeout' do
+ before do
+ runner.update!(maximum_timeout: 2000)
+ job.update!(options: { job_timeout: 3000 })
end
- context 'when runner timeout is lower than project timeout' do
- let(:runner) { create(:ci_runner, maximum_timeout: 1900) }
+ it_behaves_like 'sets timeout', 'runner_timeout_source', 2000
+ end
+ end
- it_behaves_like 'sets timeout', 'runner_timeout_source', 1900
+ context 'when job, project timeout values are set and runner is assigned' do
+ context 'when runner has no timeout set' do
+ before do
+ runner.update!(maximum_timeout: nil)
+ job.update!(options: { job_timeout: 3000 })
end
- context 'when runner timeout is higher than project timeout' do
- let(:runner) { create(:ci_runner, maximum_timeout: 2100) }
+ it_behaves_like 'sets timeout', 'job_timeout_source', 3000
+ end
+ end
- it_behaves_like 'sets timeout', 'project_timeout_source', 2000
+ context 'when only job and project timeouts are defined' do
+ context 'when job timeout is lower then project timeout' do
+ before do
+ job.update!(options: { job_timeout: 1000 })
end
- end
- context 'when job timeout is set' do
- context 'when job timeout is higher than project timeout' do
- let(:job) { create(:ci_build, pipeline: pipeline, options: { job_timeout: 3000 }) }
+ it_behaves_like 'sets timeout', 'job_timeout_source', 1000
+ end
- it_behaves_like 'sets timeout', 'job_timeout_source', 3000
+ context 'when project timeout is lower then job timeout' do
+ before do
+ job.update!(options: { job_timeout: 3000 })
end
- context 'when job timeout is lower than project timeout' do
- let(:job) { create(:ci_build, pipeline: pipeline, options: { job_timeout: 1000 }) }
+ it_behaves_like 'sets timeout', 'job_timeout_source', 3000
+ end
+ end
+
+ context 'when only project and runner timeouts are defined' do
+ before do
+ runner.update!(maximum_timeout: 1900)
+ end
+
+ context 'when runner timeout is lower then project timeout' do
+ it_behaves_like 'sets timeout', 'runner_timeout_source', 1900
+ end
- it_behaves_like 'sets timeout', 'job_timeout_source', 1000
+ context 'when project timeout is lower then runner timeout' do
+ before do
+ runner.update!(maximum_timeout: 2100)
end
+
+ it_behaves_like 'sets timeout', 'project_timeout_source', 2000
end
+ end
- context 'when both runner and job timeouts are set' do
+ context 'when only job and runner timeouts are defined' do
+ context 'when runner timeout is lower them job timeout' do
before do
- job.update!(runner: runner)
+ job.update!(options: { job_timeout: 2000 })
+ runner.update!(maximum_timeout: 1900)
end
- context 'when job timeout is higher than runner timeout' do
- let(:job) { create(:ci_build, pipeline: pipeline, options: { job_timeout: 3000 }) }
- let(:runner) { create(:ci_runner, maximum_timeout: 2100) }
+ it_behaves_like 'sets timeout', 'runner_timeout_source', 1900
+ end
- it_behaves_like 'sets timeout', 'runner_timeout_source', 2100
+ context 'when job timeout is lower them runner timeout' do
+ before do
+ job.update!(options: { job_timeout: 1000 })
+ runner.update!(maximum_timeout: 1900)
end
- context 'when job timeout is lower than runner timeout' do
- let(:job) { create(:ci_build, pipeline: pipeline, options: { job_timeout: 1900 }) }
- let(:runner) { create(:ci_runner, maximum_timeout: 2100) }
+ it_behaves_like 'sets timeout', 'job_timeout_source', 1000
+ end
+ end
+
+ context 'when only job timeout is defined and runner is assigned, but has no timeout set' do
+ before do
+ job.update!(options: { job_timeout: 1000 })
+ runner.update!(maximum_timeout: nil)
+ end
+
+ it_behaves_like 'sets timeout', 'job_timeout_source', 1000
+ end
- it_behaves_like 'sets timeout', 'job_timeout_source', 1900
+ context 'when only one timeout value is defined' do
+ context 'when only project timeout value is defined' do
+ before do
+ job.update!(options: { job_timeout: nil })
+ runner.update!(maximum_timeout: nil)
end
+
+ it_behaves_like 'sets timeout', 'project_timeout_source', 2000
end
end
end
@@ -107,9 +155,7 @@ RSpec.describe Ci::BuildMetadata do
}
metadata.id_tokens = {
TEST_JWT_TOKEN: {
- id_token: {
- aud: 'https://gitlab.test'
- }
+ aud: 'https://gitlab.test'
}
}
@@ -152,6 +198,29 @@ RSpec.describe Ci::BuildMetadata do
end
end
+ describe '#enable_debug_trace!' do
+ subject { metadata.enable_debug_trace! }
+
+ context 'when debug_trace_enabled is false' do
+ it 'sets debug_trace_enabled to true' do
+ subject
+
+ expect(metadata.debug_trace_enabled).to eq(true)
+ end
+ end
+
+ context 'when debug_trace_enabled is true' do
+ before do
+ metadata.update!(debug_trace_enabled: true)
+ end
+
+ it 'does not set debug_trace_enabled to true', :aggregate_failures do
+ expect(described_class).not_to receive(:save!)
+ expect(metadata.debug_trace_enabled).to eq(true)
+ end
+ end
+ end
+
describe 'partitioning' do
context 'with job' do
let(:status) { build(:commit_status, partition_id: 123) }
@@ -183,28 +252,6 @@ RSpec.describe Ci::BuildMetadata do
end
end
- describe 'routing table switch' do
- context 'with ff disabled' do
- before do
- stub_feature_flags(ci_partitioning_use_ci_builds_metadata_routing_table: false)
- end
-
- it 'uses the legacy table' do
- expect(described_class.table_name).to eq('ci_builds_metadata')
- end
- end
-
- context 'with ff enabled' do
- before do
- stub_feature_flags(ci_partitioning_use_ci_builds_metadata_routing_table: true)
- end
-
- it 'uses the routing table' do
- expect(described_class.table_name).to eq('p_ci_builds_metadata')
- end
- end
- end
-
context 'jsonb fields serialization' do
it 'changing other fields does not change config_options' do
expect { metadata.id = metadata.id }.not_to change(metadata, :changes)
diff --git a/spec/models/ci/build_need_spec.rb b/spec/models/ci/build_need_spec.rb
index c2cf9027055..aa1c57d1788 100644
--- a/spec/models/ci/build_need_spec.rb
+++ b/spec/models/ci/build_need_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::BuildNeed, model: true do
+RSpec.describe Ci::BuildNeed, model: true, feature_category: :continuous_integration do
let(:build_need) { build(:ci_build_need) }
it { is_expected.to belong_to(:build).class_name('Ci::Processable') }
@@ -35,4 +35,62 @@ RSpec.describe Ci::BuildNeed, model: true do
end
end
end
+
+ describe 'partitioning' do
+ context 'with build' do
+ let(:build) { FactoryBot.build(:ci_build, partition_id: ci_testing_partition_id) }
+ let(:build_need) { FactoryBot.build(:ci_build_need, build: build) }
+
+ it 'sets partition_id to the current partition value' do
+ expect { build_need.valid? }.to change { build_need.partition_id }.to(ci_testing_partition_id)
+ end
+
+ context 'when it is already set' do
+ let(:build_need) { FactoryBot.build(:ci_build_need, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { build_need.valid? }.not_to change { build_need.partition_id }
+ end
+ end
+ end
+
+ context 'without build' do
+ let(:build_need) { FactoryBot.build(:ci_build_need, build: nil) }
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { build_need.valid? }.not_to change { build_need.partition_id }
+ end
+ end
+
+ context 'when using bulk_insert' do
+ include Ci::PartitioningHelpers
+
+ let(:new_pipeline) { create(:ci_pipeline) }
+ let(:ci_build) { build(:ci_build, pipeline: new_pipeline) }
+
+ before do
+ stub_current_partition_id
+ end
+
+ it 'creates build needs successfully', :aggregate_failures do
+ ci_build.needs_attributes = [
+ { name: "build", artifacts: true },
+ { name: "build2", artifacts: true },
+ { name: "build3", artifacts: true }
+ ]
+
+ expect(described_class).to receive(:bulk_insert!).and_call_original
+
+ BulkInsertableAssociations.with_bulk_insert do
+ ci_build.save!
+ end
+
+ expect(described_class.count).to eq(3)
+ expect(described_class.first.partition_id).to eq(ci_testing_partition_id)
+ expect(described_class.second.partition_id).to eq(ci_testing_partition_id)
+ end
+ end
+ end
end
diff --git a/spec/models/ci/build_pending_state_spec.rb b/spec/models/ci/build_pending_state_spec.rb
index a546d2aff65..756180621ec 100644
--- a/spec/models/ci/build_pending_state_spec.rb
+++ b/spec/models/ci/build_pending_state_spec.rb
@@ -24,4 +24,33 @@ RSpec.describe Ci::BuildPendingState do
end
end
end
+
+ describe 'partitioning' do
+ context 'with build' do
+ let(:build) { FactoryBot.build(:ci_build, partition_id: ci_testing_partition_id) }
+ let(:build_pending_state) { FactoryBot.build(:ci_build_pending_state, build: build) }
+
+ it 'sets partition_id to the current partition value' do
+ expect { build_pending_state.valid? }.to change { build_pending_state.partition_id }.to(ci_testing_partition_id)
+ end
+
+ context 'when it is already set' do
+ let(:build_pending_state) { FactoryBot.build(:ci_build_pending_state, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { build_pending_state.valid? }.not_to change { build_pending_state.partition_id }
+ end
+ end
+ end
+
+ context 'without build' do
+ let(:build_pending_state) { FactoryBot.build(:ci_build_pending_state, build: nil) }
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { build_pending_state.valid? }.not_to change { build_pending_state.partition_id }
+ end
+ end
+ end
end
diff --git a/spec/models/ci/build_report_result_spec.rb b/spec/models/ci/build_report_result_spec.rb
index 09ea19cf077..90b23d3e824 100644
--- a/spec/models/ci/build_report_result_spec.rb
+++ b/spec/models/ci/build_report_result_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Ci::BuildReportResult do
- let(:build_report_result) { build(:ci_build_report_result, :with_junit_success) }
+ let_it_be_with_reload(:build_report_result) { create(:ci_build_report_result, :with_junit_success) }
it_behaves_like 'cleanup by a loose foreign key' do
let!(:parent) { create(:project) }
@@ -70,4 +70,34 @@ RSpec.describe Ci::BuildReportResult do
expect(build_report_result.tests_skipped).to eq(0)
end
end
+
+ describe 'partitioning' do
+ let(:build_report_result) { FactoryBot.build(:ci_build_report_result, build: build) }
+
+ context 'with build' do
+ let(:build) { FactoryBot.build(:ci_build, partition_id: ci_testing_partition_id) }
+
+ it 'copies the partition_id from build' do
+ expect { build_report_result.valid? }.to change { build_report_result.partition_id }.to(ci_testing_partition_id)
+ end
+
+ context 'when it is already set' do
+ let(:build_report_result) { FactoryBot.build(:ci_build_report_result, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { build_report_result.valid? }.not_to change { build_report_result.partition_id }
+ end
+ end
+ end
+
+ context 'without build' do
+ subject(:build_report_result) { FactoryBot.build(:ci_build_report_result, build: nil, partition_id: 125) }
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { build_report_result.valid? }.not_to change { build_report_result.partition_id }
+ end
+ end
+ end
end
diff --git a/spec/models/ci/build_runner_session_spec.rb b/spec/models/ci/build_runner_session_spec.rb
index 8dfe854511c..5e1a489ed8b 100644
--- a/spec/models/ci/build_runner_session_spec.rb
+++ b/spec/models/ci/build_runner_session_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::BuildRunnerSession, model: true do
+RSpec.describe Ci::BuildRunnerSession, model: true, feature_category: :continuous_integration do
let!(:build) { create(:ci_build, :with_runner_session) }
let(:url) { 'https://new.example.com' }
@@ -174,4 +174,20 @@ RSpec.describe Ci::BuildRunnerSession, model: true do
end
end
end
+
+ describe 'partitioning' do
+ include Ci::PartitioningHelpers
+
+ let(:new_pipeline) { create(:ci_pipeline) }
+ let(:new_build) { create(:ci_build, pipeline: new_pipeline) }
+ let(:build_runner_session) { create(:ci_build_runner_session, build: new_build) }
+
+ before do
+ stub_current_partition_id
+ end
+
+ it 'assigns the same partition id as the one that build has' do
+ expect(build_runner_session.partition_id).to eq(ci_testing_partition_id)
+ end
+ end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 813b4b3faa6..c978e33bf54 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::Build do
+RSpec.describe Ci::Build, feature_category: :continuous_integration do
include Ci::TemplateHelpers
include AfterNextHelpers
@@ -1754,8 +1754,8 @@ RSpec.describe Ci::Build do
end
end
- describe '#starts_environment?' do
- subject { build.starts_environment? }
+ describe '#deployment_job?' do
+ subject { build.deployment_job? }
context 'when environment is defined' do
before do
@@ -2528,20 +2528,24 @@ RSpec.describe Ci::Build do
end
describe '#ref_slug' do
- {
- 'master' => 'master',
- '1-foo' => '1-foo',
- 'fix/1-foo' => 'fix-1-foo',
- 'fix-1-foo' => 'fix-1-foo',
- 'a' * 63 => 'a' * 63,
- 'a' * 64 => 'a' * 63,
- 'FOO' => 'foo',
- '-' + 'a' * 61 + '-' => 'a' * 61,
- '-' + 'a' * 62 + '-' => 'a' * 62,
- '-' + 'a' * 63 + '-' => 'a' * 62,
- 'a' * 62 + ' ' => 'a' * 62
- }.each do |ref, slug|
- it "transforms #{ref} to #{slug}" do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:ref, :slug) do
+ 'master' | 'master'
+ '1-foo' | '1-foo'
+ 'fix/1-foo' | 'fix-1-foo'
+ 'fix-1-foo' | 'fix-1-foo'
+ 'a' * 63 | 'a' * 63
+ 'a' * 64 | 'a' * 63
+ 'FOO' | 'foo'
+ '-' + 'a' * 61 + '-' | 'a' * 61
+ '-' + 'a' * 62 + '-' | 'a' * 62
+ '-' + 'a' * 63 + '-' | 'a' * 62
+ 'a' * 62 + ' ' | 'a' * 62
+ end
+
+ with_them do
+ it "transforms ref to slug" do
build.ref = ref
expect(build.ref_slug).to eq(slug)
@@ -2737,6 +2741,7 @@ RSpec.describe Ci::Build do
{ key: 'CI_PROJECT_PATH', value: project.full_path, public: true, masked: false },
{ key: 'CI_PROJECT_PATH_SLUG', value: project.full_path_slug, public: true, masked: false },
{ key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true, masked: false },
+ { key: 'CI_PROJECT_NAMESPACE_ID', value: project.namespace.id.to_s, public: true, masked: false },
{ key: 'CI_PROJECT_ROOT_NAMESPACE', value: project.namespace.root_ancestor.path, public: true, masked: false },
{ key: 'CI_PROJECT_URL', value: project.web_url, public: true, masked: false },
{ key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true, masked: false },
@@ -2883,6 +2888,9 @@ RSpec.describe Ci::Build do
value: 'var',
public: true }]
build.environment = 'staging'
+
+ # CI_ENVIRONMENT_NAME is set in predefined_variables when job environment is provided
+ predefined_variables.insert(20, { key: 'CI_ENVIRONMENT_NAME', value: 'staging', public: true, masked: false })
end
it 'matches explicit variables ordering' do
@@ -3539,8 +3547,8 @@ RSpec.describe Ci::Build do
rsa_key = OpenSSL::PKey::RSA.generate(3072).to_s
stub_application_setting(ci_jwt_signing_key: rsa_key)
build.metadata.update!(id_tokens: {
- 'ID_TOKEN_1' => { id_token: { aud: 'developers' } },
- 'ID_TOKEN_2' => { id_token: { aud: 'maintainers' } }
+ 'ID_TOKEN_1' => { aud: 'developers' },
+ 'ID_TOKEN_2' => { aud: 'maintainers' }
})
end
@@ -3817,22 +3825,6 @@ RSpec.describe Ci::Build do
it 'assigns the token' do
expect { build.enqueue }.to change(build, :token).from(nil).to(an_instance_of(String))
end
-
- context 'with ci_assign_job_token_on_scheduling disabled' do
- before do
- stub_feature_flags(ci_assign_job_token_on_scheduling: false)
- end
-
- it 'assigns the token on creation' do
- expect(build.token).to be_present
- end
-
- it 'does not change the token when enqueuing' do
- expect { build.enqueue }.not_to change(build, :token)
-
- expect(build).to be_pending
- end
- end
end
describe 'state transition: pending: :running' do
@@ -5442,7 +5434,7 @@ RSpec.describe Ci::Build do
it 'delegates to Ci::BuildTraceMetadata' do
expect(Ci::BuildTraceMetadata)
.to receive(:find_or_upsert_for!)
- .with(build.id)
+ .with(build.id, build.partition_id)
build.ensure_trace_metadata!
end
@@ -5617,4 +5609,72 @@ RSpec.describe Ci::Build do
end
end
end
+
+ describe '#runtime_hooks' do
+ let(:build1) do
+ FactoryBot.build(:ci_build,
+ options: { hooks: { pre_get_sources_script: ["echo 'hello pre_get_sources_script'"] } })
+ end
+
+ subject(:runtime_hooks) { build1.runtime_hooks }
+
+ it 'returns an array of hook objects' do
+ expect(runtime_hooks.size).to eq(1)
+ expect(runtime_hooks[0].name).to eq('pre_get_sources_script')
+ expect(runtime_hooks[0].script).to eq(["echo 'hello pre_get_sources_script'"])
+ end
+ end
+
+ describe 'partitioning', :ci_partitionable do
+ include Ci::PartitioningHelpers
+
+ let(:new_pipeline) { create(:ci_pipeline) }
+ let(:ci_build) { FactoryBot.build(:ci_build, pipeline: new_pipeline) }
+
+ before do
+ stub_current_partition_id
+ end
+
+ it 'assigns partition_id to job variables successfully', :aggregate_failures do
+ ci_build.job_variables_attributes = [
+ { key: 'TEST_KEY', value: 'new value' },
+ { key: 'NEW_KEY', value: 'exciting new value' }
+ ]
+
+ ci_build.save!
+
+ expect(ci_build.job_variables.count).to eq(2)
+ expect(ci_build.job_variables.first.partition_id).to eq(ci_testing_partition_id)
+ expect(ci_build.job_variables.second.partition_id).to eq(ci_testing_partition_id)
+ end
+ end
+
+ describe 'assigning token', :ci_partitionable do
+ include Ci::PartitioningHelpers
+
+ let(:new_pipeline) { create(:ci_pipeline) }
+ let(:ci_build) { create(:ci_build, pipeline: new_pipeline) }
+
+ before do
+ stub_current_partition_id
+ end
+
+ it 'includes partition_id as a token prefix' do
+ prefix = ci_build.token.split('_').first.to_i(16)
+
+ expect(prefix).to eq(ci_testing_partition_id)
+ end
+
+ context 'when ci_build_partition_id_token_prefix is disabled' do
+ before do
+ stub_feature_flags(ci_build_partition_id_token_prefix: false)
+ end
+
+ it 'does not include partition_id as a token prefix' do
+ prefix = ci_build.token.split('_').first.to_i(16)
+
+ expect(prefix).not_to eq(ci_testing_partition_id)
+ end
+ end
+ end
end
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index 3328ed62f15..ac0a18a176d 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -15,13 +15,13 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state, :clean_git
described_class.new(build: build, chunk_index: chunk_index, data_store: data_store, raw_data: raw_data)
end
- it_behaves_like 'having unique enum values'
-
before do
stub_feature_flags(ci_enable_live_trace: true)
stub_artifacts_object_storage
end
+ it_behaves_like 'having unique enum values'
+
def redis_instance
{
redis: Gitlab::Redis::SharedState,
@@ -954,4 +954,33 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state, :clean_git
it { is_expected.to eq(value) }
end
end
+
+ describe 'partitioning' do
+ context 'with build' do
+ let(:build) { FactoryBot.build(:ci_build, partition_id: ci_testing_partition_id) }
+ let(:build_trace_chunk) { FactoryBot.build(:ci_build_trace_chunk, build: build) }
+
+ it 'sets partition_id to the current partition value' do
+ expect { build_trace_chunk.valid? }.to change { build_trace_chunk.partition_id }.to(ci_testing_partition_id)
+ end
+
+ context 'when it is already set' do
+ let(:build_trace_chunk) { FactoryBot.build(:ci_build_trace_chunk, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { build_trace_chunk.valid? }.not_to change { build_trace_chunk.partition_id }
+ end
+ end
+ end
+
+ context 'without build' do
+ let(:build_trace_chunk) { FactoryBot.build(:ci_build_trace_chunk, build: nil, partition_id: 125) }
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { build_trace_chunk.valid? }.not_to change { build_trace_chunk.partition_id }
+ end
+ end
+ end
end
diff --git a/spec/models/ci/build_trace_metadata_spec.rb b/spec/models/ci/build_trace_metadata_spec.rb
index 120e4289da2..2ab300e4054 100644
--- a/spec/models/ci/build_trace_metadata_spec.rb
+++ b/spec/models/ci/build_trace_metadata_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::BuildTraceMetadata do
+RSpec.describe Ci::BuildTraceMetadata, feature_category: :continuous_integration do
it { is_expected.to belong_to(:build) }
it { is_expected.to belong_to(:trace_artifact) }
@@ -106,7 +106,7 @@ RSpec.describe Ci::BuildTraceMetadata do
let_it_be(:build) { create(:ci_build) }
subject(:execute) do
- described_class.find_or_upsert_for!(build.id)
+ described_class.find_or_upsert_for!(build.id, build.partition_id)
end
it 'creates a new record' do
@@ -158,4 +158,22 @@ RSpec.describe Ci::BuildTraceMetadata do
it { is_expected.to eq(result) }
end
end
+
+ describe 'partitioning' do
+ include Ci::PartitioningHelpers
+
+ let_it_be(:pipeline) { create(:ci_pipeline) }
+ let_it_be(:build) { create(:ci_build, pipeline: pipeline) }
+ let(:new_pipeline) { create(:ci_pipeline) }
+ let(:new_build) { create(:ci_build, pipeline: new_pipeline) }
+ let(:metadata) { create(:ci_build_trace_metadata, build: new_build) }
+
+ before do
+ stub_current_partition_id
+ end
+
+ it 'assigns the same partition id as the one that build has' do
+ expect(metadata.partition_id).to eq(ci_testing_partition_id)
+ end
+ end
end
diff --git a/spec/models/ci/freeze_period_spec.rb b/spec/models/ci/freeze_period_spec.rb
index b9bf1657e28..d8add736d6a 100644
--- a/spec/models/ci/freeze_period_spec.rb
+++ b/spec/models/ci/freeze_period_spec.rb
@@ -2,16 +2,22 @@
require 'spec_helper'
-RSpec.describe Ci::FreezePeriod, type: :model do
+RSpec.describe Ci::FreezePeriod, feature_category: :release_orchestration, type: :model do
+ let_it_be(:project) { create(:project) }
+
+ # Freeze period factory is on a weekend, so we travel in time, in and around that.
+ let(:friday_2300_time) { Time.utc(2020, 4, 10, 23, 0) }
+ let(:saturday_1200_time) { Time.utc(2020, 4, 11, 12, 0) }
+ let(:monday_0700_time) { Time.utc(2020, 4, 13, 7, 0) }
+ let(:tuesday_0800_time) { Time.utc(2020, 4, 14, 8, 0) }
+
subject { build(:ci_freeze_period) }
it_behaves_like 'cleanup by a loose foreign key' do
let!(:parent) { create(:project) }
- let!(:model) { create(:ci_freeze_period, project: parent) }
+ let!(:model) { create(:ci_freeze_period, project: parent) }
end
- let(:invalid_cron) { '0 0 0 * *' }
-
it { is_expected.to belong_to(:project) }
it { is_expected.to respond_to(:freeze_start) }
@@ -19,37 +25,142 @@ RSpec.describe Ci::FreezePeriod, type: :model do
it { is_expected.to respond_to(:cron_timezone) }
describe 'cron validations' do
+ let(:invalid_cron) { '0 0 0 * *' }
+
it 'allows valid cron patterns' do
- freeze_period = build(:ci_freeze_period)
+ freeze_period = build_stubbed(:ci_freeze_period)
expect(freeze_period).to be_valid
end
it 'does not allow invalid cron patterns on freeze_start' do
- freeze_period = build(:ci_freeze_period, freeze_start: invalid_cron)
+ freeze_period = build_stubbed(:ci_freeze_period, freeze_start: invalid_cron)
expect(freeze_period).not_to be_valid
end
it 'does not allow invalid cron patterns on freeze_end' do
- freeze_period = build(:ci_freeze_period, freeze_end: invalid_cron)
+ freeze_period = build_stubbed(:ci_freeze_period, freeze_end: invalid_cron)
expect(freeze_period).not_to be_valid
end
it 'does not allow an invalid timezone' do
- freeze_period = build(:ci_freeze_period, cron_timezone: 'invalid')
+ freeze_period = build_stubbed(:ci_freeze_period, cron_timezone: 'invalid')
expect(freeze_period).not_to be_valid
end
context 'when cron contains trailing whitespaces' do
it 'strips the attribute' do
- freeze_period = build(:ci_freeze_period, freeze_start: ' 0 0 * * * ')
+ freeze_period = build_stubbed(:ci_freeze_period, freeze_start: ' 0 0 * * * ')
expect(freeze_period).to be_valid
expect(freeze_period.freeze_start).to eq('0 0 * * *')
end
end
end
+
+ shared_examples 'within freeze period' do |time|
+ it 'is frozen' do
+ travel_to(time) do
+ expect(subject).to eq(Ci::FreezePeriod::STATUS_ACTIVE)
+ end
+ end
+ end
+
+ shared_examples 'outside freeze period' do |time|
+ it 'is not frozen' do
+ travel_to(time) do
+ expect(subject).to eq(Ci::FreezePeriod::STATUS_INACTIVE)
+ end
+ end
+ end
+
+ describe '#status' do
+ subject { freeze_period.status }
+
+ describe 'single freeze period' do
+ let(:freeze_period) do
+ build_stubbed(:ci_freeze_period, project: project)
+ end
+
+ it_behaves_like 'outside freeze period', Time.utc(2020, 4, 10, 22, 59)
+ it_behaves_like 'within freeze period', Time.utc(2020, 4, 10, 23, 1)
+ it_behaves_like 'within freeze period', Time.utc(2020, 4, 13, 6, 59)
+ it_behaves_like 'outside freeze period', Time.utc(2020, 4, 13, 7, 1)
+ end
+
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/370472
+ context 'when period overlaps with itself' do
+ let(:freeze_period) do
+ build_stubbed(:ci_freeze_period, project: project, freeze_start: '* * * 8 *', freeze_end: '* * * 10 *')
+ end
+
+ it_behaves_like 'within freeze period', Time.utc(2020, 8, 11, 0, 0)
+ it_behaves_like 'outside freeze period', Time.utc(2020, 10, 11, 0, 0)
+ end
+ end
+
+ shared_examples 'a freeze period method' do
+ let(:freeze_period) { build_stubbed(:ci_freeze_period, project: project) }
+
+ it 'returns the correct value' do
+ travel_to(now) do
+ expect(freeze_period.send(method)).to eq(expected)
+ end
+ end
+ end
+
+ describe '#active?' do
+ context 'when freeze period status is active' do
+ it_behaves_like 'a freeze period method' do
+ let(:now) { saturday_1200_time }
+ let(:method) { :active? }
+ let(:expected) { true }
+ end
+ end
+
+ context 'when freeze period status is inactive' do
+ it_behaves_like 'a freeze period method' do
+ let(:now) { tuesday_0800_time }
+ let(:method) { :active? }
+ let(:expected) { false }
+ end
+ end
+ end
+
+ describe '#time_start' do
+ it_behaves_like 'a freeze period method' do
+ let(:now) { monday_0700_time }
+ let(:method) { :time_start }
+ let(:expected) { friday_2300_time }
+ end
+ end
+
+ describe '#next_time_start' do
+ let(:next_friday_2300_time) { Time.utc(2020, 4, 17, 23, 0) }
+
+ it_behaves_like 'a freeze period method' do
+ let(:now) { monday_0700_time }
+ let(:method) { :next_time_start }
+ let(:expected) { next_friday_2300_time }
+ end
+ end
+
+ describe '#time_end_from_now' do
+ it_behaves_like 'a freeze period method' do
+ let(:now) { saturday_1200_time }
+ let(:method) { :time_end_from_now }
+ let(:expected) { monday_0700_time }
+ end
+ end
+
+ describe '#time_end_from_start' do
+ it_behaves_like 'a freeze period method' do
+ let(:now) { saturday_1200_time }
+ let(:method) { :time_end_from_start }
+ let(:expected) { monday_0700_time }
+ end
+ end
end
diff --git a/spec/models/ci/freeze_period_status_spec.rb b/spec/models/ci/freeze_period_status_spec.rb
deleted file mode 100644
index ecbb7af64f7..00000000000
--- a/spec/models/ci/freeze_period_status_spec.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-# frozen_string_literal: true
-require 'spec_helper'
-
-RSpec.describe Ci::FreezePeriodStatus do
- let(:project) { create :project }
- # '0 23 * * 5' == "At 23:00 on Friday."", '0 7 * * 1' == "At 07:00 on Monday.""
- let(:friday_2300) { '0 23 * * 5' }
- let(:monday_0700) { '0 7 * * 1' }
-
- subject { described_class.new(project: project).execute }
-
- shared_examples 'within freeze period' do |time|
- it 'is frozen' do
- travel_to(time) do
- expect(subject).to be_truthy
- end
- end
- end
-
- shared_examples 'outside freeze period' do |time|
- it 'is not frozen' do
- travel_to(time) do
- expect(subject).to be_falsy
- end
- end
- end
-
- describe 'single freeze period' do
- let!(:freeze_period) { create(:ci_freeze_period, project: project, freeze_start: friday_2300, freeze_end: monday_0700) }
-
- it_behaves_like 'outside freeze period', Time.utc(2020, 4, 10, 22, 59)
-
- it_behaves_like 'within freeze period', Time.utc(2020, 4, 10, 23, 1)
-
- it_behaves_like 'within freeze period', Time.utc(2020, 4, 13, 6, 59)
-
- it_behaves_like 'outside freeze period', Time.utc(2020, 4, 13, 7, 1)
- end
-
- describe 'multiple freeze periods' do
- # '30 23 * * 5' == "At 23:30 on Friday."", '0 8 * * 1' == "At 08:00 on Monday.""
- let(:friday_2330) { '30 23 * * 5' }
- let(:monday_0800) { '0 8 * * 1' }
-
- let!(:freeze_period_1) { create(:ci_freeze_period, project: project, freeze_start: friday_2300, freeze_end: monday_0700) }
- let!(:freeze_period_2) { create(:ci_freeze_period, project: project, freeze_start: friday_2330, freeze_end: monday_0800) }
-
- it_behaves_like 'outside freeze period', Time.utc(2020, 4, 10, 22, 59)
-
- it_behaves_like 'within freeze period', Time.utc(2020, 4, 10, 23, 29)
-
- it_behaves_like 'within freeze period', Time.utc(2020, 4, 11, 10, 0)
-
- it_behaves_like 'within freeze period', Time.utc(2020, 4, 10, 23, 1)
-
- it_behaves_like 'within freeze period', Time.utc(2020, 4, 13, 6, 59)
-
- it_behaves_like 'within freeze period', Time.utc(2020, 4, 13, 7, 59)
-
- it_behaves_like 'outside freeze period', Time.utc(2020, 4, 13, 8, 1)
- end
-
- # https://gitlab.com/gitlab-org/gitlab/-/issues/370472
- context 'when period overlaps with itself' do
- let!(:freeze_period) { create(:ci_freeze_period, project: project, freeze_start: '* * * 8 *', freeze_end: '* * * 10 *') }
-
- it_behaves_like 'within freeze period', Time.utc(2020, 8, 11, 0, 0)
-
- it_behaves_like 'outside freeze period', Time.utc(2020, 10, 11, 0, 0)
- end
-end
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index 098f8bd4514..18aaab1d1f3 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -356,50 +356,6 @@ RSpec.describe Ci::JobArtifact do
end
end
- describe 'callbacks' do
- describe '#schedule_background_upload' do
- subject { create(:ci_job_artifact, :archive) }
-
- context 'when object storage is disabled' do
- before do
- stub_artifacts_object_storage(enabled: false)
- end
-
- it 'does not schedule the migration' do
- expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
-
- subject
- end
- end
-
- context 'when object storage is enabled' do
- context 'when background upload is enabled' do
- before do
- stub_artifacts_object_storage(background_upload: true)
- end
-
- it 'schedules the model for migration' do
- expect(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async).with('JobArtifactUploader', described_class.name, :file, kind_of(Numeric))
-
- subject
- end
- end
-
- context 'when background upload is disabled' do
- before do
- stub_artifacts_object_storage(background_upload: false)
- end
-
- it 'schedules the model for migration' do
- expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
-
- subject
- end
- end
- end
- end
- end
-
context 'creating the artifact' do
let(:project) { create(:project) }
let(:artifact) { create(:ci_job_artifact, :archive, project: project) }
diff --git a/spec/models/ci/job_token/allowlist_spec.rb b/spec/models/ci/job_token/allowlist_spec.rb
new file mode 100644
index 00000000000..45083d64393
--- /dev/null
+++ b/spec/models/ci/job_token/allowlist_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::JobToken::Allowlist, feature_category: :continuous_integration do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:source_project) { create(:project) }
+
+ let(:allowlist) { described_class.new(source_project, direction: direction) }
+ let(:direction) { :outbound }
+
+ describe '#projects' do
+ subject(:projects) { allowlist.projects }
+
+ context 'when no projects are added to the scope' do
+ [:inbound, :outbound].each do |d|
+ let(:direction) { d }
+
+ it 'returns the project defining the scope' do
+ expect(projects).to contain_exactly(source_project)
+ end
+ end
+ end
+
+ context 'when projects are added to the scope' do
+ include_context 'with scoped projects'
+
+ where(:direction, :additional_project) do
+ :outbound | ref(:outbound_scoped_project)
+ :inbound | ref(:inbound_scoped_project)
+ end
+
+ with_them do
+ it 'returns all projects that can be accessed from a given scope' do
+ expect(projects).to contain_exactly(source_project, additional_project)
+ end
+ end
+ end
+ end
+
+ describe '#includes?' do
+ subject { allowlist.includes?(includes_project) }
+
+ context 'without scoped projects' do
+ let(:unscoped_project) { build(:project) }
+
+ where(:includes_project, :direction, :result) do
+ ref(:source_project) | :outbound | false
+ ref(:source_project) | :inbound | false
+ ref(:unscoped_project) | :outbound | false
+ ref(:unscoped_project) | :inbound | false
+ end
+
+ with_them do
+ it { is_expected.to be result }
+ end
+ end
+
+ context 'with scoped projects' do
+ include_context 'with scoped projects'
+
+ where(:includes_project, :direction, :result) do
+ ref(:source_project) | :outbound | false
+ ref(:source_project) | :inbound | false
+ ref(:inbound_scoped_project) | :outbound | false
+ ref(:inbound_scoped_project) | :inbound | true
+ ref(:outbound_scoped_project) | :outbound | true
+ ref(:outbound_scoped_project) | :inbound | false
+ ref(:unscoped_project1) | :outbound | false
+ ref(:unscoped_project1) | :inbound | false
+ ref(:unscoped_project2) | :outbound | false
+ ref(:unscoped_project2) | :inbound | false
+ end
+
+ with_them do
+ it { is_expected.to be result }
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/job_token/project_scope_link_spec.rb b/spec/models/ci/job_token/project_scope_link_spec.rb
index 92ed86b55b2..91491733c44 100644
--- a/spec/models/ci/job_token/project_scope_link_spec.rb
+++ b/spec/models/ci/job_token/project_scope_link_spec.rb
@@ -2,14 +2,14 @@
require 'spec_helper'
-RSpec.describe Ci::JobToken::ProjectScopeLink do
+RSpec.describe Ci::JobToken::ProjectScopeLink, feature_category: :continuous_integration do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:group) { create(:group) }
+
it { is_expected.to belong_to(:source_project) }
it { is_expected.to belong_to(:target_project) }
it { is_expected.to belong_to(:added_by) }
- let_it_be(:group) { create(:group) }
- let_it_be(:project) { create(:project) }
-
it_behaves_like 'cleanup by a loose foreign key' do
let!(:parent) { create(:user) }
let!(:model) { create(:ci_job_token_project_scope_link, added_by: parent) }
@@ -50,8 +50,8 @@ RSpec.describe Ci::JobToken::ProjectScopeLink do
end
end
- describe '.from_project' do
- subject { described_class.from_project(project) }
+ describe '.with_source' do
+ subject { described_class.with_source(project) }
let!(:source_link) { create(:ci_job_token_project_scope_link, source_project: project) }
let!(:target_link) { create(:ci_job_token_project_scope_link, target_project: project) }
@@ -61,8 +61,8 @@ RSpec.describe Ci::JobToken::ProjectScopeLink do
end
end
- describe '.to_project' do
- subject { described_class.to_project(project) }
+ describe '.with_target' do
+ subject { described_class.with_target(project) }
let!(:source_link) { create(:ci_job_token_project_scope_link, source_project: project) }
let!(:target_link) { create(:ci_job_token_project_scope_link, target_project: project) }
diff --git a/spec/models/ci/job_token/scope_spec.rb b/spec/models/ci/job_token/scope_spec.rb
index 1e3f6d044d2..37c56973506 100644
--- a/spec/models/ci/job_token/scope_spec.rb
+++ b/spec/models/ci/job_token/scope_spec.rb
@@ -2,58 +2,72 @@
require 'spec_helper'
-RSpec.describe Ci::JobToken::Scope do
- let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) }
+RSpec.describe Ci::JobToken::Scope, feature_category: :continuous_integration do
+ let_it_be(:source_project) { create(:project, ci_outbound_job_token_scope_enabled: true) }
- let(:scope) { described_class.new(project) }
+ let(:scope) { described_class.new(source_project) }
describe '#all_projects' do
subject(:all_projects) { scope.all_projects }
context 'when no projects are added to the scope' do
it 'returns the project defining the scope' do
- expect(all_projects).to contain_exactly(project)
+ expect(all_projects).to contain_exactly(source_project)
end
end
- context 'when other projects are added to the scope' do
- let_it_be(:scoped_project) { create(:project) }
- let_it_be(:unscoped_project) { create(:project) }
-
- let!(:link_in_scope) { create(:ci_job_token_project_scope_link, source_project: project, target_project: scoped_project) }
- let!(:link_out_of_scope) { create(:ci_job_token_project_scope_link, target_project: unscoped_project) }
+ context 'when projects are added to the scope' do
+ include_context 'with scoped projects'
it 'returns all projects that can be accessed from a given scope' do
- expect(subject).to contain_exactly(project, scoped_project)
+ expect(subject).to contain_exactly(source_project, outbound_scoped_project)
end
end
end
- describe '#includes?' do
- subject { scope.includes?(target_project) }
+ describe '#allows?' do
+ subject { scope.allows?(includes_project) }
- context 'when param is the project defining the scope' do
- let(:target_project) { project }
+ context 'without scoped projects' do
+ context 'when self referential' do
+ let(:includes_project) { source_project }
- it { is_expected.to be_truthy }
+ it { is_expected.to be_truthy }
+ end
end
- context 'when param is a project in scope' do
- let(:target_link) { create(:ci_job_token_project_scope_link, source_project: project) }
- let(:target_project) { target_link.target_project }
+ context 'with scoped projects' do
+ include_context 'with scoped projects'
- it { is_expected.to be_truthy }
- end
+ context 'when project is in outbound scope' do
+ let(:includes_project) { outbound_scoped_project }
- context 'when param is a project in another scope' do
- let(:scope_link) { create(:ci_job_token_project_scope_link) }
- let(:target_project) { scope_link.target_project }
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when project is in inbound scope' do
+ let(:includes_project) { inbound_scoped_project }
+
+ it { is_expected.to be_falsey }
+ end
- it { is_expected.to be_falsey }
+ context 'when project is linked to a different project' do
+ let(:includes_project) { unscoped_project1 }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when project is unlinked to a project' do
+ let(:includes_project) { unscoped_project2 }
+
+ it { is_expected.to be_falsey }
+ end
context 'when project scope setting is disabled' do
+ let(:includes_project) { unscoped_project1 }
+
before do
- project.ci_outbound_job_token_scope_enabled = false
+ source_project.ci_outbound_job_token_scope_enabled = false
end
it 'considers any project to be part of the scope' do
diff --git a/spec/models/ci/job_variable_spec.rb b/spec/models/ci/job_variable_spec.rb
index 4aebd3283f0..0a65708160a 100644
--- a/spec/models/ci/job_variable_spec.rb
+++ b/spec/models/ci/job_variable_spec.rb
@@ -2,11 +2,63 @@
require 'spec_helper'
-RSpec.describe Ci::JobVariable do
- subject { build(:ci_job_variable) }
-
+RSpec.describe Ci::JobVariable, feature_category: :continuous_integration do
it_behaves_like "CI variable"
- it { is_expected.to belong_to(:job) }
- it { is_expected.to validate_uniqueness_of(:key).scoped_to(:job_id) }
+ describe 'associations' do
+ let!(:job_variable) { create(:ci_job_variable) }
+
+ it { is_expected.to belong_to(:job) }
+ it { is_expected.to validate_uniqueness_of(:key).scoped_to(:job_id) }
+ end
+
+ describe 'partitioning' do
+ let(:job_variable) { build(:ci_job_variable, job: ci_build) }
+
+ context 'with build' do
+ let(:ci_build) { build(:ci_build, partition_id: ci_testing_partition_id) }
+
+ it 'copies the partition_id from build' do
+ expect { job_variable.valid? }.to change { job_variable.partition_id }.to(ci_testing_partition_id)
+ end
+
+ context 'when it is already set' do
+ let(:job_variable) { build(:ci_job_variable, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { job_variable.valid? }.not_to change { job_variable.partition_id }
+ end
+ end
+ end
+
+ context 'without build' do
+ subject(:job_variable) { build(:ci_job_variable, job: nil, partition_id: 125) }
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { job_variable.valid? }.not_to change { job_variable.partition_id }
+ end
+ end
+
+ context 'when using bulk_insert', :ci_partitionable do
+ include Ci::PartitioningHelpers
+
+ let(:new_pipeline) { create(:ci_pipeline) }
+ let(:ci_build) { create(:ci_build, pipeline: new_pipeline) }
+ let(:job_variable_2) { build(:ci_job_variable, job: ci_build) }
+
+ before do
+ stub_current_partition_id
+ end
+
+ it 'creates job variables successfully', :aggregate_failures do
+ described_class.bulk_insert!([job_variable, job_variable_2])
+
+ expect(described_class.count).to eq(2)
+ expect(described_class.first.partition_id).to eq(ci_testing_partition_id)
+ expect(described_class.last.partition_id).to eq(ci_testing_partition_id)
+ end
+ end
+ end
end
diff --git a/spec/models/ci/pending_build_spec.rb b/spec/models/ci/pending_build_spec.rb
index 4bb43233dbd..331522070df 100644
--- a/spec/models/ci/pending_build_spec.rb
+++ b/spec/models/ci/pending_build_spec.rb
@@ -196,6 +196,28 @@ RSpec.describe Ci::PendingBuild do
end
end
+ describe 'partitioning', :ci_partitionable do
+ include Ci::PartitioningHelpers
+
+ before do
+ stub_current_partition_id
+ end
+
+ let(:new_pipeline ) { create(:ci_pipeline, project: pipeline.project) }
+ let(:new_build) { create(:ci_build, pipeline: new_pipeline) }
+
+ it 'assigns the same partition id as the one that build has', :aggregate_failures do
+ expect(new_build.partition_id).to eq ci_testing_partition_id
+ expect(new_build.partition_id).not_to eq pipeline.partition_id
+
+ described_class.upsert_from_build!(build)
+ described_class.upsert_from_build!(new_build)
+
+ expect(build.reload.queuing_entry.partition_id).to eq pipeline.partition_id
+ expect(new_build.reload.queuing_entry.partition_id).to eq ci_testing_partition_id
+ end
+ end
+
it_behaves_like 'cleanup by a loose foreign key' do
let!(:parent) { create(:namespace) }
let!(:model) { create(:ci_pending_build, namespace: parent) }
diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb
index b28b61e2b39..9b70f7c2839 100644
--- a/spec/models/ci/pipeline_schedule_spec.rb
+++ b/spec/models/ci/pipeline_schedule_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::PipelineSchedule do
+RSpec.describe Ci::PipelineSchedule, feature_category: :continuous_integration do
let_it_be_with_reload(:project) { create_default(:project) }
subject { build(:ci_pipeline_schedule) }
@@ -41,6 +41,12 @@ RSpec.describe Ci::PipelineSchedule do
expect(pipeline_schedule).not_to be_valid
end
+ it 'does not allow empty variable key' do
+ pipeline_schedule = build(:ci_pipeline_schedule, variables_attributes: [{ secret_value: 'test_value' }])
+
+ expect(pipeline_schedule).not_to be_valid
+ end
+
context 'when active is false' do
it 'does not allow nullified ref' do
pipeline_schedule = build(:ci_pipeline_schedule, :inactive, ref: nil)
@@ -110,48 +116,18 @@ RSpec.describe Ci::PipelineSchedule do
end
describe '#set_next_run_at' do
- using RSpec::Parameterized::TableSyntax
-
- where(:worker_cron, :schedule_cron, :plan_limit, :now, :result) do
- '0 1 2 3 *' | '0 1 * * *' | nil | Time.zone.local(2021, 3, 2, 1, 0) | Time.zone.local(2022, 3, 2, 1, 0)
- '0 1 2 3 *' | '0 1 * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | Time.zone.local(2021, 3, 2, 1, 0) | Time.zone.local(2022, 3, 2, 1, 0)
- '*/5 * * * *' | '*/1 * * * *' | nil | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 5)
- '*/5 * * * *' | '*/1 * * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 0)
- '*/5 * * * *' | '*/1 * * * *' | (1.day.in_minutes / 10).to_i | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 10)
- '*/5 * * * *' | '*/1 * * * *' | 200 | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 10)
- '*/5 * * * *' | '0 * * * *' | nil | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 5)
- '*/5 * * * *' | '0 * * * *' | (1.day.in_minutes / 10).to_i | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 0)
- '*/5 * * * *' | '0 * * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 0)
- '*/5 * * * *' | '0 * * * *' | (1.day.in_minutes / 2.hours.in_minutes).to_i | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 5)
- '*/5 * * * *' | '0 1 * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | Time.zone.local(2021, 5, 27, 1, 0) | Time.zone.local(2021, 5, 28, 1, 0)
- '*/5 * * * *' | '0 1 * * *' | (1.day.in_minutes / 10).to_i | Time.zone.local(2021, 5, 27, 1, 0) | Time.zone.local(2021, 5, 28, 1, 0)
- '*/5 * * * *' | '0 1 * * *' | (1.day.in_minutes / 8).to_i | Time.zone.local(2021, 5, 27, 1, 0) | Time.zone.local(2021, 5, 28, 1, 0)
- '*/5 * * * *' | '0 1 1 * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | Time.zone.local(2021, 5, 1, 1, 0) | Time.zone.local(2021, 6, 1, 1, 0)
- '*/9 * * * *' | '0 1 1 * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | Time.zone.local(2021, 5, 1, 1, 9) | Time.zone.local(2021, 6, 1, 1, 0)
- '*/5 * * * *' | '59 14 * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | Time.zone.local(2021, 5, 1, 15, 0) | Time.zone.local(2021, 5, 2, 15, 0)
- '*/5 * * * *' | '45 21 1 2 *' | (1.day.in_minutes / 5).to_i | Time.zone.local(2021, 2, 1, 21, 45) | Time.zone.local(2022, 2, 1, 21, 45)
- end
+ let(:now) { Time.zone.local(2021, 3, 2, 1, 0) }
+ let(:pipeline_schedule) { create(:ci_pipeline_schedule, cron: "0 1 * * *") }
- with_them do
- let(:pipeline_schedule) { create(:ci_pipeline_schedule, cron: schedule_cron) }
+ it 'calls fallback method next_run_at if there is no plan limit' do
+ allow(Settings).to receive(:cron_jobs).and_return({ 'pipeline_schedule_worker' => { 'cron' => "0 1 2 3 *" } })
- before do
- allow(Settings).to receive(:cron_jobs) do
- { 'pipeline_schedule_worker' => { 'cron' => worker_cron } }
- end
+ travel_to(now) do
+ expect(pipeline_schedule).to receive(:calculate_next_run_at).and_call_original
- create(:plan_limits, :default_plan, ci_daily_pipeline_schedule_triggers: plan_limit) if plan_limit
+ pipeline_schedule.set_next_run_at
- # Setting this here to override initial save with the current time
- pipeline_schedule.next_run_at = now
- end
-
- it 'updates next_run_at' do
- travel_to(now) do
- pipeline_schedule.set_next_run_at
-
- expect(pipeline_schedule.next_run_at).to eq(result)
- end
+ expect(pipeline_schedule.next_run_at).to eq(Time.zone.local(2022, 3, 2, 1, 0))
end
end
@@ -288,6 +264,17 @@ RSpec.describe Ci::PipelineSchedule do
end
end
+ describe '#worker_cron' do
+ before do
+ allow(Settings).to receive(:cron_jobs)
+ .and_return({ pipeline_schedule_worker: { cron: "* 1 2 3 4" } }.with_indifferent_access)
+ end
+
+ it "returns cron expression set in Settings" do
+ expect(subject.worker_cron_expression).to eq("* 1 2 3 4")
+ end
+ end
+
context 'loose foreign key on ci_pipeline_schedules.project_id' do
it_behaves_like 'cleanup by a loose foreign key' do
let!(:parent) { create(:project) }
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 2c945898e61..b72693d9994 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -219,6 +219,29 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ describe '.for_name' do
+ subject { described_class.for_name(name) }
+
+ let_it_be(:pipeline1) { create(:ci_pipeline, name: 'Build pipeline') }
+ let_it_be(:pipeline2) { create(:ci_pipeline, name: 'Chatops pipeline') }
+
+ context 'when name exists' do
+ let(:name) { 'build Pipeline' }
+
+ it 'performs case insensitive compare' do
+ is_expected.to contain_exactly(pipeline1)
+ end
+ end
+
+ context 'when name does not exist' do
+ let(:name) { 'absent-name' }
+
+ it 'returns empty' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
describe '.created_after' do
let_it_be(:old_pipeline) { create(:ci_pipeline, created_at: 1.week.ago) }
let_it_be(:pipeline) { create(:ci_pipeline) }
@@ -5287,6 +5310,20 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
end
+
+ context 'when the current user is not the bridge user' do
+ let(:current_user) { create(:user) }
+
+ before do
+ project.add_maintainer(current_user)
+ end
+
+ it 'changes bridge user to current user' do
+ expect { reset_bridge }
+ .to change { bridge.reload.user }
+ .from(owner).to(current_user)
+ end
+ end
end
context 'when the user does not have permissions for the processable' do
@@ -5305,6 +5342,15 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
.and not_change { bridge_dependant_dag_job.reload.status }
end
end
+
+ context 'when the current user is not the bridge user' do
+ let(:current_user) { create(:user) }
+
+ it 'does not change bridge user' do
+ expect { reset_bridge }
+ .to not_change { bridge.reload.user }
+ end
+ end
end
end
diff --git a/spec/models/ci/processable_spec.rb b/spec/models/ci/processable_spec.rb
index e62e5f84a6d..07fac4ee2f7 100644
--- a/spec/models/ci/processable_spec.rb
+++ b/spec/models/ci/processable_spec.rb
@@ -73,6 +73,7 @@ RSpec.describe Ci::Processable do
job_artifacts_network_referee job_artifacts_dotenv
job_artifacts_cobertura needs job_artifacts_accessibility
job_artifacts_requirements job_artifacts_coverage_fuzzing
+ job_artifacts_requirements_v2
job_artifacts_api_fuzzing terraform_state_versions job_artifacts_cyclonedx].freeze
end
@@ -423,8 +424,8 @@ RSpec.describe Ci::Processable do
it 'returns all needs attributes' do
is_expected.to contain_exactly(
- { 'artifacts' => true, 'name' => 'test1', 'optional' => false },
- { 'artifacts' => true, 'name' => 'test2', 'optional' => false }
+ { 'artifacts' => true, 'name' => 'test1', 'optional' => false, 'partition_id' => build.partition_id },
+ { 'artifacts' => true, 'name' => 'test2', 'optional' => false, 'partition_id' => build.partition_id }
)
end
end
diff --git a/spec/models/ci/resource_group_spec.rb b/spec/models/ci/resource_group_spec.rb
index e8eccc233db..01acf5194f0 100644
--- a/spec/models/ci/resource_group_spec.rb
+++ b/spec/models/ci/resource_group_spec.rb
@@ -33,7 +33,13 @@ RSpec.describe Ci::ResourceGroup do
end
end
- describe '#assign_resource_to' do
+ describe '#assign_resource_to', :ci_partitionable do
+ include Ci::PartitioningHelpers
+
+ before do
+ stub_current_partition_id
+ end
+
subject { resource_group.assign_resource_to(build) }
let(:build) { create(:ci_build) }
@@ -41,10 +47,12 @@ RSpec.describe Ci::ResourceGroup do
it 'retains resource for the processable' do
expect(resource_group.resources.first.processable).to be_nil
+ expect(resource_group.resources.first.partition_id).to be_nil
is_expected.to eq(true)
expect(resource_group.resources.first.processable).to eq(build)
+ expect(resource_group.resources.first.partition_id).to eq(build.partition_id)
end
context 'when there are no free resources' do
@@ -66,7 +74,13 @@ RSpec.describe Ci::ResourceGroup do
end
end
- describe '#release_resource_from' do
+ describe '#release_resource_from', :ci_partitionable do
+ include Ci::PartitioningHelpers
+
+ before do
+ stub_current_partition_id
+ end
+
subject { resource_group.release_resource_from(build) }
let(:build) { create(:ci_build) }
@@ -79,10 +93,12 @@ RSpec.describe Ci::ResourceGroup do
it 'releases resource from the build' do
expect(resource_group.resources.first.processable).to eq(build)
+ expect(resource_group.resources.first.partition_id).to eq(build.partition_id)
is_expected.to eq(true)
expect(resource_group.resources.first.processable).to be_nil
+ expect(resource_group.resources.first.partition_id).to be_nil
end
end
diff --git a/spec/models/ci/runner_namespace_spec.rb b/spec/models/ci/runner_namespace_spec.rb
index 2d1fe11147c..6cbb151d703 100644
--- a/spec/models/ci/runner_namespace_spec.rb
+++ b/spec/models/ci/runner_namespace_spec.rb
@@ -12,4 +12,27 @@ RSpec.describe Ci::RunnerNamespace do
let!(:parent) { model.namespace }
end
+
+ describe '.for_runner' do
+ subject(:for_runner) { described_class.for_runner(runner_ids) }
+
+ let_it_be(:group) { create(:group) }
+ let_it_be(:runners) { create_list(:ci_runner, 3, :group, groups: [group]) }
+
+ context 'with runner ids' do
+ let(:runner_ids) { runners[1..2].map(&:id) }
+
+ it 'returns requested runner namespaces' do
+ is_expected.to eq(runners[1..2].flat_map(&:runner_namespaces))
+ end
+ end
+
+ context 'with runners' do
+ let(:runner_ids) { runners.first }
+
+ it 'returns requested runner namespaces' do
+ is_expected.to eq(runners.first.runner_namespaces)
+ end
+ end
+ end
end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 13eb7086586..803b766c822 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::Runner do
+RSpec.describe Ci::Runner, feature_category: :runner do
include StubGitlabCalls
it_behaves_like 'having unique enum values'
@@ -701,6 +701,30 @@ RSpec.describe Ci::Runner do
it { is_expected.to eq([runner1]) }
end
+ describe '.with_running_builds' do
+ subject { described_class.with_running_builds }
+
+ let_it_be(:runner1) { create(:ci_runner) }
+
+ context 'with no builds running' do
+ it { is_expected.to be_empty }
+ end
+
+ context 'with single build running on runner2' do
+ let(:runner2) { create(:ci_runner) }
+ let(:runner3) { create(:ci_runner) }
+
+ before do
+ project = create(:project, :repository)
+ pipeline = create(:ci_pipeline, project: project)
+ create(:ci_build, :running, runner: runner2, pipeline: pipeline)
+ create(:ci_build, :running, runner: runner3, pipeline: pipeline)
+ end
+
+ it { is_expected.to contain_exactly(runner2, runner3) }
+ end
+ end
+
describe '#matches_build?' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/models/ci/runner_version_spec.rb b/spec/models/ci/runner_version_spec.rb
index 7a4b2e8f21e..552b271fe85 100644
--- a/spec/models/ci/runner_version_spec.rb
+++ b/spec/models/ci/runner_version_spec.rb
@@ -2,16 +2,16 @@
require 'spec_helper'
-RSpec.describe Ci::RunnerVersion do
- it_behaves_like 'having unique enum values'
+RSpec.describe Ci::RunnerVersion, feature_category: :runner_fleet do
+ let_it_be(:runner_version_recommended) do
+ create(:ci_runner_version, version: 'abc234', status: :recommended)
+ end
let_it_be(:runner_version_not_available) do
create(:ci_runner_version, version: 'abc123', status: :not_available)
end
- let_it_be(:runner_version_recommended) do
- create(:ci_runner_version, version: 'abc234', status: :recommended)
- end
+ it_behaves_like 'having unique enum values'
describe '.not_available' do
subject { described_class.not_available }
@@ -28,11 +28,9 @@ RSpec.describe Ci::RunnerVersion do
end
it 'contains any valid or unprocessed runner version that is not already recommended' do
- is_expected.to match_array([
- runner_version_nil,
- runner_version_not_available,
- runner_version_available
- ])
+ is_expected.to match_array(
+ [runner_version_nil, runner_version_not_available, runner_version_available]
+ )
end
end
diff --git a/spec/models/ci/running_build_spec.rb b/spec/models/ci/running_build_spec.rb
index d2f74494308..1a5ea044ba3 100644
--- a/spec/models/ci/running_build_spec.rb
+++ b/spec/models/ci/running_build_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::RunningBuild do
+RSpec.describe Ci::RunningBuild, feature_category: :continuous_integration do
let_it_be(:project) { create(:project) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
@@ -50,6 +50,28 @@ RSpec.describe Ci::RunningBuild do
end
end
+ describe 'partitioning', :ci_partitionable do
+ include Ci::PartitioningHelpers
+
+ before do
+ stub_current_partition_id
+ end
+
+ let(:new_pipeline ) { create(:ci_pipeline, project: pipeline.project) }
+ let(:new_build) { create(:ci_build, :running, pipeline: new_pipeline, runner: runner) }
+
+ it 'assigns the same partition id as the one that build has', :aggregate_failures do
+ expect(new_build.partition_id).to eq ci_testing_partition_id
+ expect(new_build.partition_id).not_to eq pipeline.partition_id
+
+ described_class.upsert_shared_runner_build!(build)
+ described_class.upsert_shared_runner_build!(new_build)
+
+ expect(build.reload.runtime_metadata.partition_id).to eq pipeline.partition_id
+ expect(new_build.reload.runtime_metadata.partition_id).to eq ci_testing_partition_id
+ end
+ end
+
it_behaves_like 'cleanup by a loose foreign key' do
let!(:parent) { create(:project) }
let!(:model) { create(:ci_running_build, project: parent) }
diff --git a/spec/models/ci/secure_file_spec.rb b/spec/models/ci/secure_file_spec.rb
index 4413bd8e98b..87077fe2db1 100644
--- a/spec/models/ci/secure_file_spec.rb
+++ b/spec/models/ci/secure_file_spec.rb
@@ -138,17 +138,48 @@ RSpec.describe Ci::SecureFile do
end
describe '#update_metadata!' do
- it 'assigns the expected metadata when a parsable file is supplied' do
+ it 'assigns the expected metadata when a parsable .cer file is supplied' do
file = create(:ci_secure_file, name: 'file1.cer',
file: CarrierWaveStringFile.new(fixture_file('ci_secure_files/sample.cer')))
file.update_metadata!
+ file.reload
+
expect(file.expires_at).to eq(DateTime.parse('2022-04-26 19:20:40'))
expect(file.metadata['id']).to eq('33669367788748363528491290218354043267')
expect(file.metadata['issuer']['CN']).to eq('Apple Worldwide Developer Relations Certification Authority')
expect(file.metadata['subject']['OU']).to eq('N7SYAN8PX8')
end
+ it 'assigns the expected metadata when a parsable .p12 file is supplied' do
+ file = create(:ci_secure_file, name: 'file1.p12',
+ file: CarrierWaveStringFile.new(fixture_file('ci_secure_files/sample.p12')))
+ file.update_metadata!
+
+ file.reload
+
+ expect(file.expires_at).to eq(DateTime.parse('2022-09-21 14:56:00'))
+ expect(file.metadata['id']).to eq('75949910542696343243264405377658443914')
+ expect(file.metadata['issuer']['CN']).to eq('Apple Worldwide Developer Relations Certification Authority')
+ expect(file.metadata['subject']['OU']).to eq('N7SYAN8PX8')
+ end
+
+ it 'assigns the expected metadata when a parsable .mobileprovision file is supplied' do
+ file = create(:ci_secure_file, name: 'file1.mobileprovision',
+ file: CarrierWaveStringFile.new(
+ fixture_file('ci_secure_files/sample.mobileprovision')
+ ))
+ file.update_metadata!
+
+ file.reload
+
+ expect(file.expires_at).to eq(DateTime.parse('2023-08-01 23:15:13'))
+ expect(file.metadata['id']).to eq('6b9fcce1-b9a9-4b37-b2ce-ec4da2044abf')
+ expect(file.metadata['platforms'].first).to eq('iOS')
+ expect(file.metadata['app_name']).to eq('iOS Demo')
+ expect(file.metadata['app_id']).to eq('match Development com.gitlab.ios-demo')
+ end
+
it 'logs an error when something goes wrong with the file parsing' do
corrupt_file = create(:ci_secure_file, name: 'file1.cer', file: CarrierWaveStringFile.new('11111111'))
message = 'Validation failed: Metadata must be a valid json schema - not enough data.'
diff --git a/spec/models/ci/sources/pipeline_spec.rb b/spec/models/ci/sources/pipeline_spec.rb
index fdc1c111c40..707872d0a15 100644
--- a/spec/models/ci/sources/pipeline_spec.rb
+++ b/spec/models/ci/sources/pipeline_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::Sources::Pipeline do
+RSpec.describe Ci::Sources::Pipeline, feature_category: :continuous_integration do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:pipeline) }
@@ -31,4 +31,20 @@ RSpec.describe Ci::Sources::Pipeline do
let!(:model) { create(:ci_sources_pipeline, project: parent) }
end
end
+
+ describe 'partitioning', :ci_partitioning do
+ include Ci::PartitioningHelpers
+
+ let(:new_pipeline) { create(:ci_pipeline) }
+ let(:source_pipeline) { create(:ci_sources_pipeline, pipeline: new_pipeline) }
+
+ before do
+ stub_current_partition_id
+ end
+
+ it 'assigns partition_id and source_partition_id from pipeline and source_job', :aggregate_failures do
+ expect(source_pipeline.partition_id).to eq(ci_testing_partition_id)
+ expect(source_pipeline.source_partition_id).to eq(ci_testing_partition_id)
+ end
+ end
end
diff --git a/spec/models/ci/unit_test_failure_spec.rb b/spec/models/ci/unit_test_failure_spec.rb
index f9b8c66b603..7ffd103bc7d 100644
--- a/spec/models/ci/unit_test_failure_spec.rb
+++ b/spec/models/ci/unit_test_failure_spec.rb
@@ -70,4 +70,46 @@ RSpec.describe Ci::UnitTestFailure do
end
end
end
+
+ describe 'partitioning', :ci_partitionable do
+ let(:project) { FactoryBot.build(:project) }
+ let(:unit_test) { FactoryBot.build(:ci_unit_test, project: project) }
+
+ context 'with build' do
+ let(:build) { FactoryBot.build(:ci_build, partition_id: ci_testing_partition_id) }
+ let(:unit_test_failure) do
+ FactoryBot.build(:ci_unit_test_failure, build: build, unit_test: unit_test, failed_at: 1.day.ago)
+ end
+
+ it 'copies the partition_id from build' do
+ expect { unit_test_failure.valid? }.to change { unit_test_failure.partition_id }.to(ci_testing_partition_id)
+ end
+
+ context 'when it is already set' do
+ let(:unit_test_failure) do
+ FactoryBot.build(
+ :ci_unit_test_failure,
+ build: build,
+ unit_test: unit_test,
+ failed_at: 1.day.ago,
+ partition_id: 125
+ )
+ end
+
+ it 'does not change the partition_id value' do
+ expect { unit_test_failure.valid? }.not_to change { unit_test_failure.partition_id }
+ end
+ end
+ end
+
+ context 'without build' do
+ subject(:unit_test_failure) { FactoryBot.build(:ci_unit_test_failure, build: nil, partition_id: 125) }
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { unit_test_failure.valid? }.not_to change { unit_test_failure.partition_id }
+ end
+ end
+ end
end
diff --git a/spec/models/clusters/agent_token_spec.rb b/spec/models/clusters/agent_token_spec.rb
index efa2a3eb09b..74723e3abd8 100644
--- a/spec/models/clusters/agent_token_spec.rb
+++ b/spec/models/clusters/agent_token_spec.rb
@@ -49,4 +49,12 @@ RSpec.describe Clusters::AgentToken do
expect(agent_token.token.length).to be >= 50
end
end
+
+ describe '#to_ability_name' do
+ it 'is :cluster' do
+ agent_token = build(:cluster_agent_token)
+
+ expect(agent_token.to_ability_name).to eq :cluster
+ end
+ end
end
diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb
index e5caa11452e..2be59e5f515 100644
--- a/spec/models/clusters/applications/ingress_spec.rb
+++ b/spec/models/clusters/applications/ingress_spec.rb
@@ -5,6 +5,11 @@ require 'spec_helper'
RSpec.describe Clusters::Applications::Ingress do
let(:ingress) { create(:clusters_applications_ingress) }
+ before do
+ allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in)
+ allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async)
+ end
+
it_behaves_like 'having unique enum values'
include_examples 'cluster application core specs', :clusters_applications_ingress
@@ -13,11 +18,6 @@ RSpec.describe Clusters::Applications::Ingress do
include_examples 'cluster application helm specs', :clusters_applications_ingress
include_examples 'cluster application initial status specs'
- before do
- allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in)
- allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async)
- end
-
describe 'default values' do
it { expect(subject.ingress_type).to eq("nginx") }
it { expect(subject.version).to eq(described_class::VERSION) }
diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb
index 3914450339a..f6b13f4a93f 100644
--- a/spec/models/clusters/applications/knative_spec.rb
+++ b/spec/models/clusters/applications/knative_spec.rb
@@ -5,18 +5,18 @@ require 'spec_helper'
RSpec.describe Clusters::Applications::Knative do
let(:knative) { create(:clusters_applications_knative) }
- include_examples 'cluster application core specs', :clusters_applications_knative
- include_examples 'cluster application status specs', :clusters_applications_knative
- include_examples 'cluster application helm specs', :clusters_applications_knative
- include_examples 'cluster application version specs', :clusters_applications_knative
- include_examples 'cluster application initial status specs'
-
before do
allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in)
allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async)
allow(ClusterConfigureIstioWorker).to receive(:perform_async)
end
+ include_examples 'cluster application core specs', :clusters_applications_knative
+ include_examples 'cluster application status specs', :clusters_applications_knative
+ include_examples 'cluster application helm specs', :clusters_applications_knative
+ include_examples 'cluster application version specs', :clusters_applications_knative
+ include_examples 'cluster application initial status specs'
+
describe 'associations' do
it { is_expected.to have_one(:serverless_domain_cluster).class_name('::Serverless::DomainCluster').with_foreign_key('clusters_applications_knative_id').inverse_of(:knative) }
end
diff --git a/spec/models/commit_signatures/gpg_signature_spec.rb b/spec/models/commit_signatures/gpg_signature_spec.rb
index 1ffaaeba396..75cc5d448df 100644
--- a/spec/models/commit_signatures/gpg_signature_spec.rb
+++ b/spec/models/commit_signatures/gpg_signature_spec.rb
@@ -23,6 +23,7 @@ RSpec.describe CommitSignatures::GpgSignature do
it_behaves_like 'having unique enum values'
it_behaves_like 'commit signature'
+ it_behaves_like 'signature with type checking', :gpg
describe 'associations' do
it { is_expected.to belong_to(:gpg_key) }
@@ -86,9 +87,9 @@ RSpec.describe CommitSignatures::GpgSignature do
end
end
- describe '#user' do
+ describe '#signed_by_user' do
it 'retrieves the gpg_key user' do
- expect(signature.user).to eq(gpg_key.user)
+ expect(signature.signed_by_user).to eq(gpg_key.user)
end
end
end
diff --git a/spec/models/commit_signatures/ssh_signature_spec.rb b/spec/models/commit_signatures/ssh_signature_spec.rb
index 08530bf6964..629d9c5ec53 100644
--- a/spec/models/commit_signatures/ssh_signature_spec.rb
+++ b/spec/models/commit_signatures/ssh_signature_spec.rb
@@ -22,6 +22,7 @@ RSpec.describe CommitSignatures::SshSignature do
it_behaves_like 'having unique enum values'
it_behaves_like 'commit signature'
+ it_behaves_like 'signature with type checking', :ssh
describe 'associations' do
it { is_expected.to belong_to(:key).optional }
@@ -37,4 +38,10 @@ RSpec.describe CommitSignatures::SshSignature do
).to contain_exactly(signature, another_signature)
end
end
+
+ describe '#signed_by_user' do
+ it 'returns the user associated with the SSH key' do
+ expect(signature.signed_by_user).to eq(ssh_key.user)
+ end
+ end
end
diff --git a/spec/models/commit_signatures/x509_commit_signature_spec.rb b/spec/models/commit_signatures/x509_commit_signature_spec.rb
index b971fd078e2..cceb96ec70d 100644
--- a/spec/models/commit_signatures/x509_commit_signature_spec.rb
+++ b/spec/models/commit_signatures/x509_commit_signature_spec.rb
@@ -23,6 +23,7 @@ RSpec.describe CommitSignatures::X509CommitSignature do
it_behaves_like 'having unique enum values'
it_behaves_like 'commit signature'
+ it_behaves_like 'signature with type checking', :x509
describe 'validation' do
it { is_expected.to validate_presence_of(:x509_certificate_id) }
@@ -37,12 +38,12 @@ RSpec.describe CommitSignatures::X509CommitSignature do
let!(:user) { create(:user, email: X509Helpers::User1.certificate_email) }
it 'returns user' do
- expect(described_class.safe_create!(attributes).user).to eq(user)
+ expect(described_class.safe_create!(attributes).signed_by_user).to eq(user)
end
end
it 'if email is not assigned to a user, return nil' do
- expect(described_class.safe_create!(attributes).user).to be_nil
+ expect(described_class.safe_create!(attributes).signed_by_user).to be_nil
end
end
end
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index bab6247d4f9..4b5aabe745b 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -828,12 +828,14 @@ eos
describe 'signed commits' do
let(:gpg_signed_commit) { project.commit_by(oid: '0b4bc9a49b562e85de7cc9e834518ea6828729b9') }
let(:x509_signed_commit) { project.commit_by(oid: '189a6c924013fc3fe40d6f1ec1dc20214183bc97') }
+ let(:ssh_signed_commit) { project.commit_by(oid: '7b5160f9bb23a3d58a0accdbe89da13b96b1ece9') }
let(:unsigned_commit) { project.commit_by(oid: '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51') }
let!(:commit) { create(:commit, project: project) }
it 'returns signature_type properly' do
expect(gpg_signed_commit.signature_type).to eq(:PGP)
expect(x509_signed_commit.signature_type).to eq(:X509)
+ expect(ssh_signed_commit.signature_type).to eq(:SSH)
expect(unsigned_commit.signature_type).to eq(:NONE)
expect(commit.signature_type).to eq(:NONE)
end
@@ -841,9 +843,24 @@ eos
it 'returns has_signature? properly' do
expect(gpg_signed_commit.has_signature?).to be_truthy
expect(x509_signed_commit.has_signature?).to be_truthy
+ expect(ssh_signed_commit.has_signature?).to be_truthy
expect(unsigned_commit.has_signature?).to be_falsey
expect(commit.has_signature?).to be_falsey
end
+
+ context 'when feature flag "ssh_commit_signatures" is disabled' do
+ before do
+ stub_feature_flags(ssh_commit_signatures: false)
+ end
+
+ it 'reports no signature' do
+ expect(ssh_signed_commit).not_to have_signature
+ end
+
+ it 'does not return signature data' do
+ expect(ssh_signed_commit.signature).to be_nil
+ end
+ end
end
describe '#has_been_reverted?' do
diff --git a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
index 358000ee174..e8d84fe9630 100644
--- a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
+++ b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
@@ -27,6 +27,7 @@ RSpec.describe BatchDestroyDependentAssociations do
let_it_be(:build) { create(:ci_build, project: project) }
let_it_be(:notification_setting) { create(:notification_setting, project: project) }
let_it_be(:note) { create(:note, project: project) }
+ let_it_be(:merge_request) { create(:merge_request, source_project: project) }
it 'destroys multiple notes' do
create(:note, project: project)
@@ -51,9 +52,10 @@ RSpec.describe BatchDestroyDependentAssociations do
end
it 'excludes associations' do
- project.destroy_dependent_associations_in_batches(exclude: [:notes])
+ project.destroy_dependent_associations_in_batches(exclude: [:merge_requests])
- expect(Note.count).to eq(1)
+ expect(MergeRequest.count).to eq(1)
+ expect(Note.count).to eq(0)
expect(Ci::Build.count).to eq(1)
expect(User.count).to be > 0
expect(NotificationSetting.count).to eq(User.count)
diff --git a/spec/models/concerns/bulk_insertable_associations_spec.rb b/spec/models/concerns/bulk_insertable_associations_spec.rb
index 9713f1ce9a4..3187dcd8f93 100644
--- a/spec/models/concerns/bulk_insertable_associations_spec.rb
+++ b/spec/models/concerns/bulk_insertable_associations_spec.rb
@@ -3,34 +3,41 @@
require 'spec_helper'
RSpec.describe BulkInsertableAssociations do
- class BulkFoo < ApplicationRecord
- include BulkInsertSafe
+ before do
+ stub_const('BulkFoo', Class.new(ApplicationRecord))
+ stub_const('BulkBar', Class.new(ApplicationRecord))
+ stub_const('SimpleBar', Class.new(ApplicationRecord))
+ stub_const('BulkParent', Class.new(ApplicationRecord))
- self.table_name = '_test_bulk_foos'
+ BulkFoo.class_eval do
+ include BulkInsertSafe
- validates :name, presence: true
- end
+ self.table_name = '_test_bulk_foos'
- class BulkBar < ApplicationRecord
- include BulkInsertSafe
+ validates :name, presence: true
+ end
- self.table_name = '_test_bulk_bars'
- end
+ BulkBar.class_eval do
+ include BulkInsertSafe
- SimpleBar = Class.new(ApplicationRecord) do
- self.table_name = '_test_simple_bars'
- end
+ self.table_name = '_test_bulk_bars'
+ end
+
+ SimpleBar.class_eval do
+ self.table_name = '_test_simple_bars'
+ end
- class BulkParent < ApplicationRecord
- include BulkInsertableAssociations
+ BulkParent.class_eval do
+ include BulkInsertableAssociations
- self.table_name = '_test_bulk_parents'
+ self.table_name = '_test_bulk_parents'
- has_many :bulk_foos, class_name: 'BulkFoo'
- has_many :bulk_hunks, class_name: 'BulkFoo'
- has_many :bulk_bars, class_name: 'BulkBar'
- has_many :simple_bars, class_name: 'SimpleBar' # not `BulkInsertSafe`
- has_one :bulk_foo # not supported
+ has_many :bulk_foos, class_name: 'BulkFoo'
+ has_many :bulk_hunks, class_name: 'BulkFoo'
+ has_many :bulk_bars, class_name: 'BulkBar'
+ has_many :simple_bars, class_name: 'SimpleBar' # not `BulkInsertSafe`
+ has_one :bulk_foo # not supported
+ end
end
before(:all) do
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index 19b9a1519eb..f85f636ebe5 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -231,7 +231,7 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
end
describe '#updated_cached_html_for' do
- let(:thing) { klass.new(description: markdown, description_html: html, cached_markdown_version: cache_version) }
+ let(:thing) { klass.new(project_id: project.id, namespace_id: project.project_namespace_id, description: markdown, description_html: html, cached_markdown_version: cache_version) }
context 'when the markdown cache is outdated' do
before do
@@ -286,7 +286,7 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
end
shared_examples 'a class with mentionable markdown fields' do
- let(:mentionable) { klass.new(description: markdown, description_html: html, title: markdown, title_html: html, cached_markdown_version: cache_version) }
+ let(:mentionable) { klass.new(project_id: project.id, namespace_id: project.project_namespace_id, description: markdown, description_html: html, title: markdown, title_html: html, cached_markdown_version: cache_version) }
context 'when klass is a Mentionable', :aggregate_failures do
before do
@@ -336,7 +336,7 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
end
context 'when the markdown field also a mentionable attribute' do
- let(:thing) { klass.new(description: markdown, description_html: html, cached_markdown_version: cache_version) }
+ let(:thing) { klass.new(project_id: project.id, namespace_id: project.project_namespace_id, description: markdown, description_html: html, cached_markdown_version: cache_version) }
it 'calls #store_mentions!' do
expect(thing).to receive(:mentionable_attributes_changed?).and_return(true)
@@ -349,7 +349,7 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
end
context 'when the markdown field is not mentionable attribute' do
- let(:thing) { klass.new(title: markdown, title_html: html, cached_markdown_version: cache_version) }
+ let(:thing) { klass.new(project_id: project.id, namespace_id: project.project_namespace_id, title: markdown, title_html: html, cached_markdown_version: cache_version) }
it 'does not call #store_mentions!' do
expect(thing).not_to receive(:store_mentions!)
@@ -363,7 +363,7 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
end
context 'when the markdown field does not exist' do
- let(:thing) { klass.new(cached_markdown_version: cache_version) }
+ let(:thing) { klass.new(project_id: project.id, namespace_id: project.project_namespace_id, cached_markdown_version: cache_version) }
it 'does not call #store_mentions!' do
expect(thing).not_to receive(:store_mentions!)
@@ -376,13 +376,15 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
end
context 'for Active record classes' do
+ let_it_be(:project) { create(:project) }
+
let(:klass) { ar_class }
it_behaves_like 'a class with cached markdown fields'
it_behaves_like 'a class with mentionable markdown fields'
describe '#attribute_invalidated?' do
- let(:thing) { klass.create!(description: markdown, description_html: html, cached_markdown_version: cache_version) }
+ let(:thing) { klass.create!(project_id: project.id, namespace_id: project.project_namespace_id, description: markdown, description_html: html, cached_markdown_version: cache_version) }
it 'returns true when cached_markdown_version is different' do
thing.cached_markdown_version += 1
@@ -425,7 +427,7 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
let(:thing) do
# This forces the record to have outdated HTML. We can't use `create` because the `before_create` hook
# would re-render the HTML to the latest version
- klass.create!.tap do |thing|
+ klass.create!(project_id: project.id, namespace_id: project.project_namespace_id).tap do |thing|
thing.update_columns(description: markdown, description_html: old_html, cached_markdown_version: old_version)
end
end
@@ -441,6 +443,8 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
end
context 'for other classes' do
+ let_it_be(:project) { create(:project) }
+
let(:klass) { other_class }
it_behaves_like 'a class with cached markdown fields'
diff --git a/spec/models/concerns/ci/partitionable/partitioned_filter_spec.rb b/spec/models/concerns/ci/partitionable/partitioned_filter_spec.rb
new file mode 100644
index 00000000000..bb25d7d1665
--- /dev/null
+++ b/spec/models/concerns/ci/partitionable/partitioned_filter_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::Partitionable::PartitionedFilter, :aggregate_failures, feature_category: :continuous_integration do
+ before do
+ create_tables(<<~SQL)
+ CREATE TABLE _test_ci_jobs_metadata (
+ id serial NOT NULL,
+ partition_id int NOT NULL DEFAULT 10,
+ name text,
+ PRIMARY KEY (id, partition_id)
+ ) PARTITION BY LIST(partition_id);
+
+ CREATE TABLE _test_ci_jobs_metadata_1
+ PARTITION OF _test_ci_jobs_metadata
+ FOR VALUES IN (10);
+ SQL
+ end
+
+ let(:model) do
+ Class.new(Ci::ApplicationRecord) do
+ include Ci::Partitionable::PartitionedFilter
+
+ self.primary_key = :id
+ self.table_name = :_test_ci_jobs_metadata
+
+ def self.name
+ 'TestCiJobMetadata'
+ end
+ end
+ end
+
+ let!(:record) { model.create! }
+
+ let(:where_filter) do
+ /WHERE "_test_ci_jobs_metadata"."id" = #{record.id} AND "_test_ci_jobs_metadata"."partition_id" = 10/
+ end
+
+ describe '#save' do
+ it 'uses id and partition_id' do
+ record.name = 'test'
+ recorder = ActiveRecord::QueryRecorder.new { record.save! }
+
+ expect(recorder.log).to include(where_filter)
+ expect(record.name).to eq('test')
+ end
+ end
+
+ describe '#update' do
+ it 'uses id and partition_id' do
+ recorder = ActiveRecord::QueryRecorder.new { record.update!(name: 'test') }
+
+ expect(recorder.log).to include(where_filter)
+ expect(record.name).to eq('test')
+ end
+ end
+
+ describe '#delete' do
+ it 'uses id and partition_id' do
+ recorder = ActiveRecord::QueryRecorder.new { record.delete }
+
+ expect(recorder.log).to include(where_filter)
+ expect(model.count).to be_zero
+ end
+ end
+
+ describe '#destroy' do
+ it 'uses id and partition_id' do
+ recorder = ActiveRecord::QueryRecorder.new { record.destroy! }
+
+ expect(recorder.log).to include(where_filter)
+ expect(model.count).to be_zero
+ end
+ end
+
+ def create_tables(table_sql)
+ Ci::ApplicationRecord.connection.execute(table_sql)
+ end
+end
diff --git a/spec/models/concerns/ci/partitionable_spec.rb b/spec/models/concerns/ci/partitionable_spec.rb
index f3d33c971c7..430ef57d493 100644
--- a/spec/models/concerns/ci/partitionable_spec.rb
+++ b/spec/models/concerns/ci/partitionable_spec.rb
@@ -40,4 +40,28 @@ RSpec.describe Ci::Partitionable do
it { expect(ci_model.ancestors).to include(described_class::Switch) }
end
+
+ context 'with partitioned options' do
+ before do
+ stub_const("#{described_class}::Testing::PARTITIONABLE_MODELS", [ci_model.name])
+
+ ci_model.include(described_class)
+ ci_model.partitionable scope: ->(r) { 1 }, partitioned: partitioned
+ end
+
+ context 'when partitioned is true' do
+ let(:partitioned) { true }
+
+ it { expect(ci_model.ancestors).to include(described_class::PartitionedFilter) }
+ it { expect(ci_model).to be_partitioned }
+ end
+
+ context 'when partitioned is false' do
+ let(:partitioned) { false }
+
+ it { expect(ci_model.ancestors).not_to include(described_class::PartitionedFilter) }
+
+ it { expect(ci_model).not_to be_partitioned }
+ end
+ end
end
diff --git a/spec/models/concerns/commit_signature_spec.rb b/spec/models/concerns/commit_signature_spec.rb
new file mode 100644
index 00000000000..4bba5a6ee41
--- /dev/null
+++ b/spec/models/concerns/commit_signature_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe CommitSignature do
+ describe '#signed_by_user' do
+ context 'when class does not define the signed_by_user method' do
+ subject(:implementation) do
+ Class.new(ActiveRecord::Base) do
+ self.table_name = 'ssh_signatures'
+ end.include(described_class).new
+ end
+
+ it 'raises a NoMethodError with custom message' do
+ expect do
+ implementation.signed_by_user
+ end.to raise_error(NoMethodError, 'must implement `signed_by_user` method')
+ end
+ end
+ end
+end
diff --git a/spec/models/concerns/counter_attribute_spec.rb b/spec/models/concerns/counter_attribute_spec.rb
index 66ccd4559e5..1dd9b78d642 100644
--- a/spec/models/concerns/counter_attribute_spec.rb
+++ b/spec/models/concerns/counter_attribute_spec.rb
@@ -8,85 +8,70 @@ RSpec.describe CounterAttribute, :counter_attribute, :clean_gitlab_redis_shared_
let(:project_statistics) { create(:project_statistics) }
let(:model) { CounterAttributeModel.find(project_statistics.id) }
- it_behaves_like CounterAttribute, [:build_artifacts_size, :commit_count] do
+ it_behaves_like CounterAttribute, [:build_artifacts_size, :commit_count, :packages_size] do
let(:model) { CounterAttributeModel.find(project_statistics.id) }
end
- describe 'after_flush callbacks' do
- let(:attribute) { model.class.counter_attributes.first }
-
- subject { model.flush_increments_to_database!(attribute) }
+ describe '#counter_attribute_enabled?' do
+ it 'is true when counter attribute is defined' do
+ expect(project_statistics.counter_attribute_enabled?(:build_artifacts_size))
+ .to be_truthy
+ end
- it 'has registered callbacks' do # defined in :counter_attribute RSpec tag
- expect(model.class.after_flush_callbacks.size).to eq(1)
+ it 'is false when counter attribute is not defined' do
+ expect(model.counter_attribute_enabled?(:nope)).to be_falsey
end
- context 'when there are increments to flush' do
- before do
- model.delayed_increment_counter(attribute, 10)
- end
+ context 'with a conditional counter attribute' do
+ [true, false].each do |enabled|
+ context "where the condition evaluates to #{enabled}" do
+ subject { model.counter_attribute_enabled?(:packages_size) }
- it 'executes the callbacks' do
- subject
+ before do
+ model.allow_package_size_counter = enabled
+ end
- expect(model.flushed).to be_truthy
+ it { is_expected.to eq(enabled) }
+ end
end
end
+ end
- context 'when there are no increments to flush' do
- it 'does not execute the callbacks' do
- subject
+ describe '#counter' do
+ using RSpec::Parameterized::TableSyntax
- expect(model.flushed).to be_nil
- end
+ it 'returns the counter for the respective attribute' do
+ expect(model.counter(:build_artifacts_size).send(:attribute)).to eq(:build_artifacts_size)
+ expect(model.counter(:commit_count).send(:attribute)).to eq(:commit_count)
end
- end
- describe '.steal_increments' do
- let(:increment_key) { 'counters:Model:123:attribute' }
- let(:flushed_key) { 'counter:Model:123:attribute:flushed' }
-
- subject { model.send(:steal_increments, increment_key, flushed_key) }
-
- where(:increment, :flushed, :result, :flushed_key_present) do
- nil | nil | 0 | false
- nil | 0 | 0 | false
- 0 | 0 | 0 | false
- 1 | 0 | 1 | true
- 1 | nil | 1 | true
- 1 | 1 | 2 | true
- 1 | -2 | -1 | true
- -1 | 1 | 0 | false
+ it 'returns the appropriate counter for the attribute' do
+ expect(model.counter(:build_artifacts_size).class).to eq(Gitlab::Counters::BufferedCounter)
+ expect(model.counter(:packages_size).class).to eq(Gitlab::Counters::BufferedCounter)
+ expect(model.counter(:wiki_size).class).to eq(Gitlab::Counters::LegacyCounter)
end
- with_them do
- before do
- Gitlab::Redis::SharedState.with do |redis|
- redis.set(increment_key, increment) if increment
- redis.set(flushed_key, flushed) if flushed
- end
+ context 'with a conditional counter attribute' do
+ where(:enabled, :expected_counter_class) do
+ [
+ [true, Gitlab::Counters::BufferedCounter],
+ [false, Gitlab::Counters::LegacyCounter]
+ ]
end
- it { is_expected.to eq(result) }
-
- it 'drops the increment key and creates the flushed key if it does not exist' do
- subject
+ with_them do
+ before do
+ model.allow_package_size_counter = enabled
+ end
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.exists?(increment_key)).to eq(false)
- expect(redis.exists?(flushed_key)).to eq(flushed_key_present)
+ it 'returns the appropriate counter based on the condition' do
+ expect(model.counter(:packages_size).class).to eq(expected_counter_class)
end
end
end
- end
-
- describe '.counter_attribute_enabled?' do
- it 'is true when counter attribute is defined' do
- expect(CounterAttributeModel.counter_attribute_enabled?(:build_artifacts_size)).to be_truthy
- end
- it 'is false when counter attribute is not defined' do
- expect(CounterAttributeModel.counter_attribute_enabled?(:nope)).to be_falsey
+ it 'raises error for unknown attribute' do
+ expect { model.counter(:unknown) }.to raise_error(ArgumentError, 'attribute "unknown" does not exist')
end
end
end
diff --git a/spec/models/concerns/has_user_type_spec.rb b/spec/models/concerns/has_user_type_spec.rb
index b2ea7b22dea..462b28f99be 100644
--- a/spec/models/concerns/has_user_type_spec.rb
+++ b/spec/models/concerns/has_user_type_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe User do
specify 'types consistency checks', :aggregate_failures do
expect(described_class::USER_TYPES.keys)
- .to match_array(%w[human ghost alert_bot project_bot support_bot service_user security_bot visual_review_bot migration_bot automation_bot])
+ .to match_array(%w[human ghost alert_bot project_bot support_bot service_user security_bot visual_review_bot migration_bot automation_bot admin_bot])
expect(described_class::USER_TYPES).to include(*described_class::BOT_USER_TYPES)
expect(described_class::USER_TYPES).to include(*described_class::NON_INTERNAL_USER_TYPES)
expect(described_class::USER_TYPES).to include(*described_class::INTERNAL_USER_TYPES)
@@ -37,12 +37,6 @@ RSpec.describe User do
end
end
- describe '.bots_without_project_bot' do
- it 'includes all bots except project_bot' do
- expect(described_class.bots_without_project_bot).to match_array(bots - [project_bot])
- end
- end
-
describe '.non_internal' do
it 'includes all non_internal users' do
expect(described_class.non_internal).to match_array(non_internal)
diff --git a/spec/models/concerns/pg_full_text_searchable_spec.rb b/spec/models/concerns/pg_full_text_searchable_spec.rb
index 98b44a2eec2..87f1dc5a27b 100644
--- a/spec/models/concerns/pg_full_text_searchable_spec.rb
+++ b/spec/models/concerns/pg_full_text_searchable_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe PgFullTextSearchable do
- let(:project) { build(:project) }
+ let(:project) { build(:project, project_namespace: build(:project_namespace)) }
let(:model_class) do
Class.new(ActiveRecord::Base) do
@@ -12,6 +12,7 @@ RSpec.describe PgFullTextSearchable do
self.table_name = 'issues'
belongs_to :project
+ belongs_to :namespace
has_one :search_data, class_name: 'Issues::SearchData'
before_validation -> { self.work_item_type_id = ::WorkItems::Type.default_issue_type.id }
@@ -41,7 +42,7 @@ RSpec.describe PgFullTextSearchable do
end
describe 'after commit hook' do
- let(:model) { model_class.create!(project: project) }
+ let(:model) { model_class.create!(project: project, namespace: project.project_namespace) }
before do
model_class.pg_full_text_searchable columns: [{ name: 'title', weight: 'A' }]
@@ -76,9 +77,9 @@ RSpec.describe PgFullTextSearchable do
end
describe '.pg_full_text_search' do
- let(:english) { model_class.create!(project: project, title: 'title', description: 'something description english') }
- let(:with_accent) { model_class.create!(project: project, title: 'Jürgen', description: 'Ærøskøbing') }
- let(:japanese) { model_class.create!(project: project, title: '日本語 title', description: 'another english description') }
+ let(:english) { model_class.create!(project: project, namespace: project.project_namespace, title: 'title', description: 'something description english') }
+ let(:with_accent) { model_class.create!(project: project, namespace: project.project_namespace, title: 'Jürgen', description: 'Ærøskøbing') }
+ let(:japanese) { model_class.create!(project: project, namespace: project.project_namespace, title: '日本語 title', description: 'another english description') }
before do
model_class.pg_full_text_searchable columns: [{ name: 'title', weight: 'A' }, { name: 'description', weight: 'B' }]
@@ -91,7 +92,7 @@ RSpec.describe PgFullTextSearchable do
end
it 'searches specified columns only' do
- matching_object = model_class.create!(project: project, title: 'english', description: 'some description')
+ matching_object = model_class.create!(project: project, namespace: project.project_namespace, title: 'english', description: 'some description')
matching_object.update_search_data!
expect(model_class.pg_full_text_search('english', matched_columns: %w(title))).to contain_exactly(matching_object)
@@ -115,7 +116,7 @@ RSpec.describe PgFullTextSearchable do
end
context 'when search term has a URL' do
- let(:with_url) { model_class.create!(project: project, title: 'issue with url', description: 'sample url,https://gitlab.com/gitlab-org/gitlab') }
+ let(:with_url) { model_class.create!(project: project, namespace: project.project_namespace, title: 'issue with url', description: 'sample url,https://gitlab.com/gitlab-org/gitlab') }
it 'allows searching by full URL, ignoring the scheme' do
with_url.update_search_data!
@@ -127,7 +128,7 @@ RSpec.describe PgFullTextSearchable do
context 'when search term is a path with underscores' do
let(:path) { 'browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb' }
- let(:with_underscore) { model_class.create!(project: project, title: 'issue with path', description: "some #{path} other text") }
+ let(:with_underscore) { model_class.create!(project: project, namespace: project.project_namespace, title: 'issue with path', description: "some #{path} other text") }
it 'allows searching by the path' do
with_underscore.update_search_data!
@@ -137,7 +138,7 @@ RSpec.describe PgFullTextSearchable do
end
context 'when text has numbers preceded by a dash' do
- let(:with_dash) { model_class.create!(project: project, title: 'issue with dash', description: 'ABC-123') }
+ let(:with_dash) { model_class.create!(project: project, namespace: project.project_namespace, title: 'issue with dash', description: 'ABC-123') }
it 'allows searching by numbers only' do
with_dash.update_search_data!
@@ -148,7 +149,7 @@ RSpec.describe PgFullTextSearchable do
end
describe '#update_search_data!' do
- let(:model) { model_class.create!(project: project, title: 'title', description: 'description') }
+ let(:model) { model_class.create!(project: project, namespace: project.project_namespace, title: 'title', description: 'description') }
before do
model_class.pg_full_text_searchable columns: [{ name: 'title', weight: 'A' }, { name: 'description', weight: 'B' }]
@@ -162,7 +163,7 @@ RSpec.describe PgFullTextSearchable do
end
context 'with accented and non-Latin characters' do
- let(:model) { model_class.create!(project: project, title: '日本語', description: 'Jürgen') }
+ let(:model) { model_class.create!(project: project, namespace: project.project_namespace, title: '日本語', description: 'Jürgen') }
it 'transliterates accented characters and removes non-Latin ones' do
model.update_search_data!
@@ -173,7 +174,7 @@ RSpec.describe PgFullTextSearchable do
end
context 'with long words' do
- let(:model) { model_class.create!(project: project, title: 'title ' + 'long/sequence+1' * 4, description: 'description ' + '@user1' * 20) }
+ let(:model) { model_class.create!(project: project, namespace: project.project_namespace, title: 'title ' + 'long/sequence+1' * 4, description: 'description ' + '@user1' * 20) }
it 'strips words that are 50 characters or longer' do
model.update_search_data!
@@ -197,7 +198,7 @@ RSpec.describe PgFullTextSearchable do
context 'with strings that go over tsvector limit', :delete do
let(:long_string) { Array.new(30_000) { SecureRandom.hex }.join(' ') }
- let(:model) { model_class.create!(project: project, title: 'title', description: long_string) }
+ let(:model) { model_class.create!(project: project, namespace: project.project_namespace, title: 'title', description: long_string) }
it 'does not raise an exception' do
expect(Gitlab::AppJsonLogger).to receive(:error).with(
@@ -218,6 +219,7 @@ RSpec.describe PgFullTextSearchable do
self.table_name = 'issues'
belongs_to :project
+ belongs_to :namespace
has_one :search_data, class_name: 'Issues::SearchData'
before_validation -> { self.work_item_type_id = ::WorkItems::Type.default_issue_type.id }
diff --git a/spec/models/concerns/schedulable_spec.rb b/spec/models/concerns/schedulable_spec.rb
index b98dcf1c174..b8084a70046 100644
--- a/spec/models/concerns/schedulable_spec.rb
+++ b/spec/models/concerns/schedulable_spec.rb
@@ -77,7 +77,7 @@ RSpec.describe Schedulable do
end.new
end
- it 'works' do
+ it 'raises a NotImplementedError' do
expect { schedulable_instance.set_next_run_at }.to raise_error(NotImplementedError)
end
end
diff --git a/spec/models/concerns/sensitive_serializable_hash_spec.rb b/spec/models/concerns/sensitive_serializable_hash_spec.rb
index 3c9199ce18f..591a4383a03 100644
--- a/spec/models/concerns/sensitive_serializable_hash_spec.rb
+++ b/spec/models/concerns/sensitive_serializable_hash_spec.rb
@@ -35,12 +35,6 @@ RSpec.describe SensitiveSerializableHash do
expect(model.serializable_hash).not_to include('super_secret')
end
- context 'unsafe_serialization_hash option' do
- it 'includes the field in serializable_hash' do
- expect(model.serializable_hash(unsafe_serialization_hash: true)).to include('super_secret')
- end
- end
-
it 'does not change parent class attributes_exempt_from_serializable_hash' do
expect(test_class.attributes_exempt_from_serializable_hash).to contain_exactly(:super_secret)
expect(another_class.attributes_exempt_from_serializable_hash).to contain_exactly(:sub_secret)
@@ -65,21 +59,6 @@ RSpec.describe SensitiveSerializableHash do
expect(model.as_json).not_to include(attribute)
end
end
-
- context 'unsafe_serialization_hash option' do
- it 'includes the field in serializable_hash' do
- attributes.each do |attribute|
- expect(model.attributes).to include(attribute) # double-check the attribute does exist
-
- # Do not expect binary columns to appear in JSON
- next if klass.columns_hash[attribute]&.type == :binary
-
- expect(model.serializable_hash(unsafe_serialization_hash: true)).to include(attribute)
- expect(model.to_json(unsafe_serialization_hash: true)).to include(attribute)
- expect(model.as_json(unsafe_serialization_hash: true)).to include(attribute)
- end
- end
- end
end
end
@@ -120,18 +99,6 @@ RSpec.describe SensitiveSerializableHash do
expect(model.as_json).not_to include(attribute)
end
end
-
- context 'unsafe_serialization_hash option' do
- it 'includes the field in serializable_hash' do
- attributes.each do |attribute|
- expect(model.attributes).to include(attribute) # double-check the attribute does exist
-
- expect(model.serializable_hash(unsafe_serialization_hash: true)).to include(attribute)
- expect(model.to_json(unsafe_serialization_hash: true)).to include(attribute)
- expect(model.as_json(unsafe_serialization_hash: true)).to include(attribute)
- end
- end
- end
end
end
diff --git a/spec/models/concerns/signature_type_spec.rb b/spec/models/concerns/signature_type_spec.rb
new file mode 100644
index 00000000000..d8e2b617e0e
--- /dev/null
+++ b/spec/models/concerns/signature_type_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe SignatureType do
+ describe '#type' do
+ context 'when class does not define a type method' do
+ subject(:implementation) { Class.new.include(described_class).new }
+
+ it 'raises a NoMethodError with custom message' do
+ expect { implementation.type }.to raise_error(NoMethodError, 'must implement `type` method')
+ end
+ end
+ end
+end
diff --git a/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb b/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
index e0ebb86585a..2df86804f34 100644
--- a/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
+++ b/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
@@ -237,6 +237,7 @@ RSpec.describe TokenAuthenticatableStrategies::Encrypted do
expect(subject.set_token(instance, 'my-value')).to eq 'my-value'
end
end
+
context 'when encryption is optional' do
let(:options) { { encrypted: :optional } }
diff --git a/spec/models/concerns/triggerable_hooks_spec.rb b/spec/models/concerns/triggerable_hooks_spec.rb
index 90c88c888ff..5682a189c41 100644
--- a/spec/models/concerns/triggerable_hooks_spec.rb
+++ b/spec/models/concerns/triggerable_hooks_spec.rb
@@ -4,8 +4,10 @@ require 'spec_helper'
RSpec.describe TriggerableHooks do
before do
- class TestableHook < WebHook
- include TriggerableHooks
+ stub_const('TestableHook', Class.new(WebHook))
+
+ TestableHook.class_eval do
+ include TriggerableHooks # rubocop:disable RSpec/DescribedClass
triggerable_hooks [:push_hooks]
end
end
diff --git a/spec/models/container_repository_spec.rb b/spec/models/container_repository_spec.rb
index 9af53bae204..33d3cabb325 100644
--- a/spec/models/container_repository_spec.rb
+++ b/spec/models/container_repository_spec.rb
@@ -879,10 +879,11 @@ RSpec.describe ContainerRepository, :aggregate_failures do
it 'updates deletion status attributes' do
expect { subject }.to change(repository, :status).from(nil).to('delete_ongoing')
.and change(repository, :delete_started_at).from(nil).to(Time.zone.now)
+ .and change(repository, :status_updated_at).from(nil).to(Time.zone.now)
end
end
- describe '#set_delete_scheduled_status' do
+ describe '#set_delete_scheduled_status', :freeze_time do
let_it_be(:repository) { create(:container_repository, :status_delete_ongoing, delete_started_at: 3.minutes.ago) }
subject { repository.set_delete_scheduled_status }
@@ -890,6 +891,27 @@ RSpec.describe ContainerRepository, :aggregate_failures do
it 'updates delete attributes' do
expect { subject }.to change(repository, :status).from('delete_ongoing').to('delete_scheduled')
.and change(repository, :delete_started_at).to(nil)
+ .and change(repository, :status_updated_at).to(Time.zone.now)
+ end
+ end
+
+ describe '#status_updated_at', :freeze_time do
+ let_it_be_with_reload(:repository) { create(:container_repository) }
+
+ %i[delete_scheduled delete_ongoing delete_failed].each do |status|
+ context "when status is updated to #{status}" do
+ it 'updates status_changed_at' do
+ expect { repository.update!(status: status) }.to change(repository, :status_updated_at).from(nil).to(Time.zone.now)
+ end
+ end
+ end
+
+ context 'when status is not changed' do
+ it 'does not update status_changed_at' do
+ repository.name = 'different-image'
+
+ expect { repository.save! }.not_to change(repository, :status_updated_at)
+ end
end
end
@@ -1632,7 +1654,7 @@ RSpec.describe ContainerRepository, :aggregate_failures do
stub_application_setting(container_registry_import_target_plan: valid_container_repository.migration_plan)
end
- it 'works' do
+ it 'returns valid container repositories' do
expect(subject).to contain_exactly(valid_container_repository, valid_container_repository2)
end
end
diff --git a/spec/models/deploy_token_spec.rb b/spec/models/deploy_token_spec.rb
index 04763accc42..1b8dd62455e 100644
--- a/spec/models/deploy_token_spec.rb
+++ b/spec/models/deploy_token_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe DeployToken do
+RSpec.describe DeployToken, feature_category: :continuous_delivery do
subject(:deploy_token) { create(:deploy_token) }
it { is_expected.to have_many :project_deploy_tokens }
@@ -82,9 +82,7 @@ RSpec.describe DeployToken do
describe '#ensure_at_least_one_scope' do
context 'with at least one scope' do
- it 'is valid' do
- is_expected.to be_valid
- end
+ it { is_expected.to be_valid }
end
context 'with no scopes' do
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index daa65f528e9..fb3883820fd 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -925,6 +925,21 @@ RSpec.describe Deployment do
end
end
+ describe '#triggered_by?' do
+ subject { deployment.triggered_by?(user) }
+
+ let(:user) { create(:user) }
+ let(:deployment) { create(:deployment, user: user) }
+
+ it { is_expected.to eq(true) }
+
+ context 'when deployment triggerer is different' do
+ let(:deployment) { create(:deployment) }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
describe '.find_successful_deployment!' do
it 'returns a successful deployment' do
deploy = create(:deployment, :success)
diff --git a/spec/models/design_management/version_spec.rb b/spec/models/design_management/version_spec.rb
index 44ecae82174..8c0d7e99ae5 100644
--- a/spec/models/design_management/version_spec.rb
+++ b/spec/models/design_management/version_spec.rb
@@ -232,7 +232,7 @@ RSpec.describe DesignManagement::Version do
context 'there is a single design' do
let_it_be(:design) { create(:design) }
- shared_examples :a_correctly_categorised_design do |kind, category|
+ shared_examples 'a correctly categorised design' do |kind, category|
let_it_be(:version) { create(:design_version, kind => [design]) }
it 'returns a hash with a single key and the single design in that bucket' do
@@ -240,9 +240,9 @@ RSpec.describe DesignManagement::Version do
end
end
- it_behaves_like :a_correctly_categorised_design, :created_designs, 'creation'
- it_behaves_like :a_correctly_categorised_design, :modified_designs, 'modification'
- it_behaves_like :a_correctly_categorised_design, :deleted_designs, 'deletion'
+ it_behaves_like 'a correctly categorised design', :created_designs, 'creation'
+ it_behaves_like 'a correctly categorised design', :modified_designs, 'modification'
+ it_behaves_like 'a correctly categorised design', :deleted_designs, 'deletion'
end
context 'there are a bunch of different designs in a variety of states' do
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index 8a3d43f58e0..2670127442e 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -64,6 +64,34 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
end
end
+ describe 'preloading deployment associations' do
+ let!(:environment) { create(:environment, project: project) }
+
+ associations = [:last_deployment, :last_visible_deployment, :upcoming_deployment]
+ associations.concat Deployment::FINISHED_STATUSES.map { |status| "last_#{status}_deployment".to_sym }
+ associations.concat Deployment::UPCOMING_STATUSES.map { |status| "last_#{status}_deployment".to_sym }
+
+ context 'raises error for legacy approach' do
+ let!(:error_pattern) { /Preloading instance dependent scopes is not supported/ }
+
+ subject { described_class.preload(association_name).find_by(id: environment) }
+
+ shared_examples 'raises error' do
+ it do
+ expect { subject }.to raise_error(error_pattern)
+ end
+ end
+
+ associations.each do |association|
+ context association.to_s do
+ let!(:association_name) { association }
+
+ include_examples "raises error"
+ end
+ end
+ end
+ end
+
describe 'validate and sanitize external url' do
let_it_be_with_refind(:environment) { create(:environment) }
@@ -184,7 +212,7 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
end
describe ".schedule_to_delete" do
- subject { described_class.for_id(deletable_environment).schedule_to_delete }
+ subject { described_class.id_in(deletable_environment).schedule_to_delete }
it "schedules the record for deletion" do
freeze_time do
@@ -282,6 +310,72 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
end
end
+ describe '.for_name_like_within_folder' do
+ subject { project.environments.for_name_like_within_folder(query, limit: limit) }
+
+ let!(:environment) { create(:environment, name: 'review/test-app', project: project) }
+ let!(:environment_a) { create(:environment, name: 'test-app', project: project) }
+ let(:query) { 'test' }
+ let(:limit) { 5 }
+
+ it 'returns a found name' do
+ is_expected.to contain_exactly(environment)
+ end
+
+ it 'does not return environment without folder' do
+ is_expected.not_to include(environment_a)
+ end
+
+ context 'when query is test-app' do
+ let(:query) { 'test-app' }
+
+ it 'returns a found name' do
+ is_expected.to include(environment)
+ end
+ end
+
+ context 'when query is test-app-a' do
+ let(:query) { 'test-app-a' }
+
+ it 'returns empty array' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when query is empty string' do
+ let(:query) { '' }
+ let!(:environment_b) { create(:environment, name: 'review/test-app-1', project: project) }
+
+ it 'returns only the foldered environments' do
+ is_expected.to contain_exactly(environment, environment_b)
+ end
+ end
+
+ context 'when query is nil' do
+ let(:query) {}
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(NoMethodError)
+ end
+ end
+
+ context 'when query is partially matched in the middle of environment name' do
+ let(:query) { 'app' }
+
+ it 'returns empty array' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when query contains a wildcard character' do
+ let(:query) { 'test%' }
+
+ it 'prevents wildcard injection' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
describe '.auto_stoppable' do
subject { described_class.auto_stoppable(limit) }
@@ -1916,4 +2010,23 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
end
end
end
+
+ describe '#deploy_freezes' do
+ let(:environment) { create(:environment, project: project, name: 'staging') }
+ let(:freeze_period) { create(:ci_freeze_period, project: project) }
+
+ subject { environment.deploy_freezes }
+
+ it 'returns the freeze periods of the associated project' do
+ expect(subject).to contain_exactly(freeze_period)
+ end
+
+ it 'caches the freeze periods' do
+ expect(Gitlab::SafeRequestStore).to receive(:fetch)
+ .at_least(:once)
+ .and_return([freeze_period])
+
+ subject
+ end
+ end
end
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index 9579c4c2d27..667f3ddff72 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -2,7 +2,9 @@
require 'spec_helper'
-RSpec.describe Event do
+RSpec.describe Event, feature_category: :users do
+ let_it_be_with_reload(:project) { create(:project) }
+
describe "Associations" do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:target) }
@@ -17,8 +19,6 @@ RSpec.describe Event do
end
describe 'Callbacks' do
- let(:project) { create(:project) }
-
describe 'after_create :reset_project_activity' do
it 'calls the reset_project_activity method' do
expect_next_instance_of(described_class) do |instance|
@@ -105,7 +105,7 @@ RSpec.describe Event do
describe 'created_at' do
it 'can find the right event' do
time = 1.day.ago
- event = create(:event, created_at: time)
+ event = create(:event, created_at: time, project: project)
false_positive = create(:event, created_at: 2.days.ago)
found = described_class.created_at(time)
@@ -116,11 +116,11 @@ RSpec.describe Event do
end
describe '.for_fingerprint' do
- let_it_be(:with_fingerprint) { create(:event, fingerprint: 'aaa') }
+ let_it_be(:with_fingerprint) { create(:event, fingerprint: 'aaa', project: project) }
before_all do
- create(:event)
- create(:event, fingerprint: 'bbb')
+ create(:event, project: project)
+ create(:event, fingerprint: 'bbb', project: project)
end
it 'returns none if there is no fingerprint' do
@@ -137,28 +137,43 @@ RSpec.describe Event do
.to contain_exactly(with_fingerprint)
end
end
+
+ describe '.contributions' do
+ let!(:merge_request_event) { create(:event, :created, :for_merge_request, project: project) }
+ let!(:issue_event) { create(:event, :created, :for_issue, project: project) }
+ let!(:work_item_event) { create(:event, :created, :for_work_item, project: project) }
+ let!(:design_event) { create(:design_event, project: project) }
+
+ it 'returns events for MergeRequest, Issue and WorkItem' do
+ expect(described_class.contributions).to contain_exactly(
+ merge_request_event,
+ issue_event,
+ work_item_event
+ )
+ end
+ end
end
describe '#fingerprint' do
it 'is unique scoped to target' do
- issue = create(:issue)
- mr = create(:merge_request)
+ issue = create(:issue, project: project)
+ mr = create(:merge_request, source_project: project)
- expect { create_list(:event, 2, target: issue, fingerprint: '1234') }
+ expect { create_list(:event, 2, target: issue, fingerprint: '1234', project: project) }
.to raise_error(include('fingerprint'))
expect do
- create(:event, target: mr, fingerprint: 'abcd')
- create(:event, target: issue, fingerprint: 'abcd')
- create(:event, target: issue, fingerprint: 'efgh')
+ create(:event, target: mr, fingerprint: 'abcd', project: project)
+ create(:event, target: issue, fingerprint: 'abcd', project: project)
+ create(:event, target: issue, fingerprint: 'efgh', project: project)
end.not_to raise_error
end
end
describe "Push event" do
- let(:project) { create(:project, :private) }
- let(:user) { project.first_owner }
- let(:event) { create_push_event(project, user) }
+ let(:private_project) { create(:project, :private) }
+ let(:user) { private_project.first_owner }
+ let(:event) { create_push_event(private_project, user) }
it do
expect(event.push_action?).to be_truthy
@@ -171,8 +186,6 @@ RSpec.describe Event do
end
describe '#target_title' do
- let_it_be(:project) { create(:project) }
-
let(:author) { project.first_owner }
let(:target) { nil }
@@ -303,11 +316,11 @@ RSpec.describe Event do
end
def visible_to_none_except(*roles)
- visible_to_none.merge(roles.to_h { |role| [role, true] })
+ visible_to_none.merge(roles.index_with { true })
end
def visible_to_all_except(*roles)
- visible_to_all.merge(roles.to_h { |role| [role, false] })
+ visible_to_all.merge(roles.index_with { false })
end
shared_examples 'visibility examples' do
diff --git a/spec/models/factories_spec.rb b/spec/models/factories_spec.rb
index 65b993cca7f..4915c0bd870 100644
--- a/spec/models/factories_spec.rb
+++ b/spec/models/factories_spec.rb
@@ -44,6 +44,8 @@ RSpec.describe 'factories', :saas do
[:ci_pipeline_artifact, :remote_store],
# EE
[:dast_profile, :with_dast_site_validation],
+ [:dependency_proxy_manifest, :remote_store],
+ [:geo_dependency_proxy_manifest_state, any],
[:ee_ci_build, :dependency_scanning_report],
[:ee_ci_build, :license_scan_v1],
[:ee_ci_job_artifact, :v1],
diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb
index 9d70019734b..fe22b20ecf9 100644
--- a/spec/models/generic_commit_status_spec.rb
+++ b/spec/models/generic_commit_status_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe GenericCommitStatus do
end
describe '#name_uniqueness_across_types' do
- let(:attributes) { {} }
+ let(:attributes) { { context: 'default' } }
let(:commit_status) { described_class.new(attributes) }
let(:status_name) { 'test-job' }
@@ -39,7 +39,7 @@ RSpec.describe GenericCommitStatus do
end
context 'with only a pipeline' do
- let(:attributes) { { pipeline: pipeline } }
+ let(:attributes) { { pipeline: pipeline, context: 'default' } }
context 'without name' do
it_behaves_like 'it does not have uniqueness errors'
@@ -129,32 +129,6 @@ RSpec.describe GenericCommitStatus do
end
end
- describe 'set_default_values' do
- before do
- generic_commit_status.context = nil
- generic_commit_status.stage = nil
- generic_commit_status.save!
- end
-
- describe '#context' do
- subject { generic_commit_status.context }
-
- it { is_expected.not_to be_nil }
- end
-
- describe '#stage' do
- subject { generic_commit_status.stage }
-
- it { is_expected.not_to be_nil }
- end
-
- describe '#stage_idx' do
- subject { generic_commit_status.stage_idx }
-
- it { is_expected.not_to be_nil }
- end
- end
-
describe '#present' do
subject { generic_commit_status.present }
diff --git a/spec/models/group_deploy_key_spec.rb b/spec/models/group_deploy_key_spec.rb
index dfb4fee593f..3480e72c226 100644
--- a/spec/models/group_deploy_key_spec.rb
+++ b/spec/models/group_deploy_key_spec.rb
@@ -3,13 +3,13 @@
require 'spec_helper'
RSpec.describe GroupDeployKey do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:group_deploy_key) { create(:group_deploy_key) }
+
it { is_expected.to validate_presence_of(:user) }
it { is_expected.to belong_to(:user) }
it { is_expected.to have_many(:groups) }
- let_it_be(:group_deploy_key) { create(:group_deploy_key) }
- let_it_be(:group) { create(:group) }
-
it 'is of type DeployKey' do
expect(build(:group_deploy_key).type).to eq('DeployKey')
end
@@ -30,6 +30,12 @@ RSpec.describe GroupDeployKey do
end
end
+ describe '.defined_enums' do
+ it 'excludes the inherited enum' do
+ expect(described_class.defined_enums).to eq({})
+ end
+ end
+
describe '#can_be_edited_for' do
let_it_be(:user) { create(:user) }
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 6ba450b6d57..dfba0470d35 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -40,7 +40,7 @@ RSpec.describe Group do
it { is_expected.to have_many(:bulk_import_exports).class_name('BulkImports::Export') }
it { is_expected.to have_many(:contacts).class_name('CustomerRelations::Contact') }
it { is_expected.to have_many(:organizations).class_name('CustomerRelations::Organization') }
- it { is_expected.to have_many(:protected_branches) }
+ it { is_expected.to have_many(:protected_branches).inverse_of(:group).with_foreign_key(:namespace_id) }
it { is_expected.to have_one(:crm_settings) }
it { is_expected.to have_one(:group_feature) }
it { is_expected.to have_one(:harbor_integration) }
@@ -842,10 +842,12 @@ RSpec.describe Group do
it 'returns matching records based on paths' do
expect(described_class.by_ids_or_paths(nil, [group_path])).to match_array([group])
+ expect(described_class.by_ids_or_paths(nil, [group_path.upcase])).to match_array([group])
end
it 'returns matching records based on ids' do
expect(described_class.by_ids_or_paths([group_id], nil)).to match_array([group])
+ expect(described_class.by_ids_or_paths([group_id], [])).to match_array([group])
end
it 'returns matching records based on both paths and ids' do
@@ -853,6 +855,13 @@ RSpec.describe Group do
expect(described_class.by_ids_or_paths([new_group.id], [group_path])).to match_array([group, new_group])
end
+
+ it 'returns matching records based on full_paths' do
+ new_group = create(:group, parent: group)
+
+ expect(described_class.by_ids_or_paths(nil, [new_group.full_path])).to match_array([new_group])
+ expect(described_class.by_ids_or_paths(nil, [new_group.full_path.upcase])).to match_array([new_group])
+ end
end
describe 'accessible_to_user' do
@@ -1955,6 +1964,78 @@ RSpec.describe Group do
end
end
+ describe '#self_and_hierarchy_intersecting_with_user_groups' do
+ let_it_be(:user) { create(:user) }
+ let(:subject) { group.self_and_hierarchy_intersecting_with_user_groups(user) }
+
+ it 'makes a call to GroupsFinder' do
+ expect(GroupsFinder).to receive_message_chain(:new, :execute, :unscope)
+
+ subject
+ end
+
+ context 'when the group is private' do
+ let_it_be(:group) { create(:group, :private) }
+
+ context 'when the user is not a member of the group' do
+ it 'is an empty array' do
+ expect(subject).to eq([])
+ end
+ end
+
+ context 'when the user is a member of the group' do
+ before do
+ group.add_developer(user)
+ end
+
+ it 'is equal to the group' do
+ expect(subject).to match_array([group])
+ end
+ end
+
+ context 'when the group has a sub group' do
+ let_it_be(:subgroup) { create(:group, :private, parent: group) }
+
+ context 'when the user is not a member of the subgroup' do
+ it 'is an empty array' do
+ expect(subject).to eq([])
+ end
+ end
+
+ context 'when the user is a member of the subgroup' do
+ before do
+ subgroup.add_developer(user)
+ end
+
+ it 'is equal to the group and subgroup' do
+ expect(subject).to match_array([group, subgroup])
+ end
+
+ context 'when the group has an ancestor' do
+ let_it_be(:ancestor) { create(:group, :private) }
+
+ before do
+ group.parent = ancestor
+ group.save!
+ end
+
+ it 'is equal to the ancestor, group and subgroup' do
+ expect(subject).to match_array([ancestor, group, subgroup])
+ end
+ end
+ end
+ end
+ end
+
+ context 'when the group is public' do
+ let_it_be(:group) { create(:group, :public) }
+
+ it 'is equal to the public group regardless of membership' do
+ expect(subject).to match_array([group])
+ end
+ end
+ end
+
describe '#update_two_factor_requirement_for_members' do
let_it_be_with_reload(:user) { create(:user) }
@@ -2735,7 +2816,7 @@ RSpec.describe Group do
end
describe 'has_project_with_service_desk_enabled?' do
- let_it_be(:group) { create(:group, :private) }
+ let_it_be_with_refind(:group) { create(:group, :private) }
subject { group.has_project_with_service_desk_enabled? }
@@ -3294,6 +3375,13 @@ RSpec.describe Group do
end
end
+ describe '#work_items_mvc_feature_flag_enabled?' do
+ it_behaves_like 'checks self and root ancestor feature flag' do
+ let(:feature_flag) { :work_items_mvc }
+ let(:feature_flag_method) { :work_items_mvc_feature_flag_enabled? }
+ end
+ end
+
describe '#work_items_mvc_2_feature_flag_enabled?' do
it_behaves_like 'checks self and root ancestor feature flag' do
let(:feature_flag) { :work_items_mvc_2 }
diff --git a/spec/models/hooks/active_hook_filter_spec.rb b/spec/models/hooks/active_hook_filter_spec.rb
index 47c0fbdb106..d2a8f89041e 100644
--- a/spec/models/hooks/active_hook_filter_spec.rb
+++ b/spec/models/hooks/active_hook_filter_spec.rb
@@ -85,23 +85,5 @@ RSpec.describe ActiveHookFilter do
it { expect(filter.matches?(:push_hooks, { ref: 'refs/heads/feature1' })).to be true }
end
end
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(enhanced_webhook_support_regex: false)
- end
-
- let(:hook) do
- build(
- :project_hook,
- push_events: true,
- push_events_branch_filter: '(master)',
- branch_filter_strategy: 'regex'
- )
- end
-
- it { expect(filter.matches?(:push_hooks, { ref: 'refs/heads/master' })).to be false }
- it { expect(filter.matches?(:push_hooks, { ref: 'refs/heads/(master)' })).to be true }
- end
end
end
diff --git a/spec/models/hooks/service_hook_spec.rb b/spec/models/hooks/service_hook_spec.rb
index 68c284a913c..2ece04c7158 100644
--- a/spec/models/hooks/service_hook_spec.rb
+++ b/spec/models/hooks/service_hook_spec.rb
@@ -11,6 +11,32 @@ RSpec.describe ServiceHook do
it { is_expected.to validate_presence_of(:integration) }
end
+ describe 'executable?' do
+ let!(:hooks) do
+ [
+ [0, Time.current],
+ [0, 1.minute.from_now],
+ [1, 1.minute.from_now],
+ [3, 1.minute.from_now],
+ [4, nil],
+ [4, 1.day.ago],
+ [4, 1.minute.from_now],
+ [0, nil],
+ [0, 1.day.ago],
+ [1, nil],
+ [1, 1.day.ago],
+ [3, nil],
+ [3, 1.day.ago]
+ ].map do |(recent_failures, disabled_until)|
+ create(:service_hook, recent_failures: recent_failures, disabled_until: disabled_until)
+ end
+ end
+
+ it 'is always true' do
+ expect(hooks).to all(be_executable)
+ end
+ end
+
describe 'execute' do
let(:hook) { build(:service_hook) }
let(:data) { { key: 'value' } }
diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb
index 9b55db15f3b..994d5688808 100644
--- a/spec/models/hooks/web_hook_spec.rb
+++ b/spec/models/hooks/web_hook_spec.rb
@@ -31,7 +31,7 @@ RSpec.describe WebHook do
it { is_expected.to allow_value({ 'MY_TOKEN' => 'bar' }).for(:url_variables) }
it { is_expected.to allow_value({ 'foo2' => 'bar' }).for(:url_variables) }
it { is_expected.to allow_value({ 'x' => 'y' }).for(:url_variables) }
- it { is_expected.to allow_value({ 'x' => ('a' * 100) }).for(:url_variables) }
+ it { is_expected.to allow_value({ 'x' => ('a' * 2048) }).for(:url_variables) }
it { is_expected.to allow_value({ 'foo' => 'bar', 'bar' => 'baz' }).for(:url_variables) }
it { is_expected.to allow_value((1..20).to_h { ["k#{_1}", 'value'] }).for(:url_variables) }
it { is_expected.to allow_value({ 'MY-TOKEN' => 'bar' }).for(:url_variables) }
@@ -45,7 +45,7 @@ RSpec.describe WebHook do
it { is_expected.not_to allow_value({ 'bar' => :baz }).for(:url_variables) }
it { is_expected.not_to allow_value({ 'bar' => nil }).for(:url_variables) }
it { is_expected.not_to allow_value({ 'foo' => '' }).for(:url_variables) }
- it { is_expected.not_to allow_value({ 'foo' => ('a' * 101) }).for(:url_variables) }
+ it { is_expected.not_to allow_value({ 'foo' => ('a' * 2049) }).for(:url_variables) }
it { is_expected.not_to allow_value({ 'has spaces' => 'foo' }).for(:url_variables) }
it { is_expected.not_to allow_value({ '' => 'foo' }).for(:url_variables) }
it { is_expected.not_to allow_value({ '1foo' => 'foo' }).for(:url_variables) }
@@ -237,16 +237,6 @@ RSpec.describe WebHook do
it { is_expected.to contain_exactly(:token, :url, :url_variables) }
end
- describe '.web_hooks_disable_failed?' do
- it 'returns true when feature is enabled for parent' do
- second_hook = build(:project_hook)
- stub_feature_flags(web_hooks_disable_failed: [false, second_hook.project])
-
- expect(described_class.web_hooks_disable_failed?(hook)).to eq(false)
- expect(described_class.web_hooks_disable_failed?(second_hook)).to eq(true)
- end
- end
-
describe 'execute' do
let(:data) { { key: 'value' } }
let(:hook_name) { 'project hook' }
@@ -327,16 +317,6 @@ RSpec.describe WebHook do
expect(described_class.where(project_id: project.id).executable).to match_array executables
expect(described_class.where(project_id: project.id).disabled).to match_array not_executable
end
-
- context 'when the feature flag is not enabled' do
- before do
- stub_feature_flags(web_hooks_disable_failed: false)
- end
-
- specify 'enabled is the same as all' do
- expect(described_class.where(project_id: project.id).executable).to match_array(executables + not_executable)
- end
- end
end
describe '#executable?' do
@@ -384,26 +364,6 @@ RSpec.describe WebHook do
it 'has the correct state' do
expect(web_hook.executable?).to eq(executable)
end
-
- context 'when the feature flag is enabled for a project' do
- before do
- stub_feature_flags(web_hooks_disable_failed: project)
- end
-
- it 'has the expected value' do
- expect(web_hook.executable?).to eq(executable)
- end
- end
-
- context 'when the feature flag is not enabled' do
- before do
- stub_feature_flags(web_hooks_disable_failed: false)
- end
-
- it 'is executable' do
- expect(web_hook).to be_executable
- end
- end
end
end
@@ -643,12 +603,6 @@ RSpec.describe WebHook do
it 'is true' do
expect(hook).to be_temporarily_disabled
end
-
- it 'is false when `web_hooks_disable_failed` flag is disabled' do
- stub_feature_flags(web_hooks_disable_failed: false)
-
- expect(hook).not_to be_temporarily_disabled
- end
end
end
@@ -665,12 +619,6 @@ RSpec.describe WebHook do
it 'is true' do
expect(hook).to be_permanently_disabled
end
-
- it 'is false when `web_hooks_disable_failed` flag is disabled' do
- stub_feature_flags(web_hooks_disable_failed: false)
-
- expect(hook).not_to be_permanently_disabled
- end
end
end
@@ -730,17 +678,9 @@ RSpec.describe WebHook do
expect { hook.to_json }.not_to raise_error
end
- it 'does not error, when serializing unsafe attributes' do
- expect { hook.to_json(unsafe_serialization_hash: true) }.not_to raise_error
- end
-
it 'does not contain binary attributes' do
expect(hook.to_json).not_to include('encrypted_url_variables')
end
-
- it 'does not contain binary attributes, even when serializing unsafe attributes' do
- expect(hook.to_json(unsafe_serialization_hash: true)).not_to include('encrypted_url_variables')
- end
end
describe '#interpolated_url' do
diff --git a/spec/models/integration_spec.rb b/spec/models/integration_spec.rb
index 4938e1797af..78b30221a24 100644
--- a/spec/models/integration_spec.rb
+++ b/spec/models/integration_spec.rb
@@ -1303,8 +1303,8 @@ RSpec.describe Integration do
describe '#async_execute' do
let(:integration) { described_class.new(id: 123) }
- let(:data) { { object_kind: 'push' } }
- let(:supported_events) { %w[push] }
+ let(:data) { { object_kind: 'build' } }
+ let(:supported_events) { %w[push build] }
subject(:async_execute) { integration.async_execute(data) }
diff --git a/spec/models/integrations/base_chat_notification_spec.rb b/spec/models/integrations/base_chat_notification_spec.rb
index b959ead2cae..67fc09fd8b5 100644
--- a/spec/models/integrations/base_chat_notification_spec.rb
+++ b/spec/models/integrations/base_chat_notification_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe Integrations::BaseChatNotification do
webhook: webhook_url
)
- WebMock.stub_request(:post, webhook_url)
+ WebMock.stub_request(:post, webhook_url) if webhook_url.present?
subject.active = true
end
@@ -55,6 +55,33 @@ RSpec.describe Integrations::BaseChatNotification do
end
end
+ context 'when webhook is blank' do
+ let(:webhook_url) { '' }
+
+ it 'returns false' do
+ expect(chat_integration).not_to receive(:notify)
+ expect(chat_integration.execute(data)).to be false
+ end
+
+ context 'when webhook is not required' do
+ it 'returns true' do
+ allow(chat_integration).to receive(:requires_webhook?).and_return(false)
+
+ expect(chat_integration).to receive(:notify).and_return(true)
+ expect(chat_integration.execute(data)).to be true
+ end
+ end
+ end
+
+ context 'when event is not supported' do
+ it 'returns false' do
+ allow(chat_integration).to receive(:supported_events).and_return(['foo'])
+
+ expect(chat_integration).not_to receive(:notify)
+ expect(chat_integration.execute(data)).to be false
+ end
+ end
+
context 'with a project with name containing spaces' do
it 'does not remove spaces' do
allow(project).to receive(:full_name).and_return('Project Name')
diff --git a/spec/models/integrations/base_slack_notification_spec.rb b/spec/models/integrations/base_slack_notification_spec.rb
new file mode 100644
index 00000000000..8f7f4e8858d
--- /dev/null
+++ b/spec/models/integrations/base_slack_notification_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::BaseSlackNotification do
+ # This spec should only contain tests that cannot be tested through
+ # `base_slack_notification_shared_examples.rb`.
+
+ describe '#metrics_key_prefix (private method)' do
+ it 'raises a NotImplementedError error when not defined' do
+ subclass = Class.new(described_class)
+
+ expect { subclass.new.send(:metrics_key_prefix) }.to raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/models/integrations/chat_message/pipeline_message_spec.rb b/spec/models/integrations/chat_message/pipeline_message_spec.rb
index f3388853b37..413cb097327 100644
--- a/spec/models/integrations/chat_message/pipeline_message_spec.rb
+++ b/spec/models/integrations/chat_message/pipeline_message_spec.rb
@@ -40,8 +40,6 @@ RSpec.describe Integrations::ChatMessage::PipelineMessage do
let(:has_yaml_errors) { false }
- it_behaves_like Integrations::ChatMessage
-
before do
test_commit = double("A test commit", committer: args[:user], title: "A test commit message")
test_project = build(:project, name: args[:project][:name])
@@ -62,6 +60,8 @@ RSpec.describe Integrations::ChatMessage::PipelineMessage do
allow(Gitlab::UrlBuilder).to receive(:build).with(args[:user]).and_return("http://example.gitlab.com/hacker")
end
+ it_behaves_like Integrations::ChatMessage
+
it 'returns an empty pretext' do
expect(subject.pretext).to be_empty
end
diff --git a/spec/models/integrations/drone_ci_spec.rb b/spec/models/integrations/drone_ci_spec.rb
index 6ff6888e0d3..c46face9702 100644
--- a/spec/models/integrations/drone_ci_spec.rb
+++ b/spec/models/integrations/drone_ci_spec.rb
@@ -37,7 +37,7 @@ RSpec.describe Integrations::DroneCi, :use_clean_rails_memory_store_caching do
end
end
- shared_context :drone_ci_integration do
+ shared_context 'drone ci integration' do
subject(:drone) do
described_class.new(
project: project,
@@ -116,7 +116,7 @@ RSpec.describe Integrations::DroneCi, :use_clean_rails_memory_store_caching do
end
it_behaves_like Integrations::HasWebHook do
- include_context :drone_ci_integration
+ include_context 'drone ci integration'
let(:integration) { drone }
let(:hook_url) { "#{drone_url}/hook?owner=#{project.namespace.full_path}&name=#{project.path}&access_token={token}" }
@@ -130,14 +130,14 @@ RSpec.describe Integrations::DroneCi, :use_clean_rails_memory_store_caching do
end
describe "integration page/path methods" do
- include_context :drone_ci_integration
+ include_context 'drone ci integration'
it { expect(drone.build_page(sha, branch)).to eq(build_page) }
it { expect(drone.commit_status_path(sha, branch)).to eq(commit_status_path) }
end
describe '#commit_status' do
- include_context :drone_ci_integration
+ include_context 'drone ci integration'
it 'returns the contents of the reactive cache' do
stub_reactive_cache(drone, { commit_status: 'foo' }, 'sha', 'ref')
@@ -147,7 +147,7 @@ RSpec.describe Integrations::DroneCi, :use_clean_rails_memory_store_caching do
end
describe '#calculate_reactive_cache' do
- include_context :drone_ci_integration
+ include_context 'drone ci integration'
describe '#commit_status' do
subject { drone.calculate_reactive_cache(sha, branch)[:commit_status] }
@@ -193,7 +193,7 @@ RSpec.describe Integrations::DroneCi, :use_clean_rails_memory_store_caching do
end
describe "execute" do
- include_context :drone_ci_integration
+ include_context 'drone ci integration'
let(:user) { build(:user, username: 'username') }
let(:push_sample_data) do
diff --git a/spec/models/integrations/flowdock_spec.rb b/spec/models/integrations/flowdock_spec.rb
deleted file mode 100644
index daafb1b3958..00000000000
--- a/spec/models/integrations/flowdock_spec.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Integrations::Flowdock do
- describe 'Validations' do
- context 'when integration is active' do
- before do
- subject.active = true
- end
-
- it { is_expected.to validate_presence_of(:token) }
- end
-
- context 'when integration is inactive' do
- before do
- subject.active = false
- end
-
- it { is_expected.not_to validate_presence_of(:token) }
- end
- end
-
- describe "Execute" do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
- let(:sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
- let(:api_url) { 'https://api.flowdock.com/v1/messages' }
-
- subject(:flowdock_integration) { described_class.new }
-
- before do
- allow(flowdock_integration).to receive_messages(
- project_id: project.id,
- project: project,
- token: 'verySecret'
- )
- WebMock.stub_request(:post, api_url)
- end
-
- it "calls FlowDock API" do
- flowdock_integration.execute(sample_data)
-
- sample_data[:commits].each do |commit|
- # One request to Flowdock per new commit
- next if commit[:id] == sample_data[:before]
-
- expect(WebMock).to have_requested(:post, api_url).with(
- body: /#{commit[:id]}.*#{project.path}/
- ).once
- end
- end
- end
-end
diff --git a/spec/models/integrations/jira_spec.rb b/spec/models/integrations/jira_spec.rb
index af1112cf50d..a4ccae459cf 100644
--- a/spec/models/integrations/jira_spec.rb
+++ b/spec/models/integrations/jira_spec.rb
@@ -469,7 +469,8 @@ RSpec.describe Integrations::Jira do
end
describe '#client' do
- subject do
+ it 'uses the default GitLab::HTTP timeouts' do
+ timeouts = Gitlab::HTTP::DEFAULT_TIMEOUT_OPTIONS
stub_request(:get, 'http://jira.example.com/foo')
expect(Gitlab::HTTP).to receive(:httparty_perform_request)
@@ -477,32 +478,6 @@ RSpec.describe Integrations::Jira do
jira_integration.client.get('/foo')
end
-
- context 'when the FF :jira_raise_timeouts is enabled' do
- let(:timeouts) do
- {
- open_timeout: 2.minutes,
- read_timeout: 2.minutes,
- write_timeout: 2.minutes
- }
- end
-
- it 'uses custom timeouts' do
- subject
- end
- end
-
- context 'when the FF :jira_raise_timeouts is disabled' do
- before do
- stub_feature_flags(jira_raise_timeouts: false)
- end
-
- let(:timeouts) { Gitlab::HTTP::DEFAULT_TIMEOUT_OPTIONS }
-
- it 'uses the default GitLab::HTTP timeouts' do
- subject
- end
- end
end
describe '#find_issue' do
@@ -612,7 +587,7 @@ RSpec.describe Integrations::Jira do
close_issue
end
- it_behaves_like 'Snowplow event tracking' do
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
subject { close_issue }
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
@@ -968,7 +943,7 @@ RSpec.describe Integrations::Jira do
subject
end
- it_behaves_like 'Snowplow event tracking' do
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:category) { 'Integrations::Jira' }
let(:action) { 'perform_integrations_action' }
diff --git a/spec/models/integrations/slack_spec.rb b/spec/models/integrations/slack_spec.rb
index a12bc7f4831..218d92ffe05 100644
--- a/spec/models/integrations/slack_spec.rb
+++ b/spec/models/integrations/slack_spec.rb
@@ -4,5 +4,9 @@ require 'spec_helper'
RSpec.describe Integrations::Slack do
it_behaves_like Integrations::SlackMattermostNotifier, 'Slack'
- it_behaves_like Integrations::BaseSlackNotification, factory: :integrations_slack
+ it_behaves_like Integrations::BaseSlackNotification, factory: :integrations_slack do
+ before do
+ stub_request(:post, integration.webhook)
+ end
+ end
end
diff --git a/spec/models/issue_collection_spec.rb b/spec/models/issue_collection_spec.rb
deleted file mode 100644
index 183082bab26..00000000000
--- a/spec/models/issue_collection_spec.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe IssueCollection do
- let(:user) { create(:user) }
- let(:project) { create(:project) }
- let(:issue1) { create(:issue, project: project) }
- let(:issue2) { create(:issue, project: project) }
- let(:collection) { described_class.new([issue1, issue2]) }
-
- describe '#collection' do
- it 'returns the issues in the same order as the input Array' do
- expect(collection.collection).to eq([issue1, issue2])
- end
- end
-
- describe '#updatable_by_user' do
- context 'using an admin user' do
- it 'returns all issues' do
- user = create(:admin)
-
- expect(collection.updatable_by_user(user)).to eq([issue1, issue2])
- end
- end
-
- context 'using a user that has no access to the project' do
- it 'returns no issues when the user is not an assignee or author' do
- expect(collection.updatable_by_user(user)).to be_empty
- end
-
- it 'returns the issues the user is assigned to' do
- issue1.assignees << user
-
- expect(collection.updatable_by_user(user)).to eq([issue1])
- end
-
- it 'returns the issues for which the user is the author' do
- issue1.author = user
-
- expect(collection.updatable_by_user(user)).to eq([issue1])
- end
- end
-
- context 'using a user that has reporter access to the project' do
- it 'returns the issues of the project' do
- project.add_reporter(user)
-
- expect(collection.updatable_by_user(user)).to eq([issue1, issue2])
- end
- end
-
- # TODO update when we have multiple owners of a project
- # https://gitlab.com/gitlab-org/gitlab/-/issues/350605
- context 'using a user that is an owner of a project' do
- it 'returns the issues of the project' do
- expect(collection.updatable_by_user(project.namespace.owner))
- .to eq([issue1, issue2])
- end
- end
- end
-
- describe '#visible_to' do
- it 'is an alias for updatable_by_user' do
- updatable_by_user = described_class.instance_method(:updatable_by_user)
- visible_to = described_class.instance_method(:visible_to)
-
- expect(visible_to).to eq(updatable_by_user)
- end
- end
-end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index aea8bdaf343..7c147067714 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issue do
+RSpec.describe Issue, feature_category: :project_management do
include ExternalAuthorizationServiceHelpers
using RSpec::Parameterized::TableSyntax
@@ -25,7 +25,7 @@ RSpec.describe Issue do
it { is_expected.to have_many(:design_versions) }
it { is_expected.to have_one(:sentry_issue) }
it { is_expected.to have_one(:alert_management_alert) }
- it { is_expected.to have_many(:alert_management_alerts) }
+ it { is_expected.to have_many(:alert_management_alerts).validate(false) }
it { is_expected.to have_many(:resource_milestone_events) }
it { is_expected.to have_many(:resource_state_events) }
it { is_expected.to have_and_belong_to_many(:prometheus_alert_events) }
@@ -138,6 +138,31 @@ RSpec.describe Issue do
end
end
+ describe '#allowed_work_item_type_change' do
+ where(:old_type, :new_type, :is_valid) do
+ :issue | :incident | true
+ :incident | :issue | true
+ :test_case | :issue | true
+ :issue | :test_case | true
+ :issue | :task | false
+ :test_case | :task | false
+ :incident | :task | false
+ :task | :issue | false
+ :task | :incident | false
+ :task | :test_case | false
+ end
+
+ with_them do
+ it 'is possible to change type only between selected types' do
+ issue = create(:issue, old_type, project: reusable_project)
+
+ issue.work_item_type_id = WorkItems::Type.default_by_type(new_type).id
+
+ expect(issue.valid?).to eq(is_valid)
+ end
+ end
+ end
+
describe 'confidentiality' do
let_it_be(:project) { create(:project) }
@@ -257,7 +282,7 @@ RSpec.describe Issue do
end
context 'when no type was set' do
- let_it_be(:issue, refind: true) { build(:issue, project: project, work_item_type: nil).tap { |issue| issue.save!(validate: false) } }
+ let(:issue) { build(:issue, project: project, work_item_type: nil) }
it 'sets a work item type before validation' do
expect(issue.work_item_type_id).to be_nil
@@ -449,17 +474,6 @@ RSpec.describe Issue do
end
end
- # TODO: Remove when NOT NULL constraint is added to the relationship
- describe '#work_item_type' do
- let(:issue) { build(:issue, :incident, project: reusable_project, work_item_type: nil).tap { |issue| issue.save!(validate: false) } }
-
- it 'returns a default type if the legacy issue does not have a work item type associated yet' do
- expect(issue.work_item_type_id).to be_nil
- expect(issue.issue_type).to eq('incident')
- expect(issue.work_item_type).to eq(WorkItems::Type.default_by_type(:incident))
- end
- end
-
describe '#sort' do
let(:project) { reusable_project }
diff --git a/spec/models/jira_connect_installation_spec.rb b/spec/models/jira_connect_installation_spec.rb
index 09a4a7a488c..525690fa6b7 100644
--- a/spec/models/jira_connect_installation_spec.rb
+++ b/spec/models/jira_connect_installation_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JiraConnectInstallation do
+RSpec.describe JiraConnectInstallation, feature_category: :integrations do
describe 'associations' do
it { is_expected.to have_many(:subscriptions).class_name('JiraConnectSubscription') }
end
@@ -124,6 +124,20 @@ RSpec.describe JiraConnectInstallation do
end
end
+ describe 'audience_uninstalled_event_url' do
+ let(:installation) { build(:jira_connect_installation) }
+
+ subject(:audience) { installation.audience_uninstalled_event_url }
+
+ it { is_expected.to eq(nil) }
+
+ context 'when proxy installation' do
+ let(:installation) { build(:jira_connect_installation, instance_url: 'https://example.com') }
+
+ it { is_expected.to eq('https://example.com/-/jira_connect/events/uninstalled') }
+ end
+ end
+
describe 'proxy?' do
let(:installation) { build(:jira_connect_installation) }
diff --git a/spec/models/jira_import_state_spec.rb b/spec/models/jira_import_state_spec.rb
index 95e9594f885..c31bedd08f3 100644
--- a/spec/models/jira_import_state_spec.rb
+++ b/spec/models/jira_import_state_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JiraImportState do
+RSpec.describe JiraImportState, feature_category: :integrations do
describe "associations" do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:user) }
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index b98c0e8eae0..92f4d6d8531 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe Key, :mailer do
+ it_behaves_like 'having unique enum values'
+
describe "Associations" do
it { is_expected.to belong_to(:user) }
end
@@ -216,6 +218,20 @@ RSpec.describe Key, :mailer do
end
end
end
+
+ context 'usage type scopes' do
+ let_it_be(:auth_key) { create(:key, usage_type: :auth) }
+ let_it_be(:auth_and_signing_key) { create(:key, usage_type: :auth_and_signing) }
+ let_it_be(:signing_key) { create(:key, usage_type: :signing) }
+
+ it 'auth scope returns auth and auth_and_signing keys' do
+ expect(described_class.auth).to match_array([auth_key, auth_and_signing_key])
+ end
+
+ it 'signing scope returns signing and auth_and_signing keys' do
+ expect(described_class.signing).to match_array([signing_key, auth_and_signing_key])
+ end
+ end
end
context 'validation of uniqueness (based on fingerprint uniqueness)' do
diff --git a/spec/models/lfs_object_spec.rb b/spec/models/lfs_object_spec.rb
index c25d0451f18..e38ffd97eb9 100644
--- a/spec/models/lfs_object_spec.rb
+++ b/spec/models/lfs_object_spec.rb
@@ -92,65 +92,13 @@ RSpec.describe LfsObject do
end
end
- describe '#schedule_background_upload' do
+ describe 'storage types' do
before do
stub_lfs_setting(enabled: true)
end
subject { create(:lfs_object, :with_file) }
- context 'when object storage is disabled' do
- before do
- stub_lfs_object_storage(enabled: false)
- end
-
- it 'does not schedule the migration' do
- expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
-
- subject
- end
- end
-
- context 'when object storage is enabled' do
- context 'when background upload is enabled' do
- context 'when is licensed' do
- before do
- stub_lfs_object_storage(background_upload: true)
- end
-
- it 'schedules the model for migration' do
- expect(ObjectStorage::BackgroundMoveWorker)
- .to receive(:perform_async)
- .with('LfsObjectUploader', described_class.name, :file, kind_of(Numeric))
- .once
-
- subject
- end
-
- it 'schedules the model for migration once' do
- expect(ObjectStorage::BackgroundMoveWorker)
- .to receive(:perform_async)
- .with('LfsObjectUploader', described_class.name, :file, kind_of(Numeric))
- .once
-
- create(:lfs_object, :with_file)
- end
- end
- end
-
- context 'when background upload is disabled' do
- before do
- stub_lfs_object_storage(background_upload: false)
- end
-
- it 'schedules the model for migration' do
- expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
-
- subject
- end
- end
- end
-
describe 'file is being stored' do
subject { create(:lfs_object, :with_file) }
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index 2ecd10cccc6..0ebccf1cb65 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -175,26 +175,31 @@ RSpec.describe Member do
end
context 'member role access level' do
- let_it_be(:member) { create(:group_member, access_level: Gitlab::Access::DEVELOPER) }
+ let_it_be_with_reload(:member) { create(:group_member, access_level: Gitlab::Access::DEVELOPER) }
- context 'no member role is associated' do
+ context 'when no member role is associated' do
it 'is valid' do
expect(member).to be_valid
end
end
- context 'member role is associated' do
+ context 'when member role is associated' do
let!(:member_role) do
- create(:member_role, members: [member], base_access_level: Gitlab::Access::DEVELOPER)
+ create(
+ :member_role,
+ members: [member],
+ base_access_level: Gitlab::Access::DEVELOPER,
+ namespace: member.member_namespace
+ )
end
- context 'member role matches access level' do
+ context 'when member role matches access level' do
it 'is valid' do
expect(member).to be_valid
end
end
- context 'member role does not match access level' do
+ context 'when member role does not match access level' do
it 'is invalid' do
member_role.base_access_level = Gitlab::Access::MAINTAINER
@@ -202,13 +207,57 @@ RSpec.describe Member do
end
end
- context 'access_level cannot be changed' do
+ context 'when access_level is changed' do
it 'is invalid' do
member.access_level = Gitlab::Access::MAINTAINER
expect(member).not_to be_valid
- expect(member.errors.full_messages).to include(
- "Access level cannot be changed since member is associated with a custom role"
+ expect(member.errors[:access_level]).to include(
+ _("cannot be changed since member is associated with a custom role")
+ )
+ end
+ end
+ end
+ end
+
+ context 'member role namespace' do
+ let_it_be_with_reload(:member) { create(:group_member) }
+
+ context 'when no member role is associated' do
+ it 'is valid' do
+ expect(member).to be_valid
+ end
+ end
+
+ context 'when member role is associated' do
+ let_it_be(:member_role) do
+ create(:member_role, members: [member], namespace: member.group, base_access_level: member.access_level)
+ end
+
+ context 'when member#member_namespace is a group within hierarchy of member_role#namespace' do
+ it 'is valid' do
+ member.member_namespace = create(:group, parent: member_role.namespace)
+
+ expect(member).to be_valid
+ end
+ end
+
+ context 'when member#member_namespace is a project within hierarchy of member_role#namespace' do
+ it 'is valid' do
+ project = create(:project, group: member_role.namespace)
+ member.member_namespace = Namespace.find(project.parent_id)
+
+ expect(member).to be_valid
+ end
+ end
+
+ context 'when member#member_namespace is outside hierarchy of member_role#namespace' do
+ it 'is invalid' do
+ member.member_namespace = create(:group)
+
+ expect(member).not_to be_valid
+ expect(member.errors[:member_namespace]).to include(
+ _("must be in same hierarchy as custom role's namespace")
)
end
end
@@ -248,7 +297,7 @@ RSpec.describe Member do
accepted_invite_user = build(:user, state: :active)
@accepted_invite_member = create(:project_member, :invited, :developer, project: project)
- .tap { |u| u.accept_invite!(accepted_invite_user) }
+ .tap { |u| u.accept_invite!(accepted_invite_user) }
requested_user = create(:user).tap { |u| project.request_access(u) }
@requested_member = project.requesters.find_by(user_id: requested_user.id)
@@ -612,14 +661,14 @@ RSpec.describe Member do
subject { described_class.authorizable.to_a }
it 'includes the member who has an associated user record,'\
- 'but also having an invite_token' do
- member = create(:project_member,
- :developer,
- :invited,
- user: create(:user))
+ 'but also having an invite_token' do
+ member = create(:project_member,
+ :developer,
+ :invited,
+ user: create(:user))
- expect(subject).to include(member)
- end
+ expect(subject).to include(member)
+ end
it { is_expected.to include @owner }
it { is_expected.to include @maintainer }
@@ -750,9 +799,35 @@ RSpec.describe Member do
end
describe '#request?' do
- subject { create(:project_member, requested_at: Time.current.utc) }
+ context 'when request for project' do
+ subject { create(:project_member, requested_at: Time.current.utc) }
+
+ it 'calls notification service but not todo service' do
+ expect_next_instance_of(NotificationService) do |instance|
+ expect(instance).to receive(:new_access_request)
+ end
- it { is_expected.to be_request }
+ expect(TodoService).not_to receive(:new)
+
+ is_expected.to be_request
+ end
+ end
+
+ context 'when request for group' do
+ subject { create(:group_member, requested_at: Time.current.utc) }
+
+ it 'calls notification and todo service' do
+ expect_next_instance_of(NotificationService) do |instance|
+ expect(instance).to receive(:new_access_request)
+ end
+
+ expect_next_instance_of(TodoService) do |instance|
+ expect(instance).to receive(:create_member_access_request)
+ end
+
+ is_expected.to be_request
+ end
+ end
end
describe '#pending?' do
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index 77bc6d9753f..4ac7ce95b84 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -120,6 +120,40 @@ RSpec.describe GroupMember do
end
end
+ describe '#last_owner_of_the_group?' do
+ context 'when member is an owner' do
+ let_it_be(:group_member) { build(:group_member, :owner) }
+
+ using RSpec::Parameterized::TableSyntax
+
+ where(:member_last_owner?, :member_last_blocked_owner?, :expected) do
+ false | false | false
+ true | false | true
+ false | true | true
+ true | true | true
+ end
+
+ with_them do
+ it "returns expected" do
+ allow(group_member.group).to receive(:member_last_owner?).with(group_member).and_return(member_last_owner?)
+ allow(group_member.group).to receive(:member_last_blocked_owner?)
+ .with(group_member)
+ .and_return(member_last_blocked_owner?)
+
+ expect(group_member.last_owner_of_the_group?).to be(expected)
+ end
+ end
+ end
+
+ context 'when member is not an owner' do
+ let_it_be(:group_member) { build(:group_member, :guest) }
+
+ subject { group_member.last_owner_of_the_group? }
+
+ it { is_expected.to be(false) }
+ end
+ end
+
context 'access levels' do
context 'with parent group' do
it_behaves_like 'inherited access level as a member of entity' do
diff --git a/spec/models/members/member_role_spec.rb b/spec/models/members/member_role_spec.rb
index e2691e2e78c..f9d6757bb90 100644
--- a/spec/models/members/member_role_spec.rb
+++ b/spec/models/members/member_role_spec.rb
@@ -9,39 +9,50 @@ RSpec.describe MemberRole do
end
describe 'validation' do
- subject { described_class.new }
+ subject(:member_role) { build(:member_role) }
it { is_expected.to validate_presence_of(:namespace) }
it { is_expected.to validate_presence_of(:base_access_level) }
- context 'for namespace' do
- subject { build(:member_role) }
-
+ context 'when for namespace' do
let_it_be(:root_group) { create(:group) }
context 'when namespace is a subgroup' do
it 'is invalid' do
subgroup = create(:group, parent: root_group)
- subject.namespace = subgroup
+ member_role.namespace = subgroup
- expect(subject).to be_invalid
+ expect(member_role).to be_invalid
+ expect(member_role.errors[:namespace]).to include(
+ s_("MemberRole|must be top-level namespace")
+ )
end
end
context 'when namespace is a root group' do
it 'is valid' do
- subject.namespace = root_group
+ member_role.namespace = root_group
- expect(subject).to be_valid
+ expect(member_role).to be_valid
end
end
context 'when namespace is not present' do
it 'is invalid with a different error message' do
- subject.namespace = nil
+ member_role.namespace = nil
+
+ expect(member_role).to be_invalid
+ expect(member_role.errors[:namespace]).to include(_("can't be blank"))
+ end
+ end
+
+ context 'when namespace is outside hierarchy of member' do
+ it 'creates a validation error' do
+ member_role.save!
+ member_role.namespace = create(:group)
- expect(subject).to be_invalid
- expect(subject.errors.full_messages).to eq(["Namespace can't be blank"])
+ expect(member_role).not_to be_valid
+ expect(member_role.errors[:namespace]).to include(s_("MemberRole|can't be changed"))
end
end
end
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index e56c6b38992..d573fde5a74 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -85,6 +85,27 @@ RSpec.describe ProjectMember do
end
end
+ describe '#holder_of_the_personal_namespace?' do
+ let_it_be(:project_member) { build(:project_member) }
+
+ using RSpec::Parameterized::TableSyntax
+
+ where(:personal_namespace_holder?, :expected) do
+ false | false
+ true | true
+ end
+
+ with_them do
+ it "returns expected" do
+ allow(project_member.project).to receive(:personal_namespace_holder?)
+ .with(project_member.user)
+ .and_return(personal_namespace_holder?)
+
+ expect(project_member.holder_of_the_personal_namespace?).to be(expected)
+ end
+ end
+ end
+
describe '.import_team' do
before do
@project_1 = create(:project)
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
index 22fed716897..a17b90930f0 100644
--- a/spec/models/merge_request_diff_spec.rb
+++ b/spec/models/merge_request_diff_spec.rb
@@ -507,6 +507,50 @@ RSpec.describe MergeRequestDiff do
end
end
+ describe '#paginated_diffs' do
+ context 'when no persisted files available' do
+ before do
+ diff_with_commits.clean!
+ end
+
+ it 'returns a Gitlab::Diff::FileCollection::Compare' do
+ diffs = diff_with_commits.paginated_diffs(1, 10)
+
+ expect(diffs).to be_a(Gitlab::Diff::FileCollection::Compare)
+ expect(diffs.diff_files.size).to eq(10)
+ end
+ end
+
+ context 'when persisted files available' do
+ it 'returns paginated diffs' do
+ diffs = diff_with_commits.paginated_diffs(1, 10)
+
+ expect(diffs).to be_a(Gitlab::Diff::FileCollection::PaginatedMergeRequestDiff)
+ expect(diffs.diff_files.size).to eq(10)
+ end
+
+ it 'sorts diff files directory first' do
+ diff_with_commits.update!(sorted: false) # Mark as unsorted so it'll re-order
+
+ # There will be 11 returned, as we have to take into account for new and old paths
+ expect(diff_with_commits.paginated_diffs(1, 10).diff_paths).to eq(
+ [
+ 'bar/branch-test.txt',
+ 'custom-highlighting/test.gitlab-custom',
+ 'encoding/iso8859.txt',
+ 'files/images/wm.svg',
+ 'files/js/commit.js.coffee',
+ 'files/js/commit.coffee',
+ 'files/lfs/lfs_object.iso',
+ 'files/ruby/popen.rb',
+ 'files/ruby/regex.rb',
+ 'files/.DS_Store',
+ 'files/whitespace'
+ ])
+ end
+ end
+ end
+
describe '#diffs' do
let(:diff_options) { {} }
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index cf4f58f558c..05586cbfc64 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -30,6 +30,7 @@ RSpec.describe MergeRequest, factory_default: :keep do
it { is_expected.to have_many(:resource_state_events) }
it { is_expected.to have_many(:draft_notes) }
it { is_expected.to have_many(:reviews).inverse_of(:merge_request) }
+ it { is_expected.to have_many(:reviewed_by_users).through(:reviews).source(:author) }
it { is_expected.to have_one(:cleanup_schedule).inverse_of(:merge_request) }
it { is_expected.to have_many(:created_environments).class_name('Environment').inverse_of(:merge_request) }
@@ -46,6 +47,20 @@ RSpec.describe MergeRequest, factory_default: :keep do
expect(project.merge_requests.find(merge_request.id)).to eq(merge_request)
end
end
+
+ describe '#reviewed_by_users' do
+ let!(:merge_request) { create(:merge_request) }
+
+ context 'when the same user has several reviews' do
+ before do
+ 2.times { create(:review, merge_request: merge_request, project: merge_request.project, author: merge_request.author) }
+ end
+
+ it 'returns distinct users' do
+ expect(merge_request.reviewed_by_users).to match_array([merge_request.author])
+ end
+ end
+ end
end
describe '.from_and_to_forks' do
@@ -4229,6 +4244,18 @@ RSpec.describe MergeRequest, factory_default: :keep do
transition!
end
+
+ context 'when transaction is not committed' do
+ it_behaves_like 'transition not triggering mergeRequestMergeStatusUpdated GraphQL subscription' do
+ def transition!
+ MergeRequest.transaction do
+ super
+
+ raise ActiveRecord::Rollback
+ end
+ end
+ end
+ end
end
shared_examples 'for an invalid state transition' do
diff --git a/spec/models/milestone_note_spec.rb b/spec/models/milestone_note_spec.rb
index db1a7ca05f8..9371cef7540 100644
--- a/spec/models/milestone_note_spec.rb
+++ b/spec/models/milestone_note_spec.rb
@@ -20,6 +20,8 @@ RSpec.describe MilestoneNote do
it 'creates the expected note' do
expect(subject.note_html).to include('removed milestone')
expect(subject.note_html).not_to include('changed milestone to')
+ expect(subject.created_at).to eq(event.created_at)
+ expect(subject.updated_at).to eq(event.created_at)
end
end
end
diff --git a/spec/models/ml/candidate_metadata_spec.rb b/spec/models/ml/candidate_metadata_spec.rb
new file mode 100644
index 00000000000..94e21a910be
--- /dev/null
+++ b/spec/models/ml/candidate_metadata_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ml::CandidateMetadata, feature_category: :mlops do
+ describe 'associations' do
+ it { is_expected.to belong_to(:candidate) }
+ end
+
+ describe 'uniqueness of name' do
+ let_it_be(:metadata) { create(:ml_candidate_metadata, name: 'some_metadata') }
+ let_it_be(:candidate) { metadata.candidate }
+
+ it 'is unique within candidate' do
+ expect do
+ candidate.metadata.create!(name: 'some_metadata', value: 'blah')
+ end.to raise_error.with_message(/Name 'some_metadata' already taken/)
+ end
+ end
+end
diff --git a/spec/models/ml/candidate_spec.rb b/spec/models/ml/candidate_spec.rb
index b35496363fe..9ce411191f0 100644
--- a/spec/models/ml/candidate_spec.rb
+++ b/spec/models/ml/candidate_spec.rb
@@ -5,11 +5,18 @@ require 'spec_helper'
RSpec.describe Ml::Candidate, factory_default: :keep do
let_it_be(:candidate) { create(:ml_candidates, :with_metrics_and_params) }
+ let(:project) { candidate.experiment.project }
+
describe 'associations' do
it { is_expected.to belong_to(:experiment) }
it { is_expected.to belong_to(:user) }
it { is_expected.to have_many(:params) }
it { is_expected.to have_many(:metrics) }
+ it { is_expected.to have_many(:metadata) }
+ end
+
+ describe 'default values' do
+ it { expect(described_class.new.iid).to be_present }
end
describe '.artifact_root' do
@@ -18,8 +25,38 @@ RSpec.describe Ml::Candidate, factory_default: :keep do
it { is_expected.to eq("/ml_candidate_#{candidate.iid}/-/") }
end
- describe 'default values' do
- it { expect(described_class.new.iid).to be_present }
+ describe '.package_name' do
+ subject { candidate.package_name }
+
+ it { is_expected.to eq("ml_candidate_#{candidate.iid}") }
+ end
+
+ describe '.package_version' do
+ subject { candidate.package_version }
+
+ it { is_expected.to eq('-') }
+ end
+
+ describe '.artifact' do
+ subject { candidate.artifact }
+
+ context 'when has logged artifacts' do
+ let(:package) do
+ create(:generic_package, name: candidate.package_name, version: candidate.package_version, project: project)
+ end
+
+ it 'returns the package' do
+ package
+
+ is_expected.to eq(package)
+ end
+ end
+
+ context 'when does not have logged artifacts' do
+ let(:tested_candidate) { create(:ml_candidates, :with_metrics_and_params) }
+
+ it { is_expected.to be_nil }
+ end
end
describe '#by_project_id_and_iid' do
diff --git a/spec/models/ml/experiment_metadata_spec.rb b/spec/models/ml/experiment_metadata_spec.rb
new file mode 100644
index 00000000000..e989d495a1c
--- /dev/null
+++ b/spec/models/ml/experiment_metadata_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ml::ExperimentMetadata, feature_category: :mlops do
+ describe 'associations' do
+ it { is_expected.to belong_to(:experiment) }
+ end
+
+ describe 'uniqueness of name' do
+ let_it_be(:metadata) { create(:ml_experiment_metadata, name: 'some_metadata') }
+ let_it_be(:experiment) { metadata.experiment }
+
+ it 'is unique within experiment' do
+ expect do
+ experiment.metadata.create!(name: 'some_metadata', value: 'blah')
+ end.to raise_error.with_message(/Name 'some_metadata' already taken/)
+ end
+ end
+end
diff --git a/spec/models/ml/experiment_spec.rb b/spec/models/ml/experiment_spec.rb
index 789bb3aa88a..52e9f9217f5 100644
--- a/spec/models/ml/experiment_spec.rb
+++ b/spec/models/ml/experiment_spec.rb
@@ -13,6 +13,7 @@ RSpec.describe Ml::Experiment do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:user) }
it { is_expected.to have_many(:candidates) }
+ it { is_expected.to have_many(:metadata) }
end
describe '#by_project_id_and_iid' do
diff --git a/spec/models/namespace_setting_spec.rb b/spec/models/namespace_setting_spec.rb
index 17c49e13c85..e06a6a30f9a 100644
--- a/spec/models/namespace_setting_spec.rb
+++ b/spec/models/namespace_setting_spec.rb
@@ -178,6 +178,63 @@ RSpec.describe NamespaceSetting, type: :model do
end
end
+ describe '#runner_registration_enabled?' do
+ context 'when not a subgroup' do
+ let_it_be(:settings) { create(:namespace_settings) }
+ let_it_be(:group) { create(:group, namespace_settings: settings) }
+
+ before do
+ group.update!(runner_registration_enabled: runner_registration_enabled)
+ end
+
+ context 'when :runner_registration_enabled is false' do
+ let(:runner_registration_enabled) { false }
+
+ it 'returns false' do
+ expect(group.runner_registration_enabled?).to be_falsey
+ end
+
+ it 'does not query the db' do
+ expect { group.runner_registration_enabled? }.not_to exceed_query_limit(0)
+ end
+ end
+
+ context 'when :runner_registration_enabled is true' do
+ let(:runner_registration_enabled) { true }
+
+ it 'returns true' do
+ expect(group.runner_registration_enabled?).to be_truthy
+ end
+ end
+ end
+
+ context 'when a group has parent groups' do
+ let_it_be(:grandparent) { create(:group) }
+ let_it_be(:parent) { create(:group, parent: grandparent) }
+ let_it_be(:group) { create(:group, parent: parent) }
+
+ before do
+ grandparent.update!(runner_registration_enabled: runner_registration_enabled)
+ end
+
+ context 'when a parent group has runner registration disabled' do
+ let(:runner_registration_enabled) { false }
+
+ it 'returns false' do
+ expect(group.runner_registration_enabled?).to be_falsey
+ end
+ end
+
+ context 'when all parent groups have runner registration enabled' do
+ let(:runner_registration_enabled) { true }
+
+ it 'returns true' do
+ expect(group.runner_registration_enabled?).to be_truthy
+ end
+ end
+ end
+ end
+
describe '#delayed_project_removal' do
it_behaves_like 'a cascading namespace setting boolean attribute', settings_attribute_name: :delayed_project_removal
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 0516d446945..80721e11049 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -34,6 +34,7 @@ RSpec.describe Namespace do
it { is_expected.to have_many :member_roles }
it { is_expected.to have_one :cluster_enabled_grant }
it { is_expected.to have_many(:work_items) }
+ it { is_expected.to have_many :achievements }
it do
is_expected.to have_one(:ci_cd_settings).class_name('NamespaceCiCdSetting').inverse_of(:namespace).autosave(true)
@@ -1895,6 +1896,30 @@ RSpec.describe Namespace do
end
end
+ describe '#bot_user_namespace?' do
+ subject { namespace.bot_user_namespace? }
+
+ context 'when owner is a bot user user' do
+ let(:user) { create(:user, :project_bot) }
+ let(:namespace) { user.namespace }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when owner is a non-bot user' do
+ let(:user) { create(:user) }
+ let(:namespace) { user.namespace }
+
+ it { is_expected.to be_falsy }
+ end
+
+ context 'when type is a group' do
+ let(:namespace) { create(:group) }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+
describe '#aggregation_scheduled?' do
let(:namespace) { create(:namespace) }
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 7c71080d63e..328d3ba7dda 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -579,6 +579,7 @@ RSpec.describe Note do
expect(commit_note.confidential?).to be_falsy
end
end
+
context 'when note is confidential' do
it 'is true even when a noteable is not confidential' do
issue = create(:issue, confidential: false)
diff --git a/spec/models/notification_recipient_spec.rb b/spec/models/notification_recipient_spec.rb
index 068166ebb0d..2275bea4c7f 100644
--- a/spec/models/notification_recipient_spec.rb
+++ b/spec/models/notification_recipient_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe NotificationRecipient do
+RSpec.describe NotificationRecipient, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
let(:target) { create(:issue, project: project) }
diff --git a/spec/models/packages/package_file_spec.rb b/spec/models/packages/package_file_spec.rb
index c665f738ead..a244ed34e54 100644
--- a/spec/models/packages/package_file_spec.rb
+++ b/spec/models/packages/package_file_spec.rb
@@ -104,15 +104,9 @@ RSpec.describe Packages::PackageFile, type: :model do
let_it_be(:package, reload: true) { create(:package) }
context 'when the package file has an explicit size' do
- it_behaves_like 'UpdateProjectStatistics' do
- subject { build(:package_file, :jar, package: package, size: 42) }
- end
- end
+ subject { build(:package_file, :jar, package: package, size: 42) }
- context 'when the package file does not have a size' do
- it_behaves_like 'UpdateProjectStatistics' do
- subject { build(:package_file, package: package, size: nil) }
- end
+ it_behaves_like 'UpdateProjectStatistics', :packages_size
end
end
diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb
index 241c585099c..d6f71f2401c 100644
--- a/spec/models/packages/package_spec.rb
+++ b/spec/models/packages/package_spec.rb
@@ -708,12 +708,14 @@ RSpec.describe Packages::Package, type: :model do
describe '#destroy' do
let(:package) { create(:npm_package) }
let(:package_file) { package.package_files.first }
- let(:project_statistics) { ProjectStatistics.for_project_ids(package.project.id).first }
+ let(:project_statistics) { package.project.statistics }
- it 'affects project statistics' do
- expect { package.destroy! }
- .to change { project_statistics.reload.packages_size }
- .from(package_file.size).to(0)
+ subject(:destroy!) { package.destroy! }
+
+ it 'updates the project statistics' do
+ expect(project_statistics).to receive(:increment_counter).with(:packages_size, -package_file.size)
+
+ destroy!
end
end
diff --git a/spec/models/packages/rpm/repository_file_spec.rb b/spec/models/packages/rpm/repository_file_spec.rb
index 34347793dd8..1147fd66ac6 100644
--- a/spec/models/packages/rpm/repository_file_spec.rb
+++ b/spec/models/packages/rpm/repository_file_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Rpm::RepositoryFile, type: :model do
+RSpec.describe Packages::Rpm::RepositoryFile, type: :model, feature_category: :package_registry do
using RSpec::Parameterized::TableSyntax
let_it_be(:repository_file) { create(:rpm_repository_file) }
@@ -16,6 +16,32 @@ RSpec.describe Packages::Rpm::RepositoryFile, type: :model do
it { is_expected.to validate_presence_of(:project) }
end
+ describe '.has_oversized_filelists?' do
+ let_it_be(:filelists) { create(:rpm_repository_file, :filelists, size: 21.megabytes) }
+
+ subject { described_class.has_oversized_filelists?(project_id: filelists.project_id) }
+
+ context 'when has oversized filelists' do
+ it { expect(subject).to be true }
+ end
+
+ context 'when filelists.xml is not oversized' do
+ before do
+ filelists.update!(size: 19.megabytes)
+ end
+
+ it { expect(subject).to be_falsey }
+ end
+
+ context 'when there is no filelists.xml' do
+ before do
+ filelists.destroy!
+ end
+
+ it { expect(subject).to be_falsey }
+ end
+ end
+
context 'when updating project statistics' do
context 'when the package file has an explicit size' do
it_behaves_like 'UpdateProjectStatistics' do
diff --git a/spec/models/pages/lookup_path_spec.rb b/spec/models/pages/lookup_path_spec.rb
index 2d7ee8ba3be..6f684eceaec 100644
--- a/spec/models/pages/lookup_path_spec.rb
+++ b/spec/models/pages/lookup_path_spec.rb
@@ -74,6 +74,12 @@ RSpec.describe Pages::LookupPath do
end
end
+ it 'does not recreate source hash' do
+ expect(deployment.file).to receive(:url_or_file_path).once
+
+ 2.times { lookup_path.source }
+ end
+
context 'when deployment is in the local storage' do
before do
deployment.file.migrate!(::ObjectStorage::Store::LOCAL)
diff --git a/spec/models/pages_deployment_spec.rb b/spec/models/pages_deployment_spec.rb
index a27d836e2c2..268c5006a88 100644
--- a/spec/models/pages_deployment_spec.rb
+++ b/spec/models/pages_deployment_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe PagesDeployment do
+RSpec.describe PagesDeployment, feature_category: :pages do
let_it_be(:project) { create(:project) }
describe 'associations' do
diff --git a/spec/models/performance_monitoring/prometheus_dashboard_spec.rb b/spec/models/performance_monitoring/prometheus_dashboard_spec.rb
index ee2407f21b6..21b16bdeb17 100644
--- a/spec/models/performance_monitoring/prometheus_dashboard_spec.rb
+++ b/spec/models/performance_monitoring/prometheus_dashboard_spec.rb
@@ -229,83 +229,37 @@ RSpec.describe PerformanceMonitoring::PrometheusDashboard do
allow(Gitlab::Metrics::Dashboard::Finder).to receive(:find_raw).with(project, dashboard_path: path).and_call_original
end
- context 'metrics_dashboard_exhaustive_validations is on' do
- before do
- stub_feature_flags(metrics_dashboard_exhaustive_validations: true)
- end
-
- context 'when schema is valid' do
- let(:dashboard_schema) { YAML.safe_load(fixture_file('lib/gitlab/metrics/dashboard/sample_dashboard.yml')) }
+ context 'when schema is valid' do
+ let(:dashboard_schema) { YAML.safe_load(fixture_file('lib/gitlab/metrics/dashboard/sample_dashboard.yml')) }
- it 'returns empty array' do
- expect(Gitlab::Metrics::Dashboard::Validator).to receive(:errors).with(dashboard_schema, dashboard_path: path, project: project).and_return([])
+ it 'returns empty array' do
+ expect(described_class).to receive(:from_json).with(dashboard_schema)
- expect(schema_validation_warnings).to eq []
- end
- end
-
- context 'when schema is invalid' do
- let(:dashboard_schema) { YAML.safe_load(fixture_file('lib/gitlab/metrics/dashboard/dashboard_missing_panel_groups.yml')) }
-
- it 'returns array with errors messages' do
- error = ::Gitlab::Metrics::Dashboard::Validator::Errors::SchemaValidationError.new
-
- expect(Gitlab::Metrics::Dashboard::Validator).to receive(:errors).with(dashboard_schema, dashboard_path: path, project: project).and_return([error])
-
- expect(schema_validation_warnings).to eq [error.message]
- end
- end
-
- context 'when YAML has wrong syntax' do
- let(:project) { create(:project, :repository, :custom_repo, files: { path => fixture_file('lib/gitlab/metrics/dashboard/broken_yml_syntax.yml') }) }
-
- subject(:schema_validation_warnings) { described_class.new(path: path, environment: environment).schema_validation_warnings }
-
- it 'returns array with errors messages' do
- expect(Gitlab::Metrics::Dashboard::Validator).not_to receive(:errors)
-
- expect(schema_validation_warnings).to eq ['Invalid yaml']
- end
+ expect(schema_validation_warnings).to eq []
end
end
- context 'metrics_dashboard_exhaustive_validations is off' do
- before do
- stub_feature_flags(metrics_dashboard_exhaustive_validations: false)
- end
-
- context 'when schema is valid' do
- let(:dashboard_schema) { YAML.safe_load(fixture_file('lib/gitlab/metrics/dashboard/sample_dashboard.yml')) }
-
- it 'returns empty array' do
- expect(described_class).to receive(:from_json).with(dashboard_schema)
-
- expect(schema_validation_warnings).to eq []
- end
- end
-
- context 'when schema is invalid' do
- let(:dashboard_schema) { YAML.safe_load(fixture_file('lib/gitlab/metrics/dashboard/dashboard_missing_panel_groups.yml')) }
+ context 'when schema is invalid' do
+ let(:dashboard_schema) { YAML.safe_load(fixture_file('lib/gitlab/metrics/dashboard/dashboard_missing_panel_groups.yml')) }
- it 'returns array with errors messages' do
- instance = described_class.new
- instance.errors.add(:test, 'test error')
+ it 'returns array with errors messages' do
+ instance = described_class.new
+ instance.errors.add(:test, 'test error')
- expect(described_class).to receive(:from_json).and_raise(ActiveModel::ValidationError.new(instance))
- expect(described_class.new.schema_validation_warnings).to eq ['test: test error']
- end
+ expect(described_class).to receive(:from_json).and_raise(ActiveModel::ValidationError.new(instance))
+ expect(described_class.new.schema_validation_warnings).to eq ['test: test error']
end
+ end
- context 'when YAML has wrong syntax' do
- let(:project) { create(:project, :repository, :custom_repo, files: { path => fixture_file('lib/gitlab/metrics/dashboard/broken_yml_syntax.yml') }) }
+ context 'when YAML has wrong syntax' do
+ let(:project) { create(:project, :repository, :custom_repo, files: { path => fixture_file('lib/gitlab/metrics/dashboard/broken_yml_syntax.yml') }) }
- subject(:schema_validation_warnings) { described_class.new(path: path, environment: environment).schema_validation_warnings }
+ subject(:schema_validation_warnings) { described_class.new(path: path, environment: environment).schema_validation_warnings }
- it 'returns array with errors messages' do
- expect(described_class).not_to receive(:from_json)
+ it 'returns array with errors messages' do
+ expect(described_class).not_to receive(:from_json)
- expect(schema_validation_warnings).to eq ['Invalid yaml']
- end
+ expect(schema_validation_warnings).to eq ['Invalid yaml']
end
end
end
diff --git a/spec/models/plan_limits_spec.rb b/spec/models/plan_limits_spec.rb
index f9c458b2c80..d4e550657c8 100644
--- a/spec/models/plan_limits_spec.rb
+++ b/spec/models/plan_limits_spec.rb
@@ -200,6 +200,7 @@ RSpec.describe PlanLimits do
ci_max_artifact_size_cluster_applications
ci_max_artifact_size_secret_detection
ci_max_artifact_size_requirements
+ ci_max_artifact_size_requirements_v2
ci_max_artifact_size_coverage_fuzzing
ci_max_artifact_size_api_fuzzing
]
diff --git a/spec/models/programming_language_spec.rb b/spec/models/programming_language_spec.rb
index b202c10e30b..403cd77c707 100644
--- a/spec/models/programming_language_spec.rb
+++ b/spec/models/programming_language_spec.rb
@@ -3,6 +3,10 @@
require 'spec_helper'
RSpec.describe ProgrammingLanguage do
+ let_it_be(:ruby) { create(:programming_language, name: 'Ruby') }
+ let_it_be(:python) { create(:programming_language, name: 'Python') }
+ let_it_be(:swift) { create(:programming_language, name: 'Swift') }
+
it { is_expected.to respond_to(:name) }
it { is_expected.to respond_to(:color) }
@@ -12,10 +16,6 @@ RSpec.describe ProgrammingLanguage do
it { is_expected.not_to allow_value("#0z0000").for(:color) }
describe '.with_name_case_insensitive scope' do
- let_it_be(:ruby) { create(:programming_language, name: 'Ruby') }
- let_it_be(:python) { create(:programming_language, name: 'Python') }
- let_it_be(:swift) { create(:programming_language, name: 'Swift') }
-
it 'accepts a single name parameter' do
expect(described_class.with_name_case_insensitive('swift')).to(
contain_exactly(swift)
@@ -28,4 +28,17 @@ RSpec.describe ProgrammingLanguage do
)
end
end
+
+ describe '.most_popular' do
+ before do
+ create_list(:repository_language, 3, programming_language_id: ruby.id)
+ create(:repository_language, programming_language_id: python.id)
+ create_list(:repository_language, 2, programming_language_id: swift.id)
+ ApplicationRecord.connection.execute('analyze repository_languages')
+ end
+
+ it 'returns the most popular programming languages' do
+ expect(described_class.most_popular(2)).to(contain_exactly(ruby, swift))
+ end
+ end
end
diff --git a/spec/models/project_export_job_spec.rb b/spec/models/project_export_job_spec.rb
index 653d4d2df27..01b0aaff0ff 100644
--- a/spec/models/project_export_job_spec.rb
+++ b/spec/models/project_export_job_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ProjectExportJob, type: :model do
+RSpec.describe ProjectExportJob, feature_category: :importers, type: :model do
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to have_many(:relation_exports) }
@@ -13,4 +13,54 @@ RSpec.describe ProjectExportJob, type: :model do
it { is_expected.to validate_presence_of(:jid) }
it { is_expected.to validate_presence_of(:status) }
end
+
+ context 'when pruning expired jobs' do
+ let_it_be(:old_job_1) { create(:project_export_job, updated_at: 37.months.ago) }
+ let_it_be(:old_job_2) { create(:project_export_job, updated_at: 12.months.ago) }
+ let_it_be(:old_job_3) { create(:project_export_job, updated_at: 8.days.ago) }
+ let_it_be(:fresh_job_1) { create(:project_export_job, updated_at: 1.day.ago) }
+ let_it_be(:fresh_job_2) { create(:project_export_job, updated_at: 2.days.ago) }
+ let_it_be(:fresh_job_3) { create(:project_export_job, updated_at: 6.days.ago) }
+
+ let_it_be(:old_relation_export_1) { create(:project_relation_export, project_export_job_id: old_job_1.id) }
+ let_it_be(:old_relation_export_2) { create(:project_relation_export, project_export_job_id: old_job_2.id) }
+ let_it_be(:old_relation_export_3) { create(:project_relation_export, project_export_job_id: old_job_3.id) }
+ let_it_be(:fresh_relation_export_1) { create(:project_relation_export, project_export_job_id: fresh_job_1.id) }
+
+ let_it_be(:old_upload_1) { create(:relation_export_upload, project_relation_export_id: old_relation_export_1.id) }
+ let_it_be(:old_upload_2) { create(:relation_export_upload, project_relation_export_id: old_relation_export_2.id) }
+ let_it_be(:old_upload_3) { create(:relation_export_upload, project_relation_export_id: old_relation_export_3.id) }
+ let_it_be(:fresh_upload_1) do
+ create(
+ :relation_export_upload,
+ project_relation_export_id: fresh_relation_export_1.id
+ )
+ end
+
+ it 'prunes jobs and associations older than 7 days' do
+ expect { described_class.prune_expired_jobs }.to change { described_class.count }.by(-3)
+
+ expect(described_class.find_by(id: old_job_1.id)).to be_nil
+ expect(described_class.find_by(id: old_job_2.id)).to be_nil
+ expect(described_class.find_by(id: old_job_3.id)).to be_nil
+
+ expect(Projects::ImportExport::RelationExport.find_by(id: old_relation_export_1.id)).to be_nil
+ expect(Projects::ImportExport::RelationExport.find_by(id: old_relation_export_2.id)).to be_nil
+ expect(Projects::ImportExport::RelationExport.find_by(id: old_relation_export_3.id)).to be_nil
+
+ expect(Projects::ImportExport::RelationExportUpload.find_by(id: old_upload_1.id)).to be_nil
+ expect(Projects::ImportExport::RelationExportUpload.find_by(id: old_upload_2.id)).to be_nil
+ expect(Projects::ImportExport::RelationExportUpload.find_by(id: old_upload_3.id)).to be_nil
+ end
+
+ it 'does not delete associated records for jobs younger than 7 days' do
+ described_class.prune_expired_jobs
+
+ expect(fresh_job_1.reload).to be_present
+ expect(fresh_job_2.reload).to be_present
+ expect(fresh_job_3.reload).to be_present
+ expect(fresh_relation_export_1.reload).to be_present
+ expect(fresh_upload_1.reload).to be_present
+ end
+ end
end
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index dae0f84eda3..fb6aaffdf22 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -291,6 +291,7 @@ RSpec.describe ProjectFeature do
end
end
end
+
# rubocop:disable Gitlab/FeatureAvailableUsage
describe '#feature_available?' do
let(:features) { ProjectFeature::FEATURES }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 1cae03ae2ae..f33001b9c5b 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -21,7 +21,6 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to belong_to(:creator).class_name('User') }
it { is_expected.to belong_to(:pool_repository) }
it { is_expected.to have_many(:users) }
- it { is_expected.to have_many(:integrations) }
it { is_expected.to have_many(:events) }
it { is_expected.to have_many(:merge_requests) }
it { is_expected.to have_many(:merge_request_metrics).class_name('MergeRequest::Metrics') }
@@ -58,7 +57,6 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_one(:pipelines_email_integration) }
it { is_expected.to have_one(:irker_integration) }
it { is_expected.to have_one(:pivotaltracker_integration) }
- it { is_expected.to have_one(:flowdock_integration) }
it { is_expected.to have_one(:assembla_integration) }
it { is_expected.to have_one(:slack_slash_commands_integration) }
it { is_expected.to have_one(:mattermost_slash_commands_integration) }
@@ -151,6 +149,20 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_many(:project_callouts).class_name('Users::ProjectCallout').with_foreign_key(:project_id) }
it { is_expected.to have_many(:pipeline_metadata).class_name('Ci::PipelineMetadata') }
it { is_expected.to have_many(:incident_management_timeline_event_tags).class_name('IncidentManagement::TimelineEventTag') }
+ it { is_expected.to have_many(:integrations) }
+ it { is_expected.to have_many(:push_hooks_integrations).class_name('Integration') }
+ it { is_expected.to have_many(:tag_push_hooks_integrations).class_name('Integration') }
+ it { is_expected.to have_many(:issue_hooks_integrations).class_name('Integration') }
+ it { is_expected.to have_many(:confidential_issue_hooks_integrations).class_name('Integration') }
+ it { is_expected.to have_many(:merge_request_hooks_integrations).class_name('Integration') }
+ it { is_expected.to have_many(:note_hooks_integrations).class_name('Integration') }
+ it { is_expected.to have_many(:confidential_note_hooks_integrations).class_name('Integration') }
+ it { is_expected.to have_many(:job_hooks_integrations).class_name('Integration') }
+ it { is_expected.to have_many(:archive_trace_hooks_integrations).class_name('Integration') }
+ it { is_expected.to have_many(:pipeline_hooks_integrations).class_name('Integration') }
+ it { is_expected.to have_many(:wiki_page_hooks_integrations).class_name('Integration') }
+ it { is_expected.to have_many(:deployment_hooks_integrations).class_name('Integration') }
+ it { is_expected.to have_many(:alert_hooks_integrations).class_name('Integration') }
# GitLab Pages
it { is_expected.to have_many(:pages_domains) }
@@ -356,6 +368,33 @@ RSpec.describe Project, factory_default: :keep do
subject.ci_pipelines
end
end
+
+ context 'order of the `has_many :notes` association' do
+ let(:associations_having_dependent_destroy) do
+ described_class.reflect_on_all_associations(:has_many).select do |assoc|
+ assoc.options[:dependent] == :destroy
+ end
+ end
+
+ let(:associations_having_dependent_destroy_with_issuable_included) do
+ associations_having_dependent_destroy.select do |association|
+ association.klass.include?(Issuable)
+ end
+ end
+
+ it 'has `has_many :notes` as the first association among all the other associations that'\
+ 'includes the `Issuable` module' do
+ names_of_associations_having_dependent_destroy = associations_having_dependent_destroy.map(&:name)
+ index_of_has_many_notes_association = names_of_associations_having_dependent_destroy.find_index(:notes)
+
+ associations_having_dependent_destroy_with_issuable_included.each do |issuable_included_association|
+ index_of_issuable_included_association =
+ names_of_associations_having_dependent_destroy.find_index(issuable_included_association.name)
+
+ expect(index_of_has_many_notes_association).to be < index_of_issuable_included_association
+ end
+ end
+ end
end
describe 'modules' do
@@ -5759,6 +5798,32 @@ RSpec.describe Project, factory_default: :keep do
integration.project.execute_integrations(anything, :merge_request_hooks)
end
+
+ it 'does not trigger extra queries when called multiple times' do
+ integration.project.execute_integrations({}, :push_hooks)
+
+ recorder = ActiveRecord::QueryRecorder.new do
+ integration.project.execute_integrations({}, :push_hooks)
+ end
+
+ expect(recorder.count).to be_zero
+ end
+
+ context 'with cache_project_integrations disabled' do
+ before do
+ stub_feature_flags(cache_project_integrations: false)
+ end
+
+ it 'triggers extra queries when called multiple times' do
+ integration.project.execute_integrations({}, :push_hooks)
+
+ recorder = ActiveRecord::QueryRecorder.new do
+ integration.project.execute_integrations({}, :push_hooks)
+ end
+
+ expect(recorder.count).not_to be_zero
+ end
+ end
end
describe '#has_active_hooks?' do
@@ -8156,6 +8221,16 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '#work_items_mvc_feature_flag_enabled?' do
+ let_it_be(:group_project) { create(:project, :in_subgroup) }
+
+ it_behaves_like 'checks parent group feature flag' do
+ let(:feature_flag_method) { :work_items_mvc_feature_flag_enabled? }
+ let(:feature_flag) { :work_items_mvc }
+ let(:subject_project) { group_project }
+ end
+ end
+
describe '#work_items_mvc_2_feature_flag_enabled?' do
let_it_be(:group_project) { create(:project, :in_subgroup) }
@@ -8370,6 +8445,105 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to be(false) }
end
+ describe '.cascading_with_parent_namespace' do
+ let_it_be_with_reload(:group) { create(:group, :with_root_storage_statistics) }
+ let_it_be_with_reload(:subgroup) { create(:group, parent: group) }
+ let_it_be_with_reload(:project) { create(:project, group: subgroup) }
+ let_it_be_with_reload(:project_without_group) { create(:project) }
+
+ shared_examples 'cascading settings' do |attribute|
+ it 'return self value when no parent' do
+ expect(project_without_group.group).to be_nil
+
+ project_without_group.update!(attribute => true)
+ expect(project_without_group.public_send("#{attribute}?", inherit_group_setting: true)).to be_truthy
+ expect(project_without_group.public_send("#{attribute}_locked?")).to be_falsey
+
+ project_without_group.update!(attribute => false)
+ expect(project_without_group.public_send("#{attribute}?", inherit_group_setting: true)).to be_falsey
+ expect(project_without_group.public_send("#{attribute}_locked?")).to be_falsey
+ end
+
+ it 'return self value when unlocked' do
+ subgroup.namespace_settings.update!(attribute => false)
+ group.namespace_settings.update!(attribute => false)
+
+ project.update!(attribute => true)
+ expect(project.public_send("#{attribute}?", inherit_group_setting: true)).to be_truthy
+ expect(project.public_send("#{attribute}_locked?")).to be_falsey
+
+ project.update!(attribute => false)
+ expect(project.public_send("#{attribute}?", inherit_group_setting: true)).to be_falsey
+ expect(project.public_send("#{attribute}_locked?")).to be_falsey
+ end
+
+ it 'still return self value when locked subgroup' do
+ subgroup.namespace_settings.update!(attribute => true)
+ group.namespace_settings.update!(attribute => false)
+
+ project.update!(attribute => true)
+ expect(project.public_send("#{attribute}?", inherit_group_setting: true)).to be_truthy
+ expect(project.public_send("#{attribute}_locked?")).to be_falsey
+
+ project.update!(attribute => false)
+ expect(project.public_send("#{attribute}?", inherit_group_setting: true)).to be_falsey
+ expect(project.public_send("#{attribute}_locked?")).to be_falsey
+ end
+
+ it 'still return unlocked value when locked group' do
+ subgroup.namespace_settings.update!(attribute => false)
+ group.namespace_settings.update!(attribute => true)
+
+ project.update!(attribute => true)
+ expect(project.public_send("#{attribute}?", inherit_group_setting: true)).to be_truthy
+ expect(project.public_send("#{attribute}_locked?")).to be_falsey
+
+ project.update!(attribute => false)
+ expect(project.public_send("#{attribute}?", inherit_group_setting: true)).to be_falsey
+ expect(project.public_send("#{attribute}_locked?")).to be_falsey
+ end
+ end
+
+ it_behaves_like 'cascading settings', :only_allow_merge_if_pipeline_succeeds
+ it_behaves_like 'cascading settings', :allow_merge_on_skipped_pipeline
+ it_behaves_like 'cascading settings', :only_allow_merge_if_all_discussions_are_resolved
+ end
+
+ describe '#archived' do
+ it { expect(subject.archived).to be_falsey }
+ it { expect(described_class.new(archived: true).archived).to be_truthy }
+ end
+
+ describe '#resolve_outdated_diff_discussions' do
+ it { expect(subject.resolve_outdated_diff_discussions).to be_falsey }
+
+ context 'when set explicitly' do
+ subject { described_class.new(resolve_outdated_diff_discussions: true) }
+
+ it { expect(subject.resolve_outdated_diff_discussions).to be_truthy }
+ end
+ end
+
+ describe '#only_allow_merge_if_all_discussions_are_resolved' do
+ it { expect(subject.only_allow_merge_if_all_discussions_are_resolved).to be_falsey }
+
+ context 'when set explicitly' do
+ subject { described_class.new(only_allow_merge_if_all_discussions_are_resolved: true) }
+
+ it { expect(subject.only_allow_merge_if_all_discussions_are_resolved).to be_truthy }
+ end
+ end
+
+ describe '#remove_source_branch_after_merge' do
+ it { expect(subject.remove_source_branch_after_merge).to be_truthy }
+
+ context 'when set explicitly' do
+ subject { described_class.new(remove_source_branch_after_merge: false) }
+
+ it { expect(subject.remove_source_branch_after_merge).to be_falsey }
+ end
+ end
+
private
def finish_job(export_job)
diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb
index 9de31ea66e4..a6e2bcf1525 100644
--- a/spec/models/project_statistics_spec.rb
+++ b/spec/models/project_statistics_spec.rb
@@ -467,6 +467,13 @@ RSpec.describe ProjectStatistics do
.to change { statistics.reload.storage_size }
.by(20)
end
+
+ it 'schedules a namespace aggregation worker' do
+ expect(Namespaces::ScheduleAggregationWorker).to receive(:perform_async)
+ .with(statistics.project.namespace.id)
+
+ described_class.increment_statistic(project, stat, 20)
+ end
end
shared_examples 'a statistic that increases storage_size asynchronously' do
@@ -474,7 +481,8 @@ RSpec.describe ProjectStatistics do
described_class.increment_statistic(project, stat, 13)
Gitlab::Redis::SharedState.with do |redis|
- increment = redis.get(statistics.counter_key(stat))
+ key = statistics.counter(stat).key
+ increment = redis.get(key)
expect(increment.to_i).to eq(13)
end
end
@@ -482,7 +490,7 @@ RSpec.describe ProjectStatistics do
it 'schedules a worker to update the statistic and storage_size async', :sidekiq_inline do
expect(FlushCounterIncrementsWorker)
.to receive(:perform_in)
- .with(CounterAttribute::WORKER_DELAY, described_class.name, statistics.id, stat)
+ .with(Gitlab::Counters::BufferedCounter::WORKER_DELAY, described_class.name, statistics.id, stat)
.and_call_original
expect { described_class.increment_statistic(project, stat, 20) }
@@ -506,20 +514,20 @@ RSpec.describe ProjectStatistics do
context 'when adjusting :packages_size' do
let(:stat) { :packages_size }
- it_behaves_like 'a statistic that increases storage_size'
+ it_behaves_like 'a statistic that increases storage_size asynchronously'
end
context 'when the amount is 0' do
it 'does not execute a query' do
project
- expect { described_class.increment_statistic(project.id, :build_artifacts_size, 0) }
+ expect { described_class.increment_statistic(project, :build_artifacts_size, 0) }
.not_to exceed_query_limit(0)
end
end
context 'when using an invalid column' do
it 'raises an error' do
- expect { described_class.increment_statistic(project.id, :id, 13) }
+ expect { described_class.increment_statistic(project, :id, 13) }
.to raise_error(ArgumentError, "Cannot increment attribute: id")
end
end
diff --git a/spec/models/projects/build_artifacts_size_refresh_spec.rb b/spec/models/projects/build_artifacts_size_refresh_spec.rb
index 21cd8e0b9d4..caff06262d9 100644
--- a/spec/models/projects/build_artifacts_size_refresh_spec.rb
+++ b/spec/models/projects/build_artifacts_size_refresh_spec.rb
@@ -67,6 +67,8 @@ RSpec.describe Projects::BuildArtifactsSizeRefresh, type: :model do
let!(:last_job_artifact_id_on_refresh_start) { create(:ci_job_artifact, project: refresh.project) }
+ let(:statistics) { refresh.project.statistics }
+
before do
stats = create(:project_statistics, project: refresh.project, build_artifacts_size: 120)
stats.increment_counter(:build_artifacts_size, 30)
@@ -89,11 +91,11 @@ RSpec.describe Projects::BuildArtifactsSizeRefresh, type: :model do
end
it 'resets the build artifacts size stats' do
- expect { refresh.process! }.to change { refresh.project.statistics.build_artifacts_size }.to(0)
+ expect { refresh.process! }.to change { statistics.build_artifacts_size }.to(0)
end
it 'resets the counter attribute to zero' do
- expect { refresh.process! }.to change { refresh.project.statistics.get_counter_value(:build_artifacts_size) }.to(0)
+ expect { refresh.process! }.to change { statistics.counter(:build_artifacts_size).get }.to(0)
end
end
diff --git a/spec/models/projects/forks/divergence_counts_spec.rb b/spec/models/projects/forks/divergence_counts_spec.rb
new file mode 100644
index 00000000000..fd69cc0f3e7
--- /dev/null
+++ b/spec/models/projects/forks/divergence_counts_spec.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::Forks::DivergenceCounts, feature_category: :source_code_management do
+ include ProjectForksHelper
+
+ let_it_be(:user) { create(:user) }
+
+ describe '#counts', :use_clean_rails_redis_caching do
+ let(:source_repo) { create(:project, :repository, :public).repository }
+ let(:fork_repo) { fork_project(source_repo.project, user, { repository: true }).repository }
+ let(:fork_branch) { 'fork-branch' }
+ let(:cache_key) { ['project_forks', fork_repo.project.id, fork_branch, 'divergence_counts'] }
+
+ def expect_cached_counts(value)
+ counts = described_class.new(fork_repo.project, fork_branch).counts
+
+ ahead, behind = value
+ expect(counts).to eq({ ahead: ahead, behind: behind })
+
+ cached_value = [source_repo.commit.sha, fork_repo.commit(fork_branch).sha, value]
+ expect(Rails.cache.read(cache_key)).to eq(cached_value)
+ end
+
+ it 'shows how far behind/ahead a fork is from the upstream' do
+ fork_repo.create_branch(fork_branch)
+
+ expect_cached_counts([0, 0])
+
+ fork_repo.commit_files(
+ user,
+ branch_name: fork_branch, message: 'Committing something',
+ actions: [{ action: :create, file_path: 'encoding/CHANGELOG', content: 'New file' }]
+ )
+
+ expect_cached_counts([1, 0])
+
+ fork_repo.commit_files(
+ user,
+ branch_name: fork_branch, message: 'Committing something else',
+ actions: [{ action: :create, file_path: 'encoding/ONE-MORE-CHANGELOG', content: 'One more new file' }]
+ )
+
+ expect_cached_counts([2, 0])
+
+ source_repo.commit_files(
+ user,
+ branch_name: source_repo.root_ref, message: 'Commit to root ref',
+ actions: [{ action: :create, file_path: 'encoding/CHANGELOG', content: 'One more' }]
+ )
+
+ expect_cached_counts([2, 1])
+
+ source_repo.commit_files(
+ user,
+ branch_name: source_repo.root_ref, message: 'Another commit to root ref',
+ actions: [{ action: :create, file_path: 'encoding/NEW-CHANGELOG', content: 'One more time' }]
+ )
+
+ expect_cached_counts([2, 2])
+
+ # When the fork is too far ahead
+ stub_const("#{described_class}::LATEST_COMMITS_COUNT", 1)
+ fork_repo.commit_files(
+ user,
+ branch_name: fork_branch, message: 'Another commit to fork',
+ actions: [{ action: :create, file_path: 'encoding/TOO-NEW-CHANGELOG', content: 'New file' }]
+ )
+
+ expect_cached_counts(nil)
+ end
+
+ context 'when counts calculated from a branch that exists upstream' do
+ let(:fork_branch) { 'feature' }
+
+ it 'compares the fork branch to upstream default branch' do
+ # The branch itself diverges from the upstream default branch
+ expect_cached_counts([1, 29])
+
+ source_repo.commit_files(
+ user,
+ branch_name: source_repo.root_ref, message: 'Commit to root ref',
+ actions: [{ action: :create, file_path: 'encoding/CHANGELOG', content: 'New file' }]
+ )
+
+ fork_repo.commit_files(
+ user,
+ branch_name: fork_branch, message: 'Committing to feature branch',
+ actions: [{ action: :create, file_path: 'encoding/FEATURE-BRANCH', content: 'New file' }]
+ )
+
+ # It takes into account diverged commits from upstream AND from fork
+ expect_cached_counts([2, 30])
+ end
+ end
+ end
+end
diff --git a/spec/models/release_highlight_spec.rb b/spec/models/release_highlight_spec.rb
index 3555dfba769..4148452f849 100644
--- a/spec/models/release_highlight_spec.rb
+++ b/spec/models/release_highlight_spec.rb
@@ -2,8 +2,8 @@
require 'spec_helper'
-RSpec.describe ReleaseHighlight, :clean_gitlab_redis_cache do
- let(:fixture_dir_glob) { Dir.glob(File.join(Rails.root, 'spec', 'fixtures', 'whats_new', '*.yml')).grep(/\d*\_(\d*\_\d*)\.yml$/) }
+RSpec.describe ReleaseHighlight, :clean_gitlab_redis_cache, feature_category: :release_orchestration do
+ let(:fixture_dir_glob) { Dir.glob(File.join(Rails.root, 'spec', 'fixtures', 'whats_new', '*.yml')).grep(/\d*_(\d*_\d*)\.yml$/) }
before do
allow(Dir).to receive(:glob).with(Rails.root.join('data', 'whats_new', '*.yml')).and_return(fixture_dir_glob)
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index c17e180f282..969a279dd52 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -5,7 +5,9 @@ require 'spec_helper'
RSpec.describe Repository do
include RepoHelpers
- TestBlob = Struct.new(:path)
+ before do
+ stub_const('TestBlob', Struct.new(:path))
+ end
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
@@ -164,11 +166,11 @@ RSpec.describe Repository do
repository.add_tag(user, annotated_tag_name, 'a48e4fc218069f68ef2e769dd8dfea3991362175', 'test tag message\n')
end
- it { is_expected.to eq(['v1.0.0', 'v1.1.0', annotated_tag_name]) }
-
after do
repository.rm_tag(user, annotated_tag_name)
end
+
+ it { is_expected.to eq(['v1.0.0', 'v1.1.0', annotated_tag_name]) }
end
end
@@ -2224,32 +2226,34 @@ RSpec.describe Repository do
describe '#after_change_head' do
it 'flushes the method caches' do
- expect(repository).to receive(:expire_method_caches).with([
- :size,
- :commit_count,
- :readme_path,
- :contribution_guide,
- :changelog,
- :license_blob,
- :license_licensee,
- :license_gitaly,
- :gitignore,
- :gitlab_ci_yml,
- :branch_names,
- :tag_names,
- :branch_count,
- :tag_count,
- :avatar,
- :exists?,
- :root_ref,
- :merged_branch_names,
- :has_visible_content?,
- :issue_template_names_hash,
- :merge_request_template_names_hash,
- :user_defined_metrics_dashboard_paths,
- :xcode_project?,
- :has_ambiguous_refs?
- ])
+ expect(repository).to receive(:expire_method_caches).with(
+ [
+ :size,
+ :commit_count,
+ :readme_path,
+ :contribution_guide,
+ :changelog,
+ :license_blob,
+ :license_licensee,
+ :license_gitaly,
+ :gitignore,
+ :gitlab_ci_yml,
+ :branch_names,
+ :tag_names,
+ :branch_count,
+ :tag_count,
+ :avatar,
+ :exists?,
+ :root_ref,
+ :merged_branch_names,
+ :has_visible_content?,
+ :issue_template_names_hash,
+ :merge_request_template_names_hash,
+ :user_defined_metrics_dashboard_paths,
+ :xcode_project?,
+ :has_ambiguous_refs?
+ ]
+ )
repository.after_change_head
end
diff --git a/spec/models/service_desk_setting_spec.rb b/spec/models/service_desk_setting_spec.rb
index f99ac84175c..c1ec35732b8 100644
--- a/spec/models/service_desk_setting_spec.rb
+++ b/spec/models/service_desk_setting_spec.rb
@@ -39,11 +39,16 @@ RSpec.describe ServiceDeskSetting do
let_it_be(:project1) { create(:project, name: 'test-one', group: group) }
let_it_be(:project2) { create(:project, name: 'one', group: subgroup) }
let_it_be(:project_key) { 'key' }
-
- before_all do
+ let!(:setting) do
create(:service_desk_setting, project: project1, project_key: project_key)
end
+ context 'when project_key exists' do
+ it 'is valid' do
+ expect(setting).to be_valid
+ end
+ end
+
context 'when project_key is unique for every project slug' do
it 'does not add error' do
settings = build(:service_desk_setting, project: project2, project_key: 'otherkey')
diff --git a/spec/models/snippet_repository_spec.rb b/spec/models/snippet_repository_spec.rb
index 655cfad57c9..050f99fd4d5 100644
--- a/spec/models/snippet_repository_spec.rb
+++ b/spec/models/snippet_repository_spec.rb
@@ -38,6 +38,9 @@ RSpec.describe SnippetRepository do
let(:update_file) { { previous_path: 'README', file_path: 'README', content: 'bar' } }
let(:data) { [new_file, move_file, update_file] }
+ let_it_be(:unnamed_snippet) { { file_path: '', content: 'dummy', action: :create } }
+ let_it_be(:named_snippet) { { file_path: 'fee.txt', content: 'bar', action: :create } }
+
it 'returns nil when files argument is empty' do
expect(snippet.repository).not_to receive(:commit_files)
@@ -210,9 +213,6 @@ RSpec.describe SnippetRepository do
end
end
- let_it_be(:named_snippet) { { file_path: 'fee.txt', content: 'bar', action: :create } }
- let_it_be(:unnamed_snippet) { { file_path: '', content: 'dummy', action: :create } }
-
context 'when existing file has a default name' do
let(:default_name) { 'snippetfile1.txt' }
let(:new_file) { { file_path: '', content: 'bar' } }
diff --git a/spec/models/state_note_spec.rb b/spec/models/state_note_spec.rb
index e91150695b0..0afdf6bbcb9 100644
--- a/spec/models/state_note_spec.rb
+++ b/spec/models/state_note_spec.rb
@@ -19,6 +19,7 @@ RSpec.describe StateNote do
it 'contains the expected values' do
expect(subject.author).to eq(author)
expect(subject.created_at).to eq(event.created_at)
+ expect(subject.updated_at).to eq(event.created_at)
expect(subject.note).to eq(state)
end
end
@@ -33,7 +34,8 @@ RSpec.describe StateNote do
it 'contains the expected values' do
expect(subject.author).to eq(author)
- expect(subject.created_at).to eq(subject.created_at)
+ expect(subject.created_at).to eq(event.created_at)
+ expect(subject.updated_at).to eq(event.created_at)
expect(subject.note).to eq("closed via commit #{commit.id}")
end
end
@@ -45,6 +47,7 @@ RSpec.describe StateNote do
it 'contains the expected values' do
expect(subject.author).to eq(author)
expect(subject.created_at).to eq(event.created_at)
+ expect(subject.updated_at).to eq(event.created_at)
expect(subject.note).to eq("closed via merge request !#{merge_request.iid}")
end
end
@@ -55,6 +58,7 @@ RSpec.describe StateNote do
it 'contains the expected values' do
expect(subject.author).to eq(author)
expect(subject.created_at).to eq(event.created_at)
+ expect(subject.updated_at).to eq(event.created_at)
expect(subject.note).to eq('resolved the corresponding error and closed the issue')
end
end
@@ -65,6 +69,7 @@ RSpec.describe StateNote do
it 'contains the expected values' do
expect(subject.author).to eq(author)
expect(subject.created_at).to eq(event.created_at)
+ expect(subject.updated_at).to eq(event.created_at)
expect(subject.note).to eq('automatically closed this incident because the alert resolved')
end
end
diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb
index 23ba0be2fbc..221f09dd87f 100644
--- a/spec/models/todo_spec.rb
+++ b/spec/models/todo_spec.rb
@@ -56,6 +56,15 @@ RSpec.describe Todo do
expect(subject.body).to eq 'quick fix'
end
+
+ it 'returns full path of target when action is member_access_requested' do
+ group = create(:group)
+
+ subject.target = group
+ subject.action = Todo::MEMBER_ACCESS_REQUESTED
+
+ expect(subject.body).to eq group.full_path
+ end
end
describe '#done' do
@@ -182,6 +191,17 @@ RSpec.describe Todo do
expect(subject.target_reference).to eq issue.to_reference(full: false)
end
+
+ context 'when target is member access requested' do
+ it 'returns group full path' do
+ group = create(:group)
+
+ subject.target = group
+ subject.action = Todo::MEMBER_ACCESS_REQUESTED
+
+ expect(subject.target_reference).to eq group.full_path
+ end
+ end
end
describe '#self_added?' do
diff --git a/spec/models/user_detail_spec.rb b/spec/models/user_detail_spec.rb
index 04964d36dcd..ed55aca49b7 100644
--- a/spec/models/user_detail_spec.rb
+++ b/spec/models/user_detail_spec.rb
@@ -48,6 +48,23 @@ RSpec.describe UserDetail do
describe '#website_url' do
it { is_expected.to validate_length_of(:website_url).is_at_most(500) }
+
+ it 'only validates the website_url if it is changed' do
+ user_detail = create(:user_detail)
+ # `update_attribute` required to bypass current validations
+ # Validations on `User#website_url` were added after
+ # there was already data in the database and `UserDetail#website_url` is
+ # derived from `User#website_url` so this reproduces the state of some of
+ # our production data
+ user_detail.update_attribute(:website_url, 'NotAUrl')
+
+ expect(user_detail).to be_valid
+
+ user_detail.website_url = 'AlsoNotAUrl'
+
+ expect(user_detail).not_to be_valid
+ expect(user_detail.errors.full_messages).to match_array(["Website url is not a valid URL"])
+ end
end
end
diff --git a/spec/models/user_preference_spec.rb b/spec/models/user_preference_spec.rb
index d76334d7c9e..a6f64c90657 100644
--- a/spec/models/user_preference_spec.rb
+++ b/spec/models/user_preference_spec.rb
@@ -3,7 +3,9 @@
require 'spec_helper'
RSpec.describe UserPreference do
- let(:user_preference) { create(:user_preference) }
+ let_it_be(:user) { create(:user) }
+
+ let(:user_preference) { create(:user_preference, user: user) }
describe 'validations' do
describe 'diffs_deletion_color and diffs_addition_color' do
@@ -132,10 +134,24 @@ RSpec.describe UserPreference do
describe '#tab_width' do
it 'is set to 8 by default' do
# Intentionally not using factory here to test the constructor.
- pref = UserPreference.new
+ pref = described_class.new
+
+ expect(pref.tab_width).to eq(8)
+ end
+
+ it 'returns default value when assigning nil' do
+ pref = described_class.new(tab_width: nil)
+
expect(pref.tab_width).to eq(8)
end
+ it 'returns default value when the value is NULL' do
+ pref = create(:user_preference, user: user)
+ pref.update_column(:tab_width, nil)
+
+ expect(pref.reload.tab_width).to eq(8)
+ end
+
it do
is_expected.to validate_numericality_of(:tab_width)
.only_integer
@@ -143,4 +159,141 @@ RSpec.describe UserPreference do
.is_less_than_or_equal_to(12)
end
end
+
+ describe '#tab_width=' do
+ it 'sets to default value when nil' do
+ pref = described_class.new(tab_width: nil)
+
+ expect(pref.read_attribute(:tab_width)).to eq(8)
+ end
+
+ it 'sets user values' do
+ pref = described_class.new(tab_width: 12)
+
+ expect(pref.read_attribute(:tab_width)).to eq(12)
+ end
+ end
+
+ describe '#time_display_relative' do
+ it 'is set to true by default' do
+ pref = described_class.new
+
+ expect(pref.time_display_relative).to eq(true)
+ end
+
+ it 'returns default value when assigning nil' do
+ pref = described_class.new(time_display_relative: nil)
+
+ expect(pref.time_display_relative).to eq(true)
+ end
+
+ it 'returns default value when the value is NULL' do
+ pref = create(:user_preference, user: user)
+ pref.update_column(:time_display_relative, nil)
+
+ expect(pref.reload.time_display_relative).to eq(true)
+ end
+
+ it 'returns assigned value' do
+ pref = described_class.new(time_display_relative: false)
+
+ expect(pref.time_display_relative).to eq(false)
+ end
+ end
+
+ describe '#time_display_relative=' do
+ it 'sets to default value when nil' do
+ pref = described_class.new(time_display_relative: nil)
+
+ expect(pref.read_attribute(:time_display_relative)).to eq(true)
+ end
+
+ it 'sets user values' do
+ pref = described_class.new(time_display_relative: false)
+
+ expect(pref.read_attribute(:time_display_relative)).to eq(false)
+ end
+ end
+
+ describe '#time_format_in_24h' do
+ it 'is set to false by default' do
+ pref = described_class.new
+
+ expect(pref.time_format_in_24h).to eq(false)
+ end
+
+ it 'returns default value when assigning nil' do
+ pref = described_class.new(time_format_in_24h: nil)
+
+ expect(pref.time_format_in_24h).to eq(false)
+ end
+
+ it 'returns default value when the value is NULL' do
+ pref = create(:user_preference, user: user)
+ pref.update_column(:time_format_in_24h, nil)
+
+ expect(pref.reload.time_format_in_24h).to eq(false)
+ end
+
+ it 'returns assigned value' do
+ pref = described_class.new(time_format_in_24h: true)
+
+ expect(pref.time_format_in_24h).to eq(true)
+ end
+ end
+
+ describe '#time_format_in_24h=' do
+ it 'sets to default value when nil' do
+ pref = described_class.new(time_format_in_24h: nil)
+
+ expect(pref.read_attribute(:time_format_in_24h)).to eq(false)
+ end
+
+ it 'sets user values' do
+ pref = described_class.new(time_format_in_24h: true)
+
+ expect(pref.read_attribute(:time_format_in_24h)).to eq(true)
+ end
+ end
+
+ describe '#render_whitespace_in_code' do
+ it 'is set to false by default' do
+ pref = described_class.new
+
+ expect(pref.render_whitespace_in_code).to eq(false)
+ end
+
+ it 'returns default value when assigning nil' do
+ pref = described_class.new(render_whitespace_in_code: nil)
+
+ expect(pref.render_whitespace_in_code).to eq(false)
+ end
+
+ it 'returns default value when the value is NULL' do
+ pref = create(:user_preference, user: user)
+ pref.update_column(:render_whitespace_in_code, nil)
+
+ expect(pref.reload.render_whitespace_in_code).to eq(false)
+ end
+
+ it 'returns assigned value' do
+ pref = described_class.new(render_whitespace_in_code: true)
+
+ expect(pref.render_whitespace_in_code).to eq(true)
+ end
+ end
+
+ describe '#render_whitespace_in_code=' do
+ it 'sets to default value when nil' do
+ pref = described_class.new(render_whitespace_in_code: nil)
+
+ expect(pref.read_attribute(:render_whitespace_in_code)).to eq(false)
+ end
+
+ it 'sets user values' do
+ pref = described_class.new(render_whitespace_in_code: true)
+
+ expect(pref.read_attribute(:render_whitespace_in_code)).to eq(true)
+ end
+ end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 7207ee0b172..4a66af4ddf1 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -81,6 +81,9 @@ RSpec.describe User do
it { is_expected.to delegate_method(:use_legacy_web_ide).to(:user_preference) }
it { is_expected.to delegate_method(:use_legacy_web_ide=).to(:user_preference).with_arguments(:args) }
+ it { is_expected.to delegate_method(:use_new_navigation).to(:user_preference) }
+ it { is_expected.to delegate_method(:use_new_navigation=).to(:user_preference).with_arguments(:args) }
+
it { is_expected.to delegate_method(:job_title).to(:user_detail).allow_nil }
it { is_expected.to delegate_method(:job_title=).to(:user_detail).with_arguments(:args).allow_nil }
@@ -146,6 +149,21 @@ RSpec.describe User do
it { is_expected.to have_many(:project_callouts).class_name('Users::ProjectCallout') }
it { is_expected.to have_many(:created_projects).dependent(:nullify).class_name('Project') }
+ describe 'default values' do
+ let(:user) { described_class.new }
+
+ it { expect(user.admin).to be_falsey }
+ it { expect(user.external).to eq(Gitlab::CurrentSettings.user_default_external) }
+ it { expect(user.can_create_group).to eq(Gitlab::CurrentSettings.can_create_group) }
+ it { expect(user.can_create_team).to be_falsey }
+ it { expect(user.hide_no_ssh_key).to be_falsey }
+ it { expect(user.hide_no_password).to be_falsey }
+ it { expect(user.project_view).to eq('files') }
+ it { expect(user.notified_of_own_activity).to be_falsey }
+ it { expect(user.preferred_language).to eq(I18n.default_locale.to_s) }
+ it { expect(user.theme_id).to eq(described_class.gitlab_config.default_theme) }
+ end
+
describe '#user_detail' do
it 'does not persist `user_detail` by default' do
expect(create(:user).user_detail).not_to be_persisted
@@ -345,52 +363,33 @@ RSpec.describe User do
context 'check_password_weakness' do
let(:weak_password) { "qwertyuiop" }
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(block_weak_passwords: false)
- end
-
- it 'does not add an error when password is weak' do
- expect(Security::WeakPasswords).not_to receive(:weak_for_user?)
-
- user.password = weak_password
- expect(user).to be_valid
- end
+ it 'checks for password weakness when password changes' do
+ expect(Security::WeakPasswords).to receive(:weak_for_user?)
+ .with(weak_password, user).and_call_original
+ user.password = weak_password
+ expect(user).not_to be_valid
end
- context 'when feature flag is enabled' do
- before do
- stub_feature_flags(block_weak_passwords: true)
- end
-
- it 'checks for password weakness when password changes' do
- expect(Security::WeakPasswords).to receive(:weak_for_user?)
- .with(weak_password, user).and_call_original
- user.password = weak_password
- expect(user).not_to be_valid
- end
-
- it 'adds an error when password is weak' do
- user.password = weak_password
- expect(user).not_to be_valid
- expect(user.errors).to be_of_kind(:password, 'must not contain commonly used combinations of words and letters')
- end
+ it 'adds an error when password is weak' do
+ user.password = weak_password
+ expect(user).not_to be_valid
+ expect(user.errors).to be_of_kind(:password, 'must not contain commonly used combinations of words and letters')
+ end
- it 'is valid when password is not weak' do
- user.password = ::User.random_password
- expect(user).to be_valid
- end
+ it 'is valid when password is not weak' do
+ user.password = ::User.random_password
+ expect(user).to be_valid
+ end
- it 'is valid when weak password was already set' do
- user = build(:user, password: weak_password)
- user.save!(validate: false)
+ it 'is valid when weak password was already set' do
+ user = build(:user, password: weak_password)
+ user.save!(validate: false)
- expect(Security::WeakPasswords).not_to receive(:weak_for_user?)
+ expect(Security::WeakPasswords).not_to receive(:weak_for_user?)
- # Change an unrelated value
- user.name = "Example McExampleFace"
- expect(user).to be_valid
- end
+ # Change an unrelated value
+ user.name = "Example McExampleFace"
+ expect(user).to be_valid
end
end
end
@@ -417,7 +416,7 @@ RSpec.describe User do
end
it 'falls back to english when I18n.default_locale is not an available language' do
- I18n.default_locale = :kl
+ allow(I18n).to receive(:default_locale) { :kl }
default_preferred_language = user.send(:default_preferred_language)
expect(user.preferred_language).to eq default_preferred_language
@@ -1590,10 +1589,6 @@ RSpec.describe User do
let(:expired_confirmation_sent_at) { Date.today - described_class.confirm_within - 7.days }
let(:extant_confirmation_sent_at) { Date.today }
- before do
- allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(true)
- end
-
let(:user) do
create(:user, :unconfirmed, unconfirmed_email: 'test@gitlab.com').tap do |user|
user.update!(confirmation_sent_at: confirmation_sent_at)
@@ -3090,6 +3085,14 @@ RSpec.describe User do
expect(described_class.find_by_ssh_key_id(-1)).to be_nil
end
end
+
+ it 'does not return a signing-only key', :aggregate_failures do
+ signing_key = create(:key, usage_type: :signing, user: user)
+ auth_and_signing_key = create(:key, usage_type: :auth_and_signing, user: user)
+
+ expect(described_class.find_by_ssh_key_id(signing_key.id)).to be_nil
+ expect(described_class.find_by_ssh_key_id(auth_and_signing_key.id)).to eq(user)
+ end
end
shared_examples "find user by login" do
@@ -3209,6 +3212,7 @@ RSpec.describe User do
expect(described_class.find_by_full_path('unknown')).to eq(nil)
end
end
+
context 'with the follow_redirects option set to true' do
it 'returns nil' do
expect(described_class.find_by_full_path('unknown', follow_redirects: true)).to eq(nil)
@@ -4431,7 +4435,7 @@ RSpec.describe User do
shared_context '#ci_owned_runners' do
let(:user) { create(:user) }
- shared_examples :nested_groups_owner do
+ shared_examples 'nested groups owner' do
context 'when the user is the owner of a multi-level group' do
before do
set_permissions_for_users
@@ -4448,7 +4452,7 @@ RSpec.describe User do
end
end
- shared_examples :group_owner do
+ shared_examples 'group owner' do
context 'when the user is the owner of a one level group' do
before do
group.add_owner(user)
@@ -4464,7 +4468,7 @@ RSpec.describe User do
end
end
- shared_examples :project_owner do
+ shared_examples 'project owner' do
context 'when the user is the owner of a project' do
it 'loads the runner belonging to the project' do
expect(user.ci_owned_runners).to contain_exactly(runner)
@@ -4476,7 +4480,7 @@ RSpec.describe User do
end
end
- shared_examples :project_member do
+ shared_examples 'project member' do
context 'when the user is a maintainer' do
before do
add_user(:maintainer)
@@ -4534,7 +4538,7 @@ RSpec.describe User do
end
end
- shared_examples :group_member do
+ shared_examples 'group member' do
context 'when the user is a maintainer' do
before do
add_user(:maintainer)
@@ -4607,7 +4611,7 @@ RSpec.describe User do
let!(:project) { create(:project, namespace: namespace) }
let!(:runner) { create(:ci_runner, :project, projects: [project]) }
- it_behaves_like :project_owner
+ it_behaves_like 'project owner'
end
context 'with group runner in a non owned group' do
@@ -4618,14 +4622,14 @@ RSpec.describe User do
group.add_member(user, access)
end
- it_behaves_like :group_member
+ it_behaves_like 'group member'
end
context 'with group runner in an owned group' do
let!(:group) { create(:group) }
let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
- it_behaves_like :group_owner
+ it_behaves_like 'group owner'
end
context 'with group runner in an owned group and group runner in a different owner subgroup' do
@@ -4640,7 +4644,7 @@ RSpec.describe User do
subgroup.add_owner(another_user)
end
- it_behaves_like :nested_groups_owner
+ it_behaves_like 'nested groups owner'
end
context 'with personal project runner in an an owned group and a group runner in that same group' do
@@ -4653,7 +4657,7 @@ RSpec.describe User do
group.add_owner(user)
end
- it_behaves_like :nested_groups_owner
+ it_behaves_like 'nested groups owner'
end
context 'with personal project runner in an owned group and a group runner in a subgroup' do
@@ -4667,7 +4671,7 @@ RSpec.describe User do
group.add_owner(user)
end
- it_behaves_like :nested_groups_owner
+ it_behaves_like 'nested groups owner'
end
context 'with personal project runner in an owned group in an owned namespace and a group runner in that group' do
@@ -4681,7 +4685,7 @@ RSpec.describe User do
group.add_owner(user)
end
- it_behaves_like :nested_groups_owner
+ it_behaves_like 'nested groups owner'
end
context 'with personal project runner in an owned namespace, an owned group, a subgroup and a group runner in that subgroup' do
@@ -4696,7 +4700,7 @@ RSpec.describe User do
group.add_owner(user)
end
- it_behaves_like :nested_groups_owner
+ it_behaves_like 'nested groups owner'
end
context 'with a project runner that belong to projects that belong to a not owned group' do
@@ -4708,7 +4712,7 @@ RSpec.describe User do
project.add_member(user, access)
end
- it_behaves_like :project_member
+ it_behaves_like 'project member'
end
context 'with project runners that belong to projects that do not belong to any group' do
@@ -4731,7 +4735,7 @@ RSpec.describe User do
group.add_member(another_user, :owner)
end
- it_behaves_like :group_member
+ it_behaves_like 'group member'
end
end
@@ -5221,6 +5225,10 @@ RSpec.describe User do
describe '#invalidate_issue_cache_counts' do
let(:user) { build_stubbed(:user) }
+ before do
+ stub_feature_flags(limit_assigned_issues_count: false)
+ end
+
it 'invalidates cache for issue counter' do
cache_mock = double
@@ -5230,6 +5238,23 @@ RSpec.describe User do
user.invalidate_issue_cache_counts
end
+
+ context 'when limit_assigned_issues_count is enabled' do
+ before do
+ stub_feature_flags(limit_assigned_issues_count: true)
+ end
+
+ it 'invalidates cache for issue counter' do
+ cache_mock = double
+
+ expect(cache_mock).to receive(:delete).with(['users', user.id, 'assigned_open_issues_count'])
+ expect(cache_mock).to receive(:delete).with(['users', user.id, 'max_assigned_open_issues_count'])
+
+ allow(Rails).to receive(:cache).and_return(cache_mock)
+
+ user.invalidate_issue_cache_counts
+ end
+ end
end
describe '#invalidate_merge_request_cache_counts' do
@@ -6155,33 +6180,44 @@ RSpec.describe User do
describe '#notification_email_for' do
let(:user) { create(:user) }
- let(:group) { create(:group) }
- subject { user.notification_email_for(group) }
+ subject { user.notification_email_for(namespace) }
- context 'when group is nil' do
- let(:group) { nil }
+ context 'when namespace is nil' do
+ let(:namespace) { nil }
it 'returns global notification email' do
is_expected.to eq(user.notification_email_or_default)
end
end
- context 'when group has no notification email set' do
- it 'returns global notification email' do
- create(:notification_setting, user: user, source: group, notification_email: '')
+ context 'for group namespace' do
+ let(:namespace) { create(:group) }
- is_expected.to eq(user.notification_email_or_default)
+ context 'when group has no notification email set' do
+ it 'returns global notification email' do
+ create(:notification_setting, user: user, source: namespace, notification_email: '')
+
+ is_expected.to eq(user.notification_email_or_default)
+ end
+ end
+
+ context 'when group has notification email set' do
+ it 'returns group notification email' do
+ group_notification_email = 'user+group@example.com'
+ create(:email, :confirmed, user: user, email: group_notification_email)
+ create(:notification_setting, user: user, source: namespace, notification_email: group_notification_email)
+
+ is_expected.to eq(group_notification_email)
+ end
end
end
- context 'when group has notification email set' do
- it 'returns group notification email' do
- group_notification_email = 'user+group@example.com'
- create(:email, :confirmed, user: user, email: group_notification_email)
- create(:notification_setting, user: user, source: group, notification_email: group_notification_email)
+ context 'for user namespace' do
+ let(:namespace) { create(:user_namespace) }
- is_expected.to eq(group_notification_email)
+ it 'returns global notification email' do
+ is_expected.to eq(user.notification_email_or_default)
end
end
end
@@ -6799,7 +6835,8 @@ RSpec.describe User do
{ user_type: :alert_bot },
{ user_type: :support_bot },
{ user_type: :security_bot },
- { user_type: :automation_bot }
+ { user_type: :automation_bot },
+ { user_type: :admin_bot }
]
end
@@ -6881,11 +6918,12 @@ RSpec.describe User do
using RSpec::Parameterized::TableSyntax
where(:user_type, :expected_result) do
- 'human' | true
- 'alert_bot' | false
- 'support_bot' | false
- 'security_bot' | false
- 'automation_bot' | false
+ 'human' | true
+ 'alert_bot' | false
+ 'support_bot' | false
+ 'security_bot' | false
+ 'automation_bot' | false
+ 'admin_bot' | false
end
with_them do
@@ -7034,17 +7072,26 @@ RSpec.describe User do
it_behaves_like 'bot users', :security_bot
it_behaves_like 'bot users', :ghost
it_behaves_like 'bot users', :automation_bot
+ it_behaves_like 'bot users', :admin_bot
it_behaves_like 'bot user avatars', :alert_bot, 'alert-bot.png'
it_behaves_like 'bot user avatars', :support_bot, 'support-bot.png'
it_behaves_like 'bot user avatars', :security_bot, 'security-bot.png'
it_behaves_like 'bot user avatars', :automation_bot, 'support-bot.png'
+ it_behaves_like 'bot user avatars', :admin_bot, 'admin-bot.png'
context 'when bot is the support_bot' do
subject { described_class.support_bot }
it { is_expected.to be_confirmed }
end
+
+ context 'when bot is the admin bot' do
+ subject { described_class.admin_bot }
+
+ it { is_expected.to be_admin }
+ it { is_expected.to be_confirmed }
+ end
end
describe '#confirmation_required_on_sign_in?' do
@@ -7307,4 +7354,51 @@ RSpec.describe User do
expect(user.account_age_in_days).to be(1)
end
end
+
+ describe 'state machine and default attributes' do
+ let(:model) do
+ Class.new(ApplicationRecord) do
+ self.table_name = User.table_name
+
+ attribute :external, default: -> { 1 / 0 }
+
+ state_machine :state, initial: :active do
+ end
+ end
+ end
+
+ it 'raises errors by default' do
+ expect { model }.to raise_error(ZeroDivisionError)
+ end
+
+ context 'with state machine default attributes override' do
+ let(:model) do
+ Class.new(ApplicationRecord) do
+ self.table_name = User.table_name
+
+ attribute :external, default: -> { 1 / 0 }
+
+ state_machine :state, initial: :active do
+ def owner_class_attribute_default; end
+ end
+ end
+ end
+
+ it 'does not raise errors' do
+ expect { model }.not_to raise_error
+ end
+
+ it 'raises errors when default attributes are used' do
+ expect { model.new.attributes }.to raise_error(ZeroDivisionError)
+ end
+
+ it 'does not evaluate default attributes when values are provided' do
+ expect { model.new(external: false).attributes }.not_to raise_error
+ end
+
+ it 'sets the state machine default value' do
+ expect(model.new(external: true).state).to eq('active')
+ end
+ end
+ end
end
diff --git a/spec/models/users/phone_number_validation_spec.rb b/spec/models/users/phone_number_validation_spec.rb
index 2f0fd1d3ac9..7ab461a4346 100644
--- a/spec/models/users/phone_number_validation_spec.rb
+++ b/spec/models/users/phone_number_validation_spec.rb
@@ -78,4 +78,42 @@ RSpec.describe Users::PhoneNumberValidation do
it { is_expected.to eq(false) }
end
end
+
+ describe '#for_user' do
+ let_it_be(:user_1) { create(:user) }
+ let_it_be(:user_2) { create(:user) }
+
+ let_it_be(:phone_number_record_1) { create(:phone_number_validation, user: user_1) }
+ let_it_be(:phone_number_record_2) { create(:phone_number_validation, user: user_2) }
+
+ context 'when multiple records exist for multiple users' do
+ it 'returns the correct phone number record for user' do
+ records = described_class.for_user(user_1.id)
+
+ expect(records.count).to be(1)
+ expect(records.first).to eq(phone_number_record_1)
+ end
+ end
+ end
+
+ describe '#validated?' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:phone_number_record) { create(:phone_number_validation, user: user) }
+
+ context 'when phone number record is not validated' do
+ it 'returns false' do
+ expect(phone_number_record.validated?).to be(false)
+ end
+ end
+
+ context 'when phone number record is validated' do
+ before do
+ phone_number_record.update!(validated_at: Time.now.utc)
+ end
+
+ it 'returns true' do
+ expect(phone_number_record.validated?).to be(true)
+ end
+ end
+ end
end
diff --git a/spec/models/work_item_spec.rb b/spec/models/work_item_spec.rb
index 341f9a9c60f..1c34936c5c2 100644
--- a/spec/models/work_item_spec.rb
+++ b/spec/models/work_item_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItem do
+RSpec.describe WorkItem, feature_category: :portfolio_management do
let_it_be(:reusable_project) { create(:project) }
describe 'associations' do
@@ -176,4 +176,59 @@ RSpec.describe WorkItem do
end
end
end
+
+ context 'with hierarchy' do
+ let_it_be(:type1) { create(:work_item_type, namespace: reusable_project.namespace) }
+ let_it_be(:type2) { create(:work_item_type, namespace: reusable_project.namespace) }
+ let_it_be(:type3) { create(:work_item_type, namespace: reusable_project.namespace) }
+ let_it_be(:type4) { create(:work_item_type, namespace: reusable_project.namespace) }
+ let_it_be(:hierarchy_restriction1) { create(:hierarchy_restriction, parent_type: type1, child_type: type2) }
+ let_it_be(:hierarchy_restriction2) { create(:hierarchy_restriction, parent_type: type2, child_type: type2) }
+ let_it_be(:hierarchy_restriction3) { create(:hierarchy_restriction, parent_type: type2, child_type: type3) }
+ let_it_be(:hierarchy_restriction4) { create(:hierarchy_restriction, parent_type: type3, child_type: type3) }
+ let_it_be(:hierarchy_restriction5) { create(:hierarchy_restriction, parent_type: type3, child_type: type4) }
+ let_it_be(:item1) { create(:work_item, work_item_type: type1, project: reusable_project) }
+ let_it_be(:item2_1) { create(:work_item, work_item_type: type2, project: reusable_project) }
+ let_it_be(:item2_2) { create(:work_item, work_item_type: type2, project: reusable_project) }
+ let_it_be(:item3_1) { create(:work_item, work_item_type: type3, project: reusable_project) }
+ let_it_be(:item3_2) { create(:work_item, work_item_type: type3, project: reusable_project) }
+ let_it_be(:item4) { create(:work_item, work_item_type: type4, project: reusable_project) }
+ let_it_be(:ignored_ancestor) { create(:work_item, work_item_type: type1, project: reusable_project) }
+ let_it_be(:ignored_descendant) { create(:work_item, work_item_type: type4, project: reusable_project) }
+ let_it_be(:link1) { create(:parent_link, work_item_parent: item1, work_item: item2_1) }
+ let_it_be(:link2) { create(:parent_link, work_item_parent: item2_1, work_item: item2_2) }
+ let_it_be(:link3) { create(:parent_link, work_item_parent: item2_2, work_item: item3_1) }
+ let_it_be(:link4) { create(:parent_link, work_item_parent: item3_1, work_item: item3_2) }
+ let_it_be(:link5) { create(:parent_link, work_item_parent: item3_2, work_item: item4) }
+
+ describe '#ancestors' do
+ it 'returns all ancestors in ascending order' do
+ expect(item3_1.ancestors).to eq([item2_2, item2_1, item1])
+ end
+
+ it 'returns an empty array if there are no ancestors' do
+ expect(item1.ancestors).to be_empty
+ end
+ end
+
+ describe '#same_type_base_and_ancestors' do
+ it 'returns self and all ancestors of the same type in ascending order' do
+ expect(item3_2.same_type_base_and_ancestors).to eq([item3_2, item3_1])
+ end
+
+ it 'returns self if there are no ancestors of the same type' do
+ expect(item3_1.same_type_base_and_ancestors).to match_array([item3_1])
+ end
+ end
+
+ describe '#same_type_descendants_depth' do
+ it 'returns max descendants depth including self' do
+ expect(item3_1.same_type_descendants_depth).to eq(2)
+ end
+
+ it 'returns 1 if there are no descendants' do
+ expect(item1.same_type_descendants_depth).to eq(1)
+ end
+ end
+ end
end
diff --git a/spec/models/work_items/hierarchy_restriction_spec.rb b/spec/models/work_items/hierarchy_restriction_spec.rb
new file mode 100644
index 00000000000..2c4d5d32fb8
--- /dev/null
+++ b/spec/models/work_items/hierarchy_restriction_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WorkItems::HierarchyRestriction do
+ describe 'associations' do
+ it { is_expected.to belong_to(:parent_type) }
+ it { is_expected.to belong_to(:child_type) }
+ end
+
+ describe 'validations' do
+ subject { build(:hierarchy_restriction) }
+
+ it { is_expected.to validate_presence_of(:parent_type) }
+ it { is_expected.to validate_presence_of(:child_type) }
+ it { is_expected.to validate_uniqueness_of(:child_type).scoped_to(:parent_type_id) }
+ end
+end
diff --git a/spec/models/work_items/parent_link_spec.rb b/spec/models/work_items/parent_link_spec.rb
index 070b2eef86a..82e79e8fbdf 100644
--- a/spec/models/work_items/parent_link_spec.rb
+++ b/spec/models/work_items/parent_link_spec.rb
@@ -2,7 +2,9 @@
require 'spec_helper'
-RSpec.describe WorkItems::ParentLink do
+RSpec.describe WorkItems::ParentLink, feature_category: :portfolio_management do
+ let_it_be(:project) { create(:project) }
+
describe 'associations' do
it { is_expected.to belong_to(:work_item) }
it { is_expected.to belong_to(:work_item_parent).class_name('WorkItem') }
@@ -16,7 +18,6 @@ RSpec.describe WorkItems::ParentLink do
it { is_expected.to validate_uniqueness_of(:work_item) }
describe 'hierarchy' do
- let_it_be(:project) { create(:project) }
let_it_be(:issue) { build(:work_item, project: project) }
let_it_be(:incident) { build(:work_item, :incident, project: project) }
let_it_be(:task1) { build(:work_item, :task, project: project) }
@@ -30,18 +31,82 @@ RSpec.describe WorkItems::ParentLink do
expect(build(:parent_link, work_item: task1, work_item_parent: incident)).to be_valid
end
- it 'is not valid if child is not task' do
- link = build(:parent_link, work_item: issue)
+ context 'when assigning to various parent types' do
+ using RSpec::Parameterized::TableSyntax
- expect(link).not_to be_valid
- expect(link.errors[:work_item]).to include('only Task can be assigned as a child in hierarchy.')
+ where(:parent_type_sym, :child_type_sym, :is_valid) do
+ :issue | :task | true
+ :incident | :task | true
+ :task | :issue | false
+ :issue | :issue | false
+ :objective | :objective | true
+ :objective | :key_result | true
+ :key_result | :objective | false
+ :key_result | :key_result | false
+ :objective | :issue | false
+ :task | :objective | false
+ end
+
+ with_them do
+ it 'validates if child can be added to the parent' do
+ parent_type = WorkItems::Type.default_by_type(parent_type_sym)
+ child_type = WorkItems::Type.default_by_type(child_type_sym)
+ parent = build(:work_item, issue_type: parent_type_sym, work_item_type: parent_type, project: project)
+ child = build(:work_item, issue_type: child_type_sym, work_item_type: child_type, project: project)
+ link = build(:parent_link, work_item: child, work_item_parent: parent)
+
+ expect(link.valid?).to eq(is_valid)
+ end
+ end
end
- it 'is not valid if parent is task' do
- link = build(:parent_link, work_item_parent: task1)
+ context 'with nested ancestors' do
+ let_it_be(:type1) { create(:work_item_type, namespace: project.namespace) }
+ let_it_be(:type2) { create(:work_item_type, namespace: project.namespace) }
+ let_it_be(:item1) { create(:work_item, work_item_type: type1, project: project) }
+ let_it_be(:item2) { create(:work_item, work_item_type: type2, project: project) }
+ let_it_be(:item3) { create(:work_item, work_item_type: type2, project: project) }
+ let_it_be(:item4) { create(:work_item, work_item_type: type2, project: project) }
+ let_it_be(:hierarchy_restriction1) { create(:hierarchy_restriction, parent_type: type1, child_type: type2) }
+ let_it_be(:hierarchy_restriction2) { create(:hierarchy_restriction, parent_type: type2, child_type: type1) }
+
+ let_it_be(:hierarchy_restriction3) do
+ create(:hierarchy_restriction, parent_type: type2, child_type: type2, maximum_depth: 2)
+ end
- expect(link).not_to be_valid
- expect(link.errors[:work_item_parent]).to include('only Issue and Incident can be parent of Task.')
+ let_it_be(:link1) { create(:parent_link, work_item_parent: item1, work_item: item2) }
+ let_it_be(:link2) { create(:parent_link, work_item_parent: item3, work_item: item4) }
+
+ describe '#validate_depth' do
+ it 'is valid if depth is in limit' do
+ link = build(:parent_link, work_item_parent: item1, work_item: item3)
+
+ expect(link).to be_valid
+ end
+
+ it 'is not valid when maximum depth is reached' do
+ link = build(:parent_link, work_item_parent: item2, work_item: item3)
+
+ expect(link).not_to be_valid
+ expect(link.errors[:work_item]).to include('reached maximum depth')
+ end
+ end
+
+ describe '#validate_cyclic_reference' do
+ it 'is not valid if parent and child are same' do
+ link1.work_item_parent = item2
+
+ expect(link1).not_to be_valid
+ expect(link1.errors[:work_item]).to include('is not allowed to point to itself')
+ end
+
+ it 'is not valid if child is already in ancestors' do
+ link = build(:parent_link, work_item_parent: item4, work_item: item3)
+
+ expect(link).not_to be_valid
+ expect(link.errors[:work_item]).to include('is already present in ancestors')
+ end
+ end
end
it 'is not valid if parent is in other project' do
@@ -96,8 +161,26 @@ RSpec.describe WorkItems::ParentLink do
end
end
- context 'with confidential work items' do
+ describe 'scopes' do
let_it_be(:project) { create(:project) }
+ let_it_be(:issue1) { build(:work_item, project: project) }
+ let_it_be(:issue2) { build(:work_item, project: project) }
+ let_it_be(:issue3) { build(:work_item, project: project) }
+ let_it_be(:task1) { build(:work_item, :task, project: project) }
+ let_it_be(:task2) { build(:work_item, :task, project: project) }
+ let_it_be(:link1) { create(:parent_link, work_item_parent: issue1, work_item: task1) }
+ let_it_be(:link2) { create(:parent_link, work_item_parent: issue2, work_item: task2) }
+
+ describe 'for_parents' do
+ it 'includes the correct records' do
+ result = described_class.for_parents([issue1.id, issue2.id, issue3.id])
+
+ expect(result).to include(link1, link2)
+ end
+ end
+ end
+
+ context 'with confidential work items' do
let_it_be(:confidential_child) { create(:work_item, :task, confidential: true, project: project) }
let_it_be(:putlic_child) { create(:work_item, :task, project: project) }
let_it_be(:confidential_parent) { create(:work_item, confidential: true, project: project) }
diff --git a/spec/models/work_items/type_spec.rb b/spec/models/work_items/type_spec.rb
index 6685720778a..1ada783385e 100644
--- a/spec/models/work_items/type_spec.rb
+++ b/spec/models/work_items/type_spec.rb
@@ -43,7 +43,7 @@ RSpec.describe WorkItems::Type do
end
it 'does not delete type when there are related issues' do
- type = create(:work_item_type, work_items: [work_item])
+ type = work_item.work_item_type
expect { type.destroy! }.to raise_error(ActiveRecord::InvalidForeignKey)
expect(Issue.count).to eq(1)
@@ -70,11 +70,38 @@ RSpec.describe WorkItems::Type do
::WorkItems::Widgets::Labels,
::WorkItems::Widgets::Assignees,
::WorkItems::Widgets::StartAndDueDate,
- ::WorkItems::Widgets::Milestone
+ ::WorkItems::Widgets::Milestone,
+ ::WorkItems::Widgets::Notes
)
end
end
+ describe '.default_by_type' do
+ let(:default_issue_type) { described_class.find_by(namespace_id: nil, base_type: :issue) }
+
+ subject { described_class.default_by_type(:issue) }
+
+ it 'returns default work item type by base type without calling importer' do
+ expect(Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter).not_to receive(:upsert_types)
+ expect(Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter).not_to receive(:upsert_restrictions)
+
+ expect(subject).to eq(default_issue_type)
+ end
+
+ context 'when default types are missing' do
+ before do
+ described_class.delete_all
+ end
+
+ it 'creates types and restrictions and returns default work item type by base type' do
+ expect(Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter).to receive(:upsert_types)
+ expect(Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter).to receive(:upsert_restrictions)
+
+ expect(subject).to eq(default_issue_type)
+ end
+ end
+ end
+
describe '#default?' do
subject { build(:work_item_type, namespace: namespace).default? }
diff --git a/spec/models/work_items/widgets/notes_spec.rb b/spec/models/work_items/widgets/notes_spec.rb
new file mode 100644
index 00000000000..cc98f1ebe54
--- /dev/null
+++ b/spec/models/work_items/widgets/notes_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WorkItems::Widgets::Notes, feature_category: :team_planning do
+ let_it_be(:work_item) { create(:work_item) }
+ let_it_be(:note) { create(:note, noteable: work_item, project: work_item.project) }
+
+ describe '.type' do
+ it { expect(described_class.type).to eq(:notes) }
+ end
+
+ describe '#type' do
+ it { expect(described_class.new(work_item).type).to eq(:notes) }
+ end
+
+ describe '#notes' do
+ it { expect(described_class.new(work_item).notes).to eq(work_item.notes) }
+ end
+end
diff --git a/spec/models/zoom_meeting_spec.rb b/spec/models/zoom_meeting_spec.rb
index 2b45533035d..d3d75a19fed 100644
--- a/spec/models/zoom_meeting_spec.rb
+++ b/spec/models/zoom_meeting_spec.rb
@@ -29,6 +29,7 @@ RSpec.describe ZoomMeeting do
expect(meetings_added).not_to include(removed_meeting.id)
end
end
+
describe '.removed_from_issue' do
it 'gets only removed meetings' do
meetings_removed = described_class.removed_from_issue.pluck(:id)
diff --git a/spec/policies/ci/runner_policy_spec.rb b/spec/policies/ci/runner_policy_spec.rb
index 773d3d9a01d..6039d60ec2f 100644
--- a/spec/policies/ci/runner_policy_spec.rb
+++ b/spec/policies/ci/runner_policy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::RunnerPolicy do
+RSpec.describe Ci::RunnerPolicy, feature_category: :runner do
describe 'ability :read_runner' do
let_it_be(:guest) { create(:user) }
let_it_be(:developer) { create(:user) }
diff --git a/spec/policies/concerns/archived_abilities_spec.rb b/spec/policies/concerns/archived_abilities_spec.rb
new file mode 100644
index 00000000000..8e3fd8a209f
--- /dev/null
+++ b/spec/policies/concerns/archived_abilities_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ArchivedAbilities, feature_category: :projects do
+ let(:test_class) do
+ Class.new do
+ include ArchivedAbilities
+ end
+ end
+
+ before do
+ stub_const('TestClass', test_class)
+ end
+
+ describe '.archived_abilities' do
+ it 'returns an array of abilites to be prevented when archived' do
+ expect(TestClass.archived_abilities).to include(*described_class::ARCHIVED_ABILITIES)
+ end
+ end
+
+ describe '.archived_features' do
+ it 'returns an array of features to be prevented when archived' do
+ expect(TestClass.archived_features).to include(*described_class::ARCHIVED_FEATURES)
+ end
+ end
+end
diff --git a/spec/policies/concerns/readonly_abilities_spec.rb b/spec/policies/concerns/readonly_abilities_spec.rb
deleted file mode 100644
index 864924a091d..00000000000
--- a/spec/policies/concerns/readonly_abilities_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe ReadonlyAbilities do
- let(:test_class) do
- Class.new do
- include ReadonlyAbilities
- end
- end
-
- before do
- stub_const('TestClass', test_class)
- end
-
- describe '.readonly_abilities' do
- it 'returns an array of abilites to be prevented when readonly' do
- expect(TestClass.readonly_abilities).to include(*described_class::READONLY_ABILITIES)
- end
- end
-
- describe '.readonly_features' do
- it 'returns an array of features to be prevented when readonly' do
- expect(TestClass.readonly_features).to include(*described_class::READONLY_FEATURES)
- end
- end
-end
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index 60acacac814..65abb43b6c4 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -3,6 +3,7 @@
require 'spec_helper'
RSpec.describe GroupPolicy do
+ include AdminModeHelper
include_context 'GroupPolicy context'
context 'public group with no user' do
@@ -1190,12 +1191,28 @@ RSpec.describe GroupPolicy do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:register_group_runners) }
+ context 'with specific group runner registration disabled' do
+ before do
+ group.runner_registration_enabled = false
+ end
+
+ it { is_expected.to be_allowed(:register_group_runners) }
+ end
+
context 'with group runner registration disabled' do
before do
stub_application_setting(valid_runner_registrars: ['project'])
end
it { is_expected.to be_allowed(:register_group_runners) }
+
+ context 'with specific group runner registration disabled' do
+ before do
+ group.runner_registration_enabled = false
+ end
+
+ it { is_expected.to be_allowed(:register_group_runners) }
+ end
end
end
@@ -1216,6 +1233,14 @@ RSpec.describe GroupPolicy do
it { is_expected.to be_disallowed(:register_group_runners) }
end
+
+ context 'with specific group runner registration disabled' do
+ before do
+ group.runner_registration_enabled = false
+ end
+
+ it { is_expected.to be_disallowed(:register_group_runners) }
+ end
end
context 'with maintainer' do
@@ -1344,4 +1369,32 @@ RSpec.describe GroupPolicy do
subject { described_class.new(current_user, group) }
end
+
+ describe 'read_usage_quotas policy' do
+ context 'reading usage quotas' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:policy) { :read_usage_quotas }
+
+ where(:role, :admin_mode, :allowed) do
+ :owner | nil | true
+ :admin | true | true
+ :admin | false | false
+ :maintainer | nil | false
+ :developer | nil | false
+ :reporter | nil | false
+ :guest | nil | false
+ end
+
+ with_them do
+ let(:current_user) { public_send(role) }
+
+ before do
+ enable_admin_mode!(current_user) if admin_mode
+ end
+
+ it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
+ end
+ end
+ end
end
diff --git a/spec/policies/issue_policy_spec.rb b/spec/policies/issue_policy_spec.rb
index c110ca705bd..905ef591b53 100644
--- a/spec/policies/issue_policy_spec.rb
+++ b/spec/policies/issue_policy_spec.rb
@@ -2,16 +2,19 @@
require 'spec_helper'
-RSpec.describe IssuePolicy do
+RSpec.describe IssuePolicy, feature_category: :team_planning do
include_context 'ProjectPolicyTable context'
include ExternalAuthorizationServiceHelpers
include ProjectHelpers
include UserHelpers
+ let(:admin) { create(:user, :admin) }
let(:guest) { create(:user) }
let(:author) { create(:user) }
let(:assignee) { create(:user) }
let(:reporter) { create(:user) }
+ let(:maintainer) { create(:user) }
+ let(:owner) { create(:user) }
let(:group) { create(:group, :public) }
let(:reporter_from_group_link) { create(:user) }
let(:non_member) { create(:user) }
@@ -197,6 +200,8 @@ RSpec.describe IssuePolicy do
before do
project.add_guest(guest)
project.add_reporter(reporter)
+ project.add_maintainer(maintainer)
+ project.add_owner(owner)
group.add_reporter(reporter_from_group_link)
@@ -305,7 +310,6 @@ RSpec.describe IssuePolicy do
let(:issue) { create(:issue, project: project, author: author) }
let(:visitor) { create(:user) }
- let(:admin) { create(:user, :admin) }
it 'forbids visitors from viewing issues' do
expect(permissions(visitor, issue)).to be_disallowed(:read_issue)
@@ -394,12 +398,15 @@ RSpec.describe IssuePolicy do
expect(permissions(assignee, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue, :set_issue_metadata, :set_confidentiality)
end
+
+ it 'allows admins to read confidential issues' do
+ expect(permissions(admin, confidential_issue)).to be_allowed(:read_issue)
+ end
end
context 'with a hidden issue' do
let(:user) { create(:user) }
let(:banned_user) { create(:user, :banned) }
- let(:admin) { create(:user, :admin) }
let(:hidden_issue) { create(:issue, project: project, author: banned_user) }
it 'does not allow non-admin user to read the issue' do
@@ -410,6 +417,37 @@ RSpec.describe IssuePolicy do
expect(permissions(admin, hidden_issue)).to be_allowed(:read_issue)
end
end
+
+ context 'when accounting for notes widget' do
+ let(:policy) { described_class.new(reporter, note) }
+
+ before do
+ widgets_per_type = WorkItems::Type::WIDGETS_FOR_TYPE.dup
+ widgets_per_type[:task] = [::WorkItems::Widgets::Description]
+ stub_const('WorkItems::Type::WIDGETS_FOR_TYPE', widgets_per_type)
+ end
+
+ context 'and notes widget is disabled for task' do
+ let(:task) { create(:work_item, :task, project: project) }
+
+ it 'does not allow accessing notes' do
+ # if notes widget is disabled not even maintainer can access notes
+ expect(permissions(maintainer, task)).to be_disallowed(:create_note, :read_note, :mark_note_as_confidential, :read_internal_note)
+ expect(permissions(admin, task)).to be_disallowed(:create_note, :read_note, :read_internal_note, :mark_note_as_confidential, :set_note_created_at)
+ end
+ end
+
+ context 'and notes widget is enabled for issue' do
+ it 'allows accessing notes' do
+ # with notes widget enabled, even guests can access notes
+ expect(permissions(guest, issue)).to be_allowed(:create_note, :read_note)
+ expect(permissions(guest, issue)).to be_disallowed(:read_internal_note, :mark_note_as_confidential, :set_note_created_at)
+ expect(permissions(reporter, issue)).to be_allowed(:create_note, :read_note, :read_internal_note, :mark_note_as_confidential)
+ expect(permissions(maintainer, issue)).to be_allowed(:create_note, :read_note, :read_internal_note, :mark_note_as_confidential)
+ expect(permissions(owner, issue)).to be_allowed(:create_note, :read_note, :read_internal_note, :mark_note_as_confidential, :set_note_created_at)
+ end
+ end
+ end
end
context 'with external authorization enabled' do
diff --git a/spec/policies/merge_request_policy_spec.rb b/spec/policies/merge_request_policy_spec.rb
index 7e1af132b1d..741a0db3009 100644
--- a/spec/policies/merge_request_policy_spec.rb
+++ b/spec/policies/merge_request_policy_spec.rb
@@ -10,6 +10,7 @@ RSpec.describe MergeRequestPolicy do
let_it_be(:reporter) { create(:user) }
let_it_be(:developer) { create(:user) }
let_it_be(:non_team_member) { create(:user) }
+ let_it_be(:bot) { create(:user, :project_bot) }
def permissions(user, merge_request)
described_class.new(user, merge_request)
@@ -72,6 +73,7 @@ RSpec.describe MergeRequestPolicy do
project.add_guest(guest)
project.add_guest(author)
project.add_developer(developer)
+ project.add_developer(bot)
end
context 'when merge request is public' do
@@ -95,6 +97,18 @@ RSpec.describe MergeRequestPolicy do
it do
is_expected.to be_allowed(:approve_merge_request)
end
+
+ it do
+ is_expected.to be_disallowed(:reset_merge_request_approvals)
+ end
+ end
+
+ context 'and the user is a bot' do
+ let(:user) { bot }
+
+ it do
+ is_expected.to be_allowed(:reset_merge_request_approvals)
+ end
end
end
end
@@ -123,6 +137,14 @@ RSpec.describe MergeRequestPolicy do
it_behaves_like 'a denied user'
end
+
+ describe 'a bot' do
+ let(:subject) { permissions(bot, merge_request) }
+
+ it do
+ is_expected.to be_disallowed(:reset_merge_request_approvals)
+ end
+ end
end
context 'when merge requests are private' do
@@ -144,6 +166,14 @@ RSpec.describe MergeRequestPolicy do
it_behaves_like 'a user with full access'
end
+
+ describe 'a bot' do
+ let(:subject) { permissions(bot, merge_request) }
+
+ it do
+ is_expected.to be_allowed(:reset_merge_request_approvals)
+ end
+ end
end
context 'when merge request is unlocked' do
@@ -214,6 +244,7 @@ RSpec.describe MergeRequestPolicy do
group.add_guest(author)
group.add_reporter(reporter)
group.add_developer(developer)
+ group.add_developer(bot)
end
context 'when project is public' do
@@ -222,9 +253,25 @@ RSpec.describe MergeRequestPolicy do
describe 'the merge request author' do
subject { permissions(author, merge_request) }
- specify do
+ it do
is_expected.to be_allowed(:approve_merge_request)
end
+
+ it do
+ is_expected.to be_disallowed(:reset_merge_request_approvals)
+ end
+ end
+
+ describe 'a bot' do
+ subject { permissions(bot, merge_request) }
+
+ it do
+ is_expected.to be_allowed(:approve_merge_request)
+ end
+
+ it do
+ is_expected.to be_allowed(:reset_merge_request_approvals)
+ end
end
context 'and merge requests are private' do
@@ -250,6 +297,14 @@ RSpec.describe MergeRequestPolicy do
it_behaves_like 'a user with full access'
end
+
+ describe 'a bot' do
+ let(:subject) { permissions(bot, merge_request) }
+
+ it do
+ is_expected.to be_allowed(:reset_merge_request_approvals)
+ end
+ end
end
end
@@ -273,6 +328,14 @@ RSpec.describe MergeRequestPolicy do
it_behaves_like 'a user with full access'
end
+
+ describe 'a bot' do
+ let(:subject) { permissions(bot, merge_request) }
+
+ it do
+ is_expected.to be_allowed(:reset_merge_request_approvals)
+ end
+ end
end
end
@@ -297,11 +360,28 @@ RSpec.describe MergeRequestPolicy do
group_access: Gitlab::Access::DEVELOPER)
group.add_guest(non_team_member)
+ group.add_guest(bot)
end
- specify do
+ it do
is_expected.to be_allowed(:approve_merge_request)
end
+
+ it do
+ is_expected.to be_disallowed(:reset_merge_request_approvals)
+ end
+
+ context 'and the user is a bot' do
+ let(:user) { bot }
+
+ it do
+ is_expected.to be_allowed(:approve_merge_request)
+ end
+
+ it do
+ is_expected.to be_allowed(:reset_merge_request_approvals)
+ end
+ end
end
end
@@ -313,9 +393,25 @@ RSpec.describe MergeRequestPolicy do
subject { permissions(non_team_member, merge_request) }
- specify do
+ it do
is_expected.not_to be_allowed(:approve_merge_request)
end
+
+ it do
+ is_expected.not_to be_allowed(:reset_merge_request_approvals)
+ end
+
+ context 'and the user is a bot' do
+ subject { permissions(bot, merge_request) }
+
+ it do
+ is_expected.not_to be_allowed(:approve_merge_request)
+ end
+
+ it do
+ is_expected.not_to be_allowed(:reset_merge_request_approvals)
+ end
+ end
end
context 'when merge requests are disabled' do
diff --git a/spec/policies/namespaces/user_namespace_policy_spec.rb b/spec/policies/namespaces/user_namespace_policy_spec.rb
index 42d27d0f3d6..bb821490e30 100644
--- a/spec/policies/namespaces/user_namespace_policy_spec.rb
+++ b/spec/policies/namespaces/user_namespace_policy_spec.rb
@@ -35,6 +35,13 @@ RSpec.describe Namespaces::UserNamespacePolicy do
it { is_expected.to be_disallowed(:create_projects) }
it { is_expected.to be_disallowed(:transfer_projects) }
end
+
+ context 'bot user' do
+ let(:owner) { create(:user, :project_bot) }
+
+ it { is_expected.to be_disallowed(:create_projects) }
+ it { is_expected.to be_disallowed(:transfer_projects) }
+ end
end
context 'admin' do
diff --git a/spec/policies/note_policy_spec.rb b/spec/policies/note_policy_spec.rb
index 6a261b4ff5b..dcfc398806a 100644
--- a/spec/policies/note_policy_spec.rb
+++ b/spec/policies/note_policy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe NotePolicy do
+RSpec.describe NotePolicy, feature_category: :team_planning do
describe '#rules', :aggregate_failures do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
@@ -255,6 +255,31 @@ RSpec.describe NotePolicy do
it_behaves_like 'user can read the note'
end
+
+ context 'when notes widget is disabled for task' do
+ let(:policy) { described_class.new(developer, note) }
+
+ before do
+ widgets_per_type = WorkItems::Type::WIDGETS_FOR_TYPE.dup
+ widgets_per_type[:task] = [::WorkItems::Widgets::Description]
+ stub_const('WorkItems::Type::WIDGETS_FOR_TYPE', widgets_per_type)
+ end
+
+ context 'when noteable is task' do
+ let(:noteable) { create(:work_item, :task, project: project) }
+ let(:note) { create(:note, system: true, noteable: noteable, author: user, project: project) }
+
+ it_behaves_like 'user cannot read or act on the note'
+ end
+
+ context 'when noteable is issue' do
+ let(:noteable) { create(:work_item, :issue, project: project) }
+ let(:note) { create(:note, system: true, noteable: noteable, author: user, project: project) }
+
+ it_behaves_like 'user can read the note'
+ it_behaves_like 'user can act on the note'
+ end
+ end
end
context 'when it is a system note referencing a confidential issue' do
@@ -313,7 +338,7 @@ RSpec.describe NotePolicy do
end
it 'does not allow guests to read confidential notes and replies' do
- expect(permissions(guest, confidential_note)).to be_disallowed(:read_note, :admin_note, :reposition_note, :resolve_note, :award_emoji, :mark_note_as_confidential)
+ expect(permissions(guest, confidential_note)).to be_disallowed(:read_note, :read_internal_note, :admin_note, :reposition_note, :resolve_note, :award_emoji, :mark_note_as_confidential)
end
it 'allows reporter to read all notes but not resolve and admin them' do
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 973ed66b8d8..9b2d10283f1 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -1965,87 +1965,6 @@ RSpec.describe ProjectPolicy do
it_behaves_like 'Self-managed Core resource access tokens'
- describe 'operations feature' do
- using RSpec::Parameterized::TableSyntax
-
- let(:guest_permissions) { [:read_environment, :read_deployment] }
-
- let(:developer_permissions) do
- guest_permissions + [
- :read_feature_flag, :read_sentry_issue, :read_alert_management_alert, :read_terraform_state,
- :metrics_dashboard, :read_pod_logs, :read_prometheus, :create_feature_flag,
- :create_environment, :create_deployment, :update_feature_flag, :update_environment,
- :update_sentry_issue, :update_alert_management_alert, :update_deployment,
- :destroy_feature_flag, :destroy_environment, :admin_feature_flag
- ]
- end
-
- let(:maintainer_permissions) do
- developer_permissions + [
- :read_cluster, :create_cluster, :update_cluster, :admin_environment,
- :admin_cluster, :admin_terraform_state, :admin_deployment
- ]
- end
-
- before do
- stub_feature_flags(split_operations_visibility_permissions: false)
- end
-
- where(:project_visibility, :access_level, :role, :allowed) do
- :public | ProjectFeature::ENABLED | :maintainer | true
- :public | ProjectFeature::ENABLED | :developer | true
- :public | ProjectFeature::ENABLED | :guest | true
- :public | ProjectFeature::ENABLED | :anonymous | true
- :public | ProjectFeature::PRIVATE | :maintainer | true
- :public | ProjectFeature::PRIVATE | :developer | true
- :public | ProjectFeature::PRIVATE | :guest | true
- :public | ProjectFeature::PRIVATE | :anonymous | false
- :public | ProjectFeature::DISABLED | :maintainer | false
- :public | ProjectFeature::DISABLED | :developer | false
- :public | ProjectFeature::DISABLED | :guest | false
- :public | ProjectFeature::DISABLED | :anonymous | false
- :internal | ProjectFeature::ENABLED | :maintainer | true
- :internal | ProjectFeature::ENABLED | :developer | true
- :internal | ProjectFeature::ENABLED | :guest | true
- :internal | ProjectFeature::ENABLED | :anonymous | false
- :internal | ProjectFeature::PRIVATE | :maintainer | true
- :internal | ProjectFeature::PRIVATE | :developer | true
- :internal | ProjectFeature::PRIVATE | :guest | true
- :internal | ProjectFeature::PRIVATE | :anonymous | false
- :internal | ProjectFeature::DISABLED | :maintainer | false
- :internal | ProjectFeature::DISABLED | :developer | false
- :internal | ProjectFeature::DISABLED | :guest | false
- :internal | ProjectFeature::DISABLED | :anonymous | false
- :private | ProjectFeature::ENABLED | :maintainer | true
- :private | ProjectFeature::ENABLED | :developer | true
- :private | ProjectFeature::ENABLED | :guest | false
- :private | ProjectFeature::ENABLED | :anonymous | false
- :private | ProjectFeature::PRIVATE | :maintainer | true
- :private | ProjectFeature::PRIVATE | :developer | true
- :private | ProjectFeature::PRIVATE | :guest | false
- :private | ProjectFeature::PRIVATE | :anonymous | false
- :private | ProjectFeature::DISABLED | :maintainer | false
- :private | ProjectFeature::DISABLED | :developer | false
- :private | ProjectFeature::DISABLED | :guest | false
- :private | ProjectFeature::DISABLED | :anonymous | false
- end
-
- with_them do
- let(:current_user) { user_subject(role) }
- let(:project) { project_subject(project_visibility) }
-
- it 'allows/disallows the abilities based on the operation feature access level' do
- project.project_feature.update!(operations_access_level: access_level)
-
- if allowed
- expect_allowed(*permissions_abilities(role))
- else
- expect_disallowed(*permissions_abilities(role))
- end
- end
- end
- end
-
describe 'environments feature' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/presenters/blob_presenter_spec.rb b/spec/presenters/blob_presenter_spec.rb
index 88dafb7ea1f..f8cba8e9203 100644
--- a/spec/presenters/blob_presenter_spec.rb
+++ b/spec/presenters/blob_presenter_spec.rb
@@ -118,7 +118,7 @@ RSpec.describe BlobPresenter do
end
describe '#permalink_path' do
- it { expect(presenter.permalink_path).to eq("/#{project.full_path}/-/blob/#{project.repository.commit.sha}/files/ruby/regex.rb") }
+ it { expect(presenter.permalink_path).to eq("/#{project.full_path}/-/blob/#{project.repository.commit(blob.commit_id).sha}/files/ruby/regex.rb") }
end
context 'environment has been deployed' do
diff --git a/spec/presenters/ci/freeze_period_presenter_spec.rb b/spec/presenters/ci/freeze_period_presenter_spec.rb
new file mode 100644
index 00000000000..e9959540b8d
--- /dev/null
+++ b/spec/presenters/ci/freeze_period_presenter_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::FreezePeriodPresenter, feature_category: :release_orchestration do
+ let_it_be(:project) { build_stubbed(:project) }
+
+ let(:presenter) { described_class.new(freeze_period) }
+
+ describe '#start_time' do
+ let(:freeze_period) { build_stubbed(:ci_freeze_period, project: project) }
+
+ context 'when active' do
+ # Default freeze period factory is on a weekend, so let's travel in time to a Saturday!
+ let(:time) { Time.utc(2022, 12, 3, 6) }
+ let(:previous_start) { Time.utc(2022, 12, 2, 23) }
+
+ it 'returns the previous time of the freeze period start' do
+ travel_to(time) do
+ expect(presenter.start_time).to eq(previous_start)
+ end
+ end
+ end
+
+ context 'when inactive' do
+ # Default freeze period factory is on a weekend, so we travel back a couple of days earlier.
+ let(:time) { Time.utc(2022, 11, 30, 6) }
+ let(:next_start) { Time.utc(2022, 12, 2, 23) }
+
+ it 'returns the next time of the freeze period start' do
+ travel_to(time) do
+ expect(presenter.start_time).to eq(next_start)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/presenters/group_member_presenter_spec.rb b/spec/presenters/group_member_presenter_spec.rb
index 352f81356e0..25992871160 100644
--- a/spec/presenters/group_member_presenter_spec.rb
+++ b/spec/presenters/group_member_presenter_spec.rb
@@ -54,6 +54,24 @@ RSpec.describe GroupMemberPresenter do
end
end
+ describe '#last_owner?' do
+ context 'when member is the last owner of the group' do
+ before do
+ allow(group_member).to receive(:last_owner_of_the_group?).and_return(true)
+ end
+
+ it { expect(presenter.last_owner?).to eq(true) }
+ end
+
+ context 'when member is not the last owner of the group' do
+ before do
+ allow(group_member).to receive(:last_owner_of_the_group?).and_return(false)
+ end
+
+ it { expect(presenter.last_owner?).to eq(false) }
+ end
+ end
+
describe '#can_update?' do
context 'when user can update_group_member' do
before do
diff --git a/spec/presenters/member_presenter_spec.rb b/spec/presenters/member_presenter_spec.rb
new file mode 100644
index 00000000000..65e23d20051
--- /dev/null
+++ b/spec/presenters/member_presenter_spec.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe MemberPresenter, feature_category: :subgroups do
+ let_it_be(:member) { build(:group_member) }
+ let(:presenter) { described_class.new(member) }
+
+ describe '#last_owner?' do
+ it 'raises `NotImplementedError`' do
+ expect { presenter.last_owner? }.to raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/presenters/packages/pypi/simple_package_versions_presenter_spec.rb b/spec/presenters/packages/pypi/simple_package_versions_presenter_spec.rb
index c966b1fc8e1..50a5cb631f9 100644
--- a/spec/presenters/packages/pypi/simple_package_versions_presenter_spec.rb
+++ b/spec/presenters/packages/pypi/simple_package_versions_presenter_spec.rb
@@ -36,6 +36,14 @@ RSpec.describe ::Packages::Pypi::SimplePackageVersionsPresenter, :aggregate_fail
it { is_expected.to include expected_link }
end
+
+ it 'avoids N+1 database queries' do
+ control = ActiveRecord::QueryRecorder.new { subject }
+
+ create(:pypi_package, project: project, name: package_name)
+
+ expect { described_class.new(project.packages, project_or_group).body }.not_to exceed_query_limit(control)
+ end
end
context 'for project' do
diff --git a/spec/presenters/project_member_presenter_spec.rb b/spec/presenters/project_member_presenter_spec.rb
index 1cfc8cfb53b..28afb78cdce 100644
--- a/spec/presenters/project_member_presenter_spec.rb
+++ b/spec/presenters/project_member_presenter_spec.rb
@@ -54,6 +54,24 @@ RSpec.describe ProjectMemberPresenter do
end
end
+ describe '#last_owner?' do
+ context 'when member is the holder of the personal namespace' do
+ before do
+ allow(project_member).to receive(:holder_of_the_personal_namespace?).and_return(true)
+ end
+
+ it { expect(presenter.last_owner?).to eq(true) }
+ end
+
+ context 'when member is not the holder of the personal namespace' do
+ before do
+ allow(project_member).to receive(:holder_of_the_personal_namespace?).and_return(false)
+ end
+
+ it { expect(presenter.last_owner?).to eq(false) }
+ end
+ end
+
describe '#can_update?' do
context 'when user is NOT attempting to update an Owner' do
before do
diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb
index c32cc87afbb..4c2b87f34a1 100644
--- a/spec/presenters/project_presenter_spec.rb
+++ b/spec/presenters/project_presenter_spec.rb
@@ -76,21 +76,21 @@ RSpec.describe ProjectPresenter do
let_it_be(:project) { create(:project, :public, :repository) }
it 'returns files and readme if user has repository access' do
- allow(presenter).to receive(:can?).with(nil, :download_code, project).and_return(true)
+ allow(presenter).to receive(:can?).with(nil, :read_code, project).and_return(true)
expect(presenter.default_view).to eq('files')
end
it 'returns wiki if user does not have repository access and can read wiki, which exists' do
allow(project).to receive(:wiki_repository_exists?).and_return(true)
- allow(presenter).to receive(:can?).with(nil, :download_code, project).and_return(false)
+ allow(presenter).to receive(:can?).with(nil, :read_code, project).and_return(false)
allow(presenter).to receive(:can?).with(nil, :read_wiki, project).and_return(true)
expect(presenter.default_view).to eq('wiki')
end
it 'returns activity if user does not have repository or wiki access' do
- allow(presenter).to receive(:can?).with(nil, :download_code, project).and_return(false)
+ allow(presenter).to receive(:can?).with(nil, :read_code, project).and_return(false)
allow(presenter).to receive(:can?).with(nil, :read_issue, project).and_return(false)
allow(presenter).to receive(:can?).with(nil, :read_wiki, project).and_return(false)
@@ -117,7 +117,7 @@ RSpec.describe ProjectPresenter do
context 'when the user is allowed to see the code' do
it 'returns the project view' do
- allow(presenter).to receive(:can?).with(user, :download_code, project).and_return(true)
+ allow(presenter).to receive(:can?).with(user, :read_code, project).and_return(true)
expect(presenter.default_view).to eq('readme')
end
@@ -126,7 +126,7 @@ RSpec.describe ProjectPresenter do
context 'with wikis enabled and the right policy for the user' do
before do
project.project_feature.update_attribute(:issues_access_level, 0)
- allow(presenter).to receive(:can?).with(user, :download_code, project).and_return(false)
+ allow(presenter).to receive(:can?).with(user, :read_code, project).and_return(false)
end
it 'returns wiki if the user has the right policy and the wiki exists' do
@@ -146,7 +146,7 @@ RSpec.describe ProjectPresenter do
context 'with issues as a feature available' do
it 'return issues' do
- allow(presenter).to receive(:can?).with(user, :download_code, project).and_return(false)
+ allow(presenter).to receive(:can?).with(user, :read_code, project).and_return(false)
allow(presenter).to receive(:can?).with(user, :read_issue, project).and_return(true)
allow(presenter).to receive(:can?).with(user, :read_wiki, project).and_return(false)
@@ -157,7 +157,7 @@ RSpec.describe ProjectPresenter do
context 'with no activity, no wikies and no issues' do
it 'returns activity as default' do
project.project_feature.update_attribute(:issues_access_level, 0)
- allow(presenter).to receive(:can?).with(user, :download_code, project).and_return(false)
+ allow(presenter).to receive(:can?).with(user, :read_code, project).and_return(false)
allow(presenter).to receive(:can?).with(user, :read_wiki, project).and_return(false)
allow(presenter).to receive(:can?).with(user, :read_issue, project).and_return(false)
@@ -623,14 +623,6 @@ RSpec.describe ProjectPresenter do
context 'empty repo' do
let(:project) { create(:project, :stubbed_repository) }
- context 'for a guest user' do
- it 'orders the items correctly' do
- expect(empty_repo_statistics_buttons.map(&:label)).to start_with(
- a_string_including('No license')
- )
- end
- end
-
it 'includes a button to configure integrations for maintainers' do
project.add_maintainer(user)
diff --git a/spec/presenters/projects/security/configuration_presenter_spec.rb b/spec/presenters/projects/security/configuration_presenter_spec.rb
index ca7f96b567d..4fe459a798a 100644
--- a/spec/presenters/projects/security/configuration_presenter_spec.rb
+++ b/spec/presenters/projects/security/configuration_presenter_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Projects::Security::ConfigurationPresenter do
let(:presenter) { described_class.new(project, current_user: current_user) }
before do
- stub_licensed_features(licensed_scan_types.to_h { |type| [type, true] })
+ stub_licensed_features(licensed_scan_types.index_with { true })
end
describe '#to_html_data_attribute' do
diff --git a/spec/presenters/search_service_presenter_spec.rb b/spec/presenters/search_service_presenter_spec.rb
index af9fee8cfd9..a235f954366 100644
--- a/spec/presenters/search_service_presenter_spec.rb
+++ b/spec/presenters/search_service_presenter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe SearchServicePresenter do
+RSpec.describe SearchServicePresenter, feature_category: :global_search do
let(:user) { create(:user) }
let(:search) { '' }
let(:search_service) { SearchService.new(user, search: search, scope: scope) }
@@ -51,4 +51,10 @@ RSpec.describe SearchServicePresenter do
it { expect(presenter.show_results_status?).to eq(result) }
end
end
+
+ describe '#advanced_search_enabled?' do
+ let(:scope) { nil }
+
+ it { expect(presenter.advanced_search_enabled?).to eq(false) }
+ end
end
diff --git a/spec/requests/abuse_reports_controller_spec.rb b/spec/requests/abuse_reports_controller_spec.rb
index 94c80ccb89a..510855d95e0 100644
--- a/spec/requests/abuse_reports_controller_spec.rb
+++ b/spec/requests/abuse_reports_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AbuseReportsController do
+RSpec.describe AbuseReportsController, feature_category: :users do
let(:reporter) { create(:user) }
let(:user) { create(:user) }
let(:attrs) do
diff --git a/spec/requests/admin/applications_controller_spec.rb b/spec/requests/admin/applications_controller_spec.rb
index 03553757080..c83137ebbce 100644
--- a/spec/requests/admin/applications_controller_spec.rb
+++ b/spec/requests/admin/applications_controller_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Admin::ApplicationsController, :enable_admin_mode do
+RSpec.describe Admin::ApplicationsController, :enable_admin_mode,
+feature_category: :authentication_and_authorization do
let_it_be(:admin) { create(:admin) }
let_it_be(:application) { create(:oauth_application, owner_id: nil, owner_type: nil) }
let_it_be(:show_path) { admin_application_path(application) }
diff --git a/spec/requests/admin/background_migrations_controller_spec.rb b/spec/requests/admin/background_migrations_controller_spec.rb
index fe2a2470511..db3e2fa0df6 100644
--- a/spec/requests/admin/background_migrations_controller_spec.rb
+++ b/spec/requests/admin/background_migrations_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Admin::BackgroundMigrationsController, :enable_admin_mode do
+RSpec.describe Admin::BackgroundMigrationsController, :enable_admin_mode, feature_category: :database do
let(:admin) { create(:admin) }
before do
diff --git a/spec/requests/admin/batched_jobs_controller_spec.rb b/spec/requests/admin/batched_jobs_controller_spec.rb
index fb51b3bce88..8060b19b8b3 100644
--- a/spec/requests/admin/batched_jobs_controller_spec.rb
+++ b/spec/requests/admin/batched_jobs_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Admin::BatchedJobsController, :enable_admin_mode do
+RSpec.describe Admin::BatchedJobsController, :enable_admin_mode, feature_category: :database do
let(:admin) { create(:admin) }
before do
diff --git a/spec/requests/admin/broadcast_messages_controller_spec.rb b/spec/requests/admin/broadcast_messages_controller_spec.rb
index eb29092845c..69b84d6d795 100644
--- a/spec/requests/admin/broadcast_messages_controller_spec.rb
+++ b/spec/requests/admin/broadcast_messages_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Admin::BroadcastMessagesController, :enable_admin_mode do
+RSpec.describe Admin::BroadcastMessagesController, :enable_admin_mode, feature_category: :onboarding do
let(:broadcast_message) { build(:broadcast_message) }
let(:broadcast_message_params) { broadcast_message.as_json(root: true, only: [:message, :starts_at, :ends_at]) }
diff --git a/spec/requests/admin/clusters/integrations_controller_spec.rb b/spec/requests/admin/clusters/integrations_controller_spec.rb
index ee1c1d5aad4..d5e3f03627a 100644
--- a/spec/requests/admin/clusters/integrations_controller_spec.rb
+++ b/spec/requests/admin/clusters/integrations_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Admin::Clusters::IntegrationsController, :enable_admin_mode do
+RSpec.describe Admin::Clusters::IntegrationsController, :enable_admin_mode, feature_category: :integrations do
include AccessMatchersForController
shared_examples 'a secure endpoint' do
diff --git a/spec/requests/admin/hook_logs_controller_spec.rb b/spec/requests/admin/hook_logs_controller_spec.rb
index f8d3381c052..fa9f317dbba 100644
--- a/spec/requests/admin/hook_logs_controller_spec.rb
+++ b/spec/requests/admin/hook_logs_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Admin::HookLogsController, :enable_admin_mode do
+RSpec.describe Admin::HookLogsController, :enable_admin_mode, feature_category: :integrations do
let_it_be(:user) { create(:admin) }
let_it_be_with_refind(:web_hook) { create(:system_hook) }
let_it_be_with_refind(:web_hook_log) { create(:web_hook_log, web_hook: web_hook) }
diff --git a/spec/requests/admin/impersonation_tokens_controller_spec.rb b/spec/requests/admin/impersonation_tokens_controller_spec.rb
index ee0e12ad0c0..15212db0e77 100644
--- a/spec/requests/admin/impersonation_tokens_controller_spec.rb
+++ b/spec/requests/admin/impersonation_tokens_controller_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Admin::ImpersonationTokensController, :enable_admin_mode do
+RSpec.describe Admin::ImpersonationTokensController, :enable_admin_mode,
+feature_category: :authentication_and_authorization do
let(:admin) { create(:admin) }
let!(:user) { create(:user) }
diff --git a/spec/requests/admin/integrations_controller_spec.rb b/spec/requests/admin/integrations_controller_spec.rb
index 128aada0975..efd0e3d91ee 100644
--- a/spec/requests/admin/integrations_controller_spec.rb
+++ b/spec/requests/admin/integrations_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Admin::IntegrationsController, :enable_admin_mode do
+RSpec.describe Admin::IntegrationsController, :enable_admin_mode, feature_category: :integrations do
let_it_be(:admin) { create(:admin) }
before do
diff --git a/spec/requests/admin/version_check_controller_spec.rb b/spec/requests/admin/version_check_controller_spec.rb
index 7e2f33d5bc5..47221bf37e5 100644
--- a/spec/requests/admin/version_check_controller_spec.rb
+++ b/spec/requests/admin/version_check_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Admin::VersionCheckController, :enable_admin_mode do
+RSpec.describe Admin::VersionCheckController, :enable_admin_mode, feature_category: :not_owned do
let(:admin) { create(:admin) }
before do
diff --git a/spec/requests/api/access_requests_spec.rb b/spec/requests/api/access_requests_spec.rb
index 2af6c438fc9..8c14ead9e42 100644
--- a/spec/requests/api/access_requests_spec.rb
+++ b/spec/requests/api/access_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::AccessRequests do
+RSpec.describe API::AccessRequests, feature_category: :authentication_and_authorization do
let_it_be(:maintainer) { create(:user) }
let_it_be(:developer) { create(:user) }
let_it_be(:access_requester) { create(:user) }
diff --git a/spec/requests/api/admin/batched_background_migrations_spec.rb b/spec/requests/api/admin/batched_background_migrations_spec.rb
index 3b396a91d3e..9712777d261 100644
--- a/spec/requests/api/admin/batched_background_migrations_spec.rb
+++ b/spec/requests/api/admin/batched_background_migrations_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Admin::BatchedBackgroundMigrations do
+RSpec.describe API::Admin::BatchedBackgroundMigrations, feature_category: :database do
let(:admin) { create(:admin) }
let(:unauthorized_user) { create(:user) }
diff --git a/spec/requests/api/admin/instance_clusters_spec.rb b/spec/requests/api/admin/instance_clusters_spec.rb
index 7b3224f58c5..7b510f74fd4 100644
--- a/spec/requests/api/admin/instance_clusters_spec.rb
+++ b/spec/requests/api/admin/instance_clusters_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::API::Admin::InstanceClusters do
+RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_management do
include KubernetesHelpers
let_it_be(:regular_user) { create(:user) }
diff --git a/spec/requests/api/admin/plan_limits_spec.rb b/spec/requests/api/admin/plan_limits_spec.rb
index 74ea3b0973f..2de7a66d803 100644
--- a/spec/requests/api/admin/plan_limits_spec.rb
+++ b/spec/requests/api/admin/plan_limits_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do
+RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :not_owned do
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
let_it_be(:plan) { create(:plan, name: 'default') }
@@ -40,6 +40,7 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do
expect(json_response['pypi_max_file_size']).to eq(Plan.default.actual_limits.pypi_max_file_size)
expect(json_response['terraform_module_max_file_size']).to eq(Plan.default.actual_limits.terraform_module_max_file_size)
expect(json_response['storage_size_limit']).to eq(Plan.default.actual_limits.storage_size_limit)
+ expect(json_response['pipeline_hierarchy_size']).to eq(Plan.default.actual_limits.pipeline_hierarchy_size)
end
end
@@ -70,6 +71,7 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do
expect(json_response['pypi_max_file_size']).to eq(Plan.default.actual_limits.pypi_max_file_size)
expect(json_response['terraform_module_max_file_size']).to eq(Plan.default.actual_limits.terraform_module_max_file_size)
expect(json_response['storage_size_limit']).to eq(Plan.default.actual_limits.storage_size_limit)
+ expect(json_response['pipeline_hierarchy_size']).to eq(Plan.default.actual_limits.pipeline_hierarchy_size)
end
end
@@ -118,7 +120,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do
'nuget_max_file_size': 50,
'pypi_max_file_size': 60,
'terraform_module_max_file_size': 70,
- 'storage_size_limit': 80
+ 'storage_size_limit': 80,
+ 'pipeline_hierarchy_size': 250
}
expect(response).to have_gitlab_http_status(:ok)
@@ -140,6 +143,7 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do
expect(json_response['pypi_max_file_size']).to eq(60)
expect(json_response['terraform_module_max_file_size']).to eq(70)
expect(json_response['storage_size_limit']).to eq(80)
+ expect(json_response['pipeline_hierarchy_size']).to eq(250)
end
it 'updates single plan limits' do
@@ -183,7 +187,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do
'nuget_max_file_size': 'e',
'pypi_max_file_size': 'f',
'terraform_module_max_file_size': 'g',
- 'storage_size_limit': 'j'
+ 'storage_size_limit': 'j',
+ 'pipeline_hierarchy_size': 'r'
}
expect(response).to have_gitlab_http_status(:bad_request)
@@ -204,7 +209,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do
'nuget_max_file_size is invalid',
'pypi_max_file_size is invalid',
'terraform_module_max_file_size is invalid',
- 'storage_size_limit is invalid'
+ 'storage_size_limit is invalid',
+ 'pipeline_hierarchy_size is invalid'
)
end
end
diff --git a/spec/requests/api/admin/sidekiq_spec.rb b/spec/requests/api/admin/sidekiq_spec.rb
index 1e626c90e7e..0b456721d4f 100644
--- a/spec/requests/api/admin/sidekiq_spec.rb
+++ b/spec/requests/api/admin/sidekiq_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Admin::Sidekiq, :clean_gitlab_redis_queues do
+RSpec.describe API::Admin::Sidekiq, :clean_gitlab_redis_queues, feature_category: :not_owned do
let_it_be(:admin) { create(:admin) }
describe 'DELETE /admin/sidekiq/queues/:queue_name' do
diff --git a/spec/requests/api/alert_management_alerts_spec.rb b/spec/requests/api/alert_management_alerts_spec.rb
index 680a3883387..8dd0c46eab6 100644
--- a/spec/requests/api/alert_management_alerts_spec.rb
+++ b/spec/requests/api/alert_management_alerts_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::AlertManagementAlerts do
+RSpec.describe API::AlertManagementAlerts, feature_category: :incident_management do
let_it_be(:creator) { create(:user) }
let_it_be(:project) do
create(:project, :public, creator_id: creator.id, namespace: creator.namespace)
diff --git a/spec/requests/api/api_guard/admin_mode_middleware_spec.rb b/spec/requests/api/api_guard/admin_mode_middleware_spec.rb
index ba7a01a2cd9..21f3691c20b 100644
--- a/spec/requests/api/api_guard/admin_mode_middleware_spec.rb
+++ b/spec/requests/api/api_guard/admin_mode_middleware_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::APIGuard::AdminModeMiddleware, :request_store do
+RSpec.describe API::APIGuard::AdminModeMiddleware, :request_store, feature_category: :not_owned do
let(:user) { create(:admin) }
it 'is loaded' do
diff --git a/spec/requests/api/api_guard/response_coercer_middleware_spec.rb b/spec/requests/api/api_guard/response_coercer_middleware_spec.rb
index 6f3f97fe846..77498c2e2b3 100644
--- a/spec/requests/api/api_guard/response_coercer_middleware_spec.rb
+++ b/spec/requests/api/api_guard/response_coercer_middleware_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::APIGuard::ResponseCoercerMiddleware do
+RSpec.describe API::APIGuard::ResponseCoercerMiddleware, feature_category: :not_owned do
using RSpec::Parameterized::TableSyntax
it 'is loaded' do
diff --git a/spec/requests/api/api_spec.rb b/spec/requests/api/api_spec.rb
index 260f7cbc226..9cf9c313f11 100644
--- a/spec/requests/api/api_spec.rb
+++ b/spec/requests/api/api_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::API do
+RSpec.describe API::API, feature_category: :authentication_and_authorization do
include GroupAPIHelpers
describe 'Record user last activity in after hook' do
diff --git a/spec/requests/api/appearance_spec.rb b/spec/requests/api/appearance_spec.rb
index 69176e18d2e..84d5b091b8d 100644
--- a/spec/requests/api/appearance_spec.rb
+++ b/spec/requests/api/appearance_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Appearance, 'Appearance' do
+RSpec.describe API::Appearance, 'Appearance', feature_category: :navigation do
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
@@ -33,6 +33,7 @@ RSpec.describe API::Appearance, 'Appearance' do
expect(json_response['new_project_guidelines']).to eq('')
expect(json_response['profile_image_guidelines']).to eq('')
expect(json_response['title']).to eq('')
+ expect(json_response['short_title']).to eq('')
end
end
end
@@ -51,6 +52,7 @@ RSpec.describe API::Appearance, 'Appearance' do
it "allows updating the settings" do
put api("/application/appearance", admin), params: {
title: "GitLab Test Instance",
+ short_title: "GitLab",
description: "gitlab-test.example.com",
new_project_guidelines: "Please read the FAQs for help.",
profile_image_guidelines: "Custom profile image guidelines"
@@ -70,6 +72,7 @@ RSpec.describe API::Appearance, 'Appearance' do
expect(json_response['new_project_guidelines']).to eq('Please read the FAQs for help.')
expect(json_response['profile_image_guidelines']).to eq('Custom profile image guidelines')
expect(json_response['title']).to eq('GitLab Test Instance')
+ expect(json_response['short_title']).to eq('GitLab')
end
end
diff --git a/spec/requests/api/applications_spec.rb b/spec/requests/api/applications_spec.rb
index 022451553ee..e238a1fb554 100644
--- a/spec/requests/api/applications_spec.rb
+++ b/spec/requests/api/applications_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Applications, :api do
+RSpec.describe API::Applications, :api, feature_category: :authentication_and_authorization do
let(:admin_user) { create(:user, admin: true) }
let(:user) { create(:user, admin: false) }
let(:scopes) { 'api' }
diff --git a/spec/requests/api/avatar_spec.rb b/spec/requests/api/avatar_spec.rb
index 656a086e550..8affbe6ec2b 100644
--- a/spec/requests/api/avatar_spec.rb
+++ b/spec/requests/api/avatar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Avatar do
+RSpec.describe API::Avatar, feature_category: :users do
let(:gravatar_service) { double('GravatarService') }
describe 'GET /avatar' do
diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb
index bb563f93bfe..87dc06b7d15 100644
--- a/spec/requests/api/award_emoji_spec.rb
+++ b/spec/requests/api/award_emoji_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::AwardEmoji do
+RSpec.describe API::AwardEmoji, feature_category: :not_owned do
let_it_be_with_reload(:project) { create(:project, :private) }
let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, project: project) }
diff --git a/spec/requests/api/badges_spec.rb b/spec/requests/api/badges_spec.rb
index d8a345a79b0..6c6a7cc7cc6 100644
--- a/spec/requests/api/badges_spec.rb
+++ b/spec/requests/api/badges_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Badges do
+RSpec.describe API::Badges, feature_category: :projects do
let(:maintainer) { create(:user, username: 'maintainer_user') }
let(:developer) { create(:user) }
let(:access_requester) { create(:user) }
diff --git a/spec/requests/api/boards_spec.rb b/spec/requests/api/boards_spec.rb
index 4d7256a1f03..69804c2c4a4 100644
--- a/spec/requests/api/boards_spec.rb
+++ b/spec/requests/api/boards_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Boards do
+RSpec.describe API::Boards, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:non_member) { create(:user) }
let_it_be(:guest) { create(:user) }
diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb
index 750b9a39e15..eba1a06b5e4 100644
--- a/spec/requests/api/branches_spec.rb
+++ b/spec/requests/api/branches_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Branches do
+RSpec.describe API::Branches, feature_category: :source_code_management do
let_it_be(:user) { create(:user) }
let(:project) { create(:project, :repository, creator: user, path: 'my.project', create_branch: 'ends-with.txt') }
@@ -344,6 +344,18 @@ RSpec.describe API::Branches do
end
end
+ context 'when branch is ambiguous' do
+ let(:branch_name) { 'prefix' }
+
+ before do
+ project.repository.create_branch('prefix/branch')
+ end
+
+ it_behaves_like '404 response' do
+ let(:request) { get api(route, current_user) }
+ end
+ end
+
context 'when repository does not exist' do
it_behaves_like '404 response' do
let(:project) { create(:project, creator: user) }
diff --git a/spec/requests/api/broadcast_messages_spec.rb b/spec/requests/api/broadcast_messages_spec.rb
index 76412c80f4c..5cbb7dbfa12 100644
--- a/spec/requests/api/broadcast_messages_spec.rb
+++ b/spec/requests/api/broadcast_messages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::BroadcastMessages do
+RSpec.describe API::BroadcastMessages, feature_category: :onboarding do
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
let_it_be(:message) { create(:broadcast_message) }
diff --git a/spec/requests/api/bulk_imports_spec.rb b/spec/requests/api/bulk_imports_spec.rb
index ad57a370fc5..13f079c69e7 100644
--- a/spec/requests/api/bulk_imports_spec.rb
+++ b/spec/requests/api/bulk_imports_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::BulkImports do
+RSpec.describe API::BulkImports, feature_category: :importers do
let_it_be(:user) { create(:user) }
let_it_be(:import_1) { create(:bulk_import, user: user) }
let_it_be(:import_2) { create(:bulk_import, user: user) }
@@ -50,6 +50,9 @@ RSpec.describe API::BulkImports do
.to receive(:instance_version)
.and_return(
Gitlab::VersionInfo.new(::BulkImport::MIN_MAJOR_VERSION, ::BulkImport::MIN_MINOR_VERSION_FOR_PROJECT))
+ allow(instance)
+ .to receive(:instance_enterprise)
+ .and_return(false)
end
end
diff --git a/spec/requests/api/ci/job_artifacts_spec.rb b/spec/requests/api/ci/job_artifacts_spec.rb
index da9eb6b2216..a4a38179d11 100644
--- a/spec/requests/api/ci/job_artifacts_spec.rb
+++ b/spec/requests/api/ci/job_artifacts_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::JobArtifacts do
+RSpec.describe API::Ci::JobArtifacts, feature_category: :build_artifacts do
include HttpBasicAuthHelpers
include DependencyProxyHelpers
diff --git a/spec/requests/api/ci/jobs_spec.rb b/spec/requests/api/ci/jobs_spec.rb
index c1b7461f444..4e348ae64b6 100644
--- a/spec/requests/api/ci/jobs_spec.rb
+++ b/spec/requests/api/ci/jobs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Jobs do
+RSpec.describe API::Ci::Jobs, feature_category: :continuous_integration do
include HttpBasicAuthHelpers
include DependencyProxyHelpers
@@ -190,15 +190,56 @@ RSpec.describe API::Ci::Jobs do
describe 'GET /job/allowed_agents' do
let_it_be(:group) { create(:group) }
- let_it_be(:group_agent) { create(:cluster_agent, project: create(:project, group: group)) }
- let_it_be(:group_authorization) { create(:agent_group_authorization, agent: group_agent, group: group) }
- let_it_be(:project_agent) { create(:cluster_agent, project: project) }
- before(:all) do
- project.update!(group: group_authorization.group)
+ # create a different project for the group agents to reference
+ # otherwise the AgentAuthorizationsFinder will pick up the project.cluster_agents' implicit authorizations
+ let_it_be(:other_project) { create(:project, group: group) }
+
+ let_it_be(:agent_authorizations_without_env) do
+ [
+ create(:agent_group_authorization, agent: create(:cluster_agent, project: other_project), group: group),
+ create(:agent_project_authorization, agent: create(:cluster_agent, project: project), project: project),
+ Clusters::Agents::ImplicitAuthorization.new(agent: create(:cluster_agent, project: project))
+ ]
+ end
+
+ let_it_be(:agent_authorizations_with_review_and_production_env) do
+ [
+ create(
+ :agent_group_authorization,
+ agent: create(:cluster_agent, project: other_project),
+ group: group,
+ environments: ['production', 'review/*']
+ ),
+ create(
+ :agent_project_authorization,
+ agent: create(:cluster_agent, project: project),
+ project: project,
+ environments: ['production', 'review/*']
+ )
+ ]
+ end
+
+ let_it_be(:agent_authorizations_with_staging_env) do
+ [
+ create(
+ :agent_group_authorization,
+ agent: create(:cluster_agent, project: other_project),
+ group: group,
+ environments: ['staging']
+ ),
+ create(
+ :agent_project_authorization,
+ agent: create(:cluster_agent, project: project),
+ project: project,
+ environments: ['staging']
+ )
+ ]
end
- let(:implicit_authorization) { Clusters::Agents::ImplicitAuthorization.new(agent: project_agent) }
+ before(:all) do
+ project.update!(group: group)
+ end
let(:headers) { { API::Ci::Helpers::Runner::JOB_TOKEN_HEADER => job.token } }
let(:job) { create(:ci_build, :artifacts, pipeline: pipeline, user: api_user, status: job_status) }
@@ -215,30 +256,46 @@ RSpec.describe API::Ci::Jobs do
context 'when token is valid and user is authorized' do
shared_examples_for 'valid allowed_agents request' do
- it 'returns agent info', :aggregate_failures do
+ it 'returns the job info', :aggregate_failures do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.dig('job', 'id')).to eq(job.id)
expect(json_response.dig('pipeline', 'id')).to eq(job.pipeline_id)
expect(json_response.dig('project', 'id')).to eq(job.project_id)
- expect(json_response.dig('project', 'groups')).to match_array([{ 'id' => group_authorization.group.id }])
+ expect(json_response.dig('project', 'groups')).to match_array([{ 'id' => group.id }])
expect(json_response.dig('user', 'id')).to eq(api_user.id)
expect(json_response.dig('user', 'username')).to eq(api_user.username)
expect(json_response.dig('user', 'roles_in_project')).to match_array %w(guest reporter developer)
expect(json_response).not_to include('environment')
- expect(json_response['allowed_agents']).to match_array(
- [
- {
- 'id' => implicit_authorization.agent_id,
- 'config_project' => hash_including('id' => implicit_authorization.agent.project_id),
- 'configuration' => implicit_authorization.config
- },
- {
- 'id' => group_authorization.agent_id,
- 'config_project' => hash_including('id' => group_authorization.agent.project_id),
- 'configuration' => group_authorization.config
- }
- ])
+ end
+
+ it 'returns the agents allowed for the job' do
+ expected_allowed_agents = agent_authorizations_without_env.map do |agent_auth|
+ {
+ 'id' => agent_auth.agent_id,
+ 'config_project' => hash_including('id' => agent_auth.agent.project_id),
+ 'configuration' => agent_auth.config
+ }
+ end
+
+ expect(json_response['allowed_agents']).to match_array expected_allowed_agents
+ end
+ end
+
+ shared_examples_for 'valid allowed_agents request for a job with environment' do
+ it 'return the agents configured for the given environment' do
+ expected_allowed_agents = (
+ agent_authorizations_without_env +
+ agent_authorizations_with_review_and_production_env
+ ).map do |agent_auth|
+ {
+ 'id' => agent_auth.agent_id,
+ 'config_project' => hash_including('id' => agent_auth.agent.project_id),
+ 'configuration' => agent_auth.config
+ }
+ end
+
+ expect(json_response['allowed_agents']).to match_array(expected_allowed_agents)
end
end
@@ -254,21 +311,25 @@ RSpec.describe API::Ci::Jobs do
it 'includes environment tier' do
expect(json_response.dig('environment', 'tier')).to eq('production')
end
+
+ it_behaves_like 'valid allowed_agents request for a job with environment'
end
context 'when non-deployment environment action' do
let(:job) do
- create(:environment, name: 'review', project_id: project.id)
- create(:ci_build, :artifacts, :stop_review_app, environment: 'review', pipeline: pipeline, user: api_user, status: job_status)
+ create(:environment, name: 'review/123', project_id: project.id)
+ create(:ci_build, :artifacts, :stop_review_app, environment: 'review/123', pipeline: pipeline, user: api_user, status: job_status)
end
it 'includes environment slug' do
- expect(json_response.dig('environment', 'slug')).to eq('review')
+ expect(json_response.dig('environment', 'slug')).to match('review-123-.*')
end
it 'includes environment tier' do
expect(json_response.dig('environment', 'tier')).to eq('development')
end
+
+ it_behaves_like 'valid allowed_agents request for a job with environment'
end
context 'when passing the token as params' do
@@ -325,7 +386,7 @@ RSpec.describe API::Ci::Jobs do
context 'authorized user' do
it 'returns project jobs' do
expect(response).to have_gitlab_http_status(:ok)
- expect(response).to include_pagination_headers
+ expect(response).to include_limited_pagination_headers
expect(json_response).to be_an Array
end
@@ -426,6 +487,46 @@ RSpec.describe API::Ci::Jobs do
end
end
+ describe 'GET /projects/:id/jobs rate limited' do
+ let(:query) { {} }
+
+ context 'with the ci_enforce_rate_limits_jobs_api feature flag on' do
+ before do
+ stub_feature_flags(ci_enforce_rate_limits_jobs_api: true)
+
+ allow_next_instance_of(Gitlab::ApplicationRateLimiter::BaseStrategy) do |strategy|
+ threshold = Gitlab::ApplicationRateLimiter.rate_limits[:jobs_index][:threshold]
+ allow(strategy).to receive(:increment).and_return(threshold + 1)
+ end
+
+ get api("/projects/#{project.id}/jobs", api_user), params: query
+ end
+
+ it 'enforces rate limits for the endpoint' do
+ expect(response).to have_gitlab_http_status :too_many_requests
+ expect(json_response['message']['error']).to eq('This endpoint has been requested too many times. Try again later.')
+ end
+ end
+
+ context 'with the ci_enforce_rate_limits_jobs_api feature flag off' do
+ before do
+ stub_feature_flags(ci_enforce_rate_limits_jobs_api: false)
+
+ allow_next_instance_of(Gitlab::ApplicationRateLimiter::BaseStrategy) do |strategy|
+ threshold = Gitlab::ApplicationRateLimiter.rate_limits[:jobs_index][:threshold]
+ allow(strategy).to receive(:increment).and_return(threshold + 1)
+ end
+
+ get api("/projects/#{project.id}/jobs", api_user), params: query
+ end
+
+ it 'makes a successful request' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_limited_pagination_headers
+ end
+ end
+ end
+
describe 'GET /projects/:id/jobs/:job_id' do
before do |example|
unless example.metadata[:skip_before_request]
diff --git a/spec/requests/api/ci/pipeline_schedules_spec.rb b/spec/requests/api/ci/pipeline_schedules_spec.rb
index 30badadde13..2a2c5f65aee 100644
--- a/spec/requests/api/ci/pipeline_schedules_spec.rb
+++ b/spec/requests/api/ci/pipeline_schedules_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::PipelineSchedules do
+RSpec.describe API::Ci::PipelineSchedules, feature_category: :continuous_integration do
let_it_be(:developer) { create(:user) }
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository, public_builds: false) }
diff --git a/spec/requests/api/ci/pipelines_spec.rb b/spec/requests/api/ci/pipelines_spec.rb
index c9d06f37c8b..6d69da85449 100644
--- a/spec/requests/api/ci/pipelines_spec.rb
+++ b/spec/requests/api/ci/pipelines_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Pipelines do
+RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
let_it_be(:user) { create(:user) }
let_it_be(:non_member) { create(:user) }
let_it_be(:project2) { create(:project, creator: user) }
diff --git a/spec/requests/api/ci/resource_groups_spec.rb b/spec/requests/api/ci/resource_groups_spec.rb
index 2a67a3e4322..26265aec1dc 100644
--- a/spec/requests/api/ci/resource_groups_spec.rb
+++ b/spec/requests/api/ci/resource_groups_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::ResourceGroups do
+RSpec.describe API::Ci::ResourceGroups, feature_category: :continuous_delivery do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
let_it_be(:reporter) { create(:user).tap { |u| project.add_reporter(u) } }
diff --git a/spec/requests/api/ci/runner/jobs_artifacts_spec.rb b/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
index 9af0541bd2c..1c119079c50 100644
--- a/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
+RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state, feature_category: :runner do
include StubGitlabCalls
include RedisHelpers
include WorkhorseHelpers
diff --git a/spec/requests/api/ci/runner/jobs_put_spec.rb b/spec/requests/api/ci/runner/jobs_put_spec.rb
index 8c95748aa5f..22817922b1b 100644
--- a/spec/requests/api/ci/runner/jobs_put_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_put_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
+RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state, feature_category: :runner do
include StubGitlabCalls
include RedisHelpers
include WorkhorseHelpers
diff --git a/spec/requests/api/ci/runner/jobs_request_post_spec.rb b/spec/requests/api/ci/runner/jobs_request_post_spec.rb
index d69a3f5a980..d15bc9d2dd5 100644
--- a/spec/requests/api/ci/runner/jobs_request_post_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_request_post_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
+RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state, feature_category: :runner do
include StubGitlabCalls
include RedisHelpers
include WorkhorseHelpers
@@ -175,6 +175,10 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
'allow_failure' => true }]
end
+ let(:expected_hooks) do
+ [{ 'name' => 'pre_get_sources_script', 'script' => ["echo 'hello pre_get_sources_script'"] }]
+ end
+
let(:expected_variables) do
[{ 'key' => 'CI_JOB_NAME', 'value' => 'spinach', 'public' => true, 'masked' => false },
{ 'key' => 'CI_JOB_STAGE', 'value' => 'test', 'public' => true, 'masked' => false },
@@ -230,6 +234,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
'variables' => [{ 'key' => 'MYSQL_ROOT_PASSWORD', 'value' => 'root123.' }], 'pull_policy' => nil }
])
expect(json_response['steps']).to eq(expected_steps)
+ expect(json_response['hooks']).to eq(expected_hooks)
expect(json_response['artifacts']).to eq(expected_artifacts)
expect(json_response['cache']).to match(expected_cache)
expect(json_response['variables']).to include(*expected_variables)
@@ -769,6 +774,19 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
end
end
+
+ context 'when the FF ci_hooks_pre_get_sources_script is disabled' do
+ before do
+ stub_feature_flags(ci_hooks_pre_get_sources_script: false)
+ end
+
+ it 'does not return the pre_get_sources_script' do
+ request_job
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response).not_to have_key('hooks')
+ end
+ end
end
describe 'port support' do
diff --git a/spec/requests/api/ci/runner/jobs_trace_spec.rb b/spec/requests/api/ci/runner/jobs_trace_spec.rb
index d42043a7fe5..de67cec0a27 100644
--- a/spec/requests/api/ci/runner/jobs_trace_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_trace_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Runner, :clean_gitlab_redis_trace_chunks do
+RSpec.describe API::Ci::Runner, :clean_gitlab_redis_trace_chunks, feature_category: :runner do
include StubGitlabCalls
include RedisHelpers
include WorkhorseHelpers
diff --git a/spec/requests/api/ci/runner/runners_delete_spec.rb b/spec/requests/api/ci/runner/runners_delete_spec.rb
index 9d1bae7cce8..65c287a9535 100644
--- a/spec/requests/api/ci/runner/runners_delete_spec.rb
+++ b/spec/requests/api/ci/runner/runners_delete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
+RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state, feature_category: :runner_fleet do
include StubGitlabCalls
include RedisHelpers
include WorkhorseHelpers
diff --git a/spec/requests/api/ci/runner/runners_post_spec.rb b/spec/requests/api/ci/runner/runners_post_spec.rb
index 47302046865..73f8e87a9fb 100644
--- a/spec/requests/api/ci/runner/runners_post_spec.rb
+++ b/spec/requests/api/ci/runner/runners_post_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
+RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state, feature_category: :runner_fleet do
describe '/api/v4/runners' do
describe 'POST /api/v4/runners' do
context 'when no token is provided' do
diff --git a/spec/requests/api/ci/runner/runners_reset_spec.rb b/spec/requests/api/ci/runner/runners_reset_spec.rb
index 02b66a89a0a..6ab21138d26 100644
--- a/spec/requests/api/ci/runner/runners_reset_spec.rb
+++ b/spec/requests/api/ci/runner/runners_reset_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
+RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state, feature_category: :runner_fleet do
include StubGitlabCalls
include RedisHelpers
include WorkhorseHelpers
diff --git a/spec/requests/api/ci/runner/runners_verify_post_spec.rb b/spec/requests/api/ci/runner/runners_verify_post_spec.rb
index 038e126deaa..22a954cc444 100644
--- a/spec/requests/api/ci/runner/runners_verify_post_spec.rb
+++ b/spec/requests/api/ci/runner/runners_verify_post_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
+RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state, feature_category: :runner do
include StubGitlabCalls
include RedisHelpers
include WorkhorseHelpers
diff --git a/spec/requests/api/ci/runners_reset_registration_token_spec.rb b/spec/requests/api/ci/runners_reset_registration_token_spec.rb
index b8e4370fd46..1110dbf5fbc 100644
--- a/spec/requests/api/ci/runners_reset_registration_token_spec.rb
+++ b/spec/requests/api/ci/runners_reset_registration_token_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Runners do
+RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
subject { post api("#{prefix}/runners/reset_registration_token", user) }
shared_examples 'bad request' do |result|
diff --git a/spec/requests/api/ci/runners_spec.rb b/spec/requests/api/ci/runners_spec.rb
index dd9894f2972..b07dd388390 100644
--- a/spec/requests/api/ci/runners_spec.rb
+++ b/spec/requests/api/ci/runners_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Runners do
+RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
let_it_be(:admin) { create(:user, :admin) }
let_it_be(:user) { create(:user) }
let_it_be(:user2) { create(:user) }
diff --git a/spec/requests/api/ci/secure_files_spec.rb b/spec/requests/api/ci/secure_files_spec.rb
index b0bca6e9125..700fd97152a 100644
--- a/spec/requests/api/ci/secure_files_spec.rb
+++ b/spec/requests/api/ci/secure_files_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::SecureFiles do
+RSpec.describe API::Ci::SecureFiles, feature_category: :pipeline_authoring do
before do
stub_ci_secure_file_object_storage
stub_feature_flags(ci_secure_files: true)
@@ -31,23 +31,6 @@ RSpec.describe API::Ci::SecureFiles do
end
describe 'GET /projects/:id/secure_files' do
- context 'feature flag' do
- it 'returns a 503 when the feature flag is disabled' do
- stub_feature_flags(ci_secure_files: false)
-
- get api("/projects/#{project.id}/secure_files", maintainer)
-
- expect(response).to have_gitlab_http_status(:service_unavailable)
- end
-
- it 'returns a 200 when the feature flag is enabled' do
- get api("/projects/#{project.id}/secure_files", maintainer)
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response).to be_a(Array)
- end
- end
-
context 'ci_secure_files_read_only feature flag' do
context 'when the flag is enabled' do
before do
diff --git a/spec/requests/api/ci/triggers_spec.rb b/spec/requests/api/ci/triggers_spec.rb
index f9b7880a4c4..ff54ba61309 100644
--- a/spec/requests/api/ci/triggers_spec.rb
+++ b/spec/requests/api/ci/triggers_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Triggers do
+RSpec.describe API::Ci::Triggers, feature_category: :continuous_integration do
let_it_be(:user) { create(:user) }
let_it_be(:user2) { create(:user) }
diff --git a/spec/requests/api/ci/variables_spec.rb b/spec/requests/api/ci/variables_spec.rb
index cafb841995d..c5d01afb7c4 100644
--- a/spec/requests/api/ci/variables_spec.rb
+++ b/spec/requests/api/ci/variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Variables do
+RSpec.describe API::Ci::Variables, feature_category: :pipeline_authoring do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let!(:project) { create(:project, creator_id: user.id) }
diff --git a/spec/requests/api/clusters/agent_tokens_spec.rb b/spec/requests/api/clusters/agent_tokens_spec.rb
index a33bef53b14..b2d996e8002 100644
--- a/spec/requests/api/clusters/agent_tokens_spec.rb
+++ b/spec/requests/api/clusters/agent_tokens_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Clusters::AgentTokens do
+RSpec.describe API::Clusters::AgentTokens, feature_category: :kubernetes_management do
let_it_be(:agent) { create(:cluster_agent) }
let_it_be(:agent_token_one) { create(:cluster_agent_token, agent: agent) }
let_it_be(:revoked_agent_token) { create(:cluster_agent_token, :revoked, agent: agent) }
@@ -80,6 +80,27 @@ RSpec.describe API::Clusters::AgentTokens do
end
end
+ it 'returns an agent token that is revoked' do
+ get api("/projects/#{project.id}/cluster_agents/#{agent.id}/tokens/#{revoked_agent_token.id}", user)
+
+ aggregate_failures "testing response" do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('public_api/v4/agent_token')
+ expect(json_response['id']).to eq(revoked_agent_token.id)
+ expect(json_response['name']).to eq(revoked_agent_token.name)
+ expect(json_response['agent_id']).to eq(agent.id)
+ expect(json_response['status']).to eq('revoked')
+ end
+ end
+
+ it 'returns a 404 if agent does not exist' do
+ path = "/projects/#{project.id}/cluster_agents/#{non_existing_record_id}/tokens/#{non_existing_record_id}"
+
+ get api(path, user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
it 'returns a 404 error if agent token id is not available' do
get api("/projects/#{project.id}/cluster_agents/#{agent.id}/tokens/#{non_existing_record_id}", user)
@@ -160,6 +181,21 @@ RSpec.describe API::Clusters::AgentTokens do
expect(agent_token_one.reload).to be_revoked
end
+ it 'returns a success response when revoking an already revoked agent token', :aggregate_failures do
+ delete api("/projects/#{project.id}/cluster_agents/#{agent.id}/tokens/#{revoked_agent_token.id}", user)
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ expect(revoked_agent_token.reload).to be_revoked
+ end
+
+ it 'returns a 404 error when given agent_id does not exist' do
+ path = "/projects/#{project.id}/cluster_agents/#{non_existing_record_id}/tokens/#{non_existing_record_id}"
+
+ delete api(path, user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
it 'returns a 404 error when revoking non existent agent token' do
delete api("/projects/#{project.id}/cluster_agents/#{agent.id}/tokens/#{non_existing_record_id}", user)
diff --git a/spec/requests/api/clusters/agents_spec.rb b/spec/requests/api/clusters/agents_spec.rb
index 5e3bdd69529..a09713bd6e7 100644
--- a/spec/requests/api/clusters/agents_spec.rb
+++ b/spec/requests/api/clusters/agents_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Clusters::Agents do
+RSpec.describe API::Clusters::Agents, feature_category: :kubernetes_management do
let_it_be(:agent) { create(:cluster_agent) }
let(:user) { agent.created_by_user }
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index dc5d9620dc4..025d065df7b 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::CommitStatuses do
+RSpec.describe API::CommitStatuses, feature_category: :continuous_integration do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:commit) { project.repository.commit }
let_it_be(:guest) { create_user(:guest) }
@@ -167,7 +167,7 @@ RSpec.describe API::CommitStatuses do
let!(:pipeline) { create(:ci_pipeline, project: project, sha: sha, ref: 'ref') }
let(:params) { { state: 'pending' } }
- shared_examples_for 'creates a commit status for the existing pipeline' do
+ shared_examples_for 'creates a commit status for the existing pipeline with an external stage' do
it do
expect do
post api(post_url, developer), params: params
@@ -176,19 +176,73 @@ RSpec.describe API::CommitStatuses do
job = pipeline.statuses.find_by_name(json_response['name'])
expect(response).to have_gitlab_http_status(:created)
+ expect(job.ci_stage.name).to eq('external')
+ expect(job.ci_stage.position).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
+ expect(job.ci_stage.pipeline).to eq(pipeline)
expect(job.status).to eq('pending')
expect(job.stage_idx).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
end
end
- it_behaves_like 'creates a commit status for the existing pipeline'
+ shared_examples_for 'updates the commit status with an external stage' do
+ before do
+ post api(post_url, developer), params: { state: 'pending' }
+ end
+
+ it 'updates the commit status with the external stage' do
+ post api(post_url, developer), params: { state: 'running' }
+ job = pipeline.statuses.find_by_name(json_response['name'])
+
+ expect(job.ci_stage.name).to eq('external')
+ expect(job.ci_stage.position).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
+ expect(job.ci_stage.pipeline).to eq(pipeline)
+ expect(job.status).to eq('running')
+ expect(job.stage_idx).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
+ end
+ end
context 'with pipeline for merge request' do
let!(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline, source_project: project) }
let!(:pipeline) { merge_request.all_pipelines.last }
let(:sha) { pipeline.sha }
- it_behaves_like 'creates a commit status for the existing pipeline'
+ it_behaves_like 'creates a commit status for the existing pipeline with an external stage'
+ end
+
+ context 'when an external stage does not exist' do
+ context 'when the commit status does not exist' do
+ it_behaves_like 'creates a commit status for the existing pipeline with an external stage'
+ end
+
+ context 'when the commit status exists' do
+ it_behaves_like 'updates the commit status with an external stage'
+ end
+ end
+
+ context 'when an external stage already exists' do
+ let(:stage) { create(:ci_stage, name: 'external', pipeline: pipeline, position: 1_000_000) }
+
+ context 'when the commit status exists' do
+ it_behaves_like 'updates the commit status with an external stage'
+ end
+
+ context 'when the commit status does not exist' do
+ it_behaves_like 'creates a commit status for the existing pipeline with an external stage'
+ end
+ end
+ end
+
+ context 'when the pipeline does not exist' do
+ it 'creates a commit status and a stage' do
+ expect do
+ post api(post_url, developer), params: { state: 'pending' }
+ end.to change { Ci::Pipeline.count }.by(1)
+ job = Ci::Pipeline.last.statuses.find_by_name(json_response['name'])
+
+ expect(job.ci_stage.name).to eq('external')
+ expect(job.ci_stage.position).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
+ expect(job.status).to eq('pending')
+ expect(job.stage_idx).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
end
end
end
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 8a08d5203fd..5874d764b00 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require 'mime/types'
-RSpec.describe API::Commits do
+RSpec.describe API::Commits, feature_category: :source_code_management do
include ProjectForksHelper
include SessionHelpers
@@ -492,12 +492,32 @@ RSpec.describe API::Commits do
subject
end
- it_behaves_like 'Snowplow event tracking' do
- let(:namespace) { project.namespace }
- let(:category) { 'ide_edit' }
- let(:action) { 'g_edit_by_web_ide' }
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:namespace) { project.namespace.reload }
+ let(:category) { 'Gitlab::UsageDataCounters::EditorUniqueCounter' }
+ let(:action) { 'ide_edit' }
+ let(:property) { 'g_edit_by_web_ide' }
+ let(:label) { 'usage_activity_by_stage_monthly.create.action_monthly_active_users_ide_edit' }
+ let(:context) { [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event_name).to_context] }
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
end
+
+ context 'counts.web_ide_commits Snowplow event tracking' do
+ before do
+ allow(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_web_ide_edit_action)
+ end
+
+ it_behaves_like 'Snowplow event tracking' do
+ let(:action) { :commit }
+ let(:category) { described_class.to_s }
+ let(:namespace) { project.namespace.reload }
+ let(:label) { 'counts.web_ide_commits' }
+ let(:feature_flag_name) { 'route_hll_to_snowplow_phase3' }
+ let(:context) do
+ [Gitlab::Tracking::ServicePingContext.new(data_source: :redis, key_path: 'counts.web_ide_commits').to_context.to_json]
+ end
+ end
+ end
end
context 'a new file in project repo' do
@@ -2206,7 +2226,7 @@ RSpec.describe API::Commits do
end
describe 'GET /projects/:id/repository/commits/:sha/signature' do
- let!(:project) { create(:project, :repository, :public) }
+ let_it_be(:project) { create(:project, :repository, :public) }
let(:project_id) { project.id }
let(:commit_id) { project.repository.commit.id }
let(:route) { "/projects/#{project_id}/repository/commits/#{commit_id}/signature" }
@@ -2228,7 +2248,7 @@ RSpec.describe API::Commits do
end
context 'gpg signed commit' do
- let(:commit) { project.repository.commit(GpgHelpers::SIGNED_COMMIT_SHA) }
+ let!(:commit) { project.commit(GpgHelpers::SIGNED_COMMIT_SHA) }
let(:commit_id) { commit.id }
it 'returns correct JSON' do
@@ -2244,8 +2264,8 @@ RSpec.describe API::Commits do
end
context 'x509 signed commit' do
- let(:commit) { project.repository.commit_by(oid: '189a6c924013fc3fe40d6f1ec1dc20214183bc97') }
- let(:commit_id) { commit.id }
+ let(:commit_id) { '189a6c924013fc3fe40d6f1ec1dc20214183bc97' }
+ let!(:commit) { project.commit(commit_id) }
it 'returns correct JSON' do
get api(route, current_user)
@@ -2276,5 +2296,59 @@ RSpec.describe API::Commits do
end
end
end
+
+ context 'with ssh signed commit' do
+ let(:commit_id) { '7b5160f9bb23a3d58a0accdbe89da13b96b1ece9' }
+ let!(:commit) { project.commit(commit_id) }
+
+ context 'when key belonging to author does not exist' do
+ it 'returns data without key' do
+ get api(route, current_user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['signature_type']).to eq('SSH')
+ expect(json_response['verification_status']).to eq(commit.signature.verification_status)
+ expect(json_response['key']).to be_nil
+ expect(json_response['commit_source']).to eq('gitaly')
+ end
+ end
+
+ context 'when key belonging to author exists' do
+ let(:user) { create(:user, email: commit.committer_email) }
+ let!(:key) { create(:key, user: user, key: extract_public_key_from_commit(commit), expires_at: 2.days.from_now) }
+
+ def extract_public_key_from_commit(commit)
+ ssh_commit = Gitlab::Ssh::Commit.new(commit)
+ signature_data = ::SSHData::Signature.parse_pem(ssh_commit.signature_text)
+ signature_data.public_key.openssh
+ end
+
+ it 'returns data including key' do
+ get api(route, current_user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['signature_type']).to eq('SSH')
+ expect(json_response['verification_status']).to eq(commit.signature.verification_status)
+ expect(json_response['key']['id']).to eq(key.id)
+ expect(json_response['key']['title']).to eq(key.title)
+ expect(json_response['key']['key']).to eq(key.publishable_key)
+ expect(Time.parse(json_response['key']['created_at'])).to be_like_time(key.created_at)
+ expect(Time.parse(json_response['key']['expires_at'])).to be_like_time(key.expires_at)
+ expect(json_response['commit_source']).to eq('gitaly')
+ end
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(ssh_commit_signatures: false)
+ end
+
+ it 'returns 404' do
+ get api(route, current_user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
end
end
diff --git a/spec/requests/api/composer_packages_spec.rb b/spec/requests/api/composer_packages_spec.rb
index 53f3ef10743..0c726d46a01 100644
--- a/spec/requests/api/composer_packages_spec.rb
+++ b/spec/requests/api/composer_packages_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe API::ComposerPackages do
+RSpec.describe API::ComposerPackages, feature_category: :package_registry do
include HttpBasicAuthHelpers
let_it_be(:user) { create(:user) }
@@ -14,7 +14,10 @@ RSpec.describe API::ComposerPackages do
let_it_be(:deploy_token_for_group) { create(:deploy_token, :group, read_package_registry: true, write_package_registry: true) }
let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: deploy_token_for_group, group: group) }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } }
+ let(:snowplow_gitlab_standard_context) do
+ { project: project, namespace: project.namespace, user: user, property: 'i_package_composer_user' }
+ end
+
let(:headers) { {} }
using RSpec::Parameterized::TableSyntax
@@ -491,7 +494,6 @@ RSpec.describe API::ComposerPackages do
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
@@ -501,6 +503,10 @@ RSpec.describe API::ComposerPackages do
include_context 'Composer user type', params[:user_role], params[:member] do
if params[:expected_status] == :success
+ let(:snowplow_gitlab_standard_context) do
+ { project: project, namespace: project.namespace, property: 'i_package_composer_user' }
+ end
+
it_behaves_like 'a package tracking event', described_class.name, 'pull_package'
else
it_behaves_like 'not a package tracking event'
@@ -509,6 +515,17 @@ RSpec.describe API::ComposerPackages do
end
it_behaves_like 'Composer publish with deploy tokens'
+
+ context 'with access to package registry for everyone' do
+ let(:headers) { {} }
+
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC)
+ end
+
+ it_behaves_like 'returning response status', :success
+ end
end
end
diff --git a/spec/requests/api/conan_instance_packages_spec.rb b/spec/requests/api/conan_instance_packages_spec.rb
index b343e0cfc97..0c1d94560b5 100644
--- a/spec/requests/api/conan_instance_packages_spec.rb
+++ b/spec/requests/api/conan_instance_packages_spec.rb
@@ -2,8 +2,8 @@
require 'spec_helper'
-RSpec.describe API::ConanInstancePackages do
- let(:snowplow_standard_context_params) { { user: user, project: project, namespace: project.namespace } }
+RSpec.describe API::ConanInstancePackages, feature_category: :package_registry do
+ let(:snowplow_gitlab_standard_context) { { user: user, project: project, namespace: project.namespace, property: 'i_package_conan_user' } }
include_context 'conan api setup'
diff --git a/spec/requests/api/conan_project_packages_spec.rb b/spec/requests/api/conan_project_packages_spec.rb
index 4e6af9942ef..814745f9e29 100644
--- a/spec/requests/api/conan_project_packages_spec.rb
+++ b/spec/requests/api/conan_project_packages_spec.rb
@@ -1,11 +1,21 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe API::ConanProjectPackages do
+RSpec.describe API::ConanProjectPackages, feature_category: :package_registry do
include_context 'conan api setup'
let(:project_id) { project.id }
- let(:snowplow_standard_context_params) { { user: user, project: project, namespace: project.namespace } }
+
+ shared_examples 'accept get request on private project with access to package registry for everyone' do
+ subject { get api(url) }
+
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC)
+ end
+
+ it_behaves_like 'returning response status', :ok
+ end
describe 'GET /api/v4/projects/:id/packages/conan/v1/ping' do
let(:url) { "/projects/#{project.id}/packages/conan/v1/ping" }
@@ -41,43 +51,50 @@ RSpec.describe API::ConanProjectPackages do
include_context 'conan recipe endpoints'
let(:url_prefix) { "#{Settings.gitlab.base_url}/api/v4/projects/#{project_id}" }
+ let(:recipe_path) { package.conan_recipe_path }
+
+ subject { get api(url), headers: headers }
describe 'GET /api/v4/projects/:id/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel' do
- let(:recipe_path) { package.conan_recipe_path }
let(:url) { "/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}" }
it_behaves_like 'recipe snapshot endpoint'
+ it_behaves_like 'accept get request on private project with access to package registry for everyone'
end
describe 'GET /api/v4/projects/:id/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/packages/:conan_package_reference' do
- let(:recipe_path) { package.conan_recipe_path }
let(:url) { "/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}/packages/#{conan_package_reference}" }
it_behaves_like 'package snapshot endpoint'
+ it_behaves_like 'accept get request on private project with access to package registry for everyone'
end
describe 'GET /api/v4/projects/:id/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/digest' do
- subject { get api("/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}/digest"), headers: headers }
+ let(:url) { "/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}/digest" }
it_behaves_like 'recipe download_urls endpoint'
+ it_behaves_like 'accept get request on private project with access to package registry for everyone'
end
describe 'GET /api/v4/projects/:id/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/packages/:conan_package_reference/download_urls' do
- subject { get api("/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}/packages/#{conan_package_reference}/download_urls"), headers: headers }
+ let(:url) { "/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}/packages/#{conan_package_reference}/download_urls" }
it_behaves_like 'package download_urls endpoint'
+ it_behaves_like 'accept get request on private project with access to package registry for everyone'
end
describe 'GET /api/v4/projects/:id/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/download_urls' do
- subject { get api("/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}/download_urls"), headers: headers }
+ let(:url) { "/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}/download_urls" }
it_behaves_like 'recipe download_urls endpoint'
+ it_behaves_like 'accept get request on private project with access to package registry for everyone'
end
describe 'GET /api/v4/projects/:id/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/packages/:conan_package_reference/digest' do
- subject { get api("/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}/packages/#{conan_package_reference}/digest"), headers: headers }
+ let(:url) { "/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}/packages/#{conan_package_reference}/digest" }
it_behaves_like 'package download_urls endpoint'
+ it_behaves_like 'accept get request on private project with access to package registry for everyone'
end
describe 'POST /api/v4/projects/:id/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/upload_urls' do
@@ -102,24 +119,22 @@ RSpec.describe API::ConanProjectPackages do
context 'file download endpoints', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/326194' do
include_context 'conan file download endpoints'
+ subject { get api(url), headers: headers }
+
describe 'GET /api/v4/projects/:id/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/export/:file_name' do
- subject do
- get api("/projects/#{project_id}/packages/conan/v1/files/#{recipe_path}/#{metadata.recipe_revision}/export/#{recipe_file.file_name}"),
- headers: headers
- end
+ let(:url) { "/projects/#{project_id}/packages/conan/v1/files/#{recipe_path}/#{metadata.recipe_revision}/export/#{recipe_file.file_name}" }
it_behaves_like 'recipe file download endpoint'
it_behaves_like 'project not found by project id'
+ it_behaves_like 'accept get request on private project with access to package registry for everyone'
end
describe 'GET /api/v4/projects/:id/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name' do
- subject do
- get api("/projects/#{project_id}/packages/conan/v1/files/#{recipe_path}/#{metadata.recipe_revision}/package/#{metadata.conan_package_reference}/#{metadata.package_revision}/#{package_file.file_name}"),
- headers: headers
- end
+ let(:url) { "/projects/#{project_id}/packages/conan/v1/files/#{recipe_path}/#{metadata.recipe_revision}/package/#{metadata.conan_package_reference}/#{metadata.package_revision}/#{package_file.file_name}" }
it_behaves_like 'package file download endpoint'
it_behaves_like 'project not found by project id'
+ it_behaves_like 'accept get request on private project with access to package registry for everyone'
end
end
diff --git a/spec/requests/api/container_registry_event_spec.rb b/spec/requests/api/container_registry_event_spec.rb
index 767e6e0b2ff..32c4b0e9598 100644
--- a/spec/requests/api/container_registry_event_spec.rb
+++ b/spec/requests/api/container_registry_event_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ContainerRegistryEvent do
+RSpec.describe API::ContainerRegistryEvent, feature_category: :container_registry do
let(:secret_token) { 'secret_token' }
let(:events) { [{ action: 'push' }] }
let(:registry_headers) { { 'Content-Type' => ::API::ContainerRegistryEvent::DOCKER_DISTRIBUTION_EVENTS_V1_JSON } }
diff --git a/spec/requests/api/container_repositories_spec.rb b/spec/requests/api/container_repositories_spec.rb
index 90f0243dbfc..4c1e52df4fc 100644
--- a/spec/requests/api/container_repositories_spec.rb
+++ b/spec/requests/api/container_repositories_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ContainerRepositories do
+RSpec.describe API::ContainerRepositories, feature_category: :container_registry do
include_context 'container registry client stubs'
let_it_be(:project) { create(:project, :private) }
@@ -75,6 +75,13 @@ RSpec.describe API::ContainerRepositories do
expect(json_response['id']).to eq(repository.id)
expect(response.body).to include('tags')
+ expect(json_response['tags']).to eq(repository.tags.map do |tag|
+ {
+ "location" => tag.location,
+ "name" => tag.name,
+ "path" => tag.path
+ }
+ end)
end
context 'with a network error' do
diff --git a/spec/requests/api/debian_group_packages_spec.rb b/spec/requests/api/debian_group_packages_spec.rb
index 9dbb75becf8..f4d5ef3fe90 100644
--- a/spec/requests/api/debian_group_packages_spec.rb
+++ b/spec/requests/api/debian_group_packages_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe API::DebianGroupPackages do
+RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do
include HttpBasicAuthHelpers
include WorkhorseHelpers
diff --git a/spec/requests/api/debian_project_packages_spec.rb b/spec/requests/api/debian_project_packages_spec.rb
index 6bef669cb3a..c27e165b39b 100644
--- a/spec/requests/api/debian_project_packages_spec.rb
+++ b/spec/requests/api/debian_project_packages_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe API::DebianProjectPackages do
+RSpec.describe API::DebianProjectPackages, feature_category: :package_registry do
include HttpBasicAuthHelpers
include WorkhorseHelpers
diff --git a/spec/requests/api/dependency_proxy_spec.rb b/spec/requests/api/dependency_proxy_spec.rb
index 7af4ed08cb8..ef94cdbbe2b 100644
--- a/spec/requests/api/dependency_proxy_spec.rb
+++ b/spec/requests/api/dependency_proxy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::DependencyProxy, api: true do
+RSpec.describe API::DependencyProxy, api: true, feature_category: :dependency_proxy do
let_it_be(:user) { create(:user) }
let_it_be(:blob) { create(:dependency_proxy_blob) }
let_it_be(:group, reload: true) { blob.group }
diff --git a/spec/requests/api/deploy_keys_spec.rb b/spec/requests/api/deploy_keys_spec.rb
index 1daa7c38e04..15880d920c5 100644
--- a/spec/requests/api/deploy_keys_spec.rb
+++ b/spec/requests/api/deploy_keys_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::DeployKeys do
+RSpec.describe API::DeployKeys, feature_category: :continuous_delivery do
let_it_be(:user) { create(:user) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:admin) { create(:admin) }
diff --git a/spec/requests/api/deploy_tokens_spec.rb b/spec/requests/api/deploy_tokens_spec.rb
index e0296248a03..4efe49e843f 100644
--- a/spec/requests/api/deploy_tokens_spec.rb
+++ b/spec/requests/api/deploy_tokens_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::DeployTokens do
+RSpec.describe API::DeployTokens, feature_category: :continuous_delivery do
let_it_be(:user) { create(:user) }
let_it_be(:creator) { create(:user) }
let_it_be(:project) { create(:project, creator_id: creator.id) }
@@ -346,7 +346,7 @@ RSpec.describe API::DeployTokens do
context 'deploy token creation' do
shared_examples 'creating a deploy token' do |entity, unauthenticated_response, authorized_role|
- let(:expires_time) { 1.year.from_now }
+ let(:expires_time) { 1.year.from_now.to_datetime }
let(:params) do
{
name: 'Foo',
@@ -414,6 +414,14 @@ RSpec.describe API::DeployTokens do
it { is_expected.to have_gitlab_http_status(:bad_request) }
end
+
+ context 'with an invalid expires_at date' do
+ before do
+ params[:expires_at] = 'foo'
+ end
+
+ it { is_expected.to have_gitlab_http_status(:bad_request) }
+ end
end
end
diff --git a/spec/requests/api/deployments_spec.rb b/spec/requests/api/deployments_spec.rb
index 8124080abea..efe76c9cfda 100644
--- a/spec/requests/api/deployments_spec.rb
+++ b/spec/requests/api/deployments_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Deployments do
+RSpec.describe API::Deployments, feature_category: :continuous_delivery do
let_it_be(:user) { create(:user) }
let_it_be(:non_member) { create(:user) }
@@ -16,8 +16,8 @@ RSpec.describe API::Deployments do
let_it_be(:staging) { create(:environment, :staging, project: project) }
let_it_be(:build) { create(:ci_build, :success, project: project) }
let_it_be(:deployment_1) { create(:deployment, :success, project: project, environment: production, deployable: build, ref: 'master', created_at: Time.now, updated_at: Time.now) }
- let_it_be(:deployment_2) { create(:deployment, :success, project: project, environment: staging, deployable: build, ref: 'master', created_at: 1.day.ago, updated_at: 2.hours.ago) }
- let_it_be(:deployment_3) { create(:deployment, :success, project: project, environment: staging, deployable: build, ref: 'master', created_at: 2.days.ago, updated_at: 1.hour.ago) }
+ let_it_be(:deployment_2) { create(:deployment, :success, project: project, environment: staging, deployable: build, ref: 'master', created_at: 1.day.ago, finished_at: 2.hours.ago, updated_at: 2.hours.ago) }
+ let_it_be(:deployment_3) { create(:deployment, :success, project: project, environment: staging, deployable: build, ref: 'master', created_at: 2.days.ago, finished_at: 1.hour.ago, updated_at: 1.hour.ago) }
def perform_request(params = {})
get api("/projects/#{project.id}/deployments", user), params: params
@@ -47,7 +47,7 @@ RSpec.describe API::Deployments do
end
context 'when forbidden order_by is specified' do
- it 'returns projects deployments with last update in specified datetime range' do
+ it 'returns an error' do
perform_request({ updated_before: 30.minutes.ago, updated_after: 90.minutes.ago, order_by: :id })
expect(response).to have_gitlab_http_status(:bad_request)
@@ -56,6 +56,44 @@ RSpec.describe API::Deployments do
end
end
+ context 'with finished after and before filters specified' do
+ context 'for successful deployments' do
+ it 'returns projects deployments finished before the specified datetime range' do
+ perform_request({ status: :success, finished_before: 90.minutes.ago, order_by: :finished_at, environment: 'staging' })
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response.first['id']).to eq(deployment_2.id)
+ end
+
+ it 'returns projects deployments finished after the specified datetime range' do
+ perform_request({ status: :success, finished_after: 90.minutes.ago, order_by: :finished_at, environment: 'staging' })
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response.first['id']).to eq(deployment_3.id)
+ end
+ end
+
+ context 'for unsuccessful deployments' do
+ it 'returns an error' do
+ perform_request({ status: :failed, finished_before: 30.minutes.ago, order_by: :finished_at })
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to include('`finished_at` filter must be combined with `success` status filter.')
+ end
+ end
+
+ context 'when a forbidden order_by is specified' do
+ it 'returns an error' do
+ perform_request({ status: :success, finished_before: 30.minutes.ago, order_by: :id })
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to include('`finished_at` filter requires `finished_at` sort.')
+ end
+ end
+ end
+
context 'with the environment filter specifed' do
it 'returns deployments for the environment' do
perform_request({ environment: production.name })
diff --git a/spec/requests/api/discussions_spec.rb b/spec/requests/api/discussions_spec.rb
index 258bd26c05a..38016375b8f 100644
--- a/spec/requests/api/discussions_spec.rb
+++ b/spec/requests/api/discussions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Discussions do
+RSpec.describe API::Discussions, feature_category: :team_planning do
let(:user) { create(:user) }
let!(:project) { create(:project, :public, :repository, namespace: user.namespace) }
let(:private_user) { create(:user) }
@@ -29,6 +29,73 @@ RSpec.describe API::Discussions do
end
end
+ context 'when noteable is a WorkItem' do
+ let!(:work_item) { create(:work_item, :issue, project: project, author: user) }
+ let!(:work_item_note) { create(:discussion_note_on_issue, noteable: work_item, project: project, author: user) }
+
+ let(:parent) { project }
+ let(:noteable) { work_item }
+ let(:note) { work_item_note }
+ let(:url) { "/projects/#{parent.id}/issues/#{noteable[:iid]}/discussions" }
+
+ it_behaves_like 'discussions API', 'projects', 'issues', 'iid', can_reply_to_individual_notes: true
+
+ context 'with work item without notes widget' do
+ before do
+ stub_const('WorkItems::Type::BASE_TYPES', { issue: { name: 'NoNotesWidget', enum_value: 0 } })
+ stub_const('WorkItems::Type::WIDGETS_FOR_TYPE', { issue: [::WorkItems::Widgets::Description] })
+ end
+
+ context 'when fetching discussions' do
+ it "returns 404" do
+ get api(url, user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when single fetching discussion by discussion_id' do
+ it "returns 404" do
+ get api("#{url}/#{work_item_note.discussion_id}", user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when trying to create a new discussion' do
+ it "returns 404" do
+ post api(url, user), params: { body: 'hi!' }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when trying to create a new comment on a discussion' do
+ it 'returns 404' do
+ post api("#{url}/#{note.discussion_id}/notes", user), params: { body: 'Hello!' }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when trying to update a new comment on a discussion' do
+ it 'returns 404' do
+ put api("#{url}/notes/#{note.id}", user), params: { body: 'Update Hello!' }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when deleting a note' do
+ it 'returns 404' do
+ delete api("#{url}/#{note.discussion_id}/notes/#{note.id}", user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+ end
+
context 'when noteable is a Snippet' do
let!(:snippet) { create(:project_snippet, project: project, author: user) }
let!(:snippet_note) { create(:discussion_note_on_project_snippet, noteable: snippet, project: project, author: user) }
diff --git a/spec/requests/api/doorkeeper_access_spec.rb b/spec/requests/api/doorkeeper_access_spec.rb
index 14da9a600cd..5116f074894 100644
--- a/spec/requests/api/doorkeeper_access_spec.rb
+++ b/spec/requests/api/doorkeeper_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'doorkeeper access' do
+RSpec.describe 'doorkeeper access', feature_category: :authentication_and_authorization do
let!(:user) { create(:user) }
let!(:application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) }
let!(:token) { Doorkeeper::AccessToken.create! application_id: application.id, resource_owner_id: user.id, scopes: "api" }
diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb
index a35c1630caa..d06e70a1a02 100644
--- a/spec/requests/api/environments_spec.rb
+++ b/spec/requests/api/environments_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Environments do
+RSpec.describe API::Environments, feature_category: :continuous_delivery do
let_it_be(:user) { create(:user) }
let_it_be(:non_member) { create(:user) }
let_it_be(:project) { create(:project, :private, :repository, namespace: user.namespace) }
@@ -321,8 +321,8 @@ RSpec.describe API::Environments do
expect(json_response["scheduled_entries"].size).to eq(1)
expect(json_response["scheduled_entries"].first["id"]).to eq(old_stopped_review_env.id)
expect(json_response["unprocessable_entries"].size).to eq(0)
- expect(json_response["scheduled_entries"]).to match_schema('public_api/v4/environments')
- expect(json_response["unprocessable_entries"]).to match_schema('public_api/v4/environments')
+ expect(json_response["scheduled_entries"]).to match_schema('public_api/v4/basic_environments')
+ expect(json_response["unprocessable_entries"]).to match_schema('public_api/v4/basic_environments')
expect(old_stopped_review_env.reload.auto_delete_at).to eq(1.week.from_now)
expect(new_stopped_review_env.reload.auto_delete_at).to be_nil
diff --git a/spec/requests/api/error_tracking/client_keys_spec.rb b/spec/requests/api/error_tracking/client_keys_spec.rb
index ba4d713dff2..cb840e1cffa 100644
--- a/spec/requests/api/error_tracking/client_keys_spec.rb
+++ b/spec/requests/api/error_tracking/client_keys_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ErrorTracking::ClientKeys do
+RSpec.describe API::ErrorTracking::ClientKeys, feature_category: :error_tracking do
let_it_be(:guest) { create(:user) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:setting) { create(:project_error_tracking_setting) }
diff --git a/spec/requests/api/error_tracking/collector_spec.rb b/spec/requests/api/error_tracking/collector_spec.rb
index dfca994d1c3..6a3e71bc859 100644
--- a/spec/requests/api/error_tracking/collector_spec.rb
+++ b/spec/requests/api/error_tracking/collector_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ErrorTracking::Collector do
+RSpec.describe API::ErrorTracking::Collector, feature_category: :error_tracking do
let_it_be(:project) { create(:project, :private) }
let_it_be(:setting) { create(:project_error_tracking_setting, :integrated, project: project) }
let_it_be(:client_key) { create(:error_tracking_client_key, project: project) }
diff --git a/spec/requests/api/error_tracking/project_settings_spec.rb b/spec/requests/api/error_tracking/project_settings_spec.rb
index c0c0680ef31..5906cdf105a 100644
--- a/spec/requests/api/error_tracking/project_settings_spec.rb
+++ b/spec/requests/api/error_tracking/project_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ErrorTracking::ProjectSettings do
+RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tracking do
let_it_be(:user) { create(:user) }
let(:setting) { create(:project_error_tracking_setting) }
diff --git a/spec/requests/api/events_spec.rb b/spec/requests/api/events_spec.rb
index d6c3999f22f..5c061a37ff3 100644
--- a/spec/requests/api/events_spec.rb
+++ b/spec/requests/api/events_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Events do
+RSpec.describe API::Events, feature_category: :users do
let(:user) { create(:user) }
let(:non_member) { create(:user) }
let(:private_project) { create(:project, :private, creator_id: user.id, namespace: user.namespace) }
diff --git a/spec/requests/api/feature_flags_spec.rb b/spec/requests/api/feature_flags_spec.rb
index bf7eec167f5..69e3633de57 100644
--- a/spec/requests/api/feature_flags_spec.rb
+++ b/spec/requests/api/feature_flags_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe API::FeatureFlags do
+RSpec.describe API::FeatureFlags, feature_category: :feature_flags do
include FeatureFlagHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/feature_flags_user_lists_spec.rb b/spec/requests/api/feature_flags_user_lists_spec.rb
index bfc57042ff4..443cbbea147 100644
--- a/spec/requests/api/feature_flags_user_lists_spec.rb
+++ b/spec/requests/api/feature_flags_user_lists_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::FeatureFlagsUserLists do
+RSpec.describe API::FeatureFlagsUserLists, feature_category: :feature_flags do
let_it_be(:project, refind: true) { create(:project) }
let_it_be(:client, refind: true) { create(:operations_feature_flags_client, project: project) }
let_it_be(:developer) { create(:user) }
diff --git a/spec/requests/api/features_spec.rb b/spec/requests/api/features_spec.rb
index 85dafef569d..9f1af746080 100644
--- a/spec/requests/api/features_spec.rb
+++ b/spec/requests/api/features_spec.rb
@@ -2,8 +2,9 @@
require 'spec_helper'
-RSpec.describe API::Features, stub_feature_flags: false do
- let_it_be(:user) { create(:user) }
+RSpec.describe API::Features, stub_feature_flags: false, feature_category: :feature_flags do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:opted_out) { create(:user) }
let_it_be(:admin) { create(:admin) }
# Find any `development` feature flag name
@@ -35,7 +36,10 @@ RSpec.describe API::Features, stub_feature_flags: false do
{
'name' => 'feature_1',
'state' => 'on',
- 'gates' => [{ 'key' => 'boolean', 'value' => true }],
+ 'gates' => [
+ { 'key' => 'boolean', 'value' => true },
+ { 'key' => 'actors', 'value' => ["#{opted_out.flipper_id}:opt_out"] }
+ ],
'definition' => nil
},
{
@@ -64,6 +68,7 @@ RSpec.describe API::Features, stub_feature_flags: false do
before do
Feature.enable('feature_1')
+ Feature.opt_out('feature_1', opted_out)
Feature.disable('feature_2')
Feature.enable('feature_3', Feature.group(:perf_team))
Feature.enable(known_feature_flag.name)
@@ -654,12 +659,53 @@ RSpec.describe API::Features, stub_feature_flags: false do
it_behaves_like 'sets the feature flag status'
+ it 'opts given actors out' do
+ Feature.enable(feature_name)
+ expect(Feature.enabled?(feature_name, user)).to be_truthy
+
+ post api("/features/#{feature_name}", admin), params: { value: 'opt_out', user: user.username }
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response).to include(
+ 'name' => feature_name,
+ 'state' => 'on',
+ 'gates' => [
+ { 'key' => 'boolean', 'value' => true },
+ { 'key' => 'actors', 'value' => ["#{user.flipper_id}:opt_out"] }
+ ]
+ )
+ end
+
+ context 'when the actor has opted-out' do
+ before do
+ Feature.enable(feature_name)
+ Feature.opt_out(feature_name, user)
+ end
+
+ it 'refuses to enable the feature' do
+ post api("/features/#{feature_name}", admin), params: { value: 'true', user: user.username }
+
+ expect(Feature).not_to be_enabled(feature_name, user)
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
context 'when feature flag set_feature_flag_service is disabled' do
before do
stub_feature_flags(set_feature_flag_service: false)
end
it_behaves_like 'sets the feature flag status'
+
+ it 'rejects opt_out requests' do
+ Feature.enable(feature_name)
+ expect(Feature).to be_enabled(feature_name, user)
+
+ post api("/features/#{feature_name}", admin), params: { value: 'opt_out', user: user.username }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
end
end
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index d4d3aace204..9cee3c06bb1 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Files do
+RSpec.describe API::Files, feature_category: :source_code_management do
include RepoHelpers
let_it_be(:group) { create(:group, :public) }
@@ -11,8 +11,12 @@ RSpec.describe API::Files do
let_it_be(:inherited_reporter) { create(:user) }
let_it_be(:inherited_developer) { create(:user) }
- let!(:project) { create(:project, :repository, namespace: user.namespace) }
- let(:guest) { create(:user) { |u| project.add_guest(u) } }
+ let_it_be_with_reload(:project) { create(:project, :repository, namespace: user.namespace) }
+ let_it_be_with_reload(:public_project) { create(:project, :public, :repository) }
+ let_it_be_with_reload(:private_project) { create(:project, :private, :repository, group: group) }
+ let_it_be_with_reload(:public_project_private_repo) { create(:project, :public, :repository, :repository_private, group: group) }
+
+ let_it_be(:guest) { create(:user) { |u| project.add_guest(u) } }
let(:file_path) { 'files%2Fruby%2Fpopen%2Erb' }
let(:file_name) { 'popen.rb' }
let(:last_commit_id) { '570e7b2abdd848b95f2f578043fc23bd6f6fd24d' }
@@ -183,8 +187,9 @@ RSpec.describe API::Files do
context 'when unauthenticated' do
context 'and project is public' do
+ let(:project) { public_project }
+
it_behaves_like 'repository files' do
- let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
@@ -361,7 +366,7 @@ RSpec.describe API::Files do
context 'when unauthenticated' do
context 'and project is public' do
it_behaves_like 'repository files' do
- let(:project) { create(:project, :public, :repository) }
+ let(:project) { public_project }
let(:current_user) { nil }
let(:api_user) { nil }
end
@@ -406,7 +411,7 @@ RSpec.describe API::Files do
context 'when authenticated' do
context 'and user is an inherited member from the group' do
context 'when project is public with private repository' do
- let_it_be(:project) { create(:project, :public, :repository, :repository_private, group: group) }
+ let(:project) { public_project_private_repo }
context 'and user is a guest' do
it_behaves_like 'returns non-executable file attributes as json' do
@@ -428,7 +433,7 @@ RSpec.describe API::Files do
end
context 'when project is private' do
- let_it_be(:project) { create(:project, :private, :repository, group: group) }
+ let(:project) { private_project }
context 'and user is a guest' do
it_behaves_like '403 response' do
@@ -655,7 +660,7 @@ RSpec.describe API::Files do
context 'when unauthenticated' do
context 'and project is public' do
it_behaves_like 'repository blame files' do
- let(:project) { create(:project, :public, :repository) }
+ let(:project) { public_project }
let(:current_user) { nil }
end
end
@@ -774,12 +779,69 @@ RSpec.describe API::Files do
let(:request) { get api(route(file_path), current_user), params: params }
end
end
+
+ context 'when lfs parameter is true and the project has lfs enabled' do
+ before do
+ allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
+ project.update_attribute(:lfs_enabled, true)
+ end
+
+ let(:request) { get api(route(file_path) + '/raw', current_user), params: params.merge(lfs: true) }
+ let(:file_path) { 'files%2Flfs%2Flfs_object.iso' }
+
+ it_behaves_like '404 response'
+
+ context 'and the file has an lfs object' do
+ let_it_be(:lfs_object) { create(:lfs_object, :with_file, oid: '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897') }
+
+ it_behaves_like '404 response'
+
+ context 'and the project has access to the lfs object' do
+ before do
+ project.lfs_objects << lfs_object
+ end
+
+ context 'and lfs uses local file storage' do
+ before do
+ Grape::Endpoint.before_each do |endpoint|
+ allow(endpoint).to receive(:sendfile).with(lfs_object.file.path)
+ end
+ end
+
+ after do
+ Grape::Endpoint.before_each nil
+ end
+
+ it 'responds with the lfs object file' do
+ request
+ expect(response.headers["Content-Disposition"]).to eq(
+ "attachment; filename=\"#{lfs_object.file.filename}\"; filename*=UTF-8''#{lfs_object.file.filename}"
+ )
+ end
+ end
+
+ context 'and lfs uses remote object storage' do
+ before do
+ stub_lfs_object_storage
+ lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
+ end
+
+ it 'redirects to the lfs object file' do
+ request
+
+ expect(response).to have_gitlab_http_status(:found)
+ expect(response.location).to include(lfs_object.reload.file.path)
+ end
+ end
+ end
+ end
+ end
end
context 'when unauthenticated' do
context 'and project is public' do
it_behaves_like 'repository raw files' do
- let(:project) { create(:project, :public, :repository) }
+ let(:project) { public_project }
let(:current_user) { nil }
end
end
@@ -821,7 +883,7 @@ RSpec.describe API::Files do
end
describe 'POST /projects/:id/repository/files/:file_path' do
- let!(:file_path) { 'new_subfolder%2Fnewfile%2Erb' }
+ let(:file_path) { FFaker::Guid.guid }
let(:params) do
{
@@ -939,14 +1001,13 @@ RSpec.describe API::Files do
it_behaves_like 'creates a new file in the project repo' do
let(:current_user) { user }
- let(:file_path) { 'newfile%2Erb' }
+ let(:file_path) { FFaker::Guid.guid }
end
end
context 'when specifying an author' do
it 'creates a new file with the specified author' do
params.merge!(author_email: author_email, author_name: author_name)
-
post api(route('new_file_with_author%2Etxt'), user), params: params
expect(response).to have_gitlab_http_status(:created)
@@ -963,7 +1024,7 @@ RSpec.describe API::Files do
context 'when authenticated' do
context 'and user is an inherited member from the group' do
context 'when project is public with private repository' do
- let_it_be(:project) { create(:project, :public, :repository, :repository_private, group: group) }
+ let(:project) { public_project_private_repo }
context 'and user is a guest' do
it_behaves_like '403 response' do
@@ -985,7 +1046,7 @@ RSpec.describe API::Files do
end
context 'when project is private' do
- let_it_be(:project) { create(:project, :private, :repository, group: group) }
+ let(:project) { private_project }
context 'and user is a guest' do
it_behaves_like '403 response' do
@@ -1161,64 +1222,76 @@ RSpec.describe API::Files do
}
end
- it 'returns 400 when file path is invalid' do
- delete api(route(invalid_file_path), user), params: params
+ describe 'when files are deleted' do
+ let(:file_path) { FFaker::Guid.guid }
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['error']).to eq(invalid_file_message)
- end
+ before do
+ create_file_in_repo(project, 'master', 'master', file_path, 'Test file')
+ end
- it_behaves_like 'when path is absolute' do
- subject { delete api(route(absolute_path), user), params: params }
- end
+ it 'deletes existing file in project repo' do
+ delete api(route(file_path), user), params: params
- it 'deletes existing file in project repo' do
- delete api(route(file_path), user), params: params
+ expect(response).to have_gitlab_http_status(:no_content)
+ end
- expect(response).to have_gitlab_http_status(:no_content)
- end
+ context 'when specifying an author' do
+ before do
+ params.merge!(author_email: author_email, author_name: author_name)
+ end
- context 'when no params given' do
- it 'returns a 400 bad request' do
- delete api(route(file_path), user)
+ it 'removes a file with the specified author' do
+ delete api(route(file_path), user), params: params
- expect(response).to have_gitlab_http_status(:bad_request)
+ expect(response).to have_gitlab_http_status(:no_content)
+ end
end
end
- context 'when the commit message is empty' do
- before do
- params[:commit_message] = ''
+ describe 'when files are not deleted' do
+ it_behaves_like 'when path is absolute' do
+ subject { delete api(route(absolute_path), user), params: params }
end
- it 'returns a 400 bad request' do
- delete api(route(file_path), user), params: params
+ it 'returns 400 when file path is invalid' do
+ delete api(route(invalid_file_path), user), params: params
expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq(invalid_file_message)
end
- end
- context 'when fails to delete file' do
- before do
- allow_next_instance_of(Repository) do |instance|
- allow(instance).to receive(:delete_file).and_raise(Gitlab::Git::CommitError, 'Cannot delete file')
+ context 'when no params given' do
+ it 'returns a 400 bad request' do
+ delete api(route(file_path), user)
+
+ expect(response).to have_gitlab_http_status(:bad_request)
end
end
- it 'returns a 400 bad request' do
- delete api(route(file_path), user), params: params
+ context 'when the commit message is empty' do
+ before do
+ params[:commit_message] = ''
+ end
- expect(response).to have_gitlab_http_status(:bad_request)
+ it 'returns a 400 bad request' do
+ delete api(route(file_path), user), params: params
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
end
- end
- context 'when specifying an author' do
- it 'removes a file with the specified author' do
- params.merge!(author_email: author_email, author_name: author_name)
+ context 'when fails to delete file' do
+ before do
+ allow_next_instance_of(Repository) do |instance|
+ allow(instance).to receive(:delete_file).and_raise(Gitlab::Git::CommitError, 'Cannot delete file')
+ end
+ end
- delete api(route(file_path), user), params: params
+ it 'returns a 400 bad request' do
+ delete api(route(file_path), user), params: params
- expect(response).to have_gitlab_http_status(:no_content)
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
end
end
end
diff --git a/spec/requests/api/freeze_periods_spec.rb b/spec/requests/api/freeze_periods_spec.rb
index 3da992301d5..170871706dc 100644
--- a/spec/requests/api/freeze_periods_spec.rb
+++ b/spec/requests/api/freeze_periods_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::FreezePeriods do
+RSpec.describe API::FreezePeriods, feature_category: :continuous_delivery do
let_it_be(:project) { create(:project, :repository, :private) }
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
diff --git a/spec/requests/api/generic_packages_spec.rb b/spec/requests/api/generic_packages_spec.rb
index 0478e123086..6b3f378a4bc 100644
--- a/spec/requests/api/generic_packages_spec.rb
+++ b/spec/requests/api/generic_packages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::GenericPackages do
+RSpec.describe API::GenericPackages, feature_category: :package_registry do
include HttpBasicAuthHelpers
using RSpec::Parameterized::TableSyntax
@@ -19,7 +19,7 @@ RSpec.describe API::GenericPackages do
let(:user) { personal_access_token.user }
let(:ci_build) { create(:ci_build, :running, user: user, project: project) }
- let(:snowplow_standard_context_params) { { user: user, project: project, namespace: project.namespace } }
+ let(:snowplow_gitlab_standard_context) { { user: user, project: project, namespace: project.namespace, property: 'i_package_generic_user' } }
def auth_header
return {} if user_role == :anonymous
@@ -408,8 +408,6 @@ RSpec.describe API::GenericPackages do
end
context 'event tracking' do
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } }
-
subject { upload_file(params, workhorse_headers.merge(personal_access_token_header)) }
it_behaves_like 'a package tracking event', described_class.name, 'push_package'
@@ -645,8 +643,6 @@ RSpec.describe API::GenericPackages do
end
context 'event tracking' do
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } }
-
before do
project.add_developer(user)
end
diff --git a/spec/requests/api/geo_spec.rb b/spec/requests/api/geo_spec.rb
index 4e77fa9405c..3dec91fd2fa 100644
--- a/spec/requests/api/geo_spec.rb
+++ b/spec/requests/api/geo_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Geo do
+RSpec.describe API::Geo, feature_category: :geo_replication do
include WorkhorseHelpers
describe 'GET /geo/proxy' do
diff --git a/spec/requests/api/go_proxy_spec.rb b/spec/requests/api/go_proxy_spec.rb
index 5498ed6df13..17189087ade 100644
--- a/spec/requests/api/go_proxy_spec.rb
+++ b/spec/requests/api/go_proxy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::GoProxy do
+RSpec.describe API::GoProxy, feature_category: :package_registry do
include PackagesManagerApiSpecHelpers
include HttpBasicAuthHelpers
diff --git a/spec/requests/api/graphql/boards/board_list_issues_query_spec.rb b/spec/requests/api/graphql/boards/board_list_issues_query_spec.rb
index 9bed720c815..2775c3d4c5a 100644
--- a/spec/requests/api/graphql/boards/board_list_issues_query_spec.rb
+++ b/spec/requests/api/graphql/boards/board_list_issues_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'get board lists' do
+RSpec.describe 'get board lists', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/boards/board_list_query_spec.rb b/spec/requests/api/graphql/boards/board_list_query_spec.rb
index f01f7e87f10..b5ed0fe35d5 100644
--- a/spec/requests/api/graphql/boards/board_list_query_spec.rb
+++ b/spec/requests/api/graphql/boards/board_list_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Querying a Board list' do
+RSpec.describe 'Querying a Board list', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/boards/board_lists_query_spec.rb b/spec/requests/api/graphql/boards/board_lists_query_spec.rb
index ad7df5c9344..2f23e93e2c6 100644
--- a/spec/requests/api/graphql/boards/board_lists_query_spec.rb
+++ b/spec/requests/api/graphql/boards/board_lists_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'get board lists' do
+RSpec.describe 'get board lists', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/boards/boards_query_spec.rb b/spec/requests/api/graphql/boards/boards_query_spec.rb
index 50004e5a8a1..1407034fa5f 100644
--- a/spec/requests/api/graphql/boards/boards_query_spec.rb
+++ b/spec/requests/api/graphql/boards/boards_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'get list of boards' do
+RSpec.describe 'get list of boards', feature_category: :team_planning do
include GraphqlHelpers
include_context 'group and project boards query context'
diff --git a/spec/requests/api/graphql/ci/application_setting_spec.rb b/spec/requests/api/graphql/ci/application_setting_spec.rb
index 156ee550f16..42ab1786fee 100644
--- a/spec/requests/api/graphql/ci/application_setting_spec.rb
+++ b/spec/requests/api/graphql/ci/application_setting_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting Application Settings' do
+RSpec.describe 'getting Application Settings', feature_category: :continuous_integration do
include GraphqlHelpers
let(:fields) do
diff --git a/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb b/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb
index 2dc7b9764fe..0437a30eccd 100644
--- a/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb
+++ b/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'Getting Ci Cd Setting' do
+RSpec.describe 'Getting Ci Cd Setting', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be_with_reload(:project) { create(:project, :repository) }
diff --git a/spec/requests/api/graphql/ci/config_spec.rb b/spec/requests/api/graphql/ci/config_spec.rb
index 784019ee926..8154f132430 100644
--- a/spec/requests/api/graphql/ci/config_spec.rb
+++ b/spec/requests/api/graphql/ci/config_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.ciConfig' do
+RSpec.describe 'Query.ciConfig', feature_category: :continuous_integration do
include GraphqlHelpers
include StubRequests
diff --git a/spec/requests/api/graphql/ci/config_variables_spec.rb b/spec/requests/api/graphql/ci/config_variables_spec.rb
index 17133d7ea66..e6d73701b8f 100644
--- a/spec/requests/api/graphql/ci/config_variables_spec.rb
+++ b/spec/requests/api/graphql/ci/config_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.project(fullPath).ciConfigVariables(sha)' do
+RSpec.describe 'Query.project(fullPath).ciConfigVariables(sha)', feature_category: :pipeline_authoring do
include GraphqlHelpers
include ReactiveCachingHelpers
diff --git a/spec/requests/api/graphql/ci/group_variables_spec.rb b/spec/requests/api/graphql/ci/group_variables_spec.rb
index 7baf26c7648..51cbb4719f7 100644
--- a/spec/requests/api/graphql/ci/group_variables_spec.rb
+++ b/spec/requests/api/graphql/ci/group_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.group(fullPath).ciVariables' do
+RSpec.describe 'Query.group(fullPath).ciVariables', feature_category: :pipeline_authoring do
include GraphqlHelpers
let_it_be(:group) { create(:group) }
diff --git a/spec/requests/api/graphql/ci/groups_spec.rb b/spec/requests/api/graphql/ci/groups_spec.rb
index d1a4395d2c9..d1588833d8f 100644
--- a/spec/requests/api/graphql/ci/groups_spec.rb
+++ b/spec/requests/api/graphql/ci/groups_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'Query.project.pipeline.stages.groups' do
+RSpec.describe 'Query.project.pipeline.stages.groups', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository, :public) }
diff --git a/spec/requests/api/graphql/ci/instance_variables_spec.rb b/spec/requests/api/graphql/ci/instance_variables_spec.rb
index cd6b2de98a1..e0397e17923 100644
--- a/spec/requests/api/graphql/ci/instance_variables_spec.rb
+++ b/spec/requests/api/graphql/ci/instance_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.ciVariables' do
+RSpec.describe 'Query.ciVariables', feature_category: :pipeline_authoring do
include GraphqlHelpers
let(:query) do
diff --git a/spec/requests/api/graphql/ci/job_artifacts_spec.rb b/spec/requests/api/graphql/ci/job_artifacts_spec.rb
index df6e398fbe5..5fcb363d479 100644
--- a/spec/requests/api/graphql/ci/job_artifacts_spec.rb
+++ b/spec/requests/api/graphql/ci/job_artifacts_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.project(fullPath).pipelines.jobs.artifacts' do
+RSpec.describe 'Query.project(fullPath).pipelines.jobs.artifacts', feature_category: :build do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository, :public) }
diff --git a/spec/requests/api/graphql/ci/job_spec.rb b/spec/requests/api/graphql/ci/job_spec.rb
index 3721155c71b..8121c5e5c85 100644
--- a/spec/requests/api/graphql/ci/job_spec.rb
+++ b/spec/requests/api/graphql/ci/job_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.project(fullPath).pipelines.job(id)' do
+RSpec.describe 'Query.project(fullPath).pipelines.job(id)', feature_category: :continuous_integration do
include GraphqlHelpers
around do |example|
diff --git a/spec/requests/api/graphql/ci/jobs_spec.rb b/spec/requests/api/graphql/ci/jobs_spec.rb
index a161c5c98ed..7a1dc614dcf 100644
--- a/spec/requests/api/graphql/ci/jobs_spec.rb
+++ b/spec/requests/api/graphql/ci/jobs_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'Query.project.pipeline' do
+RSpec.describe 'Query.project.pipeline', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository, :public) }
@@ -367,4 +367,65 @@ RSpec.describe 'Query.project.pipeline' do
expect_graphql_errors_to_include [/"jobs" field can be requested only for 1 Project\(s\) at a time./]
end
end
+
+ context 'when batched querying jobs for multiple projects' do
+ let(:batched) do
+ [
+ { query: query_1 },
+ { query: query_2 }
+ ]
+ end
+
+ let(:query_1) do
+ %(
+ query Page1 {
+ projects {
+ nodes {
+ jobs {
+ nodes {
+ name
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ let(:query_2) do
+ %(
+ query Page2 {
+ projects {
+ nodes {
+ jobs {
+ nodes {
+ name
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ before do
+ create_list(:project, 2).each do |project|
+ project.add_developer(user)
+ create(:ci_build, project: project)
+ end
+ end
+
+ it 'limits the specific field evaluation per query' do
+ get_multiplex(batched, current_user: user)
+
+ resp = json_response
+
+ expect(resp.first.dig('data', 'projects', 'nodes').first.dig('jobs', 'nodes').first['name']).to eq('test')
+ expect(resp.first['errors'].first['message'])
+ .to match(/"jobs" field can be requested only for 1 Project\(s\) at a time./)
+ expect(resp.second.dig('data', 'projects', 'nodes').first.dig('jobs', 'nodes').first['name']).to eq('test')
+ expect(resp.second['errors'].first['message'])
+ .to match(/"jobs" field can be requested only for 1 Project\(s\) at a time./)
+ end
+ end
end
diff --git a/spec/requests/api/graphql/ci/manual_variables_spec.rb b/spec/requests/api/graphql/ci/manual_variables_spec.rb
index a15bac2b8bd..921c69e535d 100644
--- a/spec/requests/api/graphql/ci/manual_variables_spec.rb
+++ b/spec/requests/api/graphql/ci/manual_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.project(fullPath).pipelines.jobs.manualVariables' do
+RSpec.describe 'Query.project(fullPath).pipelines.jobs.manualVariables', feature_category: :pipeline_authoring do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/ci/pipeline_schedules_spec.rb b/spec/requests/api/graphql/ci/pipeline_schedules_spec.rb
index 8b8ba09a95c..76adce6ff1b 100644
--- a/spec/requests/api/graphql/ci/pipeline_schedules_spec.rb
+++ b/spec/requests/api/graphql/ci/pipeline_schedules_spec.rb
@@ -2,11 +2,11 @@
require 'spec_helper'
-RSpec.describe 'Query.project.pipelineSchedules' do
+RSpec.describe 'Query.project.pipelineSchedules', feature_category: :continuous_integration do
include GraphqlHelpers
- let_it_be(:project) { create(:project, :repository, :public) }
let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :repository, :public, creator: user, namespace: user.namespace) }
let_it_be(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: user) }
let(:pipeline_schedule_graphql_data) { graphql_data_at(:project, :pipeline_schedules, :nodes, 0) }
@@ -29,6 +29,8 @@ RSpec.describe 'Query.project.pipelineSchedules' do
forTag
cron
cronTimezone
+ editPath
+ variables { nodes { #{all_graphql_fields_for('PipelineScheduleVariable')} } }
}
QUERY
end
@@ -61,6 +63,58 @@ RSpec.describe 'Query.project.pipelineSchedules' do
expect(pipeline_schedule_graphql_data['refPath']).to eq("/#{project.full_path}/-/commits/#{ref_for_display}")
expect(pipeline_schedule_graphql_data['forTag']).to be(false)
end
+
+ it 'returns the edit_path for a pipeline schedule' do
+ edit_path = pipeline_schedule_graphql_data['editPath']
+
+ expect(edit_path).to eq("/#{project.full_path}/-/pipeline_schedules/#{pipeline_schedule.id}/edit")
+ end
+ end
+
+ describe 'variables' do
+ let!(:env_vars) { create_list(:ci_pipeline_schedule_variable, 5, pipeline_schedule: pipeline_schedule) }
+
+ it 'returns all variables' do
+ post_graphql(query, current_user: user)
+
+ variables = pipeline_schedule_graphql_data['variables']['nodes']
+ expected = env_vars.map do |var|
+ a_graphql_entity_for(var, :key, :value, variable_type: var.variable_type.upcase)
+ end
+
+ expect(variables).to match_array(expected)
+ end
+
+ it 'is N+1 safe on the variables level' do
+ baseline = ActiveRecord::QueryRecorder.new { post_graphql(query, current_user: user) }
+
+ create_list(:ci_pipeline_schedule_variable, 2, pipeline_schedule: pipeline_schedule)
+
+ expect { post_graphql(query, current_user: user) }.not_to exceed_query_limit(baseline)
+ end
+
+ it 'is N+1 safe on the schedules level' do
+ baseline = ActiveRecord::QueryRecorder.new { post_graphql(query, current_user: user) }
+
+ pipeline_schedule_2 = create(:ci_pipeline_schedule, project: project, owner: user)
+ create_list(:ci_pipeline_schedule_variable, 2, pipeline_schedule: pipeline_schedule_2)
+
+ expect { post_graphql(query, current_user: user) }.not_to exceed_query_limit(baseline)
+ end
+ end
+
+ describe 'permissions' do
+ let_it_be(:another_user) { create(:user) }
+
+ before do
+ post_graphql(query, current_user: another_user)
+ end
+
+ it 'does not return the edit_path for a pipeline schedule for a user that does not have permissions' do
+ edit_path = pipeline_schedule_graphql_data['editPath']
+
+ expect(edit_path).to be nil
+ end
end
it 'avoids N+1 queries' do
diff --git a/spec/requests/api/graphql/ci/pipelines_spec.rb b/spec/requests/api/graphql/ci/pipelines_spec.rb
index 948704e8770..9fe71533b5e 100644
--- a/spec/requests/api/graphql/ci/pipelines_spec.rb
+++ b/spec/requests/api/graphql/ci/pipelines_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.project(fullPath).pipelines' do
+RSpec.describe 'Query.project(fullPath).pipelines', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository, :public) }
diff --git a/spec/requests/api/graphql/ci/project_variables_spec.rb b/spec/requests/api/graphql/ci/project_variables_spec.rb
index d49a4a7e768..0338b58a0ea 100644
--- a/spec/requests/api/graphql/ci/project_variables_spec.rb
+++ b/spec/requests/api/graphql/ci/project_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.project(fullPath).ciVariables' do
+RSpec.describe 'Query.project(fullPath).ciVariables', feature_category: :pipeline_authoring do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb
index 94c0a3c41bd..ca08e780758 100644
--- a/spec/requests/api/graphql/ci/runner_spec.rb
+++ b/spec/requests/api/graphql/ci/runner_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.runner(id)' do
+RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
include GraphqlHelpers
let_it_be(:user) { create(:user, :admin) }
@@ -74,34 +74,39 @@ RSpec.describe 'Query.runner(id)' do
runner_data = graphql_data_at(:runner)
expect(runner_data).not_to be_nil
- expect(runner_data).to match a_hash_including(
- 'id' => runner.to_global_id.to_s,
- 'description' => runner.description,
- 'createdAt' => runner.created_at&.iso8601,
- 'contactedAt' => runner.contacted_at&.iso8601,
- 'version' => runner.version,
- 'shortSha' => runner.short_sha,
- 'revision' => runner.revision,
- 'locked' => false,
- 'active' => runner.active,
- 'paused' => !runner.active,
- 'status' => runner.status('14.5').to_s.upcase,
- 'maximumTimeout' => runner.maximum_timeout,
- 'accessLevel' => runner.access_level.to_s.upcase,
- 'runUntagged' => runner.run_untagged,
- 'ipAddress' => runner.ip_address,
- 'runnerType' => runner.instance_type? ? 'INSTANCE_TYPE' : 'PROJECT_TYPE',
- 'executorName' => runner.executor_type&.dasherize,
- 'architectureName' => runner.architecture,
- 'platformName' => runner.platform,
- 'maintenanceNote' => runner.maintenance_note,
- 'maintenanceNoteHtml' =>
+ expect(runner_data).to match a_graphql_entity_for(
+ runner,
+ description: runner.description,
+ created_at: runner.created_at&.iso8601,
+ contacted_at: runner.contacted_at&.iso8601,
+ version: runner.version,
+ short_sha: runner.short_sha,
+ revision: runner.revision,
+ locked: false,
+ active: runner.active,
+ paused: !runner.active,
+ status: runner.status('14.5').to_s.upcase,
+ job_execution_status: runner.builds.running.any? ? 'RUNNING' : 'IDLE',
+ maximum_timeout: runner.maximum_timeout,
+ access_level: runner.access_level.to_s.upcase,
+ run_untagged: runner.run_untagged,
+ ip_address: runner.ip_address,
+ runner_type: runner.instance_type? ? 'INSTANCE_TYPE' : 'PROJECT_TYPE',
+ executor_name: runner.executor_type&.dasherize,
+ architecture_name: runner.architecture,
+ platform_name: runner.platform,
+ maintenance_note: runner.maintenance_note,
+ maintenance_note_html:
runner.maintainer_note.present? ? a_string_including('<strong>Test maintenance note</strong>') : '',
- 'jobCount' => 0,
- 'jobs' => a_hash_including("count" => 0, "nodes" => [], "pageInfo" => anything),
- 'projectCount' => nil,
- 'adminUrl' => "http://localhost/admin/runners/#{runner.id}",
- 'userPermissions' => {
+ job_count: runner.builds.count,
+ jobs: a_hash_including(
+ "count" => runner.builds.count,
+ "nodes" => an_instance_of(Array),
+ "pageInfo" => anything
+ ),
+ project_count: nil,
+ admin_url: "http://localhost/admin/runners/#{runner.id}",
+ user_permissions: {
'readRunner' => true,
'updateRunner' => true,
'deleteRunner' => true,
@@ -129,10 +134,7 @@ RSpec.describe 'Query.runner(id)' do
runner_data = graphql_data_at(:runner)
expect(runner_data).not_to be_nil
- expect(runner_data).to match a_hash_including(
- 'id' => runner.to_global_id.to_s,
- 'adminUrl' => nil
- )
+ expect(runner_data).to match a_graphql_entity_for(runner, admin_url: nil)
expect(runner_data['tagList']).to match_array runner.tag_list
end
end
@@ -179,6 +181,16 @@ RSpec.describe 'Query.runner(id)' do
expect(runner_data).not_to include('tagList')
end
end
+
+ context 'with build running' do
+ before do
+ project = create(:project, :repository)
+ pipeline = create(:ci_pipeline, project: project)
+ create(:ci_build, :running, runner: runner, pipeline: pipeline)
+ end
+
+ it_behaves_like 'runner details fetch'
+ end
end
describe 'for project runner' do
@@ -216,9 +228,47 @@ RSpec.describe 'Query.runner(id)' do
runner_data = graphql_data_at(:runner)
- expect(runner_data).to match a_hash_including(
- 'id' => project_runner.to_global_id.to_s,
- 'locked' => is_locked
+ expect(runner_data).to match a_graphql_entity_for(project_runner, locked: is_locked)
+ end
+ end
+ end
+
+ describe 'jobCount' do
+ let_it_be(:pipeline1) { create(:ci_pipeline, project: project1) }
+ let_it_be(:pipeline2) { create(:ci_pipeline, project: project1) }
+ let_it_be(:build1) { create(:ci_build, :running, runner: active_project_runner, pipeline: pipeline1) }
+ let_it_be(:build2) { create(:ci_build, :running, runner: active_project_runner, pipeline: pipeline2) }
+
+ let(:runner_query_fragment) { 'id jobCount' }
+ let(:query) do
+ %(
+ query {
+ runner1: runner(id: "#{active_project_runner.to_global_id}") { #{runner_query_fragment} }
+ runner2: runner(id: "#{inactive_instance_runner.to_global_id}") { #{runner_query_fragment} }
+ }
+ )
+ end
+
+ it 'retrieves correct jobCount values' do
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data).to match a_hash_including(
+ 'runner1' => a_graphql_entity_for(active_project_runner, job_count: 2),
+ 'runner2' => a_graphql_entity_for(inactive_instance_runner, job_count: 0)
+ )
+ end
+
+ context 'when JOB_COUNT_LIMIT is in effect' do
+ before do
+ stub_const('Types::Ci::RunnerType::JOB_COUNT_LIMIT', 0)
+ end
+
+ it 'retrieves correct capped jobCount values' do
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data).to match a_hash_including(
+ 'runner1' => a_graphql_entity_for(active_project_runner, job_count: 1),
+ 'runner2' => a_graphql_entity_for(inactive_instance_runner, job_count: 0)
)
end
end
@@ -243,18 +293,8 @@ RSpec.describe 'Query.runner(id)' do
post_graphql(query, current_user: user)
expect(graphql_data).to match a_hash_including(
- 'runner1' => {
- 'id' => runner1.to_global_id.to_s,
- 'ownerProject' => {
- 'id' => project2.to_global_id.to_s
- }
- },
- 'runner2' => {
- 'id' => runner2.to_global_id.to_s,
- 'ownerProject' => {
- 'id' => project1.to_global_id.to_s
- }
- }
+ 'runner1' => a_graphql_entity_for(runner1, owner_project: a_graphql_entity_for(project2)),
+ 'runner2' => a_graphql_entity_for(runner2, owner_project: a_graphql_entity_for(project1))
)
end
end
@@ -284,8 +324,8 @@ RSpec.describe 'Query.runner(id)' do
it 'retrieves groups field with expected value' do
post_graphql(query, current_user: user)
- runner_data = graphql_data_at(:runner, :groups)
- expect(runner_data).to eq 'nodes' => [{ 'id' => group.to_global_id.to_s }]
+ runner_data = graphql_data_at(:runner, :groups, :nodes)
+ expect(runner_data).to contain_exactly(a_graphql_entity_for(group))
end
end
@@ -409,13 +449,13 @@ RSpec.describe 'Query.runner(id)' do
'jobCount' => 1,
'jobs' => a_hash_including(
"count" => 1,
- "nodes" => [{ "id" => job.to_global_id.to_s, "status" => job.status.upcase }]
+ "nodes" => [a_graphql_entity_for(job, status: job.status.upcase)]
),
'projectCount' => 2,
'projects' => {
'nodes' => [
- { 'id' => project1.to_global_id.to_s },
- { 'id' => project2.to_global_id.to_s }
+ a_graphql_entity_for(project1),
+ a_graphql_entity_for(project2)
]
})
expect(runner2_data).to match a_hash_including(
@@ -486,15 +526,24 @@ RSpec.describe 'Query.runner(id)' do
groups {
nodes {
id
+ path
+ fullPath
+ webUrl
}
}
projects {
nodes {
id
+ path
+ fullPath
+ webUrl
}
}
ownerProject {
id
+ path
+ fullPath
+ webUrl
}
}
SINGLE
@@ -503,8 +552,8 @@ RSpec.describe 'Query.runner(id)' do
let(:active_project_runner2) { create(:ci_runner, :project) }
let(:active_group_runner2) { create(:ci_runner, :group) }
- # Currently excluding known N+1 issues, see https://gitlab.com/gitlab-org/gitlab/-/issues/334759
- let(:excluded_fields) { %w[jobCount groups projects ownerProject] }
+ # Exclude fields that are already hardcoded above
+ let(:excluded_fields) { %w[jobs groups projects ownerProject] }
let(:single_query) do
<<~QUERY
@@ -542,27 +591,98 @@ RSpec.describe 'Query.runner(id)' do
expect(graphql_data.count).to eq 6
expect(graphql_data).to match(
a_hash_including(
- 'instance_runner1' => a_hash_including('id' => active_instance_runner.to_global_id.to_s),
- 'instance_runner2' => a_hash_including('id' => inactive_instance_runner.to_global_id.to_s),
- 'group_runner1' => a_hash_including(
- 'id' => active_group_runner.to_global_id.to_s,
- 'groups' => { 'nodes' => [a_hash_including('id' => group.to_global_id.to_s)] }
+ 'instance_runner1' => a_graphql_entity_for(active_instance_runner),
+ 'instance_runner2' => a_graphql_entity_for(inactive_instance_runner),
+ 'group_runner1' => a_graphql_entity_for(
+ active_group_runner,
+ groups: { 'nodes' => contain_exactly(a_graphql_entity_for(group)) }
),
- 'group_runner2' => a_hash_including(
- 'id' => active_group_runner2.to_global_id.to_s,
- 'groups' => { 'nodes' => [a_hash_including('id' => active_group_runner2.groups[0].to_global_id.to_s)] }
+ 'group_runner2' => a_graphql_entity_for(
+ active_group_runner2,
+ groups: { 'nodes' => active_group_runner2.groups.map { |g| a_graphql_entity_for(g) } }
),
- 'project_runner1' => a_hash_including(
- 'id' => active_project_runner.to_global_id.to_s,
- 'projects' => { 'nodes' => [a_hash_including('id' => active_project_runner.projects[0].to_global_id.to_s)] },
- 'ownerProject' => a_hash_including('id' => active_project_runner.projects[0].to_global_id.to_s)
+ 'project_runner1' => a_graphql_entity_for(
+ active_project_runner,
+ projects: { 'nodes' => active_project_runner.projects.map { |p| a_graphql_entity_for(p) } },
+ owner_project: a_graphql_entity_for(active_project_runner.projects[0])
),
- 'project_runner2' => a_hash_including(
- 'id' => active_project_runner2.to_global_id.to_s,
- 'projects' => {
- 'nodes' => [a_hash_including('id' => active_project_runner2.projects[0].to_global_id.to_s)]
- },
- 'ownerProject' => a_hash_including('id' => active_project_runner2.projects[0].to_global_id.to_s)
+ 'project_runner2' => a_graphql_entity_for(
+ active_project_runner2,
+ projects: { 'nodes' => active_project_runner2.projects.map { |p| a_graphql_entity_for(p) } },
+ owner_project: a_graphql_entity_for(active_project_runner2.projects[0])
+ )
+ ))
+ end
+ end
+
+ describe 'Query limits with jobs' do
+ let!(:group1) { create(:group) }
+ let!(:group2) { create(:group) }
+ let!(:project1) { create(:project, :repository, group: group1) }
+ let!(:project2) { create(:project, :repository, group: group1) }
+ let!(:project3) { create(:project, :repository, group: group2) }
+
+ let!(:merge_request1) { create(:merge_request, source_project: project1) }
+ let!(:merge_request2) { create(:merge_request, source_project: project3) }
+
+ let(:project_runner2) { create(:ci_runner, :project, projects: [project1, project2]) }
+ let!(:build1) { create(:ci_build, :success, name: 'Build One', runner: project_runner2, pipeline: pipeline1) }
+ let!(:pipeline1) do
+ create(:ci_pipeline, project: project1, source: :merge_request_event, merge_request: merge_request1, ref: 'main',
+ target_sha: 'xxx')
+ end
+
+ let(:query) do
+ <<~QUERY
+ {
+ runner(id: "#{project_runner2.to_global_id}") {
+ id
+ jobs {
+ nodes {
+ id
+ detailedStatus {
+ id
+ detailsPath
+ group
+ icon
+ text
+ }
+ shortSha
+ commitPath
+ finishedAt
+ duration
+ queuedDuration
+ tags
+ }
+ }
+ }
+ }
+ QUERY
+ end
+
+ it 'does not execute more queries per job', :aggregate_failures do
+ # warm-up license cache and so on:
+ personal_access_token = create(:personal_access_token, user: user)
+ args = { current_user: user, token: { personal_access_token: personal_access_token } }
+ post_graphql(query, **args)
+
+ control = ActiveRecord::QueryRecorder.new(query_recorder_debug: true) { post_graphql(query, **args) }
+
+ # Add a new build to project_runner2
+ project_runner2.runner_projects << build(:ci_runner_project, runner: project_runner2, project: project3)
+ pipeline2 = create(:ci_pipeline, project: project3, source: :merge_request_event, merge_request: merge_request2,
+ ref: 'main', target_sha: 'xxx')
+ build2 = create(:ci_build, :success, name: 'Build Two', runner: project_runner2, pipeline: pipeline2)
+
+ args[:current_user] = create(:user, :admin) # do not reuse same user
+ expect { post_graphql(query, **args) }.not_to exceed_all_query_limit(control)
+
+ expect(graphql_data.count).to eq 1
+ expect(graphql_data).to match(
+ a_hash_including(
+ 'runner' => a_graphql_entity_for(
+ project_runner2,
+ jobs: { 'nodes' => containing_exactly(a_graphql_entity_for(build1), a_graphql_entity_for(build2)) }
)
))
end
diff --git a/spec/requests/api/graphql/ci/runner_web_url_edge_spec.rb b/spec/requests/api/graphql/ci/runner_web_url_edge_spec.rb
index 767e958ea82..e84a1ca4cc4 100644
--- a/spec/requests/api/graphql/ci/runner_web_url_edge_spec.rb
+++ b/spec/requests/api/graphql/ci/runner_web_url_edge_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'RunnerWebUrlEdge' do
+RSpec.describe 'RunnerWebUrlEdge', feature_category: :runner_fleet do
include GraphqlHelpers
describe 'inside a Query.group' do
diff --git a/spec/requests/api/graphql/ci/runners_spec.rb b/spec/requests/api/graphql/ci/runners_spec.rb
index 3054b866812..75d8609dc38 100644
--- a/spec/requests/api/graphql/ci/runners_spec.rb
+++ b/spec/requests/api/graphql/ci/runners_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'Query.runners' do
+RSpec.describe 'Query.runners', feature_category: :runner_fleet do
include GraphqlHelpers
let_it_be(:current_user) { create_default(:user, :admin) }
@@ -48,7 +48,7 @@ RSpec.describe 'Query.runners' do
it_behaves_like 'a working graphql query'
it 'returns expected runner' do
- expect(runners_graphql_data['nodes'].map { |n| n['id'] }).to contain_exactly(expected_runner.to_global_id.to_s)
+ expect(runners_graphql_data['nodes']).to contain_exactly(a_graphql_entity_for(expected_runner))
end
end
diff --git a/spec/requests/api/graphql/ci/stages_spec.rb b/spec/requests/api/graphql/ci/stages_spec.rb
index 1edd6e58486..f4e1a69d455 100644
--- a/spec/requests/api/graphql/ci/stages_spec.rb
+++ b/spec/requests/api/graphql/ci/stages_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'Query.project.pipeline.stages' do
+RSpec.describe 'Query.project.pipeline.stages', feature_category: :continuous_integration do
include GraphqlHelpers
subject(:post_query) { post_graphql(query, current_user: user) }
diff --git a/spec/requests/api/graphql/ci/template_spec.rb b/spec/requests/api/graphql/ci/template_spec.rb
index 1bbef7d7f30..aaec219f734 100644
--- a/spec/requests/api/graphql/ci/template_spec.rb
+++ b/spec/requests/api/graphql/ci/template_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'Querying CI template' do
+RSpec.describe 'Querying CI template', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:project) { create(:project, :public) }
diff --git a/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb b/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb
index 14c55e61a65..88f63fd59d7 100644
--- a/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb
+++ b/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'container repository details' do
+RSpec.describe 'container repository details', feature_category: :container_registry do
include_context 'container registry tags'
include_context 'container registry client stubs'
diff --git a/spec/requests/api/graphql/crm/contacts_spec.rb b/spec/requests/api/graphql/crm/contacts_spec.rb
index a676e92dc3b..3ae19de63ed 100644
--- a/spec/requests/api/graphql/crm/contacts_spec.rb
+++ b/spec/requests/api/graphql/crm/contacts_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting CRM contacts' do
+RSpec.describe 'getting CRM contacts', feature_category: :service_desk do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/current_user/groups_query_spec.rb b/spec/requests/api/graphql/current_user/groups_query_spec.rb
index 6e36beb2afc..151d07ff0a7 100644
--- a/spec/requests/api/graphql/current_user/groups_query_spec.rb
+++ b/spec/requests/api/graphql/current_user/groups_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query current user groups' do
+RSpec.describe 'Query current user groups', feature_category: :subgroups do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/current_user/todos_query_spec.rb b/spec/requests/api/graphql/current_user/todos_query_spec.rb
index 5a45f0db518..f7e23aeb241 100644
--- a/spec/requests/api/graphql/current_user/todos_query_spec.rb
+++ b/spec/requests/api/graphql/current_user/todos_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query current user todos' do
+RSpec.describe 'Query current user todos', feature_category: :source_code_management do
include GraphqlHelpers
include DesignManagementTestHelpers
@@ -19,7 +19,7 @@ RSpec.describe 'Query current user todos' do
let(:fields) do
<<~QUERY
nodes {
- #{all_graphql_fields_for('todos'.classify)}
+ #{all_graphql_fields_for('todos'.classify, max_depth: 2)}
}
QUERY
end
diff --git a/spec/requests/api/graphql/current_user_query_spec.rb b/spec/requests/api/graphql/current_user_query_spec.rb
index 086a57094ca..53d2580caee 100644
--- a/spec/requests/api/graphql/current_user_query_spec.rb
+++ b/spec/requests/api/graphql/current_user_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting project information' do
+RSpec.describe 'getting project information', feature_category: :authentication_and_authorization do
include GraphqlHelpers
let(:fields) do
diff --git a/spec/requests/api/graphql/current_user_todos_spec.rb b/spec/requests/api/graphql/current_user_todos_spec.rb
index da1c893ec2b..eaed51982e1 100644
--- a/spec/requests/api/graphql/current_user_todos_spec.rb
+++ b/spec/requests/api/graphql/current_user_todos_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'A Todoable that implements the CurrentUserTodos interface' do
+RSpec.describe 'A Todoable that implements the CurrentUserTodos interface',
+feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/custom_emoji_query_spec.rb b/spec/requests/api/graphql/custom_emoji_query_spec.rb
index 5dd5ad117b0..7b804623e01 100644
--- a/spec/requests/api/graphql/custom_emoji_query_spec.rb
+++ b/spec/requests/api/graphql/custom_emoji_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting custom emoji within namespace' do
+RSpec.describe 'getting custom emoji within namespace', feature_category: :not_owned do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/environments/deployments_query_spec.rb b/spec/requests/api/graphql/environments/deployments_query_spec.rb
deleted file mode 100644
index 6da00057449..00000000000
--- a/spec/requests/api/graphql/environments/deployments_query_spec.rb
+++ /dev/null
@@ -1,487 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Environments Deployments query' do
- include GraphqlHelpers
-
- let_it_be(:project) { create(:project, :private, :repository) }
- let_it_be(:environment) { create(:environment, project: project) }
- let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
- let_it_be(:guest) { create(:user).tap { |u| project.add_guest(u) } }
-
- let(:user) { developer }
-
- subject { GitlabSchema.execute(query, context: { current_user: user }).as_json }
-
- context 'when there are deployments in the environment' do
- let_it_be(:finished_deployment_old) do
- create(:deployment, :success, environment: environment, project: project, finished_at: 2.days.ago)
- end
-
- let_it_be(:finished_deployment_new) do
- create(:deployment, :success, environment: environment, project: project, finished_at: 1.day.ago)
- end
-
- let_it_be(:upcoming_deployment_old) do
- create(:deployment, :created, environment: environment, project: project, created_at: 2.hours.ago)
- end
-
- let_it_be(:upcoming_deployment_new) do
- create(:deployment, :created, environment: environment, project: project, created_at: 1.hour.ago)
- end
-
- let_it_be(:other_environment) { create(:environment, project: project) }
- let_it_be(:other_deployment) { create(:deployment, :success, environment: other_environment, project: project) }
-
- let(:query) do
- %(
- query {
- project(fullPath: "#{project.full_path}") {
- environment(name: "#{environment.name}") {
- deployments {
- nodes {
- id
- iid
- ref
- tag
- sha
- createdAt
- updatedAt
- finishedAt
- status
- }
- }
- }
- }
- }
- )
- end
-
- it 'returns all deployments of the environment' do
- deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
-
- expect(deployments.count).to eq(4)
- end
-
- context 'when query last deployment' do
- let(:query) do
- %(
- query {
- project(fullPath: "#{project.full_path}") {
- environment(name: "#{environment.name}") {
- deployments(statuses: [SUCCESS], orderBy: { finishedAt: DESC }, first: 1) {
- nodes {
- iid
- }
- }
- }
- }
- }
- )
- end
-
- it 'returns deployment' do
- deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
-
- expect(deployments.count).to eq(1)
- expect(deployments[0]['iid']).to eq(finished_deployment_new.iid.to_s)
- end
- end
-
- context 'when query latest upcoming deployment' do
- let(:query) do
- %(
- query {
- project(fullPath: "#{project.full_path}") {
- environment(name: "#{environment.name}") {
- deployments(statuses: [CREATED RUNNING BLOCKED], orderBy: { createdAt: DESC }, first: 1) {
- nodes {
- iid
- }
- }
- }
- }
- }
- )
- end
-
- it 'returns deployment' do
- deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
-
- expect(deployments.count).to eq(1)
- expect(deployments[0]['iid']).to eq(upcoming_deployment_new.iid.to_s)
- end
- end
-
- context 'when query finished deployments in descending order' do
- let(:query) do
- %(
- query {
- project(fullPath: "#{project.full_path}") {
- environment(name: "#{environment.name}") {
- deployments(statuses: [SUCCESS FAILED CANCELED], orderBy: { finishedAt: DESC }) {
- nodes {
- iid
- }
- }
- }
- }
- }
- )
- end
-
- it 'returns deployments' do
- deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
-
- expect(deployments.count).to eq(2)
- expect(deployments[0]['iid']).to eq(finished_deployment_new.iid.to_s)
- expect(deployments[1]['iid']).to eq(finished_deployment_old.iid.to_s)
- end
- end
-
- context 'when query finished deployments in ascending order' do
- let(:query) do
- %(
- query {
- project(fullPath: "#{project.full_path}") {
- environment(name: "#{environment.name}") {
- deployments(statuses: [SUCCESS FAILED CANCELED], orderBy: { finishedAt: ASC }) {
- nodes {
- iid
- }
- }
- }
- }
- }
- )
- end
-
- it 'returns deployments' do
- deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
-
- expect(deployments.count).to eq(2)
- expect(deployments[0]['iid']).to eq(finished_deployment_old.iid.to_s)
- expect(deployments[1]['iid']).to eq(finished_deployment_new.iid.to_s)
- end
- end
-
- context 'when query upcoming deployments in descending order' do
- let(:query) do
- %(
- query {
- project(fullPath: "#{project.full_path}") {
- environment(name: "#{environment.name}") {
- deployments(statuses: [CREATED RUNNING BLOCKED], orderBy: { createdAt: DESC }) {
- nodes {
- iid
- }
- }
- }
- }
- }
- )
- end
-
- it 'returns deployments' do
- deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
-
- expect(deployments.count).to eq(2)
- expect(deployments[0]['iid']).to eq(upcoming_deployment_new.iid.to_s)
- expect(deployments[1]['iid']).to eq(upcoming_deployment_old.iid.to_s)
- end
- end
-
- context 'when query upcoming deployments in ascending order' do
- let(:query) do
- %(
- query {
- project(fullPath: "#{project.full_path}") {
- environment(name: "#{environment.name}") {
- deployments(statuses: [CREATED RUNNING BLOCKED], orderBy: { createdAt: ASC }) {
- nodes {
- iid
- }
- }
- }
- }
- }
- )
- end
-
- it 'returns deployments' do
- deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
-
- expect(deployments.count).to eq(2)
- expect(deployments[0]['iid']).to eq(upcoming_deployment_old.iid.to_s)
- expect(deployments[1]['iid']).to eq(upcoming_deployment_new.iid.to_s)
- end
- end
-
- context 'when query last deployments of multiple environments' do
- let(:query) do
- %(
- query {
- project(fullPath: "#{project.full_path}") {
- environments {
- nodes {
- name
- deployments(statuses: [SUCCESS], orderBy: { finishedAt: DESC }, first: 1) {
- nodes {
- iid
- }
- }
- }
- }
- }
- }
- )
- end
-
- it 'returns an error for preventing N+1 queries' do
- expect(subject['errors'][0]['message'])
- .to include('"deployments" field can be requested only for 1 Environment(s) at a time.')
- end
- end
-
- context 'when query finished and upcoming deployments together' do
- let(:query) do
- %(
- query {
- project(fullPath: "#{project.full_path}") {
- environment(name: "#{environment.name}") {
- deployments(statuses: [CREATED SUCCESS]) {
- nodes {
- iid
- }
- }
- }
- }
- }
- )
- end
-
- it 'raises an error' do
- expect { subject }.to raise_error(DeploymentsFinder::InefficientQueryError)
- end
- end
-
- context 'when multiple orderBy input are specified' do
- let(:query) do
- %(
- query {
- project(fullPath: "#{project.full_path}") {
- environment(name: "#{environment.name}") {
- deployments(orderBy: { finishedAt: DESC, createdAt: ASC }) {
- nodes {
- iid
- }
- }
- }
- }
- }
- )
- end
-
- it 'raises an error' do
- expect(subject['errors'][0]['message']).to include('orderBy parameter must contain one key-value pair.')
- end
- end
-
- context 'when user is guest' do
- let(:user) { guest }
-
- it 'returns nothing' do
- expect(subject['data']['project']['environment']).to be_nil
- end
- end
-
- shared_examples_for 'avoids N+1 database queries' do
- it 'does not increase the query count' do
- create_deployments
-
- baseline = ActiveRecord::QueryRecorder.new do
- run_with_clean_state(query, context: { current_user: user })
- end
-
- create_deployments
-
- multi = ActiveRecord::QueryRecorder.new do
- run_with_clean_state(query, context: { current_user: user })
- end
-
- expect(multi).not_to exceed_query_limit(baseline)
- end
-
- def create_deployments
- create_list(:deployment, 3, environment: environment, project: project).each do |deployment|
- deployment.user = create(:user).tap { |u| project.add_developer(u) }
- deployment.deployable =
- create(:ci_build, project: project, environment: environment.name, deployment: deployment,
- user: deployment.user)
-
- deployment.save!
- end
- end
- end
-
- context 'when requesting commits of deployments' do
- let(:query) do
- %(
- query {
- project(fullPath: "#{project.full_path}") {
- environment(name: "#{environment.name}") {
- deployments {
- nodes {
- iid
- commit {
- author {
- avatarUrl
- name
- webPath
- }
- fullTitle
- webPath
- sha
- }
- }
- }
- }
- }
- }
- )
- end
-
- it_behaves_like 'avoids N+1 database queries'
-
- it 'returns commits of deployments' do
- deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
-
- deployments.each do |deployment|
- deployment_in_record = project.deployments.find_by_iid(deployment['iid'])
-
- expect(deployment_in_record.sha).to eq(deployment['commit']['sha'])
- end
- end
- end
-
- context 'when requesting triggerers of deployments' do
- let(:query) do
- %(
- query {
- project(fullPath: "#{project.full_path}") {
- environment(name: "#{environment.name}") {
- deployments {
- nodes {
- iid
- triggerer {
- id
- avatarUrl
- name
- webPath
- }
- }
- }
- }
- }
- }
- )
- end
-
- it_behaves_like 'avoids N+1 database queries'
-
- it 'returns triggerers of deployments' do
- deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
-
- deployments.each do |deployment|
- deployment_in_record = project.deployments.find_by_iid(deployment['iid'])
-
- expect(deployment_in_record.deployed_by.name).to eq(deployment['triggerer']['name'])
- end
- end
- end
-
- context 'when requesting jobs of deployments' do
- let(:query) do
- %(
- query {
- project(fullPath: "#{project.full_path}") {
- environment(name: "#{environment.name}") {
- deployments {
- nodes {
- iid
- job {
- id
- status
- name
- webPath
- }
- }
- }
- }
- }
- }
- )
- end
-
- it_behaves_like 'avoids N+1 database queries'
-
- it 'returns jobs of deployments' do
- deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
-
- deployments.each do |deployment|
- deployment_in_record = project.deployments.find_by_iid(deployment['iid'])
-
- expect(deployment_in_record.build.to_global_id.to_s).to eq(deployment['job']['id'])
- end
- end
- end
-
- describe 'sorting and pagination' do
- let(:data_path) { [:project, :environment, :deployments] }
- let(:current_user) { user }
-
- def pagination_query(params)
- %(
- query {
- project(fullPath: "#{project.full_path}") {
- environment(name: "#{environment.name}") {
- deployments(statuses: [SUCCESS], #{params}) {
- nodes {
- iid
- }
- pageInfo {
- startCursor
- endCursor
- hasNextPage
- hasPreviousPage
- }
- }
- }
- }
- }
- )
- end
-
- def pagination_results_data(nodes)
- nodes.map { |deployment| deployment['iid'].to_i }
- end
-
- context 'when sorting by finished_at in ascending order' do
- it_behaves_like 'sorted paginated query' do
- let(:sort_argument) { graphql_args(orderBy: { finishedAt: :ASC }) }
- let(:first_param) { 2 }
- let(:all_records) { [finished_deployment_old.iid, finished_deployment_new.iid] }
- end
- end
-
- context 'when sorting by finished_at in descending order' do
- it_behaves_like 'sorted paginated query' do
- let(:sort_argument) { graphql_args(orderBy: { finishedAt: :DESC }) }
- let(:first_param) { 2 }
- let(:all_records) { [finished_deployment_new.iid, finished_deployment_old.iid] }
- end
- end
- end
- end
-end
diff --git a/spec/requests/api/graphql/environments/deployments_spec.rb b/spec/requests/api/graphql/environments/deployments_spec.rb
new file mode 100644
index 00000000000..0022a38d2d3
--- /dev/null
+++ b/spec/requests/api/graphql/environments/deployments_spec.rb
@@ -0,0 +1,524 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Environments Deployments query', feature_category: :continuous_delivery do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project, :private, :repository) }
+ let_it_be(:environment) { create(:environment, project: project) }
+ let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
+ let_it_be(:guest) { create(:user).tap { |u| project.add_guest(u) } }
+
+ let(:user) { developer }
+
+ subject { GitlabSchema.execute(query, context: { current_user: user }).as_json }
+
+ context 'when there are deployments in the environment' do
+ let_it_be(:finished_deployment_old) do
+ create(:deployment, :success, environment: environment, project: project, finished_at: 2.days.ago)
+ end
+
+ let_it_be(:finished_deployment_new) do
+ create(:deployment, :success, environment: environment, project: project, finished_at: 1.day.ago)
+ end
+
+ let_it_be(:upcoming_deployment_old) do
+ create(:deployment, :created, environment: environment, project: project, created_at: 2.hours.ago)
+ end
+
+ let_it_be(:upcoming_deployment_new) do
+ create(:deployment, :created, environment: environment, project: project, created_at: 1.hour.ago)
+ end
+
+ let_it_be(:other_environment) { create(:environment, project: project) }
+ let_it_be(:other_deployment) { create(:deployment, :success, environment: other_environment, project: project) }
+
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments {
+ nodes {
+ id
+ iid
+ ref
+ tag
+ sha
+ createdAt
+ updatedAt
+ finishedAt
+ status
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'returns all deployments of the environment' do
+ deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
+
+ expect(deployments.count).to eq(4)
+ end
+
+ context 'when query last deployment' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments(statuses: [SUCCESS], orderBy: { finishedAt: DESC }, first: 1) {
+ nodes {
+ iid
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'returns deployment' do
+ deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
+
+ expect(deployments.count).to eq(1)
+ expect(deployments[0]['iid']).to eq(finished_deployment_new.iid.to_s)
+ end
+ end
+
+ context 'when query latest upcoming deployment' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments(statuses: [CREATED RUNNING BLOCKED], orderBy: { createdAt: DESC }, first: 1) {
+ nodes {
+ iid
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'returns deployment' do
+ deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
+
+ expect(deployments.count).to eq(1)
+ expect(deployments[0]['iid']).to eq(upcoming_deployment_new.iid.to_s)
+ end
+ end
+
+ context 'when query finished deployments in descending order' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments(statuses: [SUCCESS FAILED CANCELED], orderBy: { finishedAt: DESC }) {
+ nodes {
+ iid
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'returns deployments' do
+ deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
+
+ expect(deployments.count).to eq(2)
+ expect(deployments[0]['iid']).to eq(finished_deployment_new.iid.to_s)
+ expect(deployments[1]['iid']).to eq(finished_deployment_old.iid.to_s)
+ end
+ end
+
+ context 'when query finished deployments in ascending order' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments(statuses: [SUCCESS FAILED CANCELED], orderBy: { finishedAt: ASC }) {
+ nodes {
+ iid
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'returns deployments' do
+ deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
+
+ expect(deployments.count).to eq(2)
+ expect(deployments[0]['iid']).to eq(finished_deployment_old.iid.to_s)
+ expect(deployments[1]['iid']).to eq(finished_deployment_new.iid.to_s)
+ end
+ end
+
+ context 'when query upcoming deployments in descending order' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments(statuses: [CREATED RUNNING BLOCKED], orderBy: { createdAt: DESC }) {
+ nodes {
+ iid
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'returns deployments' do
+ deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
+
+ expect(deployments.count).to eq(2)
+ expect(deployments[0]['iid']).to eq(upcoming_deployment_new.iid.to_s)
+ expect(deployments[1]['iid']).to eq(upcoming_deployment_old.iid.to_s)
+ end
+ end
+
+ context 'when query upcoming deployments in ascending order' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments(statuses: [CREATED RUNNING BLOCKED], orderBy: { createdAt: ASC }) {
+ nodes {
+ iid
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'returns deployments' do
+ deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
+
+ expect(deployments.count).to eq(2)
+ expect(deployments[0]['iid']).to eq(upcoming_deployment_old.iid.to_s)
+ expect(deployments[1]['iid']).to eq(upcoming_deployment_new.iid.to_s)
+ end
+ end
+
+ context 'when query last deployments of multiple environments' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environments {
+ nodes {
+ name
+ deployments(statuses: [SUCCESS], orderBy: { finishedAt: DESC }, first: 1) {
+ nodes {
+ iid
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'returns an error for preventing N+1 queries' do
+ expect(subject['errors'][0]['message'])
+ .to include('"deployments" field can be requested only for 1 Environment(s) at a time.')
+ end
+ end
+
+ context 'when query finished and upcoming deployments together' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments(statuses: [CREATED SUCCESS]) {
+ nodes {
+ iid
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(DeploymentsFinder::InefficientQueryError)
+ end
+ end
+
+ context 'when multiple orderBy input are specified' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments(orderBy: { finishedAt: DESC, createdAt: ASC }) {
+ nodes {
+ iid
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'raises an error' do
+ expect(subject['errors'][0]['message']).to include('orderBy parameter must contain one key-value pair.')
+ end
+ end
+
+ context 'when user is guest' do
+ let(:user) { guest }
+
+ it 'returns nothing' do
+ expect(subject['data']['project']['environment']).to be_nil
+ end
+ end
+
+ shared_examples_for 'avoids N+1 database queries' do
+ it 'does not increase the query count' do
+ create_deployments
+
+ baseline = ActiveRecord::QueryRecorder.new do
+ run_with_clean_state(query, context: { current_user: user })
+ end
+
+ create_deployments
+
+ multi = ActiveRecord::QueryRecorder.new do
+ run_with_clean_state(query, context: { current_user: user })
+ end
+
+ expect(multi).not_to exceed_query_limit(baseline)
+ end
+
+ def create_deployments
+ create_list(:deployment, 3, environment: environment, project: project).each do |deployment|
+ deployment.user = create(:user).tap { |u| project.add_developer(u) }
+ deployment.deployable =
+ create(:ci_build, project: project, environment: environment.name, deployment: deployment,
+ user: deployment.user)
+
+ deployment.save!
+ end
+ end
+ end
+
+ context 'when requesting commits of deployments' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments {
+ nodes {
+ iid
+ commit {
+ author {
+ avatarUrl
+ name
+ webPath
+ }
+ fullTitle
+ webPath
+ sha
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it_behaves_like 'avoids N+1 database queries'
+
+ it 'returns commits of deployments' do
+ deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
+
+ deployments.each do |deployment|
+ deployment_in_record = project.deployments.find_by_iid(deployment['iid'])
+
+ expect(deployment_in_record.sha).to eq(deployment['commit']['sha'])
+ end
+ end
+ end
+
+ context 'when requesting triggerers of deployments' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments {
+ nodes {
+ iid
+ triggerer {
+ id
+ avatarUrl
+ name
+ webPath
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it_behaves_like 'avoids N+1 database queries'
+
+ it 'returns triggerers of deployments' do
+ deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
+
+ deployments.each do |deployment|
+ deployment_in_record = project.deployments.find_by_iid(deployment['iid'])
+
+ expect(deployment_in_record.deployed_by.name).to eq(deployment['triggerer']['name'])
+ end
+ end
+ end
+
+ context 'when requesting jobs of deployments' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments {
+ nodes {
+ iid
+ job {
+ id
+ status
+ name
+ webPath
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it_behaves_like 'avoids N+1 database queries'
+
+ it 'returns jobs of deployments' do
+ deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
+
+ deployments.each do |deployment|
+ deployment_in_record = project.deployments.find_by_iid(deployment['iid'])
+
+ expect(deployment_in_record.build.to_global_id.to_s).to eq(deployment['job']['id'])
+ end
+ end
+ end
+
+ context 'when requesting user permissions' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments {
+ nodes {
+ iid
+ userPermissions {
+ updateDeployment
+ destroyDeployment
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it_behaves_like 'avoids N+1 database queries'
+
+ it 'returns user permissions of the deployments', :aggregate_failures do
+ deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
+
+ deployments.each do |deployment|
+ deployment_in_record = project.deployments.find_by_iid(deployment['iid'])
+
+ expect(deployment['userPermissions']['updateDeployment'])
+ .to eq(Ability.allowed?(user, :update_deployment, deployment_in_record))
+ expect(deployment['userPermissions']['destroyDeployment'])
+ .to eq(Ability.allowed?(user, :destroy_deployment, deployment_in_record))
+ end
+ end
+ end
+
+ describe 'sorting and pagination' do
+ let(:data_path) { [:project, :environment, :deployments] }
+ let(:current_user) { user }
+
+ def pagination_query(params)
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments(statuses: [SUCCESS], #{params}) {
+ nodes {
+ iid
+ }
+ pageInfo {
+ startCursor
+ endCursor
+ hasNextPage
+ hasPreviousPage
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ def pagination_results_data(nodes)
+ nodes.map { |deployment| deployment['iid'].to_i }
+ end
+
+ context 'when sorting by finished_at in ascending order' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_argument) { graphql_args(orderBy: { finishedAt: :ASC }) }
+ let(:first_param) { 2 }
+ let(:all_records) { [finished_deployment_old.iid, finished_deployment_new.iid] }
+ end
+ end
+
+ context 'when sorting by finished_at in descending order' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_argument) { graphql_args(orderBy: { finishedAt: :DESC }) }
+ let(:first_param) { 2 }
+ let(:all_records) { [finished_deployment_new.iid, finished_deployment_old.iid] }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/gitlab_schema_spec.rb b/spec/requests/api/graphql/gitlab_schema_spec.rb
index c1beadb6c45..7937091ea7c 100644
--- a/spec/requests/api/graphql/gitlab_schema_spec.rb
+++ b/spec/requests/api/graphql/gitlab_schema_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'GitlabSchema configurations' do
+RSpec.describe 'GitlabSchema configurations', feature_category: :not_owned do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/group/container_repositories_spec.rb b/spec/requests/api/graphql/group/container_repositories_spec.rb
index 8ec321c8d7c..51d12261247 100644
--- a/spec/requests/api/graphql/group/container_repositories_spec.rb
+++ b/spec/requests/api/graphql/group/container_repositories_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'getting container repositories in a group' do
+RSpec.describe 'getting container repositories in a group', feature_category: :source_code_management do
using RSpec::Parameterized::TableSyntax
include GraphqlHelpers
diff --git a/spec/requests/api/graphql/group/dependency_proxy_blobs_spec.rb b/spec/requests/api/graphql/group/dependency_proxy_blobs_spec.rb
index daa1483e956..2c4770a31a7 100644
--- a/spec/requests/api/graphql/group/dependency_proxy_blobs_spec.rb
+++ b/spec/requests/api/graphql/group/dependency_proxy_blobs_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'getting dependency proxy blobs in a group' do
+RSpec.describe 'getting dependency proxy blobs in a group', feature_category: :dependency_proxy do
using RSpec::Parameterized::TableSyntax
include GraphqlHelpers
diff --git a/spec/requests/api/graphql/group/dependency_proxy_group_setting_spec.rb b/spec/requests/api/graphql/group/dependency_proxy_group_setting_spec.rb
index cc706c3051f..aca8527ba0a 100644
--- a/spec/requests/api/graphql/group/dependency_proxy_group_setting_spec.rb
+++ b/spec/requests/api/graphql/group/dependency_proxy_group_setting_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'getting dependency proxy settings for a group' do
+RSpec.describe 'getting dependency proxy settings for a group', feature_category: :dependency_proxy do
using RSpec::Parameterized::TableSyntax
include GraphqlHelpers
diff --git a/spec/requests/api/graphql/group/dependency_proxy_image_ttl_policy_spec.rb b/spec/requests/api/graphql/group/dependency_proxy_image_ttl_policy_spec.rb
index 3b2b04b1322..edff4dc1dae 100644
--- a/spec/requests/api/graphql/group/dependency_proxy_image_ttl_policy_spec.rb
+++ b/spec/requests/api/graphql/group/dependency_proxy_image_ttl_policy_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'getting dependency proxy image ttl policy for a group' do
+RSpec.describe 'getting dependency proxy image ttl policy for a group', feature_category: :dependency_proxy do
using RSpec::Parameterized::TableSyntax
include GraphqlHelpers
diff --git a/spec/requests/api/graphql/group/dependency_proxy_manifests_spec.rb b/spec/requests/api/graphql/group/dependency_proxy_manifests_spec.rb
index 37ef7089c2f..d2d686104ad 100644
--- a/spec/requests/api/graphql/group/dependency_proxy_manifests_spec.rb
+++ b/spec/requests/api/graphql/group/dependency_proxy_manifests_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'getting dependency proxy manifests in a group' do
+RSpec.describe 'getting dependency proxy manifests in a group', feature_category: :dependency_proxy do
using RSpec::Parameterized::TableSyntax
include GraphqlHelpers
diff --git a/spec/requests/api/graphql/group/group_members_spec.rb b/spec/requests/api/graphql/group/group_members_spec.rb
index 5f8becc0726..26d1fb48408 100644
--- a/spec/requests/api/graphql/group/group_members_spec.rb
+++ b/spec/requests/api/graphql/group/group_members_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting group members information' do
+RSpec.describe 'getting group members information', feature_category: :subgroups do
include GraphqlHelpers
let_it_be(:parent_group) { create(:group, :public) }
diff --git a/spec/requests/api/graphql/group/issues_spec.rb b/spec/requests/api/graphql/group/issues_spec.rb
index 26338f46611..95aeed32558 100644
--- a/spec/requests/api/graphql/group/issues_spec.rb
+++ b/spec/requests/api/graphql/group/issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting an issue list for a group' do
+RSpec.describe 'getting an issue list for a group', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/group/labels_query_spec.rb b/spec/requests/api/graphql/group/labels_query_spec.rb
index 31556ffca30..28886f8d80b 100644
--- a/spec/requests/api/graphql/group/labels_query_spec.rb
+++ b/spec/requests/api/graphql/group/labels_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting group label information' do
+RSpec.describe 'getting group label information', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:group) { create(:group, :public) }
diff --git a/spec/requests/api/graphql/group/merge_requests_spec.rb b/spec/requests/api/graphql/group/merge_requests_spec.rb
index 434b0d16569..6976685ecc0 100644
--- a/spec/requests/api/graphql/group/merge_requests_spec.rb
+++ b/spec/requests/api/graphql/group/merge_requests_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
# Based on ee/spec/requests/api/epics_spec.rb
# Should follow closely in order to ensure all situations are covered
-RSpec.describe 'Query.group.mergeRequests' do
+RSpec.describe 'Query.group.mergeRequests', feature_category: :code_review do
include GraphqlHelpers
let_it_be(:group) { create(:group) }
diff --git a/spec/requests/api/graphql/group/milestones_spec.rb b/spec/requests/api/graphql/group/milestones_spec.rb
index 7c51409f907..28cd68493c0 100644
--- a/spec/requests/api/graphql/group/milestones_spec.rb
+++ b/spec/requests/api/graphql/group/milestones_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Milestones through GroupQuery' do
+RSpec.describe 'Milestones through GroupQuery', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/group/packages_spec.rb b/spec/requests/api/graphql/group/packages_spec.rb
index cf8736db5af..0b4057c87f8 100644
--- a/spec/requests/api/graphql/group/packages_spec.rb
+++ b/spec/requests/api/graphql/group/packages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting a package list for a group' do
+RSpec.describe 'getting a package list for a group', feature_category: :package_registry do
include GraphqlHelpers
let_it_be(:resource) { create(:group, :private) }
diff --git a/spec/requests/api/graphql/group/recent_issue_boards_query_spec.rb b/spec/requests/api/graphql/group/recent_issue_boards_query_spec.rb
index 4914beec870..2dfbc95bac9 100644
--- a/spec/requests/api/graphql/group/recent_issue_boards_query_spec.rb
+++ b/spec/requests/api/graphql/group/recent_issue_boards_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting group recent issue boards' do
+RSpec.describe 'getting group recent issue boards', feature_category: :team_planning do
include GraphqlHelpers
it_behaves_like 'querying a GraphQL type recent boards' do
diff --git a/spec/requests/api/graphql/group/timelogs_spec.rb b/spec/requests/api/graphql/group/timelogs_spec.rb
index 05b6ee3ff89..b67b39edff2 100644
--- a/spec/requests/api/graphql/group/timelogs_spec.rb
+++ b/spec/requests/api/graphql/group/timelogs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Timelogs through GroupQuery' do
+RSpec.describe 'Timelogs through GroupQuery', feature_category: :team_planning do
include GraphqlHelpers
describe 'Get list of timelogs from a group issues' do
diff --git a/spec/requests/api/graphql/group/work_item_types_spec.rb b/spec/requests/api/graphql/group/work_item_types_spec.rb
index 35090e2a89f..791c0fb9524 100644
--- a/spec/requests/api/graphql/group/work_item_types_spec.rb
+++ b/spec/requests/api/graphql/group/work_item_types_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting a list of work item types for a group' do
+RSpec.describe 'getting a list of work item types for a group', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:developer) { create(:user) }
diff --git a/spec/requests/api/graphql/group_query_spec.rb b/spec/requests/api/graphql/group_query_spec.rb
index 8ee5c3c5d73..bc288c0a98b 100644
--- a/spec/requests/api/graphql/group_query_spec.rb
+++ b/spec/requests/api/graphql/group_query_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
# Based on spec/requests/api/groups_spec.rb
# Should follow closely in order to ensure all situations are covered
-RSpec.describe 'getting group information' do
+RSpec.describe 'getting group information', feature_category: :subgroups do
include GraphqlHelpers
include UploadHelpers
diff --git a/spec/requests/api/graphql/issue/issue_spec.rb b/spec/requests/api/graphql/issue/issue_spec.rb
index 6e2d736f244..101de692aa5 100644
--- a/spec/requests/api/graphql/issue/issue_spec.rb
+++ b/spec/requests/api/graphql/issue/issue_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.issue(id)' do
+RSpec.describe 'Query.issue(id)', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/issue_status_counts_spec.rb b/spec/requests/api/graphql/issue_status_counts_spec.rb
index 89ecbf44b10..72a1968cb27 100644
--- a/spec/requests/api/graphql/issue_status_counts_spec.rb
+++ b/spec/requests/api/graphql/issue_status_counts_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'getting Issue counts by status' do
+RSpec.describe 'getting Issue counts by status', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/requests/api/graphql/issues_spec.rb b/spec/requests/api/graphql/issues_spec.rb
index 8838ad78f72..ba6f8ec2cab 100644
--- a/spec/requests/api/graphql/issues_spec.rb
+++ b/spec/requests/api/graphql/issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting an issue list at root level' do
+RSpec.describe 'getting an issue list at root level', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:developer) { create(:user) }
@@ -13,34 +13,81 @@ RSpec.describe 'getting an issue list at root level' do
let_it_be(:project_b) { create(:project, :repository, :private, group: group1) }
let_it_be(:project_c) { create(:project, :repository, :public, group: group2) }
let_it_be(:project_d) { create(:project, :repository, :private, group: group2) }
- let_it_be(:early_milestone) { create(:milestone, project: project_d, due_date: 10.days.from_now) }
- let_it_be(:late_milestone) { create(:milestone, project: project_c, due_date: 30.days.from_now) }
+ let_it_be(:milestone1) { create(:milestone, project: project_c, due_date: 10.days.from_now) }
+ let_it_be(:milestone2) { create(:milestone, project: project_d, due_date: 20.days.from_now) }
+ let_it_be(:milestone3) { create(:milestone, project: project_d, due_date: 30.days.from_now) }
+ let_it_be(:milestone4) { create(:milestone, project: project_a, due_date: 40.days.from_now) }
let_it_be(:priority1) { create(:label, project: project_c, priority: 1) }
let_it_be(:priority2) { create(:label, project: project_d, priority: 5) }
let_it_be(:priority3) { create(:label, project: project_a, priority: 10) }
+ let_it_be(:priority4) { create(:label, project: project_d, priority: 15) }
+
+ let_it_be(:issue_a) do
+ create(
+ :issue,
+ project: project_a,
+ labels: [priority3],
+ due_date: 1.day.ago,
+ milestone: milestone4,
+ relative_position: 1000
+ )
+ end
+
+ let_it_be(:issue_b) do
+ create(
+ :issue,
+ :with_alert,
+ project: project_b,
+ discussion_locked: true,
+ due_date: 1.day.from_now,
+ relative_position: 3000
+ )
+ end
- let_it_be(:issue_a) { create(:issue, project: project_a, labels: [priority3]) }
- let_it_be(:issue_b) { create(:issue, :with_alert, project: project_b, discussion_locked: true) }
let_it_be(:issue_c) do
create(
:issue,
+ :confidential,
project: project_c,
title: 'title matching issue plus',
labels: [priority1],
- milestone: late_milestone
+ milestone: milestone1,
+ due_date: 3.days.from_now,
+ relative_position: nil
)
end
- let_it_be(:issue_d) { create(:issue, :with_alert, project: project_d, discussion_locked: true, labels: [priority2]) }
- let_it_be(:issue_e) { create(:issue, project: project_d, milestone: early_milestone) }
+ let_it_be(:issue_d) do
+ create(
+ :issue,
+ :with_alert,
+ project: project_d,
+ discussion_locked: true,
+ labels: [priority2],
+ milestone: milestone3,
+ relative_position: 5000
+ )
+ end
- let(:issue_filter_params) { {} }
+ let_it_be(:issue_e) do
+ create(
+ :issue,
+ :confidential,
+ project: project_d,
+ milestone: milestone2,
+ due_date: 3.days.ago,
+ relative_position: nil,
+ labels: [priority2, priority4]
+ )
+ end
+ let_it_be(:issues, reload: true) { [issue_a, issue_b, issue_c, issue_d, issue_e] }
+
+ let(:issue_filter_params) { {} }
+ let(:current_user) { developer }
let(:fields) do
<<~QUERY
- nodes {
- #{all_graphql_fields_for('issues'.classify)}
- }
+ nodes { id }
QUERY
end
@@ -60,13 +107,16 @@ RSpec.describe 'getting an issue list at root level' do
end
end
+ # All new specs should be added to the shared example if the change also
+ # affects the `issues` query at the root level of the API.
+ # Shared example also used in spec/requests/api/graphql/project/issues_spec.rb
it_behaves_like 'graphql issue list request spec' do
- subject(:post_query) { post_graphql(query, current_user: current_user) }
+ let_it_be(:external_user) { create(:user) }
+
+ let(:public_projects) { [project_a, project_c] }
- let(:current_user) { developer }
let(:another_user) { reporter }
- let(:issues_data) { graphql_data['issues']['nodes'] }
- let(:issue_ids) { graphql_dig_at(issues_data, :id) }
+ let(:issue_nodes_path) { %w[issues nodes] }
# filters
let(:expected_negated_assignee_issues) { [issue_b, issue_c, issue_d, issue_e] }
@@ -77,12 +127,25 @@ RSpec.describe 'getting an issue list at root level' do
let(:unlocked_discussion_issues) { [issue_a, issue_c, issue_e] }
let(:search_title_term) { 'matching issue' }
let(:title_search_issue) { issue_c }
+ let(:confidential_issues) { [issue_c, issue_e] }
+ let(:non_confidential_issues) { [issue_a, issue_b, issue_d] }
+ let(:public_non_confidential_issues) { [issue_a] }
# sorting
let(:data_path) { [:issues] }
- let(:expected_severity_sorted_asc) { [issue_c, issue_a, issue_b, issue_e, issue_d] }
- let(:expected_priority_sorted_asc) { [issue_e, issue_c, issue_d, issue_a, issue_b] }
- let(:expected_priority_sorted_desc) { [issue_c, issue_e, issue_a, issue_d, issue_b] }
+ let(:expected_priority_sorted_asc) { [issue_c, issue_e, issue_d, issue_a, issue_b] }
+ let(:expected_priority_sorted_desc) { [issue_a, issue_d, issue_e, issue_c, issue_b] }
+ let(:expected_due_date_sorted_desc) { [issue_c, issue_b, issue_a, issue_e, issue_d] }
+ let(:expected_due_date_sorted_asc) { [issue_e, issue_a, issue_b, issue_c, issue_d] }
+ let(:expected_relative_position_sorted_asc) { [issue_a, issue_b, issue_d, issue_c, issue_e] }
+ let(:expected_label_priority_sorted_asc) { [issue_c, issue_e, issue_d, issue_a, issue_b] }
+ let(:expected_label_priority_sorted_desc) { [issue_a, issue_e, issue_d, issue_c, issue_b] }
+ let(:expected_milestone_sorted_asc) { [issue_c, issue_e, issue_d, issue_a, issue_b] }
+ let(:expected_milestone_sorted_desc) { [issue_a, issue_d, issue_e, issue_c, issue_b] }
+
+ # N+1 queries
+ let(:same_project_issue1) { issue_d }
+ let(:same_project_issue2) { issue_e }
before_all do
issue_a.assignee_ids = developer.id
@@ -90,12 +153,6 @@ RSpec.describe 'getting an issue list at root level' do
create(:award_emoji, :upvote, user: developer, awardable: issue_a)
create(:award_emoji, :upvote, user: developer, awardable: issue_c)
-
- # severity sorting
- create(:issuable_severity, issue: issue_a, severity: :unknown)
- create(:issuable_severity, issue: issue_b, severity: :low)
- create(:issuable_severity, issue: issue_d, severity: :critical)
- create(:issuable_severity, issue: issue_e, severity: :high)
end
def pagination_query(params)
@@ -107,6 +164,27 @@ RSpec.describe 'getting an issue list at root level' do
end
end
+ context 'when fetching issues from multiple projects' do
+ it 'avoids N+1 queries' do
+ post_query # warm-up
+
+ control = ActiveRecord::QueryRecorder.new { post_query }
+
+ new_private_project = create(:project, :private).tap { |project| project.add_developer(current_user) }
+ create(:issue, project: new_private_project)
+
+ expect { post_query }.not_to exceed_query_limit(control)
+ end
+ end
+
+ def execute_query
+ post_query
+ end
+
+ def post_query(request_user = current_user)
+ post_graphql(query, current_user: request_user)
+ end
+
def query(params = issue_filter_params)
graphql_query_for(
:issues,
diff --git a/spec/requests/api/graphql/jobs_query_spec.rb b/spec/requests/api/graphql/jobs_query_spec.rb
index 5907566be7f..0aea8e4c253 100644
--- a/spec/requests/api/graphql/jobs_query_spec.rb
+++ b/spec/requests/api/graphql/jobs_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting job information' do
+RSpec.describe 'getting job information', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:job) { create(:ci_build, :success, name: 'job1') }
diff --git a/spec/requests/api/graphql/merge_request/merge_request_spec.rb b/spec/requests/api/graphql/merge_request/merge_request_spec.rb
index d89f381753e..213697bacc1 100644
--- a/spec/requests/api/graphql/merge_request/merge_request_spec.rb
+++ b/spec/requests/api/graphql/merge_request/merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.merge_request(id)' do
+RSpec.describe 'Query.merge_request(id)', feature_category: :code_review do
include GraphqlHelpers
let_it_be(:project) { create(:project, :empty_repo) }
diff --git a/spec/requests/api/graphql/metadata_query_spec.rb b/spec/requests/api/graphql/metadata_query_spec.rb
index 435e1b5b596..7d1850b1b93 100644
--- a/spec/requests/api/graphql/metadata_query_spec.rb
+++ b/spec/requests/api/graphql/metadata_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting project information' do
+RSpec.describe 'getting project information', feature_category: :projects do
include GraphqlHelpers
let(:query) { graphql_query_for('metadata', {}, all_graphql_fields_for('Metadata')) }
diff --git a/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb b/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb
index 72ec2b8e070..4dd47142c40 100644
--- a/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb
+++ b/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Getting Metrics Dashboard Annotations' do
+RSpec.describe 'Getting Metrics Dashboard Annotations', feature_category: :metrics do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/metrics/dashboard_query_spec.rb b/spec/requests/api/graphql/metrics/dashboard_query_spec.rb
index 1b84acff0e2..8db0844c6d7 100644
--- a/spec/requests/api/graphql/metrics/dashboard_query_spec.rb
+++ b/spec/requests/api/graphql/metrics/dashboard_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Getting Metrics Dashboard' do
+RSpec.describe 'Getting Metrics Dashboard', feature_category: :metrics do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
@@ -26,156 +26,73 @@ RSpec.describe 'Getting Metrics Dashboard' do
)
end
- context 'with metrics_dashboard_exhaustive_validations feature flag off' do
+ context 'for anonymous user' do
before do
- stub_feature_flags(metrics_dashboard_exhaustive_validations: false)
+ post_graphql(query, current_user: current_user)
end
- context 'for anonymous user' do
- before do
- post_graphql(query, current_user: current_user)
- end
-
- context 'requested dashboard is available' do
- let(:path) { 'config/prometheus/common_metrics.yml' }
-
- it_behaves_like 'a working graphql query'
-
- it 'returns nil' do
- dashboard = graphql_data.dig('project', 'environments', 'nodes')
-
- expect(dashboard).to be_nil
- end
- end
- end
-
- context 'for user with developer access' do
- before do
- project.add_developer(current_user)
- post_graphql(query, current_user: current_user)
- end
-
- context 'requested dashboard is available' do
- let(:path) { 'config/prometheus/common_metrics.yml' }
-
- it_behaves_like 'a working graphql query'
-
- it 'returns metrics dashboard' do
- dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
-
- expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => nil)
- end
-
- context 'invalid dashboard' do
- let(:path) { '.gitlab/dashboards/metrics.yml' }
- let(:project) { create(:project, :repository, :custom_repo, namespace: current_user.namespace, files: { path => "---\ndashboard: 'test'" }) }
-
- it 'returns metrics dashboard' do
- dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
-
- expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["panel_groups: should be an array of panel_groups objects"])
- end
- end
-
- context 'empty dashboard' do
- let(:path) { '.gitlab/dashboards/metrics.yml' }
- let(:project) { create(:project, :repository, :custom_repo, namespace: current_user.namespace, files: { path => "" }) }
-
- it 'returns metrics dashboard' do
- dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
+ context 'requested dashboard is available' do
+ let(:path) { 'config/prometheus/common_metrics.yml' }
- expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["dashboard: can't be blank", "panel_groups: should be an array of panel_groups objects"])
- end
- end
- end
-
- context 'requested dashboard can not be found' do
- let(:path) { 'config/prometheus/i_am_not_here.yml' }
+ it_behaves_like 'a working graphql query'
- it_behaves_like 'a working graphql query'
+ it 'returns nil' do
+ dashboard = graphql_data.dig('project', 'environments', 'nodes')
- it 'returns nil' do
- dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
-
- expect(dashboard).to be_nil
- end
+ expect(dashboard).to be_nil
end
end
end
- context 'with metrics_dashboard_exhaustive_validations feature flag on' do
+ context 'for user with developer access' do
before do
- stub_feature_flags(metrics_dashboard_exhaustive_validations: true)
+ project.add_developer(current_user)
+ post_graphql(query, current_user: current_user)
end
- context 'for anonymous user' do
- before do
- post_graphql(query, current_user: current_user)
- end
-
- context 'requested dashboard is available' do
- let(:path) { 'config/prometheus/common_metrics.yml' }
-
- it_behaves_like 'a working graphql query'
+ context 'requested dashboard is available' do
+ let(:path) { 'config/prometheus/common_metrics.yml' }
- it 'returns nil' do
- dashboard = graphql_data.dig('project', 'environments', 'nodes')
+ it_behaves_like 'a working graphql query'
- expect(dashboard).to be_nil
- end
- end
- end
+ it 'returns metrics dashboard' do
+ dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
- context 'for user with developer access' do
- before do
- project.add_developer(current_user)
- post_graphql(query, current_user: current_user)
+ expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => nil)
end
- context 'requested dashboard is available' do
- let(:path) { 'config/prometheus/common_metrics.yml' }
-
- it_behaves_like 'a working graphql query'
+ context 'invalid dashboard' do
+ let(:path) { '.gitlab/dashboards/metrics.yml' }
+ let(:project) { create(:project, :repository, :custom_repo, namespace: current_user.namespace, files: { path => "---\ndashboard: 'test'" }) }
it 'returns metrics dashboard' do
dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
- expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => nil)
- end
-
- context 'invalid dashboard' do
- let(:path) { '.gitlab/dashboards/metrics.yml' }
- let(:project) { create(:project, :repository, :custom_repo, namespace: current_user.namespace, files: { path => "---\ndashboard: 'test'" }) }
-
- it 'returns metrics dashboard' do
- dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
-
- expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["root is missing required keys: panel_groups"])
- end
+ expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["panel_groups: should be an array of panel_groups objects"])
end
+ end
- context 'empty dashboard' do
- let(:path) { '.gitlab/dashboards/metrics.yml' }
- let(:project) { create(:project, :repository, :custom_repo, namespace: current_user.namespace, files: { path => "" }) }
+ context 'empty dashboard' do
+ let(:path) { '.gitlab/dashboards/metrics.yml' }
+ let(:project) { create(:project, :repository, :custom_repo, namespace: current_user.namespace, files: { path => "" }) }
- it 'returns metrics dashboard' do
- dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
+ it 'returns metrics dashboard' do
+ dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
- expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["root is missing required keys: dashboard, panel_groups"])
- end
+ expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["dashboard: can't be blank", "panel_groups: should be an array of panel_groups objects"])
end
end
+ end
- context 'requested dashboard can not be found' do
- let(:path) { 'config/prometheus/i_am_not_here.yml' }
+ context 'requested dashboard can not be found' do
+ let(:path) { 'config/prometheus/i_am_not_here.yml' }
- it_behaves_like 'a working graphql query'
+ it_behaves_like 'a working graphql query'
- it 'returns nil' do
- dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
+ it 'returns nil' do
+ dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
- expect(dashboard).to be_nil
- end
+ expect(dashboard).to be_nil
end
end
end
diff --git a/spec/requests/api/graphql/milestone_spec.rb b/spec/requests/api/graphql/milestone_spec.rb
index 78e7ec39ee3..2cea9fd0408 100644
--- a/spec/requests/api/graphql/milestone_spec.rb
+++ b/spec/requests/api/graphql/milestone_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Querying a Milestone' do
+RSpec.describe 'Querying a Milestone', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:group) { create(:group, :public) }
diff --git a/spec/requests/api/graphql/multiplexed_queries_spec.rb b/spec/requests/api/graphql/multiplexed_queries_spec.rb
index f79bac6ae3b..4d615d3eaa4 100644
--- a/spec/requests/api/graphql/multiplexed_queries_spec.rb
+++ b/spec/requests/api/graphql/multiplexed_queries_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'Multiplexed queries' do
+RSpec.describe 'Multiplexed queries', feature_category: :not_owned do
include GraphqlHelpers
it 'returns responses for multiple queries' do
diff --git a/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb b/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb
index f992e46879f..64ea6d32f5f 100644
--- a/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb
+++ b/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues do
+RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues, feature_category: :not_owned do
include GraphqlHelpers
let_it_be(:admin) { create(:admin) }
diff --git a/spec/requests/api/graphql/mutations/alert_management/alerts/create_alert_issue_spec.rb b/spec/requests/api/graphql/mutations/alert_management/alerts/create_alert_issue_spec.rb
index f637ca98353..fbe6d95dfff 100644
--- a/spec/requests/api/graphql/mutations/alert_management/alerts/create_alert_issue_spec.rb
+++ b/spec/requests/api/graphql/mutations/alert_management/alerts/create_alert_issue_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Create an alert issue from an alert' do
+RSpec.describe 'Create an alert issue from an alert', feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb b/spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb
index fcef7b4e3ec..935856814c4 100644
--- a/spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb
+++ b/spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting assignees of an alert' do
+RSpec.describe 'Setting assignees of an alert', feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/mutations/alert_management/alerts/todo/create_spec.rb b/spec/requests/api/graphql/mutations/alert_management/alerts/todo/create_spec.rb
index 48307964345..570324a3126 100644
--- a/spec/requests/api/graphql/mutations/alert_management/alerts/todo/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/alert_management/alerts/todo/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Creating a todo for the alert' do
+RSpec.describe 'Creating a todo for the alert', feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb b/spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb
index 802d8d6c5a1..6537747850c 100644
--- a/spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb
+++ b/spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting the status of an alert' do
+RSpec.describe 'Setting the status of an alert', feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/alert_management/http_integration/create_spec.rb b/spec/requests/api/graphql/mutations/alert_management/http_integration/create_spec.rb
index ff93da2153f..187c88363c6 100644
--- a/spec/requests/api/graphql/mutations/alert_management/http_integration/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/alert_management/http_integration/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Creating a new HTTP Integration' do
+RSpec.describe 'Creating a new HTTP Integration', feature_category: :integrations do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/alert_management/http_integration/destroy_spec.rb b/spec/requests/api/graphql/mutations/alert_management/http_integration/destroy_spec.rb
index 1ecb5c76b57..1c77c71daba 100644
--- a/spec/requests/api/graphql/mutations/alert_management/http_integration/destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/alert_management/http_integration/destroy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Removing an HTTP Integration' do
+RSpec.describe 'Removing an HTTP Integration', feature_category: :integrations do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/alert_management/http_integration/reset_token_spec.rb b/spec/requests/api/graphql/mutations/alert_management/http_integration/reset_token_spec.rb
index badd9412589..427277dd540 100644
--- a/spec/requests/api/graphql/mutations/alert_management/http_integration/reset_token_spec.rb
+++ b/spec/requests/api/graphql/mutations/alert_management/http_integration/reset_token_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Resetting a token on an existing HTTP Integration' do
+RSpec.describe 'Resetting a token on an existing HTTP Integration', feature_category: :integrations do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/alert_management/http_integration/update_spec.rb b/spec/requests/api/graphql/mutations/alert_management/http_integration/update_spec.rb
index 18cbb7d8b00..a9d189d564d 100644
--- a/spec/requests/api/graphql/mutations/alert_management/http_integration/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/alert_management/http_integration/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Updating an existing HTTP Integration' do
+RSpec.describe 'Updating an existing HTTP Integration', feature_category: :integrations do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb
index 4c359d9b357..3dee7f50af3 100644
--- a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Creating a new Prometheus Integration' do
+RSpec.describe 'Creating a new Prometheus Integration', feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb
index 31053c50cac..15127843b95 100644
--- a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb
+++ b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Resetting a token on an existing Prometheus Integration' do
+RSpec.describe 'Resetting a token on an existing Prometheus Integration', feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/update_spec.rb b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/update_spec.rb
index ad26ec118d7..63e95f4513b 100644
--- a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Updating an existing Prometheus Integration' do
+RSpec.describe 'Updating an existing Prometheus Integration', feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb b/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb
index 3879e58cecf..fdbff0f93cd 100644
--- a/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb
+++ b/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Adding an AwardEmoji' do
+RSpec.describe 'Adding an AwardEmoji', feature_category: :not_owned do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb b/spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb
index e81621209fb..e200bfc2d18 100644
--- a/spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb
+++ b/spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Removing an AwardEmoji' do
+RSpec.describe 'Removing an AwardEmoji', feature_category: :not_owned do
include GraphqlHelpers
let(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb b/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb
index b151da72b55..6dba2b58357 100644
--- a/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb
+++ b/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Toggling an AwardEmoji' do
+RSpec.describe 'Toggling an AwardEmoji', feature_category: :not_owned do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/boards/create_spec.rb b/spec/requests/api/graphql/mutations/boards/create_spec.rb
index ca848c0c92f..10eb12f277f 100644
--- a/spec/requests/api/graphql/mutations/boards/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/boards/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Mutations::Boards::Create do
+RSpec.describe Mutations::Boards::Create, feature_category: :team_planning do
let_it_be(:parent) { create(:project) }
let_it_be(:current_user, reload: true) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/boards/destroy_spec.rb b/spec/requests/api/graphql/mutations/boards/destroy_spec.rb
index 7620da3e7e0..44924159137 100644
--- a/spec/requests/api/graphql/mutations/boards/destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/boards/destroy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Mutations::Boards::Destroy do
+RSpec.describe Mutations::Boards::Destroy, feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:current_user, reload: true) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/boards/issues/issue_move_list_spec.rb b/spec/requests/api/graphql/mutations/boards/issues/issue_move_list_spec.rb
index 06093e9f7c2..df64caa1cfb 100644
--- a/spec/requests/api/graphql/mutations/boards/issues/issue_move_list_spec.rb
+++ b/spec/requests/api/graphql/mutations/boards/issues/issue_move_list_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Reposition and move issue within board lists' do
+RSpec.describe 'Reposition and move issue within board lists', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:group) { create(:group, :private) }
diff --git a/spec/requests/api/graphql/mutations/boards/lists/create_spec.rb b/spec/requests/api/graphql/mutations/boards/lists/create_spec.rb
index fec9a8c6307..f5381be2741 100644
--- a/spec/requests/api/graphql/mutations/boards/lists/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/boards/lists/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Create a label or backlog board list' do
+RSpec.describe 'Create a label or backlog board list', feature_category: :team_planning do
let_it_be(:group) { create(:group, :private) }
let_it_be(:board) { create(:board, group: group) }
diff --git a/spec/requests/api/graphql/mutations/boards/lists/destroy_spec.rb b/spec/requests/api/graphql/mutations/boards/lists/destroy_spec.rb
index 83309ead352..b37b1a7b935 100644
--- a/spec/requests/api/graphql/mutations/boards/lists/destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/boards/lists/destroy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Mutations::Boards::Lists::Destroy do
+RSpec.describe Mutations::Boards::Lists::Destroy, feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:current_user, reload: true) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/boards/lists/update_spec.rb b/spec/requests/api/graphql/mutations/boards/lists/update_spec.rb
index c7885879a9d..6a7edcd7349 100644
--- a/spec/requests/api/graphql/mutations/boards/lists/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/boards/lists/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Update of an existing board list' do
+RSpec.describe 'Update of an existing board list', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/branches/create_spec.rb b/spec/requests/api/graphql/mutations/branches/create_spec.rb
index 9ee2f41e8fc..32512e2ee1b 100644
--- a/spec/requests/api/graphql/mutations/branches/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/branches/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Creation of a new branch' do
+RSpec.describe 'Creation of a new branch', feature_category: :source_code_management do
include GraphqlHelpers
let_it_be(:group) { create(:group, :public) }
diff --git a/spec/requests/api/graphql/mutations/ci/job/artifacts_destroy_spec.rb b/spec/requests/api/graphql/mutations/ci/job/artifacts_destroy_spec.rb
index bdad80995ea..6cdf8788957 100644
--- a/spec/requests/api/graphql/mutations/ci/job/artifacts_destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job/artifacts_destroy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'JobArtifactsDestroy' do
+RSpec.describe 'JobArtifactsDestroy', feature_category: :build_artifacts do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/ci/job/destroy_spec.rb b/spec/requests/api/graphql/mutations/ci/job/destroy_spec.rb
index 5855eb6bb51..88dfec41d36 100644
--- a/spec/requests/api/graphql/mutations/ci/job/destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job/destroy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'JobArtifactsDestroy' do
+RSpec.describe 'JobArtifactsDestroy', feature_category: :build_artifacts do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/ci/job_artifact/destroy_spec.rb b/spec/requests/api/graphql/mutations/ci/job_artifact/destroy_spec.rb
index a5ec9ea343d..ac3592130b8 100644
--- a/spec/requests/api/graphql/mutations/ci/job_artifact/destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job_artifact/destroy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'ArtifactDestroy' do
+RSpec.describe 'ArtifactDestroy', feature_category: :build_artifacts do
include GraphqlHelpers
let(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/ci/job_cancel_spec.rb b/spec/requests/api/graphql/mutations/ci/job_cancel_spec.rb
index ee0f0a9bccb..468a9e57f56 100644
--- a/spec/requests/api/graphql/mutations/ci/job_cancel_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job_cancel_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "JobCancel" do
+RSpec.describe "JobCancel", feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/ci/job_play_spec.rb b/spec/requests/api/graphql/mutations/ci/job_play_spec.rb
index 0874e225259..014a5e0f1c7 100644
--- a/spec/requests/api/graphql/mutations/ci/job_play_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job_play_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'JobPlay' do
+RSpec.describe 'JobPlay', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/ci/job_retry_spec.rb b/spec/requests/api/graphql/mutations/ci/job_retry_spec.rb
index 8cf559a372a..e49ee6f3163 100644
--- a/spec/requests/api/graphql/mutations/ci/job_retry_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job_retry_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'JobRetry' do
+RSpec.describe 'JobRetry', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb b/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb
index b2f84ab2869..490716ddbe2 100644
--- a/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'CiJobTokenScopeAddProject' do
+RSpec.describe 'CiJobTokenScopeAddProject', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) }
@@ -60,7 +60,7 @@ RSpec.describe 'CiJobTokenScopeAddProject' do
post_graphql_mutation(mutation, current_user: current_user)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response.dig('ciJobTokenScope', 'projects', 'nodes')).not_to be_empty
- end.to change { Ci::JobToken::Scope.new(project).includes?(target_project) }.from(false).to(true)
+ end.to change { Ci::JobToken::Scope.new(project).allows?(target_project) }.from(false).to(true)
end
context 'when invalid target project is provided' do
diff --git a/spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb b/spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb
index 2b0adf89f40..607c6bd85c2 100644
--- a/spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'CiJobTokenScopeRemoveProject' do
+RSpec.describe 'CiJobTokenScopeRemoveProject', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) }
@@ -66,7 +66,7 @@ RSpec.describe 'CiJobTokenScopeRemoveProject' do
post_graphql_mutation(mutation, current_user: current_user)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response.dig('ciJobTokenScope', 'projects', 'nodes')).not_to be_empty
- end.to change { Ci::JobToken::Scope.new(project).includes?(target_project) }.from(true).to(false)
+ end.to change { Ci::JobToken::Scope.new(project).allows?(target_project) }.from(true).to(false)
end
context 'when invalid target project is provided' do
diff --git a/spec/requests/api/graphql/mutations/ci/job_unschedule_spec.rb b/spec/requests/api/graphql/mutations/ci/job_unschedule_spec.rb
index 4ddc019a2b5..6868b0ea279 100644
--- a/spec/requests/api/graphql/mutations/ci/job_unschedule_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job_unschedule_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'JobUnschedule' do
+RSpec.describe 'JobUnschedule', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_cancel_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_cancel_spec.rb
index 6ec1b7ce9b6..8c1359384ed 100644
--- a/spec/requests/api/graphql/mutations/ci/pipeline_cancel_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/pipeline_cancel_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'PipelineCancel' do
+RSpec.describe 'PipelineCancel', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb
index 7abd5ca8772..9ddfaf83d34 100644
--- a/spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'PipelineDestroy' do
+RSpec.describe 'PipelineDestroy', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_retry_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_retry_spec.rb
index f6acf29c321..e7edc86bea0 100644
--- a/spec/requests/api/graphql/mutations/ci/pipeline_retry_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/pipeline_retry_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'PipelineRetry' do
+RSpec.describe 'PipelineRetry', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_create_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_schedule_create_spec.rb
new file mode 100644
index 00000000000..4a45d255d99
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/ci/pipeline_schedule_create_spec.rb
@@ -0,0 +1,151 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'PipelineSchedulecreate' do
+ include GraphqlHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :public, :repository) }
+
+ let(:mutation) do
+ variables = {
+ project_path: project.full_path,
+ **pipeline_schedule_parameters
+ }
+
+ graphql_mutation(
+ :pipeline_schedule_create,
+ variables,
+ <<-QL
+ pipelineSchedule {
+ id
+ description
+ cron
+ refForDisplay
+ active
+ cronTimezone
+ variables {
+ nodes {
+ key
+ value
+ }
+ }
+ owner {
+ id
+ }
+ }
+ errors
+ QL
+ )
+ end
+
+ let(:pipeline_schedule_parameters) do
+ {
+ description: 'created_desc',
+ cron: '0 1 * * *',
+ cronTimezone: 'UTC',
+ ref: 'patch-x',
+ active: true,
+ variables: [
+ { key: 'AAA', value: "AAA123", variableType: 'ENV_VAR' }
+ ]
+ }
+ end
+
+ let(:mutation_response) { graphql_mutation_response(:pipeline_schedule_create) }
+
+ context 'when unauthorized' do
+ it 'returns an error' do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(graphql_errors).not_to be_empty
+ expect(graphql_errors[0]['message'])
+ .to eq(
+ "The resource that you are attempting to access does not exist " \
+ "or you don't have permission to perform this action"
+ )
+ end
+ end
+
+ context 'when authorized' do
+ before do
+ project.add_developer(user)
+ end
+
+ context 'when success' do
+ it do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+
+ expect(mutation_response['pipelineSchedule']['owner']['id']).to eq(user.to_global_id.to_s)
+
+ %w[description cron cronTimezone active].each do |key|
+ expect(mutation_response['pipelineSchedule'][key]).to eq(pipeline_schedule_parameters[key.to_sym])
+ end
+
+ expect(mutation_response['pipelineSchedule']['refForDisplay']).to eq(pipeline_schedule_parameters[:ref])
+
+ expect(mutation_response['pipelineSchedule']['variables']['nodes'][0]['key']).to eq('AAA')
+ expect(mutation_response['pipelineSchedule']['variables']['nodes'][0]['value']).to eq('AAA123')
+
+ expect(mutation_response['pipelineSchedule']['owner']['id']).to eq(user.to_global_id.to_s)
+
+ expect(mutation_response['errors']).to eq([])
+ end
+ end
+
+ context 'when failure' do
+ context 'when params are invalid' do
+ let(:pipeline_schedule_parameters) do
+ {
+ description: 'some description',
+ cron: 'abc',
+ cronTimezone: 'cCc',
+ ref: 'asd',
+ active: true,
+ variables: []
+ }
+ end
+
+ it do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+
+ expect(mutation_response['errors'])
+ .to match_array(
+ ["Cron is invalid syntax", "Cron timezone is invalid syntax"]
+ )
+ end
+ end
+
+ context 'when variables have duplicate name' do
+ before do
+ pipeline_schedule_parameters.merge!(
+ {
+ variables: [
+ { key: 'AAA', value: "AAA123", variableType: 'ENV_VAR' },
+ { key: 'AAA', value: "AAA123", variableType: 'ENV_VAR' }
+ ]
+ }
+ )
+ end
+
+ it 'returns error' do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+
+ expect(mutation_response['errors'])
+ .to match_array(
+ [
+ "Variables have duplicate values (AAA)"
+ ]
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_delete_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_schedule_delete_spec.rb
index b197d223463..b846ff0aec8 100644
--- a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_delete_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/pipeline_schedule_delete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'PipelineScheduleDelete' do
+RSpec.describe 'PipelineScheduleDelete', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_play_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_schedule_play_spec.rb
new file mode 100644
index 00000000000..0e43fa024f3
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/ci/pipeline_schedule_play_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'PipelineSchedulePlay', feature_category: :continuious_integration do
+ include GraphqlHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:pipeline_schedule) do
+ create(
+ :ci_pipeline_schedule,
+ :every_minute,
+ project: project,
+ owner: user
+ )
+ end
+
+ let(:mutation) do
+ graphql_mutation(
+ :pipeline_schedule_play,
+ { id: pipeline_schedule.to_global_id.to_s },
+ <<-QL
+ pipelineSchedule { id, nextRunAt }
+ errors
+ QL
+ )
+ end
+
+ let(:mutation_response) { graphql_mutation_response(:pipeline_schedule_play) }
+
+ context 'when unauthorized' do
+ it 'returns an error' do
+ post_graphql_mutation(mutation, current_user: create(:user))
+
+ expect(graphql_errors).not_to be_empty
+ expect(graphql_errors[0]['message'])
+ .to eq(
+ "The resource that you are attempting to access does not exist " \
+ "or you don't have permission to perform this action"
+ )
+ end
+ end
+
+ context 'when authorized' do
+ before do
+ project.add_maintainer(user)
+ pipeline_schedule.update_columns(next_run_at: 2.hours.ago)
+ end
+
+ context 'when mutation succeeds' do
+ it do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(mutation_response['pipelineSchedule']['id']).to include(pipeline_schedule.id.to_s)
+ new_next_run_at = DateTime.parse(mutation_response['pipelineSchedule']['nextRunAt'])
+ expect(new_next_run_at).not_to eq(pipeline_schedule.next_run_at)
+ expect(new_next_run_at).to eq(pipeline_schedule.reset.next_run_at)
+ expect(mutation_response['errors']).to eq([])
+ end
+ end
+
+ context 'when mutation fails' do
+ before do
+ allow(RunPipelineScheduleWorker).to receive(:perform_async).and_return(nil)
+ end
+
+ it do
+ expect(RunPipelineScheduleWorker)
+ .to receive(:perform_async)
+ .with(pipeline_schedule.id, user.id)
+
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(mutation_response['pipelineSchedule']).to be_nil
+ expect(mutation_response['errors']).to match_array(['Unable to schedule a pipeline to run immediately.'])
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_take_ownership_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_schedule_take_ownership_spec.rb
index 8dfbf20d00b..2d1f1565a73 100644
--- a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_take_ownership_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/pipeline_schedule_take_ownership_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'PipelineScheduleTakeOwnership' do
+RSpec.describe 'PipelineScheduleTakeOwnership', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb b/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb
index c808cf5ede9..7a6ee7c2ecc 100644
--- a/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'ProjectCiCdSettingsUpdate' do
+RSpec.describe 'ProjectCiCdSettingsUpdate', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:project) do
diff --git a/spec/requests/api/graphql/mutations/ci/runners_registration_token/reset_spec.rb b/spec/requests/api/graphql/mutations/ci/runners_registration_token/reset_spec.rb
index 54e63df96a6..752242c3ab3 100644
--- a/spec/requests/api/graphql/mutations/ci/runners_registration_token/reset_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/runners_registration_token/reset_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'RunnersRegistrationTokenReset' do
+RSpec.describe 'RunnersRegistrationTokenReset', feature_category: :runner_fleet do
include GraphqlHelpers
let(:mutation) { graphql_mutation(:runners_registration_token_reset, input) }
diff --git a/spec/requests/api/graphql/mutations/clusters/agent_tokens/agent_tokens/create_spec.rb b/spec/requests/api/graphql/mutations/clusters/agent_tokens/agent_tokens/create_spec.rb
index aac8eb22771..f544cef8864 100644
--- a/spec/requests/api/graphql/mutations/clusters/agent_tokens/agent_tokens/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/clusters/agent_tokens/agent_tokens/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Create a new cluster agent token' do
+RSpec.describe 'Create a new cluster agent token', feature_category: :kubernetes_management do
include GraphqlHelpers
let_it_be(:cluster_agent) { create(:cluster_agent) }
diff --git a/spec/requests/api/graphql/mutations/clusters/agents/create_spec.rb b/spec/requests/api/graphql/mutations/clusters/agents/create_spec.rb
index c2ef2362d66..66e6c5cc629 100644
--- a/spec/requests/api/graphql/mutations/clusters/agents/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/clusters/agents/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Create a new cluster agent' do
+RSpec.describe 'Create a new cluster agent', feature_category: :kubernetes_management do
include GraphqlHelpers
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/requests/api/graphql/mutations/clusters/agents/delete_spec.rb b/spec/requests/api/graphql/mutations/clusters/agents/delete_spec.rb
index 4891e64aab8..27a566dfb8c 100644
--- a/spec/requests/api/graphql/mutations/clusters/agents/delete_spec.rb
+++ b/spec/requests/api/graphql/mutations/clusters/agents/delete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Delete a cluster agent' do
+RSpec.describe 'Delete a cluster agent', feature_category: :kubernetes_management do
include GraphqlHelpers
let(:cluster_agent) { create(:cluster_agent) }
diff --git a/spec/requests/api/graphql/mutations/commits/create_spec.rb b/spec/requests/api/graphql/mutations/commits/create_spec.rb
index 619cba99c4e..e298d8284c6 100644
--- a/spec/requests/api/graphql/mutations/commits/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/commits/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Creation of a new commit' do
+RSpec.describe 'Creation of a new commit', feature_category: :source_code_management do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb b/spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb
index ca7c1b2ce5f..97e2a5d0bf7 100644
--- a/spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Updating the container expiration policy' do
+RSpec.describe 'Updating the container expiration policy', feature_category: :container_registry do
include GraphqlHelpers
using RSpec::Parameterized::TableSyntax
diff --git a/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb b/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb
index 5a27d39ecbc..8b76c19cda6 100644
--- a/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Destroying a container repository' do
+RSpec.describe 'Destroying a container repository', feature_category: :container_registry do
using RSpec::Parameterized::TableSyntax
include GraphqlHelpers
@@ -80,25 +80,6 @@ RSpec.describe 'Destroying a container repository' do
it_behaves_like params[:shared_examples_name]
end
-
- context 'with container_registry_delete_repository_with_cron_worker disabled' do
- before do
- project.add_maintainer(user)
- stub_feature_flags(container_registry_delete_repository_with_cron_worker: false)
- end
-
- it 'enqueues a removal job' do
- expect(::Packages::CreateEventService)
- .to receive(:new).with(nil, user, event_name: :delete_repository, scope: :container).and_call_original
- expect(DeleteContainerRepositoryWorker)
- .to receive(:perform_async).with(user.id, container_repository.id)
-
- expect { subject }.to change { ::Packages::Event.count }.by(1)
-
- expect(container_repository_mutation_response).to match_schema('graphql/container_repository')
- expect(container_repository_mutation_response['status']).to eq('DELETE_SCHEDULED')
- end
- end
end
context 'with invalid id' do
diff --git a/spec/requests/api/graphql/mutations/container_repository/destroy_tags_spec.rb b/spec/requests/api/graphql/mutations/container_repository/destroy_tags_spec.rb
index ef00f45ef18..9e07a831076 100644
--- a/spec/requests/api/graphql/mutations/container_repository/destroy_tags_spec.rb
+++ b/spec/requests/api/graphql/mutations/container_repository/destroy_tags_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Destroying a container repository tags' do
+RSpec.describe 'Destroying a container repository tags', feature_category: :container_registry do
include_context 'container repository delete tags service shared context'
using RSpec::Parameterized::TableSyntax
diff --git a/spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb b/spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb
index 66facdebe78..ea2ce8a13e2 100644
--- a/spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Creation of a new Custom Emoji' do
+RSpec.describe 'Creation of a new Custom Emoji', feature_category: :not_owned do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb b/spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb
index 7d25206e617..ad7a043909a 100644
--- a/spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Deletion of custom emoji' do
+RSpec.describe 'Deletion of custom emoji', feature_category: :not_owned do
include GraphqlHelpers
let_it_be(:group) { create(:group) }
diff --git a/spec/requests/api/graphql/mutations/dependency_proxy/group_settings/update_spec.rb b/spec/requests/api/graphql/mutations/dependency_proxy/group_settings/update_spec.rb
index 9eb13e534ac..5d5696d3f66 100644
--- a/spec/requests/api/graphql/mutations/dependency_proxy/group_settings/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/dependency_proxy/group_settings/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Updating the dependency proxy group settings' do
+RSpec.describe 'Updating the dependency proxy group settings', feature_category: :dependency_proxy do
include GraphqlHelpers
using RSpec::Parameterized::TableSyntax
diff --git a/spec/requests/api/graphql/mutations/dependency_proxy/image_ttl_group_policy/update_spec.rb b/spec/requests/api/graphql/mutations/dependency_proxy/image_ttl_group_policy/update_spec.rb
index 31ba7ecdf0e..66ee17f356c 100644
--- a/spec/requests/api/graphql/mutations/dependency_proxy/image_ttl_group_policy/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/dependency_proxy/image_ttl_group_policy/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Updating the dependency proxy image ttl policy' do
+RSpec.describe 'Updating the dependency proxy image ttl policy', feature_category: :dependency_proxy do
include GraphqlHelpers
using RSpec::Parameterized::TableSyntax
diff --git a/spec/requests/api/graphql/mutations/design_management/delete_spec.rb b/spec/requests/api/graphql/mutations/design_management/delete_spec.rb
index e2ab08b301b..7ea32ae6d19 100644
--- a/spec/requests/api/graphql/mutations/design_management/delete_spec.rb
+++ b/spec/requests/api/graphql/mutations/design_management/delete_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "deleting designs" do
+RSpec.describe "deleting designs", feature_category: :design_management do
include GraphqlHelpers
include DesignManagementTestHelpers
diff --git a/spec/requests/api/graphql/mutations/design_management/move_spec.rb b/spec/requests/api/graphql/mutations/design_management/move_spec.rb
index dd121ec733e..27b5259c56b 100644
--- a/spec/requests/api/graphql/mutations/design_management/move_spec.rb
+++ b/spec/requests/api/graphql/mutations/design_management/move_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-RSpec.describe "moving designs" do
+RSpec.describe "moving designs", feature_category: :design_management do
include GraphqlHelpers
include DesignManagementTestHelpers
diff --git a/spec/requests/api/graphql/mutations/design_management/upload_spec.rb b/spec/requests/api/graphql/mutations/design_management/upload_spec.rb
index d3e6c689a59..9b42b32c150 100644
--- a/spec/requests/api/graphql/mutations/design_management/upload_spec.rb
+++ b/spec/requests/api/graphql/mutations/design_management/upload_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
-RSpec.describe "uploading designs" do
+RSpec.describe "uploading designs", feature_category: :design_management do
include GraphqlHelpers
include DesignManagementTestHelpers
include WorkhorseHelpers
diff --git a/spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb b/spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb
index 632a934cd95..16d3bbb6518 100644
--- a/spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb
+++ b/spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Toggling the resolve status of a discussion' do
+RSpec.describe 'Toggling the resolve status of a discussion', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project, :public, :repository) }
diff --git a/spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb b/spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb
index 3771ae0746e..0e9317a4879 100644
--- a/spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Update Environment Canary Ingress', :clean_gitlab_redis_cache do
+RSpec.describe 'Update Environment Canary Ingress', :clean_gitlab_redis_cache, feature_category: :deployment_management do
include GraphqlHelpers
include KubernetesHelpers
diff --git a/spec/requests/api/graphql/mutations/groups/update_spec.rb b/spec/requests/api/graphql/mutations/groups/update_spec.rb
index b9dfb8e37ab..ea3d42a4463 100644
--- a/spec/requests/api/graphql/mutations/groups/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/groups/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'GroupUpdate' do
+RSpec.describe 'GroupUpdate', feature_category: :subgroups do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb b/spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb
index fc3b666dd3d..49cee4f6801 100644
--- a/spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Creating an incident timeline event' do
+RSpec.describe 'Creating an incident timeline event', feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/incident_management/timeline_event/destroy_spec.rb b/spec/requests/api/graphql/mutations/incident_management/timeline_event/destroy_spec.rb
index 85208869ad9..6e1a7b36736 100644
--- a/spec/requests/api/graphql/mutations/incident_management/timeline_event/destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/incident_management/timeline_event/destroy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Removing an incident timeline event' do
+RSpec.describe 'Removing an incident timeline event', feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb b/spec/requests/api/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb
index 62eeecb3fb7..ca9557b3183 100644
--- a/spec/requests/api/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb
+++ b/spec/requests/api/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Promote an incident timeline event from a comment' do
+RSpec.describe 'Promote an incident timeline event from a comment', feature_category: :incident_management do
include GraphqlHelpers
include NotesHelper
diff --git a/spec/requests/api/graphql/mutations/incident_management/timeline_event/update_spec.rb b/spec/requests/api/graphql/mutations/incident_management/timeline_event/update_spec.rb
index 542d51b990f..163c689e399 100644
--- a/spec/requests/api/graphql/mutations/incident_management/timeline_event/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/incident_management/timeline_event/update_spec.rb
@@ -2,24 +2,36 @@
require 'spec_helper'
-RSpec.describe 'Updating an incident timeline event' do
+RSpec.describe 'Updating an incident timeline event', feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:incident) { create(:incident, project: project) }
+ let_it_be(:tag1) { create(:incident_management_timeline_event_tag, project: project, name: 'Tag 1') }
+ let_it_be(:tag2) { create(:incident_management_timeline_event_tag, project: project, name: 'Tag 2') }
let_it_be_with_reload(:timeline_event) do
create(:incident_management_timeline_event, incident: incident, project: project)
end
+ # Pre-attach a tag to the event
+ let_it_be(:tag_link1) do
+ create(:incident_management_timeline_event_tag_link,
+ timeline_event: timeline_event,
+ timeline_event_tag: tag1
+ )
+ end
+
let(:occurred_at) { 1.minute.ago.iso8601 }
let(:note) { 'Updated note' }
+ let(:tag_names) { [] }
let(:variables) do
{
id: timeline_event.to_global_id.to_s,
note: note,
- occurred_at: occurred_at
+ occurred_at: occurred_at,
+ timeline_event_tag_names: tag_names
}
end
@@ -33,6 +45,7 @@ RSpec.describe 'Updating an incident timeline event' do
author { id username }
updatedByUser { id username }
incident { id title }
+ timelineEventTags { nodes { name } }
note
noteHtml
occurredAt
@@ -71,6 +84,9 @@ RSpec.describe 'Updating an incident timeline event' do
'id' => incident.to_global_id.to_s,
'title' => incident.title
},
+ 'timelineEventTags' => {
+ 'nodes' => []
+ },
'note' => note,
'noteHtml' => timeline_event.note_html,
'occurredAt' => occurred_at,
@@ -85,4 +101,27 @@ RSpec.describe 'Updating an incident timeline event' do
it_behaves_like 'timeline event mutation responds with validation error',
error_message: 'Timeline text is too long (maximum is 280 characters)'
end
+
+ context 'when timeline event tag names are passed' do
+ context 'when tags exist' do
+ let(:tag_names) { [tag2.name] }
+
+ it 'removes tag1 and adds tag2' do
+ post_graphql_mutation(mutation, current_user: user)
+
+ timeline_event_response = mutation_response['timelineEvent']
+ tag_names = timeline_event_response['timelineEventTags']['nodes']
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(tag_names).to contain_exactly({ "name" => tag2.name })
+ end
+ end
+
+ context 'when tags do not exist' do
+ let(:tag_names) { ['some other tag'] }
+
+ it_behaves_like 'timeline event mutation responds with validation error',
+ error_message: "Following tags don't exist: [\"some other tag\"]"
+ end
+ end
end
diff --git a/spec/requests/api/graphql/mutations/incident_management/timeline_event_tag/create_spec.rb b/spec/requests/api/graphql/mutations/incident_management/timeline_event_tag/create_spec.rb
index 7476499d9da..b37a5331421 100644
--- a/spec/requests/api/graphql/mutations/incident_management/timeline_event_tag/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/incident_management/timeline_event_tag/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Creating a timeline event tag' do
+RSpec.describe 'Creating a timeline event tag', feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/issues/create_spec.rb b/spec/requests/api/graphql/mutations/issues/create_spec.rb
index a489b7424e8..d2d2f0014d6 100644
--- a/spec/requests/api/graphql/mutations/issues/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Create an issue' do
+RSpec.describe 'Create an issue', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/issues/link_alerts_spec.rb b/spec/requests/api/graphql/mutations/issues/link_alerts_spec.rb
new file mode 100644
index 00000000000..85e21952f47
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/issues/link_alerts_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Link alerts to an incident', feature_category: :incident_management do
+ include GraphqlHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:linked_alert) { create(:alert_management_alert, project: project) }
+ let_it_be(:alert1) { create(:alert_management_alert, project: project) }
+ let_it_be(:alert2) { create(:alert_management_alert, project: project) }
+ let_it_be(:incident) { create(:incident, project: project, alert_management_alerts: [linked_alert]) }
+
+ let(:mutation) do
+ variables = {
+ project_path: project.full_path,
+ iid: incident.iid.to_s,
+ alert_references: [alert1.to_reference, alert2.details_url]
+ }
+
+ graphql_mutation(:issue_link_alerts, variables,
+ <<-QL.strip_heredoc
+ clientMutationId
+ errors
+ issue {
+ iid
+ alertManagementAlerts {
+ nodes {
+ iid
+ }
+ }
+ }
+ QL
+ )
+ end
+
+ def mutation_response
+ graphql_mutation_response(:issue_link_alerts)
+ end
+
+ context 'when the user is not allowed to update the incident' do
+ it 'returns an error' do
+ error = Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(graphql_errors).to include(a_hash_including('message' => error))
+ end
+ end
+
+ context 'when the user is allowed to update the incident' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'links alerts to the incident' do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expected_response = [linked_alert, alert1, alert2].map { |a| { 'iid' => a.iid.to_s } }
+ expect(mutation_response.dig('issue', 'alertManagementAlerts', 'nodes')).to match_array(expected_response)
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/issues/move_spec.rb b/spec/requests/api/graphql/mutations/issues/move_spec.rb
index 20ed16879f6..7d9579067b6 100644
--- a/spec/requests/api/graphql/mutations/issues/move_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/move_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Moving an issue' do
+RSpec.describe 'Moving an issue', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/issues/set_confidential_spec.rb b/spec/requests/api/graphql/mutations/issues/set_confidential_spec.rb
index 12ab504da14..c5e6901d8f8 100644
--- a/spec/requests/api/graphql/mutations/issues/set_confidential_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/set_confidential_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting an issue as confidential' do
+RSpec.describe 'Setting an issue as confidential', feature_category: :team_planning do
include GraphqlHelpers
let(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb b/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb
index 395a490bfc3..9fce5f8497f 100644
--- a/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting issues crm contacts' do
+RSpec.describe 'Setting issues crm contacts', feature_category: :service_desk do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/issues/set_due_date_spec.rb b/spec/requests/api/graphql/mutations/issues/set_due_date_spec.rb
index 8e223b6fdaf..1a5a64e4196 100644
--- a/spec/requests/api/graphql/mutations/issues/set_due_date_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/set_due_date_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting Due Date of an issue' do
+RSpec.describe 'Setting Due Date of an issue', feature_category: :team_planning do
include GraphqlHelpers
let(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/issues/set_escalation_status_spec.rb b/spec/requests/api/graphql/mutations/issues/set_escalation_status_spec.rb
index a81364d37b2..8fc3ad4236d 100644
--- a/spec/requests/api/graphql/mutations/issues/set_escalation_status_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/set_escalation_status_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting the escalation status of an incident' do
+RSpec.describe 'Setting the escalation status of an incident', feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/mutations/issues/set_locked_spec.rb b/spec/requests/api/graphql/mutations/issues/set_locked_spec.rb
index 435ed0f9eb2..a8025894b1e 100644
--- a/spec/requests/api/graphql/mutations/issues/set_locked_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/set_locked_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting an issue as locked' do
+RSpec.describe 'Setting an issue as locked', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/issues/set_severity_spec.rb b/spec/requests/api/graphql/mutations/issues/set_severity_spec.rb
index cd9d695bd2c..77262c7f64f 100644
--- a/spec/requests/api/graphql/mutations/issues/set_severity_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/set_severity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting severity level of an incident' do
+RSpec.describe 'Setting severity level of an incident', feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/issues/set_subscription_spec.rb b/spec/requests/api/graphql/mutations/issues/set_subscription_spec.rb
index 1edc1e0553b..6c8e5b1d15d 100644
--- a/spec/requests/api/graphql/mutations/issues/set_subscription_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/set_subscription_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting subscribed status of an issue' do
+RSpec.describe 'Setting subscribed status of an issue', feature_category: :team_planning do
include GraphqlHelpers
it_behaves_like 'a subscribable resource api' do
diff --git a/spec/requests/api/graphql/mutations/issues/unlink_alerts_spec.rb b/spec/requests/api/graphql/mutations/issues/unlink_alerts_spec.rb
new file mode 100644
index 00000000000..7f6f968b1dd
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/issues/unlink_alerts_spec.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Unlink alert from an incident', feature_category: :incident_management do
+ include GraphqlHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:another_project) { create(:project) }
+ let_it_be(:internal_alert) { create(:alert_management_alert, project: project) }
+ let_it_be(:external_alert) { create(:alert_management_alert, project: another_project) }
+ let_it_be(:incident) do
+ create(:incident, project: project, alert_management_alerts: [internal_alert, external_alert])
+ end
+
+ let(:mutation) do
+ variables = {
+ project_path: project.full_path,
+ iid: incident.iid.to_s,
+ alert_id: alert_to_unlink.to_global_id.to_s
+ }
+
+ graphql_mutation(:issue_unlink_alert, variables,
+ <<-QL.strip_heredoc
+ clientMutationId
+ errors
+ issue {
+ iid
+ alertManagementAlerts {
+ nodes {
+ id
+ }
+ }
+ }
+ QL
+ )
+ end
+
+ def mutation_response
+ graphql_mutation_response(:issue_unlink_alert)
+ end
+
+ context 'when the user is not allowed to update the incident' do
+ let(:alert_to_unlink) { internal_alert }
+
+ it 'returns an error' do
+ error = Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(graphql_errors).to include(a_hash_including('message' => error))
+ end
+ end
+
+ context 'when the user is allowed to update the incident' do
+ before_all do
+ project.add_developer(user)
+ end
+
+ shared_examples 'unlinking' do
+ it 'unlinks the alert from the incident', :aggregate_failures do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expected_response = visible_remainded_alerts.map { |a| { 'id' => a.to_global_id.to_s } }
+ expect(mutation_response.dig('issue', 'alertManagementAlerts', 'nodes')).to match_array(expected_response)
+
+ expect(incident.reload.alert_management_alerts).to match_array(actual_remainded_alerts)
+ end
+ end
+
+ context 'when the alert is internal' do
+ let(:alert_to_unlink) { internal_alert }
+ let(:actual_remainded_alerts) { [external_alert] }
+ let(:visible_remainded_alerts) { [] } # The user cannot fetch external alerts without reading permissions
+
+ it_behaves_like 'unlinking'
+ end
+
+ context 'when the alert is external' do
+ let(:alert_to_unlink) { external_alert }
+ let(:actual_remainded_alerts) { [internal_alert] }
+ let(:visible_remainded_alerts) { [internal_alert] }
+
+ it_behaves_like 'unlinking'
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/issues/update_spec.rb b/spec/requests/api/graphql/mutations/issues/update_spec.rb
index f38deb426b1..e51c057c182 100644
--- a/spec/requests/api/graphql/mutations/issues/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Update of an existing issue' do
+RSpec.describe 'Update of an existing issue', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb b/spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb
index b438e1ba881..ab15aa97680 100644
--- a/spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb
+++ b/spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Importing Jira Users' do
+RSpec.describe 'Importing Jira Users', feature_category: :integrations do
include JiraIntegrationHelpers
include GraphqlHelpers
diff --git a/spec/requests/api/graphql/mutations/jira_import/start_spec.rb b/spec/requests/api/graphql/mutations/jira_import/start_spec.rb
index 1508ba31e37..a864bc88afc 100644
--- a/spec/requests/api/graphql/mutations/jira_import/start_spec.rb
+++ b/spec/requests/api/graphql/mutations/jira_import/start_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Starting a Jira Import' do
+RSpec.describe 'Starting a Jira Import', feature_category: :integrations do
include JiraIntegrationHelpers
include GraphqlHelpers
diff --git a/spec/requests/api/graphql/mutations/labels/create_spec.rb b/spec/requests/api/graphql/mutations/labels/create_spec.rb
index d19411f6c1d..607e20af977 100644
--- a/spec/requests/api/graphql/mutations/labels/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/labels/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Mutations::Labels::Create do
+RSpec.describe Mutations::Labels::Create, feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb
index 3a4508489a1..c954fd50cc4 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Creation of a new merge request' do
+RSpec.describe 'Creation of a new merge request', feature_category: :code_review do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb
index 2e4f35cbcde..c41161eff2b 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting assignees of a merge request' do
+RSpec.describe 'Setting assignees of a merge request', feature_category: :code_review do
include GraphqlHelpers
let(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb
index 8cec5867aca..364d13291db 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting assignees of a merge request', :assume_throttled do
+RSpec.describe 'Setting assignees of a merge request', :assume_throttled, feature_category: :code_review do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_draft_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_draft_spec.rb
index bea2365eaa6..b48a94fbeb9 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/set_draft_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_draft_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting Draft status of a merge request' do
+RSpec.describe 'Setting Draft status of a merge request', feature_category: :code_review do
include GraphqlHelpers
let(:current_user) { create(:user) }
@@ -64,7 +64,7 @@ RSpec.describe 'Setting Draft status of a merge request' do
post_graphql_mutation(mutation, current_user: current_user)
expect(response).to have_gitlab_http_status(:success)
- expect(mutation_response['mergeRequest']['title']).not_to start_with(/draft\:/)
+ expect(mutation_response['mergeRequest']['title']).not_to start_with(/draft:/)
end
it 'unmarks the merge request as `Draft`' do
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb
index a1a35bc1dcc..d88982c508c 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting locked status of a merge request' do
+RSpec.describe 'Setting locked status of a merge request', feature_category: :code_review do
include GraphqlHelpers
let(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb
index d7e2602bd0a..a0f0e45d1fc 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting milestone of a merge request' do
+RSpec.describe 'Setting milestone of a merge request', feature_category: :code_review do
include GraphqlHelpers
let(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_reviewers_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_reviewers_spec.rb
index be786256ef2..a5be2a95c8b 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/set_reviewers_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_reviewers_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting reviewers of a merge request', :assume_throttled do
+RSpec.describe 'Setting reviewers of a merge request', :assume_throttled, feature_category: :code_review do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb
index d90faa605c0..daf1f529847 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting subscribed status of a merge request' do
+RSpec.describe 'Setting subscribed status of a merge request', feature_category: :code_review do
include GraphqlHelpers
it_behaves_like 'a subscribable resource api' do
diff --git a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb
index 9ef443af76a..bce57b47aab 100644
--- a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Mutations::Metrics::Dashboard::Annotations::Create do
+RSpec.describe Mutations::Metrics::Dashboard::Annotations::Create, feature_category: :metrics do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb
index b956734068c..f505dc25dc0 100644
--- a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb
+++ b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Mutations::Metrics::Dashboard::Annotations::Delete do
+RSpec.describe Mutations::Metrics::Dashboard::Annotations::Delete, feature_category: :metrics do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb b/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb
index 567d8799d93..f4f4f34fe29 100644
--- a/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Updating the package settings' do
+RSpec.describe 'Updating the package settings', feature_category: :package_registry do
include GraphqlHelpers
using RSpec::Parameterized::TableSyntax
diff --git a/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb
index a432fb17a70..3cf78230a4c 100644
--- a/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb
+++ b/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Adding a DiffNote' do
+RSpec.describe 'Adding a DiffNote', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb
index 8f2438cb741..0ce239d9ca5 100644
--- a/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb
+++ b/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Adding an image DiffNote' do
+RSpec.describe 'Adding an image DiffNote', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
index 9c3842db31a..00e25909746 100644
--- a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
+++ b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Adding a Note' do
+RSpec.describe 'Adding a Note', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
@@ -102,6 +102,35 @@ RSpec.describe 'Adding a Note' do
it_behaves_like 'a Note mutation with confidential notes'
end
+
+ context 'as work item' do
+ let(:noteable) { create(:work_item, :issue, project: project) }
+
+ context 'when using internal param' do
+ let(:variables_extra) { { internal: true } }
+
+ it_behaves_like 'a Note mutation with confidential notes'
+ end
+
+ context 'when using deprecated confidential param' do
+ let(:variables_extra) { { confidential: true } }
+
+ it_behaves_like 'a Note mutation with confidential notes'
+ end
+
+ context 'without notes widget' do
+ let(:variables_extra) { {} }
+
+ before do
+ stub_const('WorkItems::Type::BASE_TYPES', { issue: { name: 'NoNotesWidget', enum_value: 0 } })
+ stub_const('WorkItems::Type::WIDGETS_FOR_TYPE', { issue: [::WorkItems::Widgets::Description] })
+ end
+
+ it_behaves_like 'a Note mutation that does not create a Note'
+ it_behaves_like 'a mutation that returns top-level errors',
+ errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
+ end
+ end
end
context 'when body only contains quick actions' do
diff --git a/spec/requests/api/graphql/mutations/notes/destroy_spec.rb b/spec/requests/api/graphql/mutations/notes/destroy_spec.rb
index 49f09fadfea..eb45e2aa033 100644
--- a/spec/requests/api/graphql/mutations/notes/destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/notes/destroy_spec.rb
@@ -2,17 +2,14 @@
require 'spec_helper'
-RSpec.describe 'Destroying a Note' do
+RSpec.describe 'Destroying a Note', feature_category: :team_planning do
include GraphqlHelpers
- let!(:note) { create(:note) }
- let(:mutation) do
- variables = {
- id: GitlabSchema.id_from_object(note).to_s
- }
-
- graphql_mutation(:destroy_note, variables)
- end
+ let(:noteable) { create(:work_item, :issue) }
+ let!(:note) { create(:note, noteable: noteable, project: noteable.project) }
+ let(:global_note_id) { GitlabSchema.id_from_object(note).to_s }
+ let(:variables) { { id: global_note_id } }
+ let(:mutation) { graphql_mutation(:destroy_note, variables) }
def mutation_response
graphql_mutation_response(:destroy_note)
@@ -47,5 +44,31 @@ RSpec.describe 'Destroying a Note' do
expect(mutation_response).to have_key('note')
expect(mutation_response['note']).to be_nil
end
+
+ context 'when note is system' do
+ let!(:note) { create(:note, :system) }
+
+ it 'does not destroy system note' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ end.not_to change { Note.count }
+ end
+ end
+
+ context 'without notes widget' do
+ before do
+ stub_const('WorkItems::Type::BASE_TYPES', { issue: { name: 'NoNotesWidget', enum_value: 0 } })
+ stub_const('WorkItems::Type::WIDGETS_FOR_TYPE', { issue: [::WorkItems::Widgets::Description] })
+ end
+
+ it 'does not update the Note' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ end.to not_change { Note.count }
+ end
+
+ it_behaves_like 'a mutation that returns top-level errors',
+ errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
+ end
end
end
diff --git a/spec/requests/api/graphql/mutations/notes/reposition_image_diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/reposition_image_diff_note_spec.rb
index c4674155aa0..e9cd27a1e20 100644
--- a/spec/requests/api/graphql/mutations/notes/reposition_image_diff_note_spec.rb
+++ b/spec/requests/api/graphql/mutations/notes/reposition_image_diff_note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Repositioning an ImageDiffNote' do
+RSpec.describe 'Repositioning an ImageDiffNote', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:noteable) { create(:merge_request) }
diff --git a/spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb
index cfd0b34b815..a5cd3c8b019 100644
--- a/spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb
+++ b/spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Updating an image DiffNote' do
+RSpec.describe 'Updating an image DiffNote', feature_category: :team_planning do
include GraphqlHelpers
using RSpec::Parameterized::TableSyntax
diff --git a/spec/requests/api/graphql/mutations/notes/update/note_spec.rb b/spec/requests/api/graphql/mutations/notes/update/note_spec.rb
index bae5c58abff..dff8a87314b 100644
--- a/spec/requests/api/graphql/mutations/notes/update/note_spec.rb
+++ b/spec/requests/api/graphql/mutations/notes/update/note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Updating a Note' do
+RSpec.describe 'Updating a Note', feature_category: :team_planning do
include GraphqlHelpers
let!(:note) { create(:note, note: original_body) }
@@ -36,49 +36,32 @@ RSpec.describe 'Updating a Note' do
it_behaves_like 'a Note mutation when the given resource id is not for a Note'
- it 'updates the Note' do
- post_graphql_mutation(mutation, current_user: current_user)
-
- expect(note.reload.note).to eq(updated_body)
- end
-
- it 'returns the updated Note' do
- post_graphql_mutation(mutation, current_user: current_user)
-
- expect(mutation_response['note']['body']).to eq(updated_body)
- end
+ it_behaves_like 'a Note mutation updates a note successfully'
+ it_behaves_like 'a Note mutation update with errors'
+ it_behaves_like 'a Note mutation update only with quick actions'
- context 'when there are ActiveRecord validation errors' do
- let(:params) { { body: '', confidential: true } }
+ context 'for work item' do
+ let(:noteable) { create(:work_item, :issue) }
+ let(:note) { create(:note, noteable: noteable, project: noteable.project, note: original_body) }
- it_behaves_like 'a mutation that returns errors in the response',
- errors: ["Note can't be blank", 'Confidential can not be changed for existing notes']
+ it_behaves_like 'a Note mutation updates a note successfully'
+ it_behaves_like 'a Note mutation update with errors'
+ it_behaves_like 'a Note mutation update only with quick actions'
- it 'does not update the Note' do
- post_graphql_mutation(mutation, current_user: current_user)
-
- expect(note.reload.note).to eq(original_body)
- expect(note.confidential).to be_falsey
- end
-
- it 'returns the original Note' do
- post_graphql_mutation(mutation, current_user: current_user)
-
- expect(mutation_response['note']['body']).to eq(original_body)
- expect(mutation_response['note']['confidential']).to be_falsey
- end
- end
+ context 'without notes widget' do
+ before do
+ stub_const('WorkItems::Type::BASE_TYPES', { issue: { name: 'NoNotesWidget', enum_value: 0 } })
+ stub_const('WorkItems::Type::WIDGETS_FOR_TYPE', { issue: [::WorkItems::Widgets::Description] })
+ end
- context 'when body only contains quick actions' do
- let(:updated_body) { '/close' }
+ it 'does not update the Note' do
+ post_graphql_mutation(mutation, current_user: current_user)
- it 'returns a nil note and empty errors' do
- post_graphql_mutation(mutation, current_user: current_user)
+ expect(note.reload.note).to eq(original_body)
+ end
- expect(mutation_response).to include(
- 'errors' => [],
- 'note' => nil
- )
+ it_behaves_like 'a mutation that returns top-level errors',
+ errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
end
end
end
diff --git a/spec/requests/api/graphql/mutations/packages/bulk_destroy_spec.rb b/spec/requests/api/graphql/mutations/packages/bulk_destroy_spec.rb
index 1fe01af4f1c..d0980a2b43d 100644
--- a/spec/requests/api/graphql/mutations/packages/bulk_destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/packages/bulk_destroy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Destroying multiple packages' do
+RSpec.describe 'Destroying multiple packages', feature_category: :package_registry do
using RSpec::Parameterized::TableSyntax
include GraphqlHelpers
diff --git a/spec/requests/api/graphql/mutations/packages/cleanup/policy/update_spec.rb b/spec/requests/api/graphql/mutations/packages/cleanup/policy/update_spec.rb
index 7e00f3ca53a..2540e06be9a 100644
--- a/spec/requests/api/graphql/mutations/packages/cleanup/policy/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/packages/cleanup/policy/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Updating the packages cleanup policy' do
+RSpec.describe 'Updating the packages cleanup policy', feature_category: :package_registry do
include GraphqlHelpers
using RSpec::Parameterized::TableSyntax
diff --git a/spec/requests/api/graphql/mutations/packages/destroy_file_spec.rb b/spec/requests/api/graphql/mutations/packages/destroy_file_spec.rb
index cd25aba9e00..a4b7001fdd5 100644
--- a/spec/requests/api/graphql/mutations/packages/destroy_file_spec.rb
+++ b/spec/requests/api/graphql/mutations/packages/destroy_file_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Destroying a package file' do
+RSpec.describe 'Destroying a package file', feature_category: :package_registry do
using RSpec::Parameterized::TableSyntax
include GraphqlHelpers
diff --git a/spec/requests/api/graphql/mutations/packages/destroy_files_spec.rb b/spec/requests/api/graphql/mutations/packages/destroy_files_spec.rb
index 002cd634ebd..cdd05d80fc2 100644
--- a/spec/requests/api/graphql/mutations/packages/destroy_files_spec.rb
+++ b/spec/requests/api/graphql/mutations/packages/destroy_files_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Destroying multiple package files' do
+RSpec.describe 'Destroying multiple package files', feature_category: :package_registry do
using RSpec::Parameterized::TableSyntax
include GraphqlHelpers
diff --git a/spec/requests/api/graphql/mutations/packages/destroy_spec.rb b/spec/requests/api/graphql/mutations/packages/destroy_spec.rb
index 2340a6a36d8..86167e7116f 100644
--- a/spec/requests/api/graphql/mutations/packages/destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/packages/destroy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Destroying a package' do
+RSpec.describe 'Destroying a package', feature_category: :package_registry do
using RSpec::Parameterized::TableSyntax
include GraphqlHelpers
diff --git a/spec/requests/api/graphql/mutations/release_asset_links/create_spec.rb b/spec/requests/api/graphql/mutations/release_asset_links/create_spec.rb
index c7a4cb1ebce..418a0e47a36 100644
--- a/spec/requests/api/graphql/mutations/release_asset_links/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/release_asset_links/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Creation of a new release asset link' do
+RSpec.describe 'Creation of a new release asset link', feature_category: :release_orchestration do
include GraphqlHelpers
let_it_be(:project) { create(:project, :private, :repository) }
diff --git a/spec/requests/api/graphql/mutations/release_asset_links/delete_spec.rb b/spec/requests/api/graphql/mutations/release_asset_links/delete_spec.rb
index 57489c82ec2..b6d2c3f691d 100644
--- a/spec/requests/api/graphql/mutations/release_asset_links/delete_spec.rb
+++ b/spec/requests/api/graphql/mutations/release_asset_links/delete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Deletes a release asset link' do
+RSpec.describe 'Deletes a release asset link', feature_category: :release_orchestration do
include GraphqlHelpers
let_it_be(:project) { create(:project, :private, :repository) }
diff --git a/spec/requests/api/graphql/mutations/release_asset_links/update_spec.rb b/spec/requests/api/graphql/mutations/release_asset_links/update_spec.rb
index 92b558d4be3..61395cc4042 100644
--- a/spec/requests/api/graphql/mutations/release_asset_links/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/release_asset_links/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Updating an existing release asset link' do
+RSpec.describe 'Updating an existing release asset link', feature_category: :release_orchestration do
include GraphqlHelpers
let_it_be(:project) { create(:project, :private, :repository) }
diff --git a/spec/requests/api/graphql/mutations/releases/create_spec.rb b/spec/requests/api/graphql/mutations/releases/create_spec.rb
index 2541072b766..295b8c0e97e 100644
--- a/spec/requests/api/graphql/mutations/releases/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/releases/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Creation of a new release' do
+RSpec.describe 'Creation of a new release', feature_category: :release_orchestration do
include GraphqlHelpers
include Presentable
diff --git a/spec/requests/api/graphql/mutations/releases/delete_spec.rb b/spec/requests/api/graphql/mutations/releases/delete_spec.rb
index eb4f0b594ea..bb398787cc6 100644
--- a/spec/requests/api/graphql/mutations/releases/delete_spec.rb
+++ b/spec/requests/api/graphql/mutations/releases/delete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Deleting a release' do
+RSpec.describe 'Deleting a release', feature_category: :release_orchestration do
include GraphqlHelpers
include Presentable
diff --git a/spec/requests/api/graphql/mutations/releases/update_spec.rb b/spec/requests/api/graphql/mutations/releases/update_spec.rb
index 240db764f40..2b88576a70e 100644
--- a/spec/requests/api/graphql/mutations/releases/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/releases/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Updating an existing release' do
+RSpec.describe 'Updating an existing release', feature_category: :release_orchestration do
include GraphqlHelpers
include Presentable
diff --git a/spec/requests/api/graphql/mutations/security/ci_configuration/configure_sast_iac_spec.rb b/spec/requests/api/graphql/mutations/security/ci_configuration/configure_sast_iac_spec.rb
index 0c034f38dc8..cdd25f8f6ec 100644
--- a/spec/requests/api/graphql/mutations/security/ci_configuration/configure_sast_iac_spec.rb
+++ b/spec/requests/api/graphql/mutations/security/ci_configuration/configure_sast_iac_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'ConfigureSastIac' do
+RSpec.describe 'ConfigureSastIac', feature_category: :static_application_security_testing do
include GraphqlHelpers
let_it_be(:project) { create(:project, :test_repo) }
diff --git a/spec/requests/api/graphql/mutations/security/ci_configuration/configure_secret_detection_spec.rb b/spec/requests/api/graphql/mutations/security/ci_configuration/configure_secret_detection_spec.rb
index 8fa6e44b208..370abf2fe00 100644
--- a/spec/requests/api/graphql/mutations/security/ci_configuration/configure_secret_detection_spec.rb
+++ b/spec/requests/api/graphql/mutations/security/ci_configuration/configure_secret_detection_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'ConfigureSecretDetection' do
+RSpec.describe 'ConfigureSecretDetection', feature_category: :secret_detection do
include GraphqlHelpers
let_it_be(:project) { create(:project, :test_repo) }
diff --git a/spec/requests/api/graphql/mutations/snippets/create_spec.rb b/spec/requests/api/graphql/mutations/snippets/create_spec.rb
index 264fa5732c3..0b1af2bf628 100644
--- a/spec/requests/api/graphql/mutations/snippets/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Creating a Snippet' do
+RSpec.describe 'Creating a Snippet', feature_category: :source_code_management do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/snippets/destroy_spec.rb b/spec/requests/api/graphql/mutations/snippets/destroy_spec.rb
index 1be8ce142ac..09e884d9412 100644
--- a/spec/requests/api/graphql/mutations/snippets/destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/destroy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Destroying a Snippet' do
+RSpec.describe 'Destroying a Snippet', feature_category: :source_code_management do
include GraphqlHelpers
let(:current_user) { snippet.author }
diff --git a/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb b/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb
index 77fd6cddc09..9a8c027da8a 100644
--- a/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Mark snippet as spam' do
+RSpec.describe 'Mark snippet as spam', feature_category: :source_code_management do
include GraphqlHelpers
include AfterNextHelpers
diff --git a/spec/requests/api/graphql/mutations/snippets/update_spec.rb b/spec/requests/api/graphql/mutations/snippets/update_spec.rb
index 1a5d3620f22..fa087e6773c 100644
--- a/spec/requests/api/graphql/mutations/snippets/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Updating a Snippet' do
+RSpec.describe 'Updating a Snippet', feature_category: :source_code_management do
include GraphqlHelpers
include SessionHelpers
@@ -192,11 +192,18 @@ RSpec.describe 'Updating a Snippet' do
stub_session('warden.user.user.key' => [[current_user.id], current_user.authenticatable_salt])
end
- it_behaves_like 'Snowplow event tracking' do
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:user) { current_user }
+ let(:property) { 'g_edit_by_snippet_ide' }
let(:namespace) { project.namespace }
- let(:category) { 'ide_edit' }
- let(:action) { 'g_edit_by_snippet_ide' }
+ let(:category) { 'Gitlab::UsageDataCounters::EditorUniqueCounter' }
+ let(:action) { 'ide_edit' }
+ let(:label) { 'usage_activity_by_stage_monthly.create.action_monthly_active_users_ide_edit' }
+ let(:context) do
+ [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event_name).to_context]
+ end
+
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
end
end
diff --git a/spec/requests/api/graphql/mutations/timelogs/create_spec.rb b/spec/requests/api/graphql/mutations/timelogs/create_spec.rb
index eea04b89783..42249818a92 100644
--- a/spec/requests/api/graphql/mutations/timelogs/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/timelogs/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Create a timelog' do
+RSpec.describe 'Create a timelog', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:author) { create(:user) }
@@ -11,14 +11,6 @@ RSpec.describe 'Create a timelog' do
let(:current_user) { nil }
let(:users_container) { project }
- let(:mutation) do
- graphql_mutation(:timelogCreate, {
- 'time_spent' => time_spent,
- 'spent_at' => '2022-07-08',
- 'summary' => 'Test summary',
- 'issuable_id' => issuable.to_global_id.to_s
- })
- end
let(:mutation_response) { graphql_mutation_response(:timelog_create) }
diff --git a/spec/requests/api/graphql/mutations/timelogs/delete_spec.rb b/spec/requests/api/graphql/mutations/timelogs/delete_spec.rb
index d304bfbdf00..d04b4d193e6 100644
--- a/spec/requests/api/graphql/mutations/timelogs/delete_spec.rb
+++ b/spec/requests/api/graphql/mutations/timelogs/delete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Delete a timelog' do
+RSpec.describe 'Delete a timelog', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:author) { create(:user) }
let_it_be(:project) { create(:project, :public) }
diff --git a/spec/requests/api/graphql/mutations/todos/create_spec.rb b/spec/requests/api/graphql/mutations/todos/create_spec.rb
index aca00519682..5d7ecb3c927 100644
--- a/spec/requests/api/graphql/mutations/todos/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/todos/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Create a todo' do
+RSpec.describe 'Create a todo', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/todos/mark_all_done_spec.rb b/spec/requests/api/graphql/mutations/todos/mark_all_done_spec.rb
index dc20fde8e3c..c611c6ee2a1 100644
--- a/spec/requests/api/graphql/mutations/todos/mark_all_done_spec.rb
+++ b/spec/requests/api/graphql/mutations/todos/mark_all_done_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Marking all todos done' do
+RSpec.describe 'Marking all todos done', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/mutations/todos/mark_done_spec.rb b/spec/requests/api/graphql/mutations/todos/mark_done_spec.rb
index 7f5ea71c760..60700d8024c 100644
--- a/spec/requests/api/graphql/mutations/todos/mark_done_spec.rb
+++ b/spec/requests/api/graphql/mutations/todos/mark_done_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Marking todos done' do
+RSpec.describe 'Marking todos done', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/mutations/todos/restore_many_spec.rb b/spec/requests/api/graphql/mutations/todos/restore_many_spec.rb
index 4316bd060c1..9daa243cf8e 100644
--- a/spec/requests/api/graphql/mutations/todos/restore_many_spec.rb
+++ b/spec/requests/api/graphql/mutations/todos/restore_many_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Restoring many Todos' do
+RSpec.describe 'Restoring many Todos', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/mutations/todos/restore_spec.rb b/spec/requests/api/graphql/mutations/todos/restore_spec.rb
index d995191c97e..868298763ec 100644
--- a/spec/requests/api/graphql/mutations/todos/restore_spec.rb
+++ b/spec/requests/api/graphql/mutations/todos/restore_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Restoring Todos' do
+RSpec.describe 'Restoring Todos', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/mutations/uploads/delete_spec.rb b/spec/requests/api/graphql/mutations/uploads/delete_spec.rb
index 2d1b33cc086..08dbbe23b6b 100644
--- a/spec/requests/api/graphql/mutations/uploads/delete_spec.rb
+++ b/spec/requests/api/graphql/mutations/uploads/delete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Delete an upload' do
+RSpec.describe 'Delete an upload', feature_category: :navigation do
include GraphqlHelpers
let_it_be(:group) { create(:group) }
diff --git a/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb b/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb
index 28a46583d2a..eb35d310760 100644
--- a/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Create a user callout' do
+RSpec.describe 'Create a user callout', feature_category: :navigation do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb b/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb
index e1c7fd9d60d..31d17401b9e 100644
--- a/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Mutations::UserPreferences::Update do
+RSpec.describe Mutations::UserPreferences::Update, feature_category: :users do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb b/spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb
index c6a980b5cef..97bf060356a 100644
--- a/spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb
+++ b/spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Create a work item from a task in a work item's description" do
+RSpec.describe "Create a work item from a task in a work item's description", feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/mutations/work_items/create_spec.rb b/spec/requests/api/graphql/mutations/work_items/create_spec.rb
index be3917316c3..16f78b67b5c 100644
--- a/spec/requests/api/graphql/mutations/work_items/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/work_items/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Create a work item' do
+RSpec.describe 'Create a work item', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
@@ -123,7 +123,7 @@ RSpec.describe 'Create a work item' do
post_graphql_mutation(mutation, current_user: current_user)
expect(mutation_response['errors'])
- .to contain_exactly(/cannot be added: only Issue and Incident can be parent of Task./)
+ .to contain_exactly(/cannot be added: is not allowed to add this type of parent/)
expect(mutation_response['workItem']).to be_nil
end
end
diff --git a/spec/requests/api/graphql/mutations/work_items/delete_spec.rb b/spec/requests/api/graphql/mutations/work_items/delete_spec.rb
index 0a84225a7ab..e25ff5613e4 100644
--- a/spec/requests/api/graphql/mutations/work_items/delete_spec.rb
+++ b/spec/requests/api/graphql/mutations/work_items/delete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Delete a work item' do
+RSpec.describe 'Delete a work item', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/mutations/work_items/delete_task_spec.rb b/spec/requests/api/graphql/mutations/work_items/delete_task_spec.rb
index c44939c8d54..b1828de046f 100644
--- a/spec/requests/api/graphql/mutations/work_items/delete_task_spec.rb
+++ b/spec/requests/api/graphql/mutations/work_items/delete_task_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Delete a task in a work item's description" do
+RSpec.describe "Delete a task in a work item's description", feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/mutations/work_items/update_spec.rb b/spec/requests/api/graphql/mutations/work_items/update_spec.rb
index 96736457f26..14cb18d04b8 100644
--- a/spec/requests/api/graphql/mutations/work_items/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/work_items/update_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Update a work item' do
+RSpec.describe 'Update a work item', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:group) { create(:group) }
@@ -339,7 +339,7 @@ RSpec.describe 'Update a work item' do
let_it_be(:invalid_parent) { create(:work_item, :task, project: project) }
context 'when parent work item type is invalid' do
- let(:error) { "#{work_item.to_reference} cannot be added: only Issue and Incident can be parent of Task." }
+ let(:error) { "#{work_item.to_reference} cannot be added: is not allowed to add this type of parent" }
let(:input) do
{ 'hierarchyWidget' => { 'parentId' => invalid_parent.to_global_id.to_s }, 'title' => 'new title' }
end
@@ -450,7 +450,7 @@ RSpec.describe 'Update a work item' do
let(:input) { { 'hierarchyWidget' => { 'childrenIds' => children_ids } } }
let(:error) do
- "#{invalid_child.to_reference} cannot be added: only Task can be assigned as a child in hierarchy."
+ "#{invalid_child.to_reference} cannot be added: is not allowed to add this type of parent"
end
context 'when child work item type is invalid' do
@@ -632,7 +632,7 @@ RSpec.describe 'Update a work item' do
end
context 'when unsupported widget input is sent' do
- let_it_be(:test_case) { create(:work_item_type, :default, :test_case, name: 'some_test_case_name') }
+ let_it_be(:test_case) { create(:work_item_type, :default, :test_case) }
let_it_be(:work_item) { create(:work_item, work_item_type: test_case, project: project) }
let(:input) do
@@ -642,7 +642,7 @@ RSpec.describe 'Update a work item' do
end
it_behaves_like 'a mutation that returns top-level errors',
- errors: ["Following widget keys are not supported by some_test_case_name type: [:hierarchy_widget]"]
+ errors: ["Following widget keys are not supported by Test Case type: [:hierarchy_widget]"]
end
end
end
diff --git a/spec/requests/api/graphql/mutations/work_items/update_task_spec.rb b/spec/requests/api/graphql/mutations/work_items/update_task_spec.rb
index 55285be5a5d..999c685ac6a 100644
--- a/spec/requests/api/graphql/mutations/work_items/update_task_spec.rb
+++ b/spec/requests/api/graphql/mutations/work_items/update_task_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Update a work item task' do
+RSpec.describe 'Update a work item task', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/namespace/package_settings_spec.rb b/spec/requests/api/graphql/namespace/package_settings_spec.rb
index 42fd07dbdc7..bd441032626 100644
--- a/spec/requests/api/graphql/namespace/package_settings_spec.rb
+++ b/spec/requests/api/graphql/namespace/package_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting namespace package settings in a namespace' do
+RSpec.describe 'getting namespace package settings in a namespace', feature_category: :package_registry do
include GraphqlHelpers
let_it_be(:package_settings) { create(:namespace_package_setting) }
diff --git a/spec/requests/api/graphql/namespace/projects_spec.rb b/spec/requests/api/graphql/namespace/projects_spec.rb
index d5410f1a7cb..4e12da3e3ab 100644
--- a/spec/requests/api/graphql/namespace/projects_spec.rb
+++ b/spec/requests/api/graphql/namespace/projects_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting projects' do
+RSpec.describe 'getting projects', feature_category: :projects do
include GraphqlHelpers
let(:group) { create(:group) }
diff --git a/spec/requests/api/graphql/namespace/root_storage_statistics_spec.rb b/spec/requests/api/graphql/namespace/root_storage_statistics_spec.rb
index 8d8a0baae36..cee698d6dc5 100644
--- a/spec/requests/api/graphql/namespace/root_storage_statistics_spec.rb
+++ b/spec/requests/api/graphql/namespace/root_storage_statistics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'rendering namespace statistics' do
+RSpec.describe 'rendering namespace statistics', feature_category: :metrics do
include GraphqlHelpers
let(:namespace) { user.namespace }
diff --git a/spec/requests/api/graphql/namespace_query_spec.rb b/spec/requests/api/graphql/namespace_query_spec.rb
index e17469901c6..d12a3875ebf 100644
--- a/spec/requests/api/graphql/namespace_query_spec.rb
+++ b/spec/requests/api/graphql/namespace_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query' do
+RSpec.describe 'Query', feature_category: :subgroups do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/packages/composer_spec.rb b/spec/requests/api/graphql/packages/composer_spec.rb
index 89c01d44771..dd61582b055 100644
--- a/spec/requests/api/graphql/packages/composer_spec.rb
+++ b/spec/requests/api/graphql/packages/composer_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'package details' do
+RSpec.describe 'package details', feature_category: :package_registry do
include GraphqlHelpers
include_context 'package details setup'
diff --git a/spec/requests/api/graphql/packages/conan_spec.rb b/spec/requests/api/graphql/packages/conan_spec.rb
index 7ad85edecef..b8226c482ac 100644
--- a/spec/requests/api/graphql/packages/conan_spec.rb
+++ b/spec/requests/api/graphql/packages/conan_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'conan package details' do
+RSpec.describe 'conan package details', feature_category: :package_registry do
include GraphqlHelpers
include_context 'package details setup'
diff --git a/spec/requests/api/graphql/packages/helm_spec.rb b/spec/requests/api/graphql/packages/helm_spec.rb
index 79a589e2dc2..65b3f80d6df 100644
--- a/spec/requests/api/graphql/packages/helm_spec.rb
+++ b/spec/requests/api/graphql/packages/helm_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'helm package details' do
+RSpec.describe 'helm package details', feature_category: :package_registry do
include GraphqlHelpers
include_context 'package details setup'
diff --git a/spec/requests/api/graphql/packages/maven_spec.rb b/spec/requests/api/graphql/packages/maven_spec.rb
index b7f39efcf73..26c45ada4a1 100644
--- a/spec/requests/api/graphql/packages/maven_spec.rb
+++ b/spec/requests/api/graphql/packages/maven_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'maven package details' do
+RSpec.describe 'maven package details', feature_category: :package_registry do
include GraphqlHelpers
include_context 'package details setup'
diff --git a/spec/requests/api/graphql/packages/nuget_spec.rb b/spec/requests/api/graphql/packages/nuget_spec.rb
index 7de132d1574..1c3af46909e 100644
--- a/spec/requests/api/graphql/packages/nuget_spec.rb
+++ b/spec/requests/api/graphql/packages/nuget_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'nuget package details' do
+RSpec.describe 'nuget package details', feature_category: :package_registry do
include GraphqlHelpers
include_context 'package details setup'
diff --git a/spec/requests/api/graphql/packages/package_spec.rb b/spec/requests/api/graphql/packages/package_spec.rb
index 02a3206f587..42927634119 100644
--- a/spec/requests/api/graphql/packages/package_spec.rb
+++ b/spec/requests/api/graphql/packages/package_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'package details' do
+RSpec.describe 'package details', feature_category: :package_registry do
include GraphqlHelpers
let_it_be_with_reload(:group) { create(:group) }
@@ -226,5 +226,16 @@ RSpec.describe 'package details' do
end
end
end
+
+ context 'with package that has no default status' do
+ before do
+ composer_package.update!(status: :error)
+ subject
+ end
+
+ it "does not return package's details" do
+ expect(package_details).to be_nil
+ end
+ end
end
end
diff --git a/spec/requests/api/graphql/packages/pypi_spec.rb b/spec/requests/api/graphql/packages/pypi_spec.rb
index c0e589f3597..4441f04dabb 100644
--- a/spec/requests/api/graphql/packages/pypi_spec.rb
+++ b/spec/requests/api/graphql/packages/pypi_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'pypi package details' do
+RSpec.describe 'pypi package details', feature_category: :package_registry do
include GraphqlHelpers
include_context 'package details setup'
diff --git a/spec/requests/api/graphql/project/alert_management/alert/assignees_spec.rb b/spec/requests/api/graphql/project/alert_management/alert/assignees_spec.rb
index a59402208ec..c4843c3cf97 100644
--- a/spec/requests/api/graphql/project/alert_management/alert/assignees_spec.rb
+++ b/spec/requests/api/graphql/project/alert_management/alert/assignees_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting Alert Management Alert Assignees' do
+RSpec.describe 'getting Alert Management Alert Assignees', feature_category: :projects do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/project/alert_management/alert/issue_spec.rb b/spec/requests/api/graphql/project/alert_management/alert/issue_spec.rb
index 29896c16f5b..3c9ec4fb60b 100644
--- a/spec/requests/api/graphql/project/alert_management/alert/issue_spec.rb
+++ b/spec/requests/api/graphql/project/alert_management/alert/issue_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting Alert Management Alert Issue' do
+RSpec.describe 'getting Alert Management Alert Issue', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb b/spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb
index 352a94cfc1d..b430fdeb18f 100644
--- a/spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb
+++ b/spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting Alert Management Alert Assignees' do
+RSpec.describe 'getting Alert Management Alert Assignees', feature_category: :projects do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/project/alert_management/alert/notes_spec.rb b/spec/requests/api/graphql/project/alert_management/alert/notes_spec.rb
index 72d185144ef..16dd0dfcfcb 100644
--- a/spec/requests/api/graphql/project/alert_management/alert/notes_spec.rb
+++ b/spec/requests/api/graphql/project/alert_management/alert/notes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting Alert Management Alert Notes' do
+RSpec.describe 'getting Alert Management Alert Notes', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/project/alert_management/alert/todos_spec.rb b/spec/requests/api/graphql/project/alert_management/alert/todos_spec.rb
index ca58079fdfe..ad4361dfa50 100644
--- a/spec/requests/api/graphql/project/alert_management/alert/todos_spec.rb
+++ b/spec/requests/api/graphql/project/alert_management/alert/todos_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting Alert Management Alert Assignees' do
+RSpec.describe 'getting Alert Management Alert Assignees', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb b/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb
index ecd93d169d3..7ce5bf23357 100644
--- a/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb
+++ b/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'getting Alert Management Alert counts by status' do
+RSpec.describe 'getting Alert Management Alert counts by status', feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/requests/api/graphql/project/alert_management/alerts_spec.rb b/spec/requests/api/graphql/project/alert_management/alerts_spec.rb
index fe77d9dc86d..304edfbf4e4 100644
--- a/spec/requests/api/graphql/project/alert_management/alerts_spec.rb
+++ b/spec/requests/api/graphql/project/alert_management/alerts_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'getting Alert Management Alerts' do
+RSpec.describe 'getting Alert Management Alerts', feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:payload) { { 'custom' => { 'alert' => 'payload' }, 'runbook' => 'runbook' } }
@@ -59,6 +59,7 @@ RSpec.describe 'getting Alert Management Alerts' do
it 'returns the correct properties of the alerts' do
expect(first_alert).to include(
+ 'id' => triggered_alert.to_global_id.to_s,
'iid' => triggered_alert.iid.to_s,
'title' => triggered_alert.title,
'description' => triggered_alert.description,
@@ -80,6 +81,7 @@ RSpec.describe 'getting Alert Management Alerts' do
)
expect(second_alert).to include(
+ 'id' => resolved_alert.to_global_id.to_s,
'iid' => resolved_alert.iid.to_s,
'status' => 'RESOLVED',
'endedAt' => resolved_alert.ended_at.strftime('%Y-%m-%dT%H:%M:%SZ')
diff --git a/spec/requests/api/graphql/project/alert_management/integrations_spec.rb b/spec/requests/api/graphql/project/alert_management/integrations_spec.rb
index 773922c1864..e8d19513a4e 100644
--- a/spec/requests/api/graphql/project/alert_management/integrations_spec.rb
+++ b/spec/requests/api/graphql/project/alert_management/integrations_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'getting Alert Management Integrations' do
+RSpec.describe 'getting Alert Management Integrations', feature_category: :integrations do
include ::Gitlab::Routing
include GraphqlHelpers
diff --git a/spec/requests/api/graphql/project/base_service_spec.rb b/spec/requests/api/graphql/project/base_service_spec.rb
index 58d10ade8cf..7b1b95eaf58 100644
--- a/spec/requests/api/graphql/project/base_service_spec.rb
+++ b/spec/requests/api/graphql/project/base_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'query Jira service' do
+RSpec.describe 'query Jira service', feature_category: :authentication_and_authorization do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/project/branch_protections/merge_access_levels_spec.rb b/spec/requests/api/graphql/project/branch_protections/merge_access_levels_spec.rb
index a80f683ea93..d7672cc5116 100644
--- a/spec/requests/api/graphql/project/branch_protections/merge_access_levels_spec.rb
+++ b/spec/requests/api/graphql/project/branch_protections/merge_access_levels_spec.rb
@@ -2,6 +2,6 @@
require 'spec_helper'
-RSpec.describe 'getting merge access levels for a branch protection' do
- include_examples 'perform graphql requests for AccessLevel type objects', :merge
+RSpec.describe 'getting merge access levels for a branch protection', feature_category: :source_code_management do
+ it_behaves_like 'a GraphQL query for access levels', :merge
end
diff --git a/spec/requests/api/graphql/project/branch_protections/push_access_levels_spec.rb b/spec/requests/api/graphql/project/branch_protections/push_access_levels_spec.rb
index cfdaf1096c3..65b9bc93a6b 100644
--- a/spec/requests/api/graphql/project/branch_protections/push_access_levels_spec.rb
+++ b/spec/requests/api/graphql/project/branch_protections/push_access_levels_spec.rb
@@ -2,6 +2,6 @@
require 'spec_helper'
-RSpec.describe 'getting push access levels for a branch protection' do
- include_examples 'perform graphql requests for AccessLevel type objects', :push
+RSpec.describe 'getting push access levels for a branch protection', feature_category: :source_code_management do
+ it_behaves_like 'a GraphQL query for access levels', :push
end
diff --git a/spec/requests/api/graphql/project/branch_rules/branch_protection_spec.rb b/spec/requests/api/graphql/project/branch_rules/branch_protection_spec.rb
index 8a3f546ef95..560819cb989 100644
--- a/spec/requests/api/graphql/project/branch_rules/branch_protection_spec.rb
+++ b/spec/requests/api/graphql/project/branch_rules/branch_protection_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting branch protection for a branch rule' do
+RSpec.describe 'getting branch protection for a branch rule', feature_category: :source_code_management do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/project/branch_rules_spec.rb b/spec/requests/api/graphql/project/branch_rules_spec.rb
index ed866305445..7f6a66e2377 100644
--- a/spec/requests/api/graphql/project/branch_rules_spec.rb
+++ b/spec/requests/api/graphql/project/branch_rules_spec.rb
@@ -2,21 +2,11 @@
require 'spec_helper'
-RSpec.describe 'getting list of branch rules for a project' do
+RSpec.describe 'getting list of branch rules for a project', feature_category: :source_code_management do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository, :public) }
let_it_be(:current_user) { create(:user) }
- let_it_be(:branch_name_a) { TestEnv::BRANCH_SHA.each_key.first }
- let_it_be(:branch_name_b) { 'diff-*' }
- let_it_be(:branch_rules) { [branch_rule_a, branch_rule_b] }
- let_it_be(:branch_rule_a) do
- create(:protected_branch, project: project, name: branch_name_a)
- end
-
- let_it_be(:branch_rule_b) do
- create(:protected_branch, project: project, name: branch_name_b)
- end
let(:branch_rules_data) { graphql_data_at('project', 'branchRules', 'edges') }
let(:variables) { { path: project.full_path } }
@@ -61,39 +51,39 @@ RSpec.describe 'getting list of branch rules for a project' do
end
describe 'queries' do
+ include_context 'when user tracking is disabled'
+
+ let(:query) do
+ <<~GQL
+ query($path: ID!) {
+ project(fullPath: $path) {
+ branchRules {
+ nodes {
+ matchingBranchesCount
+ }
+ }
+ }
+ }
+ GQL
+ end
+
before do
- # rubocop:disable RSpec/AnyInstanceOf
- allow_any_instance_of(User).to receive(:update_tracked_fields!)
- allow_any_instance_of(Users::ActivityService).to receive(:execute)
- # rubocop:enable RSpec/AnyInstanceOf
+ create(:protected_branch, project: project)
allow_next_instance_of(Resolvers::ProjectResolver) do |resolver|
allow(resolver).to receive(:resolve)
.with(full_path: project.full_path)
.and_return(project)
end
allow(project.repository).to receive(:branch_names).and_call_original
- allow(project.repository.gitaly_ref_client).to receive(:branch_names).and_call_original
end
- it 'matching_branches_count avoids N+1 queries' do
- query = <<~GQL
- query($path: ID!) {
- project(fullPath: $path) {
- branchRules {
- nodes {
- matchingBranchesCount
- }
- }
- }
- }
- GQL
-
- control = ActiveRecord::QueryRecorder.new do
+ it 'avoids N+1 queries', :use_sql_query_cache, :aggregate_failures do
+ control = ActiveRecord::QueryRecorder.new(skip_cached: false) do
post_graphql(query, current_user: current_user, variables: variables)
end
# Verify the response includes the field
- expect_n_matching_branches_count_fields(2)
+ expect_n_matching_branches_count_fields(1)
create(:protected_branch, project: project)
create(:protected_branch, name: '*', project: project)
@@ -102,10 +92,8 @@ RSpec.describe 'getting list of branch rules for a project' do
post_graphql(query, current_user: current_user, variables: variables)
end.not_to exceed_all_query_limit(control)
+ expect_n_matching_branches_count_fields(3)
expect(project.repository).to have_received(:branch_names).at_least(2).times
- expect(project.repository.gitaly_ref_client).to have_received(:branch_names).once
-
- expect_n_matching_branches_count_fields(4)
end
def expect_n_matching_branches_count_fields(count)
@@ -118,21 +106,28 @@ RSpec.describe 'getting list of branch rules for a project' do
end
describe 'response' do
+ let_it_be(:branch_name_a) { TestEnv::BRANCH_SHA.each_key.first }
+ let_it_be(:branch_name_b) { 'diff-*' }
+ let_it_be(:branch_rules) { [branch_rule_a, branch_rule_b] }
+ let_it_be(:branch_rule_a) do
+ create(:protected_branch, project: project, name: branch_name_a, id: 9999)
+ end
+
+ let_it_be(:branch_rule_b) do
+ create(:protected_branch, project: project, name: branch_name_b, id: 10000)
+ end
+
+ # branchRules are returned in reverse order, newest first, sorted by primary_key.
+ let(:branch_rule_b_data) { branch_rules_data.dig(0, 'node') }
+ let(:branch_rule_a_data) { branch_rules_data.dig(1, 'node') }
+
before do
post_graphql(query, current_user: current_user, variables: variables)
end
it_behaves_like 'a working graphql query'
- it 'includes all fields', :aggregate_failures do
- # Responses will be sorted alphabetically. Branch names for this spec
- # come from an external constant so we check which is first
- br_a_idx = branch_name_a < branch_name_b ? 0 : 1
- br_b_idx = 1 - br_a_idx
-
- branch_rule_a_data = branch_rules_data.dig(br_a_idx, 'node')
- branch_rule_b_data = branch_rules_data.dig(br_b_idx, 'node')
-
+ it 'includes all fields', :use_sql_query_cache, :aggregate_failures do
expect(branch_rule_a_data['name']).to eq(branch_name_a)
expect(branch_rule_a_data['isDefault']).to be(true).or be(false)
expect(branch_rule_a_data['branchProtection']).to be_present
diff --git a/spec/requests/api/graphql/project/cluster_agents_spec.rb b/spec/requests/api/graphql/project/cluster_agents_spec.rb
index bb716cf2849..0881eb9cdc3 100644
--- a/spec/requests/api/graphql/project/cluster_agents_spec.rb
+++ b/spec/requests/api/graphql/project/cluster_agents_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project.cluster_agents' do
+RSpec.describe 'Project.cluster_agents', feature_category: :kubernetes_management do
include GraphqlHelpers
let_it_be(:project) { create(:project, :public) }
diff --git a/spec/requests/api/graphql/project/container_expiration_policy_spec.rb b/spec/requests/api/graphql/project/container_expiration_policy_spec.rb
index e3ea9e46353..e484e0618aa 100644
--- a/spec/requests/api/graphql/project/container_expiration_policy_spec.rb
+++ b/spec/requests/api/graphql/project/container_expiration_policy_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'getting a repository in a project' do
+RSpec.describe 'getting a repository in a project', feature_category: :container_registry do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/project/container_repositories_spec.rb b/spec/requests/api/graphql/project/container_repositories_spec.rb
index 01b117a89d8..7ccf8a6f5bf 100644
--- a/spec/requests/api/graphql/project/container_repositories_spec.rb
+++ b/spec/requests/api/graphql/project/container_repositories_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'getting container repositories in a project' do
+RSpec.describe 'getting container repositories in a project', feature_category: :container_registry do
using RSpec::Parameterized::TableSyntax
include GraphqlHelpers
diff --git a/spec/requests/api/graphql/project/deployment_spec.rb b/spec/requests/api/graphql/project/deployment_spec.rb
index e5ef7bcafbf..e3ec33ec131 100644
--- a/spec/requests/api/graphql/project/deployment_spec.rb
+++ b/spec/requests/api/graphql/project/deployment_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project Deployment query' do
+RSpec.describe 'Project Deployment query', feature_category: :continuous_delivery do
let_it_be(:project) { create(:project, :private, :repository) }
let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
let_it_be(:guest) { create(:user).tap { |u| project.add_guest(u) } }
diff --git a/spec/requests/api/graphql/project/environments_spec.rb b/spec/requests/api/graphql/project/environments_spec.rb
index e5b6aebbf2c..618f591affa 100644
--- a/spec/requests/api/graphql/project/environments_spec.rb
+++ b/spec/requests/api/graphql/project/environments_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project Environments query' do
+RSpec.describe 'Project Environments query', feature_category: :continuous_delivery do
include GraphqlHelpers
let_it_be(:project) { create(:project, :private, :repository) }
@@ -47,6 +47,60 @@ RSpec.describe 'Project Environments query' do
expect(environment_data['environmentType']).to eq(production.environment_type)
end
+ describe 'user permissions' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{production.name}") {
+ userPermissions {
+ updateEnvironment
+ destroyEnvironment
+ stopEnvironment
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'returns user permissions of the environment', :aggregate_failures do
+ subject
+
+ permission_data = graphql_data.dig('project', 'environment', 'userPermissions')
+ expect(permission_data['updateEnvironment']).to eq(true)
+ expect(permission_data['destroyEnvironment']).to eq(false)
+ expect(permission_data['stopEnvironment']).to eq(true)
+ end
+
+ context 'when fetching user permissions for multiple environments' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environments {
+ nodes {
+ userPermissions {
+ updateEnvironment
+ destroyEnvironment
+ stopEnvironment
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'limits the result', :aggregate_failures do
+ subject
+
+ expect_graphql_errors_to_include('"userPermissions" field can be requested only ' \
+ 'for 1 Environment(s) at a time.')
+ end
+ end
+ end
+
describe 'last deployments of environments' do
::Deployment.statuses.each do |status, _|
let_it_be(:"production_#{status}_deployment") do
@@ -130,4 +184,81 @@ RSpec.describe 'Project Environments query' do
expect(multi).not_to exceed_query_limit(baseline)
end
end
+
+ describe 'nested environments' do
+ let_it_be(:testing1) { create(:environment, name: 'testing/one', project: project) }
+ let_it_be(:testing2) { create(:environment, name: 'testing/two', project: project) }
+
+ context 'with query' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ nestedEnvironments {
+ nodes {
+ name
+ size
+ environment {
+ name
+ path
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'can fetch nested environments' do
+ subject
+
+ nested_envs = graphql_data.dig('project', 'nestedEnvironments', 'nodes')
+ expect(nested_envs.count).to be(3)
+ expect(nested_envs.pluck('name')).to match_array(%w[production staging testing])
+ expect(nested_envs.pluck('size')).to match_array([1, 1, 2])
+ expect(nested_envs[0].dig('environment', 'name')).to eq(production.name)
+ end
+
+ context 'when user is guest' do
+ let(:user) { create(:user).tap { |u| project.add_guest(u) } }
+
+ it 'returns nothing' do
+ subject
+
+ nested_envs = graphql_data.dig('project', 'nestedEnvironments', 'nodes')
+
+ expect(nested_envs).to be_nil
+ end
+ end
+ end
+
+ context 'when using pagination' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ nestedEnvironments(first: 1) {
+ nodes {
+ name
+ }
+ pageInfo {
+ hasPreviousPage
+ startCursor
+ endCursor
+ hasNextPage
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'supports pagination' do
+ subject
+ nested_envs = graphql_data.dig('project', 'nestedEnvironments')
+ expect(nested_envs['nodes'].count).to eq(1)
+ expect(nested_envs.dig('pageInfo', 'hasNextPage')).to be_truthy
+ end
+ end
+ end
end
diff --git a/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb b/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb
index 2fe5fb593fe..e1a8304dce6 100644
--- a/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb
+++ b/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'getting a detailed sentry error' do
+RSpec.describe 'getting a detailed sentry error', feature_category: :error_tracking do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb b/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb
index 3ca0e35882a..2abb1f62ea9 100644
--- a/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb
+++ b/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'sentry errors requests' do
+RSpec.describe 'sentry errors requests', feature_category: :error_tracking do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/requests/api/graphql/project/fork_details_spec.rb b/spec/requests/api/graphql/project/fork_details_spec.rb
new file mode 100644
index 00000000000..efd48b00833
--- /dev/null
+++ b/spec/requests/api/graphql/project/fork_details_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'getting project fork details', feature_category: :source_code_management do
+ include GraphqlHelpers
+ include ProjectForksHelper
+
+ let_it_be(:project) { create(:project, :public, :repository_private, :repository) }
+ let_it_be(:current_user) { create(:user, maintainer_projects: [project]) }
+ let_it_be(:forked_project) { fork_project(project, current_user, repository: true) }
+
+ let(:queried_project) { forked_project }
+
+ let(:query) do
+ graphql_query_for(:project,
+ { full_path: queried_project.full_path }, <<~QUERY
+ forkDetails(ref: "feature"){
+ ahead
+ behind
+ }
+ QUERY
+ )
+ end
+
+ it 'returns fork details' do
+ post_graphql(query, current_user: current_user)
+
+ expect(graphql_data['project']['forkDetails']).to eq(
+ { 'ahead' => 1, 'behind' => 29 }
+ )
+ end
+
+ context 'when a project is not a fork' do
+ let(:queried_project) { project }
+
+ it 'does not return fork details' do
+ post_graphql(query, current_user: current_user)
+
+ expect(graphql_data['project']['forkDetails']).to be_nil
+ end
+ end
+
+ context 'when a user cannot read the code' do
+ let_it_be(:current_user) { create(:user) }
+
+ before do
+ forked_project.update!({
+ repository_access_level: 'private',
+ merge_requests_access_level: 'private'
+ })
+ end
+
+ it 'does not return fork details' do
+ post_graphql(query, current_user: current_user)
+
+ expect(graphql_data['project']['forkDetails']).to be_nil
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/project/fork_targets_spec.rb b/spec/requests/api/graphql/project/fork_targets_spec.rb
index b21a11ff4dc..f2a3901b2c6 100644
--- a/spec/requests/api/graphql/project/fork_targets_spec.rb
+++ b/spec/requests/api/graphql/project/fork_targets_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting a list of fork targets for a project' do
+RSpec.describe 'getting a list of fork targets for a project', feature_category: :source_code_management do
include GraphqlHelpers
let_it_be(:group) { create(:group) }
diff --git a/spec/requests/api/graphql/project/grafana_integration_spec.rb b/spec/requests/api/graphql/project/grafana_integration_spec.rb
index e7534945e7a..1d4f52a8ace 100644
--- a/spec/requests/api/graphql/project/grafana_integration_spec.rb
+++ b/spec/requests/api/graphql/project/grafana_integration_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'Getting Grafana Integration' do
+RSpec.describe 'Getting Grafana Integration', feature_category: :metrics do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb b/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb
index 544d2d7bd95..7587b227d9f 100644
--- a/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb
+++ b/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting incident timeline events' do
+RSpec.describe 'getting incident timeline events', feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb b/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb
index 0444ce43c22..5ccf5c1999a 100644
--- a/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb
+++ b/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha)' do
+RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha)',
+feature_category: :design_management do
include GraphqlHelpers
include DesignManagementTestHelpers
@@ -67,7 +68,7 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha)
query_graphql_field(:design_at_version, dav_params, 'id filename')
end
- shared_examples :finds_dav do
+ shared_examples 'finds dav' do
it 'finds all the designs as of the given version' do
post_query
@@ -88,19 +89,19 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha)
context 'by ID' do
let(:dav_params) { { id: global_id_of(design_at_version) } }
- include_examples :finds_dav
+ include_examples 'finds dav'
end
context 'by filename' do
let(:dav_params) { { filename: design.filename } }
- include_examples :finds_dav
+ include_examples 'finds dav'
end
context 'by design_id' do
let(:dav_params) { { design_id: global_id_of(design) } }
- include_examples :finds_dav
+ include_examples 'finds dav'
end
end
diff --git a/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb b/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb
index 46fd65db1c5..a15e4c1e792 100644
--- a/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb
+++ b/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Getting versions related to an issue' do
+RSpec.describe 'Getting versions related to an issue', feature_category: :design_management do
include GraphqlHelpers
include DesignManagementTestHelpers
diff --git a/spec/requests/api/graphql/project/issue/designs/designs_spec.rb b/spec/requests/api/graphql/project/issue/designs/designs_spec.rb
index 965534654ea..3765899daf2 100644
--- a/spec/requests/api/graphql/project/issue/designs/designs_spec.rb
+++ b/spec/requests/api/graphql/project/issue/designs/designs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Getting designs related to an issue' do
+RSpec.describe 'Getting designs related to an issue', feature_category: :design_management do
include GraphqlHelpers
include DesignManagementTestHelpers
diff --git a/spec/requests/api/graphql/project/issue/designs/notes_spec.rb b/spec/requests/api/graphql/project/issue/designs/notes_spec.rb
index 3b1eb0b4b02..69ca7030292 100644
--- a/spec/requests/api/graphql/project/issue/designs/notes_spec.rb
+++ b/spec/requests/api/graphql/project/issue/designs/notes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Getting designs related to an issue' do
+RSpec.describe 'Getting designs related to an issue', feature_category: :design_management do
include GraphqlHelpers
include DesignManagementTestHelpers
diff --git a/spec/requests/api/graphql/project/issue/notes_spec.rb b/spec/requests/api/graphql/project/issue/notes_spec.rb
index 97f5261ef1d..0c7f042510e 100644
--- a/spec/requests/api/graphql/project/issue/notes_spec.rb
+++ b/spec/requests/api/graphql/project/issue/notes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting notes for an issue' do
+RSpec.describe 'getting notes for an issue', feature_category: :team_planning do
include GraphqlHelpers
let(:noteable) { create(:issue) }
diff --git a/spec/requests/api/graphql/project/issue_spec.rb b/spec/requests/api/graphql/project/issue_spec.rb
index 2415e9ef60f..bc90f9e89e6 100644
--- a/spec/requests/api/graphql/project/issue_spec.rb
+++ b/spec/requests/api/graphql/project/issue_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.project(fullPath).issue(iid)' do
+RSpec.describe 'Query.project(fullPath).issue(iid)', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/project/issues_spec.rb b/spec/requests/api/graphql/project/issues_spec.rb
index 214165cb171..ec5e3c6f0de 100644
--- a/spec/requests/api/graphql/project/issues_spec.rb
+++ b/spec/requests/api/graphql/project/issues_spec.rb
@@ -2,44 +2,92 @@
require 'spec_helper'
-RSpec.describe 'getting an issue list for a project' do
+RSpec.describe 'getting an issue list for a project', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, :public, group: group) }
let_it_be(:current_user) { create(:user) }
let_it_be(:another_user) { create(:user).tap { |u| group.add_reporter(u) } }
- let_it_be(:early_milestone) { create(:milestone, project: project, due_date: 10.days.from_now) }
- let_it_be(:late_milestone) { create(:milestone, project: project, due_date: 30.days.from_now) }
+ let_it_be(:milestone1) { create(:milestone, project: project, due_date: 10.days.from_now) }
+ let_it_be(:milestone2) { create(:milestone, project: project, due_date: 20.days.from_now) }
+ let_it_be(:milestone3) { create(:milestone, project: project, due_date: 30.days.from_now) }
+ let_it_be(:milestone4) { create(:milestone, project: project, due_date: 40.days.from_now) }
let_it_be(:priority1) { create(:label, project: project, priority: 1) }
let_it_be(:priority2) { create(:label, project: project, priority: 5) }
let_it_be(:priority3) { create(:label, project: project, priority: 10) }
- let_it_be(:issue_a, reload: true) { create(:issue, project: project, discussion_locked: true, labels: [priority3]) }
- let_it_be(:issue_b, reload: true) { create(:issue, :with_alert, project: project, title: 'title matching issue i') }
- let_it_be(:issue_c) { create(:issue, project: project, labels: [priority1], milestone: late_milestone) }
- let_it_be(:issue_d) { create(:issue, project: project, labels: [priority2]) }
- let_it_be(:issue_e) { create(:issue, project: project, milestone: early_milestone) }
- let_it_be(:issues, reload: true) { [issue_a, issue_b, issue_c, issue_d, issue_e] }
+ let_it_be(:issue_a) do
+ create(
+ :issue,
+ project: project,
+ discussion_locked: true,
+ labels: [priority3],
+ relative_position: 1000,
+ milestone: milestone4
+ )
+ end
- let(:issue_a_gid) { issue_a.to_global_id.to_s }
- let(:issue_b_gid) { issue_b.to_global_id.to_s }
- let(:issues_data) { graphql_data['project']['issues']['nodes'] }
- let(:issue_filter_params) { {} }
+ let_it_be(:issue_b) do
+ create(
+ :issue,
+ :with_alert,
+ project: project,
+ title: 'title matching issue i',
+ due_date: 3.days.ago,
+ relative_position: 3000,
+ labels: [priority2, priority3],
+ milestone: milestone1
+ )
+ end
- let(:fields) do
- <<~QUERY
- nodes {
- #{all_graphql_fields_for('issues'.classify)}
- }
- QUERY
+ let_it_be(:issue_c) do
+ create(
+ :issue,
+ project: project,
+ labels: [priority1],
+ milestone: milestone2,
+ due_date: 1.day.ago,
+ relative_position: nil
+ )
+ end
+
+ let_it_be(:issue_d) do
+ create(:issue,
+ project: project,
+ labels: [priority2],
+ due_date: 3.days.from_now,
+ relative_position: 5000,
+ milestone: milestone3
+ )
+ end
+
+ let_it_be(:issue_e) do
+ create(
+ :issue,
+ :confidential,
+ project: project,
+ due_date: 1.day.from_now,
+ relative_position: nil
+ )
end
+ let_it_be(:issues, reload: true) { [issue_a, issue_b, issue_c, issue_d, issue_e] }
+
+ let(:issue_nodes_path) { %w[project issues nodes] }
+ let(:issue_filter_params) { {} }
+
# All new specs should be added to the shared example if the change also
# affects the `issues` query at the root level of the API.
# Shared example also used in spec/requests/api/graphql/issues_spec.rb
it_behaves_like 'graphql issue list request spec' do
- subject(:post_query) { post_graphql(query, current_user: current_user) }
+ let_it_be(:external_user) { create(:user) }
+
+ let(:public_projects) { [project] }
+
+ before_all do
+ group.add_developer(current_user)
+ end
# filters
let(:expected_negated_assignee_issues) { [issue_b, issue_c, issue_d, issue_e] }
@@ -50,24 +98,31 @@ RSpec.describe 'getting an issue list for a project' do
let(:unlocked_discussion_issues) { [issue_b, issue_c, issue_d, issue_e] }
let(:search_title_term) { 'matching issue' }
let(:title_search_issue) { issue_b }
+ let(:confidential_issues) { [issue_e] }
+ let(:non_confidential_issues) { [issue_a, issue_b, issue_c, issue_d] }
+ let(:public_non_confidential_issues) { non_confidential_issues }
# sorting
let(:data_path) { [:project, :issues] }
- let(:expected_severity_sorted_asc) { [issue_c, issue_a, issue_b, issue_e, issue_d] }
- let(:expected_priority_sorted_asc) { [issue_e, issue_c, issue_d, issue_a, issue_b] }
- let(:expected_priority_sorted_desc) { [issue_c, issue_e, issue_a, issue_d, issue_b] }
+ let(:expected_priority_sorted_asc) { [issue_b, issue_c, issue_d, issue_a, issue_e] }
+ let(:expected_priority_sorted_desc) { [issue_a, issue_d, issue_c, issue_b, issue_e] }
+ let(:expected_due_date_sorted_desc) { [issue_d, issue_e, issue_c, issue_b, issue_a] }
+ let(:expected_due_date_sorted_asc) { [issue_b, issue_c, issue_e, issue_d, issue_a] }
+ let(:expected_relative_position_sorted_asc) { [issue_a, issue_b, issue_d, issue_c, issue_e] }
+ let(:expected_label_priority_sorted_asc) { [issue_c, issue_d, issue_b, issue_a, issue_e] }
+ let(:expected_label_priority_sorted_desc) { [issue_a, issue_d, issue_b, issue_c, issue_e] }
+ let(:expected_milestone_sorted_asc) { [issue_b, issue_c, issue_d, issue_a, issue_e] }
+ let(:expected_milestone_sorted_desc) { [issue_a, issue_d, issue_c, issue_b, issue_e] }
+
+ # N+1 queries
+ let(:same_project_issue1) { issue_a }
+ let(:same_project_issue2) { issue_b }
before_all do
issue_a.assignee_ids = current_user.id
issue_b.assignee_ids = another_user.id
create(:award_emoji, :upvote, user: current_user, awardable: issue_a)
-
- # severity sorting
- create(:issuable_severity, issue: issue_a, severity: :unknown)
- create(:issuable_severity, issue: issue_b, severity: :low)
- create(:issuable_severity, issue: issue_d, severity: :critical)
- create(:issuable_severity, issue: issue_e, severity: :high)
end
def pagination_query(params)
@@ -77,591 +132,10 @@ RSpec.describe 'getting an issue list for a project' do
query_graphql_field(:issues, params, "#{page_info} nodes { id }")
)
end
- end
-
- context 'when limiting the number of results' do
- let(:query) do
- <<~GQL
- query($path: ID!, $n: Int) {
- project(fullPath: $path) {
- issues(first: $n) { #{fields} }
- }
- }
- GQL
- end
-
- let(:issue_limit) { 1 }
- let(:variables) do
- { path: project.full_path, n: issue_limit }
- end
-
- it_behaves_like 'a working graphql query' do
- before do
- post_graphql(query, current_user: current_user, variables: variables)
- end
-
- it 'only returns N issues' do
- expect(issues_data.size).to eq(issue_limit)
- end
- end
-
- context 'when no limit is provided' do
- let(:issue_limit) { nil }
-
- it 'returns all issues' do
- post_graphql(query, current_user: current_user, variables: variables)
-
- expect(issues_data.size).to be > 1
- end
- end
-
- it 'is expected to check permissions on the first issue only' do
- allow(Ability).to receive(:allowed?).and_call_original
- # Newest first, we only want to see the newest checked
- expect(Ability).not_to receive(:allowed?).with(current_user, :read_issue, issues.first)
-
- post_graphql(query, current_user: current_user, variables: variables)
- end
- end
-
- context 'when the user does not have access to the issue' do
- it 'returns nil' do
- project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
-
- post_graphql(query)
-
- expect(issues_data).to eq([])
- end
- end
-
- context 'when there is a confidential issue' do
- let_it_be(:confidential_issue) do
- create(:issue, :confidential, project: project)
- end
-
- let(:confidential_issue_gid) { confidential_issue.to_global_id.to_s }
-
- context 'when the user cannot see confidential issues' do
- it 'returns issues without confidential issues' do
- post_graphql(query, current_user: current_user)
-
- expect(issues_data.size).to eq(5)
-
- issues_data.each do |issue|
- expect(issue['confidential']).to eq(false)
- end
- end
-
- context 'filtering for confidential issues' do
- let(:issue_filter_params) { { confidential: true } }
-
- it 'returns no issues' do
- post_graphql(query, current_user: current_user)
-
- expect(issues_data.size).to eq(0)
- end
- end
-
- context 'filtering for non-confidential issues' do
- let(:issue_filter_params) { { confidential: false } }
-
- it 'returns correctly filtered issues' do
- post_graphql(query, current_user: current_user)
-
- expect(issue_ids).to match_array(issues.map { |i| i.to_gid.to_s })
- end
- end
- end
-
- context 'when the user can see confidential issues' do
- before do
- project.add_developer(current_user)
- end
-
- it 'returns issues with confidential issues' do
- post_graphql(query, current_user: current_user)
-
- expect(issues_data.size).to eq(6)
-
- confidentials = issues_data.map do |issue|
- issue['confidential']
- end
-
- expect(confidentials).to contain_exactly(true, false, false, false, false, false)
- end
-
- context 'filtering for confidential issues' do
- let(:issue_filter_params) { { confidential: true } }
-
- it 'returns correctly filtered issues' do
- post_graphql(query, current_user: current_user)
-
- expect(issue_ids).to contain_exactly(confidential_issue_gid)
- end
- end
-
- context 'filtering for non-confidential issues' do
- let(:issue_filter_params) { { confidential: false } }
-
- it 'returns correctly filtered issues' do
- post_graphql(query, current_user: current_user)
-
- expect(issue_ids).to match_array([issue_a, issue_b, issue_c, issue_d, issue_e].map { |i| i.to_gid.to_s })
- end
- end
- end
- end
-
- describe 'sorting and pagination' do
- let_it_be(:sort_project) { create(:project, :public) }
- let_it_be(:data_path) { [:project, :issues] }
-
- def pagination_query(params)
- graphql_query_for(
- :project,
- { full_path: sort_project.full_path },
- query_graphql_field(:issues, params, "#{page_info} nodes { iid }")
- )
- end
- def pagination_results_data(data)
- data.map { |issue| issue['iid'].to_i }
+ def post_query(request_user = current_user)
+ post_graphql(query, current_user: request_user)
end
-
- # rubocop:disable RSpec/MultipleMemoizedHelpers
- context 'when sorting by due date' do
- let_it_be(:due_issue1) { create(:issue, project: sort_project, due_date: 3.days.from_now) }
- let_it_be(:due_issue2) { create(:issue, project: sort_project, due_date: nil) }
- let_it_be(:due_issue3) { create(:issue, project: sort_project, due_date: 2.days.ago) }
- let_it_be(:due_issue4) { create(:issue, project: sort_project, due_date: nil) }
- let_it_be(:due_issue5) { create(:issue, project: sort_project, due_date: 1.day.ago) }
-
- context 'when ascending' do
- it_behaves_like 'sorted paginated query' do
- let(:sort_param) { :DUE_DATE_ASC }
- let(:first_param) { 2 }
- let(:all_records) { [due_issue3.iid, due_issue5.iid, due_issue1.iid, due_issue4.iid, due_issue2.iid] }
- end
- end
-
- context 'when descending' do
- it_behaves_like 'sorted paginated query' do
- let(:sort_param) { :DUE_DATE_DESC }
- let(:first_param) { 2 }
- let(:all_records) { [due_issue1.iid, due_issue5.iid, due_issue3.iid, due_issue4.iid, due_issue2.iid] }
- end
- end
- end
-
- context 'when sorting by relative position' do
- let_it_be(:relative_issue1) { create(:issue, project: sort_project, relative_position: 2000) }
- let_it_be(:relative_issue2) { create(:issue, project: sort_project, relative_position: nil) }
- let_it_be(:relative_issue3) { create(:issue, project: sort_project, relative_position: 1000) }
- let_it_be(:relative_issue4) { create(:issue, project: sort_project, relative_position: nil) }
- let_it_be(:relative_issue5) { create(:issue, project: sort_project, relative_position: 500) }
-
- context 'when ascending' do
- it_behaves_like 'sorted paginated query', is_reversible: true do
- let(:sort_param) { :RELATIVE_POSITION_ASC }
- let(:first_param) { 2 }
- let(:all_records) do
- [
- relative_issue5.iid, relative_issue3.iid, relative_issue1.iid,
- relative_issue2.iid, relative_issue4.iid
- ]
- end
- end
- end
- end
-
- context 'when sorting by label priority' do
- let_it_be(:label1) { create(:label, project: sort_project, priority: 1) }
- let_it_be(:label2) { create(:label, project: sort_project, priority: 5) }
- let_it_be(:label3) { create(:label, project: sort_project, priority: 10) }
- let_it_be(:label_issue1) { create(:issue, project: sort_project, labels: [label1]) }
- let_it_be(:label_issue2) { create(:issue, project: sort_project, labels: [label2]) }
- let_it_be(:label_issue3) { create(:issue, project: sort_project, labels: [label1, label3]) }
- let_it_be(:label_issue4) { create(:issue, project: sort_project) }
-
- context 'when ascending' do
- it_behaves_like 'sorted paginated query' do
- let(:sort_param) { :LABEL_PRIORITY_ASC }
- let(:first_param) { 2 }
- let(:all_records) { [label_issue3.iid, label_issue1.iid, label_issue2.iid, label_issue4.iid] }
- end
- end
-
- context 'when descending' do
- it_behaves_like 'sorted paginated query' do
- let(:sort_param) { :LABEL_PRIORITY_DESC }
- let(:first_param) { 2 }
- let(:all_records) { [label_issue2.iid, label_issue3.iid, label_issue1.iid, label_issue4.iid] }
- end
- end
- end
- # rubocop:enable RSpec/MultipleMemoizedHelpers
-
- context 'when sorting by milestone due date' do
- let_it_be(:early_milestone) { create(:milestone, project: sort_project, due_date: 10.days.from_now) }
- let_it_be(:late_milestone) { create(:milestone, project: sort_project, due_date: 30.days.from_now) }
- let_it_be(:milestone_issue1) { create(:issue, project: sort_project) }
- let_it_be(:milestone_issue2) { create(:issue, project: sort_project, milestone: early_milestone) }
- let_it_be(:milestone_issue3) { create(:issue, project: sort_project, milestone: late_milestone) }
-
- context 'when ascending' do
- it_behaves_like 'sorted paginated query' do
- let(:sort_param) { :MILESTONE_DUE_ASC }
- let(:first_param) { 2 }
- let(:all_records) { [milestone_issue2.iid, milestone_issue3.iid, milestone_issue1.iid] }
- end
- end
-
- context 'when descending' do
- it_behaves_like 'sorted paginated query' do
- let(:sort_param) { :MILESTONE_DUE_DESC }
- let(:first_param) { 2 }
- let(:all_records) { [milestone_issue3.iid, milestone_issue2.iid, milestone_issue1.iid] }
- end
- end
- end
- end
-
- context 'when fetching alert management alert' do
- let(:fields) do
- <<~QUERY
- nodes {
- iid
- alertManagementAlert {
- title
- }
- alertManagementAlerts {
- nodes {
- title
- }
- }
- }
- QUERY
- end
-
- # Alerts need to have developer permission and above
- before do
- project.add_developer(current_user)
- end
-
- it 'avoids N+1 queries' do
- control = ActiveRecord::QueryRecorder.new { post_graphql(query, current_user: current_user) }
-
- create(:alert_management_alert, :with_incident, project: project)
-
- expect { post_graphql(query, current_user: current_user) }.not_to exceed_query_limit(control)
- end
-
- it 'returns the alert data' do
- post_graphql(query, current_user: current_user)
-
- alert_titles = issues_data.map { |issue| issue.dig('alertManagementAlert', 'title') }
- expected_titles = issues.map { |issue| issue.alert_management_alert&.title }
-
- expect(alert_titles).to contain_exactly(*expected_titles)
- end
-
- it 'returns the alerts data' do
- post_graphql(query, current_user: current_user)
-
- alert_titles = issues_data.map { |issue| issue.dig('alertManagementAlerts', 'nodes') }
- expected_titles = issues.map do |issue|
- issue.alert_management_alerts.map { |alert| { 'title' => alert.title } }
- end
-
- expect(alert_titles).to contain_exactly(*expected_titles)
- end
- end
-
- context 'when fetching customer_relations_contacts' do
- let(:fields) do
- <<~QUERY
- nodes {
- id
- customerRelationsContacts {
- nodes {
- firstName
- }
- }
- }
- QUERY
- end
-
- def clean_state_query
- run_with_clean_state(query, context: { current_user: current_user })
- end
-
- it 'avoids N+1 queries' do
- create(:issue_customer_relations_contact, :for_issue, issue: issue_a)
-
- control = ActiveRecord::QueryRecorder.new(skip_cached: false) { clean_state_query }
-
- create(:issue_customer_relations_contact, :for_issue, issue: issue_a)
-
- expect { clean_state_query }.not_to exceed_all_query_limit(control)
- end
- end
-
- context 'when fetching labels' do
- let(:fields) do
- <<~QUERY
- nodes {
- id
- labels {
- nodes {
- id
- }
- }
- }
- QUERY
- end
-
- before do
- issues.each do |issue|
- # create a label for each issue we have to properly test N+1
- label = create(:label, project: project)
- issue.update!(labels: [label])
- end
- end
-
- def response_label_ids(response_data)
- response_data.map do |node|
- node['labels']['nodes'].map { |u| u['id'] }
- end.flatten
- end
-
- def labels_as_global_ids(issues)
- issues.map(&:labels).flatten.map(&:to_global_id).map(&:to_s)
- end
-
- it 'avoids N+1 queries', :aggregate_failures do
- control = ActiveRecord::QueryRecorder.new { post_graphql(query, current_user: current_user) }
- expect(issues_data.count).to eq(5)
- expect(response_label_ids(issues_data)).to match_array(labels_as_global_ids(issues))
-
- new_issues = issues + [create(:issue, project: project, labels: [create(:label, project: project)])]
-
- expect { post_graphql(query, current_user: current_user) }.not_to exceed_query_limit(control)
- # graphql_data is memoized (see spec/support/helpers/graphql_helpers.rb)
- # so we have to parse the body ourselves the second time
- issues_data = Gitlab::Json.parse(response.body)['data']['project']['issues']['nodes']
- expect(issues_data.count).to eq(6)
- expect(response_label_ids(issues_data)).to match_array(labels_as_global_ids(new_issues))
- end
- end
-
- context 'when fetching assignees' do
- let(:fields) do
- <<~QUERY
- nodes {
- id
- assignees {
- nodes {
- id
- }
- }
- }
- QUERY
- end
-
- before do
- issues.each do |issue|
- # create an assignee for each issue we have to properly test N+1
- assignee = create(:user)
- issue.update!(assignees: [assignee])
- end
- end
-
- def response_assignee_ids(response_data)
- response_data.map do |node|
- node['assignees']['nodes'].map { |node| node['id'] }
- end.flatten
- end
-
- def assignees_as_global_ids(issues)
- issues.map(&:assignees).flatten.map(&:to_global_id).map(&:to_s)
- end
-
- it 'avoids N+1 queries', :aggregate_failures do
- control = ActiveRecord::QueryRecorder.new { post_graphql(query, current_user: current_user) }
- expect(issues_data.count).to eq(5)
- expect(response_assignee_ids(issues_data)).to match_array(assignees_as_global_ids(issues))
-
- new_issues = issues + [create(:issue, project: project, assignees: [create(:user)])]
-
- expect { post_graphql(query, current_user: current_user) }.not_to exceed_query_limit(control)
- # graphql_data is memoized (see spec/support/helpers/graphql_helpers.rb)
- # so we have to parse the body ourselves the second time
- issues_data = Gitlab::Json.parse(response.body)['data']['project']['issues']['nodes']
- expect(issues_data.count).to eq(6)
- expect(response_assignee_ids(issues_data)).to match_array(assignees_as_global_ids(new_issues))
- end
- end
-
- context 'when fetching escalation status' do
- let_it_be(:escalation_status) { create(:incident_management_issuable_escalation_status, issue: issue_a) }
-
- let(:statuses) { issue_data.to_h { |issue| [issue['iid'], issue['escalationStatus']] } }
- let(:fields) do
- <<~QUERY
- nodes {
- id
- escalationStatus
- }
- QUERY
- end
-
- before do
- issue_a.update!(issue_type: Issue.issue_types[:incident])
- end
-
- it 'returns the escalation status values' do
- post_graphql(query, current_user: current_user)
-
- statuses = issues_data.map { |issue| issue['escalationStatus'] }
-
- expect(statuses).to contain_exactly(escalation_status.status_name.upcase.to_s, nil, nil, nil, nil)
- end
-
- it 'avoids N+1 queries', :aggregate_failures do
- base_count = ActiveRecord::QueryRecorder.new { run_with_clean_state(query, context: { current_user: current_user }) }
-
- new_incident = create(:incident, project: project)
- create(:incident_management_issuable_escalation_status, issue: new_incident)
-
- expect { run_with_clean_state(query, context: { current_user: current_user }) }.not_to exceed_query_limit(base_count)
- end
- end
-
- describe 'N+1 query checks' do
- let(:extra_iid_for_second_query) { issue_b.iid.to_s }
- let(:search_params) { { iids: [issue_a.iid.to_s] } }
-
- def execute_query
- query = graphql_query_for(
- :project,
- { full_path: project.full_path },
- query_graphql_field(
- :issues, search_params,
- query_graphql_field(:nodes, nil, requested_fields)
- )
- )
- post_graphql(query, current_user: current_user)
- end
-
- context 'when requesting `user_notes_count`' do
- let(:requested_fields) { [:user_notes_count] }
-
- before do
- create_list(:note_on_issue, 2, noteable: issue_a, project: project)
- create(:note_on_issue, noteable: issue_b, project: project)
- end
-
- include_examples 'N+1 query check'
- end
-
- context 'when requesting `user_discussions_count`' do
- let(:requested_fields) { [:user_discussions_count] }
-
- before do
- create_list(:note_on_issue, 2, noteable: issue_a, project: project)
- create(:note_on_issue, noteable: issue_b, project: project)
- end
-
- include_examples 'N+1 query check'
- end
-
- context 'when requesting `merge_requests_count`' do
- let(:requested_fields) { [:merge_requests_count] }
-
- before do
- create_list(:merge_requests_closing_issues, 2, issue: issue_a)
- create_list(:merge_requests_closing_issues, 3, issue: issue_b)
- end
-
- include_examples 'N+1 query check'
- end
-
- context 'when requesting `timelogs`' do
- let(:requested_fields) { 'timelogs { nodes { timeSpent } }' }
-
- before do
- create_list(:issue_timelog, 2, issue: issue_a)
- create(:issue_timelog, issue: issue_b)
- end
-
- include_examples 'N+1 query check'
- end
-
- context 'when requesting `closed_as_duplicate_of`' do
- let(:requested_fields) { 'closedAsDuplicateOf { id }' }
- let(:issue_a_dup) { create(:issue, project: project) }
- let(:issue_b_dup) { create(:issue, project: project) }
-
- before do
- issue_a.update!(duplicated_to_id: issue_a_dup)
- issue_b.update!(duplicated_to_id: issue_a_dup)
- end
-
- include_examples 'N+1 query check'
- end
-
- context 'when award emoji votes' do
- let(:requested_fields) { [:upvotes, :downvotes] }
-
- before do
- create_list(:award_emoji, 2, name: 'thumbsup', awardable: issue_a)
- create_list(:award_emoji, 2, name: 'thumbsdown', awardable: issue_b)
- end
-
- include_examples 'N+1 query check'
- end
-
- context 'when requesting participants' do
- let_it_be(:issue_c) { create(:issue, project: project) }
-
- let(:search_params) { { iids: [issue_a.iid.to_s, issue_c.iid.to_s] } }
- let(:requested_fields) { 'participants { nodes { name } }' }
-
- before do
- create(:award_emoji, :upvote, awardable: issue_a)
- create(:award_emoji, :upvote, awardable: issue_b)
- create(:award_emoji, :upvote, awardable: issue_c)
-
- note_with_emoji_a = create(:note_on_issue, noteable: issue_a, project: project)
- note_with_emoji_b = create(:note_on_issue, noteable: issue_b, project: project)
- note_with_emoji_c = create(:note_on_issue, noteable: issue_c, project: project)
-
- create(:award_emoji, :upvote, awardable: note_with_emoji_a)
- create(:award_emoji, :upvote, awardable: note_with_emoji_b)
- create(:award_emoji, :upvote, awardable: note_with_emoji_c)
- end
-
- # Executes 3 extra queries to fetch participant_attrs
- include_examples 'N+1 query check', threshold: 3
- end
-
- context 'when requesting labels' do
- let(:requested_fields) { ['labels { nodes { id } }'] }
-
- before do
- project_labels = create_list(:label, 2, project: project)
- group_labels = create_list(:group_label, 2, group: group)
-
- issue_a.update!(labels: [project_labels.first, group_labels.first].flatten)
- issue_b.update!(labels: [project_labels, group_labels].flatten)
- end
-
- include_examples 'N+1 query check', skip_cached: false
- end
- end
-
- def issue_ids
- graphql_dig_at(issues_data, :id)
end
def query(params = issue_filter_params)
diff --git a/spec/requests/api/graphql/project/jira_import_spec.rb b/spec/requests/api/graphql/project/jira_import_spec.rb
index 202220f4bf6..821357b6988 100644
--- a/spec/requests/api/graphql/project/jira_import_spec.rb
+++ b/spec/requests/api/graphql/project/jira_import_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'query Jira import data' do
+RSpec.describe 'query Jira import data', feature_category: :integrations do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/project/jira_projects_spec.rb b/spec/requests/api/graphql/project/jira_projects_spec.rb
index 410d5b21505..3cd689deda5 100644
--- a/spec/requests/api/graphql/project/jira_projects_spec.rb
+++ b/spec/requests/api/graphql/project/jira_projects_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'query Jira projects' do
+RSpec.describe 'query Jira projects', feature_category: :integrations do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/project/jira_service_spec.rb b/spec/requests/api/graphql/project/jira_service_spec.rb
index d6abe94b873..23f32d2c2d2 100644
--- a/spec/requests/api/graphql/project/jira_service_spec.rb
+++ b/spec/requests/api/graphql/project/jira_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'query Jira service' do
+RSpec.describe 'query Jira service', feature_category: :integrations do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/project/job_spec.rb b/spec/requests/api/graphql/project/job_spec.rb
index 6edd4cf753f..ba1c8a1f616 100644
--- a/spec/requests/api/graphql/project/job_spec.rb
+++ b/spec/requests/api/graphql/project/job_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.project.job' do
+RSpec.describe 'Query.project.job', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/project/jobs_spec.rb b/spec/requests/api/graphql/project/jobs_spec.rb
index 7d0eb203d60..d05d4a2f4b6 100644
--- a/spec/requests/api/graphql/project/jobs_spec.rb
+++ b/spec/requests/api/graphql/project/jobs_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'Query.project.jobs' do
+RSpec.describe 'Query.project.jobs', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository, :public) }
diff --git a/spec/requests/api/graphql/project/labels_query_spec.rb b/spec/requests/api/graphql/project/labels_query_spec.rb
index eeaaaaee575..1930a22ad30 100644
--- a/spec/requests/api/graphql/project/labels_query_spec.rb
+++ b/spec/requests/api/graphql/project/labels_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting project label information' do
+RSpec.describe 'getting project label information', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project, :public) }
diff --git a/spec/requests/api/graphql/project/languages_spec.rb b/spec/requests/api/graphql/project/languages_spec.rb
index 6ef500cde41..88a196c3ff4 100644
--- a/spec/requests/api/graphql/project/languages_spec.rb
+++ b/spec/requests/api/graphql/project/languages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project.languages' do
+RSpec.describe 'Project.languages', feature_category: :internationalization do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb b/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb
index b1ecb32b365..36e148468bc 100644
--- a/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb
+++ b/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting notes for a merge request' do
+RSpec.describe 'getting notes for a merge request', feature_category: :code_review do
include GraphqlHelpers
let_it_be(:noteable) { create(:merge_request) }
diff --git a/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb b/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb
index 4dc272b5c2e..fb7e46cff8e 100644
--- a/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb
+++ b/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.project.mergeRequests.pipelines' do
+RSpec.describe 'Query.project.mergeRequests.pipelines', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:project) { create(:project, :public, :repository) }
diff --git a/spec/requests/api/graphql/project/merge_request_spec.rb b/spec/requests/api/graphql/project/merge_request_spec.rb
index 6a59df81405..b7aafdf305a 100644
--- a/spec/requests/api/graphql/project/merge_request_spec.rb
+++ b/spec/requests/api/graphql/project/merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting merge request information nested in a project' do
+RSpec.describe 'getting merge request information nested in a project', feature_category: :code_review do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository, :public) }
diff --git a/spec/requests/api/graphql/project/merge_requests_spec.rb b/spec/requests/api/graphql/project/merge_requests_spec.rb
index 2895737ae6f..b3b4c8fe0d5 100644
--- a/spec/requests/api/graphql/project/merge_requests_spec.rb
+++ b/spec/requests/api/graphql/project/merge_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting merge request listings nested in a project' do
+RSpec.describe 'getting merge request listings nested in a project', feature_category: :code_review do
include GraphqlHelpers
let_it_be(:group) { create(:group) }
diff --git a/spec/requests/api/graphql/project/milestones_spec.rb b/spec/requests/api/graphql/project/milestones_spec.rb
index a577c367fe5..3b31da77a75 100644
--- a/spec/requests/api/graphql/project/milestones_spec.rb
+++ b/spec/requests/api/graphql/project/milestones_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting milestone listings nested in a project' do
+RSpec.describe 'getting milestone listings nested in a project', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:today) { Time.now.utc.to_date }
diff --git a/spec/requests/api/graphql/project/packages_cleanup_policy_spec.rb b/spec/requests/api/graphql/project/packages_cleanup_policy_spec.rb
index 33e1dbcba27..a7d5cc79f1a 100644
--- a/spec/requests/api/graphql/project/packages_cleanup_policy_spec.rb
+++ b/spec/requests/api/graphql/project/packages_cleanup_policy_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'getting the packages cleanup policy linked to a project' do
+RSpec.describe 'getting the packages cleanup policy linked to a project', feature_category: :package_registry do
using RSpec::Parameterized::TableSyntax
include GraphqlHelpers
diff --git a/spec/requests/api/graphql/project/packages_spec.rb b/spec/requests/api/graphql/project/packages_spec.rb
index d9ee997eb02..3413b80e8b4 100644
--- a/spec/requests/api/graphql/project/packages_spec.rb
+++ b/spec/requests/api/graphql/project/packages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting a package list for a project' do
+RSpec.describe 'getting a package list for a project', feature_category: :package_registry do
include GraphqlHelpers
let_it_be(:resource) { create(:project, :repository) }
diff --git a/spec/requests/api/graphql/project/pipeline_spec.rb b/spec/requests/api/graphql/project/pipeline_spec.rb
index 41915d3cdee..0eeb382510e 100644
--- a/spec/requests/api/graphql/project/pipeline_spec.rb
+++ b/spec/requests/api/graphql/project/pipeline_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting pipeline information nested in a project' do
+RSpec.describe 'getting pipeline information nested in a project', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository, :public) }
diff --git a/spec/requests/api/graphql/project/project_members_spec.rb b/spec/requests/api/graphql/project/project_members_spec.rb
index 97a79ab3b0e..1f1d8027592 100644
--- a/spec/requests/api/graphql/project/project_members_spec.rb
+++ b/spec/requests/api/graphql/project/project_members_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting project members information' do
+RSpec.describe 'getting project members information', feature_category: :projects do
include GraphqlHelpers
let_it_be(:parent_group) { create(:group, :public) }
diff --git a/spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb b/spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb
index 39a68d98d84..a13e96eb9d3 100644
--- a/spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb
+++ b/spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'rendering project pipeline statistics' do
+RSpec.describe 'rendering project pipeline statistics', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/project/project_statistics_spec.rb b/spec/requests/api/graphql/project/project_statistics_spec.rb
index b57c594c64f..d078659b954 100644
--- a/spec/requests/api/graphql/project/project_statistics_spec.rb
+++ b/spec/requests/api/graphql/project/project_statistics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'rendering project statistics' do
+RSpec.describe 'rendering project statistics', feature_category: :project_statistics do
include GraphqlHelpers
let(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/project/recent_issue_boards_query_spec.rb b/spec/requests/api/graphql/project/recent_issue_boards_query_spec.rb
index b3daf86c4af..ae459fd26fb 100644
--- a/spec/requests/api/graphql/project/recent_issue_boards_query_spec.rb
+++ b/spec/requests/api/graphql/project/recent_issue_boards_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting project recent issue boards' do
+RSpec.describe 'getting project recent issue boards', feature_category: :team_planning do
include GraphqlHelpers
it_behaves_like 'querying a GraphQL type recent boards' do
diff --git a/spec/requests/api/graphql/project/release_spec.rb b/spec/requests/api/graphql/project/release_spec.rb
index c4899dbb71e..477388585ca 100644
--- a/spec/requests/api/graphql/project/release_spec.rb
+++ b/spec/requests/api/graphql/project/release_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.project(fullPath).release(tagName)' do
+RSpec.describe 'Query.project(fullPath).release(tagName)', feature_category: :release_orchestration do
include GraphqlHelpers
include Presentable
diff --git a/spec/requests/api/graphql/project/releases_spec.rb b/spec/requests/api/graphql/project/releases_spec.rb
index c28a6fa7666..aa454349fcf 100644
--- a/spec/requests/api/graphql/project/releases_spec.rb
+++ b/spec/requests/api/graphql/project/releases_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.project(fullPath).releases()' do
+RSpec.describe 'Query.project(fullPath).releases()', feature_category: :release_orchestration do
include GraphqlHelpers
let_it_be(:stranger) { create(:user) }
diff --git a/spec/requests/api/graphql/project/repository/blobs_spec.rb b/spec/requests/api/graphql/project/repository/blobs_spec.rb
index ba87f1100f2..a4ee0910d30 100644
--- a/spec/requests/api/graphql/project/repository/blobs_spec.rb
+++ b/spec/requests/api/graphql/project/repository/blobs_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'getting blobs in a project repository' do
+RSpec.describe 'getting blobs in a project repository', feature_category: :source_code_management do
include GraphqlHelpers
let(:project) { create(:project, :repository) }
diff --git a/spec/requests/api/graphql/project/repository_spec.rb b/spec/requests/api/graphql/project/repository_spec.rb
index b00f64c3db6..9f4d69c7b17 100644
--- a/spec/requests/api/graphql/project/repository_spec.rb
+++ b/spec/requests/api/graphql/project/repository_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'getting a repository in a project' do
+RSpec.describe 'getting a repository in a project', feature_category: :source_code_management do
include GraphqlHelpers
let(:project) { create(:project, :repository) }
diff --git a/spec/requests/api/graphql/project/runners_spec.rb b/spec/requests/api/graphql/project/runners_spec.rb
new file mode 100644
index 00000000000..7304de7bec6
--- /dev/null
+++ b/spec/requests/api/graphql/project/runners_spec.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Project.runners', feature_category: :runner do
+ include GraphqlHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group, :public) }
+ let_it_be(:project) { create(:project, :public, group: group) }
+ let_it_be(:instance_runner) { create(:ci_runner, :instance) }
+ let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project]) }
+ let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group]) }
+ let_it_be(:other_project) { create(:project, :repository, :public) }
+ let_it_be(:other_project_runner) { create(:ci_runner, :project, projects: [other_project]) }
+
+ let_it_be(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ runners {
+ nodes {
+ id
+ }
+ }
+ }
+ }
+ )
+ end
+
+ context 'when the user is a project admin' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ let(:expected_ids) { [project_runner, group_runner, instance_runner].map { |g| g.to_global_id.to_s } }
+
+ it 'returns all runners available to project' do
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data_at(:project, :runners, :nodes).pluck('id')).to match_array(expected_ids)
+ end
+ end
+
+ context 'when the user is a project developer' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'returns no runners' do
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data_at(:project, :runners, :nodes)).to be_empty
+ end
+ end
+
+ context 'when on_demand_scans_runner_tags feature flag is disabled' do
+ before do
+ stub_feature_flags(on_demand_scans_runner_tags: false)
+ end
+
+ it 'returns no runners' do
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data_at(:project, :runners, :nodes)).to be_empty
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/project/terraform/state_spec.rb b/spec/requests/api/graphql/project/terraform/state_spec.rb
index 5e207ec0963..1889e7a1064 100644
--- a/spec/requests/api/graphql/project/terraform/state_spec.rb
+++ b/spec/requests/api/graphql/project/terraform/state_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'query a single terraform state' do
+RSpec.describe 'query a single terraform state', feature_category: :infrastructure_as_code do
include GraphqlHelpers
include ::API::Helpers::RelatedResourcesHelpers
diff --git a/spec/requests/api/graphql/project/terraform/states_spec.rb b/spec/requests/api/graphql/project/terraform/states_spec.rb
index cc3660bcc6b..25fc07ef509 100644
--- a/spec/requests/api/graphql/project/terraform/states_spec.rb
+++ b/spec/requests/api/graphql/project/terraform/states_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'query terraform states' do
+RSpec.describe 'query terraform states', feature_category: :infrastructure_as_code do
include GraphqlHelpers
include ::API::Helpers::RelatedResourcesHelpers
diff --git a/spec/requests/api/graphql/project/tree/tree_spec.rb b/spec/requests/api/graphql/project/tree/tree_spec.rb
index e63e0d3dd04..77b72bf39a1 100644
--- a/spec/requests/api/graphql/project/tree/tree_spec.rb
+++ b/spec/requests/api/graphql/project/tree/tree_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'getting a tree in a project' do
+RSpec.describe 'getting a tree in a project', feature_category: :source_code_management do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
@@ -166,6 +166,54 @@ RSpec.describe 'getting a tree in a project' do
end
end
+ context 'when the ref points to a SSH-signed commit' do
+ let_it_be(:ref) { 'ssh-signed-commit' }
+ let_it_be(:commit) { project.commit(ref) }
+ let_it_be(:current_user) { create(:user, email: commit.committer_email).tap { |user| project.add_owner(user) } }
+
+ let(:fields) do
+ <<~QUERY
+ tree(path:"#{path}", ref:"#{ref}") {
+ lastCommit {
+ signature {
+ ... on SshSignature {
+ #{all_graphql_fields_for('SshSignature'.classify, max_depth: 2)}
+ }
+ }
+ }
+ }
+ QUERY
+ end
+
+ let_it_be(:key) do
+ create(:key, user: current_user, key: extract_public_key_from_commit(commit), expires_at: 2.days.from_now)
+ end
+
+ def extract_public_key_from_commit(commit)
+ ssh_commit = Gitlab::Ssh::Commit.new(commit)
+ signature_data = ::SSHData::Signature.parse_pem(ssh_commit.signature_text)
+ signature_data.public_key.openssh
+ end
+
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ it 'returns the expected signature data' do
+ signature = graphql_data['project']['repository']['tree']['lastCommit']['signature']
+
+ expect(signature['commitSha']).to eq(commit.id)
+ expect(signature['verificationStatus']).to eq('VERIFIED')
+ expect(signature['project']['id']).to eq("gid://gitlab/Project/#{project.id}")
+ expect(signature['user']['id']).to eq("gid://gitlab/User/#{current_user.id}")
+ expect(signature['key']['id']).to eq("gid://gitlab/Key/#{key.id}")
+ expect(signature['key']['title']).to eq(key.title)
+ expect(signature['key']['createdAt']).to be_present
+ expect(signature['key']['expiresAt']).to be_present
+ expect(signature['key']['key']).to match(key.key)
+ end
+ end
+
context 'when current user is nil' do
it 'returns empty project' do
post_graphql(query, current_user: nil)
diff --git a/spec/requests/api/graphql/project/work_item_types_spec.rb b/spec/requests/api/graphql/project/work_item_types_spec.rb
index 3d30baab816..c31a260c4b8 100644
--- a/spec/requests/api/graphql/project/work_item_types_spec.rb
+++ b/spec/requests/api/graphql/project/work_item_types_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting a list of work item types for a project' do
+RSpec.describe 'getting a list of work item types for a project', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:developer) { create(:user) }
diff --git a/spec/requests/api/graphql/project/work_items_spec.rb b/spec/requests/api/graphql/project/work_items_spec.rb
index 6d20799c9ec..a59da706a8a 100644
--- a/spec/requests/api/graphql/project/work_items_spec.rb
+++ b/spec/requests/api/graphql/project/work_items_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting a work item list for a project' do
+RSpec.describe 'getting a work item list for a project', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:group) { create(:group) }
@@ -93,6 +93,11 @@ RSpec.describe 'getting a work item list for a project' do
}
... on WorkItemWidgetHierarchy {
parent { id }
+ children {
+ nodes {
+ id
+ }
+ }
}
... on WorkItemWidgetLabels {
labels { nodes { id } }
@@ -112,6 +117,57 @@ RSpec.describe 'getting a work item list for a project' do
end
end
+ context 'when querying WorkItemWidgetHierarchy' do
+ let_it_be(:children) { create_list(:work_item, 3, :task, project: project) }
+ let_it_be(:child_link1) { create(:parent_link, work_item_parent: item1, work_item: children[0]) }
+
+ let(:fields) do
+ <<~GRAPHQL
+ nodes {
+ widgets {
+ type
+ ... on WorkItemWidgetHierarchy {
+ hasChildren
+ parent { id }
+ children { nodes { id } }
+ }
+ }
+ }
+ GRAPHQL
+ end
+
+ it 'executes limited number of N+1 queries' do
+ post_graphql(query, current_user: current_user) # warm-up
+
+ control = ActiveRecord::QueryRecorder.new do
+ post_graphql(query, current_user: current_user)
+ end
+
+ parent_work_items = create_list(:work_item, 2, project: project)
+ create(:parent_link, work_item_parent: parent_work_items[0], work_item: children[1])
+ create(:parent_link, work_item_parent: parent_work_items[1], work_item: children[2])
+
+ # There are 2 extra queries for fetching the children field
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/363569
+ expect { post_graphql(query, current_user: current_user) }
+ .not_to exceed_query_limit(control).with_threshold(2)
+ end
+
+ it 'avoids N+1 queries when children are added to a work item' do
+ post_graphql(query, current_user: current_user) # warm-up
+
+ control = ActiveRecord::QueryRecorder.new do
+ post_graphql(query, current_user: current_user)
+ end
+
+ create(:parent_link, work_item_parent: item1, work_item: children[1])
+ create(:parent_link, work_item_parent: item1, work_item: children[2])
+
+ expect { post_graphql(query, current_user: current_user) }
+ .not_to exceed_query_limit(control)
+ end
+ end
+
it_behaves_like 'a working graphql query' do
before do
post_graphql(query, current_user: current_user)
@@ -188,6 +244,60 @@ RSpec.describe 'getting a work item list for a project' do
end
end
+ describe 'fetching work item notes widget' do
+ let(:item_filter_params) { { iid: item2.iid.to_s } }
+ let(:fields) do
+ <<~GRAPHQL
+ edges {
+ node {
+ widgets {
+ type
+ ... on WorkItemWidgetNotes {
+ system: discussions(filter: ONLY_ACTIVITY, first: 10) { nodes { id notes { nodes { id system internal body } } } },
+ comments: discussions(filter: ONLY_COMMENTS, first: 10) { nodes { id notes { nodes { id system internal body } } } },
+ all_notes: discussions(filter: ALL_NOTES, first: 10) { nodes { id notes { nodes { id system internal body } } } }
+ }
+ }
+ }
+ }
+ GRAPHQL
+ end
+
+ before do
+ create_notes(item1, "some note1")
+ create_notes(item2, "some note2")
+ end
+
+ shared_examples 'fetches work item notes' do |user_comments_count:, system_notes_count:|
+ it "fetches notes" do
+ post_graphql(query, current_user: current_user)
+
+ all_widgets = graphql_dig_at(items_data, :node, :widgets)
+ notes_widget = all_widgets.find { |x| x["type"] == "NOTES" }
+
+ all_notes = graphql_dig_at(notes_widget["all_notes"], :nodes)
+ system_notes = graphql_dig_at(notes_widget["system"], :nodes)
+ comments = graphql_dig_at(notes_widget["comments"], :nodes)
+
+ expect(comments.count).to eq(user_comments_count)
+ expect(system_notes.count).to eq(system_notes_count)
+ expect(all_notes.count).to eq(user_comments_count + system_notes_count)
+ end
+ end
+
+ context 'when user has permission to view internal notes' do
+ before do
+ project.add_developer(current_user)
+ end
+
+ it_behaves_like 'fetches work item notes', user_comments_count: 2, system_notes_count: 5
+ end
+
+ context 'when user cannot view internal notes' do
+ it_behaves_like 'fetches work item notes', user_comments_count: 1, system_notes_count: 5
+ end
+ end
+
def item_ids
graphql_dig_at(items_data, :node, :id)
end
@@ -199,4 +309,26 @@ RSpec.describe 'getting a work item list for a project' do
query_graphql_field('workItems', params, fields)
)
end
+
+ def create_notes(work_item, note_body)
+ create(:note, system: true, project: work_item.project, noteable: work_item)
+
+ disc_start = create(:discussion_note_on_issue, noteable: work_item, project: work_item.project, note: note_body)
+ create(:note,
+ discussion_id: disc_start.discussion_id, noteable: work_item,
+ project: work_item.project, note: "reply on #{note_body}")
+
+ create(:resource_label_event, user: current_user, issue: work_item, label: label1, action: 'add')
+ create(:resource_label_event, user: current_user, issue: work_item, label: label1, action: 'remove')
+
+ create(:resource_milestone_event, issue: work_item, milestone: milestone1, action: 'add')
+ create(:resource_milestone_event, issue: work_item, milestone: milestone1, action: 'remove')
+
+ # confidential notes are currently available only on issues and epics
+ conf_disc_start = create(:discussion_note_on_issue, :confidential,
+ noteable: work_item, project: work_item.project, note: "confidential #{note_body}")
+ create(:note, :confidential,
+ discussion_id: conf_disc_start.discussion_id, noteable: work_item,
+ project: work_item.project, note: "reply on confidential #{note_body}")
+ end
end
diff --git a/spec/requests/api/graphql/project_query_spec.rb b/spec/requests/api/graphql/project_query_spec.rb
index d1b990629a1..281a08e6548 100644
--- a/spec/requests/api/graphql/project_query_spec.rb
+++ b/spec/requests/api/graphql/project_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting project information' do
+RSpec.describe 'getting project information', feature_category: :projects do
include GraphqlHelpers
let_it_be(:group) { create(:group) }
diff --git a/spec/requests/api/graphql/query_spec.rb b/spec/requests/api/graphql/query_spec.rb
index 359c599cd3a..2b9d66ec744 100644
--- a/spec/requests/api/graphql/query_spec.rb
+++ b/spec/requests/api/graphql/query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query' do
+RSpec.describe 'Query', feature_category: :not_owned do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/read_only_spec.rb b/spec/requests/api/graphql/read_only_spec.rb
index d2a45603886..aec8d3151c8 100644
--- a/spec/requests/api/graphql/read_only_spec.rb
+++ b/spec/requests/api/graphql/read_only_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Requests on a read-only node' do
+RSpec.describe 'Requests on a read-only node', feature_category: :database do
context 'when db is read-only' do
before do
allow(Gitlab::Database).to receive(:read_only?) { true }
diff --git a/spec/requests/api/graphql/snippets_spec.rb b/spec/requests/api/graphql/snippets_spec.rb
index 9edd805678a..3cd95b0841c 100644
--- a/spec/requests/api/graphql/snippets_spec.rb
+++ b/spec/requests/api/graphql/snippets_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'snippets' do
+RSpec.describe 'snippets', feature_category: :source_code_management do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/tasks/task_completion_status_spec.rb b/spec/requests/api/graphql/tasks/task_completion_status_spec.rb
index 5f4d2aec718..ea89487c176 100644
--- a/spec/requests/api/graphql/tasks/task_completion_status_spec.rb
+++ b/spec/requests/api/graphql/tasks/task_completion_status_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting task completion status information' do
+RSpec.describe 'getting task completion status information', feature_category: :team_planning do
include GraphqlHelpers
description_0_done = '- [ ] task 1\n- [ ] task 2'
diff --git a/spec/requests/api/graphql/terraform/state/delete_spec.rb b/spec/requests/api/graphql/terraform/state/delete_spec.rb
index ba0619ea611..f4af402492c 100644
--- a/spec/requests/api/graphql/terraform/state/delete_spec.rb
+++ b/spec/requests/api/graphql/terraform/state/delete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'delete a terraform state' do
+RSpec.describe 'delete a terraform state', feature_category: :infrastructure_as_code do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/terraform/state/lock_spec.rb b/spec/requests/api/graphql/terraform/state/lock_spec.rb
index e4d3b6336ab..4219f4f4651 100644
--- a/spec/requests/api/graphql/terraform/state/lock_spec.rb
+++ b/spec/requests/api/graphql/terraform/state/lock_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'lock a terraform state' do
+RSpec.describe 'lock a terraform state', feature_category: :infrastructure_as_code do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/terraform/state/unlock_spec.rb b/spec/requests/api/graphql/terraform/state/unlock_spec.rb
index e90730f2d8f..84ccc09711d 100644
--- a/spec/requests/api/graphql/terraform/state/unlock_spec.rb
+++ b/spec/requests/api/graphql/terraform/state/unlock_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'unlock a terraform state' do
+RSpec.describe 'unlock a terraform state', feature_category: :infrastructure_as_code do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/todo_query_spec.rb b/spec/requests/api/graphql/todo_query_spec.rb
index 7fe19448083..b8ce065dbbb 100644
--- a/spec/requests/api/graphql/todo_query_spec.rb
+++ b/spec/requests/api/graphql/todo_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Todo Query' do
+RSpec.describe 'Todo Query', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:current_user) { nil }
diff --git a/spec/requests/api/graphql/usage_trends_measurements_spec.rb b/spec/requests/api/graphql/usage_trends_measurements_spec.rb
index 78a4321f522..68b97fbb130 100644
--- a/spec/requests/api/graphql/usage_trends_measurements_spec.rb
+++ b/spec/requests/api/graphql/usage_trends_measurements_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'UsageTrendsMeasurements' do
+RSpec.describe 'UsageTrendsMeasurements', feature_category: :devops_reports do
include GraphqlHelpers
let(:current_user) { create(:user, :admin) }
diff --git a/spec/requests/api/graphql/user/group_member_query_spec.rb b/spec/requests/api/graphql/user/group_member_query_spec.rb
index e47cef8cc37..d09cb319877 100644
--- a/spec/requests/api/graphql/user/group_member_query_spec.rb
+++ b/spec/requests/api/graphql/user/group_member_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'GroupMember' do
+RSpec.describe 'GroupMember', feature_category: :subgroups do
include GraphqlHelpers
let_it_be(:member) { create(:group_member, :developer) }
diff --git a/spec/requests/api/graphql/user/project_member_query_spec.rb b/spec/requests/api/graphql/user/project_member_query_spec.rb
index 01827e94d5d..1baa7815793 100644
--- a/spec/requests/api/graphql/user/project_member_query_spec.rb
+++ b/spec/requests/api/graphql/user/project_member_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'ProjectMember' do
+RSpec.describe 'ProjectMember', feature_category: :subgroups do
include GraphqlHelpers
let_it_be(:member) { create(:project_member, :developer) }
diff --git a/spec/requests/api/graphql/user/starred_projects_query_spec.rb b/spec/requests/api/graphql/user/starred_projects_query_spec.rb
index 75a17ed34c4..7d4284300d8 100644
--- a/spec/requests/api/graphql/user/starred_projects_query_spec.rb
+++ b/spec/requests/api/graphql/user/starred_projects_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Getting starredProjects of the user' do
+RSpec.describe 'Getting starredProjects of the user', feature_category: :projects do
include GraphqlHelpers
let(:query) do
diff --git a/spec/requests/api/graphql/user_query_spec.rb b/spec/requests/api/graphql/user_query_spec.rb
index 8f286180617..ca319ed1b2e 100644
--- a/spec/requests/api/graphql/user_query_spec.rb
+++ b/spec/requests/api/graphql/user_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting user information' do
+RSpec.describe 'getting user information', feature_category: :user_management do
include GraphqlHelpers
let(:query) do
diff --git a/spec/requests/api/graphql/user_spec.rb b/spec/requests/api/graphql/user_spec.rb
index a3b2b750bc3..2e1e4971767 100644
--- a/spec/requests/api/graphql/user_spec.rb
+++ b/spec/requests/api/graphql/user_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User' do
+RSpec.describe 'User', feature_category: :users do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/users_spec.rb b/spec/requests/api/graphql/users_spec.rb
index 79ee3c2cb57..83c360fdaf8 100644
--- a/spec/requests/api/graphql/users_spec.rb
+++ b/spec/requests/api/graphql/users_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Users' do
+RSpec.describe 'Users', feature_category: :user_management do
include GraphqlHelpers
let_it_be(:user0) { create(:user, created_at: 1.day.ago) }
diff --git a/spec/requests/api/graphql/work_item_spec.rb b/spec/requests/api/graphql/work_item_spec.rb
index a55de6adfb2..df7dbaea420 100644
--- a/spec/requests/api/graphql/work_item_spec.rb
+++ b/spec/requests/api/graphql/work_item_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.work_item(id)' do
+RSpec.describe 'Query.work_item(id)', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:developer) { create(:user) }
@@ -116,6 +116,7 @@ RSpec.describe 'Query.work_item(id)' do
id
}
}
+ hasChildren
}
}
GRAPHQL
@@ -132,7 +133,8 @@ RSpec.describe 'Query.work_item(id)' do
[
hash_including('id' => child_link1.work_item.to_gid.to_s),
hash_including('id' => child_link2.work_item.to_gid.to_s)
- ]) }
+ ]) },
+ 'hasChildren' => true
)
)
)
@@ -165,7 +167,8 @@ RSpec.describe 'Query.work_item(id)' do
'children' => { 'nodes' => match_array(
[
hash_including('id' => child_link1.work_item.to_gid.to_s)
- ]) }
+ ]) },
+ 'hasChildren' => true
)
)
)
@@ -183,7 +186,8 @@ RSpec.describe 'Query.work_item(id)' do
hash_including(
'type' => 'HIERARCHY',
'parent' => hash_including('id' => parent_link.work_item_parent.to_gid.to_s),
- 'children' => { 'nodes' => match_array([]) }
+ 'children' => { 'nodes' => match_array([]) },
+ 'hasChildren' => false
)
)
)
diff --git a/spec/requests/api/graphql_spec.rb b/spec/requests/api/graphql_spec.rb
index 1c1ae73ddfe..d7724371cce 100644
--- a/spec/requests/api/graphql_spec.rb
+++ b/spec/requests/api/graphql_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'GraphQL' do
+RSpec.describe 'GraphQL', feature_category: :not_owned do
include GraphqlHelpers
include AfterNextHelpers
@@ -25,12 +25,12 @@ RSpec.describe 'GraphQL' do
"query_analysis.used_fields" => ['Query.echo'],
"query_analysis.used_deprecated_fields" => [],
# query_fingerprint starts with operation name
- query_fingerprint: %r{^anonymous\/},
+ query_fingerprint: %r{^anonymous/},
duration_s: kind_of(Numeric),
trace_type: 'execute_query',
operation_name: nil,
# operation_fingerprint starts with operation name
- operation_fingerprint: %r{^anonymous\/},
+ operation_fingerprint: %r{^anonymous/},
is_mutation: false,
variables: variables.to_s,
query_string: query
diff --git a/spec/requests/api/group_avatar_spec.rb b/spec/requests/api/group_avatar_spec.rb
index 50379d29b09..9a0e79ee9f8 100644
--- a/spec/requests/api/group_avatar_spec.rb
+++ b/spec/requests/api/group_avatar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::GroupAvatar do
+RSpec.describe API::GroupAvatar, feature_category: :subgroups do
def avatar_path(group)
"/groups/#{ERB::Util.url_encode(group.full_path)}/avatar"
end
diff --git a/spec/requests/api/group_boards_spec.rb b/spec/requests/api/group_boards_spec.rb
index cc110aa4017..01f0e6e2061 100644
--- a/spec/requests/api/group_boards_spec.rb
+++ b/spec/requests/api/group_boards_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::GroupBoards do
+RSpec.describe API::GroupBoards, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:non_member) { create(:user) }
let_it_be(:guest) { create(:user) }
diff --git a/spec/requests/api/group_clusters_spec.rb b/spec/requests/api/group_clusters_spec.rb
index 8e127bf0710..68c3af01e56 100644
--- a/spec/requests/api/group_clusters_spec.rb
+++ b/spec/requests/api/group_clusters_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::GroupClusters do
+RSpec.describe API::GroupClusters, feature_category: :kubernetes_management do
include KubernetesHelpers
let(:current_user) { create(:user) }
diff --git a/spec/requests/api/group_container_repositories_spec.rb b/spec/requests/api/group_container_repositories_spec.rb
index 82daab0e5e8..cd88b060a3a 100644
--- a/spec/requests/api/group_container_repositories_spec.rb
+++ b/spec/requests/api/group_container_repositories_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::GroupContainerRepositories do
+RSpec.describe API::GroupContainerRepositories, feature_category: :container_registry do
let_it_be(:group) { create(:group, :private) }
let_it_be(:project) { create(:project, :private, group: group) }
let_it_be(:reporter) { create(:user) }
@@ -35,7 +35,9 @@ RSpec.describe API::GroupContainerRepositories do
describe 'GET /groups/:id/registry/repositories' do
let(:url) { "/groups/#{group.id}/registry/repositories" }
- let(:snowplow_gitlab_standard_context) { { user: api_user, namespace: group } }
+ let(:snowplow_gitlab_standard_context) do
+ { user: api_user, namespace: group, property: 'i_package_container_user' }
+ end
subject { get api(url, api_user) }
diff --git a/spec/requests/api/group_debian_distributions_spec.rb b/spec/requests/api/group_debian_distributions_spec.rb
index 21c5f2f09a0..57b481e4f9f 100644
--- a/spec/requests/api/group_debian_distributions_spec.rb
+++ b/spec/requests/api/group_debian_distributions_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe API::GroupDebianDistributions do
+RSpec.describe API::GroupDebianDistributions, feature_category: :package_registry do
include HttpBasicAuthHelpers
include WorkhorseHelpers
diff --git a/spec/requests/api/group_export_spec.rb b/spec/requests/api/group_export_spec.rb
index 83c34204c78..565365506a7 100644
--- a/spec/requests/api/group_export_spec.rb
+++ b/spec/requests/api/group_export_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::GroupExport do
+RSpec.describe API::GroupExport, feature_category: :importers do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/group_import_spec.rb b/spec/requests/api/group_import_spec.rb
index efad6334518..07c21c93585 100644
--- a/spec/requests/api/group_import_spec.rb
+++ b/spec/requests/api/group_import_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::GroupImport do
+RSpec.describe API::GroupImport, feature_category: :importers do
include WorkhorseHelpers
include_context 'workhorse headers'
@@ -198,12 +198,6 @@ RSpec.describe API::GroupImport do
include_examples 'when all params are correct'
include_examples 'when some params are missing'
end
-
- it "doesn't attempt to migrate file to object storage" do
- expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
-
- subject
- end
end
context 'with object storage enabled' do
diff --git a/spec/requests/api/group_labels_spec.rb b/spec/requests/api/group_labels_spec.rb
index 34533da53dd..1dd90413d35 100644
--- a/spec/requests/api/group_labels_spec.rb
+++ b/spec/requests/api/group_labels_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::GroupLabels do
+RSpec.describe API::GroupLabels, feature_category: :team_planning do
let_it_be(:valid_group_label_title_1) { 'Label foo & bar:subgroup::v.1' }
let_it_be(:valid_group_label_title_1_esc) { ERB::Util.url_encode(valid_group_label_title_1) }
let_it_be(:valid_group_label_title_2) { 'Bar & foo:subgroup::v.2' }
diff --git a/spec/requests/api/group_milestones_spec.rb b/spec/requests/api/group_milestones_spec.rb
index da84e98b905..91f64d02d43 100644
--- a/spec/requests/api/group_milestones_spec.rb
+++ b/spec/requests/api/group_milestones_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::GroupMilestones do
+RSpec.describe API::GroupMilestones, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, :private) }
let_it_be(:project) { create(:project, namespace: group) }
diff --git a/spec/requests/api/group_packages_spec.rb b/spec/requests/api/group_packages_spec.rb
index a2b0b35c76a..0b4f6130132 100644
--- a/spec/requests/api/group_packages_spec.rb
+++ b/spec/requests/api/group_packages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::GroupPackages do
+RSpec.describe API::GroupPackages, feature_category: :package_registry do
let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, :public, namespace: group, name: 'project A') }
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/group_variables_spec.rb b/spec/requests/api/group_variables_spec.rb
index a07a8ae4292..90b9606ec7b 100644
--- a/spec/requests/api/group_variables_spec.rb
+++ b/spec/requests/api/group_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::GroupVariables do
+RSpec.describe API::GroupVariables, feature_category: :pipeline_authoring do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:variable) { create(:ci_group_variable, group: group) }
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index c94bc1e1bac..12a6553f51a 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Groups do
+RSpec.describe API::Groups, feature_category: :subgroups do
include GroupAPIHelpers
include UploadHelpers
include WorkhorseHelpers
diff --git a/spec/requests/api/helm_packages_spec.rb b/spec/requests/api/helm_packages_spec.rb
index 6bd81f64913..584f6e3c7d4 100644
--- a/spec/requests/api/helm_packages_spec.rb
+++ b/spec/requests/api/helm_packages_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe API::HelmPackages do
+RSpec.describe API::HelmPackages, feature_category: :package_registry do
include_context 'helm api setup'
using RSpec::Parameterized::TableSyntax
@@ -17,6 +17,8 @@ RSpec.describe API::HelmPackages do
let_it_be(:package_file2_2) { create(:helm_package_file, package: package2, file_sha256: 'file2', file_name: 'filename2.tgz', channel: 'test', description: 'hello from test channel') }
let_it_be(:other_package) { create(:npm_package, project: project) }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_helm_user' } }
+
describe 'GET /api/v4/projects/:id/packages/helm/:channel/index.yaml' do
let(:project_id) { project.id }
let(:channel) { 'stable' }
@@ -63,7 +65,6 @@ RSpec.describe API::HelmPackages do
with_them do
let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, personal_access_token.token) }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
before do
project.update!(visibility: visibility.to_s)
@@ -74,8 +75,6 @@ RSpec.describe API::HelmPackages do
end
context 'with access to package registry for everyone' do
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
-
before do
project.update!(visibility: Gitlab::VisibilityLevel::PRIVATE)
project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC)
@@ -152,7 +151,6 @@ RSpec.describe API::HelmPackages do
let(:params) { { chart: temp_file(file_name) } }
let(:file_key) { :chart }
let(:send_rewritten_field) { true }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
subject do
workhorse_finalize(
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index e29e5c31a34..38275ce0057 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require 'raven/transports/dummy'
require_relative '../../../config/initializers/sentry'
-RSpec.describe API::Helpers do
+RSpec.describe API::Helpers, :enable_admin_mode, feature_category: :authentication_and_authorization do
include API::APIGuard::HelperMethods
include described_class
include TermsHelper
diff --git a/spec/requests/api/import_bitbucket_server_spec.rb b/spec/requests/api/import_bitbucket_server_spec.rb
index 8ab41f49549..7c2df52fdf3 100644
--- a/spec/requests/api/import_bitbucket_server_spec.rb
+++ b/spec/requests/api/import_bitbucket_server_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ImportBitbucketServer do
+RSpec.describe API::ImportBitbucketServer, feature_category: :importers do
let(:base_uri) { "https://test:7990" }
let(:user) { create(:user) }
let(:token) { "asdasd12345" }
diff --git a/spec/requests/api/import_github_spec.rb b/spec/requests/api/import_github_spec.rb
index 4f95295c14d..dce82f1cf37 100644
--- a/spec/requests/api/import_github_spec.rb
+++ b/spec/requests/api/import_github_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ImportGithub do
+RSpec.describe API::ImportGithub, feature_category: :importers do
let(:token) { "asdasd12345" }
let(:provider) { :github }
let(:access_params) { { github_access_token: token } }
diff --git a/spec/requests/api/integrations/jira_connect/subscriptions_spec.rb b/spec/requests/api/integrations/jira_connect/subscriptions_spec.rb
index 8a222a99b34..8d7b462771d 100644
--- a/spec/requests/api/integrations/jira_connect/subscriptions_spec.rb
+++ b/spec/requests/api/integrations/jira_connect/subscriptions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Integrations::JiraConnect::Subscriptions do
+RSpec.describe API::Integrations::JiraConnect::Subscriptions, feature_category: :integrations do
describe 'POST /integrations/jira_connect/subscriptions' do
subject(:post_subscriptions) { post api('/integrations/jira_connect/subscriptions') }
@@ -20,20 +20,6 @@ RSpec.describe API::Integrations::JiraConnect::Subscriptions do
post api('/integrations/jira_connect/subscriptions', user), params: { jwt: jwt, namespace_path: group.path }
end
- context 'with feature flag disabled' do
- before do
- stub_feature_flags(jira_connect_oauth: false)
- end
-
- let(:jwt) { '123' }
-
- it 'returns 404' do
- post_subscriptions
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
context 'with invalid JWT' do
let(:jwt) { '123' }
diff --git a/spec/requests/api/integrations_spec.rb b/spec/requests/api/integrations_spec.rb
index 1e8061f9606..c35b9bab0ec 100644
--- a/spec/requests/api/integrations_spec.rb
+++ b/spec/requests/api/integrations_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe API::Integrations do
+RSpec.describe API::Integrations, feature_category: :integrations do
let_it_be(:user) { create(:user) }
let_it_be(:user2) { create(:user) }
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index 32cacfc713c..f9284f21aaa 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Internal::Base do
+RSpec.describe API::Internal::Base, feature_category: :authentication_and_authorization do
include GitlabShellHelpers
include APIInternalBaseHelpers
@@ -325,6 +325,28 @@ RSpec.describe API::Internal::Base do
expect(json_response['name']).to eq(user.name)
end
+ context 'when signing key is passed' do
+ it 'does not authenticate user' do
+ key.signing!
+
+ get(api("/internal/discover"), params: { key_id: key.id }, headers: gitlab_shell_internal_api_request_header)
+
+ expect(json_response).to be_nil
+ end
+ end
+
+ context 'when auth-only key is passed' do
+ it 'authenticates user' do
+ key.auth!
+
+ get(api("/internal/discover"), params: { key_id: key.id }, headers: gitlab_shell_internal_api_request_header)
+
+ expect(response).to have_gitlab_http_status(:ok)
+
+ expect(json_response['name']).to eq(user.name)
+ end
+ end
+
it "finds a user by username" do
get(api("/internal/discover"), params: { username: user.username }, headers: gitlab_shell_internal_api_request_header)
@@ -360,6 +382,30 @@ RSpec.describe API::Internal::Base do
expect(json_response['key'].split[1]).to eq(key.key.split[1])
end
+ context 'when signing key is passed' do
+ it 'does not return the key' do
+ key.signing!
+
+ get(api('/internal/authorized_keys'), params: { key: key.key.split[1] }, headers: gitlab_shell_internal_api_request_header)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+
+ expect(json_response['id']).to be_nil
+ end
+ end
+
+ context 'when auth-only key is passed' do
+ it 'authenticates user' do
+ key.auth!
+
+ get(api('/internal/authorized_keys'), params: { key: key.key.split[1] }, headers: gitlab_shell_internal_api_request_header)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['id']).to eq(key.id)
+ expect(json_response['key'].split[1]).to eq(key.key.split[1])
+ end
+ end
+
it 'exposes the comment of the key as a simple identifier of username + hostname' do
get(api('/internal/authorized_keys'), params: { key: key.key.split[1] }, headers: gitlab_shell_internal_api_request_header)
diff --git a/spec/requests/api/internal/container_registry/migration_spec.rb b/spec/requests/api/internal/container_registry/migration_spec.rb
index db2918e65f1..b9258e4627a 100644
--- a/spec/requests/api/internal/container_registry/migration_spec.rb
+++ b/spec/requests/api/internal/container_registry/migration_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Internal::ContainerRegistry::Migration, :aggregate_failures do
+RSpec.describe API::Internal::ContainerRegistry::Migration, :aggregate_failures, feature_category: :database do
let_it_be_with_reload(:repository) { create(:container_repository) }
let(:secret_token) { 'secret_token' }
diff --git a/spec/requests/api/internal/error_tracking_spec.rb b/spec/requests/api/internal/error_tracking_spec.rb
index 4c420eb8505..83012e26138 100644
--- a/spec/requests/api/internal/error_tracking_spec.rb
+++ b/spec/requests/api/internal/error_tracking_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Internal::ErrorTracking do
+RSpec.describe API::Internal::ErrorTracking, feature_category: :error_tracking do
let(:secret_token) { Gitlab::CurrentSettings.error_tracking_access_token }
let(:headers) do
{ ::API::Internal::ErrorTracking::GITLAB_ERROR_TRACKING_TOKEN_HEADER => secret_token }
diff --git a/spec/requests/api/internal/kubernetes_spec.rb b/spec/requests/api/internal/kubernetes_spec.rb
index 3c6604cf409..dc631ad7921 100644
--- a/spec/requests/api/internal/kubernetes_spec.rb
+++ b/spec/requests/api/internal/kubernetes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Internal::Kubernetes do
+RSpec.describe API::Internal::Kubernetes, feature_category: :kubernetes_management do
let(:jwt_auth_headers) do
jwt_token = JWT.encode({ 'iss' => Gitlab::Kas::JWT_ISSUER }, Gitlab::Kas.secret, 'HS256')
@@ -103,15 +103,19 @@ RSpec.describe API::Internal::Kubernetes do
expect(response).to have_gitlab_http_status(:bad_request)
end
- it 'tracks events' do
+ it 'tracks events and unique events', :aggregate_failures do
+ request_count = 2
counters = { gitops_sync: 10, k8s_api_proxy_request: 5 }
- unique_counters = { agent_users_using_ci_tunnel: [10] }
+ unique_counters = { agent_users_using_ci_tunnel: [10, 999, 777, 10] }
expected_counters = {
- kubernetes_agent_gitops_sync: counters[:gitops_sync],
- kubernetes_agent_k8s_api_proxy_request: counters[:k8s_api_proxy_request]
+ kubernetes_agent_gitops_sync: request_count * counters[:gitops_sync],
+ kubernetes_agent_k8s_api_proxy_request: request_count * counters[:k8s_api_proxy_request]
}
+ expected_hll_count = unique_counters[:agent_users_using_ci_tunnel].uniq.count
- send_request(params: { counters: counters, unique_counters: unique_counters })
+ request_count.times do
+ send_request(params: { counters: counters, unique_counters: unique_counters })
+ end
expect(Gitlab::UsageDataCounters::KubernetesAgentCounter.totals).to eq(expected_counters)
@@ -121,7 +125,7 @@ RSpec.describe API::Internal::Kubernetes do
event_names: 'agent_users_using_ci_tunnel',
start_date: Date.current, end_date: Date.current + 10
)
- ).to eq(1)
+ ).to eq(expected_hll_count)
end
end
end
diff --git a/spec/requests/api/internal/lfs_spec.rb b/spec/requests/api/internal/lfs_spec.rb
index 9eb48db5bd5..1021c03f736 100644
--- a/spec/requests/api/internal/lfs_spec.rb
+++ b/spec/requests/api/internal/lfs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Internal::Lfs do
+RSpec.describe API::Internal::Lfs, feature_category: :source_code_management do
include GitlabShellHelpers
include APIInternalBaseHelpers
diff --git a/spec/requests/api/internal/mail_room_spec.rb b/spec/requests/api/internal/mail_room_spec.rb
index a0a9c1f9cb3..7baa26e3508 100644
--- a/spec/requests/api/internal/mail_room_spec.rb
+++ b/spec/requests/api/internal/mail_room_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Internal::MailRoom do
+RSpec.describe API::Internal::MailRoom, feature_category: :service_desk do
let(:base_configs) do
{
enabled: true,
diff --git a/spec/requests/api/internal/pages_spec.rb b/spec/requests/api/internal/pages_spec.rb
index 5b970ca605c..56f1089843b 100644
--- a/spec/requests/api/internal/pages_spec.rb
+++ b/spec/requests/api/internal/pages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Internal::Pages do
+RSpec.describe API::Internal::Pages, feature_category: :pages do
let(:auth_headers) do
jwt_token = JWT.encode({ 'iss' => 'gitlab-pages' }, Gitlab::Pages.secret, 'HS256')
{ Gitlab::Pages::INTERNAL_API_REQUEST_HEADER => jwt_token }
diff --git a/spec/requests/api/internal/workhorse_spec.rb b/spec/requests/api/internal/workhorse_spec.rb
index bcf63bf7c2f..99d0ecabbb7 100644
--- a/spec/requests/api/internal/workhorse_spec.rb
+++ b/spec/requests/api/internal/workhorse_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Internal::Workhorse, :allow_forgery_protection do
+RSpec.describe API::Internal::Workhorse, :allow_forgery_protection, feature_category: :not_owned do
include WorkhorseHelpers
context '/authorize_upload' do
diff --git a/spec/requests/api/invitations_spec.rb b/spec/requests/api/invitations_spec.rb
index c07d2e11363..9d3ab269ca1 100644
--- a/spec/requests/api/invitations_spec.rb
+++ b/spec/requests/api/invitations_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Invitations do
+RSpec.describe API::Invitations, feature_category: :users do
let_it_be(:maintainer) { create(:user, username: 'maintainer_user') }
let_it_be(:maintainer2) { create(:user, username: 'user-with-maintainer-role') }
let_it_be(:developer) { create(:user) }
diff --git a/spec/requests/api/issue_links_spec.rb b/spec/requests/api/issue_links_spec.rb
index 98f72f22cdc..93bf17d72d7 100644
--- a/spec/requests/api/issue_links_spec.rb
+++ b/spec/requests/api/issue_links_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::IssueLinks do
+RSpec.describe API::IssueLinks, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
@@ -189,6 +189,8 @@ RSpec.describe API::IssueLinks do
end
context 'when authenticated' do
+ let_it_be(:target_issue) { create(:issue, project: project) }
+
context 'when issue link does not exist' do
it 'returns 404' do
perform_request(non_existing_record_id, user)
@@ -197,8 +199,6 @@ RSpec.describe API::IssueLinks do
end
end
- let_it_be(:target_issue) { create(:issue, project: project) }
-
context 'when issue link does not belong to the specified issue' do
it 'returns 404' do
other_issue = create(:issue, project: project)
diff --git a/spec/requests/api/issues/get_group_issues_spec.rb b/spec/requests/api/issues/get_group_issues_spec.rb
index 5c06214316b..0641c2135c1 100644
--- a/spec/requests/api/issues/get_group_issues_spec.rb
+++ b/spec/requests/api/issues/get_group_issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Issues do
+RSpec.describe API::Issues, feature_category: :team_planning do
let_it_be(:user2) { create(:user) }
let_it_be(:admin) { create(:user, :admin) }
let_it_be(:non_member) { create(:user) }
diff --git a/spec/requests/api/issues/get_project_issues_spec.rb b/spec/requests/api/issues/get_project_issues_spec.rb
index ec6cc060c83..70966d23576 100644
--- a/spec/requests/api/issues/get_project_issues_spec.rb
+++ b/spec/requests/api/issues/get_project_issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Issues do
+RSpec.describe API::Issues, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, :public, :repository, creator_id: user.id, namespace: user.namespace) }
let_it_be(:private_mrs_project) do
diff --git a/spec/requests/api/issues/issues_spec.rb b/spec/requests/api/issues/issues_spec.rb
index 0e20b2133db..94f0443e14a 100644
--- a/spec/requests/api/issues/issues_spec.rb
+++ b/spec/requests/api/issues/issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Issues do
+RSpec.describe API::Issues, feature_category: :team_planning do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/issues/post_projects_issues_spec.rb b/spec/requests/api/issues/post_projects_issues_spec.rb
index deaf7be96ab..7305da1305a 100644
--- a/spec/requests/api/issues/post_projects_issues_spec.rb
+++ b/spec/requests/api/issues/post_projects_issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Issues do
+RSpec.describe API::Issues, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) do
create(:project, :public, creator_id: user.id, namespace: user.namespace)
diff --git a/spec/requests/api/issues/put_projects_issues_spec.rb b/spec/requests/api/issues/put_projects_issues_spec.rb
index d6c57b460e0..2d7439d65c1 100644
--- a/spec/requests/api/issues/put_projects_issues_spec.rb
+++ b/spec/requests/api/issues/put_projects_issues_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Issues do
+RSpec.describe API::Issues, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:owner) { create(:owner) }
let(:user2) { create(:user) }
diff --git a/spec/requests/api/keys_spec.rb b/spec/requests/api/keys_spec.rb
index 67c3de324dc..d9a0f061156 100644
--- a/spec/requests/api/keys_spec.rb
+++ b/spec/requests/api/keys_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Keys do
+RSpec.describe API::Keys, feature_category: :authentication_and_authorization do
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
let_it_be(:email) { create(:email, user: user) }
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index 97ab90c9776..b5d7d564749 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Labels do
+RSpec.describe API::Labels, feature_category: :team_planning do
def put_labels_api(route_type, user, spec_params, request_params = {})
if route_type == :deprecated
put api("/projects/#{project.id}/labels", user),
diff --git a/spec/requests/api/lint_spec.rb b/spec/requests/api/lint_spec.rb
index 5d8ed3dd0f5..82b87007a9b 100644
--- a/spec/requests/api/lint_spec.rb
+++ b/spec/requests/api/lint_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Lint do
+RSpec.describe API::Lint, feature_category: :pipeline_authoring do
describe 'POST /ci/lint' do
context 'when signup settings are disabled' do
before do
diff --git a/spec/requests/api/markdown_golden_master_spec.rb b/spec/requests/api/markdown_golden_master_spec.rb
index 4fa946de342..1bb5a1d67ae 100644
--- a/spec/requests/api/markdown_golden_master_spec.rb
+++ b/spec/requests/api/markdown_golden_master_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
# See spec/fixtures/markdown/markdown_golden_master_examples.yml for documentation on how this spec works.
-RSpec.describe API::Markdown, 'Golden Master' do
+RSpec.describe API::Markdown, 'Golden Master', feature_category: :team_planning do
markdown_yml_file_path = File.expand_path('../../fixtures/markdown/markdown_golden_master_examples.yml', __dir__)
include_context 'API::Markdown Golden Master shared context', markdown_yml_file_path
end
diff --git a/spec/requests/api/markdown_snapshot_spec.rb b/spec/requests/api/markdown_snapshot_spec.rb
index f2019172a54..866cbcf8ff6 100644
--- a/spec/requests/api/markdown_snapshot_spec.rb
+++ b/spec/requests/api/markdown_snapshot_spec.rb
@@ -4,6 +4,6 @@ require 'spec_helper'
# See https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#markdown-snapshot-testing
# for documentation on this spec.
-RSpec.describe API::Markdown, 'Snapshot' do
+RSpec.describe API::Markdown, 'Snapshot', feature_category: :team_planning do
include_context 'with API::Markdown Snapshot shared context'
end
diff --git a/spec/requests/api/markdown_spec.rb b/spec/requests/api/markdown_spec.rb
index 6239ac4e749..db5bbd610fc 100644
--- a/spec/requests/api/markdown_spec.rb
+++ b/spec/requests/api/markdown_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe API::Markdown do
+RSpec.describe API::Markdown, feature_category: :team_planning do
describe "POST /markdown" do
let(:user) {} # No-op. It gets overwritten in the contexts below.
let(:disable_authenticate_markdown_api) { false }
diff --git a/spec/requests/api/maven_packages_spec.rb b/spec/requests/api/maven_packages_spec.rb
index ac8c4aacdf2..092eb442f1f 100644
--- a/spec/requests/api/maven_packages_spec.rb
+++ b/spec/requests/api/maven_packages_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe API::MavenPackages do
+RSpec.describe API::MavenPackages, feature_category: :package_registry do
using RSpec::Parameterized::TableSyntax
include WorkhorseHelpers
@@ -22,7 +22,8 @@ RSpec.describe API::MavenPackages do
let_it_be(:deploy_token_for_group) { create(:deploy_token, :group, read_package_registry: true, write_package_registry: true) }
let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: deploy_token_for_group, group: group) }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_maven_user' } }
+
let(:package_name) { 'com/example/my-app' }
let(:headers) { workhorse_headers }
let(:headers_with_token) { headers.merge('Private-Token' => personal_access_token.token) }
@@ -98,8 +99,6 @@ RSpec.describe API::MavenPackages do
context 'with jar file' do
let_it_be(:package_file) { jar_file }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
-
it_behaves_like 'a package tracking event', described_class.name, 'pull_package'
end
end
@@ -900,6 +899,8 @@ RSpec.describe API::MavenPackages do
it_behaves_like 'package workhorse uploads'
context 'event tracking' do
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user, property: 'i_package_maven_user' } }
+
it_behaves_like 'a package tracking event', described_class.name, 'push_package'
context 'when the package file fails to be created' do
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index 69be574f38a..4eff5e96e9c 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Members do
+RSpec.describe API::Members, feature_category: :subgroups do
let_it_be(:maintainer) { create(:user, username: 'maintainer_user') }
let_it_be(:maintainer2) { create(:user, username: 'user-with-maintainer-role') }
let_it_be(:developer) { create(:user) }
diff --git a/spec/requests/api/merge_request_approvals_spec.rb b/spec/requests/api/merge_request_approvals_spec.rb
index b18f3017e03..a1d6abec97e 100644
--- a/spec/requests/api/merge_request_approvals_spec.rb
+++ b/spec/requests/api/merge_request_approvals_spec.rb
@@ -2,8 +2,10 @@
require 'spec_helper'
-RSpec.describe API::MergeRequestApprovals do
+RSpec.describe API::MergeRequestApprovals, feature_category: :source_code_management do
let_it_be(:user) { create(:user) }
+ let_it_be(:user2) { create(:user) }
+ let_it_be(:bot) { create(:user, :project_bot) }
let_it_be(:project) { create(:project, :public, :repository, creator: user, namespace: user.namespace) }
let_it_be(:approver) { create :user }
let_it_be(:group) { create :group }
@@ -87,4 +89,83 @@ RSpec.describe API::MergeRequestApprovals do
end
end
end
+
+ describe 'PUT :id/merge_requests/:merge_request_iid/reset_approvals' do
+ before do
+ merge_request.approvals.create!(user: user2)
+ create(:project_member, :maintainer, user: bot, source: project)
+ end
+
+ context 'for a bot user' do
+ it 'clears approvals of the merge_request' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/reset_approvals", bot)
+
+ merge_request.reload
+ expect(response).to have_gitlab_http_status(:accepted)
+ expect(merge_request.approvals).to be_empty
+ end
+
+ context 'when bot user approved the merge request' do
+ before do
+ merge_request.approvals.create!(user: bot)
+ end
+
+ it 'clears approvals of the merge_request' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/reset_approvals", bot)
+
+ merge_request.reload
+ expect(response).to have_gitlab_http_status(:accepted)
+ expect(merge_request.approvals).to be_empty
+ end
+ end
+ end
+
+ context 'for users with non-bot roles' do
+ let(:human_user) { create(:user) }
+
+ [:add_owner, :add_maintainer, :add_developer, :add_guest].each do |role_method|
+ it 'returns 401' do
+ project.send(role_method, human_user)
+
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/reset_approvals", human_user)
+
+ merge_request.reload
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(merge_request.approvals.pluck(:user_id)).to contain_exactly(user2.id)
+ end
+ end
+ end
+
+ context 'for bot-users from external namespaces' do
+ let_it_be(:external_bot) { create(:user, :project_bot) }
+
+ context 'for external group bot-user' do
+ before do
+ create(:group_member, :maintainer, user: external_bot, source: create(:group))
+ end
+
+ it 'returns 401' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/reset_approvals", external_bot)
+
+ merge_request.reload
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(merge_request.approvals.pluck(:user_id)).to contain_exactly(user2.id)
+ end
+ end
+
+ context 'for external project bot-user' do
+ before do
+ create(:project_member, :maintainer, user: external_bot, source: create(:project))
+ end
+
+ it 'returns 401' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/reset_approvals", external_bot)
+
+ merge_request.reload
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(merge_request.approvals.pluck(:user_id)).to contain_exactly(user2.id)
+ end
+ end
+ end
+ end
end
diff --git a/spec/requests/api/merge_request_diffs_spec.rb b/spec/requests/api/merge_request_diffs_spec.rb
index caef946273a..4f812e5d8eb 100644
--- a/spec/requests/api/merge_request_diffs_spec.rb
+++ b/spec/requests/api/merge_request_diffs_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe API::MergeRequestDiffs, 'MergeRequestDiffs' do
+RSpec.describe API::MergeRequestDiffs, 'MergeRequestDiffs', feature_category: :source_code_management do
let!(:user) { create(:user) }
let!(:merge_request) { create(:merge_request, importing: true) }
let!(:project) { merge_request.target_project }
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index eea223485ce..0b69000ae7e 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -2,14 +2,13 @@
require "spec_helper"
-RSpec.describe API::MergeRequests do
+RSpec.describe API::MergeRequests, feature_category: :source_code_management do
include ProjectForksHelper
let_it_be(:base_time) { Time.now }
let_it_be(:user) { create(:user) }
let_it_be(:user2) { create(:user) }
let_it_be(:admin) { create(:user, :admin) }
- let_it_be(:bot) { create(:user, :project_bot) }
let_it_be(:project) { create(:project, :public, :repository, creator: user, namespace: user.namespace, only_allow_merge_if_pipeline_succeeds: false) }
let(:milestone1) { create(:milestone, title: '0.9', project: project) }
@@ -1788,6 +1787,58 @@ RSpec.describe API::MergeRequests do
end
end
+ describe 'GET /projects/:id/merge_requests/:merge_request_iid/diffs' do
+ let_it_be(:merge_request) do
+ create(
+ :merge_request,
+ :simple,
+ author: user,
+ assignees: [user],
+ source_project: project,
+ target_project: project,
+ source_branch: 'markdown',
+ title: "Test",
+ created_at: base_time
+ )
+ end
+
+ it 'returns a 404 when merge_request_iid not found' do
+ get api("/projects/#{project.id}/merge_requests/0/diffs", user)
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'returns a 404 when merge_request id is used instead of iid' do
+ get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/diffs", user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ context 'when merge request author has only guest access' do
+ it_behaves_like 'rejects user from accessing merge request info' do
+ let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/diffs" }
+ end
+ end
+
+ it 'returns the diffs of the merge_request' do
+ get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/diffs", user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.size).to eq(merge_request.diffs.size)
+ end
+
+ context 'when pagination params are present' do
+ it 'returns limited diffs' do
+ get(
+ api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/diffs", user),
+ params: { page: 1, per_page: 1 }
+ )
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.size).to eq(1)
+ end
+ end
+ end
+
describe 'GET /projects/:id/merge_requests/:merge_request_iid/pipelines' do
let_it_be(:merge_request) { create(:merge_request, :simple, author: user, assignees: [user], source_project: project, target_project: project, source_branch: 'markdown', title: "Test", created_at: base_time) }
@@ -3560,71 +3611,6 @@ RSpec.describe API::MergeRequests do
end
end
- describe 'PUT :id/merge_requests/:merge_request_iid/reset_approvals' do
- before do
- merge_request.approvals.create!(user: user2)
- create(:project_member, :maintainer, user: bot, source: project)
- end
-
- context 'when reset_approvals can be performed' do
- it 'clears approvals of the merge_request' do
- put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/reset_approvals", bot)
-
- merge_request.reload
- expect(response).to have_gitlab_http_status(:accepted)
- expect(merge_request.approvals).to be_empty
- end
-
- it 'for users with bot role' do
- put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/reset_approvals", bot)
-
- expect(response).to have_gitlab_http_status(:accepted)
- end
-
- context 'for users with non-bot roles' do
- let(:human_user) { create(:user) }
-
- [:add_owner, :add_maintainer, :add_developer, :add_guest].each do |role_method|
- it 'returns 401' do
- project.send(role_method, human_user)
-
- put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/reset_approvals", human_user)
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
- end
- end
-
- context 'for bot-users from external namespaces' do
- let_it_be(:external_bot) { create(:user, :project_bot) }
-
- context 'external group bot-user' do
- before do
- create(:group_member, :maintainer, user: external_bot, source: create(:group))
- end
-
- it 'returns 401' do
- put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/reset_approvals", external_bot)
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
- end
-
- context 'external project bot-user' do
- before do
- create(:project_member, :maintainer, user: external_bot, source: create(:project))
- end
-
- it 'returns 401' do
- put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/reset_approvals", external_bot)
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
- end
- end
- end
- end
-
describe 'Time tracking' do
let!(:issuable) { create(:merge_request, :simple, author: user, assignees: [user], source_project: project, target_project: project, source_branch: 'markdown', title: "Test", created_at: base_time) }
diff --git a/spec/requests/api/metadata_spec.rb b/spec/requests/api/metadata_spec.rb
index 5b6407c689b..b9bdadb01cc 100644
--- a/spec/requests/api/metadata_spec.rb
+++ b/spec/requests/api/metadata_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Metadata do
+RSpec.describe API::Metadata, feature_category: :not_owned do
shared_examples_for 'GET /metadata' do
context 'when unauthenticated' do
it 'returns authentication error' do
diff --git a/spec/requests/api/metrics/dashboard/annotations_spec.rb b/spec/requests/api/metrics/dashboard/annotations_spec.rb
index a09596f167d..7932dd29e4d 100644
--- a/spec/requests/api/metrics/dashboard/annotations_spec.rb
+++ b/spec/requests/api/metrics/dashboard/annotations_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Metrics::Dashboard::Annotations do
+RSpec.describe API::Metrics::Dashboard::Annotations, feature_category: :metrics do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :private, :repository, namespace: user.namespace) }
let_it_be(:environment) { create(:environment, project: project) }
diff --git a/spec/requests/api/metrics/user_starred_dashboards_spec.rb b/spec/requests/api/metrics/user_starred_dashboards_spec.rb
index 7f019e1226a..38d3c0be8b2 100644
--- a/spec/requests/api/metrics/user_starred_dashboards_spec.rb
+++ b/spec/requests/api/metrics/user_starred_dashboards_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Metrics::UserStarredDashboards do
+RSpec.describe API::Metrics::UserStarredDashboards, feature_category: :metrics do
let_it_be(:user) { create(:user) }
let_it_be(:dashboard_yml) { fixture_file('lib/gitlab/metrics/dashboard/sample_dashboard.yml') }
let_it_be(:dashboard) { '.gitlab/dashboards/find&seek.yml' }
diff --git a/spec/requests/api/ml/mlflow_spec.rb b/spec/requests/api/ml/mlflow_spec.rb
index 9448f009742..c1ed7d56ba4 100644
--- a/spec/requests/api/ml/mlflow_spec.rb
+++ b/spec/requests/api/ml/mlflow_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require 'mime/types'
-RSpec.describe API::Ml::Mlflow do
+RSpec.describe API::Ml::Mlflow, feature_category: :mlops do
include SessionHelpers
include ApiHelpers
include HttpBasicAuthHelpers
@@ -12,12 +12,13 @@ RSpec.describe API::Ml::Mlflow do
let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
let_it_be(:another_project) { build(:project).tap { |p| p.add_developer(developer) } }
let_it_be(:experiment) do
- create(:ml_experiments, user: project.creator, project: project)
+ create(:ml_experiments, :with_metadata, project: project)
end
let_it_be(:candidate) do
create(:ml_candidates,
- :with_metrics_and_params, user: experiment.user, start_time: 1234, experiment: experiment)
+ :with_metrics_and_params, :with_metadata,
+ user: experiment.user, start_time: 1234, experiment: experiment)
end
let_it_be(:tokens) do
@@ -151,7 +152,17 @@ RSpec.describe API::Ml::Mlflow do
'experiment_id' => experiment_iid,
'name' => experiment.name,
'lifecycle_stage' => 'active',
- 'artifact_location' => 'not_implemented'
+ 'artifact_location' => 'not_implemented',
+ 'tags' => [
+ {
+ 'key' => experiment.metadata[0].name,
+ 'value' => experiment.metadata[0].value
+ },
+ {
+ 'key' => experiment.metadata[1].name,
+ 'value' => experiment.metadata[1].value
+ }
+ ]
}
})
end
@@ -187,7 +198,17 @@ RSpec.describe API::Ml::Mlflow do
'experiment_id' => experiment.iid.to_s,
'name' => experiment.name,
'lifecycle_stage' => 'active',
- 'artifact_location' => 'not_implemented'
+ 'artifact_location' => 'not_implemented',
+ 'tags' => [
+ {
+ 'key' => experiment.metadata[0].name,
+ 'value' => experiment.metadata[0].value
+ },
+ {
+ 'key' => experiment.metadata[1].name,
+ 'value' => experiment.metadata[1].value
+ }
+ ]
]
})
end
@@ -220,7 +241,17 @@ RSpec.describe API::Ml::Mlflow do
'experiment_id' => experiment.iid.to_s,
'name' => experiment_name,
'lifecycle_stage' => 'active',
- 'artifact_location' => 'not_implemented'
+ 'artifact_location' => 'not_implemented',
+ 'tags' => [
+ {
+ 'key' => experiment.metadata[0].name,
+ 'value' => experiment.metadata[0].value
+ },
+ {
+ 'key' => experiment.metadata[1].name,
+ 'value' => experiment.metadata[1].value
+ }
+ ]
}
})
end
@@ -284,10 +315,44 @@ RSpec.describe API::Ml::Mlflow do
end
end
+ describe 'POST /projects/:id/ml/mlflow/api/2.0/mlflow/experiments/set-experiment-tag' do
+ let(:route) { "/projects/#{project_id}/ml/mlflow/api/2.0/mlflow/experiments/set-experiment-tag" }
+ let(:default_params) { { experiment_id: experiment.iid.to_s, key: 'some_key', value: 'value' } }
+ let(:params) { default_params }
+ let(:request) { post api(route), params: params, headers: headers }
+
+ it 'logs the tag', :aggregate_failures do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to be_empty
+ expect(experiment.reload.metadata.map(&:name)).to include('some_key')
+ end
+
+ describe 'Error Cases' do
+ context 'when tag was already set' do
+ let(:params) { default_params.merge(key: experiment.metadata[0].name) }
+
+ it_behaves_like 'Bad Request'
+ end
+
+ it_behaves_like 'shared error cases'
+ it_behaves_like 'Requires api scope'
+ it_behaves_like 'Bad Request on missing required', [:key, :value]
+ end
+ end
+
describe 'Runs' do
describe 'POST /projects/:id/ml/mlflow/api/2.0/mlflow/runs/create' do
let(:route) { "/projects/#{project_id}/ml/mlflow/api/2.0/mlflow/runs/create" }
- let(:params) { { experiment_id: experiment.iid.to_s, start_time: Time.now.to_i } }
+ let(:params) do
+ {
+ experiment_id: experiment.iid.to_s,
+ start_time: Time.now.to_i,
+ tags: [
+ { key: 'hello', value: 'world' }
+ ]
+ }
+ end
+
let(:request) { post api(route), params: params, headers: headers }
it 'creates the run', :aggregate_failures do
@@ -295,14 +360,18 @@ RSpec.describe API::Ml::Mlflow do
'experiment_id' => params[:experiment_id],
'user_id' => current_user.id.to_s,
'start_time' => params[:start_time],
- 'status' => "RUNNING",
- 'lifecycle_stage' => "active"
+ 'status' => 'RUNNING',
+ 'lifecycle_stage' => 'active'
}
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('ml/run')
expect(json_response['run']).to include('info' => hash_including(**expected_properties),
- 'data' => { 'metrics' => [], 'params' => [] })
+ 'data' => {
+ 'metrics' => [],
+ 'params' => [],
+ 'tags' => [{ 'key' => 'hello', 'value' => 'world' }]
+ })
end
describe 'Error States' do
@@ -355,6 +424,10 @@ RSpec.describe API::Ml::Mlflow do
'params' => [
{ 'key' => candidate.params[0].name, 'value' => candidate.params[0].value },
{ 'key' => candidate.params[1].name, 'value' => candidate.params[1].value }
+ ],
+ 'tags' => [
+ { 'key' => 'metadata_1', 'value' => 'value1' },
+ { 'key' => 'metadata_2', 'value' => 'value2' }
]
})
end
@@ -454,6 +527,31 @@ RSpec.describe API::Ml::Mlflow do
end
end
+ describe 'POST /projects/:id/ml/mlflow/api/2.0/mlflow/runs/set-tag' do
+ let(:route) { "/projects/#{project_id}/ml/mlflow/api/2.0/mlflow/runs/set-tag" }
+ let(:default_params) { { run_id: candidate.iid.to_s, key: 'some_key', value: 'value' } }
+ let(:request) { post api(route), params: params, headers: headers }
+
+ it 'logs the tag', :aggregate_failures do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to be_empty
+ expect(candidate.reload.metadata.map(&:name)).to include('some_key')
+ end
+
+ describe 'Error Cases' do
+ context 'when tag was already logged' do
+ let(:params) { default_params.tap { |p| p[:key] = candidate.metadata[0].name } }
+
+ it_behaves_like 'Bad Request'
+ end
+
+ it_behaves_like 'shared error cases'
+ it_behaves_like 'Requires api scope'
+ it_behaves_like 'run_id param error cases'
+ it_behaves_like 'Bad Request on missing required', [:key, :value]
+ end
+ end
+
describe 'POST /projects/:id/ml/mlflow/api/2.0/mlflow/runs/log-batch' do
let(:candidate2) do
create(:ml_candidates, user: experiment.user, start_time: 1234, experiment: experiment)
@@ -467,7 +565,8 @@ RSpec.describe API::Ml::Mlflow do
{ key: 'mae', value: 2.5, timestamp: 1552550804 },
{ key: 'rmse', value: 2.7, timestamp: 1552550804 }
],
- params: [{ key: 'model_class', value: 'LogisticRegression' }]
+ params: [{ key: 'model_class', value: 'LogisticRegression' }],
+ tags: [{ key: 'tag1', value: 'tag.value.1' }]
}
end
@@ -477,6 +576,7 @@ RSpec.describe API::Ml::Mlflow do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_empty
expect(candidate2.params.size).to eq(1)
+ expect(candidate2.metadata.size).to eq(1)
expect(candidate2.metrics.size).to eq(2)
end
@@ -493,6 +593,19 @@ RSpec.describe API::Ml::Mlflow do
end
end
+ context 'when tag was already logged' do
+ let(:params) do
+ default_params.tap { |p| p[:tags] = [{ key: 'tag1', value: 'a' }, { key: 'tag1', value: 'b' }] }
+ end
+
+ it 'logs only 1', :aggregate_failures do
+ candidate.metadata.reload
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(candidate2.metadata.size).to eq(1)
+ end
+ end
+
describe 'Error Cases' do
context 'when required metric key is missing' do
let(:params) { default_params.tap { |p| p[:metrics] = [p[:metrics][0].delete(:key)] } }
diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb
index ab39c29653f..30616964371 100644
--- a/spec/requests/api/namespaces_spec.rb
+++ b/spec/requests/api/namespaces_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Namespaces do
+RSpec.describe API::Namespaces, feature_category: :subgroups do
let_it_be(:admin) { create(:admin) }
let_it_be(:user) { create(:user) }
let_it_be(:group1) { create(:group, name: 'group.one') }
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 89abb28140a..c2d9db1e6fb 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Notes do
+RSpec.describe API::Notes, feature_category: :team_planning do
let!(:user) { create(:user) }
let!(:project) { create(:project, :public) }
let(:private_user) { create(:user) }
@@ -203,6 +203,51 @@ RSpec.describe API::Notes do
end
end
end
+
+ context 'without notes widget' do
+ let(:request_body) { 'Hi!' }
+ let(:params) { { body: request_body } }
+ let(:request_path) { "/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes" }
+
+ before do
+ stub_const('WorkItems::Type::BASE_TYPES', { issue: { name: 'NoNotesWidget', enum_value: 0 } })
+ stub_const('WorkItems::Type::WIDGETS_FOR_TYPE', { issue: [::WorkItems::Widgets::Description] })
+ end
+
+ it 'does not fetch notes' do
+ get api(request_path, private_user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'does not fetch specific note' do
+ get api("#{request_path}/#{cross_reference_note.id}", private_user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'does not create note' do
+ post api(request_path, private_user), params: params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'does not update note' do
+ put api("#{request_path}/#{cross_reference_note.id}", private_user), params: params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'does not run quick actions' do
+ params[:body] = "/spend 1h"
+
+ expect do
+ post api("#{request_path}/#{cross_reference_note.id}", private_user), params: params
+ end.to not_change { Note.system.count }.and(not_change { Note.where(system: false).count })
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
end
end
diff --git a/spec/requests/api/notification_settings_spec.rb b/spec/requests/api/notification_settings_spec.rb
index b5551c21738..2a80dc4bbe9 100644
--- a/spec/requests/api/notification_settings_spec.rb
+++ b/spec/requests/api/notification_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::NotificationSettings do
+RSpec.describe API::NotificationSettings, feature_category: :team_planning do
let(:user) { create(:user) }
let!(:group) { create(:group) }
let!(:project) { create(:project, :public, creator_id: user.id, namespace: group) }
diff --git a/spec/requests/api/npm_instance_packages_spec.rb b/spec/requests/api/npm_instance_packages_spec.rb
index 698885ddcf4..dcd2e4ae677 100644
--- a/spec/requests/api/npm_instance_packages_spec.rb
+++ b/spec/requests/api/npm_instance_packages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::NpmInstancePackages do
+RSpec.describe API::NpmInstancePackages, feature_category: :package_registry do
# We need to create a subgroup with the same name as the hosting group.
# It has to be created first to exhibit this bug: https://gitlab.com/gitlab-org/gitlab/-/issues/321958
let_it_be(:another_namespace) { create(:group, :public) }
@@ -33,4 +33,16 @@ RSpec.describe API::NpmInstancePackages do
let(:url) { api("/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
end
end
+
+ describe 'POST /api/v4/packages/npm/-/npm/v1/security/advisories/bulk' do
+ it_behaves_like 'handling audit request', path: 'advisories/bulk', scope: :instance do
+ let(:url) { api('/packages/npm/-/npm/v1/security/advisories/bulk') }
+ end
+ end
+
+ describe 'POST /api/v4/packages/npm/-/npm/v1/security/audits/quick' do
+ it_behaves_like 'handling audit request', path: 'audits/quick', scope: :instance do
+ let(:url) { api('/packages/npm/-/npm/v1/security/audits/quick') }
+ end
+ end
end
diff --git a/spec/requests/api/npm_project_packages_spec.rb b/spec/requests/api/npm_project_packages_spec.rb
index 373327787a2..c62c0849776 100644
--- a/spec/requests/api/npm_project_packages_spec.rb
+++ b/spec/requests/api/npm_project_packages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::NpmProjectPackages do
+RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
include_context 'npm api setup'
shared_examples 'accept get request on private project with access to package registry for everyone' do
@@ -42,8 +42,19 @@ RSpec.describe API::NpmProjectPackages do
end
end
+ describe 'POST /api/v4/projects/:id/packages/npm/-/npm/v1/security/advisories/bulk' do
+ it_behaves_like 'handling audit request', path: 'advisories/bulk', scope: :project do
+ let(:url) { api("/projects/#{project.id}/packages/npm/-/npm/v1/security/advisories/bulk") }
+ end
+ end
+
+ describe 'POST /api/v4/projects/:id/packages/npm/-/npm/v1/security/audits/quick' do
+ it_behaves_like 'handling audit request', path: 'audits/quick', scope: :project do
+ let(:url) { api("/projects/#{project.id}/packages/npm/-/npm/v1/security/audits/quick") }
+ end
+ end
+
describe 'GET /api/v4/projects/:id/packages/npm/*package_name/-/*file_name' do
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
let(:package_file) { package.package_files.first }
let(:headers) { {} }
@@ -203,7 +214,7 @@ RSpec.describe API::NpmProjectPackages do
let_it_be(:version) { '1.2.3' }
let(:params) { upload_params(package_name: package_name, package_version: version) }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user, property: 'i_package_npm_user' } }
shared_examples 'handling upload with different authentications' do
context 'with access token' do
diff --git a/spec/requests/api/nuget_group_packages_spec.rb b/spec/requests/api/nuget_group_packages_spec.rb
index c1375288809..9de612f7bc7 100644
--- a/spec/requests/api/nuget_group_packages_spec.rb
+++ b/spec/requests/api/nuget_group_packages_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe API::NugetGroupPackages do
+RSpec.describe API::NugetGroupPackages, feature_category: :package_registry do
include_context 'nuget api setup'
using RSpec::Parameterized::TableSyntax
@@ -12,6 +12,7 @@ RSpec.describe API::NugetGroupPackages do
let_it_be(:deploy_token) { create(:deploy_token, :group, read_package_registry: true, write_package_registry: true) }
let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: deploy_token, group: group) }
+ let(:snowplow_gitlab_standard_context) { { namespace: project.group, property: 'i_package_nuget_user' } }
let(:target_type) { 'groups' }
shared_examples 'handling all endpoints' do
@@ -46,7 +47,6 @@ RSpec.describe API::NugetGroupPackages do
let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: deploy_token, group: subgroup) }
let(:target) { subgroup }
- let(:snowplow_gitlab_standard_context) { { namespace: subgroup } }
it_behaves_like 'handling all endpoints'
@@ -58,7 +58,7 @@ RSpec.describe API::NugetGroupPackages do
context 'a group' do
let(:target) { group }
- let(:snowplow_gitlab_standard_context) { { namespace: group } }
+ let(:snowplow_gitlab_standard_context) { { namespace: target, property: 'i_package_nuget_user' } }
it_behaves_like 'handling all endpoints'
diff --git a/spec/requests/api/nuget_project_packages_spec.rb b/spec/requests/api/nuget_project_packages_spec.rb
index f608f296295..1e0d35ad451 100644
--- a/spec/requests/api/nuget_project_packages_spec.rb
+++ b/spec/requests/api/nuget_project_packages_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe API::NugetProjectPackages do
+RSpec.describe API::NugetProjectPackages, feature_category: :package_registry do
include_context 'nuget api setup'
using RSpec::Parameterized::TableSyntax
@@ -9,38 +9,62 @@ RSpec.describe API::NugetProjectPackages do
let_it_be_with_reload(:project) { create(:project, :public) }
let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) }
let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) }
+ let_it_be(:package_name) { 'Dummy.Package' }
let(:target) { project }
let(:target_type) { 'projects' }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_nuget_user' } }
- describe 'GET /api/v4/projects/:id/packages/nuget' do
- it_behaves_like 'handling nuget service requests' do
- let(:url) { "/projects/#{target.id}/packages/nuget/index.json" }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
+ shared_examples 'accept get request on private project with access to package registry for everyone' do
+ subject { get api(url) }
+
+ before do
+ update_visibility_to(Gitlab::VisibilityLevel::PRIVATE)
+ project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC)
end
+
+ it_behaves_like 'returning response status', :ok
+ end
+
+ describe 'GET /api/v4/projects/:id/packages/nuget' do
+ let(:url) { "/projects/#{target.id}/packages/nuget/index.json" }
+
+ it_behaves_like 'handling nuget service requests'
+
+ it_behaves_like 'accept get request on private project with access to package registry for everyone'
end
describe 'GET /api/v4/projects/:id/packages/nuget/metadata/*package_name/index' do
- it_behaves_like 'handling nuget metadata requests with package name' do
- let(:url) { "/projects/#{target.id}/packages/nuget/metadata/#{package_name}/index.json" }
+ let(:url) { "/projects/#{target.id}/packages/nuget/metadata/#{package_name}/index.json" }
+
+ it_behaves_like 'handling nuget metadata requests with package name'
+
+ it_behaves_like 'accept get request on private project with access to package registry for everyone' do
+ let_it_be(:packages) { create_list(:nuget_package, 5, :with_metadatum, name: package_name, project: project) }
end
end
describe 'GET /api/v4/projects/:id/packages/nuget/metadata/*package_name/*package_version' do
- it_behaves_like 'handling nuget metadata requests with package name and package version' do
- let(:url) { "/projects/#{target.id}/packages/nuget/metadata/#{package_name}/#{package.version}.json" }
+ let(:url) { "/projects/#{target.id}/packages/nuget/metadata/#{package_name}/#{package.version}.json" }
+
+ it_behaves_like 'handling nuget metadata requests with package name and package version'
+
+ it_behaves_like 'accept get request on private project with access to package registry for everyone' do
+ let_it_be(:package) { create(:nuget_package, :with_metadatum, name: package_name, project: project) }
end
end
describe 'GET /api/v4/projects/:id/packages/nuget/query' do
- it_behaves_like 'handling nuget search requests' do
- let(:url) { "/projects/#{target.id}/packages/nuget/query?#{query_parameters.to_query}" }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
+ let(:url) { "/projects/#{target.id}/packages/nuget/query?#{query_parameters.to_query}" }
+
+ it_behaves_like 'handling nuget search requests'
+
+ it_behaves_like 'accept get request on private project with access to package registry for everyone' do
+ let_it_be(:query_parameters) { { q: 'query', take: 5, skip: 0, prerelease: true } }
end
end
describe 'GET /api/v4/projects/:id/packages/nuget/download/*package_name/index' do
- let_it_be(:package_name) { 'Dummy.Package' }
let_it_be(:packages) { create_list(:nuget_package, 5, name: package_name, project: project) }
let(:url) { "/projects/#{target.id}/packages/nuget/download/#{package_name}/index.json" }
@@ -88,10 +112,11 @@ RSpec.describe API::NugetProjectPackages do
it_behaves_like 'rejects nuget access with unknown target id'
it_behaves_like 'rejects nuget access with invalid target id'
+
+ it_behaves_like 'accept get request on private project with access to package registry for everyone'
end
describe 'GET /api/v4/projects/:id/packages/nuget/download/*package_name/*package_version/*package_filename' do
- let_it_be(:package_name) { 'Dummy.Package' }
let_it_be(:package) { create(:nuget_package, :with_symbol_package, project: project, name: package_name) }
let(:format) { 'nupkg' }
@@ -124,7 +149,6 @@ RSpec.describe API::NugetProjectPackages do
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
subject { get api(url), headers: headers }
@@ -134,6 +158,8 @@ RSpec.describe API::NugetProjectPackages do
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
end
+
+ it_behaves_like 'accept get request on private project with access to package registry for everyone'
end
it_behaves_like 'deploy token for package GET requests' do
diff --git a/spec/requests/api/oauth_tokens_spec.rb b/spec/requests/api/oauth_tokens_spec.rb
index f07dcfcccd6..b29f1e9e661 100644
--- a/spec/requests/api/oauth_tokens_spec.rb
+++ b/spec/requests/api/oauth_tokens_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'OAuth tokens' do
+RSpec.describe 'OAuth tokens', feature_category: :authentication_and_authorization do
include HttpBasicAuthHelpers
context 'Resource Owner Password Credentials' do
@@ -85,8 +85,6 @@ RSpec.describe 'OAuth tokens' do
context 'with invalid credentials' do
it 'does not create an access token' do
- pending 'Enable this example after https://github.com/doorkeeper-gem/doorkeeper/pull/1488 is merged and released'
-
user = create(:user)
request_oauth_token(user, basic_auth_header(client.uid, 'invalid secret'))
diff --git a/spec/requests/api/package_files_spec.rb b/spec/requests/api/package_files_spec.rb
index 01c7ef1476f..f47dca387ef 100644
--- a/spec/requests/api/package_files_spec.rb
+++ b/spec/requests/api/package_files_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::PackageFiles do
+RSpec.describe API::PackageFiles, feature_category: :package_registry do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:package) { create(:maven_package, project: project) }
diff --git a/spec/requests/api/pages/internal_access_spec.rb b/spec/requests/api/pages/internal_access_spec.rb
index 4ac47f17b7e..fdc25ecdcd3 100644
--- a/spec/requests/api/pages/internal_access_spec.rb
+++ b/spec/requests/api/pages/internal_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Internal Project Pages Access" do
+RSpec.describe "Internal Project Pages Access", feature_category: :pages do
using RSpec::Parameterized::TableSyntax
include AccessMatchers
diff --git a/spec/requests/api/pages/pages_spec.rb b/spec/requests/api/pages/pages_spec.rb
index 4a94bf90205..c426f2a433c 100644
--- a/spec/requests/api/pages/pages_spec.rb
+++ b/spec/requests/api/pages/pages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Pages do
+RSpec.describe API::Pages, feature_category: :pages do
let_it_be(:project) { create(:project, path: 'my.project', pages_https_only: false) }
let_it_be(:admin) { create(:admin) }
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/pages/private_access_spec.rb b/spec/requests/api/pages/private_access_spec.rb
index c1c0e406508..5cc1b8f9a69 100644
--- a/spec/requests/api/pages/private_access_spec.rb
+++ b/spec/requests/api/pages/private_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Private Project Pages Access" do
+RSpec.describe "Private Project Pages Access", feature_category: :pages do
using RSpec::Parameterized::TableSyntax
include AccessMatchers
diff --git a/spec/requests/api/pages/public_access_spec.rb b/spec/requests/api/pages/public_access_spec.rb
index c45b3a4c55e..1137f91f4b0 100644
--- a/spec/requests/api/pages/public_access_spec.rb
+++ b/spec/requests/api/pages/public_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Public Project Pages Access" do
+RSpec.describe "Public Project Pages Access", feature_category: :pages do
using RSpec::Parameterized::TableSyntax
include AccessMatchers
diff --git a/spec/requests/api/pages_domains_spec.rb b/spec/requests/api/pages_domains_spec.rb
index 8ef4e899193..65fcf9e006a 100644
--- a/spec/requests/api/pages_domains_spec.rb
+++ b/spec/requests/api/pages_domains_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::PagesDomains do
+RSpec.describe API::PagesDomains, feature_category: :pages do
let_it_be(:project) { create(:project, path: 'my.project', pages_https_only: false) }
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
diff --git a/spec/requests/api/performance_bar_spec.rb b/spec/requests/api/performance_bar_spec.rb
index a4dbb3d17b8..9fbe34914c5 100644
--- a/spec/requests/api/performance_bar_spec.rb
+++ b/spec/requests/api/performance_bar_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Performance Bar for API requests', :request_store, :clean_gitlab_redis_cache do
+RSpec.describe 'Performance Bar for API requests', :request_store, :clean_gitlab_redis_cache,
+feature_category: :metrics do
context 'with user that has access to the performance bar' do
let_it_be(:admin) { create(:admin) }
diff --git a/spec/requests/api/personal_access_tokens/self_information_spec.rb b/spec/requests/api/personal_access_tokens/self_information_spec.rb
index bdfac3ed14f..4a3c0ad8904 100644
--- a/spec/requests/api/personal_access_tokens/self_information_spec.rb
+++ b/spec/requests/api/personal_access_tokens/self_information_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::PersonalAccessTokens::SelfInformation do
+RSpec.describe API::PersonalAccessTokens::SelfInformation, feature_category: :authentication_and_authorization do
let(:path) { '/personal_access_tokens/self' }
let(:token) { create(:personal_access_token, user: current_user) }
diff --git a/spec/requests/api/personal_access_tokens_spec.rb b/spec/requests/api/personal_access_tokens_spec.rb
index 1fa2ad6ebfa..32adc7ebd61 100644
--- a/spec/requests/api/personal_access_tokens_spec.rb
+++ b/spec/requests/api/personal_access_tokens_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::PersonalAccessTokens do
+RSpec.describe API::PersonalAccessTokens, feature_category: :authentication_and_authorization do
let_it_be(:path) { '/personal_access_tokens' }
describe 'GET /personal_access_tokens' do
diff --git a/spec/requests/api/project_attributes.yml b/spec/requests/api/project_attributes.yml
index 2ff4cd72f1e..cc399d25429 100644
--- a/spec/requests/api/project_attributes.yml
+++ b/spec/requests/api/project_attributes.yml
@@ -43,6 +43,7 @@ itself: # project
- storage_version
- topic_list
- updated_at
+ - mirror_branch_regex
remapped_attributes:
avatar: avatar_url
build_allow_git_fetch: build_git_strategy
@@ -124,10 +125,6 @@ project_feature:
- created_at
- metrics_dashboard_access_level
- package_registry_access_level
- - monitor_access_level
- - infrastructure_access_level
- - feature_flags_access_level
- - environments_access_level
- project_id
- updated_at
computed_attributes:
@@ -163,6 +160,7 @@ project_setting:
- suggested_reviewers_enabled
- jitsu_key
- mirror_branch_regex
+ - allow_pipeline_trigger_approve_deployment
build_service_desk_setting: # service_desk_setting
unexposed_attributes:
diff --git a/spec/requests/api/project_clusters_spec.rb b/spec/requests/api/project_clusters_spec.rb
index 4c7da78f0d4..895192252da 100644
--- a/spec/requests/api/project_clusters_spec.rb
+++ b/spec/requests/api/project_clusters_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ProjectClusters do
+RSpec.describe API::ProjectClusters, feature_category: :kubernetes_management do
include KubernetesHelpers
let_it_be(:maintainer_user) { create(:user) }
diff --git a/spec/requests/api/project_container_repositories_spec.rb b/spec/requests/api/project_container_repositories_spec.rb
index 52ec06d76a9..a2e1a1c1721 100644
--- a/spec/requests/api/project_container_repositories_spec.rb
+++ b/spec/requests/api/project_container_repositories_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ProjectContainerRepositories do
+RSpec.describe API::ProjectContainerRepositories, feature_category: :package_registry do
include ExclusiveLeaseHelpers
let_it_be(:project) { create(:project, :private) }
@@ -33,7 +33,10 @@ RSpec.describe API::ProjectContainerRepositories do
let(:method) { :get }
let(:params) { {} }
- let(:snowplow_gitlab_standard_context) { { user: api_user, project: project, namespace: project.namespace } }
+ let(:snowplow_gitlab_standard_context) do
+ { user: api_user, project: project, namespace: project.namespace,
+ property: 'i_package_container_user' }
+ end
before_all do
project.add_maintainer(maintainer)
@@ -144,20 +147,6 @@ RSpec.describe API::ProjectContainerRepositories do
expect(response).to have_gitlab_http_status(:accepted)
end
-
- context 'with container_registry_delete_repository_with_cron_worker disabled' do
- before do
- stub_feature_flags(container_registry_delete_repository_with_cron_worker: false)
- end
-
- it 'schedules removal of repository' do
- expect(DeleteContainerRepositoryWorker).to receive(:perform_async)
- .with(maintainer.id, root_repository.id)
- expect { subject }.to change { root_repository.reload.status }.from(nil).to('delete_scheduled')
-
- expect(response).to have_gitlab_http_status(:accepted)
- end
- end
end
end
end
@@ -414,6 +403,9 @@ RSpec.describe API::ProjectContainerRepositories do
context 'for developer', :snowplow do
let(:api_user) { developer }
+ let(:service_ping_context) do
+ [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: 'i_package_container_user').to_h]
+ end
context 'when there are multiple tags' do
before do
@@ -427,7 +419,10 @@ RSpec.describe API::ProjectContainerRepositories do
subject
expect(response).to have_gitlab_http_status(:ok)
- expect_snowplow_event(category: described_class.name, action: 'delete_tag', project: project, user: api_user, namespace: project.namespace)
+ expect_snowplow_event(category: described_class.name, action: 'delete_tag', project: project,
+ user: api_user, namespace: project.namespace.reload,
+ label: 'redis_hll_counters.user_packages.user_packages_total_unique_counts_monthly',
+ property: 'i_package_container_user', context: service_ping_context)
end
end
@@ -443,7 +438,10 @@ RSpec.describe API::ProjectContainerRepositories do
subject
expect(response).to have_gitlab_http_status(:ok)
- expect_snowplow_event(category: described_class.name, action: 'delete_tag', project: project, user: api_user, namespace: project.namespace)
+ expect_snowplow_event(category: described_class.name, action: 'delete_tag', project: project,
+ user: api_user, namespace: project.namespace.reload,
+ label: 'redis_hll_counters.user_packages.user_packages_total_unique_counts_monthly',
+ property: 'i_package_container_user', context: service_ping_context)
end
end
end
diff --git a/spec/requests/api/project_debian_distributions_spec.rb b/spec/requests/api/project_debian_distributions_spec.rb
index 2b993f24046..9807f177c5d 100644
--- a/spec/requests/api/project_debian_distributions_spec.rb
+++ b/spec/requests/api/project_debian_distributions_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe API::ProjectDebianDistributions do
+RSpec.describe API::ProjectDebianDistributions, feature_category: :package_registry do
include HttpBasicAuthHelpers
include WorkhorseHelpers
@@ -23,13 +23,13 @@ RSpec.describe API::ProjectDebianDistributions do
describe 'GET projects/:id/debian_distributions' do
let(:url) { "/projects/#{container.id}/debian_distributions" }
- it_behaves_like 'Debian distributions read endpoint', 'GET', :success, /^\[{.*"codename":"existing-codename\",.*"components":\["existing-component"\],.*"architectures":\["all","existing-arch"\]/
+ it_behaves_like 'Debian distributions read endpoint', 'GET', :success, /^\[{.*"codename":"existing-codename",.*"components":\["existing-component"\],.*"architectures":\["all","existing-arch"\]/
end
describe 'GET projects/:id/debian_distributions/:codename' do
let(:url) { "/projects/#{container.id}/debian_distributions/#{distribution.codename}" }
- it_behaves_like 'Debian distributions read endpoint', 'GET', :success, /^{.*"codename":"existing-codename\",.*"components":\["existing-component"\],.*"architectures":\["all","existing-arch"\]/
+ it_behaves_like 'Debian distributions read endpoint', 'GET', :success, /^{.*"codename":"existing-codename",.*"components":\["existing-component"\],.*"architectures":\["all","existing-arch"\]/
end
describe 'GET projects/:id/debian_distributions/:codename/key.asc' do
@@ -56,7 +56,7 @@ RSpec.describe API::ProjectDebianDistributions do
let(:method) { :delete }
let(:url) { "/projects/#{container.id}/debian_distributions/#{distribution.codename}" }
- it_behaves_like 'Debian distributions maintainer write endpoint', 'DELETE', :success, /^{\"message\":\"202 Accepted\"}$/
+ it_behaves_like 'Debian distributions maintainer write endpoint', 'DELETE', :success, /^{"message":"202 Accepted"}$/
context 'when destroy fails' do
before do
diff --git a/spec/requests/api/project_events_spec.rb b/spec/requests/api/project_events_spec.rb
index f3e592f9796..69d8eb76cf3 100644
--- a/spec/requests/api/project_events_spec.rb
+++ b/spec/requests/api/project_events_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ProjectEvents do
+RSpec.describe API::ProjectEvents, feature_category: :users do
let(:user) { create(:user) }
let(:non_member) { create(:user) }
let(:private_project) { create(:project, :private, creator_id: user.id, namespace: user.namespace) }
diff --git a/spec/requests/api/project_export_spec.rb b/spec/requests/api/project_export_spec.rb
index d74fd82ca09..fdd76c63069 100644
--- a/spec/requests/api/project_export_spec.rb
+++ b/spec/requests/api/project_export_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache do
+RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category: :importers do
let_it_be(:project) { create(:project) }
let_it_be(:project_none) { create(:project) }
let_it_be(:project_started) { create(:project) }
diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb
index 2d925620a91..8e5e9d847ea 100644
--- a/spec/requests/api/project_hooks_spec.rb
+++ b/spec/requests/api/project_hooks_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ProjectHooks, 'ProjectHooks' do
+RSpec.describe API::ProjectHooks, 'ProjectHooks', feature_category: :integrations do
let_it_be(:user) { create(:user) }
let_it_be(:user3) { create(:user) }
let_it_be(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb
index 05fe55b06a1..027c61bb9e1 100644
--- a/spec/requests/api/project_import_spec.rb
+++ b/spec/requests/api/project_import_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ProjectImport, :aggregate_failures do
+RSpec.describe API::ProjectImport, :aggregate_failures, feature_category: :importers do
include WorkhorseHelpers
include AfterNextHelpers
@@ -44,7 +44,7 @@ RSpec.describe API::ProjectImport, :aggregate_failures do
it_behaves_like 'requires authentication'
- it 'executes a limited number of queries' do
+ it 'executes a limited number of queries', :use_clean_rails_redis_caching do
control_count = ActiveRecord::QueryRecorder.new { subject }.count
expect(control_count).to be <= 111
@@ -126,13 +126,31 @@ RSpec.describe API::ProjectImport, :aggregate_failures do
end
end
- it 'schedules an import at the user namespace level' do
- stub_import(user.namespace)
- params[:path] = 'test-import2'
+ context 'when namespace not set' do
+ it 'schedules an import at the user namespace level' do
+ stub_import(user.namespace)
+ params[:path] = 'test-import2'
- subject
+ subject
- expect(response).to have_gitlab_http_status(:created)
+ expect(response).to have_gitlab_http_status(:created)
+ end
+
+ context 'when current user is a bot user' do
+ let(:user) { create(:user, :project_bot) }
+
+ it 'does not schedule an import' do
+ expect_any_instance_of(ProjectImportState).not_to receive(:schedule)
+
+ params[:namespace] = nil
+ params[:path] = 'test-import3'
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eq("Namespace is not valid")
+ end
+ end
end
it 'does not schedule an import for a namespace that does not exist' do
@@ -161,6 +179,20 @@ RSpec.describe API::ProjectImport, :aggregate_failures do
expect(json_response['message']).to eq('404 Namespace Not Found')
end
+ context 'when passed in namespace is a bot user namespace' do
+ let(:user) { create(:user, :project_bot) }
+
+ it 'does not schedule an import' do
+ expect_any_instance_of(ProjectImportState).not_to receive(:schedule)
+ params[:namespace] = user.namespace.full_path
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eq("Namespace is not valid")
+ end
+ end
+
context 'if user uploads no valid file' do
let(:file) { 'README.md' }
diff --git a/spec/requests/api/project_milestones_spec.rb b/spec/requests/api/project_milestones_spec.rb
index 8294ca143d3..9d722e4a445 100644
--- a/spec/requests/api/project_milestones_spec.rb
+++ b/spec/requests/api/project_milestones_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ProjectMilestones do
+RSpec.describe API::ProjectMilestones, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:project) { create(:project, namespace: user.namespace) }
let_it_be(:closed_milestone) { create(:closed_milestone, project: project, title: 'version1', description: 'closed milestone') }
diff --git a/spec/requests/api/project_packages_spec.rb b/spec/requests/api/project_packages_spec.rb
index 00d295b3490..d3adef85f8d 100644
--- a/spec/requests/api/project_packages_spec.rb
+++ b/spec/requests/api/project_packages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ProjectPackages do
+RSpec.describe API::ProjectPackages, feature_category: :package_registry do
let_it_be(:project) { create(:project, :public) }
let(:user) { create(:user) }
@@ -350,6 +350,16 @@ RSpec.describe API::ProjectPackages do
end
end
end
+
+ context 'when package has no default status' do
+ let!(:package1) { create(:npm_package, :error, project: project) }
+
+ it 'returns 404' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
end
describe 'DELETE /projects/:id/packages/:package_id' do
diff --git a/spec/requests/api/project_repository_storage_moves_spec.rb b/spec/requests/api/project_repository_storage_moves_spec.rb
index 5b272121233..96ed3042d00 100644
--- a/spec/requests/api/project_repository_storage_moves_spec.rb
+++ b/spec/requests/api/project_repository_storage_moves_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ProjectRepositoryStorageMoves do
+RSpec.describe API::ProjectRepositoryStorageMoves, feature_category: :gitaly do
it_behaves_like 'repository_storage_moves API', 'projects' do
let_it_be(:container) { create(:project, :repository) }
let_it_be(:storage_move) { create(:project_repository_storage_move, :scheduled, container: container) }
diff --git a/spec/requests/api/project_snapshots_spec.rb b/spec/requests/api/project_snapshots_spec.rb
index bf78ff56206..5d3c596e605 100644
--- a/spec/requests/api/project_snapshots_spec.rb
+++ b/spec/requests/api/project_snapshots_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ProjectSnapshots do
+RSpec.describe API::ProjectSnapshots, feature_category: :source_code_management do
include WorkhorseHelpers
let(:project) { create(:project) }
@@ -21,7 +21,7 @@ RSpec.describe API::ProjectSnapshots do
expect(type).to eq('git-snapshot')
expect(params).to eq(
'GitalyServer' => {
- 'features' => { 'gitaly-feature-foobar' => 'true' },
+ 'call_metadata' => { 'gitaly-feature-foobar' => 'true' },
'address' => Gitlab::GitalyClient.address(repository.project.repository_storage),
'token' => Gitlab::GitalyClient.token(repository.project.repository_storage)
},
diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb
index 1d255f7c1d8..568486deb7f 100644
--- a/spec/requests/api/project_snippets_spec.rb
+++ b/spec/requests/api/project_snippets_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ProjectSnippets do
+RSpec.describe API::ProjectSnippets, feature_category: :source_code_management do
include SnippetHelpers
let_it_be(:project) { create(:project, :public) }
diff --git a/spec/requests/api/project_statistics_spec.rb b/spec/requests/api/project_statistics_spec.rb
index d314af0746a..39ead8cc573 100644
--- a/spec/requests/api/project_statistics_spec.rb
+++ b/spec/requests/api/project_statistics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ProjectStatistics do
+RSpec.describe API::ProjectStatistics, feature_category: :source_code_management do
let_it_be(:reporter) { create(:user) }
let_it_be(:public_project) { create(:project, :public) }
diff --git a/spec/requests/api/project_templates_spec.rb b/spec/requests/api/project_templates_spec.rb
index 87d70a87f42..38d6a05a104 100644
--- a/spec/requests/api/project_templates_spec.rb
+++ b/spec/requests/api/project_templates_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ProjectTemplates do
+RSpec.describe API::ProjectTemplates, feature_category: :source_code_management do
let_it_be(:public_project) { create(:project, :public, :repository, create_templates: :merge_request, path: 'path.with.dot') }
let_it_be(:private_project) { create(:project, :private, :repository, create_templates: :issue) }
let_it_be(:developer) { create(:user) }
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 3831e8e1dfe..6e8168c0ee1 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.shared_examples 'languages and percentages JSON response' do
+RSpec.shared_examples 'languages and percentages JSON response', feature_category: :projects do
let(:expected_languages) { project.repository.languages.to_h { |language| language.values_at(:label, :value) } }
before do
@@ -231,14 +231,16 @@ RSpec.describe API::Projects do
include_examples 'includes container_registry_access_level'
end
- it 'includes releases_access_level', :aggregate_failures do
- project.project_feature.update!(releases_access_level: ProjectFeature::DISABLED)
-
+ it 'includes various project feature fields', :aggregate_failures do
get api('/projects', user)
project_response = json_response.find { |p| p['id'] == project.id }
expect(response).to have_gitlab_http_status(:ok)
- expect(project_response['releases_access_level']).to eq('disabled')
+ expect(project_response['releases_access_level']).to eq('enabled')
+ expect(project_response['environments_access_level']).to eq('enabled')
+ expect(project_response['feature_flags_access_level']).to eq('enabled')
+ expect(project_response['infrastructure_access_level']).to eq('enabled')
+ expect(project_response['monitor_access_level']).to eq('enabled')
end
context 'when some projects are in a group' do
@@ -1192,6 +1194,10 @@ RSpec.describe API::Projects do
attrs[:container_registry_access_level] = 'private'
attrs[:security_and_compliance_access_level] = 'private'
attrs[:releases_access_level] = 'disabled'
+ attrs[:environments_access_level] = 'disabled'
+ attrs[:feature_flags_access_level] = 'disabled'
+ attrs[:infrastructure_access_level] = 'disabled'
+ attrs[:monitor_access_level] = 'disabled'
end
post api('/projects', user), params: project
@@ -1201,7 +1207,8 @@ RSpec.describe API::Projects do
project.each_pair do |k, v|
next if %i[
has_external_issue_tracker has_external_wiki issues_enabled merge_requests_enabled wiki_enabled storage_version
- container_registry_access_level releases_access_level
+ container_registry_access_level releases_access_level environments_access_level feature_flags_access_level
+ infrastructure_access_level monitor_access_level
].include?(k)
expect(json_response[k.to_s]).to eq(v)
@@ -1217,6 +1224,10 @@ RSpec.describe API::Projects do
expect(project.project_feature.container_registry_access_level).to eq(ProjectFeature::PRIVATE)
expect(project.project_feature.security_and_compliance_access_level).to eq(ProjectFeature::PRIVATE)
expect(project.project_feature.releases_access_level).to eq(ProjectFeature::DISABLED)
+ expect(project.project_feature.environments_access_level).to eq(ProjectFeature::DISABLED)
+ expect(project.project_feature.feature_flags_access_level).to eq(ProjectFeature::DISABLED)
+ expect(project.project_feature.infrastructure_access_level).to eq(ProjectFeature::DISABLED)
+ expect(project.project_feature.monitor_access_level).to eq(ProjectFeature::DISABLED)
end
it 'assigns container_registry_enabled to project', :aggregate_failures do
@@ -2356,6 +2367,10 @@ RSpec.describe API::Projects do
expect(json_response['operations_access_level']).to be_present
expect(json_response['security_and_compliance_access_level']).to be_present
expect(json_response['releases_access_level']).to be_present
+ expect(json_response['environments_access_level']).to be_present
+ expect(json_response['feature_flags_access_level']).to be_present
+ expect(json_response['infrastructure_access_level']).to be_present
+ expect(json_response['monitor_access_level']).to be_present
end
it 'exposes all necessary attributes' do
@@ -2426,6 +2441,10 @@ RSpec.describe API::Projects do
expect(json_response['operations_access_level']).to be_present
expect(json_response['security_and_compliance_access_level']).to be_present
expect(json_response['releases_access_level']).to be_present
+ expect(json_response['environments_access_level']).to be_present
+ expect(json_response['feature_flags_access_level']).to be_present
+ expect(json_response['infrastructure_access_level']).to be_present
+ expect(json_response['monitor_access_level']).to be_present
expect(json_response).to have_key('emails_disabled')
expect(json_response['resolve_outdated_diff_discussions']).to eq(project.resolve_outdated_diff_discussions)
expect(json_response['remove_source_branch_after_merge']).to be_truthy
@@ -3410,12 +3429,14 @@ RSpec.describe API::Projects do
expect(Project.find_by(path: project[:path]).analytics_access_level).to eq(ProjectFeature::PRIVATE)
end
- it 'sets releases_access_level', :aggregate_failures do
- put api("/projects/#{project.id}", user), params: { releases_access_level: 'private' }
+ %i(releases_access_level environments_access_level feature_flags_access_level infrastructure_access_level monitor_access_level).each do |field|
+ it "sets #{field}", :aggregate_failures do
+ put api("/projects/#{project.id}", user), params: { field => 'private' }
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['releases_access_level']).to eq('private')
- expect(Project.find_by(path: project[:path]).releases_access_level).to eq(ProjectFeature::PRIVATE)
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response[field.to_s]).to eq('private')
+ expect(Project.find_by(path: project[:path]).public_send(field)).to eq(ProjectFeature::PRIVATE)
+ end
end
it 'returns 400 when nothing sent' do
@@ -4687,6 +4708,7 @@ RSpec.describe API::Projects do
end
end
end
+
describe 'PUT /projects/:id/transfer' do
context 'when authenticated as owner' do
let(:group) { create :group }
diff --git a/spec/requests/api/protected_branches_spec.rb b/spec/requests/api/protected_branches_spec.rb
index b46859a0e70..8e8a25a8dc2 100644
--- a/spec/requests/api/protected_branches_spec.rb
+++ b/spec/requests/api/protected_branches_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ProtectedBranches do
+RSpec.describe API::ProtectedBranches, feature_category: :source_code_management do
let_it_be_with_reload(:project) { create(:project, :repository) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:guest) { create(:user) }
diff --git a/spec/requests/api/protected_tags_spec.rb b/spec/requests/api/protected_tags_spec.rb
index f1db39ac204..5b128d4ec9e 100644
--- a/spec/requests/api/protected_tags_spec.rb
+++ b/spec/requests/api/protected_tags_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ProtectedTags do
+RSpec.describe API::ProtectedTags, feature_category: :source_code_management do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:project2) { create(:project, path: 'project2', namespace: user.namespace) }
diff --git a/spec/requests/api/pypi_packages_spec.rb b/spec/requests/api/pypi_packages_spec.rb
index 12091158a02..59d93cd48e3 100644
--- a/spec/requests/api/pypi_packages_spec.rb
+++ b/spec/requests/api/pypi_packages_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe API::PypiPackages do
+RSpec.describe API::PypiPackages, feature_category: :package_registry do
include WorkhorseHelpers
include PackagesManagerApiSpecHelpers
include HttpBasicAuthHelpers
@@ -14,6 +14,7 @@ RSpec.describe API::PypiPackages do
let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) }
let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) }
let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_pypi_user' } }
let(:headers) { {} }
@@ -25,7 +26,7 @@ RSpec.describe API::PypiPackages do
describe 'GET /api/v4/groups/:id/-/packages/pypi/simple' do
let(:url) { "/groups/#{group.id}/-/packages/pypi/simple" }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_pypi_user' } }
it_behaves_like 'pypi simple index API endpoint'
it_behaves_like 'rejects PyPI access with unknown group id'
@@ -63,7 +64,7 @@ RSpec.describe API::PypiPackages do
describe 'GET /api/v4/projects/:id/packages/pypi/simple' do
let(:package_name) { package.name }
let(:url) { "/projects/#{project.id}/packages/pypi/simple" }
- let(:snowplow_gitlab_standard_context) { { project: nil, namespace: group } }
+ let(:snowplow_gitlab_standard_context) { { project: nil, namespace: group, property: 'i_package_pypi_user' } }
it_behaves_like 'pypi simple index API endpoint'
it_behaves_like 'rejects PyPI access with unknown project id'
@@ -81,13 +82,13 @@ RSpec.describe API::PypiPackages do
context 'simple package API endpoint' do
let_it_be(:package) { create(:pypi_package, project: project) }
+ let(:snowplow_gitlab_standard_context) { { project: nil, namespace: group, property: 'i_package_pypi_user' } }
subject { get api(url), headers: headers }
describe 'GET /api/v4/groups/:id/-/packages/pypi/simple/:package_name' do
let(:package_name) { package.name }
let(:url) { "/groups/#{group.id}/-/packages/pypi/simple/#{package_name}" }
- let(:snowplow_gitlab_standard_context) { { project: nil, namespace: group } }
it_behaves_like 'pypi simple API endpoint'
it_behaves_like 'rejects PyPI access with unknown group id'
@@ -125,7 +126,7 @@ RSpec.describe API::PypiPackages do
describe 'GET /api/v4/projects/:id/packages/pypi/simple/:package_name' do
let(:package_name) { package.name }
let(:url) { "/projects/#{project.id}/packages/pypi/simple/#{package_name}" }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_pypi_user' } }
it_behaves_like 'pypi simple API endpoint'
it_behaves_like 'rejects PyPI access with unknown project id'
@@ -202,7 +203,7 @@ RSpec.describe API::PypiPackages do
let(:base_params) { { requires_python: requires_python, version: '1.0.0', name: 'sample-project', sha256_digest: '1' * 64, md5_digest: '1' * 32 } }
let(:params) { base_params.merge(content: temp_file(file_name)) }
let(:send_rewritten_field) { true }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user, property: 'i_package_pypi_user' } }
subject do
workhorse_finalize(
@@ -366,7 +367,6 @@ RSpec.describe API::PypiPackages do
describe 'GET /api/v4/groups/:id/-/packages/pypi/files/:sha256/*file_identifier' do
let(:url) { "/groups/#{group.id}/-/packages/pypi/files/#{package.package_files.first.file_sha256}/#{package_name}-1.0.0.tar.gz" }
- let(:snowplow_gitlab_standard_context) { {} }
it_behaves_like 'pypi file download endpoint'
it_behaves_like 'rejects PyPI access with unknown group id'
@@ -375,7 +375,6 @@ RSpec.describe API::PypiPackages do
describe 'GET /api/v4/projects/:id/packages/pypi/files/:sha256/*file_identifier' do
let(:url) { "/projects/#{project.id}/packages/pypi/files/#{package.package_files.first.file_sha256}/#{package_name}-1.0.0.tar.gz" }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
it_behaves_like 'pypi file download endpoint'
it_behaves_like 'rejects PyPI access with unknown project id'
diff --git a/spec/requests/api/release/links_spec.rb b/spec/requests/api/release/links_spec.rb
index 38166c5ce97..6036960c43c 100644
--- a/spec/requests/api/release/links_spec.rb
+++ b/spec/requests/api/release/links_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Release::Links do
+RSpec.describe API::Release::Links, feature_category: :release_orchestration do
let(:project) { create(:project, :repository, :private) }
let(:maintainer) { create(:user) }
let(:developer) { create(:user) }
diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb
index 754b77af60e..a1aff9a6b1c 100644
--- a/spec/requests/api/releases_spec.rb
+++ b/spec/requests/api/releases_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Releases do
+RSpec.describe API::Releases, feature_category: :release_orchestration do
let(:project) { create(:project, :repository, :private) }
let(:maintainer) { create(:user) }
let(:reporter) { create(:user) }
diff --git a/spec/requests/api/remote_mirrors_spec.rb b/spec/requests/api/remote_mirrors_spec.rb
index 338647224e0..3da1760e319 100644
--- a/spec/requests/api/remote_mirrors_spec.rb
+++ b/spec/requests/api/remote_mirrors_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::RemoteMirrors do
+RSpec.describe API::RemoteMirrors, feature_category: :source_code_management do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository, :remote_mirror) }
let_it_be(:developer) { create(:user) { |u| project.add_developer(u) } }
@@ -90,7 +90,9 @@ RSpec.describe API::RemoteMirrors do
}
expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']['url']).to eq(["is blocked: Only allowed schemes are ssh, git, http, https"])
+ expect(json_response['message']['url']).to match_array(
+ ["is blocked: Only allowed schemes are http, https, ssh, git"]
+ )
end
end
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 3c22f918af5..393ada1da4f 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require 'mime/types'
-RSpec.describe API::Repositories do
+RSpec.describe API::Repositories, feature_category: :source_code_management do
include RepoHelpers
include WorkhorseHelpers
include ProjectForksHelper
@@ -300,7 +300,7 @@ RSpec.describe API::Repositories do
type, params = workhorse_send_data
expect(type).to eq('git-archive')
- expect(params['ArchivePath']).to match(/#{project.path}\-[^\.]+\.tar.gz/)
+ expect(params['ArchivePath']).to match(/#{project.path}-[^.]+\.tar.gz/)
expect(response.parsed_body).to be_empty
end
@@ -312,7 +312,7 @@ RSpec.describe API::Repositories do
type, params = workhorse_send_data
expect(type).to eq('git-archive')
- expect(params['ArchivePath']).to match(/#{project.path}\-[^\.]+\.zip/)
+ expect(params['ArchivePath']).to match(/#{project.path}-[^.]+\.zip/)
end
it 'returns the repository archive archive.tar.bz2' do
@@ -323,7 +323,7 @@ RSpec.describe API::Repositories do
type, params = workhorse_send_data
expect(type).to eq('git-archive')
- expect(params['ArchivePath']).to match(/#{project.path}\-[^\.]+\.tar.bz2/)
+ expect(params['ArchivePath']).to match(/#{project.path}-[^.]+\.tar.bz2/)
end
context 'when sha does not exist' do
@@ -342,7 +342,7 @@ RSpec.describe API::Repositories do
type, params = workhorse_send_data
expect(type).to eq('git-archive')
- expect(params['ArchivePath']).to match(/#{project.path}\-[^\.]+\-#{path}\.tar.gz/)
+ expect(params['ArchivePath']).to match(/#{project.path}-[^.]+-#{path}\.tar.gz/)
end
it 'rate limits user when thresholds hit' do
diff --git a/spec/requests/api/resource_access_tokens_spec.rb b/spec/requests/api/resource_access_tokens_spec.rb
index 24efac3128d..6a89e9a56df 100644
--- a/spec/requests/api/resource_access_tokens_spec.rb
+++ b/spec/requests/api/resource_access_tokens_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe API::ResourceAccessTokens do
+RSpec.describe API::ResourceAccessTokens, feature_category: :authentication_and_authorization do
let_it_be(:user) { create(:user) }
let_it_be(:user_non_priviledged) { create(:user) }
diff --git a/spec/requests/api/resource_label_events_spec.rb b/spec/requests/api/resource_label_events_spec.rb
index a4a70d89812..1adffea17b7 100644
--- a/spec/requests/api/resource_label_events_spec.rb
+++ b/spec/requests/api/resource_label_events_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ResourceLabelEvents do
+RSpec.describe API::ResourceLabelEvents, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, :public, namespace: user.namespace) }
let_it_be(:label) { create(:label, project: project) }
diff --git a/spec/requests/api/resource_milestone_events_spec.rb b/spec/requests/api/resource_milestone_events_spec.rb
index 5c81c2180d7..fe991533c85 100644
--- a/spec/requests/api/resource_milestone_events_spec.rb
+++ b/spec/requests/api/resource_milestone_events_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ResourceMilestoneEvents do
+RSpec.describe API::ResourceMilestoneEvents, feature_category: :team_planning do
let!(:user) { create(:user) }
let!(:project) { create(:project, :public, namespace: user.namespace) }
let!(:milestone) { create(:milestone, project: project) }
diff --git a/spec/requests/api/rpm_project_packages_spec.rb b/spec/requests/api/rpm_project_packages_spec.rb
index 68511795c94..515970f86a1 100644
--- a/spec/requests/api/rpm_project_packages_spec.rb
+++ b/spec/requests/api/rpm_project_packages_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe API::RpmProjectPackages do
+RSpec.describe API::RpmProjectPackages, feature_category: :package_registry do
include HttpBasicAuthHelpers
include WorkhorseHelpers
@@ -136,7 +136,7 @@ RSpec.describe API::RpmProjectPackages do
end
describe 'GET /api/v4/projects/:id/packages/rpm/:package_file_id/:filename' do
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: group } }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: group, property: 'i_package_rpm_user' } }
let(:url) { "/projects/#{project.id}/packages/rpm/#{package_file_id}/#{package_name}" }
subject { get api(url), headers: headers }
@@ -148,7 +148,10 @@ RSpec.describe API::RpmProjectPackages do
end
describe 'POST /api/v4/projects/:project_id/packages/rpm' do
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: group, user: user } }
+ let(:snowplow_gitlab_standard_context) do
+ { project: project, namespace: group, user: user, property: 'i_package_rpm_user' }
+ end
+
let(:url) { "/projects/#{project.id}/packages/rpm" }
let(:file_upload) { fixture_file_upload('spec/fixtures/packages/rpm/hello-0.0.1-1.fc29.x86_64.rpm') }
@@ -213,6 +216,19 @@ RSpec.describe API::RpmProjectPackages do
expect(response.body).to match(/File is too large/)
end
end
+
+ context 'when filelists.xml file size too large' do
+ before do
+ create(:rpm_repository_file, :filelists, size: 21.megabytes, project: project)
+ end
+
+ it 'returns an error' do
+ upload_file(params: { file: file_upload }, request_headers: headers)
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(response.body).to match(/Repository packages limit exceeded/)
+ end
+ end
end
def upload_file(params: {}, request_headers: headers)
diff --git a/spec/requests/api/rubygem_packages_spec.rb b/spec/requests/api/rubygem_packages_spec.rb
index a7d461781b8..6f048fa57a8 100644
--- a/spec/requests/api/rubygem_packages_spec.rb
+++ b/spec/requests/api/rubygem_packages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::RubygemPackages do
+RSpec.describe API::RubygemPackages, feature_category: :package_registry do
include PackagesManagerApiSpecHelpers
include WorkhorseHelpers
using RSpec::Parameterized::TableSyntax
@@ -15,7 +15,7 @@ RSpec.describe API::RubygemPackages do
let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) }
let_it_be(:headers) { {} }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user, property: 'i_package_rubygems_user' } }
let(:tokens) do
{
@@ -164,7 +164,7 @@ RSpec.describe API::RubygemPackages do
with_them do
let(:token) { valid_token ? tokens[token_type] : 'invalid-token123' }
let(:headers) { user_role == :anonymous ? {} : { 'HTTP_AUTHORIZATION' => token } }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_rubygems_user' } }
before do
project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility.to_s))
@@ -323,7 +323,7 @@ RSpec.describe API::RubygemPackages do
let(:token) { valid_token ? tokens[token_type] : 'invalid-token123' }
let(:user_headers) { user_role == :anonymous ? {} : { 'HTTP_AUTHORIZATION' => token } }
let(:headers) { user_headers.merge(workhorse_headers) }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: snowplow_user } }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: snowplow_user, property: 'i_package_rubygems_user' } }
let(:snowplow_user) do
case token_type
when :deploy_token
diff --git a/spec/requests/api/search_spec.rb b/spec/requests/api/search_spec.rb
index 60acf6b71dd..430d3b7d187 100644
--- a/spec/requests/api/search_spec.rb
+++ b/spec/requests/api/search_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Search do
+RSpec.describe API::Search, feature_category: :global_search do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project, reload: true) { create(:project, :wiki_repo, :public, name: 'awesome project', group: group) }
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 3a9b2d02af5..e93ef52ef03 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
+RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, feature_category: :not_owned do
let(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
@@ -25,6 +25,7 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
expect(json_response['secret_detection_token_revocation_url']).to be_nil
expect(json_response['secret_detection_revocation_token_types_url']).to be_nil
expect(json_response['sourcegraph_public_only']).to be_truthy
+ expect(json_response['default_preferred_language']).to be_a String
expect(json_response['default_project_visibility']).to be_a String
expect(json_response['default_snippet_visibility']).to be_a String
expect(json_response['default_group_visibility']).to be_a String
@@ -55,12 +56,15 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
expect(json_response['group_runner_token_expiration_interval']).to be_nil
expect(json_response['project_runner_token_expiration_interval']).to be_nil
expect(json_response['max_export_size']).to eq(0)
+ expect(json_response['max_terraform_state_size_bytes']).to eq(0)
expect(json_response['pipeline_limit_per_project_user_sha']).to eq(0)
expect(json_response['delete_inactive_projects']).to be(false)
expect(json_response['inactive_projects_delete_after_months']).to eq(2)
expect(json_response['inactive_projects_min_size_mb']).to eq(0)
expect(json_response['inactive_projects_send_warning_email_after_months']).to eq(1)
expect(json_response['can_create_group']).to eq(true)
+ expect(json_response['jira_connect_application_key']).to eq(nil)
+ expect(json_response['jira_connect_proxy_url']).to eq(nil)
end
end
@@ -146,6 +150,7 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
mailgun_events_enabled: true,
mailgun_signing_key: 'MAILGUN_SIGNING_KEY',
max_export_size: 6,
+ max_terraform_state_size_bytes: 1_000,
disabled_oauth_sign_in_sources: 'unknown',
import_sources: 'github,bitbucket',
wiki_page_max_content_bytes: 12345,
@@ -158,7 +163,10 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
inactive_projects_delete_after_months: 24,
inactive_projects_min_size_mb: 10,
inactive_projects_send_warning_email_after_months: 12,
- can_create_group: false
+ can_create_group: false,
+ jira_connect_application_key: '123',
+ jira_connect_proxy_url: 'http://example.com',
+ bulk_import_enabled: false
}
expect(response).to have_gitlab_http_status(:ok)
@@ -207,6 +215,7 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
expect(json_response['mailgun_events_enabled']).to be(true)
expect(json_response['mailgun_signing_key']).to eq('MAILGUN_SIGNING_KEY')
expect(json_response['max_export_size']).to eq(6)
+ expect(json_response['max_terraform_state_size_bytes']).to eq(1_000)
expect(json_response['disabled_oauth_sign_in_sources']).to eq([])
expect(json_response['import_sources']).to match_array(%w(github bitbucket))
expect(json_response['wiki_page_max_content_bytes']).to eq(12345)
@@ -220,6 +229,9 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
expect(json_response['inactive_projects_min_size_mb']).to eq(10)
expect(json_response['inactive_projects_send_warning_email_after_months']).to eq(12)
expect(json_response['can_create_group']).to eq(false)
+ expect(json_response['jira_connect_application_key']).to eq('123')
+ expect(json_response['jira_connect_proxy_url']).to eq('http://example.com')
+ expect(json_response['bulk_import_enabled']).to be(false)
end
end
diff --git a/spec/requests/api/sidekiq_metrics_spec.rb b/spec/requests/api/sidekiq_metrics_spec.rb
index 302d824e650..1085df97cc7 100644
--- a/spec/requests/api/sidekiq_metrics_spec.rb
+++ b/spec/requests/api/sidekiq_metrics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::SidekiqMetrics do
+RSpec.describe API::SidekiqMetrics, feature_category: :not_owned do
let(:admin) { create(:user, :admin) }
describe 'GET sidekiq/*' do
diff --git a/spec/requests/api/snippet_repository_storage_moves_spec.rb b/spec/requests/api/snippet_repository_storage_moves_spec.rb
index 40d01500ac1..6081531aee9 100644
--- a/spec/requests/api/snippet_repository_storage_moves_spec.rb
+++ b/spec/requests/api/snippet_repository_storage_moves_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::SnippetRepositoryStorageMoves do
+RSpec.describe API::SnippetRepositoryStorageMoves, feature_category: :gitaly do
it_behaves_like 'repository_storage_moves API', 'snippets' do
let_it_be(:container) { create(:snippet, :repository).tap { |snippet| snippet.create_repository } }
let_it_be(:storage_move) { create(:snippet_repository_storage_move, :scheduled, container: container) }
diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb
index 9408d1cc248..dd0da0cb887 100644
--- a/spec/requests/api/snippets_spec.rb
+++ b/spec/requests/api/snippets_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Snippets, factory_default: :keep do
+RSpec.describe API::Snippets, factory_default: :keep, feature_category: :source_code_management do
include SnippetHelpers
let_it_be(:admin) { create(:user, :admin) }
diff --git a/spec/requests/api/statistics_spec.rb b/spec/requests/api/statistics_spec.rb
index baffb2792e9..85fed48a077 100644
--- a/spec/requests/api/statistics_spec.rb
+++ b/spec/requests/api/statistics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Statistics, 'Statistics' do
+RSpec.describe API::Statistics, 'Statistics', feature_category: :devops_reports do
include ProjectForksHelper
tables_to_analyze = %w[
projects
diff --git a/spec/requests/api/submodules_spec.rb b/spec/requests/api/submodules_spec.rb
index 9840476ca27..7b041ab7c4b 100644
--- a/spec/requests/api/submodules_spec.rb
+++ b/spec/requests/api/submodules_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Submodules do
+RSpec.describe API::Submodules, feature_category: :source_code_management do
let(:user) { create(:user) }
let!(:project) { create(:project, :repository, namespace: user.namespace) }
let(:guest) { create(:user) { |u| project.add_guest(u) } }
diff --git a/spec/requests/api/suggestions_spec.rb b/spec/requests/api/suggestions_spec.rb
index dfc5d169af6..93b2435c601 100644
--- a/spec/requests/api/suggestions_spec.rb
+++ b/spec/requests/api/suggestions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Suggestions do
+RSpec.describe API::Suggestions, feature_category: :code_review do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb
index 0f1dbea2e73..51edf4b3b3e 100644
--- a/spec/requests/api/system_hooks_spec.rb
+++ b/spec/requests/api/system_hooks_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::SystemHooks do
+RSpec.describe API::SystemHooks, feature_category: :integrations do
let_it_be(:non_admin) { create(:user) }
let_it_be(:admin) { create(:admin) }
let_it_be_with_refind(:hook) { create(:system_hook, url: "http://example.com") }
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
index 3f2ca2a0938..b02c7135b7b 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Tags do
+RSpec.describe API::Tags, feature_category: :source_code_management do
let(:user) { create(:user) }
let(:guest) { create(:user).tap { |u| project.add_guest(u) } }
let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
@@ -479,4 +479,60 @@ RSpec.describe API::Tags do
end
end
end
+
+ describe 'GET /projects/:id/repository/tags/:tag_name/signature' do
+ let_it_be(:project) { create(:project, :repository, :public) }
+ let(:project_id) { project.id }
+ let(:route) { "/projects/#{project_id}/repository/tags/#{tag_name}/signature" }
+
+ context 'when tag does not exist' do
+ let(:tag_name) { 'unknown' }
+
+ it_behaves_like '404 response' do
+ let(:request) { get api(route, current_user) }
+ let(:message) { '404 Tag Not Found' }
+ end
+ end
+
+ context 'unsigned tag' do
+ let(:tag_name) { 'v1.1.0' }
+
+ it_behaves_like '404 response' do
+ let(:request) { get api(route, current_user) }
+ let(:message) { '404 Signature Not Found' }
+ end
+ end
+
+ context 'x509 signed tag' do
+ let(:tag_name) { 'v1.1.1' }
+ let(:tag) { project.repository.find_tag(tag_name) }
+ let(:signature) { tag.signature }
+ let(:x509_certificate) { signature.x509_certificate }
+ let(:x509_issuer) { x509_certificate.x509_issuer }
+
+ it 'returns correct JSON' do
+ get api(route, current_user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to eq(
+ 'signature_type' => 'X509',
+ 'verification_status' => signature.verification_status.to_s,
+ 'x509_certificate' => {
+ 'id' => x509_certificate.id,
+ 'subject' => x509_certificate.subject,
+ 'subject_key_identifier' => x509_certificate.subject_key_identifier,
+ 'email' => x509_certificate.email,
+ 'serial_number' => x509_certificate.serial_number,
+ 'certificate_status' => x509_certificate.certificate_status,
+ 'x509_issuer' => {
+ 'id' => x509_issuer.id,
+ 'subject' => x509_issuer.subject,
+ 'subject_key_identifier' => x509_issuer.subject_key_identifier,
+ 'crl_url' => x509_issuer.crl_url
+ }
+ }
+ )
+ end
+ end
+ end
end
diff --git a/spec/requests/api/task_completion_status_spec.rb b/spec/requests/api/task_completion_status_spec.rb
index 97ce858ba12..c46d6954da3 100644
--- a/spec/requests/api/task_completion_status_spec.rb
+++ b/spec/requests/api/task_completion_status_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'task completion status response' do
+RSpec.describe 'task completion status response', features: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:project) do
create(:project, :public, creator_id: user.id, namespace: user.namespace)
@@ -10,44 +10,44 @@ RSpec.describe 'task completion status response' do
shared_examples 'taskable completion status provider' do |path|
samples = [
- {
- description: '',
- expected_count: 0,
- expected_completed_count: 0
- },
- {
- description: 'Lorem ipsum',
- expected_count: 0,
- expected_completed_count: 0
- },
- {
- description: %{- [ ] task 1
+ {
+ description: '',
+ expected_count: 0,
+ expected_completed_count: 0
+ },
+ {
+ description: 'Lorem ipsum',
+ expected_count: 0,
+ expected_completed_count: 0
+ },
+ {
+ description: %{- [ ] task 1
- [x] task 2 },
- expected_count: 2,
- expected_completed_count: 1
- },
- {
- description: %{- [ ] task 1
+ expected_count: 2,
+ expected_completed_count: 1
+ },
+ {
+ description: %{- [ ] task 1
- [ ] task 2 },
- expected_count: 2,
- expected_completed_count: 0
- },
- {
- description: %{- [x] task 1
+ expected_count: 2,
+ expected_completed_count: 0
+ },
+ {
+ description: %{- [x] task 1
- [x] task 2 },
- expected_count: 2,
- expected_completed_count: 2
- },
- {
- description: %{- [ ] task 1},
- expected_count: 1,
- expected_completed_count: 0
- },
- {
- description: %{- [x] task 1},
- expected_count: 1,
- expected_completed_count: 1
- }
+ expected_count: 2,
+ expected_completed_count: 2
+ },
+ {
+ description: %{- [ ] task 1},
+ expected_count: 1,
+ expected_completed_count: 0
+ },
+ {
+ description: %{- [x] task 1},
+ expected_count: 1,
+ expected_completed_count: 1
+ }
]
samples.each do |sample_data|
context "with a description of #{sample_data[:description].inspect}" do
diff --git a/spec/requests/api/templates_spec.rb b/spec/requests/api/templates_spec.rb
index adb37c62dc3..8782c3cba4b 100644
--- a/spec/requests/api/templates_spec.rb
+++ b/spec/requests/api/templates_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Templates do
+RSpec.describe API::Templates, feature_category: :source_code_management do
context 'the Template Entity' do
before do
get api('/templates/gitignores/Ruby')
diff --git a/spec/requests/api/terraform/modules/v1/packages_spec.rb b/spec/requests/api/terraform/modules/v1/packages_spec.rb
index ae61017f5bb..2bd7cb027aa 100644
--- a/spec/requests/api/terraform/modules/v1/packages_spec.rb
+++ b/spec/requests/api/terraform/modules/v1/packages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Terraform::Modules::V1::Packages do
+RSpec.describe API::Terraform::Modules::V1::Packages, feature_category: :package_registry do
include PackagesManagerApiSpecHelpers
include WorkhorseHelpers
using RSpec::Parameterized::TableSyntax
@@ -418,7 +418,8 @@ RSpec.describe API::Terraform::Modules::V1::Packages do
{
project: project,
user: user_role == :anonymous ? nil : user,
- namespace: project.namespace
+ namespace: project.namespace,
+ property: 'i_package_terraform_module_user'
}
end
@@ -583,7 +584,10 @@ RSpec.describe API::Terraform::Modules::V1::Packages do
with_them do
let(:user_headers) { user_role == :anonymous ? {} : { token_header => token } }
let(:headers) { user_headers.merge(workhorse_headers) }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: snowplow_user } }
+ let(:snowplow_gitlab_standard_context) do
+ { project: project, namespace: project.namespace, user: snowplow_user, property: 'i_package_terraform_module_user' }
+ end
+
let(:snowplow_user) do
case token_type
when :deploy_token
diff --git a/spec/requests/api/terraform/state_spec.rb b/spec/requests/api/terraform/state_spec.rb
index 38b08b4e214..fd34345d814 100644
--- a/spec/requests/api/terraform/state_spec.rb
+++ b/spec/requests/api/terraform/state_spec.rb
@@ -2,20 +2,22 @@
require 'spec_helper'
-RSpec.describe API::Terraform::State, :snowplow do
+RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructure_as_code do
include HttpBasicAuthHelpers
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user, developer_projects: [project]) }
let_it_be(:maintainer) { create(:user, maintainer_projects: [project]) }
- let!(:state) { create(:terraform_state, :with_version, project: project) }
-
let(:current_user) { maintainer }
let(:auth_header) { user_basic_auth_header(current_user) }
let(:project_id) { project.id }
- let(:state_name) { state.name }
+
+ let(:state_name) { "some-state" }
let(:state_path) { "/projects/#{project_id}/terraform/state/#{state_name}" }
+ let!(:state) do
+ create(:terraform_state, :with_version, project: project, name: URI.decode_www_form_component(state_name))
+ end
before do
stub_terraform_state_object_storage
@@ -91,15 +93,35 @@ RSpec.describe API::Terraform::State, :snowplow do
end
end
- context 'personal acceess token authentication' do
+ shared_examples 'can access terraform state' do
+ it 'returns terraform state of a project of given state name' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.body).to eq(state.reload.latest_file.read)
+ end
+ end
+
+ context 'personal access token authentication' do
context 'with maintainer permissions' do
let(:current_user) { maintainer }
- it 'returns terraform state belonging to a project of given state name' do
- request
+ where(given_state_name: %w[test-state test.state test%2Ffoo])
+ with_them do
+ it_behaves_like 'can access terraform state' do
+ let(:state_name) { given_state_name }
+ end
+ end
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.body).to eq(state.reload.latest_file.read)
+ context 'allow_dots_on_tf_state_names is disabled, and the state name contains a dot' do
+ let(:state_name) { 'state-name-with-dot' }
+ let(:state_path) { "/projects/#{project_id}/terraform/state/#{state_name}.tfstate" }
+
+ before do
+ stub_feature_flags(allow_dots_on_tf_state_names: false)
+ end
+
+ it_behaves_like 'can access terraform state'
end
context 'for a project that does not exist' do
@@ -112,18 +134,23 @@ RSpec.describe API::Terraform::State, :snowplow do
end
end
+ context 'with invalid state name' do
+ let(:state_name) { 'foo/bar' }
+
+ it 'returns a 404 error' do
+ request
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
it_behaves_like 'cannot access a state that is scheduled for deletion'
end
context 'with developer permissions' do
let(:current_user) { developer }
- it 'returns terraform state belonging to a project of given state name' do
- request
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.body).to eq(state.reload.latest_file.read)
- end
+ it_behaves_like 'can access terraform state'
end
end
@@ -133,12 +160,7 @@ RSpec.describe API::Terraform::State, :snowplow do
context 'with maintainer permissions' do
let(:job) { create(:ci_build, status: :running, project: project, user: maintainer) }
- it 'returns terraform state belonging to a project of given state name' do
- request
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.body).to eq(state.reload.latest_file.read)
- end
+ it_behaves_like 'can access terraform state'
it 'returns unauthorized if the the job is not running' do
job.update!(status: :failed)
@@ -161,12 +183,7 @@ RSpec.describe API::Terraform::State, :snowplow do
context 'with developer permissions' do
let(:job) { create(:ci_build, status: :running, project: project, user: developer) }
- it 'returns terraform state belonging to a project of given state name' do
- request
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.body).to eq(state.reload.latest_file.read)
- end
+ it_behaves_like 'can access terraform state'
end
end
end
@@ -182,11 +199,26 @@ RSpec.describe API::Terraform::State, :snowplow do
context 'with maintainer permissions' do
let(:current_user) { maintainer }
- it 'updates the state' do
- expect { request }.to change { Terraform::State.count }.by(0)
+ where(given_state_name: %w[test-state test.state test%2Ffoo])
+ with_them do
+ let(:state_name) { given_state_name }
- expect(response).to have_gitlab_http_status(:ok)
- expect(Gitlab::Json.parse(response.body)).to be_empty
+ it 'updates the state' do
+ expect { request }.to change { Terraform::State.count }.by(0)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(Gitlab::Json.parse(response.body)).to be_empty
+ end
+ end
+
+ context 'with invalid state name' do
+ let(:state_name) { 'foo/bar' }
+
+ it 'returns a 404 error' do
+ request
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
end
context 'when serial already exists' do
@@ -224,16 +256,39 @@ RSpec.describe API::Terraform::State, :snowplow do
end
context 'when there is no terraform state of a given name' do
- let(:state_name) { 'example2' }
+ let(:non_existing_state_name) { 'non-existing-state' }
+ let(:non_existing_state_path) { "/projects/#{project_id}/terraform/state/#{non_existing_state_name}" }
+
+ subject(:request) { post api(non_existing_state_path), headers: auth_header, as: :json, params: params }
context 'with maintainer permissions' do
let(:current_user) { maintainer }
- it 'creates a new state' do
- expect { request }.to change { Terraform::State.count }.by(1)
+ where(given_state_name: %w[test-state test.state test%2Ffoo])
+ with_them do
+ let(:state_name) { given_state_name }
- expect(response).to have_gitlab_http_status(:ok)
- expect(Gitlab::Json.parse(response.body)).to be_empty
+ it 'creates a new state' do
+ expect { request }.to change { Terraform::State.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(Gitlab::Json.parse(response.body)).to be_empty
+ end
+ end
+
+ context 'allow_dots_on_tf_state_names is disabled, and the state name contains a dot' do
+ let(:non_existing_state_name) { 'state-name-with-dot.tfstate' }
+
+ before do
+ stub_feature_flags(allow_dots_on_tf_state_names: false)
+ end
+
+ it 'strips characters after the dot' do
+ expect { request }.to change { Terraform::State.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(Terraform::State.last.name).to eq('state-name-with-dot')
+ end
end
end
@@ -269,6 +324,48 @@ RSpec.describe API::Terraform::State, :snowplow do
expect(state.reload_latest_version.build).to eq(job)
end
end
+
+ describe 'response depending on the max allowed state size' do
+ let(:current_user) { maintainer }
+
+ before do
+ stub_application_setting(max_terraform_state_size_bytes: max_allowed_state_size)
+
+ request
+ end
+
+ context 'when the max allowed state size is unlimited (set as 0)' do
+ let(:max_allowed_state_size) { 0 }
+
+ it 'returns a success response' do
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'when the max allowed state size is greater than the request state size' do
+ let(:max_allowed_state_size) { params.to_json.size + 1 }
+
+ it 'returns a success response' do
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'when the max allowed state size is equal to the request state size' do
+ let(:max_allowed_state_size) { params.to_json.size }
+
+ it 'returns a success response' do
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'when the max allowed state size is less than the request state size' do
+ let(:max_allowed_state_size) { params.to_json.size - 1 }
+
+ it "returns a 'payload too large' response" do
+ expect(response).to have_gitlab_http_status(:payload_too_large)
+ end
+ end
+ end
end
describe 'DELETE /projects/:id/terraform/state/:name' do
@@ -276,11 +373,8 @@ RSpec.describe API::Terraform::State, :snowplow do
it_behaves_like 'endpoint with unique user tracking'
- context 'with maintainer permissions' do
- let(:current_user) { maintainer }
- let(:deletion_service) { instance_double(Terraform::States::TriggerDestroyService) }
-
- it 'schedules the state for deletion and returns empty body' do
+ shared_examples 'schedules the state for deletion' do
+ it 'returns empty body' do
expect(Terraform::States::TriggerDestroyService).to receive(:new).and_return(deletion_service)
expect(deletion_service).to receive(:execute).once
@@ -289,6 +383,40 @@ RSpec.describe API::Terraform::State, :snowplow do
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Json.parse(response.body)).to be_empty
end
+ end
+
+ context 'with maintainer permissions' do
+ let(:current_user) { maintainer }
+ let(:deletion_service) { instance_double(Terraform::States::TriggerDestroyService) }
+
+ where(given_state_name: %w[test-state test.state test%2Ffoo])
+ with_them do
+ let(:state_name) { given_state_name }
+
+ it_behaves_like 'schedules the state for deletion'
+ end
+
+ context 'allow_dots_on_tf_state_names is disabled, and the state name contains a dot' do
+ let(:state_name) { 'state-name-with-dot' }
+ let(:state_name_with_dot) { "#{state_name}.tfstate" }
+ let(:state_path) { "/projects/#{project_id}/terraform/state/#{state_name_with_dot}" }
+
+ before do
+ stub_feature_flags(allow_dots_on_tf_state_names: false)
+ end
+
+ it_behaves_like 'schedules the state for deletion'
+ end
+
+ context 'with invalid state name' do
+ let(:state_name) { 'foo/bar' }
+
+ it 'returns a 404 error' do
+ request
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
it_behaves_like 'cannot access a state that is scheduled for deletion'
end
@@ -304,7 +432,7 @@ RSpec.describe API::Terraform::State, :snowplow do
end
end
- describe 'PUT /projects/:id/terraform/state/:name/lock' do
+ describe 'POST /projects/:id/terraform/state/:name/lock' do
let(:params) do
{
ID: '123-456',
@@ -322,10 +450,14 @@ RSpec.describe API::Terraform::State, :snowplow do
it_behaves_like 'endpoint with unique user tracking'
it_behaves_like 'cannot access a state that is scheduled for deletion'
- it 'locks the terraform state' do
- request
+ context 'with invalid state name' do
+ let(:state_name) { 'foo/bar' }
- expect(response).to have_gitlab_http_status(:ok)
+ it 'returns a 404 error' do
+ request
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
end
context 'state is already locked' do
@@ -349,6 +481,47 @@ RSpec.describe API::Terraform::State, :snowplow do
expect(response).to have_gitlab_http_status(:forbidden)
end
end
+
+ where(given_state_name: %w[test-state test%2Ffoo])
+ with_them do
+ let(:state_name) { given_state_name }
+
+ it 'locks the terraform state' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'with a dot in the state name' do
+ let(:state_name) { 'test.state' }
+
+ context 'with allow_dots_on_tf_state_names ff enabled' do
+ before do
+ stub_feature_flags(allow_dots_on_tf_state_names: true)
+ end
+
+ let(:state_name) { 'test.state' }
+
+ it 'locks the terraform state' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'with allow_dots_on_tf_state_names ff disabled' do
+ before do
+ stub_feature_flags(allow_dots_on_tf_state_names: false)
+ end
+
+ it 'returns 404' do
+ request
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
end
describe 'DELETE /projects/:id/terraform/state/:name/lock' do
@@ -365,8 +538,9 @@ RSpec.describe API::Terraform::State, :snowplow do
end
before do
- state.lock_xid = '123-456'
+ state.lock_xid = '123.456'
state.save!
+ stub_feature_flags(allow_dots_on_tf_state_names: true)
end
subject(:request) { delete api("#{state_path}/lock"), headers: auth_header, params: params }
@@ -379,28 +553,61 @@ RSpec.describe API::Terraform::State, :snowplow do
let(:lock_id) { 'irrelevant to this test, just needs to be present' }
end
- context 'with the correct lock id' do
- let(:lock_id) { '123-456' }
+ where(given_state_name: %w[test-state test.state test%2Ffoo])
+ with_them do
+ let(:state_name) { given_state_name }
- it 'removes the terraform state lock' do
- request
+ context 'with the correct lock id' do
+ let(:lock_id) { '123.456' }
- expect(response).to have_gitlab_http_status(:ok)
+ it 'removes the terraform state lock' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'with allow_dots_on_tf_state_names ff disabled' do
+ before do
+ stub_feature_flags(allow_dots_on_tf_state_names: false)
+ end
+
+ context 'with dots in the state name' do
+ let(:lock_id) { '123.456' }
+ let(:state_name) { 'test.state' }
+
+ it 'returns 404' do
+ request
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ context 'with no lock id (force-unlock)' do
+ let(:params) { {} }
+
+ it 'removes the terraform state lock' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
end
end
- context 'with no lock id (force-unlock)' do
- let(:params) { {} }
+ context 'with invalid state name' do
+ let(:lock_id) { '123.456' }
+ let(:state_name) { 'foo/bar' }
- it 'removes the terraform state lock' do
+ it 'returns a 404 error' do
request
- expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'with an incorrect lock id' do
- let(:lock_id) { '456-789' }
+ let(:lock_id) { '456.789' }
it 'returns an error' do
request
@@ -420,7 +627,7 @@ RSpec.describe API::Terraform::State, :snowplow do
end
context 'user does not have permission to unlock the state' do
- let(:lock_id) { '123-456' }
+ let(:lock_id) { '123.456' }
let(:current_user) { developer }
it 'returns an error' do
diff --git a/spec/requests/api/terraform/state_version_spec.rb b/spec/requests/api/terraform/state_version_spec.rb
index ade0aacf805..28abbb5749d 100644
--- a/spec/requests/api/terraform/state_version_spec.rb
+++ b/spec/requests/api/terraform/state_version_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Terraform::StateVersion do
+RSpec.describe API::Terraform::StateVersion, feature_category: :infrastructure_as_code do
include HttpBasicAuthHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb
index 7a626ee4d29..5a342f79926 100644
--- a/spec/requests/api/todos_spec.rb
+++ b/spec/requests/api/todos_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Todos do
+RSpec.describe API::Todos, feature_category: :source_code_management do
include DesignManagementTestHelpers
let_it_be(:group) { create(:group) }
@@ -15,6 +15,7 @@ RSpec.describe API::Todos do
let_it_be(:work_item) { create(:work_item, :task, project: project_1) }
let_it_be(:merge_request) { create(:merge_request, source_project: project_1) }
let_it_be(:alert) { create(:alert_management_alert, project: project_1) }
+ let_it_be(:group_request_todo) { create(:todo, author: author_1, user: john_doe, target: group, action: Todo::MEMBER_ACCESS_REQUESTED) }
let_it_be(:alert_todo) { create(:todo, project: project_1, author: john_doe, user: john_doe, target: alert) }
let_it_be(:merge_request_todo) { create(:todo, project: project_1, author: author_2, user: john_doe, target: merge_request) }
let_it_be(:pending_1) { create(:todo, :mentioned, project: project_1, author: author_1, user: john_doe, target: issue) }
@@ -71,7 +72,7 @@ RSpec.describe API::Todos do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(json_response.length).to eq(6)
+ expect(json_response.length).to eq(7)
expect(json_response[0]).to include(
'id' => pending_5.id,
@@ -127,6 +128,17 @@ RSpec.describe API::Todos do
'title' => alert.title
)
)
+
+ expect(json_response[6]).to include(
+ 'target_type' => 'Namespace',
+ 'action_name' => 'member_access_requested',
+ 'target' => hash_including(
+ 'id' => group.id,
+ 'name' => group.name,
+ 'full_path' => group.full_path
+ ),
+ 'target_url' => Gitlab::Routing.url_helpers.group_group_members_url(group, tab: 'access_requests')
+ )
end
context "when current user does not have access to one of the TODO's target" do
@@ -137,7 +149,7 @@ RSpec.describe API::Todos do
get api('/todos', john_doe)
- expect(json_response.count).to eq(6)
+ expect(json_response.count).to eq(7)
expect(json_response.map { |t| t['id'] }).not_to include(no_access_todo.id, pending_4.id)
end
end
@@ -231,7 +243,7 @@ RSpec.describe API::Todos do
create(:on_commit_todo, project: new_todo.project, author: author_1, user: john_doe, target: merge_request_3)
create(:todo, project: new_todo.project, author: author_2, user: john_doe, target: merge_request_3)
- expect { get api('/todos', john_doe) }.not_to exceed_query_limit(control1).with_threshold(5)
+ expect { get api('/todos', john_doe) }.not_to exceed_query_limit(control1).with_threshold(6)
control2 = ActiveRecord::QueryRecorder.new { get api('/todos', john_doe) }
create_issue_todo_for(john_doe)
diff --git a/spec/requests/api/topics_spec.rb b/spec/requests/api/topics_spec.rb
index 1ad6f876fab..14719292557 100644
--- a/spec/requests/api/topics_spec.rb
+++ b/spec/requests/api/topics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Topics do
+RSpec.describe API::Topics, feature_category: :projects do
include WorkhorseHelpers
let_it_be(:file) { fixture_file_upload('spec/fixtures/dk.png') }
diff --git a/spec/requests/api/unleash_spec.rb b/spec/requests/api/unleash_spec.rb
index 4d382f91023..5daf7cd7b75 100644
--- a/spec/requests/api/unleash_spec.rb
+++ b/spec/requests/api/unleash_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Unleash do
+RSpec.describe API::Unleash, feature_category: :feature_flags do
include FeatureFlagHelpers
let_it_be(:project, refind: true) { create(:project) }
@@ -88,85 +88,6 @@ RSpec.describe API::Unleash do
end
end
- shared_examples_for 'support multiple environments' do
- let!(:client) { create(:operations_feature_flags_client, project: project) }
- let!(:base_headers) { { "UNLEASH-INSTANCEID" => client.token } }
- let!(:headers) { base_headers.merge({ "UNLEASH-APPNAME" => "test" }) }
-
- let!(:feature_flag_1) do
- create(:operations_feature_flag, name: "feature_flag_1", project: project, active: true)
- end
-
- let!(:feature_flag_2) do
- create(:operations_feature_flag, name: "feature_flag_2", project: project, active: false)
- end
-
- before do
- create_scope(feature_flag_1, 'production', false)
- create_scope(feature_flag_2, 'review/*', true)
- end
-
- it 'does not have N+1 problem' do
- control_count = ActiveRecord::QueryRecorder.new { get api(features_url), headers: headers }.count
-
- create(:operations_feature_flag, name: "feature_flag_3", project: project, active: true)
-
- expect { get api(features_url), headers: headers }.not_to exceed_query_limit(control_count)
- end
-
- context 'when app name is staging' do
- let(:headers) { base_headers.merge({ "UNLEASH-APPNAME" => "staging" }) }
-
- it 'returns correct active values' do
- subject
-
- feature_flag_1 = json_response['features'].find { |f| f['name'] == 'feature_flag_1' }
- feature_flag_2 = json_response['features'].find { |f| f['name'] == 'feature_flag_2' }
-
- expect(feature_flag_1['enabled']).to eq(true)
- expect(feature_flag_2['enabled']).to eq(false)
- end
- end
-
- context 'when app name is production' do
- let(:headers) { base_headers.merge({ "UNLEASH-APPNAME" => "production" }) }
-
- it 'returns correct active values' do
- subject
-
- feature_flag_1 = json_response['features'].find { |f| f['name'] == 'feature_flag_1' }
- feature_flag_2 = json_response['features'].find { |f| f['name'] == 'feature_flag_2' }
-
- expect(feature_flag_1['enabled']).to eq(false)
- expect(feature_flag_2['enabled']).to eq(false)
- end
- end
-
- context 'when app name is review/patch-1' do
- let(:headers) { base_headers.merge({ "UNLEASH-APPNAME" => "review/patch-1" }) }
-
- it 'returns correct active values' do
- subject
-
- feature_flag_1 = json_response['features'].find { |f| f['name'] == 'feature_flag_1' }
- feature_flag_2 = json_response['features'].find { |f| f['name'] == 'feature_flag_2' }
-
- expect(feature_flag_1['enabled']).to eq(true)
- expect(feature_flag_2['enabled']).to eq(false)
- end
- end
-
- context 'when app name is empty' do
- let(:headers) { base_headers }
-
- it 'returns empty list' do
- subject
-
- expect(json_response['features'].count).to eq(0)
- end
- end
- end
-
%w(/feature_flags/unleash/:project_id/features /feature_flags/unleash/:project_id/client/features).each do |features_endpoint|
describe "GET #{features_endpoint}", :use_clean_rails_redis_caching do
let(:features_url) { features_endpoint.sub(':project_id', project_id.to_s) }
diff --git a/spec/requests/api/usage_data_non_sql_metrics_spec.rb b/spec/requests/api/usage_data_non_sql_metrics_spec.rb
index 0b73d0f96a4..0a6f248af2c 100644
--- a/spec/requests/api/usage_data_non_sql_metrics_spec.rb
+++ b/spec/requests/api/usage_data_non_sql_metrics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::UsageDataNonSqlMetrics do
+RSpec.describe API::UsageDataNonSqlMetrics, feature_category: :service_ping do
include UsageDataHelpers
let_it_be(:admin) { create(:user, admin: true) }
diff --git a/spec/requests/api/usage_data_queries_spec.rb b/spec/requests/api/usage_data_queries_spec.rb
index 6ce03954246..e556064025c 100644
--- a/spec/requests/api/usage_data_queries_spec.rb
+++ b/spec/requests/api/usage_data_queries_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require 'rake_helper'
-RSpec.describe API::UsageDataQueries do
+RSpec.describe API::UsageDataQueries, feature_category: :service_ping do
include UsageDataHelpers
let_it_be(:admin) { create(:user, admin: true) }
@@ -80,7 +80,7 @@ RSpec.describe API::UsageDataQueries do
end
it 'matches the generated query' do
- Timecop.freeze(2021, 1, 1) do
+ travel_to(Time.utc(2021, 1, 1)) do
get api(endpoint, admin)
end
diff --git a/spec/requests/api/usage_data_spec.rb b/spec/requests/api/usage_data_spec.rb
index d532fb6c168..935ddbf4764 100644
--- a/spec/requests/api/usage_data_spec.rb
+++ b/spec/requests/api/usage_data_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::UsageData do
+RSpec.describe API::UsageData, feature_category: :service_ping do
let_it_be(:user) { create(:user) }
describe 'POST /usage_data/increment_counter' do
diff --git a/spec/requests/api/user_counts_spec.rb b/spec/requests/api/user_counts_spec.rb
index 369ae49de08..27e5311e2eb 100644
--- a/spec/requests/api/user_counts_spec.rb
+++ b/spec/requests/api/user_counts_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::UserCounts do
+RSpec.describe API::UserCounts, feature_category: :service_ping do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public) }
let_it_be(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
diff --git a/spec/requests/api/users_preferences_spec.rb b/spec/requests/api/users_preferences_spec.rb
index 97e37263ee6..53f366371e5 100644
--- a/spec/requests/api/users_preferences_spec.rb
+++ b/spec/requests/api/users_preferences_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Users do
+RSpec.describe API::Users, feature_category: :users do
let_it_be(:user) { create(:user) }
describe 'PUT /user/preferences/' do
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 6688a998a1a..bfb71d95f5e 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Users do
+RSpec.describe API::Users, feature_category: :users do
include WorkhorseHelpers
let_it_be(:admin) { create(:admin) }
@@ -1988,11 +1988,19 @@ RSpec.describe API::Users do
expect(json_response['error']).to eq('title is missing')
end
- it "creates ssh key" do
- key_attrs = attributes_for :key
+ it "creates ssh key", :aggregate_failures do
+ key_attrs = attributes_for(:key, usage_type: :signing)
+
expect do
post api("/users/#{user.id}/keys", admin), params: key_attrs
end.to change { user.keys.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(:created)
+
+ key = user.keys.last
+ expect(key.title).to eq(key_attrs[:title])
+ expect(key.key).to eq(key_attrs[:key])
+ expect(key.usage_type).to eq(key_attrs[:usage_type].to_s)
end
it 'creates SSH key with `expires_at` attribute' do
@@ -2848,12 +2856,19 @@ RSpec.describe API::Users do
end
describe "POST /user/keys" do
- it "creates ssh key" do
- key_attrs = attributes_for :key
+ it "creates ssh key", :aggregate_failures do
+ key_attrs = attributes_for(:key, usage_type: :signing)
+
expect do
post api("/user/keys", user), params: key_attrs
end.to change { user.keys.count }.by(1)
+
expect(response).to have_gitlab_http_status(:created)
+
+ key = user.keys.last
+ expect(key.title).to eq(key_attrs[:title])
+ expect(key.key).to eq(key_attrs[:key])
+ expect(key.usage_type).to eq(key_attrs[:usage_type].to_s)
end
it 'creates SSH key with `expires_at` attribute' do
diff --git a/spec/requests/api/v3/github_spec.rb b/spec/requests/api/v3/github_spec.rb
index 5bfea15f0ca..0b8fac5c55c 100644
--- a/spec/requests/api/v3/github_spec.rb
+++ b/spec/requests/api/v3/github_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::V3::Github do
+RSpec.describe API::V3::Github, feature_category: :integrations do
let_it_be(:user) { create(:user) }
let_it_be(:unauthorized_user) { create(:user) }
let_it_be(:admin) { create(:user, :admin) }
diff --git a/spec/requests/api/wikis_spec.rb b/spec/requests/api/wikis_spec.rb
index f4096eef8d0..00e38a5bb7e 100644
--- a/spec/requests/api/wikis_spec.rb
+++ b/spec/requests/api/wikis_spec.rb
@@ -12,7 +12,7 @@ require 'spec_helper'
# - maintainer
# because they are 3 edge cases of using wiki pages.
-RSpec.describe API::Wikis do
+RSpec.describe API::Wikis, feature_category: :wiki do
include WorkhorseHelpers
include AfterNextHelpers
diff --git a/spec/requests/concerns/planning_hierarchy_spec.rb b/spec/requests/concerns/planning_hierarchy_spec.rb
index ece9270b3a1..89232916936 100644
--- a/spec/requests/concerns/planning_hierarchy_spec.rb
+++ b/spec/requests/concerns/planning_hierarchy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe PlanningHierarchy, type: :request do
+RSpec.describe PlanningHierarchy, type: :request, feature_category: :projects do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
diff --git a/spec/requests/content_security_policy_spec.rb b/spec/requests/content_security_policy_spec.rb
index 06fc5b0e190..3f0665f1ce5 100644
--- a/spec/requests/content_security_policy_spec.rb
+++ b/spec/requests/content_security_policy_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
# The AnonymousController doesn't support setting the CSP
# This is why an arbitrary test request was chosen instead
# of testing in application_controller_spec.
-RSpec.describe 'Content Security Policy' do
+RSpec.describe 'Content Security Policy', feature_category: :application_instrumentation do
let(:snowplow_host) { 'snowplow.example.com' }
shared_examples 'snowplow is not in the CSP' do
diff --git a/spec/requests/dashboard/projects_controller_spec.rb b/spec/requests/dashboard/projects_controller_spec.rb
index 4cd3b6c4f9e..752799196c9 100644
--- a/spec/requests/dashboard/projects_controller_spec.rb
+++ b/spec/requests/dashboard/projects_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Dashboard::ProjectsController do
+RSpec.describe Dashboard::ProjectsController, feature_category: :projects do
context 'token authentication' do
it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: false do
let(:url) { dashboard_projects_url(:atom) }
diff --git a/spec/requests/dashboard_controller_spec.rb b/spec/requests/dashboard_controller_spec.rb
index 62655d720c5..9edacb27c93 100644
--- a/spec/requests/dashboard_controller_spec.rb
+++ b/spec/requests/dashboard_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe DashboardController do
+RSpec.describe DashboardController, feature_category: :authentication_and_authorization do
context 'token authentication' do
it_behaves_like 'authenticates sessionless user for the request spec', 'issues atom', public_resource: false do
let(:url) { issues_dashboard_url(:atom, assignee_username: user.username) }
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 20d298edfe5..66337b94c75 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Git HTTP requests' do
+RSpec.describe 'Git HTTP requests', feature_category: :source_code_management do
include ProjectForksHelper
include TermsHelper
include GitHttpHelpers
diff --git a/spec/requests/groups/autocomplete_sources_spec.rb b/spec/requests/groups/autocomplete_sources_spec.rb
index d053e0fe773..e44fb9f6c37 100644
--- a/spec/requests/groups/autocomplete_sources_spec.rb
+++ b/spec/requests/groups/autocomplete_sources_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'groups autocomplete' do
+RSpec.describe 'groups autocomplete', feature_category: :subgroups do
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:group) { create(:group, :private) }
diff --git a/spec/requests/groups/clusters/integrations_controller_spec.rb b/spec/requests/groups/clusters/integrations_controller_spec.rb
index 29e37e2e48c..0b9148e917b 100644
--- a/spec/requests/groups/clusters/integrations_controller_spec.rb
+++ b/spec/requests/groups/clusters/integrations_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::Clusters::IntegrationsController do
+RSpec.describe Groups::Clusters::IntegrationsController, features: :integrations do
include AccessMatchersForController
shared_examples 'a secure endpoint' do
diff --git a/spec/requests/groups/crm/contacts_controller_spec.rb b/spec/requests/groups/crm/contacts_controller_spec.rb
index 70086ddbbba..4916ce60108 100644
--- a/spec/requests/groups/crm/contacts_controller_spec.rb
+++ b/spec/requests/groups/crm/contacts_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::Crm::ContactsController do
+RSpec.describe Groups::Crm::ContactsController, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
shared_examples 'response with 404 status' do
diff --git a/spec/requests/groups/crm/organizations_controller_spec.rb b/spec/requests/groups/crm/organizations_controller_spec.rb
index e841dd80b67..3e7e9a8e878 100644
--- a/spec/requests/groups/crm/organizations_controller_spec.rb
+++ b/spec/requests/groups/crm/organizations_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::Crm::OrganizationsController do
+RSpec.describe Groups::Crm::OrganizationsController, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
shared_examples 'response with 404 status' do
diff --git a/spec/requests/groups/deploy_tokens_controller_spec.rb b/spec/requests/groups/deploy_tokens_controller_spec.rb
index b3dce9b9cf1..05fd8d9691c 100644
--- a/spec/requests/groups/deploy_tokens_controller_spec.rb
+++ b/spec/requests/groups/deploy_tokens_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::DeployTokensController do
+RSpec.describe Groups::DeployTokensController, feature_category: :continuous_delivery do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:deploy_token) { create(:deploy_token, :group, groups: [group]) }
diff --git a/spec/requests/groups/email_campaigns_controller_spec.rb b/spec/requests/groups/email_campaigns_controller_spec.rb
index 4d630ef6710..7db5c084793 100644
--- a/spec/requests/groups/email_campaigns_controller_spec.rb
+++ b/spec/requests/groups/email_campaigns_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::EmailCampaignsController do
+RSpec.describe Groups::EmailCampaignsController, feature_category: :navigation do
using RSpec::Parameterized::TableSyntax
describe 'GET #index', :snowplow do
diff --git a/spec/requests/groups/harbor/artifacts_controller_spec.rb b/spec/requests/groups/harbor/artifacts_controller_spec.rb
index ea9529119a6..7ec792d081d 100644
--- a/spec/requests/groups/harbor/artifacts_controller_spec.rb
+++ b/spec/requests/groups/harbor/artifacts_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::Harbor::ArtifactsController do
+RSpec.describe Groups::Harbor::ArtifactsController, feature_category: :build_artifacts do
it_behaves_like 'a harbor artifacts controller', anonymous_status_code: '404' do
let_it_be(:container) { create(:group) }
let_it_be(:harbor_integration) { create(:harbor_integration, group: container, project: nil) }
diff --git a/spec/requests/groups/harbor/repositories_controller_spec.rb b/spec/requests/groups/harbor/repositories_controller_spec.rb
index b4022561f54..f397e9192ea 100644
--- a/spec/requests/groups/harbor/repositories_controller_spec.rb
+++ b/spec/requests/groups/harbor/repositories_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::Harbor::RepositoriesController do
+RSpec.describe Groups::Harbor::RepositoriesController, feature_category: :source_code_management do
it_behaves_like 'a harbor repositories controller', anonymous_status_code: '404' do
let_it_be(:container, reload: true) { create(:group) }
let_it_be(:harbor_integration) { create(:harbor_integration, group: container, project: nil) }
diff --git a/spec/requests/groups/harbor/tags_controller_spec.rb b/spec/requests/groups/harbor/tags_controller_spec.rb
index 257d4366837..15da1cb1c4b 100644
--- a/spec/requests/groups/harbor/tags_controller_spec.rb
+++ b/spec/requests/groups/harbor/tags_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::Harbor::TagsController do
+RSpec.describe Groups::Harbor::TagsController, feature_category: :source_code_management do
it_behaves_like 'a harbor tags controller', anonymous_status_code: '404' do
let_it_be(:container) { create(:group) }
let_it_be(:harbor_integration) { create(:harbor_integration, group: container, project: nil) }
diff --git a/spec/requests/groups/milestones_controller_spec.rb b/spec/requests/groups/milestones_controller_spec.rb
index e6418c7694d..54a25333c02 100644
--- a/spec/requests/groups/milestones_controller_spec.rb
+++ b/spec/requests/groups/milestones_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::MilestonesController do
+RSpec.describe Groups::MilestonesController, feature_category: :team_planning do
context 'N+1 DB queries' do
let_it_be(:user) { create(:user) }
let_it_be(:public_group) { create(:group, :public) }
diff --git a/spec/requests/groups/observability_controller_spec.rb b/spec/requests/groups/observability_controller_spec.rb
index a08231fe939..46690d60539 100644
--- a/spec/requests/groups/observability_controller_spec.rb
+++ b/spec/requests/groups/observability_controller_spec.rb
@@ -2,14 +2,13 @@
require 'spec_helper'
-RSpec.describe Groups::ObservabilityController do
- include ContentSecurityPolicyHelpers
-
+RSpec.describe Groups::ObservabilityController, feature_category: :tracing do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let(:observability_url) { Gitlab::Observability.observability_url }
- let(:expected_observability_path) { "/" }
+ let(:path) { nil }
+ let(:expected_observability_path) { nil }
shared_examples 'observability route request' do
subject do
@@ -17,6 +16,10 @@ RSpec.describe Groups::ObservabilityController do
response
end
+ it_behaves_like 'observability csp policy' do
+ let(:tested_path) { path }
+ end
+
context 'when user is not authenticated' do
it 'returns 404' do
expect(subject).to have_gitlab_http_status(:not_found)
@@ -70,101 +73,22 @@ RSpec.describe Groups::ObservabilityController do
describe 'GET #dashboards' do
let(:path) { group_observability_dashboards_path(group) }
- let(:expected_observability_path) { "#{observability_url}/#{group.id}/" }
+ let(:expected_observability_path) { "#{observability_url}/-/#{group.id}/" }
it_behaves_like 'observability route request'
end
describe 'GET #manage' do
let(:path) { group_observability_manage_path(group) }
- let(:expected_observability_path) { "#{observability_url}/#{group.id}/dashboards" }
+ let(:expected_observability_path) { "#{observability_url}/-/#{group.id}/dashboards" }
it_behaves_like 'observability route request'
end
describe 'GET #explore' do
let(:path) { group_observability_explore_path(group) }
- let(:expected_observability_path) { "#{observability_url}/#{group.id}/explore" }
+ let(:expected_observability_path) { "#{observability_url}/-/#{group.id}/explore" }
it_behaves_like 'observability route request'
end
-
- describe 'CSP' do
- before do
- setup_csp_for_controller(described_class, csp)
- end
-
- subject do
- get group_observability_dashboards_path(group)
- response.headers['Content-Security-Policy']
- end
-
- context 'when there is no CSP config' do
- let(:csp) { ActionDispatch::ContentSecurityPolicy.new }
-
- it 'does not add any csp header' do
- expect(subject).to be_blank
- end
- end
-
- context 'when frame-src exists in the CSP config' do
- let(:csp) do
- ActionDispatch::ContentSecurityPolicy.new do |p|
- p.frame_src 'https://something.test'
- end
- end
-
- it 'appends the proper url to frame-src CSP directives' do
- expect(subject).to include(
- "frame-src https://something.test #{observability_url} 'self'")
- end
- end
-
- context 'when self is already present in the policy' do
- let(:csp) do
- ActionDispatch::ContentSecurityPolicy.new do |p|
- p.frame_src "'self'"
- end
- end
-
- it 'does not append self again' do
- expect(subject).to include(
- "frame-src 'self' #{observability_url};")
- end
- end
-
- context 'when default-src exists in the CSP config' do
- let(:csp) do
- ActionDispatch::ContentSecurityPolicy.new do |p|
- p.default_src 'https://something.test'
- end
- end
-
- it 'does not change default-src' do
- expect(subject).to include(
- "default-src https://something.test;")
- end
-
- it 'appends the proper url to frame-src CSP directives' do
- expect(subject).to include(
- "frame-src https://something.test #{observability_url} 'self'")
- end
- end
-
- context 'when frame-src and default-src exist in the CSP config' do
- let(:csp) do
- ActionDispatch::ContentSecurityPolicy.new do |p|
- p.default_src 'https://something_default.test'
- p.frame_src 'https://something.test'
- end
- end
-
- it 'appends to frame-src CSP directives' do
- expect(subject).to include(
- "frame-src https://something.test #{observability_url} 'self'")
- expect(subject).to include(
- "default-src https://something_default.test")
- end
- end
- end
end
diff --git a/spec/requests/groups/registry/repositories_controller_spec.rb b/spec/requests/groups/registry/repositories_controller_spec.rb
index 0699f48c2be..f54acf118bb 100644
--- a/spec/requests/groups/registry/repositories_controller_spec.rb
+++ b/spec/requests/groups/registry/repositories_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::Registry::RepositoriesController do
+RSpec.describe Groups::Registry::RepositoriesController, feature_category: :container_registry do
let_it_be(:group, reload: true) { create(:group) }
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/groups/settings/access_tokens_controller_spec.rb b/spec/requests/groups/settings/access_tokens_controller_spec.rb
index 6b150e0acb6..f26b69f8d30 100644
--- a/spec/requests/groups/settings/access_tokens_controller_spec.rb
+++ b/spec/requests/groups/settings/access_tokens_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::Settings::AccessTokensController do
+RSpec.describe Groups::Settings::AccessTokensController, feature_category: :authentication_and_authorization do
let_it_be(:user) { create(:user) }
let_it_be(:resource) { create(:group) }
let_it_be(:access_token_user) { create(:user, :project_bot) }
diff --git a/spec/requests/groups/settings/applications_controller_spec.rb b/spec/requests/groups/settings/applications_controller_spec.rb
index 74313491414..fb91cd8bdab 100644
--- a/spec/requests/groups/settings/applications_controller_spec.rb
+++ b/spec/requests/groups/settings/applications_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::Settings::ApplicationsController do
+RSpec.describe Groups::Settings::ApplicationsController, feature_category: :authentication_and_authorization do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:application) { create(:oauth_application, owner_id: group.id, owner_type: 'Namespace') }
diff --git a/spec/requests/groups/usage_quotas_controller_spec.rb b/spec/requests/groups/usage_quotas_controller_spec.rb
new file mode 100644
index 00000000000..bddc95434ce
--- /dev/null
+++ b/spec/requests/groups/usage_quotas_controller_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Groups::UsageQuotasController, feature_category: :subscription_cost_management do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:subgroup) { create(:group, parent: group) }
+ let_it_be(:user) { create(:user) }
+
+ subject(:request) { get group_usage_quotas_path(group) }
+
+ before do
+ sign_in(user)
+ end
+
+ describe 'GET /groups/*group_id/-/usage_quotas' do
+ context 'when user has read_usage_quotas permission' do
+ before do
+ group.add_owner(user)
+ end
+
+ it 'renders index with 200 status code' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.body).to match(/Placeholder for usage quotas Vue app/)
+ end
+
+ it 'renders 404 page if subgroup' do
+ get group_usage_quotas_path(subgroup)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when user does not have read_usage_quotas permission' do
+ before do
+ group.add_maintainer(user)
+ end
+
+ it 'renders not_found' do
+ request
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+end
diff --git a/spec/requests/groups_controller_spec.rb b/spec/requests/groups_controller_spec.rb
index 422c108f2ad..7fc14910819 100644
--- a/spec/requests/groups_controller_spec.rb
+++ b/spec/requests/groups_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe GroupsController do
+RSpec.describe GroupsController, feature_category: :subgroups do
context 'token authentication' do
context 'when public group' do
let_it_be(:public_group) { create(:group, :public) }
diff --git a/spec/requests/health_controller_spec.rb b/spec/requests/health_controller_spec.rb
index ae15b63df19..83ec1565095 100644
--- a/spec/requests/health_controller_spec.rb
+++ b/spec/requests/health_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe HealthController do
+RSpec.describe HealthController, feature_category: :database do
include StubENV
let(:token) { Gitlab::CurrentSettings.health_check_access_token }
diff --git a/spec/requests/ide_controller_spec.rb b/spec/requests/ide_controller_spec.rb
index 8d61399c824..b287ded799d 100644
--- a/spec/requests/ide_controller_spec.rb
+++ b/spec/requests/ide_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe IdeController do
+RSpec.describe IdeController, feature_category: :web_ide do
using RSpec::Parameterized::TableSyntax
let_it_be(:reporter) { create(:user) }
@@ -21,7 +21,20 @@ RSpec.describe IdeController do
let(:user) { creator }
let(:branch) { '' }
+ def find_csp_frame_src
+ csp = response.headers['Content-Security-Policy']
+
+ # Transform "frame-src foo bar; connect-src foo bar; script-src ..."
+ # into array of connect-src values
+ csp.split(';')
+ .map(&:strip)
+ .find { |entry| entry.starts_with?('frame-src') }
+ .split(' ')
+ .drop(1)
+ end
+
before do
+ stub_feature_flags(vscode_web_ide: true)
sign_in(user)
end
@@ -265,5 +278,17 @@ RSpec.describe IdeController do
end
end
end
+
+ describe 'frame-src content security policy' do
+ let(:route) { '/-/ide' }
+
+ before do
+ subject
+ end
+
+ it 'adds https://*.vscode-cdn.net in frame-src CSP policy' do
+ expect(find_csp_frame_src).to include("https://*.vscode-cdn.net/")
+ end
+ end
end
end
diff --git a/spec/requests/import/github_groups_controller_spec.rb b/spec/requests/import/github_groups_controller_spec.rb
index 544cbf88cd2..6393dd35a98 100644
--- a/spec/requests/import/github_groups_controller_spec.rb
+++ b/spec/requests/import/github_groups_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Import::GithubGroupsController do
+RSpec.describe Import::GithubGroupsController, feature_category: :importers do
describe 'GET status' do
subject(:status) { get '/import/github_group/status', params: params, headers: headers }
diff --git a/spec/requests/import/gitlab_groups_controller_spec.rb b/spec/requests/import/gitlab_groups_controller_spec.rb
index 8d5c1e3ebab..1766c48cca1 100644
--- a/spec/requests/import/gitlab_groups_controller_spec.rb
+++ b/spec/requests/import/gitlab_groups_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Import::GitlabGroupsController do
+RSpec.describe Import::GitlabGroupsController, feature_category: :importers do
include WorkhorseHelpers
include_context 'workhorse headers'
diff --git a/spec/requests/import/gitlab_projects_controller_spec.rb b/spec/requests/import/gitlab_projects_controller_spec.rb
index eed035608d0..b2c2d306e53 100644
--- a/spec/requests/import/gitlab_projects_controller_spec.rb
+++ b/spec/requests/import/gitlab_projects_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Import::GitlabProjectsController do
+RSpec.describe Import::GitlabProjectsController, feature_category: :importers do
include WorkhorseHelpers
include_context 'workhorse headers'
diff --git a/spec/requests/import/url_controller_spec.rb b/spec/requests/import/url_controller_spec.rb
index 63af5e8b469..fa2abda6711 100644
--- a/spec/requests/import/url_controller_spec.rb
+++ b/spec/requests/import/url_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Import::UrlController do
+RSpec.describe Import::UrlController, feature_category: :importers do
let_it_be(:user) { create(:user) }
before do
diff --git a/spec/requests/jira_authorizations_spec.rb b/spec/requests/jira_authorizations_spec.rb
index f67288b286b..8c27b61712c 100644
--- a/spec/requests/jira_authorizations_spec.rb
+++ b/spec/requests/jira_authorizations_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Jira authorization requests' do
+RSpec.describe 'Jira authorization requests', feature_category: :integrations do
let(:user) { create :user }
let(:application) { create :oauth_application, scopes: 'api' }
let(:redirect_uri) { oauth_jira_dvcs_callback_url(host: "http://www.example.com") }
diff --git a/spec/requests/jira_connect/cors_preflight_checks_controller_spec.rb b/spec/requests/jira_connect/cors_preflight_checks_controller_spec.rb
deleted file mode 100644
index d441a8575d0..00000000000
--- a/spec/requests/jira_connect/cors_preflight_checks_controller_spec.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe JiraConnect::CorsPreflightChecksController do
- shared_examples 'allows cross-origin requests on self managed' do
- it 'renders not found' do
- options path
-
- expect(response).to have_gitlab_http_status(:not_found)
- expect(response.headers['Access-Control-Allow-Origin']).to be_nil
- end
-
- context 'with jira_connect_proxy_url setting' do
- before do
- stub_application_setting(jira_connect_proxy_url: 'https://gitlab.com')
-
- options path, headers: { 'Origin' => 'http://notgitlab.com' }
- end
-
- it 'returns 200' do
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'responds with access-control-allow headers', :aggregate_failures do
- expect(response.headers['Access-Control-Allow-Origin']).to eq 'https://gitlab.com'
- expect(response.headers['Access-Control-Allow-Methods']).to eq allowed_methods
- expect(response.headers['Access-Control-Allow-Credentials']).to be_nil
- end
-
- context 'when on GitLab.com' do
- before do
- allow(Gitlab).to receive(:com?).and_return(true)
- end
-
- it 'renders not found' do
- options path
-
- expect(response).to have_gitlab_http_status(:not_found)
- expect(response.headers['Access-Control-Allow-Origin']).to be_nil
- end
- end
- end
- end
-
- describe 'OPTIONS /-/jira_connect/oauth_application_id' do
- let(:allowed_methods) { 'GET, OPTIONS' }
- let(:path) { '/-/jira_connect/oauth_application_id' }
-
- it_behaves_like 'allows cross-origin requests on self managed'
- end
-
- describe 'OPTIONS /-/jira_connect/subscriptions/:id' do
- let(:allowed_methods) { 'DELETE, OPTIONS' }
- let(:path) { '/-/jira_connect/subscriptions/123' }
-
- it_behaves_like 'allows cross-origin requests on self managed'
- end
-end
diff --git a/spec/requests/jira_connect/installations_controller_spec.rb b/spec/requests/jira_connect/installations_controller_spec.rb
index 6315c66a41a..67544bbca2e 100644
--- a/spec/requests/jira_connect/installations_controller_spec.rb
+++ b/spec/requests/jira_connect/installations_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JiraConnect::InstallationsController do
+RSpec.describe JiraConnect::InstallationsController, feature_category: :integrations do
let_it_be(:installation) { create(:jira_connect_installation) }
describe 'GET /-/jira_connect/installations' do
@@ -47,16 +47,18 @@ RSpec.describe JiraConnect::InstallationsController do
end
describe 'PUT /-/jira_connect/installations' do
- before do
+ subject(:do_request) do
put '/-/jira_connect/installations', params: { jwt: jwt, installation: { instance_url: update_instance_url } }
end
- let(:update_instance_url) { 'https://example.com' }
+ let(:update_instance_url) { nil }
context 'without JWT' do
let(:jwt) { nil }
it 'returns 403' do
+ do_request
+
expect(response).to have_gitlab_http_status(:forbidden)
end
end
@@ -66,28 +68,69 @@ RSpec.describe JiraConnect::InstallationsController do
let(:jwt) { Atlassian::Jwt.encode({ iss: installation.client_key, qsh: qsh }, installation.shared_secret) }
it 'returns 200' do
+ do_request
+
expect(response).to have_gitlab_http_status(:ok)
end
- it 'updates the instance_url' do
- expect(json_response).to eq({
- 'gitlab_com' => false,
- 'instance_url' => 'https://example.com'
- })
- end
+ context 'with instance_url param' do
+ let(:update_instance_url) { 'https://example.com' }
- context 'invalid URL' do
- let(:update_instance_url) { 'invalid url' }
+ context 'instance response with success' do
+ before do
+ stub_request(:post, 'https://example.com/-/jira_connect/events/installed')
+ end
- it 'returns 422 and errors', :aggregate_failures do
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
- expect(json_response).to eq({
- 'errors' => {
- 'instance_url' => [
- 'is blocked: Only allowed schemes are http, https'
- ]
- }
- })
+ it 'updates the instance_url' do
+ do_request
+
+ expect(json_response).to eq({
+ 'gitlab_com' => false,
+ 'instance_url' => 'https://example.com'
+ })
+ end
+
+ it 'sends an installed event to the self-managed instance' do
+ do_request
+
+ expect(WebMock).to have_requested(:post, 'https://example.com/-/jira_connect/events/installed')
+ end
+ end
+
+ context 'instance response with error' do
+ before do
+ stub_request(:post, 'https://example.com/-/jira_connect/events/installed').to_return(status: 422)
+ end
+
+ it 'returns 422 and errors', :aggregate_failures do
+ do_request
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(json_response).to eq({
+ 'errors' => {
+ 'instance_url' => [
+ 'Could not be installed on the instance. Error response code 422'
+ ]
+ }
+ })
+ end
+ end
+
+ context 'invalid URL' do
+ let(:update_instance_url) { 'invalid url' }
+
+ it 'returns 422 and errors', :aggregate_failures do
+ do_request
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(json_response).to eq({
+ 'errors' => {
+ 'instance_url' => [
+ 'is blocked: Only allowed schemes are http, https'
+ ]
+ }
+ })
+ end
end
end
end
diff --git a/spec/requests/jira_connect/oauth_application_ids_controller_spec.rb b/spec/requests/jira_connect/oauth_application_ids_controller_spec.rb
index 1d772e973ff..d111edd06da 100644
--- a/spec/requests/jira_connect/oauth_application_ids_controller_spec.rb
+++ b/spec/requests/jira_connect/oauth_application_ids_controller_spec.rb
@@ -2,9 +2,9 @@
require 'spec_helper'
-RSpec.describe JiraConnect::OauthApplicationIdsController do
+RSpec.describe JiraConnect::OauthApplicationIdsController, feature_category: :integrations do
describe 'GET /-/jira_connect/oauth_application_id' do
- let(:cors_request_headers) { { 'Origin' => 'http://notgitlab.com' } }
+ let(:cors_request_headers) { { 'Origin' => 'https://gitlab.com' } }
before do
stub_application_setting(jira_connect_application_key: '123456')
@@ -38,9 +38,9 @@ RSpec.describe JiraConnect::OauthApplicationIdsController do
end
end
- context 'when jira_connect_oauth_self_managed disabled' do
+ context 'on GitLab.com' do
before do
- stub_feature_flags(jira_connect_oauth_self_managed: false)
+ allow(Gitlab).to receive(:com?).and_return(true)
end
it 'renders not found' do
@@ -49,17 +49,22 @@ RSpec.describe JiraConnect::OauthApplicationIdsController do
expect(response).to have_gitlab_http_status(:not_found)
end
end
+ end
- context 'on GitLab.com' do
- before do
- allow(Gitlab).to receive(:com?).and_return(true)
- end
+ describe 'OPTIONS /-/jira_connect/oauth_application_id' do
+ let(:cors_request_headers) { { 'Origin' => 'https://gitlab.com', 'access-control-request-method' => 'GET' } }
- it 'renders not found' do
- get '/-/jira_connect/oauth_application_id'
+ before do
+ stub_application_setting(jira_connect_application_key: '123456')
+ stub_application_setting(jira_connect_proxy_url: 'https://gitlab.com')
+ end
- expect(response).to have_gitlab_http_status(:not_found)
- end
+ it 'allows cross-origin requests', :aggregate_failures do
+ options '/-/jira_connect/oauth_application_id', headers: cors_request_headers
+
+ expect(response.headers['Access-Control-Allow-Origin']).to eq 'https://gitlab.com'
+ expect(response.headers['Access-Control-Allow-Methods']).to eq 'GET, OPTIONS'
+ expect(response.headers['Access-Control-Allow-Credentials']).to be_nil
end
end
end
diff --git a/spec/requests/jira_connect/oauth_callbacks_controller_spec.rb b/spec/requests/jira_connect/oauth_callbacks_controller_spec.rb
index 12b9429b648..676b562c193 100644
--- a/spec/requests/jira_connect/oauth_callbacks_controller_spec.rb
+++ b/spec/requests/jira_connect/oauth_callbacks_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JiraConnect::OauthCallbacksController do
+RSpec.describe JiraConnect::OauthCallbacksController, feature_category: :integrations do
describe 'GET /-/jira_connect/oauth_callbacks' do
context 'when logged in' do
it 'renders a page prompting the user to close the window' do
diff --git a/spec/requests/jira_connect/public_keys_controller_spec.rb b/spec/requests/jira_connect/public_keys_controller_spec.rb
index 2eca4c0ea2f..bf472469d85 100644
--- a/spec/requests/jira_connect/public_keys_controller_spec.rb
+++ b/spec/requests/jira_connect/public_keys_controller_spec.rb
@@ -2,15 +2,15 @@
require 'spec_helper'
-RSpec.describe JiraConnect::PublicKeysController do
+RSpec.describe JiraConnect::PublicKeysController, feature_category: :integrations do
describe 'GET /-/jira_connect/public_keys/:uuid' do
+ let(:uuid) { non_existing_record_id }
+ let(:public_key_storage_enabled) { true }
+
before do
- allow(Gitlab).to receive(:com?).and_return(dot_com)
+ allow(Gitlab.config.jira_connect).to receive(:enable_public_keys_storage).and_return(public_key_storage_enabled)
end
- let(:uuid) { non_existing_record_id }
- let(:dot_com) { true }
-
it 'renders 404' do
get jira_connect_public_key_path(id: uuid)
@@ -29,8 +29,8 @@ RSpec.describe JiraConnect::PublicKeysController do
expect(response.body).to eq(public_key.key)
end
- context 'when not on GitLab.com' do
- let(:dot_com) { false }
+ context 'when public key storage disabled' do
+ let(:public_key_storage_enabled) { false }
it 'renders 404' do
get jira_connect_public_key_path(id: uuid)
diff --git a/spec/requests/jira_connect/subscriptions_controller_spec.rb b/spec/requests/jira_connect/subscriptions_controller_spec.rb
index b5f3ab916a4..8b019970b61 100644
--- a/spec/requests/jira_connect/subscriptions_controller_spec.rb
+++ b/spec/requests/jira_connect/subscriptions_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JiraConnect::SubscriptionsController do
+RSpec.describe JiraConnect::SubscriptionsController, feature_category: :integrations do
describe 'GET /-/jira_connect/subscriptions' do
let_it_be(:installation) { create(:jira_connect_installation, instance_url: 'http://self-managed-gitlab.com') }
let(:qsh) do
@@ -10,7 +10,7 @@ RSpec.describe JiraConnect::SubscriptionsController do
end
let(:jwt) { Atlassian::Jwt.encode({ iss: installation.client_key, qsh: qsh }, installation.shared_secret) }
- let(:cors_request_headers) { { 'Origin' => 'http://notgitlab.com' } }
+ let(:cors_request_headers) { { 'Origin' => 'https://gitlab.com' } }
let(:path) { '/-/jira_connect/subscriptions' }
let(:params) { { jwt: jwt } }
@@ -19,7 +19,7 @@ RSpec.describe JiraConnect::SubscriptionsController do
end
subject(:content_security_policy) do
- get path, params: params
+ get path, params: params, headers: cors_request_headers
response.headers['Content-Security-Policy']
end
@@ -27,6 +27,17 @@ RSpec.describe JiraConnect::SubscriptionsController do
it { is_expected.to include('http://self-managed-gitlab.com/-/jira_connect/') }
it { is_expected.to include('http://self-managed-gitlab.com/api/') }
it { is_expected.to include('http://self-managed-gitlab.com/oauth/') }
+ it { is_expected.to include('frame-ancestors \'self\' https://*.atlassian.net https://*.jira.com') }
+
+ context 'with additional iframe ancestors' do
+ before do
+ allow(Gitlab.config.jira_connect).to receive(:additional_iframe_ancestors).and_return(['http://localhost:*', 'http://dev.gitlab.com'])
+ end
+
+ it {
+ is_expected.to include('frame-ancestors \'self\' https://*.atlassian.net https://*.jira.com http://localhost:* http://dev.gitlab.com')
+ }
+ end
context 'with no self-managed instance configured' do
let_it_be(:installation) { create(:jira_connect_installation, instance_url: '') }
@@ -36,14 +47,48 @@ RSpec.describe JiraConnect::SubscriptionsController do
it { is_expected.not_to include('http://self-managed-gitlab.com/oauth/') }
end
- context 'with jira_connect_oauth_self_managed_setting feature disabled' do
- before do
- stub_feature_flags(jira_connect_oauth_self_managed_setting: false)
+ context 'when json format' do
+ let(:path) { '/-/jira_connect/subscriptions.json' }
+
+ it 'allows cross-origin requests', :aggregate_failures do
+ get path, params: params, headers: cors_request_headers
+
+ expect(response.headers['Access-Control-Allow-Origin']).to eq 'https://gitlab.com'
+ expect(response.headers['Access-Control-Allow-Methods']).to eq 'GET, OPTIONS'
+ expect(response.headers['Access-Control-Allow-Credentials']).to be_nil
end
+ end
+ end
- it { is_expected.not_to include('http://self-managed-gitlab.com/-/jira_connect/') }
- it { is_expected.not_to include('http://self-managed-gitlab.com/api/') }
- it { is_expected.not_to include('http://self-managed-gitlab.com/oauth/') }
+ describe 'OPTIONS /-/jira_connect/subscriptions' do
+ let(:cors_request_headers) { { 'Origin' => 'https://gitlab.com', 'access-control-request-method' => 'GET' } }
+
+ before do
+ stub_application_setting(jira_connect_proxy_url: 'https://gitlab.com')
+ end
+
+ it 'allows cross-origin requests', :aggregate_failures do
+ options '/-/jira_connect/subscriptions.json', headers: cors_request_headers
+
+ expect(response.headers['Access-Control-Allow-Origin']).to eq 'https://gitlab.com'
+ expect(response.headers['Access-Control-Allow-Methods']).to eq 'GET, OPTIONS'
+ expect(response.headers['Access-Control-Allow-Credentials']).to be_nil
+ end
+ end
+
+ describe 'OPTIONS /-/jira_connect/subscriptions/:id' do
+ let(:cors_request_headers) { { 'Origin' => 'https://gitlab.com', 'access-control-request-method' => 'DELETE' } }
+
+ before do
+ stub_application_setting(jira_connect_proxy_url: 'https://gitlab.com')
+ end
+
+ it 'allows cross-origin requests', :aggregate_failures do
+ options '/-/jira_connect/subscriptions/1', headers: cors_request_headers
+
+ expect(response.headers['Access-Control-Allow-Origin']).to eq 'https://gitlab.com'
+ expect(response.headers['Access-Control-Allow-Methods']).to eq 'DELETE, OPTIONS'
+ expect(response.headers['Access-Control-Allow-Credentials']).to be_nil
end
end
@@ -56,7 +101,7 @@ RSpec.describe JiraConnect::SubscriptionsController do
end
let(:jwt) { Atlassian::Jwt.encode({ iss: installation.client_key, qsh: qsh }, installation.shared_secret) }
- let(:cors_request_headers) { { 'Origin' => 'http://notgitlab.com' } }
+ let(:cors_request_headers) { { 'Origin' => 'https://gitlab.com' } }
let(:params) { { jwt: jwt, format: :json } }
before do
diff --git a/spec/requests/jira_connect/users_controller_spec.rb b/spec/requests/jira_connect/users_controller_spec.rb
index 6e927aaba91..c02bd324708 100644
--- a/spec/requests/jira_connect/users_controller_spec.rb
+++ b/spec/requests/jira_connect/users_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JiraConnect::UsersController do
+RSpec.describe JiraConnect::UsersController, feature_category: :integrations do
describe 'GET /-/jira_connect/users' do
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/jira_routing_spec.rb b/spec/requests/jira_routing_spec.rb
index e0e170044de..f1edb58bb10 100644
--- a/spec/requests/jira_routing_spec.rb
+++ b/spec/requests/jira_routing_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Jira referenced paths', type: :request do
+RSpec.describe 'Jira referenced paths', type: :request, feature_category: :integrations do
using RSpec::Parameterized::TableSyntax
let(:user) { create(:user) }
diff --git a/spec/requests/jwks_controller_spec.rb b/spec/requests/jwks_controller_spec.rb
index c9dcc238c29..ac9765c35d8 100644
--- a/spec/requests/jwks_controller_spec.rb
+++ b/spec/requests/jwks_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JwksController do
+RSpec.describe JwksController, feature_category: :authentication_and_authorization do
describe 'Endpoints from the parent Doorkeeper::OpenidConnect::DiscoveryController' do
it 'respond successfully' do
[
diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb
index e6916e02fde..00222cb1977 100644
--- a/spec/requests/jwt_controller_spec.rb
+++ b/spec/requests/jwt_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JwtController do
+RSpec.describe JwtController, feature_category: :authentication_and_authorization do
include_context 'parsed logs'
let(:service) { double(execute: {} ) }
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index 3529239a4d9..e5f03d16dda 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'Git LFS API and storage' do
+RSpec.describe 'Git LFS API and storage', feature_category: :source_code_management do
using RSpec::Parameterized::TableSyntax
include LfsHttpHelpers
@@ -378,6 +378,21 @@ RSpec.describe 'Git LFS API and storage' do
it_behaves_like 'LFS http 401 response'
end
+ context 'when deploy token is from an unrelated group to the project' do
+ let(:group) { create(:group) }
+ let(:deploy_token) { create(:deploy_token, :group, groups: [group]) }
+
+ it_behaves_like 'LFS http 401 response'
+ end
+
+ context 'when deploy token is from a parent group of the project and valid' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, group: group) }
+ let(:deploy_token) { create(:deploy_token, :group, groups: [group]) }
+
+ it_behaves_like 'an authorized request', renew_authorization: false
+ end
+
# TODO: We should fix this test case that causes flakyness by alternating the result of the above test cases.
context 'when Deploy Token is valid' do
let(:deploy_token) { create(:deploy_token, projects: [project]) }
@@ -842,14 +857,6 @@ RSpec.describe 'Git LFS API and storage' do
lfs_object.destroy!
end
- context 'with object storage disabled' do
- it "doesn't attempt to migrate file to object storage" do
- expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
-
- put_finalize(with_tempfile: true)
- end
- end
-
context 'with object storage enabled' do
context 'and direct upload enabled' do
let!(:fog_connection) do
@@ -911,18 +918,6 @@ RSpec.describe 'Git LFS API and storage' do
end
end
end
-
- context 'and background upload enabled' do
- before do
- stub_lfs_object_storage(background_upload: true)
- end
-
- it 'schedules migration of file to object storage' do
- expect(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async).with('LfsObjectUploader', 'LfsObject', :file, kind_of(Numeric))
-
- put_finalize(with_tempfile: true)
- end
- end
end
end
@@ -1036,7 +1031,7 @@ RSpec.describe 'Git LFS API and storage' do
end
describe 'to a forked project' do
- let_it_be(:upstream_project) { create(:project, :public) }
+ let_it_be_with_reload(:upstream_project) { create(:project, :public) }
let_it_be(:project_owner) { create(:user) }
let(:project) { fork_project(upstream_project, project_owner) }
@@ -1074,6 +1069,56 @@ RSpec.describe 'Git LFS API and storage' do
end
end
+ describe 'when user has push access to upstream project' do
+ before do
+ upstream_project.add_maintainer(user)
+ end
+
+ context 'an MR exists on target forked project' do
+ let(:allow_collaboration) { true }
+ let(:merge_request) do
+ create(:merge_request,
+ target_project: upstream_project,
+ source_project: project,
+ allow_collaboration: allow_collaboration)
+ end
+
+ before do
+ merge_request
+ end
+
+ context 'with allow_collaboration option set to true' do
+ context 'and request is sent by gitlab-workhorse to authorize the request' do
+ before do
+ put_authorize
+ end
+
+ it_behaves_like 'LFS http 200 workhorse response'
+ end
+
+ context 'and request is sent by gitlab-workhorse to finalize the upload' do
+ before do
+ put_finalize
+ end
+
+ it_behaves_like 'LFS http 200 response'
+ end
+ end
+
+ context 'with allow_collaboration option set to false' do
+ context 'request is sent by gitlab-workhorse to authorize the request' do
+ let(:allow_collaboration) { false }
+
+ before do
+ put_authorize
+ end
+
+ it_behaves_like 'forbidden'
+ end
+ end
+ end
+ end
+
describe 'and user does not have push access' do
it_behaves_like 'forbidden'
end
diff --git a/spec/requests/lfs_locks_api_spec.rb b/spec/requests/lfs_locks_api_spec.rb
index 0eb3cb4ca07..363a16f014b 100644
--- a/spec/requests/lfs_locks_api_spec.rb
+++ b/spec/requests/lfs_locks_api_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Git LFS File Locking API' do
+RSpec.describe 'Git LFS File Locking API', feature_category: :source_code_management do
include LfsHttpHelpers
include WorkhorseHelpers
diff --git a/spec/requests/mailgun/webhooks_controller_spec.rb b/spec/requests/mailgun/webhooks_controller_spec.rb
index ae6dc89d003..669401242b2 100644
--- a/spec/requests/mailgun/webhooks_controller_spec.rb
+++ b/spec/requests/mailgun/webhooks_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Mailgun::WebhooksController do
+RSpec.describe Mailgun::WebhooksController, feature_category: :team_planning do
let(:mailgun_signing_key) { 'abc123' }
let(:valid_signature) do
{
diff --git a/spec/requests/oauth/applications_controller_spec.rb b/spec/requests/oauth/applications_controller_spec.rb
index 78f0cedb56f..94ee08f6272 100644
--- a/spec/requests/oauth/applications_controller_spec.rb
+++ b/spec/requests/oauth/applications_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Oauth::ApplicationsController do
+RSpec.describe Oauth::ApplicationsController, feature_category: :authentication_and_authorization do
let_it_be(:user) { create(:user) }
let_it_be(:application) { create(:oauth_application, owner: user) }
let_it_be(:show_path) { oauth_application_path(application) }
diff --git a/spec/requests/oauth/authorizations_controller_spec.rb b/spec/requests/oauth/authorizations_controller_spec.rb
index 8d19c92865e..52188717210 100644
--- a/spec/requests/oauth/authorizations_controller_spec.rb
+++ b/spec/requests/oauth/authorizations_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Oauth::AuthorizationsController do
+RSpec.describe Oauth::AuthorizationsController, feature_category: :authentication_and_authorization do
let_it_be(:user) { create(:user) }
let_it_be(:application) { create(:oauth_application, redirect_uri: 'custom://test') }
let_it_be(:oauth_authorization_path) do
diff --git a/spec/requests/oauth/tokens_controller_spec.rb b/spec/requests/oauth/tokens_controller_spec.rb
index 507489d92cf..cdfad8cb59c 100644
--- a/spec/requests/oauth/tokens_controller_spec.rb
+++ b/spec/requests/oauth/tokens_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Oauth::TokensController do
+RSpec.describe Oauth::TokensController, feature_category: :authentication_and_authorization do
let(:cors_request_headers) { { 'Origin' => 'http://notgitlab.com' } }
let(:other_headers) { {} }
let(:headers) { cors_request_headers.merge(other_headers) }
diff --git a/spec/requests/oauth_tokens_spec.rb b/spec/requests/oauth_tokens_spec.rb
index f2fb380bde0..053bd317fcc 100644
--- a/spec/requests/oauth_tokens_spec.rb
+++ b/spec/requests/oauth_tokens_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'OAuth Tokens requests' do
+RSpec.describe 'OAuth Tokens requests', feature_category: :authentication_and_authorization do
let(:user) { create :user }
let(:application) { create :oauth_application, scopes: 'api' }
let(:grant_type) { 'authorization_code' }
diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb
index 3a40fec58e8..b45f4f1e39f 100644
--- a/spec/requests/openid_connect_spec.rb
+++ b/spec/requests/openid_connect_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'OpenID Connect requests' do
+RSpec.describe 'OpenID Connect requests', feature_category: :authentication_and_authorization do
let(:user) do
create(
:user,
diff --git a/spec/requests/profiles/notifications_controller_spec.rb b/spec/requests/profiles/notifications_controller_spec.rb
index d7dfb1c675d..21e166e04d3 100644
--- a/spec/requests/profiles/notifications_controller_spec.rb
+++ b/spec/requests/profiles/notifications_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'view user notifications' do
+RSpec.describe 'view user notifications', feature_category: :team_planning do
let(:user) do
create(:user) do |user|
user.emails.create!(email: 'original@example.com', confirmed_at: Time.current)
diff --git a/spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb b/spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb
index c5e7369b0a9..b0c7427fa81 100644
--- a/spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb
+++ b/spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects::Ci::PrometheusMetrics::HistogramsController' do
+RSpec.describe 'Projects::Ci::PrometheusMetrics::HistogramsController', feature_category: :pipeline_authoring do
let_it_be(:project) { create(:project, :public) }
describe 'POST /*namespace_id/:project_id/-/ci/prometheus_metrics/histograms' do
diff --git a/spec/requests/projects/cluster_agents_controller_spec.rb b/spec/requests/projects/cluster_agents_controller_spec.rb
index 914d5b17ba8..d7c791fa0c1 100644
--- a/spec/requests/projects/cluster_agents_controller_spec.rb
+++ b/spec/requests/projects/cluster_agents_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::ClusterAgentsController do
+RSpec.describe Projects::ClusterAgentsController, feature_category: :kubernetes_management do
let_it_be(:cluster_agent) { create(:cluster_agent) }
let(:project) { cluster_agent.project }
diff --git a/spec/requests/projects/clusters/integrations_controller_spec.rb b/spec/requests/projects/clusters/integrations_controller_spec.rb
index c05e3da675c..505b63e1ff6 100644
--- a/spec/requests/projects/clusters/integrations_controller_spec.rb
+++ b/spec/requests/projects/clusters/integrations_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::Clusters::IntegrationsController do
+RSpec.describe Projects::Clusters::IntegrationsController, feature_category: :integrations do
include AccessMatchersForController
shared_examples 'a secure endpoint' do
diff --git a/spec/requests/projects/commits_controller_spec.rb b/spec/requests/projects/commits_controller_spec.rb
index 158902c0ffd..f9e3ef82fc1 100644
--- a/spec/requests/projects/commits_controller_spec.rb
+++ b/spec/requests/projects/commits_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::CommitsController do
+RSpec.describe Projects::CommitsController, feature_category: :source_code_management do
context 'token authentication' do
context 'when public project' do
let_it_be(:public_project) { create(:project, :repository, :public) }
diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb
index 370febf82ff..3f9dd74c145 100644
--- a/spec/requests/projects/cycle_analytics_events_spec.rb
+++ b/spec/requests/projects/cycle_analytics_events_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'value stream analytics events' do
+RSpec.describe 'value stream analytics events', feature_category: :planning_analytics do
include CycleAnalyticsHelpers
let(:user) { create(:user) }
diff --git a/spec/requests/projects/environments_controller_spec.rb b/spec/requests/projects/environments_controller_spec.rb
index 66ab265fc0f..41ae2d434fa 100644
--- a/spec/requests/projects/environments_controller_spec.rb
+++ b/spec/requests/projects/environments_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::EnvironmentsController do
+RSpec.describe Projects::EnvironmentsController, feature_category: :continuous_delivery do
let_it_be_with_refind(:project) { create(:project, :repository) }
let(:environment) { create(:environment, name: 'production', project: project) }
diff --git a/spec/requests/projects/google_cloud/configuration_controller_spec.rb b/spec/requests/projects/google_cloud/configuration_controller_spec.rb
index 41593b8d7a7..1aa44d1a49a 100644
--- a/spec/requests/projects/google_cloud/configuration_controller_spec.rb
+++ b/spec/requests/projects/google_cloud/configuration_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::GoogleCloud::ConfigurationController do
+RSpec.describe Projects::GoogleCloud::ConfigurationController, feature_category: :kubernetes_management do
let_it_be(:project) { create(:project, :public) }
let_it_be(:url) { project_google_cloud_configuration_path(project) }
diff --git a/spec/requests/projects/google_cloud/databases_controller_spec.rb b/spec/requests/projects/google_cloud/databases_controller_spec.rb
index 4edef71f326..e91a51ce2ef 100644
--- a/spec/requests/projects/google_cloud/databases_controller_spec.rb
+++ b/spec/requests/projects/google_cloud/databases_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::GoogleCloud::DatabasesController, :snowplow do
+RSpec.describe Projects::GoogleCloud::DatabasesController, :snowplow, feature_category: :kubernetes_management do
shared_examples 'shared examples for database controller endpoints' do
include_examples 'requires `admin_project_google_cloud` role'
diff --git a/spec/requests/projects/google_cloud/deployments_controller_spec.rb b/spec/requests/projects/google_cloud/deployments_controller_spec.rb
index c777e8c1f69..d564a31f835 100644
--- a/spec/requests/projects/google_cloud/deployments_controller_spec.rb
+++ b/spec/requests/projects/google_cloud/deployments_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::GoogleCloud::DeploymentsController do
+RSpec.describe Projects::GoogleCloud::DeploymentsController, feature_category: :kubernetes_management do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:repository) { project.repository }
diff --git a/spec/requests/projects/google_cloud/gcp_regions_controller_spec.rb b/spec/requests/projects/google_cloud/gcp_regions_controller_spec.rb
index e77bcdb40b8..de4b96a2e01 100644
--- a/spec/requests/projects/google_cloud/gcp_regions_controller_spec.rb
+++ b/spec/requests/projects/google_cloud/gcp_regions_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::GoogleCloud::GcpRegionsController do
+RSpec.describe Projects::GoogleCloud::GcpRegionsController, feature_category: :kubernetes_management do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:repository) { project.repository }
diff --git a/spec/requests/projects/google_cloud/revoke_oauth_controller_spec.rb b/spec/requests/projects/google_cloud/revoke_oauth_controller_spec.rb
index 9bd8468767d..5965953cf6f 100644
--- a/spec/requests/projects/google_cloud/revoke_oauth_controller_spec.rb
+++ b/spec/requests/projects/google_cloud/revoke_oauth_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::GoogleCloud::RevokeOauthController do
+RSpec.describe Projects::GoogleCloud::RevokeOauthController, feature_category: :kubernetes_management do
include SessionHelpers
describe 'POST #create', :snowplow, :clean_gitlab_redis_sessions, :aggregate_failures do
diff --git a/spec/requests/projects/google_cloud/service_accounts_controller_spec.rb b/spec/requests/projects/google_cloud/service_accounts_controller_spec.rb
index d91e5a4f068..9b048f814ef 100644
--- a/spec/requests/projects/google_cloud/service_accounts_controller_spec.rb
+++ b/spec/requests/projects/google_cloud/service_accounts_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::GoogleCloud::ServiceAccountsController do
+RSpec.describe Projects::GoogleCloud::ServiceAccountsController, feature_category: :kubernetes_management do
let_it_be(:project) { create(:project, :public) }
describe 'GET index', :snowplow do
diff --git a/spec/requests/projects/harbor/artifacts_controller_spec.rb b/spec/requests/projects/harbor/artifacts_controller_spec.rb
index 310fbcf0a0f..d0ed93acaf7 100644
--- a/spec/requests/projects/harbor/artifacts_controller_spec.rb
+++ b/spec/requests/projects/harbor/artifacts_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::Harbor::ArtifactsController do
+RSpec.describe Projects::Harbor::ArtifactsController, feature_category: :build_artifacts do
it_behaves_like 'a harbor artifacts controller', anonymous_status_code: '302' do
let_it_be(:container) { create(:project) }
let_it_be(:harbor_integration) { create(:harbor_integration, project: container) }
diff --git a/spec/requests/projects/harbor/repositories_controller_spec.rb b/spec/requests/projects/harbor/repositories_controller_spec.rb
index 751becaa20a..7430ac5a64f 100644
--- a/spec/requests/projects/harbor/repositories_controller_spec.rb
+++ b/spec/requests/projects/harbor/repositories_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::Harbor::RepositoriesController do
+RSpec.describe Projects::Harbor::RepositoriesController, feature_category: :source_code_management do
it_behaves_like 'a harbor repositories controller', anonymous_status_code: '302' do
let_it_be(:container, reload: true) { create(:project) }
let_it_be(:harbor_integration) { create(:harbor_integration, project: container) }
diff --git a/spec/requests/projects/harbor/tags_controller_spec.rb b/spec/requests/projects/harbor/tags_controller_spec.rb
index 119d1c746ac..f1ac2f01c57 100644
--- a/spec/requests/projects/harbor/tags_controller_spec.rb
+++ b/spec/requests/projects/harbor/tags_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::Harbor::TagsController do
+RSpec.describe Projects::Harbor::TagsController, feature_category: :source_code_management do
it_behaves_like 'a harbor tags controller', anonymous_status_code: '302' do
let_it_be(:container) { create(:project) }
let_it_be(:harbor_integration) { create(:harbor_integration, project: container) }
diff --git a/spec/requests/projects/hook_logs_controller_spec.rb b/spec/requests/projects/hook_logs_controller_spec.rb
index 8b3ec307e53..c71906b4895 100644
--- a/spec/requests/projects/hook_logs_controller_spec.rb
+++ b/spec/requests/projects/hook_logs_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::HookLogsController do
+RSpec.describe Projects::HookLogsController, feature_category: :integrations do
let_it_be(:user) { create(:user) }
let_it_be_with_refind(:web_hook) { create(:project_hook) }
let_it_be_with_refind(:web_hook_log) { create(:web_hook_log, web_hook: web_hook) }
diff --git a/spec/requests/projects/incident_management/pagerduty_incidents_spec.rb b/spec/requests/projects/incident_management/pagerduty_incidents_spec.rb
index 32434435475..a25dcb7f299 100644
--- a/spec/requests/projects/incident_management/pagerduty_incidents_spec.rb
+++ b/spec/requests/projects/incident_management/pagerduty_incidents_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'PagerDuty webhook' do
+RSpec.describe 'PagerDuty webhook', feature_category: :incident_management do
let_it_be(:project) { create(:project) }
describe 'POST /incidents/pagerduty' do
diff --git a/spec/requests/projects/incident_management/timeline_events_spec.rb b/spec/requests/projects/incident_management/timeline_events_spec.rb
index f7dead4834d..22a1f654ee2 100644
--- a/spec/requests/projects/incident_management/timeline_events_spec.rb
+++ b/spec/requests/projects/incident_management/timeline_events_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Timeline Events' do
+RSpec.describe 'Timeline Events', feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:incident) { create(:incident, project: project) }
diff --git a/spec/requests/projects/integrations/shimos_controller_spec.rb b/spec/requests/projects/integrations/shimos_controller_spec.rb
index 7322143f87e..bd7af0bb4ac 100644
--- a/spec/requests/projects/integrations/shimos_controller_spec.rb
+++ b/spec/requests/projects/integrations/shimos_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Projects::Integrations::ShimosController do
+RSpec.describe ::Projects::Integrations::ShimosController, feature_category: :integrations do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user, developer_projects: [project]) }
let_it_be(:shimo_integration) { create(:shimo_integration, project: project) }
diff --git a/spec/requests/projects/issue_links_controller_spec.rb b/spec/requests/projects/issue_links_controller_spec.rb
index e5f40625cfa..0535156b4b8 100644
--- a/spec/requests/projects/issue_links_controller_spec.rb
+++ b/spec/requests/projects/issue_links_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::IssueLinksController do
+RSpec.describe Projects::IssueLinksController, feature_category: :team_planning do
let(:user) { create :user }
let(:project) { create(:project_empty_repo) }
let(:issue) { create :issue, project: project }
diff --git a/spec/requests/projects/issues/discussions_spec.rb b/spec/requests/projects/issues/discussions_spec.rb
index dcdca2d9c27..0f4a0bd2e5c 100644
--- a/spec/requests/projects/issues/discussions_spec.rb
+++ b/spec/requests/projects/issues/discussions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'issue discussions' do
+RSpec.describe 'issue discussions', feature_category: :team_planning do
describe 'GET /:namespace/:project/-/issues/:iid/discussions' do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/projects/issues_controller_spec.rb b/spec/requests/projects/issues_controller_spec.rb
index aa2ba5e114b..bbf200eaacd 100644
--- a/spec/requests/projects/issues_controller_spec.rb
+++ b/spec/requests/projects/issues_controller_spec.rb
@@ -2,12 +2,36 @@
require 'spec_helper'
-RSpec.describe Projects::IssuesController do
+RSpec.describe Projects::IssuesController, feature_category: :team_planning do
let_it_be(:issue) { create(:issue) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { issue.project }
let_it_be(:user) { issue.author }
+ describe 'GET #new' do
+ before do
+ login_as(user)
+ end
+
+ it_behaves_like "observability csp policy", described_class do
+ let(:tested_path) do
+ new_project_issue_path(project)
+ end
+ end
+ end
+
+ describe 'GET #show' do
+ before do
+ login_as(user)
+ end
+
+ it_behaves_like "observability csp policy", described_class do
+ let(:tested_path) do
+ project_issue_path(project, issue)
+ end
+ end
+ end
+
describe 'GET #discussions' do
before do
login_as(user)
diff --git a/spec/requests/projects/merge_requests/content_spec.rb b/spec/requests/projects/merge_requests/content_spec.rb
index 7e5ec6f64c4..6c58dcb5722 100644
--- a/spec/requests/projects/merge_requests/content_spec.rb
+++ b/spec/requests/projects/merge_requests/content_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'merge request content spec' do
+RSpec.describe 'merge request content spec', feature_category: :code_review do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
let_it_be(:merge_request) { create(:merge_request, :with_head_pipeline, target_project: project, source_project: project) }
diff --git a/spec/requests/projects/merge_requests/context_commit_diffs_spec.rb b/spec/requests/projects/merge_requests/context_commit_diffs_spec.rb
index ec65e8cf11e..10e57970704 100644
--- a/spec/requests/projects/merge_requests/context_commit_diffs_spec.rb
+++ b/spec/requests/projects/merge_requests/context_commit_diffs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge Requests Context Commit Diffs' do
+RSpec.describe 'Merge Requests Context Commit Diffs', feature_category: :code_review do
let_it_be(:sha1) { "33f3729a45c02fc67d00adb1b8bca394b0e761d9" }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/projects/merge_requests/creations_spec.rb b/spec/requests/projects/merge_requests/creations_spec.rb
index 842ad01656e..59e2047e1c7 100644
--- a/spec/requests/projects/merge_requests/creations_spec.rb
+++ b/spec/requests/projects/merge_requests/creations_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'merge requests creations' do
+RSpec.describe 'merge requests creations', feature_category: :code_review do
describe 'GET /:namespace/:project/merge_requests/new' do
include ProjectForksHelper
@@ -24,5 +24,17 @@ RSpec.describe 'merge requests creations' do
expect { get_new }.not_to exceed_query_limit(control)
end
+
+ it_behaves_like "observability csp policy", Projects::MergeRequests::CreationsController do
+ let(:tested_path) do
+ project_new_merge_request_path(project, merge_request: {
+ title: 'Some feature',
+ source_branch: 'fix',
+ target_branch: 'feature',
+ target_project: project,
+ source_project: project
+ })
+ end
+ end
end
end
diff --git a/spec/requests/projects/merge_requests/diffs_spec.rb b/spec/requests/projects/merge_requests/diffs_spec.rb
index 12990b54617..858acac7f0d 100644
--- a/spec/requests/projects/merge_requests/diffs_spec.rb
+++ b/spec/requests/projects/merge_requests/diffs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Merge Requests Diffs' do
+RSpec.describe 'Merge Requests Diffs', feature_category: :code_review do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
let_it_be(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) }
@@ -80,20 +80,6 @@ RSpec.describe 'Merge Requests Diffs' do
expect(response).to have_gitlab_http_status(:not_modified)
end
- context 'with check_etags_diffs_batch_before_write_cache flag turned off' do
- before do
- stub_feature_flags(check_etags_diffs_batch_before_write_cache: false)
- end
-
- it 'does not serialize diffs' do
- expect(PaginatedDiffSerializer).not_to receive(:new)
-
- go(headers: headers, page: 0, per_page: 5)
-
- expect(response).to have_gitlab_http_status(:not_modified)
- end
- end
-
context 'with the different user' do
let(:another_user) { create(:user) }
let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
diff --git a/spec/requests/projects/merge_requests_controller_spec.rb b/spec/requests/projects/merge_requests_controller_spec.rb
index 2ee86bb423b..f5f8b5c2d83 100644
--- a/spec/requests/projects/merge_requests_controller_spec.rb
+++ b/spec/requests/projects/merge_requests_controller_spec.rb
@@ -2,11 +2,24 @@
require 'spec_helper'
-RSpec.describe Projects::MergeRequestsController do
+RSpec.describe Projects::MergeRequestsController, feature_category: :source_code_management do
+ let_it_be(:merge_request) { create(:merge_request) }
+ let_it_be(:project) { merge_request.project }
+ let_it_be(:user) { merge_request.author }
+
+ describe 'GET #show' do
+ before do
+ login_as(user)
+ end
+
+ it_behaves_like "observability csp policy", described_class do
+ let(:tested_path) do
+ project_merge_request_path(project, merge_request)
+ end
+ end
+ end
+
describe 'GET #discussions' do
- let_it_be(:merge_request) { create(:merge_request) }
- let_it_be(:project) { merge_request.project }
- let_it_be(:user) { merge_request.author }
let_it_be(:discussion) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project) }
let_it_be(:discussion_reply) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project, in_reply_to: discussion) }
let_it_be(:state_event) { create(:resource_state_event, merge_request: merge_request) }
diff --git a/spec/requests/projects/merge_requests_discussions_spec.rb b/spec/requests/projects/merge_requests_discussions_spec.rb
index 305ca6147be..d82fa284a42 100644
--- a/spec/requests/projects/merge_requests_discussions_spec.rb
+++ b/spec/requests/projects/merge_requests_discussions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'merge requests discussions' do
+RSpec.describe 'merge requests discussions', feature_category: :source_code_management do
# Further tests can be found at merge_requests_controller_spec.rb
describe 'GET /:namespace/:project/-/merge_requests/:iid/discussions' do
let(:project) { create(:project, :repository, :public) }
diff --git a/spec/requests/projects/merge_requests_spec.rb b/spec/requests/projects/merge_requests_spec.rb
index 91153554e55..9600d1a3656 100644
--- a/spec/requests/projects/merge_requests_spec.rb
+++ b/spec/requests/projects/merge_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'merge requests actions' do
+RSpec.describe 'merge requests actions', feature_category: :source_code_management do
let_it_be(:project) { create(:project, :repository) }
let(:merge_request) do
diff --git a/spec/requests/projects/metrics/dashboards/builder_spec.rb b/spec/requests/projects/metrics/dashboards/builder_spec.rb
index 002acca2135..c929beaed70 100644
--- a/spec/requests/projects/metrics/dashboards/builder_spec.rb
+++ b/spec/requests/projects/metrics/dashboards/builder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects::Metrics::Dashboards::BuilderController' do
+RSpec.describe 'Projects::Metrics::Dashboards::BuilderController', feature_category: :metrics do
let_it_be(:project) { create(:project) }
let_it_be(:environment) { create(:environment, project: project) }
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/projects/metrics_dashboard_spec.rb b/spec/requests/projects/metrics_dashboard_spec.rb
index 61bfe1c6edf..01925f8345b 100644
--- a/spec/requests/projects/metrics_dashboard_spec.rb
+++ b/spec/requests/projects/metrics_dashboard_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects::MetricsDashboardController' do
+RSpec.describe 'Projects::MetricsDashboardController', feature_category: :metrics do
let_it_be(:project) { create(:project) }
let_it_be(:environment) { create(:environment, project: project) }
let_it_be(:environment2) { create(:environment, project: project) }
diff --git a/spec/requests/projects/ml/candidates_controller_spec.rb b/spec/requests/projects/ml/candidates_controller_spec.rb
new file mode 100644
index 00000000000..4a0fd1ce4f5
--- /dev/null
+++ b/spec/requests/projects/ml/candidates_controller_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::Ml::CandidatesController, feature_category: :mlops do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { project.first_owner }
+ let_it_be(:experiment) { create(:ml_experiments, project: project, user: user) }
+ let_it_be(:candidate) { create(:ml_candidates, experiment: experiment, user: user) }
+
+ let(:ff_value) { true }
+ let(:threshold) { 4 }
+ let(:candidate_iid) { candidate.iid }
+
+ before do
+ stub_feature_flags(ml_experiment_tracking: false)
+ stub_feature_flags(ml_experiment_tracking: project) if ff_value
+
+ sign_in(user)
+ end
+
+ shared_examples '404 if feature flag disabled' do
+ context 'when :ml_experiment_tracking disabled' do
+ let(:ff_value) { false }
+
+ it 'is 404' do
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ describe 'GET show' do
+ let(:params) { basic_params.merge(id: experiment.iid) }
+
+ before do
+ show_candidate
+ end
+
+ it 'renders the template' do
+ expect(response).to render_template('projects/ml/candidates/show')
+ end
+
+ # MR removing this xit https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104166
+ xit 'does not perform N+1 sql queries' do
+ control_count = ActiveRecord::QueryRecorder.new { show_candidate }
+
+ create_list(:ml_candidate_params, 3, candidate: candidate)
+ create_list(:ml_candidate_metrics, 3, candidate: candidate)
+
+ expect { show_candidate }.not_to exceed_all_query_limit(control_count).with_threshold(threshold)
+ end
+
+ context 'when candidate does not exist' do
+ let(:candidate_iid) { non_existing_record_id.to_s }
+
+ it 'returns 404' do
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ it_behaves_like '404 if feature flag disabled'
+ end
+
+ private
+
+ def show_candidate
+ get project_ml_candidate_path(project, candidate_iid)
+ end
+end
diff --git a/spec/requests/projects/ml/experiments_controller_spec.rb b/spec/requests/projects/ml/experiments_controller_spec.rb
index 67a2fe47dc8..f35f93b1e6c 100644
--- a/spec/requests/projects/ml/experiments_controller_spec.rb
+++ b/spec/requests/projects/ml/experiments_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::Ml::ExperimentsController do
+RSpec.describe Projects::Ml::ExperimentsController, feature_category: :mlops do
let_it_be(:project_with_feature) { create(:project, :repository) }
let_it_be(:user) { project_with_feature.first_owner }
let_it_be(:project_without_feature) do
@@ -17,7 +17,6 @@ RSpec.describe Projects::Ml::ExperimentsController do
let(:params) { basic_params }
let(:ff_value) { true }
- let(:threshold) { 4 }
let(:project) { project_with_feature }
let(:basic_params) { { namespace_id: project.namespace.to_param, project_id: project } }
@@ -48,11 +47,11 @@ RSpec.describe Projects::Ml::ExperimentsController do
end
it 'does not perform N+1 sql queries' do
- control_count = ActiveRecord::QueryRecorder.new { list_experiments }
+ control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { list_experiments }
create_list(:ml_experiments, 2, project: project, user: user)
- expect { list_experiments }.not_to exceed_all_query_limit(control_count).with_threshold(threshold)
+ expect { list_experiments }.not_to exceed_all_query_limit(control_count)
end
context 'when :ml_experiment_tracking is disabled for the project' do
@@ -77,7 +76,8 @@ RSpec.describe Projects::Ml::ExperimentsController do
expect(response).to render_template('projects/ml/experiments/show')
end
- it 'does not perform N+1 sql queries' do
+ # MR removing this xit https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104166
+ xit 'does not perform N+1 sql queries' do
control_count = ActiveRecord::QueryRecorder.new { show_experiment }
create_list(:ml_candidates, 2, :with_metrics_and_params, experiment: experiment)
diff --git a/spec/requests/projects/network_controller_spec.rb b/spec/requests/projects/network_controller_spec.rb
new file mode 100644
index 00000000000..954f9655558
--- /dev/null
+++ b/spec/requests/projects/network_controller_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::NetworkController, feature_category: :source_code_management do
+ let_it_be(:project) { create(:project, :repository, :private) }
+ let(:ref) { 'master' }
+
+ describe 'GET #show' do
+ subject { get project_network_path(project, ref) }
+
+ context 'when user is unauthorized' do
+ it 'shows 404' do
+ subject
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+
+ context 'when user is authorized' do
+ let(:user) { project.creator }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'renders content' do
+ subject
+ expect(response).to be_successful
+ end
+
+ context 'when ref_type is provided' do
+ subject { get project_network_path(project, ref, ref_type: 'heads') }
+
+ it 'assigns url with ref_type' do
+ subject
+ expect(assigns(:url)).to eq(project_network_path(project, ref, format: :json, ref_type: 'heads'))
+ end
+
+ context 'when the use_ref_type_parameter flag is disabled' do
+ before do
+ stub_feature_flags(use_ref_type_parameter: false)
+ end
+
+ it 'assigns url without ref_type' do
+ subject
+ expect(assigns(:url)).to eq(project_network_path(project, ref, format: :json))
+ end
+ end
+ end
+
+ it 'assigns url' do
+ subject
+ expect(assigns(:url)).to eq(project_network_path(project, ref, format: :json))
+ end
+ end
+ end
+end
diff --git a/spec/requests/projects/noteable_notes_spec.rb b/spec/requests/projects/noteable_notes_spec.rb
index 44ee50ca002..5699bf17b80 100644
--- a/spec/requests/projects/noteable_notes_spec.rb
+++ b/spec/requests/projects/noteable_notes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project noteable notes' do
+RSpec.describe 'Project noteable notes', feature_category: :team_planning do
describe '#index' do
let_it_be(:merge_request) { create(:merge_request) }
diff --git a/spec/requests/projects/packages/package_files_controller_spec.rb b/spec/requests/projects/packages/package_files_controller_spec.rb
index a6daf57f0fa..e5849be9f13 100644
--- a/spec/requests/projects/packages/package_files_controller_spec.rb
+++ b/spec/requests/projects/packages/package_files_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::Packages::PackageFilesController do
+RSpec.describe Projects::Packages::PackageFilesController, feature_category: :package_registry do
let_it_be(:project) { create(:project, :public) }
let_it_be(:package) { create(:package, project: project) }
let_it_be(:package_file) { create(:package_file, package: package) }
diff --git a/spec/requests/projects/pipelines_controller_spec.rb b/spec/requests/projects/pipelines_controller_spec.rb
index 1c6b1039aee..7f185ade339 100644
--- a/spec/requests/projects/pipelines_controller_spec.rb
+++ b/spec/requests/projects/pipelines_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::PipelinesController do
+RSpec.describe Projects::PipelinesController, feature_category: :continuous_integration do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
diff --git a/spec/requests/projects/redirect_controller_spec.rb b/spec/requests/projects/redirect_controller_spec.rb
index 3bbca3ca32b..e828c546198 100644
--- a/spec/requests/projects/redirect_controller_spec.rb
+++ b/spec/requests/projects/redirect_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe "Projects::RedirectController requests" do
+RSpec.describe "Projects::RedirectController requests", feature_category: :projects do
using RSpec::Parameterized::TableSyntax
let_it_be(:private_project) { create(:project, :private) }
diff --git a/spec/requests/projects/releases_controller_spec.rb b/spec/requests/projects/releases_controller_spec.rb
index 8e492125ace..d331142583d 100644
--- a/spec/requests/projects/releases_controller_spec.rb
+++ b/spec/requests/projects/releases_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Projects::ReleasesController' do
+RSpec.describe 'Projects::ReleasesController', feature_category: :release_orchestration do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/projects/settings/access_tokens_controller_spec.rb b/spec/requests/projects/settings/access_tokens_controller_spec.rb
index 17389cdcce7..defb35fd496 100644
--- a/spec/requests/projects/settings/access_tokens_controller_spec.rb
+++ b/spec/requests/projects/settings/access_tokens_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::Settings::AccessTokensController do
+RSpec.describe Projects::Settings::AccessTokensController, feature_category: :authentication_and_authorization do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:resource) { create(:project, group: group) }
diff --git a/spec/requests/projects/settings/integration_hook_logs_controller_spec.rb b/spec/requests/projects/settings/integration_hook_logs_controller_spec.rb
index 77daff901a1..6cd0df19468 100644
--- a/spec/requests/projects/settings/integration_hook_logs_controller_spec.rb
+++ b/spec/requests/projects/settings/integration_hook_logs_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::Settings::IntegrationHookLogsController do
+RSpec.describe Projects::Settings::IntegrationHookLogsController, feature_category: :integrations do
let_it_be(:user) { create(:user) }
let_it_be(:integration) { create(:datadog_integration) }
let_it_be_with_refind(:web_hook) { integration.service_hook }
diff --git a/spec/requests/projects/settings/packages_and_registries_controller_spec.rb b/spec/requests/projects/settings/packages_and_registries_controller_spec.rb
index 6d8a152c769..2806beadd4e 100644
--- a/spec/requests/projects/settings/packages_and_registries_controller_spec.rb
+++ b/spec/requests/projects/settings/packages_and_registries_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::Settings::PackagesAndRegistriesController do
+RSpec.describe Projects::Settings::PackagesAndRegistriesController, feature_category: :package_registry do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, namespace: user.namespace) }
diff --git a/spec/requests/projects/tags_controller_spec.rb b/spec/requests/projects/tags_controller_spec.rb
index b9531a2739c..c0b0b1728c2 100644
--- a/spec/requests/projects/tags_controller_spec.rb
+++ b/spec/requests/projects/tags_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::TagsController do
+RSpec.describe Projects::TagsController, feature_category: :source_code_management do
context 'token authentication' do
context 'when public project' do
let_it_be(:public_project) { create(:project, :repository, :public) }
diff --git a/spec/requests/projects/uploads_spec.rb b/spec/requests/projects/uploads_spec.rb
index de5ef36be7e..aec2636b69c 100644
--- a/spec/requests/projects/uploads_spec.rb
+++ b/spec/requests/projects/uploads_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'File uploads' do
+RSpec.describe 'File uploads', feature_category: :not_owned do
include WorkhorseHelpers
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/requests/projects/usage_quotas_spec.rb b/spec/requests/projects/usage_quotas_spec.rb
index 6e449a21804..60ab64c30c3 100644
--- a/spec/requests/projects/usage_quotas_spec.rb
+++ b/spec/requests/projects/usage_quotas_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project Usage Quotas' do
+RSpec.describe 'Project Usage Quotas', feature_category: :subscription_cost_management do
let_it_be(:project) { create(:project) }
let_it_be(:role) { :maintainer }
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/projects/work_items_spec.rb b/spec/requests/projects/work_items_spec.rb
index 4d7acc73d4f..056416d380d 100644
--- a/spec/requests/projects/work_items_spec.rb
+++ b/spec/requests/projects/work_items_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Work Items' do
+RSpec.describe 'Work Items', feature_category: :team_planning do
let_it_be(:work_item) { create(:work_item) }
let_it_be(:developer) { create(:user) }
diff --git a/spec/requests/projects_controller_spec.rb b/spec/requests/projects_controller_spec.rb
index d2200d5a4ec..f08f3578dc0 100644
--- a/spec/requests/projects_controller_spec.rb
+++ b/spec/requests/projects_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ProjectsController do
+RSpec.describe ProjectsController, feature_category: :projects do
context 'token authentication' do
context 'when public project' do
let_it_be(:public_project) { create(:project, :public) }
diff --git a/spec/requests/pwa_controller_spec.rb b/spec/requests/pwa_controller_spec.rb
index 7a295b17231..3971790c094 100644
--- a/spec/requests/pwa_controller_spec.rb
+++ b/spec/requests/pwa_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe PwaController do
+RSpec.describe PwaController, feature_category: :navigation do
describe 'GET #manifest' do
it 'responds with json' do
get manifest_path(format: :json)
@@ -10,6 +10,23 @@ RSpec.describe PwaController do
expect(response.body).to include('The complete DevOps platform.')
expect(response).to have_gitlab_http_status(:success)
end
+
+ context 'with customized appearance' do
+ let_it_be(:appearance) do
+ create(:appearance, title: 'Long name', short_title: 'Short name', description: 'This is a test')
+ end
+
+ it 'uses custom values', :aggregate_failures do
+ get manifest_path(format: :json)
+
+ expect(Gitlab::Json.parse(response.body)).to include({
+ 'description' => 'This is a test',
+ 'name' => 'Long name',
+ 'short_name' => 'Short name'
+ })
+ expect(response).to have_gitlab_http_status(:success)
+ end
+ end
end
describe 'GET #offline' do
diff --git a/spec/requests/rack_attack_global_spec.rb b/spec/requests/rack_attack_global_spec.rb
index f6b9bc527ac..643a98da441 100644
--- a/spec/requests/rack_attack_global_spec.rb
+++ b/spec/requests/rack_attack_global_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_caching do
+RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_caching,
+feature_category: :authentication_and_authorization do
include RackAttackSpecHelpers
include SessionHelpers
diff --git a/spec/requests/recursive_webhook_detection_spec.rb b/spec/requests/recursive_webhook_detection_spec.rb
index fe27c90b6c8..a74d4f9a603 100644
--- a/spec/requests/recursive_webhook_detection_spec.rb
+++ b/spec/requests/recursive_webhook_detection_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Recursive webhook detection', :sidekiq_inline, :clean_gitlab_redis_shared_state, :request_store do
+RSpec.describe 'Recursive webhook detection', :sidekiq_inline, :clean_gitlab_redis_shared_state, :request_store,
+feature_category: :integrations do
include StubRequests
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/robots_txt_spec.rb b/spec/requests/robots_txt_spec.rb
index 7c0b7d8117a..18a14677e0c 100644
--- a/spec/requests/robots_txt_spec.rb
+++ b/spec/requests/robots_txt_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Robots.txt Requests', :aggregate_failures do
+RSpec.describe 'Robots.txt Requests', :aggregate_failures, feature_category: :build do
before do
Gitlab::Testing::RobotsBlockerMiddleware.block_requests!
end
diff --git a/spec/requests/runner_setup_controller_spec.rb b/spec/requests/runner_setup_controller_spec.rb
index 665c896e30d..8d75b9e81b7 100644
--- a/spec/requests/runner_setup_controller_spec.rb
+++ b/spec/requests/runner_setup_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe RunnerSetupController do
+RSpec.describe RunnerSetupController, feature_category: :runner_fleet do
let(:user) { create(:user) }
before do
diff --git a/spec/requests/sandbox_controller_spec.rb b/spec/requests/sandbox_controller_spec.rb
index 4fc26580123..77913065380 100644
--- a/spec/requests/sandbox_controller_spec.rb
+++ b/spec/requests/sandbox_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe SandboxController do
+RSpec.describe SandboxController, feature_category: :not_owned do
describe 'GET #mermaid' do
it 'renders page without template' do
get sandbox_mermaid_path
diff --git a/spec/requests/search_controller_spec.rb b/spec/requests/search_controller_spec.rb
index 613732c19ea..98dda75a2b0 100644
--- a/spec/requests/search_controller_spec.rb
+++ b/spec/requests/search_controller_spec.rb
@@ -2,10 +2,11 @@
require 'spec_helper'
-RSpec.describe SearchController, type: :request do
+RSpec.describe SearchController, type: :request, feature_category: :global_search do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :public, :repository, :wiki_repo, name: 'awesome project', group: group) }
+ let_it_be(:projects) { create_list(:project, 5, :public, :repository, :wiki_repo) }
before do
login_as(user)
@@ -20,9 +21,16 @@ RSpec.describe SearchController, type: :request do
create(object, *creation_traits, creation_args)
control = ActiveRecord::QueryRecorder.new(skip_cached: false) { send_search_request(params) }
- create_list(object, 3, *creation_traits, creation_args)
+ expect(response.body).to include('search-results') # Confirm there are search results to prevent false positives
+
+ projects.each do |project|
+ creation_args[:source_project] = project if creation_args.key?(:source_project)
+ creation_args[:project] = project if creation_args.key?(:project)
+ create(object, *creation_traits, creation_args)
+ end
expect { send_search_request(params) }.not_to exceed_all_query_limit(control).with_threshold(threshold)
+ expect(response.body).to include('search-results') # Confirm there are search results to prevent false positives
end
end
@@ -33,26 +41,26 @@ RSpec.describe SearchController, type: :request do
let(:object) { :issue }
let(:creation_args) { { project: project, title: 'foo' } }
let(:params) { { search: 'foo', scope: 'issues' } }
- # there are 4 additional queries run for the logged in user:
- # (1) geo_nodes, (1) users, (2) broadcast_messages
- let(:threshold) { 4 }
+ # some N+1 queries still exist
+ # each issue runs an extra query for group namespaces
+ let(:threshold) { 1 }
it_behaves_like 'an efficient database result'
end
- context 'for merge_request scope' do
+ context 'for merge_requests scope' do
let(:creation_traits) { [:unique_branches] }
let(:object) { :merge_request }
let(:creation_args) { { source_project: project, title: 'bar' } }
let(:params) { { search: 'bar', scope: 'merge_requests' } }
- # there are 4 additional queries run for the logged in user:
- # - (1) geo_nodes, (1) users, (2) broadcast_messages
+ # some N+1 queries still exist
+ # each merge request runs an extra query for project routes
let(:threshold) { 4 }
it_behaves_like 'an efficient database result'
end
- context 'for project scope' do
+ context 'for projects scope' do
let(:creation_traits) { [:public] }
let(:object) { :project }
let(:creation_args) { { name: 'project' } }
@@ -63,12 +71,76 @@ RSpec.describe SearchController, type: :request do
# - one count for open MRs
# - one count for open Issues
# there are 4 additional queries run for the logged in user:
- # (1) geo_nodes, (1) users, (2) broadcast_messages
- let(:threshold) { 13 }
+ # (1) user preferences, (1) user statuses, (1) user details, (1) users
+ let(:threshold) { 17 }
it_behaves_like 'an efficient database result'
end
+ context 'for milestones scope' do
+ let(:object) { :milestone }
+ let(:creation_args) { { project: project } }
+ let(:params) { { search: 'title', scope: 'milestones' } }
+ let(:threshold) { 0 }
+
+ it_behaves_like 'an efficient database result'
+ end
+
+ context 'for users scope' do
+ let(:object) { :user }
+ let(:creation_args) { { name: 'georgia' } }
+ let(:params) { { search: 'georgia', scope: 'users' } }
+ let(:threshold) { 0 }
+
+ it_behaves_like 'an efficient database result'
+ end
+
+ context 'for notes scope' do
+ let(:creation_traits) { [:on_commit] }
+ let(:object) { :note }
+ let(:creation_args) { { project: project, note: 'hello world' } }
+ let(:params) { { search: 'hello world', scope: 'notes', project_id: project.id } }
+ let(:threshold) { 0 }
+
+ it_behaves_like 'an efficient database result'
+ end
+
+ context 'for blobs scope' do
+ # blobs are enabled for project search only in basic search
+ let(:params_for_one) { { search: 'test', project_id: project.id, scope: 'blobs', per_page: 1 } }
+ let(:params_for_many) { { search: 'test', project_id: project.id, scope: 'blobs', per_page: 5 } }
+
+ it 'avoids N+1 database queries' do
+ control = ActiveRecord::QueryRecorder.new { send_search_request(params_for_one) }
+ expect(response.body).to include('search-results') # Confirm search results to prevent false positives
+
+ expect { send_search_request(params_for_many) }.not_to exceed_query_limit(control.count)
+ expect(response.body).to include('search-results') # Confirm search results to prevent false positives
+ end
+ end
+
+ context 'for commits scope' do
+ let(:params_for_one) { { search: 'test', project_id: project.id, scope: 'commits', per_page: 1 } }
+ let(:params_for_many) { { search: 'test', project_id: project.id, scope: 'commits', per_page: 5 } }
+
+ it 'avoids N+1 database queries' do
+ control = ActiveRecord::QueryRecorder.new { send_search_request(params_for_one) }
+ expect(response.body).to include('search-results') # Confirm search results to prevent false positives
+
+ expect { send_search_request(params_for_many) }.not_to exceed_query_limit(control.count)
+ expect(response.body).to include('search-results') # Confirm search results to prevent false positives
+ end
+ end
+
+ context 'for code search' do
+ let(:params_for_code_search) { { search: 'blob: hello' } }
+
+ it 'sets scope to blobs if code search literals are used' do
+ send_search_request(params_for_code_search)
+ expect(response).to redirect_to(search_path(params_for_code_search.merge({ scope: 'blobs' })))
+ end
+ end
+
context 'when searching by SHA' do
let(:sha) { '6d394385cf567f80a8fd85055db1ab4c5295806f' }
diff --git a/spec/requests/self_monitoring_project_spec.rb b/spec/requests/self_monitoring_project_spec.rb
index 64c5f94657d..ce4dd10a52d 100644
--- a/spec/requests/self_monitoring_project_spec.rb
+++ b/spec/requests/self_monitoring_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Self-Monitoring project requests' do
+RSpec.describe 'Self-Monitoring project requests', feature_category: :projects do
let(:admin) { create(:admin) }
describe 'POST #create_self_monitoring_project' do
diff --git a/spec/requests/sessions_spec.rb b/spec/requests/sessions_spec.rb
index 95df181b7b0..7b3fd23980a 100644
--- a/spec/requests/sessions_spec.rb
+++ b/spec/requests/sessions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Sessions' do
+RSpec.describe 'Sessions', feature_category: :authentication_and_authorization do
context 'authentication', :allow_forgery_protection do
let(:user) { create(:user) }
diff --git a/spec/requests/terraform/services_controller_spec.rb b/spec/requests/terraform/services_controller_spec.rb
index 54f7348513e..928c57613fa 100644
--- a/spec/requests/terraform/services_controller_spec.rb
+++ b/spec/requests/terraform/services_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Terraform::ServicesController do
+RSpec.describe Terraform::ServicesController, feature_category: :package_registry do
describe 'GET /.well-known/terraform.json' do
subject { get '/.well-known/terraform.json' }
diff --git a/spec/requests/user_activity_spec.rb b/spec/requests/user_activity_spec.rb
index 148bb2d6fae..f9682d81640 100644
--- a/spec/requests/user_activity_spec.rb
+++ b/spec/requests/user_activity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Update of user activity' do
+RSpec.describe 'Update of user activity', feature_category: :users do
paths_to_visit = [
'/group',
'/group/project',
diff --git a/spec/requests/user_avatar_spec.rb b/spec/requests/user_avatar_spec.rb
index 1397741af18..4e3c2744d56 100644
--- a/spec/requests/user_avatar_spec.rb
+++ b/spec/requests/user_avatar_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Loading a user avatar' do
+RSpec.describe 'Loading a user avatar', feature_category: :users do
let(:user) { create(:user, :with_avatar) }
context 'when logged in' do
diff --git a/spec/requests/user_sends_malformed_strings_spec.rb b/spec/requests/user_sends_malformed_strings_spec.rb
index da533606be5..4c131bfe452 100644
--- a/spec/requests/user_sends_malformed_strings_spec.rb
+++ b/spec/requests/user_sends_malformed_strings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User sends malformed strings' do
+RSpec.describe 'User sends malformed strings', feature_category: :user_management do
include GitHttpHelpers
let(:null_byte) { "\u0000" }
diff --git a/spec/requests/user_spoofs_ip_spec.rb b/spec/requests/user_spoofs_ip_spec.rb
index 833dae78529..0244d60bc3b 100644
--- a/spec/requests/user_spoofs_ip_spec.rb
+++ b/spec/requests/user_spoofs_ip_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User spoofs their IP' do
+RSpec.describe 'User spoofs their IP', feature_category: :user_management do
it 'raises a 400 error' do
get '/nonexistent', headers: { 'Client-Ip' => '1.2.3.4', 'X-Forwarded-For' => '5.6.7.8' }
diff --git a/spec/requests/users/group_callouts_spec.rb b/spec/requests/users/group_callouts_spec.rb
index a8680c3add4..d186bf92bc7 100644
--- a/spec/requests/users/group_callouts_spec.rb
+++ b/spec/requests/users/group_callouts_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Group callouts' do
+RSpec.describe 'Group callouts', feature_category: :navigation do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
diff --git a/spec/requests/users/project_callouts_spec.rb b/spec/requests/users/project_callouts_spec.rb
index 98c00fef052..a15dd225e84 100644
--- a/spec/requests/users/project_callouts_spec.rb
+++ b/spec/requests/users/project_callouts_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project callouts' do
+RSpec.describe 'Project callouts', feature_category: :navigation do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb
index e78d4cc326e..608284c05f3 100644
--- a/spec/requests/users_controller_spec.rb
+++ b/spec/requests/users_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe UsersController do
+RSpec.describe UsersController, feature_category: :user_management do
# This user should have the same e-mail address associated with the GPG key prepared for tests
let(:user) { create(:user, email: GpgHelpers::User1.emails[0]) }
let(:private_user) { create(:user, private_profile: true) }
diff --git a/spec/requests/verifies_with_email_spec.rb b/spec/requests/verifies_with_email_spec.rb
index 34fda1cce4d..cac754a9cb1 100644
--- a/spec/requests/verifies_with_email_spec.rb
+++ b/spec/requests/verifies_with_email_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_redis_rate_limiting do
+RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_redis_rate_limiting,
+feature_category: :user_management do
include SessionHelpers
include EmailHelpers
@@ -78,15 +79,25 @@ RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_
end
context 'when the user is signing in from an unknown ip address' do
+ let(:ip_check_enabled) { true }
+
before do
+ stub_feature_flags(check_ip_address_for_email_verification: ip_check_enabled)
allow(AuthenticationEvent)
.to receive(:initial_login_or_known_ip_address?)
.and_return(false)
+
perform_enqueued_jobs { sign_in }
end
it_behaves_like 'send verification instructions'
it_behaves_like 'prompt for email verification'
+
+ context 'when the check_ip_address_for_email_verification feature flag is disabled' do
+ let(:ip_check_enabled) { false }
+
+ it_behaves_like 'not verifying with email'
+ end
end
end
@@ -187,6 +198,18 @@ RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_
expect(response).to redirect_to(users_successful_verification_path)
end
end
+
+ context 'when not completing identity verification and logging in with another account' do
+ let(:another_user) { create(:user) }
+
+ before do
+ post user_session_path, params: { user: { login: another_user.username, password: another_user.password } }
+ end
+
+ it 'does not redirect to the successful verification path' do
+ expect(response).not_to redirect_to(users_successful_verification_path)
+ end
+ end
end
context 'when signing in with a valid password' do
diff --git a/spec/requests/web_ide/remote_ide_controller_spec.rb b/spec/requests/web_ide/remote_ide_controller_spec.rb
new file mode 100644
index 00000000000..367c7527f10
--- /dev/null
+++ b/spec/requests/web_ide/remote_ide_controller_spec.rb
@@ -0,0 +1,145 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WebIde::RemoteIdeController, feature_category: :remote_development do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:user) { create(:user) }
+
+ let_it_be(:top_nav_partial) { 'layouts/header/_default' }
+
+ let_it_be(:connection_token) { 'random1Connection3Token7' }
+ let_it_be(:remote_path) { 'test/foo/README.md' }
+ let_it_be(:return_url) { 'https://example.com/-/original/location' }
+ let_it_be(:csp_nonce) { 'just=some=noncense' }
+
+ let(:remote_host) { 'my-remote-host.example.com:1234' }
+ let(:ff_vscode_web_ide) { true }
+
+ before do
+ sign_in(user)
+
+ stub_feature_flags(vscode_web_ide: ff_vscode_web_ide)
+
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:content_security_policy_nonce).and_return(csp_nonce)
+ end
+ end
+
+ shared_examples_for '404 response' do
+ it 'has not_found status' do
+ post_to_remote_ide
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ describe "#index" do
+ context "when feature flag is on *and* user is not using legacy Web IDE" do
+ before do
+ post_to_remote_ide
+ end
+
+ it "renders the correct layout" do
+ expect(response).to render_template(layout: 'fullscreen')
+ end
+
+ it "renders with minimal: true" do
+ # This indirectly tests that `minimal: true` was passed to the fullscreen layout
+ expect(response).not_to render_template(top_nav_partial)
+ end
+
+ it "renders root element with data" do
+ expected = {
+ connection_token: connection_token,
+ remote_host: remote_host,
+ remote_path: remote_path,
+ return_url: return_url,
+ csp_nonce: csp_nonce
+ }
+
+ expect(find_root_element_data).to eq(expected)
+ end
+
+ it "updates the content security policy with the correct connect sources" do
+ expect(find_csp_source('connect-src')).to include(
+ "ws://#{remote_host}",
+ "wss://#{remote_host}",
+ "http://#{remote_host}",
+ "https://#{remote_host}"
+ )
+ end
+
+ it "updates the content security policy with the correct frame sources" do
+ expect(find_csp_source('frame-src')).to include("https://*.vscode-cdn.net/")
+ end
+ end
+
+ context 'when remote_host does not have port' do
+ let(:remote_host) { "my-remote-host.example.com" }
+
+ before do
+ post_to_remote_ide
+ end
+
+ it "updates the content security policy with the correct remote_host" do
+ expect(find_csp_source('connect-src')).to include(
+ "ws://#{remote_host}",
+ "wss://#{remote_host}",
+ "http://#{remote_host}",
+ "https://#{remote_host}"
+ )
+ end
+
+ it 'renders remote_host in root element data' do
+ expect(find_root_element_data).to include(remote_host: remote_host)
+ end
+ end
+
+ context 'when feature flag is off' do
+ let(:ff_vscode_web_ide) { false }
+
+ it_behaves_like '404 response'
+ end
+
+ context "when the remote host is invalid" do
+ let(:remote_host) { 'invalid:host:1:1:' }
+
+ it_behaves_like '404 response'
+ end
+ end
+
+ def find_root_element_data
+ ide_attrs = Nokogiri::HTML.parse(response.body).at_css('#ide').attributes.transform_values(&:value)
+
+ {
+ connection_token: ide_attrs['data-connection-token'],
+ remote_host: ide_attrs['data-remote-host'],
+ remote_path: ide_attrs['data-remote-path'],
+ return_url: ide_attrs['data-return-url'],
+ csp_nonce: ide_attrs['data-csp-nonce']
+ }
+ end
+
+ def find_csp_source(key)
+ csp = response.headers['Content-Security-Policy']
+
+ # Transform "default-src foo bar; connect-src foo bar; script-src ..."
+ # into array of values for a single directive based on the given key
+ csp.split(';')
+ .map(&:strip)
+ .find { |entry| entry.starts_with?(key) }
+ .split(' ')
+ .drop(1)
+ end
+
+ def post_to_remote_ide
+ params = {
+ connection_token: connection_token,
+ return_url: return_url
+ }
+
+ post ide_remote_path(remote_host: remote_host, remote_path: remote_path), params: params
+ end
+end
diff --git a/spec/requests/whats_new_controller_spec.rb b/spec/requests/whats_new_controller_spec.rb
index d4976a2bba3..1e8cb514945 100644
--- a/spec/requests/whats_new_controller_spec.rb
+++ b/spec/requests/whats_new_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WhatsNewController, :clean_gitlab_redis_cache do
+RSpec.describe WhatsNewController, :clean_gitlab_redis_cache, feature_category: :navigation do
after do
ReleaseHighlight.instance_variable_set(:@file_paths, nil)
end
diff --git a/spec/routing/group_routing_spec.rb b/spec/routing/group_routing_spec.rb
index 68e619e5246..54fbe9e962d 100644
--- a/spec/routing/group_routing_spec.rb
+++ b/spec/routing/group_routing_spec.rb
@@ -83,6 +83,10 @@ RSpec.shared_examples 'groups routing' do
it 'routes to the observability controller manage method' do
expect(get("groups/#{group_path}/-/observability/manage")).to route_to('groups/observability#manage', group_id: group_path)
end
+
+ it 'routes to the usage quotas controller' do
+ expect(get("groups/#{group_path}/-/usage_quotas")).to route_to("groups/usage_quotas#index", group_id: group_path)
+ end
end
RSpec.describe "Groups", "routing" do
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 42196a7d8af..1b6a5182531 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -240,7 +240,7 @@ RSpec.describe 'project routing' do
it 'to #show' do
expect(get('/gitlab/gitlabhq/-/merge_requests/1.diff')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'diff')
expect(get('/gitlab/gitlabhq/-/merge_requests/1.patch')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'patch')
- expect(get('/gitlab/gitlabhq/-/merge_requests/1/diffs')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', tab: 'diffs')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/1/diffs')).to route_to('projects/merge_requests#diffs', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', tab: 'diffs')
expect(get('/gitlab/gitlabhq/-/merge_requests/1/commits')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', tab: 'commits')
expect(get('/gitlab/gitlabhq/-/merge_requests/1/pipelines')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', tab: 'pipelines')
end
@@ -248,7 +248,7 @@ RSpec.describe 'project routing' do
it 'to #show from scoped route' do
expect(get('/gitlab/gitlabhq/-/merge_requests/1.diff')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'diff')
expect(get('/gitlab/gitlabhq/-/merge_requests/1.patch')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'patch')
- expect(get('/gitlab/gitlabhq/-/merge_requests/1/diffs')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', tab: 'diffs')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/1/diffs')).to route_to('projects/merge_requests#diffs', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', tab: 'diffs')
end
it_behaves_like 'resource routing' do
@@ -301,6 +301,7 @@ RSpec.describe 'project routing' do
expect(get('/gitlab/gitlabhq/-/merge_requests/1/conflicts')).to route_to('projects/merge_requests/conflicts#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
end
end
+
# raw_project_snippet GET /:project_id/snippets/:id/raw(.:format) snippets#raw
# project_snippets GET /:project_id/snippets(.:format) snippets#index
# new_project_snippet GET /:project_id/snippets/new(.:format) snippets#new
diff --git a/spec/routing/user_routing_spec.rb b/spec/routing/user_routing_spec.rb
new file mode 100644
index 00000000000..7bb589565fa
--- /dev/null
+++ b/spec/routing/user_routing_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'user routing', :clean_gitlab_redis_sessions, feature_category: :authentication_and_authorization do
+ include SessionHelpers
+
+ context 'when GitHub OAuth on project import is cancelled' do
+ it_behaves_like 'redirecting a legacy path', '/users/auth?error=access_denied&state=xyz', '/users/sign_in'
+ end
+
+ context 'when GitHub OAuth on sign in is cancelled' do
+ before do
+ stub_session(auth_on_failure_path: '/projects/new#import_project')
+ end
+
+ context 'when all required parameters are present' do
+ it_behaves_like 'redirecting a legacy path',
+ '/users/auth?error=access_denied&state=xyz',
+ '/projects/new#import_project'
+ end
+
+ context 'when one of the required parameters is missing' do
+ it_behaves_like 'redirecting a legacy path',
+ '/users/auth?error=access_denied&state=',
+ '/auth'
+ end
+ end
+end
diff --git a/spec/routing/web_ide_routing_spec.rb b/spec/routing/web_ide_routing_spec.rb
new file mode 100644
index 00000000000..58c24189dfd
--- /dev/null
+++ b/spec/routing/web_ide_routing_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe "Web IDE routing", feature_category: :remote_development do
+ describe 'remote' do
+ it "routes to #index, without remote_path" do
+ expect(post("/-/ide/remote/my.env.gitlab.example.com%3A3443")).to route_to(
+ "web_ide/remote_ide#index",
+ remote_host: 'my.env.gitlab.example.com:3443'
+ )
+ end
+
+ it "routes to #index, with remote_path" do
+ expect(post("/-/ide/remote/my.env.gitlab.example.com%3A3443/foo/bar.dev/test.dir")).to route_to(
+ "web_ide/remote_ide#index",
+ remote_host: 'my.env.gitlab.example.com:3443',
+ remote_path: 'foo/bar.dev/test.dir'
+ )
+ end
+ end
+end
diff --git a/spec/rubocop/cop/feature_flag_usage_spec.rb b/spec/rubocop/cop/feature_flag_usage_spec.rb
new file mode 100644
index 00000000000..13f58ca7084
--- /dev/null
+++ b/spec/rubocop/cop/feature_flag_usage_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require 'rubocop_spec_helper'
+
+require_relative '../../../rubocop/cop/feature_flag_usage'
+
+RSpec.describe RuboCop::Cop::FeatureFlagUsage, feature_category: :scalability do
+ let(:msg) { described_class::MSG }
+
+ context 'when calling Feature.enabled?' do
+ it 'registers offence' do
+ expect_offense(<<~PATTERN)
+ Feature.enabled?(:fflag)
+ ^^^^^^^^^^^^^^^^^^^^^^^^ #{msg}
+ PATTERN
+ end
+
+ it 'registers offence when called with type parameter' do
+ expect_offense(<<~PATTERN)
+ Feature.enabled?(:fflag, type: :ops)
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{msg}
+ PATTERN
+ end
+
+ it 'registers offence when called under global namespace' do
+ expect_offense(<<~PATTERN)
+ ::Feature.enabled?(:fflag, type: :ops)
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{msg}
+ PATTERN
+ end
+ end
+
+ context 'when calling Feature.disabled?' do
+ it 'registers offence' do
+ expect_offense(<<~PATTERN)
+ Feature.disabled?(:fflag)
+ ^^^^^^^^^^^^^^^^^^^^^^^^^ #{msg}
+ PATTERN
+ end
+
+ it 'registers offence when called with type parameter' do
+ expect_offense(<<~PATTERN)
+ Feature.disabled?(:fflag, type: :ops)
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{msg}
+ PATTERN
+ end
+
+ it 'registers offence when called under global namespace' do
+ expect_offense(<<~PATTERN)
+ ::Feature.disabled?(:fflag, type: :ops)
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{msg}
+ PATTERN
+ end
+ end
+end
diff --git a/spec/rubocop/cop/filename_length_spec.rb b/spec/rubocop/cop/filename_length_spec.rb
index 1ea368d282f..a5bdce9a339 100644
--- a/spec/rubocop/cop/filename_length_spec.rb
+++ b/spec/rubocop/cop/filename_length_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rubocop_spec_helper'
-require 'rubocop/rspec/support'
require_relative '../../../rubocop/cop/filename_length'
RSpec.describe RuboCop::Cop::FilenameLength do
diff --git a/spec/rubocop/cop/gitlab/feature_available_usage_spec.rb b/spec/rubocop/cop/gitlab/feature_available_usage_spec.rb
index 30edd33a318..b15c298099d 100644
--- a/spec/rubocop/cop/gitlab/feature_available_usage_spec.rb
+++ b/spec/rubocop/cop/gitlab/feature_available_usage_spec.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
require 'rubocop_spec_helper'
-require 'rubocop'
-require 'rubocop/rspec/support'
require_relative '../../../../rubocop/cop/gitlab/feature_available_usage'
RSpec.describe RuboCop::Cop::Gitlab::FeatureAvailableUsage do
diff --git a/spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb b/spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb
index 6e60889f737..bfc0cebe203 100644
--- a/spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb
+++ b/spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
require 'rubocop_spec_helper'
-require 'rubocop'
-require 'rubocop/rspec/support'
require_relative '../../../../rubocop/cop/gitlab/mark_used_feature_flags'
RSpec.describe RuboCop::Cop::Gitlab::MarkUsedFeatureFlags do
diff --git a/spec/rubocop/cop/gitlab/strong_memoize_attr_spec.rb b/spec/rubocop/cop/gitlab/strong_memoize_attr_spec.rb
new file mode 100644
index 00000000000..0ed699f4e8c
--- /dev/null
+++ b/spec/rubocop/cop/gitlab/strong_memoize_attr_spec.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+require 'rubocop_spec_helper'
+require_relative '../../../../rubocop/cop/gitlab/strong_memoize_attr'
+
+RSpec.describe RuboCop::Cop::Gitlab::StrongMemoizeAttr do
+ context 'when strong_memoize() is the entire body of a method' do
+ context 'when the memoization name is the same as the method name' do
+ it 'registers an offense and autocorrects' do
+ expect_offense(<<~RUBY)
+ class Foo
+ def memoized_method
+ strong_memoize(:memoized_method) do
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `strong_memoize_attr`, instead of using `strong_memoize` directly
+ 'This is a memoized method'
+ end
+ end
+ end
+ RUBY
+
+ expect_correction(<<~RUBY)
+ class Foo
+ def memoized_method
+ 'This is a memoized method'
+ end
+ strong_memoize_attr :memoized_method
+ end
+ RUBY
+ end
+ end
+
+ context 'when the memoization name is different from the method name' do
+ it 'registers an offense and autocorrects' do
+ expect_offense(<<~RUBY)
+ class Foo
+ def enabled?
+ strong_memoize(:enabled) do
+ ^^^^^^^^^^^^^^^^^^^^^^^^ Use `strong_memoize_attr`, instead of using `strong_memoize` directly
+ true
+ end
+ end
+ end
+ RUBY
+
+ expect_correction(<<~RUBY)
+ class Foo
+ def enabled?
+ true
+ end
+ strong_memoize_attr :enabled?, :enabled
+ end
+ RUBY
+ end
+ end
+ end
+
+ context 'when strong_memoize() is not the entire body of the method' do
+ it 'registers an offense and does not autocorrect' do
+ expect_offense(<<~RUBY)
+ class Foo
+ def memoized_method
+ msg = 'This is a memoized method'
+
+ strong_memoize(:memoized_method) do
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `strong_memoize_attr`, instead of using `strong_memoize` directly
+ msg
+ end
+ end
+ end
+ RUBY
+
+ expect_no_corrections
+ end
+ end
+end
diff --git a/spec/rubocop/cop/graphql/descriptions_spec.rb b/spec/rubocop/cop/graphql/descriptions_spec.rb
index 8826e700fdf..0ff2812d6f6 100644
--- a/spec/rubocop/cop/graphql/descriptions_spec.rb
+++ b/spec/rubocop/cop/graphql/descriptions_spec.rb
@@ -16,6 +16,8 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions do
end
end
TYPE
+
+ expect_no_corrections
end
it 'adds an offense when description does not end in a period' do
@@ -44,6 +46,8 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions do
end
end
TYPE
+
+ expect_no_corrections
end
it 'adds an offense when description begins with "The"' do
@@ -58,6 +62,46 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions do
end
end
TYPE
+
+ expect_no_corrections
+ end
+
+ it 'adds an offense when description contains the demonstrative "this"' do
+ expect_offense(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ ^^^^^^^^^^^^^^^ #{described_class::MSG_CONTAINS_THIS}
+ GraphQL::Types::String,
+ null: false,
+ description: 'Description of this thing.'
+ end
+ end
+ TYPE
+
+ expect_correction(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Description of the thing.'
+ end
+ end
+ TYPE
+ end
+
+ it 'does not add an offense when a word does not contain the substring "this"' do
+ expect_no_offenses(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Description of thistle.'
+ end
+ end
+ TYPE
end
it 'does not add an offense when description is correct' do
@@ -96,6 +140,8 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions do
end
end
TYPE
+
+ expect_no_corrections
end
it 'adds an offense when description does not end in a period' do
@@ -124,6 +170,8 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions do
end
end
TYPE
+
+ expect_no_corrections
end
it 'adds an offense when description begins with "The"' do
@@ -138,6 +186,46 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions do
end
end
TYPE
+
+ expect_no_corrections
+ end
+
+ it 'adds an offense when description contains the demonstrative "this"' do
+ expect_offense(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ argument :a_thing,
+ ^^^^^^^^^^^^^^^^^^ #{described_class::MSG_CONTAINS_THIS}
+ GraphQL::Types::String,
+ null: false,
+ description: 'Description of this thing.'
+ end
+ end
+ TYPE
+
+ expect_correction(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ argument :a_thing,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Description of the thing.'
+ end
+ end
+ TYPE
+ end
+
+ it 'does not add an offense when a word does not contain the substring "this"' do
+ expect_no_offenses(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ argument :a_thing,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Description of thistle.'
+ end
+ end
+ TYPE
end
it 'does not add an offense when description is correct' do
@@ -164,6 +252,8 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions do
end
end
TYPE
+
+ expect_no_corrections
end
it 'adds an offense when description does not end in a period' do
@@ -186,6 +276,8 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions do
end
end
TYPE
+
+ expect_no_corrections
end
it 'adds an offense when description begins with "A"' do
@@ -197,6 +289,37 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions do
end
end
TYPE
+
+ expect_no_corrections
+ end
+
+ it 'adds an offense when description contains the demonstrative "this"' do
+ expect_offense(<<~TYPE.strip)
+ module Types
+ class FakeEnum < BaseEnum
+ value 'FOO', value: 'foo', description: 'Description of this issue.'
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{described_class::MSG_CONTAINS_THIS}
+ end
+ end
+ TYPE
+
+ expect_correction(<<~TYPE.strip)
+ module Types
+ class FakeEnum < BaseEnum
+ value 'FOO', value: 'foo', description: 'Description of the issue.'
+ end
+ end
+ TYPE
+ end
+
+ it 'does not add an offense when a word does not contain the substring "this"' do
+ expect_no_offenses(<<~TYPE.strip)
+ module Types
+ class FakeEnum < BaseEnum
+ value 'FOO', value: 'foo', description: 'Description of thistle.'
+ end
+ end
+ TYPE
end
it 'does not add an offense when description is correct (defined using `description:`)' do
@@ -220,8 +343,8 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions do
end
end
- describe 'autocorrecting descriptions without periods' do
- it 'can autocorrect' do
+ describe 'autocorrecting periods in descriptions' do
+ it 'autocorrects missing periods' do
expect_offense(<<~TYPE)
module Types
class FakeType < BaseObject
@@ -246,7 +369,20 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions do
TYPE
end
- it 'can autocorrect a heredoc' do
+ it 'does not autocorrect if periods exist' do
+ expect_no_offenses(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Behold! A description.'
+ end
+ end
+ TYPE
+ end
+
+ it 'autocorrects a heredoc' do
expect_offense(<<~TYPE)
module Types
class FakeType < BaseObject
@@ -274,5 +410,104 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions do
end
TYPE
end
+
+ it 'does not autocorrect a heredoc if periods exist' do
+ expect_no_offenses(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ GraphQL::Types::String,
+ null: false,
+ description: <<~DESC
+ Behold! A description.
+ DESC
+ end
+ end
+ TYPE
+ end
+ end
+
+ describe 'autocorrecting "this" to "the"' do
+ it 'autocorrects if "this" is found' do
+ expect_offense(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ ^^^^^^^^^^^^^^^ #{described_class::MSG_CONTAINS_THIS}
+ GraphQL::Types::String,
+ null: false,
+ description: 'Description of this thing.'
+ end
+ end
+ TYPE
+
+ expect_correction(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Description of the thing.'
+ end
+ end
+ TYPE
+ end
+
+ it 'does not autocorrect if "this" is not found' do
+ expect_no_offenses(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Description of the thing.'
+ end
+ end
+ TYPE
+ end
+
+ it 'autocorrects a heredoc if "this" is found' do
+ expect_offense(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ ^^^^^^^^^^^^^^^ #{described_class::MSG_CONTAINS_THIS}
+ GraphQL::Types::String,
+ null: false,
+ description: <<~DESC
+ Description of this thing.
+ DESC
+ end
+ end
+ TYPE
+
+ expect_correction(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ GraphQL::Types::String,
+ null: false,
+ description: <<~DESC
+ Description of the thing.
+ DESC
+ end
+ end
+ TYPE
+ end
+
+ it 'does not autocorrect a heredoc if "this" is not found' do
+ expect_no_offenses(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ GraphQL::Types::String,
+ null: false,
+ description: <<~DESC
+ Description of the thing.
+ DESC
+ end
+ end
+ TYPE
+ end
end
end
diff --git a/spec/rubocop/cop/migration/add_column_with_default_spec.rb b/spec/rubocop/cop/migration/add_column_with_default_spec.rb
deleted file mode 100644
index 865f567db44..00000000000
--- a/spec/rubocop/cop/migration/add_column_with_default_spec.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-
-require 'rubocop_spec_helper'
-require_relative '../../../../rubocop/cop/migration/add_column_with_default'
-
-RSpec.describe RuboCop::Cop::Migration::AddColumnWithDefault do
- let(:cop) { described_class.new }
-
- context 'when outside of a migration' do
- it 'does not register any offenses' do
- expect_no_offenses(<<~RUBY)
- def up
- add_column_with_default(:merge_request_diff_files, :artifacts, :boolean, default: true, allow_null: false)
- end
- RUBY
- end
- end
-
- context 'when in a migration' do
- before do
- allow(cop).to receive(:in_migration?).and_return(true)
- end
-
- it 'registers an offense' do
- expect_offense(<<~RUBY)
- def up
- add_column_with_default(:merge_request_diff_files, :artifacts, :boolean, default: true, allow_null: false)
- ^^^^^^^^^^^^^^^^^^^^^^^ `add_column_with_default` is deprecated, use `add_column` instead
- end
- RUBY
- end
- end
-end
diff --git a/spec/rubocop/cop/migration/add_limit_to_text_columns_spec.rb b/spec/rubocop/cop/migration/add_limit_to_text_columns_spec.rb
index 85a86a27c48..a6a072e2caf 100644
--- a/spec/rubocop/cop/migration/add_limit_to_text_columns_spec.rb
+++ b/spec/rubocop/cop/migration/add_limit_to_text_columns_spec.rb
@@ -37,8 +37,8 @@ RSpec.describe RuboCop::Cop::Migration::AddLimitToTextColumns do
add_column :test_text_limits, :email, :text
^^^^^^^^^^ #{msg}
- add_column_with_default :test_text_limits, :role, :text, default: 'default'
- ^^^^^^^^^^^^^^^^^^^^^^^ #{msg}
+ add_column :test_text_limits, :role, :text, default: 'default'
+ ^^^^^^^^^^ #{msg}
change_column_type_concurrently :test_text_limits, :test_id, :text
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{msg}
@@ -67,7 +67,7 @@ RSpec.describe RuboCop::Cop::Migration::AddLimitToTextColumns do
end
add_column :test_text_limits, :email, :text
- add_column_with_default :test_text_limits, :role, :text, default: 'default'
+ add_column :test_text_limits, :role, :text, default: 'default'
change_column_type_concurrently :test_text_limits, :test_id, :text
add_text_limit :test_text_limits, :name, 255
@@ -115,7 +115,7 @@ RSpec.describe RuboCop::Cop::Migration::AddLimitToTextColumns do
end
add_column :test_text_limits, :email, :text, array: true
- add_column_with_default :test_text_limits, :role, :text, default: [], array: true
+ add_column :test_text_limits, :role, :text, default: [], array: true
change_column_type_concurrently :test_text_limits, :test_id, :text, array: true
end
end
@@ -141,8 +141,8 @@ RSpec.describe RuboCop::Cop::Migration::AddLimitToTextColumns do
add_column :test_text_limits, :email, :text
^^^^^^^^^^ #{msg}
- add_column_with_default :test_text_limits, :role, :text, default: 'default'
- ^^^^^^^^^^^^^^^^^^^^^^^ #{msg}
+ add_column :test_text_limits, :role, :text, default: 'default'
+ ^^^^^^^^^^ #{msg}
change_column_type_concurrently :test_text_limits, :test_id, :text
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{msg}
@@ -170,7 +170,7 @@ RSpec.describe RuboCop::Cop::Migration::AddLimitToTextColumns do
end
add_column :encrypted_test_text_limits, :encrypted_email, :text
- add_column_with_default :encrypted_test_text_limits, :encrypted_role, :text, default: 'default'
+ add_column :encrypted_test_text_limits, :encrypted_role, :text, default: 'default'
change_column_type_concurrently :encrypted_test_text_limits, :encrypted_test_id, :text
end
end
@@ -194,7 +194,7 @@ RSpec.describe RuboCop::Cop::Migration::AddLimitToTextColumns do
add_column :no_offense_on_down, :email, :text
- add_column_with_default :no_offense_on_down, :role, :text, default: 'default'
+ add_column :no_offense_on_down, :role, :text, default: 'default'
end
end
RUBY
@@ -215,7 +215,7 @@ RSpec.describe RuboCop::Cop::Migration::AddLimitToTextColumns do
end
add_column :test_text_limits, :email, :text
- add_column_with_default :test_text_limits, :role, :text, default: 'default'
+ add_column :test_text_limits, :role, :text, default: 'default'
change_column_type_concurrently :test_text_limits, :test_id, :text
end
end
diff --git a/spec/rubocop/cop/migration/batch_migrations_post_only_spec.rb b/spec/rubocop/cop/migration/batch_migrations_post_only_spec.rb
new file mode 100644
index 00000000000..b5e2e83e788
--- /dev/null
+++ b/spec/rubocop/cop/migration/batch_migrations_post_only_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'rubocop_spec_helper'
+require_relative '../../../../rubocop/cop/migration/batch_migrations_post_only'
+
+RSpec.describe RuboCop::Cop::Migration::BatchMigrationsPostOnly do
+ let(:cop) { described_class.new }
+
+ before do
+ allow(cop).to receive(:in_post_deployment_migration?).and_return post_migration?
+ end
+
+ context 'when methods appear in a regular migration' do
+ let(:post_migration?) { false }
+
+ it "does not allow 'ensure_batched_background_migration_is_finished' to be called" do
+ expect_offense(<<~CODE)
+ ensure_batched_background_migration_is_finished
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This method must only be used in post-deployment migrations.
+ CODE
+ end
+
+ it "does not allow 'queue_batched_background_migration' to be called" do
+ expect_offense(<<~CODE)
+ queue_batched_background_migration
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This method must only be used in post-deployment migrations.
+ CODE
+ end
+
+ it "does not allow 'delete_batched_background_migration' to be called" do
+ expect_offense(<<~CODE)
+ delete_batched_background_migration
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This method must only be used in post-deployment migrations.
+ CODE
+ end
+
+ it "does not allow 'ensure_batched_background_migration_is_finished' to be called" do
+ expect_offense(<<~CODE)
+ finalize_batched_background_migration
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This method must only be used in post-deployment migrations.
+ CODE
+ end
+
+ it 'allows arbitrary other method to be called' do
+ expect_no_offenses(<<~CODE)
+ foo
+ CODE
+ end
+ end
+
+ context 'when methods appear in a post-deployment migration' do
+ let(:post_migration?) { true }
+
+ it "allows 'ensure_batched_background_migration_is_finished' to be called" do
+ expect_no_offenses(<<~CODE)
+ ensure_batched_background_migration_is_finished
+ CODE
+ end
+
+ it "allows 'queue_batched_background_migration' to be called" do
+ expect_no_offenses(<<~CODE)
+ queue_batched_background_migration
+ CODE
+ end
+
+ it "allows 'delete_batched_background_migration' to be called" do
+ expect_no_offenses(<<~CODE)
+ delete_batched_background_migration
+ CODE
+ end
+
+ it "allows 'ensure_batched_background_migration_is_finished' to be called" do
+ expect_no_offenses(<<~CODE)
+ finalize_batched_background_migration
+ CODE
+ end
+
+ it 'allows arbitrary other method to be called' do
+ expect_no_offenses(<<~CODE)
+ foo
+ CODE
+ end
+ end
+end
diff --git a/spec/rubocop/cop/migration/prevent_strings_spec.rb b/spec/rubocop/cop/migration/prevent_strings_spec.rb
index f1adeae6786..455b7765aa2 100644
--- a/spec/rubocop/cop/migration/prevent_strings_spec.rb
+++ b/spec/rubocop/cop/migration/prevent_strings_spec.rb
@@ -27,8 +27,8 @@ RSpec.describe RuboCop::Cop::Migration::PreventStrings do
add_column(:users, :bio, :string)
^^^^^^^^^^ %{msg}
- add_column_with_default(:users, :url, :string, default: '/-/user', allow_null: false, limit: 255)
- ^^^^^^^^^^^^^^^^^^^^^^^ %{msg}
+ add_column(:users, :url, :string, default: '/-/user', allow_null: false, limit: 255)
+ ^^^^^^^^^^ %{msg}
change_column_type_concurrently :users, :commit_id, :string
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ %{msg}
@@ -67,7 +67,7 @@ RSpec.describe RuboCop::Cop::Migration::PreventStrings do
end
add_column(:users, :bio, :text)
- add_column_with_default(:users, :url, :text, default: '/-/user', allow_null: false, limit: 255)
+ add_column(:users, :url, :text, default: '/-/user', allow_null: false, limit: 255)
change_column_type_concurrently :users, :commit_id, :text
end
end
@@ -86,7 +86,7 @@ RSpec.describe RuboCop::Cop::Migration::PreventStrings do
end
add_column :test_string_arrays, :email, :string, array: true
- add_column_with_default :test_string_arrays, :role, :string, default: [], array: true
+ add_column :test_string_arrays, :role, :string, default: [], array: true
change_column_type_concurrently :test_string_arrays, :test_id, :string, array: true
end
end
@@ -112,7 +112,7 @@ RSpec.describe RuboCop::Cop::Migration::PreventStrings do
end
add_column(:users, :bio, :string)
- add_column_with_default(:users, :url, :string, default: '/-/user', allow_null: false, limit: 255)
+ add_column(:users, :url, :string, default: '/-/user', allow_null: false, limit: 255)
change_column_type_concurrently :users, :commit_id, :string
end
end
@@ -133,7 +133,7 @@ RSpec.describe RuboCop::Cop::Migration::PreventStrings do
end
add_column(:users, :bio, :string)
- add_column_with_default(:users, :url, :string, default: '/-/user', allow_null: false, limit: 255)
+ add_column(:users, :url, :string, default: '/-/user', allow_null: false, limit: 255)
change_column_type_concurrently :users, :commit_id, :string
end
end
diff --git a/spec/rubocop/cop/migration/versioned_migration_class_spec.rb b/spec/rubocop/cop/migration/versioned_migration_class_spec.rb
index b44f5d64a62..506e3146afa 100644
--- a/spec/rubocop/cop/migration/versioned_migration_class_spec.rb
+++ b/spec/rubocop/cop/migration/versioned_migration_class_spec.rb
@@ -6,7 +6,7 @@ require_relative '../../../../rubocop/cop/migration/versioned_migration_class'
RSpec.describe RuboCop::Cop::Migration::VersionedMigrationClass do
let(:migration) do
<<~SOURCE
- class TestMigration < Gitlab::Database::Migration[1.0]
+ class TestMigration < Gitlab::Database::Migration[2.1]
def up
execute 'select 1'
end
@@ -49,23 +49,23 @@ RSpec.describe RuboCop::Cop::Migration::VersionedMigrationClass do
it 'adds an offence if inheriting from ActiveRecord::Migration' do
expect_offense(<<~RUBY)
class MyMigration < ActiveRecord::Migration[6.1]
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't inherit from ActiveRecord::Migration but use Gitlab::Database::Migration[1.0] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't inherit from ActiveRecord::Migration but use Gitlab::Database::Migration[2.1] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.
end
RUBY
end
it 'adds an offence if including Gitlab::Database::MigrationHelpers directly' do
expect_offense(<<~RUBY)
- class MyMigration < Gitlab::Database::Migration[1.0]
+ class MyMigration < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't include migration helper modules directly. Inherit from Gitlab::Database::Migration[1.0] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't include migration helper modules directly. Inherit from Gitlab::Database::Migration[2.1] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.
end
RUBY
end
it 'excludes ActiveRecord classes defined inside the migration' do
expect_no_offenses(<<~RUBY)
- class TestMigration < Gitlab::Database::Migration[1.0]
+ class TestMigration < Gitlab::Database::Migration[2.1]
class TestModel < ApplicationRecord
end
diff --git a/spec/rubocop/cop/performance/readlines_each_spec.rb b/spec/rubocop/cop/performance/readlines_each_spec.rb
index d876cbf79a5..11e2cee9262 100644
--- a/spec/rubocop/cop/performance/readlines_each_spec.rb
+++ b/spec/rubocop/cop/performance/readlines_each_spec.rb
@@ -6,7 +6,7 @@ require_relative '../../../../rubocop/cop/performance/readlines_each'
RSpec.describe RuboCop::Cop::Performance::ReadlinesEach do
let(:message) { 'Avoid `IO.readlines.each`, since it reads contents into memory in full. Use `IO.each_line` or `IO.each` instead.' }
- shared_examples_for(:class_read) do |klass|
+ shared_examples_for('class read') do |klass|
context "and it is called as a class method on #{klass}" do
it 'flags it as an offense' do
leading_readline = "#{klass}.readlines(file_path)."
@@ -29,7 +29,7 @@ RSpec.describe RuboCop::Cop::Performance::ReadlinesEach do
context 'when reading all lines using IO.readlines.each' do
%w(IO File).each do |klass|
- it_behaves_like(:class_read, klass)
+ it_behaves_like('class read', klass)
end
context 'and it is called as an instance method on a return value' do
diff --git a/spec/rubocop/cop/rspec/avoid_test_prof_spec.rb b/spec/rubocop/cop/rspec/avoid_test_prof_spec.rb
new file mode 100644
index 00000000000..b180134b226
--- /dev/null
+++ b/spec/rubocop/cop/rspec/avoid_test_prof_spec.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require 'rubocop_spec_helper'
+require 'rspec-parameterized'
+
+require_relative '../../../../rubocop/cop/rspec/avoid_test_prof'
+
+RSpec.describe RuboCop::Cop::RSpec::AvoidTestProf, feature_category: :not_owned do
+ using RSpec::Parameterized::TableSyntax
+
+ context 'when there are offenses' do
+ where(:method_call, :method_name, :alternatives) do
+ 'let_it_be(:user)' | 'let_it_be' | '`let` or `let!`'
+ 'let_it_be_with_reload(:user)' | 'let_it_be_with_reload' | '`let` or `let!`'
+ 'let_it_be_with_refind(:user)' | 'let_it_be_with_refind' | '`let` or `let!`'
+ 'before_all' | 'before_all' | '`before` or `before(:all)`'
+ end
+
+ with_them do
+ it 'registers the offense' do
+ error_message = "Prefer #{alternatives} over `#{method_name}` in migration specs. " \
+ 'See ' \
+ 'https://docs.gitlab.com/ee/development/testing_guide/best_practices.html' \
+ '#testprof-in-migration-specs'
+
+ expect_offense(<<~RUBY)
+ describe 'foo' do
+ #{method_call} { table(:users) }
+ #{'^' * method_call.size} #{error_message}
+ end
+ RUBY
+ end
+ end
+ end
+
+ context 'when there are no offenses' do
+ where(method_call: %w[let(:user) let!(:user) before before(:all)])
+
+ with_them do
+ it 'does not register an offense' do
+ expect_no_offenses(<<~RUBY)
+ describe 'foo' do
+ #{method_call} { table(:users) }
+ end
+ RUBY
+ end
+ end
+ end
+end
diff --git a/spec/rubocop/cop/rspec/timecop_freeze_spec.rb b/spec/rubocop/cop/rspec/timecop_freeze_spec.rb
deleted file mode 100644
index 4361f587da3..00000000000
--- a/spec/rubocop/cop/rspec/timecop_freeze_spec.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# frozen_string_literal: true
-
-require 'rubocop_spec_helper'
-
-require_relative '../../../../rubocop/cop/rspec/timecop_freeze'
-
-RSpec.describe RuboCop::Cop::RSpec::TimecopFreeze do
- context 'when calling Timecop.freeze' do
- it 'registers an offense and corrects', :aggregate_failures do
- expect_offense(<<~CODE)
- Timecop.freeze(Time.current) { example.run }
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not use `Timecop.freeze`, use `freeze_time` instead. [...]
- CODE
-
- expect_correction(<<~CODE)
- freeze_time(Time.current) { example.run }
- CODE
- end
- end
-
- context 'when calling a different method on Timecop' do
- it 'does not register an offense' do
- expect_no_offenses(<<~CODE)
- Timecop.travel(Time.current)
- CODE
- end
- end
-end
diff --git a/spec/rubocop/cop/rspec/timecop_travel_spec.rb b/spec/rubocop/cop/rspec/timecop_travel_spec.rb
deleted file mode 100644
index 89c46ff6c59..00000000000
--- a/spec/rubocop/cop/rspec/timecop_travel_spec.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# frozen_string_literal: true
-
-require 'rubocop_spec_helper'
-
-require_relative '../../../../rubocop/cop/rspec/timecop_travel'
-
-RSpec.describe RuboCop::Cop::RSpec::TimecopTravel do
- context 'when calling Timecop.travel' do
- it 'registers an offense and corrects', :aggregate_failures do
- expect_offense(<<~CODE)
- Timecop.travel(1.day.ago) { create(:issue) }
- ^^^^^^^^^^^^^^^^^^^^^^^^^ Do not use `Timecop.travel`, use `travel_to` instead. [...]
- CODE
-
- expect_correction(<<~CODE)
- travel_to(1.day.ago) { create(:issue) }
- CODE
- end
- end
-
- context 'when calling a different method on Timecop' do
- it 'does not register an offense' do
- expect_no_offenses(<<~CODE)
- Timecop.freeze { create(:issue) }
- CODE
- end
- end
-end
diff --git a/spec/rubocop/cop/user_admin_spec.rb b/spec/rubocop/cop/user_admin_spec.rb
index 99e87d619c0..21bf027324b 100644
--- a/spec/rubocop/cop/user_admin_spec.rb
+++ b/spec/rubocop/cop/user_admin_spec.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
require 'rubocop_spec_helper'
-
-require 'rubocop'
require_relative '../../../rubocop/cop/user_admin'
RSpec.describe RuboCop::Cop::UserAdmin do
diff --git a/spec/rubocop/formatter/graceful_formatter_spec.rb b/spec/rubocop/formatter/graceful_formatter_spec.rb
index 1ed8533ac16..d76e566e2b4 100644
--- a/spec/rubocop/formatter/graceful_formatter_spec.rb
+++ b/spec/rubocop/formatter/graceful_formatter_spec.rb
@@ -1,9 +1,7 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require 'rspec-parameterized'
-require 'rubocop'
-require 'rubocop/rspec/support'
require 'stringio'
require_relative '../../../rubocop/formatter/graceful_formatter'
diff --git a/spec/rubocop/support_workaround.rb b/spec/rubocop/support_workaround.rb
new file mode 100644
index 00000000000..d83aa8a7232
--- /dev/null
+++ b/spec/rubocop/support_workaround.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+# This replicates `require 'rubocop/rspec/support'` to workaround the issue
+# in https://gitlab.com/gitlab-org/gitlab/-/issues/382452.
+#
+# All helpers are only included in rubocop specs (type: :rubocop/:rubocop_rspec).
+
+require 'rubocop/rspec/cop_helper'
+require 'rubocop/rspec/host_environment_simulation_helper'
+require 'rubocop/rspec/shared_contexts'
+require 'rubocop/rspec/expect_offense'
+require 'rubocop/rspec/parallel_formatter'
+
+RSpec.configure do |config|
+ config.include CopHelper, type: :rubocop
+ config.include CopHelper, type: :rubocop_rspec
+ config.include HostEnvironmentSimulatorHelper, type: :rubocop
+ config.include HostEnvironmentSimulatorHelper, type: :rubocop_rspec
+ config.include_context 'config', :config
+ config.include_context 'isolated environment', :isolated_environment
+ config.include_context 'maintain registry', :restore_registry
+ config.include_context 'ruby 2.0', :ruby20
+ config.include_context 'ruby 2.1', :ruby21
+ config.include_context 'ruby 2.2', :ruby22
+ config.include_context 'ruby 2.3', :ruby23
+ config.include_context 'ruby 2.4', :ruby24
+ config.include_context 'ruby 2.5', :ruby25
+ config.include_context 'ruby 2.6', :ruby26
+ config.include_context 'ruby 2.7', :ruby27
+ config.include_context 'ruby 3.0', :ruby30
+ config.include_context 'ruby 3.1', :ruby31
+ config.include_context 'ruby 3.2', :ruby32
+end
diff --git a/spec/rubocop_spec_helper.rb b/spec/rubocop_spec_helper.rb
index d57461960f2..9884cdd0272 100644
--- a/spec/rubocop_spec_helper.rb
+++ b/spec/rubocop_spec_helper.rb
@@ -6,9 +6,9 @@ require 'fast_spec_helper'
# To prevent load order issues we need to require `rubocop` first.
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47008
require 'rubocop'
-require 'rubocop/rspec/support'
+require 'rubocop/rspec/shared_contexts/default_rspec_language_config_context'
-require_relative './support/shared_contexts/rubocop_default_rspec_language_config_context'
+require_relative 'rubocop/support_workaround'
RSpec.configure do |config|
config.define_derived_metadata(file_path: %r{spec/rubocop}) do |metadata|
diff --git a/spec/scripts/lib/glfm/shared_spec.rb b/spec/scripts/lib/glfm/shared_spec.rb
index 3ce9d44ba3d..3717c7ce18f 100644
--- a/spec/scripts/lib/glfm/shared_spec.rb
+++ b/spec/scripts/lib/glfm/shared_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Glfm::Shared do
end
describe '#write_file' do
- it 'works' do
+ it 'creates the file' do
filename = Dir::Tmpname.create('basename') do |path|
instance.write_file(path, 'test')
end
@@ -20,7 +20,7 @@ RSpec.describe Glfm::Shared do
end
describe '#run_external_cmd' do
- it 'works' do
+ it 'runs the external command' do
expect(instance.run_external_cmd('echo "hello"')).to eq("hello\n")
end
@@ -35,7 +35,7 @@ RSpec.describe Glfm::Shared do
end
describe '#dump_yaml_with_formatting' do
- it 'works' do
+ it 'returns formatted yaml' do
hash = { a: 'b' }
yaml = instance.dump_yaml_with_formatting(hash, literal_scalars: true)
expect(yaml).to eq("---\na: |-\n b\n")
diff --git a/spec/scripts/lib/glfm/update_specification_spec.rb b/spec/scripts/lib/glfm/update_specification_spec.rb
index ccf1a8fd26a..ed5650e7310 100644
--- a/spec/scripts/lib/glfm/update_specification_spec.rb
+++ b/spec/scripts/lib/glfm/update_specification_spec.rb
@@ -53,9 +53,9 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
let(:ghfm_spec_txt_examples) do
<<~MARKDOWN
- # Section with Examples
+ # Section with examples
- ## Emphasis and Strong
+ ## Emphasis and strong
```````````````````````````````` example
_EMPHASIS LINE 1_
@@ -101,20 +101,24 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
<<~MARKDOWN
# Official Specification Section with Examples
- Some examples.
+ ```````````````````````````````` example
+ official example
+ .
+ <p>official example</p>
+ ````````````````````````````````
+
MARKDOWN
end
let(:glfm_official_specification_md_contents) do
<<~MARKDOWN
- # GLFM Introduction
-
- GLFM intro text.
+ GLFM official text before examples
- <!-- BEGIN TESTS -->
+ #{described_class::BEGIN_TESTS_COMMENT_LINE_TEXT}
#{glfm_official_specification_md_examples}
- <!-- END TESTS -->
- # Non-example official content
+ #{described_class::END_TESTS_COMMENT_LINE_TEXT}
+
+ GLFM official text after examples
MARKDOWN
end
@@ -122,17 +126,19 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
<<~MARKDOWN
# Internal Extension Section with Examples
- Some examples.
+ ```````````````````````````````` example
+ internal example
+ .
+ <p>internal extension example</p>
+ ````````````````````````````````
MARKDOWN
end
let(:glfm_internal_extensions_md_contents) do
<<~MARKDOWN
- # Non-example internal content
- <!-- BEGIN TESTS -->
+ #{described_class::BEGIN_TESTS_COMMENT_LINE_TEXT}
#{glfm_internal_extensions_md_examples}
- <!-- END TESTS -->
- # More non-example internal content
+ #{described_class::END_TESTS_COMMENT_LINE_TEXT}
MARKDOWN
end
@@ -258,29 +264,46 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
describe 'writing output_example_snapshots/snapshot_spec.md' do
let(:es_snapshot_spec_md_contents) { reread_io(es_snapshot_spec_md_io) }
- before do
- subject.process(skip_spec_html_generation: true)
- end
+ context 'with valid glfm_internal_extensions.md' do
+ before do
+ subject.process(skip_spec_html_generation: true)
+ end
- it 'replaces the header text with the GitLab version' do
- expect(es_snapshot_spec_md_contents).not_to match(/GitHub Flavored Markdown Spec/m)
- expect(es_snapshot_spec_md_contents).not_to match(/^version: \d\.\d/m)
- expect(es_snapshot_spec_md_contents).not_to match(/^date: /m)
+ it 'replaces the header text with the GitLab version' do
+ expect(es_snapshot_spec_md_contents).not_to match(/GitHub Flavored Markdown Spec/m)
+ expect(es_snapshot_spec_md_contents).not_to match(/^version: \d\.\d/m)
+ expect(es_snapshot_spec_md_contents).not_to match(/^date: /m)
- expect(es_snapshot_spec_md_contents).to match(/#{Regexp.escape(described_class::GLFM_SPEC_TXT_HEADER)}/mo)
+ expect(es_snapshot_spec_md_contents).to match(/#{Regexp.escape(described_class::ES_SNAPSHOT_SPEC_MD_HEADER)}/mo)
+ end
+
+ it 'includes header and all examples', :unlimited_max_formatted_output_length do
+ # rubocop:disable Style/StringConcatenation (string contatenation is more readable)
+ expected = described_class::ES_SNAPSHOT_SPEC_MD_HEADER +
+ ghfm_spec_txt_examples +
+ "\n" +
+ glfm_official_specification_md_examples +
+ "\n\n" + # NOTE: We want a blank line between the official and internal examples
+ glfm_internal_extensions_md_examples +
+ "\n"
+ # rubocop:enable Style/StringConcatenation
+ expect(es_snapshot_spec_md_contents).to eq(expected)
+ end
end
- it 'includes header and all examples', :unlimited_max_formatted_output_length do
- # rubocop:disable Style/StringConcatenation (string contatenation is more readable)
- expected = described_class::GLFM_SPEC_TXT_HEADER +
- ghfm_spec_txt_examples +
- "\n" +
- glfm_official_specification_md_examples +
- "\n\n" + # NOTE: We want a blank line between the official and internal examples
- glfm_internal_extensions_md_examples +
- "\n"
- # rubocop:enable Style/StringConcatenation
- expect(es_snapshot_spec_md_contents).to eq(expected)
+ context 'with invalid non-example content in glfm_internal_extensions.md' do
+ let(:glfm_internal_extensions_md_contents) do
+ <<~MARKDOWN
+ THIS TEXT IS NOT ALLOWED IN THIS FILE, ONLY EXAMPLES IN BEGIN/END TEST BLOCK ARE ALLOWED
+ #{described_class::BEGIN_TESTS_COMMENT_LINE_TEXT}
+ #{glfm_internal_extensions_md_examples}
+ #{described_class::END_TESTS_COMMENT_LINE_TEXT}
+ MARKDOWN
+ end
+
+ it 'raises an error' do
+ expect { subject.process(skip_spec_html_generation: true) }.to raise_error /no content is allowed outside/i
+ end
end
end
@@ -294,66 +317,56 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
end
it 'renders expected HTML', :unlimited_max_formatted_output_length do
- # NOTE: We do assertions for both output HTML files in this same `it` example block,
+ # NOTE: We do all assertions for both output HTML files in this same `it` example block,
# because calling a full `subject.process` without `skip_spec_html_generation: true`
- # is very slow, and want to avoid doing it twice.
-
- expected_spec_html = <<~RENDERED_HTML
- <div class="gl-relative markdown-code-block js-markdown-code">
- <pre data-sourcepos="1:1-4:3" lang="yaml" class="code highlight js-syntax-highlight language-yaml" data-lang-params="frontmatter" v-pre="true"><code><span id="LC1" class="line" lang="yaml"><span class="na">title</span><span class="pi">:</span> <span class="s">GitLab Flavored Markdown (GLFM) Spec</span></span>
- <span id="LC2" class="line" lang="yaml"><span class="na">version</span><span class="pi">:</span> <span class="s">alpha</span></span></code></pre>
- <copy-code></copy-code>
- </div>
- <h1 data-sourcepos="5:1-5:19" dir="auto">
- <a id="user-content-glfm-introduction" class="anchor" href="#glfm-introduction" aria-hidden="true"></a>GLFM Introduction</h1>
- <p data-sourcepos="7:1-7:16" dir="auto">GLFM intro text.</p>
-
- <h1 data-sourcepos="10:1-10:46" dir="auto">
- <a id="user-content-official-specification-section-with-examples" class="anchor" href="#official-specification-section-with-examples" aria-hidden="true"></a>Official Specification Section with Examples</h1>
- <p data-sourcepos="12:1-12:14" dir="auto">Some examples.</p>
-
- <h1 data-sourcepos="15:1-15:30" dir="auto">
- <a id="user-content-non-example-official-content" class="anchor" href="#non-example-official-content" aria-hidden="true"></a>Non-example official content</h1>
- RENDERED_HTML
- expect(spec_html_contents).to be == expected_spec_html
-
- expected_snapshot_spec_html = <<~RENDERED_HTML
- <div class="gl-relative markdown-code-block js-markdown-code">
- <pre data-sourcepos="1:1-4:3" lang="yaml" class="code highlight js-syntax-highlight language-yaml" data-lang-params="frontmatter" v-pre="true"><code><span id="LC1" class="line" lang="yaml"><span class="na">title</span><span class="pi">:</span> <span class="s">GitLab Flavored Markdown (GLFM) Spec</span></span>
- <span id="LC2" class="line" lang="yaml"><span class="na">version</span><span class="pi">:</span> <span class="s">alpha</span></span></code></pre>
- <copy-code></copy-code>
- </div>
- <h1 data-sourcepos="5:1-5:23" dir="auto">
- <a id="user-content-section-with-examples" class="anchor" href="#section-with-examples" aria-hidden="true"></a>Section with Examples</h1>
- <h2 data-sourcepos="7:1-7:22" dir="auto">
- <a id="user-content-emphasis-and-strong" class="anchor" href="#emphasis-and-strong" aria-hidden="true"></a>Emphasis and Strong</h2>
- <div class="gl-relative markdown-code-block js-markdown-code">
- <pre data-sourcepos="9:1-12:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_EMPHASIS LINE 1_</span>
- <span id="LC2" class="line" lang="plaintext">_EMPHASIS LINE 2_</span></code></pre>
- <copy-code></copy-code>
- </div>
- <div class="gl-relative markdown-code-block js-markdown-code">
- <pre data-sourcepos="14:1-17:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;EMPHASIS LINE 1&lt;/em&gt;</span>
- <span id="LC2" class="line" lang="plaintext">&lt;em&gt;EMPHASIS LINE 2&lt;/em&gt;&lt;/p&gt;</span></code></pre>
- <copy-code></copy-code>
- </div>
- <div class="gl-relative markdown-code-block js-markdown-code">
- <pre data-sourcepos="19:1-21:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__STRONG!__</span></code></pre>
- <copy-code></copy-code>
- </div>
- <div class="gl-relative markdown-code-block js-markdown-code">
- <pre data-sourcepos="23:1-25:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;STRONG!&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
- <copy-code></copy-code>
- </div>
- <p data-sourcepos="27:1-27:36" dir="auto">End of last GitHub examples section.</p>
- <h1 data-sourcepos="29:1-29:46" dir="auto">
- <a id="user-content-official-specification-section-with-examples" class="anchor" href="#official-specification-section-with-examples" aria-hidden="true"></a>Official Specification Section with Examples</h1>
- <p data-sourcepos="31:1-31:14" dir="auto">Some examples.</p>
- <h1 data-sourcepos="34:1-34:42" dir="auto">
- <a id="user-content-internal-extension-section-with-examples" class="anchor" href="#internal-extension-section-with-examples" aria-hidden="true"></a>Internal Extension Section with Examples</h1>
- <p data-sourcepos="36:1-36:14" dir="auto">Some examples.</p>
- RENDERED_HTML
- expect(snapshot_spec_html_contents).to be == expected_snapshot_spec_html
+ # is very slow, and want to avoid doing it multiple times
+ #
+ # We also do fairly loose and minimal assertions around the basic structure of the files.
+ # Otherwise, if we asserted the complete exact structure of the HTML, this would be a
+ # brittle test which would breaks every time that something minor changed around the
+ # GLFM rendering. E.g. classes, ids, attribute ordering, etc. All of this behavior
+ # should be thoroughly covered elsewhere by other testing. If there are regressions
+ # in the update specification logic in the future which are not caught by this example,
+ # additional test coverage can be added as necessary.
+
+ # --------------------
+ # spec.html assertions
+ # --------------------
+
+ # correct title should in a header
+ expect(spec_html_contents).to match(%r{<h1.*#{described_class::GLFM_SPEC_TXT_TITLE}</h1>}o)
+
+ # correct text should be included with correct ordering
+ expect(spec_html_contents)
+ .to match(%r{official text before.*official example.*official text after}m)
+
+ # -----------------------------
+ # snapshot_spec.html assertions
+ # -----------------------------
+
+ # correct title should in a header
+ expect(snapshot_spec_html_contents).to match(%r{<h1.*#{described_class::ES_SNAPSHOT_SPEC_TITLE}</h1>}o)
+
+ # correct example text should be included
+ expect(snapshot_spec_html_contents)
+ .to match(%r{internal example}m)
+
+ # -----------------------------
+ # general formatting assertions
+ # -----------------------------
+
+ md = '_EMPHASIS LINE 1_'
+ html = '&lt;em&gt;EMPHASIS LINE 1&lt;/em&gt;'
+
+ # examples should have markdown and HTML in separate pre+code blocks
+ expected_example_1_regex = "<pre.*<code.*#{md}.*</code></pre>.*<pre.*<code.*#{html}.*</code></pre>"
+ expect(snapshot_spec_html_contents).to match(%r{#{expected_example_1_regex}}m)
+
+ # examples should have proper divs prepended for numbering, links, and styling
+ empty_div_for_example_class = '<div>'
+ examplenum_div = '<div><a href="#example-1">Example 1</a></div>'
+ expect(snapshot_spec_html_contents)
+ .to match(%r{#{empty_div_for_example_class}\n#{examplenum_div}.*#{expected_example_1_regex}.*}m)
end
end
# rubocop:enable RSpec/MultipleMemoizedHelpers
diff --git a/spec/scripts/trigger-build_spec.rb b/spec/scripts/trigger-build_spec.rb
index ac8e3c7797c..52682387e20 100644
--- a/spec/scripts/trigger-build_spec.rb
+++ b/spec/scripts/trigger-build_spec.rb
@@ -318,28 +318,6 @@ RSpec.describe Trigger do
end
end
- describe "GITLAB_ASSETS_TAG" do
- context 'when CI_COMMIT_TAG is set' do
- before do
- stub_env('CI_COMMIT_TAG', 'v1.0')
- end
-
- it 'sets GITLAB_ASSETS_TAG to CI_COMMIT_REF_NAME' do
- expect(subject.variables['GITLAB_ASSETS_TAG']).to eq(env['CI_COMMIT_REF_NAME'])
- end
- end
-
- context 'when CI_COMMIT_TAG is nil' do
- before do
- stub_env('CI_COMMIT_TAG', nil)
- end
-
- it 'sets GITLAB_ASSETS_TAG to CI_COMMIT_SHA' do
- expect(subject.variables['GITLAB_ASSETS_TAG']).to eq(env['CI_COMMIT_SHA'])
- end
- end
- end
-
describe "CE_PIPELINE" do
context 'when Trigger.ee? is true' do
before do
diff --git a/spec/serializers/ci/group_variable_entity_spec.rb b/spec/serializers/ci/group_variable_entity_spec.rb
index 9b64e263992..42c4e940421 100644
--- a/spec/serializers/ci/group_variable_entity_spec.rb
+++ b/spec/serializers/ci/group_variable_entity_spec.rb
@@ -3,14 +3,16 @@
require 'spec_helper'
RSpec.describe Ci::GroupVariableEntity do
- let(:variable) { create(:ci_group_variable) }
+ let(:variable) { build(:ci_group_variable) }
let(:entity) { described_class.new(variable) }
describe '#as_json' do
subject { entity.as_json }
it 'contains required fields' do
- expect(subject).to include(:id, :key, :value, :protected, :variable_type, :environment_scope)
+ expect(subject.keys).to contain_exactly(
+ :id, :key, :value, :protected, :variable_type, :environment_scope, :raw, :masked
+ )
end
end
end
diff --git a/spec/serializers/ci/variable_entity_spec.rb b/spec/serializers/ci/variable_entity_spec.rb
index 38da0b16bbd..96111604028 100644
--- a/spec/serializers/ci/variable_entity_spec.rb
+++ b/spec/serializers/ci/variable_entity_spec.rb
@@ -3,14 +3,16 @@
require 'spec_helper'
RSpec.describe Ci::VariableEntity do
- let(:variable) { create(:ci_variable) }
+ let(:variable) { build(:ci_variable) }
let(:entity) { described_class.new(variable) }
describe '#as_json' do
subject { entity.as_json }
it 'contains required fields' do
- expect(subject).to include(:id, :key, :value, :protected, :environment_scope, :variable_type)
+ expect(subject.keys).to contain_exactly(
+ :id, :key, :value, :protected, :environment_scope, :variable_type, :raw, :masked
+ )
end
end
end
diff --git a/spec/serializers/deploy_keys/deploy_key_entity_spec.rb b/spec/serializers/deploy_keys/deploy_key_entity_spec.rb
index 7719cafae11..4302ed3a097 100644
--- a/spec/serializers/deploy_keys/deploy_key_entity_spec.rb
+++ b/spec/serializers/deploy_keys/deploy_key_entity_spec.rb
@@ -39,7 +39,8 @@ RSpec.describe DeployKeys::DeployKeyEntity do
id: project.id,
name: project.name,
full_path: project_path(project),
- full_name: project.full_name
+ full_name: project.full_name,
+ refs_url: refs_project_path(project)
}
}
]
diff --git a/spec/serializers/entity_date_helper_spec.rb b/spec/serializers/entity_date_helper_spec.rb
index a8c338675e2..5a4571339b3 100644
--- a/spec/serializers/entity_date_helper_spec.rb
+++ b/spec/serializers/entity_date_helper_spec.rb
@@ -48,7 +48,7 @@ RSpec.describe EntityDateHelper do
describe '#remaining_days_in_words' do
around do |example|
- Timecop.freeze(Time.utc(2017, 3, 17)) { example.run }
+ travel_to(Time.utc(2017, 3, 17)) { example.run }
end
context 'when less than 31 days remaining' do
@@ -75,7 +75,9 @@ RSpec.describe EntityDateHelper do
end
it 'returns 1 day remaining when queried mid-day' do
- Timecop.freeze(Time.utc(2017, 3, 17, 13, 10)) do
+ travel_back
+
+ travel_to(Time.utc(2017, 3, 17, 13, 10)) do
expect(milestone_remaining).to eq("<strong>1</strong> day remaining")
end
end
diff --git a/spec/serializers/issue_entity_spec.rb b/spec/serializers/issue_entity_spec.rb
index 6161d4d7ec2..9d53d8bb235 100644
--- a/spec/serializers/issue_entity_spec.rb
+++ b/spec/serializers/issue_entity_spec.rb
@@ -162,4 +162,24 @@ RSpec.describe IssueEntity do
end
it_behaves_like 'issuable entity current_user properties'
+
+ context 'when issue has email participants' do
+ before do
+ resource.issue_email_participants.create!(email: 'any@email.com')
+ end
+
+ context 'when issue is confidential' do
+ it 'returns email participants' do
+ resource.update!(confidential: true)
+
+ expect(subject[:issue_email_participants]).to match_array([{ email: "any@email.com" }])
+ end
+ end
+
+ context 'when issue is not confidential' do
+ it 'returns empty array' do
+ expect(subject[:issue_email_participants]).to be_empty
+ end
+ end
+ end
end
diff --git a/spec/serializers/linked_project_issue_entity_spec.rb b/spec/serializers/linked_project_issue_entity_spec.rb
index 523b89921b6..d415d1cbcb2 100644
--- a/spec/serializers/linked_project_issue_entity_spec.rb
+++ b/spec/serializers/linked_project_issue_entity_spec.rb
@@ -6,10 +6,10 @@ RSpec.describe LinkedProjectIssueEntity do
include Gitlab::Routing.url_helpers
let_it_be(:user) { create(:user) }
- let_it_be(:project) { create(:project) }
let_it_be(:issue_link) { create(:issue_link) }
let(:request) { double('request') }
+ let(:issue_type) { :task }
let(:related_issue) { issue_link.source.related_issues(user).first }
let(:entity) { described_class.new(related_issue, request: request, current_user: user) }
@@ -31,9 +31,7 @@ RSpec.describe LinkedProjectIssueEntity do
end
context 'when related issue is a task' do
- before do
- related_issue.update!(issue_type: :task, work_item_type: WorkItems::Type.default_by_type(:task))
- end
+ let_it_be(:issue_link) { create(:issue_link, target: create(:issue, :task)) }
it 'returns a work item issue type' do
expect(serialized_entity).to include(type: 'TASK')
@@ -47,9 +45,7 @@ RSpec.describe LinkedProjectIssueEntity do
end
context 'when related issue is a task' do
- before do
- related_issue.update!(issue_type: :task, work_item_type: WorkItems::Type.default_by_type(:task))
- end
+ let_it_be(:issue_link) { create(:issue_link, target: create(:issue, :task)) }
context 'when use_iid_in_work_items_path feature flag is disabled' do
before do
diff --git a/spec/serializers/member_entity_spec.rb b/spec/serializers/member_entity_spec.rb
index 370fa14b1e8..350355eb72d 100644
--- a/spec/serializers/member_entity_spec.rb
+++ b/spec/serializers/member_entity_spec.rb
@@ -90,6 +90,28 @@ RSpec.describe MemberEntity do
it_behaves_like 'is_direct_member'
end
+ context 'is_last_owner' do
+ context 'when member is last owner' do
+ before do
+ allow(member).to receive(:last_owner?).and_return(true)
+ end
+
+ it 'exposes `is_last_owner` as `true`' do
+ expect(entity_hash[:is_last_owner]).to be(true)
+ end
+ end
+
+ context 'when owner is not last owner' do
+ before do
+ allow(member).to receive(:last_owner?).and_return(false)
+ end
+
+ it 'exposes `is_last_owner` as `false`' do
+ expect(entity_hash[:is_last_owner]).to be(false)
+ end
+ end
+ end
+
context 'new member user state is blocked_pending_approval' do
let(:user) { create(:user, :blocked_pending_approval) }
let(:group_member) { create(:group_member, :invited, group: group, invite_email: user.email) }
diff --git a/spec/serializers/merge_request_poll_cached_widget_entity_spec.rb b/spec/serializers/merge_request_poll_cached_widget_entity_spec.rb
index 702c6d9fe98..f883156628a 100644
--- a/spec/serializers/merge_request_poll_cached_widget_entity_spec.rb
+++ b/spec/serializers/merge_request_poll_cached_widget_entity_spec.rb
@@ -152,10 +152,10 @@ RSpec.describe MergeRequestPollCachedWidgetEntity do
.to eq(closed_event.author_id)
expect(subject.dig(:metrics, :merged_at).to_s)
- .to eq(merge_event.updated_at.to_s)
+ .to eq(merge_event.updated_at.iso8601)
expect(subject.dig(:metrics, :closed_at).to_s)
- .to eq(closed_event.updated_at.to_s)
+ .to eq(closed_event.updated_at.iso8601)
end
end
diff --git a/spec/serializers/merge_request_user_entity_spec.rb b/spec/serializers/merge_request_user_entity_spec.rb
index 5c7120ab6a4..3e136f29247 100644
--- a/spec/serializers/merge_request_user_entity_spec.rb
+++ b/spec/serializers/merge_request_user_entity_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe MergeRequestUserEntity do
- let_it_be(:user) { create(:user) }
+ let_it_be_with_reload(:user) { create(:user) }
let_it_be(:merge_request) { create(:merge_request, assignees: [user]) }
let(:request) { EntityRequest.new(project: merge_request.target_project, current_user: user) }
diff --git a/spec/serializers/pipeline_details_entity_spec.rb b/spec/serializers/pipeline_details_entity_spec.rb
index ca38721cc7f..764d55e8b4a 100644
--- a/spec/serializers/pipeline_details_entity_spec.rb
+++ b/spec/serializers/pipeline_details_entity_spec.rb
@@ -11,16 +11,16 @@ RSpec.describe PipelineDetailsEntity do
described_class.represent(pipeline, request: request)
end
- it 'inherits from PipelineEntity' do
- expect(described_class).to be < Ci::PipelineEntity
- end
-
before do
stub_not_protect_default_branch
allow(request).to receive(:current_user).and_return(user)
end
+ it 'inherits from PipelineEntity' do
+ expect(described_class).to be < Ci::PipelineEntity
+ end
+
describe '#as_json' do
subject { entity.as_json }
diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb
index 9caaeb3450b..33fee68a2f2 100644
--- a/spec/serializers/pipeline_serializer_spec.rb
+++ b/spec/serializers/pipeline_serializer_spec.rb
@@ -183,25 +183,25 @@ RSpec.describe PipelineSerializer do
context 'with triggered pipelines' do
before do
- pipeline_1 = create(:ci_pipeline)
+ pipeline_1 = create(:ci_pipeline, project: project)
build_1 = create(:ci_build, pipeline: pipeline_1)
create(:ci_sources_pipeline, source_job: build_1)
-
- pipeline_2 = create(:ci_pipeline)
- build_2 = create(:ci_build, pipeline: pipeline_2)
- create(:ci_sources_pipeline, source_job: build_2)
end
it 'verifies number of queries', :request_store do
- recorded = ActiveRecord::QueryRecorder.new { subject }
+ control = ActiveRecord::QueryRecorder.new do
+ serializer.represent(Ci::Pipeline.all, preload: true)
+ end
- # Existing numbers are high and require performance optimization
- # Ongoing issue:
- # https://gitlab.com/gitlab-org/gitlab/-/issues/225156
- expected_queries = Gitlab.ee? ? 78 : 74
+ pipeline_2 = create(:ci_pipeline, project: project)
+ build_2 = create(:ci_build, pipeline: pipeline_2)
+ create(:ci_sources_pipeline, source_job: build_2)
- expect(recorded.count).to be_within(2).of(expected_queries)
- expect(recorded.cached_count).to eq(0)
+ recorded = ActiveRecord::QueryRecorder.new do
+ serializer.represent(Ci::Pipeline.all, preload: true)
+ end
+
+ expect(recorded).not_to exceed_query_limit(control)
end
end
diff --git a/spec/serializers/prometheus_alert_entity_spec.rb b/spec/serializers/prometheus_alert_entity_spec.rb
index 91a1e3377c2..02da5a5bb88 100644
--- a/spec/serializers/prometheus_alert_entity_spec.rb
+++ b/spec/serializers/prometheus_alert_entity_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
RSpec.describe PrometheusAlertEntity do
- let(:user) { create(:user) }
- let(:prometheus_alert) { create(:prometheus_alert) }
+ let(:user) { build_stubbed(:user) }
+ let(:prometheus_alert) { build_stubbed(:prometheus_alert) }
let(:request) { double('prometheus_alert', current_user: user) }
let(:entity) { described_class.new(prometheus_alert, request: request) }
diff --git a/spec/serializers/release_serializer_spec.rb b/spec/serializers/release_serializer_spec.rb
index b31172c3a50..e9f56e3453e 100644
--- a/spec/serializers/release_serializer_spec.rb
+++ b/spec/serializers/release_serializer_spec.rb
@@ -3,18 +3,13 @@
require 'spec_helper'
RSpec.describe ReleaseSerializer do
- let(:user) { create(:user) }
- let(:project) { create :project }
+ let(:user) { build_stubbed(:user) }
subject { described_class.new.represent(resource, current_user: user) }
- before do
- project.add_developer(user)
- end
-
describe '#represent' do
context 'when a single object is being serialized' do
- let(:resource) { create(:release, project: project) }
+ let(:resource) { build_stubbed(:release) }
it 'serializes the label object' do
expect(subject[:tag]).to eq resource.tag
@@ -26,7 +21,7 @@ RSpec.describe ReleaseSerializer do
end
context 'when multiple objects are being serialized' do
- let(:resource) { create_list(:release, 3) }
+ let(:resource) { build_stubbed_list(:release, 3) }
it 'serializes the array of releases' do
expect(subject.size).to eq(3)
diff --git a/spec/services/admin/set_feature_flag_service_spec.rb b/spec/services/admin/set_feature_flag_service_spec.rb
index 9a9c5545e23..45ee914558a 100644
--- a/spec/services/admin/set_feature_flag_service_spec.rb
+++ b/spec/services/admin/set_feature_flag_service_spec.rb
@@ -13,11 +13,69 @@ RSpec.describe Admin::SetFeatureFlagService do
# Find any `development` feature flag name
let(:known_feature_flag) do
Feature::Definition.definitions
- .values.find(&:development?)
+ .values.find { |defn| defn.development? && !defn.default_enabled }
+ end
+
+ describe 'sequences of executions' do
+ subject(:flag) do
+ Feature.get(feature_name) # rubocop: disable Gitlab/AvoidFeatureGet
+ end
+
+ context 'if we enable_percentage_of_actors and then disable' do
+ before do
+ described_class
+ .new(feature_flag_name: feature_name, params: { key: 'percentage_of_actors', value: '50.0' })
+ .execute
+
+ described_class
+ .new(feature_flag_name: feature_name, params: { key: 'percentage_of_actors', value: '0.0' })
+ .execute
+ end
+
+ it 'leaves the flag off' do
+ expect(flag.state).to eq(:off)
+ end
+ end
+
+ context 'if we enable and then enable_percentage_of_actors' do
+ before do
+ described_class
+ .new(feature_flag_name: feature_name, params: { key: 'percentage_of_actors', value: '100.0' })
+ .execute
+ end
+
+ it 'reports an error' do
+ result = described_class
+ .new(feature_flag_name: feature_name, params: { key: 'percentage_of_actors', value: '50.0' })
+ .execute
+
+ expect(flag.state).to eq(:on)
+ expect(result).to be_error
+ end
+
+ context 'if we disable the flag first' do
+ before do
+ described_class
+ .new(feature_flag_name: feature_name, params: { value: 'false' })
+ .execute
+ end
+
+ it 'sets the percentage of actors' do
+ result = described_class
+ .new(feature_flag_name: feature_name, params: { key: 'percentage_of_actors', value: '50.0' })
+ .execute
+
+ expect(flag.state).to eq(:conditional)
+ expect(result).not_to be_error
+ end
+ end
+ end
end
describe '#execute' do
before do
+ unstub_all_feature_flags
+
Feature.reset
Flipper.unregister_groups
Flipper.register(:perf_team) do |actor|
@@ -25,7 +83,87 @@ RSpec.describe Admin::SetFeatureFlagService do
end
end
- subject { service.execute }
+ subject(:result) { service.execute }
+
+ context 'when we cannot interpret the operation' do
+ let(:params) { { value: 'wibble', key: 'unknown' } }
+
+ it { is_expected.to be_error }
+ it { is_expected.to have_attributes(reason: :illegal_operation) }
+ it { is_expected.to have_attributes(message: %(Cannot set '#{feature_name}' ("unknown") to "wibble")) }
+
+ context 'when the key is absent' do
+ let(:params) { { value: 'wibble' } }
+
+ it { is_expected.to be_error }
+ it { is_expected.to have_attributes(reason: :illegal_operation) }
+ it { is_expected.to have_attributes(message: %(Cannot set '#{feature_name}' to "wibble")) }
+ end
+ end
+
+ context 'when the value to set cannot be parsed' do
+ let(:params) { { value: 'wibble', key: 'percentage_of_actors' } }
+
+ it { is_expected.to be_error }
+ it { is_expected.to have_attributes(reason: :illegal_operation) }
+ it { is_expected.to have_attributes(message: 'Not a percentage') }
+ end
+
+ context 'when value is "remove_opt_out"' do
+ before do
+ Feature.enable(feature_name)
+ end
+
+ context 'without a target' do
+ let(:params) { { value: 'remove_opt_out' } }
+
+ it 'returns an error' do
+ expect(result).to be_error
+ expect(result.reason).to eq(:illegal_operation)
+ end
+ end
+
+ context 'with a target' do
+ let(:params) { { value: 'remove_opt_out', user: user.username } }
+
+ context 'when there is currently no opt-out' do
+ it 'returns an error' do
+ expect(result).to be_error
+ expect(result.reason).to eq(:illegal_operation)
+ expect(Feature).to be_enabled(feature_name, user)
+ end
+ end
+
+ context 'when there is currently an opt-out' do
+ before do
+ Feature.opt_out(feature_name, user)
+ end
+
+ it 'removes the opt out' do
+ expect(result).to be_success
+ expect(Feature).to be_enabled(feature_name, user)
+ end
+ end
+ end
+ end
+
+ context 'when value is "opt_out"' do
+ let(:params) { { value: 'opt_out', namespace: group.full_path, user: user.username } }
+
+ it 'opts the user and group out' do
+ expect(Feature).to receive(:opt_out).with(feature_name, user)
+ expect(Feature).to receive(:opt_out).with(feature_name, group)
+ expect(result).to be_success
+ end
+
+ context 'without a target' do
+ let(:params) { { value: 'opt_out' } }
+
+ it { is_expected.to be_error }
+
+ it { is_expected.to have_attributes(reason: :illegal_operation) }
+ end
+ end
context 'when enabling the feature flag' do
let(:params) { { value: 'true' } }
@@ -38,6 +176,18 @@ RSpec.describe Admin::SetFeatureFlagService do
expect(feature_flag.name).to eq(feature_name)
end
+ context 'when the flag is default_enabled' do
+ let(:known_feature_flag) do
+ Feature::Definition.definitions
+ .values.find { |defn| defn.development? && defn.default_enabled }
+ end
+
+ it 'leaves the flag enabled' do
+ expect(subject).to be_success
+ expect(Feature).to be_enabled(feature_name)
+ end
+ end
+
it 'logs the event' do
expect(Feature.logger).to receive(:info).once
@@ -52,6 +202,31 @@ RSpec.describe Admin::SetFeatureFlagService do
expect(subject).to be_success
end
+ context 'when the flag has been opted out for user' do
+ before do
+ Feature.enable(feature_name)
+ Feature.opt_out(feature_name, user)
+ end
+
+ it 'records an error' do
+ expect(subject).to be_error
+ expect(subject.reason).to eq(:illegal_operation)
+ expect(Feature).not_to be_enabled(feature_name, user)
+ end
+ end
+
+ context 'when the flag is default_enabled' do
+ let(:known_feature_flag) do
+ Feature::Definition.definitions
+ .values.find { |defn| defn.development? && defn.default_enabled }
+ end
+
+ it 'leaves the feature enabled' do
+ expect(subject).to be_success
+ expect(Feature).to be_enabled(feature_name, user)
+ end
+ end
+
context 'when user does not exist' do
let(:params) { { value: 'true', user: 'unknown-user' } }
@@ -166,6 +341,16 @@ RSpec.describe Admin::SetFeatureFlagService do
expect(subject).to be_success
end
end
+
+ context 'with a target' do
+ before do
+ params[:user] = user.username
+ end
+
+ it { is_expected.to be_error }
+
+ it { is_expected.to have_attributes(reason: :illegal_operation) }
+ end
end
context 'when enabling given a percentage of actors' do
@@ -184,6 +369,16 @@ RSpec.describe Admin::SetFeatureFlagService do
expect(subject).to be_success
end
end
+
+ context 'with a target' do
+ before do
+ params[:user] = user.username
+ end
+
+ it { is_expected.to be_error }
+
+ it { is_expected.to have_attributes(reason: :illegal_operation) }
+ end
end
end
diff --git a/spec/services/bulk_imports/create_service_spec.rb b/spec/services/bulk_imports/create_service_spec.rb
index bf174f5d5a2..f1e5533139e 100644
--- a/spec/services/bulk_imports/create_service_spec.rb
+++ b/spec/services/bulk_imports/create_service_spec.rb
@@ -5,6 +5,8 @@ require 'spec_helper'
RSpec.describe BulkImports::CreateService do
let(:user) { create(:user) }
let(:credentials) { { url: 'http://gitlab.example', access_token: 'token' } }
+ let(:destination_group) { create(:group, path: 'destination1') }
+ let_it_be(:parent_group) { create(:group, path: 'parent-group') }
let(:params) do
[
{
@@ -39,10 +41,12 @@ RSpec.describe BulkImports::CreateService do
before do
allow_next_instance_of(BulkImports::Clients::HTTP) do |instance|
allow(instance).to receive(:instance_version).and_return(source_version)
+ allow(instance).to receive(:instance_enterprise).and_return(false)
end
end
it 'creates bulk import' do
+ parent_group.add_owner(user)
expect { subject.execute }.to change { BulkImport.count }.by(1)
last_bulk_import = BulkImport.last
@@ -50,11 +54,21 @@ RSpec.describe BulkImports::CreateService do
expect(last_bulk_import.user).to eq(user)
expect(last_bulk_import.source_version).to eq(source_version.to_s)
expect(last_bulk_import.user).to eq(user)
+ expect(last_bulk_import.source_enterprise).to eq(false)
+
expect_snowplow_event(
category: 'BulkImports::CreateService',
action: 'create',
label: 'bulk_import_group'
)
+
+ expect_snowplow_event(
+ category: 'BulkImports::CreateService',
+ action: 'create',
+ label: 'import_access_level',
+ user: user,
+ extra: { user_role: 'Owner', import_type: 'bulk_import_group' }
+ )
end
it 'creates bulk import entities' do
@@ -87,5 +101,109 @@ RSpec.describe BulkImports::CreateService do
expect(result).to be_error
expect(result.message).to eq("Validation failed: Source full path can't be blank")
end
+
+ describe '#user-role' do
+ context 'when there is a parent_namespace and the user is a member' do
+ let(:group2) { create(:group, path: 'destination200', source_id: parent_group.id ) }
+ let(:params) do
+ [
+ {
+ source_type: 'group_entity',
+ source_full_path: 'full/path/to/group1',
+ destination_slug: 'destination200',
+ destination_namespace: 'parent-group'
+ }
+ ]
+ end
+
+ it 'defines access_level from parent namespace membership' do
+ parent_group.add_guest(user)
+ subject.execute
+
+ expect_snowplow_event(
+ category: 'BulkImports::CreateService',
+ action: 'create',
+ label: 'import_access_level',
+ user: user,
+ extra: { user_role: 'Guest', import_type: 'bulk_import_group' }
+ )
+ end
+ end
+
+ context 'when there is a parent_namespace and the user is not a member' do
+ let(:params) do
+ [
+ {
+ source_type: 'group_entity',
+ source_full_path: 'full/path/to/group1',
+ destination_slug: 'destination-group-1',
+ destination_namespace: 'parent-group'
+ }
+ ]
+ end
+
+ it 'defines access_level as not a member' do
+ subject.execute
+ expect_snowplow_event(
+ category: 'BulkImports::CreateService',
+ action: 'create',
+ label: 'import_access_level',
+ user: user,
+ extra: { user_role: 'Not a member', import_type: 'bulk_import_group' }
+ )
+ end
+ end
+
+ context 'when there is a destination_namespace but no parent_namespace' do
+ let(:params) do
+ [
+ {
+ source_type: 'group_entity',
+ source_full_path: 'full/path/to/group1',
+ destination_slug: 'destination-group-1',
+ destination_namespace: 'destination1'
+ }
+ ]
+ end
+
+ it 'defines access_level from destination_namespace' do
+ destination_group.add_developer(user)
+ subject.execute
+
+ expect_snowplow_event(
+ category: 'BulkImports::CreateService',
+ action: 'create',
+ label: 'import_access_level',
+ user: user,
+ extra: { user_role: 'Developer', import_type: 'bulk_import_group' }
+ )
+ end
+ end
+
+ context 'when there is no destination_namespace or parent_namespace' do
+ let(:params) do
+ [
+ {
+ source_type: 'group_entity',
+ source_full_path: 'full/path/to/group1',
+ destination_slug: 'destinationational mcdestiny',
+ destination_namespace: 'destinational-mcdestiny'
+ }
+ ]
+ end
+
+ it 'defines access_level as owner' do
+ subject.execute
+
+ expect_snowplow_event(
+ category: 'BulkImports::CreateService',
+ action: 'create',
+ label: 'import_access_level',
+ user: user,
+ extra: { user_role: 'Owner', import_type: 'bulk_import_group' }
+ )
+ end
+ end
+ end
end
end
diff --git a/spec/services/bulk_imports/file_download_service_spec.rb b/spec/services/bulk_imports/file_download_service_spec.rb
index ec9cc719e24..27f77b678e3 100644
--- a/spec/services/bulk_imports/file_download_service_spec.rb
+++ b/spec/services/bulk_imports/file_download_service_spec.rb
@@ -14,22 +14,19 @@ RSpec.describe BulkImports::FileDownloadService do
let_it_be(:filepath) { File.join(tmpdir, filename) }
let_it_be(:content_length) { 1000 }
- let(:chunk_double) { double('chunk', size: 100, code: 200) }
-
- let(:response_double) do
- double(
- code: 200,
- success?: true,
- parsed_response: {},
- headers: {
- 'content-length' => content_length,
- 'content-type' => content_type,
- 'content-disposition' => content_disposition
- }
- )
+ let(:headers) do
+ {
+ 'content-length' => content_length,
+ 'content-type' => content_type,
+ 'content-disposition' => content_disposition
+ }
end
- subject do
+ let(:chunk_size) { 100 }
+ let(:chunk_code) { 200 }
+ let(:chunk_double) { double('chunk', size: chunk_size, code: chunk_code, http_response: double(to_hash: headers)) }
+
+ subject(:service) do
described_class.new(
configuration: config,
relative_url: '/test',
@@ -42,9 +39,10 @@ RSpec.describe BulkImports::FileDownloadService do
before do
allow_next_instance_of(BulkImports::Clients::HTTP) do |client|
- allow(client).to receive(:head).and_return(response_double)
allow(client).to receive(:stream).and_yield(chunk_double)
end
+
+ allow(service).to receive(:response_headers).and_return(headers)
end
shared_examples 'downloads file' do
@@ -136,7 +134,7 @@ RSpec.describe BulkImports::FileDownloadService do
end
context 'when chunk code is not 200' do
- let(:chunk_double) { double('chunk', size: 1000, code: 500) }
+ let(:chunk_code) { 500 }
it 'raises an error' do
expect { subject.execute }.to raise_error(
@@ -146,7 +144,7 @@ RSpec.describe BulkImports::FileDownloadService do
end
context 'when chunk code is redirection' do
- let(:chunk_double) { double('redirection', size: 1000, code: 303) }
+ let(:chunk_code) { 303 }
it 'does not write a redirection chunk' do
expect { subject.execute }.not_to raise_error
@@ -157,10 +155,9 @@ RSpec.describe BulkImports::FileDownloadService do
context 'when redirection chunk appears at a later stage of the download' do
it 'raises an error' do
another_chunk_double = double('another redirection', size: 1000, code: 303)
- data_chunk = double('data chunk', size: 1000, code: 200)
+ data_chunk = double('data chunk', size: 1000, code: 200, http_response: double(to_hash: {}))
allow_next_instance_of(BulkImports::Clients::HTTP) do |client|
- allow(client).to receive(:head).and_return(response_double)
allow(client)
.to receive(:stream)
.and_yield(chunk_double)
@@ -177,6 +174,51 @@ RSpec.describe BulkImports::FileDownloadService do
end
end
+ describe 'remote content validation' do
+ context 'on redirect chunk' do
+ let(:chunk_code) { 303 }
+
+ it 'does not run content type & length validations' do
+ expect(service).not_to receive(:validate_content_type)
+ expect(service).not_to receive(:validate_content_length)
+
+ service.execute
+ end
+ end
+
+ context 'when there is one data chunk' do
+ it 'validates content type & length' do
+ expect(service).to receive(:validate_content_type)
+ expect(service).to receive(:validate_content_length)
+
+ service.execute
+ end
+ end
+
+ context 'when there are multiple data chunks' do
+ it 'validates content type & length only once' do
+ data_chunk = double(
+ 'data chunk',
+ size: 1000,
+ code: 200,
+ http_response: double(to_hash: {})
+ )
+
+ allow_next_instance_of(BulkImports::Clients::HTTP) do |client|
+ allow(client)
+ .to receive(:stream)
+ .and_yield(chunk_double)
+ .and_yield(data_chunk)
+ end
+
+ expect(service).to receive(:validate_content_type).once
+ expect(service).to receive(:validate_content_length).once
+
+ service.execute
+ end
+ end
+ end
+
context 'when file is a symlink' do
let_it_be(:symlink) { File.join(tmpdir, 'symlink') }
diff --git a/spec/services/chat_names/find_user_service_spec.rb b/spec/services/chat_names/find_user_service_spec.rb
index 4b0a1204558..10cb0a2f065 100644
--- a/spec/services/chat_names/find_user_service_spec.rb
+++ b/spec/services/chat_names/find_user_service_spec.rb
@@ -4,17 +4,16 @@ require 'spec_helper'
RSpec.describe ChatNames::FindUserService, :clean_gitlab_redis_shared_state do
describe '#execute' do
- let(:integration) { create(:integration) }
-
- subject { described_class.new(integration, params).execute }
+ subject { described_class.new(team_id, user_id).execute }
context 'find user mapping' do
- let(:user) { create(:user) }
- let!(:chat_name) { create(:chat_name, user: user, integration: integration) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:chat_name) { create(:chat_name, user: user) }
- context 'when existing user is requested' do
- let(:params) { { team_id: chat_name.team_id, user_id: chat_name.chat_id } }
+ let(:team_id) { chat_name.team_id }
+ let(:user_id) { chat_name.chat_id }
+ context 'when existing user is requested' do
it 'returns the existing chat_name' do
is_expected.to eq(chat_name)
end
@@ -28,7 +27,8 @@ RSpec.describe ChatNames::FindUserService, :clean_gitlab_redis_shared_state do
end
it 'only updates an existing timestamp once within a certain time frame' do
- service = described_class.new(integration, params)
+ chat_name = create(:chat_name, user: user)
+ service = described_class.new(team_id, user_id)
expect(chat_name.last_used_at).to be_nil
@@ -43,9 +43,9 @@ RSpec.describe ChatNames::FindUserService, :clean_gitlab_redis_shared_state do
end
context 'when different user is requested' do
- let(:params) { { team_id: chat_name.team_id, user_id: 'non-existing-user' } }
+ let(:user_id) { 'non-existing-user' }
- it 'returns existing user' do
+ it 'returns nil' do
is_expected.to be_nil
end
end
diff --git a/spec/services/ci/after_requeue_job_service_spec.rb b/spec/services/ci/after_requeue_job_service_spec.rb
deleted file mode 100644
index e6f46fb9ebe..00000000000
--- a/spec/services/ci/after_requeue_job_service_spec.rb
+++ /dev/null
@@ -1,320 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
- let_it_be(:project) { create(:project, :empty_repo) }
- let_it_be(:user) { project.first_owner }
-
- before_all do
- project.repository.create_file(user, 'init', 'init', message: 'init', branch_name: 'master')
- end
-
- subject(:service) { described_class.new(project, user) }
-
- context 'stage-dag mixed pipeline' do
- let(:config) do
- <<-EOY
- stages: [a, b, c]
-
- a1:
- stage: a
- script: exit $(($RANDOM % 2))
-
- a2:
- stage: a
- script: exit 0
- needs: [a1]
-
- a3:
- stage: a
- script: exit 0
- needs: [a2]
-
- b1:
- stage: b
- script: exit 0
- needs: []
-
- b2:
- stage: b
- script: exit 0
- needs: [a2]
-
- c1:
- stage: c
- script: exit 0
- needs: [b2]
-
- c2:
- stage: c
- script: exit 0
- EOY
- end
-
- let(:pipeline) do
- Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload
- end
-
- let(:a1) { find_job('a1') }
- let(:b1) { find_job('b1') }
-
- before do
- stub_ci_pipeline_yaml_file(config)
- check_jobs_statuses(
- a1: 'pending',
- a2: 'created',
- a3: 'created',
- b1: 'pending',
- b2: 'created',
- c1: 'created',
- c2: 'created'
- )
-
- b1.success!
- check_jobs_statuses(
- a1: 'pending',
- a2: 'created',
- a3: 'created',
- b1: 'success',
- b2: 'created',
- c1: 'created',
- c2: 'created'
- )
-
- a1.drop!
- check_jobs_statuses(
- a1: 'failed',
- a2: 'skipped',
- a3: 'skipped',
- b1: 'success',
- b2: 'skipped',
- c1: 'skipped',
- c2: 'skipped'
- )
-
- new_a1 = Ci::RetryJobService.new(project, user).clone!(a1)
- new_a1.enqueue!
- check_jobs_statuses(
- a1: 'pending',
- a2: 'skipped',
- a3: 'skipped',
- b1: 'success',
- b2: 'skipped',
- c1: 'skipped',
- c2: 'skipped'
- )
- end
-
- it 'marks subsequent skipped jobs as processable' do
- execute_after_requeue_service(a1)
-
- check_jobs_statuses(
- a1: 'pending',
- a2: 'created',
- a3: 'created',
- b1: 'success',
- b2: 'created',
- c1: 'created',
- c2: 'created'
- )
- end
-
- context 'when executed by a different user than the original owner' do
- let(:retryer) { create(:user).tap { |u| project.add_maintainer(u) } }
- let(:service) { described_class.new(project, retryer) }
-
- it 'reassigns jobs with updated statuses to the retryer' do
- expect(jobs_name_status_owner_needs).to contain_exactly(
- { 'name' => 'a1', 'status' => 'pending', 'user_id' => user.id, 'needs' => [] },
- { 'name' => 'a2', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['a1'] },
- { 'name' => 'a3', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['a2'] },
- { 'name' => 'b1', 'status' => 'success', 'user_id' => user.id, 'needs' => [] },
- { 'name' => 'b2', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['a2'] },
- { 'name' => 'c1', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['b2'] },
- { 'name' => 'c2', 'status' => 'skipped', 'user_id' => user.id, 'needs' => [] }
- )
-
- execute_after_requeue_service(a1)
-
- expect(jobs_name_status_owner_needs).to contain_exactly(
- { 'name' => 'a1', 'status' => 'pending', 'user_id' => user.id, 'needs' => [] },
- { 'name' => 'a2', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['a1'] },
- { 'name' => 'a3', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['a2'] },
- { 'name' => 'b1', 'status' => 'success', 'user_id' => user.id, 'needs' => [] },
- { 'name' => 'b2', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['a2'] },
- { 'name' => 'c1', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['b2'] },
- { 'name' => 'c2', 'status' => 'created', 'user_id' => retryer.id, 'needs' => [] }
- )
- end
- end
- end
-
- context 'stage-dag mixed pipeline with some same-stage needs' do
- let(:config) do
- <<-EOY
- stages: [a, b, c]
-
- a1:
- stage: a
- script: exit $(($RANDOM % 2))
-
- a2:
- stage: a
- script: exit 0
- needs: [a1]
-
- b1:
- stage: b
- script: exit 0
- needs: [b2]
-
- b2:
- stage: b
- script: exit 0
-
- c1:
- stage: c
- script: exit 0
- needs: [b2]
-
- c2:
- stage: c
- script: exit 0
- EOY
- end
-
- let(:pipeline) do
- Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload
- end
-
- let(:a1) { find_job('a1') }
-
- before do
- stub_ci_pipeline_yaml_file(config)
- check_jobs_statuses(
- a1: 'pending',
- a2: 'created',
- b1: 'created',
- b2: 'created',
- c1: 'created',
- c2: 'created'
- )
-
- a1.drop!
- check_jobs_statuses(
- a1: 'failed',
- a2: 'skipped',
- b1: 'skipped',
- b2: 'skipped',
- c1: 'skipped',
- c2: 'skipped'
- )
-
- new_a1 = Ci::RetryJobService.new(project, user).clone!(a1)
- new_a1.enqueue!
- check_jobs_statuses(
- a1: 'pending',
- a2: 'skipped',
- b1: 'skipped',
- b2: 'skipped',
- c1: 'skipped',
- c2: 'skipped'
- )
- end
-
- it 'marks subsequent skipped jobs as processable' do
- execute_after_requeue_service(a1)
-
- check_jobs_statuses(
- a1: 'pending',
- a2: 'created',
- b1: 'created',
- b2: 'created',
- c1: 'created',
- c2: 'created'
- )
- end
- end
-
- context 'with same-stage needs' do
- let(:config) do
- <<-EOY
- a:
- script: exit $(($RANDOM % 2))
-
- b:
- script: exit 0
- needs: [a]
-
- c:
- script: exit 0
- needs: [b]
- EOY
- end
-
- let(:pipeline) do
- Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload
- end
-
- let(:a) { find_job('a') }
-
- before do
- stub_ci_pipeline_yaml_file(config)
- check_jobs_statuses(
- a: 'pending',
- b: 'created',
- c: 'created'
- )
-
- a.drop!
- check_jobs_statuses(
- a: 'failed',
- b: 'skipped',
- c: 'skipped'
- )
-
- new_a = Ci::RetryJobService.new(project, user).clone!(a)
- new_a.enqueue!
- check_jobs_statuses(
- a: 'pending',
- b: 'skipped',
- c: 'skipped'
- )
- end
-
- it 'marks subsequent skipped jobs as processable' do
- execute_after_requeue_service(a)
-
- check_jobs_statuses(
- a: 'pending',
- b: 'created',
- c: 'created'
- )
- end
- end
-
- private
-
- def find_job(name)
- processables.find_by!(name: name)
- end
-
- def check_jobs_statuses(statuses)
- expect(processables.order(:name).pluck(:name, :status)).to contain_exactly(*statuses.stringify_keys.to_a)
- end
-
- def processables
- pipeline.processables.latest
- end
-
- def jobs_name_status_owner_needs
- processables.reload.map do |job|
- job.attributes.slice('name', 'status', 'user_id').merge('needs' => job.needs.map(&:name))
- end
- end
-
- def execute_after_requeue_service(processable)
- service.execute(processable)
- end
-end
diff --git a/spec/services/ci/append_build_trace_service_spec.rb b/spec/services/ci/append_build_trace_service_spec.rb
index 487dbacbe90..20f7967d1f1 100644
--- a/spec/services/ci/append_build_trace_service_spec.rb
+++ b/spec/services/ci/append_build_trace_service_spec.rb
@@ -76,4 +76,36 @@ RSpec.describe Ci::AppendBuildTraceService do
expect(build.failure_reason).to eq 'trace_size_exceeded'
end
end
+
+ context 'when debug_trace param is provided' do
+ let(:metadata) { Ci::BuildMetadata.find_by(build_id: build) }
+ let(:stream_size) { 192.kilobytes }
+ let(:body_data) { 'x' * stream_size }
+ let(:content_range) { "#{body_start}-#{stream_size}" }
+
+ context 'when sending the first trace' do
+ let(:body_start) { 0 }
+
+ it 'updates build metadata debug_trace_enabled' do
+ described_class
+ .new(build, content_range: content_range, debug_trace: true)
+ .execute(body_data)
+
+ expect(metadata.debug_trace_enabled).to be(true)
+ end
+ end
+
+ context 'when sending the second trace' do
+ let(:body_start) { 1 }
+
+ it 'does not update build metadata debug_trace_enabled', :aggregate_failures do
+ query_recorder = ActiveRecord::QueryRecorder.new do
+ described_class.new(build, content_range: content_range, debug_trace: true).execute(body_data)
+ end
+
+ expect(metadata.debug_trace_enabled).to be(false)
+ expect(query_recorder.log).not_to include(/p_ci_builds_metadata/)
+ end
+ end
+ end
end
diff --git a/spec/services/ci/create_downstream_pipeline_service_spec.rb b/spec/services/ci/create_downstream_pipeline_service_spec.rb
index 9c02c5218f1..bcdb2b4f796 100644
--- a/spec/services/ci/create_downstream_pipeline_service_spec.rb
+++ b/spec/services/ci/create_downstream_pipeline_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
+RSpec.describe Ci::CreateDownstreamPipelineService, '#execute', feature_category: :continuous_integration do
include Ci::SourcePipelineHelpers
# Using let_it_be on user and projects for these specs can cause
@@ -13,7 +13,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
let(:downstream_project) { create(:project, :repository) }
let!(:upstream_pipeline) do
- create(:ci_pipeline, :running, project: upstream_project)
+ create(:ci_pipeline, :created, project: upstream_project)
end
let(:trigger) do
@@ -33,6 +33,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
end
let(:service) { described_class.new(upstream_project, user) }
+ let(:pipeline) { subject.payload }
before do
upstream_project.add_developer(user)
@@ -40,6 +41,12 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
subject { service.execute(bridge) }
+ shared_context 'when ci_bridge_remove_sourced_pipelines is disabled' do
+ before do
+ stub_feature_flags(ci_bridge_remove_sourced_pipelines: false)
+ end
+ end
+
context 'when downstream project has not been found' do
let(:trigger) do
{ trigger: { project: 'unknown/project' } }
@@ -48,6 +55,8 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'does not create a pipeline' do
expect { subject }
.not_to change { Ci::Pipeline.count }
+ expect(subject).to be_error
+ expect(subject.message).to eq("Pre-conditions not met")
end
it 'changes pipeline bridge job status to failed' do
@@ -63,9 +72,11 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'does not create a new pipeline' do
expect { subject }
.not_to change { Ci::Pipeline.count }
+ expect(subject).to be_error
+ expect(subject.message).to eq("Pre-conditions not met")
end
- it 'changes status of the bridge build' do
+ it 'changes status of the bridge build to failed' do
subject
expect(bridge.reload).to be_failed
@@ -82,9 +93,11 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'does not create a new pipeline' do
expect { subject }
.not_to change { Ci::Pipeline.count }
+ expect(subject).to be_error
+ expect(subject.message).to eq("Pre-conditions not met")
end
- it 'changes status of the bridge build' do
+ it 'changes status of the bridge build to failed' do
subject
expect(bridge.reload).to be_failed
@@ -103,35 +116,51 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'creates only one new pipeline' do
expect { subject }
.to change { Ci::Pipeline.count }.by(1)
+ expect(subject).to be_success
end
it 'creates a new pipeline in a downstream project' do
- pipeline = subject
-
expect(pipeline.user).to eq bridge.user
expect(pipeline.project).to eq downstream_project
- expect(bridge.sourced_pipelines.first.pipeline).to eq pipeline
+ expect(bridge.reload.sourced_pipeline.pipeline).to eq pipeline
expect(pipeline.triggered_by_pipeline).to eq upstream_pipeline
expect(pipeline.source_bridge).to eq bridge
expect(pipeline.source_bridge).to be_a ::Ci::Bridge
end
+ context 'when ci_bridge_remove_sourced_pipelines is disabled' do
+ include_context 'when ci_bridge_remove_sourced_pipelines is disabled'
+
+ it 'creates a new pipeline in a downstream project' do
+ expect(pipeline.user).to eq bridge.user
+ expect(pipeline.project).to eq downstream_project
+ expect(bridge.sourced_pipelines.first.pipeline).to eq pipeline
+ expect(pipeline.triggered_by_pipeline).to eq upstream_pipeline
+ expect(pipeline.source_bridge).to eq bridge
+ expect(pipeline.source_bridge).to be_a ::Ci::Bridge
+ end
+ end
+
it_behaves_like 'logs downstream pipeline creation' do
+ let(:downstream_pipeline) { pipeline }
let(:expected_root_pipeline) { upstream_pipeline }
let(:expected_hierarchy_size) { 2 }
let(:expected_downstream_relationship) { :multi_project }
end
it 'updates bridge status when downstream pipeline gets processed' do
- pipeline = subject
-
expect(pipeline.reload).to be_created
expect(bridge.reload).to be_success
end
- context 'when bridge job has already any downstream pipelines' do
+ it 'triggers the upstream pipeline duration calculation', :sidekiq_inline do
+ expect { subject }
+ .to change { upstream_pipeline.reload.duration }.from(nil).to(an_instance_of(Integer))
+ end
+
+ context 'when bridge job has already any downstream pipeline' do
before do
- bridge.sourced_pipelines.create!(
+ bridge.create_sourced_pipeline!(
source_pipeline: bridge.pipeline,
source_project: bridge.project,
project: bridge.project,
@@ -147,7 +176,33 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
bridge_id: bridge.id, project_id: bridge.project.id)
.and_call_original
expect(Ci::CreatePipelineService).not_to receive(:new)
- expect(subject).to eq({ message: "Already has a downstream pipeline", status: :error })
+ expect(subject).to be_error
+ expect(subject.message).to eq("Already has a downstream pipeline")
+ end
+
+ context 'when ci_bridge_remove_sourced_pipelines is disabled' do
+ include_context 'when ci_bridge_remove_sourced_pipelines is disabled'
+
+ before do
+ bridge.sourced_pipelines.create!(
+ source_pipeline: bridge.pipeline,
+ source_project: bridge.project,
+ project: bridge.project,
+ pipeline: create(:ci_pipeline, project: bridge.project)
+ )
+ end
+
+ it 'logs an error and exits' do
+ expect(Gitlab::ErrorTracking)
+ .to receive(:track_exception)
+ .with(
+ instance_of(described_class::DuplicateDownstreamPipelineError),
+ bridge_id: bridge.id, project_id: bridge.project.id)
+ .and_call_original
+ expect(Ci::CreatePipelineService).not_to receive(:new)
+ expect(subject).to be_error
+ expect(subject.message).to eq("Already has a downstream pipeline")
+ end
end
end
@@ -157,8 +212,6 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
end
it 'is using default branch name' do
- pipeline = subject
-
expect(pipeline.ref).to eq 'master'
end
end
@@ -171,22 +224,33 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'creates only one new pipeline' do
expect { subject }
.to change { Ci::Pipeline.count }.by(1)
+ expect(subject).to be_error
+ expect(subject.message).to match_array(["jobs job config should implement a script: or a trigger: keyword"])
end
it 'creates a new pipeline in a downstream project' do
- pipeline = subject
-
expect(pipeline.user).to eq bridge.user
expect(pipeline.project).to eq downstream_project
- expect(bridge.sourced_pipelines.first.pipeline).to eq pipeline
+ expect(bridge.reload.sourced_pipeline.pipeline).to eq pipeline
expect(pipeline.triggered_by_pipeline).to eq upstream_pipeline
expect(pipeline.source_bridge).to eq bridge
expect(pipeline.source_bridge).to be_a ::Ci::Bridge
end
- it 'updates the bridge status when downstream pipeline gets processed' do
- pipeline = subject
+ context 'when ci_bridge_remove_sourced_pipelines is disabled' do
+ include_context 'when ci_bridge_remove_sourced_pipelines is disabled'
+
+ it 'creates a new pipeline in a downstream project' do
+ expect(pipeline.user).to eq bridge.user
+ expect(pipeline.project).to eq downstream_project
+ expect(bridge.sourced_pipelines.first.pipeline).to eq pipeline
+ expect(pipeline.triggered_by_pipeline).to eq upstream_pipeline
+ expect(pipeline.source_bridge).to eq bridge
+ expect(pipeline.source_bridge).to be_a ::Ci::Bridge
+ end
+ end
+ it 'updates the bridge status when downstream pipeline gets processed' do
expect(pipeline.reload).to be_failed
expect(bridge.reload).to be_failed
end
@@ -201,6 +265,8 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'does not create a new pipeline' do
expect { subject }
.not_to change { Ci::Pipeline.count }
+ expect(subject).to be_error
+ expect(subject.message).to eq("Pre-conditions not met")
end
it 'changes status of the bridge build' do
@@ -222,32 +288,39 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'creates only one new pipeline' do
expect { subject }
.to change { Ci::Pipeline.count }.by(1)
+ expect(subject).to be_success
end
it 'creates a child pipeline in the same project' do
- pipeline = subject
- pipeline.reload
-
expect(pipeline.builds.map(&:name)).to match_array(%w[rspec echo])
expect(pipeline.user).to eq bridge.user
expect(pipeline.project).to eq bridge.project
- expect(bridge.sourced_pipelines.first.pipeline).to eq pipeline
+ expect(bridge.reload.sourced_pipeline.pipeline).to eq pipeline
expect(pipeline.triggered_by_pipeline).to eq upstream_pipeline
expect(pipeline.source_bridge).to eq bridge
expect(pipeline.source_bridge).to be_a ::Ci::Bridge
end
- it 'updates bridge status when downstream pipeline gets processed' do
- pipeline = subject
+ context 'when ci_bridge_remove_sourced_pipelines is disabled' do
+ include_context 'when ci_bridge_remove_sourced_pipelines is disabled'
+
+ it 'creates a child pipeline in the same project' do
+ expect(pipeline.builds.map(&:name)).to match_array(%w[rspec echo])
+ expect(pipeline.user).to eq bridge.user
+ expect(pipeline.project).to eq bridge.project
+ expect(bridge.sourced_pipelines.first.pipeline).to eq pipeline
+ expect(pipeline.triggered_by_pipeline).to eq upstream_pipeline
+ expect(pipeline.source_bridge).to eq bridge
+ expect(pipeline.source_bridge).to be_a ::Ci::Bridge
+ end
+ end
+ it 'updates bridge status when downstream pipeline gets processed' do
expect(pipeline.reload).to be_created
expect(bridge.reload).to be_success
end
it 'propagates parent pipeline settings to the child pipeline' do
- pipeline = subject
- pipeline.reload
-
expect(pipeline.ref).to eq(upstream_pipeline.ref)
expect(pipeline.sha).to eq(upstream_pipeline.sha)
expect(pipeline.source_sha).to eq(upstream_pipeline.source_sha)
@@ -276,6 +349,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it_behaves_like 'creates a child pipeline'
it_behaves_like 'logs downstream pipeline creation' do
+ let(:downstream_pipeline) { pipeline }
let(:expected_root_pipeline) { upstream_pipeline }
let(:expected_hierarchy_size) { 2 }
let(:expected_downstream_relationship) { :parent_child }
@@ -283,6 +357,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'updates the bridge job to success' do
expect { subject }.to change { bridge.status }.to 'success'
+ expect(subject).to be_success
end
context 'when bridge uses "depend" strategy' do
@@ -292,8 +367,9 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
}
end
- it 'does not update the bridge job status' do
- expect { subject }.not_to change { bridge.status }
+ it 'update the bridge job to running status' do
+ expect { subject }.to change { bridge.status }.from('pending').to('running')
+ expect(subject).to be_success
end
end
@@ -323,8 +399,6 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it_behaves_like 'creates a child pipeline'
it 'propagates the merge request to the child pipeline' do
- pipeline = subject
-
expect(pipeline.merge_request).to eq(merge_request)
expect(pipeline).to be_merge_request
end
@@ -341,11 +415,13 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'creates the pipeline' do
expect { subject }
.to change { Ci::Pipeline.count }.by(1)
+ expect(subject).to be_success
expect(bridge.reload).to be_success
end
it_behaves_like 'logs downstream pipeline creation' do
+ let(:downstream_pipeline) { pipeline }
let(:expected_root_pipeline) { upstream_pipeline.parent_pipeline }
let(:expected_hierarchy_size) { 3 }
let(:expected_downstream_relationship) { :parent_child }
@@ -394,6 +470,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'create the pipeline' do
expect { subject }.to change { Ci::Pipeline.count }.by(1)
+ expect(subject).to be_success
end
end
@@ -406,11 +483,10 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'creates a new pipeline allowing variables to be passed downstream' do
expect { subject }.to change { Ci::Pipeline.count }.by(1)
+ expect(subject).to be_success
end
it 'passes variables downstream from the bridge' do
- pipeline = subject
-
pipeline.variables.map(&:key).tap do |variables|
expect(variables).to include 'BRIDGE'
end
@@ -466,6 +542,8 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'does not create a new pipeline' do
expect { subject }
.not_to change { Ci::Pipeline.count }
+ expect(subject).to be_error
+ expect(subject.message).to eq("Pre-conditions not met")
end
it 'changes status of the bridge build' do
@@ -480,6 +558,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'creates a new pipeline' do
expect { subject }
.to change { Ci::Pipeline.count }
+ expect(subject).to be_success
end
it 'expect bridge build not to be failed' do
@@ -559,18 +638,16 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'creates only one new pipeline' do
expect { subject }
.to change { Ci::Pipeline.count }.by(1)
+ expect(subject).to be_error
+ expect(subject.message).to match_array(["jobs invalid config should implement a script: or a trigger: keyword"])
end
it 'creates a new pipeline in the downstream project' do
- pipeline = subject
-
expect(pipeline.user).to eq bridge.user
expect(pipeline.project).to eq downstream_project
end
it 'drops the bridge' do
- pipeline = subject
-
expect(pipeline.reload).to be_failed
expect(bridge.reload).to be_failed
expect(bridge.failure_reason).to eq('downstream_pipeline_creation_failed')
@@ -585,15 +662,10 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
bridge.drop!
end
- it 'tracks the exception' do
- expect(Gitlab::ErrorTracking)
- .to receive(:track_exception)
- .with(
- instance_of(Ci::Bridge::InvalidTransitionError),
- bridge_id: bridge.id,
- downstream_pipeline_id: kind_of(Numeric))
-
- subject
+ it 'returns the error' do
+ expect { subject }.not_to change(downstream_project.ci_pipelines, :count)
+ expect(subject).to be_error
+ expect(subject.message).to eq('Can not run the bridge')
end
end
@@ -603,8 +675,6 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
end
it 'passes bridge variables to downstream pipeline' do
- pipeline = subject
-
expect(pipeline.variables.first)
.to have_attributes(key: 'BRIDGE', value: 'var')
end
@@ -616,8 +686,6 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
end
it 'does not pass pipeline variables directly downstream' do
- pipeline = subject
-
pipeline.variables.map(&:key).tap do |variables|
expect(variables).not_to include 'PIPELINE_VARIABLE'
end
@@ -629,8 +697,6 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
end
it 'makes it possible to pass pipeline variable downstream' do
- pipeline = subject
-
pipeline.variables.find_by(key: 'BRIDGE').tap do |variable|
expect(variable.value).to eq 'my-value-var'
end
@@ -644,11 +710,11 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'does not create a new pipeline' do
expect { subject }
.not_to change { Ci::Pipeline.count }
+ expect(subject).to be_error
+ expect(subject.message).to match_array(["Insufficient permissions to set pipeline variables"])
end
it 'ignores variables passed downstream from the bridge' do
- pipeline = subject
-
pipeline.variables.map(&:key).tap do |variables|
expect(variables).not_to include 'BRIDGE'
end
@@ -668,7 +734,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
# TODO: Move this context into a feature spec that uses
# multiple pipeline processing services. Location TBD in:
# https://gitlab.com/gitlab-org/gitlab/issues/36216
- context 'when configured with bridge job rules' do
+ context 'when configured with bridge job rules', :sidekiq_inline do
before do
stub_ci_pipeline_yaml_file(config)
downstream_project.add_maintainer(upstream_project.first_owner)
@@ -701,6 +767,8 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'creates the downstream pipeline' do
expect { subject }
.to change(downstream_project.ci_pipelines, :count).by(1)
+ expect(subject).to be_error
+ expect(subject.message).to eq("Already has a downstream pipeline")
end
end
end
@@ -731,6 +799,8 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'does not create a pipeline and drops the bridge' do
expect { subject }.not_to change(downstream_project.ci_pipelines, :count)
+ expect(subject).to be_error
+ expect(subject.message).to match_array(["Reference not found"])
expect(bridge.reload).to be_failed
expect(bridge.failure_reason).to eq('downstream_pipeline_creation_failed')
@@ -754,6 +824,8 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'does not create a pipeline and drops the bridge' do
expect { subject }.not_to change(downstream_project.ci_pipelines, :count)
+ expect(subject).to be_error
+ expect(subject.message).to match_array(["No stages / jobs for this pipeline."])
expect(bridge.reload).to be_failed
expect(bridge.failure_reason).to eq('downstream_pipeline_creation_failed')
@@ -776,6 +848,10 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'creates the pipeline but drops the bridge' do
expect { subject }.to change(downstream_project.ci_pipelines, :count).by(1)
+ expect(subject).to be_error
+ expect(subject.message).to eq(
+ ["test job: chosen stage does not exist; available stages are .pre, build, test, deploy, .post"]
+ )
expect(bridge.reload).to be_failed
expect(bridge.failure_reason).to eq('downstream_pipeline_creation_failed')
@@ -808,6 +884,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'creates the pipeline' do
expect { subject }.to change(downstream_project.ci_pipelines, :count).by(1)
+ expect(subject).to be_success
expect(bridge.reload).to be_success
end
@@ -822,6 +899,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
context 'when a downstream pipeline has sibling pipelines' do
it_behaves_like 'logs downstream pipeline creation' do
+ let(:downstream_pipeline) { pipeline }
let(:expected_root_pipeline) { upstream_pipeline }
let(:expected_downstream_relationship) { :multi_project }
@@ -839,23 +917,47 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
let_it_be(:child) { create(:ci_pipeline, child_of: parent) }
let_it_be(:sibling) { create(:ci_pipeline, child_of: parent) }
- before do
- stub_const("#{described_class}::MAX_HIERARCHY_SIZE", 3)
- end
-
+ let(:project) { build(:project, :repository) }
let(:bridge) do
- create(:ci_bridge, status: :pending, user: user, options: trigger, pipeline: child)
+ create(:ci_bridge, status: :pending, user: user, options: trigger, pipeline: child, project: project)
end
- it 'does not create a new pipeline' do
- expect { subject }.not_to change { Ci::Pipeline.count }
+ context 'when limit was specified by admin' do
+ before do
+ project.actual_limits.update!(pipeline_hierarchy_size: 3)
+ end
+
+ it 'does not create a new pipeline' do
+ expect { subject }.not_to change { Ci::Pipeline.count }
+ end
+
+ it 'drops the trigger job with an explanatory reason' do
+ subject
+
+ expect(bridge.reload).to be_failed
+ expect(bridge.failure_reason).to eq('reached_max_pipeline_hierarchy_size')
+ end
end
- it 'drops the trigger job with an explanatory reason' do
- subject
+ context 'when there was no limit specified by admin' do
+ before do
+ allow(bridge.pipeline).to receive(:complete_hierarchy_count).and_return(1000)
+ end
- expect(bridge.reload).to be_failed
- expect(bridge.failure_reason).to eq('reached_max_pipeline_hierarchy_size')
+ context 'when pipeline count reaches the default limit of 1000' do
+ it 'does not create a new pipeline' do
+ expect { subject }.not_to change { Ci::Pipeline.count }
+ expect(subject).to be_error
+ expect(subject.message).to eq("Pre-conditions not met")
+ end
+
+ it 'drops the trigger job with an explanatory reason' do
+ subject
+
+ expect(bridge.reload).to be_failed
+ expect(bridge.failure_reason).to eq('reached_max_pipeline_hierarchy_size')
+ end
+ end
end
context 'with :ci_limit_complete_hierarchy_size disabled' do
@@ -865,6 +967,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'creates a new pipeline' do
expect { subject }.to change { Ci::Pipeline.count }.by(1)
+ expect(subject).to be_success
end
it 'marks the bridge job as successful' do
diff --git a/spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb b/spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb
index 74d3534eb45..0d5017a763f 100644
--- a/spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb
+++ b/spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb
@@ -57,7 +57,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute', :yaml_processor_feature_fl
pipeline = create_pipeline!
test = pipeline.statuses.find_by(name: 'instrumentation_test')
- expect(test).to be_pending
+ expect(test).to be_running
expect(pipeline.triggered_pipelines.count).to eq(1)
end
diff --git a/spec/services/ci/create_pipeline_service/environment_spec.rb b/spec/services/ci/create_pipeline_service/environment_spec.rb
index 438cb6ac895..b713cad2cad 100644
--- a/spec/services/ci/create_pipeline_service/environment_spec.rb
+++ b/spec/services/ci/create_pipeline_service/environment_spec.rb
@@ -46,6 +46,24 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
end
end
+ context 'when branch pipeline creates a dynamic environment' do
+ before do
+ config = YAML.dump(
+ review_app: {
+ script: 'echo',
+ environment: { name: "review/$CI_COMMIT_REF_NAME" }
+ })
+
+ stub_ci_pipeline_yaml_file(config)
+ end
+
+ it 'does not associate merge request with the environment' do
+ is_expected.to be_created_successfully
+
+ expect(Environment.find_by_name('review/master').merge_request).to be_nil
+ end
+ end
+
context 'when variables are dependent on stage name' do
let(:config) do
<<~YAML
diff --git a/spec/services/ci/create_pipeline_service/logger_spec.rb b/spec/services/ci/create_pipeline_service/logger_spec.rb
index 3045f8e92b1..ccb15bfa684 100644
--- a/spec/services/ci/create_pipeline_service/logger_spec.rb
+++ b/spec/services/ci/create_pipeline_service/logger_spec.rb
@@ -2,8 +2,10 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
- context 'pipeline logger' do
+RSpec.describe Ci::CreatePipelineService, # rubocop: disable RSpec/FilePath
+ :yaml_processor_feature_flag_corectness,
+ feature_category: :continuous_integration do
+ describe 'pipeline logger' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
@@ -12,17 +14,11 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
let(:pipeline) { service.execute(:push).payload }
let(:file_location) { 'spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml' }
- before do
- stub_ci_pipeline_yaml_file(gitlab_ci_yaml)
- end
-
let(:counters) do
{
'count' => a_kind_of(Numeric),
- 'avg' => a_kind_of(Numeric),
- 'sum' => a_kind_of(Numeric),
'max' => a_kind_of(Numeric),
- 'min' => a_kind_of(Numeric)
+ 'sum' => a_kind_of(Numeric)
}
end
@@ -34,15 +30,22 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
'pipeline_persisted' => true,
'project_id' => project.id,
'pipeline_creation_service_duration_s' => a_kind_of(Numeric),
- 'pipeline_creation_duration_s' => counters,
- 'pipeline_size_count' => counters,
- 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => counters,
+ 'pipeline_creation_duration_s' => a_kind_of(Numeric),
+ 'pipeline_size_count' => a_kind_of(Numeric),
+ 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => a_kind_of(Numeric),
'pipeline_seed_build_inclusion_duration_s' => counters,
+ 'pipeline_seed_build_errors_duration_s' => counters,
+ 'pipeline_seed_build_to_resource_duration_s' => counters,
+ 'pipeline_seed_stage_seeds_duration_s' => counters,
'pipeline_builds_tags_count' => a_kind_of(Numeric),
'pipeline_builds_distinct_tags_count' => a_kind_of(Numeric)
}
end
+ before do
+ stub_ci_pipeline_yaml_file(gitlab_ci_yaml)
+ end
+
context 'when the duration is under the threshold' do
it 'does not create a log entry but it collects the data' do
expect(Gitlab::AppJsonLogger).not_to receive(:info)
@@ -51,9 +54,9 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
expect(service.logger.observations_hash)
.to match(
a_hash_including(
- 'pipeline_creation_duration_s' => counters,
- 'pipeline_size_count' => counters,
- 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => counters
+ 'pipeline_creation_duration_s' => a_kind_of(Numeric),
+ 'pipeline_size_count' => a_kind_of(Numeric),
+ 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => a_kind_of(Numeric)
)
)
end
@@ -62,7 +65,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
context 'when the durations exceeds the threshold' do
let(:timer) do
proc do
- @timer = @timer.to_i + 30
+ @timer = @timer.to_i + 30 # rubocop: disable RSpec/InstanceVariable
end
end
@@ -88,17 +91,15 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
'pipeline_persisted' => false,
'project_id' => project.id,
'pipeline_creation_service_duration_s' => a_kind_of(Numeric),
- 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => counters
+ 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => a_kind_of(Numeric)
}
end
- before do
+ it 'creates a log entry' do
allow_next_instance_of(Ci::Pipeline) do |pipeline|
expect(pipeline).to receive(:save!).and_raise { RuntimeError }
end
- end
- it 'creates a log entry' do
expect(Gitlab::AppJsonLogger)
.to receive(:info)
.with(a_hash_including(loggable_data))
@@ -125,7 +126,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
context 'when the size exceeds the threshold' do
before do
allow_next_instance_of(Ci::Pipeline) do |pipeline|
- allow(pipeline).to receive(:total_size) { 5000 }
+ allow(pipeline).to receive(:total_size).and_return(5000)
end
end
diff --git a/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb b/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb
index 513cbbed6cd..eb17935967c 100644
--- a/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb
+++ b/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb
@@ -108,7 +108,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute', :yaml_processor_feature_fl
pipeline = create_pipeline!
test = pipeline.statuses.find_by(name: 'instrumentation_test')
- expect(test).to be_pending
+ expect(test).to be_running
expect(pipeline.triggered_pipelines.count).to eq(1)
end
diff --git a/spec/services/ci/create_pipeline_service/partitioning_spec.rb b/spec/services/ci/create_pipeline_service/partitioning_spec.rb
index f34d103d965..a87135cefdd 100644
--- a/spec/services/ci/create_pipeline_service/partitioning_spec.rb
+++ b/spec/services/ci/create_pipeline_service/partitioning_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness, :aggregate_failures,
-:ci_partitionable do
+:ci_partitionable, feature_category: :continuous_integration do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
@@ -15,8 +15,13 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
- test
- deploy
+ needs:build:
+ stage: build
+ script: echo "needs..."
+
build:
stage: build
+ needs: ["needs:build"]
script: make build
test:
@@ -95,6 +100,12 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
expect(pipeline.variables.size).to eq(2)
expect(variables_partition_ids).to eq([current_partition_id])
end
+
+ it 'assigns partition_id to needs' do
+ needs = find_need('build')
+
+ expect(needs.partition_id).to eq(current_partition_id)
+ end
end
context 'with parent child pipelines' do
@@ -144,4 +155,12 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
.find { |job| job.name == name }
.metadata
end
+
+ def find_need(name)
+ pipeline
+ .processables
+ .find { |job| job.name == name }
+ .needs
+ .first
+ end
end
diff --git a/spec/services/ci/create_pipeline_service/rules_spec.rb b/spec/services/ci/create_pipeline_service/rules_spec.rb
index 5fdefb2b306..b866293393b 100644
--- a/spec/services/ci/create_pipeline_service/rules_spec.rb
+++ b/spec/services/ci/create_pipeline_service/rules_spec.rb
@@ -912,7 +912,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
context 'when outside freeze period' do
it 'creates two jobs' do
- Timecop.freeze(2020, 4, 10, 22, 59) do
+ travel_to(Time.utc(2020, 4, 10, 22, 59)) do
expect(pipeline).to be_persisted
expect(build_names).to contain_exactly('test-job', 'deploy-job')
end
@@ -921,7 +921,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
context 'when inside freeze period' do
it 'creates one job' do
- Timecop.freeze(2020, 4, 10, 23, 1) do
+ travel_to(Time.utc(2020, 4, 10, 23, 1)) do
expect(pipeline).to be_persisted
expect(build_names).to contain_exactly('test-job')
end
@@ -946,7 +946,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
context 'when outside freeze period' do
it 'creates two jobs' do
- Timecop.freeze(2020, 4, 10, 22, 59) do
+ travel_to(Time.utc(2020, 4, 10, 22, 59)) do
expect(pipeline).to be_persisted
expect(build_names).to contain_exactly('deploy-job')
end
@@ -955,7 +955,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
context 'when inside freeze period' do
it 'does not create the pipeline', :aggregate_failures do
- Timecop.freeze(2020, 4, 10, 23, 1) do
+ travel_to(Time.utc(2020, 4, 10, 23, 1)) do
expect(response).to be_error
expect(pipeline).not_to be_persisted
end
diff --git a/spec/services/ci/create_pipeline_service/scripts_spec.rb b/spec/services/ci/create_pipeline_service/scripts_spec.rb
new file mode 100644
index 00000000000..50b558e505a
--- /dev/null
+++ b/spec/services/ci/create_pipeline_service/scripts_spec.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { project.first_owner }
+
+ let(:service) { described_class.new(project, user, { ref: 'master' }) }
+ let(:pipeline) { service.execute(:push).payload }
+
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ end
+
+ context 'when job has script and nested before_script and after_script' do
+ let(:config) do
+ <<-CI_CONFIG
+ default:
+ before_script: echo 'hello default before_script'
+ after_script: echo 'hello default after_script'
+
+ job:
+ before_script: echo 'hello job before_script'
+ after_script: echo 'hello job after_script'
+ script: echo 'hello job script'
+ CI_CONFIG
+ end
+
+ it 'creates a job with script data' do
+ expect(pipeline).to be_created_successfully
+ expect(pipeline.builds.first).to have_attributes(
+ name: 'job',
+ stage: 'test',
+ options: { script: ["echo 'hello job script'"],
+ before_script: ["echo 'hello job before_script'"],
+ after_script: ["echo 'hello job after_script'"] }
+ )
+ end
+ end
+
+ context 'when job has hooks and default hooks' do
+ let(:config) do
+ <<-CI_CONFIG
+ default:
+ hooks:
+ pre_get_sources_script:
+ - echo 'hello default pre_get_sources_script'
+
+ job1:
+ hooks:
+ pre_get_sources_script:
+ - echo 'hello job1 pre_get_sources_script'
+ script: echo 'hello job1 script'
+
+ job2:
+ script: echo 'hello job2 script'
+
+ job3:
+ inherit:
+ default: false
+ script: echo 'hello job3 script'
+ CI_CONFIG
+ end
+
+ it 'creates jobs with hook data' do
+ expect(pipeline).to be_created_successfully
+ expect(pipeline.builds.find_by(name: 'job1')).to have_attributes(
+ name: 'job1',
+ stage: 'test',
+ options: { script: ["echo 'hello job1 script'"],
+ hooks: { pre_get_sources_script: ["echo 'hello job1 pre_get_sources_script'"] } }
+ )
+ expect(pipeline.builds.find_by(name: 'job2')).to have_attributes(
+ name: 'job2',
+ stage: 'test',
+ options: { script: ["echo 'hello job2 script'"],
+ hooks: { pre_get_sources_script: ["echo 'hello default pre_get_sources_script'"] } }
+ )
+ expect(pipeline.builds.find_by(name: 'job3')).to have_attributes(
+ name: 'job3',
+ stage: 'test',
+ options: { script: ["echo 'hello job3 script'"] }
+ )
+ end
+
+ context 'when the FF ci_hooks_pre_get_sources_script is disabled' do
+ before do
+ stub_feature_flags(ci_hooks_pre_get_sources_script: false)
+ end
+
+ it 'creates jobs without hook data' do
+ expect(pipeline).to be_created_successfully
+ expect(pipeline.builds.find_by(name: 'job1')).to have_attributes(
+ name: 'job1',
+ stage: 'test',
+ options: { script: ["echo 'hello job1 script'"] }
+ )
+ expect(pipeline.builds.find_by(name: 'job2')).to have_attributes(
+ name: 'job2',
+ stage: 'test',
+ options: { script: ["echo 'hello job2 script'"] }
+ )
+ expect(pipeline.builds.find_by(name: 'job3')).to have_attributes(
+ name: 'job3',
+ stage: 'test',
+ options: { script: ["echo 'hello job3 script'"] }
+ )
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index 67c13649c6f..8628e95ba80 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -79,11 +79,11 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
let(:accepted_n_plus_ones) do
1 + # SELECT "ci_instance_variables"
- 1 + # INSERT INTO "ci_stages"
- 1 + # SELECT "ci_builds".* FROM "ci_builds"
- 1 + # INSERT INTO "ci_builds"
- 1 + # INSERT INTO "ci_builds_metadata"
- 1 # SELECT "taggings".* FROM "taggings"
+ 1 + # INSERT INTO "ci_stages"
+ 1 + # SELECT "ci_builds".* FROM "ci_builds"
+ 1 + # INSERT INTO "ci_builds"
+ 1 + # INSERT INTO "ci_builds_metadata"
+ 1 # SELECT "taggings".* FROM "taggings"
end
end
end
@@ -710,6 +710,29 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
end
end
+ context 'when the configuration includes ID tokens' do
+ it 'creates variables for the ID tokens' do
+ config = YAML.dump({
+ job_with_id_tokens: {
+ script: 'ls',
+ id_tokens: {
+ 'TEST_ID_TOKEN' => {
+ aud: 'https://gitlab.com'
+ }
+ }
+ }
+ })
+ stub_ci_pipeline_yaml_file(config)
+
+ result = execute_service.payload
+
+ expect(result).to be_persisted
+ expect(result.builds.first.id_tokens).to eq({
+ 'TEST_ID_TOKEN' => { 'aud' => 'https://gitlab.com' }
+ })
+ end
+ end
+
context 'with manual actions' do
before do
config = YAML.dump({ deploy: { script: 'ls', when: 'manual' } })
diff --git a/spec/services/ci/enqueue_job_service_spec.rb b/spec/services/ci/enqueue_job_service_spec.rb
new file mode 100644
index 00000000000..c2bb0bb2bb5
--- /dev/null
+++ b/spec/services/ci/enqueue_job_service_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::EnqueueJobService, '#execute', feature_category: :continuous_integration do
+ let_it_be(:project) { create(:project) }
+ let(:user) { create(:user, developer_projects: [project]) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, :manual, pipeline: pipeline) }
+
+ let(:service) do
+ described_class.new(build, current_user: user)
+ end
+
+ subject(:execute) { service.execute }
+
+ it 'assigns the user to the job' do
+ expect { execute }.to change { build.reload.user }.to(user)
+ end
+
+ it 'calls enqueue!' do
+ expect(build).to receive(:enqueue!)
+ execute
+ end
+
+ it 'calls Ci::ResetSkippedJobsService' do
+ expect_next_instance_of(Ci::ResetSkippedJobsService) do |service|
+ expect(service).to receive(:execute).with(build)
+ end
+
+ execute
+ end
+
+ it 'returns the job' do
+ expect(execute).to eq(build)
+ end
+
+ context 'when variables are supplied' do
+ let(:job_variables) do
+ [{ key: 'first', secret_value: 'first' },
+ { key: 'second', secret_value: 'second' }]
+ end
+
+ let(:service) do
+ described_class.new(build, current_user: user, variables: job_variables)
+ end
+
+ it 'assigns the variables to the job' do
+ execute
+ expect(build.reload.job_variables.map(&:key)).to contain_exactly('first', 'second')
+ end
+ end
+
+ context 'when the job transition is invalid' do
+ let(:bridge) { create(:ci_bridge, :failed, pipeline: pipeline, project: project) }
+
+ let(:service) do
+ described_class.new(bridge, current_user: user)
+ end
+
+ it 'raises StateMachines::InvalidTransition' do
+ expect { execute }.to raise_error StateMachines::InvalidTransition
+ end
+ end
+
+ context 'when a transition block is supplied' do
+ let(:bridge) { create(:ci_bridge, :playable, pipeline: pipeline) }
+
+ let(:service) do
+ described_class.new(bridge, current_user: user)
+ end
+
+ subject(:execute) { service.execute(&:pending!) }
+
+ it 'calls the transition block instead of enqueue!' do
+ expect(bridge).to receive(:pending!)
+ expect(bridge).not_to receive(:enqueue!)
+ execute
+ end
+ end
+end
diff --git a/spec/services/ci/generate_kubeconfig_service_spec.rb b/spec/services/ci/generate_kubeconfig_service_spec.rb
index bfde39780dd..c0858b0f0c9 100644
--- a/spec/services/ci/generate_kubeconfig_service_spec.rb
+++ b/spec/services/ci/generate_kubeconfig_service_spec.rb
@@ -4,52 +4,98 @@ require 'spec_helper'
RSpec.describe Ci::GenerateKubeconfigService do
describe '#execute' do
- let(:project) { create(:project) }
- let(:build) { create(:ci_build, project: project) }
- let(:pipeline) { build.pipeline }
- let(:agent1) { create(:cluster_agent, project: project) }
- let(:agent2) { create(:cluster_agent) }
- let(:authorization1) { create(:agent_project_authorization, agent: agent1) }
- let(:authorization2) { create(:agent_project_authorization, agent: agent2) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, project: project) }
+ let_it_be(:build) { create(:ci_build, project: project, pipeline: pipeline) }
- let(:template) { instance_double(Gitlab::Kubernetes::Kubeconfig::Template) }
+ let_it_be(:agent_project) { create(:project, group: group, name: 'project-containing-agent-config') }
- subject { described_class.new(pipeline, token: build.token).execute }
+ let_it_be(:project_agent_authorization) do
+ agent = create(:cluster_agent, project: agent_project)
+ create(:agent_project_authorization, agent: agent, project: project)
+ end
+
+ let_it_be(:group_agent_authorization) do
+ agent = create(:cluster_agent, project: agent_project)
+ create(:agent_group_authorization, agent: agent, group: group)
+ end
+
+ let(:template) do
+ instance_double(
+ Gitlab::Kubernetes::Kubeconfig::Template,
+ add_cluster: nil,
+ add_user: nil,
+ add_context: nil
+ )
+ end
+
+ let(:agent_authorizations) { [project_agent_authorization, group_agent_authorization] }
+ let(:filter_service) do
+ instance_double(
+ ::Clusters::Agents::FilterAuthorizationsService,
+ execute: agent_authorizations
+ )
+ end
+
+ subject(:execute) { described_class.new(pipeline, token: build.token, environment: nil).execute }
before do
- expect(Gitlab::Kubernetes::Kubeconfig::Template).to receive(:new).and_return(template)
- expect(pipeline).to receive(:cluster_agent_authorizations).and_return([authorization1, authorization2])
+ allow(Gitlab::Kubernetes::Kubeconfig::Template).to receive(:new).and_return(template)
+ allow(::Clusters::Agents::FilterAuthorizationsService).to receive(:new).and_return(filter_service)
+ end
+
+ it 'returns a Kubeconfig Template' do
+ expect(execute).to eq(template)
end
- it 'adds a cluster, and a user and context for each available agent' do
+ it 'adds a cluster' do
expect(template).to receive(:add_cluster).with(
name: 'gitlab',
url: Gitlab::Kas.tunnel_url
).once
- expect(template).to receive(:add_user).with(
- name: "agent:#{agent1.id}",
- token: "ci:#{agent1.id}:#{build.token}"
- )
- expect(template).to receive(:add_user).with(
- name: "agent:#{agent2.id}",
- token: "ci:#{agent2.id}:#{build.token}"
- )
+ execute
+ end
- expect(template).to receive(:add_context).with(
- name: "#{project.full_path}:#{agent1.name}",
- namespace: 'production',
- cluster: 'gitlab',
- user: "agent:#{agent1.id}"
- )
- expect(template).to receive(:add_context).with(
- name: "#{agent2.project.full_path}:#{agent2.name}",
- namespace: 'production',
- cluster: 'gitlab',
- user: "agent:#{agent2.id}"
+ it "filters the pipeline's agents by `nil` environment" do
+ expect(::Clusters::Agents::FilterAuthorizationsService).to receive(:new).with(
+ pipeline.cluster_agent_authorizations,
+ environment: nil
)
- expect(subject).to eq(template)
+ execute
+ end
+
+ it 'adds user and context for all eligible agents', :aggregate_failures do
+ agent_authorizations.each do |authorization|
+ expect(template).to receive(:add_user).with(
+ name: "agent:#{authorization.agent.id}",
+ token: "ci:#{authorization.agent.id}:#{build.token}"
+ )
+
+ expect(template).to receive(:add_context).with(
+ name: "#{agent_project.full_path}:#{authorization.agent.name}",
+ namespace: 'production',
+ cluster: 'gitlab',
+ user: "agent:#{authorization.agent.id}"
+ )
+ end
+
+ execute
+ end
+
+ context 'when environment is specified' do
+ subject(:execute) { described_class.new(pipeline, token: build.token, environment: 'production').execute }
+
+ it "filters the pipeline's agents by the specified environment" do
+ expect(::Clusters::Agents::FilterAuthorizationsService).to receive(:new).with(
+ pipeline.cluster_agent_authorizations,
+ environment: 'production'
+ )
+
+ execute
+ end
end
end
end
diff --git a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb
index 1fbefc1fa22..2f2af9f6c85 100644
--- a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb
+++ b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::PipelineProcessing::AtomicProcessingService do
+RSpec.describe Ci::PipelineProcessing::AtomicProcessingService, feature_category: :continuous_integration do
describe 'Pipeline Processing Service Tests With Yaml' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/pipeline_schedules/calculate_next_run_service_spec.rb b/spec/services/ci/pipeline_schedules/calculate_next_run_service_spec.rb
new file mode 100644
index 00000000000..182c5bebbc1
--- /dev/null
+++ b/spec/services/ci/pipeline_schedules/calculate_next_run_service_spec.rb
@@ -0,0 +1,107 @@
+# frozen_string_literal: true
+# rubocop:disable Layout/LineLength
+require 'spec_helper'
+
+RSpec.describe Ci::PipelineSchedules::CalculateNextRunService, feature_category: :continuous_integration do
+ let_it_be(:project) { create(:project, :public, :repository) }
+
+ describe '#execute' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:run_service) do
+ described_class.new(project).execute(pipeline_schedule,
+ fallback_method: pipeline_schedule.method(:calculate_next_run_at))
+ end
+
+ let(:pipeline_schedule) { create(:ci_pipeline_schedule, cron: schedule_cron) }
+ let(:daily_limit_of_144_runs) { 1.day / 10.minutes }
+ let(:daily_limit_of_24_runs) { 1.day / 1.hour }
+
+ before do
+ allow(Settings).to receive(:cron_jobs) { { 'pipeline_schedule_worker' => { 'cron' => worker_cron } } }
+ create(:plan_limits, :default_plan, ci_daily_pipeline_schedule_triggers: plan_limit) if plan_limit
+ end
+
+ context "when there is invalid or no plan limits" do
+ where(:worker_cron, :schedule_cron, :plan_limit, :now, :expected_result) do
+ '0 1 2 3 *' | '0 1 * * *' | nil | Time.zone.local(2021, 3, 2, 1, 0) | Time.zone.local(2022, 3, 2, 1, 0)
+ '*/5 * * * *' | '*/1 * * * *' | nil | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 5)
+ '*/5 * * * *' | '0 * * * *' | nil | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 5)
+ # 1.day / 2.hours => 12 times a day and it is invalid because there is a minimum for plan limits.
+ # See: https://docs.gitlab.com/ee/administration/instance_limits.html#limit-the-number-of-pipelines-created-by-a-pipeline-schedule-per-day
+ '*/5 * * * *' | '0 * * * *' | 1.day / 2.hours | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 5)
+ end
+
+ with_them do
+ it 'calls fallback method to get next_run_at' do
+ travel_to(now) do
+ expect(pipeline_schedule).to receive(:calculate_next_run_at).and_call_original
+
+ result = run_service
+
+ expect(result).to eq(expected_result)
+ end
+ end
+ end
+ end
+
+ context "when the workers next run matches schedule's earliest run" do
+ where(:worker_cron, :schedule_cron, :plan_limit, :now, :expected_result) do
+ '*/5 * * * *' | '0 * * * *' | daily_limit_of_144_runs | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 0)
+ '*/5 * * * *' | '*/5 * * * *' | daily_limit_of_144_runs | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 10)
+ '*/5 * * * *' | '0 1 * * *' | daily_limit_of_144_runs | Time.zone.local(2021, 5, 27, 1, 0) | Time.zone.local(2021, 5, 28, 1, 0)
+ '*/5 * * * *' | '0 2 * * *' | daily_limit_of_144_runs | Time.zone.local(2021, 5, 27, 1, 0) | Time.zone.local(2021, 5, 27, 2, 0)
+ '*/5 * * * *' | '0 3 * * *' | daily_limit_of_144_runs | Time.zone.local(2021, 5, 27, 1, 0) | Time.zone.local(2021, 5, 27, 3, 0)
+ '*/5 * * * *' | '0 1 1 * *' | daily_limit_of_144_runs | Time.zone.local(2021, 5, 1, 1, 0) | Time.zone.local(2021, 6, 1, 1, 0)
+ '*/9 * * * *' | '0 1 1 * *' | daily_limit_of_144_runs | Time.zone.local(2021, 5, 1, 1, 9) | Time.zone.local(2021, 6, 1, 1, 0)
+ '*/5 * * * *' | '45 21 1 2 *' | daily_limit_of_144_runs | Time.zone.local(2021, 2, 1, 21, 45) | Time.zone.local(2022, 2, 1, 21, 45)
+ end
+
+ with_them do
+ it 'calculates the next_run_at to be earliest point of match' do
+ travel_to(now) do
+ result = run_service
+
+ expect(result).to eq(expected_result)
+ end
+ end
+ end
+ end
+
+ context "when next_run_at is restricted by plan limit" do
+ where(:worker_cron, :schedule_cron, :plan_limit, :now, :expected_result) do
+ '*/5 * * * *' | '59 14 * * *' | daily_limit_of_24_runs | Time.zone.local(2021, 5, 1, 15, 0) | Time.zone.local(2021, 5, 2, 15, 0)
+ '*/5 * * * *' | '*/1 * * * *' | daily_limit_of_24_runs | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 0)
+ '*/5 * * * *' | '*/1 * * * *' | daily_limit_of_144_runs | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 10)
+ '*/5 * * * *' | '*/1 * * * *' | (1.day / 7.minutes).to_i | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 10)
+ end
+
+ with_them do
+ it 'calculates the next_run_at based on next available limit' do
+ travel_to(now) do
+ result = run_service
+
+ expect(result).to eq(expected_result)
+ end
+ end
+ end
+ end
+
+ context "when next_run_at is restricted by worker's availability" do
+ where(:worker_cron, :schedule_cron, :plan_limit, :now, :expected_result) do
+ '0 1 2 3 *' | '0 1 * * *' | daily_limit_of_144_runs | Time.zone.local(2021, 3, 2, 1, 0) | Time.zone.local(2022, 3, 2, 1, 0)
+ end
+
+ with_them do
+ it 'calculates the next_run_at using worker_cron' do
+ travel_to(now) do
+ result = run_service
+
+ expect(result).to eq(expected_result)
+ end
+ end
+ end
+ end
+ end
+end
+# rubocop:enable Layout/LineLength
diff --git a/spec/services/ci/pipeline_trigger_service_spec.rb b/spec/services/ci/pipeline_trigger_service_spec.rb
index 4b3e774ff3c..4946367380e 100644
--- a/spec/services/ci/pipeline_trigger_service_spec.rb
+++ b/spec/services/ci/pipeline_trigger_service_spec.rb
@@ -197,8 +197,7 @@ RSpec.describe Ci::PipelineTriggerService do
end
it_behaves_like 'logs downstream pipeline creation' do
- subject { result[:pipeline] }
-
+ let(:downstream_pipeline) { result[:pipeline] }
let(:expected_root_pipeline) { pipeline }
let(:expected_hierarchy_size) { 2 }
let(:expected_downstream_relationship) { :multi_project }
diff --git a/spec/services/ci/pipelines/add_job_service_spec.rb b/spec/services/ci/pipelines/add_job_service_spec.rb
index e735b2752d9..c62aa9506bd 100644
--- a/spec/services/ci/pipelines/add_job_service_spec.rb
+++ b/spec/services/ci/pipelines/add_job_service_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Ci::Pipelines::AddJobService do
include ExclusiveLeaseHelpers
- let_it_be(:pipeline) { create(:ci_pipeline) }
+ let_it_be_with_reload(:pipeline) { create(:ci_pipeline) }
let(:job) { build(:ci_build) }
@@ -35,7 +35,7 @@ RSpec.describe Ci::Pipelines::AddJobService do
end
it 'assigns partition_id to job and metadata' do
- pipeline.partition_id = 123
+ pipeline.partition_id = ci_testing_partition_id
expect { execute }
.to change(job, :partition_id).to(pipeline.partition_id)
diff --git a/spec/services/ci/process_build_service_spec.rb b/spec/services/ci/process_build_service_spec.rb
index 9301098b083..de308bb1a87 100644
--- a/spec/services/ci/process_build_service_spec.rb
+++ b/spec/services/ci/process_build_service_spec.rb
@@ -19,31 +19,25 @@ RSpec.describe Ci::ProcessBuildService, '#execute' do
end
end
- shared_context 'with ci_retry_job_fix disabled' do
- before do
- stub_feature_flags(ci_retry_job_fix: false)
- end
- end
-
context 'for single build' do
let!(:build) { create(:ci_build, *[trait].compact, :created, **conditions, pipeline: pipeline) }
- where(:trait, :conditions, :current_status, :after_status, :retry_after_status, :retry_disabled_after_status) do
- nil | { when: :on_success } | 'success' | 'pending' | 'pending' | 'pending'
- nil | { when: :on_success } | 'skipped' | 'pending' | 'pending' | 'pending'
- nil | { when: :on_success } | 'failed' | 'skipped' | 'skipped' | 'skipped'
- nil | { when: :on_failure } | 'success' | 'skipped' | 'skipped' | 'skipped'
- nil | { when: :on_failure } | 'skipped' | 'skipped' | 'skipped' | 'skipped'
- nil | { when: :on_failure } | 'failed' | 'pending' | 'pending' | 'pending'
- nil | { when: :always } | 'success' | 'pending' | 'pending' | 'pending'
- nil | { when: :always } | 'skipped' | 'pending' | 'pending' | 'pending'
- nil | { when: :always } | 'failed' | 'pending' | 'pending' | 'pending'
- :actionable | { when: :manual } | 'success' | 'manual' | 'pending' | 'manual'
- :actionable | { when: :manual } | 'skipped' | 'manual' | 'pending' | 'manual'
- :actionable | { when: :manual } | 'failed' | 'skipped' | 'skipped' | 'skipped'
- :schedulable | { when: :delayed } | 'success' | 'scheduled' | 'pending' | 'scheduled'
- :schedulable | { when: :delayed } | 'skipped' | 'scheduled' | 'pending' | 'scheduled'
- :schedulable | { when: :delayed } | 'failed' | 'skipped' | 'skipped' | 'skipped'
+ where(:trait, :conditions, :current_status, :after_status, :retry_after_status) do
+ nil | { when: :on_success } | 'success' | 'pending' | 'pending'
+ nil | { when: :on_success } | 'skipped' | 'pending' | 'pending'
+ nil | { when: :on_success } | 'failed' | 'skipped' | 'skipped'
+ nil | { when: :on_failure } | 'success' | 'skipped' | 'skipped'
+ nil | { when: :on_failure } | 'skipped' | 'skipped' | 'skipped'
+ nil | { when: :on_failure } | 'failed' | 'pending' | 'pending'
+ nil | { when: :always } | 'success' | 'pending' | 'pending'
+ nil | { when: :always } | 'skipped' | 'pending' | 'pending'
+ nil | { when: :always } | 'failed' | 'pending' | 'pending'
+ :actionable | { when: :manual } | 'success' | 'manual' | 'pending'
+ :actionable | { when: :manual } | 'skipped' | 'manual' | 'pending'
+ :actionable | { when: :manual } | 'failed' | 'skipped' | 'skipped'
+ :schedulable | { when: :delayed } | 'success' | 'scheduled' | 'pending'
+ :schedulable | { when: :delayed } | 'skipped' | 'scheduled' | 'pending'
+ :schedulable | { when: :delayed } | 'failed' | 'skipped' | 'skipped'
end
with_them do
@@ -57,14 +51,6 @@ RSpec.describe Ci::ProcessBuildService, '#execute' do
it 'updates the job status to retry_after_status' do
expect { subject }.to change { build.status }.to(retry_after_status)
end
-
- context 'when feature flag ci_retry_job_fix is disabled' do
- include_context 'with ci_retry_job_fix disabled'
-
- it "updates the job status to retry_disabled_after_status" do
- expect { subject }.to change { build.status }.to(retry_disabled_after_status)
- end
- end
end
end
end
@@ -84,15 +70,15 @@ RSpec.describe Ci::ProcessBuildService, '#execute' do
let!(:other_build) { create(:ci_build, :created, when: :on_success, pipeline: pipeline) }
- where(:trait, :build_when, :current_status, :after_status, :retry_after_status, :retry_disabled_after_status) do
- nil | :on_success | 'success' | 'pending' | 'pending' | 'pending'
- nil | :on_success | 'skipped' | 'skipped' | 'skipped' | 'skipped'
- nil | :manual | 'success' | 'manual' | 'pending' | 'manual'
- nil | :manual | 'skipped' | 'skipped' | 'skipped' | 'skipped'
- nil | :delayed | 'success' | 'manual' | 'pending' | 'manual'
- nil | :delayed | 'skipped' | 'skipped' | 'skipped' | 'skipped'
- :schedulable | :delayed | 'success' | 'scheduled' | 'pending' | 'scheduled'
- :schedulable | :delayed | 'skipped' | 'skipped' | 'skipped' | 'skipped'
+ where(:trait, :build_when, :current_status, :after_status, :retry_after_status) do
+ nil | :on_success | 'success' | 'pending' | 'pending'
+ nil | :on_success | 'skipped' | 'skipped' | 'skipped'
+ nil | :manual | 'success' | 'manual' | 'pending'
+ nil | :manual | 'skipped' | 'skipped' | 'skipped'
+ nil | :delayed | 'success' | 'manual' | 'pending'
+ nil | :delayed | 'skipped' | 'skipped' | 'skipped'
+ :schedulable | :delayed | 'success' | 'scheduled' | 'pending'
+ :schedulable | :delayed | 'skipped' | 'skipped' | 'skipped'
end
with_them do
@@ -106,14 +92,6 @@ RSpec.describe Ci::ProcessBuildService, '#execute' do
it 'updates the job status to retry_after_status' do
expect { subject }.to change { build.status }.to(retry_after_status)
end
-
- context 'when feature flag ci_retry_job_fix is disabled' do
- include_context 'with ci_retry_job_fix disabled'
-
- it "updates the job status to retry_disabled_after_status" do
- expect { subject }.to change { build.status }.to(retry_disabled_after_status)
- end
- end
end
end
end
diff --git a/spec/services/ci/reset_skipped_jobs_service_spec.rb b/spec/services/ci/reset_skipped_jobs_service_spec.rb
new file mode 100644
index 00000000000..712a21e665b
--- /dev/null
+++ b/spec/services/ci/reset_skipped_jobs_service_spec.rb
@@ -0,0 +1,320 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::ResetSkippedJobsService, :sidekiq_inline, feature_category: :continuous_integration do
+ let_it_be(:project) { create(:project, :empty_repo) }
+ let_it_be(:user) { project.first_owner }
+
+ before_all do
+ project.repository.create_file(user, 'init', 'init', message: 'init', branch_name: 'master')
+ end
+
+ subject(:service) { described_class.new(project, user) }
+
+ context 'with a stage-dag mixed pipeline' do
+ let(:config) do
+ <<-YAML
+ stages: [a, b, c]
+
+ a1:
+ stage: a
+ script: exit $(($RANDOM % 2))
+
+ a2:
+ stage: a
+ script: exit 0
+ needs: [a1]
+
+ a3:
+ stage: a
+ script: exit 0
+ needs: [a2]
+
+ b1:
+ stage: b
+ script: exit 0
+ needs: []
+
+ b2:
+ stage: b
+ script: exit 0
+ needs: [a2]
+
+ c1:
+ stage: c
+ script: exit 0
+ needs: [b2]
+
+ c2:
+ stage: c
+ script: exit 0
+ YAML
+ end
+
+ let(:pipeline) do
+ Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload
+ end
+
+ let(:a1) { find_job('a1') }
+ let(:b1) { find_job('b1') }
+
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'created',
+ a3: 'created',
+ b1: 'pending',
+ b2: 'created',
+ c1: 'created',
+ c2: 'created'
+ )
+
+ b1.success!
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'created',
+ a3: 'created',
+ b1: 'success',
+ b2: 'created',
+ c1: 'created',
+ c2: 'created'
+ )
+
+ a1.drop!
+ check_jobs_statuses(
+ a1: 'failed',
+ a2: 'skipped',
+ a3: 'skipped',
+ b1: 'success',
+ b2: 'skipped',
+ c1: 'skipped',
+ c2: 'skipped'
+ )
+
+ new_a1 = Ci::RetryJobService.new(project, user).clone!(a1)
+ new_a1.enqueue!
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'skipped',
+ a3: 'skipped',
+ b1: 'success',
+ b2: 'skipped',
+ c1: 'skipped',
+ c2: 'skipped'
+ )
+ end
+
+ it 'marks subsequent skipped jobs as processable' do
+ execute_after_requeue_service(a1)
+
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'created',
+ a3: 'created',
+ b1: 'success',
+ b2: 'created',
+ c1: 'created',
+ c2: 'created'
+ )
+ end
+
+ context 'when executed by a different user than the original owner' do
+ let(:retryer) { create(:user).tap { |u| project.add_maintainer(u) } }
+ let(:service) { described_class.new(project, retryer) }
+
+ it 'reassigns jobs with updated statuses to the retryer' do
+ expect(jobs_name_status_owner_needs).to contain_exactly(
+ { 'name' => 'a1', 'status' => 'pending', 'user_id' => user.id, 'needs' => [] },
+ { 'name' => 'a2', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['a1'] },
+ { 'name' => 'a3', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['a2'] },
+ { 'name' => 'b1', 'status' => 'success', 'user_id' => user.id, 'needs' => [] },
+ { 'name' => 'b2', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['a2'] },
+ { 'name' => 'c1', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['b2'] },
+ { 'name' => 'c2', 'status' => 'skipped', 'user_id' => user.id, 'needs' => [] }
+ )
+
+ execute_after_requeue_service(a1)
+
+ expect(jobs_name_status_owner_needs).to contain_exactly(
+ { 'name' => 'a1', 'status' => 'pending', 'user_id' => user.id, 'needs' => [] },
+ { 'name' => 'a2', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['a1'] },
+ { 'name' => 'a3', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['a2'] },
+ { 'name' => 'b1', 'status' => 'success', 'user_id' => user.id, 'needs' => [] },
+ { 'name' => 'b2', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['a2'] },
+ { 'name' => 'c1', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['b2'] },
+ { 'name' => 'c2', 'status' => 'created', 'user_id' => retryer.id, 'needs' => [] }
+ )
+ end
+ end
+ end
+
+ context 'with stage-dag mixed pipeline with some same-stage needs' do
+ let(:config) do
+ <<-YAML
+ stages: [a, b, c]
+
+ a1:
+ stage: a
+ script: exit $(($RANDOM % 2))
+
+ a2:
+ stage: a
+ script: exit 0
+ needs: [a1]
+
+ b1:
+ stage: b
+ script: exit 0
+ needs: [b2]
+
+ b2:
+ stage: b
+ script: exit 0
+
+ c1:
+ stage: c
+ script: exit 0
+ needs: [b2]
+
+ c2:
+ stage: c
+ script: exit 0
+ YAML
+ end
+
+ let(:pipeline) do
+ Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload
+ end
+
+ let(:a1) { find_job('a1') }
+
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'created',
+ b1: 'created',
+ b2: 'created',
+ c1: 'created',
+ c2: 'created'
+ )
+
+ a1.drop!
+ check_jobs_statuses(
+ a1: 'failed',
+ a2: 'skipped',
+ b1: 'skipped',
+ b2: 'skipped',
+ c1: 'skipped',
+ c2: 'skipped'
+ )
+
+ new_a1 = Ci::RetryJobService.new(project, user).clone!(a1)
+ new_a1.enqueue!
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'skipped',
+ b1: 'skipped',
+ b2: 'skipped',
+ c1: 'skipped',
+ c2: 'skipped'
+ )
+ end
+
+ it 'marks subsequent skipped jobs as processable' do
+ execute_after_requeue_service(a1)
+
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'created',
+ b1: 'created',
+ b2: 'created',
+ c1: 'created',
+ c2: 'created'
+ )
+ end
+ end
+
+ context 'with same-stage needs' do
+ let(:config) do
+ <<-YAML
+ a:
+ script: exit $(($RANDOM % 2))
+
+ b:
+ script: exit 0
+ needs: [a]
+
+ c:
+ script: exit 0
+ needs: [b]
+ YAML
+ end
+
+ let(:pipeline) do
+ Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload
+ end
+
+ let(:a) { find_job('a') }
+
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ check_jobs_statuses(
+ a: 'pending',
+ b: 'created',
+ c: 'created'
+ )
+
+ a.drop!
+ check_jobs_statuses(
+ a: 'failed',
+ b: 'skipped',
+ c: 'skipped'
+ )
+
+ new_a = Ci::RetryJobService.new(project, user).clone!(a)
+ new_a.enqueue!
+ check_jobs_statuses(
+ a: 'pending',
+ b: 'skipped',
+ c: 'skipped'
+ )
+ end
+
+ it 'marks subsequent skipped jobs as processable' do
+ execute_after_requeue_service(a)
+
+ check_jobs_statuses(
+ a: 'pending',
+ b: 'created',
+ c: 'created'
+ )
+ end
+ end
+
+ private
+
+ def find_job(name)
+ processables.find_by!(name: name)
+ end
+
+ def check_jobs_statuses(statuses)
+ expect(processables.order(:name).pluck(:name, :status)).to contain_exactly(*statuses.stringify_keys.to_a)
+ end
+
+ def processables
+ pipeline.processables.latest
+ end
+
+ def jobs_name_status_owner_needs
+ processables.reload.map do |job|
+ job.attributes.slice('name', 'status', 'user_id').merge('needs' => job.needs.map(&:name))
+ end
+ end
+
+ def execute_after_requeue_service(processable)
+ service.execute(processable)
+ end
+end
diff --git a/spec/services/ci/retry_job_service_spec.rb b/spec/services/ci/retry_job_service_spec.rb
index 540e700efa6..c3d80f2cb56 100644
--- a/spec/services/ci/retry_job_service_spec.rb
+++ b/spec/services/ci/retry_job_service_spec.rb
@@ -48,12 +48,6 @@ RSpec.describe Ci::RetryJobService do
end
end
- shared_context 'with ci_retry_job_fix disabled' do
- before do
- stub_feature_flags(ci_retry_job_fix: false)
- end
- end
-
shared_examples_for 'clones the job' do
let(:job) { job_to_clone }
@@ -284,14 +278,6 @@ RSpec.describe Ci::RetryJobService do
with_them do
it_behaves_like 'checks enqueue_immediately?'
-
- context 'when feature flag is disabled' do
- include_context 'with ci_retry_job_fix disabled'
-
- it_behaves_like 'checks enqueue_immediately?' do
- let(:enqueue_immediately) { false }
- end
- end
end
end
end
@@ -384,15 +370,6 @@ RSpec.describe Ci::RetryJobService do
expect(subject).to be_success
expect(new_job.status).to eq after_status
end
-
- context 'when feature flag is disabled' do
- include_context 'with ci_retry_job_fix disabled'
-
- it 'enqueues the new job' do
- expect(subject).to be_success
- expect(new_job).to be_pending
- end
- end
end
end
@@ -435,15 +412,6 @@ RSpec.describe Ci::RetryJobService do
expect(subject).to be_success
expect(new_job.status).to eq after_status
end
-
- context 'when feature flag is disabled' do
- include_context 'with ci_retry_job_fix disabled'
-
- it 'enqueues the new job' do
- expect(subject).to be_success
- expect(new_job).to be_pending
- end
- end
end
end
@@ -487,19 +455,6 @@ RSpec.describe Ci::RetryJobService do
end
it_behaves_like 'checks enqueue_immediately?'
-
- context 'when feature flag is disabled' do
- include_context 'with ci_retry_job_fix disabled'
-
- it 'enqueues the new job' do
- expect(subject).to be_success
- expect(new_job).to be_pending
- end
-
- it_behaves_like 'checks enqueue_immediately?' do
- let(:enqueue_immediately) { false }
- end
- end
end
end
end
diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb
index 77345096537..07518c35fab 100644
--- a/spec/services/ci/retry_pipeline_service_spec.rb
+++ b/spec/services/ci/retry_pipeline_service_spec.rb
@@ -349,7 +349,10 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
it 'marks source bridge as pending' do
expect { service.execute(pipeline) }.to change { bridge.reload.status }.to('pending')
- .and not_change { bridge.reload.user }
+ end
+
+ it 'assigns the current user to the source bridge' do
+ expect { service.execute(pipeline) }.to change { bridge.reload.user }.to(user)
end
end
end
diff --git a/spec/services/ci/runners/assign_runner_service_spec.rb b/spec/services/ci/runners/assign_runner_service_spec.rb
index 08bb99830fb..92f6db2bdfb 100644
--- a/spec/services/ci/runners/assign_runner_service_spec.rb
+++ b/spec/services/ci/runners/assign_runner_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Ci::Runners::AssignRunnerService, '#execute' do
+RSpec.describe ::Ci::Runners::AssignRunnerService, '#execute', feature_category: :runner_fleet do
subject(:execute) { described_class.new(runner, project, user).execute }
let_it_be(:runner) { create(:ci_runner, :project, projects: [project]) }
diff --git a/spec/services/ci/runners/bulk_delete_runners_service_spec.rb b/spec/services/ci/runners/bulk_delete_runners_service_spec.rb
index fa8af1100df..5e697565972 100644
--- a/spec/services/ci/runners/bulk_delete_runners_service_spec.rb
+++ b/spec/services/ci/runners/bulk_delete_runners_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Ci::Runners::BulkDeleteRunnersService, '#execute' do
+RSpec.describe ::Ci::Runners::BulkDeleteRunnersService, '#execute', feature_category: :runner_fleet do
subject(:execute) { described_class.new(**service_args).execute }
let_it_be(:admin_user) { create(:user, :admin) }
diff --git a/spec/services/ci/runners/process_runner_version_update_service_spec.rb b/spec/services/ci/runners/process_runner_version_update_service_spec.rb
index b885138fc7a..d2a7e87b2d5 100644
--- a/spec/services/ci/runners/process_runner_version_update_service_spec.rb
+++ b/spec/services/ci/runners/process_runner_version_update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::Runners::ProcessRunnerVersionUpdateService do
+RSpec.describe Ci::Runners::ProcessRunnerVersionUpdateService, feature_category: :runner_fleet do
subject(:service) { described_class.new(version) }
let(:version) { '1.0.0' }
diff --git a/spec/services/ci/runners/reconcile_existing_runner_versions_service_spec.rb b/spec/services/ci/runners/reconcile_existing_runner_versions_service_spec.rb
index 1690190320a..39082b5c0f4 100644
--- a/spec/services/ci/runners/reconcile_existing_runner_versions_service_spec.rb
+++ b/spec/services/ci/runners/reconcile_existing_runner_versions_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Ci::Runners::ReconcileExistingRunnerVersionsService, '#execute' do
+RSpec.describe ::Ci::Runners::ReconcileExistingRunnerVersionsService, '#execute', feature_category: :runner_fleet do
include RunnerReleasesHelper
subject(:execute) { described_class.new.execute }
diff --git a/spec/services/ci/runners/register_runner_service_spec.rb b/spec/services/ci/runners/register_runner_service_spec.rb
index 2d1b109072f..47d399cb19a 100644
--- a/spec/services/ci/runners/register_runner_service_spec.rb
+++ b/spec/services/ci/runners/register_runner_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Ci::Runners::RegisterRunnerService, '#execute' do
+RSpec.describe ::Ci::Runners::RegisterRunnerService, '#execute', feature_category: :runner_fleet do
let(:registration_token) { 'abcdefg123456' }
let(:token) {}
let(:args) { {} }
diff --git a/spec/services/ci/runners/reset_registration_token_service_spec.rb b/spec/services/ci/runners/reset_registration_token_service_spec.rb
index 79059712032..c8115236034 100644
--- a/spec/services/ci/runners/reset_registration_token_service_spec.rb
+++ b/spec/services/ci/runners/reset_registration_token_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Ci::Runners::ResetRegistrationTokenService, '#execute' do
+RSpec.describe ::Ci::Runners::ResetRegistrationTokenService, '#execute', feature_category: :runner_fleet do
subject(:execute) { described_class.new(scope, current_user).execute }
let_it_be(:user) { build(:user) }
diff --git a/spec/services/ci/runners/set_runner_associated_projects_service_spec.rb b/spec/services/ci/runners/set_runner_associated_projects_service_spec.rb
index e5cba80d567..9921f9322bd 100644
--- a/spec/services/ci/runners/set_runner_associated_projects_service_spec.rb
+++ b/spec/services/ci/runners/set_runner_associated_projects_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Ci::Runners::SetRunnerAssociatedProjectsService, '#execute' do
+RSpec.describe ::Ci::Runners::SetRunnerAssociatedProjectsService, '#execute', feature_category: :runner_fleet do
subject(:execute) { described_class.new(runner: runner, current_user: user, project_ids: project_ids).execute }
let_it_be(:owner_project) { create(:project) }
diff --git a/spec/services/ci/runners/unassign_runner_service_spec.rb b/spec/services/ci/runners/unassign_runner_service_spec.rb
index cf710cf6893..e91d4249473 100644
--- a/spec/services/ci/runners/unassign_runner_service_spec.rb
+++ b/spec/services/ci/runners/unassign_runner_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Ci::Runners::UnassignRunnerService, '#execute' do
+RSpec.describe ::Ci::Runners::UnassignRunnerService, '#execute', feature_category: :runner_fleet do
let_it_be(:project) { create(:project) }
let_it_be(:runner) { create(:ci_runner, :project, projects: [project]) }
diff --git a/spec/services/ci/runners/unregister_runner_service_spec.rb b/spec/services/ci/runners/unregister_runner_service_spec.rb
index 77fc299e4e1..fb779e1a673 100644
--- a/spec/services/ci/runners/unregister_runner_service_spec.rb
+++ b/spec/services/ci/runners/unregister_runner_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Ci::Runners::UnregisterRunnerService, '#execute' do
+RSpec.describe ::Ci::Runners::UnregisterRunnerService, '#execute', feature_category: :runner_fleet do
subject(:execute) { described_class.new(runner, 'some_token').execute }
let(:runner) { create(:ci_runner) }
diff --git a/spec/services/ci/runners/update_runner_service_spec.rb b/spec/services/ci/runners/update_runner_service_spec.rb
index 1f953ac4cbb..86875df70a2 100644
--- a/spec/services/ci/runners/update_runner_service_spec.rb
+++ b/spec/services/ci/runners/update_runner_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::Runners::UpdateRunnerService, '#execute' do
+RSpec.describe Ci::Runners::UpdateRunnerService, '#execute', feature_category: :runner_fleet do
subject(:execute) { described_class.new(runner).execute(params) }
let(:runner) { create(:ci_runner) }
diff --git a/spec/services/ci/test_failure_history_service_spec.rb b/spec/services/ci/test_failure_history_service_spec.rb
index c19df6e217b..10f6c6f5007 100644
--- a/spec/services/ci/test_failure_history_service_spec.rb
+++ b/spec/services/ci/test_failure_history_service_spec.rb
@@ -3,16 +3,19 @@
require 'spec_helper'
RSpec.describe Ci::TestFailureHistoryService, :aggregate_failures do
- describe '#execute' do
- let(:project) { create(:project) }
- let(:pipeline) { create(:ci_empty_pipeline, status: :created, project: project) }
+ let_it_be(:project) { create(:project, :repository) }
+
+ let_it_be_with_reload(:pipeline) do
+ create(:ci_pipeline, status: :created, project: project, ref: project.default_branch)
+ end
+ describe '#execute' do
subject(:execute_service) { described_class.new(pipeline).execute }
context 'when pipeline has failed builds with test reports' do
- before do
+ let_it_be(:job) do
# The test report has 2 unit test failures
- create(:ci_build, :failed, :test_reports, pipeline: pipeline, project: project)
+ create(:ci_build, :failed, :test_reports, pipeline: pipeline)
end
it 'creates unit test failures records' do
@@ -22,6 +25,14 @@ RSpec.describe Ci::TestFailureHistoryService, :aggregate_failures do
expect(Ci::UnitTestFailure.count).to eq(2)
end
+ it 'assigns partition_id to Ci::UnitTestFailure' do
+ execute_service
+
+ unit_test_failure_partition_ids = Ci::UnitTestFailure.distinct.pluck(:partition_id)
+
+ expect(unit_test_failure_partition_ids).to match_array([job.partition_id])
+ end
+
context 'when pipeline is not for the default branch' do
before do
pipeline.update_column(:ref, 'new-feature')
@@ -67,7 +78,7 @@ RSpec.describe Ci::TestFailureHistoryService, :aggregate_failures do
# This other test report has 1 unique unit test failure which brings us to 3 total failures across all builds
# thus exceeding the limit of 2 for MAX_TRACKABLE_FAILURES
- create(:ci_build, :failed, :test_reports_with_duplicate_failed_test_names, pipeline: pipeline, project: project)
+ create(:ci_build, :failed, :test_reports_with_duplicate_failed_test_names, pipeline: pipeline)
end
it 'does not persist data' do
@@ -82,7 +93,7 @@ RSpec.describe Ci::TestFailureHistoryService, :aggregate_failures do
context 'when test failure data have duplicates within the same payload (happens when the JUnit report has duplicate unit test names but have different failures)' do
before do
# The test report has 2 unit test failures but with the same unit test keys
- create(:ci_build, :failed, :test_reports_with_duplicate_failed_test_names, pipeline: pipeline, project: project)
+ create(:ci_build, :failed, :test_reports_with_duplicate_failed_test_names, pipeline: pipeline)
end
it 'does not fail but does not persist duplicate data' do
@@ -95,8 +106,8 @@ RSpec.describe Ci::TestFailureHistoryService, :aggregate_failures do
context 'when pipeline has no failed builds with test reports' do
before do
- create(:ci_build, :test_reports, pipeline: pipeline, project: project)
- create(:ci_build, :failed, pipeline: pipeline, project: project)
+ create(:ci_build, :test_reports, pipeline: pipeline)
+ create(:ci_build, :failed, pipeline: pipeline)
end
it 'does not persist data' do
@@ -109,14 +120,10 @@ RSpec.describe Ci::TestFailureHistoryService, :aggregate_failures do
end
describe '#should_track_failures?' do
- let(:project) { create(:project, :repository) }
- let(:pipeline) { create(:ci_empty_pipeline, status: :created, project: project, ref: project.default_branch) }
-
subject { described_class.new(pipeline).should_track_failures? }
- before do
- create(:ci_build, :test_reports, :failed, pipeline: pipeline, project: project)
- create(:ci_build, :test_reports, :failed, pipeline: pipeline, project: project)
+ let_it_be(:jobs) do
+ create_list(:ci_build, 2, :test_reports, :failed, pipeline: pipeline)
end
context 'when feature flag is enabled and pipeline ref is the default branch' do
diff --git a/spec/services/ci/track_failed_build_service_spec.rb b/spec/services/ci/track_failed_build_service_spec.rb
index d83e56f0669..676769d2fc7 100644
--- a/spec/services/ci/track_failed_build_service_spec.rb
+++ b/spec/services/ci/track_failed_build_service_spec.rb
@@ -21,19 +21,22 @@ RSpec.describe Ci::TrackFailedBuildService do
expect(response.success?).to be true
+ context = {
+ schema: described_class::SCHEMA_URL,
+ data: {
+ build_id: build.id,
+ build_name: build.name,
+ build_artifact_types: ["sast"],
+ exit_code: exit_code,
+ failure_reason: failure_reason,
+ project: project.id
+ }
+ }
+
expect_snowplow_event(
category: 'ci::build',
action: 'failed',
- context: [{
- schema: described_class::SCHEMA_URL,
- data: {
- build_id: build.id,
- build_name: build.name,
- build_artifact_types: ["sast"],
- exit_code: exit_code,
- failure_reason: failure_reason
- }
- }],
+ context: [context],
user: user,
project: project.id)
end
diff --git a/spec/services/ci/unlock_artifacts_service_spec.rb b/spec/services/ci/unlock_artifacts_service_spec.rb
index f21afc7fe9e..c15e1cb2b5d 100644
--- a/spec/services/ci/unlock_artifacts_service_spec.rb
+++ b/spec/services/ci/unlock_artifacts_service_spec.rb
@@ -5,11 +5,11 @@ require 'spec_helper'
RSpec.describe Ci::UnlockArtifactsService do
using RSpec::Parameterized::TableSyntax
- where(:tag, :ci_update_unlocked_job_artifacts) do
- false | false
- false | true
- true | false
- true | true
+ where(:tag) do
+ [
+ [false],
+ [true]
+ ]
end
with_them do
@@ -31,7 +31,6 @@ RSpec.describe Ci::UnlockArtifactsService do
before do
stub_const("#{described_class}::BATCH_SIZE", 1)
- stub_feature_flags(ci_update_unlocked_job_artifacts: ci_update_unlocked_job_artifacts)
end
describe '#execute' do
@@ -69,17 +68,11 @@ RSpec.describe Ci::UnlockArtifactsService do
end
it 'unlocks job artifact records' do
- pending unless ci_update_unlocked_job_artifacts
-
expect { execute }.to change { ::Ci::JobArtifact.artifact_unlocked.count }.from(0).to(2)
end
it 'unlocks pipeline artifact records' do
- if ci_update_unlocked_job_artifacts
- expect { execute }.to change { ::Ci::PipelineArtifact.artifact_unlocked.count }.from(0).to(1)
- else
- expect { execute }.not_to change { ::Ci::PipelineArtifact.artifact_unlocked.count }
- end
+ expect { execute }.to change { ::Ci::PipelineArtifact.artifact_unlocked.count }.from(0).to(1)
end
end
@@ -111,17 +104,11 @@ RSpec.describe Ci::UnlockArtifactsService do
end
it 'unlocks job artifact records' do
- pending unless ci_update_unlocked_job_artifacts
-
expect { execute }.to change { ::Ci::JobArtifact.artifact_unlocked.count }.from(0).to(8)
end
it 'unlocks pipeline artifact records' do
- if ci_update_unlocked_job_artifacts
- expect { execute }.to change { ::Ci::PipelineArtifact.artifact_unlocked.count }.from(0).to(1)
- else
- expect { execute }.not_to change { ::Ci::PipelineArtifact.artifact_unlocked.count }
- end
+ expect { execute }.to change { ::Ci::PipelineArtifact.artifact_unlocked.count }.from(0).to(1)
end
end
end
diff --git a/spec/services/clusters/agents/filter_authorizations_service_spec.rb b/spec/services/clusters/agents/filter_authorizations_service_spec.rb
new file mode 100644
index 00000000000..62cff405d0c
--- /dev/null
+++ b/spec/services/clusters/agents/filter_authorizations_service_spec.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Clusters::Agents::FilterAuthorizationsService, feature_category: :continuous_integration do
+ describe '#execute' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+
+ let(:agent_authorizations_without_env) do
+ [
+ build(:agent_project_authorization, project: project, agent: build(:cluster_agent, project: project)),
+ build(:agent_group_authorization, group: group, agent: build(:cluster_agent, project: project)),
+ ::Clusters::Agents::ImplicitAuthorization.new(agent: build(:cluster_agent, project: project))
+ ]
+ end
+
+ let(:filter_params) { {} }
+
+ subject(:execute_filter) { described_class.new(agent_authorizations, filter_params).execute }
+
+ context 'when there are no filters' do
+ let(:agent_authorizations) { agent_authorizations_without_env }
+
+ it 'returns the authorizations as is' do
+ expect(execute_filter).to eq agent_authorizations
+ end
+ end
+
+ context 'when filtering by environment' do
+ let(:agent_authorizations_with_env) do
+ [
+ build(
+ :agent_project_authorization,
+ project: project,
+ agent: build(:cluster_agent, project: project),
+ environments: ['staging', 'review/*', 'production']
+ ),
+ build(
+ :agent_group_authorization,
+ group: group,
+ agent: build(:cluster_agent, project: project),
+ environments: ['staging', 'review/*', 'production']
+ )
+ ]
+ end
+
+ let(:agent_authorizations_with_different_env) do
+ [
+ build(
+ :agent_project_authorization,
+ project: project,
+ agent: build(:cluster_agent, project: project),
+ environments: ['staging']
+ ),
+ build(
+ :agent_group_authorization,
+ group: group,
+ agent: build(:cluster_agent, project: project),
+ environments: ['staging']
+ )
+ ]
+ end
+
+ let(:agent_authorizations) do
+ (
+ agent_authorizations_without_env +
+ agent_authorizations_with_env +
+ agent_authorizations_with_different_env
+ )
+ end
+
+ let(:filter_params) { { environment: 'production' } }
+
+ it 'returns the authorizations with the given environment AND authorizations without any environment' do
+ expected_authorizations = agent_authorizations_with_env + agent_authorizations_without_env
+
+ expect(execute_filter).to match_array expected_authorizations
+ end
+
+ context 'when environment filter has a wildcard' do
+ let(:filter_params) { { environment: 'review/123' } }
+
+ it 'returns the authorizations with matching environments AND authorizations without any environment' do
+ expected_authorizations = agent_authorizations_with_env + agent_authorizations_without_env
+
+ expect(execute_filter).to match_array expected_authorizations
+ end
+ end
+
+ context 'when environment filter is nil' do
+ let(:filter_params) { { environment: nil } }
+
+ it 'returns the authorizations without any environment' do
+ expect(execute_filter).to match_array agent_authorizations_without_env
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/clusters/agents/refresh_authorization_service_spec.rb b/spec/services/clusters/agents/refresh_authorization_service_spec.rb
index 09bec7ae0e8..fa38bc202e7 100644
--- a/spec/services/clusters/agents/refresh_authorization_service_spec.rb
+++ b/spec/services/clusters/agents/refresh_authorization_service_spec.rb
@@ -113,6 +113,16 @@ RSpec.describe Clusters::Agents::RefreshAuthorizationService do
expect(modified_authorization.config).to eq({ 'default_namespace' => 'new-namespace' })
end
+ context 'project does not belong to a group, and is in the same namespace as the agent' do
+ let(:root_ancestor) { create(:namespace) }
+ let(:added_project) { create(:project, namespace: root_ancestor) }
+
+ it 'creates an authorization record for the project' do
+ expect(subject).to be_truthy
+ expect(agent.authorized_projects).to contain_exactly(added_project)
+ end
+ end
+
context 'project does not belong to a group, and is authorizing itself' do
let(:root_ancestor) { create(:namespace) }
let(:added_project) { project }
diff --git a/spec/services/clusters/applications/install_service_spec.rb b/spec/services/clusters/applications/install_service_spec.rb
deleted file mode 100644
index d34b4dd943c..00000000000
--- a/spec/services/clusters/applications/install_service_spec.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Clusters::Applications::InstallService do
- describe '#execute' do
- let(:application) { create(:clusters_applications_helm, :scheduled) }
- let!(:install_command) { application.install_command }
- let(:service) { described_class.new(application) }
- let(:helm_client) { instance_double(Gitlab::Kubernetes::Helm::API) }
-
- before do
- allow(service).to receive(:install_command).and_return(install_command)
- allow(service).to receive(:helm_api).and_return(helm_client)
- end
-
- context 'when there are no errors' do
- before do
- expect(helm_client).to receive(:install).with(install_command)
- allow(ClusterWaitForAppInstallationWorker).to receive(:perform_in).and_return(nil)
- end
-
- it 'make the application installing' do
- expect(application.cluster).not_to be_nil
- service.execute
-
- expect(application).to be_installing
- end
-
- it 'schedule async installation status check' do
- expect(ClusterWaitForAppInstallationWorker).to receive(:perform_in).once
-
- service.execute
- end
- end
-
- context 'when k8s cluster communication fails' do
- let(:error) { Kubeclient::HttpError.new(500, 'system failure', nil) }
-
- before do
- expect(helm_client).to receive(:install).with(install_command).and_raise(error)
- end
-
- include_examples 'logs kubernetes errors' do
- let(:error_name) { 'Kubeclient::HttpError' }
- let(:error_message) { 'system failure' }
- let(:error_code) { 500 }
- end
-
- it 'make the application errored' do
- service.execute
-
- expect(application).to be_errored
- expect(application.status_reason).to match('Kubernetes error: 500')
- end
- end
-
- context 'a non kubernetes error happens' do
- let(:application) { create(:clusters_applications_helm, :scheduled) }
- let(:error) { StandardError.new('something bad happened') }
-
- before do
- expect(helm_client).to receive(:install).with(install_command).and_raise(error)
- end
-
- include_examples 'logs kubernetes errors' do
- let(:error_name) { 'StandardError' }
- let(:error_message) { 'something bad happened' }
- let(:error_code) { nil }
- end
-
- it 'make the application errored' do
- service.execute
-
- expect(application).to be_errored
- expect(application.status_reason).to eq('Failed to install.')
- end
- end
- end
-end
diff --git a/spec/services/clusters/applications/prometheus_config_service_spec.rb b/spec/services/clusters/applications/prometheus_config_service_spec.rb
deleted file mode 100644
index 7399f250248..00000000000
--- a/spec/services/clusters/applications/prometheus_config_service_spec.rb
+++ /dev/null
@@ -1,162 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Clusters::Applications::PrometheusConfigService do
- include Gitlab::Routing.url_helpers
-
- let_it_be(:project) { create(:project) }
- let_it_be(:production) { create(:environment, project: project) }
- let_it_be(:cluster) { create(:cluster, :provided_by_user, projects: [project]) }
-
- let(:application) do
- create(:clusters_applications_prometheus, :installed, cluster: cluster)
- end
-
- subject { described_class.new(project, cluster, application).execute(input) }
-
- describe '#execute' do
- let(:input) do
- YAML.load_file(Rails.root.join('vendor/prometheus/values.yaml'))
- end
-
- context 'with alerts' do
- let!(:alert) do
- create(:prometheus_alert, project: project, environment: production)
- end
-
- it 'enables alertmanager' do
- expect(subject.dig('alertmanager', 'enabled')).to eq(true)
- end
-
- describe 'alertmanagerFiles' do
- let(:alertmanager) do
- subject.dig('alertmanagerFiles', 'alertmanager.yml')
- end
-
- it 'contains receivers and route' do
- expect(alertmanager.keys).to contain_exactly('receivers', 'route')
- end
-
- describe 'receivers' do
- let(:receiver) { alertmanager.dig('receivers', 0) }
- let(:webhook_config) { receiver.dig('webhook_configs', 0) }
-
- let(:notify_url) do
- notify_project_prometheus_alerts_url(project, format: :json)
- end
-
- it 'sets receiver' do
- expect(receiver['name']).to eq('gitlab')
- end
-
- it 'sets webhook_config' do
- expect(webhook_config).to eq(
- 'url' => notify_url,
- 'send_resolved' => true,
- 'http_config' => {
- 'bearer_token' => application.alert_manager_token
- }
- )
- end
- end
-
- describe 'route' do
- let(:route) { alertmanager.fetch('route') }
-
- it 'sets route' do
- expect(route).to eq(
- 'receiver' => 'gitlab',
- 'group_wait' => '30s',
- 'group_interval' => '5m',
- 'repeat_interval' => '4h'
- )
- end
- end
- end
-
- describe 'serverFiles' do
- let(:groups) { subject.dig('serverFiles', 'alerts', 'groups') }
-
- it 'sets the alerts' do
- rules = groups.dig(0, 'rules')
- expect(rules.size).to eq(1)
-
- expect(rules.first['alert']).to eq(alert.title)
- end
-
- context 'with parameterized queries' do
- let!(:alert) do
- create(:prometheus_alert,
- project: project,
- environment: production,
- prometheus_metric: metric,
- operator: PrometheusAlert.operators['gt'],
- threshold: 0)
- end
-
- let(:metric) do
- create(:prometheus_metric, query: query, project: project)
- end
-
- let(:query) { 'up{environment="{{ci_environment_slug}}"}' }
-
- it 'substitutes query variables' do
- expect(Gitlab::Prometheus::QueryVariables)
- .to receive(:call)
- .with(production, start_time: nil, end_time: nil)
- .and_call_original
-
- expr = groups.dig(0, 'rules', 0, 'expr')
- expect(expr).to eq("up{environment=\"#{production.slug}\"} > 0.0")
- end
- end
-
- context 'with multiple environments' do
- let(:staging) { create(:environment, project: project) }
-
- before do
- create(:prometheus_alert, project: project, environment: production)
- create(:prometheus_alert, project: project, environment: staging)
- end
-
- it 'sets alerts for multiple environment' do
- env_names = groups.map { |group| group['name'] }
- expect(env_names).to contain_exactly(
- "#{production.name}.rules",
- "#{staging.name}.rules"
- )
- end
-
- it 'substitutes query variables once per environment' do
- allow(Gitlab::Prometheus::QueryVariables).to receive(:call).and_call_original
-
- expect(Gitlab::Prometheus::QueryVariables)
- .to receive(:call)
- .with(production, start_time: nil, end_time: nil)
-
- expect(Gitlab::Prometheus::QueryVariables)
- .to receive(:call)
- .with(staging, start_time: nil, end_time: nil)
-
- subject
- end
- end
- end
- end
-
- context 'without alerts' do
- it 'disables alertmanager' do
- expect(subject.dig('alertmanager', 'enabled')).to eq(false)
- end
-
- it 'removes alertmanagerFiles' do
- expect(subject).not_to include('alertmanagerFiles')
- end
-
- it 'removes alerts' do
- expect(subject.dig('serverFiles', 'alerts')).to eq({})
- end
- end
- end
-end
diff --git a/spec/services/clusters/applications/upgrade_service_spec.rb b/spec/services/clusters/applications/upgrade_service_spec.rb
deleted file mode 100644
index 22fbb7ca6e3..00000000000
--- a/spec/services/clusters/applications/upgrade_service_spec.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Clusters::Applications::UpgradeService do
- describe '#execute' do
- let(:application) { create(:clusters_applications_helm, :scheduled) }
- let!(:install_command) { application.install_command }
- let(:service) { described_class.new(application) }
- let(:helm_client) { instance_double(Gitlab::Kubernetes::Helm::API) }
-
- before do
- allow(service).to receive(:install_command).and_return(install_command)
- allow(service).to receive(:helm_api).and_return(helm_client)
- end
-
- context 'when there are no errors' do
- before do
- expect(helm_client).to receive(:update).with(install_command)
- allow(ClusterWaitForAppInstallationWorker).to receive(:perform_in).and_return(nil)
- end
-
- it 'make the application updating' do
- expect(application.cluster).not_to be_nil
- service.execute
-
- expect(application).to be_updating
- end
-
- it 'schedule async installation status check' do
- expect(ClusterWaitForAppInstallationWorker).to receive(:perform_in).once
-
- service.execute
- end
- end
-
- context 'when kubernetes cluster communication fails' do
- let(:error) { Kubeclient::HttpError.new(500, 'system failure', nil) }
-
- before do
- expect(helm_client).to receive(:update).with(install_command).and_raise(error)
- end
-
- include_examples 'logs kubernetes errors' do
- let(:error_name) { 'Kubeclient::HttpError' }
- let(:error_message) { 'system failure' }
- let(:error_code) { 500 }
- end
-
- it 'make the application errored' do
- service.execute
-
- expect(application).to be_update_errored
- expect(application.status_reason).to eq(_('Kubernetes error: %{error_code}') % { error_code: 500 })
- end
- end
-
- context 'a non kubernetes error happens' do
- let(:application) { create(:clusters_applications_helm, :scheduled) }
- let(:error) { StandardError.new('something bad happened') }
-
- before do
- expect(helm_client).to receive(:update).with(install_command).and_raise(error)
- end
-
- include_examples 'logs kubernetes errors' do
- let(:error_name) { 'StandardError' }
- let(:error_message) { 'something bad happened' }
- let(:error_code) { nil }
- end
-
- it 'make the application errored' do
- service.execute
-
- expect(application).to be_update_errored
- expect(application.status_reason).to eq(_('Failed to upgrade.'))
- end
- end
- end
-end
diff --git a/spec/services/database/consistency_check_service_spec.rb b/spec/services/database/consistency_check_service_spec.rb
index 6695e4b5e9f..d7dee50f7c2 100644
--- a/spec/services/database/consistency_check_service_spec.rb
+++ b/spec/services/database/consistency_check_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Database::ConsistencyCheckService do
+RSpec.describe Database::ConsistencyCheckService, feature_category: :database do
let(:batch_size) { 5 }
let(:max_batches) { 2 }
diff --git a/spec/services/deployments/update_environment_service_spec.rb b/spec/services/deployments/update_environment_service_spec.rb
index c952bcddd9a..31a3abda8c7 100644
--- a/spec/services/deployments/update_environment_service_spec.rb
+++ b/spec/services/deployments/update_environment_service_spec.rb
@@ -115,7 +115,7 @@ RSpec.describe Deployments::UpdateEnvironmentService do
let(:external_url) { 'javascript:alert("hello")' }
it 'fails to update the tier due to validation error' do
- expect { subject.execute }.not_to change { environment.tier }
+ expect { subject.execute }.not_to change { environment.reload.tier }
end
it 'tracks an exception' do
diff --git a/spec/services/environments/create_for_build_service_spec.rb b/spec/services/environments/create_for_build_service_spec.rb
index 721822f355b..c7aadb20c01 100644
--- a/spec/services/environments/create_for_build_service_spec.rb
+++ b/spec/services/environments/create_for_build_service_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe Environments::CreateForBuildService do
let(:merge_request) {}
describe '#execute' do
- subject { service.execute(job, merge_request: merge_request) }
+ subject { service.execute(job) }
shared_examples_for 'returning a correct environment' do
let(:expected_auto_stop_in_seconds) do
@@ -187,10 +187,11 @@ RSpec.describe Environments::CreateForBuildService do
end
context 'when merge_request is provided' do
+ let(:pipeline) { create(:ci_pipeline, project: project, merge_request: merge_request) }
let(:environment_name) { 'development' }
let(:attributes) { { environment: environment_name, options: { environment: { name: environment_name } } } }
let(:merge_request) { create(:merge_request, source_project: project) }
- let(:seed) { described_class.new(job, merge_request: merge_request) }
+ let(:seed) { described_class.new(job) }
context 'and environment does not exist' do
let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' }
@@ -216,7 +217,8 @@ RSpec.describe Environments::CreateForBuildService do
end
context 'when a pipeline contains a deployment job' do
- let!(:job) { build(:ci_build, :start_review_app, project: project) }
+ let(:pipeline) { create(:ci_pipeline, project: project, merge_request: merge_request) }
+ let!(:job) { build(:ci_build, :start_review_app, project: project, pipeline: pipeline) }
context 'and the environment does not exist' do
it 'creates the environment specified by the job' do
diff --git a/spec/services/environments/stop_service_spec.rb b/spec/services/environments/stop_service_spec.rb
index 4f766b73710..5f983a2151a 100644
--- a/spec/services/environments/stop_service_spec.rb
+++ b/spec/services/environments/stop_service_spec.rb
@@ -204,6 +204,8 @@ RSpec.describe Environments::StopService do
context 'and merge request has associated created_environments' do
let!(:environment1) { create(:environment, project: project, merge_request: merge_request) }
let!(:environment2) { create(:environment, project: project, merge_request: merge_request) }
+ let!(:environment3) { create(:environment, project: project) }
+ let!(:environment3_deployment) { create(:deployment, environment: environment3, sha: pipeline.sha) }
before do
subject
@@ -215,8 +217,7 @@ RSpec.describe Environments::StopService do
end
it 'does not affect environments that are not associated to the merge request' do
- expect(pipeline.environments_in_self_and_project_descendants.first.merge_request).to be_nil
- expect(pipeline.environments_in_self_and_project_descendants.first).to be_available
+ expect(environment3.reload).to be_available
end
end
diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb
index c3ae062a4b2..e60954a19ed 100644
--- a/spec/services/event_create_service_spec.rb
+++ b/spec/services/event_create_service_spec.rb
@@ -72,12 +72,13 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:category) { described_class.name }
let(:action) { 'created' }
- let(:label) { 'usage_activity_by_stage_monthly.create.merge_requests_users' }
+ let(:label) { described_class::MR_EVENT_LABEL }
let(:namespace) { project.namespace }
let(:project) { merge_request.project }
let(:user) { merge_request.author }
+ let(:property) { described_class::MR_EVENT_PROPERTY }
let(:context) do
- [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: 'merge_requests_users').to_context]
+ [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: property).to_context]
end
end
end
@@ -101,12 +102,13 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:category) { described_class.name }
let(:action) { 'closed' }
- let(:label) { 'usage_activity_by_stage_monthly.create.merge_requests_users' }
+ let(:label) { described_class::MR_EVENT_LABEL }
let(:namespace) { project.namespace }
let(:project) { merge_request.project }
let(:user) { merge_request.author }
+ let(:property) { described_class::MR_EVENT_PROPERTY }
let(:context) do
- [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: 'merge_requests_users').to_context]
+ [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: property).to_context]
end
end
end
@@ -130,12 +132,13 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:category) { described_class.name }
let(:action) { 'merged' }
- let(:label) { 'usage_activity_by_stage_monthly.create.merge_requests_users' }
+ let(:label) { described_class::MR_EVENT_LABEL }
let(:namespace) { project.namespace }
let(:project) { merge_request.project }
let(:user) { merge_request.author }
+ let(:property) { described_class::MR_EVENT_PROPERTY }
let(:context) do
- [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: 'merge_requests_users').to_context]
+ [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: property).to_context]
end
end
end
@@ -318,10 +321,7 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
let(:namespace) { project.namespace }
let(:feature_flag_name) { :route_hll_to_snowplow }
let(:label) { 'usage_activity_by_stage_monthly.create.action_monthly_active_users_project_repo' }
- let(:context) do
- [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll,
- event: 'action_active_users_project_repo').to_context]
- end
+ let(:property) { 'project_action' }
end
end
@@ -348,10 +348,7 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
let(:namespace) { project.namespace }
let(:feature_flag_name) { :route_hll_to_snowplow }
let(:label) { 'usage_activity_by_stage_monthly.create.action_monthly_active_users_project_repo' }
- let(:context) do
- [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll,
- event: 'action_active_users_project_repo').to_context]
- end
+ let(:property) { 'project_action' }
end
end
@@ -408,41 +405,28 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION }
end
- it 'records correct create payload with Snowplow event' do
- service.save_designs(author, create: [design])
-
- expect_snowplow_event(
- category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
- action: 'create',
- namespace: design.project.namespace,
- user: author,
- project: design.project,
- label: 'design_users'
- )
- end
-
- it 'records correct update payload with Snowplow event' do
- service.save_designs(author, update: [design])
+ describe 'Snowplow tracking' do
+ let(:project) { design.project }
+ let(:namespace) { project.namespace }
+ let(:category) { described_class.name }
+ let(:property) { Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s }
+ let(:label) { ::EventCreateService::DEGIGN_EVENT_LABEL }
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
- expect_snowplow_event(
- category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
- action: 'update',
- namespace: design.project.namespace,
- user: author,
- project: design.project,
- label: 'design_users'
- )
- end
+ context 'for create event' do
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ subject(:design_service) { service.save_designs(author, create: [design]) }
- context 'when FF is disabled' do
- before do
- stub_feature_flags(route_hll_to_snowplow_phase2: false)
+ let(:action) { 'create' }
+ end
end
- it 'doesnt emit snowwplow events', :snowplow do
- subject
+ context 'for update event' do
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ subject(:design_service) { service.save_designs(author, update: [design]) }
- expect_no_snowplow_event
+ let(:action) { 'update' }
+ end
end
end
end
@@ -469,29 +453,17 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION }
end
- it 'records correct payload with Snowplow event' do
- service.destroy_designs([design], author)
-
- expect_snowplow_event(
- category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
- action: 'destroy',
- namespace: design.project.namespace,
- user: author,
- project: design.project,
- label: 'design_users'
- )
- end
-
- context 'when FF is disabled' do
- before do
- stub_feature_flags(route_hll_to_snowplow_phase2: false)
- end
-
- it 'doesnt emit snowplow events', :snowplow do
- subject
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ subject(:design_service) { service.destroy_designs([design], author) }
- expect_no_snowplow_event
- end
+ let(:project) { design.project }
+ let(:namespace) { project.namespace }
+ let(:category) { described_class.name }
+ let(:action) { 'destroy' }
+ let(:user) { author }
+ let(:property) { Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s }
+ let(:label) { ::EventCreateService::DEGIGN_EVENT_LABEL }
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
end
end
end
@@ -519,12 +491,13 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
let(:note) { create(:diff_note_on_merge_request) }
let(:category) { described_class.name }
let(:action) { 'commented' }
- let(:label) { 'usage_activity_by_stage_monthly.create.merge_requests_users' }
+ let(:property) { described_class::MR_EVENT_PROPERTY }
+ let(:label) { described_class::MR_EVENT_LABEL }
let(:namespace) { project.namespace }
let(:project) { note.project }
let(:user) { author }
let(:context) do
- [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: 'merge_requests_users').to_context]
+ [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: property).to_context]
end
end
end
diff --git a/spec/services/feature_flags/hook_service_spec.rb b/spec/services/feature_flags/hook_service_spec.rb
index 19c935e43f3..f3edaca52a9 100644
--- a/spec/services/feature_flags/hook_service_spec.rb
+++ b/spec/services/feature_flags/hook_service_spec.rb
@@ -14,14 +14,14 @@ RSpec.describe FeatureFlags::HookService do
subject(:service) { described_class.new(feature_flag, user) }
- describe 'HOOK_NAME' do
- specify { expect(described_class::HOOK_NAME).to eq(:feature_flag_hooks) }
- end
-
before do
allow(Gitlab::DataBuilder::FeatureFlag).to receive(:build).with(feature_flag, user).once.and_return(hook_data)
end
+ describe 'HOOK_NAME' do
+ specify { expect(described_class::HOOK_NAME).to eq(:feature_flag_hooks) }
+ end
+
it 'calls feature_flag.project.execute_hooks' do
expect(feature_flag.project).to receive(:execute_hooks).with(hook_data, described_class::HOOK_NAME)
diff --git a/spec/services/google_cloud/fetch_google_ip_list_service_spec.rb b/spec/services/google_cloud/fetch_google_ip_list_service_spec.rb
index b83037f80cd..ef77958fa60 100644
--- a/spec/services/google_cloud/fetch_google_ip_list_service_spec.rb
+++ b/spec/services/google_cloud/fetch_google_ip_list_service_spec.rb
@@ -2,8 +2,8 @@
require 'spec_helper'
-RSpec.describe GoogleCloud::FetchGoogleIpListService,
- :use_clean_rails_memory_store_caching, :clean_gitlab_redis_rate_limiting do
+RSpec.describe GoogleCloud::FetchGoogleIpListService, :use_clean_rails_memory_store_caching,
+:clean_gitlab_redis_rate_limiting, feature_category: :continuous_integration do
include StubRequests
let(:google_cloud_ips) { File.read(Rails.root.join('spec/fixtures/cdn/google_cloud.json')) }
diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb
index f2dbb69f855..2791203f395 100644
--- a/spec/services/groups/destroy_service_spec.rb
+++ b/spec/services/groups/destroy_service_spec.rb
@@ -8,8 +8,8 @@ RSpec.describe Groups::DestroyService do
let!(:nested_group) { create(:group, parent: group) }
let!(:project) { create(:project, :repository, :legacy_storage, namespace: group) }
let!(:notification_setting) { create(:notification_setting, source: group) }
- let(:gitlab_shell) { Gitlab::Shell.new }
let(:remove_path) { group.path + "+#{group.id}+deleted" }
+ let(:removed_repo) { Gitlab::Git::Repository.new(project.repository_storage, remove_path, nil, nil) }
before do
group.add_member(user, Gitlab::Access::OWNER)
@@ -70,8 +70,11 @@ RSpec.describe Groups::DestroyService do
end
it 'verifies that paths have been deleted' do
- expect(TestEnv.storage_dir_exists?(project.repository_storage, group.path)).to be_falsey
- expect(TestEnv.storage_dir_exists?(project.repository_storage, remove_path)).to be_falsey
+ Gitlab::GitalyClient::NamespaceService.allow do
+ expect(Gitlab::GitalyClient::NamespaceService.new(project.repository_storage)
+ .exists?(group.path)).to be_falsey
+ end
+ expect(removed_repo).not_to exist
end
end
end
@@ -98,8 +101,11 @@ RSpec.describe Groups::DestroyService do
end
it 'verifies original paths and projects still exist' do
- expect(TestEnv.storage_dir_exists?(project.repository_storage, group.path)).to be_truthy
- expect(TestEnv.storage_dir_exists?(project.repository_storage, remove_path)).to be_falsey
+ Gitlab::GitalyClient::NamespaceService.allow do
+ expect(Gitlab::GitalyClient::NamespaceService.new(project.repository_storage)
+ .exists?(group.path)).to be_truthy
+ end
+ expect(removed_repo).not_to exist
expect(Project.unscoped.count).to eq(1)
expect(Group.unscoped.count).to eq(2)
end
@@ -150,7 +156,7 @@ RSpec.describe Groups::DestroyService do
let!(:project) { create(:project, :legacy_storage, :empty_repo, namespace: group) }
it 'removes repository' do
- expect(gitlab_shell.repository_exists?(project.repository_storage, "#{project.disk_path}.git")).to be_falsey
+ expect(project.repository.raw).not_to exist
end
end
@@ -158,7 +164,7 @@ RSpec.describe Groups::DestroyService do
let!(:project) { create(:project, :empty_repo, namespace: group) }
it 'removes repository' do
- expect(gitlab_shell.repository_exists?(project.repository_storage, "#{project.disk_path}.git")).to be_falsey
+ expect(project.repository.raw).not_to exist
end
end
end
diff --git a/spec/services/groups/import_export/import_service_spec.rb b/spec/services/groups/import_export/import_service_spec.rb
index 66b50704939..d41acbcc2de 100644
--- a/spec/services/groups/import_export/import_service_spec.rb
+++ b/spec/services/groups/import_export/import_service_spec.rb
@@ -148,6 +148,14 @@ RSpec.describe Groups::ImportExport::ImportService do
action: 'create',
label: 'import_group_from_file'
)
+
+ expect_snowplow_event(
+ category: 'Groups::ImportExport::ImportService',
+ action: 'create',
+ label: 'import_access_level',
+ user: user,
+ extra: { user_role: 'Owner', import_type: 'import_group_from_file' }
+ )
end
it 'removes import file' do
@@ -235,6 +243,14 @@ RSpec.describe Groups::ImportExport::ImportService do
)
service.execute
+
+ expect_snowplow_event(
+ category: 'Groups::ImportExport::ImportService',
+ action: 'create',
+ label: 'import_access_level',
+ user: user,
+ extra: { user_role: 'Owner', import_type: 'import_group_from_file' }
+ )
end
end
end
@@ -275,6 +291,14 @@ RSpec.describe Groups::ImportExport::ImportService do
action: 'create',
label: 'import_group_from_file'
)
+
+ expect_snowplow_event(
+ category: 'Groups::ImportExport::ImportService',
+ action: 'create',
+ label: 'import_access_level',
+ user: user,
+ extra: { user_role: 'Owner', import_type: 'import_group_from_file' }
+ )
end
it 'removes import file' do
@@ -352,6 +376,24 @@ RSpec.describe Groups::ImportExport::ImportService do
expect(service.execute).to be_truthy
end
+ it 'tracks the event' do
+ service.execute
+
+ expect_snowplow_event(
+ category: 'Groups::ImportExport::ImportService',
+ action: 'create',
+ label: 'import_group_from_file'
+ )
+
+ expect_snowplow_event(
+ category: 'Groups::ImportExport::ImportService',
+ action: 'create',
+ label: 'import_access_level',
+ user: user,
+ extra: { user_role: 'Owner', import_type: 'import_group_from_file' }
+ )
+ end
+
it 'logs the import success' do
allow(Gitlab::Import::Logger).to receive(:build).and_return(import_logger)
diff --git a/spec/services/import/bitbucket_server_service_spec.rb b/spec/services/import/bitbucket_server_service_spec.rb
index 0b9fe10e95a..555812ca9cf 100644
--- a/spec/services/import/bitbucket_server_service_spec.rb
+++ b/spec/services/import/bitbucket_server_service_spec.rb
@@ -31,6 +31,25 @@ RSpec.describe Import::BitbucketServerService do
allow(subject).to receive(:authorized?).and_return(true)
end
+ context 'execute' do
+ before do
+ allow(subject).to receive(:authorized?).and_return(true)
+ allow(client).to receive(:repo).with(project_key, repo_slug).and_return(double(repo))
+ end
+
+ it 'tracks an access level event' do
+ subject.execute(credentials)
+
+ expect_snowplow_event(
+ category: 'Import::BitbucketServerService',
+ action: 'create',
+ label: 'import_access_level',
+ user: user,
+ extra: { import_type: 'bitbucket', user_role: 'Owner' }
+ )
+ end
+ end
+
context 'when no repo is found' do
before do
allow(subject).to receive(:authorized?).and_return(true)
diff --git a/spec/services/import/github/gists_import_service_spec.rb b/spec/services/import/github/gists_import_service_spec.rb
new file mode 100644
index 00000000000..c5d73e6479d
--- /dev/null
+++ b/spec/services/import/github/gists_import_service_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Import::Github::GistsImportService, feature_category: :importer do
+ subject(:import) { described_class.new(user, params) }
+
+ let_it_be(:user) { create(:user) }
+ let(:params) { { github_access_token: 'token' } }
+ let(:import_status) { instance_double('Gitlab::GithubGistsImport::Status') }
+
+ describe '#execute', :aggregate_failures do
+ before do
+ allow(Gitlab::GithubGistsImport::Status).to receive(:new).and_return(import_status)
+ end
+
+ context 'when import in progress' do
+ let(:expected_result) do
+ {
+ status: :error,
+ http_status: 422,
+ message: 'Import already in progress'
+ }
+ end
+
+ it 'returns error' do
+ expect(import_status).to receive(:started?).and_return(true)
+ expect(import.execute).to eq(expected_result)
+ end
+ end
+
+ context 'when import was not started' do
+ it 'returns success' do
+ encrypted_token = Gitlab::CryptoHelper.aes256_gcm_encrypt(params[:github_access_token])
+ expect(import_status).to receive(:started?).and_return(false)
+ expect(Gitlab::CryptoHelper)
+ .to receive(:aes256_gcm_encrypt).with(params[:github_access_token])
+ .and_return(encrypted_token)
+ expect(Gitlab::GithubGistsImport::StartImportWorker)
+ .to receive(:perform_async).with(user.id, encrypted_token)
+ expect(import_status).to receive(:start!)
+
+ expect(import.execute).to eq({ status: :success })
+ end
+ end
+ end
+end
diff --git a/spec/services/import/github_service_spec.rb b/spec/services/import/github_service_spec.rb
index 38d84009f08..d1b372c5e87 100644
--- a/spec/services/import/github_service_spec.rb
+++ b/spec/services/import/github_service_spec.rb
@@ -82,9 +82,16 @@ RSpec.describe Import::GithubService do
end
context 'when there is no repository size limit defined' do
- it 'skips the check and succeeds' do
+ it 'skips the check, succeeds, and tracks an access level' do
expect(subject.execute(access_params, :github)).to include(status: :success)
expect(settings).to have_received(:write).with(nil)
+ expect_snowplow_event(
+ category: 'Import::GithubService',
+ action: 'create',
+ label: 'import_access_level',
+ user: user,
+ extra: { import_type: 'github', user_role: 'Owner' }
+ )
end
end
@@ -98,6 +105,13 @@ RSpec.describe Import::GithubService do
it 'succeeds when the repository is smaller than the limit' do
expect(subject.execute(access_params, :github)).to include(status: :success)
expect(settings).to have_received(:write).with(nil)
+ expect_snowplow_event(
+ category: 'Import::GithubService',
+ action: 'create',
+ label: 'import_access_level',
+ user: user,
+ extra: { import_type: 'github', user_role: 'Not a member' }
+ )
end
it 'returns error when the repository is larger than the limit' do
@@ -118,6 +132,13 @@ RSpec.describe Import::GithubService do
it 'succeeds when the repository is smaller than the limit' do
expect(subject.execute(access_params, :github)).to include(status: :success)
expect(settings).to have_received(:write).with(nil)
+ expect_snowplow_event(
+ category: 'Import::GithubService',
+ action: 'create',
+ label: 'import_access_level',
+ user: user,
+ extra: { import_type: 'github', user_role: 'Owner' }
+ )
end
it 'returns error when the repository is larger than the limit' do
diff --git a/spec/services/incident_management/incidents/create_service_spec.rb b/spec/services/incident_management/incidents/create_service_spec.rb
index 851b21e1227..7db762b9c5b 100644
--- a/spec/services/incident_management/incidents/create_service_spec.rb
+++ b/spec/services/incident_management/incidents/create_service_spec.rb
@@ -66,6 +66,26 @@ RSpec.describe IncidentManagement::Incidents::CreateService do
end
end
end
+
+ context 'with an alert' do
+ subject(:create_incident) { described_class.new(project, user, title: title, description: description, alert: alert).execute }
+
+ context 'when the alert is valid' do
+ let(:alert) { create(:alert_management_alert, project: project) }
+
+ it 'associates the alert with the incident' do
+ expect(create_incident[:issue].reload.alert_management_alerts).to match_array([alert])
+ end
+ end
+
+ context 'when the alert is not valid' do
+ let(:alert) { create(:alert_management_alert, :with_validation_errors, project: project) }
+
+ it 'does not associate the alert with the incident' do
+ expect(create_incident[:issue].reload.alert_management_alerts).to be_empty
+ end
+ end
+ end
end
context 'when incident has no title' do
@@ -89,10 +109,6 @@ RSpec.describe IncidentManagement::Incidents::CreateService do
subject(:create_incident) { described_class.new(project, user, title: title, description: description, alert: alert).execute }
- it 'associates the alert with the incident' do
- expect(create_incident[:issue].alert_management_alert).to eq(alert)
- end
-
context 'the alert prevents the issue from saving' do
let(:alert) { create(:alert_management_alert, :with_validation_errors, project: project) }
diff --git a/spec/services/incident_management/link_alerts/create_service_spec.rb b/spec/services/incident_management/link_alerts/create_service_spec.rb
new file mode 100644
index 00000000000..fab28771174
--- /dev/null
+++ b/spec/services/incident_management/link_alerts/create_service_spec.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe IncidentManagement::LinkAlerts::CreateService, feature_category: :incident_management do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:another_project) { create(:project) }
+ let_it_be(:linked_alert) { create(:alert_management_alert, project: project) }
+ let_it_be(:alert1) { create(:alert_management_alert, project: project) }
+ let_it_be(:alert2) { create(:alert_management_alert, project: project) }
+ let_it_be(:external_alert) { create(:alert_management_alert, project: another_project) }
+ let_it_be(:incident) { create(:incident, project: project, alert_management_alerts: [linked_alert]) }
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:another_developer) { create(:user) }
+
+ before_all do
+ project.add_guest(guest)
+ project.add_developer(developer)
+ project.add_developer(another_developer)
+
+ another_project.add_guest(guest)
+ another_project.add_developer(developer)
+ end
+
+ describe '#execute' do
+ subject(:execute) { described_class.new(incident, current_user, alert_references).execute }
+
+ let(:alert_references) { [alert1.to_reference, alert2.details_url] }
+
+ context 'when current user is a guest' do
+ let(:current_user) { guest }
+
+ it 'responds with error', :aggregate_failures do
+ response = execute
+
+ expect(response).to be_error
+ expect(response.message).to eq('You have insufficient permissions to manage alerts for this project')
+ end
+
+ it 'does not link alerts to the incident' do
+ expect { execute }.not_to change { incident.reload.alert_management_alerts.to_a }
+ end
+ end
+
+ context 'when current user is a developer' do
+ let(:current_user) { developer }
+
+ it 'responds with success', :aggregate_failures do
+ response = execute
+
+ expect(response).to be_success
+ expect(response.payload[:incident]).to eq(incident)
+ end
+
+ it 'links alerts to the incident' do
+ expect { execute }
+ .to change { incident.reload.alert_management_alerts.to_a }
+ .from([linked_alert])
+ .to match_array([linked_alert, alert1, alert2])
+ end
+
+ context 'when linking an already linked alert' do
+ let(:alert_references) { [linked_alert.details_url] }
+
+ it 'does not change incident alerts list' do
+ expect { execute }.not_to change { incident.reload.alert_management_alerts.to_a }
+ end
+ end
+
+ context 'when linking an alert from another project' do
+ let(:alert_references) { [external_alert.details_url] }
+
+ it 'links an external alert to the incident' do
+ expect { execute }
+ .to change { incident.reload.alert_management_alerts.to_a }
+ .from([linked_alert])
+ .to match_array([linked_alert, external_alert])
+ end
+ end
+ end
+
+ context 'when current user does not have permission to read alerts on external project' do
+ let(:current_user) { another_developer }
+
+ context 'when linking alerts from current and external projects' do
+ let(:alert_references) { [alert1.details_url, external_alert.details_url] }
+
+ it 'links only alerts the current user can read' do
+ expect { execute }
+ .to change { incident.reload.alert_management_alerts.to_a }
+ .from([linked_alert])
+ .to match_array([linked_alert, alert1])
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/incident_management/link_alerts/destroy_service_spec.rb b/spec/services/incident_management/link_alerts/destroy_service_spec.rb
new file mode 100644
index 00000000000..13885ab7d5d
--- /dev/null
+++ b/spec/services/incident_management/link_alerts/destroy_service_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe IncidentManagement::LinkAlerts::DestroyService, feature_category: :incident_management do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:another_project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:incident) { create(:incident, project: project) }
+ let_it_be(:another_incident) { create(:incident, project: project) }
+ let_it_be(:internal_alert) { create(:alert_management_alert, project: project, issue: incident) }
+ let_it_be(:external_alert) { create(:alert_management_alert, project: another_project, issue: incident) }
+ let_it_be(:unrelated_alert) { create(:alert_management_alert, project: project, issue: another_incident) }
+
+ before_all do
+ project.add_guest(guest)
+ project.add_developer(developer)
+ end
+
+ describe '#execute' do
+ subject(:execute) { described_class.new(incident, current_user, alert).execute }
+
+ let(:alert) { internal_alert }
+
+ context 'when current user is a guest' do
+ let(:current_user) { guest }
+
+ it 'responds with error', :aggregate_failures do
+ response = execute
+
+ expect(response).to be_error
+ expect(response.message).to eq('You have insufficient permissions to manage alerts for this project')
+ end
+
+ it 'does not unlink alert from the incident' do
+ expect { execute }.not_to change { incident.reload.alert_management_alerts.to_a }
+ end
+ end
+
+ context 'when current user is a developer' do
+ let(:current_user) { developer }
+
+ it 'responds with success', :aggregate_failures do
+ response = execute
+
+ expect(response).to be_success
+ expect(response.payload[:incident]).to eq(incident)
+ end
+
+ context 'when unlinking internal alert' do
+ let(:alert) { internal_alert }
+
+ it 'unlinks the alert' do
+ expect { execute }
+ .to change { incident.reload.alert_management_alerts.to_a }
+ .to match_array([external_alert])
+ end
+ end
+
+ context 'when unlinking external alert' do
+ let(:alert) { external_alert }
+
+ it 'unlinks the alert' do
+ expect { execute }
+ .to change { incident.reload.alert_management_alerts.to_a }
+ .to match_array([internal_alert])
+ end
+ end
+
+ context 'when unlinking an alert not related to the incident' do
+ let(:alert) { unrelated_alert }
+
+ it "does not change the incident's alerts" do
+ expect { execute }.not_to change { incident.reload.alert_management_alerts.to_a }
+ end
+
+ it "does not change another incident's alerts" do
+ expect { execute }.not_to change { another_incident.reload.alert_management_alerts.to_a }
+ end
+
+ it "does not change the alert's incident" do
+ expect { execute }.not_to change { unrelated_alert.reload.issue }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb b/spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb
index 572b1a20166..2fda789cf56 100644
--- a/spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb
+++ b/spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe IncidentManagement::PagerDuty::CreateIncidentIssueService do
let(:webhook_payload) { Gitlab::Json.parse(fixture_file('pager_duty/webhook_incident_trigger.json')) }
let(:parsed_payload) { ::PagerDuty::WebhookPayloadParser.call(webhook_payload) }
- let(:incident_payload) { parsed_payload.first['incident'] }
+ let(:incident_payload) { parsed_payload['incident'] }
subject(:execute) { described_class.new(project, incident_payload).execute }
@@ -41,14 +41,14 @@ RSpec.describe IncidentManagement::PagerDuty::CreateIncidentIssueService do
expect(execute.payload[:issue].description).to eq(
<<~MARKDOWN.chomp
- **Incident:** [My new incident](https://webdemo.pagerduty.com/incidents/PRORDTY)#{markdown_line_break}
- **Incident number:** 33#{markdown_line_break}
+ **Incident:** [[FILTERED]](https://gitlab-1.pagerduty.com/incidents/Q1XZUF87W1HB5A)#{markdown_line_break}
+ **Incident number:** 2#{markdown_line_break}
**Urgency:** high#{markdown_line_break}
**Status:** triggered#{markdown_line_break}
- **Incident key:** #{markdown_line_break}
- **Created at:** 26 September 2017, 3:14PM (UTC)#{markdown_line_break}
- **Assignees:** [Laura Haley](https://webdemo.pagerduty.com/users/P553OPV)#{markdown_line_break}
- **Impacted services:** [Production XDB Cluster](https://webdemo.pagerduty.com/services/PN49J75)
+ **Incident key:** [FILTERED]#{markdown_line_break}
+ **Created at:** 30 November 2022, 8:46AM (UTC)#{markdown_line_break}
+ **Assignees:** [Rajendra Kadam](https://gitlab-1.pagerduty.com/users/PIN0B5C)#{markdown_line_break}
+ **Impacted service:** [Test service](https://gitlab-1.pagerduty.com/services/PK6IKMT)
MARKDOWN
)
end
diff --git a/spec/services/incident_management/pager_duty/process_webhook_service_spec.rb b/spec/services/incident_management/pager_duty/process_webhook_service_spec.rb
index 8b6eb21c25d..e2aba0b61af 100644
--- a/spec/services/incident_management/pager_duty/process_webhook_service_spec.rb
+++ b/spec/services/incident_management/pager_duty/process_webhook_service_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe IncidentManagement::PagerDuty::ProcessWebhookService do
end
it 'processes issues' do
- incident_payload = ::PagerDuty::WebhookPayloadParser.call(webhook_payload).first['incident']
+ incident_payload = ::PagerDuty::WebhookPayloadParser.call(webhook_payload)['incident']
expect(::IncidentManagement::PagerDuty::ProcessIncidentWorker)
.to receive(:perform_async)
diff --git a/spec/services/incident_management/timeline_events/create_service_spec.rb b/spec/services/incident_management/timeline_events/create_service_spec.rb
index b10862a78b5..a3810879c65 100644
--- a/spec/services/incident_management/timeline_events/create_service_spec.rb
+++ b/spec/services/incident_management/timeline_events/create_service_spec.rb
@@ -55,6 +55,15 @@ RSpec.describe IncidentManagement::TimelineEvents::CreateService do
end
it_behaves_like 'an incident management tracked event', :incident_management_timeline_event_created
+
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { project.namespace.reload }
+ let(:category) { described_class.to_s }
+ let(:user) { current_user }
+ let(:action) { 'incident_management_timeline_event_created' }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ end
end
subject(:execute) { service.execute }
@@ -276,6 +285,15 @@ RSpec.describe IncidentManagement::TimelineEvents::CreateService do
it_behaves_like 'an incident management tracked event', :incident_management_timeline_event_created
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { project.namespace.reload }
+ let(:category) { described_class.to_s }
+ let(:user) { current_user }
+ let(:action) { 'incident_management_timeline_event_created' }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ end
+
it 'successfully creates a database record', :aggregate_failures do
expect { execute }.to change { ::IncidentManagement::TimelineEvent.count }.by(1)
end
diff --git a/spec/services/incident_management/timeline_events/destroy_service_spec.rb b/spec/services/incident_management/timeline_events/destroy_service_spec.rb
index e1b258960ae..f90ff72a2bf 100644
--- a/spec/services/incident_management/timeline_events/destroy_service_spec.rb
+++ b/spec/services/incident_management/timeline_events/destroy_service_spec.rb
@@ -65,6 +65,15 @@ RSpec.describe IncidentManagement::TimelineEvents::DestroyService do
end
it_behaves_like 'an incident management tracked event', :incident_management_timeline_event_deleted
+
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { project.namespace.reload }
+ let(:category) { described_class.to_s }
+ let(:user) { current_user }
+ let(:action) { 'incident_management_timeline_event_deleted' }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ end
end
end
end
diff --git a/spec/services/incident_management/timeline_events/update_service_spec.rb b/spec/services/incident_management/timeline_events/update_service_spec.rb
index 2373a73e108..ff802109715 100644
--- a/spec/services/incident_management/timeline_events/update_service_spec.rb
+++ b/spec/services/incident_management/timeline_events/update_service_spec.rb
@@ -2,10 +2,20 @@
require 'spec_helper'
-RSpec.describe IncidentManagement::TimelineEvents::UpdateService do
+RSpec.describe IncidentManagement::TimelineEvents::UpdateService, feature_category: :incident_management do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:incident) { create(:incident, project: project) }
+ let_it_be(:tag1) { create(:incident_management_timeline_event_tag, project: project, name: 'Tag 1') }
+ let_it_be(:tag2) { create(:incident_management_timeline_event_tag, project: project, name: 'Tag 2') }
+ let_it_be(:tag3) { create(:incident_management_timeline_event_tag, project: project, name: 'Tag 3') }
+
+ let!(:tag_link1) do
+ create(:incident_management_timeline_event_tag_link,
+ timeline_event: timeline_event,
+ timeline_event_tag: tag3
+ )
+ end
let!(:timeline_event) { create(:incident_management_timeline_event, project: project, incident: incident) }
let(:occurred_at) { 1.minute.ago }
@@ -13,6 +23,24 @@ RSpec.describe IncidentManagement::TimelineEvents::UpdateService do
let(:current_user) { user }
describe '#execute' do
+ shared_examples 'successful tag response' do
+ it_behaves_like 'successful response'
+
+ it 'adds the new tag' do
+ expect { execute }.to change { timeline_event.timeline_event_tags.count }.by(1)
+ end
+
+ it 'adds the new tag link' do
+ expect { execute }.to change { IncidentManagement::TimelineEventTagLink.count }.by(1)
+ end
+
+ it 'returns the new tag in response' do
+ timeline_event = execute.payload[:timeline_event]
+
+ expect(timeline_event.timeline_event_tags.pluck_names).to contain_exactly(tag1.name, tag3.name)
+ end
+ end
+
shared_examples 'successful response' do
it 'responds with success', :aggregate_failures do
expect(execute).to be_success
@@ -20,6 +48,14 @@ RSpec.describe IncidentManagement::TimelineEvents::UpdateService do
end
it_behaves_like 'an incident management tracked event', :incident_management_timeline_event_edited
+
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { project.namespace.reload }
+ let(:category) { described_class.to_s }
+ let(:action) { 'incident_management_timeline_event_edited' }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ end
end
shared_examples 'error response' do |message|
@@ -67,7 +103,7 @@ RSpec.describe IncidentManagement::TimelineEvents::UpdateService do
it_behaves_like 'passing the correct was_changed value', :occurred_at_and_note
context 'when note is nil' do
- let(:params) { { occurred_at: occurred_at } }
+ let(:params) { { occurred_at: occurred_at, timeline_event_tag_names: [tag3.name, tag2.name] } }
it_behaves_like 'successful response'
it_behaves_like 'passing the correct was_changed value', :occurred_at
@@ -79,18 +115,30 @@ RSpec.describe IncidentManagement::TimelineEvents::UpdateService do
it 'updates occurred_at' do
expect { execute }.to change { timeline_event.occurred_at }.to(params[:occurred_at])
end
+
+ it 'updates the tags' do
+ expect { execute }.to change { timeline_event.timeline_event_tags.count }.by(1)
+ end
end
context 'when note is blank' do
- let(:params) { { note: '', occurred_at: occurred_at } }
+ let(:params) { { note: '', occurred_at: occurred_at, timeline_event_tag_names: [tag3.name, tag2.name] } }
it_behaves_like 'error response', "Timeline text can't be blank"
+
+ it 'does not add the tags as it rollsback the transaction' do
+ expect { execute }.not_to change { timeline_event.timeline_event_tags.count }
+ end
end
context 'when note is more than 280 characters long' do
- let(:params) { { note: 'n' * 281, occurred_at: occurred_at } }
+ let(:params) { { note: 'n' * 281, occurred_at: occurred_at, timeline_event_tag_names: [tag3.name, tag2.name] } }
it_behaves_like 'error response', 'Timeline text is too long (maximum is 280 characters)'
+
+ it 'does not add the tags as it rollsback the transaction' do
+ expect { execute }.not_to change { timeline_event.timeline_event_tags.count }
+ end
end
context 'when occurred_at is nil' do
@@ -109,9 +157,13 @@ RSpec.describe IncidentManagement::TimelineEvents::UpdateService do
end
context 'when occurred_at is blank' do
- let(:params) { { note: 'Updated note', occurred_at: '' } }
+ let(:params) { { note: 'Updated note', occurred_at: '', timeline_event_tag_names: [tag3.name, tag2.name] } }
it_behaves_like 'error response', "Occurred at can't be blank"
+
+ it 'does not add the tags as it rollsback the transaction' do
+ expect { execute }.not_to change { timeline_event.timeline_event_tags.count }
+ end
end
context 'when both occurred_at and note is nil' do
@@ -142,6 +194,112 @@ RSpec.describe IncidentManagement::TimelineEvents::UpdateService do
it_behaves_like 'error response',
'You have insufficient permissions to manage timeline events for this incident'
end
+
+ context 'when timeline event tags are passed' do
+ context 'when predefined tags are passed' do
+ let(:params) do
+ {
+ note: 'Updated note',
+ occurred_at: occurred_at,
+ timeline_event_tag_names: ['start time', 'end time']
+ }
+ end
+
+ it 'returns the new tag in response' do
+ timeline_event = execute.payload[:timeline_event]
+
+ expect(timeline_event.timeline_event_tags.pluck_names).to contain_exactly('Start time', 'End time')
+ end
+
+ it 'creates the predefined tags on the project' do
+ execute
+
+ expect(project.incident_management_timeline_event_tags.pluck_names).to include('Start time', 'End time')
+ end
+ end
+
+ context 'when they exist' do
+ let(:params) do
+ {
+ note: 'Updated note',
+ occurred_at: occurred_at,
+ timeline_event_tag_names: [tag3.name, tag1.name]
+ }
+ end
+
+ it_behaves_like 'successful tag response'
+
+ context 'when tag name is of random case' do
+ let(:params) do
+ {
+ note: 'Updated note',
+ occurred_at: occurred_at,
+ timeline_event_tag_names: ['tAg 3', 'TaG 1']
+ }
+ end
+
+ it_behaves_like 'successful tag response'
+ end
+
+ context 'when tag is removed' do
+ let(:params) { { note: 'Updated note', occurred_at: occurred_at, timeline_event_tag_names: [tag2.name] } }
+
+ it_behaves_like 'successful response'
+
+ it 'adds the new tag and removes the old tag' do
+ # Since it adds a tag (+1) and removes old tag (-1) so next change in count in 0
+ expect { execute }.to change { timeline_event.timeline_event_tags.count }.by(0)
+ end
+
+ it 'adds the new tag link and removes the old tag link' do
+ # Since it adds a tag link (+1) and removes old tag link (-1) so next change in count in 0
+ expect { execute }.to change { IncidentManagement::TimelineEventTagLink.count }.by(0)
+ end
+
+ it 'returns the new tag and does not contain the old tag in response' do
+ timeline_event = execute.payload[:timeline_event]
+
+ expect(timeline_event.timeline_event_tags.pluck_names).to contain_exactly(tag2.name)
+ end
+ end
+
+ context 'when all assigned tags are removed' do
+ let(:params) { { note: 'Updated note', occurred_at: occurred_at, timeline_event_tag_names: [] } }
+
+ it_behaves_like 'successful response'
+
+ it 'removes all the assigned tags' do
+ expect { execute }.to change { timeline_event.timeline_event_tags.count }.by(-1)
+ end
+
+ it 'removes all the assigned tag links' do
+ expect { execute }.to change { IncidentManagement::TimelineEventTagLink.count }.by(-1)
+ end
+
+ it 'does not contain any tags in response' do
+ timeline_event = execute.payload[:timeline_event]
+
+ expect(timeline_event.timeline_event_tags.pluck_names).to be_empty
+ end
+ end
+ end
+
+ context 'when they do not exist' do
+ let(:params) do
+ {
+ note: 'Updated note 2',
+ occurred_at: occurred_at,
+ timeline_event_tag_names: ['non existing tag']
+ }
+ end
+
+ it_behaves_like 'error response', "Following tags don't exist: [\"non existing tag\"]"
+
+ it 'does not update the note' do
+ expect { execute }.not_to change { timeline_event.reload.note }
+ end
+ end
+ end
end
context 'when user does not have permissions' do
diff --git a/spec/services/issuable/discussions_list_service_spec.rb b/spec/services/issuable/discussions_list_service_spec.rb
index 2ce47f42a72..ecdd8d031c9 100644
--- a/spec/services/issuable/discussions_list_service_spec.rb
+++ b/spec/services/issuable/discussions_list_service_spec.rb
@@ -17,6 +17,19 @@ RSpec.describe Issuable::DiscussionsListService do
let_it_be(:issuable) { create(:issue, project: project) }
it_behaves_like 'listing issuable discussions', :guest, 1, 7
+
+ context 'without notes widget' do
+ let_it_be(:issuable) { create(:work_item, :issue, project: project) }
+
+ before do
+ stub_const('WorkItems::Type::BASE_TYPES', { issue: { name: 'NoNotesWidget', enum_value: 0 } })
+ stub_const('WorkItems::Type::WIDGETS_FOR_TYPE', { issue: [::WorkItems::Widgets::Description] })
+ end
+
+ it "returns no notes" do
+ expect(discussions_service.execute).to be_empty
+ end
+ end
end
describe 'fetching notes for merge requests' do
diff --git a/spec/services/issue_links/create_service_spec.rb b/spec/services/issue_links/create_service_spec.rb
index 9cb5980716a..88e8470658d 100644
--- a/spec/services/issue_links/create_service_spec.rb
+++ b/spec/services/issue_links/create_service_spec.rb
@@ -41,6 +41,14 @@ RSpec.describe IssueLinks::CreateService do
it_behaves_like 'an incident management tracked event', :incident_management_incident_relate do
let(:current_user) { user }
end
+
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { issue.namespace }
+ let(:category) { described_class.to_s }
+ let(:action) { 'incident_management_incident_relate' }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ end
end
end
end
diff --git a/spec/services/issue_links/destroy_service_spec.rb b/spec/services/issue_links/destroy_service_spec.rb
index a478a2c1448..ecb53b5cd31 100644
--- a/spec/services/issue_links/destroy_service_spec.rb
+++ b/spec/services/issue_links/destroy_service_spec.rb
@@ -25,6 +25,14 @@ RSpec.describe IssueLinks::DestroyService do
it_behaves_like 'an incident management tracked event', :incident_management_incident_unrelate do
let(:current_user) { user }
end
+
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { issue_b.namespace }
+ let(:category) { described_class.to_s }
+ let(:action) { 'incident_management_incident_unrelate' }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ end
end
end
end
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index ef92b6984d5..e6ad755f911 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -99,6 +99,14 @@ RSpec.describe Issues::CloseService do
it_behaves_like 'an incident management tracked event', :incident_management_incident_closed
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { issue.namespace }
+ let(:category) { described_class.to_s }
+ let(:action) { 'incident_management_incident_closed' }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ end
+
it 'creates a new escalation resolved escalation status', :aggregate_failures do
expect { service.execute(issue) }.to change { IncidentManagement::IssuableEscalationStatus.where(issue: issue).count }.by(1)
@@ -346,31 +354,26 @@ RSpec.describe Issues::CloseService do
context 'when there is an associated Alert Management Alert' do
context 'when alert can be resolved' do
- let!(:alert) { create(:alert_management_alert, issue: issue, project: project) }
-
it 'resolves an alert and sends a system note' do
- expect_any_instance_of(SystemNoteService) do |notes_service|
- expect(notes_service).to receive(:change_alert_status).with(
- alert,
- current_user,
- " by closing issue #{issue.to_reference(project)}"
- )
- end
+ alert = create(:alert_management_alert, issue: issue, project: project)
+
+ expect(SystemNoteService).to receive(:change_alert_status)
+ .with(alert, User.alert_bot, " because #{user.to_reference} closed incident #{issue.to_reference(project)}")
close_issue
- expect(alert.reload.resolved?).to eq(true)
+ expect(alert.reload).to be_resolved
end
end
context 'when alert cannot be resolved' do
- let!(:alert) { create(:alert_management_alert, :with_validation_errors, issue: issue, project: project) }
-
before do
allow(Gitlab::AppLogger).to receive(:warn).and_call_original
end
it 'writes a warning into the log' do
+ alert = create(:alert_management_alert, :with_validation_errors, issue: issue, project: project)
+
close_issue
expect(Gitlab::AppLogger).to have_received(:warn).with(
@@ -383,6 +386,23 @@ RSpec.describe Issues::CloseService do
end
end
+ context 'when there are several associated Alert Management Alerts' do
+ context 'when alerts can be resolved' do
+ it 'resolves an alert and sends a system note', :aggregate_failures do
+ alerts = create_list(:alert_management_alert, 2, issue: issue, project: project)
+
+ alerts.each do |alert|
+ expect(SystemNoteService).to receive(:change_alert_status)
+ .with(alert, User.alert_bot, " because #{user.to_reference} closed incident #{issue.to_reference(project)}")
+ end
+
+ close_issue
+
+ expect(alerts.map(&:reload)).to all(be_resolved)
+ end
+ end
+ end
+
it 'deletes milestone issue counters cache' do
issue.update!(milestone: create(:milestone, project: project))
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 5ddf91e167e..7ab2046b6be 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -9,21 +9,22 @@ RSpec.describe Issues::CreateService do
let_it_be_with_reload(:project) { create(:project, :public, group: group) }
let_it_be(:user) { create(:user) }
+ let(:opts) { { title: 'title' } }
let(:spam_params) { double }
+ let(:service) { described_class.new(project: project, current_user: user, params: opts, spam_params: spam_params) }
it_behaves_like 'rate limited service' do
let(:key) { :issues_create }
let(:key_scope) { %i[project current_user external_author] }
let(:application_limit_key) { :issues_create_limit }
let(:created_model) { Issue }
- let(:service) { described_class.new(project: project, current_user: user, params: { title: 'title' }, spam_params: double) }
end
describe '#execute' do
let_it_be(:assignee) { create(:user) }
let_it_be(:milestone) { create(:milestone, project: project) }
- let(:result) { described_class.new(project: project, current_user: user, params: opts, spam_params: spam_params).execute }
+ let(:result) { service.execute }
let(:issue) { result[:issue] }
before do
@@ -54,6 +55,7 @@ RSpec.describe Issues::CreateService do
let(:opts) do
{ title: 'Awesome issue',
+ issue_type: :task,
description: 'please fix',
assignee_ids: [assignee.id],
label_ids: labels.map(&:id),
@@ -118,10 +120,26 @@ RSpec.describe Issues::CreateService do
expect(issue.labels).to match_array(labels)
expect(issue.milestone).to eq(milestone)
expect(issue.due_date).to eq(Date.tomorrow)
- expect(issue.work_item_type.base_type).to eq('issue')
+ expect(issue.work_item_type.base_type).to eq('task')
expect(issue.issue_customer_relations_contacts).to be_empty
end
+ context 'when the work item type is not allowed to create' do
+ before do
+ allow_next_instance_of(::Issues::BuildService) do |instance|
+ allow(instance).to receive(:create_issue_type_allowed?).twice.and_return(false)
+ end
+ end
+
+ it 'ignores the type and creates default issue' do
+ expect(result).to be_success
+ expect(issue).to be_persisted
+ expect(issue).to be_a(::Issue)
+ expect(issue.work_item_type.base_type).to eq('issue')
+ expect(issue.issue_type).to eq('issue')
+ end
+ end
+
it 'calls NewIssueWorker with correct arguments' do
expect(NewIssueWorker).to receive(:perform_async).with(Integer, user.id, 'Issue')
@@ -405,7 +423,8 @@ RSpec.describe Issues::CreateService do
iid: { current: kind_of(Integer), previous: nil },
project_id: { current: project.id, previous: nil },
title: { current: opts[:title], previous: nil },
- updated_at: { current: kind_of(Time), previous: nil }
+ updated_at: { current: kind_of(Time), previous: nil },
+ time_estimate: { current: 0, previous: nil }
},
object_attributes: include(
opts.merge(
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
index 655c5085fdc..324b2aa9fe2 100644
--- a/spec/services/issues/move_service_spec.rb
+++ b/spec/services/issues/move_service_spec.rb
@@ -508,4 +508,25 @@ RSpec.describe Issues::MoveService do
end
end
end
+
+ context 'copying email participants' do
+ let!(:participant1) { create(:issue_email_participant, email: 'user1@example.com', issue: old_issue) }
+ let!(:participant2) { create(:issue_email_participant, email: 'user2@example.com', issue: old_issue) }
+ let!(:participant3) { create(:issue_email_participant, email: 'other_project_customer@example.com') }
+
+ include_context 'user can move issue'
+
+ subject(:new_issue) do
+ move_service.execute(old_issue, new_project)
+ end
+
+ it 'copies moved issue email participants' do
+ new_issue
+
+ expect(participant1.reload.issue).to eq(old_issue)
+ expect(participant2.reload.issue).to eq(old_issue)
+ expect(new_issue.issue_email_participants.pluck(:email))
+ .to match_array([participant1.email, participant2.email])
+ end
+ end
end
diff --git a/spec/services/issues/reopen_service_spec.rb b/spec/services/issues/reopen_service_spec.rb
index 6013826f9b1..529b3ff266b 100644
--- a/spec/services/issues/reopen_service_spec.rb
+++ b/spec/services/issues/reopen_service_spec.rb
@@ -74,6 +74,14 @@ RSpec.describe Issues::ReopenService do
it_behaves_like 'an incident management tracked event', :incident_management_incident_reopened
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { issue.namespace }
+ let(:category) { described_class.to_s }
+ let(:action) { 'incident_management_incident_reopened' }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ end
+
it 'creates a timeline event' do
expect(IncidentManagement::TimelineEvents::CreateService)
.to receive(:reopen_incident)
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index f1ee62fd589..70fc6ffc38f 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -60,7 +60,7 @@ RSpec.describe Issues::UpdateService, :mailer do
description: 'Also please fix',
assignee_ids: [user2.id],
state_event: 'close',
- label_ids: [label.id],
+ label_ids: [label&.id],
due_date: Date.tomorrow,
discussion_locked: true,
severity: 'low',
@@ -189,6 +189,27 @@ RSpec.describe Issues::UpdateService, :mailer do
subject { update_issue(confidential: true) }
it_behaves_like 'an incident management tracked event', :incident_management_incident_change_confidential
+
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { issue.namespace }
+ let(:category) { described_class.to_s }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ let(:action) { 'incident_management_incident_change_confidential' }
+ let(:opts) do
+ {
+ title: 'New title',
+ description: 'Also please fix',
+ assignee_ids: [user2.id],
+ state_event: 'close',
+ due_date: Date.tomorrow,
+ discussion_locked: true,
+ severity: 'low',
+ milestone_id: milestone.id,
+ add_contacts: [contact.email]
+ }
+ end
+ end
end
end
@@ -673,6 +694,14 @@ RSpec.describe Issues::UpdateService, :mailer do
let(:current_user) { user }
it_behaves_like 'an incident management tracked event', :incident_management_incident_assigned
+
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { issue.namespace }
+ let(:category) { described_class.to_s }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ let(:action) { "incident_management_incident_assigned" }
+ end
end
end
diff --git a/spec/services/issues/zoom_link_service_spec.rb b/spec/services/issues/zoom_link_service_spec.rb
index d662d9fa978..ad1f91ab5e6 100644
--- a/spec/services/issues/zoom_link_service_spec.rb
+++ b/spec/services/issues/zoom_link_service_spec.rb
@@ -95,6 +95,14 @@ RSpec.describe Issues::ZoomLinkService do
let(:current_user) { user }
it_behaves_like 'an incident management tracked event', :incident_management_incident_zoom_meeting
+
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { issue.namespace }
+ let(:category) { described_class.to_s }
+ let(:action) { 'incident_management_incident_zoom_meeting' }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ end
end
context 'with insufficient issue update permissions' do
diff --git a/spec/services/jira_connect/create_asymmetric_jwt_service_spec.rb b/spec/services/jira_connect/create_asymmetric_jwt_service_spec.rb
index f5359e5b643..bb96e327307 100644
--- a/spec/services/jira_connect/create_asymmetric_jwt_service_spec.rb
+++ b/spec/services/jira_connect/create_asymmetric_jwt_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JiraConnect::CreateAsymmetricJwtService do
+RSpec.describe JiraConnect::CreateAsymmetricJwtService, feature_category: :integrations do
describe '#execute' do
let_it_be(:jira_connect_installation) { create(:jira_connect_installation) }
@@ -19,27 +19,40 @@ RSpec.describe JiraConnect::CreateAsymmetricJwtService do
let(:public_key_id) { Atlassian::Jwt.decode(jwt_token, nil, false, algorithm: 'RS256').last['kid'] }
let(:public_key_cdn) { 'https://gitlab.com/-/jira_connect/public_keys/' }
+ let(:event_url) { 'https://gitlab.test/-/jira_connect/events/installed' }
let(:jwt_verification_claims) do
{
aud: 'https://gitlab.test/-/jira_connect',
iss: jira_connect_installation.client_key,
- qsh: Atlassian::Jwt.create_query_string_hash('https://gitlab.test/-/jira_connect/events/installed', 'POST', 'https://gitlab.test/-/jira_connect')
+ qsh: Atlassian::Jwt.create_query_string_hash(event_url, 'POST', 'https://gitlab.test/-/jira_connect')
}
end
subject(:jwt_token) { service.execute }
+ shared_examples 'produces a valid JWT' do
+ it 'produces a valid JWT' do
+ public_key = OpenSSL::PKey.read(JiraConnect::PublicKey.find(public_key_id).key)
+ options = jwt_verification_claims.except(:qsh).merge({ verify_aud: true, verify_iss: true,
+ algorithm: 'RS256' })
+
+ decoded_token = Atlassian::Jwt.decode(jwt_token, public_key, true, options).first
+
+ expect(decoded_token).to eq(jwt_verification_claims.stringify_keys)
+ end
+ end
+
it 'stores the public key' do
expect { JiraConnect::PublicKey.find(public_key_id) }.not_to raise_error
end
- it 'is produces a valid JWT' do
- public_key = OpenSSL::PKey.read(JiraConnect::PublicKey.find(public_key_id).key)
- options = jwt_verification_claims.except(:qsh).merge({ verify_aud: true, verify_iss: true, algorithm: 'RS256' })
+ it_behaves_like 'produces a valid JWT'
- decoded_token = Atlassian::Jwt.decode(jwt_token, public_key, true, options).first
+ context 'with uninstalled event option' do
+ let(:service) { described_class.new(jira_connect_installation, event: :uninstalled) }
+ let(:event_url) { 'https://gitlab.test/-/jira_connect/events/uninstalled' }
- expect(decoded_token).to eq(jwt_verification_claims.stringify_keys)
+ it_behaves_like 'produces a valid JWT'
end
end
end
diff --git a/spec/services/jira_connect_installations/proxy_lifecycle_event_service_spec.rb b/spec/services/jira_connect_installations/proxy_lifecycle_event_service_spec.rb
new file mode 100644
index 00000000000..c621388a734
--- /dev/null
+++ b/spec/services/jira_connect_installations/proxy_lifecycle_event_service_spec.rb
@@ -0,0 +1,154 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe JiraConnectInstallations::ProxyLifecycleEventService, feature_category: :integrations do
+ describe '.execute' do
+ let(:installation) { create(:jira_connect_installation) }
+
+ it 'creates an instance and calls execute' do
+ expect_next_instance_of(described_class, installation, 'installed', 'https://test.gitlab.com') do |update_service|
+ expect(update_service).to receive(:execute)
+ end
+
+ described_class.execute(installation, 'installed', 'https://test.gitlab.com')
+ end
+ end
+
+ describe '.new' do
+ let_it_be(:installation) { create(:jira_connect_installation, instance_url: nil) }
+
+ let(:event) { :installed }
+
+ subject(:service) { described_class.new(installation, event, 'https://test.gitlab.com') }
+
+ it 'creates an internal duplicate of the installation and sets the instance_url' do
+ expect(service.instance_variable_get(:@installation).instance_url).to eq('https://test.gitlab.com')
+ end
+
+ context 'with unknown event' do
+ let(:event) { 'test' }
+
+ it 'raises an error' do
+ expect { service }.to raise_error(ArgumentError, 'Unknown event \'test\'')
+ end
+ end
+ end
+
+ describe '#execute' do
+ let_it_be(:installation) { create(:jira_connect_installation, instance_url: 'https://old_instance_url.example.com') }
+
+ let(:service) { described_class.new(installation, evnet_type, 'https://gitlab.example.com') }
+ let(:service_instance_installation) { service.instance_variable_get(:@installation) }
+
+ before do
+ allow_next_instance_of(JiraConnect::CreateAsymmetricJwtService) do |create_asymmetric_jwt_service|
+ allow(create_asymmetric_jwt_service).to receive(:execute).and_return('123456')
+ end
+
+ stub_request(:post, hook_url)
+ end
+
+ subject(:execute_service) { service.execute }
+
+ shared_examples 'sends the event hook' do
+ it 'returns a ServiceResponse' do
+ expect(execute_service).to be_kind_of(ServiceResponse)
+ expect(execute_service[:status]).to eq(:success)
+ end
+
+ it 'sends an installed event to the instance' do
+ execute_service
+
+ expect(WebMock).to have_requested(:post, hook_url).with(body: expected_request_body)
+ end
+
+ it 'creates the JWT token with the event and installation' do
+ expect_next_instance_of(
+ JiraConnect::CreateAsymmetricJwtService,
+ service_instance_installation,
+ event: evnet_type
+ ) do |create_asymmetric_jwt_service|
+ expect(create_asymmetric_jwt_service).to receive(:execute).and_return('123456')
+ end
+
+ expect(execute_service[:status]).to eq(:success)
+ end
+
+ context 'and the instance responds with an error' do
+ before do
+ stub_request(:post, hook_url).to_return(
+ status: 422,
+ body: 'Error message',
+ headers: {}
+ )
+ end
+
+ it 'returns an error ServiceResponse', :aggregate_failures do
+ expect(execute_service).to be_kind_of(ServiceResponse)
+ expect(execute_service[:status]).to eq(:error)
+ expect(execute_service[:message]).to eq( { type: :response_error, code: 422 } )
+ end
+
+ it 'logs the error response' do
+ expect(Gitlab::IntegrationsLogger).to receive(:info).with(
+ integration: 'JiraConnect',
+ message: 'Proxy lifecycle event received error response',
+ event_type: evnet_type,
+ status_code: 422,
+ body: 'Error message'
+ )
+
+ execute_service
+ end
+ end
+
+ context 'and the request raises an error' do
+ before do
+ allow(Gitlab::HTTP).to receive(:post).and_raise(Errno::ECONNREFUSED, 'error message')
+ end
+
+ it 'returns an error ServiceResponse', :aggregate_failures do
+ expect(execute_service).to be_kind_of(ServiceResponse)
+ expect(execute_service[:status]).to eq(:error)
+ expect(execute_service[:message]).to eq(
+ {
+ type: :network_error,
+ message: 'Connection refused - error message'
+ }
+ )
+ end
+ end
+ end
+
+ context 'when installed event' do
+ let(:evnet_type) { :installed }
+ let(:hook_url) { 'https://gitlab.example.com/-/jira_connect/events/installed' }
+ let(:expected_request_body) do
+ {
+ clientKey: installation.client_key,
+ sharedSecret: installation.shared_secret,
+ baseUrl: installation.base_url,
+ jwt: '123456',
+ eventType: 'installed'
+ }
+ end
+
+ it_behaves_like 'sends the event hook'
+ end
+
+ context 'when uninstalled event' do
+ let(:evnet_type) { :uninstalled }
+ let(:hook_url) { 'https://gitlab.example.com/-/jira_connect/events/uninstalled' }
+ let(:expected_request_body) do
+ {
+ clientKey: installation.client_key,
+ jwt: '123456',
+ eventType: 'uninstalled'
+ }
+ end
+
+ it_behaves_like 'sends the event hook'
+ end
+ end
+end
diff --git a/spec/services/jira_connect_installations/update_service_spec.rb b/spec/services/jira_connect_installations/update_service_spec.rb
new file mode 100644
index 00000000000..ec5bb5d6d6a
--- /dev/null
+++ b/spec/services/jira_connect_installations/update_service_spec.rb
@@ -0,0 +1,186 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe JiraConnectInstallations::UpdateService, feature_category: :integrations do
+ describe '.execute' do
+ it 'creates an instance and calls execute' do
+ expect_next_instance_of(described_class, 'param1', 'param2') do |update_service|
+ expect(update_service).to receive(:execute)
+ end
+
+ described_class.execute('param1', 'param2')
+ end
+ end
+
+ describe '#execute' do
+ let_it_be_with_reload(:installation) { create(:jira_connect_installation) }
+ let(:update_params) { { client_key: 'new_client_key' } }
+
+ subject(:execute_service) { described_class.new(installation, update_params).execute }
+
+ it 'returns a ServiceResponse' do
+ expect(execute_service).to be_kind_of(ServiceResponse)
+ expect(execute_service[:status]).to eq(:success)
+ end
+
+ it 'updates the installation' do
+ expect { execute_service }.to change { installation.client_key }.to('new_client_key')
+ end
+
+ it 'returns a successful result' do
+ expect(execute_service.success?).to eq(true)
+ end
+
+ context 'and model validation fails' do
+ let(:update_params) { { instance_url: 'invalid' } }
+
+ it 'returns an error result' do
+ expect(execute_service.error?).to eq(true)
+ expect(execute_service.message).to eq(installation.errors)
+ end
+ end
+
+ context 'and the installation has an instance_url' do
+ let_it_be_with_reload(:installation) { create(:jira_connect_installation, instance_url: 'https://other_gitlab.example.com') }
+
+ it 'sends an installed event to the instance', :aggregate_failures do
+ expect_next_instance_of(JiraConnectInstallations::ProxyLifecycleEventService, installation, :installed,
+'https://other_gitlab.example.com') do |proxy_lifecycle_events_service|
+ expect(proxy_lifecycle_events_service).to receive(:execute).and_return(ServiceResponse.new(status: :success))
+ end
+
+ expect(JiraConnect::SendUninstalledHookWorker).not_to receive(:perform_async)
+
+ expect { execute_service }.not_to change { installation.instance_url }
+ end
+
+ context 'and instance_url gets updated' do
+ let(:update_params) { { instance_url: 'https://gitlab.example.com' } }
+
+ before do
+ stub_request(:post, 'https://other_gitlab.example.com/-/jira_connect/events/uninstalled')
+ end
+
+ it 'starts an async worker to send an uninstalled event to the previous instance' do
+ expect(JiraConnect::SendUninstalledHookWorker).to receive(:perform_async).with(installation.id, 'https://other_gitlab.example.com')
+
+ expect(JiraConnectInstallations::ProxyLifecycleEventService)
+ .to receive(:execute).with(installation, :installed, 'https://gitlab.example.com')
+ .and_return(ServiceResponse.new(status: :success))
+
+ execute_service
+
+ expect(installation.instance_url).to eq(update_params[:instance_url])
+ end
+
+ context 'and the new instance_url is empty' do
+ let(:update_params) { { instance_url: nil } }
+
+ it 'starts an async worker to send an uninstalled event to the previous instance' do
+ expect(JiraConnect::SendUninstalledHookWorker).to receive(:perform_async).with(installation.id, 'https://other_gitlab.example.com')
+
+ execute_service
+
+ expect(installation.instance_url).to eq(nil)
+ end
+
+ it 'does not send an installed event' do
+ expect(JiraConnectInstallations::ProxyLifecycleEventService).not_to receive(:new)
+
+ execute_service
+ end
+ end
+ end
+ end
+
+ context 'and instance_url is updated' do
+ let(:update_params) { { instance_url: 'https://gitlab.example.com' } }
+
+ it 'sends an installed event to the instance and updates instance_url' do
+ expect_next_instance_of(JiraConnectInstallations::ProxyLifecycleEventService, installation, :installed,
+'https://gitlab.example.com') do |proxy_lifecycle_events_service|
+ expect(proxy_lifecycle_events_service).to receive(:execute).and_return(ServiceResponse.new(status: :success))
+ end
+
+ expect(JiraConnect::SendUninstalledHookWorker).not_to receive(:perform_async)
+
+ execute_service
+
+ expect(installation.instance_url).to eq(update_params[:instance_url])
+ end
+
+ context 'and the instance installation cannot be created' do
+ before do
+ allow_next_instance_of(
+ JiraConnectInstallations::ProxyLifecycleEventService,
+ installation,
+ :installed,
+ 'https://gitlab.example.com'
+ ) do |proxy_lifecycle_events_service|
+ allow(proxy_lifecycle_events_service).to receive(:execute).and_return(
+ ServiceResponse.error(
+ message: {
+ type: :response_error,
+ code: '422'
+ }
+ )
+ )
+ end
+ end
+
+ it 'does not change instance_url' do
+ expect { execute_service }.not_to change { installation.instance_url }
+ end
+
+ it 'returns an error message' do
+ expect(execute_service[:status]).to eq(:error)
+ expect(execute_service[:message]).to eq(
+ {
+ instance_url: ["Could not be installed on the instance. Error response code 422"]
+ }
+ )
+ end
+
+ context 'and the installation had a previous instance_url' do
+ let(:installation) { build(:jira_connect_installation, instance_url: 'https://other_gitlab.example.com') }
+
+ it 'does not send the uninstalled hook to the previous instance_url' do
+ expect(JiraConnect::SendUninstalledHookWorker).not_to receive(:perform_async)
+
+ execute_service
+ end
+ end
+
+ context 'when failure because of a network error' do
+ before do
+ allow_next_instance_of(
+ JiraConnectInstallations::ProxyLifecycleEventService,
+ installation,
+ :installed,
+ 'https://gitlab.example.com'
+ ) do |proxy_lifecycle_events_service|
+ allow(proxy_lifecycle_events_service).to receive(:execute).and_return(
+ ServiceResponse.error(
+ message: {
+ type: :network_error,
+ message: 'Connection refused - error message'
+ }
+ )
+ )
+ end
+ end
+
+ it 'returns an error message' do
+ expect(execute_service[:status]).to eq(:error)
+ expect(execute_service[:message]).to eq(
+ {
+ instance_url: ["Could not be installed on the instance. Network error"]
+ }
+ )
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/markup/rendering_service_spec.rb b/spec/services/markup/rendering_service_spec.rb
index d54bc71f0a4..99ab87f2072 100644
--- a/spec/services/markup/rendering_service_spec.rb
+++ b/spec/services/markup/rendering_service_spec.rb
@@ -75,25 +75,6 @@ RSpec.describe Markup::RenderingService do
is_expected.to eq(expected_html)
end
-
- context 'when renderer returns an error' do
- before do
- allow(Banzai).to receive(:render).and_raise(StandardError, "An error")
- end
-
- it 'returns html (rendered by ActionView:TextHelper)' do
- is_expected.to eq('<p>Noël</p>')
- end
-
- it 'logs the error' do
- expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
- instance_of(StandardError),
- project_id: context[:project].id, file_name: 'foo.md'
- )
-
- subject
- end
- end
end
context 'when file is asciidoc file' do
@@ -130,37 +111,5 @@ RSpec.describe Markup::RenderingService do
is_expected.to eq(expected_html)
end
end
-
- context 'when rendering takes too long' do
- let(:file_name) { 'foo.bar' }
-
- before do
- stub_const("Markup::RenderingService::RENDER_TIMEOUT", 0.1)
- allow(Gitlab::OtherMarkup).to receive(:render) do
- sleep(0.2)
- text
- end
- end
-
- it 'times out' do
- expect(Gitlab::RenderTimeout).to receive(:timeout).and_call_original
- expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
- instance_of(Timeout::Error),
- project_id: context[:project].id, file_name: file_name
- )
-
- is_expected.to eq("<p>#{text}</p>")
- end
-
- context 'when markup_rendering_timeout is disabled' do
- it 'waits until the execution completes' do
- stub_feature_flags(markup_rendering_timeout: false)
-
- expect(Gitlab::RenderTimeout).not_to receive(:timeout)
-
- is_expected.to eq(text)
- end
- end
- end
end
end
diff --git a/spec/services/merge_requests/after_create_service_spec.rb b/spec/services/merge_requests/after_create_service_spec.rb
index 2155b4ffad1..f477b2166d9 100644
--- a/spec/services/merge_requests/after_create_service_spec.rb
+++ b/spec/services/merge_requests/after_create_service_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe MergeRequests::AfterCreateService do
it 'calls the merge request activity counter' do
expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
.to receive(:track_create_mr_action)
- .with(user: merge_request.author)
+ .with(user: merge_request.author, merge_request: merge_request)
expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
.to receive(:track_mr_including_ci_config)
diff --git a/spec/services/merge_requests/approval_service_spec.rb b/spec/services/merge_requests/approval_service_spec.rb
index da6492aca95..1d6427900b9 100644
--- a/spec/services/merge_requests/approval_service_spec.rb
+++ b/spec/services/merge_requests/approval_service_spec.rb
@@ -91,6 +91,10 @@ RSpec.describe MergeRequests::ApprovalService do
it_behaves_like 'triggers GraphQL subscription mergeRequestReviewersUpdated' do
let(:action) { service.execute(merge_request) }
end
+
+ it_behaves_like 'triggers GraphQL subscription mergeRequestApprovalStateUpdated' do
+ let(:action) { service.execute(merge_request) }
+ end
end
context 'user cannot update the merge request' do
@@ -109,6 +113,10 @@ RSpec.describe MergeRequests::ApprovalService do
it_behaves_like 'does not trigger GraphQL subscription mergeRequestReviewersUpdated' do
let(:action) { service.execute(merge_request) }
end
+
+ it_behaves_like 'does not trigger GraphQL subscription mergeRequestApprovalStateUpdated' do
+ let(:action) { service.execute(merge_request) }
+ end
end
end
end
diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb
index 4f27ff30da7..79c779678a4 100644
--- a/spec/services/merge_requests/build_service_spec.rb
+++ b/spec/services/merge_requests/build_service_spec.rb
@@ -25,7 +25,9 @@ RSpec.describe MergeRequests::BuildService do
safe_message: 'Initial commit',
gitaly_commit?: false,
id: 'f00ba6',
- parent_ids: ['f00ba5'])
+ parent_ids: ['f00ba5'],
+ author_email: 'tom@example.com',
+ author_name: 'Tom Example')
end
let(:commit_2) do
@@ -34,7 +36,9 @@ RSpec.describe MergeRequests::BuildService do
safe_message: "Closes #1234 Second commit\n\nCreate the app",
gitaly_commit?: false,
id: 'f00ba7',
- parent_ids: ['f00ba6'])
+ parent_ids: ['f00ba6'],
+ author_email: 'alice@example.com',
+ author_name: 'Alice Example')
end
let(:commit_3) do
@@ -43,7 +47,9 @@ RSpec.describe MergeRequests::BuildService do
safe_message: 'This is a bad commit message!',
gitaly_commit?: false,
id: 'f00ba8',
- parent_ids: ['f00ba7'])
+ parent_ids: ['f00ba7'],
+ author_email: 'jo@example.com',
+ author_name: 'Jo Example')
end
let(:commits) { nil }
@@ -742,4 +748,91 @@ RSpec.describe MergeRequests::BuildService do
end
end
end
+
+ describe '#replace_variables_in_description' do
+ context 'when the merge request description is blank' do
+ let(:description) { nil }
+
+ it 'does not update the description' do
+ expect(merge_request.description).to eq(nil)
+ end
+ end
+
+ context 'when the merge request description contains template variables' do
+ let(:description) { <<~MSG.rstrip }
+ source_branch:%{source_branch}
+ target_branch:%{target_branch}
+ title:%{title}
+ issues:%{issues}
+ description:%{description}
+ first_commit:%{first_commit}
+ first_multiline_commit:%{first_multiline_commit}
+ url:%{url}
+ approved_by:%{approved_by}
+ merged_by:%{merged_by}
+ co_authored_by:%{co_authored_by}
+ all_commits:%{all_commits}
+ MSG
+
+ context 'when there are multiple commits in the diff' do
+ let(:commits) { Commit.decorate([commit_1, commit_2, commit_3], project) }
+
+ before do
+ stub_compare
+ end
+
+ it 'replaces the variables in the description' do
+ expect(merge_request.description).to eq <<~MSG.rstrip
+ source_branch:feature-branch
+ target_branch:master
+ title:
+ issues:
+ description:
+ first_commit:Initial commit
+ first_multiline_commit:Closes #1234 Second commit
+
+ Create the app
+ url:
+ approved_by:
+ merged_by:
+ co_authored_by:Co-authored-by: Jo Example <jo@example.com>
+ Co-authored-by: Alice Example <alice@example.com>
+ Co-authored-by: Tom Example <tom@example.com>
+ all_commits:* This is a bad commit message!
+
+ * Closes #1234 Second commit
+
+ Create the app
+
+ * Initial commit
+ MSG
+ end
+ end
+
+ context 'when there are no commits in the diff' do
+ let(:commits) { [] }
+
+ before do
+ stub_compare
+ end
+
+ it 'replaces the variables in the description' do
+ expect(merge_request.description).to eq <<~MSG.rstrip
+ source_branch:feature-branch
+ target_branch:master
+ title:
+ issues:
+ description:
+ first_commit:
+ first_multiline_commit:
+ url:
+ approved_by:
+ merged_by:
+ co_authored_by:
+ all_commits:
+ MSG
+ end
+ end
+ end
+ end
end
diff --git a/spec/services/merge_requests/create_pipeline_service_spec.rb b/spec/services/merge_requests/create_pipeline_service_spec.rb
index dc96b5c0e5e..7984fff3031 100644
--- a/spec/services/merge_requests/create_pipeline_service_spec.rb
+++ b/spec/services/merge_requests/create_pipeline_service_spec.rb
@@ -223,5 +223,26 @@ RSpec.describe MergeRequests::CreatePipelineService, :clean_gitlab_redis_cache d
expect(response.payload).to be_nil
end
end
+
+ context 'when merge request pipeline creates a dynamic environment' do
+ let(:config) do
+ {
+ review_app: {
+ script: 'echo',
+ only: ['merge_requests'],
+ environment: { name: "review/$CI_COMMIT_REF_NAME" }
+ }
+ }
+ end
+
+ it 'associates merge request with the environment' do
+ expect { response }.to change { Ci::Pipeline.count }.by(1)
+
+ environment = Environment.find_by_name('review/feature')
+ expect(response).to be_success
+ expect(environment).to be_present
+ expect(environment.merge_request).to eq(merge_request)
+ end
+ end
end
end
diff --git a/spec/services/merge_requests/remove_approval_service_spec.rb b/spec/services/merge_requests/remove_approval_service_spec.rb
index 7b38f0d1c45..fd8240935e8 100644
--- a/spec/services/merge_requests/remove_approval_service_spec.rb
+++ b/spec/services/merge_requests/remove_approval_service_spec.rb
@@ -53,6 +53,10 @@ RSpec.describe MergeRequests::RemoveApprovalService do
it_behaves_like 'triggers GraphQL subscription mergeRequestReviewersUpdated' do
let(:action) { execute! }
end
+
+ it_behaves_like 'triggers GraphQL subscription mergeRequestApprovalStateUpdated' do
+ let(:action) { execute! }
+ end
end
context 'with a user who has not approved' do
@@ -77,6 +81,10 @@ RSpec.describe MergeRequests::RemoveApprovalService do
it_behaves_like 'does not trigger GraphQL subscription mergeRequestReviewersUpdated' do
let(:action) { execute! }
end
+
+ it_behaves_like 'does not trigger GraphQL subscription mergeRequestApprovalStateUpdated' do
+ let(:action) { execute! }
+ end
end
end
end
diff --git a/spec/services/ml/experiment_tracking/candidate_repository_spec.rb b/spec/services/ml/experiment_tracking/candidate_repository_spec.rb
index 8002b2ebc86..ff3b295d185 100644
--- a/spec/services/ml/experiment_tracking/candidate_repository_spec.rb
+++ b/spec/services/ml/experiment_tracking/candidate_repository_spec.rb
@@ -31,13 +31,17 @@ RSpec.describe ::Ml::ExperimentTracking::CandidateRepository do
end
describe '#create!' do
- subject { repository.create!(experiment, 1234) }
+ subject { repository.create!(experiment, 1234, [{ key: 'hello', value: 'world' }]) }
it 'creates the candidate' do
expect(subject.start_time).to eq(1234)
expect(subject.iid).not_to be_nil
expect(subject.end_time).to be_nil
end
+
+ it 'creates with tag' do
+ expect(subject.metadata.length).to eq(1)
+ end
end
describe '#update' do
@@ -118,6 +122,32 @@ RSpec.describe ::Ml::ExperimentTracking::CandidateRepository do
end
end
+ describe '#add_tag!' do
+ let(:props) { { name: 'abc', value: 'def' } }
+
+ subject { repository.add_tag!(candidate, props[:name], props[:value]) }
+
+ it 'adds a new tag' do
+ expect { subject }.to change { candidate.reload.metadata.size }.by(1)
+ end
+
+ context 'when name missing' do
+ let(:props) { { value: 1234 } }
+
+ it 'throws RecordInvalid' do
+ expect { subject }.to raise_error(ActiveRecord::RecordInvalid)
+ end
+ end
+
+ context 'when tag was already added' do
+ it 'throws RecordInvalid' do
+ repository.add_tag!(candidate, 'new', props[:value])
+
+ expect { repository.add_tag!(candidate, 'new', props[:value]) }.to raise_error(ActiveRecord::RecordInvalid)
+ end
+ end
+ end
+
describe "#add_params" do
let(:params) do
[{ key: 'model_class', value: 'LogisticRegression' }, { 'key': 'pythonEnv', value: '3.10' }]
@@ -196,4 +226,50 @@ RSpec.describe ::Ml::ExperimentTracking::CandidateRepository do
end
end
end
+
+ describe "#add_tags" do
+ let(:tags) do
+ [{ key: 'gitlab.tag1', value: 'hello' }, { 'key': 'gitlab.tag2', value: 'world' }]
+ end
+
+ subject { repository.add_tags(candidate, tags) }
+
+ it 'adds the tags' do
+ expect { subject }.to change { candidate.reload.metadata.size }.by(2)
+ end
+
+ context 'if tags misses key' do
+ let(:tags) { [{ value: 'hello' }] }
+
+ it 'does throw and does not add' do
+ expect { subject }.to raise_error(ActiveRecord::ActiveRecordError)
+ end
+ end
+
+ context 'if tag misses value' do
+ let(:tags) { [{ key: 'gitlab.tag1' }] }
+
+ it 'does throw and does not add' do
+ expect { subject }.to raise_error(ActiveRecord::ActiveRecordError)
+ end
+ end
+
+ context 'if tag repeated' do
+ let(:params) do
+ [
+ { 'key': 'gitlab.tag1', value: 'hello' },
+ { 'key': 'gitlab.tag2', value: 'world' },
+ { 'key': 'gitlab.tag1', value: 'gitlab' }
+ ]
+ end
+
+ before do
+ repository.add_tag!(candidate, 'gitlab.tag2', '0')
+ end
+
+ it 'does not throw and adds only the first of each kind' do
+ expect { subject }.to change { candidate.reload.metadata.size }.by(1)
+ end
+ end
+ end
end
diff --git a/spec/services/ml/experiment_tracking/experiment_repository_spec.rb b/spec/services/ml/experiment_tracking/experiment_repository_spec.rb
index 80e1fa025d1..c3c716b831a 100644
--- a/spec/services/ml/experiment_tracking/experiment_repository_spec.rb
+++ b/spec/services/ml/experiment_tracking/experiment_repository_spec.rb
@@ -59,10 +59,11 @@ RSpec.describe ::Ml::ExperimentTracking::ExperimentRepository do
describe '#create!' do
let(:name) { 'hello' }
+ let(:tags) { nil }
- subject { repository.create!(name) }
+ subject { repository.create!(name, tags) }
- it 'creates the candidate' do
+ it 'creates the experiment' do
expect { subject }.to change { repository.all.size }.by(1)
end
@@ -74,6 +75,14 @@ RSpec.describe ::Ml::ExperimentTracking::ExperimentRepository do
end
end
+ context 'when has tags' do
+ let(:tags) { [{ key: 'hello', value: 'world' }] }
+
+ it 'creates the experiment with tag' do
+ expect(subject.metadata.length).to eq(1)
+ end
+ end
+
context 'when name is missing' do
let(:name) { nil }
@@ -82,4 +91,30 @@ RSpec.describe ::Ml::ExperimentTracking::ExperimentRepository do
end
end
end
+
+ describe '#add_tag!' do
+ let(:props) { { name: 'abc', value: 'def' } }
+
+ subject { repository.add_tag!(experiment, props[:name], props[:value]) }
+
+ it 'adds a new tag' do
+ expect { subject }.to change { experiment.reload.metadata.size }.by(1)
+ end
+
+ context 'when name missing' do
+ let(:props) { { value: 1234 } }
+
+ it 'throws RecordInvalid' do
+ expect { subject }.to raise_error(ActiveRecord::RecordInvalid)
+ end
+ end
+
+ context 'when tag was already added' do
+ it 'throws RecordInvalid' do
+ repository.add_tag!(experiment, 'new', props[:value])
+
+ expect { repository.add_tag!(experiment, 'new', props[:value]) }.to raise_error(ActiveRecord::RecordInvalid)
+ end
+ end
+ end
end
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 4922e72b7a4..2f1c5a5b0f3 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -102,6 +102,14 @@ RSpec.describe Notes::CreateService do
it_behaves_like 'an incident management tracked event', :incident_management_incident_comment do
let(:current_user) { user }
end
+
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { issue.namespace }
+ let(:category) { described_class.to_s }
+ let(:action) { 'incident_management_incident_comment' }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ end
end
describe 'event tracking', :snowplow do
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 7857bd2263f..1ca14cd430b 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -361,8 +361,14 @@ RSpec.describe NotificationService, :mailer do
subject(:notification_service) { notification.access_token_revoked(user, pat.name) }
- it 'sends email to the token owner' do
- expect { notification_service }.to have_enqueued_email(user, pat.name, mail: "access_token_revoked_email")
+ it 'sends email to the token owner without source' do
+ expect { notification_service }.to have_enqueued_email(user, pat.name, nil, mail: "access_token_revoked_email")
+ end
+
+ it 'sends email to the token owner with source' do
+ expect do
+ notification.access_token_revoked(user, pat.name, 'secret_detection')
+ end.to have_enqueued_email(user, pat.name, 'secret_detection', mail: "access_token_revoked_email")
end
context 'when user is not allowed to receive notifications' do
diff --git a/spec/services/packages/debian/process_changes_service_spec.rb b/spec/services/packages/debian/process_changes_service_spec.rb
index a45dd68cd6e..27b49a13d52 100644
--- a/spec/services/packages/debian/process_changes_service_spec.rb
+++ b/spec/services/packages/debian/process_changes_service_spec.rb
@@ -42,7 +42,7 @@ RSpec.describe Packages::Debian::ProcessChangesService do
end
context 'marked as pending_destruction' do
- it 'creates a package' do
+ it 'does not re-use the existing package' do
existing_package.pending_destruction!
expect { subject.execute }
@@ -73,7 +73,7 @@ RSpec.describe Packages::Debian::ProcessChangesService do
end
end
- it 'remove the package file', :aggregate_failures do
+ it 're-raise error', :aggregate_failures do
expect(::Packages::Debian::GenerateDistributionWorker).not_to receive(:perform_async)
expect { subject.execute }
.to not_change { Packages::Package.count }
diff --git a/spec/services/packages/debian/process_package_file_service_spec.rb b/spec/services/packages/debian/process_package_file_service_spec.rb
new file mode 100644
index 00000000000..571861f42cf
--- /dev/null
+++ b/spec/services/packages/debian/process_package_file_service_spec.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::Debian::ProcessPackageFileService do
+ describe '#execute' do
+ let_it_be(:user) { create(:user) }
+ let_it_be_with_reload(:distribution) { create(:debian_project_distribution, :with_file, codename: 'unstable') }
+
+ let!(:incoming) { create(:debian_incoming, project: distribution.project) }
+
+ let(:distribution_name) { distribution.codename }
+ let(:debian_file_metadatum) { package_file.debian_file_metadatum }
+
+ subject { described_class.new(package_file, user, distribution_name, component_name) }
+
+ RSpec.shared_context 'with Debian package file' do |file_name|
+ let(:package_file) { incoming.package_files.with_file_name(file_name).first }
+ end
+
+ using RSpec::Parameterized::TableSyntax
+
+ where(:case_name, :expected_file_type, :file_name, :component_name) do
+ 'with a deb' | 'deb' | 'libsample0_1.2.3~alpha2_amd64.deb' | 'main'
+ 'with an udeb' | 'udeb' | 'sample-udeb_1.2.3~alpha2_amd64.udeb' | 'contrib'
+ end
+
+ with_them do
+ include_context 'with Debian package file', params[:file_name] do
+ it 'creates package and updates package file', :aggregate_failures do
+ expect(::Packages::Debian::GenerateDistributionWorker)
+ .to receive(:perform_async).with(:project, distribution.id)
+ expect { subject.execute }
+ .to change(Packages::Package, :count).from(1).to(2)
+ .and not_change(Packages::PackageFile, :count)
+ .and change(incoming.package_files, :count).from(7).to(6)
+ .and change(debian_file_metadatum, :file_type).from('unknown').to(expected_file_type)
+ .and change(debian_file_metadatum, :component).from(nil).to(component_name)
+
+ created_package = Packages::Package.last
+ expect(created_package.name).to eq 'sample'
+ expect(created_package.version).to eq '1.2.3~alpha2'
+ expect(created_package.creator).to eq user
+ end
+
+ context 'with existing package' do
+ let_it_be_with_reload(:existing_package) do
+ create(:debian_package, name: 'sample', version: '1.2.3~alpha2', project: distribution.project)
+ end
+
+ before do
+ existing_package.update!(debian_distribution: distribution)
+ end
+
+ it 'does not create a package and assigns the package_file to the existing package' do
+ expect(::Packages::Debian::GenerateDistributionWorker)
+ .to receive(:perform_async).with(:project, distribution.id)
+ expect { subject.execute }
+ .to not_change(Packages::Package, :count)
+ .and not_change(Packages::PackageFile, :count)
+ .and change(incoming.package_files, :count).from(7).to(6)
+ .and change(package_file, :package).from(incoming).to(existing_package)
+ .and change(debian_file_metadatum, :file_type).from('unknown').to(expected_file_type.to_s)
+ .and change(debian_file_metadatum, :component).from(nil).to(component_name)
+ end
+
+ context 'when marked as pending_destruction' do
+ it 'does not re-use the existing package' do
+ existing_package.pending_destruction!
+
+ expect { subject.execute }
+ .to change(Packages::Package, :count).by(1)
+ .and not_change(Packages::PackageFile, :count)
+ end
+ end
+ end
+ end
+ end
+
+ context 'without a distribution' do
+ let(:package_file) { incoming.package_files.with_file_name('libsample0_1.2.3~alpha2_amd64.deb').first }
+ let(:component_name) { 'main' }
+
+ before do
+ distribution.destroy!
+ end
+
+ it 'raise ActiveRecord::RecordNotFound', :aggregate_failures do
+ expect(::Packages::Debian::GenerateDistributionWorker).not_to receive(:perform_async)
+ expect { subject.execute }
+ .to not_change(Packages::Package, :count)
+ .and not_change(Packages::PackageFile, :count)
+ .and not_change(incoming.package_files, :count)
+ .and raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ context 'with package file without Debian metadata' do
+ let!(:package_file) { create(:debian_package_file, without_loaded_metadatum: true) }
+ let(:component_name) { 'main' }
+
+ it 'raise ArgumentError', :aggregate_failures do
+ expect(::Packages::Debian::GenerateDistributionWorker).not_to receive(:perform_async)
+ expect { subject.execute }
+ .to not_change(Packages::Package, :count)
+ .and not_change(Packages::PackageFile, :count)
+ .and not_change(incoming.package_files, :count)
+ .and raise_error(ArgumentError, 'package file without Debian metadata')
+ end
+ end
+
+ context 'with already processed package file' do
+ let_it_be(:package_file) { create(:debian_package_file) }
+
+ let(:component_name) { 'main' }
+
+ it 'raise ArgumentError', :aggregate_failures do
+ expect(::Packages::Debian::GenerateDistributionWorker).not_to receive(:perform_async)
+ expect { subject.execute }
+ .to not_change(Packages::Package, :count)
+ .and not_change(Packages::PackageFile, :count)
+ .and not_change(incoming.package_files, :count)
+ .and raise_error(ArgumentError, 'already processed package file')
+ end
+ end
+
+ context 'with invalid package file type' do
+ let(:package_file) { incoming.package_files.with_file_name('sample_1.2.3~alpha2.tar.xz').first }
+ let(:component_name) { 'main' }
+
+ it 'raise ArgumentError', :aggregate_failures do
+ expect(::Packages::Debian::GenerateDistributionWorker).not_to receive(:perform_async)
+ expect { subject.execute }
+ .to not_change(Packages::Package, :count)
+ .and not_change(Packages::PackageFile, :count)
+ .and not_change(incoming.package_files, :count)
+ .and raise_error(ArgumentError, 'invalid package file type: source')
+ end
+ end
+
+ context 'when creating package fails' do
+ let(:package_file) { incoming.package_files.with_file_name('libsample0_1.2.3~alpha2_amd64.deb').first }
+ let(:component_name) { 'main' }
+
+ before do
+ allow_next_instance_of(::Packages::Debian::FindOrCreatePackageService) do |find_or_create_package_service|
+ allow(find_or_create_package_service)
+ .to receive(:execute).and_raise(ActiveRecord::ConnectionTimeoutError, 'connect timeout')
+ end
+ end
+
+ it 're-raise error', :aggregate_failures do
+ expect(::Packages::Debian::GenerateDistributionWorker).not_to receive(:perform_async)
+ expect { subject.execute }
+ .to not_change(Packages::Package, :count)
+ .and not_change(Packages::PackageFile, :count)
+ .and not_change(incoming.package_files, :count)
+ .and raise_error(ActiveRecord::ConnectionTimeoutError, 'connect timeout')
+ end
+ end
+ end
+end
diff --git a/spec/services/pages_domains/retry_acme_order_service_spec.rb b/spec/services/pages_domains/retry_acme_order_service_spec.rb
index 601de24e766..3152e05f2f1 100644
--- a/spec/services/pages_domains/retry_acme_order_service_spec.rb
+++ b/spec/services/pages_domains/retry_acme_order_service_spec.rb
@@ -2,21 +2,37 @@
require 'spec_helper'
-RSpec.describe PagesDomains::RetryAcmeOrderService do
- let(:domain) { create(:pages_domain, auto_ssl_enabled: true, auto_ssl_failed: true) }
+RSpec.describe PagesDomains::RetryAcmeOrderService, feature_category: :pages do
+ let_it_be(:project) { create(:project) }
+
+ let(:domain) { create(:pages_domain, project: project, auto_ssl_enabled: true, auto_ssl_failed: true) }
let(:service) { described_class.new(domain) }
it 'clears auto_ssl_failed' do
- expect do
- service.execute
- end.to change { domain.auto_ssl_failed }.from(true).to(false)
+ expect { service.execute }
+ .to change { domain.auto_ssl_failed }
+ .from(true).to(false)
+ .and publish_event(PagesDomains::PagesDomainUpdatedEvent)
+ .with(
+ project_id: project.id,
+ namespace_id: project.namespace.id,
+ root_namespace_id: project.root_namespace.id,
+ domain: domain.domain
+ )
end
- it 'schedules renewal worker' do
+ it 'schedules renewal worker and publish PagesDomainUpdatedEvent event' do
expect(PagesDomainSslRenewalWorker).to receive(:perform_async).with(domain.id).and_return(nil).once
- service.execute
+ expect { service.execute }
+ .to publish_event(PagesDomains::PagesDomainUpdatedEvent)
+ .with(
+ project_id: project.id,
+ namespace_id: project.namespace.id,
+ root_namespace_id: project.root_namespace.id,
+ domain: domain.domain
+ )
end
it "doesn't schedule renewal worker if Let's Encrypt integration is not enabled" do
@@ -24,7 +40,8 @@ RSpec.describe PagesDomains::RetryAcmeOrderService do
expect(PagesDomainSslRenewalWorker).not_to receive(:new)
- service.execute
+ expect { service.execute }
+ .to not_publish_event(PagesDomains::PagesDomainUpdatedEvent)
end
it "doesn't schedule renewal worker if auto ssl has not failed yet" do
@@ -32,6 +49,7 @@ RSpec.describe PagesDomains::RetryAcmeOrderService do
expect(PagesDomainSslRenewalWorker).not_to receive(:new)
- service.execute
+ expect { service.execute }
+ .to not_publish_event(PagesDomains::PagesDomainUpdatedEvent)
end
end
diff --git a/spec/services/personal_access_tokens/revoke_service_spec.rb b/spec/services/personal_access_tokens/revoke_service_spec.rb
index f16b6f00a0a..562d6017405 100644
--- a/spec/services/personal_access_tokens/revoke_service_spec.rb
+++ b/spec/services/personal_access_tokens/revoke_service_spec.rb
@@ -8,7 +8,12 @@ RSpec.describe PersonalAccessTokens::RevokeService do
it { expect(service.token.revoked?).to be true }
it 'logs the event' do
- expect(Gitlab::AppLogger).to receive(:info).with(/PAT REVOCATION: revoked_by: '#{current_user.username}', revoked_for: '#{token.user.username}', token_id: '\d+'/)
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ class: described_class.to_s,
+ message: 'PAT Revoked',
+ revoked_by: revoked_by,
+ revoked_for: token.user.username,
+ token_id: token.id)
subject
end
@@ -29,7 +34,9 @@ RSpec.describe PersonalAccessTokens::RevokeService do
let_it_be(:current_user) { create(:admin) }
let_it_be(:token) { create(:personal_access_token) }
- it_behaves_like 'a successfully revoked token'
+ it_behaves_like 'a successfully revoked token' do
+ let(:revoked_by) { current_user.username }
+ end
end
context 'when admin mode is disabled' do
@@ -52,7 +59,38 @@ RSpec.describe PersonalAccessTokens::RevokeService do
context 'token belongs to current_user' do
let_it_be(:token) { create(:personal_access_token, user: current_user) }
- it_behaves_like 'a successfully revoked token'
+ it_behaves_like 'a successfully revoked token' do
+ let(:revoked_by) { current_user.username }
+ end
+ end
+ end
+
+ context 'when source' do
+ let(:service) { described_class.new(nil, token: token, source: source) }
+
+ let_it_be(:current_user) { nil }
+
+ context 'when source is valid' do
+ let_it_be(:source) { 'secret_detection' }
+ let_it_be(:token) { create(:personal_access_token) }
+
+ it_behaves_like 'a successfully revoked token' do
+ let(:revoked_by) { 'secret_detection' }
+ end
+ end
+
+ context 'when source is invalid' do
+ let_it_be(:source) { 'external_request' }
+ let_it_be(:token) { create(:personal_access_token) }
+
+ it_behaves_like 'an unsuccessfully revoked token'
+ end
+
+ context 'when source is missing' do
+ let_it_be(:source) { nil }
+ let_it_be(:token) { create(:personal_access_token) }
+
+ it_behaves_like 'an unsuccessfully revoked token'
end
end
end
diff --git a/spec/services/projects/after_rename_service_spec.rb b/spec/services/projects/after_rename_service_spec.rb
index edf4bbe0f7f..72bb0adbf56 100644
--- a/spec/services/projects/after_rename_service_spec.rb
+++ b/spec/services/projects/after_rename_service_spec.rb
@@ -9,6 +9,16 @@ RSpec.describe Projects::AfterRenameService do
let!(:full_path_before_rename) { project.full_path }
let!(:path_after_rename) { "#{project.path}-renamed" }
let!(:full_path_after_rename) { "#{project.full_path}-renamed" }
+ let!(:repo_before_rename) { project.repository.raw }
+ let!(:wiki_repo_before_rename) { project.wiki.repository.raw }
+
+ let(:repo_after_rename) do
+ Gitlab::Git::Repository.new(project.repository_storage, "#{full_path_after_rename}.git", nil, nil)
+ end
+
+ let(:wiki_repo_after_rename) do
+ Gitlab::Git::Repository.new(project.repository_storage, "#{full_path_after_rename}.wiki.git", nil, nil)
+ end
describe '#execute' do
context 'using legacy storage' do
@@ -35,13 +45,15 @@ RSpec.describe Projects::AfterRenameService do
.to receive(:rename_project)
.with(path_before_rename, path_after_rename, project.namespace.full_path)
- expect_repository_exist("#{full_path_before_rename}.git")
- expect_repository_exist("#{full_path_before_rename}.wiki.git")
+ expect(repo_before_rename).to exist
+ expect(wiki_repo_before_rename).to exist
service_execute
- expect_repository_exist("#{full_path_after_rename}.git")
- expect_repository_exist("#{full_path_after_rename}.wiki.git")
+ expect(repo_before_rename).not_to exist
+ expect(wiki_repo_before_rename).not_to exist
+ expect(repo_after_rename).to exist
+ expect(wiki_repo_after_rename).to exist
end
context 'container registry with images' do
@@ -212,13 +224,4 @@ RSpec.describe Projects::AfterRenameService do
described_class.new(project, path_before: path_before_rename, full_path_before: full_path_before_rename).execute
end
-
- def expect_repository_exist(full_path_with_extension)
- expect(
- TestEnv.storage_dir_exists?(
- project.repository_storage,
- full_path_with_extension
- )
- ).to be_truthy
- end
end
diff --git a/spec/services/projects/container_repository/destroy_service_spec.rb b/spec/services/projects/container_repository/destroy_service_spec.rb
index 20e75d94e05..0ec0aecaa04 100644
--- a/spec/services/projects/container_repository/destroy_service_spec.rb
+++ b/spec/services/projects/container_repository/destroy_service_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe Projects::ContainerRepository::DestroyService do
let!(:repository) { create(:container_repository, :root, project: project) }
it 'does not delete a repository' do
- expect { subject.execute(repository) }.not_to change { ContainerRepository.all.count }
+ expect { subject.execute(repository) }.not_to change { ContainerRepository.count }
end
end
@@ -29,23 +29,62 @@ RSpec.describe Projects::ContainerRepository::DestroyService do
let!(:repository) { create(:container_repository, :root, project: project) }
before do
- stub_container_registry_tags(repository: :any, tags: [])
+ stub_container_registry_tags(repository: :any, tags: %w[latest stable])
end
it 'deletes the repository' do
- expect(repository).to receive(:delete_tags!).and_call_original
- expect { described_class.new(project, user).execute(repository) }.to change { ContainerRepository.all.count }.by(-1)
+ expect_cleanup_tags_service_with(container_repository: repository, return_status: :success)
+ expect { subject.execute(repository) }.to change { ContainerRepository.count }.by(-1)
end
- context 'when destroy fails' do
- it 'set delete_status' do
+ it 'sends disable_timeout = true as part of the params as default' do
+ expect_cleanup_tags_service_with(container_repository: repository, return_status: :success, disable_timeout: true)
+ expect { subject.execute(repository) }.to change { ContainerRepository.count }.by(-1)
+ end
+
+ it 'sends disable_timeout = false as part of the params if it is set to false' do
+ expect_cleanup_tags_service_with(container_repository: repository, return_status: :success, disable_timeout: false)
+ expect { subject.execute(repository, disable_timeout: false) }.to change { ContainerRepository.count }.by(-1)
+ end
+
+ context 'when deleting the tags fails' do
+ it 'sets status as deleted_failed' do
+ expect_cleanup_tags_service_with(container_repository: repository, return_status: :error)
+ allow(Gitlab::AppLogger).to receive(:error).and_call_original
+
+ subject.execute(repository)
+
+ expect(repository).to be_delete_failed
+ expect(Gitlab::AppLogger).to have_received(:error)
+ .with("Container repository with ID: #{repository.id} and path: #{repository.path} failed with message: error in deleting tags")
+ end
+ end
+
+ context 'when destroying the repository fails' do
+ it 'sets status as deleted_failed' do
+ expect_cleanup_tags_service_with(container_repository: repository, return_status: :success)
allow(repository).to receive(:destroy).and_return(false)
+ allow(repository.errors).to receive(:full_messages).and_return(['Error 1', 'Error 2'])
+ allow(Gitlab::AppLogger).to receive(:error).and_call_original
subject.execute(repository)
expect(repository).to be_delete_failed
+ expect(Gitlab::AppLogger).to have_received(:error)
+ .with("Container repository with ID: #{repository.id} and path: #{repository.path} failed with message: Error 1. Error 2")
end
end
+
+ def expect_cleanup_tags_service_with(container_repository:, return_status:, disable_timeout: true)
+ delete_tags_service = instance_double(Projects::ContainerRepository::CleanupTagsService)
+
+ expect(Projects::ContainerRepository::CleanupTagsService).to receive(:new).with(
+ container_repository: container_repository,
+ params: described_class::CLEANUP_TAGS_SERVICE_PARAMS.merge('disable_timeout' => disable_timeout)
+ ).and_return(delete_tags_service)
+
+ expect(delete_tags_service).to receive(:execute).and_return(status: return_status)
+ end
end
end
end
diff --git a/spec/services/projects/container_repository/gitlab/cleanup_tags_service_spec.rb b/spec/services/projects/container_repository/gitlab/cleanup_tags_service_spec.rb
index 59827ea035e..b06a5709bd5 100644
--- a/spec/services/projects/container_repository/gitlab/cleanup_tags_service_spec.rb
+++ b/spec/services/projects/container_repository/gitlab/cleanup_tags_service_spec.rb
@@ -49,6 +49,9 @@ RSpec.describe Projects::ContainerRepository::Gitlab::CleanupTagsService do
it_behaves_like 'when regex matching everything is specified',
delete_expectations: [%w[A], %w[Ba Bb], %w[C D], %w[E]]
+ it_behaves_like 'when regex matching everything is specified and latest is not kept',
+ delete_expectations: [%w[latest A], %w[Ba Bb], %w[C D], %w[E]]
+
it_behaves_like 'when delete regex matching specific tags is used'
it_behaves_like 'when delete regex matching specific tags is used with overriding allow regex'
@@ -97,6 +100,19 @@ RSpec.describe Projects::ContainerRepository::Gitlab::CleanupTagsService do
is_expected.to eq(response)
end
+
+ context 'when disable_timeout is set to true' do
+ let(:params) do
+ { 'name_regex_delete' => '.*', 'disable_timeout' => true }
+ end
+
+ it 'does not check if it timed out' do
+ expect(service).not_to receive(:timeout?)
+ end
+
+ it_behaves_like 'when regex matching everything is specified',
+ delete_expectations: [%w[A], %w[Ba Bb], %w[C D], %w[E]]
+ end
end
end
diff --git a/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb b/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb
index 8d8907119f0..f03912dba80 100644
--- a/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb
+++ b/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb
@@ -24,6 +24,10 @@ RSpec.describe Projects::ContainerRepository::Gitlab::DeleteTagsService do
context 'with tags to delete' do
let(:timeout) { 10 }
+ before do
+ stub_application_setting(container_registry_delete_tags_service_timeout: timeout)
+ end
+
it_behaves_like 'deleting tags'
it 'succeeds when tag delete returns 404' do
@@ -48,10 +52,6 @@ RSpec.describe Projects::ContainerRepository::Gitlab::DeleteTagsService do
end
end
- before do
- stub_application_setting(container_registry_delete_tags_service_timeout: timeout)
- end
-
context 'with timeout' do
context 'set to a valid value' do
before do
diff --git a/spec/services/projects/container_repository/third_party/cleanup_tags_service_spec.rb b/spec/services/projects/container_repository/third_party/cleanup_tags_service_spec.rb
index 2d034d577ac..7227834b131 100644
--- a/spec/services/projects/container_repository/third_party/cleanup_tags_service_spec.rb
+++ b/spec/services/projects/container_repository/third_party/cleanup_tags_service_spec.rb
@@ -51,6 +51,16 @@ RSpec.describe Projects::ContainerRepository::ThirdParty::CleanupTagsService, :c
},
supports_caching: true
+ it_behaves_like 'when regex matching everything is specified and latest is not kept',
+ delete_expectations: [%w[A Ba Bb C D E latest]],
+ service_response_extra: {
+ before_truncate_size: 7,
+ after_truncate_size: 7,
+ before_delete_size: 7,
+ cached_tags_count: 0
+ },
+ supports_caching: true
+
it_behaves_like 'when delete regex matching specific tags is used',
service_response_extra: {
before_truncate_size: 2,
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index 9c8aeb5cf7b..f42ab198a04 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -145,6 +145,20 @@ RSpec.describe Projects::CreateService, '#execute' do
end
end
end
+
+ context 'when the passed in namespace is for a bot user' do
+ let(:bot_user) { create(:user, :project_bot) }
+ let(:opts) do
+ { name: project_name, namespace_id: bot_user.namespace.id }
+ end
+
+ it 'raises an error' do
+ project = create_project(bot_user, opts)
+
+ expect(project.errors.errors.length).to eq 1
+ expect(project.errors.messages[:namespace].first).to eq(("is not valid"))
+ end
+ end
end
describe 'after create actions' do
@@ -908,27 +922,6 @@ RSpec.describe Projects::CreateService, '#execute' do
end
end
- it_behaves_like 'measurable service' do
- before do
- opts.merge!(
- current_user: user,
- path: 'foo'
- )
- end
-
- let(:base_log_data) do
- {
- class: Projects::CreateService.name,
- current_user: user.name,
- project_full_path: "#{user.namespace.full_path}/#{opts[:path]}"
- }
- end
-
- after do
- create_project(user, opts)
- end
- end
-
context 'with specialized project_authorization workers' do
let_it_be(:other_user) { create(:user) }
let_it_be(:group) { create(:group) }
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index f7f02769f6a..ff2de45661f 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publisher do
include ProjectForksHelper
+ include BatchDestroyDependentAssociationsHelper
let_it_be(:user) { create(:user) }
@@ -331,8 +332,8 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
context 'when image repository deletion succeeds' do
it 'removes tags' do
- expect_any_instance_of(ContainerRepository)
- .to receive(:delete_tags!).and_return(true)
+ expect_any_instance_of(Projects::ContainerRepository::CleanupTagsService)
+ .to receive(:execute).and_return({ status: :success })
destroy_project(project, user)
end
@@ -340,8 +341,8 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
context 'when image repository deletion fails' do
it 'raises an exception' do
- expect_any_instance_of(ContainerRepository)
- .to receive(:delete_tags!).and_raise(RuntimeError)
+ expect_any_instance_of(Projects::ContainerRepository::CleanupTagsService)
+ .to receive(:execute).and_raise(RuntimeError)
expect(destroy_project(project, user)).to be false
end
@@ -548,6 +549,30 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
end
end
+ context 'associations destoyed in batches' do
+ let!(:merge_request) { create(:merge_request, source_project: project) }
+ let!(:issue) { create(:issue, project: project) }
+ let!(:label) { create(:label, project: project) }
+
+ it 'destroys the associations marked as `dependent: :destroy`, in batches' do
+ query_recorder = ActiveRecord::QueryRecorder.new do
+ destroy_project(project, user, {})
+ end
+
+ expect(project.merge_requests).to be_empty
+ expect(project.issues).to be_empty
+ expect(project.labels).to be_empty
+
+ expected_queries = [
+ delete_in_batches_regexps(:merge_requests, :target_project_id, project, [merge_request]),
+ delete_in_batches_regexps(:issues, :project_id, project, [issue]),
+ delete_in_batches_regexps(:labels, :project_id, project, [label])
+ ].flatten
+
+ expect(query_recorder.log).to include(*expected_queries)
+ end
+ end
+
def destroy_project(project, user, params = {})
described_class.new(project, user, params).public_send(async ? :async_execute : :execute)
end
diff --git a/spec/services/projects/download_service_spec.rb b/spec/services/projects/download_service_spec.rb
index 7d4fce814f5..f158b11a9fa 100644
--- a/spec/services/projects/download_service_spec.rb
+++ b/spec/services/projects/download_service_spec.rb
@@ -21,8 +21,8 @@ RSpec.describe Projects::DownloadService do
context 'for URLs that are on the whitelist' do
before do
# `ssrf_filter` resolves the hostname. See https://github.com/carrierwaveuploader/carrierwave/commit/91714adda998bc9e8decf5b1f5d260d808761304
- stub_request(:get, %r{http://[\d\.]+/rails_sample.jpg}).to_return(body: File.read(Rails.root + 'spec/fixtures/rails_sample.jpg'))
- stub_request(:get, %r{http://[\d\.]+/doc_sample.txt}).to_return(body: File.read(Rails.root + 'spec/fixtures/doc_sample.txt'))
+ stub_request(:get, %r{http://[\d.]+/rails_sample.jpg}).to_return(body: File.read(Rails.root + 'spec/fixtures/rails_sample.jpg'))
+ stub_request(:get, %r{http://[\d.]+/doc_sample.txt}).to_return(body: File.read(Rails.root + 'spec/fixtures/doc_sample.txt'))
end
context 'an image file' do
diff --git a/spec/services/projects/import_export/export_service_spec.rb b/spec/services/projects/import_export/export_service_spec.rb
index 285687505e9..2c1ebe27014 100644
--- a/spec/services/projects/import_export/export_service_spec.rb
+++ b/spec/services/projects/import_export/export_service_spec.rb
@@ -216,24 +216,9 @@ RSpec.describe Projects::ImportExport::ExportService do
it 'fails' do
expected_message =
"User with ID: %s does not have required permissions for Project: %s with ID: %s" %
- [another_user.id, project.name, project.id]
+ [another_user.id, project.name, project.id]
expect { service.execute }.to raise_error(Gitlab::ImportExport::Error).with_message(expected_message)
end
end
-
- it_behaves_like 'measurable service' do
- let(:base_log_data) do
- {
- class: described_class.name,
- current_user: user.name,
- project_full_path: project.full_path,
- file_path: shared.export_path
- }
- end
-
- after do
- service.execute(after_export_strategy)
- end
- end
end
end
diff --git a/spec/services/projects/import_export/parallel_export_service_spec.rb b/spec/services/projects/import_export/parallel_export_service_spec.rb
new file mode 100644
index 00000000000..b9f2867077c
--- /dev/null
+++ b/spec/services/projects/import_export/parallel_export_service_spec.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::ImportExport::ParallelExportService, feature_category: :importers do
+ let_it_be(:user) { create(:user) }
+
+ let(:export_job) { create(:project_export_job) }
+ let(:after_export_strategy) { Gitlab::ImportExport::AfterExportStrategies::DownloadNotificationStrategy.new }
+ let(:project) { export_job.project }
+
+ before do
+ allow_next_instance_of(Gitlab::ImportExport::Project::ExportedRelationsMerger) do |saver|
+ allow(saver).to receive(:save).and_return(true)
+ end
+
+ allow_next_instance_of(Gitlab::ImportExport::VersionSaver) do |saver|
+ allow(saver).to receive(:save).and_return(true)
+ end
+ end
+
+ describe '#execute' do
+ subject(:service) { described_class.new(export_job, user, after_export_strategy) }
+
+ it 'creates a project export archive file' do
+ expect(Gitlab::ImportExport::Saver).to receive(:save)
+ .with(exportable: project, shared: project.import_export_shared)
+
+ service.execute
+ end
+
+ it 'logs export progress' do
+ allow(Gitlab::ImportExport::Saver).to receive(:save).and_return(true)
+
+ logger = service.instance_variable_get(:@logger)
+ messages = [
+ 'Parallel project export started',
+ 'Parallel project export - Gitlab::ImportExport::VersionSaver saver started',
+ 'Parallel project export - Gitlab::ImportExport::Project::ExportedRelationsMerger saver started',
+ 'Parallel project export finished successfully'
+ ]
+ messages.each do |message|
+ expect(logger).to receive(:info).ordered.with(hash_including(message: message))
+ end
+
+ service.execute
+ end
+
+ it 'executes after export stragegy on export success' do
+ allow(Gitlab::ImportExport::Saver).to receive(:save).and_return(true)
+
+ expect(after_export_strategy).to receive(:execute)
+
+ service.execute
+ end
+
+ it 'ensures files are cleaned up' do
+ shared = project.import_export_shared
+ FileUtils.mkdir_p(shared.archive_path)
+ FileUtils.mkdir_p(shared.export_path)
+
+ allow(Gitlab::ImportExport::Saver).to receive(:save).and_raise(StandardError)
+
+ expect { service.execute }.to raise_error(StandardError)
+
+ expect(File.exist?(shared.export_path)).to eq(false)
+ expect(File.exist?(shared.archive_path)).to eq(false)
+ end
+
+ context 'when export fails' do
+ it 'notifies the error to the user' do
+ allow(Gitlab::ImportExport::Saver).to receive(:save).and_return(false)
+
+ allow(project.import_export_shared).to receive(:errors).and_return(['Error'])
+
+ expect_next_instance_of(NotificationService) do |instance|
+ expect(instance).to receive(:project_not_exported).with(project, user, ['Error'])
+ end
+
+ service.execute
+ end
+ end
+
+ context 'when after export stragegy fails' do
+ it 'notifies the error to the user' do
+ allow(Gitlab::ImportExport::Saver).to receive(:save).and_return(true)
+ allow(after_export_strategy).to receive(:execute).and_return(false)
+ allow(project.import_export_shared).to receive(:errors).and_return(['Error'])
+
+ expect_next_instance_of(NotificationService) do |instance|
+ expect(instance).to receive(:project_not_exported).with(project, user, ['Error'])
+ end
+
+ service.execute
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
index b3f8980a7bd..bb11b2e617e 100644
--- a/spec/services/projects/import_service_spec.rb
+++ b/spec/services/projects/import_service_spec.rb
@@ -419,25 +419,5 @@ RSpec.describe Projects::ImportService do
end
end
end
-
- it_behaves_like 'measurable service' do
- let(:base_log_data) do
- {
- class: described_class.name,
- current_user: user.name,
- project_full_path: project.full_path,
- import_type: project.import_type,
- file_path: project.import_source
- }
- end
-
- before do
- project.import_type = 'github'
- end
-
- after do
- subject.execute
- end
- end
end
end
diff --git a/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb
index d472d6493c3..80b3c4d0403 100644
--- a/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb
+++ b/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb
@@ -39,7 +39,7 @@ RSpec.describe Projects::LfsPointers::LfsDownloadLinkListService do
]
end
- subject { described_class.new(project, remote_uri: remote_uri) }
+ subject(:service) { described_class.new(project, remote_uri: remote_uri) }
before do
allow(project).to receive(:lfs_enabled?).and_return(true)
@@ -48,19 +48,24 @@ RSpec.describe Projects::LfsPointers::LfsDownloadLinkListService do
allow(Gitlab::HTTP).to receive(:post).and_return(response)
end
- describe '#execute' do
- let(:download_objects) { subject.execute(new_oids) }
-
+ describe '#each_link' do
it 'retrieves each download link of every non existent lfs object' do
- download_objects.each do |lfs_download_object|
- expect(lfs_download_object.link).to eq "#{import_url}/gitlab-lfs/objects/#{lfs_download_object.oid}"
- end
+ links = []
+ service.each_link(new_oids) { |lfs_download_object| links << lfs_download_object.link }
+
+ expect(links).to contain_exactly(
+ "#{import_url}/gitlab-lfs/objects/oid1",
+ "#{import_url}/gitlab-lfs/objects/oid2"
+ )
end
it 'stores headers' do
- download_objects.each do |lfs_download_object|
- expect(lfs_download_object.headers).to eq(headers)
+ expected_headers = []
+ service.each_link(new_oids) do |lfs_download_object|
+ expected_headers << lfs_download_object.headers
end
+
+ expect(expected_headers).to contain_exactly(headers, headers)
end
context 'when lfs objects size is larger than the batch size' do
@@ -97,10 +102,13 @@ RSpec.describe Projects::LfsPointers::LfsDownloadLinkListService do
stub_successful_request([data[4]])
end
- it 'retreives them in batches' do
- subject.execute(new_oids).each do |lfs_download_object|
+ it 'retrieves them in batches' do
+ checksum = 0
+ service.each_link(new_oids) do |lfs_download_object|
expect(lfs_download_object.link).to eq "#{import_url}/gitlab-lfs/objects/#{lfs_download_object.oid}"
+ checksum += 1
end
+ expect(checksum).to eq new_oids.size
end
end
@@ -127,9 +135,12 @@ RSpec.describe Projects::LfsPointers::LfsDownloadLinkListService do
an_instance_of(error_class), project_id: project.id, batch_size: 5, oids_count: 5
)
- subject.execute(new_oids).each do |lfs_download_object|
+ checksum = 0
+ service.each_link(new_oids) do |lfs_download_object|
expect(lfs_download_object.link).to eq "#{import_url}/gitlab-lfs/objects/#{lfs_download_object.oid}"
+ checksum += 1
end
+ expect(checksum).to eq new_oids.size
end
end
@@ -153,7 +164,7 @@ RSpec.describe Projects::LfsPointers::LfsDownloadLinkListService do
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
an_instance_of(error_class), project_id: project.id, batch_size: 2, oids_count: 5
)
- expect { subject.execute(new_oids) }.to raise_error(described_class::DownloadLinksError)
+ expect { service.each_link(new_oids) }.to raise_error(described_class::DownloadLinksError)
end
end
end
@@ -165,21 +176,23 @@ RSpec.describe Projects::LfsPointers::LfsDownloadLinkListService do
let(:import_url) { 'http://user:password@www.gitlab.com/demo/repo.git' }
it 'adds credentials to the download_link' do
- result = subject.execute(new_oids)
-
- result.each do |lfs_download_object|
+ checksum = 0
+ service.each_link(new_oids) do |lfs_download_object|
expect(lfs_download_object.link.starts_with?('http://user:password@')).to be_truthy
+ checksum += 1
end
+ expect(checksum).to eq new_oids.size
end
end
context 'when lfs_endpoint does not have any credentials' do
it 'does not add any credentials' do
- result = subject.execute(new_oids)
-
- result.each do |lfs_download_object|
+ checksum = 0
+ service.each_link(new_oids) do |lfs_download_object|
expect(lfs_download_object.link.starts_with?('http://user:password@')).to be_falsey
+ checksum += 1
end
+ expect(checksum).to eq new_oids.size
end
end
end
@@ -189,17 +202,18 @@ RSpec.describe Projects::LfsPointers::LfsDownloadLinkListService do
let(:lfs_endpoint) { "#{import_url_with_credentials}/info/lfs/objects/batch" }
it 'downloads without any credentials' do
- result = subject.execute(new_oids)
-
- result.each do |lfs_download_object|
+ checksum = 0
+ service.each_link(new_oids) do |lfs_download_object|
expect(lfs_download_object.link.starts_with?('http://user:password@')).to be_falsey
+ checksum += 1
end
+ expect(checksum).to eq new_oids.size
end
end
end
end
- describe '#get_download_links' do
+ describe '#download_links_for' do
context 'if request fails' do
before do
request_timeout_net_response = Net::HTTPRequestTimeout.new('', '', '')
@@ -208,7 +222,7 @@ RSpec.describe Projects::LfsPointers::LfsDownloadLinkListService do
end
it 'raises an error' do
- expect { subject.send(:get_download_links, new_oids) }.to raise_error(described_class::DownloadLinksError)
+ expect { subject.send(:download_links_for, new_oids) }.to raise_error(described_class::DownloadLinksError)
end
end
@@ -218,7 +232,7 @@ RSpec.describe Projects::LfsPointers::LfsDownloadLinkListService do
allow(response).to receive(:body).and_return(body)
allow(Gitlab::HTTP).to receive(:post).and_return(response)
- expect { subject.send(:get_download_links, new_oids) }.to raise_error(described_class::DownloadLinksError)
+ expect { subject.send(:download_links_for, new_oids) }.to raise_error(described_class::DownloadLinksError)
end
end
diff --git a/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb
index 6c7164c5e06..c815ad38843 100644
--- a/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb
+++ b/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb
@@ -108,7 +108,7 @@ RSpec.describe Projects::LfsPointers::LfsDownloadService do
end
end
- context 'when file download fails' do
+ context 'when file downloading response code is not success' do
before do
allow(Gitlab::HTTP).to receive(:get).and_return(code: 500, 'success?' => false)
end
@@ -122,6 +122,20 @@ RSpec.describe Projects::LfsPointers::LfsDownloadService do
end
end
+ context 'when file downloading request timeout few times' do
+ before do
+ allow(Gitlab::HTTP).to receive(:get).and_raise(Net::OpenTimeout)
+ end
+
+ it_behaves_like 'no lfs object is created'
+
+ it 'retries to get LFS object 3 times before raising exception' do
+ subject.execute
+
+ expect(Gitlab::HTTP).to have_received(:get).exactly(3).times
+ end
+ end
+
context 'when file download returns a redirect' do
let(:redirect_link) { 'http://external-link' }
diff --git a/spec/services/projects/lfs_pointers/lfs_import_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_import_service_spec.rb
index b36b0b8d6b2..32b86ade81e 100644
--- a/spec/services/projects/lfs_pointers/lfs_import_service_spec.rb
+++ b/spec/services/projects/lfs_pointers/lfs_import_service_spec.rb
@@ -5,7 +5,12 @@ RSpec.describe Projects::LfsPointers::LfsImportService do
let(:project) { create(:project) }
let(:user) { project.creator }
let(:import_url) { 'http://www.gitlab.com/demo/repo.git' }
- let(:oid_download_links) { { 'oid1' => "#{import_url}/gitlab-lfs/objects/oid1", 'oid2' => "#{import_url}/gitlab-lfs/objects/oid2" } }
+ let(:oid_download_links) do
+ [
+ { 'oid1' => "#{import_url}/gitlab-lfs/objects/oid1" },
+ { 'oid2' => "#{import_url}/gitlab-lfs/objects/oid2" }
+ ]
+ end
subject { described_class.new(project, user) }
@@ -17,7 +22,8 @@ RSpec.describe Projects::LfsPointers::LfsImportService do
it 'downloads lfs objects' do
service = double
expect_next_instance_of(Projects::LfsPointers::LfsObjectDownloadListService) do |instance|
- expect(instance).to receive(:execute).and_return(oid_download_links)
+ expect(instance).to receive(:each_list_item)
+ .and_yield(oid_download_links[0]).and_yield(oid_download_links[1])
end
expect(Projects::LfsPointers::LfsDownloadService).to receive(:new).and_return(service).twice
expect(service).to receive(:execute).twice
@@ -30,7 +36,7 @@ RSpec.describe Projects::LfsPointers::LfsImportService do
context 'when no downloadable lfs object links' do
it 'does not call LfsDownloadService' do
expect_next_instance_of(Projects::LfsPointers::LfsObjectDownloadListService) do |instance|
- expect(instance).to receive(:execute).and_return({})
+ expect(instance).to receive(:each_list_item)
end
expect(Projects::LfsPointers::LfsDownloadService).not_to receive(:new)
@@ -44,7 +50,7 @@ RSpec.describe Projects::LfsPointers::LfsImportService do
it 'returns error' do
error_message = "error message"
expect_next_instance_of(Projects::LfsPointers::LfsObjectDownloadListService) do |instance|
- expect(instance).to receive(:execute).and_raise(StandardError, error_message)
+ expect(instance).to receive(:each_list_item).and_raise(StandardError, error_message)
end
result = subject.execute
diff --git a/spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb
index adcc2b85706..59eb1ed7a29 100644
--- a/spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb
+++ b/spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb
@@ -9,7 +9,13 @@ RSpec.describe Projects::LfsPointers::LfsObjectDownloadListService do
let!(:lfs_objects_project) { create_list(:lfs_objects_project, 2, project: project) }
let!(:existing_lfs_objects) { LfsObject.pluck(:oid, :size).to_h }
let(:oids) { { 'oid1' => 123, 'oid2' => 125 } }
- let(:oid_download_links) { { 'oid1' => "#{import_url}/gitlab-lfs/objects/oid1", 'oid2' => "#{import_url}/gitlab-lfs/objects/oid2" } }
+ let(:oid_download_links) do
+ [
+ { 'oid1' => "#{import_url}/gitlab-lfs/objects/oid1" },
+ { 'oid2' => "#{import_url}/gitlab-lfs/objects/oid2" }
+ ]
+ end
+
let(:all_oids) { existing_lfs_objects.merge(oids) }
let(:remote_uri) { URI.parse(lfs_endpoint) }
@@ -21,17 +27,24 @@ RSpec.describe Projects::LfsPointers::LfsObjectDownloadListService do
allow_any_instance_of(Projects::LfsPointers::LfsListService).to receive(:execute).and_return(all_oids)
end
- describe '#execute' do
+ describe '#each_list_item' do
context 'when no lfs pointer is linked' do
before do
- allow_any_instance_of(Projects::LfsPointers::LfsDownloadLinkListService).to receive(:execute).and_return(oid_download_links)
- expect(Projects::LfsPointers::LfsDownloadLinkListService).to receive(:new).with(project, remote_uri: URI.parse(default_endpoint)).and_call_original
+ allow_any_instance_of(Projects::LfsPointers::LfsDownloadLinkListService)
+ .to receive(:each_link).with(oids)
+ .and_yield(oid_download_links[0])
+ .and_yield(oid_download_links[1])
end
it 'retrieves all lfs pointers in the project repository' do
+ expect(Projects::LfsPointers::LfsDownloadLinkListService)
+ .to receive(:new).with(project, remote_uri: URI.parse(default_endpoint))
+ .and_call_original
expect_any_instance_of(Projects::LfsPointers::LfsListService).to receive(:execute)
- subject.execute
+ checksum = 0
+ subject.each_list_item { |lfs_object| checksum += 1 }
+ expect(checksum).to eq 2
end
context 'when no LFS objects exist' do
@@ -40,17 +53,23 @@ RSpec.describe Projects::LfsPointers::LfsObjectDownloadListService do
end
it 'retrieves all LFS objects' do
- expect_any_instance_of(Projects::LfsPointers::LfsDownloadLinkListService).to receive(:execute).with(all_oids)
+ expect(Projects::LfsPointers::LfsDownloadLinkListService)
+ .to receive(:new).with(project, remote_uri: URI.parse(default_endpoint)).and_call_original
+ expect_any_instance_of(Projects::LfsPointers::LfsDownloadLinkListService)
+ .to receive(:each_link).with(all_oids)
- subject.execute
+ subject.each_list_item {}
end
end
context 'when some LFS objects already exist' do
it 'retrieves the download links of non-existent objects' do
- expect_any_instance_of(Projects::LfsPointers::LfsDownloadLinkListService).to receive(:execute).with(oids)
+ expect_any_instance_of(Projects::LfsPointers::LfsDownloadLinkListService)
+ .to receive(:each_link).with(oids)
- subject.execute
+ checksum = 0
+ subject.each_list_item { |lfs_object| checksum += 1 }
+ expect(checksum).to eq 2
end
end
end
@@ -62,16 +81,15 @@ RSpec.describe Projects::LfsPointers::LfsObjectDownloadListService do
context 'when url points to the same import url host' do
let(:lfs_endpoint) { "#{import_url}/different_endpoint" }
- let(:service) { double }
-
- before do
- allow(service).to receive(:execute)
- end
+ let(:service) { instance_double(Projects::LfsPointers::LfsDownloadLinkListService, each_link: nil) }
it 'downloads lfs object using the new endpoint' do
- expect(Projects::LfsPointers::LfsDownloadLinkListService).to receive(:new).with(project, remote_uri: remote_uri).and_return(service)
+ expect(Projects::LfsPointers::LfsDownloadLinkListService)
+ .to receive(:new)
+ .with(project, remote_uri: remote_uri)
+ .and_return(service)
- subject.execute
+ subject.each_list_item {}
end
context 'when import url has credentials' do
@@ -79,10 +97,14 @@ RSpec.describe Projects::LfsPointers::LfsObjectDownloadListService do
it 'adds the credentials to the new endpoint' do
expect(Projects::LfsPointers::LfsDownloadLinkListService)
- .to receive(:new).with(project, remote_uri: URI.parse("http://user:password@www.gitlab.com/demo/repo.git/different_endpoint"))
+ .to receive(:new)
+ .with(
+ project,
+ remote_uri: URI.parse("http://user:password@www.gitlab.com/demo/repo.git/different_endpoint")
+ )
.and_return(service)
- subject.execute
+ subject.each_list_item {}
end
context 'when url has its own credentials' do
@@ -93,7 +115,7 @@ RSpec.describe Projects::LfsPointers::LfsObjectDownloadListService do
.to receive(:new).with(project, remote_uri: remote_uri)
.and_return(service)
- subject.execute
+ subject.each_list_item {}
end
end
end
@@ -105,7 +127,7 @@ RSpec.describe Projects::LfsPointers::LfsObjectDownloadListService do
it 'disables lfs from the project' do
expect(project.lfs_enabled?).to be_truthy
- subject.execute
+ subject.each_list_item {}
expect(project.lfs_enabled?).to be_falsey
end
@@ -113,7 +135,7 @@ RSpec.describe Projects::LfsPointers::LfsObjectDownloadListService do
it 'does not download anything' do
expect_any_instance_of(Projects::LfsPointers::LfsListService).not_to receive(:execute)
- subject.execute
+ subject.each_list_item {}
end
end
end
diff --git a/spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb b/spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb
index 6a715312097..a3cff345f68 100644
--- a/spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb
+++ b/spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb
@@ -28,6 +28,7 @@ RSpec.describe Projects::RefreshBuildArtifactsSizeStatisticsService, :clean_gitl
end
let(:now) { Time.zone.now }
+ let(:statistics) { project.statistics }
around do |example|
freeze_time { example.run }
@@ -45,7 +46,7 @@ RSpec.describe Projects::RefreshBuildArtifactsSizeStatisticsService, :clean_gitl
end
it 'increments the counter attribute by the total size of the current batch of artifacts' do
- expect { service.execute }.to change { project.statistics.get_counter_value(:build_artifacts_size) }.to(3)
+ expect { service.execute }.to change { statistics.counter(:build_artifacts_size).get }.to(3)
end
it 'updates the last_job_artifact_id to the ID of the last artifact from the batch' do
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 8f505c31c5a..4d75786a4c3 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -263,7 +263,7 @@ RSpec.describe Projects::TransferService do
end
context 'when transfer fails' do
- let!(:original_path) { project_path(project) }
+ let!(:original_path) { project.repository.relative_path }
def attempt_project_transfer(&block)
expect do
@@ -277,21 +277,11 @@ RSpec.describe Projects::TransferService do
expect_any_instance_of(Labels::TransferService).to receive(:execute).and_raise(ActiveRecord::StatementInvalid, "PG ERROR")
end
- def project_path(project)
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.repository.path_to_repo
- end
- end
-
- def current_path
- project_path(project)
- end
-
it 'rolls back repo location' do
attempt_project_transfer
expect(project.repository.raw.exists?).to be(true)
- expect(original_path).to eq current_path
+ expect(original_path).to eq project.repository.relative_path
end
it 'rolls back project full path in gitaly' do
@@ -462,6 +452,22 @@ RSpec.describe Projects::TransferService do
end
end
+ context 'target namespace belongs to bot user', :enable_admin_mode do
+ let(:bot) { create(:user, :project_bot) }
+ let(:target) { bot.namespace }
+ let(:executor) { create(:user, :admin) }
+
+ it 'does not allow project transfer' do
+ namespace = project.namespace
+
+ transfer_result = execute_transfer
+
+ expect(transfer_result).to eq false
+ expect(project.namespace).to eq(namespace)
+ expect(project.errors[:new_namespace]).to include("You don't have permission to transfer projects into that namespace.")
+ end
+ end
+
context 'when user does not own the project' do
let(:project) { create(:project, :repository, :legacy_storage) }
diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb
index a69db3b9970..d908a169898 100644
--- a/spec/services/projects/update_pages_service_spec.rb
+++ b/spec/services/projects/update_pages_service_spec.rb
@@ -320,10 +320,11 @@ RSpec.describe Projects::UpdatePagesService do
end
context 'when retrying the job' do
+ let(:stage) { create(:ci_stage, position: 1_000_000, name: 'deploy', pipeline: pipeline) }
let!(:older_deploy_job) do
create(:generic_commit_status, :failed, pipeline: pipeline,
ref: build.ref,
- stage: 'deploy',
+ ci_stage: stage,
name: 'pages:deploy')
end
@@ -337,13 +338,15 @@ RSpec.describe Projects::UpdatePagesService do
expect(execute).to eq(:success)
expect(older_deploy_job.reload).to be_retried
+ expect(deploy_status.ci_stage).to eq(stage)
+ expect(deploy_status.stage_idx).to eq(stage.position)
end
end
private
def deploy_status
- GenericCommitStatus.find_by(name: 'pages:deploy')
+ GenericCommitStatus.where(name: 'pages:deploy').last
end
def execute
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index 7d8951bf111..3cda6bc2627 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-
require 'spec_helper'
RSpec.describe Projects::UpdateService do
@@ -303,6 +302,25 @@ RSpec.describe Projects::UpdateService do
expect(project.default_branch).to eq 'master'
expect(project.previous_default_branch).to be_nil
end
+
+ context 'when repository has an ambiguous branch named "HEAD"' do
+ before do
+ allow(project.repository.raw).to receive(:write_ref).and_return(false)
+ allow(project.repository).to receive(:branch_names) { %w[fix master main HEAD] }
+ end
+
+ it 'returns an error to the user' do
+ result = update_project(project, admin, default_branch: 'fix')
+
+ expect(result).to include(status: :error)
+ expect(result[:message]).to include("Could not set the default branch. Do you have a branch named 'HEAD' in your repository?")
+
+ project.reload
+
+ expect(project.default_branch).to eq 'master'
+ expect(project.previous_default_branch).to be_nil
+ end
+ end
end
context 'when we update project but not enabling a wiki' do
@@ -422,25 +440,6 @@ RSpec.describe Projects::UpdateService do
expect(feature.feature_flags_access_level).not_to eq(ProjectFeature::DISABLED)
expect(feature.environments_access_level).not_to eq(ProjectFeature::DISABLED)
end
-
- context 'when split_operations_visibility_permissions feature is disabled' do
- before do
- stub_feature_flags(split_operations_visibility_permissions: false)
- end
-
- it 'syncs the changes to the related fields' do
- result = update_project(project, user, project_feature_attributes: feature_params)
-
- expect(result).to eq({ status: :success })
- feature = project.project_feature
-
- expect(feature.operations_access_level).to eq(ProjectFeature::DISABLED)
- expect(feature.monitor_access_level).to eq(ProjectFeature::DISABLED)
- expect(feature.infrastructure_access_level).to eq(ProjectFeature::DISABLED)
- expect(feature.feature_flags_access_level).to eq(ProjectFeature::DISABLED)
- expect(feature.environments_access_level).to eq(ProjectFeature::DISABLED)
- end
- end
end
context 'when updating a project that contains container images' do
diff --git a/spec/services/protected_branches/api_service_spec.rb b/spec/services/protected_branches/api_service_spec.rb
index 94484f5a7b9..c98e253267b 100644
--- a/spec/services/protected_branches/api_service_spec.rb
+++ b/spec/services/protected_branches/api_service_spec.rb
@@ -3,32 +3,55 @@
require 'spec_helper'
RSpec.describe ProtectedBranches::ApiService do
- let_it_be(:project) { create(:project) }
- let_it_be(:user) { create(:user, maintainer_projects: [project]) }
-
- it 'creates a protected branch with prefilled defaults' do
- expect(::ProtectedBranches::CreateService).to receive(:new).with(
- project, user, hash_including(
- push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
- merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }]
- )
- ).and_call_original
-
- expect(described_class.new(project, user, { name: 'new name' }).create).to be_valid
+ shared_examples 'execute with entity' do
+ it 'creates a protected branch with prefilled defaults' do
+ expect(::ProtectedBranches::CreateService).to receive(:new).with(
+ entity, user, hash_including(
+ push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
+ merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }]
+ )
+ ).and_call_original
+
+ expect(described_class.new(entity, user, { name: 'new name' }).create).to be_valid
+ end
+
+ it 'updates a protected branch without prefilled defaults' do
+ expect(::ProtectedBranches::UpdateService).to receive(:new).with(
+ entity, user, hash_including(
+ push_access_levels_attributes: [],
+ merge_access_levels_attributes: []
+ )
+ ).and_call_original
+
+ expect do
+ expect(described_class.new(entity, user, { name: 'new name' }).update(protected_branch)).to be_valid
+ end.not_to change { protected_branch.reload.allow_force_push }
+ end
+ end
+
+ context 'with entity project' do
+ let_it_be_with_reload(:entity) { create(:project) }
+ let_it_be_with_reload(:protected_branch) { create(:protected_branch, project: entity, allow_force_push: true) }
+ let(:user) { entity.first_owner }
+
+ it_behaves_like 'execute with entity'
end
- it 'updates a protected branch without prefilled defaults' do
- protected_branch = create(:protected_branch, project: project, allow_force_push: true)
+ context 'with entity group' do
+ let_it_be_with_reload(:entity) { create(:group) }
+ let_it_be_with_reload(:user) { create(:user) }
+ let_it_be_with_reload(:protected_branch) do
+ create(:protected_branch, group: entity, project: nil, allow_force_push: true)
+ end
- expect(::ProtectedBranches::UpdateService).to receive(:new).with(
- project, user, hash_including(
- push_access_levels_attributes: [],
- merge_access_levels_attributes: []
- )
- ).and_call_original
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :update_protected_branch, protected_branch).and_return(true)
+ allow(Ability)
+ .to receive(:allowed?)
+ .with(user, :create_protected_branch, instance_of(ProtectedBranch))
+ .and_return(true)
+ end
- expect do
- expect(described_class.new(project, user, { name: 'new name' }).update(protected_branch)).to be_valid
- end.not_to change { protected_branch.reload.allow_force_push }
+ it_behaves_like 'execute with entity'
end
end
diff --git a/spec/services/protected_branches/cache_service_spec.rb b/spec/services/protected_branches/cache_service_spec.rb
index d7a3258160b..ea434922661 100644
--- a/spec/services/protected_branches/cache_service_spec.rb
+++ b/spec/services/protected_branches/cache_service_spec.rb
@@ -4,122 +4,150 @@
require 'spec_helper'
RSpec.describe ProtectedBranches::CacheService, :clean_gitlab_redis_cache do
- subject(:service) { described_class.new(project, user) }
+ shared_examples 'execute with entity' do
+ subject(:service) { described_class.new(entity, user) }
- let_it_be(:project) { create(:project) }
- let_it_be(:user) { project.first_owner }
+ let(:immediate_expiration) { 0 }
- let(:immediate_expiration) { 0 }
-
- describe '#fetch' do
- it 'caches the value' do
- expect(service.fetch('main') { true }).to eq(true)
- expect(service.fetch('not-found') { false }).to eq(false)
-
- # Uses cached values
- expect(service.fetch('main') { false }).to eq(true)
- expect(service.fetch('not-found') { true }).to eq(false)
- end
-
- it 'sets expiry on the key' do
- stub_const("#{described_class.name}::CACHE_EXPIRE_IN", immediate_expiration)
-
- expect(service.fetch('main') { true }).to eq(true)
- expect(service.fetch('not-found') { false }).to eq(false)
-
- expect(service.fetch('main') { false }).to eq(false)
- expect(service.fetch('not-found') { true }).to eq(true)
- end
-
- it 'does not set an expiry on the key after the hash is already created' do
- expect(service.fetch('main') { true }).to eq(true)
+ describe '#fetch' do
+ it 'caches the value' do
+ expect(service.fetch('main') { true }).to eq(true)
+ expect(service.fetch('not-found') { false }).to eq(false)
- stub_const("#{described_class.name}::CACHE_EXPIRE_IN", immediate_expiration)
+ # Uses cached values
+ expect(service.fetch('main') { false }).to eq(true)
+ expect(service.fetch('not-found') { true }).to eq(false)
+ end
- expect(service.fetch('not-found') { false }).to eq(false)
+ it 'sets expiry on the key' do
+ stub_const("#{described_class.name}::CACHE_EXPIRE_IN", immediate_expiration)
- expect(service.fetch('main') { false }).to eq(true)
- expect(service.fetch('not-found') { true }).to eq(false)
- end
+ expect(service.fetch('main') { true }).to eq(true)
+ expect(service.fetch('not-found') { false }).to eq(false)
- context 'when CACHE_LIMIT is exceeded' do
- before do
- stub_const("#{described_class.name}::CACHE_LIMIT", 2)
+ expect(service.fetch('main') { false }).to eq(false)
+ expect(service.fetch('not-found') { true }).to eq(true)
end
- it 'recreates cache' do
+ it 'does not set an expiry on the key after the hash is already created' do
expect(service.fetch('main') { true }).to eq(true)
+
+ stub_const("#{described_class.name}::CACHE_EXPIRE_IN", immediate_expiration)
+
expect(service.fetch('not-found') { false }).to eq(false)
- # Uses cached values
expect(service.fetch('main') { false }).to eq(true)
expect(service.fetch('not-found') { true }).to eq(false)
+ end
- # Overflow
- expect(service.fetch('new-branch') { true }).to eq(true)
+ context 'when CACHE_LIMIT is exceeded' do
+ before do
+ stub_const("#{described_class.name}::CACHE_LIMIT", 2)
+ end
- # Refreshes values
- expect(service.fetch('main') { false }).to eq(false)
- expect(service.fetch('not-found') { true }).to eq(true)
- end
- end
+ it 'recreates cache' do
+ expect(service.fetch('main') { true }).to eq(true)
+ expect(service.fetch('not-found') { false }).to eq(false)
+
+ # Uses cached values
+ expect(service.fetch('main') { false }).to eq(true)
+ expect(service.fetch('not-found') { true }).to eq(false)
- context 'when dry_run is on' do
- it 'does not use cached value' do
- expect(service.fetch('main', dry_run: true) { true }).to eq(true)
- expect(service.fetch('main', dry_run: true) { false }).to eq(false)
+ # Overflow
+ expect(service.fetch('new-branch') { true }).to eq(true)
+
+ # Refreshes values
+ expect(service.fetch('main') { false }).to eq(false)
+ expect(service.fetch('not-found') { true }).to eq(true)
+ end
end
- context 'when cache mismatch' do
- it 'logs an error' do
+ context 'when dry_run is on' do
+ it 'does not use cached value' do
expect(service.fetch('main', dry_run: true) { true }).to eq(true)
+ expect(service.fetch('main', dry_run: true) { false }).to eq(false)
+ end
+
+ context 'when cache mismatch' do
+ it 'logs an error' do
+ expect(service.fetch('main', dry_run: true) { true }).to eq(true)
+
+ expect(Gitlab::AppLogger).to receive(:error).with(
+ {
+ 'class' => described_class.name,
+ 'message' => /Cache mismatch/,
+ 'record_class' => entity.class.name,
+ 'record_id' => entity.id,
+ 'record_path' => entity.full_path
+ }
+ )
+
+ expect(service.fetch('main', dry_run: true) { false }).to eq(false)
+ end
+ end
- expect(Gitlab::AppLogger).to receive(:error).with(
- {
- 'class' => described_class.name,
- 'message' => /Cache mismatch/,
- 'project_id' => project.id,
- 'project_path' => project.full_path
- }
- )
+ context 'when cache matches' do
+ it 'does not log an error' do
+ expect(service.fetch('main', dry_run: true) { true }).to eq(true)
- expect(service.fetch('main', dry_run: true) { false }).to eq(false)
+ expect(Gitlab::AppLogger).not_to receive(:error)
+
+ expect(service.fetch('main', dry_run: true) { true }).to eq(true)
+ end
end
end
+ end
- context 'when cache matches' do
- it 'does not log an error' do
- expect(service.fetch('main', dry_run: true) { true }).to eq(true)
+ describe '#refresh' do
+ it 'clears cached values' do
+ expect(service.fetch('main') { true }).to eq(true)
+ expect(service.fetch('not-found') { false }).to eq(false)
- expect(Gitlab::AppLogger).not_to receive(:error)
+ service.refresh
- expect(service.fetch('main', dry_run: true) { true }).to eq(true)
+ # Recreates cache
+ expect(service.fetch('main') { false }).to eq(false)
+ expect(service.fetch('not-found') { true }).to eq(true)
+ end
+ end
+
+ describe 'metrics' do
+ it 'records hit ratio metrics' do
+ expect_next_instance_of(Gitlab::Cache::Metrics) do |metrics|
+ expect(metrics).to receive(:increment_cache_miss).once
+ expect(metrics).to receive(:increment_cache_hit).exactly(4).times
end
+
+ 5.times { service.fetch('main') { true } }
end
end
end
- describe '#refresh' do
- it 'clears cached values' do
- expect(service.fetch('main') { true }).to eq(true)
- expect(service.fetch('not-found') { false }).to eq(false)
+ context 'with entity project' do
+ let_it_be_with_reload(:entity) { create(:project) }
+ let(:user) { entity.first_owner }
- service.refresh
+ it_behaves_like 'execute with entity'
+ end
+
+ context 'with entity group' do
+ let_it_be_with_reload(:entity) { create(:group) }
+ let_it_be_with_reload(:user) { create(:user) }
- # Recreates cache
- expect(service.fetch('main') { false }).to eq(false)
- expect(service.fetch('not-found') { true }).to eq(true)
+ before do
+ entity.add_owner(user)
end
- end
- describe 'metrics' do
- it 'records hit ratio metrics' do
- expect_next_instance_of(Gitlab::Cache::Metrics) do |metrics|
- expect(metrics).to receive(:increment_cache_miss).once
- expect(metrics).to receive(:increment_cache_hit).exactly(4).times
+ context 'when feature flag enabled' do
+ it_behaves_like 'execute with entity'
+ end
+
+ context 'when feature flag disabled' do
+ before do
+ stub_feature_flags(group_protected_branches: false)
end
- 5.times { service.fetch('main') { true } }
+ it_behaves_like 'execute with entity'
end
end
end
diff --git a/spec/services/protected_branches/create_service_spec.rb b/spec/services/protected_branches/create_service_spec.rb
index b42524e761c..9c8fe769ed8 100644
--- a/spec/services/protected_branches/create_service_spec.rb
+++ b/spec/services/protected_branches/create_service_spec.rb
@@ -3,70 +3,75 @@
require 'spec_helper'
RSpec.describe ProtectedBranches::CreateService do
- let_it_be_with_reload(:project) { create(:project) }
-
- let(:user) { project.first_owner }
- let(:params) do
- {
- name: name,
- merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
- push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }]
- }
- end
-
- subject(:service) { described_class.new(project, user, params) }
-
- describe '#execute' do
- let(:name) { 'master' }
-
- it 'creates a new protected branch' do
- expect { service.execute }.to change(ProtectedBranch, :count).by(1)
- expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
- expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
+ shared_examples 'execute with entity' do
+ let(:params) do
+ {
+ name: name,
+ merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
+ push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }]
+ }
end
- it 'refreshes the cache' do
- expect_next_instance_of(ProtectedBranches::CacheService) do |cache_service|
- expect(cache_service).to receive(:refresh)
- end
+ subject(:service) { described_class.new(entity, user, params) }
- service.execute
- end
-
- context 'when protecting a branch with a name that contains HTML tags' do
- let(:name) { 'foo<b>bar<\b>' }
+ describe '#execute' do
+ let(:name) { 'master' }
it 'creates a new protected branch' do
expect { service.execute }.to change(ProtectedBranch, :count).by(1)
- expect(project.protected_branches.last.name).to eq(name)
+ expect(entity.protected_branches.last.push_access_levels.map(&:access_level)).to match_array([Gitlab::Access::MAINTAINER])
+ expect(entity.protected_branches.last.merge_access_levels.map(&:access_level)).to match_array([Gitlab::Access::MAINTAINER])
end
- end
- context 'when user does not have permission' do
- let(:user) { create(:user) }
+ it 'refreshes the cache' do
+ expect_next_instance_of(ProtectedBranches::CacheService) do |cache_service|
+ expect(cache_service).to receive(:refresh)
+ end
- before do
- project.add_developer(user)
+ service.execute
end
- it 'creates a new protected branch if we skip authorization step' do
- expect { service.execute(skip_authorization: true) }.to change(ProtectedBranch, :count).by(1)
+ context 'when protecting a branch with a name that contains HTML tags' do
+ let(:name) { 'foo<b>bar<\b>' }
+
+ it 'creates a new protected branch' do
+ expect { service.execute }.to change(ProtectedBranch, :count).by(1)
+ expect(entity.protected_branches.last.name).to eq(name)
+ end
end
- it 'raises Gitlab::Access:AccessDeniedError' do
- expect { service.execute }.to raise_error(Gitlab::Access::AccessDeniedError)
+ context 'when a policy restricts rule creation' do
+ it "prevents creation of the protected branch rule" do
+ disallow(:create_protected_branch, an_instance_of(ProtectedBranch))
+
+ expect do
+ service.execute
+ end.to raise_error(Gitlab::Access::AccessDeniedError)
+ end
+
+ it 'creates a new protected branch if we skip authorization step' do
+ expect { service.execute(skip_authorization: true) }.to change(ProtectedBranch, :count).by(1)
+ end
end
end
+ end
- context 'when a policy restricts rule creation' do
- it "prevents creation of the protected branch rule" do
- disallow(:create_protected_branch, an_instance_of(ProtectedBranch))
+ context 'with entity project' do
+ let_it_be_with_reload(:entity) { create(:project) }
+ let(:user) { entity.first_owner }
- expect do
- service.execute
- end.to raise_error(Gitlab::Access::AccessDeniedError)
- end
+ it_behaves_like 'execute with entity'
+ end
+
+ context 'with entity group' do
+ let_it_be_with_reload(:entity) { create(:group) }
+ let_it_be_with_reload(:user) { create(:user) }
+
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :create_protected_branch, instance_of(ProtectedBranch)).and_return(true)
end
+
+ it_behaves_like 'execute with entity'
end
def disallow(ability, protected_branch)
diff --git a/spec/services/protected_branches/destroy_service_spec.rb b/spec/services/protected_branches/destroy_service_spec.rb
index 123deeea005..421d4aae5bb 100644
--- a/spec/services/protected_branches/destroy_service_spec.rb
+++ b/spec/services/protected_branches/destroy_service_spec.rb
@@ -3,37 +3,54 @@
require 'spec_helper'
RSpec.describe ProtectedBranches::DestroyService do
- let_it_be_with_reload(:project) { create(:project) }
+ shared_examples 'execute with entity' do
+ subject(:service) { described_class.new(entity, user) }
- let!(:protected_branch) { create(:protected_branch, project: project) }
- let(:user) { project.first_owner }
+ describe '#execute' do
+ it 'destroys a protected branch' do
+ service.execute(protected_branch)
- subject(:service) { described_class.new(project, user) }
-
- describe '#execute' do
- it 'destroys a protected branch' do
- service.execute(protected_branch)
+ expect(protected_branch).to be_destroyed
+ end
- expect(protected_branch).to be_destroyed
- end
+ it 'refreshes the cache' do
+ expect_next_instance_of(ProtectedBranches::CacheService) do |cache_service|
+ expect(cache_service).to receive(:refresh)
+ end
- it 'refreshes the cache' do
- expect_next_instance_of(ProtectedBranches::CacheService) do |cache_service|
- expect(cache_service).to receive(:refresh)
+ service.execute(protected_branch)
end
- service.execute(protected_branch)
+ context 'when a policy restricts rule deletion' do
+ it "prevents deletion of the protected branch rule" do
+ disallow(:destroy_protected_branch, protected_branch)
+
+ expect do
+ service.execute(protected_branch)
+ end.to raise_error(Gitlab::Access::AccessDeniedError)
+ end
+ end
end
+ end
- context 'when a policy restricts rule deletion' do
- it "prevents deletion of the protected branch rule" do
- disallow(:destroy_protected_branch, protected_branch)
+ context 'with entity project' do
+ let_it_be_with_reload(:entity) { create(:project) }
+ let!(:protected_branch) { create(:protected_branch, project: entity) }
+ let(:user) { entity.first_owner }
- expect do
- service.execute(protected_branch)
- end.to raise_error(Gitlab::Access::AccessDeniedError)
- end
+ it_behaves_like 'execute with entity'
+ end
+
+ context 'with entity group' do
+ let_it_be_with_reload(:entity) { create(:group) }
+ let_it_be_with_reload(:user) { create(:user) }
+ let!(:protected_branch) { create(:protected_branch, group: entity, project: nil) }
+
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :destroy_protected_branch, protected_branch).and_return(true)
end
+
+ it_behaves_like 'execute with entity'
end
def disallow(ability, protected_branch)
diff --git a/spec/services/protected_branches/update_service_spec.rb b/spec/services/protected_branches/update_service_spec.rb
index 2ff6c3c489a..c70cc032a6a 100644
--- a/spec/services/protected_branches/update_service_spec.rb
+++ b/spec/services/protected_branches/update_service_spec.rb
@@ -3,54 +3,64 @@
require 'spec_helper'
RSpec.describe ProtectedBranches::UpdateService do
- let_it_be_with_reload(:project) { create(:project) }
+ shared_examples 'execute with entity' do
+ let(:params) { { name: new_name } }
- let!(:protected_branch) { create(:protected_branch, project: project) }
- let(:user) { project.first_owner }
- let(:params) { { name: new_name } }
+ subject(:service) { described_class.new(entity, user, params) }
- subject(:service) { described_class.new(project, user, params) }
+ describe '#execute' do
+ let(:new_name) { 'new protected branch name' }
+ let(:result) { service.execute(protected_branch) }
- describe '#execute' do
- let(:new_name) { 'new protected branch name' }
- let(:result) { service.execute(protected_branch) }
+ it 'updates a protected branch' do
+ expect(result.reload.name).to eq(params[:name])
+ end
- it 'updates a protected branch' do
- expect(result.reload.name).to eq(params[:name])
- end
+ it 'refreshes the cache' do
+ expect_next_instance_of(ProtectedBranches::CacheService) do |cache_service|
+ expect(cache_service).to receive(:refresh)
+ end
- it 'refreshes the cache' do
- expect_next_instance_of(ProtectedBranches::CacheService) do |cache_service|
- expect(cache_service).to receive(:refresh)
+ result
end
- result
- end
-
- context 'when updating name of a protected branch to one that contains HTML tags' do
- let(:new_name) { 'foo<b>bar<\b>' }
- let(:result) { service.execute(protected_branch) }
+ context 'when updating name of a protected branch to one that contains HTML tags' do
+ let(:new_name) { 'foo<b>bar<\b>' }
+ let(:result) { service.execute(protected_branch) }
- it 'updates a protected branch' do
- expect(result.reload.name).to eq(new_name)
+ it 'updates a protected branch' do
+ expect(result.reload.name).to eq(new_name)
+ end
end
- end
- context 'without admin_project permissions' do
- let(:user) { create(:user) }
+ context 'when a policy restricts rule update' do
+ it "prevents update of the protected branch rule" do
+ disallow(:update_protected_branch, protected_branch)
- it "raises error" do
- expect { service.execute(protected_branch) }.to raise_error(Gitlab::Access::AccessDeniedError)
+ expect { service.execute(protected_branch) }.to raise_error(Gitlab::Access::AccessDeniedError)
+ end
end
end
+ end
- context 'when a policy restricts rule update' do
- it "prevents update of the protected branch rule" do
- disallow(:update_protected_branch, protected_branch)
+ context 'with entity project' do
+ let_it_be_with_reload(:entity) { create(:project) }
+ let!(:protected_branch) { create(:protected_branch, project: entity) }
+ let(:user) { entity.first_owner }
- expect { service.execute(protected_branch) }.to raise_error(Gitlab::Access::AccessDeniedError)
- end
+ it_behaves_like 'execute with entity'
+ end
+
+ context 'with entity group' do
+ let_it_be_with_reload(:entity) { create(:group) }
+ let_it_be_with_reload(:user) { create(:user) }
+ let!(:protected_branch) { create(:protected_branch, group: entity, project: nil) }
+
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :update_protected_branch, protected_branch).and_return(true)
end
+
+ it_behaves_like 'execute with entity'
end
def disallow(ability, protected_branch)
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index f9c16c84121..8eccb9e41bb 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -567,7 +567,13 @@ RSpec.describe QuickActions::InterpretService do
it 'returns the duplicate message' do
_, _, message = service.execute(content, issuable)
- expect(message).to eq("Marked this issue as a duplicate of #{issue_duplicate.to_reference(project)}.")
+ expect(message).to eq("Closed this issue. Marked as related to, and a duplicate of, #{issue_duplicate.to_reference(project)}.")
+ end
+
+ it 'includes duplicate reference' do
+ _, explanations = service.explain(content, issuable)
+
+ expect(explanations).to eq(["Closes this issue. Marks as related to, and a duplicate of, #{issue_duplicate.to_reference(project)}."])
end
end
@@ -2491,6 +2497,16 @@ RSpec.describe QuickActions::InterpretService do
expect(message).to eq('One or more contacts were successfully removed.')
end
end
+
+ context 'when using an alias' do
+ it 'returns the correct execution message' do
+ content = "/labels ~#{bug.title}"
+
+ _, _, message = service.execute(content, issue)
+
+ expect(message).to eq("Added ~\"Bug\" label.")
+ end
+ end
end
describe '#explain' do
diff --git a/spec/services/repositories/housekeeping_service_spec.rb b/spec/services/repositories/housekeeping_service_spec.rb
index fbd9affb33c..57245136fbe 100644
--- a/spec/services/repositories/housekeeping_service_spec.rb
+++ b/spec/services/repositories/housekeeping_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Repositories::HousekeepingService do
+RSpec.describe Repositories::HousekeepingService, feature_category: :source_code_management do
it_behaves_like 'housekeeps repository' do
let_it_be(:resource) { create(:project, :repository) }
end
diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb
index 26def474b88..90e80a45515 100644
--- a/spec/services/search_service_spec.rb
+++ b/spec/services/search_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe SearchService do
+RSpec.describe SearchService, feature_category: :global_search do
let_it_be(:user) { create(:user) }
let_it_be(:accessible_group) { create(:group, :private) }
diff --git a/spec/services/security/merge_reports_service_spec.rb b/spec/services/security/merge_reports_service_spec.rb
index 8415ed8a22f..249f4da5f34 100644
--- a/spec/services/security/merge_reports_service_spec.rb
+++ b/spec/services/security/merge_reports_service_spec.rb
@@ -187,25 +187,25 @@ RSpec.describe Security::MergeReportsService, '#execute' do
it 'deduplicates (except cwe and wasc) and sorts the vulnerabilities by severity (desc) then by compare key' do
expect(merged_report.findings).to(
eq([
- finding_cwe_2,
- finding_wasc_2,
- finding_cwe_1,
- finding_id_2_loc_2,
- finding_id_2_loc_1,
- finding_wasc_1,
- finding_id_1
- ])
+ finding_cwe_2,
+ finding_wasc_2,
+ finding_cwe_1,
+ finding_id_2_loc_2,
+ finding_id_2_loc_1,
+ finding_wasc_1,
+ finding_id_1
+ ])
)
end
it 'deduplicates scanned resources' do
expect(merged_report.scanned_resources).to(
eq([
- scanned_resource,
- scanned_resource_1,
- scanned_resource_2,
- scanned_resource_3
- ])
+ scanned_resource,
+ scanned_resource_1,
+ scanned_resource_2,
+ scanned_resource_3
+ ])
)
end
diff --git a/spec/services/service_ping/submit_service_ping_service_spec.rb b/spec/services/service_ping/submit_service_ping_service_spec.rb
index 5dbf5edb776..37231307156 100644
--- a/spec/services/service_ping/submit_service_ping_service_spec.rb
+++ b/spec/services/service_ping/submit_service_ping_service_spec.rb
@@ -340,7 +340,7 @@ RSpec.describe ServicePing::SubmitService do
end
end
- def stub_response(url: service_ping_payload_url, body:, status: 201)
+ def stub_response(body:, url: service_ping_payload_url, status: 201)
stub_full_request(url, method: :post)
.to_return(
headers: { 'Content-Type' => 'application/json' },
diff --git a/spec/services/timelogs/create_service_spec.rb b/spec/services/timelogs/create_service_spec.rb
index b5ed4a005c7..73860619bcc 100644
--- a/spec/services/timelogs/create_service_spec.rb
+++ b/spec/services/timelogs/create_service_spec.rb
@@ -2,16 +2,12 @@
require 'spec_helper'
-RSpec.describe Timelogs::CreateService do
+RSpec.describe Timelogs::CreateService, feature_category: :team_planning do
let_it_be(:author) { create(:user) }
let_it_be(:project) { create(:project, :public) }
- let_it_be(:time_spent) { 3600 }
- let_it_be(:spent_at) { "2022-07-08" }
- let_it_be(:summary) { "Test summary" }
let(:issuable) { nil }
let(:users_container) { project }
- let(:service) { described_class.new(issuable, time_spent, spent_at, summary, user) }
describe '#execute' do
subject { service.execute }
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index 774a6ddcfb3..c4ed34a693e 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -209,6 +209,15 @@ RSpec.describe TodoService do
it_behaves_like 'an incident management tracked event', :incident_management_incident_todo do
let(:current_user) { john_doe }
end
+
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:namespace) { project.namespace }
+ let(:category) { described_class.to_s }
+ let(:action) { 'incident_management_incident_todo' }
+ let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
+ let(:user) { john_doe }
+ end
end
end
@@ -1250,6 +1259,96 @@ RSpec.describe TodoService do
end
end
+ describe '#create_member_access_request' do
+ context 'snowplow event tracking' do
+ it 'does not track snowplow event when todos are for access request for project', :snowplow do
+ user = create(:user)
+ project = create(:project)
+ requester = create(:project_member, project: project, user: assignee)
+ project.add_owner(user)
+
+ expect_no_snowplow_event
+
+ service.create_member_access_request(requester)
+ end
+ end
+
+ context 'when the group has more than 10 owners' do
+ it 'creates todos for 10 recently active group owners' do
+ group = create(:group, :public)
+
+ users = create_list(:user, 12, :with_sign_ins)
+ users.each do |user|
+ group.add_owner(user)
+ end
+ ten_most_recently_active_group_owners = users.sort_by(&:last_sign_in_at).last(10)
+ excluded_group_owners = users - ten_most_recently_active_group_owners
+
+ requester = create(:group_member, group: group, user: assignee)
+
+ service.create_member_access_request(requester)
+
+ ten_most_recently_active_group_owners.each do |owner|
+ expect(Todo.where(user: owner, target: group, action: Todo::MEMBER_ACCESS_REQUESTED, author: assignee).count).to eq 1
+ end
+
+ excluded_group_owners.each do |owner|
+ expect(Todo.where(user: owner, target: group, action: Todo::MEMBER_ACCESS_REQUESTED, author: assignee).count).to eq 0
+ end
+ end
+ end
+
+ context 'when total owners are less than 10' do
+ it 'creates todos for all group owners' do
+ group = create(:group, :public)
+
+ users = create_list(:user, 4, :with_sign_ins)
+ users.map do |user|
+ group.add_owner(user)
+ end
+
+ requester = create(:group_member, user: assignee, group: group)
+ requester.requested_at = Time.now.utc
+ requester.save!
+
+ service.create_member_access_request(requester)
+
+ users.each do |owner|
+ expect(Todo.where(user: owner, target: group, action: Todo::MEMBER_ACCESS_REQUESTED, author: assignee).count).to eq 1
+ end
+ end
+ end
+
+ context 'when multiple access requests are raised' do
+ it 'creates todos for 10 recently active group owners for multiple requests' do
+ group = create(:group, :public)
+
+ users = create_list(:user, 12, :with_sign_ins)
+ users.each do |user|
+ group.add_owner(user)
+ end
+ ten_most_recently_active_group_owners = users.sort_by(&:last_sign_in_at).last(10)
+ excluded_group_owners = users - ten_most_recently_active_group_owners
+
+ requester1 = create(:group_member, group: group, user: assignee)
+ requester2 = create(:group_member, group: group, user: non_member)
+
+ service.create_member_access_request(requester1)
+ service.create_member_access_request(requester2)
+
+ ten_most_recently_active_group_owners.each do |owner|
+ expect(Todo.where(user: owner, target: group, action: Todo::MEMBER_ACCESS_REQUESTED, author: assignee).count).to eq 1
+ expect(Todo.where(user: owner, target: group, action: Todo::MEMBER_ACCESS_REQUESTED, author: non_member).count).to eq 1
+ end
+
+ excluded_group_owners.each do |owner|
+ expect(Todo.where(user: owner, target: group, action: Todo::MEMBER_ACCESS_REQUESTED, author: assignee).count).to eq 0
+ expect(Todo.where(user: owner, target: group, action: Todo::MEMBER_ACCESS_REQUESTED, author: non_member).count).to eq 0
+ end
+ end
+ end
+ end
+
def should_create_todo(attributes = {})
attributes.reverse_merge!(
project: project,
diff --git a/spec/services/users/assigned_issues_count_service_spec.rb b/spec/services/users/assigned_issues_count_service_spec.rb
new file mode 100644
index 00000000000..afa6a0af3dd
--- /dev/null
+++ b/spec/services/users/assigned_issues_count_service_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Users::AssignedIssuesCountService, :use_clean_rails_memory_store_caching,
+ feature_category: :project_management do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:max_limit) { 10 }
+
+ let(:current_user) { user }
+
+ subject { described_class.new(current_user: current_user, max_limit: max_limit) }
+
+ it_behaves_like 'a counter caching service'
+
+ context 'when user has assigned open issues from archived and closed projects' do
+ before do
+ project = create(:project, :public)
+ archived_project = create(:project, :public, :archived)
+
+ create(:issue, project: project, author: user, assignees: [user])
+ create(:issue, :closed, project: project, author: user, assignees: [user])
+ create(:issue, project: archived_project, author: user, assignees: [user])
+ end
+
+ it 'count all assigned open issues excluding those from closed or archived projects' do
+ expect(subject.count).to eq(1)
+ end
+ end
+
+ context 'when the number of assigned open issues exceeds max_limit' do
+ let_it_be(:banned_user) { create(:user, :banned) }
+ let_it_be(:project) { create(:project).tap { |p| p.add_developer(user) } }
+
+ context 'when user is admin', :enable_admin_mode do
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:issues) { create_list(:issue, max_limit + 1, project: project, assignees: [admin]) }
+ let_it_be(:banned_issue) { create(:issue, project: project, assignees: [admin], author: banned_user) }
+
+ let(:current_user) { admin }
+
+ it 'returns the max_limit count' do
+ expect(subject.count).to eq max_limit
+ end
+ end
+
+ context 'when user is non-admin' do
+ let_it_be(:issues) { create_list(:issue, max_limit + 1, project: project, assignees: [user]) }
+ let_it_be(:closed_issue) { create(:issue, :closed, project: project, assignees: [user]) }
+ let_it_be(:banned_issue) { create(:issue, project: project, assignees: [user], author: banned_user) }
+
+ it 'returns the max_limit count' do
+ expect(subject.count).to eq max_limit
+ end
+ end
+ end
+end
diff --git a/spec/services/users/keys_count_service_spec.rb b/spec/services/users/keys_count_service_spec.rb
index aff267cce5e..607d2946b2c 100644
--- a/spec/services/users/keys_count_service_spec.rb
+++ b/spec/services/users/keys_count_service_spec.rb
@@ -17,6 +17,12 @@ RSpec.describe Users::KeysCountService, :use_clean_rails_memory_store_caching do
it 'returns the number of SSH keys as an Integer' do
expect(subject.count).to eq(1)
end
+
+ it 'does not count signing keys' do
+ create(:key, usage_type: :signing, user: user)
+
+ expect(subject.count).to eq(1)
+ end
end
describe '#uncached_count' do
diff --git a/spec/services/users/migrate_records_to_ghost_user_service_spec.rb b/spec/services/users/migrate_records_to_ghost_user_service_spec.rb
index 6082c7bd10e..827d6f652a4 100644
--- a/spec/services/users/migrate_records_to_ghost_user_service_spec.rb
+++ b/spec/services/users/migrate_records_to_ghost_user_service_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe Users::MigrateRecordsToGhostUserService do
+ include BatchDestroyDependentAssociationsHelper
+
let!(:user) { create(:user) }
let(:service) { described_class.new(user, admin, execution_tracker) }
let(:execution_tracker) { instance_double(::Gitlab::Utils::ExecutionTracker, over_limit?: false) }
@@ -125,6 +127,12 @@ RSpec.describe Users::MigrateRecordsToGhostUserService do
let(:created_record) { create(:review, author: user) }
end
end
+
+ context 'for releases' do
+ include_examples 'migrating records to the ghost user', Release, [:author] do
+ let(:created_record) { create(:release, author: user) }
+ end
+ end
end
context 'on post-migrate cleanups' do
@@ -150,12 +158,6 @@ RSpec.describe Users::MigrateRecordsToGhostUserService do
def nullify_in_batches_regexp(table, column, user, batch_size: 100)
%r{^UPDATE "#{table}" SET "#{column}" = NULL WHERE "#{table}"."id" IN \(SELECT "#{table}"."id" FROM "#{table}" WHERE "#{table}"."#{column}" = #{user.id} LIMIT #{batch_size}\)}
end
-
- def delete_in_batches_regexps(table, column, user, items, batch_size: 1000)
- select_query = %r{^SELECT "#{table}".* FROM "#{table}" WHERE "#{table}"."#{column}" = #{user.id}.*ORDER BY "#{table}"."id" ASC LIMIT #{batch_size}}
-
- [select_query] + items.map { |item| %r{^DELETE FROM "#{table}" WHERE "#{table}"."id" = #{item.id}} }
- end
# rubocop:enable Layout/LineLength
it 'nullifies related associations in batches' do
diff --git a/spec/services/users/registrations_build_service_spec.rb b/spec/services/users/registrations_build_service_spec.rb
index bc3718dbdb2..fa53a4cc604 100644
--- a/spec/services/users/registrations_build_service_spec.rb
+++ b/spec/services/users/registrations_build_service_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe Users::RegistrationsBuildService do
context 'when automatic user confirmation is not enabled' do
before do
- stub_application_setting(send_user_confirmation_email: true)
+ stub_application_setting_enum('email_confirmation_setting', 'hard')
end
context 'when skip_confirmation is true' do
@@ -44,7 +44,7 @@ RSpec.describe Users::RegistrationsBuildService do
context 'when automatic user confirmation is enabled' do
before do
- stub_application_setting(send_user_confirmation_email: false)
+ stub_application_setting_enum('email_confirmation_setting', 'off')
end
context 'when skip_confirmation is true' do
diff --git a/spec/services/web_hooks/log_execution_service_spec.rb b/spec/services/web_hooks/log_execution_service_spec.rb
index fd97d01fa9f..8a845f60ad2 100644
--- a/spec/services/web_hooks/log_execution_service_spec.rb
+++ b/spec/services/web_hooks/log_execution_service_spec.rb
@@ -42,14 +42,6 @@ RSpec.describe WebHooks::LogExecutionService do
service.execute
end
- it 'does not update the last failure when the feature flag is disabled' do
- stub_feature_flags(web_hooks_disable_failed: false)
-
- expect(project_hook).not_to receive(:update_last_failure)
-
- service.execute
- end
-
context 'obtaining an exclusive lease' do
let(:lease_key) { "web_hooks:update_hook_failure_state:#{project_hook.id}" }
@@ -136,19 +128,6 @@ RSpec.describe WebHooks::LogExecutionService do
expect { service.execute }.not_to change(project_hook, :recent_failures)
end
-
- context 'when the web_hooks_disable_failed FF is disabled' do
- before do
- # Hook will only be executed if the flag is disabled.
- stub_feature_flags(web_hooks_disable_failed: false)
- end
-
- it 'does not allow the failure count to overflow' do
- project_hook.update!(recent_failures: 32767)
-
- expect { service.execute }.not_to change(project_hook, :recent_failures)
- end
- end
end
context 'when response_category is :error' do
@@ -165,6 +144,24 @@ RSpec.describe WebHooks::LogExecutionService do
end
end
+ context 'with url_variables' do
+ before do
+ project_hook.update!(
+ url: 'http://example1.test/{foo}-{bar}',
+ url_variables: { 'foo' => 'supers3cret', 'bar' => 'token' }
+ )
+ end
+
+ let(:data) { super().merge(response_headers: { 'X-Token-Id' => 'supers3cret-token', 'X-Request' => 'PUBLIC-token' }) }
+ let(:expected_headers) { { 'X-Token-Id' => '{foo}-{bar}', 'X-Request' => 'PUBLIC-{bar}' } }
+
+ it 'logs the data and masks response headers' do
+ expect { service.execute }.to change(::WebHookLog, :count).by(1)
+
+ expect(WebHookLog.recent.first.response_headers).to eq(expected_headers)
+ end
+ end
+
context 'with X-Gitlab-Token' do
let(:request_headers) { { 'X-Gitlab-Token' => project_hook.token } }
diff --git a/spec/services/work_items/create_and_link_service_spec.rb b/spec/services/work_items/create_and_link_service_spec.rb
index e259a22d388..00372d460e1 100644
--- a/spec/services/work_items/create_and_link_service_spec.rb
+++ b/spec/services/work_items/create_and_link_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItems::CreateAndLinkService do
+RSpec.describe WorkItems::CreateAndLinkService, feature_category: :portfolio_management do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:user) { create(:user) }
@@ -94,7 +94,7 @@ RSpec.describe WorkItems::CreateAndLinkService do
end
it 'returns a link creation error message' do
- expect(service_result.errors).to contain_exactly(/only Issue and Incident can be parent of Task./)
+ expect(service_result.errors).to contain_exactly(/is not allowed to add this type of parent/)
end
end
end
diff --git a/spec/services/work_items/create_service_spec.rb b/spec/services/work_items/create_service_spec.rb
index 1bd7e15db67..a952486ee64 100644
--- a/spec/services/work_items/create_service_spec.rb
+++ b/spec/services/work_items/create_service_spec.rb
@@ -159,7 +159,7 @@ RSpec.describe WorkItems::CreateService do
{
title: 'Awesome work_item',
description: 'please fix',
- work_item_type: create(:work_item_type, :task)
+ work_item_type: WorkItems::Type.default_by_type(:task)
}
end
@@ -176,7 +176,7 @@ RSpec.describe WorkItems::CreateService do
let_it_be(:parent) { create(:work_item, :task, project: project) }
it_behaves_like 'fails creating work item and returns errors' do
- let(:error_message) { 'only Issue and Incident can be parent of Task.' }
+ let(:error_message) { 'is not allowed to add this type of parent' }
end
end
end
diff --git a/spec/services/work_items/parent_links/create_service_spec.rb b/spec/services/work_items/parent_links/create_service_spec.rb
index 0ba41373544..2f2e830845a 100644
--- a/spec/services/work_items/parent_links/create_service_spec.rb
+++ b/spec/services/work_items/parent_links/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItems::ParentLinks::CreateService do
+RSpec.describe WorkItems::ParentLinks::CreateService, feature_category: :portfolio_management do
describe '#execute' do
let_it_be(:user) { create(:user) }
let_it_be(:guest) { create(:user) }
@@ -117,7 +117,7 @@ RSpec.describe WorkItems::ParentLinks::CreateService do
end
it 'returns error status' do
- error = "#{issue.to_reference} cannot be added: only Task can be assigned as a child in hierarchy.. " \
+ error = "#{issue.to_reference} cannot be added: is not allowed to add this type of parent. " \
"#{other_project_task.to_reference} cannot be added: parent must be in the same project as child."
is_expected.to eq(service_error(error, http_status: 422))
@@ -139,7 +139,7 @@ RSpec.describe WorkItems::ParentLinks::CreateService do
let(:params) { { target_issuable: task1 } }
it 'returns error status' do
- error = "#{task1.to_reference} cannot be added: only Issue and Incident can be parent of Task."
+ error = "#{task1.to_reference} cannot be added: is not allowed to add this type of parent"
is_expected.to eq(service_error(error, http_status: 422))
end
diff --git a/spec/services/work_items/update_service_spec.rb b/spec/services/work_items/update_service_spec.rb
index 68efb4c220b..87665bcad2c 100644
--- a/spec/services/work_items/update_service_spec.rb
+++ b/spec/services/work_items/update_service_spec.rb
@@ -284,7 +284,7 @@ RSpec.describe WorkItems::UpdateService do
it 'returns error status' do
expect(subject[:status]).to be(:error)
expect(subject[:message])
- .to match("#{child_work_item.to_reference} cannot be added: only Task can be assigned as a child in hierarchy.")
+ .to match("#{child_work_item.to_reference} cannot be added: is not allowed to add this type of parent")
end
it 'does not update work item attributes' do
diff --git a/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb b/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb
index 1b8c4c5f15f..5a5bb8a1674 100644
--- a/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb
+++ b/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do
+RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_category: :portfolio_management do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
@@ -81,7 +81,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do
it_behaves_like 'raises a WidgetError' do
let(:message) do
- "#{child_issue.to_reference} cannot be added: only Task can be assigned as a child in hierarchy."
+ "#{child_issue.to_reference} cannot be added: is not allowed to add this type of parent"
end
end
end
@@ -136,7 +136,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do
it_behaves_like 'raises a WidgetError' do
let(:message) do
- "#{work_item.to_reference} cannot be added: only Issue and Incident can be parent of Task."
+ "#{work_item.to_reference} cannot be added: is not allowed to add this type of parent"
end
end
end
diff --git a/spec/sidekiq_cluster/sidekiq_cluster_spec.rb b/spec/sidekiq_cluster/sidekiq_cluster_spec.rb
index c0a919a4aec..25a600405fe 100644
--- a/spec/sidekiq_cluster/sidekiq_cluster_spec.rb
+++ b/spec/sidekiq_cluster/sidekiq_cluster_spec.rb
@@ -20,13 +20,16 @@ RSpec.describe Gitlab::SidekiqCluster do # rubocop:disable RSpec/FilePath
"SIDEKIQ_WORKER_ID" => "0"
},
"bundle", "exec", "sidekiq", "-c10", "-eproduction", "-t25", "-gqueues:foo", "-rfoo/bar", "-qfoo,1", process_options
- )
+ ).and_return(1)
+ expect(Process).to receive(:detach).ordered.with(1)
+
expect(Process).to receive(:spawn).ordered.with({
"ENABLE_SIDEKIQ_CLUSTER" => "1",
"SIDEKIQ_WORKER_ID" => "1"
},
"bundle", "exec", "sidekiq", "-c10", "-eproduction", "-t25", "-gqueues:bar,baz", "-rfoo/bar", "-qbar,1", "-qbaz,1", process_options
- )
+ ).and_return(2)
+ expect(Process).to receive(:detach).ordered.with(2)
described_class.start([%w(foo), %w(bar baz)], env: :production, directory: 'foo/bar', max_concurrency: 20, min_concurrency: 10)
end
@@ -35,7 +38,7 @@ RSpec.describe Gitlab::SidekiqCluster do # rubocop:disable RSpec/FilePath
expected_options = {
env: :development,
directory: an_instance_of(String),
- max_concurrency: 50,
+ max_concurrency: 20,
min_concurrency: 0,
worker_id: an_instance_of(Integer),
timeout: 25,
@@ -58,11 +61,13 @@ RSpec.describe Gitlab::SidekiqCluster do # rubocop:disable RSpec/FilePath
let(:env) { { "ENABLE_SIDEKIQ_CLUSTER" => "1", "SIDEKIQ_WORKER_ID" => first_worker_id.to_s } }
let(:args) { ['bundle', 'exec', 'sidekiq', anything, '-eproduction', '-t10', *([anything] * 5)] }
+ let(:waiter_thread) { instance_double('Process::Waiter') }
+
it 'starts a Sidekiq process' do
allow(Process).to receive(:spawn).and_return(1)
+ allow(Process).to receive(:detach).with(1).and_return(waiter_thread)
- expect(Gitlab::ProcessManagement).to receive(:wait_async).with(1)
- expect(described_class.start_sidekiq(%w(foo), **options)).to eq(1)
+ expect(described_class.start_sidekiq(%w(foo), **options)).to eq(waiter_thread)
end
it 'handles duplicate queue names' do
@@ -70,9 +75,9 @@ RSpec.describe Gitlab::SidekiqCluster do # rubocop:disable RSpec/FilePath
.to receive(:spawn)
.with(env, *args, anything)
.and_return(1)
+ allow(Process).to receive(:detach).with(1).and_return(waiter_thread)
- expect(Gitlab::ProcessManagement).to receive(:wait_async).with(1)
- expect(described_class.start_sidekiq(%w(foo foo bar baz), **options)).to eq(1)
+ expect(described_class.start_sidekiq(%w(foo foo bar baz), **options)).to eq(waiter_thread)
end
it 'runs the sidekiq process in a new process group' do
@@ -80,9 +85,9 @@ RSpec.describe Gitlab::SidekiqCluster do # rubocop:disable RSpec/FilePath
.to receive(:spawn)
.with(anything, *args, a_hash_including(pgroup: true))
.and_return(1)
+ allow(Process).to receive(:detach).with(1).and_return(waiter_thread)
- allow(Gitlab::ProcessManagement).to receive(:wait_async)
- expect(described_class.start_sidekiq(%w(foo bar baz), **options)).to eq(1)
+ expect(described_class.start_sidekiq(%w(foo bar baz), **options)).to eq(waiter_thread)
end
end
diff --git a/spec/simplecov_env.rb b/spec/simplecov_env.rb
index dbaecc6a233..70bd01091ba 100644
--- a/spec/simplecov_env.rb
+++ b/spec/simplecov_env.rb
@@ -21,12 +21,14 @@ module SimpleCovEnv
def configure_formatter
SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
- SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new([
- SimpleCov::Formatter::SimpleFormatter,
- SimpleCov::Formatter::HTMLFormatter,
- SimpleCov::Formatter::CoberturaFormatter,
- SimpleCov::Formatter::LcovFormatter
- ])
+ SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new(
+ [
+ SimpleCov::Formatter::SimpleFormatter,
+ SimpleCov::Formatter::HTMLFormatter,
+ SimpleCov::Formatter::CoberturaFormatter,
+ SimpleCov::Formatter::LcovFormatter
+ ]
+ )
end
def configure_job
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 8e73073e68b..23083203cfe 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -211,7 +211,6 @@ RSpec.configure do |config|
end
config.before(:suite) do
- Timecop.safe_mode = true
TestEnv.init
# Reload all feature flags definitions
@@ -271,6 +270,10 @@ RSpec.configure do |config|
# cause spec failures.
stub_feature_flags(use_click_house_database_for_error_tracking: false)
+ # Disable this to avoid the Web IDE modals popping up in tests:
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/385453
+ stub_feature_flags(vscode_web_ide: false)
+
enable_rugged = example.metadata[:enable_rugged].present?
# Disable Rugged features by default
diff --git a/spec/support/atlassian/jira_connect/schemata.rb b/spec/support/atlassian/jira_connect/schemata.rb
index 61e8aa8e15c..73a6833b7cc 100644
--- a/spec/support/atlassian/jira_connect/schemata.rb
+++ b/spec/support/atlassian/jira_connect/schemata.rb
@@ -11,7 +11,7 @@ module Atlassian
schemaVersion pipelineId buildNumber updateSequenceNumber
displayName url state issueKeys testInfo references
lastUpdated
- ),
+ ),
'properties' => {
'schemaVersion' => schema_version_type,
'pipelineId' => { 'type' => 'string' },
diff --git a/spec/support/banzai/filter_timeout_shared_examples.rb b/spec/support/banzai/filter_timeout_shared_examples.rb
new file mode 100644
index 00000000000..1f2ebe6fef6
--- /dev/null
+++ b/spec/support/banzai/filter_timeout_shared_examples.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+# This shared_example requires the following variables:
+# - text: The text to be run through the filter
+#
+# Usage:
+#
+# it_behaves_like 'filter timeout' do
+# let(:text) { 'some text' }
+# end
+RSpec.shared_examples 'filter timeout' do
+ context 'when rendering takes too long' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:context) { { project: project } }
+
+ it 'times out' do
+ stub_const("Banzai::Filter::TimeoutHtmlPipelineFilter::RENDER_TIMEOUT", 0.1)
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:call_with_timeout) do
+ sleep(0.2)
+ text
+ end
+ end
+
+ expect(Gitlab::RenderTimeout).to receive(:timeout).and_call_original
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
+ instance_of(Timeout::Error),
+ project_id: context[:project].id,
+ class_name: described_class.name.demodulize
+ )
+
+ result = filter(text)
+
+ expect(result.to_html).to eq text
+ end
+ end
+end
diff --git a/spec/support/before_all_adapter.rb b/spec/support/before_all_adapter.rb
index 890bdd6a2c4..f4946ff271f 100644
--- a/spec/support/before_all_adapter.rb
+++ b/spec/support/before_all_adapter.rb
@@ -1,27 +1,44 @@
# frozen_string_literal: true
-class BeforeAllAdapter # rubocop:disable Gitlab/NamespacedClass
- def self.all_connection_classes
- @all_connection_classes ||= [ActiveRecord::Base] + ActiveRecord::Base.descendants.select(&:connection_class?) # rubocop: disable Database/MultipleDatabases
- end
-
- def self.begin_transaction
- self.all_connection_classes.each do |connection_class|
- connection_class.connection.begin_transaction(joinable: false)
+module TestProfBeforeAllAdapter
+ module MultipleDatabaseAdapter
+ def self.all_connection_classes
+ @all_connection_classes ||= [ActiveRecord::Base] + ActiveRecord::Base.descendants.select(&:connection_class?) # rubocop: disable Database/MultipleDatabases
end
- end
- def self.rollback_transaction
- self.all_connection_classes.each do |connection_class|
- if connection_class.connection.open_transactions.zero?
- warn "!!! before_all transaction has been already rollbacked and " \
- "could work incorrectly"
- next
+ def self.begin_transaction
+ self.all_connection_classes.each do |connection_class|
+ connection_class.connection.begin_transaction(joinable: false)
end
+ end
- connection_class.connection.rollback_transaction
+ def self.rollback_transaction
+ self.all_connection_classes.each do |connection_class|
+ if connection_class.connection.open_transactions.zero?
+ warn "!!! before_all transaction has been already rollbacked and " \
+ "could work incorrectly"
+ next
+ end
+
+ connection_class.connection.rollback_transaction
+ end
end
end
+
+ # This class is required so we can disable transactions on migration specs
+ module NoTransactionAdapter
+ def self.begin_transaction; end
+
+ def self.rollback_transaction; end
+ end
+
+ def self.default_adapter
+ MultipleDatabaseAdapter
+ end
+
+ def self.no_transaction_adapter
+ NoTransactionAdapter
+ end
end
-TestProf::BeforeAll.adapter = ::BeforeAllAdapter
+TestProf::BeforeAll.adapter = ::TestProfBeforeAllAdapter.default_adapter
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index 57065400220..aea853d1c23 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -16,15 +16,17 @@ Capybara.server_port = ENV['CAPYBARA_PORT'] if ENV['CAPYBARA_PORT']
JSConsoleError = Class.new(StandardError)
# Filter out innocuous JS console messages
-JS_CONSOLE_FILTER = Regexp.union([
- '"[HMR] Waiting for update signal from WDS..."',
- '"[WDS] Hot Module Replacement enabled."',
- '"[WDS] Live Reloading enabled."',
- 'Download the Vue Devtools extension',
- 'Download the Apollo DevTools',
- "Unrecognized feature: 'interest-cohort'",
- 'Does this page need fixes or improvements?'
-])
+JS_CONSOLE_FILTER = Regexp.union(
+ [
+ '"[HMR] Waiting for update signal from WDS..."',
+ '"[WDS] Hot Module Replacement enabled."',
+ '"[WDS] Live Reloading enabled."',
+ 'Download the Vue Devtools extension',
+ 'Download the Apollo DevTools',
+ "Unrecognized feature: 'interest-cohort'",
+ 'Does this page need fixes or improvements?'
+ ]
+)
CAPYBARA_WINDOW_SIZE = [1366, 768].freeze
diff --git a/spec/support/counter_attribute.rb b/spec/support/counter_attribute.rb
index 8bd40b72dcf..44df2df0ea5 100644
--- a/spec/support/counter_attribute.rb
+++ b/spec/support/counter_attribute.rb
@@ -7,12 +7,15 @@ RSpec.configure do |config|
CounterAttributeModel.class_eval do
include CounterAttribute
+ after_initialize { self.allow_package_size_counter = true }
+
counter_attribute :build_artifacts_size
counter_attribute :commit_count
+ counter_attribute :packages_size, if: ->(instance) { instance.allow_package_size_counter }
- attr_accessor :flushed
+ attr_accessor :flushed, :allow_package_size_counter
- counter_attribute_after_flush do |subject|
+ counter_attribute_after_commit do |subject|
subject.flushed = true
end
end
diff --git a/spec/support/cycle_analytics_helpers/test_generation.rb b/spec/support/cycle_analytics_helpers/test_generation.rb
index f866220b919..816caf5f775 100644
--- a/spec/support/cycle_analytics_helpers/test_generation.rb
+++ b/spec/support/cycle_analytics_helpers/test_generation.rb
@@ -42,17 +42,17 @@ module CycleAnalyticsHelpers
end_time = start_time + rand(1..5).days
start_time_conditions.each do |condition_name, condition_fn|
- Timecop.freeze(start_time) { condition_fn[self, data] }
+ travel_to(start_time) { condition_fn[self, data] }
end
# Run `before_end_fn` at the midpoint between `start_time` and `end_time`
- Timecop.freeze(start_time + (end_time - start_time) / 2) { before_end_fn[self, data] } if before_end_fn
+ travel_to(start_time + (end_time - start_time) / 2) { before_end_fn[self, data] } if before_end_fn
end_time_conditions.each do |condition_name, condition_fn|
- Timecop.freeze(end_time) { condition_fn[self, data] }
+ travel_to(end_time) { condition_fn[self, data] }
end
- Timecop.freeze(end_time + 1.day) { post_fn[self, data] } if post_fn
+ travel_to(end_time + 1.day) { post_fn[self, data] } if post_fn
end_time - start_time
end
@@ -74,14 +74,14 @@ module CycleAnalyticsHelpers
end_time = rand(1..10).days.from_now
start_time_conditions.each do |condition_name, condition_fn|
- Timecop.freeze(start_time) { condition_fn[self, data] }
+ travel_to(start_time) { condition_fn[self, data] }
end
end_time_conditions.each do |condition_name, condition_fn|
- Timecop.freeze(end_time) { condition_fn[self, data] }
+ travel_to(end_time) { condition_fn[self, data] }
end
- Timecop.freeze(end_time + 1.day) { post_fn[self, data] } if post_fn
+ travel_to(end_time + 1.day) { post_fn[self, data] } if post_fn
# Turn off the stub before checking assertions
allow(self).to receive(:project).and_call_original
@@ -97,17 +97,17 @@ module CycleAnalyticsHelpers
end_time = start_time + rand(1..5).days
# Run `before_end_fn` at the midpoint between `start_time` and `end_time`
- Timecop.freeze(start_time + (end_time - start_time) / 2) { before_end_fn[self, data] } if before_end_fn
+ travel_to(start_time + (end_time - start_time) / 2) { before_end_fn[self, data] } if before_end_fn
end_time_conditions.each do |condition_name, condition_fn|
- Timecop.freeze(start_time) { condition_fn[self, data] }
+ travel_to(start_time) { condition_fn[self, data] }
end
start_time_conditions.each do |condition_name, condition_fn|
- Timecop.freeze(end_time) { condition_fn[self, data] }
+ travel_to(end_time) { condition_fn[self, data] }
end
- Timecop.freeze(end_time + 1.day) { post_fn[self, data] } if post_fn
+ travel_to(end_time + 1.day) { post_fn[self, data] } if post_fn
expect(subject[phase].project_median).to be_nil
end
@@ -122,10 +122,10 @@ module CycleAnalyticsHelpers
end_time = rand(1..10).days.from_now
end_time_conditions.each_with_index do |(_condition_name, condition_fn), index|
- Timecop.freeze(end_time + index.days) { condition_fn[self, data] }
+ travel_to(end_time + index.days) { condition_fn[self, data] }
end
- Timecop.freeze(end_time + 1.day) { post_fn[self, data] } if post_fn
+ travel_to(end_time + 1.day) { post_fn[self, data] } if post_fn
expect(subject[phase].project_median).to be_nil
end
@@ -139,7 +139,7 @@ module CycleAnalyticsHelpers
start_time = Time.now
start_time_conditions.each do |condition_name, condition_fn|
- Timecop.freeze(start_time) { condition_fn[self, data] }
+ travel_to(start_time) { condition_fn[self, data] }
end
post_fn[self, data] if post_fn
diff --git a/spec/support/database/query_recorder.rb b/spec/support/database/query_recorder.rb
index 1050120e528..c0736221af3 100644
--- a/spec/support/database/query_recorder.rb
+++ b/spec/support/database/query_recorder.rb
@@ -3,7 +3,15 @@
RSpec.configure do |config|
# Truncate the query_recorder log file before starting the suite
config.before(:suite) do
- log_path = Rails.root.join(Gitlab::Database::QueryAnalyzers::QueryRecorder::LOG_FILE)
- File.write(log_path, '') if File.exist?(log_path)
+ log_file = Rails.root.join(Gitlab::Database::QueryAnalyzers::QueryRecorder.log_file)
+ File.write(log_file, '') if File.exist?(log_file)
+ File.delete("#{log_file}.gz") if File.exist?("#{log_file}.gz")
+ end
+
+ config.after(:suite) do
+ if ENV['CI']
+ log_file = Rails.root.join(Gitlab::Database::QueryAnalyzers::QueryRecorder.log_file)
+ system("gzip #{log_file}") if File.exist?(log_file)
+ end
end
end
diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb
index 24cdbe04fc2..588fe466a42 100644
--- a/spec/support/db_cleaner.rb
+++ b/spec/support/db_cleaner.rb
@@ -2,7 +2,7 @@
module DbCleaner
def all_connection_classes
- ::BeforeAllAdapter.all_connection_classes
+ ::TestProfBeforeAllAdapter::MultipleDatabaseAdapter.all_connection_classes
end
def delete_from_all_tables!(except: [])
@@ -12,7 +12,7 @@ module DbCleaner
end
def deletion_except_tables
- ['work_item_types']
+ %w[work_item_types work_item_hierarchy_restrictions]
end
def setup_database_cleaner
diff --git a/spec/support/finder_collection_allowlist.yml b/spec/support/finder_collection_allowlist.yml
index c8af07905c2..750295e16c4 100644
--- a/spec/support/finder_collection_allowlist.yml
+++ b/spec/support/finder_collection_allowlist.yml
@@ -1,8 +1,10 @@
# Allow list for spec/support/finder_collection.rb
-# Permenant excludes
+# Permanent excludes
# For example:
# FooFinder # Reason: It uses a memory backend
+- Namespaces::BilledUsersFinder # Reason: There is no need to have anything else besides the ids is current structure
+- Namespaces::FreeUserCap::UsersFinder # Reason: There is no need to have anything else besides the count
# Temporary excludes (aka TODOs)
# For example:
diff --git a/spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb b/spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb
index e9a13f7bf63..cef9860fe25 100644
--- a/spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb
+++ b/spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb
@@ -30,7 +30,7 @@ RSpec.shared_examples 'a correct instrumented metric query' do |params|
end
before do
- allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false)
+ allow(metric.send(:relation).connection).to receive(:transaction_open?).and_return(false)
end
it 'has correct generate query' do
diff --git a/spec/support/gitlab_stubs/gitlab_ci.yml b/spec/support/gitlab_stubs/gitlab_ci.yml
index 94523591765..dbbfe044e35 100644
--- a/spec/support/gitlab_stubs/gitlab_ci.yml
+++ b/spec/support/gitlab_stubs/gitlab_ci.yml
@@ -12,7 +12,8 @@ variables:
description: 'value of KEY_VALUE_VAR'
DB_NAME: postgres
ENVIRONMENT_VAR:
- value: ['env var value', 'env var value2']
+ value: 'env var value'
+ options: ['env var value', 'env var value2']
description: 'env var description'
stages:
diff --git a/spec/support/helpers/batch_destroy_dependent_associations_helper.rb b/spec/support/helpers/batch_destroy_dependent_associations_helper.rb
new file mode 100644
index 00000000000..22170de053b
--- /dev/null
+++ b/spec/support/helpers/batch_destroy_dependent_associations_helper.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module BatchDestroyDependentAssociationsHelper
+ private
+
+ def delete_in_batches_regexps(table, column, resource, items, batch_size: 1000)
+ # rubocop:disable Layout/LineLength
+ select_query = %r{^SELECT "#{table}".* FROM "#{table}" WHERE.* "#{table}"."#{column}" = #{resource.id}.*ORDER BY "#{table}"."id" ASC LIMIT #{batch_size}}
+ # rubocop:enable Layout/LineLength
+
+ [select_query] + items.map { |item| %r{^DELETE FROM "#{table}" WHERE "#{table}"."id" = #{item.id}} }
+ end
+end
diff --git a/spec/support/helpers/ci/partitioning_helpers.rb b/spec/support/helpers/ci/partitioning_helpers.rb
new file mode 100644
index 00000000000..110199a3147
--- /dev/null
+++ b/spec/support/helpers/ci/partitioning_helpers.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Ci
+ module PartitioningHelpers
+ def stub_current_partition_id(id = Ci::PartitioningTesting::PartitionIdentifiers.ci_testing_partition_id)
+ allow(::Ci::Pipeline)
+ .to receive(:current_partition_value)
+ .and_return(id)
+ end
+ end
+end
diff --git a/spec/support/helpers/content_security_policy_helpers.rb b/spec/support/helpers/content_security_policy_helpers.rb
index 230075ead70..7e3de9fd219 100644
--- a/spec/support/helpers/content_security_policy_helpers.rb
+++ b/spec/support/helpers/content_security_policy_helpers.rb
@@ -4,11 +4,17 @@ module ContentSecurityPolicyHelpers
# Expecting 2 calls to current_content_security_policy by default:
# 1. call that's being tested
# 2. call in ApplicationController
- def setup_csp_for_controller(controller_class, csp = ActionDispatch::ContentSecurityPolicy.new, times: 2)
+ def setup_csp_for_controller(
+ controller_class, csp = ActionDispatch::ContentSecurityPolicy.new, times: 2,
+any_time: false)
expect_next_instance_of(controller_class) do |controller|
- expect(controller)
+ if any_time
+ expect(controller).to receive(:current_content_security_policy).at_least(:once).and_return(csp)
+ else
+ expect(controller)
.to receive(:current_content_security_policy).exactly(times).times
.and_return(csp)
+ end
end
end
end
diff --git a/spec/support/helpers/cookie_helper.rb b/spec/support/helpers/cookie_helper.rb
index ea4be12355b..8971c03a5cc 100644
--- a/spec/support/helpers/cookie_helper.rb
+++ b/spec/support/helpers/cookie_helper.rb
@@ -27,6 +27,12 @@ module CookieHelper
page.driver.browser.manage.cookie_named(name)
end
+ def wait_for_cookie_set(name)
+ wait_for("Complete setting cookie") do
+ get_cookie(name)
+ end
+ end
+
private
def on_a_page?
diff --git a/spec/support/helpers/countries_controller_test_helper.rb b/spec/support/helpers/countries_controller_test_helper.rb
deleted file mode 100644
index 5d36a29bba7..00000000000
--- a/spec/support/helpers/countries_controller_test_helper.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-module CountriesControllerTestHelper
- def world_deny_list
- ::World::DENYLIST + ::World::JH_MARKET
- end
-end
-
-CountriesControllerTestHelper.prepend_mod
diff --git a/spec/support/helpers/doc_url_helper.rb b/spec/support/helpers/doc_url_helper.rb
index bbff4827c56..f98c16a3cc4 100644
--- a/spec/support/helpers/doc_url_helper.rb
+++ b/spec/support/helpers/doc_url_helper.rb
@@ -13,7 +13,7 @@ module DocUrlHelper
"#{documentation_base_url}/ee/#{path}.html"
end
- def stub_doc_file_read(file_name: 'index.md', content: )
+ def stub_doc_file_read(content:, file_name: 'index.md')
expect_file_read(File.join(Rails.root, 'doc', file_name), content: content)
end
end
diff --git a/spec/support/helpers/features/branches_helpers.rb b/spec/support/helpers/features/branches_helpers.rb
index 2a50b41cb4e..d4f96718cc0 100644
--- a/spec/support/helpers/features/branches_helpers.rb
+++ b/spec/support/helpers/features/branches_helpers.rb
@@ -22,10 +22,14 @@ module Spec
end
def select_branch(branch_name)
- find(".js-branch-select").click
+ ref_selector = '.ref-selector'
+ find(ref_selector).click
+ wait_for_requests
- page.within("#new-branch-form .dropdown-menu") do
- click_link(branch_name)
+ page.within(ref_selector) do
+ fill_in _('Search by Git revision'), with: branch_name
+ wait_for_requests
+ find('li', text: branch_name, match: :prefer_exact).click
end
end
end
diff --git a/spec/support/helpers/features/invite_members_modal_helper.rb b/spec/support/helpers/features/invite_members_modal_helper.rb
index d02ec06d886..47cbd6b5208 100644
--- a/spec/support/helpers/features/invite_members_modal_helper.rb
+++ b/spec/support/helpers/features/invite_members_modal_helper.rb
@@ -5,7 +5,7 @@ module Spec
module Helpers
module Features
module InviteMembersModalHelper
- def invite_member(names, role: 'Guest', expires_at: nil, refresh: true)
+ def invite_member(names, role: 'Guest', expires_at: nil)
click_on 'Invite members'
page.within invite_modal_selector do
@@ -14,7 +14,23 @@ module Spec
submit_invites
end
- page.refresh if refresh
+ wait_for_requests
+ end
+
+ def invite_member_by_email(role)
+ click_on _('Invite members')
+
+ page.within invite_modal_selector do
+ choose_options(role, nil)
+ find(member_dropdown_selector).set('new_email@gitlab.com')
+ wait_for_requests
+
+ find('.dropdown-item', text: 'Invite "new_email@gitlab.com" by email').click
+
+ submit_invites
+
+ wait_for_requests
+ end
end
def input_invites(names)
@@ -43,8 +59,6 @@ module Spec
choose_options(role, expires_at)
submit_invites
-
- page.refresh
end
def submit_invites
@@ -52,12 +66,7 @@ module Spec
end
def choose_options(role, expires_at)
- unless role == 'Guest'
- click_button 'Guest'
- wait_for_requests
- click_button role
- end
-
+ select role, from: 'Select a role'
fill_in 'YYYY-MM-DD', with: expires_at.strftime('%Y-%m-%d') if expires_at
end
diff --git a/spec/support/helpers/features/runners_helpers.rb b/spec/support/helpers/features/runners_helpers.rb
index 63fc628358c..c5d26108953 100644
--- a/spec/support/helpers/features/runners_helpers.rb
+++ b/spec/support/helpers/features/runners_helpers.rb
@@ -50,7 +50,7 @@ module Spec
page.within(search_bar_selector) do
click_on filter
- # For OPERATOR_IS_ONLY, clicking the filter
+ # For OPERATORS_IS, clicking the filter
# immediately preselects "=" operator
page.find('input').send_keys(value)
diff --git a/spec/support/helpers/gitaly_setup.rb b/spec/support/helpers/gitaly_setup.rb
index 278dc79e1d0..20c104cd85c 100644
--- a/spec/support/helpers/gitaly_setup.rb
+++ b/spec/support/helpers/gitaly_setup.rb
@@ -205,7 +205,7 @@ module GitalySetup
# This code needs to work in an environment where we cannot use bundler,
# so we cannot easily use the toml-rb gem. This ad-hoc parser should be
# good enough.
- config_text = IO.read(toml)
+ config_text = File.read(toml)
config_text.lines.each do |line|
match_data = line.match(/^\s*(socket_path|listen_addr)\s*=\s*"([^"]*)"$/)
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index bd0efc96bd8..2176a477371 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -859,7 +859,7 @@ module GraphqlHelpers
# A lookahead that selects everything
def positive_lookahead
- double(selects?: true).tap do |selection|
+ double(selected?: true, selects?: true).tap do |selection|
allow(selection).to receive(:selection).and_return(selection)
allow(selection).to receive(:selections).and_return(selection)
allow(selection).to receive(:map).and_return(double(include?: true))
@@ -868,7 +868,7 @@ module GraphqlHelpers
# A lookahead that selects nothing
def negative_lookahead
- double(selects?: false).tap do |selection|
+ double(selected?: false, selects?: false, selections: []).tap do |selection|
allow(selection).to receive(:selection).and_return(selection)
end
end
diff --git a/spec/support/helpers/javascript_fixtures_helpers.rb b/spec/support/helpers/javascript_fixtures_helpers.rb
index 32e6e8d50bd..40eb46878ad 100644
--- a/spec/support/helpers/javascript_fixtures_helpers.rb
+++ b/spec/support/helpers/javascript_fixtures_helpers.rb
@@ -3,12 +3,14 @@
require 'action_dispatch/testing/test_request'
require 'fileutils'
require 'graphlyte'
+require 'active_support/testing/time_helpers'
require_relative '../../../lib/gitlab/popen'
module JavaScriptFixturesHelpers
extend ActiveSupport::Concern
include Gitlab::Popen
+ include ActiveSupport::Testing::TimeHelpers
extend self
@@ -22,7 +24,7 @@ module JavaScriptFixturesHelpers
# pick an arbitrary date from the past, so tests are not time dependent
# Also see spec/frontend/__helpers__/fake_date/jest.js
- Timecop.freeze(Time.utc(2015, 7, 3, 10)) { example.run }
+ travel_to(Time.utc(2015, 7, 3, 10)) { example.run }
raise NoMethodError.new('You need to set `response` for the fixture generator! This will automatically happen with `type: :controller` or `type: :request`.', 'response') unless respond_to?(:response)
diff --git a/spec/support/helpers/listbox_input_helper.rb b/spec/support/helpers/listbox_input_helper.rb
new file mode 100644
index 00000000000..ca7fbac5daa
--- /dev/null
+++ b/spec/support/helpers/listbox_input_helper.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module ListboxInputHelper
+ include WaitForRequests
+
+ def listbox_input(value, from:)
+ open_listbox_input(from) do
+ find('[role="option"]', text: value).click
+ end
+ end
+
+ def open_listbox_input(selector)
+ page.within(selector) do
+ page.find('button[aria-haspopup="listbox"]').click
+ yield
+ end
+ end
+end
diff --git a/spec/support/helpers/migrations_helpers/work_item_types_helper.rb b/spec/support/helpers/migrations_helpers/work_item_types_helper.rb
index b05caf265ee..40f84486537 100644
--- a/spec/support/helpers/migrations_helpers/work_item_types_helper.rb
+++ b/spec/support/helpers/migrations_helpers/work_item_types_helper.rb
@@ -2,26 +2,9 @@
module MigrationHelpers
module WorkItemTypesHelper
- DEFAULT_WORK_ITEM_TYPES = {
- issue: { name: 'Issue', icon_name: 'issue-type-issue', enum_value: 0 },
- incident: { name: 'Incident', icon_name: 'issue-type-incident', enum_value: 1 },
- test_case: { name: 'Test Case', icon_name: 'issue-type-test-case', enum_value: 2 },
- requirement: { name: 'Requirement', icon_name: 'issue-type-requirements', enum_value: 3 },
- task: { name: 'Task', icon_name: 'issue-type-task', enum_value: 4 }
- }.freeze
-
def reset_work_item_types
- work_item_types_table.delete_all
-
- DEFAULT_WORK_ITEM_TYPES.each do |type, attributes|
- work_item_types_table.create!(base_type: attributes[:enum_value], **attributes.slice(:name, :icon_name))
- end
- end
-
- private
-
- def work_item_types_table
- table(:work_item_types)
+ Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.upsert_types
+ Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
end
end
end
diff --git a/spec/support/helpers/project_template_test_helper.rb b/spec/support/helpers/project_template_test_helper.rb
index eab41f6a1cf..bedbb8601e8 100644
--- a/spec/support/helpers/project_template_test_helper.rb
+++ b/spec/support/helpers/project_template_test_helper.rb
@@ -3,14 +3,14 @@
module ProjectTemplateTestHelper
def all_templates
%w[
- rails spring express iosswift dotnetcore android
- gomicro gatsby hugo jekyll plainhtml gitbook
- hexo middleman gitpod_spring_petclinic nfhugo
- nfjekyll nfplainhtml nfgitbook nfhexo salesforcedx
- serverless_framework tencent_serverless_framework
- jsonnet cluster_management kotlin_native_linux
- pelican
- ]
+ rails spring express iosswift dotnetcore android
+ gomicro gatsby hugo jekyll plainhtml gitbook
+ hexo middleman gitpod_spring_petclinic nfhugo
+ nfjekyll nfplainhtml nfgitbook nfhexo salesforcedx
+ serverless_framework tencent_serverless_framework
+ jsonnet cluster_management kotlin_native_linux
+ pelican bridgetown typo3_distribution
+ ]
end
end
diff --git a/spec/support/helpers/repo_helpers.rb b/spec/support/helpers/repo_helpers.rb
index e76a1dd5a74..9f37cf61cc9 100644
--- a/spec/support/helpers/repo_helpers.rb
+++ b/spec/support/helpers/repo_helpers.rb
@@ -137,4 +137,28 @@ eos
file_content: content
).execute
end
+
+ def create_and_delete_files(project, files, &block)
+ files.each do |filename, content|
+ project.repository.create_file(
+ project.creator,
+ filename,
+ content,
+ message: "Automatically created file #{filename}",
+ branch_name: project.default_branch_or_main
+ )
+ end
+
+ yield
+
+ ensure
+ files.each do |filename, _content|
+ project.repository.delete_file(
+ project.creator,
+ filename,
+ message: "Automatically deleted file #{filename}",
+ branch_name: project.default_branch_or_main
+ )
+ end
+ end
end
diff --git a/spec/support/helpers/search_helpers.rb b/spec/support/helpers/search_helpers.rb
index 7d0f8c09933..eab30be9243 100644
--- a/spec/support/helpers/search_helpers.rb
+++ b/spec/support/helpers/search_helpers.rb
@@ -35,6 +35,8 @@ module SearchHelpers
def select_search_scope(scope)
page.within '[data-testid="search-filter"]' do
click_link scope
+
+ wait_for_all_requests
end
end
diff --git a/spec/support/helpers/service_desk_helper.rb b/spec/support/helpers/service_desk_helper.rb
new file mode 100644
index 00000000000..d67ee5b8a11
--- /dev/null
+++ b/spec/support/helpers/service_desk_helper.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module ServiceDeskHelper
+ def set_template_file(file_name, content)
+ file_path = ".gitlab/issue_templates/#{file_name}.md"
+ project.repository.create_file(user, file_path, content, message: 'message', branch_name: 'master')
+ settings.update!(issue_template_key: file_name)
+ end
+end
diff --git a/spec/support/helpers/smime_helper.rb b/spec/support/helpers/smime_helper.rb
index fa16c433c6b..1a414c72fbb 100644
--- a/spec/support/helpers/smime_helper.rb
+++ b/spec/support/helpers/smime_helper.rb
@@ -17,7 +17,7 @@ module SmimeHelper
end
# returns a hash { key:, cert: } containing a generated key, cert pair
- def issue(email_address: 'test@example.com', cn: nil, signed_by:, expires_in:, certificate_authority:)
+ def issue(signed_by:, expires_in:, certificate_authority:, email_address: 'test@example.com', cn: nil)
key = OpenSSL::PKey::RSA.new(4096)
public_key = key.public_key
diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb
index 24c768258a1..4ca8f26be9e 100644
--- a/spec/support/helpers/stub_configuration.rb
+++ b/spec/support/helpers/stub_configuration.rb
@@ -20,6 +20,17 @@ module StubConfiguration
allow_any_instance_of(ApplicationSetting).to receive(:cached_html_up_to_date?).and_return(false)
end
+ # For enums with `_prefix: true`, this allows us to stub the application setting properly
+ def stub_application_setting_enum(setting, value)
+ stub_application_setting(setting.to_sym => value)
+
+ ApplicationSetting.send(setting.pluralize.to_sym).each_key do |key|
+ stub_application_setting("#{setting}_#{key}".to_sym => key == value)
+ end
+
+ Gitlab::CurrentSettings.send(setting)
+ end
+
def stub_not_protect_default_branch
stub_application_setting(
default_branch_protection: Gitlab::Access::PROTECTION_NONE)
diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb
index 87e2a71b1cd..c163ce1d880 100644
--- a/spec/support/helpers/stub_object_storage.rb
+++ b/spec/support/helpers/stub_object_storage.rb
@@ -12,7 +12,6 @@ module StubObjectStorage
uploader:,
enabled: true,
proxy_download: false,
- background_upload: false,
direct_upload: false,
cdn: {}
)
@@ -20,7 +19,6 @@ module StubObjectStorage
new_config = config.to_h.deep_symbolize_keys.merge({
enabled: enabled,
proxy_download: proxy_download,
- background_upload: background_upload,
direct_upload: direct_upload,
cdn: cdn
})
@@ -30,7 +28,6 @@ module StubObjectStorage
allow(config).to receive(:to_h).and_return(new_config)
allow(config).to receive(:enabled) { enabled }
allow(config).to receive(:proxy_download) { proxy_download }
- allow(config).to receive(:background_upload) { background_upload }
allow(config).to receive(:direct_upload) { direct_upload }
uploader_config = Settingslogic.new(new_config.deep_stringify_keys)
diff --git a/spec/support/helpers/stub_snowplow.rb b/spec/support/helpers/stub_snowplow.rb
index 85c605efea3..80342863f7b 100644
--- a/spec/support/helpers/stub_snowplow.rb
+++ b/spec/support/helpers/stub_snowplow.rb
@@ -10,7 +10,7 @@ module StubSnowplow
# rubocop:disable RSpec/AnyInstanceOf
allow_any_instance_of(Gitlab::Tracking::Destinations::Snowplow)
.to receive(:emitter)
- .and_return(SnowplowTracker::Emitter.new(host, buffer_size: buffer_size))
+ .and_return(SnowplowTracker::Emitter.new(endpoint: host, options: { buffer_size: buffer_size }))
# rubocop:enable RSpec/AnyInstanceOf
stub_application_setting(snowplow_enabled: true, snowplow_collector_hostname: host)
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index e1b461cf37e..3530d1b1a39 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -91,7 +91,8 @@ module TestEnv
'utf-16' => 'f05a987',
'gitaly-rename-test' => '94bb47c',
'smime-signed-commits' => 'ed775cc',
- 'Ääh-test-utf-8' => '7975be0'
+ 'Ääh-test-utf-8' => '7975be0',
+ 'ssh-signed-commit' => '7b5160f'
}.freeze
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
@@ -312,12 +313,6 @@ module TestEnv
end
end
- def storage_dir_exists?(storage, dir)
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- File.exist?(File.join(GitalySetup.repos_path(storage), dir))
- end
- end
-
def repos_path
@repos_path ||= GitalySetup.repos_path
end
@@ -375,6 +370,7 @@ module TestEnv
def seed_db
Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.upsert_types
+ Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
end
private
@@ -427,6 +423,8 @@ module TestEnv
return if File.exist?(install_dir) && ci?
if component_needs_update?(install_dir, version)
+ puts "==> Starting #{component} set up...\n"
+
# Cleanup the component entirely to ensure we start fresh
FileUtils.rm_rf(install_dir) if fresh_install
@@ -486,12 +484,14 @@ module TestEnv
# The HEAD of the component_folder will be used as heuristic for the version
# of the binaries, allowing to use Git to determine if HEAD is later than
# the expected version. Note: Git considers HEAD to be an anchestor of HEAD.
- _out, exit_status = Gitlab::Popen.popen(%W[
- #{Gitlab.config.git.bin_path}
- -C #{component_folder}
- merge-base --is-ancestor
- #{expected_version} HEAD
-])
+ _out, exit_status = Gitlab::Popen.popen(
+ %W[
+ #{Gitlab.config.git.bin_path}
+ -C #{component_folder}
+ merge-base --is-ancestor
+ #{expected_version} HEAD
+ ]
+ )
exit_status == 0
end
diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb
index 92a946db337..78ceaf297a8 100644
--- a/spec/support/helpers/usage_data_helpers.rb
+++ b/spec/support/helpers/usage_data_helpers.rb
@@ -2,124 +2,118 @@
module UsageDataHelpers
COUNTS_KEYS = %i(
- assignee_lists
- ci_builds
- ci_internal_pipelines
- ci_external_pipelines
- ci_pipeline_config_auto_devops
- ci_pipeline_config_repository
- ci_runners
- ci_triggers
- ci_pipeline_schedules
- auto_devops_enabled
- auto_devops_disabled
- deploy_keys
- deployments
- successful_deployments
- failed_deployments
- environments
- clusters
- clusters_enabled
- project_clusters_enabled
- group_clusters_enabled
- instance_clusters_enabled
- clusters_disabled
- project_clusters_disabled
- group_clusters_disabled
- instance_clusters_disabled
- clusters_platforms_eks
- clusters_platforms_gke
- clusters_platforms_user
- clusters_integrations_prometheus
- clusters_management_project
- in_review_folder
- grafana_integrated_projects
- groups
- issues
- issues_created_from_gitlab_error_tracking_ui
- issues_with_associated_zoom_link
- issues_using_zoom_quick_actions
- issues_with_embedded_grafana_charts_approx
- incident_issues
- keys
- label_lists
- labels
- lfs_objects
- merge_requests
- milestone_lists
- milestones
- notes
- pool_repositories
- projects
- projects_imported_from_github
- projects_asana_active
- projects_jenkins_active
- projects_jira_active
- projects_jira_server_active
- projects_jira_cloud_active
- projects_jira_dvcs_cloud_active
- projects_jira_dvcs_server_active
- projects_slack_active
- projects_slack_slash_commands_active
- projects_custom_issue_tracker_active
- projects_mattermost_active
- projects_prometheus_active
- projects_with_repositories_enabled
- projects_with_error_tracking_enabled
- projects_with_enabled_alert_integrations
- projects_with_expiration_policy_enabled_with_older_than_unset
- projects_with_expiration_policy_enabled_with_older_than_set_to_7d
- projects_with_expiration_policy_enabled_with_older_than_set_to_14d
- projects_with_expiration_policy_enabled_with_older_than_set_to_30d
- projects_with_expiration_policy_enabled_with_older_than_set_to_60d
- projects_with_expiration_policy_enabled_with_older_than_set_to_90d
- projects_with_terraform_reports
- projects_with_terraform_states
- pages_domains
- protected_branches
- protected_branches_except_default
- releases
- remote_mirrors
- snippets
- personal_snippets
- project_snippets
- suggestions
- terraform_reports
- terraform_states
- todos
- uploads
- web_hooks
- user_preferences_user_gitpod_enabled
- ).freeze
+ assignee_lists
+ ci_builds
+ ci_internal_pipelines
+ ci_external_pipelines
+ ci_pipeline_config_auto_devops
+ ci_pipeline_config_repository
+ ci_runners
+ ci_triggers
+ ci_pipeline_schedules
+ auto_devops_enabled
+ auto_devops_disabled
+ deploy_keys
+ deployments
+ successful_deployments
+ failed_deployments
+ environments
+ clusters
+ clusters_enabled
+ project_clusters_enabled
+ group_clusters_enabled
+ instance_clusters_enabled
+ clusters_disabled
+ project_clusters_disabled
+ group_clusters_disabled
+ instance_clusters_disabled
+ clusters_platforms_eks
+ clusters_platforms_gke
+ clusters_platforms_user
+ clusters_integrations_prometheus
+ clusters_management_project
+ in_review_folder
+ grafana_integrated_projects
+ groups
+ issues
+ issues_created_from_gitlab_error_tracking_ui
+ issues_with_associated_zoom_link
+ issues_using_zoom_quick_actions
+ issues_with_embedded_grafana_charts_approx
+ incident_issues
+ keys
+ label_lists
+ labels
+ lfs_objects
+ merge_requests
+ milestone_lists
+ milestones
+ notes
+ pool_repositories
+ projects
+ projects_imported_from_github
+ projects_asana_active
+ projects_jenkins_active
+ projects_jira_active
+ projects_jira_server_active
+ projects_jira_cloud_active
+ projects_jira_dvcs_cloud_active
+ projects_jira_dvcs_server_active
+ projects_slack_active
+ projects_slack_slash_commands_active
+ projects_custom_issue_tracker_active
+ projects_mattermost_active
+ projects_prometheus_active
+ projects_with_repositories_enabled
+ projects_with_error_tracking_enabled
+ projects_with_enabled_alert_integrations
+ projects_with_terraform_reports
+ projects_with_terraform_states
+ pages_domains
+ protected_branches
+ protected_branches_except_default
+ releases
+ remote_mirrors
+ snippets
+ personal_snippets
+ project_snippets
+ suggestions
+ terraform_reports
+ terraform_states
+ todos
+ uploads
+ web_hooks
+ user_preferences_user_gitpod_enabled
+ ).freeze
USAGE_DATA_KEYS = %i(
- active_user_count
- counts
- counts_monthly
- recorded_at
- edition
- version
- installation_type
- uuid
- hostname
- mattermost_enabled
- signup_enabled
- ldap_enabled
- gravatar_enabled
- omniauth_enabled
- reply_by_email_enabled
- container_registry_enabled
- dependency_proxy_enabled
- gitlab_shared_runners_enabled
- gitlab_pages
- git
- gitaly
- database
- prometheus_metrics_enabled
- web_ide_clientside_preview_enabled
- object_store
- topology
- ).freeze
+ active_user_count
+ counts
+ counts_monthly
+ recorded_at
+ edition
+ version
+ installation_type
+ uuid
+ hostname
+ mattermost_enabled
+ signup_enabled
+ ldap_enabled
+ gravatar_enabled
+ omniauth_enabled
+ reply_by_email_enabled
+ container_registry_enabled
+ dependency_proxy_enabled
+ gitlab_shared_runners_enabled
+ gitlab_pages
+ git
+ gitaly
+ database
+ prometheus_metrics_enabled
+ web_ide_clientside_preview_enabled
+ object_store
+ topology
+ ).freeze
def stub_usage_data_connections
allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false)
@@ -162,7 +156,6 @@ module UsageDataHelpers
'direct_upload' => true,
'connection' =>
{ 'provider' => 'AWS', 'aws_access_key_id' => 'minio', 'aws_secret_access_key' => 'gdk-minio', 'region' => 'gdk', 'endpoint' => 'http://127.0.0.1:9000', 'path_style' => true },
- 'background_upload' => false,
'proxy_download' => false } }
)
@@ -177,7 +170,6 @@ module UsageDataHelpers
'direct_upload' => true,
'connection' =>
{ 'provider' => 'AWS', 'aws_access_key_id' => 'minio', 'aws_secret_access_key' => 'gdk-minio', 'region' => 'gdk', 'endpoint' => 'http://127.0.0.1:9000', 'path_style' => true },
- 'background_upload' => false,
'proxy_download' => false } }
)
allow(Settings).to receive(:[]).with('uploads')
@@ -188,7 +180,6 @@ module UsageDataHelpers
'direct_upload' => true,
'connection' =>
{ 'provider' => 'AWS', 'aws_access_key_id' => 'minio', 'aws_secret_access_key' => 'gdk-minio', 'region' => 'gdk', 'endpoint' => 'http://127.0.0.1:9000', 'path_style' => true },
- 'background_upload' => false,
'proxy_download' => false } }
)
allow(Settings).to receive(:[]).with('packages')
@@ -200,7 +191,6 @@ module UsageDataHelpers
'direct_upload' => false,
'connection' =>
{ 'provider' => 'AWS', 'aws_access_key_id' => 'minio', 'aws_secret_access_key' => 'gdk-minio', 'region' => 'gdk', 'endpoint' => 'http://127.0.0.1:9000', 'path_style' => true },
- 'background_upload' => true,
'proxy_download' => false } }
)
end
diff --git a/spec/support/helpers/workhorse_helpers.rb b/spec/support/helpers/workhorse_helpers.rb
index 6f22df9ae0f..f894aff373c 100644
--- a/spec/support/helpers/workhorse_helpers.rb
+++ b/spec/support/helpers/workhorse_helpers.rb
@@ -39,11 +39,11 @@ module WorkhorseHelpers
# workhorse_finalize will transform file_key inside params as if it was the finalize call of an inline object storage upload.
# note that based on the content of the params it can simulate a disc acceleration or an object storage upload
- def workhorse_finalize(url, method: :post, file_key:, params:, headers: {}, send_rewritten_field: false)
+ def workhorse_finalize(url, file_key:, params:, method: :post, headers: {}, send_rewritten_field: false)
workhorse_finalize_with_multiple_files(url, method: method, file_keys: file_key, params: params, headers: headers, send_rewritten_field: send_rewritten_field)
end
- def workhorse_finalize_with_multiple_files(url, method: :post, file_keys:, params:, headers: {}, send_rewritten_field: false)
+ def workhorse_finalize_with_multiple_files(url, file_keys:, params:, method: :post, headers: {}, send_rewritten_field: false)
workhorse_request_with_multiple_files(method, url,
file_keys: file_keys,
params: params,
@@ -52,11 +52,11 @@ module WorkhorseHelpers
)
end
- def workhorse_request_with_file(method, url, file_key:, params:, env: {}, extra_headers: {}, send_rewritten_field:)
+ def workhorse_request_with_file(method, url, file_key:, params:, send_rewritten_field:, env: {}, extra_headers: {})
workhorse_request_with_multiple_files(method, url, file_keys: file_key, params: params, env: env, extra_headers: extra_headers, send_rewritten_field: send_rewritten_field)
end
- def workhorse_request_with_multiple_files(method, url, file_keys:, params:, env: {}, extra_headers: {}, send_rewritten_field:)
+ def workhorse_request_with_multiple_files(method, url, file_keys:, params:, send_rewritten_field:, env: {}, extra_headers: {})
workhorse_params = params.dup
file_keys = Array(file_keys)
diff --git a/spec/support/import_export/common_util.rb b/spec/support/import_export/common_util.rb
index 9da151895a7..3d7a0d29e71 100644
--- a/spec/support/import_export/common_util.rb
+++ b/spec/support/import_export/common_util.rb
@@ -83,7 +83,7 @@ module ImportExport
path = File.join(dir_path, "#{exportable_path}.json")
return unless File.exist?(path)
- Gitlab::Json.parse(IO.read(path))
+ Gitlab::Json.parse(File.read(path))
end
def consume_relations(dir_path, exportable_path, key)
@@ -101,7 +101,7 @@ module ImportExport
end
def project_json(filename)
- Gitlab::Json.parse(IO.read(filename))
+ Gitlab::Json.parse(File.read(filename))
end
end
end
diff --git a/spec/support/import_export/export_file_helper.rb b/spec/support/import_export/export_file_helper.rb
index 3134e5c32a3..9a26f50903f 100644
--- a/spec/support/import_export/export_file_helper.rb
+++ b/spec/support/import_export/export_file_helper.rb
@@ -117,8 +117,8 @@ module ExportFileHelper
# Check whether this is a hash attribute inside a model
if model.is_a?(Symbol)
return true if (safe_hashes[model] - parent.keys).empty?
- else
- return true if safe_model?(model, excluded_attributes, parent)
+ elsif safe_model?(model, excluded_attributes, parent)
+ return true
end
end
diff --git a/spec/support/matchers/exceed_query_limit.rb b/spec/support/matchers/exceed_query_limit.rb
index 6d7658b7c33..a5a017828b3 100644
--- a/spec/support/matchers/exceed_query_limit.rb
+++ b/spec/support/matchers/exceed_query_limit.rb
@@ -63,14 +63,16 @@ module ExceedQueryLimitHelpers
end
end
- MARGINALIA_ANNOTATION_REGEX = %r{\s*\/\*.*\*\/}.freeze
-
- DB_QUERY_RE = Regexp.union([
- /^(?<prefix>SELECT .* FROM "?[a-z_]+"?) (?<suffix>.*)$/m,
- /^(?<prefix>UPDATE "?[a-z_]+"?) (?<suffix>.*)$/m,
- /^(?<prefix>INSERT INTO "[a-z_]+" \((?:"[a-z_]+",?\s?)+\)) (?<suffix>.*)$/m,
- /^(?<prefix>DELETE FROM "[a-z_]+") (?<suffix>.*)$/m
- ]).freeze
+ MARGINALIA_ANNOTATION_REGEX = %r{\s*/\*.*\*/}.freeze
+
+ DB_QUERY_RE = Regexp.union(
+ [
+ /^(?<prefix>SELECT .* FROM "?[a-z_]+"?) (?<suffix>.*)$/m,
+ /^(?<prefix>UPDATE "?[a-z_]+"?) (?<suffix>.*)$/m,
+ /^(?<prefix>INSERT INTO "[a-z_]+" \((?:"[a-z_]+",?\s?)+\)) (?<suffix>.*)$/m,
+ /^(?<prefix>DELETE FROM "[a-z_]+") (?<suffix>.*)$/m
+ ]
+ ).freeze
def with_threshold(threshold)
@threshold = threshold
diff --git a/spec/support/memory_instrumentation_helper.rb b/spec/support/memory_instrumentation_helper.rb
index 84ec02fa5aa..51506376a75 100644
--- a/spec/support/memory_instrumentation_helper.rb
+++ b/spec/support/memory_instrumentation_helper.rb
@@ -5,13 +5,10 @@
# This concept is currently tried to be upstreamed here:
# - https://github.com/ruby/ruby/pull/3978
module MemoryInstrumentationHelper
- def skip_memory_instrumentation!
+ def verify_memory_instrumentation_available!
return if ::Gitlab::Memory::Instrumentation.available?
- # if we are running in CI, a test cannot be skipped
- return if ENV['CI']
-
- skip 'Missing a memory instrumentation patch. ' \
+ raise 'Ruby is missing a required patch that enables memory instrumentation. ' \
'More information can be found here: https://gitlab.com/gitlab-org/gitlab/-/issues/296530.'
end
end
diff --git a/spec/support/migration.rb b/spec/support/migration.rb
index 4d4a293e9ff..b1e75d9c9e2 100644
--- a/spec/support/migration.rb
+++ b/spec/support/migration.rb
@@ -20,6 +20,14 @@ RSpec.configure do |config|
Gitlab::CurrentSettings.clear_in_memory_application_settings!
end
+ config.prepend_before(:all, :migration) do
+ TestProf::BeforeAll.adapter = ::TestProfBeforeAllAdapter.no_transaction_adapter
+ end
+
+ config.append_after(:all, :migration) do
+ TestProf::BeforeAll.adapter = ::TestProfBeforeAllAdapter.default_adapter
+ end
+
config.append_after(:context, :migration) do
recreate_databases_and_seed_if_needed || ensure_schema_and_empty_tables
end
diff --git a/spec/support/migrations_helpers/vulnerabilities_findings_helper.rb b/spec/support/migrations_helpers/vulnerabilities_findings_helper.rb
index a3cccc3a75d..9a5313c3fa4 100644
--- a/spec/support/migrations_helpers/vulnerabilities_findings_helper.rb
+++ b/spec/support/migrations_helpers/vulnerabilities_findings_helper.rb
@@ -92,10 +92,10 @@ module MigrationHelpers
"url" => "http://goat:8080/WebGoat/logout",
"body" => "",
"headers" => [
- {
- "name" => "Accept",
- "value" => "*/*"
- }
+ {
+ "name" => "Accept",
+ "value" => "*/*"
+ }
]
},
"response" => {
diff --git a/spec/support/models/ci/partitioning_testing/cascade_check.rb b/spec/support/models/ci/partitioning_testing/cascade_check.rb
index f553a47ef4f..bcfc9675476 100644
--- a/spec/support/models/ci/partitioning_testing/cascade_check.rb
+++ b/spec/support/models/ci/partitioning_testing/cascade_check.rb
@@ -15,6 +15,13 @@ module PartitioningTesting
raise "partition_id was expected to equal #{partition_scope_value} but it was #{partition_id}."
end
+
+ class_methods do
+ # Allowing partition callback to be used with BulkInsertSafe
+ def _bulk_insert_callback_allowed?(name, args)
+ super || args.first == :after && args.second == :check_partition_cascade_value
+ end
+ end
end
end
diff --git a/spec/support/models/ci/partitioning_testing/schema_helpers.rb b/spec/support/models/ci/partitioning_testing/schema_helpers.rb
index 712178710da..3a79ed1b5a9 100644
--- a/spec/support/models/ci/partitioning_testing/schema_helpers.rb
+++ b/spec/support/models/ci/partitioning_testing/schema_helpers.rb
@@ -8,10 +8,10 @@ module Ci
module_function
def with_routing_tables
- Ci::BuildMetadata.table_name = :p_ci_builds_metadata
+ # model.table_name = :routing_table
yield
- ensure
- Ci::BuildMetadata.table_name = :ci_builds_metadata
+ # ensure
+ # model.table_name = :regular_table
end
# We're dropping the default values here to ensure that the application code
diff --git a/spec/support/patches/rspec_mocks_prepended_methods.rb b/spec/support/patches/rspec_mocks_prepended_methods.rb
index fa3a74c670c..d51fb37a499 100644
--- a/spec/support/patches/rspec_mocks_prepended_methods.rb
+++ b/spec/support/patches/rspec_mocks_prepended_methods.rb
@@ -45,7 +45,7 @@ module RSpec
private
def method_owner
- @method_owner ||= Object.instance_method(:method).bind(object).call(@method_name).owner
+ @method_owner ||= Object.instance_method(:method).bind_call(object, @method_name).owner
end
end
end
diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/prometheus/additional_metrics_shared_examples.rb
index 3a5909cd908..e589baf0909 100644
--- a/spec/support/prometheus/additional_metrics_shared_examples.rb
+++ b/spec/support/prometheus/additional_metrics_shared_examples.rb
@@ -92,15 +92,15 @@ RSpec.shared_examples 'additional metrics query' do
metrics: [
{
title: 'title', weight: 1, y_label: 'Values', queries: [
- { query_range: 'query_range_a', result: query_range_result },
- { query_range: 'query_range_b', label: 'label', unit: 'unit', result: query_range_result }
- ]
+ { query_range: 'query_range_a', result: query_range_result },
+ { query_range: 'query_range_b', label: 'label', unit: 'unit', result: query_range_result }
+ ]
}
]
}
]
- expect(query_result).to match_schema('prometheus/additional_metrics_query_result')
+ expect(query_result.to_json).to match_schema('prometheus/additional_metrics_query_result')
expect(query_result).to eq(expected)
end
end
@@ -128,7 +128,7 @@ RSpec.shared_examples 'additional metrics query' do
queries_with_result_a = { queries: [{ query_range: 'query_range_a', result: query_range_result }] }
queries_with_result_b = { queries: [{ query_range: 'query_range_b', result: query_range_result }] }
- expect(query_result).to match_schema('prometheus/additional_metrics_query_result')
+ expect(query_result.to_json).to match_schema('prometheus/additional_metrics_query_result')
expect(query_result.count).to eq(2)
expect(query_result).to all(satisfy { |r| r[:metrics].count == 1 })
@@ -147,7 +147,7 @@ RSpec.shared_examples 'additional metrics query' do
it 'return group data only for query with results' do
queries_with_result = { queries: [{ query_range: 'query_range_a', result: query_range_result }] }
- expect(query_result).to match_schema('prometheus/additional_metrics_query_result')
+ expect(query_result.to_json).to match_schema('prometheus/additional_metrics_query_result')
expect(query_result.count).to eq(1)
expect(query_result).to all(satisfy { |r| r[:metrics].count == 1 })
diff --git a/spec/support/redis/redis_shared_examples.rb b/spec/support/redis/redis_shared_examples.rb
index 33945509675..0368fd63357 100644
--- a/spec/support/redis/redis_shared_examples.rb
+++ b/spec/support/redis/redis_shared_examples.rb
@@ -4,6 +4,7 @@ RSpec.shared_examples "redis_shared_examples" do
include StubENV
let(:test_redis_url) { "redis://redishost:#{redis_port}" }
+ let(:test_cluster_config) { { cluster: [{ host: "redis://redishost", port: redis_port }] } }
let(:config_file_name) { instance_specific_config_file }
let(:config_old_format_socket) { "spec/fixtures/config/redis_old_format_socket.yml" }
let(:config_new_format_socket) { "spec/fixtures/config/redis_new_format_socket.yml" }
@@ -11,6 +12,7 @@ RSpec.shared_examples "redis_shared_examples" do
let(:new_socket_path) { "/path/to/redis.sock" }
let(:config_old_format_host) { "spec/fixtures/config/redis_old_format_host.yml" }
let(:config_new_format_host) { "spec/fixtures/config/redis_new_format_host.yml" }
+ let(:config_cluster_format_host) { "spec/fixtures/config/redis_cluster_format_host.yml" }
let(:redis_port) { 6379 }
let(:redis_database) { 99 }
let(:sentinel_port) { 26379 }
@@ -191,6 +193,30 @@ RSpec.shared_examples "redis_shared_examples" do
end
end
end
+
+ context 'with redis cluster format' do
+ let(:config_file_name) { config_cluster_format_host }
+
+ where(:rails_env, :host) do
+ [
+ %w[development development-master],
+ %w[test test-master],
+ %w[production production-master]
+ ]
+ end
+
+ with_them do
+ it 'returns hash with cluster and password' do
+ is_expected.to include(password: 'myclusterpassword',
+ cluster: [
+ { host: "#{host}1", port: redis_port },
+ { host: "#{host}2", port: redis_port }
+ ]
+ )
+ is_expected.not_to have_key(:url)
+ end
+ end
+ end
end
end
@@ -317,6 +343,14 @@ RSpec.shared_examples "redis_shared_examples" do
expect(subject).to eq(redis_database)
end
end
+
+ context 'with cluster-mode' do
+ let(:config_file_name) { config_cluster_format_host }
+
+ it 'returns the correct db' do
+ expect(subject).to eq(0)
+ end
+ end
end
describe '#sentinels' do
@@ -350,6 +384,14 @@ RSpec.shared_examples "redis_shared_examples" do
is_expected.to be_nil
end
end
+
+ context 'when cluster is defined' do
+ let(:config_file_name) { config_cluster_format_host }
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
+ end
end
describe '#sentinels?' do
@@ -370,6 +412,14 @@ RSpec.shared_examples "redis_shared_examples" do
is_expected.to be_falsey
end
end
+
+ context 'when cluster is defined' do
+ let(:config_file_name) { config_cluster_format_host }
+
+ it 'returns false' do
+ is_expected.to be_falsey
+ end
+ end
end
describe '#raw_config_hash' do
@@ -377,6 +427,11 @@ RSpec.shared_examples "redis_shared_examples" do
expect(subject).to receive(:fetch_config) { test_redis_url }
expect(subject.send(:raw_config_hash)).to eq(url: test_redis_url)
end
+
+ it 'returns cluster config without url key in a hash' do
+ expect(subject).to receive(:fetch_config) { test_cluster_config }
+ expect(subject.send(:raw_config_hash)).to eq(test_cluster_config)
+ end
end
describe '#fetch_config' do
diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb
index 71dfc3fd5a3..ff0b5bebe33 100644
--- a/spec/support/rspec.rb
+++ b/spec/support/rspec.rb
@@ -8,6 +8,8 @@ require_relative "helpers/stub_object_storage"
require_relative "helpers/stub_env"
require_relative "helpers/fast_rails_root"
+require_relative "../../lib/gitlab/utils"
+
RSpec::Expectations.configuration.on_potential_false_positives = :raise
RSpec.configure do |config|
@@ -35,4 +37,13 @@ RSpec.configure do |config|
config.include StubObjectStorage
config.include StubENV
config.include FastRailsRoot
+
+ warn_missing_feature_category = Gitlab::Utils.to_boolean(ENV['RSPEC_WARN_MISSING_FEATURE_CATEGORY'], default: true)
+
+ # Add warning for example missing feature_category
+ config.before do |example|
+ if warn_missing_feature_category && example.metadata[:feature_category].blank? && !ENV['CI']
+ warn "Missing metadata feature_category: #{example.location} See https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#feature-category-metadata"
+ end
+ end
end
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index 67b7023f1ff..489ed89c048 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -5,9 +5,6 @@
#
---
- './ee/spec/components/billing/plan_component_spec.rb'
-- './ee/spec/components/namespaces/free_user_cap/alert_component_spec.rb'
-- './ee/spec/components/namespaces/free_user_cap/preview_alert_component_spec.rb'
-- './ee/spec/components/namespaces/free_user_cap/preview_usage_quota_alert_component_spec.rb'
- './ee/spec/components/namespaces/free_user_cap/usage_quota_alert_component_spec.rb'
- './ee/spec/components/namespaces/free_user_cap/usage_quota_trial_alert_component_spec.rb'
- './ee/spec/components/namespaces/storage/limit_alert_component_spec.rb'
@@ -137,8 +134,6 @@
- './ee/spec/controllers/projects/environments_controller_spec.rb'
- './ee/spec/controllers/projects/feature_flag_issues_controller_spec.rb'
- './ee/spec/controllers/projects/imports_controller_spec.rb'
-- './ee/spec/controllers/projects/incident_management/escalation_policies_controller_spec.rb'
-- './ee/spec/controllers/projects/incident_management/oncall_schedules_controller_spec.rb'
- './ee/spec/controllers/projects/insights_controller_spec.rb'
- './ee/spec/controllers/projects/integrations/jira/issues_controller_spec.rb'
- './ee/spec/controllers/projects/integrations/zentao/issues_controller_spec.rb'
@@ -262,7 +257,6 @@
- './ee/spec/features/boards/user_visits_board_spec.rb'
- './ee/spec/features/burndown_charts_spec.rb'
- './ee/spec/features/burnup_charts_spec.rb'
-- './ee/spec/features/ci/ci_minutes_spec.rb'
- './ee/spec/features/ci_shared_runner_settings_spec.rb'
- './ee/spec/features/ci_shared_runner_warnings_spec.rb'
- './ee/spec/features/clusters/cluster_detail_page_spec.rb'
@@ -353,8 +347,6 @@
- './ee/spec/features/groups/wiki/user_views_wiki_empty_spec.rb'
- './ee/spec/features/ide/user_commits_changes_spec.rb'
- './ee/spec/features/ide/user_opens_ide_spec.rb'
-- './ee/spec/features/incidents/incident_details_spec.rb'
-- './ee/spec/features/incidents/incidents_list_spec.rb'
- './ee/spec/features/integrations/jira/jira_issues_list_spec.rb'
- './ee/spec/features/invites_spec.rb'
- './ee/spec/features/issues/blocking_issues_spec.rb'
@@ -421,7 +413,6 @@
- './ee/spec/features/profiles/account_spec.rb'
- './ee/spec/features/profiles/billing_spec.rb'
- './ee/spec/features/profiles/password_spec.rb'
-- './ee/spec/features/profiles/usage_quotas_spec.rb'
- './ee/spec/features/profiles/user_visits_public_profile_spec.rb'
- './ee/spec/features/projects/active_tabs_spec.rb'
- './ee/spec/features/projects/audit_events_spec.rb'
@@ -592,13 +583,6 @@
- './ee/spec/finders/group_projects_finder_spec.rb'
- './ee/spec/finders/group_saml_identity_finder_spec.rb'
- './ee/spec/finders/groups_with_templates_finder_spec.rb'
-- './ee/spec/finders/incident_management/escalation_policies_finder_spec.rb'
-- './ee/spec/finders/incident_management/escalation_rules_finder_spec.rb'
-- './ee/spec/finders/incident_management/issuable_resource_links_finder_spec.rb'
-- './ee/spec/finders/incident_management/member_oncall_rotations_finder_spec.rb'
-- './ee/spec/finders/incident_management/oncall_rotations_finder_spec.rb'
-- './ee/spec/finders/incident_management/oncall_schedules_finder_spec.rb'
-- './ee/spec/finders/incident_management/oncall_users_finder_spec.rb'
- './ee/spec/finders/issues_finder_spec.rb'
- './ee/spec/finders/iterations/cadences_finder_spec.rb'
- './ee/spec/finders/iterations_finder_spec.rb'
@@ -625,8 +609,6 @@
- './ee/spec/finders/security/vulnerability_reads_finder_spec.rb'
- './ee/spec/finders/snippets_finder_spec.rb'
- './ee/spec/finders/software_license_policies_finder_spec.rb'
-- './ee/spec/finders/status_page/incident_comments_finder_spec.rb'
-- './ee/spec/finders/status_page/incidents_finder_spec.rb'
- './ee/spec/finders/template_finder_spec.rb'
- './ee/spec/finders/users_finder_spec.rb'
- './ee/spec/frontend/fixtures/analytics/charts.rb'
@@ -714,17 +696,6 @@
- './ee/spec/graphql/mutations/epics/create_spec.rb'
- './ee/spec/graphql/mutations/epics/update_spec.rb'
- './ee/spec/graphql/mutations/gitlab_subscriptions/activate_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/escalation_policy/create_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/escalation_policy/destroy_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/escalation_policy/update_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/issuable_resource_link/create_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/issuable_resource_link/destroy_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/oncall_rotation/create_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/oncall_rotation/destroy_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/oncall_rotation/update_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/oncall_schedule/create_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/oncall_schedule/destroy_spec.rb'
-- './ee/spec/graphql/mutations/incident_management/oncall_schedule/update_spec.rb'
- './ee/spec/graphql/mutations/instance_security_dashboard/add_project_spec.rb'
- './ee/spec/graphql/mutations/instance_security_dashboard/remove_project_spec.rb'
- './ee/spec/graphql/mutations/issues/create_spec.rb'
@@ -796,12 +767,6 @@
- './ee/spec/graphql/resolvers/geo/snippet_repository_registries_resolver_spec.rb'
- './ee/spec/graphql/resolvers/geo/terraform_state_version_registries_resolver_spec.rb'
- './ee/spec/graphql/resolvers/geo/upload_registries_resolver_spec.rb'
-- './ee/spec/graphql/resolvers/incident_management/escalation_policies_resolver_spec.rb'
-- './ee/spec/graphql/resolvers/incident_management/issuable_resource_links_resolver_spec.rb'
-- './ee/spec/graphql/resolvers/incident_management/oncall_rotations_resolver_spec.rb'
-- './ee/spec/graphql/resolvers/incident_management/oncall_schedule_resolver_spec.rb'
-- './ee/spec/graphql/resolvers/incident_management/oncall_shifts_resolver_spec.rb'
-- './ee/spec/graphql/resolvers/incident_management/oncall_users_resolver_spec.rb'
- './ee/spec/graphql/resolvers/instance_security_dashboard/projects_resolver_spec.rb'
- './ee/spec/graphql/resolvers/instance_security_dashboard_resolver_spec.rb'
- './ee/spec/graphql/resolvers/iterations/cadences_resolver_spec.rb'
@@ -899,16 +864,6 @@
- './ee/spec/graphql/types/group_release_stats_type_spec.rb'
- './ee/spec/graphql/types/group_stats_type_spec.rb'
- './ee/spec/graphql/types/health_status_enum_spec.rb'
-- './ee/spec/graphql/types/incident_management/escalation_policy_type_spec.rb'
-- './ee/spec/graphql/types/incident_management/escalation_rule_input_type_spec.rb'
-- './ee/spec/graphql/types/incident_management/escalation_rule_type_spec.rb'
-- './ee/spec/graphql/types/incident_management/issuable_resource_link_type_enum_spec.rb'
-- './ee/spec/graphql/types/incident_management/issuable_resource_link_type_spec.rb'
-- './ee/spec/graphql/types/incident_management/oncall_participant_type_spec.rb'
-- './ee/spec/graphql/types/incident_management/oncall_rotation_date_input_type_spec.rb'
-- './ee/spec/graphql/types/incident_management/oncall_rotation_type_spec.rb'
-- './ee/spec/graphql/types/incident_management/oncall_schedule_type_spec.rb'
-- './ee/spec/graphql/types/incident_management/oncall_shift_type_spec.rb'
- './ee/spec/graphql/types/instance_security_dashboard_type_spec.rb'
- './ee/spec/graphql/types/issue_connection_type_spec.rb'
- './ee/spec/graphql/types/issue_type_spec.rb'
@@ -1049,7 +1004,6 @@
- './ee/spec/helpers/ee/operations_helper_spec.rb'
- './ee/spec/helpers/ee/personal_access_tokens_helper_spec.rb'
- './ee/spec/helpers/ee/profiles_helper_spec.rb'
-- './ee/spec/helpers/ee/projects/incidents_helper_spec.rb'
- './ee/spec/helpers/ee/projects/pipeline_helper_spec.rb'
- './ee/spec/helpers/ee/projects/security/api_fuzzing_configuration_helper_spec.rb'
- './ee/spec/helpers/ee/projects/security/configuration_helper_spec.rb'
@@ -1074,8 +1028,6 @@
- './ee/spec/helpers/groups/ldap_sync_helper_spec.rb'
- './ee/spec/helpers/groups/security_features_helper_spec.rb'
- './ee/spec/helpers/groups/sso_helper_spec.rb'
-- './ee/spec/helpers/incident_management/escalation_policy_helper_spec.rb'
-- './ee/spec/helpers/incident_management/oncall_schedule_helper_spec.rb'
- './ee/spec/helpers/kerberos_helper_spec.rb'
- './ee/spec/helpers/license_helper_spec.rb'
- './ee/spec/helpers/license_monitoring_helper_spec.rb'
@@ -1525,7 +1477,6 @@
- './ee/spec/lib/gitlab/geo/log_cursor/daemon_spec.rb'
- './ee/spec/lib/gitlab/geo/log_cursor/event_logs_spec.rb'
- './ee/spec/lib/gitlab/geo/log_cursor/events/cache_invalidation_event_spec.rb'
-- './ee/spec/lib/gitlab/geo/log_cursor/events/container_repository_updated_event_spec.rb'
- './ee/spec/lib/gitlab/geo/log_cursor/events/design_repository_updated_event_spec.rb'
- './ee/spec/lib/gitlab/geo/log_cursor/events/event_spec.rb'
- './ee/spec/lib/gitlab/geo/log_cursor/events/hashed_storage_attachments_event_spec.rb'
@@ -1572,7 +1523,6 @@
- './ee/spec/lib/gitlab/import_export/group/relation_factory_spec.rb'
- './ee/spec/lib/gitlab/import_export/project/object_builder_spec.rb'
- './ee/spec/lib/gitlab/import_sources_spec.rb'
-- './ee/spec/lib/gitlab/incident_management_spec.rb'
- './ee/spec/lib/gitlab/ingestion/bulk_insertable_task_spec.rb'
- './ee/spec/lib/gitlab/insights/executors/dora_executor_spec.rb'
- './ee/spec/lib/gitlab/insights/executors/issuable_executor_spec.rb'
@@ -1629,14 +1579,6 @@
- './ee/spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb'
- './ee/spec/lib/gitlab/spdx/catalogue_gateway_spec.rb'
- './ee/spec/lib/gitlab/spdx/catalogue_spec.rb'
-- './ee/spec/lib/gitlab/status_page/filter/image_filter_spec.rb'
-- './ee/spec/lib/gitlab/status_page/filter/mention_anonymization_filter_spec.rb'
-- './ee/spec/lib/gitlab/status_page/pipeline/post_process_pipeline_spec.rb'
-- './ee/spec/lib/gitlab/status_page_spec.rb'
-- './ee/spec/lib/gitlab/status_page/storage/s3_client_spec.rb'
-- './ee/spec/lib/gitlab/status_page/storage/s3_multipart_upload_spec.rb'
-- './ee/spec/lib/gitlab/status_page/storage_spec.rb'
-- './ee/spec/lib/gitlab/status_page/usage_data_counters/incident_counter_spec.rb'
- './ee/spec/lib/gitlab/subscription_portal/clients/graphql_spec.rb'
- './ee/spec/lib/gitlab/subscription_portal/client_spec.rb'
- './ee/spec/lib/gitlab/subscription_portal/clients/rest_spec.rb'
@@ -1686,7 +1628,6 @@
- './ee/spec/lib/gitlab/web_ide/config/entry/schema_spec.rb'
- './ee/spec/lib/gitlab/web_ide/config/entry/schemas_spec.rb'
- './ee/spec/lib/gitlab/web_ide/config/entry/schema/uri_spec.rb'
-- './ee/spec/lib/incident_management/oncall_shift_generator_spec.rb'
- './ee/spec/lib/omni_auth/strategies/group_saml_spec.rb'
- './ee/spec/lib/omni_auth/strategies/kerberos_spec.rb'
- './ee/spec/lib/peek/views/elasticsearch_spec.rb'
@@ -1830,7 +1771,6 @@
- './ee/spec/models/concerns/geo/verifiable_model_spec.rb'
- './ee/spec/models/concerns/geo/verification_state_spec.rb'
- './ee/spec/models/concerns/health_status_spec.rb'
-- './ee/spec/models/concerns/incident_management/base_pending_escalation_spec.rb'
- './ee/spec/models/concerns/password_complexity_spec.rb'
- './ee/spec/models/concerns/scim_paginatable_spec.rb'
- './ee/spec/models/container_registry/event_spec.rb'
@@ -1874,7 +1814,6 @@
- './ee/spec/models/ee/group_group_link_spec.rb'
- './ee/spec/models/ee/groups/feature_setting_spec.rb'
- './ee/spec/models/ee/group_spec.rb'
-- './ee/spec/models/ee/incident_management/project_incident_management_setting_spec.rb'
- './ee/spec/models/ee/integrations/jira_spec.rb'
- './ee/spec/models/ee/integration_spec.rb'
- './ee/spec/models/ee/iterations/cadence_spec.rb'
@@ -1930,7 +1869,6 @@
- './ee/spec/models/geo/cache_invalidation_event_spec.rb'
- './ee/spec/models/geo/ci_secure_file_registry_spec.rb'
- './ee/spec/models/geo/container_repository_registry_spec.rb'
-- './ee/spec/models/geo/container_repository_updated_event_spec.rb'
- './ee/spec/models/geo/deleted_project_spec.rb'
- './ee/spec/models/geo/design_registry_spec.rb'
- './ee/spec/models/geo/event_log_spec.rb'
@@ -1974,16 +1912,6 @@
- './ee/spec/models/historical_data_spec.rb'
- './ee/spec/models/hooks/group_hook_spec.rb'
- './ee/spec/models/identity_spec.rb'
-- './ee/spec/models/incident_management/escalation_policy_spec.rb'
-- './ee/spec/models/incident_management/escalation_rule_spec.rb'
-- './ee/spec/models/incident_management/issuable_escalation_status_spec.rb'
-- './ee/spec/models/incident_management/issuable_resource_link_spec.rb'
-- './ee/spec/models/incident_management/oncall_participant_spec.rb'
-- './ee/spec/models/incident_management/oncall_rotation_spec.rb'
-- './ee/spec/models/incident_management/oncall_schedule_spec.rb'
-- './ee/spec/models/incident_management/oncall_shift_spec.rb'
-- './ee/spec/models/incident_management/pending_escalations/alert_spec.rb'
-- './ee/spec/models/incident_management/pending_escalations/issue_spec.rb'
- './ee/spec/models/instance_security_dashboard_spec.rb'
- './ee/spec/models/integrations/chat_message/vulnerability_message_spec.rb'
- './ee/spec/models/integrations/github/remote_project_spec.rb'
@@ -2069,8 +1997,6 @@
- './ee/spec/models/snippet_spec.rb'
- './ee/spec/models/software_license_policy_spec.rb'
- './ee/spec/models/software_license_spec.rb'
-- './ee/spec/models/status_page/project_setting_spec.rb'
-- './ee/spec/models/status_page/published_incident_spec.rb'
- './ee/spec/models/storage_shard_spec.rb'
- './ee/spec/models/uploads/local_spec.rb'
- './ee/spec/models/upload_spec.rb'
@@ -2133,9 +2059,6 @@
- './ee/spec/policies/group_hook_policy_spec.rb'
- './ee/spec/policies/group_policy_spec.rb'
- './ee/spec/policies/identity_provider_policy_spec.rb'
-- './ee/spec/policies/incident_management/oncall_rotation_policy_spec.rb'
-- './ee/spec/policies/incident_management/oncall_schedule_policy_spec.rb'
-- './ee/spec/policies/incident_management/oncall_shift_policy_spec.rb'
- './ee/spec/policies/instance_security_dashboard_policy_spec.rb'
- './ee/spec/policies/issuable_policy_spec.rb'
- './ee/spec/policies/issue_policy_spec.rb'
@@ -2267,7 +2190,6 @@
- './ee/spec/requests/api/graphql/group/epics_spec.rb'
- './ee/spec/requests/api/graphql/group/external_audit_event_destinations_spec.rb'
- './ee/spec/requests/api/graphql/group_query_spec.rb'
-- './ee/spec/requests/api/graphql/incident_management/issuable_resource_links_spec.rb'
- './ee/spec/requests/api/graphql/instance_security_dashboard_spec.rb'
- './ee/spec/requests/api/graphql/iterations/cadences_spec.rb'
- './ee/spec/requests/api/graphql/iterations/iterations_spec.rb'
@@ -2321,16 +2243,6 @@
- './ee/spec/requests/api/graphql/mutations/epics/update_spec.rb'
- './ee/spec/requests/api/graphql/mutations/epic_tree/reorder_spec.rb'
- './ee/spec/requests/api/graphql/mutations/gitlab_subscriptions/activate_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/escalation_policy/create_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/escalation_policy/destroy_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/escalation_policy/update_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/issuable_resource_link/create_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/issuable_resource_link/destroy_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/oncall_rotation/create_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/oncall_rotation/update_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/oncall_schedule/create_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/oncall_schedule/destroy_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/incident_management/oncall_schedule/update_spec.rb'
- './ee/spec/requests/api/graphql/mutations/issues/create_spec.rb'
- './ee/spec/requests/api/graphql/mutations/issues/promote_to_epic_spec.rb'
- './ee/spec/requests/api/graphql/mutations/issues/set_epic_spec.rb'
@@ -2378,11 +2290,6 @@
- './ee/spec/requests/api/graphql/project/dast_site_profile_spec.rb'
- './ee/spec/requests/api/graphql/project/dast_site_profiles_spec.rb'
- './ee/spec/requests/api/graphql/project/dast_site_validations_spec.rb'
-- './ee/spec/requests/api/graphql/project/incident_management/escalation_policies_spec.rb'
-- './ee/spec/requests/api/graphql/project/incident_management/escalation_policy/rules_spec.rb'
-- './ee/spec/requests/api/graphql/project/incident_management/oncall_participants_spec.rb'
-- './ee/spec/requests/api/graphql/project/incident_management/oncall_schedules_spec.rb'
-- './ee/spec/requests/api/graphql/project/incident_management/oncall_shifts_spec.rb'
- './ee/spec/requests/api/graphql/project/issues_spec.rb'
- './ee/spec/requests/api/graphql/project/merge_requests_spec.rb'
- './ee/spec/requests/api/graphql/project/path_locks_spec.rb'
@@ -2501,14 +2408,12 @@
- './ee/spec/requests/groups/roadmap_controller_spec.rb'
- './ee/spec/requests/groups/security/credentials_controller_spec.rb'
- './ee/spec/requests/groups/settings/reporting_controller_spec.rb'
-- './ee/spec/requests/groups/usage_quotas_spec.rb'
- './ee/spec/requests/jwt_controller_spec.rb'
- './ee/spec/requests/lfs_http_spec.rb'
- './ee/spec/requests/lfs_locks_api_spec.rb'
- './ee/spec/requests/omniauth_kerberos_spec.rb'
- './ee/spec/requests/projects/analytics/code_reviews_controller_spec.rb'
- './ee/spec/requests/projects/audit_events_spec.rb'
-- './ee/spec/requests/projects/incidents_controller_spec.rb'
- './ee/spec/requests/projects/issue_feature_flags_controller_spec.rb'
- './ee/spec/requests/projects/issues_controller_spec.rb'
- './ee/spec/requests/projects/merge_requests_controller_spec.rb'
@@ -2588,8 +2493,6 @@
- './ee/spec/serializers/fork_namespace_entity_spec.rb'
- './ee/spec/serializers/geo_project_registry_entity_spec.rb'
- './ee/spec/serializers/group_vulnerability_autocomplete_entity_spec.rb'
-- './ee/spec/serializers/incident_management/escalation_policy_entity_spec.rb'
-- './ee/spec/serializers/incident_management/oncall_schedule_entity_spec.rb'
- './ee/spec/serializers/integrations/field_entity_spec.rb'
- './ee/spec/serializers/integrations/jira_serializers/issue_detail_entity_spec.rb'
- './ee/spec/serializers/integrations/jira_serializers/issue_entity_spec.rb'
@@ -2620,10 +2523,6 @@
- './ee/spec/serializers/security/license_policy_entity_spec.rb'
- './ee/spec/serializers/security/vulnerability_report_data_entity_spec.rb'
- './ee/spec/serializers/security/vulnerability_report_data_serializer_spec.rb'
-- './ee/spec/serializers/status_page/incident_comment_entity_spec.rb'
-- './ee/spec/serializers/status_page/incident_entity_spec.rb'
-- './ee/spec/serializers/status_page/incident_serializer_spec.rb'
-- './ee/spec/serializers/status_page/renderer_spec.rb'
- './ee/spec/serializers/storage_shard_entity_spec.rb'
- './ee/spec/serializers/test_reports_comparer_entity_spec.rb'
- './ee/spec/serializers/test_reports_comparer_serializer_spec.rb'
@@ -2836,9 +2735,6 @@
- './ee/spec/services/ee/groups/deploy_tokens/revoke_service_spec.rb'
- './ee/spec/services/ee/groups/import_export/export_service_spec.rb'
- './ee/spec/services/ee/groups/import_export/import_service_spec.rb'
-- './ee/spec/services/ee/incident_management/issuable_escalation_statuses/after_update_service_spec.rb'
-- './ee/spec/services/ee/incident_management/issuable_escalation_statuses/create_service_spec.rb'
-- './ee/spec/services/ee/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb'
- './ee/spec/services/ee/integrations/test/project_service_spec.rb'
- './ee/spec/services/ee/ip_restrictions/update_service_spec.rb'
- './ee/spec/services/ee/issuable/bulk_update_service_spec.rb'
@@ -2956,7 +2852,6 @@
- './ee/spec/services/geo/cache_invalidation_event_store_spec.rb'
- './ee/spec/services/geo/container_repository_sync_service_spec.rb'
- './ee/spec/services/geo/container_repository_sync_spec.rb'
-- './ee/spec/services/geo/container_repository_updated_event_store_spec.rb'
- './ee/spec/services/geo/design_repository_sync_service_spec.rb'
- './ee/spec/services/geo/event_service_spec.rb'
- './ee/spec/services/geo/file_registry_removal_service_spec.rb'
@@ -3030,26 +2925,6 @@
- './ee/spec/services/groups/update_service_spec.rb'
- './ee/spec/services/historical_user_data/csv_service_spec.rb'
- './ee/spec/services/ide/schemas_config_service_spec.rb'
-- './ee/spec/services/incident_management/create_incident_sla_exceeded_label_service_spec.rb'
-- './ee/spec/services/incident_management/escalation_policies/create_service_spec.rb'
-- './ee/spec/services/incident_management/escalation_policies/destroy_service_spec.rb'
-- './ee/spec/services/incident_management/escalation_policies/update_service_spec.rb'
-- './ee/spec/services/incident_management/escalation_rules/destroy_service_spec.rb'
-- './ee/spec/services/incident_management/incidents/create_sla_service_spec.rb'
-- './ee/spec/services/incident_management/incidents/upload_metric_service_spec.rb'
-- './ee/spec/services/incident_management/issuable_resource_links/create_service_spec.rb'
-- './ee/spec/services/incident_management/issuable_resource_links/destroy_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_rotations/create_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_rotations/destroy_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_rotations/edit_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_rotations/remove_participant_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_rotations/remove_participants_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_schedules/create_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_schedules/destroy_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_schedules/update_service_spec.rb'
-- './ee/spec/services/incident_management/oncall_shifts/read_service_spec.rb'
-- './ee/spec/services/incident_management/pending_escalations/create_service_spec.rb'
-- './ee/spec/services/incident_management/pending_escalations/process_service_spec.rb'
- './ee/spec/services/issuable/destroy_label_links_service_spec.rb'
- './ee/spec/services/issue_feature_flags/list_service_spec.rb'
- './ee/spec/services/issues/build_service_spec.rb'
@@ -3233,13 +3108,6 @@
- './ee/spec/services/software_license_policies/create_service_spec.rb'
- './ee/spec/services/software_license_policies/update_service_spec.rb'
- './ee/spec/services/start_pull_mirroring_service_spec.rb'
-- './ee/spec/services/status_page/mark_for_publication_service_spec.rb'
-- './ee/spec/services/status_page/publish_attachments_service_spec.rb'
-- './ee/spec/services/status_page/publish_details_service_spec.rb'
-- './ee/spec/services/status_page/publish_list_service_spec.rb'
-- './ee/spec/services/status_page/publish_service_spec.rb'
-- './ee/spec/services/status_page/trigger_publish_service_spec.rb'
-- './ee/spec/services/status_page/unpublish_details_service_spec.rb'
- './ee/spec/services/system_notes/epics_service_spec.rb'
- './ee/spec/services/system_note_service_spec.rb'
- './ee/spec/services/system_notes/escalations_service_spec.rb'
@@ -3512,15 +3380,6 @@
- './ee/spec/workers/group_wikis/git_garbage_collect_worker_spec.rb'
- './ee/spec/workers/historical_data_worker_spec.rb'
- './ee/spec/workers/import_software_licenses_worker_spec.rb'
-- './ee/spec/workers/incident_management/apply_incident_sla_exceeded_label_worker_spec.rb'
-- './ee/spec/workers/incident_management/incident_sla_exceeded_check_worker_spec.rb'
-- './ee/spec/workers/incident_management/oncall_rotations/persist_all_rotations_shifts_job_spec.rb'
-- './ee/spec/workers/incident_management/oncall_rotations/persist_shifts_job_spec.rb'
-- './ee/spec/workers/incident_management/pending_escalations/alert_check_worker_spec.rb'
-- './ee/spec/workers/incident_management/pending_escalations/alert_create_worker_spec.rb'
-- './ee/spec/workers/incident_management/pending_escalations/issue_check_worker_spec.rb'
-- './ee/spec/workers/incident_management/pending_escalations/issue_create_worker_spec.rb'
-- './ee/spec/workers/incident_management/pending_escalations/schedule_check_cron_worker_spec.rb'
- './ee/spec/workers/iterations/cadences/create_iterations_worker_spec.rb'
- './ee/spec/workers/iterations/cadences/schedule_create_iterations_worker_spec.rb'
- './ee/spec/workers/iterations/roll_over_issues_worker_spec.rb'
@@ -3559,7 +3418,6 @@
- './ee/spec/workers/security/sync_scan_policies_worker_spec.rb'
- './ee/spec/workers/security/track_secure_scans_worker_spec.rb'
- './ee/spec/workers/set_user_status_based_on_user_cap_setting_worker_spec.rb'
-- './ee/spec/workers/status_page/publish_worker_spec.rb'
- './ee/spec/workers/store_security_reports_worker_spec.rb'
- './ee/spec/workers/sync_seat_link_request_worker_spec.rb'
- './ee/spec/workers/sync_seat_link_worker_spec.rb'
@@ -3787,7 +3645,6 @@
- './spec/controllers/projects/hooks_controller_spec.rb'
- './spec/controllers/projects/import/jira_controller_spec.rb'
- './spec/controllers/projects/imports_controller_spec.rb'
-- './spec/controllers/projects/incidents_controller_spec.rb'
- './spec/controllers/projects/issue_links_controller_spec.rb'
- './spec/controllers/projects/issues_controller_spec.rb'
- './spec/controllers/projects/jobs_controller_spec.rb'
@@ -4081,12 +3938,6 @@
- './spec/features/ide/user_commits_changes_spec.rb'
- './spec/features/ide/user_opens_merge_request_spec.rb'
- './spec/features/import/manifest_import_spec.rb'
-- './spec/features/incidents/incident_details_spec.rb'
-- './spec/features/incidents/incidents_list_spec.rb'
-- './spec/features/incidents/incident_timeline_events_spec.rb'
-- './spec/features/incidents/user_creates_new_incident_spec.rb'
-- './spec/features/incidents/user_filters_incidents_by_status_spec.rb'
-- './spec/features/incidents/user_searches_incidents_spec.rb'
- './spec/features/invites_spec.rb'
- './spec/features/issuables/issuable_list_spec.rb'
- './spec/features/issuables/markdown_references/internal_references_spec.rb'
@@ -4114,7 +3965,6 @@
- './spec/features/issues/form_spec.rb'
- './spec/features/issues/gfm_autocomplete_spec.rb'
- './spec/features/issues/group_label_sidebar_spec.rb'
-- './spec/features/issues/incident_issue_spec.rb'
- './spec/features/issues/issue_detail_spec.rb'
- './spec/features/issues/issue_header_spec.rb'
- './spec/features/issues/issue_sidebar_spec.rb'
@@ -4408,7 +4258,6 @@
- './spec/features/projects/integrations/user_activates_assembla_spec.rb'
- './spec/features/projects/integrations/user_activates_atlassian_bamboo_ci_spec.rb'
- './spec/features/projects/integrations/user_activates_emails_on_push_spec.rb'
-- './spec/features/projects/integrations/user_activates_flowdock_spec.rb'
- './spec/features/projects/integrations/user_activates_irker_spec.rb'
- './spec/features/projects/integrations/user_activates_issue_tracker_spec.rb'
- './spec/features/projects/integrations/user_activates_jetbrains_teamcity_ci_spec.rb'
@@ -4730,7 +4579,6 @@
- './spec/finders/groups/projects_requiring_authorizations_refresh/on_direct_membership_finder_spec.rb'
- './spec/finders/groups/projects_requiring_authorizations_refresh/on_transfer_finder_spec.rb'
- './spec/finders/groups/user_groups_finder_spec.rb'
-- './spec/finders/incident_management/timeline_events_finder_spec.rb'
- './spec/finders/issuables/crm_contact_filter_spec.rb'
- './spec/finders/issuables/crm_organization_filter_spec.rb'
- './spec/finders/issues_finder_spec.rb'
@@ -4907,10 +4755,6 @@
- './spec/graphql/mutations/discussions/toggle_resolve_spec.rb'
- './spec/graphql/mutations/environments/canary_ingress/update_spec.rb'
- './spec/graphql/mutations/groups/update_spec.rb'
-- './spec/graphql/mutations/incident_management/timeline_event/create_spec.rb'
-- './spec/graphql/mutations/incident_management/timeline_event/destroy_spec.rb'
-- './spec/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb'
-- './spec/graphql/mutations/incident_management/timeline_event/update_spec.rb'
- './spec/graphql/mutations/issues/create_spec.rb'
- './spec/graphql/mutations/issues/move_spec.rb'
- './spec/graphql/mutations/issues/set_assignees_spec.rb'
@@ -5023,7 +4867,6 @@
- './spec/graphql/resolvers/group_packages_resolver_spec.rb'
- './spec/graphql/resolvers/group_resolver_spec.rb'
- './spec/graphql/resolvers/groups_resolver_spec.rb'
-- './spec/graphql/resolvers/incident_management/timeline_events_resolver_spec.rb'
- './spec/graphql/resolvers/issues_resolver_spec.rb'
- './spec/graphql/resolvers/issue_status_counts_resolver_spec.rb'
- './spec/graphql/resolvers/kas/agent_configurations_resolver_spec.rb'
@@ -5218,8 +5061,6 @@
- './spec/graphql/types/group_member_relation_enum_spec.rb'
- './spec/graphql/types/group_member_type_spec.rb'
- './spec/graphql/types/group_type_spec.rb'
-- './spec/graphql/types/incident_management/escalation_status_enum_spec.rb'
-- './spec/graphql/types/incident_management/timeline_event_type_spec.rb'
- './spec/graphql/types/invitation_interface_spec.rb'
- './spec/graphql/types/issuable_searchable_field_enum_spec.rb'
- './spec/graphql/types/issuable_severity_enum_spec.rb'
@@ -5470,7 +5311,6 @@
- './spec/helpers/projects/cluster_agents_helper_spec.rb'
- './spec/helpers/projects/error_tracking_helper_spec.rb'
- './spec/helpers/projects_helper_spec.rb'
-- './spec/helpers/projects/incidents_helper_spec.rb'
- './spec/helpers/projects/pipeline_helper_spec.rb'
- './spec/helpers/projects/project_members_helper_spec.rb'
- './spec/helpers/projects/security/configuration_helper_spec.rb'
@@ -5739,7 +5579,6 @@
- './spec/lib/banzai/pipeline/emoji_pipeline_spec.rb'
- './spec/lib/banzai/pipeline/full_pipeline_spec.rb'
- './spec/lib/banzai/pipeline/gfm_pipeline_spec.rb'
-- './spec/lib/banzai/pipeline/incident_management/timeline_event_pipeline_spec.rb'
- './spec/lib/banzai/pipeline/jira_import/adf_commonmark_pipeline_spec.rb'
- './spec/lib/banzai/pipeline/plain_markdown_pipeline_spec.rb'
- './spec/lib/banzai/pipeline/post_process_pipeline_spec.rb'
@@ -7118,7 +6957,6 @@
- './spec/lib/gitlab/import/set_async_jid_spec.rb'
- './spec/lib/gitlab/import_sources_spec.rb'
- './spec/lib/gitlab/inactive_projects_deletion_warning_tracker_spec.rb'
-- './spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb'
- './spec/lib/gitlab/incoming_email_spec.rb'
- './spec/lib/gitlab/insecure_key_fingerprint_spec.rb'
- './spec/lib/gitlab/instrumentation_helper_spec.rb'
@@ -7598,7 +7436,6 @@
- './spec/lib/gitlab/tracking/destinations/snowplow_micro_spec.rb'
- './spec/lib/gitlab/tracking/destinations/snowplow_spec.rb'
- './spec/lib/gitlab/tracking/event_definition_spec.rb'
-- './spec/lib/gitlab/tracking/incident_management_spec.rb'
- './spec/lib/gitlab/tracking/snowplow_schema_validation_spec.rb'
- './spec/lib/gitlab/tracking_spec.rb'
- './spec/lib/gitlab/tracking/standard_context_spec.rb'
@@ -7902,7 +7739,6 @@
- './spec/migrations/20211207125331_remove_jobs_for_recalculate_vulnerabilities_occurrences_uuid_spec.rb'
- './spec/migrations/20211207135331_schedule_recalculate_uuid_on_vulnerabilities_occurrences4_spec.rb'
- './spec/migrations/20211210140629_encrypt_static_object_token_spec.rb'
-- './spec/migrations/20211214012507_backfill_incident_issue_escalation_statuses_spec.rb'
- './spec/migrations/20211217174331_mark_recalculate_finding_signatures_as_completed_spec.rb'
- './spec/migrations/20220106111958_add_insert_or_update_vulnerability_reads_trigger_spec.rb'
- './spec/migrations/20220106112043_add_update_vulnerability_reads_trigger_spec.rb'
@@ -7959,7 +7795,6 @@
- './spec/migrations/20220627090231_schedule_disable_legacy_open_source_license_for_inactive_public_projects_spec.rb'
- './spec/migrations/20220627152642_queue_update_delayed_project_removal_to_null_for_user_namespace_spec.rb'
- './spec/migrations/20220628012902_finalise_project_namespace_members_spec.rb'
-- './spec/migrations/20220629184402_unset_escalation_policies_for_alert_incidents_spec.rb'
- './spec/migrations/20220715163254_update_notes_in_past_spec.rb'
- './spec/migrations/20220721031446_schedule_disable_legacy_open_source_license_for_one_member_no_repo_projects_spec.rb'
- './spec/migrations/20220722084543_schedule_disable_legacy_open_source_license_for_no_issues_no_repo_projects_spec.rb'
@@ -8421,9 +8256,6 @@
- './spec/models/identity_spec.rb'
- './spec/models/import_export_upload_spec.rb'
- './spec/models/import_failure_spec.rb'
-- './spec/models/incident_management/issuable_escalation_status_spec.rb'
-- './spec/models/incident_management/project_incident_management_setting_spec.rb'
-- './spec/models/incident_management/timeline_event_spec.rb'
- './spec/models/instance_configuration_spec.rb'
- './spec/models/instance_metadata/kas_spec.rb'
- './spec/models/instance_metadata_spec.rb'
@@ -8455,7 +8287,6 @@
- './spec/models/integrations/ewm_spec.rb'
- './spec/models/integrations/external_wiki_spec.rb'
- './spec/models/integrations/field_spec.rb'
-- './spec/models/integrations/flowdock_spec.rb'
- './spec/models/integrations/hangouts_chat_spec.rb'
- './spec/models/integrations/harbor_spec.rb'
- './spec/models/integrations/irker_spec.rb'
@@ -8795,7 +8626,6 @@
- './spec/policies/group_member_policy_spec.rb'
- './spec/policies/group_policy_spec.rb'
- './spec/policies/identity_provider_policy_spec.rb'
-- './spec/policies/incident_management/timeline_event_policy_spec.rb'
- './spec/policies/instance_metadata_policy_spec.rb'
- './spec/policies/integration_policy_spec.rb'
- './spec/policies/issuable_policy_spec.rb'
@@ -9067,10 +8897,6 @@
- './spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb'
- './spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb'
- './spec/requests/api/graphql/mutations/groups/update_spec.rb'
-- './spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb'
-- './spec/requests/api/graphql/mutations/incident_management/timeline_event/destroy_spec.rb'
-- './spec/requests/api/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb'
-- './spec/requests/api/graphql/mutations/incident_management/timeline_event/update_spec.rb'
- './spec/requests/api/graphql/mutations/issues/create_spec.rb'
- './spec/requests/api/graphql/mutations/issues/move_spec.rb'
- './spec/requests/api/graphql/mutations/issues/set_confidential_spec.rb'
@@ -9164,7 +8990,6 @@
- './spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb'
- './spec/requests/api/graphql/project/fork_targets_spec.rb'
- './spec/requests/api/graphql/project/grafana_integration_spec.rb'
-- './spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb'
- './spec/requests/api/graphql/project/issue/design_collection/version_spec.rb'
- './spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb'
- './spec/requests/api/graphql/project/issue/designs/designs_spec.rb'
@@ -9395,7 +9220,6 @@
- './spec/requests/projects/harbor/artifacts_controller_spec.rb'
- './spec/requests/projects/harbor/repositories_controller_spec.rb'
- './spec/requests/projects/harbor/tags_controller_spec.rb'
-- './spec/requests/projects/incident_management/pagerduty_incidents_spec.rb'
- './spec/requests/projects/integrations/shimos_controller_spec.rb'
- './spec/requests/projects/issue_links_controller_spec.rb'
- './spec/requests/projects/issues_controller_spec.rb'
@@ -9981,15 +9805,6 @@
- './spec/services/import/gitlab_projects/file_acquisition_strategies/remote_file_spec.rb'
- './spec/services/import/prepare_service_spec.rb'
- './spec/services/import/validate_remote_git_endpoint_service_spec.rb'
-- './spec/services/incident_management/incidents/create_service_spec.rb'
-- './spec/services/incident_management/issuable_escalation_statuses/after_update_service_spec.rb'
-- './spec/services/incident_management/issuable_escalation_statuses/build_service_spec.rb'
-- './spec/services/incident_management/issuable_escalation_statuses/create_service_spec.rb'
-- './spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb'
-- './spec/services/incident_management/pager_duty/process_webhook_service_spec.rb'
-- './spec/services/incident_management/timeline_events/create_service_spec.rb'
-- './spec/services/incident_management/timeline_events/destroy_service_spec.rb'
-- './spec/services/incident_management/timeline_events/update_service_spec.rb'
- './spec/services/integrations/propagate_service_spec.rb'
- './spec/services/integrations/test/project_service_spec.rb'
- './spec/services/issuable/bulk_update_service_spec.rb'
@@ -10379,8 +10194,6 @@
- './spec/services/system_notes/commit_service_spec.rb'
- './spec/services/system_notes/design_management_service_spec.rb'
- './spec/services/system_note_service_spec.rb'
-- './spec/services/system_notes/incident_service_spec.rb'
-- './spec/services/system_notes/incidents_service_spec.rb'
- './spec/services/system_notes/issuables_service_spec.rb'
- './spec/services/system_notes/merge_requests_service_spec.rb'
- './spec/services/system_notes/time_tracking_service_spec.rb'
@@ -10599,7 +10412,6 @@
- './spec/uploaders/records_uploads_spec.rb'
- './spec/uploaders/terraform/state_uploader_spec.rb'
- './spec/uploaders/uploader_helper_spec.rb'
-- './spec/uploaders/workers/object_storage/background_move_worker_spec.rb'
- './spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb'
- './spec/validators/addressable_url_validator_spec.rb'
- './spec/validators/any_field_validator_spec.rb'
@@ -10945,10 +10757,6 @@
- './spec/workers/hashed_storage/project_rollback_worker_spec.rb'
- './spec/workers/hashed_storage/rollbacker_worker_spec.rb'
- './spec/workers/import_issues_csv_worker_spec.rb'
-- './spec/workers/incident_management/add_severity_system_note_worker_spec.rb'
-- './spec/workers/incident_management/close_incident_worker_spec.rb'
-- './spec/workers/incident_management/pager_duty/process_incident_worker_spec.rb'
-- './spec/workers/incident_management/process_alert_worker_v2_spec.rb'
- './spec/workers/integrations/create_external_cross_reference_worker_spec.rb'
- './spec/workers/integrations/execute_worker_spec.rb'
- './spec/workers/integrations/irker_worker_spec.rb'
diff --git a/spec/support/shared_contexts/disable_user_tracking.rb b/spec/support/shared_contexts/disable_user_tracking.rb
new file mode 100644
index 00000000000..e6689c41d4a
--- /dev/null
+++ b/spec/support/shared_contexts/disable_user_tracking.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'when user tracking is disabled' do
+ before do
+ # rubocop:disable RSpec/AnyInstanceOf
+ allow_any_instance_of(User).to receive(:update_tracked_fields!)
+ allow_any_instance_of(Users::ActivityService).to receive(:execute)
+ # rubocop:enable RSpec/AnyInstanceOf
+ end
+end
diff --git a/spec/support/shared_contexts/email_shared_context.rb b/spec/support/shared_contexts/email_shared_context.rb
index 086cdf50e9d..12d4af5170b 100644
--- a/spec/support/shared_contexts/email_shared_context.rb
+++ b/spec/support/shared_contexts/email_shared_context.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_context :email_shared_context do
+RSpec.shared_context 'email shared context' do
let(:mail_key) { '59d8df8370b7e95c5a49fbf86aeb2c93' }
let(:receiver) { Gitlab::Email::Receiver.new(email_raw) }
let(:markdown) { '![image](uploads/image.png)' }
@@ -27,7 +27,7 @@ def service_desk_fixture(path, slug: nil, key: 'mykey')
fixture_file(path).gsub('project_slug', slug).gsub('project_key', key)
end
-RSpec.shared_examples :reply_processing_shared_examples do
+RSpec.shared_examples 'reply processing shared examples' do
context 'when the user could not be found' do
before do
user.destroy!
@@ -49,7 +49,7 @@ RSpec.shared_examples :reply_processing_shared_examples do
end
end
-RSpec.shared_examples :checks_permissions_on_noteable_examples do
+RSpec.shared_examples 'checks permissions on noteable examples' do
context 'when user has access' do
before do
project.add_reporter(user)
@@ -67,7 +67,7 @@ RSpec.shared_examples :checks_permissions_on_noteable_examples do
end
end
-RSpec.shared_examples :note_handler_shared_examples do |forwardable|
+RSpec.shared_examples 'note handler shared examples' do |forwardable|
context 'when the noteable could not be found' do
before do
noteable.destroy!
@@ -157,7 +157,7 @@ RSpec.shared_examples :note_handler_shared_examples do |forwardable|
noteable.update_attribute(:discussion_locked, true)
end
- it_behaves_like :checks_permissions_on_noteable_examples
+ it_behaves_like 'checks permissions on noteable examples'
end
context 'when everything is fine' do
diff --git a/spec/support/shared_contexts/models/ci/job_token_scope.rb b/spec/support/shared_contexts/models/ci/job_token_scope.rb
new file mode 100644
index 00000000000..51f671b139d
--- /dev/null
+++ b/spec/support/shared_contexts/models/ci/job_token_scope.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'with scoped projects' do
+ let_it_be(:inbound_scoped_project) { create_scoped_project(source_project, direction: :inbound) }
+ let_it_be(:outbound_scoped_project) { create_scoped_project(source_project, direction: :outbound) }
+ let_it_be(:unscoped_project1) { create(:project) }
+ let_it_be(:unscoped_project2) { create(:project) }
+
+ let_it_be(:link_out_of_scope) { create(:ci_job_token_project_scope_link, target_project: unscoped_project1) }
+
+ def create_scoped_project(source_project, direction:)
+ create(:project).tap do |scoped_project|
+ create(
+ :ci_job_token_project_scope_link,
+ source_project: source_project,
+ target_project: scoped_project,
+ direction: direction
+ )
+ end
+ end
+end
diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
index a6226fe903b..f6ac98c7669 100644
--- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
@@ -14,7 +14,7 @@ RSpec.shared_context 'GroupPolicy context' do
%i[
read_group read_counts
read_label read_issue_board_list read_milestone read_issue_board
- ]
+ ]
end
let(:guest_permissions) do
@@ -22,32 +22,32 @@ RSpec.shared_context 'GroupPolicy context' do
read_label read_group upload_file read_namespace read_group_activity
read_group_issues read_group_boards read_group_labels read_group_milestones
read_group_merge_requests
- ]
+ ]
end
let(:reporter_permissions) do
%i[
- admin_label
- admin_milestone
- admin_issue_board
- read_container_image
- read_harbor_registry
- read_metrics_dashboard_annotation
- read_prometheus
- read_crm_contact
- read_crm_organization
- ]
+ admin_label
+ admin_milestone
+ admin_issue_board
+ read_container_image
+ read_harbor_registry
+ read_metrics_dashboard_annotation
+ read_prometheus
+ read_crm_contact
+ read_crm_organization
+ ]
end
let(:developer_permissions) do
%i[
- create_metrics_dashboard_annotation
- delete_metrics_dashboard_annotation
- update_metrics_dashboard_annotation
- create_custom_emoji
- create_package
- read_cluster
- ]
+ create_metrics_dashboard_annotation
+ delete_metrics_dashboard_annotation
+ update_metrics_dashboard_annotation
+ create_custom_emoji
+ create_package
+ read_cluster
+ ]
end
let(:maintainer_permissions) do
diff --git a/spec/support/shared_contexts/rack_attack_shared_context.rb b/spec/support/shared_contexts/rack_attack_shared_context.rb
index e7b2ee76c3c..12625ead72b 100644
--- a/spec/support/shared_contexts/rack_attack_shared_context.rb
+++ b/spec/support/shared_contexts/rack_attack_shared_context.rb
@@ -6,7 +6,7 @@ RSpec.shared_context 'rack attack cache store' do
Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
# Make time-dependent tests deterministic
- Timecop.freeze { example.run }
+ freeze_time { example.run }
Rack::Attack.cache.store = Rails.cache
end
diff --git a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb
index 3974338238a..7c37e5189f1 100644
--- a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb
+++ b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb
@@ -28,6 +28,10 @@ RSpec.shared_context 'conan api setup' do
)
end
+ let(:snowplow_gitlab_standard_context) do
+ { user: user, project: project, namespace: project.namespace, property: 'i_package_conan_user' }
+ end
+
before do
project.add_developer(user)
allow(Settings).to receive(:attr_encrypted_db_key_base).and_return(base_secret)
diff --git a/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb b/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb
index 89f290d8d68..1e50505162d 100644
--- a/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb
+++ b/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb
@@ -17,6 +17,7 @@ RSpec.shared_context 'npm api setup' do
let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) }
let(:package_name) { package.name }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_npm_user' } }
before do
# create a duplicated package without triggering model validation errors
diff --git a/spec/support/shared_contexts/rubocop_default_rspec_language_config_context.rb b/spec/support/shared_contexts/rubocop_default_rspec_language_config_context.rb
deleted file mode 100644
index a207c6ae9d1..00000000000
--- a/spec/support/shared_contexts/rubocop_default_rspec_language_config_context.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-# From https://github.com/rubocop/rubocop-rspec/blob/master/spec/shared/default_rspec_language_config_context.rb
-# This can be removed once we have https://github.com/rubocop/rubocop-rspec/pull/1377
-
-RSpec.shared_context 'with default RSpec/Language config' do
- include_context 'config'
-
- # Deep duplication is needed to prevent config leakage between examples
- let(:other_cops) do
- default_language = RuboCop::ConfigLoader
- .default_configuration['RSpec']['Language']
- default_include = RuboCop::ConfigLoader
- .default_configuration['RSpec']['Include']
- { 'RSpec' =>
- {
- 'Include' => default_include,
- 'Language' => deep_dup(default_language)
- } }
- end
-
- def deep_dup(object)
- case object
- when Array
- object.map { |item| deep_dup(item) }
- when Hash
- object.transform_values { |value| deep_dup(value) }
- else
- object # only collections undergo modifications and need duping
- end
- end
-end
diff --git a/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb b/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb
index e26b8cd8b37..7db479bcfd2 100644
--- a/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb
+++ b/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb
@@ -23,7 +23,7 @@ RSpec.shared_context 'container repository delete tags service shared context' d
end
def stub_delete_reference_requests(tags)
- tags = Array.wrap(tags).to_h { |tag| [tag, 200] } unless tags.is_a?(Hash)
+ tags = Array.wrap(tags).index_with { 200 } unless tags.is_a?(Hash)
tags.each do |tag, status|
stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/tags/reference/#{tag}")
diff --git a/spec/support/shared_examples/boards/destroy_service_shared_examples.rb b/spec/support/shared_examples/boards/destroy_service_shared_examples.rb
index b1cb58a736f..578ed0e6260 100644
--- a/spec/support/shared_examples/boards/destroy_service_shared_examples.rb
+++ b/spec/support/shared_examples/boards/destroy_service_shared_examples.rb
@@ -15,7 +15,7 @@ RSpec.shared_examples 'board destroy service' do
expect do
expect(service.execute(board)).to be_success
- end.to change(boards, :count).by(-1)
+ end.to change { boards.count }.by(-1)
end
end
@@ -23,7 +23,7 @@ RSpec.shared_examples 'board destroy service' do
it 'does remove board' do
expect do
service.execute(board)
- end.to change(boards, :count).by(-1)
+ end.to change { boards.count }.by(-1)
end
end
end
diff --git a/spec/support/shared_examples/ci/log_downstream_pipeline_shared_examples.rb b/spec/support/shared_examples/ci/log_downstream_pipeline_shared_examples.rb
index db724dcfe99..0c3b9ec4151 100644
--- a/spec/support/shared_examples/ci/log_downstream_pipeline_shared_examples.rb
+++ b/spec/support/shared_examples/ci/log_downstream_pipeline_shared_examples.rb
@@ -13,10 +13,8 @@ RSpec.shared_examples 'logs downstream pipeline creation' do
end
it 'logs details' do
- pipeline = nil
-
log_entry = record_downstream_pipeline_logs do
- pipeline = subject
+ downstream_pipeline
end
expect(log_entry).to be_present
@@ -24,7 +22,7 @@ RSpec.shared_examples 'logs downstream pipeline creation' do
message: "downstream pipeline created",
class: described_class.name,
root_pipeline_id: expected_root_pipeline.id,
- downstream_pipeline_id: pipeline.id,
+ downstream_pipeline_id: downstream_pipeline.id,
downstream_pipeline_relationship: expected_downstream_relationship,
hierarchy_size: expected_hierarchy_size,
root_pipeline_plan: expected_root_pipeline.project.actual_plan_name,
diff --git a/spec/support/shared_examples/ci/retryable_shared_examples.rb b/spec/support/shared_examples/ci/retryable_shared_examples.rb
index 4622dbe4e31..dc34ea8bb3c 100644
--- a/spec/support/shared_examples/ci/retryable_shared_examples.rb
+++ b/spec/support/shared_examples/ci/retryable_shared_examples.rb
@@ -10,7 +10,7 @@ RSpec.shared_examples 'a retryable job' do
describe '#set_enqueue_immediately!' do
it 'changes #enqueue_immediately? to true' do
expect { subject.set_enqueue_immediately! }
- .to change(subject, :enqueue_immediately?).from(false).to(true)
+ .to change { subject.enqueue_immediately? }.from(false).to(true)
end
end
end
diff --git a/spec/support/shared_examples/ci/stuck_builds_shared_examples.rb b/spec/support/shared_examples/ci/stuck_builds_shared_examples.rb
index 4fcea18393c..dd0a57c6b6d 100644
--- a/spec/support/shared_examples/ci/stuck_builds_shared_examples.rb
+++ b/spec/support/shared_examples/ci/stuck_builds_shared_examples.rb
@@ -30,6 +30,6 @@ end
RSpec.shared_examples 'job is unchanged' do
it 'does not change status' do
- expect { service.execute }.not_to change(job, :status)
+ expect { service.execute }.not_to change { job.status }
end
end
diff --git a/spec/support/shared_examples/controllers/destroy_hook_shared_examples.rb b/spec/support/shared_examples/controllers/destroy_hook_shared_examples.rb
index 710aa333dec..420973b6882 100644
--- a/spec/support/shared_examples/controllers/destroy_hook_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/destroy_hook_shared_examples.rb
@@ -9,7 +9,7 @@ RSpec.shared_examples 'Web hook destroyer' do
delete :destroy, params: params
expect(response).to have_gitlab_http_status(:found)
- expect(flash[:notice]).to eq("#{hook.model_name.human} was deleted")
+ expect(flash[:notice]).to eq('Webhook was deleted')
end
it 'displays a message about async delete', :aggregate_failures do
@@ -20,7 +20,7 @@ RSpec.shared_examples 'Web hook destroyer' do
delete :destroy, params: params
expect(response).to have_gitlab_http_status(:found)
- expect(flash[:notice]).to eq("#{hook.model_name.human} was scheduled for deletion")
+ expect(flash[:notice]).to eq('Webhook was scheduled for deletion')
end
it 'displays an error if deletion failed', :aggregate_failures do
diff --git a/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb b/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
index bbbe93a644f..5506b05ca55 100644
--- a/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
@@ -356,7 +356,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).and_return(double(execute: project))
- expect { post :create, params: { target_namespace: provider_repo[:name] }, format: :json }.to change(Namespace, :count).by(1)
+ expect { post :create, params: { target_namespace: provider_repo[:name] }, format: :json }.to change { Namespace.count }.by(1)
end
it "takes the new namespace" do
@@ -377,7 +377,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).and_return(double(execute: project))
- expect { post :create, format: :json }.not_to change(Namespace, :count)
+ expect { post :create, format: :json }.not_to change { Namespace.count }
end
it "takes the current user's namespace" do
diff --git a/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb b/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb
index 6749ebd471f..7e99066110d 100644
--- a/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb
+++ b/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb
@@ -16,12 +16,14 @@
RSpec.shared_examples 'Snowplow event tracking' do |overrides: {}|
let(:extra) { {} }
- it 'is not emitted if FF is disabled' do
- stub_feature_flags(feature_flag_name => false)
+ if try(:feature_flag_name)
+ it 'is not emitted if FF is disabled' do
+ stub_feature_flags(feature_flag_name => false)
- subject
+ subject
- expect_no_snowplow_event(category: category, action: action)
+ expect_no_snowplow_event(category: category, action: action)
+ end
end
it 'is emitted' do
diff --git a/spec/support/shared_examples/controllers/variables_shared_examples.rb b/spec/support/shared_examples/controllers/variables_shared_examples.rb
index 34632993cf0..d979683cce7 100644
--- a/spec/support/shared_examples/controllers/variables_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/variables_shared_examples.rb
@@ -19,13 +19,15 @@ RSpec.shared_examples 'PATCH #update updates variables' do
{ id: variable.id,
key: variable.key,
secret_value: variable.value,
- protected: variable.protected?.to_s }
+ protected: variable.protected?.to_s,
+ raw: (!variable.raw?).to_s }
end
let(:new_variable_attributes) do
{ key: 'new_key',
secret_value: 'dummy_value',
- protected: 'false' }
+ protected: 'false',
+ raw: 'true' }
end
let(:variables_scope) { owner.variables }
@@ -86,7 +88,13 @@ RSpec.shared_examples 'PATCH #update updates variables' do
end
it 'updates the existing variable' do
- expect { subject }.to change { variable.reload.value }.to('other_value')
+ old_raw = variable.raw
+
+ subject
+
+ variable.reload
+ expect(variable.value).to eq('other_value')
+ expect(variable.raw?).not_to be(old_raw)
end
it 'creates the new variable' do
diff --git a/spec/support/shared_examples/csp.rb b/spec/support/shared_examples/csp.rb
index 91242ae9f37..725d0a832c2 100644
--- a/spec/support/shared_examples/csp.rb
+++ b/spec/support/shared_examples/csp.rb
@@ -29,7 +29,8 @@ RSpec.shared_examples 'setting CSP' do |rule_name|
context 'when feature is enabled' do
it "appends to #{rule_name}" do
- is_expected.to eql("#{rule_name} #{default_csp_values} #{allowlisted_url}")
+ is_expected.to include("#{rule_name} #{default_csp_values}")
+ is_expected.to include(allowlisted_url)
end
end
@@ -37,7 +38,7 @@ RSpec.shared_examples 'setting CSP' do |rule_name|
include_context 'disable feature'
it "keeps original #{rule_name}" do
- is_expected.to eql("#{rule_name} #{default_csp_values}")
+ is_expected.to include("#{rule_name} #{default_csp_values}")
end
end
end
@@ -47,7 +48,8 @@ RSpec.shared_examples 'setting CSP' do |rule_name|
context 'when feature is enabled' do
it "uses default-src values in #{rule_name}" do
- is_expected.to eql("default-src #{default_csp_values}; #{rule_name} #{default_csp_values} #{allowlisted_url}")
+ is_expected.to include("default-src #{default_csp_values}")
+ is_expected.to include(allowlisted_url)
end
end
@@ -55,7 +57,7 @@ RSpec.shared_examples 'setting CSP' do |rule_name|
include_context 'disable feature'
it "does not add #{rule_name}" do
- is_expected.to eql("default-src #{default_csp_values}")
+ is_expected.to include("default-src #{default_csp_values}")
end
end
end
@@ -65,7 +67,8 @@ RSpec.shared_examples 'setting CSP' do |rule_name|
context 'when feature is enabled' do
it "uses default-src values in #{rule_name}" do
- is_expected.to eql("font-src #{default_csp_values}; #{rule_name} #{allowlisted_url}")
+ is_expected.to include("font-src #{default_csp_values}")
+ is_expected.not_to include("#{rule_name} #{default_csp_values}")
end
end
@@ -73,7 +76,7 @@ RSpec.shared_examples 'setting CSP' do |rule_name|
include_context 'disable feature'
it "does not add #{rule_name}" do
- is_expected.to eql("font-src #{default_csp_values}")
+ is_expected.to include("font-src #{default_csp_values}")
end
end
end
diff --git a/spec/support/shared_examples/features/container_registry_shared_examples.rb b/spec/support/shared_examples/features/container_registry_shared_examples.rb
index 784f82fdda1..06b2b8c621c 100644
--- a/spec/support/shared_examples/features/container_registry_shared_examples.rb
+++ b/spec/support/shared_examples/features/container_registry_shared_examples.rb
@@ -7,19 +7,3 @@ RSpec.shared_examples 'handling feature network errors with the container regist
expect(page).to have_content 'We are having trouble connecting to the Container Registry'
end
end
-
-RSpec.shared_examples 'rejecting tags destruction for an importing repository on' do |tags: []|
- it 'rejects the tag destruction operation' do
- service = instance_double('Projects::ContainerRepository::DeleteTagsService')
- expect(service).to receive(:execute).with(container_repository) { { status: :error, message: 'repository importing' } }
- expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(container_repository.project, user, tags: tags) { service }
-
- first('[data-testid="additional-actions"]').click
- first('[data-testid="single-delete-button"]').click
- expect(find('.modal .modal-title')).to have_content _('Remove tag')
- find('.modal .modal-footer .btn-danger').click
-
- expect(page).to have_content('Tags temporarily cannot be marked for deletion. Please try again in a few minutes.')
- expect(page).to have_link('More details', href: help_page_path('user/packages/container_registry/index', anchor: 'tags-temporarily-cannot-be-marked-for-deletion'))
- end
-end
diff --git a/spec/support/shared_examples/features/content_editor_shared_examples.rb b/spec/support/shared_examples/features/content_editor_shared_examples.rb
index f01e3c88dad..efdf7513b2d 100644
--- a/spec/support/shared_examples/features/content_editor_shared_examples.rb
+++ b/spec/support/shared_examples/features/content_editor_shared_examples.rb
@@ -327,7 +327,7 @@ RSpec.shared_examples 'edits content using the content editor' do
end
def dropdown_scroll_top
- evaluate_script("document.querySelector('#{suggestions_dropdown} .gl-new-dropdown-inner').scrollTop")
+ evaluate_script("document.querySelector('#{suggestions_dropdown} .gl-dropdown-inner').scrollTop")
end
end
end
diff --git a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
index 2cfe353d5d7..7f31ea8f9be 100644
--- a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
@@ -64,7 +64,7 @@ RSpec.shared_examples 'a creatable merge request' do
visit project_new_merge_request_path(source_project)
first('.js-target-project').click
- find('.dropdown-target-project .dropdown-content a', text: target_project.full_path).click
+ find('.dropdown-target-project li', text: target_project.full_path).click
wait_for_requests
diff --git a/spec/support/shared_examples/features/discussion_comments_shared_example.rb b/spec/support/shared_examples/features/discussion_comments_shared_example.rb
index 68c0d06e7d0..d6f1efc09fc 100644
--- a/spec/support/shared_examples/features/discussion_comments_shared_example.rb
+++ b/spec/support/shared_examples/features/discussion_comments_shared_example.rb
@@ -19,6 +19,8 @@ RSpec.shared_examples 'thread comments for commit and snippet' do |resource_name
find('.js-comment-button').click
+ wait_for_all_requests
+
expect(page).to have_content(comment)
new_comment = all(comments_selector).last
@@ -301,7 +303,7 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re
if resource_name == 'merge request'
let(:note_id) { find("#{comments_selector} .note:first-child", match: :first)['data-note-id'] }
- let(:reply_id) { find("#{comments_selector} .note:last-of-type", match: :first)['data-note-id'] }
+ let(:reply_id) { all("#{comments_selector} [data-note-id]")[1]['data-note-id'] }
it 'can be replied to after resolving' do
find('button[data-testid="resolve-discussion-button"]').click
diff --git a/spec/support/shared_examples/features/inviting_members_shared_examples.rb b/spec/support/shared_examples/features/inviting_members_shared_examples.rb
index 277ec6a7fa7..2eca2a72997 100644
--- a/spec/support/shared_examples/features/inviting_members_shared_examples.rb
+++ b/spec/support/shared_examples/features/inviting_members_shared_examples.rb
@@ -81,7 +81,7 @@ RSpec.shared_examples 'inviting members' do |snowplow_invite_label|
invite_member(user2.name, role: 'Developer')
- invite_member(user2.name, role: 'Reporter', refresh: false)
+ invite_member(user2.name, role: 'Reporter')
expect(page).not_to have_selector(invite_modal_selector)
@@ -101,7 +101,7 @@ RSpec.shared_examples 'inviting members' do |snowplow_invite_label|
invite_member(email, role: 'Developer')
- invite_member(email, role: 'Reporter', refresh: false)
+ invite_member(email, role: 'Reporter')
expect(page).not_to have_selector(invite_modal_selector)
@@ -127,7 +127,7 @@ RSpec.shared_examples 'inviting members' do |snowplow_invite_label|
it 'adds the user as a member on sub-entity with higher access level', :js do
visit subentity_members_page_path
- invite_member(user2.name, role: role, refresh: false)
+ invite_member(user2.name, role: role)
expect(page).not_to have_selector(invite_modal_selector)
@@ -145,7 +145,7 @@ RSpec.shared_examples 'inviting members' do |snowplow_invite_label|
it 'fails with an error', :js do
visit subentity_members_page_path
- invite_member(user2.name, role: role, refresh: false)
+ invite_member(user2.name, role: role)
invite_modal = page.find(invite_modal_selector)
expect(invite_modal).to have_content "#{user2.name}: Access level should be greater than or equal to " \
@@ -177,7 +177,7 @@ RSpec.shared_examples 'inviting members' do |snowplow_invite_label|
visit subentity_members_page_path
- invite_member([user2.name, user3.name, user4.name, user6.name, user7.name], role: role, refresh: false)
+ invite_member([user2.name, user3.name, user4.name, user6.name, user7.name], role: role)
# we have more than 2 errors, so one will be hidden
invite_modal = page.find(invite_modal_selector)
@@ -266,7 +266,7 @@ RSpec.shared_examples 'inviting members' do |snowplow_invite_label|
it 'only shows the error for an invalid formatted email and does not display other member errors', :js do
visit subentity_members_page_path
- invite_member([user2.name, user3.name, 'bad@email'], role: role, refresh: false)
+ invite_member([user2.name, user3.name, 'bad@email'], role: role)
invite_modal = page.find(invite_modal_selector)
expect(invite_modal).to have_text('email contains an invalid email address')
diff --git a/spec/support/shared_examples/features/reportable_note_shared_examples.rb b/spec/support/shared_examples/features/reportable_note_shared_examples.rb
index 288e1df9b2a..c35f711111b 100644
--- a/spec/support/shared_examples/features/reportable_note_shared_examples.rb
+++ b/spec/support/shared_examples/features/reportable_note_shared_examples.rb
@@ -20,7 +20,7 @@ RSpec.shared_examples 'reportable note' do |type|
dropdown = comment.find(more_actions_selector)
open_dropdown(dropdown)
- expect(dropdown).to have_link('Report abuse to admin', href: abuse_report_path)
+ expect(dropdown).to have_link('Report abuse to administrator', href: abuse_report_path)
if type == 'issue' || type == 'merge_request'
expect(dropdown).to have_button('Delete comment')
@@ -33,7 +33,7 @@ RSpec.shared_examples 'reportable note' do |type|
dropdown = comment.find(more_actions_selector)
open_dropdown(dropdown)
- dropdown.click_link('Report abuse to admin')
+ dropdown.click_link('Report abuse to administrator')
expect(find('#user_name')['value']).to match(note.author.username)
expect(find('#abuse_report_message')['value']).to match(noteable_note_url(note))
diff --git a/spec/support/shared_examples/features/runners_shared_examples.rb b/spec/support/shared_examples/features/runners_shared_examples.rb
index a7bc19da45f..20078243cfb 100644
--- a/spec/support/shared_examples/features/runners_shared_examples.rb
+++ b/spec/support/shared_examples/features/runners_shared_examples.rb
@@ -63,10 +63,13 @@ RSpec.shared_examples 'shows and resets runner registration token' do
end
RSpec.shared_examples 'shows no runners registered' do
- it 'shows counts with 0' do
- expect(page).to have_text "#{s_('Runners|Online')} 0"
- expect(page).to have_text "#{s_('Runners|Offline')} 0"
- expect(page).to have_text "#{s_('Runners|Stale')} 0"
+ it 'shows total count with 0' do
+ expect(find('[data-testid="runner-type-tabs"]')).to have_text "#{s_('Runners|All')} 0"
+
+ # No stats are shown
+ expect(page).not_to have_text s_('Runners|Online')
+ expect(page).not_to have_text s_('Runners|Offline')
+ expect(page).not_to have_text s_('Runners|Stale')
end
it 'shows "no runners" message' do
diff --git a/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb b/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb
index cc74c977064..b73f40ff28c 100644
--- a/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb
+++ b/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb
@@ -6,7 +6,7 @@ RSpec.shared_examples 'search timeouts' do |scope|
context 'when search times out' do
before do
allow_next_instance_of(SearchService) do |service|
- allow(service).to receive(:search_objects).and_raise(ActiveRecord::QueryCanceled)
+ allow(service).to receive(:search_results).and_raise(ActiveRecord::QueryCanceled)
end
visit(search_path(search: 'test', scope: scope, **additional_params))
diff --git a/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb b/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb
index 281a70e46c4..95b306fdaaa 100644
--- a/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb
+++ b/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb
@@ -17,7 +17,7 @@ RSpec.shared_examples 'labels sidebar widget' do
end
it 'shows labels list in the dropdown' do
- expect(labels_widget.find('.gl-new-dropdown-contents')).to have_selector('li.gl-new-dropdown-item', count: 4)
+ expect(labels_widget.find('.gl-dropdown-contents')).to have_selector('li.gl-dropdown-item', count: 4)
end
it 'adds a label' do
@@ -57,8 +57,8 @@ RSpec.shared_examples 'labels sidebar widget' do
expect(page).to have_css('.labels-fetch-loading')
wait_for_all_requests
- expect(page).to have_css('[data-testid="dropdown-content"] .gl-new-dropdown-item')
- expect(page.all(:css, '[data-testid="dropdown-content"] .gl-new-dropdown-item').length).to eq(1)
+ expect(page).to have_css('[data-testid="dropdown-content"] .gl-dropdown-item')
+ expect(page.all(:css, '[data-testid="dropdown-content"] .gl-dropdown-item').length).to eq(1)
find_field('Search').native.send_keys(:enter)
click_button 'Close'
diff --git a/spec/support/shared_examples/features/sidebar/sidebar_milestone_shared_examples.rb b/spec/support/shared_examples/features/sidebar/sidebar_milestone_shared_examples.rb
index da730240e8e..f8982c242f8 100644
--- a/spec/support/shared_examples/features/sidebar/sidebar_milestone_shared_examples.rb
+++ b/spec/support/shared_examples/features/sidebar/sidebar_milestone_shared_examples.rb
@@ -20,15 +20,15 @@ RSpec.shared_examples 'milestone sidebar widget' do
it 'shows milestones list in the dropdown' do
# 5 milestones + "No milestone" = 6 items
- expect(milestone_widget.find('.gl-new-dropdown-contents')).to have_selector('li.gl-new-dropdown-item', count: 6)
+ expect(milestone_widget.find('.gl-dropdown-contents')).to have_selector('li.gl-dropdown-item', count: 6)
end
it 'shows expired milestone at the bottom of the list and milestone due earliest at the top of the list', :aggregate_failures do
- within(milestone_widget, '.gl-new-dropdown-contents') do
+ within(milestone_widget, '.gl-dropdown-contents') do
expect(page.find('li:last-child')).to have_content milestone_expired.title
[milestone3, milestone2, milestone1, milestone_no_duedate].each_with_index do |m, i|
- expect(page.all('li.gl-new-dropdown-item')[i + 1]).to have_content m.title
+ expect(page.all('li.gl-dropdown-item')[i + 1]).to have_content m.title
end
end
end
diff --git a/spec/support/shared_examples/features/wiki/file_attachments_shared_examples.rb b/spec/support/shared_examples/features/wiki/file_attachments_shared_examples.rb
index 8d1502bed84..7a3b94ad81d 100644
--- a/spec/support/shared_examples/features/wiki/file_attachments_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/file_attachments_shared_examples.rb
@@ -55,7 +55,7 @@ RSpec.shared_examples 'wiki file attachments' do
wait_for_requests
expect(page.find('#wiki_content').value)
- .to match(%r{\!\[dk\]\(uploads/\h{32}/dk\.png\)$})
+ .to match(%r{!\[dk\]\(uploads/\h{32}/dk\.png\)$})
end
it 'the links point to the wiki root url' do
diff --git a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb
index 8b3a344a841..9d1f05d5543 100644
--- a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb
+++ b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb
@@ -109,20 +109,77 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
end
context 'filtering by release' do
- context 'when the release tag is none' do
+ context 'when filter by none' do
let(:params) { { release_tag: 'none' } }
it 'returns items without releases' do
expect(items).to contain_exactly(item2, item3, item4, item5)
end
+
+ context 'when sort by milestone' do
+ let(:params) { { release_tag: 'none', sort: 'milestone_due_desc' } }
+
+ it 'returns items without any releases' do
+ expect(items).to contain_exactly(item2, item3, item4, item5)
+ end
+ end
+ end
+
+ context 'when filter by any' do
+ let(:params) { { release_tag: 'any' } }
+
+ it 'returns items with any releases' do
+ expect(items).to contain_exactly(item1)
+ end
+
+ context 'when sort by milestone' do
+ let(:params) { { release_tag: 'any', sort: 'milestone_due_desc' } }
+
+ it 'returns items without any releases' do
+ expect(items).to contain_exactly(item1)
+ end
+ end
end
- context 'when the release tag exists' do
+ context 'when filter by a release_tag' do
let(:params) { { project_id: project1.id, release_tag: release.tag } }
- it 'returns the items associated with that release' do
+ it 'returns the items associated with the release tag' do
expect(items).to contain_exactly(item1)
end
+
+ context 'when sort by milestone' do
+ let(:params) { { project_id: project1.id, release_tag: release.tag, sort: 'milestone_due_desc' } }
+
+ it 'returns the items associated with the release tag' do
+ expect(items).to contain_exactly(item1)
+ end
+ end
+ end
+
+ context 'when filter by a negated release_tag' do
+ let_it_be(:another_release) { create(:release, project: project1, tag: 'v2.0.0') }
+ let_it_be(:another_milestone) { create(:milestone, project: project1, releases: [another_release]) }
+ let_it_be(:another_item) do
+ create(factory,
+ project: project1,
+ milestone: another_milestone,
+ title: 'another item')
+ end
+
+ let(:params) { { not: { release_tag: release.tag, project_id: project1.id } } }
+
+ it 'returns the items not associated with the release' do
+ expect(items).to contain_exactly(another_item)
+ end
+
+ context 'when sort by milestone' do
+ let(:params) { { not: { release_tag: release.tag, project_id: project1.id }, sort: 'milestone_due_desc' } }
+
+ it 'returns the items not associated with the release' do
+ expect(items).to contain_exactly(another_item)
+ end
+ end
end
end
@@ -864,12 +921,15 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
context 'filtering by item type' do
let_it_be(:incident_item) { create(factory, issue_type: :incident, project: project1) }
+ let_it_be(:objective) { create(factory, issue_type: :objective, project: project1) }
+ let_it_be(:key_result) { create(factory, issue_type: :key_result, project: project1) }
context 'no type given' do
let(:params) { { issue_types: [] } }
it 'returns all items' do
- expect(items).to contain_exactly(incident_item, item1, item2, item3, item4, item5)
+ expect(items)
+ .to contain_exactly(incident_item, item1, item2, item3, item4, item5, objective, key_result)
end
end
@@ -881,6 +941,22 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
end
end
+ context 'objective type' do
+ let(:params) { { issue_types: ['objective'] } }
+
+ it 'returns incident items' do
+ expect(items).to contain_exactly(objective)
+ end
+ end
+
+ context 'key_result type' do
+ let(:params) { { issue_types: ['key_result'] } }
+
+ it 'returns incident items' do
+ expect(items).to contain_exactly(key_result)
+ end
+ end
+
context 'item type' do
let(:params) { { issue_types: ['issue'] } }
diff --git a/spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb b/spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb
index 601a53ed913..f00d6e776ec 100644
--- a/spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb
+++ b/spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb
@@ -237,8 +237,8 @@ RSpec.shared_examples 'snippet visibility' do
if project.private?
project.add_developer(external) unless member
- else
- member.delete if member
+ elsif member
+ member.delete
end
end
end
diff --git a/spec/support/shared_examples/graphql/label_fields.rb b/spec/support/shared_examples/graphql/label_fields.rb
index 4159e4e03ab..030a2feafcd 100644
--- a/spec/support/shared_examples/graphql/label_fields.rb
+++ b/spec/support/shared_examples/graphql/label_fields.rb
@@ -42,9 +42,7 @@ RSpec.shared_examples 'querying a GraphQL type with labels' do
make_query(
[
query_graphql_field(:label, label_params, all_graphql_fields_for(Label)),
- query_graphql_field(:labels, labels_params, [
- query_graphql_field(:nodes, nil, all_graphql_fields_for(Label))
- ])
+ query_graphql_field(:labels, labels_params, [query_graphql_field(:nodes, nil, all_graphql_fields_for(Label))])
]
)
end
diff --git a/spec/support/shared_examples/graphql/mutations/incident_management_timeline_events_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/incident_management_timeline_events_shared_examples.rb
index cd591248ff6..030aca5bd1c 100644
--- a/spec/support/shared_examples/graphql/mutations/incident_management_timeline_events_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/incident_management_timeline_events_shared_examples.rb
@@ -7,7 +7,7 @@ require 'spec_helper'
# * Defined expected timeline event via `let(:expected_timeline_event) { instance_double(...) }`
RSpec.shared_examples 'creating an incident timeline event' do
it 'creates a timeline event' do
- expect { resolve }.to change(IncidentManagement::TimelineEvent, :count).by(1)
+ expect { resolve }.to change { IncidentManagement::TimelineEvent.count }.by(1)
end
it 'responds with a timeline event', :aggregate_failures do
diff --git a/spec/support/shared_examples/graphql/mutations/timelogs/create_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/timelogs/create_shared_examples.rb
index f28348fb945..c6402a89f02 100644
--- a/spec/support/shared_examples/graphql/mutations/timelogs/create_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/timelogs/create_shared_examples.rb
@@ -1,6 +1,17 @@
# frozen_string_literal: true
RSpec.shared_examples 'issuable supports timelog creation mutation' do
+ let(:mutation_response) { graphql_mutation_response(:timelog_create) }
+ let(:mutation) do
+ variables = {
+ 'time_spent' => time_spent,
+ 'spent_at' => '2022-11-16T12:59:35+0100',
+ 'summary' => 'Test summary',
+ 'issuable_id' => issuable.to_global_id.to_s
+ }
+ graphql_mutation(:timelogCreate, variables)
+ end
+
context 'when the user is anonymous' do
before do
post_graphql_mutation(mutation, current_user: current_user)
@@ -32,13 +43,14 @@ RSpec.shared_examples 'issuable supports timelog creation mutation' do
it 'creates the timelog' do
expect do
post_graphql_mutation(mutation, current_user: current_user)
- end.to change(Timelog, :count).by(1)
+ end.to change { Timelog.count }.by(1)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response['errors']).to be_empty
expect(mutation_response['timelog']).to include(
'timeSpent' => 3600,
- 'spentAt' => '2022-07-08T00:00:00Z',
+ # This also checks that the ISO time was converted to UTC
+ 'spentAt' => '2022-11-16T11:59:35Z',
'summary' => 'Test summary'
)
end
@@ -50,10 +62,11 @@ RSpec.shared_examples 'issuable supports timelog creation mutation' do
it 'returns an error' do
expect do
post_graphql_mutation(mutation, current_user: current_user)
- end.to change(Timelog, :count).by(0)
+ end.to change { Timelog.count }.by(0)
expect(response).to have_gitlab_http_status(:success)
- expect(mutation_response['errors']).to match_array(['Time spent can\'t be blank'])
+ expect(mutation_response['errors']).to match_array(
+ ['Time spent must be formatted correctly. For example: 1h 30m.'])
expect(mutation_response['timelog']).to be_nil
end
end
@@ -61,6 +74,17 @@ RSpec.shared_examples 'issuable supports timelog creation mutation' do
end
RSpec.shared_examples 'issuable does not support timelog creation mutation' do
+ let(:mutation_response) { graphql_mutation_response(:timelog_create) }
+ let(:mutation) do
+ variables = {
+ 'time_spent' => time_spent,
+ 'spent_at' => '2022-11-16T12:59:35+0100',
+ 'summary' => 'Test summary',
+ 'issuable_id' => issuable.to_global_id.to_s
+ }
+ graphql_mutation(:timelogCreate, variables)
+ end
+
context 'when the user is anonymous' do
before do
post_graphql_mutation(mutation, current_user: current_user)
diff --git a/spec/support/shared_examples/graphql/mutations/work_items/update_description_widget_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/work_items/update_description_widget_shared_examples.rb
index 56c2ca22e15..f672ec7f5ac 100644
--- a/spec/support/shared_examples/graphql/mutations/work_items/update_description_widget_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/work_items/update_description_widget_shared_examples.rb
@@ -5,7 +5,7 @@ RSpec.shared_examples 'update work item description widget' do
expect do
post_graphql_mutation(mutation, current_user: current_user)
work_item.reload
- end.to change(work_item, :description).from(nil).to(new_description)
+ end.to change { work_item.description }.from(nil).to(new_description)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response['workItem']['widgets']).to include(
diff --git a/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb b/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb
index bdd4dbfe209..3ff93371c19 100644
--- a/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb
@@ -20,7 +20,7 @@ RSpec.shared_examples 'a Note mutation when the user does not have permission' d
it_behaves_like 'a Note mutation that does not create a Note'
it_behaves_like 'a mutation that returns top-level errors',
- errors: ['The resource that you are attempting to access does not exist or you don\'t have permission to perform this action']
+ errors: ['The resource that you are attempting to access does not exist or you don\'t have permission to perform this action']
end
RSpec.shared_examples 'a Note mutation when there are active record validation errors' do |model: Note|
@@ -74,7 +74,7 @@ RSpec.shared_examples 'a Note mutation when there are rate limit validation erro
it_behaves_like 'a Note mutation that does not create a Note'
it_behaves_like 'a mutation that returns top-level errors',
- errors: ['This endpoint has been requested too many times. Try again later.']
+ errors: ['This endpoint has been requested too many times. Try again later.']
context 'when the user is in the allowlist' do
before do
@@ -97,3 +97,55 @@ RSpec.shared_examples 'a Note mutation with confidential notes' do
expect(mutation_response['note']['internal']).to eq(true)
end
end
+
+RSpec.shared_examples 'a Note mutation updates a note successfully' do
+ it 'updates the Note' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(note.reload.note).to eq(updated_body)
+ end
+
+ it 'returns the updated Note' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['note']['body']).to eq(updated_body)
+ end
+end
+
+RSpec.shared_examples 'a Note mutation update with errors' do
+ context 'when there are ActiveRecord validation errors' do
+ let(:params) { { body: '', confidential: true } }
+
+ it_behaves_like 'a mutation that returns errors in the response',
+ errors: ["Note can't be blank", 'Confidential can not be changed for existing notes']
+
+ it 'does not update the Note' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(note.reload.note).to eq(original_body)
+ expect(note.confidential).to be_falsey
+ end
+
+ it 'returns the original Note' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['note']['body']).to eq(original_body)
+ expect(mutation_response['note']['confidential']).to be_falsey
+ end
+ end
+end
+
+RSpec.shared_examples 'a Note mutation update only with quick actions' do
+ context 'when body only contains quick actions' do
+ let(:updated_body) { '/close' }
+
+ it 'returns a nil note and empty errors' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response).to include(
+ 'errors' => [],
+ 'note' => nil
+ )
+ end
+ end
+end
diff --git a/spec/support/shared_examples/initializers/uses_gitlab_url_blocker_shared_examples.rb b/spec/support/shared_examples/initializers/uses_gitlab_url_blocker_shared_examples.rb
index afa495fc9a4..553e9f10b0d 100644
--- a/spec/support/shared_examples/initializers/uses_gitlab_url_blocker_shared_examples.rb
+++ b/spec/support/shared_examples/initializers/uses_gitlab_url_blocker_shared_examples.rb
@@ -152,4 +152,11 @@ RSpec.shared_examples 'a request using Gitlab::UrlBlocker' do
expect(request_stub).to have_been_requested
end
end
+
+ context 'when a non HTTP/HTTPS URL is provided' do
+ it 'raises an error' do
+ expect { make_request('ssh://example.com') }
+ .to raise_error(ArgumentError)
+ end
+ end
end
diff --git a/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_shared_examples.rb
index 459d4f5cd3e..7141492bd49 100644
--- a/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_shared_examples.rb
@@ -16,20 +16,20 @@ RSpec.shared_examples 'backfill migration for project repositories' do |storage|
projects.create!(name: "foo-#{index}", path: "foo-#{index}", namespace_id: group.id, storage_version: storage_version)
end
- expect { described_class.new.perform(1, projects.last.id) }.to change(project_repositories, :count).by(2)
+ expect { described_class.new.perform(1, projects.last.id) }.to change { project_repositories.count }.by(2)
end
it "does nothing for projects on #{storage} storage that have already a project_repository row" do
projects.create!(id: 1, name: 'foo', path: 'foo', namespace_id: group.id, storage_version: storage_version)
project_repositories.create!(project_id: 1, disk_path: 'phony/foo/bar', shard_id: shard.id)
- expect { described_class.new.perform(1, projects.last.id) }.not_to change(project_repositories, :count)
+ expect { described_class.new.perform(1, projects.last.id) }.not_to change { project_repositories.count }
end
it "does nothing for projects on #{storage == :legacy ? 'hashed' : 'legacy'} storage" do
projects.create!(name: 'foo', path: 'foo', namespace_id: group.id, storage_version: storage == :legacy ? 1 : nil)
- expect { described_class.new.perform(1, projects.last.id) }.not_to change(project_repositories, :count)
+ expect { described_class.new.perform(1, projects.last.id) }.not_to change { project_repositories.count }
end
it 'inserts rows in a single query' do
diff --git a/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb b/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb
index 9ffc55f7e7e..d471a758f3e 100644
--- a/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb
+++ b/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb
@@ -62,14 +62,14 @@ RSpec.shared_examples 'deployment metrics examples' do
describe '#deployment_frequency' do
subject { stage_summary.fourth[:value] }
- it 'includes the unit: `/day`' do
- expect(stage_summary.fourth[:unit]).to eq _('/day')
- end
-
before do
travel_to(5.days.ago) { create_deployment(project: project) }
end
+ it 'includes the unit: `/day`' do
+ expect(stage_summary.fourth[:unit]).to eq _('/day')
+ end
+
it 'returns 0.0 when there were deploys but the frequency was too low' do
options[:from] = 30.days.ago
diff --git a/spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb
index a28fefcfc58..286f10a186d 100644
--- a/spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb
@@ -28,7 +28,7 @@ RSpec.shared_examples 'finalized background migration' do |worker_class|
.new
.select do |scheduled|
scheduled.klass == worker_class.name &&
- scheduled.args.first == job_class_name
+ scheduled.args.first == job_class_name
end
expect(queued.size).to eq(0)
end
diff --git a/spec/support/shared_examples/lib/gitlab/middleware/multipart_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/middleware/multipart_shared_examples.rb
index 40deaa27955..16b048ae325 100644
--- a/spec/support/shared_examples/lib/gitlab/middleware/multipart_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/middleware/multipart_shared_examples.rb
@@ -25,10 +25,12 @@ RSpec.shared_examples 'handling all upload parameters conditions' do
end
it 'builds UploadedFiles' do
- expect_uploaded_files([
- { filepath: uploaded_filepath, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(file1) },
- { filepath: uploaded_filepath2, original_filename: filename2, remote_id: remote_id2, size: uploaded_file2.size, params_path: %w(file2) }
- ])
+ expect_uploaded_files(
+ [
+ { filepath: uploaded_filepath, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(file1) },
+ { filepath: uploaded_filepath2, original_filename: filename2, remote_id: remote_id2, size: uploaded_file2.size, params_path: %w(file2) }
+ ]
+ )
subject
end
@@ -61,10 +63,12 @@ RSpec.shared_examples 'handling all upload parameters conditions' do
end
it 'builds UploadedFiles' do
- expect_uploaded_files([
- { filepath: uploaded_filepath, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(user avatar) },
- { filepath: uploaded_filepath2, original_filename: filename2, remote_id: remote_id2, size: uploaded_file2.size, params_path: %w(user screenshot) }
- ])
+ expect_uploaded_files(
+ [
+ { filepath: uploaded_filepath, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(user avatar) },
+ { filepath: uploaded_filepath2, original_filename: filename2, remote_id: remote_id2, size: uploaded_file2.size, params_path: %w(user screenshot) }
+ ]
+ )
subject
end
@@ -101,10 +105,12 @@ RSpec.shared_examples 'handling all upload parameters conditions' do
end
it 'builds UploadedFiles' do
- expect_uploaded_files([
- { filepath: uploaded_file, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(user avatar bananas) },
- { filepath: uploaded_file2, original_filename: filename2, remote_id: remote_id2, size: uploaded_file2.size, params_path: %w(user friend ananas) }
- ])
+ expect_uploaded_files(
+ [
+ { filepath: uploaded_file, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(user avatar bananas) },
+ { filepath: uploaded_file2, original_filename: filename2, remote_id: remote_id2, size: uploaded_file2.size, params_path: %w(user friend ananas) }
+ ]
+ )
subject
end
@@ -133,11 +139,13 @@ RSpec.shared_examples 'handling all upload parameters conditions' do
end
it 'builds UploadedFiles' do
- expect_uploaded_files([
- { filepath: uploaded_filepath, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(file) },
- { filepath: uploaded_filepath2, original_filename: filename2, remote_id: remote_id2, size: uploaded_file2.size, params_path: %w(user avatar) },
- { filepath: uploaded_filepath3, original_filename: filename3, remote_id: remote_id3, size: uploaded_file3.size, params_path: %w(user friend avatar) }
- ])
+ expect_uploaded_files(
+ [
+ { filepath: uploaded_filepath, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(file) },
+ { filepath: uploaded_filepath2, original_filename: filename2, remote_id: remote_id2, size: uploaded_file2.size, params_path: %w(user avatar) },
+ { filepath: uploaded_filepath3, original_filename: filename3, remote_id: remote_id3, size: uploaded_file3.size, params_path: %w(user friend avatar) }
+ ]
+ )
subject
end
diff --git a/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb
index 481e11bcf0e..d4802a19202 100644
--- a/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb
@@ -1,38 +1,5 @@
# frozen_string_literal: true
-RSpec.shared_examples 'a daily tracked issuable event' do
- before do
- stub_application_setting(usage_ping_enabled: true)
- end
-
- def count_unique(date_from: 1.minute.ago, date_to: 1.minute.from_now)
- Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: action, start_date: date_from, end_date: date_to)
- end
-
- specify do
- aggregate_failures do
- expect(track_action(author: user1)).to be_truthy
- expect(track_action(author: user1)).to be_truthy
- expect(track_action(author: user2)).to be_truthy
- expect(count_unique).to eq(2)
- end
- end
-
- it 'does not track edit actions if author is not present' do
- expect(track_action(author: nil)).to be_nil
- end
-end
-
-RSpec.shared_examples 'does not track when feature flag is disabled' do |feature_flag|
- context "when feature flag #{feature_flag} is disabled" do
- it 'does not track action' do
- stub_feature_flags(feature_flag => false)
-
- expect(track_action(author: user1)).to be_nil
- end
- end
-end
-
RSpec.shared_examples 'a daily tracked issuable snowplow and service ping events for given event params' do
before do
stub_application_setting(usage_ping_enabled: true)
@@ -76,15 +43,27 @@ end
RSpec.shared_examples 'daily tracked issuable snowplow and service ping events with project' do
it_behaves_like 'a daily tracked issuable snowplow and service ping events for given event params' do
+ let(:context) do
+ Gitlab::Tracking::ServicePingContext
+ .new(data_source: :redis_hll, event: event_property)
+ .to_h
+ end
+
let(:track_params) { { project: project } }
- let(:event_params) { track_params.merge(label: event_label, property: event_property, namespace: project.namespace) }
+ let(:event_params) { track_params.merge(label: event_label, property: event_property, namespace: project.namespace, context: [context]) }
end
end
RSpec.shared_examples 'a daily tracked issuable snowplow and service ping events with namespace' do
it_behaves_like 'a daily tracked issuable snowplow and service ping events for given event params' do
+ let(:context) do
+ Gitlab::Tracking::ServicePingContext
+ .new(data_source: :redis_hll, event: event_property)
+ .to_h
+ end
+
let(:track_params) { { namespace: namespace } }
- let(:event_params) { track_params.merge(label: event_label, property: event_property) }
+ let(:event_params) { track_params.merge(label: event_label, property: event_property, context: [context]) }
end
end
diff --git a/spec/support/shared_examples/mailers/notify_shared_examples.rb b/spec/support/shared_examples/mailers/notify_shared_examples.rb
index 919311adc96..b0cbf0b0d65 100644
--- a/spec/support/shared_examples/mailers/notify_shared_examples.rb
+++ b/spec/support/shared_examples/mailers/notify_shared_examples.rb
@@ -75,7 +75,7 @@ RSpec.shared_examples 'a new thread email with reply-by-email enabled' do
aggregate_failures do
is_expected.to have_header('Message-ID', "<#{route_key}@#{host}>")
- is_expected.to have_header('References', /\A<reply\-.*@#{host}>\Z/ )
+ is_expected.to have_header('References', /\A<reply-.*@#{host}>\Z/ )
end
end
end
@@ -91,7 +91,7 @@ RSpec.shared_examples 'a thread answer email with reply-by-email enabled' do
aggregate_failures do
is_expected.to have_header('Message-ID', /\A<.*@#{host}>\Z/)
is_expected.to have_header('In-Reply-To', "<#{route_key}@#{host}>")
- is_expected.to have_header('References', /\A<reply\-.*@#{host}> <#{route_key}@#{host}>\Z/ )
+ is_expected.to have_header('References', /\A<reply-.*@#{host}> <#{route_key}@#{host}>\Z/ )
is_expected.to have_subject(/^Re: /)
end
end
diff --git a/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb b/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb
index ef4b08c7865..c07d1552ba2 100644
--- a/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb
+++ b/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb
@@ -129,8 +129,8 @@ RSpec.shared_examples 'record ActiveRecord metrics in a metrics transaction' do
expect(transaction).to receive(:increment).with("gitlab_transaction_db_#{db_role}_wal_count_total".to_sym, 1, { db_config_name: db_config_name })
expect(transaction).to receive(:increment).with("gitlab_transaction_db_#{db_role}_wal_cached_count_total".to_sym, 1, { db_config_name: db_config_name }) if record_cached_query
end
- else
- expect(transaction).not_to receive(:increment).with("gitlab_transaction_db_#{db_role}_wal_count_total".to_sym, 1, { db_config_name: db_config_name }) if db_role
+ elsif db_role
+ expect(transaction).not_to receive(:increment).with("gitlab_transaction_db_#{db_role}_wal_count_total".to_sym, 1, { db_config_name: db_config_name })
end
subscriber.sql(event)
diff --git a/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb b/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb
index a658d02f09a..a20bb794095 100644
--- a/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb
@@ -6,21 +6,18 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
Gitlab::ApplicationContext.push(feature_category: 'test', caller_id: 'caller')
end
- it 'defines a Redis counter_key' do
- expect(model.counter_key(:counter_name))
- .to eq("project:{#{model.project_id}}:counters:CounterAttributeModel:#{model.id}:counter_name")
- end
-
it 'defines a method to store counters' do
- expect(model.class.counter_attributes.to_a).to eq(counter_attributes)
+ registered_attributes = model.class.counter_attributes.map { |e| e[:attribute] } # rubocop:disable Rails/Pluck
+ expect(registered_attributes).to contain_exactly(*counter_attributes)
end
counter_attributes.each do |attribute|
describe attribute do
- describe '#delayed_increment_counter', :redis do
+ describe '#increment_counter', :redis do
let(:increment) { 10 }
+ let(:counter_key) { model.counter(attribute).key }
- subject { model.delayed_increment_counter(attribute, increment) }
+ subject { model.increment_counter(attribute, increment) }
context 'when attribute is a counter attribute' do
where(:increment) { [10, -3] }
@@ -44,7 +41,7 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
subject
Gitlab::Redis::SharedState.with do |redis|
- counter = redis.get(model.counter_key(attribute))
+ counter = redis.get(counter_key)
expect(counter).to eq(increment.to_s)
end
end
@@ -55,7 +52,7 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
it 'schedules a worker to flush counter increments asynchronously' do
expect(FlushCounterIncrementsWorker).to receive(:perform_in)
- .with(CounterAttribute::WORKER_DELAY, model.class.name, model.id, attribute)
+ .with(Gitlab::Counters::BufferedCounter::WORKER_DELAY, model.class.name, model.id, attribute)
.and_call_original
subject
@@ -73,128 +70,13 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
end
end
end
-
- context 'when attribute is not a counter attribute' do
- it 'raises ArgumentError' do
- expect { model.delayed_increment_counter(:unknown_attribute, 10) }
- .to raise_error(ArgumentError, 'unknown_attribute is not a counter attribute')
- end
- end
- end
- end
- end
-
- describe '.flush_increments_to_database!', :redis do
- let(:incremented_attribute) { counter_attributes.first }
-
- subject { model.flush_increments_to_database!(incremented_attribute) }
-
- it 'obtains an exclusive lease during processing' do
- expect(model)
- .to receive(:in_lock)
- .with(model.counter_lock_key(incremented_attribute), ttl: described_class::WORKER_LOCK_TTL)
- .and_call_original
-
- subject
- end
-
- context 'when there is a counter to flush' do
- before do
- model.delayed_increment_counter(incremented_attribute, 10)
- model.delayed_increment_counter(incremented_attribute, -3)
- end
-
- it 'updates the record and logs it', :aggregate_failures do
- expect(Gitlab::AppLogger).to receive(:info).with(
- hash_including(
- message: 'Acquiring lease for project statistics update',
- attributes: [incremented_attribute]
- )
- )
-
- expect(Gitlab::AppLogger).to receive(:info).with(
- hash_including(
- message: 'Flush counter attribute to database',
- attribute: incremented_attribute,
- project_id: model.project_id,
- increment: 7,
- previous_db_value: 0,
- new_db_value: 7,
- 'correlation_id' => an_instance_of(String),
- 'meta.feature_category' => 'test',
- 'meta.caller_id' => 'caller'
- )
- )
-
- expect { subject }.to change { model.reset.read_attribute(incremented_attribute) }.by(7)
- end
-
- it 'removes the increment entry from Redis' do
- Gitlab::Redis::SharedState.with do |redis|
- key_exists = redis.exists?(model.counter_key(incremented_attribute))
- expect(key_exists).to be_truthy
- end
-
- subject
-
- Gitlab::Redis::SharedState.with do |redis|
- key_exists = redis.exists?(model.counter_key(incremented_attribute))
- expect(key_exists).to be_falsey
- end
- end
- end
-
- context 'when there are no counters to flush' do
- context 'when there are no counters in the relative :flushed key' do
- it 'does not change the record' do
- expect { subject }.not_to change { model.reset.attributes }
- end
- end
-
- # This can be the case where updating counters in the database fails with error
- # and retrying the worker will retry flushing the counters but the main key has
- # disappeared and the increment has been moved to the "<...>:flushed" key.
- context 'when there are counters in the relative :flushed key' do
- before do
- Gitlab::Redis::SharedState.with do |redis|
- redis.incrby(model.counter_flushed_key(incremented_attribute), 10)
- end
- end
-
- it 'updates the record' do
- expect { subject }.to change { model.reset.read_attribute(incremented_attribute) }.by(10)
- end
-
- it 'deletes the relative :flushed key' do
- subject
-
- Gitlab::Redis::SharedState.with do |redis|
- key_exists = redis.exists?(model.counter_flushed_key(incremented_attribute))
- expect(key_exists).to be_falsey
- end
- end
- end
- end
-
- context 'when deleting :flushed key fails' do
- before do
- Gitlab::Redis::SharedState.with do |redis|
- redis.incrby(model.counter_flushed_key(incremented_attribute), 10)
-
- expect(redis).to receive(:del).and_raise('could not delete key')
- end
- end
-
- it 'does a rollback of the counter update' do
- expect { subject }.to raise_error('could not delete key')
-
- expect(model.reset.read_attribute(incremented_attribute)).to eq(0)
end
end
end
describe '#reset_counter!' do
let(:attribute) { counter_attributes.first }
+ let(:counter_key) { model.counter(attribute).key }
before do
model.update!(attribute => 123)
@@ -207,7 +89,7 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
expect { subject }.to change { model.reload.send(attribute) }.from(123).to(0)
Gitlab::Redis::SharedState.with do |redis|
- key_exists = redis.exists?(model.counter_key(attribute))
+ key_exists = redis.exists?(counter_key)
expect(key_exists).to be_falsey
end
end
diff --git a/spec/support/shared_examples/models/concerns/integrations/base_slack_notification_shared_examples.rb b/spec/support/shared_examples/models/concerns/integrations/base_slack_notification_shared_examples.rb
index f0581333b28..2e528f7996c 100644
--- a/spec/support/shared_examples/models/concerns/integrations/base_slack_notification_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/integrations/base_slack_notification_shared_examples.rb
@@ -5,14 +5,16 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
let_it_be(:project) { create(:project, :repository, :wiki_repo) }
let_it_be(:integration) { create(factory, branches_to_be_notified: 'all', project: project) }
- before do
- stub_request(:post, integration.webhook)
+ def usage_tracking_key(action)
+ prefix = integration.send(:metrics_key_prefix)
+
+ "#{prefix}_#{action}_notification"
end
it 'uses only known events', :aggregate_failures do
described_class::SUPPORTED_EVENTS_FOR_USAGE_LOG.each do |action|
expect(
- Gitlab::UsageDataCounters::HLLRedisCounter.known_event?("i_ecosystem_slack_service_#{action}_notification")
+ Gitlab::UsageDataCounters::HLLRedisCounter.known_event?(usage_tracking_key(action))
).to be true
end
end
@@ -20,7 +22,9 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
context 'when hook data includes a user object' do
let_it_be(:user) { create_default(:user) }
- shared_examples 'increases the usage data counter' do |event_name|
+ shared_examples 'increases the usage data counter' do |event|
+ let(:event_name) { usage_tracking_key(event) }
+
subject(:execute) { integration.execute(data) }
it 'increases the usage data counter' do
@@ -30,7 +34,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
execute
end
- it_behaves_like 'Snowplow event tracking' do
+ it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:category) { described_class.to_s }
let(:action) { 'perform_integrations_action' }
@@ -47,7 +51,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
it 'does not increase the usage data counter' do
expect(Gitlab::UsageDataCounters::HLLRedisCounter)
- .not_to receive(:track_event).with('i_ecosystem_slack_service_pipeline_notification', values: user.id)
+ .not_to receive(:track_event).with(usage_tracking_key(:pipeline), values: user.id)
integration.execute(data)
end
@@ -58,13 +62,13 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
let(:data) { issue.to_hook_data(user) }
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_issue_notification'
+ it_behaves_like 'increases the usage data counter', :issue
end
context 'for push notification' do
let(:data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_push_notification'
+ it_behaves_like 'increases the usage data counter', :push
end
context 'for deployment notification' do
@@ -72,7 +76,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
let(:data) { Gitlab::DataBuilder::Deployment.build(deployment, deployment.status, Time.current) }
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_deployment_notification'
+ it_behaves_like 'increases the usage data counter', :deployment
end
context 'for wiki_page notification' do
@@ -88,7 +92,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
allow(project.wiki).to receive(:after_wiki_activity)
end
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_wiki_page_notification'
+ it_behaves_like 'increases the usage data counter', :wiki_page
end
context 'for merge_request notification' do
@@ -96,7 +100,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
let(:data) { merge_request.to_hook_data(user) }
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_merge_request_notification'
+ it_behaves_like 'increases the usage data counter', :merge_request
end
context 'for note notification' do
@@ -104,7 +108,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
let(:data) { Gitlab::DataBuilder::Note.build(issue_note, user) }
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_note_notification'
+ it_behaves_like 'increases the usage data counter', :note
end
context 'for tag_push notification' do
@@ -115,7 +119,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
Git::TagHooksService.new(project, user, change: { oldrev: oldrev, newrev: newrev, ref: ref }).send(:push_data)
end
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_tag_push_notification'
+ it_behaves_like 'increases the usage data counter', :tag_push
end
context 'for confidential note notification' do
@@ -125,7 +129,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
let(:data) { Gitlab::DataBuilder::Note.build(confidential_issue_note, user) }
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_confidential_note_notification'
+ it_behaves_like 'increases the usage data counter', :confidential_note
end
context 'for confidential issue notification' do
@@ -133,7 +137,7 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
let(:data) { issue.to_hook_data(user) }
- it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_confidential_issue_notification'
+ it_behaves_like 'increases the usage data counter', :confidential_issue
end
end
diff --git a/spec/support/shared_examples/models/concerns/sanitizable_shared_examples.rb b/spec/support/shared_examples/models/concerns/sanitizable_shared_examples.rb
index ed94a71892d..aedbfe4deb3 100644
--- a/spec/support/shared_examples/models/concerns/sanitizable_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/sanitizable_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'sanitizable' do |factory, fields|
- let(:attributes) { fields.to_h { |field| [field, input] } }
+ let(:attributes) { fields.index_with { input } }
it 'includes Sanitizable' do
expect(described_class).to include(Sanitizable)
diff --git a/spec/support/shared_examples/models/concerns/signature_type_shared_examples.rb b/spec/support/shared_examples/models/concerns/signature_type_shared_examples.rb
new file mode 100644
index 00000000000..728855b74f8
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/signature_type_shared_examples.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+METHODS = %i[
+ gpg?
+ ssh?
+ x509?
+].freeze
+
+RSpec.shared_examples 'signature with type checking' do |type|
+ describe 'signature type checkers' do
+ where(:method, :expected) do
+ METHODS.map do |method|
+ [method, method == "#{type}?".to_sym]
+ end
+ end
+
+ with_them do
+ specify { expect(subject.public_send(method)).to eq(expected) }
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/integrations/has_web_hook_shared_examples.rb b/spec/support/shared_examples/models/integrations/has_web_hook_shared_examples.rb
index a764d47d7c0..a1269b158a2 100644
--- a/spec/support/shared_examples/models/integrations/has_web_hook_shared_examples.rb
+++ b/spec/support/shared_examples/models/integrations/has_web_hook_shared_examples.rb
@@ -65,7 +65,7 @@ RSpec.shared_examples Integrations::HasWebHook do
end
it 'creates or updates a service hook' do
- expect { call }.to change(ServiceHook, :count).by(1)
+ expect { call }.to change { ServiceHook.count }.by(1)
expect(integration.service_hook.url).to eq(hook_url)
integration.service_hook.update!(url: 'http://other.com')
@@ -98,10 +98,10 @@ RSpec.shared_examples Integrations::HasWebHook do
it 'creates the webhook if necessary and executes it' do
expect_next(ServiceHook).to receive(:execute).with(*args)
- expect { call }.to change(ServiceHook, :count).by(1)
+ expect { call }.to change { ServiceHook.count }.by(1)
expect(integration.service_hook).to receive(:execute).with(*args)
- expect { call }.not_to change(ServiceHook, :count)
+ expect { call }.not_to change { ServiceHook.count }
end
it 'raises an error if the service hook could not be saved' do
diff --git a/spec/support/shared_examples/models/label_note_shared_examples.rb b/spec/support/shared_examples/models/label_note_shared_examples.rb
index f61007f57fd..3facd533d7a 100644
--- a/spec/support/shared_examples/models/label_note_shared_examples.rb
+++ b/spec/support/shared_examples/models/label_note_shared_examples.rb
@@ -30,6 +30,8 @@ RSpec.shared_examples 'label note created from events' do
expect(note.noteable).to eq event.issuable
expect(note.note).to be_present
expect(note.note_html).to be_present
+ expect(note.created_at).to eq create_event.created_at
+ expect(note.updated_at).to eq create_event.created_at
end
it 'updates markdown cache if reference is not set yet' do
diff --git a/spec/support/shared_examples/models/member_shared_examples.rb b/spec/support/shared_examples/models/member_shared_examples.rb
index 287b046cbec..f8cff5c5558 100644
--- a/spec/support/shared_examples/models/member_shared_examples.rb
+++ b/spec/support/shared_examples/models/member_shared_examples.rb
@@ -491,7 +491,7 @@ RSpec.shared_examples_for "bulk member creation" do
:developer,
tasks_to_be_done: %w(issues),
tasks_project_id: task_project.id)
- end.not_to change(MemberTask, :count)
+ end.not_to change { MemberTask.count }
member.reset
expect(member.tasks_to_be_done).to match_array([:code, :ci])
@@ -505,7 +505,7 @@ RSpec.shared_examples_for "bulk member creation" do
:developer,
tasks_to_be_done: %w(issues),
tasks_project_id: task_project.id)
- end.to change(MemberTask, :count).by(1)
+ end.to change { MemberTask.count }.by(1)
member = source.members.find_by(user_id: user1.id)
expect(member.tasks_to_be_done).to match_array([:issues])
diff --git a/spec/support/shared_examples/models/update_highest_role_shared_examples.rb b/spec/support/shared_examples/models/update_highest_role_shared_examples.rb
index 34c4ada1718..1fdd0962fbb 100644
--- a/spec/support/shared_examples/models/update_highest_role_shared_examples.rb
+++ b/spec/support/shared_examples/models/update_highest_role_shared_examples.rb
@@ -25,7 +25,7 @@ RSpec.shared_examples 'update highest role with exclusive lease' do
expect(UpdateHighestRoleWorker).to receive(:perform_in).with(described_class::HIGHEST_ROLE_JOB_DELAY, user_id).and_call_original
- expect { subject }.to change(UpdateHighestRoleWorker.jobs, :size).by(1)
+ expect { subject }.to change { UpdateHighestRoleWorker.jobs.size }.by(1)
end
end
@@ -33,7 +33,7 @@ RSpec.shared_examples 'update highest role with exclusive lease' do
it 'only schedules one job' do
stub_exclusive_lease_taken(lease_key, timeout: described_class::HIGHEST_ROLE_LEASE_TIMEOUT)
- expect { subject }.not_to change(UpdateHighestRoleWorker.jobs, :size)
+ expect { subject }.not_to change { UpdateHighestRoleWorker.jobs.size }
end
end
end
diff --git a/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb
index b81bd514d0a..eb742921d35 100644
--- a/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb
+++ b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb
@@ -15,7 +15,7 @@ RSpec.shared_examples 'UpdateProjectStatistics' do |with_counter_attribute|
def read_pending_increment
Gitlab::Redis::SharedState.with do |redis|
- key = project.statistics.counter_key(project_statistics_name)
+ key = project.statistics.counter(project_statistics_name).key
redis.get(key).to_i
end
end
@@ -25,7 +25,7 @@ RSpec.shared_examples 'UpdateProjectStatistics' do |with_counter_attribute|
def expect_flush_counter_increments_worker_performed
expect(FlushCounterIncrementsWorker)
.to receive(:perform_in)
- .with(CounterAttribute::WORKER_DELAY, project.statistics.class.name, project.statistics.id, project_statistics_name)
+ .with(Gitlab::Counters::BufferedCounter::WORKER_DELAY, project.statistics.class.name, project.statistics.id, project_statistics_name)
yield
diff --git a/spec/support/shared_examples/models/with_debian_distributions_shared_examples.rb b/spec/support/shared_examples/models/with_debian_distributions_shared_examples.rb
index e86f1e77447..d6071b20dca 100644
--- a/spec/support/shared_examples/models/with_debian_distributions_shared_examples.rb
+++ b/spec/support/shared_examples/models/with_debian_distributions_shared_examples.rb
@@ -9,9 +9,9 @@ RSpec.shared_examples 'model with Debian distributions' do
it 'removes distribution files on removal' do
distribution_file_paths = distributions.map do |distribution|
[distribution.file.path] +
- distribution.component_files.map do |component_file|
- component_file.file.path
- end
+ distribution.component_files.map do |component_file|
+ component_file.file.path
+ end
end.flatten
expect { subject.destroy! }
diff --git a/spec/support/shared_examples/observability/csp_shared_examples.rb b/spec/support/shared_examples/observability/csp_shared_examples.rb
new file mode 100644
index 00000000000..868d7023d14
--- /dev/null
+++ b/spec/support/shared_examples/observability/csp_shared_examples.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+
+# Verifies that the proper CSP rules for Observabilty UI are applied to a given controller/path
+#
+# The path under test needs to be declared with `let(:tested_path) { .. }` in the context including this example
+#
+# ```
+# it_behaves_like "observability csp policy" do
+# let(:tested_path) { ....the path under test }
+# end
+# ```
+#
+# It optionally supports specifying the controller class handling the tested path as a parameter, e.g.
+#
+# ```
+# it_behaves_like "observability csp policy", Groups::ObservabilityController
+# ```
+# (If not specified it will default to `described_class`)
+#
+RSpec.shared_examples 'observability csp policy' do |controller_class = described_class|
+ include ContentSecurityPolicyHelpers
+
+ let(:observability_url) { Gitlab::Observability.observability_url }
+ let(:signin_url) do
+ Gitlab::Utils.append_path(Gitlab.config.gitlab.url,
+ '/users/sign_in')
+ end
+
+ let(:oauth_url) do
+ Gitlab::Utils.append_path(Gitlab.config.gitlab.url,
+ '/oauth/authorize')
+ end
+
+ before do
+ setup_csp_for_controller(controller_class, csp, any_time: true)
+ end
+
+ subject do
+ get tested_path
+ response.headers['Content-Security-Policy']
+ end
+
+ context 'when there is no CSP config' do
+ let(:csp) { ActionDispatch::ContentSecurityPolicy.new }
+
+ it 'does not add any csp header' do
+ expect(subject).to be_blank
+ end
+ end
+
+ context 'when frame-src exists in the CSP config' do
+ let(:csp) do
+ ActionDispatch::ContentSecurityPolicy.new do |p|
+ p.frame_src 'https://something.test'
+ end
+ end
+
+ it 'appends the proper url to frame-src CSP directives' do
+ expect(subject).to include(
+ "frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}")
+ end
+ end
+
+ context 'when signin is already present in the policy' do
+ let(:csp) do
+ ActionDispatch::ContentSecurityPolicy.new do |p|
+ p.frame_src signin_url
+ end
+ end
+
+ it 'does not append signin again' do
+ expect(subject).to include(
+ "frame-src #{signin_url} #{observability_url} #{oauth_url};")
+ end
+ end
+
+ context 'when oauth is already present in the policy' do
+ let(:csp) do
+ ActionDispatch::ContentSecurityPolicy.new do |p|
+ p.frame_src oauth_url
+ end
+ end
+
+ it 'does not append oauth again' do
+ expect(subject).to include(
+ "frame-src #{oauth_url} #{observability_url} #{signin_url};")
+ end
+ end
+
+ context 'when default-src exists in the CSP config' do
+ let(:csp) do
+ ActionDispatch::ContentSecurityPolicy.new do |p|
+ p.default_src 'https://something.test'
+ end
+ end
+
+ it 'does not change default-src' do
+ expect(subject).to include(
+ "default-src https://something.test;")
+ end
+
+ it 'appends the proper url to frame-src CSP directives' do
+ expect(subject).to include(
+ "frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}")
+ end
+ end
+
+ context 'when frame-src and default-src exist in the CSP config' do
+ let(:csp) do
+ ActionDispatch::ContentSecurityPolicy.new do |p|
+ p.default_src 'https://something_default.test'
+ p.frame_src 'https://something.test'
+ end
+ end
+
+ it 'appends to frame-src CSP directives' do
+ expect(subject).to include(
+ "frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}")
+ expect(subject).to include(
+ "default-src https://something_default.test")
+ end
+ end
+end
diff --git a/spec/support/shared_examples/policies/project_policy_shared_examples.rb b/spec/support/shared_examples/policies/project_policy_shared_examples.rb
index cfcc3615e13..15d56c402d1 100644
--- a/spec/support/shared_examples/policies/project_policy_shared_examples.rb
+++ b/spec/support/shared_examples/policies/project_policy_shared_examples.rb
@@ -2,13 +2,13 @@
RSpec.shared_examples 'archived project policies' do
let(:feature_write_abilities) do
- described_class.readonly_features.flat_map do |feature|
+ described_class.archived_features.flat_map do |feature|
described_class.create_update_admin_destroy(feature)
end + additional_maintainer_permissions
end
let(:other_write_abilities) do
- described_class.readonly_abilities
+ described_class.archived_abilities
end
context 'when the project is archived' do
diff --git a/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb b/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb
index f7731af8dc6..f70621673d5 100644
--- a/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb
+++ b/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'when regex matching everything is specified' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
let(:params) do
{ 'name_regex_delete' => '.*' }
end
@@ -23,6 +23,19 @@ RSpec.shared_examples 'when regex matching everything is specified' do
end
end
+RSpec.shared_examples 'when regex matching everything is specified and latest is not kept' do
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
+
+ let(:params) do
+ { 'name_regex_delete' => '.*', 'keep_latest' => false }
+ end
+
+ it_behaves_like 'removing the expected tags',
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: delete_expectations
+end
+
RSpec.shared_examples 'when delete regex matching specific tags is used' do
|service_response_extra: {}, supports_caching: false|
let(:params) do
@@ -65,7 +78,7 @@ RSpec.shared_examples 'when delete regex matching specific tags is used with ove
end
RSpec.shared_examples 'with allow regex value' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
let(:params) do
{
'name_regex_delete' => '.*',
@@ -80,7 +93,7 @@ RSpec.shared_examples 'with allow regex value' do
end
RSpec.shared_examples 'when keeping only N tags' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
let(:params) do
{
'name_regex' => 'A|B.*|C',
@@ -99,7 +112,7 @@ RSpec.shared_examples 'when keeping only N tags' do
end
RSpec.shared_examples 'when not keeping N tags' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
let(:params) do
{ 'name_regex' => 'A|B.*|C' }
end
@@ -115,7 +128,7 @@ RSpec.shared_examples 'when not keeping N tags' do
end
RSpec.shared_examples 'when removing keeping only 3' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
let(:params) do
{ 'name_regex_delete' => '.*',
'keep_n' => 3 }
@@ -128,7 +141,7 @@ RSpec.shared_examples 'when removing keeping only 3' do
end
RSpec.shared_examples 'when removing older than 1 day' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
let(:params) do
{
'name_regex_delete' => '.*',
@@ -143,7 +156,7 @@ RSpec.shared_examples 'when removing older than 1 day' do
end
RSpec.shared_examples 'when combining all parameters' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
let(:params) do
{
'name_regex_delete' => '.*',
@@ -159,7 +172,7 @@ RSpec.shared_examples 'when combining all parameters' do
end
RSpec.shared_examples 'when running a container_expiration_policy' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
let(:user) { nil }
context 'with valid container_expiration_policy param' do
@@ -191,7 +204,7 @@ RSpec.shared_examples 'not removing anything' do |service_response_extra: {}, su
end
RSpec.shared_examples 'removing the expected tags' do
- |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ |delete_expectations:, service_response_extra: {}, supports_caching: false|
it 'removes the expected tags' do
delete_expectations.each { |expectation| expect_delete(expectation) }
expect_no_caching unless supports_caching
diff --git a/spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb b/spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb
index 4d142199c95..1cd529aa50b 100644
--- a/spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb
@@ -1,21 +1,23 @@
# frozen_string_literal: true
RSpec.shared_examples 'issuable quick actions' do
- QuickAction = Struct.new(:action_text, :expectation, :before_action, keyword_init: true) do
- # Pass a block as :before_action if
- # issuable state needs to be changed before
- # the quick action is executed.
- def call_before_action
- before_action.call if before_action
- end
+ before do
+ stub_const('QuickAction', Struct.new(:action_text, :expectation, :before_action, keyword_init: true) do
+ # Pass a block as :before_action if
+ # issuable state needs to be changed before
+ # the quick action is executed.
+ def call_before_action
+ before_action.call if before_action
+ end
- def skip_access_check
- action_text["/todo"] ||
- action_text["/done"] ||
- action_text["/subscribe"] ||
- action_text["/shrug"] ||
- action_text["/tableflip"]
- end
+ def skip_access_check
+ action_text["/todo"] ||
+ action_text["/done"] ||
+ action_text["/subscribe"] ||
+ action_text["/shrug"] ||
+ action_text["/tableflip"]
+ end
+ end)
end
let(:unlabel_expectation) do
@@ -145,6 +147,12 @@ RSpec.shared_examples 'issuable quick actions' do
}
),
QuickAction.new(
+ action_text: "/labels ~feature",
+ expectation: ->(noteable, can_use_quick_action) {
+ expect(noteable.labels&.last&.id == feature_label.id).to eq(can_use_quick_action)
+ }
+ ),
+ QuickAction.new(
action_text: "/unlabel",
expectation: unlabel_expectation
),
diff --git a/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb
index 18304951e41..56a1cee44c8 100644
--- a/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb
@@ -22,6 +22,12 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
end
end
+ def open_create_timelog_form
+ page.within time_tracker_selector do
+ find('[data-testid="add-time-entry-button"]').click
+ end
+ end
+
it 'renders the sidebar component empty state' do
page.within '[data-testid="noTrackingPane"]' do
expect(page).to have_content 'No estimate or time spent'
@@ -74,11 +80,13 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
end
end
- it 'shows the help state when icon is clicked' do
- page.within time_tracker_selector do
- find('[data-testid="helpButton"]').click
- expect(page).to have_content 'Track time with quick actions'
- expect(page).to have_content 'Learn more'
+ it 'shows the create timelog form when add button is clicked' do
+ open_create_timelog_form
+
+ page.within '[data-testid="create-timelog-modal"]' do
+ expect(page).to have_content 'Add time entry'
+ expect(page).to have_content 'Time spent'
+ expect(page).to have_content 'Spent at'
end
end
@@ -123,24 +131,6 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
expect(page).to have_content '1d'
end
end
-
- it 'hides the help state when close icon is clicked' do
- page.within time_tracker_selector do
- find('[data-testid="helpButton"]').click
- find('[data-testid="closeHelpButton"]').click
-
- expect(page).not_to have_content 'Track time with quick actions'
- expect(page).not_to have_content 'Learn more'
- end
- end
-
- it 'displays the correct help url' do
- page.within time_tracker_selector do
- find('[data-testid="helpButton"]').click
-
- expect(find_link('Learn more')[:href]).to have_content('/help/user/project/time_tracking.md')
- end
- end
end
def submit_time(quick_action)
diff --git a/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb
index 629d93676eb..c76bc7c3107 100644
--- a/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb
@@ -593,7 +593,7 @@ RSpec.shared_examples 'delete package endpoint' do
project.add_maintainer(user)
end
- it_behaves_like 'a gitlab tracking event', 'API::ConanPackages', 'delete_package'
+ it_behaves_like 'a package tracking event', 'API::ConanPackages', 'delete_package'
it 'deletes a package' do
expect { subject }.to change { Packages::Package.count }.from(2).to(1)
@@ -708,7 +708,7 @@ RSpec.shared_examples 'package file download endpoint' do
context 'tracking the conan_package.tgz download' do
let(:package_file) { package.package_files.find_by(file_name: ::Packages::Conan::FileMetadatum::PACKAGE_BINARY) }
- it_behaves_like 'a gitlab tracking event', 'API::ConanPackages', 'pull_package'
+ it_behaves_like 'a package tracking event', 'API::ConanPackages', 'pull_package'
end
end
@@ -781,7 +781,7 @@ RSpec.shared_examples 'workhorse package file upload endpoint' do
context 'tracking the conan_package.tgz upload' do
let(:file_name) { ::Packages::Conan::FileMetadatum::PACKAGE_BINARY }
- it_behaves_like 'a gitlab tracking event', 'API::ConanPackages', 'push_package'
+ it_behaves_like 'a package tracking event', 'API::ConanPackages', 'push_package'
end
end
@@ -849,12 +849,6 @@ RSpec.shared_examples 'uploads a package file' do
expect(package_file.file_name).to eq(params[:file].original_filename)
end
- it "doesn't attempt to migrate file to object storage" do
- expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
-
- subject
- end
-
context 'with existing package' do
let!(:existing_package) { create(:conan_package, name: 'foo', version: 'bar', project: project) }
@@ -936,8 +930,6 @@ RSpec.shared_examples 'uploads a package file' do
end
end
end
-
- it_behaves_like 'background upload schedules a file migration'
end
end
diff --git a/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb
index 5469fd80a4f..d4479e462af 100644
--- a/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb
@@ -1,6 +1,15 @@
# frozen_string_literal: true
RSpec.shared_examples 'graphql issue list request spec' do
+ let(:issue_ids) { graphql_dig_at(issues_data, :id) }
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ #{all_graphql_fields_for('issues'.classify)}
+ }
+ QUERY
+ end
+
it_behaves_like 'a working graphql query' do
before do
post_query
@@ -109,10 +118,57 @@ RSpec.shared_examples 'graphql issue list request spec' do
let(:ids) { issue_ids }
end
end
+
+ context 'when filtering by confidentiality' do
+ context 'when fetching confidential issues' do
+ let(:issue_filter_params) { { confidential: true } }
+
+ it 'returns only confidential issues' do
+ post_query
+
+ expect(issue_ids).to match_array(to_gid_list(confidential_issues))
+ end
+
+ context 'when user cannot see confidential issues' do
+ it 'returns an empty list' do
+ post_query(external_user)
+
+ expect(issue_ids).to be_empty
+ end
+ end
+ end
+
+ context 'when fetching non-confidential issues' do
+ let(:issue_filter_params) { { confidential: false } }
+
+ it 'returns only non-confidential issues' do
+ post_query
+
+ expect(issue_ids).to match_array(to_gid_list(non_confidential_issues))
+ end
+
+ context 'when user cannot see confidential issues' do
+ it 'returns an empty list' do
+ post_query(external_user)
+
+ expect(issue_ids).to match_array(to_gid_list(public_non_confidential_issues))
+ end
+ end
+ end
+ end
end
describe 'sorting and pagination' do
context 'when sorting by severity' do
+ let(:expected_severity_sorted_asc) { [issue_c, issue_a, issue_b, issue_e, issue_d] }
+
+ before_all do
+ create(:issuable_severity, issue: issue_a, severity: :unknown)
+ create(:issuable_severity, issue: issue_b, severity: :low)
+ create(:issuable_severity, issue: issue_d, severity: :critical)
+ create(:issuable_severity, issue: issue_e, severity: :high)
+ end
+
context 'when ascending' do
it_behaves_like 'sorted paginated query' do
let(:sort_param) { :SEVERITY_ASC }
@@ -147,6 +203,459 @@ RSpec.shared_examples 'graphql issue list request spec' do
end
end
end
+
+ context 'when sorting by due date' do
+ context 'when ascending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :DUE_DATE_ASC }
+ let(:first_param) { 2 }
+ let(:all_records) { to_gid_list(expected_due_date_sorted_asc) }
+ end
+ end
+
+ context 'when descending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :DUE_DATE_DESC }
+ let(:first_param) { 2 }
+ let(:all_records) { to_gid_list(expected_due_date_sorted_desc) }
+ end
+ end
+ end
+
+ context 'when sorting by relative position' do
+ context 'when ascending' do
+ it_behaves_like 'sorted paginated query', is_reversible: true do
+ let(:sort_param) { :RELATIVE_POSITION_ASC }
+ let(:first_param) { 2 }
+ let(:all_records) { to_gid_list(expected_relative_position_sorted_asc) }
+ end
+ end
+ end
+
+ context 'when sorting by label priority' do
+ context 'when ascending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :LABEL_PRIORITY_ASC }
+ let(:first_param) { 2 }
+ let(:all_records) { to_gid_list(expected_label_priority_sorted_asc) }
+ end
+ end
+
+ context 'when descending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :LABEL_PRIORITY_DESC }
+ let(:first_param) { 2 }
+ let(:all_records) { to_gid_list(expected_label_priority_sorted_desc) }
+ end
+ end
+ end
+
+ context 'when sorting by milestone due date' do
+ context 'when ascending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :MILESTONE_DUE_ASC }
+ let(:first_param) { 2 }
+ let(:all_records) { to_gid_list(expected_milestone_sorted_asc) }
+ end
+ end
+
+ context 'when descending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :MILESTONE_DUE_DESC }
+ let(:first_param) { 2 }
+ let(:all_records) { to_gid_list(expected_milestone_sorted_desc) }
+ end
+ end
+ end
+ end
+
+ describe 'N+1 query checks' do
+ let(:extra_iid_for_second_query) { issue_b.iid.to_s }
+ let(:search_params) { { iids: [issue_a.iid.to_s] } }
+ let(:issue_filter_params) { search_params }
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ id
+ #{requested_fields}
+ }
+ QUERY
+ end
+
+ def execute_query
+ post_query
+ end
+
+ context 'when requesting `user_notes_count` and `user_discussions_count`' do
+ let(:requested_fields) { 'userNotesCount userDiscussionsCount' }
+
+ before do
+ create_list(:note_on_issue, 2, noteable: issue_a, project: issue_a.project)
+ create(:note_on_issue, noteable: issue_b, project: issue_b.project)
+ end
+
+ include_examples 'N+1 query check'
+ end
+
+ context 'when requesting `merge_requests_count`' do
+ let(:requested_fields) { 'mergeRequestsCount' }
+
+ before do
+ create_list(:merge_requests_closing_issues, 2, issue: issue_a)
+ create_list(:merge_requests_closing_issues, 3, issue: issue_b)
+ end
+
+ include_examples 'N+1 query check'
+ end
+
+ context 'when requesting `timelogs`' do
+ let(:requested_fields) { 'timelogs { nodes { timeSpent } }' }
+
+ before do
+ create_list(:issue_timelog, 2, issue: issue_a)
+ create(:issue_timelog, issue: issue_b)
+ end
+
+ include_examples 'N+1 query check'
+ end
+
+ context 'when requesting `closed_as_duplicate_of`' do
+ let(:requested_fields) { 'closedAsDuplicateOf { id }' }
+ let(:issue_a_dup) { create(:issue, project: issue_a.project) }
+ let(:issue_b_dup) { create(:issue, project: issue_b.project) }
+
+ before do
+ issue_a.update!(duplicated_to_id: issue_a_dup)
+ issue_b.update!(duplicated_to_id: issue_a_dup)
+ end
+
+ include_examples 'N+1 query check'
+ end
+
+ context 'when award emoji votes' do
+ let(:requested_fields) { 'upvotes downvotes' }
+
+ before do
+ create_list(:award_emoji, 2, name: 'thumbsup', awardable: issue_a)
+ create_list(:award_emoji, 2, name: 'thumbsdown', awardable: issue_b)
+ end
+
+ include_examples 'N+1 query check'
+ end
+
+ context 'when requesting participants' do
+ let(:search_params) { { iids: [issue_a.iid.to_s, issue_c.iid.to_s] } }
+ let(:requested_fields) { 'participants { nodes { name } }' }
+
+ before do
+ create(:award_emoji, :upvote, awardable: issue_a)
+ create(:award_emoji, :upvote, awardable: issue_b)
+ create(:award_emoji, :upvote, awardable: issue_c)
+
+ note_with_emoji_a = create(:note_on_issue, noteable: issue_a, project: issue_a.project)
+ note_with_emoji_b = create(:note_on_issue, noteable: issue_b, project: issue_b.project)
+ note_with_emoji_c = create(:note_on_issue, noteable: issue_c, project: issue_c.project)
+
+ create(:award_emoji, :upvote, awardable: note_with_emoji_a)
+ create(:award_emoji, :upvote, awardable: note_with_emoji_b)
+ create(:award_emoji, :upvote, awardable: note_with_emoji_c)
+ end
+
+ # Executes 3 extra queries to fetch participant_attrs
+ include_examples 'N+1 query check', threshold: 3
+ end
+
+ context 'when requesting labels', :use_sql_query_cache do
+ let(:requested_fields) { 'labels { nodes { id } }' }
+ let(:extra_iid_for_second_query) { same_project_issue2.iid.to_s }
+ let(:search_params) { { iids: [same_project_issue1.iid.to_s] } }
+
+ before do
+ current_project = same_project_issue1.project
+ project_labels = create_list(:label, 2, project: current_project)
+ group_labels = create_list(:group_label, 2, group: current_project.group)
+
+ same_project_issue1.update!(labels: [project_labels.first, group_labels.first].flatten)
+ same_project_issue2.update!(labels: [project_labels, group_labels].flatten)
+ end
+
+ include_examples 'N+1 query check', skip_cached: false
+ end
+ end
+
+ context 'when confidential issues exist' do
+ context 'when user can see confidential issues' do
+ it 'includes confidential issues' do
+ post_query
+
+ all_issues = confidential_issues + non_confidential_issues
+
+ expect(issue_ids).to match_array(to_gid_list(all_issues))
+ expect(issues_data.pluck('confidential')).to match_array(all_issues.map(&:confidential))
+ end
+ end
+
+ context 'when user cannot see confidential issues' do
+ let(:current_user) { external_user }
+
+ it 'does not include confidential issues' do
+ post_query
+
+ expect(issue_ids).to match_array(to_gid_list(public_non_confidential_issues))
+ end
+ end
+ end
+
+ context 'when limiting the number of results' do
+ let(:issue_limit) { 1 }
+ let(:issue_filter_params) { { first: issue_limit } }
+
+ it_behaves_like 'a working graphql query' do
+ before do
+ post_query
+ end
+
+ it 'only returns N issues' do
+ expect(issues_data.size).to eq(issue_limit)
+ end
+ end
+
+ context 'when no limit is provided' do
+ let(:issue_limit) { nil }
+
+ it 'returns all issues' do
+ post_query
+
+ expect(issues_data.size).to be > 1
+ end
+ end
+
+ it 'is expected to check permissions on the first issue only' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ # Newest first, we only want to see the newest checked
+ expect(Ability).not_to receive(:allowed?).with(current_user, :read_issue, issues.first)
+
+ post_query
+ end
+ end
+
+ context 'when the user does not have access to the issue' do
+ let(:current_user) { external_user }
+
+ it 'returns no issues' do
+ public_projects.each do |public_project|
+ public_project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
+ end
+
+ post_query
+
+ expect(issues_data).to eq([])
+ end
+ end
+
+ context 'when fetching escalation status' do
+ let_it_be(:escalation_status) { create(:incident_management_issuable_escalation_status, issue: issue_a) }
+
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ id
+ escalationStatus
+ }
+ QUERY
+ end
+
+ before do
+ issue_a.update_columns(issue_type: Issue.issue_types[:incident])
+ end
+
+ it 'returns the escalation status values' do
+ post_query
+
+ statuses = issues_data.pluck('escalationStatus')
+
+ expect(statuses).to contain_exactly(escalation_status.status_name.upcase.to_s, nil, nil, nil, nil)
+ end
+
+ it 'avoids N+1 queries', :aggregate_failures do
+ control = ActiveRecord::QueryRecorder.new { run_with_clean_state(query, context: { current_user: current_user }) }
+
+ new_incident = create(:incident, project: public_projects.first)
+ create(:incident_management_issuable_escalation_status, issue: new_incident)
+
+ expect { run_with_clean_state(query, context: { current_user: current_user }) }.not_to exceed_query_limit(control)
+ end
+ end
+
+ context 'when fetching alert management alert' do
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ iid
+ alertManagementAlert {
+ title
+ }
+ alertManagementAlerts {
+ nodes {
+ title
+ }
+ }
+ }
+ QUERY
+ end
+
+ it 'avoids N+1 queries' do
+ control = ActiveRecord::QueryRecorder.new { post_query }
+
+ create(:alert_management_alert, :with_incident, project: public_projects.first)
+
+ expect { post_query }.not_to exceed_query_limit(control)
+ end
+
+ it 'returns the alert data' do
+ post_query
+
+ alert_titles = issues_data.map { |issue| issue.dig('alertManagementAlert', 'title') }
+ expected_titles = issues.map { |issue| issue.alert_management_alerts.first&.title }
+
+ expect(alert_titles).to contain_exactly(*expected_titles)
+ end
+
+ it 'returns the alerts data' do
+ post_query
+
+ alert_titles = issues_data.map { |issue| issue.dig('alertManagementAlerts', 'nodes') }
+ expected_titles = issues.map do |issue|
+ issue.alert_management_alerts.map { |alert| { 'title' => alert.title } }
+ end
+
+ expect(alert_titles).to contain_exactly(*expected_titles)
+ end
+ end
+
+ context 'when fetching customer_relations_contacts' do
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ id
+ customerRelationsContacts {
+ nodes {
+ firstName
+ }
+ }
+ }
+ QUERY
+ end
+
+ def clean_state_query
+ run_with_clean_state(query, context: { current_user: current_user })
+ end
+
+ it 'avoids N+1 queries' do
+ create(:issue_customer_relations_contact, :for_issue, issue: issue_a)
+
+ control = ActiveRecord::QueryRecorder.new(skip_cached: false) { clean_state_query }
+
+ create(:issue_customer_relations_contact, :for_issue, issue: issue_a)
+
+ expect { clean_state_query }.not_to exceed_all_query_limit(control)
+ end
+ end
+
+ context 'when fetching labels' do
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ id
+ labels {
+ nodes {
+ id
+ }
+ }
+ }
+ QUERY
+ end
+
+ before do
+ issues.each do |issue|
+ # create a label for each issue we have to properly test N+1
+ label = create(:label, project: issue.project)
+ issue.update!(labels: [label])
+ end
+ end
+
+ def response_label_ids(response_data)
+ response_data.map do |node|
+ node['labels']['nodes'].pluck('id')
+ end.flatten
+ end
+
+ def labels_as_global_ids(issues)
+ issues.map(&:labels).flatten.map(&:to_global_id).map(&:to_s)
+ end
+
+ it 'avoids N+1 queries', :aggregate_failures do
+ control = ActiveRecord::QueryRecorder.new { post_query }
+ expect(issues_data.count).to eq(5)
+ expect(response_label_ids(issues_data)).to match_array(labels_as_global_ids(issues))
+
+ public_project = public_projects.first
+ new_issues = issues + [
+ create(:issue, project: public_project, labels: [create(:label, project: public_project)])
+ ]
+
+ expect { post_query }.not_to exceed_query_limit(control)
+
+ expect(issues_data.count).to eq(6)
+ expect(response_label_ids(issues_data)).to match_array(labels_as_global_ids(new_issues))
+ end
+ end
+
+ context 'when fetching assignees' do
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ id
+ assignees {
+ nodes {
+ id
+ }
+ }
+ }
+ QUERY
+ end
+
+ before do
+ issues.each do |issue|
+ # create an assignee for each issue we have to properly test N+1
+ assignee = create(:user)
+ issue.update!(assignees: [assignee])
+ end
+ end
+
+ def response_assignee_ids(response_data)
+ response_data.map do |node|
+ node['assignees']['nodes'].pluck('id')
+ end.flatten
+ end
+
+ def assignees_as_global_ids(issues)
+ issues.map(&:assignees).flatten.map(&:to_global_id).map(&:to_s)
+ end
+
+ it 'avoids N+1 queries', :aggregate_failures do
+ control = ActiveRecord::QueryRecorder.new { post_query }
+ expect(issues_data.count).to eq(5)
+ expect(response_assignee_ids(issues_data)).to match_array(assignees_as_global_ids(issues))
+
+ public_project = public_projects.first
+ new_issues = issues + [create(:issue, project: public_project, assignees: [create(:user)])]
+
+ expect { post_query }.not_to exceed_query_limit(control)
+
+ expect(issues_data.count).to eq(6)
+ expect(response_assignee_ids(issues_data)).to match_array(assignees_as_global_ids(new_issues))
+ end
end
it 'includes a web_url' do
@@ -167,4 +676,8 @@ RSpec.shared_examples 'graphql issue list request spec' do
def to_gid_list(instance_list)
instance_list.map { |instance| instance.to_gid.to_s }
end
+
+ def issues_data
+ graphql_data.dig(*issue_nodes_path)
+ end
end
diff --git a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb
index fb4aacfd7a9..f5835460a77 100644
--- a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb
@@ -62,6 +62,21 @@ RSpec.shared_examples 'group and project packages query' do
it 'returns the count of the packages' do
expect(packages_count).to eq(4)
end
+
+ context '_links' do
+ let_it_be(:errored_package) { create(:maven_package, :error, project: project1) }
+
+ let(:package_web_paths) { graphql_data_at(resource_type, :packages, :nodes, :_links, :web_path) }
+
+ it 'does not contain the web path of errored package' do
+ expect(package_web_paths.compact).to contain_exactly(
+ "/#{project1.full_path}/-/packages/#{npm_package.id}",
+ "/#{project1.full_path}/-/packages/#{maven_package.id}",
+ "/#{project2.full_path}/-/packages/#{debian_package.id}",
+ "/#{project2.full_path}/-/packages/#{composer_package.id}"
+ )
+ end
+ end
end
context 'when the user does not have access to the resource' do
@@ -139,7 +154,7 @@ RSpec.shared_examples 'group and project packages query' do
end
it 'throws an error' do
- expect_graphql_errors_to_include(/Argument \'sort\' on Field \'packages\' has an invalid value/)
+ expect_graphql_errors_to_include(/Argument 'sort' on Field 'packages' has an invalid value/)
end
end
diff --git a/spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb b/spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb
index 54cc13fac94..6b4d8cae2ce 100644
--- a/spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb
@@ -1,13 +1,13 @@
# frozen_string_literal: true
-RSpec.shared_examples 'perform graphql requests for AccessLevel type objects' do |access_level_kind|
+RSpec.shared_examples 'a GraphQL query for access levels' do |access_level_kind|
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:current_user) { create(:user, maintainer_projects: [project]) }
let_it_be(:variables) { { path: project.full_path } }
- let(:fields) { all_graphql_fields_for("#{access_level_kind.to_s.classify}AccessLevel", max_depth: 2) }
+ let(:fields) { all_graphql_fields_for("#{access_level_kind.to_s.classify}AccessLevel") }
let(:access_levels) { protected_branch.public_send("#{access_level_kind}_access_levels") }
let(:access_levels_count) { access_levels.size }
let(:maintainer_access_level) { access_levels.for_role.first }
@@ -61,17 +61,35 @@ RSpec.shared_examples 'perform graphql requests for AccessLevel type objects' do
create(:protected_branch, "maintainers_can_#{access_level_kind}", project: project)
end
- before do
- post_graphql(query, current_user: current_user, variables: variables)
+ describe 'query' do
+ it 'avoids N+1 queries' do
+ control = ActiveRecord::QueryRecorder.new do
+ post_graphql(query, current_user: current_user, variables: variables)
+ end
+ expect_graphql_errors_to_be_empty
+
+ create("protected_branch_#{access_level_kind}_access_level", protected_branch: protected_branch)
+
+ expect do
+ post_graphql(query, current_user: current_user, variables: variables)
+ end.not_to exceed_all_query_limit(control)
+ expect_graphql_errors_to_be_empty
+ end
end
- it_behaves_like 'a working graphql query'
+ describe 'response' do
+ before do
+ post_graphql(query, current_user: current_user, variables: variables)
+ end
+
+ it_behaves_like 'a working graphql query'
- it 'returns all the access level attributes' do
- expect(maintainer_access_level_data['accessLevel']).to eq(maintainer_access_level.access_level)
- expect(maintainer_access_level_data['accessLevelDescription']).to eq(maintainer_access_level.humanize)
- expect(maintainer_access_level_data.dig('group', 'name')).to be_nil
- expect(maintainer_access_level_data.dig('user', 'name')).to be_nil
+ it 'returns all the access level attributes' do
+ expect(maintainer_access_level_data['accessLevel']).to eq(maintainer_access_level.access_level)
+ expect(maintainer_access_level_data['accessLevelDescription']).to eq(maintainer_access_level.humanize)
+ expect(maintainer_access_level_data.dig('group', 'name')).to be_nil
+ expect(maintainer_access_level_data.dig('user', 'name')).to be_nil
+ end
end
end
end
diff --git a/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb
index 8bf6b162508..7803f0ff04d 100644
--- a/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb
@@ -156,25 +156,13 @@ RSpec.shared_examples 'process helm upload' do |user_type, status|
end
context 'and direct upload disabled' do
- context 'and background upload disabled' do
- let(:fog_connection) do
- stub_package_file_object_storage(direct_upload: false, background_upload: false)
- end
-
- it_behaves_like 'creates helm package files'
+ let(:fog_connection) do
+ stub_package_file_object_storage(direct_upload: false)
end
- context 'and background upload enabled' do
- let(:fog_connection) do
- stub_package_file_object_storage(direct_upload: false, background_upload: true)
- end
-
- it_behaves_like 'creates helm package files'
- end
+ it_behaves_like 'creates helm package files'
end
end
-
- it_behaves_like 'background upload schedules a file migration'
end
end
diff --git a/spec/support/shared_examples/requests/api/notes_shared_examples.rb b/spec/support/shared_examples/requests/api/notes_shared_examples.rb
index 11f9565989f..efe5ed3bcf9 100644
--- a/spec/support/shared_examples/requests/api/notes_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/notes_shared_examples.rb
@@ -159,7 +159,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
expect do
post api(uri, user), params: { body: 'hi!' }
- end.to change(Event, :count).by(1)
+ end.to change { Event.count }.by(1)
end
context 'setting created_at' do
diff --git a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
index 85ac2b5e1ea..b55639a6b82 100644
--- a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
@@ -323,6 +323,171 @@ RSpec.shared_examples 'handling get metadata requests' do |scope: :project|
end
end
+RSpec.shared_examples 'handling audit request' do |path:, scope: :project|
+ using RSpec::Parameterized::TableSyntax
+
+ let(:headers) { {} }
+ let(:params) do
+ ActiveSupport::Gzip.compress(
+ Gitlab::Json.dump({
+ '@gitlab-org/npm-test': ['1.0.6'],
+ 'call-bind': ['1.0.2']
+ })
+ )
+ end
+
+ let(:default_headers) do
+ { 'HTTP_CONTENT_ENCODING' => 'gzip', 'CONTENT_TYPE' => 'application/json' }
+ end
+
+ subject { post(url, headers: headers.merge(default_headers), params: params) }
+
+ shared_examples 'accept audit request' do |status:|
+ it 'accepts the audit request' do
+ subject
+
+ expect(response).to have_gitlab_http_status(status)
+ expect(response.media_type).to eq('application/json')
+ expect(json_response).to eq([])
+ end
+ end
+
+ shared_examples 'reject audit request' do |status:|
+ it 'rejects the audit request' do
+ subject
+
+ expect(response).to have_gitlab_http_status(status)
+ end
+ end
+
+ shared_examples 'redirect audit request' do |status:|
+ it 'redirects audit request' do
+ subject
+
+ expect(response).to have_gitlab_http_status(status)
+ expect(response.headers['Location']).to eq("https://registry.npmjs.org/-/npm/v1/security/#{path}")
+ end
+ end
+
+ shared_examples 'handling all conditions' do
+ include_context 'dependency proxy helpers context'
+
+ where(:auth, :request_forward, :visibility, :user_role, :expected_result, :expected_status) do
+ nil | true | :public | nil | :reject | :unauthorized
+ nil | false | :public | nil | :reject | :unauthorized
+ nil | true | :private | nil | :reject | :unauthorized
+ nil | false | :private | nil | :reject | :unauthorized
+ nil | true | :internal | nil | :reject | :unauthorized
+ nil | false | :internal | nil | :reject | :unauthorized
+
+ :oauth | true | :public | :guest | :redirect | :temporary_redirect
+ :oauth | true | :public | :reporter | :redirect | :temporary_redirect
+ :oauth | false | :public | :guest | :accept | :ok
+ :oauth | false | :public | :reporter | :accept | :ok
+ :oauth | true | :private | :reporter | :redirect | :temporary_redirect
+ :oauth | false | :private | :guest | :reject | :forbidden
+ :oauth | false | :private | :reporter | :accept | :ok
+ :oauth | true | :private | :guest | :redirect | :temporary_redirect
+ :oauth | true | :internal | :guest | :redirect | :temporary_redirect
+ :oauth | true | :internal | :reporter | :redirect | :temporary_redirect
+ :oauth | false | :internal | :guest | :accept | :ok
+ :oauth | false | :internal | :reporter | :accept | :ok
+
+ :personal_access_token | true | :public | :guest | :redirect | :temporary_redirect
+ :personal_access_token | true | :public | :reporter | :redirect | :temporary_redirect
+ :personal_access_token | false | :public | :guest | :accept | :ok
+ :personal_access_token | false | :public | :reporter | :accept | :ok
+ :personal_access_token | true | :private | :guest | :redirect | :temporary_redirect
+ :personal_access_token | true | :private | :reporter | :redirect | :temporary_redirect
+ :personal_access_token | false | :private | :guest | :reject | :forbidden # instance might fail
+ :personal_access_token | false | :private | :reporter | :accept | :ok
+ :personal_access_token | true | :internal | :guest | :redirect | :temporary_redirect
+ :personal_access_token | true | :internal | :reporter | :redirect | :temporary_redirect
+ :personal_access_token | false | :internal | :guest | :accept | :ok
+ :personal_access_token | false | :internal | :reporter | :accept | :ok
+
+ :job_token | true | :public | :developer | :redirect | :temporary_redirect
+ :job_token | false | :public | :developer | :accept | :ok
+ :job_token | true | :private | :developer | :redirect | :temporary_redirect
+ :job_token | false | :private | :developer | :accept | :ok
+ :job_token | true | :internal | :developer | :redirect | :temporary_redirect
+ :job_token | false | :internal | :developer | :accept | :ok
+
+ :deploy_token | true | :public | nil | :redirect | :temporary_redirect
+ :deploy_token | false | :public | nil | :accept | :ok
+ :deploy_token | true | :private | nil | :redirect | :temporary_redirect
+ :deploy_token | false | :private | nil | :accept | :ok
+ :deploy_token | true | :internal | nil | :redirect | :temporary_redirect
+ :deploy_token | false | :internal | nil | :accept | :ok
+ end
+
+ with_them do
+ let(:headers) do
+ case auth
+ when :oauth
+ build_token_auth_header(token.plaintext_token)
+ when :personal_access_token
+ build_token_auth_header(personal_access_token.token)
+ when :job_token
+ build_token_auth_header(job.token)
+ when :deploy_token
+ build_token_auth_header(deploy_token.token)
+ else
+ {}
+ end
+ end
+
+ before do
+ project.send("add_#{user_role}", user) if user_role
+ project.update!(visibility: visibility.to_s)
+
+ if scope == :instance
+ allow_fetch_application_setting(attribute: "npm_package_requests_forwarding", return_value: request_forward)
+ else
+ allow_fetch_cascade_application_setting(attribute: "npm_package_requests_forwarding", return_value: request_forward)
+ end
+ end
+
+ example_name = "#{params[:expected_result]} audit request"
+ status = params[:expected_status]
+
+ if scope == :instance && params[:expected_status] != :unauthorized
+ if params[:request_forward]
+ example_name = 'redirect audit request'
+ status = :temporary_redirect
+ else
+ example_name = 'reject audit request'
+ status = :not_found
+ end
+ end
+
+ it_behaves_like example_name, status: status
+ end
+ end
+
+ context 'with a group namespace' do
+ it_behaves_like 'handling all conditions'
+ end
+
+ context 'with a developer' do
+ let(:headers) { build_token_auth_header(personal_access_token.token) }
+
+ before do
+ project.add_developer(user)
+ end
+
+ context 'with a job token' do
+ let(:headers) { build_token_auth_header(job.token) }
+
+ before do
+ job.update!(status: :success)
+ end
+
+ it_behaves_like 'reject audit request', status: :unauthorized
+ end
+ end
+end
+
RSpec.shared_examples 'handling get dist tags requests' do |scope: :project|
using RSpec::Parameterized::TableSyntax
include_context 'set package name from package name type'
diff --git a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
index fdd55893deb..bace570e47a 100644
--- a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
@@ -224,25 +224,13 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
end
context 'and direct upload disabled' do
- context 'and background upload disabled' do
- let(:fog_connection) do
- stub_package_file_object_storage(direct_upload: false, background_upload: false)
- end
-
- it_behaves_like 'creates nuget package files'
+ let(:fog_connection) do
+ stub_package_file_object_storage(direct_upload: false)
end
- context 'and background upload enabled' do
- let(:fog_connection) do
- stub_package_file_object_storage(direct_upload: false, background_upload: true)
- end
-
- it_behaves_like 'creates nuget package files'
- end
+ it_behaves_like 'creates nuget package files'
end
end
-
- it_behaves_like 'background upload schedules a file migration'
end
end
@@ -507,7 +495,7 @@ RSpec.shared_examples 'nuget upload endpoint' do |symbol_package: false|
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
let(:headers) { user_headers.merge(workhorse_headers) }
- let(:snowplow_gitlab_standard_context) { { project: project, user: user, namespace: project.namespace } }
+ let(:snowplow_gitlab_standard_context) { { project: project, user: user, namespace: project.namespace, property: 'i_package_nuget_user' } }
before do
update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false))
diff --git a/spec/support/shared_examples/requests/api/packages_shared_examples.rb b/spec/support/shared_examples/requests/api/packages_shared_examples.rb
index 860cb1b1d86..98264baa61d 100644
--- a/spec/support/shared_examples/requests/api/packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/packages_shared_examples.rb
@@ -142,15 +142,26 @@ RSpec.shared_examples 'job token for package uploads' do |authorize_endpoint: fa
end
end
-RSpec.shared_examples 'a package tracking event' do |category, action|
+RSpec.shared_examples 'a package tracking event' do |category, action, service_ping_context = true|
before do
stub_feature_flags(collect_package_events: true)
end
+ let(:context) do
+ [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll,
+ event: snowplow_gitlab_standard_context[:property]).to_h]
+ end
+
it "creates a gitlab tracking event #{action}", :snowplow, :aggregate_failures do
expect { subject }.to change { Packages::Event.count }.by(1)
- expect_snowplow_event(category: category, action: action, **snowplow_gitlab_standard_context)
+ if service_ping_context
+ expect_snowplow_event(category: category, action: action,
+ label: "redis_hll_counters.user_packages.user_packages_total_unique_counts_monthly",
+ context: context, **snowplow_gitlab_standard_context)
+ else
+ expect_snowplow_event(category: category, action: action, **snowplow_gitlab_standard_context)
+ end
end
end
diff --git a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
index a9b44015206..a267476b7cb 100644
--- a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
@@ -95,25 +95,13 @@ RSpec.shared_examples 'PyPI package creation' do |user_type, status, add_member
end
context 'and direct upload disabled' do
- context 'and background upload disabled' do
- let(:fog_connection) do
- stub_package_file_object_storage(direct_upload: false, background_upload: false)
- end
-
- it_behaves_like 'creating pypi package files'
+ let(:fog_connection) do
+ stub_package_file_object_storage(direct_upload: false)
end
- context 'and background upload enabled' do
- let(:fog_connection) do
- stub_package_file_object_storage(direct_upload: false, background_upload: true)
- end
-
- it_behaves_like 'creating pypi package files'
- end
+ it_behaves_like 'creating pypi package files'
end
end
-
- it_behaves_like 'background upload schedules a file migration'
end
end
@@ -285,7 +273,7 @@ RSpec.shared_examples 'pypi simple API endpoint' do
let(:url) { "/projects/#{project.id}/packages/pypi/simple/my-package" }
let(:headers) { basic_auth_header(user.username, personal_access_token.token) }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: group, property: 'i_package_pypi_user' } }
it_behaves_like 'PyPI package versions', :developer, :success
end
diff --git a/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb b/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb
index 2d036cb2aa3..2154a76d765 100644
--- a/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb
@@ -71,11 +71,7 @@ RSpec.shared_examples 'repository_storage_moves API' do |container_type|
get_container_repository_storage_moves
json_ids = json_response.map { |storage_move| storage_move['id'] }
- expect(json_ids).to eq([
- storage_move.id,
- storage_move_middle.id,
- storage_move_oldest.id
- ])
+ expect(json_ids).to eq([storage_move.id, storage_move_middle.id, storage_move_oldest.id])
end
describe 'permissions' do
diff --git a/spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb
index f075927e7bf..da09d70c777 100644
--- a/spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb
@@ -122,21 +122,11 @@ RSpec.shared_examples 'process rubygems upload' do |user_type, status, add_membe
end
context 'and direct upload disabled' do
- context 'and background upload disabled' do
- let(:fog_connection) do
- stub_package_file_object_storage(direct_upload: false, background_upload: false)
- end
-
- it_behaves_like 'creates rubygems package files'
+ let(:fog_connection) do
+ stub_package_file_object_storage(direct_upload: false)
end
- context 'and background upload enabled' do
- let(:fog_connection) do
- stub_package_file_object_storage(direct_upload: false, background_upload: true)
- end
-
- it_behaves_like 'creates rubygems package files'
- end
+ it_behaves_like 'creates rubygems package files'
end
end
end
diff --git a/spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb b/spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb
index bdff2c65691..ae2855083f6 100644
--- a/spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb
@@ -264,21 +264,11 @@ RSpec.shared_examples 'process terraform module upload' do |user_type, status, a
end
context 'and direct upload disabled' do
- context 'and background upload disabled' do
- let(:fog_connection) do
- stub_package_file_object_storage(direct_upload: false, background_upload: false)
- end
-
- it_behaves_like 'creates terraform module package files'
+ let(:fog_connection) do
+ stub_package_file_object_storage(direct_upload: false)
end
- context 'and background upload enabled' do
- let(:fog_connection) do
- stub_package_file_object_storage(direct_upload: false, background_upload: true)
- end
-
- it_behaves_like 'creates terraform module package files'
- end
+ it_behaves_like 'creates terraform module package files'
end
end
end
diff --git a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
index 11759b6671f..82ed6eb4c95 100644
--- a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
+++ b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
@@ -68,6 +68,7 @@ RSpec.shared_examples 'rate-limited token requests' do
# Set low limits
settings_to_set[:"#{throttle_setting_prefix}_requests_per_period"] = requests_per_period
settings_to_set[:"#{throttle_setting_prefix}_period_in_seconds"] = period_in_seconds
+ travel_back
end
after do
@@ -220,6 +221,7 @@ RSpec.shared_examples 'rate-limited web authenticated requests' do
# Set low limits
settings_to_set[:"#{throttle_setting_prefix}_requests_per_period"] = requests_per_period
settings_to_set[:"#{throttle_setting_prefix}_period_in_seconds"] = period_in_seconds
+ travel_back
end
after do
@@ -436,6 +438,7 @@ RSpec.shared_examples 'rate-limited unauthenticated requests' do
# Set low limits
settings_to_set[:"#{throttle_setting_prefix}_requests_per_period"] = requests_per_period
settings_to_set[:"#{throttle_setting_prefix}_period_in_seconds"] = period_in_seconds
+ travel_back
end
context 'when the throttle is enabled' do
diff --git a/spec/support/shared_examples/security_training_providers_importer.rb b/spec/support/shared_examples/security_training_providers_importer.rb
index 568e3e1a4f2..69d92964270 100644
--- a/spec/support/shared_examples/security_training_providers_importer.rb
+++ b/spec/support/shared_examples/security_training_providers_importer.rb
@@ -8,7 +8,7 @@ RSpec.shared_examples 'security training providers importer' do
end
it 'upserts security training providers' do
- expect { 2.times { subject } }.to change(security_training_providers, :count).from(0).to(2)
+ expect { 2.times { subject } }.to change { security_training_providers.count }.from(0).to(2)
expect(security_training_providers.all.map(&:name)).to match_array(['Kontra', 'Secure Code Warrior'])
end
end
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb
index 0db9519f760..6a9da91eaa7 100644
--- a/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb
@@ -11,7 +11,7 @@ RSpec.shared_examples 'creates an alert management alert or errors' do
it 'creates AlertManagement::Alert' do
expect(Gitlab::AppLogger).not_to receive(:warn)
- expect { subject }.to change(AlertManagement::Alert, :count).by(1)
+ expect { subject }.to change { AlertManagement::Alert.count }.by(1)
end
it 'executes the alert service hooks' do
@@ -118,7 +118,7 @@ end
RSpec.shared_examples 'does not create an alert management alert' do
specify do
- expect { subject }.not_to change(AlertManagement::Alert, :count)
+ expect { subject }.not_to change { AlertManagement::Alert.count }
end
end
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/incident_resolution_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/incident_resolution_shared_examples.rb
index 1973577d742..2740b6bf59d 100644
--- a/spec/support/shared_examples/services/alert_management/alert_processing/incident_resolution_shared_examples.rb
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/incident_resolution_shared_examples.rb
@@ -14,7 +14,7 @@ RSpec.shared_examples 'closes related incident if enabled' do
specify do
expect { Sidekiq::Testing.inline! { subject } }
.to change { alert.issue.reload.closed? }.from(false).to(true)
- .and change(ResourceStateEvent, :count).by(1)
+ .and change { ResourceStateEvent.count }.by(1)
end
end
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/system_notes_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/system_notes_shared_examples.rb
index 57d598c0259..2d0815ba27c 100644
--- a/spec/support/shared_examples/services/alert_management/alert_processing/system_notes_shared_examples.rb
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/system_notes_shared_examples.rb
@@ -19,7 +19,7 @@ RSpec.shared_examples 'creates expected system notes for alert' do |*notes|
end
it "for #{notes.join(', ')}" do
- expect { subject }.to change(Note, :count).by(expected_note_count)
+ expect { subject }.to change { Note.count }.by(expected_note_count)
expected_notes.each_value.with_index do |value, index|
expect(new_notes[index]).to include(value)
@@ -29,6 +29,6 @@ end
RSpec.shared_examples 'does not create a system note for alert' do
specify do
- expect { subject }.not_to change(Note, :count)
+ expect { subject }.not_to change { Note.count }
end
end
diff --git a/spec/support/shared_examples/services/alert_management_shared_examples.rb b/spec/support/shared_examples/services/alert_management_shared_examples.rb
index b46ace1824a..b8fc2eb5475 100644
--- a/spec/support/shared_examples/services/alert_management_shared_examples.rb
+++ b/spec/support/shared_examples/services/alert_management_shared_examples.rb
@@ -68,8 +68,8 @@ RSpec.shared_examples 'processes one firing and one resolved prometheus alerts'
expect(Gitlab::AppLogger).not_to receive(:warn)
expect { subject }
- .to change(AlertManagement::Alert, :count).by(1)
- .and change(Note, :count).by(1)
+ .to change { AlertManagement::Alert.count }.by(1)
+ .and change { Note.count }.by(1)
expect(subject).to be_success
expect(subject.payload).to eq({})
diff --git a/spec/support/shared_examples/services/approval_state_updated_trigger_shared_examples.rb b/spec/support/shared_examples/services/approval_state_updated_trigger_shared_examples.rb
new file mode 100644
index 00000000000..455fd308be8
--- /dev/null
+++ b/spec/support/shared_examples/services/approval_state_updated_trigger_shared_examples.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'triggers GraphQL subscription mergeRequestApprovalStateUpdated' do
+ specify do
+ expect(GraphqlTriggers).to receive(:merge_request_approval_state_updated).with(merge_request)
+
+ action
+ end
+end
+
+RSpec.shared_examples 'does not trigger GraphQL subscription mergeRequestApprovalStateUpdated' do
+ specify do
+ expect(GraphqlTriggers).not_to receive(:merge_request_approval_state_updated)
+
+ action
+ end
+end
diff --git a/spec/support/shared_examples/services/boards/boards_create_service_shared_examples.rb b/spec/support/shared_examples/services/boards/boards_create_service_shared_examples.rb
index f28c78aec97..67a5af587e9 100644
--- a/spec/support/shared_examples/services/boards/boards_create_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/boards/boards_create_service_shared_examples.rb
@@ -3,7 +3,7 @@
RSpec.shared_examples 'boards create service' do
context 'when parent does not have a board' do
it 'creates a new board' do
- expect { service.execute }.to change(Board, :count).by(1)
+ expect { service.execute }.to change { Board.count }.by(1)
end
it 'creates the default lists' do
@@ -23,7 +23,7 @@ RSpec.shared_examples 'boards create service' do
it 'does not create a new board' do
expect(service).to receive(:can_create_board?) { false }
- expect { service.execute }.not_to change(parent.boards, :count)
+ expect { service.execute }.not_to change { parent.boards.count }
end
end
end
diff --git a/spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb b/spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb
index fd832d4484d..80d5c771abd 100644
--- a/spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb
@@ -2,7 +2,7 @@
RSpec.shared_examples 'boards list service' do
it 'does not create a new board' do
- expect { service.execute }.not_to change(parent.boards, :count)
+ expect { service.execute }.not_to change { parent.boards.count }
end
it 'returns parent boards' do
diff --git a/spec/support/shared_examples/services/boards/boards_recent_visit_shared_examples.rb b/spec/support/shared_examples/services/boards/boards_recent_visit_shared_examples.rb
index 68ea460dabc..8bf01ad84ff 100644
--- a/spec/support/shared_examples/services/boards/boards_recent_visit_shared_examples.rb
+++ b/spec/support/shared_examples/services/boards/boards_recent_visit_shared_examples.rb
@@ -5,7 +5,7 @@ RSpec.shared_examples 'boards recent visit' do
describe '#visited' do
it 'creates a visit if one does not exists' do
- expect { described_class.visited!(user, board) }.to change(described_class, :count).by(1)
+ expect { described_class.visited!(user, board) }.to change { described_class.count }.by(1)
end
shared_examples 'was visited previously' do
diff --git a/spec/support/shared_examples/services/boards/lists_destroy_service_shared_examples.rb b/spec/support/shared_examples/services/boards/lists_destroy_service_shared_examples.rb
index af88644ced7..52d427d6684 100644
--- a/spec/support/shared_examples/services/boards/lists_destroy_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/boards/lists_destroy_service_shared_examples.rb
@@ -5,7 +5,7 @@ RSpec.shared_examples 'lists destroy service' do
it 'removes list from board' do
service = described_class.new(parent, user)
- expect { service.execute(list) }.to change(board.lists, :count).by(-1)
+ expect { service.execute(list) }.to change { board.lists.count }.by(-1)
end
it 'decrements position of higher lists' do
@@ -24,6 +24,6 @@ RSpec.shared_examples 'lists destroy service' do
it 'does not remove list from board when list type is closed' do
service = described_class.new(parent, user)
- expect { service.execute(closed_list) }.not_to change(board.lists, :count)
+ expect { service.execute(closed_list) }.not_to change { board.lists.count }
end
end
diff --git a/spec/support/shared_examples/services/boards/lists_list_service_shared_examples.rb b/spec/support/shared_examples/services/boards/lists_list_service_shared_examples.rb
index e1143562661..b9f28fab558 100644
--- a/spec/support/shared_examples/services/boards/lists_list_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/boards/lists_list_service_shared_examples.rb
@@ -5,7 +5,7 @@ RSpec.shared_examples 'lists list service' do
let!(:backlog_list) { create_backlog_list(board) }
it 'does not create a backlog list' do
- expect { service.execute(board) }.not_to change(board.lists, :count)
+ expect { service.execute(board) }.not_to change { board.lists.count }
end
it "returns board's lists" do
@@ -35,11 +35,11 @@ RSpec.shared_examples 'lists list service' do
context 'when the board does not have a backlog list' do
it 'creates a backlog list' do
- expect { service.execute(board) }.to change(board.lists, :count).by(1)
+ expect { service.execute(board) }.to change { board.lists.count }.by(1)
end
it 'does not create a backlog list when create_default_lists is false' do
- expect { service.execute(board, create_default_lists: false) }.not_to change(board.lists, :count)
+ expect { service.execute(board, create_default_lists: false) }.not_to change { board.lists.count }
end
it "returns board's lists" do
diff --git a/spec/support/shared_examples/services/container_expiration_policy_shared_examples.rb b/spec/support/shared_examples/services/container_expiration_policy_shared_examples.rb
index 28bf46a57d5..38e19e58706 100644
--- a/spec/support/shared_examples/services/container_expiration_policy_shared_examples.rb
+++ b/spec/support/shared_examples/services/container_expiration_policy_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples 'updating the container expiration policy attributes' do |mode:, from: {}, to:|
+RSpec.shared_examples 'updating the container expiration policy attributes' do |mode:, to:, from: {}|
if mode == :create
it 'creates a new container expiration policy' do
expect { subject }
diff --git a/spec/support/shared_examples/services/dependency_proxy_ttl_policies_shared_examples.rb b/spec/support/shared_examples/services/dependency_proxy_ttl_policies_shared_examples.rb
index f6692646ca8..dcc9c3d898f 100644
--- a/spec/support/shared_examples/services/dependency_proxy_ttl_policies_shared_examples.rb
+++ b/spec/support/shared_examples/services/dependency_proxy_ttl_policies_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples 'updating the dependency proxy image ttl policy attributes' do |from: {}, to:|
+RSpec.shared_examples 'updating the dependency proxy image ttl policy attributes' do |to:, from: {}|
it_behaves_like 'not creating the dependency proxy image ttl policy'
it 'updates the dependency proxy image ttl policy' do
diff --git a/spec/support/shared_examples/services/incident_shared_examples.rb b/spec/support/shared_examples/services/incident_shared_examples.rb
index b533b095aac..a87e7c1f801 100644
--- a/spec/support/shared_examples/services/incident_shared_examples.rb
+++ b/spec/support/shared_examples/services/incident_shared_examples.rb
@@ -55,7 +55,7 @@ RSpec.shared_examples 'incident management label service' do
shared_examples 'existing label' do
it 'returns the existing label' do
- expect { execute }.not_to change(Label, :count)
+ expect { execute }.not_to change { Label.count }
expect(execute).to be_success
expect(execute.payload).to eq(label: label)
@@ -64,7 +64,7 @@ RSpec.shared_examples 'incident management label service' do
shared_examples 'new label' do
it 'creates a new label' do
- expect { execute }.to change(Label, :count).by(1)
+ expect { execute }.to change { Label.count }.by(1)
label = project.reload.labels.last
expect(execute).to be_success
diff --git a/spec/support/shared_examples/services/issuable/update_service_shared_examples.rb b/spec/support/shared_examples/services/issuable/update_service_shared_examples.rb
index 3d90885dd6f..ff7acc7e907 100644
--- a/spec/support/shared_examples/services/issuable/update_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable/update_service_shared_examples.rb
@@ -5,7 +5,7 @@ RSpec.shared_examples_for 'issuable update service updating last_edited_at value
let(:update_params) { { title: 'updated title' } }
it 'does not update last_edited values' do
- expect { update_issuable }.to change(issuable, :title).from(issuable.title).to('updated title').and(
+ expect { update_issuable }.to change { issuable.title }.from(issuable.title).to('updated title').and(
not_change(issuable, :last_edited_at)
).and(
not_change(issuable, :last_edited_by)
@@ -19,10 +19,10 @@ RSpec.shared_examples_for 'issuable update service updating last_edited_at value
it 'updates last_edited values' do
expect do
update_issuable
- end.to change(issuable, :description).from(issuable.description).to('updated description').and(
- change(issuable, :last_edited_at)
+ end.to change { issuable.description }.from(issuable.description).to('updated description').and(
+ change { issuable.last_edited_at }
).and(
- change(issuable, :last_edited_by)
+ change { issuable.last_edited_by }
)
end
end
diff --git a/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb b/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb
index 65351ac94ab..12f2b5d78a5 100644
--- a/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb
@@ -24,7 +24,7 @@ RSpec.shared_examples 'issuable link creation' do
end
it 'no relationship is created' do
- expect { subject }.not_to change(issuable_link_class, :count)
+ expect { subject }.not_to change { issuable_link_class.count }
end
end
@@ -38,7 +38,7 @@ RSpec.shared_examples 'issuable link creation' do
end
it 'no relationship is created' do
- expect { subject }.not_to change(issuable_link_class, :count)
+ expect { subject }.not_to change { issuable_link_class.count }
end
end
@@ -54,7 +54,7 @@ RSpec.shared_examples 'issuable link creation' do
end
it 'no relationship is created' do
- expect { subject }.not_to change(issuable_link_class, :count)
+ expect { subject }.not_to change { issuable_link_class.count }
end
end
@@ -64,7 +64,7 @@ RSpec.shared_examples 'issuable link creation' do
end
it 'creates relationships' do
- expect { subject }.to change(issuable_link_class, :count).by(2)
+ expect { subject }.to change { issuable_link_class.count }.by(2)
expect(issuable_link_class.find_by!(target: issuable2)).to have_attributes(source: issuable, link_type: 'relates_to')
expect(issuable_link_class.find_by!(target: issuable3)).to have_attributes(source: issuable, link_type: 'relates_to')
diff --git a/spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb b/spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb
index 5e80014da1d..cc170c6544d 100644
--- a/spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb
@@ -8,7 +8,7 @@ RSpec.shared_examples 'a destroyable issuable link' do
end
it 'removes related issue' do
- expect { subject }.to change(issuable_link.class, :count).by(-1)
+ expect { subject }.to change { issuable_link.class.count }.by(-1)
end
it 'creates notes' do
@@ -28,7 +28,7 @@ RSpec.shared_examples 'a destroyable issuable link' do
context 'when failing to remove an issuable link' do
it 'does not remove relation' do
- expect { subject }.not_to change(issuable_link.class, :count).from(1)
+ expect { subject }.not_to change { issuable_link.class.count }.from(1)
end
it 'does not create notes' do
diff --git a/spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb b/spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb
index f7a6bd3676a..11a786fdefb 100644
--- a/spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb
+++ b/spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples 'updating the namespace package setting attributes' do |from: {}, to:|
+RSpec.shared_examples 'updating the namespace package setting attributes' do |to:, from: {}|
it_behaves_like 'not creating the namespace package setting'
it 'updates the namespace package setting' do
diff --git a/spec/support/shared_examples/services/packages_shared_examples.rb b/spec/support/shared_examples/services/packages_shared_examples.rb
index ca4dea90c55..e0dd08ec50e 100644
--- a/spec/support/shared_examples/services/packages_shared_examples.rb
+++ b/spec/support/shared_examples/services/packages_shared_examples.rb
@@ -188,20 +188,6 @@ RSpec.shared_examples 'returns paginated packages' do
end
end
-RSpec.shared_examples 'background upload schedules a file migration' do
- context 'background upload enabled' do
- before do
- stub_package_file_object_storage(background_upload: true)
- end
-
- it 'schedules migration of file to object storage' do
- expect(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async).with('Packages::PackageFileUploader', 'Packages::PackageFile', :file, kind_of(Numeric))
-
- subject
- end
- end
-end
-
RSpec.shared_context 'package filter context' do
def package_filter_url(filter, param)
"/projects/#{project.id}/packages?package_#{filter}=#{param}"
diff --git a/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb b/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
index 14af35e58b7..9f940d27341 100644
--- a/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
@@ -71,7 +71,7 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
it 'does not enqueue a GC run' do
expect { subject.execute }
- .not_to change(Projects::GitGarbageCollectWorker.jobs, :count)
+ .not_to change { Projects::GitGarbageCollectWorker.jobs.count }
end
end
@@ -84,12 +84,12 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
stub_application_setting(housekeeping_enabled: false)
expect { subject.execute }
- .not_to change(Projects::GitGarbageCollectWorker.jobs, :count)
+ .not_to change { Projects::GitGarbageCollectWorker.jobs.count }
end
it 'enqueues a GC run' do
expect { subject.execute }
- .to change(Projects::GitGarbageCollectWorker.jobs, :count).by(1)
+ .to change { Projects::GitGarbageCollectWorker.jobs.count }.by(1)
end
end
end
diff --git a/spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb b/spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb
index 4c00faee56b..8a937303711 100644
--- a/spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb
+++ b/spec/support/shared_examples/services/repositories/housekeeping_shared_examples.rb
@@ -12,7 +12,7 @@ RSpec.shared_examples 'housekeeps repository' do
expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :incremental_repack, :the_lease_key, :the_uuid).and_call_original
Sidekiq::Testing.fake! do
- expect { subject.execute }.to change(resource.git_garbage_collect_worker_klass.jobs, :size).by(1)
+ expect { subject.execute }.to change { resource.git_garbage_collect_worker_klass.jobs.size }.by(1)
end
end
@@ -71,9 +71,6 @@ RSpec.shared_examples 'housekeeps repository' do
# At push 10, 20, ... (except those above)
expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :incremental_repack, :the_lease_key, :the_uuid)
.exactly(16).times
- # At push 6, 12, 18, ... (except those above)
- expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :pack_refs, :the_lease_key, :the_uuid)
- .exactly(27).times
201.times do
subject.increment!
@@ -82,6 +79,37 @@ RSpec.shared_examples 'housekeeps repository' do
expect(resource.pushes_since_gc).to eq(1)
end
+
+ context 'when optimized_repository feature flag is disabled' do
+ before do
+ stub_feature_flags(optimized_housekeeping: false)
+ end
+
+ it 'calls also the garbage collect worker with pack_refs every 6 commits' do
+ allow(subject).to receive(:try_obtain_lease).and_return(:the_uuid)
+ allow(subject).to receive(:lease_key).and_return(:the_lease_key)
+
+ # At push 200
+ expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :gc, :the_lease_key, :the_uuid)
+ .once
+ # At push 50, 100, 150
+ expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :full_repack, :the_lease_key, :the_uuid)
+ .exactly(3).times
+ # At push 10, 20, ... (except those above)
+ expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :incremental_repack, :the_lease_key, :the_uuid)
+ .exactly(16).times
+ # At push 6, 12, 18, ... (except those above)
+ expect(resource.git_garbage_collect_worker_klass).to receive(:perform_async).with(resource.id, :pack_refs, :the_lease_key, :the_uuid)
+ .exactly(27).times
+
+ 201.times do
+ subject.increment!
+ subject.execute if subject.needed?
+ end
+
+ expect(resource.pushes_since_gc).to eq(1)
+ end
+ end
end
it 'runs the task specifically requested' do
@@ -107,6 +135,17 @@ RSpec.shared_examples 'housekeeps repository' do
allow(resource).to receive(:pushes_since_gc).and_return(10)
expect(subject.needed?).to eq(true)
end
+
+ context 'when optimized_housekeeping is disabled' do
+ before do
+ stub_feature_flags(optimized_housekeeping: false)
+ end
+
+ it 'returns true pack refs is needed' do
+ allow(resource).to receive(:pushes_since_gc).and_return(described_class::PACK_REFS_PERIOD)
+ expect(subject.needed?).to eq(true)
+ end
+ end
end
describe '#increment!' do
diff --git a/spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb b/spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb
index 97304680316..acf15730180 100644
--- a/spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb
+++ b/spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb
@@ -11,7 +11,7 @@ RSpec.shared_examples 'moves repository shard in bulk' do
describe '#execute' do
it 'schedules container repository storage moves' do
expect { subject.execute(source_storage_name, destination_storage_name) }
- .to change(move_service_klass, :count).by(1)
+ .to change { move_service_klass.count }.by(1)
storage_move = container.repository_storage_moves.last!
@@ -29,7 +29,7 @@ RSpec.shared_examples 'moves repository shard in bulk' do
expect(subject).to receive(:log_info)
.with(/Container #{container.full_path} \(#{container.id}\) was skipped: #{container.class} is read-only/)
expect { subject.execute(source_storage_name, destination_storage_name) }
- .to change(move_service_klass, :count).by(0)
+ .to change { move_service_klass.count }.by(0)
end
end
end
diff --git a/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb b/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb
index 31919a4263d..e72e8e79411 100644
--- a/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb
+++ b/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb
@@ -7,5 +7,5 @@ RSpec.shared_examples 'issue_edit snowplow tracking' do
let(:namespace) { project.namespace }
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
- it_behaves_like 'Snowplow event tracking'
+ it_behaves_like 'Snowplow event tracking with RedisHLL context'
end
diff --git a/spec/support/shared_examples/services/timelogs/create_service_shared_examples.rb b/spec/support/shared_examples/services/timelogs/create_service_shared_examples.rb
index 53c42ec0e00..00d4224f021 100644
--- a/spec/support/shared_examples/services/timelogs/create_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/timelogs/create_service_shared_examples.rb
@@ -1,6 +1,12 @@
# frozen_string_literal: true
RSpec.shared_examples 'issuable supports timelog creation service' do
+ let_it_be(:time_spent) { 3600 }
+ let_it_be(:spent_at) { Time.now }
+ let_it_be(:summary) { "Test summary" }
+
+ let(:service) { described_class.new(issuable, time_spent, spent_at, summary, user) }
+
shared_examples 'success_response' do
it 'sucessfully saves the timelog' do
is_expected.to be_success
@@ -9,7 +15,7 @@ RSpec.shared_examples 'issuable supports timelog creation service' do
expect(timelog).to be_persisted
expect(timelog.time_spent).to eq(time_spent)
- expect(timelog.spent_at).to eq('Fri, 08 Jul 2022 00:00:00.000000000 UTC +00:00')
+ expect(timelog.spent_at).to eq(spent_at)
expect(timelog.summary).to eq(summary)
expect(timelog.issuable).to eq(issuable)
end
@@ -34,6 +40,39 @@ RSpec.shared_examples 'issuable supports timelog creation service' do
users_container.add_reporter(user)
end
+ context 'when spent_at is in the future' do
+ let_it_be(:spent_at) { Time.now + 2.hours }
+
+ it 'returns an error' do
+ is_expected.to be_error
+
+ expect(subject.message).to eq("Spent at can't be a future date and time.")
+ expect(subject.http_status).to eq(404)
+ end
+ end
+
+ context 'when time_spent is zero' do
+ let_it_be(:time_spent) { 0 }
+
+ it 'returns an error' do
+ is_expected.to be_error
+
+ expect(subject.message).to eq("Time spent can't be zero.")
+ expect(subject.http_status).to eq(404)
+ end
+ end
+
+ context 'when time_spent is nil' do
+ let_it_be(:time_spent) { nil }
+
+ it 'returns an error' do
+ is_expected.to be_error
+
+ expect(subject.message).to eq("Time spent can't be blank")
+ expect(subject.http_status).to eq(404)
+ end
+ end
+
context 'when the timelog save fails' do
before do
allow_next_instance_of(Timelog) do |timelog|
@@ -54,6 +93,12 @@ RSpec.shared_examples 'issuable supports timelog creation service' do
end
RSpec.shared_examples 'issuable does not support timelog creation service' do
+ let_it_be(:time_spent) { 3600 }
+ let_it_be(:spent_at) { Time.now }
+ let_it_be(:summary) { "Test summary" }
+
+ let(:service) { described_class.new(issuable, time_spent, spent_at, summary, user) }
+
shared_examples 'error_response' do
it 'returns an error' do
is_expected.to be_error
diff --git a/spec/support/shared_examples/services/users/build_service_shared_examples.rb b/spec/support/shared_examples/services/users/build_service_shared_examples.rb
index 6a8695e1786..e448f2f874b 100644
--- a/spec/support/shared_examples/services/users/build_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/users/build_service_shared_examples.rb
@@ -84,9 +84,10 @@ RSpec.shared_examples_for 'current user not admin build items' do
end
end
- context 'when "send_user_confirmation_email" application setting is true' do
+ context 'when "email_confirmation_setting" application setting is set to `hard`' do
before do
- stub_application_setting(send_user_confirmation_email: true, signup_enabled?: true)
+ stub_application_setting_enum('email_confirmation_setting', 'hard')
+ stub_application_setting(signup_enabled?: true)
end
it 'does not confirm the user' do
@@ -94,9 +95,10 @@ RSpec.shared_examples_for 'current user not admin build items' do
end
end
- context 'when "send_user_confirmation_email" application setting is false' do
+ context 'when "email_confirmation_setting" application setting is set to `off`' do
before do
- stub_application_setting(send_user_confirmation_email: false, signup_enabled?: true)
+ stub_application_setting_enum('email_confirmation_setting', 'off')
+ stub_application_setting(signup_enabled?: true)
end
it 'confirms the user' do
diff --git a/spec/support/shared_examples/services/wiki_pages/create_service_shared_examples.rb b/spec/support/shared_examples/services/wiki_pages/create_service_shared_examples.rb
index 980a752cf86..ced49b3e481 100644
--- a/spec/support/shared_examples/services/wiki_pages/create_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/wiki_pages/create_service_shared_examples.rb
@@ -75,7 +75,7 @@ RSpec.shared_examples 'WikiPages::CreateService#execute' do |container_type|
end
it 'does not record the activity' do
- expect { service.execute }.not_to change(Event, :count)
+ expect { service.execute }.not_to change { Event.count }
end
it 'reports the error' do
diff --git a/spec/support/shared_examples/services/wiki_pages/update_service_shared_examples.rb b/spec/support/shared_examples/services/wiki_pages/update_service_shared_examples.rb
index fd10dd4367e..5511843e681 100644
--- a/spec/support/shared_examples/services/wiki_pages/update_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/wiki_pages/update_service_shared_examples.rb
@@ -79,7 +79,7 @@ RSpec.shared_examples 'WikiPages::UpdateService#execute' do |container_type|
end
it 'does not record the activity' do
- expect { service.execute page }.not_to change(Event, :count)
+ expect { service.execute page }.not_to change { Event.count }
end
it 'reports the error' do
diff --git a/spec/support/shared_examples/services/work_items/widgets/milestone_service_shared_examples.rb b/spec/support/shared_examples/services/work_items/widgets/milestone_service_shared_examples.rb
index ac17915c15a..ac064ed4c33 100644
--- a/spec/support/shared_examples/services/work_items/widgets/milestone_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/work_items/widgets/milestone_service_shared_examples.rb
@@ -23,7 +23,7 @@ RSpec.shared_examples "setting work item's milestone" do
it "sets the work item's milestone" do
expect { execute_callback }
- .to change(work_item, :milestone)
+ .to change { work_item.milestone }
.from(nil)
.to(group_milestone)
end
@@ -34,7 +34,7 @@ RSpec.shared_examples "setting work item's milestone" do
it "sets the work item's milestone" do
expect { execute_callback }
- .to change(work_item, :milestone)
+ .to change { work_item.milestone }
.from(nil)
.to(project_milestone)
end
diff --git a/spec/support/shared_examples/work_item_base_types_importer.rb b/spec/support/shared_examples/work_item_base_types_importer.rb
index 593670ac4b8..b1011037584 100644
--- a/spec/support/shared_examples/work_item_base_types_importer.rb
+++ b/spec/support/shared_examples/work_item_base_types_importer.rb
@@ -4,7 +4,7 @@ RSpec.shared_examples 'work item base types importer' do
it "creates all base work item types if they don't exist" do
WorkItems::Type.delete_all
- expect { subject }.to change(WorkItems::Type, :count).from(0).to(WorkItems::Type::BASE_TYPES.count)
+ expect { subject }.to change { WorkItems::Type.count }.from(0).to(WorkItems::Type::BASE_TYPES.count)
types_in_db = WorkItems::Type.all.map { |type| type.slice(:base_type, :icon_name, :name).symbolize_keys }
expected_types = WorkItems::Type::BASE_TYPES.map do |type, attributes|
@@ -25,7 +25,7 @@ RSpec.shared_examples 'work item base types importer' do
subject
first_type.reload
end.to not_change(WorkItems::Type, :count).and(
- change(first_type, :name).from(original_name.upcase).to(original_name)
+ change { first_type.name }.from(original_name.upcase).to(original_name)
)
end
@@ -40,7 +40,7 @@ RSpec.shared_examples 'work item base types importer' do
it 'inserts all types and does nothing if some already existed' do
expect { subject }.to make_queries_matching(/INSERT/, 1).and(
- change(WorkItems::Type, :count).by(1)
+ change { WorkItems::Type.count }.by(1)
)
expect(WorkItems::Type.count).to eq(WorkItems::Type::BASE_TYPES.count)
end
diff --git a/spec/support/shared_examples/work_item_hierarchy_restrictions_importer.rb b/spec/support/shared_examples/work_item_hierarchy_restrictions_importer.rb
new file mode 100644
index 00000000000..b75aa27b2b7
--- /dev/null
+++ b/spec/support/shared_examples/work_item_hierarchy_restrictions_importer.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'work item hierarchy restrictions importer' do
+ shared_examples_for 'adds restrictions' do
+ it "adds all restrictions if they don't exist" do
+ expect { subject }.to change { WorkItems::HierarchyRestriction.count }.from(0).to(4)
+ end
+ end
+
+ context 'when restrictions are missing' do
+ before do
+ WorkItems::HierarchyRestriction.delete_all
+ end
+
+ it_behaves_like 'adds restrictions'
+ end
+
+ context 'when base types are missing' do
+ before do
+ WorkItems::Type.delete_all
+ end
+
+ it_behaves_like 'adds restrictions'
+ end
+
+ context 'when restrictions already exist' do
+ before do
+ Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
+ end
+
+ it 'upserts restrictions' do
+ restriction = WorkItems::HierarchyRestriction.first
+ depth = restriction.maximum_depth
+
+ restriction.update!(maximum_depth: depth + 1)
+
+ expect do
+ subject
+ restriction.reload
+ end.to not_change { WorkItems::HierarchyRestriction.count }.and(
+ change { restriction.maximum_depth }.from(depth + 1).to(depth)
+ )
+ end
+ end
+
+ context 'when some restrictions are missing' do
+ before do
+ Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
+ WorkItems::HierarchyRestriction.limit(1).delete_all
+ end
+
+ it 'inserts missing restrictions and does nothing if some already existed' do
+ expect { subject }.to make_queries_matching(/INSERT/, 1).and(
+ change { WorkItems::HierarchyRestriction.count }.by(1)
+ )
+ expect(WorkItems::HierarchyRestriction.count).to eq(4)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb b/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb
new file mode 100644
index 00000000000..ae29b76ee87
--- /dev/null
+++ b/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb
@@ -0,0 +1,203 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'batched background migrations execution worker' do
+ include ExclusiveLeaseHelpers
+
+ it 'is a limited capacity worker' do
+ expect(described_class.new).to be_a(LimitedCapacity::Worker)
+ end
+
+ describe 'defining the job attributes' do
+ it 'defines the data_consistency as always' do
+ expect(described_class.get_data_consistency).to eq(:always)
+ end
+
+ it 'defines the feature_category as database' do
+ expect(described_class.get_feature_category).to eq(:database)
+ end
+
+ it 'defines the idempotency as false' do
+ expect(described_class).not_to be_idempotent
+ end
+
+ it 'does not retry failed jobs' do
+ expect(described_class.sidekiq_options['retry']).to eq(0)
+ end
+
+ it 'does not deduplicate jobs' do
+ expect(described_class.get_deduplicate_strategy).to eq(:none)
+ end
+
+ it 'defines the queue namespace' do
+ expect(described_class.queue_namespace).to eq('batched_background_migrations')
+ end
+ end
+
+ describe '.perform_with_capacity' do
+ it 'enqueues jobs without modifying provided arguments' do
+ expect_next_instance_of(described_class) do |instance|
+ expect(instance).to receive(:remove_failed_jobs)
+ end
+
+ args = [['main', 123]]
+
+ expect(described_class)
+ .to receive(:bulk_perform_async)
+ .with(args)
+
+ described_class.perform_with_capacity(args)
+ end
+ end
+
+ describe '.max_running_jobs' do
+ it 'returns MAX_RUNNING_MIGRATIONS' do
+ expect(described_class.max_running_jobs).to eq(described_class::MAX_RUNNING_MIGRATIONS)
+ end
+ end
+
+ describe '#max_running_jobs' do
+ it 'returns MAX_RUNNING_MIGRATIONS' do
+ expect(described_class.new.max_running_jobs).to eq(described_class::MAX_RUNNING_MIGRATIONS)
+ end
+ end
+
+ describe '#remaining_work_count' do
+ it 'returns 0' do
+ expect(described_class.new.remaining_work_count).to eq(0)
+ end
+ end
+
+ describe '#perform_work' do
+ let(:database_name) { Gitlab::Database::MAIN_DATABASE_NAME.to_sym }
+ let(:base_model) { Gitlab::Database.database_base_models[database_name] }
+ let(:table_name) { :events }
+ let(:job_interval) { 5.minutes }
+ let(:lease_timeout) { job_interval * described_class::LEASE_TIMEOUT_MULTIPLIER }
+ let(:interval_variance) { described_class::INTERVAL_VARIANCE }
+
+ subject(:worker) { described_class.new }
+
+ context 'when the feature flag is disabled' do
+ let(:migration) do
+ create(:batched_background_migration, :active, interval: job_interval, table_name: table_name)
+ end
+
+ before do
+ stub_feature_flags(execute_batched_migrations_on_schedule: false)
+ end
+
+ it 'does nothing' do
+ expect(Gitlab::Database::BackgroundMigration::BatchedMigration).not_to receive(:find_executable)
+ expect(worker).not_to receive(:run_migration_job)
+
+ worker.perform_work(database_name, migration.id)
+ end
+ end
+
+ context 'when the feature flag is enabled' do
+ before do
+ stub_feature_flags(execute_batched_migrations_on_schedule: true)
+ end
+
+ context 'when the provided database is sharing config' do
+ before do
+ skip_if_multiple_databases_not_setup
+ end
+
+ it 'does nothing' do
+ ci_model = Gitlab::Database.database_base_models['ci']
+ expect(Gitlab::Database).to receive(:db_config_share_with)
+ .with(ci_model.connection_db_config).and_return('main')
+
+ expect(Gitlab::Database::BackgroundMigration::BatchedMigration).not_to receive(:find_executable)
+ expect(worker).not_to receive(:run_migration_job)
+
+ worker.perform_work(:ci, 123)
+ end
+ end
+
+ context 'when migration does not exist' do
+ it 'does nothing' do
+ expect(worker).not_to receive(:run_migration_job)
+
+ worker.perform_work(database_name, non_existing_record_id)
+ end
+ end
+
+ context 'when migration exist' do
+ let(:migration) do
+ create(:batched_background_migration, :active, interval: job_interval, table_name: table_name)
+ end
+
+ before do
+ allow(Gitlab::Database::BackgroundMigration::BatchedMigration).to receive(:find_executable)
+ .with(migration.id, connection: base_model.connection)
+ .and_return(migration)
+ end
+
+ context 'when the migration is no longer active' do
+ it 'does not run the migration' do
+ expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
+
+ expect(migration).to receive(:active?).and_return(false)
+
+ expect(worker).not_to receive(:run_migration_job)
+
+ worker.perform_work(database_name, migration.id)
+ end
+ end
+
+ context 'when the interval has not elapsed' do
+ it 'does not run the migration' do
+ expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
+ expect(migration).to receive(:interval_elapsed?).with(variance: interval_variance).and_return(false)
+ expect(worker).not_to receive(:run_migration_job)
+
+ worker.perform_work(database_name, migration.id)
+ end
+ end
+
+ context 'when the migration is still active and the interval has elapsed' do
+ let(:table_name_lease_key) do
+ "#{described_class.name.underscore}:database_name:#{database_name}:" \
+ "table_name:#{table_name}"
+ end
+
+ context 'when can not obtain lease on the table name' do
+ it 'does nothing' do
+ stub_exclusive_lease_taken(table_name_lease_key, timeout: lease_timeout)
+
+ expect(worker).not_to receive(:run_migration_job)
+
+ worker.perform_work(database_name, migration.id)
+ end
+ end
+
+ it 'always cleans up the exclusive lease' do
+ expect_to_obtain_exclusive_lease(table_name_lease_key, 'uuid-table-name', timeout: lease_timeout)
+ expect_to_cancel_exclusive_lease(table_name_lease_key, 'uuid-table-name')
+
+ expect(worker).to receive(:run_migration_job).and_raise(RuntimeError, 'I broke')
+
+ expect { worker.perform_work(database_name, migration.id) }.to raise_error(RuntimeError, 'I broke')
+ end
+
+ it 'runs the migration' do
+ expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
+
+ expect_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner) do |instance|
+ expect(instance).to receive(:run_migration_job).with(migration)
+ end
+
+ expect_to_obtain_exclusive_lease(table_name_lease_key, 'uuid-table-name', timeout: lease_timeout)
+ expect_to_cancel_exclusive_lease(table_name_lease_key, 'uuid-table-name')
+
+ expect(worker).to receive(:run_migration_job).and_call_original
+
+ worker.perform_work(database_name, migration.id)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb b/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb
index 0be55fd2a3e..09ebc495e61 100644
--- a/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb
+++ b/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb
@@ -125,10 +125,28 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
end
context 'when no active migrations exist' do
- it 'does nothing' do
- expect(worker).not_to receive(:run_active_migration)
+ context 'when parallel execution is disabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: false)
+ end
- worker.perform
+ it 'does nothing' do
+ expect(worker).not_to receive(:run_active_migration)
+
+ worker.perform
+ end
+ end
+
+ context 'when parallel execution is enabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: true)
+ end
+
+ it 'does nothing' do
+ expect(worker).not_to receive(:queue_migrations_for_execution)
+
+ worker.perform
+ end
end
end
@@ -136,7 +154,6 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
let(:job_interval) { 5.minutes }
let(:lease_timeout) { 15.minutes }
let(:lease_key) { described_class.name.demodulize.underscore }
- let(:interval_variance) { described_class::INTERVAL_VARIANCE }
let(:migration_id) { 123 }
let(:migration) do
build(
@@ -145,52 +162,86 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
)
end
+ let(:execution_worker_class) do
+ case tracking_database
+ when :main
+ Database::BatchedBackgroundMigration::MainExecutionWorker
+ when :ci
+ Database::BatchedBackgroundMigration::CiExecutionWorker
+ end
+ end
+
before do
allow(Gitlab::Database::BackgroundMigration::BatchedMigration).to receive(:active_migration)
.with(connection: base_model.connection)
.and_return(migration)
-
- allow(migration).to receive(:interval_elapsed?).with(variance: interval_variance).and_return(true)
- allow(migration).to receive(:reload)
end
- context 'when the calculated timeout is less than the minimum allowed' do
- let(:minimum_timeout) { described_class::MINIMUM_LEASE_TIMEOUT }
- let(:job_interval) { 2.minutes }
+ context 'when parallel execution is disabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: false)
+ end
+
+ let(:execution_worker) { instance_double(execution_worker_class) }
- it 'sets the lease timeout to the minimum value' do
- expect_to_obtain_exclusive_lease(lease_key, timeout: minimum_timeout)
+ context 'when the calculated timeout is less than the minimum allowed' do
+ let(:minimum_timeout) { described_class::MINIMUM_LEASE_TIMEOUT }
+ let(:job_interval) { 2.minutes }
- expect_next_instance_of(Database::BatchedBackgroundMigration::ExecutionWorker) do |worker|
- expect(worker).to receive(:perform).with(tracking_database, migration_id)
+ it 'sets the lease timeout to the minimum value' do
+ expect_to_obtain_exclusive_lease(lease_key, timeout: minimum_timeout)
+
+ expect(execution_worker_class).to receive(:new).and_return(execution_worker)
+ expect(execution_worker).to receive(:perform_work).with(tracking_database, migration_id)
+
+ expect(worker).to receive(:run_active_migration).and_call_original
+
+ worker.perform
end
+ end
- expect(worker).to receive(:run_active_migration).and_call_original
+ it 'always cleans up the exclusive lease' do
+ lease = stub_exclusive_lease_taken(lease_key, timeout: lease_timeout)
+
+ expect(lease).to receive(:try_obtain).and_return(true)
+
+ expect(worker).to receive(:run_active_migration).and_raise(RuntimeError, 'I broke')
+ expect(lease).to receive(:cancel)
+
+ expect { worker.perform }.to raise_error(RuntimeError, 'I broke')
+ end
+
+ it 'delegetes the execution to ExecutionWorker' do
+ base_model = Gitlab::Database.database_base_models[tracking_database]
+
+ expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
+ expect(execution_worker_class).to receive(:new).and_return(execution_worker)
+ expect(execution_worker).to receive(:perform_work).with(tracking_database, migration_id)
worker.perform
end
end
- it 'always cleans up the exclusive lease' do
- lease = stub_exclusive_lease_taken(lease_key, timeout: lease_timeout)
+ context 'when parallel execution is enabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: true)
+ end
- expect(lease).to receive(:try_obtain).and_return(true)
+ it 'delegetes the execution to ExecutionWorker' do
+ expect(Gitlab::Database::BackgroundMigration::BatchedMigration)
+ .to receive(:active_migrations_distinct_on_table).with(
+ connection: base_model.connection,
+ limit: execution_worker_class.max_running_jobs
+ ).and_return([migration])
- expect(worker).to receive(:run_active_migration).and_raise(RuntimeError, 'I broke')
- expect(lease).to receive(:cancel)
+ expected_arguments = [
+ [tracking_database.to_s, migration_id]
+ ]
- expect { worker.perform }.to raise_error(RuntimeError, 'I broke')
- end
+ expect(execution_worker_class).to receive(:perform_with_capacity).with(expected_arguments)
- it 'delegetes the execution to ExecutionWorker' do
- base_model = Gitlab::Database.database_base_models[tracking_database]
-
- expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
- expect_next_instance_of(Database::BatchedBackgroundMigration::ExecutionWorker) do |worker|
- expect(worker).to receive(:perform).with(tracking_database, migration_id)
+ worker.perform
end
-
- worker.perform
end
end
end
@@ -249,6 +300,8 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
end
before do
+ stub_feature_flags(execute_batched_migrations_on_schedule: true)
+
# Create example table populated with test data to migrate.
#
# Test data should have two records that won't be updated:
@@ -269,80 +322,96 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
WHERE some_column = #{migration_records - 5};
SQL
- stub_feature_flags(execute_batched_migrations_on_schedule: true)
-
stub_const('Gitlab::BackgroundMigration::ExampleDataMigration', migration_class)
end
- subject(:full_migration_run) do
- # process all batches, then do an extra execution to mark the job as finished
- (number_of_batches + 1).times do
- described_class.new.perform
+ shared_examples 'batched background migration execution' do
+ subject(:full_migration_run) do
+ # process all batches, then do an extra execution to mark the job as finished
+ (number_of_batches + 1).times do
+ described_class.new.perform
- travel_to((migration.interval + described_class::INTERVAL_VARIANCE).seconds.from_now)
+ travel_to((migration.interval + described_class::INTERVAL_VARIANCE).seconds.from_now)
+ end
end
- end
- it 'marks the migration record as finished' do
- expect { full_migration_run }.to change { migration.reload.status }.from(1).to(3) # active -> finished
- end
+ it 'marks the migration record as finished' do
+ expect { full_migration_run }.to change { migration.reload.status }.from(1).to(3) # active -> finished
+ end
- it 'creates job records for each processed batch', :aggregate_failures do
- expect { full_migration_run }.to change { migration.reload.batched_jobs.count }.from(0)
+ it 'creates job records for each processed batch', :aggregate_failures do
+ expect { full_migration_run }.to change { migration.reload.batched_jobs.count }.from(0)
- final_min_value = migration.batched_jobs.reduce(1) do |next_min_value, batched_job|
- expect(batched_job.min_value).to eq(next_min_value)
+ final_min_value = migration.batched_jobs.order(id: :asc).reduce(1) do |next_min_value, batched_job|
+ expect(batched_job.min_value).to eq(next_min_value)
- batched_job.max_value + 1
+ batched_job.max_value + 1
+ end
+
+ final_max_value = final_min_value - 1
+ expect(final_max_value).to eq(migration_records)
end
- final_max_value = final_min_value - 1
- expect(final_max_value).to eq(migration_records)
- end
+ it 'marks all job records as succeeded', :aggregate_failures do
+ expect { full_migration_run }.to change { migration.reload.batched_jobs.count }.from(0)
- it 'marks all job records as succeeded', :aggregate_failures do
- expect { full_migration_run }.to change { migration.reload.batched_jobs.count }.from(0)
+ expect(migration.batched_jobs).to all(be_succeeded)
+ end
- expect(migration.batched_jobs).to all(be_succeeded)
- end
+ it 'updates matching records in the range', :aggregate_failures do
+ expect { full_migration_run }
+ .to change { example_data.where('status = 1 AND some_column <> 0').count }
+ .from(migration_records).to(1)
- it 'updates matching records in the range', :aggregate_failures do
- expect { full_migration_run }
- .to change { example_data.where('status = 1 AND some_column <> 0').count }
- .from(migration_records).to(1)
+ record_outside_range = example_data.last
- record_outside_range = example_data.last
+ expect(record_outside_range.status).to eq(1)
+ expect(record_outside_range.some_column).not_to eq(0)
+ end
- expect(record_outside_range.status).to eq(1)
- expect(record_outside_range.some_column).not_to eq(0)
- end
+ it 'does not update non-matching records in the range' do
+ expect { full_migration_run }.not_to change { example_data.where('status <> 1 AND some_column <> 0').count }
+ end
- it 'does not update non-matching records in the range' do
- expect { full_migration_run }.not_to change { example_data.where('status <> 1 AND some_column <> 0').count }
- end
+ context 'health status' do
+ subject(:migration_run) { described_class.new.perform }
+
+ it 'puts migration on hold when there is autovaccum activity on related tables' do
+ swapout_view_for_table(:postgres_autovacuum_activity, connection: connection)
+ create(
+ :postgres_autovacuum_activity,
+ table: migration.table_name,
+ table_identifier: "public.#{migration.table_name}"
+ )
+
+ expect { migration_run }.to change { migration.reload.on_hold? }.from(false).to(true)
+ end
- context 'health status' do
- subject(:migration_run) { described_class.new.perform }
+ it 'puts migration on hold when the pending WAL count is above the limit' do
+ sql = Gitlab::Database::BackgroundMigration::HealthStatus::Indicators::WriteAheadLog::PENDING_WAL_COUNT_SQL
+ limit = Gitlab::Database::BackgroundMigration::HealthStatus::Indicators::WriteAheadLog::LIMIT
- it 'puts migration on hold when there is autovaccum activity on related tables' do
- swapout_view_for_table(:postgres_autovacuum_activity, connection: connection)
- create(
- :postgres_autovacuum_activity,
- table: migration.table_name,
- table_identifier: "public.#{migration.table_name}"
- )
+ expect(connection).to receive(:execute).with(sql).and_return([{ 'pending_wal_count' => limit + 1 }])
- expect { migration_run }.to change { migration.reload.on_hold? }.from(false).to(true)
+ expect { migration_run }.to change { migration.reload.on_hold? }.from(false).to(true)
+ end
end
+ end
- it 'puts migration on hold when the pending WAL count is above the limit' do
- sql = Gitlab::Database::BackgroundMigration::HealthStatus::Indicators::WriteAheadLog::PENDING_WAL_COUNT_SQL
- limit = Gitlab::Database::BackgroundMigration::HealthStatus::Indicators::WriteAheadLog::LIMIT
+ context 'when parallel execution is disabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: false)
+ end
- expect(connection).to receive(:execute).with(sql).and_return([{ 'pending_wal_count' => limit + 1 }])
+ it_behaves_like 'batched background migration execution'
+ end
- expect { migration_run }.to change { migration.reload.on_hold? }.from(false).to(true)
+ context 'when parallel execution is enabled', :sidekiq_inline do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: true)
end
+
+ it_behaves_like 'batched background migration execution'
end
end
end
diff --git a/spec/support/shared_examples/workers/schedule_bulk_repository_shard_moves_shared_examples.rb b/spec/support/shared_examples/workers/schedule_bulk_repository_shard_moves_shared_examples.rb
index 465aca63148..6707f65eb69 100644
--- a/spec/support/shared_examples/workers/schedule_bulk_repository_shard_moves_shared_examples.rb
+++ b/spec/support/shared_examples/workers/schedule_bulk_repository_shard_moves_shared_examples.rb
@@ -15,7 +15,7 @@ RSpec.shared_examples 'schedules bulk repository shard moves' do
let(:job_args) { [source_storage_name, destination_storage_name] }
it 'schedules container repository storage moves' do
- expect { subject }.to change(move_service_klass, :count).by(1)
+ expect { subject }.to change { move_service_klass.count }.by(1)
storage_move = container.repository_storage_moves.last!
diff --git a/spec/support/shared_examples/workers/update_repository_move_shared_examples.rb b/spec/support/shared_examples/workers/update_repository_move_shared_examples.rb
index babd7cfbbeb..c50dc6d5372 100644
--- a/spec/support/shared_examples/workers/update_repository_move_shared_examples.rb
+++ b/spec/support/shared_examples/workers/update_repository_move_shared_examples.rb
@@ -15,7 +15,7 @@ RSpec.shared_examples 'an update storage move worker' do
expect do
subject.perform(container.id, 'test_second_storage')
- end.to change(repository_storage_move_klass, :count).by(1)
+ end.to change { repository_storage_move_klass.count }.by(1)
storage_move = container.repository_storage_moves.last
expect(storage_move).to have_attributes(
@@ -32,7 +32,7 @@ RSpec.shared_examples 'an update storage move worker' do
expect do
subject.perform(nil, nil, repository_storage_move.id)
- end.not_to change(repository_storage_move_klass, :count)
+ end.not_to change { repository_storage_move_klass.count }
end
end
end
diff --git a/spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb b/spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb
index a6f5b3862a2..0e79e32b78a 100644
--- a/spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb
+++ b/spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb
@@ -275,7 +275,7 @@ RSpec.describe ExceedQueryLimitHelpers do
expect(test_matcher.log_message)
.to match(%r{ORDER BY.*#{TestQueries.table_name}.*LIMIT 1})
expect(test_matcher.log_message)
- .not_to match(%r{\/\*.*correlation_id.*\*\/})
+ .not_to match(%r{/\*.*correlation_id.*\*/})
end
end
end
diff --git a/spec/tasks/gitlab/db/lock_writes_rake_spec.rb b/spec/tasks/gitlab/db/lock_writes_rake_spec.rb
index ebea644bbf0..e3155d3c377 100644
--- a/spec/tasks/gitlab/db/lock_writes_rake_spec.rb
+++ b/spec/tasks/gitlab/db/lock_writes_rake_spec.rb
@@ -19,6 +19,30 @@ RSpec.describe 'gitlab:db:lock_writes', :silence_stdout, :reestablished_active_r
let(:main_connection) { ApplicationRecord.connection }
let(:ci_connection) { Ci::ApplicationRecord.connection }
+ let(:detached_partition_table) { '_test_gitlab_main_part_20220101' }
+
+ before do
+ create_detached_partition_sql = <<~SQL
+ CREATE TABLE IF NOT EXISTS gitlab_partitions_dynamic._test_gitlab_main_part_20220101 (
+ id bigserial primary key not null
+ )
+ SQL
+
+ main_connection.execute(create_detached_partition_sql)
+ ci_connection.execute(create_detached_partition_sql)
+
+ Gitlab::Database::SharedModel.using_connection(main_connection) do
+ Postgresql::DetachedPartition.create!(
+ table_name: detached_partition_table,
+ drop_after: Time.current
+ )
+ end
+
+ allow(Gitlab::Database::GitlabSchema).to receive(:table_schema).and_call_original
+ allow(Gitlab::Database::GitlabSchema).to receive(:table_schema)
+ .with(detached_partition_table).and_return(:gitlab_main)
+ end
+
context 'single database' do
before do
skip_if_multiple_databases_are_setup
@@ -46,6 +70,13 @@ RSpec.describe 'gitlab:db:lock_writes', :silence_stdout, :reestablished_active_r
context 'multiple databases' do
before do
skip_if_multiple_databases_not_setup
+
+ Gitlab::Database::SharedModel.using_connection(ci_connection) do
+ Postgresql::DetachedPartition.create!(
+ table_name: detached_partition_table,
+ drop_after: Time.current
+ )
+ end
end
context 'when locking writes' do
@@ -87,6 +118,13 @@ RSpec.describe 'gitlab:db:lock_writes', :silence_stdout, :reestablished_active_r
main_connection.execute("truncate ci_build_needs")
end.to raise_error(ActiveRecord::StatementInvalid, /Table: "ci_build_needs" is write protected/)
end
+
+ it 'prevents writes to detached partitions' do
+ run_rake_task('gitlab:db:lock_writes')
+ expect do
+ ci_connection.execute("INSERT INTO gitlab_partitions_dynamic.#{detached_partition_table} DEFAULT VALUES")
+ end.to raise_error(ActiveRecord::StatementInvalid, /Table: "#{detached_partition_table}" is write protected/)
+ end
end
context 'when running in dry_run mode' do
@@ -138,6 +176,14 @@ RSpec.describe 'gitlab:db:lock_writes', :silence_stdout, :reestablished_active_r
main_connection.execute("delete from ci_builds")
end.not_to raise_error
end
+
+ it 'allows writes again to detached partitions' do
+ run_rake_task('gitlab:db:unlock_writes')
+
+ expect do
+ ci_connection.execute("INSERT INTO gitlab_partitions_dynamic._test_gitlab_main_part_20220101 DEFAULT VALUES")
+ end.not_to raise_error
+ end
end
end
diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb
index 08bec9fda78..22abfc33d1b 100644
--- a/spec/tasks/gitlab/db_rake_spec.rb
+++ b/spec/tasks/gitlab/db_rake_spec.rb
@@ -701,6 +701,16 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do
describe '#up' do
subject { run_rake_task("gitlab:db:migration_testing:up:#{db}") }
+ let(:migrations_id_runner) do
+ instance_double('Gitlab::Database::Migrations::BatchedMigrationLastId', store: true)
+ end
+
+ before do
+ allow(::Gitlab::Database::Migrations::Runner).to(
+ receive(:batched_migrations_last_id).and_return(migrations_id_runner)
+ )
+ end
+
it 'delegates to the migration runner' do
expect(::Gitlab::Database::Migrations::Runner).to receive(:up).with(database: db).and_return(runner)
expect(runner).to receive(:run)
diff --git a/spec/tasks/gitlab/feature_categories_rake_spec.rb b/spec/tasks/gitlab/feature_categories_rake_spec.rb
new file mode 100644
index 00000000000..22f36309a7c
--- /dev/null
+++ b/spec/tasks/gitlab/feature_categories_rake_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'rake_helper'
+
+RSpec.describe 'gitlab:feature_categories:index', :silence_stdout, feature_category: :scalability do
+ before do
+ Rake.application.rake_require 'tasks/gitlab/feature_categories'
+ end
+
+ it 'outputs objects by stage group' do
+ # Sample items that _hopefully_ won't change very often.
+ expected = {
+ 'controller_actions' => a_hash_including(
+ 'integrations' => a_collection_including(
+ klass: 'Oauth::JiraDvcs::AuthorizationsController',
+ action: 'new',
+ source_location: [
+ 'app/controllers/oauth/jira_dvcs/authorizations_controller.rb',
+ an_instance_of(Integer)
+ ]
+ )
+ ),
+ 'api_endpoints' => a_hash_including(
+ 'authentication_and_authorization' => a_collection_including(
+ klass: 'API::AccessRequests',
+ action: '/groups/:id/access_requests',
+ source_location: [
+ 'lib/api/access_requests.rb',
+ an_instance_of(Integer)
+ ]
+ )
+ ),
+ 'sidekiq_workers' => a_hash_including(
+ 'source_code_management' => a_collection_including(
+ klass: 'MergeWorker',
+ source_location: [
+ 'app/workers/merge_worker.rb',
+ an_instance_of(Integer)
+ ]
+ )
+ ),
+ 'database_tables' => a_hash_including(
+ 'container_scanning' => a_collection_including('vulnerability_advisories')
+ )
+ }
+
+ expect(YAML).to receive(:dump).with(a_hash_including(expected))
+
+ run_rake_task('gitlab:feature_categories:index')
+ end
+end
diff --git a/spec/tasks/gitlab/lfs/migrate_rake_spec.rb b/spec/tasks/gitlab/lfs/migrate_rake_spec.rb
index 3b571507bac..bc3113c2926 100644
--- a/spec/tasks/gitlab/lfs/migrate_rake_spec.rb
+++ b/spec/tasks/gitlab/lfs/migrate_rake_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe 'gitlab:lfs namespace rake task', :silence_stdout do
let(:remote) { ObjectStorage::Store::REMOTE }
before do
- stub_lfs_object_storage(background_upload: false, direct_upload: false)
+ stub_lfs_object_storage(direct_upload: false)
end
describe 'migrate' do
@@ -43,7 +43,7 @@ RSpec.describe 'gitlab:lfs namespace rake task', :silence_stdout do
let(:lfs_object) { create(:lfs_object, :with_file, :object_storage) }
before do
- stub_lfs_object_storage(background_upload: false, direct_upload: true)
+ stub_lfs_object_storage(direct_upload: true)
end
context 'object storage enabled' do
diff --git a/spec/tasks/gitlab/refresh_project_statistics_build_artifacts_size_rake_spec.rb b/spec/tasks/gitlab/refresh_project_statistics_build_artifacts_size_rake_spec.rb
index 3495b535cff..3ee01977cba 100644
--- a/spec/tasks/gitlab/refresh_project_statistics_build_artifacts_size_rake_spec.rb
+++ b/spec/tasks/gitlab/refresh_project_statistics_build_artifacts_size_rake_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe 'gitlab:refresh_project_statistics_build_artifacts_size rake task
end
it 'inserts refreshes in batches with a sleep' do
- expect(Projects::BuildArtifactsSizeRefresh).to receive(:enqueue_refresh).with([project_1, project_2]).ordered
+ expect(Projects::BuildArtifactsSizeRefresh).to receive(:enqueue_refresh).with(match_array([project_1, project_2])).ordered
expect(Kernel).to receive(:sleep).with(1)
expect(Projects::BuildArtifactsSizeRefresh).to receive(:enqueue_refresh).with([project_3]).ordered
diff --git a/spec/tasks/gitlab/shell_rake_spec.rb b/spec/tasks/gitlab/shell_rake_spec.rb
index 52a9738fb51..195859eac70 100644
--- a/spec/tasks/gitlab/shell_rake_spec.rb
+++ b/spec/tasks/gitlab/shell_rake_spec.rb
@@ -22,4 +22,23 @@ RSpec.describe 'gitlab:shell rake tasks', :silence_stdout do
run_rake_task('gitlab:shell:install')
end
end
+
+ describe 'setup task' do
+ it 'writes authorized keys into the file' do
+ allow(Gitlab::CurrentSettings).to receive(:authorized_keys_enabled?).and_return(true)
+ stub_env('force', 'yes')
+
+ auth_key = create(:key)
+ auth_and_signing_key = create(:key, usage_type: :auth_and_signing)
+ create(:key, usage_type: :signing)
+
+ expect_next_instance_of(Gitlab::AuthorizedKeys) do |instance|
+ expect(instance).to receive(:batch_add_keys).once do |keys|
+ expect(keys).to match_array([auth_key, auth_and_signing_key])
+ end
+ end
+
+ run_rake_task('gitlab:shell:setup')
+ end
+ end
end
diff --git a/spec/tasks/gitlab/update_templates_rake_spec.rb b/spec/tasks/gitlab/update_templates_rake_spec.rb
index 85da490f718..47eeea239ea 100644
--- a/spec/tasks/gitlab/update_templates_rake_spec.rb
+++ b/spec/tasks/gitlab/update_templates_rake_spec.rb
@@ -2,13 +2,14 @@
require 'rake_helper'
-RSpec.describe 'gitlab:update_project_templates rake task', :silence_stdout do
+RSpec.describe 'gitlab:update_project_templates rake task', :silence_stdout, feature_category: :importers do
let!(:tmpdir) { Dir.mktmpdir }
let(:template) { Gitlab::ProjectTemplate.find(:rails) }
before do
Rake.application.rake_require 'tasks/gitlab/update_templates'
- create(:admin)
+ admin = create(:admin)
+ create(:key, user: admin)
allow(Gitlab::ProjectTemplate)
.to receive(:archive_directory)
@@ -28,6 +29,9 @@ RSpec.describe 'gitlab:update_project_templates rake task', :silence_stdout do
end
it 'updates valid project templates' do
+ expect(Gitlab::TaskHelpers).to receive(:run_command!).with(anything).exactly(6).times.and_call_original
+ expect(Gitlab::TaskHelpers).to receive(:run_command!).with(%w[git push -u origin master])
+
expect { run_rake_task('gitlab:update_project_templates', [template.name]) }
.to change { Dir.entries(tmpdir) }
.by(["#{template.name}.tar.gz"])
diff --git a/spec/tasks/gitlab/usage_data_rake_spec.rb b/spec/tasks/gitlab/usage_data_rake_spec.rb
index 7ddba4ceb9b..95ebaf6ea24 100644
--- a/spec/tasks/gitlab/usage_data_rake_spec.rb
+++ b/spec/tasks/gitlab/usage_data_rake_spec.rb
@@ -70,8 +70,15 @@ RSpec.describe 'gitlab:usage data take tasks', :silence_stdout do
end
describe 'generate_ci_template_events' do
- it "generates #{Gitlab::UsageDataCounters::CiTemplateUniqueCounter::KNOWN_EVENTS_FILE_PATH}" do
+ around do |example|
FileUtils.rm_rf(Gitlab::UsageDataCounters::CiTemplateUniqueCounter::KNOWN_EVENTS_FILE_PATH)
+
+ example.run
+
+ `git checkout -- #{Gitlab::UsageDataCounters::CiTemplateUniqueCounter::KNOWN_EVENTS_FILE_PATH}`
+ end
+
+ it "generates #{Gitlab::UsageDataCounters::CiTemplateUniqueCounter::KNOWN_EVENTS_FILE_PATH}" do
run_rake_task('gitlab:usage_data:generate_ci_template_events')
expect(File.exist?(Gitlab::UsageDataCounters::CiTemplateUniqueCounter::KNOWN_EVENTS_FILE_PATH)).to be true
@@ -80,7 +87,7 @@ RSpec.describe 'gitlab:usage data take tasks', :silence_stdout do
private
- def stub_response(url: service_ping_payload_url, body:, status: 201)
+ def stub_response(body:, url: service_ping_payload_url, status: 201)
stub_full_request(url, method: :post)
.to_return(
headers: { 'Content-Type' => 'application/json' },
diff --git a/spec/tooling/danger/feature_flag_spec.rb b/spec/tooling/danger/feature_flag_spec.rb
index 7cae3e0a8b3..0e9eda54510 100644
--- a/spec/tooling/danger/feature_flag_spec.rb
+++ b/spec/tooling/danger/feature_flag_spec.rb
@@ -135,7 +135,7 @@ RSpec.describe Tooling::Danger::FeatureFlag do
end
context 'when MR labels does not match FF group' do
- let(:mr_group_label) { 'group::access' }
+ let(:mr_group_label) { 'group::authentication and authorization' }
specify { expect(result).to eq(false) }
end
diff --git a/spec/tooling/danger/product_intelligence_spec.rb b/spec/tooling/danger/product_intelligence_spec.rb
index ea08e3bc6db..fab8b0c61fa 100644
--- a/spec/tooling/danger/product_intelligence_spec.rb
+++ b/spec/tooling/danger/product_intelligence_spec.rb
@@ -12,12 +12,16 @@ RSpec.describe Tooling::Danger::ProductIntelligence do
subject(:product_intelligence) { fake_danger.new(helper: fake_helper) }
let(:fake_danger) { DangerSpecHelper.fake_danger.include(described_class) }
- let(:changed_files) { ['metrics/counts_7d/test_metric.yml'] }
- let(:changed_lines) { ['+tier: ee'] }
+ let(:previous_label_to_add) { 'label_to_add' }
+ let(:labels_to_add) { [previous_label_to_add] }
+ let(:ci_env) { true }
+ let(:has_product_intelligence_label) { true }
before do
- allow(fake_helper).to receive(:all_changed_files).and_return(changed_files)
allow(fake_helper).to receive(:changed_lines).and_return(changed_lines)
+ allow(fake_helper).to receive(:labels_to_add).and_return(labels_to_add)
+ allow(fake_helper).to receive(:ci?).and_return(ci_env)
+ allow(fake_helper).to receive(:mr_has_labels?).with('product intelligence').and_return(has_product_intelligence_label)
end
describe '#check!' do
@@ -26,17 +30,13 @@ RSpec.describe Tooling::Danger::ProductIntelligence do
let(:markdown_formatted_list) { 'markdown formatted list' }
let(:review_pending_label) { 'product intelligence::review pending' }
let(:approved_label) { 'product intelligence::approved' }
- let(:ci_env) { true }
- let(:previous_label_to_add) { 'label_to_add' }
- let(:labels_to_add) { [previous_label_to_add] }
- let(:has_product_intelligence_label) { true }
+ let(:changed_files) { ['metrics/counts_7d/test_metric.yml'] }
+ let(:changed_lines) { ['+tier: ee'] }
before do
+ allow(fake_helper).to receive(:all_changed_files).and_return(changed_files)
allow(fake_helper).to receive(:changes_by_category).and_return(product_intelligence: changed_files, database: ['other_files.yml'])
- allow(fake_helper).to receive(:ci?).and_return(ci_env)
- allow(fake_helper).to receive(:mr_has_labels?).with('product intelligence').and_return(has_product_intelligence_label)
allow(fake_helper).to receive(:markdown_list).with(changed_files).and_return(markdown_formatted_list)
- allow(fake_helper).to receive(:labels_to_add).and_return(labels_to_add)
end
shared_examples "doesn't add new labels" do
@@ -121,4 +121,58 @@ RSpec.describe Tooling::Danger::ProductIntelligence do
end
end
end
+
+ describe '#check_affected_scopes!' do
+ let(:fixture_dir_glob) { Dir.glob(File.join('spec', 'tooling', 'fixtures', 'metrics', '*.rb')) }
+ let(:changed_lines) { ['+ scope :active, -> { iwhere(email: Array(emails)) }'] }
+
+ before do
+ allow(Dir).to receive(:glob).and_return(fixture_dir_glob)
+ allow(fake_helper).to receive(:markdown_list).with({ 'active' => fixture_dir_glob }).and_return('a')
+ end
+
+ context 'when a model was modified' do
+ let(:modified_files) { ['app/models/super_user.rb'] }
+
+ context 'when a scope is changed' do
+ context 'and a metrics uses the affected scope' do
+ it 'producing warning' do
+ expect(product_intelligence).to receive(:warn).with(%r{#{modified_files}})
+
+ product_intelligence.check_affected_scopes!
+ end
+ end
+
+ context 'when no metrics using the affected scope' do
+ let(:changed_lines) { ['+scope :foo, -> { iwhere(email: Array(emails)) }'] }
+
+ it 'doesnt do anything' do
+ expect(product_intelligence).not_to receive(:warn)
+
+ product_intelligence.check_affected_scopes!
+ end
+ end
+ end
+ end
+
+ context 'when an unrelated model with matching scope was modified' do
+ let(:modified_files) { ['app/models/post_box.rb'] }
+
+ it 'doesnt do anything' do
+ expect(product_intelligence).not_to receive(:warn)
+
+ product_intelligence.check_affected_scopes!
+ end
+ end
+
+ context 'when models arent modified' do
+ let(:modified_files) { ['spec/app/models/user_spec.rb'] }
+
+ it 'doesnt do anything' do
+ expect(product_intelligence).not_to receive(:warn)
+
+ product_intelligence.check_affected_scopes!
+ end
+ end
+ end
end
diff --git a/spec/tooling/danger/project_helper_spec.rb b/spec/tooling/danger/project_helper_spec.rb
index f9ad9ed13c2..669867ffb4f 100644
--- a/spec/tooling/danger/project_helper_spec.rb
+++ b/spec/tooling/danger/project_helper_spec.rb
@@ -156,8 +156,6 @@ RSpec.describe Tooling::Danger::ProjectHelper do
'lib/gitlab/database.rb' | [:database, :backend]
'lib/gitlab/database/foo' | [:database, :backend]
'ee/lib/gitlab/database/foo' | [:database, :backend]
- 'lib/gitlab/github_import.rb' | [:database, :backend]
- 'lib/gitlab/github_import/foo' | [:database, :backend]
'lib/gitlab/sql/foo' | [:database, :backend]
'rubocop/cop/migration/foo' | [:database]
diff --git a/spec/tooling/danger/specs_spec.rb b/spec/tooling/danger/specs_spec.rb
index d6aed86e7dc..dcc1f592062 100644
--- a/spec/tooling/danger/specs_spec.rb
+++ b/spec/tooling/danger/specs_spec.rb
@@ -9,7 +9,7 @@ require 'gitlab/dangerfiles/spec_helper'
require_relative '../../../tooling/danger/specs'
require_relative '../../../tooling/danger/project_helper'
-RSpec.describe Tooling::Danger::Specs do
+RSpec.describe Tooling::Danger::Specs, feature_category: :tooling do
include_context "with dangerfile"
let(:fake_danger) { DangerSpecHelper.fake_danger.include(described_class) }
@@ -217,4 +217,68 @@ RSpec.describe Tooling::Danger::Specs do
specs.add_suggestions_for_project_factory_usage(filename)
end
end
+
+ describe '#add_suggestions_for_feature_category' do
+ let(:template) do
+ <<~SUGGESTION_MARKDOWN
+ ```suggestion
+ %<suggested_line>s
+ ```
+
+ Consider adding `feature_category: <feature_category_name>` for this example if it is not set already.
+ See [testing best practices](https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#feature-category-metadata).
+ SUGGESTION_MARKDOWN
+ end
+
+ let(:file_lines) do
+ [
+ " require 'spec_helper'",
+ " \n",
+ " RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController, feature_category: :planning_analytics do",
+ " end",
+ "RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do",
+ " let_it_be(:user) { create(:user) }",
+ " end",
+ " describe 'GET \"time_summary\"' do",
+ " end",
+ " RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do",
+ " let_it_be(:user) { create(:user) }",
+ " end",
+ " describe 'GET \"time_summary\"' do",
+ " end"
+ ]
+ end
+
+ let(:matching_lines) do
+ [
+ "+ RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController, feature_category: :planning_analytics do",
+ "+RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do",
+ "+ RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do"
+ ]
+ end
+
+ let(:changed_lines) do
+ [
+ "+ RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController, feature_category: :planning_analytics do",
+ "+RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do",
+ "+ let_it_be(:user) { create(:user) }",
+ "- end",
+ "+ describe 'GET \"time_summary\"' do",
+ "+ RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do"
+ ]
+ end
+
+ it 'adds suggestions at the correct lines', :aggregate_failures do
+ [
+ { suggested_line: "RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do", number: 5 },
+ { suggested_line: " RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do", number: 10 }
+
+ ].each do |test_case|
+ comment = format(template, suggested_line: test_case[:suggested_line])
+ expect(specs).to receive(:markdown).with(comment, file: filename, line: test_case[:number])
+ end
+
+ specs.add_suggestions_for_feature_category(filename)
+ end
+ end
end
diff --git a/spec/tooling/danger/stable_branch_spec.rb b/spec/tooling/danger/stable_branch_spec.rb
new file mode 100644
index 00000000000..08fd25b30e0
--- /dev/null
+++ b/spec/tooling/danger/stable_branch_spec.rb
@@ -0,0 +1,169 @@
+# frozen_string_literal: true
+
+require 'gitlab-dangerfiles'
+require 'gitlab/dangerfiles/spec_helper'
+require 'rspec-parameterized'
+require 'httparty'
+
+require_relative '../../../tooling/danger/stable_branch'
+
+RSpec.describe Tooling::Danger::StableBranch, feature_category: :delivery do
+ using RSpec::Parameterized::TableSyntax
+
+ include_context 'with dangerfile'
+ let(:fake_danger) { DangerSpecHelper.fake_danger.include(described_class) }
+
+ let(:stable_branch) { fake_danger.new(helper: fake_helper) }
+
+ describe '#check!' do
+ subject { stable_branch.check! }
+
+ shared_examples 'without a failure' do
+ it 'does not add a failure' do
+ expect(stable_branch).not_to receive(:fail)
+
+ subject
+ end
+ end
+
+ shared_examples 'with a failure' do |failure_message|
+ it 'fails' do
+ expect(stable_branch).to receive(:fail).with(failure_message)
+
+ subject
+ end
+ end
+
+ context 'when not applicable' do
+ where(:stable_branch?, :security_mr?) do
+ true | true
+ false | true
+ false | false
+ end
+
+ with_them do
+ before do
+ allow(fake_helper).to receive(:mr_target_branch).and_return(stable_branch? ? '15-1-stable-ee' : 'main')
+ allow(fake_helper).to receive(:security_mr?).and_return(security_mr?)
+ end
+
+ it_behaves_like "without a failure"
+ end
+ end
+
+ context 'when applicable' do
+ let(:target_branch) { '15-1-stable-ee' }
+ let(:feature_label_present) { false }
+ let(:bug_label_present) { true }
+ let(:response_success) { true }
+ let(:parsed_response) do
+ [
+ { 'version' => '15.1.1' },
+ { 'version' => '15.1.0' },
+ { 'version' => '15.0.2' },
+ { 'version' => '15.0.1' },
+ { 'version' => '15.0.0' },
+ { 'version' => '14.10.3' },
+ { 'version' => '14.10.2' },
+ { 'version' => '14.9.3' }
+ ]
+ end
+
+ let(:version_response) do
+ instance_double(
+ HTTParty::Response,
+ success?: response_success,
+ parsed_response: parsed_response
+ )
+ end
+
+ before do
+ allow(fake_helper).to receive(:mr_target_branch).and_return(target_branch)
+ allow(fake_helper).to receive(:security_mr?).and_return(false)
+ allow(fake_helper).to receive(:mr_has_labels?).with('type::feature').and_return(feature_label_present)
+ allow(fake_helper).to receive(:mr_has_labels?).with('type::bug').and_return(bug_label_present)
+ allow(HTTParty).to receive(:get).with(/page=1/).and_return(version_response)
+ end
+
+ # the stubbed behavior above is the success path
+ it_behaves_like "without a failure"
+
+ context 'with a feature label' do
+ let(:feature_label_present) { true }
+
+ it_behaves_like 'with a failure', described_class::FEATURE_ERROR_MESSAGE
+ end
+
+ context 'without a bug label' do
+ let(:bug_label_present) { false }
+
+ it_behaves_like 'with a failure', described_class::BUG_ERROR_MESSAGE
+ end
+
+ context 'when not an applicable version' do
+ let(:target_branch) { '14-9-stable-ee' }
+
+ it_behaves_like 'with a failure', described_class::VERSION_ERROR_MESSAGE
+ end
+
+ context 'when the version API request fails' do
+ let(:response_success) { false }
+
+ it 'adds a warning' do
+ expect(stable_branch).to receive(:warn).with(described_class::FAILED_VERSION_REQUEST_MESSAGE)
+
+ subject
+ end
+ end
+
+ context 'when more than one page of versions is needed' do
+ # we target a version we know will not be returned in the first request
+ let(:target_branch) { '14-10-stable-ee' }
+
+ let(:first_version_response) do
+ instance_double(
+ HTTParty::Response,
+ success?: response_success,
+ parsed_response: [
+ { 'version' => '15.1.1' },
+ { 'version' => '15.1.0' },
+ { 'version' => '15.0.2' },
+ { 'version' => '15.0.1' }
+ ]
+ )
+ end
+
+ let(:second_version_response) do
+ instance_double(
+ HTTParty::Response,
+ success?: response_success,
+ parsed_response: [
+ { 'version' => '15.0.0' },
+ { 'version' => '14.10.3' },
+ { 'version' => '14.10.2' },
+ { 'version' => '14.9.3' }
+ ]
+ )
+ end
+
+ before do
+ allow(HTTParty).to receive(:get).with(/page=1/).and_return(first_version_response)
+ allow(HTTParty).to receive(:get).with(/page=2/).and_return(second_version_response)
+ end
+
+ it_behaves_like "without a failure"
+ end
+
+ context 'when too many version API requests are made' do
+ let(:parsed_response) { [{ 'version' => '15.0.0' }] }
+
+ it 'adds a warning' do
+ expect(HTTParty).to receive(:get).and_return(version_response).at_least(10).times
+ expect(stable_branch).to receive(:warn).with(described_class::FAILED_VERSION_REQUEST_MESSAGE)
+
+ subject
+ end
+ end
+ end
+ end
+end
diff --git a/spec/tooling/danger/user_types_spec.rb b/spec/tooling/danger/user_types_spec.rb
new file mode 100644
index 00000000000..53556601212
--- /dev/null
+++ b/spec/tooling/danger/user_types_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'gitlab-dangerfiles'
+require 'gitlab/dangerfiles/spec_helper'
+require_relative '../../../tooling/danger/user_types'
+
+RSpec.describe Tooling::Danger::UserTypes, feature_category: :subscription_cost_management do
+ include_context 'with dangerfile'
+
+ let(:fake_danger) { DangerSpecHelper.fake_danger.include(described_class) }
+ let(:user_types) { fake_danger.new(helper: fake_helper) }
+
+ describe 'changed files' do
+ subject(:bot_user_types_change_warning) { user_types.bot_user_types_change_warning }
+
+ before do
+ allow(fake_helper).to receive(:modified_files).and_return(modified_files)
+ allow(fake_helper).to receive(:changed_lines).and_return(changed_lines)
+ end
+
+ context 'when has_user_type.rb file is not impacted' do
+ let(:modified_files) { ['app/models/concerns/importable.rb'] }
+ let(:changed_lines) { ['+ANY_CHANGES'] }
+
+ it "doesn't add any warnings" do
+ expect(user_types).not_to receive(:warn)
+
+ bot_user_types_change_warning
+ end
+ end
+
+ context 'when the has_user_type.rb file is impacted' do
+ let(:modified_files) { ['app/models/concerns/has_user_type.rb'] }
+
+ context 'with BOT_USER_TYPES changes' do
+ let(:changed_lines) { ['+BOT_USER_TYPES'] }
+
+ it 'adds warning' do
+ expect(user_types).to receive(:warn).with(described_class::BOT_USER_TYPES_CHANGED_WARNING)
+
+ bot_user_types_change_warning
+ end
+ end
+
+ context 'without BOT_USER_TYPES changes' do
+ let(:changed_lines) { ['+OTHER_CHANGES'] }
+
+ it "doesn't add any warnings" do
+ expect(user_types).not_to receive(:warn)
+
+ bot_user_types_change_warning
+ end
+ end
+ end
+ end
+end
diff --git a/spec/tooling/docs/deprecation_handling_spec.rb b/spec/tooling/docs/deprecation_handling_spec.rb
index 15dd69275c9..94c93d99b94 100644
--- a/spec/tooling/docs/deprecation_handling_spec.rb
+++ b/spec/tooling/docs/deprecation_handling_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe Docs::DeprecationHandling do
# Create dummy YAML data based on file name
allow(YAML).to receive(:load_file) do |file_name|
{
- 'name' => file_name[/[a-z]*\.yml/],
+ 'title' => file_name[/[a-z]*\.yml/],
'announcement_milestone' => file_name[/\d+-\d+/].tr('-', '.')
}
end
@@ -29,7 +29,7 @@ RSpec.describe Docs::DeprecationHandling do
entries = arguments[:entries]
expect(milestones).to eq(['14.10', '14.2'])
- expect(entries.map { |e| e['name'] }).to eq(['a.yml', 'b.yml', 'c.yml'])
+ expect(entries.map { |e| e['title'] }).to eq(['a.yml', 'b.yml', 'c.yml'])
end
end
diff --git a/spec/tooling/fixtures/metrics/sample_instrumentation_metric.rb b/spec/tooling/fixtures/metrics/sample_instrumentation_metric.rb
new file mode 100644
index 00000000000..d6b86137b1b
--- /dev/null
+++ b/spec/tooling/fixtures/metrics/sample_instrumentation_metric.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Instrumentations
+ class ActiveUserCountMetric < DatabaseMetric
+ operation :count
+
+ relation { SuperUser.active }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/tooling/quality/test_level_spec.rb b/spec/tooling/quality/test_level_spec.rb
index 6084dc194da..3f46b3e79f4 100644
--- a/spec/tooling/quality/test_level_spec.rb
+++ b/spec/tooling/quality/test_level_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe Quality::TestLevel do
context 'when level is unit' do
it 'returns a pattern' do
expect(subject.pattern(:unit))
- .to eq("spec/{bin,channels,config,db,dependencies,elastic,elastic_integration,experiments,factories,finders,frontend,graphql,haml_lint,helpers,initializers,lib,metrics_server,models,policies,presenters,rack_servers,replicators,routing,rubocop,scripts,serializers,services,sidekiq,sidekiq_cluster,spam,support_specs,tasks,uploaders,validators,views,workers,tooling,components}{,/**/}*_spec.rb")
+ .to eq("spec/{bin,channels,config,contracts,db,dependencies,elastic,elastic_integration,experiments,factories,finders,frontend,graphql,haml_lint,helpers,initializers,lib,metrics_server,models,policies,presenters,rack_servers,replicators,routing,rubocop,scripts,serializers,services,sidekiq,sidekiq_cluster,spam,support_specs,tasks,uploaders,validators,views,workers,tooling,components}{,/**/}*_spec.rb")
end
end
@@ -121,7 +121,7 @@ RSpec.describe Quality::TestLevel do
context 'when level is unit' do
it 'returns a regexp' do
expect(subject.regexp(:unit))
- .to eq(%r{spec/(bin|channels|config|db|dependencies|elastic|elastic_integration|experiments|factories|finders|frontend|graphql|haml_lint|helpers|initializers|lib|metrics_server|models|policies|presenters|rack_servers|replicators|routing|rubocop|scripts|serializers|services|sidekiq|sidekiq_cluster|spam|support_specs|tasks|uploaders|validators|views|workers|tooling|components)/})
+ .to eq(%r{spec/(bin|channels|config|contracts|db|dependencies|elastic|elastic_integration|experiments|factories|finders|frontend|graphql|haml_lint|helpers|initializers|lib|metrics_server|models|policies|presenters|rack_servers|replicators|routing|rubocop|scripts|serializers|services|sidekiq|sidekiq_cluster|spam|support_specs|tasks|uploaders|validators|views|workers|tooling|components)/})
end
end
diff --git a/spec/uploaders/ci/secure_file_uploader_spec.rb b/spec/uploaders/ci/secure_file_uploader_spec.rb
index 4bac591704b..ec7bbf637a1 100644
--- a/spec/uploaders/ci/secure_file_uploader_spec.rb
+++ b/spec/uploaders/ci/secure_file_uploader_spec.rb
@@ -46,12 +46,6 @@ RSpec.describe Ci::SecureFileUploader do
end
end
- describe '.background_upload_enabled?' do
- it 'returns false' do
- expect(described_class.background_upload_enabled?).to eq(false)
- end
- end
-
describe '.default_store' do
context 'when object storage is enabled' do
it 'returns REMOTE' do
diff --git a/spec/uploaders/external_diff_uploader_spec.rb b/spec/uploaders/external_diff_uploader_spec.rb
index ee23c1e36b7..a889181b72c 100644
--- a/spec/uploaders/external_diff_uploader_spec.rb
+++ b/spec/uploaders/external_diff_uploader_spec.rb
@@ -24,29 +24,6 @@ RSpec.describe ExternalDiffUploader do
store_dir: %r[merge_request_diffs/mr-\d+]
end
- describe 'migration to object storage' do
- context 'with object storage disabled' do
- it "is skipped" do
- expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
-
- diff
- end
- end
-
- context 'with object storage enabled' do
- before do
- stub_external_diffs_setting(enabled: true)
- stub_external_diffs_object_storage(background_upload: true)
- end
-
- it 'is scheduled to run after creation' do
- expect(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async).with(described_class.name, 'MergeRequestDiff', :external_diff, kind_of(Numeric))
-
- diff
- end
- end
- end
-
describe 'remote file' do
context 'with object storage enabled' do
before do
@@ -57,8 +34,6 @@ RSpec.describe ExternalDiffUploader do
end
it 'can store file remotely' do
- allow(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async)
-
diff
expect(diff.external_diff_store).to eq(described_class::Store::REMOTE)
diff --git a/spec/uploaders/file_mover_spec.rb b/spec/uploaders/file_mover_spec.rb
index 3b8c6f6f881..13a5a7e0549 100644
--- a/spec/uploaders/file_mover_spec.rb
+++ b/spec/uploaders/file_mover_spec.rb
@@ -57,12 +57,6 @@ RSpec.describe FileMover do
.to change { tmp_upload.reload.attributes.values_at('model_id', 'model_type') }
.from([user.id, 'User']).to([snippet.id, 'Snippet'])
end
-
- it 'schedules a background migration' do
- expect_any_instance_of(PersonalFileUploader).to receive(:schedule_background_upload).once
-
- subject
- end
end
context 'when update_markdown fails' do
diff --git a/spec/uploaders/gitlab_uploader_spec.rb b/spec/uploaders/gitlab_uploader_spec.rb
index db70441aaf5..f62ab726631 100644
--- a/spec/uploaders/gitlab_uploader_spec.rb
+++ b/spec/uploaders/gitlab_uploader_spec.rb
@@ -6,7 +6,7 @@ require 'carrierwave/storage/fog'
RSpec.describe GitlabUploader do
let(:uploader_class) { Class.new(described_class) }
- subject { uploader_class.new(double) }
+ subject(:uploader) { uploader_class.new(double) }
describe '#file_storage?' do
context 'when file storage is used' do
@@ -161,6 +161,19 @@ RSpec.describe GitlabUploader do
end
end
+ describe '#multi_read' do
+ let(:file) { fixture_file_upload('spec/fixtures/trace/sample_trace', 'text/plain') }
+ let(:byte_offsets) { [[4, 10], [17, 29]] }
+
+ subject { uploader.multi_read(byte_offsets) }
+
+ before do
+ uploader.store!(file)
+ end
+
+ it { is_expected.to eq(%w[Running gitlab-runner]) }
+ end
+
describe '.version' do
subject { uploader_class.version }
diff --git a/spec/uploaders/lfs_object_uploader_spec.rb b/spec/uploaders/lfs_object_uploader_spec.rb
index d1a3fb243ac..b85892a42b5 100644
--- a/spec/uploaders/lfs_object_uploader_spec.rb
+++ b/spec/uploaders/lfs_object_uploader_spec.rb
@@ -25,28 +25,6 @@ RSpec.describe LfsObjectUploader do
store_dir: %r[\h{2}/\h{2}]
end
- describe 'migration to object storage' do
- context 'with object storage disabled' do
- it "is skipped" do
- expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
-
- lfs_object
- end
- end
-
- context 'with object storage enabled' do
- before do
- stub_lfs_object_storage(background_upload: true)
- end
-
- it 'is scheduled to run after creation' do
- expect(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async).with(described_class.name, 'LfsObject', :file, kind_of(Numeric))
-
- lfs_object
- end
- end
- end
-
describe 'remote file' do
let(:lfs_object) { create(:lfs_object, :object_storage, :with_file) }
@@ -56,8 +34,6 @@ RSpec.describe LfsObjectUploader do
end
it 'can store file remotely' do
- allow(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async)
-
lfs_object
expect(lfs_object.file_store).to eq(described_class::Store::REMOTE)
diff --git a/spec/uploaders/packages/composer/cache_uploader_spec.rb b/spec/uploaders/packages/composer/cache_uploader_spec.rb
index a4ba4cc2a1e..7ceaa24f463 100644
--- a/spec/uploaders/packages/composer/cache_uploader_spec.rb
+++ b/spec/uploaders/packages/composer/cache_uploader_spec.rb
@@ -33,8 +33,6 @@ RSpec.describe Packages::Composer::CacheUploader do
end
it 'can store file remotely' do
- allow(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async)
-
cache_file
expect(cache_file.file_store).to eq(described_class::Store::REMOTE)
diff --git a/spec/uploaders/packages/debian/component_file_uploader_spec.rb b/spec/uploaders/packages/debian/component_file_uploader_spec.rb
index de60ec94acf..bee82fb2715 100644
--- a/spec/uploaders/packages/debian/component_file_uploader_spec.rb
+++ b/spec/uploaders/packages/debian/component_file_uploader_spec.rb
@@ -38,8 +38,6 @@ RSpec.describe Packages::Debian::ComponentFileUploader do
end
it 'can store file remotely' do
- allow(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async)
-
component_file
expect(component_file.file_store).to eq(described_class::Store::REMOTE)
diff --git a/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb b/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb
index dbbf69e3c8d..96655edb186 100644
--- a/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb
+++ b/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb
@@ -38,8 +38,6 @@ RSpec.describe Packages::Debian::DistributionReleaseFileUploader do
end
it 'can store file remotely' do
- allow(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async)
-
distribution
expect(distribution.file_store).to eq(described_class::Store::REMOTE)
diff --git a/spec/uploaders/packages/package_file_uploader_spec.rb b/spec/uploaders/packages/package_file_uploader_spec.rb
index 0c7bf6432cb..7d270ad03c9 100644
--- a/spec/uploaders/packages/package_file_uploader_spec.rb
+++ b/spec/uploaders/packages/package_file_uploader_spec.rb
@@ -33,8 +33,6 @@ RSpec.describe Packages::PackageFileUploader do
end
it 'can store file remotely' do
- allow(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async)
-
package_file
expect(package_file.file_store).to eq(described_class::Store::REMOTE)
diff --git a/spec/uploaders/packages/rpm/repository_file_uploader_spec.rb b/spec/uploaders/packages/rpm/repository_file_uploader_spec.rb
index 720e109533b..b3767ae179a 100644
--- a/spec/uploaders/packages/rpm/repository_file_uploader_spec.rb
+++ b/spec/uploaders/packages/rpm/repository_file_uploader_spec.rb
@@ -33,8 +33,6 @@ RSpec.describe Packages::Rpm::RepositoryFileUploader do
end
it 'can store file remotely' do
- allow(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async)
-
repository_file
expect(repository_file.file_store).to eq(described_class::Store::REMOTE)
diff --git a/spec/uploaders/personal_file_uploader_spec.rb b/spec/uploaders/personal_file_uploader_spec.rb
index d2eae5d7a54..1373ccac23d 100644
--- a/spec/uploaders/personal_file_uploader_spec.rb
+++ b/spec/uploaders/personal_file_uploader_spec.rb
@@ -43,8 +43,8 @@ RSpec.describe PersonalFileUploader do
it 'builds correct paths for both local and remote storage' do
paths = uploader.upload_paths('test.jpg')
- expect(paths.first).to match(%r[\h+\/test.jpg])
- expect(paths.second).to match(%r[^personal_snippet\/\d+\/\h+\/test.jpg])
+ expect(paths.first).to match(%r[\h+/test.jpg])
+ expect(paths.second).to match(%r[^personal_snippet/\d+/\h+/test.jpg])
end
end
@@ -52,7 +52,7 @@ RSpec.describe PersonalFileUploader do
it_behaves_like 'builds correct paths',
store_dir: %r[uploads/-/system/personal_snippet/\d+/\h+],
upload_path: %r[\h+/\S+],
- absolute_path: %r[#{CarrierWave.root}/uploads/-/system/personal_snippet\/\d+\/\h+\/\S+$]
+ absolute_path: %r[#{CarrierWave.root}/uploads/-/system/personal_snippet/\d+/\h+/\S+$]
it_behaves_like '#base_dir'
it_behaves_like '#to_h'
@@ -67,7 +67,7 @@ RSpec.describe PersonalFileUploader do
it_behaves_like 'builds correct paths',
store_dir: %r[\d+/\h+],
- upload_path: %r[^personal_snippet\/\d+\/\h+\/<filename>]
+ upload_path: %r[^personal_snippet/\d+/\h+/<filename>]
it_behaves_like '#base_dir'
it_behaves_like '#to_h'
diff --git a/spec/uploaders/terraform/state_uploader_spec.rb b/spec/uploaders/terraform/state_uploader_spec.rb
index bd8e7fbc016..2c17edf4e67 100644
--- a/spec/uploaders/terraform/state_uploader_spec.rb
+++ b/spec/uploaders/terraform/state_uploader_spec.rb
@@ -72,12 +72,6 @@ RSpec.describe Terraform::StateUploader do
end
end
- describe '.background_upload_enabled?' do
- it 'returns false' do
- expect(described_class.background_upload_enabled?).to eq(false)
- end
- end
-
describe '.proxy_download_enabled?' do
it 'returns true' do
expect(described_class.proxy_download_enabled?).to eq(true)
diff --git a/spec/uploaders/workers/object_storage/background_move_worker_spec.rb b/spec/uploaders/workers/object_storage/background_move_worker_spec.rb
deleted file mode 100644
index a481939ed7a..00000000000
--- a/spec/uploaders/workers/object_storage/background_move_worker_spec.rb
+++ /dev/null
@@ -1,116 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe ObjectStorage::BackgroundMoveWorker do
- let(:local) { ObjectStorage::Store::LOCAL }
- let(:remote) { ObjectStorage::Store::REMOTE }
-
- def perform
- described_class.perform_async(uploader_class.name, subject_class, file_field, subject_id)
- end
-
- context 'for LFS' do
- let!(:lfs_object) { create(:lfs_object, :with_file, file_store: local) }
- let(:uploader_class) { LfsObjectUploader }
- let(:subject_class) { LfsObject }
- let(:file_field) { :file }
- let(:subject_id) { lfs_object.id }
-
- context 'when object storage is enabled' do
- before do
- stub_lfs_object_storage(background_upload: true)
- end
-
- it 'uploads object to storage', :sidekiq_might_not_need_inline do
- expect { perform }.to change { lfs_object.reload.file_store }.from(local).to(remote)
- end
-
- context 'when background upload is disabled' do
- before do
- allow(Gitlab.config.lfs.object_store).to receive(:background_upload) { false }
- end
-
- it 'is skipped' do
- expect { perform }.not_to change { lfs_object.reload.file_store }
- end
- end
- end
-
- context 'when object storage is disabled' do
- before do
- stub_lfs_object_storage(enabled: false)
- end
-
- it "doesn't migrate files" do
- perform
-
- expect(lfs_object.reload.file_store).to eq(local)
- end
- end
- end
-
- context 'for job artifacts' do
- let(:artifact) { create(:ci_job_artifact, :archive) }
- let(:uploader_class) { JobArtifactUploader }
- let(:subject_class) { Ci::JobArtifact }
- let(:file_field) { :file }
- let(:subject_id) { artifact.id }
-
- context 'when local storage is used' do
- let(:store) { local }
-
- context 'and remote storage is defined' do
- before do
- stub_artifacts_object_storage(background_upload: true)
- end
-
- it "migrates file to remote storage", :sidekiq_might_not_need_inline do
- perform
-
- expect(artifact.reload.file_store).to eq(remote)
- end
- end
- end
- end
-
- context 'for uploads' do
- let!(:project) { create(:project, :with_avatar) }
- let(:uploader_class) { AvatarUploader }
- let(:file_field) { :avatar }
-
- context 'when local storage is used' do
- let(:store) { local }
-
- context 'and remote storage is defined' do
- before do
- stub_uploads_object_storage(uploader_class, background_upload: true)
- end
-
- describe 'supports using the model' do
- let(:subject_class) { project.class }
- let(:subject_id) { project.id }
-
- it "migrates file to remote storage", :sidekiq_might_not_need_inline do
- perform
- project.reload
- BatchLoader::Executor.clear_current
-
- expect(project.avatar).not_to be_file_storage
- end
- end
-
- describe 'supports using the Upload' do
- let(:subject_class) { Upload }
- let(:subject_id) { project.avatar.upload.id }
-
- it "migrates file to remote storage", :sidekiq_might_not_need_inline do
- perform
-
- expect(project.reload.avatar).not_to be_file_storage
- end
- end
- end
- end
- end
-end
diff --git a/spec/validators/iso8601_date_validator_spec.rb b/spec/validators/iso8601_date_validator_spec.rb
new file mode 100644
index 00000000000..8b9b8a5bb8f
--- /dev/null
+++ b/spec/validators/iso8601_date_validator_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Iso8601DateValidator do
+ subject do
+ Class.new(ActiveRecord::Base) do
+ self.table_name = 'deploy_tokens'
+
+ attribute :expires_at
+
+ validates :expires_at, iso8601_date: true
+
+ def self.name
+ "DeployToken"
+ end
+ end.new
+ end
+
+ it 'passes a valid date' do
+ subject.expires_at = DateTime.now
+
+ expect(subject.valid?).to be_truthy
+ end
+
+ it 'errors on an invalid date' do
+ subject.expires_at = '2-12-2022'
+
+ expect(subject.valid?).to be_falsy
+ expect(subject.errors.full_messages).to include('Expires at must be in ISO 8601 format')
+ end
+end
diff --git a/spec/views/admin/application_settings/_ci_cd.html.haml_spec.rb b/spec/views/admin/application_settings/_ci_cd.html.haml_spec.rb
index 12593b88009..d5aa7139e2b 100644
--- a/spec/views/admin/application_settings/_ci_cd.html.haml_spec.rb
+++ b/spec/views/admin/application_settings/_ci_cd.html.haml_spec.rb
@@ -14,7 +14,8 @@ RSpec.describe 'admin/application_settings/_ci_cd' do
ci_pipeline_schedules: 40,
ci_needs_size_limit: 50,
ci_registered_group_runners: 60,
- ci_registered_project_runners: 70
+ ci_registered_project_runners: 70,
+ pipeline_hierarchy_size: 300
}
end
@@ -58,6 +59,11 @@ RSpec.describe 'admin/application_settings/_ci_cd' do
expect(rendered).to have_field('Maximum number of runners registered per project', type: 'number')
expect(page.find_field('Maximum number of runners registered per project').value).to eq('70')
+
+ expect(rendered).to have_field("Maximum number of downstream pipelines in a pipeline's hierarchy tree",
+type: 'number')
+ expect(page.find_field("Maximum number of downstream pipelines in a pipeline's hierarchy tree").value)
+ .to eq('300')
end
it 'does not display the plan name when there is only one plan' do
diff --git a/spec/views/admin/application_settings/_repository_check.html.haml_spec.rb b/spec/views/admin/application_settings/_repository_check.html.haml_spec.rb
index fbabc890a8b..dc3459f84ef 100644
--- a/spec/views/admin/application_settings/_repository_check.html.haml_spec.rb
+++ b/spec/views/admin/application_settings/_repository_check.html.haml_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'admin/application_settings/_repository_check.html.haml' do
+RSpec.describe 'admin/application_settings/_repository_check.html.haml', feature_category: :source_code_management do
let_it_be(:user) { create(:admin) }
let_it_be(:application_setting) { build(:application_setting) }
@@ -40,9 +40,28 @@ RSpec.describe 'admin/application_settings/_repository_check.html.haml' do
render
expect(rendered).to have_field('Enable automatic repository housekeeping')
- expect(rendered).to have_field('Incremental repack period')
- expect(rendered).to have_field('Full repack period')
- expect(rendered).to have_field('Git GC period')
+ expect(rendered).to have_field('Optimize repository period')
+
+ # TODO: Remove it along with optimized_housekeeping feature flag
+ expect(rendered).not_to have_field('Incremental repack period')
+ expect(rendered).not_to have_field('Full repack period')
+ expect(rendered).not_to have_field('Git GC period')
+ end
+
+ context 'when optimized_housekeeping is disabled' do
+ before do
+ stub_feature_flags(optimized_housekeeping: false)
+ end
+
+ it 'renders the correct setting subsection content' do
+ render
+
+ expect(rendered).to have_field('Enable automatic repository housekeeping')
+ expect(rendered).to have_field('Incremental repack period')
+ expect(rendered).to have_field('Full repack period')
+ expect(rendered).to have_field('Git GC period')
+ expect(rendered).not_to have_field('Optimize repository period')
+ end
end
end
diff --git a/spec/views/admin/application_settings/general.html.haml_spec.rb b/spec/views/admin/application_settings/general.html.haml_spec.rb
index a8c7bec36e3..f229fd2dcdc 100644
--- a/spec/views/admin/application_settings/general.html.haml_spec.rb
+++ b/spec/views/admin/application_settings/general.html.haml_spec.rb
@@ -75,18 +75,6 @@ RSpec.describe 'admin/application_settings/general.html.haml' do
expect(rendered).to have_css('#js-jira_connect-settings')
end
-
- context 'when the jira_connect_oauth_self_managed_setting feature flag is disabled' do
- before do
- stub_feature_flags(jira_connect_oauth_self_managed_setting: false)
- end
-
- it 'does not show the jira connect settings section' do
- render
-
- expect(rendered).not_to have_css('#js-jira_connect-settings')
- end
- end
end
describe 'sign-up restrictions' do
diff --git a/spec/views/admin/dashboard/index.html.haml_spec.rb b/spec/views/admin/dashboard/index.html.haml_spec.rb
index 6e06af92232..337964f1354 100644
--- a/spec/views/admin/dashboard/index.html.haml_spec.rb
+++ b/spec/views/admin/dashboard/index.html.haml_spec.rb
@@ -7,9 +7,7 @@ RSpec.describe 'admin/dashboard/index.html.haml' do
include StubVersion
before do
- counts = Admin::DashboardController::COUNTED_ITEMS.each_with_object({}) do |item, hash|
- hash[item] = 100
- end
+ counts = Admin::DashboardController::COUNTED_ITEMS.index_with { 100 }
assign(:counts, counts)
assign(:projects, create_list(:project, 1))
diff --git a/spec/views/help/index.html.haml_spec.rb b/spec/views/help/index.html.haml_spec.rb
index 1d26afcc567..c041c41a412 100644
--- a/spec/views/help/index.html.haml_spec.rb
+++ b/spec/views/help/index.html.haml_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe 'help/index' do
context 'when logged in' do
def version_link_regexp(path)
base_url = "#{view.source_host_url}/#{view.source_code_group}"
- %r{#{Regexp.escape(base_url)}/(gitlab|gitlab\-foss)/#{Regexp.escape(path)}}
+ %r{#{Regexp.escape(base_url)}/(gitlab|gitlab-foss)/#{Regexp.escape(path)}}
end
before do
diff --git a/spec/views/import/gitlab_projects/new.html.haml_spec.rb b/spec/views/import/gitlab_projects/new.html.haml_spec.rb
index c09c798f487..68e7019c892 100644
--- a/spec/views/import/gitlab_projects/new.html.haml_spec.rb
+++ b/spec/views/import/gitlab_projects/new.html.haml_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe 'import/gitlab_projects/new.html.haml' do
render
- expect(rendered).to have_select('namespace_id', count: 1)
+ expect(rendered).to have_css('.js-vue-new-project-url-select', count: 1)
end
end
end
diff --git a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
index d0d220fed66..080a53cc1a2 100644
--- a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
+++ b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
@@ -96,10 +96,24 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
describe 'Commits' do
- it 'has a link to the project commits path' do
- render
+ context 'when the use_ref_type_parameter flag is not enabled' do
+ before do
+ stub_feature_flags(use_ref_type_parameter: false)
+ end
- expect(rendered).to have_link('Commits', href: project_commits_path(project, current_ref), id: 'js-onboarding-commits-link')
+ it 'has a link to the project commits path' do
+ render
+
+ expect(rendered).to have_link('Commits', href: project_commits_path(project, current_ref), id: 'js-onboarding-commits-link')
+ end
+ end
+
+ context 'when the use_ref_type_parameter flag is enabled' do
+ it 'has a link to the fully qualified project commits path' do
+ render
+
+ expect(rendered).to have_link('Commits', href: project_commits_path(project, current_ref, ref_type: 'heads'), id: 'js-onboarding-commits-link')
+ end
end
end
@@ -120,10 +134,24 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
describe 'Contributors' do
- it 'has a link to the project contributors path' do
- render
+ context 'and the use_ref_type_parameter flag is disabled' do
+ before do
+ stub_feature_flags(use_ref_type_parameter: false)
+ end
- expect(rendered).to have_link('Contributors', href: project_graph_path(project, current_ref))
+ it 'has a link to the project contributors path' do
+ render
+
+ expect(rendered).to have_link('Contributors', href: project_graph_path(project, current_ref))
+ end
+ end
+
+ context 'and the use_ref_type_parameter flag is enabled' do
+ it 'has a link to the project contributors path' do
+ render
+
+ expect(rendered).to have_link('Contributors', href: project_graph_path(project, current_ref, ref_type: 'heads'))
+ end
end
end
diff --git a/spec/views/profiles/keys/_form.html.haml_spec.rb b/spec/views/profiles/keys/_form.html.haml_spec.rb
index 3c61afb21c5..dd8af14100a 100644
--- a/spec/views/profiles/keys/_form.html.haml_spec.rb
+++ b/spec/views/profiles/keys/_form.html.haml_spec.rb
@@ -32,6 +32,11 @@ RSpec.describe 'profiles/keys/_form.html.haml' do
expect(rendered).to have_text('Key titles are publicly visible.')
end
+ it 'has the usage type field', :aggregate_failures do
+ expect(page).to have_select _('Usage type'),
+ selected: 'Authentication & Signing', options: ['Authentication & Signing', 'Authentication', 'Signing']
+ end
+
it 'has the expires at field', :aggregate_failures do
expect(rendered).to have_field('Expiration date', type: 'text')
expect(page.find_field('Expiration date')['min']).to eq(l(1.day.from_now, format: "%Y-%m-%d"))
diff --git a/spec/views/profiles/keys/_key.html.haml_spec.rb b/spec/views/profiles/keys/_key.html.haml_spec.rb
index 1040541332d..d2e27bd2ee0 100644
--- a/spec/views/profiles/keys/_key.html.haml_spec.rb
+++ b/spec/views/profiles/keys/_key.html.haml_spec.rb
@@ -30,6 +30,26 @@ RSpec.describe 'profiles/keys/_key.html.haml' do
expect(response).to render_template(partial: 'shared/ssh_keys/_key_delete')
end
+ context 'displays the usage type' do
+ where(:usage_type, :usage_type_text) do
+ [
+ [:auth, 'Authentication'],
+ [:auth_and_signing, 'Authentication & Signing'],
+ [:signing, 'Signing']
+ ]
+ end
+
+ with_them do
+ let(:key) { create(:key, user: user, usage_type: usage_type) }
+
+ it 'renders usage type text' do
+ render
+
+ expect(rendered).to have_text(usage_type_text)
+ end
+ end
+ end
+
context 'when the key has not been used' do
let_it_be(:key) do
create(:personal_key,
diff --git a/spec/views/profiles/keys/_key_details.html.haml_spec.rb b/spec/views/profiles/keys/_key_details.html.haml_spec.rb
new file mode 100644
index 00000000000..c223d6702c5
--- /dev/null
+++ b/spec/views/profiles/keys/_key_details.html.haml_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'profiles/keys/_key_details.html.haml' do
+ let_it_be(:user) { create(:user) }
+
+ before do
+ assign(:key, key)
+ allow(view).to receive(:is_admin).and_return(false)
+ end
+
+ describe 'displays the usage type' do
+ where(:usage_type, :usage_type_text) do
+ [
+ [:auth, 'Authentication'],
+ [:auth_and_signing, 'Authentication & Signing'],
+ [:signing, 'Signing']
+ ]
+ end
+
+ with_them do
+ let(:key) { create(:key, user: user, usage_type: usage_type) }
+
+ it 'renders usage type text' do
+ render
+
+ expect(rendered).to have_text(usage_type_text)
+ end
+ end
+ end
+end
diff --git a/spec/views/projects/_files.html.haml_spec.rb b/spec/views/projects/_files.html.haml_spec.rb
new file mode 100644
index 00000000000..b6a8b4735b0
--- /dev/null
+++ b/spec/views/projects/_files.html.haml_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'projects/_files' do
+ include ProjectForksHelper
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:source_project) { create(:project, :repository, :public) }
+
+ context 'when the project is a fork' do
+ let_it_be(:project) { fork_project(source_project, user, { repository: true }) }
+
+ before do
+ assign(:project, project)
+ assign(:ref, project.default_branch)
+ assign(:path, '/')
+ assign(:id, project.commit.id)
+
+ allow(view).to receive(:current_user).and_return(user)
+ end
+
+ context 'when user can read fork source' do
+ before do
+ allow(view).to receive(:can?).with(user, :read_project, source_project).and_return(true)
+ end
+
+ it 'shows the forked-from project' do
+ render
+
+ expect(rendered).to have_content("Forked from #{source_project.full_name}")
+ expect(rendered).to have_content("Up to date with upstream repository")
+ end
+
+ context 'when fork_divergence_counts is disabled' do
+ before do
+ stub_feature_flags(fork_divergence_counts: false)
+ end
+
+ it 'does not show fork info' do
+ render
+
+ expect(rendered).not_to have_content("Forked from #{source_project.full_name}")
+ expect(rendered).not_to have_content("Up to date with upstream repository")
+ end
+ end
+ end
+
+ context 'when user cannot read fork source' do
+ before do
+ allow(view).to receive(:can?).with(user, :read_project, source_project).and_return(false)
+ end
+
+ it 'does not show the forked-from project' do
+ render
+
+ expect(rendered).to have_content("Forked from an inaccessible project")
+ end
+
+ context 'when fork_divergence_counts is disabled' do
+ before do
+ stub_feature_flags(fork_divergence_counts: false)
+ end
+
+ it 'does not show fork info' do
+ render
+
+ expect(rendered).not_to have_content("Forked from an inaccessible project")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/views/projects/_flash_messages.html.haml_spec.rb b/spec/views/projects/_flash_messages.html.haml_spec.rb
index e1858229208..231aa12d920 100644
--- a/spec/views/projects/_flash_messages.html.haml_spec.rb
+++ b/spec/views/projects/_flash_messages.html.haml_spec.rb
@@ -12,10 +12,10 @@ RSpec.describe 'projects/_flash_messages' do
before do
allow(view).to receive(:current_user).and_return(user)
- allow(view).to receive(:can?).with(user, :download_code, project).and_return(true)
+ allow(view).to receive(:can?).with(user, :read_code, project).and_return(true)
end
- context 'when current_user has download_code permission' do
+ context 'when current_user has read_code permission' do
context 'when user has a terraform state' do
let_it_be(:project) { create(:project) }
let_it_be(:terraform_state) { create(:terraform_state, :locked, :with_version, project: project) }
diff --git a/spec/views/projects/_home_panel.html.haml_spec.rb b/spec/views/projects/_home_panel.html.haml_spec.rb
index 78131937d3c..6f6a2d9a04d 100644
--- a/spec/views/projects/_home_panel.html.haml_spec.rb
+++ b/spec/views/projects/_home_panel.html.haml_spec.rb
@@ -190,22 +190,50 @@ RSpec.describe 'projects/_home_panel' do
end
context 'user can read fork source' do
- it 'shows the forked-from project' do
+ before do
allow(view).to receive(:can?).with(user, :read_project, source_project).and_return(true)
+ end
+ it 'does not show the forked-from project' do
render
- expect(rendered).to have_content("Forked from #{source_project.full_name}")
+ expect(rendered).not_to have_content("Forked from #{source_project.full_name}")
+ end
+
+ context 'when fork_divergence_counts is disabled' do
+ before do
+ stub_feature_flags(fork_divergence_counts: false)
+ end
+
+ it 'shows the forked-from project' do
+ render
+
+ expect(rendered).to have_content("Forked from #{source_project.full_name}")
+ end
end
end
context 'user cannot read fork source' do
- it 'does not show the forked-from project' do
+ before do
allow(view).to receive(:can?).with(user, :read_project, source_project).and_return(false)
+ end
+ it 'shows the message that forked project is inaccessible' do
render
- expect(rendered).to have_content("Forked from an inaccessible project")
+ expect(rendered).not_to have_content("Forked from an inaccessible project")
+ end
+
+ context 'when fork_divergence_counts is disabled' do
+ before do
+ stub_feature_flags(fork_divergence_counts: false)
+ end
+
+ it 'shows the message that forked project is inaccessible' do
+ render
+
+ expect(rendered).to have_content("Forked from an inaccessible project")
+ end
end
end
end
diff --git a/spec/views/projects/commit/show.html.haml_spec.rb b/spec/views/projects/commit/show.html.haml_spec.rb
index 4d5c987ce37..1d9e5e782e5 100644
--- a/spec/views/projects/commit/show.html.haml_spec.rb
+++ b/spec/views/projects/commit/show.html.haml_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'projects/commit/show.html.haml' do
+RSpec.describe 'projects/commit/show.html.haml', feature_category: :source_code do
let(:project) { create(:project, :repository) }
let(:commit) { project.commit }
@@ -76,4 +76,44 @@ RSpec.describe 'projects/commit/show.html.haml' do
end
end
end
+
+ context 'when commit is signed' do
+ let(:page) { Nokogiri::HTML.parse(rendered) }
+ let(:badge) { page.at('.gpg-status-box') }
+ let(:badge_attributes) { badge.attributes }
+ let(:title) { badge_attributes['data-title'].value }
+ let(:content) { badge_attributes['data-content'].value }
+
+ before do
+ render
+ end
+
+ context 'with GPG' do
+ let(:commit) { project.commit(GpgHelpers::SIGNED_COMMIT_SHA) }
+
+ it 'renders unverified badge' do
+ expect(title).to include('This commit was signed with an <strong>unverified</strong> signature.')
+ expect(content).to include(commit.signature.gpg_key_primary_keyid)
+ end
+ end
+
+ context 'with SSH' do
+ let(:commit) { project.commit('7b5160f9bb23a3d58a0accdbe89da13b96b1ece9') }
+
+ it 'renders unverified badge' do
+ expect(title).to include('This commit was signed with an <strong>unverified</strong> signature.')
+ expect(content).to match(/SSH key fingerprint:[\s\S]+Unknown/)
+ end
+ end
+
+ context 'with X.509' do
+ let(:commit) { project.commit('189a6c924013fc3fe40d6f1ec1dc20214183bc97') }
+
+ it 'renders unverified badge' do
+ expect(title).to include('This commit was signed with an <strong>unverified</strong> signature.')
+ expect(content).to include(commit.signature.x509_certificate.subject_key_identifier.tr(":", " "))
+ expect(content).to include(commit.signature.x509_certificate.email)
+ end
+ end
+ end
end
diff --git a/spec/views/projects/issues/_related_branches.html.haml_spec.rb b/spec/views/projects/issues/_related_branches.html.haml_spec.rb
index ba6f7068024..deec2db6865 100644
--- a/spec/views/projects/issues/_related_branches.html.haml_spec.rb
+++ b/spec/views/projects/issues/_related_branches.html.haml_spec.rb
@@ -9,10 +9,13 @@ RSpec.describe 'projects/issues/_related_branches' do
let(:status) { pipeline.detailed_status(build(:user)) }
before do
- assign(:related_branches, [
- { name: 'other', link: 'link-to-other', pipeline_status: nil },
- { name: 'feature', link: 'link-to-feature', pipeline_status: status }
- ])
+ assign(:related_branches,
+ [
+ { name: 'other', link: 'link-to-other', pipeline_status: nil },
+ { name: 'feature', link: 'link-to-feature', pipeline_status: status }
+
+ ]
+ )
render
end
diff --git a/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb b/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb
index 3776af9e757..7886a811c9a 100644
--- a/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb
+++ b/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe 'projects/notes/_more_actions_dropdown' do
it 'shows Report abuse to admin button if not editable and not current users comment' do
render 'projects/notes/more_actions_dropdown', current_user: not_author_user, note_editable: false, note: note
- expect(rendered).to have_link('Report abuse to admin')
+ expect(rendered).to have_link('Report abuse to administrator')
end
it 'does not show the More actions button if not editable and current users comment' do
@@ -26,10 +26,10 @@ RSpec.describe 'projects/notes/_more_actions_dropdown' do
expect(rendered).not_to have_selector('.dropdown.more-actions')
end
- it 'shows Report abuse to admin and Delete buttons if editable and not current users comment' do
+ it 'shows Report abuse and Delete buttons if editable and not current users comment' do
render 'projects/notes/more_actions_dropdown', current_user: not_author_user, note_editable: true, note: note
- expect(rendered).to have_link('Report abuse to admin')
+ expect(rendered).to have_link('Report abuse to administrator')
expect(rendered).to have_link('Delete comment')
end
diff --git a/spec/views/projects/pipelines/show.html.haml_spec.rb b/spec/views/projects/pipelines/show.html.haml_spec.rb
index 7e300fb1e6e..b9c7da20d1a 100644
--- a/spec/views/projects/pipelines/show.html.haml_spec.rb
+++ b/spec/views/projects/pipelines/show.html.haml_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'projects/pipelines/show' do
+RSpec.describe 'projects/pipelines/show', feature_category: :pipeline_authoring do
include Devise::Test::ControllerHelpers
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
@@ -13,7 +13,6 @@ RSpec.describe 'projects/pipelines/show' do
before do
assign(:project, project)
assign(:pipeline, presented_pipeline)
- stub_feature_flags(pipeline_tabs_vue: false)
end
context 'when pipeline has errors' do
@@ -24,14 +23,14 @@ RSpec.describe 'projects/pipelines/show' do
it 'shows errors' do
render
- expect(rendered).to have_content('Found errors in your .gitlab-ci.yml')
+ expect(rendered).to have_content('Unable to create pipeline')
expect(rendered).to have_content('some errors')
end
it 'does not render the pipeline tabs' do
render
- expect(rendered).not_to have_css('ul.pipelines-tabs')
+ expect(rendered).not_to have_selector('#js-pipeline-tabs')
end
end
@@ -39,13 +38,13 @@ RSpec.describe 'projects/pipelines/show' do
it 'does not show errors' do
render
- expect(rendered).not_to have_content('Found errors in your .gitlab-ci.yml')
+ expect(rendered).not_to have_content('Unable to create pipeline')
end
it 'renders the pipeline tabs' do
render
- expect(rendered).to have_css('ul.pipelines-tabs')
+ expect(rendered).to have_selector('#js-pipeline-tabs')
end
end
end
diff --git a/spec/views/projects/tree/show.html.haml_spec.rb b/spec/views/projects/tree/show.html.haml_spec.rb
index 62a52bcf83f..5a1ae715f8f 100644
--- a/spec/views/projects/tree/show.html.haml_spec.rb
+++ b/spec/views/projects/tree/show.html.haml_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe 'projects/tree/show' do
it 'displays correctly' do
render
- expect(rendered).to have_css('.js-project-refs-dropdown .dropdown-toggle-text', text: ref)
+ expect(rendered).to have_css('#js-tree-ref-switcher')
end
end
end
diff --git a/spec/views/search/_results.html.haml_spec.rb b/spec/views/search/_results.html.haml_spec.rb
index 2149c394320..e81462ee518 100644
--- a/spec/views/search/_results.html.haml_spec.rb
+++ b/spec/views/search/_results.html.haml_spec.rb
@@ -3,36 +3,60 @@
require 'spec_helper'
RSpec.describe 'search/_results' do
- let(:user) { create(:user) }
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:user) { create(:user) }
+
let(:search_objects) { Issue.page(1).per(2) }
let(:scope) { 'issues' }
let(:term) { 'foo' }
+ let(:search_results) { instance_double('Gitlab::SearchResults', { formatted_count: 10, current_user: user } ) }
+ let(:search_service) { class_double(SearchServicePresenter, scope: scope, search: term, current_user: user) }
before do
controller.params[:action] = 'show'
controller.params[:search] = term
- allow(self).to receive(:current_user).and_return(user)
- allow(@search_results).to receive(:formatted_count).with(scope).and_return(10)
- allow(self).to receive(:search_count_path).with(any_args).and_return("test count link")
- allow(self).to receive(:search_path).with(any_args).and_return("link test")
-
- stub_feature_flags(search_page_vertical_nav: false)
-
create_list(:issue, 3)
- @search_objects = search_objects
- @scope = scope
- @search_term = term
- @search_service = SearchServicePresenter.new(SearchService.new(user, search: term, scope: scope))
+ allow(view).to receive(:current_user) { user }
+ assign(:search_count_path, 'test count link')
+ assign(:search_path, 'link test')
+ assign(:search_results, search_results)
+ assign(:search_objects, search_objects)
+ assign(:search_term, term)
+ assign(:scope, scope)
+ @search_service = SearchServicePresenter.new(SearchService.new(user, search: term, scope: scope))
allow(@search_service).to receive(:search_objects).and_return(search_objects)
end
- it 'displays the page size' do
- render
+ where(search_page_vertical_nav_enabled: [true, false])
+
+ with_them do
+ describe 'page size' do
+ before do
+ stub_feature_flags(search_page_vertical_nav: search_page_vertical_nav_enabled)
+ end
+
+ context 'when search results have a count' do
+ it 'displays the page size' do
+ render
+
+ expect(rendered).to have_content('Showing 1 - 2 of 3 issues for foo')
+ end
+ end
+
+ context 'when search results do not have a count' do
+ let(:search_objects) { Issue.page(1).per(2).without_count }
+
+ it 'does not display the page size' do
+ render
- expect(rendered).to have_content('Showing 1 - 2 of 3 issues for foo')
+ expect(rendered).not_to have_content(/Showing .* of .*/)
+ end
+ end
+ end
end
context 'when searching notes which contain quotes in markdown' do
@@ -51,18 +75,6 @@ RSpec.describe 'search/_results' do
end
end
- context 'when search results do not have a count' do
- before do
- @search_objects = @search_objects.without_count
- end
-
- it 'does not display the page size' do
- render
-
- expect(rendered).not_to have_content(/Showing .* of .*/)
- end
- end
-
context 'rendering all types of search results' do
let_it_be(:project) { create(:project, :repository, :wiki_repo) }
let_it_be(:issue) { create(:issue, project: project, title: 'testing') }
diff --git a/spec/views/search/show.html.haml_spec.rb b/spec/views/search/show.html.haml_spec.rb
index 5f9c6c65a08..26ec2c6ae74 100644
--- a/spec/views/search/show.html.haml_spec.rb
+++ b/spec/views/search/show.html.haml_spec.rb
@@ -2,27 +2,32 @@
require 'spec_helper'
-RSpec.describe 'search/show' do
+RSpec.describe 'search/show', feature_category: :global_search do
let(:search_term) { nil }
let(:user) { build(:user) }
+ let(:search_service_presenter) do
+ instance_double(SearchServicePresenter, without_count?: false, advanced_search_enabled?: false)
+ end
before do
stub_template "search/_category.html.haml" => 'Category Partial'
stub_template "search/_results.html.haml" => 'Results Partial'
+
+ assign(:search_service, search_service_presenter)
end
context 'search_page_vertical_nav feature flag enabled' do
before do
allow(view).to receive(:current_user) { user }
assign(:search_term, search_term)
-
- render
end
context 'when search term is supplied' do
let(:search_term) { 'Search Foo' }
it 'will not render category partial' do
+ render
+
expect(rendered).not_to render_template('search/_category')
expect(rendered).to render_template('search/_results')
end
@@ -34,17 +39,19 @@ RSpec.describe 'search/show' do
stub_feature_flags(search_page_vertical_nav: false)
assign(:search_term, search_term)
-
- render
end
context 'when the search page is opened' do
it 'displays the title' do
+ render
+
expect(rendered).to have_selector('h1.page-title', text: 'Search')
expect(rendered).not_to have_selector('h1.page-title code')
end
it 'does not render partials' do
+ render
+
expect(rendered).not_to render_template('search/_category')
expect(rendered).not_to render_template('search/_results')
end
@@ -54,6 +61,8 @@ RSpec.describe 'search/show' do
let(:search_term) { 'Search Foo' }
it 'renders partials' do
+ render
+
expect(rendered).to render_template('search/_category')
expect(rendered).to render_template('search/_results')
end
@@ -73,8 +82,8 @@ RSpec.describe 'search/show' do
end
context 'search with full count' do
- before do
- assign(:without_count, false)
+ let(:search_service_presenter) do
+ instance_double(SearchServicePresenter, without_count?: false, advanced_search_enabled?: false)
end
it 'renders meta tags for a group' do
@@ -96,8 +105,8 @@ RSpec.describe 'search/show' do
end
context 'search without full count' do
- before do
- assign(:without_count, true)
+ let(:search_service_presenter) do
+ instance_double(SearchServicePresenter, without_count?: true, advanced_search_enabled?: false)
end
it 'renders meta tags for a group' do
diff --git a/spec/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml_spec.rb b/spec/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml_spec.rb
new file mode 100644
index 00000000000..4387a3f5b07
--- /dev/null
+++ b/spec/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'shared/gitlab_version/_security_patch_upgrade_alert' do
+ describe 'when show_security_patch_upgrade_alert? is true' do
+ before do
+ allow(view).to receive(:show_security_patch_upgrade_alert?).and_return(true)
+ render
+ end
+
+ it 'renders the security patch upgrade alert' do
+ expect(rendered).to have_selector('#js-security-patch-upgrade-alert')
+ end
+
+ it 'renders the security patch upgrade alert modal' do
+ expect(rendered).to have_selector('#js-security-patch-upgrade-alert-modal')
+ end
+ end
+end
diff --git a/spec/views/shared/ssh_keys/_key_delete.html.haml_spec.rb b/spec/views/shared/ssh_keys/_key_delete.html.haml_spec.rb
new file mode 100644
index 00000000000..c9bdcabb4b6
--- /dev/null
+++ b/spec/views/shared/ssh_keys/_key_delete.html.haml_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe 'shared/ssh_keys/_key_delete.html.haml' do
+ context 'when the icon parameter is used' do
+ it 'has text' do
+ render partial: 'shared/ssh_keys/key_delete', formats: :html, locals: { icon: true, button_data: '' }
+
+ expect(rendered).not_to have_button('Delete')
+ expect(rendered).to have_selector('[data-testid=remove-icon]')
+ end
+ end
+
+ context 'when the icon parameter is not used' do
+ it 'does not have text' do
+ render partial: 'shared/ssh_keys/key_delete', formats: :html, locals: { button_data: '' }
+
+ expect(rendered).to have_button('Delete')
+ expect(rendered).not_to have_selector('[data-testid=remove-icon]')
+ end
+ end
+end
diff --git a/spec/views/shared/ssh_keys/_key_details.html.haml_spec.rb b/spec/views/shared/ssh_keys/_key_details.html.haml_spec.rb
deleted file mode 100644
index 1bee9f7463f..00000000000
--- a/spec/views/shared/ssh_keys/_key_details.html.haml_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-require 'spec_helper'
-
-RSpec.describe 'shared/ssh_keys/_key_delete.html.haml' do
- context 'when the text parameter is used' do
- it 'has text' do
- render partial: 'shared/ssh_keys/key_delete', formats: :html, locals: { text: 'Button', html_class: '', button_data: '' }
-
- expect(rendered).to have_button('Button')
- end
- end
-
- context 'when the text parameter is not used' do
- it 'does not have text' do
- render partial: 'shared/ssh_keys/key_delete', formats: :html, locals: { html_class: '', button_data: '' }
-
- expect(rendered).to have_button('Delete')
- end
- end
-end
diff --git a/spec/workers/bulk_import_worker_spec.rb b/spec/workers/bulk_import_worker_spec.rb
index 0d0b81d2ec0..61c33f123fa 100644
--- a/spec/workers/bulk_import_worker_spec.rb
+++ b/spec/workers/bulk_import_worker_spec.rb
@@ -72,7 +72,7 @@ RSpec.describe BulkImportWorker do
entity_2 = create(:bulk_import_entity, :created, bulk_import: bulk_import)
expect { subject.perform(bulk_import.id) }
- .to change(BulkImports::Tracker, :count)
+ .to change { BulkImports::Tracker.count }
.by(BulkImports::Groups::Stage.new(entity_1).pipelines.size * 2)
expect(entity_1.trackers).not_to be_empty
diff --git a/spec/workers/bulk_imports/entity_worker_spec.rb b/spec/workers/bulk_imports/entity_worker_spec.rb
index e3f0ee65205..4cd37c93d5f 100644
--- a/spec/workers/bulk_imports/entity_worker_spec.rb
+++ b/spec/workers/bulk_imports/entity_worker_spec.rb
@@ -114,6 +114,8 @@ RSpec.describe BulkImports::EntityWorker do
)
subject
+
+ expect(entity.reload.failed?).to eq(true)
end
context 'in first stage' do
diff --git a/spec/workers/bulk_imports/export_request_worker_spec.rb b/spec/workers/bulk_imports/export_request_worker_spec.rb
index 7eb8150fb2e..7260e0c0f67 100644
--- a/spec/workers/bulk_imports/export_request_worker_spec.rb
+++ b/spec/workers/bulk_imports/export_request_worker_spec.rb
@@ -2,9 +2,10 @@
require 'spec_helper'
-RSpec.describe BulkImports::ExportRequestWorker do
+RSpec.describe BulkImports::ExportRequestWorker, feature_category: :importers do
let_it_be(:bulk_import) { create(:bulk_import) }
let_it_be(:config) { create(:bulk_import_configuration, bulk_import: bulk_import) }
+ let_it_be(:entity) { create(:bulk_import_entity, bulk_import: bulk_import) }
let_it_be(:version_url) { 'https://gitlab.example/api/v4/version' }
let(:response_double) { double(code: 200, success?: true, parsed_response: {}) }
@@ -31,73 +32,6 @@ RSpec.describe BulkImports::ExportRequestWorker do
perform_multiple(job_args)
end
- context 'when network error is raised' do
- let(:exception) { BulkImports::NetworkError.new('Export error') }
-
- before do
- allow_next_instance_of(BulkImports::Clients::HTTP) do |client|
- allow(client).to receive(:post).and_raise(exception).twice
- end
- end
-
- context 'when error is retriable' do
- it 'logs retry request and reenqueues' do
- allow(exception).to receive(:retriable?).twice.and_return(true)
-
- expect_next_instance_of(Gitlab::Import::Logger) do |logger|
- expect(logger).to receive(:error).with(
- a_hash_including(
- 'bulk_import_entity_id' => entity.id,
- 'bulk_import_id' => entity.bulk_import_id,
- 'bulk_import_entity_type' => entity.source_type,
- 'source_full_path' => entity.source_full_path,
- 'exception.backtrace' => anything,
- 'exception.class' => 'BulkImports::NetworkError',
- 'exception.message' => 'Export error',
- 'message' => 'Retrying export request',
- 'importer' => 'gitlab_migration',
- 'source_version' => entity.bulk_import.source_version_info.to_s
- )
- ).twice
- end
-
- expect(described_class).to receive(:perform_in).twice.with(2.seconds, entity.id)
-
- perform_multiple(job_args)
- end
- end
-
- context 'when error is not retriable' do
- it 'logs export failure and marks entity as failed' do
- allow(exception).to receive(:retriable?).twice.and_return(false)
-
- expect_next_instance_of(Gitlab::Import::Logger) do |logger|
- expect(logger).to receive(:error).with(
- a_hash_including(
- 'bulk_import_entity_id' => entity.id,
- 'bulk_import_id' => entity.bulk_import_id,
- 'bulk_import_entity_type' => entity.source_type,
- 'source_full_path' => entity.source_full_path,
- 'exception.backtrace' => anything,
- 'exception.class' => 'BulkImports::NetworkError',
- 'exception.message' => 'Export error',
- 'message' => "Request to export #{entity.source_type} failed",
- 'importer' => 'gitlab_migration',
- 'source_version' => entity.bulk_import.source_version_info.to_s
- )
- ).twice
- end
-
- perform_multiple(job_args)
-
- failure = entity.failures.last
-
- expect(failure.pipeline_class).to eq('ExportRequestWorker')
- expect(failure.exception_message).to eq('Export error')
- end
- end
- end
-
context 'when source id is nil' do
let(:entity_source_id) { 'gid://gitlab/Model/1234567' }
@@ -179,4 +113,24 @@ RSpec.describe BulkImports::ExportRequestWorker do
it_behaves_like 'requests relations export for api resource'
end
end
+
+ describe '#sidekiq_retries_exhausted' do
+ it 'logs export failure and marks entity as failed' do
+ entity = create(:bulk_import_entity, bulk_import: bulk_import)
+ error = 'Exhausted error!'
+
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ expect(logger)
+ .to receive(:error)
+ .with(hash_including('message' => "Request to export #{entity.source_type} failed"))
+ end
+
+ described_class
+ .sidekiq_retries_exhausted_block
+ .call({ 'args' => [entity.id] }, StandardError.new(error))
+
+ expect(entity.reload.failed?).to eq(true)
+ expect(entity.failures.last.exception_message).to eq(error)
+ end
+ end
end
diff --git a/spec/workers/bulk_imports/pipeline_worker_spec.rb b/spec/workers/bulk_imports/pipeline_worker_spec.rb
index 23fbc5688ec..03ec6267ca8 100644
--- a/spec/workers/bulk_imports/pipeline_worker_spec.rb
+++ b/spec/workers/bulk_imports/pipeline_worker_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BulkImports::PipelineWorker do
+RSpec.describe BulkImports::PipelineWorker, feature_category: :importers do
let(:pipeline_class) do
Class.new do
def initialize(_); end
@@ -19,6 +19,15 @@ RSpec.describe BulkImports::PipelineWorker do
let_it_be(:config) { create(:bulk_import_configuration, bulk_import: bulk_import) }
let_it_be(:entity) { create(:bulk_import_entity, bulk_import: bulk_import) }
+ let(:pipeline_tracker) do
+ create(
+ :bulk_import_tracker,
+ entity: entity,
+ pipeline_name: 'FakePipeline',
+ status_event: 'enqueue'
+ )
+ end
+
before do
stub_const('FakePipeline', pipeline_class)
@@ -60,45 +69,12 @@ RSpec.describe BulkImports::PipelineWorker do
end
end
- it_behaves_like 'successfully runs the pipeline' do
- let(:pipeline_tracker) do
- create(
- :bulk_import_tracker,
- entity: entity,
- pipeline_name: 'FakePipeline',
- status_event: 'enqueue'
- )
- end
- end
+ it_behaves_like 'successfully runs the pipeline'
- context 'when the pipeline cannot be found' do
- it 'logs the error' do
- pipeline_tracker = create(
- :bulk_import_tracker,
- :finished,
- entity: entity,
- pipeline_name: 'FakePipeline'
- )
-
- expect_next_instance_of(Gitlab::Import::Logger) do |logger|
- expect(logger)
- .to receive(:error)
- .with(
- hash_including(
- 'pipeline_tracker_id' => pipeline_tracker.id,
- 'bulk_import_entity_id' => entity.id,
- 'bulk_import_id' => entity.bulk_import_id,
- 'bulk_import_entity_type' => entity.source_type,
- 'source_full_path' => entity.source_full_path,
- 'source_version' => entity.bulk_import.source_version_info.to_s,
- 'message' => 'Unstarted pipeline not found'
- )
- )
- end
-
- expect(BulkImports::EntityWorker)
- .to receive(:perform_async)
- .with(entity.id, pipeline_tracker.stage)
+ context 'when exclusive lease cannot be obtained' do
+ it 'does not run the pipeline' do
+ expect(subject).to receive(:try_obtain_lease).and_return(false)
+ expect(subject).not_to receive(:run)
subject.perform(pipeline_tracker.id, pipeline_tracker.stage, entity.id)
end
@@ -145,13 +121,15 @@ RSpec.describe BulkImports::PipelineWorker do
.to receive(:track_exception)
.with(
instance_of(StandardError),
- bulk_import_entity_id: entity.id,
- bulk_import_id: entity.bulk_import.id,
- bulk_import_entity_type: entity.source_type,
- source_full_path: entity.source_full_path,
- pipeline_name: pipeline_tracker.pipeline_name,
- importer: 'gitlab_migration',
- source_version: entity.bulk_import.source_version_info.to_s
+ hash_including(
+ 'bulk_import_entity_id' => entity.id,
+ 'bulk_import_id' => entity.bulk_import.id,
+ 'bulk_import_entity_type' => entity.source_type,
+ 'source_full_path' => entity.source_full_path,
+ 'pipeline_name' => pipeline_tracker.pipeline_name,
+ 'importer' => 'gitlab_migration',
+ 'source_version' => entity.bulk_import.source_version_info.to_s
+ )
)
expect(BulkImports::EntityWorker)
@@ -179,6 +157,111 @@ RSpec.describe BulkImports::PipelineWorker do
expect(pipeline_tracker.jid).to eq('jid')
end
+ shared_examples 'successfully runs the pipeline' do
+ it 'runs the given pipeline successfully' do
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ expect(logger)
+ .to receive(:info)
+ .with(
+ hash_including(
+ 'pipeline_name' => 'FakePipeline',
+ 'bulk_import_id' => entity.bulk_import_id,
+ 'bulk_import_entity_id' => entity.id,
+ 'bulk_import_entity_type' => entity.source_type,
+ 'source_full_path' => entity.source_full_path
+ )
+ )
+ end
+
+ expect(BulkImports::EntityWorker)
+ .to receive(:perform_async)
+ .with(entity.id, pipeline_tracker.stage)
+
+ allow(subject).to receive(:jid).and_return('jid')
+
+ subject.perform(pipeline_tracker.id, pipeline_tracker.stage, entity.id)
+
+ pipeline_tracker.reload
+
+ expect(pipeline_tracker.status_name).to eq(:finished)
+ expect(pipeline_tracker.jid).to eq('jid')
+ end
+ end
+
+ context 'when enqueued pipeline cannot be found' do
+ shared_examples 'logs the error' do
+ it 'logs the error' do
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ status = pipeline_tracker.human_status_name
+
+ expect(logger)
+ .to receive(:error)
+ .with(
+ hash_including(
+ 'bulk_import_entity_id' => entity.id,
+ 'bulk_import_id' => entity.bulk_import_id,
+ 'bulk_import_entity_type' => entity.source_type,
+ 'pipeline_tracker_id' => pipeline_tracker.id,
+ 'pipeline_tracker_state' => status,
+ 'pipeline_name' => pipeline_tracker.pipeline_name,
+ 'source_full_path' => entity.source_full_path,
+ 'source_version' => entity.bulk_import.source_version_info.to_s,
+ 'importer' => 'gitlab_migration',
+ 'message' => "Pipeline in #{status} state instead of expected enqueued state"
+ )
+ )
+ end
+
+ expect(BulkImports::EntityWorker)
+ .to receive(:perform_async)
+ .with(entity.id, pipeline_tracker.stage)
+
+ subject.perform(pipeline_tracker.id, pipeline_tracker.stage, entity.id)
+ end
+ end
+
+ context 'when pipeline is finished' do
+ let(:pipeline_tracker) do
+ create(
+ :bulk_import_tracker,
+ :finished,
+ entity: entity,
+ pipeline_name: 'FakePipeline'
+ )
+ end
+
+ include_examples 'logs the error'
+ end
+
+ context 'when pipeline is skipped' do
+ let(:pipeline_tracker) do
+ create(
+ :bulk_import_tracker,
+ :skipped,
+ entity: entity,
+ pipeline_name: 'FakePipeline'
+ )
+ end
+
+ include_examples 'logs the error'
+ end
+
+ context 'when tracker is started' do
+ it 'marks tracker as failed' do
+ pipeline_tracker = create(
+ :bulk_import_tracker,
+ :started,
+ entity: entity,
+ pipeline_name: 'FakePipeline'
+ )
+
+ subject.perform(pipeline_tracker.id, pipeline_tracker.stage, entity.id)
+
+ expect(pipeline_tracker.reload.failed?).to eq(true)
+ end
+ end
+ end
+
context 'when entity is failed' do
it 'marks tracker as skipped and logs the skip' do
pipeline_tracker = create(
@@ -343,23 +426,64 @@ RSpec.describe BulkImports::PipelineWorker do
end
context 'when export status is empty' do
- it 'reenqueues pipeline worker' do
+ before do
allow_next_instance_of(BulkImports::ExportStatus) do |status|
allow(status).to receive(:started?).and_return(false)
allow(status).to receive(:empty?).and_return(true)
allow(status).to receive(:failed?).and_return(false)
end
- expect(described_class)
- .to receive(:perform_in)
- .with(
- described_class::FILE_EXTRACTION_PIPELINE_PERFORM_DELAY,
- pipeline_tracker.id,
- pipeline_tracker.stage,
- entity.id
- )
+ entity.update!(created_at: entity_created_at)
+ end
- subject.perform(pipeline_tracker.id, pipeline_tracker.stage, entity.id)
+ context 'when timeout is not reached' do
+ let(:entity_created_at) { 1.minute.ago }
+
+ it 'reenqueues pipeline worker' do
+ expect(described_class)
+ .to receive(:perform_in)
+ .with(
+ described_class::FILE_EXTRACTION_PIPELINE_PERFORM_DELAY,
+ pipeline_tracker.id,
+ pipeline_tracker.stage,
+ entity.id
+ )
+
+ subject.perform(pipeline_tracker.id, pipeline_tracker.stage, entity.id)
+
+ expect(pipeline_tracker.reload.status_name).to eq(:enqueued)
+ end
+ end
+
+ context 'when timeout is reached' do
+ let(:entity_created_at) { 10.minutes.ago }
+
+ it 'marks as failed and logs the error' do
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ expect(logger)
+ .to receive(:error)
+ .with(
+ hash_including(
+ 'pipeline_name' => 'NdjsonPipeline',
+ 'bulk_import_entity_id' => entity.id,
+ 'bulk_import_id' => entity.bulk_import_id,
+ 'bulk_import_entity_type' => entity.source_type,
+ 'source_full_path' => entity.source_full_path,
+ 'class' => 'BulkImports::PipelineWorker',
+ 'exception.backtrace' => anything,
+ 'exception.class' => 'BulkImports::Pipeline::ExpiredError',
+ 'exception.message' => 'Empty export status on source instance',
+ 'importer' => 'gitlab_migration',
+ 'message' => 'Pipeline failed',
+ 'source_version' => entity.bulk_import.source_version_info.to_s
+ )
+ )
+ end
+
+ subject.perform(pipeline_tracker.id, pipeline_tracker.stage, entity.id)
+
+ expect(pipeline_tracker.reload.status_name).to eq(:failed)
+ end
end
end
diff --git a/spec/workers/ci/create_downstream_pipeline_worker_spec.rb b/spec/workers/ci/create_downstream_pipeline_worker_spec.rb
index 7a75da850d9..b4add681e67 100644
--- a/spec/workers/ci/create_downstream_pipeline_worker_spec.rb
+++ b/spec/workers/ci/create_downstream_pipeline_worker_spec.rb
@@ -9,19 +9,52 @@ RSpec.describe Ci::CreateDownstreamPipelineWorker do
let(:bridge) { create(:ci_bridge, user: user, pipeline: pipeline) }
- let(:service) { double('pipeline creation service') }
-
describe '#perform' do
context 'when bridge exists' do
- it 'calls cross project pipeline creation service' do
+ let(:service) { double('pipeline creation service') }
+
+ let(:service_result) { ServiceResponse.success(payload: instance_double(Ci::Pipeline, id: 100)) }
+
+ it 'calls cross project pipeline creation service and logs the new pipeline id' do
expect(Ci::CreateDownstreamPipelineService)
.to receive(:new)
.with(project, user)
.and_return(service)
- expect(service).to receive(:execute).with(bridge)
+ expect(service)
+ .to receive(:execute)
+ .with(bridge)
+ .and_return(service_result)
+
+ worker = described_class.new
+ worker.perform(bridge.id)
+
+ expect(worker.logging_extras).to eq({ "extra.ci_create_downstream_pipeline_worker.new_pipeline_id" => 100 })
+ end
+
+ context 'when downstream pipeline creation errors' do
+ let(:service_result) { ServiceResponse.error(message: 'Already has a downstream pipeline') }
+
+ it 'calls cross project pipeline creation service and logs the error' do
+ expect(Ci::CreateDownstreamPipelineService)
+ .to receive(:new)
+ .with(project, user)
+ .and_return(service)
+
+ expect(service)
+ .to receive(:execute)
+ .with(bridge)
+ .and_return(service_result)
+
+ worker = described_class.new
+ worker.perform(bridge.id)
- described_class.new.perform(bridge.id)
+ expect(worker.logging_extras).to eq(
+ {
+ "extra.ci_create_downstream_pipeline_worker.create_error_message" => "Already has a downstream pipeline"
+ }
+ )
+ end
end
end
diff --git a/spec/workers/ci/ref_delete_unlock_artifacts_worker_spec.rb b/spec/workers/ci/ref_delete_unlock_artifacts_worker_spec.rb
index fe4bc2421a4..f14b7f9d1d0 100644
--- a/spec/workers/ci/ref_delete_unlock_artifacts_worker_spec.rb
+++ b/spec/workers/ci/ref_delete_unlock_artifacts_worker_spec.rb
@@ -46,30 +46,11 @@ RSpec.describe Ci::RefDeleteUnlockArtifactsWorker do
context 'when a locked pipeline with persisted artifacts exists' do
let!(:pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: 'master', project: project, locked: :artifacts_locked) }
- context 'with ci_update_unlocked_job_artifacts disabled' do
- before do
- stub_feature_flags(ci_update_unlocked_job_artifacts: false)
- end
+ it 'logs the correct extra metadata' do
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:unlocked_pipelines, 1)
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:unlocked_job_artifacts, 2)
- it 'logs the correct extra metadata' do
- expect(worker).to receive(:log_extra_metadata_on_done).with(:unlocked_pipelines, 1)
- expect(worker).to receive(:log_extra_metadata_on_done).with(:unlocked_job_artifacts, 0)
-
- perform
- end
- end
-
- context 'with ci_update_unlocked_job_artifacts enabled' do
- before do
- stub_feature_flags(ci_update_unlocked_job_artifacts: true)
- end
-
- it 'logs the correct extra metadata' do
- expect(worker).to receive(:log_extra_metadata_on_done).with(:unlocked_pipelines, 1)
- expect(worker).to receive(:log_extra_metadata_on_done).with(:unlocked_job_artifacts, 2)
-
- perform
- end
+ perform
end
end
end
diff --git a/spec/workers/ci/runners/process_runner_version_update_worker_spec.rb b/spec/workers/ci/runners/process_runner_version_update_worker_spec.rb
index ff67266c3e8..30b451f2112 100644
--- a/spec/workers/ci/runners/process_runner_version_update_worker_spec.rb
+++ b/spec/workers/ci/runners/process_runner_version_update_worker_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::Runners::ProcessRunnerVersionUpdateWorker do
+RSpec.describe Ci::Runners::ProcessRunnerVersionUpdateWorker, feature_category: :runner_fleet do
subject(:worker) { described_class.new }
describe '#perform' do
diff --git a/spec/workers/ci/runners/reconcile_existing_runner_versions_cron_worker_spec.rb b/spec/workers/ci/runners/reconcile_existing_runner_versions_cron_worker_spec.rb
index 1292df62ce5..34b1cb33e6b 100644
--- a/spec/workers/ci/runners/reconcile_existing_runner_versions_cron_worker_spec.rb
+++ b/spec/workers/ci/runners/reconcile_existing_runner_versions_cron_worker_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::Runners::ReconcileExistingRunnerVersionsCronWorker do
+RSpec.describe Ci::Runners::ReconcileExistingRunnerVersionsCronWorker, feature_category: :runner_fleet do
subject(:worker) { described_class.new }
describe '#perform' do
diff --git a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
index ece0c5053cb..02190201986 100644
--- a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
+++ b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
@@ -194,6 +194,43 @@ RSpec.describe Gitlab::GithubImport::ObjectImporter, :aggregate_failures do
.to raise_error(NoMethodError, /^undefined method `github_identifiers/)
end
end
+
+ context 'when the record is invalid' do
+ it 'logs an error' do
+ expect(Gitlab::GithubImport::Logger)
+ .to receive(:info)
+ .with(
+ {
+ github_identifiers: github_identifiers,
+ message: 'starting importer',
+ project_id: project.id,
+ importer: 'klass_name'
+ }
+ )
+
+ expect(importer_class)
+ .to receive(:new)
+ .with(instance_of(MockRepresantation), project, client)
+ .and_return(importer_instance)
+
+ exception = ActiveRecord::RecordInvalid.new
+ expect(importer_instance)
+ .to receive(:execute)
+ .and_raise(exception)
+
+ expect(Gitlab::Import::ImportFailureService)
+ .to receive(:track)
+ .with(
+ project_id: project.id,
+ exception: exception,
+ error_source: 'klass_name',
+ fail_import: false
+ )
+ .and_call_original
+
+ worker.import(project, client, { 'number' => 10, 'github_id' => 1 })
+ end
+ end
end
describe '#increment_object_counter?' do
diff --git a/spec/workers/concerns/waitable_worker_spec.rb b/spec/workers/concerns/waitable_worker_spec.rb
index bf156c3b8cb..1449c327052 100644
--- a/spec/workers/concerns/waitable_worker_spec.rb
+++ b/spec/workers/concerns/waitable_worker_spec.rb
@@ -14,12 +14,6 @@ RSpec.describe WaitableWorker do
include ApplicationWorker
prepend WaitableWorker
- # This is a workaround for a Ruby 2.3.7 bug. rspec-mocks cannot restore
- # the visibility of prepended modules. See
- # https://github.com/rspec/rspec-mocks/issues/1231 for more details.
- def self.bulk_perform_inline(args_list)
- end
-
def perform(count = 0)
self.class.counter += count
end
@@ -37,27 +31,6 @@ RSpec.describe WaitableWorker do
worker.bulk_perform_and_wait(arguments)
end
-
- context 'when the feature flag `always_async_project_authorizations_refresh` is turned off' do
- before do
- stub_feature_flags(always_async_project_authorizations_refresh: false)
- end
-
- it 'inlines the job' do
- args_list = [[1]]
- expect(worker).to receive(:bulk_perform_inline).with(args_list).and_call_original
- expect(Gitlab::AppJsonLogger).to(
- receive(:info).with(a_hash_including('message' => 'running inline',
- 'class' => 'Gitlab::Foo::Bar::DummyWorker',
- 'job_status' => 'running',
- 'queue' => 'foo_bar_dummy'))
- .once)
-
- worker.bulk_perform_and_wait(args_list)
-
- expect(worker.counter).to eq(1)
- end
- end
end
context 'between 2 and 3 jobs' do
@@ -81,22 +54,6 @@ RSpec.describe WaitableWorker do
end
end
- describe '.bulk_perform_inline' do
- it 'runs the jobs inline' do
- expect(worker).not_to receive(:bulk_perform_async)
-
- worker.bulk_perform_inline([[1], [2]])
-
- expect(worker.counter).to eq(3)
- end
-
- it 'enqueues jobs if an error is raised' do
- expect(worker).to receive(:bulk_perform_async).with([['foo']])
-
- worker.bulk_perform_inline([[1], ['foo']])
- end
- end
-
describe '#perform' do
shared_examples 'perform' do
it 'notifies the JobWaiter when done if the key is provided' do
diff --git a/spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb b/spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb
index 3cb83a7a5d7..8eda943f36e 100644
--- a/spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb
+++ b/spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb
@@ -398,8 +398,7 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
end
def cleanup_service_response(
- status: :finished,
- repository:,
+ repository:, status: :finished,
cleanup_tags_service_original_size: 100,
cleanup_tags_service_before_truncate_size: 80,
cleanup_tags_service_after_truncate_size: 80,
diff --git a/spec/workers/container_registry/cleanup_worker_spec.rb b/spec/workers/container_registry/cleanup_worker_spec.rb
index ffcb421ce1e..a510b660412 100644
--- a/spec/workers/container_registry/cleanup_worker_spec.rb
+++ b/spec/workers/container_registry/cleanup_worker_spec.rb
@@ -63,19 +63,5 @@ RSpec.describe ContainerRegistry::CleanupWorker, :aggregate_failures do
perform
end
end
-
- context 'with container_registry_delete_repository_with_cron_worker disabled' do
- before do
- stub_feature_flags(container_registry_delete_repository_with_cron_worker: false)
- end
-
- it 'does not run' do
- expect(worker).not_to receive(:reset_stale_deletes)
- expect(worker).not_to receive(:enqueue_delete_container_repository_jobs)
- expect(worker).not_to receive(:log_counts)
-
- subject
- end
- end
end
end
diff --git a/spec/workers/container_registry/migration/guard_worker_spec.rb b/spec/workers/container_registry/migration/guard_worker_spec.rb
index d2bcfef2f5b..4ad2d5c300c 100644
--- a/spec/workers/container_registry/migration/guard_worker_spec.rb
+++ b/spec/workers/container_registry/migration/guard_worker_spec.rb
@@ -41,7 +41,7 @@ RSpec.describe ContainerRegistry::Migration::GuardWorker, :aggregate_failures do
expect(ContainerRegistry::Migration).to receive(timeout).and_call_original
expect { subject }
- .to change(import_aborted_migrations, :count).by(1)
+ .to change { import_aborted_migrations.count }.by(1)
.and change { stale_migration.reload.migration_state }.to('import_aborted')
.and not_change { ongoing_migration.migration_state }
end
@@ -67,7 +67,7 @@ RSpec.describe ContainerRegistry::Migration::GuardWorker, :aggregate_failures do
expect(ContainerRegistry::Migration).to receive(timeout).and_call_original
expect { subject }
- .to change(import_skipped_migrations, :count)
+ .to change { import_skipped_migrations.count }
expect(stale_migration.reload.migration_state).to eq('import_skipped')
expect(stale_migration.reload.migration_skipped_reason).to eq('migration_canceled')
@@ -124,11 +124,11 @@ RSpec.describe ContainerRegistry::Migration::GuardWorker, :aggregate_failures do
expect(worker).to receive(:log_extra_metadata_on_done).with(:aborted_stale_migrations_count, 1)
expect { subject }
- .to change(pre_importing_migrations, :count).by(-1)
+ .to change { pre_importing_migrations.count }.by(-1)
.and not_change(pre_import_done_migrations, :count)
.and not_change(importing_migrations, :count)
.and not_change(import_done_migrations, :count)
- .and change(import_aborted_migrations, :count).by(1)
+ .and change { import_aborted_migrations.count }.by(1)
.and change { stale_migration.reload.migration_state }.from('pre_importing').to('import_aborted')
.and not_change { ongoing_migration.migration_state }
end
@@ -223,10 +223,10 @@ RSpec.describe ContainerRegistry::Migration::GuardWorker, :aggregate_failures do
expect { subject }
.to not_change(pre_importing_migrations, :count)
- .and change(pre_import_done_migrations, :count).by(-1)
+ .and change { pre_import_done_migrations.count }.by(-1)
.and not_change(importing_migrations, :count)
.and not_change(import_done_migrations, :count)
- .and change(import_aborted_migrations, :count).by(1)
+ .and change { import_aborted_migrations.count }.by(1)
.and change { stale_migration.reload.migration_state }.from('pre_import_done').to('import_aborted')
.and not_change { ongoing_migration.migration_state }
end
@@ -252,9 +252,9 @@ RSpec.describe ContainerRegistry::Migration::GuardWorker, :aggregate_failures do
expect { subject }
.to not_change(pre_importing_migrations, :count)
.and not_change(pre_import_done_migrations, :count)
- .and change(importing_migrations, :count).by(-1)
+ .and change { importing_migrations.count }.by(-1)
.and not_change(import_done_migrations, :count)
- .and change(import_aborted_migrations, :count).by(1)
+ .and change { import_aborted_migrations.count }.by(1)
.and change { stale_migration.reload.migration_state }.from('importing').to('import_aborted')
.and not_change { ongoing_migration.migration_state }
end
diff --git a/spec/workers/database/batched_background_migration/ci_execution_worker_spec.rb b/spec/workers/database/batched_background_migration/ci_execution_worker_spec.rb
new file mode 100644
index 00000000000..ec77a15c7ef
--- /dev/null
+++ b/spec/workers/database/batched_background_migration/ci_execution_worker_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Database::BatchedBackgroundMigration::CiExecutionWorker,
+ :clean_gitlab_redis_shared_state,
+ feature_category: :database do
+ it_behaves_like 'batched background migrations execution worker'
+ end
diff --git a/spec/workers/database/batched_background_migration/execution_worker_spec.rb b/spec/workers/database/batched_background_migration/execution_worker_spec.rb
deleted file mode 100644
index 9a850a98f2f..00000000000
--- a/spec/workers/database/batched_background_migration/execution_worker_spec.rb
+++ /dev/null
@@ -1,141 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Database::BatchedBackgroundMigration::ExecutionWorker, :clean_gitlab_redis_shared_state do
- include ExclusiveLeaseHelpers
-
- describe '#perform' do
- let(:database_name) { Gitlab::Database::MAIN_DATABASE_NAME.to_sym }
- let(:base_model) { Gitlab::Database.database_base_models[database_name] }
- let(:table_name) { :events }
- let(:job_interval) { 5.minutes }
- let(:lease_timeout) { job_interval * described_class::LEASE_TIMEOUT_MULTIPLIER }
- let(:interval_variance) { described_class::INTERVAL_VARIANCE }
-
- subject(:worker) { described_class.new }
-
- context 'when the feature flag is disabled' do
- let(:migration) do
- create(:batched_background_migration, :active, interval: job_interval, table_name: table_name)
- end
-
- before do
- stub_feature_flags(execute_batched_migrations_on_schedule: false)
- end
-
- it 'does nothing' do
- expect(Gitlab::Database::BackgroundMigration::BatchedMigration).not_to receive(:find_executable)
- expect(worker).not_to receive(:run_migration_job)
-
- worker.perform(database_name, migration.id)
- end
- end
-
- context 'when the feature flag is enabled' do
- before do
- stub_feature_flags(execute_batched_migrations_on_schedule: true)
- end
-
- context 'when the provided database is sharing config' do
- before do
- skip_if_multiple_databases_not_setup
- end
-
- it 'does nothing' do
- ci_model = Gitlab::Database.database_base_models['ci']
- expect(Gitlab::Database).to receive(:db_config_share_with)
- .with(ci_model.connection_db_config).and_return('main')
-
- expect(Gitlab::Database::BackgroundMigration::BatchedMigration).not_to receive(:find_executable)
- expect(worker).not_to receive(:run_migration_job)
-
- worker.perform(:ci, 123)
- end
- end
-
- context 'when migration does not exist' do
- it 'does nothing' do
- expect(worker).not_to receive(:run_migration_job)
-
- worker.perform(database_name, non_existing_record_id)
- end
- end
-
- context 'when migration exist' do
- let(:migration) do
- create(:batched_background_migration, :active, interval: job_interval, table_name: table_name)
- end
-
- before do
- allow(Gitlab::Database::BackgroundMigration::BatchedMigration).to receive(:find_executable)
- .with(migration.id, connection: base_model.connection)
- .and_return(migration)
- end
-
- context 'when the migration is no longer active' do
- it 'does not run the migration' do
- expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
-
- expect(migration).to receive(:active?).and_return(false)
-
- expect(worker).not_to receive(:run_migration_job)
-
- worker.perform(database_name, migration.id)
- end
- end
-
- context 'when the interval has not elapsed' do
- it 'does not run the migration' do
- expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
- expect(migration).to receive(:interval_elapsed?).with(variance: interval_variance).and_return(false)
- expect(worker).not_to receive(:run_migration_job)
-
- worker.perform(database_name, migration.id)
- end
- end
-
- context 'when the migration is still active and the interval has elapsed' do
- let(:table_name_lease_key) do
- "#{described_class.name.underscore}:database_name:#{database_name}:" \
- "table_name:#{table_name}"
- end
-
- context 'when can not obtain lease on the table name' do
- it 'does nothing' do
- stub_exclusive_lease_taken(table_name_lease_key, timeout: lease_timeout)
-
- expect(worker).not_to receive(:run_migration_job)
-
- worker.perform(database_name, migration.id)
- end
- end
-
- it 'always cleans up the exclusive lease' do
- expect_to_obtain_exclusive_lease(table_name_lease_key, 'uuid-table-name', timeout: lease_timeout)
- expect_to_cancel_exclusive_lease(table_name_lease_key, 'uuid-table-name')
-
- expect(worker).to receive(:run_migration_job).and_raise(RuntimeError, 'I broke')
-
- expect { worker.perform(database_name, migration.id) }.to raise_error(RuntimeError, 'I broke')
- end
-
- it 'runs the migration' do
- expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
-
- expect_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner) do |instance|
- expect(instance).to receive(:run_migration_job).with(migration)
- end
-
- expect_to_obtain_exclusive_lease(table_name_lease_key, 'uuid-table-name', timeout: lease_timeout)
- expect_to_cancel_exclusive_lease(table_name_lease_key, 'uuid-table-name')
-
- expect(worker).to receive(:run_migration_job).and_call_original
-
- worker.perform(database_name, migration.id)
- end
- end
- end
- end
- end
-end
diff --git a/spec/workers/database/batched_background_migration/main_execution_worker_spec.rb b/spec/workers/database/batched_background_migration/main_execution_worker_spec.rb
new file mode 100644
index 00000000000..42a3675f735
--- /dev/null
+++ b/spec/workers/database/batched_background_migration/main_execution_worker_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Database::BatchedBackgroundMigration::MainExecutionWorker,
+ :clean_gitlab_redis_shared_state,
+ feature_category: :database do
+ it_behaves_like 'batched background migrations execution worker'
+ end
diff --git a/spec/workers/delete_container_repository_worker_spec.rb b/spec/workers/delete_container_repository_worker_spec.rb
index a011457444a..6ad131b4c14 100644
--- a/spec/workers/delete_container_repository_worker_spec.rb
+++ b/spec/workers/delete_container_repository_worker_spec.rb
@@ -10,112 +10,10 @@ RSpec.describe DeleteContainerRepositoryWorker do
let(:worker) { described_class.new }
describe '#perform' do
- let(:user_id) { user.id }
- let(:repository_id) { repository.id }
+ subject(:perform) { worker.perform(user.id, repository.id) }
- subject(:perform) { worker.perform(user_id, repository_id) }
-
- it 'executes the destroy service' do
- expect_destroy_service_execution
-
- perform
- end
-
- context 'with an invalid user id' do
- let(:user_id) { -1 }
-
- it { expect { perform }.not_to raise_error }
- end
-
- context 'with an invalid repository id' do
- let(:repository_id) { -1 }
-
- it { expect { perform }.not_to raise_error }
- end
-
- context 'with a repository being migrated', :freeze_time do
- before do
- stub_application_setting(
- container_registry_pre_import_tags_rate: 0.5,
- container_registry_import_timeout: 10.minutes.to_i
- )
- end
-
- shared_examples 'destroying the repository' do
- it 'does destroy the repository' do
- expect_next_found_instance_of(ContainerRepository) do |container_repository|
- expect(container_repository).not_to receive(:tags_count)
- end
- expect(described_class).not_to receive(:perform_in)
- expect_destroy_service_execution
-
- perform
- end
- end
-
- shared_examples 'not re enqueuing job if feature flag is disabled' do
- before do
- stub_feature_flags(container_registry_migration_phase2_delete_container_repository_worker_support: false)
- end
-
- it_behaves_like 'destroying the repository'
- end
-
- context 'with migration state set to pre importing' do
- let_it_be(:repository) { create(:container_repository, :pre_importing) }
-
- let(:tags_count) { 60 }
- let(:delay) { (tags_count * 0.5).seconds + 10.minutes + described_class::FIXED_DELAY }
-
- it 'does not destroy the repository and re enqueue the job' do
- expect_next_found_instance_of(ContainerRepository) do |container_repository|
- expect(container_repository).to receive(:tags_count).and_return(tags_count)
- end
- expect(described_class).to receive(:perform_in).with(delay.from_now)
- expect(worker).to receive(:log_extra_metadata_on_done).with(:delete_postponed, delay)
- expect(::Projects::ContainerRepository::DestroyService).not_to receive(:new)
-
- perform
- end
-
- it_behaves_like 'not re enqueuing job if feature flag is disabled'
- end
-
- %i[pre_import_done importing import_aborted].each do |migration_state|
- context "with migration state set to #{migration_state}" do
- let_it_be(:repository) { create(:container_repository, migration_state) }
-
- let(:delay) { 10.minutes + described_class::FIXED_DELAY }
-
- it 'does not destroy the repository and re enqueue the job' do
- expect_next_found_instance_of(ContainerRepository) do |container_repository|
- expect(container_repository).not_to receive(:tags_count)
- end
- expect(described_class).to receive(:perform_in).with(delay.from_now)
- expect(worker).to receive(:log_extra_metadata_on_done).with(:delete_postponed, delay)
- expect(::Projects::ContainerRepository::DestroyService).not_to receive(:new)
-
- perform
- end
-
- it_behaves_like 'not re enqueuing job if feature flag is disabled'
- end
- end
-
- %i[default import_done import_skipped].each do |migration_state|
- context "with migration state set to #{migration_state}" do
- let_it_be(:repository) { create(:container_repository, migration_state) }
-
- it_behaves_like 'destroying the repository'
- it_behaves_like 'not re enqueuing job if feature flag is disabled'
- end
- end
- end
-
- def expect_destroy_service_execution
- service = instance_double(Projects::ContainerRepository::DestroyService)
- expect(service).to receive(:execute)
- expect(Projects::ContainerRepository::DestroyService).to receive(:new).with(project, user).and_return(service)
+ it 'is a no op' do
+ expect { subject }.to not_change { ContainerRepository.count }
end
end
end
diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb
index e705ca28e54..788f5d8222c 100644
--- a/spec/workers/every_sidekiq_worker_spec.rb
+++ b/spec/workers/every_sidekiq_worker_spec.rb
@@ -139,6 +139,7 @@ RSpec.describe 'Every Sidekiq worker' do
'BuildQueueWorker' => 3,
'BuildSuccessWorker' => 3,
'BulkImportWorker' => false,
+ 'BulkImports::ExportRequestWorker' => 5,
'BulkImports::EntityWorker' => false,
'BulkImports::PipelineWorker' => false,
'Chaos::CpuSpinWorker' => 3,
@@ -193,6 +194,8 @@ RSpec.describe 'Every Sidekiq worker' do
'CreateGithubWebhookWorker' => 3,
'CreateNoteDiffFileWorker' => 3,
'CreatePipelineWorker' => 3,
+ 'Database::BatchedBackgroundMigration::CiExecutionWorker' => 0,
+ 'Database::BatchedBackgroundMigration::MainExecutionWorker' => 0,
'DeleteContainerRepositoryWorker' => 3,
'DeleteDiffFilesWorker' => 3,
'DeleteMergedBranchesWorker' => 3,
@@ -286,6 +289,9 @@ RSpec.describe 'Every Sidekiq worker' do
'Gitlab::GithubImport::Stage::ImportPullRequestsReviewsWorker' => 5,
'Gitlab::GithubImport::Stage::ImportPullRequestsWorker' => 5,
'Gitlab::GithubImport::Stage::ImportRepositoryWorker' => 5,
+ 'Gitlab::GithubGistsImport::ImportGistWorker' => 5,
+ 'Gitlab::GithubGistsImport::StartImportWorker' => 5,
+ 'Gitlab::GithubGistsImport::FinishImportWorker' => 5,
'Gitlab::JiraImport::AdvanceStageWorker' => 5,
'Gitlab::JiraImport::ImportIssueWorker' => 5,
'Gitlab::JiraImport::Stage::FinishImportWorker' => 5,
@@ -340,6 +346,7 @@ RSpec.describe 'Every Sidekiq worker' do
'MergeRequestMergeabilityCheckWorker' => 3,
'MergeRequestResetApprovalsWorker' => 3,
'MergeRequests::AssigneesChangeWorker' => 3,
+ 'MergeRequests::CaptureSuggestedReviewersAcceptedWorker' => 3,
'MergeRequests::CreatePipelineWorker' => 3,
'MergeRequests::DeleteSourceBranchWorker' => 3,
'MergeRequests::FetchSuggestedReviewersWorker' => 3,
@@ -366,7 +373,6 @@ RSpec.describe 'Every Sidekiq worker' do
'ObjectPool::DestroyWorker' => 3,
'ObjectPool::JoinWorker' => 3,
'ObjectPool::ScheduleJoinWorker' => 3,
- 'ObjectStorage::BackgroundMoveWorker' => 5,
'ObjectStorage::MigrateUploadsWorker' => 3,
'Onboarding::CreateLearnGitlabWorker' => 3,
'Packages::CleanupPackageFileWorker' => 0,
@@ -388,6 +394,7 @@ RSpec.describe 'Every Sidekiq worker' do
'PipelineProcessWorker' => 3,
'PostReceive' => 3,
'ProcessCommitWorker' => 3,
+ 'ProductAnalytics::InitializeAnalyticsWorker' => 3,
'ProjectCacheWorker' => 3,
'ProjectDestroyWorker' => 3,
'ProjectExportWorker' => false,
diff --git a/spec/workers/flush_counter_increments_worker_spec.rb b/spec/workers/flush_counter_increments_worker_spec.rb
index 14b49b97ac3..83670acf4b6 100644
--- a/spec/workers/flush_counter_increments_worker_spec.rb
+++ b/spec/workers/flush_counter_increments_worker_spec.rb
@@ -12,29 +12,32 @@ RSpec.describe FlushCounterIncrementsWorker, :counter_attribute do
subject { worker.perform(model.class.name, model.id, attribute) }
- it 'flushes increments to database' do
+ it 'commits increments to database' do
expect(model.class).to receive(:find_by_id).and_return(model)
- expect(model)
- .to receive(:flush_increments_to_database!)
- .with(attribute)
- .and_call_original
+ expect_next_instance_of(Gitlab::Counters::BufferedCounter, model, attribute) do |service|
+ expect(service).to receive(:commit_increment!)
+ end
subject
end
context 'when model class does not exist' do
- subject { worker.perform('non-existend-model') }
+ subject { worker.perform('NonExistentModel', 1, attribute) }
it 'does nothing' do
- expect(worker).not_to receive(:in_lock)
+ expect(Gitlab::Counters::BufferedCounter).not_to receive(:new)
+
+ subject
end
end
context 'when record does not exist' do
- subject { worker.perform(model.class.name, model.id + 100, attribute) }
+ subject { worker.perform(model.class.name, non_existing_record_id, attribute) }
it 'does nothing' do
- expect(worker).not_to receive(:in_lock)
+ expect(Gitlab::Counters::BufferedCounter).not_to receive(:new)
+
+ subject
end
end
end
diff --git a/spec/workers/gitlab/export/prune_project_export_jobs_worker_spec.rb b/spec/workers/gitlab/export/prune_project_export_jobs_worker_spec.rb
new file mode 100644
index 00000000000..eded07c7a2f
--- /dev/null
+++ b/spec/workers/gitlab/export/prune_project_export_jobs_worker_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Export::PruneProjectExportJobsWorker, feature_category: :importers do
+ let_it_be(:old_job_1) { create(:project_export_job, updated_at: 37.months.ago) }
+ let_it_be(:old_job_2) { create(:project_export_job, updated_at: 12.months.ago) }
+ let_it_be(:old_job_3) { create(:project_export_job, updated_at: 8.days.ago) }
+ let_it_be(:fresh_job_1) { create(:project_export_job, updated_at: 1.day.ago) }
+ let_it_be(:fresh_job_2) { create(:project_export_job, updated_at: 2.days.ago) }
+ let_it_be(:fresh_job_3) { create(:project_export_job, updated_at: 6.days.ago) }
+
+ let_it_be(:old_relation_export_1) { create(:project_relation_export, project_export_job_id: old_job_1.id) }
+ let_it_be(:old_relation_export_2) { create(:project_relation_export, project_export_job_id: old_job_2.id) }
+ let_it_be(:old_relation_export_3) { create(:project_relation_export, project_export_job_id: old_job_3.id) }
+ let_it_be(:fresh_relation_export_1) { create(:project_relation_export, project_export_job_id: fresh_job_1.id) }
+
+ let_it_be(:old_upload_1) { create(:relation_export_upload, project_relation_export_id: old_relation_export_1.id) }
+ let_it_be(:old_upload_2) { create(:relation_export_upload, project_relation_export_id: old_relation_export_2.id) }
+ let_it_be(:old_upload_3) { create(:relation_export_upload, project_relation_export_id: old_relation_export_3.id) }
+ let_it_be(:fresh_upload_1) { create(:relation_export_upload, project_relation_export_id: fresh_relation_export_1.id) }
+
+ subject(:worker) { described_class.new }
+
+ describe '#perform' do
+ include_examples 'an idempotent worker' do
+ it 'prunes jobs and associations older than 7 days' do
+ expect { perform_multiple }.to change { ProjectExportJob.count }.by(-3)
+ expect(ProjectExportJob.find_by(id: old_job_1.id)).to be_nil
+ expect(ProjectExportJob.find_by(id: old_job_2.id)).to be_nil
+ expect(ProjectExportJob.find_by(id: old_job_3.id)).to be_nil
+
+ expect(Projects::ImportExport::RelationExport.find_by(id: old_relation_export_1.id)).to be_nil
+ expect(Projects::ImportExport::RelationExport.find_by(id: old_relation_export_2.id)).to be_nil
+ expect(Projects::ImportExport::RelationExport.find_by(id: old_relation_export_3.id)).to be_nil
+
+ expect(Projects::ImportExport::RelationExportUpload.find_by(id: old_upload_1.id)).to be_nil
+ expect(Projects::ImportExport::RelationExportUpload.find_by(id: old_upload_2.id)).to be_nil
+ expect(Projects::ImportExport::RelationExportUpload.find_by(id: old_upload_3.id)).to be_nil
+ end
+
+ it 'leaves fresh jobs and associations' do
+ perform_multiple
+ expect(fresh_job_1.reload).to be_present
+ expect(fresh_job_2.reload).to be_present
+ expect(fresh_job_3.reload).to be_present
+ expect(fresh_relation_export_1.reload).to be_present
+ expect(fresh_upload_1.reload).to be_present
+ end
+ end
+ end
+end
diff --git a/spec/workers/gitlab/github_gists_import/finish_import_worker_spec.rb b/spec/workers/gitlab/github_gists_import/finish_import_worker_spec.rb
new file mode 100644
index 00000000000..c4c19f2f9c5
--- /dev/null
+++ b/spec/workers/gitlab/github_gists_import/finish_import_worker_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubGistsImport::FinishImportWorker, feature_category: :importer do
+ subject(:worker) { described_class.new }
+
+ let_it_be(:user) { create(:user) }
+
+ describe '#perform', :aggregate_failures do
+ context 'when there are no remaining jobs' do
+ it 'marks import status as finished' do
+ waiter = instance_double(Gitlab::JobWaiter, key: :key, jobs_remaining: 0)
+ expect(Gitlab::JobWaiter).to receive(:new).and_return(waiter)
+ expect(waiter).to receive(:wait).with(described_class::BLOCKING_WAIT_TIME)
+ expect_next_instance_of(Gitlab::GithubGistsImport::Status) do |status|
+ expect(status).to receive(:finish!)
+ end
+ expect(Gitlab::GithubImport::Logger)
+ .to receive(:info)
+ .with(user_id: user.id, message: 'GitHub Gists import finished')
+
+ worker.perform(user.id, waiter.key, waiter.jobs_remaining)
+ end
+ end
+
+ context 'when there are remaining jobs' do
+ it 'reschedules the worker' do
+ waiter = instance_double(Gitlab::JobWaiter, key: :key, jobs_remaining: 2)
+ expect(Gitlab::JobWaiter).to receive(:new).and_return(waiter)
+ expect(waiter).to receive(:wait).with(described_class::BLOCKING_WAIT_TIME)
+ expect(described_class).to receive(:perform_in)
+ .with(described_class::INTERVAL, user.id, waiter.key, waiter.jobs_remaining)
+
+ worker.perform(user.id, waiter.key, waiter.jobs_remaining)
+ end
+ end
+ end
+
+ describe '.sidekiq_retries_exhausted' do
+ it 'sets status to failed' do
+ job = { 'args' => [user.id, 'some_key', '1'], 'jid' => '123' }
+
+ expect_next_instance_of(Gitlab::GithubGistsImport::Status) do |status|
+ expect(status).to receive(:fail!)
+ end
+
+ described_class.sidekiq_retries_exhausted_block.call(job)
+ end
+ end
+end
diff --git a/spec/workers/gitlab/github_gists_import/import_gist_worker_spec.rb b/spec/workers/gitlab/github_gists_import/import_gist_worker_spec.rb
new file mode 100644
index 00000000000..dfc5084bb10
--- /dev/null
+++ b/spec/workers/gitlab/github_gists_import/import_gist_worker_spec.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubGistsImport::ImportGistWorker, feature_category: :importer do
+ subject { described_class.new }
+
+ let_it_be(:user) { create(:user) }
+ let(:token) { 'token' }
+ let(:gist_hash) do
+ {
+ id: '055b70',
+ git_pull_url: 'https://gist.github.com/foo/bar.git',
+ files: {
+ 'random.txt': {
+ filename: 'random.txt',
+ type: 'text/plain',
+ language: 'Text',
+ raw_url: 'https://gist.githubusercontent.com/user_name/055b70/raw/66a7be0d/random.txt',
+ size: 166903
+ }
+ },
+ is_public: false,
+ created_at: '2022-09-06T11:38:18Z',
+ updated_at: '2022-09-06T11:38:18Z',
+ description: 'random text'
+ }
+ end
+
+ let(:importer) { instance_double('Gitlab::GithubGistsImport::Importer::GistImporter') }
+ let(:importer_result) { instance_double('ServiceResponse', success?: true) }
+ let(:gist_object) do
+ instance_double('Gitlab::GithubGistsImport::Representation::Gist',
+ gist_hash.merge(github_identifiers: { id: '055b70' }, truncated_title: 'random text', visibility_level: 0))
+ end
+
+ let(:log_attributes) do
+ {
+ 'user_id' => user.id,
+ 'github_identifiers' => { 'id': gist_object.id },
+ 'class' => 'Gitlab::GithubGistsImport::ImportGistWorker',
+ 'correlation_id' => 'new-correlation-id',
+ 'jid' => nil,
+ 'job_status' => 'running',
+ 'queue' => 'github_gists_importer:github_gists_import_import_gist'
+ }
+ end
+
+ describe '#perform' do
+ before do
+ allow(Gitlab::GithubGistsImport::Representation::Gist)
+ .to receive(:from_json_hash)
+ .with(gist_hash)
+ .and_return(gist_object)
+
+ allow(Gitlab::GithubGistsImport::Importer::GistImporter)
+ .to receive(:new)
+ .with(gist_object, user.id)
+ .and_return(importer)
+
+ allow(Gitlab::ApplicationContext).to receive(:current).and_return('correlation_id' => 'new-correlation-id')
+ allow(described_class).to receive(:queue).and_return('github_gists_importer:github_gists_import_import_gist')
+ end
+
+ context 'when success' do
+ it 'imports gist' do
+ expect(Gitlab::GithubImport::Logger)
+ .to receive(:info)
+ .with(log_attributes.merge('message' => 'start importer'))
+ expect(importer).to receive(:execute).and_return(importer_result)
+ expect(Gitlab::JobWaiter).to receive(:notify).with('some_key', subject.jid)
+ expect(Gitlab::GithubImport::Logger)
+ .to receive(:info)
+ .with(log_attributes.merge('message' => 'importer finished'))
+
+ subject.perform(user.id, gist_hash, 'some_key')
+ end
+ end
+
+ context 'when importer raised an error' do
+ it 'raises an error' do
+ exception = StandardError.new('_some_error_')
+
+ expect(importer).to receive(:execute).and_raise(exception)
+ expect(Gitlab::GithubImport::Logger)
+ .to receive(:error)
+ .with(log_attributes.merge('message' => 'importer failed', 'error.message' => '_some_error_'))
+ expect(Gitlab::ErrorTracking).to receive(:track_exception)
+
+ expect { subject.perform(user.id, gist_hash, 'some_key') }.to raise_error(StandardError)
+ end
+ end
+ end
+end
diff --git a/spec/workers/gitlab/github_gists_import/start_import_worker_spec.rb b/spec/workers/gitlab/github_gists_import/start_import_worker_spec.rb
new file mode 100644
index 00000000000..523b7463a9d
--- /dev/null
+++ b/spec/workers/gitlab/github_gists_import/start_import_worker_spec.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubGistsImport::StartImportWorker, feature_category: :importer do
+ subject(:worker) { described_class.new }
+
+ let_it_be(:user) { create(:user) }
+ let(:token) { Gitlab::CryptoHelper.aes256_gcm_encrypt('token') }
+ let(:importer) { instance_double(Gitlab::GithubGistsImport::Importer::GistsImporter) }
+ let(:waiter) { instance_double(Gitlab::JobWaiter, key: :key, jobs_remaining: 1) }
+ let(:importer_context) { Struct.new(:success?, :error, :waiter, :next_attempt_in, keyword_init: true) }
+ let(:log_attributes) do
+ {
+ 'user_id' => user.id,
+ 'class' => described_class.name,
+ 'correlation_id' => 'new-correlation-id',
+ 'jid' => nil,
+ 'job_status' => 'running',
+ 'queue' => 'github_gists_importer:github_gists_import_start_import'
+ }
+ end
+
+ describe '#perform', :aggregate_failures do
+ before do
+ allow(Gitlab::GithubImport::Logger)
+ .to receive(:info)
+ .with(log_attributes.merge('message' => 'starting importer'))
+
+ allow(Gitlab::ApplicationContext).to receive(:current).and_return('correlation_id' => 'new-correlation-id')
+ allow(described_class).to receive(:queue).and_return('github_gists_importer:github_gists_import_start_import')
+ end
+
+ context 'when import was successfull' do
+ it 'imports all the gists' do
+ expect(Gitlab::CryptoHelper)
+ .to receive(:aes256_gcm_decrypt)
+ .with(token)
+ .and_call_original
+
+ expect(Gitlab::GithubGistsImport::Importer::GistsImporter)
+ .to receive(:new)
+ .with(user, 'token')
+ .and_return(importer)
+
+ expect(importer)
+ .to receive(:execute)
+ .and_return(importer_context.new(success?: true, waiter: waiter))
+
+ expect(Gitlab::GithubGistsImport::FinishImportWorker)
+ .to receive(:perform_async)
+ .with(user.id, waiter.key, waiter.jobs_remaining)
+
+ expect(Gitlab::GithubImport::Logger)
+ .to receive(:info)
+ .with(log_attributes.merge('message' => 'importer finished'))
+
+ worker.perform(user.id, token)
+ end
+ end
+
+ context 'when importer returns an error' do
+ it 'raises an error' do
+ exception = StandardError.new('_some_error_')
+ importer_result = importer_context.new(success?: false, error: exception)
+
+ expect_next_instance_of(Gitlab::GithubGistsImport::Importer::GistsImporter) do |importer|
+ expect(importer).to receive(:execute).and_return(importer_result)
+ end
+
+ expect(Gitlab::GithubImport::Logger)
+ .to receive(:error)
+ .with(log_attributes.merge('message' => 'import failed', 'error.message' => exception.message))
+
+ expect { worker.perform(user.id, token) }.to raise_error(StandardError)
+ end
+ end
+
+ context 'when rate limit is reached' do
+ it 'reschedules worker' do
+ exception = Gitlab::GithubImport::RateLimitError.new
+ importer_result = importer_context.new(success?: false, error: exception, next_attempt_in: 5)
+
+ expect_next_instance_of(Gitlab::GithubGistsImport::Importer::GistsImporter) do |importer|
+ expect(importer).to receive(:execute).and_return(importer_result)
+ end
+
+ expect(Gitlab::GithubImport::Logger)
+ .to receive(:info)
+ .with(log_attributes.merge('message' => 'rate limit reached'))
+
+ expect(described_class).to receive(:perform_in).with(5, user.id, token)
+
+ worker.perform(user.id, token)
+ end
+ end
+ end
+
+ describe '.sidekiq_retries_exhausted' do
+ it 'sets status to failed' do
+ job = { 'args' => [user.id, token], 'jid' => '123' }
+
+ expect_next_instance_of(Gitlab::GithubGistsImport::Status) do |status|
+ expect(status).to receive(:fail!)
+ end
+
+ described_class.sidekiq_retries_exhausted_block.call(job)
+ end
+ end
+end
diff --git a/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb b/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb
index 15bc55c1526..c92741e8f10 100644
--- a/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb
@@ -14,6 +14,7 @@ RSpec.describe Gitlab::GithubImport::ImportDiffNoteWorker do
hash = {
'noteable_id' => 42,
'github_id' => 42,
+ 'html_url' => 'https://github.com/foo/bar/pull/42',
'path' => 'README.md',
'commit_id' => '123abc',
'diff_hunk' => "@@ -1 +1 @@\n-Hello\n+Hello world",
diff --git a/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb b/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb
index 695e21f4733..0244e69b7b6 100644
--- a/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb
+++ b/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe Gitlab::JiraImport::ImportIssueWorker do
describe '#perform', :clean_gitlab_redis_cache do
let(:assignee_ids) { [user.id] }
let(:issue_attrs) do
- build(:issue, project_id: project.id, title: 'jira issue')
+ build(:issue, project_id: project.id, namespace_id: project.project_namespace_id, title: 'jira issue')
.as_json.merge(
'label_ids' => [jira_issue_label_1.id, jira_issue_label_2.id], 'assignee_ids' => assignee_ids
).except('issue_type')
@@ -71,6 +71,7 @@ RSpec.describe Gitlab::JiraImport::ImportIssueWorker do
expect(issue.title).to eq('jira issue')
expect(issue.project).to eq(project)
+ expect(issue.namespace).to eq(project.project_namespace)
expect(issue.labels).to match_array([label, jira_issue_label_1, jira_issue_label_2])
expect(issue.assignees).to eq([user])
end
diff --git a/spec/workers/gitlab_shell_worker_spec.rb b/spec/workers/gitlab_shell_worker_spec.rb
index a5419291d35..838f2ef4ba4 100644
--- a/spec/workers/gitlab_shell_worker_spec.rb
+++ b/spec/workers/gitlab_shell_worker_spec.rb
@@ -18,29 +18,13 @@ RSpec.describe GitlabShellWorker, :sidekiq_inline do
end
describe 'all other commands' do
- context 'when verify_gitlab_shell_worker_method_names is enabled' do
- it 'raises ArgumentError' do
- allow_next_instance_of(described_class) do |job_instance|
- expect(job_instance).not_to receive(:gitlab_shell)
- end
-
- expect { described_class.perform_async('foo', 'bar', 'baz') }
- .to raise_error(ArgumentError, 'foo not allowed for GitlabShellWorker')
- end
- end
-
- context 'when verify_gitlab_shell_worker_method_names is disabled' do
- before do
- stub_feature_flags(verify_gitlab_shell_worker_method_names: false)
+ it 'raises ArgumentError' do
+ allow_next_instance_of(described_class) do |job_instance|
+ expect(job_instance).not_to receive(:gitlab_shell)
end
- it 'forwards the message to Gitlab::Shell' do
- expect_next_instance_of(Gitlab::Shell) do |instance|
- expect(instance).to receive('foo').with('bar', 'baz')
- end
-
- described_class.perform_async('foo', 'bar', 'baz')
- end
+ expect { described_class.perform_async('foo', 'bar', 'baz') }
+ .to raise_error(ArgumentError, 'foo not allowed for GitlabShellWorker')
end
end
end
diff --git a/spec/workers/incident_management/close_incident_worker_spec.rb b/spec/workers/incident_management/close_incident_worker_spec.rb
index b0d284ba5db..c96bb4a3d1e 100644
--- a/spec/workers/incident_management/close_incident_worker_spec.rb
+++ b/spec/workers/incident_management/close_incident_worker_spec.rb
@@ -17,14 +17,14 @@ RSpec.describe IncidentManagement::CloseIncidentWorker do
expect(service).to receive(:execute).with(issue, system_note: false).and_call_original
end
- expect { worker.perform(issue_id) }.to change(ResourceStateEvent, :count).by(1)
+ expect { worker.perform(issue_id) }.to change { ResourceStateEvent.count }.by(1)
end
shared_examples 'does not call the close issue service' do
specify do
expect(Issues::CloseService).not_to receive(:new)
- expect { worker.perform(issue_id) }.not_to change(ResourceStateEvent, :count)
+ expect { worker.perform(issue_id) }.not_to change { ResourceStateEvent.count }
end
end
@@ -58,7 +58,7 @@ RSpec.describe IncidentManagement::CloseIncidentWorker do
end
specify do
- expect { worker.perform(issue_id) }.not_to change(ResourceStateEvent, :count)
+ expect { worker.perform(issue_id) }.not_to change { ResourceStateEvent.count }
end
end
end
diff --git a/spec/workers/incident_management/pager_duty/process_incident_worker_spec.rb b/spec/workers/incident_management/pager_duty/process_incident_worker_spec.rb
index e2be91516b9..b81f1a575b5 100644
--- a/spec/workers/incident_management/pager_duty/process_incident_worker_spec.rb
+++ b/spec/workers/incident_management/pager_duty/process_incident_worker_spec.rb
@@ -22,14 +22,14 @@ RSpec.describe IncidentManagement::PagerDuty::ProcessIncidentWorker do
'assignees' => [{
'summary' => 'Laura Haley', 'url' => 'https://webdemo.pagerduty.com/users/P553OPV'
}],
- 'impacted_services' => [{
+ 'impacted_service' => {
'summary' => 'Production XDB Cluster', 'url' => 'https://webdemo.pagerduty.com/services/PN49J75'
- }]
+ }
}
end
it 'creates a GitLab issue' do
- expect { perform }.to change(Issue, :count).by(1)
+ expect { perform }.to change { Issue.count }.by(1)
end
end
@@ -41,7 +41,7 @@ RSpec.describe IncidentManagement::PagerDuty::ProcessIncidentWorker do
end
it 'does not create a GitLab issue' do
- expect { perform }.not_to change(Issue, :count)
+ expect { perform }.not_to change { Issue.count }
end
it 'logs a warning' do
diff --git a/spec/workers/issuable_export_csv_worker_spec.rb b/spec/workers/issuable_export_csv_worker_spec.rb
index a18d10ad3df..a5172d916b6 100644
--- a/spec/workers/issuable_export_csv_worker_spec.rb
+++ b/spec/workers/issuable_export_csv_worker_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe IssuableExportCsvWorker do
let(:issuable_type) { :issue }
it 'emails a CSV' do
- expect { subject }.to change(ActionMailer::Base.deliveries, :size).by(1)
+ expect { subject }.to change { ActionMailer::Base.deliveries.size }.by(1)
end
it 'ensures that project_id is passed to issues_finder' do
@@ -54,7 +54,7 @@ RSpec.describe IssuableExportCsvWorker do
let(:issuable_type) { :merge_request }
it 'emails a CSV' do
- expect { subject }.to change(ActionMailer::Base.deliveries, :size).by(1)
+ expect { subject }.to change { ActionMailer::Base.deliveries.size }.by(1)
end
it 'calls the MR export service' do
diff --git a/spec/workers/jira_connect/forward_event_worker_spec.rb b/spec/workers/jira_connect/forward_event_worker_spec.rb
index 7de9952a1da..d3db07b8cb4 100644
--- a/spec/workers/jira_connect/forward_event_worker_spec.rb
+++ b/spec/workers/jira_connect/forward_event_worker_spec.rb
@@ -24,14 +24,14 @@ RSpec.describe JiraConnect::ForwardEventWorker do
expect(Atlassian::Jwt).to receive(:encode).with({ iss: client_key, qsh: 'some_qsh' }, shared_secret).and_return('auth_token')
expect(JiraConnect::RetryRequestWorker).to receive(:perform_async).with(event_url, 'auth_token')
- expect { perform }.to change(JiraConnectInstallation, :count).by(-1)
+ expect { perform }.to change { JiraConnectInstallation.count }.by(-1)
end
context 'when installation does not exist' do
let(:jira_connect_installation) { instance_double(JiraConnectInstallation, id: -1) }
it 'does nothing' do
- expect { perform }.not_to change(JiraConnectInstallation, :count)
+ expect { perform }.not_to change { JiraConnectInstallation.count }
end
end
@@ -39,7 +39,7 @@ RSpec.describe JiraConnect::ForwardEventWorker do
let!(:jira_connect_installation) { create(:jira_connect_installation) }
it 'forwards the event including the auth header' do
- expect { perform }.to change(JiraConnectInstallation, :count).by(-1)
+ expect { perform }.to change { JiraConnectInstallation.count }.by(-1)
expect(JiraConnect::RetryRequestWorker).not_to receive(:perform_async)
end
diff --git a/spec/workers/jira_connect/send_uninstalled_hook_worker_spec.rb b/spec/workers/jira_connect/send_uninstalled_hook_worker_spec.rb
new file mode 100644
index 00000000000..d8ca8dee54d
--- /dev/null
+++ b/spec/workers/jira_connect/send_uninstalled_hook_worker_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe JiraConnect::SendUninstalledHookWorker, feature_category: :integrations do
+ describe '#perform' do
+ let_it_be(:jira_connect_installation) { create(:jira_connect_installation) }
+ let(:instance_url) { 'http://example.com' }
+ let(:attempts) { 3 }
+ let(:service_response) { ServiceResponse.new(status: :success) }
+ let(:job_args) { [jira_connect_installation.id, instance_url] }
+
+ before do
+ allow(JiraConnectInstallations::ProxyLifecycleEventService).to receive(:execute).and_return(service_response)
+ end
+
+ include_examples 'an idempotent worker' do
+ it 'calls the ProxyLifecycleEventService service' do
+ expect(JiraConnectInstallations::ProxyLifecycleEventService).to receive(:execute).with(
+ jira_connect_installation,
+ :uninstalled,
+ instance_url
+ ).twice
+
+ subject
+ end
+ end
+ end
+end
diff --git a/spec/workers/mail_scheduler/notification_service_worker_spec.rb b/spec/workers/mail_scheduler/notification_service_worker_spec.rb
index 3c17025c152..482d99a43c2 100644
--- a/spec/workers/mail_scheduler/notification_service_worker_spec.rb
+++ b/spec/workers/mail_scheduler/notification_service_worker_spec.rb
@@ -53,31 +53,15 @@ RSpec.describe MailScheduler::NotificationServiceWorker do
end
context 'when the method is not allowed' do
- context 'when verify_mail_scheduler_notification_service_worker_method_names is enabled' do
- it 'raises ArgumentError' do
- expect(worker.notification_service).not_to receive(:async)
- expect(worker.notification_service).not_to receive(:foo)
+ it 'raises ArgumentError' do
+ expect(worker.notification_service).not_to receive(:async)
+ expect(worker.notification_service).not_to receive(:foo)
- expect { worker.perform('async', *serialize(key)) }
- .to raise_error(ArgumentError, 'async not allowed for MailScheduler::NotificationServiceWorker')
+ expect { worker.perform('async', *serialize(key)) }
+ .to raise_error(ArgumentError, 'async not allowed for MailScheduler::NotificationServiceWorker')
- expect { worker.perform('foo', *serialize(key)) }
- .to raise_error(ArgumentError, 'foo not allowed for MailScheduler::NotificationServiceWorker')
- end
- end
-
- context 'when verify_mail_scheduler_notification_service_worker_method_names is disabled' do
- before do
- stub_feature_flags(verify_mail_scheduler_notification_service_worker_method_names: false)
- end
-
- it 'forwards the argument to the service' do
- expect(worker.notification_service).to receive(:async)
- expect(worker.notification_service).to receive(:foo)
-
- worker.perform('async', *serialize(key))
- worker.perform('foo', *serialize(key))
- end
+ expect { worker.perform('foo', *serialize(key)) }
+ .to raise_error(ArgumentError, 'foo not allowed for MailScheduler::NotificationServiceWorker')
end
end
end
diff --git a/spec/workers/merge_requests/delete_branch_worker_spec.rb b/spec/workers/merge_requests/delete_branch_worker_spec.rb
deleted file mode 100644
index 80ca8c061f5..00000000000
--- a/spec/workers/merge_requests/delete_branch_worker_spec.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe MergeRequests::DeleteBranchWorker do
- let_it_be(:merge_request) { create(:merge_request) }
- let_it_be(:user) { create(:user) }
-
- let(:branch) { merge_request.source_branch }
- let(:sha) { merge_request.source_branch_sha }
- let(:retarget_branch) { true }
- let(:worker) { described_class.new }
-
- describe '#perform' do
- context 'with a non-existing merge request' do
- it 'does nothing' do
- expect(::Branches::DeleteService).not_to receive(:new)
- worker.perform(non_existing_record_id, user.id, branch, retarget_branch)
- end
- end
-
- context 'with a non-existing user' do
- it 'does nothing' do
- expect(::Branches::DeleteService).not_to receive(:new)
-
- worker.perform(merge_request.id, non_existing_record_id, branch, retarget_branch)
- end
- end
-
- context 'with existing user and merge request' do
- it 'calls service to delete source branch' do
- expect_next_instance_of(::Branches::DeleteService) do |instance|
- expect(instance).to receive(:execute).with(branch)
- end
-
- worker.perform(merge_request.id, user.id, branch, retarget_branch)
- end
-
- context 'when retarget branch param is true' do
- it 'calls the retarget chain service' do
- expect_next_instance_of(::MergeRequests::RetargetChainService) do |instance|
- expect(instance).to receive(:execute).with(merge_request)
- end
-
- worker.perform(merge_request.id, user.id, branch, retarget_branch)
- end
- end
-
- context 'when retarget branch param is false' do
- let(:retarget_branch) { false }
-
- it 'does not call the retarget chain service' do
- expect(::MergeRequests::RetargetChainService).not_to receive(:new)
-
- worker.perform(merge_request.id, user.id, branch, retarget_branch)
- end
- end
- end
-
- it_behaves_like 'an idempotent worker' do
- let(:merge_request) { create(:merge_request) }
- let(:job_args) { [merge_request.id, sha, user.id, true] }
- end
- end
-end
diff --git a/spec/workers/merge_requests/delete_source_branch_worker_spec.rb b/spec/workers/merge_requests/delete_source_branch_worker_spec.rb
index 2935d3ef5dc..a7e4ffad259 100644
--- a/spec/workers/merge_requests/delete_source_branch_worker_spec.rb
+++ b/spec/workers/merge_requests/delete_source_branch_worker_spec.rb
@@ -3,17 +3,24 @@
require 'spec_helper'
RSpec.describe MergeRequests::DeleteSourceBranchWorker do
- let_it_be(:merge_request) { create(:merge_request) }
let_it_be(:user) { create(:user) }
+ let_it_be(:merge_request) { create(:merge_request, author: user) }
let(:sha) { merge_request.source_branch_sha }
let(:worker) { described_class.new }
describe '#perform' do
+ before do
+ allow_next_instance_of(::Projects::DeleteBranchWorker) do |instance|
+ allow(instance).to receive(:perform).with(merge_request.source_project.id, user.id,
+ merge_request.source_branch)
+ end
+ end
+
context 'when the add_delete_branch_worker feature flag is enabled' do
context 'with a non-existing merge request' do
it 'does nothing' do
- expect(::MergeRequests::DeleteBranchWorker).not_to receive(:perform_async)
+ expect(::Projects::DeleteBranchWorker).not_to receive(:new)
worker.perform(non_existing_record_id, sha, user.id)
end
@@ -21,7 +28,7 @@ RSpec.describe MergeRequests::DeleteSourceBranchWorker do
context 'with a non-existing user' do
it 'does nothing' do
- expect(::MergeRequests::DeleteBranchWorker).not_to receive(:perform_async)
+ expect(::Projects::DeleteBranchWorker).not_to receive(:new)
worker.perform(merge_request.id, sha, non_existing_record_id)
end
@@ -29,15 +36,17 @@ RSpec.describe MergeRequests::DeleteSourceBranchWorker do
context 'with existing user and merge request' do
it 'creates a new delete branch worker async' do
- expect(::MergeRequests::DeleteBranchWorker).to receive(:perform_async).with(merge_request.id, user.id,
- merge_request.source_branch, true)
+ expect_next_instance_of(::Projects::DeleteBranchWorker) do |instance|
+ expect(instance).to receive(:perform).with(merge_request.source_project.id, user.id,
+ merge_request.source_branch)
+ end
worker.perform(merge_request.id, sha, user.id)
end
context 'source branch sha does not match' do
it 'does nothing' do
- expect(::MergeRequests::DeleteBranchWorker).not_to receive(:perform_async)
+ expect(::Projects::DeleteBranchWorker).not_to receive(:new)
worker.perform(merge_request.id, 'new-source-branch-sha', user.id)
end
@@ -45,7 +54,6 @@ RSpec.describe MergeRequests::DeleteSourceBranchWorker do
end
it_behaves_like 'an idempotent worker' do
- let(:merge_request) { create(:merge_request) }
let(:job_args) { [merge_request.id, sha, user.id] }
end
end
@@ -117,7 +125,6 @@ RSpec.describe MergeRequests::DeleteSourceBranchWorker do
end
it_behaves_like 'an idempotent worker' do
- let(:merge_request) { create(:merge_request) }
let(:job_args) { [merge_request.id, sha, user.id] }
end
end
diff --git a/spec/workers/metrics/dashboard/prune_old_annotations_worker_spec.rb b/spec/workers/metrics/dashboard/prune_old_annotations_worker_spec.rb
index 11343f69d6f..491ea64cff1 100644
--- a/spec/workers/metrics/dashboard/prune_old_annotations_worker_spec.rb
+++ b/spec/workers/metrics/dashboard/prune_old_annotations_worker_spec.rb
@@ -10,23 +10,24 @@ RSpec.describe Metrics::Dashboard::PruneOldAnnotationsWorker do
describe '#perform' do
it 'removes all annotations older than cut off', :aggregate_failures do
- Timecop.freeze(now) do
+ travel_to(now) do
described_class.new.perform
expect(Metrics::Dashboard::Annotation.all).to match_array([one_day_old_annotation, two_weeks_old_annotation])
# is idempotent in the scope of 24h
expect { described_class.new.perform }.not_to change { Metrics::Dashboard::Annotation.all.to_a }
- travel_to(24.hours.from_now) do
- described_class.new.perform
- expect(Metrics::Dashboard::Annotation.all).to match_array([one_day_old_annotation])
- end
+ end
+
+ travel_to(now + 24.hours) do
+ described_class.new.perform
+ expect(Metrics::Dashboard::Annotation.all).to match_array([one_day_old_annotation])
end
end
context 'batch to be deleted is bigger than upper limit' do
it 'schedules second job to clear remaining records' do
- Timecop.freeze(now) do
+ travel_to(now) do
create(:metrics_dashboard_annotation, starting_at: 1.month.ago)
stub_const("#{described_class}::DELETE_LIMIT", 1)
diff --git a/spec/workers/metrics/dashboard/sync_dashboards_worker_spec.rb b/spec/workers/metrics/dashboard/sync_dashboards_worker_spec.rb
index f151780ffd7..4b670a753e7 100644
--- a/spec/workers/metrics/dashboard/sync_dashboards_worker_spec.rb
+++ b/spec/workers/metrics/dashboard/sync_dashboards_worker_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe Metrics::Dashboard::SyncDashboardsWorker do
describe ".perform" do
context 'with valid dashboard hash' do
it 'imports metrics' do
- expect { worker.perform(project.id) }.to change(PrometheusMetric, :count).by(3)
+ expect { worker.perform(project.id) }.to change { PrometheusMetric.count }.by(3)
end
it 'is idempotent' do
@@ -32,7 +32,7 @@ RSpec.describe Metrics::Dashboard::SyncDashboardsWorker do
end
it 'does not import metrics' do
- expect { worker.perform(project.id) }.not_to change(PrometheusMetric, :count)
+ expect { worker.perform(project.id) }.not_to change { PrometheusMetric.count }
end
it 'does not raise an error' do
diff --git a/spec/workers/namespaces/process_sync_events_worker_spec.rb b/spec/workers/namespaces/process_sync_events_worker_spec.rb
index 5e5179eab62..9f389089609 100644
--- a/spec/workers/namespaces/process_sync_events_worker_spec.rb
+++ b/spec/workers/namespaces/process_sync_events_worker_spec.rb
@@ -31,7 +31,7 @@ RSpec.describe Namespaces::ProcessSyncEventsWorker do
expect(described_class).to receive(:perform_async).at_least(:twice).and_call_original
expect do
described_class.perform_async
- end.to change(Namespaces::SyncEvent, :count).from(3).to(0)
+ end.to change { Namespaces::SyncEvent.count }.from(3).to(0)
end
end
@@ -44,11 +44,11 @@ RSpec.describe Namespaces::ProcessSyncEventsWorker do
end
it 'consumes all sync events' do
- expect { perform }.to change(Namespaces::SyncEvent, :count).from(5).to(0)
+ expect { perform }.to change { Namespaces::SyncEvent.count }.from(5).to(0)
end
it 'syncs namespace hierarchy traversal ids' do
- expect { perform }.to change(Ci::NamespaceMirror, :all).to contain_exactly(
+ expect { perform }.to change { Ci::NamespaceMirror.all }.to contain_exactly(
an_object_having_attributes(namespace_id: group1.id, traversal_ids: [group1.id]),
an_object_having_attributes(namespace_id: group2.id, traversal_ids: [group1.id, group2.id]),
an_object_having_attributes(namespace_id: group3.id, traversal_ids: [group1.id, group2.id, group3.id])
diff --git a/spec/workers/namespaces/root_statistics_worker_spec.rb b/spec/workers/namespaces/root_statistics_worker_spec.rb
index 30854415405..e047c94816f 100644
--- a/spec/workers/namespaces/root_statistics_worker_spec.rb
+++ b/spec/workers/namespaces/root_statistics_worker_spec.rb
@@ -92,7 +92,6 @@ RSpec.describe Namespaces::RootStatisticsWorker, '#perform' do
it_behaves_like 'worker with data consistency',
described_class,
- feature_flag: :root_statistics_worker_read_replica,
data_consistency: :sticky
it 'has the `until_executed` deduplicate strategy' do
diff --git a/spec/workers/namespaces/schedule_aggregation_worker_spec.rb b/spec/workers/namespaces/schedule_aggregation_worker_spec.rb
index f2fe53d6112..62f9be501cc 100644
--- a/spec/workers/namespaces/schedule_aggregation_worker_spec.rb
+++ b/spec/workers/namespaces/schedule_aggregation_worker_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe Namespaces::ScheduleAggregationWorker, '#perform', :clean_gitlab_
expect do
worker.perform(group.id)
- end.not_to change(Namespace::AggregationSchedule, :count)
+ end.not_to change { Namespace::AggregationSchedule.count }
end
end
@@ -26,7 +26,7 @@ RSpec.describe Namespaces::ScheduleAggregationWorker, '#perform', :clean_gitlab_
expect do
worker.perform(group.id)
- end.to change(Namespace::AggregationSchedule, :count).by(1)
+ end.to change { Namespace::AggregationSchedule.count }.by(1)
expect(group.aggregation_schedule).to be_present
end
diff --git a/spec/workers/packages/debian/process_changes_worker_spec.rb b/spec/workers/packages/debian/process_changes_worker_spec.rb
index 93eba4bfa9a..fc482245ebe 100644
--- a/spec/workers/packages/debian/process_changes_worker_spec.rb
+++ b/spec/workers/packages/debian/process_changes_worker_spec.rb
@@ -78,10 +78,28 @@ RSpec.describe Packages::Debian::ProcessChangesWorker, type: :worker do
end
end
+ context 'without a distribution' do
+ before do
+ distribution.destroy!
+ end
+
+ it 'removes package file and log exception', :aggregate_failures do
+ expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
+ instance_of(ActiveRecord::RecordNotFound),
+ package_file_id: package_file_id,
+ user_id: user_id
+ )
+ expect { subject }
+ .to not_change { Packages::Package.count }
+ .and change { Packages::PackageFile.count }.by(-1)
+ .and change { incoming.package_files.count }.from(7).to(6)
+ end
+ end
+
context 'when the service raises an error' do
let(:package_file) { incoming.package_files.first }
- it 'removes package file', :aggregate_failures do
+ it 'removes package file and log exception', :aggregate_failures do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
instance_of(Packages::Debian::ExtractChangesMetadataService::ExtractionError),
package_file_id: package_file_id,
diff --git a/spec/workers/packages/debian/process_package_file_worker_spec.rb b/spec/workers/packages/debian/process_package_file_worker_spec.rb
new file mode 100644
index 00000000000..532bfb096a3
--- /dev/null
+++ b/spec/workers/packages/debian/process_package_file_worker_spec.rb
@@ -0,0 +1,138 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Debian::ProcessPackageFileWorker, type: :worker, feature_category: :package_registry do
+ let_it_be(:user) { create(:user) }
+ let_it_be_with_reload(:distribution) { create(:debian_project_distribution, :with_file, codename: 'unstable') }
+
+ let(:incoming) { create(:debian_incoming, project: distribution.project) }
+ let(:distribution_name) { distribution.codename }
+ let(:worker) { described_class.new }
+
+ describe '#perform' do
+ let(:package_file_id) { package_file.id }
+ let(:user_id) { user.id }
+
+ subject { worker.perform(package_file_id, user_id, distribution_name, component_name) }
+
+ shared_examples 'returns early without error' do
+ it 'returns early without error' do
+ expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
+ expect(::Packages::Debian::ProcessPackageFileService).not_to receive(:new)
+
+ subject
+ end
+ end
+
+ using RSpec::Parameterized::TableSyntax
+
+ where(:case_name, :expected_file_type, :file_name, :component_name) do
+ 'with a deb' | 'deb' | 'libsample0_1.2.3~alpha2_amd64.deb' | 'main'
+ 'with an udeb' | 'udeb' | 'sample-udeb_1.2.3~alpha2_amd64.udeb' | 'contrib'
+ end
+
+ with_them do
+ context 'with Debian package file' do
+ let(:package_file) { incoming.package_files.with_file_name(file_name).first }
+
+ context 'with mocked service' do
+ it 'calls ProcessPackageFileService' do
+ expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
+ expect_next_instance_of(::Packages::Debian::ProcessPackageFileService) do |service|
+ expect(service).to receive(:execute)
+ .with(no_args)
+ end
+
+ subject
+ end
+ end
+
+ context 'with non existing user' do
+ let(:user_id) { non_existing_record_id }
+
+ it_behaves_like 'returns early without error'
+ end
+
+ context 'with nil user id' do
+ let(:user_id) { nil }
+
+ it_behaves_like 'returns early without error'
+ end
+
+ context 'when the service raises an error' do
+ let(:package_file) { incoming.package_files.with_file_name('sample_1.2.3~alpha2.tar.xz').first }
+
+ it 'removes package file', :aggregate_failures do
+ expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
+ instance_of(ArgumentError),
+ package_file_id: package_file_id,
+ user_id: user_id,
+ distribution_name: distribution_name,
+ component_name: component_name
+ )
+ expect { subject }
+ .to not_change(Packages::Package, :count)
+ .and change { Packages::PackageFile.count }.by(-1)
+ .and change { incoming.package_files.count }.from(7).to(6)
+
+ expect { package_file.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ it_behaves_like 'an idempotent worker' do
+ let(:job_args) { [package_file.id, user.id, distribution_name, component_name] }
+
+ it 'sets the Debian file type as deb', :aggregate_failures do
+ expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
+
+ # Using subject inside this block will process the job multiple times
+ expect { subject }
+ .to change { Packages::Package.count }.from(1).to(2)
+ .and not_change(Packages::PackageFile, :count)
+ .and change { incoming.package_files.count }.from(7).to(6)
+ .and change {
+ package_file&.debian_file_metadatum&.reload&.file_type
+ }.from('unknown').to(expected_file_type)
+
+ created_package = Packages::Package.last
+ expect(created_package.name).to eq 'sample'
+ expect(created_package.version).to eq '1.2.3~alpha2'
+ expect(created_package.creator).to eq user
+ end
+ end
+ end
+ end
+
+ context 'with already processed package file' do
+ let_it_be(:package_file) { create(:debian_package_file) }
+
+ let(:component_name) { 'main' }
+
+ it_behaves_like 'returns early without error'
+ end
+
+ context 'with a deb' do
+ let(:package_file) { incoming.package_files.with_file_name('libsample0_1.2.3~alpha2_amd64.deb').first }
+ let(:component_name) { 'main' }
+
+ context 'with FIPS mode enabled', :fips_mode do
+ it 'raises an error' do
+ expect { subject }.to raise_error(::Packages::FIPS::DisabledError)
+ end
+ end
+
+ context 'with non existing package file' do
+ let(:package_file_id) { non_existing_record_id }
+
+ it_behaves_like 'returns early without error'
+ end
+
+ context 'with nil package file id' do
+ let(:package_file_id) { nil }
+
+ it_behaves_like 'returns early without error'
+ end
+ end
+ end
+end
diff --git a/spec/workers/pipeline_schedule_worker_spec.rb b/spec/workers/pipeline_schedule_worker_spec.rb
index 4a7db0eca56..d23907a8def 100644
--- a/spec/workers/pipeline_schedule_worker_spec.rb
+++ b/spec/workers/pipeline_schedule_worker_spec.rb
@@ -17,8 +17,12 @@ RSpec.describe PipelineScheduleWorker do
before do
stub_application_setting(auto_devops_enabled: false)
stub_ci_pipeline_to_return_yaml_file
+ end
- pipeline_schedule.update_column(:next_run_at, 1.day.ago)
+ around do |example|
+ travel_to(pipeline_schedule.next_run_at + 1.hour) do
+ example.run
+ end
end
context 'when the schedule is runnable by the user' do
@@ -26,16 +30,22 @@ RSpec.describe PipelineScheduleWorker do
project.add_maintainer(user)
end
- context 'when there is a scheduled pipeline within next_run_at' do
+ context 'when there is a scheduled pipeline within next_run_at', :sidekiq_inline do
shared_examples 'successful scheduling' do
- it 'creates a new pipeline', :sidekiq_might_not_need_inline do
+ it 'creates a new pipeline' do
expect { subject }.to change { project.ci_pipelines.count }.by(1)
- expect(Ci::Pipeline.last).to be_schedule
+ last_pipeline = project.ci_pipelines.last
+
+ expect(last_pipeline).to be_schedule
+ expect(last_pipeline.pipeline_schedule).to eq(pipeline_schedule)
+ end
+
+ it 'updates next_run_at' do
+ expect { subject }.to change { pipeline_schedule.reload.next_run_at }.by(1.day)
+ end
- pipeline_schedule.reload
- expect(pipeline_schedule.next_run_at).to be > Time.current
- expect(pipeline_schedule).to eq(project.ci_pipelines.last.pipeline_schedule)
- expect(pipeline_schedule).to be_active
+ it 'does not change active status' do
+ expect { subject }.not_to change { pipeline_schedule.reload.active? }.from(true)
end
end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index d632ca39e44..210987555c9 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -275,30 +275,17 @@ RSpec.describe PostReceive do
expect { perform }.to change { counter.read(:pushes) }.by(1)
end
- it 'records correct payload with Snowplow event', :snowplow do
- stub_feature_flags(route_hll_to_snowplow_phase2: true)
-
- perform
-
- expect_snowplow_event(
- category: 'PostReceive',
- action: 'source_code_pushes',
- namespace: project.namespace,
- user: project.first_owner,
- project: project
- )
- end
-
- context 'when FF is disabled' do
- before do
- stub_feature_flags(route_hll_to_snowplow_phase2: false)
- end
-
- it 'doesnt emit snowplow events', :snowplow do
- perform
-
- expect_no_snowplow_event
- end
+ it_behaves_like 'Snowplow event tracking' do
+ let(:action) { :push }
+ let(:category) { described_class.name }
+ let(:namespace) { project.namespace }
+ let(:user) { project.creator }
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:label) { 'counts.source_code_pushes' }
+ let(:property) { 'source_code_pushes' }
+ let(:context) { [Gitlab::Tracking::ServicePingContext.new(data_source: :redis, key_path: label).to_h] }
+
+ subject(:post_receive) { perform }
end
end
end
@@ -324,8 +311,8 @@ RSpec.describe PostReceive do
expect do
perform
project.reload
- end.to change(project, :last_activity_at)
- .and change(project, :last_repository_updated_at)
+ end.to change { project.last_activity_at }
+ .and change { project.last_repository_updated_at }
end
end
diff --git a/spec/workers/process_commit_worker_spec.rb b/spec/workers/process_commit_worker_spec.rb
index 01c44399b0c..072c660bc2b 100644
--- a/spec/workers/process_commit_worker_spec.rb
+++ b/spec/workers/process_commit_worker_spec.rb
@@ -118,7 +118,7 @@ RSpec.describe ProcessCommitWorker do
it 'creates Issue::CloseWorker jobs' do
expect do
worker.close_issues(project, user, user, commit, [issue])
- end.to change(Issues::CloseWorker.jobs, :size).by(1)
+ end.to change { Issues::CloseWorker.jobs.size }.by(1)
end
end
diff --git a/spec/workers/projects/delete_branch_worker_spec.rb b/spec/workers/projects/delete_branch_worker_spec.rb
new file mode 100644
index 00000000000..c1289f56929
--- /dev/null
+++ b/spec/workers/projects/delete_branch_worker_spec.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+# rubocop: disable Gitlab/ServiceResponse
+
+require 'spec_helper'
+
+RSpec.describe Projects::DeleteBranchWorker, feature_category: :source_code_management do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+
+ let(:branch) { 'master' }
+ let(:worker) { described_class.new }
+ let(:service_result) { ServiceResponse.success(message: 'placeholder', http_status: 200) }
+
+ before do
+ allow_next_instance_of(::Branches::DeleteService) do |instance|
+ allow(instance).to receive(:execute).with(branch).and_return(service_result)
+ end
+ end
+
+ describe '#perform' do
+ context 'when the branch does not exist' do
+ let(:branch) { 'non_existent_branch_name' }
+
+ it 'does nothing' do
+ expect(::Branches::DeleteService).not_to receive(:new)
+
+ worker.perform(project.id, user.id, branch)
+ end
+ end
+
+ context 'with a non-existing project' do
+ it 'does nothing' do
+ expect(::Branches::DeleteService).not_to receive(:new)
+
+ worker.perform(non_existing_record_id, user.id, branch)
+ end
+ end
+
+ context 'with a non-existing user' do
+ it 'does nothing' do
+ expect(::Branches::DeleteService).not_to receive(:new)
+
+ worker.perform(project.id, non_existing_record_id, branch)
+ end
+ end
+
+ context 'with existing user and project' do
+ it 'calls service to delete source branch' do
+ expect_next_instance_of(::Branches::DeleteService) do |instance|
+ expect(instance).to receive(:execute).with(branch).and_return(service_result)
+ end
+
+ worker.perform(project.id, user.id, branch)
+ end
+
+ context 'when delete service returns an error' do
+ let(:service_result) { ServiceResponse.error(message: 'placeholder', http_status: status_code) }
+
+ context 'when the status code is 400' do
+ let(:status_code) { 400 }
+
+ it 'tracks and raises the exception' do
+ expect_next_instance_of(::Branches::DeleteService) do |instance|
+ expect(instance).to receive(:execute).with(branch).and_return(service_result)
+ end
+
+ expect(service_result).to receive(:track_and_raise_exception).and_call_original
+
+ expect { worker.perform(project.id, user.id, branch) }.to raise_error(StandardError)
+ end
+ end
+
+ context 'when the status code is not 400' do
+ let(:status_code) { 405 }
+
+ it 'does not track the exception' do
+ expect_next_instance_of(::Branches::DeleteService) do |instance|
+ expect(instance).to receive(:execute).with(branch).and_return(service_result)
+ end
+
+ expect(service_result).not_to receive(:track_and_raise_exception)
+
+ expect { worker.perform(project.id, user.id, branch) }.not_to raise_error
+ end
+ end
+
+ context 'when track_and_raise_delete_source_errors is disabled' do
+ let(:status_code) { 400 }
+
+ before do
+ stub_feature_flags(track_and_raise_delete_source_errors: false)
+ end
+
+ it 'does not track the exception' do
+ expect_next_instance_of(::Branches::DeleteService) do |instance|
+ expect(instance).to receive(:execute).with(branch).and_return(service_result)
+ end
+
+ expect(service_result).not_to receive(:track_and_raise_exception)
+
+ expect { worker.perform(project.id, user.id, branch) }.not_to raise_error
+ end
+ end
+ end
+ end
+
+ it_behaves_like 'an idempotent worker' do
+ let(:job_args) { [project.id, user.id, branch] }
+ end
+ end
+ # rubocop: enable Gitlab/ServiceResponse
+end
diff --git a/spec/workers/projects/import_export/parallel_project_export_worker_spec.rb b/spec/workers/projects/import_export/parallel_project_export_worker_spec.rb
new file mode 100644
index 00000000000..d3ac0a34295
--- /dev/null
+++ b/spec/workers/projects/import_export/parallel_project_export_worker_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::ImportExport::ParallelProjectExportWorker, feature_category: :importers do
+ let_it_be(:user) { create(:user) }
+
+ let(:export_job) { create(:project_export_job, :started) }
+ let(:after_export_strategy) { {} }
+ let(:job_args) { [export_job.id, user.id, after_export_strategy] }
+
+ before do
+ allow_next_instance_of(described_class) do |job|
+ allow(job).to receive(:jid) { SecureRandom.hex(8) }
+ end
+ end
+
+ describe '#perform' do
+ it_behaves_like 'an idempotent worker' do
+ it 'sets the export job status to finished' do
+ subject
+
+ expect(export_job.reload.finished?).to eq(true)
+ end
+ end
+
+ context 'when after export strategy does not exist' do
+ let(:after_export_strategy) { { 'klass' => 'InvalidStrategy' } }
+
+ it 'sets the export job status to failed' do
+ described_class.new.perform(*job_args)
+
+ expect(export_job.reload.failed?).to eq(true)
+ end
+ end
+ end
+
+ describe '.sidekiq_retries_exhausted' do
+ let(:job) { { 'args' => job_args, 'error_message' => 'Error message' } }
+
+ it 'sets export_job status to failed' do
+ described_class.sidekiq_retries_exhausted_block.call(job)
+
+ expect(export_job.reload.failed?).to eq(true)
+ end
+
+ it 'logs an error message' do
+ expect_next_instance_of(Gitlab::Export::Logger) do |logger|
+ expect(logger).to receive(:error).with(
+ hash_including(
+ message: 'Parallel project export error',
+ export_error: 'Error message'
+ )
+ )
+ end
+
+ described_class.sidekiq_retries_exhausted_block.call(job)
+ end
+ end
+end
diff --git a/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb b/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb
index 50b5b0a6e7b..f3c6434dc85 100644
--- a/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb
+++ b/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe Projects::InactiveProjectsDeletionCronWorker do
describe "#perform" do
subject(:worker) { described_class.new }
- let_it_be(:admin_user) { create(:user, :admin) }
+ let_it_be(:admin_bot) { create(:user, :admin_bot) }
let_it_be(:non_admin_user) { create(:user) }
let_it_be(:new_blank_project) do
create_project_with_statistics.tap do |project|
@@ -121,7 +121,7 @@ RSpec.describe Projects::InactiveProjectsDeletionCronWorker do
end
expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_async)
- expect(::Projects::DestroyService).to receive(:new).with(inactive_large_project, admin_user, {})
+ expect(::Projects::DestroyService).to receive(:new).with(inactive_large_project, admin_bot, {})
.at_least(:once).and_call_original
worker.perform
diff --git a/spec/workers/projects/process_sync_events_worker_spec.rb b/spec/workers/projects/process_sync_events_worker_spec.rb
index 202942ce905..a10a4797b2c 100644
--- a/spec/workers/projects/process_sync_events_worker_spec.rb
+++ b/spec/workers/projects/process_sync_events_worker_spec.rb
@@ -26,11 +26,11 @@ RSpec.describe Projects::ProcessSyncEventsWorker do
end
it 'consumes all sync events' do
- expect { perform }.to change(Projects::SyncEvent, :count).from(2).to(0)
+ expect { perform }.to change { Projects::SyncEvent.count }.from(2).to(0)
end
it 'syncs project namespace id' do
- expect { perform }.to change(Ci::ProjectMirror, :all).to contain_exactly(
+ expect { perform }.to change { Ci::ProjectMirror.all }.to contain_exactly(
an_object_having_attributes(namespace_id: group.id)
)
end
diff --git a/spec/workers/releases/create_evidence_worker_spec.rb b/spec/workers/releases/create_evidence_worker_spec.rb
index 743f2abc8a7..7e3edcfe44a 100644
--- a/spec/workers/releases/create_evidence_worker_spec.rb
+++ b/spec/workers/releases/create_evidence_worker_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Releases::CreateEvidenceWorker do
expect(service).to receive(:execute).and_call_original
end
- expect { described_class.new.perform(release.id) }.to change(Releases::Evidence, :count).by(1)
+ expect { described_class.new.perform(release.id) }.to change { Releases::Evidence.count }.by(1)
end
it 'creates a new Evidence record with pipeline' do
@@ -21,6 +21,6 @@ RSpec.describe Releases::CreateEvidenceWorker do
expect(service).to receive(:execute).and_call_original
end
- expect { described_class.new.perform(release.id, pipeline.id) }.to change(Releases::Evidence, :count).by(1)
+ expect { described_class.new.perform(release.id, pipeline.id) }.to change { Releases::Evidence.count }.by(1)
end
end
diff --git a/spec/workers/releases/manage_evidence_worker_spec.rb b/spec/workers/releases/manage_evidence_worker_spec.rb
index 886fcd346eb..0004a4f4bfb 100644
--- a/spec/workers/releases/manage_evidence_worker_spec.rb
+++ b/spec/workers/releases/manage_evidence_worker_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Releases::ManageEvidenceWorker do
specify :sidekiq_inline do
aggregate_failures do
expect(::Releases::CreateEvidenceService).not_to receive(:execute)
- expect { described_class.new.perform }.to change(Releases::Evidence, :count).by(0)
+ expect { described_class.new.perform }.to change { Releases::Evidence.count }.by(0)
end
end
end
@@ -23,7 +23,7 @@ RSpec.describe Releases::ManageEvidenceWorker do
expect(service).to receive(:execute).and_call_original
end
- expect { described_class.new.perform }.to change(Releases::Evidence, :count).by(1)
+ expect { described_class.new.perform }.to change { Releases::Evidence.count }.by(1)
end
end
diff --git a/spec/workers/repository_check/single_repository_worker_spec.rb b/spec/workers/repository_check/single_repository_worker_spec.rb
index b8db262598b..0a37a296e7a 100644
--- a/spec/workers/repository_check/single_repository_worker_spec.rb
+++ b/spec/workers/repository_check/single_repository_worker_spec.rb
@@ -98,16 +98,6 @@ RSpec.describe RepositoryCheck::SingleRepositoryWorker do
expect(project.reload.last_repository_check_failed).to eq(false)
end
- it 'does not create a wiki if the main repo does not exist at all' do
- project = create(:project, :repository)
- project.repository.raw.remove
- project.wiki.repository.raw.remove
-
- subject.perform(project.id)
-
- expect(TestEnv.storage_dir_exists?(project.repository_storage, project.wiki.path)).to eq(false)
- end
-
def create_push_event(project)
project.events.create!(action: :pushed, author_id: create(:user).id)
end
diff --git a/spec/workers/run_pipeline_schedule_worker_spec.rb b/spec/workers/run_pipeline_schedule_worker_spec.rb
index 5fa7c5d64db..4fdf6149435 100644
--- a/spec/workers/run_pipeline_schedule_worker_spec.rb
+++ b/spec/workers/run_pipeline_schedule_worker_spec.rb
@@ -3,6 +3,10 @@
require 'spec_helper'
RSpec.describe RunPipelineScheduleWorker do
+ it 'has an until_executed deduplicate strategy' do
+ expect(described_class.get_deduplicate_strategy).to eq(:until_executed)
+ end
+
describe '#perform' do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, namespace: group) }
@@ -11,6 +15,12 @@ RSpec.describe RunPipelineScheduleWorker do
let(:worker) { described_class.new }
+ around do |example|
+ travel_to(pipeline_schedule.next_run_at + 1.hour) do
+ example.run
+ end
+ end
+
context 'when a schedule not found' do
it 'does not call the Service' do
expect(Ci::CreatePipelineService).not_to receive(:new)
diff --git a/spec/workers/tasks_to_be_done/create_worker_spec.rb b/spec/workers/tasks_to_be_done/create_worker_spec.rb
index a158872273f..e884a71933e 100644
--- a/spec/workers/tasks_to_be_done/create_worker_spec.rb
+++ b/spec/workers/tasks_to_be_done/create_worker_spec.rb
@@ -24,13 +24,13 @@ RSpec.describe TasksToBeDone::CreateWorker do
.and_call_original
end
- expect { described_class.new.perform(*job_args) }.to change(Issue, :count).by(3)
+ expect { described_class.new.perform(*job_args) }.to change { Issue.count }.by(3)
end
end
include_examples 'an idempotent worker' do
it 'creates 3 task issues' do
- expect { subject }.to change(Issue, :count).by(3)
+ expect { subject }.to change { Issue.count }.by(3)
end
end
end
diff --git a/spec/workers/update_highest_role_worker_spec.rb b/spec/workers/update_highest_role_worker_spec.rb
index 0c8ee53da9a..cd127f26e95 100644
--- a/spec/workers/update_highest_role_worker_spec.rb
+++ b/spec/workers/update_highest_role_worker_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe UpdateHighestRoleWorker, :clean_gitlab_redis_shared_state do
describe '#perform' do
context 'when user is not found' do
it 'does not update or deletes any highest role', :aggregate_failures do
- expect { worker.perform(-1) }.not_to change(UserHighestRole, :count)
+ expect { worker.perform(-1) }.not_to change { UserHighestRole.count }
end
end
@@ -71,7 +71,7 @@ RSpec.describe UpdateHighestRoleWorker, :clean_gitlab_redis_shared_state do
it 'does not delete a highest role' do
user = create(:user, state: 'blocked')
- expect { worker.perform(user.id) }.not_to change(UserHighestRole, :count)
+ expect { worker.perform(user.id) }.not_to change { UserHighestRole.count }
end
end
end
diff --git a/tests.yml b/tests.yml
index 76ebb05eddb..be6cb2c84c7 100644
--- a/tests.yml
+++ b/tests.yml
@@ -23,6 +23,14 @@ mapping:
- source: lib/gitlab/usage_data_counters/(.+)\.rb
test: spec/lib/gitlab/usage_data_spec.rb
+ # See https://gitlab.com/gitlab-org/quality/engineering-productivity/master-broken-incidents/-/issues/54#note_1160811638
+ - source: lib/gitlab/ci/config/(.+)\.rb
+ test: spec/lib/gitlab/ci/yaml_processor_spec.rb
+ - source: ee/lib/gitlab/ci/config/(.+)\.rb
+ test: spec/lib/gitlab/ci/yaml_processor_spec.rb
+ - source: ee/lib/gitlab/ci/config/(.+)\.rb
+ test: ee/spec/lib/gitlab/ci/yaml_processor_spec.rb
+
# FOSS lib & tooling should map to respective spec
- source: (tooling/)?lib/(.+)\.rb
test: spec/%slib/%s_spec.rb
@@ -61,6 +69,10 @@ mapping:
- source: data/whats_new/\w*.yml
test: spec/lib/release_highlights/validator_spec.rb
+ # The documentation index page is used in this haml_lint spec
+ - source: doc/index.md
+ test: spec/haml_lint/linter/documentation_links_spec.rb
+
- source: (ee/)?app/workers/.+\.rb
test: spec/workers/every_sidekiq_worker_spec.rb
diff --git a/tooling/config/CODEOWNERS.yml b/tooling/config/CODEOWNERS.yml
index ed712f18956..07fddde056c 100644
--- a/tooling/config/CODEOWNERS.yml
+++ b/tooling/config/CODEOWNERS.yml
@@ -56,6 +56,11 @@
- 'token_access/'
- 'pipelines/'
- 'ci/runner/'
+ - 'config/events/'
+ - 'config/audit_events/'
+ - 'runner_token_expiration/'
+ - '*metadata_id_tokens*'
+ - '/app/assets/javascripts/invite_members/'
patterns:
- '%{keyword}'
diff --git a/tooling/danger/product_intelligence.rb b/tooling/danger/product_intelligence.rb
index 621a7b509b0..58e327408a1 100644
--- a/tooling/danger/product_intelligence.rb
+++ b/tooling/danger/product_intelligence.rb
@@ -4,15 +4,21 @@
module Tooling
module Danger
module ProductIntelligence
+ METRIC_DIRS = %w[lib/gitlab/usage/metrics/instrumentations ee/lib/gitlab/usage/metrics/instrumentations].freeze
APPROVED_LABEL = 'product intelligence::approved'
REVIEW_LABEL = 'product intelligence::review pending'
CHANGED_FILES_MESSAGE = <<~MSG
- For the following files, a review from the [Data team and Product Intelligence team](https://gitlab.com/groups/gitlab-org/analytics-section/product-intelligence/engineers/-/group_members?with_inherited_permissions=exclude) is recommended
- Please check the ~"product intelligence" [Service Ping guide](https://docs.gitlab.com/ee/development/service_ping/) or the [Snowplow guide](https://docs.gitlab.com/ee/development/snowplow/).
+ For the following files, a review from the [Data team and Product Intelligence team](https://gitlab.com/groups/gitlab-org/analytics-section/product-intelligence/engineers/-/group_members?with_inherited_permissions=exclude) is recommended
+ Please check the ~"product intelligence" [Service Ping guide](https://docs.gitlab.com/ee/development/service_ping/) or the [Snowplow guide](https://docs.gitlab.com/ee/development/snowplow/).
- For MR review guidelines, see the [Service Ping review guidelines](https://docs.gitlab.com/ee/development/service_ping/review_guidelines.html) or the [Snowplow review guidelines](https://docs.gitlab.com/ee/development/snowplow/review_guidelines.html).
+ For MR review guidelines, see the [Service Ping review guidelines](https://docs.gitlab.com/ee/development/service_ping/review_guidelines.html) or the [Snowplow review guidelines](https://docs.gitlab.com/ee/development/snowplow/review_guidelines.html).
- %<changed_files>s
+ %<changed_files>s
+
+ MSG
+
+ CHANGED_SCOPE_MESSAGE = <<~MSG
+ The following metrics could be affected by the modified scopes and require ~"product intelligence" review:
MSG
@@ -33,8 +39,61 @@ module Tooling
helper.labels_to_add.concat(labels_to_add) unless labels_to_add.empty?
end
+ def check_affected_scopes!
+ metric_scope_list = metric_scope_affected
+ return if metric_scope_list.empty?
+
+ warn CHANGED_SCOPE_MESSAGE + convert_to_table(metric_scope_list)
+ helper.labels_to_add.concat(missing_labels) unless missing_labels.empty?
+ end
+
private
+ def convert_to_table(items)
+ message = "Scope | Affected files |\n"
+ message += "--- | ----- |\n"
+ items.each_key do |scope|
+ affected_files = items[scope]
+ message += "`#{scope}`| `#{affected_files[0]}` |\n"
+ affected_files[1..]&.each do |file_name|
+ message += " | `#{file_name}` |\n"
+ end
+ end
+ message
+ end
+
+ def metric_scope_affected
+ select_models(helper.modified_files).each_with_object(Hash.new { |h, k| h[k] = [] }) do |file_name, matched_files|
+ helper.changed_lines(file_name).each do |mod_line, _i|
+ next unless mod_line =~ /^\+\s+scope :\w+/
+
+ affected_scope = mod_line.match(/:\w+/)
+ next if affected_scope.nil?
+
+ affected_class = File.basename(file_name, '.rb').split('_').map(&:capitalize).join
+ scope_name = "#{affected_class}.#{affected_scope[0][1..]}"
+
+ each_metric do |metric_def|
+ next unless File.read(metric_def).include?("relation { #{scope_name}")
+
+ matched_files[scope_name].push(metric_def)
+ end
+ end
+ end
+ end
+
+ def select_models(files)
+ files.select do |f|
+ f.start_with?('app/models/', 'ee/app/models/')
+ end
+ end
+
+ def each_metric(&block)
+ METRIC_DIRS.each do |dir|
+ Dir.glob(File.join(dir, '*.rb')).each(&block)
+ end
+ end
+
def missing_labels
return [] unless helper.ci?
diff --git a/tooling/danger/project_helper.rb b/tooling/danger/project_helper.rb
index a69d9049035..fbf102422aa 100644
--- a/tooling/danger/project_helper.rb
+++ b/tooling/danger/project_helper.rb
@@ -98,14 +98,14 @@ module Tooling
%r{\A((ee|jh)/)?db/(geo/)?(migrate|post_migrate)/} => [:database, :migration],
%r{\A((ee|jh)/)?db/(?!fixtures)[^/]+} => [:database],
- %r{\A((ee|jh)/)?lib/gitlab/(database|background_migration|sql|github_import)(/|\.rb)} => [:database, :backend],
+ %r{\A((ee|jh)/)?lib/gitlab/(database|background_migration|sql)(/|\.rb)} => [:database, :backend],
%r{\A(app/services/authorized_project_update/find_records_due_for_refresh_service)(/|\.rb)} => [:database, :backend],
%r{\A(app/models/project_authorization|app/services/users/refresh_authorized_projects_service)(/|\.rb)} => [:database, :backend],
%r{\A((ee|jh)/)?app/finders/} => [:database, :backend],
%r{\Arubocop/cop/migration(/|\.rb)} => :database,
%r{\A(\.ruby-version\z|\.nvmrc\z|\.tool-versions\z)} => :tooling,
- %r{\A(\.gitlab-ci\.yml\z|\.gitlab\/ci)} => :tooling,
+ %r{\A(\.gitlab-ci\.yml\z|\.gitlab/ci)} => :tooling,
%r{\A\.codeclimate\.yml\z} => :tooling,
%r{\Alefthook.yml\z} => :tooling,
%r{\A\.editorconfig\z} => :tooling,
diff --git a/tooling/danger/specs.rb b/tooling/danger/specs.rb
index 6832f7d10d1..c7baf920314 100644
--- a/tooling/danger/specs.rb
+++ b/tooling/danger/specs.rb
@@ -45,6 +45,13 @@ module Tooling
for background information and alternative options.
SUGGEST_COMMENT
+ FEATURE_CATEGORY_REGEX = /^\+.?RSpec\.describe(.+)(?!feature_category)/.freeze
+ FEATURE_CATEGORY_SUGGESTION = <<~SUGGESTION_MARKDOWN
+ Consider adding `feature_category: <feature_category_name>` for this example if it is not set already.
+ See [testing best practices](https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#feature-category-metadata).
+ SUGGESTION_MARKDOWN
+ FEATURE_CATEGORY_EXCLUDE = 'feature_category'
+
def changed_specs_files(ee: :include)
changed_files = helper.all_changed_files
folder_prefix =
@@ -64,8 +71,8 @@ module Tooling
add_suggestion(
filename,
MATCH_WITH_ARRAY_REGEX,
- MATCH_WITH_ARRAY_REPLACEMENT,
- MATCH_WITH_ARRAY_SUGGESTION
+ MATCH_WITH_ARRAY_SUGGESTION,
+ MATCH_WITH_ARRAY_REPLACEMENT
)
end
@@ -73,19 +80,30 @@ module Tooling
add_suggestion(
filename,
PROJECT_FACTORY_REGEX,
- PROJECT_FACTORY_REPLACEMENT,
- PROJECT_FACTORY_SUGGESTION
+ PROJECT_FACTORY_SUGGESTION,
+ PROJECT_FACTORY_REPLACEMENT
+ )
+ end
+
+ def add_suggestions_for_feature_category(filename)
+ add_suggestion(
+ filename,
+ FEATURE_CATEGORY_REGEX,
+ FEATURE_CATEGORY_SUGGESTION,
+ nil,
+ FEATURE_CATEGORY_EXCLUDE
)
end
private
def added_lines_matching(filename, regex)
- helper.changed_lines(filename).grep(/\A\+ /).grep(regex)
+ helper.changed_lines(filename).grep(/\A\+( )?/).grep(regex)
end
- def add_suggestion(filename, regex, replacement, comment_text)
+ def add_suggestion(filename, regex, comment_text, replacement = nil, exclude = nil)
added_lines = added_lines_matching(filename, regex)
+
return if added_lines.empty?
spec_file_lines = project_helper.file_lines(filename)
@@ -93,9 +111,14 @@ module Tooling
added_lines.each_with_object([]) do |added_line, processed_line_numbers|
line_number = find_line_number(spec_file_lines, added_line.delete_prefix('+'), exclude_indexes: processed_line_numbers)
next unless line_number
+ next if !exclude.nil? && added_line.include?(exclude)
processed_line_numbers << line_number
- text = format(comment(comment_text), suggested_line: spec_file_lines[line_number].gsub(regex, replacement))
+
+ suggested_line = spec_file_lines[line_number]
+ suggested_line = suggested_line.gsub(regex, replacement) unless replacement.nil?
+
+ text = format(comment(comment_text), suggested_line: suggested_line)
markdown(text, file: filename, line: line_number.succ)
end
end
diff --git a/tooling/danger/stable_branch.rb b/tooling/danger/stable_branch.rb
new file mode 100644
index 00000000000..6c0b94b4f06
--- /dev/null
+++ b/tooling/danger/stable_branch.rb
@@ -0,0 +1,138 @@
+# frozen_string_literal: true
+
+module Tooling
+ module Danger
+ module StableBranch
+ VersionApiError = Class.new(StandardError)
+
+ STABLE_BRANCH_REGEX = %r{\A(?<version>\d+-\d+)-stable-ee\z}.freeze
+
+ # rubocop:disable Lint/MixedRegexpCaptureTypes
+ VERSION_REGEX = %r{
+ \A(?<major>\d+)
+ \.(?<minor>\d+)
+ (\.(?<patch>\d+))?
+ (-(?<rc>rc(?<rc_number>\d*)))?
+ (-\h+\.\h+)?
+ (-ee|\.ee\.\d+)?\z
+ }x.freeze
+ # rubocop:enable Lint/MixedRegexpCaptureTypes
+
+ MAINTENANCE_POLICY_URL = 'https://docs.gitlab.com/ee/policy/maintenance.html'
+
+ MAINTENANCE_POLICY_MESSAGE = <<~MSG
+ See the [release and maintenance policy](#{MAINTENANCE_POLICY_URL}) for more information.
+ MSG
+
+ FEATURE_ERROR_MESSAGE = <<~MSG
+ This MR includes the `type::feature` label. Features do not qualify for patch releases. #{MAINTENANCE_POLICY_MESSAGE}
+ MSG
+
+ BUG_ERROR_MESSAGE = <<~MSG
+ This branch is meant for backporting bug fixes. If this MR qualifies please add the `type::bug` label. #{MAINTENANCE_POLICY_MESSAGE}
+ MSG
+
+ VERSION_ERROR_MESSAGE = <<~MSG
+ Patches are only being accepted on the most recent 3 minor versions of GitLab. #{MAINTENANCE_POLICY_MESSAGE}
+ MSG
+
+ FAILED_VERSION_REQUEST_MESSAGE = <<~MSG
+ There was a problem checking if this is a qualified version for backporting. Re-running this job may fix the problem.
+ MSG
+
+ # rubocop:disable Style/SignalException
+ def check!
+ return unless stable_target_branch && !helper.security_mr?
+
+ fail FEATURE_ERROR_MESSAGE if has_feature_label?
+ fail BUG_ERROR_MESSAGE unless has_bug_label?
+ fail VERSION_ERROR_MESSAGE unless targeting_patchable_version?
+ end
+ # rubocop:enable Style/SignalException
+
+ private
+
+ def stable_target_branch
+ helper.mr_target_branch.match(STABLE_BRANCH_REGEX)
+ end
+
+ def has_feature_label?
+ helper.mr_has_labels?('type::feature')
+ end
+
+ def has_bug_label?
+ helper.mr_has_labels?('type::bug')
+ end
+
+ def targeting_patchable_version?
+ raise VersionApiError if last_three_minor_versions.empty?
+
+ last_three_minor_versions.include?(targeted_version)
+ rescue VersionApiError
+ # don't fail the job since we do not know the recent versions
+ warn FAILED_VERSION_REQUEST_MESSAGE
+ true
+ end
+
+ def last_three_minor_versions
+ return [] unless versions
+
+ current_version = versions.first.match(VERSION_REGEX)
+ version_1 = previous_minor_version(current_version)
+ version_2 = previous_minor_version(version_1)
+
+ [
+ version_to_minor_string(current_version),
+ version_to_minor_string(version_1),
+ version_to_minor_string(version_2)
+ ]
+ end
+
+ def targeted_version
+ stable_target_branch[1].tr('-', '.')
+ end
+
+ def versions(page = 1)
+ version_api_endpoint = "https://version.gitlab.com/api/v1/versions?per_page=50&page=#{page}"
+ response = HTTParty.get(version_api_endpoint) # rubocop:disable Gitlab/HTTParty
+
+ raise VersionApiError unless response.success?
+
+ version_list = response.parsed_response.map { |v| v['version'] } # rubocop:disable Rails/Pluck
+
+ version_list.sort_by { |v| Gem::Version.new(v) }.reverse
+ end
+
+ def previous_minor_version(version)
+ previous_minor = version[:minor].to_i - 1
+
+ return "#{version[:major]}.#{previous_minor}".match(VERSION_REGEX) if previous_minor >= 0
+
+ fetch_last_minor_version_for_major(version[:major].to_i - 1)
+ end
+
+ def fetch_last_minor_version_for_major(major)
+ page = 1
+ last_minor_version = nil
+
+ while last_minor_version.nil?
+ last_minor_version = versions(page).find do |version|
+ version.split('.').first.to_i == major
+ end
+
+ break if page > 10
+
+ page += 1
+ end
+
+ raise VersionApiError if last_minor_version.nil?
+
+ last_minor_version.match(VERSION_REGEX)
+ end
+
+ def version_to_minor_string(version)
+ "#{version[:major]}.#{version[:minor]}"
+ end
+ end
+ end
+end
diff --git a/tooling/danger/user_types.rb b/tooling/danger/user_types.rb
new file mode 100644
index 00000000000..8320c43ae93
--- /dev/null
+++ b/tooling/danger/user_types.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Tooling
+ module Danger
+ module UserTypes
+ FILE_PATH = "app/models/concerns/has_user_type.rb"
+ BOT_USER_TYPES_CHANGE_INDICATOR_REGEX = %r{BOT_USER_TYPES}.freeze
+ BOT_USER_TYPES_CHANGED_WARNING = <<~MSG
+ You are changing BOT_USER_TYPES in `app/models/concerns/has_user_type.rb`.
+ If you are adding or removing new bots, remember to update the `active_billable_users` index with the new value.
+ If the bot is not billable, remember to make sure that it's not counted as a billable user.
+ MSG
+
+ def bot_user_types_change_warning
+ return unless impacted?
+
+ warn BOT_USER_TYPES_CHANGED_WARNING if bot_user_types_impacted?
+ end
+
+ private
+
+ def impacted?
+ helper.modified_files.include?(FILE_PATH)
+ end
+
+ def bot_user_types_impacted?
+ helper.changed_lines(FILE_PATH).any? { |change| change =~ BOT_USER_TYPES_CHANGE_INDICATOR_REGEX }
+ end
+ end
+ end
+end
diff --git a/tooling/docs/deprecation_handling.rb b/tooling/docs/deprecation_handling.rb
index a620eac4c91..bcdf73e0044 100644
--- a/tooling/docs/deprecation_handling.rb
+++ b/tooling/docs/deprecation_handling.rb
@@ -22,7 +22,7 @@ module Docs
entries = source_file_paths.flat_map do |file|
YAML.load_file(file)
end
- entries = entries.sort_by { |d| d["name"] }
+ entries = entries.sort_by { |d| d["title"] }
milestones = entries.map { |entry| entry[milestone_key_name] }.uniq
milestones = VersionSorter.rsort(milestones)
diff --git a/tooling/lib/tooling/helm3_client.rb b/tooling/lib/tooling/helm3_client.rb
index d83dbeac76b..d4e7faa802e 100644
--- a/tooling/lib/tooling/helm3_client.rb
+++ b/tooling/lib/tooling/helm3_client.rb
@@ -35,10 +35,7 @@ module Tooling
end
def delete(release_name:)
- run_command([
- 'uninstall',
- release_name
- ])
+ run_command(['uninstall', release_name])
end
private
diff --git a/tooling/quality/test_level.rb b/tooling/quality/test_level.rb
index d630ffd5432..29da7dddd03 100644
--- a/tooling/quality/test_level.rb
+++ b/tooling/quality/test_level.rb
@@ -19,6 +19,7 @@ module Quality
bin
channels
config
+ contracts
db
dependencies
elastic
diff --git a/vendor/gems/attr_encrypted/README.md b/vendor/gems/attr_encrypted/README.md
index 87ac7219a92..1a332a2edd5 100644
--- a/vendor/gems/attr_encrypted/README.md
+++ b/vendor/gems/attr_encrypted/README.md
@@ -451,7 +451,7 @@ When storing your encrypted data, please consider the length requirements of the
It is advisable to also store metadata regarding the circumstances of your encrypted data. Namely, you should store information about the key used to encrypt your data, as well as the algorithm. Having this metadata with every record will make key rotation and migrating to a new algorithm signficantly easier. It will allow you to continue to decrypt old data using the information provided in the metadata and new data can be encrypted using your new key and algorithm of choice.
#### Enforcing the IV as a nonce
-On a related note, most algorithms require that your IV be unique for every record and key combination. You can enforce this using composite unique indexes on your IV and encryption key name/id column. [RFC 5084](https://tools.ietf.org/html/rfc5084#section-1.5)
+On a related note, most algorithms require that your IV be unique for every record and key combination. You can enforce this using composite unique indexes on your IV and encryption key name/id column. [RFC 5084](https://www.rfc-editor.org/rfc/rfc5084#section-1.5)
#### Unique key per record
Lastly, while the `:per_attribute_iv_and_salt` mode is more secure than `:per_attribute_iv` mode because it uses a unique key per record, it uses a PBKDF function which introduces a huge performance hit (175x slower by my benchmarks). There are other ways of deriving a unique key per record that would be much faster.
diff --git a/vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb b/vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb
index fed0e11080f..47a9b676f1d 100644
--- a/vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb
+++ b/vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb
@@ -16,14 +16,22 @@ module Bundler::Checksum::Command
.send(:compact_index_client)
.instance_variable_get(:@cache)
- seen = []
Bundler.definition.resolve.sort_by(&:name).each do |spec|
next unless spec.source.is_a?(Bundler::Source::Rubygems)
+ spec_identifier = "#{spec.name}==#{spec.version}"
- next if seen.include?(spec.name)
- seen << spec.name
+ previous_checksum = previous_checksums.select do |checksum|
+ checksum[:name] == spec.name && checksum[:version] == spec.version.to_s
+ end
+
+ if !previous_checksum.empty?
+ $stderr.puts "Using #{spec_identifier}"
+ checksums += previous_checksum
+
+ next
+ end
- $stderr.puts "Adding #{spec.name}==#{spec.version}"
+ $stderr.puts "Adding #{spec_identifier}"
compact_index_dependencies = compact_index_cache.dependencies(spec.name).select { |item| item.first == spec.version.to_s }
@@ -54,6 +62,15 @@ module Bundler::Checksum::Command
private
+ def previous_checksums
+ @previous_checksums ||=
+ if File.exist?(checksum_file)
+ ::Bundler::Checksum.checksums_from_file
+ else
+ []
+ end
+ end
+
def checksum_file
::Bundler::Checksum.checksum_file
end
diff --git a/vendor/gems/kubeclient/.gitignore b/vendor/gems/kubeclient/.gitignore
new file mode 100644
index 00000000000..a0afe33a553
--- /dev/null
+++ b/vendor/gems/kubeclient/.gitignore
@@ -0,0 +1,16 @@
+/.bundle/
+/.yardoc
+/Gemfile.lock
+/_yardoc/
+/coverage/
+/doc/
+/pkg/
+/spec/reports/
+/tmp/
+*.bundle
+*.so
+*.o
+*.a
+mkmf.log
+*.idea*
+/Gemfile.dev.rb
diff --git a/vendor/gems/kubeclient/CHANGELOG.md b/vendor/gems/kubeclient/CHANGELOG.md
new file mode 100644
index 00000000000..3237d4a3c2d
--- /dev/null
+++ b/vendor/gems/kubeclient/CHANGELOG.md
@@ -0,0 +1,247 @@
+# Changelog
+
+Notable changes to this project will be documented in this file.
+The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
+Kubeclient release versioning follows [SemVer](https://semver.org/).
+
+## 4.9.3 — 2021-03-23
+
+### Fixed
+
+- VULNERABILITY FIX: Previously, whenever kubeconfig did not define custom CA
+ (normal situation for production clusters with public domain and certificate!),
+ `Config` was returning ssl_options[:verify_ssl] hard-coded to `VERIFY_NONE` :-(
+
+ Assuming you passed those ssl_options to Kubeclient::Client, this means that
+ instead of checking server's certificate against your system CA store,
+ it would accept ANY certificate, allowing easy man-in-the middle attacks.
+
+ This is especially dangerous with user/password or token credentials
+ because MITM attacker could simply steal those credentials to the cluster
+ and do anything you could do on the cluster.
+
+ This was broken IN ALL RELEASES MADE BEFORE 2022, ever since
+ [`Kubeclient::Config` was created](https://github.com/ManageIQ/kubeclient/pull/127/files#diff-32e70f2f6781a9e9c7b83ae5e7eaf5ffd068a05649077fa38f6789e72f3de837R41-R48).
+
+- Bug fix: kubeconfig `insecure-skip-tls-verify` field was ignored.
+ When kubeconfig did define custom CA, `Config` was returning hard-coded `VERIFY_PEER`.
+
+ Now we honor it, return `VERIFY_NONE` iff kubeconfig has explicit
+ `insecure-skip-tls-verify: true`, otherwise `VERIFY_PEER`.
+
+- `Config`: fixed parsing of `certificate-authority` file containing concatenation of
+ several certificates. Previously, server's cert was checked against only first CA cert,
+ resulting in possible "certificate verify failed" errors.
+
+ An important use case is a chain of root & intermediate cert(s) - necessary when cluster's CA
+ itself is signed by another custom CA.
+ But also helps when you simply concatenate independent certs. (#461, #552)
+
+ - Still broken (#460): inline `certificate-authority-data` is still parsed using `add_cert`
+ method that handles only one cert.
+
+These don't affect code that supplies `Client` parameters directly,
+only code that uses `Config`.
+
+## 4.9.2 — 2021-05-30
+
+### Added
+- Ruby 3.0 compatibility (#500, #505).
+
+### Removed
+- Reduce .gem size by dropping test/ directory, it's useless at run time (#502).
+
+## 4.9.1 — 2020-08-31
+### Fixed
+- Now should work with apiserver deployed not at root of domain but a sub-path,
+ which is standard with Rancher.
+ Notably, `create_...` methods were sending bad apiVersion and getting 400 error.
+ (#457, hopefully fixes #318, #418 and https://gitlab.com/gitlab-org/gitlab/-/issues/22043)
+
+## 4.9.0 - 2020-08-03
+### Added
+- Support for `user: exec` credential plugins using TLS client auth (#453)
+
+## 4.8.0 — 2020-07-03
+
+### Added
+- Support for server-side apply (#448).
+
+### Fixed
+- Declared forgotten dependency on jsonpath, needed for `gcp` provider with `cmd-path` (#450).
+
+## 4.7.0 — 2020-06-14
+
+### Fixed
+- Ruby 2.7 compatibility: bumped minimum recursive-open-struct to one that works on 2.7 (#439).
+- Ruby 2.7 warnings (#433, #438).
+- Improved watch documentation, including behavior planned to change in 5.0.0 (#436).
+
+### Added
+- Google Application Default Credentials: Added `userinfo.email` to requested scopes, which is necessary for RBAC policies (#441).
+
+## 4.6.0 — 2019-12-30
+
+### Fixed
+- AmazonEksCredentials was sometimes leaving base64 padding that IAM auth of the EKS cluster rejects. Now padding is always stripped. (#424, #423)
+
+### Added
+- Allow calling `watch_foos` methods with a block, simpler to use and guarantees closing the connection. (#425)
+
+- Support `limitBytes` query parameter for `get_pod_log`. (#426)
+
+## 4.5.0 — 2019-09-27
+
+### Added
+- Support `:resourceVersion` parameter in `get_foos` methods (similar to existing support in `watch_foos` methods). (#420)
+
+- Relax dependency on `http` gem to allow both 3.x and 4.x. (#413)
+
+## 4.4.0 — 2019-05-03
+
+### Added
+- GCP configs with `user[auth-provider][name] == 'gcp'` will execute credential plugin (normally the `gcloud config config-helper` subcommand) when the config specifies it in `cmd-path`, `cmd-args` fields (similar to `exec` support). This code path works without `googleauth` gem. Otherwise, `GoogleApplicationDefaultCredentials` path will be tried as before. (#410)
+- `AmazonEksCredentials` helper for obtaining a token to authenticate against Amazon EKS. This is not currently integrated in `Config`, you will need to invoke it yourself. You'll need some aws gems that Kubeclient _does not_ include. (#404, #406)
+
+### Changed
+- OpenID Connect tokens which cannot be validaded because we cannot identify the key they were signed with will be considered expired and refreshed as usual. (#407)
+
+## 4.3.0 — 2019-03-03
+
+### Changed
+- `GoogleApplicationDefaultCredentials` will now automatically be used by `Config` if the `user[auth-provider][name] == 'gcp'` in the provided context. Note that `user[exec]` is checked first in anticipation of this functionality being added to GCP sometime in the future. Kubeclient _does not_ include the required `googleauth` gem, so you will need to include it in your calling application. (#394)
+
+### Added
+- OpenID Connect credentials will automatically be used if the `user[auth-provider][name] == 'oidc'` in the provided context. Note that `user[exec]` is checked first. Kubeclient _does not_ include the required `openid_connect` gem, so you will need to include it in your calling application. (#396)
+
+- Support for `json_patch_#{entity}` and `merge_patch_#{entity}`. `patch_#{entity}` will continue to use strategic merge patch. (#390)
+
+## 4.2.2 — 2019-01-09
+
+### Added
+- New `http_max_redirects` option (#374).
+
+### Changed
+- Default max redirects for watch increased from 4 to 10, to match other verbs (#374).
+
+## 4.2.1 — 2018-12-26
+
+### Fixed
+- For resources that contain dashes in name, there will be an attempt to resolve the method name based on singular name prefix or by replacing the dash in names with underscores (#383).
+
+## 4.2.0 — 2018-12-20
+
+### Added
+- Support `user: exec: ...` credential plugins like in Go client (#363, #375).
+
+### Security
+- Really made `Kubeclient::Config.new(data, nil)` prevent external file lookups. (#372)
+ README documented this since 3.1.1 (#334) but alas that was a lie — absolute paths always worked.
+ Now this also prevents credential plugin execution.
+
+ Even in this mode, using config from untrusted sources is not recommended.
+
+This release included all changes up to 4.1.1, but NOT 4.1.2 which was branched off later (4.2.1 does include same fix).
+
+## 4.1.2 — 2018-12-26
+
+### Fixed
+- For resources that contain dashes in name, there will be an attempt to resolve the method name based on singular name prefix or by replacing the dash in names with underscores (#382).
+
+## 4.1.1 — 2018-12-17
+
+### Fixed
+- Fixed method names for non-suffix plurals such as y -> ies (#377).
+
+## 4.1.0 — 2018-11-28 — REGRESSION
+
+This version broke method names where plural is not just adding a suffix, notably y -> ies (bug #376).
+
+### Fixed
+- Support custom resources with lowercase `kind` (#361).
+- `create_security_context_constraint` now works (#366).
+- `get_security_context_constraints.kind`, `get_endpoints.kind` are now plural as in kubernetes (#366).
+
+### Added
+- Add support for retrieving large lists of objects in chunks (#356).
+
+## 4.0.0 — 2018-07-23
+
+### Removed
+- Bumped officially supported kubernetes versions to >= 1.3.
+- Specifically `proxy_url` no longer works for <= 1.2 (#323).
+
+### Fixed
+- `proxy_url` now works for kubernetes 1.10 and later (#323).
+
+### Changed
+- Switched `http` gem dependency from 2.y to 3.y (#321).
+
+## 3.1.2 — 2018-06-11
+
+### Fixed
+- Fixed `Kubeclient::Config.read` regression, no longer crashes on YAML timestamps (#338).
+
+## 3.1.1 - 2018-06-01 — REGRESSION
+
+In this version `Kubeclient::Config.read` raises Psych::DisallowedClass on legal yaml configs containing a timestamp, for example gcp access-token expiry (bug #337).
+
+### Security
+- Changed `Kubeclient::Config.read` to use `YAML.safe_load` (#334).
+
+ Previously, could deserialize arbitrary ruby classes. The risk depends on ruby classes available in the application; sometimes a class may have side effects - up to arbitrary code execution - when instantiated and/or built up with `x[key] = value` during YAML parsing.
+
+ Despite this fix, using config from untrusted sources is not recommended.
+
+## 3.1.0 - 2018-05-27
+
+### Fixed
+- Fixed watch `.finish` sometimes caused `HTTP::ConnectionError` exception from the reading loop (#315).
+
+### Added
+- `get_pod_log` now has `timestamps`, `since_time` (#319) and `tail_lines` (#326) params.
+- `Kubeclient::Config::Context#namespace` now set, if present in kubeconfig file (#308).
+- Improved README directions for authenticating within a kubernetes cluster (#316).
+- `Kubeclient::GoogleApplicationDefaultCredentials` helper for Google application default credentials (#213). Needs `googleauth` gem.
+- New `as: :parsed` and `as: :parsed_symbolized` formats (#306).
+- Allow setting default `as:` format for the whole client (#299, #305).
+- Relaxed `recursive-open-struct` dependency to allow 1.1+ as well (#313).
+
+## 3.0.0 - 2018-02-04
+### Removed
+- Dropped entity classes (`Kubeclient::Pod` etc.), only `Kubeclient::Resource` exists now (#292, #288).
+- Ruby 2.0, 2.1 no longer supported (#253, #291).
+
+### Fixed
+- Added missing singular `get_security_context_constraint`, fixed `get_security_context_constraints` to mean plural (#261).
+- Fixed `@http_proxy_uri` undefined warning (#261).
+- Documentation fixes & improvements (#225, #229, #243, #296).
+
+### Added
+- `delete_options:` parameter to `delete_*` methods, useful for cascade delete (#267).
+- `as: :raw` option for watch (#285).
+- Now raises `Kubeclient::HttpError`. Rescuing `KubeException` still works but is deprecated. (#195, #288)
+ - 404 error raise `Kubeclient::ResourceNotFoundError`, a subclass of `HttpError` (#233).
+- Include request info in exception message (#221).
+- Ruby 2.4 and 2.5 are now supported & tested (#247, #295).
+
+### Changed
+- `Kubeclient::Config#context(nonexistent_context_name)` raises `KeyError` instead of `RuntimeError`.
+- `update_*`, `delete_*`, `patch_*` now all return `RecursiveOpenStruct` consistently (#290).
+- Many dependencies bumped (#204, #231, #253, #269).
+
+## 2.5.2 - 2018-02-04
+- Watch results are now `RecursiveOpenStruct` inside arrays too (#279).
+- Fixed watch `.finish` sometimes caused `Errno::EBADF` exception from the reading loop (#280).
+- Easing dependency version (#287, #301)
+
+## 2.5.1 - 2017-10-12
+No changes since 2.5.0, fixed packaging mistake.
+
+## [2.5.0 - 2017-10-12 was YANKED]
+
+### Added
+
+- `as: raw` option for `get_*` methods returning a string (#262 via #271).
+
+## 2.4.0 - 2017-05-10
diff --git a/vendor/gems/kubeclient/Gemfile b/vendor/gems/kubeclient/Gemfile
new file mode 100644
index 00000000000..da50b39459f
--- /dev/null
+++ b/vendor/gems/kubeclient/Gemfile
@@ -0,0 +1,7 @@
+source 'https://rubygems.org'
+
+dev_gemfile = File.expand_path('Gemfile.dev.rb', __dir__)
+eval_gemfile(dev_gemfile) if File.exist?(dev_gemfile)
+
+# Specify your gem's dependencies in kubeclient.gemspec
+gemspec
diff --git a/vendor/gems/kubeclient/LICENSE.txt b/vendor/gems/kubeclient/LICENSE.txt
new file mode 100644
index 00000000000..c79ef416c4b
--- /dev/null
+++ b/vendor/gems/kubeclient/LICENSE.txt
@@ -0,0 +1,22 @@
+Copyright (c) 2014 Alissa Bonas
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/gems/kubeclient/README.md b/vendor/gems/kubeclient/README.md
new file mode 100644
index 00000000000..6cf0fb67293
--- /dev/null
+++ b/vendor/gems/kubeclient/README.md
@@ -0,0 +1,889 @@
+# Kubeclient
+
+[![Gem Version](https://badge.fury.io/rb/kubeclient.svg)](http://badge.fury.io/rb/kubeclient)
+[![Build Status](https://travis-ci.org/abonas/kubeclient.svg?branch=master)](https://travis-ci.org/abonas/kubeclient)
+[![Code Climate](http://img.shields.io/codeclimate/github/abonas/kubeclient.svg)](https://codeclimate.com/github/abonas/kubeclient)
+
+A Ruby client for Kubernetes REST api.
+The client supports GET, POST, PUT, DELETE on all the entities available in kubernetes in both the core and group apis.
+The client currently supports Kubernetes REST api version v1.
+To learn more about groups and versions in kubernetes refer to [k8s docs](https://kubernetes.io/docs/api/)
+
+## VULNERABILITYâ—
+
+If you use `Kubeclient::Config`, all gem versions released before 2022 could return incorrect `ssl_options[:verify_ssl]`,
+endangering your connection and cluster credentials.
+See [latest CHANGELOG.md](https://github.com/ManageIQ/kubeclient/blob/master/CHANGELOG.md) for details and which versions got a fix.
+Open an issue if you want a backport to another version.
+
+## Installation
+
+Add this line to your application's Gemfile:
+
+```ruby
+gem 'kubeclient'
+```
+
+And then execute:
+
+```Bash
+bundle
+```
+
+Or install it yourself as:
+
+```Bash
+gem install kubeclient
+```
+
+## Usage
+
+Initialize the client:
+
+```ruby
+client = Kubeclient::Client.new('http://localhost:8080/api/', "v1")
+```
+
+Or without specifying version (it will be set by default to "v1")
+
+```ruby
+client = Kubeclient::Client.new('http://localhost:8080/api/')
+```
+
+For A Group Api:
+
+```ruby
+client = Kubeclient::Client.new('http://localhost:8080/apis/batch', 'v1')
+```
+
+Another option is to initialize the client with URI object:
+
+```ruby
+uri = URI::HTTP.build(host: "somehostname", port: 8080)
+client = Kubeclient::Client.new(uri)
+```
+
+### SSL
+
+It is also possible to use https and configure ssl with:
+
+```ruby
+ssl_options = {
+ client_cert: OpenSSL::X509::Certificate.new(File.read('/path/to/client.crt')),
+ client_key: OpenSSL::PKey::RSA.new(File.read('/path/to/client.key')),
+ ca_file: '/path/to/ca.crt',
+ verify_ssl: OpenSSL::SSL::VERIFY_PEER
+}
+client = Kubeclient::Client.new(
+ 'https://localhost:8443/api/', "v1", ssl_options: ssl_options
+)
+```
+
+As an alternative to the `ca_file` it's possible to use the `cert_store`:
+
+```ruby
+cert_store = OpenSSL::X509::Store.new
+cert_store.add_cert(OpenSSL::X509::Certificate.new(ca_cert_data))
+ssl_options = {
+ cert_store: cert_store,
+ verify_ssl: OpenSSL::SSL::VERIFY_PEER
+}
+client = Kubeclient::Client.new(
+ 'https://localhost:8443/api/', "v1", ssl_options: ssl_options
+)
+```
+
+For testing and development purpose you can disable the ssl check with:
+
+```ruby
+ssl_options = { verify_ssl: OpenSSL::SSL::VERIFY_NONE }
+client = Kubeclient::Client.new(
+ 'https://localhost:8443/api/', 'v1', ssl_options: ssl_options
+)
+```
+
+### Authentication
+
+If you are using basic authentication or bearer tokens as described
+[here](https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/authentication.md) then you can specify one
+of the following:
+
+```ruby
+auth_options = {
+ username: 'username',
+ password: 'password'
+}
+client = Kubeclient::Client.new(
+ 'https://localhost:8443/api/', 'v1', auth_options: auth_options
+)
+```
+
+or
+
+```ruby
+auth_options = {
+ bearer_token: 'MDExMWJkMjItOWY1Ny00OGM5LWJlNDEtMjBiMzgxODkxYzYz'
+}
+client = Kubeclient::Client.new(
+ 'https://localhost:8443/api/', 'v1', auth_options: auth_options
+)
+```
+
+or
+
+```ruby
+auth_options = {
+ bearer_token_file: '/path/to/token_file'
+}
+client = Kubeclient::Client.new(
+ 'https://localhost:8443/api/', 'v1', auth_options: auth_options
+)
+```
+
+#### Inside a Kubernetes cluster
+
+The [recommended way to locate the API server](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod) within the pod is with the `kubernetes.default.svc` DNS name, which resolves to a Service IP which in turn will be routed to an API server.
+
+The recommended way to authenticate to the API server is with a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/). kube-system associates a pod with a service account and a bearer token for that service account is placed into the filesystem tree of each container in that pod at `/var/run/secrets/kubernetes.io/serviceaccount/token`.
+
+If available, a certificate bundle is placed into the filesystem tree of each container at `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt`, and should be used to verify the serving certificate of the API server.
+
+For example:
+
+```ruby
+auth_options = {
+ bearer_token_file: '/var/run/secrets/kubernetes.io/serviceaccount/token'
+}
+ssl_options = {}
+if File.exist?("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt")
+ ssl_options[:ca_file] = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
+end
+client = Kubeclient::Client.new(
+ 'https://kubernetes.default.svc',
+ 'v1',
+ auth_options: auth_options,
+ ssl_options: ssl_options
+)
+```
+
+Finally, the default namespace to be used for namespaced API operations is placed in a file at `/var/run/secrets/kubernetes.io/serviceaccount/namespace` in each container. It is recommended that you use this namespace when issuing API commands below.
+
+```ruby
+namespace = File.read('/var/run/secrets/kubernetes.io/serviceaccount/namespace')
+```
+You can find information about tokens in [this guide](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod) and in [this reference](http://kubernetes.io/docs/admin/authentication/).
+
+### Non-blocking IO
+
+You can also use kubeclient with non-blocking sockets such as Celluloid::IO, see [here](https://github.com/httprb/http/wiki/Parallel-requests-with-Celluloid%3A%3AIO)
+for details. For example:
+
+```ruby
+require 'celluloid/io'
+socket_options = {
+ socket_class: Celluloid::IO::TCPSocket,
+ ssl_socket_class: Celluloid::IO::SSLSocket
+}
+client = Kubeclient::Client.new(
+ 'https://localhost:8443/api/', 'v1', socket_options: socket_options
+)
+```
+
+This affects only `.watch_*` sockets, not one-off actions like `.get_*`, `.delete_*` etc.
+
+### Proxies
+
+You can also use kubeclient with an http proxy server such as tinyproxy. It can be entered as a string or a URI object.
+For example:
+```ruby
+proxy_uri = URI::HTTP.build(host: "myproxyhost", port: 8443)
+client = Kubeclient::Client.new(
+ 'https://localhost:8443/api/', http_proxy_uri: proxy_uri
+)
+```
+
+### Redirects
+
+You can optionally not allow redirection with kubeclient. For example:
+
+```ruby
+client = Kubeclient::Client.new(
+ 'https://localhost:8443/api/', http_max_redirects: 0
+)
+```
+
+### Timeouts
+
+Watching configures the socket to never time out (however, sooner or later all watches terminate).
+
+One-off actions like `.get_*`, `.delete_*` have a configurable timeout:
+```ruby
+timeouts = {
+ open: 10, # unit is seconds
+ read: nil # nil means never time out
+}
+client = Kubeclient::Client.new(
+ 'https://localhost:8443/api/', timeouts: timeouts
+)
+```
+
+Default timeouts match `Net::HTTP` and `RestClient`, which unfortunately depends on ruby version:
+- open was infinite up to ruby 2.2, 60 seconds in 2.3+.
+- read is 60 seconds.
+
+If you want ruby-independent behavior, always specify `:open`.
+
+### Discovery
+
+Discovery from the kube-apiserver is done lazily on method calls so it would not change behavior.
+
+It can also be done explicitly:
+
+```ruby
+client = Kubeclient::Client.new('http://localhost:8080/api', 'v1')
+client.discover
+```
+
+It is possible to check the status of discovery
+
+```ruby
+unless client.discovered
+ client.discover
+end
+```
+
+### Kubeclient::Config
+
+If you've been using `kubectl` and have a `.kube/config` file (possibly referencing other files in fields such as `client-certificate`), you can auto-populate a config object using `Kubeclient::Config`:
+
+```ruby
+# assuming $KUBECONFIG is one file, won't merge multiple like kubectl
+config = Kubeclient::Config.read(ENV['KUBECONFIG'] || '/path/to/.kube/config')
+```
+
+This will lookup external files; relative paths will be resolved relative to the file's directory, if config refers to them with relative path.
+This includes external [`exec:` credential plugins][exec] to be executed.
+
+[exec]: https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins
+
+You can also construct `Config` directly from nested data. For example if you have JSON or YAML config data in a variable:
+
+```ruby
+config = Kubeclient::Config.new(YAML.safe_load(yaml_text), nil)
+# or
+config = Kubeclient::Config.new(JSON.parse(json_text), nil)
+```
+
+The 2nd argument is a base directory for finding external files, if config refers to them with relative path.
+Setting it to `nil` disables file lookups, and `exec:` execution - such configs will raise an exception. (A config can be self-contained by using inline fields such as `client-certificate-data`.)
+
+To create a client based on a Config object:
+
+```ruby
+# default context according to `current-context` field:
+context = config.context
+# or to use a specific context, by name:
+context = config.context('default/192-168-99-100:8443/system:admin')
+
+Kubeclient::Client.new(
+ context.api_endpoint,
+ 'v1',
+ ssl_options: context.ssl_options,
+ auth_options: context.auth_options
+)
+```
+
+
+#### Amazon EKS Credentials
+
+On Amazon EKS by default the authentication method is IAM. When running kubectl a temporary token is generated by shelling out to
+the aws-iam-authenticator binary which is sent to authenticate the user.
+See [aws-iam-authenticator](https://github.com/kubernetes-sigs/aws-iam-authenticator).
+To replicate that functionality, the `Kubeclient::AmazonEksCredentials` class can accept a set of IAM credentials and
+contains a helper method to generate the authentication token for you.
+
+This requires a set of gems which are _not_ included in
+`kubeclient` dependencies (`aws-sigv4`) so you should add them to your bundle.
+You will also require either the `aws-sdk` v2 or `aws-sdk-core` v3 gems to generate the required `Aws:Credentials` object to pass to this method.
+
+To obtain a token:
+
+```ruby
+require 'aws-sdk-core'
+# Use keys
+credentials = Aws::Credentials.new(access_key, secret_key)
+# Or a profile
+credentials = Aws::SharedCredentials.new(profile_name: 'default').credentials
+
+auth_options = {
+ bearer_token: Kubeclient::AmazonEksCredentials.token(credentials, eks_cluster_name)
+}
+client = Kubeclient::Client.new(
+ eks_cluster_https_endpoint, 'v1', auth_options: auth_options
+)
+```
+
+Note that this returns a token good for one minute. If your code requires authorization for longer than that, you should plan to
+acquire a new one, see [How to manually renew](#how-to-manually-renew-expired-credentials) section.
+
+#### Google GCP credential plugin
+
+If kubeconfig file has `user: {auth-provider: {name: gcp, cmd-path: ..., cmd-args: ..., token-key: ...}}`, the command will be executed to obtain a token.
+(Normally this would be a `gcloud config config-helper` command.)
+
+Note that this returns an expiring token. If your code requires authorization for a long time, you should plan to acquire a new one, see [How to manually renew](#how-to-manually-renew-expired-credentials) section.
+
+#### Google's Application Default Credentials
+
+On Google Compute Engine, Google App Engine, or Google Cloud Functions, as well as `gcloud`-configured systems
+with [application default credentials](https://developers.google.com/identity/protocols/application-default-credentials),
+kubeclient can use `googleauth` gem to authorize.
+
+This requires the [`googleauth` gem](https://github.com/google/google-auth-library-ruby) that is _not_ included in
+`kubeclient` dependencies so you should add it to your bundle.
+
+If you use `Config.context(...).auth_options` and the kubeconfig file has `user: {auth-provider: {name: gcp}}`, but does not contain `cmd-path` key, kubeclient will automatically try this (raising LoadError if you don't have `googleauth` in your bundle).
+
+Or you can obtain a token manually:
+
+```ruby
+require 'googleauth'
+
+auth_options = {
+ bearer_token: Kubeclient::GoogleApplicationDefaultCredentials.token
+}
+client = Kubeclient::Client.new(
+ 'https://localhost:8443/api/', 'v1', auth_options: auth_options
+)
+```
+
+Note that this returns a token good for one hour. If your code requires authorization for longer than that, you should plan to
+acquire a new one, see [How to manually renew](#how-to-manually-renew-expired-credentials) section.
+
+#### OIDC Auth Provider
+
+If the cluster you are using has OIDC authentication enabled you can use the `openid_connect` gem to obtain
+id-tokens if the one in your kubeconfig has expired.
+
+This requires the [`openid_connect` gem](https://github.com/nov/openid_connect) which is not included in
+the `kubeclient` dependencies so should be added to your own applications bundle.
+
+The OIDC Auth Provider will not perform the initial setup of your `$KUBECONFIG` file. You will need to use something
+like [`dexter`](https://github.com/gini/dexter) in order to configure the auth-provider in your `$KUBECONFIG` file.
+
+If you use `Config.context(...).auth_options` and the `$KUBECONFIG` file has user: `{auth-provider: {name: oidc}}`,
+kubeclient will automatically obtain a token (or use `id-token` if still valid)
+
+Tokens are typically short-lived (e.g. 1 hour) and the expiration time is determined by the OIDC Provider (e.g. Google).
+If your code requires authentication for longer than that you should obtain a new token periodically, see [How to manually renew](#how-to-manually-renew-expired-credentials) section.
+
+Note: id-tokens retrieved via this provider are not written back to the `$KUBECONFIG` file as they would be when
+using `kubectl`.
+
+#### How to manually renew expired credentials
+
+Kubeclient [does not yet](https://github.com/abonas/kubeclient/issues/393) help with this.
+
+The division of labor between `Config` and `Context` objects may change, for now please make no assumptions at which stage `exec:` and `auth-provider:` are handled and whether they're cached.
+The currently guaranteed way to renew is create a new `Config` object.
+
+The more painful part is that you'll then need to create new `Client` object(s) with the credentials from new config.
+So repeat all of this:
+```ruby
+config = Kubeclient::Config.read(ENV['KUBECONFIG'] || '/path/to/.kube/config')
+context = config.context
+ssl_options = context.ssl_options
+auth_options = context.auth_options
+
+client = Kubeclient::Client.new(
+ context.api_endpoint, 'v1',
+ ssl_options: ssl_options, auth_options: auth_options
+)
+# and additional Clients if needed...
+```
+
+#### Security: Don't use config from untrusted sources
+
+`Config.read` is catastrophically unsafe — it will execute arbitrary command lines specified by the config!
+
+`Config.new(data, nil)` is better but Kubeclient was never reviewed for behaving safely with malicious / malformed config.
+It might crash / misbehave in unexpected ways...
+
+#### namespace
+
+Additionally, the `config.context` object will contain a `namespace` attribute, if it was defined in the file.
+It is recommended that you use this namespace when issuing API commands below.
+This is the same behavior that is implemented by `kubectl` command.
+
+You can read it as follows:
+
+```ruby
+puts config.context.namespace
+```
+
+### Supported kubernetes versions
+
+We try to support the last 3 minor versions, matching the [official support policy for Kubernetes](https://github.com/kubernetes/community/blob/master/contributors/design-proposals/release/versioning.md#supported-releases-and-component-skew).
+Kubernetes 1.2 and below have known issues and are unsupported.
+Kubernetes 1.3 presumed to still work although nobody is really testing on such old versions...
+
+## Supported actions & examples:
+
+Summary of main CRUD actions:
+
+```
+get_foos(namespace: 'namespace', **opts) # namespaced collection
+get_foos(**opts) # all namespaces or global collection
+
+get_foo('name', 'namespace', opts) # namespaced
+get_foo('name', nil, opts) # global
+
+watch_foos(namespace: ns, **opts) # namespaced collection
+watch_foos(**opts) # all namespaces or global collection
+watch_foos(namespace: ns, name: 'name', **opts) # namespaced single object
+watch_foos(name: 'name', **opts) # global single object
+
+delete_foo('name', 'namespace', opts) # namespaced
+delete_foo('name', nil, opts) # global
+
+create_foo(Kubeclient::Resource.new({metadata: {name: 'name', namespace: 'namespace', ...}, ...}))
+create_foo(Kubeclient::Resource.new({metadata: {name: 'name', ...}, ...})) # global
+
+update_foo(Kubeclient::Resource.new({metadata: {name: 'name', namespace: 'namespace', ...}, ...}))
+update_foo(Kubeclient::Resource.new({metadata: {name: 'name', ...}, ...})) # global
+
+patch_foo('name', patch, 'namespace') # namespaced
+patch_foo('name', patch) # global
+
+apply_foo(Kubeclient::Resource.new({metadata: {name: 'name', namespace: 'namespace', ...}, ...}), field_manager: 'myapp', **opts)
+apply_foo(Kubeclient::Resource.new({metadata: {name: 'name', ...}, ...}), field_manager: 'myapp', **opts) # global
+```
+
+These grew to be quite inconsistent :confounded:, see https://github.com/abonas/kubeclient/issues/312 and https://github.com/abonas/kubeclient/issues/332 for improvement plans.
+
+### Get all instances of a specific entity type
+Such as: `get_pods`, `get_secrets`, `get_services`, `get_nodes`, `get_replication_controllers`, `get_resource_quotas`, `get_limit_ranges`, `get_persistent_volumes`, `get_persistent_volume_claims`, `get_component_statuses`, `get_service_accounts`
+
+```ruby
+pods = client.get_pods
+```
+
+Get all entities of a specific type in a namespace:
+
+```ruby
+services = client.get_services(namespace: 'development')
+```
+
+You can get entities which have specific labels by specifying a parameter named `label_selector` (named `labelSelector` in Kubernetes server):
+
+```ruby
+pods = client.get_pods(label_selector: 'name=redis-master')
+```
+
+You can specify multiple labels (that option will return entities which have both labels:
+
+```ruby
+pods = client.get_pods(label_selector: 'name=redis-master,app=redis')
+```
+
+There is also [a limited ability to filter by *some* fields](https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/). Which fields are supported is not documented, you can try and see if you get an error...
+```ruby
+client.get_pods(field_selector: 'spec.nodeName=master-0')
+```
+
+You can ask for entities at a specific version by specifying a parameter named `resource_version`:
+```ruby
+pods = client.get_pods(resource_version: '0')
+```
+but it's not guaranteed you'll get it. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions to understand the semantics.
+
+With default (`as: :ros`) return format, the returned object acts like an array of the individual pods, but also supports a `.resourceVersion` method.
+
+With `:parsed` and `:parsed_symbolized` formats, the returned data structure matches kubernetes list structure: it's a hash containing `metadata` and `items` keys, the latter containing the individual pods.
+
+#### Get all entities of a specific type in chunks
+
+```ruby
+continue = nil
+loop do
+ entities = client.get_pods(limit: 1_000, continue: continue)
+ continue = entities.continue
+
+ break if entities.last?
+end
+```
+
+See https://kubernetes.io/docs/reference/using-api/api-concepts/#retrieving-large-results-sets-in-chunks for more information.
+
+The continue tokens expire after a short amount of time, so similar to a watch if you don't request a subsequent page within aprox. 5 minutes of the previous page being returned the server will return a `410 Gone` error and the client must request the list from the start (i.e. omit the continue token for the next call).
+
+Support for chunking was added in v1.9 so previous versions will ignore the option and return the full collection.
+
+#### Get a specific instance of an entity (by name)
+Such as: `get_service "service name"` , `get_pod "pod name"` , `get_replication_controller "rc name"`, `get_secret "secret name"`, `get_resource_quota "resource quota name"`, `get_limit_range "limit range name"` , `get_persistent_volume "persistent volume name"` , `get_persistent_volume_claim "persistent volume claim name"`, `get_component_status "component name"`, `get_service_account "service account name"`
+
+The GET request should include the namespace name, except for nodes and namespaces entities.
+
+```ruby
+node = client.get_node "127.0.0.1"
+```
+
+```ruby
+service = client.get_service "guestbook", 'development'
+```
+
+Note - Kubernetes doesn't work with the uid, but rather with the 'name' property.
+Querying with uid causes 404.
+
+#### Getting raw responses
+
+To avoid overhead from parsing and building `RecursiveOpenStruct` objects for each reply, pass the `as: :raw` option when initializing `Kubeclient::Client` or when calling `get_` / `watch_` methods.
+The result can then be printed, or searched with a regex, or parsed via `JSON.parse(r)`.
+
+```ruby
+client = Kubeclient::Client.new(as: :raw)
+```
+
+or
+
+```ruby
+pods = client.get_pods as: :raw
+node = client.get_node "127.0.0.1", as: :raw
+```
+
+Other formats are:
+ - `:ros` (default) for `RecursiveOpenStruct`
+ - `:parsed` for `JSON.parse`
+ - `:parsed_symbolized` for `JSON.parse(..., symbolize_names: true)`
+
+### Watch — Receive entities updates
+
+See https://kubernetes.io/docs/reference/using-api/api-concepts/#efficient-detection-of-changes for an overview.
+
+It is possible to receive live update notices watching the relevant entities:
+
+```ruby
+client.watch_pods do |notice|
+ # process notice data
+end
+```
+
+The notices have `.type` field which may be `'ADDED'`, `'MODIFIED'`, `'DELETED'`, or currently `'ERROR'`, and an `.object` field containing the object. **UPCOMING CHANGE**: In next major version, we plan to raise exceptions instead of passing on ERROR into the block.
+
+For namespaced entities, the default watches across all namespaces, and you can specify `client.watch_secrets(namespace: 'foo')` to only watch in a single namespace.
+
+You can narrow down using `label_selector:` and `field_selector:` params, like with `get_pods` methods.
+
+You can also watch a single object by specifying `name:` e.g. `client.watch_nodes(name: 'gandalf')` (not namespaced so a name is enough) or `client.watch_pods(namespace: 'foo', name: 'bar')` (namespaced, need both params).
+Note the method name is still plural! There is no `watch_pod`, only `watch_pods`. The yielded "type" remains the same — watch notices, it's just they'll always refer to the same object.
+
+You can use `as:` param to control the format of the yielded notices.
+
+#### All watches come to an end!
+
+While nominally the watch block *looks* like an infinite loop, that's unrealistic. Network connections eventually get severed, and kubernetes apiserver is known to terminate watches.
+
+Unfortunately, this sometimes raises an exception and sometimes the loop just exits. **UPCOMING CHANGE**: In next major version, non-deliberate termination will always raise an exception; the block will only exit silenty if stopped deliberately.
+
+#### Deliberately stopping a watch
+
+You can use `break` or `return` inside the watch block.
+
+It is possible to interrupt the watcher from another thread with:
+
+```ruby
+watcher = client.watch_pods
+
+watcher.each do |notice|
+ # process notice data
+end
+# <- control will pass here after .finish is called
+
+### In another thread ###
+watcher.finish
+```
+
+#### Starting watch version
+
+You can specify version to start from, commonly used in "List+Watch" pattern:
+```
+list = client.get_pods
+collection_version = list.resourceVersion
+# or with other return formats:
+list = client.get_pods(as: :parsed)
+collection_version = list['metadata']['resourceVersion']
+
+# note spelling resource_version vs resourceVersion.
+client.watch_pods(resource_version: collection_version) do |notice|
+ # process notice data
+end
+```
+It's important to understand [the effects of unset/0/specific resource_version](https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions) as it modifies the behavior of the watch — in some modes you'll first see a burst of synthetic 'ADDED' notices for all existing objects.
+
+If you re-try a terminated watch again without specific resourceVersion, you might see previously seen notices again, and might miss some events.
+
+To attempt resuming a watch from same point, you can try using last resourceVersion observed during the watch. Or do list+watch again.
+
+Whenever you ask for a specific version, you must be prepared for an 410 "Gone" error if the server no longer recognizes it.
+
+#### Watch events about a particular object
+Events are [entities in their own right](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#event-v1-core).
+You can use the `field_selector` option as part of the watch methods.
+
+```ruby
+client.watch_events(namespace: 'development', field_selector: 'involvedObject.name=redis-master') do |notice|
+ # process notice date
+end
+```
+
+### Delete an entity (by name)
+
+For example: `delete_pod "pod name"` , `delete_replication_controller "rc name"`, `delete_node "node name"`, `delete_secret "secret name"`
+
+Input parameter - name (string) specifying service name, pod name, replication controller name.
+
+```ruby
+deleted = client.delete_service("redis-service")
+```
+
+If you want to cascade delete, for example a deployment, you can use the `delete_options` parameter.
+
+```ruby
+deployment_name = 'redis-deployment'
+namespace = 'default'
+delete_options = Kubeclient::Resource.new(
+ apiVersion: 'meta/v1',
+ gracePeriodSeconds: 0,
+ kind: 'DeleteOptions',
+ propagationPolicy: 'Foreground' # Orphan, Foreground, or Background
+)
+client.delete_deployment(deployment_name, namespace, delete_options: delete_options)
+```
+
+### Create an entity
+For example: `create_pod pod_object`, `create_replication_controller rc_obj`, `create_secret secret_object`, `create_resource_quota resource_quota_object`, `create_limit_range limit_range_object`, `create_persistent_volume persistent_volume_object`, `create_persistent_volume_claim persistent_volume_claim_object`, `create_service_account service_account_object`
+
+Input parameter - object of type `Service`, `Pod`, `ReplicationController`.
+
+The below example is for v1
+
+```ruby
+service = Kubeclient::Resource.new
+service.metadata = {}
+service.metadata.name = "redis-master"
+service.metadata.namespace = 'staging'
+service.spec = {}
+service.spec.ports = [{
+ 'port' => 6379,
+ 'targetPort' => 'redis-server'
+}]
+service.spec.selector = {}
+service.spec.selector.name = "redis"
+service.spec.selector.role = "master"
+service.metadata.labels = {}
+service.metadata.labels.app = 'redis'
+service.metadata.labels.role = 'slave'
+client.create_service(service)
+```
+
+### Update an entity
+For example: `update_pod`, `update_service`, `update_replication_controller`, `update_secret`, `update_resource_quota`, `update_limit_range`, `update_persistent_volume`, `update_persistent_volume_claim`, `update_service_account`
+
+Input parameter - object of type `Pod`, `Service`, `ReplicationController` etc.
+
+The below example is for v1
+
+```ruby
+updated = client.update_service(service1)
+```
+
+### Patch an entity (by name)
+For example: `patch_pod`, `patch_service`, `patch_secret`, `patch_resource_quota`, `patch_persistent_volume`
+
+Input parameters - name (string) specifying the entity name, patch (hash) to be applied to the resource, optional: namespace name (string)
+
+The PATCH request should include the namespace name, except for nodes and namespaces entities.
+
+The below example is for v1
+
+```ruby
+patched = client.patch_pod("docker-registry", {metadata: {annotations: {key: 'value'}}}, "default")
+```
+
+`patch_#{entity}` is called using a [strategic merge patch](https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#notes-on-the-strategic-merge-patch). `json_patch_#{entity}` and `merge_patch_#{entity}` are also available that use JSON patch and JSON merge patch, respectively. These strategies are useful for resources that do not support strategic merge patch, such as Custom Resources. Consult the [Kubernetes docs](https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment) for more information about the different patch strategies.
+
+### Apply an entity
+
+This is similar to `kubectl apply --server-side` (kubeclient doesn't implement logic for client-side apply). See https://kubernetes.io/docs/reference/using-api/api-concepts/#server-side-apply
+
+For example: `apply_pod`
+
+Input parameters - resource (Kubeclient::Resource) representing the desired state of the resource, field_manager (String) to identify the system managing the state of the resource, force (Boolean) whether or not to override a field managed by someone else.
+
+Example:
+
+```ruby
+service = Kubeclient::Resource.new(
+ metadata: {
+ name: 'redis-master',
+ namespace: 'staging',
+ },
+ spec: {
+ ...
+ }
+)
+
+client.apply_service(service, field_manager: 'myapp')
+```
+
+### Get all entities of all types : all_entities
+
+Makes requests for all entities of each discovered kind (in this client's API group). This method is a convenience method instead of calling each entity's get method separately.
+
+Returns a hash with keys being the *singular* entity kind, in lowercase underscore style. For example for core API group may return keys `"node'`, `"secret"`, `"service"`, `"pod"`, `"replication_controller"`, `"namespace"`, `"resource_quota"`, `"limit_range"`, `"endpoint"`, `"event"`, `"persistent_volume"`, `"persistent_volume_claim"`, `"component_status"`, `"service_account"`. Each key points to an EntityList of same type.
+
+```ruby
+client.all_entities
+```
+
+### Get a proxy URL
+You can get a complete URL for connecting a kubernetes entity via the proxy.
+
+```ruby
+client.proxy_url('service', 'srvname', 'srvportname', 'ns')
+# => "https://localhost.localdomain:8443/api/v1/proxy/namespaces/ns/services/srvname:srvportname"
+```
+
+Note the third parameter, port, is a port name for services and an integer for pods:
+
+```ruby
+client.proxy_url('pod', 'podname', 5001, 'ns')
+# => "https://localhost.localdomain:8443/api/v1/namespaces/ns/pods/podname:5001/proxy"
+```
+
+### Get the logs of a pod
+You can get the logs of a running pod, specifying the name of the pod and the
+namespace where the pod is running:
+
+```ruby
+client.get_pod_log('pod-name', 'default')
+# => "Running...\nRunning...\nRunning...\n"
+```
+
+If that pod has more than one container, you must specify the container:
+
+```ruby
+client.get_pod_log('pod-name', 'default', container: 'ruby')
+# => "..."
+```
+
+If a container in a pod terminates, a new container is started, and you want to
+retrieve the logs of the dead container, you can pass in the `:previous` option:
+
+```ruby
+client.get_pod_log('pod-name', 'default', previous: true)
+# => "..."
+```
+
+Kubernetes can add timestamps to every log line or filter by lines time:
+```ruby
+client.get_pod_log('pod-name', 'default', timestamps: true, since_time: '2018-04-27T18:30:17.480321984Z')
+# => "..."
+```
+`since_time` can be a a `Time`, `DateTime` or `String` formatted according to RFC3339
+
+Kubernetes can fetch a specific number of lines from the end of the logs:
+```ruby
+client.get_pod_log('pod-name', 'default', tail_lines: 10)
+# => "..."
+```
+
+Kubernetes can fetch a specific number of bytes from the log, but the exact size is not guaranteed and last line may not be terminated:
+```ruby
+client.get_pod_log('pod-name', 'default', limit_bytes: 10)
+# => "..."
+```
+
+You can also watch the logs of a pod to get a stream of data:
+
+```ruby
+client.watch_pod_log('pod-name', 'default', container: 'ruby') do |line|
+ puts line
+end
+```
+
+### OpenShift: Process a template
+Returns a processed template containing a list of objects to create.
+Input parameter - template (hash)
+Besides its metadata, the template should include a list of objects to be processed and a list of parameters
+to be substituted. Note that for a required parameter that does not provide a generated value, you must supply a value.
+
+##### Note: This functionality is not supported by K8s at this moment. See the following [issue](https://github.com/kubernetes/kubernetes/issues/23896)
+
+```ruby
+client.process_template template
+```
+
+## Upgrading
+
+Kubeclient release versioning follows [SemVer](https://semver.org/).
+See [CHANGELOG.md](CHANGELOG.md) for full changelog.
+
+#### past version 4.0
+
+Old kubernetes versions < 1.3 no longer supported.
+
+#### past version 3.0
+
+Ruby versions < 2.2 are no longer supported
+
+Specific entity classes mentioned in [past version 1.2.0](#past_version_1.2.0) have been dropped.
+Return values and expected classes are always Kubeclient::Resource.
+Checking the type of a resource can be done using:
+```
+> pod.kind
+=> "Pod"
+```
+
+update_* delete_* and patch_* now return a RecursiveOpenStruct like the get_* methods
+
+The `Kubeclient::Client` class raises `Kubeclient::HttpError` or subclasses now. Catching `KubeException` still works but is deprecated.
+
+`Kubeclient::Config#context` raises `KeyError` instead of `RuntimeError` for non-existent context name.
+
+<a name="past_version_1.2.0">
+
+#### past version 1.2.0
+Replace Specific Entity class references:
+
+```ruby
+Kubeclient::Service
+```
+
+with the generic
+
+```ruby
+Kubeclient::Resource.new
+```
+
+Where ever possible.
+
+## Contributing
+
+1. Fork it ( https://github.com/[my-github-username]/kubeclient/fork )
+2. Create your feature branch (`git checkout -b my-new-feature`)
+3. Test your changes with `rake test rubocop`, add new tests if needed.
+4. If you added a new functionality, add it to README
+5. Commit your changes (`git commit -am 'Add some feature'`)
+6. Push to the branch (`git push origin my-new-feature`)
+7. Create a new Pull Request
+
+## Tests
+
+This client is tested with Minitest and also uses VCR recordings in some tests.
+Please run all tests before submitting a Pull Request, and add new tests for new functionality.
+
+Running tests:
+```ruby
+rake test
+```
diff --git a/vendor/gems/kubeclient/RELEASING.md b/vendor/gems/kubeclient/RELEASING.md
new file mode 100644
index 00000000000..c8a9a121eda
--- /dev/null
+++ b/vendor/gems/kubeclient/RELEASING.md
@@ -0,0 +1,69 @@
+# Releasing Kubeclient
+
+## Versioning
+Kubeclient release versioning follows [SemVer](https://semver.org/).
+At some point in time it is decided to release version x.y.z.
+
+```bash
+RELEASE_BRANCH="master"
+```
+
+## 0. (once) Install gem-release, needed for several commands here:
+
+```bash
+gem install gem-release
+```
+
+## 1. PR(s) for changelog & bump
+
+Edit `CHANGELOG.md` as necessary. Even if all included changes remembered to update it, you should replace "Unreleased" section header with appropriate "x.y.z — 20yy-mm-dd" header.
+
+Bump `lib/kubeclient/version.rb` manually, or by using:
+```bash
+RELEASE_VERSION=x.y.z
+
+git checkout -b "release-$RELEASE_VERSION" $RELEASE_BRANCH
+# Won't work with uncommitted changes, you have to commit the changelog first.
+gem bump --version $RELEASE_VERSION
+git show # View version bump change.
+```
+
+Open a PR with target branch $RELEASE_BRANCH and get it reviewed & merged (if open for long, remember to update date in CHANGELOG to actual day of release).
+
+## 2. (once) Grabbing an authentication token for rubygems.org api
+```bash
+RUBYGEMS_USERNAME=bob
+curl -u $RUBYGEMS_USERNAME https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials; chmod 0600 ~/.gem/credentials
+
+cat ~/.gem/credentials
+# Should look like this:
+:rubygems_api_key: ****
+```
+
+## 3. Actual release
+
+Make sure we're locally after the bump PR *merge commit*:
+```bash
+git checkout $RELEASE_BRANCH
+git status # Make sure there are no local changes
+git pull --ff-only https://github.com/abonas/kubeclient $RELEASE_BRANCH
+git log -n1
+```
+
+Last sanity check:
+```bash
+bundle install
+bundle exec rake test rubocop
+```
+
+Create and push the tag:
+```bash
+gem tag --no-push
+git push --tags --dry-run https://github.com/abonas/kubeclient # Check for unexpected tags
+git push --tags https://github.com/abonas/kubeclient
+```
+
+Release onto rubygems.org:
+```bash
+gem release
+```
diff --git a/vendor/gems/kubeclient/Rakefile b/vendor/gems/kubeclient/Rakefile
new file mode 100644
index 00000000000..a749a9c926e
--- /dev/null
+++ b/vendor/gems/kubeclient/Rakefile
@@ -0,0 +1,9 @@
+require 'bundler/gem_tasks'
+require 'rake/testtask'
+require 'rubocop/rake_task'
+require 'yaml'
+
+task default: %i[test rubocop] # same as .travis.yml
+
+Rake::TestTask.new
+RuboCop::RakeTask.new
diff --git a/vendor/gems/kubeclient/kubeclient.gemspec b/vendor/gems/kubeclient/kubeclient.gemspec
new file mode 100644
index 00000000000..975db8cdb59
--- /dev/null
+++ b/vendor/gems/kubeclient/kubeclient.gemspec
@@ -0,0 +1,39 @@
+# coding: utf-8
+
+lib = File.expand_path('../lib', __FILE__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+require 'kubeclient/version'
+
+Gem::Specification.new do |spec|
+ spec.name = 'kubeclient'
+ spec.version = Kubeclient::VERSION
+ spec.authors = ['Alissa Bonas']
+ spec.email = ['abonas@redhat.com']
+ spec.summary = 'A client for Kubernetes REST api'
+ spec.description = 'A client for Kubernetes REST api'
+ spec.homepage = 'https://github.com/abonas/kubeclient'
+ spec.license = 'MIT'
+
+ spec.files = Dir.glob("lib/**/*.*")
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
+ spec.test_files = []
+ spec.require_paths = ['lib']
+ spec.required_ruby_version = '>= 2.2.0'
+
+ spec.add_development_dependency 'bundler', '>= 1.6'
+ spec.add_development_dependency 'rake', '~> 13.0'
+ spec.add_development_dependency 'minitest', '~> 5.15.0'
+ spec.add_development_dependency 'minitest-rg'
+ spec.add_development_dependency 'webmock', '~> 3.0'
+ spec.add_development_dependency 'vcr'
+ spec.add_development_dependency 'rubocop', '= 0.49.1'
+ spec.add_development_dependency 'googleauth', '~> 0.5.1'
+ spec.add_development_dependency('mocha', '~> 1.5')
+ spec.add_development_dependency 'openid_connect', '~> 1.1'
+ spec.add_development_dependency 'net-smtp'
+
+ spec.add_dependency 'jsonpath', '~> 1.0'
+ spec.add_dependency 'rest-client', '~> 2.0'
+ spec.add_dependency 'recursive-open-struct', '~> 1.1', '>= 1.1.1'
+ spec.add_dependency 'http', '>= 3.0', '< 6.0'
+end
diff --git a/vendor/gems/kubeclient/lib/kubeclient.rb b/vendor/gems/kubeclient/lib/kubeclient.rb
new file mode 100644
index 00000000000..eed4872834e
--- /dev/null
+++ b/vendor/gems/kubeclient/lib/kubeclient.rb
@@ -0,0 +1,35 @@
+require 'json'
+require 'rest-client'
+
+require 'kubeclient/aws_eks_credentials'
+require 'kubeclient/common'
+require 'kubeclient/config'
+require 'kubeclient/entity_list'
+require 'kubeclient/exec_credentials'
+require 'kubeclient/gcp_auth_provider'
+require 'kubeclient/http_error'
+require 'kubeclient/missing_kind_compatibility'
+require 'kubeclient/oidc_auth_provider'
+require 'kubeclient/resource'
+require 'kubeclient/resource_not_found_error'
+require 'kubeclient/version'
+require 'kubeclient/watch_stream'
+
+module Kubeclient
+ # Kubernetes Client
+ class Client
+ include ClientMixin
+ def initialize(
+ uri,
+ version = 'v1',
+ **options
+ )
+ initialize_client(
+ uri,
+ '/api',
+ version,
+ **options
+ )
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/lib/kubeclient/aws_eks_credentials.rb b/vendor/gems/kubeclient/lib/kubeclient/aws_eks_credentials.rb
new file mode 100644
index 00000000000..9b54b9e06cc
--- /dev/null
+++ b/vendor/gems/kubeclient/lib/kubeclient/aws_eks_credentials.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Kubeclient
+ # Get a bearer token to authenticate against aws eks.
+ class AmazonEksCredentials
+ class AmazonEksDependencyError < LoadError # rubocop:disable Lint/InheritException
+ end
+
+ class << self
+ def token(credentials, eks_cluster)
+ begin
+ require 'aws-sigv4'
+ require 'base64'
+ require 'cgi'
+ rescue LoadError => e
+ raise AmazonEksDependencyError,
+ 'Error requiring aws gems. Kubeclient itself does not include the following ' \
+ 'gems: [aws-sigv4]. To support auth-provider eks, you must ' \
+ "include it in your calling application. Failed with: #{e.message}"
+ end
+ # https://github.com/aws/aws-sdk-ruby/pull/1848
+ # Get a signer
+ # Note - sts only has ONE endpoint (not regional) so 'us-east-1' hardcoding should be OK
+ signer = Aws::Sigv4::Signer.new(
+ service: 'sts',
+ region: 'us-east-1',
+ credentials: credentials
+ )
+
+ # https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/Sigv4/Signer.html#presign_url-instance_method
+ presigned_url_string = signer.presign_url(
+ http_method: 'GET',
+ url: 'https://sts.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15',
+ body: '',
+ credentials: credentials,
+ expires_in: 60,
+ headers: {
+ 'X-K8s-Aws-Id' => eks_cluster
+ }
+ )
+ kube_token = 'k8s-aws-v1.' + Base64.urlsafe_encode64(presigned_url_string.to_s).sub(/=*$/, '') # rubocop:disable Metrics/LineLength
+ kube_token
+ end
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/lib/kubeclient/common.rb b/vendor/gems/kubeclient/lib/kubeclient/common.rb
new file mode 100644
index 00000000000..51087fbe888
--- /dev/null
+++ b/vendor/gems/kubeclient/lib/kubeclient/common.rb
@@ -0,0 +1,661 @@
+require 'json'
+require 'rest-client'
+
+module Kubeclient
+ # Common methods
+ # this is mixed in by other gems
+ module ClientMixin
+ ENTITY_METHODS = %w[get watch delete create update patch json_patch merge_patch apply].freeze
+
+ DEFAULT_SSL_OPTIONS = {
+ client_cert: nil,
+ client_key: nil,
+ ca_file: nil,
+ cert_store: nil,
+ verify_ssl: OpenSSL::SSL::VERIFY_PEER
+ }.freeze
+
+ DEFAULT_AUTH_OPTIONS = {
+ username: nil,
+ password: nil,
+ bearer_token: nil,
+ bearer_token_file: nil
+ }.freeze
+
+ DEFAULT_SOCKET_OPTIONS = {
+ socket_class: nil,
+ ssl_socket_class: nil
+ }.freeze
+
+ DEFAULT_TIMEOUTS = {
+ # These do NOT affect watch, watching never times out.
+ open: Net::HTTP.new('127.0.0.1').open_timeout, # depends on ruby version
+ read: Net::HTTP.new('127.0.0.1').read_timeout
+ }.freeze
+
+ DEFAULT_HTTP_PROXY_URI = nil
+ DEFAULT_HTTP_MAX_REDIRECTS = 10
+
+ SEARCH_ARGUMENTS = {
+ 'labelSelector' => :label_selector,
+ 'fieldSelector' => :field_selector,
+ 'resourceVersion' => :resource_version,
+ 'limit' => :limit,
+ 'continue' => :continue
+ }.freeze
+
+ WATCH_ARGUMENTS = {
+ 'labelSelector' => :label_selector,
+ 'fieldSelector' => :field_selector,
+ 'resourceVersion' => :resource_version
+ }.freeze
+
+ attr_reader :api_endpoint
+ attr_reader :ssl_options
+ attr_reader :auth_options
+ attr_reader :http_proxy_uri
+ attr_reader :http_max_redirects
+ attr_reader :headers
+ attr_reader :discovered
+
+ def initialize_client(
+ uri,
+ path,
+ version,
+ ssl_options: DEFAULT_SSL_OPTIONS,
+ auth_options: DEFAULT_AUTH_OPTIONS,
+ socket_options: DEFAULT_SOCKET_OPTIONS,
+ timeouts: DEFAULT_TIMEOUTS,
+ http_proxy_uri: DEFAULT_HTTP_PROXY_URI,
+ http_max_redirects: DEFAULT_HTTP_MAX_REDIRECTS,
+ as: :ros
+ )
+ validate_auth_options(auth_options)
+ handle_uri(uri, path)
+
+ @entities = {}
+ @discovered = false
+ @api_version = version
+ @headers = {}
+ @ssl_options = ssl_options
+ @auth_options = auth_options
+ @socket_options = socket_options
+ # Allow passing partial timeouts hash, without unspecified
+ # @timeouts[:foo] == nil resulting in infinite timeout.
+ @timeouts = DEFAULT_TIMEOUTS.merge(timeouts)
+ @http_proxy_uri = http_proxy_uri ? http_proxy_uri.to_s : nil
+ @http_max_redirects = http_max_redirects
+ @as = as
+
+ if auth_options[:bearer_token]
+ bearer_token(@auth_options[:bearer_token])
+ elsif auth_options[:bearer_token_file]
+ validate_bearer_token_file
+ bearer_token(File.read(@auth_options[:bearer_token_file]))
+ end
+ end
+
+ def method_missing(method_sym, *args, &block)
+ if discovery_needed?(method_sym)
+ discover
+ send(method_sym, *args, &block)
+ else
+ super
+ end
+ end
+
+ def respond_to_missing?(method_sym, include_private = false)
+ if discovery_needed?(method_sym)
+ discover
+ respond_to?(method_sym, include_private)
+ else
+ super
+ end
+ end
+
+ def discovery_needed?(method_sym)
+ !@discovered && ENTITY_METHODS.any? { |x| method_sym.to_s.start_with?(x) }
+ end
+
+ def handle_exception
+ yield
+ rescue RestClient::Exception => e
+ json_error_msg = begin
+ JSON.parse(e.response || '') || {}
+ rescue JSON::ParserError
+ {}
+ end
+ err_message = json_error_msg['message'] || e.message
+ error_klass = e.http_code == 404 ? ResourceNotFoundError : HttpError
+ raise error_klass.new(e.http_code, err_message, e.response)
+ end
+
+ def discover
+ load_entities
+ define_entity_methods
+ @discovered = true
+ end
+
+ def self.parse_definition(kind, name)
+ # Kubernetes gives us 3 inputs:
+ # kind: "ComponentStatus", "NetworkPolicy", "Endpoints"
+ # name: "componentstatuses", "networkpolicies", "endpoints"
+ # singularName: "componentstatus" etc (usually omitted, defaults to kind.downcase)
+ # and want to derive singular and plural method names, with underscores:
+ # "network_policy"
+ # "network_policies"
+ # kind's CamelCase word boundaries determine our placement of underscores.
+
+ if IRREGULAR_NAMES[kind]
+ # In a few cases, the given kind / singularName itself is still plural.
+ # We require a distinct singular method name, so force it.
+ method_names = IRREGULAR_NAMES[kind]
+ else
+ # TODO: respect singularName from discovery?
+ # But how? If it differs from kind.downcase, kind's word boundaries don't apply.
+ singular_name = kind.downcase
+
+ if !(/[A-Z]/ =~ kind)
+ # Some custom resources have a fully lowercase kind - can't infer underscores.
+ method_names = [singular_name, name]
+ else
+ # Some plurals are not exact suffixes, e.g. NetworkPolicy -> networkpolicies.
+ # So don't expect full last word to match.
+ /^(?<prefix>(.*[A-Z]))(?<singular_suffix>[^A-Z]*)$/ =~ kind # "NetworkP", "olicy"
+ if name.start_with?(prefix.downcase)
+ plural_suffix = name[prefix.length..-1] # "olicies"
+ prefix_underscores = ClientMixin.underscore_entity(prefix) # "network_p"
+ method_names = [prefix_underscores + singular_suffix, # "network_policy"
+ prefix_underscores + plural_suffix] # "network_policies"
+ else
+ method_names = resolve_unconventional_method_names(name, kind, singular_name)
+ end
+ end
+ end
+
+ OpenStruct.new(
+ entity_type: kind,
+ resource_name: name,
+ method_names: method_names
+ )
+ end
+
+ def self.resolve_unconventional_method_names(name, kind, singular_name)
+ underscored_name = name.tr('-', '_')
+ singular_underscores = ClientMixin.underscore_entity(kind)
+ if underscored_name.start_with?(singular_underscores)
+ [singular_underscores, underscored_name]
+ else
+ # fallback to lowercase, no separators for both names
+ [singular_name, underscored_name.tr('_', '')]
+ end
+ end
+
+ def handle_uri(uri, path)
+ raise ArgumentError, 'Missing uri' unless uri
+ @api_endpoint = (uri.is_a?(URI) ? uri : URI.parse(uri))
+
+ # This regex will anchor at the last `/api`, `/oapi` or`/apis/:group`) part of the URL
+ # The whole path will be matched and if existing, the api_group will be extracted.
+ re = /^(?<path>.*\/o?api(?:s\/(?<apigroup>[^\/]+))?)$/mi
+ match = re.match(@api_endpoint.path.chomp('/'))
+
+ if match
+ # Since `re` captures 2 groups, match will always have 3 elements
+ # If thus we have a non-nil value in match 2, this is our api_group.
+ @api_group = match[:apigroup].nil? ? '' : match[:apigroup] + '/'
+ @api_endpoint.path = match[:path]
+ else
+ # This is a fallback, for when `/api` was not provided as part of the uri
+ @api_group = ''
+ @api_endpoint.path = @api_endpoint.path.chomp('/') + path
+ end
+ end
+
+ def build_namespace_prefix(namespace)
+ namespace.to_s.empty? ? '' : "namespaces/#{namespace}/"
+ end
+
+ # rubocop:disable Metrics/BlockLength
+ def define_entity_methods
+ @entities.values.each do |entity|
+ # get all entities of a type e.g. get_nodes, get_pods, etc.
+ define_singleton_method("get_#{entity.method_names[1]}") do |options = {}|
+ get_entities(entity.entity_type, entity.resource_name, options)
+ end
+
+ # watch all entities of a type e.g. watch_nodes, watch_pods, etc.
+ define_singleton_method("watch_#{entity.method_names[1]}") do |options = {}, &block|
+ # This method used to take resource_version as a param, so
+ # this conversion is to keep backwards compatibility
+ options = { resource_version: options } unless options.is_a?(Hash)
+
+ watch_entities(entity.resource_name, options, &block)
+ end
+
+ # get a single entity of a specific type by name
+ define_singleton_method("get_#{entity.method_names[0]}") \
+ do |name, namespace = nil, opts = {}|
+ get_entity(entity.resource_name, name, namespace, opts)
+ end
+
+ define_singleton_method("delete_#{entity.method_names[0]}") \
+ do |name, namespace = nil, opts = {}|
+ delete_entity(entity.resource_name, name, namespace, **opts)
+ end
+
+ define_singleton_method("create_#{entity.method_names[0]}") do |entity_config|
+ create_entity(entity.entity_type, entity.resource_name, entity_config)
+ end
+
+ define_singleton_method("update_#{entity.method_names[0]}") do |entity_config|
+ update_entity(entity.resource_name, entity_config)
+ end
+
+ define_singleton_method("patch_#{entity.method_names[0]}") \
+ do |name, patch, namespace = nil|
+ patch_entity(entity.resource_name, name, patch, 'strategic-merge-patch', namespace)
+ end
+
+ define_singleton_method("json_patch_#{entity.method_names[0]}") \
+ do |name, patch, namespace = nil|
+ patch_entity(entity.resource_name, name, patch, 'json-patch', namespace)
+ end
+
+ define_singleton_method("merge_patch_#{entity.method_names[0]}") \
+ do |name, patch, namespace = nil|
+ patch_entity(entity.resource_name, name, patch, 'merge-patch', namespace)
+ end
+
+ define_singleton_method("apply_#{entity.method_names[0]}") do |resource, opts = {}|
+ apply_entity(entity.resource_name, resource, **opts)
+ end
+ end
+ end
+ # rubocop:enable Metrics/BlockLength
+
+ def self.underscore_entity(entity_name)
+ entity_name.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
+ end
+
+ def create_rest_client(path = nil)
+ path ||= @api_endpoint.path
+ options = {
+ ssl_ca_file: @ssl_options[:ca_file],
+ ssl_cert_store: @ssl_options[:cert_store],
+ verify_ssl: @ssl_options[:verify_ssl],
+ ssl_client_cert: @ssl_options[:client_cert],
+ ssl_client_key: @ssl_options[:client_key],
+ proxy: @http_proxy_uri,
+ max_redirects: @http_max_redirects,
+ user: @auth_options[:username],
+ password: @auth_options[:password],
+ open_timeout: @timeouts[:open],
+ read_timeout: @timeouts[:read]
+ }
+ RestClient::Resource.new(@api_endpoint.merge(path).to_s, options)
+ end
+
+ def rest_client
+ @rest_client ||= begin
+ create_rest_client("#{@api_endpoint.path}/#{@api_version}")
+ end
+ end
+
+ # Accepts the following options:
+ # :namespace (string) - the namespace of the entity.
+ # :name (string) - the name of the entity to watch.
+ # :label_selector (string) - a selector to restrict the list of returned objects by labels.
+ # :field_selector (string) - a selector to restrict the list of returned objects by fields.
+ # :resource_version (string) - shows changes that occur after passed version of a resource.
+ # :as (:raw|:ros) - defaults to :ros
+ # :raw - return the raw response body as a string
+ # :ros - return a collection of RecursiveOpenStruct objects
+ # Accepts an optional block, that will be called with each entity,
+ # otherwise returns a WatchStream
+ def watch_entities(resource_name, options = {}, &block)
+ ns = build_namespace_prefix(options[:namespace])
+
+ path = "watch/#{ns}#{resource_name}"
+ path += "/#{options[:name]}" if options[:name]
+ uri = @api_endpoint.merge("#{@api_endpoint.path}/#{@api_version}/#{path}")
+
+ params = {}
+ WATCH_ARGUMENTS.each { |k, v| params[k] = options[v] if options[v] }
+ uri.query = URI.encode_www_form(params) if params.any?
+
+ watcher = Kubeclient::Common::WatchStream.new(
+ uri,
+ http_options(uri),
+ formatter: ->(value) { format_response(options[:as] || @as, value) }
+ )
+
+ return_or_yield_to_watcher(watcher, &block)
+ end
+
+ # Accepts the following options:
+ # :namespace (string) - the namespace of the entity.
+ # :label_selector (string) - a selector to restrict the list of returned objects by labels.
+ # :field_selector (string) - a selector to restrict the list of returned objects by fields.
+ # :limit (integer) - a maximum number of items to return in each response
+ # :continue (string) - a token used to retrieve the next chunk of entities
+ # :as (:raw|:ros) - defaults to :ros
+ # :raw - return the raw response body as a string
+ # :ros - return a collection of RecursiveOpenStruct objects
+ def get_entities(entity_type, resource_name, options = {})
+ params = {}
+ SEARCH_ARGUMENTS.each { |k, v| params[k] = options[v] if options[v] }
+
+ ns_prefix = build_namespace_prefix(options[:namespace])
+ response = handle_exception do
+ rest_client[ns_prefix + resource_name]
+ .get({ 'params' => params }.merge(@headers))
+ end
+ format_response(options[:as] || @as, response.body, entity_type)
+ end
+
+ # Accepts the following options:
+ # :as (:raw|:ros) - defaults to :ros
+ # :raw - return the raw response body as a string
+ # :ros - return a collection of RecursiveOpenStruct objects
+ def get_entity(resource_name, name, namespace = nil, options = {})
+ ns_prefix = build_namespace_prefix(namespace)
+ response = handle_exception do
+ rest_client[ns_prefix + resource_name + "/#{name}"]
+ .get(@headers)
+ end
+ format_response(options[:as] || @as, response.body)
+ end
+
+ # delete_options are passed as a JSON payload in the delete request
+ def delete_entity(resource_name, name, namespace = nil, delete_options: {})
+ delete_options_hash = delete_options.to_hash
+ ns_prefix = build_namespace_prefix(namespace)
+ payload = delete_options_hash.to_json unless delete_options_hash.empty?
+ response = handle_exception do
+ rs = rest_client[ns_prefix + resource_name + "/#{name}"]
+ RestClient::Request.execute(
+ rs.options.merge(
+ method: :delete,
+ url: rs.url,
+ headers: { 'Content-Type' => 'application/json' }.merge(@headers),
+ payload: payload
+ )
+ )
+ end
+ format_response(@as, response.body)
+ end
+
+ def create_entity(entity_type, resource_name, entity_config)
+ # Duplicate the entity_config to a hash so that when we assign
+ # kind and apiVersion, this does not mutate original entity_config obj.
+ hash = entity_config.to_hash
+
+ ns_prefix = build_namespace_prefix(hash[:metadata][:namespace])
+
+ # TODO: temporary solution to add "kind" and apiVersion to request
+ # until this issue is solved
+ # https://github.com/GoogleCloudPlatform/kubernetes/issues/6439
+ hash[:kind] = entity_type
+ hash[:apiVersion] = @api_group + @api_version
+ response = handle_exception do
+ rest_client[ns_prefix + resource_name]
+ .post(hash.to_json, { 'Content-Type' => 'application/json' }.merge(@headers))
+ end
+ format_response(@as, response.body)
+ end
+
+ def update_entity(resource_name, entity_config)
+ name = entity_config[:metadata][:name]
+ ns_prefix = build_namespace_prefix(entity_config[:metadata][:namespace])
+ response = handle_exception do
+ rest_client[ns_prefix + resource_name + "/#{name}"]
+ .put(entity_config.to_h.to_json, { 'Content-Type' => 'application/json' }.merge(@headers))
+ end
+ format_response(@as, response.body)
+ end
+
+ def patch_entity(resource_name, name, patch, strategy, namespace)
+ ns_prefix = build_namespace_prefix(namespace)
+ response = handle_exception do
+ rest_client[ns_prefix + resource_name + "/#{name}"]
+ .patch(
+ patch.to_json,
+ { 'Content-Type' => "application/#{strategy}+json" }.merge(@headers)
+ )
+ end
+ format_response(@as, response.body)
+ end
+
+ def apply_entity(resource_name, resource, field_manager:, force: true)
+ name = "#{resource[:metadata][:name]}?fieldManager=#{field_manager}&force=#{force}"
+ ns_prefix = build_namespace_prefix(resource[:metadata][:namespace])
+ response = handle_exception do
+ rest_client[ns_prefix + resource_name + "/#{name}"]
+ .patch(
+ resource.to_json,
+ { 'Content-Type' => 'application/apply-patch+yaml' }.merge(@headers)
+ )
+ end
+ format_response(@as, response.body)
+ end
+
+ def all_entities(options = {})
+ discover unless @discovered
+ @entities.values.each_with_object({}) do |entity, result_hash|
+ # method call for get each entities
+ # build hash of entity name to array of the entities
+ method_name = "get_#{entity.method_names[1]}"
+ begin
+ result_hash[entity.method_names[0]] = send(method_name, options)
+ rescue Kubeclient::HttpError
+ next # do not fail due to resources not supporting get
+ end
+ end
+ end
+
+ def get_pod_log(pod_name, namespace,
+ container: nil, previous: false,
+ timestamps: false, since_time: nil, tail_lines: nil, limit_bytes: nil)
+ params = {}
+ params[:previous] = true if previous
+ params[:container] = container if container
+ params[:timestamps] = timestamps if timestamps
+ params[:sinceTime] = format_datetime(since_time) if since_time
+ params[:tailLines] = tail_lines if tail_lines
+ params[:limitBytes] = limit_bytes if limit_bytes
+
+ ns = build_namespace_prefix(namespace)
+ handle_exception do
+ rest_client[ns + "pods/#{pod_name}/log"]
+ .get({ 'params' => params }.merge(@headers))
+ end
+ end
+
+ def watch_pod_log(pod_name, namespace, container: nil, &block)
+ # Adding the "follow=true" query param tells the Kubernetes API to keep
+ # the connection open and stream updates to the log.
+ params = { follow: true }
+ params[:container] = container if container
+
+ ns = build_namespace_prefix(namespace)
+
+ uri = @api_endpoint.dup
+ uri.path += "/#{@api_version}/#{ns}pods/#{pod_name}/log"
+ uri.query = URI.encode_www_form(params)
+
+ watcher = Kubeclient::Common::WatchStream.new(
+ uri, http_options(uri), formatter: ->(value) { value }
+ )
+ return_or_yield_to_watcher(watcher, &block)
+ end
+
+ def proxy_url(kind, name, port, namespace = '')
+ discover unless @discovered
+ entity_name_plural =
+ if %w[services pods nodes].include?(kind.to_s)
+ kind.to_s
+ else
+ @entities[kind.to_s].resource_name
+ end
+ ns_prefix = build_namespace_prefix(namespace)
+ rest_client["#{ns_prefix}#{entity_name_plural}/#{name}:#{port}/proxy"].url
+ end
+
+ def process_template(template)
+ ns_prefix = build_namespace_prefix(template[:metadata][:namespace])
+ response = handle_exception do
+ rest_client[ns_prefix + 'processedtemplates']
+ .post(template.to_h.to_json, { 'Content-Type' => 'application/json' }.merge(@headers))
+ end
+ JSON.parse(response)
+ end
+
+ def api_valid?
+ result = api
+ result.is_a?(Hash) && (result['versions'] || []).any? do |group|
+ @api_group.empty? ? group.include?(@api_version) : group['version'] == @api_version
+ end
+ end
+
+ def api
+ response = handle_exception { create_rest_client.get(@headers) }
+ JSON.parse(response)
+ end
+
+ private
+
+ IRREGULAR_NAMES = {
+ # In a few cases, the given kind itself is still plural.
+ # https://github.com/kubernetes/kubernetes/issues/8115
+ 'Endpoints' => %w[endpoint endpoints],
+ 'SecurityContextConstraints' => %w[security_context_constraint
+ security_context_constraints]
+ }.freeze
+
+ # Format datetime according to RFC3339
+ def format_datetime(value)
+ case value
+ when DateTime, Time
+ value.strftime('%FT%T.%9N%:z')
+ when String
+ value
+ else
+ raise ArgumentError, "unsupported type '#{value.class}' of time value '#{value}'"
+ end
+ end
+
+ def format_response(as, body, list_type = nil)
+ case as
+ when :raw
+ body
+ when :parsed
+ JSON.parse(body)
+ when :parsed_symbolized
+ JSON.parse(body, symbolize_names: true)
+ when :ros
+ result = JSON.parse(body)
+
+ if list_type
+ resource_version =
+ result.fetch('resourceVersion') do
+ result.fetch('metadata', {}).fetch('resourceVersion', nil)
+ end
+
+ # If 'limit' was passed save the continue token
+ # see https://kubernetes.io/docs/reference/using-api/api-concepts/#retrieving-large-results-sets-in-chunks
+ continue = result.fetch('metadata', {}).fetch('continue', nil)
+
+ # result['items'] might be nil due to https://github.com/kubernetes/kubernetes/issues/13096
+ collection = result['items'].to_a.map { |item| Kubeclient::Resource.new(item) }
+
+ Kubeclient::Common::EntityList.new(list_type, resource_version, collection, continue)
+ else
+ Kubeclient::Resource.new(result)
+ end
+ else
+ raise ArgumentError, "Unsupported format #{as.inspect}"
+ end
+ end
+
+ def load_entities
+ @entities = {}
+ fetch_entities['resources'].each do |resource|
+ next if resource['name'].include?('/')
+ # Not a regular entity, special functionality covered by `process_template`.
+ # https://github.com/openshift/origin/issues/21668
+ next if resource['kind'] == 'Template' && resource['name'] == 'processedtemplates'
+ resource['kind'] ||=
+ Kubeclient::Common::MissingKindCompatibility.resource_kind(resource['name'])
+ entity = ClientMixin.parse_definition(resource['kind'], resource['name'])
+ @entities[entity.method_names[0]] = entity if entity
+ end
+ end
+
+ def fetch_entities
+ JSON.parse(handle_exception { rest_client.get(@headers) })
+ end
+
+ def bearer_token(bearer_token)
+ @headers ||= {}
+ @headers[:Authorization] = "Bearer #{bearer_token}"
+ end
+
+ def validate_auth_options(opts)
+ # maintain backward compatibility:
+ opts[:username] = opts[:user] if opts[:user]
+
+ if %i[bearer_token bearer_token_file username].count { |key| opts[key] } > 1
+ raise(
+ ArgumentError,
+ 'Invalid auth options: specify only one of username/password,' \
+ ' bearer_token or bearer_token_file'
+ )
+ elsif %i[username password].count { |key| opts[key] } == 1
+ raise ArgumentError, 'Basic auth requires both username & password'
+ end
+ end
+
+ def validate_bearer_token_file
+ msg = "Token file #{@auth_options[:bearer_token_file]} does not exist"
+ raise ArgumentError, msg unless File.file?(@auth_options[:bearer_token_file])
+
+ msg = "Cannot read token file #{@auth_options[:bearer_token_file]}"
+ raise ArgumentError, msg unless File.readable?(@auth_options[:bearer_token_file])
+ end
+
+ def return_or_yield_to_watcher(watcher, &block)
+ return watcher unless block_given?
+
+ begin
+ watcher.each(&block)
+ ensure
+ watcher.finish
+ end
+ end
+
+ def http_options(uri)
+ options = {
+ basic_auth_user: @auth_options[:username],
+ basic_auth_password: @auth_options[:password],
+ headers: @headers,
+ http_proxy_uri: @http_proxy_uri,
+ http_max_redirects: http_max_redirects
+ }
+
+ if uri.scheme == 'https'
+ options[:ssl] = {
+ ca_file: @ssl_options[:ca_file],
+ cert: @ssl_options[:client_cert],
+ cert_store: @ssl_options[:cert_store],
+ key: @ssl_options[:client_key],
+ # ruby HTTP uses verify_mode instead of verify_ssl
+ # http://ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html
+ verify_mode: @ssl_options[:verify_ssl]
+ }
+ end
+
+ options.merge(@socket_options)
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/lib/kubeclient/config.rb b/vendor/gems/kubeclient/lib/kubeclient/config.rb
new file mode 100644
index 00000000000..3598afe83fe
--- /dev/null
+++ b/vendor/gems/kubeclient/lib/kubeclient/config.rb
@@ -0,0 +1,202 @@
+require 'yaml'
+require 'base64'
+require 'pathname'
+
+module Kubeclient
+ # Kubernetes client configuration class
+ class Config
+ # Kubernetes client configuration context class
+ class Context
+ attr_reader :api_endpoint, :api_version, :ssl_options, :auth_options, :namespace
+
+ def initialize(api_endpoint, api_version, ssl_options, auth_options, namespace)
+ @api_endpoint = api_endpoint
+ @api_version = api_version
+ @ssl_options = ssl_options
+ @auth_options = auth_options
+ @namespace = namespace
+ end
+ end
+
+ # data (Hash) - Parsed kubeconfig data.
+ # kcfg_path (string) - Base directory for resolving relative references to external files.
+ # If set to nil, all external lookups & commands are disabled (even for absolute paths).
+ # See also the more convenient Config.read
+ def initialize(data, kcfg_path)
+ @kcfg = data
+ @kcfg_path = kcfg_path
+ raise 'Unknown kubeconfig version' if @kcfg['apiVersion'] != 'v1'
+ end
+
+ # Builds Config instance by parsing given file, with lookups relative to file's directory.
+ def self.read(filename)
+ parsed =
+ if RUBY_VERSION >= '2.6'
+ YAML.safe_load(File.read(filename), permitted_classes: [Date, Time])
+ else
+ YAML.safe_load(File.read(filename), [Date, Time])
+ end
+ Config.new(parsed, File.dirname(filename))
+ end
+
+ def contexts
+ @kcfg['contexts'].map { |x| x['name'] }
+ end
+
+ def context(context_name = nil)
+ cluster, user, namespace = fetch_context(context_name || @kcfg['current-context'])
+
+ if user.key?('exec')
+ exec_opts = expand_command_option(user['exec'], 'command')
+ user['exec_result'] = ExecCredentials.run(exec_opts)
+ end
+
+ client_cert_data = fetch_user_cert_data(user)
+ client_key_data = fetch_user_key_data(user)
+ auth_options = fetch_user_auth_options(user)
+
+ ssl_options = {}
+
+ ssl_options[:verify_ssl] = if cluster['insecure-skip-tls-verify'] == true
+ OpenSSL::SSL::VERIFY_NONE
+ else
+ OpenSSL::SSL::VERIFY_PEER
+ end
+
+ if cluster_ca_data?(cluster)
+ cert_store = OpenSSL::X509::Store.new
+ populate_cert_store_from_cluster_ca_data(cluster, cert_store)
+ ssl_options[:cert_store] = cert_store
+ end
+
+ unless client_cert_data.nil?
+ ssl_options[:client_cert] = OpenSSL::X509::Certificate.new(client_cert_data)
+ end
+
+ unless client_key_data.nil?
+ ssl_options[:client_key] = OpenSSL::PKey.read(client_key_data)
+ end
+
+ Context.new(cluster['server'], @kcfg['apiVersion'], ssl_options, auth_options, namespace)
+ end
+
+ private
+
+ def allow_external_lookups?
+ @kcfg_path != nil
+ end
+
+ def ext_file_path(path)
+ unless allow_external_lookups?
+ raise "Kubeclient::Config: external lookups disabled, can't load '#{path}'"
+ end
+ Pathname(path).absolute? ? path : File.join(@kcfg_path, path)
+ end
+
+ def ext_command_path(path)
+ unless allow_external_lookups?
+ raise "Kubeclient::Config: external lookups disabled, can't execute '#{path}'"
+ end
+ # Like go client https://github.com/kubernetes/kubernetes/pull/59495#discussion_r171138995,
+ # distinguish 3 cases:
+ # - absolute (e.g. /path/to/foo)
+ # - $PATH-based (e.g. curl)
+ # - relative to config file's dir (e.g. ./foo)
+ if Pathname(path).absolute?
+ path
+ elsif File.basename(path) == path
+ path
+ else
+ File.join(@kcfg_path, path)
+ end
+ end
+
+ def fetch_context(context_name)
+ context = @kcfg['contexts'].detect do |x|
+ break x['context'] if x['name'] == context_name
+ end
+
+ raise KeyError, "Unknown context #{context_name}" unless context
+
+ cluster = @kcfg['clusters'].detect do |x|
+ break x['cluster'] if x['name'] == context['cluster']
+ end
+
+ raise KeyError, "Unknown cluster #{context['cluster']}" unless cluster
+
+ user = @kcfg['users'].detect do |x|
+ break x['user'] if x['name'] == context['user']
+ end || {}
+
+ namespace = context['namespace']
+
+ [cluster, user, namespace]
+ end
+
+ def cluster_ca_data?(cluster)
+ cluster.key?('certificate-authority') || cluster.key?('certificate-authority-data')
+ end
+
+ def populate_cert_store_from_cluster_ca_data(cluster, cert_store)
+ if cluster.key?('certificate-authority')
+ cert_store.add_file(ext_file_path(cluster['certificate-authority']))
+ elsif cluster.key?('certificate-authority-data')
+ ca_cert_data = Base64.decode64(cluster['certificate-authority-data'])
+ cert_store.add_cert(OpenSSL::X509::Certificate.new(ca_cert_data))
+ end
+ end
+
+ def fetch_user_cert_data(user)
+ if user.key?('client-certificate')
+ File.read(ext_file_path(user['client-certificate']))
+ elsif user.key?('client-certificate-data')
+ Base64.decode64(user['client-certificate-data'])
+ elsif user.key?('exec_result') && user['exec_result'].key?('clientCertificateData')
+ user['exec_result']['clientCertificateData']
+ end
+ end
+
+ def fetch_user_key_data(user)
+ if user.key?('client-key')
+ File.read(ext_file_path(user['client-key']))
+ elsif user.key?('client-key-data')
+ Base64.decode64(user['client-key-data'])
+ elsif user.key?('exec_result') && user['exec_result'].key?('clientKeyData')
+ user['exec_result']['clientKeyData']
+ end
+ end
+
+ def fetch_user_auth_options(user)
+ options = {}
+ if user.key?('token')
+ options[:bearer_token] = user['token']
+ elsif user.key?('exec_result') && user['exec_result'].key?('token')
+ options[:bearer_token] = user['exec_result']['token']
+ elsif user.key?('auth-provider')
+ options[:bearer_token] = fetch_token_from_provider(user['auth-provider'])
+ else
+ %w[username password].each do |attr|
+ options[attr.to_sym] = user[attr] if user.key?(attr)
+ end
+ end
+ options
+ end
+
+ def fetch_token_from_provider(auth_provider)
+ case auth_provider['name']
+ when 'gcp'
+ config = expand_command_option(auth_provider['config'], 'cmd-path')
+ Kubeclient::GCPAuthProvider.token(config)
+ when 'oidc'
+ Kubeclient::OIDCAuthProvider.token(auth_provider['config'])
+ end
+ end
+
+ def expand_command_option(config, key)
+ config = config.dup
+ config[key] = ext_command_path(config[key]) if config[key]
+
+ config
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/lib/kubeclient/entity_list.rb b/vendor/gems/kubeclient/lib/kubeclient/entity_list.rb
new file mode 100644
index 00000000000..2f734560a4b
--- /dev/null
+++ b/vendor/gems/kubeclient/lib/kubeclient/entity_list.rb
@@ -0,0 +1,21 @@
+require 'delegate'
+module Kubeclient
+ module Common
+ # Kubernetes Entity List
+ class EntityList < DelegateClass(Array)
+ attr_reader :continue, :kind, :resourceVersion
+
+ def initialize(kind, resource_version, list, continue = nil)
+ @kind = kind
+ # rubocop:disable Style/VariableName
+ @resourceVersion = resource_version
+ @continue = continue
+ super(list)
+ end
+
+ def last?
+ continue.nil?
+ end
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/lib/kubeclient/exec_credentials.rb b/vendor/gems/kubeclient/lib/kubeclient/exec_credentials.rb
new file mode 100644
index 00000000000..016d48ae289
--- /dev/null
+++ b/vendor/gems/kubeclient/lib/kubeclient/exec_credentials.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+module Kubeclient
+ # An exec-based client auth provide
+ # https://kubernetes.io/docs/reference/access-authn-authz/authentication/#configuration
+ # Inspired by https://github.com/kubernetes/client-go/blob/master/plugin/pkg/client/auth/exec/exec.go
+ class ExecCredentials
+ class << self
+ def run(opts)
+ require 'open3'
+ require 'json'
+
+ raise ArgumentError, 'exec options are required' if opts.nil?
+
+ cmd = opts['command']
+ args = opts['args']
+ env = map_env(opts['env'])
+
+ # Validate exec options
+ validate_opts(opts)
+
+ out, err, st = Open3.capture3(env, cmd, *args)
+
+ raise "exec command failed: #{err}" unless st.success?
+
+ creds = JSON.parse(out)
+ validate_credentials(opts, creds)
+ creds['status']
+ end
+
+ private
+
+ def validate_opts(opts)
+ raise KeyError, 'exec command is required' unless opts['command']
+ end
+
+ def validate_client_credentials_status(status)
+ has_client_cert_data = status.key?('clientCertificateData')
+ has_client_key_data = status.key?('clientKeyData')
+
+ if has_client_cert_data && !has_client_key_data
+ raise 'exec plugin didn\'t return client key data'
+ end
+
+ if !has_client_cert_data && has_client_key_data
+ raise 'exec plugin didn\'t return client certificate data'
+ end
+
+ has_client_cert_data && has_client_key_data
+ end
+
+ def validate_credentials_status(status)
+ raise 'exec plugin didn\'t return a status field' if status.nil?
+
+ has_client_credentials = validate_client_credentials_status(status)
+ has_token = status.key?('token')
+
+ if has_client_credentials && has_token
+ raise 'exec plugin returned both token and client data'
+ end
+
+ return if has_client_credentials || has_token
+
+ raise 'exec plugin didn\'t return a token or client data' unless has_token
+ end
+
+ def validate_credentials(opts, creds)
+ # out should have ExecCredential structure
+ raise 'invalid credentials' if creds.nil?
+
+ # Verify apiVersion?
+ api_version = opts['apiVersion']
+ if api_version && api_version != creds['apiVersion']
+ raise "exec plugin is configured to use API version #{api_version}, " \
+ "plugin returned version #{creds['apiVersion']}"
+ end
+
+ validate_credentials_status(creds['status'])
+ end
+
+ # Transform name/value pairs to hash
+ def map_env(env)
+ return {} unless env
+
+ Hash[env.map { |e| [e['name'], e['value']] }]
+ end
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/lib/kubeclient/gcp_auth_provider.rb b/vendor/gems/kubeclient/lib/kubeclient/gcp_auth_provider.rb
new file mode 100644
index 00000000000..b28e54bfd88
--- /dev/null
+++ b/vendor/gems/kubeclient/lib/kubeclient/gcp_auth_provider.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'kubeclient/google_application_default_credentials'
+require 'kubeclient/gcp_command_credentials'
+
+module Kubeclient
+ # Handle different ways to get a bearer token for Google Cloud Platform.
+ class GCPAuthProvider
+ class << self
+ def token(config)
+ if config.key?('cmd-path')
+ Kubeclient::GCPCommandCredentials.token(config)
+ else
+ Kubeclient::GoogleApplicationDefaultCredentials.token
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/lib/kubeclient/gcp_command_credentials.rb b/vendor/gems/kubeclient/lib/kubeclient/gcp_command_credentials.rb
new file mode 100644
index 00000000000..9c68c1a2847
--- /dev/null
+++ b/vendor/gems/kubeclient/lib/kubeclient/gcp_command_credentials.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Kubeclient
+ # Generates a bearer token for Google Cloud Platform.
+ class GCPCommandCredentials
+ class << self
+ def token(config)
+ require 'open3'
+ require 'shellwords'
+ require 'json'
+ require 'jsonpath'
+
+ cmd = config['cmd-path']
+ args = config['cmd-args']
+ token_key = config['token-key']
+
+ out, err, st = Open3.capture3(cmd, *args.split(' '))
+
+ raise "exec command failed: #{err}" unless st.success?
+
+ extract_token(out, token_key)
+ end
+
+ private
+
+ def extract_token(output, token_key)
+ JsonPath.on(output, token_key.gsub(/^{|}$/, '')).first
+ end
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/lib/kubeclient/google_application_default_credentials.rb b/vendor/gems/kubeclient/lib/kubeclient/google_application_default_credentials.rb
new file mode 100644
index 00000000000..78f99ec9f32
--- /dev/null
+++ b/vendor/gems/kubeclient/lib/kubeclient/google_application_default_credentials.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Kubeclient
+ # Get a bearer token from the Google's application default credentials.
+ class GoogleApplicationDefaultCredentials
+ class GoogleDependencyError < LoadError # rubocop:disable Lint/InheritException
+ end
+
+ class << self
+ def token
+ begin
+ require 'googleauth'
+ rescue LoadError => e
+ raise GoogleDependencyError,
+ 'Error requiring googleauth gem. Kubeclient itself does not include the ' \
+ 'googleauth gem. To support auth-provider gcp, you must include it in your ' \
+ "calling application. Failed with: #{e.message}"
+ end
+
+ scopes = [
+ 'https://www.googleapis.com/auth/cloud-platform',
+ 'https://www.googleapis.com/auth/userinfo.email'
+ ]
+
+ authorization = Google::Auth.get_application_default(scopes)
+ authorization.apply({})
+ authorization.access_token
+ end
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/lib/kubeclient/http_error.rb b/vendor/gems/kubeclient/lib/kubeclient/http_error.rb
new file mode 100644
index 00000000000..121368c2f17
--- /dev/null
+++ b/vendor/gems/kubeclient/lib/kubeclient/http_error.rb
@@ -0,0 +1,25 @@
+# TODO: remove this on next major version bump
+# Deprected http exception
+class KubeException < StandardError
+ attr_reader :error_code, :message, :response
+
+ def initialize(error_code, message, response)
+ @error_code = error_code
+ @message = message
+ @response = response
+ end
+
+ def to_s
+ string = "HTTP status code #{@error_code}, #{@message}"
+ if @response.is_a?(RestClient::Response) && @response.request
+ string << " for #{@response.request.method.upcase} #{@response.request.url}"
+ end
+ string
+ end
+end
+
+module Kubeclient
+ # Exception that is raised when a http request fails
+ class HttpError < KubeException
+ end
+end
diff --git a/vendor/gems/kubeclient/lib/kubeclient/missing_kind_compatibility.rb b/vendor/gems/kubeclient/lib/kubeclient/missing_kind_compatibility.rb
new file mode 100644
index 00000000000..ec88960a546
--- /dev/null
+++ b/vendor/gems/kubeclient/lib/kubeclient/missing_kind_compatibility.rb
@@ -0,0 +1,68 @@
+module Kubeclient
+ module Common
+ # Backward compatibility for old versions where kind is missing (e.g. OpenShift Enterprise 3.1)
+ class MissingKindCompatibility
+ MAPPING = {
+ 'bindings' => 'Binding',
+ 'componentstatuses' => 'ComponentStatus',
+ 'endpoints' => 'Endpoints',
+ 'events' => 'Event',
+ 'limitranges' => 'LimitRange',
+ 'namespaces' => 'Namespace',
+ 'nodes' => 'Node',
+ 'persistentvolumeclaims' => 'PersistentVolumeClaim',
+ 'persistentvolumes' => 'PersistentVolume',
+ 'pods' => 'Pod',
+ 'podtemplates' => 'PodTemplate',
+ 'replicationcontrollers' => 'ReplicationController',
+ 'resourcequotas' => 'ResourceQuota',
+ 'secrets' => 'Secret',
+ 'securitycontextconstraints' => 'SecurityContextConstraints',
+ 'serviceaccounts' => 'ServiceAccount',
+ 'services' => 'Service',
+ 'buildconfigs' => 'BuildConfig',
+ 'builds' => 'Build',
+ 'clusternetworks' => 'ClusterNetwork',
+ 'clusterpolicies' => 'ClusterPolicy',
+ 'clusterpolicybindings' => 'ClusterPolicyBinding',
+ 'clusterrolebindings' => 'ClusterRoleBinding',
+ 'clusterroles' => 'ClusterRole',
+ 'deploymentconfigrollbacks' => 'DeploymentConfigRollback',
+ 'deploymentconfigs' => 'DeploymentConfig',
+ 'generatedeploymentconfigs' => 'DeploymentConfig',
+ 'groups' => 'Group',
+ 'hostsubnets' => 'HostSubnet',
+ 'identities' => 'Identity',
+ 'images' => 'Image',
+ 'imagestreamimages' => 'ImageStreamImage',
+ 'imagestreammappings' => 'ImageStreamMapping',
+ 'imagestreams' => 'ImageStream',
+ 'imagestreamtags' => 'ImageStreamTag',
+ 'localresourceaccessreviews' => 'LocalResourceAccessReview',
+ 'localsubjectaccessreviews' => 'LocalSubjectAccessReview',
+ 'netnamespaces' => 'NetNamespace',
+ 'oauthaccesstokens' => 'OAuthAccessToken',
+ 'oauthauthorizetokens' => 'OAuthAuthorizeToken',
+ 'oauthclientauthorizations' => 'OAuthClientAuthorization',
+ 'oauthclients' => 'OAuthClient',
+ 'policies' => 'Policy',
+ 'policybindings' => 'PolicyBinding',
+ 'processedtemplates' => 'Template',
+ 'projectrequests' => 'ProjectRequest',
+ 'projects' => 'Project',
+ 'resourceaccessreviews' => 'ResourceAccessReview',
+ 'rolebindings' => 'RoleBinding',
+ 'roles' => 'Role',
+ 'routes' => 'Route',
+ 'subjectaccessreviews' => 'SubjectAccessReview',
+ 'templates' => 'Template',
+ 'useridentitymappings' => 'UserIdentityMapping',
+ 'users' => 'User'
+ }.freeze
+
+ def self.resource_kind(name)
+ MAPPING[name]
+ end
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/lib/kubeclient/oidc_auth_provider.rb b/vendor/gems/kubeclient/lib/kubeclient/oidc_auth_provider.rb
new file mode 100644
index 00000000000..ffdfd7e2a5d
--- /dev/null
+++ b/vendor/gems/kubeclient/lib/kubeclient/oidc_auth_provider.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module Kubeclient
+ # Uses OIDC id-tokens and refreshes them if they are stale.
+ class OIDCAuthProvider
+ class OpenIDConnectDependencyError < LoadError # rubocop:disable Lint/InheritException
+ end
+
+ class << self
+ def token(provider_config)
+ begin
+ require 'openid_connect'
+ rescue LoadError => e
+ raise OpenIDConnectDependencyError,
+ 'Error requiring openid_connect gem. Kubeclient itself does not include the ' \
+ 'openid_connect gem. To support auth-provider oidc, you must include it in your ' \
+ "calling application. Failed with: #{e.message}"
+ end
+
+ issuer_url = provider_config['idp-issuer-url']
+ discovery = OpenIDConnect::Discovery::Provider::Config.discover! issuer_url
+
+ if provider_config.key? 'id-token'
+ return provider_config['id-token'] unless expired?(provider_config['id-token'], discovery)
+ end
+
+ client = OpenIDConnect::Client.new(
+ identifier: provider_config['client-id'],
+ secret: provider_config['client-secret'],
+ authorization_endpoint: discovery.authorization_endpoint,
+ token_endpoint: discovery.token_endpoint,
+ userinfo_endpoint: discovery.userinfo_endpoint
+ )
+ client.refresh_token = provider_config['refresh-token']
+ client.access_token!.id_token
+ end
+
+ def expired?(id_token, discovery)
+ decoded_token = OpenIDConnect::ResponseObject::IdToken.decode(
+ id_token,
+ discovery.jwks
+ )
+ # If token expired or expiring within 60 seconds
+ Time.now.to_i + 60 > decoded_token.exp.to_i
+ rescue JSON::JWK::Set::KidNotFound
+ # Token cannot be verified: the kid it was signed with is not available for discovery
+ # Consider it expired and fetch a new one.
+ true
+ end
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/lib/kubeclient/resource.rb b/vendor/gems/kubeclient/lib/kubeclient/resource.rb
new file mode 100644
index 00000000000..08a50c3fe4f
--- /dev/null
+++ b/vendor/gems/kubeclient/lib/kubeclient/resource.rb
@@ -0,0 +1,11 @@
+require 'recursive_open_struct'
+
+module Kubeclient
+ # Represents all the objects returned by Kubeclient
+ class Resource < RecursiveOpenStruct
+ def initialize(hash = nil, args = {})
+ args[:recurse_over_arrays] = true
+ super(hash, args)
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/lib/kubeclient/resource_not_found_error.rb b/vendor/gems/kubeclient/lib/kubeclient/resource_not_found_error.rb
new file mode 100644
index 00000000000..045a83642d7
--- /dev/null
+++ b/vendor/gems/kubeclient/lib/kubeclient/resource_not_found_error.rb
@@ -0,0 +1,4 @@
+module Kubeclient
+ class ResourceNotFoundError < HttpError
+ end
+end
diff --git a/vendor/gems/kubeclient/lib/kubeclient/version.rb b/vendor/gems/kubeclient/lib/kubeclient/version.rb
new file mode 100644
index 00000000000..bff50841794
--- /dev/null
+++ b/vendor/gems/kubeclient/lib/kubeclient/version.rb
@@ -0,0 +1,4 @@
+# Kubernetes REST-API Client
+module Kubeclient
+ VERSION = '4.9.4-gitlab1'.freeze
+end
diff --git a/vendor/gems/kubeclient/lib/kubeclient/watch_stream.rb b/vendor/gems/kubeclient/lib/kubeclient/watch_stream.rb
new file mode 100644
index 00000000000..ef676660d53
--- /dev/null
+++ b/vendor/gems/kubeclient/lib/kubeclient/watch_stream.rb
@@ -0,0 +1,97 @@
+require 'json'
+require 'http'
+module Kubeclient
+ module Common
+ # HTTP Stream used to watch changes on entities
+ class WatchStream
+ def initialize(uri, http_options, formatter:)
+ @uri = uri
+ @http_client = nil
+ @http_options = http_options
+ @http_options[:http_max_redirects] ||= Kubeclient::Client::DEFAULT_HTTP_MAX_REDIRECTS
+ @formatter = formatter
+ end
+
+ def each
+ @finished = false
+
+ @http_client = build_client
+ response = @http_client.request(:get, @uri, build_client_options)
+ unless response.code < 300
+ raise Kubeclient::HttpError.new(response.code, response.reason, response)
+ end
+
+ buffer = ''
+ response.body.each do |chunk|
+ buffer << chunk
+ while (line = buffer.slice!(/.+\n/))
+ yield @formatter.call(line.chomp)
+ end
+ end
+ rescue StandardError
+ raise unless @finished
+ end
+
+ def finish
+ @finished = true
+ @http_client.close unless @http_client.nil?
+ end
+
+ private
+
+ def max_hops
+ @http_options[:http_max_redirects] + 1
+ end
+
+ def follow_option
+ if max_hops > 1
+ { max_hops: max_hops }
+ else
+ # i.e. Do not follow redirects as we have set http_max_redirects to 0
+ # Setting `{ max_hops: 1 }` does not work FWIW
+ false
+ end
+ end
+
+ def build_client
+ client = HTTP::Client.new(follow: follow_option)
+
+ if @http_options[:basic_auth_user] && @http_options[:basic_auth_password]
+ client = client.basic_auth(
+ user: @http_options[:basic_auth_user],
+ pass: @http_options[:basic_auth_password]
+ )
+ end
+
+ client
+ end
+
+ def using_proxy
+ proxy = @http_options[:http_proxy_uri]
+ return nil unless proxy
+ p_uri = URI.parse(proxy)
+ {
+ proxy_address: p_uri.hostname,
+ proxy_port: p_uri.port,
+ proxy_username: p_uri.user,
+ proxy_password: p_uri.password
+ }
+ end
+
+ def build_client_options
+ client_options = {
+ headers: @http_options[:headers],
+ proxy: using_proxy
+ }
+ if @http_options[:ssl]
+ client_options[:ssl] = @http_options[:ssl]
+ socket_option = :ssl_socket_class
+ else
+ socket_option = :socket_class
+ end
+ client_options[socket_option] = @http_options[socket_option] if @http_options[socket_option]
+ client_options
+ end
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/test/cassettes/kubernetes_guestbook.yml b/vendor/gems/kubeclient/test/cassettes/kubernetes_guestbook.yml
new file mode 100644
index 00000000000..3829add6d75
--- /dev/null
+++ b/vendor/gems/kubeclient/test/cassettes/kubernetes_guestbook.yml
@@ -0,0 +1,879 @@
+---
+http_interactions:
+- request:
+ method: delete
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 404
+ message: Not Found
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '253'
+ body:
+ encoding: UTF-8
+ string: |-
+ {
+ "kind": "Status",
+ "apiVersion": "v1",
+ "metadata": {},
+ "status": "Failure",
+ "message": "namespaces \"kubeclient-ns\" not found",
+ "reason": "NotFound",
+ "details": {
+ "name": "kubeclient-ns",
+ "kind": "namespaces"
+ },
+ "code": 404
+ }
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: delete
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services/guestbook
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 404
+ message: Not Found
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '239'
+ body:
+ encoding: UTF-8
+ string: |-
+ {
+ "kind": "Status",
+ "apiVersion": "v1",
+ "metadata": {},
+ "status": "Failure",
+ "message": "service \"guestbook\" not found",
+ "reason": "NotFound",
+ "details": {
+ "name": "guestbook",
+ "kind": "service"
+ },
+ "code": 404
+ }
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: delete
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services/redis-master
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 404
+ message: Not Found
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '245'
+ body:
+ encoding: UTF-8
+ string: |-
+ {
+ "kind": "Status",
+ "apiVersion": "v1",
+ "metadata": {},
+ "status": "Failure",
+ "message": "service \"redis-master\" not found",
+ "reason": "NotFound",
+ "details": {
+ "name": "redis-master",
+ "kind": "service"
+ },
+ "code": 404
+ }
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: delete
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services/redis-slave
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 404
+ message: Not Found
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '243'
+ body:
+ encoding: UTF-8
+ string: |-
+ {
+ "kind": "Status",
+ "apiVersion": "v1",
+ "metadata": {},
+ "status": "Failure",
+ "message": "service \"redis-slave\" not found",
+ "reason": "NotFound",
+ "details": {
+ "name": "redis-slave",
+ "kind": "service"
+ },
+ "code": 404
+ }
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: delete
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers/guestbook
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 404
+ message: Not Found
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '269'
+ body:
+ encoding: UTF-8
+ string: |-
+ {
+ "kind": "Status",
+ "apiVersion": "v1",
+ "metadata": {},
+ "status": "Failure",
+ "message": "replicationControllers \"guestbook\" not found",
+ "reason": "NotFound",
+ "details": {
+ "name": "guestbook",
+ "kind": "replicationControllers"
+ },
+ "code": 404
+ }
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: delete
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers/redis-master
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 404
+ message: Not Found
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '275'
+ body:
+ encoding: UTF-8
+ string: |-
+ {
+ "kind": "Status",
+ "apiVersion": "v1",
+ "metadata": {},
+ "status": "Failure",
+ "message": "replicationControllers \"redis-master\" not found",
+ "reason": "NotFound",
+ "details": {
+ "name": "redis-master",
+ "kind": "replicationControllers"
+ },
+ "code": 404
+ }
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: delete
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers/redis-slave
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 404
+ message: Not Found
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '273'
+ body:
+ encoding: UTF-8
+ string: |-
+ {
+ "kind": "Status",
+ "apiVersion": "v1",
+ "metadata": {},
+ "status": "Failure",
+ "message": "replicationControllers \"redis-slave\" not found",
+ "reason": "NotFound",
+ "details": {
+ "name": "redis-slave",
+ "kind": "replicationControllers"
+ },
+ "code": 404
+ }
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: post
+ uri: http://10.35.0.23:8080/api/v1/namespaces
+ body:
+ encoding: UTF-8
+ string: '{"metadata":{"name":"kubeclient-ns"},"kind":"Namespace","apiVersion":"v1"}'
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Length:
+ - '74'
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 201
+ message: Created
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '297'
+ body:
+ encoding: UTF-8
+ string: '{"kind":"Namespace","apiVersion":"v1","metadata":{"name":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns","uid":"f41e6b27-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"534","creationTimestamp":"2015-08-09T10:03:59Z"},"spec":{"finalizers":["kubernetes"]},"status":{"phase":"Active"}}'
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: post
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services
+ body:
+ encoding: UTF-8
+ string: '{"metadata":{"namespace":"kubeclient-ns","labels":{"name":"guestbook"},"name":"guestbook"},"spec":{"selector":{"app":"guestbook"},"ports":[{"port":3000,"targetPort":"http-server"}]},"type":"LoadBalancer","kind":"Service","apiVersion":"v1"}'
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Length:
+ - '239'
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 201
+ message: Created
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '521'
+ body:
+ encoding: UTF-8
+ string: '{"kind":"Service","apiVersion":"v1","metadata":{"name":"guestbook","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/services/guestbook","uid":"f42187e1-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"538","creationTimestamp":"2015-08-09T10:03:59Z","labels":{"name":"guestbook"}},"spec":{"ports":[{"protocol":"TCP","port":3000,"targetPort":"http-server","nodePort":0}],"selector":{"app":"guestbook"},"clusterIP":"10.0.0.80","type":"ClusterIP","sessionAffinity":"None"},"status":{"loadBalancer":{}}}'
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: post
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services
+ body:
+ encoding: UTF-8
+ string: '{"metadata":{"namespace":"kubeclient-ns","labels":{"app":"redis","role":"master"},"name":"redis-master"},"spec":{"selector":{"app":"redis","role":"master"},"ports":[{"port":6379,"targetPort":"redis-server"}]},"kind":"Service","apiVersion":"v1"}'
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Length:
+ - '244'
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 201
+ message: Created
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '552'
+ body:
+ encoding: UTF-8
+ string: '{"kind":"Service","apiVersion":"v1","metadata":{"name":"redis-master","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/services/redis-master","uid":"f423bf8b-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"542","creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"redis","role":"master"}},"spec":{"ports":[{"protocol":"TCP","port":6379,"targetPort":"redis-server","nodePort":0}],"selector":{"app":"redis","role":"master"},"clusterIP":"10.0.0.140","type":"ClusterIP","sessionAffinity":"None"},"status":{"loadBalancer":{}}}'
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: post
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services
+ body:
+ encoding: UTF-8
+ string: '{"metadata":{"namespace":"kubeclient-ns","labels":{"app":"redis","role":"slave"},"name":"redis-slave"},"spec":{"selector":{"app":"redis","role":"slave"},"ports":[{"port":6379,"targetPort":"redis-server"}]},"kind":"Service","apiVersion":"v1"}'
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Length:
+ - '241'
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 201
+ message: Created
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '548'
+ body:
+ encoding: UTF-8
+ string: '{"kind":"Service","apiVersion":"v1","metadata":{"name":"redis-slave","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/services/redis-slave","uid":"f4264678-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"545","creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"redis","role":"slave"}},"spec":{"ports":[{"protocol":"TCP","port":6379,"targetPort":"redis-server","nodePort":0}],"selector":{"app":"redis","role":"slave"},"clusterIP":"10.0.0.154","type":"ClusterIP","sessionAffinity":"None"},"status":{"loadBalancer":{}}}'
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: post
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers
+ body:
+ encoding: UTF-8
+ string: '{"metadata":{"namespace":"kubeclient-ns","labels":{"app":"guestbook","role":"slave"},"name":"guestbook"},"spec":{"selector":{"app":"guestbook"},"template":{"metadata":{"labels":{"app":"guestbook"}},"spec":{"containers":[{"name":"guestbook","image":"kubernetes/guestbook:v2","ports":[{"name":"http-server","containerPort":3000}]}]}},"replicas":3},"kind":"ReplicationController","apiVersion":"v1"}'
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Length:
+ - '395'
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 201
+ message: Created
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '815'
+ body:
+ encoding: UTF-8
+ string: '{"kind":"ReplicationController","apiVersion":"v1","metadata":{"name":"guestbook","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/replicationcontrollers/guestbook","uid":"f4287784-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"547","generation":1,"creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"guestbook","role":"slave"}},"spec":{"replicas":3,"selector":{"app":"guestbook"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"guestbook"}},"spec":{"containers":[{"name":"guestbook","image":"kubernetes/guestbook:v2","ports":[{"name":"http-server","containerPort":3000,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","dnsPolicy":"ClusterFirst"}}},"status":{"replicas":0}}'
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: post
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers
+ body:
+ encoding: UTF-8
+ string: '{"metadata":{"namespace":"kubeclient-ns","labels":{"app":"redis","role":"master"},"name":"redis-master"},"spec":{"selector":{"app":"redis","role":"master"},"template":{"metadata":{"labels":{"app":"redis","role":"master"}},"spec":{"containers":[{"name":"redis-master","image":"redis","ports":[{"name":"redis-server","containerPort":6379}]}]}},"replicas":1},"kind":"ReplicationController","apiVersion":"v1"}'
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Length:
+ - '405'
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 201
+ message: Created
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '828'
+ body:
+ encoding: UTF-8
+ string: '{"kind":"ReplicationController","apiVersion":"v1","metadata":{"name":"redis-master","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/replicationcontrollers/redis-master","uid":"f42a9800-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"558","generation":1,"creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"redis","role":"master"}},"spec":{"replicas":1,"selector":{"app":"redis","role":"master"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"redis","role":"master"}},"spec":{"containers":[{"name":"redis-master","image":"redis","ports":[{"name":"redis-server","containerPort":6379,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","dnsPolicy":"ClusterFirst"}}},"status":{"replicas":0}}'
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: post
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers
+ body:
+ encoding: UTF-8
+ string: '{"metadata":{"namespace":"kubeclient-ns","labels":{"app":"redis","role":"slave"},"name":"redis-slave"},"spec":{"selector":{"app":"redis","role":"slave"},"template":{"metadata":{"labels":{"app":"redis","role":"slave"}},"spec":{"containers":[{"name":"redis-slave","image":"kubernetes/redis-slave:v2","ports":[{"name":"redis-server","containerPort":6379}]}]}},"replicas":2},"kind":"ReplicationController","apiVersion":"v1"}'
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Length:
+ - '420'
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 201
+ message: Created
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '842'
+ body:
+ encoding: UTF-8
+ string: '{"kind":"ReplicationController","apiVersion":"v1","metadata":{"name":"redis-slave","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/replicationcontrollers/redis-slave","uid":"f42e1d09-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"567","generation":1,"creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"redis","role":"slave"}},"spec":{"replicas":2,"selector":{"app":"redis","role":"slave"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"redis","role":"slave"}},"spec":{"containers":[{"name":"redis-slave","image":"kubernetes/redis-slave:v2","ports":[{"name":"redis-server","containerPort":6379,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","dnsPolicy":"ClusterFirst"}}},"status":{"replicas":0}}'
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: get
+ uri: http://10.35.0.23:8080/api/v1/namespaces
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '629'
+ body:
+ encoding: UTF-8
+ string: '{"kind":"NamespaceList","apiVersion":"v1","metadata":{"selfLink":"/api/v1/namespaces","resourceVersion":"570"},"items":[{"metadata":{"name":"default","selfLink":"/api/v1/namespaces/default","uid":"37360c82-3e77-11e5-a75a-18037327aaeb","resourceVersion":"6","creationTimestamp":"2015-08-09T09:15:45Z"},"spec":{"finalizers":["kubernetes"]},"status":{"phase":"Active"}},{"metadata":{"name":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns","uid":"f41e6b27-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"534","creationTimestamp":"2015-08-09T10:03:59Z"},"spec":{"finalizers":["kubernetes"]},"status":{"phase":"Active"}}]}'
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: get
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '1661'
+ body:
+ encoding: UTF-8
+ string: '{"kind":"ServiceList","apiVersion":"v1","metadata":{"selfLink":"/api/v1/namespaces/kubeclient-ns/services","resourceVersion":"571"},"items":[{"metadata":{"name":"guestbook","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/services/guestbook","uid":"f42187e1-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"538","creationTimestamp":"2015-08-09T10:03:59Z","labels":{"name":"guestbook"}},"spec":{"ports":[{"protocol":"TCP","port":3000,"targetPort":"http-server","nodePort":0}],"selector":{"app":"guestbook"},"clusterIP":"10.0.0.80","type":"ClusterIP","sessionAffinity":"None"},"status":{"loadBalancer":{}}},{"metadata":{"name":"redis-master","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/services/redis-master","uid":"f423bf8b-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"542","creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"redis","role":"master"}},"spec":{"ports":[{"protocol":"TCP","port":6379,"targetPort":"redis-server","nodePort":0}],"selector":{"app":"redis","role":"master"},"clusterIP":"10.0.0.140","type":"ClusterIP","sessionAffinity":"None"},"status":{"loadBalancer":{}}},{"metadata":{"name":"redis-slave","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/services/redis-slave","uid":"f4264678-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"545","creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"redis","role":"slave"}},"spec":{"ports":[{"protocol":"TCP","port":6379,"targetPort":"redis-server","nodePort":0}],"selector":{"app":"redis","role":"slave"},"clusterIP":"10.0.0.154","type":"ClusterIP","sessionAffinity":"None"},"status":{"loadBalancer":{}}}]}'
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: get
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Transfer-Encoding:
+ - chunked
+ body:
+ encoding: UTF-8
+ string: '{"kind":"ReplicationControllerList","apiVersion":"v1","metadata":{"selfLink":"/api/v1/namespaces/kubeclient-ns/replicationcontrollers","resourceVersion":"571"},"items":[{"metadata":{"name":"guestbook","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/replicationcontrollers/guestbook","uid":"f4287784-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"557","generation":1,"creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"guestbook","role":"slave"}},"spec":{"replicas":3,"selector":{"app":"guestbook"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"guestbook"}},"spec":{"containers":[{"name":"guestbook","image":"kubernetes/guestbook:v2","ports":[{"name":"http-server","containerPort":3000,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","dnsPolicy":"ClusterFirst"}}},"status":{"replicas":3,"observedGeneration":1}},{"metadata":{"name":"redis-master","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/replicationcontrollers/redis-master","uid":"f42a9800-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"565","generation":1,"creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"redis","role":"master"}},"spec":{"replicas":1,"selector":{"app":"redis","role":"master"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"redis","role":"master"}},"spec":{"containers":[{"name":"redis-master","image":"redis","ports":[{"name":"redis-server","containerPort":6379,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","dnsPolicy":"ClusterFirst"}}},"status":{"replicas":0,"observedGeneration":1}},{"metadata":{"name":"redis-slave","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/replicationcontrollers/redis-slave","uid":"f42e1d09-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"567","generation":1,"creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"redis","role":"slave"}},"spec":{"replicas":2,"selector":{"app":"redis","role":"slave"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"redis","role":"slave"}},"spec":{"containers":[{"name":"redis-slave","image":"kubernetes/redis-slave:v2","ports":[{"name":"redis-server","containerPort":6379,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","dnsPolicy":"ClusterFirst"}}},"status":{"replicas":0}}]}'
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: delete
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services/guestbook
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '100'
+ body:
+ encoding: UTF-8
+ string: |-
+ {
+ "kind": "Status",
+ "apiVersion": "v1",
+ "metadata": {},
+ "status": "Success",
+ "code": 200
+ }
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: delete
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services/redis-master
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '100'
+ body:
+ encoding: UTF-8
+ string: |-
+ {
+ "kind": "Status",
+ "apiVersion": "v1",
+ "metadata": {},
+ "status": "Success",
+ "code": 200
+ }
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: delete
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services/redis-slave
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '100'
+ body:
+ encoding: UTF-8
+ string: |-
+ {
+ "kind": "Status",
+ "apiVersion": "v1",
+ "metadata": {},
+ "status": "Success",
+ "code": 200
+ }
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: delete
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers/guestbook
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '100'
+ body:
+ encoding: UTF-8
+ string: |-
+ {
+ "kind": "Status",
+ "apiVersion": "v1",
+ "metadata": {},
+ "status": "Success",
+ "code": 200
+ }
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: delete
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers/redis-master
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '100'
+ body:
+ encoding: UTF-8
+ string: |-
+ {
+ "kind": "Status",
+ "apiVersion": "v1",
+ "metadata": {},
+ "status": "Success",
+ "code": 200
+ }
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: delete
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers/redis-slave
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '100'
+ body:
+ encoding: UTF-8
+ string: |-
+ {
+ "kind": "Status",
+ "apiVersion": "v1",
+ "metadata": {},
+ "status": "Success",
+ "code": 200
+ }
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: delete
+ uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - '*/*; q=0.5, application/xml'
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Sun, 09 Aug 2015 10:03:59 GMT
+ Content-Length:
+ - '345'
+ body:
+ encoding: UTF-8
+ string: '{"kind":"Namespace","apiVersion":"v1","metadata":{"name":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns","uid":"f41e6b27-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"584","creationTimestamp":"2015-08-09T10:03:59Z","deletionTimestamp":"2015-08-09T10:03:59Z"},"spec":{"finalizers":["kubernetes"]},"status":{"phase":"Terminating"}}'
+ http_version:
+ recorded_at: Sun, 09 Aug 2015 10:00:02 GMT
+- request:
+ method: get
+ uri: http://10.35.0.23:8080/api/v1
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ User-Agent:
+ - rest-client/2.0.0 (linux-gnu x86_64) ruby/2.3.0p0
+ Host:
+ - localhost:8080
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Content-Type:
+ - application/json
+ Date:
+ - Mon, 29 Aug 2016 15:51:30 GMT
+ Transfer-Encoding:
+ - chunked
+ body:
+ encoding: UTF-8
+ string: '{"kind":"APIResourceList","groupVersion":"v1","resources":[{"name":"bindings","namespaced":true,"kind":"Binding"},{"name":"componentstatuses","namespaced":false,"kind":"ComponentStatus"},{"name":"configmaps","namespaced":true,"kind":"ConfigMap"},{"name":"endpoints","namespaced":true,"kind":"Endpoints"},{"name":"events","namespaced":true,"kind":"Event"},{"name":"limitranges","namespaced":true,"kind":"LimitRange"},{"name":"namespaces","namespaced":false,"kind":"Namespace"},{"name":"namespaces/finalize","namespaced":false,"kind":"Namespace"},{"name":"namespaces/status","namespaced":false,"kind":"Namespace"},{"name":"nodes","namespaced":false,"kind":"Node"},{"name":"nodes/proxy","namespaced":false,"kind":"Node"},{"name":"nodes/status","namespaced":false,"kind":"Node"},{"name":"persistentvolumeclaims","namespaced":true,"kind":"PersistentVolumeClaim"},{"name":"persistentvolumeclaims/status","namespaced":true,"kind":"PersistentVolumeClaim"},{"name":"persistentvolumes","namespaced":false,"kind":"PersistentVolume"},{"name":"persistentvolumes/status","namespaced":false,"kind":"PersistentVolume"},{"name":"pods","namespaced":true,"kind":"Pod"},{"name":"pods/attach","namespaced":true,"kind":"Pod"},{"name":"pods/binding","namespaced":true,"kind":"Binding"},{"name":"pods/exec","namespaced":true,"kind":"Pod"},{"name":"pods/log","namespaced":true,"kind":"Pod"},{"name":"pods/portforward","namespaced":true,"kind":"Pod"},{"name":"pods/proxy","namespaced":true,"kind":"Pod"},{"name":"pods/status","namespaced":true,"kind":"Pod"},{"name":"podtemplates","namespaced":true,"kind":"PodTemplate"},{"name":"replicationcontrollers","namespaced":true,"kind":"ReplicationController"},{"name":"replicationcontrollers/scale","namespaced":true,"kind":"Scale"},{"name":"replicationcontrollers/status","namespaced":true,"kind":"ReplicationController"},{"name":"resourcequotas","namespaced":true,"kind":"ResourceQuota"},{"name":"resourcequotas/status","namespaced":true,"kind":"ResourceQuota"},{"name":"secrets","namespaced":true,"kind":"Secret"},{"name":"serviceaccounts","namespaced":true,"kind":"ServiceAccount"},{"name":"services","namespaced":true,"kind":"Service"},{"name":"services/proxy","namespaced":true,"kind":"Service"},{"name":"services/status","namespaced":true,"kind":"Service"}]}
+
+'
+ http_version:
+ recorded_at: Mon, 29 Aug 2016 15:51:30 GMT
+recorded_with: VCR 3.0.3
diff --git a/vendor/gems/kubeclient/test/config/allinone.kubeconfig b/vendor/gems/kubeclient/test/config/allinone.kubeconfig
new file mode 100644
index 00000000000..f06db6af123
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/allinone.kubeconfig
@@ -0,0 +1,21 @@
+
+apiVersion: v1
+clusters:
+- cluster:
+ server: https://localhost:6443
+ certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvekNDQWVlZ0F3SUJBZ0lUUGJmcHkyOWFCRzY3Q2hSZEI2bEplZ1RrbURBTkJna3Foa2lHOXcwQkFRc0YKQURBWU1SWXdGQVlEVlFRREV3MXJkV0psY201bGRHVnpMV05oTUI0WERUSXlNREl5TVRBNU1ESXdNRm9YRFRNeQpNREl4T1RBNU1ESXdNRm93R0RFV01CUUdBMVVFQXhNTmEzVmlaWEp1WlhSbGN5MWpZVENDQVNJd0RRWUpLb1pJCmh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTUxqWjJjRkJhbHpvaW1hRWNwVDlKejJKbWRnc0hNT2FnVmQKSXQ3T1FwVHdEWjNucElJQ1ZwZ3VFaDl4dG92UjhtOC9IWU0rL2E0dk1RSFQrM3A4SFBqaURhUllHZzdPWjlMKwpGcC85emhCdWlhSXZnOForQmJ5czlROVV1ajZWRXdmRkpCY05INlRtemRpRGdRVXM1L2srNi92dHVKNHlzM3NECktrQU94cVBYRGFCb0FObkxwSXhkSU1RRGNXU0xGQTB3bUZoZFpKcTNLRUFvSnBFTDBXWW8xWlJCVjNpSDc3eWYKc0RiTjFPQnUydk5uUlorRHJWMFpKNUFwbWJGWFBYOGk0S0phVzlsQ0I2MkZOMGo1WHNORG95VGVBVnBlc2ZOcwp6WXVmVnBCZHFOWkZrT0tnOWRpTXVUTWlrYTJhWWZEdWlWemRlYkRnY3A5YU1sb0t0YkVDQXdFQUFhTkNNRUF3CkRnWURWUjBQQVFIL0JBUURBZ0VHTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkJkT2l5Z0MKTGN1SnJxOHJOYTF4QURyNVNwN0NNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUURDeTRJbGhBU2g2QnI1WEVjSQpUcFA1VGhEMU95UnpRbnNQZTZQMXFnV1Aza0JYSy9BY3NTbCtWR3RhWnAyb0VoSm9VbnN6N2tFOHlXM2dLK1BBCjUxelk0YUhUaUY5eGt5ZDV6T0NBR0IrY2ZwOVlzK3N6V3p5dTBRUTlJQmpKNCtlRGpnN1cwL1MrQk0yUW4xaUwKalRGSWUyQmRmK1EvSjI0L3Eza3NUWEsxN1VOdW4xNHZEUnNKZ3NOY3JGdC9ydW1mSFB4MXl0d3NpcUt5RUtWNwprRnhTd2EzZDgvQXZoR2dGcFBtZlJqVTdnQUpDRmNIejUwMXpoaTJhNkw1VFlCVGVjVlJicVpvZUhpWjBZTldJCmlzNWc0Vm1WQitCeE1BTTJXRWQyOXY0bC8zb0kxUGV5OXJ2dDdOSnFTZTFpbTl1cVpnVkRlZy92UDh6S3MvZEYKWll3OAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
+ name: local
+contexts:
+- context:
+ cluster: local
+ namespace: default
+ user: user
+ name: Default
+current-context: Default
+kind: Config
+preferences: {}
+users:
+- name: user
+ user:
+ client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURXVENDQWtHZ0F3SUJBZ0lVS08xZVJtWG1DYyt2dk0rTG9CeVpFMElNR0Q4d0RRWUpLb1pJaHZjTkFRRUwKQlFBd0dERVdNQlFHQTFVRUF4TU5hM1ZpWlhKdVpYUmxjeTFqWVRBZUZ3MHlNakF5TWpFd09UQXlNREJhRncweQpNekF5TWpFd09UQXlNREJhTURReEZ6QVZCZ05WQkFvVERuTjVjM1JsYlRwdFlYTjBaWEp6TVJrd0Z3WURWUVFECkV4QnJkV0psY201bGRHVnpMV0ZrYldsdU1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0MKQVFFQXdodzF6SmFBWVcwa0ljbjVtV3lYTm03SVlEYStRTXpIM0hWWnJnd1l5Y0NaWlA5MGJZSmo0VEYrKyszbgpQTVoydU1mWkVYY21OcEdYQ0lxek5ZSlF6dlQrYTF6Q011VVQ5K3YxZzM0My9WYVlweFJ6R3VlQ1ZGK3B2Qi9nCmpJMFU0bDFZbUpJRWs1WGNETTl4RStob1Vrd3FWYWhNbXJ2c3E4aHluWWp6V0V0bDBtSitUTzg5LzFaandBdVEKdDRvaE5PMXBRdTNyNEhDdTNIR2JGU1RQSjZFNG94UmdkTXFIbHFzd3BHMCtaTFJYVmtvZ1VidGxJVUx0Y3RvVQpUNjdmaHNUaFJDWXUyQUFpbitMMjBncjNUY3A0VXRNV3RUY1o2NnZsTDJFcDZUa1dpdXRiMDRML0JvNDZWZnhPCnNLQkNkTjFqeXNPZVRQZ1RXS2gwODh2TTB3SURBUUFCbzM4d2ZUQU9CZ05WSFE4QkFmOEVCQU1DQmFBd0hRWUQKVlIwbEJCWXdGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUF3R0ExVWRFd0VCL3dRQ01BQXdIUVlEVlIwTwpCQllFRko2NzV6N0dDSXVKY2JHeEo3YkxJM0pBTDNCZU1COEdBMVVkSXdRWU1CYUFGQmRPaXlnQ0xjdUpycThyCk5hMXhBRHI1U3A3Q01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1hHQ2hlbytWVXhsRlJQRVhSU3B3NDgwbGIKMm12ZHpCNE8vck9JRzJIOXZ6Z2poeGJkQ0pOVmVka0d6bUhaNzVlRUh6djFFa0luZWJTR0xGejUvUDNCUVVYdwp4eXU4NGpOSmVyOVJlMmdlbVYwdDJtd3pIV0NmUUdMS0c0QUppN00rY3haNDlYaHVtWlJWa3c3aU9SRitxWU9qCmhTbG9CLzRrZnlxM1VhcHJLQ3ZocWpFc0tKcURuZHdsT3dLTFZNaWMzbFFETExYb0thNVRpdjZpaEJpQ1ZXQUkKWFV5NWwyZG9EdlpoemdFakxlUTBDR0hiNTl2Q3Zkb3RUYzBiN0hKNFp3MjFBTHoyQ0Y3eGt3WGpQNXVVSXE1Ngo2UG1RK2dwSFhrbGJOSklPRHRrZi85STRyTWxIVXplSzVEVllZNWtkeEpUemgybng0UjBpY0Z4MWpuYzAKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
+ client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBd2h3MXpKYUFZVzBrSWNuNW1XeVhObTdJWURhK1FNekgzSFZacmd3WXljQ1paUDkwCmJZSmo0VEYrKyszblBNWjJ1TWZaRVhjbU5wR1hDSXF6TllKUXp2VCthMXpDTXVVVDkrdjFnMzQzL1ZhWXB4UnoKR3VlQ1ZGK3B2Qi9nakkwVTRsMVltSklFazVYY0RNOXhFK2hvVWt3cVZhaE1tcnZzcThoeW5ZanpXRXRsMG1KKwpUTzg5LzFaandBdVF0NG9oTk8xcFF1M3I0SEN1M0hHYkZTVFBKNkU0b3hSZ2RNcUhscXN3cEcwK1pMUlhWa29nClVidGxJVUx0Y3RvVVQ2N2Zoc1RoUkNZdTJBQWluK0wyMGdyM1RjcDRVdE1XdFRjWjY2dmxMMkVwNlRrV2l1dGIKMDRML0JvNDZWZnhPc0tCQ2ROMWp5c09lVFBnVFdLaDA4OHZNMHdJREFRQUJBb0lCQVFEQlhHQ3JRSEQ2bkVJVgo5cURSR0w4NDFmcDgvWXRmK1o1T0dnZ1B2TFVrcE5zcEpOL1NCc1dBR2xJb204QnhaakgxdC82ZnkxVWhucjRaCklER002QmVmVWFYdlJTT2VsMXZnNkFoVnlISDF4MEdoamxsclA1c3dlV3NYbjVtTDZTNFlvR3dVNzcvblZLMHoKaGFGYTkzU1VKcE0xYU1XR2poVWd1amlTZlU0TGNMRFJWS0RFUk80OFhQOEZpQ0E2UEIySUpYUDBTOUJhTVZLUQpIQVJDV05HWDV6aU5hbzFyb0l2UFFORU1EVHRuV2JiNHo0U3ZSbXBjRjYwRS9mNEpwMDZTSDAwUU5lVHY3bjU3CkR6U1hZZGNxbGZMUjlRU1RkVmt0U094UThjSGFsVGN5NXNVMnh5RndjMVM5YVpaOG56UnBiRnUxVllGQ0lWY2YKRTRRVnYxVUJBb0dCQU1XZzRuWlRIaUd0aGY2S3l6T0draDFyMmtCU21WeTlCY1lqQjhUYnpRYnJOVXk3WUFsVAppN2VPbkYrdVRLY2FWYzV2blVyVjBsN3pEM1dTV3UxQVJiOUs2WnBWTDV3allObW0venVyUittSmM4TEtsN2Q2CjU0ejFjMTExVDVpaXZNMWJZUm5VME91akhEb1g1WkQ1V0dzeE1LYUh0TGFHV1gvZ0s0OWhQYmZ6QW9HQkFQdHgKVFdRSGlPcS91VnNrMFFuWWtLWkV3ZXpmZEMyZGFWSm9BeURBVjVnY1U0TnNONjJVS2ZnVHJhRCtDaG15SjVSNwpqT1NMOGJVWDMzUVFFdm1LOVdZWE1WQnd6MjhIclp5cHVCeVFFRWM4ZTl5eTRZUStSWVoxTDJrbUdkYWVaOE16CmlEZmZMclhieTdOWnA0eGNmTVpXWW1Md2RHRVNZbzhqenVjaTFLK2hBb0dCQUpESXprQmJvbTZQM3VQZHNRTGQKcXZ4TkFJY3hQRlA1MDFvV1hlRzJHaDNnZ1pybWgzUXR0ZVZUWUhLa2tsbTE3SGtod2oyS0t1WU84aHR6anBQVQpDNFVhajh2V2J0dlgrMk5aZWhHdjZTNUoyZm95VERaS240cmdZNVZybFZYQW04dGpEOTlKejRsaVpSS1dZVVAxCnVQWkhBbHB1ZjFGZFdnSmFLKytPRVJaTEFvR0FLSmQ5K3V3TWVubEJIeW11Wlh5RXZaTFVDNzEzTC9YOWpzUWoKM1NHd0FtcHdRUU16YWQ1RmVEc1ZDS3g2VFBPcDJCcXFBQ3RuZGVqSXRoL3lNRDd5cHV5UGxZRGd1L2Z0V3lFNwpDOEZtSDFud1ZReTd3M0dhSDc3RFRLSk9BWXZKRElaQk0yUGdVcE9OS3dNS1BXcWc2aFFBQmlEemFNaGpDT0NyCkFqMXBRSUVDZ1lFQWw2THZncFl6enlUVThtRmFYazUrQXVaVHB1dlZzNXNOVEUwQmdyUWJBN0haL0hjT3hLaWQKYkxPU1diVzNaZ05aV0xXOFNhdWlURCt4L2lmVGR6UUJHQnZOaGFJaklGNnltdFlwTzNaWCs2MG15L1hUZmJGUQordUJ3UDducU1JUnNuQjUzRlc0S3VWcWljMDVEbGdvRWJ5NWM1eTlPQjlyWUQzUnJTT01EbGFzPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
diff --git a/vendor/gems/kubeclient/test/config/another-ca1.pem b/vendor/gems/kubeclient/test/config/another-ca1.pem
new file mode 100644
index 00000000000..50825d47519
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/another-ca1.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDADCCAeigAwIBAgIUQZjM/5qoAF78qIDyc+rKi4qBdOIwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAxMNa3ViZXJuZXRlcy1jYTAeFw0yMjAzMjIxNDQzMDBaFw0z
+MjAzMTkxNDQzMDBaMBgxFjAUBgNVBAMTDWt1YmVybmV0ZXMtY2EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGkG7g+UjpDhZ7A4Pm7Hme+RWs5IHz4I2X
+IclvtO3LuJ26yzz2S8VaXFFeUqzEPb2G1RxFGvoAVN7qrTw0n5MQJCFLAA4dI7oY
+8XLRJ7KgTBBIw1jYpgKb2zyHPIJE6VmslliKUiX+QDovdRU/dsbdup2EucrnGw4+
+QNNAc3XMbXgm6lubA6znYZlSpcQ8BKer3tq75q4KUZicIjS6gKQyZjk9a6fcOuCS
+ybtlAKp9lYzcwxZkNrx+V1PJMQ1qaJWPnMAVi7Oj5Dm3Jmf1WHBcNEh52Q/0vYlt
+4WSaeM5t/Py/m/7c4Ve97f5m2X6EhYyUbzov4qeZOnIJI3MnU1FxAgMBAAGjQjBA
+MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSl1qyt
+jd96WstRE8h9x5qkCvZUvjANBgkqhkiG9w0BAQsFAAOCAQEAJt55qYvBaniAwvgO
+tbO79g1FcQGrxpMX45TuoCE/K+MWDjrr6bp+FbLOqT8MwOsbGwwJIRTHGvkEkVso
+5AWI5aSNs3hWnltOdz27ZSHeX77WB4daK1tLK6ggZrp3v9iIpbBwWBFdmAqsPvEs
+H17K2BgAzdh6xRKPQd0BGTUpJBfk50R2gDMj7FKyIzBN69IOGytBfAXBhHzEGy4+
+MvtTEIMUjR//KgCrpNeyDuaWHttR5FdnuRxFO7O3BAfyNSaNmd/IEHQf7DIGgzOy
++xWLyH/HRHj5C70qAqjbnrgBODI99BsA9U7oXTuyPLdIboAcFt2zD5DIYgZET52X
+53w4jA==
+-----END CERTIFICATE-----
diff --git a/vendor/gems/kubeclient/test/config/another-ca2.pem b/vendor/gems/kubeclient/test/config/another-ca2.pem
new file mode 100644
index 00000000000..53be72e87d7
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/another-ca2.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDADCCAeigAwIBAgIUHW3OPnmuTquJ0YgbGpmm/blsY2QwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAxMNa3ViZXJuZXRlcy1jYTAeFw0yMjAzMjIxNDQ0MDBaFw0z
+MjAzMTkxNDQ0MDBaMBgxFjAUBgNVBAMTDWt1YmVybmV0ZXMtY2EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLMEJs5agS0hNQBxPTtsI6dIhIi/pY8liI
+sNukbi5KwKf80FYNyRXqE8ufDVyTFzOc+MG96jnHjDaBWjrVN9On0PgUBo4nPyd4
+DtyvYx2jMzwToSEIo/Z1aroMx1oGywCgdS4/3FWAbhlSbyXKJmhfh6gX0TxWz+dV
+zqNuqQq9EWuRhOMg9vgzjfp3mjiPE10lW8pT0j5JT3PI/eGO+C2Z7z33LJXb6GM2
+nXvhGFMGY+7XG65pqJ3L8g1mk+LjPiwyIItw8wPtrnrZ2VXMklMd5Mn+jgCTNe1B
+om0nPpPIiTblCr6gcNcVjy5WGN37OKlqrT0JTuSPHcxSUp05LFjDAgMBAAGjQjBA
+MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQvV/sB
+wbR3UwjkLAMN+6P3fZ/3OjANBgkqhkiG9w0BAQsFAAOCAQEACAk4EQwCkw2EBsSR
+2SKoa1SjYFkZzIr/0/TB2YcMUvHF+RpvlD5vQ8/RJjeAl1kc6/niZ9TWCemjBLqI
+hPoFe49zr49DyQjC2ZfsXVJvFCr6g7o4q4DtQ6ltyBuTJbkn1hI+aB8zgvpofG44
+mKj18Y7tPvgXtRua4SaeBq777+22AOvKxPied9p4PTrMN4RKTP6+yIbLflej7dBD
+zQDjfmmYsH0T2ZRtBpE1dYrUbU3tkizcMZRJBgreoxoff+r5coibMIm/7gh+YoSb
+BCItCaeuGSKQ8CJb8DElcPUd6nKUjmeiQL68ztsG/+CXLiL/TZb914VaaCXvPInw
+49jJ7w==
+-----END CERTIFICATE-----
diff --git a/vendor/gems/kubeclient/test/config/concatenated-ca.kubeconfig b/vendor/gems/kubeclient/test/config/concatenated-ca.kubeconfig
new file mode 100644
index 00000000000..ed20e4dde50
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/concatenated-ca.kubeconfig
@@ -0,0 +1,20 @@
+apiVersion: v1
+clusters:
+- cluster:
+ certificate-authority: concatenated-ca.pem
+ server: https://localhost:6443
+ name: local
+contexts:
+- context:
+ cluster: local
+ namespace: default
+ user: user
+ name: Default
+current-context: Default
+kind: Config
+preferences: {}
+users:
+- name: user
+ user:
+ client-certificate: external-cert.pem
+ client-key: external-key.rsa
diff --git a/vendor/gems/kubeclient/test/config/concatenated-ca.pem b/vendor/gems/kubeclient/test/config/concatenated-ca.pem
new file mode 100644
index 00000000000..1117298ff2d
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/concatenated-ca.pem
@@ -0,0 +1,57 @@
+-----BEGIN CERTIFICATE-----
+MIIDADCCAeigAwIBAgIUQZjM/5qoAF78qIDyc+rKi4qBdOIwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAxMNa3ViZXJuZXRlcy1jYTAeFw0yMjAzMjIxNDQzMDBaFw0z
+MjAzMTkxNDQzMDBaMBgxFjAUBgNVBAMTDWt1YmVybmV0ZXMtY2EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGkG7g+UjpDhZ7A4Pm7Hme+RWs5IHz4I2X
+IclvtO3LuJ26yzz2S8VaXFFeUqzEPb2G1RxFGvoAVN7qrTw0n5MQJCFLAA4dI7oY
+8XLRJ7KgTBBIw1jYpgKb2zyHPIJE6VmslliKUiX+QDovdRU/dsbdup2EucrnGw4+
+QNNAc3XMbXgm6lubA6znYZlSpcQ8BKer3tq75q4KUZicIjS6gKQyZjk9a6fcOuCS
+ybtlAKp9lYzcwxZkNrx+V1PJMQ1qaJWPnMAVi7Oj5Dm3Jmf1WHBcNEh52Q/0vYlt
+4WSaeM5t/Py/m/7c4Ve97f5m2X6EhYyUbzov4qeZOnIJI3MnU1FxAgMBAAGjQjBA
+MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSl1qyt
+jd96WstRE8h9x5qkCvZUvjANBgkqhkiG9w0BAQsFAAOCAQEAJt55qYvBaniAwvgO
+tbO79g1FcQGrxpMX45TuoCE/K+MWDjrr6bp+FbLOqT8MwOsbGwwJIRTHGvkEkVso
+5AWI5aSNs3hWnltOdz27ZSHeX77WB4daK1tLK6ggZrp3v9iIpbBwWBFdmAqsPvEs
+H17K2BgAzdh6xRKPQd0BGTUpJBfk50R2gDMj7FKyIzBN69IOGytBfAXBhHzEGy4+
+MvtTEIMUjR//KgCrpNeyDuaWHttR5FdnuRxFO7O3BAfyNSaNmd/IEHQf7DIGgzOy
++xWLyH/HRHj5C70qAqjbnrgBODI99BsA9U7oXTuyPLdIboAcFt2zD5DIYgZET52X
+53w4jA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC/zCCAeegAwIBAgITPbfpy29aBG67ChRdB6lJegTkmDANBgkqhkiG9w0BAQsF
+ADAYMRYwFAYDVQQDEw1rdWJlcm5ldGVzLWNhMB4XDTIyMDIyMTA5MDIwMFoXDTMy
+MDIxOTA5MDIwMFowGDEWMBQGA1UEAxMNa3ViZXJuZXRlcy1jYTCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMLjZ2cFBalzoimaEcpT9Jz2JmdgsHMOagVd
+It7OQpTwDZ3npIICVpguEh9xtovR8m8/HYM+/a4vMQHT+3p8HPjiDaRYGg7OZ9L+
+Fp/9zhBuiaIvg8Z+Bbys9Q9Uuj6VEwfFJBcNH6TmzdiDgQUs5/k+6/vtuJ4ys3sD
+KkAOxqPXDaBoANnLpIxdIMQDcWSLFA0wmFhdZJq3KEAoJpEL0WYo1ZRBV3iH77yf
+sDbN1OBu2vNnRZ+DrV0ZJ5ApmbFXPX8i4KJaW9lCB62FN0j5XsNDoyTeAVpesfNs
+zYufVpBdqNZFkOKg9diMuTMika2aYfDuiVzdebDgcp9aMloKtbECAwEAAaNCMEAw
+DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFBdOiygC
+LcuJrq8rNa1xADr5Sp7CMA0GCSqGSIb3DQEBCwUAA4IBAQDCy4IlhASh6Br5XEcI
+TpP5ThD1OyRzQnsPe6P1qgWP3kBXK/AcsSl+VGtaZp2oEhJoUnsz7kE8yW3gK+PA
+51zY4aHTiF9xkyd5zOCAGB+cfp9Ys+szWzyu0QQ9IBjJ4+eDjg7W0/S+BM2Qn1iL
+jTFIe2Bdf+Q/J24/q3ksTXK17UNun14vDRsJgsNcrFt/rumfHPx1ytwsiqKyEKV7
+kFxSwa3d8/AvhGgFpPmfRjU7gAJCFcHz501zhi2a6L5TYBTecVRbqZoeHiZ0YNWI
+is5g4VmVB+BxMAM2WEd29v4l/3oI1Pey9rvt7NJqSe1im9uqZgVDeg/vP8zKs/dF
+ZYw8
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDADCCAeigAwIBAgIUHW3OPnmuTquJ0YgbGpmm/blsY2QwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAxMNa3ViZXJuZXRlcy1jYTAeFw0yMjAzMjIxNDQ0MDBaFw0z
+MjAzMTkxNDQ0MDBaMBgxFjAUBgNVBAMTDWt1YmVybmV0ZXMtY2EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLMEJs5agS0hNQBxPTtsI6dIhIi/pY8liI
+sNukbi5KwKf80FYNyRXqE8ufDVyTFzOc+MG96jnHjDaBWjrVN9On0PgUBo4nPyd4
+DtyvYx2jMzwToSEIo/Z1aroMx1oGywCgdS4/3FWAbhlSbyXKJmhfh6gX0TxWz+dV
+zqNuqQq9EWuRhOMg9vgzjfp3mjiPE10lW8pT0j5JT3PI/eGO+C2Z7z33LJXb6GM2
+nXvhGFMGY+7XG65pqJ3L8g1mk+LjPiwyIItw8wPtrnrZ2VXMklMd5Mn+jgCTNe1B
+om0nPpPIiTblCr6gcNcVjy5WGN37OKlqrT0JTuSPHcxSUp05LFjDAgMBAAGjQjBA
+MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQvV/sB
+wbR3UwjkLAMN+6P3fZ/3OjANBgkqhkiG9w0BAQsFAAOCAQEACAk4EQwCkw2EBsSR
+2SKoa1SjYFkZzIr/0/TB2YcMUvHF+RpvlD5vQ8/RJjeAl1kc6/niZ9TWCemjBLqI
+hPoFe49zr49DyQjC2ZfsXVJvFCr6g7o4q4DtQ6ltyBuTJbkn1hI+aB8zgvpofG44
+mKj18Y7tPvgXtRua4SaeBq777+22AOvKxPied9p4PTrMN4RKTP6+yIbLflej7dBD
+zQDjfmmYsH0T2ZRtBpE1dYrUbU3tkizcMZRJBgreoxoff+r5coibMIm/7gh+YoSb
+BCItCaeuGSKQ8CJb8DElcPUd6nKUjmeiQL68ztsG/+CXLiL/TZb914VaaCXvPInw
+49jJ7w==
+-----END CERTIFICATE-----
diff --git a/vendor/gems/kubeclient/test/config/execauth.kubeconfig b/vendor/gems/kubeclient/test/config/execauth.kubeconfig
new file mode 100644
index 00000000000..c9a9773fe5e
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/execauth.kubeconfig
@@ -0,0 +1,61 @@
+apiVersion: v1
+clusters:
+- cluster:
+ server: https://localhost:6443
+ name: localhost:6443
+contexts:
+- context:
+ cluster: localhost:6443
+ namespace: default
+ user: system:admin:exec-search-path
+ name: localhost/system:admin:exec-search-path
+- context:
+ cluster: localhost:6443
+ namespace: default
+ user: system:admin:exec-relative-path
+ name: localhost/system:admin:exec-relative-path
+- context:
+ cluster: localhost:6443
+ namespace: default
+ user: system:admin:exec-absolute-path
+ name: localhost/system:admin:exec-absolute-path
+kind: Config
+preferences: {}
+users:
+- name: system:admin:exec-search-path
+ user:
+ exec:
+ # Command to execute. Required.
+ command: "example-exec-plugin"
+
+ # API version to use when decoding the ExecCredentials resource. Required.
+ #
+ # The API version returned by the plugin MUST match the version listed here.
+ #
+ # To integrate with tools that support multiple versions (such as client.authentication.k8s.io/v1alpha1),
+ # set an environment variable or pass an argument to the tool that indicates which version the exec plugin expects.
+ apiVersion: "client.authentication.k8s.io/v1beta1"
+
+ # Environment variables to set when executing the plugin. Optional.
+ env:
+ - name: "FOO"
+ value: "bar"
+
+ # Arguments to pass when executing the plugin. Optional.
+ args:
+ - "arg1"
+ - "arg2"
+
+- name: system:admin:exec-relative-path
+ user:
+ exec:
+ # Command to execute. Required.
+ command: "dir/example-exec-plugin"
+ apiVersion: "client.authentication.k8s.io/v1beta1"
+
+- name: system:admin:exec-absolute-path
+ user:
+ exec:
+ # Command to execute. Required.
+ command: "/abs/path/example-exec-plugin"
+ apiVersion: "client.authentication.k8s.io/v1beta1"
diff --git a/vendor/gems/kubeclient/test/config/external-ca.pem b/vendor/gems/kubeclient/test/config/external-ca.pem
new file mode 100644
index 00000000000..f1c8a8b615d
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/external-ca.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC/zCCAeegAwIBAgITPbfpy29aBG67ChRdB6lJegTkmDANBgkqhkiG9w0BAQsF
+ADAYMRYwFAYDVQQDEw1rdWJlcm5ldGVzLWNhMB4XDTIyMDIyMTA5MDIwMFoXDTMy
+MDIxOTA5MDIwMFowGDEWMBQGA1UEAxMNa3ViZXJuZXRlcy1jYTCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMLjZ2cFBalzoimaEcpT9Jz2JmdgsHMOagVd
+It7OQpTwDZ3npIICVpguEh9xtovR8m8/HYM+/a4vMQHT+3p8HPjiDaRYGg7OZ9L+
+Fp/9zhBuiaIvg8Z+Bbys9Q9Uuj6VEwfFJBcNH6TmzdiDgQUs5/k+6/vtuJ4ys3sD
+KkAOxqPXDaBoANnLpIxdIMQDcWSLFA0wmFhdZJq3KEAoJpEL0WYo1ZRBV3iH77yf
+sDbN1OBu2vNnRZ+DrV0ZJ5ApmbFXPX8i4KJaW9lCB62FN0j5XsNDoyTeAVpesfNs
+zYufVpBdqNZFkOKg9diMuTMika2aYfDuiVzdebDgcp9aMloKtbECAwEAAaNCMEAw
+DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFBdOiygC
+LcuJrq8rNa1xADr5Sp7CMA0GCSqGSIb3DQEBCwUAA4IBAQDCy4IlhASh6Br5XEcI
+TpP5ThD1OyRzQnsPe6P1qgWP3kBXK/AcsSl+VGtaZp2oEhJoUnsz7kE8yW3gK+PA
+51zY4aHTiF9xkyd5zOCAGB+cfp9Ys+szWzyu0QQ9IBjJ4+eDjg7W0/S+BM2Qn1iL
+jTFIe2Bdf+Q/J24/q3ksTXK17UNun14vDRsJgsNcrFt/rumfHPx1ytwsiqKyEKV7
+kFxSwa3d8/AvhGgFpPmfRjU7gAJCFcHz501zhi2a6L5TYBTecVRbqZoeHiZ0YNWI
+is5g4VmVB+BxMAM2WEd29v4l/3oI1Pey9rvt7NJqSe1im9uqZgVDeg/vP8zKs/dF
+ZYw8
+-----END CERTIFICATE-----
diff --git a/vendor/gems/kubeclient/test/config/external-cert.pem b/vendor/gems/kubeclient/test/config/external-cert.pem
new file mode 100644
index 00000000000..6c8b5232d7a
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/external-cert.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDWTCCAkGgAwIBAgIUKO1eRmXmCc+vvM+LoByZE0IMGD8wDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAxMNa3ViZXJuZXRlcy1jYTAeFw0yMjAyMjEwOTAyMDBaFw0y
+MzAyMjEwOTAyMDBaMDQxFzAVBgNVBAoTDnN5c3RlbTptYXN0ZXJzMRkwFwYDVQQD
+ExBrdWJlcm5ldGVzLWFkbWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAwhw1zJaAYW0kIcn5mWyXNm7IYDa+QMzH3HVZrgwYycCZZP90bYJj4TF+++3n
+PMZ2uMfZEXcmNpGXCIqzNYJQzvT+a1zCMuUT9+v1g343/VaYpxRzGueCVF+pvB/g
+jI0U4l1YmJIEk5XcDM9xE+hoUkwqVahMmrvsq8hynYjzWEtl0mJ+TO89/1ZjwAuQ
+t4ohNO1pQu3r4HCu3HGbFSTPJ6E4oxRgdMqHlqswpG0+ZLRXVkogUbtlIULtctoU
+T67fhsThRCYu2AAin+L20gr3Tcp4UtMWtTcZ66vlL2Ep6TkWiutb04L/Bo46VfxO
+sKBCdN1jysOeTPgTWKh088vM0wIDAQABo38wfTAOBgNVHQ8BAf8EBAMCBaAwHQYD
+VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0O
+BBYEFJ675z7GCIuJcbGxJ7bLI3JAL3BeMB8GA1UdIwQYMBaAFBdOiygCLcuJrq8r
+Na1xADr5Sp7CMA0GCSqGSIb3DQEBCwUAA4IBAQCXGCheo+VUxlFRPEXRSpw480lb
+2mvdzB4O/rOIG2H9vzgjhxbdCJNVedkGzmHZ75eEHzv1EkInebSGLFz5/P3BQUXw
+xyu84jNJer9Re2gemV0t2mwzHWCfQGLKG4AJi7M+cxZ49XhumZRVkw7iORF+qYOj
+hSloB/4kfyq3UaprKCvhqjEsKJqDndwlOwKLVMic3lQDLLXoKa5Tiv6ihBiCVWAI
+XUy5l2doDvZhzgEjLeQ0CGHb59vCvdotTc0b7HJ4Zw21ALz2CF7xkwXjP5uUIq56
+6PmQ+gpHXklbNJIODtkf/9I4rMlHUzeK5DVYY5kdxJTzh2nx4R0icFx1jnc0
+-----END CERTIFICATE-----
diff --git a/vendor/gems/kubeclient/test/config/external-key.rsa b/vendor/gems/kubeclient/test/config/external-key.rsa
new file mode 100644
index 00000000000..c79c1f2c310
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/external-key.rsa
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAwhw1zJaAYW0kIcn5mWyXNm7IYDa+QMzH3HVZrgwYycCZZP90
+bYJj4TF+++3nPMZ2uMfZEXcmNpGXCIqzNYJQzvT+a1zCMuUT9+v1g343/VaYpxRz
+GueCVF+pvB/gjI0U4l1YmJIEk5XcDM9xE+hoUkwqVahMmrvsq8hynYjzWEtl0mJ+
+TO89/1ZjwAuQt4ohNO1pQu3r4HCu3HGbFSTPJ6E4oxRgdMqHlqswpG0+ZLRXVkog
+UbtlIULtctoUT67fhsThRCYu2AAin+L20gr3Tcp4UtMWtTcZ66vlL2Ep6TkWiutb
+04L/Bo46VfxOsKBCdN1jysOeTPgTWKh088vM0wIDAQABAoIBAQDBXGCrQHD6nEIV
+9qDRGL841fp8/Ytf+Z5OGggPvLUkpNspJN/SBsWAGlIom8BxZjH1t/6fy1Uhnr4Z
+IDGM6BefUaXvRSOel1vg6AhVyHH1x0GhjllrP5sweWsXn5mL6S4YoGwU77/nVK0z
+haFa93SUJpM1aMWGjhUgujiSfU4LcLDRVKDERO48XP8FiCA6PB2IJXP0S9BaMVKQ
+HARCWNGX5ziNao1roIvPQNEMDTtnWbb4z4SvRmpcF60E/f4Jp06SH00QNeTv7n57
+DzSXYdcqlfLR9QSTdVktSOxQ8cHalTcy5sU2xyFwc1S9aZZ8nzRpbFu1VYFCIVcf
+E4QVv1UBAoGBAMWg4nZTHiGthf6KyzOGkh1r2kBSmVy9BcYjB8TbzQbrNUy7YAlT
+i7eOnF+uTKcaVc5vnUrV0l7zD3WSWu1ARb9K6ZpVL5wjYNmm/zurR+mJc8LKl7d6
+54z1c111T5iivM1bYRnU0OujHDoX5ZD5WGsxMKaHtLaGWX/gK49hPbfzAoGBAPtx
+TWQHiOq/uVsk0QnYkKZEwezfdC2daVJoAyDAV5gcU4NsN62UKfgTraD+ChmyJ5R7
+jOSL8bUX33QQEvmK9WYXMVBwz28HrZypuByQEEc8e9yy4YQ+RYZ1L2kmGdaeZ8Mz
+iDffLrXby7NZp4xcfMZWYmLwdGESYo8jzuci1K+hAoGBAJDIzkBbom6P3uPdsQLd
+qvxNAIcxPFP501oWXeG2Gh3ggZrmh3QtteVTYHKkklm17Hkhwj2KKuYO8htzjpPU
+C4Uaj8vWbtvX+2NZehGv6S5J2foyTDZKn4rgY5VrlVXAm8tjD99Jz4liZRKWYUP1
+uPZHAlpuf1FdWgJaK++OERZLAoGAKJd9+uwMenlBHymuZXyEvZLUC713L/X9jsQj
+3SGwAmpwQQMzad5FeDsVCKx6TPOp2BqqACtndejIth/yMD7ypuyPlYDgu/ftWyE7
+C8FmH1nwVQy7w3GaH77DTKJOAYvJDIZBM2PgUpONKwMKPWqg6hQABiDzaMhjCOCr
+Aj1pQIECgYEAl6LvgpYzzyTU8mFaXk5+AuZTpuvVs5sNTE0BgrQbA7HZ/HcOxKid
+bLOSWbW3ZgNZWLW8SauiTD+x/ifTdzQBGBvNhaIjIF6ymtYpO3ZX+60my/XTfbFQ
++uBwP7nqMIRsnB53FW4KuVqic05DlgoEby5c5y9OB9rYD3RrSOMDlas=
+-----END RSA PRIVATE KEY-----
diff --git a/vendor/gems/kubeclient/test/config/external-without-ca.kubeconfig b/vendor/gems/kubeclient/test/config/external-without-ca.kubeconfig
new file mode 100644
index 00000000000..1f3d617a40d
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/external-without-ca.kubeconfig
@@ -0,0 +1,21 @@
+apiVersion: v1
+clusters:
+- cluster:
+ # Not defining custom `certificate-authority`.
+ # Without it, the localhost cert should be rejected.
+ server: https://localhost:6443
+ name: local
+contexts:
+- context:
+ cluster: local
+ namespace: default
+ user: user
+ name: Default
+current-context: Default
+kind: Config
+preferences: {}
+users:
+- name: user
+ user:
+ client-certificate: external-cert.pem
+ client-key: external-key.rsa
diff --git a/vendor/gems/kubeclient/test/config/external.kubeconfig b/vendor/gems/kubeclient/test/config/external.kubeconfig
new file mode 100644
index 00000000000..ef2dca61348
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/external.kubeconfig
@@ -0,0 +1,20 @@
+apiVersion: v1
+clusters:
+- cluster:
+ certificate-authority: external-ca.pem
+ server: https://localhost:6443
+ name: local
+contexts:
+- context:
+ cluster: local
+ namespace: default
+ user: user
+ name: Default
+current-context: Default
+kind: Config
+preferences: {}
+users:
+- name: user
+ user:
+ client-certificate: external-cert.pem
+ client-key: external-key.rsa
diff --git a/vendor/gems/kubeclient/test/config/gcpauth.kubeconfig b/vendor/gems/kubeclient/test/config/gcpauth.kubeconfig
new file mode 100644
index 00000000000..0ee387ebb16
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/gcpauth.kubeconfig
@@ -0,0 +1,21 @@
+apiVersion: v1
+clusters:
+- cluster:
+ server: https://localhost:8443
+ name: localhost:8443
+contexts:
+- context:
+ cluster: localhost:8443
+ namespace: default
+ user: application-default-credentials
+ name: localhost/application-default-credentials
+kind: Config
+preferences: {}
+users:
+- name: application-default-credentials
+ user:
+ auth-provider:
+ config:
+ access-token: <fake_token>
+ expiry: 2019-02-19T11:07:29.827352-05:00
+ name: gcp
diff --git a/vendor/gems/kubeclient/test/config/gcpcmdauth.kubeconfig b/vendor/gems/kubeclient/test/config/gcpcmdauth.kubeconfig
new file mode 100644
index 00000000000..2e2db395834
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/gcpcmdauth.kubeconfig
@@ -0,0 +1,25 @@
+apiVersion: v1
+clusters:
+- cluster:
+ server: https://localhost:8443
+ name: localhost:8443
+contexts:
+- context:
+ cluster: localhost:8443
+ namespace: default
+ user: application-default-credentials
+ name: localhost/application-default-credentials
+kind: Config
+preferences: {}
+users:
+- name: application-default-credentials
+ user:
+ auth-provider:
+ config:
+ access-token: <fake_token>
+ cmd-args: config config-helper --format=json
+ cmd-path: /path/to/gcloud
+ expiry: 2019-04-09T19:26:18Z
+ expiry-key: '{.credential.token_expiry}'
+ token-key: '{.credential.access_token}'
+ name: gcp
diff --git a/vendor/gems/kubeclient/test/config/insecure-custom-ca.kubeconfig b/vendor/gems/kubeclient/test/config/insecure-custom-ca.kubeconfig
new file mode 100644
index 00000000000..06c803c16bf
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/insecure-custom-ca.kubeconfig
@@ -0,0 +1,22 @@
+apiVersion: v1
+clusters:
+- cluster:
+ # This is a silly configuration, skip-tls-verify makes CA data useless, but testing for completeness.
+ certificate-authority: external-ca.pem
+ server: https://localhost:6443
+ insecure-skip-tls-verify: true
+ name: local
+contexts:
+- context:
+ cluster: local
+ namespace: default
+ user: user
+ name: Default
+current-context: Default
+kind: Config
+preferences: {}
+users:
+- name: user
+ user:
+ client-certificate: external-cert.pem
+ client-key: external-key.rsa
diff --git a/vendor/gems/kubeclient/test/config/insecure.kubeconfig b/vendor/gems/kubeclient/test/config/insecure.kubeconfig
new file mode 100644
index 00000000000..d7c28087a92
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/insecure.kubeconfig
@@ -0,0 +1,25 @@
+apiVersion: v1
+clusters:
+- cluster:
+ server: https://localhost:6443
+ insecure-skip-tls-verify: true
+ name: local
+contexts:
+- context:
+ cluster: local
+ namespace: default
+ user: user
+ name: Default
+current-context: Default
+kind: Config
+preferences: {}
+users:
+- name: user
+ user:
+ # Providing ANY credentials in `insecure-skip-tls-verify` mode is unwise due to MITM risk.
+ # At least client certs are not as catastrophic as bearer tokens.
+ #
+ # This combination of insecure + client certs was once broken in kubernetes but
+ # is meaningful since 2015 (https://github.com/kubernetes/kubernetes/pull/15430).
+ client-certificate: external-cert.pem
+ client-key: external-key.rsa
diff --git a/vendor/gems/kubeclient/test/config/nouser.kubeconfig b/vendor/gems/kubeclient/test/config/nouser.kubeconfig
new file mode 100644
index 00000000000..9289895cc81
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/nouser.kubeconfig
@@ -0,0 +1,15 @@
+apiVersion: v1
+clusters:
+- cluster:
+ server: https://localhost:6443
+ name: localhost:6443
+contexts:
+- context:
+ cluster: localhost:6443
+ namespace: default
+ user: ""
+ name: default/localhost:6443/nouser
+current-context: default/localhost:6443/nouser
+kind: Config
+preferences: {}
+users: []
diff --git a/vendor/gems/kubeclient/test/config/oidcauth.kubeconfig b/vendor/gems/kubeclient/test/config/oidcauth.kubeconfig
new file mode 100644
index 00000000000..e1f389da46f
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/oidcauth.kubeconfig
@@ -0,0 +1,24 @@
+apiVersion: v1
+clusters:
+- cluster:
+ server: https://localhost:8443
+ name: localhost:8443
+contexts:
+- context:
+ cluster: localhost:8443
+ namespace: default
+ user: oidc-auth-provider
+ name: localhost/oidc-auth-provider
+kind: Config
+preferences: {}
+users:
+- name: oidc-auth-provider
+ user:
+ auth-provider:
+ config:
+ client-id: fake-client-id
+ client-secret: fake-client-secret
+ id-token: fake-id-token
+ idp-issuer-url: https://accounts.google.com
+ refresh-token: fake-refresh-token
+ name: oidc
diff --git a/vendor/gems/kubeclient/test/config/secure-without-ca.kubeconfig b/vendor/gems/kubeclient/test/config/secure-without-ca.kubeconfig
new file mode 100644
index 00000000000..1b1acefe905
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/secure-without-ca.kubeconfig
@@ -0,0 +1,22 @@
+apiVersion: v1
+clusters:
+- cluster:
+ # Not defining custom `certificate-authority`.
+ # Without it, the localhost cert should be rejected.
+ server: https://localhost:6443
+ insecure-skip-tls-verify: false # Same as external-without-ca.kubeconfig but with explicit false here.
+ name: local
+contexts:
+- context:
+ cluster: local
+ namespace: default
+ user: user
+ name: Default
+current-context: Default
+kind: Config
+preferences: {}
+users:
+- name: user
+ user:
+ client-certificate: external-cert.pem
+ client-key: external-key.rsa
diff --git a/vendor/gems/kubeclient/test/config/secure.kubeconfig b/vendor/gems/kubeclient/test/config/secure.kubeconfig
new file mode 100644
index 00000000000..b0a00bb8d0d
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/secure.kubeconfig
@@ -0,0 +1,21 @@
+apiVersion: v1
+clusters:
+- cluster:
+ certificate-authority: external-ca.pem
+ server: https://localhost:6443
+ insecure-skip-tls-verify: false # Same as external.kubeconfig but with explicit false here.
+ name: local
+contexts:
+- context:
+ cluster: local
+ namespace: default
+ user: user
+ name: Default
+current-context: Default
+kind: Config
+preferences: {}
+users:
+- name: user
+ user:
+ client-certificate: external-cert.pem
+ client-key: external-key.rsa
diff --git a/vendor/gems/kubeclient/test/config/timestamps.kubeconfig b/vendor/gems/kubeclient/test/config/timestamps.kubeconfig
new file mode 100644
index 00000000000..7b718da21e9
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/timestamps.kubeconfig
@@ -0,0 +1,25 @@
+apiVersion: v1
+users:
+- name: gke_username
+ user:
+ auth-provider:
+ config:
+ access-token: REDACTED
+ cmd-args: config config-helper --format=json
+ cmd-path: /Users/tannerbruce/opt/google-cloud-sdk/bin/gcloud
+ expiry: 2018-07-07T18:25:36Z
+ expiry-key: '{.credential.token_expiry}'
+ token-key: '{.credential.access_token}'
+ name: gcp
+
+# More syntaxes from go-yaml tests, hopefully covering all types possible in kubeconfig
+IPv4: 1.2.3.4
+Duration: 3s
+date_only: 2015-01-01
+rfc3339: 2015-02-24T18:19:39Z
+longer: 2015-02-24T18:19:39.123456789-03:00
+shorter: 2015-2-3T3:4:5Z
+iso_lower_t: 2015-02-24t18:19:39Z
+space_no_tz: 2015-02-24 18:19:39
+space_tz: 2001-12-14 21:59:43.10 -5
+timestamp_like_string: "2015-02-24T18:19:39Z"
diff --git a/vendor/gems/kubeclient/test/config/update_certs_k0s.rb b/vendor/gems/kubeclient/test/config/update_certs_k0s.rb
new file mode 100755
index 00000000000..2632d72685c
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/update_certs_k0s.rb
@@ -0,0 +1,53 @@
+#!/usr/bin/env ruby
+# https://docs.k0sproject.io/latest/k0s-in-docker/
+# Runs in --prividged mode, only run this if you trust the k0s distribution.
+
+require 'English'
+
+# Like Kernel#system, returns true iff exit status == 0
+def sh?(*cmd)
+ puts("+ #{cmd.join(' ')}")
+ system(*cmd)
+end
+
+# Raises if exit status != 0
+def sh!(*cmd)
+ sh?(*cmd) || raise("returned #{$CHILD_STATUS}")
+end
+
+# allow DOCKER='sudo docker', DOCKER=podman etc.
+DOCKER = ENV['DOCKER'] || 'docker'
+
+CONTAINER = 'k0s'.freeze
+
+sh! "#{DOCKER} container inspect #{CONTAINER} --format='exists' ||
+ #{DOCKER} run -d --name #{CONTAINER} --hostname k0s --privileged -v /var/lib/k0s -p 6443:6443 \
+ ghcr.io/k0sproject/k0s/k0s:v1.23.3-k0s.1"
+
+# sh! "#{DOCKER} exec #{CONTAINER} kubectl config view --raw"
+# is another way to dump kubeconfig but succeeds with dummy output even before admin.conf exists;
+# so accessing the file is better way as it lets us poll until ready:
+sleep(1) until sh?("#{DOCKER} exec #{CONTAINER} ls -l /var/lib/k0s/pki/admin.conf")
+
+sh! "#{DOCKER} exec #{CONTAINER} cat /var/lib/k0s/pki/admin.conf > test/config/allinone.kubeconfig"
+# The rest could easily be extracted from allinone.kubeconfig, but the test is more robust
+# if we don't reuse YAML and/or Kubeclient::Config parsing to construct test data.
+sh! "#{DOCKER} exec #{CONTAINER} cat /var/lib/k0s/pki/ca.crt > test/config/external-ca.pem"
+sh! 'cat test/config/another-ca1.pem test/config/external-ca.pem '\
+ ' test/config/another-ca2.pem > test/config/concatenated-ca.pem'
+sh! "#{DOCKER} exec #{CONTAINER} cat /var/lib/k0s/pki/admin.crt > test/config/external-cert.pem"
+sh! "#{DOCKER} exec #{CONTAINER} cat /var/lib/k0s/pki/admin.key > test/config/external-key.rsa"
+
+# Wait for apiserver to be up. To speed startup, this only retries connection errors;
+# without `--fail-with-body` curl still returns 0 for well-formed 4xx or 5xx responses.
+sleep(1) until sh?(
+ 'curl --cacert test/config/external-ca.pem ' \
+ '--key test/config/external-key.rsa ' \
+ '--cert test/config/external-cert.pem https://127.0.0.1:6443/healthz'
+)
+
+sh! 'env KUBECLIENT_TEST_REAL_CLUSTER=true bundle exec rake test'
+
+sh! "#{DOCKER} rm -f #{CONTAINER}"
+
+puts 'If you run this only for tests, cleanup by running: git restore test/config/'
diff --git a/vendor/gems/kubeclient/test/config/userauth.kubeconfig b/vendor/gems/kubeclient/test/config/userauth.kubeconfig
new file mode 100644
index 00000000000..604e3bda920
--- /dev/null
+++ b/vendor/gems/kubeclient/test/config/userauth.kubeconfig
@@ -0,0 +1,27 @@
+apiVersion: v1
+clusters:
+- cluster:
+ server: https://localhost:6443
+ name: localhost:6443
+contexts:
+- context:
+ cluster: localhost:6443
+ namespace: default
+ user: system:admin:token
+ name: localhost/system:admin:token
+- context:
+ cluster: localhost:6443
+ namespace: default
+ user: system:admin:userpass
+ name: localhost/system:admin:userpass
+current-context: localhost/system:admin:token
+kind: Config
+preferences: {}
+users:
+- name: system:admin:token
+ user:
+ token: 0123456789ABCDEF0123456789ABCDEF
+- name: system:admin:userpass
+ user:
+ username: admin
+ password: pAssw0rd123
diff --git a/vendor/gems/kubeclient/test/json/bindings_list.json b/vendor/gems/kubeclient/test/json/bindings_list.json
new file mode 100644
index 00000000000..260748c3aa4
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/bindings_list.json
@@ -0,0 +1,10 @@
+{
+ "kind": "Status",
+ "apiVersion": "v1",
+ "metadata": {},
+ "status": "Failure",
+ "message": "the server could not find the requested resource",
+ "reason": "NotFound",
+ "details": {},
+ "code": 404
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/component_status.json b/vendor/gems/kubeclient/test/json/component_status.json
new file mode 100644
index 00000000000..109936d3dac
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/component_status.json
@@ -0,0 +1,17 @@
+{
+ "kind": "ComponentStatus",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "etcd-0",
+ "selfLink": "/api/v1/namespaces/componentstatuses/etcd-0",
+ "creationTimestamp": null
+ },
+ "conditions": [
+ {
+ "type": "Healthy",
+ "status": "True",
+ "message": "{\"health\": \"true\"}",
+ "error": "nil"
+ }
+ ]
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/component_status_list.json b/vendor/gems/kubeclient/test/json/component_status_list.json
new file mode 100644
index 00000000000..1849f489eb2
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/component_status_list.json
@@ -0,0 +1,52 @@
+{
+ "kind": "ComponentStatusList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/componentstatuses"
+ },
+ "items": [
+ {
+ "metadata": {
+ "name": "controller-manager",
+ "selfLink": "/api/v1/namespaces/componentstatuses/controller-manager",
+ "creationTimestamp": null
+ },
+ "conditions": [
+ {
+ "type": "Healthy",
+ "status": "Unknown",
+ "error": "Get http://127.0.0.1:10252/healthz: dial tcp 127.0.0.1:10252: connection refused"
+ }
+ ]
+ },
+ {
+ "metadata": {
+ "name": "scheduler",
+ "selfLink": "/api/v1/namespaces/componentstatuses/scheduler",
+ "creationTimestamp": null
+ },
+ "conditions": [
+ {
+ "type": "Healthy",
+ "status": "Unknown",
+ "error": "Get http://127.0.0.1:10251/healthz: dial tcp 127.0.0.1:10251: connection refused"
+ }
+ ]
+ },
+ {
+ "metadata": {
+ "name": "etcd-0",
+ "selfLink": "/api/v1/namespaces/componentstatuses/etcd-0",
+ "creationTimestamp": null
+ },
+ "conditions": [
+ {
+ "type": "Healthy",
+ "status": "True",
+ "message": "{\"health\": \"true\"}",
+ "error": "nil"
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/config.istio.io_api_resource_list.json b/vendor/gems/kubeclient/test/json/config.istio.io_api_resource_list.json
new file mode 100644
index 00000000000..5317e865b63
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/config.istio.io_api_resource_list.json
@@ -0,0 +1,679 @@
+{
+ "kind": "APIResourceList",
+ "apiVersion": "v1",
+ "groupVersion": "config.istio.io/v1alpha2",
+ "resources": [
+ {
+ "name": "metrics",
+ "singularName": "metric",
+ "namespaced": true,
+ "kind": "metric",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "servicecontrolreports",
+ "singularName": "servicecontrolreport",
+ "namespaced": true,
+ "kind": "servicecontrolreport",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "opas",
+ "singularName": "opa",
+ "namespaced": true,
+ "kind": "opa",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "redisquotas",
+ "singularName": "redisquota",
+ "namespaced": true,
+ "kind": "redisquota",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "authorizations",
+ "singularName": "authorization",
+ "namespaced": true,
+ "kind": "authorization",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "listentries",
+ "singularName": "listentry",
+ "namespaced": true,
+ "kind": "listentry",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "logentries",
+ "singularName": "logentry",
+ "namespaced": true,
+ "kind": "logentry",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "tracespans",
+ "singularName": "tracespan",
+ "namespaced": true,
+ "kind": "tracespan",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "quotaspecs",
+ "singularName": "quotaspec",
+ "namespaced": true,
+ "kind": "QuotaSpec",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "fluentds",
+ "singularName": "fluentd",
+ "namespaced": true,
+ "kind": "fluentd",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "rbacs",
+ "singularName": "rbac",
+ "namespaced": true,
+ "kind": "rbac",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "checknothings",
+ "singularName": "checknothing",
+ "namespaced": true,
+ "kind": "checknothing",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "edges",
+ "singularName": "edge",
+ "namespaced": true,
+ "kind": "edge",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "apikeys",
+ "singularName": "apikey",
+ "namespaced": true,
+ "kind": "apikey",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "kuberneteses",
+ "singularName": "kubernetes",
+ "namespaced": true,
+ "kind": "kubernetes",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "httpapispecs",
+ "singularName": "httpapispec",
+ "namespaced": true,
+ "kind": "HTTPAPISpec",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "attributemanifests",
+ "singularName": "attributemanifest",
+ "namespaced": true,
+ "kind": "attributemanifest",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "kubernetesenvs",
+ "singularName": "kubernetesenv",
+ "namespaced": true,
+ "kind": "kubernetesenv",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "listcheckers",
+ "singularName": "listchecker",
+ "namespaced": true,
+ "kind": "listchecker",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "quotas",
+ "singularName": "quota",
+ "namespaced": true,
+ "kind": "quota",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "instances",
+ "singularName": "instance",
+ "namespaced": true,
+ "kind": "instance",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "memquotas",
+ "singularName": "memquota",
+ "namespaced": true,
+ "kind": "memquota",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "noops",
+ "singularName": "noop",
+ "namespaced": true,
+ "kind": "noop",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "prometheuses",
+ "singularName": "prometheus",
+ "namespaced": true,
+ "kind": "prometheus",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "solarwindses",
+ "singularName": "solarwinds",
+ "namespaced": true,
+ "kind": "solarwinds",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "cloudwatches",
+ "singularName": "cloudwatch",
+ "namespaced": true,
+ "kind": "cloudwatch",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "reportnothings",
+ "singularName": "reportnothing",
+ "namespaced": true,
+ "kind": "reportnothing",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "stackdrivers",
+ "singularName": "stackdriver",
+ "namespaced": true,
+ "kind": "stackdriver",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "statsds",
+ "singularName": "statsd",
+ "namespaced": true,
+ "kind": "statsd",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "httpapispecbindings",
+ "singularName": "httpapispecbinding",
+ "namespaced": true,
+ "kind": "HTTPAPISpecBinding",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "quotaspecbindings",
+ "singularName": "quotaspecbinding",
+ "namespaced": true,
+ "kind": "QuotaSpecBinding",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "bypasses",
+ "singularName": "bypass",
+ "namespaced": true,
+ "kind": "bypass",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "circonuses",
+ "singularName": "circonus",
+ "namespaced": true,
+ "kind": "circonus",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "deniers",
+ "singularName": "denier",
+ "namespaced": true,
+ "kind": "denier",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "signalfxs",
+ "singularName": "signalfx",
+ "namespaced": true,
+ "kind": "signalfx",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "adapters",
+ "singularName": "adapter",
+ "namespaced": true,
+ "kind": "adapter",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "servicecontrols",
+ "singularName": "servicecontrol",
+ "namespaced": true,
+ "kind": "servicecontrol",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "templates",
+ "singularName": "template",
+ "namespaced": true,
+ "kind": "template",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "handlers",
+ "singularName": "handler",
+ "namespaced": true,
+ "kind": "handler",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "rules",
+ "singularName": "rule",
+ "namespaced": true,
+ "kind": "rule",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "dogstatsds",
+ "singularName": "dogstatsd",
+ "namespaced": true,
+ "kind": "dogstatsd",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "stdios",
+ "singularName": "stdio",
+ "namespaced": true,
+ "kind": "stdio",
+ "verbs": [
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "create",
+ "update",
+ "watch"
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/config_map_list.json b/vendor/gems/kubeclient/test/json/config_map_list.json
new file mode 100644
index 00000000000..85e0e30d7fe
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/config_map_list.json
@@ -0,0 +1,9 @@
+{
+ "kind": "ConfigMapList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/configmaps",
+ "resourceVersion": "665"
+ },
+ "items": []
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/core_api_resource_list.json b/vendor/gems/kubeclient/test/json/core_api_resource_list.json
new file mode 100644
index 00000000000..395acb249df
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/core_api_resource_list.json
@@ -0,0 +1,181 @@
+{
+ "kind": "APIResourceList",
+ "groupVersion": "v1",
+ "resources": [
+ {
+ "name": "bindings",
+ "namespaced": true,
+ "kind": "Binding"
+ },
+ {
+ "name": "componentstatuses",
+ "namespaced": false,
+ "kind": "ComponentStatus"
+ },
+ {
+ "name": "configmaps",
+ "namespaced": true,
+ "kind": "ConfigMap"
+ },
+ {
+ "name": "endpoints",
+ "namespaced": true,
+ "kind": "Endpoints"
+ },
+ {
+ "name": "events",
+ "namespaced": true,
+ "kind": "Event"
+ },
+ {
+ "name": "limitranges",
+ "namespaced": true,
+ "kind": "LimitRange"
+ },
+ {
+ "name": "namespaces",
+ "namespaced": false,
+ "kind": "Namespace"
+ },
+ {
+ "name": "namespaces/finalize",
+ "namespaced": false,
+ "kind": "Namespace"
+ },
+ {
+ "name": "namespaces/status",
+ "namespaced": false,
+ "kind": "Namespace"
+ },
+ {
+ "name": "nodes",
+ "namespaced": false,
+ "kind": "Node"
+ },
+ {
+ "name": "nodes/proxy",
+ "namespaced": false,
+ "kind": "Node"
+ },
+ {
+ "name": "nodes/status",
+ "namespaced": false,
+ "kind": "Node"
+ },
+ {
+ "name": "persistentvolumeclaims",
+ "namespaced": true,
+ "kind": "PersistentVolumeClaim"
+ },
+ {
+ "name": "persistentvolumeclaims/status",
+ "namespaced": true,
+ "kind": "PersistentVolumeClaim"
+ },
+ {
+ "name": "persistentvolumes",
+ "namespaced": false,
+ "kind": "PersistentVolume"
+ },
+ {
+ "name": "persistentvolumes/status",
+ "namespaced": false,
+ "kind": "PersistentVolume"
+ },
+ {
+ "name": "pods",
+ "namespaced": true,
+ "kind": "Pod"
+ },
+ {
+ "name": "pods/attach",
+ "namespaced": true,
+ "kind": "Pod"
+ },
+ {
+ "name": "pods/binding",
+ "namespaced": true,
+ "kind": "Binding"
+ },
+ {
+ "name": "pods/exec",
+ "namespaced": true,
+ "kind": "Pod"
+ },
+ {
+ "name": "pods/log",
+ "namespaced": true,
+ "kind": "Pod"
+ },
+ {
+ "name": "pods/portforward",
+ "namespaced": true,
+ "kind": "Pod"
+ },
+ {
+ "name": "pods/proxy",
+ "namespaced": true,
+ "kind": "Pod"
+ },
+ {
+ "name": "pods/status",
+ "namespaced": true,
+ "kind": "Pod"
+ },
+ {
+ "name": "podtemplates",
+ "namespaced": true,
+ "kind": "PodTemplate"
+ },
+ {
+ "name": "replicationcontrollers",
+ "namespaced": true,
+ "kind": "ReplicationController"
+ },
+ {
+ "name": "replicationcontrollers/scale",
+ "namespaced": true,
+ "kind": "Scale"
+ },
+ {
+ "name": "replicationcontrollers/status",
+ "namespaced": true,
+ "kind": "ReplicationController"
+ },
+ {
+ "name": "resourcequotas",
+ "namespaced": true,
+ "kind": "ResourceQuota"
+ },
+ {
+ "name": "resourcequotas/status",
+ "namespaced": true,
+ "kind": "ResourceQuota"
+ },
+ {
+ "name": "secrets",
+ "namespaced": true,
+ "kind": "Secret"
+ },
+ {
+ "name": "serviceaccounts",
+ "namespaced": true,
+ "kind": "ServiceAccount"
+ },
+ {
+ "name": "services",
+ "namespaced": true,
+ "kind": "Service"
+ },
+ {
+ "name": "services/proxy",
+ "namespaced": true,
+ "kind": "Service"
+ },
+ {
+ "name": "services/status",
+ "namespaced": true,
+ "kind": "Service"
+ }
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/core_api_resource_list_without_kind.json b/vendor/gems/kubeclient/test/json/core_api_resource_list_without_kind.json
new file mode 100644
index 00000000000..f60e113d6e5
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/core_api_resource_list_without_kind.json
@@ -0,0 +1,129 @@
+{
+ "groupVersion": "v1",
+ "resources": [
+ {
+ "name": "bindings",
+ "namespaced": true
+ },
+ {
+ "name": "componentstatuses",
+ "namespaced": true
+ },
+ {
+ "name": "endpoints",
+ "namespaced": true
+ },
+ {
+ "name": "events",
+ "namespaced": true
+ },
+ {
+ "name": "limitranges",
+ "namespaced": true
+ },
+ {
+ "name": "namespaces",
+ "namespaced": false
+ },
+ {
+ "name": "namespaces/finalize",
+ "namespaced": false
+ },
+ {
+ "name": "namespaces/status",
+ "namespaced": false
+ },
+ {
+ "name": "nodes",
+ "namespaced": false
+ },
+ {
+ "name": "nodes/status",
+ "namespaced": false
+ },
+ {
+ "name": "persistentvolumeclaims",
+ "namespaced": true
+ },
+ {
+ "name": "persistentvolumeclaims/status",
+ "namespaced": true
+ },
+ {
+ "name": "persistentvolumes",
+ "namespaced": false
+ },
+ {
+ "name": "persistentvolumes/status",
+ "namespaced": false
+ },
+ {
+ "name": "pods",
+ "namespaced": true
+ },
+ {
+ "name": "pods/attach",
+ "namespaced": true
+ },
+ {
+ "name": "pods/binding",
+ "namespaced": true
+ },
+ {
+ "name": "pods/exec",
+ "namespaced": true
+ },
+ {
+ "name": "pods/log",
+ "namespaced": true
+ },
+ {
+ "name": "pods/portforward",
+ "namespaced": true
+ },
+ {
+ "name": "pods/proxy",
+ "namespaced": true
+ },
+ {
+ "name": "pods/status",
+ "namespaced": true
+ },
+ {
+ "name": "podtemplates",
+ "namespaced": true
+ },
+ {
+ "name": "replicationcontrollers",
+ "namespaced": true
+ },
+ {
+ "name": "replicationcontrollers/status",
+ "namespaced": true
+ },
+ {
+ "name": "resourcequotas",
+ "namespaced": true
+ },
+ {
+ "name": "resourcequotas/status",
+ "namespaced": true
+ },
+ {
+ "name": "secrets",
+ "namespaced": true
+ },
+ {
+ "name": "securitycontextconstraints",
+ "namespaced": false
+ },
+ {
+ "name": "serviceaccounts",
+ "namespaced": true
+ },
+ {
+ "name": "services",
+ "namespaced": true
+ }
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/core_oapi_resource_list_without_kind.json b/vendor/gems/kubeclient/test/json/core_oapi_resource_list_without_kind.json
new file mode 100644
index 00000000000..a902a6a7fd1
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/core_oapi_resource_list_without_kind.json
@@ -0,0 +1,197 @@
+{
+ "groupVersion": "v1",
+ "resources": [
+ {
+ "name": "buildconfigs",
+ "namespaced": true
+ },
+ {
+ "name": "buildconfigs/instantiate",
+ "namespaced": true
+ },
+ {
+ "name": "buildconfigs/instantiatebinary",
+ "namespaced": true
+ },
+ {
+ "name": "buildconfigs/webhooks",
+ "namespaced": true
+ },
+ {
+ "name": "builds",
+ "namespaced": true
+ },
+ {
+ "name": "builds/clone",
+ "namespaced": true
+ },
+ {
+ "name": "builds/details",
+ "namespaced": true
+ },
+ {
+ "name": "builds/log",
+ "namespaced": true
+ },
+ {
+ "name": "clusternetworks",
+ "namespaced": false
+ },
+ {
+ "name": "clusterpolicies",
+ "namespaced": false
+ },
+ {
+ "name": "clusterpolicybindings",
+ "namespaced": false
+ },
+ {
+ "name": "clusterrolebindings",
+ "namespaced": false
+ },
+ {
+ "name": "clusterroles",
+ "namespaced": false
+ },
+ {
+ "name": "deploymentconfigrollbacks",
+ "namespaced": true
+ },
+ {
+ "name": "deploymentconfigs",
+ "namespaced": true
+ },
+ {
+ "name": "deploymentconfigs/log",
+ "namespaced": true
+ },
+ {
+ "name": "deploymentconfigs/scale",
+ "namespaced": true
+ },
+ {
+ "name": "generatedeploymentconfigs",
+ "namespaced": true
+ },
+ {
+ "name": "groups",
+ "namespaced": false
+ },
+ {
+ "name": "hostsubnets",
+ "namespaced": false
+ },
+ {
+ "name": "identities",
+ "namespaced": false
+ },
+ {
+ "name": "images",
+ "namespaced": false
+ },
+ {
+ "name": "imagestreamimages",
+ "namespaced": true
+ },
+ {
+ "name": "imagestreammappings",
+ "namespaced": true
+ },
+ {
+ "name": "imagestreams",
+ "namespaced": true
+ },
+ {
+ "name": "imagestreams/status",
+ "namespaced": true
+ },
+ {
+ "name": "imagestreamtags",
+ "namespaced": true
+ },
+ {
+ "name": "localresourceaccessreviews",
+ "namespaced": true
+ },
+ {
+ "name": "localsubjectaccessreviews",
+ "namespaced": true
+ },
+ {
+ "name": "netnamespaces",
+ "namespaced": false
+ },
+ {
+ "name": "oauthaccesstokens",
+ "namespaced": false
+ },
+ {
+ "name": "oauthauthorizetokens",
+ "namespaced": false
+ },
+ {
+ "name": "oauthclientauthorizations",
+ "namespaced": false
+ },
+ {
+ "name": "oauthclients",
+ "namespaced": false
+ },
+ {
+ "name": "policies",
+ "namespaced": true
+ },
+ {
+ "name": "policybindings",
+ "namespaced": true
+ },
+ {
+ "name": "processedtemplates",
+ "namespaced": true
+ },
+ {
+ "name": "projectrequests",
+ "namespaced": false
+ },
+ {
+ "name": "projects",
+ "namespaced": false
+ },
+ {
+ "name": "resourceaccessreviews",
+ "namespaced": true
+ },
+ {
+ "name": "rolebindings",
+ "namespaced": true
+ },
+ {
+ "name": "roles",
+ "namespaced": true
+ },
+ {
+ "name": "routes",
+ "namespaced": true
+ },
+ {
+ "name": "routes/status",
+ "namespaced": true
+ },
+ {
+ "name": "subjectaccessreviews",
+ "namespaced": true
+ },
+ {
+ "name": "templates",
+ "namespaced": true
+ },
+ {
+ "name": "useridentitymappings",
+ "namespaced": false
+ },
+ {
+ "name": "users",
+ "namespaced": false
+ }
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/created_endpoint.json b/vendor/gems/kubeclient/test/json/created_endpoint.json
new file mode 100644
index 00000000000..1e0fd7dc41d
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/created_endpoint.json
@@ -0,0 +1,28 @@
+{
+ "kind": "Endpoints",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "myendpoint",
+ "namespace": "default",
+ "selfLink": "/api/v1/namespaces/default/endpoints/myendpoint",
+ "uid": "59d05b48-dadb-11e5-937e-18037327aaeb",
+ "resourceVersion": "393",
+ "creationTimestamp": "2016-02-24T09:45:34Z"
+ },
+ "subsets": [
+ {
+ "addresses": [
+ {
+ "ip": "172.17.0.25"
+ }
+ ],
+ "ports": [
+ {
+ "name": "https",
+ "port": 6443,
+ "protocol": "TCP"
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/created_namespace.json b/vendor/gems/kubeclient/test/json/created_namespace.json
new file mode 100644
index 00000000000..218bc000aa8
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/created_namespace.json
@@ -0,0 +1,20 @@
+{
+"kind": "Namespace",
+"apiVersion": "v1",
+"metadata": {
+"name": "development",
+"selfLink": "/api/v1/namespaces/development",
+"uid": "13d820d6-df5b-11e4-bd42-f8b156af4ae1",
+"resourceVersion": "2533",
+"creationTimestamp": "2015-04-10T08:24:59Z"
+},
+"spec": {
+"finalizers": [
+"kubernetes"
+]
+},
+"status": {
+"phase": "Active"
+}
+}
+
diff --git a/vendor/gems/kubeclient/test/json/created_secret.json b/vendor/gems/kubeclient/test/json/created_secret.json
new file mode 100644
index 00000000000..bcea8848335
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/created_secret.json
@@ -0,0 +1,16 @@
+{
+ "kind": "Secret",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "test-secret",
+ "namespace": "dev",
+ "selfLink": "/api/v1/namespaces/dev/secrets/test-secret",
+ "uid": "4e38a198-2bcb-11e5-a483-0e840567604d",
+ "resourceVersion": "245569",
+ "creationTimestamp": "2015-07-16T14:59:49Z"
+ },
+ "data": {
+ "super-secret": "Y2F0J3MgYXJlIGF3ZXNvbWUK"
+ },
+ "type": "Opaque"
+}
diff --git a/vendor/gems/kubeclient/test/json/created_security_context_constraint.json b/vendor/gems/kubeclient/test/json/created_security_context_constraint.json
new file mode 100644
index 00000000000..c2981712cb5
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/created_security_context_constraint.json
@@ -0,0 +1,65 @@
+{
+ "allowHostDirVolumePlugin": false,
+ "allowHostIPC": false,
+ "allowHostNetwork": false,
+ "allowHostPID": false,
+ "allowHostPorts": false,
+ "allowPrivilegedContainer": false,
+ "allowedCapabilities": null,
+ "allowedFlexVolumes": null,
+ "apiVersion": "security.openshift.io/v1",
+ "defaultAddCapabilities": null,
+ "fsGroup": {
+ "type": "RunAsAny"
+ },
+ "groups": [],
+ "kind": "SecurityContextConstraints",
+ "metadata": {
+ "creationTimestamp": "2018-11-23T10:01:42Z",
+ "name": "teleportation",
+ "resourceVersion": "5274",
+ "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints/teleportation",
+ "uid": "c6e8e2ec-ef06-11e8-b4c0-68f728fac3ab"
+ },
+ "priority": null,
+ "readOnlyRootFilesystem": false,
+ "requiredDropCapabilities": null,
+ "runAsUser": {
+ "type": "MustRunAs"
+ },
+ "seLinuxContext": {
+ "type": "MustRunAs"
+ },
+ "supplementalGroups": {
+ "type": "RunAsAny"
+ },
+ "users": [],
+ "volumes": [
+ "awsElasticBlockStore",
+ "azureDisk",
+ "azureFile",
+ "cephFS",
+ "cinder",
+ "configMap",
+ "downwardAPI",
+ "emptyDir",
+ "fc",
+ "flexVolume",
+ "flocker",
+ "gcePersistentDisk",
+ "gitRepo",
+ "glusterfs",
+ "iscsi",
+ "nfs",
+ "persistentVolumeClaim",
+ "photonPersistentDisk",
+ "portworxVolume",
+ "projected",
+ "quobyte",
+ "rbd",
+ "scaleIO",
+ "secret",
+ "storageOS",
+ "vsphere"
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/created_service.json b/vendor/gems/kubeclient/test/json/created_service.json
new file mode 100644
index 00000000000..4c2bc7d7196
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/created_service.json
@@ -0,0 +1,31 @@
+{
+ "kind": "Service",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "guestbook",
+ "namespace": "staging",
+ "selfLink": "/api/v1/namespaces/staging/services/guestbook",
+ "uid": "29885239-df58-11e4-bd42-f8b156af4ae1",
+ "resourceVersion": "1908",
+ "creationTimestamp": "2015-04-10T08:04:07Z",
+ "labels": {
+ "name": "guestbook"
+ }
+ },
+ "spec": {
+ "ports": [
+ {
+ "name": "",
+ "protocol": "TCP",
+ "port": 3000,
+ "targetPort": "http-server"
+ }
+ ],
+ "selector": {
+ "name": "guestbook"
+ },
+ "clusterIP": "10.0.0.99",
+ "sessionAffinity": "None"
+ },
+ "status": {}
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/empty_pod_list.json b/vendor/gems/kubeclient/test/json/empty_pod_list.json
new file mode 100644
index 00000000000..a82cba1148f
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/empty_pod_list.json
@@ -0,0 +1,9 @@
+{
+ "kind": "PodList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/pods",
+ "resourceVersion": "565"
+ },
+ "items": []
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/endpoint_list.json b/vendor/gems/kubeclient/test/json/endpoint_list.json
new file mode 100644
index 00000000000..bd6c00ab678
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/endpoint_list.json
@@ -0,0 +1,48 @@
+{
+ "kind": "EndpointsList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/namespaces/endpoints",
+ "resourceVersion": "39"
+ },
+ "items": [
+ {
+ "metadata": {
+ "name": "example",
+ "namespace": "default",
+ "selfLink": "/api/v1/namespaces/default/endpoints/example",
+ "uid": "db467530-b6aa-11e4-974a-525400c903c1",
+ "resourceVersion": "38",
+ "creationTimestamp": "2015-02-17T08:42:46-05:00"
+ },
+ "endpoints": [
+ "172.17.0.63:80",
+ "172.17.0.64:80"
+ ]
+ },
+ {
+ "metadata": {
+ "name": "kubernetes",
+ "namespace": "default",
+ "selfLink": "/api/v1/namespaces/default/endpoints/kubernetes",
+ "resourceVersion": "8",
+ "creationTimestamp": null
+ },
+ "endpoints": [
+ "192.168.122.4:6443"
+ ]
+ },
+ {
+ "metadata": {
+ "name": "kubernetes-ro",
+ "namespace": "default",
+ "selfLink": "/api/v1/namespaces/default/endpoints/kubernetes-ro",
+ "resourceVersion": "7",
+ "creationTimestamp": null
+ },
+ "endpoints": [
+ "192.168.122.4:7080"
+ ]
+ }
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/entity_list.json b/vendor/gems/kubeclient/test/json/entity_list.json
new file mode 100644
index 00000000000..3dd140d38c8
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/entity_list.json
@@ -0,0 +1,56 @@
+{
+ "kind": "ServiceList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/services",
+ "resourceVersion": "59"
+ },
+ "items": [
+ {
+ "metadata": {
+ "name": "kubernetes",
+ "namespace": "default",
+ "selfLink": "/api/v1/services/kubernetes?namespace=default",
+ "uid": "016e9dcd-ce39-11e4-ac24-3c970e4a436a",
+ "resourceVersion": "6",
+ "creationTimestamp": "2015-03-19T15:08:16+02:00",
+ "labels": {
+ "component": "apiserver",
+ "provider": "kubernetes"
+ }
+ },
+ "spec": {
+ "port": 443,
+ "protocol": "TCP",
+ "selector": null,
+ "clusterIP": "10.0.0.2",
+ "containerPort": 0,
+ "sessionAffinity": "None"
+ },
+ "status": {}
+ },
+ {
+ "metadata": {
+ "name": "kubernetes-ro",
+ "namespace": "default",
+ "selfLink": "/api/v1/services/kubernetes-ro?namespace=default",
+ "uid": "015b78bf-ce39-11e4-ac24-3c970e4a436a",
+ "resourceVersion": "5",
+ "creationTimestamp": "2015-03-19T15:08:15+02:00",
+ "labels": {
+ "component": "apiserver",
+ "provider": "kubernetes"
+ }
+ },
+ "spec": {
+ "port": 80,
+ "protocol": "TCP",
+ "selector": null,
+ "clusterIP": "10.0.0.1",
+ "containerPort": 0,
+ "sessionAffinity": "None"
+ },
+ "status": {}
+ }
+ ]
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/event_list.json b/vendor/gems/kubeclient/test/json/event_list.json
new file mode 100644
index 00000000000..45abfccc278
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/event_list.json
@@ -0,0 +1,35 @@
+{
+ "kind": "EventList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/events",
+ "resourceVersion": "152"
+ },
+ "items": [
+ {
+ "metadata": {
+ "name": "php.13c130f78da4641e",
+ "namespace": "default",
+ "selfLink": "/api/v1/events/php.13c130f78da4641e?namespace=default",
+ "uid": "f3a454d2-b03a-11e4-89e4-525400c903c1",
+ "resourceVersion": "178",
+ "creationTimestamp": "2015-02-09T04:06:37-05:00"
+ },
+ "involvedObject": {
+ "kind": "BoundPod",
+ "namespace": "default",
+ "name": "php",
+ "uid": "64273d20-b03a-11e4-89e4-525400c903c1",
+ "apiVersion": "v1beta1",
+ "fieldPath": "spec.containers{nginx}"
+ },
+ "reason": "created",
+ "message": "Created with docker id 9ba2a714411d2d0dd1e826b2fe5c3222b5cbfd9dd9133c841585cbb96b8c2c0f",
+ "source": {
+ "component": "kubelet",
+ "host": "127.0.0.1"
+ },
+ "timestamp": "2015-02-09T04:06:37-05:00"
+ }
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/extensions_v1beta1_api_resource_list.json b/vendor/gems/kubeclient/test/json/extensions_v1beta1_api_resource_list.json
new file mode 100644
index 00000000000..16fc80cf4d7
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/extensions_v1beta1_api_resource_list.json
@@ -0,0 +1,217 @@
+{
+ "kind": "APIResourceList",
+ "groupVersion": "extensions/v1beta1",
+ "resources": [
+ {
+ "name": "daemonsets",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "DaemonSet",
+ "verbs": [
+ "create",
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "update",
+ "watch"
+ ],
+ "shortNames": [
+ "ds"
+ ]
+ },
+ {
+ "name": "daemonsets/status",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "DaemonSet",
+ "verbs": [
+ "get",
+ "patch",
+ "update"
+ ]
+ },
+ {
+ "name": "deployments",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "Deployment",
+ "verbs": [
+ "create",
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "update",
+ "watch"
+ ],
+ "shortNames": [
+ "deploy"
+ ]
+ },
+ {
+ "name": "deployments/rollback",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "DeploymentRollback",
+ "verbs": [
+ "create"
+ ]
+ },
+ {
+ "name": "deployments/scale",
+ "singularName": "",
+ "namespaced": true,
+ "group": "extensions",
+ "version": "v1beta1",
+ "kind": "Scale",
+ "verbs": [
+ "get",
+ "patch",
+ "update"
+ ]
+ },
+ {
+ "name": "deployments/status",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "Deployment",
+ "verbs": [
+ "get",
+ "patch",
+ "update"
+ ]
+ },
+ {
+ "name": "ingresses",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "Ingress",
+ "verbs": [
+ "create",
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "update",
+ "watch"
+ ],
+ "shortNames": [
+ "ing"
+ ]
+ },
+ {
+ "name": "ingresses/status",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "Ingress",
+ "verbs": [
+ "get",
+ "patch",
+ "update"
+ ]
+ },
+ {
+ "name": "networkpolicies",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "NetworkPolicy",
+ "verbs": [
+ "create",
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "update",
+ "watch"
+ ],
+ "shortNames": [
+ "netpol"
+ ]
+ },
+ {
+ "name": "podsecuritypolicies",
+ "singularName": "",
+ "namespaced": false,
+ "kind": "PodSecurityPolicy",
+ "verbs": [
+ "create",
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "update",
+ "watch"
+ ],
+ "shortNames": [
+ "psp"
+ ]
+ },
+ {
+ "name": "replicasets",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "ReplicaSet",
+ "verbs": [
+ "create",
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "update",
+ "watch"
+ ],
+ "shortNames": [
+ "rs"
+ ]
+ },
+ {
+ "name": "replicasets/scale",
+ "singularName": "",
+ "namespaced": true,
+ "group": "extensions",
+ "version": "v1beta1",
+ "kind": "Scale",
+ "verbs": [
+ "get",
+ "patch",
+ "update"
+ ]
+ },
+ {
+ "name": "replicasets/status",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "ReplicaSet",
+ "verbs": [
+ "get",
+ "patch",
+ "update"
+ ]
+ },
+ {
+ "name": "replicationcontrollers",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "ReplicationControllerDummy",
+ "verbs": []
+ },
+ {
+ "name": "replicationcontrollers/scale",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "Scale",
+ "verbs": [
+ "get",
+ "patch",
+ "update"
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/limit_range.json b/vendor/gems/kubeclient/test/json/limit_range.json
new file mode 100644
index 00000000000..ac2e21aa744
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/limit_range.json
@@ -0,0 +1,23 @@
+{
+ "kind": "LimitRange",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "limits",
+ "namespace": "quota-example",
+ "selfLink": "/api/v1/namespaces/quota-example/limitranges/limits",
+ "uid": "7a76a44c-3e9d-11e5-8214-0aaeec44370e",
+ "resourceVersion": "103384",
+ "creationTimestamp": "2015-08-09T13:49:39Z"
+ },
+ "spec": {
+ "limits": [
+ {
+ "type": "Container",
+ "default": {
+ "cpu": "100m",
+ "memory": "512Mi"
+ }
+ }
+ ]
+ }
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/limit_range_list.json b/vendor/gems/kubeclient/test/json/limit_range_list.json
new file mode 100644
index 00000000000..7986ec861d8
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/limit_range_list.json
@@ -0,0 +1,31 @@
+{
+ "kind": "LimitRangeList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/namespaces/quota-example/limitranges/",
+ "resourceVersion": "103421"
+ },
+ "items": [
+ {
+ "metadata": {
+ "name": "limits",
+ "namespace": "quota-example",
+ "selfLink": "/api/v1/namespaces/quota-example/limitranges/limits",
+ "uid": "7a76a44c-3e9d-11e5-8214-0aaeec44370e",
+ "resourceVersion": "103384",
+ "creationTimestamp": "2015-08-09T13:49:39Z"
+ },
+ "spec": {
+ "limits": [
+ {
+ "type": "Container",
+ "default": {
+ "cpu": "100m",
+ "memory": "512Mi"
+ }
+ }
+ ]
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/namespace.json b/vendor/gems/kubeclient/test/json/namespace.json
new file mode 100644
index 00000000000..5a856730188
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/namespace.json
@@ -0,0 +1,13 @@
+{
+ "kind": "Namespace",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "staging",
+ "selfLink": "/api/v1/namespaces/staging",
+ "uid": "e388bc10-c021-11e4-a514-3c970e4a436a",
+ "resourceVersion": "1168",
+ "creationTimestamp": "2015-03-01T16:47:31+02:00"
+ },
+ "spec": {},
+ "status": {}
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/namespace_exception.json b/vendor/gems/kubeclient/test/json/namespace_exception.json
new file mode 100644
index 00000000000..555ef24cabb
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/namespace_exception.json
@@ -0,0 +1,8 @@
+{
+ "kind": "Status",
+ "apiVersion": "v1",
+ "metadata": {},
+ "status": "Failure",
+ "message": "converting to : type names don't match (Pod, Namespace)",
+ "code": 500
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/namespace_list.json b/vendor/gems/kubeclient/test/json/namespace_list.json
new file mode 100644
index 00000000000..af6feb7485c
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/namespace_list.json
@@ -0,0 +1,32 @@
+{
+ "kind": "NamespaceList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/namespaces",
+ "resourceVersion": "1707"
+ },
+ "items": [
+ {
+ "metadata": {
+ "name": "default",
+ "selfLink": "/api/v1/namespaces/default",
+ "uid": "56c3eb7c-c009-11e4-a514-3c970e4a436a",
+ "resourceVersion": "4",
+ "creationTimestamp": "2015-03-01T13:51:47+02:00"
+ },
+ "spec": {},
+ "status": {}
+ },
+ {
+ "metadata": {
+ "name": "staging",
+ "selfLink": "/api/v1/namespaces/staging",
+ "uid": "e388bc10-c021-11e4-a514-3c970e4a436a",
+ "resourceVersion": "1168",
+ "creationTimestamp": "2015-03-01T16:47:31+02:00"
+ },
+ "spec": {},
+ "status": {}
+ }
+ ]
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/node.json b/vendor/gems/kubeclient/test/json/node.json
new file mode 100644
index 00000000000..bb4772c3f20
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/node.json
@@ -0,0 +1,29 @@
+{
+ "kind": "Node",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "127.0.0.1",
+ "selfLink": "/api/v1/nodes/127.0.0.1",
+ "uid": "041143c5-ce39-11e4-ac24-3c970e4a436a",
+ "resourceVersion": "1724",
+ "creationTimestamp": "2015-03-19T15:08:20+02:00"
+ },
+ "spec": {
+ "capacity": {
+ "cpu": "1",
+ "memory": "3Gi"
+ }
+ },
+ "status": {
+ "hostIP": "127.0.0.1",
+ "conditions": [
+ {
+ "kind": "Ready",
+ "status": "None",
+ "lastProbeTime": "2015-03-20T14:16:52+02:00",
+ "lastTransitionTime": "2015-03-19T15:08:20+02:00",
+ "reason": "Node health check failed: kubelet /healthz endpoint returns not ok"
+ }
+ ]
+ }
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/node_list.json b/vendor/gems/kubeclient/test/json/node_list.json
new file mode 100644
index 00000000000..a5c7cbb77a3
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/node_list.json
@@ -0,0 +1,37 @@
+{
+ "kind": "NodeList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/nodes",
+ "resourceVersion": "137"
+ },
+ "items": [
+ {
+ "metadata": {
+ "name": "127.0.0.1",
+ "selfLink": "/api/v1/nodes/127.0.0.1",
+ "uid": "041143c5-ce39-11e4-ac24-3c970e4a436a",
+ "resourceVersion": "137",
+ "creationTimestamp": "2015-03-19T15:08:20+02:00"
+ },
+ "spec": {
+ "capacity": {
+ "cpu": "1",
+ "memory": "3Gi"
+ }
+ },
+ "status": {
+ "hostIP": "127.0.0.1",
+ "conditions": [
+ {
+ "kind": "Ready",
+ "status": "None",
+ "lastProbeTime": "2015-03-19T15:29:33+02:00",
+ "lastTransitionTime": "2015-03-19T15:08:20+02:00",
+ "reason": "Node health check failed: kubelet /healthz endpoint returns not ok"
+ }
+ ]
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/node_notice.json b/vendor/gems/kubeclient/test/json/node_notice.json
new file mode 100644
index 00000000000..69ee438ecc2
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/node_notice.json
@@ -0,0 +1,160 @@
+{
+ "type": "ADDED",
+ "object": {
+ "apiVersion": "v1",
+ "kind": "Node",
+ "metadata": {
+ "annotations": {
+ "volumes.kubernetes.io/controller-managed-attach-detach": "true"
+ },
+ "creationTimestamp": "2017-12-11T12:00:13Z",
+ "labels": {
+ "beta.kubernetes.io/arch": "amd64",
+ "beta.kubernetes.io/os": "linux",
+ "kubernetes.io/hostname": "openshift.local",
+ "openshift-infra": "apiserver"
+ },
+ "name": "openshift.local",
+ "resourceVersion": "367410",
+ "selfLink": "/api/v1/nodes/openshift.local",
+ "uid": "d88c7af6-de6a-11e7-8725-52540080f1d2"
+ },
+ "spec": {
+ "externalID": "openshift.local"
+ },
+ "status": {
+ "addresses": [
+ {
+ "address": "192.168.122.40",
+ "type": "InternalIP"
+ },
+ {
+ "address": "openshift.local",
+ "type": "Hostname"
+ }
+ ],
+ "allocatable": {
+ "cpu": "2",
+ "memory": "8072896Ki",
+ "pods": "20"
+ },
+ "capacity": {
+ "cpu": "2",
+ "memory": "8175296Ki",
+ "pods": "20"
+ },
+ "conditions": [
+ {
+ "lastHeartbeatTime": "2017-12-15T00:36:13Z",
+ "lastTransitionTime": "2017-12-11T12:00:13Z",
+ "message": "kubelet has sufficient disk space available",
+ "reason": "KubeletHasSufficientDisk",
+ "status": "False",
+ "type": "OutOfDisk"
+ },
+ {
+ "lastHeartbeatTime": "2017-12-15T00:36:13Z",
+ "lastTransitionTime": "2017-12-11T12:00:13Z",
+ "message": "kubelet has sufficient memory available",
+ "reason": "KubeletHasSufficientMemory",
+ "status": "False",
+ "type": "MemoryPressure"
+ },
+ {
+ "lastHeartbeatTime": "2017-12-15T00:36:13Z",
+ "lastTransitionTime": "2017-12-11T12:00:13Z",
+ "message": "kubelet has no disk pressure",
+ "reason": "KubeletHasNoDiskPressure",
+ "status": "False",
+ "type": "DiskPressure"
+ },
+ {
+ "lastHeartbeatTime": "2017-12-15T00:36:13Z",
+ "lastTransitionTime": "2017-12-14T15:43:39Z",
+ "message": "kubelet is posting ready status",
+ "reason": "KubeletReady",
+ "status": "True",
+ "type": "Ready"
+ }
+ ],
+ "daemonEndpoints": {
+ "kubeletEndpoint": {
+ "Port": 10250
+ }
+ },
+ "images": [
+ {
+ "names": [
+ "docker.io/openshift/origin@sha256:908c6c9ccf0e0feefe2658899656c6e73d2854777fa340738fb903f0a40c328d",
+ "docker.io/openshift/origin:latest"
+ ],
+ "sizeBytes": 1222636603
+ },
+ {
+ "names": [
+ "docker.io/openshift/origin-deployer@sha256:3d324bce1870047edc418041cefdec88e0a5bbb5b3b9f6fd35b43f14919a656c",
+ "docker.io/openshift/origin-deployer:v3.7.0"
+ ],
+ "sizeBytes": 1098951248
+ },
+ {
+ "names": [
+ "docker.io/cockpit/kubernetes@sha256:a8e58cd5e6f5a4d12d1e2dfd339686b74f3c22586952ca7aa184dc254ab49714",
+ "docker.io/cockpit/kubernetes:latest"
+ ],
+ "sizeBytes": 375926556
+ },
+ {
+ "names": [
+ "docker.io/cockpit/kubernetes@sha256:0745b3823efc57e03a5ef378614dfcb6c2b1e3964220bbf908fb3046a91cef70"
+ ],
+ "sizeBytes": 350062743
+ },
+ {
+ "names": [
+ "docker.io/openshift/origin-service-catalog@sha256:ef851e06276af96838a93320d0e4be51cc8de6e5afb2fb0efd4e56cec114b937"
+ ],
+ "sizeBytes": 284732029
+ },
+ {
+ "names": [
+ "docker.io/openshift/origin-service-catalog@sha256:8addfd742d92d8da819b091d6bda40edc45e88d1446ffd1ad658b6d21b3c36fd"
+ ],
+ "sizeBytes": 284731998
+ },
+ {
+ "names": [
+ "docker.io/openshift/origin-service-catalog@sha256:b3a737cc346b3cae85ef2f5d020b607781a1cac38fe70678cb78fee2c2a3bf8a"
+ ],
+ "sizeBytes": 284731943
+ },
+ {
+ "names": [
+ "docker.io/openshift/origin-service-catalog@sha256:957934537721da33362693d4f1590dc79dc5da7438799bf14d645165768e53ef",
+ "docker.io/openshift/origin-service-catalog:latest"
+ ],
+ "sizeBytes": 283929631
+ },
+ {
+ "names": [
+ "docker.io/openshift/origin-pod@sha256:2c257d83a01607b229ef5e3dca09f52c3a2a2788c09dc33f0444ec4e572a9e1d",
+ "docker.io/openshift/origin-pod:v3.7.0"
+ ],
+ "sizeBytes": 218423400
+ }
+ ],
+ "nodeInfo": {
+ "architecture": "amd64",
+ "bootID": "75be791d-88a2-4f56-a588-c071a80bf7cf",
+ "containerRuntimeVersion": "docker://1.12.6",
+ "kernelVersion": "3.10.0-693.11.1.el7.x86_64",
+ "kubeProxyVersion": "v1.7.6+a08f5eeb62",
+ "kubeletVersion": "v1.7.6+a08f5eeb62",
+ "machineID": "adf09ffc2de2624aa5ed335727c7400d",
+ "operatingSystem": "linux",
+ "osImage": "CentOS Linux 7 (Core)",
+ "systemUUID": "FC9FF0AD-E22D-4A62-A5ED-335727C7400D"
+ }
+ }
+ }
+}
diff --git a/vendor/gems/kubeclient/test/json/persistent_volume.json b/vendor/gems/kubeclient/test/json/persistent_volume.json
new file mode 100644
index 00000000000..612d9df1564
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/persistent_volume.json
@@ -0,0 +1,37 @@
+{
+ "kind": "PersistentVolume",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "pv0001",
+ "selfLink": "/api/v1/persistentvolumes/pv0001",
+ "uid": "c83eece1-4b38-11e5-8d27-28d2447dcefe",
+ "resourceVersion": "1585",
+ "creationTimestamp": "2015-08-25T14:51:35Z",
+ "labels": {
+ "type": "local"
+ }
+ },
+ "spec": {
+ "capacity": {
+ "storage": "10Gi"
+ },
+ "hostPath": {
+ "path": "/tmp/data01"
+ },
+ "accessModes": [
+ "ReadWriteOnce"
+ ],
+ "claimRef": {
+ "kind": "PersistentVolumeClaim",
+ "namespace": "default",
+ "name": "myclaim-1",
+ "uid": "d47384a3-4b38-11e5-8d27-28d2447dcefe",
+ "apiVersion": "v1",
+ "resourceVersion": "1582"
+ },
+ "persistentVolumeReclaimPolicy": "Retain"
+ },
+ "status": {
+ "phase": "Bound"
+ }
+}
diff --git a/vendor/gems/kubeclient/test/json/persistent_volume_claim.json b/vendor/gems/kubeclient/test/json/persistent_volume_claim.json
new file mode 100644
index 00000000000..65154685348
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/persistent_volume_claim.json
@@ -0,0 +1,32 @@
+{
+ "kind": "PersistentVolumeClaim",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "myclaim-1",
+ "namespace": "default",
+ "selfLink": "/api/v1/namespaces/default/persistentvolumeclaims/myclaim-1",
+ "uid": "d47384a3-4b38-11e5-8d27-28d2447dcefe",
+ "resourceVersion": "1584",
+ "creationTimestamp": "2015-08-25T14:51:55Z"
+ },
+ "spec": {
+ "accessModes": [
+ "ReadWriteOnce"
+ ],
+ "resources": {
+ "requests": {
+ "storage": "3Gi"
+ }
+ },
+ "volumeName": "pv0001"
+ },
+ "status": {
+ "phase": "Bound",
+ "accessModes": [
+ "ReadWriteOnce"
+ ],
+ "capacity": {
+ "storage": "10Gi"
+ }
+ }
+}
diff --git a/vendor/gems/kubeclient/test/json/persistent_volume_claim_list.json b/vendor/gems/kubeclient/test/json/persistent_volume_claim_list.json
new file mode 100644
index 00000000000..9533d75603b
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/persistent_volume_claim_list.json
@@ -0,0 +1,40 @@
+{
+ "kind": "PersistentVolumeClaimList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/persistentvolumeclaims",
+ "resourceVersion": "3188"
+ },
+ "items": [
+ {
+ "metadata": {
+ "name": "myclaim-1",
+ "namespace": "default",
+ "selfLink": "/api/v1/namespaces/default/persistentvolumeclaims/myclaim-1",
+ "uid": "d47384a3-4b38-11e5-8d27-28d2447dcefe",
+ "resourceVersion": "1584",
+ "creationTimestamp": "2015-08-25T14:51:55Z"
+ },
+ "spec": {
+ "accessModes": [
+ "ReadWriteOnce"
+ ],
+ "resources": {
+ "requests": {
+ "storage": "3Gi"
+ }
+ },
+ "volumeName": "pv0001"
+ },
+ "status": {
+ "phase": "Bound",
+ "accessModes": [
+ "ReadWriteOnce"
+ ],
+ "capacity": {
+ "storage": "10Gi"
+ }
+ }
+ }
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/persistent_volume_claims_nil_items.json b/vendor/gems/kubeclient/test/json/persistent_volume_claims_nil_items.json
new file mode 100644
index 00000000000..b23ff4dcffd
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/persistent_volume_claims_nil_items.json
@@ -0,0 +1,8 @@
+{
+ "kind": "PersistentVolumeClaimList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/persistentvolumeclaims",
+ "resourceVersion": "1089012"
+ }
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/persistent_volume_list.json b/vendor/gems/kubeclient/test/json/persistent_volume_list.json
new file mode 100644
index 00000000000..fa7e53cb914
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/persistent_volume_list.json
@@ -0,0 +1,45 @@
+{
+ "kind": "PersistentVolumeList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/persistentvolumes",
+ "resourceVersion": "2999"
+ },
+ "items": [
+ {
+ "metadata": {
+ "name": "pv0001",
+ "selfLink": "/api/v1/persistentvolumes/pv0001",
+ "uid": "c83eece1-4b38-11e5-8d27-28d2447dcefe",
+ "resourceVersion": "1585",
+ "creationTimestamp": "2015-08-25T14:51:35Z",
+ "labels": {
+ "type": "local"
+ }
+ },
+ "spec": {
+ "capacity": {
+ "storage": "10Gi"
+ },
+ "hostPath": {
+ "path": "/tmp/data01"
+ },
+ "accessModes": [
+ "ReadWriteOnce"
+ ],
+ "claimRef": {
+ "kind": "PersistentVolumeClaim",
+ "namespace": "default",
+ "name": "myclaim-1",
+ "uid": "d47384a3-4b38-11e5-8d27-28d2447dcefe",
+ "apiVersion": "v1",
+ "resourceVersion": "1582"
+ },
+ "persistentVolumeReclaimPolicy": "Retain"
+ },
+ "status": {
+ "phase": "Bound"
+ }
+ }
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/pod.json b/vendor/gems/kubeclient/test/json/pod.json
new file mode 100644
index 00000000000..913c2146f6a
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/pod.json
@@ -0,0 +1,92 @@
+{
+ "kind": "Pod",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "redis-master3",
+ "namespace": "default",
+ "selfLink": "/api/v1/pods/redis-master3?namespace=default",
+ "uid": "a344023f-a23c-11e4-a36b-3c970e4a436a",
+ "resourceVersion": "9",
+ "creationTimestamp": "2015-01-22T15:43:24+02:00",
+ "labels": {
+ "name": "redis-master"
+ }
+ },
+ "spec": {
+ "volumes": null,
+ "containers": [
+ {
+ "name": "master",
+ "image": "dockerfile/redis",
+ "ports": [
+ {
+ "hostPort": 6379,
+ "containerPort": 6379,
+ "protocol": "TCP"
+ }
+ ],
+ "memory": "0",
+ "cpu": "100m",
+ "imagePullPolicy": ""
+ },
+ {
+ "name": "php-redis",
+ "image": "kubernetes/example-guestbook-php-redis",
+ "ports": [
+ {
+ "hostPort": 8000,
+ "containerPort": 80,
+ "protocol": "TCP"
+ }
+ ],
+ "memory": "50000000",
+ "cpu": "100m",
+ "imagePullPolicy": ""
+ }
+ ],
+ "restartPolicy": {
+ "always": {
+
+ }
+ },
+ "dnsPolicy": "ClusterFirst"
+ },
+ "status": {
+ "phase": "Running",
+ "host": "127.0.0.1",
+ "podIP": "172.17.0.2",
+ "info": {
+ "master": {
+ "state": {
+ "running": {
+ "startedAt": "2015-01-22T13:43:29Z"
+ }
+ },
+ "restartCount": 0,
+ "containerID": "docker://87458d9a12f9dc9a01b52c1eee5f09cf48939380271c0eaf31af298ce67b125e",
+ "image": "dockerfile/redis"
+ },
+ "net": {
+ "state": {
+ "running": {
+ "startedAt": "2015-01-22T13:43:27Z"
+ }
+ },
+ "restartCount": 0,
+ "containerID": "docker://3bb5ced1f831322d370f70b58137e1dd41216c2960b7a99394542b5230cbd259",
+ "podIP": "172.17.0.2",
+ "image": "kubernetes/pause:latest"
+ },
+ "php-redis": {
+ "state": {
+ "running": {
+ "startedAt": "2015-01-22T13:43:31Z"
+ }
+ },
+ "restartCount": 0,
+ "containerID": "docker://5f08685c0a7a5c974d438a52c6560d72bb0aae7e805d2a34302b9b460f1297c7",
+ "image": "kubernetes/example-guestbook-php-redis"
+ }
+ }
+ }
+}
diff --git a/vendor/gems/kubeclient/test/json/pod_list.json b/vendor/gems/kubeclient/test/json/pod_list.json
new file mode 100644
index 00000000000..d08bbdce0c0
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/pod_list.json
@@ -0,0 +1,79 @@
+{
+ "kind": "PodList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/pods",
+ "resourceVersion": "1315"
+ },
+ "items": [
+ {
+ "metadata": {
+ "name": "redis-master3",
+ "namespace": "default",
+ "selfLink": "/api/v1/pods/redis-master3?namespace=default",
+ "uid": "1da148b4-cef5-11e4-ac24-3c970e4a436a",
+ "resourceVersion": "1301",
+ "creationTimestamp": "2015-03-20T13:34:48+02:00",
+ "labels": {
+ "mylabel": "mylabelvalue",
+ "role": "pod"
+ }
+ },
+ "spec": {
+ "volumes": null,
+ "containers": [
+ {
+ "name": "master",
+ "image": "dockerfile/redis",
+ "ports": [
+ {
+ "hostPort": 6379,
+ "containerPort": 6379,
+ "protocol": "TCP"
+ }
+ ],
+ "resources": {
+ "limits": {
+ "cpu": "100m"
+ }
+ },
+ "terminationMessagePath": "/dev/termination-log",
+ "imagePullPolicy": "IfNotPresent",
+ "securityContext": {
+ "capabilities": {}
+ }
+ },
+ {
+ "name": "php-redis",
+ "image": "kubernetes/example-guestbook-php-redis",
+ "ports": [
+ {
+ "hostPort": 8000,
+ "containerPort": 80,
+ "protocol": "TCP"
+ }
+ ],
+ "resources": {
+ "limits": {
+ "cpu": "100m",
+ "memory": "50000000"
+ }
+ },
+ "terminationMessagePath": "/dev/termination-log",
+ "imagePullPolicy": "IfNotPresent",
+ "securityContext": {
+ "capabilities": {}
+ }
+ }
+ ],
+ "restartPolicy": {
+ "always": {}
+ },
+ "dnsPolicy": "ClusterFirst"
+ },
+ "status": {
+ "phase": "Pending"
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/pod_template_list.json b/vendor/gems/kubeclient/test/json/pod_template_list.json
new file mode 100644
index 00000000000..5acb2c2f83a
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/pod_template_list.json
@@ -0,0 +1,9 @@
+{
+ "kind": "PodTemplateList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/podtemplates",
+ "resourceVersion": "672"
+ },
+ "items": []
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/pods_1.json b/vendor/gems/kubeclient/test/json/pods_1.json
new file mode 100644
index 00000000000..ec00bd3b5cc
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/pods_1.json
@@ -0,0 +1,265 @@
+{
+ "kind":"PodList",
+ "apiVersion":"v1",
+ "metadata":{
+ "selfLink":"/api/v1/pods",
+ "resourceVersion":"53225946",
+ "continue":"eyJ2IjoibWV0YS5rOHMua"
+ },
+ "items":[
+ {
+ "metadata":{
+ "name":"my-ruby-project-2-build",
+ "namespace":"my-project",
+ "selfLink":"/api/v1/namespaces/my-project/pods/my-ruby-project-2-build",
+ "uid":"0c478b50-babb-11e8-ba7e-d094660d31fb",
+ "resourceVersion":"42398462",
+ "creationTimestamp":"2018-09-17T20:48:36Z",
+ "deletionTimestamp":"2018-09-19T13:22:50Z",
+ "deletionGracePeriodSeconds":0,
+ "labels":{
+ "openshift.io/build.name":"my-ruby-project-2"
+ },
+ "annotations":{
+ "openshift.io/build.name":"my-ruby-project-2",
+ "openshift.io/scc":"privileged"
+ },
+ "ownerReferences":[
+ {
+ "apiVersion":"build.openshift.io/v1",
+ "kind":"Build",
+ "name":"my-ruby-project-2",
+ "uid":"0c450e6d-babb-11e8-ba7e-d094660d31fb",
+ "controller":true
+ }
+ ],
+ "finalizers":[
+ "foregroundDeletion"
+ ]
+ },
+ "spec":{
+ "volumes":[
+ {
+ "name":"buildworkdir",
+ "emptyDir":{
+
+ }
+ },
+ {
+ "name":"docker-socket",
+ "hostPath":{
+ "path":"/var/run/docker.sock",
+ "type":""
+ }
+ },
+ {
+ "name":"crio-socket",
+ "hostPath":{
+ "path":"/var/run/crio/crio.sock",
+ "type":""
+ }
+ },
+ {
+ "name":"builder-dockercfg-rjgnm-push",
+ "secret":{
+ "secretName":"builder-dockercfg-rjgnm",
+ "defaultMode":384
+ }
+ },
+ {
+ "name":"builder-token-zkpb6",
+ "secret":{
+ "secretName":"builder-token-zkpb6",
+ "defaultMode":420
+ }
+ }
+ ],
+ "containers":[
+ {
+ "name":"sti-build",
+ "image":"openshift3/ose-sti-builder:v3.9.25",
+ "command":[
+ "openshift-sti-build"
+ ]
+ }
+ ],
+ "restartPolicy":"Never",
+ "terminationGracePeriodSeconds":30,
+ "dnsPolicy":"ClusterFirst",
+ "nodeSelector":{
+ "node-role.kubernetes.io/compute":"true"
+ },
+ "serviceAccountName":"builder",
+ "serviceAccount":"builder",
+ "nodeName":"dell-r430-20.example.com",
+ "securityContext":{
+
+ },
+ "imagePullSecrets":[
+ {
+ "name":"builder-dockercfg-rjgnm"
+ }
+ ],
+ "schedulerName":"default-scheduler"
+ },
+ "status":{
+ "phase":"Failed",
+ "conditions":[
+ {
+ "type":"Initialized",
+ "status":"True",
+ "lastProbeTime":null,
+ "lastTransitionTime":"2018-09-17T20:48:49Z"
+ },
+ {
+ "type":"Ready",
+ "status":"False",
+ "lastProbeTime":null,
+ "lastTransitionTime":"2018-09-17T20:49:08Z",
+ "reason":"ContainersNotReady",
+ "message":"containers with unready status: [sti-build]"
+ },
+ {
+ "type":"PodScheduled",
+ "status":"True",
+ "lastProbeTime":null,
+ "lastTransitionTime":"2018-09-17T20:48:36Z"
+ }
+ ],
+ "hostIP":"10.8.96.55",
+ "podIP":"10.129.0.207",
+ "startTime":"2018-09-17T20:48:36Z",
+ "qosClass":"BestEffort"
+ }
+ },
+ {
+ "metadata":{
+ "name":"redis-1-94zxb",
+ "generateName":"redis-1-",
+ "namespace":"customer-logging",
+ "selfLink":"/api/v1/namespaces/customer-logging/pods/redis-1-94zxb",
+ "uid":"a8aea5f4-5f91-11e8-ba7e-d094660d31fb",
+ "resourceVersion":"47622190",
+ "creationTimestamp":"2018-05-24T20:33:03Z",
+ "labels":{
+ "app":"elastic-log-ripper",
+ "deployment":"redis-1",
+ "deploymentconfig":"redis",
+ "name":"redis"
+ },
+ "annotations":{
+ "openshift.io/deployment-config.latest-version":"1",
+ "openshift.io/deployment-config.name":"redis",
+ "openshift.io/deployment.name":"redis-1",
+ "openshift.io/generated-by":"OpenShiftNewApp",
+ "openshift.io/scc":"restricted"
+ },
+ "ownerReferences":[
+ {
+ "apiVersion":"v1",
+ "kind":"ReplicationController",
+ "name":"redis-1",
+ "uid":"9e2e46b3-5f91-11e8-ba7e-d094660d31fb",
+ "controller":true,
+ "blockOwnerDeletion":true
+ }
+ ]
+ },
+ "spec":{
+ "volumes":[
+ {
+ "name":"default-token-n2wzs",
+ "secret":{
+ "secretName":"default-token-n2wzs",
+ "defaultMode":420
+ }
+ }
+ ],
+ "containers":[
+ {
+ "name":"redis",
+ "image":"manageiq/redis:latest",
+ "ports":[
+ {
+ "containerPort":6379,
+ "protocol":"TCP"
+ }
+ ],
+ "resources":{
+
+ },
+ "volumeMounts":[
+ {
+ "name":"default-token-n2wzs",
+ "readOnly":true,
+ "mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"
+ }
+ ],
+ "terminationMessagePath":"/dev/termination-log",
+ "terminationMessagePolicy":"File",
+ "imagePullPolicy":"Always",
+ "securityContext":{
+ "capabilities":{
+ "drop":[
+ "KILL",
+ "MKNOD",
+ "SETGID",
+ "SETUID"
+ ]
+ },
+ "runAsUser":1000260000
+ }
+ }
+ ],
+ "restartPolicy":"Always",
+ "terminationGracePeriodSeconds":30,
+ "dnsPolicy":"ClusterFirst",
+ "nodeSelector":{
+ "node-role.kubernetes.io/compute":"true"
+ },
+ "serviceAccountName":"default",
+ "serviceAccount":"default",
+ "nodeName":"dell-r430-20.example.com",
+ "securityContext":{
+ "seLinuxOptions":{
+ "level":"s0:c16,c10"
+ },
+ "fsGroup":1000260000
+ },
+ "imagePullSecrets":[
+ {
+ "name":"default-dockercfg-ck286"
+ }
+ ],
+ "schedulerName":"default-scheduler"
+ },
+ "status":{
+ "phase":"Running",
+ "conditions":[
+ {
+ "type":"Initialized",
+ "status":"True",
+ "lastProbeTime":null,
+ "lastTransitionTime":"2018-05-24T20:33:04Z"
+ },
+ {
+ "type":"Ready",
+ "status":"True",
+ "lastProbeTime":null,
+ "lastTransitionTime":"2018-09-18T12:06:18Z"
+ },
+ {
+ "type":"PodScheduled",
+ "status":"True",
+ "lastProbeTime":null,
+ "lastTransitionTime":"2018-05-24T20:33:03Z"
+ }
+ ],
+ "hostIP":"10.8.96.55",
+ "podIP":"10.129.0.222",
+ "startTime":"2018-05-24T20:33:04Z",
+ "qosClass":"BestEffort"
+ }
+ }
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/pods_2.json b/vendor/gems/kubeclient/test/json/pods_2.json
new file mode 100644
index 00000000000..fd6085bc5de
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/pods_2.json
@@ -0,0 +1,102 @@
+{
+ "kind":"PodList",
+ "apiVersion":"v1",
+ "metadata":{
+ "selfLink":"/api/v1/pods",
+ "resourceVersion":"53226147"
+ },
+ "items":[
+ {
+ "metadata":{
+ "name":"topological-inventory-persister-9-hznds",
+ "generateName":"topological-inventory-persister-9-",
+ "namespace":"topological-inventory-ci",
+ "selfLink":"/api/v1/namespaces/topological-inventory-ci/pods/topological-inventory-persister-9-hznds",
+ "uid":"0c114dde-d865-11e8-ba7e-d094660d31fb",
+ "resourceVersion":"51987342",
+ "creationTimestamp":"2018-10-25T14:48:34Z",
+ "labels":{
+ "name":"topological-inventory-persister"
+ }
+ },
+ "spec":{
+ "volumes":[
+ {
+ "name":"default-token-5pdjl",
+ "secret":{
+ "secretName":"default-token-5pdjl",
+ "defaultMode":420
+ }
+ }
+ ],
+ "containers":[
+ {
+ "name":"topological-inventory-persister",
+ "image":"docker-registry.default.svc:5000/topological-inventory-ci/topological-inventory-persister@sha256:0f654ea09e749019cf3bcc4b8ee43b8dd813fcbf487843b917cf190213741927",
+ "resources":{
+ }
+ }
+ ],
+ "restartPolicy":"Always",
+ "terminationGracePeriodSeconds":30,
+ "dnsPolicy":"ClusterFirst",
+ "nodeSelector":{
+ "node-role.kubernetes.io/compute":"true"
+ },
+ "serviceAccountName":"default",
+ "serviceAccount":"default",
+ "nodeName":"dell-r430-20.example.com",
+ "schedulerName":"default-scheduler"
+ },
+ "status":{
+ "phase":"Running",
+ "hostIP":"10.8.96.55",
+ "podIP":"10.129.1.108",
+ "startTime":"2018-10-25T14:48:34Z",
+ "qosClass":"BestEffort"
+ }
+ },
+ {
+ "metadata":{
+ "name":"topological-inventory-persister-9-vzr6h",
+ "generateName":"topological-inventory-persister-9-",
+ "namespace":"topological-inventory-ci",
+ "selfLink":"/api/v1/namespaces/topological-inventory-ci/pods/topological-inventory-persister-9-vzr6h",
+ "uid":"3065d8ce-d86a-11e8-ba7e-d094660d31fb",
+ "resourceVersion":"51996115",
+ "creationTimestamp":"2018-10-25T15:25:22Z",
+ "labels":{
+ "name":"topological-inventory-persister"
+ }
+ },
+ "spec":{
+ "volumes":null,
+ "containers":[
+ {
+ "name":"topological-inventory-persister",
+ "image":"docker-registry.default.svc:5000/topological-inventory-ci/topological-inventory-persister@sha256:0f654ea09e749019cf3bcc4b8ee43b8dd813fcbf487843b917cf190213741927",
+ "resources":{
+ }
+ }
+ ],
+ "restartPolicy":"Always",
+ "terminationGracePeriodSeconds":30,
+ "dnsPolicy":"ClusterFirst",
+ "nodeSelector":{
+ "node-role.kubernetes.io/compute":"true"
+ },
+ "serviceAccountName":"default",
+ "serviceAccount":"default",
+ "nodeName":"dell-r430-20.example.com",
+ "schedulerName":"default-scheduler"
+ },
+ "status":{
+ "phase":"Running",
+ "hostIP":"10.8.96.55",
+ "podIP":"10.129.1.168",
+ "startTime":"2018-10-25T15:25:22Z",
+ "qosClass":"BestEffort"
+ }
+ }
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/pods_410.json b/vendor/gems/kubeclient/test/json/pods_410.json
new file mode 100644
index 00000000000..eb1a31937b7
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/pods_410.json
@@ -0,0 +1,9 @@
+{
+ "kind":"Status",
+ "apiVersion":"v1",
+ "metadata":{},
+ "status":"Failure",
+ "message":"The provided from parameter is too old to display a consistent list result. You must start a new list without the from.",
+ "reason":"Expired",
+ "code":410
+}
diff --git a/vendor/gems/kubeclient/test/json/processed_template.json b/vendor/gems/kubeclient/test/json/processed_template.json
new file mode 100644
index 00000000000..66c6e32ea70
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/processed_template.json
@@ -0,0 +1,27 @@
+{
+ "kind": "Template",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "my-templtae",
+ "namespace": "default",
+ "selfLink": "/oapi/v1/namespaces/default/processedtemplates/my-templtae",
+ "uid": "2240c61c-8f70-11e5-a806-001a4a231290",
+ "resourceVersion": "1399",
+ "creationTimestamp": "2015-11-20T10:19:07Z"
+ },
+ "objects": [
+ {
+ "apiVersion": "v1",
+ "kind": "Service",
+ "metadata": {
+ "name": "test/my-service"
+ }
+ }
+ ],
+ "parameters": [
+ {
+ "name": "NAME_PREFIX",
+ "value": "test/"
+ }
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/replication_controller.json b/vendor/gems/kubeclient/test/json/replication_controller.json
new file mode 100644
index 00000000000..26ac7a9a39e
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/replication_controller.json
@@ -0,0 +1,57 @@
+{
+ "kind": "ReplicationController",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "guestbook-controller",
+ "namespace": "default",
+ "selfLink": "/api/v1/replicationcontrollers/guestbook-controller?namespace=default",
+ "uid": "c71aa4c0-a240-11e4-a265-3c970e4a436a",
+ "resourceVersion": "8",
+ "creationTimestamp": "2015-01-22T16:13:02+02:00",
+ "labels": {
+ "name": "guestbook"
+ }
+ },
+ "spec": {
+ "replicas": 3,
+ "selector": {
+ "name": "guestbook"
+ },
+ "template": {
+ "metadata": {
+ "creationTimestamp": null,
+ "labels": {
+ "name": "guestbook"
+ }
+ },
+ "spec": {
+ "volumes": null,
+ "containers": [
+ {
+ "name": "guestbook",
+ "image": "kubernetes/guestbook",
+ "ports": [
+ {
+ "name": "http-server",
+ "containerPort": 3000,
+ "protocol": "TCP"
+ }
+ ],
+ "memory": "0",
+ "cpu": "0m",
+ "imagePullPolicy": ""
+ }
+ ],
+ "restartPolicy": {
+ "always": {
+
+ }
+ },
+ "dnsPolicy": "ClusterFirst"
+ }
+ }
+ },
+ "status": {
+ "replicas": 3
+ }
+}
diff --git a/vendor/gems/kubeclient/test/json/replication_controller_list.json b/vendor/gems/kubeclient/test/json/replication_controller_list.json
new file mode 100644
index 00000000000..b7f2f7bd521
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/replication_controller_list.json
@@ -0,0 +1,66 @@
+{
+ "kind": "ReplicationControllerList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/namespaces/default/replicationcontrollers",
+ "resourceVersion": "1636"
+ },
+ "items": [
+ {
+ "metadata": {
+ "name": "redis-master-controller",
+ "namespace": "default",
+ "selfLink": "/api/v1/namespaces/default/replicationcontrollers/redis-master-controller?namespace=default",
+ "uid": "108eb547-cefa-11e4-ac24-3c970e4a436a",
+ "resourceVersion": "1631",
+ "creationTimestamp": "2015-03-20T14:10:14+02:00",
+ "labels": {
+ "name": "redis-master"
+ }
+ },
+ "spec": {
+ "replicas": 1,
+ "selector": {
+ "name": "redis-master"
+ },
+ "template": {
+ "metadata": {
+ "creationTimestamp": null,
+ "labels": {
+ "app": "redis",
+ "name": "redis-master"
+ }
+ },
+ "spec": {
+ "volumes": null,
+ "containers": [
+ {
+ "name": "redis-master",
+ "image": "dockerfile/redis",
+ "ports": [
+ {
+ "containerPort": 6379,
+ "protocol": "TCP"
+ }
+ ],
+ "resources": {},
+ "terminationMessagePath": "/dev/termination-log",
+ "imagePullPolicy": "IfNotPresent",
+ "securityContext": {
+ "capabilities": {}
+ }
+ }
+ ],
+ "restartPolicy": {
+ "always": {}
+ },
+ "dnsPolicy": "ClusterFirst"
+ }
+ }
+ },
+ "status": {
+ "replicas": 1
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/resource_quota.json b/vendor/gems/kubeclient/test/json/resource_quota.json
new file mode 100644
index 00000000000..75b40391d68
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/resource_quota.json
@@ -0,0 +1,46 @@
+{
+ "kind": "ResourceQuota",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "quota",
+ "namespace": "quota-example",
+ "selfLink": "/api/v1/namespaces/quota-example/resourcequotas/quota",
+ "uid": "ab9f24a4-3c43-11e5-8214-0aaeec44370e",
+ "resourceVersion": "12919",
+ "creationTimestamp": "2015-08-06T14:01:44Z"
+ },
+ "spec": {
+ "hard": {
+ "cpu": "20",
+ "memory": "1Gi",
+ "persistentvolumeclaims": "10",
+ "pods": "10",
+ "replicationcontrollers": "20",
+ "resourcequotas": "1",
+ "secrets": "10",
+ "services": "5"
+ }
+ },
+ "status": {
+ "hard": {
+ "cpu": "20",
+ "memory": "1Gi",
+ "persistentvolumeclaims": "10",
+ "pods": "10",
+ "replicationcontrollers": "20",
+ "resourcequotas": "1",
+ "secrets": "10",
+ "services": "5"
+ },
+ "used": {
+ "cpu": "0",
+ "memory": "0",
+ "persistentvolumeclaims": "0",
+ "pods": "0",
+ "replicationcontrollers": "1",
+ "resourcequotas": "1",
+ "secrets": "9",
+ "services": "0"
+ }
+ }
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/resource_quota_list.json b/vendor/gems/kubeclient/test/json/resource_quota_list.json
new file mode 100644
index 00000000000..371f3feb649
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/resource_quota_list.json
@@ -0,0 +1,54 @@
+{
+ "kind": "ResourceQuotaList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/namespaces/quota-example/resourcequotas/",
+ "resourceVersion": "102452"
+ },
+ "items": [
+ {
+ "metadata": {
+ "name": "quota",
+ "namespace": "quota-example",
+ "selfLink": "/api/v1/namespaces/quota-example/resourcequotas/quota",
+ "uid": "ab9f24a4-3c43-11e5-8214-0aaeec44370e",
+ "resourceVersion": "12919",
+ "creationTimestamp": "2015-08-06T14:01:44Z"
+ },
+ "spec": {
+ "hard": {
+ "cpu": "20",
+ "memory": "1Gi",
+ "persistentvolumeclaims": "10",
+ "pods": "10",
+ "replicationcontrollers": "20",
+ "resourcequotas": "1",
+ "secrets": "10",
+ "services": "5"
+ }
+ },
+ "status": {
+ "hard": {
+ "cpu": "20",
+ "memory": "1Gi",
+ "persistentvolumeclaims": "10",
+ "pods": "10",
+ "replicationcontrollers": "20",
+ "resourcequotas": "1",
+ "secrets": "10",
+ "services": "5"
+ },
+ "used": {
+ "cpu": "0",
+ "memory": "0",
+ "persistentvolumeclaims": "0",
+ "pods": "0",
+ "replicationcontrollers": "1",
+ "resourcequotas": "1",
+ "secrets": "9",
+ "services": "0"
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/secret_list.json b/vendor/gems/kubeclient/test/json/secret_list.json
new file mode 100644
index 00000000000..bfd7661b390
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/secret_list.json
@@ -0,0 +1,44 @@
+{
+ "kind": "SecretList",
+ "apiVersion":"v1",
+ "metadata":{
+ "selfLink":"/api/v1/secrets",
+ "resourceVersion":"256788"
+ },
+ "items": [
+ {
+ "metadata": {
+ "name":"default-token-my2pi",
+ "namespace":"default",
+ "selfLink":"/api/v1/namespaces/default/secrets/default-token-my2pi",
+ "uid":"07b60654-2a65-11e5-a483-0e840567604d",
+ "resourceVersion":"11",
+ "creationTimestamp":"2015-07-14T20:15:11Z",
+ "annotations": {
+ "kubernetes.io/service-account.name":"default",
+ "kubernetes.io/service-account.uid":"07b350a0-2a65-11e5-a483-0e840567604d"
+ }
+ },
+ "data":{
+ "ca.crt":"Y2F0J3MgYXJlIGF3ZXNvbWUK",
+ "token":"Y2F0J3MgYXJlIGF3ZXNvbWUK"
+ },
+ "type":"kubernetes.io/service-account-token"
+ },
+ {
+ "metadata": {
+ "name": "test-secret",
+ "namespace": "dev",
+ "selfLink": "/api/v1/namespaces/dev/secrets/test-secret",
+ "uid": "4e38a198-2bcb-11e5-a483-0e840567604d",
+ "resourceVersion": "245569",
+ "creationTimestamp": "2015-07-16T14:59:49Z"
+ },
+ "data": {
+ "super-secret": "Y2F0J3MgYXJlIGF3ZXNvbWUK"
+ },
+ "type": "Opaque"
+ }
+ ]
+}
+
diff --git a/vendor/gems/kubeclient/test/json/security.openshift.io_api_resource_list.json b/vendor/gems/kubeclient/test/json/security.openshift.io_api_resource_list.json
new file mode 100644
index 00000000000..18e2021cff5
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/security.openshift.io_api_resource_list.json
@@ -0,0 +1,69 @@
+{
+ "kind": "APIResourceList",
+ "apiVersion": "v1",
+ "groupVersion": "security.openshift.io/v1",
+ "resources": [
+ {
+ "name": "podsecuritypolicyreviews",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "PodSecurityPolicyReview",
+ "verbs": [
+ "create"
+ ]
+ },
+ {
+ "name": "podsecuritypolicyselfsubjectreviews",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "PodSecurityPolicySelfSubjectReview",
+ "verbs": [
+ "create"
+ ]
+ },
+ {
+ "name": "podsecuritypolicysubjectreviews",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "PodSecurityPolicySubjectReview",
+ "verbs": [
+ "create"
+ ]
+ },
+ {
+ "name": "rangeallocations",
+ "singularName": "",
+ "namespaced": false,
+ "kind": "RangeAllocation",
+ "verbs": [
+ "create",
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "securitycontextconstraints",
+ "singularName": "",
+ "namespaced": false,
+ "kind": "SecurityContextConstraints",
+ "verbs": [
+ "create",
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "update",
+ "watch"
+ ],
+ "shortNames": [
+ "scc"
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/security_context_constraint_list.json b/vendor/gems/kubeclient/test/json/security_context_constraint_list.json
new file mode 100644
index 00000000000..1e9d4c474a7
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/security_context_constraint_list.json
@@ -0,0 +1,375 @@
+{
+ "kind": "SecurityContextConstraintsList",
+ "apiVersion": "security.openshift.io/v1",
+ "metadata": {
+ "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints",
+ "resourceVersion": "5751"
+ },
+ "items": [
+ {
+ "metadata": {
+ "name": "anyuid",
+ "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints/anyuid",
+ "uid": "12ba8540-ef00-11e8-b4c0-68f728fac3ab",
+ "resourceVersion": "71",
+ "creationTimestamp": "2018-11-23T09:13:42Z",
+ "annotations": {
+ "kubernetes.io/description": "anyuid provides all features of the restricted SCC but allows users to run with any UID and any GID."
+ }
+ },
+ "priority": 10,
+ "allowPrivilegedContainer": false,
+ "defaultAddCapabilities": null,
+ "requiredDropCapabilities": [
+ "MKNOD"
+ ],
+ "allowedCapabilities": null,
+ "allowHostDirVolumePlugin": false,
+ "volumes": [
+ "configMap",
+ "downwardAPI",
+ "emptyDir",
+ "persistentVolumeClaim",
+ "projected",
+ "secret"
+ ],
+ "allowedFlexVolumes": null,
+ "allowHostNetwork": false,
+ "allowHostPorts": false,
+ "allowHostPID": false,
+ "allowHostIPC": false,
+ "seLinuxContext": {
+ "type": "MustRunAs"
+ },
+ "runAsUser": {
+ "type": "RunAsAny"
+ },
+ "supplementalGroups": {
+ "type": "RunAsAny"
+ },
+ "fsGroup": {
+ "type": "RunAsAny"
+ },
+ "readOnlyRootFilesystem": false,
+ "users": [],
+ "groups": [
+ "system:cluster-admins"
+ ]
+ },
+ {
+ "metadata": {
+ "name": "hostaccess",
+ "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints/hostaccess",
+ "uid": "12b5b3a2-ef00-11e8-b4c0-68f728fac3ab",
+ "resourceVersion": "69",
+ "creationTimestamp": "2018-11-23T09:13:42Z",
+ "annotations": {
+ "kubernetes.io/description": "hostaccess allows access to all host namespaces but still requires pods to be run with a UID and SELinux context that are allocated to the namespace. WARNING: this SCC allows host access to namespaces, file systems, and PIDS. It should only be used by trusted pods. Grant with caution."
+ }
+ },
+ "priority": null,
+ "allowPrivilegedContainer": false,
+ "defaultAddCapabilities": null,
+ "requiredDropCapabilities": [
+ "KILL",
+ "MKNOD",
+ "SETUID",
+ "SETGID"
+ ],
+ "allowedCapabilities": null,
+ "allowHostDirVolumePlugin": true,
+ "volumes": [
+ "configMap",
+ "downwardAPI",
+ "emptyDir",
+ "hostPath",
+ "persistentVolumeClaim",
+ "projected",
+ "secret"
+ ],
+ "allowedFlexVolumes": null,
+ "allowHostNetwork": true,
+ "allowHostPorts": true,
+ "allowHostPID": true,
+ "allowHostIPC": true,
+ "seLinuxContext": {
+ "type": "MustRunAs"
+ },
+ "runAsUser": {
+ "type": "MustRunAsRange"
+ },
+ "supplementalGroups": {
+ "type": "RunAsAny"
+ },
+ "fsGroup": {
+ "type": "MustRunAs"
+ },
+ "readOnlyRootFilesystem": false,
+ "users": [],
+ "groups": []
+ },
+ {
+ "metadata": {
+ "name": "hostmount-anyuid",
+ "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints/hostmount-anyuid",
+ "uid": "12b512c0-ef00-11e8-b4c0-68f728fac3ab",
+ "resourceVersion": "68",
+ "creationTimestamp": "2018-11-23T09:13:42Z",
+ "annotations": {
+ "kubernetes.io/description": "hostmount-anyuid provides all the features of the restricted SCC but allows host mounts and any UID by a pod. This is primarily used by the persistent volume recycler. WARNING: this SCC allows host file system access as any UID, including UID 0. Grant with caution."
+ }
+ },
+ "priority": null,
+ "allowPrivilegedContainer": false,
+ "defaultAddCapabilities": null,
+ "requiredDropCapabilities": [
+ "MKNOD"
+ ],
+ "allowedCapabilities": null,
+ "allowHostDirVolumePlugin": true,
+ "volumes": [
+ "configMap",
+ "downwardAPI",
+ "emptyDir",
+ "hostPath",
+ "nfs",
+ "persistentVolumeClaim",
+ "projected",
+ "secret"
+ ],
+ "allowedFlexVolumes": null,
+ "allowHostNetwork": false,
+ "allowHostPorts": false,
+ "allowHostPID": false,
+ "allowHostIPC": false,
+ "seLinuxContext": {
+ "type": "MustRunAs"
+ },
+ "runAsUser": {
+ "type": "RunAsAny"
+ },
+ "supplementalGroups": {
+ "type": "RunAsAny"
+ },
+ "fsGroup": {
+ "type": "RunAsAny"
+ },
+ "readOnlyRootFilesystem": false,
+ "users": [
+ "system:serviceaccount:openshift-infra:pv-recycler-controller"
+ ],
+ "groups": []
+ },
+ {
+ "metadata": {
+ "name": "hostnetwork",
+ "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints/hostnetwork",
+ "uid": "12bb0984-ef00-11e8-b4c0-68f728fac3ab",
+ "resourceVersion": "72",
+ "creationTimestamp": "2018-11-23T09:13:42Z",
+ "annotations": {
+ "kubernetes.io/description": "hostnetwork allows using host networking and host ports but still requires pods to be run with a UID and SELinux context that are allocated to the namespace."
+ }
+ },
+ "priority": null,
+ "allowPrivilegedContainer": false,
+ "defaultAddCapabilities": null,
+ "requiredDropCapabilities": [
+ "KILL",
+ "MKNOD",
+ "SETUID",
+ "SETGID"
+ ],
+ "allowedCapabilities": null,
+ "allowHostDirVolumePlugin": false,
+ "volumes": [
+ "configMap",
+ "downwardAPI",
+ "emptyDir",
+ "persistentVolumeClaim",
+ "projected",
+ "secret"
+ ],
+ "allowedFlexVolumes": null,
+ "allowHostNetwork": true,
+ "allowHostPorts": true,
+ "allowHostPID": false,
+ "allowHostIPC": false,
+ "seLinuxContext": {
+ "type": "MustRunAs"
+ },
+ "runAsUser": {
+ "type": "MustRunAsRange"
+ },
+ "supplementalGroups": {
+ "type": "MustRunAs"
+ },
+ "fsGroup": {
+ "type": "MustRunAs"
+ },
+ "readOnlyRootFilesystem": false,
+ "users": [],
+ "groups": []
+ },
+ {
+ "metadata": {
+ "name": "nonroot",
+ "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints/nonroot",
+ "uid": "12b37c59-ef00-11e8-b4c0-68f728fac3ab",
+ "resourceVersion": "67",
+ "creationTimestamp": "2018-11-23T09:13:42Z",
+ "annotations": {
+ "kubernetes.io/description": "nonroot provides all features of the restricted SCC but allows users to run with any non-root UID. The user must specify the UID or it must be specified on the by the manifest of the container runtime."
+ }
+ },
+ "priority": null,
+ "allowPrivilegedContainer": false,
+ "defaultAddCapabilities": null,
+ "requiredDropCapabilities": [
+ "KILL",
+ "MKNOD",
+ "SETUID",
+ "SETGID"
+ ],
+ "allowedCapabilities": null,
+ "allowHostDirVolumePlugin": false,
+ "volumes": [
+ "configMap",
+ "downwardAPI",
+ "emptyDir",
+ "persistentVolumeClaim",
+ "projected",
+ "secret"
+ ],
+ "allowedFlexVolumes": null,
+ "allowHostNetwork": false,
+ "allowHostPorts": false,
+ "allowHostPID": false,
+ "allowHostIPC": false,
+ "seLinuxContext": {
+ "type": "MustRunAs"
+ },
+ "runAsUser": {
+ "type": "MustRunAsNonRoot"
+ },
+ "supplementalGroups": {
+ "type": "RunAsAny"
+ },
+ "fsGroup": {
+ "type": "RunAsAny"
+ },
+ "readOnlyRootFilesystem": false,
+ "users": [],
+ "groups": []
+ },
+ {
+ "metadata": {
+ "name": "privileged",
+ "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints/privileged",
+ "uid": "12b18f4a-ef00-11e8-b4c0-68f728fac3ab",
+ "resourceVersion": "300",
+ "creationTimestamp": "2018-11-23T09:13:42Z",
+ "annotations": {
+ "kubernetes.io/description": "privileged allows access to all privileged and host features and the ability to run as any user, any group, any fsGroup, and with any SELinux context. WARNING: this is the most relaxed SCC and should be used only for cluster administration. Grant with caution."
+ }
+ },
+ "priority": null,
+ "allowPrivilegedContainer": true,
+ "defaultAddCapabilities": null,
+ "requiredDropCapabilities": null,
+ "allowedCapabilities": [
+ "*"
+ ],
+ "allowHostDirVolumePlugin": true,
+ "volumes": [
+ "*"
+ ],
+ "allowedFlexVolumes": null,
+ "allowHostNetwork": true,
+ "allowHostPorts": true,
+ "allowHostPID": true,
+ "allowHostIPC": true,
+ "seLinuxContext": {
+ "type": "RunAsAny"
+ },
+ "runAsUser": {
+ "type": "RunAsAny"
+ },
+ "supplementalGroups": {
+ "type": "RunAsAny"
+ },
+ "fsGroup": {
+ "type": "RunAsAny"
+ },
+ "readOnlyRootFilesystem": false,
+ "users": [
+ "system:admin",
+ "system:serviceaccount:openshift-infra:build-controller",
+ "system:serviceaccount:default:pvinstaller",
+ "system:serviceaccount:default:registry",
+ "system:serviceaccount:default:router"
+ ],
+ "groups": [
+ "system:cluster-admins",
+ "system:nodes",
+ "system:masters"
+ ],
+ "seccompProfiles": [
+ "*"
+ ]
+ },
+ {
+ "metadata": {
+ "name": "restricted",
+ "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints/restricted",
+ "uid": "12b9a842-ef00-11e8-b4c0-68f728fac3ab",
+ "resourceVersion": "70",
+ "creationTimestamp": "2018-11-23T09:13:42Z",
+ "annotations": {
+ "kubernetes.io/description": "restricted denies access to all host features and requires pods to be run with a UID, and SELinux context that are allocated to the namespace. This is the most restrictive SCC and it is used by default for authenticated users."
+ }
+ },
+ "priority": null,
+ "allowPrivilegedContainer": false,
+ "defaultAddCapabilities": null,
+ "requiredDropCapabilities": [
+ "KILL",
+ "MKNOD",
+ "SETUID",
+ "SETGID"
+ ],
+ "allowedCapabilities": null,
+ "allowHostDirVolumePlugin": false,
+ "volumes": [
+ "configMap",
+ "downwardAPI",
+ "emptyDir",
+ "persistentVolumeClaim",
+ "projected",
+ "secret"
+ ],
+ "allowedFlexVolumes": null,
+ "allowHostNetwork": false,
+ "allowHostPorts": false,
+ "allowHostPID": false,
+ "allowHostIPC": false,
+ "seLinuxContext": {
+ "type": "MustRunAs"
+ },
+ "runAsUser": {
+ "type": "MustRunAsRange"
+ },
+ "supplementalGroups": {
+ "type": "RunAsAny"
+ },
+ "fsGroup": {
+ "type": "MustRunAs"
+ },
+ "readOnlyRootFilesystem": false,
+ "users": [],
+ "groups": [
+ "system:authenticated"
+ ]
+ }
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/service.json b/vendor/gems/kubeclient/test/json/service.json
new file mode 100644
index 00000000000..6e2e5c79ce1
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/service.json
@@ -0,0 +1,33 @@
+{
+ "kind": "Service",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "redis-slave",
+ "namespace": "development",
+ "selfLink": "/api/v1/namespaces/development/services/redis-slave",
+ "uid": "bdb80a8f-db93-11e4-b293-f8b156af4ae1",
+ "resourceVersion": "2815",
+ "creationTimestamp": "2015-04-05T13:00:31Z",
+ "labels": {
+ "name": "redis",
+ "role": "slave"
+ }
+ },
+ "spec": {
+ "ports": [
+ {
+ "name": "",
+ "protocol": "TCP",
+ "port": 6379,
+ "targetPort": "redis-server"
+ }
+ ],
+ "selector": {
+ "name": "redis",
+ "role": "slave"
+ },
+ "clusterIP": "10.0.0.140",
+ "sessionAffinity": "None"
+ },
+ "status": {}
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/service_account.json b/vendor/gems/kubeclient/test/json/service_account.json
new file mode 100644
index 00000000000..632d429260d
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/service_account.json
@@ -0,0 +1,25 @@
+{
+ "kind": "ServiceAccount",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "default",
+ "namespace": "default",
+ "selfLink": "/api/v1/namespaces/default/serviceaccounts/default",
+ "uid": "d3d773f4-6bf0-11e5-843a-525400f8b93e",
+ "resourceVersion": "94",
+ "creationTimestamp": "2015-10-06T06:09:39Z"
+ },
+ "secrets": [
+ {
+ "name": "default-token-6s23q"
+ },
+ {
+ "name": "default-dockercfg-62tf3"
+ }
+ ],
+ "imagePullSecrets": [
+ {
+ "name": "default-dockercfg-62tf3"
+ }
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/service_account_list.json b/vendor/gems/kubeclient/test/json/service_account_list.json
new file mode 100644
index 00000000000..934e729ed08
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/service_account_list.json
@@ -0,0 +1,82 @@
+{
+ "kind": "List",
+ "apiVersion": "v1",
+ "metadata": {},
+ "items": [
+ {
+ "kind": "ServiceAccount",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "builder",
+ "namespace": "default",
+ "selfLink": "/api/v1/namespaces/default/serviceaccounts/builder",
+ "uid": "d40655f6-6bf0-11e5-843a-525400f8b93e",
+ "resourceVersion": "133",
+ "creationTimestamp": "2015-10-06T06:09:39Z"
+ },
+ "secrets": [
+ {
+ "name": "builder-token-5v6z2"
+ },
+ {
+ "name": "builder-dockercfg-qe2re"
+ }
+ ],
+ "imagePullSecrets": [
+ {
+ "name": "builder-dockercfg-qe2re"
+ }
+ ]
+ },
+ {
+ "kind": "ServiceAccount",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "default",
+ "namespace": "default",
+ "selfLink": "/api/v1/namespaces/default/serviceaccounts/default",
+ "uid": "d3d773f4-6bf0-11e5-843a-525400f8b93e",
+ "resourceVersion": "94",
+ "creationTimestamp": "2015-10-06T06:09:39Z"
+ },
+ "secrets": [
+ {
+ "name": "default-token-6s23q"
+ },
+ {
+ "name": "default-dockercfg-62tf3"
+ }
+ ],
+ "imagePullSecrets": [
+ {
+ "name": "default-dockercfg-62tf3"
+ }
+ ]
+ },
+ {
+ "kind": "ServiceAccount",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "deployer",
+ "namespace": "default",
+ "selfLink": "/api/v1/namespaces/default/serviceaccounts/deployer",
+ "uid": "d41d385e-6bf0-11e5-843a-525400f8b93e",
+ "resourceVersion": "137",
+ "creationTimestamp": "2015-10-06T06:09:39Z"
+ },
+ "secrets": [
+ {
+ "name": "deployer-token-h3i57"
+ },
+ {
+ "name": "deployer-dockercfg-qgjjj"
+ }
+ ],
+ "imagePullSecrets": [
+ {
+ "name": "deployer-dockercfg-qgjjj"
+ }
+ ]
+ }
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/service_illegal_json_404.json b/vendor/gems/kubeclient/test/json/service_illegal_json_404.json
new file mode 100644
index 00000000000..faa82b3aa87
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/service_illegal_json_404.json
@@ -0,0 +1 @@
+404: Page Not Found \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/service_json_patch.json b/vendor/gems/kubeclient/test/json/service_json_patch.json
new file mode 100644
index 00000000000..f960242ba4d
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/service_json_patch.json
@@ -0,0 +1,26 @@
+{
+ "status" : {},
+ "kind" : "Service",
+ "apiVersion" : "v1",
+ "spec" : {
+ "ports" : [
+ {
+ "targetPort" : 80,
+ "nodePort" : 0,
+ "port" : 80,
+ "protocol" : "TCP"
+ }
+ ],
+ "clusterIP" : "1.2.3.4",
+ "type": "LoadBalancer"
+ },
+ "metadata" : {
+ "name" : "my-service",
+ "creationTimestamp" : null,
+ "namespace" : "development",
+ "resourceVersion" : "2",
+ "annotations" : {
+ "key" : "value"
+ }
+ }
+}
diff --git a/vendor/gems/kubeclient/test/json/service_list.json b/vendor/gems/kubeclient/test/json/service_list.json
new file mode 100644
index 00000000000..f0f97a12c4e
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/service_list.json
@@ -0,0 +1,97 @@
+{
+ "kind": "ServiceList",
+ "apiVersion": "v1",
+ "metadata": {
+ "selfLink": "/api/v1/services",
+ "resourceVersion": "36727"
+ },
+ "items": [
+ {
+ "metadata": {
+ "name": "kubernetes",
+ "namespace": "default",
+ "selfLink": "/api/v1/namespaces/default/services/kubernetes",
+ "uid": "b6606490-db86-11e4-b293-f8b156af4ae1",
+ "resourceVersion": "6",
+ "creationTimestamp": "2015-04-05T11:27:15Z",
+ "labels": {
+ "component": "apiserver",
+ "provider": "kubernetes"
+ }
+ },
+ "spec": {
+ "ports": [
+ {
+ "name": "",
+ "protocol": "TCP",
+ "port": 443,
+ "targetPort": 443
+ }
+ ],
+ "selector": null,
+ "clusterIP": "10.0.0.2",
+ "sessionAffinity": "None"
+ },
+ "status": {}
+ },
+ {
+ "metadata": {
+ "name": "kubernetes-ro",
+ "namespace": "default",
+ "selfLink": "/api/v1/namespaces/default/services/kubernetes-ro",
+ "uid": "b6606694-db86-11e4-b293-f8b156af4ae1",
+ "resourceVersion": "5",
+ "creationTimestamp": "2015-04-05T11:27:15Z",
+ "labels": {
+ "component": "apiserver",
+ "provider": "kubernetes"
+ }
+ },
+ "spec": {
+ "ports": [
+ {
+ "name": "",
+ "protocol": "TCP",
+ "port": 80,
+ "targetPort": 80
+ }
+ ],
+ "selector": null,
+ "clusterIP": "10.0.0.1",
+ "sessionAffinity": "None"
+ },
+ "status": {}
+ },
+ {
+ "metadata": {
+ "name": "redis-slave",
+ "namespace": "development",
+ "selfLink": "/api/v1/namespaces/development/services/redis-slave",
+ "uid": "bdb80a8f-db93-11e4-b293-f8b156af4ae1",
+ "resourceVersion": "2815",
+ "creationTimestamp": "2015-04-05T13:00:31Z",
+ "labels": {
+ "name": "redis",
+ "role": "slave"
+ }
+ },
+ "spec": {
+ "ports": [
+ {
+ "name": "",
+ "protocol": "TCP",
+ "port": 6379,
+ "targetPort": "redis-server"
+ }
+ ],
+ "selector": {
+ "name": "redis",
+ "role": "slave"
+ },
+ "clusterIP": "10.0.0.140",
+ "sessionAffinity": "None"
+ },
+ "status": {}
+ }
+ ]
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/service_merge_patch.json b/vendor/gems/kubeclient/test/json/service_merge_patch.json
new file mode 100644
index 00000000000..5a9cf7dd384
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/service_merge_patch.json
@@ -0,0 +1,26 @@
+{
+ "status" : {},
+ "kind" : "Service",
+ "apiVersion" : "v1",
+ "spec" : {
+ "ports" : [
+ {
+ "targetPort" : 80,
+ "nodePort" : 0,
+ "port" : 80,
+ "protocol" : "TCP"
+ }
+ ],
+ "clusterIP" : "1.2.3.4",
+ "type": "NodePort"
+ },
+ "metadata" : {
+ "name" : "my-service",
+ "creationTimestamp" : null,
+ "namespace" : "development",
+ "resourceVersion" : "2",
+ "annotations" : {
+ "key" : "value"
+ }
+ }
+}
diff --git a/vendor/gems/kubeclient/test/json/service_patch.json b/vendor/gems/kubeclient/test/json/service_patch.json
new file mode 100644
index 00000000000..d6c9c29e691
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/service_patch.json
@@ -0,0 +1,25 @@
+{
+ "status" : {},
+ "kind" : "Service",
+ "apiVersion" : "v1",
+ "spec" : {
+ "ports" : [
+ {
+ "targetPort" : 80,
+ "nodePort" : 0,
+ "port" : 80,
+ "protocol" : "TCP"
+ }
+ ],
+ "clusterIP" : "1.2.3.4"
+ },
+ "metadata" : {
+ "name" : "my_service",
+ "creationTimestamp" : null,
+ "namespace" : "development",
+ "resourceVersion" : "2",
+ "annotations" : {
+ "key" : "value"
+ }
+ }
+}
diff --git a/vendor/gems/kubeclient/test/json/service_update.json b/vendor/gems/kubeclient/test/json/service_update.json
new file mode 100644
index 00000000000..1053d750e60
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/service_update.json
@@ -0,0 +1,22 @@
+{
+ "status" : {},
+ "kind" : "Service",
+ "apiVersion" : "v1",
+ "spec" : {
+ "ports" : [
+ {
+ "targetPort" : 80,
+ "nodePort" : 0,
+ "port" : 80,
+ "protocol" : "TCP"
+ }
+ ],
+ "clusterIP" : "1.2.3.4"
+ },
+ "metadata" : {
+ "name" : "my_service",
+ "creationTimestamp" : null,
+ "namespace" : "default",
+ "resourceVersion" : "2"
+ }
+}
diff --git a/vendor/gems/kubeclient/test/json/template.json b/vendor/gems/kubeclient/test/json/template.json
new file mode 100644
index 00000000000..85d8bad748e
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/template.json
@@ -0,0 +1,27 @@
+{
+ "apiVersion": "template.openshift.io/v1",
+ "kind": "Template",
+ "metadata": {
+ "creationTimestamp": "2018-12-17T16:11:36Z",
+ "name": "my-template",
+ "namespace": "default",
+ "resourceVersion": "21954",
+ "selfLink": "/apis/template.openshift.io/v1/namespaces/default/templates/my-template",
+ "uid": "6e03e3e6-0216-11e9-b1e0-68f728fac3ab"
+ },
+ "objects": [
+ {
+ "apiVersion": "v1",
+ "kind": "Service",
+ "metadata": {
+ "name": "${NAME_PREFIX}my-service"
+ }
+ }
+ ],
+ "parameters": [
+ {
+ "description": "Prefix for names",
+ "name": "NAME_PREFIX"
+ }
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/template.openshift.io_api_resource_list.json b/vendor/gems/kubeclient/test/json/template.openshift.io_api_resource_list.json
new file mode 100644
index 00000000000..1ba147f795d
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/template.openshift.io_api_resource_list.json
@@ -0,0 +1,75 @@
+{
+ "kind": "APIResourceList",
+ "apiVersion": "v1",
+ "groupVersion": "template.openshift.io/v1",
+ "resources": [
+ {
+ "name": "brokertemplateinstances",
+ "singularName": "",
+ "namespaced": false,
+ "kind": "BrokerTemplateInstance",
+ "verbs": [
+ "create",
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "processedtemplates",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "Template",
+ "verbs": [
+ "create"
+ ]
+ },
+ {
+ "name": "templateinstances",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "TemplateInstance",
+ "verbs": [
+ "create",
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "update",
+ "watch"
+ ]
+ },
+ {
+ "name": "templateinstances/status",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "TemplateInstance",
+ "verbs": [
+ "get",
+ "patch",
+ "update"
+ ]
+ },
+ {
+ "name": "templates",
+ "singularName": "",
+ "namespaced": true,
+ "kind": "Template",
+ "verbs": [
+ "create",
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "update",
+ "watch"
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/vendor/gems/kubeclient/test/json/template_list.json b/vendor/gems/kubeclient/test/json/template_list.json
new file mode 100644
index 00000000000..a0f84ad3e01
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/template_list.json
@@ -0,0 +1,35 @@
+{
+ "kind": "TemplateList",
+ "apiVersion": "template.openshift.io/v1",
+ "metadata": {
+ "selfLink": "/apis/template.openshift.io/v1/namespaces/default/templates",
+ "resourceVersion": "22758"
+ },
+ "items": [
+ {
+ "metadata": {
+ "name": "my-template",
+ "namespace": "default",
+ "selfLink": "/apis/template.openshift.io/v1/namespaces/default/templates/my-template",
+ "uid": "6e03e3e6-0216-11e9-b1e0-68f728fac3ab",
+ "resourceVersion": "21954",
+ "creationTimestamp": "2018-12-17T16:11:36Z"
+ },
+ "objects": [
+ {
+ "apiVersion": "v1",
+ "kind": "Service",
+ "metadata": {
+ "name": "${NAME_PREFIX}my-service"
+ }
+ }
+ ],
+ "parameters": [
+ {
+ "name": "NAME_PREFIX",
+ "description": "Prefix for names"
+ }
+ ]
+ }
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/versions_list.json b/vendor/gems/kubeclient/test/json/versions_list.json
new file mode 100644
index 00000000000..372e101b6ab
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/versions_list.json
@@ -0,0 +1,6 @@
+{
+ "versions": [
+ "v1beta3",
+ "v1"
+ ]
+}
diff --git a/vendor/gems/kubeclient/test/json/watch_stream.json b/vendor/gems/kubeclient/test/json/watch_stream.json
new file mode 100644
index 00000000000..aa8f03dd078
--- /dev/null
+++ b/vendor/gems/kubeclient/test/json/watch_stream.json
@@ -0,0 +1,3 @@
+{"type":"ADDED","object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"php","namespace":"default","selfLink":"/api/v1/pods/php","uid":"e75f2c07-b047-11e4-89e4-525400c903c1","resourceVersion":"1389","creationTimestamp":"2015-02-09T05:39:19-05:00","labels":{"name":"foo"}},"spec":{"volumes":null,"containers":[{"name":"nginx","image":"dockerfile/nginx","ports":[{"hostPort":9090,"containerPort":80,"protocol":"TCP"}],"resources":{},"livenessProbe":{"httpGet":{"path":"/index.html","port":"9090"},"initialDelaySeconds":30},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent","securityContext":{"capabilities":{}}}],"restartPolicy":{"always":{}},"dnsPolicy":"ClusterFirst"},"status":{"phase":"Pending"}}}
+{"type":"MODIFIED","object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"php","namespace":"default","selfLink":"/api/v1/pods/php","uid":"e75f2c07-b047-11e4-89e4-525400c903c1","resourceVersion":"1390","creationTimestamp":"2015-02-09T05:39:19-05:00","labels":{"name":"foo"}},"spec":{"volumes":null,"containers":[{"name":"nginx","image":"dockerfile/nginx","ports":[{"hostPort":9090,"containerPort":80,"protocol":"TCP"}],"resources":{},"livenessProbe":{"httpGet":{"path":"/index.html","port":"9090"},"initialDelaySeconds":30},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent","securityContext":{"capabilities":{}}}],"restartPolicy":{"always":{}},"dnsPolicy":"ClusterFirst"},"status":{"phase":"Pending","host":"127.0.0.1"}}}
+{"type":"DELETED","object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"php","namespace":"default","selfLink":"/api/v1/pods/php","uid":"e75f2c07-b047-11e4-89e4-525400c903c1","resourceVersion":"1398","creationTimestamp":"2015-02-09T05:39:19-05:00","labels":{"name":"foo"}},"spec":{"volumes":null,"containers":[{"name":"nginx","image":"dockerfile/nginx","ports":[{"hostPort":9090,"containerPort":80,"protocol":"TCP"}],"resources":{},"livenessProbe":{"httpGet":{"path":"/index.html","port":"9090"},"initialDelaySeconds":30},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent","securityContext":{"capabilities":{}}}],"restartPolicy":{"always":{}},"dnsPolicy":"ClusterFirst"},"status":{"phase":"Pending","host":"127.0.0.1"}}}
diff --git a/vendor/gems/kubeclient/test/test_common.rb b/vendor/gems/kubeclient/test/test_common.rb
new file mode 100644
index 00000000000..32bf826085d
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_common.rb
@@ -0,0 +1,95 @@
+
+require_relative 'test_helper'
+
+# Unit tests for the common module
+class CommonTest < MiniTest::Test
+ class ClientStub
+ include Kubeclient::ClientMixin
+ end
+
+ def client
+ @client ||= ClientStub.new
+ end
+
+ def test_underscore_entity
+ %w[
+ Pod pod
+ Service service
+ ReplicationController replication_controller
+ Node node
+ Event event
+ Endpoint endpoint
+ Namespace namespace
+ Secret secret
+ ResourceQuota resource_quota
+ LimitRange limit_range
+ PersistentVolume persistent_volume
+ PersistentVolumeClaim persistent_volume_claim
+ ComponentStatus component_status
+ ServiceAccount service_account
+ Project project
+ Route route
+ ClusterRoleBinding cluster_role_binding
+ Build build
+ BuildConfig build_config
+ Image image
+ ImageStream image_stream
+ dogstatsd dogstatsd
+ lowerCamelUPPERCase lower_camel_uppercase
+ HTTPAPISpecBinding httpapispec_binding
+ APIGroup apigroup
+ APIGroupList apigroup_list
+ APIResourceList apiresource_list
+ APIService apiservice
+ APIServiceList apiservice_list
+ APIVersions apiversions
+ OAuthAccessToken oauth_access_token
+ OAuthAccessTokenList oauth_access_token_list
+ OAuthAuthorizeToken oauth_authorize_token
+ OAuthAuthorizeTokenList oauth_authorize_token_list
+ OAuthClient oauth_client
+ OAuthClientAuthorization oauth_client_authorization
+ OAuthClientAuthorizationList oauth_client_authorization_list
+ OAuthClientList oauth_client_list
+ ].each_slice(2) do |kind, expected_underscore|
+ underscore = Kubeclient::ClientMixin.underscore_entity(kind)
+ assert_equal(underscore, expected_underscore)
+ end
+ end
+
+ def test_format_datetime_with_string
+ value = '2018-04-27T18:30:17.480321984Z'
+ formatted = client.send(:format_datetime, value)
+ assert_equal(formatted, value)
+ end
+
+ def test_format_datetime_with_datetime
+ value = DateTime.new(2018, 4, 30, 19, 20, 33)
+ formatted = client.send(:format_datetime, value)
+ assert_equal(formatted, '2018-04-30T19:20:33.000000000+00:00')
+ end
+
+ def test_format_datetime_with_time
+ value = Time.new(2018, 4, 30, 19, 20, 33, 0)
+ formatted = client.send(:format_datetime, value)
+ assert_equal(formatted, '2018-04-30T19:20:33.000000000+00:00')
+ end
+
+ def test_parse_definition_with_unconventional_names
+ %w[
+ PluralPolicy pluralpolicies plural_policy plural_policies
+ LatinDatum latindata latin_datum latin_data
+ Noseparator noseparators noseparator noseparators
+ lowercase lowercases lowercase lowercases
+ TestWithDash test-with-dashes test_with_dash test_with_dashes
+ TestUnderscore test_underscores test_underscore test_underscores
+ TestMismatch other-odd-name testmismatch otheroddname
+ MixedDashMinus mixed-dash_minuses mixed_dash_minus mixed_dash_minuses
+ SameUptoWordboundary sameup-toword-boundarys sameuptowordboundary sameuptowordboundarys
+ ].each_slice(4) do |kind, plural, expected_single, expected_plural|
+ method_names = Kubeclient::ClientMixin.parse_definition(kind, plural).method_names
+ assert_equal(method_names[0], expected_single)
+ assert_equal(method_names[1], expected_plural)
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_common_url_handling.rb b/vendor/gems/kubeclient/test/test_common_url_handling.rb
new file mode 100644
index 00000000000..e7798c2f489
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_common_url_handling.rb
@@ -0,0 +1,160 @@
+require_relative 'test_helper'
+
+# URLHandling tests
+class TestCommonUrlHandling < MiniTest::Test
+ def test_no_path_in_uri
+ client = Kubeclient::Client.new('http://localhost:8080', 'v1')
+ rest_client = client.rest_client
+ assert_equal('v1', client.instance_variable_get(:@api_version))
+ assert_equal('', client.instance_variable_get(:@api_group))
+ assert_equal('http://localhost:8080/api/v1', rest_client.url.to_s)
+ end
+
+ def test_with_api_path_in_uri
+ client = Kubeclient::Client.new('http://localhost:8080/api', 'v1')
+ rest_client = client.rest_client
+ assert_equal('v1', client.instance_variable_get(:@api_version))
+ assert_equal('', client.instance_variable_get(:@api_group))
+ assert_equal('http://localhost:8080/api/v1', rest_client.url.to_s)
+ end
+
+ def test_with_api_path_in_uri_other_version
+ client = Kubeclient::Client.new('http://localhost:8080/api', 'v2')
+ rest_client = client.rest_client
+ assert_equal('v2', client.instance_variable_get(:@api_version))
+ assert_equal('', client.instance_variable_get(:@api_group))
+ assert_equal('http://localhost:8080/api/v2', rest_client.url.to_s)
+ end
+
+ def test_with_api_group_path_in_uri
+ client = Kubeclient::Client.new('http://localhost:8080/apis/this_is_the_group', 'v1')
+ rest_client = client.rest_client
+ assert_equal('v1', client.instance_variable_get(:@api_version))
+ assert_equal('this_is_the_group/', client.instance_variable_get(:@api_group))
+ assert_equal('http://localhost:8080/apis/this_is_the_group/v1', rest_client.url.to_s)
+ end
+
+ def test_with_api_group_path_in_uri_other_version
+ client = Kubeclient::Client.new('http://localhost:8080/apis/this_is_the_group', 'v2')
+ rest_client = client.rest_client
+ assert_equal('v2', client.instance_variable_get(:@api_version))
+ assert_equal('this_is_the_group/', client.instance_variable_get(:@api_group))
+ assert_equal('http://localhost:8080/apis/this_is_the_group/v2', rest_client.url.to_s)
+ end
+
+ def test_with_api_path_in_uri_trailing_slash
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ rest_client = client.rest_client
+ assert_equal('v1', client.instance_variable_get(:@api_version))
+ assert_equal('', client.instance_variable_get(:@api_group))
+ assert_equal('http://localhost:8080/api/v1', rest_client.url.to_s)
+ end
+
+ def test_with_api_path_in_api
+ client = Kubeclient::Client.new('http://localhost:8080/api/but/I/want/a/hidden/k8s/api', 'v1')
+ rest_client = client.rest_client
+ assert_equal('v1', client.instance_variable_get(:@api_version))
+ assert_equal('', client.instance_variable_get(:@api_group))
+ assert_equal('http://localhost:8080/api/but/I/want/a/hidden/k8s/api/v1', rest_client.url.to_s)
+ end
+
+ def test_with_api_group_path_in_api
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/api/but/I/want/a/hidden/k8s/apis/this_is_the_group',
+ 'v1'
+ )
+ rest_client = client.rest_client
+ assert_equal('v1', client.instance_variable_get(:@api_version))
+ assert_equal('this_is_the_group/', client.instance_variable_get(:@api_group))
+ assert_equal(
+ 'http://localhost:8080/api/but/I/want/a/hidden/k8s/apis/this_is_the_group/v1',
+ rest_client.url.to_s
+ )
+ end
+
+ def test_rancher_with_api_path_in_uri
+ client = Kubeclient::Client.new('http://localhost:8080/k8s/clusters/c-somerancherID/api', 'v1')
+ rest_client = client.rest_client
+ assert_equal('v1', client.instance_variable_get(:@api_version))
+ assert_equal('', client.instance_variable_get(:@api_group))
+ assert_equal('http://localhost:8080/k8s/clusters/c-somerancherID/api/v1', rest_client.url.to_s)
+ end
+
+ def test_rancher_no_api_path_in_uri
+ client = Kubeclient::Client.new('http://localhost:8080/k8s/clusters/c-somerancherID', 'v1')
+ rest_client = client.rest_client
+ assert_equal('v1', client.instance_variable_get(:@api_version))
+ assert_equal('', client.instance_variable_get(:@api_group))
+ assert_equal('http://localhost:8080/k8s/clusters/c-somerancherID/api/v1', rest_client.url.to_s)
+ end
+
+ def test_rancher_no_api_path_in_uri_trailing_slash
+ client = Kubeclient::Client.new('http://localhost:8080/k8s/clusters/c-somerancherID/', 'v1')
+ rest_client = client.rest_client
+ assert_equal('v1', client.instance_variable_get(:@api_version))
+ assert_equal('', client.instance_variable_get(:@api_group))
+ assert_equal('http://localhost:8080/k8s/clusters/c-somerancherID/api/v1', rest_client.url.to_s)
+ end
+
+ def test_rancher_with_api_path_in_uri_trailing_slash
+ client = Kubeclient::Client.new('http://localhost:8080/k8s/clusters/c-somerancherID/api/', 'v1')
+ rest_client = client.rest_client
+ assert_equal('v1', client.instance_variable_get(:@api_version))
+ assert_equal('', client.instance_variable_get(:@api_group))
+ assert_equal('http://localhost:8080/k8s/clusters/c-somerancherID/api/v1', rest_client.url.to_s)
+ end
+
+ def test_rancher_with_api_group_in_uri_trailing_slash
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/k8s/clusters/c-somerancherID/apis/this_is_the_group',
+ 'v1'
+ )
+ rest_client = client.rest_client
+ assert_equal('v1', client.instance_variable_get(:@api_version))
+ assert_equal('this_is_the_group/', client.instance_variable_get(:@api_group))
+ assert_equal(
+ 'http://localhost:8080/k8s/clusters/c-somerancherID/apis/this_is_the_group/v1',
+ rest_client.url.to_s
+ )
+ end
+
+ def test_with_openshift_api_path_in_uri
+ client = Kubeclient::Client.new('http://localhost:8080/oapi', 'v1')
+ rest_client = client.rest_client
+ assert_equal('v1', client.instance_variable_get(:@api_version))
+ assert_equal('', client.instance_variable_get(:@api_group))
+ assert_equal('http://localhost:8080/oapi/v1', rest_client.url.to_s)
+ end
+
+ def test_arbitrary_path_with_openshift_api_path_in_uri
+ client = Kubeclient::Client.new('http://localhost:8080/foobarbaz/oapi', 'v1')
+ rest_client = client.rest_client
+ assert_equal('v1', client.instance_variable_get(:@api_version))
+ assert_equal('', client.instance_variable_get(:@api_group))
+ assert_equal('http://localhost:8080/foobarbaz/oapi/v1', rest_client.url.to_s)
+ end
+
+ def test_with_openshift_api_path_in_uri_trailing_slash
+ client = Kubeclient::Client.new('http://localhost:8080/oapi/', 'v1')
+ rest_client = client.rest_client
+ assert_equal('v1', client.instance_variable_get(:@api_version))
+ assert_equal('', client.instance_variable_get(:@api_group))
+ assert_equal('http://localhost:8080/oapi/v1', rest_client.url.to_s)
+ end
+
+ def test_with_arbitrary_path_in_uri
+ client = Kubeclient::Client.new('http://localhost:8080/foobarbaz', 'v1')
+ rest_client = client.rest_client
+ assert_equal('v1', client.instance_variable_get(:@api_version))
+ assert_equal('', client.instance_variable_get(:@api_group))
+ assert_equal('http://localhost:8080/foobarbaz/api/v1', rest_client.url.to_s)
+ end
+
+ def test_with_arbitrary_and_api_path_in_uri
+ client = Kubeclient::Client.new('http://localhost:8080/foobarbaz/api', 'v1')
+ rest_client = client.rest_client
+ assert_equal('v1', client.instance_variable_get(:@api_version))
+ assert_equal('', client.instance_variable_get(:@api_group))
+ assert_equal('http://localhost:8080/foobarbaz/api/v1', rest_client.url.to_s)
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_component_status.rb b/vendor/gems/kubeclient/test/test_component_status.rb
new file mode 100644
index 00000000000..7bb4ca1ad51
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_component_status.rb
@@ -0,0 +1,29 @@
+require_relative 'test_helper'
+
+# ComponentStatus tests
+class TestComponentStatus < MiniTest::Test
+ def test_get_from_json_v3
+ stub_core_api_list
+ stub_request(:get, %r{/componentstatuses})
+ .to_return(body: open_test_file('component_status.json'), status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ component_status = client.get_component_status('etcd-0', 'default')
+
+ assert_instance_of(Kubeclient::Resource, component_status)
+ assert_equal('etcd-0', component_status.metadata.name)
+ assert_equal('Healthy', component_status.conditions[0].type)
+ assert_equal('True', component_status.conditions[0].status)
+
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1',
+ times: 1
+ )
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1/namespaces/default/componentstatuses/etcd-0',
+ times: 1
+ )
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_config.rb b/vendor/gems/kubeclient/test/test_config.rb
new file mode 100644
index 00000000000..d47827e0393
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_config.rb
@@ -0,0 +1,271 @@
+require_relative 'test_helper'
+require 'yaml'
+require 'open3'
+
+# Testing Kubernetes client configuration
+class KubeclientConfigTest < MiniTest::Test
+ def test_allinone
+ config = Kubeclient::Config.read(config_file('allinone.kubeconfig'))
+ assert_equal(['Default'], config.contexts)
+ check_context(config.context, ssl: true, custom_ca: true, client_cert: true)
+ end
+
+ def test_external
+ config = Kubeclient::Config.read(config_file('external.kubeconfig'))
+ assert_equal(['Default'], config.contexts)
+ check_context(config.context, ssl: true, custom_ca: true, client_cert: true)
+ end
+
+ def test_explicit_secure
+ config = Kubeclient::Config.read(config_file('secure.kubeconfig'))
+ assert_equal(['Default'], config.contexts)
+ # Same as external.kubeconfig, but with explicit `insecure-skip-tls-verify: false`
+ check_context(config.context, ssl: true, custom_ca: true, client_cert: true)
+ end
+
+ def test_external_public_ca
+ config = Kubeclient::Config.read(config_file('external-without-ca.kubeconfig'))
+ assert_equal(['Default'], config.contexts)
+ # Same as external.kubeconfig, no custom CA data (cluster has a publicly trusted cert)
+ check_context(config.context, ssl: true, custom_ca: false, client_cert: true)
+ end
+
+ def test_secure_public_ca
+ config = Kubeclient::Config.read(config_file('secure-without-ca.kubeconfig'))
+ assert_equal(['Default'], config.contexts)
+ # no custom CA data + explicit `insecure-skip-tls-verify: false`
+ check_context(config.context, ssl: true, custom_ca: false, client_cert: true)
+ end
+
+ def test_insecure
+ config = Kubeclient::Config.read(config_file('insecure.kubeconfig'))
+ assert_equal(['Default'], config.contexts)
+ # Has explicit `insecure-skip-tls-verify: false`
+ check_context(config.context, ssl: false, custom_ca: false, client_cert: true)
+ end
+
+ def test_insecure_custom_ca
+ config = Kubeclient::Config.read(config_file('insecure-custom-ca.kubeconfig'))
+ assert_equal(['Default'], config.contexts)
+ # Has explicit `insecure-skip-tls-verify: false`
+ check_context(config.context, ssl: false, custom_ca: true, client_cert: true)
+ end
+
+ def test_allinone_nopath
+ yaml = File.read(config_file('allinone.kubeconfig'))
+ # A self-contained config shouldn't depend on kcfg_path.
+ config = Kubeclient::Config.new(YAML.safe_load(yaml), nil)
+ assert_equal(['Default'], config.contexts)
+ check_context(config.context, ssl: true, custom_ca: true, client_cert: true)
+ end
+
+ def test_external_nopath
+ yaml = File.read(config_file('external.kubeconfig'))
+ # kcfg_path = nil should prevent file access
+ config = Kubeclient::Config.new(YAML.safe_load(yaml), nil)
+ assert_raises(StandardError) do
+ config.context
+ end
+ end
+
+ def test_external_nopath_absolute
+ yaml = File.read(config_file('external.kubeconfig'))
+ # kcfg_path = nil should prevent file access, even if absolute path specified
+ ca_absolute_path = File.absolute_path(config_file('external-'))
+ yaml = yaml.gsub('external-', ca_absolute_path)
+ config = Kubeclient::Config.new(YAML.safe_load(yaml), nil)
+ assert_raises(StandardError) do
+ config.context
+ end
+ end
+
+ def test_concatenated_ca
+ config = Kubeclient::Config.read(config_file('concatenated-ca.kubeconfig'))
+ assert_equal(['Default'], config.contexts)
+ check_context(config.context, ssl: true)
+ end
+
+ def test_nouser
+ config = Kubeclient::Config.read(config_file('nouser.kubeconfig'))
+ assert_equal(['default/localhost:6443/nouser'], config.contexts)
+ check_context(config.context, ssl: true, custom_ca: false, client_cert: false)
+ end
+
+ def test_user_token
+ config = Kubeclient::Config.read(config_file('userauth.kubeconfig'))
+ assert_equal(['localhost/system:admin:token', 'localhost/system:admin:userpass'],
+ config.contexts)
+ context = config.context('localhost/system:admin:token')
+ check_context(context, ssl: true, custom_ca: false, client_cert: false)
+ assert_equal('0123456789ABCDEF0123456789ABCDEF', context.auth_options[:bearer_token])
+ end
+
+ def test_user_password
+ config = Kubeclient::Config.read(config_file('userauth.kubeconfig'))
+ assert_equal(['localhost/system:admin:token', 'localhost/system:admin:userpass'],
+ config.contexts)
+ context = config.context('localhost/system:admin:userpass')
+ check_context(context, ssl: true, custom_ca: false, client_cert: false)
+ assert_equal('admin', context.auth_options[:username])
+ assert_equal('pAssw0rd123', context.auth_options[:password])
+ end
+
+ def test_timestamps
+ # Test YAML parsing doesn't crash on YAML timestamp syntax.
+ Kubeclient::Config.read(config_file('timestamps.kubeconfig'))
+ end
+
+ def test_user_exec
+ token = '0123456789ABCDEF0123456789ABCDEF'
+ creds = {
+ 'apiVersion': 'client.authentication.k8s.io/v1beta1',
+ 'status': {
+ 'token': token
+ }
+ }
+
+ config = Kubeclient::Config.read(config_file('execauth.kubeconfig'))
+ assert_equal(['localhost/system:admin:exec-search-path',
+ 'localhost/system:admin:exec-relative-path',
+ 'localhost/system:admin:exec-absolute-path'],
+ config.contexts)
+
+ # A bare command name in config means search PATH, so it's executed as bare command.
+ stub_exec(%r{^example-exec-plugin$}, creds) do
+ context = config.context('localhost/system:admin:exec-search-path')
+ check_context(context, ssl: true, custom_ca: false, client_cert: false)
+ assert_equal(token, context.auth_options[:bearer_token])
+ end
+
+ # A relative path is taken relative to the dir of the kubeconfig.
+ stub_exec(%r{.*config/dir/example-exec-plugin$}, creds) do
+ context = config.context('localhost/system:admin:exec-relative-path')
+ check_context(context, ssl: true, custom_ca: false, client_cert: false)
+ assert_equal(token, context.auth_options[:bearer_token])
+ end
+
+ # An absolute path is taken as-is.
+ stub_exec(%r{^/abs/path/example-exec-plugin$}, creds) do
+ context = config.context('localhost/system:admin:exec-absolute-path')
+ check_context(context, ssl: true, custom_ca: false, client_cert: false)
+ assert_equal(token, context.auth_options[:bearer_token])
+ end
+ end
+
+ def test_user_exec_nopath
+ yaml = File.read(config_file('execauth.kubeconfig'))
+ config = Kubeclient::Config.new(YAML.safe_load(yaml), nil)
+ config.contexts.each do |context_name|
+ Open3.stub(:capture3, proc { flunk 'should not execute command' }) do
+ assert_raises(StandardError) do
+ config.context(context_name)
+ end
+ end
+ end
+ end
+
+ def test_gcp_default_auth
+ Kubeclient::GoogleApplicationDefaultCredentials.expects(:token).returns('token1').once
+ parsed = load_yaml(config_file('gcpauth.kubeconfig'))
+ config = Kubeclient::Config.new(parsed, nil)
+ config.context(config.contexts.first)
+ end
+
+ # Each call to .context() obtains a new token, calling .auth_options doesn't change anything.
+ # NOTE: this is not a guarantee, may change, just testing current behavior.
+ def test_gcp_default_auth_renew
+ Kubeclient::GoogleApplicationDefaultCredentials.expects(:token).returns('token1').once
+ parsed = load_yaml(config_file('gcpauth.kubeconfig'))
+ config = Kubeclient::Config.new(parsed, nil)
+ context = config.context(config.contexts.first)
+ assert_equal({ bearer_token: 'token1' }, context.auth_options)
+ assert_equal({ bearer_token: 'token1' }, context.auth_options)
+
+ Kubeclient::GoogleApplicationDefaultCredentials.expects(:token).returns('token2').once
+ context2 = config.context(config.contexts.first)
+ assert_equal({ bearer_token: 'token2' }, context2.auth_options)
+ assert_equal({ bearer_token: 'token1' }, context.auth_options)
+ end
+
+ def test_gcp_command_auth
+ Kubeclient::GCPCommandCredentials.expects(:token)
+ .with('access-token' => '<fake_token>',
+ 'cmd-args' => 'config config-helper --format=json',
+ 'cmd-path' => '/path/to/gcloud',
+ 'expiry' => '2019-04-09 19:26:18 UTC',
+ 'expiry-key' => '{.credential.token_expiry}',
+ 'token-key' => '{.credential.access_token}')
+ .returns('token1')
+ .once
+ config = Kubeclient::Config.read(config_file('gcpcmdauth.kubeconfig'))
+ config.context(config.contexts.first)
+ end
+
+ def test_oidc_auth_provider
+ Kubeclient::OIDCAuthProvider.expects(:token)
+ .with('client-id' => 'fake-client-id',
+ 'client-secret' => 'fake-client-secret',
+ 'id-token' => 'fake-id-token',
+ 'idp-issuer-url' => 'https://accounts.google.com',
+ 'refresh-token' => 'fake-refresh-token')
+ .returns('token1')
+ .once
+ parsed = YAML.safe_load(File.read(config_file('oidcauth.kubeconfig')))
+ config = Kubeclient::Config.new(parsed, nil)
+ config.context(config.contexts.first)
+ end
+
+ private
+
+ def check_context(context, ssl: true, custom_ca: true, client_cert: true)
+ assert_equal('https://localhost:6443', context.api_endpoint)
+ assert_equal('v1', context.api_version)
+ assert_equal('default', context.namespace)
+ if custom_ca
+ assert_kind_of(OpenSSL::X509::Store, context.ssl_options[:cert_store])
+ else
+ assert_nil(context.ssl_options[:cert_store])
+ end
+ if client_cert
+ assert_kind_of(OpenSSL::X509::Certificate, context.ssl_options[:client_cert])
+ assert_kind_of(OpenSSL::PKey::RSA, context.ssl_options[:client_key])
+ if custom_ca
+ # When certificates expire one way to recreate them is using a k0s single-node cluster:
+ # test/config/update_certs_k0s.rb
+ assert(context.ssl_options[:cert_store].verify(context.ssl_options[:client_cert]))
+ end
+ else
+ assert_nil(context.ssl_options[:client_cert])
+ assert_nil(context.ssl_options[:client_key])
+ end
+ if ssl
+ assert_equal(OpenSSL::SSL::VERIFY_PEER, context.ssl_options[:verify_ssl],
+ 'expected VERIFY_PEER')
+ else
+ assert_equal(OpenSSL::SSL::VERIFY_NONE, context.ssl_options[:verify_ssl],
+ 'expected VERIFY_NONE')
+ end
+ end
+
+ def stub_exec(command_regexp, creds)
+ st = Minitest::Mock.new
+ st.expect(:success?, true)
+
+ capture3_stub = lambda do |_env, command, *_args|
+ assert_match command_regexp, command
+ [JSON.dump(creds), nil, st]
+ end
+
+ Open3.stub(:capture3, capture3_stub) do
+ yield
+ end
+ end
+
+ def load_yaml(file_name)
+ if RUBY_VERSION >= '2.6'
+ YAML.safe_load(File.read(file_name), permitted_classes: [Date, Time])
+ else
+ YAML.safe_load(File.read(file_name), [Date, Time])
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_endpoint.rb b/vendor/gems/kubeclient/test/test_endpoint.rb
new file mode 100644
index 00000000000..ec2cd9306d4
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_endpoint.rb
@@ -0,0 +1,54 @@
+require_relative 'test_helper'
+
+# kind: 'Endpoints' entity tests.
+# This is one of the unusual `kind`s that are already plural (https://github.com/kubernetes/kubernetes/issues/8115).
+# We force singular in method names like 'create_endpoint',
+# but `kind` should remain plural as in kubernetes.
+class TestEndpoint < MiniTest::Test
+ def test_create_endpoint
+ stub_core_api_list
+ testing_ep = Kubeclient::Resource.new
+ testing_ep.metadata = {}
+ testing_ep.metadata.name = 'myendpoint'
+ testing_ep.metadata.namespace = 'default'
+ testing_ep.subsets = [
+ {
+ 'addresses' => [{ 'ip' => '172.17.0.25' }],
+ 'ports' => [{ 'name' => 'https', 'port' => 6443, 'protocol' => 'TCP' }]
+ }
+ ]
+
+ req_body = '{"metadata":{"name":"myendpoint","namespace":"default"},' \
+ '"subsets":[{"addresses":[{"ip":"172.17.0.25"}],"ports":[{"name":"https",' \
+ '"port":6443,"protocol":"TCP"}]}],"kind":"Endpoints","apiVersion":"v1"}'
+
+ stub_request(:post, 'http://localhost:8080/api/v1/namespaces/default/endpoints')
+ .with(body: req_body)
+ .to_return(body: open_test_file('created_endpoint.json'), status: 201)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ created_ep = client.create_endpoint(testing_ep)
+ assert_equal('Endpoints', created_ep.kind)
+ assert_equal('v1', created_ep.apiVersion)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1', as: :parsed_symbolized)
+ created_ep = client.create_endpoint(testing_ep)
+ assert_equal('Endpoints', created_ep[:kind])
+ assert_equal('v1', created_ep[:apiVersion])
+ end
+
+ def test_get_endpoints
+ stub_core_api_list
+ stub_request(:get, %r{/endpoints})
+ .to_return(body: open_test_file('endpoint_list.json'), status: 200)
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+
+ collection = client.get_endpoints(as: :parsed_symbolized)
+ assert_equal('EndpointsList', collection[:kind])
+ assert_equal('v1', collection[:apiVersion])
+
+ # Stripping of 'List' in collection.kind RecursiveOpenStruct mode only is historic.
+ collection = client.get_endpoints
+ assert_equal('Endpoints', collection.kind)
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_exec_credentials.rb b/vendor/gems/kubeclient/test/test_exec_credentials.rb
new file mode 100644
index 00000000000..f0e51827f55
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_exec_credentials.rb
@@ -0,0 +1,225 @@
+require_relative 'test_helper'
+require 'open3'
+
+# Unit tests for the ExecCredentials provider
+class ExecCredentialsTest < MiniTest::Test
+ def test_exec_opts_missing
+ expected_msg =
+ 'exec options are required'
+ exception = assert_raises(ArgumentError) do
+ Kubeclient::ExecCredentials.run(nil)
+ end
+ assert_equal(expected_msg, exception.message)
+ end
+
+ def test_exec_command_missing
+ expected_msg =
+ 'exec command is required'
+ exception = assert_raises(KeyError) do
+ Kubeclient::ExecCredentials.run({})
+ end
+ assert_equal(expected_msg, exception.message)
+ end
+
+ def test_exec_command_failure
+ err = 'Error'
+ expected_msg =
+ "exec command failed: #{err}"
+
+ st = Minitest::Mock.new
+ st.expect(:success?, false)
+
+ opts = { 'command' => 'dummy' }
+
+ Open3.stub(:capture3, [nil, err, st]) do
+ exception = assert_raises(RuntimeError) do
+ Kubeclient::ExecCredentials.run(opts)
+ end
+ assert_equal(expected_msg, exception.message)
+ end
+ end
+
+ def test_run_with_token_credentials
+ opts = { 'command' => 'dummy' }
+
+ credentials = {
+ 'token' => '0123456789ABCDEF0123456789ABCDEF'
+ }
+
+ creds = JSON.dump(
+ 'apiVersion' => 'client.authentication.k8s.io/v1alpha1',
+ 'status' => credentials
+ )
+
+ st = Minitest::Mock.new
+ st.expect(:success?, true)
+
+ Open3.stub(:capture3, [creds, nil, st]) do
+ assert_equal(credentials, Kubeclient::ExecCredentials.run(opts))
+ end
+ end
+
+ def test_run_with_client_credentials
+ opts = { 'command' => 'dummy' }
+
+ credentials = {
+ 'clientCertificateData' => '0123456789ABCDEF0123456789ABCDEF',
+ 'clientKeyData' => '0123456789ABCDEF0123456789ABCDEF'
+ }
+
+ creds = JSON.dump(
+ 'apiVersion' => 'client.authentication.k8s.io/v1alpha1',
+ 'status' => credentials
+ )
+
+ st = Minitest::Mock.new
+ st.expect(:success?, true)
+
+ Open3.stub(:capture3, [creds, nil, st]) do
+ assert_equal(credentials, Kubeclient::ExecCredentials.run(opts))
+ end
+ end
+
+ def test_run_with_missing_client_certificate_data
+ opts = { 'command' => 'dummy' }
+
+ credentials = {
+ 'clientKeyData' => '0123456789ABCDEF0123456789ABCDEF'
+ }
+
+ creds = JSON.dump(
+ 'apiVersion' => 'client.authentication.k8s.io/v1alpha1',
+ 'status' => credentials
+ )
+
+ st = Minitest::Mock.new
+ st.expect(:success?, true)
+
+ expected_msg = 'exec plugin didn\'t return client certificate data'
+
+ Open3.stub(:capture3, [creds, nil, st]) do
+ exception = assert_raises(RuntimeError) do
+ Kubeclient::ExecCredentials.run(opts)
+ end
+ assert_equal(expected_msg, exception.message)
+ end
+ end
+
+ def test_run_with_missing_client_key_data
+ opts = { 'command' => 'dummy' }
+
+ credentials = {
+ 'clientCertificateData' => '0123456789ABCDEF0123456789ABCDEF'
+ }
+
+ creds = JSON.dump(
+ 'apiVersion' => 'client.authentication.k8s.io/v1alpha1',
+ 'status' => credentials
+ )
+
+ st = Minitest::Mock.new
+ st.expect(:success?, true)
+
+ expected_msg = 'exec plugin didn\'t return client key data'
+
+ Open3.stub(:capture3, [creds, nil, st]) do
+ exception = assert_raises(RuntimeError) do
+ Kubeclient::ExecCredentials.run(opts)
+ end
+ assert_equal(expected_msg, exception.message)
+ end
+ end
+
+ def test_run_with_client_data_and_token
+ opts = { 'command' => 'dummy' }
+
+ credentials = {
+ 'clientCertificateData' => '0123456789ABCDEF0123456789ABCDEF',
+ 'clientKeyData' => '0123456789ABCDEF0123456789ABCDEF',
+ 'token' => '0123456789ABCDEF0123456789ABCDEF'
+ }
+
+ creds = JSON.dump(
+ 'apiVersion' => 'client.authentication.k8s.io/v1alpha1',
+ 'status' => credentials
+ )
+
+ st = Minitest::Mock.new
+ st.expect(:success?, true)
+
+ expected_msg = 'exec plugin returned both token and client data'
+
+ Open3.stub(:capture3, [creds, nil, st]) do
+ exception = assert_raises(RuntimeError) do
+ Kubeclient::ExecCredentials.run(opts)
+ end
+ assert_equal(expected_msg, exception.message)
+ end
+ end
+
+ def test_status_missing
+ opts = { 'command' => 'dummy' }
+
+ creds = JSON.dump('apiVersion' => 'client.authentication.k8s.io/v1alpha1')
+
+ st = Minitest::Mock.new
+ st.expect(:success?, true)
+
+ expected_msg = 'exec plugin didn\'t return a status field'
+
+ Open3.stub(:capture3, [creds, nil, st]) do
+ exception = assert_raises(RuntimeError) do
+ Kubeclient::ExecCredentials.run(opts)
+ end
+ assert_equal(expected_msg, exception.message)
+ end
+ end
+
+ def test_credentials_missing
+ opts = { 'command' => 'dummy' }
+
+ creds = JSON.dump(
+ 'apiVersion' => 'client.authentication.k8s.io/v1alpha1',
+ 'status' => {}
+ )
+
+ st = Minitest::Mock.new
+ st.expect(:success?, true)
+
+ expected_msg = 'exec plugin didn\'t return a token or client data'
+
+ Open3.stub(:capture3, [creds, nil, st]) do
+ exception = assert_raises(RuntimeError) do
+ Kubeclient::ExecCredentials.run(opts)
+ end
+ assert_equal(expected_msg, exception.message)
+ end
+ end
+
+ def test_api_version_mismatch
+ api_version = 'client.authentication.k8s.io/v1alpha1'
+ expected_version = 'client.authentication.k8s.io/v1beta1'
+
+ opts = {
+ 'command' => 'dummy',
+ 'apiVersion' => expected_version
+ }
+
+ creds = JSON.dump(
+ 'apiVersion' => api_version
+ )
+
+ st = Minitest::Mock.new
+ st.expect(:success?, true)
+
+ expected_msg = "exec plugin is configured to use API version #{expected_version}," \
+ " plugin returned version #{api_version}"
+
+ Open3.stub(:capture3, [creds, nil, st]) do
+ exception = assert_raises(RuntimeError) do
+ Kubeclient::ExecCredentials.run(opts)
+ end
+ assert_equal(expected_msg, exception.message)
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_gcp_command_credentials.rb b/vendor/gems/kubeclient/test/test_gcp_command_credentials.rb
new file mode 100644
index 00000000000..f95b8fd045e
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_gcp_command_credentials.rb
@@ -0,0 +1,27 @@
+require_relative 'test_helper'
+require 'open3'
+
+# Unit tests for the GCPCommandCredentials token provider
+class GCPCommandCredentialsTest < MiniTest::Test
+ def test_token
+ opts = { 'cmd-args' => 'config config-helper --format=json',
+ 'cmd-path' => '/path/to/gcloud',
+ 'expiry-key' => '{.credential.token_expiry}',
+ 'token-key' => '{.credential.access_token}' }
+
+ creds = JSON.dump(
+ 'credential' => {
+ 'access_token' => '9A3A941836F2458175BE18AA1971EBBF47949B07',
+ 'token_expiry' => '2019-04-12T15:02:51Z'
+ }
+ )
+
+ st = Minitest::Mock.new
+ st.expect(:success?, true)
+
+ Open3.stub(:capture3, [creds, nil, st]) do
+ assert_equal('9A3A941836F2458175BE18AA1971EBBF47949B07',
+ Kubeclient::GCPCommandCredentials.token(opts))
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_google_application_default_credentials.rb b/vendor/gems/kubeclient/test/test_google_application_default_credentials.rb
new file mode 100644
index 00000000000..238ae729c16
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_google_application_default_credentials.rb
@@ -0,0 +1,15 @@
+require_relative 'test_helper'
+require 'googleauth'
+
+# Unit tests for the ApplicationDefaultCredentials token provider
+class GoogleApplicationDefaultCredentialsTest < MiniTest::Test
+ def test_token
+ auth = Minitest::Mock.new
+ auth.expect(:apply, nil, [{}])
+ auth.expect(:access_token, 'valid_token')
+
+ Google::Auth.stub(:get_application_default, auth) do
+ assert_equal('valid_token', Kubeclient::GoogleApplicationDefaultCredentials.token)
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_guestbook_go.rb b/vendor/gems/kubeclient/test/test_guestbook_go.rb
new file mode 100644
index 00000000000..a50e192a0c5
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_guestbook_go.rb
@@ -0,0 +1,237 @@
+require_relative 'test_helper'
+require 'vcr'
+
+# creation of google's example of guest book
+class CreateGuestbookGo < MiniTest::Test
+ def test_create_guestbook_entities
+ VCR.configure do |c|
+ c.cassette_library_dir = 'test/cassettes'
+ c.hook_into(:webmock)
+ end
+
+ # WebMock.allow_net_connect!
+ VCR.use_cassette('kubernetes_guestbook') do # , record: :new_episodes) do
+ client = Kubeclient::Client.new('http://10.35.0.23:8080/api/', 'v1')
+
+ testing_ns = Kubeclient::Resource.new
+ testing_ns.metadata = {}
+ testing_ns.metadata.name = 'kubeclient-ns'
+
+ # delete in case they existed before so creation can be tested
+ delete_namespace(client, testing_ns.metadata.name)
+ delete_services(
+ client, testing_ns.metadata.name,
+ ['guestbook', 'redis-master', 'redis-slave']
+ )
+ delete_replication_controllers(
+ client, testing_ns.metadata.name,
+ ['guestbook', 'redis-master', 'redis-slave']
+ )
+
+ client.create_namespace(testing_ns)
+ services = create_services(client, testing_ns.metadata.name)
+ replicators = create_replication_controllers(client, testing_ns.metadata.name)
+
+ get_namespaces(client)
+ get_services(client, testing_ns.metadata.name)
+ get_replication_controllers(client, testing_ns.metadata.name)
+
+ delete_services(client, testing_ns.metadata.name, services)
+ delete_replication_controllers(client, testing_ns.metadata.name, replicators)
+
+ client.delete_namespace(testing_ns.metadata.name)
+ end
+ ensure
+ VCR.turn_off!
+ end
+
+ def delete_namespace(client, namespace_name)
+ client.delete_namespace(namespace_name)
+ rescue Kubeclient::ResourceNotFoundError => exception
+ assert_equal(404, exception.error_code)
+ end
+
+ def get_namespaces(client)
+ namespaces = client.get_namespaces
+ assert(true, namespaces.size > 2)
+ end
+
+ def get_services(client, ns)
+ retrieved_services = client.get_services(namespace: ns)
+ assert_equal(3, retrieved_services.size)
+ end
+
+ def get_replication_controllers(client, ns)
+ retrieved_replicators = client.get_replication_controllers(namespace: ns)
+ assert_equal(3, retrieved_replicators.size)
+ end
+
+ def create_services(client, ns)
+ guestbook_service = client.create_service(guestbook_service(ns))
+ redis_service = client.create_service(redis_service(ns))
+ redis_slave_service = client.create_service(redis_slave_service(ns))
+ [guestbook_service, redis_service, redis_slave_service]
+ end
+
+ def create_replication_controllers(client, namespace)
+ rc = client.create_replication_controller(guestbook_rc(namespace))
+ rc2 = client.create_replication_controller(redis_master_rc(namespace))
+ rc3 = client.create_replication_controller(redis_slave_rc(namespace))
+ [rc, rc2, rc3]
+ end
+
+ def delete_services(client, namespace, services)
+ # if the entity is not found, no need to fail the test
+ services.each do |service|
+ begin
+ if service.instance_of?(Kubeclient::Resource)
+ client.delete_service(service.metadata.name, namespace)
+ else
+ # it's just a string - service name
+ client.delete_service(service, namespace)
+ end
+ rescue Kubeclient::ResourceNotFoundError => exception
+ assert_equal(404, exception.error_code)
+ end
+ end
+ end
+
+ def delete_replication_controllers(client, namespace, replication_controllers)
+ # if the entity is not found, no need to fail the test
+ replication_controllers.each do |rc|
+ begin
+ if rc.instance_of?(Kubeclient::Resource)
+ client.delete_replication_controller(rc.metadata.name, namespace)
+ else
+ # it's just a string - rc name
+ client.delete_replication_controller(rc, namespace)
+ end
+ rescue Kubeclient::ResourceNotFoundError => exception
+ assert_equal(404, exception.error_code)
+ end
+ end
+ end
+
+ private
+
+ def construct_base_rc(namespace)
+ rc = Kubeclient::Resource.new
+ rc.metadata = {}
+ rc.metadata.namespace = namespace
+ rc.metadata.labels = {}
+ rc.spec = {}
+ rc.spec.selector = {}
+ rc.spec.template = {}
+ rc.spec.template.metadata = {}
+ rc.spec.template.spec = {}
+ rc.spec.template.metadata.labels = {}
+ rc
+ end
+
+ def redis_master_rc(namespace)
+ rc = construct_base_rc(namespace)
+ rc.metadata.name = 'redis-master'
+ rc.metadata.labels.app = 'redis'
+ rc.metadata.labels.role = 'master'
+ rc.spec.replicas = 1
+ rc.spec.selector.app = 'redis'
+ rc.spec.selector.role = 'master'
+ rc.spec.template.metadata.labels.app = 'redis'
+ rc.spec.template.metadata.labels.role = 'master'
+ rc.spec.template.spec.containers = [{
+ 'name' => 'redis-master',
+ 'image' => 'redis',
+ 'ports' => [{
+ 'name' => 'redis-server',
+ 'containerPort' => 6379
+ }]
+ }]
+ rc
+ end
+
+ def redis_slave_rc(namespace)
+ rc = construct_base_rc(namespace)
+ rc.metadata.name = 'redis-slave'
+ rc.metadata.labels.app = 'redis'
+ rc.metadata.labels.role = 'slave'
+ rc.spec.replicas = 2
+ rc.spec.selector.app = 'redis'
+ rc.spec.selector.role = 'slave'
+ rc.spec.template.metadata.labels.app = 'redis'
+ rc.spec.template.metadata.labels.role = 'slave'
+ rc.spec.template.spec.containers = [{
+ 'name' => 'redis-slave',
+ 'image' => 'kubernetes/redis-slave:v2',
+ 'ports' => [{
+ 'name' => 'redis-server',
+ 'containerPort' => 6379
+ }]
+ }]
+ rc
+ end
+
+ def guestbook_rc(namespace)
+ rc = construct_base_rc(namespace)
+ rc.metadata.name = 'guestbook'
+ rc.metadata.labels.app = 'guestbook'
+ rc.metadata.labels.role = 'slave'
+ rc.spec.replicas = 3
+ rc.spec.selector.app = 'guestbook'
+ rc.spec.template.metadata.labels.app = 'guestbook'
+ rc.spec.template.spec.containers = [
+ {
+ 'name' => 'guestbook',
+ 'image' => 'kubernetes/guestbook:v2',
+ 'ports' => [
+ {
+ 'name' => 'http-server',
+ 'containerPort' => 3000
+ }
+ ]
+ }
+ ]
+ rc
+ end
+
+ def base_service(namespace)
+ our_service = Kubeclient::Resource.new
+ our_service.metadata = {}
+ our_service.metadata.namespace = namespace
+ our_service.metadata.labels = {}
+ our_service.spec = {}
+ our_service.spec.selector = {}
+ our_service
+ end
+
+ def redis_slave_service(namespace)
+ our_service = base_service(namespace)
+ our_service.metadata.name = 'redis-slave'
+ our_service.metadata.labels.app = 'redis'
+ our_service.metadata.labels.role = 'slave'
+ our_service.spec.ports = [{ 'port' => 6379, 'targetPort' => 'redis-server' }]
+ our_service.spec.selector.app = 'redis'
+ our_service.spec.selector.role = 'slave'
+ our_service
+ end
+
+ def redis_service(namespace)
+ our_service = base_service(namespace)
+ our_service.metadata.name = 'redis-master'
+ our_service.metadata.labels.app = 'redis'
+ our_service.metadata.labels.role = 'master'
+ our_service.spec.ports = [{ 'port' => 6379, 'targetPort' => 'redis-server' }]
+ our_service.spec.selector.app = 'redis'
+ our_service.spec.selector.role = 'master'
+ our_service
+ end
+
+ def guestbook_service(namespace)
+ our_service = base_service(namespace)
+ our_service.metadata.name = 'guestbook'
+ our_service.metadata.labels.name = 'guestbook'
+ our_service.spec.ports = [{ 'port' => 3000, 'targetPort' => 'http-server' }]
+ our_service.spec.selector.app = 'guestbook'
+ our_service.type = 'LoadBalancer'
+ our_service
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_helper.rb b/vendor/gems/kubeclient/test/test_helper.rb
new file mode 100644
index 00000000000..042a08a0d80
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_helper.rb
@@ -0,0 +1,28 @@
+require 'bundler/setup'
+require 'minitest/autorun'
+require 'minitest/rg'
+require 'webmock/minitest'
+require 'mocha/minitest'
+require 'json'
+require 'kubeclient'
+
+MiniTest::Test.class_eval do
+ # Assumes test files will be in a subdirectory with the same name as the
+ # file suffix. e.g. a file named foo.json would be a "json" subdirectory.
+ def open_test_file(name)
+ File.new(File.join(File.dirname(__FILE__), name.split('.').last, name))
+ end
+
+ # kubeconfig files deviate from above convention.
+ # They link to relaved certs etc. with various extensions, all in same dir.
+ def config_file(name)
+ File.join(File.dirname(__FILE__), 'config', name)
+ end
+
+ def stub_core_api_list
+ stub_request(:get, %r{/api/v1$})
+ .to_return(body: open_test_file('core_api_resource_list.json'), status: 200)
+ end
+end
+
+WebMock.disable_net_connect!
diff --git a/vendor/gems/kubeclient/test/test_kubeclient.rb b/vendor/gems/kubeclient/test/test_kubeclient.rb
new file mode 100644
index 00000000000..f866bfc89df
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_kubeclient.rb
@@ -0,0 +1,881 @@
+require_relative 'test_helper'
+
+# Kubernetes client entity tests
+class KubeclientTest < MiniTest::Test
+ def test_json
+ our_object = Kubeclient::Resource.new
+ our_object.foo = 'bar'
+ our_object.nested = {}
+ our_object.nested.again = {}
+ our_object.nested.again.again = {}
+ our_object.nested.again.again.name = 'aaron'
+
+ expected = {
+ 'foo' => 'bar',
+ 'nested' => { 'again' => { 'again' => { 'name' => 'aaron' } } }
+ }
+
+ assert_equal(expected, JSON.parse(JSON.dump(our_object.to_h)))
+ end
+
+ def test_pass_uri
+ # URI::Generic#hostname= was added in ruby 1.9.3 and will automatically
+ # wrap an ipv6 address in []
+ uri = URI::HTTP.build(port: 8080)
+ uri.hostname = 'localhost'
+ client = Kubeclient::Client.new(uri)
+ rest_client = client.rest_client
+ assert_equal('http://localhost:8080/api/v1', rest_client.url.to_s)
+ end
+
+ def test_no_path_in_uri
+ client = Kubeclient::Client.new('http://localhost:8080', 'v1')
+ rest_client = client.rest_client
+ assert_equal('http://localhost:8080/api/v1', rest_client.url.to_s)
+ end
+
+ def test_no_version_passed
+ client = Kubeclient::Client.new('http://localhost:8080')
+ rest_client = client.rest_client
+ assert_equal('http://localhost:8080/api/v1', rest_client.url.to_s)
+ end
+
+ def test_pass_proxy
+ uri = URI::HTTP.build(host: 'localhost', port: 8080)
+ proxy_uri = URI::HTTP.build(host: 'myproxyhost', port: 8888)
+ stub_core_api_list
+
+ client = Kubeclient::Client.new(uri, http_proxy_uri: proxy_uri)
+ rest_client = client.rest_client
+ assert_equal(proxy_uri.to_s, rest_client.options[:proxy])
+
+ watch_client = client.watch_pods
+ assert_equal(watch_client.send(:build_client_options)[:proxy][:proxy_address], proxy_uri.host)
+ assert_equal(watch_client.send(:build_client_options)[:proxy][:proxy_port], proxy_uri.port)
+ end
+
+ def test_pass_max_redirects
+ max_redirects = 0
+ client = Kubeclient::Client.new('http://localhost:8080/api/', http_max_redirects: max_redirects)
+ rest_client = client.rest_client
+ assert_equal(max_redirects, rest_client.options[:max_redirects])
+
+ stub_request(:get, 'http://localhost:8080/api')
+ .to_return(status: 302, headers: { location: 'http://localhost:1234/api' })
+
+ exception = assert_raises(Kubeclient::HttpError) { client.api }
+ assert_equal(302, exception.error_code)
+ end
+
+ def test_exception
+ stub_core_api_list
+ stub_request(:post, %r{/services})
+ .to_return(body: open_test_file('namespace_exception.json'), status: 409)
+
+ service = Kubeclient::Resource.new
+ service.metadata = {}
+ service.metadata.name = 'redisslave'
+ service.metadata.namespace = 'default'
+ # service.port = 80
+ # service.container_port = 6379
+ # service.protocol = 'TCP'
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
+
+ exception = assert_raises(Kubeclient::HttpError) do
+ service = client.create_service(service)
+ end
+
+ assert_instance_of(Kubeclient::HttpError, exception)
+ assert_equal("converting to : type names don't match (Pod, Namespace)",
+ exception.message)
+
+ assert_includes(exception.to_s, ' for POST http://localhost:8080/api')
+ assert_equal(409, exception.error_code)
+ end
+
+ def test_deprecated_exception
+ error_message = 'certificate verify failed'
+
+ stub_request(:get, 'http://localhost:8080/api')
+ .to_raise(OpenSSL::SSL::SSLError.new(error_message))
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
+
+ exception = assert_raises(KubeException) { client.api }
+ assert_equal(error_message, exception.message)
+ end
+
+ def test_api
+ stub_request(:get, 'http://localhost:8080/api')
+ .to_return(status: 200, body: open_test_file('versions_list.json'))
+
+ response = client.api
+ assert_includes(response, 'versions')
+ end
+
+ def test_api_ssl_failure
+ error_message = 'certificate verify failed'
+
+ stub_request(:get, 'http://localhost:8080/api')
+ .to_raise(OpenSSL::SSL::SSLError.new(error_message))
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
+
+ exception = assert_raises(Kubeclient::HttpError) { client.api }
+ assert_equal(error_message, exception.message)
+ end
+
+ def test_api_timeout
+ stub_request(:get, 'http://localhost:8080/api').to_timeout
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
+
+ exception = assert_raises(Kubeclient::HttpError) { client.api }
+ assert_match(/(timed out|timeout)/i, exception.message)
+ end
+
+ def test_api_valid
+ stub_request(:get, 'http://localhost:8080/api')
+ .to_return(status: 200, body: open_test_file('versions_list.json'))
+
+ args = ['http://localhost:8080/api/']
+
+ [nil, 'v1beta3', 'v1'].each do |version|
+ client = Kubeclient::Client.new(*(version ? args + [version] : args))
+ assert client.api_valid?
+ end
+ end
+
+ def test_api_valid_with_invalid_version
+ stub_request(:get, 'http://localhost:8080/api')
+ .to_return(status: 200, body: open_test_file('versions_list.json'))
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'foobar1')
+ refute client.api_valid?
+ end
+
+ def test_api_valid_with_unreported_versions
+ stub_request(:get, 'http://localhost:8080/api')
+ .to_return(status: 200, body: '{}')
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
+ refute client.api_valid?
+ end
+
+ def test_api_valid_with_invalid_json
+ stub_request(:get, 'http://localhost:8080/api')
+ .to_return(status: 200, body: '[]')
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
+ refute client.api_valid?
+ end
+
+ def test_api_valid_with_bad_endpoint
+ stub_request(:get, 'http://localhost:8080/api')
+ .to_return(status: [404, 'Resource Not Found'])
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
+ assert_raises(Kubeclient::HttpError) { client.api_valid? }
+ end
+
+ def test_api_valid_with_non_json
+ stub_request(:get, 'http://localhost:8080/api')
+ .to_return(status: 200, body: '<html></html>')
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
+ assert_raises(JSON::ParserError) { client.api_valid? }
+ end
+
+ def test_nonjson_exception
+ stub_core_api_list
+ stub_request(:get, %r{/servic})
+ .to_return(body: open_test_file('service_illegal_json_404.json'), status: 404)
+
+ exception = assert_raises(Kubeclient::ResourceNotFoundError) do
+ client.get_services
+ end
+
+ assert(exception.message.include?('Not Found'))
+ assert_equal(404, exception.error_code)
+ end
+
+ def test_nonjson_exception_raw
+ stub_core_api_list
+ stub_request(:get, %r{/servic})
+ .to_return(body: open_test_file('service_illegal_json_404.json'), status: 404)
+
+ exception = assert_raises(Kubeclient::ResourceNotFoundError) do
+ client.get_services(as: :raw)
+ end
+
+ assert(exception.message.include?('Not Found'))
+ assert_equal(404, exception.error_code)
+ end
+
+ def test_entity_list
+ stub_core_api_list
+ stub_get_services
+
+ services = client.get_services
+
+ refute_empty(services)
+ assert_instance_of(Kubeclient::Common::EntityList, services)
+ # Stripping of 'List' in collection.kind RecursiveOpenStruct mode only is historic.
+ assert_equal('Service', services.kind)
+ assert_equal(2, services.size)
+ assert_instance_of(Kubeclient::Resource, services[0])
+ assert_instance_of(Kubeclient::Resource, services[1])
+
+ assert_requested(:get, 'http://localhost:8080/api/v1/services', times: 1)
+ end
+
+ def test_entity_list_raw
+ stub_core_api_list
+ stub_get_services
+
+ response = client.get_services(as: :raw)
+
+ refute_empty(response)
+ assert_equal(open_test_file('entity_list.json').read, response)
+
+ assert_requested(:get, 'http://localhost:8080/api/v1/services', times: 1)
+ end
+
+ def test_entity_list_parsed
+ stub_core_api_list
+ stub_get_services
+
+ response = client.get_services(as: :parsed)
+ assert_equal Hash, response.class
+ assert_equal 'ServiceList', response['kind']
+ assert_equal %w[metadata spec status], response['items'].first.keys
+ end
+
+ def test_entity_list_parsed_symbolized
+ stub_core_api_list
+ stub_get_services
+
+ response = client.get_services(as: :parsed_symbolized)
+ assert_equal Hash, response.class
+ assert_equal 'ServiceList', response[:kind]
+ assert_equal %i[metadata spec status], response[:items].first.keys
+ end
+
+ def test_entity_list_unknown
+ stub_core_api_list
+ stub_get_services
+
+ e = assert_raises(ArgumentError) { client.get_services(as: :whoops) }
+ assert_equal 'Unsupported format :whoops', e.message
+ end
+
+ def test_entity_list_raw_failure
+ stub_core_api_list
+ stub_request(:get, %r{/services})
+ .to_return(body: open_test_file('entity_list.json'), status: 500)
+
+ exception = assert_raises(Kubeclient::HttpError) { client.get_services(as: :raw) }
+ assert_equal('500 Internal Server Error', exception.message)
+ assert_equal(500, exception.error_code)
+ end
+
+ def test_entities_with_label_selector
+ selector = 'component=apiserver'
+
+ stub_core_api_list
+ stub_get_services
+
+ services = client.get_services(label_selector: selector)
+
+ assert_instance_of(Kubeclient::Common::EntityList, services)
+ assert_requested(
+ :get,
+ "http://localhost:8080/api/v1/services?labelSelector=#{selector}",
+ times: 1
+ )
+ end
+
+ def test_entities_with_resource_version
+ version = '329'
+
+ stub_core_api_list
+ stub_get_services
+
+ services = client.get_services(resource_version: version)
+
+ assert_instance_of(Kubeclient::Common::EntityList, services)
+ assert_requested(
+ :get,
+ "http://localhost:8080/api/v1/services?resourceVersion=#{version}",
+ times: 1
+ )
+ end
+
+ def test_entities_with_field_selector
+ selector = 'involvedObject.name=redis-master'
+
+ stub_core_api_list
+ stub_get_services
+
+ services = client.get_services(field_selector: selector)
+
+ assert_instance_of(Kubeclient::Common::EntityList, services)
+ assert_requested(
+ :get,
+ "http://localhost:8080/api/v1/services?fieldSelector=#{selector}",
+ times: 1
+ )
+ end
+
+ def test_empty_list
+ stub_core_api_list
+ stub_request(:get, %r{/pods})
+ .to_return(body: open_test_file('empty_pod_list.json'), status: 200)
+
+ pods = client.get_pods
+ assert_instance_of(Kubeclient::Common::EntityList, pods)
+ assert_equal(0, pods.size)
+ end
+
+ def test_get_all
+ stub_core_api_list
+
+ stub_request(:get, %r{/bindings})
+ .to_return(body: open_test_file('bindings_list.json'), status: 404)
+
+ stub_request(:get, %r{/configmaps})
+ .to_return(body: open_test_file('config_map_list.json'), status: 200)
+
+ stub_request(:get, %r{/podtemplates})
+ .to_return(body: open_test_file('pod_template_list.json'), status: 200)
+
+ stub_request(:get, %r{/services})
+ .to_return(body: open_test_file('service_list.json'), status: 200)
+
+ stub_request(:get, %r{/pods})
+ .to_return(body: open_test_file('pod_list.json'), status: 200)
+
+ stub_request(:get, %r{/nodes})
+ .to_return(body: open_test_file('node_list.json'), status: 200)
+
+ stub_request(:get, %r{/replicationcontrollers})
+ .to_return(body: open_test_file('replication_controller_list.json'), status: 200)
+
+ stub_request(:get, %r{/events})
+ .to_return(body: open_test_file('event_list.json'), status: 200)
+
+ stub_request(:get, %r{/endpoints})
+ .to_return(body: open_test_file('endpoint_list.json'), status: 200)
+
+ stub_request(:get, %r{/namespaces})
+ .to_return(body: open_test_file('namespace_list.json'), status: 200)
+
+ stub_request(:get, %r{/secrets})
+ .to_return(body: open_test_file('secret_list.json'), status: 200)
+
+ stub_request(:get, %r{/resourcequotas})
+ .to_return(body: open_test_file('resource_quota_list.json'), status: 200)
+
+ stub_request(:get, %r{/limitranges})
+ .to_return(body: open_test_file('limit_range_list.json'), status: 200)
+
+ stub_request(:get, %r{/persistentvolumes})
+ .to_return(body: open_test_file('persistent_volume_list.json'), status: 200)
+
+ stub_request(:get, %r{/persistentvolumeclaims})
+ .to_return(body: open_test_file('persistent_volume_claim_list.json'), status: 200)
+
+ stub_request(:get, %r{/componentstatuses})
+ .to_return(body: open_test_file('component_status_list.json'), status: 200)
+
+ stub_request(:get, %r{/serviceaccounts})
+ .to_return(body: open_test_file('service_account_list.json'), status: 200)
+
+ result = client.all_entities
+ assert_equal(16, result.keys.size)
+ assert_instance_of(Kubeclient::Common::EntityList, result['node'])
+ assert_instance_of(Kubeclient::Common::EntityList, result['service'])
+ assert_instance_of(Kubeclient::Common::EntityList, result['replication_controller'])
+ assert_instance_of(Kubeclient::Common::EntityList, result['pod'])
+ assert_instance_of(Kubeclient::Common::EntityList, result['event'])
+ assert_instance_of(Kubeclient::Common::EntityList, result['namespace'])
+ assert_instance_of(Kubeclient::Common::EntityList, result['secret'])
+ assert_instance_of(Kubeclient::Resource, result['service'][0])
+ assert_instance_of(Kubeclient::Resource, result['node'][0])
+ assert_instance_of(Kubeclient::Resource, result['event'][0])
+ assert_instance_of(Kubeclient::Resource, result['endpoint'][0])
+ assert_instance_of(Kubeclient::Resource, result['namespace'][0])
+ assert_instance_of(Kubeclient::Resource, result['secret'][0])
+ assert_instance_of(Kubeclient::Resource, result['resource_quota'][0])
+ assert_instance_of(Kubeclient::Resource, result['limit_range'][0])
+ assert_instance_of(Kubeclient::Resource, result['persistent_volume'][0])
+ assert_instance_of(Kubeclient::Resource, result['persistent_volume_claim'][0])
+ assert_instance_of(Kubeclient::Resource, result['component_status'][0])
+ assert_instance_of(Kubeclient::Resource, result['service_account'][0])
+ end
+
+ def test_get_all_raw
+ stub_core_api_list
+
+ stub_request(:get, %r{/bindings})
+ .to_return(body: open_test_file('bindings_list.json'), status: 404)
+
+ stub_request(:get, %r{/configmaps})
+ .to_return(body: open_test_file('config_map_list.json'), status: 200)
+
+ stub_request(:get, %r{/podtemplates})
+ .to_return(body: open_test_file('pod_template_list.json'), status: 200)
+
+ stub_request(:get, %r{/services})
+ .to_return(body: open_test_file('service_list.json'), status: 200)
+
+ stub_request(:get, %r{/pods})
+ .to_return(body: open_test_file('pod_list.json'), status: 200)
+
+ stub_request(:get, %r{/nodes})
+ .to_return(body: open_test_file('node_list.json'), status: 200)
+
+ stub_request(:get, %r{/replicationcontrollers})
+ .to_return(body: open_test_file('replication_controller_list.json'), status: 200)
+
+ stub_request(:get, %r{/events})
+ .to_return(body: open_test_file('event_list.json'), status: 200)
+
+ stub_request(:get, %r{/endpoints})
+ .to_return(body: open_test_file('endpoint_list.json'), status: 200)
+
+ stub_request(:get, %r{/namespaces})
+ .to_return(body: open_test_file('namespace_list.json'), status: 200)
+
+ stub_request(:get, %r{/secrets})
+ .to_return(body: open_test_file('secret_list.json'), status: 200)
+
+ stub_request(:get, %r{/resourcequotas})
+ .to_return(body: open_test_file('resource_quota_list.json'), status: 200)
+
+ stub_request(:get, %r{/limitranges})
+ .to_return(body: open_test_file('limit_range_list.json'), status: 200)
+
+ stub_request(:get, %r{/persistentvolumes})
+ .to_return(body: open_test_file('persistent_volume_list.json'), status: 200)
+
+ stub_request(:get, %r{/persistentvolumeclaims})
+ .to_return(body: open_test_file('persistent_volume_claim_list.json'), status: 200)
+
+ stub_request(:get, %r{/componentstatuses})
+ .to_return(body: open_test_file('component_status_list.json'), status: 200)
+
+ stub_request(:get, %r{/serviceaccounts})
+ .to_return(body: open_test_file('service_account_list.json'), status: 200)
+
+ result = client.all_entities(as: :raw)
+ assert_equal(16, result.keys.size)
+
+ %w[
+ component_status config_map endpoint event limit_range namespace node
+ persistent_volume persistent_volume_claim pod replication_controller
+ resource_quota secret service service_account
+ ].each do |entity|
+ assert_equal(open_test_file("#{entity}_list.json").read, result[entity])
+ end
+ end
+
+ def test_api_bearer_token_with_params_success
+ stub_request(:get, 'http://localhost:8080/api/v1/pods?labelSelector=name=redis-master')
+ .with(headers: { Authorization: 'Bearer valid_token' })
+ .to_return(body: open_test_file('pod_list.json'), status: 200)
+ stub_request(:get, %r{/api/v1$})
+ .with(headers: { Authorization: 'Bearer valid_token' })
+ .to_return(body: open_test_file('core_api_resource_list.json'), status: 200)
+
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/api/',
+ auth_options: { bearer_token: 'valid_token' }
+ )
+
+ pods = client.get_pods(label_selector: 'name=redis-master')
+
+ assert_equal('Pod', pods.kind)
+ assert_equal(1, pods.size)
+ end
+
+ def test_api_bearer_token_success
+ stub_core_api_list
+ stub_request(:get, 'http://localhost:8080/api/v1/pods')
+ .with(headers: { Authorization: 'Bearer valid_token' })
+ .to_return(
+ body: open_test_file('pod_list.json'), status: 200
+ )
+
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/api/',
+ auth_options: { bearer_token: 'valid_token' }
+ )
+
+ pods = client.get_pods
+
+ assert_equal('Pod', pods.kind)
+ assert_equal(1, pods.size)
+ end
+
+ def test_api_bearer_token_failure
+ error_message =
+ '"/api/v1" is forbidden because ' \
+ 'system:anonymous cannot list on pods in'
+ response = OpenStruct.new(code: 401, message: error_message)
+
+ stub_request(:get, 'http://localhost:8080/api/v1')
+ .with(headers: { Authorization: 'Bearer invalid_token' })
+ .to_raise(Kubeclient::HttpError.new(403, error_message, response))
+
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/api/',
+ auth_options: { bearer_token: 'invalid_token' }
+ )
+
+ exception = assert_raises(Kubeclient::HttpError) { client.get_pods }
+ assert_equal(403, exception.error_code)
+ assert_equal(error_message, exception.message)
+ assert_equal(response, exception.response)
+ end
+
+ def test_api_bearer_token_failure_raw
+ error_message =
+ '"/api/v1" is forbidden because ' \
+ 'system:anonymous cannot list on pods in'
+ response = OpenStruct.new(code: 401, message: error_message)
+
+ stub_request(:get, 'http://localhost:8080/api/v1')
+ .with(headers: { Authorization: 'Bearer invalid_token' })
+ .to_raise(Kubeclient::HttpError.new(403, error_message, response))
+
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/api/',
+ auth_options: { bearer_token: 'invalid_token' }
+ )
+
+ exception = assert_raises(Kubeclient::HttpError) { client.get_pods(as: :raw) }
+ assert_equal(403, exception.error_code)
+ assert_equal(error_message, exception.message)
+ assert_equal(response, exception.response)
+ end
+
+ def test_api_basic_auth_success
+ stub_request(:get, 'http://localhost:8080/api/v1')
+ .with(basic_auth: %w[username password])
+ .to_return(body: open_test_file('core_api_resource_list.json'), status: 200)
+ stub_request(:get, 'http://localhost:8080/api/v1/pods')
+ .with(basic_auth: %w[username password])
+ .to_return(body: open_test_file('pod_list.json'), status: 200)
+
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/api/',
+ auth_options: { username: 'username', password: 'password' }
+ )
+
+ pods = client.get_pods
+
+ assert_equal('Pod', pods.kind)
+ assert_equal(1, pods.size)
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1/pods',
+ times: 1
+ )
+ end
+
+ def test_api_basic_auth_back_comp_success
+ stub_request(:get, 'http://localhost:8080/api/v1')
+ .with(basic_auth: %w[username password])
+ .to_return(body: open_test_file('core_api_resource_list.json'), status: 200)
+ stub_request(:get, 'http://localhost:8080/api/v1/pods')
+ .with(basic_auth: %w[username password])
+ .to_return(body: open_test_file('pod_list.json'), status: 200)
+
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/api/',
+ auth_options: { user: 'username', password: 'password' }
+ )
+
+ pods = client.get_pods
+
+ assert_equal('Pod', pods.kind)
+ assert_equal(1, pods.size)
+ assert_requested(:get, 'http://localhost:8080/api/v1/pods', times: 1)
+ end
+
+ def test_api_basic_auth_failure
+ error_message = 'HTTP status code 401, 401 Unauthorized'
+ response = OpenStruct.new(code: 401, message: '401 Unauthorized')
+
+ stub_request(:get, 'http://localhost:8080/api/v1')
+ .with(basic_auth: %w[username password])
+ .to_raise(Kubeclient::HttpError.new(401, error_message, response))
+
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/api/',
+ auth_options: { username: 'username', password: 'password' }
+ )
+
+ exception = assert_raises(Kubeclient::HttpError) { client.get_pods }
+ assert_equal(401, exception.error_code)
+ assert_equal(error_message, exception.message)
+ assert_equal(response, exception.response)
+ assert_requested(:get, 'http://localhost:8080/api/v1', times: 1)
+ end
+
+ def test_api_basic_auth_failure_raw
+ error_message = 'HTTP status code 401, 401 Unauthorized'
+ response = OpenStruct.new(code: 401, message: '401 Unauthorized')
+
+ stub_request(:get, 'http://localhost:8080/api/v1')
+ .with(basic_auth: %w[username password])
+ .to_raise(Kubeclient::HttpError.new(401, error_message, response))
+
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/api/',
+ auth_options: { username: 'username', password: 'password' }
+ )
+
+ exception = assert_raises(Kubeclient::HttpError) { client.get_pods(as: :raw) }
+ assert_equal(401, exception.error_code)
+ assert_equal(error_message, exception.message)
+ assert_equal(response, exception.response)
+
+ assert_requested(:get, 'http://localhost:8080/api/v1', times: 1)
+ end
+
+ def test_init_username_no_password
+ expected_msg = 'Basic auth requires both username & password'
+ exception = assert_raises(ArgumentError) do
+ Kubeclient::Client.new(
+ 'http://localhost:8080',
+ auth_options: { username: 'username' }
+ )
+ end
+ assert_equal(expected_msg, exception.message)
+ end
+
+ def test_init_user_no_password
+ expected_msg = 'Basic auth requires both username & password'
+ exception = assert_raises(ArgumentError) do
+ Kubeclient::Client.new(
+ 'http://localhost:8080',
+ auth_options: { user: 'username' }
+ )
+ end
+ assert_equal(expected_msg, exception.message)
+ end
+
+ def test_init_username_and_bearer_token
+ expected_msg = 'Invalid auth options: specify only one of username/password,' \
+ ' bearer_token or bearer_token_file'
+ exception = assert_raises(ArgumentError) do
+ Kubeclient::Client.new(
+ 'http://localhost:8080',
+ auth_options: { username: 'username', bearer_token: 'token' }
+ )
+ end
+ assert_equal(expected_msg, exception.message)
+ end
+
+ def test_init_username_and_bearer_token_file
+ expected_msg = 'Invalid auth options: specify only one of username/password,' \
+ ' bearer_token or bearer_token_file'
+ exception = assert_raises(ArgumentError) do
+ Kubeclient::Client.new(
+ 'http://localhost:8080',
+ auth_options: { username: 'username', bearer_token_file: 'token-file' }
+ )
+ end
+ assert_equal(expected_msg, exception.message)
+ end
+
+ def test_bearer_token_and_bearer_token_file
+ expected_msg =
+ 'Invalid auth options: specify only one of username/password,' \
+ ' bearer_token or bearer_token_file'
+ exception = assert_raises(ArgumentError) do
+ Kubeclient::Client.new(
+ 'http://localhost:8080',
+ auth_options: { bearer_token: 'token', bearer_token_file: 'token-file' }
+ )
+ end
+ assert_equal(expected_msg, exception.message)
+ end
+
+ def test_bearer_token_file_not_exist
+ expected_msg = 'Token file token-file does not exist'
+ exception = assert_raises(ArgumentError) do
+ Kubeclient::Client.new(
+ 'http://localhost:8080',
+ auth_options: { bearer_token_file: 'token-file' }
+ )
+ end
+ assert_equal(expected_msg, exception.message)
+ end
+
+ def test_api_bearer_token_file_success
+ stub_core_api_list
+ stub_request(:get, 'http://localhost:8080/api/v1/pods')
+ .with(headers: { Authorization: 'Bearer valid_token' })
+ .to_return(body: open_test_file('pod_list.json'), status: 200)
+
+ file = File.join(File.dirname(__FILE__), 'valid_token_file')
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/api/',
+ auth_options: { bearer_token_file: file }
+ )
+
+ pods = client.get_pods
+
+ assert_equal('Pod', pods.kind)
+ assert_equal(1, pods.size)
+ end
+
+ def test_proxy_url
+ stub_core_api_list
+
+ client = Kubeclient::Client.new('http://host:8080', 'v1')
+ assert_equal(
+ 'http://host:8080/api/v1/namespaces/ns/services/srvname:srvportname/proxy',
+ client.proxy_url('service', 'srvname', 'srvportname', 'ns')
+ )
+
+ assert_equal(
+ 'http://host:8080/api/v1/namespaces/ns/services/srvname:srvportname/proxy',
+ client.proxy_url('services', 'srvname', 'srvportname', 'ns')
+ )
+
+ assert_equal(
+ 'http://host:8080/api/v1/namespaces/ns/pods/srvname:srvportname/proxy',
+ client.proxy_url('pod', 'srvname', 'srvportname', 'ns')
+ )
+
+ assert_equal(
+ 'http://host:8080/api/v1/namespaces/ns/pods/srvname:srvportname/proxy',
+ client.proxy_url('pods', 'srvname', 'srvportname', 'ns')
+ )
+
+ # Check no namespace provided
+ assert_equal(
+ 'http://host:8080/api/v1/nodes/srvname:srvportname/proxy',
+ client.proxy_url('nodes', 'srvname', 'srvportname')
+ )
+
+ assert_equal(
+ 'http://host:8080/api/v1/nodes/srvname:srvportname/proxy',
+ client.proxy_url('node', 'srvname', 'srvportname')
+ )
+
+ # Check integer port
+ assert_equal(
+ 'http://host:8080/api/v1/nodes/srvname:5001/proxy',
+ client.proxy_url('nodes', 'srvname', 5001)
+ )
+
+ assert_equal(
+ 'http://host:8080/api/v1/nodes/srvname:5001/proxy',
+ client.proxy_url('node', 'srvname', 5001)
+ )
+ end
+
+ def test_attr_readers
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/api/',
+ ssl_options: { client_key: 'secret' },
+ auth_options: { bearer_token: 'token' }
+ )
+ assert_equal('/api', client.api_endpoint.path)
+ assert_equal('secret', client.ssl_options[:client_key])
+ assert_equal('token', client.auth_options[:bearer_token])
+ assert_equal('Bearer token', client.headers[:Authorization])
+ end
+
+ def test_nil_items
+ # handle https://github.com/kubernetes/kubernetes/issues/13096
+ stub_core_api_list
+ stub_request(:get, %r{/persistentvolumeclaims})
+ .to_return(body: open_test_file('persistent_volume_claims_nil_items.json'), status: 200)
+
+ client.get_persistent_volume_claims
+ end
+
+ # Timeouts
+
+ def test_timeouts_defaults
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/api/'
+ )
+ rest_client = client.rest_client
+ assert_default_open_timeout(rest_client.open_timeout)
+ assert_equal(60, rest_client.read_timeout)
+ end
+
+ def test_timeouts_open
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/api/',
+ timeouts: { open: 10 }
+ )
+ rest_client = client.rest_client
+ assert_equal(10, rest_client.open_timeout)
+ assert_equal(60, rest_client.read_timeout)
+ end
+
+ def test_timeouts_read
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/api/',
+ timeouts: { read: 300 }
+ )
+ rest_client = client.rest_client
+ assert_default_open_timeout(rest_client.open_timeout)
+ assert_equal(300, rest_client.read_timeout)
+ end
+
+ def test_timeouts_both
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/api/',
+ timeouts: { open: 10, read: 300 }
+ )
+ rest_client = client.rest_client
+ assert_equal(10, rest_client.open_timeout)
+ assert_equal(300, rest_client.read_timeout)
+ end
+
+ def test_timeouts_infinite
+ client = Kubeclient::Client.new(
+ 'http://localhost:8080/api/',
+ timeouts: { open: nil, read: nil }
+ )
+ rest_client = client.rest_client
+ assert_nil(rest_client.open_timeout)
+ assert_nil(rest_client.read_timeout)
+ end
+
+ def assert_default_open_timeout(actual)
+ if RUBY_VERSION >= '2.3'
+ assert_equal(60, actual)
+ else
+ assert_nil(actual)
+ end
+ end
+
+ private
+
+ def stub_get_services
+ stub_request(:get, %r{/services})
+ .to_return(body: open_test_file('entity_list.json'), status: 200)
+ end
+
+ def client
+ @client ||= Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ end
+
+ # dup method creates a shallow copy which is not good in this case
+ # since rename_keys changes the input hash
+ # hence need to create a deep_copy
+ def deep_copy(hash)
+ Marshal.load(Marshal.dump(hash))
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_limit_range.rb b/vendor/gems/kubeclient/test/test_limit_range.rb
new file mode 100644
index 00000000000..e9822578e00
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_limit_range.rb
@@ -0,0 +1,25 @@
+require_relative 'test_helper'
+
+# LimitRange tests
+class TestLimitRange < MiniTest::Test
+ def test_get_from_json_v1
+ stub_core_api_list
+ stub_request(:get, %r{/limitranges})
+ .to_return(body: open_test_file('limit_range.json'), status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ limit_range = client.get_limit_range('limits', 'quota-example')
+
+ assert_instance_of(Kubeclient::Resource, limit_range)
+ assert_equal('limits', limit_range.metadata.name)
+ assert_equal('Container', limit_range.spec.limits[0].type)
+ assert_equal('100m', limit_range.spec.limits[0].default.cpu)
+ assert_equal('512Mi', limit_range.spec.limits[0].default.memory)
+
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1/namespaces/quota-example/limitranges/limits',
+ times: 1
+ )
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_missing_methods.rb b/vendor/gems/kubeclient/test/test_missing_methods.rb
new file mode 100644
index 00000000000..67614c95adc
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_missing_methods.rb
@@ -0,0 +1,80 @@
+require_relative 'test_helper'
+
+# Test method_missing, respond_to? and respond_to_missing behaviour
+class TestMissingMethods < MiniTest::Test
+ def test_missing
+ stub_core_api_list
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ assert_equal(true, client.respond_to?(:get_pod))
+ assert_equal(true, client.respond_to?(:get_pods))
+ assert_equal(false, client.respond_to?(:get_pie))
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') # Reset discovery
+ assert_equal(false, client.respond_to?(:get_pie))
+ assert_equal(true, client.respond_to?(:get_pods))
+ assert_equal(true, client.respond_to?(:get_pod))
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') # Reset discovery
+ assert_instance_of(Method, client.method(:get_pods))
+ assert_raises(NameError) do
+ client.method(:get_pies)
+ end
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') # Reset discovery
+ assert_raises(NameError) do
+ client.method(:get_pies)
+ end
+ assert_instance_of(Method, client.method(:get_pods))
+
+ stub_request(:get, %r{/api/v1$}).to_return(
+ body: '',
+ status: 404
+ ) # If discovery fails we expect the below raise an exception
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ assert_raises(Kubeclient::HttpError) do
+ client.discover
+ end
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ assert_raises(Kubeclient::HttpError) do
+ client.method(:get_pods)
+ end
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ assert_raises(Kubeclient::HttpError) do
+ client.respond_to?(:get_pods)
+ end
+ end
+
+ def test_nonsuffix_plurals
+ stub_request(:get, %r{/apis/extensions/v1beta1$}).to_return(
+ body: open_test_file('extensions_v1beta1_api_resource_list.json'),
+ status: 200
+ )
+ client = Kubeclient::Client.new('http://localhost:8080/apis/extensions', 'v1beta1')
+ assert_equal(true, client.respond_to?(:get_network_policy))
+ assert_equal(true, client.respond_to?(:get_network_policies))
+ assert_equal(true, client.respond_to?(:get_pod_security_policy))
+ assert_equal(true, client.respond_to?(:get_pod_security_policies))
+ end
+
+ def test_irregular_names
+ stub_core_api_list
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ assert_equal(true, client.respond_to?(:get_endpoint))
+ assert_equal(true, client.respond_to?(:get_endpoints))
+
+ stub_request(:get, %r{/apis/security.openshift.io/v1$}).to_return(
+ body: open_test_file('security.openshift.io_api_resource_list.json'),
+ status: 200
+ )
+ client = Kubeclient::Client.new('http://localhost:8080/apis/security.openshift.io', 'v1')
+ assert_equal(true, client.respond_to?(:get_security_context_constraint))
+ assert_equal(true, client.respond_to?(:get_security_context_constraints))
+ end
+
+ def test_lowercase_kind
+ stub_request(:get, %r{/apis/config.istio.io/v1alpha2$}).to_return(
+ body: open_test_file('config.istio.io_api_resource_list.json'),
+ status: 200
+ )
+ client = Kubeclient::Client.new('http://localhost:8080/apis/config.istio.io', 'v1alpha2')
+ assert_equal(true, client.respond_to?(:get_servicecontrolreport))
+ assert_equal(true, client.respond_to?(:get_servicecontrolreports))
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_namespace.rb b/vendor/gems/kubeclient/test/test_namespace.rb
new file mode 100644
index 00000000000..7283aa69b67
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_namespace.rb
@@ -0,0 +1,59 @@
+require_relative 'test_helper'
+
+# Namespace entity tests
+class TestNamespace < MiniTest::Test
+ def test_get_namespace_v1
+ stub_core_api_list
+ stub_request(:get, %r{/namespaces})
+ .to_return(body: open_test_file('namespace.json'), status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ namespace = client.get_namespace('staging')
+
+ assert_instance_of(Kubeclient::Resource, namespace)
+ assert_equal('e388bc10-c021-11e4-a514-3c970e4a436a', namespace.metadata.uid)
+ assert_equal('staging', namespace.metadata.name)
+ assert_equal('1168', namespace.metadata.resourceVersion)
+ assert_equal('v1', namespace.apiVersion)
+
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1/namespaces/staging',
+ times: 1
+ )
+ end
+
+ def test_delete_namespace_v1
+ our_namespace = Kubeclient::Resource.new
+ our_namespace.metadata = {}
+ our_namespace.metadata.name = 'staging'
+
+ stub_core_api_list
+ stub_request(:delete, %r{/namespaces})
+ .to_return(body: open_test_file('namespace.json'), status: 200)
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ our_namespace = client.delete_namespace(our_namespace.metadata.name)
+ assert_kind_of(RecursiveOpenStruct, our_namespace)
+
+ assert_requested(
+ :delete,
+ 'http://localhost:8080/api/v1/namespaces/staging',
+ times: 1
+ )
+ end
+
+ def test_create_namespace
+ stub_core_api_list
+ stub_request(:post, %r{/namespaces})
+ .to_return(body: open_test_file('created_namespace.json'), status: 201)
+
+ namespace = Kubeclient::Resource.new
+ namespace.metadata = {}
+ namespace.metadata.name = 'development'
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
+ created_namespace = client.create_namespace(namespace)
+ assert_instance_of(Kubeclient::Resource, created_namespace)
+ assert_equal(namespace.metadata.name, created_namespace.metadata.name)
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_node.rb b/vendor/gems/kubeclient/test/test_node.rb
new file mode 100644
index 00000000000..aa7459d63c9
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_node.rb
@@ -0,0 +1,70 @@
+require_relative 'test_helper'
+
+# Node entity tests
+class TestNode < MiniTest::Test
+ def test_get_from_json_v1
+ stub_core_api_list
+ stub_request(:get, %r{/nodes})
+ .to_return(body: open_test_file('node.json'), status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ node = client.get_node('127.0.0.1')
+
+ assert_instance_of(Kubeclient::Resource, node)
+
+ assert_equal('041143c5-ce39-11e4-ac24-3c970e4a436a', node.metadata.uid)
+ assert_equal('127.0.0.1', node.metadata.name)
+ assert_equal('1724', node.metadata.resourceVersion)
+ assert_equal('v1', node.apiVersion)
+ assert_equal('2015-03-19T15:08:20+02:00', node.metadata.creationTimestamp)
+
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1',
+ times: 1
+ )
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1/nodes/127.0.0.1',
+ times: 1
+ )
+ end
+
+ def test_get_from_json_v1_raw
+ stub_core_api_list
+ stub_request(:get, %r{/nodes})
+ .to_return(body: open_test_file('node.json'), status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ response = client.get_node('127.0.0.1', nil, as: :raw)
+
+ assert_equal(open_test_file('node.json').read, response)
+
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1',
+ times: 1
+ )
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1/nodes/127.0.0.1',
+ times: 1
+ )
+ end
+
+ def test_get_from_json_v1_raw_error
+ stub_request(:get, %r{/nodes})
+ .to_return(body: open_test_file('node.json'), status: 200)
+ stub_request(:get, %r{/api/v1$})
+ .to_return(body: open_test_file('core_api_resource_list.json'), status: 500)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+
+ exception = assert_raises(Kubeclient::HttpError) do
+ client.get_node('127.0.0.1', nil, as: :raw)
+ end
+
+ assert_instance_of(Kubeclient::HttpError, exception)
+ assert_equal('500 Internal Server Error', exception.message)
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_oidc_auth_provider.rb b/vendor/gems/kubeclient/test/test_oidc_auth_provider.rb
new file mode 100644
index 00000000000..cdf325e9406
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_oidc_auth_provider.rb
@@ -0,0 +1,103 @@
+require_relative 'test_helper'
+require 'openid_connect'
+
+class OIDCAuthProviderTest < MiniTest::Test
+ def setup
+ @client_id = 'client_id'
+ @client_secret = 'client_secret'
+ @idp_issuer_url = 'idp_issuer_url'
+ @refresh_token = 'refresh_token'
+ @id_token = 'id_token'
+ @new_id_token = 'new_id_token'
+ end
+
+ def test_expired_token
+ OpenIDConnect::Discovery::Provider::Config.stub(:discover!, discovery_mock) do
+ OpenIDConnect::ResponseObject::IdToken.stub(:decode, id_token_mock(Time.now.to_i - 7200)) do
+ OpenIDConnect::Client.stub(:new, openid_client_mock) do
+ retrieved_id_token = Kubeclient::OIDCAuthProvider.token(
+ 'client-id' => @client_id,
+ 'client-secret' => @client_secret,
+ 'id-token' => @id_token,
+ 'idp-issuer-url' => @idp_issuer_url,
+ 'refresh-token' => @refresh_token
+ )
+ assert_equal(@new_id_token, retrieved_id_token)
+ end
+ end
+ end
+ end
+
+ def test_valid_token
+ OpenIDConnect::Discovery::Provider::Config.stub(:discover!, discovery_mock) do
+ OpenIDConnect::ResponseObject::IdToken.stub(:decode, id_token_mock(Time.now.to_i + 7200)) do
+ retrieved_id_token = Kubeclient::OIDCAuthProvider.token(
+ 'client-id' => @client_id,
+ 'client-secret' => @client_secret,
+ 'id-token' => @id_token,
+ 'idp-issuer-url' => @idp_issuer_url,
+ 'refresh-token' => @refresh_token
+ )
+ assert_equal(@id_token, retrieved_id_token)
+ end
+ end
+ end
+
+ def test_missing_id_token
+ OpenIDConnect::Discovery::Provider::Config.stub(:discover!, discovery_mock) do
+ OpenIDConnect::Client.stub(:new, openid_client_mock) do
+ retrieved_id_token = Kubeclient::OIDCAuthProvider.token(
+ 'client-id' => @client_id,
+ 'client-secret' => @client_secret,
+ 'idp-issuer-url' => @idp_issuer_url,
+ 'refresh-token' => @refresh_token
+ )
+ assert_equal(@new_id_token, retrieved_id_token)
+ end
+ end
+ end
+
+ def test_token_with_unknown_kid
+ OpenIDConnect::Discovery::Provider::Config.stub(:discover!, discovery_mock) do
+ OpenIDConnect::ResponseObject::IdToken.stub(
+ :decode, ->(_token, _jwks) { raise JSON::JWK::Set::KidNotFound }
+ ) do
+ OpenIDConnect::Client.stub(:new, openid_client_mock) do
+ retrieved_id_token = Kubeclient::OIDCAuthProvider.token(
+ 'client-id' => @client_id,
+ 'client-secret' => @client_secret,
+ 'id-token' => @id_token,
+ 'idp-issuer-url' => @idp_issuer_url,
+ 'refresh-token' => @refresh_token
+ )
+ assert_equal(@new_id_token, retrieved_id_token)
+ end
+ end
+ end
+ end
+
+ private
+
+ def openid_client_mock
+ access_token = Minitest::Mock.new
+ access_token.expect(@id_token, @new_id_token)
+
+ openid_client = Minitest::Mock.new
+ openid_client.expect(:refresh_token=, nil, [@refresh_token])
+ openid_client.expect(:access_token!, access_token)
+ end
+
+ def id_token_mock(expiry)
+ id_token_mock = Minitest::Mock.new
+ id_token_mock.expect(:exp, expiry)
+ end
+
+ def discovery_mock
+ discovery = Minitest::Mock.new
+ discovery.expect(:jwks, 'jwks')
+ discovery.expect(:authorization_endpoint, 'authz_endpoint')
+ discovery.expect(:token_endpoint, 'token_endpoint')
+ discovery.expect(:userinfo_endpoint, 'userinfo_endpoint')
+ discovery
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_persistent_volume.rb b/vendor/gems/kubeclient/test/test_persistent_volume.rb
new file mode 100644
index 00000000000..8b283868a1f
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_persistent_volume.rb
@@ -0,0 +1,29 @@
+require_relative 'test_helper'
+
+# PersistentVolume tests
+class TestPersistentVolume < MiniTest::Test
+ def test_get_from_json_v1
+ stub_core_api_list
+ stub_request(:get, %r{/persistentvolumes})
+ .to_return(body: open_test_file('persistent_volume.json'), status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ volume = client.get_persistent_volume('pv0001')
+
+ assert_instance_of(Kubeclient::Resource, volume)
+ assert_equal('pv0001', volume.metadata.name)
+ assert_equal('10Gi', volume.spec.capacity.storage)
+ assert_equal('/tmp/data01', volume.spec.hostPath.path)
+
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1',
+ times: 1
+ )
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1/persistentvolumes/pv0001',
+ times: 1
+ )
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_persistent_volume_claim.rb b/vendor/gems/kubeclient/test/test_persistent_volume_claim.rb
new file mode 100644
index 00000000000..e51d8562e60
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_persistent_volume_claim.rb
@@ -0,0 +1,28 @@
+require_relative 'test_helper'
+
+# PersistentVolumeClaim tests
+class TestPersistentVolumeClaim < MiniTest::Test
+ def test_get_from_json_v1
+ stub_core_api_list
+ stub_request(:get, %r{/persistentvolumeclaims})
+ .to_return(body: open_test_file('persistent_volume_claim.json'), status: 200)
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ claim = client.get_persistent_volume_claim('myclaim-1', 'default')
+
+ assert_instance_of(Kubeclient::Resource, claim)
+ assert_equal('myclaim-1', claim.metadata.name)
+ assert_equal('3Gi', claim.spec.resources.requests.storage)
+ assert_equal('pv0001', claim.spec.volumeName)
+
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1',
+ times: 1
+ )
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1/namespaces/default/persistentvolumeclaims/myclaim-1',
+ times: 1
+ )
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_pod.rb b/vendor/gems/kubeclient/test/test_pod.rb
new file mode 100644
index 00000000000..afad1774f5e
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_pod.rb
@@ -0,0 +1,81 @@
+require_relative 'test_helper'
+
+# Pod entity tests
+class TestPod < MiniTest::Test
+ def test_get_from_json_v1
+ stub_core_api_list
+ stub_request(:get, %r{/pods})
+ .to_return(body: open_test_file('pod.json'), status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ pod = client.get_pod('redis-master-pod', 'default')
+
+ assert_instance_of(Kubeclient::Resource, pod)
+ assert_equal('redis-master3', pod.metadata.name)
+ assert_equal('dockerfile/redis', pod.spec.containers[0]['image'])
+
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1',
+ times: 1
+ )
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1/namespaces/default/pods/redis-master-pod',
+ times: 1
+ )
+ end
+
+ def test_get_chunks
+ stub_core_api_list
+ stub_request(:get, %r{/pods})
+ .to_return(body: open_test_file('pods_1.json'), status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ pods = client.get_pods(limit: 2)
+
+ assert_equal(2, pods.count)
+ assert_equal('eyJ2IjoibWV0YS5rOHMua', pods.continue)
+
+ continue = pods.continue
+
+ stub_request(:get, %r{/pods})
+ .to_return(body: open_test_file('pods_2.json'), status: 200)
+
+ pods = client.get_pods(limit: 2, continue: continue)
+ assert_equal(2, pods.count)
+ assert_nil(pods.continue)
+
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1',
+ times: 1
+ )
+ assert_requested(
+ :get,
+ 'http://localhost:8080/api/v1/pods?limit=2',
+ times: 1
+ )
+ assert_requested(
+ :get,
+ "http://localhost:8080/api/v1/pods?continue=#{continue}&limit=2",
+ times: 1
+ )
+ end
+
+ def test_get_chunks_410_gone
+ stub_core_api_list
+ stub_request(:get, %r{/pods})
+ .to_return(body: open_test_file('pods_410.json'), status: 410)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+
+ err = assert_raises Kubeclient::HttpError do
+ client.get_pods(limit: 2, continue: 'eyJ2IjoibWV0YS5')
+ end
+
+ assert_equal(err.message,
+ "The provided from parameter is too old to display a consistent list result. \
+You must start a new list without the from.")
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_pod_log.rb b/vendor/gems/kubeclient/test/test_pod_log.rb
new file mode 100644
index 00000000000..d9ba3eaabbd
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_pod_log.rb
@@ -0,0 +1,157 @@
+require_relative 'test_helper'
+
+# Pod log tests
+class TestPodLog < MiniTest::Test
+ def test_get_pod_log
+ stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log})
+ .to_return(body: open_test_file('pod_log.txt'),
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ retrieved_log = client.get_pod_log('redis-master-pod', 'default')
+
+ assert_equal(open_test_file('pod_log.txt').read, retrieved_log)
+
+ assert_requested(:get,
+ 'http://localhost:8080/api/v1/namespaces/default/pods/redis-master-pod/log',
+ times: 1)
+ end
+
+ def test_get_pod_log_container
+ stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log})
+ .to_return(body: open_test_file('pod_log.txt'),
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ retrieved_log = client.get_pod_log('redis-master-pod', 'default', container: 'ruby')
+
+ assert_equal(open_test_file('pod_log.txt').read, retrieved_log)
+
+ assert_requested(:get,
+ 'http://localhost:8080/api/v1/namespaces/default/pods/redis-master-pod/log?container=ruby',
+ times: 1)
+ end
+
+ def test_get_pod_log_since_time
+ stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log})
+ .to_return(body: open_test_file('pod_log.txt'),
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ retrieved_log = client.get_pod_log('redis-master-pod',
+ 'default',
+ timestamps: true,
+ since_time: '2018-04-27T18:30:17.480321984Z')
+
+ assert_equal(open_test_file('pod_log.txt').read, retrieved_log)
+
+ assert_requested(:get,
+ 'http://localhost:8080/api/v1/namespaces/default/pods/redis-master-pod/log?sinceTime=2018-04-27T18:30:17.480321984Z&timestamps=true',
+ times: 1)
+ end
+
+ def test_get_pod_log_tail_lines
+ selected_lines = open_test_file('pod_log.txt').to_a[-2..1].join
+
+ stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log})
+ .to_return(body: selected_lines,
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ retrieved_log = client.get_pod_log('redis-master-pod',
+ 'default',
+ tail_lines: 2)
+
+ assert_equal(selected_lines, retrieved_log)
+
+ assert_requested(:get,
+ 'http://localhost:8080/api/v1/namespaces/default/pods/redis-master-pod/log?tailLines=2',
+ times: 1)
+ end
+
+ def test_get_pod_limit_bytes
+ selected_bytes = open_test_file('pod_log.txt').read(10)
+
+ stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log})
+ .to_return(body: selected_bytes,
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ retrieved_log = client.get_pod_log('redis-master-pod',
+ 'default',
+ limit_bytes: 10)
+
+ assert_equal(selected_bytes, retrieved_log)
+
+ assert_requested(:get,
+ 'http://localhost:8080/api/v1/namespaces/default/pods/redis-master-pod/log?limitBytes=10',
+ times: 1)
+ end
+
+ def test_watch_pod_log
+ file = open_test_file('pod_log.txt')
+ expected_lines = file.read.split("\n")
+
+ stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log\?.*follow})
+ .to_return(body: file, status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+
+ stream = client.watch_pod_log('redis-master-pod', 'default')
+ stream.to_enum.with_index do |notice, index|
+ assert_instance_of(String, notice)
+ assert_equal(expected_lines[index], notice)
+ end
+ end
+
+ def test_watch_pod_log_with_block
+ file = open_test_file('pod_log.txt')
+ first = file.readlines.first.chomp
+
+ stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log\?.*follow})
+ .to_return(body: file, status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+
+ client.watch_pod_log('redis-master-pod', 'default') do |line|
+ assert_equal first, line
+ break
+ end
+ end
+
+ def test_watch_pod_log_follow_redirect
+ expected_lines = open_test_file('pod_log.txt').read.split("\n")
+ redirect = 'http://localhost:1234/api/namespaces/default/pods/redis-master-pod/log'
+
+ stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log\?.*follow})
+ .to_return(status: 302, headers: { location: redirect })
+
+ stub_request(:get, redirect)
+ .to_return(body: open_test_file('pod_log.txt'),
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ stream = client.watch_pod_log('redis-master-pod', 'default')
+ stream.to_enum.with_index do |notice, index|
+ assert_instance_of(String, notice)
+ assert_equal(expected_lines[index], notice)
+ end
+ end
+
+ def test_watch_pod_log_max_redirect
+ redirect = 'http://localhost:1234/api/namespaces/default/pods/redis-master-pod/log'
+
+ stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log\?.*follow})
+ .to_return(status: 302, headers: { location: redirect })
+
+ stub_request(:get, redirect)
+ .to_return(body: open_test_file('pod_log.txt'),
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1', http_max_redirects: 0)
+ assert_raises(Kubeclient::HttpError) do
+ client.watch_pod_log('redis-master-pod', 'default').each do
+ end
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_process_template.rb b/vendor/gems/kubeclient/test/test_process_template.rb
new file mode 100644
index 00000000000..e3b4670fb87
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_process_template.rb
@@ -0,0 +1,80 @@
+require_relative 'test_helper'
+
+# Process Template tests
+class TestProcessTemplate < MiniTest::Test
+ def test_process_template
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ template = {}
+ template[:metadata] = {}
+ template[:metadata][:name] = 'my-template'
+ template[:metadata][:namespace] = 'default'
+ template[:kind] = 'Template'
+ template[:apiVersion] = 'v1'
+ service = {}
+ service[:metadata] = {}
+ service[:metadata][:name] = '${NAME_PREFIX}my-service'
+ service[:kind] = 'Service'
+ service[:apiVersion] = 'v1'
+ template[:objects] = [service]
+ param = { name: 'NAME_PREFIX', value: 'test/' }
+ template[:parameters] = [param]
+
+ req_body = '{"metadata":{"name":"my-template","namespace":"default"},' \
+ '"kind":"Template","apiVersion":"v1","objects":[{"metadata":' \
+ '{"name":"${NAME_PREFIX}my-service"},"kind":"Service","apiVersion":"v1"}],' \
+ '"parameters":[{"name":"NAME_PREFIX","value":"test/"}]}'
+
+ expected_url = 'http://localhost:8080/api/v1/namespaces/default/processedtemplates'
+ stub_request(:post, expected_url)
+ .with(body: req_body, headers: { 'Content-Type' => 'application/json' })
+ .to_return(body: open_test_file('processed_template.json'), status: 200)
+
+ processed_template = client.process_template(template)
+
+ assert_equal('test/my-service', processed_template['objects'].first['metadata']['name'])
+
+ assert_requested(:post, expected_url, times: 1) do |req|
+ data = JSON.parse(req.body)
+ data['kind'] == 'Template' &&
+ data['apiVersion'] == 'v1' &&
+ data['metadata']['name'] == 'my-template' &&
+ data['metadata']['namespace'] == 'default'
+ end
+ end
+
+ # Ensure _template and _templates methods hit `/templates` rather than
+ # `/processedtemplates` URL.
+ def test_templates_methods
+ stub_request(:get, %r{/apis/template\.openshift\.io/v1$}).to_return(
+ body: open_test_file('template.openshift.io_api_resource_list.json'),
+ status: 200
+ )
+ client = Kubeclient::Client.new('http://localhost:8080/apis/template.openshift.io', 'v1')
+
+ expected_url = 'http://localhost:8080/apis/template.openshift.io/v1/namespaces/default/templates'
+ stub_request(:get, expected_url)
+ .to_return(body: open_test_file('template_list.json'), status: 200)
+ client.get_templates(namespace: 'default')
+ assert_requested(:get, expected_url, times: 1)
+
+ expected_url = 'http://localhost:8080/apis/template.openshift.io/v1/namespaces/default/templates/my-template'
+ stub_request(:get, expected_url)
+ .to_return(body: open_test_file('template.json'), status: 200)
+ client.get_template('my-template', 'default')
+ assert_requested(:get, expected_url, times: 1)
+ end
+
+ def test_no_processedtemplates_methods
+ stub_request(:get, %r{/apis/template\.openshift\.io/v1$}).to_return(
+ body: open_test_file('template.openshift.io_api_resource_list.json'),
+ status: 200
+ )
+ client = Kubeclient::Client.new('http://localhost:8080/apis/template.openshift.io', 'v1')
+ client.discover
+
+ refute_respond_to(client, :get_processedtemplates)
+ refute_respond_to(client, :get_processedtemplate)
+ refute_respond_to(client, :get_processed_templates)
+ refute_respond_to(client, :get_processed_template)
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_real_cluster.rb b/vendor/gems/kubeclient/test/test_real_cluster.rb
new file mode 100644
index 00000000000..7ce9493a1bb
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_real_cluster.rb
@@ -0,0 +1,162 @@
+require_relative 'test_helper'
+
+class KubeclientRealClusterTest < MiniTest::Test
+ # Tests here actually connect to a cluster!
+ # For simplicity, these tests use same config/*.kubeconfig files as test_config.rb,
+ # so are intended to run from config/update_certs_k0s.rb script.
+ def setup
+ if ENV['KUBECLIENT_TEST_REAL_CLUSTER'] == 'true'
+ WebMock.enable_net_connect!
+ else
+ skip('Requires real cluster, see test/config/update_certs_k0s.rb.')
+ end
+ end
+
+ def teardown
+ WebMock.disable_net_connect! # Don't allow any connections in other tests.
+ end
+
+ # Partially isolated tests that check Client behavior with given `verify_ssl` value:
+
+ # localhost and 127.0.0.1 are among names on the certificate
+ HOSTNAME_COVERED_BY_CERT = 'https://127.0.0.1:6443'.freeze
+ # 127.0.0.2 also means localhost but is not included in the certificate.
+ HOSTNAME_NOT_ON_CERT = 'https://127.0.0.2:6443'.freeze
+
+ def test_real_cluster_verify_peer
+ config = Kubeclient::Config.read(config_file('external.kubeconfig'))
+ context = config.context
+ client1 = Kubeclient::Client.new(
+ HOSTNAME_COVERED_BY_CERT, 'v1',
+ ssl_options: context.ssl_options.merge(verify_ssl: OpenSSL::SSL::VERIFY_PEER),
+ auth_options: context.auth_options
+ )
+ check_cert_accepted(client1)
+ client2 = Kubeclient::Client.new(
+ HOSTNAME_NOT_ON_CERT, 'v1',
+ ssl_options: context.ssl_options.merge(verify_ssl: OpenSSL::SSL::VERIFY_PEER),
+ auth_options: context.auth_options
+ )
+ check_cert_rejected(client2)
+ end
+
+ def test_real_cluster_verify_none
+ config = Kubeclient::Config.read(config_file('external.kubeconfig'))
+ context = config.context
+ client1 = Kubeclient::Client.new(
+ HOSTNAME_COVERED_BY_CERT, 'v1',
+ ssl_options: context.ssl_options.merge(verify_ssl: OpenSSL::SSL::VERIFY_NONE),
+ auth_options: context.auth_options
+ )
+ check_cert_accepted(client1)
+ client2 = Kubeclient::Client.new(
+ HOSTNAME_NOT_ON_CERT, 'v1',
+ ssl_options: context.ssl_options.merge(verify_ssl: OpenSSL::SSL::VERIFY_NONE),
+ auth_options: context.auth_options
+ )
+ check_cert_accepted(client2)
+ end
+
+ # Integration tests that check combined Config -> Client behavior wrt. `verify_ssl`.
+ # Quite redundant, but this was an embarrasing vulnerability so want to confirm...
+
+ def test_real_cluster_concatenated_ca
+ config = Kubeclient::Config.read(config_file('concatenated-ca.kubeconfig'))
+ context = config.context
+ client1 = Kubeclient::Client.new(
+ HOSTNAME_COVERED_BY_CERT, 'v1',
+ ssl_options: context.ssl_options, auth_options: context.auth_options
+ )
+ check_cert_accepted(client1)
+ client2 = Kubeclient::Client.new(
+ HOSTNAME_NOT_ON_CERT, 'v1',
+ ssl_options: context.ssl_options, auth_options: context.auth_options
+ )
+ check_cert_rejected(client2)
+ end
+
+ def test_real_cluster_verify_ssl_with_ca
+ config = Kubeclient::Config.read(config_file('external.kubeconfig'))
+ context = config.context
+ client1 = Kubeclient::Client.new(
+ HOSTNAME_COVERED_BY_CERT, 'v1',
+ ssl_options: context.ssl_options, auth_options: context.auth_options
+ )
+ check_cert_accepted(client1)
+ client2 = Kubeclient::Client.new(
+ HOSTNAME_NOT_ON_CERT, 'v1',
+ ssl_options: context.ssl_options, auth_options: context.auth_options
+ )
+ check_cert_rejected(client2)
+ end
+
+ def test_real_cluster_verify_ssl_without_ca
+ config = Kubeclient::Config.read(config_file('external-without-ca.kubeconfig'))
+ context = config.context
+ # Hostname matches cert but the local cluster uses self-signed certs from custom CA,
+ # and this config omits CA data, so verification can't succeed.
+ client1 = Kubeclient::Client.new(
+ HOSTNAME_COVERED_BY_CERT, 'v1',
+ ssl_options: context.ssl_options, auth_options: context.auth_options
+ )
+ check_cert_rejected(client1)
+ client2 = Kubeclient::Client.new(
+ HOSTNAME_NOT_ON_CERT, 'v1',
+ ssl_options: context.ssl_options, auth_options: context.auth_options
+ )
+ check_cert_rejected(client2)
+ end
+
+ def test_real_cluster_insecure_without_ca
+ config = Kubeclient::Config.read(config_file('insecure.kubeconfig'))
+ context = config.context
+ # Hostname matches cert but the local cluster uses self-signed certs from custom CA,
+ # and this config omits CA data, so verification would fail;
+ # however, this config specifies `insecure-skip-tls-verify: true` so any cert goes.
+ client1 = Kubeclient::Client.new(
+ HOSTNAME_COVERED_BY_CERT, 'v1',
+ ssl_options: context.ssl_options, auth_options: context.auth_options
+ )
+ check_cert_accepted(client1)
+ client2 = Kubeclient::Client.new(
+ HOSTNAME_NOT_ON_CERT, 'v1',
+ ssl_options: context.ssl_options, auth_options: context.auth_options
+ )
+ check_cert_accepted(client2)
+ end
+
+ private
+
+ # Test cert checking on discovery, CRUD, and watch code paths.
+ def check_cert_accepted(client)
+ client.discover
+ client.get_nodes
+ exercise_watcher_with_timeout(client.watch_nodes)
+ end
+
+ def check_cert_rejected(client)
+ # TODO: all OpenSSL exceptions should be wrapped with Kubeclient error.
+ assert_raises(Kubeclient::HttpError, OpenSSL::SSL::SSLError) do
+ client.discover
+ end
+ # Since discovery fails, methods like .get_nodes, .watch_nodes would all fail
+ # on method_missing -> discover. Call lower-level methods to test actual connection.
+ assert_raises(Kubeclient::HttpError, OpenSSL::SSL::SSLError) do
+ client.get_entities('Node', 'nodes', {})
+ end
+ assert_raises(Kubeclient::HttpError, OpenSSL::SSL::SSLError) do
+ exercise_watcher_with_timeout(client.watch_entities('nodes'))
+ end
+ end
+
+ def exercise_watcher_with_timeout(watcher)
+ thread = Thread.new do
+ sleep(1)
+ watcher.finish
+ end
+ watcher.each do |_notice|
+ break
+ end
+ thread.join
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_replication_controller.rb b/vendor/gems/kubeclient/test/test_replication_controller.rb
new file mode 100644
index 00000000000..47af72210e5
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_replication_controller.rb
@@ -0,0 +1,47 @@
+require_relative 'test_helper'
+
+# Replication Controller entity tests
+class TestReplicationController < MiniTest::Test
+ def test_get_from_json_v1
+ stub_core_api_list
+ stub_request(:get, %r{/replicationcontrollers})
+ .to_return(body: open_test_file('replication_controller.json'),
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ rc = client.get_replication_controller('frontendController', 'default')
+
+ assert_instance_of(Kubeclient::Resource, rc)
+ assert_equal('guestbook-controller', rc.metadata.name)
+ assert_equal('c71aa4c0-a240-11e4-a265-3c970e4a436a', rc.metadata.uid)
+ assert_equal('default', rc.metadata.namespace)
+ assert_equal(3, rc.spec.replicas)
+ assert_equal('guestbook', rc.spec.selector.name)
+
+ assert_requested(:get,
+ 'http://localhost:8080/api/v1/namespaces/default/replicationcontrollers/frontendController',
+ times: 1)
+ end
+
+ def test_delete_replicaset_cascade
+ stub_core_api_list
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ opts = Kubeclient::Resource.new(
+ apiVersion: 'meta/v1',
+ gracePeriodSeconds: 0,
+ kind: 'DeleteOptions',
+ propagationPolicy: 'Foreground'
+ )
+
+ stub_request(:delete,
+ 'http://localhost:8080/api/v1/namespaces/default/replicationcontrollers/frontendController')
+ .with(body: opts.to_hash.to_json)
+ .to_return(status: 200, body: open_test_file('replication_controller.json'), headers: {})
+ rc = client.delete_replication_controller('frontendController', 'default', delete_options: opts)
+ assert_kind_of(RecursiveOpenStruct, rc)
+
+ assert_requested(:delete,
+ 'http://localhost:8080/api/v1/namespaces/default/replicationcontrollers/frontendController',
+ times: 1)
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_resource_list_without_kind.rb b/vendor/gems/kubeclient/test/test_resource_list_without_kind.rb
new file mode 100644
index 00000000000..89ab042b5f5
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_resource_list_without_kind.rb
@@ -0,0 +1,78 @@
+require_relative 'test_helper'
+
+# Core api resource list without kind tests
+class TestResourceListWithoutKind < MiniTest::Test
+ def test_get_from_json_api_v1
+ stub_request(:get, %r{/api/v1$})
+ .to_return(body: open_test_file('core_api_resource_list_without_kind.json'),
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ client.discover
+
+ [
+ {
+ entity: 'pod',
+ type: 'Pod',
+ name: 'pods',
+ methods: %w[pod pods]
+ },
+ {
+ entity: 'node',
+ type: 'Node',
+ name: 'nodes',
+ methods: %w[node nodes]
+ },
+ {
+ entity: 'service',
+ type: 'Service',
+ name: 'services',
+ methods: %w[service services]
+ }
+ ].each { |h| assert_entities(client.instance_variable_get(:@entities)[h[:entity]], h) }
+
+ assert_requested(:get,
+ 'http://localhost:8080/api/v1',
+ times: 1)
+ end
+
+ def test_get_from_json_oapi_v1
+ stub_request(:get, %r{/oapi/v1$})
+ .to_return(body: open_test_file('core_oapi_resource_list_without_kind.json'),
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/oapi/', 'v1')
+ client.discover
+
+ [
+ {
+ entity: 'template',
+ type: 'Template',
+ name: 'templates',
+ methods: %w[template templates]
+ },
+ {
+ entity: 'build',
+ type: 'Build',
+ name: 'builds',
+ methods: %w[build builds]
+ },
+ {
+ entity: 'project',
+ type: 'Project',
+ name: 'projects',
+ methods: %w[project projects]
+ }
+ ].each { |h| assert_entities(client.instance_variable_get(:@entities)[h[:entity]], h) }
+
+ assert_requested(:get,
+ 'http://localhost:8080/oapi/v1',
+ times: 1)
+ end
+
+ def assert_entities(entity, h)
+ assert_equal(entity.entity_type, h[:type])
+ assert_equal(entity.resource_name, h[:name])
+ assert_equal(entity.method_names, h[:methods])
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_resource_quota.rb b/vendor/gems/kubeclient/test/test_resource_quota.rb
new file mode 100644
index 00000000000..cf91a111196
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_resource_quota.rb
@@ -0,0 +1,23 @@
+require_relative 'test_helper'
+
+# ResourceQuota tests
+class TestResourceQuota < MiniTest::Test
+ def test_get_from_json_v1
+ stub_core_api_list
+ stub_request(:get, %r{/resourcequotas})
+ .to_return(body: open_test_file('resource_quota.json'),
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ quota = client.get_resource_quota('quota', 'quota-example')
+
+ assert_instance_of(Kubeclient::Resource, quota)
+ assert_equal('quota', quota.metadata.name)
+ assert_equal('20', quota.spec.hard.cpu)
+ assert_equal('10', quota.spec.hard.secrets)
+
+ assert_requested(:get,
+ 'http://localhost:8080/api/v1/namespaces/quota-example/resourcequotas/quota',
+ times: 1)
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_secret.rb b/vendor/gems/kubeclient/test/test_secret.rb
new file mode 100644
index 00000000000..ec129075a14
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_secret.rb
@@ -0,0 +1,62 @@
+require_relative 'test_helper'
+
+# Namespace entity tests
+class TestSecret < MiniTest::Test
+ def test_get_secret_v1
+ stub_core_api_list
+ stub_request(:get, %r{/secrets})
+ .to_return(body: open_test_file('created_secret.json'),
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ secret = client.get_secret('test-secret', 'dev')
+
+ assert_instance_of(Kubeclient::Resource, secret)
+ assert_equal('4e38a198-2bcb-11e5-a483-0e840567604d', secret.metadata.uid)
+ assert_equal('test-secret', secret.metadata.name)
+ assert_equal('v1', secret.apiVersion)
+ assert_equal('Y2F0J3MgYXJlIGF3ZXNvbWUK', secret.data['super-secret'])
+
+ assert_requested(:get,
+ 'http://localhost:8080/api/v1/namespaces/dev/secrets/test-secret',
+ times: 1)
+ end
+
+ def test_delete_secret_v1
+ stub_core_api_list
+ stub_request(:delete, %r{/secrets})
+ .to_return(status: 200, body: open_test_file('created_secret.json'))
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ secret = client.delete_secret('test-secret', 'dev')
+ assert_kind_of(RecursiveOpenStruct, secret)
+
+ assert_requested(:delete,
+ 'http://localhost:8080/api/v1/namespaces/dev/secrets/test-secret',
+ times: 1)
+ end
+
+ def test_create_secret_v1
+ stub_core_api_list
+ stub_request(:post, %r{/secrets})
+ .to_return(body: open_test_file('created_secret.json'),
+ status: 201)
+
+ secret = Kubeclient::Resource.new
+ secret.metadata = {}
+ secret.metadata.name = 'test-secret'
+ secret.metadata.namespace = 'dev'
+ secret.data = {}
+ secret.data['super-secret'] = 'Y2F0J3MgYXJlIGF3ZXNvbWUK'
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
+ created_secret = client.create_secret(secret)
+ assert_instance_of(Kubeclient::Resource, created_secret)
+ assert_equal(secret.metadata.name, created_secret.metadata.name)
+ assert_equal(secret.metadata.namespace, created_secret.metadata.namespace)
+ assert_equal(
+ secret.data['super-secret'],
+ created_secret.data['super-secret']
+ )
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_security_context_constraint.rb b/vendor/gems/kubeclient/test/test_security_context_constraint.rb
new file mode 100644
index 00000000000..23e53c4464e
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_security_context_constraint.rb
@@ -0,0 +1,62 @@
+require_relative 'test_helper'
+
+# kind: 'SecurityContextConstraints' entity tests.
+# This is one of the unusual `kind`s that are already plural (https://github.com/kubernetes/kubernetes/issues/8115).
+# We force singular in method names like 'create_endpoint',
+# but `kind` should remain plural as in kubernetes.
+class TestSecurityContextConstraints < MiniTest::Test
+ def test_create_security_context_constraint
+ stub_request(:get, %r{/apis/security.openshift.io/v1$}).to_return(
+ body: open_test_file('security.openshift.io_api_resource_list.json'),
+ status: 200
+ )
+
+ testing_scc = Kubeclient::Resource.new(
+ metadata: {
+ name: 'teleportation'
+ },
+ runAsUser: {
+ type: 'MustRunAs'
+ },
+ seLinuxContext: {
+ type: 'MustRunAs'
+ }
+ )
+ req_body = '{"metadata":{"name":"teleportation"},"runAsUser":{"type":"MustRunAs"},' \
+ '"seLinuxContext":{"type":"MustRunAs"},' \
+ '"kind":"SecurityContextConstraints","apiVersion":"security.openshift.io/v1"}'
+
+ stub_request(:post, 'http://localhost:8080/apis/security.openshift.io/v1/securitycontextconstraints')
+ .with(body: req_body)
+ .to_return(body: open_test_file('created_security_context_constraint.json'), status: 201)
+
+ client = Kubeclient::Client.new('http://localhost:8080/apis/security.openshift.io', 'v1')
+ created_scc = client.create_security_context_constraint(testing_scc)
+ assert_equal('SecurityContextConstraints', created_scc.kind)
+ assert_equal('security.openshift.io/v1', created_scc.apiVersion)
+
+ client = Kubeclient::Client.new('http://localhost:8080/apis/security.openshift.io', 'v1',
+ as: :parsed_symbolized)
+ created_scc = client.create_security_context_constraint(testing_scc)
+ assert_equal('SecurityContextConstraints', created_scc[:kind])
+ assert_equal('security.openshift.io/v1', created_scc[:apiVersion])
+ end
+
+ def test_get_security_context_constraints
+ stub_request(:get, %r{/apis/security.openshift.io/v1$}).to_return(
+ body: open_test_file('security.openshift.io_api_resource_list.json'),
+ status: 200
+ )
+ stub_request(:get, %r{/securitycontextconstraints})
+ .to_return(body: open_test_file('security_context_constraint_list.json'), status: 200)
+ client = Kubeclient::Client.new('http://localhost:8080/apis/security.openshift.io', 'v1')
+
+ collection = client.get_security_context_constraints(as: :parsed_symbolized)
+ assert_equal('SecurityContextConstraintsList', collection[:kind])
+ assert_equal('security.openshift.io/v1', collection[:apiVersion])
+
+ # Stripping of 'List' in collection.kind RecursiveOpenStruct mode only is historic.
+ collection = client.get_security_context_constraints
+ assert_equal('SecurityContextConstraints', collection.kind)
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_service.rb b/vendor/gems/kubeclient/test/test_service.rb
new file mode 100644
index 00000000000..6ef3368780d
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_service.rb
@@ -0,0 +1,357 @@
+require_relative 'test_helper'
+
+# Service entity tests
+class TestService < MiniTest::Test
+ def test_construct_our_own_service
+ our_service = Kubeclient::Resource.new
+ our_service.metadata = {}
+ our_service.metadata.name = 'guestbook'
+ our_service.metadata.namespace = 'staging'
+ our_service.metadata.labels = {}
+ our_service.metadata.labels.name = 'guestbook'
+
+ our_service.spec = {}
+ our_service.spec.ports = [{
+ 'port' => 3000,
+ 'targetPort' => 'http-server',
+ 'protocol' => 'TCP'
+ }]
+
+ assert_equal('guestbook', our_service.metadata.labels.name)
+
+ hash = our_service.to_h
+
+ assert_equal(our_service.metadata.labels.name,
+ hash[:metadata][:labels][:name])
+
+ expected_url = 'http://localhost:8080/api/v1/namespaces/staging/services'
+ stub_core_api_list
+ stub_request(:post, expected_url)
+ .to_return(body: open_test_file('created_service.json'), status: 201)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
+ created = client.create_service(our_service)
+
+ assert_instance_of(Kubeclient::Resource, created)
+ assert_equal(created.metadata.name, our_service.metadata.name)
+ assert_equal(created.spec.ports.size, our_service.spec.ports.size)
+
+ # Check that original entity_config is not modified by kind/apiVersion patches:
+ assert_nil(our_service.kind)
+
+ assert_requested(:post, expected_url, times: 1) do |req|
+ data = JSON.parse(req.body)
+ data['kind'] == 'Service' &&
+ data['apiVersion'] == 'v1' &&
+ data['metadata']['name'] == 'guestbook' &&
+ data['metadata']['namespace'] == 'staging'
+ end
+ end
+
+ def test_construct_service_from_symbol_keys
+ service = Kubeclient::Resource.new
+ service.metadata = {
+ labels: { tier: 'frontend' },
+ name: 'test-service',
+ namespace: 'staging'
+ }
+ service.spec = {
+ ports: [{
+ port: 3000,
+ targetPort: 'http-server',
+ protocol: 'TCP'
+ }]
+ }
+
+ expected_url = 'http://localhost:8080/api/v1/namespaces/staging/services'
+ stub_core_api_list
+ stub_request(:post, expected_url)
+ .to_return(body: open_test_file('created_service.json'), status: 201)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
+ client.create_service(service)
+
+ assert_requested(:post, expected_url, times: 1) do |req|
+ data = JSON.parse(req.body)
+ data['kind'] == 'Service' &&
+ data['apiVersion'] == 'v1' &&
+ data['metadata']['name'] == 'test-service' &&
+ data['metadata']['labels']['tier'] == 'frontend' &&
+ data['metadata']['namespace'] == 'staging'
+ end
+ end
+
+ def test_construct_service_from_string_keys
+ service = Kubeclient::Resource.new
+ service.metadata = {
+ 'labels' => { 'tier' => 'frontend' },
+ 'name' => 'test-service',
+ 'namespace' => 'staging'
+ }
+ service.spec = {
+ 'ports' => [{
+ 'port' => 3000,
+ 'targetPort' => 'http-server',
+ 'protocol' => 'TCP'
+ }]
+ }
+
+ stub_core_api_list
+ expected_url = 'http://localhost:8080/api/v1/namespaces/staging/services'
+ stub_request(:post, %r{namespaces/staging/services})
+ .to_return(body: open_test_file('created_service.json'), status: 201)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
+ client.create_service(service)
+
+ assert_requested(:post, expected_url, times: 1) do |req|
+ data = JSON.parse(req.body)
+ data['kind'] == 'Service' &&
+ data['apiVersion'] == 'v1' &&
+ data['metadata']['name'] == 'test-service' &&
+ data['metadata']['labels']['tier'] == 'frontend' &&
+ data['metadata']['namespace'] == 'staging'
+ end
+ end
+
+ def test_conversion_from_json_v1
+ stub_core_api_list
+ stub_request(:get, %r{/services})
+ .to_return(body: open_test_file('service.json'),
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
+ service = client.get_service('redis-slave', 'development')
+
+ assert_instance_of(Kubeclient::Resource, service)
+ assert_equal('2015-04-05T13:00:31Z',
+ service.metadata.creationTimestamp)
+ assert_equal('bdb80a8f-db93-11e4-b293-f8b156af4ae1', service.metadata.uid)
+ assert_equal('redis-slave', service.metadata.name)
+ assert_equal('2815', service.metadata.resourceVersion)
+ assert_equal('v1', service.apiVersion)
+ assert_equal('10.0.0.140', service.spec.clusterIP)
+ assert_equal('development', service.metadata.namespace)
+
+ assert_equal('TCP', service.spec.ports[0].protocol)
+ assert_equal(6379, service.spec.ports[0].port)
+ assert_equal('', service.spec.ports[0].name)
+ assert_equal('redis-server', service.spec.ports[0].targetPort)
+
+ assert_requested(:get,
+ 'http://localhost:8080/api/v1/namespaces/development/services/redis-slave',
+ times: 1)
+ end
+
+ def test_delete_service
+ our_service = Kubeclient::Resource.new
+ our_service.name = 'redis-service'
+ # TODO, new ports assignment to be added
+ our_service.labels = {}
+ our_service.labels.component = 'apiserver'
+ our_service.labels.provider = 'kubernetes'
+
+ stub_core_api_list
+ stub_request(:delete, %r{/namespaces/default/services})
+ .to_return(body: open_test_file('service.json'), status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ our_service = client.delete_service(our_service.name, 'default')
+ assert_kind_of(RecursiveOpenStruct, our_service)
+
+ assert_requested(:delete,
+ 'http://localhost:8080/api/v1/namespaces/default/services/redis-service',
+ times: 1)
+ end
+
+ def test_get_service_no_ns
+ stub_core_api_list
+ # when not specifying namespace for entities which
+ # are not node or namespace, the request will fail
+ stub_request(:get, %r{/services/redis-slave})
+ .to_return(status: 404)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
+
+ exception = assert_raises(Kubeclient::HttpError) do
+ client.get_service('redis-slave')
+ end
+ assert_equal(404, exception.error_code)
+ end
+
+ def test_get_service
+ stub_core_api_list
+ stub_request(:get, %r{/namespaces/development/services/redis-slave})
+ .to_return(body: open_test_file('service.json'),
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
+ service = client.get_service('redis-slave', 'development')
+ assert_equal('redis-slave', service.metadata.name)
+
+ assert_requested(:get,
+ 'http://localhost:8080/api/v1/namespaces/development/services/redis-slave',
+ times: 1)
+ end
+
+ def test_update_service
+ service = Kubeclient::Resource.new
+ name = 'my_service'
+
+ service.metadata = {}
+ service.metadata.name = name
+ service.metadata.namespace = 'development'
+
+ stub_core_api_list
+ expected_url = "http://localhost:8080/api/v1/namespaces/development/services/#{name}"
+ stub_request(:put, expected_url)
+ .to_return(body: open_test_file('service_update.json'), status: 201)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ service = client.update_service(service)
+ assert_kind_of(RecursiveOpenStruct, service)
+
+ assert_requested(:put, expected_url, times: 1) do |req|
+ data = JSON.parse(req.body)
+ data['metadata']['name'] == name &&
+ data['metadata']['namespace'] == 'development'
+ end
+ end
+
+ def test_update_service_with_string_keys
+ service = Kubeclient::Resource.new
+ name = 'my_service'
+
+ service.metadata = {
+ 'name' => name,
+ 'namespace' => 'development'
+ }
+
+ stub_core_api_list
+ expected_url = "http://localhost:8080/api/v1/namespaces/development/services/#{name}"
+ stub_request(:put, expected_url)
+ .to_return(body: open_test_file('service_update.json'), status: 201)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ service = client.update_service(service)
+ assert_kind_of(RecursiveOpenStruct, service)
+
+ assert_requested(:put, expected_url, times: 1) do |req|
+ data = JSON.parse(req.body)
+ data['metadata']['name'] == name &&
+ data['metadata']['namespace'] == 'development'
+ end
+ end
+
+ def test_patch_service
+ service = Kubeclient::Resource.new
+ name = 'my_service'
+
+ service.metadata = {}
+ service.metadata.name = name
+ service.metadata.namespace = 'development'
+
+ stub_core_api_list
+ expected_url = "http://localhost:8080/api/v1/namespaces/development/services/#{name}"
+ stub_request(:patch, expected_url)
+ .to_return(body: open_test_file('service_patch.json'), status: 200)
+
+ patch = {
+ metadata: {
+ annotations: {
+ key: 'value'
+ }
+ }
+ }
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ service = client.patch_service(name, patch, 'development')
+ assert_kind_of(RecursiveOpenStruct, service)
+
+ assert_requested(:patch, expected_url, times: 1) do |req|
+ data = JSON.parse(req.body)
+ data['metadata']['annotations']['key'] == 'value'
+ end
+ end
+
+ def test_apply_service
+ service = Kubeclient::Resource.new
+ name = 'my_service'
+
+ service.metadata = {}
+ service.metadata.name = name
+ service.metadata.namespace = 'development'
+ service.metadata.annotations = {}
+ service.metadata.annotations['key'] = 'value'
+
+ stub_core_api_list
+ resource_name = "#{name}?fieldManager=myapp&force=true"
+ expected_url = "http://localhost:8080/api/v1/namespaces/development/services/#{resource_name}"
+ stub_request(:patch, expected_url)
+ .to_return(body: open_test_file('service_patch.json'), status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ service = client.apply_service(service, field_manager: 'myapp')
+ assert_kind_of(RecursiveOpenStruct, service)
+
+ assert_requested(:patch, expected_url, times: 1) do |req|
+ data = JSON.parse(req.body)
+ req.headers['Content-Type'] == 'application/apply-patch+yaml' &&
+ data['metadata']['annotations']['key'] == 'value'
+ end
+ end
+
+ def test_json_patch_service
+ service = Kubeclient::Resource.new
+ name = 'my-service'
+
+ service.metadata = {}
+ service.metadata.name = name
+ service.metadata.namespace = 'development'
+
+ stub_core_api_list
+ expected_url = "http://localhost:8080/api/v1/namespaces/development/services/#{name}"
+ stub_request(:patch, expected_url)
+ .to_return(body: open_test_file('service_json_patch.json'), status: 200)
+
+ patch = [
+ { 'op' => 'add', 'path' => '/spec/type', 'value' => 'LoadBalancer' }
+ ]
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ service = client.json_patch_service(name, patch, 'development')
+ assert_kind_of(RecursiveOpenStruct, service)
+
+ assert_requested(:patch, expected_url, times: 1) do |req|
+ data = JSON.parse(req.body)
+ req.headers['Content-Type'] == 'application/json-patch+json' &&
+ data == patch
+ end
+ end
+
+ def test_merge_patch_service
+ service = Kubeclient::Resource.new
+ name = 'my-service'
+
+ service.metadata = {}
+ service.metadata.name = name
+ service.metadata.namespace = 'development'
+
+ stub_core_api_list
+ expected_url = "http://localhost:8080/api/v1/namespaces/development/services/#{name}"
+ stub_request(:patch, expected_url)
+ .to_return(body: open_test_file('service_merge_patch.json'), status: 200)
+
+ patch = { spec: { type: 'NodePort' } }
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ service = client.merge_patch_service(name, patch, 'development')
+ assert_kind_of(RecursiveOpenStruct, service)
+
+ assert_requested(:patch, expected_url, times: 1) do |req|
+ data = JSON.parse(req.body)
+ req.headers['Content-Type'] == 'application/merge-patch+json' &&
+ data['spec']['type'] == 'NodePort'
+ end
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_service_account.rb b/vendor/gems/kubeclient/test/test_service_account.rb
new file mode 100644
index 00000000000..87a08a215bd
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_service_account.rb
@@ -0,0 +1,26 @@
+require_relative 'test_helper'
+
+# ServiceAccount tests
+class TestServiceAccount < MiniTest::Test
+ def test_get_from_json_v1
+ stub_core_api_list
+ stub_request(:get, %r{/serviceaccounts})
+ .to_return(body: open_test_file('service_account.json'),
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ account = client.get_service_account('default')
+
+ assert_instance_of(Kubeclient::Resource, account)
+ assert_equal('default', account.metadata.name)
+ assert_equal('default-token-6s23q', account.secrets[0].name)
+ assert_equal('default-dockercfg-62tf3', account.secrets[1].name)
+
+ assert_requested(:get,
+ 'http://localhost:8080/api/v1/serviceaccounts/default',
+ times: 1)
+ assert_requested(:get,
+ 'http://localhost:8080/api/v1',
+ times: 1)
+ end
+end
diff --git a/vendor/gems/kubeclient/test/test_watch.rb b/vendor/gems/kubeclient/test/test_watch.rb
new file mode 100644
index 00000000000..8d74008c851
--- /dev/null
+++ b/vendor/gems/kubeclient/test/test_watch.rb
@@ -0,0 +1,195 @@
+require_relative 'test_helper'
+
+# Watch entity tests
+class TestWatch < MiniTest::Test
+ def test_watch_pod_success
+ stub_core_api_list
+
+ expected = [
+ { 'type' => 'ADDED', 'resourceVersion' => '1389' },
+ { 'type' => 'MODIFIED', 'resourceVersion' => '1390' },
+ { 'type' => 'DELETED', 'resourceVersion' => '1398' }
+ ]
+
+ stub_request(:get, %r{/watch/pods})
+ .to_return(body: open_test_file('watch_stream.json'),
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+
+ client.watch_pods.to_enum.with_index do |notice, index|
+ assert_instance_of(Kubeclient::Resource, notice)
+ assert_equal(expected[index]['type'], notice.type)
+ assert_equal('Pod', notice.object.kind)
+ assert_equal('php', notice.object.metadata.name)
+ assert_equal(expected[index]['resourceVersion'],
+ notice.object.metadata.resourceVersion)
+ end
+ end
+
+ def test_watch_pod_block
+ stub_core_api_list
+ stub_request(:get, %r{/watch/pods})
+ .to_return(body: open_test_file('watch_stream.json'),
+ status: 200)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ yielded = []
+ client.watch_pods { |notice| yielded << notice.type }
+
+ assert_equal %w[ADDED MODIFIED DELETED], yielded
+ end
+
+ def test_watch_pod_raw
+ stub_core_api_list
+
+ stub_request(:get, %r{/watch/pods}).to_return(
+ body: open_test_file('watch_stream.json'),
+ status: 200
+ )
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+
+ got = nil
+ client.watch_pods(as: :raw).each { |notice| got = notice }
+ assert_match(/\A{"type":"DELETED"/, got)
+ end
+
+ def test_watch_pod_failure
+ stub_core_api_list
+ stub_request(:get, %r{/watch/pods}).to_return(status: 404)
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+ assert_raises(Kubeclient::HttpError) do
+ client.watch_pods.each do
+ end
+ end
+ end
+
+ def test_watch_pod_follow_redirect
+ stub_core_api_list
+
+ redirect = 'http://localhost:1234/api/v1/watch/pods'
+ stub_request(:get, %r{/watch/pods})
+ .to_return(status: 302, headers: { location: redirect })
+
+ stub_request(:get, redirect).to_return(
+ body: open_test_file('watch_stream.json'),
+ status: 200
+ )
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
+
+ got = nil
+ client.watch_pods.each { |notice| got = notice }
+ assert_equal('DELETED', got.type)
+ end
+
+ def test_watch_pod_max_redirect
+ stub_core_api_list
+
+ redirect = 'http://localhost:1234/api/v1/watcher/pods'
+ stub_request(:get, %r{/watch/pods})
+ .to_return(status: 302, headers: { location: redirect })
+
+ stub_request(:get, redirect).to_return(
+ body: open_test_file('watch_stream.json'),
+ status: 200
+ )
+
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1', http_max_redirects: 0)
+
+ assert_raises(Kubeclient::HttpError) do
+ client.watch_pods.each do
+ end
+ end
+ end
+
+ # Ensure that WatchStream respects a format that's not JSON
+ def test_watch_stream_text
+ url = 'http://www.example.com/foobar'
+ expected_lines = open_test_file('pod_log.txt').read.split("\n")
+
+ stub_request(:get, url)
+ .to_return(body: open_test_file('pod_log.txt'),
+ status: 200)
+
+ stream = Kubeclient::Common::WatchStream.new(URI.parse(url), {}, formatter: ->(v) { v })
+ stream.to_enum.with_index do |line, index|
+ assert_instance_of(String, line)
+ assert_equal(expected_lines[index], line)
+ end
+ end
+
+ def test_watch_with_resource_version
+ api_host = 'http://localhost:8080/api'
+ version = '1995'
+ stub_core_api_list
+ stub_request(:get, %r{.*\/watch/events})
+ .to_return(body: open_test_file('watch_stream.json'),
+ status: 200)
+
+ client = Kubeclient::Client.new(api_host, 'v1')
+ results = client.watch_events(version).to_enum
+
+ assert_equal(3, results.count)
+ assert_requested(:get,
+ "#{api_host}/v1/watch/events?resourceVersion=#{version}",
+ times: 1)
+ end
+
+ def test_watch_with_label_selector
+ api_host = 'http://localhost:8080/api'
+ selector = 'name=redis-master'
+
+ stub_core_api_list
+ stub_request(:get, %r{.*\/watch/events})
+ .to_return(body: open_test_file('watch_stream.json'),
+ status: 200)
+
+ client = Kubeclient::Client.new(api_host, 'v1')
+ results = client.watch_events(label_selector: selector).to_enum
+
+ assert_equal(3, results.count)
+ assert_requested(:get,
+ "#{api_host}/v1/watch/events?labelSelector=#{selector}",
+ times: 1)
+ end
+
+ def test_watch_with_field_selector
+ api_host = 'http://localhost:8080/api'
+ selector = 'involvedObject.kind=Pod'
+
+ stub_core_api_list
+ stub_request(:get, %r{.*\/watch/events})
+ .to_return(body: open_test_file('watch_stream.json'),
+ status: 200)
+
+ client = Kubeclient::Client.new(api_host, 'v1')
+ results = client.watch_events(field_selector: selector).to_enum
+
+ assert_equal(3, results.count)
+ assert_requested(:get,
+ "#{api_host}/v1/watch/events?fieldSelector=#{selector}",
+ times: 1)
+ end
+
+ def test_watch_with_finish_and_ebadf
+ api_host = 'http://localhost:8080/api'
+
+ stub_core_api_list
+ stub_request(:get, %r{.*\/watch/events})
+ .to_return(body: open_test_file('watch_stream.json'), status: 200)
+
+ client = Kubeclient::Client.new(api_host, 'v1')
+ watcher = client.watch_events
+
+ # explodes when StandardError is not caught
+ watcher.each do
+ watcher.finish
+ raise StandardError
+ end
+
+ assert_requested(:get, "#{api_host}/v1/watch/events", times: 1)
+ end
+end
diff --git a/vendor/gems/kubeclient/test/txt/pod_log.txt b/vendor/gems/kubeclient/test/txt/pod_log.txt
new file mode 100644
index 00000000000..9f9a75157aa
--- /dev/null
+++ b/vendor/gems/kubeclient/test/txt/pod_log.txt
@@ -0,0 +1,6 @@
+Initializing server...
+...loaded configuration
+...updated settings
+...discovered local servers
+...frobinated disks
+Complete!
diff --git a/vendor/gems/kubeclient/test/valid_token_file b/vendor/gems/kubeclient/test/valid_token_file
new file mode 100644
index 00000000000..df2c2eb6572
--- /dev/null
+++ b/vendor/gems/kubeclient/test/valid_token_file
@@ -0,0 +1 @@
+valid_token
diff --git a/vendor/project_templates/bridgetown.tar.gz b/vendor/project_templates/bridgetown.tar.gz
new file mode 100644
index 00000000000..1fb89694d0f
--- /dev/null
+++ b/vendor/project_templates/bridgetown.tar.gz
Binary files differ
diff --git a/vendor/project_templates/dotnetcore.tar.gz b/vendor/project_templates/dotnetcore.tar.gz
index 7e4406dbb23..63b86320cb1 100644
--- a/vendor/project_templates/dotnetcore.tar.gz
+++ b/vendor/project_templates/dotnetcore.tar.gz
Binary files differ
diff --git a/vendor/project_templates/middleman.tar.gz b/vendor/project_templates/middleman.tar.gz
index 21dd38f3720..db09a84ab75 100644
--- a/vendor/project_templates/middleman.tar.gz
+++ b/vendor/project_templates/middleman.tar.gz
Binary files differ
diff --git a/vendor/project_templates/rails.tar.gz b/vendor/project_templates/rails.tar.gz
index 17706a67dd0..0d82bf639de 100644
--- a/vendor/project_templates/rails.tar.gz
+++ b/vendor/project_templates/rails.tar.gz
Binary files differ
diff --git a/vendor/project_templates/typo3_distribution.tar.gz b/vendor/project_templates/typo3_distribution.tar.gz
new file mode 100644
index 00000000000..13c158d1462
--- /dev/null
+++ b/vendor/project_templates/typo3_distribution.tar.gz
Binary files differ
diff --git a/workhorse/gitaly_integration_test.go b/workhorse/gitaly_integration_test.go
index a2826c3edc4..ed44aaddbc3 100644
--- a/workhorse/gitaly_integration_test.go
+++ b/workhorse/gitaly_integration_test.go
@@ -58,7 +58,6 @@ func ensureGitalyRepository(t *testing.T, apiResponse *api.Response) error {
ctx, namespace, err := gitaly.NewNamespaceClient(
context.Background(),
apiResponse.GitalyServer,
- gitaly.WithFeatures(apiResponse.GitalyServer.Features),
)
if err != nil {
diff --git a/workhorse/gitaly_test.go b/workhorse/gitaly_test.go
index 234a11e5dc9..2d7f727003f 100644
--- a/workhorse/gitaly_test.go
+++ b/workhorse/gitaly_test.go
@@ -78,7 +78,7 @@ func TestGetInfoRefsProxiedToGitalySuccessfully(t *testing.T) {
for k, v := range badMetadata {
features[k] = v
}
- apiResponse.GitalyServer.Features = features
+ apiResponse.GitalyServer.CallMetadata = features
testCases := []struct {
showAllRefs bool
diff --git a/workhorse/go.mod b/workhorse/go.mod
index 51adab831c2..80c017ad1cb 100644
--- a/workhorse/go.mod
+++ b/workhorse/go.mod
@@ -6,18 +6,18 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1
github.com/BurntSushi/toml v1.2.1
github.com/FZambia/sentinel v1.1.1
- github.com/alecthomas/chroma/v2 v2.3.0
- github.com/aws/aws-sdk-go v1.44.136
+ github.com/alecthomas/chroma/v2 v2.4.0
+ github.com/aws/aws-sdk-go v1.44.157
github.com/disintegration/imaging v1.6.2
github.com/getsentry/raven-go v0.2.0
- github.com/golang-jwt/jwt/v4 v4.4.2
+ github.com/golang-jwt/jwt/v4 v4.4.3
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f
github.com/golang/protobuf v1.5.2
github.com/gomodule/redigo v2.0.0+incompatible
github.com/gorilla/websocket v1.5.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
- github.com/johannesboyne/gofakes3 v0.0.0-20221110173912-32fb85c5aed6
+ github.com/johannesboyne/gofakes3 v0.0.0-20221128113635-c2f5cc6b5294
github.com/jpillora/backoff v1.0.0
github.com/mitchellh/copystructure v1.2.0
github.com/prometheus/client_golang v1.14.0
@@ -26,7 +26,7 @@ require (
github.com/sirupsen/logrus v1.9.0
github.com/smartystreets/goconvey v1.7.2
github.com/stretchr/testify v1.8.1
- gitlab.com/gitlab-org/gitaly/v15 v15.5.1
+ gitlab.com/gitlab-org/gitaly/v15 v15.6.2
gitlab.com/gitlab-org/golang-archive-zip v0.1.1
gitlab.com/gitlab-org/labkit v1.16.1
gocloud.dev v0.27.0
@@ -35,7 +35,7 @@ require (
golang.org/x/net v0.1.0
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c
golang.org/x/tools v0.1.12
- google.golang.org/grpc v1.50.1
+ google.golang.org/grpc v1.51.0
google.golang.org/protobuf v1.28.1
honnef.co/go/tools v0.3.3
)
@@ -69,7 +69,7 @@ require (
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/go-ole/go-ole v1.2.4 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
- github.com/golang-jwt/jwt v3.2.1+incompatible // indirect
+ github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/pprof v0.0.0-20220608213341-c488b8fa1db3 // indirect
@@ -107,13 +107,13 @@ require (
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
- golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
+ golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // indirect
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
- golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
+ golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.1.0 // indirect
golang.org/x/text v0.4.0 // indirect
- golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
+ golang.org/x/time v0.2.0 // indirect
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
google.golang.org/api v0.91.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
diff --git a/workhorse/go.sum b/workhorse/go.sum
index bf0c7df390d..5e095f5b417 100644
--- a/workhorse/go.sum
+++ b/workhorse/go.sum
@@ -194,8 +194,9 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUW
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
-github.com/alecthomas/chroma/v2 v2.3.0 h1:83xfxrnjv8eK+Cf8qZDzNo3PPF9IbTWHs7z28GY6D0U=
-github.com/alecthomas/chroma/v2 v2.3.0/go.mod h1:mZxeWZlxP2Dy+/8cBob2PYd8O2DwNAzave5AY7A2eQw=
+github.com/alecthomas/assert/v2 v2.2.0 h1:f6L/b7KE2bfA+9O4FL3CM/xJccDEwPVYd5fALBiuwvw=
+github.com/alecthomas/chroma/v2 v2.4.0 h1:Loe2ZjT5x3q1bcWwemqyqEi8p11/IV/ncFCeLYDpWC4=
+github.com/alecthomas/chroma/v2 v2.4.0/go.mod h1:6kHzqF5O6FUSJzBXW7fXELjb+e+7OXW4UpoPqMO7IBQ=
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -227,8 +228,8 @@ github.com/aws/aws-sdk-go v1.43.11/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4
github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go v1.44.45/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go v1.44.68/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
-github.com/aws/aws-sdk-go v1.44.136 h1:J1KJJssa8pjU8jETYUxwRS37KTcxjACfKd9GK8t+5ZU=
-github.com/aws/aws-sdk-go v1.44.136/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
+github.com/aws/aws-sdk-go v1.44.157 h1:JVBPpEWC8+yA7CbfAuTl/ZFFlHS3yoqWFqxFyTCISwg=
+github.com/aws/aws-sdk-go v1.44.157/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aws/aws-sdk-go-v2 v1.16.8 h1:gOe9UPR98XSf7oEJCcojYg+N2/jCRm4DdeIsP85pIyQ=
github.com/aws/aws-sdk-go-v2 v1.16.8/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw=
@@ -682,12 +683,14 @@ github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
+github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
+github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
-github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
+github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f h1:16RtHeWGkJMc80Etb8RPCcKevXGldr57+LOyZt8zOlg=
@@ -909,6 +912,7 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/hetznercloud/hcloud-go v1.33.1/go.mod h1:XX/TQub3ge0yWR2yHWmnDVIrB+MQbda1pHxkUmDlUME=
github.com/hetznercloud/hcloud-go v1.35.0/go.mod h1:mepQwR6va27S3UQthaEPGS86jtzSY9xWL1e9dyxXpgA=
+github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
@@ -975,8 +979,8 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
-github.com/johannesboyne/gofakes3 v0.0.0-20221110173912-32fb85c5aed6 h1:eQGUsj2LcsLzfrHY1noKDSU7h+c9/rw9pQPwbQ9g1jQ=
-github.com/johannesboyne/gofakes3 v0.0.0-20221110173912-32fb85c5aed6/go.mod h1:LIAXxPvcUXwOcTIj9LSNSUpE9/eMHalTWxsP/kmWxQI=
+github.com/johannesboyne/gofakes3 v0.0.0-20221128113635-c2f5cc6b5294 h1:AJISYN7tPo3lGqwYmEYQdlftcQz48i8LNk/BRUKCTig=
+github.com/johannesboyne/gofakes3 v0.0.0-20221128113635-c2f5cc6b5294/go.mod h1:LIAXxPvcUXwOcTIj9LSNSUpE9/eMHalTWxsP/kmWxQI=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
@@ -1482,8 +1486,8 @@ github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
-gitlab.com/gitlab-org/gitaly/v15 v15.5.1 h1:EbkAYAeTLllJzX3N3Sy3ZcmKtBzI5OovT5c5MWI16Bo=
-gitlab.com/gitlab-org/gitaly/v15 v15.5.1/go.mod h1:G5q5H6OYMSEDnKXsQoYTzI+ysCTfM4Of2z0v6xeHtRY=
+gitlab.com/gitlab-org/gitaly/v15 v15.6.2 h1:ivbMoXWgkDSJebuIFtPYGAIQ9/2P5ShxJoHt0cflwfo=
+gitlab.com/gitlab-org/gitaly/v15 v15.6.2/go.mod h1:RKa+3ADKfTonDb1pe8AtppdNHNeOM+ChtMmB7T0QWhY=
gitlab.com/gitlab-org/golang-archive-zip v0.1.1 h1:35k9giivbxwF03+8A05Cm8YoxoakU8FBCj5gysjCTCE=
gitlab.com/gitlab-org/golang-archive-zip v0.1.1/go.mod h1:ZDtqpWPGPB9qBuZnZDrKQjIdJtkN7ZAoVwhT6H2o2kE=
gitlab.com/gitlab-org/labkit v1.16.1 h1:J+HmNVR5bvPfrv9/fgKICFis2nmEugRXHMeRPvsVZUg=
@@ -1616,8 +1620,9 @@ golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A=
+golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1785,8 +1790,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1960,8 +1966,9 @@ golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ=
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE=
+golang.org/x/time v0.2.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -2260,8 +2267,8 @@ google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY=
-google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
+google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
+google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
diff --git a/workhorse/internal/api/api.go b/workhorse/internal/api/api.go
index 6a6a51b27bb..1758bb5a6a8 100644
--- a/workhorse/internal/api/api.go
+++ b/workhorse/internal/api/api.go
@@ -18,6 +18,7 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/config"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/log"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/secret"
)
@@ -128,6 +129,7 @@ type Response struct {
// GL_REPOSITORY is an environment variable used by gitlab-shell hooks during
// 'git push' and 'git pull'
GL_REPOSITORY string
+
// GitConfigOptions holds the custom options that we want to pass to the git command
GitConfigOptions []string
// StoreLFSPath is provided by the GitLab Rails application to mark where the tmp file should be placed.
@@ -162,9 +164,9 @@ type Response struct {
}
type GitalyServer struct {
- Address string `json:"address"`
- Token string `json:"token"`
- Features map[string]string `json:"features"`
+ Address string `json:"address"`
+ Token string `json:"token"`
+ CallMetadata map[string]string `json:"call_metadata"`
}
// singleJoiningSlash is taken from reverseproxy.go:singleJoiningSlash
@@ -225,7 +227,7 @@ func (api *API) newRequest(r *http.Request, suffix string) (*http.Request, error
authReq := &http.Request{
Method: r.Method,
URL: rebaseUrl(r.URL, api.URL, suffix),
- Header: helper.HeaderClone(r.Header),
+ Header: r.Header.Clone(),
}
authReq = authReq.WithContext(r.Context())
@@ -306,7 +308,7 @@ func (api *API) PreAuthorizeFixedPath(r *http.Request, method string, path strin
if err != nil {
return nil, fmt.Errorf("construct auth request: %w", err)
}
- authReq.Header = helper.HeaderClone(r.Header)
+ authReq.Header = r.Header.Clone()
authReq.URL.RawQuery = r.URL.RawQuery
failureResponse, apiResponse, err := api.PreAuthorize(path, authReq)
@@ -334,7 +336,7 @@ func (api *API) PreAuthorizeHandler(next HandleFunc, suffix string) http.Handler
}
if err != nil {
- helper.Fail500(w, r, err)
+ fail.Request(w, r, err)
return
}
@@ -360,7 +362,7 @@ func (api *API) doRequestWithoutRedirects(authReq *http.Request) (*http.Response
}
// removeConnectionHeaders removes hop-by-hop headers listed in the "Connection" header of h.
-// See https://tools.ietf.org/html/rfc7230#section-6.1
+// See https://www.rfc-editor.org/rfc/rfc7230#section-6.1
func removeConnectionHeaders(h http.Header) {
for _, f := range h["Connection"] {
for _, sf := range strings.Split(f, ",") {
@@ -389,7 +391,7 @@ func passResponseBack(httpResponse *http.Response, w http.ResponseWriter, r *htt
// the entire response body in memory before sending it on.
responseBody, err := bufferResponse(httpResponse.Body)
if err != nil {
- helper.Fail500(w, r, err)
+ fail.Request(w, r, err)
return
}
httpResponse.Body.Close() // Free up the Puma thread
diff --git a/workhorse/internal/api/block.go b/workhorse/internal/api/block.go
index 43763fc2b13..aac43f8cf77 100644
--- a/workhorse/internal/api/block.go
+++ b/workhorse/internal/api/block.go
@@ -5,6 +5,7 @@ import (
"net/http"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
)
// Prevent internal API responses intended for gitlab-workhorse from
@@ -48,7 +49,7 @@ func (b *blocker) WriteHeader(status int) {
b.status = 500
b.Header().Del("Content-Length")
b.hijacked = true
- helper.Fail500(b.rw, b.r, fmt.Errorf("api.blocker: forbidden content-type: %q", ResponseContentType))
+ fail.Request(b.rw, b.r, fmt.Errorf("api.blocker: forbidden content-type: %q", ResponseContentType))
return
}
diff --git a/workhorse/internal/api/block_test.go b/workhorse/internal/api/block_test.go
index 0beb401d2f5..c1ffe93dfb8 100644
--- a/workhorse/internal/api/block_test.go
+++ b/workhorse/internal/api/block_test.go
@@ -20,7 +20,7 @@ func TestBlocker(t *testing.T) {
{
desc: "blocked",
contentType: ResponseContentType,
- out: "Internal server error\n",
+ out: "Internal Server Error\n",
},
{
desc: "pass",
diff --git a/workhorse/internal/api/channel_settings.go b/workhorse/internal/api/channel_settings.go
index 91798334a03..ed03b04a69b 100644
--- a/workhorse/internal/api/channel_settings.go
+++ b/workhorse/internal/api/channel_settings.go
@@ -8,8 +8,6 @@ import (
"net/url"
"github.com/gorilla/websocket"
-
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
)
type ChannelSettings struct {
@@ -53,7 +51,10 @@ func (t *ChannelSettings) Dialer() *websocket.Dialer {
func (t *ChannelSettings) Clone() *ChannelSettings {
// Doesn't clone the strings, but that's OK as strings are immutable in go
cloned := *t
- cloned.Header = helper.HeaderClone(t.Header)
+ cloned.Header = t.Header.Clone()
+ if cloned.Header == nil {
+ cloned.Header = make(http.Header)
+ }
return &cloned
}
diff --git a/workhorse/internal/artifacts/entry.go b/workhorse/internal/artifacts/entry.go
index d5b3dfb672c..e2eef174989 100644
--- a/workhorse/internal/artifacts/entry.go
+++ b/workhorse/internal/artifacts/entry.go
@@ -16,7 +16,8 @@ import (
"gitlab.com/gitlab-org/labkit/log"
"gitlab.com/gitlab-org/labkit/mask"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/command"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/senddata"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/zipartifacts"
)
@@ -30,7 +31,7 @@ var SendEntry = &entry{"artifacts-entry:"}
func (e *entry) Inject(w http.ResponseWriter, r *http.Request, sendData string) {
var params entryParams
if err := e.Unpack(&params, sendData); err != nil {
- helper.Fail500(w, r, fmt.Errorf("SendEntry: unpack sendData: %v", err))
+ fail.Request(w, r, fmt.Errorf("SendEntry: unpack sendData: %v", err))
return
}
@@ -41,7 +42,7 @@ func (e *entry) Inject(w http.ResponseWriter, r *http.Request, sendData string)
}).Print("SendEntry: sending")
if params.Archive == "" || params.Entry == "" {
- helper.Fail500(w, r, fmt.Errorf("SendEntry: Archive or Entry is empty"))
+ fail.Request(w, r, fmt.Errorf("SendEntry: Archive or Entry is empty"))
return
}
@@ -50,7 +51,7 @@ func (e *entry) Inject(w http.ResponseWriter, r *http.Request, sendData string)
if os.IsNotExist(err) {
http.NotFound(w, r)
} else if err != nil {
- helper.Fail500(w, r, fmt.Errorf("SendEntry: %v", err))
+ fail.Request(w, r, fmt.Errorf("SendEntry: %v", err))
}
}
@@ -83,7 +84,7 @@ func unpackFileFromZip(ctx context.Context, archivePath, encodedFilename string,
if err := catFile.Start(); err != nil {
return fmt.Errorf("start %v: %v", catFile.Args, err)
}
- defer helper.CleanUpProcessGroup(catFile)
+ defer command.KillProcessGroup(catFile)
basename := filepath.Base(fileName)
reader := bufio.NewReader(stdout)
@@ -114,7 +115,7 @@ func waitCatFile(cmd *exec.Cmd) error {
return nil
}
- st, ok := helper.ExitStatus(err)
+ st, ok := command.ExitStatus(err)
if ok && (st == zipartifacts.CodeArchiveNotFound || st == zipartifacts.CodeEntryNotFound) {
return os.ErrNotExist
diff --git a/workhorse/internal/builds/register.go b/workhorse/internal/builds/register.go
index f28ad75e1d8..0a2fe47ed7e 100644
--- a/workhorse/internal/builds/register.go
+++ b/workhorse/internal/builds/register.go
@@ -1,8 +1,10 @@
package builds
import (
+ "bytes"
"encoding/json"
"errors"
+ "io"
"net/http"
"time"
@@ -10,6 +12,7 @@ import (
"github.com/prometheus/client_golang/prometheus/promauto"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/redis"
)
@@ -63,11 +66,18 @@ func readRunnerBody(w http.ResponseWriter, r *http.Request) ([]byte, error) {
registerHandlerOpenAtReading.Inc()
defer registerHandlerOpenAtReading.Dec()
- return helper.ReadRequestBody(w, r, maxRegisterBodySize)
+ return readRequestBody(w, r, maxRegisterBodySize)
+}
+
+func readRequestBody(w http.ResponseWriter, r *http.Request, maxBodySize int64) ([]byte, error) {
+ limitedBody := http.MaxBytesReader(w, r.Body, maxBodySize)
+ defer limitedBody.Close()
+
+ return io.ReadAll(limitedBody)
}
func readRunnerRequest(r *http.Request, body []byte) (*runnerRequest, error) {
- if !helper.IsApplicationJson(r) {
+ if !isApplicationJson(r) {
return nil, errors.New("invalid content-type received")
}
@@ -80,6 +90,11 @@ func readRunnerRequest(r *http.Request, body []byte) (*runnerRequest, error) {
return &runnerRequest, nil
}
+func isApplicationJson(r *http.Request) bool {
+ contentType := r.Header.Get("Content-Type")
+ return helper.IsContentType("application/json", contentType)
+}
+
func proxyRegisterRequest(h http.Handler, w http.ResponseWriter, r *http.Request) {
registerHandlerOpenAtProxying.Inc()
defer registerHandlerOpenAtProxying.Dec()
@@ -105,11 +120,12 @@ func RegisterHandler(h http.Handler, watchHandler WatchKeyHandler, pollingDurati
requestBody, err := readRunnerBody(w, r)
if err != nil {
registerHandlerBodyReadErrors.Inc()
- helper.RequestEntityTooLarge(w, r, &largeBodyError{err})
+ fail.Request(w, r, &largeBodyError{err},
+ fail.WithStatus(http.StatusRequestEntityTooLarge))
return
}
- newRequest := helper.CloneRequestWithNewBody(r, requestBody)
+ newRequest := cloneRequestWithNewBody(r, requestBody)
runnerRequest, err := readRunnerRequest(r, requestBody)
if err != nil {
@@ -161,3 +177,10 @@ func RegisterHandler(h http.Handler, watchHandler WatchKeyHandler, pollingDurati
}
})
}
+
+func cloneRequestWithNewBody(r *http.Request, body []byte) *http.Request {
+ newReq := r.Clone(r.Context())
+ newReq.Body = io.NopCloser(bytes.NewReader(body))
+ newReq.ContentLength = int64(len(body))
+ return newReq
+}
diff --git a/workhorse/internal/builds/register_test.go b/workhorse/internal/builds/register_test.go
index 3c975f61003..d5cbebd500b 100644
--- a/workhorse/internal/builds/register_test.go
+++ b/workhorse/internal/builds/register_test.go
@@ -106,3 +106,50 @@ func TestRegisterHandlerWatcherNoChange(t *testing.T) {
expectWatcherToBeExecuted(t, redis.WatchKeyStatusNoChange, nil,
http.StatusNoContent)
}
+
+func TestReadRequestBody(t *testing.T) {
+ data := []byte("123456")
+ rw := httptest.NewRecorder()
+ req, _ := http.NewRequest("POST", "/test", bytes.NewBuffer(data))
+
+ result, err := readRequestBody(rw, req, 1000)
+ require.NoError(t, err)
+ require.Equal(t, data, result)
+}
+
+func TestReadRequestBodyLimit(t *testing.T) {
+ data := []byte("123456")
+ rw := httptest.NewRecorder()
+ req, _ := http.NewRequest("POST", "/test", bytes.NewBuffer(data))
+
+ _, err := readRequestBody(rw, req, 2)
+ require.Error(t, err)
+}
+
+func TestApplicationJson(t *testing.T) {
+ req, _ := http.NewRequest("POST", "/test", nil)
+ req.Header.Set("Content-Type", "application/json")
+
+ require.True(t, isApplicationJson(req), "expected to match 'application/json' as 'application/json'")
+
+ req.Header.Set("Content-Type", "application/json; charset=utf-8")
+ require.True(t, isApplicationJson(req), "expected to match 'application/json; charset=utf-8' as 'application/json'")
+
+ req.Header.Set("Content-Type", "text/plain")
+ require.False(t, isApplicationJson(req), "expected not to match 'text/plain' as 'application/json'")
+}
+
+func TestCloneRequestWithBody(t *testing.T) {
+ input := []byte("test")
+ newInput := []byte("new body")
+ req, _ := http.NewRequest("POST", "/test", bytes.NewBuffer(input))
+ newReq := cloneRequestWithNewBody(req, newInput)
+
+ require.NotEqual(t, req, newReq)
+ require.NotEqual(t, req.Body, newReq.Body)
+ require.NotEqual(t, len(newInput), newReq.ContentLength)
+
+ var buffer bytes.Buffer
+ io.Copy(&buffer, newReq.Body)
+ require.Equal(t, newInput, buffer.Bytes())
+}
diff --git a/workhorse/internal/channel/channel.go b/workhorse/internal/channel/channel.go
index e740015d54a..f8228620a83 100644
--- a/workhorse/internal/channel/channel.go
+++ b/workhorse/internal/channel/channel.go
@@ -2,7 +2,9 @@ package channel
import (
"fmt"
+ "net"
"net/http"
+ "strings"
"time"
"github.com/gorilla/websocket"
@@ -10,7 +12,7 @@ import (
"gitlab.com/gitlab-org/labkit/log"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/api"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
)
var (
@@ -24,7 +26,7 @@ var (
func Handler(myAPI *api.API) http.Handler {
return myAPI.PreAuthorizeHandler(func(w http.ResponseWriter, r *http.Request, a *api.Response) {
if err := a.Channel.Validate(); err != nil {
- helper.Fail500(w, r, err)
+ fail.Request(w, r, err)
return
}
@@ -45,7 +47,7 @@ func Handler(myAPI *api.API) http.Handler {
func ProxyChannel(w http.ResponseWriter, r *http.Request, settings *api.ChannelSettings, proxy *Proxy) {
server, err := connectToServer(settings, r)
if err != nil {
- helper.Fail500(w, r, err)
+ fail.Request(w, r, err)
log.ContextLogger(r.Context()).WithError(err).Print("Channel: connecting to server failed")
return
}
@@ -109,7 +111,7 @@ func pingLoop(conn Connection) {
func connectToServer(settings *api.ChannelSettings, r *http.Request) (Connection, error) {
settings = settings.Clone()
- helper.SetForwardedFor(&settings.Header, r)
+ setForwardedFor(&settings.Header, r)
conn, _, err := settings.Dial()
if err != nil {
@@ -130,3 +132,19 @@ func closeAfterMaxTime(proxy *Proxy, maxSessionTime int) {
maxSessionTime,
)
}
+
+func setForwardedFor(newHeaders *http.Header, originalRequest *http.Request) {
+ if clientIP, _, err := net.SplitHostPort(originalRequest.RemoteAddr); err == nil {
+ var header string
+
+ // If we aren't the first proxy retain prior
+ // X-Forwarded-For information as a comma+space
+ // separated list and fold multiple headers into one.
+ if prior, ok := originalRequest.Header["X-Forwarded-For"]; ok {
+ header = strings.Join(prior, ", ") + ", " + clientIP
+ } else {
+ header = clientIP
+ }
+ newHeaders.Set("X-Forwarded-For", header)
+ }
+}
diff --git a/workhorse/internal/channel/channel_test.go b/workhorse/internal/channel/channel_test.go
new file mode 100644
index 00000000000..fade6e42c27
--- /dev/null
+++ b/workhorse/internal/channel/channel_test.go
@@ -0,0 +1,49 @@
+package channel
+
+import (
+ "net/http"
+ "testing"
+)
+
+func TestSetForwardedForGeneratesHeader(t *testing.T) {
+ testCases := []struct {
+ remoteAddr string
+ previousForwardedFor []string
+ expected string
+ }{
+ {
+ "8.8.8.8:3000",
+ nil,
+ "8.8.8.8",
+ },
+ {
+ "8.8.8.8:3000",
+ []string{"138.124.33.63, 151.146.211.237"},
+ "138.124.33.63, 151.146.211.237, 8.8.8.8",
+ },
+ {
+ "8.8.8.8:3000",
+ []string{"8.154.76.107", "115.206.118.179"},
+ "8.154.76.107, 115.206.118.179, 8.8.8.8",
+ },
+ }
+ for _, tc := range testCases {
+ headers := http.Header{}
+ originalRequest := http.Request{
+ RemoteAddr: tc.remoteAddr,
+ }
+
+ if tc.previousForwardedFor != nil {
+ originalRequest.Header = http.Header{
+ "X-Forwarded-For": tc.previousForwardedFor,
+ }
+ }
+
+ setForwardedFor(&headers, &originalRequest)
+
+ result := headers.Get("X-Forwarded-For")
+ if result != tc.expected {
+ t.Fatalf("Expected %v, got %v", tc.expected, result)
+ }
+ }
+}
diff --git a/workhorse/internal/dependencyproxy/dependencyproxy.go b/workhorse/internal/dependencyproxy/dependencyproxy.go
index 6651b5aee84..e170b001806 100644
--- a/workhorse/internal/dependencyproxy/dependencyproxy.go
+++ b/workhorse/internal/dependencyproxy/dependencyproxy.go
@@ -8,7 +8,7 @@ import (
"gitlab.com/gitlab-org/labkit/log"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/senddata"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/transport"
)
@@ -57,7 +57,7 @@ func (p *Injector) SetUploadHandler(uploadHandler http.Handler) {
func (p *Injector) Inject(w http.ResponseWriter, r *http.Request, sendData string) {
dependencyResponse, err := p.fetchUrl(r.Context(), sendData)
if err != nil {
- helper.Fail500(w, r, err)
+ fail.Request(w, r, err)
return
}
defer dependencyResponse.Body.Close()
@@ -72,9 +72,9 @@ func (p *Injector) Inject(w http.ResponseWriter, r *http.Request, sendData strin
teeReader := io.TeeReader(dependencyResponse.Body, w)
saveFileRequest, err := http.NewRequestWithContext(r.Context(), "POST", r.URL.String()+"/upload", teeReader)
if err != nil {
- helper.Fail500(w, r, fmt.Errorf("dependency proxy: failed to create request: %w", err))
+ fail.Request(w, r, fmt.Errorf("dependency proxy: failed to create request: %w", err))
}
- saveFileRequest.Header = helper.HeaderClone(r.Header)
+ saveFileRequest.Header = r.Header.Clone()
// forward headers from dependencyResponse to rails and client
for key, values := range dependencyResponse.Header {
@@ -96,7 +96,7 @@ func (p *Injector) Inject(w http.ResponseWriter, r *http.Request, sendData strin
if nrw.status != http.StatusOK {
fields := log.Fields{"code": nrw.status}
- helper.Fail500WithFields(nrw, r, fmt.Errorf("dependency proxy: failed to upload file"), fields)
+ fail.Request(nrw, r, fmt.Errorf("dependency proxy: failed to upload file"), fail.WithFields(fields))
}
}
diff --git a/workhorse/internal/dependencyproxy/dependencyproxy_test.go b/workhorse/internal/dependencyproxy/dependencyproxy_test.go
index 6056433f3b1..d893ddc500f 100644
--- a/workhorse/internal/dependencyproxy/dependencyproxy_test.go
+++ b/workhorse/internal/dependencyproxy/dependencyproxy_test.go
@@ -153,14 +153,14 @@ func TestIncorrectSendData(t *testing.T) {
response := makeRequest(NewInjector(), "")
require.Equal(t, 500, response.Code)
- require.Equal(t, "Internal server error\n", response.Body.String())
+ require.Equal(t, "Internal Server Error\n", response.Body.String())
}
func TestIncorrectSendDataUrl(t *testing.T) {
response := makeRequest(NewInjector(), `{"Token": "token", "Url": "url"}`)
require.Equal(t, 500, response.Code)
- require.Equal(t, "Internal server error\n", response.Body.String())
+ require.Equal(t, "Internal Server Error\n", response.Body.String())
}
func TestFailedOriginServer(t *testing.T) {
diff --git a/workhorse/internal/git/archive.go b/workhorse/internal/git/archive.go
index 4c7b519310f..3361a8bed44 100644
--- a/workhorse/internal/git/archive.go
+++ b/workhorse/internal/git/archive.go
@@ -23,7 +23,7 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/api"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/gitaly"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/log"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/senddata"
)
@@ -53,14 +53,14 @@ var (
func (a *archive) Inject(w http.ResponseWriter, r *http.Request, sendData string) {
var params archiveParams
if err := a.Unpack(&params, sendData); err != nil {
- helper.Fail500(w, r, fmt.Errorf("SendArchive: unpack sendData: %v", err))
+ fail.Request(w, r, fmt.Errorf("SendArchive: unpack sendData: %v", err))
return
}
urlPath := r.URL.Path
format, ok := parseBasename(filepath.Base(urlPath))
if !ok {
- helper.Fail500(w, r, fmt.Errorf("SendArchive: invalid format: %s", urlPath))
+ fail.Request(w, r, fmt.Errorf("SendArchive: invalid format: %s", urlPath))
return
}
@@ -93,7 +93,7 @@ func (a *archive) Inject(w http.ResponseWriter, r *http.Request, sendData string
// to finalize the cached archive.
tempFile, err = prepareArchiveTempfile(path.Dir(params.ArchivePath), archiveFilename)
if err != nil {
- helper.Fail500(w, r, fmt.Errorf("SendArchive: create tempfile: %v", err))
+ fail.Request(w, r, fmt.Errorf("SendArchive: create tempfile: %v", err))
return
}
defer tempFile.Close()
@@ -104,7 +104,7 @@ func (a *archive) Inject(w http.ResponseWriter, r *http.Request, sendData string
archiveReader, err = handleArchiveWithGitaly(r, &params, format)
if err != nil {
- helper.Fail500(w, r, fmt.Errorf("operations.GetArchive: %v", err))
+ fail.Request(w, r, fmt.Errorf("operations.GetArchive: %v", err))
return
}
@@ -132,11 +132,7 @@ func (a *archive) Inject(w http.ResponseWriter, r *http.Request, sendData string
func handleArchiveWithGitaly(r *http.Request, params *archiveParams, format gitalypb.GetArchiveRequest_Format) (io.Reader, error) {
var request *gitalypb.GetArchiveRequest
- ctx, c, err := gitaly.NewRepositoryClient(
- r.Context(),
- params.GitalyServer,
- gitaly.WithFeatures(params.GitalyServer.Features),
- )
+ ctx, c, err := gitaly.NewRepositoryClient(r.Context(), params.GitalyServer)
if err != nil {
return nil, err
diff --git a/workhorse/internal/git/blob.go b/workhorse/internal/git/blob.go
index 39bd4490e66..06b0eb08228 100644
--- a/workhorse/internal/git/blob.go
+++ b/workhorse/internal/git/blob.go
@@ -8,7 +8,7 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/api"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/gitaly"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/senddata"
)
@@ -23,24 +23,20 @@ var SendBlob = &blob{"git-blob:"}
func (b *blob) Inject(w http.ResponseWriter, r *http.Request, sendData string) {
var params blobParams
if err := b.Unpack(&params, sendData); err != nil {
- helper.Fail500(w, r, fmt.Errorf("SendBlob: unpack sendData: %v", err))
+ fail.Request(w, r, fmt.Errorf("SendBlob: unpack sendData: %v", err))
return
}
- ctx, blobClient, err := gitaly.NewBlobClient(
- r.Context(),
- params.GitalyServer,
- gitaly.WithFeatures(params.GitalyServer.Features),
- )
+ ctx, blobClient, err := gitaly.NewBlobClient(r.Context(), params.GitalyServer)
if err != nil {
- helper.Fail500(w, r, fmt.Errorf("blob.GetBlob: %v", err))
+ fail.Request(w, r, fmt.Errorf("blob.GetBlob: %v", err))
return
}
setBlobHeaders(w)
if err := blobClient.SendBlob(ctx, w, &params.GetBlobRequest); err != nil {
- helper.Fail500(w, r, fmt.Errorf("blob.GetBlob: %v", err))
+ fail.Request(w, r, fmt.Errorf("blob.GetBlob: %v", err))
return
}
}
diff --git a/workhorse/internal/git/diff.go b/workhorse/internal/git/diff.go
index b4878384e2b..d450d1b9034 100644
--- a/workhorse/internal/git/diff.go
+++ b/workhorse/internal/git/diff.go
@@ -8,7 +8,7 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/api"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/gitaly"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/log"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/senddata"
)
@@ -24,23 +24,19 @@ var SendDiff = &diff{"git-diff:"}
func (d *diff) Inject(w http.ResponseWriter, r *http.Request, sendData string) {
var params diffParams
if err := d.Unpack(&params, sendData); err != nil {
- helper.Fail500(w, r, fmt.Errorf("SendDiff: unpack sendData: %v", err))
+ fail.Request(w, r, fmt.Errorf("SendDiff: unpack sendData: %v", err))
return
}
request := &gitalypb.RawDiffRequest{}
if err := gitaly.UnmarshalJSON(params.RawDiffRequest, request); err != nil {
- helper.Fail500(w, r, fmt.Errorf("diff.RawDiff: %v", err))
+ fail.Request(w, r, fmt.Errorf("diff.RawDiff: %v", err))
return
}
- ctx, diffClient, err := gitaly.NewDiffClient(
- r.Context(),
- params.GitalyServer,
- gitaly.WithFeatures(params.GitalyServer.Features),
- )
+ ctx, diffClient, err := gitaly.NewDiffClient(r.Context(), params.GitalyServer)
if err != nil {
- helper.Fail500(w, r, fmt.Errorf("diff.RawDiff: %v", err))
+ fail.Request(w, r, fmt.Errorf("diff.RawDiff: %v", err))
return
}
diff --git a/workhorse/internal/git/format-patch.go b/workhorse/internal/git/format-patch.go
index 264a4001232..a4306474aa5 100644
--- a/workhorse/internal/git/format-patch.go
+++ b/workhorse/internal/git/format-patch.go
@@ -8,7 +8,7 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/api"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/gitaly"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/log"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/senddata"
)
@@ -24,24 +24,20 @@ var SendPatch = &patch{"git-format-patch:"}
func (p *patch) Inject(w http.ResponseWriter, r *http.Request, sendData string) {
var params patchParams
if err := p.Unpack(&params, sendData); err != nil {
- helper.Fail500(w, r, fmt.Errorf("SendPatch: unpack sendData: %v", err))
+ fail.Request(w, r, fmt.Errorf("SendPatch: unpack sendData: %v", err))
return
}
request := &gitalypb.RawPatchRequest{}
if err := gitaly.UnmarshalJSON(params.RawPatchRequest, request); err != nil {
- helper.Fail500(w, r, fmt.Errorf("diff.RawPatch: %v", err))
+ fail.Request(w, r, fmt.Errorf("diff.RawPatch: %v", err))
return
}
- ctx, diffClient, err := gitaly.NewDiffClient(
- r.Context(),
- params.GitalyServer,
- gitaly.WithFeatures(params.GitalyServer.Features),
- )
+ ctx, diffClient, err := gitaly.NewDiffClient(r.Context(), params.GitalyServer)
if err != nil {
- helper.Fail500(w, r, fmt.Errorf("diff.RawPatch: %v", err))
+ fail.Request(w, r, fmt.Errorf("diff.RawPatch: %v", err))
return
}
diff --git a/workhorse/internal/git/info-refs.go b/workhorse/internal/git/info-refs.go
index 2eaed388f60..3e0e4dcb3e5 100644
--- a/workhorse/internal/git/info-refs.go
+++ b/workhorse/internal/git/info-refs.go
@@ -14,7 +14,7 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/api"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/gitaly"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
)
func GetInfoRefsHandler(a *api.API) http.Handler {
@@ -47,21 +47,16 @@ func handleGetInfoRefs(rw http.ResponseWriter, r *http.Request, a *api.Response)
err = fmt.Errorf("handleGetInfoRefs: %v", err)
if status != nil && status.Code() == grpccodes.Unavailable {
- helper.CaptureAndFail(responseWriter, r, err, "The git server, Gitaly, is not available at this time. Please contact your administrator.", http.StatusServiceUnavailable)
+ fail.Request(responseWriter, r, err, fail.WithStatus(http.StatusServiceUnavailable),
+ fail.WithBody("The git server, Gitaly, is not available at this time. Please contact your administrator."))
} else {
- helper.Fail500(responseWriter, r, err)
+ fail.Request(responseWriter, r, err)
}
}
}
func handleGetInfoRefsWithGitaly(ctx context.Context, responseWriter *HttpResponseWriter, a *api.Response, rpc, gitProtocol, encoding string) error {
- ctx, smarthttp, err := gitaly.NewSmartHTTPClient(
- ctx,
- a.GitalyServer,
- gitaly.WithFeatures(a.GitalyServer.Features),
- gitaly.WithUserID(a.GL_ID),
- gitaly.WithUsername(a.GL_USERNAME),
- )
+ ctx, smarthttp, err := gitaly.NewSmartHTTPClient(ctx, a.GitalyServer)
if err != nil {
return err
}
diff --git a/workhorse/internal/git/io.go b/workhorse/internal/git/io.go
new file mode 100644
index 00000000000..7b62b04395c
--- /dev/null
+++ b/workhorse/internal/git/io.go
@@ -0,0 +1,178 @@
+package git
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "os"
+ "sync"
+)
+
+type contextReader struct {
+ ctx context.Context
+ underlyingReader io.Reader
+}
+
+func newContextReader(ctx context.Context, underlyingReader io.Reader) *contextReader {
+ return &contextReader{
+ ctx: ctx,
+ underlyingReader: underlyingReader,
+ }
+}
+
+func (r *contextReader) Read(b []byte) (int, error) {
+ if r.canceled() {
+ return 0, r.err()
+ }
+
+ n, err := r.underlyingReader.Read(b)
+
+ if r.canceled() {
+ err = r.err()
+ }
+
+ return n, err
+}
+
+func (r *contextReader) canceled() bool {
+ return r.err() != nil
+}
+
+func (r *contextReader) err() error {
+ return r.ctx.Err()
+}
+
+type writeFlusher interface {
+ io.Writer
+ Flush() error
+}
+
+// Couple r and w so that until r has been drained (before r.Read() has
+// returned some error), all writes to w are sent to a tempfile first.
+// The caller must call Flush() on the returned WriteFlusher to ensure
+// all data is propagated to w.
+func newWriteAfterReader(r io.Reader, w io.Writer) (io.Reader, writeFlusher) {
+ br := &busyReader{Reader: r}
+ return br, &coupledWriter{Writer: w, busyReader: br}
+}
+
+type busyReader struct {
+ io.Reader
+
+ error
+ errorMutex sync.RWMutex
+}
+
+func (r *busyReader) Read(p []byte) (int, error) {
+ if err := r.getError(); err != nil {
+ return 0, err
+ }
+
+ n, err := r.Reader.Read(p)
+ if err != nil {
+ if err != io.EOF {
+ err = fmt.Errorf("busyReader: %w", err)
+ }
+ r.setError(err)
+ }
+ return n, err
+}
+
+func (r *busyReader) IsBusy() bool {
+ return r.getError() == nil
+}
+
+func (r *busyReader) getError() error {
+ r.errorMutex.RLock()
+ defer r.errorMutex.RUnlock()
+ return r.error
+}
+
+func (r *busyReader) setError(err error) {
+ if err == nil {
+ panic("busyReader: attempt to reset error to nil")
+ }
+ r.errorMutex.Lock()
+ defer r.errorMutex.Unlock()
+ r.error = err
+}
+
+type coupledWriter struct {
+ io.Writer
+ *busyReader
+
+ tempfile *os.File
+ tempfileMutex sync.Mutex
+
+ writeError error
+}
+
+func (w *coupledWriter) Write(data []byte) (int, error) {
+ if w.writeError != nil {
+ return 0, w.writeError
+ }
+
+ if w.busyReader.IsBusy() {
+ n, err := w.tempfileWrite(data)
+ if err != nil {
+ w.writeError = fmt.Errorf("coupledWriter: %w", err)
+ }
+ return n, w.writeError
+ }
+
+ if err := w.Flush(); err != nil {
+ w.writeError = fmt.Errorf("coupledWriter: %w", err)
+ return 0, w.writeError
+ }
+
+ return w.Writer.Write(data)
+}
+
+func (w *coupledWriter) Flush() error {
+ w.tempfileMutex.Lock()
+ defer w.tempfileMutex.Unlock()
+
+ tempfile := w.tempfile
+ if tempfile == nil {
+ return nil
+ }
+
+ w.tempfile = nil
+ defer tempfile.Close()
+
+ if _, err := tempfile.Seek(0, 0); err != nil {
+ return err
+ }
+ if _, err := io.Copy(w.Writer, tempfile); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (w *coupledWriter) tempfileWrite(data []byte) (int, error) {
+ w.tempfileMutex.Lock()
+ defer w.tempfileMutex.Unlock()
+
+ if w.tempfile == nil {
+ tempfile, err := w.newTempfile()
+ if err != nil {
+ return 0, err
+ }
+ w.tempfile = tempfile
+ }
+
+ return w.tempfile.Write(data)
+}
+
+func (*coupledWriter) newTempfile() (tempfile *os.File, err error) {
+ tempfile, err = os.CreateTemp("", "gitlab-workhorse-coupledWriter")
+ if err != nil {
+ return nil, err
+ }
+ if err := os.Remove(tempfile.Name()); err != nil {
+ tempfile.Close()
+ return nil, err
+ }
+
+ return tempfile, nil
+}
diff --git a/workhorse/internal/git/io_test.go b/workhorse/internal/git/io_test.go
new file mode 100644
index 00000000000..f283c20c23c
--- /dev/null
+++ b/workhorse/internal/git/io_test.go
@@ -0,0 +1,191 @@
+package git
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "io"
+ "testing"
+ "testing/iotest"
+ "time"
+
+ "github.com/stretchr/testify/require"
+)
+
+type fakeReader struct {
+ n int
+ err error
+}
+
+func (f *fakeReader) Read(b []byte) (int, error) {
+ return f.n, f.err
+}
+
+type fakeContextWithTimeout struct {
+ n int
+ threshold int
+}
+
+func (*fakeContextWithTimeout) Deadline() (deadline time.Time, ok bool) {
+ return
+}
+
+func (*fakeContextWithTimeout) Done() <-chan struct{} {
+ return nil
+}
+
+func (*fakeContextWithTimeout) Value(key interface{}) interface{} {
+ return nil
+}
+
+func (f *fakeContextWithTimeout) Err() error {
+ f.n++
+ if f.n > f.threshold {
+ return context.DeadlineExceeded
+ }
+
+ return nil
+}
+
+func TestContextReaderRead(t *testing.T) {
+ underlyingReader := &fakeReader{n: 1, err: io.EOF}
+
+ for _, tc := range []struct {
+ desc string
+ ctx *fakeContextWithTimeout
+ expectedN int
+ expectedErr error
+ }{
+ {
+ desc: "Before and after read deadline checks are fine",
+ ctx: &fakeContextWithTimeout{n: 0, threshold: 2},
+ expectedN: underlyingReader.n,
+ expectedErr: underlyingReader.err,
+ },
+ {
+ desc: "Before read deadline check fails",
+ ctx: &fakeContextWithTimeout{n: 0, threshold: 0},
+ expectedN: 0,
+ expectedErr: context.DeadlineExceeded,
+ },
+ {
+ desc: "After read deadline check fails",
+ ctx: &fakeContextWithTimeout{n: 0, threshold: 1},
+ expectedN: underlyingReader.n,
+ expectedErr: context.DeadlineExceeded,
+ },
+ } {
+ t.Run(tc.desc, func(t *testing.T) {
+ cr := newContextReader(tc.ctx, underlyingReader)
+
+ n, err := cr.Read(nil)
+ require.Equal(t, tc.expectedN, n)
+ require.Equal(t, tc.expectedErr, err)
+ })
+ }
+}
+
+func TestBusyReader(t *testing.T) {
+ testData := "test data"
+ r := testReader(testData)
+ br, _ := newWriteAfterReader(r, &bytes.Buffer{})
+
+ result, err := io.ReadAll(br)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if string(result) != testData {
+ t.Fatalf("expected %q, got %q", testData, result)
+ }
+}
+
+func TestFirstWriteAfterReadDone(t *testing.T) {
+ writeRecorder := &bytes.Buffer{}
+ br, cw := newWriteAfterReader(&bytes.Buffer{}, writeRecorder)
+ if _, err := io.Copy(io.Discard, br); err != nil {
+ t.Fatalf("copy from busyreader: %v", err)
+ }
+ testData := "test data"
+ if _, err := io.Copy(cw, testReader(testData)); err != nil {
+ t.Fatalf("copy test data: %v", err)
+ }
+ if err := cw.Flush(); err != nil {
+ t.Fatalf("flush error: %v", err)
+ }
+ if result := writeRecorder.String(); result != testData {
+ t.Fatalf("expected %q, got %q", testData, result)
+ }
+}
+
+func TestWriteDelay(t *testing.T) {
+ writeRecorder := &bytes.Buffer{}
+ w := &complainingWriter{Writer: writeRecorder}
+ br, cw := newWriteAfterReader(&bytes.Buffer{}, w)
+
+ testData1 := "1 test"
+ if _, err := io.Copy(cw, testReader(testData1)); err != nil {
+ t.Fatalf("error on first copy: %v", err)
+ }
+
+ // Unblock the coupled writer by draining the reader
+ if _, err := io.Copy(io.Discard, br); err != nil {
+ t.Fatalf("copy from busyreader: %v", err)
+ }
+ // Now it is no longer an error if 'w' receives a Write()
+ w.CheerUp()
+
+ testData2 := "2 experiment"
+ if _, err := io.Copy(cw, testReader(testData2)); err != nil {
+ t.Fatalf("error on second copy: %v", err)
+ }
+
+ if err := cw.Flush(); err != nil {
+ t.Fatalf("flush error: %v", err)
+ }
+
+ expected := testData1 + testData2
+ if result := writeRecorder.String(); result != expected {
+ t.Fatalf("total write: expected %q, got %q", expected, result)
+ }
+}
+
+func TestComplainingWriterSanity(t *testing.T) {
+ recorder := &bytes.Buffer{}
+ w := &complainingWriter{Writer: recorder}
+
+ testData := "test data"
+ if _, err := io.Copy(w, testReader(testData)); err == nil {
+ t.Error("error expected, none received")
+ }
+
+ w.CheerUp()
+ if _, err := io.Copy(w, testReader(testData)); err != nil {
+ t.Errorf("copy after CheerUp: %v", err)
+ }
+
+ if result := recorder.String(); result != testData {
+ t.Errorf("expected %q, got %q", testData, result)
+ }
+}
+
+func testReader(data string) io.Reader {
+ return iotest.OneByteReader(bytes.NewBuffer([]byte(data)))
+}
+
+type complainingWriter struct {
+ happy bool
+ io.Writer
+}
+
+func (comp *complainingWriter) Write(data []byte) (int, error) {
+ if comp.happy {
+ return comp.Writer.Write(data)
+ }
+
+ return 0, fmt.Errorf("I am unhappy about you wanting to write %q", data)
+}
+
+func (comp *complainingWriter) CheerUp() {
+ comp.happy = true
+}
diff --git a/workhorse/internal/git/receive-pack.go b/workhorse/internal/git/receive-pack.go
index a85f0edccac..5e93c0f36d1 100644
--- a/workhorse/internal/git/receive-pack.go
+++ b/workhorse/internal/git/receive-pack.go
@@ -6,7 +6,6 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/api"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/gitaly"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
)
// Will not return a non-nil error after the response body has been
@@ -15,18 +14,12 @@ func handleReceivePack(w *HttpResponseWriter, r *http.Request, a *api.Response)
action := getService(r)
writePostRPCHeader(w, action)
- cr, cw := helper.NewWriteAfterReader(r.Body, w)
+ cr, cw := newWriteAfterReader(r.Body, w)
defer cw.Flush()
gitProtocol := r.Header.Get("Git-Protocol")
- ctx, smarthttp, err := gitaly.NewSmartHTTPClient(
- r.Context(),
- a.GitalyServer,
- gitaly.WithFeatures(a.GitalyServer.Features),
- gitaly.WithUserID(a.GL_ID),
- gitaly.WithUsername(a.GL_USERNAME),
- )
+ ctx, smarthttp, err := gitaly.NewSmartHTTPClient(r.Context(), a.GitalyServer)
if err != nil {
return fmt.Errorf("smarthttp.ReceivePack: %v", err)
}
diff --git a/workhorse/internal/git/snapshot.go b/workhorse/internal/git/snapshot.go
index 70832ec9211..777ecd144a8 100644
--- a/workhorse/internal/git/snapshot.go
+++ b/workhorse/internal/git/snapshot.go
@@ -9,7 +9,7 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/api"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/gitaly"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/log"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/senddata"
)
@@ -31,30 +31,26 @@ func (s *snapshot) Inject(w http.ResponseWriter, r *http.Request, sendData strin
var params snapshotParams
if err := s.Unpack(&params, sendData); err != nil {
- helper.Fail500(w, r, fmt.Errorf("SendSnapshot: unpack sendData: %v", err))
+ fail.Request(w, r, fmt.Errorf("SendSnapshot: unpack sendData: %v", err))
return
}
request := &gitalypb.GetSnapshotRequest{}
if err := gitaly.UnmarshalJSON(params.GetSnapshotRequest, request); err != nil {
- helper.Fail500(w, r, fmt.Errorf("SendSnapshot: unmarshal GetSnapshotRequest: %v", err))
+ fail.Request(w, r, fmt.Errorf("SendSnapshot: unmarshal GetSnapshotRequest: %v", err))
return
}
- ctx, c, err := gitaly.NewRepositoryClient(
- r.Context(),
- params.GitalyServer,
- gitaly.WithFeatures(params.GitalyServer.Features),
- )
+ ctx, c, err := gitaly.NewRepositoryClient(r.Context(), params.GitalyServer)
if err != nil {
- helper.Fail500(w, r, fmt.Errorf("SendSnapshot: gitaly.NewRepositoryClient: %v", err))
+ fail.Request(w, r, fmt.Errorf("SendSnapshot: gitaly.NewRepositoryClient: %v", err))
return
}
reader, err := c.SnapshotReader(ctx, request)
if err != nil {
- helper.Fail500(w, r, fmt.Errorf("SendSnapshot: client.SnapshotReader: %v", err))
+ fail.Request(w, r, fmt.Errorf("SendSnapshot: client.SnapshotReader: %v", err))
return
}
diff --git a/workhorse/internal/git/upload-pack.go b/workhorse/internal/git/upload-pack.go
index bbed5224b2d..ef2a00bf3ac 100644
--- a/workhorse/internal/git/upload-pack.go
+++ b/workhorse/internal/git/upload-pack.go
@@ -9,7 +9,6 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/api"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/gitaly"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
)
var (
@@ -31,8 +30,8 @@ func handleUploadPack(w *HttpResponseWriter, r *http.Request, a *api.Response) e
readerCtx, cancel := context.WithTimeout(ctx, uploadPackTimeout)
defer cancel()
- limited := helper.NewContextReader(readerCtx, r.Body)
- cr, cw := helper.NewWriteAfterReader(limited, w)
+ limited := newContextReader(readerCtx, r.Body)
+ cr, cw := newWriteAfterReader(limited, w)
defer cw.Flush()
action := getService(r)
@@ -44,13 +43,7 @@ func handleUploadPack(w *HttpResponseWriter, r *http.Request, a *api.Response) e
}
func handleUploadPackWithGitaly(ctx context.Context, a *api.Response, clientRequest io.Reader, clientResponse io.Writer, gitProtocol string) error {
- ctx, smarthttp, err := gitaly.NewSmartHTTPClient(
- ctx,
- a.GitalyServer,
- gitaly.WithFeatures(a.GitalyServer.Features),
- gitaly.WithUserID(a.GL_ID),
- gitaly.WithUsername(a.GL_USERNAME),
- )
+ ctx, smarthttp, err := gitaly.NewSmartHTTPClient(ctx, a.GitalyServer)
if err != nil {
return fmt.Errorf("get gitaly client: %w", err)
}
diff --git a/workhorse/internal/gitaly/gitaly.go b/workhorse/internal/gitaly/gitaly.go
index b695acbb688..af7425be1cf 100644
--- a/workhorse/internal/gitaly/gitaly.go
+++ b/workhorse/internal/gitaly/gitaly.go
@@ -67,42 +67,23 @@ func InitializeSidechannelRegistry(logger *logrus.Logger) {
}
}
-type MetadataFunc func(metadata.MD)
-
-func WithUserID(userID string) MetadataFunc {
- return func(md metadata.MD) {
- md.Append("user_id", userID)
- }
+var allowedMetadataKeys = map[string]bool{
+ "user_id": true,
+ "username": true,
+ "remote_ip": true,
}
-func WithUsername(username string) MetadataFunc {
- return func(md metadata.MD) {
- md.Append("username", username)
- }
-}
-
-func WithFeatures(features map[string]string) MetadataFunc {
- return func(md metadata.MD) {
- for k, v := range features {
- if !strings.HasPrefix(k, "gitaly-feature-") {
- continue
- }
- md.Append(k, v)
- }
- }
-}
-
-func withOutgoingMetadata(ctx context.Context, addMetadataFuncs ...MetadataFunc) context.Context {
+func withOutgoingMetadata(ctx context.Context, gs api.GitalyServer) context.Context {
md := metadata.New(nil)
-
- for _, f := range addMetadataFuncs {
- f(md)
+ for k, v := range gs.CallMetadata {
+ if strings.HasPrefix(k, "gitaly-feature-") || allowedMetadataKeys[k] {
+ md.Set(k, v)
+ }
}
-
return metadata.NewOutgoingContext(ctx, md)
}
-func NewSmartHTTPClient(ctx context.Context, server api.GitalyServer, metadataFuncs ...MetadataFunc) (context.Context, *SmartHTTPClient, error) {
+func NewSmartHTTPClient(ctx context.Context, server api.GitalyServer) (context.Context, *SmartHTTPClient, error) {
conn, err := getOrCreateConnection(server)
if err != nil {
return nil, nil, err
@@ -112,48 +93,44 @@ func NewSmartHTTPClient(ctx context.Context, server api.GitalyServer, metadataFu
SmartHTTPServiceClient: grpcClient,
sidechannelRegistry: sidechannelRegistry,
}
-
- return withOutgoingMetadata(
- ctx,
- metadataFuncs...,
- ), smartHTTPClient, nil
+ return withOutgoingMetadata(ctx, server), smartHTTPClient, nil
}
-func NewBlobClient(ctx context.Context, server api.GitalyServer, addMetadataFuncs ...MetadataFunc) (context.Context, *BlobClient, error) {
+func NewBlobClient(ctx context.Context, server api.GitalyServer) (context.Context, *BlobClient, error) {
conn, err := getOrCreateConnection(server)
if err != nil {
return nil, nil, err
}
grpcClient := gitalypb.NewBlobServiceClient(conn)
- return withOutgoingMetadata(ctx, addMetadataFuncs...), &BlobClient{grpcClient}, nil
+ return withOutgoingMetadata(ctx, server), &BlobClient{grpcClient}, nil
}
-func NewRepositoryClient(ctx context.Context, server api.GitalyServer, addMetadataFuncs ...MetadataFunc) (context.Context, *RepositoryClient, error) {
+func NewRepositoryClient(ctx context.Context, server api.GitalyServer) (context.Context, *RepositoryClient, error) {
conn, err := getOrCreateConnection(server)
if err != nil {
return nil, nil, err
}
grpcClient := gitalypb.NewRepositoryServiceClient(conn)
- return withOutgoingMetadata(ctx, addMetadataFuncs...), &RepositoryClient{grpcClient}, nil
+ return withOutgoingMetadata(ctx, server), &RepositoryClient{grpcClient}, nil
}
// NewNamespaceClient is only used by the Gitaly integration tests at present
-func NewNamespaceClient(ctx context.Context, server api.GitalyServer, addMetadataFuncs ...MetadataFunc) (context.Context, *NamespaceClient, error) {
+func NewNamespaceClient(ctx context.Context, server api.GitalyServer) (context.Context, *NamespaceClient, error) {
conn, err := getOrCreateConnection(server)
if err != nil {
return nil, nil, err
}
grpcClient := gitalypb.NewNamespaceServiceClient(conn)
- return withOutgoingMetadata(ctx, addMetadataFuncs...), &NamespaceClient{grpcClient}, nil
+ return withOutgoingMetadata(ctx, server), &NamespaceClient{grpcClient}, nil
}
-func NewDiffClient(ctx context.Context, server api.GitalyServer, addMetadataFuncs ...MetadataFunc) (context.Context, *DiffClient, error) {
+func NewDiffClient(ctx context.Context, server api.GitalyServer) (context.Context, *DiffClient, error) {
conn, err := getOrCreateConnection(server)
if err != nil {
return nil, nil, err
}
grpcClient := gitalypb.NewDiffServiceClient(conn)
- return withOutgoingMetadata(ctx, addMetadataFuncs...), &DiffClient{grpcClient}, nil
+ return withOutgoingMetadata(ctx, server), &DiffClient{grpcClient}, nil
}
func getOrCreateConnection(server api.GitalyServer) (*grpc.ClientConn, error) {
diff --git a/workhorse/internal/gitaly/gitaly_test.go b/workhorse/internal/gitaly/gitaly_test.go
index f693f102447..0ea5da20da3 100644
--- a/workhorse/internal/gitaly/gitaly_test.go
+++ b/workhorse/internal/gitaly/gitaly_test.go
@@ -21,13 +21,9 @@ func TestNewSmartHTTPClient(t *testing.T) {
ctx, client, err := NewSmartHTTPClient(
context.Background(),
serverFixture(),
- WithFeatures(features()),
- WithUsername("gl_username"),
- WithUserID("gl_id"),
)
require.NoError(t, err)
testOutgoingMetadata(t, ctx)
- testOutgoingIDAndUsername(t, ctx)
require.NotNil(t, client.sidechannelRegistry)
}
@@ -35,7 +31,6 @@ func TestNewBlobClient(t *testing.T) {
ctx, _, err := NewBlobClient(
context.Background(),
serverFixture(),
- WithFeatures(features()),
)
require.NoError(t, err)
testOutgoingMetadata(t, ctx)
@@ -45,7 +40,6 @@ func TestNewRepositoryClient(t *testing.T) {
ctx, _, err := NewRepositoryClient(
context.Background(),
serverFixture(),
- WithFeatures(features()),
)
require.NoError(t, err)
@@ -56,7 +50,6 @@ func TestNewNamespaceClient(t *testing.T) {
ctx, _, err := NewNamespaceClient(
context.Background(),
serverFixture(),
- WithFeatures(features()),
)
require.NoError(t, err)
testOutgoingMetadata(t, ctx)
@@ -66,62 +59,45 @@ func TestNewDiffClient(t *testing.T) {
ctx, _, err := NewDiffClient(
context.Background(),
serverFixture(),
- WithFeatures(features()),
)
require.NoError(t, err)
testOutgoingMetadata(t, ctx)
}
func testOutgoingMetadata(t *testing.T, ctx context.Context) {
+ t.Helper()
md, ok := metadata.FromOutgoingContext(ctx)
require.True(t, ok, "get metadata from context")
- for k, v := range allowedFeatures() {
- actual := md[k]
- require.Len(t, actual, 1, "expect one value for %v", k)
- require.Equal(t, v, actual[0], "value for %v", k)
- }
-
- for k := range badFeatureMetadata() {
- require.Empty(t, md[k], "value for bad key %v", k)
- }
-}
-
-func testOutgoingIDAndUsername(t *testing.T, ctx context.Context) {
- md, ok := metadata.FromOutgoingContext(ctx)
- require.True(t, ok, "get metadata from context")
-
- require.Equal(t, md["user_id"], []string{"gl_id"})
- require.Equal(t, md["username"], []string{"gl_username"})
-}
-
-func features() map[string]string {
- features := make(map[string]string)
- for k, v := range allowedFeatures() {
- features[k] = v
- }
-
- for k, v := range badFeatureMetadata() {
- features[k] = v
- }
-
- return features
+ require.Equal(t, metadata.MD{"username": {"janedoe"}}, md)
}
func serverFixture() api.GitalyServer {
- return api.GitalyServer{Address: "tcp://localhost:123"}
-}
-
-func allowedFeatures() map[string]string {
- return map[string]string{
- "gitaly-feature-foo": "bar",
- "gitaly-feature-qux": "baz",
+ return api.GitalyServer{
+ Address: "tcp://localhost:123",
+ CallMetadata: map[string]string{"username": "janedoe"},
}
}
-func badFeatureMetadata() map[string]string {
- return map[string]string{
- "bad-metadata-1": "bad-value-1",
- "bad-metadata-2": "bad-value-2",
- }
+func TestWithOutgoingMetadata(t *testing.T) {
+ ctx := withOutgoingMetadata(context.Background(), api.GitalyServer{
+ CallMetadata: map[string]string{
+ "gitaly-feature-abc": "true",
+ "gitaly-featuregarbage": "blocked",
+ "bad-header": "blocked",
+ "user_id": "234",
+ "username": "janedoe",
+ "remote_ip": "1.2.3.4",
+ },
+ })
+
+ md, ok := metadata.FromOutgoingContext(ctx)
+ require.True(t, ok)
+
+ require.Equal(t, metadata.MD{
+ "gitaly-feature-abc": {"true"},
+ "user_id": {"234"},
+ "username": {"janedoe"},
+ "remote_ip": {"1.2.3.4"},
+ }, md)
}
diff --git a/workhorse/internal/helper/command/command.go b/workhorse/internal/helper/command/command.go
new file mode 100644
index 00000000000..59c8c9a3db2
--- /dev/null
+++ b/workhorse/internal/helper/command/command.go
@@ -0,0 +1,30 @@
+package command
+
+import (
+ "os/exec"
+ "syscall"
+)
+
+func ExitStatus(err error) (int, bool) {
+ if v, ok := err.(interface{ ExitCode() int }); ok {
+ return v.ExitCode(), true
+ } else if err != nil {
+ return -1, false
+ } else {
+ return 0, false
+ }
+}
+
+func KillProcessGroup(cmd *exec.Cmd) {
+ if cmd == nil {
+ return
+ }
+
+ if p := cmd.Process; p != nil && p.Pid > 0 {
+ // Send SIGTERM to the process group of cmd
+ syscall.Kill(-p.Pid, syscall.SIGTERM)
+ }
+
+ // reap our child process
+ cmd.Wait()
+}
diff --git a/workhorse/internal/helper/context_reader.go b/workhorse/internal/helper/context_reader.go
deleted file mode 100644
index a4764043147..00000000000
--- a/workhorse/internal/helper/context_reader.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package helper
-
-import (
- "context"
- "io"
-)
-
-type ContextReader struct {
- ctx context.Context
- underlyingReader io.Reader
-}
-
-func NewContextReader(ctx context.Context, underlyingReader io.Reader) *ContextReader {
- return &ContextReader{
- ctx: ctx,
- underlyingReader: underlyingReader,
- }
-}
-
-func (r *ContextReader) Read(b []byte) (int, error) {
- if r.canceled() {
- return 0, r.err()
- }
-
- n, err := r.underlyingReader.Read(b)
-
- if r.canceled() {
- err = r.err()
- }
-
- return n, err
-}
-
-func (r *ContextReader) canceled() bool {
- return r.err() != nil
-}
-
-func (r *ContextReader) err() error {
- return r.ctx.Err()
-}
diff --git a/workhorse/internal/helper/context_reader_test.go b/workhorse/internal/helper/context_reader_test.go
deleted file mode 100644
index 257ec4e35f2..00000000000
--- a/workhorse/internal/helper/context_reader_test.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package helper
-
-import (
- "context"
- "io"
- "testing"
- "time"
-
- "github.com/stretchr/testify/require"
-)
-
-type fakeReader struct {
- n int
- err error
-}
-
-func (f *fakeReader) Read(b []byte) (int, error) {
- return f.n, f.err
-}
-
-type fakeContextWithTimeout struct {
- n int
- threshold int
-}
-
-func (*fakeContextWithTimeout) Deadline() (deadline time.Time, ok bool) {
- return
-}
-
-func (*fakeContextWithTimeout) Done() <-chan struct{} {
- return nil
-}
-
-func (*fakeContextWithTimeout) Value(key interface{}) interface{} {
- return nil
-}
-
-func (f *fakeContextWithTimeout) Err() error {
- f.n++
- if f.n > f.threshold {
- return context.DeadlineExceeded
- }
-
- return nil
-}
-
-func TestContextReaderRead(t *testing.T) {
- underlyingReader := &fakeReader{n: 1, err: io.EOF}
-
- for _, tc := range []struct {
- desc string
- ctx *fakeContextWithTimeout
- expectedN int
- expectedErr error
- }{
- {
- desc: "Before and after read deadline checks are fine",
- ctx: &fakeContextWithTimeout{n: 0, threshold: 2},
- expectedN: underlyingReader.n,
- expectedErr: underlyingReader.err,
- },
- {
- desc: "Before read deadline check fails",
- ctx: &fakeContextWithTimeout{n: 0, threshold: 0},
- expectedN: 0,
- expectedErr: context.DeadlineExceeded,
- },
- {
- desc: "After read deadline check fails",
- ctx: &fakeContextWithTimeout{n: 0, threshold: 1},
- expectedN: underlyingReader.n,
- expectedErr: context.DeadlineExceeded,
- },
- } {
- t.Run(tc.desc, func(t *testing.T) {
- cr := NewContextReader(tc.ctx, underlyingReader)
-
- n, err := cr.Read(nil)
- require.Equal(t, tc.expectedN, n)
- require.Equal(t, tc.expectedErr, err)
- })
- }
-}
diff --git a/workhorse/internal/helper/exception/exception.go b/workhorse/internal/helper/exception/exception.go
new file mode 100644
index 00000000000..9b1628ffecb
--- /dev/null
+++ b/workhorse/internal/helper/exception/exception.go
@@ -0,0 +1,58 @@
+package exception
+
+import (
+ "net/http"
+ "reflect"
+
+ raven "github.com/getsentry/raven-go"
+
+ //lint:ignore SA1019 this was recently deprecated. Update workhorse to use labkit errortracking package.
+ correlation "gitlab.com/gitlab-org/labkit/correlation/raven"
+
+ "gitlab.com/gitlab-org/labkit/log"
+)
+
+var ravenHeaderBlacklist = []string{
+ "Authorization",
+ "Private-Token",
+}
+
+func Track(r *http.Request, err error, fields log.Fields) {
+ client := raven.DefaultClient
+ extra := raven.Extra{}
+
+ for k, v := range fields {
+ extra[k] = v
+ }
+
+ interfaces := []raven.Interface{}
+ if r != nil {
+ CleanHeaders(r)
+ interfaces = append(interfaces, raven.NewHttp(r))
+
+ //lint:ignore SA1019 this was recently deprecated. Update workhorse to use labkit errortracking package.
+ extra = correlation.SetExtra(r.Context(), extra)
+ }
+
+ exception := &raven.Exception{
+ Stacktrace: raven.NewStacktrace(2, 3, nil),
+ Value: err.Error(),
+ Type: reflect.TypeOf(err).String(),
+ }
+ interfaces = append(interfaces, exception)
+
+ packet := raven.NewPacketWithExtra(err.Error(), extra, interfaces...)
+ client.Capture(packet, nil)
+}
+
+func CleanHeaders(r *http.Request) {
+ if r == nil {
+ return
+ }
+
+ for _, key := range ravenHeaderBlacklist {
+ if r.Header.Get(key) != "" {
+ r.Header.Set(key, "[redacted]")
+ }
+ }
+}
diff --git a/workhorse/internal/helper/fail/fail.go b/workhorse/internal/helper/fail/fail.go
new file mode 100644
index 00000000000..32c2940a0cc
--- /dev/null
+++ b/workhorse/internal/helper/fail/fail.go
@@ -0,0 +1,45 @@
+package fail
+
+import (
+ "net/http"
+
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/log"
+)
+
+type failure struct {
+ status int
+ body string
+ fields log.Fields
+}
+
+type Option func(*failure)
+
+// WithStatus sets the HTTP status and body text of the failure response.
+func WithStatus(status int) Option {
+ return func(f *failure) {
+ f.status = status
+ f.body = http.StatusText(status)
+ }
+}
+
+// WithBody sets the body text of the failure response. Note that
+// subsequent applications of WithStatus will override the response body.
+func WithBody(body string) Option { return func(f *failure) { f.body = body } }
+
+// WithFields adds log fields to the failure log message.
+func WithFields(fields log.Fields) Option { return func(f *failure) { f.fields = fields } }
+
+// Request combines error handling actions for a failed HTTP request. By
+// default it writes a generic HTTP 500 response to w. The status code
+// and response body can be modified by passing options. The value of
+// err, if non nil, is logged and reported to Sentry.
+func Request(w http.ResponseWriter, r *http.Request, err error, options ...Option) {
+ f := &failure{}
+ WithStatus(http.StatusInternalServerError)(f)
+ for _, opt := range options {
+ opt(f)
+ }
+
+ http.Error(w, f.body, f.status)
+ log.WithRequest(r).WithFields(f.fields).WithError(err).Error()
+}
diff --git a/workhorse/internal/helper/fail/fail_test.go b/workhorse/internal/helper/fail/fail_test.go
new file mode 100644
index 00000000000..ceb037d2da7
--- /dev/null
+++ b/workhorse/internal/helper/fail/fail_test.go
@@ -0,0 +1,21 @@
+package fail
+
+import (
+ "bytes"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestRequestWorksWithNils(t *testing.T) {
+ body := bytes.NewBuffer(nil)
+ w := httptest.NewRecorder()
+ w.Body = body
+
+ Request(w, nil, nil)
+
+ require.Equal(t, http.StatusInternalServerError, w.Code)
+ require.Equal(t, "Internal Server Error\n", body.String())
+}
diff --git a/workhorse/internal/helper/helpers.go b/workhorse/internal/helper/helpers.go
index 33318407f88..a4a91901ea9 100644
--- a/workhorse/internal/helper/helpers.go
+++ b/workhorse/internal/helper/helpers.go
@@ -1,69 +1,14 @@
package helper
import (
- "bytes"
"errors"
- "io"
"mime"
- "net"
- "net/http"
"net/url"
"os"
- "os/exec"
- "strings"
- "syscall"
- "github.com/sebest/xff"
- "gitlab.com/gitlab-org/labkit/log"
- "gitlab.com/gitlab-org/labkit/mask"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/log"
)
-const NginxResponseBufferHeader = "X-Accel-Buffering"
-
-func logErrorWithFields(r *http.Request, err error, fields log.Fields) {
- if err != nil {
- CaptureRavenError(r, err, fields)
- }
-
- printError(r, err, fields)
-}
-
-func CaptureAndFail(w http.ResponseWriter, r *http.Request, err error, msg string, code int) {
- http.Error(w, msg, code)
- logErrorWithFields(r, err, nil)
-}
-
-func Fail500(w http.ResponseWriter, r *http.Request, err error) {
- CaptureAndFail(w, r, err, "Internal server error", http.StatusInternalServerError)
-}
-
-func Fail500WithFields(w http.ResponseWriter, r *http.Request, err error, fields log.Fields) {
- http.Error(w, "Internal server error", http.StatusInternalServerError)
- logErrorWithFields(r, err, fields)
-}
-
-func RequestEntityTooLarge(w http.ResponseWriter, r *http.Request, err error) {
- CaptureAndFail(w, r, err, "Request Entity Too Large", http.StatusRequestEntityTooLarge)
-}
-
-func printError(r *http.Request, err error, fields log.Fields) {
- if r != nil {
- entry := log.WithContextFields(r.Context(), log.Fields{
- "method": r.Method,
- "uri": mask.URL(r.RequestURI),
- })
- entry.WithFields(fields).WithError(err).Error()
- } else {
- log.WithFields(fields).WithError(err).Error("unknown error")
- }
-}
-
-func SetNoCacheHeaders(header http.Header) {
- header.Set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate")
- header.Set("Pragma", "no-cache")
- header.Set("Expires", "Fri, 01 Jan 1990 00:00:00 GMT")
-}
-
func OpenFile(path string) (file *os.File, fi os.FileInfo, err error) {
file, err = os.Open(path)
if err != nil {
@@ -97,113 +42,12 @@ func OpenFile(path string) (file *os.File, fi os.FileInfo, err error) {
func URLMustParse(s string) *url.URL {
u, err := url.Parse(s)
if err != nil {
- log.WithError(err).WithField("url", s).Fatal("urlMustParse")
+ log.WithError(err).WithFields(log.Fields{"url": s}).Fatal("urlMustParse")
}
return u
}
-func HTTPError(w http.ResponseWriter, r *http.Request, error string, code int) {
- if r.ProtoAtLeast(1, 1) {
- // Force client to disconnect if we render request error
- w.Header().Set("Connection", "close")
- }
-
- http.Error(w, error, code)
-}
-
-func HeaderClone(h http.Header) http.Header {
- h2 := make(http.Header, len(h))
- for k, vv := range h {
- vv2 := make([]string, len(vv))
- copy(vv2, vv)
- h2[k] = vv2
- }
- return h2
-}
-
-func CleanUpProcessGroup(cmd *exec.Cmd) {
- if cmd == nil {
- return
- }
-
- process := cmd.Process
- if process != nil && process.Pid > 0 {
- // Send SIGTERM to the process group of cmd
- syscall.Kill(-process.Pid, syscall.SIGTERM)
- }
-
- // reap our child process
- cmd.Wait()
-}
-
-func ExitStatus(err error) (int, bool) {
- exitError, ok := err.(*exec.ExitError)
- if !ok {
- return 0, false
- }
-
- waitStatus, ok := exitError.Sys().(syscall.WaitStatus)
- if !ok {
- return 0, false
- }
-
- return waitStatus.ExitStatus(), true
-}
-
-func DisableResponseBuffering(w http.ResponseWriter) {
- w.Header().Set(NginxResponseBufferHeader, "no")
-}
-
-func AllowResponseBuffering(w http.ResponseWriter) {
- w.Header().Del(NginxResponseBufferHeader)
-}
-
-func FixRemoteAddr(r *http.Request) {
- // Unix domain sockets have a remote addr of @. This will make the
- // xff package lookup the X-Forwarded-For address if available.
- if r.RemoteAddr == "@" {
- r.RemoteAddr = "127.0.0.1:0"
- }
- r.RemoteAddr = xff.GetRemoteAddr(r)
-}
-
-func SetForwardedFor(newHeaders *http.Header, originalRequest *http.Request) {
- if clientIP, _, err := net.SplitHostPort(originalRequest.RemoteAddr); err == nil {
- var header string
-
- // If we aren't the first proxy retain prior
- // X-Forwarded-For information as a comma+space
- // separated list and fold multiple headers into one.
- if prior, ok := originalRequest.Header["X-Forwarded-For"]; ok {
- header = strings.Join(prior, ", ") + ", " + clientIP
- } else {
- header = clientIP
- }
- newHeaders.Set("X-Forwarded-For", header)
- }
-}
-
func IsContentType(expected, actual string) bool {
parsed, _, err := mime.ParseMediaType(actual)
return err == nil && parsed == expected
}
-
-func IsApplicationJson(r *http.Request) bool {
- contentType := r.Header.Get("Content-Type")
- return IsContentType("application/json", contentType)
-}
-
-func ReadRequestBody(w http.ResponseWriter, r *http.Request, maxBodySize int64) ([]byte, error) {
- limitedBody := http.MaxBytesReader(w, r.Body, maxBodySize)
- defer limitedBody.Close()
-
- return io.ReadAll(limitedBody)
-}
-
-func CloneRequestWithNewBody(r *http.Request, body []byte) *http.Request {
- newReq := *r
- newReq.Body = io.NopCloser(bytes.NewReader(body))
- newReq.Header = HeaderClone(r.Header)
- newReq.ContentLength = int64(len(body))
- return &newReq
-}
diff --git a/workhorse/internal/helper/helpers_test.go b/workhorse/internal/helper/helpers_test.go
deleted file mode 100644
index 93d1ee33d59..00000000000
--- a/workhorse/internal/helper/helpers_test.go
+++ /dev/null
@@ -1,142 +0,0 @@
-package helper
-
-import (
- "bytes"
- "io"
- "net/http"
- "net/http/httptest"
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestFixRemoteAddr(t *testing.T) {
- testCases := []struct {
- initial string
- forwarded string
- expected string
- }{
- {initial: "@", forwarded: "", expected: "127.0.0.1:0"},
- {initial: "@", forwarded: "18.245.0.1", expected: "18.245.0.1:0"},
- {initial: "@", forwarded: "127.0.0.1", expected: "127.0.0.1:0"},
- {initial: "@", forwarded: "192.168.0.1", expected: "127.0.0.1:0"},
- {initial: "192.168.1.1:0", forwarded: "", expected: "192.168.1.1:0"},
- {initial: "192.168.1.1:0", forwarded: "18.245.0.1", expected: "18.245.0.1:0"},
- }
-
- for _, tc := range testCases {
- req, err := http.NewRequest("POST", "unix:///tmp/test.socket/info/refs", nil)
- require.NoError(t, err)
-
- req.RemoteAddr = tc.initial
-
- if tc.forwarded != "" {
- req.Header.Add("X-Forwarded-For", tc.forwarded)
- }
-
- FixRemoteAddr(req)
-
- require.Equal(t, tc.expected, req.RemoteAddr)
- }
-}
-
-func TestSetForwardedForGeneratesHeader(t *testing.T) {
- testCases := []struct {
- remoteAddr string
- previousForwardedFor []string
- expected string
- }{
- {
- "8.8.8.8:3000",
- nil,
- "8.8.8.8",
- },
- {
- "8.8.8.8:3000",
- []string{"138.124.33.63, 151.146.211.237"},
- "138.124.33.63, 151.146.211.237, 8.8.8.8",
- },
- {
- "8.8.8.8:3000",
- []string{"8.154.76.107", "115.206.118.179"},
- "8.154.76.107, 115.206.118.179, 8.8.8.8",
- },
- }
- for _, tc := range testCases {
- headers := http.Header{}
- originalRequest := http.Request{
- RemoteAddr: tc.remoteAddr,
- }
-
- if tc.previousForwardedFor != nil {
- originalRequest.Header = http.Header{
- "X-Forwarded-For": tc.previousForwardedFor,
- }
- }
-
- SetForwardedFor(&headers, &originalRequest)
-
- result := headers.Get("X-Forwarded-For")
- if result != tc.expected {
- t.Fatalf("Expected %v, got %v", tc.expected, result)
- }
- }
-}
-
-func TestReadRequestBody(t *testing.T) {
- data := []byte("123456")
- rw := httptest.NewRecorder()
- req, _ := http.NewRequest("POST", "/test", bytes.NewBuffer(data))
-
- result, err := ReadRequestBody(rw, req, 1000)
- require.NoError(t, err)
- require.Equal(t, data, result)
-}
-
-func TestReadRequestBodyLimit(t *testing.T) {
- data := []byte("123456")
- rw := httptest.NewRecorder()
- req, _ := http.NewRequest("POST", "/test", bytes.NewBuffer(data))
-
- _, err := ReadRequestBody(rw, req, 2)
- require.Error(t, err)
-}
-
-func TestCloneRequestWithBody(t *testing.T) {
- input := []byte("test")
- newInput := []byte("new body")
- req, _ := http.NewRequest("POST", "/test", bytes.NewBuffer(input))
- newReq := CloneRequestWithNewBody(req, newInput)
-
- require.NotEqual(t, req, newReq)
- require.NotEqual(t, req.Body, newReq.Body)
- require.NotEqual(t, len(newInput), newReq.ContentLength)
-
- var buffer bytes.Buffer
- io.Copy(&buffer, newReq.Body)
- require.Equal(t, newInput, buffer.Bytes())
-}
-
-func TestApplicationJson(t *testing.T) {
- req, _ := http.NewRequest("POST", "/test", nil)
- req.Header.Set("Content-Type", "application/json")
-
- require.True(t, IsApplicationJson(req), "expected to match 'application/json' as 'application/json'")
-
- req.Header.Set("Content-Type", "application/json; charset=utf-8")
- require.True(t, IsApplicationJson(req), "expected to match 'application/json; charset=utf-8' as 'application/json'")
-
- req.Header.Set("Content-Type", "text/plain")
- require.False(t, IsApplicationJson(req), "expected not to match 'text/plain' as 'application/json'")
-}
-
-func TestFail500WorksWithNils(t *testing.T) {
- body := bytes.NewBuffer(nil)
- w := httptest.NewRecorder()
- w.Body = body
-
- Fail500(w, nil, nil)
-
- require.Equal(t, http.StatusInternalServerError, w.Code)
- require.Equal(t, "Internal server error\n", body.String())
-}
diff --git a/workhorse/internal/helper/nginx/nginx.go b/workhorse/internal/helper/nginx/nginx.go
new file mode 100644
index 00000000000..ca7c8543f75
--- /dev/null
+++ b/workhorse/internal/helper/nginx/nginx.go
@@ -0,0 +1,13 @@
+package nginx
+
+import "net/http"
+
+const ResponseBufferHeader = "X-Accel-Buffering"
+
+func DisableResponseBuffering(w http.ResponseWriter) {
+ w.Header().Set(ResponseBufferHeader, "no")
+}
+
+func AllowResponseBuffering(w http.ResponseWriter) {
+ w.Header().Del(ResponseBufferHeader)
+}
diff --git a/workhorse/internal/helper/raven.go b/workhorse/internal/helper/raven.go
deleted file mode 100644
index 898e8ec85f8..00000000000
--- a/workhorse/internal/helper/raven.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package helper
-
-import (
- "net/http"
- "reflect"
-
- raven "github.com/getsentry/raven-go"
-
- //lint:ignore SA1019 this was recently deprecated. Update workhorse to use labkit errortracking package.
- correlation "gitlab.com/gitlab-org/labkit/correlation/raven"
-
- "gitlab.com/gitlab-org/labkit/log"
-)
-
-var ravenHeaderBlacklist = []string{
- "Authorization",
- "Private-Token",
-}
-
-func CaptureRavenError(r *http.Request, err error, fields log.Fields) {
- client := raven.DefaultClient
- extra := raven.Extra{}
-
- for k, v := range fields {
- extra[k] = v
- }
-
- interfaces := []raven.Interface{}
- if r != nil {
- CleanHeadersForRaven(r)
- interfaces = append(interfaces, raven.NewHttp(r))
-
- //lint:ignore SA1019 this was recently deprecated. Update workhorse to use labkit errortracking package.
- extra = correlation.SetExtra(r.Context(), extra)
- }
-
- exception := &raven.Exception{
- Stacktrace: raven.NewStacktrace(2, 3, nil),
- Value: err.Error(),
- Type: reflect.TypeOf(err).String(),
- }
- interfaces = append(interfaces, exception)
-
- packet := raven.NewPacketWithExtra(err.Error(), extra, interfaces...)
- client.Capture(packet, nil)
-}
-
-func CleanHeadersForRaven(r *http.Request) {
- if r == nil {
- return
- }
-
- for _, key := range ravenHeaderBlacklist {
- if r.Header.Get(key) != "" {
- r.Header.Set(key, "[redacted]")
- }
- }
-}
diff --git a/workhorse/internal/helper/tempfile.go b/workhorse/internal/helper/tempfile.go
deleted file mode 100644
index f5864f549d0..00000000000
--- a/workhorse/internal/helper/tempfile.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package helper
-
-import (
- "io"
- "os"
-)
-
-func ReadAllTempfile(r io.Reader) (tempfile *os.File, err error) {
- tempfile, err = os.CreateTemp("", "gitlab-workhorse-read-all-tempfile")
- if err != nil {
- return nil, err
- }
-
- defer func() {
- // Avoid leaking an open file if the function returns with an error
- if err != nil {
- tempfile.Close()
- }
- }()
-
- if err := os.Remove(tempfile.Name()); err != nil {
- return nil, err
- }
-
- if _, err := io.Copy(tempfile, r); err != nil {
- return nil, err
- }
-
- if _, err := tempfile.Seek(0, 0); err != nil {
- return nil, err
- }
-
- return tempfile, nil
-}
diff --git a/workhorse/internal/helper/writeafterreader.go b/workhorse/internal/helper/writeafterreader.go
deleted file mode 100644
index 3626d70e493..00000000000
--- a/workhorse/internal/helper/writeafterreader.go
+++ /dev/null
@@ -1,143 +0,0 @@
-package helper
-
-import (
- "fmt"
- "io"
- "os"
- "sync"
-)
-
-type WriteFlusher interface {
- io.Writer
- Flush() error
-}
-
-// Couple r and w so that until r has been drained (before r.Read() has
-// returned some error), all writes to w are sent to a tempfile first.
-// The caller must call Flush() on the returned WriteFlusher to ensure
-// all data is propagated to w.
-func NewWriteAfterReader(r io.Reader, w io.Writer) (io.Reader, WriteFlusher) {
- br := &busyReader{Reader: r}
- return br, &coupledWriter{Writer: w, busyReader: br}
-}
-
-type busyReader struct {
- io.Reader
-
- error
- errorMutex sync.RWMutex
-}
-
-func (r *busyReader) Read(p []byte) (int, error) {
- if err := r.getError(); err != nil {
- return 0, err
- }
-
- n, err := r.Reader.Read(p)
- if err != nil {
- if err != io.EOF {
- err = fmt.Errorf("busyReader: %w", err)
- }
- r.setError(err)
- }
- return n, err
-}
-
-func (r *busyReader) IsBusy() bool {
- return r.getError() == nil
-}
-
-func (r *busyReader) getError() error {
- r.errorMutex.RLock()
- defer r.errorMutex.RUnlock()
- return r.error
-}
-
-func (r *busyReader) setError(err error) {
- if err == nil {
- panic("busyReader: attempt to reset error to nil")
- }
- r.errorMutex.Lock()
- defer r.errorMutex.Unlock()
- r.error = err
-}
-
-type coupledWriter struct {
- io.Writer
- *busyReader
-
- tempfile *os.File
- tempfileMutex sync.Mutex
-
- writeError error
-}
-
-func (w *coupledWriter) Write(data []byte) (int, error) {
- if w.writeError != nil {
- return 0, w.writeError
- }
-
- if w.busyReader.IsBusy() {
- n, err := w.tempfileWrite(data)
- if err != nil {
- w.writeError = fmt.Errorf("coupledWriter: %w", err)
- }
- return n, w.writeError
- }
-
- if err := w.Flush(); err != nil {
- w.writeError = fmt.Errorf("coupledWriter: %w", err)
- return 0, w.writeError
- }
-
- return w.Writer.Write(data)
-}
-
-func (w *coupledWriter) Flush() error {
- w.tempfileMutex.Lock()
- defer w.tempfileMutex.Unlock()
-
- tempfile := w.tempfile
- if tempfile == nil {
- return nil
- }
-
- w.tempfile = nil
- defer tempfile.Close()
-
- if _, err := tempfile.Seek(0, 0); err != nil {
- return err
- }
- if _, err := io.Copy(w.Writer, tempfile); err != nil {
- return err
- }
- return nil
-}
-
-func (w *coupledWriter) tempfileWrite(data []byte) (int, error) {
- w.tempfileMutex.Lock()
- defer w.tempfileMutex.Unlock()
-
- if w.tempfile == nil {
- tempfile, err := w.newTempfile()
- if err != nil {
- return 0, err
- }
- w.tempfile = tempfile
- }
-
- return w.tempfile.Write(data)
-}
-
-func (*coupledWriter) newTempfile() (tempfile *os.File, err error) {
- tempfile, err = os.CreateTemp("", "gitlab-workhorse-coupledWriter")
- if err != nil {
- return nil, err
- }
- if err := os.Remove(tempfile.Name()); err != nil {
- tempfile.Close()
- return nil, err
- }
-
- return tempfile, nil
-}
diff --git a/workhorse/internal/helper/writeafterreader_test.go b/workhorse/internal/helper/writeafterreader_test.go
deleted file mode 100644
index c3da428184b..00000000000
--- a/workhorse/internal/helper/writeafterreader_test.go
+++ /dev/null
@@ -1,114 +0,0 @@
-package helper
-
-import (
- "bytes"
- "fmt"
- "io"
- "testing"
- "testing/iotest"
-)
-
-func TestBusyReader(t *testing.T) {
- testData := "test data"
- r := testReader(testData)
- br, _ := NewWriteAfterReader(r, &bytes.Buffer{})
-
- result, err := io.ReadAll(br)
- if err != nil {
- t.Fatal(err)
- }
-
- if string(result) != testData {
- t.Fatalf("expected %q, got %q", testData, result)
- }
-}
-
-func TestFirstWriteAfterReadDone(t *testing.T) {
- writeRecorder := &bytes.Buffer{}
- br, cw := NewWriteAfterReader(&bytes.Buffer{}, writeRecorder)
- if _, err := io.Copy(io.Discard, br); err != nil {
- t.Fatalf("copy from busyreader: %v", err)
- }
- testData := "test data"
- if _, err := io.Copy(cw, testReader(testData)); err != nil {
- t.Fatalf("copy test data: %v", err)
- }
- if err := cw.Flush(); err != nil {
- t.Fatalf("flush error: %v", err)
- }
- if result := writeRecorder.String(); result != testData {
- t.Fatalf("expected %q, got %q", testData, result)
- }
-}
-
-func TestWriteDelay(t *testing.T) {
- writeRecorder := &bytes.Buffer{}
- w := &complainingWriter{Writer: writeRecorder}
- br, cw := NewWriteAfterReader(&bytes.Buffer{}, w)
-
- testData1 := "1 test"
- if _, err := io.Copy(cw, testReader(testData1)); err != nil {
- t.Fatalf("error on first copy: %v", err)
- }
-
- // Unblock the coupled writer by draining the reader
- if _, err := io.Copy(io.Discard, br); err != nil {
- t.Fatalf("copy from busyreader: %v", err)
- }
- // Now it is no longer an error if 'w' receives a Write()
- w.CheerUp()
-
- testData2 := "2 experiment"
- if _, err := io.Copy(cw, testReader(testData2)); err != nil {
- t.Fatalf("error on second copy: %v", err)
- }
-
- if err := cw.Flush(); err != nil {
- t.Fatalf("flush error: %v", err)
- }
-
- expected := testData1 + testData2
- if result := writeRecorder.String(); result != expected {
- t.Fatalf("total write: expected %q, got %q", expected, result)
- }
-}
-
-func TestComplainingWriterSanity(t *testing.T) {
- recorder := &bytes.Buffer{}
- w := &complainingWriter{Writer: recorder}
-
- testData := "test data"
- if _, err := io.Copy(w, testReader(testData)); err == nil {
- t.Error("error expected, none received")
- }
-
- w.CheerUp()
- if _, err := io.Copy(w, testReader(testData)); err != nil {
- t.Errorf("copy after CheerUp: %v", err)
- }
-
- if result := recorder.String(); result != testData {
- t.Errorf("expected %q, got %q", testData, result)
- }
-}
-
-func testReader(data string) io.Reader {
- return iotest.OneByteReader(bytes.NewBuffer([]byte(data)))
-}
-
-type complainingWriter struct {
- happy bool
- io.Writer
-}
-
-func (comp *complainingWriter) Write(data []byte) (int, error) {
- if comp.happy {
- return comp.Writer.Write(data)
- }
-
- return 0, fmt.Errorf("I am unhappy about you wanting to write %q", data)
-}
-
-func (comp *complainingWriter) CheerUp() {
- comp.happy = true
-}
diff --git a/workhorse/internal/imageresizer/image_resizer.go b/workhorse/internal/imageresizer/image_resizer.go
index 092369cd2af..72f345239a6 100644
--- a/workhorse/internal/imageresizer/image_resizer.go
+++ b/workhorse/internal/imageresizer/image_resizer.go
@@ -20,7 +20,8 @@ import (
"gitlab.com/gitlab-org/labkit/tracing"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/config"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/command"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/log"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/senddata"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/transport"
@@ -191,7 +192,7 @@ func (r *Resizer) Inject(w http.ResponseWriter, req *http.Request, paramsData st
// We need to log this separately since the subsequent steps might add other failures.
log.WithRequest(req).WithFields(logFields(start, params, &outcome)).WithError(err).Error()
}
- defer helper.CleanUpProcessGroup(resizeCmd)
+ defer command.KillProcessGroup(resizeCmd)
w.Header().Del("Content-Length")
outcome.bytesWritten, err = serveImage(imageReader, w, resizeCmd)
@@ -419,7 +420,7 @@ func handleOutcome(w http.ResponseWriter, req *http.Request, startTime time.Time
switch outcome.status {
case statusRequestFailure:
if outcome.bytesWritten <= 0 {
- helper.Fail500WithFields(w, req, outcome.err, fields)
+ fail.Request(w, req, outcome.err, fail.WithFields(fields))
} else {
log.WithError(outcome.err).Error(outcome.status)
}
diff --git a/workhorse/internal/log/logging.go b/workhorse/internal/log/logging.go
index 80c09c1bf02..004ae8a8604 100644
--- a/workhorse/internal/log/logging.go
+++ b/workhorse/internal/log/logging.go
@@ -8,7 +8,7 @@ import (
"gitlab.com/gitlab-org/labkit/mask"
"golang.org/x/net/context"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/exception"
)
type Fields = log.Fields
@@ -79,10 +79,18 @@ func Error(args ...interface{}) {
NewBuilder().Error(args...)
}
+func (b *Builder) trackException() {
+ if b.err != nil {
+ exception.Track(b.req, b.err, b.fields)
+ }
+}
+
func (b *Builder) Error(args ...interface{}) {
+ b.trackException()
b.entry.Error(args...)
+}
- if b.req != nil && b.err != nil {
- helper.CaptureRavenError(b.req, b.err, b.fields)
- }
+func (b *Builder) Fatal(args ...interface{}) {
+ b.trackException()
+ b.entry.Fatal(args...)
}
diff --git a/workhorse/internal/proxy/proxy.go b/workhorse/internal/proxy/proxy.go
index 06e2c65a6a8..a7c3b322da7 100644
--- a/workhorse/internal/proxy/proxy.go
+++ b/workhorse/internal/proxy/proxy.go
@@ -8,6 +8,7 @@ import (
"time"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/nginx"
)
var (
@@ -45,6 +46,14 @@ func NewProxy(myURL *url.URL, version string, roundTripper http.RoundTripper, op
u.Path = ""
p.reverseProxy = httputil.NewSingleHostReverseProxy(&u)
p.reverseProxy.Transport = roundTripper
+ chainDirector(p.reverseProxy, func(r *http.Request) {
+ r.Header.Set("Gitlab-Workhorse", p.Version)
+ r.Header.Set("Gitlab-Workhorse-Proxy-Start", fmt.Sprintf("%d", time.Now().UnixNano()))
+
+ for k, v := range p.customHeaders {
+ r.Header.Set(k, v)
+ }
+ })
for _, option := range options {
option(&p)
@@ -54,10 +63,7 @@ func NewProxy(myURL *url.URL, version string, roundTripper http.RoundTripper, op
// because of https://github.com/golang/go/issues/28168, the
// upstream won't receive the expected Host header unless this
// is forced in the Director func here
- previousDirector := p.reverseProxy.Director
- p.reverseProxy.Director = func(request *http.Request) {
- previousDirector(request)
-
+ chainDirector(p.reverseProxy, func(request *http.Request) {
// send original host along for the upstream
// to know it's being proxied under a different Host
// (for redirects and other stuff that depends on this)
@@ -66,27 +72,23 @@ func NewProxy(myURL *url.URL, version string, roundTripper http.RoundTripper, op
// override the Host with the target
request.Host = request.URL.Host
- }
+ })
}
return &p
}
-func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- // Clone request
- req := *r
- req.Header = helper.HeaderClone(r.Header)
-
- // Set Workhorse version
- req.Header.Set("Gitlab-Workhorse", p.Version)
- req.Header.Set("Gitlab-Workhorse-Proxy-Start", fmt.Sprintf("%d", time.Now().UnixNano()))
-
- for k, v := range p.customHeaders {
- req.Header.Set(k, v)
+func chainDirector(rp *httputil.ReverseProxy, nextDirector func(*http.Request)) {
+ previous := rp.Director
+ rp.Director = func(r *http.Request) {
+ previous(r)
+ nextDirector(r)
}
+}
+func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if p.AllowResponseBuffering {
- helper.AllowResponseBuffering(w)
+ nginx.AllowResponseBuffering(w)
}
// If the ultimate client disconnects when the response isn't fully written
@@ -100,5 +102,5 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}()
- p.reverseProxy.ServeHTTP(w, &req)
+ p.reverseProxy.ServeHTTP(w, r)
}
diff --git a/workhorse/internal/queueing/requests.go b/workhorse/internal/queueing/requests.go
index 34d4c985f53..c3df614de41 100644
--- a/workhorse/internal/queueing/requests.go
+++ b/workhorse/internal/queueing/requests.go
@@ -6,7 +6,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
)
const (
@@ -46,7 +46,7 @@ func QueueRequests(name string, h http.Handler, limit, queueLimit uint, queueTim
http.Error(w, "Service Unavailable", http.StatusServiceUnavailable)
default:
- helper.Fail500(w, r, err)
+ fail.Request(w, r, err)
}
})
diff --git a/workhorse/internal/senddata/senddata.go b/workhorse/internal/senddata/senddata.go
index 190a37c1a15..4cb96890ee2 100644
--- a/workhorse/internal/senddata/senddata.go
+++ b/workhorse/internal/senddata/senddata.go
@@ -5,6 +5,7 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/headers"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/nginx"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/senddata/contentprocessor"
"github.com/prometheus/client_golang/prometheus"
@@ -88,7 +89,7 @@ func (s *sendDataResponseWriter) tryInject() bool {
for _, injecter := range s.injecters {
if injecter.Match(header) {
s.hijacked = true
- helper.DisableResponseBuffering(s.rw)
+ nginx.DisableResponseBuffering(s.rw)
crw := helper.NewCountingResponseWriter(s.rw)
injecter.Inject(crw, s.req, header)
sendDataResponses.WithLabelValues(injecter.Name()).Inc()
diff --git a/workhorse/internal/sendfile/sendfile.go b/workhorse/internal/sendfile/sendfile.go
index 07b1789445a..70d93f1109c 100644
--- a/workhorse/internal/sendfile/sendfile.go
+++ b/workhorse/internal/sendfile/sendfile.go
@@ -20,6 +20,8 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/headers"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/nginx"
)
var (
@@ -93,7 +95,7 @@ func (s *sendFileResponseWriter) WriteHeader(status int) {
s.hijacked = true
// Serve the file
- helper.DisableResponseBuffering(s.rw)
+ nginx.DisableResponseBuffering(s.rw)
sendFileFromDisk(s.rw, s.req, file)
return
}
@@ -129,7 +131,7 @@ func sendFileFromDisk(w http.ResponseWriter, r *http.Request, file string) {
if contentTypeHeaderPresent {
data, err := io.ReadAll(io.LimitReader(content, headers.MaxDetectSize))
if err != nil {
- helper.Fail500(w, r, fmt.Errorf("content type detection: %v", err))
+ fail.Request(w, r, fmt.Errorf("content type detection: %v", err))
return
}
diff --git a/workhorse/internal/sendurl/sendurl.go b/workhorse/internal/sendurl/sendurl.go
index 8e679c6b475..e689fc84a0f 100644
--- a/workhorse/internal/sendurl/sendurl.go
+++ b/workhorse/internal/sendurl/sendurl.go
@@ -10,7 +10,7 @@ import (
"gitlab.com/gitlab-org/labkit/mask"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/log"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/senddata"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/transport"
@@ -83,7 +83,7 @@ func (e *entry) Inject(w http.ResponseWriter, r *http.Request, sendData string)
defer sendURLOpenRequests.Dec()
if err := e.Unpack(&params, sendData); err != nil {
- helper.Fail500(w, r, fmt.Errorf("SendURL: unpack sendData: %v", err))
+ fail.Request(w, r, fmt.Errorf("SendURL: unpack sendData: %v", err))
return
}
@@ -94,7 +94,7 @@ func (e *entry) Inject(w http.ResponseWriter, r *http.Request, sendData string)
if params.URL == "" {
sendURLRequestsInvalidData.Inc()
- helper.Fail500(w, r, fmt.Errorf("SendURL: URL is empty"))
+ fail.Request(w, r, fmt.Errorf("SendURL: URL is empty"))
return
}
@@ -102,7 +102,7 @@ func (e *entry) Inject(w http.ResponseWriter, r *http.Request, sendData string)
newReq, err := http.NewRequest("GET", params.URL, nil)
if err != nil {
sendURLRequestsInvalidData.Inc()
- helper.Fail500(w, r, fmt.Errorf("SendURL: NewRequest: %v", err))
+ fail.Request(w, r, fmt.Errorf("SendURL: NewRequest: %v", err))
return
}
newReq = newReq.WithContext(r.Context())
@@ -120,7 +120,7 @@ func (e *entry) Inject(w http.ResponseWriter, r *http.Request, sendData string)
}
if err != nil {
sendURLRequestsRequestFailed.Inc()
- helper.Fail500(w, r, fmt.Errorf("SendURL: Do request: %v", err))
+ fail.Request(w, r, fmt.Errorf("SendURL: Do request: %v", err))
return
}
diff --git a/workhorse/internal/staticpages/deploy_page.go b/workhorse/internal/staticpages/deploy_page.go
index 3dc2d982981..ca0931addd0 100644
--- a/workhorse/internal/staticpages/deploy_page.go
+++ b/workhorse/internal/staticpages/deploy_page.go
@@ -4,8 +4,6 @@ import (
"net/http"
"os"
"path/filepath"
-
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
)
func (s *Static) DeployPage(handler http.Handler) http.Handler {
@@ -18,7 +16,7 @@ func (s *Static) DeployPage(handler http.Handler) http.Handler {
return
}
- helper.SetNoCacheHeaders(w.Header())
+ setNoCacheHeaders(w.Header())
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusOK)
w.Write(data)
diff --git a/workhorse/internal/staticpages/error_pages.go b/workhorse/internal/staticpages/error_pages.go
index e0ba7a5ceef..d1aa7603658 100644
--- a/workhorse/internal/staticpages/error_pages.go
+++ b/workhorse/internal/staticpages/error_pages.go
@@ -9,8 +9,6 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
-
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
)
var (
@@ -84,7 +82,7 @@ func (s *errorPageResponseWriter) WriteHeader(status int) {
s.hijacked = true
staticErrorResponses.WithLabelValues(fmt.Sprintf("%d", s.status)).Inc()
- helper.SetNoCacheHeaders(s.rw.Header())
+ setNoCacheHeaders(s.rw.Header())
s.rw.Header().Set("Content-Type", contentType)
s.rw.Header().Set("Content-Length", fmt.Sprintf("%d", len(data)))
s.rw.Header().Del("Transfer-Encoding")
diff --git a/workhorse/internal/staticpages/static.go b/workhorse/internal/staticpages/static.go
index 5b804e4d644..c5c0573090b 100644
--- a/workhorse/internal/staticpages/static.go
+++ b/workhorse/internal/staticpages/static.go
@@ -1,6 +1,14 @@
package staticpages
+import "net/http"
+
type Static struct {
DocumentRoot string
Exclude []string
}
+
+func setNoCacheHeaders(header http.Header) {
+ header.Set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate")
+ header.Set("Pragma", "no-cache")
+ header.Set("Expires", "Fri, 01 Jan 1990 00:00:00 GMT")
+}
diff --git a/workhorse/internal/upload/artifacts_uploader.go b/workhorse/internal/upload/artifacts_uploader.go
index a8c944a1d33..c83874e7293 100644
--- a/workhorse/internal/upload/artifacts_uploader.go
+++ b/workhorse/internal/upload/artifacts_uploader.go
@@ -16,7 +16,7 @@ import (
"gitlab.com/gitlab-org/labkit/log"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/api"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/command"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/lsif_transformer/parser"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/upload/destination"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/zipartifacts"
@@ -83,7 +83,7 @@ func (a *artifactsUploadProcessor) generateMetadataFromZip(ctx context.Context,
if err := zipMd.Start(); err != nil {
return nil, err
}
- defer helper.CleanUpProcessGroup(zipMd)
+ defer command.KillProcessGroup(zipMd)
fh, err := destination.Upload(ctx, zipMdOut, -1, "metadata.gz", metaOpts)
if err != nil {
@@ -91,7 +91,7 @@ func (a *artifactsUploadProcessor) generateMetadataFromZip(ctx context.Context,
}
if err := zipMd.Wait(); err != nil {
- st, ok := helper.ExitStatus(err)
+ st, ok := command.ExitStatus(err)
if !ok {
return nil, err
diff --git a/workhorse/internal/upload/body_uploader.go b/workhorse/internal/upload/body_uploader.go
index 4b5152c283c..4733415c2c1 100644
--- a/workhorse/internal/upload/body_uploader.go
+++ b/workhorse/internal/upload/body_uploader.go
@@ -8,7 +8,7 @@ import (
"strings"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/api"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/upload/destination"
)
@@ -19,20 +19,20 @@ func RequestBody(rails PreAuthorizer, h http.Handler, p Preparer) http.Handler {
return rails.PreAuthorizeHandler(func(w http.ResponseWriter, r *http.Request, a *api.Response) {
opts, err := p.Prepare(a)
if err != nil {
- helper.Fail500(w, r, fmt.Errorf("RequestBody: preparation failed: %v", err))
+ fail.Request(w, r, fmt.Errorf("RequestBody: preparation failed: %v", err))
return
}
fh, err := destination.Upload(r.Context(), r.Body, r.ContentLength, "upload", opts)
if err != nil {
- helper.Fail500(w, r, fmt.Errorf("RequestBody: upload failed: %v", err))
+ fail.Request(w, r, fmt.Errorf("RequestBody: upload failed: %v", err))
return
}
data := url.Values{}
fields, err := fh.GitLabFinalizeFields("file")
if err != nil {
- helper.Fail500(w, r, fmt.Errorf("RequestBody: finalize fields failed: %v", err))
+ fail.Request(w, r, fmt.Errorf("RequestBody: finalize fields failed: %v", err))
return
}
@@ -49,7 +49,7 @@ func RequestBody(rails PreAuthorizer, h http.Handler, p Preparer) http.Handler {
sft := SavedFileTracker{Request: r}
sft.Track("file", fh.LocalPath)
if err := sft.Finalize(r.Context()); err != nil {
- helper.Fail500(w, r, fmt.Errorf("RequestBody: finalize failed: %v", err))
+ fail.Request(w, r, fmt.Errorf("RequestBody: finalize failed: %v", err))
return
}
diff --git a/workhorse/internal/upload/rewrite.go b/workhorse/internal/upload/rewrite.go
index 7b9ac6b996e..ad9623f569c 100644
--- a/workhorse/internal/upload/rewrite.go
+++ b/workhorse/internal/upload/rewrite.go
@@ -67,11 +67,8 @@ func rewriteFormFilesFromMultipart(r *http.Request, writer *multipart.Writer, fi
// Create multipart reader
reader, err := r.MultipartReader()
if err != nil {
- if err == http.ErrNotMultipart {
- // We want to be able to recognize http.ErrNotMultipart elsewhere so no fmt.Errorf
- return http.ErrNotMultipart
- }
- return fmt.Errorf("get multipart reader: %v", err)
+ // We want to be able to recognize these errors elsewhere so no fmt.Errorf
+ return err
}
multipartUploadRequests.WithLabelValues(filter.Name()).Inc()
diff --git a/workhorse/internal/upload/uploads.go b/workhorse/internal/upload/uploads.go
index f214e1ac297..a3072aa5d00 100644
--- a/workhorse/internal/upload/uploads.go
+++ b/workhorse/internal/upload/uploads.go
@@ -12,7 +12,7 @@ import (
"github.com/golang-jwt/jwt/v4"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/api"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/upload/destination"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/upload/exif"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/zipartifacts"
@@ -51,23 +51,23 @@ func interceptMultipartFiles(w http.ResponseWriter, r *http.Request, h http.Hand
err := rewriteFormFilesFromMultipart(r, writer, filter, fa, p)
if err != nil {
switch err {
- case ErrInjectedClientParam:
- helper.CaptureAndFail(w, r, err, "Bad Request", http.StatusBadRequest)
- case ErrTooManyFilesUploaded:
- helper.CaptureAndFail(w, r, err, err.Error(), http.StatusBadRequest)
case http.ErrNotMultipart:
h.ServeHTTP(w, r)
- case destination.ErrEntityTooLarge:
- helper.RequestEntityTooLarge(w, r, err)
- case zipartifacts.ErrBadMetadata:
- helper.RequestEntityTooLarge(w, r, err)
+ case ErrInjectedClientParam, http.ErrMissingBoundary:
+ fail.Request(w, r, err, fail.WithStatus(http.StatusBadRequest))
+ case ErrTooManyFilesUploaded:
+ fail.Request(w, r, err, fail.WithStatus(http.StatusBadRequest), fail.WithBody(err.Error()))
+ case destination.ErrEntityTooLarge, zipartifacts.ErrBadMetadata:
+ fail.Request(w, r, err, fail.WithStatus(http.StatusRequestEntityTooLarge))
case exif.ErrRemovingExif:
- helper.CaptureAndFail(w, r, err, "Failed to process image", http.StatusUnprocessableEntity)
+ fail.Request(w, r, err, fail.WithStatus(http.StatusUnprocessableEntity),
+ fail.WithBody("Failed to process image"))
default:
if errors.Is(err, context.DeadlineExceeded) {
- helper.CaptureAndFail(w, r, err, "deadline exceeded", http.StatusGatewayTimeout)
+ fail.Request(w, r, err, fail.WithStatus(http.StatusGatewayTimeout),
+ fail.WithBody("deadline exceeded"))
} else {
- helper.Fail500(w, r, fmt.Errorf("handleFileUploads: extract files from multipart: %v", err))
+ fail.Request(w, r, fmt.Errorf("handleFileUploads: extract files from multipart: %v", err))
}
}
return
@@ -82,7 +82,7 @@ func interceptMultipartFiles(w http.ResponseWriter, r *http.Request, h http.Hand
r.Header.Set("Content-Type", writer.FormDataContentType())
if err := filter.Finalize(r.Context()); err != nil {
- helper.Fail500(w, r, fmt.Errorf("handleFileUploads: Finalize: %v", err))
+ fail.Request(w, r, fmt.Errorf("handleFileUploads: Finalize: %v", err))
return
}
diff --git a/workhorse/internal/upload/uploads_test.go b/workhorse/internal/upload/uploads_test.go
index 3655e9fc8c9..cc786079e36 100644
--- a/workhorse/internal/upload/uploads_test.go
+++ b/workhorse/internal/upload/uploads_test.go
@@ -352,6 +352,18 @@ func TestInvalidFileNames(t *testing.T) {
}
}
+func TestBadMultipartHeader(t *testing.T) {
+ httpRequest, err := http.NewRequest("POST", "/example", bytes.NewReader(nil))
+ require.NoError(t, err)
+
+ // Invalid header: missing boundary
+ httpRequest.Header.Set("Content-Type", "multipart/form-data")
+
+ response := httptest.NewRecorder()
+ testInterceptMultipartFiles(t, response, httpRequest, nilHandler, &SavedFileTracker{Request: httpRequest})
+ require.Equal(t, 400, response.Code)
+}
+
func TestContentDispositionRewrite(t *testing.T) {
testhelper.ConfigureSecret()
diff --git a/workhorse/internal/upstream/handlers.go b/workhorse/internal/upstream/handlers.go
index 5974170e172..85fee0bf7e2 100644
--- a/workhorse/internal/upstream/handlers.go
+++ b/workhorse/internal/upstream/handlers.go
@@ -6,7 +6,7 @@ import (
"io"
"net/http"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/fail"
)
func contentEncodingHandler(h http.Handler) http.Handler {
@@ -26,7 +26,7 @@ func contentEncodingHandler(h http.Handler) http.Handler {
}
if err != nil {
- helper.Fail500(w, r, fmt.Errorf("contentEncodingHandler: %v", err))
+ fail.Request(w, r, fmt.Errorf("contentEncodingHandler: %v", err))
return
}
defer body.Close()
diff --git a/workhorse/internal/upstream/routes.go b/workhorse/internal/upstream/routes.go
index c47053ad682..982f3a5b5f8 100644
--- a/workhorse/internal/upstream/routes.go
+++ b/workhorse/internal/upstream/routes.go
@@ -425,7 +425,7 @@ func configureRoutes(u *upstream) {
func denyWebsocket(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if websocket.IsWebSocketUpgrade(r) {
- helper.HTTPError(w, r, "websocket upgrade not allowed", http.StatusBadRequest)
+ httpError(w, r, "websocket upgrade not allowed", http.StatusBadRequest)
return
}
diff --git a/workhorse/internal/upstream/upstream.go b/workhorse/internal/upstream/upstream.go
index 248f190e316..34fe300192f 100644
--- a/workhorse/internal/upstream/upstream.go
+++ b/workhorse/internal/upstream/upstream.go
@@ -16,6 +16,7 @@ import (
"net/url"
"strings"
+ "github.com/sebest/xff"
"github.com/sirupsen/logrus"
"gitlab.com/gitlab-org/labkit/correlation"
@@ -24,6 +25,7 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/builds"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/config"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/nginx"
proxypkg "gitlab.com/gitlab-org/gitlab/workhorse/internal/proxy"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/rejectmethods"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/upload"
@@ -124,19 +126,19 @@ func (u *upstream) configureURLPrefix() {
}
func (u *upstream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- helper.FixRemoteAddr(r)
+ fixRemoteAddr(r)
- helper.DisableResponseBuffering(w)
+ nginx.DisableResponseBuffering(w)
// Drop RequestURI == "*" (FIXME: why?)
if r.RequestURI == "*" {
- helper.HTTPError(w, r, "Connection upgrade not allowed", http.StatusBadRequest)
+ httpError(w, r, "Connection upgrade not allowed", http.StatusBadRequest)
return
}
// Disallow connect
if r.Method == "CONNECT" {
- helper.HTTPError(w, r, "CONNECT not allowed", http.StatusBadRequest)
+ httpError(w, r, "CONNECT not allowed", http.StatusBadRequest)
return
}
@@ -144,7 +146,7 @@ func (u *upstream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
URIPath := urlprefix.CleanURIPath(r.URL.EscapedPath())
prefix := u.URLPrefix
if !prefix.Match(URIPath) {
- helper.HTTPError(w, r, fmt.Sprintf("Not found %q", URIPath), http.StatusNotFound)
+ httpError(w, r, fmt.Sprintf("Not found %q", URIPath), http.StatusNotFound)
return
}
@@ -155,7 +157,7 @@ func (u *upstream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if route == nil {
// The protocol spec in git/Documentation/technical/http-protocol.txt
// says we must return 403 if no matching service is found.
- helper.HTTPError(w, r, "Forbidden", http.StatusForbidden)
+ httpError(w, r, "Forbidden", http.StatusForbidden)
return
}
@@ -275,3 +277,21 @@ func (u *upstream) updateGeoProxyFieldsFromData(geoProxyData *apipkg.GeoProxyDat
u.geoProxyCableRoute = u.wsRoute(`^/-/cable\z`, geoProxyUpstream)
u.geoProxyRoute = u.route("", "", geoProxyUpstream, withGeoProxy())
}
+
+func httpError(w http.ResponseWriter, r *http.Request, error string, code int) {
+ if r.ProtoAtLeast(1, 1) {
+ // Force client to disconnect if we render request error
+ w.Header().Set("Connection", "close")
+ }
+
+ http.Error(w, error, code)
+}
+
+func fixRemoteAddr(r *http.Request) {
+ // Unix domain sockets have a remote addr of @. This will make the
+ // xff package lookup the X-Forwarded-For address if available.
+ if r.RemoteAddr == "@" {
+ r.RemoteAddr = "127.0.0.1:0"
+ }
+ r.RemoteAddr = xff.GetRemoteAddr(r)
+}
diff --git a/workhorse/internal/upstream/upstream_test.go b/workhorse/internal/upstream/upstream_test.go
index 7ab3e67116f..705e40c74d5 100644
--- a/workhorse/internal/upstream/upstream_test.go
+++ b/workhorse/internal/upstream/upstream_test.go
@@ -435,3 +435,33 @@ func startWorkhorseServer(railsServerURL string, enableGeoProxyFeature bool) (*h
return ws, ws.Close, waitForNextApiPoll
}
+
+func TestFixRemoteAddr(t *testing.T) {
+ testCases := []struct {
+ initial string
+ forwarded string
+ expected string
+ }{
+ {initial: "@", forwarded: "", expected: "127.0.0.1:0"},
+ {initial: "@", forwarded: "18.245.0.1", expected: "18.245.0.1:0"},
+ {initial: "@", forwarded: "127.0.0.1", expected: "127.0.0.1:0"},
+ {initial: "@", forwarded: "192.168.0.1", expected: "127.0.0.1:0"},
+ {initial: "192.168.1.1:0", forwarded: "", expected: "192.168.1.1:0"},
+ {initial: "192.168.1.1:0", forwarded: "18.245.0.1", expected: "18.245.0.1:0"},
+ }
+
+ for _, tc := range testCases {
+ req, err := http.NewRequest("POST", "unix:///tmp/test.socket/info/refs", nil)
+ require.NoError(t, err)
+
+ req.RemoteAddr = tc.initial
+
+ if tc.forwarded != "" {
+ req.Header.Add("X-Forwarded-For", tc.forwarded)
+ }
+
+ fixRemoteAddr(req)
+
+ require.Equal(t, tc.expected, req.RemoteAddr)
+ }
+}
diff --git a/workhorse/main_test.go b/workhorse/main_test.go
index 5ebc26c7ac7..382fb16a16c 100644
--- a/workhorse/main_test.go
+++ b/workhorse/main_test.go
@@ -29,6 +29,7 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/config"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/gitaly"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/nginx"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/secret"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/testhelper"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/upstream"
@@ -491,9 +492,9 @@ func TestSendURLForArtifacts(t *testing.T) {
transferEncoding []string
contentLength int
}{
- {"No content-length, chunked TE", chunkedHandler, []string{"chunked"}, -1}, // Case 3 in https://tools.ietf.org/html/rfc7230#section-3.3.2
- {"Known content-length, identity TE", regularHandler, nil, len(expectedBody)}, // Case 5 in https://tools.ietf.org/html/rfc7230#section-3.3.2
- {"No content-length, identity TE", rawHandler, []string{"chunked"}, -1}, // Case 7 in https://tools.ietf.org/html/rfc7230#section-3.3.2
+ {"No content-length, chunked TE", chunkedHandler, []string{"chunked"}, -1}, // Case 3 in https://www.rfc-editor.org/rfc/rfc7230#section-3.3.2
+ {"Known content-length, identity TE", regularHandler, nil, len(expectedBody)}, // Case 5 in https://www.rfc-editor.org/rfc/rfc7230#section-3.3.2
+ {"No content-length, identity TE", rawHandler, []string{"chunked"}, -1}, // Case 7 in https://www.rfc-editor.org/rfc/rfc7230#section-3.3.2
} {
t.Run(tc.name, func(t *testing.T) {
server := httptest.NewServer(tc.handler)
@@ -853,7 +854,7 @@ func httpPost(t *testing.T, url string, headers map[string]string, reqBody io.Re
}
func requireNginxResponseBuffering(t *testing.T, expected string, resp *http.Response, msgAndArgs ...interface{}) {
- actual := resp.Header.Get(helper.NginxResponseBufferHeader)
+ actual := resp.Header.Get(nginx.ResponseBufferHeader)
require.Equal(t, expected, actual, msgAndArgs...)
}
diff --git a/workhorse/raven.go b/workhorse/raven.go
index 2db24b0b3d4..582900b15f4 100644
--- a/workhorse/raven.go
+++ b/workhorse/raven.go
@@ -6,7 +6,7 @@ import (
raven "github.com/getsentry/raven-go"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper/exception"
)
func wrapRaven(h http.Handler) http.Handler {
@@ -30,7 +30,7 @@ func wrapRaven(h http.Handler) http.Handler {
func(w http.ResponseWriter, r *http.Request) {
defer func() {
if p := recover(); p != nil {
- helper.CleanHeadersForRaven(r)
+ exception.CleanHeaders(r)
panic(p)
}
}()
diff --git a/yarn.lock b/yarn.lock
index 1a2f200dd15..f158fd9ec26 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -80,33 +80,33 @@
tslib "^2.3.0"
zen-observable-ts "^1.2.0"
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.18.6":
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
dependencies:
"@babel/highlight" "^7.18.6"
-"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.10":
- version "7.17.10"
- resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.10.tgz#711dc726a492dfc8be8220028b1b92482362baab"
- integrity sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==
+"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.10", "@babel/compat-data@^7.19.0":
+ version "7.19.0"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.0.tgz#2a592fd89bacb1fcde68de31bee4f2f2dacb0e86"
+ integrity sha512-y5rqgTTPTmaF5e2nVhOxw+Ur9HDJLsWb6U/KpgUzRZEdPfE6VOubXBKLdbcUTijzRptednSBDQbYZBOSqJxpJw==
-"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.17.0", "@babel/core@^7.18.5", "@babel/core@^7.7.2", "@babel/core@^7.8.0":
- version "7.18.5"
- resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.5.tgz#c597fa680e58d571c28dda9827669c78cdd7f000"
- integrity sha512-MGY8vg3DxMnctw0LdvSEojOsumc70g0t18gNyUdAZqB1Rpd1Bqo/svHGvt+UJ6JcGX+DIekGFDxxIWofBxLCnQ==
+"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.17.0", "@babel/core@^7.18.5":
+ version "7.19.0"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.0.tgz#d2f5f4f2033c00de8096be3c9f45772563e150c3"
+ integrity sha512-reM4+U7B9ss148rh2n1Qs9ASS+w94irYXga7c2jaQv9RVzpS7Mv1a9rnYYwuDa45G+DkORt9g6An2k/V4d9LbQ==
dependencies:
"@ampproject/remapping" "^2.1.0"
- "@babel/code-frame" "^7.16.7"
- "@babel/generator" "^7.18.2"
- "@babel/helper-compilation-targets" "^7.18.2"
- "@babel/helper-module-transforms" "^7.18.0"
- "@babel/helpers" "^7.18.2"
- "@babel/parser" "^7.18.5"
- "@babel/template" "^7.16.7"
- "@babel/traverse" "^7.18.5"
- "@babel/types" "^7.18.4"
+ "@babel/code-frame" "^7.18.6"
+ "@babel/generator" "^7.19.0"
+ "@babel/helper-compilation-targets" "^7.19.0"
+ "@babel/helper-module-transforms" "^7.19.0"
+ "@babel/helpers" "^7.19.0"
+ "@babel/parser" "^7.19.0"
+ "@babel/template" "^7.18.10"
+ "@babel/traverse" "^7.19.0"
+ "@babel/types" "^7.19.0"
convert-source-map "^1.7.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
@@ -129,13 +129,13 @@
dependencies:
eslint-rule-composer "^0.3.0"
-"@babel/generator@^7.18.2", "@babel/generator@^7.7.2":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.2.tgz#33873d6f89b21efe2da63fe554460f3df1c5880d"
- integrity sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==
+"@babel/generator@^7.19.0", "@babel/generator@^7.7.2":
+ version "7.19.0"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.0.tgz#785596c06425e59334df2ccee63ab166b738419a"
+ integrity sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==
dependencies:
- "@babel/types" "^7.18.2"
- "@jridgewell/gen-mapping" "^0.3.0"
+ "@babel/types" "^7.19.0"
+ "@jridgewell/gen-mapping" "^0.3.2"
jsesc "^2.5.1"
"@babel/helper-annotate-as-pure@^7.16.7":
@@ -153,13 +153,13 @@
"@babel/helper-explode-assignable-expression" "^7.16.7"
"@babel/types" "^7.16.7"
-"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.10", "@babel/helper-compilation-targets@^7.18.2":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.2.tgz#67a85a10cbd5fc7f1457fec2e7f45441dc6c754b"
- integrity sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ==
+"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.10", "@babel/helper-compilation-targets@^7.18.2", "@babel/helper-compilation-targets@^7.19.0":
+ version "7.19.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.0.tgz#537ec8339d53e806ed422f1e06c8f17d55b96bb0"
+ integrity sha512-Ai5bNWXIvwDvWM7njqsG3feMlL9hCVQsPYXodsZyLwshYkZVJt59Gftau4VrE8S9IT9asd2uSP1hG6wCNw+sXA==
dependencies:
- "@babel/compat-data" "^7.17.10"
- "@babel/helper-validator-option" "^7.16.7"
+ "@babel/compat-data" "^7.19.0"
+ "@babel/helper-validator-option" "^7.18.6"
browserslist "^4.20.2"
semver "^6.3.0"
@@ -198,10 +198,10 @@
resolve "^1.14.2"
semver "^6.1.2"
-"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.18.2":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz#8a6d2dedb53f6bf248e31b4baf38739ee4a637bd"
- integrity sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ==
+"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.18.9":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
+ integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==
"@babel/helper-explode-assignable-expression@^7.16.7":
version "7.16.7"
@@ -210,20 +210,20 @@
dependencies:
"@babel/types" "^7.16.7"
-"@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.17.9":
- version "7.17.9"
- resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12"
- integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==
+"@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.17.9", "@babel/helper-function-name@^7.19.0":
+ version "7.19.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c"
+ integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==
dependencies:
- "@babel/template" "^7.16.7"
- "@babel/types" "^7.17.0"
+ "@babel/template" "^7.18.10"
+ "@babel/types" "^7.19.0"
-"@babel/helper-hoist-variables@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246"
- integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==
+"@babel/helper-hoist-variables@^7.16.7", "@babel/helper-hoist-variables@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678"
+ integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==
dependencies:
- "@babel/types" "^7.16.7"
+ "@babel/types" "^7.18.6"
"@babel/helper-member-expression-to-functions@^7.16.7", "@babel/helper-member-expression-to-functions@^7.17.7":
version "7.17.7"
@@ -232,26 +232,26 @@
dependencies:
"@babel/types" "^7.17.0"
-"@babel/helper-module-imports@^7.0.0-beta.49", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437"
- integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==
+"@babel/helper-module-imports@^7.0.0-beta.49", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
+ integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==
dependencies:
- "@babel/types" "^7.16.7"
+ "@babel/types" "^7.18.6"
-"@babel/helper-module-transforms@^7.18.0":
- version "7.18.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.0.tgz#baf05dec7a5875fb9235bd34ca18bad4e21221cd"
- integrity sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA==
+"@babel/helper-module-transforms@^7.18.0", "@babel/helper-module-transforms@^7.19.0":
+ version "7.19.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz#309b230f04e22c58c6a2c0c0c7e50b216d350c30"
+ integrity sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==
dependencies:
- "@babel/helper-environment-visitor" "^7.16.7"
- "@babel/helper-module-imports" "^7.16.7"
- "@babel/helper-simple-access" "^7.17.7"
- "@babel/helper-split-export-declaration" "^7.16.7"
- "@babel/helper-validator-identifier" "^7.16.7"
- "@babel/template" "^7.16.7"
- "@babel/traverse" "^7.18.0"
- "@babel/types" "^7.18.0"
+ "@babel/helper-environment-visitor" "^7.18.9"
+ "@babel/helper-module-imports" "^7.18.6"
+ "@babel/helper-simple-access" "^7.18.6"
+ "@babel/helper-split-export-declaration" "^7.18.6"
+ "@babel/helper-validator-identifier" "^7.18.6"
+ "@babel/template" "^7.18.10"
+ "@babel/traverse" "^7.19.0"
+ "@babel/types" "^7.19.0"
"@babel/helper-optimise-call-expression@^7.16.7":
version "7.16.7"
@@ -285,12 +285,12 @@
"@babel/traverse" "^7.16.7"
"@babel/types" "^7.16.7"
-"@babel/helper-simple-access@^7.17.7", "@babel/helper-simple-access@^7.18.2":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.2.tgz#4dc473c2169ac3a1c9f4a51cfcd091d1c36fcff9"
- integrity sha512-7LIrjYzndorDY88MycupkpQLKS1AFfsVRm2k/9PtKScSy5tZq0McZTj+DiMRynboZfIqOKvo03pmhTaUgiD6fQ==
+"@babel/helper-simple-access@^7.18.2", "@babel/helper-simple-access@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea"
+ integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==
dependencies:
- "@babel/types" "^7.18.2"
+ "@babel/types" "^7.18.6"
"@babel/helper-skip-transparent-expression-wrappers@^7.16.0":
version "7.16.0"
@@ -299,22 +299,27 @@
dependencies:
"@babel/types" "^7.16.0"
-"@babel/helper-split-export-declaration@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b"
- integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==
+"@babel/helper-split-export-declaration@^7.16.7", "@babel/helper-split-export-declaration@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075"
+ integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==
dependencies:
- "@babel/types" "^7.16.7"
+ "@babel/types" "^7.18.6"
+
+"@babel/helper-string-parser@^7.18.10":
+ version "7.18.10"
+ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56"
+ integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==
"@babel/helper-validator-identifier@^7.15.7", "@babel/helper-validator-identifier@^7.16.7", "@babel/helper-validator-identifier@^7.18.6":
- version "7.19.1"
- resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
- integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076"
+ integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==
-"@babel/helper-validator-option@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23"
- integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==
+"@babel/helper-validator-option@^7.16.7", "@babel/helper-validator-option@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8"
+ integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==
"@babel/helper-wrap-function@^7.16.8":
version "7.16.8"
@@ -326,14 +331,14 @@
"@babel/traverse" "^7.16.8"
"@babel/types" "^7.16.8"
-"@babel/helpers@^7.18.2":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.2.tgz#970d74f0deadc3f5a938bfa250738eb4ac889384"
- integrity sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg==
+"@babel/helpers@^7.19.0":
+ version "7.19.0"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.19.0.tgz#f30534657faf246ae96551d88dd31e9d1fa1fc18"
+ integrity sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==
dependencies:
- "@babel/template" "^7.16.7"
- "@babel/traverse" "^7.18.2"
- "@babel/types" "^7.18.2"
+ "@babel/template" "^7.18.10"
+ "@babel/traverse" "^7.19.0"
+ "@babel/types" "^7.19.0"
"@babel/highlight@^7.18.6":
version "7.18.6"
@@ -344,10 +349,10 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
-"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.16.8", "@babel/parser@^7.18.5":
- version "7.18.11"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9"
- integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==
+"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.8", "@babel/parser@^7.18.10", "@babel/parser@^7.19.0":
+ version "7.19.0"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.0.tgz#497fcafb1d5b61376959c1c338745ef0577aa02c"
+ integrity sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.17.12":
version "7.17.12"
@@ -978,37 +983,38 @@
dependencies:
regenerator-runtime "^0.13.4"
-"@babel/template@^7.16.7", "@babel/template@^7.3.3":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
- integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==
+"@babel/template@^7.16.7", "@babel/template@^7.18.10", "@babel/template@^7.3.3":
+ version "7.18.10"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71"
+ integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==
dependencies:
- "@babel/code-frame" "^7.16.7"
- "@babel/parser" "^7.16.7"
- "@babel/types" "^7.16.7"
+ "@babel/code-frame" "^7.18.6"
+ "@babel/parser" "^7.18.10"
+ "@babel/types" "^7.18.10"
-"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.18.0", "@babel/traverse@^7.18.2", "@babel/traverse@^7.18.5", "@babel/traverse@^7.7.2":
- version "7.18.5"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.5.tgz#94a8195ad9642801837988ab77f36e992d9a20cd"
- integrity sha512-aKXj1KT66sBj0vVzk6rEeAO6Z9aiiQ68wfDgge3nHhA/my6xMM/7HGQUNumKZaoa2qUPQ5whJG9aAifsxUKfLA==
+"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.19.0", "@babel/traverse@^7.7.2":
+ version "7.19.0"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.0.tgz#eb9c561c7360005c592cc645abafe0c3c4548eed"
+ integrity sha512-4pKpFRDh+utd2mbRC8JLnlsMUii3PMHjpL6a0SZ4NMZy7YFP9aXORxEhdMVOc9CpWtDF09IkciQLEhK7Ml7gRA==
dependencies:
- "@babel/code-frame" "^7.16.7"
- "@babel/generator" "^7.18.2"
- "@babel/helper-environment-visitor" "^7.18.2"
- "@babel/helper-function-name" "^7.17.9"
- "@babel/helper-hoist-variables" "^7.16.7"
- "@babel/helper-split-export-declaration" "^7.16.7"
- "@babel/parser" "^7.18.5"
- "@babel/types" "^7.18.4"
+ "@babel/code-frame" "^7.18.6"
+ "@babel/generator" "^7.19.0"
+ "@babel/helper-environment-visitor" "^7.18.9"
+ "@babel/helper-function-name" "^7.19.0"
+ "@babel/helper-hoist-variables" "^7.18.6"
+ "@babel/helper-split-export-declaration" "^7.18.6"
+ "@babel/parser" "^7.19.0"
+ "@babel/types" "^7.19.0"
debug "^4.1.0"
globals "^11.1.0"
-"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.18.0", "@babel/types@^7.18.2", "@babel/types@^7.18.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
- version "7.18.4"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.4.tgz#27eae9b9fd18e9dccc3f9d6ad051336f307be354"
- integrity sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==
+"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.18.10", "@babel/types@^7.18.2", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
+ version "7.19.0"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.0.tgz#75f21d73d73dc0351f3368d28db73465f4814600"
+ integrity sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==
dependencies:
- "@babel/helper-validator-identifier" "^7.16.7"
+ "@babel/helper-string-parser" "^7.18.10"
+ "@babel/helper-validator-identifier" "^7.18.6"
to-fast-properties "^2.0.0"
"@bcoe/v8-coverage@^0.2.3":
@@ -1041,20 +1047,32 @@
resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.1.tgz#b6b8d81780b9a9f6459f4bfe9226ac6aefaefe87"
integrity sha512-aG20vknL4/YjQF9BSV7ts4EWm/yrjagAN7OWBNmlbEOUiu0llj4OGrFoOKK3g2vey4/p2omKCoHrWtPxSwV3HA==
+"@cubejs-client/core@^0.31.0":
+ version "0.31.15"
+ resolved "https://registry.yarnpkg.com/@cubejs-client/core/-/core-0.31.15.tgz#db0ee90f5ba7f33a3fae6c81e5e13ab1cf2cd71b"
+ integrity sha512-VQqvvJn++nqO8aOr/dFtyUURNFYAlP3XlDiupiGLXmSsuUn0BuozJQAmJ5XxPPhvz5k9qBko7KkZuC6ikZTdcA==
+ dependencies:
+ core-js "^3.6.5"
+ cross-fetch "^3.0.2"
+ dayjs "^1.10.4"
+ ramda "^0.27.2"
+ url-search-params-polyfill "^7.0.0"
+ uuid "^8.3.2"
+
"@discoveryjs/json-ext@^0.5.0":
version "0.5.6"
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz#d5e0706cf8c6acd8c6032f8d54070af261bbbb2f"
integrity sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==
-"@esbuild/android-arm@0.15.9":
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.9.tgz#7e1221604ab88ed5021ead74fa8cca4405e1e431"
- integrity sha512-VZPy/ETF3fBG5PiinIkA0W/tlsvlEgJccyN2DzWZEl0DlVKRbu91PvY2D6Lxgluj4w9QtYHjOWjAT44C+oQ+EQ==
+"@esbuild/android-arm@0.15.18":
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.18.tgz#266d40b8fdcf87962df8af05b76219bc786b4f80"
+ integrity sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==
-"@esbuild/linux-loong64@0.15.9":
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.9.tgz#b658a97babf1f40783354af7039b84c3fdfc3fc3"
- integrity sha512-O+NfmkfRrb3uSsTa4jE3WApidSe3N5++fyOVGP1SmMZi4A3BZELkhUUvj5hwmMuNdlpzAZ8iAPz2vmcR7DCFQA==
+"@esbuild/linux-loong64@0.15.18":
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz#128b76ecb9be48b60cf5cfc1c63a4f00691a3239"
+ integrity sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==
"@eslint/eslintrc@^1.3.3":
version "1.3.3"
@@ -1099,6 +1117,11 @@
resolved "https://registry.yarnpkg.com/@gitlab/favicon-overlay/-/favicon-overlay-2.0.0.tgz#2f32d0b6a4d5b8ac44e2927083d9ab478a78c984"
integrity sha512-GNcORxXJ98LVGzOT9dDYKfbheqH6lNgPDD72lyXRnQIH7CjgGyos8i17aSBPq1f4s3zF3PyedFiAR4YEZbva2Q==
+"@gitlab/fonts@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@gitlab/fonts/-/fonts-1.0.1.tgz#5bcdf85d240fb053a7606a4cb79ab1c9b643f148"
+ integrity sha512-0e2a7x4A9ngEO/SsV/OQZYvuEAfLxKlS5Smn2zg+XI/6WlyFWNjmxDvdD58zav9Pjar1jrNPWMDLtGqV4U+QxQ==
+
"@gitlab/stylelint-config@4.1.0":
version "4.1.0"
resolved "https://registry.yarnpkg.com/@gitlab/stylelint-config/-/stylelint-config-4.1.0.tgz#bd431406c8f8725afba353652f08e42c3301a982"
@@ -1108,15 +1131,15 @@
stylelint-declaration-strict-value "1.8.0"
stylelint-scss "4.2.0"
-"@gitlab/svgs@3.8.0":
- version "3.8.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.8.0.tgz#bc7fa51e345e26cff56fdff629ea439adfa1e0cb"
- integrity sha512-DUWeG2Vx+1ntZ/1GT6S36ZOtXvM5Wm02MtDRrQS4GuOX4rkTeG9aoutSJuwQ2h9BNtxl0U/jkf5GVBxacj18XA==
+"@gitlab/svgs@3.14.0":
+ version "3.14.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.14.0.tgz#b32a673f08bbd5ba6d406bcf3abb6e7276271b6c"
+ integrity sha512-mQYtW9eGHY7cF6elsWd76hUF7F3NznyzrJJy5eXBHjvRdYBtyHmwkVmh1Cwr3S/2Sl8fPC+qk41a+Nm6n+1mRQ==
-"@gitlab/ui@49.10.0":
- version "49.10.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-49.10.0.tgz#54715c18d3e06f313b572c5b9b807622f7a35b19"
- integrity sha512-hBkU5TIdc2bzqe4P2X/BZXgQQQa+Sp4A5eWiKK+FVuCx5l1To2q4EyHHPMn292AJx7Y23qM2jP4GMphbs2wtDg==
+"@gitlab/ui@52.6.0":
+ version "52.6.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-52.6.0.tgz#3a2a8a1640dd92013784281929ecde37518de433"
+ integrity sha512-1s2LzOJWEGm0ik3NtjC3IE9wTA/1JlRnlTP/lpVD1HzHeQW9bgbdl1U/dNoZT5NTTcBoP4bdQusD+gdB5K7CfQ==
dependencies:
"@popperjs/core" "^2.11.2"
bootstrap-vue "2.20.1"
@@ -1132,10 +1155,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/visual-review-tools/-/visual-review-tools-1.7.3.tgz#9ea641146436da388ffbad25d7f2abe0df52c235"
integrity sha512-NMV++7Ew1FSBDN1xiZaauU9tfeSfgDHcOLpn+8bGpP+O5orUPm2Eu66R5eC5gkjBPaXosNAxNWtriee+aFk4+g==
-"@gitlab/web-ide@0.0.1-dev-20221114183058":
- version "0.0.1-dev-20221114183058"
- resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20221114183058.tgz#0b6e38900cf6986027919a6f7dd2166a7d9016e7"
- integrity sha512-M+IfbTGVPBtOfJrEkoNHEomhPoDrFFkuPFIHC1X5G0ixbXdep4UPX3jIAT/3TrOM331nrcZKX7fDMw6wn9jQnw==
+"@gitlab/web-ide@0.0.1-dev-20221217175648":
+ version "0.0.1-dev-20221217175648"
+ resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20221217175648.tgz#1042fa5a4facfef191aa8df8d8ac3b386c8d1334"
+ integrity sha512-njFkUVpIxyNJFSTY82RH5RzyndqyUkortLY87xKcfF0DeQttAOOfcD5jyyktp9ddRorj/ksWT2vNZ+qjEKwlIg==
"@graphql-eslint/eslint-plugin@3.12.0":
version "3.12.0"
@@ -1350,163 +1373,186 @@
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd"
integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==
-"@jest/console@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba"
- integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==
+"@jest/console@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.3.tgz#2030606ec03a18c31803b8a36382762e447655df"
+ integrity sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==
dependencies:
- "@jest/types" "^27.5.1"
+ "@jest/types" "^28.1.3"
"@types/node" "*"
chalk "^4.0.0"
- jest-message-util "^27.5.1"
- jest-util "^27.5.1"
+ jest-message-util "^28.1.3"
+ jest-util "^28.1.3"
slash "^3.0.0"
-"@jest/core@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626"
- integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==
+"@jest/core@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.3.tgz#0ebf2bd39840f1233cd5f2d1e6fc8b71bd5a1ac7"
+ integrity sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==
dependencies:
- "@jest/console" "^27.5.1"
- "@jest/reporters" "^27.5.1"
- "@jest/test-result" "^27.5.1"
- "@jest/transform" "^27.5.1"
- "@jest/types" "^27.5.1"
+ "@jest/console" "^28.1.3"
+ "@jest/reporters" "^28.1.3"
+ "@jest/test-result" "^28.1.3"
+ "@jest/transform" "^28.1.3"
+ "@jest/types" "^28.1.3"
"@types/node" "*"
ansi-escapes "^4.2.1"
chalk "^4.0.0"
- emittery "^0.8.1"
+ ci-info "^3.2.0"
exit "^0.1.2"
graceful-fs "^4.2.9"
- jest-changed-files "^27.5.1"
- jest-config "^27.5.1"
- jest-haste-map "^27.5.1"
- jest-message-util "^27.5.1"
- jest-regex-util "^27.5.1"
- jest-resolve "^27.5.1"
- jest-resolve-dependencies "^27.5.1"
- jest-runner "^27.5.1"
- jest-runtime "^27.5.1"
- jest-snapshot "^27.5.1"
- jest-util "^27.5.1"
- jest-validate "^27.5.1"
- jest-watcher "^27.5.1"
+ jest-changed-files "^28.1.3"
+ jest-config "^28.1.3"
+ jest-haste-map "^28.1.3"
+ jest-message-util "^28.1.3"
+ jest-regex-util "^28.0.2"
+ jest-resolve "^28.1.3"
+ jest-resolve-dependencies "^28.1.3"
+ jest-runner "^28.1.3"
+ jest-runtime "^28.1.3"
+ jest-snapshot "^28.1.3"
+ jest-util "^28.1.3"
+ jest-validate "^28.1.3"
+ jest-watcher "^28.1.3"
micromatch "^4.0.4"
+ pretty-format "^28.1.3"
rimraf "^3.0.0"
slash "^3.0.0"
strip-ansi "^6.0.0"
-"@jest/environment@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74"
- integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==
+"@jest/environment@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-28.1.3.tgz#abed43a6b040a4c24fdcb69eab1f97589b2d663e"
+ integrity sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==
dependencies:
- "@jest/fake-timers" "^27.5.1"
- "@jest/types" "^27.5.1"
+ "@jest/fake-timers" "^28.1.3"
+ "@jest/types" "^28.1.3"
"@types/node" "*"
- jest-mock "^27.5.1"
+ jest-mock "^28.1.3"
-"@jest/fake-timers@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74"
- integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==
+"@jest/expect-utils@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525"
+ integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==
dependencies:
- "@jest/types" "^27.5.1"
- "@sinonjs/fake-timers" "^8.0.1"
+ jest-get-type "^28.0.2"
+
+"@jest/expect@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-28.1.3.tgz#9ac57e1d4491baca550f6bdbd232487177ad6a72"
+ integrity sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==
+ dependencies:
+ expect "^28.1.3"
+ jest-snapshot "^28.1.3"
+
+"@jest/fake-timers@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-28.1.3.tgz#230255b3ad0a3d4978f1d06f70685baea91c640e"
+ integrity sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==
+ dependencies:
+ "@jest/types" "^28.1.3"
+ "@sinonjs/fake-timers" "^9.1.2"
"@types/node" "*"
- jest-message-util "^27.5.1"
- jest-mock "^27.5.1"
- jest-util "^27.5.1"
+ jest-message-util "^28.1.3"
+ jest-mock "^28.1.3"
+ jest-util "^28.1.3"
-"@jest/globals@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b"
- integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==
+"@jest/globals@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-28.1.3.tgz#a601d78ddc5fdef542728309894895b4a42dc333"
+ integrity sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==
dependencies:
- "@jest/environment" "^27.5.1"
- "@jest/types" "^27.5.1"
- expect "^27.5.1"
+ "@jest/environment" "^28.1.3"
+ "@jest/expect" "^28.1.3"
+ "@jest/types" "^28.1.3"
-"@jest/reporters@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04"
- integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==
+"@jest/reporters@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.3.tgz#9adf6d265edafc5fc4a434cfb31e2df5a67a369a"
+ integrity sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==
dependencies:
"@bcoe/v8-coverage" "^0.2.3"
- "@jest/console" "^27.5.1"
- "@jest/test-result" "^27.5.1"
- "@jest/transform" "^27.5.1"
- "@jest/types" "^27.5.1"
+ "@jest/console" "^28.1.3"
+ "@jest/test-result" "^28.1.3"
+ "@jest/transform" "^28.1.3"
+ "@jest/types" "^28.1.3"
+ "@jridgewell/trace-mapping" "^0.3.13"
"@types/node" "*"
chalk "^4.0.0"
collect-v8-coverage "^1.0.0"
exit "^0.1.2"
- glob "^7.1.2"
+ glob "^7.1.3"
graceful-fs "^4.2.9"
istanbul-lib-coverage "^3.0.0"
istanbul-lib-instrument "^5.1.0"
istanbul-lib-report "^3.0.0"
istanbul-lib-source-maps "^4.0.0"
istanbul-reports "^3.1.3"
- jest-haste-map "^27.5.1"
- jest-resolve "^27.5.1"
- jest-util "^27.5.1"
- jest-worker "^27.5.1"
+ jest-message-util "^28.1.3"
+ jest-util "^28.1.3"
+ jest-worker "^28.1.3"
slash "^3.0.0"
- source-map "^0.6.0"
string-length "^4.0.1"
+ strip-ansi "^6.0.0"
terminal-link "^2.0.0"
- v8-to-istanbul "^8.1.0"
+ v8-to-istanbul "^9.0.1"
-"@jest/source-map@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf"
- integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==
+"@jest/schemas@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905"
+ integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==
dependencies:
+ "@sinclair/typebox" "^0.24.1"
+
+"@jest/source-map@^28.1.2":
+ version "28.1.2"
+ resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-28.1.2.tgz#7fe832b172b497d6663cdff6c13b0a920e139e24"
+ integrity sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==
+ dependencies:
+ "@jridgewell/trace-mapping" "^0.3.13"
callsites "^3.0.0"
graceful-fs "^4.2.9"
- source-map "^0.6.0"
-"@jest/test-result@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb"
- integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==
+"@jest/test-result@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.3.tgz#5eae945fd9f4b8fcfce74d239e6f725b6bf076c5"
+ integrity sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==
dependencies:
- "@jest/console" "^27.5.1"
- "@jest/types" "^27.5.1"
+ "@jest/console" "^28.1.3"
+ "@jest/types" "^28.1.3"
"@types/istanbul-lib-coverage" "^2.0.0"
collect-v8-coverage "^1.0.0"
-"@jest/test-sequencer@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b"
- integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==
+"@jest/test-sequencer@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz#9d0c283d906ac599c74bde464bc0d7e6a82886c3"
+ integrity sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==
dependencies:
- "@jest/test-result" "^27.5.1"
+ "@jest/test-result" "^28.1.3"
graceful-fs "^4.2.9"
- jest-haste-map "^27.5.1"
- jest-runtime "^27.5.1"
+ jest-haste-map "^28.1.3"
+ slash "^3.0.0"
-"@jest/transform@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409"
- integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==
+"@jest/transform@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.3.tgz#59d8098e50ab07950e0f2fc0fc7ec462371281b0"
+ integrity sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==
dependencies:
- "@babel/core" "^7.1.0"
- "@jest/types" "^27.5.1"
+ "@babel/core" "^7.11.6"
+ "@jest/types" "^28.1.3"
+ "@jridgewell/trace-mapping" "^0.3.13"
babel-plugin-istanbul "^6.1.1"
chalk "^4.0.0"
convert-source-map "^1.4.0"
fast-json-stable-stringify "^2.0.0"
graceful-fs "^4.2.9"
- jest-haste-map "^27.5.1"
- jest-regex-util "^27.5.1"
- jest-util "^27.5.1"
+ jest-haste-map "^28.1.3"
+ jest-regex-util "^28.0.2"
+ jest-util "^28.1.3"
micromatch "^4.0.4"
pirates "^4.0.4"
slash "^3.0.0"
- source-map "^0.6.1"
- write-file-atomic "^3.0.0"
+ write-file-atomic "^4.0.1"
"@jest/types@^26.6.2":
version "26.6.2"
@@ -1519,23 +1565,24 @@
"@types/yargs" "^15.0.0"
chalk "^4.0.0"
-"@jest/types@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80"
- integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==
+"@jest/types@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b"
+ integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==
dependencies:
+ "@jest/schemas" "^28.1.3"
"@types/istanbul-lib-coverage" "^2.0.0"
"@types/istanbul-reports" "^3.0.0"
"@types/node" "*"
- "@types/yargs" "^16.0.0"
+ "@types/yargs" "^17.0.8"
chalk "^4.0.0"
-"@jridgewell/gen-mapping@^0.3.0":
- version "0.3.1"
- resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9"
- integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==
+"@jridgewell/gen-mapping@^0.3.2":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
+ integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==
dependencies:
- "@jridgewell/set-array" "^1.0.0"
+ "@jridgewell/set-array" "^1.0.1"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9"
@@ -1544,17 +1591,17 @@
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c"
integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==
-"@jridgewell/set-array@^1.0.0":
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea"
- integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==
+"@jridgewell/set-array@^1.0.1":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
+ integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
"@jridgewell/sourcemap-codec@^1.4.10":
version "1.4.11"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec"
integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==
-"@jridgewell/trace-mapping@0.3.9", "@jridgewell/trace-mapping@^0.3.0", "@jridgewell/trace-mapping@^0.3.9":
+"@jridgewell/trace-mapping@0.3.9":
version "0.3.9"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
@@ -1562,6 +1609,14 @@
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"
+"@jridgewell/trace-mapping@^0.3.0", "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.9":
+ version "0.3.15"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774"
+ integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.0.3"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+
"@jsdevtools/ono@^7.1.3":
version "7.1.3"
resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796"
@@ -1639,16 +1694,6 @@
resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-6.1.4-7.tgz#ef0b83ef40f64bc6704e13ae6624236a4a91fa6f"
integrity sha512-842WcLh0BErNgGE8rdqNh31VnqGQcklPQ7RXzQfA0ilQNZcU7AO+t576g1m//18Lk8m7cXZ8fIKA1YB41LKWAQ==
-"@sentry/browser@5.30.0":
- version "5.30.0"
- resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.30.0.tgz#c28f49d551db3172080caef9f18791a7fd39e3b3"
- integrity sha512-rOb58ZNVJWh1VuMuBG1mL9r54nZqKeaIlwSlvzJfc89vyfd7n6tQ1UXMN383QBz/MS5H5z44Hy5eE+7pCrYAfw==
- dependencies:
- "@sentry/core" "5.30.0"
- "@sentry/types" "5.30.0"
- "@sentry/utils" "5.30.0"
- tslib "^1.9.3"
-
"@sentry/core@5.30.0":
version "5.30.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3"
@@ -1660,6 +1705,15 @@
"@sentry/utils" "5.30.0"
tslib "^1.9.3"
+"@sentry/core@7.21.1":
+ version "7.21.1"
+ resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.21.1.tgz#d0423282d90875625802dfe380f9657e9242b72b"
+ integrity sha512-Og5wEEsy24fNvT/T7IKjcV4EvVK5ryY2kxbJzKY6GU2eX+i+aBl+n/vp7U0Es351C/AlTkS+0NOUsp2TQQFxZA==
+ dependencies:
+ "@sentry/types" "7.21.1"
+ "@sentry/utils" "7.21.1"
+ tslib "^1.9.3"
+
"@sentry/hub@5.30.0":
version "5.30.0"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.30.0.tgz#2453be9b9cb903404366e198bd30c7ca74cdc100"
@@ -1683,6 +1737,11 @@
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.30.0.tgz#19709bbe12a1a0115bc790b8942917da5636f402"
integrity sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==
+"@sentry/types@7.21.1":
+ version "7.21.1"
+ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.21.1.tgz#408a7b95a66ddc30c4359979594e03bee8f9fbdc"
+ integrity sha512-3/IKnd52Ol21amQvI+kz+WB76s8/LR5YvFJzMgIoI2S8d82smIr253zGijRXxHPEif8kMLX4Yt+36VzrLxg6+A==
+
"@sentry/utils@5.30.0":
version "5.30.0"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.30.0.tgz#9a5bd7ccff85ccfe7856d493bffa64cabc41e980"
@@ -1691,6 +1750,19 @@
"@sentry/types" "5.30.0"
tslib "^1.9.3"
+"@sentry/utils@7.21.1":
+ version "7.21.1"
+ resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.21.1.tgz#96582345178015fd32fe9159c25c44ccf2f99d2a"
+ integrity sha512-F0W0AAi8tgtTx6ApZRI2S9HbXEA9ENX1phTZgdNNWcMFm1BNbc21XEwLqwXBNjub5nlA6CE8xnjXRgdZKx4kzQ==
+ dependencies:
+ "@sentry/types" "7.21.1"
+ tslib "^1.9.3"
+
+"@sinclair/typebox@^0.24.1":
+ version "0.24.40"
+ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.40.tgz#00ee9b48537b147f6ffc80ebc28ab16d6016ed5c"
+ integrity sha512-Xint60L8rF0+nRy+6fCjW9jQMmu7fTpbwTBrXZiK6eq/RHDJS7LvWX/0oXC8O7fCePmrY/XdfaTv2HiUDeCq4g==
+
"@sinonjs/commons@^1.7.0":
version "1.8.1"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217"
@@ -1698,10 +1770,10 @@
dependencies:
type-detect "4.0.8"
-"@sinonjs/fake-timers@^8.0.1":
- version "8.1.0"
- resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7"
- integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==
+"@sinonjs/fake-timers@^9.1.2":
+ version "9.1.2"
+ resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c"
+ integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==
dependencies:
"@sinonjs/commons" "^1.7.0"
@@ -1947,10 +2019,10 @@
"@tiptap/extension-floating-menu" "^2.0.0-beta.56"
prosemirror-view "1.26.2"
-"@tootallnate/once@1":
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
- integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
+"@tootallnate/once@2":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
+ integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
"@tsconfig/node10@^1.0.7":
version "1.0.9"
@@ -1977,7 +2049,7 @@
resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.0.tgz#14264692a9d6e2fa4db3df5e56e94b5e25647ac0"
integrity sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A==
-"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14":
+"@types/babel__core@^7.1.14":
version "7.1.19"
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460"
integrity sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==
@@ -2003,7 +2075,7 @@
"@babel/parser" "^7.1.0"
"@babel/types" "^7.0.0"
-"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6":
+"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6":
version "7.0.15"
resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.15.tgz#db9e4238931eb69ef8aab0ad6523d4d4caa39d03"
integrity sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A==
@@ -2080,10 +2152,10 @@
"@types/minimatch" "*"
"@types/node" "*"
-"@types/graceful-fs@^4.1.2":
- version "4.1.3"
- resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.3.tgz#039af35fe26bec35003e8d86d2ee9c586354348f"
- integrity sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ==
+"@types/graceful-fs@^4.1.3":
+ version "4.1.5"
+ resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15"
+ integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==
dependencies:
"@types/node" "*"
@@ -2120,13 +2192,22 @@
dependencies:
"@types/istanbul-lib-report" "*"
-"@types/jest@^27.5.1":
- version "27.5.2"
- resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.5.2.tgz#ec49d29d926500ffb9fd22b84262e862049c026c"
- integrity sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==
+"@types/jest@^28.1.3":
+ version "28.1.8"
+ resolved "https://registry.yarnpkg.com/@types/jest/-/jest-28.1.8.tgz#6936409f3c9724ea431efd412ea0238a0f03b09b"
+ integrity sha512-8TJkV++s7B6XqnDrzR1m/TT0A0h948Pnl/097veySPN67VRAgQ4gZ7n2KfJo2rVq6njQjdxU3GCCyDvAeuHoiw==
dependencies:
- jest-matcher-utils "^27.0.0"
- pretty-format "^27.0.0"
+ expect "^28.0.0"
+ pretty-format "^28.0.0"
+
+"@types/jsdom@^16.2.4":
+ version "16.2.15"
+ resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.15.tgz#6c09990ec43b054e49636cba4d11d54367fc90d6"
+ integrity sha512-nwF87yjBKuX/roqGYerZZM0Nv1pZDMAT5YhOHYeM/72Fic+VEqJh4nyoqoapzJnW3pUlfxPY5FhgsJtM+dRnQQ==
+ dependencies:
+ "@types/node" "*"
+ "@types/parse5" "^6.0.3"
+ "@types/tough-cookie" "*"
"@types/json-schema@^7.0.0", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
version "7.0.11"
@@ -2195,7 +2276,7 @@
resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.0.tgz#9ae2106efc443d7c1e26570aa8247828c9c80f11"
integrity sha512-J5D3z703XTDIGQFYXsnU9uRCW9e9mMEFO0Kpe6kykyiboqziru/RlZ0hM2P+PKTG4NHG1SjLrqae/NrV2iJApQ==
-"@types/parse5@^6.0.0":
+"@types/parse5@^6.0.0", "@types/parse5@^6.0.3":
version "6.0.3"
resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.3.tgz#705bb349e789efa06f43f128cef51240753424cb"
integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==
@@ -2247,6 +2328,21 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff"
integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==
+"@types/strip-bom@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2"
+ integrity sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==
+
+"@types/strip-json-comments@0.0.30":
+ version "0.0.30"
+ resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1"
+ integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==
+
+"@types/tough-cookie@*":
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397"
+ integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==
+
"@types/unist@*", "@types/unist@^2.0.0":
version "2.0.6"
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
@@ -2278,10 +2374,10 @@
dependencies:
"@types/yargs-parser" "*"
-"@types/yargs@^16.0.0":
- version "16.0.4"
- resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977"
- integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==
+"@types/yargs@^17.0.8":
+ version "17.0.12"
+ resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.12.tgz#0745ff3e4872b4ace98616d4b7e37ccbd75f9526"
+ integrity sha512-Nz4MPhecOFArtm81gFQvQqdV7XYCrWKx5uUt6GNHredFHn1i2mtWqXTON7EPXMtNi1qjtjEM/VCHDhcHsAMLXQ==
dependencies:
"@types/yargs-parser" "*"
@@ -2356,16 +2452,17 @@
lodash "^4.17.15"
pretty "^2.0.0"
-"@vue/vue2-jest@^27.0.0":
- version "27.0.0"
- resolved "https://registry.yarnpkg.com/@vue/vue2-jest/-/vue2-jest-27.0.0.tgz#456076e27f7fa0179a37b04baea3e0b3cf786c6d"
- integrity sha512-r8YGOuqEWpAf2wGfgxfOL6Jce3WYOMcYji2qd8kuDe466ZsybHFeMryMJi6JrELOOI+MCA/8eFsSOx1KoJa7Dg==
+"@vue/vue2-jest@^28.1.0":
+ version "28.1.0"
+ resolved "https://registry.yarnpkg.com/@vue/vue2-jest/-/vue2-jest-28.1.0.tgz#2ac1a1ba4fdfc286947bd06f5f363129c77292f3"
+ integrity sha512-4aZB6tRQw7x5Xi0dt1itLfOv4WeNLutccV7FHw47xfHjFr/JnwgqTmzC9yOiYzC9H7T2/3DSW95Bzl3RETtFlA==
dependencies:
"@babel/plugin-transform-modules-commonjs" "^7.2.0"
"@vue/component-compiler-utils" "^3.1.0"
chalk "^2.1.0"
css-tree "^2.0.1"
source-map "0.5.6"
+ tsconfig "^7.0.0"
"@webassemblyjs/ast@1.9.0":
version "1.9.0"
@@ -2570,7 +2667,7 @@ JSV@^4.0.x:
resolved "https://registry.yarnpkg.com/JSV/-/JSV-4.0.2.tgz#d077f6825571f82132f9dffaed587b4029feff57"
integrity sha512-ZJ6wx9xaKJ3yFUhq5/sk82PJMuUyLk277I8mQeyDgCTjGdjWJIvPfaU5LIXaMuaN2UO1X3kZH4+lgphublZUHw==
-abab@^2.0.3, abab@^2.0.5:
+abab@^2.0.5, abab@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==
@@ -2628,7 +2725,7 @@ acorn@^7.1.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
-acorn@^8.0.4, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.8.0:
+acorn@^8.0.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.8.0:
version "8.8.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
@@ -2909,16 +3006,15 @@ axios@^0.24.0:
dependencies:
follow-redirects "^1.14.4"
-babel-jest@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444"
- integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==
+babel-jest@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.3.tgz#c1187258197c099072156a0a121c11ee1e3917d5"
+ integrity sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==
dependencies:
- "@jest/transform" "^27.5.1"
- "@jest/types" "^27.5.1"
+ "@jest/transform" "^28.1.3"
"@types/babel__core" "^7.1.14"
babel-plugin-istanbul "^6.1.1"
- babel-preset-jest "^27.5.1"
+ babel-preset-jest "^28.1.3"
chalk "^4.0.0"
graceful-fs "^4.2.9"
slash "^3.0.0"
@@ -2951,14 +3047,14 @@ babel-plugin-istanbul@^6.1.1:
istanbul-lib-instrument "^5.0.4"
test-exclude "^6.0.0"
-babel-plugin-jest-hoist@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e"
- integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==
+babel-plugin-jest-hoist@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz#1952c4d0ea50f2d6d794353762278d1d8cca3fbe"
+ integrity sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==
dependencies:
"@babel/template" "^7.3.3"
"@babel/types" "^7.3.3"
- "@types/babel__core" "^7.0.0"
+ "@types/babel__core" "^7.1.14"
"@types/babel__traverse" "^7.0.6"
babel-plugin-lodash@^3.3.4:
@@ -3014,12 +3110,12 @@ babel-preset-current-node-syntax@^1.0.0:
"@babel/plugin-syntax-optional-chaining" "^7.8.3"
"@babel/plugin-syntax-top-level-await" "^7.8.3"
-babel-preset-jest@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81"
- integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==
+babel-preset-jest@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz#5dfc20b99abed5db994406c2b9ab94c73aaa419d"
+ integrity sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==
dependencies:
- babel-plugin-jest-hoist "^27.5.1"
+ babel-plugin-jest-hoist "^28.1.3"
babel-preset-current-node-syntax "^1.0.0"
backo2@^1.0.2:
@@ -3666,17 +3762,12 @@ color-convert@^2.0.1:
dependencies:
color-name "~1.1.4"
-color-convert@~0.5.0:
- version "0.5.3"
- resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd"
- integrity sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=
-
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
-color-name@~1.1.4:
+color-name@^1.1.4, color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
@@ -3906,7 +3997,7 @@ core-js-pure@^3.0.0:
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
-core-js@^3.26.1:
+core-js@^3.26.1, core-js@^3.6.5:
version "3.26.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.26.1.tgz#7a9816dabd9ee846c1c0fe0e8fcad68f3709134e"
integrity sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA==
@@ -3991,6 +4082,13 @@ cropper@^2.3.0:
dependencies:
jquery ">= 1.9.1"
+cross-fetch@^3.0.2:
+ version "3.1.5"
+ resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
+ integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
+ dependencies:
+ node-fetch "2.6.7"
+
cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
@@ -4120,10 +4218,10 @@ cssfontparser@^1.2.1:
resolved "https://registry.yarnpkg.com/cssfontparser/-/cssfontparser-1.2.1.tgz#f4022fc8f9700c68029d542084afbaf425a3f3e3"
integrity sha1-9AIvyPlwDGgCnVQghK+69CWj8+M=
-cssom@^0.4.4:
- version "0.4.4"
- resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10"
- integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==
+cssom@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36"
+ integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==
cssom@~0.3.6:
version "0.3.8"
@@ -4665,14 +4763,14 @@ dagre@^0.8.5:
graphlib "^2.1.8"
lodash "^4.17.15"
-data-urls@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b"
- integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==
+data-urls@^3.0.1:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143"
+ integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==
dependencies:
- abab "^2.0.3"
- whatwg-mimetype "^2.3.0"
- whatwg-url "^8.0.0"
+ abab "^2.0.6"
+ whatwg-mimetype "^3.0.0"
+ whatwg-url "^11.0.0"
dataloader@2.0.0:
version "2.0.0"
@@ -4689,6 +4787,11 @@ dateformat@^5.0.1:
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-5.0.1.tgz#60a27a2deb339f888ba4532f533e25ac73ca3d19"
integrity sha512-DrcKxOW2am3mtqoJwBTK3OlWcF0QSk1p8diEWwpu3Mf//VdURD7XVaeOV738JvcaBiFfm9o2fisoMhiJH0aYxg==
+dayjs@^1.10.4:
+ version "1.11.6"
+ resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.6.tgz#2e79a226314ec3ec904e3ee1dd5a4f5e5b1c7afb"
+ integrity sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ==
+
de-indent@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
@@ -4728,10 +4831,10 @@ decamelize@^1.1.0, decamelize@^1.2.0:
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
-decimal.js@^10.2.1:
- version "10.3.1"
- resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783"
- integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==
+decimal.js@^10.3.1:
+ version "10.4.0"
+ resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.0.tgz#97a7448873b01e92e5ff9117d89a7bca8e63e0fe"
+ integrity sha512-Nv6ENEzyPQ6AItkGwLE2PGKinZZ9g59vSh2BeH6NqPu0OTKZ5ruJsVqh/orbAnqXc9pBbgXAIrc2EyaCj8NpGg==
deckar01-task_list@^2.3.1:
version "2.3.1"
@@ -4872,10 +4975,10 @@ detect-node@^2.0.4:
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==
-diff-sequences@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327"
- integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==
+diff-sequences@^28.1.1:
+ version "28.1.1"
+ resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6"
+ integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==
diff@^3.4.0:
version "3.5.0"
@@ -4968,12 +5071,12 @@ domelementtype@^2.0.1, domelementtype@^2.2.0:
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
-domexception@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304"
- integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==
+domexception@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673"
+ integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==
dependencies:
- webidl-conversions "^5.0.0"
+ webidl-conversions "^7.0.0"
domhandler@^4.0.0, domhandler@^4.2.0:
version "4.2.0"
@@ -5077,10 +5180,10 @@ elliptic@^6.0.0:
minimalistic-assert "^1.0.1"
minimalistic-crypto-utils "^1.0.1"
-emittery@^0.8.1:
- version "0.8.1"
- resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860"
- integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==
+emittery@^0.10.2:
+ version "0.10.2"
+ resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933"
+ integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==
emoji-regex@^10.0.0:
version "10.0.0"
@@ -5206,75 +5309,75 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
-esbuild-android-64@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.9.tgz#4a7eb320ca8d3a305f14792061fd9614ccebb7c0"
- integrity sha512-HQCX7FJn9T4kxZQkhPjNZC7tBWZqJvhlLHPU2SFzrQB/7nDXjmTIFpFTjt7Bd1uFpeXmuwf5h5fZm+x/hLnhbw==
-
-esbuild-android-arm64@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.9.tgz#c948e5686df20857ad361ec67e070d40d7cab985"
- integrity sha512-E6zbLfqbFVCNEKircSHnPiSTsm3fCRxeIMPfrkS33tFjIAoXtwegQfVZqMGR0FlsvVxp2NEDOUz+WW48COCjSg==
-
-esbuild-darwin-64@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.9.tgz#25f564fa4b39c1cec84dc46bce5634fdbce1d5e4"
- integrity sha512-gI7dClcDN/HHVacZhTmGjl0/TWZcGuKJ0I7/xDGJwRQQn7aafZGtvagOFNmuOq+OBFPhlPv1T6JElOXb0unkSQ==
-
-esbuild-darwin-arm64@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.9.tgz#60faea3ed95d15239536aa88d06bb82b29278a86"
- integrity sha512-VZIMlcRN29yg/sv7DsDwN+OeufCcoTNaTl3Vnav7dL/nvsApD7uvhVRbgyMzv0zU/PP0xRhhIpTyc7lxEzHGSw==
-
-esbuild-freebsd-64@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.9.tgz#0339ef1c90a919175e7816788224517896657a0e"
- integrity sha512-uM4z5bTvuAXqPxrI204txhlsPIolQPWRMLenvGuCPZTnnGlCMF2QLs0Plcm26gcskhxewYo9LkkmYSS5Czrb5A==
-
-esbuild-freebsd-arm64@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.9.tgz#32abfc0be3ae3dd38e5a86a9beadbbcf592f1b57"
- integrity sha512-HHDjT3O5gWzicGdgJ5yokZVN9K9KG05SnERwl9nBYZaCjcCgj/sX8Ps1jvoFSfNCO04JSsHSOWo4qvxFuj8FoA==
-
-esbuild-linux-32@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.9.tgz#93581348a4da7ed2b29bc5539f2605ad7fcee77b"
- integrity sha512-AQIdE8FugGt1DkcekKi5ycI46QZpGJ/wqcMr7w6YUmOmp2ohQ8eO4sKUsOxNOvYL7hGEVwkndSyszR6HpVHLFg==
-
-esbuild-linux-64@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.9.tgz#0d171e7946c95d0d3ed4826026af2c5632d7dcc4"
- integrity sha512-4RXjae7g6Qs7StZyiYyXTZXBlfODhb1aBVAjd+ANuPmMhWthQilWo7rFHwJwL7DQu1Fjej2sODAVwLbcIVsAYQ==
-
-esbuild-linux-arm64@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.9.tgz#9838795a3720cbe736d3bc20621bd366eac22f24"
- integrity sha512-a+bTtxJmYmk9d+s2W4/R1SYKDDAldOKmWjWP0BnrWtDbvUBNOm++du0ysPju4mZVoEFgS1yLNW+VXnG/4FNwdQ==
-
-esbuild-linux-arm@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.9.tgz#dce96cd817bc7376f6af3967649c4ab1f2f79506"
- integrity sha512-3Zf2GVGUOI7XwChH3qrnTOSqfV1V4CAc/7zLVm4lO6JT6wbJrTgEYCCiNSzziSju+J9Jhf9YGWk/26quWPC6yQ==
-
-esbuild-linux-mips64le@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.9.tgz#0335a0739e61aa97cb9b4a018e3facfcca9cdcfd"
- integrity sha512-Zn9HSylDp89y+TRREMDoGrc3Z4Hs5u56ozZLQCiZAUx2+HdbbXbWdjmw3FdTJ/i7t5Cew6/Q+6kfO3KCcFGlyw==
-
-esbuild-linux-ppc64le@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.9.tgz#18482afb95b8a705e2da0a59d7131bff221281f9"
- integrity sha512-OEiOxNAMH9ENFYqRsWUj3CWyN3V8P3ZXyfNAtX5rlCEC/ERXrCEFCJji/1F6POzsXAzxvUJrTSTCy7G6BhA6Fw==
-
-esbuild-linux-riscv64@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.9.tgz#03b6f9708272c117006b9ce1c9ae8aab91b5a5b6"
- integrity sha512-ukm4KsC3QRausEFjzTsOZ/qqazw0YvJsKmfoZZm9QW27OHjk2XKSQGGvx8gIEswft/Sadp03/VZvAaqv5AIwNA==
-
-esbuild-linux-s390x@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.9.tgz#65fb645623d575780f155f0ee52935e62f9cca4f"
- integrity sha512-uDOQEH55wQ6ahcIKzQr3VyjGc6Po/xblLGLoUk3fVL1qjlZAibtQr6XRfy5wPJLu/M2o0vQKLq4lyJ2r1tWKcw==
+esbuild-android-64@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz#20a7ae1416c8eaade917fb2453c1259302c637a5"
+ integrity sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==
+
+esbuild-android-arm64@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz#9cc0ec60581d6ad267568f29cf4895ffdd9f2f04"
+ integrity sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==
+
+esbuild-darwin-64@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz#428e1730ea819d500808f220fbc5207aea6d4410"
+ integrity sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==
+
+esbuild-darwin-arm64@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz#b6dfc7799115a2917f35970bfbc93ae50256b337"
+ integrity sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==
+
+esbuild-freebsd-64@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz#4e190d9c2d1e67164619ae30a438be87d5eedaf2"
+ integrity sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==
+
+esbuild-freebsd-arm64@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz#18a4c0344ee23bd5a6d06d18c76e2fd6d3f91635"
+ integrity sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==
+
+esbuild-linux-32@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz#9a329731ee079b12262b793fb84eea762e82e0ce"
+ integrity sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==
+
+esbuild-linux-64@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz#532738075397b994467b514e524aeb520c191b6c"
+ integrity sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==
+
+esbuild-linux-arm64@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz#5372e7993ac2da8f06b2ba313710d722b7a86e5d"
+ integrity sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==
+
+esbuild-linux-arm@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz#e734aaf259a2e3d109d4886c9e81ec0f2fd9a9cc"
+ integrity sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==
+
+esbuild-linux-mips64le@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz#c0487c14a9371a84eb08fab0e1d7b045a77105eb"
+ integrity sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==
+
+esbuild-linux-ppc64le@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz#af048ad94eed0ce32f6d5a873f7abe9115012507"
+ integrity sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==
+
+esbuild-linux-riscv64@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz#423ed4e5927bd77f842bd566972178f424d455e6"
+ integrity sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==
+
+esbuild-linux-s390x@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz#21d21eaa962a183bfb76312e5a01cc5ae48ce8eb"
+ integrity sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==
esbuild-loader@^2.20.0:
version "2.20.0"
@@ -5288,63 +5391,63 @@ esbuild-loader@^2.20.0:
tapable "^2.2.0"
webpack-sources "^2.2.0"
-esbuild-netbsd-64@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.9.tgz#7894297bb9e11f3d2f6f31efecd1be4e181f0d54"
- integrity sha512-yWgxaYTQz+TqX80wXRq6xAtb7GSBAp6gqLKfOdANg9qEmAI1Bxn04IrQr0Mzm4AhxvGKoHzjHjMgXbCCSSDxcw==
-
-esbuild-openbsd-64@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.9.tgz#0f9d4c6b6772ae50d491d68ad4cc028300dda7c0"
- integrity sha512-JmS18acQl4iSAjrEha1MfEmUMN4FcnnrtTaJ7Qg0tDCOcgpPPQRLGsZqhes0vmx8VA6IqRyScqXvaL7+Q0Uf3A==
-
-esbuild-sunos-64@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.9.tgz#c32b7ce574b08f814de810ce7c1e34b843768126"
- integrity sha512-UKynGSWpzkPmXW3D2UMOD9BZPIuRaSqphxSCwScfEE05Be3KAmvjsBhht1fLzKpiFVJb0BYMd4jEbWMyJ/z1hQ==
-
-esbuild-windows-32@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.9.tgz#37a8f7cfccdb2177cd46613a1a1e1fcb419d36df"
- integrity sha512-aqXvu4/W9XyTVqO/hw3rNxKE1TcZiEYHPsXM9LwYmKSX9/hjvfIJzXwQBlPcJ/QOxedfoMVH0YnhhQ9Ffb0RGA==
-
-esbuild-windows-64@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.9.tgz#5fe1e76fc13dd7f520febecaea110b6f1649c7b2"
- integrity sha512-zm7h91WUmlS4idMtjvCrEeNhlH7+TNOmqw5dJPJZrgFaxoFyqYG6CKDpdFCQXdyKpD5yvzaQBOMVTCBVKGZDEg==
-
-esbuild-windows-arm64@0.15.9:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.9.tgz#98504428f7ba7d2cfc11940be68ee1139173fdce"
- integrity sha512-yQEVIv27oauAtvtuhJVfSNMztJJX47ismRS6Sv2QMVV9RM+6xjbMWuuwM2nxr5A2/gj/mu2z9YlQxiwoFRCfZA==
-
-esbuild@0.15.9, esbuild@^0.15.6:
- version "0.15.9"
- resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.9.tgz#33fb18b67b85004b6f7616bec955ca4b3e58935d"
- integrity sha512-OnYr1rkMVxtmMHIAKZLMcEUlJmqcbxBz9QoBU8G9v455na0fuzlT/GLu6l+SRghrk0Mm2fSSciMmzV43Q8e0Gg==
+esbuild-netbsd-64@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz#ae75682f60d08560b1fe9482bfe0173e5110b998"
+ integrity sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==
+
+esbuild-openbsd-64@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz#79591a90aa3b03e4863f93beec0d2bab2853d0a8"
+ integrity sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==
+
+esbuild-sunos-64@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz#fd528aa5da5374b7e1e93d36ef9b07c3dfed2971"
+ integrity sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==
+
+esbuild-windows-32@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz#0e92b66ecdf5435a76813c4bc5ccda0696f4efc3"
+ integrity sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==
+
+esbuild-windows-64@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz#0fc761d785414284fc408e7914226d33f82420d0"
+ integrity sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==
+
+esbuild-windows-arm64@0.15.18:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz#5b5bdc56d341d0922ee94965c89ee120a6a86eb7"
+ integrity sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==
+
+esbuild@0.15.18, esbuild@^0.15.6:
+ version "0.15.18"
+ resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.18.tgz#ea894adaf3fbc036d32320a00d4d6e4978a2f36d"
+ integrity sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==
optionalDependencies:
- "@esbuild/android-arm" "0.15.9"
- "@esbuild/linux-loong64" "0.15.9"
- esbuild-android-64 "0.15.9"
- esbuild-android-arm64 "0.15.9"
- esbuild-darwin-64 "0.15.9"
- esbuild-darwin-arm64 "0.15.9"
- esbuild-freebsd-64 "0.15.9"
- esbuild-freebsd-arm64 "0.15.9"
- esbuild-linux-32 "0.15.9"
- esbuild-linux-64 "0.15.9"
- esbuild-linux-arm "0.15.9"
- esbuild-linux-arm64 "0.15.9"
- esbuild-linux-mips64le "0.15.9"
- esbuild-linux-ppc64le "0.15.9"
- esbuild-linux-riscv64 "0.15.9"
- esbuild-linux-s390x "0.15.9"
- esbuild-netbsd-64 "0.15.9"
- esbuild-openbsd-64 "0.15.9"
- esbuild-sunos-64 "0.15.9"
- esbuild-windows-32 "0.15.9"
- esbuild-windows-64 "0.15.9"
- esbuild-windows-arm64 "0.15.9"
+ "@esbuild/android-arm" "0.15.18"
+ "@esbuild/linux-loong64" "0.15.18"
+ esbuild-android-64 "0.15.18"
+ esbuild-android-arm64 "0.15.18"
+ esbuild-darwin-64 "0.15.18"
+ esbuild-darwin-arm64 "0.15.18"
+ esbuild-freebsd-64 "0.15.18"
+ esbuild-freebsd-arm64 "0.15.18"
+ esbuild-linux-32 "0.15.18"
+ esbuild-linux-64 "0.15.18"
+ esbuild-linux-arm "0.15.18"
+ esbuild-linux-arm64 "0.15.18"
+ esbuild-linux-mips64le "0.15.18"
+ esbuild-linux-ppc64le "0.15.18"
+ esbuild-linux-riscv64 "0.15.18"
+ esbuild-linux-s390x "0.15.18"
+ esbuild-netbsd-64 "0.15.18"
+ esbuild-openbsd-64 "0.15.18"
+ esbuild-sunos-64 "0.15.18"
+ esbuild-windows-32 "0.15.18"
+ esbuild-windows-64 "0.15.18"
+ esbuild-windows-arm64 "0.15.18"
escalade@^3.1.1:
version "3.1.1"
@@ -5484,10 +5587,10 @@ eslint-plugin-no-jquery@2.7.0:
resolved "https://registry.yarnpkg.com/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-2.7.0.tgz#855f5631cf5b8e25b930cf6f06e02dd81f132e72"
integrity sha512-Aeg7dA6GTH1AcWLlBtWNzOU9efK5KpNi7b0EhBO0o0M+awyzguUUo8gF6hXGjQ9n5h8/uRtYv9zOqQkeC5CG0w==
-eslint-plugin-no-unsanitized@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.0.1.tgz#e2343265467ba2270ade478cbe07bbafeaea412d"
- integrity sha512-y/lAMWnPPC7RYuUdxlEL/XiCL8FehN9h9s3Kjqbp/Kv0i9NZs+IXSC2kS546Fa4Bumwy31HlVS/OdWX0Kxb5Xg==
+eslint-plugin-no-unsanitized@^4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.0.2.tgz#e872b302cdfb5fe1262db989ba29cfcc334b499b"
+ integrity sha512-Pry0S9YmHoz8NCEMRQh7N0Yexh2MYCNPIlrV52hTmS7qXnTghWsjXouF08bgsrrZqaW9tt1ZiK3j5NEmPE+EjQ==
eslint-plugin-promise@^6.0.1:
version "6.0.1"
@@ -5573,10 +5676,10 @@ eslint-visitor-keys@^3.3.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
-eslint@8.26.0:
- version "8.26.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.26.0.tgz#2bcc8836e6c424c4ac26a5674a70d44d84f2181d"
- integrity sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==
+eslint@8.28.0:
+ version "8.28.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.28.0.tgz#81a680732634677cc890134bcdd9fdfea8e63d6e"
+ integrity sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==
dependencies:
"@eslint/eslintrc" "^1.3.3"
"@humanwhocodes/config-array" "^0.11.6"
@@ -5739,15 +5842,16 @@ expand-brackets@^2.1.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
-expect@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74"
- integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==
+expect@^28.0.0, expect@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec"
+ integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==
dependencies:
- "@jest/types" "^27.5.1"
- jest-get-type "^27.5.1"
- jest-matcher-utils "^27.5.1"
- jest-message-util "^27.5.1"
+ "@jest/expect-utils" "^28.1.3"
+ jest-get-type "^28.0.2"
+ jest-matcher-utils "^28.1.3"
+ jest-message-util "^28.1.3"
+ jest-util "^28.1.3"
express@^4.17.3:
version "4.17.3"
@@ -6044,10 +6148,10 @@ form-data-encoder@^1.7.1:
resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040"
integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==
-form-data@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
- integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
+form-data@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
+ integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
@@ -6248,7 +6352,7 @@ glob-parent@^6.0.2:
dependencies:
is-glob "^4.0.3"
-"glob@5 - 7", glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
+"glob@5 - 7", glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
version "7.2.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
@@ -6650,12 +6754,12 @@ hpack.js@^2.1.6:
readable-stream "^2.0.1"
wbuf "^1.1.0"
-html-encoding-sniffer@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3"
- integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==
+html-encoding-sniffer@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9"
+ integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==
dependencies:
- whatwg-encoding "^1.0.5"
+ whatwg-encoding "^2.0.0"
html-entities@^2.3.2:
version "2.3.2"
@@ -6718,12 +6822,12 @@ http-parser-js@>=0.5.1:
resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9"
integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==
-http-proxy-agent@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a"
- integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==
+http-proxy-agent@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
+ integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==
dependencies:
- "@tootallnate/once" "1"
+ "@tootallnate/once" "2"
agent-base "6"
debug "4"
@@ -6772,7 +6876,7 @@ iconv-lite@0.4, iconv-lite@0.4.24:
dependencies:
safer-buffer ">= 2.1.2 < 3"
-iconv-lite@0.6:
+iconv-lite@0.6, iconv-lite@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
@@ -7197,11 +7301,6 @@ is-symbol@^1.0.2, is-symbol@^1.0.3:
dependencies:
has-symbols "^1.0.2"
-is-typedarray@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
- integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
-
is-weakref@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
@@ -7319,246 +7418,243 @@ jed@^1.1.1:
resolved "https://registry.yarnpkg.com/jed/-/jed-1.1.1.tgz#7a549bbd9ffe1585b0cd0a191e203055bee574b4"
integrity sha1-elSbvZ/+FYWwzQoZHiAwVb7ldLQ=
-jest-canvas-mock@^2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.1.2.tgz#0d16c9f91534f773fd132fc289f2e6b6db8faa28"
- integrity sha512-1VI4PK4/X70yrSjYScYVkYJYbXYlZLKJkUrAlyHjQsfolv64aoFyIrmMDtqCjpYrpVvWYEcAGUaYv5DVJj00oQ==
+jest-canvas-mock@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.4.0.tgz#947b71442d7719f8e055decaecdb334809465341"
+ integrity sha512-mmMpZzpmLzn5vepIaHk5HoH3Ka4WykbSoLuG/EKoJd0x0ID/t+INo1l8ByfcUJuDM+RIsL4QDg/gDnBbrj2/IQ==
dependencies:
cssfontparser "^1.2.1"
- parse-color "^1.0.0"
+ moo-color "^1.0.2"
-jest-changed-files@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5"
- integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==
+jest-changed-files@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-28.1.3.tgz#d9aeee6792be3686c47cb988a8eaf82ff4238831"
+ integrity sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==
dependencies:
- "@jest/types" "^27.5.1"
execa "^5.0.0"
- throat "^6.0.1"
+ p-limit "^3.1.0"
-jest-circus@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc"
- integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==
+jest-circus@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-28.1.3.tgz#d14bd11cf8ee1a03d69902dc47b6bd4634ee00e4"
+ integrity sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==
dependencies:
- "@jest/environment" "^27.5.1"
- "@jest/test-result" "^27.5.1"
- "@jest/types" "^27.5.1"
+ "@jest/environment" "^28.1.3"
+ "@jest/expect" "^28.1.3"
+ "@jest/test-result" "^28.1.3"
+ "@jest/types" "^28.1.3"
"@types/node" "*"
chalk "^4.0.0"
co "^4.6.0"
dedent "^0.7.0"
- expect "^27.5.1"
is-generator-fn "^2.0.0"
- jest-each "^27.5.1"
- jest-matcher-utils "^27.5.1"
- jest-message-util "^27.5.1"
- jest-runtime "^27.5.1"
- jest-snapshot "^27.5.1"
- jest-util "^27.5.1"
- pretty-format "^27.5.1"
+ jest-each "^28.1.3"
+ jest-matcher-utils "^28.1.3"
+ jest-message-util "^28.1.3"
+ jest-runtime "^28.1.3"
+ jest-snapshot "^28.1.3"
+ jest-util "^28.1.3"
+ p-limit "^3.1.0"
+ pretty-format "^28.1.3"
slash "^3.0.0"
stack-utils "^2.0.3"
- throat "^6.0.1"
-jest-cli@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145"
- integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==
+jest-cli@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.3.tgz#558b33c577d06de55087b8448d373b9f654e46b2"
+ integrity sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==
dependencies:
- "@jest/core" "^27.5.1"
- "@jest/test-result" "^27.5.1"
- "@jest/types" "^27.5.1"
+ "@jest/core" "^28.1.3"
+ "@jest/test-result" "^28.1.3"
+ "@jest/types" "^28.1.3"
chalk "^4.0.0"
exit "^0.1.2"
graceful-fs "^4.2.9"
import-local "^3.0.2"
- jest-config "^27.5.1"
- jest-util "^27.5.1"
- jest-validate "^27.5.1"
+ jest-config "^28.1.3"
+ jest-util "^28.1.3"
+ jest-validate "^28.1.3"
prompts "^2.0.1"
- yargs "^16.2.0"
+ yargs "^17.3.1"
-jest-config@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41"
- integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==
+jest-config@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-28.1.3.tgz#e315e1f73df3cac31447eed8b8740a477392ec60"
+ integrity sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==
dependencies:
- "@babel/core" "^7.8.0"
- "@jest/test-sequencer" "^27.5.1"
- "@jest/types" "^27.5.1"
- babel-jest "^27.5.1"
+ "@babel/core" "^7.11.6"
+ "@jest/test-sequencer" "^28.1.3"
+ "@jest/types" "^28.1.3"
+ babel-jest "^28.1.3"
chalk "^4.0.0"
ci-info "^3.2.0"
deepmerge "^4.2.2"
- glob "^7.1.1"
+ glob "^7.1.3"
graceful-fs "^4.2.9"
- jest-circus "^27.5.1"
- jest-environment-jsdom "^27.5.1"
- jest-environment-node "^27.5.1"
- jest-get-type "^27.5.1"
- jest-jasmine2 "^27.5.1"
- jest-regex-util "^27.5.1"
- jest-resolve "^27.5.1"
- jest-runner "^27.5.1"
- jest-util "^27.5.1"
- jest-validate "^27.5.1"
+ jest-circus "^28.1.3"
+ jest-environment-node "^28.1.3"
+ jest-get-type "^28.0.2"
+ jest-regex-util "^28.0.2"
+ jest-resolve "^28.1.3"
+ jest-runner "^28.1.3"
+ jest-util "^28.1.3"
+ jest-validate "^28.1.3"
micromatch "^4.0.4"
parse-json "^5.2.0"
- pretty-format "^27.5.1"
+ pretty-format "^28.1.3"
slash "^3.0.0"
strip-json-comments "^3.1.1"
-jest-diff@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def"
- integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==
+jest-diff@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f"
+ integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==
dependencies:
chalk "^4.0.0"
- diff-sequences "^27.5.1"
- jest-get-type "^27.5.1"
- pretty-format "^27.5.1"
+ diff-sequences "^28.1.1"
+ jest-get-type "^28.0.2"
+ pretty-format "^28.1.3"
-jest-docblock@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0"
- integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==
+jest-docblock@^28.1.1:
+ version "28.1.1"
+ resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-28.1.1.tgz#6f515c3bf841516d82ecd57a62eed9204c2f42a8"
+ integrity sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==
dependencies:
detect-newline "^3.0.0"
-jest-each@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e"
- integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==
+jest-each@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-28.1.3.tgz#bdd1516edbe2b1f3569cfdad9acd543040028f81"
+ integrity sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==
dependencies:
- "@jest/types" "^27.5.1"
+ "@jest/types" "^28.1.3"
chalk "^4.0.0"
- jest-get-type "^27.5.1"
- jest-util "^27.5.1"
- pretty-format "^27.5.1"
-
-jest-environment-jsdom@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546"
- integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==
- dependencies:
- "@jest/environment" "^27.5.1"
- "@jest/fake-timers" "^27.5.1"
- "@jest/types" "^27.5.1"
+ jest-get-type "^28.0.2"
+ jest-util "^28.1.3"
+ pretty-format "^28.1.3"
+
+jest-environment-jsdom@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-28.1.3.tgz#2d4e5d61b7f1d94c3bddfbb21f0308ee506c09fb"
+ integrity sha512-HnlGUmZRdxfCByd3GM2F100DgQOajUBzEitjGqIREcb45kGjZvRrKUdlaF6escXBdcXNl0OBh+1ZrfeZT3GnAg==
+ dependencies:
+ "@jest/environment" "^28.1.3"
+ "@jest/fake-timers" "^28.1.3"
+ "@jest/types" "^28.1.3"
+ "@types/jsdom" "^16.2.4"
"@types/node" "*"
- jest-mock "^27.5.1"
- jest-util "^27.5.1"
- jsdom "^16.6.0"
-
-jest-environment-node@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e"
- integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==
- dependencies:
- "@jest/environment" "^27.5.1"
- "@jest/fake-timers" "^27.5.1"
- "@jest/types" "^27.5.1"
+ jest-mock "^28.1.3"
+ jest-util "^28.1.3"
+ jsdom "^19.0.0"
+
+jest-environment-node@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-28.1.3.tgz#7e74fe40eb645b9d56c0c4b70ca4357faa349be5"
+ integrity sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==
+ dependencies:
+ "@jest/environment" "^28.1.3"
+ "@jest/fake-timers" "^28.1.3"
+ "@jest/types" "^28.1.3"
"@types/node" "*"
- jest-mock "^27.5.1"
- jest-util "^27.5.1"
+ jest-mock "^28.1.3"
+ jest-util "^28.1.3"
-jest-get-type@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1"
- integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==
+jest-get-type@^28.0.2:
+ version "28.0.2"
+ resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203"
+ integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==
-jest-haste-map@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f"
- integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==
+jest-haste-map@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.3.tgz#abd5451129a38d9841049644f34b034308944e2b"
+ integrity sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==
dependencies:
- "@jest/types" "^27.5.1"
- "@types/graceful-fs" "^4.1.2"
+ "@jest/types" "^28.1.3"
+ "@types/graceful-fs" "^4.1.3"
"@types/node" "*"
anymatch "^3.0.3"
fb-watchman "^2.0.0"
graceful-fs "^4.2.9"
- jest-regex-util "^27.5.1"
- jest-serializer "^27.5.1"
- jest-util "^27.5.1"
- jest-worker "^27.5.1"
+ jest-regex-util "^28.0.2"
+ jest-util "^28.1.3"
+ jest-worker "^28.1.3"
micromatch "^4.0.4"
- walker "^1.0.7"
+ walker "^1.0.8"
optionalDependencies:
fsevents "^2.3.2"
-jest-jasmine2@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4"
- integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==
+jest-jasmine2@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-28.1.3.tgz#979726a3aa0755d6815bf675d38ff85b7c41f54c"
+ integrity sha512-nlNWJY1u62w+WAVgnXOQTdxFdZhqlxpKvMTn1cOK1QHX2oRrkPV3JcIcJfXwcGcifttOJZhExcgDUqSHrYQ6Dw==
dependencies:
- "@jest/environment" "^27.5.1"
- "@jest/source-map" "^27.5.1"
- "@jest/test-result" "^27.5.1"
- "@jest/types" "^27.5.1"
+ "@jest/environment" "^28.1.3"
+ "@jest/expect" "^28.1.3"
+ "@jest/source-map" "^28.1.2"
+ "@jest/test-result" "^28.1.3"
+ "@jest/types" "^28.1.3"
"@types/node" "*"
chalk "^4.0.0"
co "^4.6.0"
- expect "^27.5.1"
is-generator-fn "^2.0.0"
- jest-each "^27.5.1"
- jest-matcher-utils "^27.5.1"
- jest-message-util "^27.5.1"
- jest-runtime "^27.5.1"
- jest-snapshot "^27.5.1"
- jest-util "^27.5.1"
- pretty-format "^27.5.1"
- throat "^6.0.1"
-
-jest-junit@^12.0.0:
- version "12.0.0"
- resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-12.0.0.tgz#3ebd4a6a84b50c4ab18323a8f7d9cceb9d845df6"
- integrity sha512-+8K35LlboWiPuCnXSyiid7rFdxNlpCWWM20WEYe6IZH6psfUWKZmSpSRQ5tk0C0cBeDsvsnIzcef5mYhyJsbug==
+ jest-each "^28.1.3"
+ jest-matcher-utils "^28.1.3"
+ jest-message-util "^28.1.3"
+ jest-runtime "^28.1.3"
+ jest-snapshot "^28.1.3"
+ jest-util "^28.1.3"
+ p-limit "^3.1.0"
+ pretty-format "^28.1.3"
+
+jest-junit@^12.3.0:
+ version "12.3.0"
+ resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-12.3.0.tgz#ee41a74e439eecdc8965f163f83035cce5998d6d"
+ integrity sha512-+NmE5ogsEjFppEl90GChrk7xgz8xzvF0f+ZT5AnhW6suJC93gvQtmQjfyjDnE0Z2nXJqEkxF0WXlvjG/J+wn/g==
dependencies:
mkdirp "^1.0.4"
strip-ansi "^5.2.0"
- uuid "^3.3.3"
+ uuid "^8.3.2"
xml "^1.0.1"
-jest-leak-detector@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8"
- integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==
+jest-leak-detector@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz#a6685d9b074be99e3adee816ce84fd30795e654d"
+ integrity sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==
dependencies:
- jest-get-type "^27.5.1"
- pretty-format "^27.5.1"
+ jest-get-type "^28.0.2"
+ pretty-format "^28.1.3"
-jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab"
- integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==
+jest-matcher-utils@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e"
+ integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==
dependencies:
chalk "^4.0.0"
- jest-diff "^27.5.1"
- jest-get-type "^27.5.1"
- pretty-format "^27.5.1"
+ jest-diff "^28.1.3"
+ jest-get-type "^28.0.2"
+ pretty-format "^28.1.3"
-jest-message-util@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf"
- integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==
+jest-message-util@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d"
+ integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==
dependencies:
"@babel/code-frame" "^7.12.13"
- "@jest/types" "^27.5.1"
+ "@jest/types" "^28.1.3"
"@types/stack-utils" "^2.0.0"
chalk "^4.0.0"
graceful-fs "^4.2.9"
micromatch "^4.0.4"
- pretty-format "^27.5.1"
+ pretty-format "^28.1.3"
slash "^3.0.0"
stack-utils "^2.0.3"
-jest-mock@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6"
- integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==
+jest-mock@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-28.1.3.tgz#d4e9b1fc838bea595c77ab73672ebf513ab249da"
+ integrity sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==
dependencies:
- "@jest/types" "^27.5.1"
+ "@jest/types" "^28.1.3"
"@types/node" "*"
jest-pnp-resolver@^1.2.2:
@@ -7566,181 +7662,174 @@ jest-pnp-resolver@^1.2.2:
resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c"
integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==
-jest-regex-util@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95"
- integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==
+jest-regex-util@^28.0.2:
+ version "28.0.2"
+ resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead"
+ integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==
-jest-resolve-dependencies@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8"
- integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==
+jest-resolve-dependencies@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz#8c65d7583460df7275c6ea2791901fa975c1fe66"
+ integrity sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==
dependencies:
- "@jest/types" "^27.5.1"
- jest-regex-util "^27.5.1"
- jest-snapshot "^27.5.1"
+ jest-regex-util "^28.0.2"
+ jest-snapshot "^28.1.3"
-jest-resolve@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384"
- integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==
+jest-resolve@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-28.1.3.tgz#cfb36100341ddbb061ec781426b3c31eb51aa0a8"
+ integrity sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==
dependencies:
- "@jest/types" "^27.5.1"
chalk "^4.0.0"
graceful-fs "^4.2.9"
- jest-haste-map "^27.5.1"
+ jest-haste-map "^28.1.3"
jest-pnp-resolver "^1.2.2"
- jest-util "^27.5.1"
- jest-validate "^27.5.1"
+ jest-util "^28.1.3"
+ jest-validate "^28.1.3"
resolve "^1.20.0"
resolve.exports "^1.1.0"
slash "^3.0.0"
-jest-runner@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5"
- integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==
+jest-runner@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-28.1.3.tgz#5eee25febd730b4713a2cdfd76bdd5557840f9a1"
+ integrity sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==
dependencies:
- "@jest/console" "^27.5.1"
- "@jest/environment" "^27.5.1"
- "@jest/test-result" "^27.5.1"
- "@jest/transform" "^27.5.1"
- "@jest/types" "^27.5.1"
+ "@jest/console" "^28.1.3"
+ "@jest/environment" "^28.1.3"
+ "@jest/test-result" "^28.1.3"
+ "@jest/transform" "^28.1.3"
+ "@jest/types" "^28.1.3"
"@types/node" "*"
chalk "^4.0.0"
- emittery "^0.8.1"
+ emittery "^0.10.2"
graceful-fs "^4.2.9"
- jest-docblock "^27.5.1"
- jest-environment-jsdom "^27.5.1"
- jest-environment-node "^27.5.1"
- jest-haste-map "^27.5.1"
- jest-leak-detector "^27.5.1"
- jest-message-util "^27.5.1"
- jest-resolve "^27.5.1"
- jest-runtime "^27.5.1"
- jest-util "^27.5.1"
- jest-worker "^27.5.1"
- source-map-support "^0.5.6"
- throat "^6.0.1"
-
-jest-runtime@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af"
- integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==
- dependencies:
- "@jest/environment" "^27.5.1"
- "@jest/fake-timers" "^27.5.1"
- "@jest/globals" "^27.5.1"
- "@jest/source-map" "^27.5.1"
- "@jest/test-result" "^27.5.1"
- "@jest/transform" "^27.5.1"
- "@jest/types" "^27.5.1"
+ jest-docblock "^28.1.1"
+ jest-environment-node "^28.1.3"
+ jest-haste-map "^28.1.3"
+ jest-leak-detector "^28.1.3"
+ jest-message-util "^28.1.3"
+ jest-resolve "^28.1.3"
+ jest-runtime "^28.1.3"
+ jest-util "^28.1.3"
+ jest-watcher "^28.1.3"
+ jest-worker "^28.1.3"
+ p-limit "^3.1.0"
+ source-map-support "0.5.13"
+
+jest-runtime@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-28.1.3.tgz#a57643458235aa53e8ec7821949e728960d0605f"
+ integrity sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==
+ dependencies:
+ "@jest/environment" "^28.1.3"
+ "@jest/fake-timers" "^28.1.3"
+ "@jest/globals" "^28.1.3"
+ "@jest/source-map" "^28.1.2"
+ "@jest/test-result" "^28.1.3"
+ "@jest/transform" "^28.1.3"
+ "@jest/types" "^28.1.3"
chalk "^4.0.0"
cjs-module-lexer "^1.0.0"
collect-v8-coverage "^1.0.0"
execa "^5.0.0"
glob "^7.1.3"
graceful-fs "^4.2.9"
- jest-haste-map "^27.5.1"
- jest-message-util "^27.5.1"
- jest-mock "^27.5.1"
- jest-regex-util "^27.5.1"
- jest-resolve "^27.5.1"
- jest-snapshot "^27.5.1"
- jest-util "^27.5.1"
+ jest-haste-map "^28.1.3"
+ jest-message-util "^28.1.3"
+ jest-mock "^28.1.3"
+ jest-regex-util "^28.0.2"
+ jest-resolve "^28.1.3"
+ jest-snapshot "^28.1.3"
+ jest-util "^28.1.3"
slash "^3.0.0"
strip-bom "^4.0.0"
-jest-serializer@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64"
- integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==
- dependencies:
- "@types/node" "*"
- graceful-fs "^4.2.9"
-
-jest-snapshot@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1"
- integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==
+jest-snapshot@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.3.tgz#17467b3ab8ddb81e2f605db05583d69388fc0668"
+ integrity sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==
dependencies:
- "@babel/core" "^7.7.2"
+ "@babel/core" "^7.11.6"
"@babel/generator" "^7.7.2"
"@babel/plugin-syntax-typescript" "^7.7.2"
"@babel/traverse" "^7.7.2"
- "@babel/types" "^7.0.0"
- "@jest/transform" "^27.5.1"
- "@jest/types" "^27.5.1"
- "@types/babel__traverse" "^7.0.4"
+ "@babel/types" "^7.3.3"
+ "@jest/expect-utils" "^28.1.3"
+ "@jest/transform" "^28.1.3"
+ "@jest/types" "^28.1.3"
+ "@types/babel__traverse" "^7.0.6"
"@types/prettier" "^2.1.5"
babel-preset-current-node-syntax "^1.0.0"
chalk "^4.0.0"
- expect "^27.5.1"
+ expect "^28.1.3"
graceful-fs "^4.2.9"
- jest-diff "^27.5.1"
- jest-get-type "^27.5.1"
- jest-haste-map "^27.5.1"
- jest-matcher-utils "^27.5.1"
- jest-message-util "^27.5.1"
- jest-util "^27.5.1"
+ jest-diff "^28.1.3"
+ jest-get-type "^28.0.2"
+ jest-haste-map "^28.1.3"
+ jest-matcher-utils "^28.1.3"
+ jest-message-util "^28.1.3"
+ jest-util "^28.1.3"
natural-compare "^1.4.0"
- pretty-format "^27.5.1"
- semver "^7.3.2"
+ pretty-format "^28.1.3"
+ semver "^7.3.5"
-jest-util@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9"
- integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==
+jest-util@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0"
+ integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==
dependencies:
- "@jest/types" "^27.5.1"
+ "@jest/types" "^28.1.3"
"@types/node" "*"
chalk "^4.0.0"
ci-info "^3.2.0"
graceful-fs "^4.2.9"
picomatch "^2.2.3"
-jest-validate@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067"
- integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==
+jest-validate@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-28.1.3.tgz#e322267fd5e7c64cea4629612c357bbda96229df"
+ integrity sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==
dependencies:
- "@jest/types" "^27.5.1"
+ "@jest/types" "^28.1.3"
camelcase "^6.2.0"
chalk "^4.0.0"
- jest-get-type "^27.5.1"
+ jest-get-type "^28.0.2"
leven "^3.1.0"
- pretty-format "^27.5.1"
+ pretty-format "^28.1.3"
-jest-watcher@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2"
- integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==
+jest-watcher@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.3.tgz#c6023a59ba2255e3b4c57179fc94164b3e73abd4"
+ integrity sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==
dependencies:
- "@jest/test-result" "^27.5.1"
- "@jest/types" "^27.5.1"
+ "@jest/test-result" "^28.1.3"
+ "@jest/types" "^28.1.3"
"@types/node" "*"
ansi-escapes "^4.2.1"
chalk "^4.0.0"
- jest-util "^27.5.1"
+ emittery "^0.10.2"
+ jest-util "^28.1.3"
string-length "^4.0.1"
-jest-worker@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0"
- integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==
+jest-worker@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98"
+ integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==
dependencies:
"@types/node" "*"
merge-stream "^2.0.0"
supports-color "^8.0.0"
-jest@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc"
- integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==
+jest@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.3.tgz#e9c6a7eecdebe3548ca2b18894a50f45b36dfc6b"
+ integrity sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==
dependencies:
- "@jest/core" "^27.5.1"
+ "@jest/core" "^28.1.3"
+ "@jest/types" "^28.1.3"
import-local "^3.0.2"
- jest-cli "^27.5.1"
+ jest-cli "^28.1.3"
joycon@^3.0.1:
version "3.1.1"
@@ -7798,23 +7887,23 @@ js-yaml@^4.1.0:
dependencies:
argparse "^2.0.1"
-jsdom@^16.6.0:
- version "16.7.0"
- resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710"
- integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==
+jsdom@^19.0.0:
+ version "19.0.0"
+ resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-19.0.0.tgz#93e67c149fe26816d38a849ea30ac93677e16b6a"
+ integrity sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==
dependencies:
abab "^2.0.5"
- acorn "^8.2.4"
+ acorn "^8.5.0"
acorn-globals "^6.0.0"
- cssom "^0.4.4"
+ cssom "^0.5.0"
cssstyle "^2.3.0"
- data-urls "^2.0.0"
- decimal.js "^10.2.1"
- domexception "^2.0.1"
+ data-urls "^3.0.1"
+ decimal.js "^10.3.1"
+ domexception "^4.0.0"
escodegen "^2.0.0"
- form-data "^3.0.0"
- html-encoding-sniffer "^2.0.1"
- http-proxy-agent "^4.0.1"
+ form-data "^4.0.0"
+ html-encoding-sniffer "^3.0.0"
+ http-proxy-agent "^5.0.0"
https-proxy-agent "^5.0.0"
is-potential-custom-element-name "^1.0.1"
nwsapi "^2.2.0"
@@ -7823,13 +7912,13 @@ jsdom@^16.6.0:
symbol-tree "^3.2.4"
tough-cookie "^4.0.0"
w3c-hr-time "^1.0.2"
- w3c-xmlserializer "^2.0.0"
- webidl-conversions "^6.1.0"
- whatwg-encoding "^1.0.5"
- whatwg-mimetype "^2.3.0"
- whatwg-url "^8.5.0"
- ws "^7.4.6"
- xml-name-validator "^3.0.0"
+ w3c-xmlserializer "^3.0.0"
+ webidl-conversions "^7.0.0"
+ whatwg-encoding "^2.0.0"
+ whatwg-mimetype "^3.0.0"
+ whatwg-url "^10.0.0"
+ ws "^8.2.3"
+ xml-name-validator "^4.0.0"
jsesc@^2.5.1:
version "2.5.2"
@@ -8199,7 +8288,7 @@ lodash.values@^4.3.0:
resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347"
integrity sha1-o6bCsOvsxcLLocF+bmIP6BtT00c=
-lodash@^4.17.10, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
+lodash@^4.17.10, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -8272,12 +8361,12 @@ make-error@^1.1.1:
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
-makeerror@1.0.x:
- version "1.0.11"
- resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c"
- integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=
+makeerror@1.0.12:
+ version "1.0.12"
+ resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a"
+ integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==
dependencies:
- tmpl "1.0.x"
+ tmpl "1.0.5"
map-cache@^0.2.2:
version "0.2.2"
@@ -9165,6 +9254,13 @@ monaco-yaml@4.0.0:
vscode-uri "^3.0.0"
yaml "^2.0.0"
+moo-color@^1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/moo-color/-/moo-color-1.0.3.tgz#d56435f8359c8284d83ac58016df7427febece74"
+ integrity sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==
+ dependencies:
+ color-name "^1.1.4"
+
mousetrap@1.6.5:
version "1.6.5"
resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.5.tgz#8a766d8c272b08393d5f56074e0b5ec183485bf9"
@@ -9257,7 +9353,7 @@ node-domexception@1.0.0:
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
-node-fetch@^2.6.1, node-fetch@^2.6.7:
+node-fetch@2.6.7, node-fetch@^2.6.1, node-fetch@^2.6.7:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
@@ -9577,7 +9673,7 @@ osenv@^0.1.4:
os-homedir "^1.0.0"
os-tmpdir "^1.0.0"
-p-limit@3.1.0, p-limit@^3.0.2:
+p-limit@3.1.0, p-limit@^3.0.2, p-limit@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
@@ -9688,13 +9784,6 @@ parse-asn1@^5.0.0:
evp_bytestokey "^1.0.0"
pbkdf2 "^3.0.3"
-parse-color@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/parse-color/-/parse-color-1.0.0.tgz#7b748b95a83f03f16a94f535e52d7f3d94658619"
- integrity sha1-e3SLlag/A/FqlPU15S1/PZRlhhk=
- dependencies:
- color-convert "~0.5.0"
-
parse-json@^5.0.0, parse-json@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
@@ -10004,14 +10093,15 @@ pretty-format@^26.4.2:
ansi-styles "^4.0.0"
react-is "^17.0.1"
-pretty-format@^27.0.0, pretty-format@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
- integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
+pretty-format@^28.0.0, pretty-format@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5"
+ integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==
dependencies:
+ "@jest/schemas" "^28.1.3"
ansi-regex "^5.0.1"
ansi-styles "^5.0.0"
- react-is "^17.0.1"
+ react-is "^18.0.0"
pretty@^2.0.0:
version "2.0.0"
@@ -10290,6 +10380,11 @@ quick-lru@^4.0.1:
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==
+ramda@^0.27.2:
+ version "0.27.2"
+ resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.2.tgz#84463226f7f36dc33592f6f4ed6374c48306c3f1"
+ integrity sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA==
+
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
@@ -10345,6 +10440,11 @@ react-is@^17.0.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
+react-is@^18.0.0:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
+ integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
+
read-pkg-up@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507"
@@ -10827,7 +10927,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
-semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7:
+semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7:
version "7.3.7"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
@@ -10853,6 +10953,26 @@ send@0.17.2:
range-parser "~1.2.1"
statuses "~1.5.0"
+"sentrybrowser5@npm:@sentry/browser@5.30.0":
+ version "5.30.0"
+ resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.30.0.tgz#c28f49d551db3172080caef9f18791a7fd39e3b3"
+ integrity sha512-rOb58ZNVJWh1VuMuBG1mL9r54nZqKeaIlwSlvzJfc89vyfd7n6tQ1UXMN383QBz/MS5H5z44Hy5eE+7pCrYAfw==
+ dependencies:
+ "@sentry/core" "5.30.0"
+ "@sentry/types" "5.30.0"
+ "@sentry/utils" "5.30.0"
+ tslib "^1.9.3"
+
+"sentrybrowser7@npm:@sentry/browser@^7.21.1":
+ version "7.21.1"
+ resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.21.1.tgz#bffa3ea19050c06400107d2297b9802f9719f98b"
+ integrity sha512-cS2Jz2+fs9+4pJqLJPtYqGyY97ywJDWAWIR1Yla3hs1QQuH6m0Nz3ojZD1gE2eKH9mHwkGbnNAh+hHcrYrfGzw==
+ dependencies:
+ "@sentry/core" "7.21.1"
+ "@sentry/types" "7.21.1"
+ "@sentry/utils" "7.21.1"
+ tslib "^1.9.3"
+
serialize-javascript@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61"
@@ -10985,7 +11105,7 @@ sigmund@^1.0.1:
resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=
-signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7:
+signal-exit@^3.0.3, signal-exit@^3.0.7:
version "3.0.7"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
@@ -11095,10 +11215,10 @@ source-map-resolve@^0.5.0:
source-map-url "^0.4.0"
urix "^0.1.0"
-source-map-support@^0.5.6, source-map-support@~0.5.12:
- version "0.5.20"
- resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9"
- integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==
+source-map-support@0.5.13, source-map-support@~0.5.12:
+ version "0.5.13"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
+ integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
@@ -11118,11 +11238,6 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
-source-map@^0.7.3:
- version "0.7.3"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
- integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
-
space-separated-tokens@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.1.tgz#43193cec4fb858a2ce934b7f98b7f2c18107098b"
@@ -11348,6 +11463,11 @@ strip-indent@^3.0.0:
dependencies:
min-indent "^1.0.0"
+strip-json-comments@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+ integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
+
strip-json-comments@^3.1.0, strip-json-comments@^3.1.1, strip-json-comments@~3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
@@ -11622,11 +11742,6 @@ three@^0.143.0:
resolved "https://registry.yarnpkg.com/three/-/three-0.143.0.tgz#1455bca132cc2b20beb7f41d313e10c29e5ed9df"
integrity sha512-oKcAGYHhJ46TGEuHjodo2n6TY2R6lbvrkp+feKZxqsUL/WkH7GKKaeu6RHeyb2Xjfk2dPLRKLsOP0KM2VgT8Zg==
-throat@^6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375"
- integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==
-
throttle-debounce@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-2.1.0.tgz#257e648f0a56bd9e54fe0f132c4ab8611df4e1d5"
@@ -11681,7 +11796,7 @@ tmp@^0.0.33:
dependencies:
os-tmpdir "~1.0.2"
-tmpl@1.0.x:
+tmpl@1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==
@@ -11754,10 +11869,10 @@ tough-cookie@^4.0.0:
punycode "^2.1.1"
universalify "^0.1.2"
-tr46@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240"
- integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==
+tr46@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
+ integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
dependencies:
punycode "^2.1.1"
@@ -11812,6 +11927,16 @@ tsconfig-paths@^3.14.1:
minimist "^1.2.6"
strip-bom "^3.0.0"
+tsconfig@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7"
+ integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==
+ dependencies:
+ "@types/strip-bom" "^3.0.0"
+ "@types/strip-json-comments" "0.0.30"
+ strip-bom "^3.0.0"
+ strip-json-comments "^2.0.0"
+
tslib@2.3.0, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
@@ -11891,13 +12016,6 @@ type-is@~1.6.18:
media-typer "0.3.0"
mime-types "~2.1.24"
-typedarray-to-buffer@^3.1.5:
- version "3.1.5"
- resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
- integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==
- dependencies:
- is-typedarray "^1.0.0"
-
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
@@ -12117,6 +12235,11 @@ url-loader@^4.1.1:
mime-types "^2.1.27"
schema-utils "^3.0.0"
+url-search-params-polyfill@^7.0.0:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/url-search-params-polyfill/-/url-search-params-polyfill-7.0.1.tgz#b900cd9a0d9d2ff757d500135256f2344879cbff"
+ integrity sha512-bAw7L2E+jn9XHG5P9zrPnHdO0yJub4U+yXJOdpcpkr7OBd9T8oll4lUos0iSGRcDvfZoLUKfx9a6aNmIhJ4+mQ==
+
url@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
@@ -12159,11 +12282,6 @@ uuid@8.1.0:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d"
integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg==
-uuid@^3.3.3:
- version "3.4.0"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
- integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
-
uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
@@ -12189,14 +12307,14 @@ v8-compile-cache@^2.3.0:
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
-v8-to-istanbul@^8.1.0:
- version "8.1.1"
- resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed"
- integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==
+v8-to-istanbul@^9.0.1:
+ version "9.0.1"
+ resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4"
+ integrity sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==
dependencies:
+ "@jridgewell/trace-mapping" "^0.3.12"
"@types/istanbul-lib-coverage" "^2.0.1"
convert-source-map "^1.6.0"
- source-map "^0.7.3"
validate-npm-package-license@^3.0.1:
version "3.0.4"
@@ -12387,19 +12505,19 @@ w3c-keyname@^2.2.0:
resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.4.tgz#4ade6916f6290224cdbd1db8ac49eab03d0eef6b"
integrity sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw==
-w3c-xmlserializer@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a"
- integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==
+w3c-xmlserializer@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923"
+ integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==
dependencies:
- xml-name-validator "^3.0.0"
+ xml-name-validator "^4.0.0"
-walker@^1.0.7:
- version "1.0.7"
- resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"
- integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=
+walker@^1.0.8:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f"
+ integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==
dependencies:
- makeerror "1.0.x"
+ makeerror "1.0.12"
watchpack-chokidar2@^2.0.1:
version "2.0.1"
@@ -12451,15 +12569,10 @@ webidl-conversions@^3.0.0:
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
-webidl-conversions@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff"
- integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==
-
-webidl-conversions@^6.1.0:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
- integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
+webidl-conversions@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
+ integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
webpack-bundle-analyzer@^4.6.1:
version "4.6.1"
@@ -12612,17 +12725,33 @@ websocket-extensions@>=0.1.1:
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42"
integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==
-whatwg-encoding@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
- integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==
+whatwg-encoding@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53"
+ integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==
dependencies:
- iconv-lite "0.4.24"
+ iconv-lite "0.6.3"
-whatwg-mimetype@^2.3.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
- integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
+whatwg-mimetype@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7"
+ integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==
+
+whatwg-url@^10.0.0:
+ version "10.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-10.0.0.tgz#37264f720b575b4a311bd4094ed8c760caaa05da"
+ integrity sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==
+ dependencies:
+ tr46 "^3.0.0"
+ webidl-conversions "^7.0.0"
+
+whatwg-url@^11.0.0:
+ version "11.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018"
+ integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==
+ dependencies:
+ tr46 "^3.0.0"
+ webidl-conversions "^7.0.0"
whatwg-url@^5.0.0:
version "5.0.0"
@@ -12632,15 +12761,6 @@ whatwg-url@^5.0.0:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
-whatwg-url@^8.0.0, whatwg-url@^8.5.0:
- version "8.7.0"
- resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77"
- integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==
- dependencies:
- lodash "^4.7.0"
- tr46 "^2.1.0"
- webidl-conversions "^6.1.0"
-
which-boxed-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
@@ -12719,16 +12839,6 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
-write-file-atomic@^3.0.0:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8"
- integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==
- dependencies:
- imurmurhash "^0.1.4"
- is-typedarray "^1.0.0"
- signal-exit "^3.0.2"
- typedarray-to-buffer "^3.1.5"
-
write-file-atomic@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f"
@@ -12737,15 +12847,15 @@ write-file-atomic@^4.0.1:
imurmurhash "^0.1.4"
signal-exit "^3.0.7"
-"ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^7.3.1, ws@^7.4.6:
+"ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^7.3.1:
version "7.5.7"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67"
integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==
-ws@^8.3.0, ws@^8.4.2:
- version "8.5.0"
- resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f"
- integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==
+ws@^8.2.3, ws@^8.3.0, ws@^8.4.2:
+ version "8.8.1"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0"
+ integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==
xhr-mock@^2.5.1:
version "2.5.1"
@@ -12755,11 +12865,6 @@ xhr-mock@^2.5.1:
global "^4.3.0"
url "^0.11.0"
-xml-name-validator@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
- integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
-
xml-name-validator@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"
@@ -12828,11 +12933,16 @@ yargs-parser@^18.1.2:
camelcase "^5.0.0"
decamelize "^1.2.0"
-yargs-parser@^20.2.2, yargs-parser@^20.2.3:
+yargs-parser@^20.2.3:
version "20.2.9"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
+yargs-parser@^21.0.0:
+ version "21.1.1"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
+ integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
+
yargs@^15.4.1:
version "15.4.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
@@ -12850,18 +12960,18 @@ yargs@^15.4.1:
y18n "^4.0.0"
yargs-parser "^18.1.2"
-yargs@^16.2.0:
- version "16.2.0"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
- integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
+yargs@^17.3.1:
+ version "17.5.1"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e"
+ integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==
dependencies:
cliui "^7.0.2"
escalade "^3.1.1"
get-caller-file "^2.0.5"
require-directory "^2.1.1"
- string-width "^4.2.0"
+ string-width "^4.2.3"
y18n "^5.0.5"
- yargs-parser "^20.2.2"
+ yargs-parser "^21.0.0"
yarn-check-webpack-plugin@^1.2.0:
version "1.2.0"